GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/history.c Lines: 287 417 68.8 %
Date: 2017-11-13 Branches: 183 326 56.1 %

Line Branch Exec Source
1
/*	$OpenBSD: history.c,v 1.74 2017/10/23 15:43:38 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
#include <sys/uio.h>
17
18
#include <errno.h>
19
#include <fcntl.h>
20
#include <stdlib.h>
21
#include <stdio.h>
22
#include <string.h>
23
#include <unistd.h>
24
#include <vis.h>
25
26
#include "sh.h"
27
28
#ifdef HISTORY
29
30
static void	history_write(void);
31
static FILE	*history_open(void);
32
static void	history_load(Source *);
33
static void	history_close(void);
34
35
static int	hist_execute(char *);
36
static int	hist_replace(char **, const char *, const char *, int);
37
static char   **hist_get(const char *, int, int);
38
static char   **hist_get_oldest(void);
39
static void	histbackup(void);
40
41
static FILE	*histfh;
42
static char   **histbase;	/* actual start of the history[] allocation */
43
static char   **current;	/* current position in history[] */
44
static char    *hname;		/* current name of history file */
45
static int	hstarted;	/* set after hist_init() called */
46
static int	ignoredups;	/* ditch duplicated history lines? */
47
static int	ignorespace;	/* ditch lines starting with a space? */
48
static Source	*hist_source;
49
static uint32_t	line_co;
50
51
static struct stat last_sb;
52
53
int
54
c_fc(char **wp)
55
{
56
	struct shf *shf;
57
	struct temp *tf = NULL;
58
	char *p, *editor = NULL;
59
	int gflag = 0, lflag = 0, nflag = 0, sflag = 0, rflag = 0;
60
	int optc, ret;
61
	char *first = NULL, *last = NULL;
62
	char **hfirst, **hlast, **hp;
63
	static int depth;
64
65
336
	if (depth != 0) {
66
		bi_errorf("history function called recursively");
67
		return 1;
68
	}
69
70
168
	if (!Flag(FTALKING_I)) {
71
		bi_errorf("history functions not available");
72
		return 1;
73
	}
74
75
1206
	while ((optc = ksh_getopt(wp, &builtin_opt,
76
402
	    "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
77




468
		switch (optc) {
78
		case 'e':
79
60
			p = builtin_opt.optarg;
80
60
			if (strcmp(p, "-") == 0)
81
60
				sflag++;
82
			else {
83
				size_t len = strlen(p) + 4;
84
				editor = str_nsave(p, len, ATEMP);
85
				strlcat(editor, " $_", len);
86
			}
87
			break;
88
		case 'g': /* non-at&t ksh */
89
6
			gflag++;
90
6
			break;
91
		case 'l':
92
108
			lflag++;
93
108
			break;
94
		case 'n':
95
			nflag++;
96
			break;
97
		case 'r':
98
12
			rflag++;
99
12
			break;
100
		case 's':	/* posix version of -e - */
101
			sflag++;
102
			break;
103
		  /* kludge city - accept -num as -- -num (kind of) */
104
		case '0': case '1': case '2': case '3': case '4':
105
		case '5': case '6': case '7': case '8': case '9':
106
48
			p = shf_smprintf("-%c%s",
107
48
					optc, builtin_opt.optarg);
108
48
			if (!first)
109
24
				first = p;
110
24
			else if (!last)
111
				last = p;
112
			else {
113
				bi_errorf("too many arguments");
114
				return 1;
115
			}
116
			break;
117
		case '?':
118
			return 1;
119
		}
120
168
	wp += builtin_opt.optind;
121
122
	/* Substitute and execute command */
123
168
	if (sflag) {
124
		char *pat = NULL, *rep = NULL;
125
126
60
		if (editor || lflag || nflag || rflag) {
127
			bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
128
			return 1;
129
		}
130
131
		/* Check for pattern replacement argument */
132

132
		if (*wp && **wp && (p = strchr(*wp + 1, '='))) {
133
30
			pat = str_save(*wp, ATEMP);
134
30
			p = pat + (p - *wp);
135
30
			*p++ = '\0';
136
			rep = p;
137
30
			wp++;
138
30
		}
139
		/* Check for search prefix */
140

120
		if (!first && (first = *wp))
141
24
			wp++;
142

120
		if (last || *wp) {
143
			bi_errorf("too many arguments");
144
			return 1;
145
		}
146
147
180
		hp = first ? hist_get(first, false, false) :
148
36
		    hist_get_newest(false);
149
60
		if (!hp)
150
6
			return 1;
151
54
		depth++;
152
54
		ret = hist_replace(hp, pat, rep, gflag);
153
54
		depth--;
154
54
		return ret;
155
	}
156
157

108
	if (editor && (lflag || nflag)) {
158
		bi_errorf("can't use -l, -n with -e");
159
		return 1;
160
	}
161
162

192
	if (!first && (first = *wp))
163
42
		wp++;
164

192
	if (!last && (last = *wp))
165
30
		wp++;
166
108
	if (*wp) {
167
		bi_errorf("too many arguments");
168
		return 1;
169
	}
170
108
	if (!first) {
171
126
		hfirst = lflag ? hist_get("-16", true, true) :
172
		    hist_get_newest(false);
173
42
		if (!hfirst)
174
			return 1;
175
		/* can't fail if hfirst didn't fail */
176
42
		hlast = hist_get_newest(false);
177
42
	} else {
178
		/* POSIX says not an error if first/last out of bounds
179
		 * when range is specified; at&t ksh and pdksh allow out of
180
		 * bounds for -l as well.
181
		 */
182
132
		hfirst = hist_get(first, (lflag || last) ? true : false,
183
66
		    lflag ? true : false);
184
66
		if (!hfirst)
185
			return 1;
186
186
		hlast = last ? hist_get(last, true, lflag ? true : false) :
187
24
		    (lflag ? hist_get_newest(false) : hfirst);
188
66
		if (!hlast)
189
			return 1;
190
	}
191
108
	if (hfirst > hlast) {
192
		char **temp;
193
194
		temp = hfirst; hfirst = hlast; hlast = temp;
195
12
		rflag = !rflag; /* POSIX */
196
12
	}
197
198
	/* List history */
199
108
	if (lflag) {
200
		char *s, *t;
201
108
		const char *nfmt = nflag ? "\t" : "%d\t";
202
203
624
		for (hp = rflag ? hlast : hfirst;
204
834
		    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
205
210
			shf_fprintf(shl_stdout, nfmt,
206
210
			    hist_source->line - (int) (histptr - hp));
207
			/* print multi-line commands correctly */
208
420
			for (s = *hp; (t = strchr(s, '\n')); s = t)
209
				shf_fprintf(shl_stdout, "%.*s\t", ++t - s, s);
210
210
			shf_fprintf(shl_stdout, "%s\n", s);
211
		}
212
108
		shf_flush(shl_stdout);
213
		return 0;
214
	}
215
216
	/* Run editor on selected lines, then run resulting commands */
217
218
	tf = maketemp(ATEMP, TT_HIST_EDIT, &genv->temps);
219
	if (!(shf = tf->shf)) {
220
		bi_errorf("cannot create temp file %s - %s",
221
		    tf->name, strerror(errno));
222
		return 1;
223
	}
224
	for (hp = rflag ? hlast : hfirst;
225
	    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
226
		shf_fprintf(shf, "%s\n", *hp);
227
	if (shf_close(shf) == EOF) {
228
		bi_errorf("error writing temporary file - %s", strerror(errno));
229
		return 1;
230
	}
231
232
	/* Ignore setstr errors here (arbitrary) */
233
	setstr(local("_", false), tf->name, KSH_RETURN_ERROR);
234
235
	/* XXX: source should not get trashed by this.. */
236
	{
237
		Source *sold = source;
238
239
		ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_", 0);
240
		source = sold;
241
		if (ret)
242
			return ret;
243
	}
244
245
	{
246
		struct stat statb;
247
		XString xs;
248
		char *xp;
249
		int n;
250
251
		if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) {
252
			bi_errorf("cannot open temp file %s", tf->name);
253
			return 1;
254
		}
255
256
		n = fstat(shf->fd, &statb) < 0 ? 128 :
257
		    statb.st_size + 1;
258
		Xinit(xs, xp, n, hist_source->areap);
259
		while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
260
			xp += n;
261
			if (Xnleft(xs, xp) <= 0)
262
				XcheckN(xs, xp, Xlength(xs, xp));
263
		}
264
		if (n < 0) {
265
			bi_errorf("error reading temp file %s - %s",
266
			    tf->name, strerror(shf->errno_));
267
			shf_close(shf);
268
			return 1;
269
		}
270
		shf_close(shf);
271
		*xp = '\0';
272
		strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
273
		depth++;
274
		ret = hist_execute(Xstring(xs, xp));
275
		depth--;
276
		return ret;
277
	}
278
168
}
279
280
/* Save cmd in history, execute cmd (cmd gets trashed) */
281
static int
282
hist_execute(char *cmd)
283
{
284
	Source *sold;
285
	int ret;
286
	char *p, *q;
287
288
108
	histbackup();
289
290
216
	for (p = cmd; p; p = q) {
291
54
		if ((q = strchr(p, '\n'))) {
292
			*q++ = '\0'; /* kill the newline */
293
			if (!*q) /* ignore trailing newline */
294
				q = NULL;
295
		}
296
54
		histsave(++(hist_source->line), p, 1);
297
298
54
		shellf("%s\n", p); /* POSIX doesn't say this is done... */
299
54
		if ((p = q)) /* restore \n (trailing \n not restored) */
300
			q[-1] = '\n';
301
	}
302
303
	/* Commands are executed here instead of pushing them onto the
304
	 * input 'cause posix says the redirection and variable assignments
305
	 * in
306
	 *	X=y fc -e - 42 2> /dev/null
307
	 * are to effect the repeated commands environment.
308
	 */
309
	/* XXX: source should not get trashed by this.. */
310
54
	sold = source;
311
54
	ret = command(cmd, 0);
312
54
	source = sold;
313
54
	return ret;
314
}
315
316
static int
317
hist_replace(char **hp, const char *pat, const char *rep, int global)
318
{
319
	char *line;
320
321
108
	if (!pat)
322
24
		line = str_save(*hp, ATEMP);
323
	else {
324
		char *s, *s1;
325
30
		int pat_len = strlen(pat);
326
30
		int rep_len = strlen(rep);
327
		int len;
328
30
		XString xs;
329
		char *xp;
330
		int any_subst = 0;
331
332
30
		Xinit(xs, xp, 128, ATEMP);
333

240
		for (s = *hp; (s1 = strstr(s, pat)) && (!any_subst || global);
334
60
		    s = s1 + pat_len) {
335
			any_subst = 1;
336
60
			len = s1 - s;
337
60
			XcheckN(xs, xp, len + rep_len);
338
60
			memcpy(xp, s, len);		/* first part */
339
60
			xp += len;
340
60
			memcpy(xp, rep, rep_len);	/* replacement */
341
60
			xp += rep_len;
342
		}
343
30
		if (!any_subst) {
344
			bi_errorf("substitution failed");
345
			return 1;
346
		}
347
30
		len = strlen(s) + 1;
348
30
		XcheckN(xs, xp, len);
349
30
		memcpy(xp, s, len);
350
30
		xp += len;
351
30
		line = Xclose(xs, xp);
352
60
	}
353
54
	return hist_execute(line);
354
54
}
355
356
/*
357
 * get pointer to history given pattern
358
 * pattern is a number or string
359
 */
360
static char **
361
hist_get(const char *str, int approx, int allow_cur)
362
{
363
	char **hp = NULL;
364
372
	int n;
365
366
186
	if (getn(str, &n)) {
367
384
		hp = histptr + (n < 0 ? n : (n - hist_source->line));
368
162
		if ((long)hp < (long)history) {
369
54
			if (approx)
370
54
				hp = hist_get_oldest();
371
			else {
372
				bi_errorf("%s: not in history", str);
373
				hp = NULL;
374
			}
375
108
		} else if (hp > histptr) {
376
6
			if (approx)
377
6
				hp = hist_get_newest(allow_cur);
378
			else {
379
				bi_errorf("%s: not in history", str);
380
				hp = NULL;
381
			}
382

102
		} else if (!allow_cur && hp == histptr) {
383
			bi_errorf("%s: invalid range", str);
384
			hp = NULL;
385
		}
386
	} else {
387
54
		int anchored = *str == '?' ? (++str, 0) : 1;
388
389
		/* the -1 is to avoid the current fc command */
390
24
		n = findhist(histptr - history - 1, 0, str, anchored);
391
24
		if (n < 0) {
392
			bi_errorf("%s: not in history", str);
393
			hp = NULL;
394
		} else
395
24
			hp = &history[n];
396
	}
397
186
	return hp;
398
186
}
399
400
/* Return a pointer to the newest command in the history */
401
char **
402
hist_get_newest(int allow_cur)
403
{
404

378
	if (histptr < history || (!allow_cur && histptr == history)) {
405
6
		bi_errorf("no history (yet)");
406
6
		return NULL;
407
	}
408
90
	if (allow_cur)
409
6
		return histptr;
410
84
	return histptr - 1;
411
96
}
412
413
/* Return a pointer to the oldest command in the history */
414
static char **
415
hist_get_oldest(void)
416
{
417
108
	if (histptr <= history) {
418
		bi_errorf("no history (yet)");
419
		return NULL;
420
	}
421
54
	return history;
422
54
}
423
424
/******************************/
425
/* Back up over last histsave */
426
/******************************/
427
static void
428
histbackup(void)
429
{
430
	static int last_line = -1;
431
432

162
	if (histptr >= history && last_line != hist_source->line) {
433
54
		hist_source->line--;
434
54
		afree(*histptr, APERM);
435
54
		histptr--;
436
54
		last_line = hist_source->line;
437
54
	}
438
54
}
439
440
static void
441
histreset(void)
442
{
443
	char **hp;
444
445
	for (hp = history; hp <= histptr; hp++)
446
		afree(*hp, APERM);
447
448
	histptr = history - 1;
449
	hist_source->line = 0;
450
}
451
452
/*
453
 * Return the current position.
454
 */
455
char **
456
histpos(void)
457
{
458
	return current;
459
}
460
461
int
462
histnum(int n)
463
{
464
852
	int	last = histptr - history;
465
466

426
	if (n < 0 || n >= last) {
467
426
		current = histptr;
468
426
		return last;
469
	} else {
470
		current = &history[n];
471
		return n;
472
	}
473
426
}
474
475
/*
476
 * This will become unnecessary if hist_get is modified to allow
477
 * searching from positions other than the end, and in either
478
 * direction.
479
 */
480
int
481
findhist(int start, int fwd, const char *str, int anchored)
482
{
483
	char	**hp;
484
48
	int	maxhist = histptr - history;
485
24
	int	incr = fwd ? 1 : -1;
486
24
	int	len = strlen(str);
487
488

48
	if (start < 0 || start >= maxhist)
489
		start = maxhist;
490
491
24
	hp = &history[start];
492

108
	for (; hp >= history && hp <= histptr; hp += incr)
493

72
		if ((anchored && strncmp(*hp, str, len) == 0) ||
494
30
		    (!anchored && strstr(*hp, str)))
495
24
			return hp - history;
496
497
	return -1;
498
24
}
499
500
int
501
findhistrel(const char *str)
502
{
503
	int	maxhist = histptr - history;
504
	int	start = maxhist - 1;
505
	int	rec = atoi(str);
506
507
	if (rec == 0)
508
		return -1;
509
	if (rec > 0) {
510
		if (rec > maxhist)
511
			return -1;
512
		return rec - 1;
513
	}
514
	if (rec > maxhist)
515
		return -1;
516
	return start + rec + 1;
517
}
518
519
void
520
sethistcontrol(const char *str)
521
{
522
36
	char *spec, *tok, *state;
523
524
18
	ignorespace = 0;
525
18
	ignoredups = 0;
526
527
18
	if (str == NULL)
528
		return;
529
530
18
	spec = str_save(str, ATEMP);
531
96
	for (tok = strtok_r(spec, ":", &state); tok != NULL;
532
54
	     tok = strtok_r(NULL, ":", &state)) {
533
30
		if (strcmp(tok, "ignoredups") == 0)
534
			ignoredups = 1;
535
18
		else if (strcmp(tok, "ignorespace") == 0)
536
			ignorespace = 1;
537
	}
538
18
	afree(spec, ATEMP);
539
36
}
540
541
/*
542
 *	set history
543
 *	this means reallocating the dataspace
544
 */
545
void
546
sethistsize(int n)
547
{
548

36
	if (n > 0 && n != histsize) {
549
12
		int offset = histptr - history;
550
551
		/* save most recent history */
552
12
		if (offset > n - 1) {
553
			char **hp;
554
555
			offset = n - 1;
556
			for (hp = history; hp < histptr - offset; hp++)
557
				afree(*hp, APERM);
558
			memmove(history, histptr - offset, n * sizeof(char *));
559
		}
560
561
12
		histsize = n;
562
12
		histbase = areallocarray(histbase, n + 1, sizeof(char *), APERM);
563
12
		history = histbase + 1;
564
12
		histptr = history + offset;
565
12
	}
566
12
}
567
568
/*
569
 *	set history file
570
 *	This can mean reloading/resetting/starting history file
571
 *	maintenance
572
 */
573
void
574
sethistfile(const char *name)
575
{
576
	/* if not started then nothing to do */
577
4284
	if (hstarted == 0)
578
		return;
579
580
	/* if the name is the same as the name we have */
581
	if (hname && strcmp(hname, name) == 0)
582
		return;
583
	/*
584
	 * its a new name - possibly
585
	 */
586
	if (hname) {
587
		afree(hname, APERM);
588
		hname = NULL;
589
		histreset();
590
	}
591
592
	history_close();
593
	hist_init(hist_source);
594
2142
}
595
596
/*
597
 *	initialise the history vector
598
 */
599
void
600
init_histvec(void)
601
{
602
209526
	if (histbase == NULL) {
603
104763
		histsize = HISTORYSIZE;
604
		/*
605
		 * allocate one extra element so that histptr always
606
		 * lies within array bounds
607
		 */
608
104763
		histbase = areallocarray(NULL, histsize + 1, sizeof(char *),
609
		    APERM);
610
104763
		history = histbase + 1;
611
104763
		histptr = history - 1;
612
104763
	}
613
104763
}
614
615
static void
616
history_lock(int operation)
617
{
618

7260
	while (flock(fileno(histfh), operation) != 0) {
619
		if (errno == EINTR || errno == EAGAIN)
620
			continue;
621
		else
622
			break;
623
	}
624
1452
}
625
626
/*
627
 *	Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
628
 *	a) permit HISTSIZE to control number of lines of history stored
629
 *	b) maintain a physical history file
630
 *
631
 *	It turns out that there is a lot of ghastly hackery here
632
 */
633
634
635
/*
636
 * save command in history
637
 */
638
void
639
histsave(int lno, const char *cmd, int dowrite)
640
{
641
	char		*c, *cp;
642
643

1818
	if (ignorespace && cmd[0] == ' ')
644
12
		return;
645
646
876
	c = str_save(cmd, APERM);
647
876
	if ((cp = strrchr(c, '\n')) != NULL)
648
804
		*cp = '\0';
649
650
	/*
651
	 * XXX to properly check for duplicated lines we should first reload
652
	 * the histfile if needed
653
	 */
654

936
	if (ignoredups && histptr >= history && strcmp(*histptr, c) == 0) {
655
12
		afree(c, APERM);
656
12
		return;
657
	}
658
659
864
	if (dowrite && histfh) {
660
#ifndef SMALL
661
642
		struct stat	sb;
662
663
642
		history_lock(LOCK_EX);
664

1926
		if (fstat(fileno(histfh), &sb) != -1) {
665

1284
			if (timespeccmp(&sb.st_mtim, &last_sb.st_mtim, ==))
666
				; /* file is unchanged */
667
			else {
668
				histreset();
669
				history_load(hist_source);
670
			}
671
		}
672
#endif
673
642
	}
674
675
864
	if (histptr < history + histsize - 1)
676
828
		histptr++;
677
	else { /* remove oldest command */
678
36
		afree(*history, APERM);
679
72
		memmove(history, history + 1,
680
36
		    (histsize - 1) * sizeof(*history));
681
	}
682
864
	*histptr = c;
683
684
864
	if (dowrite && histfh) {
685
#ifndef SMALL
686
642
		char *encoded;
687
688
		/* append to file */
689

1284
		if (fseeko(histfh, 0, SEEK_END) == 0 &&
690
642
		    stravis(&encoded, c, VIS_SAFE | VIS_NL) != -1) {
691
642
			fprintf(histfh, "%s\n", encoded);
692
642
			fflush(histfh);
693
1926
			fstat(fileno(histfh), &last_sb);
694
642
			line_co++;
695
642
			history_write();
696
642
			free(encoded);
697
642
		}
698
642
		history_lock(LOCK_UN);
699
#endif
700
642
	}
701
1752
}
702
703
static FILE *
704
history_open(void)
705
{
706
	FILE		*f = NULL;
707
#ifndef SMALL
708
1760
	struct stat	sb;
709
	int		fd, fddup;
710
711
880
	if ((fd = open(hname, O_RDWR | O_CREAT | O_EXLOCK, 0600)) == -1)
712
712
		return NULL;
713

336
	if (fstat(fd, &sb) == -1 || sb.st_uid != getuid()) {
714
		close(fd);
715
		return NULL;
716
	}
717
168
	fddup = savefd(fd);
718
168
	if (fddup != fd)
719
168
		close(fd);
720
721
168
	if ((f = fdopen(fddup, "r+")) == NULL)
722
		close(fddup);
723
	else
724
168
		last_sb = sb;
725
#endif
726
168
	return f;
727
880
}
728
729
static void
730
history_close(void)
731
{
732
1712
	if (histfh) {
733
156
		fflush(histfh);
734
156
		fclose(histfh);
735
156
		histfh = NULL;
736
156
	}
737
856
}
738
739
static void
740
history_load(Source *s)
741
{
742
336
	char		*p, encoded[LINE + 1], line[LINE + 1];
743
	int		 toolongseen = 0;
744
745
168
	rewind(histfh);
746
168
	line_co = 1;
747
748
	/* just read it all; will auto resize history upon next command */
749
384
	while (fgets(encoded, sizeof(encoded), histfh)) {
750
36
		if ((p = strchr(encoded, '\n')) == NULL) {
751
			/* discard overlong line */
752
18
			do {
753
				/* maybe a missing trailing newline? */
754
18
				if (strlen(encoded) != sizeof(encoded) - 1) {
755
6
					bi_errorf("history file is corrupt");
756
6
					return;
757
				}
758
24
			} while (fgets(encoded, sizeof(encoded), histfh)
759
24
			    && strchr(encoded, '\n') == NULL);
760
761
12
			if (!toolongseen) {
762
				toolongseen = 1;
763
6
				bi_errorf("ignored history line(s) longer than"
764
				    " %d bytes", LINE);
765
6
			}
766
767
12
			continue;
768
		}
769
18
		*p = '\0';
770
18
		s->line = line_co;
771
18
		s->cmd_offset = line_co;
772
18
		strunvis(line, encoded);
773
18
		histsave(line_co, line, 0);
774
18
		line_co++;
775
	}
776
777
162
	history_write();
778
330
}
779
780
#define HMAGIC1 0xab
781
#define HMAGIC2 0xcd
782
783
void
784
hist_init(Source *s)
785
{
786
	int oldmagic1, oldmagic2;
787
788
1760
	if (Flag(FTALKING) == 0)
789
		return;
790
791
880
	hstarted = 1;
792
793
880
	hist_source = s;
794
795
880
	hname = str_val(global("HISTFILE"));
796
880
	if (hname == NULL)
797
		return;
798
880
	hname = str_save(hname, APERM);
799
880
	histfh = history_open();
800
880
	if (histfh == NULL)
801
712
		return;
802
803
168
	oldmagic1 = fgetc(histfh);
804
168
	oldmagic2 = fgetc(histfh);
805
806
168
	if (oldmagic1 == EOF || oldmagic2 == EOF) {
807



312
		if (!feof(histfh) && ferror(histfh)) {
808
			history_close();
809
			return;
810
		}
811
12
	} else if (oldmagic1 == HMAGIC1 && oldmagic2 == HMAGIC2) {
812
		bi_errorf("ignoring old style history file");
813
		history_close();
814
		return;
815
	}
816
817
168
	history_load(s);
818
819
168
	history_lock(LOCK_UN);
820
1048
}
821
822
static void
823
history_write(void)
824
{
825
1608
	char		**hp, *encoded;
826
827
	/* see if file has grown over 25% */
828
804
	if (line_co < histsize + (histsize / 4))
829
744
		return;
830
831
	/* rewrite the whole caboodle */
832
60
	rewind(histfh);
833

180
	if (ftruncate(fileno(histfh), 0) == -1) {
834
		bi_errorf("failed to rewrite history file - %s",
835
		    strerror(errno));
836
	}
837
456
	for (hp = history; hp <= histptr; hp++) {
838
168
		if (stravis(&encoded, *hp, VIS_SAFE | VIS_NL) != -1) {
839
336
			if (fprintf(histfh, "%s\n", encoded) == -1) {
840
168
				free(encoded);
841
				return;
842
			}
843
			free(encoded);
844
		}
845
	}
846
847
60
	line_co = histsize;
848
849
60
	fflush(histfh);
850
180
	fstat(fileno(histfh), &last_sb);
851
864
}
852
853
void
854
hist_finish(void)
855
{
856
1712
	history_close();
857
856
}
858
859
#else /* HISTORY */
860
861
/* No history to be compiled in: dummy routines to avoid lots more ifdefs */
862
void
863
init_histvec(void)
864
{
865
}
866
void
867
hist_init(Source *s)
868
{
869
}
870
void
871
hist_finish(void)
872
{
873
}
874
void
875
histsave(int lno, const char *cmd, int dowrite)
876
{
877
	errorf("history not enabled");
878
}
879
#endif /* HISTORY */