[BACK]Return to histedit.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / bin / sh

Annotation of src/bin/sh/histedit.c, Revision 1.16

1.16    ! christos    1: /*     $NetBSD: histedit.c,v 1.15 1997/09/14 07:43:56 lukem Exp $      */
1.6       cgd         2:
1.1       jtc         3: /*-
                      4:  * Copyright (c) 1993
                      5:  *     The Regents of the University of California.  All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to Berkeley by
                      8:  * Kenneth Almquist.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  * 3. All advertising materials mentioning features or use of this software
                     19:  *    must display the following acknowledgement:
                     20:  *     This product includes software developed by the University of
                     21:  *     California, Berkeley and its contributors.
                     22:  * 4. Neither the name of the University nor the names of its contributors
                     23:  *    may be used to endorse or promote products derived from this software
                     24:  *    without specific prior written permission.
                     25:  *
                     26:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     27:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     28:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     29:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     30:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     31:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     32:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     33:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     34:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     35:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     36:  * SUCH DAMAGE.
                     37:  */
                     38:
1.14      christos   39: #include <sys/cdefs.h>
1.1       jtc        40: #ifndef lint
1.6       cgd        41: #if 0
1.8       christos   42: static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95";
1.6       cgd        43: #else
1.16    ! christos   44: __RCSID("$NetBSD: histedit.c,v 1.15 1997/09/14 07:43:56 lukem Exp $");
1.6       cgd        45: #endif
1.1       jtc        46: #endif /* not lint */
                     47:
                     48: #include <sys/param.h>
                     49: #include <paths.h>
                     50: #include <stdio.h>
1.2       jtc        51: #include <stdlib.h>
                     52: #include <unistd.h>
1.8       christos   53: /*
                     54:  * Editline and history functions (and glue).
                     55:  */
1.1       jtc        56: #include "shell.h"
                     57: #include "parser.h"
                     58: #include "var.h"
                     59: #include "options.h"
1.8       christos   60: #include "main.h"
1.4       cgd        61: #include "output.h"
1.1       jtc        62: #include "mystring.h"
1.12      christos   63: #ifndef SMALL
1.8       christos   64: #include "myhistedit.h"
1.1       jtc        65: #include "error.h"
1.5       cgd        66: #include "eval.h"
1.1       jtc        67: #include "memalloc.h"
                     68:
                     69: #define MAXHISTLOOPS   4       /* max recursions through fc */
                     70: #define DEFEDITOR      "ed"    /* default editor *should* be $EDITOR */
                     71:
                     72: History *hist; /* history cookie */
                     73: EditLine *el;  /* editline cookie */
                     74: int displayhist;
                     75: static FILE *el_in, *el_out;
                     76:
                     77: STATIC char *fc_replace __P((const char *, char *, char *));
                     78:
                     79: /*
                     80:  * Set history and editing status.  Called whenever the status may
                     81:  * have changed (figures out what to do).
                     82:  */
1.5       cgd        83: void
1.10      christos   84: histedit()
1.4       cgd        85: {
1.1       jtc        86:
                     87: #define editing (Eflag || Vflag)
                     88:
                     89:        if (iflag) {
                     90:                if (!hist) {
                     91:                        /*
                     92:                         * turn history on
                     93:                         */
                     94:                        INTOFF;
                     95:                        hist = history_init();
                     96:                        INTON;
                     97:
                     98:                        if (hist != NULL)
1.9       christos   99:                                sethistsize(histsizeval());
1.1       jtc       100:                        else
                    101:                                out2str("sh: can't initialize history\n");
                    102:                }
                    103:                if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
                    104:                        /*
                    105:                         * turn editing on
                    106:                         */
                    107:                        INTOFF;
                    108:                        if (el_in == NULL)
                    109:                                el_in = fdopen(0, "r");
                    110:                        if (el_out == NULL)
                    111:                                el_out = fdopen(2, "w");
                    112:                        if (el_in == NULL || el_out == NULL)
                    113:                                goto bad;
                    114:                        el = el_init(arg0, el_in, el_out);
                    115:                        if (el != NULL) {
                    116:                                if (hist)
                    117:                                        el_set(el, EL_HIST, history, hist);
                    118:                                el_set(el, EL_PROMPT, getprompt);
                    119:                        } else {
                    120: bad:
                    121:                                out2str("sh: can't initialize editing\n");
                    122:                        }
                    123:                        INTON;
                    124:                } else if (!editing && el) {
                    125:                        INTOFF;
                    126:                        el_end(el);
                    127:                        el = NULL;
                    128:                        INTON;
                    129:                }
                    130:                if (el) {
                    131:                        if (Vflag)
                    132:                                el_set(el, EL_EDITOR, "vi");
                    133:                        else if (Eflag)
                    134:                                el_set(el, EL_EDITOR, "emacs");
                    135:                }
                    136:        } else {
                    137:                INTOFF;
                    138:                if (el) {       /* no editing if not interactive */
                    139:                        el_end(el);
                    140:                        el = NULL;
                    141:                }
                    142:                if (hist) {
                    143:                        history_end(hist);
                    144:                        hist = NULL;
                    145:                }
                    146:                INTON;
                    147:        }
                    148: }
                    149:
