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

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