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

1.30    ! itojun      1: /*     $NetBSD: histedit.c,v 1.29 2003/05/04 06:36:50 gmcgarry 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.30    ! itojun     44: __RCSID("$NetBSD: histedit.c,v 1.29 2003/05/04 06:36:50 gmcgarry 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.8       christos   63: #include "myhistedit.h"
1.1       jtc        64: #include "error.h"
1.17      cjs        65: #ifndef SMALL
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:
1.27      christos   77: STATIC const char *fc_replace(const char *, char *, char *);
                     78:
                     79: #ifdef DEBUG
                     80: extern FILE *tracefile;
                     81: #endif
1.1       jtc        82:
                     83: /*
                     84:  * Set history and editing status.  Called whenever the status may
                     85:  * have changed (figures out what to do).
                     86:  */
1.5       cgd        87: void
1.27      christos   88: histedit(void)
1.4       cgd        89: {
1.27      christos   90:        FILE *el_err;
1.1       jtc        91:
                     92: #define editing (Eflag || Vflag)
                     93:
                     94:        if (iflag) {
                     95:                if (!hist) {
                     96:                        /*
                     97:                         * turn history on
                     98:                         */
                     99:                        INTOFF;
                    100:                        hist = history_init();
                    101:                        INTON;
                    102:
                    103:                        if (hist != NULL)
1.9       christos  104:                                sethistsize(histsizeval());
1.1       jtc       105:                        else
                    106:                                out2str("sh: can't initialize history\n");
                    107:                }
                    108:                if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
                    109:                        /*
                    110:                         * turn editing on
                    111:                         */
                    112:                        INTOFF;
                    113:                        if (el_in == NULL)
                    114:                                el_in = fdopen(0, "r");
                    115:                        if (el_out == NULL)
                    116:                                el_out = fdopen(2, "w");
                    117:                        if (el_in == NULL || el_out == NULL)
                    118:                                goto bad;
1.27      christos  119:                        el_err = el_out;
                    120: #if DEBUG
                    121:                        if (tracefile)
                    122:                                el_err = tracefile;
                    123: #endif
                    124:                        el = el_init(arg0, el_in, el_out, el_err);
1.1       jtc       125:                        if (el != NULL) {
                    126:                                if (hist)
                    127:                                        el_set(el, EL_HIST, history, hist);
                    128:                                el_set(el, EL_PROMPT, getprompt);
                    129:                        } else {
                    130: bad:
                    131:                                out2str("sh: can't initialize editing\n");
                    132:                        }
                    133:                        INTON;
                    134:                } else if (!editing && el) {
                    135:                        INTOFF;
                    136:                        el_end(el);
                    137:                        el = NULL;
                    138:                        INTON;
                    139:                }
                    140:                if (el) {
                    141:                        if (Vflag)
                    142:                                el_set(el, EL_EDITOR, "vi");
                    143:                        else if (Eflag)
                    144:                                el_set(el, EL_EDITOR, "emacs");
1.24      mycroft   145:                        el_source(el, NULL);
1.1       jtc       146:                }
                    147:        } else {
                    148:                INTOFF;
                    149:                if (el) {       /* no editing if not interactive */
                    150:                        el_end(el);
                    151:                        el = NULL;
                    152:                }
                    153:                if (hist) {
                    154:                        history_end(hist);
                    155:                        hist = NULL;
                    156:                }
                    157:                INTON;
                    158:        }
                    159: }
                    160:
1.4       cgd       161:
                    162: void
1.27      christos  163: sethistsize(const char *hs)
1.8       christos  164: {
1.1       jtc       165:        int histsize;
1.16      christos  166:        HistEvent he;
1.1       jtc       167:
                    168:        if (hist != NULL) {
1.10      christos  169:                if (hs == NULL || *hs == '\0' ||
1.9       christos  170:                   (histsize = atoi(hs)) < 0)
1.1       jtc       171:                        histsize = 100;
1.19      christos  172:                history(hist, &he, H_SETSIZE, histsize);
1.1       jtc       173:        }
1.13      christos  174: }
                    175:
                    176: void
1.27      christos  177: setterm(const char *term)
1.13      christos  178: {
                    179:        if (el != NULL && term != NULL)
                    180:                if (el_set(el, EL_TERMINAL, term) != 0) {
                    181:                        outfmt(out2, "sh: Can't set terminal type %s\n", term);
                    182:                        outfmt(out2, "sh: Using dumb terminal settings.\n");
                    183:                }
1.1       jtc       184: }
                    185:
