[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.6

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