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