home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / LESS177.ZIP / src / search.c < prev    next >
C/C++ Source or Header  |  1992-07-18  |  7KB  |  353 lines

  1. /*
  2.  * Routines to search a file for a pattern.
  3.  */
  4.  
  5. #include "less.h"
  6. #include "position.h"
  7. #if REGCOMP
  8. #include "regexp.h"
  9. #endif
  10.  
  11. extern int sigs;
  12. extern int how_search;
  13. extern int caseless;
  14. extern int linenums;
  15. extern int jump_sline;
  16.  
  17. /*
  18.  * Search for the n-th occurrence of a specified pattern, 
  19.  * either forward or backward.
  20.  * Return the number of matches not yet found in this file
  21.  * (that is, n minus the number of matches found).
  22.  * Return -1 if the search should be aborted.
  23.  * Caller may continue the search in another file 
  24.  * if less than n matches are found in this file.
  25.  */
  26.     public int
  27. search(search_type, pattern, n)
  28.     int search_type;
  29.     char *pattern;
  30.     int n;
  31. {
  32.     POSITION pos, linepos, oldpos;
  33.     register char *p;
  34.     register char *q;
  35.     register int goforw;
  36.     register int want_match;
  37.     char *line;
  38.     int linenum;
  39.     int line_match;
  40.     static int is_caseless;
  41. #if RECOMP
  42.     char *re_comp();
  43.     PARG parg;
  44. #else
  45. #if REGCMP
  46.     char *regcmp();
  47.     static char *cpattern = NULL;
  48. #else
  49. #if REGCOMP
  50.     static struct regexp *regpattern = NULL;
  51. #else
  52.     static char lpbuf[100];
  53.     static char *last_pattern = NULL;
  54. #endif
  55. #endif
  56. #endif
  57.  
  58.     /*
  59.      * Extract flags and type of search.
  60.      */
  61.     goforw = (SRCH_DIR(search_type) == SRCH_FORW);
  62.     want_match = !(search_type & SRCH_NOMATCH);
  63.  
  64.     if (pattern != NULL && *pattern != '\0' && (is_caseless = caseless))
  65.     {
  66.         /*
  67.          * Search will ignore case, unless
  68.          * there are any uppercase letters in the pattern.
  69.          */
  70.         for (p = pattern;  *p != '\0';  p++)
  71.             if (*p >= 'A' && *p <= 'Z')
  72.             {
  73.                 is_caseless = 0;
  74.                 break;
  75.             }
  76.     }
  77. #if RECOMP
  78.  
  79.     /*
  80.      * (re_comp handles a null pattern internally, 
  81.      *  so there is no need to check for a null pattern here.)
  82.      */
  83.     if ((parg.p_string = re_comp(pattern)) != NULL)
  84.     {
  85.         error("%s", &parg);
  86.         return (-1);
  87.     }
  88. #else
  89. #if REGCMP
  90.     if (pattern == NULL || *pattern == '\0')
  91.     {
  92.         /*
  93.          * A null pattern means use the previous pattern.
  94.          * The compiled previous pattern is in cpattern, so just use it.
  95.          */
  96.         if (cpattern == NULL)
  97.         {
  98.             error("No previous regular expression", NULL_PARG);
  99.             return (-1);
  100.         }
  101.     } else
  102.     {
  103.         /*
  104.          * Otherwise compile the given pattern.
  105.          */
  106.         char *s;
  107.         if ((s = regcmp(pattern, 0)) == NULL)
  108.         {
  109.             error("Invalid pattern", NULL_PARG);
  110.             return (-1);
  111.         }
  112.         if (cpattern != NULL)
  113.             free(cpattern);
  114.         cpattern = s;
  115.     }
  116. #else
  117. #if REGCOMP
  118.     if (pattern == NULL || *pattern == '\0')
  119.     {
  120.         /*
  121.          * A null pattern means use the previous pattern.
  122.          * The compiled previous pattern is in regpattern, 
  123.          * so just use it.
  124.          */
  125.         if (regpattern == NULL)
  126.         {
  127.             error("No previous regular expression", NULL_PARG);
  128.             return (-1);
  129.         }
  130.     } else
  131.     {
  132.         /*
  133.          * Otherwise compile the given pattern.
  134.          */
  135.         struct regexp *s;
  136.         if ((s = regcomp(pattern)) == NULL)
  137.         {
  138.             error("Invalid pattern", NULL_PARG);
  139.             return (-1);
  140.         }
  141.         if (regpattern != NULL)
  142.             free(regpattern);
  143.         regpattern = s;
  144.     }
  145. #else
  146.     if (pattern == NULL || *pattern == '\0')
  147.     {
  148.         /*
  149.          * Null pattern means use the previous pattern.
  150.          */
  151.         if (last_pattern == NULL)
  152.         {
  153.             error("No previous regular expression", NULL_PARG);
  154.             return (-1);
  155.         }
  156.         pattern = last_pattern;
  157.     } else
  158.     {
  159.         strcpy(lpbuf, pattern);
  160.         last_pattern = lpbuf;
  161.     }
  162. #endif
  163. #endif
  164. #endif
  165.  
  166.     /*
  167.      * Figure out where to start the search.
  168.      */
  169.     if (empty_screen())
  170.     {
  171.         /*
  172.          * Start at the beginning (or end) of the file.
  173.          * (The empty_screen() case is mainly for 
  174.          * command line initiated searches;
  175.          * for example, "+/xyz" on the command line.)
  176.          */
  177.         if (goforw)
  178.             pos = ch_zero();
  179.         else 
  180.         {
  181.             pos = ch_length();
  182.             if (pos == NULL_POSITION)
  183.                 pos = ch_zero();
  184.         }
  185.     } else 
  186.     {
  187.         if (how_search)
  188.         {
  189.             if (goforw)
  190.                 linenum = BOTTOM_PLUS_ONE;
  191.             else
  192.                 linenum = TOP;
  193.             pos = position(linenum);
  194.         } else
  195.         {
  196.             linenum = adjsline(jump_sline);
  197.             pos = position(linenum);
  198.             if (goforw)
  199.                 pos = forw_raw_line(pos, (char **)NULL);
  200.         }
  201.     }
  202.  
  203.     if (pos == NULL_POSITION)
  204.     {
  205.         /*
  206.          * Can't find anyplace to start searching from.
  207.          */
  208.         error("Nothing to search", NULL_PARG);
  209.         return (-1);
  210.     }
  211.  
  212.     linenum = find_linenum(pos);
  213.     oldpos = pos;
  214.     for (;;)
  215.     {
  216.         /*
  217.          * Get lines until we find a matching one or 
  218.          * until we hit end-of-file (or beginning-of-file 
  219.          * if we're going backwards).
  220.          */
  221.         if (sigs)
  222.             /*
  223.              * A signal aborts the search.
  224.              */
  225.             return (-1);
  226.  
  227.         if (goforw)
  228.         {
  229.             /*
  230.              * Read the next line, and save the 
  231.              * starting position of that line in linepos.
  232.              */
  233.             linepos = pos;
  234.             pos = forw_raw_line(pos, &line);
  235.             if (linenum != 0)
  236.                 linenum++;
  237.         } else
  238.         {
  239.             /*
  240.              * Read the previous line and save the
  241.              * starting position of that line in linepos.
  242.              */
  243.             pos = back_raw_line(pos, &line);
  244.             linepos = pos;
  245.             if (linenum != 0)
  246.                 linenum--;
  247.         }
  248.  
  249.         if (pos == NULL_POSITION)
  250.         {
  251.             /*
  252.              * We hit EOF/BOF without a match.
  253.              */
  254.             return (n);
  255.         }
  256.  
  257.         /*
  258.          * If we're using line numbers, we might as well
  259.          * remember the information we have now (the position
  260.          * and line number of the current line).
  261.          * Don't do it for every line because it slows down
  262.          * the search.  Remember the line number only if
  263.          * we're "far" from the last place we remembered it.
  264.          */
  265.         if (linenums && abs(pos - oldpos) > 1024)
  266.         {
  267.             add_lnum(linenum, pos);
  268.             oldpos = pos;
  269.         }
  270.  
  271.         if (is_caseless)
  272.         {
  273.             /*
  274.              * If this is a caseless search, convert 
  275.              * uppercase in the input line to lowercase.
  276.              * While we're at it, remove any backspaces
  277.              * along with the preceding char.
  278.              * This allows us to match text which is 
  279.              * underlined or overstruck.
  280.              */
  281.             for (p = q = line;  *p != '\0';  p++, q++)
  282.             {
  283.                 if (*p >= 'A' && *p <= 'Z')
  284.                     /* Convert uppercase to lowercase. */
  285.                     *q = *p + 'a' - 'A';
  286.                 else if (q > line && *p == '\b')
  287.                     /* Delete BS and preceding char. */
  288.                     q -= 2;
  289.                 else
  290.                     /* Otherwise, just copy. */
  291.                     *q = *p;
  292.             }
  293.         }
  294.  
  295.         /*
  296.          * Test the next line to see if we have a match.
  297.          * This is done in a variety of ways, depending
  298.          * on what pattern matching functions are available.
  299.          */
  300. #if REGCMP
  301.         line_match = (regex(cpattern, line) != NULL);
  302. #else
  303. #if RECOMP
  304.         line_match = (re_exec(line) == 1);
  305. #else
  306. #if REGCOMP
  307.         line_match = regexec(regpattern, line);
  308. #else
  309.         line_match = match(pattern, line);
  310. #endif
  311. #endif
  312. #endif
  313.         /*
  314.          * We are successful if want_match and line_match are
  315.          * both true (want a match and got it),
  316.          * or both false (want a non-match and got it).
  317.          */
  318.         if (((want_match && line_match) || (!want_match && !line_match)) &&
  319.               --n <= 0)
  320.             /*
  321.              * Found the line.
  322.              */
  323.             break;
  324.     }
  325.  
  326.     jump_loc(linepos, jump_sline);
  327.     return (0);
  328. }
  329.  
  330. #if (!REGCMP) && (!RECOMP) && (!REGCOMP)
  331. /*
  332.  * We have neither regcmp() nor re_comp().
  333.  * We use this function to do simple pattern matching.
  334.  * It supports no metacharacters like *, etc.
  335.  */
  336.     static int
  337. match(pattern, buf)
  338.     char *pattern, *buf;
  339. {
  340.     register char *pp, *lp;
  341.  
  342.     for ( ;  *buf != '\0';  buf++)
  343.     {
  344.         for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
  345.             if (*pp == '\0' || *lp == '\0')
  346.                 break;
  347.         if (*pp == '\0')
  348.             return (1);
  349.     }
  350.     return (0);
  351. }
  352. #endif
  353.