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

Annotation of src/usr.bin/mg/search.c, Revision 1.40

1.40    ! lum         1: /*     $OpenBSD: search.c,v 1.39 2012/04/12 04:47:59 lum Exp $ */
1.21      kjell       2:
                      3: /* This file is in the public domain. */
1.4       niklas      4:
1.1       deraadt     5: /*
                      6:  *             Search commands.
1.7       mickey      7:  * The functions in this file implement the search commands (both plain and
1.3       millert     8:  * incremental searches are supported) and the query-replace command.
1.1       deraadt     9:  *
1.3       millert    10:  * The plain old search code is part of the original MicroEMACS "distribution".
                     11:  * The incremental search code and the query-replace code is by Rich Ellison.
1.1       deraadt    12:  */
1.3       millert    13:
                     14: #include "def.h"
1.38      kjell      15:
1.18      cloder     16: #include <ctype.h>
1.3       millert    17:
                     18: #include "macro.h"
1.1       deraadt    19:
1.2       millert    20: #define SRCH_BEGIN     (0)     /* Search sub-codes.     */
1.1       deraadt    21: #define SRCH_FORW      (-1)
                     22: #define SRCH_BACK      (-2)
                     23: #define SRCH_NOPR      (-3)
                     24: #define SRCH_ACCM      (-4)
                     25: #define SRCH_MARK      (-5)
                     26:
1.25      deraadt    27: struct srchcom {
1.34      kjell      28:        int              s_code;
1.25      deraadt    29:        struct line     *s_dotp;
1.34      kjell      30:        int              s_doto;
                     31:        int              s_dotline;
1.25      deraadt    32: };
1.3       millert    33:
1.9       millert    34: static int     isearch(int);
                     35: static void    is_cpush(int);
                     36: static void    is_lpush(void);
                     37: static void    is_pop(void);
                     38: static int     is_peek(void);
                     39: static void    is_undo(int *, int *);
                     40: static int     is_find(int);
                     41: static void    is_prompt(int, int, int);
                     42: static void    is_dspl(char *, int);
1.13      jason      43: static int     eq(int, int, int);
1.3       millert    44:
1.25      deraadt    45: static struct srchcom  cmds[NSRCH];
1.3       millert    46: static int     cip;
                     47:
                     48: int            srch_lastdir = SRCH_NOPR;       /* Last search flags.    */
                     49:
                     50: /*
                     51:  * Search forward.  Get a search string from the user, and search for it
1.7       mickey     52:  * starting at ".".  If found, "." gets moved to just after the matched
                     53:  * characters, and display does all the hard stuff.  If not found, it just
1.3       millert    54:  * prints a message.
1.1       deraadt    55:  */
1.2       millert    56: /* ARGSUSED */
1.3       millert    57: int
1.10      cloder     58: forwsearch(int f, int n)
1.1       deraadt    59: {
1.3       millert    60:        int     s;
1.1       deraadt    61:
1.2       millert    62:        if ((s = readpattern("Search")) != TRUE)
1.12      db         63:                return (s);
1.1       deraadt    64:        if (forwsrch() == FALSE) {
                     65:                ewprintf("Search failed: \"%s\"", pat);
1.12      db         66:                return (FALSE);
1.1       deraadt    67:        }
                     68:        srch_lastdir = SRCH_FORW;
1.12      db         69:        return (TRUE);
1.1       deraadt    70: }
                     71:
                     72: /*
1.7       mickey     73:  * Reverse search.  Get a search string from the user, and search, starting
                     74:  * at "." and proceeding toward the front of the buffer.  If found "." is
                     75:  * left pointing at the first character of the pattern [the last character
1.3       millert    76:  * that was matched].
1.1       deraadt    77:  */
1.2       millert    78: /* ARGSUSED */
1.3       millert    79: int
1.10      cloder     80: backsearch(int f, int n)
1.1       deraadt    81: {
1.3       millert    82:        int     s;
1.1       deraadt    83:
1.2       millert    84:        if ((s = readpattern("Search backward")) != TRUE)
1.1       deraadt    85:                return (s);
                     86:        if (backsrch() == FALSE) {
                     87:                ewprintf("Search failed: \"%s\"", pat);
1.12      db         88:                return (FALSE);
1.1       deraadt    89:        }
                     90:        srch_lastdir = SRCH_BACK;
1.12      db         91:        return (TRUE);
1.1       deraadt    92: }
                     93:
                     94: /*
1.7       mickey     95:  * Search again, using the same search string and direction as the last
                     96:  * search command. The direction has been saved in "srch_lastdir", so you
1.3       millert    97:  * know which way to go.
1.1       deraadt    98:  */
1.2       millert    99: /* ARGSUSED */
1.3       millert   100: int
1.10      cloder    101: searchagain(int f, int n)
1.1       deraadt   102: {
                    103:        if (srch_lastdir == SRCH_FORW) {
                    104:                if (forwsrch() == FALSE) {
                    105:                        ewprintf("Search failed: \"%s\"", pat);
1.12      db        106:                        return (FALSE);
1.1       deraadt   107:                }
1.12      db        108:                return (TRUE);
1.1       deraadt   109:        }
                    110:        if (srch_lastdir == SRCH_BACK) {
                    111:                if (backsrch() == FALSE) {
                    112:                        ewprintf("Search failed: \"%s\"", pat);
1.12      db        113:                        return (FALSE);
1.1       deraadt   114:                }
1.12      db        115:                return (TRUE);
1.1       deraadt   116:        }
                    117:        ewprintf("No last search");
1.12      db        118:        return (FALSE);
1.1       deraadt   119: }
                    120:
                    121: /*
1.7       mickey    122:  * Use incremental searching, initially in the forward direction.
1.1       deraadt   123:  * isearch ignores any explicit arguments.
                    124:  */
