home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / x / xvisrc.zoo / find.c < prev    next >
C/C++ Source or Header  |  1992-07-28  |  12KB  |  594 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)find.c    2.2 (Chris & John Downey) 7/29/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     find.c
  14. * module function:
  15.     Character and function searching.
  16. * history:
  17.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  18.     Originally by Tim Thompson (twitch!tjt)
  19.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  20.     Heavily modified by Chris & John Downey
  21.  
  22. ***/
  23.  
  24. #include "xvi.h"
  25. #include "regexp.h"    /* Henry Spencer's regular expression routines */
  26.  
  27. #ifdef    MEGAMAX
  28. overlay "find"
  29. #endif
  30.  
  31. /*
  32.  * Character Searches
  33.  */
  34.  
  35. static char lastc;        /* last character searched for */
  36. static int  lastcdir;        /* last direction of character search */
  37. static int  lastctype;        /* last type of search ("find" or "to") */
  38.  
  39. /*
  40.  * searchc(c, dir, type, num)
  41.  *
  42.  * Search for character 'c', in direction 'dir'. If type is 0, move to
  43.  * the position of the character, otherwise move to just before the char.
  44.  * 'num' is the number of times to do it (the prefix number).
  45.  */
  46. Posn *
  47. searchc(ch, dir, type, num)
  48. int    ch;
  49. int    dir;
  50. int    type;
  51. int    num;
  52. {
  53.     static Posn        pos;            /* saved cursor posn */
  54.     enum mvtype        (*mvfunc) P((Posn *));
  55.     enum mvtype        (*backfunc) P((Posn *));
  56.     unsigned char    c;            /* as ch but uchar */
  57.     register int    i;            /* loop counter */
  58.  
  59.     /*
  60.      * Remember details in case we want to repeat the search.
  61.      */
  62.     lastc = ch;
  63.     lastcdir = dir;
  64.     lastctype = type;
  65.  
  66.     pos = *(curwin->w_cursor);    /* save position in case we fail */
  67.     c = ch;
  68.     if (dir == FORWARD) {
  69.     mvfunc = inc;
  70.     backfunc = dec;
  71.     } else {
  72.     mvfunc = dec;
  73.     backfunc = inc;
  74.     }
  75.  
  76.     /*
  77.      * On 'to' searches, skip one to start with so we can repeat
  78.      * searches in the same direction and have it work right.
  79.      */
  80.     if (type) {
  81.     if ((*mvfunc)(&pos) != mv_SAMELINE) {
  82.         return(NULL);
  83.     }
  84.     }
  85.  
  86.     for (i = 0; i < num; i++) {
  87.     bool_t    found;
  88.  
  89.     found = FALSE;
  90.     while ((*mvfunc)(&pos) == mv_SAMELINE) {
  91.         if ((unsigned char) gchar(&pos) == c) {
  92.         found = TRUE;
  93.         break;
  94.         }
  95.     }
  96.     if (!found) {
  97.         return(NULL);
  98.     }
  99.     }
  100.     if (type) {
  101.     (void) (*backfunc)(&pos);
  102.     }
  103.     return(&pos);
  104. }
  105.  
  106. /*ARGSUSED*/
  107. Posn *
  108. crepsearch(buffer, flag, num)
  109. Buffer    *buffer;
  110. int    flag;
  111. int    num;
  112. {
  113.     Posn    *newpos;
  114.     int    dir;
  115.     int    savedir;
  116.  
  117.     if (lastc == '\0') {
  118.     return(NULL);
  119.     }
  120.  
  121.     savedir = lastcdir;
  122.     if (flag) {
  123.     dir = (lastcdir == FORWARD) ? BACKWARD : FORWARD;
  124.     } else {
  125.     dir = lastcdir;
  126.     }
  127.  
  128.     newpos = searchc(lastc, dir, lastctype, num);
  129.  
  130.     lastcdir = savedir;        /* put direction of search back how it was */
  131.  
  132.     return(newpos);
  133. }
  134.  
  135. /*
  136.  * "Other" Searches
  137.  */
  138.  
  139. /*
  140.  * showmatch - move the cursor to the matching paren or brace
  141.  */
  142. Posn *
  143. showmatch()
  144. {
  145.     register char    initc;            /* initial char */
  146.     register enum mvtype
  147.                 (*move) P((Posn *));    /* function to move cursor */
  148.     register char    findc;            /* terminating char */
  149.     static Posn        pos;            /* current position */
  150.     char        c;
  151.     int            count = 0;
  152.     bool_t        found = FALSE;
  153.  
  154.     pos = *curwin->w_cursor;
  155.  
  156.     /*
  157.      * Move forward to the first bracket character after the cursor.
  158.      * If we get to EOF before a bracket, return NULL.
  159.      */
  160.     for (found = FALSE; !found; ) {
  161.     initc = gchar(&pos);
  162.     switch (initc) {
  163.     case '(':
  164.         findc = ')';
  165.         move = inc;
  166.         found = TRUE;
  167.         break;
  168.     case ')':
  169.         findc = '(';
  170.         move = dec;
  171.         found = TRUE;
  172.         break;
  173.     case '{':
  174.         findc = '}';
  175.         move = inc;
  176.         found = TRUE;
  177.         break;
  178.     case '}':
  179.         findc = '{';
  180.         move = dec;
  181.         found = TRUE;
  182.         break;
  183.     case '[':
  184.         findc = ']';
  185.         move = inc;
  186.         found = TRUE;
  187.         break;
  188.     case ']':
  189.         findc = '[';
  190.         move = dec;
  191.         found = TRUE;
  192.         break;
  193.     default:
  194.         if (inc(&pos) == mv_NOMOVE) {
  195.         return(NULL);
  196.         }
  197.     }
  198.     }
  199.  
  200.     /*
  201.      * Move in the appropriate direction until we find a matching
  202.      * bracket or reach end of file.
  203.      */
  204.     while ((*move)(&pos) != mv_NOMOVE) {
  205.     c = gchar(&pos);
  206.     if (c == initc) {
  207.         count++;
  208.     } else if (c == findc) {
  209.         if (count == 0)
  210.         return(&pos);
  211.         count--;
  212.     }
  213.     }
  214.     return(NULL);            /* never found it */
  215. }
  216.  
  217. /*
  218.  * Find the nth next occurrence of str in the specified direction.
  219.  */
  220. Posn *
  221. find_pattern(str, dir, num)
  222. char    *str;
  223. int    dir;
  224. int    num;
  225. {
  226.     Posn    *p;
  227.     Posn    *lastp;
  228.  
  229.     p = curwin->w_cursor;
  230.     lastp = NULL;
  231.     while (num-- > 0) {
  232.     p = nsearch(curwin, p->p_line, p->p_index, dir, str);
  233.     if (p != NULL) {
  234.         lastp = p;
  235.     } else {
  236.         break;
  237.     }
  238.     }
  239.  
  240.     return(lastp);
  241. }
  242.  
  243. /*
  244.  * The following routines do the word searches performed by the
  245.  * 'w', 'W', 'b', 'B', 'e', and 'E' commands.
  246.  */
  247.  
  248. /*
  249.  * To perform these searches, characters are placed into one of three
  250.  * classes, and transitions between classes determine word boundaries.
  251.  *
  252.  * The classes are:
  253.  *
  254.  * cl_white - white space
  255.  * cl_text  - letters, digits, and underscore
  256.  * cl_punc  - everything else
  257.  */
  258.  
  259. typedef enum {
  260.     cl_white,
  261.     cl_text,
  262.     cl_punc
  263. } cclass;
  264.  
  265. static    int    stype;        /* type of the word motion being performed */
  266.  
  267. #define is_white(c)    (((c) == ' ') || ((c) == '\t') || ((c) == '\0'))
  268. #define is_text(c)    (is_alnum(c) || ((c) == '_'))
  269.  
  270. /*
  271.  * cls(c) - returns the class of character 'c'
  272.  *
  273.  * The 'type' of the current search modifies the classes of characters
  274.  * if a 'W', 'B', or 'E' motion is being done. In this case, chars. from
  275.  * class "cl_punc" are reported as class "cl_text" since only white space
  276.  * boundaries are of interest.
  277.  */
  278. static cclass
  279. cls(c)
  280. char    c;
  281. {
  282.     if (is_white(c))
  283.     return(cl_white);
  284.  
  285.     if (is_text(c))
  286.     return(cl_text);
  287.  
  288.     /*
  289.      * If stype is non-zero, report these as class 1.
  290.      */
  291.     return((stype == 0) ? cl_punc : cl_text);
  292. }
  293.  
  294.  
  295. /*
  296.  * fwd_word(pos, type, skip_white) - move forward one word
  297.  *
  298.  * Returns the resulting position, or NULL if EOF was reached.
  299.  *
  300.  * The extra argument "skip_white" (which is only used in fwd_word,
  301.  * but also included in bck_word and end_word for compatibility) is
  302.  * used to indicate whether to skip over white space to get to the
  303.  * start of the next word. If it is FALSE, the position returned will
  304.  * be the first white-space character (or punctuation) encountered.
  305.  * This is used by one command only: "cw".
  306.  */
  307. Posn *
  308. fwd_word(p, type, skip_white)
  309. Posn    *p;
  310. int    type;
  311. bool_t    skip_white;
  312. {
  313.     static    Posn    pos;
  314.     cclass        sclass;        /* starting class */
  315.  
  316.     stype = type;
  317.     sclass = cls(gchar(p));
  318.  
  319.     pos = *p;
  320.  
  321.     /*
  322.      * We always move at least one character.
  323.      */
  324.     if (inc(&pos) == mv_NOMOVE)
  325.     return(NULL);
  326.  
  327.     if (sclass != cl_white) {
  328.     /*
  329.      * We were in the middle of a word to start with.
  330.      * Move right until we change character class.
  331.      */
  332.     while (cls(gchar(&pos)) == sclass) {
  333.         if (inc(&pos) == mv_NOMOVE) {
  334.         /*
  335.          * Got to EOF. Return current position.
  336.          */
  337.         return(&pos);
  338.         }
  339.     }
  340.  
  341.     /*
  342.      * If we went from punctuation -> text
  343.      * or text -> punctuation, return here.
  344.      *
  345.      * If we went text/punctuation -> whitespace,
  346.      * we want to continue to the start of the
  347.      * next word, if there is one.
  348.      */
  349.     if (cls(gchar(&pos)) != cl_white)
  350.         return(&pos);
  351.     }
  352.  
  353.     /*
  354.      * We're in white space; go to next non-white.
  355.      */
  356.     if (skip_white) {
  357.     while (cls(gchar(&pos)) == cl_white) {
  358.         /*
  359.          * We'll stop if we land on a blank line
  360.          */
  361.         if (pos.p_index == 0 && pos.p_line->l_text[0] == '\0')
  362.         break;
  363.  
  364.         if (inc(&pos) == mv_NOMOVE) {
  365.         /*
  366.          * We have reached end of file; if we are at
  367.          * the beginning of a line, just return that
  368.          * position, otherwise try to back up so that
  369.          * we are still within the line.
  370.          */
  371.         if (pos.p_index != 0) {
  372.             (void) dec(&pos);
  373.         }
  374.         break;
  375.         }
  376.     }
  377.  
  378.     /*
  379.      * If we didn't move, return NULL.
  380.      */
  381.     if (pos.p_line == p->p_line && pos.p_index == p->p_index) {
  382.         return(NULL);
  383.     }
  384.     }
  385.  
  386.     return(&pos);
  387. }
  388.  
  389. /*
  390.  * bck_word(pos, type, skip_white) - move backward one word
  391.  *
  392.  * Returns the resulting position, or NULL if EOF was reached.
  393.  */
  394. /*ARGSUSED*/
  395. Posn *
  396. bck_word(p, type, skip_white)
  397. Posn    *p;
  398. int    type;
  399. bool_t    skip_white;
  400. {
  401.     static    Posn    pos;
  402.     cclass        sclass;        /* starting class */
  403.  
  404.     stype = type;
  405.     sclass = cls(gchar(p));
  406.  
  407.     pos = *p;
  408.  
  409.     if (dec(&pos) == mv_NOMOVE)
  410.     return(NULL);
  411.  
  412.     /*
  413.      * If we're in the middle of a word, we just have to
  414.      * back up to the start of it.
  415.      */
  416.     if (cls(gchar(&pos)) == sclass && sclass != cl_white) {
  417.     /*
  418.      * Move backward to start of the current word
  419.      */
  420.     while (cls(gchar(&pos)) == sclass) {
  421.         if (dec(&pos) == mv_NOMOVE)
  422.         return(&pos);
  423.     }
  424.     (void) inc(&pos);    /* overshot - forward one */
  425.     return(&pos);
  426.     }
  427.  
  428.     /*
  429.      * We were at the start of a word. Go back to the start
  430.      * of the prior word.
  431.      */
  432.  
  433.     while (cls(gchar(&pos)) == cl_white) {    /* skip any white space */
  434.     /*
  435.      * We'll stop if we land on a blank line
  436.      */
  437.     if (pos.p_index == 0 && pos.p_line->l_text[0] == '\0')
  438.         return(&pos);
  439.  
  440.     if (dec(&pos) == mv_NOMOVE)
  441.         return(&pos);
  442.     }
  443.  
  444.     sclass = cls(gchar(&pos));
  445.  
  446.     /*
  447.      * Move backward to start of this word.
  448.      */
  449.     while (cls(gchar(&pos)) == sclass) {
  450.     if (dec(&pos) == mv_NOMOVE)
  451.         return(&pos);
  452.     }
  453.     (void) inc(&pos);        /* overshot - forward one */
  454.  
  455.     return(&pos);
  456. }
  457.  
  458. /*
  459.  * end_word(pos, type, skip_white) - move to the next end-of-word after
  460.  *                     the current cursor position
  461.  *
  462.  * There is an apparent bug in the 'e' motion of the real vi. At least
  463.  * on the System V Release 3 version for the 80386. Unlike 'b' and 'w',
  464.  * the 'e' motion crosses blank lines. When the real vi crosses a blank
  465.  * line in an 'e' motion, the cursor is placed on the FIRST character
  466.  * of the next non-blank line. The 'E' command, however, works correctly.
  467.  * Since this appears to be a bug, I have not duplicated it here.
  468.  *
  469.  * Returns the resulting position, or NULL if EOF was reached.
  470.  */
  471. /*ARGSUSED*/
  472. Posn *
  473. end_word(p, type, skip_white)
  474. Posn    *p;
  475. int    type;
  476. bool_t    skip_white;
  477. {
  478.     static    Posn    pos;
  479.     cclass        sclass;
  480.  
  481.     stype = type;
  482.     sclass = cls(gchar(p));
  483.  
  484.     pos = *p;
  485.  
  486.     if (inc(&pos) == mv_NOMOVE)
  487.     return(NULL);
  488.  
  489.     /*
  490.      * If we're in the middle of a word, we just have to
  491.      * move to the end of it.
  492.      */
  493.     if (cls(gchar(&pos)) == sclass && sclass != cl_white) {
  494.     /*
  495.      * Move forward to end of the current word
  496.      */
  497.     while (cls(gchar(&pos)) == sclass) {
  498.         if (inc(&pos) == mv_NOMOVE) {
  499.         return(&pos);
  500.         }
  501.     }
  502.     (void) dec(&pos);        /* overshot - forward one */
  503.     return(&pos);
  504.     }
  505.  
  506.     /*
  507.      * We were at the end of a word. Go to the end
  508.      * of the next word.
  509.      */
  510.  
  511.     while (cls(gchar(&pos)) == cl_white) {    /* skip any white space */
  512.     if (inc(&pos) == mv_NOMOVE) {
  513.         return(&pos);
  514.     }
  515.     }
  516.  
  517.     sclass = cls(gchar(&pos));
  518.  
  519.     /*
  520.      * Move forward to end of this word.
  521.      */
  522.     while (cls(gchar(&pos)) == sclass) {
  523.     if (inc(&pos) == mv_NOMOVE) {
  524.         return(&pos);
  525.     }
  526.     }
  527.     (void) dec(&pos);            /* overshot - forward one */
  528.  
  529.     return(&pos);
  530. }
  531.  
  532. /*
  533.  * Search for the given pattern in the given buffer,
  534.  * in the direction specified.
  535.  */
  536. bool_t
  537. dosearch(window, str, cmd_char)
  538. Xviwin        *window;
  539. char        *str;
  540. int        cmd_char;
  541. {
  542.     Posn    *p;
  543.     unsigned    savecho;
  544.     bool_t    retval;
  545.     int        dir;
  546.     static int    lastdir = FORWARD;
  547.  
  548.     /*
  549.      * Place the cursor at bottom left of the window,
  550.      * so the user knows what we are doing.
  551.      */
  552.     gotocmd(window, FALSE);
  553.  
  554.     switch (cmd_char) {
  555.     case '/':
  556.     lastdir = dir = FORWARD;
  557.     break;
  558.     case '?':
  559.     lastdir = dir = BACKWARD;
  560.     break;
  561.     case 'n':
  562.     dir = lastdir;
  563.     break;
  564.     case 'N':
  565.     dir = (lastdir == FORWARD) ? BACKWARD : FORWARD;
  566.     break;
  567.     }
  568.  
  569.     /*
  570.      * It is safe not to put the cursor back, because
  571.      * we are going to produce some more output anyway.
  572.      */
  573.  
  574.     savecho = echo;
  575.  
  576.     p = search(window, window->w_cursor->p_line,
  577.             window->w_cursor->p_index, dir, &str);
  578.     if (p == NULL) {
  579.     regerror("Pattern not found");
  580.     retval = FALSE;
  581.     } else if (*str != '\0') {
  582.     regerror("Usage: /pattern or ?pattern");
  583.     retval = FALSE;
  584.     } else {
  585.     setpcmark(window);
  586.     move_cursor(window, p->p_line, p->p_index);
  587.     window->w_set_want_col = TRUE;
  588.     retval = TRUE;
  589.     }
  590.  
  591.     echo = savecho;
  592.     return retval;
  593. }
  594.