summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Doc/Zsh/zle.yo8
-rw-r--r--Src/Modules/parameter.c6
-rw-r--r--Src/Zle/iwidgets.list1
-rw-r--r--Src/Zle/zle_misc.c451
-rw-r--r--Src/hist.c1011
6 files changed, 1053 insertions, 431 deletions
diff --git a/ChangeLog b/ChangeLog
index 7d6c1b130..7a9d88d1e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2000-04-12 Sven Wischnowsky <wischnow@informatik.hu-berlin.de>
+
+ * 10685: Doc/Zsh/zle.yo, Src/hist.c, Src/Modules/parameter.c,
+ Src/Zle/iwidgets.list, Src/Zle/zle_misc.c: new widget
+ copy-prev-shell-word, like copy-prev-word but uses shell parsing
+ to find word
+
2000-04-11 Clint Adams <schizo@debian.org>
* 10680: Completion/User/_diff_options: spelling corrections.
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 27b149d0b..0bc857cfb 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -637,7 +637,13 @@ Copy the area from the cursor to the mark to the kill buffer.
)
tindex(copy-prev-word)
item(tt(copy-prev-word) (ESC-^_) (unbound) (unbound))(
-Duplicate the word behind the cursor.
+Duplicate the word to the left of the cursor.
+)
+tindex(copy-prev-shell-word)
+item(tt(copy-prev-shell-word) (ESC-^_) (unbound) (unbound))(
+Like tt(copy-prev-word), but the word is found by using shell parsing,
+whereas tt(copy-prev-word) looks for blanks. This makes a difference
+when the word is quoted and contains spaces.
)
tindex(vi-delete)
item(tt(vi-delete) (unbound) (d) (unbound))(
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 6bfe43479..bc8918e0f 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -1093,11 +1093,15 @@ static char **
histwgetfn(Param pm)
{
char **ret, **p, *h, *e, sav;
- LinkList l = newlinklist();
+ LinkList l = newlinklist(), ll;
LinkNode n;
int i = addhistnum(curhist, -1, HIST_FOREIGN), iw;
Histent he = quietgethistent(i, GETHIST_UPWARD);
+ ll = bufferwords(NULL);
+ for (n = firstnode(ll); n; incnode(n))
+ pushnode(l, getdata(n));
+
while (he) {
for (iw = he->nwords - 1; iw >= 0; iw--) {
h = he->text + he->words[iw * 2];
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 33d36a18c..da5bcc531 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -28,6 +28,7 @@
"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
"copy-prev-word", copyprevword, ZLE_KEEPSUFFIX
+"copy-prev-shell-word", copyprevshellword, ZLE_KEEPSUFFIX
"copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX
"delete-char", deletechar, ZLE_KEEPSUFFIX
"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 42953852f..a6adcc4fd 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -42,7 +42,7 @@ doinsert(char *str)
int neg = zmult < 0; /* insert *after* the cursor? */
int m = neg ? -zmult : zmult; /* number of copies to insert */
- iremovesuffix(c1);
+ iremovesuffix(c1, 0);
invalidatelist();
if(insmode)
@@ -57,8 +57,8 @@ doinsert(char *str)
}
/**/
-void
-selfinsert(void)
+int
+selfinsert(char **args)
{
char s[3], *p = s;
@@ -69,56 +69,61 @@ selfinsert(void)
*p++ = c;
*p = 0;
doinsert(s);
+ return 0;
}
/**/
-void
-selfinsertunmeta(void)
+int
+selfinsertunmeta(char **args)
{
c &= 0x7f;
if (c == '\r')
c = '\n';
- selfinsert();
+ return selfinsert(args);
}
/**/
-void
-deletechar(void)
+int
+deletechar(char **args)
{
if (zmult < 0) {
+ int ret;
zmult = -zmult;
- backwarddeletechar();
+ ret = backwarddeletechar(args);
zmult = -zmult;
- return;
+ return ret;
}
if (cs + zmult <= ll) {
cs += zmult;
backdel(zmult);
- } else
- feep();
+ return 0;
+ }
+ return 1;
}
/**/
-void
-backwarddeletechar(void)
+int
+backwarddeletechar(char **args)
{
if (zmult < 0) {
+ int ret;
zmult = -zmult;
- deletechar();
+ ret = deletechar(args);
zmult = -zmult;
- return;
+ return ret;
}
backdel(zmult > cs ? cs : zmult);
+ return 0;
}
/**/
-void
-killwholeline(void)
+int
+killwholeline(char **args)
{
int i, fg, n = zmult;
if (n < 0)
- return;
+ return 1;
while (n--) {
if ((fg = (cs && cs == ll)))
cs--;
@@ -127,27 +132,32 @@ killwholeline(void)
for (i = cs; i != ll && line[i] != '\n'; i++);
forekill(i - cs + (i != ll), fg);
}
+ clearlist = 1;
+ return 0;
}
/**/
-void
-killbuffer(void)
+int
+killbuffer(char **args)
{
cs = 0;
forekill(ll, 0);
+ clearlist = 1;
+ return 0;
}
/**/
-void
-backwardkillline(void)
+int
+backwardkillline(char **args)
{
int i = 0, n = zmult;
if (n < 0) {
+ int ret;
zmult = -n;
- killline();
+ ret = killline(args);
zmult = n;
- return;
+ return ret;
}
while (n--) {
if (cs && line[cs - 1] == '\n')
@@ -157,11 +167,13 @@ backwardkillline(void)
cs--, i++;
}
forekill(i, 1);
+ clearlist = 1;
+ return 0;
}
/**/
-void
-gosmacstransposechars(void)
+int
+gosmacstransposechars(char **args)
{
int cc;
@@ -169,19 +181,19 @@ gosmacstransposechars(void)
if (cs == ll || line[cs] == '\n' ||
((cs + 1 == ll || line[cs + 1] == '\n') &&
(!cs || line[cs - 1] == '\n'))) {
- feep();
- return;
+ return 1;
}
cs += (cs == 0 || line[cs - 1] == '\n') ? 2 : 1;
}
cc = line[cs - 2];
line[cs - 2] = line[cs - 1];
line[cs - 1] = cc;
+ return 0;
}
/**/
-void
-transposechars(void)
+int
+transposechars(char **args)
{
int cc, ct;
int n = zmult;
@@ -191,10 +203,8 @@ transposechars(void)
n = -n;
while (n--) {
if (!(ct = cs) || line[cs - 1] == '\n') {
- if (ll == cs || line[cs] == '\n') {
- feep();
- return;
- }
+ if (ll == cs || line[cs] == '\n')
+ return 1;
if (!neg)
cs++;
ct++;
@@ -211,29 +221,28 @@ transposechars(void)
}
if (ct == ll || line[ct] == '\n')
ct--;
- if (ct < 1 || line[ct - 1] == '\n') {
- feep();
- return;
- }
+ if (ct < 1 || line[ct - 1] == '\n')
+ return 1;
cc = line[ct - 1];
line[ct - 1] = line[ct];
line[ct] = cc;
}
+ return 0;
}
/**/
-void
-poundinsert(void)
+int
+poundinsert(char **args)
{
cs = 0;
- vifirstnonblank();
+ vifirstnonblank(zlenoargs);
if (line[cs] != '#') {
spaceinline(1);
line[cs] = '#';
cs = findeol();
while(cs != ll) {
cs++;
- vifirstnonblank();
+ vifirstnonblank(zlenoargs);
spaceinline(1);
line[cs] = '#';
cs = findeol();
@@ -243,42 +252,46 @@ poundinsert(void)
cs = findeol();
while(cs != ll) {
cs++;
- vifirstnonblank();
+ vifirstnonblank(zlenoargs);
if(line[cs] == '#')
foredel(1);
cs = findeol();
}
}
done = 1;
+ return 0;
}
/**/
-void
-acceptline(void)
+int
+acceptline(char **args)
{
done = 1;
+ return 0;
}
/**/
-void
-acceptandhold(void)
+int
+acceptandhold(char **args)
{
- pushnode(bufstack, metafy((char *)line, ll, META_DUP));
+ zpushnode(bufstack, metafy((char *)line, ll, META_DUP));
stackcs = cs;
done = 1;
+ return 0;
}
/**/
-void
-killline(void)
+int
+killline(char **args)
{
int i = 0, n = zmult;
if (n < 0) {
+ int ret;
zmult = -n;
- backwardkillline();
+ ret = backwardkillline(args);
zmult = n;
- return;
+ return ret;
}
while (n--) {
if (line[cs] == '\n')
@@ -288,11 +301,13 @@ killline(void)
cs++, i++;
}
backkill(i, 0);
+ clearlist = 1;
+ return 0;
}
/**/
-void
-killregion(void)
+int
+killregion(char **args)
{
if (mark > ll)
mark = ll;
@@ -300,11 +315,12 @@ killregion(void)
forekill(mark - cs, 0);
else
backkill(cs - mark, 1);
+ return 0;
}
/**/
-void
-copyregionaskill(void)
+int
+copyregionaskill(char **args)
{
if (mark > ll)
mark = ll;
@@ -312,25 +328,24 @@ copyregionaskill(void)
cut(cs, mark - cs, 0);
else
cut(mark, cs - mark, 1);
+ return 0;
}
static int kct, yankb, yanke;
/**/
-void
-yank(void)
+int
+yank(char **args)
{
Cutbuffer buf = &cutbuf;
int n = zmult;
if (n < 0)
- return;
+ return 1;
if (zmod.flags & MOD_VIBUF)
buf = &vibuf[zmod.vibuf];
- if (!buf->buf) {
- feep();
- return;
- }
+ if (!buf->buf)
+ return 1;
mark = cs;
yankb = cs;
while (n--) {
@@ -340,18 +355,17 @@ yank(void)
cs += buf->len;
yanke = cs;
}
+ return 0;
}
/**/
-void
-yankpop(void)
+int
+yankpop(char **args)
{
int cc;
- if (!(lastcmd & ZLE_YANK) || !kring[kct].buf) {
- feep();
- return;
- }
+ if (!(lastcmd & ZLE_YANK) || !kring[kct].buf)
+ return 1;
cs = yankb;
foredel(yanke - yankb);
cc = kring[kct].len;
@@ -360,17 +374,20 @@ yankpop(void)
cs += cc;
yanke = cs;
kct = (kct + KRINGCT - 1) % KRINGCT;
+ return 0;
}
/**/
-void
-overwritemode(void)
+int
+overwritemode(char **args)
{
insmode ^= 1;
+ return 0;
}
+
/**/
-void
-whatcursorposition(void)
+int
+whatcursorposition(char **args)
{
char msg[100];
char *s = msg;
@@ -404,18 +421,19 @@ whatcursorposition(void)
sprintf(s, " point %d of %d(%d%%) column %d", cs+1, ll+1,
ll ? 100 * cs / ll : 0, cs - bol);
showmsg(msg);
+ return 0;
}
/**/
-void
-undefinedkey(void)
+int
+undefinedkey(char **args)
{
- feep();
+ return 1;
}
/**/
-void
-quotedinsert(void)
+int
+quotedinsert(char **args)
{
#ifndef HAS_TIO
struct sgttyb sob;
@@ -426,20 +444,24 @@ quotedinsert(void)
#endif
c = getkey(0);
#ifndef HAS_TIO
- setterm();
+ zsetterm();
#endif
if (c < 0)
- feep();
+ return 1;
else
- selfinsert();
+ return selfinsert(args);
}
/**/
-void
-digitargument(void)
+int
+digitargument(char **args)
{
int sign = (zmult < 0) ? -1 : 1;
+ /* allow metafied as well as ordinary digits */
+ if ((c & 0x7f) < '0' || (c & 0x7f) > '9')
+ return 1;
+
if (!(zmod.flags & MOD_TMULT))
zmod.tmult = 0;
if (zmod.flags & MOD_NEG) {
@@ -451,26 +473,31 @@ digitargument(void)
zmod.tmult = zmod.tmult * 10 + sign * (c & 0xf);
zmod.flags |= MOD_TMULT;
prefixflag = 1;
+ return 0;
}
/**/
-void
-negargument(void)
+int
+negargument(char **args)
{
- if(zmod.flags & MOD_TMULT) {
- feep();
- return;
- }
+ if (zmod.flags & MOD_TMULT)
+ return 1;
zmod.tmult = -1;
zmod.flags |= MOD_TMULT|MOD_NEG;
prefixflag = 1;
+ return 0;
}
/**/
-void
-universalargument(void)
+int
+universalargument(char **args)
{
int digcnt = 0, pref = 0, minus = 1, gotk;
+ if (*args) {
+ zmod.mult = atoi(*args);
+ zmod.flags |= MOD_MULT;
+ return 0;
+ }
while ((gotk = getkey(0)) != EOF) {
if (gotk == '-' && !digcnt) {
minus = -1;
@@ -489,11 +516,12 @@ universalargument(void)
zmod.tmult *= 4;
zmod.flags |= MOD_TMULT;
prefixflag = 1;
+ return 0;
}
/**/
-void
-copyprevword(void)
+int
+copyprevword(char **args)
{
int len, t0;
@@ -509,18 +537,46 @@ copyprevword(void)
spaceinline(len);
memcpy((char *)&line[cs], (char *)&line[t0], len);
cs += len;
+ return 0;
}
/**/
-void
-sendbreak(void)
+int
+copyprevshellword(char **args)
+{
+ LinkList l;
+ LinkNode n;
+ int i;
+ char *p = NULL;
+
+ l = bufferwords(&i);
+
+ for (n = firstnode(l); n; incnode(n))
+ if (!i--) {
+ p = getdata(n);
+ break;
+ }
+ if (p) {
+ int len = strlen(p);
+
+ spaceinline(len);
+ memcpy(line + cs, p, len);
+ cs += len;
+ }
+ return 0;
+}
+
+/**/
+int
+sendbreak(char **args)
{
errflag = 1;
+ return 1;
}
/**/
-void
-quoteregion(void)
+int
+quoteregion(char **args)
{
char *str;
size_t len;
@@ -540,11 +596,12 @@ quoteregion(void)
memcpy((char *)&line[cs], str, len);
mark = cs;
cs += len;
+ return 0;
}
/**/
-void
-quoteline(void)
+int
+quoteline(char **args)
{
char *str;
size_t len = ll;
@@ -553,6 +610,7 @@ quoteline(void)
sizeline(len);
memcpy(line, str, len);
cs = ll = len;
+ return 0;
}
/**/
@@ -567,7 +625,7 @@ makequote(char *str, size_t *len)
if (*l == '\'')
qtct++;
*len += 2 + qtct*3;
- l = ol = (char *)halloc(*len);
+ l = ol = (char *)zhalloc(*len);
*l++ = '\'';
for (; str < end; str++)
if (*str == '\'') {
@@ -608,11 +666,13 @@ Thingy
executenamedcommand(char *prmt)
{
Thingy cmd;
- int len, l = strlen(prmt);
+ int len, l = strlen(prmt), feep = 0, listed = 0, curlist = 0;
+ int ols = (listshown && validlist), olll = lastlistlen;
char *ptr;
char *okeymap = curkeymapname;
- cmdbuf = halloc(l + NAMLEN + 2);
+ clearlist = 1;
+ cmdbuf = zhalloc(l + NAMLEN + 2);
strcpy(cmdbuf, prmt);
statusline = cmdbuf;
selectkeymap("main", 1);
@@ -621,40 +681,67 @@ executenamedcommand(char *prmt)
for (;;) {
*ptr = '_';
statusll = l + len + 1;
- refresh();
+ zrefresh();
if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
statusline = NULL;
selectkeymap(okeymap, 1);
+ if ((listshown = ols)) {
+ showinglist = -2;
+ lastlistlen = olll;
+ } else if (listed)
+ clearlist = listshown = 1;
+
return NULL;
}
if(cmd == Th(z_clearscreen)) {
- clearscreen();
+ clearscreen(zlenoargs);
+ if (curlist) {
+ int zmultsav = zmult;
+
+ zmult = 1;
+ listlist(cmdll);
+ showinglist = 0;
+ zmult = zmultsav;
+ }
} else if(cmd == Th(z_redisplay)) {
- redisplay();
+ redisplay(zlenoargs);
+ if (curlist) {
+ int zmultsav = zmult;
+
+ zmult = 1;
+ listlist(cmdll);
+ showinglist = 0;
+ zmult = zmultsav;
+ }
} else if(cmd == Th(z_viquotedinsert)) {
*ptr = '^';
- refresh();
+ zrefresh();
c = getkey(0);
if(c == EOF || !c || len == NAMLEN)
- feep();
+ feep = 1;
else
- *ptr++ = c, len++;
+ *ptr++ = c, len++, curlist = 0;
} else if(cmd == Th(z_quotedinsert)) {
if((c = getkey(0)) == EOF || !c || len == NAMLEN)
- feep();
+ feep = 1;
else
- *ptr++ = c, len++;
+ *ptr++ = c, len++, curlist = 0;
} else if(cmd == Th(z_backwarddeletechar) ||
cmd == Th(z_vibackwarddeletechar)) {
if (len)
- len--, ptr--;
+ len--, ptr--, curlist = 0;
} else if(cmd == Th(z_killregion) || cmd == Th(z_backwardkillword) ||
- cmd == Th(z_vibackwardkillword)) {
+ cmd == Th(z_vibackwardkillword)) {
+ if (len)
+ curlist = 0;
while (len && (len--, *--ptr != '-'));
} else if(cmd == Th(z_killwholeline) || cmd == Th(z_vikillline) ||
cmd == Th(z_backwardkillline)) {
len = 0;
ptr = cmdbuf;
+ if (listed)
+ clearlist = listshown = 1;
+ curlist = 0;
} else {
if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
Thingy r;
@@ -665,6 +752,11 @@ executenamedcommand(char *prmt)
unrefthingy(r);
statusline = NULL;
selectkeymap(okeymap, 1);
+ if ((listshown = ols)) {
+ showinglist = -2;
+ lastlistlen = olll;
+ } else if (listed)
+ clearlist = listshown = 1;
return r;
}
unrefthingy(r);
@@ -681,21 +773,25 @@ executenamedcommand(char *prmt)
cmd == Th(z_acceptline) || c == ' ' || c == '\t') {
cmdambig = 100;
- HEAPALLOC {
- cmdll = newlinklist();
- *ptr = 0;
+ cmdll = newlinklist();
+ *ptr = 0;
+
+ scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
- scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
- } LASTALLOC;
- if (empty(cmdll))
- feep();
- else if (cmd == Th(z_listchoices) ||
+ if (empty(cmdll)) {
+ feep = 1;
+ if (listed)
+ clearlist = listshown = 1;
+ curlist = 0;
+ } else if (cmd == Th(z_listchoices) ||
cmd == Th(z_deletecharorlist)) {
int zmultsav = zmult;
*ptr = '_';
statusll = l + len + 1;
zmult = 1;
listlist(cmdll);
+ listed = curlist = 1;
+ showinglist = 0;
zmult = zmultsav;
} else if (!nextnode(firstnode(cmdll))) {
strcpy(ptr = cmdbuf, peekfirst(cmdll));
@@ -710,22 +806,26 @@ executenamedcommand(char *prmt)
!(isset(LISTAMBIGUOUS) && cmdambig > len)) {
int zmultsav = zmult;
if (isset(LISTBEEP))
- feep();
+ feep = 1;
statusll = l + cmdambig + 1;
zmult = 1;
listlist(cmdll);
+ listed = curlist = 1;
+ showinglist = 0;
zmult = zmultsav;
}
len = cmdambig;
}
} else {
if (len == NAMLEN || icntrl(c) || cmd != Th(z_selfinsert))
- feep();
+ feep = 1;
else
- *ptr++ = c, len++;
+ *ptr++ = c, len++, curlist = 0;
}
}
- handlefeep();
+ if (feep)
+ handlefeep(zlenoargs);
+ feep = 0;
}
}
@@ -761,16 +861,22 @@ executenamedcommand(char *prmt)
* suffixlen[256] is the length to remove for non-insertion editing actions. */
/**/
-int suffixlen[257];
+mod_export int suffixlen[257];
+
+/* Shell function to call to remove the suffix. */
+
+/**/
+static char *suffixfunc;
/* Set up suffix: the last n characters are a suffix that should be *
* removed in the usual word end conditions. */
/**/
-void
+mod_export void
makesuffix(int n)
{
- suffixlen[256] = suffixlen[' '] = suffixlen['\t'] = suffixlen['\n'] = n;
+ suffixlen[256] = suffixlen[' '] = suffixlen['\t'] = suffixlen['\n'] =
+ suffixlen[';'] = suffixlen['&'] = suffixlen['|'] = n;
}
/* Set up suffix for parameter names: the last n characters are a suffix *
@@ -780,7 +886,7 @@ makesuffix(int n)
* characters that can only be used in braces are included. */
/**/
-void
+mod_export void
makeparamsuffix(int br, int n)
{
if(br || unset(KSHARRAYS))
@@ -788,20 +894,91 @@ makeparamsuffix(int br, int n)
if(br) {
suffixlen['#'] = suffixlen['%'] = suffixlen['?'] = n;
suffixlen['-'] = suffixlen['+'] = suffixlen['='] = n;
- /*{*/ suffixlen['}'] = n;
+ /*{*/ suffixlen['}'] = suffixlen['/'] = n;
}
}
+/* Set up suffix given a string containing the characters on which to *
+ * remove the suffix. */
+
+/**/
+mod_export void
+makesuffixstr(char *f, char *s, int n)
+{
+ if (f) {
+ zsfree(suffixfunc);
+ suffixfunc = ztrdup(f);
+ suffixlen[0] = n;
+ } else if (s) {
+ int inv, i, v, z = 0;
+
+ if (*s == '^' || *s == '!') {
+ inv = 1;
+ s++;
+ } else
+ inv = 0;
+ s = getkeystring(s, &i, 5, &z);
+ s = metafy(s, i, META_USEHEAP);
+
+ if (inv) {
+ v = 0;
+ for (i = 0; i < 257; i++)
+ suffixlen[i] = n;
+ } else
+ v = n;
+
+ if (z)
+ suffixlen[256] = v;
+
+ while (*s) {
+ if (s[1] == '-' && s[2]) {
+ int b = (int) *s, e = (int) s[2];
+
+ while (b <= e)
+ suffixlen[b++] = v;
+ s += 2;
+ } else
+ suffixlen[STOUC(*s)] = v;
+ s++;
+ }
+ } else
+ makesuffix(n);
+}
+
/* Remove suffix, if there is one, when inserting character c. */
/**/
-void
-iremovesuffix(int c)
+mod_export void
+iremovesuffix(int c, int keep)
{
- int sl = suffixlen[c];
- if(sl) {
- backdel(sl);
- invalidatelist();
+ if (suffixfunc) {
+ Eprog prog = getshfunc(suffixfunc);
+
+ if (prog != &dummy_eprog) {
+ LinkList args = newlinklist();
+ char buf[20];
+ int osc = sfcontext;
+
+ sprintf(buf, "%d", suffixlen[0]);
+ addlinknode(args, suffixfunc);
+ addlinknode(args, buf);
+
+ startparamscope();
+ makezleparams(0);
+ sfcontext = SFC_COMPLETE;
+ doshfunc(suffixfunc, prog, args, 0, 1);
+ sfcontext = osc;
+ endparamscope();
+ }
+ zsfree(suffixfunc);
+ suffixfunc = NULL;
+ } else {
+ int sl = suffixlen[c];
+ if(sl) {
+ backdel(sl);
+ if (!keep)
+ invalidatelist();
+ }
}
fixsuffix();
}
@@ -809,7 +986,7 @@ iremovesuffix(int c)
/* Fix the suffix in place, if there is one, making it non-removable. */
/**/
-void
+mod_export void
fixsuffix(void)
{
memset(suffixlen, 0, sizeof(suffixlen));
diff --git a/Src/hist.c b/Src/hist.c
index a4c5735c1..530b6e05c 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -30,10 +30,31 @@
#include "zsh.mdh"
#include "hist.pro"
+/* Functions to call for getting/ungetting a character and for history
+ * word control. */
+
+/**/
+mod_export int (*hgetc) _((void));
+
+/**/
+void (*hungetc) _((int));
+
+/**/
+void (*hwaddc) _((int));
+
+/**/
+void (*hwbegin) _((int));
+
+/**/
+void (*hwend) _((void));
+
+/**/
+void (*addtoline) _((int));
+
/* != 0 means history substitution is turned off */
/**/
-int stophist;
+mod_export int stophist;
/* this line began with a space, so junk it if HISTIGNORESPACE is on */
@@ -43,12 +64,12 @@ int spaceflag;
/* if != 0, we are expanding the current line */
/**/
-int expanding;
+mod_export int expanding;
/* these are used to modify the cursor position during expansion */
/**/
-int excs, exlast;
+mod_export int excs, exlast;
/*
* Current history event number
@@ -56,23 +77,29 @@ int excs, exlast;
* Note on curhist: with history inactive, this points to the
* last line actually added to the history list. With history active,
* the line does not get added to the list until hend(), if at all.
- * However, curhist is incremented to reflect the current line anyway.
- * Thus if the line is not added to the list, curhist must be
- * decremented in hend().
+ * However, curhist is incremented to reflect the current line anyway
+ * and a temporary history entry is inserted while the user is editing.
+ * If the resulting line was not added to the list, a flag is set so
+ * that curhist will be decremented in hbegin().
*/
/**/
-int curhist;
+mod_export int curhist;
-/* number of history entries */
-
/**/
-int histentct;
-
-/* array of history entries */
-
+struct histent curline;
+
+/* current line count of allocated history entries */
+
+/**/
+int histlinect;
+
+/* The history lines are kept in a hash, and also doubly-linked in a ring */
+
+/**/
+HashTable histtab;
/**/
-Histent histentarr;
+mod_export Histent hist_ring;
/* capacity of history lists */
@@ -90,6 +117,17 @@ int histdone;
/**/
int histactive;
+/* Current setting of the associated option, but sometimes also includes
+ * the setting of the HIST_SAVE_NO_DUPS option. */
+
+/**/
+int hist_ignore_all_dups;
+
+/* What flags (if any) we should skip when moving through the history */
+
+/**/
+mod_export int hist_skip_flags;
+
/* Bits of histactive variable */
#define HA_ACTIVE (1<<0) /* History mechanism is active */
#define HA_NOSTORE (1<<1) /* Don't store the line when finished */
@@ -121,12 +159,12 @@ char *hsubr;
/* pointer into the history line */
/**/
-char *hptr;
+mod_export char *hptr;
/* the current history line */
/**/
-char *chline;
+mod_export char *chline;
/* true if the last character returned by hgetc was an escaped bangchar *
* if it is set and NOBANGHIST is unset hwaddc escapes bangchars */
@@ -142,12 +180,11 @@ int hlinesz;
/* default event (usually curhist-1, that is, "!!") */
static int defev;
-
+
/* add a character to the current history word */
-/**/
-void
-hwaddc(int c)
+static void
+ihwaddc(int c)
{
/* Only if history line exists and lexing has not finished. */
if (chline && !(errflag || lexstop)) {
@@ -165,7 +202,7 @@ hwaddc(int c)
if (hptr - chline >= hlinesz) {
int oldsiz = hlinesz;
- chline = realloc(chline, hlinesz = oldsiz + 16);
+ chline = realloc(chline, hlinesz = oldsiz + 64);
hptr = chline + oldsiz;
}
}
@@ -175,12 +212,12 @@ hwaddc(int c)
* zsh expands history (see doexpandhist() in zle_tricky.c). It also *
* calculates the new cursor position after the expansion. It is called *
* from hgetc() and from gettok() in lex.c for characters in comments. */
-
+
/**/
void
-addtoline(int c)
+iaddtoline(int c)
{
- if (! expanding || lexstop)
+ if (!expanding || lexstop)
return;
if (qbang && c == bangchar && stophist < 2) {
exlast--;
@@ -199,9 +236,8 @@ addtoline(int c)
line[cs++] = itok(c) ? ztokens[c - Pound] : c;
}
-/**/
-int
-hgetc(void)
+static int
+ihgetc(void)
{
int c = ingetc();
@@ -217,7 +253,7 @@ hgetc(void)
}
if ((inbufflags & INP_HIST) && !stophist) {
/* the current character c came from a history expansion *
- * (inbufflags && INP_HIST) and history is not disabled *
+ * (inbufflags & INP_HIST) and history is not disabled *
* (e.g. we are not inside single quotes). In that case, \! *
* should be treated as ! (since this \! came from a previous *
* history line where \ was used to escape the bang). So if *
@@ -255,7 +291,7 @@ safeinungetc(int c)
void
herrflush(void)
{
- while (!lexstop && inbufct)
+ while (!lexstop && inbufct && !strin)
hwaddc(ingetc());
}
@@ -392,10 +428,10 @@ histsubchar(int c)
c = ingetc();
}
*ptr = 0;
- if (!*buf)
+ if (!*buf) {
if (c != '%') {
if (isset(CSHJUNKIEHISTORY))
- ev = curhist - 1;
+ ev = addhistnum(curhist,-1,HIST_FOREIGN);
else
ev = defev;
if (c == ':' && evset == -1)
@@ -408,11 +444,12 @@ histsubchar(int c)
else
ev = defev;
evset = 0;
+ }
} else if ((t0 = atoi(buf))) {
- ev = (t0 < 0) ? curhist + t0 : t0;
+ ev = (t0 < 0) ? addhistnum(curhist,t0,HIST_FOREIGN) : t0;
evset = 1;
} else if ((unsigned)*buf == bangchar) {
- ev = curhist - 1;
+ ev = addhistnum(curhist,-1,HIST_FOREIGN);
evset = 1;
} else if (*buf == '#') {
ev = curhist;
@@ -540,6 +577,18 @@ histsubchar(int c)
case 'q':
quote(&sline);
break;
+ case 'Q':
+ {
+ int one = noerrs, oef = errflag;
+
+ noerrs = 1;
+ parse_subst_string(sline);
+ noerrs = one;
+ errflag = oef;
+ remnulargs(sline);
+ untokenize(sline);
+ }
+ break;
case 'x':
quotebreak(&sline);
break;
@@ -588,13 +637,12 @@ histsubchar(int c)
/* unget a char and remove it from chline. It can only be used *
* to unget a character returned by hgetc. */
-/**/
-void
-hungetc(int c)
+static void
+ihungetc(int c)
{
int doit = 1;
- while (!lexstop) {
+ while (!lexstop && !errflag) {
if (hptr[-1] != (char) c && stophist < 4 &&
hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\')
hungetc('\n'), hungetc('\\');
@@ -622,18 +670,18 @@ hungetc(int c)
/* begin reading a string */
/**/
-void
-strinbeg(void)
+mod_export void
+strinbeg(int dohist)
{
strin++;
- hbegin();
+ hbegin(dohist);
lexinit();
}
/* done reading a string */
/**/
-void
+mod_export void
strinend(void)
{
hend();
@@ -643,60 +691,72 @@ strinend(void)
histdone = 0;
}
+/* dummy functions to use instead of hwaddc(), hwbegin(), and hwend() when
+ * they aren't needed */
+
+static void
+nohw(int c)
+{
+}
+
+static void
+nohwe(void)
+{
+}
+
/* initialize the history mechanism */
/**/
-void
-hbegin(void)
+mod_export void
+hbegin(int dohist)
{
- Histent curhistent;
-
isfirstln = isfirstch = 1;
errflag = histdone = spaceflag = 0;
- stophist = (!interact || unset(BANGHIST) || unset(SHINSTDIN)) << 1;
- chline = hptr = zcalloc(hlinesz = 16);
- chwords = zalloc((chwordlen = 16)*sizeof(short));
+ stophist = (!dohist || !interact || unset(SHINSTDIN)) ? 2 : 0;
+ if (stophist == 2 || (inbufflags & INP_ALIAS)) {
+ chline = hptr = NULL;
+ hlinesz = 0;
+ chwords = NULL;
+ chwordlen = 0;
+ hgetc = ingetc;
+ hungetc = inungetc;
+ hwaddc = nohw;
+ hwbegin = nohw;
+ hwend = nohwe;
+ addtoline = nohw;
+ } else {
+ chline = hptr = zcalloc(hlinesz = 64);
+ chwords = zalloc((chwordlen = 64) * sizeof(short));
+ hgetc = ihgetc;
+ hungetc = ihungetc;
+ hwaddc = ihwaddc;
+ hwbegin = ihwbegin;
+ hwend = ihwend;
+ addtoline = iaddtoline;
+ if (!isset(BANGHIST))
+ stophist = 4;
+ }
chwordpos = 0;
if (histactive & HA_JUNKED)
curhist--;
- curhistent = gethistent(curhist);
- if (!curhistent->ftim)
- curhistent->ftim = time(NULL);
- histactive = HA_ACTIVE;
+ if (hist_ring && !hist_ring->ftim)
+ hist_ring->ftim = time(NULL);
if (interact && isset(SHINSTDIN) && !strin) {
+ histactive = HA_ACTIVE;
attachtty(mypgrp);
- defev = curhist;
- curhist++;
+ if (!hist_ring)
+ hist_ring = curline.up = curline.down = &curline;
+ else {
+ curline.up = hist_ring;
+ curline.down = hist_ring->down;
+ hist_ring->down = hist_ring->down->up = &curline;
+ hist_ring = &curline;
+ }
+ curline.histnum = ++curhist;
+ defev = addhistnum(curhist, -1, HIST_FOREIGN);
} else
- histactive |= HA_NOINC;
-}
-
-/* compare current line with history entry using only text in words */
-
-/**/
-static int
-histcmp(Histent he)
-{
- int kword, lword;
- int nwords = chwordpos/2;
-
- /* If the history entry came from a file, the words were not
- * divided by the lexer so we have to resort to strcmp.
- */
- if (he->flags & HIST_READ)
- return strcmp(he->text, chline);
-
- if (nwords != he->nwords)
- return 1;
-
- for (kword = 0; kword < 2*nwords; kword += 2)
- if ((lword = chwords[kword+1]-chwords[kword])
- != he->words[kword+1]-he->words[kword] ||
- memcmp(he->text+he->words[kword], chline+chwords[kword], lword))
- return 1;
-
- return 0;
+ histactive = HA_ACTIVE | HA_NOINC;
}
/**/
@@ -725,35 +785,194 @@ histreduceblanks(void)
chline[pos] = '\0';
}
+/**/
+void
+histremovedups(void)
+{
+ Histent he, next;
+ for (he = hist_ring; he; he = next) {
+ next = up_histent(he);
+ if (he->flags & HIST_DUP)
+ freehistnode((HashNode)he);
+ }
+}
+
+/**/
+mod_export int
+addhistnum(int hl, int n, int xflags)
+{
+ int dir = n < 0? -1 : n > 0? 1 : 0;
+ Histent he = gethistent(hl, dir);
+
+ if (!he)
+ return 0;
+ if (he->histnum != hl)
+ n -= dir;
+ if (n)
+ he = movehistent(he, n, xflags);
+ if (!he)
+ return dir < 0? firsthist() : curhist;
+ return he->histnum;
+}
+
+/**/
+mod_export Histent
+movehistent(Histent he, int n, int xflags)
+{
+ while (n < 0) {
+ if (!(he = up_histent(he)))
+ return NULL;
+ if (!(he->flags & xflags))
+ n++;
+ }
+ while (n > 0) {
+ if (!(he = down_histent(he)))
+ return NULL;
+ if (!(he->flags & xflags))
+ n--;
+ }
+ return he;
+}
+
+/**/
+mod_export Histent
+up_histent(Histent he)
+{
+ return he->up == hist_ring? NULL : he->up;
+}
+
+/**/
+mod_export Histent
+down_histent(Histent he)
+{
+ return he == hist_ring? NULL : he->down;
+}
+
+/**/
+Histent
+gethistent(int ev, int nearmatch)
+{
+ Histent he;
+
+ if (!hist_ring)
+ return NULL;
+
+ if (ev - hist_ring->down->histnum < hist_ring->histnum - ev) {
+ for (he = hist_ring->down; he->histnum <= ev; he = he->down) {
+ if (he->histnum == ev)
+ return he;
+ }
+ if (nearmatch < 0)
+ return up_histent(he);
+ if (nearmatch > 0)
+ return he;
+ }
+ else {
+ for (he = hist_ring; he->histnum >= ev; he = he->up) {
+ if (he->histnum == ev)
+ return he;
+ }
+ if (nearmatch < 0)
+ return he;
+ if (nearmatch > 0)
+ return down_histent(he);
+ }
+
+ return NULL;
+}
+
+/**/
+Histent
+prepnexthistent(int histnum)
+{
+ Histent he;
+
+ if (histlinect < histsiz) {
+ he = (Histent)zcalloc(sizeof *he);
+ if (!hist_ring)
+ hist_ring = he->up = he->down = he;
+ else {
+ he->up = hist_ring;
+ he->down = hist_ring->down;
+ hist_ring->down = he->down->up = he;
+ hist_ring = he;
+ }
+ histlinect++;
+ }
+ else {
+ he = hist_ring->down;
+ if (isset(HISTEXPIREDUPSFIRST) && !(he->flags & HIST_DUP)) {
+ int max_unique_ct = getiparam("SAVEHIST");
+ do {
+ if (max_unique_ct-- <= 0) {
+ he = hist_ring->down;
+ break;
+ }
+ he = he->down;
+ } while (he != hist_ring->down && !(he->flags & HIST_DUP));
+ if (he != hist_ring->down) {
+ he->up->down = he->down;
+ he->down->up = he->up;
+ he->up = hist_ring;
+ he->down = hist_ring->down;
+ hist_ring->down = he->down->up = he;
+ }
+ }
+ freehistdata(hist_ring = he, 0);
+ }
+ hist_ring->histnum = histnum;
+ return hist_ring;
+}
+
/* say we're done using the history mechanism */
/**/
-int
+mod_export int
hend(void)
{
int flag, save = 1;
-
- DPUTS(!chline, "BUG: chline is NULL in hend()");
+ char *hf = getsparam("HISTFILE");
+
+ DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline,
+ "BUG: chline is NULL in hend()");
+ if (histdone & HISTFLAG_SETTY)
+ settyinfo(&shttyinfo);
+ if (!(histactive & HA_NOINC)) {
+ curline.up->down = curline.down;
+ curline.down->up = curline.up;
+ if (hist_ring == &curline) {
+ if (!histlinect)
+ hist_ring = NULL;
+ else
+ hist_ring = curline.up;
+ }
+ curhist--;
+ }
if (histactive & (HA_NOSTORE|HA_NOINC)) {
zfree(chline, hlinesz);
zfree(chwords, chwordlen*sizeof(short));
chline = NULL;
- if (!(histactive & HA_NOINC))
- curhist--;
histactive = 0;
return 1;
}
+ if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS)
+ && (hist_ignore_all_dups = isset(HISTIGNOREALLDUPS)) != 0)
+ histremovedups();
+ /* For history sharing, lock history file once for both read and write */
+ if (isset(SHAREHISTORY) && lockhistfile(hf, 0))
+ readhistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
flag = histdone;
histdone = 0;
if (hptr < chline + 1)
save = 0;
else {
*hptr = '\0';
- if (hptr[-1] == '\n')
+ if (hptr[-1] == '\n') {
if (chline[1]) {
*--hptr = '\0';
} else
save = 0;
+ }
if (!*chline || !strcmp(chline, "\n") ||
(isset(HISTIGNORESPACE) && spaceflag))
save = 0;
@@ -768,17 +987,18 @@ hend(void)
fflush(shout);
}
if (flag & HISTFLAG_RECALL) {
- PERMALLOC {
- pushnode(bufstack, ptr);
- } LASTALLOC;
+ zpushnode(bufstack, ptr);
+
save = 0;
} else
zsfree(ptr);
}
if (save) {
Histent he;
- int keepflags = 0;
+ int keepflags;
+ for (he = hist_ring; he && he->flags & hist_skip_flags;
+ he = up_histent(he)) ;
#ifdef DEBUG
/* debugging only */
if (chwordpos%2) {
@@ -787,28 +1007,26 @@ hend(void)
}
#endif
/* get rid of pesky \n which we've already nulled out */
- if (!chline[chwords[chwordpos-2]])
+ if (chwordpos > 1 && !chline[chwords[chwordpos-2]])
chwordpos -= 2;
/* strip superfluous blanks, if desired */
if (isset(HISTREDUCEBLANKS))
histreduceblanks();
-
- if (isset(HISTIGNOREDUPS) && (he = gethistent(curhist - 1))
- && he->text && !histcmp(he)) {
+ if ((isset(HISTIGNOREDUPS) || isset(HISTIGNOREALLDUPS)) && he
+ && histstrcmp(chline, he->text) == 0) {
/* This history entry compares the same as the previous.
* In case minor changes were made, we overwrite the
- * previous one with the current one. This also gets
- * the timestamp right. However, keep the old flags.
+ * previous one with the current one. This also gets the
+ * timestamp right. Perhaps, preserve the HIST_OLD flag.
*/
- keepflags = he->flags;
- curhist--;
+ keepflags = he->flags & HIST_OLD; /* Avoid re-saving */
+ freehistdata(he, 0);
+ } else {
+ keepflags = 0;
+ he = prepnexthistent(++curhist);
}
- he = gethistent(curhist);
- zsfree(he->text);
he->text = ztrdup(chline);
- if (he->nwords)
- zfree(he->words, he->nwords*2*sizeof(short));
he->stim = time(NULL);
he->ftim = 0L;
he->flags = keepflags;
@@ -817,12 +1035,15 @@ hend(void)
he->words = (short *)zalloc(chwordpos * sizeof(short));
memcpy(he->words, chwords, chwordpos * sizeof(short));
}
- } else
- curhist--;
+ addhistnode(histtab, he->text, he);
+ }
zfree(chline, hlinesz);
zfree(chwords, chwordlen*sizeof(short));
chline = NULL;
histactive = 0;
+ if (isset(SHAREHISTORY) || isset(INCAPPENDHISTORY))
+ savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
+ unlockhistfile(hf); /* It's OK to call this even if we aren't locked */
return !(flag & HISTFLAG_NOEXEC || errflag);
}
@@ -832,12 +1053,11 @@ hend(void)
void
remhist(void)
{
+ if (hist_ring == &curline)
+ return;
if (!(histactive & HA_ACTIVE)) {
if (!(histactive & HA_JUNKED)) {
- /* make sure this doesn't show up when we do firsthist() */
- Histent he = gethistent(curhist);
- zsfree(he->text);
- he->text = NULL;
+ freehistnode((HashNode)hist_ring);
histactive |= HA_JUNKED;
/* curhist-- is delayed until the next hbegin() */
}
@@ -854,8 +1074,10 @@ int hwgetword = -1;
/**/
void
-hwbegin(int offset)
+ihwbegin(int offset)
{
+ if (stophist == 2)
+ return;
if (chwordpos%2)
chwordpos--; /* make sure we're on a word start, not end */
/* If we're expanding an alias, we should overwrite the expansion
@@ -872,15 +1094,18 @@ hwbegin(int offset)
/**/
void
-hwend(void)
+ihwend(void)
{
+ if (stophist == 2)
+ return;
if (chwordpos%2 && chline) {
/* end of word reached and we've already begun a word */
if (hptr > chline + chwords[chwordpos-1]) {
chwords[chwordpos++] = hptr - chline;
if (chwordpos >= chwordlen) {
chwords = (short *) realloc(chwords,
- (chwordlen += 16)*sizeof(short));
+ (chwordlen += 32) *
+ sizeof(short));
}
if (hwgetword > -1) {
/* We want to reuse the current word position */
@@ -956,7 +1181,7 @@ hwrep(char *rep)
/* Get the entire current line, deleting it in the history. */
/**/
-char *
+mod_export char *
hgetline(void)
{
/* Currently only used by pushlineoredit().
@@ -1022,18 +1247,21 @@ getargspec(int argc, int marg, int evset)
static int
hconsearch(char *str, int *marg)
{
- int t0, t1 = 0;
+ int t1 = 0;
char *s;
Histent he;
- for (t0 = curhist - 1; (he = quietgethist(t0)); t0--)
+ for (he = up_histent(hist_ring); he; he = up_histent(he)) {
+ if (he->flags & HIST_FOREIGN)
+ continue;
if ((s = strstr(he->text, str))) {
int pos = s - he->text;
while (t1 < he->nwords && he->words[2*t1] <= pos)
t1++;
*marg = t1 - 1;
- return t0;
+ return he->histnum;
}
+ }
return -1;
}
@@ -1043,12 +1271,15 @@ hconsearch(char *str, int *marg)
int
hcomsearch(char *str)
{
- int t0;
- char *hs;
+ Histent he;
+ int len = strlen(str);
- for (t0 = curhist - 1; (hs = quietgetevent(t0)); t0--)
- if (!strncmp(hs, str, strlen(str)))
- return t0;
+ for (he = up_histent(hist_ring); he; he = up_histent(he)) {
+ if (he->flags & HIST_FOREIGN)
+ continue;
+ if (strncmp(he->text, str, len) == 0)
+ return he->histnum;
+ }
return -1;
}
@@ -1097,7 +1328,7 @@ rembutext(char **junkptr)
}
/**/
-int
+mod_export int
remlpaths(char **junkptr)
{
char *str = *junkptr, *remcut;
@@ -1188,7 +1419,7 @@ convamps(char *out, char *in, int inlen)
slen += inlen - 1, sdup = 1;
if (!sdup)
return out;
- ret = pp = (char *)alloc(slen + 1);
+ ret = pp = (char *) zhalloc(slen + 1);
for (ptr = out; *ptr; ptr++)
if (*ptr == '\\')
*pp++ = *++ptr;
@@ -1202,33 +1433,22 @@ convamps(char *out, char *in, int inlen)
}
/**/
-struct histent *
-quietgethist(int ev)
+mod_export Histent
+quietgethistent(int ev, int nearmatch)
{
- static struct histent storehist;
-
- if (ev < firsthist() || ev > curhist)
- return NULL;
if (ev == curhist && (histactive & HA_ACTIVE)) {
- /* The current history line has not been stored. Build it up
- * from other variables.
- */
- storehist.text = chline;
- storehist.nwords = chwordpos/2;
- storehist.words = chwords;
-
- return &storehist;
- } else
- return gethistent(ev);
+ curline.text = chline;
+ curline.nwords = chwordpos/2;
+ curline.words = chwords;
+ }
+ return gethistent(ev, nearmatch);
}
/**/
-char *
-quietgetevent(int ev)
+mod_export Histent
+quietgethist(int ev)
{
- Histent ent = quietgethist(ev);
-
- return ent ? ent->text : NULL;
+ return quietgethistent(ev, GETHIST_EXACT);
}
/**/
@@ -1301,7 +1521,7 @@ quote(char **tr)
} else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\')
len += 2;
ptr = *str;
- *str = rptr = (char *)alloc(len);
+ *str = rptr = (char *) zhalloc(len);
*rptr++ = '\'';
for (; *ptr; ptr++)
if (*ptr == '\'') {
@@ -1338,7 +1558,7 @@ quotebreak(char **tr)
else if (inblank(*ptr))
len += 2;
ptr = *str;
- *str = rptr = (char *)alloc(len);
+ *str = rptr = (char *) zhalloc(len);
*rptr++ = '\'';
for (; *ptr;)
if (*ptr == '\'') {
@@ -1358,10 +1578,9 @@ quotebreak(char **tr)
return 0;
}
-#if 0
/* read an arbitrary amount of data into a buffer until stop is found */
-/**/
+#if 0 /**/
char *
hdynread(int stop)
{
@@ -1420,61 +1639,80 @@ hdynread2(int stop)
void
inithist(void)
{
- histentct = histsiz;
- histentarr = (Histent) zcalloc(histentct * sizeof *histentarr);
+ createhisttable();
}
/**/
void
resizehistents(void)
{
- int newentct, t0, t1, firstlex;
- Histent newarr;
-
- newentct = histsiz;
- newarr = (Histent) zcalloc(newentct * sizeof *newarr);
- firstlex = curhist - histsiz + 1;
- t0 = firsthist();
- if (t0 < curhist - newentct)
- t0 = curhist - newentct;
- t1 = t0 % newentct;
- for (; t0 <= curhist; t0++) {
- newarr[t1] = *gethistent(t0);
- if (t0 < firstlex) {
- zsfree(newarr[t1].text);
- newarr[t1].text = NULL;
- }
- t1++;
- if (t1 == newentct)
- t1 = 0;
- }
- free(histentarr);
- histentarr = newarr;
- histentct = newentct;
+ while (histlinect > histsiz)
+ freehistnode((HashNode)hist_ring->down);
}
+/* Remember the last line in the history file so we can find it again. */
+static struct {
+ char *text;
+ time_t stim, mtim;
+ off_t fpos, fsiz;
+ int next_write_ev;
+} lasthist;
+
+static int histfile_linect;
+
/**/
void
-readhistfile(char *s, int err)
+readhistfile(char *fn, int err, int readflags)
{
- char *buf;
+ char *buf, *start = NULL;
FILE *in;
- Histent ent;
- time_t tim = time(NULL);
+ Histent he;
+ time_t stim, ftim, tim = time(NULL);
+ off_t fpos;
short *wordlist;
+ struct stat sb;
int nwordpos, nwordlist, bufsiz;
+ int searching, newflags;
- if (!s)
+ if (!fn && !(fn = getsparam("HISTFILE")))
+ return;
+ if (readflags & HFILE_FAST) {
+ if (stat(fn, &sb) < 0
+ || (lasthist.fsiz == sb.st_size && lasthist.mtim == sb.st_mtime)
+ || !lockhistfile(fn, 0))
+ return;
+ lasthist.fsiz = sb.st_size;
+ lasthist.mtim = sb.st_mtime;
+ }
+ else if (!lockhistfile(fn, 1))
return;
- if ((in = fopen(unmeta(s), "r"))) {
- nwordlist = 16;
+ if ((in = fopen(unmeta(fn), "r"))) {
+ nwordlist = 64;
wordlist = (short *)zalloc(nwordlist*sizeof(short));
bufsiz = 1024;
buf = zalloc(bufsiz);
- while (fgets(buf, bufsiz, in)) {
+ if (readflags & HFILE_FAST && lasthist.text) {
+ if (lasthist.fpos < lasthist.fsiz) {
+ fseek(in, lasthist.fpos, 0);
+ searching = 1;
+ }
+ else {
+ histfile_linect = 0;
+ searching = -1;
+ }
+ } else
+ searching = 0;
+
+ newflags = HIST_OLD | HIST_READ;
+ if (readflags & HFILE_FAST)
+ newflags |= HIST_FOREIGN;
+ if (readflags & HFILE_SKIPOLD
+ || (hist_ignore_all_dups && newflags & hist_skip_flags))
+ newflags |= HIST_MAKEUNIQUE;
+ while (fpos = ftell(in), fgets(buf, bufsiz, in)) {
int l = strlen(buf);
- char *pt, *start;
+ char *pt;
while (l) {
while (buf[l - 1] != '\n') {
@@ -1484,114 +1722,184 @@ readhistfile(char *s, int err)
l++;
break;
}
- l = strlen(buf);
+ l += strlen(buf+l);
}
buf[l - 1] = '\0';
if (l > 1 && buf[l - 2] == '\\') {
- buf[l - 2] = '\n';
- fgets(buf + l - 1, bufsiz - (l - 1), in);
- l = strlen(buf);
+ buf[--l - 1] = '\n';
+ fgets(buf + l, bufsiz - l, in);
+ l += strlen(buf+l);
} else
break;
}
- ent = gethistent(++curhist);
pt = buf;
if (*pt == ':') {
pt++;
- ent->stim = zstrtol(pt, NULL, 0);
+ stim = zstrtol(pt, NULL, 0);
for (; *pt != ':' && *pt; pt++);
if (*pt) {
pt++;
- ent->ftim = zstrtol(pt, NULL, 0);
+ ftim = zstrtol(pt, NULL, 0);
for (; *pt != ';' && *pt; pt++);
if (*pt)
pt++;
- } else {
- ent->ftim = tim;
- }
- if (ent->stim == 0)
- ent->stim = tim;
- if (ent->ftim == 0)
- ent->ftim = tim;
+ } else
+ ftim = stim;
} else {
- ent->ftim = ent->stim = tim;
+ if (*pt == '\\' && pt[1] == ':')
+ pt++;
+ stim = ftim = 0;
}
- zsfree(ent->text);
- ent->text = ztrdup(pt);
- ent->flags = HIST_OLD|HIST_READ;
- if (ent->nwords)
- zfree(ent->words, ent->nwords*2*sizeof(short));
+ if (searching) {
+ if (searching > 0) {
+ if (stim == lasthist.stim
+ && histstrcmp(pt, lasthist.text) == 0)
+ searching = 0;
+ else {
+ fseek(in, 0, 0);
+ histfile_linect = 0;
+ searching = -1;
+ }
+ continue;
+ }
+ else if (stim < lasthist.stim) {
+ histfile_linect++;
+ continue;
+ }
+ searching = 0;
+ }
+
+ if (readflags & HFILE_USE_OPTIONS) {
+ histfile_linect++;
+ lasthist.fpos = fpos;
+ lasthist.stim = stim;
+ }
+
+ he = prepnexthistent(++curhist);
+ he->text = ztrdup(pt);
+ he->flags = newflags;
+ if ((he->stim = stim) == 0)
+ he->stim = he->ftim = tim;
+ else if (ftim < stim)
+ he->ftim = stim + ftim;
+ else
+ he->ftim = ftim;
/* Divide up the words. We don't know how it lexes,
- so just look for spaces.
+ so just look for white-space.
*/
nwordpos = 0;
start = pt;
do {
- while (*pt == ' ')
+ while (inblank(*pt))
pt++;
if (*pt) {
if (nwordpos >= nwordlist)
wordlist = (short *) realloc(wordlist,
- (nwordlist += 16)*sizeof(short));
+ (nwordlist += 64)*sizeof(short));
wordlist[nwordpos++] = pt - start;
- while (*pt && *pt != ' ')
+ while (*pt && !inblank(*pt))
pt++;
wordlist[nwordpos++] = pt - start;
}
} while (*pt);
- ent->nwords = nwordpos/2;
- if (ent->nwords) {
- ent->words = (short *)zalloc(nwordpos*sizeof(short));
- memcpy(ent->words, wordlist, nwordpos*sizeof(short));
+ he->nwords = nwordpos/2;
+ if (he->nwords) {
+ he->words = (short *)zalloc(nwordpos*sizeof(short));
+ memcpy(he->words, wordlist, nwordpos*sizeof(short));
} else
- ent->words = (short *)NULL;
+ he->words = (short *)NULL;
+ addhistnode(histtab, he->text, he);
+ if (hist_ring != he)
+ curhist--; /* We discarded a foreign duplicate */
+ }
+ if (start && readflags & HFILE_USE_OPTIONS) {
+ zsfree(lasthist.text);
+ lasthist.text = ztrdup(start);
}
- fclose(in);
-
zfree(wordlist, nwordlist*sizeof(short));
zfree(buf, bufsiz);
+
+ fclose(in);
} else if (err)
- zerr("can't read history file", s, 0);
+ zerr("can't read history file", fn, 0);
+
+ unlockhistfile(fn);
}
/**/
void
-savehistfile(char *s, int err, int app)
+savehistfile(char *fn, int err, int writeflags)
{
- char *t;
+ char *t, *start = NULL;
FILE *out;
- int ev;
- Histent ent;
+ Histent he;
+ int xcurhist = curhist - !!(histactive & HA_ACTIVE);
int savehist = getiparam("SAVEHIST");
+ int extended_history = isset(EXTENDEDHISTORY);
- if (!s || !interact || savehist <= 0)
+ if (!interact || savehist <= 0 || !hist_ring
+ || (!fn && !(fn = getsparam("HISTFILE"))))
return;
- ev = curhist - savehist + 1;
- if (ev < firsthist())
- ev = firsthist();
- if (app & 1)
- out = fdopen(open(unmeta(s),
- O_CREAT | O_WRONLY | O_APPEND | O_NOCTTY, 0600), "a");
- else
- out = fdopen(open(unmeta(s),
- O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600), "w");
+ if (writeflags & HFILE_FAST) {
+ he = gethistent(lasthist.next_write_ev, GETHIST_DOWNWARD);
+ while (he && he->flags & HIST_OLD) {
+ lasthist.next_write_ev = he->histnum + 1;
+ he = down_histent(he);
+ }
+ if (!he || !lockhistfile(fn, 0))
+ return;
+ if (histfile_linect > savehist + savehist / 5)
+ writeflags &= ~HFILE_FAST;
+ }
+ else {
+ if (!lockhistfile(fn, 1))
+ return;
+ he = hist_ring->down;
+ }
+ if (writeflags & HFILE_USE_OPTIONS) {
+ if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY)
+ || isset(SHAREHISTORY))
+ writeflags |= HFILE_APPEND | HFILE_SKIPOLD;
+ else
+ histfile_linect = 0;
+ if (isset(HISTSAVENODUPS))
+ writeflags |= HFILE_SKIPDUPS;
+ if (isset(SHAREHISTORY))
+ extended_history = 1;
+ }
+ if (writeflags & HFILE_APPEND) {
+ out = fdopen(open(unmeta(fn),
+ O_CREAT | O_WRONLY | O_APPEND | O_NOCTTY, 0600), "a");
+ }
+ else {
+ out = fdopen(open(unmeta(fn),
+ O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600), "w");
+ }
if (out) {
- for (; ev <= curhist - !!(histactive & HA_ACTIVE); ev++) {
- ent = gethistent(ev);
- if (app & 2) {
- if (ent->flags & HIST_OLD)
+ for (; he && he->histnum <= xcurhist; he = down_histent(he)) {
+ if ((writeflags & HFILE_SKIPDUPS && he->flags & HIST_DUP)
+ || (writeflags & HFILE_SKIPFOREIGN && he->flags & HIST_FOREIGN))
+ continue;
+ if (writeflags & HFILE_SKIPOLD) {
+ if (he->flags & HIST_OLD)
continue;
- ent->flags |= HIST_OLD;
+ he->flags |= HIST_OLD;
+ if (writeflags & HFILE_USE_OPTIONS)
+ lasthist.next_write_ev = he->histnum + 1;
}
- t = ent->text;
- if (isset(EXTENDEDHISTORY)) {
- fprintf(out, ": %ld:%ld;",
- (long)ent->stim,
- (long)ent->ftim);
+ if (writeflags & HFILE_USE_OPTIONS) {
+ lasthist.fpos = ftell(out);
+ lasthist.stim = he->stim;
+ histfile_linect++;
+ }
+ t = start = he->text;
+ if (extended_history) {
+ fprintf(out, ": %ld:%ld;", (long)he->stim,
+ he->ftim? (long)(he->ftim - he->stim) : 0L);
} else if (*t == ':')
fputc('\\', out);
@@ -1602,69 +1910,188 @@ savehistfile(char *s, int err, int app)
}
fputc('\n', out);
}
+ if (start && writeflags & HFILE_USE_OPTIONS) {
+ struct stat sb;
+ fflush(out);
+ if (fstat(fileno(out), &sb) == 0) {
+ lasthist.fsiz = sb.st_size;
+ lasthist.mtim = sb.st_mtime;
+ }
+ zsfree(lasthist.text);
+ lasthist.text = ztrdup(start);
+ }
fclose(out);
- if (app & 2 && (out = fopen(unmeta(s), "r"))) {
- char **store, buf[1024], **ptr;
- int i, l, histnum = 0;
-
- store = (char **)zcalloc((savehist + 1) * sizeof *store);
- while (fgets(buf, sizeof(buf), out)) {
- char *t;
-
- if (store[i = histnum % savehist])
- free(store[i]);
- store[i] = ztrdup(buf);
- l = strlen(buf);
- if (l > 1) {
- t = store[i] + l;
- while ((t[-1] != '\n' ||
- (t[-1] == '\n' && t[-2] == '\\')) &&
- fgets(buf, sizeof(buf), out)) {
- l += strlen(buf);
- store[i] = zrealloc(store[i], l + 1);
- t = store[i] + l;
- strcat(store[i], buf);
- }
- }
- histnum++;
- }
- fclose(out);
- if ((out = fdopen(open(unmeta(s),
- O_WRONLY | O_TRUNC | O_NOCTTY, 0600), "w"))) {
- if (histnum < savehist)
- for (i = 0; i < histnum; i++)
- fprintf(out, "%s", store[i]);
- else
- for (i = histnum; i < histnum + savehist; i++)
- fprintf(out, "%s", store[i % savehist]);
- fclose(out);
- }
- for (ptr = store; *ptr; ptr++)
- zsfree(*ptr);
- free(store);
+ if ((writeflags & (HFILE_SKIPOLD | HFILE_FAST)) == HFILE_SKIPOLD) {
+ HashTable remember_histtab = histtab;
+ Histent remember_hist_ring = hist_ring;
+ int remember_histlinect = histlinect;
+ int remember_curhist = curhist;
+
+ hist_ring = NULL;
+ curhist = histlinect = 0;
+ histsiz = savehist;
+ createhisttable(); /* sets histtab */
+
+ hist_ignore_all_dups |= isset(HISTSAVENODUPS);
+ readhistfile(fn, err, 0);
+ hist_ignore_all_dups = isset(HISTIGNOREALLDUPS);
+ savehistfile(fn, err, 0);
+ deletehashtable(histtab);
+
+ curhist = remember_curhist;
+ histlinect = remember_histlinect;
+ hist_ring = remember_hist_ring;
+ histtab = remember_histtab;
}
} else if (err)
- zerr("can't write history file %s", s, 0);
+ zerr("can't write history file %s", fn, 0);
+
+ unlockhistfile(fn);
}
+static int lockhistct;
+
/**/
int
-firsthist(void)
+lockhistfile(char *fn, int keep_trying)
{
- int ev;
- Histent ent;
+ int ct = lockhistct;
- ev = curhist - histentct + 1;
- if (ev < 1)
- ev = 1;
- do {
- ent = gethistent(ev);
- if (ent->text)
- break;
- ev++;
+ if (!fn && !(fn = getsparam("HISTFILE")))
+ return 0;
+ if (!lockhistct++) {
+ struct stat sb;
+ int fd, len = strlen(fn);
+ char *tmpfile, *lockfile;
+
+#ifdef HAVE_LINK
+ tmpfile = zalloc(len + 10 + 1);
+ sprintf(tmpfile, "%s.%ld", fn, (long)mypid);
+ if ((fd = open(tmpfile, O_RDWR|O_CREAT|O_EXCL, 0644)) >= 0) {
+ write(fd, "0\n", 2);
+ close(fd);
+ lockfile = zalloc(len + 5 + 1);
+ sprintf(lockfile, "%s.LOCK", fn);
+ while (link(tmpfile, lockfile) < 0) {
+ if (stat(lockfile, &sb) < 0) {
+ if (errno == ENOENT)
+ continue;
+ }
+ else if (keep_trying) {
+ if (time(NULL) - sb.st_mtime < 10)
+ sleep(1);
+ else
+ unlink(lockfile);
+ continue;
+ }
+ lockhistct--;
+ break;
+ }
+ free(lockfile);
+ }
+ unlink(tmpfile);
+ free(tmpfile);
+#else /* not HAVE_LINK */
+ lockfile = zalloc(len + 5 + 1);
+ sprintf(lockfile, "%s.LOCK", fn);
+ while ((fd = open(lockfile, O_CREAT|O_EXCL, 0644)) < 0) {
+ if (errno == EEXIST) continue;
+ else if (keep_trying) {
+ if (time(NULL) - sb.st_mtime < 10)
+ sleep(1);
+ continue;
+ }
+ lockhistct--;
+ break;
+ }
+ free(lockfile);
+#endif /* HAVE_LINK */
+ }
+ return ct != lockhistct;
+}
+
+/* Unlock the history file if this corresponds to the last nested lock
+ * request. If we don't have the file locked, just return.
+ */
+
+/**/
+void
+unlockhistfile(char *fn)
+{
+ if (!fn && !(fn = getsparam("HISTFILE")))
+ return;
+ if (--lockhistct) {
+ if (lockhistct < 0)
+ lockhistct = 0;
+ }
+ else {
+ char *lockfile = zalloc(strlen(fn) + 5 + 1);
+ sprintf(lockfile, "%s.LOCK", fn);
+ unlink(lockfile);
+ free(lockfile);
}
- while (ev < curhist);
- return ev;
}
+/* Get the words in the current buffer. Using the lexer. */
+
+/**/
+mod_export LinkList
+bufferwords(int *index)
+{
+ LinkList list = newlinklist();
+ int num = 0, cur = -1, got = 0, ne = noerrs, ocs = cs;
+ char *p;
+
+ zleparse = 1;
+ addedx = 0;
+ noerrs = 1;
+ lexsave();
+ if (!isfirstln && chline) {
+ p = (char *) zhalloc(hptr - chline + ll + 2);
+ memcpy(p, chline, hptr - chline);
+ memcpy(p + (hptr - chline), line, ll);
+ p[(hptr - chline) + ll] = ' ';
+ p[(hptr - chline) + ll + 1] = '\0';
+ inpush(p, 0, NULL);
+ cs += hptr - chline;
+ } else {
+ p = (char *) zhalloc(ll + 2);
+ memcpy(p, line, ll);
+ p[ll] = ' ';
+ p[ll + 1] = '\0';
+ inpush(p, 0, NULL);
+ }
+ if (cs)
+ cs--;
+ strinbeg(0);
+ noaliases = 1;
+ do {
+ ctxtlex();
+ if (tok == ENDINPUT || tok == LEXERR)
+ break;
+ if (tokstr && *tokstr) {
+ untokenize((p = dupstring(tokstr)));
+ addlinknode(list, p);
+ num++;
+ }
+ if (!got && !zleparse) {
+ got = 1;
+ cur = num - 1;
+ }
+ } while (tok != ENDINPUT && tok != LEXERR);
+ if (cur < 0 && num)
+ cur = num - 1;
+ noaliases = 0;
+ strinend();
+ inpop();
+ errflag = zleparse = 0;
+ noerrs = ne;
+ lexrestore();
+ cs = ocs;
+
+ if (index)
+ *index = cur;
+
+ return list;
+}