home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / word.c < prev    next >
C/C++ Source or Header  |  1998-09-03  |  15KB  |  647 lines

  1. /*
  2.  * The routines in this file implement commands that work word or a
  3.  * paragraph at a time.  There are all sorts of word mode commands.  If I
  4.  * do any sentence mode commands, they are likely to be put in this file.
  5.  *
  6.  * $Header: /usr/build/vile/vile/RCS/word.c,v 1.63 1998/09/03 22:46:33 cmorgan Exp $
  7.  *
  8.  */
  9.  
  10. #include    "estruct.h"
  11. #include    "edef.h"
  12. #include    "nefunc.h"
  13.  
  14. /* Word wrap on n-spaces. Back-over whatever precedes the point on the current
  15.  * line and stop on the first word-break or the beginning of the line. If we
  16.  * reach the beginning of the line, jump back to the end of the word and start
  17.  * a new line.    Otherwise, break the line at the word-break, eat it, and jump
  18.  * back to the end of the word.
  19.  * Returns TRUE on success, FALSE on errors.
  20.  */
  21. int
  22. wrapword(int f, int n)
  23. {
  24.     register int cnt = 0;    /* size of word wrapped to next line */
  25.     register int c;        /* character temporary */
  26.     B_COUNT    to_delete;
  27.     C_NUM to_append = 0;
  28.  
  29.     /* Back up from the <NL> 1 char */
  30.     if (!backchar(FALSE, 1))
  31.         return(FALSE);
  32.  
  33.     /* If parameter given, delete the trailing white space.  This is done
  34.      * to support a vi-like wrapmargin mode (see insert.c).  Unlike vi,
  35.      * we're deleting all of the trailing whitespace; vi deletes only the
  36.      * portion that was added during the current insertion.
  37.      *
  38.      * If there's no trailing whitespace, and we're not inserting blanks
  39.      * (n!=0), try to split the line at the last embedded whitespace.
  40.      */
  41.     if (f) {
  42.         register LINE *lp = DOT.l;
  43.         to_delete = 0L;
  44.         if (DOT.o >= 0 && !n && !isSpace(lgetc(lp,DOT.o))) {
  45.             for (c = DOT.o; c >= 0; c--) {
  46.                 if (isSpace(lgetc(lp,c))) {
  47.                     to_append = n;
  48.                     cnt = (DOT.o - c);
  49.                     DOT.o = c;
  50.                     break;
  51.                 }
  52.             }
  53.         }
  54.         for (c = DOT.o; c >= 0; c--) {
  55.             if (isSpace(lgetc(lp,c))) {
  56.                 to_delete++;
  57.                 DOT.o = c;
  58.             } else {
  59.                 break;
  60.             }
  61.         }
  62.         if (to_delete == 0)
  63.             DOT.o++;
  64.     } else {
  65.         /* Back up until we aren't in a word, make sure there is a
  66.          * break in the line
  67.          */
  68.         while (c = char_at(DOT), !isSpace(c)) {
  69.             cnt++;
  70.             if (!backchar(FALSE, 1))
  71.                 return(FALSE);
  72.             /* if we make it to the beginning, start a new line */
  73.             if (DOT.o == 0) {
  74.                 (void)gotoeol(FALSE, 0);
  75.                 return(lnewline());
  76.             }
  77.         }
  78.         to_delete = 1L;
  79.     }
  80.  
  81.     /* delete the forward white space */
  82.     if (!ldelete(to_delete, FALSE))
  83.         return(FALSE);
  84.  
  85.     /* put in a end of line */
  86.     if (!newline(FALSE,1))
  87.         return FALSE;
  88.  
  89.     /* and past the first word */
  90.     while (cnt-- > 0) {
  91.         if (forwchar(FALSE, 1) == FALSE)
  92.             return(FALSE);
  93.     }
  94.     if (to_append > 0)
  95.         linsert(to_append, ' ');
  96.     return(TRUE);
  97. }
  98.  
  99.  
  100. /*
  101.  * Implements the vi "w" command.
  102.  *
  103.  * Move the cursor forward by the specified number of words. All of the motion
  104.  * is done by "forwchar". Error if you try and move beyond the buffer's end.
  105.  *
  106.  * Returns of SORTOFTRUE result if we're doing a non-delete operation.
  107.  * Whitespace after a word is always included on deletes (and non-operations,
  108.  * of course), but only on intermediate words for other operations, for
  109.  * example.  The last word of non-delete ops does _not_ include its whitespace.
  110.  */
  111. int
  112. forwviword(int f, int n)
  113. {
  114.     int s;
  115.  
  116.     if (n < 0)
  117.         return (backword(f, -n));
  118.     setchartype();
  119.     if (forwchar(TRUE, 1) == FALSE)
  120.         return (FALSE);
  121.     while (n--) {
  122.         int any = 0;
  123.         while (((s = isnewviwordf()) == FALSE) ||
  124.                 (s == SORTOFTRUE && n != 0)) {
  125.             if (forwchar(TRUE, 1) == FALSE)
  126.                 return (any != 0);
  127.             any++;
  128.         }
  129.     }
  130.     return TRUE;
  131. }
  132.  
  133. /*
  134.  * Implements the vi "W" command.
  135.  *
  136.  * Move the cursor forward by the specified number of words. All of the motion
  137.  * is done by "forwchar". Error if you try and move beyond the buffer's end.
  138.  */
  139. int
  140. forwword(int f, int n)
  141. {
  142.     int s;
  143.  
  144.     if (n < 0)
  145.         return (backword(f, -n));
  146.     setchartype();
  147.     if (forwchar(TRUE, 1) == FALSE)
  148.         return (FALSE);
  149.     while (n--) {
  150.         int any = 0;
  151.         while (((s = isnewwordf()) == FALSE) ||
  152.                 (s == SORTOFTRUE && n != 0)) {
  153.             if (forwchar(TRUE, 1) == FALSE)
  154.                 return (any != 0);
  155.             any++;
  156.         }
  157.     }
  158.     return(TRUE);
  159. }
  160.  
  161. /*
  162.  * Implements the vi "e" command.
  163.  *
  164.  * Move the cursor forward by the specified number of words. All of the motion
  165.  * is done by "forwchar". Error if you try and move beyond the buffer's end.
  166.  */
  167. int
  168. forwviendw(int f, int n)
  169. {
  170.     int s = FALSE;
  171.     if (!f)
  172.         n = 1;
  173.     else if (n <= 0)
  174.         return (FALSE);
  175.     if (forwchar(TRUE, 1) == FALSE)
  176.         return (FALSE);
  177.     setchartype();
  178.     while (n--) {
  179.         int    any = 0;
  180.         while ((s = isendviwordf()) == FALSE) {
  181.             if (forwchar(TRUE, 1) == FALSE)
  182.                 return (any != 0);
  183.             any++;
  184.         }
  185.  
  186.     }
  187.     if (s == SORTOFTRUE)
  188.         return TRUE;
  189.     else
  190.         return backchar(FALSE, 1);
  191. }
  192.  
  193. /*
  194.  * Implements the vi "E" command.
  195.  *
  196.  * Move the cursor forward by the specified number of words. All of the motion
  197.  * is done by "forwchar". Error if you try and move beyond the buffer's end.
  198.  */
  199. int
  200. forwendw(int f, int n)
  201. {
  202.     int s = FALSE;
  203.     if (!f)
  204.         n = 1;
  205.     else if (n <= 0)
  206.         return (FALSE);
  207.     if (forwchar(TRUE, 1) == FALSE)
  208.         return (FALSE);
  209.     setchartype();
  210.     while (n--) {
  211.         int    any = 0;
  212.         while ((s = isendwordf()) == FALSE) {
  213.             if (forwchar(TRUE, 1) == FALSE)
  214.                 return (any != 0);
  215.             any++;
  216.         }
  217.  
  218.     }
  219.     if (s == SORTOFTRUE)
  220.         return TRUE;
  221.     else
  222.         return backchar(FALSE, 1);
  223. }
  224.  
  225. /*
  226.  * Implements the vi "b" command.
  227.  *
  228.  * Move the cursor backward by "n" words. All of the details of motion are
  229.  * performed by the "backchar" and "forwchar" routines. Error if you try to
  230.  * move beyond the buffers.
  231.  */
  232. int
  233. backviword(int f, int n)
  234. {
  235.     if (n < 0)
  236.         return (forwword(f, -n));
  237.     if (backchar(FALSE, 1) == FALSE)
  238.         return (FALSE);
  239.     setchartype();
  240.     while (n--) {
  241.         int    any = 0;
  242.         while (isnewviwordb() == FALSE) {
  243.             any++;
  244.             if (backchar(FALSE, 1) == FALSE)
  245.                 return (any != 0);
  246.         }
  247.     }
  248.     return (forwchar(TRUE, 1));
  249. }
  250.  
  251. /*
  252.  * Implements the vi "B" command.
  253.  *
  254.  * Move the cursor backward by "n" words. All of the details of motion are
  255.  * performed by the "backchar" and "forwchar" routines. Error if you try to
  256.  * move beyond the buffers.
  257.  */
  258. int
  259. backword(int f, int n)
  260. {
  261.     if (n < 0)
  262.         return (forwword(f, -n));
  263.     if (backchar(FALSE, 1) == FALSE)
  264.         return (FALSE);
  265.     setchartype();
  266.     while (n--) {
  267.         int    any = 0;
  268.         while (isnewwordb() == FALSE) {
  269.             any++;
  270.             if (backchar(FALSE, 1) == FALSE)
  271.                 return (any != 0);
  272.         }
  273.     }
  274.     return (forwchar(TRUE, 1));
  275. }
  276.  
  277. int
  278. joinregion(void)
  279. {
  280.     register int status;
  281.     register int doto, c;
  282.     LINE    *end;
  283.     REGION    region;
  284.     int    done = FALSE;
  285.  
  286.     if ((status = getregion(®ion)) == TRUE
  287.      && (status = !is_last_line(region.r_orig, curbp)) == TRUE) {
  288.  
  289.         DOT = region.r_orig;
  290.         end = region.r_end.l;
  291.         regionshape = EXACT;
  292.  
  293.         while (!done && status == TRUE) {
  294.             c = EOS;
  295.             (void)gotoeol(FALSE,1);
  296.             if (DOT.o > 0)
  297.                 c = lgetc(DOT.l, DOT.o-1);
  298.             (void)setmark();
  299.             if (forwline(FALSE,1) != TRUE)
  300.                 break;
  301.             (void)firstnonwhite(FALSE,1);
  302.  
  303.             done = ((DOT.l == end) || (lforw(DOT.l) == end));
  304.             if (killregionmaybesave(FALSE) != TRUE)
  305.                 break;
  306.  
  307.             doto = DOT.o;
  308.             if (doto == 0)
  309.                 /*EMPTY*/; /* join at column 0 to empty line */
  310.             else if (doto < llength(DOT.l)) {
  311.                 if (lgetc(DOT.l, doto) == ')')
  312.                     /*EMPTY*/; /* join after parentheses */
  313.                 else if (lgetc(DOT.l, doto-1) == '.')
  314.                     status = linsert(2,' ');
  315.                 else if (!isSpace(c))
  316.                     status = linsert(1,' ');
  317.             }
  318.         }
  319.     }
  320.     return status;
  321. }
  322.  
  323. int
  324. joinlines(int f, int n)
  325. {
  326.     havemotion = &f_godotplus;
  327.     return(operjoin(f,n));
  328. }
  329.  
  330. #if OPT_FORMAT
  331. static int
  332. dot_at_section_break(void)
  333. {
  334.     regexp *expP = b_val_rexp(curbp,VAL_PARAGRAPHS)->reg;
  335.     regexp *expC = b_val_rexp(curbp,VAL_COMMENTS)->reg;
  336.  
  337.     return (lregexec(expP, DOT.l, 0, llength(DOT.l)) ||
  338.         lregexec(expC, DOT.l, 0, llength(DOT.l)) );
  339. }
  340.  
  341. /* returns the length of the comment-prefix, if it matches, otherwise -1 */
  342. static int
  343. comment_prefix (void)
  344. {
  345.     regexp *expP = b_val_rexp(curbp,VAL_CMT_PREFIX)->reg;
  346.     int result = -1;
  347.     if (lregexec(expP, DOT.l, 0, llength(DOT.l))) {
  348.         result = (int)(expP->endp[0] - DOT.l->l_text);
  349.     }
  350.     return result;
  351. }
  352.  
  353.  
  354. #define fmt_insert(count,chr) \
  355.         if ((s = linsert(count,chr)) != TRUE) \
  356.             return s; \
  357.         else \
  358.             clength += count
  359.  
  360. #define fmt_c_preprocessor(cp) \
  361.         (tb_length(*cp) == 1 \
  362.         && *tb_values(*cp) == '#' \
  363.         && is_c_mode(curbp))
  364.  
  365. #define word_finishes_c_comment(wp) \
  366.         (tb_length(*wp) >= 2 \
  367.         && memcmp("/*", tb_values(*wp), 2) == 0)
  368.  
  369. static int
  370. do_formatting(TBUFF **wp, TBUFF **cp)
  371. {
  372.     register int c;            /* current char during scan    */
  373.     register int clength;        /* position on line during fill    */
  374.     register int plength;        /* prefix to ignore during fill    */
  375.     register ALLOC_T i;        /* index during word copy    */
  376.     register int newlen;        /* tentative new line length    */
  377.     register int finished;        /* Are we at the End-Of-Paragraph? */
  378.     register int firstflag;        /* first word? (needs no space)    */
  379.     register int is_comment;    /* doing a comment block?    */
  380.     register int at_nl = TRUE;    /* just saw a newline?        */
  381.     register LINEPTR pastline;    /* pointer to line just past EOP */
  382.     register int sentence;        /* was the last char a period?    */
  383.     int secondindent;
  384.     int s;
  385.  
  386.     if (!sameline(MK, DOT)) {
  387.         REGION region;
  388.  
  389.         if (getregion(®ion) != TRUE)
  390.             return FALSE;
  391.         if (sameline(region.r_orig, MK))
  392.             swapmark();
  393.     }
  394.     pastline = MK.l;
  395.     if (pastline != buf_head(curbp))
  396.         pastline = lforw(pastline);
  397.  
  398.      finished = FALSE;
  399.      while (finished != TRUE) {  /* i.e. is FALSE or SORTOFTRUE */
  400.         if (DOT.l == pastline)    /* FIXME */
  401.             return setmark();
  402.         while (dot_at_section_break()) {
  403.             DOT.l = lforw(DOT.l);
  404.             if (DOT.l == pastline)
  405.                 return setmark();
  406.         }
  407.  
  408.         secondindent = indentlen(DOT.l);
  409.  
  410.         /* go forward to get the indent for the second
  411.             and following lines */
  412.         DOT.l = lforw(DOT.l);
  413.  
  414.         if (DOT.l != pastline) {
  415.             secondindent = indentlen(DOT.l);
  416.         }
  417.  
  418.         /* and back where we should be */
  419.         DOT.l = lback(DOT.l);
  420.         (void)firstnonwhite(FALSE,1);
  421.  
  422.         clength = indentlen(DOT.l);
  423.         tb_init(wp, EOS);
  424.         tb_init(cp, EOS);
  425.         sentence = FALSE;
  426.  
  427.         c = char_at(DOT);
  428.         is_comment = FALSE;
  429.         if ((plength = comment_prefix()) >= 0) {
  430.             is_comment = TRUE;
  431.             tb_bappend(cp,
  432.                 DOT.l->l_text + DOT.o,
  433.                 (ALLOC_T)(plength - DOT.o));
  434.         }
  435.  
  436.         /* scan through lines, filling words */
  437.         firstflag = TRUE;
  438.         finished = FALSE;
  439.         plength -= DOT.o;
  440.         while (finished == FALSE) { /* i.e. is not TRUE  */
  441.                         /* or SORTOFTRUE */
  442.             if (interrupted()) return ABORT;
  443.  
  444.             /* get the next character */
  445.             if (is_at_end_of_line(DOT)) {
  446.                 c = ' ';
  447.                 DOT.l = lforw(DOT.l);
  448.                 if (DOT.l == pastline) {
  449.                     finished = TRUE;
  450.                 } else if (dot_at_section_break()) {
  451.                     finished = SORTOFTRUE;
  452.                 }
  453.  
  454.                 if ((s = comment_prefix ()) >= 0) {
  455.                     int save = DOT.o;
  456.  
  457.                     (void)firstnonwhite(FALSE,1);
  458.                     s -= DOT.o;
  459.  
  460.                     if (s != (int) tb_length(*cp)
  461.                      || (s > 0
  462.                       && memcmp(tb_values(*cp),
  463.                         DOT.l->l_text + DOT.o,
  464.                         s))) {
  465.                         finished = SORTOFTRUE;
  466.                     }
  467.  
  468.                     if (finished == FALSE) {
  469.                         plength = s;
  470.                         tb_init(cp, EOS);
  471.                         if (plength > 0) {
  472.                             tb_bappend(cp,
  473.                                 DOT.l->l_text + DOT.o,
  474.                                 (ALLOC_T)(plength));
  475.                         }
  476.                         if (DOT.l != pastline
  477.                          && !dot_at_section_break()) {
  478.                             int spcs = DOT.o;
  479.                             DOT.o = 0;
  480.                             s = ldelete(spcs, FALSE);
  481.                             if (s != TRUE) return s;
  482.                         }
  483.                     }
  484.                     DOT.o = save;
  485.                 }
  486.                 DOT.l = lback(DOT.l);
  487.                 at_nl = TRUE;
  488.             } else {
  489.                 c = char_at(DOT);
  490.                 if (at_nl && ((plength-- > 0) || isSpace(c)))
  491.                     c = ' ';
  492.                 else
  493.                     at_nl = FALSE;
  494.             }
  495.  
  496.             /* and then delete it */
  497.             if (finished == FALSE) {
  498.                 s = ldelete(1L, FALSE);
  499.                 if (s != TRUE) return s;
  500.             }
  501.  
  502.             /* if not a separator, just add it in */
  503.             if (!isBlank(c)) {
  504.                 /* was it the end of a "sentence"? */
  505.                 sentence = ((c == '.' || c == '?' ||
  506.                         c == ':' || c == '!') &&
  507.                         global_g_val(GMDSPACESENT));
  508.                 tb_append(wp, c);
  509.             } else if (tb_length(*wp)) {
  510.                 /* at a word break with a word waiting */
  511.                 /* calculate tentative new length
  512.                             with word added */
  513.                 newlen = clength + 1 + tb_length(*wp);
  514.                 if (newlen <= b_val(curbp,VAL_FILL)) {
  515.                     /* add word to current line */
  516.                     if (!firstflag) {
  517.                         fmt_insert(1, ' ');
  518.                     }
  519.                 } else {
  520.                     /* fix the leading indent now, if
  521.                         some spaces should be tabs */
  522.                     if (b_val(curbp,MDTABINSERT))
  523.                         entabline((void *)TRUE, 0, 0);
  524.                             if (lnewline() == FALSE)
  525.                         return FALSE;
  526.                     clength = 0;
  527.                         fmt_insert(secondindent,' ');
  528.                     firstflag = TRUE;
  529.                 }
  530.  
  531.                 if (firstflag
  532.                  && is_comment
  533.                  && !word_finishes_c_comment(wp)) {
  534.                     for (i=0; i < tb_length(*cp); i++) {
  535.                         fmt_insert(1, tb_values(*cp)[i]);
  536.                     }
  537.                     if (!fmt_c_preprocessor(cp)) {
  538.                         fmt_insert(1, ' ');
  539.                     }
  540.                 }
  541.                 firstflag = FALSE;
  542.  
  543.                 /* and add the word in in either case */
  544.                 for (i=0; i < tb_length(*wp); i++) {
  545.                     fmt_insert(1, tb_values(*wp)[i]);
  546.                 }
  547.                 if (finished == FALSE && sentence) {
  548.                     fmt_insert(1, ' ');
  549.                 }
  550.                 tb_init(wp, EOS);
  551.             }
  552.         }
  553.         /* catch the case where we're done with a line not because
  554.           there's no more room, but because we're done processing a
  555.           section or the region */
  556.         if (b_val(curbp,MDTABINSERT))
  557.             entabline((void *)TRUE, 0, 0);
  558.         DOT.l = lforw(DOT.l);
  559.     }
  560.     return setmark();
  561. }
  562.  
  563. int
  564. formatregion(void)
  565. {
  566.     int s = FALSE;
  567.     TBUFF *wp = 0;        /* word buffer */
  568.     TBUFF *cp = 0;        /* comment-prefix buffer */
  569.  
  570.     if ((wp = tb_init(&wp, EOS)) != 0
  571.      && (wp = tb_init(&wp, EOS)) != 0) {
  572.         s = do_formatting(&wp, &cp);
  573.         tb_free(&wp);
  574.         tb_free(&cp);
  575.     }
  576.     return s;
  577. }
  578. #endif /* OPT_FORMAT */
  579.  
  580.  
  581. #if    OPT_WORDCOUNT
  582. /*    wordcount:    count the # of words in the marked region,
  583.             along with average word sizes, # of chars, etc,
  584.             and report on them.            */
  585. /*ARGSUSED*/
  586. int
  587. wordcount(int f GCC_UNUSED, int n GCC_UNUSED)
  588. {
  589.     register LINE *lp;    /* current line to scan */
  590.     register int offset;    /* current char to scan */
  591.     long size;        /* size of region left to count */
  592.     register int ch;    /* current character to scan */
  593.     register int wordflag;    /* are we in a word now? */
  594.     register int lastword;    /* were we just in a word? */
  595.     long nwords;        /* total # of words */
  596.     long nchars;        /* total number of chars */
  597.     int nlines;        /* total number of lines in region */
  598.     int avgch;        /* average number of chars/word */
  599.     int status;        /* status return code */
  600.     REGION region;        /* region to look at */
  601.  
  602.     /* make sure we have a region to count */
  603.     if ((status = getregion(®ion)) != TRUE) {
  604.         return(status);
  605.     }
  606.     lp     = region.r_orig.l;
  607.     offset = region.r_orig.o;
  608.     size   = region.r_size;
  609.  
  610.     /* count up things */
  611.     lastword = FALSE;
  612.     nchars = 0L;
  613.     nwords = 0L;
  614.     nlines = 0;
  615.     while (size--) {
  616.  
  617.         /* get the current character */
  618.         if (offset == llength(lp)) {    /* end of line */
  619.             ch = '\n';
  620.             lp = lforw(lp);
  621.             offset = 0;
  622.             ++nlines;
  623.         } else {
  624.             ch = lgetc(lp, offset);
  625.             ++offset;
  626.         }
  627.  
  628.         /* and tabulate it */
  629.         if (((wordflag = isident(ch)) != 0) && !lastword)
  630.             ++nwords;
  631.         lastword = wordflag;
  632.         ++nchars;
  633.     }
  634.  
  635.     /* and report on the info */
  636.     if (nwords > 0L)
  637.         avgch = (int)((100L * nchars) / nwords);
  638.     else
  639.         avgch = 0;
  640.  
  641.     mlforce("lines %d, words %D, chars %D, avg chars/word %f",
  642.         nlines, nwords, nchars, avgch);
  643.  
  644.     return(TRUE);
  645. }
  646. #endif
  647.