1.28      gmcgarry  186: int
                    187: inputrc(argc, argv)
                    188:        int argc;
                    189:        char **argv;
                    190: {
                    191:        if (argc != 2) {
                    192:                out2str("usage: inputrc file\n");
                    193:                return 1;
                    194:        }
                    195:        if (el != NULL) {
                    196:                if (el_source(el, argv[1])) {
                    197:                        out2str("inputrc: failed\n");
                    198:                        return 1;
                    199:                } else
                    200:                        return 0;
                    201:        } else {
                    202:                out2str("sh: inputrc ignored, not editing\n");
                    203:                return 1;
                    204:        }
                    205: }
                    206:
1.1       jtc       207: /*
                    208:  *  This command is provided since POSIX decided to standardize
                    209:  *  the Korn shell fc command.  Oh well...
                    210:  */
1.4       cgd       211: int
1.27      christos  212: histcmd(int argc, char **argv)
1.1       jtc       213: {
                    214:        int ch;
1.21      christos  215:        const char *editor = NULL;
1.16      christos  216:        HistEvent he;
1.1       jtc       217:        int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
1.16      christos  218:        int i, retval;
1.21      christos  219:        const char *firststr, *laststr;
1.1       jtc       220:        int first, last, direction;
                    221:        char *pat = NULL, *repl;        /* ksh "fc old=new" crap */
                    222:        static int active = 0;
                    223:        struct jmploc jmploc;
                    224:        struct jmploc *volatile savehandler;
                    225:        char editfile[MAXPATHLEN + 1];
                    226:        FILE *efp;
1.8       christos  227: #ifdef __GNUC__
                    228:        /* Avoid longjmp clobbering */
                    229:        (void) &editor;
                    230:        (void) &lflg;
                    231:        (void) &nflg;
                    232:        (void) &rflg;
                    233:        (void) &sflg;
                    234:        (void) &firststr;
                    235:        (void) &laststr;
                    236:        (void) &pat;
                    237:        (void) &repl;
                    238:        (void) &efp;
                    239:        (void) &argc;
                    240:        (void) &argv;
                    241: #endif
1.1       jtc       242:
                    243:        if (hist == NULL)
                    244:                error("history not active");
1.10      christos  245:
1.1       jtc       246:        if (argc == 1)
                    247:                error("missing history argument");
                    248:
                    249:        optreset = 1; optind = 1; /* initialize getopt */
                    250:        while (not_fcnumber(argv[optind]) &&
1.15      lukem     251:              (ch = getopt(argc, argv, ":e:lnrs")) != -1)
1.1       jtc       252:                switch ((char)ch) {
                    253:                case 'e':
1.25      christos  254:                        editor = optionarg;
1.1       jtc       255:                        break;
                    256:                case 'l':
                    257:                        lflg = 1;
                    258:                        break;
                    259:                case 'n':
                    260:                        nflg = 1;
                    261:                        break;
                    262:                case 'r':
                    263:                        rflg = 1;
                    264:                        break;
                    265:                case 's':
                    266:                        sflg = 1;
                    267:                        break;
                    268:                case ':':
                    269:                        error("option -%c expects argument", optopt);
1.20      mycroft   270:                        /* NOTREACHED */
1.1       jtc       271:                case '?':
                    272:                default:
                    273:                        error("unknown option: -%c", optopt);
1.20      mycroft   274:                        /* NOTREACHED */
1.1       jtc       275:                }
                    276:        argc -= optind, argv += optind;
                    277:
                    278:        /*
                    279:         * If executing...
                    280:         */
                    281:        if (lflg == 0 || editor || sflg) {
                    282:                lflg = 0;       /* ignore */
                    283:                editfile[0] = '\0';
                    284:                /*
                    285:                 * Catch interrupts to reset active counter and
                    286:                 * cleanup temp files.
                    287:                 */
                    288:                if (setjmp(jmploc.loc)) {
                    289:                        active = 0;
                    290:                        if (*editfile)
                    291:                                unlink(editfile);
                    292:                        handler = savehandler;
                    293:                        longjmp(handler->loc, 1);
                    294:                }
                    295:                savehandler = handler;
                    296:                handler = &jmploc;
                    297:                if (++active > MAXHISTLOOPS) {
                    298:                        active = 0;
                    299:                        displayhist = 0;
                    300:                        error("called recursively too many times");
                    301:                }
                    302:                /*
                    303:                 * Set editor.
                    304:                 */
                    305:                if (sflg == 0) {
                    306:                        if (editor == NULL &&
                    307:                            (editor = bltinlookup("FCEDIT", 1)) == NULL &&
                    308:                            (editor = bltinlookup("EDITOR", 1)) == NULL)
                    309:                                editor = DEFEDITOR;
                    310:                        if (editor[0] == '-' && editor[1] == '\0') {
                    311:                                sflg = 1;       /* no edit */
                    312:                                editor = NULL;
                    313:                        }
                    314:                }
                    315:        }
                    316:
                    317:        /*
                    318:         * If executing, parse [old=new] now
                    319:         */
1.10      christos  320:        if (lflg == 0 && argc > 0 &&
1.1       jtc       321:             ((repl = strchr(argv[0], '=')) != NULL)) {
                    322:                pat = argv[0];
                    323:                *repl++ = '\0';
                    324:                argc--, argv++;
                    325:        }
                    326:        /*
                    327:         * determine [first] and [last]
                    328:         */
                    329:        switch (argc) {
                    330:        case 0:
                    331:                firststr = lflg ? "-16" : "-1";
                    332:                laststr = "-1";
                    333:                break;
                    334:        case 1:
                    335:                firststr = argv[0];
                    336:                laststr = lflg ? "-1" : argv[0];
                    337:                break;
                    338:        case 2:
                    339:                firststr = argv[0];
                    340:                laststr = argv[1];
                    341:                break;
                    342:        default:
                    343:                error("too many args");
1.20      mycroft   344:                /* NOTREACHED */
1.1       jtc       345:        }
                    346:        /*
                    347:         * Turn into event numbers.
                    348:         */
                    349:        first = str_to_event(firststr, 0);
                    350:        last = str_to_event(laststr, 1);
                    351:
                    352:        if (rflg) {
                    353:                i = last;
                    354:                last = first;
                    355:                first = i;
                    356:        }
                    357:        /*
                    358:         * XXX - this should not depend on the event numbers
1.10      christos  359:         * always increasing.  Add sequence numbers or offset
1.1       jtc       360:         * to the history element in next (diskbased) release.
                    361:         */
                    362:        direction = first < last ? H_PREV : H_NEXT;
                    363:
                    364:        /*
                    365:         * If editing, grab a temp file.
                    366:         */
                    367:        if (editor) {
                    368:                int fd;
                    369:                INTOFF;         /* easier */
1.30    ! itojun    370:                snprintf(editfile, sizeof(editfile), "%s_shXXXXXX", _PATH_TMP);
1.1       jtc       371:                if ((fd = mkstemp(editfile)) < 0)
                    372:                        error("can't create temporary file %s", editfile);
                    373:                if ((efp = fdopen(fd, "w")) == NULL) {
                    374:                        close(fd);
1.10      christos  375:                        error("can't allocate stdio buffer for temp");
1.1       jtc       376:                }
                    377:        }
                    378:
                    379:        /*
                    380:         * Loop through selected history events.  If listing or executing,
                    381:         * do it now.  Otherwise, put into temp file and call the editor
                    382:         * after.
                    383:         *
                    384:         * The history interface needs rethinking, as the following
                    385:         * convolutions will demonstrate.
                    386:         */
1.16      christos  387:        history(hist, &he, H_FIRST);
                    388:        retval = history(hist, &he, H_NEXT_EVENT, first);
                    389:        for (;retval != -1; retval = history(hist, &he, direction)) {
1.1       jtc       390:                if (lflg) {
                    391:                        if (!nflg)
1.16      christos  392:                                out1fmt("%5d ", he.num);
                    393:                        out1str(he.str);
1.1       jtc       394:                } else {
1.21      christos  395:                        const char *s = pat ?
                    396:                           fc_replace(he.str, pat, repl) : he.str;
1.1       jtc       397:
                    398:                        if (sflg) {
                    399:                                if (displayhist) {
                    400:                                        out2str(s);
                    401:                                }
1.21      christos  402:
1.22      christos  403:                                evalstring(strcpy(stalloc(strlen(s) + 1), s), 0);
1.1       jtc       404:                                if (displayhist && hist) {
                    405:                                        /*
1.10      christos  406:                                         *  XXX what about recursive and
1.1       jtc       407:                                         *  relative histnums.
                    408:                                         */
1.16      christos  409:                                        history(hist, &he, H_ENTER, s);
1.1       jtc       410:                                }
                    411:                        } else
                    412:                                fputs(s, efp);
                    413:                }
                    414:                /*
1.16      christos  415:                 * At end?  (if we were to lose last, we'd sure be
1.1       jtc       416:                 * messed up).
                    417:                 */
1.16      christos  418:                if (he.num == last)
1.1       jtc       419:                        break;
                    420:        }
                    421:        if (editor) {
                    422:                char *editcmd;
                    423:
                    424:                fclose(efp);
                    425:                editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
                    426:                sprintf(editcmd, "%s %s", editor, editfile);
1.22      christos  427:                evalstring(editcmd, 0); /* XXX - should use no JC command */
1.1       jtc       428:                INTON;
                    429:                readcmdfile(editfile);  /* XXX - should read back - quick tst */
                    430:                unlink(editfile);
                    431:        }
1.10      christos  432:
1.1       jtc       433:        if (lflg == 0 && active > 0)
                    434:                --active;
                    435:        if (displayhist)
                    436:                displayhist = 0;
1.8       christos  437:        return 0;
1.1       jtc       438: }
                    439:
