histedit.c revision 84261
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Kenneth Almquist. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 351556Srgrimes */ 361556Srgrimes 371556Srgrimes#ifndef lint 3836150Scharnier#if 0 3936150Scharnierstatic char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; 4036150Scharnier#endif 4136150Scharnierstatic const char rcsid[] = 4250471Speter "$FreeBSD: head/bin/sh/histedit.c 84261 2001-10-01 08:43:58Z obrien $"; 431556Srgrimes#endif /* not lint */ 441556Srgrimes 4517987Speter#include <sys/param.h> 4677463Simp#include <limits.h> 4717987Speter#include <paths.h> 4817987Speter#include <stdio.h> 4917987Speter#include <stdlib.h> 5017987Speter#include <unistd.h> 511556Srgrimes/* 521556Srgrimes * Editline and history functions (and glue). 531556Srgrimes */ 541556Srgrimes#include "shell.h" 551556Srgrimes#include "parser.h" 561556Srgrimes#include "var.h" 571556Srgrimes#include "options.h" 5817987Speter#include "main.h" 5917987Speter#include "output.h" 601556Srgrimes#include "mystring.h" 6117987Speter#ifndef NO_HISTORY 6217987Speter#include "myhistedit.h" 631556Srgrimes#include "error.h" 6417987Speter#include "eval.h" 651556Srgrimes#include "memalloc.h" 661556Srgrimes 671556Srgrimes#define MAXHISTLOOPS 4 /* max recursions through fc */ 681556Srgrimes#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ 691556Srgrimes 701556SrgrimesHistory *hist; /* history cookie */ 711556SrgrimesEditLine *el; /* editline cookie */ 721556Srgrimesint displayhist; 7384261Sobrienstatic FILE *el_in, *el_out, *el_err; 741556Srgrimes 751556SrgrimesSTATIC char *fc_replace __P((const char *, char *, char *)); 761556Srgrimes 771556Srgrimes/* 781556Srgrimes * Set history and editing status. Called whenever the status may 791556Srgrimes * have changed (figures out what to do). 801556Srgrimes */ 8117987Spetervoid 8220425Sstevehistedit() 8317987Speter{ 841556Srgrimes 851556Srgrimes#define editing (Eflag || Vflag) 861556Srgrimes 871556Srgrimes if (iflag) { 881556Srgrimes if (!hist) { 891556Srgrimes /* 901556Srgrimes * turn history on 911556Srgrimes */ 921556Srgrimes INTOFF; 931556Srgrimes hist = history_init(); 941556Srgrimes INTON; 951556Srgrimes 961556Srgrimes if (hist != NULL) 9720425Ssteve sethistsize(histsizeval()); 981556Srgrimes else 991556Srgrimes out2str("sh: can't initialize history\n"); 1001556Srgrimes } 1011556Srgrimes if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ 1021556Srgrimes /* 1031556Srgrimes * turn editing on 1041556Srgrimes */ 1051556Srgrimes INTOFF; 1061556Srgrimes if (el_in == NULL) 1071556Srgrimes el_in = fdopen(0, "r"); 10884261Sobrien if (el_err == NULL) 10984261Sobrien el_err = fdopen(1, "w"); 1101556Srgrimes if (el_out == NULL) 1111556Srgrimes el_out = fdopen(2, "w"); 11284261Sobrien if (el_in == NULL || el_err == NULL || el_out == NULL) 1131556Srgrimes goto bad; 11484261Sobrien el = el_init(arg0, el_in, el_out, el_err); 1151556Srgrimes if (el != NULL) { 1161556Srgrimes if (hist) 1171556Srgrimes el_set(el, EL_HIST, history, hist); 1181556Srgrimes el_set(el, EL_PROMPT, getprompt); 1191556Srgrimes } else { 1201556Srgrimesbad: 1211556Srgrimes out2str("sh: can't initialize editing\n"); 1221556Srgrimes } 1231556Srgrimes INTON; 1241556Srgrimes } else if (!editing && el) { 1251556Srgrimes INTOFF; 1261556Srgrimes el_end(el); 1271556Srgrimes el = NULL; 1281556Srgrimes INTON; 1291556Srgrimes } 1301556Srgrimes if (el) { 1311556Srgrimes if (Vflag) 1321556Srgrimes el_set(el, EL_EDITOR, "vi"); 1331556Srgrimes else if (Eflag) 1341556Srgrimes el_set(el, EL_EDITOR, "emacs"); 1351556Srgrimes } 1361556Srgrimes } else { 1371556Srgrimes INTOFF; 1381556Srgrimes if (el) { /* no editing if not interactive */ 1391556Srgrimes el_end(el); 1401556Srgrimes el = NULL; 1411556Srgrimes } 1421556Srgrimes if (hist) { 1431556Srgrimes history_end(hist); 1441556Srgrimes hist = NULL; 1451556Srgrimes } 1461556Srgrimes INTON; 1471556Srgrimes } 1481556Srgrimes} 1491556Srgrimes 15017987Speter 15117987Spetervoid 15220425Sstevesethistsize(hs) 15320425Ssteve const char *hs; 15417987Speter{ 1551556Srgrimes int histsize; 15684261Sobrien HistEvent he; 1571556Srgrimes 1581556Srgrimes if (hist != NULL) { 15920425Ssteve if (hs == NULL || *hs == '\0' || 16020425Ssteve (histsize = atoi(hs)) < 0) 1611556Srgrimes histsize = 100; 16284261Sobrien history(hist, &he, H_EVENT, histsize); 1631556Srgrimes } 1641556Srgrimes} 1651556Srgrimes 1661556Srgrimes/* 1671556Srgrimes * This command is provided since POSIX decided to standardize 1681556Srgrimes * the Korn shell fc command. Oh well... 1691556Srgrimes */ 17017987Speterint 1711556Srgrimeshistcmd(argc, argv) 17217987Speter int argc; 17317987Speter char **argv; 1741556Srgrimes{ 1751556Srgrimes int ch; 1761556Srgrimes char *editor = NULL; 17784261Sobrien HistEvent he; 1781556Srgrimes int lflg = 0, nflg = 0, rflg = 0, sflg = 0; 17984261Sobrien int i, retval; 1801556Srgrimes char *firststr, *laststr; 1811556Srgrimes int first, last, direction; 1821556Srgrimes char *pat = NULL, *repl; /* ksh "fc old=new" crap */ 1831556Srgrimes static int active = 0; 1841556Srgrimes struct jmploc jmploc; 1851556Srgrimes struct jmploc *volatile savehandler; 18677463Simp char editfile[PATH_MAX]; 1871556Srgrimes FILE *efp; 18817987Speter#ifdef __GNUC__ 18917987Speter /* Avoid longjmp clobbering */ 19017987Speter (void) &editor; 19117987Speter (void) &lflg; 19217987Speter (void) &nflg; 19317987Speter (void) &rflg; 19417987Speter (void) &sflg; 19517987Speter (void) &firststr; 19617987Speter (void) &laststr; 19717987Speter (void) &pat; 19817987Speter (void) &repl; 19917987Speter (void) &efp; 20017987Speter (void) &argc; 20117987Speter (void) &argv; 20217987Speter#endif 2031556Srgrimes 2041556Srgrimes if (hist == NULL) 2051556Srgrimes error("history not active"); 2068855Srgrimes 2071556Srgrimes if (argc == 1) 2081556Srgrimes error("missing history argument"); 2091556Srgrimes 2101556Srgrimes optreset = 1; optind = 1; /* initialize getopt */ 2111556Srgrimes while (not_fcnumber(argv[optind]) && 21224348Simp (ch = getopt(argc, argv, ":e:lnrs")) != -1) 2131556Srgrimes switch ((char)ch) { 2141556Srgrimes case 'e': 21559436Scracauer editor = shoptarg; 2161556Srgrimes break; 2171556Srgrimes case 'l': 2181556Srgrimes lflg = 1; 2191556Srgrimes break; 2201556Srgrimes case 'n': 2211556Srgrimes nflg = 1; 2221556Srgrimes break; 2231556Srgrimes case 'r': 2241556Srgrimes rflg = 1; 2251556Srgrimes break; 2261556Srgrimes case 's': 2271556Srgrimes sflg = 1; 2281556Srgrimes break; 2291556Srgrimes case ':': 2301556Srgrimes error("option -%c expects argument", optopt); 2311556Srgrimes case '?': 2321556Srgrimes default: 2331556Srgrimes error("unknown option: -%c", optopt); 2341556Srgrimes } 2351556Srgrimes argc -= optind, argv += optind; 2361556Srgrimes 2371556Srgrimes /* 2381556Srgrimes * If executing... 2391556Srgrimes */ 2401556Srgrimes if (lflg == 0 || editor || sflg) { 2411556Srgrimes lflg = 0; /* ignore */ 2421556Srgrimes editfile[0] = '\0'; 2431556Srgrimes /* 2441556Srgrimes * Catch interrupts to reset active counter and 2451556Srgrimes * cleanup temp files. 2461556Srgrimes */ 2471556Srgrimes if (setjmp(jmploc.loc)) { 2481556Srgrimes active = 0; 2491556Srgrimes if (*editfile) 2501556Srgrimes unlink(editfile); 2511556Srgrimes handler = savehandler; 2521556Srgrimes longjmp(handler->loc, 1); 2531556Srgrimes } 2541556Srgrimes savehandler = handler; 2551556Srgrimes handler = &jmploc; 2561556Srgrimes if (++active > MAXHISTLOOPS) { 2571556Srgrimes active = 0; 2581556Srgrimes displayhist = 0; 2591556Srgrimes error("called recursively too many times"); 2601556Srgrimes } 2611556Srgrimes /* 2621556Srgrimes * Set editor. 2631556Srgrimes */ 2641556Srgrimes if (sflg == 0) { 2651556Srgrimes if (editor == NULL && 2661556Srgrimes (editor = bltinlookup("FCEDIT", 1)) == NULL && 2671556Srgrimes (editor = bltinlookup("EDITOR", 1)) == NULL) 2681556Srgrimes editor = DEFEDITOR; 2691556Srgrimes if (editor[0] == '-' && editor[1] == '\0') { 2701556Srgrimes sflg = 1; /* no edit */ 2711556Srgrimes editor = NULL; 2721556Srgrimes } 2731556Srgrimes } 2741556Srgrimes } 2751556Srgrimes 2761556Srgrimes /* 2771556Srgrimes * If executing, parse [old=new] now 2781556Srgrimes */ 2798855Srgrimes if (lflg == 0 && argc > 0 && 2801556Srgrimes ((repl = strchr(argv[0], '=')) != NULL)) { 2811556Srgrimes pat = argv[0]; 2821556Srgrimes *repl++ = '\0'; 2831556Srgrimes argc--, argv++; 2841556Srgrimes } 2851556Srgrimes /* 2861556Srgrimes * determine [first] and [last] 2871556Srgrimes */ 2881556Srgrimes switch (argc) { 2891556Srgrimes case 0: 2901556Srgrimes firststr = lflg ? "-16" : "-1"; 2911556Srgrimes laststr = "-1"; 2921556Srgrimes break; 2931556Srgrimes case 1: 2941556Srgrimes firststr = argv[0]; 2951556Srgrimes laststr = lflg ? "-1" : argv[0]; 2961556Srgrimes break; 2971556Srgrimes case 2: 2981556Srgrimes firststr = argv[0]; 2991556Srgrimes laststr = argv[1]; 3001556Srgrimes break; 3011556Srgrimes default: 3021556Srgrimes error("too many args"); 3031556Srgrimes } 3041556Srgrimes /* 3051556Srgrimes * Turn into event numbers. 3061556Srgrimes */ 3071556Srgrimes first = str_to_event(firststr, 0); 3081556Srgrimes last = str_to_event(laststr, 1); 3091556Srgrimes 3101556Srgrimes if (rflg) { 3111556Srgrimes i = last; 3121556Srgrimes last = first; 3131556Srgrimes first = i; 3141556Srgrimes } 3151556Srgrimes /* 3161556Srgrimes * XXX - this should not depend on the event numbers 3178855Srgrimes * always increasing. Add sequence numbers or offset 3181556Srgrimes * to the history element in next (diskbased) release. 3191556Srgrimes */ 3201556Srgrimes direction = first < last ? H_PREV : H_NEXT; 3211556Srgrimes 3221556Srgrimes /* 3231556Srgrimes * If editing, grab a temp file. 3241556Srgrimes */ 3251556Srgrimes if (editor) { 3261556Srgrimes int fd; 3271556Srgrimes INTOFF; /* easier */ 3281556Srgrimes sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP); 3291556Srgrimes if ((fd = mkstemp(editfile)) < 0) 3301556Srgrimes error("can't create temporary file %s", editfile); 3311556Srgrimes if ((efp = fdopen(fd, "w")) == NULL) { 3321556Srgrimes close(fd); 33318016Speter error("can't allocate stdio buffer for temp"); 3341556Srgrimes } 3351556Srgrimes } 3361556Srgrimes 3371556Srgrimes /* 3381556Srgrimes * Loop through selected history events. If listing or executing, 3391556Srgrimes * do it now. Otherwise, put into temp file and call the editor 3401556Srgrimes * after. 3411556Srgrimes * 3421556Srgrimes * The history interface needs rethinking, as the following 3431556Srgrimes * convolutions will demonstrate. 3441556Srgrimes */ 34584261Sobrien history(hist, &he, H_FIRST); 34684261Sobrien retval = history(hist, &he, H_NEXT_EVENT, first); 34784261Sobrien for (;retval != -1; retval = history(hist, &he, direction)) { 3481556Srgrimes if (lflg) { 3491556Srgrimes if (!nflg) 35084261Sobrien out1fmt("%5d ", he.num); 35184261Sobrien out1str(he.str); 3521556Srgrimes } else { 3538855Srgrimes char *s = pat ? 35484261Sobrien fc_replace(he.str, pat, repl) : (char *)he.str; 3551556Srgrimes 3561556Srgrimes if (sflg) { 3571556Srgrimes if (displayhist) { 3581556Srgrimes out2str(s); 3591556Srgrimes } 3601556Srgrimes evalstring(s); 3611556Srgrimes if (displayhist && hist) { 3621556Srgrimes /* 3638855Srgrimes * XXX what about recursive and 3641556Srgrimes * relative histnums. 3651556Srgrimes */ 36684261Sobrien history(hist, &he, H_ENTER, s); 3671556Srgrimes } 3681556Srgrimes } else 3691556Srgrimes fputs(s, efp); 3701556Srgrimes } 3711556Srgrimes /* 3721556Srgrimes * At end? (if we were to loose last, we'd sure be 3731556Srgrimes * messed up). 3741556Srgrimes */ 37584261Sobrien if (he.num == last) 3761556Srgrimes break; 3771556Srgrimes } 3781556Srgrimes if (editor) { 3791556Srgrimes char *editcmd; 3801556Srgrimes 3811556Srgrimes fclose(efp); 3821556Srgrimes editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); 3831556Srgrimes sprintf(editcmd, "%s %s", editor, editfile); 3841556Srgrimes evalstring(editcmd); /* XXX - should use no JC command */ 3851556Srgrimes INTON; 3861556Srgrimes readcmdfile(editfile); /* XXX - should read back - quick tst */ 3871556Srgrimes unlink(editfile); 3881556Srgrimes } 3898855Srgrimes 3901556Srgrimes if (lflg == 0 && active > 0) 3911556Srgrimes --active; 3921556Srgrimes if (displayhist) 3931556Srgrimes displayhist = 0; 39417987Speter return 0; 3951556Srgrimes} 3961556Srgrimes 3971556SrgrimesSTATIC char * 3981556Srgrimesfc_replace(s, p, r) 3991556Srgrimes const char *s; 4001556Srgrimes char *p, *r; 4011556Srgrimes{ 4021556Srgrimes char *dest; 4031556Srgrimes int plen = strlen(p); 4041556Srgrimes 4051556Srgrimes STARTSTACKSTR(dest); 4061556Srgrimes while (*s) { 4071556Srgrimes if (*s == *p && strncmp(s, p, plen) == 0) { 4081556Srgrimes while (*r) 4091556Srgrimes STPUTC(*r++, dest); 4101556Srgrimes s += plen; 4111556Srgrimes *p = '\0'; /* so no more matches */ 4121556Srgrimes } else 4131556Srgrimes STPUTC(*s++, dest); 4141556Srgrimes } 4151556Srgrimes STACKSTRNUL(dest); 4161556Srgrimes dest = grabstackstr(dest); 4171556Srgrimes 4181556Srgrimes return (dest); 4191556Srgrimes} 4201556Srgrimes 42117987Speterint 4221556Srgrimesnot_fcnumber(s) 42320425Ssteve char *s; 4241556Srgrimes{ 42520425Ssteve if (s == NULL) 42625224Ssteve return (0); 42720425Ssteve if (*s == '-') 42820425Ssteve s++; 4291556Srgrimes return (!is_number(s)); 4301556Srgrimes} 4311556Srgrimes 43217987Speterint 4331556Srgrimesstr_to_event(str, last) 4341556Srgrimes char *str; 4351556Srgrimes int last; 4361556Srgrimes{ 43784261Sobrien HistEvent he; 4381556Srgrimes char *s = str; 4391556Srgrimes int relative = 0; 44084261Sobrien int i, retval; 4411556Srgrimes 44284261Sobrien retval = history(hist, &he, H_FIRST); 4431556Srgrimes switch (*s) { 4441556Srgrimes case '-': 4451556Srgrimes relative = 1; 4461556Srgrimes /*FALLTHROUGH*/ 4471556Srgrimes case '+': 4481556Srgrimes s++; 4491556Srgrimes } 4501556Srgrimes if (is_number(s)) { 4511556Srgrimes i = atoi(s); 4521556Srgrimes if (relative) { 45384261Sobrien while (retval != -1 && i--) { 45484261Sobrien retval = history(hist, &he, H_NEXT); 4551556Srgrimes } 45684261Sobrien if (retval == -1) 45784261Sobrien retval = history(hist, &he, H_LAST); 4581556Srgrimes } else { 45984261Sobrien retval = history(hist, &he, H_NEXT_EVENT, i); 46084261Sobrien if (retval == -1) { 4611556Srgrimes /* 4621556Srgrimes * the notion of first and last is 4631556Srgrimes * backwards to that of the history package 4641556Srgrimes */ 46584261Sobrien retval = history(hist, &he, last ? H_FIRST : H_LAST); 4661556Srgrimes } 4671556Srgrimes } 46884261Sobrien if (retval == -1) 4691556Srgrimes error("history number %s not found (internal error)", 4701556Srgrimes str); 4711556Srgrimes } else { 4721556Srgrimes /* 4738855Srgrimes * pattern 4741556Srgrimes */ 47584261Sobrien retval = history(hist, &he, H_PREV_STR, str); 47684261Sobrien if (retval == -1) 4771556Srgrimes error("history pattern not found: %s", str); 4781556Srgrimes } 47984261Sobrien return (he.num); 4801556Srgrimes} 48125224Ssteve#else 48225224Ssteve#include "error.h" 48325224Ssteve 48425224Ssteveint 48525224Sstevehistcmd(argc, argv) 48625224Ssteve int argc; 48725224Ssteve char **argv; 48825224Ssteve{ 48925224Ssteve 49025224Ssteve error("not compiled with history support"); 49125224Ssteve /*NOTREACHED*/ 49225224Ssteve return (0); 49325224Ssteve} 49425224Ssteve#endif 495