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

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