4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / bin / sh / histedit.c
CommitLineData
3b2a15e8 1/*-
69edccc9
KB
2 * Copyright (c) 1993
3 * The Regents of the University of California. All rights reserved.
3b2a15e8
KB
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * %sccs.include.redist.c%
9 */
10
11#ifndef lint
69edccc9 12static char sccsid[] = "@(#)histedit.c 8.1 (Berkeley) %G%";
3b2a15e8
KB
13#endif /* not lint */
14
dc06984c
MT
15/*
16 * Editline and history functions (and glue).
17 */
f7280c66
MT
18#include <sys/param.h>
19#include <paths.h>
dc06984c 20#include <stdio.h>
f7280c66 21#include "shell.h"
dc06984c 22#include "parser.h"
f7280c66 23#include "var.h"
dc06984c 24#include "options.h"
f7280c66 25#include "mystring.h"
dc06984c 26#include "error.h"
dc06984c 27#include "histedit.h"
f7280c66
MT
28#include "memalloc.h"
29
30#define MAXHISTLOOPS 4 /* max recursions through fc */
31#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */
dc06984c
MT
32
33History *hist; /* history cookie */
34EditLine *el; /* editline cookie */
f7280c66 35int displayhist;
dc06984c
MT
36static FILE *el_in, *el_out;
37
f7280c66 38STATIC char *fc_replace __P((const char *, char *, char *));
dc06984c
MT
39
40/*
41 * Set history and editing status. Called whenever the status may
42 * have changed (figures out what to do).
43 */
44histedit() {
45
f7280c66
MT
46#define editing (Eflag || Vflag)
47
48 if (iflag) {
dc06984c
MT
49 if (!hist) {
50 /*
51 * turn history on
52 */
53 INTOFF;
54 hist = history_init();
55 INTON;
56
57 if (hist != NULL)
58 sethistsize();
59 else
60 out2str("sh: can't initialize history\n");
61 }
f7280c66 62 if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
dc06984c
MT
63 /*
64 * turn editing on
65 */
66 INTOFF;
67 if (el_in == NULL)
68 el_in = fdopen(0, "r");
69 if (el_out == NULL)
70 el_out = fdopen(2, "w");
71 if (el_in == NULL || el_out == NULL)
72 goto bad;
73 el = el_init(arg0, el_in, el_out);
74 if (el != NULL) {
75 if (hist)
f7280c66 76 el_set(el, EL_HIST, history, hist);
dc06984c
MT
77 el_set(el, EL_PROMPT, getprompt);
78 } else {
79bad:
80 out2str("sh: can't initialize editing\n");
81 }
82 INTON;
f7280c66 83 } else if (!editing && el) {
dc06984c
MT
84 INTOFF;
85 el_end(el);
86 el = NULL;
87 INTON;
88 }
f7280c66
MT
89 if (el) {
90 if (Vflag)
91 el_set(el, EL_EDITOR, "vi");
92 else if (Eflag)
93 el_set(el, EL_EDITOR, "emacs");
94 }
dc06984c
MT
95 } else {
96 INTOFF;
97 if (el) { /* no editing if not interactive */
98 el_end(el);
99 el = NULL;
100 }
101 if (hist) {
102 history_end(hist);
103 hist = NULL;
104 }
105 INTON;
106 }
107}
108
109sethistsize() {
110 char *cp;
111 int histsize;
112
113 if (hist != NULL) {
114 cp = lookupvar("HISTSIZE");
115 if (cp == NULL || *cp == '\0' ||
116 (histsize = atoi(cp)) < 0)
117 histsize = 100;
f7280c66
MT
118 history(hist, H_EVENT, histsize);
119 }
120}
121
122/*
123 * This command is provided since POSIX decided to standardize
124 * the Korn shell fc command. Oh well...
125 */
126histcmd(argc, argv)
127 char *argv[];
128{
129 extern char *optarg;
130 extern int optind, optopt, optreset;
131 int ch;
132 char *editor = NULL;
133 const HistEvent *he;
134 int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
135 int i;
136 char *firststr, *laststr;
137 int first, last, direction;
138 char *pat = NULL, *repl; /* ksh "fc old=new" crap */
139 static int active = 0;
140 struct jmploc jmploc;
141 struct jmploc *volatile savehandler;
142 char editfile[MAXPATHLEN + 1];
143 FILE *efp;
144
145 if (hist == NULL)
146 error("history not active");
c77caef5
KB
147
148 if (argc == 1)
149 error("missing history argument");
f7280c66
MT
150
151 optreset = 1; optind = 1; /* initialize getopt */
152 while (not_fcnumber(argv[optind]) &&
153 (ch = getopt(argc, argv, ":e:lnrs")) != EOF)
154 switch ((char)ch) {
155 case 'e':
156 editor = optarg;
157 break;
158 case 'l':
159 lflg = 1;
160 break;
161 case 'n':
162 nflg = 1;
163 break;
164 case 'r':
165 rflg = 1;
166 break;
167 case 's':
168 sflg = 1;
169 break;
170 case ':':
171 error("option -%c expects argument", optopt);
172 case '?':
173 default:
174 error("unknown option: -%c", optopt);
175 }
176 argc -= optind, argv += optind;
177
178 /*
179 * If executing...
180 */
181 if (lflg == 0 || editor || sflg) {
182 lflg = 0; /* ignore */
183 editfile[0] = '\0';
184 /*
185 * Catch interrupts to reset active counter and
186 * cleanup temp files.
187 */
188 if (setjmp(jmploc.loc)) {
189 active = 0;
190 if (*editfile)
191 unlink(editfile);
192 handler = savehandler;
193 longjmp(handler->loc, 1);
194 }
195 savehandler = handler;
196 handler = &jmploc;
197 if (++active > MAXHISTLOOPS) {
198 active = 0;
199 displayhist = 0;
200 error("called recursively too many times");
201 }
202 /*
203 * Set editor.
204 */
205 if (sflg == 0) {
206 if (editor == NULL &&
4fcca4ff
MT
207 (editor = bltinlookup("FCEDIT", 1)) == NULL &&
208 (editor = bltinlookup("EDITOR", 1)) == NULL)
f7280c66
MT
209 editor = DEFEDITOR;
210 if (editor[0] == '-' && editor[1] == '\0') {
211 sflg = 1; /* no edit */
212 editor = NULL;
213 }
214 }
215 }
216
217 /*
218 * If executing, parse [old=new] now
219 */
220 if (lflg == 0 && argc > 0 &&
221 ((repl = strchr(argv[0], '=')) != NULL)) {
222 pat = argv[0];
223 *repl++ = '\0';
224 argc--, argv++;
225 }
226 /*
227 * determine [first] and [last]
228 */
229 switch (argc) {
230 case 0:
231 firststr = lflg ? "-16" : "-1";
232 laststr = "-1";
233 break;
234 case 1:
235 firststr = argv[0];
236 laststr = lflg ? "-1" : argv[0];
237 break;
238 case 2:
239 firststr = argv[0];
240 laststr = argv[1];
241 break;
242 default:
243 error("too many args");
244 }
245 /*
246 * Turn into event numbers.
247 */
248 first = str_to_event(firststr, 0);
249 last = str_to_event(laststr, 1);
250
251 if (rflg) {
252 i = last;
253 last = first;
254 first = i;
255 }
256 /*
257 * XXX - this should not depend on the event numbers
258 * always increasing. Add sequence numbers or offset
259 * to the history element in next (diskbased) release.
260 */
261 direction = first < last ? H_PREV : H_NEXT;
262
263 /*
264 * If editing, grab a temp file.
265 */
266 if (editor) {
267 int fd;
268 INTOFF; /* easier */
269 sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP);
270 if ((fd = mkstemp(editfile)) < 0)
271 error("can't create temporary file %s", editfile);
272 if ((efp = fdopen(fd, "w")) == NULL) {
273 close(fd);
274 error("can't allocate stdio buffer for temp\n");
275 }
276 }
277
278 /*
279 * Loop through selected history events. If listing or executing,
280 * do it now. Otherwise, put into temp file and call the editor
281 * after.
282 *
283 * The history interface needs rethinking, as the following
284 * convolutions will demonstrate.
285 */
286 history(hist, H_FIRST);
287 he = history(hist, H_NEXT_EVENT, first);
288 for (;he != NULL; he = history(hist, direction)) {
289 if (lflg) {
290 if (!nflg)
291 out1fmt("%5d ", he->num);
292 out1str(he->str);
293 } else {
294 char *s = pat ?
295 fc_replace(he->str, pat, repl) : (char *)he->str;
296
297 if (sflg) {
298 if (displayhist) {
299 out2str(s);
300 }
301 evalstring(s);
302 if (displayhist && hist) {
303 /*
304 * XXX what about recursive and
305 * relative histnums.
306 */
307 history(hist, H_ENTER, s);
308 }
309 } else
310 fputs(s, efp);
311 }
312 /*
313 * At end? (if we were to loose last, we'd sure be
314 * messed up).
315 */
316 if (he->num == last)
317 break;
318 }
319 if (editor) {
320 char *editcmd;
321
322 fclose(efp);
323 editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
324 sprintf(editcmd, "%s %s", editor, editfile);
325 evalstring(editcmd); /* XXX - should use no JC command */
326 INTON;
327 readcmdfile(editfile); /* XXX - should read back - quick tst */
328 unlink(editfile);
329 }
330
331 if (lflg == 0 && active > 0)
332 --active;
333 if (displayhist)
334 displayhist = 0;
335}
336
337STATIC char *
338fc_replace(s, p, r)
339 const char *s;
340 char *p, *r;
341{
342 char *dest;
343 int plen = strlen(p);
344
345 STARTSTACKSTR(dest);
346 while (*s) {
347 if (*s == *p && strncmp(s, p, plen) == 0) {
348 while (*r)
349 STPUTC(*r++, dest);
350 s += plen;
351 *p = '\0'; /* so no more matches */
352 } else
353 STPUTC(*s++, dest);
354 }
355 STACKSTRNUL(dest);
356 dest = grabstackstr(dest);
357
358 return (dest);
359}
360
361not_fcnumber(s)
362 char *s;
363{
364 if (*s == '-')
365 s++;
366 return (!is_number(s));
367}
368
369str_to_event(str, last)
370 char *str;
371 int last;
372{
373 const HistEvent *he;
374 char *s = str;
375 int relative = 0;
376 int i, j;
377
378 he = history(hist, H_FIRST);
379 switch (*s) {
380 case '-':
381 relative = 1;
382 /*FALLTHROUGH*/
383 case '+':
384 s++;
385 }
386 if (is_number(s)) {
387 i = atoi(s);
388 if (relative) {
389 while (he != NULL && i--) {
390 he = history(hist, H_NEXT);
391 }
392 if (he == NULL)
393 he = history(hist, H_LAST);
394 } else {
395 he = history(hist, H_NEXT_EVENT, i);
396 if (he == NULL) {
397 /*
398 * the notion of first and last is
399 * backwards to that of the history package
400 */
401 he = history(hist, last ? H_FIRST : H_LAST);
402 }
403 }
404 if (he == NULL)
405 error("history number %s not found (internal error)",
406 str);
407 } else {
408 /*
409 * pattern
410 */
ba8ff910 411 he = history(hist, H_PREV_STR, str);
f7280c66
MT
412 if (he == NULL)
413 error("history pattern not found: %s", str);
dc06984c 414 }
f7280c66 415 return (he->num);
dc06984c 416}