1.2       millert   125: /* ARGSUSED */
1.3       millert   126: int
1.10      cloder    127: forwisearch(int f, int n)
1.1       deraadt   128: {
1.36      kjell     129:        if (macrodef || inmacro)
                    130:                /* We can't isearch in macro. Use search instead */
                    131:                return (forwsearch(f,n));
                    132:        else
                    133:                return (isearch(SRCH_FORW));
1.1       deraadt   134: }
                    135:
                    136: /*
                    137:  * Use incremental searching, initially in the reverse direction.
                    138:  * isearch ignores any explicit arguments.
                    139:  */
1.2       millert   140: /* ARGSUSED */
1.3       millert   141: int
1.10      cloder    142: backisearch(int f, int n)
1.1       deraadt   143: {
1.36      kjell     144:        if (macrodef || inmacro)
                    145:                /* We can't isearch in macro. Use search instead */
                    146:                return (backsearch(f,n));
                    147:        else
                    148:                return (isearch(SRCH_BACK));
1.1       deraadt   149: }
                    150:
                    151: /*
                    152:  * Incremental Search.
                    153:  *     dir is used as the initial direction to search.
                    154:  *     ^S      switch direction to forward
                    155:  *     ^R      switch direction to reverse
                    156:  *     ^Q      quote next character (allows searching for ^N etc.)
                    157:  *     <ESC>   exit from Isearch
                    158:  *     <DEL>   undoes last character typed. (tricky job to do this correctly).
                    159:  *     other ^ exit search, don't set mark
                    160:  *     else    accumulate into search string
                    161:  */
