home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / STEVIE3.ZIP / SEARCH.C < prev    next >
Text File  |  1991-03-27  |  14KB  |  722 lines

  1. /*
  2.  * STevie - ST editor for VI enthusiasts.    ...Tim Thompson...twitch!tjt...
  3.  *
  4.  * Extensive modifications by:  Tony Andrews       onecom!wldrdg!tony
  5.  *
  6.  */
  7.  
  8. #include "stevie.h"
  9.  
  10. #include <regexp.h>    /* Henry Spencer's regular expression routines */
  11.  
  12. #ifdef    MEGAMAX
  13. overlay "search"
  14. #endif
  15.  
  16. /*
  17.  * This file contains various searching-related routines. These fall into
  18.  * three groups: string searches (for /, ?, n, and N), character searches
  19.  * within a single line (for f, F, t, T, etc), and "other" kinds of searches
  20.  * like the '%' command, and 'word' searches.
  21.  */
  22.  
  23. /*
  24.  * String searches
  25.  *
  26.  * The actual searches are done using Henry Spencer's regular expression
  27.  * library.
  28.  */
  29.  
  30. #define    BEGWORD    "([^a-zA-Z0-9_]|^)"    /* replaces "\<" in search strings */
  31. #define    ENDWORD    "([^a-zA-Z0-9_]|$)"    /* likewise replaces "\>" */
  32.  
  33. bool_t    begword;    /* does the search include a 'begin word' match */
  34.  
  35. /*
  36.  * mapstring(s) - map special backslash sequences
  37.  */
  38. static char *
  39. mapstring(s)
  40. register char    *s;
  41. {
  42.     static    char    ns[80];
  43.     char    *p;
  44.  
  45.     begword = FALSE;
  46.  
  47.     for (p = ns; *s ;s++) {
  48.         if (*s != '\\') {    /* not an escape */
  49.             *p++ = *s;
  50.             continue;
  51.         }
  52.         switch (*++s) {
  53.         case '/':
  54.             *p++ = '/';
  55.             break;
  56.  
  57.         case '<':
  58.             strcpy(p, BEGWORD);
  59.             p += strlen(BEGWORD);
  60.             begword = TRUE;
  61.             break;
  62.  
  63.         case '>':
  64.             strcpy(p, ENDWORD);
  65.             p += strlen(ENDWORD);
  66.             break;
  67.  
  68.         default:
  69.             *p++ = '\\';
  70.             *p++ = *s;
  71.             break;
  72.         }
  73.     }
  74.     *p++ = NUL;
  75.  
  76.     return ns;
  77. }
  78.  
  79. static char *laststr = NULL;
  80. static int lastsdir;
  81.  
  82. static LPTR *
  83. ssearch(dir,str)
  84. int    dir;    /* FORWARD or BACKWARD */
  85. char    *str;
  86. {
  87. #ifdef MSDOS
  88.         LPTR        *bcksearch(), *fwdsearch();
  89. #else
  90.     static LPTR *bcksearch(), *fwdsearch();
  91. #endif
  92.     LPTR    *pos;
  93.  
  94.     if ( laststr != NULL )
  95.         free(laststr);
  96.     laststr = strsave(str);
  97.     lastsdir = dir;
  98.     if ( dir == BACKWARD )
  99.         pos = bcksearch(mapstring(str));
  100.     else
  101.         pos = fwdsearch(mapstring(str));
  102.  
  103.     /*
  104.      * This is kind of a kludge, but its needed to make
  105.      * 'beginning of word' searches land on the right place.
  106.      */
  107.     if (begword) {
  108.         if (pos->index != 0)
  109.             pos->index += 1;
  110.     }
  111.     return pos;
  112. }
  113.  
  114. void
  115. dosearch(dir,str)
  116. int dir;
  117. char *str;
  118. {
  119.     LPTR *p;
  120.  
  121.     if ((p = ssearch(dir,str)) == NULL)
  122.         msg("Pattern not found");
  123.     else {
  124.         LPTR savep;
  125.  
  126.         cursupdate();
  127.         /* if we're backing up, we make sure the line we're on */
  128.         /* is on the screen. */
  129.         setpcmark();
  130.         *Curschar = savep = *p;
  131.         cursupdate();
  132.     }
  133. }
  134.  
  135. #define    OTHERDIR(x)    (((x) == FORWARD) ? BACKWARD : FORWARD)
  136.  
  137. void
  138. repsearch(flag)
  139. int    flag;
  140. {
  141.     int    dir = lastsdir;
  142.  
  143.     if ( laststr == NULL )
  144.         beep();
  145.     else
  146.         dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
  147.  
  148.     lastsdir = dir;
  149. }
  150.  
  151. /*
  152.  * regerror - called by regexp routines when errors are detected.
  153.  */
  154. void
  155. regerror(s)
  156. char    *s;
  157. {
  158.     emsg(s);
  159. }
  160.  
  161. static LPTR *
  162. fwdsearch(str)
  163. register char *str;
  164. {
  165.     static LPTR infile;
  166.     register LPTR *p;
  167.     regexp *prog;
  168.     bool_t    want_start = (*str == '^');    /* looking for start of line? */
  169.  
  170.     register char *s;
  171.     register int i;
  172.  
  173.     if ((prog = regcomp(str)) == NULL) {
  174.         emsg("Invalid search string");
  175.         return NULL;
  176.     }
  177.  
  178.     p = Curschar;
  179.     i = Curschar->index + 1;
  180.     do {
  181.         s = p->linep->s + i;
  182.         i = 0;
  183.  
  184.         if (regexec(prog, s)) {        /* got a match */
  185.             /*
  186.              * If we wanted the start of a line and we aren't
  187.              * really there, then a match doesn't count.
  188.              */
  189.             if (want_start && (s != p->linep->s))
  190.                 continue;
  191.  
  192.             infile.linep = p->linep;
  193.             infile.index = (int) (prog->startp[0] - p->linep->s);
  194.             free(prog);
  195.             return (&infile);
  196.         }
  197.     } while ((p = nextline(p)) != NULL);
  198.  
  199.     /*
  200.      * If wrapscan isn't set, then don't scan from the beginning
  201.      * of the file. Just return failure here.
  202.      */
  203.     if (!P(P_WS)) {
  204.         free(prog);
  205.         return NULL;
  206.     }
  207.  
  208.     /* search from the beginning of the file to Curschar */
  209.     for (p = Filemem; p != NULL ;p = nextline(p)) {
  210.         s = p->linep->s;
  211.  
  212.         if (regexec(prog, s)) {        /* got a match */
  213.             infile.linep = p->linep;
  214.             infile.index = (int) (prog->startp[0] - s);
  215.             free(prog);
  216.             return (&infile);
  217.         }
  218.  
  219.         if (p->linep == Curschar->linep)
  220.             break;
  221.     }
  222.  
  223.     free(prog);
  224.     return(NULL);
  225. }
  226.  
  227. static LPTR *
  228. bcksearch(str)
  229. char *str;
  230. {
  231.     static LPTR infile;
  232.     register LPTR *p;
  233.     regexp    *prog;
  234.     register char *s;
  235.     register int i;
  236.     bool_t    want_start = (*str == '^');    /* looking for start of line? */
  237.     register char    *match;
  238.  
  239.     /* make sure str isn't empty */
  240.     if (str == NULL || *str == NUL)
  241.         return NULL;
  242.  
  243.     if ((prog = regcomp(str)) == NULL) {
  244.         emsg("Invalid search string");
  245.         return NULL;
  246.     }
  247.  
  248.     p = Curschar;
  249.     dec(p);
  250.  
  251.     if (begword)        /* so we don't get stuck on one match */
  252.         dec(p);
  253.  
  254.     i = (want_start) ? 0 : p->index;
  255.  
  256.     do {
  257.         s = p->linep->s;
  258.  
  259.         if (regexec(prog, s)) {        /* match somewhere on line */
  260.  
  261.             if (want_start) {    /* could only have been one */
  262.                 infile.linep = p->linep;
  263.                 infile.index = (int) (prog->startp[0] - s);
  264.                 free(prog);
  265.                 return (&infile);
  266.             }
  267.  
  268.             /*
  269.              * Now, if there are multiple matches on this line,
  270.              * we have to get the last one. Or the last one
  271.              * before the cursor, if we're on that line.
  272.              */
  273.  
  274.             match = prog->startp[0];
  275.  
  276.             while (regexec(prog, prog->endp[0])) {
  277.                 if ((i >= 0) && ((prog->startp[0] - s) > i))
  278.                     break;
  279.                 match = prog->startp[0];
  280.             }
  281.  
  282.             if ((i >= 0) && ((match - s) > i)) {
  283.                 i = -1;
  284.                 continue;
  285.             }
  286.  
  287.             infile.linep = p->linep;
  288.             infile.index = (int) (match - s);
  289.             free(prog);
  290.             return (&infile);
  291.         }
  292.         i = -1;
  293.  
  294.     } while ((p = prevline(p)) != NULL);
  295.  
  296.     /*
  297.      * If wrapscan isn't set, bag the search now
  298.      */
  299.     if (!P(P_WS)) {
  300.         free(prog);
  301.         return NULL;
  302.     }
  303.  
  304.     /* search backward from the end of the file */
  305.     p = prevline(Fileend);
  306.     do {
  307.         s = p->linep->s;
  308.  
  309.         if (regexec(prog, s)) {        /* match somewhere on line */
  310.  
  311.             if (want_start) {    /* could only have been one */
  312.                 infile.linep = p->linep;
  313.                 infile.index = (int) (prog->startp[0] - s);
  314.                 free(prog);
  315.                 return (&infile);
  316.             }
  317.  
  318.             /*
  319.              * Now, if there are multiple matches on this line,
  320.              * we have to get the last one.
  321.              */
  322.  
  323.             match = prog->startp[0];
  324.  
  325.             while (regexec(prog, prog->endp[0]))
  326.                 match = prog->startp[0];
  327.  
  328.             infile.linep = p->linep;
  329.             infile.index = (int) (match - s);
  330.             free(prog);
  331.             return (&infile);
  332.         }
  333.  
  334.         if (p->linep == Curschar->linep)
  335.             break;
  336.  
  337.     } while ((p = prevline(p)) != NULL);
  338.  
  339.     free(prog);
  340.     return NULL;
  341. }
  342.  
  343. /*
  344.  * Character Searches
  345.  */
  346.  
  347. static char lastc = NUL;    /* last character searched for */
  348. static int  lastcdir;        /* last direction of character search */
  349. static int  lastctype;        /* last type of search ("find" or "to") */
  350.  
  351. /*
  352.  * searchc(c, dir, type)
  353.  *
  354.  * Search for character 'c', in direction 'dir'. If type is 0, move to
  355.  * the position of the character, otherwise move to just before the char.
  356.  */
  357. bool_t
  358. searchc(c, dir, type)
  359. char    c;
  360. int    dir;
  361. int    type;
  362. {
  363.     LPTR    save;
  364.  
  365.     save = *Curschar;    /* save position in case we fail */
  366.     lastc = c;
  367.     lastcdir = dir;
  368.     lastctype = type;
  369.  
  370.     /*
  371.      * On 'to' searches, skip one to start with so we can repeat
  372.      * searches in the same direction and have it work right.
  373.      */
  374.     if (type)
  375.         (dir == FORWARD) ? oneright() : oneleft();
  376.  
  377.     while ( (dir == FORWARD) ? oneright() : oneleft() ) {
  378.         if (gchar(Curschar) == c) {
  379.             if (type)
  380.                 (dir == FORWARD) ? oneleft() : oneright();
  381.             return TRUE;
  382.         }
  383.     }
  384.     *Curschar = save;
  385.     return FALSE;
  386. }
  387.  
  388. bool_t
  389. crepsearch(flag)
  390. int    flag;
  391. {
  392.     int    dir = lastcdir;
  393.     int    rval;
  394.  
  395.     if (lastc == NUL)
  396.         return FALSE;
  397.  
  398.     rval = searchc(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype);
  399.  
  400.     lastcdir = dir;        /* restore dir., since it may have changed */
  401.  
  402.     return rval;
  403. }
  404.  
  405. /*
  406.  * "Other" Searches
  407.  */
  408.  
  409. /*
  410.  * showmatch - move the cursor to the matching paren or brace
  411.  */
  412. LPTR *
  413. showmatch()
  414. {
  415.     static    LPTR    pos;
  416.     int    (*move)(), inc(), dec();
  417.     char    initc = gchar(Curschar);    /* initial char */
  418.     char    findc;                /* terminating char */
  419.     char    c;
  420.     int    count = 0;
  421.  
  422.     pos = *Curschar;        /* set starting point */
  423.  
  424.     switch (initc) {
  425.  
  426.     case '(':
  427.         findc = ')';
  428.         move = inc;
  429.         break;
  430.     case ')':
  431.         findc = '(';
  432.         move = dec;
  433.         break;
  434.     case '{':
  435.         findc = '}';
  436.         move = inc;
  437.         break;
  438.     case '}':
  439.         findc = '{';
  440.         move = dec;
  441.         break;
  442.     case '[':
  443.         findc = ']';
  444.         move = inc;
  445.         break;
  446.     case ']':
  447.         findc = '[';
  448.         move = dec;
  449.         break;
  450.     default:
  451.         return (LPTR *) NULL;
  452.     }
  453.  
  454.     while ((*move)(&pos) != -1) {        /* until end of file */
  455.         c = gchar(&pos);
  456.         if (c == initc)
  457.             count++;
  458.         else if (c == findc) {
  459.             if (count == 0)
  460.                 return &pos;
  461.             count--;
  462.         }
  463.     }
  464.     return (LPTR *) NULL;            /* never found it */
  465. }
  466.  
  467. /*
  468.  * findfunc(dir) - Find the next function in direction 'dir'
  469.  *
  470.  * Return TRUE if a function was found.
  471.  */
  472. bool_t
  473. findfunc(dir)
  474. int    dir;
  475. {
  476.     LPTR    *curr;
  477.  
  478.     curr = Curschar;
  479.  
  480.     do {
  481.         curr = (dir == FORWARD) ? nextline(curr) : prevline(curr);
  482.  
  483.         if (curr != NULL && curr->linep->s[0] == '{') {
  484.             setpcmark();
  485.             *Curschar = *curr;
  486.             return TRUE;
  487.         }
  488.     } while (curr != NULL);
  489.  
  490.     return FALSE;
  491. }
  492.  
  493. /*
  494.  * The following routines do the word searches performed by the
  495.  * 'w', 'W', 'b', 'B', 'e', and 'E' commands.
  496.  */
  497.  
  498. /*
  499.  * To perform these searches, characters are placed into one of three
  500.  * classes, and transitions between classes determine word boundaries.
  501.  *
  502.  * The classes are:
  503.  *
  504.  * 0 - white space
  505.  * 1 - letters, digits, and underscore
  506.  * 2 - everything else
  507.  */
  508.  
  509. static    int    stype;        /* type of the word motion being performed */
  510.  
  511. #define    C0(c)    (((c) == ' ') || ((c) == '\t') || ((c) == NUL))
  512. #define    C1(c)    (isalpha(c) || isdigit(c) || ((c) == '_'))
  513.  
  514. /*
  515.  * cls(c) - returns the class of character 'c'
  516.  *
  517.  * The 'type' of the current search modifies the classes of characters
  518.  * if a 'W', 'B', or 'E' motion is being done. In this case, chars. from
  519.  * class 2 are reported as class 1 since only white space boundaries are
  520.  * of interest.
  521.  */
  522. static    int
  523. cls(c)
  524. char    c;
  525. {
  526.     if (C0(c))
  527.         return 0;
  528.  
  529.     if (C1(c))
  530.         return 1;
  531.  
  532.     /*
  533.      * If stype is non-zero, report these as class 1.
  534.      */
  535.     return (stype == 0) ? 2 : 1;
  536. }
  537.  
  538.  
  539. /*
  540.  * fwd_word(pos, type) - move forward one word
  541.  *
  542.  * Returns the resulting position, or NULL if EOF was reached.
  543.  */
  544. LPTR *
  545. fwd_word(p, type)
  546. LPTR    *p;
  547. int    type;
  548. {
  549.     static    LPTR    pos;
  550.     int    sclass = cls(gchar(p));        /* starting class */
  551.  
  552.     pos = *p;
  553.  
  554.     stype = type;
  555.  
  556.     /*
  557.      * We always move at least one character.
  558.      */
  559.     if (inc(&pos) == -1)
  560.         return NULL;
  561.  
  562.     if (sclass != 0) {
  563.         while (cls(gchar(&pos)) == sclass) {
  564.             if (inc(&pos) == -1)
  565.                 return NULL;
  566.         }
  567.         /*
  568.          * If we went from 1 -> 2 or 2 -> 1, return here.
  569.          */
  570.         if (cls(gchar(&pos)) != 0)
  571.             return &pos;
  572.     }
  573.  
  574.     /* We're in white space; go to next non-white */
  575.  
  576.     while (cls(gchar(&pos)) == 0) {
  577.         /*
  578.          * We'll stop if we land on a blank line
  579.          */
  580.         if (pos.index == 0 && pos.linep->s[0] == NUL)
  581.             break;
  582.  
  583.         if (inc(&pos) == -1)
  584.             return NULL;
  585.     }
  586.  
  587.     return &pos;
  588. }
  589.  
  590. /*
  591.  * bck_word(pos, type) - move backward one word
  592.  *
  593.  * Returns the resulting position, or NULL if EOF was reached.
  594.  */
  595. LPTR *
  596. bck_word(p, type)
  597. LPTR    *p;
  598. int    type;
  599. {
  600.     static    LPTR    pos;
  601.     int    sclass = cls(gchar(p));        /* starting class */
  602.  
  603.     pos = *p;
  604.  
  605.     stype = type;
  606.  
  607.     if (dec(&pos) == -1)
  608.         return NULL;
  609.  
  610.     /*
  611.      * If we're in the middle of a word, we just have to
  612.      * back up to the start of it.
  613.      */
  614.     if (cls(gchar(&pos)) == sclass && sclass != 0) {
  615.         /*
  616.          * Move backward to start of the current word
  617.          */
  618.         while (cls(gchar(&pos)) == sclass) {
  619.             if (dec(&pos) == -1)
  620.                 return NULL;
  621.         }
  622.         inc(&pos);            /* overshot - forward one */
  623.         return &pos;
  624.     }
  625.  
  626.     /*
  627.      * We were at the start of a word. Go back to the start
  628.      * of the prior word.
  629.      */
  630.  
  631.     while (cls(gchar(&pos)) == 0) {        /* skip any white space */
  632.         /*
  633.          * We'll stop if we land on a blank line
  634.          */
  635.         if (pos.index == 0 && pos.linep->s[0] == NUL)
  636.             return &pos;
  637.  
  638.         if (dec(&pos) == -1)
  639.             return NULL;
  640.     }
  641.  
  642.     sclass = cls(gchar(&pos));
  643.  
  644.     /*
  645.      * Move backward to start of this word.
  646.      */
  647.     while (cls(gchar(&pos)) == sclass) {
  648.         if (dec(&pos) == -1)
  649.             return NULL;
  650.     }
  651.     inc(&pos);            /* overshot - forward one */
  652.  
  653.     return &pos;
  654. }
  655.  
  656. /*
  657.  * end_word(pos, type) - move to the end of the word
  658.  *
  659.  * There is an apparent bug in the 'e' motion of the real vi. At least
  660.  * on the System V Release 3 version for the 80386. Unlike 'b' and 'w',
  661.  * the 'e' motion crosses blank lines. When the real vi crosses a blank
  662.  * line in an 'e' motion, the cursor is placed on the FIRST character
  663.  * of the next non-blank line. The 'E' command, however, works correctly.
  664.  * Since this appears to be a bug, I have not duplicated it here.
  665.  *
  666.  * Returns the resulting position, or NULL if EOF was reached.
  667.  */
  668. LPTR *
  669. end_word(p, type)
  670. LPTR    *p;
  671. int    type;
  672. {
  673.     static    LPTR    pos;
  674.     int    sclass = cls(gchar(p));        /* starting class */
  675.  
  676.     pos = *p;
  677.  
  678.     stype = type;
  679.  
  680.     if (inc(&pos) == -1)
  681.         return NULL;
  682.  
  683.     /*
  684.      * If we're in the middle of a word, we just have to
  685.      * move to the end of it.
  686.      */
  687.     if (cls(gchar(&pos)) == sclass && sclass != 0) {
  688.         /*
  689.          * Move forward to end of the current word
  690.          */
  691.         while (cls(gchar(&pos)) == sclass) {
  692.             if (inc(&pos) == -1)
  693.                 return NULL;
  694.         }
  695.         dec(&pos);            /* overshot - forward one */
  696.         return &pos;
  697.     }
  698.  
  699.     /*
  700.      * We were at the end of a word. Go to the end
  701.      * of the next word.
  702.      */
  703.  
  704.     while (cls(gchar(&pos)) == 0) {        /* skip any white space */
  705.         if (inc(&pos) == -1)
  706.             return NULL;
  707.     }
  708.  
  709.     sclass = cls(gchar(&pos));
  710.  
  711.     /*
  712.      * Move forward to end of this word.
  713.      */
  714.     while (cls(gchar(&pos)) == sclass) {
  715.         if (inc(&pos) == -1)
  716.             return NULL;
  717.     }
  718.     dec(&pos);            /* overshot - forward one */
  719.  
  720.     return &pos;
  721. }
  722.