1.4       cgd       150:
                    151: void
1.9       christos  152: sethistsize(hs)
                    153:        const char *hs;
1.8       christos  154: {
1.1       jtc       155:        int histsize;
1.16    ! christos  156:        HistEvent he;
1.1       jtc       157:
                    158:        if (hist != NULL) {
1.10      christos  159:                if (hs == NULL || *hs == '\0' ||
1.9       christos  160:                   (histsize = atoi(hs)) < 0)
1.1       jtc       161:                        histsize = 100;
1.16    ! christos  162:                history(hist, &he, H_SETMAXSIZE, histsize);
1.1       jtc       163:        }
1.13      christos  164: }
                    165:
                    166: void
                    167: setterm(term)
                    168:        const char *term;
                    169: {
                    170:        if (el != NULL && term != NULL)
                    171:                if (el_set(el, EL_TERMINAL, term) != 0) {
                    172:                        outfmt(out2, "sh: Can't set terminal type %s\n", term);
                    173:                        outfmt(out2, "sh: Using dumb terminal settings.\n");
                    174:                }
1.1       jtc       175: }
                    176:
                    177: /*
                    178:  *  This command is provided since POSIX decided to standardize
                    179:  *  the Korn shell fc command.  Oh well...
                    180:  */
1.4       cgd       181: int
1.1       jtc       182: histcmd(argc, argv)
1.4       cgd       183:        int argc;
1.8       christos  184:        char **argv;
1.1       jtc       185: {
                    186:        extern char *optarg;
                    187:        extern int optind, optopt, optreset;
                    188:        int ch;
                    189:        char *editor = NULL;
1.16    ! christos  190:        HistEvent he;
1.1       jtc       191:        int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
1.16    ! christos  192:        int i, retval;
1.1       jtc       193:        char *firststr, *laststr;
                    194:        int first, last, direction;
                    195:        char *pat = NULL, *repl;        /* ksh "fc old=new" crap */
                    196:        static int active = 0;
                    197:        struct jmploc jmploc;
                    198:        struct jmploc *volatile savehandler;
                    199:        char editfile[MAXPATHLEN + 1];
                    200:        FILE *efp;
1.8       christos  201: #ifdef __GNUC__
                    202:        /* Avoid longjmp clobbering */
                    203:        (void) &editor;
                    204:        (void) &lflg;
                    205:        (void) &nflg;
                    206:        (void) &rflg;
                    207:        (void) &sflg;
                    208:        (void) &firststr;
                    209:        (void) &laststr;
                    210:        (void) &pat;
                    211:        (void) &repl;
                    212:        (void) &efp;
                    213:        (void) &argc;
                    214:        (void) &argv;
                    215: #endif
1.1       jtc       216:
                    217:        if (hist == NULL)
                    218:                error("history not active");
1.10      christos  219:
1.1       jtc       220:        if (argc == 1)
                    221:                error("missing history argument");
                    222:
                    223:        optreset = 1; optind = 1; /* initialize getopt */
                    224:        while (not_fcnumber(argv[optind]) &&
1.15      lukem     225:              (ch = getopt(argc, argv, ":e:lnrs")) != -1)
1.1       jtc       226:                switch ((char)ch) {
                    227:                case 'e':
                    228:                        editor = optarg;
                    229:                        break;
                    230:                case 'l':
                    231:                        lflg = 1;
                    232:                        break;
                    233:                case 'n':
                    234:                        nflg = 1;
                    235:                        break;
                    236:                case 'r':
                    237:                        rflg = 1;
                    238:                        break;
                    239:                case 's':
                    240:                        sflg = 1;
                    241:                        break;
                    242:                case ':':
                    243:                        error("option -%c expects argument", optopt);
                    244:                case '?':
                    245:                default:
                    246:                        error("unknown option: -%c", optopt);
                    247:                }
                    248:        argc -= optind, argv += optind;
                    249:
                    250:        /*
                    251:         * If executing...
                    252:         */
                    253:        if (lflg == 0 || editor || sflg) {
                    254:                lflg = 0;       /* ignore */
                    255:                editfile[0] = '\0';
                    256:                /*
                    257:                 * Catch interrupts to reset active counter and
                    258:                 * cleanup temp files.
                    259:                 */
                    260:                if (setjmp(jmploc.loc)) {
                    261:                        active = 0;
                    262:                        if (*editfile)
                    263:                                unlink(editfile);
                    264:                        handler = savehandler;
                    265:                        longjmp(handler->loc, 1);
                    266:                }
                    267:                savehandler = handler;
                    268:                handler = &jmploc;
                    269:                if (++active > MAXHISTLOOPS) {
                    270:                        active = 0;
                    271:                        displayhist = 0;
                    272:                        error("called recursively too many times");
                    273:                }
                    274:                /*
                    275:                 * Set editor.
                    276:                 */
                    277:                if (sflg == 0) {
                    278:                        if (editor == NULL &&
                    279:                            (editor = bltinlookup("FCEDIT", 1)) == NULL &&
                    280:                            (editor = bltinlookup("EDITOR", 1)) == NULL)
                    281:                                editor = DEFEDITOR;
                    282:                        if (editor[0] == '-' && editor[1] == '\0') {
                    283:                                sflg = 1;       /* no edit */
                    284:                                editor = NULL;
                    285:                        }
                    286:                }
                    287:        }
                    288:
                    289:        /*
                    290:         * If executing, parse [old=new] now
                    291:         */
1.10      christos  292:        if (lflg == 0 && argc > 0 &&
1.1       jtc       293:             ((repl = strchr(argv[0], '=')) != NULL)) {
                    294:                pat = argv[0];
                    295:                *repl++ = '\0';
                    296:                argc--, argv++;
                    297:        }
                    298:        /*
                    299:         * determine [first] and [last]
                    300:         */
                    301:        switch (argc) {
                    302:        case 0:
                    303:                firststr = lflg ? "-16" : "-1";
                    304:                laststr = "-1";
                    305:                break;
                    306:        case 1:
                    307:                firststr = argv[0];
                    308:                laststr = lflg ? "-1" : argv[0];
                    309:                break;
                    310:        case 2:
                    311:                firststr = argv[0];
                    312:                laststr = argv[1];
                    313:                break;
                    314:        default:
                    315:                error("too many args");
                    316:        }
                    317:        /*
                    318:         * Turn into event numbers.
                    319:         */
                    320:        first = str_to_event(firststr, 0);
                    321:        last = str_to_event(laststr, 1);
                    322:
                    323:        if (rflg) {
                    324:                i = last;
                    325:                last = first;
                    326:                first = i;
                    327:        }
                    328:        /*
                    329:         * XXX - this should not depend on the event numbers
1.10      christos  330:         * always increasing.  Add sequence numbers or offset
1.1       jtc       331:         * to the history element in next (diskbased) release.
                    332:         */
                    333:        direction = first < last ? H_PREV : H_NEXT;
                    334:
                    335:        /*
                    336:         * If editing, grab a temp file.
                    337:         */
                    338:        if (editor) {
                    339:                int fd;
                    340:                INTOFF;         /* easier */
                    341:                sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP);
                    342:                if ((fd = mkstemp(editfile)) < 0)
                    343:                        error("can't create temporary file %s", editfile);
                    344:                if ((efp = fdopen(fd, "w")) == NULL) {
                    345:                        close(fd);
1.10      christos  346:                        error("can't allocate stdio buffer for temp");
1.1       jtc       347:                }
                    348:        }
                    349:
                    350:        /*
                    351:         * Loop through selected history events.  If listing or executing,
                    352:         * do it now.  Otherwise, put into temp file and call the editor
                    353:         * after.
                    354:         *
                    355:         * The history interface needs rethinking, as the following
                    356:         * convolutions will demonstrate.
                    357:         */
