VirtualBox

source: kBuild/trunk/src/ash/histedit.c@ 1158

Last change on this file since 1158 was 809, checked in by bird, 18 years ago

Solaris + cleanup.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette