home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / search.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  23.5 KB  |  1,106 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony
  8.  *                            G. R. (Fred) Walter     watmath!watcgl!grwalter
  9.  */
  10. /*
  11.  * search.c: code for normal mode searching commands
  12.  */
  13.  
  14. #include "vim.h"
  15. #include "globals.h"
  16. #include "proto.h"
  17. #include "param.h"
  18. #include "ops.h"        /* for mincl */
  19.  
  20. /* modified Henry Spencer's regular expression routines */
  21. #include "regexp.h"
  22.  
  23. static int inmacro __ARGS((char *, char *));
  24. static int cls __ARGS((void));
  25.  
  26. /*
  27.  * This file contains various searching-related routines. These fall into
  28.  * three groups:
  29.  * 1. string searches (for /, ?, n, and N)
  30.  * 2. character searches within a single line (for f, F, t, T, etc)
  31.  * 3. "other" kinds of searches like the '%' command, and 'word' searches.
  32.  */
  33.  
  34. /*
  35.  * String searches
  36.  *
  37.  * The string search functions are divided into two levels:
  38.  * lowest:    searchit(); called by dosearch() and docmdline().
  39.  * Highest: dosearch(); changes Curpos, called by normal().
  40.  *
  41.  * The last search pattern is remembered for repeating the same search.
  42.  * This pattern is shared between the :g, :s, ? and / commands.
  43.  * This is in myregcomp().
  44.  *
  45.  * The actual string matching is done using a heavily modified version of
  46.  * Henry Spencer's regular expression library.
  47.  */
  48.  
  49. static char     *search_pattern = NULL;  /* previous search pattern */
  50. static int        want_start;              /* looking for start of line? */
  51.  
  52. /*
  53.  * translate search pattern for regcomp()
  54.  */
  55.     regexp *
  56. myregcomp(pat)
  57.     char *pat;
  58. {
  59.     regexp *retval;
  60.  
  61.     if (pat == NULL || *pat == NUL)     /* use previous search pattern */
  62.     {
  63.         if (search_pattern == NULL)
  64.         {
  65.             emsg(e_noprevre);
  66.             return (regexp *) NULL;
  67.         }
  68.         pat = search_pattern;
  69.     }
  70.     else
  71.     {
  72.         if (search_pattern != NULL)
  73.             free(search_pattern);
  74.         search_pattern = strsave(pat);
  75.         reg_magic = p_magic;        /* Magic sticks with the r.e. */
  76.     }
  77.     want_start = (*pat == '^');        /* looking for start of line? */
  78.     reg_ic = p_ic;                /* tell the regexec routine how to search */
  79.     retval = regcomp(pat);
  80.     return retval;
  81. }
  82.  
  83. /*
  84.  * lowest level search function.
  85.  * Search for 'count'th occurrence of 'str' in direction 'dir'.
  86.  * Start at position 'pos' and return the found position in 'pos'.
  87.  * Return 1 for success, 0 for failure.
  88.  */
  89.     int
  90. searchit(pos, dir, str, count, end)
  91.     FPOS    *pos;
  92.     int     dir;
  93.     char    *str;
  94.     long    count;
  95.     int        end;
  96. {
  97.     int             found;
  98.     linenr_t        lnum;
  99.     linenr_t        startlnum;
  100.     regexp           *prog;
  101.     register char  *s;
  102.     char           *ptr;
  103.     register int    i;
  104.     register char  *match, *matchend;
  105.     int             loop;
  106.  
  107.     if ((prog = myregcomp(str)) == NULL)
  108.     {
  109.         emsg(e_invstring);
  110.         return 0;
  111.     }
  112. /*
  113.  * find the string
  114.  */
  115.     found = 1;
  116.     while (count-- && found)    /* stop after count matches, or no more matches */
  117.     {
  118.         startlnum = pos->lnum;    /* remember start of search for detecting no match */
  119.         found = 0;                /* default: not found */
  120.  
  121.         i = pos->col + dir;     /* search starts one postition away */
  122.         lnum = pos->lnum;
  123.  
  124.         if (dir == BACKWARD)
  125.         {
  126.             if (i < 0)
  127.                 --lnum;
  128.         }
  129.  
  130.         for (loop = 0; loop != 2; ++loop)   /* do this twice if 'wrapscan' is set */
  131.         {
  132.             for ( ; lnum > 0 && lnum <= line_count; lnum += dir, i = -1)
  133.             {
  134.                 s = ptr = nr2ptr(lnum);
  135.                 if (dir == FORWARD && i > 0)    /* first line for forward search */
  136.                 {
  137.                     if (want_start || strlen(s) <= (size_t)i)   /* match not possible */
  138.                         continue;
  139.                     s += i;
  140.                 }
  141.  
  142.                 if (regexec(prog, s, dir == BACKWARD || i <= 0))
  143.                 {                            /* match somewhere on line */
  144.                     match = prog->startp[0];
  145.                     matchend = prog->endp[0];
  146.                     if (dir == BACKWARD && !want_start)
  147.                     {
  148.                         /*
  149.                          * Now, if there are multiple matches on this line, we have to
  150.                          * get the last one. Or the last one before the cursor, if we're
  151.                          * on that line.
  152.                          */
  153.                         while (regexec(prog, prog->startp[0] + 1, (int)FALSE))
  154.                         {
  155.                             if ((i >= 0) && ((prog->startp[0] - s) > i))
  156.                                 break;
  157.                             match = prog->startp[0];
  158.                             matchend = prog->endp[0];
  159.                         }
  160.  
  161.                         if ((i >= 0) && ((match - s) > i))
  162.                             continue;
  163.                     }
  164.  
  165.                     pos->lnum = lnum;
  166.                     if (end)
  167.                         pos->col = (int) (matchend - ptr - 1);
  168.                     else
  169.                         pos->col = (int) (match - ptr);
  170.                     found = 1;
  171.                     break;
  172.                 }
  173.                 /* breakcheck is slow, do it only once in 16 lines */
  174.                 if ((lnum & 15) == 0)
  175.                     breakcheck();       /* stop if ctrl-C typed */
  176.                 if (got_int)
  177.                     break;
  178.  
  179.                 if (loop && lnum == startlnum)  /* if second loop stop where started */
  180.                     break;
  181.             }
  182.     /* stop the search if wrapscan isn't set, after an interrupt and after a match */
  183.             if (!p_ws || got_int || found)
  184.                 break;
  185.  
  186.             if (dir == BACKWARD)    /* start second loop at the other end */
  187.                 lnum = line_count;
  188.             else
  189.                 lnum = 1;
  190.         }
  191.         if (got_int)
  192.             break;
  193.     }
  194.  
  195.     free((char *) prog);
  196.  
  197.     if (!found)             /* did not find it */
  198.     {
  199.         if (got_int)
  200.                 emsg(e_interr);
  201.         else
  202.                 emsg(e_patnotf);
  203.         return 0;
  204.     }
  205.  
  206.     return 1;
  207. }
  208.  
  209. /*
  210.  * Highest level string search function.
  211.  * Search for the 'count'th occurence of string 'str' in direction 'dirc'
  212.  *                    If 'dirc' is 0: use previous dir.
  213.  * If 'str' is 0 or 'str' is empty: use previous string.
  214.  *              If 'reverse' is TRUE: go in reverse of previous dir.
  215.  *                 If 'echo' is TRUE: echo the search command
  216.  */
  217.     int
  218. dosearch(dirc, str, reverse, count, echo)
  219.     int                dirc;
  220.     char           *str;
  221.     int                reverse;
  222.     long            count;
  223.     int                echo;
  224. {
  225.     FPOS            pos;        /* position of the last match */
  226.     char            *searchstr;
  227.     static int        lastsdir = '/';    /* previous search direction */
  228.     static int        lastoffline;/* previous/current search has line offset */
  229.     static int        lastend;    /* previous/current search set cursor at end */
  230.     static long     lastoff;    /* previous/current line or char offset */
  231.     static int        nosetpm;    /* do not call setpcmark() */
  232.     register char    *p;
  233.     register long    c;
  234.     char            *dircp = NULL;
  235.  
  236.     if (dirc == 0)
  237.         dirc = lastsdir;
  238.     else
  239.         lastsdir = dirc;
  240.     if (reverse)
  241.     {
  242.         if (dirc == '/')
  243.             dirc = '?';
  244.         else
  245.             dirc = '/';
  246.     }
  247.     searchstr = str;
  248.                                     /* use previous string */
  249.     if (str == NULL || *str == NUL || *str == dirc)
  250.     {
  251.         if (search_pattern == NULL)
  252.         {
  253.             emsg(e_noprevre);
  254.             return 0;
  255.         }
  256.         searchstr = "";                /* will use search_pattern in myregcomp() */
  257.     }
  258.     if (str != NULL && *str != NUL)    /* look for (new) offset */
  259.     {
  260.         /* If there is a matching '/' or '?', toss it */
  261.         for (p = str; *p; ++p)
  262.         {
  263.             if (*p == dirc)
  264.             {
  265.                 dircp = p;        /* remember where we put the NUL */
  266.                 *p++ = NUL;
  267.                 break;
  268.             }
  269.             if (*p == '\\' && p[1] != NUL)
  270.                 ++p;    /* skip next character */
  271.         }
  272.  
  273.         lastoffline = FALSE;
  274.         lastend = FALSE;
  275.         nosetpm = FALSE;
  276.         lastoff = 0;
  277.         switch (*p)
  278.         {
  279.             case 'n':                     /* do not call setpcmark() */
  280.                         nosetpm = TRUE;
  281.                         ++p;
  282.                         break;
  283.             case '+':
  284.             case '-':                   /* got a line offset */
  285.                         lastoffline = TRUE;
  286.                         break;
  287.             case 'e':                   /* position cursor at end */
  288.                         lastend = TRUE;
  289.             case 's':                   /* got a character offset from start */
  290.                         ++p;
  291.         }
  292.         if (*p == '+' || *p == '-')     /* got an offset */
  293.         {
  294.             if (isdigit(*(p + 1)))
  295.                 lastoff = atol(p);        /* '+nr' or '-nr' */
  296.             else if (*p == '-')            /* single '-' */
  297.                 lastoff = -1;
  298.             else                        /* single '+' */
  299.                 lastoff = 1;
  300.             ++p;
  301.             while (isdigit(*p))            /* skip number */
  302.                 ++p;
  303.         }
  304.         searchcmdlen = p - str;            /* compute lenght of search command
  305.                                                         for get_address() */
  306.     }
  307.  
  308.     if (echo)
  309.     {
  310.         start_msg();
  311.         outchar(dirc);
  312.         outtrans(*searchstr == NUL ? search_pattern : searchstr, -1);
  313.         if (lastoffline || lastend || lastoff || nosetpm)
  314.         {
  315.             outchar(dirc);
  316.             if (nosetpm)
  317.                 outchar('n');
  318.             else if (lastend)
  319.                 outchar('e');
  320.             else if (!lastoffline)
  321.                 outchar('s');
  322.             if (lastoff < 0)
  323.             {
  324.                 outchar('-');
  325.                 outnum((long)-lastoff);
  326.             }
  327.             else if (lastoff > 0 || lastoffline)
  328.             {
  329.                 outchar('+');
  330.                 outnum((long)lastoff);
  331.             }
  332.         }
  333.         check_msg();
  334.  
  335.         gotocmdline(FALSE, NUL);
  336.         flushbuf();
  337.     }
  338.  
  339.     pos = Curpos;
  340.  
  341.     c = searchit(&pos, dirc == '/' ? FORWARD : BACKWARD, searchstr, count, lastend);
  342.     if (dircp)
  343.         *dircp = dirc;        /* put second '/' or '?' back for normal() */
  344.     if (!c)
  345.         return 0;
  346.  
  347.     if (!lastoffline)           /* add the character offset to the column */
  348.     {
  349.         if (lastoff > 0)        /* offset to the right, check for end of line */
  350.         {
  351.             p = pos2ptr(&pos) + 1;
  352.             c = lastoff;
  353.             while (c-- && *p++ != NUL)
  354.                 ++pos.col;
  355.         }
  356.         else                    /* offset to the left, check for start of line */
  357.         {
  358.             if ((c = pos.col + lastoff) < 0)
  359.                 c = 0;
  360.             pos.col = c;
  361.         }
  362.     }
  363.  
  364.     if (!nosetpm)
  365.         setpcmark();
  366.     Curpos = pos;
  367.     set_want_col = TRUE;
  368.  
  369.     if (!lastoffline)
  370.         return 1;
  371.  
  372. /*
  373.  * add the offset to the line number.
  374.  */
  375.     c = Curpos.lnum + lastoff;
  376.     if (c < 1)
  377.         Curpos.lnum = 1;
  378.     else if (c > line_count)
  379.         Curpos.lnum = line_count;
  380.     else
  381.         Curpos.lnum = c;
  382.     Curpos.col = 0;
  383.  
  384.     return 2;
  385. }
  386.  
  387.  
  388. /*
  389.  * Character Searches
  390.  */
  391.  
  392. /*
  393.  * searchc(c, dir, type, count)
  394.  *
  395.  * Search for character 'c', in direction 'dir'. If 'type' is 0, move to the
  396.  * position of the character, otherwise move to just before the char.
  397.  * Repeat this 'count' times.
  398.  */
  399.     int
  400. searchc(c, dir, type, count)
  401.     int             c;
  402.     register int    dir;
  403.     int             type;
  404.     long            count;
  405. {
  406.     static char     lastc = NUL;    /* last character searched for */
  407.     static int        lastcdir;        /* last direction of character search */
  408.     static int        lastctype;        /* last type of search ("find" or "to") */
  409.     register int    col;
  410.     char            *p;
  411.     int             len;
  412.  
  413.     if (c != NUL)       /* normal search: remember args for repeat */
  414.     {
  415.         lastc = c;
  416.         lastcdir = dir;
  417.         lastctype = type;
  418.     }
  419.     else                /* repeat previous search */
  420.     {
  421.         if (lastc == NUL)
  422.             return FALSE;
  423.         if (dir)        /* repeat in opposite direction */
  424.             dir = -lastcdir;
  425.         else
  426.             dir = lastcdir;
  427.     }
  428.  
  429.     p = nr2ptr(Curpos.lnum);
  430.     col = Curpos.col;
  431.     len = strlen(p);
  432.  
  433.     /*
  434.      * On 'to' searches, skip one to start with so we can repeat searches in
  435.      * the same direction and have it work right.
  436.      * REMOVED to get vi compatibility
  437.      * if (lastctype)
  438.      *    col += dir;
  439.      */
  440.  
  441.     while (count--)
  442.     {
  443.             for (;;)
  444.             {
  445.                 if ((col += dir) < 0 || col >= len)
  446.                     return FALSE;
  447.                 if (p[col] == lastc)
  448.                         break;
  449.             }
  450.     }
  451.     if (lastctype)
  452.         col -= dir;
  453.     Curpos.col = col;
  454.     return TRUE;
  455. }
  456.  
  457. /*
  458.  * "Other" Searches
  459.  */
  460.  
  461. /*
  462.  * showmatch - move the cursor to the matching paren or brace
  463.  *
  464.  * Improvement over vi: Braces inside quotes are ignored.
  465.  */
  466.     FPOS           *
  467. showmatch()
  468. {
  469.     static FPOS        pos;            /* current search position */
  470.     char            initc;            /* brace under or after the cursor */
  471.     char            findc;            /* matching brace */
  472.     char            c;
  473.     int             count = 0;        /* cumulative number of braces */
  474.     int             idx;
  475.     static char     table[6] = {'(', ')', '[', ']', '{', '}'};
  476.     int             inquote = 0;    /* non-zero when inside quotes */
  477.     register char    *linep;            /* pointer to current line */
  478.     register char    *ptr;
  479.     int                do_quotes;        /* check for quotes in current line */
  480.  
  481.     pos = Curpos;
  482.  
  483.     /*
  484.      * find the brace under or after the cursor
  485.      */
  486.     linep = nr2ptr(pos.lnum); 
  487.     for (;;)
  488.     {
  489.         initc = linep[pos.col];
  490.         if (initc == NUL)
  491.             return (FPOS *) NULL;
  492.  
  493.         for (idx = 0; idx < 6; ++idx)
  494.             if (table[idx] == initc)
  495.                 break;
  496.         if (idx != 6)
  497.             break;
  498.         ++pos.col;
  499.     }
  500.  
  501.     findc = table[idx ^ 1];        /* get matching brace */
  502.     idx &= 1;
  503.  
  504.     do_quotes = -1;
  505.     while (!got_int)
  506.     {
  507.         /*
  508.          * Go to the next position, forward or backward. We could use
  509.          * inc() and dec() here, but that is much slower
  510.          */
  511.         if (idx)                          /* backward search */
  512.         {
  513.             if (pos.col == 0)           /* at start of line, go to previous one */
  514.             {
  515.                 if (pos.lnum == 1)      /* start of file */
  516.                     break;
  517.                 --pos.lnum;
  518.                 linep = nr2ptr(pos.lnum);
  519.                 pos.col = strlen(linep);    /* put pos.col on trailing NUL */
  520.                 do_quotes = -1;
  521.             }
  522.             else
  523.                 --pos.col;
  524.         }
  525.         else                            /* forward search */
  526.         {
  527.             if (linep[pos.col] == NUL)  /* at end of line, go to next one */
  528.             {
  529.                 if (pos.lnum == line_count) /* end of file */
  530.                     break;
  531.                 ++pos.lnum;
  532.                 linep = nr2ptr(pos.lnum);
  533.                 pos.col = 0;
  534.                 do_quotes = -1;
  535.             }
  536.             else
  537.                 ++pos.col;
  538.         }
  539.  
  540.         if (do_quotes == -1)        /* count number of quotes in this line */
  541.         {
  542.                 /* we only do a breakcheck() once for every 16 lines */
  543.             if ((pos.lnum & 15) == 0)
  544.                 breakcheck();
  545.  
  546.             /*
  547.              * count the number of quotes in the line, skipping \" and '"'
  548.              */
  549.             for (ptr = linep; *ptr; ++ptr)
  550.                 if (*ptr == '"' && (ptr == linep || ptr[-1] != '\\') &&
  551.                             (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
  552.                     ++do_quotes;
  553.             do_quotes &= 1;            /* result is 1 with even number of quotes */
  554.  
  555.             /*
  556.              * If we find an uneven count, check current line and previous
  557.              * one for a '\' at the end.
  558.              */
  559.             if (!do_quotes)
  560.             {
  561.                 inquote = FALSE;
  562.                 if (ptr[-1] == '\\')
  563.                 {
  564.                     do_quotes = 1;
  565.                     if (idx)                    /* backward search */
  566.                         inquote = TRUE;
  567.                 }
  568.                 if (pos.lnum > 1)
  569.                 {
  570.                     ptr = nr2ptr(pos.lnum - 1);
  571.                     if (*ptr && *(ptr + strlen(ptr) - 1) == '\\')
  572.                     {
  573.                         do_quotes = 1;
  574.                         if (!idx)                /* forward search */
  575.                             inquote = TRUE;
  576.                     }
  577.                 }
  578.             }
  579.         }
  580.  
  581.         /*
  582.          * Things inside quotes are ignored by setting 'inquote'.
  583.          * If we find a quote without a preceding '\' invert 'inquote'.
  584.          * At the end of a line not ending in '\' we reset 'inquote'.
  585.          *
  586.          * In lines with an uneven number of quotes (without preceding '\')
  587.          * we do not know which part to ignore. Therefore we only set
  588.          * inquote if the number of quotes in a line is even,
  589.          * unless this line or the previous one ends in a '\'.
  590.          * Complicated, isn't it?
  591.          */
  592.         switch (c = linep[pos.col])
  593.         {
  594.         case NUL:
  595.             inquote = FALSE;
  596.             break;
  597.  
  598.         case '"':
  599.                 /* a quote that is preceded with a backslash is ignored */
  600.             if (do_quotes && (pos.col == 0 || linep[pos.col - 1] != '\\'))
  601.                 inquote = !inquote;
  602.             break;
  603.  
  604.         /*
  605.          * Skip things in single quotes: 'x' or '\x'.
  606.          * Be careful for single single quotes, eg jon's.
  607.          * Things like '\233' or '\x3f' are not skipped, there is never a
  608.          * brace in them.
  609.          */
  610.         case '\'':
  611.             if (idx)                        /* backward search */
  612.             {
  613.                 if (pos.col > 1)
  614.                 {
  615.                     if (linep[pos.col - 2] == '\'')
  616.                         pos.col -= 2;
  617.                     else if (linep[pos.col - 2] == '\\' && pos.col > 2 && linep[pos.col - 3] == '\'')
  618.                         pos.col -= 3;
  619.                 }
  620.             }
  621.             else if (linep[pos.col + 1])    /* forward search */
  622.             {
  623.                 if (linep[pos.col + 1] == '\\' && linep[pos.col + 2] && linep[pos.col + 3] == '\'')
  624.                     pos.col += 3;
  625.                 else if (linep[pos.col + 2] == '\'')
  626.                     pos.col += 2;
  627.             }
  628.             break;
  629.  
  630.         default:
  631.             if (!inquote)      /* only check for match outside of quotes */
  632.             {
  633.                 if (c == initc)
  634.                     count++;
  635.                 else if (c == findc)
  636.                 {
  637.                     if (count == 0)
  638.                         return &pos;
  639.                     count--;
  640.                 }
  641.             }
  642.         }
  643.     }
  644.     return (FPOS *) NULL;       /* never found it */
  645. }
  646.  
  647. /*
  648.  * findfunc(dir, what) - Find the next line starting with 'what' in direction 'dir'
  649.  *
  650.  * Return TRUE if a line was found.
  651.  */
  652.     int
  653. findfunc(dir, what, count)
  654.     int         dir;
  655.     int            what;
  656.     long        count;
  657. {
  658.     linenr_t    curr;
  659.  
  660.     curr = Curpos.lnum;
  661.  
  662.     for (;;)
  663.     {
  664.         if (dir == FORWARD)
  665.         {
  666.                 if (curr++ == line_count)
  667.                         break;
  668.         }
  669.         else
  670.         {
  671.                 if (curr-- == 1)
  672.                         break;
  673.         }
  674.  
  675.         if (*nr2ptr(curr) == what)
  676.         {
  677.             if (--count > 0)
  678.                 continue;
  679.             setpcmark();
  680.             Curpos.lnum = curr;
  681.             Curpos.col = 0;
  682.             return TRUE;
  683.         }
  684.     }
  685.  
  686.     return FALSE;
  687. }
  688.  
  689. /*
  690.  * findsent(dir, count) - Find the start of the next sentence in direction 'dir'
  691.  * Sentences are supposed to end in ".", "!" or "?" followed by white space or
  692.  * a line break. Also stop at an empty line.
  693.  * Return TRUE if the next sentence was found.
  694.  */
  695.     int
  696. findsent(dir, count)
  697.         int     dir;
  698.         long    count;
  699. {
  700.     FPOS            pos, tpos;
  701.     register int    c;
  702.     int             (*func) __PARMS((FPOS *));
  703.     int             startlnum;
  704.     int                noskip = FALSE;            /* do not skip blanks */
  705.  
  706.     pos = Curpos;
  707.     if (dir == FORWARD)
  708.         func = incl;
  709.     else
  710.         func = decl;
  711.  
  712.     while (count--)
  713.     {
  714.         /* if on an empty line, skip upto a non-empty line */
  715.         if (gchar(&pos) == NUL)
  716.         {
  717.             do
  718.                 if ((*func)(&pos) == -1)
  719.                     break;
  720.             while (gchar(&pos) == NUL);
  721.             if (dir == FORWARD)
  722.                 goto found;
  723.         }
  724.         /* if on the start of a paragraph or a section and searching
  725.          * forward, go to the next line */
  726.         else if (dir == FORWARD && pos.col == 0 && startPS(pos.lnum, NUL))
  727.         {
  728.             if (pos.lnum == line_count)
  729.                 return FALSE;
  730.             ++pos.lnum;
  731.             goto found;
  732.         }
  733.         else if (dir == BACKWARD)
  734.             decl(&pos);
  735.  
  736.         /* go back to the previous non-blank char */
  737.         while ((c = gchar(&pos)) == ' ' || c == '\t' ||
  738.                     (dir == BACKWARD && strchr(".!?)]\"'", c) != NULL && c != NUL))
  739.             if (decl(&pos) == -1)
  740.                 break;
  741.  
  742.         /* remember the line where the search started */
  743.         startlnum = pos.lnum;
  744.  
  745.         for (;;)                /* find end of sentence */
  746.         {
  747.             if ((c = gchar(&pos)) == NUL ||
  748.                             (pos.col == 0 && startPS(pos.lnum, NUL)))
  749.             {
  750.                 if (dir == BACKWARD && pos.lnum != startlnum)
  751.                     ++pos.lnum;
  752.                 break;
  753.             }
  754.             if (c == '.' || c == '!' || c == '?')
  755.             {
  756.                 tpos = pos;
  757.                 do
  758.                     if ((c = inc(&tpos)) == -1)
  759.                         break;
  760.                 while (strchr(")}\"'", c = gchar(&tpos)) != NULL && c != NUL);
  761.                 if (c == -1  || c == ' ' || c == '\t' || c == NUL)
  762.                 {
  763.                     pos = tpos;
  764.                     if (gchar(&pos) == NUL) /* skip NUL at EOL */
  765.                         inc(&pos);
  766.                     break;
  767.                 }
  768.             }
  769.             if ((*func)(&pos) == -1)
  770.             {
  771.                 if (count)
  772.                     return FALSE;
  773.                 noskip = TRUE;
  774.                 break;
  775.             }
  776.         }
  777. found:
  778.             /* skip white space */
  779.         while (!noskip && ((c = gchar(&pos)) == ' ' || c == '\t'))
  780.             if (incl(&pos) == -1)
  781.                 break;
  782.     }
  783.  
  784.     Curpos = pos;
  785.     setpcmark();
  786.     return TRUE;
  787. }
  788.  
  789. /*
  790.  * findpar(dir, count, what) - Find the next paragraph in direction 'dir'
  791.  * Paragraphs are currently supposed to be separated by empty lines.
  792.  * Return TRUE if the next paragraph was found.
  793.  * If 'what' is '{' or '}' we go to the next section.
  794.  */
  795.     int
  796. findpar(dir, count, what)
  797.     register int    dir;
  798.     long            count;
  799.     int             what;
  800. {
  801.     register linenr_t    curr;
  802.     int                    did_skip;        /* TRUE after separating lines have
  803.                                                 been skipped */
  804.     int                    first;            /* TRUE on first line */
  805.  
  806.     curr = Curpos.lnum;
  807.  
  808.     while (count--)
  809.     {
  810.         did_skip = FALSE;
  811.         for (first = TRUE; ; first = FALSE)
  812.         {
  813.                 if (*nr2ptr(curr) != NUL)
  814.                     did_skip = TRUE;
  815.  
  816.                 if (!first && did_skip && startPS(curr, what))
  817.                     break;
  818.  
  819.                 if ((curr += dir) < 1 || curr > line_count)
  820.                 {
  821.                         if (count)
  822.                                 return FALSE;
  823.                         curr -= dir;
  824.                         break;
  825.                 }
  826.         }
  827.     }
  828.     setpcmark();
  829.     Curpos.lnum = curr;
  830.     if (curr == line_count)
  831.     {
  832.         if ((Curpos.col = strlen(nr2ptr(curr))) != 0)
  833.             --Curpos.col;
  834.         mincl = TRUE;
  835.     }
  836.     else
  837.         Curpos.col = 0;
  838.     return TRUE;
  839. }
  840.  
  841. /*
  842.  * check if the string 's' is a nroff macro that is in option 'opt'
  843.  */
  844.     static int
  845. inmacro(opt, s)
  846.         char *opt;
  847.         register char *s;
  848. {
  849.         register char *macro;
  850.  
  851.         for (macro = opt; macro[0]; ++macro)
  852.         {
  853.                 if (macro[0] == s[0] && (((s[1] == NUL || s[1] == ' ')
  854.                         && (macro[1] == NUL || macro[1] == ' ')) || macro[1] == s[1]))
  855.                         break;
  856.                 ++macro;
  857.                 if (macro[0] == NUL)
  858.                         break;
  859.         }
  860.         return (macro[0] != NUL);
  861. }
  862.  
  863. /*
  864.  * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
  865.  * If 'para' is '{' or '}' only check for sections.
  866.  */
  867.     int
  868. startPS(lnum, para)
  869.     linenr_t    lnum;
  870.     int         para;
  871. {
  872.     register char *s;
  873.  
  874.     s = nr2ptr(lnum);
  875.     if (*s == para || *s == '\f')
  876.         return TRUE;
  877.     if (*s == '.' && (inmacro(p_sections, s + 1) || (!para && inmacro(p_para, s + 1))))
  878.         return TRUE;
  879.     return FALSE;
  880. }
  881.  
  882. /*
  883.  * The following routines do the word searches performed by the 'w', 'W',
  884.  * 'b', 'B', 'e', and 'E' commands.
  885.  */
  886.  
  887. /*
  888.  * To perform these searches, characters are placed into one of three
  889.  * classes, and transitions between classes determine word boundaries.
  890.  *
  891.  * The classes are:
  892.  *
  893.  * 0 - white space
  894.  * 1 - letters, digits and underscore
  895.  * 2 - everything else
  896.  */
  897.  
  898. static int        stype;            /* type of the word motion being performed */
  899.  
  900. /*
  901.  * cls() - returns the class of character at Curpos
  902.  *
  903.  * The 'type' of the current search modifies the classes of characters if a 'W',
  904.  * 'B', or 'E' motion is being done. In this case, chars. from class 2 are
  905.  * reported as class 1 since only white space boundaries are of interest.
  906.  */
  907.     static int
  908. cls()
  909. {
  910.     register int c;
  911.  
  912.     c = gcharCurpos();
  913.     if (c == ' ' || c == '\t' || c == NUL)
  914.         return 0;
  915.  
  916.     if (isidchar(c))
  917.         return 1;
  918.  
  919.     /*
  920.      * If stype is non-zero, report these as class 1.
  921.      */
  922.     return (stype == 0) ? 2 : 1;
  923. }
  924.  
  925.  
  926. /*
  927.  * fwd_word(count, type, eol) - move forward one word
  928.  *
  929.  * Returns TRUE if the cursor was already at the end of the file.
  930.  * If eol is TRUE, last word stops at end of line (for operators).
  931.  */
  932.     int
  933. fwd_word(count, type, eol)
  934.     long        count;
  935.     int         type;
  936.     int            eol;
  937. {
  938.     int         sclass;     /* starting class */
  939.     int            i;
  940.  
  941.     stype = type;
  942.     while (--count >= 0)
  943.     {
  944.         sclass = cls();
  945.  
  946.         /*
  947.          * We always move at least one character.
  948.          */
  949.         i = incCurpos();
  950.         if (i == -1)
  951.             return TRUE;
  952.         if (i == 1 && eol && count == 0)    /* started at last char in line */
  953.             return FALSE;
  954.  
  955.         if (sclass != 0)
  956.             while (cls() == sclass)
  957.             {
  958.                 i = incCurpos();
  959.                 if (i == -1 || (i == 1 && eol && count == 0))
  960.                     return FALSE;
  961.             }
  962.  
  963.         /*
  964.          * go to next non-white
  965.          */
  966.         while (cls() == 0)
  967.         {
  968.             /*
  969.              * We'll stop if we land on a blank line
  970.              */
  971.             if (Curpos.col == 0 && *nr2ptr(Curpos.lnum) == NUL)
  972.                 break;
  973.  
  974.             i = incCurpos();
  975.             if (i == -1 || (i == 1 && eol && count == 0))
  976.                 return FALSE;
  977.         }
  978.     }
  979.     return FALSE;
  980. }
  981.  
  982. /*
  983.  * bck_word(count, type) - move backward 'count' words
  984.  *
  985.  * Returns TRUE if top of the file was reached.
  986.  */
  987.     int
  988. bck_word(count, type)
  989.     long        count;
  990.     int         type;
  991. {
  992.     int         sclass;     /* starting class */
  993.  
  994.     stype = type;
  995.     while (--count >= 0)
  996.     {
  997.         sclass = cls();
  998.  
  999.         if (decCurpos() == -1)        /* started at start of file */
  1000.             return TRUE;
  1001.  
  1002.         if (cls() != sclass || sclass == 0)
  1003.         {
  1004.             /*
  1005.              * We were at the start of a word. Go back to the end of the prior
  1006.              * word.
  1007.              */
  1008.             while (cls() == 0)  /* skip any white space */
  1009.             {
  1010.                 /*
  1011.                  * We'll stop if we land on a blank line
  1012.                  */
  1013.                 if (Curpos.col == 0 && *nr2ptr(Curpos.lnum) == NUL)
  1014.                     goto finished;
  1015.  
  1016.                 if (decCurpos() == -1)        /* hit start of file, stop here */
  1017.                     return FALSE;
  1018.             }
  1019.             sclass = cls();
  1020.         }
  1021.  
  1022.         /*
  1023.          * Move backward to start of this word.
  1024.          */
  1025.         if (skip_chars(sclass, BACKWARD))
  1026.                 return FALSE;
  1027.  
  1028.         incCurpos();                    /* overshot - forward one */
  1029. finished:
  1030.         ;
  1031.     }
  1032.     return FALSE;
  1033. }
  1034.  
  1035. /*
  1036.  * end_word(count, type, stop) - move to the end of the word
  1037.  *
  1038.  * There is an apparent bug in the 'e' motion of the real vi. At least on the
  1039.  * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
  1040.  * motion crosses blank lines. When the real vi crosses a blank line in an
  1041.  * 'e' motion, the cursor is placed on the FIRST character of the next
  1042.  * non-blank line. The 'E' command, however, works correctly. Since this
  1043.  * appears to be a bug, I have not duplicated it here.
  1044.  *
  1045.  * Returns TRUE if end of the file was reached.
  1046.  *
  1047.  * If stop is TRUE and we are already on the end of a word, move one less.
  1048.  */
  1049.     int
  1050. end_word(count, type, stop)
  1051.     long        count;
  1052.     int         type;
  1053.     int            stop;
  1054. {
  1055.     int         sclass;     /* starting class */
  1056.  
  1057.     stype = type;
  1058.     while (--count >= 0)
  1059.     {
  1060.         sclass = cls();
  1061.         if (incCurpos() == -1)
  1062.             return TRUE;
  1063.  
  1064.         /*
  1065.          * If we're in the middle of a word, we just have to move to the end of it.
  1066.          */
  1067.         if (cls() == sclass && sclass != 0)
  1068.         {
  1069.             /*
  1070.              * Move forward to end of the current word
  1071.              */
  1072.             if (skip_chars(sclass, FORWARD))
  1073.                     return TRUE;
  1074.         }
  1075.         else if (!stop || sclass == 0)
  1076.         {
  1077.             /*
  1078.              * We were at the end of a word. Go to the end of the next word.
  1079.              */
  1080.  
  1081.             if (skip_chars(0, FORWARD))     /* skip any white space */
  1082.                 return TRUE;
  1083.  
  1084.             /*
  1085.              * Move forward to the end of this word.
  1086.              */
  1087.             if (skip_chars(cls(), FORWARD))
  1088.                 return TRUE;
  1089.         }
  1090.         decCurpos();                    /* overshot - backward one */
  1091.         stop = FALSE;                    /* we move only one word less */
  1092.     }
  1093.     return FALSE;
  1094. }
  1095.  
  1096.     int
  1097. skip_chars(class, dir)
  1098.     int class;
  1099.     int dir;
  1100. {
  1101.         while (cls() == class)
  1102.             if ((dir == FORWARD ? incCurpos() : decCurpos()) == -1)
  1103.                 return TRUE;
  1104.         return FALSE;
  1105. }
  1106.