3
* The Regents of the University of California. All rights reserved.
4
* Copyright (c) 1997-2005
5
* Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
7
* This code is derived from software contributed to Berkeley by
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
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.
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
35
#include <sys/param.h>
41
* Editline and history functions (and glue).
52
#include "myhistedit.h"
56
#define MAXHISTLOOPS 4 /* max recursions through fc */
57
#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */
59
History *hist; /* history cookie */
60
EditLine *el; /* editline cookie */
62
static FILE *el_in, *el_out;
64
STATIC const char *fc_replace(const char *, char *, char *);
67
extern FILE *tracefile;
71
* Set history and editing status. Called whenever the status may
72
* have changed (figures out what to do).
79
#define editing (Eflag || Vflag)
87
hist = history_init();
91
sethistsize(histsizeval());
93
out2str("sh: can't initialize history\n");
95
if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
101
el_in = fdopen(0, "r");
103
el_out = fdopen(2, "w");
104
if (el_in == NULL || el_out == NULL)
111
el = el_init(arg0, el_in, el_out, el_err);
114
el_set(el, EL_HIST, history, hist);
115
el_set(el, EL_PROMPT, getprompt);
118
out2str("sh: can't initialize editing\n");
121
} else if (!editing && el) {
129
el_set(el, EL_EDITOR, "vi");
131
el_set(el, EL_EDITOR, "emacs");
136
if (el) { /* no editing if not interactive */
150
sethistsize(const char *hs)
156
if (hs == NULL || *hs == '\0' ||
157
(histsize = atoi(hs)) < 0)
159
history(hist, &he, H_SETSIZE, histsize);
164
setterm(const char *term)
166
if (el != NULL && term != NULL)
167
if (el_set(el, EL_TERMINAL, term) != 0) {
168
outfmt(out2, "sh: Can't set terminal type %s\n", term);
169
outfmt(out2, "sh: Using dumb terminal settings.\n");
174
* This command is provided since POSIX decided to standardize
175
* the Korn shell fc command. Oh well...
178
histcmd(int argc, char **argv)
181
const char *editor = NULL;
183
int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
185
const char *firststr, *laststr;
186
int first, last, direction;
187
char *pat = NULL, *repl; /* ksh "fc old=new" crap */
188
static int active = 0;
189
struct jmploc jmploc;
190
struct jmploc *volatile savehandler;
191
char editfile[MAXPATHLEN + 1];
194
/* Avoid longjmp clobbering */
210
sh_error("history not active");
213
sh_error("missing history argument");
218
optreset = 1; optind = 1; /* initialize getopt */
220
while (not_fcnumber(argv[optind]) &&
221
(ch = getopt(argc, argv, ":e:lnrs")) != -1)
239
sh_error("option -%c expects argument", optopt);
243
sh_error("unknown option: -%c", optopt);
246
argc -= optind, argv += optind;
251
if (lflg == 0 || editor || sflg) {
252
lflg = 0; /* ignore */
255
* Catch interrupts to reset active counter and
256
* cleanup temp files.
258
if (setjmp(jmploc.loc)) {
262
handler = savehandler;
263
longjmp(handler->loc, 1);
265
savehandler = handler;
267
if (++active > MAXHISTLOOPS) {
270
sh_error("called recursively too many times");
276
if (editor == NULL &&
277
(editor = bltinlookup("FCEDIT")) == NULL &&
278
(editor = bltinlookup("EDITOR")) == NULL)
280
if (editor[0] == '-' && editor[1] == '\0') {
281
sflg = 1; /* no edit */
288
* If executing, parse [old=new] now
290
if (lflg == 0 && argc > 0 &&
291
((repl = strchr(argv[0], '=')) != NULL)) {
297
* determine [first] and [last]
301
firststr = lflg ? "-16" : "-1";
306
laststr = lflg ? "-1" : argv[0];
313
sh_error("too many args");
317
* Turn into event numbers.
319
first = str_to_event(firststr, 0);
320
last = str_to_event(laststr, 1);
328
* XXX - this should not depend on the event numbers
329
* always increasing. Add sequence numbers or offset
330
* to the history element in next (diskbased) release.
332
direction = first < last ? H_PREV : H_NEXT;
335
* If editing, grab a temp file.
340
sprintf(editfile, "%s_shXXXXXX", _PATH_TMP);
341
if ((fd = mkstemp(editfile)) < 0)
342
sh_error("can't create temporary file %s", editfile);
343
if ((efp = fdopen(fd, "w")) == NULL) {
345
sh_error("can't allocate stdio buffer for temp");
350
* Loop through selected history events. If listing or executing,
351
* do it now. Otherwise, put into temp file and call the editor
354
* The history interface needs rethinking, as the following
355
* convolutions will demonstrate.
357
history(hist, &he, H_FIRST);
358
retval = history(hist, &he, H_NEXT_EVENT, first);
359
for (;retval != -1; retval = history(hist, &he, direction)) {
362
out1fmt("%5d ", he.num);
365
const char *s = pat ?
366
fc_replace(he.str, pat, repl) : he.str;
373
evalstring(strcpy(stalloc(strlen(s) + 1), s),
375
if (displayhist && hist) {
377
* XXX what about recursive and
380
history(hist, &he, H_ENTER, s);
386
* At end? (if we were to lose last, we'd sure be
396
editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
397
sprintf(editcmd, "%s %s", editor, editfile);
398
/* XXX - should use no JC command */
399
evalstring(editcmd, ~0);
401
readcmdfile(editfile); /* XXX - should read back - quick tst */
405
if (lflg == 0 && active > 0)
413
fc_replace(const char *s, char *p, char *r)
416
int plen = strlen(p);
420
if (*s == *p && strncmp(s, p, plen) == 0) {
424
*p = '\0'; /* so no more matches */
429
dest = grabstackstr(dest);
435
not_fcnumber(char *s)
441
return (!is_number(s));
445
str_to_event(const char *str, int last)
452
retval = history(hist, &he, H_FIRST);
463
while (retval != -1 && i--) {
464
retval = history(hist, &he, H_NEXT);
467
retval = history(hist, &he, H_LAST);
469
retval = history(hist, &he, H_NEXT_EVENT, i);
472
* the notion of first and last is
473
* backwards to that of the history package
475
retval = history(hist, &he,
476
last ? H_FIRST : H_LAST);
480
sh_error("history number %s not found (internal error)",
486
retval = history(hist, &he, H_PREV_STR, str);
488
sh_error("history pattern not found: %s", str);