GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/history.c Lines: 272 410 66.3 %
Date: 2017-11-07 Branches: 172 330 52.1 %

Line Branch Exec Source
1
/*	$OpenBSD: history.c,v 1.71 2017/09/07 19:08:32 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 int	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
1300
	if (depth != 0) {
66
		bi_errorf("history function called recursively");
67
		return 1;
68
	}
69
70
650
	if (!Flag(FTALKING_I)) {
71
		bi_errorf("history functions not available");
72
		return 1;
73
	}
74
75
3150
	while ((optc = ksh_getopt(wp, &builtin_opt,
76
1575
	    "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
77




1900
		switch (optc) {
78
		case 'e':
79
250
			p = builtin_opt.optarg;
80
250
			if (strcmp(p, "-") == 0)
81
250
				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
25
			gflag++;
90
25
			break;
91
		case 'l':
92
400
			lflag++;
93
400
			break;
94
		case 'n':
95
			nflag++;
96
			break;
97
		case 'r':
98
50
			rflag++;
99
50
			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
200
			p = shf_smprintf("-%c%s",
107
200
					optc, builtin_opt.optarg);
108
200
			if (!first)
109
100
				first = p;
110
100
			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
650
	wp += builtin_opt.optind;
121
122
	/* Substitute and execute command */
123
650
	if (sflag) {
124
		char *pat = NULL, *rep = NULL;
125
126
250
		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

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

500
		if (!first && (first = *wp))
141
100
			wp++;
142

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

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

700
	if (!first && (first = *wp))
163
175
		wp++;
164

700
	if (!last && (last = *wp))
165
125
		wp++;
166
400
	if (*wp) {
167
		bi_errorf("too many arguments");
168
		return 1;
169
	}
170
400
	if (!first) {
171
375
		hfirst = lflag ? hist_get("-16", true, true) :
172
		    hist_get_newest(false);
173
125
		if (!hfirst)
174
			return 1;
175
		/* can't fail if hfirst didn't fail */
176
125
		hlast = hist_get_newest(false);
177
125
	} 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
550
		hfirst = hist_get(first, (lflag || last) ? true : false,
183
275
		    lflag ? true : false);
184
275
		if (!hfirst)
185
			return 1;
186
775
		hlast = last ? hist_get(last, true, lflag ? true : false) :
187
100
		    (lflag ? hist_get_newest(false) : hfirst);
188
275
		if (!hlast)
189
			return 1;
190
	}
191
400
	if (hfirst > hlast) {
192
		char **temp;
193
194
		temp = hfirst; hfirst = hlast; hlast = temp;
195
50
		rflag = !rflag; /* POSIX */
196
50
	}
197
198
	/* List history */
199
400
	if (lflag) {
200
		char *s, *t;
201
400
		const char *nfmt = nflag ? "\t" : "%d\t";
202
203
2350
		for (hp = rflag ? hlast : hfirst;
204
3150
		    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
205
800
			shf_fprintf(shl_stdout, nfmt,
206
800
			    hist_source->line - (int) (histptr - hp));
207
			/* print multi-line commands correctly */
208
1600
			for (s = *hp; (t = strchr(s, '\n')); s = t)
209
				shf_fprintf(shl_stdout, "%.*s\t", ++t - s, s);
210
800
			shf_fprintf(shl_stdout, "%s\n", s);
211
		}
212
400
		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
650
}
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
450
	histbackup();
289
290
900
	for (p = cmd; p; p = q) {
291
225
		if ((q = strchr(p, '\n'))) {
292
			*q++ = '\0'; /* kill the newline */
293
			if (!*q) /* ignore trailing newline */
294
				q = NULL;
295
		}
296
225
		histsave(++(hist_source->line), p, 1);
297
298
225
		shellf("%s\n", p); /* POSIX doesn't say this is done... */
299
225
		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
225
	sold = source;
311
225
	ret = command(cmd, 0);
312
225
	source = sold;
313
225
	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
450
	if (!pat)
322
100
		line = str_save(*hp, ATEMP);
323
	else {
324
		char *s, *s1;
325
125
		int pat_len = strlen(pat);
326
125
		int rep_len = strlen(rep);
327
		int len;
328
125
		XString xs;
329
		char *xp;
330
		int any_subst = 0;
331
332
125
		Xinit(xs, xp, 128, ATEMP);
333

1000
		for (s = *hp; (s1 = strstr(s, pat)) && (!any_subst || global);
334
250
		    s = s1 + pat_len) {
335
			any_subst = 1;
336
250
			len = s1 - s;
337
250
			XcheckN(xs, xp, len + rep_len);
338
250
			memcpy(xp, s, len);		/* first part */
339
250
			xp += len;
340
250
			memcpy(xp, rep, rep_len);	/* replacement */
341
250
			xp += rep_len;
342
		}
343
125
		if (!any_subst) {
344
			bi_errorf("substitution failed");
345
			return 1;
346
		}
347
125
		len = strlen(s) + 1;
348
125
		XcheckN(xs, xp, len);
349
125
		memcpy(xp, s, len);
350
125
		xp += len;
351
125
		line = Xclose(xs, xp);
352
250
	}
353
225
	return hist_execute(line);
354
225
}
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
1450
	int n;
