histedit.c revision 1.73 1 /* $NetBSD: histedit.c,v 1.73 2024/08/03 03:46:23 kre 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 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: histedit.c,v 1.73 2024/08/03 03:46:23 kre Exp $");
41 #endif
42 #endif /* not lint */
43
44 #include <sys/param.h>
45 #include <sys/stat.h>
46 #include <dirent.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <paths.h>
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 "builtins.h"
61 #include "main.h"
62 #include "output.h"
63 #include "mystring.h"
64 #include "myhistedit.h"
65 #include "error.h"
66 #include "alias.h"
67 #include "redir.h"
68
69 #ifndef SMALL /* almost all the rest of this file */
70
71 #include "eval.h"
72 #include "memalloc.h"
73 #include "show.h"
74
75 #define MAXHISTLOOPS 4 /* max recursions through fc */
76 #define DEFEDITOR "ed" /* default editor *should* be $EDITOR */
77
78 History *hist; /* history cookie */
79 EditLine *el; /* editline cookie */
80 int displayhist;
81 static FILE *el_in, *el_out;
82 static int curpos;
83
84 static char *HistFile = NULL;
85 static const char *HistFileOpen = NULL;
86 FILE *HistFP = NULL;
87 static int History_fd;
88
89 #ifdef DEBUG
90 extern FILE *tracefile;
91 #endif
92
93 static const char *fc_replace(const char *, char *, char *);
94 static int not_fcnumber(const char *);
95 static int str_to_event(const char *, int);
96 static int comparator(const void *, const void *);
97 static char **sh_matches(const char *, int, int);
98 static unsigned char sh_complete(EditLine *, int);
99 static FILE *Hist_File_Open(const char *);
100
101 /*
102 * Set history and editing status. Called whenever the status may
103 * have changed (figures out what to do).
104 */
105 void
106 histedit(void)
107 {
108 FILE *el_err;
109
110 #define editing (Eflag || Vflag)
111
112 CTRACE(DBG_HISTORY, ("histedit: %cE%cV %sinteractive\n",
113 Eflag ? '-' : '+', Vflag ? '-' : '+', iflag ? "" : "not "));
114
115 if (iflag == 1) {
116 if (!hist) {
117 /*
118 * turn history on
119 */
120 INTOFF;
121 hist = history_init();
122 INTON;
123
124 if (hist != NULL) {
125 sethistsize(histsizeval(), histsizeflags());
126 sethistfile(histfileval(), histfileflags());
127 } else
128 out2str("sh: can't initialize history\n");
129 }
130 if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
131 /*
132 * turn editing on
133 */
134 char *term;
135
136 INTOFF;
137 if (el_in == NULL)
138 el_in = fdopen(0, "r");
139 if (el_out == NULL)
140 el_out = fdopen(2, "w");
141 if (el_in == NULL || el_out == NULL)
142 goto bad;
143 el_err = el_out;
144 #if DEBUG
145 if (tracefile)
146 el_err = tracefile;
147 #endif
148 /*
149 * This odd piece of code doesn't affect the shell
150 * at all, the environment modified here is the
151 * stuff accessed via "environ" (the incoming
152 * environment to the shell) which is only ever
153 * touched at sh startup time (long before we get
154 * here) and ignored thereafter.
155 *
156 * But libedit calls getenv() to discover TERM
157 * and that searches the "environ" environment,
158 * not the shell's internal variable data struct,
159 * so we need to make sure that TERM in there is
160 * correct.
161 *
162 * This sequence copies TERM from the shell into
163 * the old "environ" environment.
164 */
165 term = lookupvar("TERM");
166 if (term)
167 setenv("TERM", term, 1);
168 else
169 unsetenv("TERM");
170 el = el_init("sh", el_in, el_out, el_err);
171 VTRACE(DBG_HISTORY, ("el_init() %sed\n",
172 el != NULL ? "succeed" : "fail"));
173 if (el != NULL) {
174 if (hist)
175 el_set(el, EL_HIST, history, hist);
176
177 set_prompt_lit(lookupvar("PSlit"), 0);
178 el_set(el, EL_SIGNAL, 1);
179 el_set(el, EL_SAFEREAD, 1);
180 el_set(el, EL_ALIAS_TEXT, alias_text, NULL);
181 el_set(el, EL_ADDFN, "rl-complete",
182 "ReadLine compatible completion function",
183 sh_complete);
184 } else {
185 bad:;
186 out2str("sh: can't initialize editing\n");
187 }
188 INTON;
189 } else if (!editing && el) {
190 INTOFF;
191 el_end(el);
192 el = NULL;
193 VTRACE(DBG_HISTORY, ("line editing disabled\n"));
194 INTON;
195 }
196 if (el) {
197 INTOFF;
198 if (Vflag)
199 el_set(el, EL_EDITOR, "vi");
200 else if (Eflag)
201 el_set(el, EL_EDITOR, "emacs");
202 VTRACE(DBG_HISTORY, ("reading $EDITRC\n"));
203 el_source(el, lookupvar("EDITRC"));
204 el_set(el, EL_BIND, "^I",
205 tabcomplete ? "rl-complete" : "ed-insert", NULL);
206 INTON;
207 }
208 } else {
209 INTOFF;
210 if (el) { /* no editing if not interactive */
211 el_end(el);
212 el = NULL;
213 }
214 if (hist) {
215 history_end(hist);
216 hist = NULL;
217 }
218 INTON;
219 VTRACE(DBG_HISTORY, ("line editing & history disabled\n"));
220 }
221 }
222
223 void
224 set_prompt_lit(char *lit_ch, int flags __unused)
225 {
226 wchar_t wc;
227
228 if (!(iflag && editing && el))
229 return;
230
231 if (lit_ch == NULL) {
232 el_set(el, EL_PROMPT, getprompt);
233 return;
234 }
235
236 mbtowc(&wc, NULL, 1); /* state init */
237
238 INTOFF;
239 if (mbtowc(&wc, lit_ch, strlen(lit_ch)) <= 0)
240 el_set(el, EL_PROMPT, getprompt);
241 else
242 el_set(el, EL_PROMPT_ESC, getprompt, (int)wc);
243 INTON;
244 }
245
246 void
247 set_editrc(char *fname, int flags)
248 {
249 INTOFF;
250 if (iflag && editing && el && !(flags & VUNSET))
251 el_source(el, fname);
252 INTON;
253 }
254
255 void
256 sethistsize(char *hs, int flags)
257 {
258 int histsize;
259 HistEvent he;
260
261 CTRACE(DBG_HISTORY, ("Set HISTSIZE=%s [%x] %s\n",
262 (hs == NULL ? "''" : hs), flags, "!hist" + (hist != NULL)));
263
264 if (hs != NULL && *hs != '\0' && (flags & VUNSAFE) && !is_number(hs))
265 hs = NULL;
266
267 if (hs == NULL || *hs == '\0' || (flags & VUNSET) ||
268 (histsize = number(hs)) < 0)
269 histsize = 100;
270
271 if (hist != NULL) {
272 INTOFF;
273 /* H_SETSIZE actually sets n-1 as the limit */
274 history(hist, &he, H_SETSIZE, histsize + 1);
275 history(hist, &he, H_SETUNIQUE, 1);
276 INTON;
277 }
278 }
279
280 void
281 sethistfile(char *hs, int flags)
282 {
283 const char *file;
284 HistEvent he;
285
286 CTRACE(DBG_HISTORY, ("Set HISTFILE=%s [%x] %s\n",
287 (hs == NULL ? "''" : hs), flags, "!hist" + (hist != NULL)));
288
289 if (hs == NULL || *hs == '\0' || (flags & VUNSET)) {
290 if (HistFP != NULL) {
291 fclose(HistFP);
292 HistFP = NULL;
293 }
294 if (HistFile != NULL) {
295 free(HistFile);
296 HistFile = NULL;
297 HistFileOpen = NULL;
298 }
299 return;
300 }
301
302 if (hist != NULL) {
303 file = expandvar(hs, flags);
304 if (file == NULL || *file == '\0')
305 return;
306
307 INTOFF;
308
309 history(hist, &he, H_LOAD, file);
310
311 /*
312 * This is needed so sethistappend() can work
313 * on the current (new) filename, not the previous one.
314 */
315 if (HistFile != NULL)
316 free(HistFile);
317
318 HistFile = strdup(hs);
319 /*
320 * We need to ensure that HistFile & HistFileOpen
321 * are not equal .. we know HistFile has just altered.
322 * If they happen to be equal (both NULL perhaps, or
323 * the strdup() just above happned to return the same
324 * buffer as was freed the line before) then simply
325 * set HistFileOpen to something which cannot be the
326 * same as anything allocated, or NULL. Its only
327 * use is to compare against HistFile.
328 */
329 if (HistFile == HistFileOpen)
330 HistFileOpen = "";
331
332 sethistappend((histappflags() & VUNSET) ? NULL : histappval(),
333 ~VUNSET & 0xFFFF);
334
335 INTON;
336 }
337 }
338
339 void
340 sethistappend(char *s, int flags __diagused)
341 {
342 CTRACE(DBG_HISTORY, ("Set HISTAPPEND=%s [%x] %s ",
343 (s == NULL ? "''" : s), flags, "!hist" + (hist != NULL)));
344
345 INTOFF;
346 if (flags & VUNSET || !boolstr(s)) {
347 CTRACE(DBG_HISTORY, ("off"));
348
349 if (HistFP != NULL) {
350 CTRACE(DBG_HISTORY, (" closing"));
351
352 fclose(HistFP);
353 HistFP = NULL;
354 HistFileOpen = NULL;
355 }
356 } else {
357 CTRACE(DBG_HISTORY, ("on"));
358
359 if (HistFileOpen != HistFile || HistFP == NULL) {
360 if (HistFP != NULL) {
361 CTRACE(DBG_HISTORY, (" closing prev"));
362 fclose(HistFP);
363 HistFP = NULL;
364 }
365 if (hist != NULL &&
366 HistFile != NULL &&
367 HistFP == NULL) {
368 CTRACE(DBG_HISTORY, ("\n"));
369
370 save_sh_history();
371
372 CTRACE(DBG_HISTORY, ("opening: "));
373
374 HistFP = Hist_File_Open(HistFile);
375 if (HistFP != NULL)
376 HistFileOpen = HistFile;
377 else {
378 CTRACE(DBG_HISTORY, ("open failed"));
379 }
380 }
381 }
382 }
383 INTON;
384
385 CTRACE(DBG_HISTORY, ("\n"));
386 }
387
388 static void
389 History_FD_Renumbered(int from, int to)
390 {
391 if (History_fd == from)
392 History_fd = to;
393
394 VTRACE(DBG_HISTORY, ("History_FD_Renumbered(%d,%d)-> %d\n",
395 from, to, History_fd));
396 }
397
398 /*
399 * The callback functions for the FILE* returned by funopen2()
400 */
401 static ssize_t
402 Hist_Write(void *cookie, const void *buf, size_t len)
403 {
404 if (cookie != (void *)&History_fd) {
405 errno = EINVAL;
406 return -1;
407 }
408
409 return write(History_fd, buf, len);
410 }
411
412 static int
413 Hist_Close(void *cookie)
414 {
415 if (cookie == (void *)&History_fd) {
416 sh_close(History_fd);
417 History_fd = -1;
418 return 0;
419 }
420
421 VTRACE(DBG_HISTORY, ("HistClose(%p) != %p\n", cookie, &History_fd));
422
423 errno = EINVAL;
424 return -1;
425 }
426
427 static off_t
428 Hist_Seek(void *cookie, off_t pos, int whence)
429 {
430 if (cookie != (void *)&History_fd) {
431 errno = EINVAL;
432 return -1;
433 }
434
435 return lseek(History_fd, pos, whence);
436 }
437
438 /*
439 * a variant of open() for history files.
440 */
441 static int
442 open_history_file(const char *name, int mode)
443 {
444 int fd;
445 struct stat statb;
446
447 fd = open(name, mode, S_IWUSR|S_IRUSR);
448
449 VTRACE(DBG_HISTORY, ("open_history_file(\"%s\", %#x) -> %d\n",
450 name, mode, fd));
451
452 if (fd == -1)
453 return -1;
454
455 if (fstat(fd, &statb) == -1) {
456 VTRACE(DBG_HISTORY, ("history file fstat(%d) failed [%d]\n",
457 fd, errno));
458 close(fd);
459 return -1;
460 }
461
462 if (statb.st_uid != getuid()) {
463 VTRACE(DBG_HISTORY,
464 ("history file wrong user (uid=%d file=%d)\n",
465 getuid(), statb.st_uid));
466 close(fd);
467 return -1;
468 }
469
470 return fd;
471 }
472
473 static FILE *
474 Hist_File_Open(const char *name)
475 {
476 FILE *fd;
477 int n;
478
479 INTOFF;
480
481 n = open_history_file(name, O_WRONLY|O_CREAT|O_APPEND|O_CLOEXEC);
482
483 VTRACE(DBG_HISTORY, ("History_File_Open(\"%s\") -> %d", name, n));
484 if (n == -1) {
485 VTRACE(DBG_HISTORY, (" [%d]\n", errno));
486 INTON;
487 return NULL;
488 }
489
490 n = to_upper_fd(n);
491 (void) lseek(n, 0, SEEK_END);
492 VTRACE(DBG_HISTORY, (" -> %d", n));
493
494 History_fd = n;
495 register_sh_fd(n, History_FD_Renumbered);
496
497 if ((fd =
498 funopen2(&History_fd, NULL, Hist_Write, Hist_Seek, NULL,
499 Hist_Close)) == NULL) {
500
501 VTRACE(DBG_HISTORY, ("; funopen2 failed[%d]\n", errno));
502
503 sh_close(n);
504 History_fd = -1;
505 INTON;
506 return NULL;
507 }
508 setlinebuf(fd);
509
510 VTRACE(DBG_HISTORY, (" fd:%p\n", fd));
511
512 INTON;
513
514 return fd;
515 }
516
517 void
518 save_sh_history(void)
519 {
520 char *var;
521 const char *file;
522 int fd;
523 FILE *fp;
524 HistEvent he;
525
526 if (HistFP != NULL) {
527 /* don't close, just make sure nothing in buffer */
528 (void) fflush(HistFP);
529 return;
530 }
531
532 if (hist == NULL)
533 return;
534
535 var = histfileval();
536 if ((histfileflags() & VUNSET) || *var == '\0')
537 return;
538
539 file = expandvar(var, histfileflags());
540
541 VTRACE(DBG_HISTORY,
542 ("save_sh_history('%s')\n", file == NULL ? "" : file));
543
544 if (file == NULL || *file == '\0')
545 return;
546
547 INTOFF;
548 fd = open_history_file(file, O_WRONLY|O_CREAT|O_TRUNC);
549 if (fd != -1) {
550 fp = fdopen(fd, "w");
551 if (fp != NULL) {
552 (void) history(hist, &he, H_SAVE_FP, fp);
553 fclose(fp);
554 } else
555 close(fd);
556 }
557 INTON;
558 }
559
560 void
561 setterm(char *term, int flags __unused)
562 {
563 INTOFF;
564 if (el != NULL && term != NULL && *term != '\0')
565 if (el_set(el, EL_TERMINAL, term) != 0) {
566 outfmt(out2, "sh: Can't set terminal type %s\n", term);
567 outfmt(out2, "sh: Using dumb terminal settings.\n");
568 }
569 INTON;
570 }
571
572 /*
573 * The built-in sh commands supported by this file
574 */
575 int
576 inputrc(int argc, char **argv)
577 {
578 CTRACE(DBG_HISTORY, ("inputrc (%d arg%s)", argc-1, argc==2?"":"s"));
579 if (argc != 2) {
580 CTRACE(DBG_HISTORY, (" -- bad\n"));
581 out2str("usage: inputrc file\n");
582 return 1;
583 }
584 CTRACE(DBG_HISTORY, (" file: \"%s\"\n", argv[1]));
585 if (el != NULL) {
586 INTOFF;
587 if (el_source(el, argv[1])) {
588 INTON;
589 out2str("inputrc: failed\n");
590 return 1;
591 }
592 INTON;
593 return 0;
594 } else {
595 out2str("sh: inputrc ignored, not editing\n");
596 return 1;
597 }
598 }
599
600 /*
601 * This command is provided since POSIX decided to standardize
602 * the Korn shell fc command. Oh well...
603 */
604 int
605 histcmd(volatile int argc, char ** volatile argv)
606 {
607 int ch;
608 const char * volatile editor = NULL;
609 HistEvent he;
610 volatile int lflg = 0, nflg = 0, rflg = 0, sflg = 0, zflg = 0;
611 int i, retval;
612 const char *firststr, *laststr;
613 int first, last, direction;
614
615 char * volatile pat = NULL; /* ksh "fc old=new" crap */
616 char * volatile repl;
617
618 static int active = 0;
619 struct jmploc jmploc;
620 struct jmploc *volatile savehandler;
621 char editfile[MAXPATHLEN + 1];
622 FILE * volatile efp;
623
624 #ifdef __GNUC__
625 repl = NULL; /* XXX gcc4 */
626 efp = NULL; /* XXX gcc4 */
627 #endif
628
629 if (hist == NULL)
630 error("history not active");
631
632 CTRACE(DBG_HISTORY, ("histcmd (fc) %d arg%s\n", argc, argc==1?"":"s"));
633 if (argc == 1)
634 error("missing history argument");
635
636 optreset = 1; optind = 1; /* initialize getopt */
637 while (not_fcnumber(argv[optind]) &&
638 (ch = getopt(argc, argv, ":e:lnrsz")) != -1)
639 switch ((char)ch) {
640 case 'e':
641 editor = optarg;
642 VTRACE(DBG_HISTORY, ("histcmd -e %s\n", editor));
643 break;
644 case 'l':
645 lflg = 1;
646 VTRACE(DBG_HISTORY, ("histcmd -l\n"));
647 break;
648 case 'n':
649 nflg = 1;
650 VTRACE(DBG_HISTORY, ("histcmd -n\n"));
651 break;
652 case 'r':
653 rflg = 1;
654 VTRACE(DBG_HISTORY, ("histcmd -r\n"));
655 break;
656 case 's':
657 sflg = 1;
658 VTRACE(DBG_HISTORY, ("histcmd -s\n"));
659 break;
660 case 'z':
661 zflg = 1;
662 VTRACE(DBG_HISTORY, ("histcmd -z\n"));
663 break;
664 case ':':
665 error("option -%c expects argument", optopt);
666 /* NOTREACHED */
667 case '?':
668 default:
669 error("unknown option: -%c", optopt);
670 /* NOTREACHED */
671 }
672 argc -= optind, argv += optind;
673
674 if (zflg) {
675 if (argc != 0 || (lflg|nflg|rflg|sflg) != 0)
676 error("Usage: fc -z");
677
678 history(hist, &he, H_CLEAR);
679 return 0;
680 }
681
682
683 /*
684 * If executing...
685 */
686 if (lflg == 0 || editor || sflg) {
687 lflg = 0; /* ignore */
688 editfile[0] = '\0';
689 /*
690 * Catch interrupts to reset active counter and
691 * cleanup temp files.
692 */
693 savehandler = handler;
694 if (setjmp(jmploc.loc)) {
695 active = 0;
696 if (*editfile) {
697 VTRACE(DBG_HISTORY,
698 ("histcmd err jump unlink temp \"%s\"\n",
699 editfile));
700 unlink(editfile);
701 }
702 handler = savehandler;
703 longjmp(handler->loc, 1);
704 }
705 handler = &jmploc;
706 VTRACE(DBG_HISTORY, ("histcmd is active %d(++)\n", active));
707 if (++active > MAXHISTLOOPS) {
708 active = 0;
709 displayhist = 0;
710 error("called recursively too many times");
711 }
712 /*
713 * Set editor.
714 */
715 if (sflg == 0) {
716 if (editor == NULL &&
717 (editor = bltinlookup("FCEDIT", 1)) == NULL &&
718 (editor = bltinlookup("EDITOR", 1)) == NULL)
719 editor = DEFEDITOR;
720 if (editor[0] == '-' && editor[1] == '\0') {
721 sflg = 1; /* no edit */
722 editor = NULL;
723 }
724 VTRACE(DBG_HISTORY, ("histcmd using %s as editor\n",
725 editor == NULL ? "-nothing-" : editor));
726 }
727 }
728
729 /*
730 * If executing, parse [old=new] now
731 */
732 if (lflg == 0 && argc > 0 &&
733 ((repl = strchr(argv[0], '=')) != NULL)) {
734 pat = argv[0];
735 *repl++ = '\0';
736 argc--, argv++;
737 VTRACE(DBG_HISTORY, ("histcmd replace old=\"%s\" new=\"%s\""
738 " (%d args)\n", pat, repl, argc));
739 }
740
741 /*
742 * If -s is specified, accept only one operand
743 */
744 if (sflg && argc >= 2)
745 error("too many args");
746
747 /*
748 * determine [first] and [last]
749 */
750 switch (argc) {
751 case 0:
752 if (lflg) {
753 firststr = "-16";
754 laststr = "-1";
755 } else
756 firststr = laststr = "-1"; /* the exact same str */
757 break;
758 case 1:
759 firststr = argv[0];
760 laststr = lflg ? "-1" : argv[0];
761 break;
762 case 2:
763 firststr = argv[0];
764 laststr = argv[1];
765 break;
766 default:
767 error("too many args");
768 /* NOTREACHED */
769 }
770 /*
771 * Turn into event numbers.
772 */
773 first = str_to_event(firststr, 0);
774 last = str_to_event(laststr, 1);
775
776 if (first == -1 || last == -1) {
777 if (lflg) /* no history exists, that's OK */
778 return 0;
779 if (first == -1 && last == -1) {
780 if (firststr != laststr)
781 error("history events %s to %s do not exist",
782 firststr, laststr);
783 else
784 error("history event %s does not exist",
785 firststr);
786 } else {
787 error("history event %s does not exist",
788 first == -1 ? firststr : laststr);
789 }
790 }
791
792 if (rflg) {
793 i = last;
794 last = first;
795 first = i;
796 }
797 VTRACE(DBG_HISTORY, ("histcmd%s first=\"%s\" (#%d) last=\"%s\" (#%d)\n",
798 rflg ? " reversed" : "", rflg ? laststr : firststr, first,
799 rflg ? firststr : laststr, last));
800
801 /*
802 * XXX - this should not depend on the event numbers
803 * always increasing. Add sequence numbers or offset
804 * to the history element in next (diskbased) release.
805 */
806 direction = first < last ? H_PREV : H_NEXT;
807
808 /*
809 * If editing, grab a temp file.
810 */
811 if (editor) {
812 int fd;
813
814 INTOFF; /* easier */
815 snprintf(editfile, sizeof(editfile),
816 "%s_shXXXXXX", _PATH_TMP);
817 if ((fd = mkstemp(editfile)) < 0)
818 error("can't create temporary file %s", editfile);
819 if ((efp = fdopen(fd, "w")) == NULL) {
820 close(fd);
821 error("can't allocate stdio buffer for temp");
822 }
823 VTRACE(DBG_HISTORY, ("histcmd created \"%s\" for edit buffer"
824 " fd=%d\n", editfile, fd));
825 }
826
827 /*
828 * Loop through selected history events. If listing or executing,
829 * do it now. Otherwise, put into temp file and call the editor
830 * after.
831 *
832 * The history interface needs rethinking, as the following
833 * convolutions will demonstrate.
834 */
835 history(hist, &he, H_FIRST);
836 retval = history(hist, &he, H_NEXT_EVENT, first);
837 for ( ; retval != -1; retval = history(hist, &he, direction)) {
838 if (lflg) {
839 if (!nflg)
840 out1fmt("%5d ", he.num);
841 out1str(he.str);
842 } else {
843 const char *s = pat ?
844 fc_replace(he.str, pat, repl) : he.str;
845
846 if (sflg) {
847 VTRACE(DBG_HISTORY, ("histcmd -s \"%s\"\n", s));
848 if (displayhist) {
849 out2str(s);
850 }
851
852 evalstring(s, 0);
853
854 if (displayhist && hist) {
855 /*
856 * XXX what about recursive and
857 * relative histnums.
858 */
859 history(hist, &he, H_ENTER, s);
860 }
861
862 break;
863 } else
864 fputs(s, efp);
865 }
866 /*
867 * At end? (if we were to lose last, we'd sure be
868 * messed up).
869 */
870 if (he.num == last)
871 break;
872 }
873 if (editor) {
874 char *editcmd;
875 size_t cmdlen;
876
877 fclose(efp);
878 cmdlen = strlen(editor) + strlen(editfile) + 2;
879 editcmd = stalloc(cmdlen);
880 snprintf(editcmd, cmdlen, "%s %s", editor, editfile);
881 VTRACE(DBG_HISTORY, ("histcmd editing: \"%s\"\n", editcmd));
882 evalstring(editcmd, 0); /* XXX - should use no JC command */
883 stunalloc(editcmd);
884 VTRACE(DBG_HISTORY, ("histcmd read cmds from %s\n", editfile));
885 readcmdfile(editfile); /* XXX - should read back - quick tst */
886 VTRACE(DBG_HISTORY, ("histcmd unlink %s\n", editfile));
887 unlink(editfile);
888 editfile[0] = '\0';
889 INTON;
890 }
891
892 if (lflg == 0 && active > 0)
893 --active;
894 if (displayhist)
895 displayhist = 0;
896 return 0;
897 }
898
899 /*
900 * and finally worker functions for those built-ins
901 */
902
903 static const char *
904 fc_replace(const char *s, char *p, char *r)
905 {
906 char *dest;
907 int plen = strlen(p);
908
909 VTRACE(DBG_HISTORY, ("histcmd s/%s/%s/ in \"%s\" -> ", p, r, s));
910 STARTSTACKSTR(dest);
911 while (*s) {
912 if (*s == *p && strncmp(s, p, plen) == 0) {
913 while (*r)
914 STPUTC(*r++, dest);
915 s += plen;
916 *p = '\0'; /* so no more matches */
917 } else
918 STPUTC(*s++, dest);
919 }
920 STPUTC('\0', dest);
921 dest = grabstackstr(dest);
922 VTRACE(DBG_HISTORY, ("\"%s\"\n", dest));
923
924 return dest;
925 }
926
927
928 /*
929 * Comparator function for qsort(). The use of curpos here is to skip
930 * characters that we already know to compare equal (common prefix).
931 */
932 static int
933 comparator(const void *a, const void *b)
934 {
935 return strcmp(*(char *const *)a + curpos,
936 *(char *const *)b + curpos);
937 }
938
939 /*
940 * This function is passed to libedit's fn_complete(). The library will
941 * use it instead of its standard function to find matches, which
942 * searches for files in current directory. If we're at the start of the
943 * line, we want to look for available commands from all paths in $PATH.
944 */
945 static char **
946 sh_matches(const char *text, int start, int end)
947 {
948 char *free_path = NULL, *dirname, *path;
949 char **matches = NULL;
950 size_t i = 0, size = 16;
951
952 if (start > 0)
953 return NULL;
954 curpos = end - start;
955 if ((free_path = path = strdup(pathval())) == NULL)
956 goto out;
957 if ((matches = malloc(size * sizeof(matches[0]))) == NULL)
958 goto out;
959 while ((dirname = strsep(&path, ":")) != NULL) {
960 struct dirent *entry;
961 DIR *dir;
962 int dfd;
963
964 if ((dir = opendir(dirname)) == NULL)
965 continue;
966 if ((dfd = dirfd(dir)) == -1)
967 continue;
968 while ((entry = readdir(dir)) != NULL) {
969 struct stat statb;
970
971 if (strncmp(entry->d_name, text, curpos) != 0)
972 continue;
973 if (entry->d_type == DT_UNKNOWN ||
974 entry->d_type == DT_LNK) {
975 if (fstatat(dfd, entry->d_name, &statb, 0)
976 == -1)
977 continue;
978 if (!S_ISREG(statb.st_mode))
979 continue;
980 } else if (entry->d_type != DT_REG)
981 continue;
982 if (++i >= size - 1) {
983 size *= 2;
984 if (reallocarr(&matches, size,
985 sizeof(*matches)))
986 {
987 closedir(dir);
988 goto out;
989 }
990 }
991 matches[i] = strdup(entry->d_name);
992 }
993 closedir(dir);
994 }
995 out:;
996 free(free_path);
997 if (i == 0) {
998 free(matches);
999 return NULL;
1000 }
1001 if (i == 1) {
1002 matches[0] = strdup(matches[1]);
1003 matches[i + 1] = NULL;
1004 } else {
1005 size_t j, k;
1006
1007 qsort(matches + 1, i, sizeof(matches[0]), comparator);
1008 for (j = 1, k = 2; k <= i; k++)
1009 if (strcmp(matches[j] + curpos, matches[k] + curpos)
1010 == 0)
1011 free(matches[k]);
1012 else
1013 matches[++j] = matches[k];
1014 matches[0] = strdup(text);
1015 matches[j + 1] = NULL;
1016 }
1017 return matches;
1018 }
1019
1020 /*
1021 * This is passed to el_set(el, EL_ADDFN, ...) so that it's possible to
1022 * bind a key (tab by default) to execute the function.
1023 */
1024 unsigned char
1025 sh_complete(EditLine *sel, int ch __unused)
1026 {
1027 return (unsigned char)fn_complete2(sel, NULL, sh_matches,
1028 L" \t\n\"\\'`@$><=;|&{(", NULL, NULL, (size_t)100,
1029 NULL, &((int) {0}), NULL, NULL, FN_QUOTE_MATCH);
1030 }
1031
1032 static int
1033 not_fcnumber(const char *s)
1034 {
1035 if (s == NULL)
1036 return 0;
1037 if (*s == '-')
1038 s++;
1039 return !is_number(s);
1040 }
1041
1042 static int
1043 str_to_event(const char *str, int last)
1044 {
1045 HistEvent he;
1046 const char *s = str;
1047 int relative = 0;
1048 int i, retval;
1049
1050 retval = history(hist, &he, H_FIRST);
1051 switch (*s) {
1052 case '-':
1053 relative = 1;
1054 /*FALLTHROUGH*/
1055 case '+':
1056 s++;
1057 }
1058 if (is_number(s)) {
1059 i = number(s);
1060 if (relative) {
1061 while (retval != -1 && i--) {
1062 retval = history(hist, &he, H_NEXT);
1063 }
1064 if (retval == -1)
1065 retval = history(hist, &he, H_LAST);
1066 } else {
1067 retval = history(hist, &he, H_NEXT_EVENT, i);
1068 if (retval == -1) {
1069 /*
1070 * the notion of first and last is
1071 * backwards to that of the history package
1072 */
1073 retval = history(hist, &he,
1074 last ? H_FIRST : H_LAST);
1075 }
1076 }
1077 if (retval == -1)
1078 return -1;
1079 } else {
1080 /*
1081 * pattern
1082 */
1083 retval = history(hist, &he, H_PREV_STR, str);
1084 if (retval == -1)
1085 error("history pattern not found: %s", str);
1086 }
1087 return he.num;
1088 }
1089
1090 #else /* defined(SMALL) */
1091
1092 int
1093 histcmd(int argc, char **argv)
1094 {
1095 error("not compiled with history support");
1096 /* NOTREACHED */
1097 }
1098
1099 int
1100 inputrc(int argc, char **argv)
1101 {
1102 error("not compiled with history support");
1103 /* NOTREACHED */
1104 }
1105
1106 #endif /* SMALL */
1107