160705Sbostic /*- 2*60706Sbostic * Copyright (c) 1993 3*60706Sbostic * The Regents of the University of California. All rights reserved. 460705Sbostic * 560705Sbostic * This code is derived from software contributed to Berkeley by 660705Sbostic * Kenneth Almquist. 760705Sbostic * 860705Sbostic * %sccs.include.redist.c% 960705Sbostic */ 1060705Sbostic 1160705Sbostic #ifndef lint 12*60706Sbostic static char sccsid[] = "@(#)histedit.c 8.1 (Berkeley) 05/31/93"; 1360705Sbostic #endif /* not lint */ 1460705Sbostic 1553861Smarc /* 1653861Smarc * Editline and history functions (and glue). 1753861Smarc */ 1855242Smarc #include <sys/param.h> 1955242Smarc #include <paths.h> 2053861Smarc #include <stdio.h> 2155242Smarc #include "shell.h" 2253861Smarc #include "parser.h" 2355242Smarc #include "var.h" 2453861Smarc #include "options.h" 2555242Smarc #include "mystring.h" 2653861Smarc #include "error.h" 2753861Smarc #include "histedit.h" 2855242Smarc #include "memalloc.h" 2953861Smarc 3055242Smarc #define MAXHISTLOOPS 4 /* max recursions through fc */ 3155242Smarc #define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ 3255242Smarc 3353861Smarc History *hist; /* history cookie */ 3453861Smarc EditLine *el; /* editline cookie */ 3555242Smarc int displayhist; 3653861Smarc static FILE *el_in, *el_out; 3753861Smarc 3855242Smarc STATIC char *fc_replace __P((const char *, char *, char *)); 3953861Smarc 4053861Smarc /* 4153861Smarc * Set history and editing status. Called whenever the status may 4253861Smarc * have changed (figures out what to do). 4353861Smarc */ 4453861Smarc histedit() { 4553861Smarc 4655242Smarc #define editing (Eflag || Vflag) 4755242Smarc 4855242Smarc if (iflag) { 4953861Smarc if (!hist) { 5053861Smarc /* 5153861Smarc * turn history on 5253861Smarc */ 5353861Smarc INTOFF; 5453861Smarc hist = history_init(); 5553861Smarc INTON; 5653861Smarc 5753861Smarc if (hist != NULL) 5853861Smarc sethistsize(); 5953861Smarc else 6053861Smarc out2str("sh: can't initialize history\n"); 6153861Smarc } 6255242Smarc if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ 6353861Smarc /* 6453861Smarc * turn editing on 6553861Smarc */ 6653861Smarc INTOFF; 6753861Smarc if (el_in == NULL) 6853861Smarc el_in = fdopen(0, "r"); 6953861Smarc if (el_out == NULL) 7053861Smarc el_out = fdopen(2, "w"); 7153861Smarc if (el_in == NULL || el_out == NULL) 7253861Smarc goto bad; 7353861Smarc el = el_init(arg0, el_in, el_out); 7453861Smarc if (el != NULL) { 7553861Smarc if (hist) 7655242Smarc el_set(el, EL_HIST, history, hist); 7753861Smarc el_set(el, EL_PROMPT, getprompt); 7853861Smarc } else { 7953861Smarc bad: 8053861Smarc out2str("sh: can't initialize editing\n"); 8153861Smarc } 8253861Smarc INTON; 8355242Smarc } else if (!editing && el) { 8453861Smarc INTOFF; 8553861Smarc el_end(el); 8653861Smarc el = NULL; 8753861Smarc INTON; 8853861Smarc } 8955242Smarc if (el) { 9055242Smarc if (Vflag) 9155242Smarc el_set(el, EL_EDITOR, "vi"); 9255242Smarc else if (Eflag) 9355242Smarc el_set(el, EL_EDITOR, "emacs"); 9455242Smarc } 9553861Smarc } else { 9653861Smarc INTOFF; 9753861Smarc if (el) { /* no editing if not interactive */ 9853861Smarc el_end(el); 9953861Smarc el = NULL; 10053861Smarc } 10153861Smarc if (hist) { 10253861Smarc history_end(hist); 10353861Smarc hist = NULL; 10453861Smarc } 10553861Smarc INTON; 10653861Smarc } 10753861Smarc } 10853861Smarc 10953861Smarc sethistsize() { 11053861Smarc char *cp; 11153861Smarc int histsize; 11253861Smarc 11353861Smarc if (hist != NULL) { 11453861Smarc cp = lookupvar("HISTSIZE"); 11553861Smarc if (cp == NULL || *cp == '\0' || 11653861Smarc (histsize = atoi(cp)) < 0) 11753861Smarc histsize = 100; 11855242Smarc history(hist, H_EVENT, histsize); 11953861Smarc } 12053861Smarc } 12155242Smarc 12255242Smarc /* 12355242Smarc * This command is provided since POSIX decided to standardize 12455242Smarc * the Korn shell fc command. Oh well... 12555242Smarc */ 12655242Smarc histcmd(argc, argv) 12755242Smarc char *argv[]; 12855242Smarc { 12955242Smarc extern char *optarg; 13055242Smarc extern int optind, optopt, optreset; 13155242Smarc int ch; 13255242Smarc char *editor = NULL; 13355242Smarc const HistEvent *he; 13455242Smarc int lflg = 0, nflg = 0, rflg = 0, sflg = 0; 13555242Smarc int i; 13655242Smarc char *firststr, *laststr; 13755242Smarc int first, last, direction; 13855242Smarc char *pat = NULL, *repl; /* ksh "fc old=new" crap */ 13955242Smarc static int active = 0; 14055242Smarc struct jmploc jmploc; 14155242Smarc struct jmploc *volatile savehandler; 14255242Smarc char editfile[MAXPATHLEN + 1]; 14355242Smarc FILE *efp; 14455242Smarc 14555242Smarc if (hist == NULL) 14655242Smarc error("history not active"); 14756930Sbostic 14856930Sbostic if (argc == 1) 14956930Sbostic error("missing history argument"); 15055242Smarc 15155242Smarc optreset = 1; optind = 1; /* initialize getopt */ 15255242Smarc while (not_fcnumber(argv[optind]) && 15355242Smarc (ch = getopt(argc, argv, ":e:lnrs")) != EOF) 15455242Smarc switch ((char)ch) { 15555242Smarc case 'e': 15655242Smarc editor = optarg; 15755242Smarc break; 15855242Smarc case 'l': 15955242Smarc lflg = 1; 16055242Smarc break; 16155242Smarc case 'n': 16255242Smarc nflg = 1; 16355242Smarc break; 16455242Smarc case 'r': 16555242Smarc rflg = 1; 16655242Smarc break; 16755242Smarc case 's': 16855242Smarc sflg = 1; 16955242Smarc break; 17055242Smarc case ':': 17155242Smarc error("option -%c expects argument", optopt); 17255242Smarc case '?': 17355242Smarc default: 17455242Smarc error("unknown option: -%c", optopt); 17555242Smarc } 17655242Smarc argc -= optind, argv += optind; 17755242Smarc 17855242Smarc /* 17955242Smarc * If executing... 18055242Smarc */ 18155242Smarc if (lflg == 0 || editor || sflg) { 18255242Smarc lflg = 0; /* ignore */ 18355242Smarc editfile[0] = '\0'; 18455242Smarc /* 18555242Smarc * Catch interrupts to reset active counter and 18655242Smarc * cleanup temp files. 18755242Smarc */ 18855242Smarc if (setjmp(jmploc.loc)) { 18955242Smarc active = 0; 19055242Smarc if (*editfile) 19155242Smarc unlink(editfile); 19255242Smarc handler = savehandler; 19355242Smarc longjmp(handler->loc, 1); 19455242Smarc } 19555242Smarc savehandler = handler; 19655242Smarc handler = &jmploc; 19755242Smarc if (++active > MAXHISTLOOPS) { 19855242Smarc active = 0; 19955242Smarc displayhist = 0; 20055242Smarc error("called recursively too many times"); 20155242Smarc } 20255242Smarc /* 20355242Smarc * Set editor. 20455242Smarc */ 20555242Smarc if (sflg == 0) { 20655242Smarc if (editor == NULL && 20755246Smarc (editor = bltinlookup("FCEDIT", 1)) == NULL && 20855246Smarc (editor = bltinlookup("EDITOR", 1)) == NULL) 20955242Smarc editor = DEFEDITOR; 21055242Smarc if (editor[0] == '-' && editor[1] == '\0') { 21155242Smarc sflg = 1; /* no edit */ 21255242Smarc editor = NULL; 21355242Smarc } 21455242Smarc } 21555242Smarc } 21655242Smarc 21755242Smarc /* 21855242Smarc * If executing, parse [old=new] now 21955242Smarc */ 22055242Smarc if (lflg == 0 && argc > 0 && 22155242Smarc ((repl = strchr(argv[0], '=')) != NULL)) { 22255242Smarc pat = argv[0]; 22355242Smarc *repl++ = '\0'; 22455242Smarc argc--, argv++; 22555242Smarc } 22655242Smarc /* 22755242Smarc * determine [first] and [last] 22855242Smarc */ 22955242Smarc switch (argc) { 23055242Smarc case 0: 23155242Smarc firststr = lflg ? "-16" : "-1"; 23255242Smarc laststr = "-1"; 23355242Smarc break; 23455242Smarc case 1: 23555242Smarc firststr = argv[0]; 23655242Smarc laststr = lflg ? "-1" : argv[0]; 23755242Smarc break; 23855242Smarc case 2: 23955242Smarc firststr = argv[0]; 24055242Smarc laststr = argv[1]; 24155242Smarc break; 24255242Smarc default: 24355242Smarc error("too many args"); 24455242Smarc } 24555242Smarc /* 24655242Smarc * Turn into event numbers. 24755242Smarc */ 24855242Smarc first = str_to_event(firststr, 0); 24955242Smarc last = str_to_event(laststr, 1); 25055242Smarc 25155242Smarc if (rflg) { 25255242Smarc i = last; 25355242Smarc last = first; 25455242Smarc first = i; 25555242Smarc } 25655242Smarc /* 25755242Smarc * XXX - this should not depend on the event numbers 25855242Smarc * always increasing. Add sequence numbers or offset 25955242Smarc * to the history element in next (diskbased) release. 26055242Smarc */ 26155242Smarc direction = first < last ? H_PREV : H_NEXT; 26255242Smarc 26355242Smarc /* 26455242Smarc * If editing, grab a temp file. 26555242Smarc */ 26655242Smarc if (editor) { 26755242Smarc int fd; 26855242Smarc INTOFF; /* easier */ 26955242Smarc sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP); 27055242Smarc if ((fd = mkstemp(editfile)) < 0) 27155242Smarc error("can't create temporary file %s", editfile); 27255242Smarc if ((efp = fdopen(fd, "w")) == NULL) { 27355242Smarc close(fd); 27455242Smarc error("can't allocate stdio buffer for temp\n"); 27555242Smarc } 27655242Smarc } 27755242Smarc 27855242Smarc /* 27955242Smarc * Loop through selected history events. If listing or executing, 28055242Smarc * do it now. Otherwise, put into temp file and call the editor 28155242Smarc * after. 28255242Smarc * 28355242Smarc * The history interface needs rethinking, as the following 28455242Smarc * convolutions will demonstrate. 28555242Smarc */ 28655242Smarc history(hist, H_FIRST); 28755242Smarc he = history(hist, H_NEXT_EVENT, first); 28855242Smarc for (;he != NULL; he = history(hist, direction)) { 28955242Smarc if (lflg) { 29055242Smarc if (!nflg) 29155242Smarc out1fmt("%5d ", he->num); 29255242Smarc out1str(he->str); 29355242Smarc } else { 29455242Smarc char *s = pat ? 29555242Smarc fc_replace(he->str, pat, repl) : (char *)he->str; 29655242Smarc 29755242Smarc if (sflg) { 29855242Smarc if (displayhist) { 29955242Smarc out2str(s); 30055242Smarc } 30155242Smarc evalstring(s); 30255242Smarc if (displayhist && hist) { 30355242Smarc /* 30455242Smarc * XXX what about recursive and 30555242Smarc * relative histnums. 30655242Smarc */ 30755242Smarc history(hist, H_ENTER, s); 30855242Smarc } 30955242Smarc } else 31055242Smarc fputs(s, efp); 31155242Smarc } 31255242Smarc /* 31355242Smarc * At end? (if we were to loose last, we'd sure be 31455242Smarc * messed up). 31555242Smarc */ 31655242Smarc if (he->num == last) 31755242Smarc break; 31855242Smarc } 31955242Smarc if (editor) { 32055242Smarc char *editcmd; 32155242Smarc 32255242Smarc fclose(efp); 32355242Smarc editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); 32455242Smarc sprintf(editcmd, "%s %s", editor, editfile); 32555242Smarc evalstring(editcmd); /* XXX - should use no JC command */ 32655242Smarc INTON; 32755242Smarc readcmdfile(editfile); /* XXX - should read back - quick tst */ 32855242Smarc unlink(editfile); 32955242Smarc } 33055242Smarc 33155242Smarc if (lflg == 0 && active > 0) 33255242Smarc --active; 33355242Smarc if (displayhist) 33455242Smarc displayhist = 0; 33555242Smarc } 33655242Smarc 33755242Smarc STATIC char * 33855242Smarc fc_replace(s, p, r) 33955242Smarc const char *s; 34055242Smarc char *p, *r; 34155242Smarc { 34255242Smarc char *dest; 34355242Smarc int plen = strlen(p); 34455242Smarc 34555242Smarc STARTSTACKSTR(dest); 34655242Smarc while (*s) { 34755242Smarc if (*s == *p && strncmp(s, p, plen) == 0) { 34855242Smarc while (*r) 34955242Smarc STPUTC(*r++, dest); 35055242Smarc s += plen; 35155242Smarc *p = '\0'; /* so no more matches */ 35255242Smarc } else 35355242Smarc STPUTC(*s++, dest); 35455242Smarc } 35555242Smarc STACKSTRNUL(dest); 35655242Smarc dest = grabstackstr(dest); 35755242Smarc 35855242Smarc return (dest); 35955242Smarc } 36055242Smarc 36155242Smarc not_fcnumber(s) 36255242Smarc char *s; 36355242Smarc { 36455242Smarc if (*s == '-') 36555242Smarc s++; 36655242Smarc return (!is_number(s)); 36755242Smarc } 36855242Smarc 36955242Smarc str_to_event(str, last) 37055242Smarc char *str; 37155242Smarc int last; 37255242Smarc { 37355242Smarc const HistEvent *he; 37455242Smarc char *s = str; 37555242Smarc int relative = 0; 37655242Smarc int i, j; 37755242Smarc 37855242Smarc he = history(hist, H_FIRST); 37955242Smarc switch (*s) { 38055242Smarc case '-': 38155242Smarc relative = 1; 38255242Smarc /*FALLTHROUGH*/ 38355242Smarc case '+': 38455242Smarc s++; 38555242Smarc } 38655242Smarc if (is_number(s)) { 38755242Smarc i = atoi(s); 38855242Smarc if (relative) { 38955242Smarc while (he != NULL && i--) { 39055242Smarc he = history(hist, H_NEXT); 39155242Smarc } 39255242Smarc if (he == NULL) 39355242Smarc he = history(hist, H_LAST); 39455242Smarc } else { 39555242Smarc he = history(hist, H_NEXT_EVENT, i); 39655242Smarc if (he == NULL) { 39755242Smarc /* 39855242Smarc * the notion of first and last is 39955242Smarc * backwards to that of the history package 40055242Smarc */ 40155242Smarc he = history(hist, last ? H_FIRST : H_LAST); 40255242Smarc } 40355242Smarc } 40455242Smarc if (he == NULL) 40555242Smarc error("history number %s not found (internal error)", 40655242Smarc str); 40755242Smarc } else { 40855242Smarc /* 40955242Smarc * pattern 41055242Smarc */ 41155298Smarc he = history(hist, H_PREV_STR, str); 41255242Smarc if (he == NULL) 41355242Smarc error("history pattern not found: %s", str); 41455242Smarc } 41555242Smarc return (he->num); 41655242Smarc } 417