2 * hist.c - history expansion
4 * This file is part of zsh, the Z shell.
6 * Copyright (c) 1992-1997 Paul Falstad
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.
33 /* Functions to call for getting/ungetting a character and for history
37 mod_export
int (*hgetc
) _((void));
40 void (*hungetc
) _((int));
43 void (*hwaddc
) _((int));
46 void (*hwbegin
) _((int));
49 void (*hwend
) _((void));
52 void (*addtoline
) _((int));
54 /* != 0 means history substitution is turned off */
57 mod_export
int stophist
;
59 /* if != 0, we are expanding the current line */
62 mod_export
int expanding
;
64 /* these are used to modify the cursor position during expansion */
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().
82 mod_export zlong curhist
;
85 struct histent curline
;
87 /* current line count of allocated history entries */
92 /* The history lines are kept in a hash, and also doubly-linked in a ring */
97 mod_export Histent hist_ring
;
99 /* capacity of history lists */
104 /* desired history-file size (in lines) */
109 /* if = 1, we have performed history substitution on the current line *
110 * if = 2, we have used the 'p' modifier */
115 /* state of the history mechanism */
120 /* Current setting of the associated option, but sometimes also includes
121 * the setting of the HIST_SAVE_NO_DUPS option. */
124 int hist_ignore_all_dups
;
126 /* What flags (if any) we should skip when moving through the history */
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. */
140 /* Max, actual position in chwords.
141 * nwords = chwordpos/2 because we record beginning and end of words.
145 int chwordlen
, chwordpos
;
147 /* the last l for s/l/r/ history substitution */
152 /* the last r for s/l/r/ history substitution */
157 /* pointer into the history line */
160 mod_export
char *hptr
;
162 /* the current history line */
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 */
173 /* max size of histline */
178 /* default event (usually curhist-1, that is, "!!") */
182 /* Remember the last line in the history file so we can find it again. */
183 static struct histfile_stats
{
190 static struct histsave
{
191 struct histfile_stats lasthist
;
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 */
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. */
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. */
242 if (!expanding
|| lexstop
)
244 if (qbang
&& c
== bangchar
&& stophist
< 2) {
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 */
256 zleentry(ZLE_CMD_ADD_TO_LINE
, itok(c
) ? ztokens
[c
- Pound
] : c
);
266 if (!stophist
&& !(inbufflags
& INP_ALIAS
)) {
267 /* If necessary, expand history characters. */
271 errflag
= lexstop
= 1;
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 '\\' */
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);
317 while (!lexstop
&& inbufct
&& !strin
)
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.
336 getsubsargs(char *subline
, int *gbalp
, int *cflagp
)
342 ptr1
= hdynread2(del
);
345 ptr2
= hdynread2(del
);
349 } else if (!hsubl
) { /* fail silently on this */
369 /* Get the maximum no. of words for a history entry. */
373 getargc(Histent ehist
)
375 return ehist
->nwords
? ehist
->nwords
-1 : 0;
383 zerr("substitution failed");
387 /* Perform history substitution, returning the next character afterwards. */
393 int farg
, evset
= -1, larg
, argc
, cflag
= 0, bflag
= 0;
395 static int marg
= -1;
396 static zlong mev
= -1;
402 /* look, no goto's */
403 if (isfirstch
&& c
== hatchar
) {
406 /* Line begins ^foo^bar */
409 if (!(ehist
= gethist(defev
))
410 || !(sline
= getargs(ehist
, 0, getargc(ehist
))))
413 if (getsubsargs(sline
, &gbal
, &cflag
))
414 return substfailed();
417 if (subst(&sline
, hsubl
, hsubr
, gbal
))
418 return substfailed();
420 /* Line doesn't begin ^foo^bar */
436 if ((c
= ingetc()) == '{') {
444 if ((!cflag
&& inblank(c
)) || c
== '=' || c
== '(' || lexstop
) {
449 ptr
= buf
= zhalloc(buflen
= 265);
451 /* get event number */
457 if (c
== '?' || c
== '\n' || lexstop
)
461 if (ptr
== buf
+ buflen
) {
462 buf
= hrealloc(buf
, buflen
, 2 * buflen
);
468 if (c
!= '\n' && !lexstop
)
471 mev
= ev
= hconsearch(hsubl
= ztrdup(buf
), &marg
);
476 zerr("no such event: %s", buf
);
483 if (inblank(c
) || c
== ';' || c
== ':' || c
== '^' ||
484 c
== '$' || c
== '*' || c
== '%' || c
== '}' ||
485 c
== '\'' || c
== '"' || c
== '`' || lexstop
)
490 if ((idigit(buf
[0]) || buf
[0] == '-') && !idigit(c
))
494 if (ptr
== buf
+ buflen
) {
495 buf
= hrealloc(buf
, buflen
, 2 * buflen
);
499 if (c
== '#' || c
== bangchar
) {
508 if (isset(CSHJUNKIEHISTORY
))
509 ev
= addhistnum(curhist
,-1,HIST_FOREIGN
);
512 if (c
== ':' && evset
== -1)
523 } else if ((t0
= zstrtol(buf
, NULL
, 10))) {
524 ev
= (t0
< 0) ? addhistnum(curhist
,t0
,HIST_FOREIGN
) : t0
;
526 } else if ((unsigned)*buf
== bangchar
) {
527 ev
= addhistnum(curhist
,-1,HIST_FOREIGN
);
529 } else if (*buf
== '#') {
532 } else if ((ev
= hcomsearch(buf
)) == -1) {
535 zerr("event not found: %s", buf
);
543 if (!(ehist
= gethist(defev
= ev
))) {
547 /* extract the relevant arguments */
549 argc
= getargc(ehist
);
553 if (c
== '%' && marg
!= -1) {
555 ehist
= gethist(defev
= mev
);
556 argc
= getargc(ehist
);
560 zerr("Ambiguous history reference");
572 larg
= farg
= getargspec(argc
, marg
, evset
);
583 } else if (c
== '-') {
585 larg
= getargspec(argc
, marg
, evset
);
599 if (!(sline
= getargs(ehist
, farg
, larg
))) {
606 /* do the modifiers */
609 c
= (cflag
) ? ':' : ingetc();
614 if ((c
= ingetc()) == 'g') {
617 if (c
!= 's' && c
!= '&') {
618 zerr("'s' or '&' modifier expected after 'g'");
624 histdone
= HISTFLAG_DONE
| HISTFLAG_NOEXEC
;
627 if (!chabspath(&sline
)) {
629 zerr("modifier failed: a");
635 if (!chrealpath(&sline
)) {
637 zerr("modifier failed: A");
642 if (!remtpath(&sline
)) {
644 zerr("modifier failed: h");
649 if (!rembutext(&sline
)) {
651 zerr("modifier failed: e");
656 if (!remtext(&sline
)) {
658 zerr("modifier failed: r");
663 if (!remlpaths(&sline
)) {
665 zerr("modifier failed: t");
670 if (getsubsargs(sline
, &gbal
, &cflag
))
671 return -1; /* fall through */
673 if (hsubl
&& hsubr
) {
674 if (subst(&sline
, hsubl
, hsubr
, gbal
))
675 return substfailed();
678 zerr("no previous substitution");
687 int one
= noerrs
, oef
= errflag
;
690 parse_subst_string(sline
);
701 sline
= casemodify(sline
, CASMOD_LOWER
);
704 sline
= casemodify(sline
, CASMOD_UPPER
);
708 zerr("illegal modifier: %c", c
);
712 if (c
!= '}' || !bflag
)
714 if (c
!= '}' && bflag
) {
715 zerr("'}' expected");
723 * Push the expanded value onto the input stack,
724 * marking this as a history word for purposes of the alias stack.
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. */
742 /* unget a char and remove it from chline. It can only be used *
743 * to unget a character returned by hgetc. */
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('\\');
760 DPUTS(hptr
<= chline
, "BUG: hungetc attempted at buffer start");
762 DPUTS(*hptr
!= (char) c
, "BUG: wrong character in hungetc() ");
763 qbang
= (c
== bangchar
&& stophist
< 2 &&
764 hptr
> chline
&& hptr
[-1] == '\\');
769 doit
= !stophist
&& ((inbufflags
& INP_HIST
) ||
770 !(inbufflags
& INP_ALIAS
));
775 /* begin reading a string */
786 /* done reading a string */
793 DPUTS(!strin
, "BUG: strinend() called without strinbeg()");
799 /* dummy functions to use instead of hwaddc(), hwbegin(), and hwend() when
800 * they aren't needed */
812 /* these functions handle adding/removing curline to/from the hist_ring */
818 hist_ring
= curline
.up
= curline
.down
= &curline
;
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
;
831 curline
.up
->down
= curline
.down
;
832 curline
.down
->up
= curline
.up
;
833 if (hist_ring
== &curline
) {
837 hist_ring
= curline
.up
;
842 /* initialize the history mechanism */
848 isfirstln
= isfirstch
= 1;
849 errflag
= histdone
= 0;
852 else if (dohist
!= 2)
853 stophist
= (!interact
|| unset(SHINSTDIN
)) ? 2 : 0;
856 if (stophist
== 2 || (inbufflags
& INP_ALIAS
)) {
857 chline
= hptr
= NULL
;
868 chline
= hptr
= zshcalloc(hlinesz
= 64);
869 chwords
= zalloc((chwordlen
= 64) * sizeof(short));
875 addtoline
= iaddtoline
;
876 if (!isset(BANGHIST
))
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
;
887 defev
= addhistnum(curhist
, -1, HIST_FOREIGN
);
889 histactive
= HA_ACTIVE
| HA_NOINC
;
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')
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
);
914 chwords
[i
+1] = chwords
[i
] + len
;
916 pos
+= len
+ needblank
;
926 for (he
= hist_ring
; he
; he
= next
) {
927 next
= up_histent(he
);
928 if (he
->node
.flags
& HIST_DUP
)
929 freehistnode(&he
->node
);
935 addhistnum(zlong hl
, int n
, int xflags
)
937 int dir
= n
< 0? -1 : n
> 0? 1 : 0;
938 Histent he
= gethistent(hl
, dir
);
942 if (he
->histnum
!= hl
)
945 he
= movehistent(he
, n
, xflags
);
947 return dir
< 0? firsthist() - 1 : curhist
+ 1;
953 movehistent(Histent he
, int n
, int xflags
)
956 if (!(he
= up_histent(he
)))
958 if (!(he
->node
.flags
& xflags
))
962 if (!(he
= down_histent(he
)))
964 if (!(he
->node
.flags
& xflags
))
973 up_histent(Histent he
)
975 return !he
|| he
->up
== hist_ring
? NULL
: he
->up
;
980 down_histent(Histent he
)
982 return he
== hist_ring
? NULL
: he
->down
;
987 gethistent(zlong ev
, int nearmatch
)
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
) {
998 || (nearmatch
< 0 && (he
= up_histent(he
)) == NULL
))
1003 for (he
= hist_ring
; he
->histnum
> ev
; he
= he
->up
) ;
1004 if (he
->histnum
!= ev
) {
1006 || (nearmatch
> 0 && (he
= down_histent(he
)) == NULL
))
1016 putoldhistentryontop(short keep_going
)
1018 static Histent next
= NULL
;
1019 Histent he
= keep_going
? next
: hist_ring
->down
;
1021 if (isset(HISTEXPIREDUPSFIRST
) && !(he
->node
.flags
& HIST_DUP
)) {
1022 static zlong max_unique_ct
= 0;
1024 max_unique_ct
= savehistsiz
;
1026 if (max_unique_ct
-- <= 0 || he
== hist_ring
) {
1028 he
= hist_ring
->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
;
1040 he
->down
= hist_ring
->down
;
1041 hist_ring
->down
= he
->down
->up
= he
;
1048 prepnexthistent(void)
1051 int curline_in_ring
= hist_ring
== &curline
;
1053 if (curline_in_ring
)
1055 if (hist_ring
&& hist_ring
->node
.flags
& HIST_TMPSTORE
) {
1057 freehistnode(&hist_ring
->node
);
1060 if (histlinect
< histsiz
) {
1061 he
= (Histent
)zshcalloc(sizeof *he
);
1063 hist_ring
= he
->up
= he
->down
= he
;
1066 he
->down
= hist_ring
->down
;
1067 hist_ring
->down
= he
->down
->up
= he
;
1073 putoldhistentryontop(0);
1074 freehistdata(hist_ring
, 0);
1077 he
->histnum
= ++curhist
;
1078 if (curline_in_ring
)
1083 /* A helper function for hend() */
1086 should_ignore_line(Eprog prog
)
1088 if (isset(HISTIGNORESPACE
)) {
1089 if (*chline
== ' ' || aliasspaceflag
)
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
)
1104 if (isset(HISTNOSTORE
)) {
1105 char *b
= getjobtext(prog
, NULL
);
1107 if (*b
== 'b' && strncmp(b
,"builtin ",8) == 0) {
1112 if (*b
== 'h' && strncmp(b
,"history",7) == 0 && (!b
[7] || b
[7] == ' ')
1113 && (saw_builtin
|| !shfunctab
->getnode(shfunctab
,"history")))
1115 if (*b
== 'r' && (!b
[1] || b
[1] == ' ')
1116 && (saw_builtin
|| !shfunctab
->getnode(shfunctab
,"r")))
1118 if (*b
== 'f' && b
[1] == 'c' && b
[2] == ' ' && b
[3] == '-'
1119 && (saw_builtin
|| !shfunctab
->getnode(shfunctab
,"fc"))) {
1124 } while (ialpha(*b
));
1131 /* say we're done using the history mechanism */
1137 LinkList hookargs
= newlinklist();
1138 int flag
, save
= 1, hookret
, stack_pos
= histsave_stack_pos
;
1141 DPUTS(stophist
!= 2 && !(inbufflags
& INP_ALIAS
) && !chline
,
1142 "BUG: chline is NULL in hend()");
1144 if (histdone
& HISTFLAG_SETTY
)
1145 settyinfo(&shttyinfo
);
1146 if (!(histactive
& HA_NOINC
))
1148 if (histactive
& HA_NOINC
) {
1149 zfree(chline
, hlinesz
);
1150 zfree(chwords
, chwordlen
*sizeof(short));
1156 if (hist_ignore_all_dups
!= isset(HISTIGNOREALLDUPS
)
1157 && (hist_ignore_all_dups
= isset(HISTIGNOREALLDUPS
)) != 0)
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");
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;
1179 if (hptr
< chline
+ 1)
1182 if (hptr
[-1] == '\n') {
1190 else if (hookret
|| should_ignore_line(prog
))
1193 if (flag
& (HISTFLAG_DONE
| HISTFLAG_RECALL
)) {
1196 ptr
= ztrdup(chline
);
1197 if ((flag
& (HISTFLAG_DONE
| HISTFLAG_RECALL
)) == HISTFLAG_DONE
) {
1202 if (flag
& HISTFLAG_RECALL
) {
1203 zpushnode(bufstack
, ptr
);
1208 if (save
|| *chline
== ' ') {
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
);
1223 /* debugging only */
1226 DPUTS(1, "BUG: uncompleted line in history");
1229 /* get rid of pesky \n which we've already nulled out */
1230 if (chwordpos
> 1 && !chline
[chwords
[chwordpos
-2]]) {
1232 /* strip superfluous blanks, if desired */
1233 if (isset(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.
1245 newflags
|= he
->node
.flags
& HIST_OLD
; /* Avoid re-saving */
1246 freehistdata(he
, 0);
1247 curline
.histnum
= curhist
;
1249 he
= prepnexthistent();
1251 he
->node
.nam
= ztrdup(chline
);
1252 he
->stim
= time(NULL
);
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));
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
)
1277 return !(flag
& HISTFLAG_NOEXEC
|| errflag
);
1280 /* Gives current expansion word if not last word before chwordpos. */
1289 ihwbegin(int offset
)
1294 chwordpos
--; /* make sure we're on a word start, not end */
1295 /* If we're expanding an alias, we should overwrite the expansion
1298 if ((inbufflags
& INP_ALIAS
) && !(inbufflags
& INP_HIST
))
1299 hwgetword
= chwordpos
;
1302 chwords
[chwordpos
++] = hptr
- chline
+ offset
;
1305 /* add a word to the history List */
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
,
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];
1329 /* scrub that last word, it doesn't exist */
1335 /* Go back to immediately after the last word, skipping space. */
1341 if (!(chwordpos
%2) && chwordpos
)
1342 hptr
= chline
+ chwords
[chwordpos
-1];
1345 /* Get the start and end point of the current history word */
1349 hwget(char **startptr
)
1351 int pos
= hwgetword
> -1 ? hwgetword
: chwordpos
- 2;
1354 /* debugging only */
1355 if (hwgetword
== -1 && !chwordpos
) {
1356 /* no words available */
1357 DPUTS(1, "BUG: hwget() called with no words");
1361 else if (hwgetword
== -1 && chwordpos
%2) {
1362 DPUTS(1, "BUG: hwget() called in middle of word");
1368 *startptr
= chline
+ chwords
[pos
];
1369 chline
[chwords
[++pos
]] = '\0';
1372 /* Replace the current history word with rep, if different */
1381 if (!strcmp(rep
, start
))
1385 chwordpos
= (hwgetword
> -1) ? hwgetword
: chwordpos
- 2;
1393 /* Get the entire current line, deleting it in the history. */
1399 /* Currently only used by pushlineoredit().
1400 * It's necessary to prevent that from getting too pally with
1405 if (!chline
|| hptr
== chline
)
1408 ret
= dupstring(chline
);
1418 /* get an argument specification */
1422 getargspec(int argc
, int marg
, int evset
)
1426 if ((c
= ingetc()) == '0')
1431 ret
= ret
* 10 + c
- '0';
1435 } else if (c
== '^')
1439 else if (c
== '%') {
1442 zerr("Ambiguous history reference");
1447 zerr("%% with no previous word matched");
1456 /* do ?foo? search */
1460 hconsearch(char *str
, int *marg
)
1466 for (he
= up_histent(hist_ring
); he
; he
= up_histent(he
)) {
1467 if (he
->node
.flags
& HIST_FOREIGN
)
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
)
1480 /* do !foo search */
1484 hcomsearch(char *str
)
1487 int len
= strlen(str
);
1489 for (he
= up_histent(hist_ring
); he
; he
= up_histent(he
)) {
1490 if (he
->node
.flags
& HIST_FOREIGN
)
1492 if (strncmp(he
->node
.nam
, str
, len
) == 0)
1498 /* various utilities for : modifiers */
1502 chabspath(char **junkptr
)
1504 char *current
, *dest
;
1509 if (**junkptr
!= '/') {
1510 *junkptr
= zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP
), "/", *junkptr
);
1516 #ifdef HAVE_SUPERROOT
1517 while (*current
== '/' && current
[1] == '.' && current
[2] == '.' &&
1518 (!current
[3] || current
[3] == '/')) {
1527 if (*current
== '/') {
1529 if (current
== *junkptr
&& current
[1] == '/')
1530 *dest
++ = *current
++;
1532 *dest
++ = *current
++;
1533 while (*current
== '/')
1535 } else if (!*current
) {
1536 while (dest
> *junkptr
+ 1 && dest
[-1] == '/')
1540 } else if (current
[0] == '.' && current
[1] == '.' &&
1541 (!current
[2] || current
[2] == '/')) {
1542 if (current
== *junkptr
|| dest
== *junkptr
) {
1546 } else if (dest
> *junkptr
+ 2 &&
1547 !strncmp(dest
- 3, "../", 3)) {
1551 } else if (dest
> *junkptr
+ 1) {
1554 dest
> *junkptr
+ 1 && dest
[-1] != '/';
1556 if (dest
[-1] != '/')
1559 if (*current
== '/')
1561 } else if (dest
== *junkptr
+ 1) {
1562 /* This might break with Cygwin's leading double slashes? */
1567 } else if (current
[0] == '.' && (current
[1] == '/' || !current
[1])) {
1568 while (*++current
== '/');
1570 while (*current
!= '/' && *current
!= '\0')
1571 if ((*dest
++ = *current
++) == Meta
)
1572 *dest
++ = *current
++;
1580 chrealpath(char **junkptr
)
1583 #ifdef HAVE_CANONICALIZE_FILE_NAME
1584 char *lastpos
, *nonreal
, *real
;
1586 # ifdef HAVE_REALPATH
1587 char *lastpos
, *nonreal
, real
[PATH_MAX
];
1594 /* Notice that this means ..'s are applied before symlinks are resolved! */
1595 if (!chabspath(junkptr
))
1598 #if !defined(HAVE_REALPATH) && !defined(HAVE_CANONICALIZE_FILE_NAME)
1602 * Notice that this means you cannot pass relative paths into this
1605 if (**junkptr
!= '/')
1608 unmetafy(*junkptr
, NULL
);
1610 lastpos
= strend(*junkptr
);
1611 nonreal
= lastpos
+ 1;
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
))
1623 realpath(*junkptr
, real
)
1626 if (errno
== EINVAL
|| errno
== ELOOP
||
1627 errno
== ENAMETOOLONG
|| errno
== ENOMEM
)
1630 if (nonreal
== *junkptr
) {
1635 while (*nonreal
!= '/' && nonreal
>= *junkptr
)
1641 while (str
<= lastpos
) {
1647 *junkptr
= metafy(bicat(real
, nonreal
), -1, META_HEAPDUP
);
1648 #ifdef HAVE_CANONICALIZE_FILE_NAME
1658 remtpath(char **junkptr
)
1660 char *str
= strend(*junkptr
);
1662 /* ignore trailing slashes */
1663 while (str
>= *junkptr
&& IS_DIRSEP(*str
))
1666 while (str
>= *junkptr
&& !IS_DIRSEP(*str
))
1668 if (str
< *junkptr
) {
1669 if (IS_DIRSEP(**junkptr
))
1670 *junkptr
= dupstring ("/");
1672 *junkptr
= dupstring (".");
1676 /* repeated slashes are considered like a single slash */
1677 while (str
> *junkptr
&& IS_DIRSEP(str
[-1]))
1679 /* never erase the root slash */
1680 if (str
== *junkptr
) {
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]))
1694 remtext(char **junkptr
)
1698 for (str
= strend(*junkptr
); str
>= *junkptr
&& !IS_DIRSEP(*str
); --str
)
1708 rembutext(char **junkptr
)
1712 for (str
= strend(*junkptr
); str
>= *junkptr
&& !IS_DIRSEP(*str
); --str
)
1714 *junkptr
= dupstring(str
+ 1); /* .xx or xx? */
1718 *junkptr
= dupstring ("");
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
))
1734 for (; str
>= *junkptr
; --str
)
1735 if (IS_DIRSEP(*str
)) {
1737 *junkptr
= dupstring(str
+ 1);
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.
1751 casemodify(char *str
, int how
)
1753 char *str2
= zhalloc(2 * strlen(str
) + 1);
1757 #ifdef MULTIBYTE_SUPPORT
1758 if (isset(MULTIBYTE
)) {
1759 VARARR(char, mbstr
, MB_CUR_MAX
);
1763 memset(&ps
, 0, sizeof(ps
));
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
1775 /* not alphanumeric */
1795 default: /* shuts up compiler */
1796 if (IS_COMBINING(wc
))
1800 else if (nextupper
) {
1806 } else if (iswupper(wc
)) {
1812 if (mod
&& (len2
= wcrtomb(mbstr
, wc
, &ps
)) > 0) {
1815 for (mbptr
= mbstr
; mbptr
< mbstr
+ len2
; mbptr
++) {
1816 if (imeta(STOUC(*mbptr
))) {
1818 *ptr2
++ = *mbptr
^ 32;
1850 default: /* shuts up compiler */
1853 else if (nextupper
) {
1857 } else if (isupper(c
))
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.
1882 subst(char **strptr
, char *in
, char *out
, int gbal
)
1884 char *str
= *strptr
, *substcut
, *sptr
;
1885 int off
, inlen
, outlen
;
1890 if (isset(HISTSUBSTPATTERN
)) {
1891 int fl
= SUB_LONG
|SUB_REST
|SUB_RETFAIL
;
1895 if (*in
== '#' || *in
== Pound
) {
1896 /* anchor at head, flag needed if SUB_END is also set */
1901 /* anchor at tail */
1906 /* no anchor, substring match */
1911 if (parse_subst_string(in
) || errflag
)
1913 if (parse_subst_string(out
) || errflag
)
1916 if (getmatch(strptr
, in
, fl
, 1, out
))
1919 if ((substcut
= (char *)strstr(str
, in
))) {
1921 sptr
= convamps(out
, in
, inlen
);
1922 outlen
= strlen(sptr
);
1926 off
= substcut
- *strptr
+ outlen
;
1928 *strptr
= zhtricat(*strptr
, sptr
, substcut
);
1929 str
= (char *)*strptr
+ off
;
1930 } while (gbal
&& (substcut
= (char *)strstr(str
, in
)));
1941 convamps(char *out
, char *in
, int inlen
)
1943 char *ptr
, *ret
, *pp
;
1946 for (ptr
= out
, slen
= 0; *ptr
; ptr
++, slen
++)
1949 else if (*ptr
== '&')
1950 slen
+= inlen
- 1, sdup
= 1;
1953 ret
= pp
= (char *) zhalloc(slen
+ 1);
1954 for (ptr
= out
; *ptr
; ptr
++)
1957 else if (*ptr
== '&') {
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
;
1979 quietgethist(int ev
)
1981 return gethistent(ev
, GETHIST_EXACT
);
1990 ret
= quietgethist(ev
);
1993 zerr("no such event: %d", ev
);
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 */
2008 zerr("no such word in event");
2012 pos1
= words
[2*arg1
];
2013 return dupstrpfx(elist
->node
.nam
+ pos1
, words
[2*arg2
+1] - pos1
);
2020 char *ptr
, *rptr
, **str
= (char **)tr
;
2024 for (ptr
= *str
; *ptr
; ptr
++, len
++)
2031 } else if (inblank(*ptr
) && !inquotes
&& ptr
[-1] != '\\')
2034 *str
= rptr
= (char *) zhalloc(len
);
2046 } else if (inblank(*ptr
) && !inquotes
&& ptr
[-1] != '\\') {
2060 quotebreak(char **tr
)
2062 char *ptr
, *rptr
, **str
= (char **)tr
;
2065 for (ptr
= *str
; *ptr
; ptr
++, len
++)
2068 else if (inblank(*ptr
))
2071 *str
= rptr
= (char *) zhalloc(len
);
2080 } else if (inblank(*ptr
)) {
2091 /* read an arbitrary amount of data into a buffer until stop is found */
2097 int bsiz
= 256, ct
= 0, c
;
2098 char *buf
= (char *)zalloc(bsiz
), *ptr
;
2101 while ((c
= ingetc()) != stop
&& c
!= '\n' && !lexstop
) {
2106 buf
= realloc(buf
, bsiz
*= 2);
2113 zerr("delimiter expected");
2125 int bsiz
= 256, ct
= 0, c
;
2126 char *buf
= (char *)zalloc(bsiz
), *ptr
;
2129 while ((c
= ingetc()) != stop
&& c
!= '\n' && !lexstop
) {
2134 buf
= realloc(buf
, bsiz
*= 2);
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
);
2168 readhistline(int start
, char **bufp
, int *bufsiz
, FILE *in
)
2171 if (fgets(buf
+ start
, *bufsiz
- start
, in
)) {
2172 int len
= start
+ strlen(buf
+ start
);
2175 if (buf
[len
- 1] != '\n') {
2177 if (len
< (*bufsiz
) - 1)
2179 *bufp
= zrealloc(buf
, 2 * (*bufsiz
));
2180 *bufsiz
= 2 * (*bufsiz
);
2181 return readhistline(len
, bufp
, bufsiz
, in
);
2185 buf
[len
- 1] = '\0';
2186 if (len
> 1 && buf
[len
- 2] == '\\') {
2187 buf
[--len
- 1] = '\n';
2189 return readhistline(len
, bufp
, bufsiz
, in
);
2199 readhistfile(char *fn
, int err
, int readflags
)
2201 char *buf
, *start
= NULL
;
2204 time_t stim
, ftim
, tim
= time(NULL
);
2208 int nwordpos
, nwordlist
, bufsiz
;
2209 int searching
, newflags
, l
;
2211 if (!fn
&& !(fn
= getsparam("HISTFILE")))
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))
2218 lasthist
.fsiz
= sb
.st_size
;
2219 lasthist
.mtim
= sb
.st_mtime
;
2221 else if (!lockhistfile(fn
, 1))
2223 if ((in
= fopen(unmeta(fn
), "r"))) {
2225 wordlist
= (short *)zalloc(nwordlist
*sizeof(short));
2227 buf
= zalloc(bufsiz
);
2229 if (readflags
& HFILE_FAST
&& lasthist
.text
) {
2230 if (lasthist
.fpos
< lasthist
.fsiz
) {
2231 fseek(in
, lasthist
.fpos
, 0);
2235 histfile_linect
= 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
))) {
2251 zerr("corrupt history file %s", fn
);
2256 stim
= zstrtol(pt
, NULL
, 0);
2257 for (; *pt
!= ':' && *pt
; pt
++);
2260 ftim
= zstrtol(pt
, NULL
, 0);
2261 for (; *pt
!= ';' && *pt
; pt
++);
2267 if (*pt
== '\\' && pt
[1] == ':')
2273 if (searching
> 0) {
2274 if (stim
== lasthist
.stim
2275 && histstrcmp(pt
, lasthist
.text
) == 0)
2279 histfile_linect
= 0;
2284 else if (stim
< lasthist
.stim
) {
2291 if (readflags
& HFILE_USE_OPTIONS
) {
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
;
2307 /* Divide up the words. We don't know how it lexes,
2308 so just look for white-space.
2313 while (inblank(*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
))
2322 wordlist
[nwordpos
++] = pt
- start
;
2326 he
->nwords
= nwordpos
/2;
2328 he
->words
= (short *)zalloc(nwordpos
*sizeof(short));
2329 memcpy(he
->words
, wordlist
, nwordpos
*sizeof(short));
2331 he
->words
= (short *)NULL
;
2332 addhistnode(histtab
, he
->node
.nam
, he
);
2333 if (he
->node
.flags
& HIST_DUP
) {
2334 freehistnode(&he
->node
);
2338 if (start
&& readflags
& HFILE_USE_OPTIONS
) {
2339 zsfree(lasthist
.text
);
2340 lasthist
.text
= ztrdup(start
);
2342 zfree(wordlist
, nwordlist
*sizeof(short));
2347 zerr("can't read history file %s", fn
);
2353 static int flock_fd
= -1;
2356 flockhistfile(char *fn
, int keep_trying
)
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
;
2367 lck
.l_len
= 0; /* lock the whole file */
2369 while (fcntl(flock_fd
, F_SETLKW
, &lck
) == -1) {
2384 savehistfile(char *fn
, int err
, int writeflags
)
2386 char *t
, *tmpfile
, *start
= NULL
;
2389 zlong xcurhist
= curhist
- !!(histactive
& HA_ACTIVE
);
2390 int extended_history
= isset(EXTENDEDHISTORY
);
2393 if (!interact
|| savehistsiz
<= 0 || !hist_ring
2394 || (!fn
&& !(fn
= getsparam("HISTFILE"))))
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))
2404 if (histfile_linect
> savehistsiz
+ savehistsiz
/ 5)
2405 writeflags
&= ~HFILE_FAST
;
2408 if (!lockhistfile(fn
, 1))
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
;
2417 histfile_linect
= 0;
2418 if (isset(HISTSAVENODUPS
))
2419 writeflags
|= HFILE_SKIPDUPS
;
2420 if (isset(SHAREHISTORY
))
2421 extended_history
= 1;
2424 if (writeflags
& HFILE_APPEND
) {
2425 int fd
= open(unmeta(fn
), O_CREAT
| O_WRONLY
| O_APPEND
| O_NOCTTY
, 0600);
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);
2431 out
= fd
>= 0 ? fdopen(fd
, "w") : NULL
;
2433 tmpfile
= bicat(unmeta(fn
), ".new");
2434 if (unlink(tmpfile
) < 0 && errno
!= ENOENT
)
2438 int old_exists
= stat(unmeta(fn
), &sb
) == 0;
2439 uid_t euid
= geteuid();
2442 #if defined HAVE_FCHMOD && defined HAVE_FCHOWN
2445 && sb
.st_uid
!= euid
) {
2449 if (isset(APPENDHISTORY
) || isset(INCAPPENDHISTORY
)
2450 || isset(SHAREHISTORY
))
2451 zerr("rewriting %s would change its ownership -- skipped", fn
);
2453 zerr("rewriting %s would change its ownership -- history not saved", fn
);
2454 err
= 0; /* Don't report a generic error below. */
2458 int fd
= open(tmpfile
, O_CREAT
| O_WRONLY
| O_EXCL
, 0600);
2459 out
= fd
>= 0 ? fdopen(fd
, "w") : NULL
;
2463 if (old_exists
&& out
) {
2465 if (fchown(fileno(out
), sb
.st_uid
, sb
.st_gid
) < 0) {} /* IGNORE FAILURE */
2467 if (fchmod(fileno(out
), sb
.st_mode
) < 0) {} /* IGNORE FAILURE */
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
)
2479 if (writeflags
& HFILE_SKIPOLD
) {
2480 if (he
->node
.flags
& HIST_OLD
)
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
;
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
++) {
2500 if ((ret
= fputc('\\', out
)) < 0)
2502 if ((ret
= fputc(*t
, out
)) < 0)
2505 if (ret
< 0 || (ret
= fputc('\n', out
)) < 0)
2508 if (ret
>= 0 && start
&& writeflags
& HFILE_USE_OPTIONS
) {
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)
2523 if (rename(tmpfile
, unmeta(fn
)) < 0) {
2524 zerr("can't rename %s.new to $HISTFILE", fn
);
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) {
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. */
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
);
2552 savehistfile(fn
, err
, 0);
2555 histactive
= remember_histactive
;
2561 if (ret
< 0 && err
) {
2563 zerr("failed to write history file %s.new: %e", fn
, errno
);
2565 zerr("failed to write history file %s: %e", fn
, errno
);
2573 static int lockhistct
;
2577 lockhistfile(char *fn
, int keep_trying
)
2579 int ct
= lockhistct
;
2581 if (!fn
&& !(fn
= getsparam("HISTFILE")))
2585 if (isset(HISTFCNTLLOCK
) && flock_fd
< 0 && !flockhistfile(fn
, keep_trying
))
2589 if (!lockhistct
++) {
2597 lockfile
= bicat(unmeta(fn
), ".LOCK");
2599 if ((fd
= gettempfile(fn
, 0, &tmpfile
)) >= 0) {
2600 FILE *out
= fdopen(fd
, "w");
2602 fprintf(out
, "%ld %s\n", (long)getpid(), getsparam("HOST"));
2606 while (link(tmpfile
, lockfile
) < 0) {
2607 if (errno
!= EEXIST
)
2608 zerr("failed to create hard link as lock file %s: %e",
2610 else if (!keep_trying
)
2612 else if (stat(lockfile
, &sb
) < 0) {
2613 if (errno
== ENOENT
)
2615 zerr("failed to stat lock file %s: %e", lockfile
, errno
);
2617 if (time(NULL
) - sb
.st_mtime
< 10)
2629 #else /* not HAVE_LINK */
2630 while ((fd
= open(lockfile
, O_WRONLY
|O_CREAT
|O_EXCL
, 0644)) < 0) {
2631 if (errno
!= EEXIST
|| !keep_trying
)
2633 if (stat(lockfile
, &sb
) < 0) {
2634 if (errno
== ENOENT
)
2638 if (time(NULL
) - sb
.st_mtime
< 10)
2646 FILE *out
= fdopen(fd
, "w");
2648 fprintf(out
, "%ld %s\n", (long)mypid
, getsparam("HOST"));
2653 #endif /* not HAVE_LINK */
2657 if (ct
== lockhistct
) {
2659 if (flock_fd
>= 0) {
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.
2675 unlockhistfile(char *fn
)
2677 if (!fn
&& !(fn
= getsparam("HISTFILE")))
2686 lockfile
= zalloc(strlen(fn
) + 5 + 1);
2687 sprintf(lockfile
, "%s.LOCK", fn
);
2691 if (flock_fd
>= 0) {
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.
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
;
2730 list
= newlinklist();
2737 int l
= strlen(buf
);
2739 p
= (char *) zhalloc(l
+ 2);
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
2748 addedspaceptr
= p
+ l
;
2749 *addedspaceptr
= ' ';
2750 addedspaceptr
[1] = '\0';
2752 zlemetall
= strlen(p
) ;
2753 zlemetacs
= zlemetall
+ 1;
2759 linein
= zleentry(ZLE_CMD_GET_LINE
, &ll
, &cs
);
2760 zlemetall
= ll
+ 1; /* length of line plus space added below */
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';
2773 * advance line length and character position over
2776 zlemetall
+= hptr
- chline
;
2777 zlemetacs
+= hptr
- chline
;
2779 p
= (char *) zhalloc(ll
+ 2);
2780 memcpy(p
, linein
, ll
);
2781 addedspaceptr
= p
+ ll
;
2782 *addedspaceptr
= ' ';
2783 p
[zlemetall
] = '\0';
2794 incond
= 1 + (tok
!= DINBRACK
&& tok
!= INPAR
&&
2795 tok
!= DBAR
&& tok
!= DAMPER
&&
2798 if (tok
== ENDINPUT
|| tok
== LEXERR
)
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
))
2817 addlinknode(list
, p
);
2820 if (IS_REDIROP(tok
) && tokfd
>= 0) {
2823 sprintf(b
, "%d%s", tokfd
, tokstrings
[tok
]);
2824 addlinknode(list
, dupstring(b
));
2826 } else if (tok
!= NEWLIN
) {
2827 addlinknode(list
, dupstring(tokstrings
[tok
]));
2831 if (!got
&& !zleparse
) {
2835 } while (tok
!= ENDINPUT
&& tok
!= LEXERR
);
2836 if (buf
&& tok
== LEXERR
&& tokstr
&& *tokstr
) {
2838 untokenize((p
= dupstring(tokstr
)));
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
))
2847 addlinknode(list
, p
);
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).
2882 pushhiststack(char *hf
, zlong hs
, zlong shs
, int level
)
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
)
2896 h
= &histsave_stack
[histsave_stack_pos
++];
2898 h
->lasthist
= lasthist
;
2900 if ((h
->histfile
= getsparam("HISTFILE")) != NULL
&& *h
->histfile
)
2901 h
->histfile
= ztrdup(h
->histfile
);
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
);
2917 setsparam("HISTFILE", ztrdup(hf
));
2919 unsetparam("HISTFILE");
2922 curhist
= histlinect
= 0;
2925 inithist(); /* sets histtab */
2927 if (curline_in_ring
)
2930 return histsave_stack_pos
;
2939 int curline_in_ring
= (histactive
& HA_ACTIVE
) && hist_ring
== &curline
;
2941 if (histsave_stack_pos
== 0)
2944 if (curline_in_ring
)
2947 deletehashtable(histtab
);
2948 zsfree(lasthist
.text
);
2950 h
= &histsave_stack
[--histsave_stack_pos
];
2952 lasthist
= h
->lasthist
;
2955 setsparam("HISTFILE", h
->histfile
);
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
)
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
2980 saveandpophiststack(int pop_through
, int writeflags
)
2982 if (pop_through
<= 0) {
2983 pop_through
+= histsave_stack_pos
+ 1;
2984 if (pop_through
<= 0)
2987 while (pop_through
> 1
2988 && histsave_stack
[pop_through
-2].locallevel
> locallevel
)
2990 if (histsave_stack_pos
< pop_through
)
2994 savehistfile(NULL
, 1, writeflags
);
2996 } while (histsave_stack_pos
>= pop_through
);