1.3       millert   162: static int
1.10      cloder    163: isearch(int dir)
1.2       millert   164: {
1.35      kjell     165:        struct line     *clp;           /* Saved line pointer */
                    166:        int              c;
                    167:        int              cbo;           /* Saved offset */
                    168:        int              success;
                    169:        int              pptr;
                    170:        int              firstc;
                    171:        int              xcase;
                    172:        int              i;
                    173:        char             opat[NPAT];
                    174:        int              cdotline;      /* Saved line number */
1.1       deraadt   175:
1.2       millert   176:        if (macrodef) {
                    177:                ewprintf("Can't isearch in macro");
1.12      db        178:                return (FALSE);
1.1       deraadt   179:        }
1.2       millert   180:        for (cip = 0; cip < NSRCH; cip++)
1.1       deraadt   181:                cmds[cip].s_code = SRCH_NOPR;
1.3       millert   182:
1.12      db        183:        (void)strlcpy(opat, pat, sizeof(opat));
1.1       deraadt   184:        cip = 0;
                    185:        pptr = -1;
                    186:        clp = curwp->w_dotp;
                    187:        cbo = curwp->w_doto;
1.30      kjell     188:        cdotline = curwp->w_dotline;
1.1       deraadt   189:        is_lpush();
                    190:        is_cpush(SRCH_BEGIN);
                    191:        success = TRUE;
                    192:        is_prompt(dir, TRUE, success);
1.3       millert   193:
1.1       deraadt   194:        for (;;) {
                    195:                update();
1.3       millert   196:
1.1       deraadt   197:                switch (c = getkey(FALSE)) {
                    198:                case CCHR('['):
1.5       art       199:                        /*
                    200:                         * If new characters come in the next 300 msec,
                    201:                         * we can assume that they belong to a longer
                    202:                         * escaped sequence so we should ungetkey the
                    203:                         * ESC to avoid writing out garbage.
                    204:                         */
                    205:                        if (ttwait(300) == FALSE)
                    206:                                ungetkey(c);
1.1       deraadt   207:                        srch_lastdir = dir;
                    208:                        curwp->w_markp = clp;
                    209:                        curwp->w_marko = cbo;
1.34      kjell     210:                        curwp->w_markline = cdotline;
1.1       deraadt   211:                        ewprintf("Mark set");
                    212:                        return (TRUE);
                    213:                case CCHR('G'):
                    214:                        if (success != TRUE) {
                    215:                                while (is_peek() == SRCH_ACCM)
                    216:                                        is_undo(&pptr, &dir);
                    217:                                success = TRUE;
                    218:                                is_prompt(dir, pptr < 0, success);
                    219:                                break;
                    220:                        }
                    221:                        curwp->w_dotp = clp;
                    222:                        curwp->w_doto = cbo;
1.30      kjell     223:                        curwp->w_dotline = cdotline;
1.37      kjell     224:                        curwp->w_rflag |= WFMOVE;
1.1       deraadt   225:                        srch_lastdir = dir;
1.6       art       226:                        (void)ctrlg(FFRAND, 0);
1.12      db        227:                        (void)strlcpy(pat, opat, sizeof(pat));
                    228:                        return (ABORT);
1.1       deraadt   229:                case CCHR('S'):
                    230:                        if (dir == SRCH_BACK) {
                    231:                                dir = SRCH_FORW;
                    232:                                is_lpush();
                    233:                                is_cpush(SRCH_FORW);
                    234:                                success = TRUE;
                    235:                        }
1.14      cloder    236:                        if (success == FALSE && dir == SRCH_FORW) {
                    237:                                /* wrap the search to beginning */
1.35      kjell     238:                                curwp->w_dotp = bfirstlp(curbp);
1.14      cloder    239:                                curwp->w_doto = 0;
1.30      kjell     240:                                curwp->w_dotline = 1;
1.14      cloder    241:                                if (is_find(dir) != FALSE) {
                    242:                                        is_cpush(SRCH_MARK);
                    243:                                        success = TRUE;
                    244:                                }
1.35      kjell     245:                                ewprintf("Overwrapped I-search: %s", pat);
1.1       deraadt   246:                                break;
1.14      cloder    247:                        }
1.1       deraadt   248:                        is_lpush();
                    249:                        pptr = strlen(pat);
1.40    ! lum       250:                        if (forwchar(FFRAND, 1) == FALSE) {
        !           251:                                 ttbeep();
        !           252:                                 success = FALSE;
        !           253:                                 ewprintf("Failed I-search: %s", pat);
        !           254:                        } else {
        !           255:                                if (is_find(SRCH_FORW) != FALSE)
        !           256:                                        is_cpush(SRCH_MARK);
        !           257:                                else {
        !           258:                                        (void)backchar(FFRAND, 1);
        !           259:                                        ttbeep();
        !           260:                                        success = FALSE;
        !           261:                                        ewprintf("Failed I-search: %s", pat);
        !           262:                                }
1.1       deraadt   263:                        }
                    264:                        is_prompt(dir, pptr < 0, success);
                    265:                        break;
                    266:                case CCHR('R'):
                    267:                        if (dir == SRCH_FORW) {
                    268:                                dir = SRCH_BACK;
                    269:                                is_lpush();
                    270:                                is_cpush(SRCH_BACK);
                    271:                                success = TRUE;
                    272:                        }
1.14      cloder    273:                        if (success == FALSE && dir == SRCH_BACK) {
                    274:                                /* wrap the search to end */
1.35      kjell     275:                                curwp->w_dotp = blastlp(curbp);
                    276:                                curwp->w_doto = llength(curwp->w_dotp);
1.30      kjell     277:                                curwp->w_dotline = curwp->w_bufp->b_lines;
1.14      cloder    278:                                if (is_find(dir) != FALSE) {
                    279:                                        is_cpush(SRCH_MARK);
                    280:                                        success = TRUE;
                    281:                                }
1.35      kjell     282:                                ewprintf("Overwrapped I-search: %s", pat);
1.1       deraadt   283:                                break;
1.14      cloder    284:                        }
1.1       deraadt   285:                        is_lpush();
                    286:                        pptr = strlen(pat);
1.40    ! lum       287:                         if (backchar(FFRAND, 1) == FALSE) {
        !           288:                                 ttbeep();
        !           289:                                 success = FALSE;
        !           290:                         } else {
        !           291:                                if (is_find(SRCH_BACK) != FALSE)
        !           292:                                        is_cpush(SRCH_MARK);
        !           293:                                else {
        !           294:                                        (void)forwchar(FFRAND, 1);
        !           295:                                        ttbeep();
        !           296:                                        success = FALSE;
        !           297:                                }
1.18      cloder    298:                        }
                    299:                        is_prompt(dir, pptr < 0, success);
                    300:                        break;
                    301:                case CCHR('W'):
                    302:                        /* add the rest of the current word to the pattern */
                    303:                        clp = curwp->w_dotp;
                    304:                        cbo = curwp->w_doto;
                    305:                        firstc = 1;
1.28      otto      306:                        if (pptr == -1)
                    307:                                pptr = 0;
1.18      cloder    308:                        if (dir == SRCH_BACK) {
                    309:                                /* when isearching backwards, cbo is the start of the pattern */
                    310:                                cbo += pptr;
                    311:                        }
                    312:
                    313:                        /* if the search is case insensitive, add to pattern using lowercase */
                    314:                        xcase = 0;
                    315:                        for (i = 0; pat[i]; i++)
                    316:                                if (ISUPPER(CHARMASK(pat[i])))
                    317:                                        xcase = 1;
                    318:
                    319:                        while (cbo < llength(clp)) {
                    320:                                c = lgetc(clp, cbo++);
1.33      kjell     321:                                if ((!firstc && !isalnum(c)))
1.18      cloder    322:                                        break;
                    323:
1.33      kjell     324:                                if (pptr == NPAT - 1) {
                    325:                                        ttbeep();
                    326:                                        break;
                    327:                                }
1.18      cloder    328:                                firstc = 0;
                    329:                                if (!xcase && ISUPPER(c))
                    330:                                        c = TOLOWER(c);
                    331:
                    332:                                pat[pptr++] = c;
                    333:                                pat[pptr] = '\0';
                    334:                                /* cursor only moves when isearching forwards */
                    335:                                if (dir == SRCH_FORW) {
                    336:                                        curwp->w_doto = cbo;
1.37      kjell     337:                                        curwp->w_rflag |= WFMOVE;
1.18      cloder    338:                                        update();
                    339:                                }
1.1       deraadt   340:                        }
                    341:                        is_prompt(dir, pptr < 0, success);
                    342:                        break;
                    343:                case CCHR('H'):
                    344:                case CCHR('?'):
                    345:                        is_undo(&pptr, &dir);
1.2       millert   346:                        if (is_peek() != SRCH_ACCM)
                    347:                                success = TRUE;
1.1       deraadt   348:                        is_prompt(dir, pptr < 0, success);
                    349:                        break;
                    350:                case CCHR('\\'):
                    351:                case CCHR('Q'):
1.3       millert   352:                        c = (char)getkey(FALSE);
1.2       millert   353:                        goto addchar;
1.1       deraadt   354:                case CCHR('M'):
                    355:                        c = CCHR('J');
1.2       millert   356:                        goto addchar;
1.1       deraadt   357:                default:
                    358:                        if (ISCTRL(c)) {
                    359:                                ungetkey(c);
                    360:                                curwp->w_markp = clp;
                    361:                                curwp->w_marko = cbo;
1.34      kjell     362:                                curwp->w_markline = cdotline;
1.1       deraadt   363:                                ewprintf("Mark set");
1.37      kjell     364:                                curwp->w_rflag |= WFMOVE;
1.12      db        365:                                return (TRUE);
1.27      kjell     366:                        }
                    367:                        /* FALLTHRU */
1.1       deraadt   368:                case CCHR('I'):
                    369:                case CCHR('J'):
1.2       millert   370:        addchar:
1.1       deraadt   371:                        if (pptr == -1)
                    372:                                pptr = 0;
                    373:                        if (pptr == 0)
                    374:                                success = TRUE;
1.33      kjell     375:                        if (pptr == NPAT - 1)
                    376:                                ttbeep();
                    377:                        else {
                    378:                                pat[pptr++] = c;
                    379:                                pat[pptr] = '\0';
1.1       deraadt   380:                        }
                    381:                        is_lpush();
                    382:                        if (success != FALSE) {
                    383:                                if (is_find(dir) != FALSE)
                    384:                                        is_cpush(c);
                    385:                                else {
                    386:                                        success = FALSE;
                    387:                                        ttbeep();
                    388:                                        is_cpush(SRCH_ACCM);
                    389:                                }
                    390:                        } else
                    391:                                is_cpush(SRCH_ACCM);
                    392:                        is_prompt(dir, FALSE, success);
                    393:                }
                    394:        }
1.2       millert   395:        /* NOTREACHED */
1.1       deraadt   396: }
                    397:
1.6       art       398: static void
1.10      cloder    399: is_cpush(int cmd)
1.2       millert   400: {
1.1       deraadt   401:        if (++cip >= NSRCH)
                    402:                cip = 0;
                    403:        cmds[cip].s_code = cmd;
                    404: }
                    405:
1.6       art       406: static void
1.10      cloder    407: is_lpush(void)
1.2       millert   408: {
1.3       millert   409:        int     ctp;
1.1       deraadt   410:
1.2       millert   411:        ctp = cip + 1;
1.1       deraadt   412:        if (ctp >= NSRCH)
                    413:                ctp = 0;
                    414:        cmds[ctp].s_code = SRCH_NOPR;
                    415:        cmds[ctp].s_doto = curwp->w_doto;
                    416:        cmds[ctp].s_dotp = curwp->w_dotp;
1.34      kjell     417:        cmds[ctp].s_dotline = curwp->w_dotline;
1.1       deraadt   418: }
                    419:
1.6       art       420: static void
1.10      cloder    421: is_pop(void)
1.2       millert   422: {
1.1       deraadt   423:        if (cmds[cip].s_code != SRCH_NOPR) {
1.2       millert   424:                curwp->w_doto = cmds[cip].s_doto;
                    425:                curwp->w_dotp = cmds[cip].s_dotp;
1.34      kjell     426:                curwp->w_dotline = cmds[cip].s_dotline;
1.37      kjell     427:                curwp->w_rflag |= WFMOVE;
1.1       deraadt   428:                cmds[cip].s_code = SRCH_NOPR;
                    429:        }
                    430:        if (--cip <= 0)
1.2       millert   431:                cip = NSRCH - 1;
1.1       deraadt   432: }
                    433:
                    434: static int
