From 904b939cbd81a542303da2c58288b95b153106f5 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Thu, 15 Apr 1999 18:17:36 +0000 Subject: zsh-3.1.5-pws-10 --- Src/.cvsignore | 1 + Src/Builtins/rlimits.c | 12 +- Src/Zle/comp.h | 7 +- Src/Zle/comp1.c | 5 +- Src/Zle/compctl.c | 12 +- Src/Zle/zle_thingy.c | 11 + Src/Zle/zle_tricky.c | 953 +++++++++++++++++++++++++++++-------------------- Src/exec.c | 2 +- Src/glob.c | 441 ++++++++++++++++++++--- Src/hashtable.c | 20 ++ Src/module.c | 49 ++- Src/params.c | 13 +- Src/subst.c | 5 +- Src/system.h | 11 +- Src/zsh.export | 1 + 15 files changed, 1054 insertions(+), 489 deletions(-) (limited to 'Src') diff --git a/Src/.cvsignore b/Src/.cvsignore index edec5401b..fcef4da67 100644 --- a/Src/.cvsignore +++ b/Src/.cvsignore @@ -19,6 +19,7 @@ zsh libzsh.so* sigcount.h signames.c +version.h zshpaths.h zshxmods.h bltinmods.list diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index 2af8dc6ac..7825a900d 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -37,7 +37,7 @@ # include "rlimits.h" -# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_UNSIGNED) +# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_LONG_LONG) || defined(RLIM_T_IS_UNSIGNED) static rlim_t zstrtorlimt(const char *s, char **t, int base) { @@ -62,9 +62,9 @@ zstrtorlimt(const char *s, char **t, int base) *t = (char *)s; return ret; } -# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */ +# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */ # define zstrtorlimt(a, b, c) zstrtol((a), (b), (c)) -# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */ +# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */ /* Display resource limits. hard indicates whether `hard' or `soft' * * limits should be displayed. lim specifies the limit, or may be -1 * @@ -107,9 +107,15 @@ showlimits(int hard, int lim) else printf("%qdkB\n", val / 1024L); # else +# ifdef RLIM_T_IS_LONG_LONG + printf("%lldMB\n", val / (1024L * 1024L)); + else + printf("%lldkB\n", val / 1024L); +# else printf("%ldMB\n", val / (1024L * 1024L)); else printf("%ldkB\n", val / 1024L); +# endif /* RLIM_T_IS_LONG_LONG */ # endif /* RLIM_T_IS_QUAD_T */ } } diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 0d5419817..76ac67114 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -271,9 +271,10 @@ struct cline { #define CLF_MISS 4 #define CLF_DIFF 8 #define CLF_SUF 16 -#define CLF_NEW 32 -#define CLF_VAR 64 -#define CLF_JOIN 128 +#define CLF_PNEW 32 +#define CLF_SNEW 64 +#define CLF_VAR 128 +#define CLF_JOIN 256 /* Flags for makecomplist*(). Things not to do. */ diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c index 77fe0a1fe..72f3cea53 100644 --- a/Src/Zle/comp1.c +++ b/Src/Zle/comp1.c @@ -49,10 +49,10 @@ void (*makecompparamsptr) _((void)); /* pointers to functions required by compctl and defined by zle */ /**/ -void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char **)); +void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **)); /**/ -char *(*comp_strptr) _((int*,int*)); +char *(*comp_strptr) _((int*, int*, int)); /**/ int (*getcpatptr) _((char *, int, char *, int)); @@ -410,6 +410,7 @@ setup_comp1(Module m) cc_first.mask2 = CC_CCCONT; compcontext = compcommand = compprefix = compsuffix = compiprefix = NULL; + makecompparamsptr = NULL; return 0; } diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index e3b778508..f71d67510 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1676,7 +1676,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) char *p, **sp, *e; char *ipre = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL; char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL; - char *ign = NULL, *rf = NULL; + char *ign = NULL, *rf = NULL, *expl = NULL; int f = 0, a = 0, dm; Cmatcher match = NULL; @@ -1757,6 +1757,10 @@ bin_compadd(char *name, char **argv, char *ops, int func) e = "matching specification expected after -%c"; dm = 1; break; + case 'X': + sp = &expl; + e = "string expected after -%c"; + break; case 'r': f |= CMF_REMOVE; sp = &rs; @@ -1802,7 +1806,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) match = cpcmatcher(match); addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group, - rs, rf, ign, f, a, match, argv); + rs, rf, ign, f, a, match, expl, argv); freecmatcher(match); return 0; @@ -2049,8 +2053,8 @@ cond_strcl(char **a, int id) zerr("zle not loaded, zle condition not available", NULL, 0); return 1; } - i = getcpatptr(comp_strptr(&ipl, NULL), i, s, id); - if (i != -1) { + i = getcpatptr(comp_strptr(&ipl, NULL, 1), i, s, id); + if (i != -1 && i >= ipl) { ignore_prefix(i - ipl); return 1; } diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index d055c45c1..28781e7f0 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -478,6 +478,17 @@ bin_zle_complete(char *name, char **args, char *ops, char func) Thingy t; Widget w, cw; +#ifdef DYNAMIC + if (!require_module(name, "compctl", 0, 0)) { + zerrnam(name, "can't load compctl module", NULL, 0); + return 1; + } +#else + if (!makecompparamsptr) { + zerrnam(name, "compctl module not available", NULL, 0); + return 1; + } +#endif t = rthingy(args[1]); cw = t->widget; unrefthingy(t); diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index af78e1c06..4b42640e1 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -214,11 +214,9 @@ 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; +static Cmlist bmatchers; -/* A list with references to all matcher we used. */ +/* A list with references to all matchers we used. */ static LinkList matchers; @@ -293,12 +291,24 @@ 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()). */ +/* This says what of the state the line is in when completion is started * + * came from a previous completion. If the FC_LINE bit is set, the * + * string was inserted. If FC_INWORD is set, the last completion moved * + * the cursor into the word although it was at the end of it when the * + * last completion was invoked. * + * This is used to detect if the string should be taken as an exact * + * match (see do_ambiguous()) and if the cursor has to be moved to the * + * end of the word before generating the completions. */ static int fromcomp; +/* This holds the end-position of the last string inserted into the line. */ + +static int lastend; + +#define FC_LINE 1 +#define FC_INWORD 2 + /**/ void completecall(void) @@ -607,6 +617,12 @@ docomplete(int lst) return; } + /* We may have to reset the cursor to its position after the * + * string inserted by the last completion. */ + + if (fromcomp & FC_INWORD) + cs = lastend; + /* Check if we have to start a menu-completion (via automenu). */ if ((amenu = (isset(AUTOMENU) && lastambig && @@ -1567,6 +1583,83 @@ addtocline(Cline *retp, Cline *lrp, *lrp = ln; } +/* This compares two cpattern lists and returns non-zero if they are + * equal. */ + +static int +cmp_cpatterns(Cpattern a, Cpattern b) +{ + while (a) { + if (a->equiv != b->equiv || memcmp(a->tab, b->tab, 256)) + return 0; + a = a->next; + b = b->next; + } + return 1; +} + +/* This compares two cmatchers and returns non-zero if they are equal. */ + +static int +cmp_cmatchers(Cmatcher a, Cmatcher b) +{ + return (a == b || + (a->flags == b->flags && + a->llen == b->llen && a->wlen == b->wlen && + (!a->llen || cmp_cpatterns(a->line, b->line)) && + (a->wlen <= 0 || cmp_cpatterns(a->word, b->word)) && + (!(a->flags & CMF_LEFT) || + (a->lalen == b->lalen && + (!a->lalen || cmp_cpatterns(a->left, b->left)))) && + (!(a->flags & CMF_RIGHT) || + (a->ralen == b->ralen && + (!a->ralen || cmp_cpatterns(a->right, b->right)))))); +} + +/* Add the given matchers to the bmatcher list. */ + +static void +add_bmatchers(Cmatcher m) +{ + Cmlist old = bmatchers, *q = &bmatchers, n; + + for (; m; m = m->next) { + if ((!m->flags && m->wlen > 0 && m->llen > 0) || + (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) { + *q = n = (Cmlist) halloc(sizeof(struct cmlist)); + n->matcher = m; + q = &(n->next); + } + } + *q = old; +} + +/* This is called when the matchers in the mstack have changed to + * ensure that the bmatchers list contains no matchers not in mstack. */ + +static void +update_bmatchers(void) +{ + Cmlist p = bmatchers, q = NULL, ms; + Cmatcher mp; + int t; + + while (p) { + t = 0; + for (ms = mstack; ms && !t; ms = ms->next) + for (mp = ms->matcher; mp && !t; mp = mp->next) + t = cmp_cmatchers(mp, p->matcher); + + p = p->next; + if (!t) { + if (q) + q->next = p; + else + bmatchers = p; + } + } +} + /* 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. @@ -1582,21 +1675,20 @@ end_list(int len, char *str) 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; - } + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + 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) { @@ -1616,7 +1708,7 @@ end_list(int len, char *str) /* 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. + * or that string in local static memory, dup it. * All this is a lot like the procedure in bld_new_pfx(), only slightly * simpler, see that function for more comments. */ @@ -1644,60 +1736,59 @@ join_strs(int la, char *sa, int lb, char *sb) } 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); + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + 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; } - 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; + 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; } } } @@ -1725,7 +1816,7 @@ join_strs(int la, char *sa, int lb, char *sb) *rp = '\0'; - return dupstring(rs); + return rs; } /* This gets two cline lists with separated runs and joins the corresponding @@ -1752,26 +1843,55 @@ join_ends(Cline o, Cline n, int *olp, int *nlp) * 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; + o->line = dupstring(j); bld_pfx(o, n, &mol, &mnl); smol += mol + o->llen; smnl += mnl + n->llen; } else { - /* Different anchor strings, so shorten the list. */ + /* Different anchors, see if we can find matching anchors + * further down the lists. */ + Cline to, tn; + int t = 0; + + /* But first build the common prefix. */ 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; + + for (to = o; to && !t; to = to->next) { + for (tn = n; tn && !t; tn = tn->next) { + if ((t = ((to->llen == tn->llen && + !strncmp(to->line, tn->line, to->llen)) || + (!(to->flags & CLF_JOIN) && + join_strs(to->llen, to->line, + tn->llen, tn->line))))) + break; + } + if (t) + break; + } + if (t) { + /* Found matching anchors, continue with them. */ + o->line = to->line; + o->llen = to->llen; + o->next = to->next; + o->flags |= CLF_MISS; + n = tn; + } else { + /* No matching anchors found, shorten the list. */ + 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). */ + * 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; @@ -1859,20 +1979,19 @@ bld_line_pfx(Cpattern pat, char *line, char *lp, 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; - } + for (ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + 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) @@ -1923,100 +2042,99 @@ bld_new_pfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp) 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); + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + /* 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; - 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); + /* 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; - ow += bl; - nw += mp->wlen; - ol -= bl; - nl -= mp->wlen; - p = ow; - t = 1; } + 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; } } } @@ -2107,19 +2225,18 @@ join_new_pfx(Cline line, int len, char *word, int *missp) 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; - } + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + 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) @@ -2148,6 +2265,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = line; else ret = line; + l = line; } } else { /* The cline doesn't have a string built by reverse matching, @@ -2163,6 +2281,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = line; else ret = line; + l = line; len = 0; } else { char sav = word[len]; @@ -2180,6 +2299,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = line; else ret = line; + l = line; len = 0; } else if (strpfx(line->word, word)) { word[len] = sav; @@ -2190,6 +2310,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = line; else ret = line; + l = line; len = 0; } else { /* Not the same and no prefix, so we try to build a @@ -2208,6 +2329,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = sl; else ret = sl; + l = sl; if (!mol) { send->next = next; word += len - mnl; @@ -2238,7 +2360,11 @@ join_new_pfx(Cline line, int len, char *word, int *missp) static void bld_pfx(Cline o, Cline n, int *olp, int *nlp) { - if (o->flags & CLF_NEW) { + if (olp) + *olp = 0; + if (nlp) + *nlp = 0; + if (o->flags & CLF_PNEW) { if (o->flags & (CLF_END | CLF_MID)) /* We split the suffix in the middle and at the end into * separate runs. */ @@ -2255,7 +2381,7 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp) o->flags |= CLF_MISS; } } else if (o->flags & (CLF_END | CLF_MID)) { - o->flags |= CLF_NEW; + o->flags |= CLF_PNEW; o->prefix = join_ends(end_list(o->wlen, o->word), end_list(n->wlen, n->word), olp, nlp); } else if (o->wlen && n->wlen) { @@ -2280,7 +2406,7 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp) * 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->flags |= CLF_PNEW; o->prefix = bld_new_pfx(o->wlen, o->word, n->wlen, n->word, &mol, &mnl, NULL); newl = 0; @@ -2300,7 +2426,8 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp) if (!o->prefix && n->wlen != o->wlen) o->flags |= CLF_MISS; - } + } else + o->wlen = 0; } /* The following function are like their counterparts above, only for @@ -2353,20 +2480,19 @@ bld_line_sfx(Cpattern pat, char *line, char *lp, 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; - } + for (ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + 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) @@ -2404,84 +2530,83 @@ bld_new_sfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp) } 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); + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + 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; - 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); + 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; - ow -= bl; - nw -= mp->wlen; - ol -= bl; - nl -= mp->wlen; - p = ow; - t = 1; } + 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; } } } @@ -2554,21 +2679,20 @@ join_new_sfx(Cline line, int len, char *word, int *missp) 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; - } + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + 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) @@ -2663,7 +2787,7 @@ join_new_sfx(Cline line, int len, char *word, int *missp) static void bld_sfx(Cline o, Cline n) { - if (o->flags & CLF_NEW) { + if (o->flags & CLF_SNEW) { int miss; o->suffix = join_new_sfx(o->suffix, n->wlen, n->word, &miss); @@ -2679,7 +2803,7 @@ bld_sfx(Cline o, Cline n) new = dupstring(n->word); newl = n->wlen; } else if (!strpfx(o->word, n->word)) { - o->flags |= CLF_NEW; + o->flags |= CLF_SNEW; o->suffix = bld_new_sfx(o->wlen, o->word, n->wlen, n->word, &mol, &mnl, NULL); newl = 0; @@ -2732,7 +2856,7 @@ join_clines(Cline o, Cline n) n = q; } } - if (n->flags & CLF_MID) { + if (n && n->flags & CLF_MID) { while (o && !(o->flags & CLF_MID)) { o->word = NULL; o->flags |= CLF_DIFF; @@ -3128,6 +3252,7 @@ inst_cline(Cline l, int pl, int sl) } l = l->next; } + lastend = cs; /* 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. */ @@ -3281,6 +3406,7 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp) * to continue after the matched portion. */ w = q; iw = j; + lw = k; l = p; il = jj; ll = kk; @@ -3501,6 +3627,7 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) } w = q + 1; iw = j; + lw = k; l = p + 1; il = jj; ll = kk; @@ -3765,6 +3892,7 @@ instmatch(Cmatch m) inststrlen(m->suf, 1, (l = strlen(m->suf))); r += l; } + lastend = cs; cs = ocs; return r; } @@ -3777,10 +3905,10 @@ instmatch(Cmatch m) 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) + int flags, int aflags, Cmatcher match, char *exp, char **argv) { - char *s, *t, *e, *me, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL; - int lpl, lsl, i, pl, sl, test, bpl, bsl, lsm, llpl; + char *s, *t, *e, *me, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL; + int lpl, lsl, i, pl, sl, test, bpl, bsl, llpl, llsl; Aminfo ai; Cline lc = NULL; LinkList l; @@ -3788,43 +3916,39 @@ 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; - } else { - 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 { + if (exp) { + expl = (Cexpl) halloc(sizeof(struct cexpl)); + expl->count = expl->fcount = 0; + expl->str = dupstring(exp); + } else + expl = NULL; + + /* Store the matcher in our stack of matchers. */ + if (match) { + mst.next = mstack; + mst.matcher = match; + mstack = &mst; + + if (!mnum) + add_bmatchers(match); + + addlinknode(matchers, match); + match->refc++; + } + if (mnum && (mstack || bmatchers)) + update_bmatchers(); + + /* Get the suffixes to ignore. */ + if (ign) + aign = get_user_var(ign); /* Get the contents of the completion variables if we have * to perform matching. */ if (aflags & CAF_MATCH) { @@ -3832,6 +3956,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, lpre = dupstring(compprefix); llpl = strlen(lpre); lsuf = dupstring(compsuffix); + llsl = strlen(lsuf); } /* Now duplicate the strings we have from the command line. */ if (ipre) @@ -3848,8 +3973,6 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, lsl = strlen(psuf); } else lsl = 0; - if (aflags & CAF_MATCH) - lsm = (psuf ? !strcmp(psuf, lsuf) : (!lsuf || !*lsuf)); if (pre) pre = dupstring(pre); if (suf) @@ -3865,6 +3988,17 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, begcmgroup(group, (aflags & CAF_NOSORT)); if (aflags & CAF_NOSORT) mgroup->flags |= CGF_NOSORT; + } else { + endcmgroup(NULL); + begcmgroup("default", 0); + } + /* Select the set of matches. */ + if (aflags & CAF_ALT) { + l = fmatches; + ai = fainfo; + } else { + l = matches; + ai = ainfo; } if (remf) { remf = dupstring(remf); @@ -3908,19 +4042,19 @@ 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)))) - test = lsm; + test = (sl >= llpl + llsl && + strpfx(lpre, s) && strsfx(lsuf, s)); if (!test && mstack && - (ms = comp_match(lpre, lsuf, - (psuf ? dyncat(t, psuf) : t), + (ms = comp_match(lpre, lsuf, s, &lc, (aflags & CAF_QUOTE), &bpl, &bsl))) test = 1; + if (!test) continue; - me = e = s + sl; + pl = sl - llsl; + me = s + sl - llsl; + e = s + llpl; } else { e = s; me = s + sl; @@ -3928,8 +4062,10 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } /* Quoting? */ if (!(aflags & CAF_QUOTE)) { - te = s + pl; - s = quotename(s, &e, te, &pl); + int tmp = me - s; + + s = quotename(s, &e, me, &tmp); + me = s + tmp; sl = strlen(s); } /* The rest is almost the same as in addmatch(). */ @@ -3944,31 +4080,48 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, if (ppre) t = dyncat(ppre, t); if (!ms && mstack) { + int bl = ((aflags & CAF_MATCH) ? llpl : 0); Cline *clp = &lc, tlc; char *ss = dupstring(s), *ee = me + (ss - s); if (ppre && *ppre) { - *clp = str_cline(ppre, strlen(ppre), &tlc); + *clp = tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR); clp = &(tlc->next); } - if (ee != ss + sl || (lpsuf && *lpsuf)) { - *clp = tlc = getcline(ss, ee - ss, + if (bl) { + *clp = str_cline(ss, bl, &tlc); + clp = &(tlc->next); + } + if (ee != ss + sl) { + *clp = tlc = getcline(ss + bl, ee - ss - bl, 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); - } + *clp = str_cline(ee, (ss + sl) - ee, &tlc); + clp = &(tlc->next); } else { - *clp = tlc = getcline(NULL, 0, ss, sl, + *clp = tlc = getcline(NULL, 0, ss + bl, sl - bl, + CLF_END | CLF_VAR); + clp = &(tlc->next); + } + if (psuf && *psuf) { + *clp = tlc = getcline(NULL, 0, psuf, lsl, CLF_END | CLF_VAR); clp = &(tlc->next); } *clp = NULL; + } else if (mstack) { + Cline tlc; + + if (ppre && *ppre) { + tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR); + tlc->next = lc; + lc = tlc; + } + if (psuf && *psuf) { + for (tlc = lc; tlc->next; tlc = tlc->next); + tlc->next = getcline(NULL, 0, psuf, lsl, + CLF_END | CLF_VAR); + } } if (ipre && *ipre) { Cline tlc = prepend_cline(ipre, lc); @@ -4047,6 +4200,8 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } } compnmatches = mnum; + if (exp) + addexpl(); } LASTALLOC; } SWITCHBACKHEAPS; @@ -4129,8 +4284,10 @@ addmatch(char *s, char *t) mpl = fpl; msl = fsl; } else { if ((cp = filecomp)) { - if ((test = domatch(s, filecomp, 0))) + if ((test = domatch(s, filecomp, 0))) { + e = s + sl; cc = 1; + } } else { e = s + sl - fsl; if ((test = !strncmp(s, fpre, fpl))) @@ -4168,6 +4325,7 @@ addmatch(char *s, char *t) strcat(tt, s); if (lpsuf) strcat(tt, lpsuf); + e += (tt - s); untokenize(s = tt); sl = strlen(s); } @@ -4196,9 +4354,10 @@ addmatch(char *s, char *t) ((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) || ((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) { if (sl >= rpl + rsl || mstack) { - if (cp) + if (cp) { test = domatch(s, patcomp, 0); - else { + e = s + sl; + } else { e = s + sl - rsl; if ((test = !strncmp(s, rpre, rpl))) if ((test = !strcmp(e, rsuf))) { @@ -4888,9 +5047,11 @@ makecomplist(char *s, int incmd, int lst) { struct cmlist ms; Cmlist m; + char *os = s; - /* If we already have a list from a previous execution of this * - * function, skip the list building code. */ + /* We build a copy of the list of matchers to use to make sure that this + * works even if a shell function called from the completion code changes + * the global matchers. */ if ((m = cmatcher)) { Cmlist mm, *mp = &mm; @@ -4906,16 +5067,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; + + /* Walk through the global matchers. */ for (;;) { + bmatchers = NULL; if (m) { ms.next = NULL; ms.matcher = m->matcher; mstack = &ms; - bmstack0.matcher = m->matcher; + + /* Store the matchers used in the bmatchers list which is used + * when building new parts for the string to insert into the + * line. */ + add_bmatchers(m->matcher); } else mstack = NULL; @@ -4932,12 +5096,12 @@ makecomplist(char *s, int incmd, int lst) lastambig = 0; amatches = 0; mnum = 0; - had_lm = 0; begcmgroup("default", 0); ccused = newlinklist(); ccstack = newlinklist(); + s = dupstring(os); if (compfunc) callcompfunc(s, compfunc); else @@ -5004,7 +5168,7 @@ ctokenize(char *p) /**/ char * -comp_str(int *ipl, int *pl) +comp_str(int *ipl, int *pl, int untok) { char *p = dupstring(compprefix); char *s = dupstring(compsuffix); @@ -5012,12 +5176,14 @@ comp_str(int *ipl, int *pl) char *str; int lp, ls, lip; - ctokenize(p); - remnulargs(p); - ctokenize(s); - remnulargs(s); - ctokenize(ip); - remnulargs(ip); + if (!untok) { + ctokenize(p); + remnulargs(p); + ctokenize(s); + remnulargs(s); + ctokenize(ip); + remnulargs(ip); + } ls = strlen(s); lip = strlen(ip); lp = strlen(p); @@ -5041,7 +5207,7 @@ makecomplistcall(Compctl cc) SWITCHHEAPS(compheap) { HEAPALLOC { int ooffs = offs, lip, lp; - char *str = comp_str(&lip, &lp); + char *str = comp_str(&lip, &lp, 0); offs = lip + lp; cc->refc++; @@ -5073,7 +5239,7 @@ makecomplistctl(int flags) SWITCHHEAPS(compheap) { HEAPALLOC { int ooffs = offs, lip, lp; - char *str = comp_str(&lip, &lp), *t; + char *str = comp_str(&lip, &lp, 0), *t; char *os = cmdstr, **ow = clwords, **p, **q; int on = clwnum, op = clwpos; @@ -5571,19 +5737,16 @@ 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; - } + + if (!mnum) + add_bmatchers(cc->matcher); + addlinknode(matchers, cc->matcher); cc->matcher->refc++; - } else { - bmstack1.matcher = NULL; - if (mnum) - had_lm = 1; } + if (mnum && (mstack || bmatchers)) + update_bmatchers(); + /* Insert the prefix (compctl -P), if any. */ if (cc->prefix) { int pl = 0; @@ -6344,7 +6507,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) } if ((tt = cc->explain)) { - if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) { + tt = dupstring(tt); + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { singsub(&tt); untokenize(tt); } @@ -6358,7 +6522,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) begcmgroup("default", 0); } else if ((tt = cc->explain)) { - if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) { + tt = dupstring(tt); + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { singsub(&tt); untokenize(tt); } @@ -6631,7 +6796,7 @@ begcmgroup(char *n, int nu) } } mgroup = (Cmgroup) halloc(sizeof(struct cmgroup)); - mgroup->name = n; + mgroup->name = dupstring(n); mgroup->flags = mgroup->lcount = mgroup->mcount = 0; mgroup->matches = NULL; mgroup->ylist = NULL; @@ -6872,7 +7037,7 @@ do_ambiguous(void) /* 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) && !fromcomp && + if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !(fromcomp & FC_LINE) && (usemenu == 0 || unset(AUTOMENU))) { do_single(ainfo->exactm); invalidatelist(); @@ -6945,7 +7110,6 @@ do_ambiguous(void) merge_cline(lc, ps, lp, NULL, 0, 0); } inst_cline(lc, pl, sl); - } else { inststrlen(ps, 1, -1); ocs = cs; @@ -6966,6 +7130,7 @@ do_ambiguous(void) cs -= brsl; inststrlen(brend, 1, -1); } + lastend = cs; cs = ocs; } /* la is non-zero if listambiguous may be used. Copying and @@ -6973,11 +7138,13 @@ do_ambiguous(void) * solution. Really. */ la = (ll != oll || strncmp(oline, (char *) line, ll)); - /* 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 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. Also we remember if we just moved the * + * cursor into the word. */ + fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) | + ((atend && cs != lastend) ? FC_INWORD : 0)); /* * If the LIST_AMBIGUOUS option (meaning roughly `show a list only * diff --git a/Src/exec.c b/Src/exec.c index 59061af3f..764b7140c 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2558,7 +2558,7 @@ execarith(Cmd cmd) val = matheval(e); } if (isset(XTRACE)) { - fprintf(stderr, " ))\n", stderr); + fprintf(stderr, " ))\n"); fflush(stderr); } errflag = 0; diff --git a/Src/glob.c b/Src/glob.c index 516f75618..47fa63567 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -71,6 +71,7 @@ struct gmatch { int badcshglob; static int mode; /* != 0 if we are parsing glob patterns */ +static int scanning; /* != 0 if we are scanning file paths */ static int pathpos; /* position in pathbuf */ static int matchsz; /* size of matchbuf */ static int matchct; /* number of matches found */ @@ -134,7 +135,7 @@ struct complist { struct comp { Comp left, right, next, exclude; char *str; - int stat; + int stat, errsmax; }; /* Type of Comp: a closure with one or two #'s, the end of a * @@ -162,6 +163,18 @@ struct comp { #define GF_PATHADD 1 /* file glob, adding path components */ #define GF_TOPLEV 2 /* outside (), so ~ ends main match */ +/* Next character after one which may be a Meta (x is any char *) */ +#define METANEXT(x) (*(x) == Meta ? (x)+2 : (x)+1) +/* + * Increment pointer which may be on a Meta (x is a pointer variable), + * returning the incremented value (i.e. like pre-increment). + */ +#define METAINC(x) ((x) += (*(x) == Meta) ? 2 : 1) +/* + * Return unmetafied char from string (x is any char *) + */ +#define UNMETA(x) (*(x) == Meta ? (x)[1] ^ 32 : *(x)) + static char *pptr; /* current place in string being matched */ static Comp tail; static int first; /* are leading dots special? */ @@ -352,6 +365,25 @@ haswilds(char *str) return 0; } +/* Flags to apply to current level of grouping */ + +static int addflags; + +/* + * Approximate matching. + * + * errsmax is used while parsing to store the current number + * of errors allowed. While executing it is usually set to -1, + * but can be set to something else to fix a different maximum + * no. of errors which acts as a limit on the local value: see + * scanner() for why this is necessary. + * + * errsfound is used while executing a pattern to record the + * number of errors found so far. + */ + +static int errsmax, errsfound; + /* Do the globbing: scanner is called recursively * * with successive bits of the path until we've * * tried all of it. */ @@ -363,6 +395,7 @@ scanner(Complist q) Comp c; int closure; int pbcwdsav = pathbufcwd; + int errssofar = errsfound; struct dirsav ds; ds.ino = ds.dev = 0; @@ -381,7 +414,7 @@ scanner(Complist q) c = q->comp; /* Now the actual matching for the current path section. */ if (!(c->next || c->left) && !haswilds(c->str) - && (!(c->stat & (C_LCMATCHUC|C_IGNCASE)) + && (!((c->stat & (C_LCMATCHUC|C_IGNCASE)) || c->errsmax) || !strcmp(".", c->str) || !strcmp("..", c->str))) { /* * We always need to match . and .. explicitly, even if we're @@ -433,6 +466,7 @@ scanner(Complist q) ((glob_pre && !strpfx(glob_pre, fn)) || (glob_suf && !strsfx(glob_suf, fn)))) continue; + errsfound = errssofar; if (domatch(fn, c, gf_noglobdots)) { /* if this name matchs the pattern... */ if (pbcwdsav == pathbufcwd && @@ -453,7 +487,34 @@ scanner(Complist q) if (dirs) { int l; - /* if not the last component in the path */ + /* + * If not the last component in the path: + * + * If we made an approximation in the new path segment, + * and the pattern includes closures other than a + * trailing * (we only test if it is not a string), + * then it is possible we made too many errors. For + * example, (ab)#(cb)# will match the directory abcb + * with one error if allowed to, even though it can + * match with none. This will stop later parts of the + * path matching, so we need to check by reducing the + * maximum number of errors and seeing if the directory + * still matches. Luckily, this is not a terribly + * common case, since complex patterns typically occur + * in the last part of the path which is not affected + * by this problem. + */ + if (errsfound > errssofar && (c->next || c->left)) { + errsmax = errsfound - 1; + while (errsmax >= errssofar) { + errsfound = errssofar; + if (!domatch(fn, c, gf_noglobdots)) + break; + errsmax = errsfound - 1; + } + errsfound = errsmax + 1; + errsmax = -1; + } if (closure) { /* if matching multiple directories */ struct stat buf; @@ -470,9 +531,14 @@ scanner(Complist q) continue; } l = strlen(fn) + 1; - subdirs = hrealloc(subdirs, subdirlen, subdirlen + l); + subdirs = hrealloc(subdirs, subdirlen, subdirlen + l + + sizeof(int)); strcpy(subdirs + subdirlen, fn); subdirlen += l; + /* store the count of errors made so far, too */ + memcpy(subdirs + subdirlen, (char *)&errsfound, + sizeof(int)); + subdirlen += sizeof(int); } else /* if the last filename component, just add it */ insert(fn, 1); @@ -482,8 +548,11 @@ scanner(Complist q) if (subdirs) { int oppos = pathpos; - for (fn = subdirs; fn < subdirs+subdirlen; fn += strlen(fn) + 1) { + for (fn = subdirs; fn < subdirs+subdirlen; ) { addpath(fn); + fn += strlen(fn) + 1; + memcpy((char *)&errsfound, fn, sizeof(int)); + fn += sizeof(int); scanner((q->closure) ? q : q->next); /* scan next level */ pathbuf[pathpos = oppos] = '\0'; } @@ -502,16 +571,13 @@ scanner(Complist q) /* Parse a series of path components pointed to by pptr */ -/* Flags to apply to current level of grourping */ - -static int addflags; - /**/ static Comp compalloc(void) { Comp c = (Comp) alloc(sizeof *c); c->stat |= addflags; + c->errsmax = errsmax; return c; } @@ -519,10 +585,19 @@ compalloc(void) static int getglobflags() { + char *nptr; /* (#X): assumes we are still positioned on the initial '(' */ pptr++; while (*++pptr && *pptr != Outpar) { switch (*pptr) { + case 'a': + /* Approximate matching, max no. of errors follows */ + errsmax = zstrtol(++pptr, &nptr, 10); + if (pptr == nptr) + return 1; + pptr = nptr-1; + break; + case 'l': /* Lowercase in pattern matches lower or upper in target */ addflags |= C_LCMATCHUC; @@ -635,6 +710,7 @@ parsecomp(int gflag) if (eptr == cstr) { /* if no string yet, carry on and get one. */ c->stat = addflags; + c->errsmax = errsmax; cstr = pptr; continue; } @@ -817,6 +893,7 @@ parsecompsw(int gflag) { Comp c1, c2, c3, excl = NULL, stail = tail; int oaddflags = addflags; + int oerrsmax = errsmax; char *sptr; /* @@ -845,12 +922,14 @@ parsecompsw(int gflag) return NULL; if (isset(EXTENDEDGLOB) && *pptr == Tilde) { /* Matching remainder of pattern excludes the pattern from matching */ - int oldmode = mode; + int oldmode = mode, olderrsmax = errsmax; mode = 1; + errsmax = 0; pptr++; excl = parsecomp(gflag); mode = oldmode; + errsmax = olderrsmax; if (!excl) return NULL; } @@ -878,8 +957,10 @@ parsecompsw(int gflag) c2->stat |= C_PATHADD; c1 = c2; } - if (!(gflag & GF_TOPLEV)) + if (!(gflag & GF_TOPLEV)) { addflags = oaddflags; + errsmax = oerrsmax; + } return c1; } @@ -965,6 +1046,7 @@ parsepat(char *str) { mode = 0; /* path components present */ addflags = 0; + errsmax = 0; pptr = str; tail = NULL; /* @@ -1102,7 +1184,7 @@ static int gmatchcmp(Gmatch a, Gmatch b) { int i, *s; - long r; + long r = 0L; for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) { switch (*s & ~GS_DESC) { @@ -1594,10 +1676,14 @@ glob(LinkList list, LinkNode np) matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) * sizeof(struct gmatch)); matchct = 0; + errsfound = 0; + errsmax = -1; /* The actual processing takes place here: matches go into * * matchbuf. This is the only top-level call to scanner(). */ + scanning = 1; scanner(q); + scanning = 0; /* Deal with failures to match depending on options */ if (matchct) @@ -2427,6 +2513,14 @@ domatch(char *str, Comp c, int fist) int ret; pptr = str; first = fist; + /* + * If scanning paths, keep the error count over the whole + * filename + */ + if (!scanning) { + errsfound = 0; + errsmax = -1; + } if (*pptr == Nularg) pptr++; PERMALLOC { @@ -2444,15 +2538,17 @@ static int excluded(Comp c, char *eptr) { char *saves = pptr; - int savei = first, savel = longest, ret; + int savei = first, savel = longest, savee = errsfound, ret; first = 0; /* * Even if we've been told always to match the longest string, * i.e. not anchored to the end, we want to match the full string * we've already matched when we're trying to exclude it. + * Likewise, approximate matching here is treated separately. */ longest = 0; + errsfound = 0; if (PATHADDP(c) && pathpos) { VARARR(char, buf, pathpos + strlen(eptr) + 1); @@ -2470,13 +2566,18 @@ excluded(Comp c, char *eptr) pptr = saves; first = savei; longest = savel; + errsfound = savee; return ret; } +/* + * Structure for storing instances of a pattern in a closured. + */ struct gclose { - char *start; - char *end; + char *start; /* Start of this instance */ + char *end; /* End of this instance */ + int errsfound; /* Errors found up to start of instance */ }; typedef struct gclose *Gclose; @@ -2488,34 +2589,48 @@ typedef struct gclose *Gclose; * also not bother going any further, since the first time we got to * that position (when it was marked), we must already have failed on * and backtracked over any further closure matches beyond that point. + * If we are using approximation, the number in the string is incremented + * by the current error count; if we come back to that point with a + * lower error count, it is worth trying from that point again, but + * we must now mark that point in trystring with the new error. */ /**/ static void -addclosures(Comp c, LinkList closlist, int *pdone, char *trystring) +addclosures(Comp c, LinkList closlist, int *pdone, unsigned char *trystring) { Gclose gcnode; char *opptr = pptr; + unsigned char *tpos; while (*pptr) { if (STARP(c)) { - if (trystring[(pptr+1)-opptr]) - break; + if (*(tpos = trystring + ((pptr+1) - opptr))) { + if ((unsigned)(errsfound+1) >= *tpos) + break; + *tpos = (unsigned)(errsfound+1); + } gcnode = (Gclose)zalloc(sizeof(struct gclose)); gcnode->start = pptr; - gcnode->end = ++pptr; + gcnode->end = METAINC(pptr); + gcnode->errsfound = errsfound; } else { char *saves = pptr; + int savee = errsfound; if (OPTIONALP(c) && *pdone >= 1) return; if (!matchonce(c) || saves == pptr || - trystring[pptr-opptr]) { + (*(tpos = trystring + (pptr-opptr)) && + (unsigned)(errsfound+1) >= *tpos)) { pptr = saves; break; } + if (*tpos) + *tpos = (unsigned)(errsfound+1); gcnode = (Gclose)zalloc(sizeof(struct gclose)); gcnode->start = saves; gcnode->end = pptr; + gcnode->errsfound = savee; } pushnode(closlist, gcnode); (*pdone)++; @@ -2523,13 +2638,29 @@ addclosures(Comp c, LinkList closlist, int *pdone, char *trystring) } /* - * Match characters with case-insensitivity. - * Note CHARMATCH(x,y) != CHARMATCH(y,x) + * Match characters with case-insensitivity. Note charmatch(x,y) != + * charmatch(y,x): the first argument comes from the target string, the + * second from the pattern. This used to be a macro, and in principle + * could be turned back into one. */ -#define CHARMATCH(x, y) \ -(x == y || (((c->stat & C_IGNCASE) ? (tulower(x) == tulower(y)) : \ - (c->stat & C_LCMATCHUC) ? (islower(y) && tuupper(y) == x) : 0))) +/**/ +static int +charmatch(Comp c, char *x, char *y) +{ + /* + * This takes care of matching two characters which may be a + * two byte sequence, Meta followed by something else. + * Here we bypass tulower() and tuupper() for speed. + */ + int xi = (STOUC(UNMETA(x)) & 0xff), yi = (STOUC(UNMETA(y)) & 0xff); + return xi == yi || + (((c->stat & C_IGNCASE) ? + ((isupper(xi) ? tolower(xi) : xi) == + (isupper(yi) ? tolower(yi) : yi)) : + (c->stat & C_LCMATCHUC) ? + (islower(yi) && toupper(yi) == xi) : 0)); +} /* see if current string in pptr matches c */ @@ -2539,14 +2670,16 @@ doesmatch(Comp c) { if (CLOSUREP(c)) { int done, retflag = 0; - char *saves, *trystring, *opptr; + char *saves, *opptr; + unsigned char *trystring, *tpos; + int savee; LinkList closlist; Gclose gcnode; if (first && *pptr == '.') return 0; - if (!inclosure && !c->left) { + if (!inclosure && !c->left && !c->errsmax) { /* We are not inside another closure, and the current * pattern is a simple string. We handle this very common * case specially: otherwise, matches like *foo* are @@ -2561,26 +2694,30 @@ doesmatch(Comp c) !itok(looka)) { /* Another simple optimisation for a very common case: * we are processing a * and there is - * an ordinary character match next. We look ahead for - * that character, taking care of Meta bytes. + * an ordinary character match (which may not be a Metafied + * character, just to make it easier) next. We look ahead + * for that character, taking care of Meta bytes. */ while (*pptr) { for (; *pptr; pptr++) { if (*pptr == Meta) pptr++; - else if (CHARMATCH(STOUC(*pptr), STOUC(looka))) + else if (charmatch(c, pptr, &looka)) break; } if (!*(saves = pptr)) break; + savee = errsfound; if (doesmatch(c->next)) return 1; pptr = saves+1; + errsfound = savee; } } else { /* Standard track-forward code */ for (done = 0; ; done++) { saves = pptr; + savee = errsfound; if ((done || ONEHASHP(c) || OPTIONALP(c)) && ((!c->next && (!LASTP(c) || !*pptr || longest)) || (c->next && doesmatch(c->next)))) @@ -2588,6 +2725,7 @@ doesmatch(Comp c) if (done && OPTIONALP(c)) return 0; pptr = saves; + errsfound = savee; first = 0; if (STARP(c)) { if (!*pptr) @@ -2602,7 +2740,7 @@ doesmatch(Comp c) /* The full, gory backtracking code is now necessary. */ inclosure++; closlist = newlinklist(); - trystring = zcalloc(strlen(pptr)+1); + trystring = (unsigned char *)zcalloc(strlen(pptr)+1); opptr = pptr; /* Start by making a list where each match is as long @@ -2621,7 +2759,7 @@ doesmatch(Comp c) retflag = 1; break; } - trystring[saves-opptr] = 1; + trystring[saves-opptr] = (unsigned)(errsfound + 1); /* * If we failed, the first thing to try is whether we can * shorten the match using the last pattern in the closure. @@ -2633,14 +2771,18 @@ doesmatch(Comp c) char savec = *gcnode->end; *gcnode->end = '\0'; pptr = gcnode->start; + errsfound = gcnode->errsfound; if (matchonce(c) && pptr != gcnode->start - && !trystring[pptr-opptr]) { + && (!*(tpos = trystring + (pptr-opptr)) || + *tpos > (unsigned)(errsfound+1))) { + if (*tpos) + *tpos = (unsigned)(errsfound+1); *gcnode->end = savec; gcnode->end = pptr; /* Try again to construct a list based on * this new position */ - addclosures(c, closlist, &done, trystring+(pptr-opptr)); + addclosures(c, closlist, &done, tpos); continue; } *gcnode->end = savec; @@ -2650,6 +2792,7 @@ doesmatch(Comp c) */ if ((gcnode = (Gclose)getlinknode(closlist))) { pptr = gcnode->start; + errsfound = gcnode->errsfound; zfree(gcnode, sizeof(struct gclose)); done--; } else @@ -2723,8 +2866,7 @@ rangematch(char **patptr, int ch, int rchar) #define PAT(X) (pat[X] == Meta ? pat[(X)+1] ^ 32 : untok(pat[X])) #define PPAT(X) (pat[(X)-1] == Meta ? pat[X] ^ 32 : untok(pat[X])) - for (pat++; *pat != Outbrack && *pat; - *pat == Meta ? pat += 2 : pat++) { + for (pat++; *pat != Outbrack && *pat; METAINC(pat)) { if (*pat == Inbrack) { /* Inbrack can only occur inside a range if we found [:...:]. */ pat += 2; @@ -2748,6 +2890,22 @@ rangematch(char **patptr, int ch, int rchar) *patptr = pat; } +/* + * matchonce() is the core of the pattern matching, handling individual + * strings and instances of a pattern in a closure. + * + * Note on approximate matching: The rule is supposed to be + * (1) Take the longest possible match without approximation. + * (2) At any failure, make the single approximation that results + * in the longest match for the remaining part (which may + * include further approximations). + * (3) If we match the same distance, take the one with fewer + * approximations. + * If this is wrong, I haven't yet discovered a counterexample. Email + * lines are now open. + * pws 1999/02/23 + */ + /**/ static int matchonce(Comp c) @@ -2758,7 +2916,7 @@ matchonce(Comp c) if (!pat || !*pat) { /* No current pattern (c->str). */ char *saves; - int savei; + int savee, savei; if (errflag) return 0; @@ -2766,6 +2924,7 @@ matchonce(Comp c) * check for exclusion of pattern or alternatives. */ saves = pptr; savei = first; + savee = errsfound; /* Loop over alternatives with exclusions: (foo~bar|...). * * Exclusions apply to the pattern in c->left. */ if (c->left || c->right) { @@ -2812,6 +2971,7 @@ matchonce(Comp c) */ exclend--; pptr = saves; + errsfound = savee; } inclosure--; if (ret) @@ -2822,19 +2982,43 @@ matchonce(Comp c) if (c->right && (!ret || inclosure)) { /* If in a closure, we always want the longest match. */ char *newpptr = pptr; + int newerrsfound = errsfound; pptr = saves; first = savei; + errsfound = savee; ret2 = doesmatch(c->right); - if (ret && (!ret2 || pptr < newpptr)) + if (ret && (!ret2 || pptr < newpptr)) { pptr = newpptr; + errsfound = newerrsfound; + } + } + if (!ret && !ret2) { + pptr = saves; + first = savei; + errsfound = savee; + break; } - if (!ret && !ret2) - return 0; } if (CLOSUREP(c)) return 1; - if (!c->next) /* no more patterns left */ - return (!LASTP(c) || !*pptr || longest); + if (!c->next) { + /* + * No more patterns left, but we may still be in the middle + * of a match, in which case alles in Ordnung... + */ + if (!LASTP(c)) + return 1; + /* + * ...else we're at the last pattern, so this is our last + * ditch attempt at an approximate match: try to omit the + * last few characters. + */ + for (; *pptr && errsfound < c->errsmax && + (errsmax == -1 || errsfound < errsmax); + METAINC(pptr)) + errsfound++; + return !*pptr || longest; + } /* optimisation when next pattern is not a closure */ if (!CLOSUREP(c->next)) { c = c->next; @@ -2843,7 +3027,10 @@ matchonce(Comp c) } return doesmatch(c->next); } - /* Don't match leading dot if first is set */ + /* + * Don't match leading dot if first is set + * (don't even try for an approximate match) + */ if (first && *pptr == '.' && *pat != '.') return 0; if (*pat == Star) { /* final * is not expanded to ?#; returns success */ @@ -2852,39 +3039,47 @@ matchonce(Comp c) return 1; } first = 0; /* finished checking start of pattern */ - if (*pat == Quest && *pptr) { + if (*pat == Quest) { /* match exactly one character */ - if (*pptr == Meta) - pptr++; - pptr++; + if (!*pptr) + break; + METAINC(pptr); pat++; continue; } if (*pat == Inbrack) { /* Match groups of characters */ char ch; + char *saves, *savep; if (!*pptr) break; - ch = *pptr == Meta ? pptr[1] ^ 32 : *pptr; + saves = pptr; + savep = pat; + ch = UNMETA(pptr); if (pat[1] == Hat || pat[1] == '^' || pat[1] == '!') { /* group is negated */ *++pat = Hat; rangematch(&pat, ch, Hat); DPUTS(!*pat, "BUG: something is very wrong in doesmatch()"); - if (*pat != Outbrack) + if (*pat != Outbrack) { + pptr = saves; + pat = savep; break; + } pat++; - *pptr == Meta ? pptr += 2 : pptr++; + METAINC(pptr); continue; } else { /* pattern is not negated (affirmed? asserted?) */ rangematch(&pat, ch, Inbrack); DPUTS(!pat || !*pat, "BUG: something is very wrong in doesmatch()"); - if (*pat == Outbrack) + if (*pat == Outbrack) { + pptr = saves; + pat = savep; break; - for (*pptr == Meta ? pptr += 2 : pptr++; - *pat != Outbrack; pat++); + } + for (METAINC(pptr); *pat != Outbrack; pat++); pat++; continue; } @@ -2892,7 +3087,7 @@ matchonce(Comp c) if (*pat == Inang) { /* Numeric globbing. */ unsigned long t1, t2, t3; - char *ptr; + char *ptr, *saves = pptr, *savep = pat; if (!idigit(*pptr)) break; @@ -2933,19 +3128,146 @@ matchonce(Comp c) pptr--; t1 /= 10; } - if (t1 < t2 || (!not3 && t1 > t3)) + if (t1 < t2 || (!not3 && t1 > t3)) { + pptr = saves; + pat = savep; break; + } } continue; } - if (CHARMATCH(STOUC(*pptr), STOUC(*pat))) { - /* just plain old characters */ - pptr++; - pat++; + /* itok(Meta) is zero */ + DPUTS(itok(*pat), "BUG: matching tokenized character"); + if (charmatch(c, pptr, pat)) { + /* just plain old characters (or maybe unplain new characters) */ + METAINC(pptr); + METAINC(pat); continue; } + if (errsfound < c->errsmax && + (errsmax == -1 || errsfound < errsmax)) { + /* + * We tried to match literal characters and failed. Now it's + * time to match approximately. Note this doesn't handle the + * case where we *didn't* try to match literal characters, + * including the case where we were already at the end of the + * pattern, because then we never get here. In that case the + * pattern has to be matched exactly, but we could maybe + * advance up the target string before trying it, so we have to + * handle that case elsewhere. + */ + char *saves = pptr, *savep = c->str; + char *maxpptr = pptr, *patnext = METANEXT(pat); + int savee, maxerrs = -1; + + /* First try to advance up the pattern. */ + c->str = patnext; + savee = ++errsfound; + if (matchonce(c)) { + maxpptr = pptr; + maxerrs = errsfound; + } + errsfound = savee; + + if (*saves) { + /* + * If we have characters on both strings, we have more + * choice. + * + * Try to edge up the target string. + */ + char *strnext = METANEXT(saves); + pptr = strnext; + c->str = pat; + if (matchonce(c) && + (maxerrs == -1 || pptr > maxpptr || + (pptr == maxpptr && errsfound <= maxerrs))) { + maxpptr = pptr; + maxerrs = errsfound; + } + errsfound = savee; + + /* + * Try edging up both of them at once. + * Note this takes precedence in the case of equal + * length as we get further up the pattern. + */ + c->str = patnext; + pptr = strnext; + if (matchonce(c) && + (maxerrs == -1 || pptr > maxpptr || + (pptr == maxpptr && errsfound <= maxerrs))) { + maxpptr = pptr; + maxerrs = errsfound; + } + errsfound = savee; + + /* + * See if we can transpose: that counts as just one error. + * + * Note, however, `abanana' and `banana': transposing + * is wrong here, as it gets us two errors, + * [ab][a]nana vs [ba][]nana, instead of the correct + * [a]banana vs []banana, so we still need to try + * the other possibilities. + */ + if (strnext && patnext && !itok(*patnext) && + charmatch(c, strnext, pat) && + charmatch(c, saves, patnext)) { + c->str = patnext; + METAINC(c->str); + + pptr = strnext; + METAINC(pptr); + + if (matchonce(c) && + (maxerrs == -1 || pptr > maxpptr || + (pptr == maxpptr && errsfound <= maxerrs))) { + maxpptr = pptr; + maxerrs = errsfound; + } + } + } + /* + * We don't usually restore state on failure, but we need + * to fix up the Comp structure which we altered to + * look at the tail of the pattern. + */ + c->str = savep; + /* + * If that failed, game over: we don't want to break + * and try the other approximate test, because we just did + * that. + */ + if (maxerrs == -1) + return 0; + pptr = maxpptr; + errsfound = maxerrs; + return 1; + } break; } + if (*pptr && errsfound < c->errsmax && + (errsmax == -1 || errsfound < errsmax)) { + /* + * The pattern failed, but we can try edging up the target string + * and rematching with an error. Note we do this from wherever we + * got to in the pattern string c->str, not the start. hence the + * need to modify c->str. + * + * At this point, we don't have a literal character in the pattern + * (handled above), so we don't try any funny business on the + * pattern itself. + */ + int ret; + char *savep = c->str; + errsfound++; + METAINC(pptr); + c->str = pat; + ret = matchonce(c); + c->str = savep; + return ret; + } return 0; } @@ -2958,6 +3280,7 @@ parsereg(char *str) remnulargs(str); mode = 1; /* no path components */ addflags = 0; + errsmax = 0; pptr = str; tail = NULL; return parsecompsw(GF_TOPLEV); diff --git a/Src/hashtable.c b/Src/hashtable.c index b816f1892..72e4db21b 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -607,6 +607,9 @@ hashdir(char **dirp) Cmdnam cn; DIR *dir; char *fn; +#ifdef _WIN32 + char *exe; +#endif if (isrelative(*dirp) || !(dir = opendir(unmeta(*dirp)))) return; @@ -618,6 +621,23 @@ hashdir(char **dirp) cn->u.name = dirp; cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn); } +#ifdef _WIN32 + /* Hash foo.exe as foo, since when no real foo exists, foo.exe + will get executed by DOS automatically. This quiets + spurious corrections when CORRECT or CORRECT_ALL is set. */ + if ((exe = strrchr(fn, '.')) && + (exe[1] == 'E' || exe[1] == 'e') && + (exe[2] == 'X' || exe[2] == 'x') && + (exe[3] == 'E' || exe[3] == 'e') && exe[4] == 0) { + *exe = 0; + if (!cmdnamtab->getnode(cmdnamtab, fn)) { + cn = (Cmdnam) zcalloc(sizeof *cn); + cn->flags = 0; + cn->u.name = dirp; + cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn); + } + } +#endif /* _WIN32 */ } closedir(dir); } diff --git a/Src/module.c b/Src/module.c index f91650a7f..1117571a4 100644 --- a/Src/module.c +++ b/Src/module.c @@ -567,6 +567,37 @@ load_module(char const *name) return m; } +/* This ensures that the module with the name given as the second argument + * is loaded. + * The third argument should be non-zero if the function should complain + * about trying to load a module with a full path name in restricted mode. + * The last argument should be non-zero if this function should signal an + * error if the module is already loaded. + * The return value is the module of NULL if the module couldn't be loaded. */ + +/**/ +Module +require_module(char *nam, char *module, int res, int test) +{ + Module m = NULL; + LinkNode node; + + node = find_module(module); + if (node && (m = ((Module) getdata(node)))->handle && + !(m->flags & MOD_UNLOAD)) { + if (test) { + zwarnnam(nam, "module %s already loaded.", module, 0); + return NULL; + } + } else if (res && isset(RESTRICTED) && strchr(module, '/')) { + zwarnnam(nam, "%s: restricted", module, 0); + return NULL; + } else + return load_module(module); + + return m; +} + /**/ void add_dep(char *name, char *from) @@ -963,22 +994,10 @@ bin_zmodload_load(char *nam, char **args, char *ops) return 0; } else { /* load modules */ - for (; *args; args++) { - Module m; - - node = find_module(*args); - if (node && (m = ((Module) getdata(node)))->handle && - !(m->flags & MOD_UNLOAD)) { - if (!ops['i']) { - zwarnnam(nam, "module %s already loaded.", *args, 0); - ret = 1; - } - } else if (isset(RESTRICTED) && strchr(*args, '/')) { - zwarnnam(nam, "%s: restricted", *args, 0); + for (; *args; args++) + if (!require_module(nam, *args, 1, (!ops['i']))) ret = 1; - } else if (!load_module(*args)) - ret = 1; - } + return ret; } } diff --git a/Src/params.c b/Src/params.c index 77166209f..e8182815e 100644 --- a/Src/params.c +++ b/Src/params.c @@ -679,11 +679,13 @@ static char **garr; static long getarg(char **str, int *inv, Value v, int a2, long *w) { - int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i; + int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash; char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt; long r = 0; Comp c; + ishash = (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED); + /* first parse any subscription flags */ if (v->pm && (*s == '(' || *s == Inpar)) { int escapes = 0; @@ -771,12 +773,13 @@ getarg(char **str, int *inv, Value v, int a2, long *w) } else if (rev) { v->isarr |= SCANPM_WANTVALS; } - if (!down && v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) + if (!down && ishash) v->isarr &= ~SCANPM_MATCHMANY; *inv = ind; } - for (t=s, i=0; *t && ((*t != ']' && *t != Outbrack && *t != ',') || i); t++) + for (t=s, i=0; + *t && ((*t != ']' && *t != Outbrack && (ishash || *t != ',')) || i); t++) if (*t == '[' || *t == Inbrack) i++; else if (*t == ']' || *t == Outbrack) @@ -791,7 +794,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) singsub(&s); if (!rev) { - if (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) { + if (ishash) { HashTable ht = v->pm->gets.hfn(v->pm); if (!ht) { ht = newparamtable(17, v->pm->nam); @@ -867,7 +870,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) if ((c = parsereg(s))) { if (v->isarr) { - if (PM_TYPE(v->pm->flags) == PM_HASHED) { + if (ishash) { scancomp = c; if (ind) v->isarr |= SCANPM_MATCHKEY; diff --git a/Src/subst.c b/Src/subst.c index 66c363145..2e9b84718 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -999,11 +999,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) if (!(v = fetchvalue((subexp ? &ov : &s), (wantt ? -1 : ((unset(KSHARRAYS) || inbrace) ? 1 : -1)), - hkeys|hvals))) + hkeys|hvals)) || + (v->pm && (v->pm->flags & PM_UNSET))) vunset = 1; if (wantt) { - if (v) { + if (v && v->pm && !(v->pm->flags & PM_UNSET)) { int f = v->pm->flags; switch (PM_TYPE(f)) { diff --git a/Src/system.h b/Src/system.h index 292943dd9..650690b51 100644 --- a/Src/system.h +++ b/Src/system.h @@ -70,8 +70,15 @@ char *alloca _((size_t)); # endif #endif -#ifdef HAVE_LIBC_H /* NeXT */ -# include +/* + * libc.h in an optional package for Debian Linux is broken (it + * defines dup() as a synonym for dup2(), which has a different + * number of arguments), so just include it for next. + */ +#ifdef __NeXT__ +# ifdef HAVE_LIBC_H +# include +# endif #endif #ifdef HAVE_SYS_TYPES_H diff --git a/Src/zsh.export b/Src/zsh.export index bfad7aea2..d31e7902e 100644 --- a/Src/zsh.export +++ b/Src/zsh.export @@ -173,6 +173,7 @@ refreshptr remlpaths remnulargs removehashnode +require_module resetneeded restoredir reswdtab -- cgit 1.4.1