home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / less-321-src.tgz / tar.out / fsf / less / search.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  25KB  |  1,220 lines

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