1 /*
2 * command history
3 *
4 * only implements in-memory history.
5 */
6
7 /*
8 * This file contains
9 * a) the original in-memory history mechanism
10 * b) a simple file saving history mechanism done by sjg@zen
11 * define EASY_HISTORY to get this
12 * c) a more complicated mechanism done by pc@hillside.co.uk
13 * that more closely follows the real ksh way of doing
14 * things. You need to have the mmap system call for this
15 * to work on your system
16 */
17
18 #include "sh.h"
19 #include "ksh_stat.h"
20
21 #ifdef HISTORY
22 # ifdef EASY_HISTORY
23
24 # ifndef HISTFILE
25 # ifdef OS2
26 # define HISTFILE "history.ksh"
27 # else /* OS2 */
28 # define HISTFILE ".pdksh_history"
29 # endif /* OS2 */
30 # endif
31
32 # else
33 /* Defines and includes for the complicated case */
34
35 # include <sys/file.h>
36 # include <sys/mman.h>
37
38 /*
39 * variables for handling the data file
40 */
41 static int histfd;
42 static int hsize;
43
44 static int hist_count_lines ARGS((unsigned char *, int));
45 static int hist_shrink ARGS((unsigned char *, int));
46 static unsigned char *hist_skip_back ARGS((unsigned char *,int *,int));
47 static void histload ARGS((Source *, unsigned char *, int));
48 static void histinsert ARGS((Source *, int, unsigned char *));
49 static void writehistfile ARGS((int, char *));
50 static int sprinkle ARGS((int));
51
52 # ifdef MAP_FILE
53 # define MAP_FLAGS (MAP_FILE|MAP_PRIVATE)
54 # else
55 # define MAP_FLAGS MAP_PRIVATE
56 # endif
57
58 # endif /* of EASY_HISTORY */
59
60 static int hist_execute ARGS((char *cmd));
61 static int hist_replace ARGS((char **hp, const char *pat, const char *rep,
62 int global));
63 static char **hist_get ARGS((const char *str, int approx, int allow_cur));
64 static char **hist_get_newest ARGS((int allow_cur));
65 static char **hist_get_oldest ARGS(());
66 static void histbackup ARGS((void));
67
68 static char **current; /* current postition in history[] */
69 static int curpos; /* current index in history[] */
70 static char *hname; /* current name of history file */
71 static int hstarted; /* set after hist_init() called */
72 static Source *hist_source;
73
74
75 int
c_fc(wp)76 c_fc(wp)
77 char **wp;
78 {
79 struct shf *shf;
80 struct temp UNINITIALIZED(*tf);
81 char *p, *editor = (char *) 0;
82 int gflag = 0, lflag = 0, nflag = 0, sflag = 0, rflag = 0;
83 int optc;
84 char *first = (char *) 0, *last = (char *) 0;
85 char **hfirst, **hlast, **hp;
86
87 while ((optc = ksh_getopt(wp, &builtin_opt, "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != EOF)
88 switch (optc) {
89 case 'e':
90 p = builtin_opt.optarg;
91 if (strcmp(p, "-") == 0)
92 sflag++;
93 else {
94 editor = str_nsave(p, strlen(p) + 4, ATEMP);
95 strcat(editor, " $_");
96 }
97 break;
98 case 'g': /* non-at&t ksh */
99 gflag++;
100 break;
101 case 'l':
102 lflag++;
103 break;
104 case 'n':
105 nflag++;
106 break;
107 case 'r':
108 rflag++;
109 break;
110 case 's': /* posix version of -e - */
111 sflag++;
112 break;
113 /* kludge city - accept -num as -- -num (kind of) */
114 case '0': case '1': case '2': case '3': case '4':
115 case '5': case '6': case '7': case '8': case '9':
116 p = shf_smprintf("-%c%s",
117 optc, builtin_opt.optarg);
118 if (!first)
119 first = p;
120 else if (!last)
121 last = p;
122 else {
123 bi_errorf("too many arguments");
124 return 1;
125 }
126 break;
127 case '?':
128 return 1;
129 }
130 wp += builtin_opt.optind;
131
132 /* Substitute and execute command */
133 if (sflag) {
134 char *pat = (char *) 0, *rep = (char *) 0;
135
136 if (editor || lflag || nflag || rflag) {
137 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
138 return 1;
139 }
140
141 /* Check for pattern replacement argument */
142 if (*wp && **wp && (p = strchr(*wp + 1, '='))) {
143 pat = str_save(*wp, ATEMP);
144 p = pat + (p - *wp);
145 *p++ = '\0';
146 rep = p;
147 wp++;
148 }
149 /* Check for search prefix */
150 if (!first && (first = *wp))
151 wp++;
152 if (last || *wp) {
153 bi_errorf("too many arguments");
154 return 1;
155 }
156
157 hp = first ? hist_get(first, FALSE, FALSE)
158 : hist_get_newest(FALSE);
159 if (!hp)
160 return 1;
161 return hist_replace(hp, pat, rep, gflag);
162 }
163
164 if (editor && (lflag || nflag)) {
165 bi_errorf("can't use -l, -n with -e");
166 return 1;
167 }
168
169 if (!first && (first = *wp))
170 wp++;
171 if (!last && (last = *wp))
172 wp++;
173 if (*wp) {
174 bi_errorf("too many arguments");
175 return 1;
176 }
177 if (!first) {
178 hfirst = lflag ? hist_get("-16", TRUE, TRUE)
179 : hist_get_newest(FALSE);
180 if (!hfirst)
181 return 1;
182 /* can't fail if hfirst didn't fail */
183 hlast = hist_get_newest(FALSE);
184 } else {
185 /* POSIX says not an error if first/last out of bounds
186 * when range is specified; at&t ksh and pdksh allow out of
187 * bounds for -l as well.
188 */
189 hfirst = hist_get(first, (lflag || last) ? TRUE : FALSE,
190 lflag ? TRUE : FALSE);
191 if (!hfirst)
192 return 1;
193 hlast = last ? hist_get(last, TRUE, lflag ? TRUE : FALSE)
194 : (lflag ? hist_get_newest(FALSE) : hfirst);
195 if (!hlast)
196 return 1;
197 }
198 if (hfirst > hlast) {
199 char **temp;
200
201 temp = hfirst; hfirst = hlast; hlast = temp;
202 rflag = !rflag; /* POSIX */
203 }
204
205 /* List history */
206 if (lflag) {
207 char *s, *t;
208 const char *nfmt = nflag ? "\t" : "%d\t";
209
210 for (hp = rflag ? hlast : hfirst;
211 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
212 {
213 shf_fprintf(shl_stdout, nfmt,
214 hist_source->line - (int) (histptr - hp));
215 /* print multi-line commands correctly */
216 for (s = *hp; (t = strchr(s, '\n')); s = t)
217 shf_fprintf(shl_stdout, "%.*s\t", ++t - s, s);
218 shf_fprintf(shl_stdout, "%s\n", s);
219 }
220 shf_flush(shl_stdout);
221 return 0;
222 }
223
224 /* Run editor on selected lines, then run resulting commands */
225
226 tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
227 if (!(shf = tf->shf)) {
228 bi_errorf("cannot create temp file %s - %s",
229 tf->name, strerror(errno));
230 return 1;
231 }
232 for (hp = rflag ? hlast : hfirst;
233 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
234 shf_fprintf(shf, "%s\n", *hp);
235 if (shf_close(shf) == EOF) {
236 bi_errorf("error writing temporary file - %s", strerror(errno));
237 return 1;
238 }
239
240 /* Ignore setstr errors here (arbitrary) */
241 setstr(local("_", FALSE), tf->name, KSH_RETURN_ERROR);
242
243 /* XXX: source should not get trashed by this.. */
244 {
245 Source *sold = source;
246 int ret;
247
248 ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_");
249 source = sold;
250 if (ret)
251 return ret;
252 }
253
254 {
255 struct stat statb;
256 XString xs;
257 char *xp;
258 int n;
259
260 if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) {
261 bi_errorf("cannot open temp file %s", tf->name);
262 return 1;
263 }
264
265 n = fstat(shf_fileno(shf), &statb) < 0 ? 128
266 : statb.st_size + 1;
267 Xinit(xs, xp, n, hist_source->areap);
268 while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
269 xp += n;
270 if (Xnleft(xs, xp) <= 0)
271 XcheckN(xs, xp, Xlength(xs, xp));
272 }
273 if (n < 0) {
274 bi_errorf("error reading temp file %s - %s",
275 tf->name, strerror(shf_errno(shf)));
276 shf_close(shf);
277 return 1;
278 }
279 shf_close(shf);
280 *xp = '\0';
281 strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
282 return hist_execute(Xstring(xs, xp));
283 }
284 }
285
286 /* Save cmd in history, execute cmd (cmd gets trashed) */
287 static int
hist_execute(cmd)288 hist_execute(cmd)
289 char *cmd;
290 {
291 Source *sold;
292 int ret;
293 char *p, *q;
294
295 histbackup();
296
297 for (p = cmd; p; p = q) {
298 if ((q = strchr(p, '\n'))) {
299 *q++ = '\0'; /* kill the newline */
300 if (!*q) /* ignore trailing newline */
301 q = (char *) 0;
302 }
303 #ifdef EASY_HISTORY
304 if (p != cmd)
305 histappend(p, TRUE);
306 else
307 #endif /* EASY_HISTORY */
308 histsave(++(hist_source->line), p, 1);
309
310 shellf("%s\n", p); /* POSIX doesn't say this is done... */
311 if ((p = q)) /* restore \n (trailing \n not restored) */
312 q[-1] = '\n';
313 }
314
315 /* Commands are executed here instead of pushing them onto the
316 * input 'cause posix says the redirection and variable assignments
317 * in
318 * X=y fc -e - 42 2> /dev/null
319 * are to effect the repeated commands environment.
320 */
321 /* XXX: source should not get trashed by this.. */
322 sold = source;
323 ret = command(cmd);
324 source = sold;
325 return ret;
326 }
327
328 static int
hist_replace(hp,pat,rep,global)329 hist_replace(hp, pat, rep, global)
330 char **hp;
331 const char *pat;
332 const char *rep;
333 int global;
334 {
335 char *line;
336
337 if (!pat)
338 line = str_save(*hp, ATEMP);
339 else {
340 char *s, *s1;
341 int pat_len = strlen(pat);
342 int rep_len = strlen(rep);
343 int len;
344 XString xs;
345 char *xp;
346 int any_subst = 0;
347
348 Xinit(xs, xp, 128, ATEMP);
349 for (s = *hp; (s1 = strstr(s, pat))
350 && (!any_subst || global) ; s = s1 + pat_len)
351 {
352 any_subst = 1;
353 len = s1 - s;
354 XcheckN(xs, xp, len + rep_len);
355 memcpy(xp, s, len); /* first part */
356 xp += len;
357 memcpy(xp, rep, rep_len); /* replacement */
358 xp += rep_len;
359 }
360 if (!any_subst) {
361 bi_errorf("substitution failed");
362 return 1;
363 }
364 len = strlen(s) + 1;
365 XcheckN(xs, xp, len);
366 memcpy(xp, s, len);
367 xp += len;
368 line = Xclose(xs, xp);
369 }
370 return hist_execute(line);
371 }
372
373 /*
374 * get pointer to history given pattern
375 * pattern is a number or string
376 */
377 static char **
hist_get(str,approx,allow_cur)378 hist_get(str, approx, allow_cur)
379 const char *str;
380 int approx;
381 int allow_cur;
382 {
383 char **hp = (char **) 0;
384 int n;
385
386 if (getn(str, &n)) {
387 hp = histptr + (n < 0 ? n : (n - hist_source->line));
388 if (hp < history) {
389 if (approx)
390 hp = hist_get_oldest();
391 else {
392 bi_errorf("%s: not in history", str);
393 hp = (char **) 0;
394 }
395 } else if (hp > histptr) {
396 if (approx)
397 hp = hist_get_newest(allow_cur);
398 else {
399 bi_errorf("%s: not in history", str);
400 hp = (char **) 0;
401 }
402 } else if (!allow_cur && hp == histptr) {
403 bi_errorf("%s: invalid range", str);
404 hp = (char **) 0;
405 }
406 } else {
407 int anchored = *str == '?' ? (++str, 0) : 1;
408
409 /* the -1 is to avoid the current fc command */
410 n = findhist(histptr - history - 1, 0, str, anchored);
411 if (n < 0) {
412 bi_errorf("%s: not in history", str);
413 hp = (char **) 0;
414 } else
415 hp = &history[n];
416 }
417 return hp;
418 }
419
420 /* Return a pointer to the newest command in the history */
421 static char **
hist_get_newest(allow_cur)422 hist_get_newest(allow_cur)
423 int allow_cur;
424 {
425 if (histptr < history || (!allow_cur && histptr == history)) {
426 bi_errorf("no history (yet)");
427 return (char **) 0;
428 }
429 if (allow_cur)
430 return histptr;
431 return histptr - 1;
432 }
433
434 /* Return a pointer to the newest command in the history */
435 static char **
hist_get_oldest()436 hist_get_oldest()
437 {
438 if (histptr <= history) {
439 bi_errorf("no history (yet)");
440 return (char **) 0;
441 }
442 return history;
443 }
444
445 /******************************/
446 /* Back up over last histsave */
447 /******************************/
448 static void
histbackup()449 histbackup()
450 {
451 static int last_line = -1;
452
453 if (histptr >= history && last_line != hist_source->line) {
454 hist_source->line--;
455 afree((void*)*histptr, APERM);
456 histptr--;
457 last_line = hist_source->line;
458 }
459 }
460
461 /*
462 * Return the current position.
463 */
464 char **
histpos()465 histpos()
466 {
467 return current;
468 }
469
470 int
histN()471 histN()
472 {
473 return curpos;
474 }
475
476 int
histnum(n)477 histnum(n)
478 int n;
479 {
480 int last = histptr - history;
481
482 if (n < 0 || n >= last) {
483 current = histptr;
484 curpos = last;
485 return last;
486 } else {
487 current = &history[n];
488 curpos = n;
489 return n;
490 }
491 }
492
493 /*
494 * This will become unecessary if hist_get is modified to allow
495 * searching from positions other than the end, and in either
496 * direction.
497 */
498 int
findhist(start,fwd,str,anchored)499 findhist(start, fwd, str, anchored)
500 int start;
501 int fwd;
502 const char *str;
503 int anchored;
504 {
505 char **hp;
506 int maxhist = histptr - history;
507 int incr = fwd ? 1 : -1;
508 int len = strlen(str);
509
510 if (start < 0 || start >= maxhist)
511 start = maxhist;
512
513 hp = &history[start];
514 for (; hp >= history && hp <= histptr; hp += incr)
515 if ((anchored && strncmp(*hp, str, len) == 0)
516 || (!anchored && strstr(*hp, str)))
517 return hp - history;
518
519 return -1;
520 }
521
522 /*
523 * set history
524 * this means reallocating the dataspace
525 */
526 void
sethistsize(n)527 sethistsize(n)
528 int n;
529 {
530 if (n > 0 && n != histsize) {
531 int cursize = histptr - history;
532
533 /* save most recent history */
534 if (n < cursize) {
535 memmove(history, histptr - n, n * sizeof(char *));
536 cursize = n;
537 }
538
539 history = (char **)aresize(history, n*sizeof(char *), APERM);
540
541 histsize = n;
542 histptr = history + cursize;
543 }
544 }
545
546 /*
547 * set history file
548 * This can mean reloading/resetting/starting history file
549 * maintenance
550 */
551 void
sethistfile(name)552 sethistfile(name)
553 const char *name;
554 {
555 /* if not started then nothing to do */
556 if (hstarted == 0)
557 return;
558
559 /* if the name is the same as the name we have */
560 if (hname && strcmp(hname, name) == 0)
561 return;
562
563 /*
564 * its a new name - possibly
565 */
566 # ifdef EASY_HISTORY
567 if (hname) {
568 afree(hname, APERM);
569 hname = NULL;
570 }
571 # else
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 # endif
584
585 hist_init(hist_source);
586 }
587
588 /*
589 * initialise the history vector
590 */
591 void
init_histvec()592 init_histvec()
593 {
594 if (history == (char **)NULL) {
595 histsize = HISTORYSIZE;
596 history = (char **)alloc(histsize*sizeof (char *), APERM);
597 histptr = history - 1;
598 }
599 }
600
601 # ifdef EASY_HISTORY
602 /*
603 * save command in history
604 */
605 void
histsave(lno,cmd,dowrite)606 histsave(lno, cmd, dowrite)
607 int lno; /* ignored (compatibility with COMPLEX_HISTORY) */
608 const char *cmd;
609 int dowrite; /* ignored (compatibility with COMPLEX_HISTORY) */
610 {
611 register char **hp = histptr;
612 char *cp;
613
614 if (++hp >= history + histsize) { /* remove oldest command */
615 afree((void*)history[0], APERM);
616 memmove(history, history + 1,
617 sizeof(history[0]) * (histsize - 1));
618 hp = &history[histsize - 1];
619 }
620 *hp = str_save(cmd, APERM);
621 /* trash trailing newline but allow imbedded newlines */
622 cp = *hp + strlen(*hp);
623 if (cp > *hp && cp[-1] == '\n')
624 cp[-1] = '\0';
625 histptr = hp;
626 }
627
628 /*
629 * Append an entry to the last saved command. Used for multiline
630 * commands
631 */
632 void
histappend(cmd,nl_separate)633 histappend(cmd, nl_separate)
634 const char *cmd;
635 int nl_separate;
636 {
637 int hlen, clen;
638 char *p;
639
640 hlen = strlen(*histptr);
641 clen = strlen(cmd);
642 if (clen > 0 && cmd[clen-1] == '\n')
643 clen--;
644 p = *histptr = (char *) aresize(*histptr, hlen + clen + 2, APERM);
645 p += hlen;
646 if (nl_separate)
647 *p++ = '\n';
648 memcpy(p, cmd, clen);
649 p[clen] = '\0';
650 }
651
652 /*
653 * 92-04-25 <sjg@zen>
654 * A simple history file implementation.
655 * At present we only save the history when we exit.
656 * This can cause problems when there are multiple shells are
657 * running under the same user-id. The last shell to exit gets
658 * to save its history.
659 */
660 void
hist_init(s)661 hist_init(s)
662 Source *s;
663 {
664 char *f;
665 FILE *fh;
666
667 if (Flag(FTALKING) == 0)
668 return;
669
670 hstarted = 1;
671
672 hist_source = s;
673
674 if ((f = str_val(global("HISTFILE"))) == NULL || *f == '\0') {
675 # if 1 /* Don't use history file unless the user asks for it */
676 hname = NULL;
677 return;
678 # else
679 char *home = str_val(global("HOME"));
680 int len;
681
682 if (home == NULL)
683 home = null;
684 f = HISTFILE;
685 hname = alloc(len = strlen(home) + strlen(f) + 2, APERM);
686 shf_snprintf(hname, len, "%s/%s", home, f);
687 # endif
688 } else
689 hname = str_save(f, APERM);
690
691 if ((fh = fopen(hname, "r"))) {
692 int pos = 0, nread = 0;
693 int contin = 0; /* continuation of previous command */
694 char *end;
695 char hline[LINE + 1];
696
697 while (1) {
698 if (pos >= nread) {
699 pos = 0;
700 nread = fread(hline, 1, LINE, fh);
701 if (nread <= 0)
702 break;
703 hline[nread] = '\0';
704 }
705 end = strchr(hline + pos, 0); /* will always succeed */
706 if (contin)
707 histappend(hline + pos, 0);
708 else {
709 hist_source->line++;
710 histsave(0, hline + pos, 0);
711 }
712 pos = end - hline + 1;
713 contin = end == &hline[nread];
714 }
715 fclose(fh);
716 }
717 }
718
719 /*
720 * save our history.
721 * We check that we do not have more than we are allowed.
722 * If the history file is read-only we do nothing.
723 * Handy for having all shells start with a useful history set.
724 */
725
726 void
hist_finish()727 hist_finish()
728 {
729 static int once;
730 FILE *fh;
731 register int i;
732 register char **hp;
733
734 if (once++)
735 return;
736 /* check how many we have */
737 i = histptr - history;
738 if (i >= histsize)
739 hp = &histptr[-histsize];
740 else
741 hp = history;
742 if (hname && (fh = fopen(hname, "w")))
743 {
744 for (i = 0; hp + i <= histptr && hp[i]; i++)
745 fprintf(fh, "%s%c", hp[i], '\0');
746 fclose(fh);
747 }
748 }
749
750 # else /* EASY_HISTORY */
751
752 /*
753 * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
754 * a) permit HISTSIZE to control number of lines of history stored
755 * b) maintain a physical history file
756 *
757 * It turns out that there is a lot of ghastly hackery here
758 */
759
760
761 /*
762 * save command in history
763 */
764 void
histsave(lno,cmd,dowrite)765 histsave(lno, cmd, dowrite)
766 int lno;
767 const char *cmd;
768 int dowrite;
769 {
770 register char **hp;
771 char *c, *cp;
772
773 c = str_save(cmd, APERM);
774 if ((cp = strchr(c, '\n')) != NULL)
775 *cp = '\0';
776
777 if (histfd && dowrite)
778 writehistfile(lno, c);
779
780 hp = histptr;
781
782 if (++hp >= history + histsize) { /* remove oldest command */
783 afree((void*)*history, APERM);
784 for (hp = history; hp < history + histsize - 1; hp++)
785 hp[0] = hp[1];
786 }
787 *hp = c;
788 histptr = hp;
789 }
790
791 /*
792 * Write history data to a file nominated by HISTFILE
793 * if HISTFILE is unset then history still happens, but
794 * the data is not written to a file
795 * All copies of ksh looking at the file will maintain the
796 * same history. This is ksh behaviour.
797 *
798 * This stuff uses mmap()
799 * if your system ain't got it - then you'll have to undef HISTORYFILE
800 */
801
802 /*
803 * Open a history file
804 * Format is:
805 * Bytes 1, 2: HMAGIC - just to check that we are dealing with
806 * the correct object
807 * Then follows a number of stored commands
808 * Each command is
809 * <command byte><command number(4 bytes)><bytes><null>
810 */
811 # define HMAGIC1 0xab
812 # define HMAGIC2 0xcd
813 # define COMMAND 0xff
814
815 void
hist_init(s)816 hist_init(s)
817 Source *s;
818 {
819 unsigned char *base;
820 int lines;
821 int fd;
822
823 if (Flag(FTALKING) == 0)
824 return;
825
826 hstarted = 1;
827
828 hist_source = s;
829
830 hname = str_val(global("HISTFILE"));
831 if (hname == NULL)
832 return;
833 hname = str_save(hname, APERM);
834
835 retry:
836 /* we have a file and are interactive */
837 if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0)
838 return;
839
840 histfd = savefd(fd, 0);
841
842 (void) flock(histfd, LOCK_EX);
843
844 hsize = lseek(histfd, 0L, SEEK_END);
845
846 if (hsize == 0) {
847 /* add magic */
848 if (sprinkle(histfd)) {
849 hist_finish();
850 return;
851 }
852 }
853 else if (hsize > 0) {
854 /*
855 * we have some data
856 */
857 base = (unsigned char *)mmap(0, hsize, PROT_READ, MAP_FLAGS, histfd, 0);
858 /*
859 * check on its validity
860 */
861 if ((int)base == -1 || *base != HMAGIC1 || base[1] != HMAGIC2) {
862 if ((int)base != -1)
863 munmap((caddr_t)base, hsize);
864 hist_finish();
865 unlink(hname);
866 goto retry;
867 }
868 if (hsize > 2) {
869 lines = hist_count_lines(base+2, hsize-2);
870 if (lines > histsize) {
871 /* we need to make the file smaller */
872 if (hist_shrink(base, hsize))
873 unlink(hname);
874 munmap((caddr_t)base, hsize);
875 hist_finish();
876 goto retry;
877 }
878 }
879 histload(hist_source, base+2, hsize-2);
880 munmap((caddr_t)base, hsize);
881 }
882 (void) flock(histfd, LOCK_UN);
883 hsize = lseek(histfd, 0L, SEEK_END);
884 }
885
886 typedef enum state {
887 shdr, /* expecting a header */
888 sline, /* looking for a null byte to end the line */
889 sn1, /* bytes 1 to 4 of a line no */
890 sn2, sn3, sn4,
891 } State;
892
893 static int
hist_count_lines(base,bytes)894 hist_count_lines(base, bytes)
895 register unsigned char *base;
896 register int bytes;
897 {
898 State state = shdr;
899 register lines = 0;
900
901 while (bytes--) {
902 switch (state)
903 {
904 case shdr:
905 if (*base == COMMAND)
906 state = sn1;
907 break;
908 case sn1:
909 state = sn2; break;
910 case sn2:
911 state = sn3; break;
912 case sn3:
913 state = sn4; break;
914 case sn4:
915 state = sline; break;
916 case sline:
917 if (*base == '\0')
918 lines++, state = shdr;
919 }
920 base++;
921 }
922 return lines;
923 }
924
925 /*
926 * Shrink the history file to histsize lines
927 */
928 static int
hist_shrink(oldbase,oldbytes)929 hist_shrink(oldbase, oldbytes)
930 unsigned char *oldbase;
931 int oldbytes;
932 {
933 int fd;
934 char nfile[1024];
935 struct stat statb;
936 unsigned char *nbase = oldbase;
937 int nbytes = oldbytes;
938
939 nbase = hist_skip_back(nbase, &nbytes, histsize);
940 if (nbase == NULL)
941 return 1;
942 if (nbase == oldbase)
943 return 0;
944
945 /*
946 * create temp file
947 */
948 (void) shf_snprintf(nfile, sizeof(nfile), "%s.%d", hname, procpid);
949 if ((fd = creat(nfile, 0600)) < 0)
950 return 1;
951
952 if (sprinkle(fd)) {
953 close(fd);
954 unlink(nfile);
955 return 1;
956 }
957 if (write(fd, nbase, nbytes) != nbytes) {
958 close(fd);
959 unlink(nfile);
960 return 1;
961 }
962 /*
963 * worry about who owns this file
964 */
965 if (fstat(histfd, &statb) >= 0)
966 fchown(fd, statb.st_uid, statb.st_gid);
967 close(fd);
968
969 /*
970 * rename
971 */
972 if (rename(nfile, hname) < 0)
973 return 1;
974 return 0;
975 }
976
977
978 /*
979 * find a pointer to the data `no' back from the end of the file
980 * return the pointer and the number of bytes left
981 */
982 static unsigned char *
hist_skip_back(base,bytes,no)983 hist_skip_back(base, bytes, no)
984 unsigned char *base;
985 int *bytes;
986 int no;
987 {
988 register int lines = 0;
989 register unsigned char *ep;
990
991 for (ep = base + *bytes; --ep > base; ) {
992 /* this doesn't really work: the 4 byte line number that is
993 * encoded after the COMMAND byte can itself contain the
994 * COMMAND byte....
995 */
996 for (; ep > base && *ep != COMMAND; ep--)
997 ;
998 if (ep == base)
999 break;
1000 if (++lines == no) {
1001 *bytes = *bytes - ((char *)ep - (char *)base);
1002 return ep;
1003 }
1004 }
1005 return NULL;
1006 }
1007
1008 /*
1009 * load the history structure from the stored data
1010 */
1011 static void
histload(s,base,bytes)1012 histload(s, base, bytes)
1013 Source *s;
1014 register unsigned char *base;
1015 register int bytes;
1016 {
1017 State state;
1018 int lno;
1019 unsigned char *line;
1020
1021 for (state = shdr; bytes-- > 0; base++) {
1022 switch (state) {
1023 case shdr:
1024 if (*base == COMMAND)
1025 state = sn1;
1026 break;
1027 case sn1:
1028 lno = (((*base)&0xff)<<24);
1029 state = sn2;
1030 break;
1031 case sn2:
1032 lno |= (((*base)&0xff)<<16);
1033 state = sn3;
1034 break;
1035 case sn3:
1036 lno |= (((*base)&0xff)<<8);
1037 state = sn4;
1038 break;
1039 case sn4:
1040 lno |= (*base)&0xff;
1041 line = base+1;
1042 state = sline;
1043 break;
1044 case sline:
1045 if (*base == '\0') {
1046 /* worry about line numbers */
1047 if (histptr >= history && lno-1 != s->line) {
1048 /* a replacement ? */
1049 histinsert(s, lno, line);
1050 }
1051 else {
1052 s->line = lno;
1053 histsave(lno, (char *)line, 0);
1054 }
1055 state = shdr;
1056 }
1057 }
1058 }
1059 }
1060
1061 /*
1062 * Insert a line into the history at a specified number
1063 */
1064 static void
histinsert(s,lno,line)1065 histinsert(s, lno, line)
1066 Source *s;
1067 int lno;
1068 unsigned char *line;
1069 {
1070 register char **hp;
1071
1072 if (lno >= s->line-(histptr-history) && lno <= s->line) {
1073 hp = &histptr[lno-s->line];
1074 if (*hp)
1075 afree((void*)*hp, APERM);
1076 *hp = str_save((char *)line, APERM);
1077 }
1078 }
1079
1080 /*
1081 * write a command to the end of the history file
1082 * This *MAY* seem easy but it's also necessary to check
1083 * that the history file has not changed in size.
1084 * If it has - then some other shell has written to it
1085 * and we should read those commands to update our history
1086 */
1087 static void
writehistfile(lno,cmd)1088 writehistfile(lno, cmd)
1089 int lno;
1090 char *cmd;
1091 {
1092 int sizenow;
1093 unsigned char *base;
1094 unsigned char *new;
1095 int bytes;
1096 char hdr[5];
1097
1098 (void) flock(histfd, LOCK_EX);
1099 sizenow = lseek(histfd, 0L, SEEK_END);
1100 if (sizenow != hsize) {
1101 /*
1102 * Things have changed
1103 */
1104 if (sizenow > hsize) {
1105 /* someone has added some lines */
1106 bytes = sizenow - hsize;
1107 base = (unsigned char *)mmap(0, sizenow, PROT_READ, MAP_FLAGS, histfd, 0);
1108 if ((int)base == -1)
1109 goto bad;
1110 new = base + hsize;
1111 if (*new != COMMAND) {
1112 munmap((caddr_t)base, sizenow);
1113 goto bad;
1114 }
1115 hist_source->line--;
1116 histload(hist_source, new, bytes);
1117 hist_source->line++;
1118 lno = hist_source->line;
1119 munmap((caddr_t)base, sizenow);
1120 hsize = sizenow;
1121 } else {
1122 /* it has shrunk */
1123 /* but to what? */
1124 /* we'll give up for now */
1125 goto bad;
1126 }
1127 }
1128 /*
1129 * we can write our bit now
1130 */
1131 hdr[0] = COMMAND;
1132 hdr[1] = (lno>>24)&0xff;
1133 hdr[2] = (lno>>16)&0xff;
1134 hdr[3] = (lno>>8)&0xff;
1135 hdr[4] = lno&0xff;
1136 (void) write(histfd, hdr, 5);
1137 (void) write(histfd, cmd, strlen(cmd)+1);
1138 hsize = lseek(histfd, 0L, SEEK_END);
1139 (void) flock(histfd, LOCK_UN);
1140 return;
1141 bad:
1142 hist_finish();
1143 }
1144
1145 void
hist_finish()1146 hist_finish()
1147 {
1148 (void) flock(histfd, LOCK_UN);
1149 (void) close(histfd);
1150 histfd = 0;
1151 }
1152
1153 /*
1154 * add magic to the history file
1155 */
1156 static int
sprinkle(fd)1157 sprinkle(fd)
1158 int fd;
1159 {
1160 static char mag[] = { HMAGIC1, HMAGIC2 };
1161
1162 return(write(fd, mag, 2) != 2);
1163 }
1164
1165 # endif
1166 #else /* HISTORY */
1167
1168 /* No history to be compiled in: dummy routines to avoid lots more ifdefs */
1169 void
init_histvec()1170 init_histvec()
1171 {
1172 }
1173 void
hist_init(s)1174 hist_init(s)
1175 Source *s;
1176 {
1177 }
1178 void
hist_finish()1179 hist_finish()
1180 {
1181 }
1182 void
histsave(lno,cmd,dowrite)1183 histsave(lno, cmd, dowrite)
1184 int lno;
1185 const char *cmd;
1186 int dowrite;
1187 {
1188 errorf("history not enabled");
1189 }
1190 #endif /* HISTORY */
1191