1.10      cloder    435: is_peek(void)
1.2       millert   436: {
1.12      db        437:        return (cmds[cip].s_code);
1.1       deraadt   438: }
                    439:
                    440: /* this used to always return TRUE (the return value was checked) */
1.6       art       441: static void
1.10      cloder    442: is_undo(int *pptr, int *dir)
1.2       millert   443: {
1.3       millert   444:        int     redo = FALSE;
                    445:
1.1       deraadt   446:        switch (cmds[cip].s_code) {
                    447:        case SRCH_BEGIN:
                    448:        case SRCH_NOPR:
                    449:                *pptr = -1;
1.27      kjell     450:                break;
1.1       deraadt   451:        case SRCH_MARK:
                    452:                break;
                    453:        case SRCH_FORW:
                    454:                *dir = SRCH_BACK;
                    455:                redo = TRUE;
                    456:                break;
                    457:        case SRCH_BACK:
                    458:                *dir = SRCH_FORW;
                    459:                redo = TRUE;
                    460:                break;
                    461:        case SRCH_ACCM:
                    462:        default:
                    463:                *pptr -= 1;
                    464:                if (*pptr < 0)
                    465:                        *pptr = 0;
                    466:                pat[*pptr] = '\0';
                    467:                break;
                    468:        }
                    469:        is_pop();
1.2       millert   470:        if (redo)
                    471:                is_undo(pptr, dir);
1.1       deraadt   472: }
                    473:
                    474: static int
1.10      cloder    475: is_find(int dir)
1.2       millert   476: {
1.34      kjell     477:        int      plen, odoto, odotline;
1.25      deraadt   478:        struct line     *odotp;
1.1       deraadt   479:
                    480:        odoto = curwp->w_doto;
                    481:        odotp = curwp->w_dotp;
1.34      kjell     482:        odotline = curwp->w_dotline;
1.1       deraadt   483:        plen = strlen(pat);
                    484:        if (plen != 0) {
1.2       millert   485:                if (dir == SRCH_FORW) {
1.6       art       486:                        (void)backchar(FFARG | FFRAND, plen);
1.1       deraadt   487:                        if (forwsrch() == FALSE) {
                    488:                                curwp->w_doto = odoto;
                    489:                                curwp->w_dotp = odotp;
1.34      kjell     490:                                curwp->w_dotline = odotline;
1.12      db        491:                                return (FALSE);
1.1       deraadt   492:                        }
1.12      db        493:                        return (TRUE);
1.1       deraadt   494:                }
1.2       millert   495:                if (dir == SRCH_BACK) {
1.6       art       496:                        (void)forwchar(FFARG | FFRAND, plen);
1.1       deraadt   497:                        if (backsrch() == FALSE) {
                    498:                                curwp->w_doto = odoto;
                    499:                                curwp->w_dotp = odotp;
1.34      kjell     500:                                curwp->w_dotline = odotline;
1.12      db        501:                                return (FALSE);
1.1       deraadt   502:                        }
1.12      db        503:                        return (TRUE);
1.1       deraadt   504:                }
                    505:                ewprintf("bad call to is_find");
1.12      db        506:                return (FALSE);
1.1       deraadt   507:        }
1.12      db        508:        return (FALSE);
1.1       deraadt   509: }
                    510:
                    511: /*
1.7       mickey    512:  * If called with "dir" not one of SRCH_FORW or SRCH_BACK, this routine used
                    513:  * to print an error message.  It also used to return TRUE or FALSE, depending
                    514:  * on if it liked the "dir".  However, none of the callers looked at the
1.3       millert   515:  * status, so I just made the checking vanish.
1.1       deraadt   516:  */
1.6       art       517: static void
1.10      cloder    518: is_prompt(int dir, int flag, int success)
1.2       millert   519: {
1.1       deraadt   520:        if (dir == SRCH_FORW) {
                    521:                if (success != FALSE)
                    522:                        is_dspl("I-search", flag);
                    523:                else
                    524:                        is_dspl("Failing I-search", flag);
                    525:        } else if (dir == SRCH_BACK) {
                    526:                if (success != FALSE)
                    527:                        is_dspl("I-search backward", flag);
                    528:                else
                    529:                        is_dspl("Failing I-search backward", flag);
1.2       millert   530:        } else
                    531:                ewprintf("Broken call to is_prompt");
1.1       deraadt   532: }
                    533:
                    534: /*
1.7       mickey    535:  * Prompt writing routine for the incremental search.  The "prompt" is just
1.3       millert   536:  * a string. The "flag" determines whether pat should be printed.
1.1       deraadt   537:  */
1.6       art       538: static void
1.10      cloder    539: is_dspl(char *prompt, int flag)
1.2       millert   540: {
1.1       deraadt   541:        if (flag != FALSE)
                    542:                ewprintf("%s: ", prompt);
                    543:        else
                    544:                ewprintf("%s: %s", prompt, pat);
                    545: }
                    546:
                    547: /*
                    548:  * Query Replace.
                    549:  *     Replace strings selectively.  Does a search and replace operation.
                    550:  */
