Annotation of src/bin/sh/histedit.c, Revision 1.16
1.16 ! christos 1: /* $NetBSD: histedit.c,v 1.15 1997/09/14 07:43:56 lukem Exp $ */
1.6 cgd 2:
1.1 jtc 3: /*-
4: * Copyright (c) 1993
5: * The Regents of the University of California. All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Kenneth Almquist.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the University of
21: * California, Berkeley and its contributors.
22: * 4. Neither the name of the University nor the names of its contributors
23: * may be used to endorse or promote products derived from this software
24: * without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36: * SUCH DAMAGE.
37: */
38:
1.14 christos 39: #include <sys/cdefs.h>
1.1 jtc 40: #ifndef lint
1.6 cgd 41: #if 0
1.8 christos 42: static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95";
1.6 cgd 43: #else
1.16 ! christos 44: __RCSID("$NetBSD: histedit.c,v 1.15 1997/09/14 07:43:56 lukem Exp $");
1.6 cgd 45: #endif
1.1 jtc 46: #endif /* not lint */
47:
48: #include <sys/param.h>
49: #include <paths.h>
50: #include <stdio.h>
1.2 jtc 51: #include <stdlib.h>
52: #include <unistd.h>
1.8 christos 53: /*
54: * Editline and history functions (and glue).
55: */
1.1 jtc 56: #include "shell.h"
57: #include "parser.h"
58: #include "var.h"
59: #include "options.h"
1.8 christos 60: #include "main.h"
1.4 cgd 61: #include "output.h"
1.1 jtc 62: #include "mystring.h"
1.12 christos 63: #ifndef SMALL
1.8 christos 64: #include "myhistedit.h"
1.1 jtc 65: #include "error.h"
1.5 cgd 66: #include "eval.h"
1.1 jtc 67: #include "memalloc.h"
68:
69: #define MAXHISTLOOPS 4 /* max recursions through fc */
70: #define DEFEDITOR "ed" /* default editor *should* be $EDITOR */
71:
72: History *hist; /* history cookie */
73: EditLine *el; /* editline cookie */
74: int displayhist;
75: static FILE *el_in, *el_out;
76:
77: STATIC char *fc_replace __P((const char *, char *, char *));
78:
79: /*
80: * Set history and editing status. Called whenever the status may
81: * have changed (figures out what to do).
82: */
1.5 cgd 83: void
1.10 christos 84: histedit()
1.4 cgd 85: {
1.1 jtc 86:
87: #define editing (Eflag || Vflag)
88:
89: if (iflag) {
90: if (!hist) {
91: /*
92: * turn history on
93: */
94: INTOFF;
95: hist = history_init();
96: INTON;
97:
98: if (hist != NULL)
1.9 christos 99: sethistsize(histsizeval());
1.1 jtc 100: else
101: out2str("sh: can't initialize history\n");
102: }
103: if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
104: /*
105: * turn editing on
106: */
107: INTOFF;
108: if (el_in == NULL)
109: el_in = fdopen(0, "r");
110: if (el_out == NULL)
111: el_out = fdopen(2, "w");
112: if (el_in == NULL || el_out == NULL)
113: goto bad;
114: el = el_init(arg0, el_in, el_out);
115: if (el != NULL) {
116: if (hist)
117: el_set(el, EL_HIST, history, hist);
118: el_set(el, EL_PROMPT, getprompt);
119: } else {
120: bad:
121: out2str("sh: can't initialize editing\n");
122: }
123: INTON;
124: } else if (!editing && el) {
125: INTOFF;
126: el_end(el);
127: el = NULL;
128: INTON;
129: }
130: if (el) {
131: if (Vflag)
132: el_set(el, EL_EDITOR, "vi");
133: else if (Eflag)
134: el_set(el, EL_EDITOR, "emacs");
135: }
136: } else {
137: INTOFF;
138: if (el) { /* no editing if not interactive */
139: el_end(el);
140: el = NULL;
141: }
142: if (hist) {
143: history_end(hist);
144: hist = NULL;
145: }
146: INTON;
147: }
148: }
149:
1.4 cgd 150:
151: void
1.9 christos 152: sethistsize(hs)
153: const char *hs;
1.8 christos 154: {
1.1 jtc 155: int histsize;
1.16 ! christos 156: HistEvent he;
1.1 jtc 157:
158: if (hist != NULL) {
1.10 christos 159: if (hs == NULL || *hs == '\0' ||
1.9 christos 160: (histsize = atoi(hs)) < 0)
1.1 jtc 161: histsize = 100;
1.16 ! christos 162: history(hist, &he, H_SETMAXSIZE, histsize);
1.1 jtc 163: }
1.13 christos 164: }
165:
166: void
167: setterm(term)
168: const char *term;
169: {
170: if (el != NULL && term != NULL)
171: if (el_set(el, EL_TERMINAL, term) != 0) {
172: outfmt(out2, "sh: Can't set terminal type %s\n", term);
173: outfmt(out2, "sh: Using dumb terminal settings.\n");
174: }
1.1 jtc 175: }
176:
177: /*
178: * This command is provided since POSIX decided to standardize
179: * the Korn shell fc command. Oh well...
180: */
1.4 cgd 181: int
1.1 jtc 182: histcmd(argc, argv)
1.4 cgd 183: int argc;
1.8 christos 184: char **argv;
1.1 jtc 185: {
186: extern char *optarg;
187: extern int optind, optopt, optreset;
188: int ch;
189: char *editor = NULL;
1.16 ! christos 190: HistEvent he;
1.1 jtc 191: int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
1.16 ! christos 192: int i, retval;
1.1 jtc 193: char *firststr, *laststr;
194: int first, last, direction;
195: char *pat = NULL, *repl; /* ksh "fc old=new" crap */
196: static int active = 0;
197: struct jmploc jmploc;
198: struct jmploc *volatile savehandler;
199: char editfile[MAXPATHLEN + 1];
200: FILE *efp;
1.8 christos 201: #ifdef __GNUC__
202: /* Avoid longjmp clobbering */
203: (void) &editor;
204: (void) &lflg;
205: (void) &nflg;
206: (void) &rflg;
207: (void) &sflg;
208: (void) &firststr;
209: (void) &laststr;
210: (void) &pat;
211: (void) &repl;
212: (void) &efp;
213: (void) &argc;
214: (void) &argv;
215: #endif
1.1 jtc 216:
217: if (hist == NULL)
218: error("history not active");
1.10 christos 219:
1.1 jtc 220: if (argc == 1)
221: error("missing history argument");
222:
223: optreset = 1; optind = 1; /* initialize getopt */
224: while (not_fcnumber(argv[optind]) &&
1.15 lukem 225: (ch = getopt(argc, argv, ":e:lnrs")) != -1)
1.1 jtc 226: switch ((char)ch) {
227: case 'e':
228: editor = optarg;
229: break;
230: case 'l':
231: lflg = 1;
232: break;
233: case 'n':
234: nflg = 1;
235: break;
236: case 'r':
237: rflg = 1;
238: break;
239: case 's':
240: sflg = 1;
241: break;
242: case ':':
243: error("option -%c expects argument", optopt);
244: case '?':
245: default:
246: error("unknown option: -%c", optopt);
247: }
248: argc -= optind, argv += optind;
249:
250: /*
251: * If executing...
252: */
253: if (lflg == 0 || editor || sflg) {
254: lflg = 0; /* ignore */
255: editfile[0] = '\0';
256: /*
257: * Catch interrupts to reset active counter and
258: * cleanup temp files.
259: */
260: if (setjmp(jmploc.loc)) {
261: active = 0;
262: if (*editfile)
263: unlink(editfile);
264: handler = savehandler;
265: longjmp(handler->loc, 1);
266: }
267: savehandler = handler;
268: handler = &jmploc;
269: if (++active > MAXHISTLOOPS) {
270: active = 0;
271: displayhist = 0;
272: error("called recursively too many times");
273: }
274: /*
275: * Set editor.
276: */
277: if (sflg == 0) {
278: if (editor == NULL &&
279: (editor = bltinlookup("FCEDIT", 1)) == NULL &&
280: (editor = bltinlookup("EDITOR", 1)) == NULL)
281: editor = DEFEDITOR;
282: if (editor[0] == '-' && editor[1] == '\0') {
283: sflg = 1; /* no edit */
284: editor = NULL;
285: }
286: }
287: }
288:
289: /*
290: * If executing, parse [old=new] now
291: */
1.10 christos 292: if (lflg == 0 && argc > 0 &&
1.1 jtc 293: ((repl = strchr(argv[0], '=')) != NULL)) {
294: pat = argv[0];
295: *repl++ = '\0';
296: argc--, argv++;
297: }
298: /*
299: * determine [first] and [last]
300: */
301: switch (argc) {
302: case 0:
303: firststr = lflg ? "-16" : "-1";
304: laststr = "-1";
305: break;
306: case 1:
307: firststr = argv[0];
308: laststr = lflg ? "-1" : argv[0];
309: break;
310: case 2:
311: firststr = argv[0];
312: laststr = argv[1];
313: break;
314: default:
315: error("too many args");
316: }
317: /*
318: * Turn into event numbers.
319: */
320: first = str_to_event(firststr, 0);
321: last = str_to_event(laststr, 1);
322:
323: if (rflg) {
324: i = last;
325: last = first;
326: first = i;
327: }
328: /*
329: * XXX - this should not depend on the event numbers
1.10 christos 330: * always increasing. Add sequence numbers or offset
1.1 jtc 331: * to the history element in next (diskbased) release.
332: */
333: direction = first < last ? H_PREV : H_NEXT;
334:
335: /*
336: * If editing, grab a temp file.
337: */
338: if (editor) {
339: int fd;
340: INTOFF; /* easier */
341: sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP);
342: if ((fd = mkstemp(editfile)) < 0)
343: error("can't create temporary file %s", editfile);
344: if ((efp = fdopen(fd, "w")) == NULL) {
345: close(fd);
1.10 christos 346: error("can't allocate stdio buffer for temp");
1.1 jtc 347: }
348: }
349:
350: /*
351: * Loop through selected history events. If listing or executing,
352: * do it now. Otherwise, put into temp file and call the editor
353: * after.
354: *
355: * The history interface needs rethinking, as the following
356: * convolutions will demonstrate.
357: */
1.16 ! christos 358: history(hist, &he, H_FIRST);
! 359: retval = history(hist, &he, H_NEXT_EVENT, first);
! 360: for (;retval != -1; retval = history(hist, &he, direction)) {
1.1 jtc 361: if (lflg) {
362: if (!nflg)
1.16 ! christos 363: out1fmt("%5d ", he.num);
! 364: out1str(he.str);
1.1 jtc 365: } else {
1.10 christos 366: char *s = pat ?
1.16 ! christos 367: fc_replace(he.str, pat, repl) : (char *)he.str;
1.1 jtc 368:
369: if (sflg) {
370: if (displayhist) {
371: out2str(s);
372: }
373: evalstring(s);
374: if (displayhist && hist) {
375: /*
1.10 christos 376: * XXX what about recursive and
1.1 jtc 377: * relative histnums.
378: */
1.16 ! christos 379: history(hist, &he, H_ENTER, s);
1.1 jtc 380: }
381: } else
382: fputs(s, efp);
383: }
384: /*
1.16 ! christos 385: * At end? (if we were to lose last, we'd sure be
1.1 jtc 386: * messed up).
387: */
1.16 ! christos 388: if (he.num == last)
1.1 jtc 389: break;
390: }
391: if (editor) {
392: char *editcmd;
393:
394: fclose(efp);
395: editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
396: sprintf(editcmd, "%s %s", editor, editfile);
397: evalstring(editcmd); /* XXX - should use no JC command */
398: INTON;
399: readcmdfile(editfile); /* XXX - should read back - quick tst */
400: unlink(editfile);
401: }
1.10 christos 402:
1.1 jtc 403: if (lflg == 0 && active > 0)
404: --active;
405: if (displayhist)
406: displayhist = 0;
1.8 christos 407: return 0;
1.1 jtc 408: }
409:
410: STATIC char *
411: fc_replace(s, p, r)
412: const char *s;
413: char *p, *r;
414: {
415: char *dest;
416: int plen = strlen(p);
417:
418: STARTSTACKSTR(dest);
419: while (*s) {
420: if (*s == *p && strncmp(s, p, plen) == 0) {
421: while (*r)
422: STPUTC(*r++, dest);
423: s += plen;
424: *p = '\0'; /* so no more matches */
425: } else
426: STPUTC(*s++, dest);
427: }
428: STACKSTRNUL(dest);
429: dest = grabstackstr(dest);
430:
431: return (dest);
432: }
433:
1.4 cgd 434: int
1.1 jtc 435: not_fcnumber(s)
436: char *s;
437: {
1.7 christos 438: if (s == NULL)
439: return 0;
1.1 jtc 440: if (*s == '-')
441: s++;
442: return (!is_number(s));
443: }
444:
1.4 cgd 445: int
1.1 jtc 446: str_to_event(str, last)
447: char *str;
448: int last;
449: {
1.16 ! christos 450: HistEvent he;
1.1 jtc 451: char *s = str;
452: int relative = 0;
1.16 ! christos 453: int i, retval;
1.1 jtc 454:
1.16 ! christos 455: retval = history(hist, &he, H_FIRST);
1.1 jtc 456: switch (*s) {
457: case '-':
458: relative = 1;
459: /*FALLTHROUGH*/
460: case '+':
461: s++;
462: }
463: if (is_number(s)) {
464: i = atoi(s);
465: if (relative) {
1.16 ! christos 466: while (retval != -1 && i--) {
! 467: retval = history(hist, &he, H_NEXT);
1.1 jtc 468: }
1.16 ! christos 469: if (retval == -1)
! 470: retval = history(hist, &he, H_LAST);
1.1 jtc 471: } else {
1.16 ! christos 472: retval = history(hist, &he, H_NEXT_EVENT, i);
! 473: if (retval == -1) {
1.1 jtc 474: /*
475: * the notion of first and last is
476: * backwards to that of the history package
477: */
1.16 ! christos 478: retval = history(hist, &he,
! 479: last ? H_FIRST : H_LAST);
1.1 jtc 480: }
481: }
1.16 ! christos 482: if (retval == -1)
1.1 jtc 483: error("history number %s not found (internal error)",
484: str);
485: } else {
486: /*
1.10 christos 487: * pattern
1.1 jtc 488: */
1.16 ! christos 489: retval = history(hist, &he, H_PREV_STR, str);
! 490: if (retval == -1)
1.1 jtc 491: error("history pattern not found: %s", str);
492: }
1.16 ! christos 493: return (he.num);
1.1 jtc 494: }
1.11 christos 495: #else
496: int
497: histcmd(argc, argv)
498: int argc;
499: char **argv;
500: {
501: error("not compiled with history support");
502: }
503: #endif
CVSweb <webmaster@jp.NetBSD.org>