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