1.16    ! christos  358:        history(hist, &he, H_FIRST);
        !           359:        retval = history(hist, &he, H_NEXT_EVENT, first);
        !           360:        for (;retval != -1; retval = history(hist, &he, direction)) {
1.1       jtc       361:                if (lflg) {
                    362:                        if (!nflg)
1.16    ! christos  363:                                out1fmt("%5d ", he.num);
        !           364:                        out1str(he.str);
1.1       jtc       365:                } else {
1.10      christos  366:                        char *s = pat ?
1.16    ! christos  367:                           fc_replace(he.str, pat, repl) : (char *)he.str;
1.1       jtc       368:
                    369:                        if (sflg) {
                    370:                                if (displayhist) {
                    371:                                        out2str(s);
                    372:                                }
                    373:                                evalstring(s);
                    374:                                if (displayhist && hist) {
                    375:                                        /*
1.10      christos  376:                                         *  XXX what about recursive and
1.1       jtc       377:                                         *  relative histnums.
                    378:                                         */
1.16    ! christos  379:                                        history(hist, &he, H_ENTER, s);
1.1       jtc       380:                                }
                    381:                        } else
                    382:                                fputs(s, efp);
                    383:                }
                    384:                /*
1.16    ! christos  385:                 * At end?  (if we were to lose last, we'd sure be
1.1       jtc       386:                 * messed up).
                    387:                 */
1.16    ! christos  388:                if (he.num == last)
1.1       jtc       389:                        break;
                    390:        }
                    391:        if (editor) {
                    392:                char *editcmd;
                    393:
                    394:                fclose(efp);
                    395:                editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
                    396:                sprintf(editcmd, "%s %s", editor, editfile);
                    397:                evalstring(editcmd);    /* XXX - should use no JC command */
                    398:                INTON;
                    399:                readcmdfile(editfile);  /* XXX - should read back - quick tst */
                    400:                unlink(editfile);
                    401:        }
