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