[BACK]Return to search.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Annotation of src/usr.bin/less/search.c, Revision 1.4

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