1.21      christos  440: STATIC const char *
1.27      christos  441: fc_replace(const char *s, char *p, char *r)
1.1       jtc       442: {
                    443:        char *dest;
                    444:        int plen = strlen(p);
                    445:
                    446:        STARTSTACKSTR(dest);
                    447:        while (*s) {
                    448:                if (*s == *p && strncmp(s, p, plen) == 0) {
                    449:                        while (*r)
                    450:                                STPUTC(*r++, dest);
                    451:                        s += plen;
                    452:                        *p = '\0';      /* so no more matches */
                    453:                } else
                    454:                        STPUTC(*s++, dest);
                    455:        }
                    456:        STACKSTRNUL(dest);
                    457:        dest = grabstackstr(dest);
                    458:
                    459:        return (dest);
                    460: }
                    461:
1.4       cgd       462: int
1.27      christos  463: not_fcnumber(char *s)
1.1       jtc       464: {
1.7       christos  465:        if (s == NULL)
                    466:                return 0;
1.1       jtc       467:         if (*s == '-')
                    468:                 s++;
                    469:        return (!is_number(s));
                    470: }
                    471:
1.4       cgd       472: int
1.27      christos  473: str_to_event(const char *str, int last)
1.1       jtc       474: {
1.16      christos  475:        HistEvent he;
1.21      christos  476:        const char *s = str;
1.1       jtc       477:        int relative = 0;
1.16      christos  478:        int i, retval;
1.1       jtc       479:
1.16      christos  480:        retval = history(hist, &he, H_FIRST);
1.1       jtc       481:        switch (*s) {
                    482:        case '-':
                    483:                relative = 1;
                    484:                /*FALLTHROUGH*/
                    485:        case '+':
                    486:                s++;
                    487:        }
                    488:        if (is_number(s)) {
                    489:                i = atoi(s);
                    490:                if (relative) {
1.16      christos  491:                        while (retval != -1 && i--) {
                    492:                                retval = history(hist, &he, H_NEXT);
1.1       jtc       493:                        }
1.16      christos  494:                        if (retval == -1)
                    495:                                retval = history(hist, &he, H_LAST);
1.1       jtc       496:                } else {
1.16      christos  497:                        retval = history(hist, &he, H_NEXT_EVENT, i);
                    498:                        if (retval == -1) {
1.1       jtc       499:                                /*
                    500:                                 * the notion of first and last is
                    501:                                 * backwards to that of the history package
                    502:                                 */
1.16      christos  503:                                retval = history(hist, &he,
                    504:                                                last ? H_FIRST : H_LAST);
1.1       jtc       505:                        }
                    506:                }
1.16      christos  507:                if (retval == -1)
1.1       jtc       508:                        error("history number %s not found (internal error)",
                    509:                               str);
                    510:        } else {
                    511:                /*
1.10      christos  512:                 * pattern
1.1       jtc       513:                 */
1.16      christos  514:                retval = history(hist, &he, H_PREV_STR, str);
                    515:                if (retval == -1)
1.1       jtc       516:                        error("history pattern not found: %s", str);
                    517:        }
1.16      christos  518:        return (he.num);
1.1       jtc       519: }
1.11      christos  520: #else
                    521: int
1.27      christos  522: histcmd(int argc, char **argv)
1.28      gmcgarry  523: {
                    524:        error("not compiled with history support");
                    525:        /* NOTREACHED */
                    526: }
                    527: int
                    528: inputrc(int argc, char **argv)
1.11      christos  529: {
                    530:        error("not compiled with history support");
1.20      mycroft   531:        /* NOTREACHED */
1.11      christos  532: }
                    533: #endif

CVSweb <webmaster@jp.NetBSD.org>