Bug Summary

File:src/bin/ksh/history.c
Warning:line 306, column 8
Although the value stored to 'p' is used in the enclosing expression, the value is never actually read from 'p'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name history.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/bin/ksh/obj -resource-dir /usr/local/lib/clang/13.0.0 -D EMACS -D VI -I . -I /usr/src/bin/ksh -I /usr/src/bin/ksh/../../lib/libc/gen -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/bin/ksh/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/bin/ksh/history.c
1/* $OpenBSD: history.c,v 1.84 2019/10/27 15:02:19 jca Exp $ */
2
3/*
4 * command history
5 */
6
7/*
8 * This file contains
9 * a) the original in-memory history mechanism
10 * b) a more complicated mechanism done by pc@hillside.co.uk
11 * that more closely follows the real ksh way of doing
12 * things.
13 */
14
15#include <sys/stat.h>
16
17#include <errno(*__errno()).h>
18#include <fcntl.h>
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <unistd.h>
23#include <vis.h>
24
25#include "sh.h"
26
27static void history_write(void);
28static FILE *history_open(void);
29static void history_load(Source *);
30static void history_close(void);
31
32static int hist_execute(char *);
33static int hist_replace(char **, const char *, const char *, int);
34static char **hist_get(const char *, int, int);
35static char **hist_get_oldest(void);
36static void histbackup(void);
37
38static FILE *histfh;
39static char **histbase; /* actual start of the history[] allocation */
40static char **current; /* current position in history[] */
41static char *hname; /* current name of history file */
42static int hstarted; /* set after hist_init() called */
43static int ignoredups; /* ditch duplicated history lines? */
44static int ignorespace; /* ditch lines starting with a space? */
45static Source *hist_source;
46static uint32_t line_co;
47
48static struct stat last_sb;
49
50static volatile sig_atomic_t c_fc_depth;
51
52int
53c_fc(char **wp)
54{
55 struct shf *shf;
56 struct temp *tf = NULL((void*)0);
57 char *p, *editor = NULL((void*)0);
58 int gflag = 0, lflag = 0, nflag = 0, sflag = 0, rflag = 0;
59 int optc, ret;
60 char *first = NULL((void*)0), *last = NULL((void*)0);
61 char **hfirst, **hlast, **hp;
62
63 if (c_fc_depth != 0) {
64 bi_errorf("history function called recursively");
65 return 1;
66 }
67
68 if (!Flag(FTALKING_I)(shell_flags[(int) (FTALKING_I)])) {
69 bi_errorf("history functions not available");
70 return 1;
71 }
72
73 while ((optc = ksh_getopt(wp, &builtin_opt,
74 "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
75 switch (optc) {
76 case 'e':
77 p = builtin_opt.optarg;
78 if (strcmp(p, "-") == 0)
79 sflag++;
80 else {
81 size_t len = strlen(p) + 4;
82 editor = str_nsave(p, len, ATEMP&genv->area);
83 strlcat(editor, " $_", len);
84 }
85 break;
86 case 'g': /* non-at&t ksh */
87 gflag++;
88 break;
89 case 'l':
90 lflag++;
91 break;
92 case 'n':
93 nflag++;
94 break;
95 case 'r':
96 rflag++;
97 break;
98 case 's': /* posix version of -e - */
99 sflag++;
100 break;
101 /* kludge city - accept -num as -- -num (kind of) */
102 case '0': case '1': case '2': case '3': case '4':
103 case '5': case '6': case '7': case '8': case '9':
104 p = shf_smprintf("-%c%s",
105 optc, builtin_opt.optarg);
106 if (!first)
107 first = p;
108 else if (!last)
109 last = p;
110 else {
111 bi_errorf("too many arguments");
112 return 1;
113 }
114 break;
115 case '?':
116 return 1;
117 }
118 wp += builtin_opt.optind;
119
120 /* Substitute and execute command */
121 if (sflag) {
122 char *pat = NULL((void*)0), *rep = NULL((void*)0);
123
124 if (editor || lflag || nflag || rflag) {
125 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
126 return 1;
127 }
128
129 /* Check for pattern replacement argument */
130 if (*wp && **wp && (p = strchr(*wp + 1, '='))) {
131 pat = str_save(*wp, ATEMP&genv->area);
132 p = pat + (p - *wp);
133 *p++ = '\0';
134 rep = p;
135 wp++;
136 }
137 /* Check for search prefix */
138 if (!first && (first = *wp))
139 wp++;
140 if (last || *wp) {
141 bi_errorf("too many arguments");
142 return 1;
143 }
144
145 hp = first ? hist_get(first, false0, false0) :
146 hist_get_newest(false0);
147 if (!hp)
148 return 1;
149 c_fc_depth++;
150 ret = hist_replace(hp, pat, rep, gflag);
151 c_fc_reset();
152 return ret;
153 }
154
155 if (editor && (lflag || nflag)) {
156 bi_errorf("can't use -l, -n with -e");
157 return 1;
158 }
159
160 if (!first && (first = *wp))
161 wp++;
162 if (!last && (last = *wp))
163 wp++;
164 if (*wp) {
165 bi_errorf("too many arguments");
166 return 1;
167 }
168 if (!first) {
169 hfirst = lflag ? hist_get("-16", true1, true1) :
170 hist_get_newest(false0);
171 if (!hfirst)
172 return 1;
173 /* can't fail if hfirst didn't fail */
174 hlast = hist_get_newest(false0);
175 } else {
176 /* POSIX says not an error if first/last out of bounds
177 * when range is specified; at&t ksh and pdksh allow out of
178 * bounds for -l as well.
179 */
180 hfirst = hist_get(first, (lflag || last) ? true1 : false0,
181 lflag ? true1 : false0);
182 if (!hfirst)
183 return 1;
184 hlast = last ? hist_get(last, true1, lflag ? true1 : false0) :
185 (lflag ? hist_get_newest(false0) : hfirst);
186 if (!hlast)
187 return 1;
188 }
189 if (hfirst > hlast) {
190 char **temp;
191
192 temp = hfirst; hfirst = hlast; hlast = temp;
193 rflag = !rflag; /* POSIX */
194 }
195
196 /* List history */
197 if (lflag) {
198 char *s, *t;
199 const char *nfmt = nflag ? "\t" : "%d\t";
200
201 for (hp = rflag ? hlast : hfirst;
202 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
203 shf_fprintf(shl_stdout(&shf_iob[1]), nfmt,
204 hist_source->line - (int) (histptr - hp));
205 /* print multi-line commands correctly */
206 for (s = *hp; (t = strchr(s, '\n')); s = t)
207 shf_fprintf(shl_stdout(&shf_iob[1]), "%.*s\t", ++t - s, s);
208 shf_fprintf(shl_stdout(&shf_iob[1]), "%s\n", s);
209 }
210 shf_flush(shl_stdout(&shf_iob[1]));
211 return 0;
212 }
213
214 /* Run editor on selected lines, then run resulting commands */
215
216 tf = maketemp(ATEMP&genv->area, TT_HIST_EDIT, &genv->temps);
217 if (!(shf = tf->shf)) {
218 bi_errorf("cannot create temp file %s - %s",
219 tf->name, strerror(errno(*__errno())));
220 return 1;
221 }
222 for (hp = rflag ? hlast : hfirst;
223 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
224 shf_fprintf(shf, "%s\n", *hp);
225 if (shf_close(shf) == EOF(-1)) {
226 bi_errorf("error writing temporary file - %s", strerror(errno(*__errno())));
227 return 1;
228 }
229
230 /* Ignore setstr errors here (arbitrary) */
231 setstr(local("_", false0), tf->name, KSH_RETURN_ERROR0x1);
232
233 /* XXX: source should not get trashed by this.. */
234 {
235 Source *sold = source;
236
237 ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_", 0);
238 source = sold;
239 if (ret)
240 return ret;
241 }
242
243 {
244 struct stat statb;
245 XString xs;
246 char *xp;
247 int n;
248
249 if (!(shf = shf_open(tf->name, O_RDONLY0x0000, 0, 0))) {
250 bi_errorf("cannot open temp file %s", tf->name);
251 return 1;
252 }
253
254 n = fstat(shf->fd, &statb) == -1 ? 128 :
255 statb.st_size + 1;
256 Xinit(xs, xp, n, hist_source->areap)do { (xs).len = n; (xs).areap = (hist_source->areap); (xs)
.beg = alloc((xs).len + 8, (xs).areap); (xs).end = (xs).beg +
(xs).len; xp = (xs).beg; } while (0)
;
257 while ((n = shf_read(xp, Xnleft(xs, xp)((xs).end - (xp)), shf)) > 0) {
258 xp += n;
259 if (Xnleft(xs, xp)((xs).end - (xp)) <= 0)
260 XcheckN(xs, xp, Xlength(xs, xp))do { ptrdiff_t more = ((xp) + (((xp) - (xs).beg))) - (xs).end
; if (more > 0) xp = Xcheck_grow_(&xs, xp, more); } while
(0)
;
261 }
262 if (n < 0) {
263 bi_errorf("error reading temp file %s - %s",
264 tf->name, strerror(shf->errno_));
265 shf_close(shf);
266 return 1;
267 }
268 shf_close(shf);
269 *xp = '\0';
270 strip_nuls(Xstring(xs, xp)((xs).beg), Xlength(xs, xp)((xp) - (xs).beg));
271 c_fc_depth++;
272 ret = hist_execute(Xstring(xs, xp)((xs).beg));
273 c_fc_reset();
274 return ret;
275 }
276}
277
278/* Reset the c_fc depth counter.
279 * Made available for when an fc call is interrupted.
280 */
281void
282c_fc_reset(void)
283{
284 c_fc_depth = 0;
285}
286
287/* Save cmd in history, execute cmd (cmd gets trashed) */
288static int
289hist_execute(char *cmd)
290{
291 Source *sold;
292 int ret;
293 char *p, *q;
294
295 histbackup();
296
297 for (p = cmd; p; p = q) {
298 if ((q = strchr(p, '\n'))) {
299 *q++ = '\0'; /* kill the newline */
300 if (!*q) /* ignore trailing newline */
301 q = NULL((void*)0);
302 }
303 histsave(++(hist_source->line), p, 1);
304
305 shellf("%s\n", p); /* POSIX doesn't say this is done... */
306 if ((p = q)) /* restore \n (trailing \n not restored) */
Although the value stored to 'p' is used in the enclosing expression, the value is never actually read from 'p'
307 q[-1] = '\n';
308 }
309
310 /* Commands are executed here instead of pushing them onto the
311 * input 'cause posix says the redirection and variable assignments
312 * in
313 * X=y fc -e - 42 2> /dev/null
314 * are to effect the repeated commands environment.
315 */
316 /* XXX: source should not get trashed by this.. */
317 sold = source;
318 ret = command(cmd, 0);
319 source = sold;
320 return ret;
321}
322
323static int
324hist_replace(char **hp, const char *pat, const char *rep, int global)
325{
326 char *line;
327
328 if (!pat)
329 line = str_save(*hp, ATEMP&genv->area);
330 else {
331 char *s, *s1;
332 int pat_len = strlen(pat);
333 int rep_len = strlen(rep);
334 int len;
335 XString xs;
336 char *xp;
337 int any_subst = 0;
338
339 Xinit(xs, xp, 128, ATEMP)do { (xs).len = 128; (xs).areap = (&genv->area); (xs).
beg = alloc((xs).len + 8, (xs).areap); (xs).end = (xs).beg + (
xs).len; xp = (xs).beg; } while (0)
;
340 for (s = *hp; (s1 = strstr(s, pat)) && (!any_subst || global);
341 s = s1 + pat_len) {
342 any_subst = 1;
343 len = s1 - s;
344 XcheckN(xs, xp, len + rep_len)do { ptrdiff_t more = ((xp) + (len + rep_len)) - (xs).end; if
(more > 0) xp = Xcheck_grow_(&xs, xp, more); } while (
0)
;
345 memcpy(xp, s, len); /* first part */
346 xp += len;
347 memcpy(xp, rep, rep_len); /* replacement */
348 xp += rep_len;
349 }
350 if (!any_subst) {
351 bi_errorf("substitution failed");
352 return 1;
353 }
354 len = strlen(s) + 1;
355 XcheckN(xs, xp, len)do { ptrdiff_t more = ((xp) + (len)) - (xs).end; if (more >
0) xp = Xcheck_grow_(&xs, xp, more); } while (0)
;
356 memcpy(xp, s, len);
357 xp += len;
358 line = Xclose(xs, xp)aresize((xs).beg, ((xp) - (xs).beg), (xs).areap);
359 }
360 return hist_execute(line);
361}
362
363/*
364 * get pointer to history given pattern
365 * pattern is a number or string
366 */
367static char **
368hist_get(const char *str, int approx, int allow_cur)
369{
370 char **hp = NULL((void*)0);
371 int n;
372
373 if (getn(str, &n)) {
374 hp = histptr + (n < 0 ? n : (n - hist_source->line));
375 if ((long)hp < (long)history) {
376 if (approx)
377 hp = hist_get_oldest();
378 else {
379 bi_errorf("%s: not in history", str);
380 hp = NULL((void*)0);
381 }
382 } else if (hp > histptr) {
383 if (approx)
384 hp = hist_get_newest(allow_cur);
385 else {
386 bi_errorf("%s: not in history", str);
387 hp = NULL((void*)0);
388 }
389 } else if (!allow_cur && hp == histptr) {
390 bi_errorf("%s: invalid range", str);
391 hp = NULL((void*)0);
392 }
393 } else {
394 int anchored = *str == '?' ? (++str, 0) : 1;
395
396 /* the -1 is to avoid the current fc command */
397 n = findhist(histptr - history - 1, 0, str, anchored);
398 if (n < 0) {
399 bi_errorf("%s: not in history", str);
400 hp = NULL((void*)0);
401 } else
402 hp = &history[n];
403 }
404 return hp;
405}
406
407/* Return a pointer to the newest command in the history */
408char **
409hist_get_newest(int allow_cur)
410{
411 if (histptr < history || (!allow_cur && histptr == history)) {
412 bi_errorf("no history (yet)");
413 return NULL((void*)0);
414 }
415 if (allow_cur)
416 return histptr;
417 return histptr - 1;
418}
419
420/* Return a pointer to the oldest command in the history */
421static char **
422hist_get_oldest(void)
423{
424 if (histptr <= history) {
425 bi_errorf("no history (yet)");
426 return NULL((void*)0);
427 }
428 return history;
429}
430
431/******************************/
432/* Back up over last histsave */
433/******************************/
434static void
435histbackup(void)
436{
437 static int last_line = -1;
438
439 if (histptr >= history && last_line != hist_source->line) {
440 hist_source->line--;
441 afree(*histptr, APERM&aperm);
442 histptr--;
443 last_line = hist_source->line;
444 }
445}
446
447static void
448histreset(void)
449{
450 char **hp;
451
452 for (hp = history; hp <= histptr; hp++)
453 afree(*hp, APERM&aperm);
454
455 histptr = history - 1;
456 hist_source->line = 0;
457}
458
459/*
460 * Return the current position.
461 */
462char **
463histpos(void)
464{
465 return current;
466}
467
468int
469histnum(int n)
470{
471 int last = histptr - history;
472
473 if (n < 0 || n >= last) {
474 current = histptr;
475 return last;
476 } else {
477 current = &history[n];
478 return n;
479 }
480}
481
482/*
483 * This will become unnecessary if hist_get is modified to allow
484 * searching from positions other than the end, and in either
485 * direction.
486 */
487int
488findhist(int start, int fwd, const char *str, int anchored)
489{
490 char **hp;
491 int maxhist = histptr - history;
492 int incr = fwd ? 1 : -1;
493 int len = strlen(str);
494
495 if (start < 0 || start >= maxhist)
496 start = maxhist;
497
498 hp = &history[start];
499 for (; hp >= history && hp <= histptr; hp += incr)
500 if ((anchored && strncmp(*hp, str, len) == 0) ||
501 (!anchored && strstr(*hp, str)))
502 return hp - history;
503
504 return -1;
505}
506
507int
508findhistrel(const char *str)
509{
510 int maxhist = histptr - history;
511 int start = maxhist - 1;
512 int rec = atoi(str);
513
514 if (rec == 0)
515 return -1;
516 if (rec > 0) {
517 if (rec > maxhist)
518 return -1;
519 return rec - 1;
520 }
521 if (rec > maxhist)
522 return -1;
523 return start + rec + 1;
524}
525
526void
527sethistcontrol(const char *str)
528{
529 char *spec, *tok, *state;
530
531 ignorespace = 0;
532 ignoredups = 0;
533
534 if (str == NULL((void*)0))
535 return;
536
537 spec = str_save(str, ATEMP&genv->area);
538 for (tok = strtok_r(spec, ":", &state); tok != NULL((void*)0);
539 tok = strtok_r(NULL((void*)0), ":", &state)) {
540 if (strcmp(tok, "ignoredups") == 0)
541 ignoredups = 1;
542 else if (strcmp(tok, "ignorespace") == 0)
543 ignorespace = 1;
544 }
545 afree(spec, ATEMP&genv->area);
546}
547
548/*
549 * set history
550 * this means reallocating the dataspace
551 */
552void
553sethistsize(int n)
554{
555 if (n > 0 && (uint32_t)n != histsize) {
556 char **tmp;
557 int offset = histptr - history;
558
559 /* save most recent history */
560 if (offset > n - 1) {
561 char **hp;
562
563 offset = n - 1;
564 for (hp = history; hp < histptr - offset; hp++)
565 afree(*hp, APERM&aperm);
566 memmove(history, histptr - offset, n * sizeof(char *));
567 }
568
569 tmp = reallocarray(histbase, n + 1, sizeof(char *));
570 if (tmp != NULL((void*)0)) {
571 histbase = tmp;
572 histsize = n;
573 history = histbase + 1;
574 histptr = history + offset;
575 } else
576 warningf(false0, "resizing history storage: %s",
577 strerror(errno(*__errno())));
578 }
579}
580
581/*
582 * set history file
583 * This can mean reloading/resetting/starting history file
584 * maintenance
585 */
586void
587sethistfile(const char *name)
588{
589 /* if not started then nothing to do */
590 if (hstarted == 0)
591 return;
592
593 /* if the name is the same as the name we have */
594 if (hname && strcmp(hname, name) == 0)
595 return;
596 /*
597 * its a new name - possibly
598 */
599 if (hname) {
600 afree(hname, APERM&aperm);
601 hname = NULL((void*)0);
602 histreset();
603 }
604
605 history_close();
606 hist_init(hist_source);
607}
608
609/*
610 * initialise the history vector
611 */
612void
613init_histvec(void)
614{
615 if (histbase == NULL((void*)0)) {
616 histsize = HISTORYSIZE500;
617 /*
618 * allocate one extra element so that histptr always
619 * lies within array bounds
620 */
621 histbase = reallocarray(NULL((void*)0), histsize + 1, sizeof(char *));
622 if (histbase == NULL((void*)0))
623 internal_errorf("allocating history storage: %s",
624 strerror(errno(*__errno())));
625 *histbase = NULL((void*)0);
626 history = histbase + 1;
627 histptr = history - 1;
628 }
629}
630
631static void
632history_lock(int operation)
633{
634 while (flock(fileno(histfh)(!__isthreaded ? ((histfh)->_file) : (fileno)(histfh)), operation) != 0) {
635 if (errno(*__errno()) == EINTR4 || errno(*__errno()) == EAGAIN35)
636 continue;
637 else
638 break;
639 }
640}
641
642/*
643 * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
644 * a) permit HISTSIZE to control number of lines of history stored
645 * b) maintain a physical history file
646 *
647 * It turns out that there is a lot of ghastly hackery here
648 */
649
650
651/*
652 * save command in history
653 */
654void
655histsave(int lno, const char *cmd, int dowrite)
656{
657 char *c, *cp;
658
659 if (ignorespace && cmd[0] == ' ')
660 return;
661
662 c = str_save(cmd, APERM&aperm);
663 if ((cp = strrchr(c, '\n')) != NULL((void*)0))
664 *cp = '\0';
665
666 /*
667 * XXX to properly check for duplicated lines we should first reload
668 * the histfile if needed
669 */
670 if (ignoredups && histptr >= history && strcmp(*histptr, c) == 0) {
671 afree(c, APERM&aperm);
672 return;
673 }
674
675 if (dowrite && histfh) {
676#ifndef SMALL
677 struct stat sb;
678
679 history_lock(LOCK_EX0x02);
680 if (fstat(fileno(histfh)(!__isthreaded ? ((histfh)->_file) : (fileno)(histfh)), &sb) != -1) {
681 if (timespeccmp(&sb.st_mtim, &last_sb.st_mtim, ==)(((&sb.st_mtim)->tv_sec == (&last_sb.st_mtim)->
tv_sec) ? ((&sb.st_mtim)->tv_nsec == (&last_sb.st_mtim
)->tv_nsec) : ((&sb.st_mtim)->tv_sec == (&last_sb
.st_mtim)->tv_sec))
)
682 ; /* file is unchanged */
683 else {
684 histreset();
685 history_load(hist_source);
686 }
687 }
688#endif
689 }
690
691 if (histptr < history + histsize - 1)
692 histptr++;
693 else { /* remove oldest command */
694 afree(*history, APERM&aperm);
695 memmove(history, history + 1,
696 (histsize - 1) * sizeof(*history));
697 }
698 *histptr = c;
699
700 if (dowrite && histfh) {
701#ifndef SMALL
702 char *encoded;
703
704 /* append to file */
705 if (fseeko(histfh, 0, SEEK_END2) == 0 &&
706 stravis(&encoded, c, VIS_SAFE0x20 | VIS_NL0x10) != -1) {
707 fprintf(histfh, "%s\n", encoded);
708 fflush(histfh);
709 fstat(fileno(histfh)(!__isthreaded ? ((histfh)->_file) : (fileno)(histfh)), &last_sb);
710 line_co++;
711 history_write();
712 free(encoded);
713 }
714 history_lock(LOCK_UN0x08);
715#endif
716 }
717}
718
719static FILE *
720history_open(void)
721{
722 FILE *f = NULL((void*)0);
723#ifndef SMALL
724 struct stat sb;
725 int fd, fddup;
726
727 if ((fd = open(hname, O_RDWR0x0002 | O_CREAT0x0200 | O_EXLOCK0x0020, 0600)) == -1)
728 return NULL((void*)0);
729 if (fstat(fd, &sb) == -1 || sb.st_uid != getuid()) {
730 close(fd);
731 return NULL((void*)0);
732 }
733 fddup = savefd(fd);
734 if (fddup != fd)
735 close(fd);
736
737 if ((f = fdopen(fddup, "r+")) == NULL((void*)0))
738 close(fddup);
739 else
740 last_sb = sb;
741#endif
742 return f;
743}
744
745static void
746history_close(void)
747{
748 if (histfh) {
749 fflush(histfh);
750 fclose(histfh);
751 histfh = NULL((void*)0);
752 }
753}
754
755static void
756history_load(Source *s)
757{
758 char *p, encoded[LINE4096 + 1], line[LINE4096 + 1];
759 int toolongseen = 0;
760
761 rewind(histfh);
762 line_co = 1;
763
764 /* just read it all; will auto resize history upon next command */
765 while (fgets(encoded, sizeof(encoded), histfh)) {
766 if ((p = strchr(encoded, '\n')) == NULL((void*)0)) {
767 /* discard overlong line */
768 do {
769 /* maybe a missing trailing newline? */
770 if (strlen(encoded) != sizeof(encoded) - 1) {
771 bi_errorf("history file is corrupt");
772 return;
773 }
774 } while (fgets(encoded, sizeof(encoded), histfh)
775 && strchr(encoded, '\n') == NULL((void*)0));
776
777 if (!toolongseen) {
778 toolongseen = 1;
779 bi_errorf("ignored history line(s) longer than"
780 " %d bytes", LINE4096);
781 }
782
783 continue;
784 }
785 *p = '\0';
786 s->line = line_co;
787 s->cmd_offset = line_co;
788 strunvis(line, encoded);
789 histsave(line_co, line, 0);
790 line_co++;
791 }
792
793 history_write();
794}
795
796#define HMAGIC10xab 0xab
797#define HMAGIC20xcd 0xcd
798
799void
800hist_init(Source *s)
801{
802 int oldmagic1, oldmagic2;
803
804 if (Flag(FTALKING)(shell_flags[(int) (FTALKING)]) == 0)
805 return;
806
807 hstarted = 1;
808
809 hist_source = s;
810
811 if (str_val(global("HISTFILE")) == null)
812 return;
813 hname = str_save(str_val(global("HISTFILE")), APERM&aperm);
814 histfh = history_open();
815 if (histfh == NULL((void*)0))
816 return;
817
818 oldmagic1 = fgetc(histfh);
819 oldmagic2 = fgetc(histfh);
820
821 if (oldmagic1 == EOF(-1) || oldmagic2 == EOF(-1)) {
822 if (!feof(histfh)(!__isthreaded ? (((histfh)->_flags & 0x0020) != 0) : (
feof)(histfh))
&& ferror(histfh)(!__isthreaded ? (((histfh)->_flags & 0x0040) != 0) : (
ferror)(histfh))
) {
823 history_close();
824 return;
825 }
826 } else if (oldmagic1 == HMAGIC10xab && oldmagic2 == HMAGIC20xcd) {
827 bi_errorf("ignoring old style history file");
828 history_close();
829 return;
830 }
831
832 history_load(s);
833
834 history_lock(LOCK_UN0x08);
835}
836
837static void
838history_write(void)
839{
840 char **hp, *encoded;
841
842 /* see if file has grown over 25% */
843 if (line_co < histsize + (histsize / 4))
844 return;
845
846 /* rewrite the whole caboodle */
847 rewind(histfh);
848 if (ftruncate(fileno(histfh)(!__isthreaded ? ((histfh)->_file) : (fileno)(histfh)), 0) == -1) {
849 bi_errorf("failed to rewrite history file - %s",
850 strerror(errno(*__errno())));
851 }
852 for (hp = history; hp <= histptr; hp++) {
853 if (stravis(&encoded, *hp, VIS_SAFE0x20 | VIS_NL0x10) != -1) {
854 if (fprintf(histfh, "%s\n", encoded) == -1) {
855 free(encoded);
856 return;
857 }
858 free(encoded);
859 }
860 }
861
862 line_co = histsize;
863
864 fflush(histfh);
865 fstat(fileno(histfh)(!__isthreaded ? ((histfh)->_file) : (fileno)(histfh)), &last_sb);
866}
867
868void
869hist_finish(void)
870{
871 history_close();
872}