/* * The routines in this file implement commands that work word at a time. * There are all sorts of word mode commands. If I do any sentence and/or * paragraph mode commands, they are likely to be put in this file. */ #include #include "ed.h" #define WPNULL -1 /* null word pointer */ /* Paragraph fill. Retain indenting of first line of paragraph. Remove * leading spaces from other lines. Remove extra space between words. * Fill each line to fillcol, breaking at word boundaries. * Delete trailing space from all lines. */ parafill( f, n) { register int c; /* current char */ register short spo; /* space offset */ register short count; short wo; /* word offset */ WINDOW *wp; LINE *flp, /* first line of paragraph */ *clp; /* current line of paragraph */ /* save old context */ /* we do this by creating a fake window which retains the context. * this will be updated by the editing commands. */ if ((wp = (WINDOW *) malloc(sizeof(WINDOW))) == NULL) { mlwrite("Cannot allocate WINDOW block"); return (FALSE); } wp->w_bufp = curbp; wp->w_dotp = curwp->w_dotp; wp->w_doto = curwp->w_doto; wp->w_markp = curwp->w_markp; wp->w_marko = curwp->w_marko; wp->w_flag = 0; wp->w_force = 0; wp->w_wndp = wheadp; wheadp = wp; /* get first line of paragraph */ for( flp = curwp->w_dotp; flp != curbp->b_linep; flp = lback( flp)) { if( llength( flp) == 0) /* blank line, found it */ break; } flp = lforw( flp); if( llength( flp) == 0) /* nothing to do */ return( TRUE); /* initialize */ count = 0; curwp->w_dotp = clp = flp; for(;;) { /* go through all lines */ spo = count; /* set space offset at current spot */ /* get to end of spaces */ for( ;; count++) { c = wgetc( &clp, count); if( (c != ' ') || (c == EOF)) break; } wo = count; /* get to end of word */ for( ;; count++) { c = wgetc( &clp, count); if( (c == ' ') || (c == EOF)) break; } if(((clp != flp) || spo) && (spo != wo)) { /* adjust spaces. if at beginning of line, delete all spaces */ curwp->w_doto = spo; if( spo) { /* past begin of line */ count += adjsp( &clp, spo, &wo); } else { /* at begin of line */ ldelete( wo - spo, FALSE); wo = count = 0; } } if( count > fillcol) { /* if past fill column, break. */ if(((clp != flp) || spo) && (spo != wo)) { /* collapse the spaces */ curwp->w_doto = spo; ldelete( wo - spo, FALSE); wo = spo; } if( wo > 0) { /* break at beginning of word */ curwp->w_doto = wo; } else { /* break at end of word */ curwp->w_doto = count; } newline( FALSE, 1); count = 0; clp = curwp->w_dotp; /* updated by newline */ } if( c == EOF) break; } /* restore context and delete fake window */ curwp->w_dotp = wp->w_dotp; curwp->w_doto = wp->w_doto; curwp->w_markp = wp->w_markp; curwp->w_marko = wp->w_marko; wheadp = wp->w_wndp; free( (char *) wp); return( TRUE); } /* adjust the spacing between words. check for colons and periods. * assumes spo > 0 and doto is set to spo. */ adjsp( lp, spo, wo) register LINE **lp; register short spo, *wo; { short result, spaces, n, c; /* decide on number of spaces */ switch( lgetc( *lp, spo -1)) { case ':': spaces = 2; break; case '.': spaces = 1; /* just a guess */ n = spo -2; if( n == 0) /* period at beginning of line */ break; else { c = lgetc( *lp, n); if((('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z'))) { /* letter */ n -= 1; if( n == 0) /* single letter followed by . */ break; else { c = lgetc( *lp, n); if((c == ' ') || (c == '.'))/* single letter followed by . */ break; } } else /* not a letter */ break; } if( *wo < llength( *lp)) { c = lgetc( *lp, *wo); if((c < 'A') || ('Z' < c)) /* not a cap letter */ break; } spaces = 2; break; default: spaces = 1; break; } /* adjust them */ if( (*wo - spo) == spaces) /* perfect */ return( 0); else if( (*wo - spo) > spaces) { /* delete extras */ result = *wo - spo - spaces; ldelete( result, FALSE); result = - result; } else { /* pad with spaces */ result = spaces - (*wo - spo); linsert( result, ' '); } *lp = curwp->w_dotp; *wo = spo + spaces; return( result); } /* Get next char, word-oriented style. If at end of line, append next * line by replacing newline with a space. Return EOF * when there is nothing left. */ wgetc( clp, n) LINE **clp; int n; { LINE *newlp; if( n > llength( *clp)) { mlwrite( "Bad para fill"); return( EOF); } if( n == llength( *clp)) { newlp = lforw( *clp); if( llength( newlp) == 0) return( EOF); curwp->w_doto = n; ldelete( 1, FALSE); linsert( 1, ' '); *clp = curwp->w_dotp; } return( lgetc( *clp, n)); } /* Break line on spaces. Back-over whatever precedes the point on the current * line and stop on the first space or the beginning of the line. If we * reach the beginning of the line, do nothing. * Otherwise, break the line at the space, eat previous spaces, and jump * back to the end of the word. * Returns TRUE on success, FALSE on errors. */ wrapword() { register int wo, spo, oldo; LINE *oldp; oldp = curwp->w_dotp; oldo = curwp->w_doto; for( spo = oldo; spo > 0; --spo) { if( lgetc( oldp, spo) == ' ') break; } if( spo) wo = spo + 1; /* wo points at start of word */ else /* can't do it */ return( FALSE); for( ; spo > 0; --spo) { if( lgetc( oldp, spo) != ' ') break; } if( spo) spo = spo + 1; /* spo points at start of spaces */ else /* can't do it */ return( FALSE); /* delete spaces and insert new line */ curwp->w_doto = spo; forwdel( FALSE, wo - spo); newline( FALSE, 1); if( oldo > spo) /* adjust point */ curwp->w_doto = oldo - wo; return( TRUE); } /* * Move the cursor backward by "n" words. All of the details of motion are * performed by the "backchar" and "forwchar" routines. Error if you try to * move beyond the buffers. */ backword(f, n) { if (n < 0) return (forwword(f, -n)); if (backchar(FALSE, 1) == FALSE) return (FALSE); while (n--) { while (inword() == FALSE) { if (backchar(FALSE, 1) == FALSE) return (FALSE); } while (inword() != FALSE) { if (backchar(FALSE, 1) == FALSE) return (FALSE); } } return (forwchar(FALSE, 1)); } /* * Move the cursor forward by the specified number of words. All of the motion * is done by "forwchar". Error if you try and move beyond the buffer's end. */ forwword(f, n) { if (n < 0) return (backword(f, -n)); while (n--) { while (inword() == FALSE) { if (forwchar(FALSE, 1) == FALSE) return (FALSE); } while (inword() != FALSE) { if (forwchar(FALSE, 1) == FALSE) return (FALSE); } } return (TRUE); } /* * Move the cursor forward by the specified number of words. As you move, * convert any characters to upper case. Error if you try and move beyond the * end of the buffer. Bound to "M-U". */ upperword(f, n) { register int c; if (n < 0) return (FALSE); while (n--) { while (inword() == FALSE) { if (forwchar(FALSE, 1) == FALSE) return (FALSE); } while (inword() != FALSE) { c = lgetc(curwp->w_dotp, curwp->w_doto); if (c>='a' && c<='z') { c -= 'a'-'A'; lputc(curwp->w_dotp, curwp->w_doto, c); lchange(WFHARD); } if (forwchar(FALSE, 1) == FALSE) return (FALSE); } } return (TRUE); } /* * Move the cursor forward by the specified number of words. As you move * convert characters to lower case. Error if you try and move over the end of * the buffer. Bound to "M-L". */ lowerword(f, n) { register int c; if (n < 0) return (FALSE); while (n--) { while (inword() == FALSE) { if (forwchar(FALSE, 1) == FALSE) return (FALSE); } while (inword() != FALSE) { c = lgetc(curwp->w_dotp, curwp->w_doto); if (c>='A' && c<='Z') { c += 'a'-'A'; lputc(curwp->w_dotp, curwp->w_doto, c); lchange(WFHARD); } if (forwchar(FALSE, 1) == FALSE) return (FALSE); } } return (TRUE); } /* * Move the cursor forward by the specified number of words. As you move * convert the first character of the word to upper case, and subsequent * characters to lower case. Error if you try and move past the end of the * buffer. Bound to "M-C". */ capword(f, n) { register int c; if (n < 0) return (FALSE); while (n--) { while (inword() == FALSE) { if (forwchar(FALSE, 1) == FALSE) return (FALSE); } if (inword() != FALSE) { c = lgetc(curwp->w_dotp, curwp->w_doto); if (c>='a' && c<='z') { c -= 'a'-'A'; lputc(curwp->w_dotp, curwp->w_doto, c); lchange(WFHARD); } if (forwchar(FALSE, 1) == FALSE) return (FALSE); while (inword() != FALSE) { c = lgetc(curwp->w_dotp, curwp->w_doto); if (c>='A' && c<='Z') { c += 'a'-'A'; lputc(curwp->w_dotp, curwp->w_doto, c); lchange(WFHARD); } if (forwchar(FALSE, 1) == FALSE) return (FALSE); } } } return (TRUE); } /* * Kill forward by "n" words. Remember the location of dot. Move forward by * the right number of words. Put dot back where it was and issue the kill * command for the right number of characters. Bound to "M-D". */ delfword(f, n) { register int size; register LINE *dotp; register int doto; if (n < 0) return (FALSE); dotp = curwp->w_dotp; doto = curwp->w_doto; size = 0; while (n--) { while (inword() == FALSE) { if (forwchar(FALSE, 1) == FALSE) return (FALSE); ++size; } while (inword() != FALSE) { if (forwchar(FALSE, 1) == FALSE) return (FALSE); ++size; } } curwp->w_dotp = dotp; curwp->w_doto = doto; return (ldelete(size, TRUE)); } /* * Kill backwards by "n" words. Move backwards by the desired number of words, * counting the characters. When dot is finally moved to its resting place, * fire off the kill command. Bound to "M-Rubout" and to "M-Backspace". */ delbword(f, n) { register int size; if (n < 0) return (FALSE); if (backchar(FALSE, 1) == FALSE) return (FALSE); size = 0; while (n--) { while (inword() == FALSE) { if (backchar(FALSE, 1) == FALSE) return (FALSE); ++size; } while (inword() != FALSE) { if (backchar(FALSE, 1) == FALSE) return (FALSE); ++size; } } if (forwchar(FALSE, 1) == FALSE) return (FALSE); return (ldelete(size, TRUE)); } /* * Return TRUE if the character at dot is a character that is considered to be * part of a word. The word character list is hard coded. Should be setable. */ inword() { register int c; if (curwp->w_doto == llength(curwp->w_dotp)) return (FALSE); c = lgetc(curwp->w_dotp, curwp->w_doto); if (c>='a' && c<='z') return (TRUE); if (c>='A' && c<='Z') return (TRUE); if (c>='0' && c<='9') return (TRUE); if (c=='$' || c=='_') /* For identifiers */ return (TRUE); return (FALSE); }