26763: fix problem on failed cd -s to relative path
[zsh.git] / Src / hist.c
blob80c5f17525ef56bfd24a65e7379a646125783f3c
1 /*
2 * hist.c - history expansion
4 * This file is part of zsh, the Z shell.
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
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.
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.
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.
30 #include "zsh.mdh"
31 #include "hist.pro"
33 /* Functions to call for getting/ungetting a character and for history
34 * word control. */
36 /**/
37 mod_export int (*hgetc) _((void));
39 /**/
40 void (*hungetc) _((int));
42 /**/
43 void (*hwaddc) _((int));
45 /**/
46 void (*hwbegin) _((int));
48 /**/
49 void (*hwend) _((void));
51 /**/
52 void (*addtoline) _((int));
54 /* != 0 means history substitution is turned off */
56 /**/
57 mod_export int stophist;
59 /* if != 0, we are expanding the current line */
61 /**/
62 mod_export int expanding;
64 /* these are used to modify the cursor position during expansion */
66 /**/
67 mod_export int excs, exlast;
70 * Current history event number
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().
81 /**/
82 mod_export zlong curhist;
84 /**/
85 struct histent curline;
87 /* current line count of allocated history entries */
89 /**/
90 zlong histlinect;
92 /* The history lines are kept in a hash, and also doubly-linked in a ring */
94 /**/
95 HashTable histtab;
96 /**/
97 mod_export Histent hist_ring;
99 /* capacity of history lists */
101 /**/
102 zlong histsiz;
104 /* desired history-file size (in lines) */
106 /**/
107 zlong savehistsiz;
109 /* if = 1, we have performed history substitution on the current line *
110 * if = 2, we have used the 'p' modifier */
112 /**/
113 int histdone;
115 /* state of the history mechanism */
117 /**/
118 int histactive;
120 /* Current setting of the associated option, but sometimes also includes
121 * the setting of the HIST_SAVE_NO_DUPS option. */
123 /**/
124 int hist_ignore_all_dups;
126 /* What flags (if any) we should skip when moving through the history */
128 /**/
129 mod_export int hist_skip_flags;
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 */
135 /* Array of word beginnings and endings in current history line. */
137 /**/
138 short *chwords;
140 /* Max, actual position in chwords.
141 * nwords = chwordpos/2 because we record beginning and end of words.
144 /**/
145 int chwordlen, chwordpos;
147 /* the last l for s/l/r/ history substitution */
149 /**/
150 char *hsubl;
152 /* the last r for s/l/r/ history substitution */
154 /**/
155 char *hsubr;
157 /* pointer into the history line */
159 /**/
160 mod_export char *hptr;
162 /* the current history line */
164 /**/
165 mod_export char *chline;
167 /* true if the last character returned by hgetc was an escaped bangchar *
168 * if it is set and NOBANGHIST is unset hwaddc escapes bangchars */
170 /**/
171 int qbang;
173 /* max size of histline */
175 /**/
176 int hlinesz;
178 /* default event (usually curhist-1, that is, "!!") */
180 static zlong defev;
182 /* Remember the last line in the history file so we can find it again. */
183 static struct histfile_stats {
184 char *text;
185 time_t stim, mtim;
186 off_t fpos, fsiz;
187 zlong next_write_ev;
188 } lasthist;
190 static struct histsave {
191 struct histfile_stats lasthist;
192 char *histfile;
193 HashTable histtab;
194 Histent hist_ring;
195 zlong curhist;
196 zlong histlinect;
197 zlong histsiz;
198 zlong savehistsiz;
199 int locallevel;
200 } *histsave_stack;
201 static int histsave_stack_size = 0;
202 static int histsave_stack_pos = 0;
204 static zlong histfile_linect;
206 /* add a character to the current history word */
208 static void
209 ihwaddc(int c)
211 /* Only if history line exists and lexing has not finished. */
212 if (chline && !(errflag || lexstop)) {
213 /* Quote un-expanded bangs in the history line. */
214 if (c == bangchar && stophist < 2 && qbang)
215 /* If qbang is not set, we do not escape this bangchar as it's *
216 * not mecessary (e.g. it's a bang in !=, or it is followed *
217 * by a space). Roughly speaking, qbang is zero only if the *
218 * history interpreter has already digested this bang and *
219 * found that it is not necessary to escape it. */
220 hwaddc('\\');
221 *hptr++ = c;
223 /* Resize history line if necessary */
224 if (hptr - chline >= hlinesz) {
225 int oldsiz = hlinesz;
227 chline = realloc(chline, hlinesz = oldsiz + 64);
228 hptr = chline + oldsiz;
233 /* This function adds a character to the zle input line. It is used when *
234 * zsh expands history (see doexpandhist() in zle_tricky.c). It also *
235 * calculates the new cursor position after the expansion. It is called *
236 * from hgetc() and from gettok() in lex.c for characters in comments. */
238 /**/
239 void
240 iaddtoline(int c)
242 if (!expanding || lexstop)
243 return;
244 if (qbang && c == bangchar && stophist < 2) {
245 exlast--;
246 zleentry(ZLE_CMD_ADD_TO_LINE, '\\');
248 if (excs > zlemetacs) {
249 excs += 1 + inbufct - exlast;
250 if (excs < zlemetacs)
251 /* this case could be handled better but it is *
252 * so rare that it does not worth it */
253 excs = zlemetacs;
255 exlast = inbufct;
256 zleentry(ZLE_CMD_ADD_TO_LINE, itok(c) ? ztokens[c - Pound] : c);
260 static int
261 ihgetc(void)
263 int c = ingetc();
265 qbang = 0;
266 if (!stophist && !(inbufflags & INP_ALIAS)) {
267 /* If necessary, expand history characters. */
268 c = histsubchar(c);
269 if (c < 0) {
270 /* bad expansion */
271 errflag = lexstop = 1;
272 return ' ';
275 if ((inbufflags & INP_HIST) && !stophist) {
276 /* the current character c came from a history expansion *
277 * (inbufflags & INP_HIST) and history is not disabled *
278 * (e.g. we are not inside single quotes). In that case, \! *
279 * should be treated as ! (since this \! came from a previous *
280 * history line where \ was used to escape the bang). So if *
281 * c == '\\' we fetch one more character to see if it's a bang, *
282 * and if it is not, we unget it and reset c back to '\\' */
283 qbang = 0;
284 if (c == '\\' && !(qbang = (c = ingetc()) == bangchar))
285 safeinungetc(c), c = '\\';
286 } else if (stophist || (inbufflags & INP_ALIAS))
287 /* If the result is a bangchar which came from history or alias *
288 * expansion, we treat it as an escaped bangchar, unless history *
289 * is disabled. If stophist == 1 it only means that history is *
290 * temporarily disabled by a !" which won't appear in in the *
291 * history, so we still have an escaped bang. stophist > 1 if *
292 * history is disabled with NOBANGHIST or by someone else (e.g. *
293 * when the lexer scans single quoted text). */
294 qbang = c == bangchar && (stophist < 2);
295 hwaddc(c);
296 addtoline(c);
298 return c;
301 /**/
302 static void
303 safeinungetc(int c)
305 if (lexstop)
306 lexstop = 0;
307 else
308 inungetc(c);
311 /**/
312 void
313 herrflush(void)
315 inpopalias();
317 while (!lexstop && inbufct && !strin)
318 hwaddc(ingetc());
322 * Extract :s/foo/bar/ delimiters and arguments
324 * The first character expected is the first delimiter.
325 * The arguments are stored in the hsubl and hsubr variables.
327 * subline is the part of the command line to be matched.
329 * If a ':' was found but was not followed by a 'G',
330 * *cflagp is set to 1 and the input is backed up to the
331 * character following the colon.
334 /**/
335 static int
336 getsubsargs(char *subline, int *gbalp, int *cflagp)
338 int del, follow;
339 char *ptr1, *ptr2;
341 del = ingetc();
342 ptr1 = hdynread2(del);
343 if (!ptr1)
344 return 1;
345 ptr2 = hdynread2(del);
346 if (strlen(ptr1)) {
347 zsfree(hsubl);
348 hsubl = ptr1;
349 } else if (!hsubl) { /* fail silently on this */
350 zsfree(ptr2);
351 return 0;
353 zsfree(hsubr);
354 hsubr = ptr2;
355 follow = ingetc();
356 if (follow == ':') {
357 follow = ingetc();
358 if (follow == 'G')
359 *gbalp = 1;
360 else {
361 inungetc(follow);
362 *cflagp = 1;
364 } else
365 inungetc(follow);
366 return 0;
369 /* Get the maximum no. of words for a history entry. */
371 /**/
372 static int
373 getargc(Histent ehist)
375 return ehist->nwords ? ehist->nwords-1 : 0;
378 /**/
379 static int
380 substfailed(void)
382 herrflush();
383 zerr("substitution failed");
384 return -1;
387 /* Perform history substitution, returning the next character afterwards. */
389 /**/
390 static int
391 histsubchar(int c)
393 int farg, evset = -1, larg, argc, cflag = 0, bflag = 0;
394 zlong ev;
395 static int marg = -1;
396 static zlong mev = -1;
397 char *buf, *ptr;
398 char *sline;
399 Histent ehist;
400 size_t buflen;
402 /* look, no goto's */
403 if (isfirstch && c == hatchar) {
404 int gbal = 0;
406 /* Line begins ^foo^bar */
407 isfirstch = 0;
408 inungetc(hatchar);
409 if (!(ehist = gethist(defev))
410 || !(sline = getargs(ehist, 0, getargc(ehist))))
411 return -1;
413 if (getsubsargs(sline, &gbal, &cflag))
414 return substfailed();
415 if (!hsubl)
416 return -1;
417 if (subst(&sline, hsubl, hsubr, gbal))
418 return substfailed();
419 } else {
420 /* Line doesn't begin ^foo^bar */
421 if (c != ' ')
422 isfirstch = 0;
423 if (c == '\\') {
424 int g = ingetc();
426 if (g != bangchar)
427 safeinungetc(g);
428 else {
429 qbang = 1;
430 return bangchar;
433 if (c != bangchar)
434 return c;
435 *hptr = '\0';
436 if ((c = ingetc()) == '{') {
437 bflag = cflag = 1;
438 c = ingetc();
440 if (c == '\"') {
441 stophist = 1;
442 return ingetc();
444 if ((!cflag && inblank(c)) || c == '=' || c == '(' || lexstop) {
445 safeinungetc(c);
446 return bangchar;
448 cflag = 0;
449 ptr = buf = zhalloc(buflen = 265);
451 /* get event number */
453 queue_signals();
454 if (c == '?') {
455 for (;;) {
456 c = ingetc();
457 if (c == '?' || c == '\n' || lexstop)
458 break;
459 else {
460 *ptr++ = c;
461 if (ptr == buf + buflen) {
462 buf = hrealloc(buf, buflen, 2 * buflen);
463 ptr = buf + buflen;
464 buflen *= 2;
468 if (c != '\n' && !lexstop)
469 c = ingetc();
470 *ptr = '\0';
471 mev = ev = hconsearch(hsubl = ztrdup(buf), &marg);
472 evset = 0;
473 if (ev == -1) {
474 herrflush();
475 unqueue_signals();
476 zerr("no such event: %s", buf);
477 return -1;
479 } else {
480 zlong t0;
482 for (;;) {
483 if (inblank(c) || c == ';' || c == ':' || c == '^' ||
484 c == '$' || c == '*' || c == '%' || c == '}' ||
485 c == '\'' || c == '"' || c == '`' || lexstop)
486 break;
487 if (ptr != buf) {
488 if (c == '-')
489 break;
490 if ((idigit(buf[0]) || buf[0] == '-') && !idigit(c))
491 break;
493 *ptr++ = c;
494 if (ptr == buf + buflen) {
495 buf = hrealloc(buf, buflen, 2 * buflen);
496 ptr = buf + buflen;
497 buflen *= 2;
499 if (c == '#' || c == bangchar) {
500 c = ingetc();
501 break;
503 c = ingetc();
505 *ptr = 0;
506 if (!*buf) {
507 if (c != '%') {
508 if (isset(CSHJUNKIEHISTORY))
509 ev = addhistnum(curhist,-1,HIST_FOREIGN);
510 else
511 ev = defev;
512 if (c == ':' && evset == -1)
513 evset = 0;
514 else
515 evset = 1;
516 } else {
517 if (marg != -1)
518 ev = mev;
519 else
520 ev = defev;
521 evset = 0;
523 } else if ((t0 = zstrtol(buf, NULL, 10))) {
524 ev = (t0 < 0) ? addhistnum(curhist,t0,HIST_FOREIGN) : t0;
525 evset = 1;
526 } else if ((unsigned)*buf == bangchar) {
527 ev = addhistnum(curhist,-1,HIST_FOREIGN);
528 evset = 1;
529 } else if (*buf == '#') {
530 ev = curhist;
531 evset = 1;
532 } else if ((ev = hcomsearch(buf)) == -1) {
533 herrflush();
534 unqueue_signals();
535 zerr("event not found: %s", buf);
536 return -1;
537 } else
538 evset = 1;
541 /* get the event */
543 if (!(ehist = gethist(defev = ev))) {
544 unqueue_signals();
545 return -1;
547 /* extract the relevant arguments */
549 argc = getargc(ehist);
550 if (c == ':') {
551 cflag = 1;
552 c = ingetc();
553 if (c == '%' && marg != -1) {
554 if (!evset) {
555 ehist = gethist(defev = mev);
556 argc = getargc(ehist);
557 } else {
558 herrflush();
559 unqueue_signals();
560 zerr("Ambiguous history reference");
561 return -1;
566 if (c == '*') {
567 farg = 1;
568 larg = argc;
569 cflag = 0;
570 } else {
571 inungetc(c);
572 larg = farg = getargspec(argc, marg, evset);
573 if (larg == -2) {
574 unqueue_signals();
575 return -1;
577 if (farg != -1)
578 cflag = 0;
579 c = ingetc();
580 if (c == '*') {
581 cflag = 0;
582 larg = argc;
583 } else if (c == '-') {
584 cflag = 0;
585 larg = getargspec(argc, marg, evset);
586 if (larg == -2) {
587 unqueue_signals();
588 return -1;
590 if (larg == -1)
591 larg = argc - 1;
592 } else
593 inungetc(c);
595 if (farg == -1)
596 farg = 0;
597 if (larg == -1)
598 larg = argc;
599 if (!(sline = getargs(ehist, farg, larg))) {
600 unqueue_signals();
601 return -1;
603 unqueue_signals();
606 /* do the modifiers */
608 for (;;) {
609 c = (cflag) ? ':' : ingetc();
610 cflag = 0;
611 if (c == ':') {
612 int gbal = 0;
614 if ((c = ingetc()) == 'g') {
615 gbal = 1;
616 c = ingetc();
617 if (c != 's' && c != '&') {
618 zerr("'s' or '&' modifier expected after 'g'");
619 return -1;
622 switch (c) {
623 case 'p':
624 histdone = HISTFLAG_DONE | HISTFLAG_NOEXEC;
625 break;
626 case 'a':
627 if (!chabspath(&sline)) {
628 herrflush();
629 zerr("modifier failed: a");
630 return -1;
632 break;
634 case 'A':
635 if (!chrealpath(&sline)) {
636 herrflush();
637 zerr("modifier failed: A");
638 return -1;
640 break;
641 case 'h':
642 if (!remtpath(&sline)) {
643 herrflush();
644 zerr("modifier failed: h");
645 return -1;
647 break;
648 case 'e':
649 if (!rembutext(&sline)) {
650 herrflush();
651 zerr("modifier failed: e");
652 return -1;
654 break;
655 case 'r':
656 if (!remtext(&sline)) {
657 herrflush();
658 zerr("modifier failed: r");
659 return -1;
661 break;
662 case 't':
663 if (!remlpaths(&sline)) {
664 herrflush();
665 zerr("modifier failed: t");
666 return -1;
668 break;
669 case 's':
670 if (getsubsargs(sline, &gbal, &cflag))
671 return -1; /* fall through */
672 case '&':
673 if (hsubl && hsubr) {
674 if (subst(&sline, hsubl, hsubr, gbal))
675 return substfailed();
676 } else {
677 herrflush();
678 zerr("no previous substitution");
679 return -1;
681 break;
682 case 'q':
683 quote(&sline);
684 break;
685 case 'Q':
687 int one = noerrs, oef = errflag;
689 noerrs = 1;
690 parse_subst_string(sline);
691 noerrs = one;
692 errflag = oef;
693 remnulargs(sline);
694 untokenize(sline);
696 break;
697 case 'x':
698 quotebreak(&sline);
699 break;
700 case 'l':
701 sline = casemodify(sline, CASMOD_LOWER);
702 break;
703 case 'u':
704 sline = casemodify(sline, CASMOD_UPPER);
705 break;
706 default:
707 herrflush();
708 zerr("illegal modifier: %c", c);
709 return -1;
711 } else {
712 if (c != '}' || !bflag)
713 inungetc(c);
714 if (c != '}' && bflag) {
715 zerr("'}' expected");
716 return -1;
718 break;
723 * Push the expanded value onto the input stack,
724 * marking this as a history word for purposes of the alias stack.
727 lexstop = 0;
728 /* this function is called only called from hgetc and only if *
729 * !(inbufflags & INP_ALIAS). History expansion should never be *
730 * done with INP_ALIAS (to prevent recursive history expansion and *
731 * histoty expansion of aliases). Escapes are not removed here. *
732 * This is now handled in hgetc. */
733 inpush(sline, INP_HIST, NULL); /* sline from heap, don't free */
734 histdone |= HISTFLAG_DONE;
735 if (isset(HISTVERIFY))
736 histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
738 /* Don't try and re-expand line. */
739 return ingetc();
742 /* unget a char and remove it from chline. It can only be used *
743 * to unget a character returned by hgetc. */
745 static void
746 ihungetc(int c)
748 int doit = 1;
750 while (!lexstop && !errflag) {
751 if (hptr[-1] != (char) c && stophist < 4 &&
752 hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\')
753 hungetc('\n'), hungetc('\\');
755 if (expanding) {
756 zlemetacs--;
757 zlemetall--;
758 exlast++;
760 DPUTS(hptr <= chline, "BUG: hungetc attempted at buffer start");
761 hptr--;
762 DPUTS(*hptr != (char) c, "BUG: wrong character in hungetc() ");
763 qbang = (c == bangchar && stophist < 2 &&
764 hptr > chline && hptr[-1] == '\\');
765 if (doit)
766 inungetc(c);
767 if (!qbang)
768 return;
769 doit = !stophist && ((inbufflags & INP_HIST) ||
770 !(inbufflags & INP_ALIAS));
771 c = '\\';
775 /* begin reading a string */
777 /**/
778 mod_export void
779 strinbeg(int dohist)
781 strin++;
782 hbegin(dohist);
783 lexinit();
786 /* done reading a string */
788 /**/
789 mod_export void
790 strinend(void)
792 hend(NULL);
793 DPUTS(!strin, "BUG: strinend() called without strinbeg()");
794 strin--;
795 isfirstch = 1;
796 histdone = 0;
799 /* dummy functions to use instead of hwaddc(), hwbegin(), and hwend() when
800 * they aren't needed */
802 static void
803 nohw(UNUSED(int c))
807 static void
808 nohwe(void)
812 /* these functions handle adding/removing curline to/from the hist_ring */
814 static void
815 linkcurline(void)
817 if (!hist_ring)
818 hist_ring = curline.up = curline.down = &curline;
819 else {
820 curline.up = hist_ring;
821 curline.down = hist_ring->down;
822 hist_ring->down = hist_ring->down->up = &curline;
823 hist_ring = &curline;
825 curline.histnum = ++curhist;
828 static void
829 unlinkcurline(void)
831 curline.up->down = curline.down;
832 curline.down->up = curline.up;
833 if (hist_ring == &curline) {
834 if (!histlinect)
835 hist_ring = NULL;
836 else
837 hist_ring = curline.up;
839 curhist--;
842 /* initialize the history mechanism */
844 /**/
845 mod_export void
846 hbegin(int dohist)
848 isfirstln = isfirstch = 1;
849 errflag = histdone = 0;
850 if (!dohist)
851 stophist = 2;
852 else if (dohist != 2)
853 stophist = (!interact || unset(SHINSTDIN)) ? 2 : 0;
854 else
855 stophist = 0;
856 if (stophist == 2 || (inbufflags & INP_ALIAS)) {
857 chline = hptr = NULL;
858 hlinesz = 0;
859 chwords = NULL;
860 chwordlen = 0;
861 hgetc = ingetc;
862 hungetc = inungetc;
863 hwaddc = nohw;
864 hwbegin = nohw;
865 hwend = nohwe;
866 addtoline = nohw;
867 } else {
868 chline = hptr = zshcalloc(hlinesz = 64);
869 chwords = zalloc((chwordlen = 64) * sizeof(short));
870 hgetc = ihgetc;
871 hungetc = ihungetc;
872 hwaddc = ihwaddc;
873 hwbegin = ihwbegin;
874 hwend = ihwend;
875 addtoline = iaddtoline;
876 if (!isset(BANGHIST))
877 stophist = 4;
879 chwordpos = 0;
881 if (hist_ring && !hist_ring->ftim && !strin)
882 hist_ring->ftim = time(NULL);
883 if ((dohist == 2 || (interact && isset(SHINSTDIN))) && !strin) {
884 histactive = HA_ACTIVE;
885 attachtty(mypgrp);
886 linkcurline();
887 defev = addhistnum(curhist, -1, HIST_FOREIGN);
888 } else
889 histactive = HA_ACTIVE | HA_NOINC;
892 /**/
893 void
894 histreduceblanks(void)
896 int i, len, pos, needblank, spacecount = 0;
898 if (isset(HISTIGNORESPACE))
899 while (chline[spacecount] == ' ') spacecount++;
901 for (i = 0, len = spacecount; i < chwordpos; i += 2) {
902 len += chwords[i+1] - chwords[i]
903 + (i > 0 && chwords[i] > chwords[i-1]);
905 if (chline[len] == '\0')
906 return;
908 for (i = 0, pos = spacecount; i < chwordpos; i += 2) {
909 len = chwords[i+1] - chwords[i];
910 needblank = (i < chwordpos-2 && chwords[i+2] > chwords[i+1]);
911 if (pos != chwords[i]) {
912 memcpy(chline + pos, chline + chwords[i], len + needblank);
913 chwords[i] = pos;
914 chwords[i+1] = chwords[i] + len;
916 pos += len + needblank;
918 chline[pos] = '\0';
921 /**/
922 void
923 histremovedups(void)
925 Histent he, next;
926 for (he = hist_ring; he; he = next) {
927 next = up_histent(he);
928 if (he->node.flags & HIST_DUP)
929 freehistnode(&he->node);
933 /**/
934 mod_export zlong
935 addhistnum(zlong hl, int n, int xflags)
937 int dir = n < 0? -1 : n > 0? 1 : 0;
938 Histent he = gethistent(hl, dir);
940 if (!he)
941 return 0;
942 if (he->histnum != hl)
943 n -= dir;
944 if (n)
945 he = movehistent(he, n, xflags);
946 if (!he)
947 return dir < 0? firsthist() - 1 : curhist + 1;
948 return he->histnum;
951 /**/
952 mod_export Histent
953 movehistent(Histent he, int n, int xflags)
955 while (n < 0) {
956 if (!(he = up_histent(he)))
957 return NULL;
958 if (!(he->node.flags & xflags))
959 n++;
961 while (n > 0) {
962 if (!(he = down_histent(he)))
963 return NULL;
964 if (!(he->node.flags & xflags))
965 n--;
967 checkcurline(he);
968 return he;
971 /**/
972 mod_export Histent
973 up_histent(Histent he)
975 return !he || he->up == hist_ring? NULL : he->up;
978 /**/
979 mod_export Histent
980 down_histent(Histent he)
982 return he == hist_ring? NULL : he->down;
985 /**/
986 mod_export Histent
987 gethistent(zlong ev, int nearmatch)
989 Histent he;
991 if (!hist_ring)
992 return NULL;
994 if (ev - hist_ring->down->histnum < hist_ring->histnum - ev) {
995 for (he = hist_ring->down; he->histnum < ev; he = he->down) ;
996 if (he->histnum != ev) {
997 if (nearmatch == 0
998 || (nearmatch < 0 && (he = up_histent(he)) == NULL))
999 return NULL;
1002 else {
1003 for (he = hist_ring; he->histnum > ev; he = he->up) ;
1004 if (he->histnum != ev) {
1005 if (nearmatch == 0
1006 || (nearmatch > 0 && (he = down_histent(he)) == NULL))
1007 return NULL;
1011 checkcurline(he);
1012 return he;
1015 static void
1016 putoldhistentryontop(short keep_going)
1018 static Histent next = NULL;
1019 Histent he = keep_going? next : hist_ring->down;
1020 next = he->down;
1021 if (isset(HISTEXPIREDUPSFIRST) && !(he->node.flags & HIST_DUP)) {
1022 static zlong max_unique_ct = 0;
1023 if (!keep_going)
1024 max_unique_ct = savehistsiz;
1025 do {
1026 if (max_unique_ct-- <= 0 || he == hist_ring) {
1027 max_unique_ct = 0;
1028 he = hist_ring->down;
1029 next = hist_ring;
1030 break;
1032 he = next;
1033 next = he->down;
1034 } while (!(he->node.flags & HIST_DUP));
1036 if (he != hist_ring->down) {
1037 he->up->down = he->down;
1038 he->down->up = he->up;
1039 he->up = hist_ring;
1040 he->down = hist_ring->down;
1041 hist_ring->down = he->down->up = he;
1043 hist_ring = he;
1046 /**/
1047 Histent
1048 prepnexthistent(void)
1050 Histent he;
1051 int curline_in_ring = hist_ring == &curline;
1053 if (curline_in_ring)
1054 unlinkcurline();
1055 if (hist_ring && hist_ring->node.flags & HIST_TMPSTORE) {
1056 curhist--;
1057 freehistnode(&hist_ring->node);
1060 if (histlinect < histsiz) {
1061 he = (Histent)zshcalloc(sizeof *he);
1062 if (!hist_ring)
1063 hist_ring = he->up = he->down = he;
1064 else {
1065 he->up = hist_ring;
1066 he->down = hist_ring->down;
1067 hist_ring->down = he->down->up = he;
1068 hist_ring = he;
1070 histlinect++;
1072 else {
1073 putoldhistentryontop(0);
1074 freehistdata(hist_ring, 0);
1075 he = hist_ring;
1077 he->histnum = ++curhist;
1078 if (curline_in_ring)
1079 linkcurline();
1080 return he;
1083 /* A helper function for hend() */
1085 static int
1086 should_ignore_line(Eprog prog)
1088 if (isset(HISTIGNORESPACE)) {
1089 if (*chline == ' ' || aliasspaceflag)
1090 return 1;
1093 if (!prog)
1094 return 0;
1096 if (isset(HISTNOFUNCTIONS)) {
1097 Wordcode pc = prog->prog;
1098 wordcode code = *pc;
1099 if (wc_code(code) == WC_LIST && WC_LIST_TYPE(code) & Z_SIMPLE
1100 && wc_code(pc[2]) == WC_FUNCDEF)
1101 return 1;
1104 if (isset(HISTNOSTORE)) {
1105 char *b = getjobtext(prog, NULL);
1106 int saw_builtin;
1107 if (*b == 'b' && strncmp(b,"builtin ",8) == 0) {
1108 b += 8;
1109 saw_builtin = 1;
1110 } else
1111 saw_builtin = 0;
1112 if (*b == 'h' && strncmp(b,"history",7) == 0 && (!b[7] || b[7] == ' ')
1113 && (saw_builtin || !shfunctab->getnode(shfunctab,"history")))
1114 return 1;
1115 if (*b == 'r' && (!b[1] || b[1] == ' ')
1116 && (saw_builtin || !shfunctab->getnode(shfunctab,"r")))
1117 return 1;
1118 if (*b == 'f' && b[1] == 'c' && b[2] == ' ' && b[3] == '-'
1119 && (saw_builtin || !shfunctab->getnode(shfunctab,"fc"))) {
1120 b += 3;
1121 do {
1122 if (*++b == 'l')
1123 return 1;
1124 } while (ialpha(*b));
1128 return 0;
1131 /* say we're done using the history mechanism */
1133 /**/
1134 mod_export int
1135 hend(Eprog prog)
1137 LinkList hookargs = newlinklist();
1138 int flag, save = 1, hookret, stack_pos = histsave_stack_pos;
1139 char *hf;
1141 DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline,
1142 "BUG: chline is NULL in hend()");
1143 queue_signals();
1144 if (histdone & HISTFLAG_SETTY)
1145 settyinfo(&shttyinfo);
1146 if (!(histactive & HA_NOINC))
1147 unlinkcurline();
1148 if (histactive & HA_NOINC) {
1149 zfree(chline, hlinesz);
1150 zfree(chwords, chwordlen*sizeof(short));
1151 chline = NULL;
1152 histactive = 0;
1153 unqueue_signals();
1154 return 1;
1156 if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS)
1157 && (hist_ignore_all_dups = isset(HISTIGNOREALLDUPS)) != 0)
1158 histremovedups();
1160 if (hptr) {
1162 * Added the following in case the test "hptr < chline + 1"
1163 * is more than just paranoia.
1165 DPUTS(hptr < chline, "History end pointer off start of line");
1166 *hptr = '\0';
1168 addlinknode(hookargs, "zshaddhistory");
1169 addlinknode(hookargs, chline);
1170 callhookfunc("zshaddhistory", hookargs, 1, &hookret);
1171 /* For history sharing, lock history file once for both read and write */
1172 hf = getsparam("HISTFILE");
1173 if (isset(SHAREHISTORY) && lockhistfile(hf, 0)) {
1174 readhistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
1175 curline.histnum = curhist+1;
1177 flag = histdone;
1178 histdone = 0;
1179 if (hptr < chline + 1)
1180 save = 0;
1181 else {
1182 if (hptr[-1] == '\n') {
1183 if (chline[1]) {
1184 *--hptr = '\0';
1185 } else
1186 save = 0;
1188 if (chwordpos <= 2)
1189 save = 0;
1190 else if (hookret || should_ignore_line(prog))
1191 save = -1;
1193 if (flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) {
1194 char *ptr;
1196 ptr = ztrdup(chline);
1197 if ((flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) == HISTFLAG_DONE) {
1198 zputs(ptr, shout);
1199 fputc('\n', shout);
1200 fflush(shout);
1202 if (flag & HISTFLAG_RECALL) {
1203 zpushnode(bufstack, ptr);
1204 save = 0;
1205 } else
1206 zsfree(ptr);
1208 if (save || *chline == ' ') {
1209 Histent he;
1210 for (he = hist_ring; he && he->node.flags & HIST_FOREIGN;
1211 he = up_histent(he)) ;
1212 if (he && he->node.flags & HIST_TMPSTORE) {
1213 if (he == hist_ring)
1214 curline.histnum = curhist--;
1215 freehistnode(&he->node);
1218 if (save) {
1219 Histent he;
1220 int newflags;
1222 #ifdef DEBUG
1223 /* debugging only */
1224 if (chwordpos%2) {
1225 hwend();
1226 DPUTS(1, "BUG: uncompleted line in history");
1228 #endif
1229 /* get rid of pesky \n which we've already nulled out */
1230 if (chwordpos > 1 && !chline[chwords[chwordpos-2]]) {
1231 chwordpos -= 2;
1232 /* strip superfluous blanks, if desired */
1233 if (isset(HISTREDUCEBLANKS))
1234 histreduceblanks();
1236 newflags = save > 0? 0 : HIST_TMPSTORE;
1237 if ((isset(HISTIGNOREDUPS) || isset(HISTIGNOREALLDUPS)) && save > 0
1238 && hist_ring && histstrcmp(chline, hist_ring->node.nam) == 0) {
1239 /* This history entry compares the same as the previous.
1240 * In case minor changes were made, we overwrite the
1241 * previous one with the current one. This also gets the
1242 * timestamp right. Perhaps, preserve the HIST_OLD flag.
1244 he = hist_ring;
1245 newflags |= he->node.flags & HIST_OLD; /* Avoid re-saving */
1246 freehistdata(he, 0);
1247 curline.histnum = curhist;
1248 } else
1249 he = prepnexthistent();
1251 he->node.nam = ztrdup(chline);
1252 he->stim = time(NULL);
1253 he->ftim = 0L;
1254 he->node.flags = newflags;
1256 if ((he->nwords = chwordpos/2)) {
1257 he->words = (short *)zalloc(chwordpos * sizeof(short));
1258 memcpy(he->words, chwords, chwordpos * sizeof(short));
1260 if (!(newflags & HIST_TMPSTORE))
1261 addhistnode(histtab, he->node.nam, he);
1263 zfree(chline, hlinesz);
1264 zfree(chwords, chwordlen*sizeof(short));
1265 chline = NULL;
1266 histactive = 0;
1267 if (isset(SHAREHISTORY)? histfileIsLocked() : isset(INCAPPENDHISTORY))
1268 savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
1269 unlockhistfile(hf); /* It's OK to call this even if we aren't locked */
1271 * No good reason for the user to push the history more than once, but
1272 * it's easy to be tidy...
1274 while (histsave_stack_pos > stack_pos)
1275 pophiststack();
1276 unqueue_signals();
1277 return !(flag & HISTFLAG_NOEXEC || errflag);
1280 /* Gives current expansion word if not last word before chwordpos. */
1282 /**/
1283 int hwgetword = -1;
1285 /* begin a word */
1287 /**/
1288 void
1289 ihwbegin(int offset)
1291 if (stophist == 2)
1292 return;
1293 if (chwordpos%2)
1294 chwordpos--; /* make sure we're on a word start, not end */
1295 /* If we're expanding an alias, we should overwrite the expansion
1296 * in the history.
1298 if ((inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST))
1299 hwgetword = chwordpos;
1300 else
1301 hwgetword = -1;
1302 chwords[chwordpos++] = hptr - chline + offset;
1305 /* add a word to the history List */
1307 /**/
1308 void
1309 ihwend(void)
1311 if (stophist == 2)
1312 return;
1313 if (chwordpos%2 && chline) {
1314 /* end of word reached and we've already begun a word */
1315 if (hptr > chline + chwords[chwordpos-1]) {
1316 chwords[chwordpos++] = hptr - chline;
1317 if (chwordpos >= chwordlen) {
1318 chwords = (short *) realloc(chwords,
1319 (chwordlen += 32) *
1320 sizeof(short));
1322 if (hwgetword > -1) {
1323 /* We want to reuse the current word position */
1324 chwordpos = hwgetword;
1325 /* Start from where previous word ended, if possible */
1326 hptr = chline + chwords[chwordpos ? chwordpos - 1 : 0];
1328 } else {
1329 /* scrub that last word, it doesn't exist */
1330 chwordpos--;
1335 /* Go back to immediately after the last word, skipping space. */
1337 /**/
1338 void
1339 histbackword(void)
1341 if (!(chwordpos%2) && chwordpos)
1342 hptr = chline + chwords[chwordpos-1];
1345 /* Get the start and end point of the current history word */
1347 /**/
1348 static void
1349 hwget(char **startptr)
1351 int pos = hwgetword > -1 ? hwgetword : chwordpos - 2;
1353 #ifdef DEBUG
1354 /* debugging only */
1355 if (hwgetword == -1 && !chwordpos) {
1356 /* no words available */
1357 DPUTS(1, "BUG: hwget() called with no words");
1358 *startptr = "";
1359 return;
1361 else if (hwgetword == -1 && chwordpos%2) {
1362 DPUTS(1, "BUG: hwget() called in middle of word");
1363 *startptr = "";
1364 return;
1366 #endif
1368 *startptr = chline + chwords[pos];
1369 chline[chwords[++pos]] = '\0';
1372 /* Replace the current history word with rep, if different */
1374 /**/
1375 void
1376 hwrep(char *rep)
1378 char *start;
1379 hwget(&start);
1381 if (!strcmp(rep, start))
1382 return;
1384 hptr = start;
1385 chwordpos = (hwgetword > -1) ? hwgetword : chwordpos - 2;
1386 hwbegin(0);
1387 qbang = 1;
1388 while (*rep)
1389 hwaddc(*rep++);
1390 hwend();
1393 /* Get the entire current line, deleting it in the history. */
1395 /**/
1396 mod_export char *
1397 hgetline(void)
1399 /* Currently only used by pushlineoredit().
1400 * It's necessary to prevent that from getting too pally with
1401 * the history code.
1403 char *ret;
1405 if (!chline || hptr == chline)
1406 return NULL;
1407 *hptr = '\0';
1408 ret = dupstring(chline);
1410 /* reset line */
1411 hptr = chline;
1412 chwordpos = 0;
1413 hwgetword = -1;
1415 return ret;
1418 /* get an argument specification */
1420 /**/
1421 static int
1422 getargspec(int argc, int marg, int evset)
1424 int c, ret = -1;
1426 if ((c = ingetc()) == '0')
1427 return 0;
1428 if (idigit(c)) {
1429 ret = 0;
1430 while (idigit(c)) {
1431 ret = ret * 10 + c - '0';
1432 c = ingetc();
1434 inungetc(c);
1435 } else if (c == '^')
1436 ret = 1;
1437 else if (c == '$')
1438 ret = argc;
1439 else if (c == '%') {
1440 if (evset) {
1441 herrflush();
1442 zerr("Ambiguous history reference");
1443 return -2;
1445 if (marg == -1) {
1446 herrflush();
1447 zerr("%% with no previous word matched");
1448 return -2;
1450 ret = marg;
1451 } else
1452 inungetc(c);
1453 return ret;
1456 /* do ?foo? search */
1458 /**/
1459 static zlong
1460 hconsearch(char *str, int *marg)
1462 int t1 = 0;
1463 char *s;
1464 Histent he;
1466 for (he = up_histent(hist_ring); he; he = up_histent(he)) {
1467 if (he->node.flags & HIST_FOREIGN)
1468 continue;
1469 if ((s = strstr(he->node.nam, str))) {
1470 int pos = s - he->node.nam;
1471 while (t1 < he->nwords && he->words[2*t1] <= pos)
1472 t1++;
1473 *marg = t1 - 1;
1474 return he->histnum;
1477 return -1;
1480 /* do !foo search */
1482 /**/
1483 zlong
1484 hcomsearch(char *str)
1486 Histent he;
1487 int len = strlen(str);
1489 for (he = up_histent(hist_ring); he; he = up_histent(he)) {
1490 if (he->node.flags & HIST_FOREIGN)
1491 continue;
1492 if (strncmp(he->node.nam, str, len) == 0)
1493 return he->histnum;
1495 return -1;
1498 /* various utilities for : modifiers */
1500 /**/
1502 chabspath(char **junkptr)
1504 char *current, *dest;
1506 if (!**junkptr)
1507 return 1;
1509 if (**junkptr != '/') {
1510 *junkptr = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", *junkptr);
1513 current = *junkptr;
1514 dest = *junkptr;
1516 #ifdef HAVE_SUPERROOT
1517 while (*current == '/' && current[1] == '.' && current[2] == '.' &&
1518 (!current[3] || current[3] == '/')) {
1519 *dest++ = '/';
1520 *dest++ = '.';
1521 *dest++ = '.';
1522 current += 3;
1524 #endif
1526 for (;;) {
1527 if (*current == '/') {
1528 #ifdef __CYGWIN__
1529 if (current == *junkptr && current[1] == '/')
1530 *dest++ = *current++;
1531 #endif
1532 *dest++ = *current++;
1533 while (*current == '/')
1534 current++;
1535 } else if (!*current) {
1536 while (dest > *junkptr + 1 && dest[-1] == '/')
1537 dest--;
1538 *dest = '\0';
1539 break;
1540 } else if (current[0] == '.' && current[1] == '.' &&
1541 (!current[2] || current[2] == '/')) {
1542 if (current == *junkptr || dest == *junkptr) {
1543 *dest++ = '.';
1544 *dest++ = '.';
1545 current += 2;
1546 } else if (dest > *junkptr + 2 &&
1547 !strncmp(dest - 3, "../", 3)) {
1548 *dest++ = '.';
1549 *dest++ = '.';
1550 current += 2;
1551 } else if (dest > *junkptr + 1) {
1552 *dest = '\0';
1553 for (dest--;
1554 dest > *junkptr + 1 && dest[-1] != '/';
1555 dest--);
1556 if (dest[-1] != '/')
1557 dest--;
1558 current += 2;
1559 if (*current == '/')
1560 current++;
1561 } else if (dest == *junkptr + 1) {
1562 /* This might break with Cygwin's leading double slashes? */
1563 current += 2;
1564 } else {
1565 return 0;
1567 } else if (current[0] == '.' && (current[1] == '/' || !current[1])) {
1568 while (*++current == '/');
1569 } else {
1570 while (*current != '/' && *current != '\0')
1571 if ((*dest++ = *current++) == Meta)
1572 *dest++ = *current++;
1575 return 1;
1578 /**/
1580 chrealpath(char **junkptr)
1582 char *str;
1583 #ifdef HAVE_CANONICALIZE_FILE_NAME
1584 char *lastpos, *nonreal, *real;
1585 #else
1586 # ifdef HAVE_REALPATH
1587 char *lastpos, *nonreal, real[PATH_MAX];
1588 # endif
1589 #endif
1591 if (!**junkptr)
1592 return 1;
1594 /* Notice that this means ..'s are applied before symlinks are resolved! */
1595 if (!chabspath(junkptr))
1596 return 0;
1598 #if !defined(HAVE_REALPATH) && !defined(HAVE_CANONICALIZE_FILE_NAME)
1599 return 1;
1600 #else
1602 * Notice that this means you cannot pass relative paths into this
1603 * function!
1605 if (**junkptr != '/')
1606 return 0;
1608 unmetafy(*junkptr, NULL);
1610 lastpos = strend(*junkptr);
1611 nonreal = lastpos + 1;
1613 while (!
1614 #ifdef HAVE_CANONICALIZE_FILE_NAME
1616 * This is a GNU extension to realpath(); it's the
1617 * same as calling realpath() with a NULL second argument
1618 * which uses malloc() to get memory. The alternative
1619 * interface is easier to test for, however.
1621 (real = canonicalize_file_name(*junkptr))
1622 #else
1623 realpath(*junkptr, real)
1624 #endif
1626 if (errno == EINVAL || errno == ELOOP ||
1627 errno == ENAMETOOLONG || errno == ENOMEM)
1628 return 0;
1630 if (nonreal == *junkptr) {
1631 *real = '\0';
1632 break;
1635 while (*nonreal != '/' && nonreal >= *junkptr)
1636 nonreal--;
1637 *nonreal = '\0';
1640 str = nonreal;
1641 while (str <= lastpos) {
1642 if (*str == '\0')
1643 *str = '/';
1644 str++;
1647 *junkptr = metafy(bicat(real, nonreal), -1, META_HEAPDUP);
1648 #ifdef HAVE_CANONICALIZE_FILE_NAME
1649 free(real);
1650 #endif
1651 #endif
1653 return 1;
1656 /**/
1658 remtpath(char **junkptr)
1660 char *str = strend(*junkptr);
1662 /* ignore trailing slashes */
1663 while (str >= *junkptr && IS_DIRSEP(*str))
1664 --str;
1665 /* skip filename */
1666 while (str >= *junkptr && !IS_DIRSEP(*str))
1667 --str;
1668 if (str < *junkptr) {
1669 if (IS_DIRSEP(**junkptr))
1670 *junkptr = dupstring ("/");
1671 else
1672 *junkptr = dupstring (".");
1674 return 0;
1676 /* repeated slashes are considered like a single slash */
1677 while (str > *junkptr && IS_DIRSEP(str[-1]))
1678 --str;
1679 /* never erase the root slash */
1680 if (str == *junkptr) {
1681 ++str;
1682 /* Leading doubled slashes (`//') have a special meaning on cygwin
1683 and some old flavor of UNIX, so we do not assimilate them to
1684 a single slash. However a greater number is ok to squeeze. */
1685 if (IS_DIRSEP(*str) && !IS_DIRSEP(str[1]))
1686 ++str;
1688 *str = '\0';
1689 return 1;
1692 /**/
1694 remtext(char **junkptr)
1696 char *str;
1698 for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str)
1699 if (*str == '.') {
1700 *str = '\0';
1701 return 1;
1703 return 0;
1706 /**/
1708 rembutext(char **junkptr)
1710 char *str;
1712 for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str)
1713 if (*str == '.') {
1714 *junkptr = dupstring(str + 1); /* .xx or xx? */
1715 return 1;
1717 /* no extension */
1718 *junkptr = dupstring ("");
1719 return 0;
1722 /**/
1723 mod_export int
1724 remlpaths(char **junkptr)
1726 char *str = strend(*junkptr);
1728 if (IS_DIRSEP(*str)) {
1729 /* remove trailing slashes */
1730 while (str >= *junkptr && IS_DIRSEP(*str))
1731 --str;
1732 str[1] = '\0';
1734 for (; str >= *junkptr; --str)
1735 if (IS_DIRSEP(*str)) {
1736 *str = '\0';
1737 *junkptr = dupstring(str + 1);
1738 return 1;
1740 return 0;
1744 * Return modified version of str from the heap with modification
1745 * according to one of the CASMOD_* types defined in zsh.h; CASMOD_NONE
1746 * is not handled, for obvious reasons.
1749 /**/
1750 char *
1751 casemodify(char *str, int how)
1753 char *str2 = zhalloc(2 * strlen(str) + 1);
1754 char *ptr2 = str2;
1755 int nextupper = 1;
1757 #ifdef MULTIBYTE_SUPPORT
1758 if (isset(MULTIBYTE)) {
1759 VARARR(char, mbstr, MB_CUR_MAX);
1760 mbstate_t ps;
1762 mb_metacharinit();
1763 memset(&ps, 0, sizeof(ps));
1764 while (*str) {
1765 wint_t wc;
1766 int len = mb_metacharlenconv(str, &wc), mod = 0, len2;
1768 * wc is set to WEOF if the start of str couldn't be
1769 * converted. Presumably WEOF doesn't match iswlower(), but
1770 * better be safe.
1772 if (wc == WEOF) {
1773 while (len--)
1774 *ptr2++ = *str++;
1775 /* not alphanumeric */
1776 nextupper = 1;
1777 continue;
1779 switch (how) {
1780 case CASMOD_LOWER:
1781 if (iswupper(wc)) {
1782 wc = towlower(wc);
1783 mod = 1;
1785 break;
1787 case CASMOD_UPPER:
1788 if (iswlower(wc)) {
1789 wc = towupper(wc);
1790 mod = 1;
1792 break;
1794 case CASMOD_CAPS:
1795 default: /* shuts up compiler */
1796 if (IS_COMBINING(wc))
1797 break;
1798 if (!iswalnum(wc))
1799 nextupper = 1;
1800 else if (nextupper) {
1801 if (iswlower(wc)) {
1802 wc = towupper(wc);
1803 mod = 1;
1805 nextupper = 0;
1806 } else if (iswupper(wc)) {
1807 wc = towlower(wc);
1808 mod = 1;
1810 break;
1812 if (mod && (len2 = wcrtomb(mbstr, wc, &ps)) > 0) {
1813 char *mbptr;
1815 for (mbptr = mbstr; mbptr < mbstr + len2; mbptr++) {
1816 if (imeta(STOUC(*mbptr))) {
1817 *ptr2++ = Meta;
1818 *ptr2++ = *mbptr ^ 32;
1819 } else
1820 *ptr2++ = *mbptr;
1822 str += len;
1823 } else {
1824 while (len--)
1825 *ptr2++ = *str++;
1829 else
1830 #endif
1831 while (*str) {
1832 int c;
1833 if (*str == Meta) {
1834 c = str[1] ^ 32;
1835 str += 2;
1836 } else
1837 c = *str++;
1838 switch (how) {
1839 case CASMOD_LOWER:
1840 if (isupper(c))
1841 c = tolower(c);
1842 break;
1844 case CASMOD_UPPER:
1845 if (islower(c))
1846 c = toupper(c);
1847 break;
1849 case CASMOD_CAPS:
1850 default: /* shuts up compiler */
1851 if (!ialnum(c))
1852 nextupper = 1;
1853 else if (nextupper) {
1854 if (islower(c))
1855 c = toupper(c);
1856 nextupper = 0;
1857 } else if (isupper(c))
1858 c = tolower(c);
1859 break;
1861 if (imeta(c)) {
1862 *ptr2++ = Meta;
1863 *ptr2++ = c ^ 32;
1864 } else
1865 *ptr2++ = c;
1867 *ptr2 = '\0';
1868 return str2;
1873 * Substitute "in" for "out" in "*strptr" and update "*strptr".
1874 * If "gbal", do global substitution.
1876 * This returns a result from the heap. There seems to have
1877 * been some confusion on this point.
1880 /**/
1882 subst(char **strptr, char *in, char *out, int gbal)
1884 char *str = *strptr, *substcut, *sptr;
1885 int off, inlen, outlen;
1887 if (!*in)
1888 in = str, gbal = 0;
1890 if (isset(HISTSUBSTPATTERN)) {
1891 int fl = SUB_LONG|SUB_REST|SUB_RETFAIL;
1892 char *oldin = in;
1893 if (gbal)
1894 fl |= SUB_GLOBAL;
1895 if (*in == '#' || *in == Pound) {
1896 /* anchor at head, flag needed if SUB_END is also set */
1897 fl |= SUB_START;
1898 in++;
1900 if (*in == '%') {
1901 /* anchor at tail */
1902 in++;
1903 fl |= SUB_END;
1905 if (in == oldin) {
1906 /* no anchor, substring match */
1907 fl |= SUB_SUBSTR;
1909 if (in == str)
1910 in = dupstring(in);
1911 if (parse_subst_string(in) || errflag)
1912 return 1;
1913 if (parse_subst_string(out) || errflag)
1914 return 1;
1915 singsub(&in);
1916 if (getmatch(strptr, in, fl, 1, out))
1917 return 0;
1918 } else {
1919 if ((substcut = (char *)strstr(str, in))) {
1920 inlen = strlen(in);
1921 sptr = convamps(out, in, inlen);
1922 outlen = strlen(sptr);
1924 do {
1925 *substcut = '\0';
1926 off = substcut - *strptr + outlen;
1927 substcut += inlen;
1928 *strptr = zhtricat(*strptr, sptr, substcut);
1929 str = (char *)*strptr + off;
1930 } while (gbal && (substcut = (char *)strstr(str, in)));
1932 return 0;
1936 return 1;
1939 /**/
1940 static char *
1941 convamps(char *out, char *in, int inlen)
1943 char *ptr, *ret, *pp;
1944 int slen, sdup = 0;
1946 for (ptr = out, slen = 0; *ptr; ptr++, slen++)
1947 if (*ptr == '\\')
1948 ptr++, sdup = 1;
1949 else if (*ptr == '&')
1950 slen += inlen - 1, sdup = 1;
1951 if (!sdup)
1952 return out;
1953 ret = pp = (char *) zhalloc(slen + 1);
1954 for (ptr = out; *ptr; ptr++)
1955 if (*ptr == '\\')
1956 *pp++ = *++ptr;
1957 else if (*ptr == '&') {
1958 strcpy(pp, in);
1959 pp += inlen;
1960 } else
1961 *pp++ = *ptr;
1962 *pp = '\0';
1963 return ret;
1966 /**/
1967 mod_export void
1968 checkcurline(Histent he)
1970 if (he->histnum == curhist && (histactive & HA_ACTIVE)) {
1971 curline.node.nam = chline;
1972 curline.nwords = chwordpos/2;
1973 curline.words = chwords;
1977 /**/
1978 mod_export Histent
1979 quietgethist(int ev)
1981 return gethistent(ev, GETHIST_EXACT);
1984 /**/
1985 static Histent
1986 gethist(int ev)
1988 Histent ret;
1990 ret = quietgethist(ev);
1991 if (!ret) {
1992 herrflush();
1993 zerr("no such event: %d", ev);
1995 return ret;
1998 /**/
1999 static char *
2000 getargs(Histent elist, int arg1, int arg2)
2002 short *words = elist->words;
2003 int pos1, nwords = elist->nwords;
2005 if (arg2 < arg1 || arg1 >= nwords || arg2 >= nwords) {
2006 /* remember, argN is indexed from 0, nwords is total no. of words */
2007 herrflush();
2008 zerr("no such word in event");
2009 return NULL;
2012 pos1 = words[2*arg1];
2013 return dupstrpfx(elist->node.nam + pos1, words[2*arg2+1] - pos1);
2016 /**/
2018 quote(char **tr)
2020 char *ptr, *rptr, **str = (char **)tr;
2021 int len = 3;
2022 int inquotes = 0;
2024 for (ptr = *str; *ptr; ptr++, len++)
2025 if (*ptr == '\'') {
2026 len += 3;
2027 if (!inquotes)
2028 inquotes = 1;
2029 else
2030 inquotes = 0;
2031 } else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\')
2032 len += 2;
2033 ptr = *str;
2034 *str = rptr = (char *) zhalloc(len);
2035 *rptr++ = '\'';
2036 for (; *ptr; ptr++)
2037 if (*ptr == '\'') {
2038 if (!inquotes)
2039 inquotes = 1;
2040 else
2041 inquotes = 0;
2042 *rptr++ = '\'';
2043 *rptr++ = '\\';
2044 *rptr++ = '\'';
2045 *rptr++ = '\'';
2046 } else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\') {
2047 *rptr++ = '\'';
2048 *rptr++ = *ptr;
2049 *rptr++ = '\'';
2050 } else
2051 *rptr++ = *ptr;
2052 *rptr++ = '\'';
2053 *rptr++ = 0;
2054 str[1] = NULL;
2055 return 0;
2058 /**/
2059 static int
2060 quotebreak(char **tr)
2062 char *ptr, *rptr, **str = (char **)tr;
2063 int len = 3;
2065 for (ptr = *str; *ptr; ptr++, len++)
2066 if (*ptr == '\'')
2067 len += 3;
2068 else if (inblank(*ptr))
2069 len += 2;
2070 ptr = *str;
2071 *str = rptr = (char *) zhalloc(len);
2072 *rptr++ = '\'';
2073 for (; *ptr;)
2074 if (*ptr == '\'') {
2075 *rptr++ = '\'';
2076 *rptr++ = '\\';
2077 *rptr++ = '\'';
2078 *rptr++ = '\'';
2079 ptr++;
2080 } else if (inblank(*ptr)) {
2081 *rptr++ = '\'';
2082 *rptr++ = *ptr++;
2083 *rptr++ = '\'';
2084 } else
2085 *rptr++ = *ptr++;
2086 *rptr++ = '\'';
2087 *rptr++ = '\0';
2088 return 0;
2091 /* read an arbitrary amount of data into a buffer until stop is found */
2093 #if 0 /**/
2094 char *
2095 hdynread(int stop)
2097 int bsiz = 256, ct = 0, c;
2098 char *buf = (char *)zalloc(bsiz), *ptr;
2100 ptr = buf;
2101 while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
2102 if (c == '\\')
2103 c = ingetc();
2104 *ptr++ = c;
2105 if (++ct == bsiz) {
2106 buf = realloc(buf, bsiz *= 2);
2107 ptr = buf + ct;
2110 *ptr = 0;
2111 if (c == '\n') {
2112 inungetc('\n');
2113 zerr("delimiter expected");
2114 zfree(buf, bsiz);
2115 return NULL;
2117 return buf;
2119 #endif
2121 /**/
2122 static char *
2123 hdynread2(int stop)
2125 int bsiz = 256, ct = 0, c;
2126 char *buf = (char *)zalloc(bsiz), *ptr;
2128 ptr = buf;
2129 while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
2130 if (c == '\\')
2131 c = ingetc();
2132 *ptr++ = c;
2133 if (++ct == bsiz) {
2134 buf = realloc(buf, bsiz *= 2);
2135 ptr = buf + ct;
2138 *ptr = 0;
2139 if (c == '\n')
2140 inungetc('\n');
2141 return buf;
2144 /**/
2145 void
2146 inithist(void)
2148 createhisttable();
2151 /**/
2152 void
2153 resizehistents(void)
2155 if (histlinect > histsiz) {
2156 /* The reason we don't just call freehistnode(hist_ring->down) is
2157 * so that we can honor the HISTEXPIREDUPSFIRST setting. */
2158 putoldhistentryontop(0);
2159 freehistnode(&hist_ring->node);
2160 while (histlinect > histsiz) {
2161 putoldhistentryontop(1);
2162 freehistnode(&hist_ring->node);
2167 static int
2168 readhistline(int start, char **bufp, int *bufsiz, FILE *in)
2170 char *buf = *bufp;
2171 if (fgets(buf + start, *bufsiz - start, in)) {
2172 int len = start + strlen(buf + start);
2173 if (len == start)
2174 return -1;
2175 if (buf[len - 1] != '\n') {
2176 if (!feof(in)) {
2177 if (len < (*bufsiz) - 1)
2178 return -1;
2179 *bufp = zrealloc(buf, 2 * (*bufsiz));
2180 *bufsiz = 2 * (*bufsiz);
2181 return readhistline(len, bufp, bufsiz, in);
2184 else {
2185 buf[len - 1] = '\0';
2186 if (len > 1 && buf[len - 2] == '\\') {
2187 buf[--len - 1] = '\n';
2188 if (!feof(in))
2189 return readhistline(len, bufp, bufsiz, in);
2192 return len;
2194 return 0;
2197 /**/
2198 void
2199 readhistfile(char *fn, int err, int readflags)
2201 char *buf, *start = NULL;
2202 FILE *in;
2203 Histent he;
2204 time_t stim, ftim, tim = time(NULL);
2205 off_t fpos;
2206 short *wordlist;
2207 struct stat sb;
2208 int nwordpos, nwordlist, bufsiz;
2209 int searching, newflags, l;
2211 if (!fn && !(fn = getsparam("HISTFILE")))
2212 return;
2213 if (readflags & HFILE_FAST) {
2214 if (stat(unmeta(fn), &sb) < 0
2215 || (lasthist.fsiz == sb.st_size && lasthist.mtim == sb.st_mtime)
2216 || !lockhistfile(fn, 0))
2217 return;
2218 lasthist.fsiz = sb.st_size;
2219 lasthist.mtim = sb.st_mtime;
2221 else if (!lockhistfile(fn, 1))
2222 return;
2223 if ((in = fopen(unmeta(fn), "r"))) {
2224 nwordlist = 64;
2225 wordlist = (short *)zalloc(nwordlist*sizeof(short));
2226 bufsiz = 1024;
2227 buf = zalloc(bufsiz);
2229 if (readflags & HFILE_FAST && lasthist.text) {
2230 if (lasthist.fpos < lasthist.fsiz) {
2231 fseek(in, lasthist.fpos, 0);
2232 searching = 1;
2234 else {
2235 histfile_linect = 0;
2236 searching = -1;
2238 } else
2239 searching = 0;
2241 newflags = HIST_OLD | HIST_READ;
2242 if (readflags & HFILE_FAST)
2243 newflags |= HIST_FOREIGN;
2244 if (readflags & HFILE_SKIPOLD
2245 || (hist_ignore_all_dups && newflags & hist_skip_flags))
2246 newflags |= HIST_MAKEUNIQUE;
2247 while (fpos = ftell(in), (l = readhistline(0, &buf, &bufsiz, in))) {
2248 char *pt = buf;
2250 if (l < 0) {
2251 zerr("corrupt history file %s", fn);
2252 break;
2254 if (*pt == ':') {
2255 pt++;
2256 stim = zstrtol(pt, NULL, 0);
2257 for (; *pt != ':' && *pt; pt++);
2258 if (*pt) {
2259 pt++;
2260 ftim = zstrtol(pt, NULL, 0);
2261 for (; *pt != ';' && *pt; pt++);
2262 if (*pt)
2263 pt++;
2264 } else
2265 ftim = stim;
2266 } else {
2267 if (*pt == '\\' && pt[1] == ':')
2268 pt++;
2269 stim = ftim = 0;
2272 if (searching) {
2273 if (searching > 0) {
2274 if (stim == lasthist.stim
2275 && histstrcmp(pt, lasthist.text) == 0)
2276 searching = 0;
2277 else {
2278 fseek(in, 0, 0);
2279 histfile_linect = 0;
2280 searching = -1;
2282 continue;
2284 else if (stim < lasthist.stim) {
2285 histfile_linect++;
2286 continue;
2288 searching = 0;
2291 if (readflags & HFILE_USE_OPTIONS) {
2292 histfile_linect++;
2293 lasthist.fpos = fpos;
2294 lasthist.stim = stim;
2297 he = prepnexthistent();
2298 he->node.nam = ztrdup(pt);
2299 he->node.flags = newflags;
2300 if ((he->stim = stim) == 0)
2301 he->stim = he->ftim = tim;
2302 else if (ftim < stim)
2303 he->ftim = stim + ftim;
2304 else
2305 he->ftim = ftim;
2307 /* Divide up the words. We don't know how it lexes,
2308 so just look for white-space.
2310 nwordpos = 0;
2311 start = pt;
2312 do {
2313 while (inblank(*pt))
2314 pt++;
2315 if (*pt) {
2316 if (nwordpos >= nwordlist)
2317 wordlist = (short *) realloc(wordlist,
2318 (nwordlist += 64)*sizeof(short));
2319 wordlist[nwordpos++] = pt - start;
2320 while (*pt && !inblank(*pt))
2321 pt++;
2322 wordlist[nwordpos++] = pt - start;
2324 } while (*pt);
2326 he->nwords = nwordpos/2;
2327 if (he->nwords) {
2328 he->words = (short *)zalloc(nwordpos*sizeof(short));
2329 memcpy(he->words, wordlist, nwordpos*sizeof(short));
2330 } else
2331 he->words = (short *)NULL;
2332 addhistnode(histtab, he->node.nam, he);
2333 if (he->node.flags & HIST_DUP) {
2334 freehistnode(&he->node);
2335 curhist--;
2338 if (start && readflags & HFILE_USE_OPTIONS) {
2339 zsfree(lasthist.text);
2340 lasthist.text = ztrdup(start);
2342 zfree(wordlist, nwordlist*sizeof(short));
2343 zfree(buf, bufsiz);
2345 fclose(in);
2346 } else if (err)
2347 zerr("can't read history file %s", fn);
2349 unlockhistfile(fn);
2352 #ifdef HAVE_FCNTL_H
2353 static int flock_fd = -1;
2355 static int
2356 flockhistfile(char *fn, int keep_trying)
2358 struct flock lck;
2359 int ctr = keep_trying ? 9 : 0;
2361 if ((flock_fd = open(unmeta(fn), O_RDWR | O_NOCTTY)) < 0)
2362 return errno == ENOENT; /* "successfully" locked missing file */
2364 lck.l_type = F_WRLCK;
2365 lck.l_whence = SEEK_SET;
2366 lck.l_start = 0;
2367 lck.l_len = 0; /* lock the whole file */
2369 while (fcntl(flock_fd, F_SETLKW, &lck) == -1) {
2370 if (--ctr < 0) {
2371 close(flock_fd);
2372 flock_fd = -1;
2373 return 0;
2375 sleep(1);
2378 return 1;
2380 #endif
2382 /**/
2383 void
2384 savehistfile(char *fn, int err, int writeflags)
2386 char *t, *tmpfile, *start = NULL;
2387 FILE *out;
2388 Histent he;
2389 zlong xcurhist = curhist - !!(histactive & HA_ACTIVE);
2390 int extended_history = isset(EXTENDEDHISTORY);
2391 int ret;
2393 if (!interact || savehistsiz <= 0 || !hist_ring
2394 || (!fn && !(fn = getsparam("HISTFILE"))))
2395 return;
2396 if (writeflags & HFILE_FAST) {
2397 he = gethistent(lasthist.next_write_ev, GETHIST_DOWNWARD);
2398 while (he && he->node.flags & HIST_OLD) {
2399 lasthist.next_write_ev = he->histnum + 1;
2400 he = down_histent(he);
2402 if (!he || !lockhistfile(fn, 0))
2403 return;
2404 if (histfile_linect > savehistsiz + savehistsiz / 5)
2405 writeflags &= ~HFILE_FAST;
2407 else {
2408 if (!lockhistfile(fn, 1))
2409 return;
2410 he = hist_ring->down;
2412 if (writeflags & HFILE_USE_OPTIONS) {
2413 if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY)
2414 || isset(SHAREHISTORY))
2415 writeflags |= HFILE_APPEND | HFILE_SKIPOLD;
2416 else
2417 histfile_linect = 0;
2418 if (isset(HISTSAVENODUPS))
2419 writeflags |= HFILE_SKIPDUPS;
2420 if (isset(SHAREHISTORY))
2421 extended_history = 1;
2423 errno = 0;
2424 if (writeflags & HFILE_APPEND) {
2425 int fd = open(unmeta(fn), O_CREAT | O_WRONLY | O_APPEND | O_NOCTTY, 0600);
2426 tmpfile = NULL;
2427 out = fd >= 0 ? fdopen(fd, "a") : NULL;
2428 } else if (!isset(HISTSAVEBYCOPY)) {
2429 int fd = open(unmeta(fn), O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600);
2430 tmpfile = NULL;
2431 out = fd >= 0 ? fdopen(fd, "w") : NULL;
2432 } else {
2433 tmpfile = bicat(unmeta(fn), ".new");
2434 if (unlink(tmpfile) < 0 && errno != ENOENT)
2435 out = NULL;
2436 else {
2437 struct stat sb;
2438 int old_exists = stat(unmeta(fn), &sb) == 0;
2439 uid_t euid = geteuid();
2441 if (old_exists
2442 #if defined HAVE_FCHMOD && defined HAVE_FCHOWN
2443 && euid
2444 #endif
2445 && sb.st_uid != euid) {
2446 free(tmpfile);
2447 tmpfile = NULL;
2448 if (err) {
2449 if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY)
2450 || isset(SHAREHISTORY))
2451 zerr("rewriting %s would change its ownership -- skipped", fn);
2452 else
2453 zerr("rewriting %s would change its ownership -- history not saved", fn);
2454 err = 0; /* Don't report a generic error below. */
2456 out = NULL;
2457 } else {
2458 int fd = open(tmpfile, O_CREAT | O_WRONLY | O_EXCL, 0600);
2459 out = fd >= 0 ? fdopen(fd, "w") : NULL;
2462 #ifdef HAVE_FCHMOD
2463 if (old_exists && out) {
2464 #ifdef HAVE_FCHOWN
2465 if (fchown(fileno(out), sb.st_uid, sb.st_gid) < 0) {} /* IGNORE FAILURE */
2466 #endif
2467 if (fchmod(fileno(out), sb.st_mode) < 0) {} /* IGNORE FAILURE */
2469 #endif
2472 if (out) {
2473 ret = 0;
2474 for (; he && he->histnum <= xcurhist; he = down_histent(he)) {
2475 if ((writeflags & HFILE_SKIPDUPS && he->node.flags & HIST_DUP)
2476 || (writeflags & HFILE_SKIPFOREIGN && he->node.flags & HIST_FOREIGN)
2477 || he->node.flags & HIST_TMPSTORE)
2478 continue;
2479 if (writeflags & HFILE_SKIPOLD) {
2480 if (he->node.flags & HIST_OLD)
2481 continue;
2482 he->node.flags |= HIST_OLD;
2483 if (writeflags & HFILE_USE_OPTIONS)
2484 lasthist.next_write_ev = he->histnum + 1;
2486 if (writeflags & HFILE_USE_OPTIONS) {
2487 lasthist.fpos = ftell(out);
2488 lasthist.stim = he->stim;
2489 histfile_linect++;
2491 t = start = he->node.nam;
2492 if (extended_history) {
2493 ret = fprintf(out, ": %ld:%ld;", (long)he->stim,
2494 he->ftim? (long)(he->ftim - he->stim) : 0L);
2495 } else if (*t == ':')
2496 ret = fputc('\\', out);
2498 for (; ret >= 0 && *t; t++) {
2499 if (*t == '\n')
2500 if ((ret = fputc('\\', out)) < 0)
2501 break;
2502 if ((ret = fputc(*t, out)) < 0)
2503 break;
2505 if (ret < 0 || (ret = fputc('\n', out)) < 0)
2506 break;
2508 if (ret >= 0 && start && writeflags & HFILE_USE_OPTIONS) {
2509 struct stat sb;
2510 if ((ret = fflush(out)) >= 0) {
2511 if (fstat(fileno(out), &sb) == 0) {
2512 lasthist.fsiz = sb.st_size;
2513 lasthist.mtim = sb.st_mtime;
2515 zsfree(lasthist.text);
2516 lasthist.text = ztrdup(start);
2519 if (fclose(out) < 0 && ret >= 0)
2520 ret = -1;
2521 if (ret >= 0) {
2522 if (tmpfile) {
2523 if (rename(tmpfile, unmeta(fn)) < 0) {
2524 zerr("can't rename %s.new to $HISTFILE", fn);
2525 ret = -1;
2526 err = 0;
2527 #ifdef HAVE_FCNTL_H
2528 } else {
2529 /* We renamed over the locked HISTFILE, so close fd.
2530 * If we do more writing, we'll get a lock then. */
2531 if (flock_fd >= 0) {
2532 close(flock_fd);
2533 flock_fd = -1;
2535 #endif
2539 if (ret >= 0 && writeflags & HFILE_SKIPOLD
2540 && !(writeflags & (HFILE_FAST | HFILE_NO_REWRITE))) {
2541 int remember_histactive = histactive;
2543 /* Zeroing histactive avoids unnecessary munging of curline. */
2544 histactive = 0;
2545 /* The NULL leaves HISTFILE alone, preserving fn's value. */
2546 pushhiststack(NULL, savehistsiz, savehistsiz, -1);
2548 hist_ignore_all_dups |= isset(HISTSAVENODUPS);
2549 readhistfile(fn, err, 0);
2550 hist_ignore_all_dups = isset(HISTIGNOREALLDUPS);
2551 if (histlinect)
2552 savehistfile(fn, err, 0);
2554 pophiststack();
2555 histactive = remember_histactive;
2558 } else
2559 ret = -1;
2561 if (ret < 0 && err) {
2562 if (tmpfile)
2563 zerr("failed to write history file %s.new: %e", fn, errno);
2564 else
2565 zerr("failed to write history file %s: %e", fn, errno);
2567 if (tmpfile)
2568 free(tmpfile);
2570 unlockhistfile(fn);
2573 static int lockhistct;
2575 /**/
2577 lockhistfile(char *fn, int keep_trying)
2579 int ct = lockhistct;
2581 if (!fn && !(fn = getsparam("HISTFILE")))
2582 return 0;
2584 #ifdef HAVE_FCNTL_H
2585 if (isset(HISTFCNTLLOCK) && flock_fd < 0 && !flockhistfile(fn, keep_trying))
2586 return 0;
2587 #endif
2589 if (!lockhistct++) {
2590 struct stat sb;
2591 int fd;
2592 char *lockfile;
2593 #ifdef HAVE_LINK
2594 char *tmpfile;
2595 #endif
2597 lockfile = bicat(unmeta(fn), ".LOCK");
2598 #ifdef HAVE_LINK
2599 if ((fd = gettempfile(fn, 0, &tmpfile)) >= 0) {
2600 FILE *out = fdopen(fd, "w");
2601 if (out) {
2602 fprintf(out, "%ld %s\n", (long)getpid(), getsparam("HOST"));
2603 fclose(out);
2604 } else
2605 close(fd);
2606 while (link(tmpfile, lockfile) < 0) {
2607 if (errno != EEXIST)
2608 zerr("failed to create hard link as lock file %s: %e",
2609 lockfile, errno);
2610 else if (!keep_trying)
2612 else if (stat(lockfile, &sb) < 0) {
2613 if (errno == ENOENT)
2614 continue;
2615 zerr("failed to stat lock file %s: %e", lockfile, errno);
2616 } else {
2617 if (time(NULL) - sb.st_mtime < 10)
2618 sleep(1);
2619 else
2620 unlink(lockfile);
2621 continue;
2623 lockhistct--;
2624 break;
2626 unlink(tmpfile);
2627 free(tmpfile);
2629 #else /* not HAVE_LINK */
2630 while ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) {
2631 if (errno != EEXIST || !keep_trying)
2632 break;
2633 if (stat(lockfile, &sb) < 0) {
2634 if (errno == ENOENT)
2635 continue;
2636 break;
2638 if (time(NULL) - sb.st_mtime < 10)
2639 sleep(1);
2640 else
2641 unlink(lockfile);
2643 if (fd < 0)
2644 lockhistct--;
2645 else {
2646 FILE *out = fdopen(fd, "w");
2647 if (out) {
2648 fprintf(out, "%ld %s\n", (long)mypid, getsparam("HOST"));
2649 fclose(out);
2650 } else
2651 close(fd);
2653 #endif /* not HAVE_LINK */
2654 free(lockfile);
2657 if (ct == lockhistct) {
2658 #ifdef HAVE_FCNTL_H
2659 if (flock_fd >= 0) {
2660 close(flock_fd);
2661 flock_fd = -1;
2663 #endif
2664 return 0;
2666 return 1;
2669 /* Unlock the history file if this corresponds to the last nested lock
2670 * request. If we don't have the file locked, just return.
2673 /**/
2674 void
2675 unlockhistfile(char *fn)
2677 if (!fn && !(fn = getsparam("HISTFILE")))
2678 return;
2679 if (--lockhistct) {
2680 if (lockhistct < 0)
2681 lockhistct = 0;
2683 else {
2684 char *lockfile;
2685 fn = unmeta(fn);
2686 lockfile = zalloc(strlen(fn) + 5 + 1);
2687 sprintf(lockfile, "%s.LOCK", fn);
2688 unlink(lockfile);
2689 free(lockfile);
2690 #ifdef HAVE_FCNTL_H
2691 if (flock_fd >= 0) {
2692 close(flock_fd);
2693 flock_fd = -1;
2695 #endif
2699 /**/
2701 histfileIsLocked(void)
2703 return lockhistct > 0;
2707 * Get the words in the current buffer. Using the lexer.
2709 * As far as I can make out, this is a gross hack based on a gross hack.
2710 * When analysing lines from within zle, we tweak the metafied line
2711 * positions (zlemetall and zlemetacs) directly in the lexer. That's
2712 * bad enough, but this function appears to be designed to be called
2713 * from outside zle, pretending to be in zle and calling out, so
2714 * we set zlemetall and zlemetacs locally and copy the current zle line,
2715 * which may not even be valid at this point.
2717 * However, I'm so confused it could simply be baking Bakewell tarts.
2720 /**/
2721 mod_export LinkList
2722 bufferwords(LinkList list, char *buf, int *index)
2724 int num = 0, cur = -1, got = 0, ne = noerrs;
2725 int owb = wb, owe = we, oadx = addedx, ozp = zleparse, onc = nocomments;
2726 int ona = noaliases, ocs = zlemetacs, oll = zlemetall;
2727 char *p, *addedspaceptr;
2729 if (!list)
2730 list = newlinklist();
2732 zleparse = 1;
2733 addedx = 0;
2734 noerrs = 1;
2735 lexsave();
2736 if (buf) {
2737 int l = strlen(buf);
2739 p = (char *) zhalloc(l + 2);
2740 memcpy(p, buf, l);
2742 * I'm sure this space is here for a reason, but it's
2743 * a pain in the neck: when we get back a string that's
2744 * not finished it's very hard to tell if a space at the
2745 * end is this one or not. We use two tricks below to
2746 * work around this.
2748 addedspaceptr = p + l;
2749 *addedspaceptr = ' ';
2750 addedspaceptr[1] = '\0';
2751 inpush(p, 0, NULL);
2752 zlemetall = strlen(p) ;
2753 zlemetacs = zlemetall + 1;
2754 nocomments = 1;
2755 } else {
2756 int ll, cs;
2757 char *linein;
2759 linein = zleentry(ZLE_CMD_GET_LINE, &ll, &cs);
2760 zlemetall = ll + 1; /* length of line plus space added below */
2761 zlemetacs = cs;
2763 if (!isfirstln && chline) {
2764 p = (char *) zhalloc(hptr - chline + ll + 2);
2765 memcpy(p, chline, hptr - chline);
2766 memcpy(p + (hptr - chline), linein, ll);
2767 addedspaceptr = p + (hptr - chline) + ll;
2768 *addedspaceptr = ' ';
2769 addedspaceptr[1] = '\0';
2770 inpush(p, 0, NULL);
2773 * advance line length and character position over
2774 * prepended string.
2776 zlemetall += hptr - chline;
2777 zlemetacs += hptr - chline;
2778 } else {
2779 p = (char *) zhalloc(ll + 2);
2780 memcpy(p, linein, ll);
2781 addedspaceptr = p + ll;
2782 *addedspaceptr = ' ';
2783 p[zlemetall] = '\0';
2784 inpush(p, 0, NULL);
2786 zsfree(linein);
2788 if (zlemetacs)
2789 zlemetacs--;
2790 strinbeg(0);
2791 noaliases = 1;
2792 do {
2793 if (incond)
2794 incond = 1 + (tok != DINBRACK && tok != INPAR &&
2795 tok != DBAR && tok != DAMPER &&
2796 tok != BANG);
2797 ctxtlex();
2798 if (tok == ENDINPUT || tok == LEXERR)
2799 break;
2800 if (tokstr && *tokstr) {
2801 untokenize((p = dupstring(tokstr)));
2802 if (ingetptr() == addedspaceptr + 1) {
2804 * Whoops, we've read past the space we added, probably
2805 * because we were expecting a terminator but when
2806 * it didn't turn up we shrugged our shoulders thinking
2807 * it might as well be a complete string anyway.
2808 * So remove the space. C.f. below for the case
2809 * where the missing terminator caused a lex error.
2810 * We use the same paranoid test.
2812 int plen = strlen(p);
2813 if (plen && p[plen-1] == ' ' &&
2814 (plen == 1 || p[plen-2] != Meta))
2815 p[plen-1] = '\0';
2817 addlinknode(list, p);
2818 num++;
2819 } else if (buf) {
2820 if (IS_REDIROP(tok) && tokfd >= 0) {
2821 char b[20];
2823 sprintf(b, "%d%s", tokfd, tokstrings[tok]);
2824 addlinknode(list, dupstring(b));
2825 num++;
2826 } else if (tok != NEWLIN) {
2827 addlinknode(list, dupstring(tokstrings[tok]));
2828 num++;
2831 if (!got && !zleparse) {
2832 got = 1;
2833 cur = num - 1;
2835 } while (tok != ENDINPUT && tok != LEXERR);
2836 if (buf && tok == LEXERR && tokstr && *tokstr) {
2837 int plen;
2838 untokenize((p = dupstring(tokstr)));
2839 plen = strlen(p);
2841 * Strip the space we added for lexing but which won't have
2842 * been swallowed by the lexer because we aborted early.
2843 * The test is paranoia.
2845 if (plen && p[plen-1] == ' ' && (plen == 1 || p[plen-2] != Meta))
2846 p[plen - 1] = '\0';
2847 addlinknode(list, p);
2848 num++;
2850 if (cur < 0 && num)
2851 cur = num - 1;
2852 noaliases = ona;
2853 strinend();
2854 inpop();
2855 errflag = 0;
2856 zleparse = ozp;
2857 nocomments = onc;
2858 noerrs = ne;
2859 lexrestore();
2860 zlemetacs = ocs;
2861 zlemetall = oll;
2862 wb = owb;
2863 we = owe;
2864 addedx = oadx;
2866 if (index)
2867 *index = cur;
2869 return list;
2872 /* Move the current history list out of the way and prepare a fresh history
2873 * list using hf for HISTFILE, hs for HISTSIZE, and shs for SAVEHIST. If
2874 * the hf value is an empty string, HISTFILE will be unset from the new
2875 * environment; if it is NULL, HISTFILE will not be changed, not even by the
2876 * pop function (this functionality is used internally to rewrite the current
2877 * history file without affecting pointers into the environment).
2880 /**/
2882 pushhiststack(char *hf, zlong hs, zlong shs, int level)
2884 struct histsave *h;
2885 int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
2887 if (histsave_stack_pos == histsave_stack_size) {
2888 histsave_stack_size += 5;
2889 histsave_stack = zrealloc(histsave_stack,
2890 histsave_stack_size * sizeof (struct histsave));
2893 if (curline_in_ring)
2894 unlinkcurline();
2896 h = &histsave_stack[histsave_stack_pos++];
2898 h->lasthist = lasthist;
2899 if (hf) {
2900 if ((h->histfile = getsparam("HISTFILE")) != NULL && *h->histfile)
2901 h->histfile = ztrdup(h->histfile);
2902 else
2903 h->histfile = "";
2904 } else
2905 h->histfile = NULL;
2906 h->histtab = histtab;
2907 h->hist_ring = hist_ring;
2908 h->curhist = curhist;
2909 h->histlinect = histlinect;
2910 h->histsiz = histsiz;
2911 h->savehistsiz = savehistsiz;
2912 h->locallevel = level;
2914 memset(&lasthist, 0, sizeof lasthist);
2915 if (hf) {
2916 if (*hf)
2917 setsparam("HISTFILE", ztrdup(hf));
2918 else
2919 unsetparam("HISTFILE");
2921 hist_ring = NULL;
2922 curhist = histlinect = 0;
2923 histsiz = hs;
2924 savehistsiz = shs;
2925 inithist(); /* sets histtab */
2927 if (curline_in_ring)
2928 linkcurline();
2930 return histsave_stack_pos;
2934 /**/
2936 pophiststack(void)
2938 struct histsave *h;
2939 int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
2941 if (histsave_stack_pos == 0)
2942 return 0;
2944 if (curline_in_ring)
2945 unlinkcurline();
2947 deletehashtable(histtab);
2948 zsfree(lasthist.text);
2950 h = &histsave_stack[--histsave_stack_pos];
2952 lasthist = h->lasthist;
2953 if (h->histfile) {
2954 if (*h->histfile)
2955 setsparam("HISTFILE", h->histfile);
2956 else
2957 unsetparam("HISTFILE");
2959 histtab = h->histtab;
2960 hist_ring = h->hist_ring;
2961 curhist = h->curhist;
2962 histlinect = h->histlinect;
2963 histsiz = h->histsiz;
2964 savehistsiz = h->savehistsiz;
2966 if (curline_in_ring)
2967 linkcurline();
2969 return histsave_stack_pos + 1;
2972 /* If pop_through > 0, pop all array items >= the 1-relative index value.
2973 * If pop_through <= 0, pop (-1)*pop_through levels off the stack.
2974 * If the (new) top of stack is from a higher locallevel, auto-pop until
2975 * it is not.
2978 /**/
2980 saveandpophiststack(int pop_through, int writeflags)
2982 if (pop_through <= 0) {
2983 pop_through += histsave_stack_pos + 1;
2984 if (pop_through <= 0)
2985 pop_through = 1;
2987 while (pop_through > 1
2988 && histsave_stack[pop_through-2].locallevel > locallevel)
2989 pop_through--;
2990 if (histsave_stack_pos < pop_through)
2991 return 0;
2992 do {
2993 if (!nohistsave)
2994 savehistfile(NULL, 1, writeflags);
2995 pophiststack();
2996 } while (histsave_stack_pos >= pop_through);
2997 return 1;