1/*
2 * hist.c - history expansion
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "zsh.mdh"
31#include "hist.pro"
32
33/* Functions to call for getting/ungetting a character and for history
34 * word control. */
35
36/**/
37mod_export int (*hgetc) _((void));
38
39/**/
40void (*hungetc) _((int));
41
42/**/
43void (*hwaddc) _((int));
44
45/**/
46void (*hwbegin) _((int));
47
48/**/
49void (*hwend) _((void));
50
51/**/
52void (*addtoline) _((int));
53
54/* != 0 means history substitution is turned off */
55
56/**/
57mod_export int stophist;
58
59/* if != 0, we are expanding the current line */
60
61/**/
62mod_export int expanding;
63
64/* these are used to modify the cursor position during expansion */
65
66/**/
67mod_export int excs, exlast;
68
69/*
70 * Current history event number
71 *
72 * Note on curhist: with history inactive, this points to the
73 * last line actually added to the history list.  With history active,
74 * the line does not get added to the list until hend(), if at all.
75 * However, curhist is incremented to reflect the current line anyway
76 * and a temporary history entry is inserted while the user is editing.
77 * If the resulting line was not added to the list, a flag is set so
78 * that curhist will be decremented in hbegin().
79 */
80
81/**/
82mod_export zlong curhist;
83
84/**/
85struct histent curline;
86
87/* current line count of allocated history entries */
88
89/**/
90zlong histlinect;
91
92/* The history lines are kept in a hash, and also doubly-linked in a ring */
93
94/**/
95HashTable histtab;
96/**/
97mod_export Histent hist_ring;
98
99/* capacity of history lists */
100
101/**/
102zlong histsiz;
103
104/* desired history-file size (in lines) */
105
106/**/
107zlong savehistsiz;
108
109/* if = 1, we have performed history substitution on the current line *
110 * if = 2, we have used the 'p' modifier                              */
111
112/**/
113int histdone;
114
115/* state of the history mechanism */
116
117/**/
118int histactive;
119
120/* Current setting of the associated option, but sometimes also includes
121 * the setting of the HIST_SAVE_NO_DUPS option. */
122
123/**/
124int hist_ignore_all_dups;
125
126/* What flags (if any) we should skip when moving through the history */
127
128/**/
129mod_export int hist_skip_flags;
130
131/* Bits of histactive variable */
132#define HA_ACTIVE	(1<<0)	/* History mechanism is active */
133#define HA_NOINC	(1<<1)	/* Don't store, curhist not incremented */
134
135/* Array of word beginnings and endings in current history line. */
136
137/**/
138short *chwords;
139
140/* Max, actual position in chwords.
141 * nwords = chwordpos/2 because we record beginning and end of words.
142 */
143
144/**/
145int chwordlen, chwordpos;
146
147/* the last l for s/l/r/ history substitution */
148
149/**/
150char *hsubl;
151
152/* the last r for s/l/r/ history substitution */
153
154/**/
155char *hsubr;
156
157/* pointer into the history line */
158
159/**/
160mod_export char *hptr;
161
162/* the current history line */
163
164/**/
165mod_export char *chline;
166
167/*
168 * The current history line as seen by ZLE.
169 * We modify chline for use in other contexts while ZLE may
170 * still be running; ZLE should see only the top-level value.
171 *
172 * To avoid having to modify this every time we modify chline,
173 * we set it when we push the stack, and unset it when we pop
174 * the appropriate value off the stack.  As it's never modified
175 * on the stack this is the only maintainance we ever do on it.
176 * In return, ZLE has to check both zle_chline and (if that's
177 * NULL) chline to get the current value.
178 */
179
180/**/
181mod_export char *zle_chline;
182
183/* true if the last character returned by hgetc was an escaped bangchar *
184 * if it is set and NOBANGHIST is unset hwaddc escapes bangchars        */
185
186/**/
187int qbang;
188
189/* max size of histline */
190
191/**/
192int hlinesz;
193
194/* default event (usually curhist-1, that is, "!!") */
195
196static zlong defev;
197
198/* Remember the last line in the history file so we can find it again. */
199static struct histfile_stats {
200    char *text;
201    time_t stim, mtim;
202    off_t fpos, fsiz;
203    zlong next_write_ev;
204} lasthist;
205
206static struct histsave {
207    struct histfile_stats lasthist;
208    char *histfile;
209    HashTable histtab;
210    Histent hist_ring;
211    zlong curhist;
212    zlong histlinect;
213    zlong histsiz;
214    zlong savehistsiz;
215    int locallevel;
216} *histsave_stack;
217static int histsave_stack_size = 0;
218static int histsave_stack_pos = 0;
219
220static zlong histfile_linect;
221
222/* add a character to the current history word */
223
224static void
225ihwaddc(int c)
226{
227    /* Only if history line exists and lexing has not finished. */
228    if (chline && !(errflag || lexstop)) {
229	/* Quote un-expanded bangs in the history line. */
230	if (c == bangchar && stophist < 2 && qbang)
231	    /* If qbang is not set, we do not escape this bangchar as it's *
232	     * not necessary (e.g. it's a bang in !=, or it is followed    *
233	     * by a space). Roughly speaking, qbang is zero only if the    *
234	     * history interpreter has already digested this bang and      *
235	     * found that it is not necessary to escape it.                */
236	    hwaddc('\\');
237	*hptr++ = c;
238
239	/* Resize history line if necessary */
240	if (hptr - chline >= hlinesz) {
241	    int oldsiz = hlinesz;
242
243	    chline = realloc(chline, hlinesz = oldsiz + 64);
244	    hptr = chline + oldsiz;
245	}
246    }
247}
248
249/* This function adds a character to the zle input line. It is used when *
250 * zsh expands history (see doexpandhist() in zle_tricky.c). It also     *
251 * calculates the new cursor position after the expansion. It is called  *
252 * from hgetc() and from gettok() in lex.c for characters in comments.   */
253
254/**/
255void
256iaddtoline(int c)
257{
258    if (!expanding || lexstop)
259	return;
260    if (qbang && c == bangchar && stophist < 2) {
261	exlast--;
262	zleentry(ZLE_CMD_ADD_TO_LINE, '\\');
263    }
264    if (excs > zlemetacs) {
265	excs += 1 + inbufct - exlast;
266	if (excs < zlemetacs)
267	    /* this case could be handled better but it is    *
268	     * so rare that it does not worth it              */
269	    excs = zlemetacs;
270    }
271    exlast = inbufct;
272    zleentry(ZLE_CMD_ADD_TO_LINE, itok(c) ? ztokens[c - Pound] : c);
273}
274
275
276static int
277ihgetc(void)
278{
279    int c = ingetc();
280
281    qbang = 0;
282    if (!stophist && !(inbufflags & INP_ALIAS)) {
283	/* If necessary, expand history characters. */
284	c = histsubchar(c);
285	if (c < 0) {
286	    /* bad expansion */
287	    errflag = lexstop = 1;
288	    return ' ';
289	}
290    }
291    if ((inbufflags & INP_HIST) && !stophist) {
292	/* the current character c came from a history expansion          *
293	 * (inbufflags & INP_HIST) and history is not disabled            *
294	 * (e.g. we are not inside single quotes). In that case, \!       *
295	 * should be treated as ! (since this \! came from a previous     *
296	 * history line where \ was used to escape the bang). So if       *
297	 * c == '\\' we fetch one more character to see if it's a bang,   *
298	 * and if it is not, we unget it and reset c back to '\\'         */
299	qbang = 0;
300	if (c == '\\' && !(qbang = (c = ingetc()) == bangchar))
301	    safeinungetc(c), c = '\\';
302    } else if (stophist || (inbufflags & INP_ALIAS))
303	/* If the result is a bangchar which came from history or alias  *
304	 * expansion, we treat it as an escaped bangchar, unless history *
305	 * is disabled. If stophist == 1 it only means that history is   *
306	 * temporarily disabled by a !" which won't appear in the        *
307	 * history, so we still have an escaped bang. stophist > 1 if    *
308	 * history is disabled with NOBANGHIST or by someone else (e.g.  *
309	 * when the lexer scans single quoted text).                     */
310	qbang = c == bangchar && (stophist < 2);
311    hwaddc(c);
312    addtoline(c);
313
314    return c;
315}
316
317/**/
318static void
319safeinungetc(int c)
320{
321    if (lexstop)
322	lexstop = 0;
323    else
324	inungetc(c);
325}
326
327/**/
328void
329herrflush(void)
330{
331    inpopalias();
332
333    while (!lexstop && inbufct && !strin)
334	hwaddc(ingetc());
335}
336
337/*
338 * Extract :s/foo/bar/ delimiters and arguments
339 *
340 * The first character expected is the first delimiter.
341 * The arguments are stored in the hsubl and hsubr variables.
342 *
343 * subline is the part of the command line to be matched.
344 *
345 * If a ':' was found but was not followed by a 'G',
346 * *cflagp is set to 1 and the input is backed up to the
347 * character following the colon.
348 */
349
350/**/
351static int
352getsubsargs(char *subline, int *gbalp, int *cflagp)
353{
354    int del, follow;
355    char *ptr1, *ptr2;
356
357    del = ingetc();
358    ptr1 = hdynread2(del);
359    if (!ptr1)
360	return 1;
361    ptr2 = hdynread2(del);
362    if (strlen(ptr1)) {
363	zsfree(hsubl);
364	hsubl = ptr1;
365    } else if (!hsubl) {		/* fail silently on this */
366	zsfree(ptr2);
367	return 0;
368    }
369    zsfree(hsubr);
370    hsubr = ptr2;
371    follow = ingetc();
372    if (follow == ':') {
373	follow = ingetc();
374	if (follow == 'G')
375	    *gbalp = 1;
376	else {
377	    inungetc(follow);
378	    *cflagp = 1;
379	}
380    } else
381	inungetc(follow);
382    return 0;
383}
384
385/* Get the maximum no. of words for a history entry. */
386
387/**/
388static int
389getargc(Histent ehist)
390{
391    return ehist->nwords ? ehist->nwords-1 : 0;
392}
393
394/**/
395static int
396substfailed(void)
397{
398    herrflush();
399    zerr("substitution failed");
400    return -1;
401}
402
403/* Perform history substitution, returning the next character afterwards. */
404
405/**/
406static int
407histsubchar(int c)
408{
409    int farg, evset = -1, larg, argc, cflag = 0, bflag = 0;
410    zlong ev;
411    static int marg = -1;
412    static zlong mev = -1;
413    char *buf, *ptr;
414    char *sline;
415    Histent ehist;
416    size_t buflen;
417
418    /* look, no goto's */
419    if (isfirstch && c == hatchar) {
420	int gbal = 0;
421
422	/* Line begins ^foo^bar */
423	isfirstch = 0;
424	inungetc(hatchar);
425	if (!(ehist = gethist(defev))
426	    || !(sline = getargs(ehist, 0, getargc(ehist))))
427	    return -1;
428
429	if (getsubsargs(sline, &gbal, &cflag))
430	    return substfailed();
431	if (!hsubl)
432	    return -1;
433	if (subst(&sline, hsubl, hsubr, gbal))
434	    return substfailed();
435    } else {
436	/* Line doesn't begin ^foo^bar */
437	if (c != ' ')
438	    isfirstch = 0;
439	if (c == '\\') {
440	    int g = ingetc();
441
442	    if (g != bangchar)
443		safeinungetc(g);
444	    else {
445		qbang = 1;
446		return bangchar;
447	    }
448	}
449	if (c != bangchar)
450	    return c;
451	*hptr = '\0';
452	if ((c = ingetc()) == '{') {
453	    bflag = cflag = 1;
454	    c = ingetc();
455	}
456	if (c == '\"') {
457	    stophist = 1;
458	    return ingetc();
459	}
460	if ((!cflag && inblank(c)) || c == '=' || c == '(' || lexstop) {
461	    safeinungetc(c);
462	    return bangchar;
463	}
464	cflag = 0;
465	ptr = buf = zhalloc(buflen = 265);
466
467	/* get event number */
468
469	queue_signals();
470	if (c == '?') {
471	    for (;;) {
472		c = ingetc();
473		if (c == '?' || c == '\n' || lexstop)
474		    break;
475		else {
476		    *ptr++ = c;
477		    if (ptr == buf + buflen) {
478			buf = hrealloc(buf, buflen, 2 * buflen);
479			ptr = buf + buflen;
480			buflen *= 2;
481		    }
482		}
483	    }
484	    if (c != '\n' && !lexstop)
485		c = ingetc();
486	    *ptr = '\0';
487	    mev = ev = hconsearch(hsubl = ztrdup(buf), &marg);
488	    evset = 0;
489	    if (ev == -1) {
490		herrflush();
491		unqueue_signals();
492		zerr("no such event: %s", buf);
493		return -1;
494	    }
495	} else {
496	    zlong t0;
497
498	    for (;;) {
499		if (inblank(c) || c == ';' || c == ':' || c == '^' ||
500		    c == '$' || c == '*' || c == '%' || c == '}' ||
501		    c == '\'' || c == '"' || c == '`' || lexstop)
502		    break;
503		if (ptr != buf) {
504		    if (c == '-')
505			break;
506		    if ((idigit(buf[0]) || buf[0] == '-') && !idigit(c))
507			break;
508		}
509		*ptr++ = c;
510		if (ptr == buf + buflen) {
511		    buf = hrealloc(buf, buflen, 2 * buflen);
512		    ptr = buf + buflen;
513		    buflen *= 2;
514		}
515		if (c == '#' || c == bangchar) {
516		    c = ingetc();
517		    break;
518		}
519		c = ingetc();
520	    }
521	    *ptr = 0;
522	    if (!*buf) {
523		if (c != '%') {
524		    if (isset(CSHJUNKIEHISTORY))
525			ev = addhistnum(curhist,-1,HIST_FOREIGN);
526		    else
527			ev = defev;
528		    if (c == ':' && evset == -1)
529			evset = 0;
530		    else
531			evset = 1;
532		} else {
533		    if (marg != -1)
534			ev = mev;
535		    else
536			ev = defev;
537		    evset = 0;
538		}
539	    } else if ((t0 = zstrtol(buf, NULL, 10))) {
540		ev = (t0 < 0) ? addhistnum(curhist,t0,HIST_FOREIGN) : t0;
541		evset = 1;
542	    } else if ((unsigned)*buf == bangchar) {
543		ev = addhistnum(curhist,-1,HIST_FOREIGN);
544		evset = 1;
545	    } else if (*buf == '#') {
546		ev = curhist;
547		evset = 1;
548	    } else if ((ev = hcomsearch(buf)) == -1) {
549		herrflush();
550		unqueue_signals();
551		zerr("event not found: %s", buf);
552		return -1;
553	    } else
554		evset = 1;
555	}
556
557	/* get the event */
558
559	if (!(ehist = gethist(defev = ev))) {
560	    unqueue_signals();
561	    return -1;
562	}
563	/* extract the relevant arguments */
564
565	argc = getargc(ehist);
566	if (c == ':') {
567	    cflag = 1;
568	    c = ingetc();
569	    if (c == '%' && marg != -1) {
570		if (!evset) {
571		    ehist = gethist(defev = mev);
572		    argc = getargc(ehist);
573		} else {
574		    herrflush();
575		    unqueue_signals();
576		    zerr("ambiguous history reference");
577		    return -1;
578		}
579
580	    }
581	}
582	if (c == '*') {
583	    farg = 1;
584	    larg = argc;
585	    cflag = 0;
586	} else {
587	    inungetc(c);
588	    larg = farg = getargspec(argc, marg, evset);
589	    if (larg == -2) {
590		unqueue_signals();
591		return -1;
592	    }
593	    if (farg != -1)
594		cflag = 0;
595	    c = ingetc();
596	    if (c == '*') {
597		cflag = 0;
598		larg = argc;
599	    } else if (c == '-') {
600		cflag = 0;
601		larg = getargspec(argc, marg, evset);
602		if (larg == -2) {
603		    unqueue_signals();
604		    return -1;
605		}
606		if (larg == -1)
607		    larg = argc - 1;
608	    } else
609		inungetc(c);
610	}
611	if (farg == -1)
612	    farg = 0;
613	if (larg == -1)
614	    larg = argc;
615	if (!(sline = getargs(ehist, farg, larg))) {
616	    unqueue_signals();
617	    return -1;
618	}
619	unqueue_signals();
620    }
621
622    /* do the modifiers */
623
624    for (;;) {
625	c = (cflag) ? ':' : ingetc();
626	cflag = 0;
627	if (c == ':') {
628	    int gbal = 0;
629
630	    if ((c = ingetc()) == 'g') {
631		gbal = 1;
632		c = ingetc();
633		if (c != 's' && c != '&') {
634		    zerr("'s' or '&' modifier expected after 'g'");
635		    return -1;
636		}
637	    }
638	    switch (c) {
639	    case 'p':
640		histdone = HISTFLAG_DONE | HISTFLAG_NOEXEC;
641		break;
642	    case 'a':
643		if (!chabspath(&sline)) {
644		    herrflush();
645		    zerr("modifier failed: a");
646		    return -1;
647		}
648		break;
649
650	    case 'A':
651		if (!chrealpath(&sline)) {
652		    herrflush();
653		    zerr("modifier failed: A");
654		    return -1;
655		}
656		break;
657	    case 'c':
658		if (!(sline = equalsubstr(sline, 0, 0))) {
659		    herrflush();
660		    zerr("modifier failed: c");
661		    return -1;
662		}
663		break;
664	    case 'h':
665		if (!remtpath(&sline)) {
666		    herrflush();
667		    zerr("modifier failed: h");
668		    return -1;
669		}
670		break;
671	    case 'e':
672		if (!rembutext(&sline)) {
673		    herrflush();
674		    zerr("modifier failed: e");
675		    return -1;
676		}
677		break;
678	    case 'r':
679		if (!remtext(&sline)) {
680		    herrflush();
681		    zerr("modifier failed: r");
682		    return -1;
683		}
684		break;
685	    case 't':
686		if (!remlpaths(&sline)) {
687		    herrflush();
688		    zerr("modifier failed: t");
689		    return -1;
690		}
691		break;
692	    case 's':
693		if (getsubsargs(sline, &gbal, &cflag))
694		    return -1; /* fall through */
695	    case '&':
696		if (hsubl && hsubr) {
697		    if (subst(&sline, hsubl, hsubr, gbal))
698			return substfailed();
699		} else {
700		    herrflush();
701		    zerr("no previous substitution");
702		    return -1;
703		}
704		break;
705	    case 'q':
706		quote(&sline);
707		break;
708	    case 'Q':
709		{
710		    int one = noerrs, oef = errflag;
711
712		    noerrs = 1;
713		    parse_subst_string(sline);
714		    noerrs = one;
715		    errflag = oef;
716		    remnulargs(sline);
717		    untokenize(sline);
718		}
719		break;
720	    case 'x':
721		quotebreak(&sline);
722		break;
723	    case 'l':
724		sline = casemodify(sline, CASMOD_LOWER);
725		break;
726	    case 'u':
727		sline = casemodify(sline, CASMOD_UPPER);
728		break;
729	    default:
730		herrflush();
731		zerr("illegal modifier: %c", c);
732		return -1;
733	    }
734	} else {
735	    if (c != '}' || !bflag)
736		inungetc(c);
737	    if (c != '}' && bflag) {
738		zerr("'}' expected");
739		return -1;
740	    }
741	    break;
742	}
743    }
744
745    /*
746     * Push the expanded value onto the input stack,
747     * marking this as a history word for purposes of the alias stack.
748     */
749
750    lexstop = 0;
751    /* this function is called only called from hgetc and only if      *
752     * !(inbufflags & INP_ALIAS). History expansion should never be    *
753     * done with INP_ALIAS (to prevent recursive history expansion and *
754     * histoty expansion of aliases). Escapes are not removed here.    *
755     * This is now handled in hgetc.                                   */
756    inpush(sline, INP_HIST, NULL); /* sline from heap, don't free */
757    histdone |= HISTFLAG_DONE;
758    if (isset(HISTVERIFY))
759	histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
760
761    /* Don't try and re-expand line. */
762    return ingetc();
763}
764
765/* unget a char and remove it from chline. It can only be used *
766 * to unget a character returned by hgetc.                     */
767
768static void
769ihungetc(int c)
770{
771    int doit = 1;
772
773    while (!lexstop && !errflag) {
774	if (hptr[-1] != (char) c && stophist < 4 &&
775	    hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\')
776	    hungetc('\n'), hungetc('\\');
777
778	if (expanding) {
779	    zlemetacs--;
780	    zlemetall--;
781	    exlast++;
782	}
783	DPUTS(hptr <= chline, "BUG: hungetc attempted at buffer start");
784	hptr--;
785	DPUTS(*hptr != (char) c, "BUG: wrong character in hungetc() ");
786	qbang = (c == bangchar && stophist < 2 &&
787		 hptr > chline && hptr[-1] == '\\');
788	if (doit)
789	    inungetc(c);
790	if (!qbang)
791	    return;
792	doit = !stophist && ((inbufflags & INP_HIST) ||
793				 !(inbufflags & INP_ALIAS));
794	c = '\\';
795    }
796}
797
798/* begin reading a string */
799
800/**/
801mod_export void
802strinbeg(int dohist)
803{
804    strin++;
805    hbegin(dohist);
806    lexinit();
807}
808
809/* done reading a string */
810
811/**/
812mod_export void
813strinend(void)
814{
815    hend(NULL);
816    DPUTS(!strin, "BUG: strinend() called without strinbeg()");
817    strin--;
818    isfirstch = 1;
819    histdone = 0;
820}
821
822/* dummy functions to use instead of hwaddc(), hwbegin(), and hwend() when
823 * they aren't needed */
824
825static void
826nohw(UNUSED(int c))
827{
828}
829
830static void
831nohwe(void)
832{
833}
834
835/* these functions handle adding/removing curline to/from the hist_ring */
836
837static void
838linkcurline(void)
839{
840    if (!hist_ring)
841	hist_ring = curline.up = curline.down = &curline;
842    else {
843	curline.up = hist_ring;
844	curline.down = hist_ring->down;
845	hist_ring->down = hist_ring->down->up = &curline;
846	hist_ring = &curline;
847    }
848    curline.histnum = ++curhist;
849}
850
851static void
852unlinkcurline(void)
853{
854    curline.up->down = curline.down;
855    curline.down->up = curline.up;
856    if (hist_ring == &curline) {
857	if (!histlinect)
858	    hist_ring = NULL;
859	else
860	    hist_ring = curline.up;
861    }
862    curhist--;
863}
864
865/* initialize the history mechanism */
866
867/**/
868mod_export void
869hbegin(int dohist)
870{
871    isfirstln = isfirstch = 1;
872    errflag = histdone = 0;
873    if (!dohist)
874	stophist = 2;
875    else if (dohist != 2)
876	stophist = (!interact || unset(SHINSTDIN)) ? 2 : 0;
877    else
878	stophist = 0;
879    /*
880     * pws: We used to test for "|| (inbufflags & INP_ALIAS)"
881     * in this test, but at this point we don't have input
882     * set up up so this can trigger unnecessarily.
883     * I don't see how the test at this point could ever be
884     * useful, since we only get here when we're initialising
885     * the history mechanism, before we've done any input.
886     *
887     * (I also don't see any point where this function is called with
888     * dohist=0.)
889     */
890    if (stophist == 2) {
891	chline = hptr = NULL;
892	hlinesz = 0;
893	chwords = NULL;
894	chwordlen = 0;
895	hgetc = ingetc;
896	hungetc = inungetc;
897	hwaddc = nohw;
898	hwbegin = nohw;
899	hwend = nohwe;
900	addtoline = nohw;
901    } else {
902	chline = hptr = zshcalloc(hlinesz = 64);
903	chwords = zalloc((chwordlen = 64) * sizeof(short));
904	hgetc = ihgetc;
905	hungetc = ihungetc;
906	hwaddc = ihwaddc;
907	hwbegin = ihwbegin;
908	hwend = ihwend;
909	addtoline = iaddtoline;
910	if (!isset(BANGHIST))
911	    stophist = 4;
912    }
913    chwordpos = 0;
914
915    if (hist_ring && !hist_ring->ftim && !strin)
916	hist_ring->ftim = time(NULL);
917    if ((dohist == 2 || (interact && isset(SHINSTDIN))) && !strin) {
918	histactive = HA_ACTIVE;
919	attachtty(mypgrp);
920	linkcurline();
921	defev = addhistnum(curhist, -1, HIST_FOREIGN);
922    } else
923	histactive = HA_ACTIVE | HA_NOINC;
924}
925
926/**/
927void
928histreduceblanks(void)
929{
930    int i, len, pos, needblank, spacecount = 0;
931
932    if (isset(HISTIGNORESPACE))
933	while (chline[spacecount] == ' ') spacecount++;
934
935    for (i = 0, len = spacecount; i < chwordpos; i += 2) {
936	len += chwords[i+1] - chwords[i]
937	     + (i > 0 && chwords[i] > chwords[i-1]);
938    }
939    if (chline[len] == '\0')
940	return;
941
942    for (i = 0, pos = spacecount; i < chwordpos; i += 2) {
943	len = chwords[i+1] - chwords[i];
944	needblank = (i < chwordpos-2 && chwords[i+2] > chwords[i+1]);
945	if (pos != chwords[i]) {
946	    memcpy(chline + pos, chline + chwords[i], len + needblank);
947	    chwords[i] = pos;
948	    chwords[i+1] = chwords[i] + len;
949	}
950	pos += len + needblank;
951    }
952    chline[pos] = '\0';
953}
954
955/**/
956void
957histremovedups(void)
958{
959    Histent he, next;
960    for (he = hist_ring; he; he = next) {
961	next = up_histent(he);
962	if (he->node.flags & HIST_DUP)
963	    freehistnode(&he->node);
964    }
965}
966
967/**/
968mod_export zlong
969addhistnum(zlong hl, int n, int xflags)
970{
971    int dir = n < 0? -1 : n > 0? 1 : 0;
972    Histent he = gethistent(hl, dir);
973
974    if (!he)
975	return 0;
976    if (he->histnum != hl)
977	n -= dir;
978    if (n)
979	he = movehistent(he, n, xflags);
980    if (!he)
981	return dir < 0? firsthist() - 1 : curhist + 1;
982    return he->histnum;
983}
984
985/**/
986mod_export Histent
987movehistent(Histent he, int n, int xflags)
988{
989    while (n < 0) {
990	if (!(he = up_histent(he)))
991	    return NULL;
992	if (!(he->node.flags & xflags))
993	    n++;
994    }
995    while (n > 0) {
996	if (!(he = down_histent(he)))
997	    return NULL;
998	if (!(he->node.flags & xflags))
999	    n--;
1000    }
1001    checkcurline(he);
1002    return he;
1003}
1004
1005/**/
1006mod_export Histent
1007up_histent(Histent he)
1008{
1009    return !he || he->up == hist_ring? NULL : he->up;
1010}
1011
1012/**/
1013mod_export Histent
1014down_histent(Histent he)
1015{
1016    return he == hist_ring? NULL : he->down;
1017}
1018
1019/**/
1020mod_export Histent
1021gethistent(zlong ev, int nearmatch)
1022{
1023    Histent he;
1024
1025    if (!hist_ring)
1026	return NULL;
1027
1028    if (ev - hist_ring->down->histnum < hist_ring->histnum - ev) {
1029	for (he = hist_ring->down; he->histnum < ev; he = he->down) ;
1030	if (he->histnum != ev) {
1031	    if (nearmatch == 0
1032	     || (nearmatch < 0 && (he = up_histent(he)) == NULL))
1033		return NULL;
1034	}
1035    }
1036    else {
1037	for (he = hist_ring; he->histnum > ev; he = he->up) ;
1038	if (he->histnum != ev) {
1039	    if (nearmatch == 0
1040	     || (nearmatch > 0 && (he = down_histent(he)) == NULL))
1041		return NULL;
1042	}
1043    }
1044
1045    checkcurline(he);
1046    return he;
1047}
1048
1049static void
1050putoldhistentryontop(short keep_going)
1051{
1052    static Histent next = NULL;
1053    Histent he = keep_going? next : hist_ring->down;
1054    next = he->down;
1055    if (isset(HISTEXPIREDUPSFIRST) && !(he->node.flags & HIST_DUP)) {
1056	static zlong max_unique_ct = 0;
1057	if (!keep_going)
1058	    max_unique_ct = savehistsiz;
1059	do {
1060	    if (max_unique_ct-- <= 0 || he == hist_ring) {
1061		max_unique_ct = 0;
1062		he = hist_ring->down;
1063		next = hist_ring;
1064		break;
1065	    }
1066	    he = next;
1067	    next = he->down;
1068	} while (!(he->node.flags & HIST_DUP));
1069    }
1070    if (he != hist_ring->down) {
1071	he->up->down = he->down;
1072	he->down->up = he->up;
1073	he->up = hist_ring;
1074	he->down = hist_ring->down;
1075	hist_ring->down = he->down->up = he;
1076    }
1077    hist_ring = he;
1078}
1079
1080/**/
1081Histent
1082prepnexthistent(void)
1083{
1084    Histent he;
1085    int curline_in_ring = hist_ring == &curline;
1086
1087    if (curline_in_ring)
1088	unlinkcurline();
1089    if (hist_ring && hist_ring->node.flags & HIST_TMPSTORE) {
1090	curhist--;
1091	freehistnode(&hist_ring->node);
1092    }
1093
1094    if (histlinect < histsiz) {
1095	he = (Histent)zshcalloc(sizeof *he);
1096	if (!hist_ring)
1097	    hist_ring = he->up = he->down = he;
1098	else {
1099	    he->up = hist_ring;
1100	    he->down = hist_ring->down;
1101	    hist_ring->down = he->down->up = he;
1102	    hist_ring = he;
1103	}
1104	histlinect++;
1105    }
1106    else {
1107	putoldhistentryontop(0);
1108	freehistdata(hist_ring, 0);
1109	he = hist_ring;
1110    }
1111    he->histnum = ++curhist;
1112    if (curline_in_ring)
1113	linkcurline();
1114    return he;
1115}
1116
1117/* A helper function for hend() */
1118
1119static int
1120should_ignore_line(Eprog prog)
1121{
1122    if (isset(HISTIGNORESPACE)) {
1123	if (*chline == ' ' || aliasspaceflag)
1124	    return 1;
1125    }
1126
1127    if (!prog)
1128	return 0;
1129
1130    if (isset(HISTNOFUNCTIONS)) {
1131	Wordcode pc = prog->prog;
1132	wordcode code = *pc;
1133	if (wc_code(code) == WC_LIST && WC_LIST_TYPE(code) & Z_SIMPLE
1134	 && wc_code(pc[2]) == WC_FUNCDEF)
1135	    return 1;
1136    }
1137
1138    if (isset(HISTNOSTORE)) {
1139	char *b = getjobtext(prog, NULL);
1140	int saw_builtin;
1141	if (*b == 'b' && strncmp(b,"builtin ",8) == 0) {
1142	    b += 8;
1143	    saw_builtin = 1;
1144	} else
1145	    saw_builtin = 0;
1146	if (*b == 'h' && strncmp(b,"history",7) == 0 && (!b[7] || b[7] == ' ')
1147	 && (saw_builtin || !shfunctab->getnode(shfunctab,"history")))
1148	    return 1;
1149	if (*b == 'r' && (!b[1] || b[1] == ' ')
1150	 && (saw_builtin || !shfunctab->getnode(shfunctab,"r")))
1151	    return 1;
1152	if (*b == 'f' && b[1] == 'c' && b[2] == ' ' && b[3] == '-'
1153	 && (saw_builtin || !shfunctab->getnode(shfunctab,"fc"))) {
1154	    b += 3;
1155	    do {
1156		if (*++b == 'l')
1157		    return 1;
1158	    } while (ialpha(*b));
1159	}
1160    }
1161
1162    return 0;
1163}
1164
1165/* say we're done using the history mechanism */
1166
1167/**/
1168mod_export int
1169hend(Eprog prog)
1170{
1171    LinkList hookargs = newlinklist();
1172    int flag, save = 1, hookret, stack_pos = histsave_stack_pos;
1173    char *hf;
1174
1175    DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline,
1176	  "BUG: chline is NULL in hend()");
1177    queue_signals();
1178    if (histdone & HISTFLAG_SETTY)
1179	settyinfo(&shttyinfo);
1180    if (!(histactive & HA_NOINC))
1181	unlinkcurline();
1182    if (histactive & HA_NOINC) {
1183	zfree(chline, hlinesz);
1184	zfree(chwords, chwordlen*sizeof(short));
1185	chline = hptr = NULL;
1186	chwords = NULL;
1187	histactive = 0;
1188	unqueue_signals();
1189	return 1;
1190    }
1191    if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS)
1192     && (hist_ignore_all_dups = isset(HISTIGNOREALLDUPS)) != 0)
1193	histremovedups();
1194
1195    if (hptr) {
1196	/*
1197	 * Added the following in case the test "hptr < chline + 1"
1198	 * is more than just paranoia.
1199	 */
1200	DPUTS(hptr < chline, "History end pointer off start of line");
1201	*hptr = '\0';
1202    }
1203    addlinknode(hookargs, "zshaddhistory");
1204    addlinknode(hookargs, chline);
1205    callhookfunc("zshaddhistory", hookargs, 1, &hookret);
1206    /* For history sharing, lock history file once for both read and write */
1207    hf = getsparam("HISTFILE");
1208    if (isset(SHAREHISTORY) && !lockhistfile(hf, 0)) {
1209	readhistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
1210	curline.histnum = curhist+1;
1211    }
1212    flag = histdone;
1213    histdone = 0;
1214    if (hptr < chline + 1)
1215	save = 0;
1216    else {
1217	if (hptr[-1] == '\n') {
1218	    if (chline[1]) {
1219		*--hptr = '\0';
1220	    } else
1221		save = 0;
1222	}
1223	if (chwordpos <= 2)
1224	    save = 0;
1225	else if (hookret || should_ignore_line(prog))
1226	    save = -1;
1227    }
1228    if (flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) {
1229	char *ptr;
1230
1231	ptr = ztrdup(chline);
1232	if ((flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) == HISTFLAG_DONE) {
1233	    zputs(ptr, shout);
1234	    fputc('\n', shout);
1235	    fflush(shout);
1236	}
1237	if (flag & HISTFLAG_RECALL) {
1238	    zpushnode(bufstack, ptr);
1239	    save = 0;
1240	} else
1241	    zsfree(ptr);
1242    }
1243    if (save || *chline == ' ') {
1244	Histent he;
1245	for (he = hist_ring; he && he->node.flags & HIST_FOREIGN;
1246	     he = up_histent(he)) ;
1247	if (he && he->node.flags & HIST_TMPSTORE) {
1248	    if (he == hist_ring)
1249		curline.histnum = curhist--;
1250	    freehistnode(&he->node);
1251	}
1252    }
1253    if (save) {
1254	Histent he;
1255	int newflags;
1256
1257#ifdef DEBUG
1258	/* debugging only */
1259	if (chwordpos%2) {
1260	    hwend();
1261	    DPUTS(1, "BUG: uncompleted line in history");
1262	}
1263#endif
1264	/* get rid of pesky \n which we've already nulled out */
1265	if (chwordpos > 1 && !chline[chwords[chwordpos-2]]) {
1266	    chwordpos -= 2;
1267	    /* strip superfluous blanks, if desired */
1268	    if (isset(HISTREDUCEBLANKS))
1269		histreduceblanks();
1270	}
1271	newflags = save > 0? 0 : HIST_TMPSTORE;
1272	if ((isset(HISTIGNOREDUPS) || isset(HISTIGNOREALLDUPS)) && save > 0
1273	 && hist_ring && histstrcmp(chline, hist_ring->node.nam) == 0) {
1274	    /* This history entry compares the same as the previous.
1275	     * In case minor changes were made, we overwrite the
1276	     * previous one with the current one.  This also gets the
1277	     * timestamp right.  Perhaps, preserve the HIST_OLD flag.
1278	     */
1279	    he = hist_ring;
1280	    newflags |= he->node.flags & HIST_OLD; /* Avoid re-saving */
1281	    freehistdata(he, 0);
1282	    curline.histnum = curhist;
1283	} else
1284	    he = prepnexthistent();
1285
1286	he->node.nam = ztrdup(chline);
1287	he->stim = time(NULL);
1288	he->ftim = 0L;
1289	he->node.flags = newflags;
1290
1291	if ((he->nwords = chwordpos/2)) {
1292	    he->words = (short *)zalloc(chwordpos * sizeof(short));
1293	    memcpy(he->words, chwords, chwordpos * sizeof(short));
1294	}
1295	if (!(newflags & HIST_TMPSTORE))
1296	    addhistnode(histtab, he->node.nam, he);
1297    }
1298    zfree(chline, hlinesz);
1299    zfree(chwords, chwordlen*sizeof(short));
1300    chline = hptr = NULL;
1301    chwords = NULL;
1302    histactive = 0;
1303    if (isset(SHAREHISTORY)? histfileIsLocked() : isset(INCAPPENDHISTORY))
1304	savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
1305    unlockhistfile(hf); /* It's OK to call this even if we aren't locked */
1306    /*
1307     * No good reason for the user to push the history more than once, but
1308     * it's easy to be tidy...
1309     */
1310    while (histsave_stack_pos > stack_pos)
1311	pophiststack();
1312    unqueue_signals();
1313    return !(flag & HISTFLAG_NOEXEC || errflag);
1314}
1315
1316/* Gives current expansion word if not last word before chwordpos. */
1317
1318/**/
1319int hwgetword = -1;
1320
1321/* begin a word */
1322
1323/**/
1324void
1325ihwbegin(int offset)
1326{
1327    if (stophist == 2)
1328	return;
1329    if (chwordpos%2)
1330	chwordpos--;	/* make sure we're on a word start, not end */
1331    /* If we're expanding an alias, we should overwrite the expansion
1332     * in the history.
1333     */
1334    if ((inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST))
1335	hwgetword = chwordpos;
1336    else
1337	hwgetword = -1;
1338    chwords[chwordpos++] = hptr - chline + offset;
1339}
1340
1341/* add a word to the history List */
1342
1343/**/
1344void
1345ihwend(void)
1346{
1347    if (stophist == 2)
1348	return;
1349    if (chwordpos%2 && chline) {
1350	/* end of word reached and we've already begun a word */
1351	if (hptr > chline + chwords[chwordpos-1]) {
1352	    chwords[chwordpos++] = hptr - chline;
1353	    if (chwordpos >= chwordlen) {
1354		chwords = (short *) realloc(chwords,
1355					    (chwordlen += 32) *
1356					    sizeof(short));
1357	    }
1358	    if (hwgetword > -1 &&
1359		(inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST)) {
1360		/* We want to reuse the current word position */
1361		chwordpos = hwgetword;
1362		/* Start from where previous word ended, if possible */
1363		hptr = chline + chwords[chwordpos ? chwordpos - 1 : 0];
1364	    }
1365	} else {
1366	    /* scrub that last word, it doesn't exist */
1367	    chwordpos--;
1368	}
1369    }
1370}
1371
1372/* Go back to immediately after the last word, skipping space. */
1373
1374/**/
1375void
1376histbackword(void)
1377{
1378    if (!(chwordpos%2) && chwordpos)
1379	hptr = chline + chwords[chwordpos-1];
1380}
1381
1382/* Get the start and end point of the current history word */
1383
1384/**/
1385static void
1386hwget(char **startptr)
1387{
1388    int pos = hwgetword > -1 ? hwgetword : chwordpos - 2;
1389
1390#ifdef DEBUG
1391    /* debugging only */
1392    if (hwgetword == -1 && !chwordpos) {
1393	/* no words available */
1394	DPUTS(1, "BUG: hwget() called with no words");
1395	*startptr = "";
1396	return;
1397    }
1398    else if (hwgetword == -1 && chwordpos%2) {
1399	DPUTS(1, "BUG: hwget() called in middle of word");
1400	*startptr = "";
1401	return;
1402    }
1403#endif
1404
1405    *startptr = chline + chwords[pos];
1406    chline[chwords[++pos]] = '\0';
1407}
1408
1409/* Replace the current history word with rep, if different */
1410
1411/**/
1412void
1413hwrep(char *rep)
1414{
1415    char *start;
1416    hwget(&start);
1417
1418    if (!strcmp(rep, start))
1419	return;
1420
1421    hptr = start;
1422    chwordpos = (hwgetword > -1) ? hwgetword : chwordpos - 2;
1423    hwbegin(0);
1424    qbang = 1;
1425    while (*rep)
1426	hwaddc(*rep++);
1427    hwend();
1428}
1429
1430/* Get the entire current line, deleting it in the history. */
1431
1432/**/
1433mod_export char *
1434hgetline(void)
1435{
1436    /* Currently only used by pushlineoredit().
1437     * It's necessary to prevent that from getting too pally with
1438     * the history code.
1439     */
1440    char *ret;
1441
1442    if (!chline || hptr == chline)
1443	return NULL;
1444    *hptr = '\0';
1445    ret = dupstring(chline);
1446
1447    /* reset line */
1448    hptr = chline;
1449    chwordpos = 0;
1450    hwgetword = -1;
1451
1452    return ret;
1453}
1454
1455/* get an argument specification */
1456
1457/**/
1458static int
1459getargspec(int argc, int marg, int evset)
1460{
1461    int c, ret = -1;
1462
1463    if ((c = ingetc()) == '0')
1464	return 0;
1465    if (idigit(c)) {
1466	ret = 0;
1467	while (idigit(c)) {
1468	    ret = ret * 10 + c - '0';
1469	    c = ingetc();
1470	}
1471	inungetc(c);
1472    } else if (c == '^')
1473	ret = 1;
1474    else if (c == '$')
1475	ret = argc;
1476    else if (c == '%') {
1477	if (evset) {
1478	    herrflush();
1479	    zerr("Ambiguous history reference");
1480	    return -2;
1481	}
1482	if (marg == -1) {
1483	    herrflush();
1484	    zerr("%% with no previous word matched");
1485	    return -2;
1486	}
1487	ret = marg;
1488    } else
1489	inungetc(c);
1490    return ret;
1491}
1492
1493/* do ?foo? search */
1494
1495/**/
1496static zlong
1497hconsearch(char *str, int *marg)
1498{
1499    int t1 = 0;
1500    char *s;
1501    Histent he;
1502
1503    for (he = up_histent(hist_ring); he; he = up_histent(he)) {
1504	if (he->node.flags & HIST_FOREIGN)
1505	    continue;
1506	if ((s = strstr(he->node.nam, str))) {
1507	    int pos = s - he->node.nam;
1508	    while (t1 < he->nwords && he->words[2*t1] <= pos)
1509		t1++;
1510	    *marg = t1 - 1;
1511	    return he->histnum;
1512	}
1513    }
1514    return -1;
1515}
1516
1517/* do !foo search */
1518
1519/**/
1520zlong
1521hcomsearch(char *str)
1522{
1523    Histent he;
1524    int len = strlen(str);
1525
1526    for (he = up_histent(hist_ring); he; he = up_histent(he)) {
1527	if (he->node.flags & HIST_FOREIGN)
1528	    continue;
1529	if (strncmp(he->node.nam, str, len) == 0)
1530	    return he->histnum;
1531    }
1532    return -1;
1533}
1534
1535/* various utilities for : modifiers */
1536
1537/**/
1538int
1539chabspath(char **junkptr)
1540{
1541    char *current, *dest;
1542
1543    if (!**junkptr)
1544	return 1;
1545
1546    if (**junkptr != '/') {
1547	*junkptr = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", *junkptr);
1548    }
1549
1550    current = *junkptr;
1551    dest = *junkptr;
1552
1553#ifdef HAVE_SUPERROOT
1554    while (*current == '/' && current[1] == '.' && current[2] == '.' &&
1555	   (!current[3] || current[3] == '/')) {
1556	*dest++ = '/';
1557	*dest++ = '.';
1558	*dest++ = '.';
1559	current += 3;
1560    }
1561#endif
1562
1563    for (;;) {
1564	if (*current == '/') {
1565#ifdef __CYGWIN__
1566	    if (current == *junkptr && current[1] == '/')
1567		*dest++ = *current++;
1568#endif
1569	    *dest++ = *current++;
1570	    while (*current == '/')
1571		current++;
1572	} else if (!*current) {
1573	    while (dest > *junkptr + 1 && dest[-1] == '/')
1574		dest--;
1575	    *dest = '\0';
1576	    break;
1577	} else if (current[0] == '.' && current[1] == '.' &&
1578		   (!current[2] || current[2] == '/')) {
1579		if (current == *junkptr || dest == *junkptr) {
1580		    *dest++ = '.';
1581		    *dest++ = '.';
1582		    current += 2;
1583		} else if (dest > *junkptr + 2 &&
1584			   !strncmp(dest - 3, "../", 3)) {
1585		    *dest++ = '.';
1586		    *dest++ = '.';
1587		    current += 2;
1588		} else if (dest > *junkptr + 1) {
1589		    *dest = '\0';
1590		    for (dest--;
1591			 dest > *junkptr + 1 && dest[-1] != '/';
1592			 dest--);
1593		    if (dest[-1] != '/')
1594			dest--;
1595		    current += 2;
1596		    if (*current == '/')
1597			current++;
1598		} else if (dest == *junkptr + 1) {
1599		    /* This might break with Cygwin's leading double slashes? */
1600		    current += 2;
1601		} else {
1602		    return 0;
1603		}
1604	} else if (current[0] == '.' && (current[1] == '/' || !current[1])) {
1605	     while (*++current == '/');
1606	} else {
1607	    while (*current != '/' && *current != '\0')
1608		if ((*dest++ = *current++) == Meta)
1609		    *dest++ = *current++;
1610	}
1611    }
1612    return 1;
1613}
1614
1615/**/
1616int
1617chrealpath(char **junkptr)
1618{
1619    char *str;
1620#ifdef HAVE_CANONICALIZE_FILE_NAME
1621    char *lastpos, *nonreal, *real;
1622#else
1623# ifdef HAVE_REALPATH
1624    char *lastpos, *nonreal, real[PATH_MAX];
1625# endif
1626#endif
1627
1628    if (!**junkptr)
1629	return 1;
1630
1631    /* Notice that this means ..'s are applied before symlinks are resolved! */
1632    if (!chabspath(junkptr))
1633	return 0;
1634
1635#if !defined(HAVE_REALPATH) && !defined(HAVE_CANONICALIZE_FILE_NAME)
1636    return 1;
1637#else
1638    /*
1639     * Notice that this means you cannot pass relative paths into this
1640     * function!
1641     */
1642    if (**junkptr != '/')
1643	return 0;
1644
1645    unmetafy(*junkptr, NULL);
1646
1647    lastpos = strend(*junkptr);
1648    nonreal = lastpos + 1;
1649
1650    while (!
1651#ifdef HAVE_CANONICALIZE_FILE_NAME
1652	   /*
1653	    * This is a GNU extension to realpath(); it's the
1654	    * same as calling realpath() with a NULL second argument
1655	    * which uses malloc() to get memory.  The alternative
1656	    * interface is easier to test for, however.
1657	    */
1658	   (real = canonicalize_file_name(*junkptr))
1659#else
1660	   realpath(*junkptr, real)
1661#endif
1662	) {
1663	if (errno == EINVAL || errno == ELOOP ||
1664	    errno == ENAMETOOLONG || errno == ENOMEM)
1665	    return 0;
1666
1667#ifdef HAVE_CANONICALIZE_FILE_NAME
1668	if (!real)
1669	    return 0;
1670#endif
1671
1672	if (nonreal == *junkptr) {
1673	    *real = '\0';
1674	    break;
1675	}
1676
1677	while (*nonreal != '/' && nonreal >= *junkptr)
1678	    nonreal--;
1679	*nonreal = '\0';
1680    }
1681
1682    str = nonreal;
1683    while (str <= lastpos) {
1684	if (*str == '\0')
1685	    *str = '/';
1686	str++;
1687    }
1688
1689    *junkptr = metafy(bicat(real, nonreal), -1, META_HEAPDUP);
1690#ifdef HAVE_CANONICALIZE_FILE_NAME
1691    free(real);
1692#endif
1693#endif
1694
1695    return 1;
1696}
1697
1698/**/
1699int
1700remtpath(char **junkptr)
1701{
1702    char *str = strend(*junkptr);
1703
1704    /* ignore trailing slashes */
1705    while (str >= *junkptr && IS_DIRSEP(*str))
1706	--str;
1707    /* skip filename */
1708    while (str >= *junkptr && !IS_DIRSEP(*str))
1709	--str;
1710    if (str < *junkptr) {
1711	if (IS_DIRSEP(**junkptr))
1712	    *junkptr = dupstring ("/");
1713	else
1714	    *junkptr = dupstring (".");
1715
1716	return 0;
1717    }
1718    /* repeated slashes are considered like a single slash */
1719    while (str > *junkptr && IS_DIRSEP(str[-1]))
1720	--str;
1721    /* never erase the root slash */
1722    if (str == *junkptr) {
1723	++str;
1724	/* Leading doubled slashes (`//') have a special meaning on cygwin
1725	   and some old flavor of UNIX, so we do not assimilate them to
1726	   a single slash.  However a greater number is ok to squeeze. */
1727	if (IS_DIRSEP(*str) && !IS_DIRSEP(str[1]))
1728	    ++str;
1729    }
1730    *str = '\0';
1731    return 1;
1732}
1733
1734/**/
1735int
1736remtext(char **junkptr)
1737{
1738    char *str;
1739
1740    for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str)
1741	if (*str == '.') {
1742	    *str = '\0';
1743	    return 1;
1744	}
1745    return 0;
1746}
1747
1748/**/
1749int
1750rembutext(char **junkptr)
1751{
1752    char *str;
1753
1754    for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str)
1755	if (*str == '.') {
1756	    *junkptr = dupstring(str + 1); /* .xx or xx? */
1757	    return 1;
1758	}
1759    /* no extension */
1760    *junkptr = dupstring ("");
1761    return 0;
1762}
1763
1764/**/
1765mod_export int
1766remlpaths(char **junkptr)
1767{
1768    char *str = strend(*junkptr);
1769
1770    if (IS_DIRSEP(*str)) {
1771	/* remove trailing slashes */
1772	while (str >= *junkptr && IS_DIRSEP(*str))
1773	    --str;
1774	str[1] = '\0';
1775    }
1776    for (; str >= *junkptr; --str)
1777	if (IS_DIRSEP(*str)) {
1778	    *str = '\0';
1779	    *junkptr = dupstring(str + 1);
1780	    return 1;
1781	}
1782    return 0;
1783}
1784
1785/*
1786 * Return modified version of str from the heap with modification
1787 * according to one of the CASMOD_* types defined in zsh.h; CASMOD_NONE
1788 * is not handled, for obvious reasons.
1789 */
1790
1791/**/
1792char *
1793casemodify(char *str, int how)
1794{
1795    char *str2 = zhalloc(2 * strlen(str) + 1);
1796    char *ptr2 = str2;
1797    int nextupper = 1;
1798
1799#ifdef MULTIBYTE_SUPPORT
1800    if (isset(MULTIBYTE)) {
1801	VARARR(char, mbstr, MB_CUR_MAX);
1802	mbstate_t ps;
1803
1804	mb_metacharinit();
1805	memset(&ps, 0, sizeof(ps));
1806	while (*str) {
1807	    wint_t wc;
1808	    int len = mb_metacharlenconv(str, &wc), mod = 0, len2;
1809	    /*
1810	     * wc is set to WEOF if the start of str couldn't be
1811	     * converted.  Presumably WEOF doesn't match iswlower(), but
1812	     * better be safe.
1813	     */
1814	    if (wc == WEOF) {
1815		while (len--)
1816		    *ptr2++ = *str++;
1817		/* not alphanumeric */
1818		nextupper = 1;
1819		continue;
1820	    }
1821	    switch (how) {
1822	    case CASMOD_LOWER:
1823		if (iswupper(wc)) {
1824		    wc = towlower(wc);
1825		    mod = 1;
1826		}
1827		break;
1828
1829	    case CASMOD_UPPER:
1830		if (iswlower(wc)) {
1831		    wc = towupper(wc);
1832		    mod = 1;
1833		}
1834		break;
1835
1836	    case CASMOD_CAPS:
1837	    default:		/* shuts up compiler */
1838		if (IS_COMBINING(wc))
1839			break;
1840		if (!iswalnum(wc))
1841		    nextupper = 1;
1842		else if (nextupper) {
1843		    if (iswlower(wc)) {
1844			wc = towupper(wc);
1845			mod = 1;
1846		    }
1847		    nextupper = 0;
1848		} else if (iswupper(wc)) {
1849		    wc = towlower(wc);
1850		    mod = 1;
1851		}
1852		break;
1853	    }
1854	    if (mod && (len2 = wcrtomb(mbstr, wc, &ps)) > 0) {
1855		char *mbptr;
1856
1857		for (mbptr = mbstr; mbptr < mbstr + len2; mbptr++) {
1858		    if (imeta(STOUC(*mbptr))) {
1859			*ptr2++ = Meta;
1860			*ptr2++ = *mbptr ^ 32;
1861		    } else
1862			*ptr2++ = *mbptr;
1863		}
1864		str += len;
1865	    } else {
1866		while (len--)
1867		    *ptr2++ = *str++;
1868	    }
1869	}
1870    }
1871    else
1872#endif
1873	while (*str) {
1874	    int c;
1875	    if (*str == Meta) {
1876		c = str[1] ^ 32;
1877		str += 2;
1878	    } else
1879		c = *str++;
1880	    switch (how) {
1881	    case CASMOD_LOWER:
1882		if (isupper(c))
1883		    c = tolower(c);
1884		break;
1885
1886	    case CASMOD_UPPER:
1887		if (islower(c))
1888		    c = toupper(c);
1889		break;
1890
1891	    case CASMOD_CAPS:
1892	    default:		/* shuts up compiler */
1893		if (!ialnum(c))
1894		    nextupper = 1;
1895		else if (nextupper) {
1896		    if (islower(c))
1897			c = toupper(c);
1898		    nextupper = 0;
1899		} else if (isupper(c))
1900		    c = tolower(c);
1901		break;
1902	    }
1903	    if (imeta(c)) {
1904		*ptr2++ = Meta;
1905		*ptr2++ = c ^ 32;
1906	    } else
1907		*ptr2++ = c;
1908	}
1909    *ptr2 = '\0';
1910    return str2;
1911}
1912
1913
1914/*
1915 * Substitute "in" for "out" in "*strptr" and update "*strptr".
1916 * If "gbal", do global substitution.
1917 *
1918 * This returns a result from the heap.  There seems to have
1919 * been some confusion on this point.
1920 */
1921
1922/**/
1923int
1924subst(char **strptr, char *in, char *out, int gbal)
1925{
1926    char *str = *strptr, *substcut, *sptr;
1927    int off, inlen, outlen;
1928
1929    if (!*in)
1930	in = str, gbal = 0;
1931
1932    if (isset(HISTSUBSTPATTERN)) {
1933	int fl = SUB_LONG|SUB_REST|SUB_RETFAIL;
1934	char *oldin = in;
1935	if (gbal)
1936	    fl |= SUB_GLOBAL;
1937	if (*in == '#' || *in == Pound) {
1938	    /* anchor at head, flag needed if SUB_END is also set */
1939	    fl |= SUB_START;
1940	    in++;
1941	}
1942	if (*in == '%') {
1943	    /* anchor at tail */
1944	    in++;
1945	    fl |= SUB_END;
1946	}
1947	if (in == oldin) {
1948	    /* no anchor, substring match */
1949	    fl |= SUB_SUBSTR;
1950	}
1951	if (in == str)
1952	    in = dupstring(in);
1953	if (parse_subst_string(in) || errflag)
1954	    return 1;
1955	if (parse_subst_string(out) || errflag)
1956	    return 1;
1957	singsub(&in);
1958	if (getmatch(strptr, in, fl, 1, out))
1959	    return 0;
1960    } else {
1961	if ((substcut = (char *)strstr(str, in))) {
1962	    inlen = strlen(in);
1963	    sptr = convamps(out, in, inlen);
1964	    outlen = strlen(sptr);
1965
1966	    do {
1967		*substcut = '\0';
1968		off = substcut - *strptr + outlen;
1969		substcut += inlen;
1970		*strptr = zhtricat(*strptr, sptr, substcut);
1971		str = (char *)*strptr + off;
1972	    } while (gbal && (substcut = (char *)strstr(str, in)));
1973
1974	    return 0;
1975	}
1976    }
1977
1978    return 1;
1979}
1980
1981/**/
1982static char *
1983convamps(char *out, char *in, int inlen)
1984{
1985    char *ptr, *ret, *pp;
1986    int slen, sdup = 0;
1987
1988    for (ptr = out, slen = 0; *ptr; ptr++, slen++)
1989	if (*ptr == '\\')
1990	    ptr++, sdup = 1;
1991	else if (*ptr == '&')
1992	    slen += inlen - 1, sdup = 1;
1993    if (!sdup)
1994	return out;
1995    ret = pp = (char *) zhalloc(slen + 1);
1996    for (ptr = out; *ptr; ptr++)
1997	if (*ptr == '\\')
1998	    *pp++ = *++ptr;
1999	else if (*ptr == '&') {
2000	    strcpy(pp, in);
2001	    pp += inlen;
2002	} else
2003	    *pp++ = *ptr;
2004    *pp = '\0';
2005    return ret;
2006}
2007
2008/**/
2009mod_export void
2010checkcurline(Histent he)
2011{
2012    if (he->histnum == curhist && (histactive & HA_ACTIVE)) {
2013	curline.node.nam = chline;
2014	curline.nwords = chwordpos/2;
2015	curline.words = chwords;
2016    }
2017}
2018
2019/**/
2020mod_export Histent
2021quietgethist(int ev)
2022{
2023    return gethistent(ev, GETHIST_EXACT);
2024}
2025
2026/**/
2027static Histent
2028gethist(int ev)
2029{
2030    Histent ret;
2031
2032    ret = quietgethist(ev);
2033    if (!ret) {
2034	herrflush();
2035	zerr("no such event: %d", ev);
2036    }
2037    return ret;
2038}
2039
2040/**/
2041static char *
2042getargs(Histent elist, int arg1, int arg2)
2043{
2044    short *words = elist->words;
2045    int pos1, nwords = elist->nwords;
2046
2047    if (arg2 < arg1 || arg1 >= nwords || arg2 >= nwords) {
2048	/* remember, argN is indexed from 0, nwords is total no. of words */
2049	herrflush();
2050	zerr("no such word in event");
2051	return NULL;
2052    }
2053
2054    pos1 = words[2*arg1];
2055    return dupstrpfx(elist->node.nam + pos1, words[2*arg2+1] - pos1);
2056}
2057
2058/**/
2059int
2060quote(char **tr)
2061{
2062    char *ptr, *rptr, **str = (char **)tr;
2063    int len = 3;
2064    int inquotes = 0;
2065
2066    for (ptr = *str; *ptr; ptr++, len++)
2067	if (*ptr == '\'') {
2068	    len += 3;
2069	    if (!inquotes)
2070		inquotes = 1;
2071	    else
2072		inquotes = 0;
2073	} else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\')
2074	    len += 2;
2075    ptr = *str;
2076    *str = rptr = (char *) zhalloc(len);
2077    *rptr++ = '\'';
2078    for (; *ptr; ptr++)
2079	if (*ptr == '\'') {
2080	    if (!inquotes)
2081		inquotes = 1;
2082	    else
2083		inquotes = 0;
2084	    *rptr++ = '\'';
2085	    *rptr++ = '\\';
2086	    *rptr++ = '\'';
2087	    *rptr++ = '\'';
2088	} else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\') {
2089	    *rptr++ = '\'';
2090	    *rptr++ = *ptr;
2091	    *rptr++ = '\'';
2092	} else
2093	    *rptr++ = *ptr;
2094    *rptr++ = '\'';
2095    *rptr++ = 0;
2096    str[1] = NULL;
2097    return 0;
2098}
2099
2100/**/
2101static int
2102quotebreak(char **tr)
2103{
2104    char *ptr, *rptr, **str = (char **)tr;
2105    int len = 3;
2106
2107    for (ptr = *str; *ptr; ptr++, len++)
2108	if (*ptr == '\'')
2109	    len += 3;
2110	else if (inblank(*ptr))
2111	    len += 2;
2112    ptr = *str;
2113    *str = rptr = (char *) zhalloc(len);
2114    *rptr++ = '\'';
2115    for (; *ptr;)
2116	if (*ptr == '\'') {
2117	    *rptr++ = '\'';
2118	    *rptr++ = '\\';
2119	    *rptr++ = '\'';
2120	    *rptr++ = '\'';
2121	    ptr++;
2122	} else if (inblank(*ptr)) {
2123	    *rptr++ = '\'';
2124	    *rptr++ = *ptr++;
2125	    *rptr++ = '\'';
2126	} else
2127	    *rptr++ = *ptr++;
2128    *rptr++ = '\'';
2129    *rptr++ = '\0';
2130    return 0;
2131}
2132
2133/* read an arbitrary amount of data into a buffer until stop is found */
2134
2135#if 0 /**/
2136char *
2137hdynread(int stop)
2138{
2139    int bsiz = 256, ct = 0, c;
2140    char *buf = (char *)zalloc(bsiz), *ptr;
2141
2142    ptr = buf;
2143    while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
2144	if (c == '\\')
2145	    c = ingetc();
2146	*ptr++ = c;
2147	if (++ct == bsiz) {
2148	    buf = realloc(buf, bsiz *= 2);
2149	    ptr = buf + ct;
2150	}
2151    }
2152    *ptr = 0;
2153    if (c == '\n') {
2154	inungetc('\n');
2155	zerr("delimiter expected");
2156	zfree(buf, bsiz);
2157	return NULL;
2158    }
2159    return buf;
2160}
2161#endif
2162
2163/**/
2164static char *
2165hdynread2(int stop)
2166{
2167    int bsiz = 256, ct = 0, c;
2168    char *buf = (char *)zalloc(bsiz), *ptr;
2169
2170    ptr = buf;
2171    while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
2172	if (c == '\\')
2173	    c = ingetc();
2174	*ptr++ = c;
2175	if (++ct == bsiz) {
2176	    buf = realloc(buf, bsiz *= 2);
2177	    ptr = buf + ct;
2178	}
2179    }
2180    *ptr = 0;
2181    if (c == '\n')
2182	inungetc('\n');
2183    return buf;
2184}
2185
2186/**/
2187void
2188inithist(void)
2189{
2190    createhisttable();
2191}
2192
2193/**/
2194void
2195resizehistents(void)
2196{
2197    if (histlinect > histsiz) {
2198	/* The reason we don't just call freehistnode(hist_ring->down) is
2199	 * so that we can honor the HISTEXPIREDUPSFIRST setting. */
2200	putoldhistentryontop(0);
2201	freehistnode(&hist_ring->node);
2202	while (histlinect > histsiz) {
2203	    putoldhistentryontop(1);
2204	    freehistnode(&hist_ring->node);
2205	}
2206    }
2207}
2208
2209static int
2210readhistline(int start, char **bufp, int *bufsiz, FILE *in)
2211{
2212    char *buf = *bufp;
2213    if (fgets(buf + start, *bufsiz - start, in)) {
2214	int len = start + strlen(buf + start);
2215	if (len == start)
2216	    return -1;
2217	if (buf[len - 1] != '\n') {
2218	    if (!feof(in)) {
2219		if (len < (*bufsiz) - 1)
2220		    return -1;
2221		*bufp = zrealloc(buf, 2 * (*bufsiz));
2222		*bufsiz = 2 * (*bufsiz);
2223		return readhistline(len, bufp, bufsiz, in);
2224	    }
2225	}
2226	else {
2227	    buf[len - 1] = '\0';
2228	    if (len > 1 && buf[len - 2] == '\\' &&
2229		(len < 3 || buf[len - 3] != '\\')) {
2230		buf[--len - 1] = '\n';
2231		if (!feof(in))
2232		    return readhistline(len, bufp, bufsiz, in);
2233	    }
2234	}
2235	return len;
2236    }
2237    return 0;
2238}
2239
2240/**/
2241void
2242readhistfile(char *fn, int err, int readflags)
2243{
2244    char *buf, *start = NULL;
2245    FILE *in;
2246    Histent he;
2247    time_t stim, ftim, tim = time(NULL);
2248    off_t fpos;
2249    short *words;
2250    struct stat sb;
2251    int nwordpos, nwords, bufsiz;
2252    int searching, newflags, l, ret, uselex;
2253
2254    if (!fn && !(fn = getsparam("HISTFILE")))
2255	return;
2256    if (stat(unmeta(fn), &sb) < 0 ||
2257	sb.st_size == 0)
2258	return;
2259    if (readflags & HFILE_FAST) {
2260	if ((lasthist.fsiz == sb.st_size && lasthist.mtim == sb.st_mtime)
2261	    || lockhistfile(fn, 0))
2262	    return;
2263	lasthist.fsiz = sb.st_size;
2264	lasthist.mtim = sb.st_mtime;
2265    } else if ((ret = lockhistfile(fn, 1))) {
2266	if (ret == 2) {
2267	    zwarn("locking failed for %s: %e: reading anyway", fn, errno);
2268	} else {
2269	    zerr("locking failed for %s: %e", fn, errno);
2270	    return;
2271	}
2272    }
2273    if ((in = fopen(unmeta(fn), "r"))) {
2274	nwords = 64;
2275	words = (short *)zalloc(nwords*sizeof(short));
2276	bufsiz = 1024;
2277	buf = zalloc(bufsiz);
2278
2279	pushheap();
2280	if (readflags & HFILE_FAST && lasthist.text) {
2281	    if (lasthist.fpos < lasthist.fsiz) {
2282		fseek(in, lasthist.fpos, 0);
2283		searching = 1;
2284	    }
2285	    else {
2286		histfile_linect = 0;
2287		searching = -1;
2288	    }
2289	} else
2290	    searching = 0;
2291
2292	newflags = HIST_OLD | HIST_READ;
2293	if (readflags & HFILE_FAST)
2294	    newflags |= HIST_FOREIGN;
2295	if (readflags & HFILE_SKIPOLD
2296	 || (hist_ignore_all_dups && newflags & hist_skip_flags))
2297	    newflags |= HIST_MAKEUNIQUE;
2298	while (fpos = ftell(in), (l = readhistline(0, &buf, &bufsiz, in))) {
2299	    char *pt = buf;
2300
2301	    if (l < 0) {
2302		zerr("corrupt history file %s", fn);
2303		break;
2304	    }
2305	    if (*pt == ':') {
2306		pt++;
2307		stim = zstrtol(pt, NULL, 0);
2308		for (; *pt != ':' && *pt; pt++);
2309		if (*pt) {
2310		    pt++;
2311		    ftim = zstrtol(pt, NULL, 0);
2312		    for (; *pt != ';' && *pt; pt++);
2313		    if (*pt)
2314			pt++;
2315		} else
2316		    ftim = stim;
2317	    } else {
2318		if (*pt == '\\' && pt[1] == ':')
2319		    pt++;
2320		stim = ftim = 0;
2321	    }
2322
2323	    if (searching) {
2324		if (searching > 0) {
2325		    if (stim == lasthist.stim
2326		     && histstrcmp(pt, lasthist.text) == 0)
2327			searching = 0;
2328		    else {
2329			fseek(in, 0, 0);
2330			histfile_linect = 0;
2331			searching = -1;
2332		    }
2333		    continue;
2334		}
2335		else if (stim < lasthist.stim) {
2336		    histfile_linect++;
2337		    continue;
2338		}
2339		searching = 0;
2340	    }
2341
2342	    if (readflags & HFILE_USE_OPTIONS) {
2343		histfile_linect++;
2344		lasthist.fpos = fpos;
2345		lasthist.stim = stim;
2346	    }
2347
2348	    he = prepnexthistent();
2349	    he->node.nam = ztrdup(pt);
2350	    he->node.flags = newflags;
2351	    if ((he->stim = stim) == 0)
2352		he->stim = he->ftim = tim;
2353	    else if (ftim < stim)
2354		he->ftim = stim + ftim;
2355	    else
2356		he->ftim = ftim;
2357
2358	    /*
2359	     * Divide up the words.
2360	     */
2361	    start = pt;
2362	    uselex = isset(HISTLEXWORDS) && !(readflags & HFILE_FAST);
2363	    histsplitwords(pt, &words, &nwords, &nwordpos, uselex);
2364	    if (uselex)
2365		freeheap();
2366
2367	    he->nwords = nwordpos/2;
2368	    if (he->nwords) {
2369		he->words = (short *)zalloc(nwordpos*sizeof(short));
2370		memcpy(he->words, words, nwordpos*sizeof(short));
2371	    } else
2372		he->words = (short *)NULL;
2373	    addhistnode(histtab, he->node.nam, he);
2374	    if (he->node.flags & HIST_DUP) {
2375		freehistnode(&he->node);
2376		curhist--;
2377	    }
2378	}
2379	if (start && readflags & HFILE_USE_OPTIONS) {
2380	    zsfree(lasthist.text);
2381	    lasthist.text = ztrdup(start);
2382	}
2383	zfree(words, nwords*sizeof(short));
2384	zfree(buf, bufsiz);
2385
2386	popheap();
2387	fclose(in);
2388    } else if (err)
2389	zerr("can't read history file %s", fn);
2390
2391    unlockhistfile(fn);
2392}
2393
2394#ifdef HAVE_FCNTL_H
2395static int flock_fd = -1;
2396
2397/*
2398 * Lock file using fcntl().  Return 0 on success, 1 on failure of
2399 * locking mechanism, 2 on permanent failure (e.g. permission).
2400 */
2401
2402static int
2403flockhistfile(char *fn, int keep_trying)
2404{
2405    struct flock lck;
2406    int ctr = keep_trying ? 9 : 0;
2407
2408    if ((flock_fd = open(unmeta(fn), O_RDWR | O_NOCTTY)) < 0)
2409	return errno == ENOENT ? 0 : 2; /* "successfully" locked missing file */
2410
2411    lck.l_type = F_WRLCK;
2412    lck.l_whence = SEEK_SET;
2413    lck.l_start = 0;
2414    lck.l_len = 0;  /* lock the whole file */
2415
2416    while (fcntl(flock_fd, F_SETLKW, &lck) == -1) {
2417	if (--ctr < 0) {
2418	    close(flock_fd);
2419	    flock_fd = -1;
2420	    return 1;
2421	}
2422	sleep(1);
2423    }
2424
2425    return 0;
2426}
2427#endif
2428
2429/**/
2430void
2431savehistfile(char *fn, int err, int writeflags)
2432{
2433    char *t, *tmpfile, *start = NULL;
2434    FILE *out;
2435    Histent he;
2436    zlong xcurhist = curhist - !!(histactive & HA_ACTIVE);
2437    int extended_history = isset(EXTENDEDHISTORY);
2438    int ret;
2439
2440    if (!interact || savehistsiz <= 0 || !hist_ring
2441     || (!fn && !(fn = getsparam("HISTFILE"))))
2442	return;
2443    if (writeflags & HFILE_FAST) {
2444	he = gethistent(lasthist.next_write_ev, GETHIST_DOWNWARD);
2445	while (he && he->node.flags & HIST_OLD) {
2446	    lasthist.next_write_ev = he->histnum + 1;
2447	    he = down_histent(he);
2448	}
2449	if (!he || lockhistfile(fn, 0))
2450	    return;
2451	if (histfile_linect > savehistsiz + savehistsiz / 5)
2452	    writeflags &= ~HFILE_FAST;
2453    }
2454    else {
2455	if (lockhistfile(fn, 1)) {
2456	    zerr("locking failed for %s: %e", fn, errno);
2457	    return;
2458	}
2459	he = hist_ring->down;
2460    }
2461    if (writeflags & HFILE_USE_OPTIONS) {
2462	if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY)
2463	 || isset(SHAREHISTORY))
2464	    writeflags |= HFILE_APPEND | HFILE_SKIPOLD;
2465	else
2466	    histfile_linect = 0;
2467	if (isset(HISTSAVENODUPS))
2468	    writeflags |= HFILE_SKIPDUPS;
2469	if (isset(SHAREHISTORY))
2470	    extended_history = 1;
2471    }
2472    errno = 0;
2473    if (writeflags & HFILE_APPEND) {
2474	int fd = open(unmeta(fn), O_CREAT | O_WRONLY | O_APPEND | O_NOCTTY, 0600);
2475	tmpfile = NULL;
2476	out = fd >= 0 ? fdopen(fd, "a") : NULL;
2477    } else if (!isset(HISTSAVEBYCOPY)) {
2478	int fd = open(unmeta(fn), O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600);
2479	tmpfile = NULL;
2480	out = fd >= 0 ? fdopen(fd, "w") : NULL;
2481    } else {
2482	tmpfile = bicat(unmeta(fn), ".new");
2483	if (unlink(tmpfile) < 0 && errno != ENOENT)
2484	    out = NULL;
2485	else {
2486	    struct stat sb;
2487	    int old_exists = stat(unmeta(fn), &sb) == 0;
2488	    uid_t euid = geteuid();
2489
2490	    if (old_exists
2491#if defined HAVE_FCHMOD && defined HAVE_FCHOWN
2492	     && euid
2493#endif
2494	     && sb.st_uid != euid) {
2495		free(tmpfile);
2496		tmpfile = NULL;
2497		if (err) {
2498		    if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY)
2499		     || isset(SHAREHISTORY))
2500			zerr("rewriting %s would change its ownership -- skipped", fn);
2501		    else
2502			zerr("rewriting %s would change its ownership -- history not saved", fn);
2503		    err = 0; /* Don't report a generic error below. */
2504		}
2505		out = NULL;
2506	    } else {
2507		int fd = open(tmpfile, O_CREAT | O_WRONLY | O_EXCL, 0600);
2508		out = fd >= 0 ? fdopen(fd, "w") : NULL;
2509	    }
2510
2511#ifdef HAVE_FCHMOD
2512	    if (old_exists && out) {
2513#ifdef HAVE_FCHOWN
2514		if (fchown(fileno(out), sb.st_uid, sb.st_gid) < 0) {} /* IGNORE FAILURE */
2515#endif
2516		if (fchmod(fileno(out), sb.st_mode) < 0) {} /* IGNORE FAILURE */
2517	    }
2518#endif
2519	}
2520    }
2521    if (out) {
2522	ret = 0;
2523	for (; he && he->histnum <= xcurhist; he = down_histent(he)) {
2524	    if ((writeflags & HFILE_SKIPDUPS && he->node.flags & HIST_DUP)
2525	     || (writeflags & HFILE_SKIPFOREIGN && he->node.flags & HIST_FOREIGN)
2526	     || he->node.flags & HIST_TMPSTORE)
2527		continue;
2528	    if (writeflags & HFILE_SKIPOLD) {
2529		if (he->node.flags & HIST_OLD)
2530		    continue;
2531		he->node.flags |= HIST_OLD;
2532		if (writeflags & HFILE_USE_OPTIONS)
2533		    lasthist.next_write_ev = he->histnum + 1;
2534	    }
2535	    if (writeflags & HFILE_USE_OPTIONS) {
2536		lasthist.fpos = ftell(out);
2537		lasthist.stim = he->stim;
2538		histfile_linect++;
2539	    }
2540	    t = start = he->node.nam;
2541	    if (extended_history) {
2542		ret = fprintf(out, ": %ld:%ld;", (long)he->stim,
2543			      he->ftim? (long)(he->ftim - he->stim) : 0L);
2544	    } else if (*t == ':')
2545		ret = fputc('\\', out);
2546
2547	    for (; ret >= 0 && *t; t++) {
2548		if (*t == '\n')
2549		    if ((ret = fputc('\\', out)) < 0)
2550			break;
2551		if ((ret = fputc(*t, out)) < 0)
2552		    break;
2553	    }
2554	    if (ret < 0 || (ret = fputc('\n', out)) < 0)
2555		break;
2556	}
2557	if (ret >= 0 && start && writeflags & HFILE_USE_OPTIONS) {
2558	    struct stat sb;
2559	    if ((ret = fflush(out)) >= 0) {
2560		if (fstat(fileno(out), &sb) == 0) {
2561		    lasthist.fsiz = sb.st_size;
2562		    lasthist.mtim = sb.st_mtime;
2563		}
2564		zsfree(lasthist.text);
2565		lasthist.text = ztrdup(start);
2566	    }
2567	}
2568	if (fclose(out) < 0 && ret >= 0)
2569	    ret = -1;
2570	if (ret >= 0) {
2571	    if (tmpfile) {
2572		if (rename(tmpfile, unmeta(fn)) < 0) {
2573		    zerr("can't rename %s.new to $HISTFILE", fn);
2574		    ret = -1;
2575		    err = 0;
2576#ifdef HAVE_FCNTL_H
2577		} else {
2578		    /* We renamed over the locked HISTFILE, so close fd.
2579		     * If we do more writing, we'll get a lock then. */
2580		    if (flock_fd >= 0) {
2581			close(flock_fd);
2582			flock_fd = -1;
2583		    }
2584#endif
2585		}
2586	    }
2587
2588	    if (ret >= 0 && writeflags & HFILE_SKIPOLD
2589		&& !(writeflags & (HFILE_FAST | HFILE_NO_REWRITE))) {
2590		int remember_histactive = histactive;
2591
2592		/* Zeroing histactive avoids unnecessary munging of curline. */
2593		histactive = 0;
2594		/* The NULL leaves HISTFILE alone, preserving fn's value. */
2595		pushhiststack(NULL, savehistsiz, savehistsiz, -1);
2596
2597		hist_ignore_all_dups |= isset(HISTSAVENODUPS);
2598		readhistfile(fn, err, 0);
2599		hist_ignore_all_dups = isset(HISTIGNOREALLDUPS);
2600		if (histlinect)
2601		    savehistfile(fn, err, 0);
2602
2603		pophiststack();
2604		histactive = remember_histactive;
2605	    }
2606	}
2607    } else
2608	ret = -1;
2609
2610    if (ret < 0 && err) {
2611	if (tmpfile)
2612	    zerr("failed to write history file %s.new: %e", fn, errno);
2613	else
2614	    zerr("failed to write history file %s: %e", fn, errno);
2615    }
2616    if (tmpfile)
2617	free(tmpfile);
2618
2619    unlockhistfile(fn);
2620}
2621
2622static int lockhistct;
2623
2624/*
2625 * Lock history file.  Return 0 on success, 1 on failure to lock this
2626 * time, 2 on permanent failure (e.g. permission).
2627 */
2628
2629/**/
2630int
2631lockhistfile(char *fn, int keep_trying)
2632{
2633    int ct = lockhistct;
2634    int ret = 0;
2635
2636    if (!fn && !(fn = getsparam("HISTFILE")))
2637	return 1;
2638
2639#ifdef HAVE_FCNTL_H
2640    if (isset(HISTFCNTLLOCK) && flock_fd < 0) {
2641	ret = flockhistfile(fn, keep_trying);
2642	if (ret)
2643	    return ret;
2644    }
2645#endif
2646
2647    if (!lockhistct++) {
2648	struct stat sb;
2649	int fd;
2650	char *lockfile;
2651#ifdef HAVE_LINK
2652# ifdef HAVE_SYMLINK
2653	char pidbuf[32], *lnk;
2654# else
2655	char *tmpfile;
2656# endif
2657#endif
2658
2659	lockfile = bicat(unmeta(fn), ".LOCK");
2660	/* NOTE: only use symlink locking on a link()-having host in order to
2661	 * avoid a change from open()-based locking to symlink()-based. */
2662#ifdef HAVE_LINK
2663# ifdef HAVE_SYMLINK
2664	sprintf(pidbuf, "/pid-%ld/host-", (long)mypid);
2665	lnk = bicat(pidbuf, getsparam("HOST"));
2666	/* We'll abuse fd as our success flag. */
2667	while ((fd = symlink(lnk, lockfile)) < 0) {
2668	    if (errno != EEXIST) {
2669		ret = 2;
2670		break;
2671	    } else if (!keep_trying) {
2672		ret = 1;
2673		break;
2674	    }
2675	    if (lstat(lockfile, &sb) < 0) {
2676		if (errno == ENOENT)
2677		    continue;
2678		break;
2679	    }
2680	    if (time(NULL) - sb.st_mtime < 10)
2681		sleep(1);
2682	    else
2683		unlink(lockfile);
2684	}
2685	if (fd < 0)
2686	    lockhistct--;
2687	free(lnk);
2688# else /* not HAVE_SYMLINK */
2689	if ((fd = gettempfile(fn, 0, &tmpfile)) >= 0) {
2690	    FILE *out = fdopen(fd, "w");
2691	    if (out) {
2692		fprintf(out, "%ld %s\n", (long)getpid(), getsparam("HOST"));
2693		fclose(out);
2694	    } else
2695		close(fd);
2696	    while (link(tmpfile, lockfile) < 0) {
2697		if (errno != EEXIST) {
2698		    ret = 2;
2699		    break;
2700		} else if (!keep_trying) {
2701		    ret = 1;
2702		    break;
2703		} else if (lstat(lockfile, &sb) < 0) {
2704		    if (errno == ENOENT)
2705			continue;
2706		    ret = 2;
2707		} else {
2708		    if (time(NULL) - sb.st_mtime < 10)
2709			sleep(1);
2710		    else
2711			unlink(lockfile);
2712		    continue;
2713		}
2714		lockhistct--;
2715		break;
2716	    }
2717	    unlink(tmpfile);
2718	    free(tmpfile);
2719	}
2720# endif /* not HAVE_SYMLINK */
2721#else /* not HAVE_LINK */
2722	while ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) {
2723	    if (errno != EEXIST) {
2724		ret = 2;
2725		break;
2726	    } else if (!keep_trying) {
2727		ret = 1;
2728		break;
2729	    }
2730	    if (lstat(lockfile, &sb) < 0) {
2731		if (errno == ENOENT)
2732		    continue;
2733		ret = 2;
2734		break;
2735	    }
2736	    if (time(NULL) - sb.st_mtime < 10)
2737		sleep(1);
2738	    else
2739		unlink(lockfile);
2740	}
2741	if (fd < 0)
2742	    lockhistct--;
2743	else {
2744	    FILE *out = fdopen(fd, "w");
2745	    if (out) {
2746		fprintf(out, "%ld %s\n", (long)mypid, getsparam("HOST"));
2747		fclose(out);
2748	    } else
2749		close(fd);
2750	}
2751#endif /* not HAVE_LINK */
2752	free(lockfile);
2753    }
2754
2755    if (ct == lockhistct) {
2756#ifdef HAVE_FCNTL_H
2757	if (flock_fd >= 0) {
2758	    close(flock_fd);
2759	    flock_fd = -1;
2760	}
2761#endif
2762	DPUTS(ret == 0, "BUG: return value non-zero on locking error");
2763	return ret;
2764    }
2765    return 0;
2766}
2767
2768/* Unlock the history file if this corresponds to the last nested lock
2769 * request.  If we don't have the file locked, just return.
2770 */
2771
2772/**/
2773void
2774unlockhistfile(char *fn)
2775{
2776    if (!fn && !(fn = getsparam("HISTFILE")))
2777	return;
2778    if (--lockhistct) {
2779	if (lockhistct < 0)
2780	    lockhistct = 0;
2781    }
2782    else {
2783	char *lockfile;
2784	fn = unmeta(fn);
2785	lockfile = zalloc(strlen(fn) + 5 + 1);
2786	sprintf(lockfile, "%s.LOCK", fn);
2787	unlink(lockfile);
2788	free(lockfile);
2789#ifdef HAVE_FCNTL_H
2790	if (flock_fd >= 0) {
2791	    close(flock_fd);
2792	    flock_fd = -1;
2793	}
2794#endif
2795    }
2796}
2797
2798/**/
2799int
2800histfileIsLocked(void)
2801{
2802    return lockhistct > 0;
2803}
2804
2805/*
2806 * Get the words in the current buffer. Using the lexer.
2807 *
2808 * As far as I can make out, this is a gross hack based on a gross hack.
2809 * When analysing lines from within zle, we tweak the metafied line
2810 * positions (zlemetall and zlemetacs) directly in the lexer.  That's
2811 * bad enough, but this function appears to be designed to be called
2812 * from outside zle, pretending to be in zle and calling out, so
2813 * we set zlemetall and zlemetacs locally and copy the current zle line,
2814 * which may not even be valid at this point.
2815 *
2816 * However, I'm so confused it could simply be baking Bakewell tarts.
2817 *
2818 * list may be an existing linked list (off the heap), in which case
2819 * it will be appended to; otherwise it will be created.
2820 *
2821 * If buf is set we will take input from that string, else we will
2822 * attempt to use ZLE directly in a way they tell you not to do on all
2823 * programming courses.
2824 *
2825 * If index is non-NULL, and input is from a string in ZLE, *index
2826 * is set to the position of the end of the current editor word.
2827 *
2828 * flags is passed directly to lexflags, see lex.c, except that
2829 * we 'or' in the bit LEXFLAGS_ACTIVE to make sure the variable
2830 * is set.
2831 */
2832
2833/**/
2834mod_export LinkList
2835bufferwords(LinkList list, char *buf, int *index, int flags)
2836{
2837    int num = 0, cur = -1, got = 0, ne = noerrs;
2838    int owb = wb, owe = we, oadx = addedx, onc = nocomments;
2839    int ona = noaliases, ocs = zlemetacs, oll = zlemetall;
2840    int forloop = 0, rcquotes = opts[RCQUOTES];
2841    char *p, *addedspaceptr;
2842
2843    if (!list)
2844	list = newlinklist();
2845
2846    /*
2847     * With RC_QUOTES, 'foo '' bar' comes back as 'foo ' bar'.  That's
2848     * not very useful.  As nothing in here requires the fully processed
2849     * string expression, we just turn the option off for this function.
2850     */
2851    opts[RCQUOTES] = 0;
2852    addedx = 0;
2853    noerrs = 1;
2854    lexsave();
2855    lexflags = flags | LEXFLAGS_ACTIVE;
2856    /*
2857     * Are we handling comments?
2858     */
2859    nocomments = !(flags & (LEXFLAGS_COMMENTS_KEEP|
2860			    LEXFLAGS_COMMENTS_STRIP));
2861    if (buf) {
2862	int l = strlen(buf);
2863
2864	p = (char *) zhalloc(l + 2);
2865	memcpy(p, buf, l);
2866	/*
2867	 * I'm sure this space is here for a reason, but it's
2868	 * a pain in the neck:  when we get back a string that's
2869	 * not finished it's very hard to tell if a space at the
2870	 * end is this one or not.  We use two tricks below to
2871	 * work around this.
2872	 */
2873	addedspaceptr = p + l;
2874	*addedspaceptr = ' ';
2875	addedspaceptr[1] = '\0';
2876	inpush(p, 0, NULL);
2877	zlemetall = strlen(p) ;
2878	zlemetacs = zlemetall + 1;
2879    } else {
2880	int ll, cs;
2881	char *linein;
2882
2883	linein = zleentry(ZLE_CMD_GET_LINE, &ll, &cs);
2884	zlemetall = ll + 1; /* length of line plus space added below */
2885	zlemetacs = cs;
2886
2887	if (!isfirstln && chline) {
2888	    p = (char *) zhalloc(hptr - chline + ll + 2);
2889	    memcpy(p, chline, hptr - chline);
2890	    memcpy(p + (hptr - chline), linein, ll);
2891	    addedspaceptr = p + (hptr - chline) + ll;
2892	    *addedspaceptr = ' ';
2893	    addedspaceptr[1] = '\0';
2894	    inpush(p, 0, NULL);
2895
2896	    /*
2897	     * advance line length and character position over
2898	     * prepended string.
2899	     */
2900	    zlemetall += hptr - chline;
2901	    zlemetacs += hptr - chline;
2902	} else {
2903	    p = (char *) zhalloc(ll + 2);
2904	    memcpy(p, linein, ll);
2905	    addedspaceptr = p + ll;
2906	    *addedspaceptr = ' ';
2907	    p[zlemetall] = '\0';
2908	    inpush(p, 0, NULL);
2909	}
2910	zsfree(linein);
2911    }
2912    if (zlemetacs)
2913	zlemetacs--;
2914    strinbeg(0);
2915    noaliases = 1;
2916    do {
2917	if (incond)
2918	    incond = 1 + (tok != DINBRACK && tok != INPAR &&
2919			  tok != DBAR && tok != DAMPER &&
2920			  tok != BANG);
2921	ctxtlex();
2922	if (tok == ENDINPUT || tok == LEXERR)
2923	    break;
2924	if (tok == FOR) {
2925	    /*
2926	     * The way for (( expr1 ; expr2; expr3 )) is parsed is:
2927	     * - a FOR tok
2928	     * - a DINPAR with no tokstr
2929	     * - two DINPARS with tokstr's expr1, expr2.
2930	     * - a DOUTPAR with tokstr expr3.
2931	     *
2932	     * We'll decrement the variable forloop as we verify
2933	     * the various stages.
2934	     *
2935	     * Don't ask me, ma'am, I'm just the programmer.
2936	     */
2937	    forloop = 5;
2938	} else {
2939	    switch (forloop) {
2940	    case 1:
2941		if (tok != DOUTPAR)
2942		    forloop = 0;
2943		break;
2944
2945	    case 2:
2946	    case 3:
2947	    case 4:
2948		if (tok != DINPAR)
2949		    forloop = 0;
2950		break;
2951
2952	    default:
2953		/* nothing to do */
2954		break;
2955	    }
2956	}
2957	if (tokstr) {
2958	    switch (tok) {
2959	    case ENVARRAY:
2960		p = dyncat(tokstr, "=(");
2961		break;
2962
2963	    case DINPAR:
2964		if (forloop) {
2965		    /* See above. */
2966		    p = dyncat(tokstr, ";");
2967		} else {
2968		    /*
2969		     * Mathematical expressions analysed as a single
2970		     * word.  That's correct because it behaves like
2971		     * double quotes.  Whitespace in the middle is
2972		     * similarly retained, so just add the parentheses back.
2973		     */
2974		    p = tricat("((", tokstr, "))");
2975		}
2976		break;
2977
2978	    default:
2979		p = dupstring(tokstr);
2980		break;
2981	    }
2982	    if (*p) {
2983		untokenize(p);
2984		if (ingetptr() == addedspaceptr + 1) {
2985		    /*
2986		     * Whoops, we've read past the space we added, probably
2987		     * because we were expecting a terminator but when
2988		     * it didn't turn up we shrugged our shoulders thinking
2989		     * it might as well be a complete string anyway.
2990		     * So remove the space.  C.f. below for the case
2991		     * where the missing terminator caused a lex error.
2992		     * We use the same paranoid test.
2993		     */
2994		    int plen = strlen(p);
2995		    if (plen && p[plen-1] == ' ' &&
2996			(plen == 1 || p[plen-2] != Meta))
2997			p[plen-1] = '\0';
2998		}
2999		addlinknode(list, p);
3000		num++;
3001	    }
3002	} else if (buf) {
3003	    if (IS_REDIROP(tok) && tokfd >= 0) {
3004		char b[20];
3005
3006		sprintf(b, "%d%s", tokfd, tokstrings[tok]);
3007		addlinknode(list, dupstring(b));
3008		num++;
3009	    } else if (tok != NEWLIN) {
3010		addlinknode(list, dupstring(tokstrings[tok]));
3011		num++;
3012	    }
3013	}
3014	if (forloop) {
3015	    if (forloop == 1) {
3016		/*
3017		 * Final "))" of for loop to match opening,
3018		 * since we've just added the preceding element.
3019 		 */
3020		addlinknode(list, dupstring("))"));
3021	    }
3022	    forloop--;
3023	}
3024	if (!got && !lexflags) {
3025	    got = 1;
3026	    cur = num - 1;
3027	}
3028    } while (tok != ENDINPUT && tok != LEXERR);
3029    if (buf && tok == LEXERR && tokstr && *tokstr) {
3030	int plen;
3031	untokenize((p = dupstring(tokstr)));
3032	plen = strlen(p);
3033	/*
3034	 * Strip the space we added for lexing but which won't have
3035	 * been swallowed by the lexer because we aborted early.
3036	 * The test is paranoia.
3037	 */
3038	if (plen && p[plen-1] == ' ' && (plen == 1 || p[plen-2] != Meta))
3039	    p[plen - 1] = '\0';
3040	addlinknode(list, p);
3041	num++;
3042    }
3043    if (cur < 0 && num)
3044	cur = num - 1;
3045    noaliases = ona;
3046    strinend();
3047    inpop();
3048    errflag = 0;
3049    nocomments = onc;
3050    noerrs = ne;
3051    lexrestore();
3052    zlemetacs = ocs;
3053    zlemetall = oll;
3054    wb = owb;
3055    we = owe;
3056    addedx = oadx;
3057    opts[RCQUOTES] = rcquotes;
3058
3059    if (index)
3060	*index = cur;
3061
3062    return list;
3063}
3064
3065/*
3066 * Split up a line into words for use in a history file.
3067 *
3068 * lineptr is the line to be split.
3069 *
3070 * *wordsp and *nwordsp are an array already allocated to hold words
3071 * and its length.  The array holds both start and end positions,
3072 * so *nwordsp actually counts twice the number of words in the
3073 * original string.  *nwordsp may be zero in which case the array
3074 * will be allocated.
3075 *
3076 * *nwordposp returns the used length of *wordsp in the same units as
3077 * *nwordsp, i.e. twice the number of words in the input line.
3078 *
3079 * If uselex is 1, attempt to do this using the lexical analyser.
3080 * This is more accurate, but slower; for reading history files it's
3081 * controlled by the option HISTLEXWORDS.  If this failed (which
3082 * indicates a bug in the shell) it falls back to whitespace-separated
3083 * strings, printing a message if in debug mode.
3084 *
3085 * If uselex is 0, just look for whitespace-separated words; the only
3086 * special handling is for a backslash-newline combination as used
3087 * by the history file format to save multiline buffers.
3088 */
3089/**/
3090mod_export void
3091histsplitwords(char *lineptr, short **wordsp, int *nwordsp, int *nwordposp,
3092	       int uselex)
3093{
3094    int nwords = *nwordsp, nwordpos = 0;
3095    short *words = *wordsp;
3096    char *start = lineptr;
3097
3098    if (uselex) {
3099	LinkList wordlist = bufferwords(NULL, lineptr, NULL,
3100					LEXFLAGS_COMMENTS_KEEP);
3101	LinkNode wordnode;
3102	int nwords_max;
3103
3104	nwords_max = 2 * countlinknodes(wordlist);
3105	if (nwords_max > nwords) {
3106	    *nwordsp = nwords = nwords_max;
3107	    *wordsp = words = (short *)zrealloc(words, nwords*sizeof(short));
3108	}
3109	for (wordnode = firstnode(wordlist);
3110	     wordnode;
3111	     incnode(wordnode)) {
3112	    char *word = getdata(wordnode);
3113	    char *lptr, *wptr = word;
3114	    int loop_next = 0, skipping;
3115
3116	    /* Skip stuff at the start of the word */
3117	    for (;;) {
3118		/*
3119		 * Not really an oddity: "\\\n" is
3120		 * removed from input as if whitespace.
3121		 */
3122		if (inblank(*lineptr))
3123		    lineptr++;
3124		else if (lineptr[0] == '\\' && lineptr[1] == '\n') {
3125		    /*
3126		     * Optimisation: we handle this in the loop below,
3127		     * too.
3128		     */
3129		    lineptr += 2;
3130		} else
3131		    break;
3132	    }
3133	    lptr = lineptr;
3134	    /*
3135	     * Skip chunks of word with possible intervening
3136	     * backslash-newline.
3137	     *
3138	     * To get round C's annoying lack of ability to
3139	     * reference the outer loop, we'll break from this
3140	     * one with
3141	     * loop_next = 0: carry on as normal
3142	     * loop_next = 1: break from outer loop
3143	     * loop_next = 2: continue round outer loop.
3144	     */
3145	    do {
3146		skipping = 0;
3147		if (strpfx(wptr, lptr)) {
3148		    /*
3149		     * Normal case: word from lexer matches start of
3150		     * string from line.  Just advance over it.
3151		     */
3152		    int len;
3153		    if (!strcmp(wptr, ";") && strpfx(";;", lptr)) {
3154			/*
3155			 * Don't get confused between a semicolon that's
3156			 * probably really a newline and a double
3157			 * semicolon that's terminating a case.
3158			 */
3159			loop_next = 2;
3160			break;
3161		    }
3162		    len = strlen(wptr);
3163		    lptr += len;
3164		    wptr += len;
3165		} else {
3166		    /*
3167		     * Didn't get to the end of the word.
3168		     * See what's amiss.
3169		     */
3170		    int bad = 0;
3171		    /*
3172		     * Oddity 1: newlines turn into semicolons.
3173		     */
3174		    if (!strcmp(wptr, ";"))
3175		    {
3176			loop_next = 2;
3177			break;
3178		    }
3179		    while (*lptr) {
3180			if (!*wptr) {
3181			    /*
3182			     * End of the word before the end of the
3183			     * line: not good.
3184			     */
3185			    bad = 1;
3186			    loop_next = 1;
3187			    break;
3188			}
3189			/*
3190			 * Oddity 2: !'s turn into |'s.
3191			 */
3192			if (*lptr == *wptr ||
3193			    (*lptr == '!' && *wptr == '|')) {
3194			    lptr++;
3195			    wptr++;
3196			} else if (lptr[0] == '\\' &&
3197				   lptr[1] == '\n') {
3198			    /*
3199			     * \\\n can occur in the middle of a word;
3200			     * wptr is already pointing at this, we
3201			     * just need to skip over the break
3202			     * in lptr and look at the next chunk.
3203			     */
3204			    lptr += 2;
3205			    skipping = 1;
3206			    break;
3207			} else {
3208			    bad = 1;
3209			    loop_next = 1;
3210			    break;
3211			}
3212		    }
3213		    if (bad) {
3214#ifdef DEBUG
3215			dputs(ERRMSG("bad wordsplit reading history: "
3216				     "%s\nat: %s\nword: %s"),
3217			      start, lineptr, word);
3218#endif
3219			lineptr = start;
3220			nwordpos = 0;
3221			uselex = 0;
3222			loop_next = 1;
3223		    }
3224		}
3225	    } while (skipping);
3226	    if (loop_next) {
3227		if (loop_next == 1)
3228		    break;
3229		continue;
3230	    }
3231	    /* Record position of current word... */
3232	    words[nwordpos++] = lineptr - start;
3233	    words[nwordpos++] = lptr - start;
3234
3235	    /* ready for start of next word. */
3236	    lineptr = lptr;
3237	}
3238    }
3239    if (!uselex) {
3240	do {
3241	    for (;;) {
3242		if (inblank(*lineptr))
3243		    lineptr++;
3244		else if (lineptr[0] == '\\' && lineptr[1] == '\n')
3245		    lineptr += 2;
3246		else
3247		    break;
3248	    }
3249	    if (*lineptr) {
3250		if (nwordpos >= nwords) {
3251		    *nwordsp = nwords = nwords + 64;
3252		    *wordsp = words = (short *)
3253			zrealloc(words, nwords*sizeof(*words));
3254		}
3255		words[nwordpos++] = lineptr - start;
3256		while (*lineptr && !inblank(*lineptr))
3257		    lineptr++;
3258		words[nwordpos++] = lineptr - start;
3259	    }
3260	} while (*lineptr);
3261    }
3262
3263    *nwordposp = nwordpos;
3264}
3265
3266/* Move the current history list out of the way and prepare a fresh history
3267 * list using hf for HISTFILE, hs for HISTSIZE, and shs for SAVEHIST.  If
3268 * the hf value is an empty string, HISTFILE will be unset from the new
3269 * environment; if it is NULL, HISTFILE will not be changed, not even by the
3270 * pop function (this functionality is used internally to rewrite the current
3271 * history file without affecting pointers into the environment).
3272 */
3273
3274/**/
3275int
3276pushhiststack(char *hf, zlong hs, zlong shs, int level)
3277{
3278    struct histsave *h;
3279    int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
3280
3281    if (histsave_stack_pos == histsave_stack_size) {
3282	histsave_stack_size += 5;
3283	histsave_stack = zrealloc(histsave_stack,
3284			    histsave_stack_size * sizeof (struct histsave));
3285    }
3286
3287    if (curline_in_ring)
3288	unlinkcurline();
3289
3290    h = &histsave_stack[histsave_stack_pos++];
3291
3292    h->lasthist = lasthist;
3293    if (hf) {
3294	if ((h->histfile = getsparam("HISTFILE")) != NULL && *h->histfile)
3295	    h->histfile = ztrdup(h->histfile);
3296	else
3297	    h->histfile = "";
3298    } else
3299	h->histfile = NULL;
3300    h->histtab = histtab;
3301    h->hist_ring = hist_ring;
3302    h->curhist = curhist;
3303    h->histlinect = histlinect;
3304    h->histsiz = histsiz;
3305    h->savehistsiz = savehistsiz;
3306    h->locallevel = level;
3307
3308    memset(&lasthist, 0, sizeof lasthist);
3309    if (hf) {
3310	if (*hf)
3311	    setsparam("HISTFILE", ztrdup(hf));
3312	else
3313	    unsetparam("HISTFILE");
3314    }
3315    hist_ring = NULL;
3316    curhist = histlinect = 0;
3317    histsiz = hs;
3318    savehistsiz = shs;
3319    inithist(); /* sets histtab */
3320
3321    if (curline_in_ring)
3322	linkcurline();
3323
3324    return histsave_stack_pos;
3325}
3326
3327
3328/**/
3329int
3330pophiststack(void)
3331{
3332    struct histsave *h;
3333    int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
3334
3335    if (histsave_stack_pos == 0)
3336	return 0;
3337
3338    if (curline_in_ring)
3339	unlinkcurline();
3340
3341    deletehashtable(histtab);
3342    zsfree(lasthist.text);
3343
3344    h = &histsave_stack[--histsave_stack_pos];
3345
3346    lasthist = h->lasthist;
3347    if (h->histfile) {
3348	if (*h->histfile)
3349	    setsparam("HISTFILE", h->histfile);
3350	else
3351	    unsetparam("HISTFILE");
3352    }
3353    histtab = h->histtab;
3354    hist_ring = h->hist_ring;
3355    curhist = h->curhist;
3356    histlinect = h->histlinect;
3357    histsiz = h->histsiz;
3358    savehistsiz = h->savehistsiz;
3359
3360    if (curline_in_ring)
3361	linkcurline();
3362
3363    return histsave_stack_pos + 1;
3364}
3365
3366/* If pop_through > 0, pop all array items >= the 1-relative index value.
3367 * If pop_through <= 0, pop (-1)*pop_through levels off the stack.
3368 * If the (new) top of stack is from a higher locallevel, auto-pop until
3369 * it is not.
3370 */
3371
3372/**/
3373int
3374saveandpophiststack(int pop_through, int writeflags)
3375{
3376    if (pop_through <= 0) {
3377	pop_through += histsave_stack_pos + 1;
3378	if (pop_through <= 0)
3379	    pop_through = 1;
3380    }
3381    while (pop_through > 1
3382     && histsave_stack[pop_through-2].locallevel > locallevel)
3383	pop_through--;
3384    if (histsave_stack_pos < pop_through)
3385	return 0;
3386    do {
3387	if (!nohistsave)
3388	    savehistfile(NULL, 1, writeflags);
3389	pophiststack();
3390    } while (histsave_stack_pos >= pop_through);
3391    return 1;
3392}
3393