home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / less373.zip / search.c < prev    next >
C/C++ Source or Header  |  2002-01-14  |  27KB  |  1,344 lines

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