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

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