diff options
Diffstat (limited to 'Src')
-rw-r--r-- | Src/Modules/zftp.c | 25 | ||||
-rw-r--r-- | Src/Zle/comp.h | 16 | ||||
-rw-r--r-- | Src/Zle/compctl.c | 8 | ||||
-rw-r--r-- | Src/Zle/zle_tricky.c | 1847 | ||||
-rw-r--r-- | Src/cond.c | 60 | ||||
-rw-r--r-- | Src/exec.c | 42 | ||||
-rw-r--r-- | Src/glob.c | 2 | ||||
-rw-r--r-- | Src/params.c | 10 | ||||
-rw-r--r-- | Src/subst.c | 32 | ||||
-rw-r--r-- | Src/utils.c | 2 | ||||
-rw-r--r-- | Src/zsh.h | 5 |
11 files changed, 1796 insertions, 253 deletions
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index d71fa642f..413dd8f43 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -554,7 +554,7 @@ zfgetline(char *ln, int lnsize, int tmout) if (setjmp(zfalrmbuf)) { alarm(0); zwarnnam("zftp", "timeout getting response", NULL, 0); - return 5; + return 6; } zfalarm(tmout); @@ -676,7 +676,7 @@ zfgetmsg() int stopit, printing = 0, tmout; if (zcfd == -1) - return 5; + return 6; if (!(verbose = getsparam("ZFTP_VERBOSE"))) verbose = ""; zsfree(lastmsg); @@ -763,7 +763,7 @@ zfgetmsg() zfclose(); /* unexpected, so tell user */ zwarnnam("zftp", "remote server has closed connection", NULL, 0); - return 6; /* pretend it failed, because it did */ + return 6; } if (lastcode == 530) { /* user not logged in */ @@ -802,20 +802,21 @@ zfsendcmd(char *cmd) int ret, tmout; if (zcfd == -1) - return 5; + return 6; tmout = getiparam("ZFTP_TMOUT"); if (setjmp(zfalrmbuf)) { alarm(0); zwarnnam("zftp", "timeout sending message", NULL, 0); - return 5; + return 6; } zfalarm(tmout); ret = write(zcfd, cmd, strlen(cmd)); alarm(0); if (ret <= 0) { - zwarnnam("zftp send", "failed sending control message", NULL, 0); - return 5; /* FTP status code */ + zwarnnam("zftp send", "failure sending control message: %e", + NULL, errno); + return 6; } return zfgetmsg(); @@ -1023,11 +1024,11 @@ zfgetdata(char *name, char *rest, char *cmd, int getsize) /* accept the connection */ len = sizeof(zdsock); newfd = zfmovefd(accept(zdfd, (struct sockaddr *)&zdsock, &len)); + if (newfd < 0) + zwarnnam(name, "unable to accept data: %e", NULL, errno); zfclosedata(); - if (newfd < 0) { - zwarnnam(name, "unable to accept data.", NULL, 0); + if (newfd < 0) return 1; - } zdfd = newfd; /* this is now the actual data fd */ } else { /* @@ -1270,7 +1271,7 @@ zfread_block(int fd, char *bf, size_t sz, int tmout) n = zfread(fd, (char *)&hdr, sizeof(hdr), tmout); } while (n < 0 && errno == EINTR); if (n != 3 && !zfdrrrring) { - zwarnnam("zftp", "failed to read FTP block header", NULL, 0); + zwarnnam("zftp", "failure reading FTP block header", NULL, 0); return n; } /* size is stored in network byte order */ @@ -1324,7 +1325,7 @@ zfwrite_block(int fd, char *bf, size_t sz, int tmout) n = zfwrite(fd, (char *)&hdr, sizeof(hdr), tmout); } while (n < 0 && errno == EINTR); if (n != 3 && !zfdrrrring) { - zwarnnam("zftp", "failed to write FTP block header", NULL, 0); + zwarnnam("zftp", "failure writing FTP block header", NULL, 0); return n; } bfptr = bf; diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 124ae454a..0d5419817 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -261,15 +261,19 @@ struct cline { int llen; /* length of line */ char *word; /* prefered string to insert */ int wlen; /* length of word */ - Cmatcher matcher; /* which matcher was used */ int flags; /* see CLF_* below */ + Cline prefix; /* prefix we've build for new parts */ + Cline suffix; /* suffix we've build for new parts */ }; -#define CLF_END 1 -#define CLF_MID 2 -#define CLF_MISS 4 -#define CLF_DIFF 8 -#define CLF_SUF 16 +#define CLF_END 1 +#define CLF_MID 2 +#define CLF_MISS 4 +#define CLF_DIFF 8 +#define CLF_SUF 16 +#define CLF_NEW 32 +#define CLF_VAR 64 +#define CLF_JOIN 128 /* Flags for makecomplist*(). Things not to do. */ diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index cec71fc5b..e3b778508 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1685,6 +1685,10 @@ bin_compadd(char *name, char **argv, char *ops, int func) return 1; } for (; *argv && **argv == '-'; argv++) { + if (!(*argv)[1]) { + argv++; + break; + } for (p = *argv + 1; *p; p++) { sp = NULL; dm = 0; @@ -1945,9 +1949,9 @@ restrict_range(int b, int e) pparams = p; zsfree(compcontext); if ((compcurrent -= b + 1)) - compcontext = ztrdup("arg"); + compcontext = ztrdup("argument"); else - compcontext = ztrdup("cmd"); + compcontext = ztrdup("command"); } /**/ diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index f1078ef11..af78e1c06 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -98,12 +98,12 @@ static int menupos, menulen, menuend, menuwe, menuinsc; /* This is for completion inside a brace expansion. brbeg and brend hold * * strings that were temporarily removed from the string to complete. * - * brpl and brsl hold the offset of these strings. * + * brpl and brsl, brbsl hold the offset of these strings. * * brpcs and brscs hold the positions of the re-inserted string in the * * line. */ static char *brbeg = NULL, *brend = NULL; -static int brpl, brsl, brpcs, brscs; +static int brpl, brsl, brbsl, brpcs, brscs; /* The list of matches. fmatches contains the matches we first ignore * * because of fignore. */ @@ -212,6 +212,12 @@ static LinkList ccstack; static Cmlist mstack; +/* The completion matchers used when building new stuff for the line. */ + +static struct cmlist bmstack0, bmstack1; +static Cmlist bmstack; +static int had_lm; + /* A list with references to all matcher we used. */ static LinkList matchers; @@ -287,6 +293,12 @@ enum { COMP_COMPLETE, static int lastambig; +/* Non-zero if the string on the line came from a previous completion. * + * This is used to detect if the string should be taken as an exact * + * match (see do_ambiguous()). */ + +static int fromcomp; + /**/ void completecall(void) @@ -1356,6 +1368,7 @@ get_comp_string(void) if (*p == Outbrace) chuck(p); brsl = strlen(s) - (p - s); + brbsl = p - s; brend[sl] = '\0'; } /* we are still waiting for an outbrace and maybe commas */ @@ -1469,7 +1482,9 @@ addtoword(char **rwp, int *rwlenp, char *nw, { int al, rwlen = *rwlenp, nl; char *as, *rw = *rwp; - + + /* Get the string and length to insert: either from the line + * or from the match. */ if (m && (m->flags & CMF_LINE)) { al = m->llen; as = l; @@ -1477,6 +1492,7 @@ addtoword(char **rwp, int *rwlenp, char *nw, al = wl; as = w; } + /* Allocate some more space if needed. */ if (!rwlen || (nl = al + (nw - rw)) >= rwlen) { char *np; @@ -1496,6 +1512,7 @@ addtoword(char **rwp, int *rwlenp, char *nw, *rwp = rw = np; rwlen = nl; } + /* Copy it in the buffer. */ if (prep) { memmove(rw + al, rw, rwlen - al); memcpy(rw, as, al); @@ -1506,13 +1523,15 @@ addtoword(char **rwp, int *rwlenp, char *nw, return nw + al; } -/* This get a new Cline structure. */ +/* This returns a new Cline structure. */ static Cline -getcline(char *l, int ll, char *w, int wl, Cmatcher m, int fl) +getcline(char *l, int ll, char *w, int wl, int fl) { Cline r; + /* Preverably take it from the buffer list (freecl), if there + * is none, allocate a new one. */ if ((r = freecl)) freecl = r->next; else @@ -1523,19 +1542,20 @@ getcline(char *l, int ll, char *w, int wl, Cmatcher m, int fl) r->llen = ll; r->word = w; r->wlen = wl; - r->matcher = m; r->flags = fl; + r->prefix = r->suffix = NULL; return r; } -/* This add a Cline structure with the given parameters. */ +/* This adds a Cline structure with the given parameters to the list we + * are building during matching. */ static void addtocline(Cline *retp, Cline *lrp, char *l, int ll, char *w, int wl, Cmatcher m, int fl) { - Cline ln = getcline(l, ll, w, wl, m, fl); + Cline ln = getcline(l, ll, w, wl, fl); if (m && (m->flags & CMF_LINE)) ln->word = NULL; @@ -1547,8 +1567,1140 @@ addtocline(Cline *retp, Cline *lrp, *lrp = ln; } +/* When building up cline lists that are to be inserted at the end of the + * string or on the left hand side in the middle, we do this separately for + * multiple runs of characters separated by the anchors of `*' match patterns. + * This function builds such a list from the given string. */ + +static Cline +end_list(int len, char *str) +{ + Cline ret = NULL, *q = &ret; + Cmlist ms; + Cmatcher mp; + int t; + char *p = str; + + while (len) { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (mp->flags == CMF_RIGHT && mp->wlen == -1 && + !mp->llen && len >= mp->ralen && mp->ralen && + pattern_match(mp->right, str, NULL, NULL)) { + /* This is one of those patterns, so add a cline struct. + * We store the anchor string in the line and the contents + * (i.e. the strings between the anchors) in the word field. */ + *q = getcline(str, mp->ralen, p, str - p, 0); + q = &((*q)->next); + str += mp->ralen; + len -= mp->ralen; + p = str; + t = 1; + } + } + } + if (!t) { + str++; + len--; + } + } + /* This is the cline struct for the remaining string at the end. */ + if (p != str) { + *q = getcline("", 0, p, str - p, 0); + q = &((*q)->next); + } + *q = NULL; + + return ret; +} + +/* This builds a string that may be put on the line that fully matches the + * given strings. The return value is NULL if no such string could be built + * or that string, allocated on the heap. + * All this is a lot like the procedure in bld_new_pfx(), only slightly + * simpler, see that function for more comments. */ + +static char * +join_strs(int la, char *sa, int lb, char *sb) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + static char *line = NULL; + static int llen = 0; + static char *rs = NULL; + static int rl = 0; + + Cmlist ms; + Cmatcher mp; + int t, bl, rr = rl; + char *rp = rs; + + /* This is a buffer for the characters to use for pairs of correspondence + * classes. */ + if (la + 1 > ealen || lb + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = (la > lb ? la : lb) + 20); + } + while (la && lb) { + if (*sa != *sb) { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= la && mp->wlen <= lb) { + if (pattern_match(mp->word, sa, NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_pfx(mp->line, line, line, + lb, sb, ea))) { + line[mp->llen] = '\0'; + if (rr <= mp->llen) { + char *or = rs; + + rs = realloc(rs, (rl += 20)); + rr += 20; + rp += rs - or; + } + memcpy(rp, line, mp->llen); + rp += mp->llen; + rr -= mp->llen; + sa += mp->wlen; + sb += bl; + la -= mp->wlen; + lb -= bl; + t = 1; + } + } else if (pattern_match(mp->word, sb, NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_pfx(mp->line, line, line, + la, sa, ea))) { + line[mp->llen] = '\0'; + if (rr <= mp->llen) { + char *or = rs; + + rs = realloc(rs, (rl += 20)); + rr += 20; + rp += rs - or; + } + memcpy(rp, line, mp->llen); + rp += mp->llen; + rr -= mp->llen; + sa += bl; + sb += mp->wlen; + la -= bl; + lb -= mp->wlen; + t = 1; + } + } + } + } + } + if (!t) + break; + } else { + if (rr <= mp->llen) { + char *or = rs; + + rs = realloc(rs, (rl += 20)); + rr += 20; + rp += rs - or; + } + *rp++ = *sa; + rr--; + sa++; + sb++; + la--; + lb--; + } + } + if (la || lb) + return NULL; + + *rp = '\0'; + + return dupstring(rs); +} + +/* This gets two cline lists with separated runs and joins the corresponding + * elements in these lists. In olp and nlp we return the lengths of the matched + * portions in o and n respectively. As always the list in o is the one that + * contains the joined result after this function. */ + +static Cline +join_ends(Cline o, Cline n, int *olp, int *nlp) +{ + Cline ret = o; + int mol, mnl, smol = 0, smnl = 0; + char *j; + + while (o && n) { + if (o->llen == n->llen && !strncmp(o->line, n->line, o->llen)) { + /* The anchors are the same, so join the contents. */ + bld_pfx(o, n, &mol, &mnl); + smol += mol + o->llen; + smnl += mnl + n->llen; + } else if (!(o->flags & CLF_JOIN) && + (j = join_strs(o->llen, o->line, n->llen, n->line))) { + /* We could build a string matching both anchors, so use that + * and mark the cline so that we don't try to join it again. */ + o->flags |= CLF_JOIN; + o->llen = strlen(j); + o->line = j; + bld_pfx(o, n, &mol, &mnl); + smol += mol + o->llen; + smnl += mnl + n->llen; + } else { + /* Different anchor strings, so shorten the list. */ + bld_pfx(o, n, &mol, &mnl); + smol += mol; + smnl += mnl; + o->flags |= CLF_MISS; + o->next = NULL; + o->llen = 0; + if (olp) + *olp = smol; + if (nlp) + *nlp = smnl; + return ret; + } + /* If we reached the end of the new list but not the end of the old + * list, we mark the old list (saying that characters are missing here). */ + if (!(n = n->next) && o->next) + o->flags |= CLF_MISS; + o = o->next; + } + if (o) { + /* Shorten the list if we haven't reached the end. */ + if (n) + o->flags |= CLF_MISS; + o->next = NULL; + o->llen = 0; + } + if (olp) + *olp = smol; + if (nlp) + *nlp = smnl; + return ret; +} + +/* This builds all the possible line patterns for the pattern pat in the + * buffer line. Initially line is the same as lp, but during recursive + * calls lp is incremented for storing successive characters. Whenever + * a full possible string is build, we test if this line matches the + * string given by wlen and word. The last argument contains the characters + * to use for the correspondence classes, it was filled by a call to + * pattern_match() in the calling function. + * The return value is the length of the string matched in the word, it + * is zero if we couldn't build a line that matches the word. */ + +/**/ +static int +bld_line_pfx(Cpattern pat, char *line, char *lp, + int wlen, char *word, unsigned char *in) +{ + if (pat) { + /* Still working on the pattern. */ + + int i, l; + unsigned char c = 0; + + /* Get the number of the character for a correspondence class + * if it has a correxponding class. */ + if (pat->equiv) + if ((c = *in)) + in++; + + /* Walk through the table in the pattern and try the characters + * that may appear in the current position. */ + for (i = 0; i < 256; i++) + if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) { + *lp = i; + /* We stored the character, now call ourselves to build + * the rest. */ + if ((l = bld_line_pfx(pat->next, line, lp + 1, + wlen, word, in))) + return l; + } + } else { + /* We reached the end, i.e. the line string is fully build, now + * see if it matches the given word. */ + static unsigned char *ea = NULL; + static int ealen = 0; + + Cmlist ms; + Cmatcher mp; + int l = lp - line, t, rl = 0; + + /* Quick test if the strings are exactly the same. */ + if (l == wlen && !strncmp(line, word, l)) + return l; + + /* We need another buffer for correspondence classes. */ + if (l + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = l + 20); + } + /* We loop through the whole line string built. */ + while (l && wlen) { + if (*word == *line) { + /* The same character in both strings, skip over. */ + line++; + word++; + l--; + wlen--; + rl++; + } else { + t = 0; + for (ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && + pattern_match(mp->line, line, NULL, ea) && + pattern_match(mp->word, word, ea, NULL)) { + /* Both the line and the word pattern matched, + * now skip over the matched portions. */ + line += mp->llen; + word += mp->wlen; + l -= mp->llen; + wlen -= mp->wlen; + rl += mp->wlen; + t = 1; + } + } + } + if (!t) + /* Didn't match, give up. */ + return 0; + } + } + if (!l) + /* Unmatched portion in the line built, return no-match. */ + return rl; + } + return 0; +} + +/* This builds a list of cline structs describing a string to insert in + * the line in a place where we didn't had something on the line when + * completion was invoked. This is called after we get the second match + * so we have two strings to start with given by (ol,ow) and (nl,nw) and + * the cline list returned describes a string that matches both of these + * strings. + * In olp and nlp we return the number of characters in ow and nw that + * are not matched by the cline list returned. Especially this means that + * they are non-zero if there are such unmatched portions. + * In lp we return a pointer to the last cline struct created. */ + +static Cline +bld_new_pfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + static char *line = NULL; + static int llen = 0; + + Cmlist ms; + Cmatcher mp; + Cline ret = NULL, l = NULL, n; + char *p = ow; + int t, bl; + + /* This is a buffer for the characters to use for pairs of correspondence + * classes. */ + if (ol + 1 > ealen || nl + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = (ol > nl ? ol : nl) + 20); + } + /* Loop through the strings. */ + while (ol && nl) { + if (*ow != *nw) { + /* Not the same character, use the patterns. */ + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + /* We use only those patterns that match a non-empty + * string in both the line and the word, that match + * strings no longer than the string we still have + * to compare, that don't have anchors, and that don't + * use the line strings for insertion. */ + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= ol && mp->wlen <= nl) { + /* Try the pattern only if the word-pattern matches + * one of the strings. */ + if (pattern_match(mp->word, ow, NULL, ea)) { + /* Make the buffer where we build the possible + * line patterns big enough. */ + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + /* Then build all the possible lines and see + * if one of them matches the othe string. */ + if ((bl = bld_line_pfx(mp->line, line, line, + nl, nw, ea))) { + /* Yep, one of the lines matched the other + * string. */ + if (p != ow) { + /* There is still a substring that is + * the same in both strings, so add + * a cline for it. */ + char sav = *ow; + + *ow = '\0'; + n = getcline(NULL, 0, dupstring(p), + ow - p, 0); + *ow = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + /* Then we add the line string built. */ + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow += mp->wlen; + nw += bl; + ol -= mp->wlen; + nl -= bl; + p = ow; + t = 1; + } + } else if (pattern_match(mp->word, nw, NULL, ea)) { + /* Now do the same for the other string. */ + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_pfx(mp->line, line, line, + ol, ow, ea))) { + if (p != ow) { + char sav = *ow; + + *ow = '\0'; + n = getcline(NULL, 0, dupstring(p), + ow - p, 0); + *ow = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow += bl; + nw += mp->wlen; + ol -= bl; + nl -= mp->wlen; + p = ow; + t = 1; + } + } + } + } + } + if (!t) + /* No pattern matched, so give up. */ + break; + } else { + /* Same character, skip over. */ + ow++; + nw++; + ol--; + nl--; + } + } + if (p != ow) { + /* There is a equal substring in both strings, build a cline + * for it. */ + char sav = *ow; + + *ow = '\0'; + n = getcline(NULL, 0, dupstring(p), ow - p, 0); + *ow = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + if (l) + l->next = NULL; + else + ret = NULL; + + if (olp) + *olp = ol; + if (nlp) + *nlp = nl; + if (lp) + *lp = l; + + return ret; +} + +/* Given a cline list for an unmatched part of the string to insert in the + * line and a new match substring, modify the cline list so that it also + * matches this string. The cline list is shortened in the place where + * we can't build a cline matching the new string. + * However, the resulting cline list is returned. The string is described + * by len and word. In missp we return non-zero if the cline list returned + * had to be shortened (and hence doesn't fully match the strings it was + * built from anymore) or if it doesn't fully match the given string. + * This function checks the string left to right and thus is to be used + * for strings where we want a common prefix. */ + +static Cline +join_new_pfx(Cline line, int len, char *word, int *missp) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + + Cline ret = NULL, l = NULL, next; + int miss = 0; + + /* Walk through the list and the string. */ + while (line && len) { + next = line->next; + /* The line element is used in those places where a new line + * string was built. */ + if (line->line) { + Cmlist ms; + Cmatcher mp; + int ll = line->llen, t; + char *p = line->line; + + /* Make the buffer for the correspondence classes big enough. */ + if (line->llen + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = line->llen + 20); + } + /* Check if the line string from the cline list matches the + * new string. */ + while (ll && len) { + if (*p == *word) { + p++; + word++; + ll--; + len--; + } else { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= len && mp->llen <= len && + pattern_match(mp->word, word, NULL, ea) && + pattern_match(mp->line, p, ea, NULL)) { + /* We have a matched substring, skip over. */ + p += mp->llen; + word += mp->wlen; + ll -= mp->llen; + len -= mp->wlen; + t = 1; + } + } + } + if (!t) + /* No match, give up. */ + break; + } + } + if (p == line->line) { + /* We couldn't match any character from the string in the + * cline, so shorten the list and don't even keep this + * struct. */ + miss = 1; + len = 0; + } else { + /* At least the beginning of the cline string can be used. */ + if (ll) { + /* But there is a portion of the string that can't be + * used, so we have to shorten the list. */ + miss = 1; + *p = '\0'; + line->llen -= ll; + len = 0; + } + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + } + } else { + /* The cline doesn't have a string built by reverse matching, + * so we have to work on the original substring in the cline + * and the new string. */ + if (line->wlen == len && strncmp(line->word, word, len)) { + /* They are equal, accept and return. If there was + * another element in the list, shorten the list. */ + if (next) + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else { + char sav = word[len]; + + /* Check if one is the prefix of the other one. */ + word[len] = '\0'; + if (strpfx(word, line->word)) { + word[len] = sav; + + line->word[len] = '\0'; + line->wlen = len; + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else if (strpfx(line->word, word)) { + word[len] = sav; + + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else { + /* Not the same and no prefix, so we try to build a + * new line string matching the string in the cline + * and the new string. */ + int mol, mnl; + Cline sl, send; + + word[len] = sav; + + if ((sl = bld_new_pfx(line->wlen, line->word, + len, word, &mol, &mnl, &send))) { + /* We could build such a string, use it in the + * cline structure. */ + if (l) + l->next = sl; + else + ret = sl; + if (!mol) { + send->next = next; + word += len - mnl; + len = mnl; + } else + len = 0; + l = send; + } else + len = 0; + } + } + } + line = next; + } + *missp = (line || len || miss); + + return ret; +} + +/* This function gets two cline structs for which we want to build a + * common prefix to put on the line. The result is placed in the cline + * struct given as first argument. + * In olp and nlp we return the matched lengths for o and n, respectively + * (but this is only guaranteed to give correct results if this is the + * first call for the given o and n). */ + +/**/ +static void +bld_pfx(Cline o, Cline n, int *olp, int *nlp) +{ + if (o->flags & CLF_NEW) { + if (o->flags & (CLF_END | CLF_MID)) + /* We split the suffix in the middle and at the end into + * separate runs. */ + o->prefix = join_ends(o->prefix, end_list(n->wlen, n->word), + NULL, NULL); + else { + /* This flag is set if we already built a cline list for such + * a prefix. We join it with the string from the other cline + * struct. */ + int miss; + + o->prefix = join_new_pfx(o->prefix, n->wlen, n->word, &miss); + if (miss) + o->flags |= CLF_MISS; + } + } else if (o->flags & (CLF_END | CLF_MID)) { + o->flags |= CLF_NEW; + o->prefix = join_ends(end_list(o->wlen, o->word), + end_list(n->wlen, n->word), olp, nlp); + } else if (o->wlen && n->wlen) { + /* We haven't built a cline list for it yet. */ + char so = o->word[o->wlen], sn = n->word[n->wlen]; + char *new = o->word; + int newl = o->wlen, mol, mnl; + + /* If one of the strings is a prefix of the other one, just keep + * that prefix. */ + o->word[o->wlen] = n->word[n->wlen] = '\0'; + if (strpfx(n->word, o->word)) { + new = dupstring(n->word); + newl = n->wlen; + if (olp) + *olp = *nlp = n->wlen; + } else if (strpfx(o->word, n->word)) { + if (olp) + *olp = *nlp = o->wlen; + } else { + /* Otherwise build a cline list describing a string that + * matches both strings from the original cline structs + * and thus can be put in the command line to represent + * them. This cline list is stored in o. */ + o->flags |= CLF_NEW; + o->prefix = bld_new_pfx(o->wlen, o->word, n->wlen, n->word, + &mol, &mnl, NULL); + newl = 0; + new = ""; + if (mol || mnl) + o->flags |= CLF_MISS; + if (olp) { + *olp = o->wlen - mol; + *nlp = n->wlen - mnl; + } + } + o->word[o->wlen] = so; + n->word[n->wlen] = sn; + + o->word = new; + o->wlen = newl; + + if (!o->prefix && n->wlen != o->wlen) + o->flags |= CLF_MISS; + } +} + +/* The following function are like their counterparts above, only for + * the other direction. */ + +static int +bld_line_sfx(Cpattern pat, char *line, char *lp, + int wlen, char *word, unsigned char *in) +{ + if (pat) { + int i, l; + unsigned char c = 0; + + if (pat->equiv) + if ((c = *in)) + in++; + + for (i = 0; i < 256; i++) + if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) { + *lp = i; + if ((l = bld_line_pfx(pat->next, line, lp + 1, + wlen, word, in))) + return l; + } + } else { + static unsigned char *ea = NULL; + static int ealen = 0; + + Cmlist ms; + Cmatcher mp; + int l = lp - line, t, rl = 0; + + if (l == wlen && !strncmp(line, word, l)) + return l; + + line = lp; + word += wlen; + + if (l + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = l + 20); + } + while (l && wlen) { + if (word[-1] == line[-1]) { + line--; + word--; + l--; + wlen--; + rl++; + } else { + t = 0; + for (ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && + pattern_match(mp->line, line - mp->llen, + NULL, ea) && + pattern_match(mp->word, word - mp->wlen, + ea, NULL)) { + line -= mp->llen; + word -= mp->wlen; + l -= mp->llen; + wlen -= mp->wlen; + rl += mp->wlen; + t = 1; + } + } + } + if (!t) + return 0; + } + } + if (!l) + return rl; + } + return 0; +} + +static Cline +bld_new_sfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + static char *line = NULL; + static int llen = 0; + + Cmlist ms; + Cmatcher mp; + Cline ret = NULL, l = NULL, n; + char *p; + int t, bl; + + ow += ol; + nw += nl; + p = ow; + + if (ol + 1 > ealen || nl + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc((ealen = (ol > nl ? ol : nl) + 20)); + } + while (ol && nl) { + if (ow[-1] != nw[-1]) { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= ol && mp->wlen <= nl) { + if (pattern_match(mp->word, ow - mp->wlen, + NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_sfx(mp->line, line, line, + nl, nw, ea))) { + if (p != ow) { + char sav = *p; + + *p = '\0'; + n = getcline(NULL, 0, dupstring(ow), + p - ow, 0); + *p = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow -= mp->wlen; + nw -= bl; + ol -= mp->wlen; + nl -= bl; + p = ow; + t = 1; + } + } else if (pattern_match(mp->word, nw - mp->wlen, + NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_sfx(mp->line, line, line, + ol, ow, ea))) { + if (p != ow) { + char sav = *p; + + *p = '\0'; + n = getcline(NULL, 0, dupstring(ow), + p - ow, 0); + *p = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow -= bl; + nw -= mp->wlen; + ol -= bl; + nl -= mp->wlen; + p = ow; + t = 1; + } + } + } + } + } + if (!t) + break; + } else { + ow--; + nw--; + ol--; + nl--; + } + } + if (p != ow) { + char sav = *p; + + *p = '\0'; + n = getcline(NULL, 0, dupstring(ow), p - ow, 0); + *p = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + if (l) + l->next = NULL; + else + ret = NULL; + + if (olp) + *olp = ol; + if (nlp) + *nlp = nl; + if (lp) + *lp = l; + + return ret; +} + +static Cline +join_new_sfx(Cline line, int len, char *word, int *missp) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + + Cline ret = NULL, l = NULL, next; + int miss = 0, ind = 0; + + word += len; + + while (line && len) { + next = line->next; + if (line->line) { + Cmlist ms; + Cmatcher mp; + int ll = line->llen, t; + char *p = line->line + ll; + + if (line->llen + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = line->llen + 20); + } + while (ll && len) { + if (p[-1] == word[-1]) { + p--; + word--; + ll--; + len--; + ind++; + } else { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= len && mp->llen <= len && + pattern_match(mp->word, word - mp->wlen, + NULL, ea) && + pattern_match(mp->line, p - mp->llen, + ea, NULL)) { + p -= mp->llen; + word -= mp->wlen; + ll -= mp->llen; + len -= mp->wlen; + ind += mp->wlen; + t = 1; + } + } + } + if (!t) + break; + } + } + if (p == line->line + line->llen) { + miss = 1; + len = 0; + } else { + if (ll) { + miss = 1; + line->line = p; + line->llen -= ll; + len = 0; + } + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + } + } else { + if (line->wlen == len && strncmp(line->word, word - len, len)) { + if (next) + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else { + char sav = word[ind]; + + word[ind] = '\0'; + if (strpfx(word - len, line->word)) { + word[ind] = sav; + + line->word += line->wlen - len; + line->wlen = ind; + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else if (strpfx(line->word, word - len)) { + word[ind] = sav; + + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else { + int mol, mnl; + Cline sl, send; + + word[len] = sav; + + if ((sl = bld_new_sfx(line->wlen, line->word, + len, word - len, + &mol, &mnl, &send))) { + if (l) + l->next = sl; + else + ret = sl; + if (!mol) { + send->next = next; + word -= len - mnl; + len = mnl; + } else + len = 0; + l = send; + } else + len = 0; + } + } + } + line = next; + } + *missp = (line || len || miss); + + return ret; +} + +/**/ +static void +bld_sfx(Cline o, Cline n) +{ + if (o->flags & CLF_NEW) { + int miss; + + o->suffix = join_new_sfx(o->suffix, n->wlen, n->word, &miss); + if (miss) + o->flags |= CLF_MISS; + } else { + char so = o->word[o->wlen], sn = n->word[n->wlen]; + char *new = o->word; + int newl = o->wlen, mol, mnl; + + o->word[o->wlen] = n->word[n->wlen] = '\0'; + if (strpfx(n->word, o->word)) { + new = dupstring(n->word); + newl = n->wlen; + } else if (!strpfx(o->word, n->word)) { + o->flags |= CLF_NEW; + o->suffix = bld_new_sfx(o->wlen, o->word, n->wlen, n->word, + &mol, &mnl, NULL); + newl = 0; + new = ""; + if (mol || mnl) + o->flags |= CLF_MISS; + } + o->word[o->wlen] = so; + n->word[n->wlen] = sn; + + o->word = new; + o->wlen = newl; + + if (!o->suffix && n->wlen != o->wlen) + o->flags |= CLF_MISS; + } +} + /* Joins two Cline lists, building the most specific line string * - * that is possible. */ + * that is possible and returns it. This is done by modifying the + * cline list given as the first argument. */ static Cline join_clines(Cline o, Cline n) @@ -1556,12 +2708,21 @@ join_clines(Cline o, Cline n) Cline oo = o; if (!o) + /* This is the first time this was called, so just return the + * the second list. In future calls we will get this list as + * the first argument. */ return n; else { Cline f = freecl, q, op = NULL; int ol, nl; + freecl = NULL; + while (o && n) { + /* CLF_MID is set in the cline struct where the prefix and the + * suffix from the line meet. If we have reached the cline + * for it in one of the lists, search the corresponding + * cline in the other list, removing all structs up to it. */ if (o->flags & CLF_MID) { while (n && !(n->flags & CLF_MID)) { q = n->next; @@ -1606,64 +2767,52 @@ join_clines(Cline o, Cline n) if (!o || !n) break; if (o->flags & CLF_MID) { - char so, sn, *os, *ns; - int ol = o->llen, l, lo, ln; - - so = o->line[o->llen]; - sn = n->line[n->llen]; - o->line[o->llen] = '\0'; - n->line[n->llen] = '\0'; - l = pfxlen(o->line, n->line); - if (l != o->llen) - o->flags |= CLF_MISS; - o->line[o->llen] = so; - n->line[n->llen] = sn; - o->llen = l; - if ((lo = o->wlen) < 0) { - os = o->line + l; - lo = ol - l; - } else - os = o->word; - ns = n->line + l; - ln = n->llen - l; - so = os[lo]; - sn = ns[ln]; - os[lo] = '\0'; - ns[ln] = '\0'; - l = sfxlen(os, ns); - os[lo] = so; - ns[ln] = sn; - o->word = os + lo - l; - o->wlen = l; + /* These are the structs in the middle, so simplify the + * prefix and the suffix in it to their longest common + * versions. */ + + char *os, *ns, *oss = o->line, *nss = n->line; + int ol, nl, mol, mnl, oll = o->llen, nll = n->llen; + + os = o->word; + ol = o->wlen; + ns = n->word; + nl = n->wlen; + + o->word = o->line; + o->wlen = o->llen; + n->word = n->line; + n->wlen = n->llen; + bld_pfx(o, n, &mol, &mnl); + o->line = o->word; + o->llen = o->wlen; + + o->word = os; + o->wlen = ol; + n->word = ns; + n->wlen = nl; + + if (o->wlen < 0) { + o->word = oss + mol; + o->wlen = oll - mol; + } + if (n->wlen < 0) { + n->word = nss + mnl; + n->wlen = nll - mnl; + } + bld_sfx(o, n); } else if (o->word) { if (n->word) { - if (o->matcher == n->matcher && - (o->flags & CLF_SUF) == (n->flags & CLF_SUF) && - ((o->flags & CLF_END) || o->matcher->wlen < 0)) { - char so = o->word[o->wlen]; - char sn = n->word[n->wlen]; - int l; - - o->word[o->wlen] = '\0'; - n->word[n->wlen] = '\0'; - - /* We have two chunks from `*' patterns. * + if (o->llen == n->llen && + (o->flags & CLF_VAR) && (n->flags & CLF_VAR) && + (o->flags & (CLF_END | CLF_SUF)) == + (n->flags & (CLF_END | CLF_SUF))) { + /* We have two chunks from `*' patterns, * reduce them to the common prefix/suffix. */ - if (o->flags & CLF_SUF) { - l = sfxlen(o->word, n->word); - o->word[o->wlen] = so; - n->word[n->wlen] = sn; - o->word += o->wlen - l; - } else { - l = pfxlen(o->word, n->word); - o->word[o->wlen] = so; - n->word[n->wlen] = sn; - o->word = dupstring(o->word); - o->word[l] = '\0'; - } - o->wlen = l; - if (l < o->wlen || l < n->wlen) - o->flags |= CLF_MISS; + if (o->flags & CLF_SUF) + bld_sfx(o, n); + else + bld_pfx(o, n, NULL, NULL); } else if (o->wlen == n->wlen) { /* Otherwise keep them if they are equal. */ if (strncmp(o->word, n->word, o->wlen)) { @@ -1694,6 +2843,8 @@ join_clines(Cline o, Cline n) o = o->next; } if (o) { + /* The old list has elements not matched by the second list + * we put all these elements in the free list. */ for (q = o; q->next; q = q->next); q->next = f; @@ -1717,7 +2868,8 @@ join_clines(Cline o, Cline n) return oo; } -/* This returns a Cline for the given string. */ +/* This returns a Cline for the given string. In lp we return a pointer to + * the last cline struct created. */ static Cline str_cline(char *s, int l, Cline *lp) @@ -1726,8 +2878,10 @@ str_cline(char *s, int l, Cline *lp) if (l < 0) l = strlen(s); + /* We build one struct per character, this makes joining it faster + * and easier. */ while (l) { - *p = n = getcline(s, 1, NULL, 0, NULL, 0); + *p = n = getcline(s, 1, NULL, 0, 0); p = &(n->next); s++; @@ -1739,7 +2893,8 @@ str_cline(char *s, int l, Cline *lp) return r; } -/* This reverts the order of the chunks. */ +/* This reverts the order of the elements of the given cline list and + * returns a pointer to the new head. */ static Cline revert_clines(Cline p) @@ -1755,7 +2910,7 @@ revert_clines(Cline p) return r; } -/* Prepend a string to a Cline. */ +/* Prepend a string to a Cline and return a pointer to the new cline list. */ static Cline prepend_cline(char *s, Cline l) @@ -1764,7 +2919,7 @@ prepend_cline(char *s, Cline l) while (l) { *p = n = getcline(l->line, l->llen, l->word, l->wlen, - l->matcher, l->flags); + l->flags); p = &(n->next); l = l->next; @@ -1772,8 +2927,8 @@ prepend_cline(char *s, Cline l) return r; } -/* This simplifies the Cline to match the given strings. The strings are: * - * the common prefix and its length, a string with the suffix at the end * +/* This simplifies the Cline to match the given strings. The strings are: + * the common prefix and its length, a string with the suffix at the end * and the suffix length. */ static void @@ -1784,6 +2939,8 @@ merge_cline(Cline lc, char *p, int pl, char *s, int sl, int psl) pll = strlen(p); if (s) { + /* If there is a suffix, get a pointer to the beginning of the + * common suffix. */ int i = strlen(s); if (ainfo->suflen < 10000) @@ -1800,44 +2957,36 @@ merge_cline(Cline lc, char *p, int pl, char *s, int sl, int psl) pll -= pl; + /* Now build a cline list from the string(s) and join it with the + * cline list we have built while testing possible matches. */ l = str_cline(p, pl, &n); q = &(n->next); if (!sl) - *q = getcline(NULL, 0, p + pl, pll, NULL, CLF_END); + *q = getcline(NULL, 0, p + pl, pll, CLF_END | CLF_VAR); else { - *q = n = getcline(p + pl, pll, s, sll, NULL, CLF_MID); + *q = n = getcline(p + pl, pll, s, sll, CLF_MID); n->next = str_cline(s + sll, sl, NULL); } join_clines(lc, l); } -/* This inserts the Cline built into the command line. */ +/* This inserts the Cline built into the command line. The last two + * arguments are the relative positions where the begining and the + * end of the brace expansion strings have to be re-inserted. */ static void inst_cline(Cline l, int pl, int sl) { int m = -1, d = -1, b = -1, hb = 0, i = 0; - Cline p = l; - - if (brend && *brend) { - while (p) { - if (l->flags & CLF_MID) { - i += l->llen; - if (l->wlen >= 0) - i += l->wlen; - } else - i += l->llen; - p = p->next; - } - sl = i - (brsl + sl) - 1; - } - else - sl = -1; + /* Adjust the position of the braces. */ pl += brpl; + sl += brbsl; i = cs - wb; + + /* Insert the brace strings now? */ if (pl >= 0 && i >= pl && brbeg && *brbeg) { inststrlen(brbeg, 1, -1); pl = -1; @@ -1848,37 +2997,140 @@ inst_cline(Cline l, int pl, int sl) sl = -1; hb = 1; } + /* Walk the list. */ while (l) { + /* If this cline describes a suffix where something is missing + * for some matches, remember the position. */ if (m < 0 && (l->flags & (CLF_MISS | CLF_SUF)) == (CLF_MISS | CLF_SUF)) m = cs; - if (l->flags & CLF_MID) { - inststrlen(l->line, 1, l->llen); - if (b < 0) + + if (l->prefix) { + Cline sl = l->prefix, ssl; + int hm; + + if (l->flags & (CLF_END | CLF_MID)) { + /* At the end and in the middle the suffix we have + * separate runs. */ + for (; sl; sl = sl->next) { + hm = 0; + if (sl->prefix) { + for (ssl = sl->prefix; ssl; ssl = ssl->next) { + if (ssl->line) + /* line for a string derived from applying + * the matchers the other way round. */ + inststrlen(ssl->line, 1, ssl->llen); + else + /* and word for substrings equal in all + * matches. */ + inststrlen(ssl->word, 1, ssl->wlen); + /* If the string differs from any of the + * matches, remember the position. */ + if (d < 0 && (ssl->flags & CLF_DIFF)) + d = cs; + } + } else if (sl->wlen) + inststrlen(sl->word, 1, sl->wlen); + + if (m < 0 && (sl->flags & CLF_MISS)) + m = cs; + if (sl->llen) + inststrlen(sl->line, 1, sl->llen); + hm = (sl->flags & CLF_MISS); + } + if ((l->flags & CLF_MID) &&hm && b < 0) { + b = cs; + hb = 1; + } + } else { + /* The cline contains a newly build part of the string + * in a sub-list. */ + for (; sl; sl = sl->next) { + if (sl->line) + /* line for a string derived from applying the + * matchers the other way round. */ + inststrlen(sl->line, 1, sl->llen); + else + /* and word for substrings equal in all matches. */ + inststrlen(sl->word, 1, sl->wlen); + /* If the string differs from any of the matches, + * remember the position. */ + if (d < 0 && (sl->flags & CLF_DIFF)) + d = cs; + } + } + if (!(l->flags & (CLF_END | CLF_MID))) + i += l->llen; + } + if (l->suffix) { + Cline sl = revert_clines(l->suffix); + + if ((sl->flags & CLF_MISS) && b < 0) { b = cs; - if (l->wlen > 0) + hb = 1; + } + for (; sl; sl = sl->next) { + if (sl->line) + inststrlen(sl->line, 1, sl->llen); + else + inststrlen(sl->word, 1, sl->wlen); + if (d < 0 && (sl->flags & CLF_DIFF)) + d = cs; + } + } + if (l->flags & CLF_MID) { + /* The cline in the middle, insert the prefix and the + * suffix. */ + if (!l->prefix && l->llen) { + inststrlen(l->line, 1, l->llen); + if (b < 0) { + b = cs; + hb = l->flags & CLF_MISS; + } + } + if (!l->suffix && l->wlen > 0) inststrlen(l->word, 1, l->wlen); - } else if (l->word) { - inststrlen(l->word, 1, l->wlen); - } else { - inststrlen(l->line, 1, l->llen); + } else if (!l->prefix && !l->suffix) { + if (l->word && + !((pl >= 0 && brbeg && *brbeg && + i < pl && i + l->llen >= pl) || + (sl >= 0 && brend && *brend && + i < sl && i + l->llen >= sl))) { + /* We insert the prefered string stored in word only if we + * don't have to put the brace beginning or end here. */ + inststrlen(l->word, 1, l->wlen); + } else { + /* Otherwise just re-insert the original string. */ + inststrlen(l->line, 1, l->llen); + } + i += l->llen; } + /* Remember the position if there is a difference or a missing + * substring. */ if (d < 0 && (l->flags & CLF_DIFF)) d = cs; if (m < 0 && (l->flags & CLF_MISS)) m = cs; - i += l->llen; + + /* Probably insert the brace beginning or end. */ if (pl >= 0 && i >= pl && brbeg && *brbeg) { + cs -= i - pl; inststrlen(brbeg, 1, -1); + cs += i - pl; pl = -1; hb = 1; } - if (sl >= 0 && i >= sl && brend && *brend) { + if (sl >= 0 && i > sl && brend && *brend) { + cs -= i - sl; inststrlen(brend, 1, -1); + cs += i - sl; sl = -1; hb = 1; } l = l->next; } + /* Now place the cursor. Preferably in a position where something + * is missing, otherwise in a place where the string differs from + * any of the matches, or just leave it at the end. */ cs = (b >= 0 && hb ? b : (m >= 0 ? m : (d >= 0 ? d : cs))); if (cs > ll) cs = ll; @@ -1890,6 +3142,7 @@ inst_cline(Cline l, int pl, int sl) * given in `in' so that we can easily test if we found the * * corresponding character. */ +/**/ static int pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out) { @@ -1898,22 +3151,26 @@ pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out) while (p) { c = *((unsigned char *) s); - if (out) *out = 0; + if (out) + *out = 0; if (p->equiv) { if (in) { c = p->tab[c]; - if ((*in && *in != c) || (!*in && !c)) return 0; + if ((*in && *in != c) || (!*in && !c)) + return 0; } else if (out) { - if (!(*out = p->tab[c])) return 0; - } else { - if (!p->tab[c]) return 0; - } - if (in) in++; - if (out) out++; - } else { - if (!p->tab[c]) return 0; - } + if (!(*out = p->tab[c])) + return 0; + } else if (!p->tab[c]) + return 0; + + if (in && *in) + in++; + if (out) + out++; + } else if (!p->tab[c]) + return 0; s++; p = p->next; @@ -1921,18 +3178,23 @@ pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out) return 1; } -/* Do the matching for a prefix. */ +/* Do the matching for a prefix. l and w contain the strings on the line and + * for the generated word, respectively. In nlp we return a cline list for this + * match. In lp we return the length of the matched string. rlp is used to + * return a pointer to the last cline struct in the list returned in nlp, and + * in bplp we return the relative position where the brace beginning would + * have to be insrted in the string returned, which is the string to use + * as the completion. */ static char * -match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, - Cmatcher nm) +match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp) { static unsigned char *ea; static int ealen = 0; static char *rw; static int rwlen; - int ll = strlen(l), lw = strlen(w), mlw; + int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw, mlw; int il = 0, iw = 0, t, bc = brpl; char *nw = rw; Cmlist ms; @@ -1942,7 +3204,7 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, if (nlp) { *nlp = NULL; - if (ll > ealen) { + if (ll + 1 > ealen) { /* This is the `in'/`out' string for pattern matching. */ if (ealen) zfree(ea, ealen); @@ -1950,15 +3212,19 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, } } while (ll && lw) { + /* First try the matchers. */ for (ms = mstack; ms; ms = ms->next) { for (mp = ms->matcher; mp; mp = mp->next) { - if (nm == mp || lm == mp) { - nm = NULL; + if (lm == mp) continue; - } + t = 1; - /* Try to match the prefix, if any. */ - if (mp->flags & CMF_LEFT) { + if ((oll == ll || olw == lw) && !nlp && mp->wlen < 0) + /* If we were called recursively, don't use `*' patterns + * at the beginning (avoiding infinite recursion). */ + t = 0; + else if (mp->flags & CMF_LEFT) { + /* Try to match the left anchor, if any. */ if (il < mp->lalen || iw < mp->lalen) t = 0; else if (mp->left) @@ -1972,42 +3238,61 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, if (ll < mp->llen || lw < mp->wlen) t = 0; else if (mp->wlen < 0) { - /* This is reached, if we have a `*' pattern. */ + /* This is reached if we have a `*' pattern. */ if ((t = pattern_match(mp->line, l, NULL, NULL))) { - if (mp->flags & CMF_RIGHT) { - if (mp->right && ll >= mp->llen + mp->ralen) - t = pattern_match(mp->right, l + mp->llen, - NULL, NULL); - else - t = 0; - } + if (mp->flags & CMF_RIGHT) + /* Check if the anchor matches what's on the + * line. If it also matches the word, we don't + * use the matcher since we don't want one of + * these patterns on the line to match more + * than one such sub-string in the word. */ + t = (mp->right && ll >= mp->llen + mp->ralen && + pattern_match(mp->right, l + mp->llen, + NULL, NULL) && + lw >= mp->ralen && + !pattern_match(mp->right, w, NULL, NULL)); if (t) { + /* The anchor matched, so find out how many + * characters are matched by the `*' pattern. + * We do this by looping over the string + * and calling this function recursively. */ int i = 0, j = iw, k = lw; int jj = il + mp->llen, kk = ll - mp->llen; char *p = l + mp->llen, *q = w; for (; k; i++, j++, k--, q++) { - if (match_pfx(p, q, NULL, NULL, - NULL, NULL, mp)) + if (match_pfx(p, q, NULL, NULL, NULL, NULL)) break; } if (k && i) { + /* We found a position where the rest of + * the line matched again (k != 0) and + * we skipped over at least one character + * (i != 0), so add a cline for this */ if (nlp) { nw = addtoword(&rw, &rwlen, nw, mp, l, w, i, 0); addtocline(nlp, &lr, l, mp->llen, w, i, mp, ((mp->flags & CMF_LEFT) ? - CLF_SUF : 0)); + CLF_SUF : 0) | CLF_VAR); } + /* ...and adjust the pointers and counters + * to continue after the matched portion. */ w = q; iw = j; - lw = k; l = p; il = jj; ll = kk; - bc -= i; - + bc -= mp->llen; + + /* In bc we count the characters used + * backward starting with the original + * position of the brace beginning. So, + * if it becomes less than or equal to + * zero, we have reached the place where + * the brace string would have to be + * inserted. */ if (bc <= 0 && bplp) { *bplp = nw - rw; bplp = NULL; @@ -2107,7 +3392,8 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, *rlp = lr; } else { - addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END); + addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, + CLF_VAR | CLF_END); nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0); } } @@ -2135,10 +3421,11 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, return ((char *) 1); } -/* Do the matching for a suffix. */ +/* Do the matching for a suffix. Almost the same as match_pfx(), only in the +* other direction. */ static char * -match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) +match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) { static unsigned char *ea; static int ealen = 0; @@ -2158,7 +3445,7 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) if (nlp) { *nlp = NULL; - if (ll > ealen) { + if (ll + 1 > ealen) { if (ealen) zfree(ea, ealen); ea = (unsigned char *) zalloc(ealen = ll + 20); @@ -2167,10 +3454,9 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) while (ll && lw) { for (ms = mstack; ms; ms = ms->next) { for (mp = ms->matcher; mp; mp = mp->next) { - if (nm == mp || lm == mp) { - nm = NULL; + if (lm == mp) continue; - } + t = 1; if (mp->flags & CMF_RIGHT) { if (il < mp->ralen || iw < mp->ralen) @@ -2187,22 +3473,21 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) else if (mp->wlen < 0) { if ((t = pattern_match(mp->line, l - mp->llen, NULL, NULL))) { - if (mp->flags & CMF_LEFT) { - if (mp->left && ll >= mp->llen + mp->lalen) - t = pattern_match(mp->left, - l - mp->llen - mp->lalen, - NULL, NULL); - else - t = 0; - } + if (mp->flags & CMF_LEFT) + t = (mp->left && ll >= mp->llen + mp->lalen && + pattern_match(mp->left, + l - mp->llen - mp->lalen, + NULL, NULL) && + lw >= mp->lalen && + !pattern_match(mp->left, w - mp->lalen, + NULL, NULL)); if (t) { int i = 0, j = iw, k = lw; int jj = il + mp->llen, kk = ll - mp->llen; char *p = l - mp->llen - 1, *q = w - 1; for (; k; i++, j++, k--, q--) - if (match_pfx(p, q, NULL, NULL, - NULL, NULL, mp)) + if (match_pfx(p, q, NULL, NULL, NULL, NULL)) break; if (k && i) { if (nlp) { @@ -2212,15 +3497,14 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) addtocline(nlp, &lr, l - mp->llen, mp->llen, w - i, i, mp, ((mp->flags & CMF_LEFT) ? - CLF_SUF : 0)); + CLF_SUF : 0) | CLF_VAR); } w = q + 1; iw = j; - lw = k; l = p + 1; il = jj; ll = kk; - bc -= i; + bc -= mp->llen; if (bc <= 0 && bslp) { *bslp = nw - rw; @@ -2252,7 +3536,8 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) } if (t) { if (nlp) { - nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1); + nw = addtoword(&rw, &rwlen, nw, mp, l - mp->llen, + w - mlw, mlw, 1); addtocline(nlp, &lr, l - mp->llen, mp->llen, w - mlw, mlw, mp, 0); } @@ -2321,7 +3606,14 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) return ((char *) 1); } -/* Check if the word `w' matches. */ +/* Check if the word `w' is matched by the strings in pfx and sfx (the prefix + * and the suffix from the line. In clp a cline list is returned for w. + * qu is non-zero if the words has to be quoted before processed any further. + * bpl and bsl are used to report the positions where the brace-strings in + * the prefix and the suffix have to be re-inserted if this match is inserted + * in the line. + * The return value is the string to use as a completion or NULL if the prefix + * and the suffix don't match the word w. */ static char * comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl) @@ -2332,18 +3624,27 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl if (qu) w = quotename(w, NULL, NULL, NULL); + if (*sfx) { + /* We have a suffix, so this gets a bit more complicated. */ char *p, *s; int sl; Cline sli, last; - if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl, NULL))) { - if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl, NULL))) { + /* First see if the prefix matches. In pl we get the length of + * the string matched by it. */ + if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl))) { + /* Then try to match the rest of the string with the suffix. */ + if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl))) { int pml, sml; + /* Now append the cline list for the suffix to the one + * for the prefix. */ last->llen -= sl; last->next = revert_clines(sli); + /* And store the correct parts of the prefix and the suffix + * in the cline struct in the middle. */ pml = strlen(p); sml = strlen(s); r = (char *) halloc(pml + sml + last->llen + 1); @@ -2351,6 +3652,8 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl strncpy(r + pml, last->line, last->llen); strcpy(r + pml + last->llen, s); } else { + /* Suffix didn't match, so free the cline list for the + * prefix. */ last->next = freecl; freecl = pli; @@ -2359,9 +3662,12 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl } else return NULL; - } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl, NULL))) + } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl))) + /* We had only the prefix to match and that didn't match. */ return NULL; + /* If there are path prefixes or suffixes, pre- and append them to + * the cline list built. */ if (lppre && *lppre) { Cline l, t = str_cline(lppre, -1, &l); @@ -2409,21 +3715,26 @@ instmatch(Cmatch m) { int l, r = 0, ocs, a = cs; + /* Ignored prefix. */ if (m->ipre) { inststrlen(m->ipre, 1, (l = strlen(m->ipre))); r += l; - } + } + /* -P prefix. */ if (m->pre) { inststrlen(m->pre, 1, (l = strlen(m->pre))); r += l; } + /* Path prefix. */ if (m->ppre) { inststrlen(m->ppre, 1, (l = strlen(m->ppre))); r += l; } + /* The string itself. */ inststrlen(m->str, 1, (l = strlen(m->str))); r += l; ocs = cs; + /* Re-insert the brace beginning, if any. */ if (brbeg && *brbeg) { cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0); l = strlen(brbeg); @@ -2433,10 +3744,12 @@ instmatch(Cmatch m) ocs += l; cs = ocs; } + /* Path suffix. */ if (m->psuf) { inststrlen(m->psuf, 1, (l = strlen(m->psuf))); r += l; } + /* Re-insert the brace end. */ if (brend && *brend) { a = cs; cs -= m->brsl; @@ -2447,6 +3760,7 @@ instmatch(Cmatch m) cs = a + l; } else brscs = -1; + /* -S suffix */ if (m->suf) { inststrlen(m->suf, 1, (l = strlen(m->suf))); r += l; @@ -2455,13 +3769,17 @@ instmatch(Cmatch m) return r; } +/* This is used by compadd to add a couple of matches. The arguments are + * the strings given via options. The last argument is the array with + * the matches. */ + /**/ void addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, char *suf, char *group, char *rems, char *remf, char *ign, int flags, int aflags, Cmatcher match, char **argv) { - char *s, *t, *e, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL; + char *s, *t, *e, *me, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL; int lpl, lsl, i, pl, sl, test, bpl, bsl, lsm, llpl; Aminfo ai; Cline lc = NULL; @@ -2470,6 +3788,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, struct cmlist mst; Cmlist oms = mstack; + /* Select the set of matches. */ if (aflags & CAF_ALT) { l = fmatches; ai = fainfo; @@ -2477,26 +3796,44 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, l = matches; ai = ainfo; } + /* Store the matcher in our stack of matchers. */ if (match) { mst.next = mstack; mst.matcher = match; mstack = &mst; + if (had_lm && mnum) + bmstack1.matcher = NULL; + else { + bmstack1.matcher = match; + had_lm = 1; + } addlinknode(matchers, match); match->refc++; + } else { + bmstack1.matcher = NULL; + if (mnum) + had_lm = 1; } + /* Use menu-completion (-U)? */ if ((aflags & CAF_MENU) && isset(AUTOMENU)) usemenu = 1; + /* Get the suffixes to ignore. */ if (ign) aign = get_user_var(ign); + /* Switch back to the heap that was used when the completion widget + * was invoked. */ SWITCHHEAPS(compheap) { HEAPALLOC { + /* Get the contents of the completion variables if we have + * to perform matching. */ if (aflags & CAF_MATCH) { lipre = dupstring(compiprefix); lpre = dupstring(compprefix); llpl = strlen(lpre); lsuf = dupstring(compsuffix); } + /* Now duplicate the strings we have from the command line. */ if (ipre) ipre = (lipre ? dyncat(lipre, ipre) : dupstring(ipre)); else if (lipre) @@ -2522,6 +3859,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, untokenize(prpre); } else prpre = dupstring(prpre); + /* Select the group in which to store the matches. */ if (group) { endcmgroup(NULL); begcmgroup(group, (aflags & CAF_NOSORT)); @@ -2533,6 +3871,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, rems = NULL; } else if (rems) rems = dupstring(rems); + /* Build the common -P prefix. */ if (ai->pprefix) { if (pre) ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0'; @@ -2541,12 +3880,16 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } else ai->pprefix = dupstring(pre ? pre : ""); + /* Walk through the matches given. */ for (; (s = dupstring(*argv)); argv++) { sl = strlen(s); + lc = NULL; ms = NULL; bpl = brpl; bsl = brsl; if ((!psuf || !*psuf) && aign) { + /* Do the suffix-test. If the match has one of the + * suffixes from ign, we put it in the alternate set. */ char **pt = aign; int filell; @@ -2564,6 +3907,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } } if (aflags & CAF_MATCH) { + /* Do the matching. */ t = (ppre ? dyncat(ppre, s) : s); pl = sl + lpl; if ((test = (llpl <= pl && !strncmp(t, lpre, pl)))) @@ -2576,30 +3920,61 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, test = 1; if (!test) continue; - e = s + sl; + me = e = s + sl; } else { e = s; - pl = lpl; + me = s + sl; + pl = sl; } + /* Quoting? */ if (!(aflags & CAF_QUOTE)) { te = s + pl; s = quotename(s, &e, te, &pl); sl = strlen(s); } - if (!ms && ai->firstm) { + /* The rest is almost the same as in addmatch(). */ + if (!ms) { if (sl < ai->minlen) ai->minlen = sl; - if ((i = sfxlen(ai->firstm->str, s)) < ai->suflen) + if (!mstack && ai->firstm && + (i = sfxlen(ai->firstm->str, s)) < ai->suflen) ai->suflen = i; } t = s; if (ppre) t = dyncat(ppre, t); + if (!ms && mstack) { + Cline *clp = &lc, tlc; + char *ss = dupstring(s), *ee = me + (ss - s); + + if (ppre && *ppre) { + *clp = str_cline(ppre, strlen(ppre), &tlc); + clp = &(tlc->next); + } + if (ee != ss + sl || (lpsuf && *lpsuf)) { + *clp = tlc = getcline(ss, ee - ss, + NULL, 0, CLF_MID); + clp = &(tlc->next); + if (ee != ss + sl) { + *clp = str_cline(ee, (ss + sl) - ee, &tlc); + clp = &(tlc->next); + } + if (lpsuf && *lpsuf) { + *clp = str_cline(lpsuf, strlen(lpsuf), &tlc); + clp = &(tlc->next); + } + } else { + *clp = tlc = getcline(NULL, 0, ss, sl, + CLF_END | CLF_VAR); + clp = &(tlc->next); + } + *clp = NULL; + } if (ipre && *ipre) { Cline tlc = prepend_cline(ipre, lc); ai->noipre = 0; - if (!ms) { + if (!ms && !mstack) { if ((aflags & CAF_MATCH) || ai->icpl > pl) ai->icpl = pl; if ((aflags & CAF_MATCH) || ai->icsl > lsl) @@ -2620,7 +3995,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, lc = tlc; } else ai->iprefix = ""; - if (!ms) { + if (!ms && !mstack) { if ((aflags & CAF_MATCH) || ai->cpl > pl) ai->cpl = pl; if ((aflags & CAF_MATCH) || ai->csl > lsl) @@ -2635,6 +4010,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, mnum++; ai->count++; + /* Finally add the match. */ cm = (Cmatch) halloc(sizeof(struct cmatch)); cm->ppre = ppre; cm->psuf = psuf; @@ -2674,6 +4050,8 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } LASTALLOC; } SWITCHBACKHEAPS; + /* We switched back to the current heap, now restore the stack of + * matchers. */ mstack = oms; } @@ -2854,10 +4232,11 @@ addmatch(char *s, char *t) } if (!test) return; - if (!ms && !ispattern && ai->firstm) { + if (!ms) { if (sl < ai->minlen) ai->minlen = sl; - if ((test = sfxlen(ai->firstm->str, s)) < ai->suflen) + if (!mstack && !ispattern && ai->firstm && + (test = sfxlen(ai->firstm->str, s)) < ai->suflen) ai->suflen = test; } @@ -2875,11 +4254,41 @@ addmatch(char *s, char *t) t = s; if (lppre) t = dyncat(lppre, t); + if (!ms && mstack) { + Cline *clp = &lc, tlc; + char *ss = dupstring(s), *ee = e + (ss - s); + + if (lppre && *lppre) { + *clp = str_cline(lppre, strlen(lppre), &tlc); + clp = &(tlc->next); + } + if (pl) { + *clp = str_cline(ss, pl, &tlc); + clp = &(tlc->next); + } + if (ee != ss + sl || (lpsuf && *lpsuf)) { + *clp = tlc = getcline(ss + pl, (ee - ss) - pl, NULL, 0, CLF_MID); + clp = &(tlc->next); + if (ee != ss + sl) { + *clp = str_cline(ee, (ss + sl) - ee, &tlc); + clp = &(tlc->next); + } + if (lpsuf && *lpsuf) { + *clp = str_cline(lpsuf, strlen(lpsuf), &tlc); + clp = &(tlc->next); + } + } else { + *clp = tlc = getcline(NULL, 0, ss + pl, sl - pl, + CLF_END | CLF_VAR); + clp = &(tlc->next); + } + *clp = NULL; + } if (ipre && *ipre) { Cline tlc = prepend_cline(ipre, lc); ai->noipre = 0; - if (!ms) { + if (!ms && !mstack) { ai->icpl = lppl + mpl; ai->icsl = lpsl + msl; if (ai->iaprefix) @@ -2899,7 +4308,7 @@ addmatch(char *s, char *t) } else ai->iprefix = ""; - if (!ms) { + if (!ms && !mstack) { ai->cpl = lppl + mpl; ai->csl = lpsl + msl; if (ai->aprefix) @@ -3146,7 +4555,8 @@ getreal(char *str) addlinknode(l, dupstring(str)); prefork(l, 0); noerrs = ne; - if (!errflag && nonempty(l)) + if (!errflag && nonempty(l) && + ((char *) peekfirst(l)) && ((char *) peekfirst(l))[0]) return dupstring(peekfirst(l)); errflag = 0; @@ -3464,14 +4874,13 @@ static int brange, erange; static unsigned long ccont; -/* Create the completion list. This is called whenever some bit of * - * completion code needs the list. If the list is already available * - * (validlist!=0), this function doesn't do anything. Along with * - * the list is maintained the prefixes/suffixes etc. When any of * - * this becomes invalid -- e.g. if some text is changed on the * - * command line -- invalidatelist() should be called, to set * - * validlist to zero and free up the memory used. This function * - * returns non-zero on error. */ +/* Create the completion list. This is called whenever some bit of * + * completion code needs the list. * + * Along with the list is maintained the prefixes/suffixes etc. When * + * any of this becomes invalid -- e.g. if some text is changed on the * + * command line -- invalidatelist() should be called, to set * + * validlist to zero and free up the memory used. This function * + * returns non-zero on error. */ /**/ static int @@ -3482,8 +4891,6 @@ makecomplist(char *s, int incmd, int lst) /* If we already have a list from a previous execution of this * * function, skip the list building code. */ - if (validlist) - return !nmatches; if ((m = cmatcher)) { Cmlist mm, *mp = &mm; @@ -3499,13 +4906,19 @@ makecomplist(char *s, int incmd, int lst) m = mm; } compmatcher = 1; + bmstack = &bmstack1; + bmstack1.next = &bmstack0; + bmstack0.next = NULL; + bmstack0.matcher = bmstack1.matcher = NULL; for (;;) { if (m) { ms.next = NULL; ms.matcher = m->matcher; mstack = &ms; + bmstack0.matcher = m->matcher; } else mstack = NULL; + ainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); fainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); @@ -3515,9 +4928,11 @@ makecomplist(char *s, int incmd, int lst) freecl = NULL; - lastambig = 0; + if (!validlist) + lastambig = 0; amatches = 0; mnum = 0; + had_lm = 0; begcmgroup("default", 0); ccused = newlinklist(); @@ -3573,11 +4988,13 @@ ctokenize(char *p) if (*p == '\\') bslash = 1; else { - if (*p == '$' || *p == '=') { + if (*p == '$' || *p == '=' || *p == '{' || *p == '}') { if (bslash) p[-1] = Bnull; else - *p = (*p == '$' ? String : Equals); + *p = (*p == '$' ? String : + (*p == '=' ? Equals : + (*p == '{' ? Inbrace : Outbrace))); } bslash = 0; } @@ -4154,8 +5571,18 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) ms.next = mstack; ms.matcher = cc->matcher; mstack = &ms; + if (had_lm && mnum) + bmstack1.matcher = NULL; + else { + bmstack1.matcher = cc->matcher; + had_lm = 1; + } addlinknode(matchers, cc->matcher); cc->matcher->refc++; + } else { + bmstack1.matcher = NULL; + if (mnum) + had_lm = 1; } /* Insert the prefix (compctl -P), if any. */ if (cc->prefix) { @@ -5001,7 +6428,7 @@ invalidatelist(void) listmatches(); if(validlist) freematches(); - lastambig = menucmp = validlist = showinglist = 0; + lastambig = menucmp = validlist = showinglist = fromcomp = 0; menucur = NULL; compwidget = NULL; } @@ -5056,15 +6483,17 @@ get_user_var(char *nam) } else { /* Otherwise it should be a parameter name. */ char **arr = NULL, *val; - if (!(arr = getaparam(nam)) && !(arr = gethparam(nam)) && - (val = getsparam(nam))) { + + if ((arr = getaparam(nam)) || (arr = gethparam(nam))) + return (incompfunc ? arrdup(arr) : arr); + + if ((val = getsparam(nam))) { arr = (char **)ncalloc(2*sizeof(char *)); arr[0] = val; arr[1] = NULL; } return arr; } - } /* This is strcmp with ignoring backslashes. */ @@ -5437,14 +6866,13 @@ static void do_ambiguous(void) { int p = (usemenu || ispattern), atend = (cs == we); - int am = 0; menucmp = 0; /* If we have to insert the first match, call do_single(). This is * * how REC_EXACT takes effect. We effectively turn the ambiguous * * completion into an unambiguous one. */ - if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && + if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !fromcomp && (usemenu == 0 || unset(AUTOMENU))) { do_single(ainfo->exactm); invalidatelist(); @@ -5464,16 +6892,21 @@ do_ambiguous(void) * normal menu completion options. */ do_ambig_menu(); } else { - int ics = cs, ocs, pl = 0, l, lp, ls; + VARARR(char, oline, ll); + int sl = 0, oll = ll; + int ocs, pl = 0, l, lp, ls, la = 0; char *ps; Cline lc; if (!ainfo) return; + /* Copy the line buffer to be able to easily test if it changed. */ + memcpy(oline, line, ll); + fixsuffix(); - /* Delete the old stuff from the command line. */ + /* First remove the old string from the line. */ cs = wb; foredel(we - wb); @@ -5499,8 +6932,6 @@ do_ambiguous(void) ls = ainfo->csl; } if (lc) { - int sl = 0; - if (lp) { if (ls) { if (ainfo->firstm->psuf) @@ -5514,6 +6945,7 @@ do_ambiguous(void) merge_cline(lc, ps, lp, NULL, 0, 0); } inst_cline(lc, pl, sl); + } else { inststrlen(ps, 1, -1); ocs = cs; @@ -5536,13 +6968,16 @@ do_ambiguous(void) } cs = ocs; } - /* If REC_EXACT and AUTO_MENU are set and what we inserted is an * - * exact match, we want to start menu completion now. Otherwise * - * on the next call to completion the inserted string would be * - * taken as a match and no menu completion would be started. */ + /* la is non-zero if listambiguous may be used. Copying and + * comparing the line looks like BFI but it is the easiest + * solution. Really. */ + la = (ll != oll || strncmp(oline, (char *) line, ll)); - if (isset(RECEXACT) && !lc && ps && ainfo->minlen == strlen(ps)) - am = 1; + /* If REC_EXACT and AUTO_MENU are set and what we inserted is an * + * exact match, we want menu completion the next time round * + * so we set fromcomp,to ensure that the word on the line is not * + * taken as an exact match. */ + fromcomp = isset(AUTOMENU); /* * If the LIST_AMBIGUOUS option (meaning roughly `show a list only * @@ -5550,9 +6985,11 @@ do_ambiguous(void) * prefix was inserted, return now, bypassing the list-displaying * * code. On the way, invalidate the list and note that we don't * * want to enter an AUTO_MENU imediately. */ - if(isset(LISTAMBIGUOUS) && !am && - (ics != cs || (ainfo->suflen && !atend))) { + if(isset(LISTAMBIGUOUS) && la) { + int fc = fromcomp; + invalidatelist(); + fromcomp = fc; lastambig = 0; return; } @@ -5564,8 +7001,6 @@ do_ambiguous(void) if (isset(AUTOLIST) && !isset(BASHAUTOLIST) && !amenu && !showinglist && smatches >= 2) showinglist = -2; - if (am) - lastambig = 1; } /* This is a stat that ignores backslashes in the filename. The `ls' * diff --git a/Src/cond.c b/Src/cond.c index a46833f66..00beb8e65 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -30,6 +30,13 @@ #include "zsh.mdh" #include "cond.pro" +int tracingcond; + +static char *condstr[COND_MOD] = { + "!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", + "-ne", "-lt", "-gt", "-le", "-ge" +}; + /**/ int evalcond(Cond c) @@ -38,11 +45,23 @@ evalcond(Cond c) switch (c->type) { case COND_NOT: + if (tracingcond) + fprintf(stderr, " %s", condstr[c->type]); return !evalcond(c->left); case COND_AND: - return evalcond(c->left) && evalcond(c->right); + if (evalcond(c->left)) { + if (tracingcond) + fprintf(stderr, " %s", condstr[c->type]); + return evalcond(c->right); + } else + return 0; case COND_OR: - return evalcond(c->left) || evalcond(c->right); + if (!evalcond(c->left)) { + if (tracingcond) + fprintf(stderr, " %s", condstr[c->type]); + return evalcond(c->right); + } else + return 1; case COND_MOD: case COND_MODI: { @@ -58,6 +77,9 @@ evalcond(Cond c) return 0; } } + if (tracingcond) + tracemodcond((char *)c->left, (char **)c->right, + c->type == COND_MODI); return cd->handler((char **) c->right, cd->condid); } else { @@ -71,6 +93,8 @@ evalcond(Cond c) zerr("unrecognized condition: `%s'", (char *) c->left, 0); return 0; } + if (tracingcond) + tracemodcond((char *)c->left, a, c->type == COND_MODI); a[0] = (char *) c->left; return cd->handler(a, cd->condid); } else @@ -86,6 +110,20 @@ evalcond(Cond c) if (c->type != COND_STREQ && c->type != COND_STRNEQ) untokenize(c->right); } + + if (tracingcond) { + if (c->type < COND_MOD) { + char *rt = (char *)c->right; + if (c->type == COND_STREQ || c->type == COND_STRNEQ) { + rt = dupstring(rt); + untokenize(rt); + } + fprintf(stderr, " %s %s %s", (char *)c->left, condstr[c->type], + rt); + } else + fprintf(stderr, " -%c %s", c->type, (char *)c->left); + } + switch (c->type) { case COND_STREQ: return matchpat(c->left, c->right); @@ -294,3 +332,21 @@ cond_match(char **args, int num, char *str) return matchpat(str, s); } + +/**/ +static void +tracemodcond(char *name, char **args, int inf) +{ + char **aptr; + MUSTUSEHEAP("tracemodcond"); + args = duparray(args, (VFunc) dupstring); + for (aptr = args; *aptr; aptr++) + untokenize(*aptr); + if (inf) { + fprintf(stderr, " %s %s %s", args[0], name, args[1]); + } else { + fprintf(stderr, " %s", name); + while (*args) + fprintf(stderr, " %s", *args++); + } +} diff --git a/Src/exec.c b/Src/exec.c index bb331f426..59061af3f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1208,7 +1208,7 @@ addvars(LinkList l, int export) addlinknode(vl, v->str); } else vl = v->arr; - prefork(vl, v->type == PM_SCALAR ? 7 : 3); + prefork(vl, v->type == PM_SCALAR ? (PF_SINGLE|PF_ASSIGN) : PF_ASSIGN); if (errflag) return; if (isset(GLOBASSIGN) || v->type != PM_SCALAR) @@ -1356,7 +1356,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } /* Do prefork substitutions */ - prefork(args, assign ? 2 : isset(MAGICEQUALSUBST)); + prefork(args, (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0); if (type == SIMPLE) { int unglobbed = 0; @@ -2519,13 +2519,26 @@ spawnpipes(LinkList l) } } +extern int tracingcond; + /* evaluate a [[ ... ]] */ /**/ static int execcond(Cmd cmd) { - return !evalcond(cmd->u.cond); + int stat; + if (isset(XTRACE)) { + fprintf(stderr, "%s[[", prompt4 ? prompt4 : ""); + tracingcond++; + } + stat = !evalcond(cmd->u.cond); + if (isset(XTRACE)) { + fprintf(stderr, " ]]\n"); + fflush(stderr); + tracingcond--; + } + return stat; } /* evaluate a ((...)) arithmetic command */ @@ -2537,8 +2550,17 @@ execarith(Cmd cmd) char *e; long val = 0; - while ((e = (char *) ugetnode(cmd->args))) + if (isset(XTRACE)) + fprintf(stderr, "%s((", prompt4 ? prompt4 : ""); + while ((e = (char *) ugetnode(cmd->args))) { + if (isset(XTRACE)) + fprintf(stderr, " %s", e); val = matheval(e); + } + if (isset(XTRACE)) { + fprintf(stderr, " ))\n", stderr); + fflush(stderr); + } errflag = 0; return !val; } @@ -2613,6 +2635,18 @@ execshfunc(Cmd cmd, Shfunc shf) deletejob(jobtab + thisjob); } + if (isset(XTRACE)) { + LinkNode lptr; + fprintf(stderr, "%s", prompt4 ? prompt4 : prompt4); + for (lptr = firstnode(cmd->args); lptr; incnode(lptr)) { + if (lptr != firstnode(cmd->args)) + fputc(' ', stderr); + fprintf(stderr, "%s", (char *)getdata(lptr)); + } + fputc('\n', stderr); + fflush(stderr); + } + doshfunc(shf->nam, shf->funcdef, cmd->args, shf->flags, 0); if (!list_pipe) diff --git a/Src/glob.c b/Src/glob.c index 5815f05a8..516f75618 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -1810,7 +1810,7 @@ xpandredir(struct redir *fn, LinkList tab) fake = newlinklist(); addlinknode(fake, fn->name); /* ...which undergoes all the usual shell expansions */ - prefork(fake, isset(MULTIOS) ? 0 : 4); + prefork(fake, isset(MULTIOS) ? 0 : PF_SINGLE); /* Globbing is only done for multios. */ if (!errflag && isset(MULTIOS)) globlist(fake); diff --git a/Src/params.c b/Src/params.c index 88b5fdf73..77166209f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -568,8 +568,10 @@ createparam(char *name, int flags) if (isset(ALLEXPORT) && !oldpm) flags |= PM_EXPORTED; - } else + } else { pm = (Param) alloc(sizeof *pm); + pm->nam = nulstring; + } pm->flags = flags; if(!(pm->flags & PM_SPECIAL)) @@ -1835,6 +1837,12 @@ arrhashsetfn(Param pm, char **val) while (*aptr) { /* The parameter name is ztrdup'd... */ v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET); + /* + * createparam() doesn't return anything if the parameter + * already existed. + */ + if (!v->pm) + v->pm = (Param) paramtab->getnode(paramtab, *aptr); zsfree(*aptr++); /* ...but we can use the value without copying. */ setstrvalue(v, *aptr++); diff --git a/Src/subst.c b/Src/subst.c index 422c9c4e9..66c363145 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -42,11 +42,7 @@ char nulstring[] = {Nularg, '\0'}; * - Brace expansion * - Tilde and equals substitution * - * Bits 0 and 1 of flags are used in filesub. - * bit 0 is set when we are doing MAGIC_EQUALSUBST or normal - * assignment but not a typeset. - * bit 1 is set on a real assignment (both typeset and normal). - * bit 2 is a flag to paramsubst (single word sub) + * PF_* flags are defined in zsh.h */ /**/ @@ -70,20 +66,22 @@ prefork(LinkList list, int flags) return; } else { if (isset(SHFILEEXPANSION)) - filesub((char **)getaddrdata(node), flags & 3); - if (!(node = stringsubst(list, node, flags & 4))) + filesub((char **)getaddrdata(node), + flags & (PF_TYPESET|PF_ASSIGN)); + if (!(node = stringsubst(list, node, flags & PF_SINGLE))) return; } } for (node = firstnode(list); node; incnode(node)) { if (*(char *)getdata(node)) { remnulargs(getdata(node)); - if (unset(IGNOREBRACES) && !(flags & 4)) + if (unset(IGNOREBRACES) && !(flags & PF_SINGLE)) while (hasbraces(getdata(node))) xpandbraces(list, &node); if (unset(SHFILEEXPANSION)) - filesub((char **)getaddrdata(node), flags & 3); - } else if (!(flags & 4)) + filesub((char **)getaddrdata(node), + flags & (PF_TYPESET|PF_ASSIGN)); + } else if (!(flags & PF_SINGLE)) uremnode(list, node); if (errflag) return; @@ -234,7 +232,7 @@ singsub(char **s) foo = newlinklist(); addlinknode(foo, *s); - prefork(foo, 4); + prefork(foo, PF_SINGLE); if (errflag) return; *s = (char *) ugetnode(foo); @@ -287,8 +285,10 @@ multsub(char **s, char ***a, int *isarr, char *sep) return !l; } -/* ~, = subs: assign = 2 => typeset; assign = 1 => something that looks - like an assignment but may not be; assign = 3 => normal assignment */ +/* + * ~, = subs: assign & PF_TYPESET => typeset or magic equals + * assign & PF_ASSIGN => normal assignment + */ /**/ void @@ -302,12 +302,8 @@ filesub(char **namptr, int assign) if (!assign) return; - if (assign < 3) { + if (assign & PF_TYPESET) { if ((*namptr)[1] && (sub = strchr(*namptr + 1, Equals))) { - if (assign == 1) - for (ptr = *namptr; ptr != sub; ptr++) - if (!iident(*ptr) && !INULL(*ptr)) - return; str = sub + 1; if ((sub[1] == Tilde || sub[1] == Equals) && filesubstr(&str, assign)) { sub[1] = '\0'; diff --git a/Src/utils.c b/Src/utils.c index ee06cfeca..4ff4a91ba 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -2261,7 +2261,7 @@ duplist(LinkList l, VFunc func) } /**/ -static char ** +char ** duparray(char **arr, VFunc func) { char **ret, **rr; diff --git a/Src/zsh.h b/Src/zsh.h index 87ec3e0c0..fa5b5632c 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -950,6 +950,11 @@ struct param { #define SUB_ALL 0x0100 /* match complete string */ #define SUB_GLOBAL 0x0200 /* global substitution ${..//all/these} */ +/* Flags as the second argument to prefork */ +#define PF_TYPESET 0x01 /* argument handled like typeset foo=bar */ +#define PF_ASSIGN 0x02 /* argument handled like the RHS of foo=bar */ +#define PF_SINGLE 0x04 /* single word substitution */ + /* node for named directory hash table (nameddirtab) */ struct nameddir { |