1.10      christos  402:
1.1       jtc       403:        if (lflg == 0 && active > 0)
                    404:                --active;
                    405:        if (displayhist)
                    406:                displayhist = 0;
1.8       christos  407:        return 0;
1.1       jtc       408: }
                    409:
                    410: STATIC char *
                    411: fc_replace(s, p, r)
                    412:        const char *s;
                    413:        char *p, *r;
                    414: {
                    415:        char *dest;
                    416:        int plen = strlen(p);
                    417:
                    418:        STARTSTACKSTR(dest);
                    419:        while (*s) {
                    420:                if (*s == *p && strncmp(s, p, plen) == 0) {
                    421:                        while (*r)
                    422:                                STPUTC(*r++, dest);
                    423:                        s += plen;
                    424:                        *p = '\0';      /* so no more matches */
                    425:                } else
                    426:                        STPUTC(*s++, dest);
                    427:        }
                    428:        STACKSTRNUL(dest);
                    429:        dest = grabstackstr(dest);
                    430:
                    431:        return (dest);
                    432: }
                    433:
1.4       cgd       434: int
1.1       jtc       435: not_fcnumber(s)
                    436:         char *s;
                    437: {
1.7       christos  438:        if (s == NULL)
                    439:                return 0;
1.1       jtc       440:         if (*s == '-')
                    441:                 s++;
                    442:        return (!is_number(s));
                    443: }
                    444:
1.4       cgd       445: int
1.1       jtc       446: str_to_event(str, last)
                    447:        char *str;
                    448:        int last;
                    449: {
1.16    ! christos  450:        HistEvent he;
1.1       jtc       451:        char *s = str;
                    452:        int relative = 0;
1.16    ! christos  453:        int i, retval;
1.1       jtc       454:
1.16    ! christos  455:        retval = history(hist, &he, H_FIRST);
1.1       jtc       456:        switch (*s) {
                    457:        case '-':
                    458:                relative = 1;
                    459:                /*FALLTHROUGH*/
                    460:        case '+':
                    461:                s++;
                    462:        }
                    463:        if (is_number(s)) {
                    464:                i = atoi(s);
                    465:                if (relative) {
1.16    ! christos  466:                        while (retval != -1 && i--) {
        !           467:                                retval = history(hist, &he, H_NEXT);
1.1       jtc       468:                        }
1.16    ! christos  469:                        if (retval == -1)
        !           470:                                retval = history(hist, &he, H_LAST);
1.1       jtc       471:                } else {
1.16    ! christos  472:                        retval = history(hist, &he, H_NEXT_EVENT, i);
        !           473:                        if (retval == -1) {
1.1       jtc       474:                                /*
                    475:                                 * the notion of first and last is
                    476:                                 * backwards to that of the history package
                    477:                                 */
1.16    ! christos  478:                                retval = history(hist, &he,
        !           479:                                                last ? H_FIRST : H_LAST);
1.1       jtc       480:                        }
                    481:                }
1.16    ! christos  482:                if (retval == -1)
1.1       jtc       483:                        error("history number %s not found (internal error)",
                    484:                               str);
                    485:        } else {
                    486:                /*
1.10      christos  487:                 * pattern
1.1       jtc       488:                 */
1.16    ! christos  489:                retval = history(hist, &he, H_PREV_STR, str);
        !           490:                if (retval == -1)
1.1       jtc       491:                        error("history pattern not found: %s", str);
                    492:        }
1.16    ! christos  493:        return (he.num);
1.1       jtc       494: }
1.11      christos  495: #else
                    496: int
                    497: histcmd(argc, argv)
                    498:        int argc;
                    499:        char **argv;
                    500: {
                    501:        error("not compiled with history support");
                    502: }
                    503: #endif

CVSweb <webmaster@jp.NetBSD.org>