1 /* $OpenBSD: history.c,v 1.39 2010/05/19 17:36:08 jasper Exp $ */
2 /* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */
3
4 /*-
5 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
6 * Thorsten Glaser <tg@mirbsd.org>
7 *
8 * Provided that these terms and disclaimer and all copyright notices
9 * are retained or reproduced in an accompanying document, permission
10 * is granted to deal in this work without restriction, including un-
11 * limited rights to use, publicly perform, distribute, sell, modify,
12 * merge, give away, or sublicence.
13 *
14 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15 * the utmost extent permitted by applicable law, neither express nor
16 * implied; without malicious intent or gross negligence. In no event
17 * may a licensor, author or contributor be held liable for indirect,
18 * direct, other damage, loss, or other issues arising in any way out
19 * of dealing in the work, even if advised of the possibility of such
20 * damage or existence of a defect, except proven that it results out
21 * of said person's immediate fault when using the work as intended.
22 */
23
24 #include "sh.h"
25 #if HAVE_SYS_FILE_H
26 #include <sys/file.h>
27 #endif
28
29 __RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.111 2011/09/07 15:24:16 tg Exp $");
30
31 /*-
32 * MirOS: This is the default mapping type, and need not be specified.
33 * IRIX doesn't have this constant.
34 */
35 #ifndef MAP_FILE
36 #define MAP_FILE 0
37 #endif
38
39 Trap sigtraps[NSIG + 1];
40 static struct sigaction Sigact_ign;
41
42 #if HAVE_PERSISTENT_HISTORY
43 static int hist_count_lines(unsigned char *, int);
44 static int hist_shrink(unsigned char *, int);
45 static unsigned char *hist_skip_back(unsigned char *,int *,int);
46 static void histload(Source *, unsigned char *, int);
47 static void histinsert(Source *, int, const char *);
48 static void writehistfile(int, char *);
49 static int sprinkle(int);
50 #endif
51
52 static int hist_execute(char *);
53 static int hist_replace(char **, const char *, const char *, bool);
54 static char **hist_get(const char *, bool, bool);
55 static char **hist_get_oldest(void);
56 static void histbackup(void);
57
58 static char **current; /* current position in history[] */
59 static int hstarted; /* set after hist_init() called */
60 static Source *hist_source;
61
62 #if HAVE_PERSISTENT_HISTORY
63 /* current history file: name, fd, size */
64 static char *hname;
65 static int histfd;
66 static size_t hsize;
67 #endif
68
69 static const char Tnot_in_history[] = "not in history";
70 #define Thistory (Tnot_in_history + 7)
71
72 int
c_fc(const char ** wp)73 c_fc(const char **wp)
74 {
75 struct shf *shf;
76 struct temp *tf;
77 const char *p;
78 char *editor = NULL;
79 bool gflag = false, lflag = false, nflag = false, rflag = false,
80 sflag = false;
81 int optc;
82 const char *first = NULL, *last = NULL;
83 char **hfirst, **hlast, **hp;
84
85 if (!Flag(FTALKING_I)) {
86 bi_errorf("history %ss not available", Tfunction);
87 return (1);
88 }
89
90 while ((optc = ksh_getopt(wp, &builtin_opt,
91 "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
92 switch (optc) {
93
94 case 'e':
95 p = builtin_opt.optarg;
96 if (ksh_isdash(p))
97 sflag = true;
98 else {
99 size_t len = strlen(p);
100
101 /* almost certainly not overflowing */
102 editor = alloc(len + 4, ATEMP);
103 memcpy(editor, p, len);
104 memcpy(editor + len, " $_", 4);
105 }
106 break;
107
108 /* non-AT&T ksh */
109 case 'g':
110 gflag = true;
111 break;
112
113 case 'l':
114 lflag = true;
115 break;
116
117 case 'n':
118 nflag = true;
119 break;
120
121 case 'r':
122 rflag = true;
123 break;
124
125 /* POSIX version of -e - */
126 case 's':
127 sflag = true;
128 break;
129
130 /* kludge city - accept -num as -- -num (kind of) */
131 case '0': case '1': case '2': case '3': case '4':
132 case '5': case '6': case '7': case '8': case '9':
133 p = shf_smprintf("-%c%s",
134 optc, builtin_opt.optarg);
135 if (!first)
136 first = p;
137 else if (!last)
138 last = p;
139 else {
140 bi_errorf("too many arguments");
141 return (1);
142 }
143 break;
144
145 case '?':
146 return (1);
147 }
148 wp += builtin_opt.optind;
149
150 /* Substitute and execute command */
151 if (sflag) {
152 char *pat = NULL, *rep = NULL;
153
154 if (editor || lflag || nflag || rflag) {
155 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
156 return (1);
157 }
158
159 /* Check for pattern replacement argument */
160 if (*wp && **wp && (p = cstrchr(*wp + 1, '='))) {
161 strdupx(pat, *wp, ATEMP);
162 rep = pat + (p - *wp);
163 *rep++ = '\0';
164 wp++;
165 }
166 /* Check for search prefix */
167 if (!first && (first = *wp))
168 wp++;
169 if (last || *wp) {
170 bi_errorf("too many arguments");
171 return (1);
172 }
173
174 hp = first ? hist_get(first, false, false) :
175 hist_get_newest(false);
176 if (!hp)
177 return (1);
178 return (hist_replace(hp, pat, rep, gflag));
179 }
180
181 if (editor && (lflag || nflag)) {
182 bi_errorf("can't use -l, -n with -e");
183 return (1);
184 }
185
186 if (!first && (first = *wp))
187 wp++;
188 if (!last && (last = *wp))
189 wp++;
190 if (*wp) {
191 bi_errorf("too many arguments");
192 return (1);
193 }
194 if (!first) {
195 hfirst = lflag ? hist_get("-16", true, true) :
196 hist_get_newest(false);
197 if (!hfirst)
198 return (1);
199 /* can't fail if hfirst didn't fail */
200 hlast = hist_get_newest(false);
201 } else {
202 /*
203 * POSIX says not an error if first/last out of bounds
204 * when range is specified; AT&T ksh and pdksh allow out
205 * of bounds for -l as well.
206 */
207 hfirst = hist_get(first, tobool(lflag || last), lflag);
208 if (!hfirst)
209 return (1);
210 hlast = last ? hist_get(last, true, lflag) :
211 (lflag ? hist_get_newest(false) : hfirst);
212 if (!hlast)
213 return (1);
214 }
215 if (hfirst > hlast) {
216 char **temp;
217
218 temp = hfirst; hfirst = hlast; hlast = temp;
219 /* POSIX */
220 rflag = !rflag;
221 }
222
223 /* List history */
224 if (lflag) {
225 char *s, *t;
226
227 for (hp = rflag ? hlast : hfirst;
228 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
229 if (!nflag)
230 shf_fprintf(shl_stdout, "%d",
231 hist_source->line - (int)(histptr - hp));
232 shf_putc('\t', shl_stdout);
233 /* print multi-line commands correctly */
234 s = *hp;
235 while ((t = strchr(s, '\n'))) {
236 *t = '\0';
237 shf_fprintf(shl_stdout, "%s\n\t", s);
238 *t++ = '\n';
239 s = t;
240 }
241 shf_fprintf(shl_stdout, "%s\n", s);
242 }
243 shf_flush(shl_stdout);
244 return (0);
245 }
246
247 /* Run editor on selected lines, then run resulting commands */
248
249 tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
250 if (!(shf = tf->shf)) {
251 bi_errorf("can't %s temporary file %s: %s",
252 "create", tf->name, strerror(errno));
253 return (1);
254 }
255 for (hp = rflag ? hlast : hfirst;
256 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
257 shf_fprintf(shf, "%s\n", *hp);
258 if (shf_close(shf) == EOF) {
259 bi_errorf("can't %s temporary file %s: %s",
260 "write", tf->name, strerror(errno));
261 return (1);
262 }
263
264 /* Ignore setstr errors here (arbitrary) */
265 setstr(local("_", false), tf->name, KSH_RETURN_ERROR);
266
267 /* XXX: source should not get trashed by this.. */
268 {
269 Source *sold = source;
270 int ret;
271
272 ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_", 0);
273 source = sold;
274 if (ret)
275 return (ret);
276 }
277
278 {
279 struct stat statb;
280 XString xs;
281 char *xp;
282 int n;
283
284 if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) {
285 bi_errorf("can't %s temporary file %s: %s",
286 "open", tf->name, strerror(errno));
287 return (1);
288 }
289
290 if (stat(tf->name, &statb) < 0)
291 n = 128;
292 else if (statb.st_size > (1024 * 1048576)) {
293 bi_errorf("%s %s too large: %lu", Thistory,
294 "file", (unsigned long)statb.st_size);
295 goto errout;
296 } else
297 n = statb.st_size + 1;
298 Xinit(xs, xp, n, hist_source->areap);
299 while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
300 xp += n;
301 if (Xnleft(xs, xp) <= 0)
302 XcheckN(xs, xp, Xlength(xs, xp));
303 }
304 if (n < 0) {
305 bi_errorf("can't %s temporary file %s: %s",
306 "read", tf->name, strerror(shf_errno(shf)));
307 errout:
308 shf_close(shf);
309 return (1);
310 }
311 shf_close(shf);
312 *xp = '\0';
313 strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
314 return (hist_execute(Xstring(xs, xp)));
315 }
316 }
317
318 /* Save cmd in history, execute cmd (cmd gets trashed) */
319 static int
hist_execute(char * cmd)320 hist_execute(char *cmd)
321 {
322 Source *sold;
323 int ret;
324 char *p, *q;
325
326 histbackup();
327
328 for (p = cmd; p; p = q) {
329 if ((q = strchr(p, '\n'))) {
330 /* kill the newline */
331 *q++ = '\0';
332 if (!*q)
333 /* ignore trailing newline */
334 q = NULL;
335 }
336 histsave(&hist_source->line, p, true, true);
337
338 /* POSIX doesn't say this is done... */
339 shellf("%s\n", p);
340 if (q)
341 /* restore \n (trailing \n not restored) */
342 q[-1] = '\n';
343 }
344
345 /*-
346 * Commands are executed here instead of pushing them onto the
347 * input 'cause POSIX says the redirection and variable assignments
348 * in
349 * X=y fc -e - 42 2> /dev/null
350 * are to effect the repeated commands environment.
351 */
352 /* XXX: source should not get trashed by this.. */
353 sold = source;
354 ret = command(cmd, 0);
355 source = sold;
356 return (ret);
357 }
358
359 static int
hist_replace(char ** hp,const char * pat,const char * rep,bool globr)360 hist_replace(char **hp, const char *pat, const char *rep, bool globr)
361 {
362 char *line;
363
364 if (!pat)
365 strdupx(line, *hp, ATEMP);
366 else {
367 char *s, *s1;
368 size_t pat_len = strlen(pat);
369 size_t rep_len = strlen(rep);
370 size_t len;
371 XString xs;
372 char *xp;
373 bool any_subst = false;
374
375 Xinit(xs, xp, 128, ATEMP);
376 for (s = *hp; (s1 = strstr(s, pat)) && (!any_subst || globr);
377 s = s1 + pat_len) {
378 any_subst = true;
379 len = s1 - s;
380 XcheckN(xs, xp, len + rep_len);
381 /*; first part */
382 memcpy(xp, s, len);
383 xp += len;
384 /* replacement */
385 memcpy(xp, rep, rep_len);
386 xp += rep_len;
387 }
388 if (!any_subst) {
389 bi_errorf("bad substitution");
390 return (1);
391 }
392 len = strlen(s) + 1;
393 XcheckN(xs, xp, len);
394 memcpy(xp, s, len);
395 xp += len;
396 line = Xclose(xs, xp);
397 }
398 return (hist_execute(line));
399 }
400
401 /*
402 * get pointer to history given pattern
403 * pattern is a number or string
404 */
405 static char **
hist_get(const char * str,bool approx,bool allow_cur)406 hist_get(const char *str, bool approx, bool allow_cur)
407 {
408 char **hp = NULL;
409 int n;
410
411 if (getn(str, &n)) {
412 hp = histptr + (n < 0 ? n : (n - hist_source->line));
413 if ((ptrdiff_t)hp < (ptrdiff_t)history) {
414 if (approx)
415 hp = hist_get_oldest();
416 else {
417 bi_errorf("%s: %s", str, Tnot_in_history);
418 hp = NULL;
419 }
420 } else if ((ptrdiff_t)hp > (ptrdiff_t)histptr) {
421 if (approx)
422 hp = hist_get_newest(allow_cur);
423 else {
424 bi_errorf("%s: %s", str, Tnot_in_history);
425 hp = NULL;
426 }
427 } else if (!allow_cur && hp == histptr) {
428 bi_errorf("%s: %s", str, "invalid range");
429 hp = NULL;
430 }
431 } else {
432 int anchored = *str == '?' ? (++str, 0) : 1;
433
434 /* the -1 is to avoid the current fc command */
435 if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
436 bi_errorf("%s: %s", str, Tnot_in_history);
437 else
438 hp = &history[n];
439 }
440 return (hp);
441 }
442
443 /* Return a pointer to the newest command in the history */
444 char **
hist_get_newest(bool allow_cur)445 hist_get_newest(bool allow_cur)
446 {
447 if (histptr < history || (!allow_cur && histptr == history)) {
448 bi_errorf("no history (yet)");
449 return (NULL);
450 }
451 return (allow_cur ? histptr : histptr - 1);
452 }
453
454 /* Return a pointer to the oldest command in the history */
455 static char **
hist_get_oldest(void)456 hist_get_oldest(void)
457 {
458 if (histptr <= history) {
459 bi_errorf("no history (yet)");
460 return (NULL);
461 }
462 return (history);
463 }
464
465 /*
466 * Back up over last histsave
467 */
468 static void
histbackup(void)469 histbackup(void)
470 {
471 static int last_line = -1;
472
473 if (histptr >= history && last_line != hist_source->line) {
474 hist_source->line--;
475 afree(*histptr, APERM);
476 histptr--;
477 last_line = hist_source->line;
478 }
479 }
480
481 /*
482 * Return the current position.
483 */
484 char **
histpos(void)485 histpos(void)
486 {
487 return (current);
488 }
489
490 int
histnum(int n)491 histnum(int n)
492 {
493 int last = histptr - history;
494
495 if (n < 0 || n >= last) {
496 current = histptr;
497 return (last);
498 } else {
499 current = &history[n];
500 return (n);
501 }
502 }
503
504 /*
505 * This will become unnecessary if hist_get is modified to allow
506 * searching from positions other than the end, and in either
507 * direction.
508 */
509 int
findhist(int start,int fwd,const char * str,int anchored)510 findhist(int start, int fwd, const char *str, int anchored)
511 {
512 char **hp;
513 int maxhist = histptr - history;
514 int incr = fwd ? 1 : -1;
515 size_t len = strlen(str);
516
517 if (start < 0 || start >= maxhist)
518 start = maxhist;
519
520 hp = &history[start];
521 for (; hp >= history && hp <= histptr; hp += incr)
522 if ((anchored && strncmp(*hp, str, len) == 0) ||
523 (!anchored && strstr(*hp, str)))
524 return (hp - history);
525
526 return (-1);
527 }
528
529 /*
530 * set history
531 * this means reallocating the dataspace
532 */
533 void
sethistsize(int n)534 sethistsize(int n)
535 {
536 if (n > 0 && n != histsize) {
537 int cursize = histptr - history;
538
539 /* save most recent history */
540 if (n < cursize) {
541 memmove(history, histptr - n, n * sizeof(char *));
542 cursize = n;
543 }
544
545 history = aresize2(history, n, sizeof(char *), APERM);
546
547 histsize = n;
548 histptr = history + cursize;
549 }
550 }
551
552 #if HAVE_PERSISTENT_HISTORY
553 /*
554 * set history file
555 * This can mean reloading/resetting/starting history file
556 * maintenance
557 */
558 void
sethistfile(const char * name)559 sethistfile(const char *name)
560 {
561 /* if not started then nothing to do */
562 if (hstarted == 0)
563 return;
564
565 /* if the name is the same as the name we have */
566 if (hname && strcmp(hname, name) == 0)
567 return;
568
569 /*
570 * its a new name - possibly
571 */
572 if (histfd) {
573 /* yes the file is open */
574 (void)close(histfd);
575 histfd = 0;
576 hsize = 0;
577 afree(hname, APERM);
578 hname = NULL;
579 /* let's reset the history */
580 histptr = history - 1;
581 hist_source->line = 0;
582 }
583
584 hist_init(hist_source);
585 }
586 #endif
587
588 /*
589 * initialise the history vector
590 */
591 void
init_histvec(void)592 init_histvec(void)
593 {
594 if (history == (char **)NULL) {
595 histsize = HISTORYSIZE;
596 history = alloc2(histsize, sizeof(char *), APERM);
597 histptr = history - 1;
598 }
599 }
600
601
602 /*
603 * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
604 * a) permit HISTSIZE to control number of lines of history stored
605 * b) maintain a physical history file
606 *
607 * It turns out that there is a lot of ghastly hackery here
608 */
609
610 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
611 /* do not save command in history but possibly sync */
612 bool
histsync(void)613 histsync(void)
614 {
615 bool changed = false;
616
617 if (histfd) {
618 int lno = hist_source->line;
619
620 hist_source->line++;
621 writehistfile(0, NULL);
622 hist_source->line--;
623
624 if (lno != hist_source->line)
625 changed = true;
626 }
627
628 return (changed);
629 }
630 #endif
631
632 /*
633 * save command in history
634 */
635 void
histsave(int * lnp,const char * cmd,bool dowrite MKSH_A_UNUSED,bool ignoredups)636 histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
637 {
638 char **hp;
639 char *c, *cp;
640
641 strdupx(c, cmd, APERM);
642 if ((cp = strchr(c, '\n')) != NULL)
643 *cp = '\0';
644
645 if (ignoredups && !strcmp(c, *histptr)
646 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
647 && !histsync()
648 #endif
649 ) {
650 afree(c, APERM);
651 return;
652 }
653 ++*lnp;
654
655 #if HAVE_PERSISTENT_HISTORY
656 if (histfd && dowrite)
657 writehistfile(*lnp, c);
658 #endif
659
660 hp = histptr;
661
662 if (++hp >= history + histsize) {
663 /* remove oldest command */
664 afree(*history, APERM);
665 for (hp = history; hp < history + histsize - 1; hp++)
666 hp[0] = hp[1];
667 }
668 *hp = c;
669 histptr = hp;
670 }
671
672 /*
673 * Write history data to a file nominated by HISTFILE
674 * if HISTFILE is unset then history still happens, but
675 * the data is not written to a file
676 * All copies of ksh looking at the file will maintain the
677 * same history. This is ksh behaviour.
678 *
679 * This stuff uses mmap()
680 * if your system ain't got it - then you'll have to undef HISTORYFILE
681 */
682
683 /*-
684 * Open a history file
685 * Format is:
686 * Bytes 1, 2:
687 * HMAGIC - just to check that we are dealing with
688 * the correct object
689 * Then follows a number of stored commands
690 * Each command is
691 * <command byte><command number(4 bytes)><bytes><null>
692 */
693 #define HMAGIC1 0xab
694 #define HMAGIC2 0xcd
695 #define COMMAND 0xff
696
697 void
hist_init(Source * s)698 hist_init(Source *s)
699 {
700 #if HAVE_PERSISTENT_HISTORY
701 unsigned char *base;
702 int lines, fd, rv = 0;
703 off_t hfsize;
704 #endif
705
706 if (Flag(FTALKING) == 0)
707 return;
708
709 hstarted = 1;
710
711 hist_source = s;
712
713 #if HAVE_PERSISTENT_HISTORY
714 if ((hname = str_val(global("HISTFILE"))) == NULL)
715 return;
716 strdupx(hname, hname, APERM);
717
718 retry:
719 /* we have a file and are interactive */
720 if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0)
721 return;
722
723 histfd = savefd(fd);
724 if (histfd != fd)
725 close(fd);
726
727 (void)flock(histfd, LOCK_EX);
728
729 hfsize = lseek(histfd, (off_t)0, SEEK_END);
730 hsize = 1024 * 1048576;
731 if (hfsize < (off_t)hsize)
732 hsize = (size_t)hfsize;
733
734 if (hsize == 0) {
735 /* add magic */
736 if (sprinkle(histfd)) {
737 hist_finish();
738 return;
739 }
740 } else if (hsize > 0) {
741 /*
742 * we have some data
743 */
744 base = (void *)mmap(NULL, hsize, PROT_READ,
745 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
746 /*
747 * check on its validity
748 */
749 if (base == (unsigned char *)MAP_FAILED ||
750 *base != HMAGIC1 || base[1] != HMAGIC2) {
751 if (base != (unsigned char *)MAP_FAILED)
752 munmap((caddr_t)base, hsize);
753 hist_finish();
754 if (unlink(hname) /* fails */)
755 goto hiniterr;
756 goto retry;
757 }
758 if (hsize > 2) {
759 lines = hist_count_lines(base+2, hsize-2);
760 if (lines > histsize) {
761 /* we need to make the file smaller */
762 if (hist_shrink(base, hsize))
763 rv = unlink(hname);
764 munmap((caddr_t)base, hsize);
765 hist_finish();
766 if (rv) {
767 hiniterr:
768 bi_errorf("can't %s %s: %s",
769 "unlink HISTFILE", hname,
770 strerror(errno));
771 hsize = 0;
772 return;
773 }
774 goto retry;
775 }
776 }
777 histload(hist_source, base+2, hsize-2);
778 munmap((caddr_t)base, hsize);
779 }
780 (void)flock(histfd, LOCK_UN);
781 hfsize = lseek(histfd, (off_t)0, SEEK_END);
782 hsize = 1024 * 1048576;
783 if (hfsize < (off_t)hsize)
784 hsize = hfsize;
785 #endif
786 }
787
788 #if HAVE_PERSISTENT_HISTORY
789 typedef enum state {
790 shdr, /* expecting a header */
791 sline, /* looking for a null byte to end the line */
792 sn1, /* bytes 1 to 4 of a line no */
793 sn2, sn3, sn4
794 } State;
795
796 static int
hist_count_lines(unsigned char * base,int bytes)797 hist_count_lines(unsigned char *base, int bytes)
798 {
799 State state = shdr;
800 int lines = 0;
801
802 while (bytes--) {
803 switch (state) {
804 case shdr:
805 if (*base == COMMAND)
806 state = sn1;
807 break;
808 case sn1:
809 state = sn2; break;
810 case sn2:
811 state = sn3; break;
812 case sn3:
813 state = sn4; break;
814 case sn4:
815 state = sline; break;
816 case sline:
817 if (*base == '\0') {
818 lines++;
819 state = shdr;
820 }
821 }
822 base++;
823 }
824 return (lines);
825 }
826
827 /*
828 * Shrink the history file to histsize lines
829 */
830 static int
hist_shrink(unsigned char * oldbase,int oldbytes)831 hist_shrink(unsigned char *oldbase, int oldbytes)
832 {
833 int fd, rv = 0;
834 char *nfile = NULL;
835 struct stat statb;
836 unsigned char *nbase = oldbase;
837 int nbytes = oldbytes;
838
839 nbase = hist_skip_back(nbase, &nbytes, histsize);
840 if (nbase == NULL)
841 return (1);
842 if (nbase == oldbase)
843 return (0);
844
845 /*
846 * create temp file
847 */
848 nfile = shf_smprintf("%s.%d", hname, (int)procpid);
849 if ((fd = open(nfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0)
850 goto errout;
851 if (fstat(histfd, &statb) >= 0 &&
852 chown(nfile, statb.st_uid, statb.st_gid))
853 goto errout;
854
855 if (sprinkle(fd) || write(fd, nbase, nbytes) != nbytes)
856 goto errout;
857 close(fd);
858 fd = -1;
859
860 /*
861 * rename
862 */
863 if (rename(nfile, hname) < 0) {
864 errout:
865 if (fd >= 0) {
866 close(fd);
867 if (nfile)
868 unlink(nfile);
869 }
870 rv = 1;
871 }
872 afree(nfile, ATEMP);
873 return (rv);
874 }
875
876 /*
877 * find a pointer to the data 'no' back from the end of the file
878 * return the pointer and the number of bytes left
879 */
880 static unsigned char *
hist_skip_back(unsigned char * base,int * bytes,int no)881 hist_skip_back(unsigned char *base, int *bytes, int no)
882 {
883 int lines = 0;
884 unsigned char *ep;
885
886 for (ep = base + *bytes; --ep > base; ) {
887 /*
888 * this doesn't really work: the 4 byte line number that
889 * is encoded after the COMMAND byte can itself contain
890 * the COMMAND byte....
891 */
892 for (; ep > base && *ep != COMMAND; ep--)
893 ;
894 if (ep == base)
895 break;
896 if (++lines == no) {
897 *bytes = *bytes - ((char *)ep - (char *)base);
898 return (ep);
899 }
900 }
901 return (NULL);
902 }
903
904 /*
905 * load the history structure from the stored data
906 */
907 static void
histload(Source * s,unsigned char * base,int bytes)908 histload(Source *s, unsigned char *base, int bytes)
909 {
910 State state;
911 int lno = 0;
912 unsigned char *line = NULL;
913
914 for (state = shdr; bytes-- > 0; base++) {
915 switch (state) {
916 case shdr:
917 if (*base == COMMAND)
918 state = sn1;
919 break;
920 case sn1:
921 lno = (((*base)&0xff)<<24);
922 state = sn2;
923 break;
924 case sn2:
925 lno |= (((*base)&0xff)<<16);
926 state = sn3;
927 break;
928 case sn3:
929 lno |= (((*base)&0xff)<<8);
930 state = sn4;
931 break;
932 case sn4:
933 lno |= (*base)&0xff;
934 line = base+1;
935 state = sline;
936 break;
937 case sline:
938 if (*base == '\0') {
939 /* worry about line numbers */
940 if (histptr >= history && lno-1 != s->line) {
941 /* a replacement ? */
942 histinsert(s, lno, (char *)line);
943 } else {
944 s->line = lno--;
945 histsave(&lno, (char *)line, false,
946 false);
947 }
948 state = shdr;
949 }
950 }
951 }
952 }
953
954 /*
955 * Insert a line into the history at a specified number
956 */
957 static void
histinsert(Source * s,int lno,const char * line)958 histinsert(Source *s, int lno, const char *line)
959 {
960 char **hp;
961
962 if (lno >= s->line - (histptr - history) && lno <= s->line) {
963 hp = &histptr[lno - s->line];
964 if (*hp)
965 afree(*hp, APERM);
966 strdupx(*hp, line, APERM);
967 }
968 }
969
970 /*
971 * write a command to the end of the history file
972 * This *MAY* seem easy but it's also necessary to check
973 * that the history file has not changed in size.
974 * If it has - then some other shell has written to it
975 * and we should read those commands to update our history
976 */
977 static void
writehistfile(int lno,char * cmd)978 writehistfile(int lno, char *cmd)
979 {
980 off_t sizenow;
981 ssize_t bytes;
982 unsigned char *base, *news, hdr[5];
983
984 (void)flock(histfd, LOCK_EX);
985 sizenow = lseek(histfd, (off_t)0, SEEK_END);
986 if ((sizenow <= (1024 * 1048576)) && ((size_t)sizenow != hsize)) {
987 /*
988 * Things have changed
989 */
990 if ((size_t)sizenow > hsize) {
991 /* someone has added some lines */
992 bytes = (size_t)sizenow - hsize;
993 base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ,
994 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
995 if (base == (unsigned char *)MAP_FAILED)
996 goto bad;
997 news = base + hsize;
998 if (*news != COMMAND) {
999 munmap((caddr_t)base, (size_t)sizenow);
1000 goto bad;
1001 }
1002 hist_source->line--;
1003 histload(hist_source, news, bytes);
1004 hist_source->line++;
1005 lno = hist_source->line;
1006 munmap((caddr_t)base, (size_t)sizenow);
1007 hsize = (size_t)sizenow;
1008 } else {
1009 /* it has shrunk */
1010 /* but to what? */
1011 /* we'll give up for now */
1012 goto bad;
1013 }
1014 }
1015 if (cmd) {
1016 /*
1017 * we can write our bit now
1018 */
1019 hdr[0] = COMMAND;
1020 hdr[1] = (lno>>24)&0xff;
1021 hdr[2] = (lno>>16)&0xff;
1022 hdr[3] = (lno>>8)&0xff;
1023 hdr[4] = lno&0xff;
1024 bytes = strlen(cmd) + 1;
1025 if ((write(histfd, hdr, 5) != 5) ||
1026 (write(histfd, cmd, bytes) != bytes))
1027 goto bad;
1028 sizenow = lseek(histfd, (off_t)0, SEEK_END);
1029 hsize = 1024 * 1048576;
1030 if (sizenow < (off_t)hsize)
1031 hsize = (size_t)sizenow;
1032 }
1033 (void)flock(histfd, LOCK_UN);
1034 return;
1035 bad:
1036 hist_finish();
1037 }
1038
1039 void
hist_finish(void)1040 hist_finish(void)
1041 {
1042 (void)flock(histfd, LOCK_UN);
1043 (void)close(histfd);
1044 histfd = 0;
1045 }
1046
1047 /*
1048 * add magic to the history file
1049 */
1050 static int
sprinkle(int fd)1051 sprinkle(int fd)
1052 {
1053 static const unsigned char mag[] = { HMAGIC1, HMAGIC2 };
1054
1055 return (write(fd, mag, 2) != 2);
1056 }
1057 #endif
1058
1059 #if !HAVE_SYS_SIGNAME
1060 static const struct mksh_sigpair {
1061 const char *const name;
1062 int nr;
1063 } mksh_sigpairs[] = {
1064 #include "signames.inc"
1065 { NULL, 0 }
1066 };
1067 #endif
1068
1069 void
inittraps(void)1070 inittraps(void)
1071 {
1072 int i;
1073 const char *cs;
1074
1075 trap_exstat = -1;
1076
1077 /* Populate sigtraps based on sys_signame and sys_siglist. */
1078 for (i = 0; i <= NSIG; i++) {
1079 sigtraps[i].signal = i;
1080 if (i == ksh_SIGERR) {
1081 sigtraps[i].name = "ERR";
1082 sigtraps[i].mess = "Error handler";
1083 } else {
1084 #if HAVE_SYS_SIGNAME
1085 cs = sys_signame[i];
1086 #else
1087 const struct mksh_sigpair *pair = mksh_sigpairs;
1088 while ((pair->nr != i) && (pair->name != NULL))
1089 ++pair;
1090 cs = pair->name;
1091 #endif
1092 if ((cs == NULL) ||
1093 (cs[0] == '\0'))
1094 sigtraps[i].name = shf_smprintf("%d", i);
1095 else {
1096 char *s;
1097
1098 if (!strncasecmp(cs, "SIG", 3))
1099 cs += 3;
1100 strdupx(s, cs, APERM);
1101 sigtraps[i].name = s;
1102 while ((*s = ksh_toupper(*s)))
1103 ++s;
1104 }
1105 #if HAVE_SYS_SIGLIST
1106 sigtraps[i].mess = sys_siglist[i];
1107 #elif HAVE_STRSIGNAL
1108 sigtraps[i].mess = strsignal(i);
1109 #else
1110 sigtraps[i].mess = NULL;
1111 #endif
1112 if ((sigtraps[i].mess == NULL) ||
1113 (sigtraps[i].mess[0] == '\0'))
1114 sigtraps[i].mess = shf_smprintf("%s %d",
1115 "Signal", i);
1116 }
1117 }
1118 /* our name for signal 0 */
1119 sigtraps[ksh_SIGEXIT].name = "EXIT";
1120
1121 (void)sigemptyset(&Sigact_ign.sa_mask);
1122 Sigact_ign.sa_flags = 0; /* interruptible */
1123 Sigact_ign.sa_handler = SIG_IGN;
1124
1125 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
1126 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
1127 /* SIGTERM is not fatal for interactive */
1128 sigtraps[SIGTERM].flags |= TF_DFL_INTR;
1129 sigtraps[SIGHUP].flags |= TF_FATAL;
1130 sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
1131
1132 /* these are always caught so we can clean up any temporary files. */
1133 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
1134 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
1135 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
1136 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
1137 }
1138
1139 static void alarm_catcher(int sig);
1140
1141 void
alarm_init(void)1142 alarm_init(void)
1143 {
1144 sigtraps[SIGALRM].flags |= TF_SHELL_USES;
1145 setsig(&sigtraps[SIGALRM], alarm_catcher,
1146 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
1147 }
1148
1149 /* ARGSUSED */
1150 static void
alarm_catcher(int sig MKSH_A_UNUSED)1151 alarm_catcher(int sig MKSH_A_UNUSED)
1152 {
1153 /* this runs inside interrupt context, with errno saved */
1154
1155 if (ksh_tmout_state == TMOUT_READING) {
1156 int left = alarm(0);
1157
1158 if (left == 0) {
1159 ksh_tmout_state = TMOUT_LEAVING;
1160 intrsig = 1;
1161 } else
1162 alarm(left);
1163 }
1164 }
1165
1166 Trap *
gettrap(const char * name,int igncase)1167 gettrap(const char *name, int igncase)
1168 {
1169 int n = NSIG + 1;
1170 Trap *p;
1171 const char *n2;
1172 int (*cmpfunc)(const char *, const char *) = strcmp;
1173
1174 if (ksh_isdigit(*name)) {
1175 if (getn(name, &n) && 0 <= n && n < NSIG)
1176 return (&sigtraps[n]);
1177 else
1178 return (NULL);
1179 }
1180
1181 n2 = strncasecmp(name, "SIG", 3) ? NULL : name + 3;
1182 if (igncase)
1183 cmpfunc = strcasecmp;
1184 for (p = sigtraps; --n >= 0; p++)
1185 if (!cmpfunc(p->name, name) || (n2 && !cmpfunc(p->name, n2)))
1186 return (p);
1187 return (NULL);
1188 }
1189
1190 /*
1191 * trap signal handler
1192 */
1193 void
trapsig(int i)1194 trapsig(int i)
1195 {
1196 Trap *p = &sigtraps[i];
1197 int errno_sv = errno;
1198
1199 trap = p->set = 1;
1200 if (p->flags & TF_DFL_INTR)
1201 intrsig = 1;
1202 if ((p->flags & TF_FATAL) && !p->trap) {
1203 fatal_trap = 1;
1204 intrsig = 1;
1205 }
1206 if (p->shtrap)
1207 (*p->shtrap)(i);
1208 errno = errno_sv;
1209 }
1210
1211 /*
1212 * called when we want to allow the user to ^C out of something - won't
1213 * work if user has trapped SIGINT.
1214 */
1215 void
intrcheck(void)1216 intrcheck(void)
1217 {
1218 if (intrsig)
1219 runtraps(TF_DFL_INTR|TF_FATAL);
1220 }
1221
1222 /*
1223 * called after EINTR to check if a signal with normally causes process
1224 * termination has been received.
1225 */
1226 int
fatal_trap_check(void)1227 fatal_trap_check(void)
1228 {
1229 int i;
1230 Trap *p;
1231
1232 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
1233 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
1234 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
1235 /* return value is used as an exit code */
1236 return (128 + p->signal);
1237 return (0);
1238 }
1239
1240 /*
1241 * Returns the signal number of any pending traps: ie, a signal which has
1242 * occurred for which a trap has been set or for which the TF_DFL_INTR flag
1243 * is set.
1244 */
1245 int
trap_pending(void)1246 trap_pending(void)
1247 {
1248 int i;
1249 Trap *p;
1250
1251 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
1252 if (p->set && ((p->trap && p->trap[0]) ||
1253 ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
1254 return (p->signal);
1255 return (0);
1256 }
1257
1258 /*
1259 * run any pending traps. If intr is set, only run traps that
1260 * can interrupt commands.
1261 */
1262 void
runtraps(int flag)1263 runtraps(int flag)
1264 {
1265 int i;
1266 Trap *p;
1267
1268 if (ksh_tmout_state == TMOUT_LEAVING) {
1269 ksh_tmout_state = TMOUT_EXECUTING;
1270 warningf(false, "timed out waiting for input");
1271 unwind(LEXIT);
1272 } else
1273 /*
1274 * XXX: this means the alarm will have no effect if a trap
1275 * is caught after the alarm() was started...not good.
1276 */
1277 ksh_tmout_state = TMOUT_EXECUTING;
1278 if (!flag)
1279 trap = 0;
1280 if (flag & TF_DFL_INTR)
1281 intrsig = 0;
1282 if (flag & TF_FATAL)
1283 fatal_trap = 0;
1284 ++trap_nested;
1285 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
1286 if (p->set && (!flag ||
1287 ((p->flags & flag) && p->trap == NULL)))
1288 runtrap(p, false);
1289 if (!--trap_nested)
1290 runtrap(NULL, true);
1291 }
1292
1293 void
runtrap(Trap * p,bool is_last)1294 runtrap(Trap *p, bool is_last)
1295 {
1296 int old_changed = 0, i;
1297 char *trapstr;
1298
1299 if (p == NULL)
1300 /* just clean up, see runtraps() above */
1301 goto donetrap;
1302 i = p->signal;
1303 trapstr = p->trap;
1304 p->set = 0;
1305 if (trapstr == NULL) {
1306 /* SIG_DFL */
1307 if (p->flags & TF_FATAL) {
1308 /* eg, SIGHUP */
1309 exstat = 128 + i;
1310 unwind(LLEAVE);
1311 }
1312 if (p->flags & TF_DFL_INTR) {
1313 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
1314 exstat = 128 + i;
1315 unwind(LINTR);
1316 }
1317 goto donetrap;
1318 }
1319 if (trapstr[0] == '\0')
1320 /* SIG_IGN */
1321 goto donetrap;
1322 if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
1323 /* avoid recursion on these */
1324 old_changed = p->flags & TF_CHANGED;
1325 p->flags &= ~TF_CHANGED;
1326 p->trap = NULL;
1327 }
1328 if (trap_exstat == -1)
1329 trap_exstat = exstat;
1330 /*
1331 * Note: trapstr is fully parsed before anything is executed, thus
1332 * no problem with afree(p->trap) in settrap() while still in use.
1333 */
1334 command(trapstr, current_lineno);
1335 if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
1336 if (p->flags & TF_CHANGED)
1337 /* don't clear TF_CHANGED */
1338 afree(trapstr, APERM);
1339 else
1340 p->trap = trapstr;
1341 p->flags |= old_changed;
1342 }
1343
1344 donetrap:
1345 /* we're the last trap of a sequence executed */
1346 if (is_last && trap_exstat != -1) {
1347 exstat = trap_exstat;
1348 trap_exstat = -1;
1349 }
1350 }
1351
1352 /* clear pending traps and reset user's trap handlers; used after fork(2) */
1353 void
cleartraps(void)1354 cleartraps(void)
1355 {
1356 int i;
1357 Trap *p;
1358
1359 trap = 0;
1360 intrsig = 0;
1361 fatal_trap = 0;
1362 for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
1363 p->set = 0;
1364 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
1365 settrap(p, NULL);
1366 }
1367 }
1368
1369 /* restore signals just before an exec(2) */
1370 void
restoresigs(void)1371 restoresigs(void)
1372 {
1373 int i;
1374 Trap *p;
1375
1376 for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
1377 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
1378 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
1379 SS_RESTORE_CURR|SS_FORCE);
1380 }
1381
1382 void
settrap(Trap * p,const char * s)1383 settrap(Trap *p, const char *s)
1384 {
1385 sig_t f;
1386
1387 if (p->trap)
1388 afree(p->trap, APERM);
1389 /* handles s == NULL */
1390 strdupx(p->trap, s, APERM);
1391 p->flags |= TF_CHANGED;
1392 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
1393
1394 p->flags |= TF_USER_SET;
1395 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
1396 f = trapsig;
1397 else if (p->flags & TF_SHELL_USES) {
1398 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
1399 /* do what user wants at exec time */
1400 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
1401 if (f == SIG_IGN)
1402 p->flags |= TF_EXEC_IGN;
1403 else
1404 p->flags |= TF_EXEC_DFL;
1405 }
1406
1407 /*
1408 * assumes handler already set to what shell wants it
1409 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
1410 */
1411 return;
1412 }
1413
1414 /* todo: should we let user know signal is ignored? how? */
1415 setsig(p, f, SS_RESTORE_CURR|SS_USER);
1416 }
1417
1418 /*
1419 * Called by c_print() when writing to a co-process to ensure SIGPIPE won't
1420 * kill shell (unless user catches it and exits)
1421 */
1422 int
block_pipe(void)1423 block_pipe(void)
1424 {
1425 int restore_dfl = 0;
1426 Trap *p = &sigtraps[SIGPIPE];
1427
1428 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
1429 setsig(p, SIG_IGN, SS_RESTORE_CURR);
1430 if (p->flags & TF_ORIG_DFL)
1431 restore_dfl = 1;
1432 } else if (p->cursig == SIG_DFL) {
1433 setsig(p, SIG_IGN, SS_RESTORE_CURR);
1434 /* restore to SIG_DFL */
1435 restore_dfl = 1;
1436 }
1437 return (restore_dfl);
1438 }
1439
1440 /* Called by c_print() to undo whatever block_pipe() did */
1441 void
restore_pipe(int restore_dfl)1442 restore_pipe(int restore_dfl)
1443 {
1444 if (restore_dfl)
1445 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
1446 }
1447
1448 /*
1449 * Set action for a signal. Action may not be set if original
1450 * action was SIG_IGN, depending on the value of flags and FTALKING.
1451 */
1452 int
setsig(Trap * p,sig_t f,int flags)1453 setsig(Trap *p, sig_t f, int flags)
1454 {
1455 struct sigaction sigact;
1456
1457 if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR)
1458 return (1);
1459
1460 /*
1461 * First time setting this signal? If so, get and note the current
1462 * setting.
1463 */
1464 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
1465 sigaction(p->signal, &Sigact_ign, &sigact);
1466 p->flags |= sigact.sa_handler == SIG_IGN ?
1467 TF_ORIG_IGN : TF_ORIG_DFL;
1468 p->cursig = SIG_IGN;
1469 }
1470
1471 /*-
1472 * Generally, an ignored signal stays ignored, except if
1473 * - the user of an interactive shell wants to change it
1474 * - the shell wants for force a change
1475 */
1476 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
1477 (!(flags & SS_USER) || !Flag(FTALKING)))
1478 return (0);
1479
1480 setexecsig(p, flags & SS_RESTORE_MASK);
1481
1482 /*
1483 * This is here 'cause there should be a way of clearing
1484 * shtraps, but don't know if this is a sane way of doing
1485 * it. At the moment, all users of shtrap are lifetime
1486 * users (SIGALRM, SIGCHLD, SIGWINCH).
1487 */
1488 if (!(flags & SS_USER))
1489 p->shtrap = (sig_t)NULL;
1490 if (flags & SS_SHTRAP) {
1491 p->shtrap = f;
1492 f = trapsig;
1493 }
1494
1495 if (p->cursig != f) {
1496 p->cursig = f;
1497 (void)sigemptyset(&sigact.sa_mask);
1498 /* interruptible */
1499 sigact.sa_flags = 0;
1500 sigact.sa_handler = f;
1501 sigaction(p->signal, &sigact, NULL);
1502 }
1503
1504 return (1);
1505 }
1506
1507 /* control what signal is set to before an exec() */
1508 void
setexecsig(Trap * p,int restore)1509 setexecsig(Trap *p, int restore)
1510 {
1511 /* XXX debugging */
1512 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
1513 internal_errorf("setexecsig: unset signal %d(%s)",
1514 p->signal, p->name);
1515
1516 /* restore original value for exec'd kids */
1517 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
1518 switch (restore & SS_RESTORE_MASK) {
1519 case SS_RESTORE_CURR:
1520 /* leave things as they currently are */
1521 break;
1522 case SS_RESTORE_ORIG:
1523 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
1524 break;
1525 case SS_RESTORE_DFL:
1526 p->flags |= TF_EXEC_DFL;
1527 break;
1528 case SS_RESTORE_IGN:
1529 p->flags |= TF_EXEC_IGN;
1530 break;
1531 }
1532 }
1533