home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d197 / stevie.lha / Stevie / search.c < prev    next >
C/C++ Source or Header  |  1989-03-28  |  22KB  |  1,034 lines

  1. /*
  2.  * STEVIE - Simply Try this Editor for VI Enthusiasts
  3.  *
  4.  * Code Contributions By : Tim Thompson           twitch!tjt
  5.  *                         Tony Andrews           onecom!wldrdg!tony 
  6.  *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
  7.  */
  8.  
  9. #include "stevie.h"
  10. /* modified Henry Spencer's regular expression routines */
  11. #include "regexp.h"
  12.  
  13. #ifdef    MEGAMAX
  14. overlay "search"
  15. #endif
  16.  
  17. /*
  18.  * This file contains various searching-related routines. These fall into
  19.  * three groups: string searches (for /, ?, n, and N), character searches
  20.  * within a single line (for f, F, t, T, etc), and "other" kinds of searches
  21.  * like the '%' command, and 'word' searches. 
  22.  */
  23.  
  24. /*
  25.  * String searches 
  26.  *
  27.  * The actual searches are done using Henry Spencer's regular expression
  28.  * library. 
  29.  */
  30.  
  31. #define    BEGWORD    "([^a-zA-Z0-9_]|^)"    /* replaces "\<" in search strings */
  32. #define    ENDWORD    "([^a-zA-Z0-9_]|$)"    /* likewise replaces "\>" */
  33.  
  34. bool_t begword;            /* does the search include a 'begin word'
  35.                  * match */
  36.  
  37. /*
  38.  * mapstring(s) - map special backslash sequences 
  39.  */
  40. static char    *
  41. mapstring(s)
  42.     register char  *s;
  43. {
  44.     static char     ns[MAX_COLUMNS + 1];
  45.     register char  *p;
  46.  
  47.     begword = FALSE;
  48.  
  49.     for (p = ns; *s; s++) {
  50.     if ((*s == '(') || (*s == ')')) {
  51.         *p++ = '\\';
  52.         *p++ = *s;
  53.         continue;
  54.     }
  55.     if (*s != '\\') {    /* not an escape */
  56.         *p++ = *s;
  57.         continue;
  58.     }
  59.     switch (*++s) {
  60.       case '/':
  61.         *p++ = '/';
  62.         break;
  63.  
  64.       case '<':
  65.         strcpy(p, BEGWORD);
  66.         p += strlen(BEGWORD);
  67.         begword = TRUE;
  68.         break;
  69.  
  70.       case '>':
  71.         strcpy(p, ENDWORD);
  72.         p += strlen(ENDWORD);
  73.         break;
  74.  
  75.       default:
  76.         *p++ = '\\';
  77.         *p++ = *s;
  78.         break;
  79.     }
  80.     }
  81.     *p++ = NUL;
  82.  
  83.     return ns;
  84. }
  85.  
  86. static LPtr    *
  87. bcksearch(str)
  88.     char           *str;
  89. {
  90.     static LPtr     infile;
  91.     register LPtr  *p;
  92.     regexp         *prog;
  93.     register char  *s;
  94.     register int    i;
  95.     bool_t          want_start = (*str == '^');    /* looking for start of line? */
  96.     register char  *match;
  97.  
  98.     /* make sure str isn't empty */
  99.     if (str == NULL || *str == NUL)
  100.     return NULL;
  101.  
  102.     if ((prog = regcomp(str)) == NULL) {
  103.     emsg("Invalid search string");
  104.     return NULL;
  105.     }
  106.     p = Curschar;
  107.     dec(p);
  108.  
  109.     if (begword)        /* so we don't get stuck on one match */
  110.     dec(p);
  111.  
  112.     i = (want_start) ? 0 : p->index;
  113.  
  114.     do {
  115.     s = p->linep->s;
  116.  
  117.     if (regexec(prog, s, TRUE)) {    /* match somewhere on line */
  118.  
  119.         if (want_start) {    /* could only have been one */
  120.         infile.linep = p->linep;
  121.         infile.index = (int) (prog->startp[0] - s);
  122.         free((char *) prog);
  123.         return (&infile);
  124.         }
  125.         /*
  126.          * Now, if there are multiple matches on this line, we have to
  127.          * get the last one. Or the last one before the cursor, if we're
  128.          * on that line. 
  129.          */
  130.  
  131.         match = prog->startp[0];
  132.  
  133.         while (regexec(prog, prog->endp[0], FALSE)) {
  134.         if ((i >= 0) && ((prog->startp[0] - s) > i))
  135.             break;
  136.         match = prog->startp[0];
  137.         }
  138.  
  139.         if ((i >= 0) && ((match - s) > i)) {
  140.         i = -1;
  141.         continue;
  142.         }
  143.         infile.linep = p->linep;
  144.         infile.index = (int) (match - s);
  145.         free((char *) prog);
  146.         return (&infile);
  147.     }
  148.     i = -1;
  149.  
  150.     } while ((p = prevline(p)) != NULL);
  151.  
  152.     /*
  153.      * If wrapscan isn't set, bag the search now 
  154.      */
  155.     if (!P(P_WS)) {
  156.     free((char *) prog);
  157.     return NULL;
  158.     }
  159.     /* search backward from the end of the file */
  160.     p = prevline(Fileend);
  161.     do {
  162.     s = p->linep->s;
  163.  
  164.     if (regexec(prog, s, TRUE)) {    /* match somewhere on line */
  165.  
  166.         if (want_start) {    /* could only have been one */
  167.         infile.linep = p->linep;
  168.         infile.index = (int) (prog->startp[0] - s);
  169.         free((char *) prog);
  170.         return (&infile);
  171.         }
  172.         /*
  173.          * Now, if there are multiple matches on this line, we have to
  174.          * get the last one. 
  175.          */
  176.  
  177.         match = prog->startp[0];
  178.  
  179.         while (regexec(prog, prog->endp[0], FALSE))
  180.         match = prog->startp[0];
  181.  
  182.         infile.linep = p->linep;
  183.         infile.index = (int) (match - s);
  184.         free((char *) prog);
  185.         return (&infile);
  186.     }
  187.     if (p->linep == Curschar->linep)
  188.         break;
  189.  
  190.     } while ((p = prevline(p)) != NULL);
  191.  
  192.     free((char *) prog);
  193.     return NULL;
  194. }
  195.  
  196. static LPtr    *
  197. fwdsearch(str)
  198.     char           *str;
  199. {
  200.     static LPtr     infile;
  201.     LPtr           *p;
  202.     regexp         *prog;
  203.     bool_t          want_start = (*str == '^');    /* looking for start of line? */
  204.  
  205.     char           *s;
  206.     int             i;
  207.  
  208.     if ((prog = regcomp(str)) == NULL) {
  209.     emsg("Invalid search string");
  210.     return NULL;
  211.     }
  212.     p = Curschar;
  213.     i = Curschar->index + 1;
  214.     do {
  215.     s = p->linep->s + i;
  216.     i = 0;
  217.  
  218.     if (regexec(prog, s, i == 0)) {    /* got a match */
  219.         /*
  220.          * If we wanted the start of a line and we aren't really there,
  221.          * then a match doesn't count. 
  222.          */
  223.         if (want_start && (s != p->linep->s))
  224.         continue;
  225.  
  226.         infile.linep = p->linep;
  227.         infile.index = (int) (prog->startp[0] - p->linep->s);
  228.         free((char *) prog);
  229.         return (&infile);
  230.     }
  231.     } while ((p = nextline(p)) != NULL);
  232.  
  233.     /*
  234.      * If wrapscan isn't set, then don't scan from the beginning of the file.
  235.      * Just return failure here. 
  236.      */
  237.     if (!P(P_WS)) {
  238.     free((char *) prog);
  239.     return NULL;
  240.     }
  241.     /* search from the beginning of the file to Curschar */
  242.     for (p = Filemem; p != NULL; p = nextline(p)) {
  243.     s = p->linep->s;
  244.  
  245.     if (regexec(prog, s, TRUE)) {    /* got a match */
  246.         infile.linep = p->linep;
  247.         infile.index = (int) (prog->startp[0] - s);
  248.         free((char *) prog);
  249.         return (&infile);
  250.     }
  251.     if (p->linep == Curschar->linep)
  252.         break;
  253.     }
  254.  
  255.     free((char *) prog);
  256.     return (NULL);
  257. }
  258.  
  259. static char    *laststr = NULL;
  260. static int      lastsdir;
  261.  
  262. static LPtr    *
  263. ssearch(dir, str)
  264.     int             dir;    /* FORWARD or BACKWARD */
  265.     char           *str;
  266. {
  267.     LPtr           *pos;
  268.  
  269.     reg_ic = P(P_IC);        /* tell the regexp routines how to search */
  270.  
  271.     if (laststr != str) {
  272.     if (laststr != NULL)
  273.         free(laststr);
  274.     laststr = strsave(str);
  275.     }
  276.     lastsdir = dir;
  277.  
  278.     if (dir == BACKWARD)
  279.     pos = bcksearch(mapstring(str));
  280.     else
  281.     pos = fwdsearch(mapstring(str));
  282.  
  283.     /*
  284.      * This is kind of a kludge, but its needed to make 'beginning of word'
  285.      * searches land on the right place. 
  286.      */
  287.     if (pos != NULL && begword) {
  288.     if (pos->index != 0)
  289.         pos->index += 1;
  290.     }
  291.     return pos;
  292. }
  293.  
  294. bool_t
  295. dosearch(dir, str)
  296.     int             dir;
  297.     char           *str;
  298. {
  299.     LPtr           *p;
  300.  
  301.     if ((p = ssearch(dir, str)) == NULL) {
  302.     msg("Pattern not found");
  303.     return (FALSE);
  304.     } else {
  305.     LPtr            savep;
  306.  
  307.     cursupdate();
  308.     /* if we're backing up, we make sure the line we're on */
  309.     /* is on the screen. */
  310.     setpcmark();
  311.     *Curschar = savep = *p;
  312.     cursupdate();
  313.  
  314.     return (TRUE);
  315.     }
  316. }
  317.  
  318. void
  319. searchagain(dir)
  320.     int             dir;
  321. {
  322.     if (laststr == NULL)
  323.     beep();
  324.     else
  325.     dosearch(dir, laststr);
  326.  
  327.     lastsdir = dir;
  328. }
  329.  
  330. #define    OTHERDIR(x)    (((x) == FORWARD) ? BACKWARD : FORWARD)
  331.  
  332. bool_t
  333. repsearch(flag)
  334.     bool_t          flag;
  335. {
  336.     int             dir = lastsdir;
  337.     bool_t          found;
  338.  
  339.     if (laststr == NULL) {
  340.     beep();
  341.     return FALSE;
  342.     }
  343.     found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
  344.  
  345.     /*
  346.      * We have to save and restore 'lastsdir' because it gets munged by
  347.      * ssearch() and winds up saving the wrong direction from here if 'flag'
  348.      * is true. 
  349.      */
  350.     lastsdir = dir;
  351.  
  352.     return (found);
  353. }
  354.  
  355. /*
  356.  * regerror - called by regexp routines when errors are detected. 
  357.  */
  358. void
  359. regerror(s)
  360.     char           *s;
  361. {
  362.     emsg(s);
  363. }
  364.  
  365. /*
  366.  * dosub(lp, up, cmd)
  367.  *
  368.  * Perform a substitution from line 'lp' to line 'up' using the
  369.  * command pointed to by 'cmd' which should be of the form:
  370.  *
  371.  * /pattern/substitution/g
  372.  *
  373.  * The trailing 'g' is optional and, if present, indicates that multiple
  374.  * substitutions should be performed on each line, if applicable.
  375.  * The usual escapes are supported as described in the regexp docs.
  376.  */
  377.  
  378. void
  379. dosub(lp, up, cmd)
  380.     LPtr           *lp, *up;
  381.     char           *cmd;
  382. {
  383.     LINE           *cp;
  384.     char           *pat, *sub;
  385.     regexp         *prog;
  386.     int             nsubs;
  387.     bool_t          do_all;    /* do multiple substitutions per line */
  388.     int             n;
  389.  
  390.     /*
  391.      * If no range was given, do the current line. If only one line was
  392.      * given, just do that one. 
  393.      */
  394.     if (lp->linep == NULL)
  395.     *up = *lp = *Curschar;
  396.     else {
  397.     if (up->linep == NULL)
  398.         *up = *lp;
  399.     }
  400.  
  401.     pat = ++cmd;        /* skip the initial '/' */
  402.  
  403.     while (*cmd) {
  404.     if (cmd[0] == '/' && cmd[-1] != '\\') {
  405.         *cmd++ = NUL;
  406.         break;
  407.     }
  408.     cmd++;
  409.     }
  410.  
  411.     if (*pat == NUL) {
  412.     emsg("NULL pattern specified");
  413.     return;
  414.     }
  415.     sub = cmd;
  416.  
  417.     do_all = FALSE;
  418.  
  419.     while (*cmd) {
  420.     if (cmd[0] == '/' && cmd[-1] != '\\') {
  421.         do_all = (cmd[1] == 'g');
  422.         *cmd++ = NUL;
  423.         break;
  424.     }
  425.     cmd++;
  426.     }
  427.  
  428.     reg_ic = P(P_IC);        /* set "ignore case" flag appropriately */
  429.  
  430.     if ((prog = regcomp(pat)) == NULL) {
  431.     emsg("Invalid search string");
  432.     return;
  433.     }
  434.     nsubs = 0;
  435.  
  436.     ResetBuffers();
  437.     n = RowNumber(lp);
  438.  
  439.     cp = lp->linep;
  440.     for (; cp != Fileend->linep && cp != NULL; cp = cp->next, n++) {
  441.     if (regexec(prog, cp->s, TRUE)) {    /* a match on this line */
  442.         char           *ns, *sns, *p;
  443.  
  444.         /*
  445.          * Save the line that was last changed for the final cursor
  446.          * position (just like the real vi). 
  447.          */
  448.         Curschar->linep = cp;
  449.  
  450.         /*
  451.          * Get some space for a temporary buffer to do the substitution
  452.          * into. 
  453.          */
  454.         sns = ns = alloc(2048);
  455.         *sns = NUL;
  456.  
  457.         p = cp->s;
  458.  
  459.         do {
  460.         for (ns = sns; *ns; ns++);
  461.         /*
  462.          * copy up to the part that matched 
  463.          */
  464.         while (p < prog->startp[0])
  465.             *ns++ = *p++;
  466.  
  467.         regsub(prog, sub, ns);
  468.  
  469.         /*
  470.          * continue searching after the match 
  471.          */
  472.         p = prog->endp[0];
  473.  
  474.         } while (regexec(prog, p, FALSE) && do_all);
  475.  
  476.         for (ns = sns; *ns; ns++);
  477.  
  478.         /*
  479.          * copy the rest of the line, that didn't match 
  480.          */
  481.         while (*p)
  482.         *ns++ = *p++;
  483.  
  484.         *ns = NUL;
  485.  
  486.         AppendPositionToUndoUndobuff(0, n);
  487.         AppendPositionToUndobuff(0, n);
  488.         AppendToUndoUndobuff("c$");
  489.         AppendToUndobuff("c$");
  490.         AppendToUndoUndobuff(sns);
  491.         AppendToUndobuff(cp->s);
  492.         AppendToUndoUndobuff(ESC_STR);
  493.         AppendToUndobuff(ESC_STR);
  494.  
  495.         free(cp->s);    /* free the original line */
  496.         cp->s = strsave(sns);    /* and save the modified str */
  497.         cp->size = strlen(cp->s) + 1;
  498.         free(sns);        /* free the temp buffer */
  499.         nsubs++;
  500.     }
  501.     if (cp == up->linep)
  502.         break;
  503.     }
  504.  
  505.     if (nsubs) {
  506.     CHANGED;
  507.     AppendPositionToUndoUndobuff(0, 1);
  508.     AppendPositionToUndobuff(0, 1);
  509.     updateNextscreen(NOT_VALID);    /* need this to update LineSizes */
  510.     cursupdate();
  511.     beginline(TRUE);
  512.     if (nsubs >= P(P_RP))
  513.         smsg("%d substitution%c", nsubs, (nsubs > 1) ? 's' : ' ');
  514.     } else
  515.     msg("No match");
  516.  
  517.     free((char *) prog);
  518. }
  519.  
  520. /*
  521.  * doglob(cmd)
  522.  *
  523.  * Execute a global command of the form:
  524.  *
  525.  * g/pattern/X
  526.  *
  527.  * where 'x' is a command character, currently one of the following:
  528.  *
  529.  * d    Delete all matching lines
  530.  * p    Print all matching lines
  531.  *
  532.  * The command character (as well as the trailing slash) is optional, and
  533.  * is assumed to be 'p' if missing.
  534.  */
  535.  
  536. void
  537. doglob(lp, up, cmd)
  538.     LPtr           *lp, *up;
  539.     char           *cmd;
  540. {
  541.     LINE           *cp;
  542.  
  543.     char           *pat;
  544.     regexp         *prog;
  545.     int             ndone;
  546.     char            cmdchar = NUL;    /* what to do with matching lines */
  547.     int             nu = 0;
  548.     int             nuu = 0;
  549.  
  550.     /*
  551.      * If no range was given, do every line. If only one line was given, just
  552.      * do that one. 
  553.      */
  554.     if (lp->linep == NULL) {
  555.     *lp = *Filemem;
  556.     *up = *Fileend;
  557.     } else {
  558.     if (up->linep == NULL)
  559.         *up = *lp;
  560.     }
  561.  
  562.     pat = ++cmd;        /* skip the initial '/' */
  563.  
  564.     while (*cmd) {
  565.     if (cmd[0] == '/' && cmd[-1] != '\\') {
  566.         cmdchar = cmd[1];
  567.         *cmd++ = NUL;
  568.         break;
  569.     }
  570.     cmd++;
  571.     }
  572.     if (cmdchar == NUL)
  573.     cmdchar = 'p';
  574.  
  575.     reg_ic = P(P_IC);        /* set "ignore case" flag appropriately */
  576.  
  577.     if (cmdchar != 'd' && cmdchar != 'p') {
  578.     emsg("Invalid command character");
  579.     return;
  580.     }
  581.     if ((prog = regcomp(pat)) == NULL) {
  582.     emsg("Invalid search string");
  583.     return;
  584.     }
  585.     msg("");
  586.     ndone = 0;
  587.  
  588.     nu = RowNumber(lp);
  589.     if (cmdchar == 'd') {
  590.     ResetBuffers();
  591.     nuu = nu;
  592.     }
  593.     cp = lp->linep;
  594.     for (; cp != Fileend->linep && cp != NULL; cp = cp->next, nu++) {
  595.     if (regexec(prog, cp->s, TRUE)) {    /* a match on this line */
  596.         Curschar->linep = cp;
  597.         Curschar->index = 0;
  598.  
  599.         switch (cmdchar) {
  600.  
  601.           case 'd':    /* delete the line */
  602.         AppendPositionToUndoUndobuff(0, nuu);
  603.         AppendToUndoUndobuff("dd");
  604.         if (buf1line() && (ndone == 0)) {
  605.             AppendToUndobuff("a");
  606.         } else if (buf1line()) {
  607.             AppendToUndobuff("j");
  608.             AppendToUndobuff("I");
  609.         } else if (cp->next == Fileend->linep) {
  610.             AppendPositionToUndobuff(0, nu);
  611.             AppendToUndobuff("o");
  612.         } else {
  613.             AppendPositionToUndobuff(0, nu);
  614.             AppendToUndobuff("O");
  615.         }
  616.         AppendToUndobuff(cp->s);
  617.         AppendToUndobuff(ESC_STR);
  618.  
  619.         delline(1, FALSE);
  620.         break;
  621.  
  622.           case 'p':    /* print the line */
  623.         if (P(P_NU)) {
  624.             sprintf(IObuff, "%6d  ", nu);
  625.             outstr(IObuff);
  626.         }
  627.         prt_line(cp->s);
  628.         outstr("\r\n");
  629.         break;
  630.         }
  631.         ndone++;
  632.     } else if (cmdchar == 'd') {
  633.         nuu++;
  634.     }
  635.     if (cp == up->linep)
  636.         break;
  637.     }
  638.  
  639.     if (ndone) {
  640.     switch (cmdchar) {
  641.  
  642.       case 'd':
  643.         AppendPositionToUndobuff(0, 1);
  644.         updateNextscreen(NOT_VALID);
  645.         cursupdate();
  646.         if (ndone >= P(P_RP))
  647.         smsg("%d fewer line%c", ndone,
  648.              (ndone > 1) ? 's' : ' ');
  649.         break;
  650.  
  651.       case 'p':
  652.         wait_return();
  653.         break;
  654.     }
  655.     stuffReadbuff("^");
  656.     } else
  657.     msg("No match");
  658.  
  659.     free((char *) prog);
  660. }
  661.  
  662. /*
  663.  * Character Searches 
  664.  */
  665.  
  666. static char     lastc = NUL;    /* last character searched for */
  667. static int      lastcdir;    /* last direction of character search */
  668. static int      lastctype;    /* last type of search ("find" or "to") */
  669.  
  670. /*
  671.  * searchc(c, dir, type) 
  672.  *
  673.  * Search for character 'c', in direction 'dir'. If type is 0, move to the
  674.  * position of the character, otherwise move to just before the char. 
  675.  */
  676. bool_t
  677. searchc(c, dir, type)
  678.     char            c;
  679.     int             dir;
  680.     int             type;
  681. {
  682.     LPtr            save;
  683.  
  684.     save = *Curschar;        /* save position in case we fail */
  685.     lastc = c;
  686.     lastcdir = dir;
  687.     lastctype = type;
  688.  
  689.     /*
  690.      * On 'to' searches, skip one to start with so we can repeat searches in
  691.      * the same direction and have it work right. 
  692.      */
  693.     if (type)
  694.     (dir == FORWARD) ? oneright() : oneleft();
  695.  
  696.     while ((dir == FORWARD) ? oneright() : oneleft()) {
  697.     if (gchar(Curschar) == c) {
  698.         if (type)
  699.         (dir == FORWARD) ? oneleft() : oneright();
  700.         return TRUE;
  701.     }
  702.     }
  703.     *Curschar = save;
  704.     return FALSE;
  705. }
  706.  
  707. bool_t
  708. crepsearch(flag)
  709.     int             flag;
  710. {
  711.     int             dir = lastcdir;
  712.     int             rval;
  713.  
  714.     if (lastc == NUL)
  715.     return FALSE;
  716.  
  717.     rval = searchc(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype);
  718.  
  719.     lastcdir = dir;        /* restore dir., since it may have changed */
  720.  
  721.     return rval;
  722. }
  723.  
  724. /*
  725.  * "Other" Searches 
  726.  */
  727.  
  728. /*
  729.  * showmatch - move the cursor to the matching paren or brace 
  730.  */
  731. LPtr           *
  732. showmatch()
  733. {
  734.     static LPtr     pos;
  735.     int             (*move) (), inc(), dec();
  736.     char            initc = gchar(Curschar);    /* initial char */
  737.     char            findc;    /* terminating char */
  738.     char            c;
  739.     int             count = 0;
  740.  
  741.     pos = *Curschar;        /* set starting point */
  742.  
  743.     switch (initc) {
  744.  
  745.       case '(':
  746.     findc = ')';
  747.     move = inc;
  748.     break;
  749.       case ')':
  750.     findc = '(';
  751.     move = dec;
  752.     break;
  753.       case '{':
  754.     findc = '}';
  755.     move = inc;
  756.     break;
  757.       case '}':
  758.     findc = '{';
  759.     move = dec;
  760.     break;
  761.       case '[':
  762.     findc = ']';
  763.     move = inc;
  764.     break;
  765.       case ']':
  766.     findc = '[';
  767.     move = dec;
  768.     break;
  769.       default:
  770.     return (LPtr *) NULL;
  771.     }
  772.  
  773.     while ((*move) (&pos) != -1) {    /* until end of file */
  774.     c = gchar(&pos);
  775.     if (c == initc)
  776.         count++;
  777.     else if (c == findc) {
  778.         if (count == 0)
  779.         return &pos;
  780.         count--;
  781.     }
  782.     }
  783.     return (LPtr *) NULL;    /* never found it */
  784. }
  785.  
  786. /*
  787.  * findfunc(dir) - Find the next function in direction 'dir' 
  788.  *
  789.  * Return TRUE if a function was found. 
  790.  */
  791. bool_t
  792. findfunc(dir)
  793.     int             dir;
  794. {
  795.     LPtr           *curr;
  796.  
  797.     curr = Curschar;
  798.  
  799.     do {
  800.     curr = (dir == FORWARD) ? nextline(curr) : prevline(curr);
  801.  
  802.     if (curr != NULL && curr->linep->s[0] == '{') {
  803.         setpcmark();
  804.         *Curschar = *curr;
  805.         return TRUE;
  806.     }
  807.     } while (curr != NULL);
  808.  
  809.     return FALSE;
  810. }
  811.  
  812. /*
  813.  * The following routines do the word searches performed by the 'w', 'W',
  814.  * 'b', 'B', 'e', and 'E' commands. 
  815.  */
  816.  
  817. /*
  818.  * To perform these searches, characters are placed into one of three
  819.  * classes, and transitions between classes determine word boundaries. 
  820.  *
  821.  * The classes are: 
  822.  *
  823.  * 0 - white space 1 - letters, digits, and underscore 2 - everything else 
  824.  */
  825.  
  826. static int      stype;        /* type of the word motion being performed */
  827.  
  828. #define    C0(c)    (((c) == ' ') || ((c) == '\t') || ((c) == NUL))
  829. #define    C1(c)    (isalpha(c) || isdigit(c) || ((c) == '_'))
  830.  
  831. /*
  832.  * cls(c) - returns the class of character 'c' 
  833.  *
  834.  * The 'type' of the current search modifies the classes of characters if a 'W',
  835.  * 'B', or 'E' motion is being done. In this case, chars. from class 2 are
  836.  * reported as class 1 since only white space boundaries are of interest. 
  837.  */
  838. static int
  839. cls(c)
  840.     char            c;
  841. {
  842.     if (C0(c))
  843.     return 0;
  844.  
  845.     if (C1(c))
  846.     return 1;
  847.  
  848.     /*
  849.      * If stype is non-zero, report these as class 1. 
  850.      */
  851.     return (stype == 0) ? 2 : 1;
  852. }
  853.  
  854.  
  855. /*
  856.  * fwd_word(pos, type) - move forward one word 
  857.  *
  858.  * Returns the resulting position, or NULL if EOF was reached. 
  859.  */
  860. LPtr           *
  861. fwd_word(p, type)
  862.     LPtr           *p;
  863.     int             type;
  864. {
  865.     static LPtr     pos;
  866.     int             sclass = cls(gchar(p));    /* starting class */
  867.  
  868.     pos = *p;
  869.  
  870.     stype = type;
  871.  
  872.     /*
  873.      * We always move at least one character. 
  874.      */
  875.     if (inc(&pos) == -1)
  876.     return NULL;
  877.  
  878.     if (sclass != 0) {
  879.     while (cls(gchar(&pos)) == sclass) {
  880.         if (inc(&pos) == -1)
  881.         return NULL;
  882.     }
  883.     /*
  884.      * If we went from 1 -> 2 or 2 -> 1, return here. 
  885.      */
  886.     if (cls(gchar(&pos)) != 0)
  887.         return &pos;
  888.     }
  889.     /* We're in white space; go to next non-white */
  890.  
  891.     while (cls(gchar(&pos)) == 0) {
  892.     /*
  893.      * We'll stop if we land on a blank line 
  894.      */
  895.     if (pos.index == 0 && pos.linep->s[0] == NUL)
  896.         break;
  897.  
  898.     if (inc(&pos) == -1)
  899.         return NULL;
  900.     }
  901.  
  902.     return &pos;
  903. }
  904.  
  905. /*
  906.  * bck_word(pos, type) - move backward one word 
  907.  *
  908.  * Returns the resulting position, or NULL if top-of-file was reached. 
  909.  */
  910. LPtr           *
  911. bck_word(p, type)
  912.     LPtr           *p;
  913.     int             type;
  914. {
  915.     static LPtr     pos;
  916.     int             sclass = cls(gchar(p));    /* starting class */
  917.  
  918.     pos = *p;
  919.  
  920.     stype = type;
  921.  
  922.     if (dec(&pos) == -1)
  923.     return NULL;
  924.  
  925.     /*
  926.      * If we're in the middle of a word, we just have to back up to the start
  927.      * of it. 
  928.      */
  929.     if (cls(gchar(&pos)) == sclass && sclass != 0) {
  930.     /*
  931.      * Move backward to start of the current word 
  932.      */
  933.     while (cls(gchar(&pos)) == sclass) {
  934.         if (dec(&pos) == -1)
  935.         return NULL;
  936.     }
  937.     inc(&pos);        /* overshot - forward one */
  938.     return &pos;
  939.     }
  940.     /*
  941.      * We were at the start of a word. Go back to the start of the prior
  942.      * word. 
  943.      */
  944.  
  945.     while (cls(gchar(&pos)) == 0) {    /* skip any white space */
  946.     /*
  947.      * We'll stop if we land on a blank line 
  948.      */
  949.     if (pos.index == 0 && pos.linep->s[0] == NUL)
  950.         return &pos;
  951.  
  952.     if (dec(&pos) == -1)
  953.         return NULL;
  954.     }
  955.  
  956.     sclass = cls(gchar(&pos));
  957.  
  958.     /*
  959.      * Move backward to start of this word. 
  960.      */
  961.     while (cls(gchar(&pos)) == sclass) {
  962.     if (dec(&pos) == -1)
  963.         return NULL;
  964.     }
  965.     inc(&pos);            /* overshot - forward one */
  966.  
  967.     return &pos;
  968. }
  969.  
  970. /*
  971.  * end_word(pos, type) - move to the end of the word 
  972.  *
  973.  * There is an apparent bug in the 'e' motion of the real vi. At least on the
  974.  * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
  975.  * motion crosses blank lines. When the real vi crosses a blank line in an
  976.  * 'e' motion, the cursor is placed on the FIRST character of the next
  977.  * non-blank line. The 'E' command, however, works correctly. Since this
  978.  * appears to be a bug, I have not duplicated it here. 
  979.  *
  980.  * Returns the resulting position, or NULL if EOF was reached. 
  981.  */
  982. LPtr           *
  983. end_word(p, type)
  984.     LPtr           *p;
  985.     int             type;
  986. {
  987.     static LPtr     pos;
  988.     int             sclass = cls(gchar(p));    /* starting class */
  989.  
  990.     pos = *p;
  991.  
  992.     stype = type;
  993.  
  994.     if (inc(&pos) == -1)
  995.     return NULL;
  996.  
  997.     /*
  998.      * If we're in the middle of a word, we just have to move to the end of
  999.      * it. 
  1000.      */
  1001.     if (cls(gchar(&pos)) == sclass && sclass != 0) {
  1002.     /*
  1003.      * Move forward to end of the current word 
  1004.      */
  1005.     while (cls(gchar(&pos)) == sclass) {
  1006.         if (inc(&pos) == -1)
  1007.         return NULL;
  1008.     }
  1009.     dec(&pos);        /* overshot - forward one */
  1010.     return &pos;
  1011.     }
  1012.     /*
  1013.      * We were at the end of a word. Go to the end of the next word. 
  1014.      */
  1015.  
  1016.     while (cls(gchar(&pos)) == 0) {    /* skip any white space */
  1017.     if (inc(&pos) == -1)
  1018.         return NULL;
  1019.     }
  1020.  
  1021.     sclass = cls(gchar(&pos));
  1022.  
  1023.     /*
  1024.      * Move forward to end of this word. 
  1025.      */
  1026.     while (cls(gchar(&pos)) == sclass) {
  1027.     if (inc(&pos) == -1)
  1028.         return NULL;
  1029.     }
  1030.     dec(&pos);            /* overshot - forward one */
  1031.  
  1032.     return &pos;
  1033. }
  1034.