home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / less2912.zip / search.c < prev    next >
C/C++ Source or Header  |  1995-03-10  |  25KB  |  1,198 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * Routines to search a file for a pattern.
  30.  */
  31.  
  32. #include "less.h"
  33. #include "position.h"
  34.  
  35. #define    MINPOS(a,b)    (((a) < (b)) ? (a) : (b))
  36. #define    MAXPOS(a,b)    (((a) > (b)) ? (a) : (b))
  37.  
  38. #if HAVE_POSIX_REGCOMP
  39. #include <regex.h>
  40. #endif
  41. #if HAVE_RE_COMP
  42. char *re_comp();
  43. int re_exec();
  44. #endif
  45. #if HAVE_REGCMP
  46. char *regcmp();
  47. char *regex();
  48. extern char *__loc1;
  49. #endif
  50. #if HAVE_V8_REGCOMP
  51. #include "regexp.h"
  52. #endif
  53. #if NO_REGEX
  54. static int match();
  55. #endif
  56.  
  57. extern int sigs;
  58. extern int how_search;
  59. extern int caseless;
  60. extern int linenums;
  61. extern int sc_height;
  62. extern int jump_sline;
  63. extern int bs_mode;
  64. #if HILITE_SEARCH
  65. extern int hilite_search;
  66. extern int screen_trashed;
  67. extern int size_linebuf;
  68. static int hide_hilite;
  69. static POSITION prep_startpos;
  70. static POSITION prep_endpos;
  71.  
  72. struct hilite
  73. {
  74.     struct hilite *hl_next;
  75.     POSITION hl_startpos;
  76.     POSITION hl_endpos;
  77. };
  78. static struct hilite hilite_anchor = { NULL };
  79. #define    hl_first    hl_next
  80. #endif
  81.  
  82. /*
  83.  * These are the static variables that represent the "remembered"
  84.  * search pattern.  
  85.  */
  86. #if HAVE_POSIX_REGCOMP
  87. static regex_t *regpattern = NULL;
  88. #endif
  89. #if HAVE_RE_COMP
  90. int re_pattern = 0;
  91. #endif
  92. #if HAVE_REGCMP
  93. static char *cpattern = NULL;
  94. #endif
  95. #if HAVE_V8_REGCOMP
  96. static struct regexp *regpattern = NULL;
  97. #endif
  98. #if NO_REGEX
  99. static char *last_pattern = NULL;
  100. #endif
  101.  
  102. static int is_caseless;
  103. static int is_ucase_pattern;
  104.  
  105. /*
  106.  * Convert text.  Perform one or more of these transformations:
  107.  */
  108. #define    CVT_TO_LC    01    /* Convert upper-case to lower-case */
  109. #define    CVT_BS        02    /* Do backspace processing */
  110.  
  111.     static void
  112. cvt_text(odst, osrc, ops)
  113.     char *odst;
  114.     char *osrc;
  115.     int ops;
  116. {
  117.     register char *dst;
  118.     register char *src;
  119.  
  120.     for (src = osrc, dst = odst;  *src != '\0';  src++, dst++)
  121.     {
  122.         if ((ops & CVT_TO_LC) && isupper(*src))
  123.             /* Convert uppercase to lowercase. */
  124.             *dst = tolower(*src);
  125.         else if ((ops & CVT_BS) && *src == '\b' && dst > odst)
  126.             /* Delete BS and preceding char. */
  127.             dst -= 2;
  128.         else 
  129.             /* Just copy. */
  130.             *dst = *src;
  131.     }
  132.     *dst = '\0';
  133. }
  134.  
  135. /*
  136.  * Are there any uppercase letters in this string?
  137.  */
  138.     static int
  139. is_ucase(s)
  140.     char *s;
  141. {
  142.     register char *p;
  143.  
  144.     for (p = s;  *p != '\0';  p++)
  145.         if (isupper(*p))
  146.             return (1);
  147.     return (0);
  148. }
  149.  
  150. /*
  151.  * Is there a previous (remembered) search pattern?
  152.  */
  153.     static int
  154. prev_pattern()
  155. {
  156. #if HAVE_POSIX_REGCOMP
  157.     return (regpattern != NULL);
  158. #endif
  159. #if HAVE_RE_COMP
  160.     return (re_pattern != 0);
  161. #endif
  162. #if HAVE_REGCMP
  163.     return (cpattern != NULL);
  164. #endif
  165. #if HAVE_V8_REGCOMP
  166.     return (regpattern != NULL);
  167. #endif
  168. #if NO_REGEX
  169.     return (last_pattern != NULL);
  170. #endif
  171. }
  172.  
  173. #if HILITE_SEARCH
  174. /*
  175.  * Repaint the hilites currently displayed on the screen.
  176.  * Repaint each line which contains highlighted text.
  177.  * If on==0, force all hilites off.
  178.  */
  179.     public void
  180. repaint_hilite(on)
  181.     int on;
  182. {
  183.     int slinenum;
  184.     POSITION pos;
  185.     POSITION epos;
  186.     int save_hide_hilite;
  187.     extern int can_goto_line;
  188.  
  189.     save_hide_hilite = hide_hilite;
  190.     if (!on)
  191.     {
  192.         if (hide_hilite)
  193.             return;
  194.         hide_hilite = 1;
  195.     }
  196.  
  197.     if (!can_goto_line)
  198.     {
  199.         repaint();
  200.         hide_hilite = save_hide_hilite;
  201.         return;
  202.     }
  203.  
  204.     for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
  205.     {
  206.         pos = position(slinenum);
  207.         if (pos == NULL_POSITION)
  208.             continue;
  209.         epos = position(slinenum+1);
  210.         /*
  211.          * If any character in the line is highlighted, 
  212.          * repaint the line.
  213.          */
  214.         if (is_hilited(pos, epos, 1))
  215.         {
  216.             (void) forw_line(pos);
  217.             goto_line(slinenum);
  218.             put_line();
  219.         }
  220.     }
  221.     hide_hilite = save_hide_hilite;
  222. }
  223. #endif
  224.  
  225. /*
  226.  * Hide search string highlighting.
  227.  */
  228.     public void
  229. undo_search()
  230. {
  231.     if (!prev_pattern())
  232.     {
  233.         error("No previous regular expression", NULL_PARG);
  234.         return;
  235.     }
  236. #if HILITE_SEARCH
  237.     hide_hilite = !hide_hilite;
  238.     repaint_hilite(1);
  239. #endif
  240. }
  241.  
  242. /*
  243.  * Compile a search pattern, for future use by match_pattern.
  244.  */
  245.     static int
  246. compile_pattern(pattern)
  247.     char *pattern;
  248. {
  249. #if HAVE_POSIX_REGCOMP
  250.     regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t));
  251.     if (regcomp(s, pattern, 0))
  252.     {
  253.         free(s);
  254.         error("Invalid pattern", NULL_PARG);
  255.         return (-1);
  256.     }
  257.     if (regpattern != NULL)
  258.         regfree(regpattern);
  259.     regpattern = s;
  260. #endif
  261. #if HAVE_RE_COMP
  262.     PARG parg;
  263.     if ((parg.p_string = re_comp(pattern)) != NULL)
  264.     {
  265.         error("%s", &parg);
  266.         return (-1);
  267.     }
  268.     re_pattern = 1;
  269. #endif
  270. #if HAVE_REGCMP
  271.     char *s;
  272.     if ((s = regcmp(pattern, 0)) == NULL)
  273.     {
  274.         error("Invalid pattern", NULL_PARG);
  275.         return (-1);
  276.     }
  277.     if (cpattern != NULL)
  278.         free(cpattern);
  279.     cpattern = s;
  280. #endif
  281. #if HAVE_V8_REGCOMP
  282.     struct regexp *s;
  283.     if ((s = regcomp(pattern)) == NULL)
  284.     {
  285.         /*
  286.          * regcomp has already printed error message via regerror().
  287.          */
  288.         return (-1);
  289.     }
  290.     if (regpattern != NULL)
  291.         free(regpattern);
  292.     regpattern = s;
  293. #endif
  294. #if NO_REGEX
  295.     static char lpbuf[100];
  296.     strcpy(lpbuf, pattern);
  297.     last_pattern = lpbuf;
  298. #endif
  299.     return (0);
  300. }
  301.  
  302. /*
  303.  * Forget that we have a compiled pattern.
  304.  */
  305.     static void
  306. uncompile_pattern()
  307. {
  308. #if HAVE_POSIX_REGCOMP
  309.     if (regpattern != NULL)
  310.         regfree(regpattern);
  311.     regpattern = NULL;
  312. #endif
  313. #if HAVE_RE_COMP
  314.     re_pattern = 0;
  315. #endif
  316. #if HAVE_REGCMP
  317.     if (cpattern != NULL)
  318.         free(cpattern);
  319.     cpattern = NULL;
  320. #endif
  321. #if HAVE_V8_REGCOMP
  322.     if (regpattern != NULL)
  323.         free(regpattern);
  324.     regpattern = NULL;
  325. #endif
  326. #if NO_REGEX
  327.     last_pattern = NULL;
  328. #endif
  329. }
  330.  
  331. /*
  332.  * Perform a pattern match with the previously compiled pattern.
  333.  * Set sp and ep to the start and end of the matched string.
  334.  */
  335.     static int
  336. match_pattern(line, sp, ep)
  337.     char *line;
  338.     char **sp;
  339.     char **ep;
  340. {
  341.     int matched;
  342. #if HAVE_POSIX_REGCOMP
  343.     regmatch_t rm;
  344.     matched = !regexec(regpattern, line, 1, &rm, 0);
  345.     if (!matched)
  346.         return (0);
  347.     *sp = line + rm.rm_so;
  348.     *ep = line + rm.rm_eo;
  349. #endif
  350. #if HAVE_RE_COMP
  351.     matched = (re_exec(line) == 1);
  352.     /*
  353.      * re_exec doesn't seem to provide a way to get the matched string.
  354.      */
  355.     *sp = *ep = NULL;
  356. #endif
  357. #if HAVE_REGCMP
  358.     *ep = regex(cpattern, line);
  359.     matched = (*ep != NULL);
  360.     if (!matched)
  361.         return (0);
  362.     *sp = __loc1;
  363. #endif
  364. #if HAVE_V8_REGCOMP
  365.     matched = regexec(regpattern, line);
  366.     if (!matched)
  367.         return (0);
  368.     *sp = regpattern->startp[0];
  369.     *ep = regpattern->endp[0];
  370. #endif
  371. #if NO_REGEX
  372.     matched = match(last_pattern, line, sp, ep);
  373. #endif
  374.     return (matched);
  375. }
  376.  
  377. #if HILITE_SEARCH
  378. /*
  379.  * Clear the hilite list.
  380.  */
  381.     public void
  382. clr_hilite()
  383. {
  384.     struct hilite *hl;
  385.     struct hilite *nexthl;
  386.  
  387.     for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = nexthl)
  388.     {
  389.         nexthl = hl->hl_next;
  390.         free((void*)hl);
  391.     }
  392.     hilite_anchor.hl_first = NULL;
  393.     prep_startpos = prep_endpos = NULL_POSITION;
  394. }
  395.  
  396. /*
  397.  * Should any characters in a specified range be highlighted?
  398.  * If nohide is nonzero, don't consider hide_hilite.
  399.  */
  400.     public int
  401. is_hilited(pos, epos, nohide)
  402.     POSITION pos;
  403.     POSITION epos;
  404.     int nohide;
  405. {
  406.     struct hilite *hl;
  407.  
  408.     if (hilite_search == 0)
  409.         /*
  410.          * Not doing highlighting.
  411.          */
  412.         return (0);
  413.  
  414.     if (!nohide && hide_hilite)
  415.         /*
  416.          * Highlighting is hidden.
  417.          */
  418.         return (0);
  419.  
  420.     /*
  421.      * Look at each highlight and see if any part of it falls in the range.
  422.      */
  423.     for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
  424.     {
  425.         if (hl->hl_endpos > pos &&
  426.             (epos == NULL_POSITION || epos > hl->hl_startpos))
  427.             return (1);
  428.     }
  429.     return (0);
  430. }
  431.  
  432. /*
  433.  * Add a new hilite to a hilite list.
  434.  */
  435.     static void
  436. add_hilite(anchor, hl)
  437.     struct hilite *anchor;
  438.     struct hilite *hl;
  439. {
  440.     struct hilite *ihl;
  441.  
  442.     /*
  443.      * Hilites are sorted in the list; find where new one belongs.
  444.      * Insert new one after ihl.
  445.      */
  446.     for (ihl = anchor;  ihl->hl_next != NULL;  ihl = ihl->hl_next)
  447.     {
  448.         if (ihl->hl_next->hl_startpos > hl->hl_startpos)
  449.             break;
  450.     }
  451.  
  452.     /*
  453.      * Truncate hilite so it doesn't overlap any existing ones
  454.      * above and below it.
  455.      */
  456.     if (ihl != anchor)
  457.         hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
  458.     if (ihl->hl_next != NULL)
  459.         hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
  460.     if (hl->hl_startpos >= hl->hl_endpos)
  461.     {
  462.         /*
  463.          * Hilite was truncated out of existence.
  464.          */
  465.         free(hl);
  466.         return;
  467.     }
  468.     hl->hl_next = ihl->hl_next;
  469.     ihl->hl_next = hl;
  470. }
  471.  
  472. /*
  473.  * Adjust hl_startpos & hl_endpos to account for backspace processing.
  474.  */
  475.     static void
  476. adj_hilite(anchor, linepos)
  477.     struct hilite *anchor;
  478.     POSITION linepos;
  479. {
  480.     char *line;
  481.     struct hilite *hl;
  482.     int checkstart;
  483.     POSITION opos;
  484.     POSITION npos;
  485.  
  486.     /*
  487.      * The line was already scanned and hilites were added (in hilite_line).
  488.      * But it was assumed that each char position in the line 
  489.      * correponds to one char position in the file.
  490.      * This may not be true if there are backspaces in the line.
  491.      * Get the raw line again.  Look at each character.
  492.      */
  493.     (void) forw_raw_line(linepos, &line);
  494.     opos = npos = linepos;
  495.     hl = anchor->hl_first;
  496.     checkstart = TRUE;
  497.     while (hl != NULL)
  498.     {
  499.         /*
  500.          * See if we need to adjust the current hl_startpos or 
  501.          * hl_endpos.  After adjusting startpos[i], move to endpos[i].
  502.          * After adjusting endpos[i], move to startpos[i+1].
  503.          * The hilite list must be sorted thus: 
  504.          * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc.
  505.          */
  506.         if (checkstart && hl->hl_startpos == opos)
  507.         {
  508.             hl->hl_startpos = npos;
  509.             checkstart = FALSE;
  510.             continue; /* {{ not really necessary }} */
  511.         } else if (!checkstart && hl->hl_endpos == opos)
  512.         {
  513.             hl->hl_endpos = npos;
  514.             checkstart = TRUE;
  515.             hl = hl->hl_next;
  516.             continue; /* {{ necessary }} */
  517.         }
  518.         if (*line == '\0')
  519.             break;
  520.         opos++;
  521.         npos++;
  522.         line++;
  523.         while (line[0] == '\b' && line[1] != '\0')
  524.         {
  525.             /*
  526.              * Found a backspace.  The file position moves
  527.              * forward by 2 relative to the processed line
  528.              * which was searched in hilite_line.
  529.              */
  530.             npos += 2;
  531.             line += 2;
  532.         }
  533.     }
  534. }
  535.  
  536. /*
  537.  * Make a hilite for each string in a physical line which matches 
  538.  * the current pattern.
  539.  * sp,ep delimit the first match already found.
  540.  */
  541.     static void
  542. hilite_line(linepos, line, sp, ep)
  543.     POSITION linepos;
  544.     char *line;
  545.     char *sp;
  546.     char *ep;
  547. {
  548.     char *searchp;
  549.     struct hilite *hl;
  550.     struct hilite hilites;
  551.  
  552.     if (sp == NULL || ep == NULL)
  553.         return;
  554.     /*
  555.      * sp and ep delimit the first match in the line.
  556.      * Mark the corresponding file positions, then
  557.      * look for further matches and mark them.
  558.      * {{ This technique, of calling match_pattern on subsequent
  559.      *    substrings of the line, may mark more than is correct
  560.      *    if, for example, the pattern starts with "^". }}
  561.      */
  562.     searchp = line;
  563.     /*
  564.      * Put the hilites into a temporary list until they're adjusted.
  565.      */
  566.     hilites.hl_first = NULL;
  567.     do {
  568.         if (ep > sp)
  569.         {
  570.             /*
  571.              * Assume that each char position in the "line"
  572.              * buffer corresponds to one char position in the file.
  573.              * This is not quite true; we need to adjust later.
  574.              */
  575.             hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
  576.             hl->hl_startpos = linepos + (sp-line);
  577.             hl->hl_endpos = linepos + (ep-line);
  578.             add_hilite(&hilites, hl);
  579.         }
  580.         /*
  581.          * If we matched more than zero characters,
  582.          * move to the first char after the string we matched.
  583.          * If we matched zero, just move to the next char.
  584.          */
  585.         if (ep > searchp)
  586.             searchp = ep;
  587.         else if (*searchp != '\0')
  588.             searchp++;
  589.         else /* end of line */
  590.             break;
  591.     } while (match_pattern(searchp, &sp, &ep));
  592.  
  593.     if (bs_mode == BS_SPECIAL) 
  594.     {
  595.         /*
  596.          * If there were backspaces in the original line, they
  597.          * were removed, and hl_startpos/hl_endpos are not correct.
  598.          * {{ This is very ugly. }}
  599.          */
  600.         adj_hilite(&hilites, linepos);
  601.     }
  602.     /*
  603.      * Now put the hilites into the real list.
  604.      */
  605.     while ((hl = hilites.hl_next) != NULL)
  606.     {
  607.         hilites.hl_next = hl->hl_next;
  608.         add_hilite(&hilite_anchor, hl);
  609.     }
  610. }
  611. #endif
  612.  
  613. /*
  614.  * Change the caseless-ness of searches.  
  615.  * Updates the internal search state to reflect a change in the -i flag.
  616.  */
  617.     public void
  618. chg_caseless()
  619. {
  620.     if (!is_ucase_pattern)
  621.         /*
  622.          * Pattern did not have uppercase.
  623.          * Just set the search caselessness to the global caselessness.
  624.          */
  625.         is_caseless = caseless;
  626.     else
  627.         /*
  628.          * Pattern did have uppercase.
  629.          * Discard the pattern; we can't change search caselessness now.
  630.          */
  631.         uncompile_pattern();
  632. }
  633.  
  634. #if HILITE_SEARCH
  635. /*
  636.  * Find matching text which is currently on screen and highlight it.
  637.  */
  638.     static void
  639. hilite_screen()
  640. {
  641.     struct scrpos scrpos;
  642.  
  643.     get_scrpos(&scrpos);
  644.     if (scrpos.pos == NULL_POSITION)
  645.         return;
  646.     prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE));
  647.     repaint_hilite(1);
  648. }
  649.  
  650. /*
  651.  * Change highlighting parameters.
  652.  */
  653.     public void
  654. chg_hilite()
  655. {
  656.     /*
  657.      * Erase any highlights currently on screen.
  658.      */
  659.     clr_hilite();
  660.     hide_hilite = 0;
  661.  
  662.     if (hilite_search == OPT_ONPLUS)
  663.         /*
  664.          * Display highlights.
  665.          */
  666.         hilite_screen();
  667. }
  668. #endif
  669.  
  670. /*
  671.  * Figure out where to start a search.
  672.  */
  673.     static POSITION
  674. search_pos(search_type)
  675.     int search_type;
  676. {
  677.     POSITION pos;
  678.     int linenum;
  679.  
  680.     if (empty_screen())
  681.     {
  682.         /*
  683.          * Start at the beginning (or end) of the file.
  684.          * The empty_screen() case is mainly for 
  685.          * command line initiated searches;
  686.          * for example, "+/xyz" on the command line.
  687.          * Also for multi-file (SRCH_PAST_EOF) searches.
  688.          */
  689.         if (search_type & SRCH_FORW)
  690.         {
  691.             return (ch_zero());
  692.         } else
  693.         {
  694.             pos = ch_length();
  695.             if (pos == NULL_POSITION)
  696.             {
  697.                 (void) ch_end_seek();
  698.                 pos = ch_length();
  699.             }
  700.             return (pos);
  701.         }
  702.     }
  703.     if (how_search)
  704.     {
  705.         /*
  706.          * Search does not include current screen.
  707.          */
  708.         if (search_type & SRCH_FORW)
  709.             linenum = BOTTOM_PLUS_ONE;
  710.         else
  711.             linenum = TOP;
  712.         pos = position(linenum);
  713.     } else
  714.     {
  715.         /*
  716.          * Search includes current screen.
  717.          * It starts at the jump target (if searching backwards),
  718.          * or at the jump target plus one (if forwards).
  719.          */
  720.         linenum = adjsline(jump_sline);
  721.         pos = position(linenum);
  722.         if (search_type & SRCH_FORW)
  723.             pos = forw_raw_line(pos, (char **)NULL);
  724.     }
  725.     return (pos);
  726. }
  727.  
  728. /*
  729.  * Search a subset of the file, specified by start/end position.
  730.  */
  731.     static int
  732. search_range(pos, endpos, search_type, n, plinepos, pendpos)
  733.     POSITION pos;
  734.     POSITION endpos;
  735.     int search_type;
  736.     int n;
  737.     POSITION *plinepos;
  738.     POSITION *pendpos;
  739. {
  740.     char *line;
  741.     int linenum;
  742.     char *sp, *ep;
  743.     int line_match;
  744.     POSITION linepos, oldpos;
  745.  
  746.     linenum = find_linenum(pos);
  747.     oldpos = pos;
  748.     for (;;)
  749.     {
  750.         /*
  751.          * Get lines until we find a matching one or until
  752.          * we hit end-of-file (or beginning-of-file if we're 
  753.          * going backwards), or until we hit the end position.
  754.          */
  755.         if (ABORT_SIGS())
  756.         {
  757.             /*
  758.              * A signal aborts the search.
  759.              */
  760.             return (-1);
  761.         }
  762.  
  763.         if (endpos != NULL_POSITION && pos >= endpos)
  764.         {
  765.             /*
  766.              * Reached end position without a match.
  767.              */
  768.             if (pendpos != NULL)
  769.                 *pendpos = pos;
  770.             return (n);
  771.         }
  772.  
  773.         if (search_type & SRCH_FORW)
  774.         {
  775.             /*
  776.              * Read the next line, and save the 
  777.              * starting position of that line in linepos.
  778.              */
  779.             linepos = pos;
  780.             pos = forw_raw_line(pos, &line);
  781.             if (linenum != 0)
  782.                 linenum++;
  783.         } else
  784.         {
  785.             /*
  786.              * Read the previous line and save the
  787.              * starting position of that line in linepos.
  788.              */
  789.             pos = back_raw_line(pos, &line);
  790.             linepos = pos;
  791.             if (linenum != 0)
  792.                 linenum--;
  793.         }
  794.  
  795.         if (pos == NULL_POSITION)
  796.         {
  797.             /*
  798.              * Reached EOF/BOF without a match.
  799.              */
  800.             if (pendpos != NULL)
  801.                 *pendpos = NULL_POSITION;
  802.             return (n);
  803.         }
  804.  
  805.         /*
  806.          * If we're using line numbers, we might as well
  807.          * remember the information we have now (the position
  808.          * and line number of the current line).
  809.          * Don't do it for every line because it slows down
  810.          * the search.  Remember the line number only if
  811.          * we're "far" from the last place we remembered it.
  812.          */
  813.         if (linenums && abs((int)(pos - oldpos)) > 1024)
  814.         {
  815.             add_lnum(linenum, pos);
  816.             oldpos = pos;
  817.         }
  818.  
  819.         /*
  820.          * If it's a caseless search, convert the line to lowercase.
  821.          * If we're doing backspace processing, delete backspaces.
  822.          */
  823.         if (is_caseless || bs_mode == BS_SPECIAL)
  824.         {
  825.             int ops = 0;
  826.             if (is_caseless) 
  827.                 ops |= CVT_TO_LC;
  828.             if (bs_mode == BS_SPECIAL)
  829.                 ops |= CVT_BS;
  830.             cvt_text(line, line, ops);
  831.         }
  832.  
  833.         /*
  834.          * Test the next line to see if we have a match.
  835.          * We are successful if we either want a match and got one,
  836.          * or if we want a non-match and got one.
  837.          */
  838.         line_match = match_pattern(line, &sp, &ep);
  839.         line_match = (!(search_type & SRCH_NOMATCH) && line_match) ||
  840.                 ((search_type & SRCH_NOMATCH) && !line_match);
  841.         if (!line_match)
  842.             continue;
  843.         /*
  844.          * Got a match.
  845.          */
  846.         if (search_type & SRCH_FIND_ALL)
  847.         {
  848. #if HILITE_SEARCH
  849.             /*
  850.              * We are supposed to find all matches in the range.
  851.              * Just add the matches in this line to the 
  852.              * hilite list and keep searching.
  853.              */
  854.             if (line_match)
  855.                 hilite_line(linepos, line, sp, ep);
  856. #endif
  857.         } else if (--n <= 0)
  858.         {
  859.             /*
  860.              * Found the one match we're looking for.
  861.              * Return it.
  862.              */
  863. #if HILITE_SEARCH
  864.             if (hilite_search == 1)
  865.             {
  866.                 /*
  867.                  * Clear the hilite list and add only
  868.                  * the matches in this one line.
  869.                  */
  870.                 clr_hilite();
  871.                 if (line_match)
  872.                     hilite_line(linepos, line, sp, ep);
  873.             }
  874. #endif
  875.             if (plinepos != NULL)
  876.                 *plinepos = linepos;
  877.             return (0);
  878.         }
  879.     }
  880. }
  881.  
  882. /*
  883.  * Search for the n-th occurrence of a specified pattern, 
  884.  * either forward or backward.
  885.  * Return the number of matches not yet found in this file
  886.  * (that is, n minus the number of matches found).
  887.  * Return -1 if the search should be aborted.
  888.  * Caller may continue the search in another file 
  889.  * if less than n matches are found in this file.
  890.  */
  891.     public int
  892. search(search_type, pattern, n)
  893.     int search_type;
  894.     char *pattern;
  895.     int n;
  896. {
  897.     POSITION pos;
  898.     int ucase;
  899.  
  900.     if (pattern == NULL || *pattern == '\0')
  901.     {
  902.         /*
  903.          * A null pattern means use the previously compiled pattern.
  904.          */
  905.         if (!prev_pattern())
  906.         {
  907.             error("No previous regular expression", NULL_PARG);
  908.             return (-1);
  909.         }
  910. #if HILITE_SEARCH
  911.         if (hilite_search == OPT_ON)
  912.         {
  913.             /*
  914.              * Erase the highlights currently on screen.
  915.              * If the search fails, we'll redisplay them later.
  916.              */
  917.             repaint_hilite(0);
  918.         }
  919.         if (hilite_search == OPT_ONPLUS && hide_hilite)
  920.         {
  921.             /*
  922.              * Highlight any matches currently on screen,
  923.              * before we actually start the search.
  924.              */
  925.             hide_hilite = 0;
  926.             hilite_screen();
  927.         }
  928.         hide_hilite = 0;
  929. #endif
  930.     } else
  931.     {
  932.         /*
  933.          * Compile the pattern.
  934.          */
  935.         ucase = is_ucase(pattern);
  936.         if (caseless == OPT_ONPLUS)
  937.             cvt_text(pattern, pattern, CVT_TO_LC);
  938.         if (compile_pattern(pattern) < 0)
  939.             return (-1);
  940.         /*
  941.          * Ignore case if -I is set OR
  942.          * -i is set AND the pattern is all lowercase.
  943.          */
  944.         is_ucase_pattern = ucase;
  945.         if (is_ucase_pattern && caseless != OPT_ONPLUS)
  946.             is_caseless = 0;
  947.         else
  948.             is_caseless = caseless;
  949. #if HILITE_SEARCH
  950.         if (hilite_search)
  951.         {
  952.             /*
  953.              * Erase the highlights currently on screen.
  954.              * Also permanently delete them from the hilite list.
  955.              */
  956.             repaint_hilite(0);
  957.             hide_hilite = 0;
  958.             clr_hilite();
  959.         }
  960.         if (hilite_search == OPT_ONPLUS)
  961.         {
  962.             /*
  963.              * Highlight any matches currently on screen,
  964.              * before we actually start the search.
  965.              */
  966.             hilite_screen();
  967.         }
  968. #endif
  969.     }
  970.  
  971.     /*
  972.      * Figure out where to start the search.
  973.      */
  974.     pos = search_pos(search_type);
  975.     if (pos == NULL_POSITION)
  976.     {
  977.         /*
  978.          * Can't find anyplace to start searching from.
  979.          */
  980.         if (search_type & SRCH_PAST_EOF)
  981.             return (n);
  982.         error("Nothing to search", NULL_PARG);
  983.         return (-1);
  984.     }
  985.  
  986.     n = search_range(pos, NULL_POSITION, search_type, n, 
  987.             &pos, (POSITION*)NULL);
  988.     if (n != 0)
  989.     {
  990.         /*
  991.          * Search was unsuccessful.
  992.          */
  993. #if HILITE_SEARCH
  994.         if (hilite_search == OPT_ON && n > 0)
  995.             /*
  996.              * Redisplay old hilites.
  997.              */
  998.             repaint_hilite(1);
  999. #endif
  1000.         return (n);
  1001.     }
  1002.  
  1003.     /*
  1004.      * Go to the matching line.
  1005.      */
  1006.     jump_loc(pos, jump_sline);
  1007.  
  1008. #if HILITE_SEARCH
  1009.     if (hilite_search == OPT_ON)
  1010.         /*
  1011.          * Display new hilites in the matching line.
  1012.          */
  1013.         repaint_hilite(1);
  1014. #endif
  1015.     return (0);
  1016. }
  1017.  
  1018. #if HILITE_SEARCH
  1019. /*
  1020.  * Prepare hilites in a given range of the file.
  1021.  *
  1022.  * The pair (prep_startpos,prep_endpos) delimits a contiguous region
  1023.  *  of the file that has been "prepared"; that is, scanned for matches for
  1024.  * the current search pattern, and hilites have been created for such matches.
  1025.  * If prep_startpos == NULL_POSITION, the prep region is empty.
  1026.  * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
  1027.  * prep_hilite asks that the range (spos,epos) be covered by the prep region.
  1028.  */
  1029.     public void
  1030. prep_hilite(spos, epos)
  1031.     POSITION spos;
  1032.     POSITION epos;
  1033. {
  1034.     POSITION nprep_startpos = prep_startpos;
  1035.     POSITION nprep_endpos = prep_endpos;
  1036. /*
  1037.  * Search beyond where we're asked to search, so the prep region covers
  1038.  * more than we need.  Do one big search instead of a bunch of small ones.
  1039.  */
  1040. #define    SEARCH_MORE (3*size_linebuf)
  1041.  
  1042.     if (!prev_pattern())
  1043.         return;
  1044.     /*
  1045.      * Find two ranges:
  1046.      * The range that we need to search (spos,epos); and the range that
  1047.      * the "prep" region will then cover (nprep_startpos,nprep_endpos).
  1048.      */
  1049.  
  1050.     if (prep_startpos == NULL_POSITION ||
  1051.         (epos != NULL_POSITION && epos < prep_startpos) ||
  1052.         (prep_endpos != NULL_POSITION && spos > prep_endpos))
  1053.     {
  1054.         /*
  1055.          * New range is not contiguous with old prep region.
  1056.          * Discard the old prep region and start a new one.
  1057.          */
  1058.         clr_hilite();
  1059.         if (epos != NULL_POSITION)
  1060.             epos += SEARCH_MORE;
  1061.         nprep_startpos = spos;
  1062.         nprep_endpos = epos;
  1063.     } else
  1064.     {
  1065.         /*
  1066.          * New range partially or completely overlaps old prep region.
  1067.          */
  1068.         if (epos == NULL_POSITION)
  1069.         {
  1070.             /*
  1071.              * New range goes to end of file.
  1072.              */
  1073.             nprep_endpos = NULL_POSITION;
  1074.         } else if (epos > prep_endpos)
  1075.         {
  1076.             /*
  1077.              * New range ends after old prep region.
  1078.              * Extend prep region to end at end of new range.
  1079.              */
  1080.             epos += SEARCH_MORE;
  1081.             nprep_endpos = epos;
  1082.         } else /* (epos <= prep_endpos) */
  1083.         {
  1084.             /*
  1085.              * New range ends within old prep region.
  1086.              * Truncate search to end at start of old prep region.
  1087.              */
  1088.             epos = prep_startpos;
  1089.         }
  1090.  
  1091.         if (spos < prep_startpos)
  1092.         {
  1093.             /*
  1094.              * New range starts before old prep region.
  1095.              * Extend old prep region backwards to start at 
  1096.              * start of new range.
  1097.              */
  1098.             if (spos < SEARCH_MORE)
  1099.                 spos = 0;
  1100.             else
  1101.                 spos -= SEARCH_MORE;
  1102.             nprep_startpos = spos;
  1103.         } else /* (spos >= prep_startpos) */
  1104.         {
  1105.             /*
  1106.              * New range starts within or after old prep region.
  1107.              * Trim search to start near end of old prep region
  1108.              * (actually, one linebuf before end of old range).
  1109.              */
  1110.             if (prep_endpos == NULL_POSITION)
  1111.                 return;
  1112.             else if (prep_endpos < size_linebuf)
  1113.                 spos = 0;
  1114.             else 
  1115.                 spos = prep_endpos - size_linebuf;
  1116.         }
  1117.     }
  1118.  
  1119.     if (epos == NULL_POSITION || epos > spos)
  1120.     {
  1121.         if (search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0,
  1122.                 (POSITION*)NULL, &epos) >= 0)
  1123.         {
  1124.             if (epos == NULL_POSITION || epos > nprep_endpos)
  1125.                 nprep_endpos = epos;
  1126.         }
  1127.     }
  1128.     prep_startpos = nprep_startpos;
  1129.     prep_endpos = nprep_endpos;
  1130. }
  1131. #endif
  1132.  
  1133. #if NO_REGEX
  1134. /*
  1135.  * We have no pattern matching function from the library.
  1136.  * We use this function to do simple pattern matching.
  1137.  * It supports no metacharacters like *, etc.
  1138.  */
  1139.     static int
  1140. match(pattern, buf, pfound, pend)
  1141.     char *pattern, *buf;
  1142.     char **pfound, **pend;
  1143. {
  1144.     register char *pp, *lp;
  1145.  
  1146.     for ( ;  *buf != '\0';  buf++)
  1147.     {
  1148.         for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
  1149.             if (*pp == '\0' || *lp == '\0')
  1150.                 break;
  1151.         if (*pp == '\0')
  1152.         {
  1153.             if (pfound != NULL)
  1154.                 *pfound = buf;
  1155.             if (pend != NULL)
  1156.                 *pend = lp;
  1157.             return (1);
  1158.         }
  1159.     }
  1160.     return (0);
  1161. }
  1162. #endif
  1163.  
  1164. #if HAVE_V8_REGCOMP
  1165. /*
  1166.  * This function is called by the V8 regcomp to report 
  1167.  * errors in regular expressions.
  1168.  */
  1169.     void 
  1170. regerror(s) 
  1171.     char *s; 
  1172. {
  1173.     PARG parg;
  1174.  
  1175.     parg.p_string = s;
  1176.     error("%s", &parg);
  1177. }
  1178. #endif
  1179.  
  1180. #if !HAVE_STRCHR
  1181. /*
  1182.  * strchr is used by regexp.c.
  1183.  */
  1184.     char *
  1185. strchr(s, c)
  1186.     char *s;
  1187.     int c;
  1188. {
  1189.     for ( ;  *s != '\0';  s++)
  1190.         if (*s == c)
  1191.             return (s);
  1192.     if (c == '\0')
  1193.         return (s);
  1194.     return (NULL);
  1195. }
  1196. #endif
  1197.  
  1198.