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