histedit.c revision 100663
1121159Sbmah/*-
2121159Sbmah * Copyright (c) 1993
3121159Sbmah *	The Regents of the University of California.  All rights reserved.
4121159Sbmah *
5121159Sbmah * This code is derived from software contributed to Berkeley by
6121159Sbmah * Kenneth Almquist.
7121159Sbmah *
8121159Sbmah * Redistribution and use in source and binary forms, with or without
9121159Sbmah * modification, are permitted provided that the following conditions
10121159Sbmah * are met:
11121159Sbmah * 1. Redistributions of source code must retain the above copyright
12121159Sbmah *    notice, this list of conditions and the following disclaimer.
13121159Sbmah * 2. Redistributions in binary form must reproduce the above copyright
14121159Sbmah *    notice, this list of conditions and the following disclaimer in the
15121159Sbmah *    documentation and/or other materials provided with the distribution.
16121159Sbmah * 3. All advertising materials mentioning features or use of this software
17121159Sbmah *    must display the following acknowledgement:
18121159Sbmah *	This product includes software developed by the University of
19121159Sbmah *	California, Berkeley and its contributors.
20121159Sbmah * 4. Neither the name of the University nor the names of its contributors
21121159Sbmah *    may be used to endorse or promote products derived from this software
22121159Sbmah *    without specific prior written permission.
23121159Sbmah *
24121159Sbmah * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25121159Sbmah * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26121159Sbmah * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27121159Sbmah * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28192923Sbms * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29121159Sbmah * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30121159Sbmah * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31121159Sbmah * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32121159Sbmah * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33121159Sbmah * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34121159Sbmah * SUCH DAMAGE.
35121159Sbmah */
36121159Sbmah
37121159Sbmah#ifndef lint
38121159Sbmah#if 0
39121159Sbmahstatic char sccsid[] = "@(#)histedit.c	8.2 (Berkeley) 5/4/95";
40121159Sbmah#endif
41121159Sbmah#endif /* not lint */
42121159Sbmah#include <sys/cdefs.h>
43121159Sbmah__FBSDID("$FreeBSD: head/bin/sh/histedit.c 100663 2002-07-25 10:47:38Z tjr $");
44121159Sbmah
45121159Sbmah#include <sys/param.h>
46121159Sbmah#include <limits.h>
47121159Sbmah#include <paths.h>
48121159Sbmah#include <stdio.h>
49121159Sbmah#include <stdlib.h>
50121159Sbmah#include <unistd.h>
51121159Sbmah/*
52121159Sbmah * Editline and history functions (and glue).
53121159Sbmah */
54121159Sbmah#include "shell.h"
55121159Sbmah#include "parser.h"
56121159Sbmah#include "var.h"
57121159Sbmah#include "options.h"
58121159Sbmah#include "main.h"
59121159Sbmah#include "output.h"
60121159Sbmah#include "mystring.h"
61121159Sbmah#ifndef NO_HISTORY
62121159Sbmah#include "myhistedit.h"
63121159Sbmah#include "error.h"
64167676Sbms#include "eval.h"
65121159Sbmah#include "memalloc.h"
66167676Sbms
67167676Sbms#define MAXHISTLOOPS	4	/* max recursions through fc */
68167676Sbms#define DEFEDITOR	"ed"	/* default editor *should* be $EDITOR */
69167676Sbms
70167676SbmsHistory *hist;	/* history cookie */
71167676SbmsEditLine *el;	/* editline cookie */
72121159Sbmahint displayhist;
73121159Sbmahstatic FILE *el_in, *el_out, *el_err;
74121159Sbmah
75121159SbmahSTATIC char *fc_replace(const char *, char *, char *);
76121159Sbmah
77121159Sbmah/*
78121159Sbmah * Set history and editing status.  Called whenever the status may
79121159Sbmah * have changed (figures out what to do).
80121159Sbmah */
81121159Sbmahvoid
82121159Sbmahhistedit(void)
83121159Sbmah{
84121159Sbmah
85121159Sbmah#define editing (Eflag || Vflag)
86121159Sbmah
87121159Sbmah	if (iflag) {
88121159Sbmah		if (!hist) {
89121159Sbmah			/*
90121159Sbmah			 * turn history on
91121159Sbmah			 */
92121159Sbmah			INTOFF;
93121159Sbmah			hist = history_init();
94121159Sbmah			INTON;
95121159Sbmah
96121159Sbmah			if (hist != NULL)
97121159Sbmah				sethistsize(histsizeval());
98121159Sbmah			else
99121159Sbmah				out2str("sh: can't initialize history\n");
100121159Sbmah		}
101121159Sbmah		if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
102121159Sbmah			/*
103121159Sbmah			 * turn editing on
104121159Sbmah			 */
105121159Sbmah			INTOFF;
106121159Sbmah			if (el_in == NULL)
107121159Sbmah				el_in = fdopen(0, "r");
108121159Sbmah			if (el_err == NULL)
109121159Sbmah				el_err = fdopen(1, "w");
110131864Sru			if (el_out == NULL)
111131864Sru				el_out = fdopen(2, "w");
112131864Sru			if (el_in == NULL || el_err == NULL || el_out == NULL)
113131864Sru				goto bad;
114131864Sru			el = el_init(arg0, el_in, el_out, el_err);
115121159Sbmah			if (el != NULL) {
116131864Sru				if (hist)
117131864Sru					el_set(el, EL_HIST, history, hist);
118131864Sru				el_set(el, EL_PROMPT, getprompt);
119121159Sbmah			} else {
120131864Srubad:
121131864Sru				out2str("sh: can't initialize editing\n");
122131864Sru			}
123121159Sbmah			INTON;
124121159Sbmah		} else if (!editing && el) {
125121159Sbmah			INTOFF;
126121159Sbmah			el_end(el);
127121159Sbmah			el = NULL;
128121159Sbmah			INTON;
129121159Sbmah		}
130121159Sbmah		if (el) {
131121159Sbmah			if (Vflag)
132121159Sbmah				el_set(el, EL_EDITOR, "vi");
133121159Sbmah			else if (Eflag)
134121159Sbmah				el_set(el, EL_EDITOR, "emacs");
135121159Sbmah			el_source(el, NULL);
136121159Sbmah		}
137121159Sbmah	} else {
138121159Sbmah		INTOFF;
139121159Sbmah		if (el) {	/* no editing if not interactive */
140121159Sbmah			el_end(el);
141121159Sbmah			el = NULL;
142121159Sbmah		}
143121159Sbmah		if (hist) {
144121159Sbmah			history_end(hist);
145121159Sbmah			hist = NULL;
146121159Sbmah		}
147121159Sbmah		INTON;
148121159Sbmah	}
149121159Sbmah}
150121159Sbmah
151121159Sbmah
152121159Sbmahvoid
153121159Sbmahsethistsize(hs)
154121159Sbmah	const char *hs;
155121159Sbmah{
156121159Sbmah	int histsize;
157121159Sbmah	HistEvent he;
158121159Sbmah
159121159Sbmah	if (hist != NULL) {
160121159Sbmah		if (hs == NULL || *hs == '\0' ||
161121159Sbmah		   (histsize = atoi(hs)) < 0)
162121159Sbmah			histsize = 100;
163121159Sbmah		history(hist, &he, H_EVENT, histsize);
164166973Sbms	}
165121159Sbmah}
166121159Sbmah
167121159Sbmah/*
168121159Sbmah *  This command is provided since POSIX decided to standardize
169121159Sbmah *  the Korn shell fc command.  Oh well...
170121159Sbmah */
171131864Sruint
172121159Sbmahhistcmd(int argc, char **argv)
173121159Sbmah{
174131864Sru	int ch;
175121159Sbmah	char *editor = NULL;
176131864Sru	HistEvent he;
177131864Sru	int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
178131864Sru	int i, retval;
179121159Sbmah	char *firststr, *laststr;
180166973Sbms	int first, last, direction;
181166973Sbms	char *pat = NULL, *repl;	/* ksh "fc old=new" crap */
182166973Sbms	static int active = 0;
183166973Sbms	struct jmploc jmploc;
184166973Sbms	struct jmploc *volatile savehandler;
185166973Sbms	char editfile[PATH_MAX];
186166973Sbms	FILE *efp;
187166973Sbms	int oldhistnum;
188166973Sbms#ifdef __GNUC__
189166973Sbms	/* Avoid longjmp clobbering */
190166973Sbms	(void) &editor;
191131864Sru	(void) &lflg;
192121159Sbmah	(void) &nflg;
193121159Sbmah	(void) &rflg;
194121159Sbmah	(void) &sflg;
195166973Sbms	(void) &firststr;
196121159Sbmah	(void) &laststr;
197131864Sru	(void) &pat;
198166973Sbms	(void) &repl;
199166973Sbms	(void) &efp;
200166973Sbms	(void) &argc;
201166973Sbms	(void) &argv;
202166973Sbms#endif
203166973Sbms
204166973Sbms	if (hist == NULL)
205166973Sbms		error("history not active");
206131472Sru
207131864Sru	if (argc == 1)
208121159Sbmah		error("missing history argument");
209121159Sbmah
210131864Sru	optreset = 1; optind = 1; /* initialize getopt */
211121159Sbmah	opterr = 0;
212121159Sbmah	while (not_fcnumber(argv[optind]) &&
213121159Sbmah	      (ch = getopt(argc, argv, ":e:lnrs")) != -1)
214121159Sbmah		switch ((char)ch) {
215121159Sbmah		case 'e':
216121159Sbmah			editor = optarg;
217121159Sbmah			break;
218121159Sbmah		case 'l':
219121159Sbmah			lflg = 1;
220121159Sbmah			break;
221121159Sbmah		case 'n':
222121159Sbmah			nflg = 1;
223121159Sbmah			break;
224121159Sbmah		case 'r':
225131864Sru			rflg = 1;
226121159Sbmah			break;
227121159Sbmah		case 's':
228131864Sru			sflg = 1;
229121159Sbmah			break;
230131864Sru		case ':':
231131864Sru			error("option -%c expects argument", optopt);
232131864Sru		case '?':
233121159Sbmah		default:
234131864Sru			error("unknown option: -%c", optopt);
235121159Sbmah		}
236121159Sbmah	argc -= optind, argv += optind;
237121159Sbmah
238121159Sbmah	/*
239121159Sbmah	 * If executing...
240121159Sbmah	 */
241121159Sbmah	if (lflg == 0 || editor || sflg) {
242121159Sbmah		lflg = 0;	/* ignore */
243121159Sbmah		editfile[0] = '\0';
244121159Sbmah		/*
245121159Sbmah		 * Catch interrupts to reset active counter and
246121159Sbmah		 * cleanup temp files.
247121159Sbmah		 */
248121159Sbmah		if (setjmp(jmploc.loc)) {
249121159Sbmah			active = 0;
250121159Sbmah			if (*editfile)
251121159Sbmah				unlink(editfile);
252121159Sbmah			handler = savehandler;
253121159Sbmah			longjmp(handler->loc, 1);
254121159Sbmah		}
255121159Sbmah		savehandler = handler;
256131864Sru		handler = &jmploc;
257121159Sbmah		if (++active > MAXHISTLOOPS) {
258131864Sru			active = 0;
259121159Sbmah			displayhist = 0;
260131864Sru			error("called recursively too many times");
261131864Sru		}
262131864Sru		/*
263131864Sru		 * Set editor.
264131864Sru		 */
265121159Sbmah		if (sflg == 0) {
266121159Sbmah			if (editor == NULL &&
267131864Sru			    (editor = bltinlookup("FCEDIT", 1)) == NULL &&
268121159Sbmah			    (editor = bltinlookup("EDITOR", 1)) == NULL)
269131864Sru				editor = DEFEDITOR;
270121159Sbmah			if (editor[0] == '-' && editor[1] == '\0') {
271121159Sbmah				sflg = 1;	/* no edit */
272131864Sru				editor = NULL;
273131864Sru			}
274131864Sru		}
275131864Sru	}
276131864Sru
277121159Sbmah	/*
278121159Sbmah	 * If executing, parse [old=new] now
279131864Sru	 */
280121159Sbmah	if (lflg == 0 && argc > 0 &&
281131864Sru	     ((repl = strchr(argv[0], '=')) != NULL)) {
282121159Sbmah		pat = argv[0];
283121159Sbmah		*repl++ = '\0';
284121159Sbmah		argc--, argv++;
285131864Sru	}
286121159Sbmah	/*
287131864Sru	 * determine [first] and [last]
288121159Sbmah	 */
289131864Sru	switch (argc) {
290121159Sbmah	case 0:
291131864Sru		firststr = lflg ? "-16" : "-1";
292121159Sbmah		laststr = "-1";
293121159Sbmah		break;
294121159Sbmah	case 1:
295121159Sbmah		firststr = argv[0];
296121159Sbmah		laststr = lflg ? "-1" : argv[0];
297131864Sru		break;
298121159Sbmah	case 2:
299131864Sru		firststr = argv[0];
300121159Sbmah		laststr = argv[1];
301121159Sbmah		break;
302121159Sbmah	default:
303121159Sbmah		error("too many args");
304121159Sbmah	}
305121159Sbmah	/*
306131864Sru	 * Turn into event numbers.
307121159Sbmah	 */
308121159Sbmah	first = str_to_event(firststr, 0);
309121159Sbmah	last = str_to_event(laststr, 1);
310121159Sbmah
311121159Sbmah	if (rflg) {
312121159Sbmah		i = last;
313121159Sbmah		last = first;
314121159Sbmah		first = i;
315121159Sbmah	}
316121159Sbmah	/*
317121159Sbmah	 * XXX - this should not depend on the event numbers
318121159Sbmah	 * always increasing.  Add sequence numbers or offset
319121159Sbmah	 * to the history element in next (diskbased) release.
320121159Sbmah	 */
321121159Sbmah	direction = first < last ? H_PREV : H_NEXT;
322121159Sbmah
323121159Sbmah	/*
324121159Sbmah	 * If editing, grab a temp file.
325121159Sbmah	 */
326121159Sbmah	if (editor) {
327121159Sbmah		int fd;
328121159Sbmah		INTOFF;		/* easier */
329261876Sbrueffer		sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP);
330121159Sbmah		if ((fd = mkstemp(editfile)) < 0)
331121159Sbmah			error("can't create temporary file %s", editfile);
332121159Sbmah		if ((efp = fdopen(fd, "w")) == NULL) {
333121159Sbmah			close(fd);
334131864Sru			error("can't allocate stdio buffer for temp");
335121159Sbmah		}
336131864Sru	}
337121159Sbmah
338121159Sbmah	/*
339121159Sbmah	 * Loop through selected history events.  If listing or executing,
340131864Sru	 * do it now.  Otherwise, put into temp file and call the editor
341121159Sbmah	 * after.
342121159Sbmah	 *
343121159Sbmah	 * The history interface needs rethinking, as the following
344131864Sru	 * convolutions will demonstrate.
345121159Sbmah	 */
346121159Sbmah	history(hist, &he, H_FIRST);
347121159Sbmah	retval = history(hist, &he, H_NEXT_EVENT, first);
348121159Sbmah	for (;retval != -1; retval = history(hist, &he, direction)) {
349121159Sbmah		if (lflg) {
350121159Sbmah			if (!nflg)
351121159Sbmah				out1fmt("%5d ", he.num);
352131864Sru			out1str(he.str);
353121159Sbmah		} else {
354121159Sbmah			char *s = pat ?
355121159Sbmah			   fc_replace(he.str, pat, repl) : (char *)he.str;
356121159Sbmah
357121159Sbmah			if (sflg) {
358121159Sbmah				if (displayhist) {
359121159Sbmah					out2str(s);
360121159Sbmah				}
361121159Sbmah				evalstring(s);
362121159Sbmah				if (displayhist && hist) {
363121159Sbmah					/*
364121159Sbmah					 *  XXX what about recursive and
365121159Sbmah					 *  relative histnums.
366121159Sbmah					 */
367121159Sbmah					oldhistnum = he.num;
368261876Sbrueffer					history(hist, &he, H_ENTER, s);
369121159Sbmah					/*
370121159Sbmah					 * XXX H_ENTER moves the internal
371121159Sbmah					 * cursor, set it back to the current
372121159Sbmah					 * entry.
373121159Sbmah					 */
374121159Sbmah					retval = history(hist, &he,
375121159Sbmah					    H_NEXT_EVENT, oldhistnum);
376121159Sbmah				}
377121159Sbmah			} else
378121159Sbmah				fputs(s, efp);
379121159Sbmah		}
380121159Sbmah		/*
381121159Sbmah		 * At end?  (if we were to loose last, we'd sure be
382121159Sbmah		 * messed up).
383121159Sbmah		 */
384121159Sbmah		if (he.num == last)
385121159Sbmah			break;
386121159Sbmah	}
387121159Sbmah	if (editor) {
388121159Sbmah		char *editcmd;
389121159Sbmah
390121159Sbmah		fclose(efp);
391121159Sbmah		editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
392121159Sbmah		sprintf(editcmd, "%s %s", editor, editfile);
393121159Sbmah		evalstring(editcmd);	/* XXX - should use no JC command */
394121159Sbmah		INTON;
395121159Sbmah		readcmdfile(editfile);	/* XXX - should read back - quick tst */
396121159Sbmah		unlink(editfile);
397121159Sbmah	}
398121159Sbmah
399121159Sbmah	if (lflg == 0 && active > 0)
400121159Sbmah		--active;
401121159Sbmah	if (displayhist)
402121159Sbmah		displayhist = 0;
403121159Sbmah	return 0;
404121159Sbmah}
405121159Sbmah
406121159SbmahSTATIC char *
407121159Sbmahfc_replace(const char *s, char *p, char *r)
408121159Sbmah{
409121159Sbmah	char *dest;
410121159Sbmah	int plen = strlen(p);
411121159Sbmah
412121159Sbmah	STARTSTACKSTR(dest);
413121159Sbmah	while (*s) {
414121159Sbmah		if (*s == *p && strncmp(s, p, plen) == 0) {
415131472Sru			while (*r)
416121159Sbmah				STPUTC(*r++, dest);
417121159Sbmah			s += plen;
418121159Sbmah			*p = '\0';	/* so no more matches */
419121159Sbmah		} else
420121159Sbmah			STPUTC(*s++, dest);
421121159Sbmah	}
422121159Sbmah	STACKSTRNUL(dest);
423121159Sbmah	dest = grabstackstr(dest);
424121159Sbmah
425121159Sbmah	return (dest);
426121159Sbmah}
427121159Sbmah
428121159Sbmahint
429121159Sbmahnot_fcnumber(char *s)
430121159Sbmah{
431131864Sru	if (s == NULL)
432121159Sbmah		return (0);
433121159Sbmah	if (*s == '-')
434121159Sbmah		s++;
435121159Sbmah	return (!is_number(s));
436121159Sbmah}
437121159Sbmah
438121159Sbmahint
439121159Sbmahstr_to_event(char *str, int last)
440121159Sbmah{
441121159Sbmah	HistEvent he;
442121159Sbmah	char *s = str;
443131864Sru	int relative = 0;
444131864Sru	int i, retval;
445131864Sru
446131864Sru	retval = history(hist, &he, H_FIRST);
447121159Sbmah	switch (*s) {
448121159Sbmah	case '-':
449131864Sru		relative = 1;
450131864Sru		/*FALLTHROUGH*/
451131864Sru	case '+':
452121159Sbmah		s++;
453121159Sbmah	}
454121159Sbmah	if (is_number(s)) {
455121159Sbmah		i = atoi(s);
456131864Sru		if (relative) {
457131864Sru			while (retval != -1 && i--) {
458131864Sru				retval = history(hist, &he, H_NEXT);
459131864Sru			}
460131864Sru			if (retval == -1)
461131864Sru				retval = history(hist, &he, H_LAST);
462131864Sru		} else {
463121159Sbmah			retval = history(hist, &he, H_NEXT_EVENT, i);
464131864Sru			if (retval == -1) {
465121159Sbmah				/*
466121159Sbmah				 * the notion of first and last is
467121159Sbmah				 * backwards to that of the history package
468121159Sbmah				 */
469121159Sbmah				retval = history(hist, &he, last ? H_FIRST : H_LAST);
470121159Sbmah			}
471121159Sbmah		}
472131864Sru		if (retval == -1)
473121159Sbmah			error("history number %s not found (internal error)",
474131864Sru			       str);
475131864Sru	} else {
476131864Sru		/*
477121159Sbmah		 * pattern
478131864Sru		 */
479121159Sbmah		retval = history(hist, &he, H_PREV_STR, str);
480131864Sru		if (retval == -1)
481121159Sbmah			error("history pattern not found: %s", str);
482131864Sru	}
483121159Sbmah	return (he.num);
484131864Sru}
485131864Sru
486131864Sruint
487131864Srubindcmd(int argc, char **argv)
488131864Sru{
489121159Sbmah
490121159Sbmah	if (el == NULL)
491121159Sbmah		error("line editing is disabled");
492121159Sbmah	return (el_parse(el, argc, argv));
493121159Sbmah}
494121159Sbmah
495121159Sbmah#else
496121159Sbmah#include "error.h"
497121159Sbmah
498121159Sbmahint
499121159Sbmahhistcmd(int argc, char **argv)
500121159Sbmah{
501121159Sbmah
502121159Sbmah	error("not compiled with history support");
503131864Sru	/*NOTREACHED*/
504131864Sru	return (0);
505131864Sru}
506121159Sbmah
507121159Sbmahint
508121159Sbmahbindcmd(int argc, char **argv)
509131864Sru{
510121159Sbmah
511121159Sbmah	error("not compiled with line editing support");
512121159Sbmah	return (0);
513121159Sbmah}
514121159Sbmah#endif
515121159Sbmah