histedit.c revision 231790
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 * 4. Neither the name of the University nor the names of its contributors 171556Srgrimes * may be used to endorse or promote products derived from this software 181556Srgrimes * without specific prior written permission. 191556Srgrimes * 201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301556Srgrimes * SUCH DAMAGE. 311556Srgrimes */ 321556Srgrimes 331556Srgrimes#ifndef lint 341556Srgrimes#if 0 351556Srgrimesstatic char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; 361556Srgrimes#endif 371556Srgrimes#endif /* not lint */ 3836150Scharnier#include <sys/cdefs.h> 3936150Scharnier__FBSDID("$FreeBSD: stable/9/bin/sh/histedit.c 231790 2012-02-15 22:45:57Z jilles $"); 4036150Scharnier 4136150Scharnier#include <sys/param.h> 4250471Speter#include <limits.h> 431556Srgrimes#include <paths.h> 441556Srgrimes#include <stdio.h> 4517987Speter#include <stdlib.h> 4677463Simp#include <unistd.h> 4717987Speter/* 4817987Speter * Editline and history functions (and glue). 4917987Speter */ 5017987Speter#include "shell.h" 511556Srgrimes#include "parser.h" 521556Srgrimes#include "var.h" 531556Srgrimes#include "options.h" 541556Srgrimes#include "main.h" 551556Srgrimes#include "output.h" 561556Srgrimes#include "mystring.h" 571556Srgrimes#ifndef NO_HISTORY 5817987Speter#include "myhistedit.h" 5917987Speter#include "error.h" 601556Srgrimes#include "eval.h" 6117987Speter#include "memalloc.h" 6217987Speter#include "builtins.h" 631556Srgrimes 6417987Speter#define MAXHISTLOOPS 4 /* max recursions through fc */ 651556Srgrimes#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ 661556Srgrimes 671556SrgrimesHistory *hist; /* history cookie */ 681556SrgrimesEditLine *el; /* editline cookie */ 691556Srgrimesint displayhist; 701556Srgrimesstatic FILE *el_in, *el_out, *el_err; 711556Srgrimes 721556Srgrimesstatic char *fc_replace(const char *, char *, char *); 7384261Sobrienstatic int not_fcnumber(const char *); 741556Srgrimesstatic int str_to_event(const char *, int); 7590111Simp 761556Srgrimes/* 771556Srgrimes * Set history and editing status. Called whenever the status may 781556Srgrimes * have changed (figures out what to do). 791556Srgrimes */ 801556Srgrimesvoid 8117987Speterhistedit(void) 8290111Simp{ 8317987Speter 841556Srgrimes#define editing (Eflag || Vflag) 851556Srgrimes 861556Srgrimes if (iflag) { 871556Srgrimes if (!hist) { 881556Srgrimes /* 891556Srgrimes * turn history on 901556Srgrimes */ 911556Srgrimes INTOFF; 921556Srgrimes hist = history_init(); 931556Srgrimes INTON; 941556Srgrimes 951556Srgrimes if (hist != NULL) 961556Srgrimes sethistsize(histsizeval()); 9720425Ssteve else 981556Srgrimes out2fmt_flush("sh: can't initialize history\n"); 991556Srgrimes } 1001556Srgrimes if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ 1011556Srgrimes /* 1021556Srgrimes * turn editing on 1031556Srgrimes */ 1041556Srgrimes char *term; 1051556Srgrimes 1061556Srgrimes INTOFF; 1071556Srgrimes if (el_in == NULL) 10884261Sobrien el_in = fdopen(0, "r"); 10984261Sobrien if (el_err == NULL) 1101556Srgrimes el_err = fdopen(1, "w"); 1111556Srgrimes if (el_out == NULL) 11284261Sobrien el_out = fdopen(2, "w"); 1131556Srgrimes if (el_in == NULL || el_err == NULL || el_out == NULL) 11484261Sobrien goto bad; 1151556Srgrimes term = lookupvar("TERM"); 1161556Srgrimes if (term) 1171556Srgrimes setenv("TERM", term, 1); 1181556Srgrimes else 1191556Srgrimes unsetenv("TERM"); 1201556Srgrimes el = el_init(arg0, el_in, el_out, el_err); 1211556Srgrimes if (el != NULL) { 1221556Srgrimes if (hist) 1231556Srgrimes el_set(el, EL_HIST, history, hist); 1241556Srgrimes el_set(el, EL_PROMPT, getprompt); 1251556Srgrimes el_set(el, EL_ADDFN, "sh-complete", 1261556Srgrimes "Filename completion", 1271556Srgrimes _el_fn_sh_complete); 1281556Srgrimes } else { 1291556Srgrimesbad: 1301556Srgrimes out2fmt_flush("sh: can't initialize editing\n"); 1311556Srgrimes } 1321556Srgrimes INTON; 1331556Srgrimes } else if (!editing && el) { 1341556Srgrimes INTOFF; 1351556Srgrimes el_end(el); 1361556Srgrimes el = NULL; 1371556Srgrimes INTON; 1381556Srgrimes } 1391556Srgrimes if (el) { 1401556Srgrimes if (Vflag) 1411556Srgrimes el_set(el, EL_EDITOR, "vi"); 1421556Srgrimes else if (Eflag) 1431556Srgrimes el_set(el, EL_EDITOR, "emacs"); 1441556Srgrimes el_set(el, EL_BIND, "^I", "sh-complete", NULL); 1451556Srgrimes el_source(el, NULL); 1461556Srgrimes } 1471556Srgrimes } else { 1481556Srgrimes INTOFF; 1491556Srgrimes if (el) { /* no editing if not interactive */ 15017987Speter el_end(el); 15117987Speter el = NULL; 15220425Ssteve } 15320425Ssteve if (hist) { 15417987Speter history_end(hist); 1551556Srgrimes hist = NULL; 15684261Sobrien } 1571556Srgrimes INTON; 1581556Srgrimes } 15920425Ssteve} 16020425Ssteve 1611556Srgrimes 16284261Sobrienvoid 1631556Srgrimessethistsize(hs) 1641556Srgrimes const char *hs; 1651556Srgrimes{ 1661556Srgrimes int histsize; 1671556Srgrimes HistEvent he; 1681556Srgrimes 1691556Srgrimes if (hist != NULL) { 17017987Speter if (hs == NULL || *hs == '\0' || 17190111Simp (histsize = atoi(hs)) < 0) 1721556Srgrimes histsize = 100; 1731556Srgrimes history(hist, &he, H_SETSIZE, histsize); 1741556Srgrimes history(hist, &he, H_SETUNIQUE, 1); 17584261Sobrien } 1761556Srgrimes} 17784261Sobrien 1781556Srgrimesvoid 1791556Srgrimessetterm(const char *term) 1801556Srgrimes{ 1811556Srgrimes if (rootshell && el != NULL && term != NULL) 1821556Srgrimes el_set(el, EL_TERMINAL, term); 1831556Srgrimes} 18477463Simp 1851556Srgrimesint 18697730Stjrhistcmd(int argc, char **argv) 18717987Speter{ 18817987Speter int ch; 18917987Speter const char *editor = NULL; 19017987Speter HistEvent he; 19117987Speter int lflg = 0, nflg = 0, rflg = 0, sflg = 0; 19217987Speter int i, retval; 19317987Speter const char *firststr, *laststr; 19417987Speter int first, last, direction; 19517987Speter char *pat = NULL, *repl = NULL; 19617987Speter static int active = 0; 19717987Speter struct jmploc jmploc; 19817987Speter struct jmploc *savehandler; 19917987Speter char editfilestr[PATH_MAX]; 20017987Speter char *volatile editfile; 20117987Speter FILE *efp = NULL; 2021556Srgrimes int oldhistnum; 2031556Srgrimes 2041556Srgrimes if (hist == NULL) 2058855Srgrimes error("history not active"); 2061556Srgrimes 2071556Srgrimes if (argc == 1) 2081556Srgrimes error("missing history argument"); 2091556Srgrimes 2101556Srgrimes optreset = 1; optind = 1; /* initialize getopt */ 21124348Simp opterr = 0; 2121556Srgrimes while (not_fcnumber(argv[optind]) && 2131556Srgrimes (ch = getopt(argc, argv, ":e:lnrs")) != -1) 21497731Stjr switch ((char)ch) { 2151556Srgrimes case 'e': 2161556Srgrimes editor = optarg; 2171556Srgrimes break; 2181556Srgrimes case 'l': 2191556Srgrimes lflg = 1; 2201556Srgrimes break; 2211556Srgrimes case 'n': 2221556Srgrimes nflg = 1; 2231556Srgrimes break; 2241556Srgrimes case 'r': 2251556Srgrimes rflg = 1; 2261556Srgrimes break; 2271556Srgrimes case 's': 2281556Srgrimes sflg = 1; 2291556Srgrimes break; 2301556Srgrimes case ':': 2311556Srgrimes error("option -%c expects argument", optopt); 2321556Srgrimes case '?': 2331556Srgrimes default: 2341556Srgrimes error("unknown option: -%c", optopt); 2351556Srgrimes } 2361556Srgrimes argc -= optind, argv += optind; 2371556Srgrimes 2381556Srgrimes savehandler = handler; 2391556Srgrimes /* 2401556Srgrimes * If executing... 2411556Srgrimes */ 2421556Srgrimes if (lflg == 0 || editor || sflg) { 2431556Srgrimes lflg = 0; /* ignore */ 2441556Srgrimes editfile = NULL; 2451556Srgrimes /* 2461556Srgrimes * Catch interrupts to reset active counter and 2471556Srgrimes * cleanup temp files. 2481556Srgrimes */ 2491556Srgrimes if (setjmp(jmploc.loc)) { 2501556Srgrimes active = 0; 2511556Srgrimes if (editfile) 2521556Srgrimes unlink(editfile); 2531556Srgrimes handler = savehandler; 2541556Srgrimes longjmp(handler->loc, 1); 2551556Srgrimes } 2561556Srgrimes handler = &jmploc; 2571556Srgrimes if (++active > MAXHISTLOOPS) { 2581556Srgrimes active = 0; 2591556Srgrimes displayhist = 0; 2601556Srgrimes error("called recursively too many times"); 2611556Srgrimes } 2621556Srgrimes /* 2631556Srgrimes * Set editor. 2641556Srgrimes */ 2651556Srgrimes if (sflg == 0) { 2661556Srgrimes if (editor == NULL && 2671556Srgrimes (editor = bltinlookup("FCEDIT", 1)) == NULL && 2681556Srgrimes (editor = bltinlookup("EDITOR", 1)) == NULL) 2691556Srgrimes editor = DEFEDITOR; 2701556Srgrimes if (editor[0] == '-' && editor[1] == '\0') { 2711556Srgrimes sflg = 1; /* no edit */ 2721556Srgrimes editor = NULL; 2731556Srgrimes } 2741556Srgrimes } 2751556Srgrimes } 2761556Srgrimes 2771556Srgrimes /* 2788855Srgrimes * If executing, parse [old=new] now 2791556Srgrimes */ 2801556Srgrimes if (lflg == 0 && argc > 0 && 2811556Srgrimes ((repl = strchr(argv[0], '=')) != NULL)) { 2821556Srgrimes pat = argv[0]; 2831556Srgrimes *repl++ = '\0'; 2841556Srgrimes argc--, argv++; 2851556Srgrimes } 2861556Srgrimes /* 2871556Srgrimes * determine [first] and [last] 2881556Srgrimes */ 2891556Srgrimes switch (argc) { 2901556Srgrimes case 0: 2911556Srgrimes firststr = lflg ? "-16" : "-1"; 2921556Srgrimes laststr = "-1"; 2931556Srgrimes break; 2941556Srgrimes case 1: 2951556Srgrimes firststr = argv[0]; 2961556Srgrimes laststr = lflg ? "-1" : argv[0]; 2971556Srgrimes break; 2981556Srgrimes case 2: 2991556Srgrimes firststr = argv[0]; 3001556Srgrimes laststr = argv[1]; 3011556Srgrimes break; 3021556Srgrimes default: 3031556Srgrimes error("too many arguments"); 3041556Srgrimes } 3051556Srgrimes /* 3061556Srgrimes * Turn into event numbers. 3071556Srgrimes */ 3081556Srgrimes first = str_to_event(firststr, 0); 3091556Srgrimes last = str_to_event(laststr, 1); 3101556Srgrimes 3111556Srgrimes if (rflg) { 3121556Srgrimes i = last; 3131556Srgrimes last = first; 3141556Srgrimes first = i; 3151556Srgrimes } 3168855Srgrimes /* 3171556Srgrimes * XXX - this should not depend on the event numbers 3181556Srgrimes * always increasing. Add sequence numbers or offset 3191556Srgrimes * to the history element in next (diskbased) release. 3201556Srgrimes */ 3211556Srgrimes direction = first < last ? H_PREV : H_NEXT; 3221556Srgrimes 3231556Srgrimes /* 3241556Srgrimes * If editing, grab a temp file. 3251556Srgrimes */ 3261556Srgrimes if (editor) { 3271556Srgrimes int fd; 3281556Srgrimes INTOFF; /* easier */ 3291556Srgrimes sprintf(editfilestr, "%s/_shXXXXXX", _PATH_TMP); 3301556Srgrimes if ((fd = mkstemp(editfilestr)) < 0) 3311556Srgrimes error("can't create temporary file %s", editfile); 33218016Speter editfile = editfilestr; 3331556Srgrimes if ((efp = fdopen(fd, "w")) == NULL) { 3341556Srgrimes close(fd); 3351556Srgrimes error("Out of space"); 3361556Srgrimes } 3371556Srgrimes } 3381556Srgrimes 3391556Srgrimes /* 3401556Srgrimes * Loop through selected history events. If listing or executing, 3411556Srgrimes * do it now. Otherwise, put into temp file and call the editor 3421556Srgrimes * after. 3431556Srgrimes * 34484261Sobrien * The history interface needs rethinking, as the following 34584261Sobrien * convolutions will demonstrate. 34684261Sobrien */ 3471556Srgrimes history(hist, &he, H_FIRST); 3481556Srgrimes retval = history(hist, &he, H_NEXT_EVENT, first); 34984261Sobrien for (;retval != -1; retval = history(hist, &he, direction)) { 35084261Sobrien if (lflg) { 3511556Srgrimes if (!nflg) 3528855Srgrimes out1fmt("%5d ", he.num); 35384261Sobrien out1str(he.str); 3541556Srgrimes } else { 3551556Srgrimes char *s = pat ? 3561556Srgrimes fc_replace(he.str, pat, repl) : (char *)he.str; 3571556Srgrimes 3581556Srgrimes if (sflg) { 3591556Srgrimes if (displayhist) { 3601556Srgrimes out2str(s); 3611556Srgrimes flushout(out2); 3628855Srgrimes } 3631556Srgrimes evalstring(s, 0); 3641556Srgrimes if (displayhist && hist) { 36597730Stjr /* 36684261Sobrien * XXX what about recursive and 36797730Stjr * relative histnums. 36897730Stjr */ 36997730Stjr oldhistnum = he.num; 37097730Stjr history(hist, &he, H_ENTER, s); 37197730Stjr /* 37297730Stjr * XXX H_ENTER moves the internal 37397730Stjr * cursor, set it back to the current 3741556Srgrimes * entry. 3751556Srgrimes */ 3761556Srgrimes retval = history(hist, &he, 3771556Srgrimes H_NEXT_EVENT, oldhistnum); 3781556Srgrimes } 3791556Srgrimes } else 3801556Srgrimes fputs(s, efp); 3811556Srgrimes } 38284261Sobrien /* 3831556Srgrimes * At end? (if we were to lose last, we'd sure be 3841556Srgrimes * messed up). 3851556Srgrimes */ 3861556Srgrimes if (he.num == last) 3871556Srgrimes break; 3881556Srgrimes } 3891556Srgrimes if (editor) { 3901556Srgrimes char *editcmd; 3911556Srgrimes 3921556Srgrimes fclose(efp); 3931556Srgrimes editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); 3941556Srgrimes sprintf(editcmd, "%s %s", editor, editfile); 3951556Srgrimes evalstring(editcmd, 0); /* XXX - should use no JC command */ 3968855Srgrimes INTON; 3971556Srgrimes readcmdfile(editfile); /* XXX - should read back - quick tst */ 3981556Srgrimes unlink(editfile); 3991556Srgrimes } 4001556Srgrimes 40117987Speter if (lflg == 0 && active > 0) 4021556Srgrimes --active; 4031556Srgrimes if (displayhist) 4041556Srgrimes displayhist = 0; 40590111Simp handler = savehandler; 4061556Srgrimes return 0; 4071556Srgrimes} 4081556Srgrimes 4091556Srgrimesstatic char * 4101556Srgrimesfc_replace(const char *s, char *p, char *r) 4111556Srgrimes{ 4121556Srgrimes char *dest; 4131556Srgrimes int plen = strlen(p); 4141556Srgrimes 4151556Srgrimes STARTSTACKSTR(dest); 4161556Srgrimes while (*s) { 4171556Srgrimes if (*s == *p && strncmp(s, p, plen) == 0) { 4181556Srgrimes STPUTS(r, dest); 4191556Srgrimes s += plen; 4201556Srgrimes *p = '\0'; /* so no more matches */ 4211556Srgrimes } else 4221556Srgrimes STPUTC(*s++, dest); 4231556Srgrimes } 4241556Srgrimes STPUTC('\0', dest); 4251556Srgrimes dest = grabstackstr(dest); 42617987Speter 42790111Simp return (dest); 4281556Srgrimes} 42920425Ssteve 43025224Sstevestatic int 43120425Sstevenot_fcnumber(const char *s) 43220425Ssteve{ 4331556Srgrimes if (s == NULL) 4341556Srgrimes return (0); 4351556Srgrimes if (*s == '-') 43617987Speter s++; 43790111Simp return (!is_number(s)); 4381556Srgrimes} 43984261Sobrien 4401556Srgrimesstatic int 4411556Srgrimesstr_to_event(const char *str, int last) 44284261Sobrien{ 4431556Srgrimes HistEvent he; 44484261Sobrien const char *s = str; 4451556Srgrimes int relative = 0; 4461556Srgrimes int i, retval; 4471556Srgrimes 4481556Srgrimes retval = history(hist, &he, H_FIRST); 4491556Srgrimes switch (*s) { 4501556Srgrimes case '-': 4511556Srgrimes relative = 1; 4521556Srgrimes /*FALLTHROUGH*/ 4531556Srgrimes case '+': 4541556Srgrimes s++; 45584261Sobrien } 45684261Sobrien if (is_number(s)) { 4571556Srgrimes i = atoi(s); 45884261Sobrien if (relative) { 45984261Sobrien while (retval != -1 && i--) { 4601556Srgrimes retval = history(hist, &he, H_NEXT); 46184261Sobrien } 46284261Sobrien if (retval == -1) 4631556Srgrimes retval = history(hist, &he, H_LAST); 4641556Srgrimes } else { 4651556Srgrimes retval = history(hist, &he, H_NEXT_EVENT, i); 4661556Srgrimes if (retval == -1) { 46784261Sobrien /* 4681556Srgrimes * the notion of first and last is 4691556Srgrimes * backwards to that of the history package 47084261Sobrien */ 4711556Srgrimes retval = history(hist, &he, last ? H_FIRST : H_LAST); 4721556Srgrimes } 4731556Srgrimes } 4741556Srgrimes if (retval == -1) 4758855Srgrimes error("history number %s not found (internal error)", 4761556Srgrimes str); 47784261Sobrien } else { 47884261Sobrien /* 4791556Srgrimes * pattern 4801556Srgrimes */ 48184261Sobrien retval = history(hist, &he, H_PREV_STR, str); 4821556Srgrimes if (retval == -1) 48325224Ssteve error("history pattern not found: %s", str); 48425224Ssteve } 48525224Ssteve return (he.num); 48625224Ssteve} 48790111Simp 48825224Ssteveint 48925224Sstevebindcmd(int argc, char **argv) 49025224Ssteve{ 49125224Ssteve 49225224Ssteve if (el == NULL) 49325224Ssteve error("line editing is disabled"); 49425224Ssteve return (el_parse(el, argc, (const char **)argv)); 495} 496 497#else 498#include "error.h" 499 500int 501histcmd(int argc, char **argv) 502{ 503 504 error("not compiled with history support"); 505 /*NOTREACHED*/ 506 return (0); 507} 508 509int 510bindcmd(int argc, char **argv) 511{ 512 513 error("not compiled with line editing support"); 514 return (0); 515} 516#endif 517