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

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