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

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