1.2       millert   551: /* ARGSUSED */
1.3       millert   552: int
1.10      cloder    553: queryrepl(int f, int n)
1.1       deraadt   554: {
1.3       millert   555:        int     s;
                    556:        int     rcnt = 0;               /* replacements made so far     */
                    557:        int     plen;                   /* length of found string       */
1.11      vincent   558:        char    news[NPAT], *rep;       /* replacement string           */
1.1       deraadt   559:
1.2       millert   560:        if (macrodef) {
                    561:                ewprintf("Can't query replace in macro");
1.12      db        562:                return (FALSE);
1.1       deraadt   563:        }
1.3       millert   564:
1.2       millert   565:        if ((s = readpattern("Query replace")) != TRUE)
1.1       deraadt   566:                return (s);
1.20      kjell     567:        if ((rep = eread("Query replace %s with: ", news, NPAT,
                    568:            EFNUL | EFNEW | EFCR, pat)) == NULL)
1.12      db        569:                return (ABORT);
1.11      vincent   570:        else if (rep[0] == '\0')
1.1       deraadt   571:                news[0] = '\0';
                    572:        ewprintf("Query replacing %s with %s:", pat, news);
                    573:        plen = strlen(pat);
                    574:
                    575:        /*
                    576:         * Search forward repeatedly, checking each time whether to insert
                    577:         * or not.  The "!" case makes the check always true, so it gets put
                    578:         * into a tighter loop for efficiency.
                    579:         */
                    580:        while (forwsrch() == TRUE) {
1.2       millert   581: retry:
1.1       deraadt   582:                update();
                    583:                switch (getkey(FALSE)) {
1.23      kjell     584:                case 'y':
1.1       deraadt   585:                case ' ':
1.24      kjell     586:                        if (lreplace((RSIZE)plen, news) == FALSE)
1.1       deraadt   587:                                return (FALSE);
                    588:                        rcnt++;
                    589:                        break;
                    590:                case '.':
1.24      kjell     591:                        if (lreplace((RSIZE)plen, news) == FALSE)
1.1       deraadt   592:                                return (FALSE);
                    593:                        rcnt++;
                    594:                        goto stopsearch;
1.23      kjell     595:                /* ^G, CR or ESC */
1.3       millert   596:                case CCHR('G'):
1.6       art       597:                        (void)ctrlg(FFRAND, 0);
1.27      kjell     598:                        goto stopsearch;
1.1       deraadt   599:                case CCHR('['):
1.23      kjell     600:                case CCHR('M'):
1.1       deraadt   601:                        goto stopsearch;
                    602:                case '!':
                    603:                        do {
1.24      kjell     604:                                if (lreplace((RSIZE)plen, news) == FALSE)
1.1       deraadt   605:                                        return (FALSE);
                    606:                                rcnt++;
                    607:                        } while (forwsrch() == TRUE);
                    608:                        goto stopsearch;
1.23      kjell     609:                case 'n':
1.1       deraadt   610:                case CCHR('H'):
1.3       millert   611:                /* To not replace */
                    612:                case CCHR('?'):
1.1       deraadt   613:                        break;
                    614:                default:
1.23      kjell     615:                        ewprintf("y/n or <SP>/<DEL>: replace/don't, [.] repl-end, [!] repl-rest, <CR>/<ESC> quit");
1.1       deraadt   616:                        goto retry;
                    617:                }
                    618:        }
                    619: stopsearch:
1.37      kjell     620:        curwp->w_rflag |= WFFULL;
1.1       deraadt   621:        update();
1.23      kjell     622:        if (rcnt == 1)
                    623:                ewprintf("Replaced 1 occurrence");
1.1       deraadt   624:        else
1.23      kjell     625:                ewprintf("Replaced %d occurrences", rcnt);
1.17      cloder    626:        return (TRUE);
                    627: }
                    628:
                    629: /*
                    630:  * Replace string globally without individual prompting.
                    631:  */
                    632: /* ARGSUSED */
                    633: int
                    634: replstr(int f, int n)
                    635: {
                    636:        char    news[NPAT];
                    637:        int     s, plen, rcnt = 0;
                    638:        char    *r;
                    639:
                    640:        if ((s = readpattern("Replace string")) != TRUE)
                    641:                return s;
                    642:
1.20      kjell     643:        r = eread("Replace string %s with: ", news, NPAT,
                    644:            EFNUL | EFNEW | EFCR,  pat);
1.17      cloder    645:        if (r == NULL)
                    646:                 return (ABORT);
                    647:
                    648:        plen = strlen(pat);
                    649:        while (forwsrch() == TRUE) {
                    650:                update();
1.24      kjell     651:                if (lreplace((RSIZE)plen, news) == FALSE)
1.17      cloder    652:                        return (FALSE);
                    653:
                    654:                rcnt++;
                    655:        }
                    656:
1.37      kjell     657:        curwp->w_rflag |= WFFULL;
1.17      cloder    658:        update();
                    659:
                    660:        if (rcnt == 1)
1.23      kjell     661:                ewprintf("Replaced 1 occurrence");
1.17      cloder    662:        else
1.23      kjell     663:                ewprintf("Replaced %d occurrences", rcnt);
1.17      cloder    664:
1.12      db        665:        return (TRUE);
1.1       deraadt   666: }
                    667:
                    668: /*
1.7       mickey    669:  * This routine does the real work of a forward search.  The pattern is sitting
1.3       millert   670:  * in the external variable "pat".  If found, dot is updated, the window system
1.7       mickey    671:  * is notified of the change, and TRUE is returned.  If the string isn't found,
1.3       millert   672:  * FALSE is returned.
1.1       deraadt   673:  */
1.3       millert   674: int
1.10      cloder    675: forwsrch(void)
1.2       millert   676: {
1.25      deraadt   677:        struct line     *clp, *tlp;
1.13      jason     678:        int      cbo, tbo, c, i, xcase = 0;
1.3       millert   679:        char    *pp;
1.30      kjell     680:        int      nline;
1.1       deraadt   681:
                    682:        clp = curwp->w_dotp;
                    683:        cbo = curwp->w_doto;
1.30      kjell     684:        nline = curwp->w_dotline;
1.13      jason     685:        for (i = 0; pat[i]; i++)
                    686:                if (ISUPPER(CHARMASK(pat[i])))
                    687:                        xcase = 1;
1.2       millert   688:        for (;;) {
1.1       deraadt   689:                if (cbo == llength(clp)) {
1.31      kjell     690:                        if ((clp = lforw(clp)) == curbp->b_headp)
1.2       millert   691:                                break;
1.30      kjell     692:                        nline++;
1.1       deraadt   693:                        cbo = 0;
                    694:                        c = CCHR('J');
                    695:                } else
                    696:                        c = lgetc(clp, cbo++);
1.13      jason     697:                if (eq(c, pat[0], xcase) != FALSE) {
1.1       deraadt   698:                        tlp = clp;
                    699:                        tbo = cbo;
1.2       millert   700:                        pp = &pat[1];
1.1       deraadt   701:                        while (*pp != 0) {
                    702:                                if (tbo == llength(tlp)) {
                    703:                                        tlp = lforw(tlp);
1.31      kjell     704:                                        if (tlp == curbp->b_headp)
1.1       deraadt   705:                                                goto fail;
                    706:                                        tbo = 0;
                    707:                                        c = CCHR('J');
1.30      kjell     708:                                        if (eq(c, *pp++, xcase) == FALSE)
                    709:                                                goto fail;
                    710:                                        nline++;
                    711:                                } else {
1.1       deraadt   712:                                        c = lgetc(tlp, tbo++);
1.30      kjell     713:                                        if (eq(c, *pp++, xcase) == FALSE)
                    714:                                                goto fail;
                    715:                                }
1.1       deraadt   716:                        }
1.2       millert   717:                        curwp->w_dotp = tlp;
                    718:                        curwp->w_doto = tbo;
1.30      kjell     719:                        curwp->w_dotline = nline;
1.37      kjell     720:                        curwp->w_rflag |= WFMOVE;
1.12      db        721:                        return (TRUE);
1.1       deraadt   722:                }
1.2       millert   723: fail:          ;
1.1       deraadt   724:        }
1.12      db        725:        return (FALSE);
1.1       deraadt   726: }
                    727:
                    728: /*
1.7       mickey    729:  * This routine does the real work of a backward search.  The pattern is
                    730:  * sitting in the external variable "pat".  If found, dot is updated, the
1.3       millert   731:  * window system is notified of the change, and TRUE is returned.  If the
1.1       deraadt   732:  * string isn't found, FALSE is returned.
                    733:  */