365
366
725
	if (getn(str, &n)) {
367
1500
		hp = histptr + (n < 0 ? n : (n - hist_source->line));
368
625
		if ((long)hp < (long)history) {
369
175
			if (approx)
370
175
				hp = hist_get_oldest();
371
			else {
372
				bi_errorf("%s: not in history", str);
373
				hp = NULL;
374
			}
375
450
		} else if (hp > histptr) {
376
25
			if (approx)
377
25
				hp = hist_get_newest(allow_cur);
378
			else {
379
				bi_errorf("%s: not in history", str);
380
				hp = NULL;
381
			}
382

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

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

675
	if (histptr >= history && last_line != hist_source->line) {
433
225
		hist_source->line--;
434
225
		afree(*histptr, APERM);
435
225
		histptr--;
436
225
		last_line = hist_source->line;
437
225
	}
438
225
}
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
3550
	int	last = histptr - history;
465
466

1775
	if (n < 0 || n >= last) {
467
1775
		current = histptr;
468
1775
		return last;
469
	} else {
470
		current = &history[n];
471
		return n;
472
	}
473
1775
}
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
200
	int	maxhist = histptr - history;
485
100
	int	incr = fwd ? 1 : -1;
486
100
	int	len = strlen(str);
487
488

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

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

300
		if ((anchored && strncmp(*hp, str, len) == 0) ||
494
125
		    (!anchored && strstr(*hp, str)))
495
100
			return hp - history;
496
497
	return -1;
498
100
}
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
150
	char *spec, *tok, *state;
523
524
75
	ignorespace = 0;
525
75
	ignoredups = 0;
526
527
75
	if (str == NULL)
528
		return;
529
530
75
	spec = str_save(str, ATEMP);
531
400
	for (tok = strtok_r(spec, ":", &state); tok != NULL;
532
225
	     tok = strtok_r(NULL, ":", &state)) {
533
125
		if (strcmp(tok, "ignoredups") == 0)
534
			ignoredups = 1;
535
75
		else if (strcmp(tok, "ignorespace") == 0)
536
			ignorespace = 1;
537
	}
538
75
	afree(spec, ATEMP);
539
150
}
540
541
/*
542
 *	set history
543
 *	this means reallocating the dataspace
544
 */
545
void
546
sethistsize(int n)
547
{
548

150
	if (n > 0 && n != histsize) {
549
50
		int offset = histptr - history;
550
551
		/* save most recent history */
552
50
		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
50
		histsize = n;
562
50
		histbase = areallocarray(histbase, n + 1, sizeof(char *), APERM);
563
50
		history = histbase + 1;
564
50
		histptr = history + offset;
565
50
	}
566
50
}
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
17750
	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
8875
}
595
596
/*
597
 *	initialise the history vector
598
 */
599
void
600
init_histvec(void)
601
{
602
435540
	if (histbase == NULL) {
603
217770
		histsize = HISTORYSIZE;
604
		/*
605
		 * allocate one extra element so that histptr always
606
		 * lies within array bounds
607
		 */
608
217770
		histbase = areallocarray(NULL, histsize + 1, sizeof(char *),
609
		    APERM);
610
217770
		history = histbase + 1;
611
217770
		histptr = history - 1;
612
217770
	}
613
217770
}
614
615
static void
616
history_lock(int operation)
617
{
618

29500
	while (flock(fileno(histfh), operation) != 0) {
619
		if (errno == EINTR || errno == EAGAIN)
620
			continue;
621
		else
622
			break;
623
	}
624
5900
}
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

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

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

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

5250
			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
2625
	}
674
675
3985
	if (histptr < history + histsize - 1)
676
3835
		histptr++;
677
	else { /* remove oldest command */
678
150
		afree(*history, APERM);
679
300
		memmove(history, history + 1,
680
150
		    (histsize - 1) * sizeof(*history));
681
	}
682
3985
	*histptr = c;
