home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / MISC / GNU / LES177AS.ZIP / SEARCH.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-11-14  |  7.3 KB  |  356 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. #ifdef TURBOC
  217.         if (kbhit()) ;    /* permit ^C check */
  218. #endif
  219.         /*
  220.          * Get lines until we find a matching one or 
  221.          * until we hit end-of-file (or beginning-of-file 
  222.          * if we're going backwards).
  223.          */
  224.         if (sigs)
  225.             /*
  226.              * A signal aborts the search.
  227.              */
  228.             return (-1);
  229.  
  230.         if (goforw)
  231.         {
  232.             /*
  233.              * Read the next line, and save the 
  234.              * starting position of that line in linepos.
  235.              */
  236.             linepos = pos;
  237.             pos = forw_raw_line(pos, &line);
  238.             if (linenum != 0)
  239.                 linenum++;
  240.         } else
  241.         {
  242.             /*
  243.              * Read the previous line and save the
  244.              * starting position of that line in linepos.
  245.              */
  246.             pos = back_raw_line(pos, &line);
  247.             linepos = pos;
  248.             if (linenum != 0)
  249.                 linenum--;
  250.         }
  251.  
  252.         if (pos == NULL_POSITION)
  253.         {
  254.             /*
  255.              * We hit EOF/BOF without a match.
  256.              */
  257.             return (n);
  258.         }
  259.  
  260.         /*
  261.          * If we're using line numbers, we might as well
  262.          * remember the information we have now (the position
  263.          * and line number of the current line).
  264.          * Don't do it for every line because it slows down
  265.          * the search.  Remember the line number only if
  266.          * we're "far" from the last place we remembered it.
  267.          */
  268.         if (linenums && abs(pos - oldpos) > 1024)
  269.         {
  270.             add_lnum(linenum, pos);
  271.             oldpos = pos;
  272.         }
  273.  
  274.         if (is_caseless)
  275.         {
  276.             /*
  277.              * If this is a caseless search, convert 
  278.              * uppercase in the input line to lowercase.
  279.              * While we're at it, remove any backspaces
  280.              * along with the preceding char.
  281.              * This allows us to match text which is 
  282.              * underlined or overstruck.
  283.              */
  284.             for (p = q = line;  *p != '\0';  p++, q++)
  285.             {
  286.                 if (*p >= 'A' && *p <= 'Z')
  287.                     /* Convert uppercase to lowercase. */
  288.                     *q = *p + 'a' - 'A';
  289.                 else if (q > line && *p == '\b')
  290.                     /* Delete BS and preceding char. */
  291.                     q -= 2;
  292.                 else
  293.                     /* Otherwise, just copy. */
  294.                     *q = *p;
  295.             }
  296.         }
  297.  
  298.         /*
  299.          * Test the next line to see if we have a match.
  300.          * This is done in a variety of ways, depending
  301.          * on what pattern matching functions are available.
  302.          */
  303. #if REGCMP
  304.         line_match = (regex(cpattern, line) != NULL);
  305. #else
  306. #if RECOMP
  307.         line_match = (re_exec(line) == 1);
  308. #else
  309. #if REGCOMP
  310.         line_match = regexec(regpattern, line);
  311. #else
  312.         line_match = match(pattern, line);
  313. #endif
  314. #endif
  315. #endif
  316.         /*
  317.          * We are successful if want_match and line_match are
  318.          * both true (want a match and got it),
  319.          * or both false (want a non-match and got it).
  320.          */
  321.         if (((want_match && line_match) || (!want_match && !line_match)) &&
  322.               --n <= 0)
  323.             /*
  324.              * Found the line.
  325.              */
  326.             break;
  327.     }
  328.  
  329.     jump_loc(linepos, jump_sline);
  330.     return (0);
  331. }
  332.  
  333. #if (!REGCMP) && (!RECOMP) && (!REGCOMP)
  334. /*
  335.  * We have neither regcmp() nor re_comp().
  336.  * We use this function to do simple pattern matching.
  337.  * It supports no metacharacters like *, etc.
  338.  */
  339.     static int
  340. match(pattern, buf)
  341.     char *pattern, *buf;
  342. {
  343.     register char *pp, *lp;
  344.  
  345.     for ( ;  *buf != '\0';  buf++)
  346.     {
  347.         for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
  348.             if (*pp == '\0' || *lp == '\0')
  349.                 break;
  350.         if (*pp == '\0')
  351.             return (1);
  352.     }
  353.     return (0);
  354. }
  355. #endif
  356.