1.3       millert   734: int
1.10      cloder    735: backsrch(void)
1.2       millert   736: {
1.25      deraadt   737:        struct line     *clp, *tlp;
1.13      jason     738:        int      cbo, tbo, c, i, xcase = 0;
1.3       millert   739:        char    *epp, *pp;
1.30      kjell     740:        int      nline;
1.1       deraadt   741:
1.2       millert   742:        for (epp = &pat[0]; epp[1] != 0; ++epp);
1.1       deraadt   743:        clp = curwp->w_dotp;
                    744:        cbo = curwp->w_doto;
1.30      kjell     745:        nline = curwp->w_dotline;
1.13      jason     746:        for (i = 0; pat[i]; i++)
                    747:                if (ISUPPER(CHARMASK(pat[i])))
                    748:                        xcase = 1;
1.1       deraadt   749:        for (;;) {
                    750:                if (cbo == 0) {
                    751:                        clp = lback(clp);
1.31      kjell     752:                        if (clp == curbp->b_headp)
1.12      db        753:                                return (FALSE);
1.30      kjell     754:                        nline--;
1.2       millert   755:                        cbo = llength(clp) + 1;
1.1       deraadt   756:                }
                    757:                if (--cbo == llength(clp))
                    758:                        c = CCHR('J');
                    759:                else
1.2       millert   760:                        c = lgetc(clp, cbo);
1.13      jason     761:                if (eq(c, *epp, xcase) != FALSE) {
1.1       deraadt   762:                        tlp = clp;
                    763:                        tbo = cbo;
1.2       millert   764:                        pp = epp;
1.1       deraadt   765:                        while (pp != &pat[0]) {
                    766:                                if (tbo == 0) {
                    767:                                        tlp = lback(tlp);
1.31      kjell     768:                                        if (tlp == curbp->b_headp)
1.1       deraadt   769:                                                goto fail;
1.30      kjell     770:                                        nline--;
1.2       millert   771:                                        tbo = llength(tlp) + 1;
1.1       deraadt   772:                                }
                    773:                                if (--tbo == llength(tlp))
                    774:                                        c = CCHR('J');
                    775:                                else
1.2       millert   776:                                        c = lgetc(tlp, tbo);
1.13      jason     777:                                if (eq(c, *--pp, xcase) == FALSE)
1.1       deraadt   778:                                        goto fail;
                    779:                        }
1.2       millert   780:                        curwp->w_dotp = tlp;
                    781:                        curwp->w_doto = tbo;
1.30      kjell     782:                        curwp->w_dotline = nline;
1.37      kjell     783:                        curwp->w_rflag |= WFMOVE;
1.12      db        784:                        return (TRUE);
1.1       deraadt   785:                }
1.2       millert   786: fail:          ;
1.1       deraadt   787:        }
1.2       millert   788:        /* NOTREACHED */
1.1       deraadt   789: }
                    790:
                    791: /*
1.7       mickey    792:  * Compare two characters.  The "bc" comes from the buffer.  It has its case
1.3       millert   793:  * folded out. The "pc" is from the pattern.
1.1       deraadt   794:  */
                    795: static int
