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