683
684
3985
	if (dowrite && histfh) {
685
#ifndef SMALL
686
2625
		char *encoded;
687
688
		/* append to file */
689

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

1300
	if (fstat(fd, &sb) == -1 || sb.st_uid != getuid()) {
714
		close(fd);
715
		return NULL;
716
	}
717
650
	fddup = savefd(fd);
718
650
	if (fddup != fd)
719
650
		close(fd);
720
721
650
	if ((f = fdopen(fddup, "r+")) == NULL)
722
		close(fddup);
723
	else
724
650
		last_sb = sb;
725
#endif
726
650
	return f;
727
3624
}
728
729
static void
730
history_close(void)
731
{
732
7032
	if (histfh) {
733
600
		fflush(histfh);
734
600
		fclose(histfh);
735
600
		histfh = NULL;
736
600
	}
737
3516
}
738
739
static int
740
history_load(Source *s)
741
{
742
1300
	char		*p, encoded[LINE + 1], line[LINE + 1];
743
744
650
	rewind(histfh);
745
746
	/* just read it all; will auto resize history upon next command */
747
650
	for (line_co = 1; ; line_co++) {
748
650
		p = fgets(encoded, sizeof(encoded), histfh);
749



650
		if (p == NULL || feof(histfh) || ferror(histfh))
750
			break;
751
		if ((p = strchr(encoded, '\n')) == NULL) {
752
			bi_errorf("history file is corrupt");
753
			return 1;
754
		}
755
		*p = '\0';
756
		s->line = line_co;
757
		s->cmd_offset = line_co;
758
		strunvis(line, encoded);
759
		histsave(line_co, line, 0);
760
	}
761
762
650
	history_write();
763
764
650
	return 0;
765
650
}
766
767
#define HMAGIC1 0xab
768
#define HMAGIC2 0xcd
769
770
void
771
hist_init(Source *s)
772
{
773
	int oldmagic1, oldmagic2;
774
775
7248
	if (Flag(FTALKING) == 0)
776
		return;
777
778
3624
	hstarted = 1;
779
780
3624
	hist_source = s;
781
782
3624
	hname = str_val(global("HISTFILE"));
783
3624
	if (hname == NULL)
784
		return;
785
3624
	hname = str_save(hname, APERM);
786
3624
	histfh = history_open();
787
3624
	if (histfh == NULL)
788
2974
		return;
789
790
650
	oldmagic1 = fgetc(histfh);
791
650
	oldmagic2 = fgetc(histfh);
792
793
650
	if (oldmagic1 == EOF || oldmagic2 == EOF) {
794



1300
		if (!feof(histfh) && ferror(histfh)) {
795
			history_close();
796
			return;
797
		}
798
	} else if (oldmagic1 == HMAGIC1 && oldmagic2 == HMAGIC2) {
799
		bi_errorf("ignoring old style history file");
800
		history_close();
801
		return;
802
	}
803
804
650
	rewind(histfh);
805
806
650
	history_load(s);
807
808
650
	history_lock(LOCK_UN);
809
4274
}
810
811
static void
812
history_write(void)
813
{
814
6550
	char		**hp, *encoded;
815
816
	/* see if file has grown over 25% */
817
3275
	if (line_co < histsize + (histsize / 4))
818
3025
		return;
819
820
	/* rewrite the whole caboodle */
821
250
	rewind(histfh);
822

750
	if (ftruncate(fileno(histfh), 0) == -1) {
823
		bi_errorf("failed to rewrite history file - %s",
824
		    strerror(errno));
825
	}
826
1900
	for (hp = history; hp <= histptr; hp++) {
827
700
		if (stravis(&encoded, *hp, VIS_SAFE | VIS_NL) != -1) {
828
1400
			if (fprintf(histfh, "%s\n", encoded) == -1) {
829
700
				free(encoded);
830
				return;
831
			}
832
			free(encoded);
833
		}
834
	}
835
836
250
	line_co = histsize;
837
838
250
	fflush(histfh);
839
750
	fstat(fileno(histfh), &last_sb);
840
3525
}
841
842
void
843
hist_finish(void)
844
{
845
7032
	history_close();
846
3516
}
847
848
#else /* HISTORY */
849
850
/* No history to be compiled in: dummy routines to avoid lots more ifdefs */
851
void
852
init_histvec(void)
853
{
854
}
855
void
856
hist_init(Source *s)
857
{
858
}
859
void
860
hist_finish(void)
861
{
862
}
863
void
864
histsave(int lno, const char *cmd, int dowrite)
865
{
866
	errorf("history not enabled");
867
}
868
#endif /* HISTORY */