1.13      jason     796: eq(int bc, int pc, int xcase)
1.1       deraadt   797: {
                    798:        bc = CHARMASK(bc);
                    799:        pc = CHARMASK(pc);
1.2       millert   800:        if (bc == pc)
1.12      db        801:                return (TRUE);
1.13      jason     802:        if (xcase)
                    803:                return (FALSE);
1.2       millert   804:        if (ISUPPER(bc))
1.12      db        805:                return (TOLOWER(bc) == pc);
1.2       millert   806:        if (ISUPPER(pc))
1.12      db        807:                return (bc == TOLOWER(pc));
                    808:        return (FALSE);
1.1       deraadt   809: }
                    810:
                    811: /*
1.7       mickey    812:  * Read a pattern.  Stash it in the external variable "pat".  The "pat" is not
                    813:  * updated if the user types in an empty line.  If the user typed an empty
                    814:  * line, and there is no old pattern, it is an error.  Display the old pattern,
                    815:  * in the style of Jeff Lomicka.  There is some do-it-yourself control
1.3       millert   816:  * expansion.
1.1       deraadt   817:  */
1.3       millert   818: int
1.10      cloder    819: readpattern(char *prompt)
1.2       millert   820: {
1.11      vincent   821:        char    tpat[NPAT], *rep;
1.12      db        822:        int     retval;
1.1       deraadt   823:
1.16      cloder    824:        if (pat[0] == '\0')
1.22      kjell     825:                rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, prompt);
1.2       millert   826:        else
1.19      kjell     827:                rep = eread("%s: (default %s) ", tpat, NPAT,
                    828:                    EFNUL | EFNEW | EFCR, prompt, pat);
1.1       deraadt   829:
1.3       millert   830:        /* specified */
1.15      cloder    831:        if (rep == NULL) {
                    832:                retval = ABORT;
1.22      kjell     833:        } else if (rep[0] != '\0') {
                    834:                (void)strlcpy(pat, tpat, sizeof(pat));
1.11      vincent   835:                retval = TRUE;
1.15      cloder    836:        } else if (pat[0] != '\0') {
1.11      vincent   837:                retval = TRUE;
                    838:        } else
                    839:                retval = FALSE;
1.12      db        840:        return (retval);
1.1       deraadt   841: }