From a61dc2074ae6cd00f1c166dc0102c491db056060 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Thu, 15 Apr 1999 18:06:33 +0000 Subject: zsh-3.1.5-pws-1 --- Src/.lastloc | 5 + Src/Makefile.in | 12 +- Src/Modules/stat.c | 1 + Src/Zle/.lastloc | 10 + Src/Zle/comp.h | 137 +- Src/Zle/comp1.c | 174 +- Src/Zle/comp1.export | 22 + Src/Zle/comp1.mdd | 2 + Src/Zle/compctl.c | 664 +++++++- Src/Zle/deltochar.c | 15 +- Src/Zle/iwidgets.list | 16 +- Src/Zle/zle.export | 10 + Src/Zle/zle.h | 11 +- Src/Zle/zle.mdd | 2 + Src/Zle/zle_hist.c | 83 +- Src/Zle/zle_main.c | 51 +- Src/Zle/zle_misc.c | 6 +- Src/Zle/zle_refresh.c | 14 +- Src/Zle/zle_thingy.c | 56 +- Src/Zle/zle_tricky.c | 4363 ++++++++++++++++++++++++++++++++++--------------- Src/Zle/zle_utils.c | 2 +- Src/Zle/zle_vi.c | 10 +- Src/builtin.c | 4 +- Src/exec.c | 4 +- Src/glob.c | 169 +- Src/jobs.c | 2 +- Src/mkmakemod.sh | 15 +- Src/modentry.c | 15 + Src/module.c | 136 +- Src/system.h | 6 + Src/utils.c | 16 +- Src/zsh.export | 235 +++ Src/zsh.h | 2 +- Src/zsh.mdd | 2 +- 34 files changed, 4711 insertions(+), 1561 deletions(-) create mode 100644 Src/.lastloc create mode 100644 Src/Zle/.lastloc create mode 100644 Src/Zle/comp1.export create mode 100644 Src/Zle/zle.export create mode 100644 Src/modentry.c create mode 100644 Src/zsh.export (limited to 'Src') diff --git a/Src/.lastloc b/Src/.lastloc new file mode 100644 index 000000000..b3c6cf0f3 --- /dev/null +++ b/Src/.lastloc @@ -0,0 +1,5 @@ +(("/home/user2/pws/src/zsh-3.1.5/Src/glob.c" . 15475) + ("/home/user2/pws/src/zsh-3.1.5/Src/zsh.export" . 794) + ("/home/user2/pws/src/zsh-3.1.5/Src/utils.c" . 10946) + ("/home/user2/pws/src/zsh-3.1.5/Src/Makefile" . 4282) + ("/home/user2/pws/src/zsh-3.1.5/Src/mkmakemod.sh" . 6832)) diff --git a/Src/Makefile.in b/Src/Makefile.in index 453159e17..269ed7c1b 100644 --- a/Src/Makefile.in +++ b/Src/Makefile.in @@ -55,12 +55,17 @@ NLIST = `cat stamp-modobjs` LIBZSH = libzsh-$(VERSION).$(DL_EXT) NIBZSH = +ZSH_EXPORT = $(EXPOPT)$(sdir)/zsh.export +ZSH_NXPORT = +ENTRYOBJ = modentry..o +NNTRYOBJ = + LDRUNPATH = LD_RUN_PATH=$(libdir)/zsh NDRUNPATH = zsh: $(@L@IBZSH) $(@L@STMP) $(MAIN_OBJS) rm -f $@ - $(@L@DRUNPATH) $(LINK) $(MAIN_OBJS) $(@L@LIST) $(@L@IBZSH) $(LIBS) + $(@L@DRUNPATH) $(LINK) $(MAIN_OBJS) $(@L@LIST) $(ZSH_@E@XPORT) $(@L@IBZSH) $(LIBS) $(LIBZSH): $(LIBOBJS) $(NSTMP) rm -f $@ @@ -114,6 +119,11 @@ modules-bltin: echo > $@; \ fi +modules: $(@E@NTRYOBJ) + +$(ENTRYOBJ): FORCE + @$(MAKE) -f Makemod $(MAKEDEFS) $@ + # ========== ANSI TO K&R CONVERSION ========== ANSI_KNR = ansi2knr diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index 09245b52f..64664abaf 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -85,6 +85,7 @@ statmodeprint(mode_t mode, char *outbuf, int flags) for (i = 1; i <= 9; i++) pm[i] = (mode & *mfp++) ? modes[i] : '-'; + pm[10] = '\0'; if (mode & S_ISUID) pm[3] = (mode & S_IXUSR) ? 's' : 'S'; diff --git a/Src/Zle/.lastloc b/Src/Zle/.lastloc new file mode 100644 index 000000000..a060de394 --- /dev/null +++ b/Src/Zle/.lastloc @@ -0,0 +1,10 @@ +(("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle.h" . 2619) + ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle.export" . 35) + ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle_main.c" . 13806) + ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/iwidgets.list" . 7831) + ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/comp.h" . 8512) + ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle_tricky.c" . 72920) + ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/comp1.c" . 9640) + ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle_vi.c" . 16035) + ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle_utils.c" . 8406) + ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/comp1.export" . 81)) diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 5d7ef74e2..84f8c3c98 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -32,6 +32,11 @@ typedef struct compctlp *Compctlp; typedef struct compctl *Compctl; typedef struct compcond *Compcond; +typedef struct patcomp *Patcomp; +typedef struct cmatcher *Cmatcher; +typedef struct cmlist *Cmlist; +typedef struct cpattern *Cpattern; +typedef struct cline *Cline; /* node for compctl hash table (compctltab) */ @@ -42,6 +47,14 @@ struct compctlp { Compctl cc; /* pointer to the compctl desc. */ }; +/* for the list of pattern compctls */ + +struct patcomp { + Patcomp next; + char *pat; + Compctl cc; +}; + /* compctl -x condition */ struct compcond { @@ -85,7 +98,7 @@ struct compcond { struct compctl { int refc; /* reference count */ Compctl next; /* next compctl for -x */ - unsigned long mask; /* mask of things to complete (CC_*) */ + unsigned long mask, mask2; /* mask of things to complete (CC_*) */ char *keyvar; /* for -k (variable) */ char *glob; /* for -g (globbing) */ char *str; /* for -s (expansion) */ @@ -97,12 +110,15 @@ struct compctl { char *withd; /* for -w (with directory */ char *hpat; /* for -H (history pattern) */ int hnum; /* for -H (number of events to search) */ + char *gname; Compctl ext; /* for -x (first of the compctls after -x) */ Compcond cond; /* for -x (condition for this compctl) */ Compctl xor; /* for + (next of the xor'ed compctls) */ + Cmatcher matcher; /* matcher control (-M) */ + char *mstr; /* matcher string */ }; -/* objects to complete */ +/* objects to complete (mask) */ #define CC_FILES (1<<0) #define CC_COMMPATH (1<<1) #define CC_REMOVE (1<<2) @@ -135,3 +151,120 @@ struct compctl { #define CC_EXPANDEXPL (1<<30) #define CC_RESERVED (1<<31) + +/* objects to complete (mask2) */ +#define CC_NOSORT (1<<0) +#define CC_XORCONT (1<<1) +#define CC_CCCONT (1<<2) +#define CC_PATCONT (1<<3) +#define CC_DEFCONT (1<<4) + +typedef struct cexpl *Cexpl; +typedef struct cmgroup *Cmgroup; +typedef struct cmatch *Cmatch; + +/* This is for explantion strings. */ + +struct cexpl { + char *str; /* the string */ + int count; /* the number of matches */ + int fcount; /* number of matches with fignore ignored */ + +}; + +/* This describes a group of matches. */ + +struct cmgroup { + char *name; /* the name of this group */ + Cmgroup prev; /* previous on the list */ + Cmgroup next; /* next one in list */ + int flags; /* see CGF_* below */ + int mcount; /* number of matches */ + Cmatch *matches; /* the matches */ + int lcount; /* number of things to list here */ + char **ylist; /* things to list */ + int ecount; /* number of explanation string */ + Cexpl *expls; /* explanation strings */ + int ccount; /* number of compctls used */ + Compctl *ccs; /* the compctls used */ + LinkList lexpls; /* list of explanation string while building */ + LinkList lmatches; /* list of matches */ + LinkList lfmatches; /* list of matches without fignore */ + LinkList lallccs; /* list of used compctls */ +}; + + +#define CGF_NOSORT 1 /* don't sort this group */ +#define CGF_LINES 2 /* these are to be printed on different lines */ + +/* This is the struct used to hold matches. */ + +struct cmatch { + char *str; /* the match itself */ + char *ipre; /* ignored prefix, has to be re-inserted */ + char *ripre; /* ignored prefix, unquoted */ + char *ppre; /* the path prefix */ + char *psuf; /* the path suffix */ + char *prpre; /* path prefix for opendir */ + char *pre; /* prefix string from -P */ + char *suf; /* suffix string from -S */ + int flags; /* see CMF_* below */ + int brpl; /* the place where to put the brace prefix */ + int brsl; /* ...and the suffix */ +}; + +#define CMF_FILE 1 /* this is a file */ +#define CMF_REMOVE 2 /* remove the suffix */ +#define CMF_PARBR 4 /* paramter expansion with a brace */ +#define CMF_NOLIST 8 /* should not be listed */ + + +/* Stuff for completion matcher control. */ + +struct cmlist { + Cmlist next; /* next one in the list of global matchers */ + Cmatcher matcher; /* the matcher definition */ + char *str; /* the string for it */ +}; + +struct cmatcher { + Cmatcher next; /* next matcher */ + int flags; /* see CMF_* below */ + Cpattern line; /* what matches on the line */ + int llen; /* length of line pattern */ + Cpattern word; /* what matches in the word */ + int wlen; /* length of word pattern */ + Cpattern left; /* left anchor */ + int lalen; /* length of left anchor */ + Cpattern right; /* right anchor */ + int ralen; /* length of right anchor */ +}; + + +#define CMF_LINE 1 +#define CMF_LEFT 2 +#define CMF_RIGHT 4 + + +struct cpattern { + Cpattern next; /* next sub-pattern */ + unsigned char tab[256]; /* table of matched characters */ + int equiv; /* if this is a {...} class */ +}; + + +struct cline { + Cline next; /* next chunk */ + char *line; /* string to insert if !word */ + 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 */ +}; + +#define CLF_END 1 +#define CLF_MID 2 +#define CLF_MISS 4 +#define CLF_DIFF 8 +#define CLF_SUF 16 diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c index acd1288a6..68aa5ef52 100644 --- a/Src/Zle/comp1.c +++ b/Src/Zle/comp1.c @@ -31,16 +31,34 @@ #include "comp1.pro" -/* default completion infos */ +/* Default completion infos */ /**/ struct compctl cc_compos, cc_default, cc_first, cc_dummy; -/* hash table for completion info for commands */ +/* Global matcher. */ + +/**/ +Cmlist cmatcher; + +/* pointers to functions required by zle */ + +/**/ +void (*printcompctlptr) _((char *, Compctl, int)); + +/**/ +Compctl (*compctl_widgetptr) _((char *, char **)); + +/* Hash table for completion info for commands */ /**/ HashTable compctltab; +/* List of pattern compctls */ + +/**/ +Patcomp patcomps; + /* Words on the command line, for use in completion */ /**/ @@ -54,6 +72,16 @@ char **clwords; /**/ int incompctlfunc; + +/* This variable and the functions rembslash() and quotename() came from * + * zle_tricky.c, but are now used in compctl.c, too. */ + +/* 1 if we are completing in a string */ + +/**/ +int instring; + + /**/ static void createcompctltable(void) @@ -71,6 +99,8 @@ createcompctltable(void) compctltab->enablenode = NULL; compctltab->freenode = freecompctlp; compctltab->printnode = NULL; + + patcomps = NULL; } /**/ @@ -103,6 +133,7 @@ freecompctl(Compctl cc) zsfree(cc->prefix); zsfree(cc->suffix); zsfree(cc->hpat); + zsfree(cc->gname); zsfree(cc->subcmd); if (cc->cond) freecompcond(cc->cond); @@ -119,6 +150,9 @@ freecompctl(Compctl cc) } if (cc->xor && cc->xor != &cc_default) freecompctl(cc->xor); + if (cc->matcher) + freecmatcher(cc->matcher); + zsfree(cc->mstr); zfree(cc, sizeof(struct compctl)); } @@ -166,6 +200,56 @@ freecompcond(void *a) } } +/**/ +void +freecmlist(Cmlist l) +{ + Cmlist n; + + while (l) { + n = l->next; + freecmatcher(l->matcher); + zsfree(l->str); + + zfree(l, sizeof(struct cmlist)); + + l = n; + } +} + +/**/ +void +freecmatcher(Cmatcher m) +{ + Cmatcher n; + + while (m) { + n = m->next; + freecpattern(m->line); + freecpattern(m->word); + freecpattern(m->left); + freecpattern(m->right); + + zfree(m, sizeof(struct cmatcher)); + + m = n; + } +} + +/**/ +void +freecpattern(Cpattern p) +{ + Cpattern n; + + while (p) { + n = p->next; + zfree(p, sizeof(struct cpattern)); + + p = n; + } +} + /**/ int compctlread(char *name, char **args, char *ops, char *reply) @@ -261,6 +345,89 @@ compctlread(char *name, char **args, char *ops, char *reply) return 0; } +/* Copy the given string and remove backslashes from the copy and return it. */ + +/**/ +char * +rembslash(char *s) +{ + char *t = s = dupstring(s); + + while (*s) + if (*s == '\\') { + chuck(s); + if (*s) + s++; + } else + s++; + + return t; +} + +/* Quote the string s and return the result. If e is non-zero, the * + * pointer it points to may point to a position in s and in e the position * + * of the corresponding character in the quoted string is returned. Like * + * e, te may point to a position in the string and pl is used to return * + * the position of the character pointed to by te in the quoted string. * + * The string is metafied and may contain tokens. */ + +/**/ +char * +quotename(const char *s, char **e, char *te, int *pl) +{ + const char *u, *tt; + char *v, buf[PATH_MAX * 2]; + int sf = 0; + + tt = v = buf; + u = s; + for (; *u; u++) { + if (e && *e == u) + *e = v, sf |= 1; + if (te == u) + *pl = v - tt, sf |= 2; + if (ispecial(*u) && + (!instring || (isset(BANGHIST) && + *u == (char)bangchar) || + (instring == 2 && + (*u == '$' || *u == '`' || *u == '\"')) || + (instring == 1 && *u == '\''))) { + if (*u == '\n' || (instring == 1 && *u == '\'')) { + if (unset(RCQUOTES)) { + *v++ = '\''; + if (*u == '\'') + *v++ = '\\'; + *v++ = *u; + *v++ = '\''; + } else if (*u == '\n') + *v++ = '"', *v++ = '\n', *v++ = '"'; + else + *v++ = '\'', *v++ = '\''; + continue; + } else + *v++ = '\\'; + } + if(*u == Meta) + *v++ = *u++; + *v++ = *u; + } + *v = '\0'; + if (strcmp(buf, s)) + tt = dupstring(buf); + else + tt = s; + v += tt - buf; + if (e && (sf & 1)) + *e += tt - buf; + + if (e && *e == u) + *e = v; + if (te == u) + *pl = v - tt; + + return (char *) tt; +} + /**/ int boot_comp1(Module m) @@ -269,10 +436,13 @@ boot_comp1(Module m) clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *)); createcompctltable(); cc_compos.mask = CC_COMMPATH; + cc_compos.mask2 = 0; cc_default.refc = 10000; cc_default.mask = CC_FILES; + cc_default.mask2 = 0; cc_first.refc = 10000; cc_first.mask = 0; + cc_first.mask2 = CC_CCCONT; return 0; } diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export new file mode 100644 index 000000000..1ac9195df --- /dev/null +++ b/Src/Zle/comp1.export @@ -0,0 +1,22 @@ +#! +cc_compos +cc_default +cc_dummy +cc_first +clwnum +clwords +clwpos +clwsize +cmatcher +compctl_widgetptr +compctltab +freecmatcher +freecmlist +freecompcond +freecompctl +incompctlfunc +instring +patcomps +printcompctlptr +quotename +rembslash diff --git a/Src/Zle/comp1.mdd b/Src/Zle/comp1.mdd index 9037e568a..f8c5f0a0d 100644 --- a/Src/Zle/comp1.mdd +++ b/Src/Zle/comp1.mdd @@ -1,3 +1,5 @@ +hasexport=1 + objects="comp1.o" headers="comp.h" diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 658cf4161..7104bfc6e 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -29,6 +29,9 @@ #include "compctl.mdh" #include "compctl.pro" +#define GLOBAL_PROTOTYPES +#include "zle_tricky.pro" +#undef GLOBAL_PROTOTYPES #define COMP_LIST (1<<0) /* -L */ #define COMP_COMMAND (1<<1) /* -C */ @@ -44,6 +47,328 @@ static int cclist; /* Mask for determining what to print */ static unsigned long showmask = 0; +/* This is a special return value for parse_cmatcher(), * + * signalling an error. */ + +#define pcm_err ((Cmatcher) 1) + +/* Copy a list of completion matchers. */ + +static Cmlist +cpcmlist(Cmlist l) +{ + Cmlist r = NULL, *p = &r, n; + + while (l) { + *p = n = (Cmlist) zalloc(sizeof(struct cmlist)); + n->next = NULL; + n->matcher = cpcmatcher(l->matcher); + n->str = ztrdup(l->str); + + p = &(n->next); + l = l->next; + } + return r; +} + +/* Copy a completion matcher list. */ + +/**/ +static Cmatcher +cpcmatcher(Cmatcher m) +{ + Cmatcher r = NULL, *p = &r, n; + + while (m) { + *p = n = (Cmatcher) zalloc(sizeof(struct cmatcher)); + + n->next = NULL; + n->flags = m->flags; + n->line = cpcpattern(m->line); + n->llen = m->llen; + n->word = cpcpattern(m->word); + n->wlen = m->wlen; + n->left = cpcpattern(m->left); + n->lalen = m->lalen; + n->right = cpcpattern(m->right); + n->ralen = m->ralen; + + p = &(n->next); + m = m->next; + } + return r; +} + +/* Copy a completion matcher pattern. */ + +/**/ +static Cpattern +cpcpattern(Cpattern o) +{ + Cpattern r = NULL, *p = &r, n; + + while (o) { + *p = n = (Cpattern) zalloc(sizeof(struct cpattern)); + + n->next = NULL; + memcpy(n->tab, o->tab, 256); + n->equiv = o->equiv; + + p = &(n->next); + o = o->next; + } + return r; +} + +/* Try to get the global matcher from the given compctl. */ + +/**/ +static int +get_gmatcher(char *name, char **argv) +{ + if (!strcmp(*argv, "-M")) { + char **p = ++argv; + Cmlist l = NULL, *q = &l, n; + Cmatcher m; + + while (*p) { + if (**p++ == '-') + return 0; + } + while (*argv) { + if ((m = parse_cmatcher(name, *argv)) == pcm_err) + return 2; + *q = n = (Cmlist) halloc(sizeof(struct cmlist)); + n->next = NULL; + n->matcher = m; + n->str = *argv++; + + q = &(n->next); + } + freecmlist(cmatcher); + PERMALLOC { + cmatcher = cpcmlist(l); + } LASTALLOC; + + return 1; + } + return 0; +} + +/* This prints the global matcher definitions. */ + +/**/ +static void +print_gmatcher(int ac) +{ + Cmlist p; + + if ((p = cmatcher)) { + printf((ac ? "compctl -M" : "MATCH")); + + while (p) { + printf(" \'%s\'", p->str); + + p = p->next; + } + putchar('\n'); + } +} + +/* Parse a string for matcher control, containing multiple matchers. */ + +/**/ +static Cmatcher +parse_cmatcher(char *name, char *s) +{ + Cmatcher ret = NULL, r, n; + Cpattern line, word, left, right; + int fl, ll, wl, lal, ral, err; + + if (!*s) + return NULL; + + while (*s) { + while (*s && inblank(*s)) s++; + + if (!*s) break; + + switch (*s) { + case 'l': fl = CMF_LEFT; break; + case 'r': fl = CMF_RIGHT; break; + case 'm': fl = 0; break; + case 'L': fl = CMF_LEFT | CMF_LINE; break; + case 'R': fl = CMF_RIGHT | CMF_LINE; break; + case 'M': fl = CMF_LINE; break; + default: + zwarnnam(name, "unknown match specification character `%c'", NULL, *s); + return pcm_err; + } + if (s[1] != ':') { + zwarnnam(name, "missing `:'", NULL, 0); + return pcm_err; + } + s += 2; + if (!*s) { + zwarnnam(name, "missing patterns", NULL, 0); + return pcm_err; + } + if (fl & CMF_LEFT) { + left = parse_pattern(name, &s, &lal, '|', &err); + if (err) + return pcm_err; + if (!*s || !*++s) { + zwarnnam(name, "missing line pattern", NULL, 0); + return pcm_err; + } + } else + left = NULL; + + line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='), + &err); + if (err) + return pcm_err; + if (!*s || !*++s) { + zwarnnam(name, ((fl & CMF_RIGHT) ? "missing right anchor" : "missing word pattern"), NULL, 0); + return pcm_err; + } + if (fl & CMF_RIGHT) { + right = parse_pattern(name, &s, &ral, '=', &err); + if (err) + return pcm_err; + if (!*s || !*++s) { + zwarnnam(name, "missing word pattern", NULL, 0); + return pcm_err; + } + } else + right = NULL; + + if (*s == '*') { + if (!(fl & (CMF_LEFT | CMF_RIGHT))) { + zwarnnam(name, "need anchor for `*'", NULL, 0); + return pcm_err; + } + word = NULL; + wl = -1; + s++; + } else { + word = parse_pattern(name, &s, &wl, 0, &err); + + if (!word && !line) { + zwarnnam(name, "need non-empty word or line pattern", NULL, 0); + return pcm_err; + } + } + if (err) + return pcm_err; + + n = (Cmatcher) zcalloc(sizeof(*ret)); + n->next = NULL; + n->flags = fl; + n->line = line; + n->llen = ll; + n->word = word; + n->wlen = wl; + n->left = left; + n->lalen = lal; + n->right = right; + n->ralen = ral; + + if (ret) r->next = n; + else ret = n; + + r = n; + } + return ret; +} + +/* Parse a pattern for matcher control. */ + +/**/ +static Cpattern +parse_pattern(char *name, char **sp, int *lp, char e, int *err) +{ + Cpattern ret = NULL, r, n; + unsigned char *s = (unsigned char *) *sp; + int l = 0; + + *err = 0; + + while (*s && (e ? (*s != e) : !inblank(*s))) { + n = (Cpattern) hcalloc(sizeof(*n)); + n->next = NULL; + n->equiv = 0; + + if (*s == '[') { + s = parse_class(n, s + 1, ']'); + if (!*s) { + *err = 1; + zwarnnam(name, "unterminated character class", NULL, 0); + return NULL; + } + } else if (*s == '{') { + n->equiv = 1; + s = parse_class(n, s + 1, '}'); + if (!*s) { + *err = 1; + zwarnnam(name, "unterminated character class", NULL, 0); + return NULL; + } + } else if (*s == '?') { + memset(n->tab, 1, 256); + } else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') { + *err = 1; + zwarnnam(name, "invalid pattern character `%c'", NULL, *s); + return NULL; + } else { + if (*s == '\\' && s[1]) + s++; + + memset(n->tab, 0, 256); + n->tab[*s] = 1; + } + if (ret) + r->next = n; + else + ret = n; + + r = n; + + l++; + s++; + } + *sp = (char *) s; + *lp = l; + return ret; +} + +/* Parse a character class for matcher control. */ + +/**/ +static unsigned char * +parse_class(Cpattern p, unsigned char *s, unsigned char e) +{ + int n = 0, i = 1, j, eq = (e == '}'), k = 1; + + if (!eq && (*s == '!' || *s == '^') && s[1] != e) { n = 1; s++; } + + memset(p->tab, n, 256); + + n = !n; + while (*s && (k || *s != e)) { + if (s[1] == '-' && s[2] != e) { + /* a run of characters */ + for (j = (int) *s; j <= (int) s[2]; j++) + p->tab[j] = (eq ? i++ : n); + + s += 3; + } else + p->tab[*s++] = (eq ? i++ : n); + k = 0; + } + return s; +} + /* Parse the basic flags for `compctl' */ /**/ @@ -176,6 +501,44 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) case '/': cct.mask |= CC_DIRS; break; + case 't': + { + char *p; + + if ((*argv)[1]) { + p = (*argv) + 1; + *argv = "" - 1; + } else if (!argv[1]) { + zwarnnam(name, "retry specification expected after -%c", NULL, + **argv); + return 1; + } else { + p = *++argv; + *argv = "" - 1; + } + while (*p) { + switch (*p) { + case '+': + cct.mask2 |= CC_XORCONT; + break; + case 'c': + cct.mask2 |= CC_CCCONT; + break; + case '-': + cct.mask2 |= CC_PATCONT; + break; + case 'x': + cct.mask2 |= CC_DEFCONT; + break; + default: + zwarnnam(name, "invalid retry specification character `%c'", + NULL, *p); + return 1; + } + p++; + } + } + break; case 'k': if ((*argv)[1]) { cct.keyvar = (*argv) + 1; @@ -307,6 +670,58 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) *argv = "" - 1; } break; + case 'J': + if ((*argv)[1]) { + cct.gname = (*argv) + 1; + *argv = "" - 1; + } else if (!argv[1]) { + zwarnnam(name, "group name expected after -%c", NULL, + **argv); + return 1; + } else { + cct.gname = *++argv; + *argv = "" - 1; + } + break; + case 'V': + if ((*argv)[1]) { + cct.gname = (*argv) + 1; + *argv = "" - 1; + } else if (!argv[1]) { + zwarnnam(name, "group name expected after -%c", NULL, + **argv); + return 1; + } else { + cct.gname = *++argv; + *argv = "" - 1; + } + cct.mask2 |= CC_NOSORT; + break; + case 'M': + if ((*argv)[1]) { + if ((cct.matcher = + parse_cmatcher(name, (cct.mstr = (*argv) + 1))) == + pcm_err) { + cct.matcher = NULL; + cct.mstr = NULL; + return 1; + } + *argv = "" - 1; + } else if (!argv[1]) { + zwarnnam(name, "matching specification expected after -%c", NULL, + **argv); + return 1; + } else { + if ((cct.matcher = + parse_cmatcher(name, (cct.mstr = *++argv))) == + pcm_err) { + cct.matcher = NULL; + cct.mstr = NULL; + return 1; + } + *argv = "" - 1; + } + break; case 'H': if ((*argv)[1]) cct.hnum = atoi((*argv) + 1); @@ -600,27 +1015,34 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef) c->u.s.s[l] = ztrdup(tt); } else if (c->type == CCT_RANGESTR || c->type == CCT_RANGEPAT) { + int hc; + /* -r[..,..] or -R[..,..]: two strings expected */ - for (; *t && *t != '\201'; t++); + for (; *t && *t != '\201' && *t != '\200'; t++); if (!*t) { zwarnnam(name, "error in condition", NULL, 0); freecompcond(m); return 1; } + hc = (*t == '\201'); *t = '\0'; c->u.l.a[l] = ztrdup(tt); - tt = ++t; - /* any more commas are text, not active */ - for (; *t && *t != '\200'; t++) - if (*t == '\201') - *t = ','; - if (!*t) { - zwarnnam(name, "error in condition", NULL, 0); - freecompcond(m); - return 1; + if (hc) { + tt = ++t; + /* any more commas are text, not active */ + for (; *t && *t != '\200'; t++) + if (*t == '\201') + *t = ','; + if (!*t) { + zwarnnam(name, "error in condition", NULL, 0); + freecompcond(m); + return 1; + } + *t = '\0'; + c->u.l.b[l] = ztrdup(tt); } - *t = '\0'; - c->u.l.b[l] = ztrdup(tt); + else + c->u.l.b[l] = NULL; } else { /* remaining patterns are number followed by string */ for (; *t && *t != '\200' && *t != '\201'; t++); @@ -700,13 +1122,6 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass) /* Copy over the details from the values in cct to those in *ccptr */ Compctl cc; - if (cct->subcmd && (cct->keyvar || cct->glob || cct->str || - cct->func || cct->explain || cct->ylist || - cct->prefix)) { - zwarnnam(name, "illegal combination of options", NULL, 0); - return 1; - } - /* Handle assignment of new default or command completion */ if (reass && !(cclist & COMP_LIST)) { /* if not listing */ @@ -746,11 +1161,15 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass) zsfree(cc->subcmd); zsfree(cc->withd); zsfree(cc->hpat); - + zsfree(cc->gname); + zsfree(cc->mstr); + freecmatcher(cc->matcher); + /* and copy over the new stuff, (permanently) allocating * space for strings. */ cc->mask = cct->mask; + cc->mask2 = cct->mask2; cc->keyvar = ztrdup(cct->keyvar); cc->glob = ztrdup(cct->glob); cc->str = ztrdup(cct->str); @@ -761,8 +1180,13 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass) cc->suffix = ztrdup(cct->suffix); cc->subcmd = ztrdup(cct->subcmd); cc->withd = ztrdup(cct->withd); + cc->gname = ztrdup(cct->gname); cc->hpat = ztrdup(cct->hpat); cc->hnum = cct->hnum; + PERMALLOC { + cc->matcher = cpcmatcher(cct->matcher); + } LASTALLOC; + cc->mstr = ztrdup(cct->mstr); /* careful with extended completion: it's already allocated */ cc->ext = cct->ext; @@ -790,16 +1214,62 @@ cc_reassign(Compctl cc) cc->ext = cc->xor = NULL; } +/* Check if the given string is a pattern. If so, return one and tokenize * + * it. If not, we just remove the backslashes. */ + +static int +compctl_name_pat(char **p) +{ + char *s = *p; + + tokenize(s = dupstring(s)); + remnulargs(s); + + if (haswilds(s)) { + *p = s; + return 1; + } else + *p = rembslash(*p); + + return 0; +} + +/* Delete the pattern compctl with the given name. */ + +static void +delpatcomp(char *n) +{ + Patcomp p, q; + + for (q = 0, p = patcomps; p; q = p, p = p->next) { + if (!strcmp(n, p->pat)) { + if (q) + q->next = p->next; + else + patcomps = p->next; + zsfree(p->pat); + freecompctl(p->cc); + free(p); + + break; + } + } +} + /**/ static void compctl_process_cc(char **s, Compctl cc) { Compctlp ccp; + char *n; if (cclist & COMP_REMOVE) { /* Delete entries for the commands listed */ for (; *s; s++) { - if ((ccp = (Compctlp) compctltab->removenode(compctltab, *s))) + n = *s; + if (compctl_name_pat(&n)) + delpatcomp(n); + else if ((ccp = (Compctlp) compctltab->removenode(compctltab, n))) compctltab->freenode((HashNode) ccp); } } else { @@ -807,10 +1277,23 @@ compctl_process_cc(char **s, Compctl cc) cc->refc = 0; for (; *s; s++) { + n = *s; + cc->refc++; - ccp = (Compctlp) zalloc(sizeof *ccp); - ccp->cc = cc; - compctltab->addnode(compctltab, ztrdup(*s), ccp); + if (compctl_name_pat(&n)) { + Patcomp pc; + + delpatcomp(n); + pc = zalloc(sizeof *pc); + pc->pat = ztrdup(n); + pc->cc = cc; + pc->next = patcomps; + patcomps = pc; + } else { + ccp = (Compctlp) zalloc(sizeof *ccp); + ccp->cc = cc; + compctltab->addnode(compctltab, ztrdup(n), ccp); + } } } } @@ -819,15 +1302,21 @@ compctl_process_cc(char **s, Compctl cc) /**/ static void -printcompctl(char *s, Compctl cc, int printflags) +printcompctl(char *s, Compctl cc, int printflags, int ispat) { Compctl cc2; char *css = "fcqovbAIFpEjrzBRGudeNOZUnQmw/"; char *mss = " pcCwWsSnNmrR"; unsigned long t = 0x7fffffff; - unsigned long flags = cc->mask; + unsigned long flags = cc->mask, flags2 = cc->mask2; unsigned long oldshowmask; + /* Printflags is used outside the standard compctl commands*/ + if (printflags & PRINT_LIST) + cclist |= COMP_LIST; + else if (printflags & PRINT_TYPE) + cclist &= ~COMP_LIST; + if ((flags & CC_EXCMDS) && !(flags & CC_DISCMDS)) flags &= ~CC_EXCMDS; @@ -851,8 +1340,13 @@ printcompctl(char *s, Compctl cc, int printflags) printf(" -D"); if (cc == &cc_first) printf(" -T"); + } else if (ispat) { + char *p = dupstring(s); + + untokenize(p); + quotedzputs(p, stdout); } else - quotedzputs(s, stdout); + quotedzputs(quotename(s, NULL, NULL, NULL), stdout); } /* loop through flags w/o args that are set, printing them if so */ @@ -868,9 +1362,23 @@ printcompctl(char *s, Compctl cc, int printflags) t >>= 1; } } - + if (flags2 & (CC_XORCONT | CC_CCCONT | CC_PATCONT | CC_DEFCONT)) { + printf(" -t"); + if (flags2 & CC_XORCONT) + putchar('+'); + if (flags2 & CC_CCCONT) + putchar('c'); + if (flags2 & CC_PATCONT) + putchar('-'); + if (flags2 & CC_DEFCONT) + putchar('x'); + } /* now flags with arguments */ - flags = cc->mask; + printif(cc->mstr, 'M'); + if (flags2 & CC_NOSORT) + printif(cc->gname, 'V'); + else + printif(cc->gname, 'J'); printif(cc->keyvar, 'k'); printif(cc->func, 'K'); printif(cc->explain, (cc->mask & CC_EXPANDEXPL) ? 'Y' : 'X'); @@ -940,7 +1448,7 @@ printcompctl(char *s, Compctl cc, int printflags) c = cc2->cond; cc2->cond = NULL; /* now print the flags for the current condition */ - printcompctl(NULL, cc2, 0); + printcompctl(NULL, cc2, 0, 0); cc2->cond = c; if ((cc2 = (Compctl) (cc2->next))) printf(" -"); @@ -952,7 +1460,7 @@ printcompctl(char *s, Compctl cc, int printflags) /* print xor'd (+) completions */ printf(" +"); if (cc->xor != &cc_default) - printcompctl(NULL, cc->xor, 0); + printcompctl(NULL, cc->xor, 0, 0); } if (s) { if ((cclist & COMP_LIST) && (cc != &cc_compos) @@ -960,7 +1468,17 @@ printcompctl(char *s, Compctl cc, int printflags) if(s[0] == '-' || s[0] == '+') printf(" -"); putchar(' '); - quotedzputs(s, stdout); + if (ispat) { + char *p = dupstring(s); + + untokenize(p); + quotedzputs(p, stdout); + } else { + char *p = dupstring(s); + + untokenize(p); + quotedzputs(quotename(p, NULL, NULL, NULL), stdout); + } } putchar('\n'); } @@ -975,7 +1493,7 @@ printcompctlp(HashNode hn, int printflags) Compctlp ccp = (Compctlp) hn; /* Function needed for use by scanhashtable() */ - printcompctl(ccp->nam, ccp->cc, printflags); + printcompctl(ccp->nam, ccp->cc, printflags, 0); } /* Main entry point for the `compctl' builtin */ @@ -991,8 +1509,14 @@ bin_compctl(char *name, char **argv, char *ops, int func) cclist = 0; showmask = 0; + instring = 0; + /* Parse all the arguments */ if (*argv) { + /* Let's see if this is a global matcher definition. */ + if ((ret = get_gmatcher(name, argv))) + return ret - 1; + cc = (Compctl) zcalloc(sizeof(*cc)); if (get_compctl(name, &argv, cc, 1, 0)) { freecompctl(cc); @@ -1013,10 +1537,16 @@ bin_compctl(char *name, char **argv, char *ops, int func) * If some flags (other than -C, -T, or -D) were given, then * * only print compctl containing those flags. */ if (!*argv && !(cclist & COMP_SPECIAL)) { + Patcomp pc; + + for (pc = patcomps; pc; pc = pc->next) + printcompctl(pc->pat, pc->cc, 0, 1); + scanhashtable(compctltab, 1, 0, 0, compctltab->printnode, 0); - printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0); - printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0); - printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0); + printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0, 0); + printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0, 0); + printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0, 0); + print_gmatcher((cclist & COMP_LIST)); return ret; } @@ -1024,23 +1554,35 @@ bin_compctl(char *name, char **argv, char *ops, int func) * or a COMP_SPECIAL flag (-D, -C, -T), so print only those. */ if (cclist & COMP_LIST) { HashNode hn; - char **ptr; + char **ptr, *n; showmask = 0; for (ptr = argv; *ptr; ptr++) { - if ((hn = compctltab->getnode(compctltab, *ptr))) { + n = *ptr; + if (compctl_name_pat(&n)) { + Patcomp pc; + + for (pc = patcomps; pc; pc = pc->next) + if (!strcmp(n, pc->pat)) { + printcompctl(pc->pat, pc->cc, 0, 1); + n = NULL; + break; + } + } else if ((hn = compctltab->getnode(compctltab, n))) { compctltab->printnode(hn, 0); - } else { - zwarnnam(name, "no compctl defined for %s", *ptr, 0); + n = NULL; + } + if (n) { + zwarnnam(name, "no compctl defined for %s", n, 0); ret = 1; } } if (cclist & COMP_COMMAND) - printcompctl("", &cc_compos, 0); + printcompctl("", &cc_compos, 0, 0); if (cclist & COMP_DEFAULT) - printcompctl("", &cc_default, 0); + printcompctl("", &cc_default, 0, 0); if (cclist & COMP_FIRST) - printcompctl("", &cc_first, 0); + printcompctl("", &cc_first, 0, 0); return ret; } @@ -1058,6 +1600,38 @@ bin_compctl(char *name, char **argv, char *ops, int func) return ret; } +/* Externally callable version of get_compctl. Used for completion widgets */ + +/**/ +static Compctl +compctl_widget(char *name, char **argv) +{ + Compctl cc = (Compctl) zcalloc(sizeof(*cc)); + cclist = 0; + showmask = 0; + + if (get_compctl(name, &argv, cc, 1, 0)) { + freecompctl(cc); + return NULL; + } + + if (cclist & COMP_REMOVE) { + zwarnnam(name, "use -D to delete widget", NULL, 0); + return NULL; + } else if (cclist) { + zwarnnam(name, "special options illegal in widget", NULL, 0); + freecompctl(cc); + return NULL; + } else if (*argv) { + zwarnnam(name, "command names illegal in widget", NULL, 0); + freecompctl(cc); + return NULL; + } + cc->refc++; + + return cc; +} + static struct builtin bintab[] = { BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL), }; @@ -1069,6 +1643,8 @@ boot_compctl(Module m) if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab))) return 1; compctltab->printnode = printcompctlp; + printcompctlptr = printcompctl; + compctl_widgetptr = compctl_widget; return 0; } @@ -1080,6 +1656,8 @@ cleanup_compctl(Module m) { compctltab->printnode = NULL; deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); + printcompctlptr = NULL; + compctl_widgetptr = NULL; return 0; } #endif diff --git a/Src/Zle/deltochar.c b/Src/Zle/deltochar.c index 87f8593b8..8869eb147 100644 --- a/Src/Zle/deltochar.c +++ b/Src/Zle/deltochar.c @@ -45,7 +45,7 @@ deltochar(void) if (dest != ll) { dest++; if (!n) { - foredel(dest - cs); + forekill(dest - cs, 0); ok++; } } @@ -57,9 +57,13 @@ deltochar(void) while (n++ && dest != 0) { while (dest != 0 && line[dest] != c) dest--; - if (line[dest] == c && !n) { - backdel(cs - dest); - ok++; + if (line[dest] == c) { + if (!n) { + backkill(cs - dest, 1); + ok++; + } + if (dest) + dest--; } } } @@ -71,7 +75,8 @@ deltochar(void) int boot_deltochar(Module m) { - w_deletetochar = addzlefunction("delete-to-char", deltochar, ZLE_KEEPSUFFIX); + w_deletetochar = addzlefunction("delete-to-char", deltochar, + ZLE_KILL | ZLE_KEEPSUFFIX); if (w_deletetochar) return 0; zwarnnam(m->nam, "name clash when adding ZLE function `delete-to-char'", diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index 01862160e..52e70fad5 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -24,7 +24,7 @@ "beginning-of-line", beginningofline, 0 "beginning-of-line-hist", beginningoflinehist, 0 "capitalize-word", capitalizeword, 0 -"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX "copy-prev-word", copyprevword, 0 "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX @@ -32,7 +32,7 @@ "delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX "delete-word", deleteword, ZLE_KEEPSUFFIX "describe-key-briefly", describekeybriefly, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "down-case-word", downcaseword, 0 "down-history", downhistory, 0 "down-line-or-history", downlineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL @@ -73,7 +73,7 @@ "magic-space", magicspace, 0 "menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX "menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "overwrite-mode", overwritemode, 0 "pound-insert", poundinsert, 0 "push-input", pushinput, 0 @@ -95,7 +95,7 @@ "transpose-words", transposewords, 0 "undefined-key", undefinedkey, 0 "undo", undo, 0 -"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "up-case-word", upcaseword, 0 "up-history", uphistory, 0 "up-line-or-history", uplineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL @@ -145,8 +145,8 @@ "vi-open-line-below", viopenlinebelow, 0 "vi-oper-swap-case", vioperswapcase, 0 "vi-pound-insert", vipoundinsert, 0 -"vi-put-after", viputafter, ZLE_YANK -"vi-put-before", viputbefore, ZLE_YANK +"vi-put-after", viputafter, ZLE_YANK | ZLE_KEEPSUFFIX +"vi-put-before", viputbefore, ZLE_YANK | ZLE_KEEPSUFFIX "vi-quoted-insert", viquotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX "vi-repeat-change", virepeatchange, 0 "vi-repeat-find", virepeatfind, 0 @@ -168,5 +168,5 @@ "what-cursor-position", whatcursorposition, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "where-is", whereis, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "which-command", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"yank", yank, ZLE_YANK -"yank-pop", yankpop, ZLE_YANK +"yank", yank, ZLE_YANK | ZLE_KEEPSUFFIX +"yank-pop", yankpop, ZLE_YANK | ZLE_KEEPSUFFIX diff --git a/Src/Zle/zle.export b/Src/Zle/zle.export new file mode 100644 index 000000000..ccd5df98a --- /dev/null +++ b/Src/Zle/zle.export @@ -0,0 +1,10 @@ +#! +addzlefunction +backdel +backkill +deletezlefunction +feep +foredel +forekill +getkey +zmod diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index faf6cf878..71a929f87 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -31,7 +31,7 @@ #undef zleread #undef spaceinline #undef gotword -#undef refresh +#undef zrefresh typedef struct widget *Widget; typedef struct thingy *Thingy; @@ -46,16 +46,23 @@ struct widget { union { ZleIntFunc fn; /* pointer to internally implemented widget */ char *fnnam; /* name of the shell function for user-defined widget */ + Compctl cc; /* for use with a WIDGET_COMP widget */ } u; }; #define WIDGET_INT (1<<0) /* widget is internally implemented */ -#define ZLE_MENUCMP (1<<1) /* DON'T invalidate completion list */ +#define WIDGET_COMP (1<<1) /* Special completion widget */ +#define ZLE_MENUCMP (1<<2) /* DON'T invalidate completion list */ #define ZLE_YANK (1<<3) #define ZLE_LINEMOVE (1<<4) /* command is a line-oriented movement */ #define ZLE_LASTCOL (1<<5) /* command maintains lastcol correctly */ #define ZLE_KILL (1<<6) #define ZLE_KEEPSUFFIX (1<<9) /* DON'T remove added suffix */ +#define ZLE_USEMENU (1<<10) /* Do ) use menu completion for */ +#define ZLE_NOMENU (1<<11) /* Don't ) widget, else use default */ +#define ZLE_USEGLOB (1<<12) /* Do ) use glob completion for */ +#define ZLE_NOGLOB (1<<13) /* Don't ) widget, else use default */ +#define ZLE_NOTCOMMAND (1<<14) /* widget should not alter lastcmd */ /* thingies */ diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd index 29f39d363..fec2e213b 100644 --- a/Src/Zle/zle.mdd +++ b/Src/Zle/zle.mdd @@ -1,3 +1,5 @@ +hasexport=1 + moddeps="comp1" autobins="bindkey vared zle" diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index 76e421c1c..fda204222 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -291,73 +291,86 @@ downhistory(void) feep(); } +static int histpos, srch_hl, srch_cs = -1; +static char *srch_str; + /**/ void historysearchbackward(void) { - int histpos, histmpos, hl = histline; + int hl = histline; int n = zmult; char *s; - if (!n) - return; - if (n < 0) { + if (zmult < 0) { zmult = -n; historysearchforward(); zmult = n; return; } - for (histpos = histmpos = 0; histpos < ll && !iblank(line[histpos]); - histpos++, histmpos++) - if(imeta(line[histpos])) - histmpos++; + if (hl == curhist || hl != srch_hl || cs != srch_cs || mark != 0 + || memcmp(srch_str, line, histpos) != 0) { + zfree(srch_str, histpos); + for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ; + if (histpos < ll) + histpos++; + srch_str = zalloc(histpos); + memcpy(srch_str, line, histpos); + } for (;;) { hl--; if (!(s = zle_get_event(hl))) { feep(); return; } - if (metadiffer(s, (char *) line, histpos) < 0 && - iblank(s[histmpos] == Meta ? s[histmpos+1]^32 : s[histmpos]) && - metadiffer(s, (char *) line, ll) && !--n) - break; + if (metadiffer(s, srch_str, histpos) < 0 && + metadiffer(s, srch_str, ll)) { + if (--n <= 0) + break; + } } zle_goto_hist(hl); + srch_hl = hl; + srch_cs = cs; } /**/ void historysearchforward(void) { - int histpos, histmpos, hl = histline; + int hl = histline; int n = zmult; char *s; - if (!n) - return; - if (n < 0) { + if (zmult < 0) { zmult = -n; historysearchbackward(); zmult = n; return; } - for (histpos = histmpos = 0; histpos < ll && !iblank(line[histpos]); - histpos++, histmpos++) - if(imeta(line[histpos])) - histmpos++; + if (hl == curhist || hl != srch_hl || cs != srch_cs || mark != 0 + || memcmp(srch_str, line, histpos) != 0) { + zfree(srch_str, histpos); + for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ; + if (histpos < ll) + histpos++; + srch_str = zalloc(histpos); + memcpy(srch_str, line, histpos); + } for (;;) { hl++; if (!(s = zle_get_event(hl))) { feep(); return; } - if (metadiffer(s, (char *) line, histpos) < (histline == curhist) && - (!s[histmpos] || - iblank(s[histmpos] == Meta ? s[histmpos+1]^32 : s[histmpos])) && - metadiffer(s, (char *) line, ll) && !--n) - break; + if (metadiffer(s, srch_str, histpos) < (hl == curhist) && + metadiffer(s, srch_str, ll)) + if (--n <= 0) + break; } zle_goto_hist(hl); + srch_hl = hl; + srch_cs = cs; } /**/ @@ -721,7 +734,7 @@ doisearch(int dir) sbuf[sbptr] = '_'; statusll = sbuf - statusline + sbptr + 1; ref: - refresh(); + zrefresh(); if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) { int i; get_isrch_spot(0, &hl, &pos, &i, &sbptr, &dir, &nomatch); @@ -809,7 +822,7 @@ doisearch(int dir) cmd == Th(z_quotedinsert)) { if(cmd == Th(z_viquotedinsert)) { sbuf[sbptr] = '^'; - refresh(); + zrefresh(); } if ((c = getkey(0)) == EOF) feep(); @@ -936,7 +949,7 @@ getvisrchstr(void) while (sptr) { sbuf[sptr] = '_'; statusll = sptr + 1; - refresh(); + zrefresh(); if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) { ret = 0; break; @@ -971,7 +984,7 @@ getvisrchstr(void) } else if(cmd == Th(z_viquotedinsert) || cmd == Th(z_quotedinsert)) { if(cmd == Th(z_viquotedinsert)) { sbuf[sptr] = '^'; - refresh(); + zrefresh(); } if ((c = getkey(0)) == EOF) feep(); @@ -1030,9 +1043,7 @@ virepeatsearch(void) feep(); return; } - if (!n) - return; - if (n < 0) { + if (zmult < 0) { n = -n; visrchsense = -visrchsense; } @@ -1078,9 +1089,7 @@ historybeginningsearchbackward(void) int n = zmult; char *s; - if (!n) - return; - if (n < 0) { + if (zmult < 0) { zmult = -n; historybeginningsearchforward(); zmult = n; @@ -1114,9 +1123,7 @@ historybeginningsearchforward(void) int n = zmult; char *s; - if (!n) - return; - if (n < 0) { + if (zmult < 0) { zmult = -n; historybeginningsearchbackward(); zmult = n; diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 338cdef41..254021bed 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -69,6 +69,9 @@ static int baud; /**/ int lastcmd; +/**/ +Widget compwidget; + /* the status line, and its length */ /**/ @@ -112,7 +115,7 @@ int feepflag; /**/ void -setterm(void) +zsetterm(void) { struct ttyinfo ti; @@ -362,7 +365,7 @@ getkey(int keytmout) ret = opts[MONITOR]; opts[MONITOR] = 1; attachtty(mypgrp); - refresh(); /* kludge! */ + zrefresh(); /* kludge! */ opts[MONITOR] = ret; die = 1; } else if (errno != 0) { @@ -372,7 +375,7 @@ getkey(int keytmout) } } if (cc == '\r') /* undo the exchange of \n and \r determined by */ - cc = '\n'; /* setterm() */ + cc = '\n'; /* zsetterm() */ else if (cc == '\n') cc = '\r'; @@ -485,7 +488,7 @@ zleread(char *lp, char *rp, int ha) initmodifier(&zmod); prefixflag = 0; feepflag = 0; - refresh(); + zrefresh(); while (!done && !errflag) { statusline = NULL; @@ -516,11 +519,11 @@ zleread(char *lp, char *rp, int ha) tv.tv_usec = 500000; if (!kungetct && select(SHTTY+1, (SELECT_ARG_2_T) & foofd, NULL, NULL, &tv) <= 0) - refresh(); + zrefresh(); } else #endif if (!kungetct) - refresh(); + zrefresh(); handlefeep(); } statusline = NULL; @@ -562,12 +565,17 @@ execzlefunc(Thingy func) showmsg(msg); zsfree(msg); feep(); - } else if((w = func->widget)->flags & WIDGET_INT) { + } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_COMP)) { int wflags = w->flags; if(!(wflags & ZLE_KEEPSUFFIX)) removesuffix(); - if(!(wflags & ZLE_MENUCMP)) { + if(!(wflags & ZLE_MENUCMP) || + ((wflags & WIDGET_COMP) && compwidget != w)) { + /* If we are doing a special completion, and the widget + * is not the one currently in use for special completion, + * we are starting a new completion. + */ fixsuffix(); invalidatelist(); } @@ -575,8 +583,13 @@ execzlefunc(Thingy func) vilinerange = 1; if(!(wflags & ZLE_LASTCOL)) lastcol = -1; - w->u.fn(); - lastcmd = wflags; + if (wflags & WIDGET_COMP) { + compwidget = w; + completespecial(); + } else + w->u.fn(); + if (!(wflags & ZLE_NOTCOMMAND)) + lastcmd = wflags; } else { List l = getshfunc(w->u.fnnam); @@ -743,7 +756,7 @@ describekeybriefly(void) return; statusline = "Describe key briefly: _"; statusll = strlen(statusline); - refresh(); + zrefresh(); seq = getkeymapcmd(curkeymap, &func, &str); statusline = NULL; if(!*seq) @@ -811,14 +824,14 @@ void trashzle(void) { if (zleactive) { - /* This refresh() is just to get the main editor display right and * - * get the cursor in the right place. For that reason, we disable * - * list display (which would otherwise result in infinite * - * recursion [at least, it would if refresh() didn't have its * - * extra `inlist' check]). */ + /* This zrefresh() is just to get the main editor display right and * + * get the cursor in the right place. For that reason, we disable * + * list display (which would otherwise result in infinite * + * recursion [at least, it would if zrefresh() didn't have its * + * extra `inlist' check]). */ int sl = showinglist; showinglist = 0; - refresh(); + zrefresh(); showinglist = sl; moveto(nlnct, 0); if (clearflag && tccan(TCCLEAREOD)) { @@ -838,7 +851,7 @@ trashzle(void) static struct builtin bintab[] = { BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaMldDANmrsLR", NULL), BUILTIN("vared", 0, bin_vared, 1, 7, 0, NULL, NULL), - BUILTIN("zle", 0, bin_zle, 0, -1, 0, "lDANL", NULL), + BUILTIN("zle", 0, bin_zle, 0, -1, 0, "lDANCLmMgG", NULL), }; /**/ @@ -848,7 +861,7 @@ boot_zle(Module m) /* Set up editor entry points */ trashzleptr = trashzle; gotwordptr = gotword; - refreshptr = refresh; + refreshptr = zrefresh; spaceinlineptr = spaceinline; zlereadptr = zleread; diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c index 42953852f..836ff09ae 100644 --- a/Src/Zle/zle_misc.c +++ b/Src/Zle/zle_misc.c @@ -426,7 +426,7 @@ quotedinsert(void) #endif c = getkey(0); #ifndef HAS_TIO - setterm(); + zsetterm(); #endif if (c < 0) feep(); @@ -621,7 +621,7 @@ executenamedcommand(char *prmt) for (;;) { *ptr = '_'; statusll = l + len + 1; - refresh(); + zrefresh(); if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) { statusline = NULL; selectkeymap(okeymap, 1); @@ -633,7 +633,7 @@ executenamedcommand(char *prmt) redisplay(); } else if(cmd == Th(z_viquotedinsert)) { *ptr = '^'; - refresh(); + zrefresh(); c = getkey(0); if(c == EOF || !c || len == NAMLEN) feep(); diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 4621b5124..27e9f2071 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -47,7 +47,7 @@ int nlnct; /* Most lines of the buffer we've shown at once with the current list * * showing. == 0 if there is no list. == -1 if a new list has just * - * been put on the screen. == -2 if refresh() needs to put up a new * + * been put on the screen. == -2 if zrefresh() needs to put up a new * * list. */ /**/ @@ -55,7 +55,7 @@ int showinglist; /* Non-zero if ALWAYS_LAST_PROMPT has been used, meaning that the * * screen below the buffer display should not be cleared by * - * refresh(), but should be by trashzle(). */ + * zrefresh(), but should be by trashzle(). */ /**/ int clearflag; @@ -75,7 +75,7 @@ int cost; #endif /* Oct/Nov 94: some code savagely redesigned to fix several bugs - - refreshline() & tc_rightcurs() majorly rewritten; refresh() fixed - + refreshline() & tc_rightcurs() majorly rewritten; zrefresh() fixed - I've put my fingers into just about every routine in here - any queries about updates to mason@werple.net.au */ @@ -224,7 +224,7 @@ static int cleareol, /* clear to end-of-line (if can't cleareod) */ /**/ void -refresh(void) +zrefresh(void) { static int inlist; /* avoiding recursion */ int canscroll = 0, /* number of lines we are allowed to scroll */ @@ -240,7 +240,7 @@ refresh(void) char **qbuf; /* tmp */ /* If this is called from listmatches() (indirectly via trashzle()), and * - * that was called from the end of refresh(), then we don't need to do * + * that was called from the end of zrefresh(), then we don't need to do * * anything. All this `inlist' code is actually unnecessary, but it * * improves speed a little in a common case. */ if (inlist) @@ -263,7 +263,7 @@ refresh(void) termflags &= ~TERM_SHORT; if (resetneeded) { onumscrolls = 0; - setterm(); + zsetterm(); #ifdef TIOCGWINSZ if (winchanged) { moveto(0, 0); @@ -547,7 +547,7 @@ individually */ inlist = 1; listmatches(); inlist = 0; - refresh(); + zrefresh(); } if (showinglist == -1) showinglist = nlnct; diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index c4f2e25e1..f8de14f17 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -244,7 +244,9 @@ unbindwidget(Thingy t, int override) static void freewidget(Widget w) { - if(!(w->flags & WIDGET_INT)) + if ((w->flags & WIDGET_COMP) && w->u.cc) + freecompctl(w->u.cc); + else if(!(w->flags & WIDGET_INT)) zsfree(w->u.fnnam); zfree(w, sizeof(*w)); } @@ -334,6 +336,7 @@ bin_zle(char *name, char **args, char *ops, int func) { 'D', bin_zle_del, 1, -1 }, { 'A', bin_zle_link, 2, 2 }, { 'N', bin_zle_new, 1, 2 }, + { 'C', bin_zle_compctl, 1, -1}, { 0, bin_zle_call, 0, -1 }, }; struct opn const *op, *opp; @@ -385,17 +388,24 @@ scanlistwidgets(HashNode hn, int list) if(w->flags & WIDGET_INT) return; if(list) { - fputs("zle -N ", stdout); + fputs((w->flags & WIDGET_COMP) ? "zle -C " : "zle -N ", stdout); if(t->nam[0] == '-') fputs("-- ", stdout); quotedzputs(t->nam, stdout); - if(strcmp(t->nam, w->u.fnnam)) { + if (w->flags & WIDGET_COMP) { + if (printcompctlptr && w->u.cc) + printcompctlptr(NULL, w->u.cc, PRINT_LIST); + } else if(strcmp(t->nam, w->u.fnnam)) { fputc(' ', stdout); quotedzputs(w->u.fnnam, stdout); } } else { nicezputs(t->nam, stdout); - if(strcmp(t->nam, w->u.fnnam)) { + if (w->flags & WIDGET_COMP) { + fputs(" -C", stdout); + if (printcompctlptr && w->u.cc) + printcompctlptr(NULL, w->u.cc, PRINT_TYPE); + } else if(strcmp(t->nam, w->u.fnnam)) { fputs(" (", stdout); nicezputs(w->u.fnnam, stdout); fputc(')', stdout); @@ -456,6 +466,44 @@ bin_zle_new(char *name, char **args, char *ops, char func) return 1; } +/**/ +static int +bin_zle_compctl(char *name, char **args, char *ops, char func) +{ + Compctl cc = NULL; + Widget w; + char *wname = args[0]; + + if (!compctl_widgetptr) { + zwarnnam(name, "compctl module is not loaded", NULL, 0); + return 1; + } + + args++; + + if (*args && !(cc = compctl_widgetptr(name, args))) + return 1; + + w = zalloc(sizeof(*w)); + w->flags = WIDGET_COMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX; + w->first = NULL; + w->u.cc = cc; + if(bindwidget(w, rthingy(wname))) { + freewidget(w); + zerrnam(name, "widget name `%s' is protected", wname, 0); + return 1; + } + if (ops['m']) + w->flags |= ZLE_USEMENU; + else if (ops['M']) + w->flags |= ZLE_NOMENU; + if (ops['g']) + w->flags |= ZLE_USEGLOB; + else if (ops['G']) + w->flags |= ZLE_NOGLOB; + return 0; +} + /**/ static int bin_zle_call(char *name, char **args, char *ops, char func) diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 1aa1a008c..9ffef3b80 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -60,6 +60,7 @@ dopestring; # endif #endif + #define inststr(X) inststrlen((X),1,-1) /* wb and we hold the beginning/end position of the word we are completing. */ @@ -80,10 +81,11 @@ static int usemenu, useglob; static int menucmp; -/* A pointer to the current position in the menu-completion array (the one * - * that was put in the command line last). */ +/* Pointers to the current position in the groups list and in the menu- * + * completion array (the one that was put in the command line last). */ -static char **menucur; +static Cmgroup menugrp; +static Cmatch *menucur; /* menupos is the point (in the command line) where the menu-completion * * strings are inserted. menulen is the length of the string that was * @@ -94,29 +96,26 @@ static char **menucur; static int menupos, menulen, menuend, menuwe, menuinsc; -/* This is used as a flag from get_comp_string() that we are doing * - * completion inside a brace expansion. */ +/* 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. */ -static int complinbrace; +static char *brbeg = NULL, *brend = NULL; +static int brpl, brsl; /* The list of matches. fmatches contains the matches we first ignore * * because of fignore. */ static LinkList matches, fmatches; -/* The list of matches turned into an array. This is used to sort this * - * list and when menu-completion is used (directly or via automenu). */ - -static char **amatches; +/* This holds the list of matches-groups. lmatches is a pointer to the * + * last element in this list. */ -/* The number of matches. */ +static Cmgroup amatches, lmatches; -static int nmatches; +/* The total number of matches and the number of matches to be listed. */ -/* A list of user-defined explanations for the completions to be shown * - * instead of amatches when listing completions. */ - -static char **aylist; +static int nmatches, smatches; /* !=0 if we have a valid completion list. */ @@ -132,68 +131,112 @@ static int ispattern; static Comp patcomp, filecomp; -/* We store the following prefixes/suffixes: * - * lpre/lsuf -- what's on the line * - * rpre/rsuf -- same as lpre/lsuf, but expanded * - * * - * ... and if we are completing files, too: * - * ppre/psuf -- the path prefix/suffix * - * fpre/fsuf -- prefix/suffix of the pathname component the cursor is in * - * prpre -- ppre in expanded form usable for opendir * - * * - * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf, * - * fpre, and fsuf. noreal is non-zero if we have rpre/rsuf. */ +/* We store the following prefixes/suffixes: * + * lpre/lsuf -- what's on the line * + * rpre/rsuf -- same as lpre/lsuf, but expanded * + * * + * ... and if we are completing files, too: * + * ppre/psuf -- the path prefix/suffix * + * lppre/lpsuf -- the path prefix/suffix, unexpanded * + * fpre/fsuf -- prefix/suffix of the pathname component the cursor is in * + * prpre -- ppre in expanded form usable for opendir * + * ipre,ripre -- the ignored prefix (quotes and unquoted) * + * * + * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf, * + * fpre, fsuf, lppre, and lpsuf. noreal is non-zero if we have rpre/rsuf. */ static char *lpre, *lsuf; static char *rpre, *rsuf; -static char *ppre, *psuf, *prpre; +static char *ppre, *psuf, *lppre, *lpsuf, *prpre; static char *fpre, *fsuf; -static int lpl, lsl, rpl, rsl, fpl, fsl; +static char *ipre, *ripre; +static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl; static int noreal; -/* This is used when completing after `$' and holds the whole prefix, * - * used in do_single() to check whether the word expands to a directory * - * name (in that case and if autoparamslash is set, we add a `/'). * - * qparampre is the same but quoted. The length of it is in qparprelen. * - * parambr is != 0 if the parameter name is in braces. */ - -static char *parampre = NULL, *qparampre = NULL; -static int qparprelen, parambr; - /* This is either zero or equal to the special character the word we are * * trying to complete starts with (e.g. Tilde or Equals). */ static char ic; -/* These hold the minimum common prefix/suffix lengths (normal and for * - * fignore ignored). */ - -static int ab, ae, fab, fae; - /* This variable says what we are currently adding to the list of matches. */ static int addwhat; -/* firstm hold the first match we found, shortest contains the shortest * - * one (normal and for fignore ignored). */ - -static char *firstm, *shortest, *ffirstm, *fshortest; - /* This holds the word we are completing in quoted from. */ static char *qword; -/* This is the length of the shortest match we found (normal and for * - * fignore ignored). */ - -static int shortl, fshortl; - /* This is non-zero if we are doing a menu-completion and this is not the * * first call (e.g. when automenu is set and menu-completion was entered * * due to this). */ static int amenu; +/* The current group of matches. */ + +static Cmgroup mgroup; + +/* A match counter. */ + +static int mnum; + +/* Match flags for all matches in this group. */ + +static int mflags; + +/* This holds the explanation strings we have to print in this group and * + * a pointer to the current cexpl structure. */ + +static LinkList expls; +static Cexpl expl; + +/* A pointer to the compctl we are using. */ + +static Compctl curcc; + +/* A list of all compctls we have already used. */ + +static LinkList ccused; + +/* A list of all compctls used so far. */ + +static LinkList allccs; + +/* A stack of currently used compctls. */ + +static LinkList ccstack; + +/* A stack of completion matchers to be used. */ + +static Cmlist mstack; + +/* A heap of free Cline structures. */ + +static Cline freecl; + +/* Information for ambiguous completions. One for fignore ignored and * + * one for normal completion. */ + +typedef struct aminfo *Aminfo; + +struct aminfo { + int cpl, csl, icpl, icsl; /* common prefix/suffix lengths */ + int prerest; /* minimum prefix rest */ + int suflen; /* minimum suffix length */ + Cmatch firstm; /* the first match */ + char *pprefix; /* common part of the -P prefixes */ + char *aprefix; /* common line prefix */ + int noipre; /* if the was no ignored prefix */ + char *iprefix; /* common ignored prefix */ + char *iaprefix; /* like aprefix, without ignored prefixes */ + int exact; /* if there was an exact match */ + Cmatch exactm; /* the exact match (if any) */ + Cline linecl, ilinecl; /* what to put on the line as a Cline */ + int count; /* number of matches */ +}; + +static Aminfo ainfo, fainfo; + /* Find out if we have to insert a tab (instead of trying to complete). */ /**/ @@ -208,14 +251,27 @@ usetab(void) return 1; } -#define COMP_COMPLETE 0 -#define COMP_LIST_COMPLETE 1 -#define COMP_SPELL 2 -#define COMP_EXPAND 3 -#define COMP_EXPAND_COMPLETE 4 -#define COMP_LIST_EXPAND 5 +enum { COMP_COMPLETE, + COMP_WIDGET, + COMP_LIST_COMPLETE, + COMP_SPELL, + COMP_EXPAND, + COMP_EXPAND_COMPLETE, + COMP_LIST_EXPAND }; #define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND) +/**/ +void +completespecial(void) +{ + int flags = compwidget->flags; + usemenu = (flags & ZLE_USEMENU) ? 1 : (flags & ZLE_NOMENU) ? 0 + : isset(MENUCOMPLETE); + useglob = (flags & ZLE_USEGLOB) ? 1 : (flags & ZLE_NOGLOB) ? 0 + : isset(GLOBCOMPLETE); + docomplete(compwidget->u.cc ? COMP_WIDGET : COMP_COMPLETE); +} + /**/ void completeword(void) @@ -261,16 +317,19 @@ spellword(void) void deletecharorlist(void) { - char **mc = menucur; + Cmgroup mg = menugrp; + Cmatch *mc = menucur; usemenu = isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); - if (cs != ll) + if (cs != ll) { + fixsuffix(); deletechar(); - else + } else docomplete(COMP_LIST_COMPLETE); menucur = mc; + menugrp = mg; } /**/ @@ -326,8 +385,13 @@ reversemenucomplete(void) return; } HEAPALLOC { - if (menucur == amatches) - menucur = amatches + nmatches - 1; + if (menucur == menugrp->matches) { + do { + if (!(menugrp = menugrp->prev)) + menugrp = lmatches; + } while (!menugrp->mcount); + menucur = menugrp->matches + menugrp->mcount - 1; + } else menucur--; metafy_line(); @@ -350,16 +414,8 @@ acceptandmenucomplete(void) } cs = menuend + menuinsc; inststrlen(" ", 1, 1); - if (qparampre) - inststrlen(qparampre, 1, qparprelen); - if (lpre && !ispattern) - inststrlen(lpre, 1, -1); - if (lsuf && !ispattern) - inststrlen(lsuf, 0, -1); + menuinsc = menulen = 0; menupos = cs; - menuend = cs + (lsuf ? strlen(lsuf) : 0); - menulen = 0; - menuinsc = 0; menuwe = 1; menucomplete(); } @@ -378,34 +434,7 @@ static int lincmd, linredir; static int lastambig; -/* This describes some important things collected during the last * - * completion. Its value is zero or the inclusive OR of some of * - * the HAS_* things below. */ - -static int haswhat; - -/* We have a suffix to add (given with compctl -S). */ - -#define HAS_SUFFIX 1 - -/* We have filenames in the completion list. */ - -#define HAS_FILES 2 - -/* We have other things than files in the completion list. If this is * - * not set but HAS_FILES is, we probably put the file type characters * - * in the completion list (if listtypes is set) and we attempt to add * - * a slash to completed directories. */ - -#define HAS_MISC 4 - -/* This is set if we have filenames in the completion list that were * - * generated by a globcompletion pattern. */ - -#define HAS_PATHPAT 8 - - -/* This holds the naem of the current command (used to find the right * +/* This holds the name of the current command (used to find the right * * compctl). */ static char *cmdstr; @@ -580,7 +609,7 @@ docomplete(int lst) if (*q == Equals) { /* The word starts with `=', see if we can expand it. */ q = s + 1; - if (cmdnamtab->getnode(cmdnamtab, q) || hashcmd(q, pathchecked)) + if (cmdnamtab->getnode(cmdnamtab, q) || hashcmd(q, pathchecked)) { if (isset(RECEXACT)) lst = COMP_EXPAND; else { @@ -602,6 +631,7 @@ docomplete(int lst) if (n == 1) lst = COMP_EXPAND; } + } } if (lst == COMP_EXPAND_COMPLETE) do { @@ -710,16 +740,17 @@ docomplete(int lst) if (*p == Tilde || *p == Equals) p++; for (; *p; p++) - if (itok(*p)) + if (itok(*p)) { if (*p != String && *p != Qstring) *p = ztokens[*p - Pound]; else if (p[1] == Inbrace) p++, skipparens(Inbrace, Outbrace, &p); - docompletion(s, lst, lincmd, 1); + } + docompletion(s, lst, lincmd); } } else /* Just do completion. */ - docompletion(s, lst, lincmd, 0); + docompletion(s, lst, lincmd); zsfree(s); } /* Reset the lexer state, pop the heap. */ @@ -745,8 +776,13 @@ do_menucmp(int lst) } /* Otherwise go to the next match in the array... */ HEAPALLOC { - if (!*++menucur) - menucur = amatches; + if (!*++menucur) { + do { + if (!(menugrp = menugrp->next)) + menugrp = amatches; + } while (!menugrp->mcount); + menucur = menugrp->matches; + } /* ... and insert it into the command line. */ metafy_line(); do_single(*menucur); @@ -754,9 +790,6 @@ do_menucmp(int lst) } LASTALLOC; } -/* 1 if we are completing in a string */ -static int instring; - /* 1 if we are completing the prefix */ static int comppref; @@ -859,7 +892,9 @@ get_comp_string(void) int t0, tt0, i, j, k, cp, rd, sl, ocs; char *s = NULL, *linptr, *tmp, *p, *tt = NULL; - complinbrace = 0; + zsfree(brbeg); + zsfree(brend); + brbeg = brend = NULL; /* This global flag is used to signal the lexer code if it should * * expand aliases or not. */ noaliases = isset(COMPLETEALIASES); @@ -1167,6 +1202,12 @@ get_comp_string(void) if (tt && tt < s + myoffs) { /* Braces are go: delete opening brace */ char *com = NULL; + int pl, sl; + + brbeg = dupstring(tt); + brpl = tt - s; + pl = 1; + sl = 0; chuck(tt); offs--; myoffs--; @@ -1177,25 +1218,33 @@ get_comp_string(void) com = p; if (com) { i = com - tt + 1; - while (i--) - chuck(tt), offs--, myoffs--; + offs -= i; + myoffs -= i; + strcpy(tt, tt + i); + pl += i; } + brbeg[pl] = '\0'; /* Look for text between subsequent comma * and closing brace or end of string and delete it */ - for (p = s + myoffs; *p && *p != Outbrace; p++) - if (*p == Comma) { - while (*p && *p != Outbrace) - chuck(p); - break; + for (p = s + myoffs; *p && *p != Outbrace && *p != Comma; p++); + if (*p == Comma || *p == Outbrace) { + brend = dupstring(p); + sl = 1; + while (*p && *p != Outbrace) { + chuck(p); sl++; } - if (*p == Outbrace) - chuck(p); - else { - /* we are still waiting for an outbrace and maybe commas */ - complinbrace = 1; + if (*p == Outbrace) + chuck(p); + brsl = strlen(s) - (p - s); + brend[sl] = '\0'; } + /* we are still waiting for an outbrace and maybe commas */ + if (brbeg) + untokenize(brbeg = ztrdup(brbeg)); + if (brend) + untokenize(brend = ztrdup(brend)); } } @@ -1245,7 +1294,7 @@ doexpansion(char *s, int lst, int olst, int explincmd) /* If expansion didn't change the word, try completion if * * expandorcomplete was called, otherwise, just beep. */ if (lst == COMP_EXPAND_COMPLETE) - docompletion(s, COMP_COMPLETE, explincmd, 0); + docompletion(s, COMP_COMPLETE, explincmd); else feep(); goto end; @@ -1292,290 +1341,1303 @@ gotword(void) } } -/* Insert the given string into the command line. If move is non-zero, * - * the cursor position is changed and len is the length of the string * - * to insert (if it is -1, the length is calculated here). */ +/* This adds a string to the currently built match. The last argument * + * is non zero if we are building the suffix, where we have to prepend * + * the given string. */ -/**/ -static void -inststrlen(char *str, int move, int len) +static char * +addtoword(char **rwp, int *rwlenp, char *nw, + Cmatcher m, char *l, char *w, int wl, int prep) { - if (!len) - return; - if (len == -1) - len = strlen(str); - spaceinline(len); - strncpy((char *)(line + cs), str, len); - if (move) - cs += len; + int al, rwlen = *rwlenp, nl; + char *as, *rw = *rwp; + + if (m && (m->flags & CMF_LINE)) { + al = m->llen; + as = l; + } else { + al = wl; + as = w; + } + if (!rwlen || (nl = al + (nw - rw)) >= rwlen) { + char *np; + + if (!rwlen) + nl = al + 20; + + np = (char *) zalloc(nl + 1); + + *rwlenp = nl; + if (rwlen) { + memcpy(np, rw, rwlen); + nw += np - rw; + zfree(rw, rwlen); + } + else + nw = np; + *rwp = rw = np; + rwlen = nl; + } + if (prep) { + memmove(rw + al, rw, rwlen - al); + memcpy(rw, as, al); + } + else + memcpy(nw, as, al); + + return nw + al; } -/* Quote the string s and return the result. If e is non-zero, it the * - * pointer it points to may point to aposition in s and in e the position * - * of the corresponding character in the quoted string is returned. Like * - * e, te may point to a position in the string and pl is used to return * - * the position of the character pointed to by te in the quoted string. * - * The string is metafied and may contain tokens. */ +/* This get a new Cline structure. */ -/**/ -static char * -quotename(const char *s, char **e, char *te, int *pl) -{ - const char *u, *tt; - char *v, buf[PATH_MAX * 2]; - int sf = 0; - - tt = v = buf; - u = s; - for (; *u; u++) { - if (e && *e == u) - *e = v, sf |= 1; - if (te == u) - *pl = v - tt, sf |= 2; - if (ispecial(*u) && - (!instring || (isset(BANGHIST) && - *u == (char)bangchar) || - (instring == 2 && - (*u == '$' || *u == '`' || *u == '\"')) || - (instring == 1 && *u == '\''))) - if (*u == '\n' || (instring == 1 && *u == '\'')) { - if (unset(RCQUOTES)) { - *v++ = '\''; - if (*u == '\'') - *v++ = '\\'; - *v++ = *u; - *v++ = '\''; - } else if (*u == '\n') - *v++ = '"', *v++ = '\n', *v++ = '"'; - else - *v++ = '\'', *v++ = '\''; - continue; - } else - *v++ = '\\'; - if(*u == Meta) - *v++ = *u++; - *v++ = *u; - } - *v = '\0'; - if (strcmp(buf, s)) - tt = dupstring(buf); +static Cline +getcline(char *l, int ll, char *w, int wl, Cmatcher m, int fl) +{ + Cline r; + + if ((r = freecl)) + freecl = r->next; else - tt = s; - v += tt - buf; - if (e && (sf & 1)) - *e += tt - buf; + r = (Cline) halloc(sizeof(*r)); - if (e && *e == u) - *e = v; - if (te == u) - *pl = v - tt; + r->next = NULL; + r->line = l; + r->llen = ll; + r->word = w; + r->wlen = wl; + r->matcher = m; + r->flags = fl; - return (char *) tt; + return r; } -/* This adds a match to the list of matches. The string to add is given * - * in s, the type of match is given in the global variable addwhat and * - * the parameter t (if not NULL) is a pointer to a hash node node which * - * may be used to give other information to this function. * - * * - * addwhat contains either one of the special values (negative, see below) * - * or the inclusive OR of some of the CC_* flags used for compctls. */ +/* This add a Cline structure with the given parameters. */ -/**/ static void -addmatch(char *s, char *t) +addtocline(Cline *retp, Cline *lrp, + char *l, int ll, char *w, int wl, Cmatcher m, int fl) { - int test = 0, sl = strlen(s), pl = rpl, cc = 0, *bp, *ep, *sp; - char *e = NULL, *tt, *te, *fc, **fm; - Comp cp = patcomp; - HashNode hn; - Param pm; - LinkList l = matches; + Cline ln = getcline(l, ll, w, wl, m, fl); -/* - * addwhat: -5 is for files, - * -6 is for glob expansions, - * -8 is for executable files (e.g. command paths), - * -9 is for parameters - * -7 is for command names (from cmdnamtab) - * -4 is for a cdable parameter - * -3 is for executable command names. - * -2 is for anything unquoted - * -1 is for other file specifications - * (things with `~' of `=' at the beginning, ...). - */ + if (m && (m->flags & CMF_LINE)) + ln->word = NULL; + if (*retp) + (*lrp)->next = ln; + else + *retp = ln; - /* Just to make the code cleaner */ - hn = (HashNode) t; - pm = (Param) t; + *lrp = ln; +} - if (!addwhat) { - test = 1; - } else if (addwhat == -1 || addwhat == -5 || addwhat == -6 || - addwhat == CC_FILES || addwhat == -7 || addwhat == -8) { - if (sl < fpl + fsl) - return; +/* Joins two Cline lists, building the most specific line string * + * that is possible. */ - if ((addwhat == CC_FILES || - addwhat == -5) && !*psuf && !*fsuf) { - /* If this is a filename, do the fignore check. */ - char **pt = fignore; - int filell; +static Cline +join_clines(Cline o, Cline n) +{ + Cline oo = o; - for (test = 1; test && *pt; pt++) - if ((filell = strlen(*pt)) < sl - && !strcmp(*pt, s + sl - filell)) - test = 0; + if (!o) + return n; + else { + Cline f = freecl, q, op = NULL; + int ol, nl; - if (!test) - l = fmatches; - } - pl = fpl; - if (addwhat == -5 || addwhat == -8) { - test = 1; - cp = filecomp; - cc = cp || ispattern; - e = s + sl - fsl; - } else { - if ((cp = filecomp)) { - if ((test = domatch(s, filecomp, 0))) - cc = 1; - } else { - e = s + sl - fsl; - if ((test = !strncmp(s, fpre, fpl))) - test = !strcmp(e, fsuf); - if (ispattern) - cc = 1; + while (o && n) { + if (o->flags & CLF_MID) { + while (n && !(n->flags & CLF_MID)) { + q = n->next; + n->next = f; + f = n; + + n = q; + } } - } - if (test) { - fc = NULL; - if (addwhat == -7 && !(fc = findcmd(s))) - return; - if (fc) - zsfree(fc); - haswhat |= HAS_FILES; + if (n->flags & CLF_MID) { + while (o && !(o->flags & CLF_MID)) { + o->word = NULL; + o->flags |= CLF_DIFF; - if (addwhat == CC_FILES || addwhat == -6 || - addwhat == -5 || addwhat == -8) { - te = s + pl; - s = quotename(s, &e, te, &pl); - sl = strlen(s); - } else if (!cc) { - s = dupstring(t = s); - e += s - t; + o = o->next; + } } - if (cc) { - tt = (char *)halloc(strlen(ppre) + strlen(psuf) + sl + 1); - strcpy(tt, ppre); - strcat(tt, s); - strcat(tt, psuf); - untokenize(s = tt); + if (o && n && !((o->flags | n->flags) & CLF_MID)) { + ol = o->llen; + nl = n->llen; + + while (o && n && ol != nl) { + /* The matched strings have different lengths, so * + * continue walking the lists until we have the same * + * matched lengths. */ + o->word = NULL; + o->flags |= CLF_DIFF; + if (ol < nl) { + op = o; + if ((o = o->next)) + ol += o->llen; + } else { + q = n->next; + n->next = f; + f = n; + + if ((n = q)) + nl += n->llen; + } + } } - } - } else if (addwhat == CC_QUOTEFLAG || addwhat == -2 || - (addwhat == -3 && !(hn->flags & DISABLED)) || - (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) && - (tt = pm->gets.cfn(pm)) && *tt == '/') || - (addwhat == -9 && !(hn->flags & PM_UNSET)) || - (addwhat > 0 && - ((!(hn->flags & PM_UNSET) && - (((addwhat & CC_ARRAYS) && (hn->flags & PM_ARRAY)) || - ((addwhat & CC_INTVARS) && (hn->flags & PM_INTEGER)) || - ((addwhat & CC_ENVVARS) && (hn->flags & PM_EXPORTED)) || - ((addwhat & CC_SCALARS) && (hn->flags & PM_SCALAR)) || - ((addwhat & CC_READONLYS) && (hn->flags & PM_READONLY)) || - ((addwhat & CC_SPECIALS) && (hn->flags & PM_SPECIAL)) || - ((addwhat & CC_PARAMS) && !(hn->flags & PM_EXPORTED)))) || - ((( addwhat & CC_SHFUNCS) || - ( addwhat & CC_BUILTINS) || - ( addwhat & CC_EXTCMDS) || - ( addwhat & CC_RESWDS) || - ((addwhat & CC_ALREG) && !(hn->flags & ALIAS_GLOBAL)) || - ((addwhat & CC_ALGLOB) && (hn->flags & ALIAS_GLOBAL))) && - (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) || - ((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) || - ((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) { - if (sl >= rpl + rsl) { - if (cp) - test = domatch(s, patcomp, 0); - else { - e = s + sl - rsl; - if ((test = !strncmp(s, rpre, rpl))) - test = !strcmp(e, rsuf); + 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; + } 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. * + * 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; + } else if (o->wlen == n->wlen) { + /* Otherwise keep them if they are equal. */ + if (strncmp(o->word, n->word, o->wlen)) { + /* If they are not equal, we make them * + * be left unchanged on the line. */ + o->word = NULL; + o->flags |= CLF_DIFF; + } + } else { + o->word = NULL; + o->flags |= CLF_DIFF; + } + } + else { + o->word = NULL; + o->flags |= CLF_DIFF; + } } + else if (n->word) + o->flags |= CLF_DIFF; + + q = n->next; + n->next = f; + f = n; + + n = q; + op = o; + o = o->next; } - if (!test && sl < lpl + lsl) - return; - if (!test && lpre && lsuf && sl >= lpl + lsl) { - e = s + sl - lsl; - if ((test = !strncmp(s, lpre, lpl))) - test = !strcmp(e, lsuf); - pl = lpl; + if (o) { + for (q = o; q->next; q = q->next); + + q->next = f; + f = o; + + if (op) + op->next = NULL; + else + return NULL; } - if (addwhat == CC_QUOTEFLAG) { - te = s + pl; - s = quotename(s, &e, te, &pl); - sl = strlen(s); + if (n) { + /* We always put the chunks from the second list back on * + * the free list. */ + for (q = n; q->next; q = q->next); + + q->next = f; + f = n; } - if (test) - haswhat |= HAS_MISC; + freecl = f; } - if (!test) - return; + return oo; +} - if (ispattern) { - t = s; - } else { - t = s += pl; - if (*e) - t = s = dupstrpfx(t, e - t); - } +/* This returns a Cline for the given string. */ - if (l == fmatches) { - bp = &fab; - ep = &fae; - sp = &fshortl; - fm = &ffirstm; - } else { - bp = &ab; - ep = &ae; - sp = &shortl; - fm = &firstm; - } - - if (!ispattern && *fm) { - if ((test = pfxlen(*fm, s)) < *bp) - *bp = test; - if ((test = sfxlen(*fm, s)) < *ep) - *ep = test; - if (*ep > *sp - *bp) - *ep = *sp - *bp; - } - - /* If we are doing a glob completion we store the whole string in * - * the list. Otherwise only the part that fits between the prefix * - * and the suffix is stored. */ - addlinknode(l, t); - if (!*fm) { - *bp = *ep = 10000; - *fm = t; - *sp = 100000; - } - if (!ispattern && (sl = strlen(t)) < *sp) { - *sp = sl; - if (l == fmatches) - fshortest = t; - else - shortest = t; +static Cline +str_cline(char *s, int l, Cline *lp) +{ + Cline r = NULL, *p = &r, n = NULL; + + if (l < 0) + l = strlen(s); + while (l) { + *p = n = getcline(s, 1, NULL, 0, NULL, 0); + + p = &(n->next); + s++; + l--; } + if (lp) + *lp = n; + + return r; } -#ifdef HAVE_NIS_PLUS -static int -match_username(nis_name table, nis_object *object, void *userdata) +/* This reverts the order of the chunks. */ + +static Cline +revert_clines(Cline p) { - if (errflag) + Cline r = NULL, n; + + while (p) { + n = p->next; + p->next = r; + r = p; + p = n; + } + return r; +} + +/* Prepend a string to a Cline. */ + +static Cline +prepend_cline(char *s, Cline l) +{ + Cline n, r = str_cline(s, -1, &n), *p = &(n->next); + + while (l) { + *p = n = getcline(l->line, l->llen, l->word, l->wlen, + l->matcher, l->flags); + + p = &(n->next); + l = l->next; + } + 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 * + * and the suffix length. */ + +static void +merge_cline(Cline lc, char *p, int pl, char *s, int sl, int psl) +{ + int pll, sll; + Cline l = NULL, *q = &l, n; + + pll = strlen(p); + if (s) { + int i = strlen(s); + + if (ainfo->suflen < 10000) + s = s + i - ainfo->suflen; + else { + s = s + i - sl; + p[pll - (sl - psl)] = '\0'; + pll -= sl - psl; + } + sll = strlen(s) - sl; + } + else + sll = 0; + + pll -= pl; + + l = str_cline(p, pl, &n); + q = &(n->next); + if (!sl) + *q = getcline(NULL, 0, p + pl, pll, NULL, CLF_END); + else { + *q = n = getcline(p + pl, pll, s, sll, NULL, CLF_MID); + + n->next = str_cline(s + sll, sl, NULL); + } + join_clines(lc, l); +} + +/* This inserts the Cline built into the command line. */ + +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; + pl += brpl; + + i = cs - wb; + while (l) { + if (d < 0 && (l->flags & CLF_DIFF)) + d = cs; + 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) + b = cs; + if (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); + } + if (m < 0 && (l->flags & CLF_MISS)) + m = cs; + i += l->llen; + if (pl >= 0 && i >= pl && brbeg && *brbeg) { + inststrlen(brbeg, 1, -1); + pl = -1; + hb = 1; + } + if (sl >= 0 && i >= sl && brend && *brend) { + inststrlen(brend, 1, -1); + sl = -1; + hb = 1; + } + l = l->next; + } + cs = (b >= 0 && hb ? b : (m >= 0 ? m : (d >= 0 ? d : cs))); + if (cs > ll) + cs = ll; +} + +/* Check if the given pattern matches the given string. * + * `in' and `out' are used for {...} classes. In `out' we store the * + * character number that was matched. In the word pattern this is * + * 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) +{ + unsigned char c; + + while (p) { + c = *((unsigned char *) s); + + if (out) *out = 0; + + if (p->equiv) { + if (in) { + c = p->tab[c]; + 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; + } + + s++; + p = p->next; + } + return 1; +} + +/* Do the matching for a prefix. */ + +static char * +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 il = 0, iw = 0, t, stil, stiw, std, bc = brpl; + char *nw = rw, *stl = NULL, *stw; + Cmlist ms; + Cmatcher mp, stm; + Cline lr = NULL; + + *nlp = NULL; + + if (ll > ealen) { + /* This is the `in'/`out' string for pattern matching. */ + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = ll + 20); + } + while (ll && lw) { + if (*l == *w) { + /* Same character, take it. */ + + if (stl) { + /* But first check, if we were collecting characters * + * for a `*'. */ + int sl = iw - stiw; + + nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0); + + addtocline(nlp, &lr, stl, stm->llen, + stw, sl, stm, (std ? CLF_SUF : 0)); + + stl = NULL; + + if (bc <= 0 && bplp) { + *bplp = nw - rw; + bplp = NULL; + } + } + nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l, 1, 0); + + addtocline(nlp, &lr, l, 1, NULL, 0, NULL, 0); + + l++; + w++; + il++; + iw++; + ll--; + lw--; + bc--; + + if (bc <= 0 && bplp) { + *bplp = nw - rw; + bplp = NULL; + } + continue; + } + for (ms = mstack; ms; ms = ms->next) { + for (mp = ms->matcher; mp; mp = mp->next) { + t = 1; + /* Try to match the prefix, if any. */ + if (mp->flags & CMF_LEFT) { + if (il < mp->lalen || iw < mp->lalen) + t = 0; + else if (mp->left) + t = pattern_match(mp->left, l - mp->lalen, NULL, NULL) && + pattern_match(mp->left, w - mp->lalen, NULL, NULL); + else + t = (!il && !iw); + } + if (t) { + /* Now match the line pattern. */ + if (ll < mp->llen || lw < mp->wlen) + t = 0; + else if (mp->wlen < 0) { + /* 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 (t && !stl) { + /* We simply keep the current position * + * and start collecting characters until * + * another matcher matches. */ + std = (mp->flags & CMF_LEFT); + stl = l; + stil = il; + stw = w; + stiw = iw; + stm = mp; + t = 0; + l += mp->llen; + il += mp->llen; + ll -= mp->llen; + + break; + } + else + t = 0; + } + } else { + /* No `*', just try to match the line and word * + * patterns. */ + t = pattern_match(mp->line, l, NULL, ea) && + pattern_match(mp->word, w, ea, NULL); + mlw = mp->wlen; + } + } + /* Now test the right anchor, if any. */ + if (t && (mp->flags & CMF_RIGHT)) { + if (ll < mp->llen + mp->ralen || lw < mlw + mp->ralen) + t = 0; + else if (mp->right) + t = pattern_match(mp->right, l + mp->llen, NULL, NULL) && + pattern_match(mp->right, w + mlw, NULL, NULL); + else + t = 0; + } + if (t) { + /* If it matched, build a new chunk on the Cline list * + * and add the string to the built match. */ + if (stl) { + int sl = iw - stiw; + + nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0); + + addtocline(nlp, &lr, + stl, stm->llen, stw, sl, stm, + (std ? CLF_SUF : 0)); + + stl = NULL; + + if (bc <= 0 && bplp) { + *bplp = nw - rw; + bplp = NULL; + } + } + nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 0); + + addtocline(nlp, &lr, l, mp->llen, w, mlw, mp, 0); + + l += mp->llen; + w += mlw; + ll -= mp->llen; + lw -= mlw; + il += mp->llen; + iw += mlw; + bc -= mp->llen; + + if (bc <= 0 && bplp) { + *bplp = nw - rw; + bplp = NULL; + } + break; + } + } + if (mp) + break; + } + if (!stl && !t) { + if (*nlp) { + lr->next = freecl; + freecl = *nlp; + } + return NULL; + } + if (stl) { + /* We are collecting characters, just skip over. */ + w++; + lw--; + iw++; + } + } + *lp = iw; + if (lw) { + /* There is a unmatched portion in the word, keep it. */ + if (rlp) { + w = dupstring(w); + addtocline(nlp, &lr, w, lw, w, -1, NULL, CLF_MID); + + *rlp = lr; + } else { + addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END); + + nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0); + } + } + else if (rlp) { + if (lr) { + lr->next = freecl; + freecl = *nlp; + } + return NULL; + } + if (nw) + *nw = '\0'; + + if (ll) { + if (*nlp) { + lr->next = freecl; + freecl = *nlp; + } + return 0; + } + /* Finally, return the built match string. */ + return dupstring(rw); +} + +/* Do the matching for a suffix. */ + +static char * +match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) +{ + static unsigned char *ea; + static int ealen = 0; + static char *rw; + static int rwlen; + + int ll = strlen(l), lw = strlen(w), mlw; + int il = 0, iw = 0, t, stil, stiw, std, bc = brsl; + char *nw = rw, *stl = NULL, *stw; + Cmlist ms; + Cmatcher mp, stm; + Cline lr = NULL; + + l += ll; + w += lw; + + *nlp = NULL; + + if (ll > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = ll + 20); + } + while (ll && lw) { + if (l[-1] == w[-1]) { + if (stl) { + int sl = iw - stiw; + + stl -= stm->llen; + stw -= sl; + nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1); + + addtocline(nlp, &lr, stl, stm->llen, + stw, sl, stm, (std ? CLF_SUF : 0)); + + stl = NULL; + + if (bc <= 0 && bslp) { + *bslp = nw - rw; + bslp = NULL; + } + } + nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l - 1, 1, 1); + + addtocline(nlp, &lr, l - 1, 1, NULL, 0, NULL, 0); + + l--; + w--; + il++; + iw++; + ll--; + lw--; + bc--; + if (bc <= 0 && bslp) { + *bslp = nw - rw; + bslp = NULL; + } + continue; + } + for (ms = mstack; ms; ms = ms->next) { + for (mp = ms->matcher; mp; mp = mp->next) { + t = 1; + if (mp->flags & CMF_RIGHT) { + if (il < mp->ralen || iw < mp->ralen) + t = 0; + else if (mp->right) + t = pattern_match(mp->right, l, NULL, NULL) && + pattern_match(mp->right, w, NULL, NULL); + else + t = (!il && !iw); + } + if (t) { + if (ll < mp->llen || lw < mp->wlen) + t = 0; + 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 (t && !stl) { + std = (mp->flags & CMF_LEFT); + stl = l; + stil = il; + stw = w; + stiw = iw; + stm = mp; + t = 0; + l -= mp->llen; + il += mp->llen; + ll -= mp->llen; + + break; + } + else + t = 0; + } + } else { + t = pattern_match(mp->line, l - mp->llen, NULL, ea) && + pattern_match(mp->word, w - mp->wlen, ea, NULL); + mlw = mp->wlen; + } + } + if (t && (mp->flags & CMF_LEFT)) { + if (ll < mp->llen + mp->lalen || lw < mlw + mp->lalen) + t = 0; + else if (mp->left) + t = pattern_match(mp->right, l - mp->llen - mp->lalen, + NULL, NULL) && + pattern_match(mp->right, w - mlw - mp->lalen, + NULL, NULL); + else + t = 0; + } + if (t) { + if (stl) { + int sl = iw - stiw; + + stl -= stm->llen; + stw -= sl; + + nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1); + + addtocline(nlp, &lr, + stl, stm->llen, stw, sl, stm, + (std ? CLF_SUF : 0)); + + stl = NULL; + + if (bc <= 0 && bslp) { + *bslp = nw - rw; + bslp = NULL; + } + } + nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1); + + addtocline(nlp, &lr, l - mp->llen, mp->llen, + w - mlw, mlw, mp, 0); + + l -= mp->llen; + w -= mlw; + ll -= mp->llen; + lw -= mlw; + il += mp->llen; + iw += mlw; + bc -= mp->llen; + if (bc <= 0 && bslp) { + *bslp = nw - rw; + bslp = NULL; + } + break; + } + } + if (mp) + break; + } + if (!stl && !t) { + if (*nlp) { + lr->next = freecl; + freecl = *nlp; + } + return NULL; + } + if (stl) { + w--; + lw--; + iw++; + } + } + *lp = iw; + if (nw) + *nw = '\0'; + + if (ll) { + if (*nlp) { + lr->next = freecl; + freecl = *nlp; + } + return 0; + } + return dupstring(rw); +} + +/* Check if the word `w' matches. */ + +static char * +comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl) +{ + char *r = NULL; + Cline pli; + int pl; + + if (qu) + w = quotename(w, NULL, NULL, NULL); + if (*sfx) { + char *p, *s; + int sl; + Cline sli, last; + + if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl))) { + if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl))) { + int pml, sml; + + last->llen -= sl; + last->next = revert_clines(sli); + + pml = strlen(p); + sml = strlen(s); + r = (char *) halloc(pml + sml + last->llen + 1); + strcpy(r, p); + strncpy(r + pml, last->line, last->llen); + strcpy(r + pml + last->llen, s); + } else { + last->next = freecl; + freecl = pli; + + return NULL; + } + } + else + return NULL; + } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl))) + return NULL; + + if (lppre && *lppre) { + Cline l, t = str_cline(lppre, -1, &l); + + l->next = pli; + pli = t; + } + if (lpsuf && *lpsuf) { + Cline n, t = str_cline(lpsuf, -1, NULL); + + if ((n = pli)) { + while (n->next) n = n->next; + + n->next = t; + } else + pli = t; + } + *clp = pli; + + return r; +} + +/* Insert the given string into the command line. If move is non-zero, * + * the cursor position is changed and len is the length of the string * + * to insert (if it is -1, the length is calculated here). */ + +/**/ +static void +inststrlen(char *str, int move, int len) +{ + if (!len) + return; + if (len == -1) + len = strlen(str); + spaceinline(len); + strncpy((char *)(line + cs), str, len); + if (move) + cs += len; +} + +/* Insert the given match. This returns the number of characters inserted.*/ + +/**/ +static int +instmatch(Cmatch m) +{ + int l, r = 0, ocs, a = cs; + + if (m->ipre) { + inststrlen(m->ipre, 1, (l = strlen(m->ipre))); + r += l; + } + if (m->pre) { + inststrlen(m->pre, 1, (l = strlen(m->pre))); + r += l; + } + if (m->ppre) { + inststrlen(m->ppre, 1, (l = strlen(m->ppre))); + r += l; + } + inststrlen(m->str, 1, (l = strlen(m->str))); + r += l; + ocs = cs; + if (brbeg && *brbeg) { + cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0); + l = strlen(brbeg); + inststrlen(brbeg, 1, l); + r += l; + ocs += l; + cs = ocs; + } + if (m->psuf) { + inststrlen(m->psuf, 1, (l = strlen(m->psuf))); + r += l; + } + if (brend && *brend) { + a = cs; + cs -= m->brsl; + ocs = cs; + l = strlen(brend); + inststrlen(brend, 1, l); + r += l; + cs = a + l; + } + if (m->suf) { + inststrlen(m->suf, 1, (l = strlen(m->suf))); + r += l; + } + cs = ocs; + return r; +} + + +/* This adds a match to the list of matches. The string to add is given * + * in s, the type of match is given in the global variable addwhat and * + * the parameter t (if not NULL) is a pointer to a hash node node which * + * may be used to give other information to this function. * + * * + * addwhat contains either one of the special values (negative, see below) * + * or the inclusive OR of some of the CC_* flags used for compctls. */ + +/**/ +static void +addmatch(char *s, char *t) +{ + int test = 0, sl = strlen(s), pl = rpl, cc = 0, isf = 0; + int mpl = 0, msl = 0, bpl = brpl, bsl = brsl; + char *e = NULL, *tt, *te, *fc, *ms = NULL; + Comp cp = patcomp; + HashNode hn; + Param pm; + LinkList l = matches; + Cmatch cm; + Cline lc = NULL; + Aminfo ai = ainfo; + +/* + * addwhat: -5 is for files, + * -6 is for glob expansions, + * -8 is for executable files (e.g. command paths), + * -9 is for parameters + * -7 is for command names (from cmdnamtab) + * -4 is for a cdable parameter + * -3 is for executable command names. + * -2 is for anything unquoted + * -1 is for other file specifications + * (things with `~' of `=' at the beginning, ...). + */ + + /* Just to make the code cleaner */ + hn = (HashNode) t; + pm = (Param) t; + + if (!addwhat) { + test = 1; + } else if (addwhat == -1 || addwhat == -5 || addwhat == -6 || + addwhat == CC_FILES || addwhat == -7 || addwhat == -8) { + if (sl < fpl + fsl) + return; + + if ((addwhat == CC_FILES || + addwhat == -5) && !*psuf && !*fsuf) { + /* If this is a filename, do the fignore check. */ + char **pt = fignore; + int filell; + + for (test = 1; test && *pt; pt++) + if ((filell = strlen(*pt)) < sl + && !strcmp(*pt, s + sl - filell)) + test = 0; + + if (!test) { + l = fmatches; + ai = fainfo; + } + } + pl = fpl; + if (addwhat == -5 || addwhat == -8) { + test = 1; + cp = filecomp; + cc = cp || ispattern; + e = s + sl - fsl; + mpl = fpl; msl = fsl; + } else { + if ((cp = filecomp)) { + if ((test = domatch(s, filecomp, 0))) + cc = 1; + } else { + e = s + sl - fsl; + if ((test = !strncmp(s, fpre, fpl))) + if ((test = !strcmp(e, fsuf))) { + mpl = fpl; msl = fsl; + } + if (!test && mstack && + (ms = comp_match(fpre, fsuf, s, &lc, + (addwhat == CC_FILES || + addwhat == -6), &bpl, &bsl))) + test = 1; + if (ispattern) + cc = 1; + } + } + if (test) { + fc = NULL; + if (addwhat == -7 && !(fc = findcmd(s))) + return; + if (fc) + zsfree(fc); + isf = CMF_FILE; + + if (addwhat == CC_FILES || addwhat == -6 || + addwhat == -5 || addwhat == -8) { + te = s + pl; + s = quotename(s, &e, te, &pl); + sl = strlen(s); + } else if (!cc) { + s = dupstring(t = s); + e += s - t; + } + if (cc) { + tt = (char *)halloc(lppl + lpsl + sl + 1); + tt[0] = '\0'; + if (lppre) + strcpy(tt, lppre); + strcat(tt, s); + if (lpsuf) + strcat(tt, lpsuf); + untokenize(s = tt); + sl = strlen(s); + } + } + } else if (addwhat == CC_QUOTEFLAG || addwhat == -2 || + (addwhat == -3 && !(hn->flags & DISABLED)) || + (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) && + (tt = pm->gets.cfn(pm)) && *tt == '/') || + (addwhat == -9 && !(hn->flags & PM_UNSET)) || + (addwhat > 0 && + ((!(hn->flags & PM_UNSET) && + (((addwhat & CC_ARRAYS) && (hn->flags & PM_ARRAY)) || + ((addwhat & CC_INTVARS) && (hn->flags & PM_INTEGER)) || + ((addwhat & CC_ENVVARS) && (hn->flags & PM_EXPORTED)) || + ((addwhat & CC_SCALARS) && (hn->flags & PM_SCALAR)) || + ((addwhat & CC_READONLYS) && (hn->flags & PM_READONLY)) || + ((addwhat & CC_SPECIALS) && (hn->flags & PM_SPECIAL)) || + ((addwhat & CC_PARAMS) && !(hn->flags & PM_EXPORTED)))) || + ((( addwhat & CC_SHFUNCS) || + ( addwhat & CC_BUILTINS) || + ( addwhat & CC_EXTCMDS) || + ( addwhat & CC_RESWDS) || + ((addwhat & CC_ALREG) && !(hn->flags & ALIAS_GLOBAL)) || + ((addwhat & CC_ALGLOB) && (hn->flags & ALIAS_GLOBAL))) && + (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) || + ((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) || + ((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) { + if (sl >= rpl + rsl || mstack) { + if (cp) + test = domatch(s, patcomp, 0); + else { + e = s + sl - rsl; + if ((test = !strncmp(s, rpre, rpl))) + if ((test = !strcmp(e, rsuf))) { + mpl = rpl; msl = rsl; + } + if (!test && mstack && + (ms = comp_match(rpre, rsuf, s, &lc, + (addwhat == CC_QUOTEFLAG), &bpl, &bsl))) + test = 1; + } + } + if (!test && sl < lpl + lsl && !mstack) + return; + if (!test && lpre && lsuf) { + e = s + sl - lsl; + if ((test = !strncmp(s, lpre, lpl))) + if ((test = !strcmp(e, lsuf))) { + mpl = lpl; msl = lsl; + } + if (!test && mstack && + (ms = comp_match(lpre, lsuf, s, &lc, + (addwhat == CC_QUOTEFLAG), &bpl, &bsl))) + test = 1; + pl = lpl; + } + if (addwhat == CC_QUOTEFLAG) { + te = s + pl; + s = quotename(s, &e, te, &pl); + sl = strlen(s); + } + } + if (!test) + return; + + if (!ms && !ispattern && ai->firstm) { + if ((test = sl - pfxlen(ai->firstm->str, s)) < ai->prerest) + ai->prerest = test; + if ((test = sfxlen(ai->firstm->str, s)) < ai->suflen) + ai->suflen = test; + } + + /* Generate the common -P prefix. */ + + if (ai->pprefix) { + if (curcc->prefix) + ai->pprefix[pfxlen(ai->pprefix, curcc->prefix)] = '\0'; + else + ai->pprefix[0] = '\0'; + } else + ai->pprefix = dupstring(curcc->prefix ? curcc->prefix : ""); + + /* Generate the prefix to insert for ambiguous completions. */ + t = s; + if (lppre) + t = dyncat(lppre, t); + if (ipre && *ipre) { + Cline tlc = prepend_cline(ipre, lc); + + ai->noipre = 0; + if (!ms) { + ai->icpl = lppl + mpl; + ai->icsl = lpsl + msl; + if (ai->iaprefix) + ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0'; + else + ai->iaprefix = dupstring(t); + } + else + ai->ilinecl = join_clines(ai->ilinecl, lc); + if (ai->iprefix) { + if (strcmp(ipre, ai->iprefix)) + ai->iprefix = ""; + } else + ai->iprefix = dupstring(ipre); + + t = dyncat(ipre, t); + lc = tlc; + } else + ai->iprefix = ""; + + if (!ms) { + ai->cpl = lppl + mpl; + ai->csl = lpsl + msl; + if (ai->aprefix) + ai->aprefix[pfxlen(ai->aprefix, t)] = '\0'; + else + ai->aprefix = dupstring(t); + } + else + ai->linecl = join_clines(ai->linecl, lc); + + mnum++; + ai->count++; + + /* Allocate and fill the match structure. */ + cm = (Cmatch) halloc(sizeof(struct cmatch)); + if (ispattern) { + if (lpsuf && *lpsuf && strsfx(lpsuf, s)) { + s[sl - lpsl] = '\0'; + cm->psuf = lpsuf; + } + else + cm->psuf = NULL; + + if (lppre && *lppre && strpfx(lppre, s)) { + s += lppl; + cm->ppre = lppre; + cm->prpre = (isf && prpre && *prpre ? prpre : NULL); + } + else + cm->ppre = cm->prpre = NULL; + } + else { + cm->ppre = (lppre && *lppre ? lppre : NULL); + cm->psuf = (lpsuf && *lpsuf ? lpsuf : NULL); + cm->prpre = (isf && prpre && *prpre ? prpre : NULL); + } + cm->str = (ms ? ms : s); + cm->ipre = (ipre && *ipre ? ipre : NULL); + cm->ripre = (ripre && *ripre ? ripre : NULL); + cm->pre = curcc->prefix; + cm->suf = curcc->suffix; + cm->flags = mflags | isf; + cm->brpl = bpl; + cm->brsl = bsl; + addlinknode(l, cm); + + /* One more match for this explanation. */ + if (expl) { + if (l == matches) + expl->count++; + else + expl->fcount++; + } + if (!ms) { + if (!ai->firstm) + ai->firstm = cm; + + /* Do we have an exact match? More than one? */ + if (!ispattern && !(e - (s + pl))) { + if (!ai->exact) + ai->exact = 1; + else { + ai->exact = 2; + cm = NULL; + } + ai->exactm = cm; + } + } +} + +#ifdef HAVE_NIS_PLUS +static int +match_username(nis_name table, nis_object *object, void *userdata) +{ + if (errflag) return 1; else { static char buf[40]; @@ -1628,6 +2690,9 @@ maketildelist(void) cb.foreach = (int (*)()) match_username; cb.data = (char *)&data; yp_all(domain, PASSWD_MAP, &cb); +/* for (n = firstnode(matches); n; incnode(n)) + if (getpwnam(getdata(n)) == NULL) + uremnode(matches, n);*/ } # else /* HAVE_NIS_PLUS */ /* Maybe we should turn this string into a #define'd constant...? */ @@ -1661,25 +2726,6 @@ maketildelist(void) addhnmatch, 0); } -/* Copy the given string and remove backslashes from the copy and return it. */ - -/**/ -static char * -rembslash(char *s) -{ - char *t = s = dupstring(s); - - while (*s) - if (*s == '\\') { - chuck(s); - if (*s) - s++; - } else - s++; - - return t; -} - /* This does the check for compctl -x `n' and `N' patterns. */ /**/ @@ -1716,265 +2762,14 @@ getcpat(char *wrd, int cpatindex, char *cpat, int class) if (!class && !*p && !--cpatindex) { zsfree(str); t += wrd - str; - for (d = 0; --t >= wrd;) - if (! INULL(*t)) - d++; - return d; - } - } - zsfree(str); - return -1; -} - -/* This holds a pointer to the compctl we are using. */ - -static Compctl ccmain; - - -/* Find the compctl to use and return it. The first argument gives a * - * compctl to start searching with (if it is zero, the hash table is * - * searched). compadd is used to return a number of characters that * - * should be ignored at the beginning of the word and incmd is * - * non-zero if we are in command position. */ - -/**/ -static Compctl -get_ccompctl(Compctl occ, int *compadd, int incmd) -{ - Compctl compc, ret; - Compctlp ccp; - int t, i, a, b, tt, ra, rb, j, isf = 1; - Compcond or, cc; - char *s, *ss, *sc, *cmd = dupstring(cmdstr); - Comp comp; - - first_rec: - *compadd = 0; - ra = 0; - rb = clwnum - 1; - sc = NULL; - - if (!(ret = compc = occ)) { - if (isf) { - isf = 0; - ret = &cc_first; - } - else if (inwhat == IN_ENV) - /* Default completion for parameter values. */ - ret = &cc_default; - else if (inwhat == IN_MATH) { - /* Parameter names inside mathematical expression. */ - cc_dummy.mask = CC_PARAMS; - ret = &cc_dummy; - cc_dummy.refc = 10000; - } else if (inwhat == IN_COND) { - /* We try to be clever here: in conditions we complete option * - * names after a `-o', file names after `-nt', `-ot', and `-ef' * - * and file names and parameter names elsewhere. */ - s = clwpos ? clwords[clwpos - 1] : ""; - cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS : - ((*s == '-' && s[1] && !s[2]) || - !strcmp("-nt", s) || - !strcmp("-ot", s) || - !strcmp("-ef", s)) ? CC_FILES : - (CC_FILES | CC_PARAMS); - ret = &cc_dummy; - cc_dummy.refc = 10000; - } else if (incmd) - ret = &cc_compos; - /* And in redirections or if there is no command name (and we are * - * not in command position) or if no special compctl was given * - * for the command: use default completion. Note that we first * - * search the complete command name and than the trailing * - * pathname component. */ - else if (linredir || - !(cmd && - (((ccp = (Compctlp) compctltab->getnode(compctltab, cmd)) && - (compc = ret = ccp->cc)) || - ((s = dupstring(cmd)) && remlpaths(&s) && - (ccp = (Compctlp) compctltab->getnode(compctltab, s)) && - (compc = ret = ccp->cc))))) - ret = &cc_default; - - ccmain = compc = ret; - ccmain->refc++; - } - /* The compctl we found has extended completion patterns, check them. */ - if (compc && compc->ext) { - compc = compc->ext; - /* This loops over the patterns separated by `--'. */ - for (t = 0; compc && !t; compc = compc->next) { - /* This loops over OR'ed patterns. */ - for (cc = compc->cond; cc && !t; cc = or) { - or = cc->or; - /* This loops over AND'ed patterns. */ - for (t = 1; cc && t; cc = cc->and) { - /* And this loops of [...] pairs. */ - for (t = i = 0; i < cc->n && !t; i++) { - s = NULL; - ra = 0; - rb = clwnum - 1; - switch (cc->type) { - case CCT_POS: - tt = clwpos; - goto cct_num; - case CCT_NUMWORDS: - tt = clwnum; - cct_num: - if ((a = cc->u.r.a[i]) < 0) - a += clwnum; - if ((b = cc->u.r.b[i]) < 0) - b += clwnum; - if (cc->type == CCT_POS) - ra = a, rb = b; - t = (tt >= a && tt <= b); - break; - case CCT_CURSUF: - case CCT_CURPRE: - s = ztrdup(clwpos < clwnum ? clwords[clwpos] : ""); - untokenize(s); - sc = rembslash(cc->u.s.s[i]); - a = strlen(sc); - if (!strncmp(s, sc, a)) { - *compadd = (cc->type == CCT_CURSUF ? a : 0); - t = 1; - } - break; - case CCT_CURSUB: - case CCT_CURSUBC: - if (clwpos < 0 || clwpos > clwnum) - t = 0; - else { - a = getcpat(clwords[clwpos], - cc->u.s.p[i], - cc->u.s.s[i], - cc->type == CCT_CURSUBC); - if (a != -1) - *compadd = a, t = 1; - } - break; - - case CCT_CURPAT: - case CCT_CURSTR: - tt = clwpos; - goto cct_str; - case CCT_WORDPAT: - case CCT_WORDSTR: - tt = 0; - cct_str: - if ((a = tt + cc->u.s.p[i]) < 0) - a += clwnum; - s = ztrdup((a < 0 || a >= clwnum) ? "" : - clwords[a]); - untokenize(s); - - if (cc->type == CCT_CURPAT || - cc->type == CCT_WORDPAT) { - tokenize(ss = dupstring(cc->u.s.s[i])); - t = ((comp = parsereg(ss)) && - domatch(s, comp, 0)); - } else - t = (!strcmp(s, rembslash(cc->u.s.s[i]))); - break; - case CCT_RANGESTR: - case CCT_RANGEPAT: - if (cc->type == CCT_RANGEPAT) - tokenize(sc = dupstring(cc->u.l.a[i])); - for (j = clwpos; j; j--) { - untokenize(s = ztrdup(clwords[j])); - if (cc->type == CCT_RANGESTR) - sc = rembslash(cc->u.l.a[i]); - if (cc->type == CCT_RANGESTR ? - !strncmp(s, sc, strlen(sc)) : - ((comp = parsereg(sc)) && - domatch(s, comp, 0))) { - zsfree(s); - ra = j + 1; - t = 1; - break; - } - zsfree(s); - } - if (t) { - if (cc->type == CCT_RANGEPAT) - tokenize(sc = dupstring(cc->u.l.b[i])); - for (j++; j < clwnum; j++) { - untokenize(s = ztrdup(clwords[j])); - if (cc->type == CCT_RANGESTR) - sc = rembslash(cc->u.l.b[i]); - if (cc->type == CCT_RANGESTR ? - !strncmp(s, sc, strlen(sc)) : - ((comp = parsereg(sc)) && - domatch(s, comp, 0))) { - zsfree(s); - rb = j - 1; - t = clwpos <= rb; - break; - } - zsfree(s); - } - } - s = NULL; - } - zsfree(s); - } - } - } - if (t) - break; - } - if (compc) - /* We found a matching pattern, we may return it. */ - ret = compc; - } - if (ret->subcmd) { - /* The thing we want to return has a subcmd flag (-l). */ - char **ow = clwords, *os = cmdstr, *ops = NULL; - int oldn = clwnum, oldp = clwpos; - - /* So we restrict the words-array. */ - if (ra >= clwnum) - ra = clwnum - 1; - if (ra < 1) - ra = 1; - if (rb >= clwnum) - rb = clwnum - 1; - if (rb < 1) - rb = 1; - clwnum = rb - ra + 1; - clwpos = clwpos - ra; - - if (ret->subcmd[0]) { - /* And probably put the command name given to the flag * - * in the array. */ - clwpos++; - clwnum++; - incmd = 0; - ops = clwords[ra - 1]; - clwords[ra - 1] = cmdstr = ret->subcmd; - clwords += ra - 1; - } else { - cmdstr = clwords[ra]; - incmd = !clwpos; - clwords += ra; + for (d = 0; --t >= wrd;) + if (! INULL(*t)) + d++; + return d; } - *compadd = 0; - if (ccmain != &cc_dummy) - freecompctl(ccmain); - /* Then we call this function recursively. */ - - ret = get_ccompctl(NULL, compadd, incmd); - /* And restore the things we changed. */ - clwords = ow; - cmdstr = os; - clwnum = oldn; - clwpos = oldp; - if (ops) - clwords[ra - 1] = ops; } - if (ret == &cc_first) - goto first_rec; - return ret; + zsfree(str); + return -1; } /* Dump a hash table (without sorting). For each element the addmatch * @@ -2021,10 +2816,10 @@ getreal(char *str) prefork(l, 0); noerrs = ne; if (!errflag && nonempty(l)) - return ztrdup(peekfirst(l)); + return dupstring(peekfirst(l)); errflag = 0; - return ztrdup(str); + return dupstring(str); } /* This reads a directory and adds the files to the list of * @@ -2040,7 +2835,6 @@ gen_matches_files(int dirs, int execs, int all) LinkList l = NULL; int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat; - addwhat = execs ? -8 : -5; opts[NULLGLOB] = 1; if (*psuf) { @@ -2066,6 +2860,7 @@ gen_matches_files(int dirs, int execs, int all) /* Ignore files beginning with `.' unless the thing we found on * * the command line also starts with a dot or GLOBDOTS is set. */ if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) { + addwhat = execs ? -8 : -5; if (filecomp) /* If we have a pattern for the filename check, use it. */ test = domatch(n, filecomp, 0); @@ -2074,6 +2869,10 @@ gen_matches_files(int dirs, int execs, int all) e = n + strlen(n) - fsl; if ((test = !strncmp(n, fpre, fpl))) test = !strcmp(e, fsuf); + if (!test && mstack) { + test = 1; + addwhat = CC_FILES; + } } /* Filename didn't match? */ if (!test) @@ -2136,45 +2935,17 @@ gen_matches_files(int dirs, int execs, int all) addwhat = aw; } -/* This holds the explanation string we have to print. */ - -static char *expl; - -/* This holds the suffix to add (given with compctl -S). */ - -static char *ccsuffix; - -/* This s non-zero if the compctl -q flag was given (the suffix should * - * be removed when a space or something like that is typed next). */ - -static int remsuffix; - -/**/ -static void -quotepresuf(char **ps) -{ - if (*ps) { - char *p = quotename(*ps, NULL, NULL, NULL); - - if (p != *ps) { - zsfree(*ps); - *ps = ztrdup(p); - } - } -} - /**/ static void -docompletion(char *s, int lst, int incmd, int untokenized) +docompletion(char *s, int lst, int incmd) { - static int delit, compadd; - - fixsuffix(); HEAPALLOC { pushheap(); + ainfo = fainfo = NULL; + /* Make sure we have the completion list and compctl. */ - if(makecomplist(s, incmd, &delit, &compadd, untokenized)) { + if (makecomplist(s, incmd)) { /* Error condition: feeeeeeeeeeeeep(). */ feep(); goto compend; @@ -2185,42 +2956,51 @@ docompletion(char *s, int lst, int incmd, int untokenized) showinglist = -2; else { /* We have matches. */ - if (delit) { - /* If we have to delete the word from the command line, * - * do it now. */ - wb -= compadd; - strcpy((char *)line + wb, (char *)line + we); - we = cs = wb; - } if (nmatches > 1) /* There are more than one match. */ do_ambiguous(); + else if (nmatches == 1) { /* Only one match. */ - do_single(amatches[0]); + do_single(amatches->matches[0]); invalidatelist(); } } - /* Print the explanation string if needed. */ - if (!showinglist && expl && nmatches != 1) { - int up; + /* Print the explanation strings if needed. */ + if (!showinglist && validlist && nmatches != 1) { + Cmgroup g = amatches; + Cexpl *e; + int up = 0, tr = 1; if (!nmatches) feep(); - trashzle(); - clearflag = (isset(USEZLE) && !termflags && - (isset(ALWAYSLASTPROMPT) && zmult == 1)) || - (unset(ALWAYSLASTPROMPT) && zmult != 1); - - up = printfmt(expl, nmatches, 1); + while (g) { + if ((e = g->expls)) + while (*e) { + if ((*e)->count) { + if (tr) { + trashzle(); + tr = 0; + } + up += printfmt((*e)->str, (*e)->count, 1); + } + e++; + } + g = g->next; + } + if (!tr) { + clearflag = ((isset(USEZLE) && !termflags && + (isset(ALWAYSLASTPROMPT) && zmult == 1)) || + (unset(ALWAYSLASTPROMPT) && zmult != 1)); - if (clearflag) - tcmultout(TCUP, TCMULTUP, up + nlnct); - else - putc('\n', shout); - fflush(shout); + if (clearflag && up + nlnct < lines) + tcmultout(TCUP, TCMULTUP, up + nlnct); + else + putc('\n', shout); + fflush(shout); + } } compend: ll = strlen((char *)line); @@ -2230,6 +3010,14 @@ docompletion(char *s, int lst, int incmd, int untokenized) } LASTALLOC; } +/* The beginning and end of a word range to be used by -l. */ + +static int brange, erange; + +/* This is used to detect when and what to continue. */ + +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 * @@ -2237,63 +3025,520 @@ docompletion(char *s, int lst, int incmd, int untokenized) * 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. delit and compadd return information * - * about bits of the command line that need to be deleted. */ + * returns non-zero on error. */ /**/ static int -makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) +makecomplist(char *s, int incmd) { - Compctl cc = NULL; - int oloffs = offs, owe = we, owb = wb, ocs = cs, oll = ll, isf = 1; - int t, sf1, sf2, ooffs; - char *p, *sd = NULL, *tt, *s1, *s2, *os = NULL; - unsigned char *ol = NULL; + struct cmlist ms; + Cmlist m = cmatcher; /* If we already have a list from a previous execution of this * * function, skip the list building code. */ if (validlist) return !nmatches; - os = dupstring(s); - ol = (unsigned char *)dupstring((char *)line); + for (;;) { + if (m) { + ms.next = NULL; + ms.matcher = m->matcher; + mstack = &ms; + } + ainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); + fainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); + + ainfo->prerest = ainfo->suflen = + fainfo->prerest = fainfo->suflen = 10000; + ainfo->noipre = fainfo->noipre= 1; + + freecl = NULL; + + lastambig = 0; + amatches = 0; + mnum = 0; + begcmgroup("default", 0); + + ccused = newlinklist(); + ccstack = newlinklist(); + + makecomplistglobal(s, incmd); + + endcmgroup(NULL); + + if (amatches) + amatches->ccs = (Compctl *) makearray(ccused, 0, + &(amatches->ccount), NULL); + else { + LinkNode n; + + for (n = firstnode(ccused); n; incnode(n)) + freecompctl((Compctl) getdata(n)); + } + + PERMALLOC { + permmatches(); + } LASTALLOC; + + if (nmatches && !errflag) { + validlist = 1; + + return 0; + } + if (!m || !(m = m->next)) + break; + + errflag = 0; + } + return 1; +} + +/* This function gets the compctls for the given command line and * + * adds all completions for them. */ + +/**/ +static void +makecomplistglobal(char *os, int incmd) +{ + Compctl cc; + char *s; + + if (inwhat == IN_ENV) + /* Default completion for parameter values. */ + cc = &cc_default; + else if (inwhat == IN_MATH) { + /* Parameter names inside mathematical expression. */ + cc_dummy.mask = CC_PARAMS; + cc = &cc_dummy; + cc_dummy.refc = 10000; + } else if (inwhat == IN_COND) { + /* We try to be clever here: in conditions we complete option * + * names after a `-o', file names after `-nt', `-ot', and `-ef' * + * and file names and parameter names elsewhere. */ + s = clwpos ? clwords[clwpos - 1] : ""; + cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS : + ((*s == '-' && s[1] && !s[2]) || + !strcmp("-nt", s) || + !strcmp("-ot", s) || + !strcmp("-ef", s)) ? CC_FILES : + (CC_FILES | CC_PARAMS); + cc = &cc_dummy; + cc_dummy.refc = 10000; + } else if (linredir) + /* In redirections use default completion. */ + cc = &cc_default; + else { + /* Otherwise get the matches for the command. */ + makecomplistcmd(os, incmd); + cc = NULL; + } + if (cc) { + /* First, use the -T compctl. */ + makecomplistcc(&cc_first, os, incmd); + + if (!(ccont & CC_CCCONT)) + return; + + makecomplistcc(cc, os, incmd); + } +} + +/* This produces the matches for a command. */ + +/**/ +static void +makecomplistcmd(char *os, int incmd) +{ + Compctl cc; + Compctlp ccp; + char *s; + + /* First, use the -T compctl. */ + makecomplistcc(&cc_first, os, incmd); + + if (!(ccont & CC_CCCONT)) + return; + + /* Then search the pattern compctls, with the command name and the * + * full pathname of the command. */ + makecomplistpc(os, incmd); + if (!(ccont & CC_CCCONT)) + return; + + /* If the command string starts with `=', try the path name of the * + * command. */ + if (cmdstr && cmdstr[0] == Equals) { + char *c = findcmd(cmdstr + 1); + + if (c) { + zsfree(cmdstr); + cmdstr = ztrdup(c); + } + } + + /* Find the compctl for this command, trying the full name and then * + * the trailing pathname component. If that doesn't yield anything, * + * use default completion. */ + if (incmd) + cc = &cc_compos; + else if (!(cmdstr && + (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) && + (cc = ccp->cc)) || + ((s = dupstring(cmdstr)) && remlpaths(&s) && + (ccp = (Compctlp) compctltab->getnode(compctltab, s)) && + (cc = ccp->cc))))) + cc = &cc_default; + + makecomplistcc(cc, os, incmd); +} + +/* This add the matches for the pattern compctls. */ + +/**/ +static void +makecomplistpc(char *os, int incmd) +{ + Patcomp pc; + Comp pat; + char *s = findcmd(cmdstr); + + for (pc = patcomps; pc; pc = pc->next) { + if ((pat = parsereg(pc->pat)) && + (domatch(cmdstr, pat, 0) || + (s && domatch(s, pat, 0)))) { + makecomplistcc(pc->cc, os, incmd); + if (!(ccont & CC_CCCONT)) + return; + } + } +} + +/* This produces the matches for one compctl. */ + +/**/ +static void +makecomplistcc(Compctl cc, char *s, int incmd) +{ + cc->refc++; + addlinknode(ccused, cc); + + ccont = 0; + + makecomplistor(cc, s, incmd, 0, 0); +} + +/* This adds the completions for one run of [x]or'ed completions. */ + +/**/ +static void +makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub) +{ + int mn, ct, um = usemenu; + + /* Loop over xors. */ + do { + mn = mnum; + + /* Loop over ors. */ + do { + /* Reset the range information if we are not in a sub-list. */ + if (!sub) { + brange = 0; + erange = clwnum - 1; + } + usemenu = 0; + makecomplistlist(cc, s, incmd, compadd); + um |= usemenu; + + ct = cc->mask2 & CC_XORCONT; + + cc = cc->xor; + } while (cc && ct); + + /* Stop if we got some matches. */ + if (mn != mnum) + break; + if (cc) { + ccont &= ~(CC_DEFCONT | CC_PATCONT); + if (!sub) + ccont &= ~CC_CCCONT; + } + } while (cc); + + usemenu = um; +} + +/* This dispatches for simple and extended completion. */ + +/**/ +static void +makecomplistlist(Compctl cc, char *s, int incmd, int compadd) +{ + int oloffs = offs, owe = we, owb = wb, ocs = cs; + + if (cc->ext) + /* Handle extended completion. */ + makecomplistext(cc, s, incmd); + else + /* Only normal flags. */ + makecomplistflags(cc, s, incmd, compadd); + + /* Reset some information variables for the next try. */ + errflag = 0; + offs = oloffs; + wb = owb; + we = owe; + cs = ocs; +} + +/* This add matches for extended completion patterns */ + +/**/ +static void +makecomplistext(Compctl occ, char *os, int incmd) +{ + Compctl compc; + Compcond or, cc; + Comp comp; + int compadd, m = 0, d = 0, t, tt, i, j, a, b; + char *sc, *s, *ss; + + /* This loops over the patterns separated by `-'s. */ + for (compc = occ->ext; compc; compc = compc->next) { + compadd = t = brange = 0; + erange = clwnum - 1; + /* This loops over OR'ed patterns. */ + for (cc = compc->cond; cc && !t; cc = or) { + or = cc->or; + /* This loops over AND'ed patterns. */ + for (t = 1; cc && t; cc = cc->and) { + /* And this loops over [...] pairs. */ + for (t = i = 0; i < cc->n && !t; i++) { + s = NULL; + brange = 0; + erange = clwnum - 1; + switch (cc->type) { + case CCT_POS: + tt = clwpos; + goto cct_num; + case CCT_NUMWORDS: + tt = clwnum; + cct_num: + if ((a = cc->u.r.a[i]) < 0) + a += clwnum; + if ((b = cc->u.r.b[i]) < 0) + b += clwnum; + if (cc->type == CCT_POS) + brange = a, erange = b; + t = (tt >= a && tt <= b); + break; + case CCT_CURSUF: + case CCT_CURPRE: + s = ztrdup(clwpos < clwnum ? clwords[clwpos] : ""); + untokenize(s); + sc = rembslash(cc->u.s.s[i]); + a = strlen(sc); + if (!strncmp(s, sc, a)) { + compadd = (cc->type == CCT_CURSUF ? a : 0); + t = 1; + } + break; + case CCT_CURSUB: + case CCT_CURSUBC: + if (clwpos < 0 || clwpos > clwnum) + t = 0; + else { + a = getcpat(clwords[clwpos], + cc->u.s.p[i], + cc->u.s.s[i], + cc->type == CCT_CURSUBC); + if (a != -1) + compadd = a, t = 1; + } + break; + + case CCT_CURPAT: + case CCT_CURSTR: + tt = clwpos; + goto cct_str; + case CCT_WORDPAT: + case CCT_WORDSTR: + tt = 0; + cct_str: + if ((a = tt + cc->u.s.p[i]) < 0) + a += clwnum; + s = ztrdup((a < 0 || a >= clwnum) ? "" : + clwords[a]); + untokenize(s); + + if (cc->type == CCT_CURPAT || + cc->type == CCT_WORDPAT) { + tokenize(ss = dupstring(cc->u.s.s[i])); + t = ((comp = parsereg(ss)) && + domatch(s, comp, 0)); + } else + t = (!strcmp(s, rembslash(cc->u.s.s[i]))); + break; + case CCT_RANGESTR: + case CCT_RANGEPAT: + if (cc->type == CCT_RANGEPAT) + tokenize(sc = dupstring(cc->u.l.a[i])); + for (j = clwpos; j; j--) { + untokenize(s = ztrdup(clwords[j])); + if (cc->type == CCT_RANGESTR) + sc = rembslash(cc->u.l.a[i]); + if (cc->type == CCT_RANGESTR ? + !strncmp(s, sc, strlen(sc)) : + ((comp = parsereg(sc)) && + domatch(s, comp, 0))) { + zsfree(s); + brange = j + 1; + t = 1; + break; + } + zsfree(s); + } + if (t && cc->u.l.b[i]) { + if (cc->type == CCT_RANGEPAT) + tokenize(sc = dupstring(cc->u.l.b[i])); + for (j++; j < clwnum; j++) { + untokenize(s = ztrdup(clwords[j])); + if (cc->type == CCT_RANGESTR) + sc = rembslash(cc->u.l.b[i]); + if (cc->type == CCT_RANGESTR ? + !strncmp(s, sc, strlen(sc)) : + ((comp = parsereg(sc)) && + domatch(s, comp, 0))) { + zsfree(s); + erange = j - 1; + t = clwpos <= erange; + break; + } + zsfree(s); + } + } + s = NULL; + } + zsfree(s); + } + } + } + if (t) { + /* The patterns matched, use the flags. */ + m = 1; + ccont &= ~(CC_PATCONT | CC_DEFCONT); + makecomplistor(compc, os, incmd, compadd, 1); + if (!d && (ccont & CC_DEFCONT)) { + d = 1; + compadd = 0; + brange = 0; + erange = clwnum - 1; + makecomplistflags(occ, os, incmd, 0); + } + if (!(ccont & CC_PATCONT)) + break; + } + } + /* If no pattern matched, use the standard flags. */ + if (!m) { + compadd = 0; + brange = 0; + erange = clwnum - 1; + makecomplistflags(occ, os, incmd, 0); + } +} + +/* This returns the node with the given data. */ +/* ...should probably be moved to linklist.c. */ + +static LinkNode +findnode(LinkList list, void *dat) +{ + LinkNode tmp = list->first; + + while (tmp && tmp->dat != dat) tmp = tmp->next; + + return tmp; +} + +/* This adds the completions for the flags in the given compctl. */ + +/**/ +static void +makecomplistflags(Compctl cc, char *s, int incmd, int compadd) +{ + int t, sf1, sf2, ooffs, um = usemenu, delit, ispar = 0; + char *p, *sd = NULL, *tt, *s1, *s2, *os = dupstring(s); + struct cmlist ms; - xorrec: + ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT)); + + if (findnode(ccstack, cc)) + return; - DPUTS(ll != strlen((char *) line), "BUG: xorrec: ll != strlen(line)"); + addlinknode(ccstack, cc); + if (allccs) { + if (findnode(allccs, cc)) { + uremnode(ccstack, firstnode(ccstack)); + return; + } + addlinknode(allccs, cc); + } /* Go to the end of the word if complete_in_word is not set. */ if (unset(COMPLETEINWORD) && cs != we) cs = we, offs = strlen(s); - ispattern = haswhat = lastambig = 0; + s = dupstring(s); + delit = ispattern = 0; + usemenu = um; patcomp = filecomp = NULL; menucur = NULL; - shortest = NULL; - fshortest = NULL; - rpre = rsuf = lpre = lsuf = ppre = psuf = prpre = - fpre = fsuf = firstm = ffirstm = parampre = qparampre = NULL; - - /* Blank out the lists. */ - matches = newlinklist(); - fmatches = newlinklist(); + rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf = prpre = + fpre = fsuf = ipre = ripre = prpre = NULL; - /* If we don't have a compctl definition yet or we have a compctl * - * with extended completion, get it (or the next one, resp.). */ - if (!cc || cc->ext) - cc = get_ccompctl(cc, compadd, incmd); + curcc = cc; - /* *compadd is the number of characters we have to ignore at the * + mflags = 0; + if (cc->ylist || cc->gname) { + endcmgroup(NULL); + begcmgroup((cc->ylist ? NULL : cc->gname), cc->mask2 & CC_NOSORT); + } + if (cc->mask & CC_REMOVE) + mflags |= CMF_REMOVE; + if (cc->mask2 & CC_NOSORT) + mgroup->flags |= CGF_NOSORT; + if (cc->explain) { + expl = (Cexpl) halloc(sizeof(struct cexpl)); + expl->count = expl->fcount = 0; + } + else + expl = NULL; + /* compadd is the number of characters we have to ignore at the * * beginning of the word. */ - wb += *compadd; - s += *compadd; - if ((offs -= *compadd) < 0) - /* It's bigger than our word prefix, so we can't help here... */ - return 1; + if (compadd) { + ipre = dupstring(s); + ipre[compadd] = '\0'; + untokenize(ipre); + wb += compadd; + s += compadd; + if ((offs -= compadd) < 0) { + /* It's bigger than our word prefix, so we can't help here... */ + uremnode(ccstack, firstnode(ccstack)); + return; + } + } + else + ipre = NULL; + if (cc->matcher) { + ms.next = mstack; + ms.matcher = cc->matcher; + mstack = &ms; + } /* Insert the prefix (compctl -P), if any. */ if (cc->prefix) { - int pl = 0, sl = strlen(cc->prefix); + int pl = 0; if (*s) { /* First find out how much of the prefix is already on the line. */ @@ -2301,23 +3546,13 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) untokenize(sd); pl = pfxlen(cc->prefix, sd); s += pl; + sd += pl; + offs -= pl; } - if (pl < sl) { - int savecs = cs; - - /* Then insert the prefix. */ - cs = wb + pl; - inststrlen(cc->prefix + pl, 0, sl - pl); - cs = savecs + sl - pl; - } - /* And adjust the word beginning/end variables. */ - wb += sl; - we += sl - pl; - offs -= pl; } /* Does this compctl have a suffix (compctl -S)? */ - if ((ccsuffix = cc->suffix) && *ccsuffix) { - char *sdup = dupstring(ccsuffix); + if (cc->suffix) { + char *sdup = dupstring(cc->suffix); int sl = strlen(sdup), suffixll; /* Ignore trailing spaces. */ @@ -2331,11 +3566,8 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) /* If the suffix is already there, ignore it (and don't add * * it again). */ if (*sd && (suffixll = strlen(sd)) >= sl && - offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl)) { - ccsuffix = NULL; - haswhat |= HAS_SUFFIX; + offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl)) s[suffixll - sl] = '\0'; - } } /* Do we have one of the special characters `~' and `=' at the beginning? */ if ((ic = *s) != Tilde && ic != Equals) @@ -2389,20 +3621,25 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) /* Now make sure that the cursor is inside the name. */ if (offs <= e - s && offs >= b - s && n <= 0) { /* It is. */ - parambr = br - 1; + if (br >= 2) + mflags |= CMF_PARBR; + /* Get the prefix (anything up to the character before the name). */ + lpsuf = dupstring(quotename(e, NULL, NULL, NULL)); *e = '\0'; - parampre = ztrduppfx(s, b - s); - qparampre = ztrdup(quotename(parampre, NULL, NULL, NULL)); - untokenize(qparampre); - qparprelen = strlen(qparampre); + lpsl = strlen(lpsuf); + ripre = dupstring(s); + ripre[b - s] = '\0'; + ipre = dupstring(quotename(ripre, NULL, NULL, NULL)); + untokenize(ipre); + ispar = 1; /* And adjust wb, we, and offs again. */ offs -= b - s; wb = cs - offs; we = wb + e - b; s = b; /* And now make sure that we complete parameter names. */ - cc = ccmain = &cc_dummy; + cc = &cc_dummy; cc_dummy.refc = 10000; cc_dummy.mask = CC_PARAMS | CC_ENVVARS; } @@ -2410,38 +3647,19 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) ooffs = offs; /* If we have to ignore the word, do that. */ if (cc->mask & CC_DELETE) { - *delit = 1; + delit = 1; *s = '\0'; offs = 0; - } else - *delit = 0; + if (isset(AUTOMENU)) usemenu = 1; + } /* Compute line prefix/suffix. */ - lpl = offs; - lpre = zalloc(lpl + 1); + lpre = halloc(lpl + 1); memcpy(lpre, s, lpl); lpre[lpl] = '\0'; - p = quotename(lpre, NULL, NULL, NULL); - if (strcmp(p, lpre) && !strpfx(p, qword)) { - int l1, l2; - - backdel(l1 = cs - wb); - untokenize(p); - inststrlen(p, 1, l2 = strlen(p)); - we += l2 - l1; - } - lsuf = ztrdup(s + offs); + lsuf = dupstring(s + offs); lsl = strlen(lsuf); - if (lsl && (p = quotename(lsuf, NULL, NULL, NULL)) && - (strcmp(p, lsuf) && !strsfx(p, qword))) { - int l1, l2; - - foredel(l1 = strlen(s + offs)); - untokenize(p); - inststrlen(p, 0, l2 = strlen(p)); - we += l2 - l1; - } /* First check for ~.../... */ if (ic == Tilde) { @@ -2454,15 +3672,15 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) } /* Compute real prefix/suffix. */ - noreal = !*delit; + noreal = !delit; for (p = lpre; *p && *p != String && *p != Tick; p++); - tt = ic && !parampre ? lpre + 1 : lpre; + tt = ic && !ispar ? lpre + 1 : lpre; rpre = (*p || *lpre == Tilde || *lpre == Equals) ? (noreal = 0, getreal(tt)) : - ztrdup(tt); + dupstring(tt); for (p = lsuf; *p && *p != String && *p != Tick; p++); - rsuf = *p ? (noreal = 0, getreal(lsuf)) : ztrdup(lsuf); + rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf); /* Check if word is a pattern. */ @@ -2523,16 +3741,47 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) /* Compute the path prefix/suffix. */ if (*s1 != '/') - ppre = ztrdup(""); + ppre = ""; else - ppre = ztrduppfx(rpre, s1 - rpre + 1); - psuf = ztrdup(s2); + ppre = dupstrpfx(rpre, s1 - rpre + 1); + psuf = dupstring(s2); + + if (cs != wb) { + char save = line[cs]; + + line[cs] = 0; + lppre = dupstring((char *) (line + wb)); + line[cs] = save; + if ((p = strrchr(lppre, '/'))) { + p[1] = '\0'; + lppl = strlen(lppre); + } else { + lppre = NULL; + lppl = 0; + } + } + else { + lppre = NULL; + lppl = 0; + } + if (cs != we) { + char save = line[we]; + + line[we] = 0; + lpsuf = strchr(dupstring((char *) (line + cs)), '/'); + line[we] = save; + lpsl = (lpsuf ? strlen(lpsuf) : 0); + } + else { + lpsuf = NULL; + lpsl = 0; + } /* And get the file prefix. */ - fpre = ztrdup(((s1 == s || s1 == rpre || ic) && - (*s != '/' || cs == wb)) ? s1 : s1 + 1); + fpre = dupstring(((s1 == s || s1 == rpre || ic) && + (*s != '/' || cs == wb)) ? s1 : s1 + 1); /* And the suffix. */ - fsuf = ztrduppfx(rsuf, s2 - rsuf); + fsuf = dupstrpfx(rsuf, s2 - rsuf); if (useglob && (ispattern & 2)) { int t2; @@ -2593,161 +3842,200 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) if (nonempty(l)) { /* And add the resulting words. */ - haswhat |= HAS_PATHPAT; + mflags |= CMF_FILE; for (n = firstnode(l); n; incnode(n)) addmatch(getdata(n), NULL); + mflags &= !CMF_FILE; } opts[NULLGLOB] = ng; } else { + char **dirs = 0, *ta[2]; + /* No pattern matching. */ addwhat = CC_FILES; - if (cc->withd) { - prpre = tricat(cc->withd, "/", ppre); - } else - prpre = ztrdup(ppre); - if (sf2) - /* We are in the path, so add only directories. */ - gen_matches_files(1, 0, 0); - else { - if (cc->mask & CC_FILES) - /* Add all files. */ - gen_matches_files(0, 0, 1); - else if (cc->mask & CC_COMMPATH) { - /* Completion of command paths. */ - if (sf1 || cc->withd) - /* There is a path prefix, so add * - * directories and executables. */ - gen_matches_files(1, 1, 0); - else { - /* No path prefix, so add the things * - * reachable via the PATH variable. */ - char **pc = path, *pp = prpre; + if (cc->withd) { + char **pp, **npp, *tp; + int tl = strlen(ppre) + 2, pl; + + if ((pp = get_user_var(cc->withd))) { + dirs = npp = + (char**) halloc(sizeof(char *)*(arrlen(pp)+1)); + while (*pp) { + pl = strlen(*pp); + tp = (char *) halloc(strlen(*pp) + tl); + strcpy(tp, *pp); + tp[pl] = '/'; + strcpy(tp + pl + 1, ppre); + *npp++ = tp; + pp++; + } + *npp = '\0'; + } + } + if (!dirs) { + dirs = ta; + if (cc->withd) { + char *tp; + int pl = strlen(cc->withd); + + ta[0] = tp = (char *) halloc(strlen(ppre) + pl + 2); + strcpy(tp, cc->withd); + tp[pl] = '/'; + strcpy(tp + pl + 1, ppre); + } else + ta[0] = ppre; + ta[1] = NULL; + } + while (*dirs) { + prpre = *dirs; - for (; *pc; pc++) - if (!**pc || (pc[0][0] == '.' && !pc[0][1])) - break; - if (*pc) { - prpre = "./"; + if (sf2) + /* We are in the path, so add only directories. */ + gen_matches_files(1, 0, 0); + else { + if (cc->mask & CC_FILES) + /* Add all files. */ + gen_matches_files(0, 0, 1); + else if (cc->mask & CC_COMMPATH) { + /* Completion of command paths. */ + if (sf1 || cc->withd) + /* There is a path prefix, so add * + * directories and executables. */ gen_matches_files(1, 1, 0); - prpre = pp; + else { + /* No path prefix, so add the things * + * reachable via the PATH variable. */ + char **pc = path, *pp = prpre; + + for (; *pc; pc++) + if (!**pc || (pc[0][0] == '.' && !pc[0][1])) + break; + if (*pc) { + prpre = "./"; + gen_matches_files(1, 1, 0); + prpre = pp; + } } - } - } else if (cc->mask & CC_DIRS) - gen_matches_files(1, 0, 0); - /* The compctl has a glob pattern (compctl -g). */ - if (cc->glob) { - int ns, pl = strlen(prpre), o; - char *g = dupstring(cc->glob), pa[PATH_MAX]; - char *p2, *p3; - int ne = noerrs, md = opts[MARKDIRS]; - - /* These are used in the globbing code to make * - * things a bit faster. */ - glob_pre = fpre; - glob_suf = fsuf; - - noerrs = 1; - addwhat = -6; - strcpy(pa, prpre); - o = strlen(pa); - opts[MARKDIRS] = 0; - - /* The compctl -g string may contain more than * - * one pattern, so we need a loop. */ - while (*g) { - LinkList l = newlinklist(); - int ng; - - /* Find the blank terminating the pattern. */ - while (*g && inblank(*g)) - g++; - /* Oops, we already reached the end of the - string. */ - if (!*g) - break; - for (p = g + 1; *p && !inblank(*p); p++) - if (*p == '\\' && p[1]) - p++; - /* Get the pattern string. */ - tokenize(g = dupstrpfx(g, p - g)); - if (*g == '=') - *g = Equals; - if (*g == '~') - *g = Tilde; - remnulargs(g); - if ((*g == Equals || *g == Tilde) && !cc->withd) { + } else if (cc->mask & CC_DIRS) + gen_matches_files(1, 0, 0); + /* The compctl has a glob pattern (compctl -g). */ + if (cc->glob) { + int ns, pl = strlen(prpre), o; + char *g = dupstring(cc->glob), pa[PATH_MAX]; + char *p2, *p3; + int ne = noerrs, md = opts[MARKDIRS]; + + /* These are used in the globbing code to make * + * things a bit faster. */ + if (ispattern || mstack) + glob_pre = glob_suf = NULL; + else { + glob_pre = fpre; + glob_suf = fsuf; + } + noerrs = 1; + addwhat = -6; + strcpy(pa, prpre); + o = strlen(pa); + opts[MARKDIRS] = 0; + + /* The compctl -g string may contain more than * + * one pattern, so we need a loop. */ + while (*g) { + LinkList l = newlinklist(); + int ng; + + /* Find the blank terminating the pattern. */ + while (*g && inblank(*g)) + g++; + /* Oops, we already reached the end of the + string. */ + if (!*g) + break; + for (p = g + 1; *p && !inblank(*p); p++) + if (*p == '\\' && p[1]) + p++; + /* Get the pattern string. */ + tokenize(g = dupstrpfx(g, p - g)); + if (*g == '=') + *g = Equals; + if (*g == '~') + *g = Tilde; + remnulargs(g); + if ((*g == Equals || *g == Tilde) && !cc->withd) { /* The pattern has a `~' or `=' at the * * beginning, so we expand this and use * * the result. */ - filesub(&g, 0); - addlinknode(l, dupstring(g)); - } else if (*g == '/' && !cc->withd) + filesub(&g, 0); + addlinknode(l, dupstring(g)); + } else if (*g == '/' && !cc->withd) /* The pattern is a full path (starting * * with '/'), so add it unchanged. */ - addlinknode(l, dupstring(g)); - else { + addlinknode(l, dupstring(g)); + else { /* It's a simple pattern, so append it to * * the path we have on the command line. */ - strcpy(pa + o, g); - addlinknode(l, dupstring(pa)); - } - /* Do the globbing. */ - ng = opts[NULLGLOB]; - opts[NULLGLOB] = 1; - globlist(l); - opts[NULLGLOB] = ng; - /* Get the results. */ - if (nonempty(l) && peekfirst(l)) { - for (p2 = (char *)peekfirst(l); *p2; p2++) - if (itok(*p2)) - break; - if (!*p2) { - if ((*g == Equals || *g == Tilde || - *g == '/') || cc->withd) { - /* IF the pattern started with `~', * - * `=', or `/', add the result only, * - * if it really matches what we have * - * on the line. * - * Do this if an initial directory * - * was specified, too. */ - while ((p2 = (char *)ugetnode(l))) - if (strpfx(prpre, p2)) - addmatch(p2 + pl, NULL); - } else { - /* Otherwise ignore the path we * - * prepended to the pattern. */ - while ((p2 = p3 = - (char *)ugetnode(l))) { - for (ns = sf1; *p3 && ns; p3++) - if (*p3 == '/') - ns--; - - addmatch(p3, NULL); + strcpy(pa + o, g); + addlinknode(l, dupstring(pa)); + } + /* Do the globbing. */ + ng = opts[NULLGLOB]; + opts[NULLGLOB] = 1; + globlist(l); + opts[NULLGLOB] = ng; + /* Get the results. */ + if (nonempty(l) && peekfirst(l)) { + for (p2 = (char *)peekfirst(l); *p2; p2++) + if (itok(*p2)) + break; + if (!*p2) { + if ((*g == Equals || *g == Tilde || + *g == '/') || cc->withd) { + /* IF the pattern started with `~', * + * `=', or `/', add the result only, * + * if it really matches what we have * + * on the line. * + * Do this if an initial directory * + * was specified, too. */ + while ((p2 = (char *)ugetnode(l))) + if (strpfx(prpre, p2)) + addmatch(p2 + pl, NULL); + } else { + /* Otherwise ignore the path we * + * prepended to the pattern. */ + while ((p2 = p3 = + (char *)ugetnode(l))) { + for (ns = sf1; *p3 && ns; p3++) + if (*p3 == '/') + ns--; + + addmatch(p3, NULL); + } } } } + pa[o] = '\0'; + g = p; } - pa[o] = '\0'; - g = p; + glob_pre = glob_suf = NULL; + noerrs = ne; + opts[MARKDIRS] = md; } - glob_pre = glob_suf = NULL; - noerrs = ne; - opts[MARKDIRS] = md; } + dirs++; } + prpre = NULL; } } + lppre = lpsuf = NULL; + lppl = lpsl = 0; } - /* Use tricat() instead of dyncat() to get zalloc()'d memory. */ if (ic) { /* Now change the `~' and `=' tokens to the real characters so * * that things starting with these characters will be added. */ - char *orpre = rpre; - - rpre = tricat("", (ic == Tilde) ? "~" : "=", rpre); + rpre = dyncat((ic == Tilde) ? "~" : "=", rpre); rpl++; - zsfree(orpre); } if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) { /* If we have to complete commands, add alias names, * @@ -2802,7 +4090,7 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) addlinknode(args, cc->func); - if (*delit) { + if (delit) { p = dupstrpfx(os, ooffs); untokenize(p); addlinknode(args, p); @@ -2949,41 +4237,6 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) /* Add the two types of aliases. */ dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - /* If we have no matches, ignore fignore. */ - if (empty(matches)) { - matches = fmatches; - firstm = ffirstm; - shortest = fshortest; - ab = fab; - ae = fae; - shortl = fshortl; - } - - /* Make an array from the list of matches. */ - makearray(matches); - PERMALLOC { - amatches = arrdup(amatches); - if (firstm) - firstm = ztrdup(firstm); - /* And quote the prefixes/suffixes. */ - if (hasspecial(s)) { - zfree(lpre, lpl); - zfree(lsuf, lsl); - lpre = zalloc(lpl + 1); - memcpy(lpre, s, lpl); - lpre[lpl] = '\0'; - lsuf = ztrdup(s + offs); - quotepresuf(&lpre); - quotepresuf(&lsuf); - untokenize(lpre); - untokenize(lsuf); - } - quotepresuf(&fpre); - quotepresuf(&fsuf); - quotepresuf(&ppre); - quotepresuf(&psuf); - } LASTALLOC; - if (!errflag && cc->ylist) { /* generate the user-defined display list: if anything fails, * * we silently allow the normal completion list to be used. */ @@ -2996,14 +4249,21 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) } else if ((list = getshfunc(cc->ylist)) != &dummy_list) { /* from function: pass completions as arg list */ LinkList args = newlinklist(); - int addlen = strlen(rpre) + strlen(rsuf) + 1; + LinkNode ln; + Cmatch m; addlinknode(args, cc->ylist); - for (yaptr = amatches; *yaptr; yaptr++) { - /* can't use tricat(). rats. */ - char *ptr = (char *)halloc(addlen + strlen(*yaptr)); - sprintf(ptr, "%s%s%s", rpre, *yaptr, rsuf); - addlinknode(args, ptr); + for (ln = firstnode(matches); ln; ln = nextnode(ln)) { + m = (Cmatch) getdata(ln); + if (m->ppre) { + char *p = (char *) halloc(strlen(m->ppre) + strlen(m->str) + + strlen(m->psuf) + 1); + + sprintf(p, "%s%s%s", m->ppre, m->str, m->psuf); + addlinknode(args, dupstring(p)); + } + else + addlinknode(args, dupstring(m->str)); } /* No harm in allowing read -l and -c here, too */ @@ -3012,70 +4272,80 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) incompctlfunc = 0; uv = "reply"; } - if (uv && (yaptr = get_user_var(uv))) { - PERMALLOC { - aylist = arrdup(yaptr); - } LASTALLOC; + + if ((tt = cc->explain)) { + if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) { + singsub(&tt); + untokenize(tt); + } + expl->str = tt; + addexpl(); } + if (uv && (yaptr = get_user_var(uv))) + endcmgroup(yaptr); + else + endcmgroup(NULL); + begcmgroup("default", 0); } - - /* Get the explanation string we will have to print: * - * do this here in case a -y function alters the messge */ - if ((expl = cc->explain)) { - if (cc->mask & CC_EXPANDEXPL && !parsestr(expl = dupstring(expl))) { - singsub(&expl); - untokenize(expl); + else if ((tt = cc->explain)) { + if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) { + singsub(&tt); + untokenize(tt); } - expl = ztrdup(expl); + expl->str = tt; + addexpl(); } + if (cc->subcmd) { + /* Handle -l sub-completion. */ + char **ow = clwords, *os = cmdstr, *ops = NULL; + int oldn = clwnum, oldp = clwpos; + unsigned long occ = ccont; + + ccont = 0; + + /* So we restrict the words-array. */ + if (brange >= clwnum) + brange = clwnum - 1; + if (brange < 1) + brange = 1; + if (erange >= clwnum) + erange = clwnum - 1; + if (erange < 1) + erange = 1; + clwnum = erange - brange + 1; + clwpos = clwpos - brange; + + if (cc->subcmd[0]) { + /* And probably put the command name given to the flag * + * in the array. */ + clwpos++; + clwnum++; + incmd = 0; + ops = clwords[brange - 1]; + clwords[brange - 1] = cc->subcmd; + cmdstr = ztrdup(cc->subcmd); + clwords += brange - 1; + } else { + cmdstr = ztrdup(clwords[brange]); + incmd = !clwpos; + clwords += brange; + } + /* Produce the matches. */ + makecomplistcmd(s, incmd); - remsuffix = (cc->mask & CC_REMOVE); - ccsuffix = cc->suffix; - - validlist = 1; - if (nmatches && !errflag) - return 0; - - if ((isf || cc->xor) && !parampre) { - /* We found no matches, but there is a xor'ed completion: * - * fine, so go back and continue with that compctl. */ - errflag = 0; - cc = cc->xor; - isf = 0; - wb = owb; - we = owe; - cs = ocs; - ll = oll; - strcpy((char *)line, (char *)ol); - offs = oloffs; - s = dupstring(os); - free(amatches); - zsfree(rpre); - zsfree(rsuf); - zsfree(lpre); - zsfree(lsuf); - zsfree(ppre); - zsfree(psuf); - zsfree(fpre); - zsfree(fsuf); - zsfree(prpre); - zsfree(parampre); - zsfree(qparampre); - zsfree(firstm); - if (expl) - zsfree(expl); - expl = NULL; - if (aylist) - freearray(aylist); - aylist = NULL; - goto xorrec; + /* And restore the things we changed. */ + clwords = ow; + zsfree(cmdstr); + cmdstr = os; + clwnum = oldn; + clwpos = oldp; + if (ops) + clwords[brange - 1] = ops; + ccont = occ; } - - /* No matches and xor'ed completion: restore the command line if * - * it was alredy quoted, which is the case when s is untokenized. */ - if (untokenized) - strcpy((char *)line, (char *)ol); - return 1; + uremnode(ccstack, firstnode(ccstack)); + if (cc->matcher) + mstack = mstack->next; } /* Invalidate the completion list. */ @@ -3086,31 +4356,11 @@ invalidatelist(void) { if(showinglist == -2) listmatches(); - if(validlist) { - freearray(amatches); - if (aylist) - freearray(aylist); - aylist = NULL; - if (expl) - zsfree(expl); - expl = 0; - zsfree(rpre); - zsfree(rsuf); - zsfree(lpre); - zsfree(lsuf); - zsfree(ppre); - zsfree(psuf); - zsfree(fpre); - zsfree(fsuf); - zsfree(prpre); - zsfree(parampre); - zsfree(qparampre); - zsfree(firstm); - if (ccmain != &cc_dummy) - freecompctl(ccmain); - } - lastambig = menucmp = showinglist = validlist = 0; + if(validlist) + freematches(); + lastambig = menucmp = validlist = showinglist = 0; menucur = NULL; + compwidget = NULL; } /* Get the words from a variable or a compctl -k list. */ @@ -3177,55 +4427,356 @@ get_user_var(char *nam) /**/ static int -strbpcmp(const void *a, const void *b) +strbpcmp(char **aa, char **bb) { - char *aa = *((char **)a), *bb = *((char **)b); + char *a = *aa, *b = *bb; - while (*aa && *bb) { - if (*aa == '\\') - aa++; - if (*bb == '\\') - bb++; - if (*aa != *bb) - return (int)(*aa - *bb); - if (*aa) - aa++; - if (*bb) - bb++; + while (*a && *b) { + if (*a == '\\') + a++; + if (*b == '\\') + b++; + if (*a != *b) + return (int)(*a - *b); + if (*a) + a++; + if (*b) + b++; } - return (int)(*aa - *bb); + return (int)(*a - *b); +} + +/* The comparison function for matches (used for sorting). */ + +static int +matchcmp(Cmatch *a, Cmatch *b) +{ + return strbpcmp(&((*a)->str), &((*b)->str)); +} + +/* This tests, whether two matches are equal (would produce the same * + * strings on the command line). */ + +#define matchstreq(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b)))) + +static int +matcheq(Cmatch a, Cmatch b) +{ + return matchstreq(a->ipre, b->ipre) && + matchstreq(a->pre, b->pre) && + matchstreq(a->ppre, b->ppre) && + matchstreq(a->str, b->str) && + matchstreq(a->psuf, b->psuf) && + matchstreq(a->suf, b->suf); } -/* Make an array from a linked list */ +/* Make an array from a linked list. The second argument says whether * + * the array should be sorted. The third argument is used to return * + * the number of elements in the resulting array. The fourth argument * + * is used to return the number of NOLIST elements. */ /**/ -static void -makearray(LinkList l) +static Cmatch * +makearray(LinkList l, int s, int *np, int *nlp) { - char **ap, **bp, **cp; + Cmatch *ap, *bp, *cp, *rp; LinkNode nod; + int n, nl = 0; /* Build an array for the matches. */ - ap = amatches = (char **)ncalloc(((nmatches = countlinknodes(l)) + 1) * - sizeof(char *)); + rp = ap = (Cmatch *)ncalloc(((n = countlinknodes(l)) + 1) * + sizeof(Cmatch)); /* And copy them into it. */ for (nod = firstnode(l); nod; incnode(nod)) - *ap++ = (char *)getdata(nod); + *ap++ = (Cmatch) getdata(nod); *ap = NULL; - /* Now sort the array. */ - qsort((void *) amatches, nmatches, sizeof(char *), - (int (*) _((const void *, const void *)))strbpcmp); + if (s == 1) { + char **ap, **bp, **cp; + + /* Now sort the array (it contains strings). */ + qsort((void *) rp, n, sizeof(char *), + (int (*) _((const void *, const void *)))strbpcmp); + + /* And delete the ones that occur more than once. */ + for (ap = cp = (char **) rp; *ap; ap++) { + *cp++ = *ap; + for (bp = ap; bp[1] && !strcmp(*ap, bp[1]); bp++, n--); + ap = bp; + } + *cp = NULL; + } + else if (s) { + /* Now sort the array (it contains matches). */ + qsort((void *) rp, n, sizeof(Cmatch), + (int (*) _((const void *, const void *)))matchcmp); + + /* And delete the ones that occur more than once. */ + for (ap = cp = rp; *ap; ap++) { + *cp++ = *ap; + for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--); + ap = bp; + /* Mark those, that would show the same string in the list. */ + for (; bp[1] && !strcmp((*ap)->str, (bp[1])->str); bp++) { + (bp[1])->flags |= CMF_NOLIST; nl++; + } + } + *cp = NULL; + } + if (np) + *np = n; + if (nlp) + *nlp = nl; + return rp; +} + +/* This begins a new group of matches. */ + +/**/ +static void +begcmgroup(char *n, int nu) +{ + if (n) { + Cmgroup p = amatches; + + while (p) { + if (p->name && ((nu && !p->lallccs) || (!nu && p->lallccs)) && + !strcmp(n, p->name)) { + mgroup = p; + + expls = p->lexpls; + matches = p->lmatches; + fmatches = p->lfmatches; + allccs = p->lallccs; + + return; + } + p = p->next; + } + } + mgroup = (Cmgroup) halloc(sizeof(struct cmgroup)); + mgroup->name = n; + mgroup->flags = mgroup->lcount = mgroup->mcount = 0; + mgroup->matches = NULL; + mgroup->ylist = NULL; + mgroup->expls = NULL; + + mgroup->lexpls = expls = newlinklist(); + mgroup->lmatches = matches = newlinklist(); + mgroup->lfmatches = fmatches = newlinklist(); + + mgroup->lallccs = allccs = (nu ? NULL : newlinklist()); + + mgroup->next = amatches; + amatches = mgroup; +} + +/* End the current group for now. */ + +/**/ +static void +endcmgroup(char **ylist) +{ + mgroup->ylist = ylist; +} + +/* Add an explanation string to the current group, joining duplicates. */ + +/**/ +static void +addexpl(void) +{ + LinkNode n; + Cexpl e; + + for (n = firstnode(expls); n; incnode(n)) { + e = (Cexpl) getdata(n); + if (!strcmp(expl->str, e->str)) { + e->count += expl->count; + e->fcount += expl->fcount; + + return; + } + } + addlinknode(expls, expl); +} + +/* This duplicates one match. */ + +/**/ +static Cmatch +dupmatch(Cmatch m) +{ + Cmatch r; + + r = (Cmatch) ncalloc(sizeof(struct cmatch)); + + r->str = ztrdup(m->str); + r->ipre = ztrdup(m->ipre); + r->ripre = ztrdup(m->ripre); + r->ppre = ztrdup(m->ppre); + r->psuf = ztrdup(m->psuf); + r->prpre = ztrdup(m->prpre); + r->pre = m->pre; + r->suf = m->suf; + r->flags = m->flags; + r->brpl = m->brpl; + r->brsl = m->brsl; + + return r; +} + +/* This duplicates all groups of matches. */ + +/**/ +static void +permmatches(void) +{ + Cmgroup g = amatches, n; + Cmatch *p, *q; + Cexpl *ep, *eq, e, o; + Compctl *cp, *cq; + int nn, nl, fi = 0; + + amatches = lmatches = NULL; + nmatches = smatches = 0; + + if (!ainfo->count) { + ainfo = fainfo; + fi = 1; + } + while (g) { + HEAPALLOC { + if (empty(g->lmatches)) + /* We have no matches, try ignoring fignore. */ + g->lmatches = g->lfmatches; + + g->matches = makearray(g->lmatches, + ((g->flags & CGF_NOSORT) ? 0 : 2), + &nn, &nl); + g->mcount = nn; + g->lcount = nn - nl; + if (g->ylist) + g->lcount = arrlen(g->ylist); + + g->expls = (Cexpl *) makearray(g->lexpls, 0, &(g->ecount), NULL); + + g->ccount = 0; + g->ccs = NULL; + } LASTALLOC; + + nmatches += g->mcount; + smatches += g->lcount; + + n = (Cmgroup) ncalloc(sizeof(struct cmgroup)); + + if (!lmatches) + lmatches = n; + if (amatches) + amatches->prev = n; + n->next = amatches; + amatches = n; + n->prev = 0; + + n->flags = g->flags; + n->mcount = g->mcount; + n->matches = p = (Cmatch *) ncalloc((n->mcount + 1) * + sizeof(Cmatch)); + for (q = g->matches; *q; q++, p++) + *p = dupmatch(*q); + *p = NULL; + + n->lcount = g->lcount; + if (g->ylist) + n->ylist = arrdup(g->ylist); + else + n->ylist = NULL; + + if ((n->ecount = g->ecount)) { + n->expls = ep = (Cexpl *) ncalloc((n->ecount + 1) * + sizeof(Cexpl)); + for (eq = g->expls; (o = *eq); eq++, ep++) { + *ep = e = (Cexpl) ncalloc(sizeof(struct cexpl)); + e->count = (fi ? o->fcount : o->count); + e->str = ztrdup(o->str); + } + *ep = NULL; + } + else + n->expls = NULL; + + if ((n->ccount = g->ccount)) { + n->ccs = cp = (Compctl *) ncalloc((n->ccount + 1) * + sizeof(Compctl)); + for (cq = g->ccs; *cq; cq++, cp++) + *cp = *cq; + *cp = NULL; + } + else + n->ccs = NULL; + g = g->next; + } +} + +/* This frees one match. */ + +/**/ +static void +freematch(Cmatch m) +{ + if (!m) return; + + zsfree(m->str); + zsfree(m->ipre); + zsfree(m->ripre); + zsfree(m->ppre); + zsfree(m->psuf); + zsfree(m->prpre); + + zfree(m, sizeof(m)); +} + +/* This frees the groups of matches. */ + +/**/ +static void +freematches(void) +{ + Cmgroup g = amatches, n; + Cmatch *m; + Cexpl *e; + Compctl *c; + + while (g) { + n = g->next; + + for (m = g->matches; *m; m++) + freematch(*m); + + if (g->ylist) + freearray(g->ylist); + + if ((e = g->expls)) { + while (*e) { + zsfree((*e)->str); + free(*e); + e++; + } + free(g->expls); + } + if ((c = g->ccs)) { + while (*c) { + if (*c != &cc_dummy) + freecompctl(*c); + c++; + } + free(g->ccs); + } + free(g); - /* And delete the ones that occur more than once. */ - for (ap = cp = amatches; *ap; ap++) { - *cp++ = *ap; - for (bp = ap; bp[1] && !strcmp(*ap, bp[1]); bp++); - ap = bp; + g = n; } - *cp = NULL; - nmatches = arrlen(amatches); } /* Handle the case were we found more than one match. */ @@ -3235,16 +4786,16 @@ static void do_ambiguous(void) { int p = (usemenu || ispattern), atend = (cs == we); - int inv = 0; + 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 (shortest && shortl == 0 && isset(RECEXACT) && + if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && (usemenu == 0 || unset(AUTOMENU))) { - do_single(shortest); + do_single(ainfo->exactm); invalidatelist(); return; } @@ -3253,6 +4804,7 @@ do_ambiguous(void) * but this might be overridden below if we can complete an * * unambiguous prefix. */ lastambig = 1; + if(p) { /* p is set if we are in a position to start using menu completion * * due to one of the menu completion options, or due to the * @@ -3261,21 +4813,93 @@ do_ambiguous(void) * normal menu completion options. */ do_ambig_menu(); } else { + int ics = cs, ocs, pl = 0, l, lp, ls; + char *ps; + Cline lc; + + if (!ainfo) + return; + + fixsuffix(); + + /* Delete the old stuff from the command line. */ + cs = wb; + foredel(we - wb); + /* Sort-of general case: we have an ambiguous completion, and aren't * * starting menu completion or doing anything really weird. We need * * to insert any unambiguous prefix and suffix, if possible. */ - if(ab) - inststrlen(firstm, 1, ab); - if(ae && !atend) - inststrlen(firstm + strlen(firstm) - ae, 0, ae); - if(ab || (ae && !atend)) - inv = 1; + + if (ainfo->iprefix && *ainfo->iprefix) { + inststrlen(ainfo->iprefix, 1, -1); + inststrlen(ainfo->pprefix, 1, -1); + ps = ainfo->iaprefix; + lc = ainfo->ilinecl; + lp = ainfo->icpl; + ls = ainfo->icsl; + } else { + if (ainfo->noipre && ainfo->pprefix) { + pl = strlen(ainfo->pprefix); + inststrlen(ainfo->pprefix, 1, pl); + } + ps = ainfo->aprefix; + lc = ainfo->linecl; + lp = ainfo->cpl; + ls = ainfo->csl; + } + if (lc) { + int sl = 0; + + if (lp) { + if (ls) { + if (ainfo->firstm->psuf) + merge_cline(lc, ps, lp, + dyncat(ainfo->firstm->str, + ainfo->firstm->psuf), + ls, (sl = strlen(ainfo->firstm->psuf))); + else + merge_cline(lc, ps, lp, ainfo->firstm->str, ls, 0); + } else + merge_cline(lc, ps, lp, NULL, 0, 0); + } + inst_cline(lc, pl, sl); + } else { + inststrlen(ps, 1, -1); + ocs = cs; + if (brbeg && *brbeg) { + cs = wb + brpl + pl; + l = strlen(brbeg); + inststrlen(brbeg, 1, l); + ocs += l; + cs = ocs; + } + if(ainfo->suflen && !atend) + inststrlen(ainfo->firstm->str + + strlen(ainfo->firstm->str) - ainfo->suflen, 1, + ainfo->suflen); + if (ainfo->firstm->psuf) + inststrlen(ainfo->firstm->psuf, 0, -1); + if (brend && *brend) { + cs -= brsl; + inststrlen(brend, 1, -1); + } + 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. */ + + if (isset(RECEXACT) && !lc && !ainfo->prerest) + am = 1; + /* If the LIST_AMBIGUOUS option (meaning roughly `show a list only * * if the completion is completely ambiguous') is set, and some * * 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) && inv) { + if(isset(LISTAMBIGUOUS) && !am && + (ics != cs || (ainfo->suflen && !atend))) { invalidatelist(); lastambig = 0; return; @@ -3287,8 +4911,8 @@ do_ambiguous(void) feep(); if (isset(AUTOLIST) && !amenu && !showinglist) showinglist = -2; - if(inv) - invalidatelist(); + if (am) + lastambig = 1; } /* This is a stat that ignores backslashes in the filename. The `ls' * @@ -3317,97 +4941,92 @@ ztat(char *nam, struct stat *buf, int ls) /**/ static void -do_single(char *str) +do_single(Cmatch m) { int l; int havesuff = 0; + char *str = m->str, *ppre = m->ppre, *psuf = m->psuf, *prpre = m->prpre; + + if (!prpre) prpre = ""; + if (!ppre) ppre = ""; + if (!psuf) psuf = ""; + fixsuffix(); if (!menucur) { /* We are currently not in a menu-completion, * * so set the position variables. */ - if (ispattern) { - cs = we; - menupos = wb; - } else - menupos = cs; + menupos = wb; menuwe = (cs == we) || isset(ALWAYSTOEND); menuend = we; } /* If we are already in a menu-completion or if we have done a * * glob completion, we have to delete some of the stuff on the * * command line. */ - if (menucur) { - if (menuinsc) { - cs = menuend + lsl; - foredel(menuinsc); - } - l = menulen; - } else if (ispattern) - l = we - wb; + if (menucur) + l = menulen + menuinsc; else - l = 0; + l = we - wb; menuinsc = 0; cs = menupos; foredel(l); - /* And than we insert the new string. */ - inststrlen(str, 1, menulen = strlen(str)); + /* And then we insert the new string. */ + menulen = instmatch(m); menuend = cs; + cs = menupos + menulen; - cs += lsl; - - if (ccsuffix) { - /* There is a compctl -S suffix. Add it. */ - if (!(haswhat & HAS_SUFFIX) && *ccsuffix) { - havesuff = 1; - inststr(ccsuffix); - menuinsc = ztrlen(ccsuffix); - if (remsuffix && menuwe) - makesuffix(menuinsc); - } + if (m->suf) { havesuff = 1; + menuinsc = ztrlen(m->suf); + if (menuwe && (m->flags & CMF_REMOVE)) { + makesuffix(menuinsc); + if (menuinsc == 1) + suffixlen[m->suf[0]] = 1; + } } else { /* There is no user-specified suffix, * * so generate one automagically. */ - if(parampre && parambr) { + if(m->ripre && (m->flags & CMF_PARBR)) { /*{{*/ /* Completing a parameter in braces. Add a removable `}' suffix. */ inststrlen("}", 1, 1); menuinsc++; + if (menuwe) + menuend++; } - if(!(haswhat & HAS_MISC) || - (parampre && isset(AUTOPARAMSLASH))) { - /* If we have only filenames or we completed a parameter name * + if((m->flags & CMF_FILE) || (m->ripre && isset(AUTOPARAMSLASH))) { + /* If we have a filename or we completed a parameter name * * and AUTO_PARAM_SLASH is set, lets see if it is a directory. * * If it is, we append a slash. */ char *p; struct stat buf; /* Build the path name. */ - if (ispattern || ic || parampre) { + if (ispattern || ic || m->ripre) { int ne = noerrs; noerrs = 1; - if (parampre) { - int pl = strlen(parampre); - p = (char *) ncalloc(pl + strlen(lpre) + strlen(str) + - strlen(lsuf) + 1); - sprintf(p, "%s%s%s%s", parampre, lpre, str, lsuf); + if (m->ripre) { + int pl = strlen(m->ripre); + + p = (char *) ncalloc(pl + strlen(str) + strlen(psuf) + 1); + sprintf(p, "%s%s%s", m->ripre, str, psuf); if (pl && p[pl-1] == Inbrace) strcpy(p+pl-1, p+pl); } else if (ic) { - p = (char *) ncalloc(strlen(ppre) + strlen(fpre) + strlen(str) + - strlen(fsuf) + strlen(psuf) + 2); - sprintf(p, "%c%s%s%s%s%s", ic, - ppre, fpre, str, fsuf, psuf); + p = (char *) ncalloc(strlen(ppre) + strlen(str) + + strlen(psuf) + 2); + sprintf(p, "%c%s%s%s", ic, ppre, str, psuf); + } + else { + p = (char *) ncalloc(strlen(ppre) + strlen(str) + + strlen(psuf) + 1); } - else - p = dupstring(str); parsestr(p); if (ic) *p = ic; @@ -3415,11 +5034,9 @@ do_single(char *str) noerrs = ne; } else { - p = (char *) ncalloc((prpre ? strlen(prpre) : 0) + strlen(fpre) + - strlen(str) + strlen(fsuf) + strlen(psuf) + 3); - sprintf(p, "%s%s%s%s%s", - (prpre && *prpre) ? prpre : "./", fpre, str, - fsuf, psuf); + p = (char *) ncalloc(strlen(prpre) + strlen(str) + + strlen(psuf) + 3); + sprintf(p, "%s%s%s", (prpre && *prpre) ? prpre : "./", str, psuf); } /* And do the stat. */ if (!ztat(p, &buf, 0) && S_ISDIR(buf.st_mode)) { @@ -3427,7 +5044,9 @@ do_single(char *str) havesuff = 1; inststrlen("/", 1, 1); menuinsc++; - if(menuwe && isset(AUTOREMOVESLASH)) { + if (menuwe) + menuend++; + if ((!menucmp || menuwe) && isset(AUTOREMOVESLASH)) { makesuffix(1); suffixlen['/'] = 1; } @@ -3435,34 +5054,36 @@ do_single(char *str) } } /* If completing in a brace expansion... */ - if(complinbrace) { - if(havesuff) { + if (brbeg) { + if (havesuff) { /*{{*/ /* If a suffix was added, and is removable, let * * `,' and `}' remove it. */ - if(isset(AUTOPARAMKEYS)) + if (isset(AUTOPARAMKEYS)) suffixlen[','] = suffixlen['}'] = suffixlen[256]; - } else { + } else if (!menucmp) { /*{{*/ /* Otherwise, add a `,' suffix, and let `}' remove it. */ + cs = menuend; havesuff = 1; inststrlen(",", 1, 1); menuinsc++; - if(menuwe && isset(AUTOPARAMKEYS)) + makesuffix(1); + if (menuwe && isset(AUTOPARAMKEYS)) suffixlen[','] = suffixlen['}'] = 1; } - } else if(!menucmp && !havesuff) { + } else if (!menucmp && !havesuff) { /* If we didn't add a suffix, add a space, unless we are * * doing menu completion. */ inststrlen(" ", 1, 1); menuinsc++; - if(menuwe) + if (menuwe) makesuffix(1); } - if(menuwe && parampre && isset(AUTOPARAMKEYS)) - makeparamsuffix(parambr, menuinsc); + if (menuwe && m->ripre && isset(AUTOPARAMKEYS)) + makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), menuinsc); - if (!menuwe) + if (menucmp && !menuwe) cs = menuend; } @@ -3474,8 +5095,11 @@ do_ambig_menu(void) { menucmp = 1; menucur = NULL; - do_single(amatches[0]); - menucur = amatches; + menugrp = amatches; + while (!menugrp->mcount) + menugrp = menugrp->next; + do_single(menugrp->matches[0]); + menucur = menugrp->matches; } /* Return the length of the common prefix of s and t. */ @@ -3517,12 +5141,13 @@ static int printfmt(char *fmt, int n, int dopr) { char *p = fmt, nc[DIGBUFSIZE]; - int l = 0, cc = 0; + int l = 0, cc = 0, b = 0, s = 0, u = 0, m; for (; *p; p++) { /* Handle the `%' stuff (%% == %, %n == ). */ if (*p == '%') { if (*++p) { + m = 0; switch (*p) { case '%': if (dopr) @@ -3535,6 +5160,38 @@ printfmt(char *fmt, int n, int dopr) fprintf(shout, nc); cc += strlen(nc); break; + case 'B': + b = 1; + tcout(TCBOLDFACEBEG); + break; + case 'b': + b = 0; m = 1; + tcout(TCALLATTRSOFF); + break; + case 'S': + s = 1; + tcout(TCSTANDOUTBEG); + break; + case 's': + s = 0; m = 1; + tcout(TCSTANDOUTEND); + break; + case 'U': + u = 1; + tcout(TCUNDERLINEBEG); + break; + case 'u': + u = 0; m = 1; + tcout(TCUNDERLINEEND); + break; + } + if (m) { + if (b) + tcout(TCBOLDFACEBEG); + if (s) + tcout(TCSTANDOUTBEG); + if (u) + tcout(TCUNDERLINEBEG); } } else break; @@ -3552,20 +5209,34 @@ printfmt(char *fmt, int n, int dopr) return l + (cc / columns); } +/* This skips over matches that are not to be listed. */ + +static Cmatch * +skipnolist(Cmatch *p) +{ + while (*p && ((*p)->flags & CMF_NOLIST)) + p++; + + return p; +} + /* List the matches. Note that the list entries are metafied. */ /**/ void listmatches(void) { - int longest = 1, fct, fw, colsz, t0, t1, ct, up, cl, xup = 0; - int off = 0, boff = 0, nboff = 0; - int of = (!aylist && isset(LISTTYPES) && !(haswhat & HAS_MISC)); - char **arr, **ap, sav; - int nfpl, nfsl, nlpl, nlsl; - int listmax = getiparam("LISTMAX"), litnl = 0; - size_t (*strlenfn) _((char const *)); - + Cmgroup g; + Cmatch *p, m; + Cexpl *e; + int nlines = 0, ncols, colsz, ngr = 0, nlist = 0, longest = 1, pnl = 0; + int of = isset(LISTTYPES), opl = 0; + int listmax = getiparam("LISTMAX"); + + if (smatches < 2) { + showinglist = 0; + return; + } #ifdef DEBUG /* Sanity check */ if(!validlist) { @@ -3574,23 +5245,6 @@ listmatches(void) } #endif - /* Calculate lengths of prefixes/suffixes to be added */ - nfpl = fpre ? niceztrlen(fpre) : 0; - nfsl = fsuf ? niceztrlen(fsuf) : 0; - nlpl = lpre ? niceztrlen(lpre) : 0; - nlsl = lsuf ? niceztrlen(lsuf) : 0; - - /* Calculate the lengths of the prefixes/suffixes we have to ignore - during printing. */ - if (ispattern && !aylist && !(haswhat & (HAS_MISC | HAS_PATHPAT))) { - if (ppre && *ppre) - off = strlen(ppre); - if (psuf && *psuf) { - boff = strlen(psuf); - nboff = niceztrlen(psuf); - } - } - /* Set the cursor below the prompt. */ trashzle(); showinglist = 0; @@ -3599,86 +5253,92 @@ listmatches(void) (isset(ALWAYSLASTPROMPT) && zmult == 1)) || (unset(ALWAYSLASTPROMPT) && zmult != 1); - /* just to keep gcc happy */ - fw = colsz = up = 0; - if (aylist) { - arr = aylist; - /* If no literal newlines, the remaining code should use strlen() */ - strlenfn = (size_t (*) _((char const *)))strlen; - - /* The hard bit here is that we are handling newlines literally. * - * In fact, we are in principle handling all characters literally, * - * but it's quite enough work with just newlines. * - * If there are such, we give up trying to print the list as * - * columns and print as rows, counting the extra newlines. */ - ct = 0; - for (ap = arr; *ap; ap++) { - ct++; - if (strchr(*ap, '\n')) - litnl++; - } - if (litnl) { - colsz = ct; - up = colsz + nlnct - clearflag; - /* Count real newlines, as well as overflowing lines. */ - for (ap = arr; *ap; ap++) { - char *nlptr, *sptr = *ap; - while (sptr && *sptr) { - up += (nlptr = strchr(sptr, '\n')) - ? 1 + (nlptr-sptr)/columns - : strlen(sptr)/columns; - sptr = nlptr ? nlptr+1 : NULL; + for (g = amatches; g; g = g->next) { + char **pp = g->ylist; + int nl = 0, l; + + if (pp) { + /* We have an ylist, lets see, if it contains newlines. */ + while (!nl && *pp) + nl = !!strchr(*pp++, '\n'); + + pp = g->ylist; + if (nl) { + /* Yup, there are newlines, count lines. */ + char *nlptr, *sptr; + + g->flags |= CGF_LINES; + + while ((sptr = *pp)) { + while (sptr && *sptr) { + nlines += (nlptr = strchr(sptr, '\n')) + ? 1 + (nlptr-sptr)/columns + : strlen(sptr)/columns; + sptr = nlptr ? nlptr+1 : NULL; + } + nlines++; + pp++; + } + nlines--; + } + else { + while (*pp) { + if ((l = strlen(*pp)) > longest) + longest = l; + nlist++; + pp++; } } } - } else { - arr = amatches; - ct = nmatches; - strlenfn = niceztrlen; - } - - - if (!litnl) { - /* Calculate the column width, the number of columns and the - number of lines. */ - for (ap = arr; *ap; ap++) - if ((cl = strlenfn(*ap + off) - nboff + - ((ispattern || aylist) ? 0 : - (!(haswhat & HAS_MISC) ? - nfpl + nfsl : nlpl + nlsl))) > longest) - longest = cl; - if (of) - longest++; - - fw = longest + 2; - fct = (columns + 1) / fw; - if (fct == 0) { - fct = 1; - colsz = ct; - up = colsz + nlnct - clearflag; - for (ap = arr; *ap; ap++) - up += (strlenfn(*ap + off) - nboff + of + - ((ispattern || aylist) ? 0 : - (!(haswhat & HAS_MISC) ? - nfpl + nfsl : nlpl + nlsl))) / columns; - } else { - colsz = (ct + fct - 1) / fct; - up = colsz + nlnct - clearflag + (ct == 0); + else { + for (p = g->matches; (m = *p); p++) { + if (!(m->flags & CMF_NOLIST)) { + if ((l = niceztrlen(m->str)) > longest) + longest = l; + nlist++; + } + } + } + if ((e = g->expls)) { + while (*e) { + if ((*e)->count) + nlines += 1 + printfmt((*e)->str, (*e)->count, 0); + e++; + } } + if (g->lcount) + ngr++; } - - /* Print the explanation string, if any. */ - if (expl) { - xup = printfmt(expl, ct, 1) + 1; - putc('\n', shout); - up += xup; + longest += 2 + of; + if ((ncols = (columns + 1) / longest)) { + colsz = (nlist + ncols - 1) / ncols; + nlines += ngr - 1 + colsz + (nlist == 0); + } else { + ncols = 1; + colsz = 1; + opl = 1; + for (g = amatches; g; g = g->next) { + char **pp = g->ylist; + + if (pp) { + if (!(g->flags & CGF_LINES)) { + while (*pp) { + nlines += 1 + (strlen(*pp) / columns); + pp++; + } + } + } else + for (p = g->matches; (m = *p); p++) + if (!(m->flags & CMF_NOLIST)) + nlines += 1 + ((1 + niceztrlen(m->str)) / columns); + } } /* Maybe we have to ask if the user wants to see the list. */ - if ((listmax && ct > listmax) || (!listmax && up >= lines)) { + if ((listmax && nlist > listmax) || (!listmax && nlines >= lines)) { int qup; - setterm(); - qup = printfmt("zsh: do you wish to see all %n possibilities? ", ct, 1); + zsetterm(); + qup = printfmt("zsh: do you wish to see all %n possibilities? ", nlist, 1); fflush(shout); if (getzlequery() != 'y') { if (clearflag) { @@ -3686,7 +5346,7 @@ listmatches(void) tcmultout(TCUP, TCMULTUP, qup); if (tccan(TCCLEAREOD)) tcout(TCCLEAREOD); - tcmultout(TCUP, TCMULTUP, nlnct + xup); + tcmultout(TCUP, TCMULTUP, nlnct); } else putc('\n', shout); return; @@ -3702,88 +5362,125 @@ listmatches(void) } /* Now print the matches. */ - for (t1 = 0; t1 != colsz; t1++) { - ap = arr + t1; - if (of) { - /* We have to print the file types. */ - while (*ap) { - int t2; - char *pb; - struct stat buf; - - /* Build the path name for the stat. */ - if (ispattern) { - int cut = strlen(*ap) - boff; - - sav = ap[0][cut]; - ap[0][cut] = '\0'; - nicezputs(*ap + off, shout); - t2 = niceztrlen(*ap + off); - ap[0][cut] = sav; - pb = *ap; - } else { - nicezputs(fpre, shout); - nicezputs(*ap, shout); - nicezputs(fsuf, shout); - t2 = nfpl + niceztrlen(*ap) + nfsl; - pb = (char *) halloc((prpre ? strlen(prpre) : 0) + 3 + - strlen(fpre) + strlen(*ap) + strlen(fsuf)); - sprintf(pb, "%s%s%s%s", - (prpre && *prpre) ? prpre : "./", fpre, *ap, fsuf); + g = amatches; + while (g) { + char **pp = g->ylist; + + if ((e = g->expls)) { + if (pnl) { + putc('\n', shout); + pnl = 0; + } + while (*e) { + if ((*e)->count) { + printfmt((*e)->str, (*e)->count, 1); + putc('\n', shout); } - if (ztat(pb, &buf, 1)) - putc(' ', shout); - else - /* Print the file type character. */ - putc(file_type(buf.st_mode), shout); - for (t0 = colsz; t0 && *ap; t0--, ap++); - if (*ap) - /* And add spaces to make the columns aligned. */ - for (++t2; t2 < fw; t2++) - putc(' ', shout); + e++; } - } else - while (*ap) { - int t2; - - if (aylist) { - zputs(*ap, shout); - t2 = strlen(*ap); - } else if (ispattern) { - int cut = strlen(*ap) - boff; - - sav = ap[0][cut]; - ap[0][cut] = '\0'; - nicezputs(*ap + off, shout); - t2 = niceztrlen(*ap + off); - ap[0][cut] = sav; - } else if (!(haswhat & HAS_MISC)) { - nicezputs(fpre, shout); - nicezputs(*ap, shout); - nicezputs(fsuf, shout); - t2 = nfpl + niceztrlen(*ap) + nfsl; - } else { - nicezputs(lpre, shout); - nicezputs(*ap, shout); - nicezputs(lsuf, shout); - t2 = nlpl + niceztrlen(*ap) + nlsl; + } + if (pp) { + if (pnl) { + putc('\n', shout); + pnl = 0; + } + if (g->flags & CGF_LINES) { + while (*pp) { + zputs(*pp, shout); + if (*++pp) + putc('\n', shout); } - for (t0 = colsz; t0 && *ap; t0--, ap++); - if (*ap) - for (; t2 < fw; t2++) - putc(' ', shout); } - if (t1 != colsz - 1 || !clearflag) - putc('\n', shout); + else { + int n = g->lcount, nl = (n + ncols - 1) / ncols, i, a; + int nc = (opl ? 1 : (n + colsz - 1) / colsz); + char **pq; + + while (n && nl--) { + i = nc; + pq = pp; + while (n && i--) { + if (pq - g->ylist >= g->lcount) + break; + zputs(*pq, shout); + if (i) { + a = longest - strlen(*pq); + while (a--) + putc(' ', shout); + } + pq += colsz; + n--; + } + if (n) + putc('\n', shout); + pp++; + } + } + } + else if (g->lcount) { + int n = g->lcount, nl = (n + ncols - 1) / ncols, i, j, a; + int nc = (opl ? 1 : (n + colsz - 1) / colsz); + Cmatch *q; + + if (n && pnl) { + putc('\n', shout); + pnl = 0; + } + for (p = skipnolist(g->matches); n && nl--;) { + i = nc; + q = p; + while (n && i--) { + if (!(m = *q)) + break; + nicezputs(m->str, shout); + if (i) + a = longest - niceztrlen(m->str); + + if (of && m->flags & CMF_FILE) { + struct stat buf; + char *pb; + + pb = (char *) halloc((m->prpre ? strlen(m->prpre) : 0) + + 3 + strlen(m->str)); + sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"), + m->str); + + if (ztat(pb, &buf, 1)) + putc(' ', shout); + else + putc(file_type(buf.st_mode), shout); + + a--; + } + if (i && !opl) + while (a--) + putc(' ', shout); + if (--n) + for (j = colsz; j && *q; j--) + q = skipnolist(q + 1); + } + if (n) { + putc('\n', shout); + p = skipnolist(p + 1); + } + } + } + if (g->lcount) + pnl = 1; + g = g->next; } - if (clearflag) + + if (clearflag) { /* Move the cursor up to the prompt, if always_last_prompt * * is set and all that... */ - if (up < lines) { - tcmultout(TCUP, TCMULTUP, up); + if ((nlines += nlnct - 1) < lines) { + tcmultout(TCUP, TCMULTUP, nlines); showinglist = -1; } else clearflag = 0, putc('\n', shout); + } + else + putc('\n', shout); } /* This is used to print expansions. */ @@ -3792,32 +5489,20 @@ listmatches(void) void listlist(LinkList l) { - int hw = haswhat, ip = ispattern; - char *lp = lpre, *ls = lsuf; - int nm = nmatches, vl = validlist; - char **am = amatches, **ay = aylist; - char *ex = expl; + struct cmgroup dg; + Cmgroup am = amatches; + int vl = validlist, sm = smatches; - haswhat = HAS_MISC; - ispattern = 0; + smatches = 1; validlist = 1; - lpre = lsuf = ""; - aylist = NULL; - expl = NULL; - - makearray(l); + amatches = &dg; + memset(&dg, 0, sizeof(struct cmgroup)); + dg.ylist = (char **) makearray(l, 1, &(dg.lcount), NULL); listmatches(); - showinglist = 0; - expl = ex; amatches = am; - aylist = ay; - nmatches = nm; validlist = vl; - lpre = lp; - lsuf = ls; - ispattern = ip; - haswhat = hw; + smatches = sm; } /* Expand the history references. */ diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 8fe3e7f0b..4cdb3c52d 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -420,7 +420,7 @@ void handlefeep(void) { if(feepflag) - beep(); + zbeep(); feepflag = 0; } diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c index a599d8091..46c6a705b 100644 --- a/Src/Zle/zle_vi.c +++ b/Src/Zle/zle_vi.c @@ -126,7 +126,7 @@ vigetkey(void) char sav = line[cs]; line[cs] = '^'; - refresh(); + zrefresh(); c = getkey(0); line[cs] = sav; if(c == EOF) { @@ -814,10 +814,10 @@ viswapcase(void) void vicapslockpanic(void) { - beep(); + zbeep(); statusline = "press a lowercase key to continue"; statusll = strlen(statusline); - refresh(); + zrefresh(); while (!islower(getkey(0))); statusline = NULL; } @@ -891,7 +891,7 @@ viquotedinsert(void) spaceinline(1); line[cs] = '^'; - refresh(); + zrefresh(); #ifndef HAS_TIO sob = shttyinfo.sgttyb; sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO; @@ -899,7 +899,7 @@ viquotedinsert(void) #endif c = getkey(0); #ifndef HAS_TIO - setterm(); + zsetterm(); #endif foredel(1); if(c < 0) diff --git a/Src/builtin.c b/Src/builtin.c index 31f396d93..3aae0f769 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2627,14 +2627,14 @@ bin_getopts(char *name, char **argv, char *ops, int func) if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) { p = "?"; err: - zsfree(zoptarg); + zsfree(zoptarg); if(quiet) { setsparam(var, ztrdup(p)); zoptarg = metafy(optbuf, lenoptbuf, META_DUP); } else { zerr(*p == '?' ? "bad option: -%c" : "argument expected after -%c option", NULL, opch); - zoptarg=ztrdup(""); + zoptarg=ztrdup(""); errflag = 0; } return 0; diff --git a/Src/exec.c b/Src/exec.c index 1b355d028..22fa526ec 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1992,8 +1992,8 @@ restore_params(LinkList restorelist, LinkList removelist) } } else paramtab->addnode(paramtab, pm->nam, pm); - if (pm->flags & PM_EXPORTED) - pm->env = addenv(pm->nam, getsparam(pm->nam)); + if ((pm->flags & PM_EXPORTED) && ((s = getsparam(pm->nam)))) + pm->env = addenv(pm->nam, s); } } } diff --git a/Src/glob.c b/Src/glob.c index be7a04515..0c66adec8 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -110,6 +110,8 @@ struct comp { #define C_CLOSURE (C_ONEHASH|C_TWOHASH|C_OPTIONAL|C_STAR) #define C_LAST 16 #define C_PATHADD 32 +#define C_LCMATCHUC 64 +#define C_IGNCASE 128 /* Test macros for the above */ #define CLOSUREP(c) (c->stat & C_CLOSURE) @@ -305,15 +307,24 @@ scanner(Complist q) if (!q) return; - if ((closure = q->closure)) /* (foo/)# - match zero or more dirs */ + if ((closure = q->closure)) { + /* (foo/)# - match zero or more dirs */ if (q->closure == 2) /* (foo/)## - match one or more dirs */ q->closure = 1; else scanner(q->next); + } c = q->comp; /* Now the actual matching for the current path section. */ - if (!(c->next || c->left) && !haswilds(c->str)) { - /* It's a straight string to the end of the path section. */ + if (!(c->next || c->left) && !haswilds(c->str) + && (!(c->stat & (C_LCMATCHUC|C_IGNCASE)) + || !strcmp(".", c->str) || !strcmp("..", c->str))) { + /* + * We always need to match . and .. explicitly, even if we're + * checking other strings for case-insensitive matches. + * + * It's a straight string to the end of the path section. + */ int l = strlen(c->str); if (l + !l + pathpos - pathbufcwd >= PATH_MAX) { @@ -436,6 +447,52 @@ 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; + return c; +} + +/**/ +static int +getglobflags() +{ + /* (#X): assumes we are still positioned on the initial '(' */ + pptr++; + while (*++pptr && *pptr != Outpar) { + switch (*pptr) { + case 'l': + /* Lowercase in pattern matches lower or upper in target */ + addflags |= C_LCMATCHUC; + break; + + case 'i': + /* Fully case insensitive */ + addflags |= C_IGNCASE; + break; + + case 'I': + /* Restore case sensitivity */ + addflags &= ~(C_LCMATCHUC|C_IGNCASE); + break; + + default: + return 1; + } + } + if (*pptr != Outpar) + return 1; + pptr++; + return 0; +} + /* enum used with ksh-like patterns, @(...) etc. */ enum { KF_NONE, KF_AT, KF_QUEST, KF_STAR, KF_PLUS, KF_NOT }; @@ -447,7 +504,7 @@ static Comp parsecomp(int gflag) { int kshfunc; - Comp c = (Comp) alloc(sizeof *c), c1, c2; + Comp c = compalloc(), c1, c2; char *cstr, *ls = NULL; /* In case of alternatives, code coming up is stored in tail. */ @@ -468,10 +525,10 @@ parsecomp(int gflag) c->str = dupstrpfx(cstr, pptr - cstr); pptr++; - c1 = (Comp) alloc(sizeof *c1); + c1 = compalloc(); c1->stat |= C_STAR; - c2 = (Comp) alloc(sizeof *c2); + c2 = compalloc(); if (!(c2->exclude = parsecomp(gflag))) return NULL; if (!*pptr || *pptr == '/') @@ -513,6 +570,39 @@ parsecomp(int gflag) pptr++; } + if (*pptr == Inpar && pptr[1] == Pound) { + /* Found some globbing flags */ + char *eptr = pptr; + if (kshfunc != KF_NONE) + eptr--; + if (getglobflags()) + return NULL; + if (eptr == cstr) { + /* if no string yet, carry on and get one. */ + c->stat = addflags; + cstr = pptr; + continue; + } + c->str = dupstrpfx(cstr, eptr - cstr); + /* + * The next bit simply handles the case where . or .. + * is followed by a set of flags, but we need to force + * them to be handled as a string. Hardly worth it. + */ + if (!*pptr || (!mode && *pptr == '/') || *pptr == Bar || + (isset(EXTENDEDGLOB) && *pptr == Tilde && + pptr[1] && pptr[1] != Outpar && pptr[1] != Bar) || + *pptr == Outpar) { + if (*pptr == '/' || !*pptr || + (isset(EXTENDEDGLOB) && *pptr == Tilde && + (gflag & GF_TOPLEV))) + c->stat |= C_LAST; + return c; + } + if (!(c->next = parsecomp(gflag))) + return NULL; + return c; + } if (*pptr == Inpar) { /* Found a group (...) */ char *startp = pptr, *endp; @@ -552,16 +642,16 @@ parsecomp(int gflag) pptr = startp; c->str = dupstrpfx(cstr, (pptr - cstr) - (kshfunc != KF_NONE)); pptr++; - c2 = (Comp) alloc(sizeof *c); + c2 = compalloc(); c->next = c2; c2->next = (dpnd || kshfunc == KF_NOT) ? - c1 : (Comp) alloc(sizeof *c); + c1 : compalloc(); if (!(c2->left = parsecompsw(0))) return NULL; if (kshfunc == KF_NOT) { /* we'd actually rather it didn't match. Instead, match * * a star and put the parsed pattern into exclude. */ - Comp c3 = (Comp) alloc(sizeof *c3); + Comp c3 = compalloc(); c3->stat |= C_STAR; c2->exclude = c2->left; @@ -584,7 +674,7 @@ parsecomp(int gflag) */ c->str = dupstrpfx(cstr, pptr - cstr); pptr++; - c1 = (Comp) alloc(sizeof *c1); + c1 = compalloc(); c1->stat |= C_STAR; if (!(c2 = parsecomp(gflag))) return NULL; @@ -596,13 +686,13 @@ parsecomp(int gflag) /* repeat whatever we've just had (ls) zero or more times */ if (!ls) return NULL; - c2 = (Comp) alloc(sizeof *c); + c2 = compalloc(); c2->str = dupstrpfx(ls, pptr - ls); pptr++; if (*pptr == Pound) { /* need one or more matches: cheat by copying previous char */ pptr++; - c->next = c1 = (Comp) alloc(sizeof *c); + c->next = c1 = compalloc(); c1->str = c2->str; } else c1 = c; @@ -669,6 +759,7 @@ static Comp parsecompsw(int gflag) { Comp c1, c2, c3, excl = NULL, stail = tail; + int oaddflags = addflags; char *sptr; /* @@ -709,7 +800,7 @@ parsecompsw(int gflag) tail = stail; if (*pptr == Bar || excl) { /* found an alternative or something to exclude */ - c2 = (Comp) alloc(sizeof *c2); + c2 = compalloc(); if (*pptr == Bar) { /* get the next alternative after the | */ pptr++; @@ -728,8 +819,10 @@ parsecompsw(int gflag) c2->next = stail; if (gflag & GF_PATHADD) c2->stat |= C_PATHADD; - return c2; + c1 = c2; } + if (!(gflag & GF_TOPLEV)) + addflags = oaddflags; return c1; } @@ -758,7 +851,7 @@ parsecomplist(void) errflag = 1; return NULL; } - p1->comp = (Comp) alloc(sizeof *p1->comp); + p1->comp = compalloc(); p1->comp->stat |= C_LAST; /* end of path component */ p1->comp->str = dupstring("*"); *p1->comp->str = Star; /* match anything... */ @@ -814,8 +907,28 @@ static Complist parsepat(char *str) { mode = 0; /* path components present */ + addflags = 0; pptr = str; tail = NULL; + /* + * Check for initial globbing flags, so that they don't form + * a bogus path component. + */ + if (*pptr == Inpar && pptr[1] == Pound && isset(EXTENDEDGLOB) && + getglobflags()) + return NULL; + + /* Now there is no (#X) in front, we can check the path. */ + if (!pathbuf) + pathbuf = zalloc(pathbufsz = PATH_MAX); + DPUTS(pathbufcwd, "BUG: glob changed directory"); + if (*pptr == '/') { /* pattern has absolute path */ + pptr++; + pathbuf[0] = '/'; + pathbuf[pathpos = 1] = '\0'; + } else /* pattern is relative to pwd */ + pathbuf[pathpos = 0] = '\0'; + return parsecomplist(); } @@ -897,7 +1010,7 @@ glob(LinkList list, LinkNode np) if (*s == Bar || *s == Outpar || (isset(EXTENDEDGLOB) && *s == Tilde)) break; - if (*s == Inpar) { + if (*s == Inpar && (!isset(EXTENDEDGLOB) || s[1] != Pound)) { /* Real qualifiers found. */ int sense = 0; /* bit 0 for match (0)/don't match (1) */ /* bit 1 for follow links (2), don't (0) */ @@ -1234,15 +1347,6 @@ glob(LinkList list, LinkNode np) } } } - if (!pathbuf) - pathbuf = zalloc(pathbufsz = PATH_MAX); - DPUTS(pathbufcwd, "BUG: glob changed directory"); - if (*str == '/') { /* pattern has absolute path */ - str++; - pathbuf[0] = '/'; - pathbuf[pathpos = 1] = '\0'; - } else /* pattern is relative to pwd */ - pathbuf[pathpos = 0] = '\0'; q = parsepat(str); if (!q || errflag) { /* if parsing failed */ if (unset(BADPATTERN)) { @@ -1267,7 +1371,7 @@ glob(LinkList list, LinkNode np) /* Deal with failures to match depending on options */ if (matchct) badcshglob |= 2; /* at least one cmd. line expansion O.K. */ - else if (!gf_nullglob) + else if (!gf_nullglob) { if (isset(CSHNULLGLOB)) { badcshglob |= 1; /* at least one cmd. line expansion failed */ } else if (isset(NOMATCH)) { @@ -1279,6 +1383,7 @@ glob(LinkList list, LinkNode np) untokenize(*matchptr++ = dupstring(ostr)); matchct = 1; } + } /* Sort arguments in to lexical (and possibly numeric) order. * * This is reversed to facilitate insertion into the list. */ qsort((void *) & matchbuf[0], matchct, sizeof(char *), @@ -1373,11 +1478,12 @@ hasbraces(char *str) *str++ = '{', *str = '}'; else bc++; - } else if (*str == Outbrace) + } else if (*str == Outbrace) { if (!bc) *str = '}'; else if (!--bc) return 1; + } return 0; } /* Otherwise we need to look for... */ @@ -1554,11 +1660,12 @@ xpandbraces(LinkList list, LinkNode *np) else if (*str2 == Outbrace) { if (--bc == 0) break; - } else if (bc == 1) + } else if (bc == 1) { if (*str2 == Comma) ++comma; /* we have {foo,bar} */ else if (*str2 == '.' && str2[1] == '.') dotdot++; /* we have {num1..num2} */ + } DPUTS(bc, "BUG: unmatched brace in xpandbraces()"); if (!comma && dotdot) { /* Expand range like 0..10 numerically: comma or recursive @@ -2489,7 +2596,10 @@ matchonce(Comp c) } continue; } - if (*pptr == *pat) { + if (*pptr == *pat || + (((c->stat & C_IGNCASE) ? (tulower(*pat) == tulower(*pptr)) : + (c->stat & C_LCMATCHUC) ? + (islower(*pat) && tuupper(*pat) == *pptr) : 0))) { /* just plain old characters */ pptr++; pat++; @@ -2508,6 +2618,7 @@ parsereg(char *str) { remnulargs(str); mode = 1; /* no path components */ + addflags = 0; pptr = str; tail = NULL; return parsecompsw(GF_TOPLEV); diff --git a/Src/jobs.c b/Src/jobs.c index 331902d9f..74fa8cda0 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -214,7 +214,7 @@ update_job(Job jn) if ((isset(NOTIFY) || job == thisjob) && (jn->stat & STAT_LOCKED)) { printjob(jn, !!isset(LONGLISTJOBS), 0); if (zleactive) - refresh(); + zrefresh(); } if (sigtrapped[SIGCHLD] && job != thisjob) dotrap(SIGCHLD); diff --git a/Src/mkmakemod.sh b/Src/mkmakemod.sh index b088929fa..9c90b75e5 100644 --- a/Src/mkmakemod.sh +++ b/Src/mkmakemod.sh @@ -145,6 +145,11 @@ if $first_stage; then echo "PROTOS =$all_proto" echo "SUBDIRS =$all_subdirs" echo + echo "ENTRYOBJ = \$(dir_src)/modentry..o" + echo "NNTRYOBJ =" + echo "ENTRYOPT = -emodentry" + echo "NNTRYOPT =" + echo echo "##### ===== INCLUDING Makemod.in.in ===== #####" echo @@ -161,7 +166,7 @@ if $first_stage; then remote_mdhs= for module in $here_modules; do - unset moddeps nozshdep alwayslink + unset moddeps nozshdep alwayslink hasexport unset autobins unset objects proto headers hdrdeps otherincs . $top_srcdir/$the_subdir/${module}.mdd @@ -172,8 +177,10 @@ if $first_stage; then dobjects=`echo $objects '' | sed 's,\.o ,..o ,g'` modhdeps= + imports= for dep in $moddeps; do eval "loc=\$loc_$dep" + imports="$imports \$(IMPOPT)\$(sdir_top)/$loc/$dep.export" case $the_subdir in $loc) mdh="${dep}.mdh" @@ -199,9 +206,11 @@ if $first_stage; then echo "##### ===== DEPENDENCIES GENERATED FROM ${module}.mdd ===== #####" echo echo "MODOBJS_${module} = $objects" - echo "MODDOBJS_${module} = $dobjects" + echo "MODDOBJS_${module} = $dobjects \$(@E@NTRYOBJ)" echo "PROTO_${module} = $proto" echo "INCS_${module} = \$(PROTO_${module}) $otherincs" + echo "EXPIMP_${module} = $imports ${hasexport+\$(EXPOPT)\$(sdir)/$module.export}" + echo "NXPIMP_${module} =" echo echo "proto.${module}: \$(PROTO_${module})" echo "\$(PROTO_${module}): \$(PROTODEPS)" @@ -212,7 +221,7 @@ if $first_stage; then if test -z "$alwayslink"; then echo "${module}.\$(DL_EXT): \$(MODDOBJS_${module})" echo ' rm -f $@' - echo " \$(DLLINK) \$(MODDOBJS_${module}) \$(LIBS)" + echo " \$(DLLINK) \$(@E@XPIMP_$module) \$(@E@NTRYOPT) \$(MODDOBJS_${module}) \$(LIBS)" echo fi echo "${module}.mdhi: ${module}.mdhs \$(INCS_${module})" diff --git a/Src/modentry.c b/Src/modentry.c new file mode 100644 index 000000000..63c4b825d --- /dev/null +++ b/Src/modentry.c @@ -0,0 +1,15 @@ +#include "zsh.mdh" + +int boot_ _((Module)); +int cleanup_ _((Module)); +int modentry _((int boot, Module m)); + +/**/ +int +modentry(int boot, Module m) +{ + if (boot) + return boot_(m); + else + return cleanup_(m); +} diff --git a/Src/module.c b/Src/module.c index ce5989f07..91687a21d 100644 --- a/Src/module.c +++ b/Src/module.c @@ -161,6 +161,44 @@ deletebuiltins(char const *nam, Builtin binl, int size) return hadf ? hads : 1; } +#ifdef AIXDYNAMIC + +#include + +static char *dlerrstr[256]; + +/**/ +static void * +load_and_bind(const char *fn) +{ + void *ret = (void *) load((char *) fn, L_NOAUTODEFER, NULL); + + if (ret) { + LinkNode node; + int err = loadbind(0, (void *) addbuiltin, ret); + for (node = firstnode(modules); !err && node; incnode(node)) { + Module m = (Module) getdata(node); + if (m->handle) + err |= loadbind(0, m->handle, ret); + } + + if (err) { + loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr)); + unload(ret); + ret = NULL; + } + } else + loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr)); + + return ret; +} + +#define dlopen(X,Y) load_and_bind(X) +#define dlclose(X) unload(X) +#define dlerror() (dlerrstr[0]) + +#else + #ifdef HAVE_DLFCN_H # include #else @@ -168,12 +206,6 @@ deletebuiltins(char const *nam, Builtin binl, int size) # include # include #endif -#ifndef RTLD_LAZY -# define RTLD_LAZY 1 -#endif -#ifndef RTLD_GLOBAL -# define RTLD_GLOBAL 0 -#endif #ifndef HAVE_DLCLOSE # define dlclose(X) ((X), 0) #endif @@ -189,6 +221,16 @@ deletebuiltins(char const *nam, Builtin binl, int size) # define STR_CLEANUP "cleanup_" # define STR_CLEANUP_S "cleanup_%s" #endif /* !DLSYM_NEEDS_UNDERSCORE */ + +#endif /* !AIXDYNAMIC */ + +#ifndef RTLD_LAZY +# define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +# define RTLD_GLOBAL 0 +#endif + typedef int (*Module_func) _((Module)); /**/ @@ -257,6 +299,24 @@ find_module(const char *name) return NULL; } +#ifdef AIXDYNAMIC + +/**/ +static int +init_module(Module m) +{ + return ((int (*)_((int,Module))) m->handle)(1, m); +} + +/**/ +static int +cleanup_module(Module m) +{ + return ((int (*)_((int,Module))) m->handle)(0, m); +} + +#else + /**/ static int init_module(Module m) @@ -288,6 +348,39 @@ init_module(Module m) return 1; } +/**/ +static int +cleanup_module(Module m) +{ + char *s, *t; +#ifndef DYNAMIC_NAME_CLASH_OK + char buf[PATH_MAX + 1]; +#endif + Module_func fn; + + s = strrchr(m->nam, '/'); + if (s) + s = dupstring(++s); + else + s = m->nam; + if ((t = strrchr(s, '.'))) + *t = '\0'; +#ifdef DYNAMIC_NAME_CLASH_OK + fn = (Module_func) dlsym(m->handle, STR_CLEANUP); +#else /* !DYNAMIC_NAME_CLASH_OK */ + if (strlen(s) + 9 > PATH_MAX) + return 1; + sprintf(buf, STR_CLEANUP_S, s); + fn = (Module_func) dlsym(m->handle, buf); +#endif /* !DYNAMIC_NAME_CLASH_OK */ + if(fn) + return fn(m); + zwarnnam(m->nam, "no cleanup function", NULL, 0); + return 1; +} + +#endif /* !AIXDYNAMIC */ + /**/ Module load_module(char const *name) @@ -337,37 +430,6 @@ load_module(char const *name) return m; } -/**/ -static int -cleanup_module(Module m) -{ - char *s, *t; -#ifndef DYNAMIC_NAME_CLASH_OK - char buf[PATH_MAX + 1]; -#endif - Module_func fn; - - s = strrchr(m->nam, '/'); - if (s) - s = dupstring(++s); - else - s = m->nam; - if ((t = strrchr(s, '.'))) - *t = '\0'; -#ifdef DYNAMIC_NAME_CLASH_OK - fn = (Module_func) dlsym(m->handle, STR_CLEANUP); -#else /* !DYNAMIC_NAME_CLASH_OK */ - if (strlen(s) + 9 > PATH_MAX) - return 1; - sprintf(buf, STR_CLEANUP_S, s); - fn = (Module_func) dlsym(m->handle, buf); -#endif /* !DYNAMIC_NAME_CLASH_OK */ - if(fn) - return fn(m); - zwarnnam(m->nam, "no cleanup function", NULL, 0); - return 1; -} - /**/ void add_dep(char *name, char *from) diff --git a/Src/system.h b/Src/system.h index e42f3b891..292943dd9 100644 --- a/Src/system.h +++ b/Src/system.h @@ -593,6 +593,12 @@ extern char PC, *BC, *UP; extern short ospeed; #endif +/* Rename some global zsh variables to avoid * + * possible name clashes with libc */ + +#define cs zshcs +#define ll zshll + #ifndef O_NOCTTY # define O_NOCTTY 0 #endif diff --git a/Src/utils.c b/Src/utils.c index 3619fa95d..87f82f7df 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -510,6 +510,10 @@ adduserdir(char *s, char *t, int flags, int always) if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s)) return; + /* Never hash PWD, because it's never useful */ + if (!strcmp(s, "PWD")) + return; + /* Normal parameter assignments generate calls to this function, * * with always==0. Unless the AUTO_NAME_DIRS option is set, we * * don't let such assignments actually create directory names. * @@ -854,7 +858,7 @@ adjustwinsize(void) setiparam("LINES", shttyinfo.winsize.ws_row); if (zleactive && (oldcols != columns || oldrows != lines)) { resetneeded = winchanged = 1; - refresh(); + zrefresh(); } #endif /* TIOCGWINSZ */ } @@ -1129,13 +1133,13 @@ checkrmall(char *s) if(isset(RMSTARWAIT)) { fputs("? (waiting ten seconds)", shout); fflush(shout); - beep(); + zbeep(); sleep(10); fputc('\n', shout); } fputs(" [yn]? ", shout); fflush(shout); - beep(); + zbeep(); return (getquery("ny", 1) == 'y'); } @@ -1181,7 +1185,7 @@ getquery(char *valid_chars, int purge) write(SHTTY, "\n", 1); break; } - beep(); + zbeep(); if (icntrl(c)) write(SHTTY, "\b \b", 3); write(SHTTY, "\b \b", 3); @@ -1327,7 +1331,7 @@ spckword(char **s, int hist, int cmd, int ask) zputs(pptbuf, shout); free(pptbuf); fflush(shout); - beep(); + zbeep(); x = getquery("nyae ", 0); } else x = 'y'; @@ -2286,7 +2290,7 @@ mkarray(char *s) /**/ void -beep(void) +zbeep(void) { if (isset(BEEP)) write(SHTTY, "\07", 1); diff --git a/Src/zsh.export b/Src/zsh.export new file mode 100644 index 000000000..8f676c7fd --- /dev/null +++ b/Src/zsh.export @@ -0,0 +1,235 @@ +#! +SHTTY +addbuiltins +addedx +addhashnode +aliastab +alloc_stackp +appstr +arrdup +arrlen +attachtty +bangchar +bin_notavail +breaks +bufstack +builtintab +chline +chuck +clearjobtab +closem +cmdnamtab +columns +compctlreadptr +coprocin +coprocout +countlinknodes +countprompt +createparam +ctxtlex +curhist +current_limits +deletebuiltins +deletehashtable +domatch +doshfunc +dputs +dquotedztrdup +dummy_list +dupstring +dupstrpfx +dyncat +emptyhashtable +endparamscope +errflag +excs +execstring +exlast +expanding +fallback_compctlread +fallback_zleread +fignore +file_type +filesub +filesubstr +findcmd +firsthist +freearray +freeheap +getaparam +gethashnode +gethashnode2 +getiparam +getkeystring +getlinknode +getshfunc +getsparam +glob_pre +glob_suf +global_heapalloc +global_permalloc +globlist +gotwordptr +halloc +hasam +hashcmd +hasher +hasspecial +haswilds +hcalloc +hgetc +hgetline +histentarr +histentct +hptr +hrealloc +inbufct +incmdpos +incond +init_io +init_shout +init_term +inpop +inpush +inredir +insertlinknode +intr +inwhat +isfirstln +jobtab +lastpid +lastval +lchdir +lexrestore +lexsave +lexstop +limits +line +lines +locallevel +metadiffer +metafy +metalen +mode_to_octal +mypgrp +mypid +nameddirtab +ncalloc +newhashtable +newlinklist +nicechar +nicezputs +niceztrdup +niceztrlen +noaliases +noerrs +noop_function +noop_function_int +optiontab +opts +paramtab +parbegin +parend +parsereg +parsestr +path +pathchecked +popheap +postedit +ppid +prefork +prepromptfns +printif +printqt +promptexpand +pushheap +putshout +pwd +quietgetevent +quietgethist +quotedzputs +refreshptr +remlpaths +remnulargs +removehashnode +resetneeded +restoredir +reswdtab +retflag +scanhashtable +setaparam +setlimits +setsparam +settyinfo +shfunctab +shingetline +shout +shttyinfo +singsub +skipparens +spaceinlineptr +spacesplit +spckword +startparamscope +stdunsetfn +stophist +stopmsg +strinbeg +strinend +strpfx +strsfx +strucpy +struncpy +tclen +tcstr +termflags +tgoto +tok +tokenize +tokstr +tputs +trashzleptr +tricat +tsetcap +ttystrname +tulower +tuupper +txtchange +typtab +ugetnode +uinsertlinknode +unmeta +unmetafy +untokenize +uremnode +useheap +winchanged +wordbeg +zalloc +zbeep +zcalloc +zchdir +zerr +zerrnam +zexit +zfree +zgetdir +zgetenv +zjoin +zleactive +zleparse +zlereadptr +zputs +zreaddir +zsetlimit +zsfree +zshcs +zshll +zstrtol +ztokens +ztrdup +ztrduppfx +ztrftime +ztrlen +ztrsub +zwarnnam diff --git a/Src/zsh.h b/Src/zsh.h index e96fc6e86..d833278c9 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -31,7 +31,7 @@ #define zleread(X,Y,H) zlereadptr(X,Y,H) #define spaceinline(X) spaceinlineptr(X) #define gotword() gotwordptr() -#define refresh() refreshptr() +#define zrefresh() refreshptr() #define compctlread(N,A,O,R) compctlreadptr(N,A,O,R) diff --git a/Src/zsh.mdd b/Src/zsh.mdd index 244029d65..f4a1e4015 100644 --- a/Src/zsh.mdd +++ b/Src/zsh.mdd @@ -12,7 +12,7 @@ headers="../config.h system.h zsh.h sigcount.h signals.h \ prototypes.h hashtable.h ztype.h" :<<\Make -signames.c: signames.awk @SIGNAL_H@ +signames.c: signames.awk ../config.h @SIGNAL_H@ $(AWK) -f $(sdir)/signames.awk @SIGNAL_H@ > $@ sigcount.h: signames.c -- cgit 1.4.1