diff options
author | Sven Wischnowsky <wischnow@users.sourceforge.net> | 2000-04-10 08:06:38 +0000 |
---|---|---|
committer | Sven Wischnowsky <wischnow@users.sourceforge.net> | 2000-04-10 08:06:38 +0000 |
commit | 0713dfcd1a1513d17d63eddaa8954f839ea5fefe (patch) | |
tree | 9552dcdfeffdd37bb76a0fcb01661ee6015091f6 | |
parent | f7a6aa096aa333f7274be219b92540d09dba1eb7 (diff) | |
download | zsh-0713dfcd1a1513d17d63eddaa8954f839ea5fefe.tar.gz zsh-0713dfcd1a1513d17d63eddaa8954f839ea5fefe.tar.xz zsh-0713dfcd1a1513d17d63eddaa8954f839ea5fefe.zip |
fix for bashautolist+automenu (10609)
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | Src/Zle/compcore.c | 5 | ||||
-rw-r--r-- | Src/Zle/compresult.c | 4 | ||||
-rw-r--r-- | Src/Zle/zle_tricky.c | 4609 |
4 files changed, 1450 insertions, 3173 deletions
diff --git a/ChangeLog b/ChangeLog index bba84ea83..14b14af5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2000-04-10 Sven Wischnowsky <wischnow@informatik.hu-berlin.de> + + * 10609: Src/Zle/compcore.c, Src/Zle/compresult.c, Src/Zle/zle_tricky.c: + fix for bashautolist+automenu + 2000-04-09 Bart Schaefer <schaefer@zsh.org> * 10606: Fix thinko in 10598. diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 2a9ebbaa5..e40873764 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -776,8 +776,9 @@ callcompfunc(char *s, char *fn) } } } - startauto = (compinsert && - !strcmp(compinsert, "automenu-unambiguous")); + startauto = ((compinsert && + !strcmp(compinsert, "automenu-unambiguous")) || + (bashlistfirst && (!compinsert || !*compinsert))); useexact = (compexact && !strcmp(compexact, "accept")); if (!comptoend || !*comptoend) diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 0d93b8727..6870bbcd9 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -665,7 +665,9 @@ do_ambiguous(void) * prefix was inserted, return now, bypassing the list-displaying * * code. On the way, invalidate the list and note that we don't * * want to enter an AUTO_MENU imediately. */ - if (uselist == 3 && la) { + if ((uselist == 3 || + (!uselist && isset(BASHAUTOLIST) && isset(LISTAMBIGUOUS))) && + la) { int fc = fromcomp; invalidatelist(); diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 1aa1a008c..3b5e97b65 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -42,157 +42,108 @@ * file only are not translated: they remain indexes into the metafied * * line. */ -#ifdef HAVE_NIS_PLUS -# include <rpcsvc/nis.h> -#else -# ifdef HAVE_NIS -# include <rpc/types.h> -# include <rpc/rpc.h> -# include <rpcsvc/ypclnt.h> -# include <rpcsvc/yp_prot.h> - -/* This is used when getting usernames from the NIS. */ -typedef struct { - int len; - char *s; -} -dopestring; -# endif -#endif - #define inststr(X) inststrlen((X),1,-1) -/* wb and we hold the beginning/end position of the word we are completing. */ - -static int wb, we; +/* The line before completion was tried. */ -/* offs is the cursor position within the tokenized * - * current word after removing nulargs. */ - -static int offs; - -/* These control the type of completion that will be done. They are * - * affected by the choice of ZLE command and by relevant shell options. */ - -static int usemenu, useglob; - -/* != 0 if we are in the middle of a menu completion */ - -static int menucmp; - -/* A pointer to the current position in the menu-completion array (the one * - * that was put in the command line last). */ +/**/ +mod_export char *origline; +/**/ +mod_export int origcs, origll; -static char **menucur; +/* Words on the command line, for use in completion */ + +/**/ +mod_export int clwsize, clwnum, clwpos; +/**/ +mod_export char **clwords; -/* menupos is the point (in the command line) where the menu-completion * - * strings are inserted. menulen is the length of the string that was * - * inserted last. menuend is the end position of this string in the * - * command line. menuwe is non-zero if the cursor was at the end of the * - * word (meaning that suffixes should go before the cursor). menuinsc is * - * the length of any suffix that has been temporarily added. */ +/* wb and we hold the beginning/end position of the word we are completing. */ -static int menupos, menulen, menuend, menuwe, menuinsc; +/**/ +mod_export int wb, we; -/* This is used as a flag from get_comp_string() that we are doing * - * completion inside a brace expansion. */ +/* offs is the cursor position within the tokenized * + * current word after removing nulargs. */ -static int complinbrace; +/**/ +mod_export int offs; -/* The list of matches. fmatches contains the matches we first ignore * - * because of fignore. */ +/* These control the type of completion that will be done. They are * + * affected by the choice of ZLE command and by relevant shell options. * + * usemenu is set to 2 if we have to start automenu and 3 if we have to * + * insert a match as if for menucompletion but without really starting it. */ -static LinkList matches, fmatches; +/**/ +mod_export int usemenu, useglob; -/* 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). */ +/* != 0 if we are in the middle of a menu completion. May be == 2 to force * + * menu completion even if using different widgets. */ -static char **amatches; +/**/ +mod_export int menucmp; -/* The number of matches. */ +/* Lists of brace-infos before/after cursor (first and last for each). */ -static int nmatches; +/**/ +mod_export Brinfo brbeg, lastbrbeg, brend, lastbrend; -/* A list of user-defined explanations for the completions to be shown * - * instead of amatches when listing completions. */ +/**/ +mod_export int nbrbeg, nbrend; -static char **aylist; +/**/ +mod_export char *lastprebr, *lastpostbr; /* !=0 if we have a valid completion list. */ -static int validlist; - -/* This flag is non-zero if we are completing a pattern (with globcomplete) */ - -static int ispattern; - -/* Two patterns used when doing glob-completion. The first one is built * - * from the whole word we are completing and the second one from that * - * part of the word that was identified as a possible filename. */ - -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. */ - -static char *lpre, *lsuf; -static char *rpre, *rsuf; -static char *ppre, *psuf, *prpre; -static char *fpre, *fsuf; -static int lpl, lsl, rpl, rsl, fpl, fsl; -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; +/**/ +mod_export int validlist; -/* This is either zero or equal to the special character the word we are * - * trying to complete starts with (e.g. Tilde or Equals). */ +/* Non-zero if we have to redisplay the list of matches. */ -static char ic; +/**/ +mod_export int showagain = 0; -/* These hold the minimum common prefix/suffix lengths (normal and for * - * fignore ignored). */ +/* This holds the word we are completing in quoted from. */ -static int ab, ae, fab, fae; +static char *qword; -/* This variable says what we are currently adding to the list of matches. */ +/* This holds the word we are working on without braces removed. */ -static int addwhat; +static char *origword; -/* firstm hold the first match we found, shortest contains the shortest * - * one (normal and for fignore ignored). */ +/* The quoted prefix/suffix and a flag saying if we want to add the + * closing quote. */ -static char *firstm, *shortest, *ffirstm, *fshortest; +/**/ +mod_export char *qipre, *qisuf, *autoq; -/* This holds the word we are completing in quoted from. */ +/* This contains the name of the function to call if this is for a new * + * style completion. */ -static char *qword; +/**/ +mod_export char *compfunc = NULL; -/* This is the length of the shortest match we found (normal and for * - * fignore ignored). */ +/* Non-zero if the last completion done was ambiguous (used to find * + * out if AUTOMENU should start). More precisely, it's nonzero after * + * successfully doing any completion, unless the completion was * + * unambiguous and did not cause the display of a completion list. * + * From the other point of view, it's nonzero iff AUTOMENU (if set) * + * should kick in on another completion. * + * * + * If both AUTOMENU and BASHAUTOLIST are set, then we get a listing * + * on the second tab, a` la bash, and then automenu kicks in when * + * lastambig == 2. */ -static int shortl, fshortl; +/**/ +mod_export int lastambig, bashlistfirst; -/* 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). */ +/* Arguments for and return value of completion widget. */ -static int amenu; +/**/ +mod_export char **cfargs; +/**/ +mod_export int cfret; /* Find out if we have to insert a tab (instead of trying to complete). */ @@ -208,208 +159,200 @@ 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 -#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND) +/**/ +int +completecall(char **args) +{ + cfargs = args; + cfret = 0; + compfunc = compwidget->u.comp.func; + if (compwidget->u.comp.fn(zlenoargs) && !cfret) + cfret = 1; + compfunc = NULL; + + return cfret; +} /**/ -void -completeword(void) +int +completeword(char **args) { - usemenu = isset(MENUCOMPLETE); + usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); if (c == '\t' && usetab()) - selfinsert(); - else - docomplete(COMP_COMPLETE); + return selfinsert(args); + else { + int ret; + if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) { + bashlistfirst = 1; + ret = docomplete(COMP_LIST_COMPLETE); + bashlistfirst = 0; + lastambig = 2; + } else + ret = docomplete(COMP_COMPLETE); + return ret; + } } /**/ -void -menucomplete(void) +mod_export int +menucomplete(char **args) { usemenu = 1; useglob = isset(GLOBCOMPLETE); if (c == '\t' && usetab()) - selfinsert(); + return selfinsert(args); else - docomplete(COMP_COMPLETE); + return docomplete(COMP_COMPLETE); } /**/ -void -listchoices(void) +int +listchoices(char **args) { - usemenu = isset(MENUCOMPLETE); + usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); - docomplete(COMP_LIST_COMPLETE); + return docomplete(COMP_LIST_COMPLETE); } /**/ -void -spellword(void) +int +spellword(char **args) { usemenu = useglob = 0; - docomplete(COMP_SPELL); + return docomplete(COMP_SPELL); } /**/ -void -deletecharorlist(void) +int +deletecharorlist(char **args) { - char **mc = menucur; - - usemenu = isset(MENUCOMPLETE); + usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); - if (cs != ll) - deletechar(); - else - docomplete(COMP_LIST_COMPLETE); - menucur = mc; + if (cs != ll) { + fixsuffix(); + invalidatelist(); + return deletechar(args); + } + return docomplete(COMP_LIST_COMPLETE); } /**/ -void -expandword(void) +int +expandword(char **args) { usemenu = useglob = 0; if (c == '\t' && usetab()) - selfinsert(); + return selfinsert(args); else - docomplete(COMP_EXPAND); + return docomplete(COMP_EXPAND); } /**/ -void -expandorcomplete(void) +int +expandorcomplete(char **args) { - usemenu = isset(MENUCOMPLETE); + usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); if (c == '\t' && usetab()) - selfinsert(); - else - docomplete(COMP_EXPAND_COMPLETE); + return selfinsert(args); + else { + int ret; + if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) { + bashlistfirst = 1; + ret = docomplete(COMP_LIST_COMPLETE); + bashlistfirst = 0; + lastambig = 2; + } else + ret = docomplete(COMP_EXPAND_COMPLETE); + return ret; + } } /**/ -void -menuexpandorcomplete(void) +int +menuexpandorcomplete(char **args) { usemenu = 1; useglob = isset(GLOBCOMPLETE); if (c == '\t' && usetab()) - selfinsert(); + return selfinsert(args); else - docomplete(COMP_EXPAND_COMPLETE); + return docomplete(COMP_EXPAND_COMPLETE); } /**/ -void -listexpand(void) +int +listexpand(char **args) { - usemenu = isset(MENUCOMPLETE); + usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); - docomplete(COMP_LIST_EXPAND); + return docomplete(COMP_LIST_EXPAND); } /**/ -void -reversemenucomplete(void) +mod_export int +reversemenucomplete(char **args) { - if (!menucmp) { - menucomplete(); - return; - } - HEAPALLOC { - if (menucur == amatches) - menucur = amatches + nmatches - 1; - else - menucur--; - metafy_line(); - do_single(*menucur); - unmetafy_line(); - } LASTALLOC; -} + if (!menucmp) + return menucomplete(args); -/* Accepts the current completion and starts a new arg, * - * with the next completions. This gives you a way to * - * accept several selections from the list of matches. */ + runhookdef(REVERSEMENUHOOK, NULL); + return 0; +} /**/ -void -acceptandmenucomplete(void) +int +acceptandmenucomplete(char **args) { - if (!menucmp) { - feep(); - return; - } - 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); - menupos = cs; - menuend = cs + (lsuf ? strlen(lsuf) : 0); - menulen = 0; - menuinsc = 0; - menuwe = 1; - menucomplete(); + if (!menucmp) + return 1; + runhookdef(ACCEPTCOMPHOOK, NULL); + return menucomplete(args); } /* These are flags saying if we are completing in the command * - * position or in a redirection. */ - -static int lincmd, linredir; + * position, in a redirection, or in a parameter expansion. */ -/* Non-zero if the last completion done was ambiguous (used to find * - * out if AUTOMENU should start). More precisely, it's nonzero after * - * successfully doing any completion, unless the completion was * - * unambiguous and did not cause the display of a completion list. * - * From the other point of view, it's nonzero iff AUTOMENU (if set) * - * should kick in on another completion. */ - -static int lastambig; +/**/ +mod_export int lincmd, linredir, linarr; -/* 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. */ +/* The string for the redirection operator. */ -static int haswhat; +/**/ +mod_export char *rdstr; -/* We have a suffix to add (given with compctl -S). */ +/* This holds the name of the current command (used to find the right * + * compctl). */ -#define HAS_SUFFIX 1 +/**/ +mod_export char *cmdstr; -/* We have filenames in the completion list. */ +/* This hold the name of the variable we are working on. */ -#define HAS_FILES 2 +/**/ +mod_export char *varname; -/* 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. */ +/* != 0 if we are in a subscript */ -#define HAS_MISC 4 +/**/ +mod_export int insubscr; -/* This is set if we have filenames in the completion list that were * - * generated by a globcompletion pattern. */ +/* Parameter pointer for completing keys of an assoc array. */ -#define HAS_PATHPAT 8 +/**/ +mod_export Param keypm; +/* 1 if we are completing in a quoted string (or inside `...`) */ -/* This holds the naem of the current command (used to find the right * - * compctl). */ +/**/ +mod_export int instring, inbackt; -static char *cmdstr; +/* Convenience macro for calling bslashquote() (formerly quotename()). * + * This uses the instring variable above. */ +#define quotename(s, e) bslashquote(s, e, instring) /* Check if the given string is the name of a parameter and if this * * parameter is one worth expanding. */ @@ -429,7 +372,7 @@ checkparams(char *p) e = 1; } return (n == 1) ? (getsparam(p) != NULL) : - (!menucmp && e && isset(RECEXACT)); + (!menucmp && e && (!hascompmod || isset(RECEXACT))); } /* Check if the given string has wildcards. The difficulty is that we * @@ -489,36 +432,112 @@ cmphaswilds(char *str) return 0; } -/* The main entry point for completion. */ +/* Check if we have to complete a parameter name. */ /**/ -static void -docomplete(int lst) +char * +parambeg(char *s) { - char *s, *ol; - int olst = lst, chl = 0, ne = noerrs, ocs; + char *p; + + /* Try to find a `$'. */ + for (p = s + offs; p > s && *p != String && *p != Qstring; p--); + if (*p == String || *p == Qstring) { + /* Handle $$'s */ + while (p > s && (p[-1] == String || p[-1] == Qstring)) + p--; + while ((p[1] == String || p[1] == Qstring) && + (p[2] == String || p[2] == Qstring)) + p += 2; + } + if ((*p == String || *p == Qstring) && p[1] != Inpar && p[1] != Inbrack) { + /* This is really a parameter expression (not $(...) or $[...]). */ + char *b = p + 1, *e = b; + int n = 0, br = 1, nest = 0; - /* If we are doing a menu-completion... */ + if (*b == Inbrace) { + char *tb = b; + + /* If this is a ${...}, see if we are before the '}'. */ + if (!skipparens(Inbrace, Outbrace, &tb)) + return NULL; + + /* Ignore the possible (...) flags. */ + b++, br++; + n = skipparens(Inpar, Outpar, &b); + + for (tb = p - 1; tb > s && *tb != Outbrace && *tb != Inbrace; tb--); + if (tb > s && *tb == Inbrace && (tb[-1] == String || *tb == Qstring)) + nest = 1; + } + + /* Ignore the stuff before the parameter name. */ + for (; *b; b++) + if (*b != '^' && *b != Hat && + *b != '=' && *b != Equals && + *b != '~' && *b != Tilde) + break; + if (*b == '#' || *b == Pound || *b == '+') + b++; - if (menucmp && lst != COMP_LIST_EXPAND) { - do_menucmp(lst); - return; + e = b; + if (br) { + while (*e == Dnull) + e++; + } + /* Find the end of the name. */ + if (*e == Quest || *e == Star || *e == String || *e == Qstring || + *e == '?' || *e == '*' || *e == '$' || + *e == '-' || *e == '!' || *e == '@') + e++; + else if (idigit(*e)) + while (idigit(*e)) + e++; + else if (iident(*e)) + while (iident(*e)) + e++; + + /* Now make sure that the cursor is inside the name. */ + if (offs <= e - s && offs >= b - s && n <= 0) { + if (br) { + p = e; + while (*p == Dnull) + p++; + } + /* It is. */ + return b; + } } + return NULL; +} - /* Check if we have to start a menu-completion (via automenu). */ +/* The main entry point for completion. */ - if ((amenu = (isset(AUTOMENU) && lastambig))) - usemenu = 1; +/**/ +static int +docomplete(int lst) +{ + char *s, *ol; + int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0; + + if (undoing) + setlastline(); + + if (runhookdef(BEFORECOMPLETEHOOK, (void *) &lst)) + return 0; /* Expand history references before starting completion. If anything * * changed, do no more. */ if (doexpandhist()) - return; + return 0; metafy_line(); ocs = cs; + origline = dupstring((char *) line); + origcs = cs; + origll = ll; if (!isfirstln && chline != NULL) { /* If we are completing in a multi-line buffer (which was not * * taken from the history), we have to prepend the stuff saved * @@ -535,6 +554,12 @@ docomplete(int lst) ol = NULL; inwhat = IN_NOTHING; qword = NULL; + zsfree(qipre); + qipre = ztrdup(""); + zsfree(qisuf); + qisuf = ztrdup(""); + zsfree(autoq); + autoq = NULL; /* Get the word to complete. */ noerrs = 1; s = get_comp_string(); @@ -558,8 +583,7 @@ docomplete(int lst) ll = strlen((char *) line); cs = ocs; unmetafy_line(); - feep(); - return; + return 1; } ocs = cs; cs = 0; @@ -580,21 +604,18 @@ 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 (isset(RECEXACT)) + if (cmdnamtab->getnode(cmdnamtab, q) || hashcmd(q, pathchecked)) { + if (!hascompmod || isset(RECEXACT)) lst = COMP_EXPAND; else { int t0, n = 0; - char *fc; struct hashnode *hn; for (t0 = cmdnamtab->hsize - 1; t0 >= 0; t0--) for (hn = cmdnamtab->nodes[t0]; hn; hn = hn->next) { - if (strpfx(q, hn->nam) && (fc = findcmd(hn->nam))) { - zsfree(fc); + if (strpfx(q, hn->nam) && findcmd(hn->nam, 0)) n++; - } if (n == 2) break; } @@ -602,10 +623,11 @@ docomplete(int lst) if (n == 1) lst = COMP_EXPAND; } + } } if (lst == COMP_EXPAND_COMPLETE) do { - /* check if there is a parameter expresiion. */ + /* Check if there is a parameter expression. */ for (; *q && *q != String; q++); if (*q == String && q[1] != Inpar && q[1] != Inbrack) { if (*++q == Inbrace) { @@ -671,19 +693,20 @@ docomplete(int lst) inwhat = IN_CMD; if (lst == COMP_SPELL) { - char *x, *q; + char *w = dupstring(origword), *x, *q, *ox; - for (q = s; *q; q++) + for (q = w; *q; q++) if (INULL(*q)) *q = Nularg; cs = wb; foredel(we - wb); - HEAPALLOC { - untokenize(x = dupstring(s)); - if (*s == Tilde || *s == Equals || *s == String) - *x = *s; - spckword(&x, 0, lincmd, 0); - } LASTALLOC; + + untokenize(x = ox = dupstring(w)); + if (*w == Tilde || *w == Equals || *w == String) + *x = *w; + spckword(&x, 0, lincmd, 0); + ret = !strcmp(x, ox); + untokenize(x); inststr(x); } else if (COMP_ISEXPAND(lst)) { @@ -693,7 +716,7 @@ docomplete(int lst) int ocs = cs, ne = noerrs; noerrs = 1; - doexpansion(s, lst, olst, lincmd); + ret = doexpansion(origword, lst, olst, lincmd); lastambig = 0; noerrs = ne; @@ -701,62 +724,43 @@ docomplete(int lst) * change the command line, do completion. */ if (olst == COMP_EXPAND_COMPLETE && !strcmp(ol, (char *)line)) { - char *p; - cs = ocs; errflag = 0; - p = s; - if (*p == Tilde || *p == Equals) - p++; - for (; *p; 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); - } + if (!compfunc) { + char *p; + + p = s; + if (*p == Tilde || *p == Equals) + p++; + for (; *p; p++) + if (itok(*p)) { + if (*p != String && *p != Qstring) + *p = ztokens[*p - Pound]; + else if (p[1] == Inbrace) + p++, skipparens(Inbrace, Outbrace, &p); + } + } + ret = docompletion(s, lst, lincmd); + } else if (ret) + clearlist = 1; } else /* Just do completion. */ - docompletion(s, lst, lincmd, 0); + ret = docompletion(s, lst, lincmd); zsfree(s); - } + } else + ret = 1; /* Reset the lexer state, pop the heap. */ lexrestore(); popheap(); zsfree(qword); unmetafy_line(); -} -/* Do completion, given that we are in the middle of a menu completion. We * - * don't need to generate a list of matches, because that's already been * - * done by previous commands. We will either list the completions, or * - * insert the next completion. */ + runhookdef(AFTERCOMPLETEHOOK, (void *) &lst); -/**/ -static void -do_menucmp(int lst) -{ - /* Just list the matches if the list was requested. */ - if (lst == COMP_LIST_COMPLETE) { - showinglist = -2; - return; - } - /* Otherwise go to the next match in the array... */ - HEAPALLOC { - if (!*++menucur) - menucur = amatches; - /* ... and insert it into the command line. */ - metafy_line(); - do_single(*menucur); - unmetafy_line(); - } LASTALLOC; + return ret; } -/* 1 if we are completing in a string */ -static int instring; - /* 1 if we are completing the prefix */ static int comppref; @@ -790,7 +794,7 @@ addx(char **ptmp) (instring && (line[cs] == '"' || line[cs] == '\'')) || (addspace = (comppref && !iblank(line[cs])))) { *ptmp = (char *)line; - line = (unsigned char *)halloc(strlen((char *)line) + 3 + addspace); + line = (unsigned char *)zhalloc(strlen((char *)line) + 3 + addspace); memcpy(line, *ptmp, cs); line[cs] = 'x'; if (addspace) @@ -806,11 +810,11 @@ addx(char **ptmp) /* Like dupstring, but add an extra space at the end of the string. */ /**/ -static char * +mod_export char * dupstrspace(const char *str) { int len = strlen((char *)str); - char *t = (char *)ncalloc(len + 2); + char *t = (char *) hcalloc(len + 2); strcpy(t, str); strcpy(t+len, " "); return t; @@ -824,7 +828,7 @@ dupstrspace(const char *str) * functions (there's one for each direction). */ /**/ -static void +mod_export void metafy_line(void) { int len = ll; @@ -840,13 +844,71 @@ metafy_line(void) } /**/ -static void +mod_export void unmetafy_line(void) { cs = ztrsub((char *) line + cs, (char *) line); (void) unmetafy((char *) line, &ll); } +/* Free a brinfo list. */ + +/**/ +mod_export void +freebrinfo(Brinfo p) +{ + Brinfo n; + + while (p) { + n = p->next; + zsfree(p->str); + zfree(p, sizeof(*p)); + + p = n; + } +} + +/* Duplicate a brinfo list. */ + +/**/ +mod_export Brinfo +dupbrinfo(Brinfo p, Brinfo *last, int heap) +{ + Brinfo ret = NULL, *q = &ret, n = NULL; + + while (p) { + n = *q = (heap ? (Brinfo) zhalloc(sizeof(*n)) : + (Brinfo) zalloc(sizeof(*n))); + q = &(n->next); + + n->next = NULL; + n->str = (heap ? dupstring(p->str) : ztrdup(p->str)); + n->pos = p->pos; + n->qpos = p->qpos; + n->curpos = p->curpos; + + p = p->next; + } + if (last) + *last = n; + + return ret; +} + +/* This is a bit like has_token(), but ignores nulls. */ + +static int +has_real_token(const char *s) +{ + while (*s) { + if (itok(*s) && !INULL(*s)) + return 1; + s++; + } + return 0; +} + + /* Lasciate ogni speranza. * * This function is a nightmare. It works, but I'm sure that nobody really * * understands why. The problem is: to make it cleaner we would need * @@ -856,16 +918,24 @@ unmetafy_line(void) static char * get_comp_string(void) { - int t0, tt0, i, j, k, cp, rd, sl, ocs; + int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct, varq = 0; char *s = NULL, *linptr, *tmp, *p, *tt = NULL; - complinbrace = 0; + freebrinfo(brbeg); + freebrinfo(brend); + brbeg = lastbrbeg = brend = lastbrend = NULL; + nbrbeg = nbrend = 0; + zsfree(lastprebr); + zsfree(lastpostbr); + lastprebr = lastpostbr = NULL; + /* This global flag is used to signal the lexer code if it should * * expand aliases or not. */ noaliases = isset(COMPLETEALIASES); /* Find out if we are somewhere in a `string', i.e. inside '...', * - * "...", `...`, or ((...)). */ + * "...", `...`, or ((...)). Nowadays this is only used to find * + * out if we are inside `...`. */ for (i = j = k = 0, p = (char *)line; p < (char *)line + cs; p++) if (*p == '`' && !(k & 1)) @@ -876,144 +946,185 @@ get_comp_string(void) k++; else if (*p == '\\' && p[1] && !(k & 1)) p++; - instring = (j & 1) ? 2 : (k & 1); + inbackt = (i & 1); + instring = 0; addx(&tmp); - if (instring) { - /* Yes, we are in a string. */ - if (!tmp) { - tmp = (char *)line; - line = (unsigned char *) dupstring((char *) line); - } - /* Now remove the quotes. * - * What?? Why that?? Well, we want to be able to complete * - * inside strings. The lexer code gives us no help here, * - * so we have to cheat. We remove the quotes, the lexer * - * will than treat the words in the strings normally and we * - * can complete them. * - * This is completely the wrong thing to do, but it's * - * occasionally useful, and we can't handle quotes properly * - * yet anyway. */ - for (p = (char *)line; *p; p++) - if (*p == '"' || *p == '\'') - *p = ' '; - } linptr = (char *)line; pushheap(); - HEAPALLOC { - start: - inwhat = IN_NOTHING; - /* Now set up the lexer and start it. */ - parbegin = parend = -1; - lincmd = incmdpos; - linredir = inredir; - zsfree(cmdstr); - cmdstr = NULL; - zleparse = 1; - clwpos = -1; - lexsave(); - inpush(dupstrspace((char *) linptr), 0, NULL); - strinbeg(); - stophist = 2; - i = tt0 = cp = rd = 0; - - /* This loop is possibly the wrong way to do this. It goes through * - * the previously massaged command line using the lexer. It stores * - * each token in each command (commands being regarded, roughly, as * - * being separated by tokens | & &! |& || &&). The loop stops when * - * the end of the command containing the cursor is reached. It's a * - * simple way to do things, but suffers from an inability to * - * distinguish actual command arguments from, for example, * - * filenames in redirections. (But note that code elsewhere checks * - * if we are completing *in* a redirection.) The only way to fix * - * this would be to pass the command line through the parser too, * - * and get the arguments that way. Maybe in 3.1... */ - do { - lincmd = incmdpos; - linredir = inredir; - /* Get the next token. */ - ctxtlex(); - if (tok == DINPAR) - tokstr = NULL; - - /* We reached the end. */ - if (tok == ENDINPUT) - break; - if (tok == BAR || tok == AMPER || - tok == BARAMP || tok == AMPERBANG || - ((tok == DBAR || tok == DAMPER) && !incond)) { - /* This is one of the things that separate commands. If we * - * already have the things we need (e.g. the token strings), * - * leave the loop. */ - if (tt) - break; - /* Otherwise reset the variables we are collecting data in. */ - i = tt0 = cp = rd = 0; - } - if (lincmd && tok == STRING) { - /* The lexer says, this token is in command position, so * - * store the token string (to find the right compctl). */ - zsfree(cmdstr); - cmdstr = ztrdup(tokstr); - i = 0; - } - if (!zleparse && !tt0) { - /* This is done when the lexer reached the word the cursor is on. */ - tt = tokstr ? dupstring(tokstr) : NULL; - /* If we added a `x', remove it. */ - if (addedx && tt) - chuck(tt + cs - wb); - tt0 = tok; - /* Store the number of this word. */ - clwpos = i; - cp = lincmd; - rd = linredir; - if (inwhat == IN_NOTHING && incond) - inwhat = IN_COND; - } + + start: + inwhat = IN_NOTHING; + /* Now set up the lexer and start it. */ + parbegin = parend = -1; + lincmd = incmdpos; + linredir = inredir; + zsfree(cmdstr); + cmdstr = NULL; + zsfree(varname); + varname = NULL; + insubscr = 0; + zleparse = 1; + clwpos = -1; + lexsave(); + inpush(dupstrspace((char *) linptr), 0, NULL); + strinbeg(0); + i = tt0 = cp = rd = ins = oins = linarr = parct = ia = 0; + + /* This loop is possibly the wrong way to do this. It goes through * + * the previously massaged command line using the lexer. It stores * + * each token in each command (commands being regarded, roughly, as * + * being separated by tokens | & &! |& || &&). The loop stops when * + * the end of the command containing the cursor is reached. It's a * + * simple way to do things, but suffers from an inability to * + * distinguish actual command arguments from, for example, * + * filenames in redirections. (But note that code elsewhere checks * + * if we are completing *in* a redirection.) The only way to fix * + * this would be to pass the command line through the parser too, * + * and get the arguments that way. Maybe in 3.1... */ + do { + lincmd = ((incmdpos && !ins && !incond) || (oins == 2 && i == 2) || + (ins == 3 && i == 1)); + linredir = (inredir && !ins); + oins = ins; + /* Get the next token. */ + if (linarr) + incmdpos = 0; + ctxtlex(); + + if (tok == LEXERR) { if (!tokstr) - continue; - /* We need to store the token strings of all words (for some of * - * the more complicated compctl -x things). They are stored in * - * the clwords array. Make this array big enough. */ - if (i + 1 == clwsize) { - int n; - clwords = (char **)realloc(clwords, - (clwsize *= 2) * sizeof(char *)); - for(n = clwsize; --n > i; ) - clwords[n] = NULL; + break; + for (j = 0, p = tokstr; *p; p++) + if (*p == Snull || *p == Dnull) + j++; + if (j & 1) { + if (lincmd && strchr(tokstr, '=')) { + varq = 1; + tok = ENVSTRING; + } else + tok = STRING; } - zsfree(clwords[i]); - /* And store the current token string. */ - clwords[i] = ztrdup(tokstr); - sl = strlen(tokstr); - /* Sometimes the lexer gives us token strings ending with * - * spaces we delete the spaces. */ - while (sl && clwords[i][sl - 1] == ' ' && - (sl < 2 || (clwords[i][sl - 2] != Bnull && - clwords[i][sl - 2] != Meta))) - clwords[i][--sl] = '\0'; - /* If this is the word the cursor is in and we added a `x', * - * remove it. */ - if (clwpos == i++ && addedx) - chuck(&clwords[i - 1][((cs - wb) >= sl) ? - (sl - 1) : (cs - wb)]); - } while (tok != LEXERR && tok != ENDINPUT && - (tok != SEPER || (zleparse && !tt0))); - /* Calculate the number of words stored in the clwords array. */ - clwnum = (tt || !i) ? i : i - 1; - zsfree(clwords[clwnum]); - clwords[clwnum] = NULL; - t0 = tt0; + } else if (tok == ENVSTRING) + varq = 0; + if (tok == ENVARRAY) { + linarr = 1; + zsfree(varname); + varname = ztrdup(tokstr); + } else if (tok == INPAR) + parct++; + else if (tok == OUTPAR) { + if (parct) + parct--; + else + linarr = 0; + } + if (inredir) + rdstr = tokstrings[tok]; + if (tok == DINPAR) + tokstr = NULL; + + /* We reached the end. */ + if (tok == ENDINPUT) + break; + if ((ins && (tok == DO || tok == SEPER)) || + (ins == 2 && i == 2) || (ins == 3 && i == 3) || + tok == BAR || tok == AMPER || + tok == BARAMP || tok == AMPERBANG || + ((tok == DBAR || tok == DAMPER) && !incond)) { + /* This is one of the things that separate commands. If we * + * already have the things we need (e.g. the token strings), * + * leave the loop. */ + if (tt) + break; + /* Otherwise reset the variables we are collecting data in. */ + i = tt0 = cp = rd = ins = 0; + } + if (lincmd && (tok == STRING || tok == FOR || tok == FOREACH || + tok == SELECT || tok == REPEAT || tok == CASE)) { + /* The lexer says, this token is in command position, so * + * store the token string (to find the right compctl). */ + ins = (tok == REPEAT ? 2 : (tok != STRING)); + zsfree(cmdstr); + cmdstr = ztrdup(tokstr); + i = 0; + } + if (!zleparse && !tt0) { + /* This is done when the lexer reached the word the cursor is on. */ + tt = tokstr ? dupstring(tokstr) : NULL; + /* If we added a `x', remove it. */ + if (addedx && tt) + chuck(tt + cs - wb); + tt0 = tok; + /* Store the number of this word. */ + clwpos = i; + cp = lincmd; + rd = linredir; + ia = linarr; + if (inwhat == IN_NOTHING && incond) + inwhat = IN_COND; + } else if (linredir) + continue; + if (incond) { + if (tok == DBAR) + tokstr = "||"; + else if (tok == DAMPER) + tokstr = "&&"; + } + if (!tokstr) + continue; + /* Hack to allow completion after `repeat n do'. */ + if (oins == 2 && !i && !strcmp(tokstr, "do")) + ins = 3; + /* We need to store the token strings of all words (for some of * + * the more complicated compctl -x things). They are stored in * + * the clwords array. Make this array big enough. */ + if (i + 1 == clwsize) { + int n; + clwords = (char **)realloc(clwords, + (clwsize *= 2) * sizeof(char *)); + for(n = clwsize; --n > i; ) + clwords[n] = NULL; + } + zsfree(clwords[i]); + /* And store the current token string. */ + clwords[i] = ztrdup(tokstr); + sl = strlen(tokstr); + /* Sometimes the lexer gives us token strings ending with * + * spaces we delete the spaces. */ + while (sl && clwords[i][sl - 1] == ' ' && + (sl < 2 || (clwords[i][sl - 2] != Bnull && + clwords[i][sl - 2] != Meta))) + clwords[i][--sl] = '\0'; + /* If this is the word the cursor is in and we added a `x', * + * remove it. */ + if (clwpos == i++ && addedx) + chuck(&clwords[i - 1][((cs - wb) >= sl) ? + (sl - 1) : (cs - wb)]); + } while (tok != LEXERR && tok != ENDINPUT && + (tok != SEPER || (zleparse && !tt0))); + /* Calculate the number of words stored in the clwords array. */ + clwnum = (tt || !i) ? i : i - 1; + zsfree(clwords[clwnum]); + clwords[clwnum] = NULL; + t0 = tt0; + if (ia) { + lincmd = linredir = 0; + inwhat = IN_ENV; + } else { lincmd = cp; linredir = rd; - strinend(); - inpop(); - errflag = zleparse = 0; - if (parbegin != -1) { - /* We are in command or process substitution */ - if (parend >= 0 && !tmp) - line = (unsigned char *) dupstring(tmp = (char *)line); - linptr = (char *) line + ll + addedx - parbegin + 1; + } + strinend(); + inpop(); + errflag = zleparse = 0; + if (parbegin != -1) { + /* We are in command or process substitution if we are not in + * a $((...)). */ + if (parend >= 0 && !tmp) + line = (unsigned char *) dupstring(tmp = (char *)line); + linptr = (char *) line + ll + addedx - parbegin + 1; + if ((linptr - (char *) line) < 3 || *linptr != '(' || + linptr[-1] != '(' || linptr[-2] != '$') { if (parend >= 0) { ll -= parend; line[ll + addedx] = '\0'; @@ -1021,2467 +1132,566 @@ get_comp_string(void) lexrestore(); goto start; } + } + + if (inwhat == IN_MATH) + s = NULL; + else if (!t0 || t0 == ENDINPUT) { + /* There was no word (empty line). */ + s = ztrdup(""); + we = wb = cs; + clwpos = clwnum; + t0 = STRING; + } else if (t0 == STRING) { + /* We found a simple string. */ + s = ztrdup(clwords[clwpos]); + } else if (t0 == ENVSTRING) { + char sav; + /* The cursor was inside a parameter assignment. */ + + if (varq) + tt = clwords[clwpos]; - if (inwhat == IN_MATH) + for (s = tt; iident(*s); s++); + sav = *s; + *s = '\0'; + zsfree(varname); + varname = ztrdup(tt); + *s = sav; + if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb) { s = NULL; - else if (!t0 || t0 == ENDINPUT) { - /* There was no word (empty line). */ - s = ztrdup(""); - we = wb = cs; - clwpos = clwnum; + inwhat = IN_MATH; + if ((keypm = (Param) paramtab->getnode(paramtab, varname)) && + (keypm->flags & PM_HASHED)) + insubscr = 2; + else + insubscr = 1; + } else if (*s == '=' && cs > wb + (s - tt)) { + s++; + wb += s - tt; t0 = STRING; - } else if (t0 == STRING) { - /* We found a simple string. */ - s = ztrdup(clwords[clwpos]); - } else if (t0 == ENVSTRING) { - /* The cursor was inside a parameter assignment. */ - for (s = tt; iident(*s); s++); - if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb) - s = NULL, inwhat = IN_MATH; - else if (*s == '=') { - s++; - wb += s - tt; - t0 = STRING; - s = ztrdup(s); - inwhat = IN_ENV; - } - lincmd = 1; + s = ztrdup(s); + inwhat = IN_ENV; } - if (we > ll) - we = ll; - tt = (char *)line; + lincmd = 1; + } + if (we > ll) + we = ll; + tt = (char *)line; + if (tmp) { + line = (unsigned char *)tmp; + ll = strlen((char *)line); + } + if (t0 != STRING && inwhat != IN_MATH) { if (tmp) { - line = (unsigned char *)tmp; - ll = strlen((char *)line); - } - if (t0 != STRING && inwhat != IN_MATH) { - if (tmp) { - tmp = NULL; - linptr = (char *)line; - lexrestore(); - goto start; - } - feep(); - noaliases = 0; + tmp = NULL; + linptr = (char *)line; lexrestore(); - LASTALLOC_RETURN NULL; + addedx = 0; + goto start; } - noaliases = 0; + lexrestore(); + return NULL; + } + + noaliases = 0; - /* Check if we are in an array subscript. We simply assume that * - * we are in a subscript if we are in brackets. Correct solution * - * is very difficult. This is quite close, but gets things like * - * foo[_ wrong (note no $). If we are in a subscript, treat it * - * as being in math. */ - if (inwhat != IN_MATH) { - int i = 0; - for (tt = s; ++tt < s + cs - wb;) - if (*tt == Inbrack) - i++; - else if (i && *tt == Outbrack) - i--; - if (i) - inwhat = IN_MATH; + /* Check if we are in an array subscript. We simply assume that * + * we are in a subscript if we are in brackets. Correct solution * + * is very difficult. This is quite close, but gets things like * + * foo[_ wrong (note no $). If we are in a subscript, treat it * + * as being in math. */ + if (inwhat != IN_MATH) { + int i = 0; + char *nnb = (iident(*s) ? s : s + 1), *nb = NULL, *ne = NULL; + + for (tt = s; ++tt < s + cs - wb;) + if (*tt == Inbrack) { + i++; + nb = nnb; + ne = tt; + } else if (i && *tt == Outbrack) + i--; + else if (!iident(*tt)) + nnb = tt + 1; + if (i) { + inwhat = IN_MATH; + insubscr = 1; + if (nb < ne) { + char sav = *ne; + *ne = '\0'; + zsfree(varname); + varname = ztrdup(nb); + *ne = sav; + if ((keypm = (Param) paramtab->getnode(paramtab, varname)) && + (keypm->flags & PM_HASHED)) + insubscr = 2; + } } - if (inwhat == IN_MATH) { - /* In mathematical expression, we complete parameter names (even * - * if they don't have a `$' in front of them). So we have to * - * find that name. */ + } + if (inwhat == IN_MATH) { + if (compfunc || insubscr == 2) { + int lev; + char *p; + + for (wb = cs - 1, lev = 0; wb > 0; wb--) + if (line[wb] == ']' || line[wb] == ')') + lev++; + else if (line[wb] == '[') { + if (!lev--) + break; + } else if (line[wb] == '(') { + if (!lev && line[wb - 1] == '(') + break; + if (lev) + lev--; + } + p = (char *) line + wb; + wb++; + if (wb && (*p == '[' || *p == '(') && + !skipparens(*p, (*p == '[' ? ']' : ')'), &p)) { + we = (p - (char *) line) - 1; + if (insubscr == 2) + insubscr = 3; + } + } else { + /* In mathematical expression, we complete parameter names * + * (even if they don't have a `$' in front of them). So we * + * have to find that name. */ for (we = cs; iident(line[we]); we++); for (wb = cs; --wb >= 0 && iident(line[wb]);); wb++; - zsfree(s); - s = zalloc(we - wb + 1); - strncpy(s, (char *) line + wb, we - wb); - s[we - wb] = '\0'; } - /* This variable will hold the current word in quoted form. */ - qword = ztrdup(s); - /* While building the quoted form, we also clean up the command line. */ - offs = cs - wb; - for (p = s, tt = qword, i = wb; *p; p++, tt++, i++) - if (INULL(*p)) { - if (i < cs) - offs--; - if (p[1] || *p != Bnull) { - if (*p == Bnull) { - *tt = '\\'; - if (cs == i + 1) - cs++, offs++; - } else { - ocs = cs; - cs = i; - foredel(1); - chuck(tt--); - if ((cs = ocs) > i--) - cs--; - we--; - } + zsfree(s); + s = zalloc(we - wb + 1); + strncpy(s, (char *) line + wb, we - wb); + s[we - wb] = '\0'; + if (wb > 2 && line[wb - 1] == '[' && iident(line[wb - 2])) { + int i = wb - 3; + unsigned char sav = line[wb - 1]; + + while (i >= 0 && iident(line[i])) + i--; + + line[wb - 1] = '\0'; + zsfree(varname); + varname = ztrdup((char *) line + i + 1); + line[wb - 1] = sav; + if ((keypm = (Param) paramtab->getnode(paramtab, varname)) && + (keypm->flags & PM_HASHED)) { + if (insubscr != 3) + insubscr = 2; + } else + insubscr = 1; + } + } + /* This variable will hold the current word in quoted form. */ + qword = ztrdup(s); + offs = cs - wb; + if ((p = parambeg(s))) { + for (p = s; *p; p++) + if (*p == Dnull) + *p = '"'; + else if (*p == Snull) + *p = '\''; + } + if ((*s == Snull || *s == Dnull) && !has_real_token(s + 1)) { + char *q = (*s == Snull ? "'" : "\""), *n = tricat(qipre, q, ""); + int sl = strlen(s); + + instring = (*s == Snull ? 1 : 2); + zsfree(qipre); + qipre = n; + if (sl > 1 && s[sl - 1] == *s) { + n = tricat(q, qisuf, ""); + zsfree(qisuf); + qisuf = n; + } + autoq = ztrdup(q); + } + /* While building the quoted form, we also clean up the command line. */ + for (p = s, tt = qword, i = wb; *p; p++, tt++, i++) + if (INULL(*p)) { + if (i < cs) + offs--; + if (p[1] || *p != Bnull) { + if (*p == Bnull) { + *tt = '\\'; + if (cs == i + 1) + cs++, offs++; } else { ocs = cs; - *tt = '\0'; - cs = we; - backdel(1); - if (ocs == we) - cs = we - 1; - else - cs = ocs; + cs = i; + foredel(1); + chuck(tt--); + if ((cs = ocs) > i--) + cs--; we--; } - chuck(p--); + } else { + ocs = cs; + *tt = '\0'; + cs = we; + backdel(1); + if (ocs == we) + cs = we - 1; + else + cs = ocs; + we--; } + chuck(p--); + } - if (!isset(IGNOREBRACES)) { - /* Try and deal with foo{xxx etc.; only simple cases - * (only one inbrace, completion after inbrace and before outbrace - * if present). - */ - int myoffs = isset(COMPLETEINWORD) ? offs : strlen(s); - tt = NULL; - /* First check the conditions mentioned above - * and locate opening brace + zsfree(origword); + origword = ztrdup(s); + + if (!isset(IGNOREBRACES)) { + /* Try and deal with foo{xxx etc. */ + char *curs = s + (isset(COMPLETEINWORD) ? offs : strlen(s)); + char *predup = dupstring(s), *dp = predup; + char *bbeg = NULL, *bend = NULL, *dbeg = NULL; + char *lastp = NULL, *firsts = NULL; + int cant = 0, begi = 0, boffs = offs, hascom = 0; + + for (i = 0, p = s; *p; p++, dp++, i++) { + /* careful, ${... is not a brace expansion... + * we try to get braces after a parameter expansion right, + * but this may fail sometimes. sorry. */ - for (i = 0, p = s; *p; p++, i++) { - /* careful, ${... is not a brace expansion... - * in fact, if it's got a substitution in it's too - * hard for us anyway. sorry. - */ - if (*p == String || *p == Qstring) { - tt = NULL; - break; - } else if (*p == Inbrace) { - if (tt) { - /* too many inbraces */ + if (*p == String || *p == Qstring) { + if (p[1] == Inbrace || p[1] == Inpar || p[1] == Inbrack) { + char *tp = p + 1; + + if (skipparens(*tp, (*tp == Inbrace ? Outbrace : + (*tp == Inpar ? Outpar : Outbrack)), + &tp)) { tt = NULL; break; } - tt = p; - } else if (*p == Outbrace && i < myoffs) { - /* outbrace is before cursor pos, so nothing to complete */ - tt = NULL; + i += tp - p; + dp += tp - p; + p = tp; + } else { + char *tp = p + 1; + + for (; *tp == '^' || *tp == Hat || + *tp == '=' || *tp == Equals || + *tp == '~' || *tp == Tilde || + *tp == '#' || *tp == Pound || *tp == '+'; + tp++); + if (*tp == Quest || *tp == Star || *tp == String || + *tp == Qstring || *tp == '?' || *tp == '*' || + *tp == '$' || *tp == '-' || *tp == '!' || + *tp == '@') + p++, i++; + else { + if (idigit(*tp)) + while (idigit(*tp)) + tp++; + else if (iident(*tp)) + while (iident(*tp)) + tp++; + else { + tt = NULL; + break; + } + if (*tp == Inbrace) { + cant = 1; + break; + } + tp--; + i += tp - p; + dp += tp - p; + p = tp; + } + } + } else if (p < curs) { + if (*p == Outbrace) { + cant = 1; break; } - } - - if (tt && tt < s + myoffs) { - /* Braces are go: delete opening brace */ - char *com = NULL; - chuck(tt); - offs--; - myoffs--; - - /* Look for text up to comma before cursor and delete it */ - for (i = tt - s, p = tt; *p && i < myoffs; p++, i++) - if (*p == Comma) - com = p; - if (com) { - i = com - tt + 1; - while (i--) - chuck(tt), offs--, myoffs--; + if (*p == Inbrace) { + if (bbeg) { + Brinfo new; + int len = bend - bbeg; + + new = (Brinfo) zalloc(sizeof(*new)); + nbrbeg++; + + new->next = NULL; + if (lastbrbeg) + lastbrbeg->next = new; + else + brbeg = new; + lastbrbeg = new; + + new->next = NULL; + new->str = ztrduppfx(bbeg, len); + untokenize(new->str); + new->pos = begi; + *dbeg = '\0'; + new->qpos = strlen(quotename(predup, NULL)); + *dbeg = '{'; + i -= len; + boffs -= len; + strcpy(dbeg, dbeg + len); + dp -= len; + } + bbeg = lastp = p; + dbeg = dp; + bend = p + 1; + begi = i; + } else if (*p == Comma && bbeg) { + bend = p + 1; + hascom = 1; } - - /* 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; + } else { + if (*p == Inbrace) { + cant = 1; + break; + } + if (p == curs) { + if (bbeg) { + Brinfo new; + int len = bend - bbeg; + + new = (Brinfo) zalloc(sizeof(*new)); + nbrbeg++; + + new->next = NULL; + if (lastbrbeg) + lastbrbeg->next = new; + else + brbeg = new; + lastbrbeg = new; + + new->str = ztrduppfx(bbeg, len); + untokenize(new->str); + new->pos = begi; + *dbeg = '\0'; + new->qpos = strlen(quotename(predup, NULL)); + *dbeg = '{'; + i -= len; + boffs -= len; + strcpy(dbeg, dbeg + len); + dp -= len; } - if (*p == Outbrace) - chuck(p); - else { - /* we are still waiting for an outbrace and maybe commas */ - complinbrace = 1; + bbeg = NULL; + } + if (*p == Comma) { + if (!bbeg) + bbeg = p; + hascom = 1; + } else if (*p == Outbrace) { + Brinfo new; + int len; + + if (!bbeg) + bbeg = p; + len = p + 1 - bbeg; + if (!firsts) + firsts = p + 1; + + new = (Brinfo) zalloc(sizeof(*new)); + nbrend++; + + if (!lastbrend) + lastbrend = new; + + new->next = brend; + brend = new; + + new->str = ztrduppfx(bbeg, len); + untokenize(new->str); + new->pos = dp - predup - len + 1; + new->qpos = len; + bbeg = NULL; } } } + if (cant) { + freebrinfo(brbeg); + freebrinfo(brend); + brbeg = lastbrbeg = brend = lastbrend = NULL; + nbrbeg = nbrend = 0; + } else { + if (p == curs && bbeg) { + Brinfo new; + int len = bend - bbeg; - } LASTALLOC; - lexrestore(); - - return (char *)s; -} - -/* Expand the current word. */ + new = (Brinfo) zalloc(sizeof(*new)); + nbrbeg++; -/**/ -static void -doexpansion(char *s, int lst, int olst, int explincmd) -{ - LinkList vl; - char *ss; - - DPUTS(useheap, "BUG: useheap in doexpansion()"); - HEAPALLOC { - pushheap(); - vl = newlinklist(); - ss = dupstring(s); - addlinknode(vl, ss); - prefork(vl, 0); - if (errflag) - goto end; - if ((lst == COMP_LIST_EXPAND) || (lst == COMP_EXPAND)) { - int ng = opts[NULLGLOB]; - - opts[NULLGLOB] = 1; - globlist(vl); - opts[NULLGLOB] = ng; - } - if (errflag) - goto end; - if (empty(vl) || !*(char *)peekfirst(vl)) { - if (!noerrs) - feep(); - goto end; - } - if (peekfirst(vl) == (void *) ss || - (olst == COMP_EXPAND_COMPLETE && - !nextnode(firstnode(vl)) && *s == Tilde && - (ss = dupstring(s), filesubstr(&ss, 0)) && - !strcmp(ss, (char *)peekfirst(vl)))) { - /* 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); - else - feep(); - goto end; - } - if (lst == COMP_LIST_EXPAND) { - /* Only the list of expansions was requested. */ - listlist(vl); - goto end; - } - /* Remove the current word and put the expansions there. */ - cs = wb; - foredel(we - wb); - while ((ss = (char *)ugetnode(vl))) { - untokenize(ss); - ss = quotename(ss, NULL, NULL, NULL); - inststr(ss); -#if 0 - if (nonempty(vl)) { - spaceinline(1); - line[cs++] = ' '; + new->next = NULL; + if (lastbrbeg) + lastbrbeg->next = new; + else + brbeg = new; + lastbrbeg = new; + + new->str = ztrduppfx(bbeg, len); + untokenize(new->str); + new->pos = begi; + *dbeg = '\0'; + new->qpos = strlen(quotename(predup, NULL)); + *dbeg = '{'; + boffs -= len; + strcpy(dbeg, dbeg + len); } -#endif - if (olst != COMP_EXPAND_COMPLETE || nonempty(vl) || - (cs && line[cs-1] != '/')) { - spaceinline(1); - line[cs++] = ' '; + if (brend) { + Brinfo bp, prev = NULL; + int p, l; + + for (bp = brend; bp; bp = bp->next) { + bp->prev = prev; + prev = bp; + p = bp->pos; + l = bp->qpos; + bp->pos = strlen(predup + p + l); + bp->qpos = strlen(quotename(predup + p + l, NULL)); + strcpy(predup + p, predup + p + l); + } } - } - end: - popheap(); - } LASTALLOC; -} + if (hascom) { + if (lastp) { + char sav = *lastp; -/* This is called from the lexer to give us word positions. */ - -/**/ -void -gotword(void) -{ - we = ll + 1 - inbufct + (addedx == 2 ? 1 : 0); - if (cs <= we) { - wb = ll - wordbeg + addedx; - zleparse = 0; + *lastp = '\0'; + untokenize(lastprebr = ztrdup(s)); + *lastp = sav; + } + if ((lastpostbr = ztrdup(firsts))) + untokenize(lastpostbr); + } + zsfree(s); + s = ztrdup(predup); + offs = boffs; + } } + lexrestore(); + + return (char *)s; } /* 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). */ + * to insert (if it is -1, the length is calculated here). * + * The last argument says if we should quote the string. */ /**/ -static void +mod_export int inststrlen(char *str, int move, int len) { - if (!len) - return; + if (!len || !str) + return 0; if (len == -1) len = strlen(str); spaceinline(len); strncpy((char *)(line + cs), str, len); if (move) cs += len; + return len; } -/* 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. */ - -/**/ -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); - 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; -} - -/* 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, *bp, *ep, *sp; - char *e = NULL, *tt, *te, *fc, **fm; - Comp cp = patcomp; - HashNode hn; - Param pm; - LinkList l = matches; - -/* - * 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; - } - 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; - } - } - if (test) { - fc = NULL; - if (addwhat == -7 && !(fc = findcmd(s))) - return; - if (fc) - zsfree(fc); - haswhat |= HAS_FILES; - - 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(strlen(ppre) + strlen(psuf) + sl + 1); - strcpy(tt, ppre); - strcat(tt, s); - strcat(tt, psuf); - untokenize(s = tt); - } - } - } 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 (!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 (addwhat == CC_QUOTEFLAG) { - te = s + pl; - s = quotename(s, &e, te, &pl); - sl = strlen(s); - } - if (test) - haswhat |= HAS_MISC; - } - if (!test) - return; - - if (ispattern) { - t = s; - } else { - t = s += pl; - if (*e) - t = s = dupstrpfx(t, e - t); - } - - 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; - } -} - -#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]; - register entry_col *ec = - object->zo_data.objdata_u.en_data.en_cols.en_cols_val; - register int l = minimum(ec->ec_value.ec_value_len, 39); - - memcpy(buf, ec->ec_value.ec_value_val, l); - buf[l] = '\0'; - - addmatch(dupstring(buf), NULL); - } - return 0; -} -#else -# ifdef HAVE_NIS -static int -match_username(int status, char *key, int keylen, char *val, int vallen, dopestring *data) -{ - if (errflag || status != YP_TRUE) - return 1; - - if (vallen > keylen && val[keylen] == ':') { - val[keylen] = '\0'; - addmatch(dupstring(val), NULL); - } - return 0; -} -# endif /* HAVE_NIS */ -#endif /* HAVE_NIS_PLUS */ - -/**/ -static void -maketildelist(void) -{ -#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS) - FILE *pwf; - char buf[BUFSIZ], *p; - int skipping; - -# ifndef HAVE_NIS_PLUS - char domain[YPMAXDOMAIN]; - struct ypall_callback cb; - dopestring data; - - data.s = fpre; - data.len = fpl; - /* Get potential matches from NIS and cull those without local accounts */ - if (getdomainname(domain, YPMAXDOMAIN) == 0) { - cb.foreach = (int (*)()) match_username; - cb.data = (char *)&data; - yp_all(domain, PASSWD_MAP, &cb); - } -# else /* HAVE_NIS_PLUS */ - /* Maybe we should turn this string into a #define'd constant...? */ - - nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH, - match_username, 0); -# endif - /* Don't forget the non-NIS matches from the flat passwd file */ - if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) { - skipping = 0; - while (fgets(buf, BUFSIZ, pwf) != NULL) { - if (strchr(buf, '\n') != NULL) { - if (!skipping) { - if ((p = strchr(buf, ':')) != NULL) { - *p = '\0'; - addmatch(dupstring(buf), NULL); - } - } else - skipping = 0; - } else - skipping = 1; - } - fclose(pwf); - } -#else /* no NIS or NIS_PLUS */ - /* add all the usernames to the named directory table */ - nameddirtab->filltable(nameddirtab); -#endif - - scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0, - 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. */ - -/**/ -static int -getcpat(char *wrd, int cpatindex, char *cpat, int class) -{ - char *str, *s, *t, *p; - int d = 0; - - if (!wrd || !*wrd) - return -1; - - cpat = rembslash(cpat); - - str = ztrdup(wrd); - untokenize(str); - if (!cpatindex) - cpatindex++, d = 0; - else if ((d = (cpatindex < 0))) - cpatindex = -cpatindex; - - for (s = d ? str + strlen(str) - 1 : str; - d ? (s >= str) : *s; - d ? s-- : s++) { - for (t = s, p = cpat; *t && *p; p++) { - if (class) { - if (*p == *s && !--cpatindex) { - zsfree(str); - return (int)(s - str + 1); - } - } else if (*t++ != *p) - break; - } - 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; - } - *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; -} - -/* Dump a hash table (without sorting). For each element the addmatch * - * function is called and at the beginning the addwhat variable is set. * - * This could be done using scanhashtable(), but this is easy and much * - * more efficient. */ - -/**/ -static void -dumphashtable(HashTable ht, int what) -{ - HashNode hn; - int i; - - addwhat = what; - - for (i = 0; i < ht->hsize; i++) - for (hn = ht->nodes[i]; hn; hn = hn->next) - addmatch(hn->nam, (char *) hn); - -} - -/* ScanFunc used by maketildelist() et al. */ - -/**/ -static void -addhnmatch(HashNode hn, int flags) -{ - addmatch(hn->nam, NULL); -} - -/* Perform expansion on the given string and return the result. * - * During this errors are not reported. */ - -/**/ -static char * -getreal(char *str) -{ - LinkList l = newlinklist(); - int ne = noerrs; - - noerrs = 1; - addlinknode(l, dupstring(str)); - prefork(l, 0); - noerrs = ne; - if (!errflag && nonempty(l)) - return ztrdup(peekfirst(l)); - errflag = 0; - - return ztrdup(str); -} - -/* This reads a directory and adds the files to the list of * - * matches. The parameters say which files should be added. */ - -/**/ -static void -gen_matches_files(int dirs, int execs, int all) -{ - DIR *d; - struct stat buf; - char *n, p[PATH_MAX], *q = NULL, *e; - LinkList l = NULL; - int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat; - - addwhat = execs ? -8 : -5; - opts[NULLGLOB] = 1; - - if (*psuf) { - /* If there is a path suffix, check if it doesn't have a `*' or * - * `)' at the end (this is used to determine if we should use * - * globbing). */ - q = psuf + strlen(psuf) - 1; - ns = !(*q == Star || *q == Outpar); - l = newlinklist(); - /* And generate only directory names. */ - dirs = 1; - all = execs = 0; - } - /* Open directory. */ - if ((d = opendir((prpre && *prpre) ? prpre : "."))) { - /* If we search only special files, prepare a path buffer for stat. */ - if (!all && prpre) { - strcpy(p, prpre); - q = p + strlen(prpre); - } - /* Fine, now read the directory. */ - while ((n = zreaddir(d, 1)) && !errflag) { - /* 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)) { - if (filecomp) - /* If we have a pattern for the filename check, use it. */ - test = domatch(n, filecomp, 0); - else { - /* Otherwise use the prefix and suffix strings directly. */ - e = n + strlen(n) - fsl; - if ((test = !strncmp(n, fpre, fpl))) - test = !strcmp(e, fsuf); - } - /* Filename didn't match? */ - if (!test) - continue; - if (!all) { - /* We still have to check the file type, so prepare * - * the path buffer by appending the filename. */ - strcpy(q, n); - /* And do the stat. */ - if (stat(p, &buf) < 0) - continue; - } - if (all || - (dirs && S_ISDIR(buf.st_mode)) || - (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) { - /* If we want all files or the file has the right type... */ - if (*psuf) { - /* We have to test for a path suffix. */ - int o = strlen(p), tt; - - /* Append it to the path buffer. */ - strcpy(p + o, psuf); - - /* Do we have to use globbing? */ - if (ispattern || (ns && isset(GLOBCOMPLETE))) { - /* Yes, so append a `*' if needed. */ - if (ns) { - int tl = strlen(p); - - p[tl] = Star; - p[tl + 1] = '\0'; - } - /* Do the globbing... */ - remnulargs(p); - addlinknode(l, p); - globlist(l); - /* And see if that produced a filename. */ - tt = nonempty(l); - while (ugetnode(l)); - } else - /* Otherwise just check, if we have access * - * to the file. */ - tt = !access(p, F_OK); - - p[o] = '\0'; - if (tt) - /* Ok, we can add the filename to the * - * list of matches. */ - addmatch(dupstring(n), NULL); - } else - /* We want all files, so just add the name * - * to the matches. */ - addmatch(dupstring(n), NULL); - } - } - } - closedir(d); - } - opts[NULLGLOB] = ng; - 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) -{ - static int delit, compadd; - - fixsuffix(); - HEAPALLOC { - pushheap(); - - /* Make sure we have the completion list and compctl. */ - if(makecomplist(s, incmd, &delit, &compadd, untokenized)) { - /* Error condition: feeeeeeeeeeeeep(). */ - feep(); - goto compend; - } - - if (lst == COMP_LIST_COMPLETE) - /* All this and the guy only wants to see the list, sigh. */ - 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]); - invalidatelist(); - } - } - - /* Print the explanation string if needed. */ - if (!showinglist && expl && nmatches != 1) { - int up; - - if (!nmatches) - feep(); - trashzle(); - - clearflag = (isset(USEZLE) && !termflags && - (isset(ALWAYSLASTPROMPT) && zmult == 1)) || - (unset(ALWAYSLASTPROMPT) && zmult != 1); - - up = printfmt(expl, nmatches, 1); - - if (clearflag) - tcmultout(TCUP, TCMULTUP, up + nlnct); - else - putc('\n', shout); - fflush(shout); - } - compend: - ll = strlen((char *)line); - if (cs > ll) - cs = ll; - popheap(); - } LASTALLOC; -} - -/* Create the completion list. This is called whenever some bit of * - * completion code needs the list. If the list is already available * - * (validlist!=0), this function doesn't do anything. Along with * - * the list is maintained the prefixes/suffixes etc. When any of * - * this becomes invalid -- e.g. if some text is changed on the * - * command line -- invalidatelist() should be called, to set * - * validlist to zero and free up the memory used. This function * - * returns non-zero on error. delit and compadd return information * - * about bits of the command line that need to be deleted. */ +/* Expand the current word. */ /**/ static int -makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized) +doexpansion(char *s, int lst, int olst, int explincmd) { - 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; - - /* 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); - - xorrec: - - DPUTS(ll != strlen((char *) line), "BUG: xorrec: ll != strlen(line)"); - - /* 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; - 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(); - - /* 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); - - /* *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; - - /* Insert the prefix (compctl -P), if any. */ - if (cc->prefix) { - int pl = 0, sl = strlen(cc->prefix); - - if (*s) { - /* First find out how much of the prefix is already on the line. */ - sd = dupstring(s); - untokenize(sd); - pl = pfxlen(cc->prefix, sd); - s += 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); - int sl = strlen(sdup), suffixll; - - /* Ignore trailing spaces. */ - for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--); - p[1] = '\0'; - - if (!sd) { - sd = dupstring(s); - untokenize(sd); - } - /* 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; - s[suffixll - sl] = '\0'; - } - } - /* Do we have one of the special characters `~' and `=' at the beginning? */ - if ((ic = *s) != Tilde && ic != Equals) - ic = 0; - - /* Check if we have to complete a parameter name... */ - - /* Try to find a `$'. */ - for (p = s + offs; p > s && *p != String; p--); - if (*p == String) { - /* Handle $$'s */ - while (p > s && p[-1] == String) - p--; - while (p[1] == String && p[2] == String) - p += 2; - } - if (*p == String && p[1] != Inpar && p[1] != Inbrack) { - /* This is really a parameter expression (not $(...) or $[...]). */ - char *b = p + 1, *e = b; - int n = 0, br = 1; - - if (*b == Inbrace) { - /* If this is a ${...}, ignore the possible (...) flags. */ - b++, br++; - n = skipparens(Inpar, Outpar, &b); - } - - /* Ignore the stuff before the parameter name. */ - for (; *b; b++) - if (*b != '^' && *b != Hat && - *b != '=' && *b != Equals && - *b != '~' && *b != Tilde) - break; - if (*b == '#' || *b == Pound || *b == '+') - b++; - - e = b; - /* Find the end of the name. */ - if (*e == Quest || *e == Star || *e == String || *e == Qstring || - *e == '?' || *e == '*' || *e == '$' || - *e == '-' || *e == '!' || *e == '@') - e++; - else if (idigit(*e)) - while (idigit(*e)) - e++; - else if (iident(*e)) - while (iident(*e) || - (useglob && (*e == Star || *e == Quest))) - e++; - - /* Now make sure that the cursor is inside the name. */ - if (offs <= e - s && offs >= b - s && n <= 0) { - /* It is. */ - parambr = br - 1; - /* Get the prefix (anything up to the character before the name). */ - *e = '\0'; - parampre = ztrduppfx(s, b - s); - qparampre = ztrdup(quotename(parampre, NULL, NULL, NULL)); - untokenize(qparampre); - qparprelen = strlen(qparampre); - /* 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_dummy.refc = 10000; - cc_dummy.mask = CC_PARAMS | CC_ENVVARS; - } - } - ooffs = offs; - /* If we have to ignore the word, do that. */ - if (cc->mask & CC_DELETE) { - *delit = 1; - *s = '\0'; - offs = 0; - } else - *delit = 0; - - /* Compute line prefix/suffix. */ - - lpl = offs; - lpre = zalloc(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); - 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) { - for (p = lpre + lpl; p > lpre; p--) - if (*p == '/') - break; - - if (*p == '/') - ic = 0; - } - /* Compute real prefix/suffix. */ - - noreal = !*delit; - for (p = lpre; *p && *p != String && *p != Tick; p++); - tt = ic && !parampre ? lpre + 1 : lpre; - rpre = (*p || *lpre == Tilde || *lpre == Equals) ? - (noreal = 0, getreal(tt)) : - ztrdup(tt); - - for (p = lsuf; *p && *p != String && *p != Tick; p++); - rsuf = *p ? (noreal = 0, getreal(lsuf)) : ztrdup(lsuf); - - /* Check if word is a pattern. */ - - for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1; - p >= rpre && (ispattern != 3 || !sf1); - p--) - if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde))) - ispattern |= sf1 ? 1 : 2; - else if (*p == '/') { - sf1++; - if (!s1) - s1 = p; - } - for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++) - if (itok(*p)) - t |= sf2 ? 4 : 2; - else if (*p == '/') { - sf2++; - if (!s2) - s2 = p; - } - ispattern = ispattern | t; - - /* But if we were asked not to do glob completion, we never treat the * - * thing as a pattern. */ - if (!useglob) - ispattern = 0; - - if (ispattern) { - /* The word should be treated as a pattern, so compute the matcher. */ - p = (char *)ncalloc(rpl + rsl + 2); - strcpy(p, rpre); - if (rpl && p[rpl - 1] != Star) { - p[rpl] = Star; - strcpy(p + rpl + 1, rsuf); - } else - strcpy(p + rpl, rsuf); - patcomp = parsereg(p); - } - if (!patcomp) { - untokenize(rpre); - untokenize(rsuf); - - rpl = strlen(rpre); - rsl = strlen(rsuf); - } - untokenize(lpre); - untokenize(lsuf); - - /* Handle completion of files specially (of course). */ - - if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) { - /* s1 and s2 point to the last/first slash in the prefix/suffix. */ - if (!s1) - s1 = rpre; - if (!s2) - s2 = rsuf + rsl; - - /* Compute the path prefix/suffix. */ - if (*s1 != '/') - ppre = ztrdup(""); - else - ppre = ztrduppfx(rpre, s1 - rpre + 1); - psuf = ztrdup(s2); - - /* And get the file prefix. */ - fpre = ztrdup(((s1 == s || s1 == rpre || ic) && - (*s != '/' || cs == wb)) ? s1 : s1 + 1); - /* And the suffix. */ - fsuf = ztrduppfx(rsuf, s2 - rsuf); - - if (useglob && (ispattern & 2)) { - int t2; - - /* We have to use globbing, so compute the pattern from * - * the file prefix and suffix with a `*' between them. */ - p = (char *)ncalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2); - strcpy(p, fpre); - if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star) - p[t2++] = Star; - strcpy(p + t2, fsuf); - filecomp = parsereg(p); - } - if (!filecomp) { - untokenize(fpre); - untokenize(fsuf); - - fpl = strlen(fpre); - fsl = strlen(fsuf); - } - addwhat = -1; - - /* Completion after `~', maketildelist adds the usernames * - * and named directories. */ - if (ic == Tilde) - maketildelist(); - else if (ic == Equals) { - /* Completion after `=', get the command names from * - * the cmdnamtab and aliases from aliastab. */ - if (isset(HASHLISTALL)) - cmdnamtab->filltable(cmdnamtab); - dumphashtable(cmdnamtab, -7); - dumphashtable(aliastab, -2); - } else { - /* Normal file completion... */ - if (ispattern & 1) { - /* But with pattern matching. */ - LinkList l = newlinklist(); - LinkNode n; - int ng = opts[NULLGLOB]; - - opts[NULLGLOB] = 1; - - addwhat = 0; - p = (char *)ncalloc(lpl + lsl + 3); - strcpy(p, lpre); - if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*') - strcat(p, "*"); - strcat(p, lsuf); - if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')') - strcat(p, "*"); - - /* Do the globbing. */ - tokenize(p); - remnulargs(p); - addlinknode(l, p); - globlist(l); - - if (nonempty(l)) { - /* And add the resulting words. */ - haswhat |= HAS_PATHPAT; - for (n = firstnode(l); n; incnode(n)) - addmatch(getdata(n), NULL); - } - opts[NULLGLOB] = ng; - } else { - /* 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; - - 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) { - /* 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) - /* The pattern is a full path (starting * - * with '/'), so add it unchanged. */ - 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); - } - } - } - } - pa[o] = '\0'; - g = p; - } - glob_pre = glob_suf = NULL; - noerrs = ne; - opts[MARKDIRS] = md; - } - } - } - } - } - /* 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); - rpl++; - zsfree(orpre); - } - if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) { - /* If we have to complete commands, add alias names, * - * shell functions and builtins too. */ - dumphashtable(aliastab, -3); - dumphashtable(reswdtab, -3); - dumphashtable(shfunctab, -3); - dumphashtable(builtintab, -3); - if (isset(HASHLISTALL)) - cmdnamtab->filltable(cmdnamtab); - dumphashtable(cmdnamtab, -3); - /* And parameter names if autocd and cdablevars are set. */ - if (isset(AUTOCD) && isset(CDABLEVARS)) - dumphashtable(paramtab, -4); - } - addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG; - - if (cc->mask & CC_NAMED) - /* Add named directories. */ - dumphashtable(nameddirtab, addwhat); - if (cc->mask & CC_OPTIONS) - /* Add option names. */ - dumphashtable(optiontab, addwhat); - if (cc->mask & CC_VARS) - /* And parameter names. */ - dumphashtable(paramtab, -9); - if (cc->mask & CC_BINDINGS) - /* And zle function names... */ - dumphashtable(thingytab, CC_BINDINGS); - if (cc->keyvar) { - /* This adds things given to the compctl -k flag * - * (from a parameter or a list of words). */ - char **usr = get_user_var(cc->keyvar); - - if (usr) - while (*usr) - addmatch(*usr++, NULL); - } - if (cc->mask & CC_USERS) - /* Add user names. */ - maketildelist(); - if (cc->func) { - /* This handles the compctl -K flag. */ - List list; - char **r; - int lv = lastval; - - /* Get the function. */ - if ((list = getshfunc(cc->func)) != &dummy_list) { - /* We have it, so build a argument list. */ - LinkList args = newlinklist(); - - addlinknode(args, cc->func); - - if (*delit) { - p = dupstrpfx(os, ooffs); - untokenize(p); - addlinknode(args, p); - p = dupstring(os + ooffs); - untokenize(p); - addlinknode(args, p); - } else { - addlinknode(args, lpre); - addlinknode(args, lsuf); - } - - /* This flag allows us to use read -l and -c. */ - incompctlfunc = 1; - /* Call the function. */ - doshfunc(list, args, 0, 1); - incompctlfunc = 0; - /* And get the result from the reply parameter. */ - if ((r = get_user_var("reply"))) - while (*r) - addmatch(*r++, NULL); - } - lastval = lv; - } - if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) { - /* Get job names. */ - int i; - char *j, *jj; - - for (i = 0; i < MAXJOB; i++) - if (jobtab[i].stat & STAT_INUSE) { - int stopped = jobtab[i].stat & STAT_STOPPED; + int ret = 1; + LinkList vl; + char *ss; - j = jj = dupstring(jobtab[i].procs->text); - /* Find the first word. */ - for (; *jj; jj++) - if (*jj == ' ') { - *jj = '\0'; - break; - } - if ((cc->mask & CC_JOBS) || - (stopped && (cc->mask & CC_STOPPED)) || - (!stopped && (cc->mask & CC_RUNNING))) - addmatch(j, NULL); - } - } - if (cc->str) { - /* Get the stuff from a compctl -s. */ - LinkList foo = newlinklist(); - LinkNode n; - int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb; - char *tmpbuf; + pushheap(); + vl = newlinklist(); + ss = dupstring(s); + addlinknode(vl, ss); + prefork(vl, 0); + if (errflag) + goto end; + if (lst == COMP_LIST_EXPAND || lst == COMP_EXPAND) { + int ng = opts[NULLGLOB]; opts[NULLGLOB] = 1; - - /* Put the strin in the lexer buffer and call the lexer to * - * get the words we have to expand. */ - zleparse = 1; - lexsave(); - tmpbuf = (char *)halloc(strlen(cc->str) + 5); - sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */ - inpush(tmpbuf, 0, NULL); - strinbeg(); - noaliases = 1; - do { - ctxtlex(); - if (tok == ENDINPUT || tok == LEXERR) - break; - if (!first && tokstr && *tokstr) - addlinknode(foo, ztrdup(tokstr)); - first = 0; - } while (tok != ENDINPUT && tok != LEXERR); - noaliases = 0; - strinend(); - inpop(); - errflag = zleparse = 0; - lexrestore(); - /* Fine, now do full expansion. */ - prefork(foo, 0); - if (!errflag) { - globlist(foo); - if (!errflag) - /* And add the resulting words as matches. */ - for (n = firstnode(foo); n; incnode(n)) - addmatch((char *)n->dat, NULL); - } + globlist(vl, 1); opts[NULLGLOB] = ng; - we = oowe; - wb = oowb; - } - if (cc->hpat) { - /* We have a pattern to take things from the history. */ - Comp compc = NULL; - char *e, *h, hpatsav; - Histent he; - int i = curhist - 1, n = cc->hnum; - - /* Parse the pattern, if it isn't the null string. */ - if (*(cc->hpat)) { - char *thpat = dupstring(cc->hpat); - - tokenize(thpat); - compc = parsereg(thpat); - } - /* n holds the number of history line we have to search. */ - if (!n) - n = -1; - - /* Now search the history. */ - while (n-- && (he = quietgethist(i--))) { - int iwords; - for (iwords = 0; iwords < he->nwords; iwords++) { - h = he->text + he->words[iwords*2]; - e = he->text + he->words[iwords*2+1]; - hpatsav = *e; - *e = '\0'; - /* We now have a word from the history, ignore it * - * if it begins with a quote or `$'. */ - if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' && - (!compc || domatch(h, compc, 0))) - /* Otherwise add it if it was matched. */ - addmatch(dupstring(h), NULL); - if (hpatsav) - *e = hpatsav; - } - } - } - if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS | - CC_READONLYS | CC_SPECIALS | CC_PARAMS))) - /* Add various flavours of parameters. */ - dumphashtable(paramtab, t); - if ((t = cc->mask & CC_SHFUNCS)) - /* Add shell functions. */ - dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - if ((t = cc->mask & CC_BUILTINS)) - /* Add builtins. */ - dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - if ((t = cc->mask & CC_EXTCMDS)) - /* Add external commands */ - dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - if ((t = cc->mask & CC_RESWDS)) - /* Add reserved words */ - dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - if ((t = cc->mask & (CC_ALREG | CC_ALGLOB))) - /* 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. */ - char **yaptr, *uv = NULL; - List list; - - if (cc->ylist[0] == '$' || cc->ylist[0] == '(') { - /* from variable */ - uv = cc->ylist + (cc->ylist[0] == '$'); - } 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; - - 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); - } - - /* No harm in allowing read -l and -c here, too */ - incompctlfunc = 1; - doshfunc(list, args, 0, 1); - incompctlfunc = 0; - uv = "reply"; - } - if (uv && (yaptr = get_user_var(uv))) { - PERMALLOC { - aylist = arrdup(yaptr); - } LASTALLOC; - } - } - - /* 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); + if (errflag) + goto end; + if (empty(vl) || !*(char *)peekfirst(vl)) + goto end; + if (peekfirst(vl) == (void *) ss || + (olst == COMP_EXPAND_COMPLETE && + !nextnode(firstnode(vl)) && *s == Tilde && + (ss = dupstring(s), filesubstr(&ss, 0)) && + !strcmp(ss, (char *)peekfirst(vl)))) { + /* 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); + goto end; + } + if (lst == COMP_LIST_EXPAND) { + /* Only the list of expansions was requested. */ + ret = listlist(vl); + showinglist = 0; + goto end; + } + /* Remove the current word and put the expansions there. */ + cs = wb; + foredel(we - wb); + while ((ss = (char *)ugetnode(vl))) { + ret = 0; + ss = quotename(ss, NULL); + untokenize(ss); + inststr(ss); +#if 0 + if (olst != COMP_EXPAND_COMPLETE || nonempty(vl) || + (cs && line[cs-1] != '/')) { +#endif + if (nonempty(vl)) { + spaceinline(1); + line[cs++] = ' '; } - expl = ztrdup(expl); - } - - 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; } + end: + popheap(); - /* 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; + return ret; } -/* Invalidate the completion list. */ +/* This is called from the lexer to give us word positions. */ /**/ void -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; - menucur = NULL; -} - -/* Get the words from a variable or a compctl -k list. */ - -/**/ -static char ** -get_user_var(char *nam) -{ - if (!nam) - return NULL; - else if (*nam == '(') { - /* It's a (...) list, not a parameter name. */ - char *ptr, *s, **uarr, **aptr; - int count = 0, notempty = 0, brk = 0; - LinkList arrlist = newlinklist(); - - ptr = dupstring(nam); - s = ptr + 1; - while (*++ptr) { - if (*ptr == '\\' && ptr[1]) - chuck(ptr), notempty = 1; - else if (*ptr == ',' || inblank(*ptr) || *ptr == ')') { - if (*ptr == ')') - brk++; - if (notempty) { - *ptr = '\0'; - count++; - if (*s == '\n') - s++; - addlinknode(arrlist, s); - } - s = ptr + 1; - notempty = 0; - } else { - notempty = 1; - if(*ptr == Meta) - ptr++; - } - if (brk) - break; - } - if (!brk || !count) - return NULL; - *ptr = '\0'; - aptr = uarr = (char **)ncalloc(sizeof(char *) * (count + 1)); - - while ((*aptr++ = (char *)ugetnode(arrlist))); - uarr[count] = NULL; - return uarr; - } else { - /* Otherwise it should be a parameter name. */ - char **arr = NULL, *val; - if (!(arr = getaparam(nam)) && (val = getsparam(nam))) { - arr = (char **)ncalloc(2*sizeof(char *)); - arr[0] = val; - arr[1] = NULL; - } - return arr; - } - -} - -/* This is strcmp with ignoring backslashes. */ - -/**/ -static int -strbpcmp(const void *a, const void *b) -{ - char *aa = *((char **)a), *bb = *((char **)b); - - while (*aa && *bb) { - if (*aa == '\\') - aa++; - if (*bb == '\\') - bb++; - if (*aa != *bb) - return (int)(*aa - *bb); - if (*aa) - aa++; - if (*bb) - bb++; - } - return (int)(*aa - *bb); -} - -/* Make an array from a linked list */ - -/**/ -static void -makearray(LinkList l) -{ - char **ap, **bp, **cp; - LinkNode nod; - - /* Build an array for the matches. */ - ap = amatches = (char **)ncalloc(((nmatches = countlinknodes(l)) + 1) * - sizeof(char *)); - - /* And copy them into it. */ - for (nod = firstnode(l); nod; incnode(nod)) - *ap++ = (char *)getdata(nod); - *ap = NULL; - - /* Now sort the array. */ - qsort((void *) amatches, nmatches, sizeof(char *), - (int (*) _((const void *, const void *)))strbpcmp); - - /* 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; - } - *cp = NULL; - nmatches = arrlen(amatches); -} - -/* Handle the case were we found more than one match. */ - -/**/ -static void -do_ambiguous(void) +gotword(void) { - int p = (usemenu || ispattern), atend = (cs == we); - int inv = 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) && - (usemenu == 0 || unset(AUTOMENU))) { - do_single(shortest); - invalidatelist(); - return; - } - /* Setting lastambig here means that the completion is ambiguous and * - * AUTO_MENU might want to start a menu completion next time round, * - * 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 * - * menu-complete-word command, or due to using GLOB_COMPLETE which * - * does menu-style completion regardless of the setting of the * - * normal menu completion options. */ - do_ambig_menu(); - } else { - /* 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 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) { - invalidatelist(); - lastambig = 0; - return; - } + we = ll + 1 - inbufct + (addedx == 2 ? 1 : 0); + if (cs <= we) { + wb = ll - wordbeg + addedx; + zleparse = 0; } - /* At this point, we might want a completion listing. Show the listing * - * if it is needed. */ - if (isset(LISTBEEP)) - feep(); - if (isset(AUTOLIST) && !amenu && !showinglist) - showinglist = -2; - if(inv) - invalidatelist(); } -/* This is a stat that ignores backslashes in the filename. The `ls' * - * parameter says if we have to do lstat() or stat(). I think this * - * should instead be done by use of a general function to expand a * - * filename (stripping backslashes), combined with the actual * - * (l)stat(). */ - /**/ static int -ztat(char *nam, struct stat *buf, int ls) +docompletion(char *s, int lst, int incmd) { - char b[PATH_MAX], *p; - - for (p = b; p < b + sizeof(b) - 1 && *nam; nam++) - if (*nam == '\\' && nam[1]) - *p++ = *++nam; - else - *p++ = *nam; - *p = '\0'; - - return ls ? lstat(b, buf) : stat(b, buf); -} - -/* Insert a single match in the command line. */ - -/**/ -static void -do_single(char *str) -{ - int l; - int havesuff = 0; - - 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; - 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; - else - l = 0; - - menuinsc = 0; - cs = menupos; - foredel(l); - - /* And than we insert the new string. */ - inststrlen(str, 1, menulen = strlen(str)); - menuend = cs; - - 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); - } - havesuff = 1; - } else { - /* There is no user-specified suffix, * - * so generate one automagically. */ - if(parampre && parambr) { - /*{{*/ - /* Completing a parameter in braces. Add a removable `}' suffix. */ - inststrlen("}", 1, 1); - menuinsc++; - } - if(!(haswhat & HAS_MISC) || - (parampre && isset(AUTOPARAMSLASH))) { - /* If we have only filenames 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) { - 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 (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); - } - else - p = dupstring(str); - parsestr(p); - if (ic) - *p = ic; - singsub(&p); - - 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); - } - /* And do the stat. */ - if (!ztat(p, &buf, 0) && S_ISDIR(buf.st_mode)) { - /* It is a directory, so add the slash. */ - havesuff = 1; - inststrlen("/", 1, 1); - menuinsc++; - if(menuwe && isset(AUTOREMOVESLASH)) { - makesuffix(1); - suffixlen['/'] = 1; - } - } - } - } - /* If completing in a brace expansion... */ - if(complinbrace) { - if(havesuff) { - /*{{*/ - /* If a suffix was added, and is removable, let * - * `,' and `}' remove it. */ - if(isset(AUTOPARAMKEYS)) - suffixlen[','] = suffixlen['}'] = suffixlen[256]; - } else { - /*{{*/ - /* Otherwise, add a `,' suffix, and let `}' remove it. */ - havesuff = 1; - inststrlen(",", 1, 1); - menuinsc++; - if(menuwe && isset(AUTOPARAMKEYS)) - suffixlen[','] = suffixlen['}'] = 1; - } - } 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) - makesuffix(1); - } - if(menuwe && parampre && isset(AUTOPARAMKEYS)) - makeparamsuffix(parambr, menuinsc); - - if (!menuwe) - cs = menuend; -} + struct compldat dat; -/* This handles the beginning of menu-completion. */ + dat.s = s; + dat.lst = lst; + dat.incmd = incmd; -/**/ -static void -do_ambig_menu(void) -{ - menucmp = 1; - menucur = NULL; - do_single(amatches[0]); - menucur = amatches; + return runhookdef(COMPLETEHOOK, (void *) &dat); } /* Return the length of the common prefix of s and t. */ /**/ -int +mod_export int pfxlen(char *s, char *t) { int i = 0; @@ -3493,7 +1703,7 @@ pfxlen(char *s, char *t) /* Return the length of the common suffix of s and t. */ -/**/ +#if 0 static int sfxlen(char *s, char *t) { @@ -3508,21 +1718,68 @@ sfxlen(char *s, char *t) } else return 0; } +#endif + +/* This is strcmp with ignoring backslashes. */ + +/**/ +mod_export int +strbpcmp(char **aa, char **bb) +{ + char *a = *aa, *b = *bb; + + while (*a && *b) { + if (*a == '\\') + a++; + if (*b == '\\') + b++; + if (*a != *b) + break; + if (*a) + a++; + if (*b) + b++; + } + if (isset(NUMERICGLOBSORT) && (idigit(*a) || idigit(*b))) { + for (; a > *aa && idigit(a[-1]); a--, b--); + if (idigit(*a) && idigit(*b)) { + while (*a == '0') + a++; + while (*b == '0') + b++; + for (; idigit(*a) && *a == *b; a++, b++); + if (idigit(*a) || idigit(*b)) { + int cmp = (int) STOUC(*a) - (int) STOUC(*b); + + while (idigit(*a) && idigit(*b)) + a++, b++; + if (idigit(*a) && !idigit(*b)) + return 1; + if (idigit(*b) && !idigit(*a)) + return -1; + + return cmp; + } + } + } + return (int)(*a - *b); +} -/* This is used to print the explanation string. * +/* This is used to print the strings (e.g. explanations). * * It returns the number of lines printed. */ /**/ -static int -printfmt(char *fmt, int n, int dopr) +mod_export int +printfmt(char *fmt, int n, int dopr, int doesc) { 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 == <number of matches>). */ - if (*p == '%') { + if (doesc && *p == '%') { if (*++p) { + m = 0; switch (*p) { case '%': if (dopr) @@ -3535,12 +1792,69 @@ printfmt(char *fmt, int n, int dopr) fprintf(shout, nc); cc += strlen(nc); break; + case 'B': + b = 1; + if (dopr) + tcout(TCBOLDFACEBEG); + break; + case 'b': + b = 0; m = 1; + if (dopr) + tcout(TCALLATTRSOFF); + break; + case 'S': + s = 1; + if (dopr) + tcout(TCSTANDOUTBEG); + break; + case 's': + s = 0; m = 1; + if (dopr) + tcout(TCSTANDOUTEND); + break; + case 'U': + u = 1; + if (dopr) + tcout(TCUNDERLINEBEG); + break; + case 'u': + u = 0; m = 1; + if (dopr) + tcout(TCUNDERLINEEND); + break; + case '{': + for (p++; *p && (*p != '%' || p[1] != '}'); p++) + if (dopr) + putc(*p, shout); + if (*p) + p++; + else + p--; + break; + } + if (dopr && m) { + if (b) + tcout(TCBOLDFACEBEG); + if (s) + tcout(TCSTANDOUTBEG); + if (u) + tcout(TCUNDERLINEBEG); } } else break; } else { cc++; if (*p == '\n') { + if (dopr) { + if (tccan(TCCLEAREOL)) + tcout(TCCLEAREOL); + else { + int s = columns - 1 - (cc % columns); + + while (s-- > 0) + putc(' ', shout); + } + } l += 1 + (cc / columns); cc = 0; } @@ -3548,137 +1862,136 @@ printfmt(char *fmt, int n, int dopr) putc(*p, shout); } } + if (dopr) { + if (tccan(TCCLEAREOL)) + tcout(TCCLEAREOL); + else { + int s = columns - 1 - (cc % columns); + while (s-- > 0) + putc(' ', shout); + } + } return l + (cc / columns); } -/* List the matches. Note that the list entries are metafied. */ +/* This is used to print expansions. */ /**/ -void -listmatches(void) +int +listlist(LinkList l) { - 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 *)); - -#ifdef DEBUG - /* Sanity check */ - if(!validlist) { - showmsg("BUG: listmatches called with bogus list"); - return; - } -#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; - - clearflag = (isset(USEZLE) && !termflags && - (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; + int num = countlinknodes(l); + VARARR(char *, data, (num + 1)); + LinkNode node; + char **p; + VARARR(int, lens, num); + VARARR(int, widths, columns); + int longest = 0, shortest = columns, totl = 0; + int len, ncols, nlines, tolast, col, i, max, pack = 0, *lenp; + + for (node = firstnode(l), p = data; node; incnode(node), p++) + *p = (char *) getdata(node); + *p = NULL; + + qsort((void *) data, num, sizeof(char *), + (int (*) _((const void *, const void *))) strbpcmp); + + for (p = data, lenp = lens; *p; p++, lenp++) { + len = *lenp = niceztrlen(*p) + 2; + if (len > longest) + longest = len; + if (len < shortest) + shortest = len; + totl += len; + } + if ((ncols = ((columns + 2) / longest))) { + int tlines = 0, tline, tcols = 0, maxlen, nth, width; + + nlines = (num + ncols - 1) / ncols; + + if (isset(LISTPACKED)) { + if (isset(LISTROWSFIRST)) { + int count, tcol, first, maxlines = 0, llines; + + for (tcols = columns / shortest; tcols > ncols; + tcols--) { + for (nth = first = maxlen = width = maxlines = + llines = tcol = 0, + count = num; + count > 0; count--) { + if (!(nth % tcols)) + llines++; + if (lens[nth] > maxlen) + maxlen = lens[nth]; + nth += tcols; + tlines++; + if (nth >= num) { + if ((width += maxlen) >= columns) + break; + widths[tcol++] = maxlen; + maxlen = 0; + nth = ++first; + if (llines > maxlines) + maxlines = llines; + llines = 0; + } + } + if (nth < num) { + widths[tcol++] = maxlen; + width += maxlen; + } + if (!count && width < columns) + break; + } + if (tcols > ncols) + tlines = maxlines; + } else { + for (tlines = ((totl + columns) / columns); + tlines < nlines; tlines++) { + for (p = data, nth = tline = width = + maxlen = tcols = 0; + *p; nth++, p++) { + if (lens[nth] > maxlen) + maxlen = lens[nth]; + if (++tline == tlines) { + if ((width += maxlen) >= columns) + break; + widths[tcols++] = maxlen; + maxlen = tline = 0; + } + } + if (tline) { + widths[tcols++] = maxlen; + width += maxlen; + } + if (nth == num && width < columns) + break; } } + if ((pack = (tlines < nlines))) { + nlines = tlines; + ncols = tcols; + } } } 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); - } + nlines = 0; + for (p = data; *p; p++) + nlines += 1 + (strlen(*p) / columns); } + /* Set the cursor below the prompt. */ + trashzle(); - /* Print the explanation string, if any. */ - if (expl) { - xup = printfmt(expl, ct, 1) + 1; - putc('\n', shout); - up += xup; - } + tolast = ((zmult == 1) == !!isset(ALWAYSLASTPROMPT)); + clearflag = (isset(USEZLE) && !termflags && tolast); - /* Maybe we have to ask if the user wants to see the list. */ - if ((listmax && ct > listmax) || (!listmax && up >= lines)) { + max = getiparam("LISTMAX"); + if ((max && num > max) || (!max && 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? ", + num, 1, 1); fflush(shout); if (getzlequery() != 'y') { if (clearflag) { @@ -3686,10 +1999,10 @@ 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; + return 1; } if (clearflag) { putc('\r', shout); @@ -3700,124 +2013,61 @@ listmatches(void) putc('\n', shout); settyinfo(&shttyinfo); } - - /* 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; + lastlistlen = (clearflag ? nlines : 0); + + if (ncols) { + if (isset(LISTROWSFIRST)) { + for (col = 1, p = data, lenp = lens; *p; + p++, lenp++, col++) { + nicezputs(*p, shout); + if (col == ncols) { + col = 0; + if (p[1]) + putc('\n', shout); } 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); + if ((i = (pack ? widths[col - 1] : longest) - *lenp + 2) > 0) + while (i--) + putc(' ', 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); } - } 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; + } else { + char **f; + int *fl, line; + + for (f = data, fl = lens, line = 0; line < nlines; + f++, fl++, line++) { + for (col = 1, p = f, lenp = fl; *p; col++) { + nicezputs(*p, shout); + if (col == ncols) + break; + if ((i = (pack ? widths[col - 1] : longest) - *lenp + 2) > 0) + while (i--) + putc(' ', shout); + for (i = nlines; i && *p; i--, p++, lenp++); } - for (t0 = colsz; t0 && *ap; t0--, ap++); - if (*ap) - for (; t2 < fw; t2++) - putc(' ', shout); + if (line + 1 < nlines) + putc('\n', shout); } - if (t1 != colsz - 1 || !clearflag) + } + } else { + for (p = data; *p; p++) { + nicezputs(*p, shout); putc('\n', shout); + } } - 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 (clearflag) { + 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. */ + if (listshown) + showagain = 1; -/**/ -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; - - haswhat = HAS_MISC; - ispattern = 0; - validlist = 1; - lpre = lsuf = ""; - aylist = NULL; - expl = NULL; - - makearray(l); - listmatches(); - showinglist = 0; - - expl = ex; - amatches = am; - aylist = ay; - nmatches = nm; - validlist = vl; - lpre = lp; - lsuf = ls; - ispattern = ip; - haswhat = hw; + return !num; } /* Expand the history references. */ @@ -3829,80 +2079,80 @@ doexpandhist(void) unsigned char *ol; int oll, ocs, ne = noerrs, err; - DPUTS(useheap, "BUG: useheap in doexpandhist()"); - HEAPALLOC { - pushheap(); - metafy_line(); - oll = ll; - ocs = cs; - ol = (unsigned char *)dupstring((char *)line); - expanding = 1; - excs = cs; - ll = cs = 0; - lexsave(); - /* We push ol as it will remain unchanged */ - inpush((char *) ol, 0, NULL); - strinbeg(); - noaliases = 1; - noerrs = 1; - exlast = inbufct; - do { - ctxtlex(); - } while (tok != ENDINPUT && tok != LEXERR); - stophist = 2; - while (!lexstop) - hgetc(); - /* We have to save errflags because it's reset in lexrestore. Since * - * noerrs was set to 1 errflag is true if there was a habort() which * - * means that the expanded string is unusable. */ - err = errflag; - noerrs = ne; - noaliases = 0; - strinend(); - inpop(); - zleparse = 0; - lexrestore(); - expanding = 0; - - if (!err) { - cs = excs; - if (strcmp((char *)line, (char *)ol)) { - unmetafy_line(); - /* For vi mode -- reset the beginning-of-insertion pointer * - * to the beginning of the line. This seems a little silly, * - * if we are, for example, expanding "exec !!". */ - if (viinsbegin > findbol()) - viinsbegin = findbol(); - popheap(); - LASTALLOC_RETURN 1; - } + pushheap(); + metafy_line(); + oll = ll; + ocs = cs; + ol = (unsigned char *)dupstring((char *)line); + expanding = 1; + excs = cs; + ll = cs = 0; + lexsave(); + /* We push ol as it will remain unchanged */ + inpush((char *) ol, 0, NULL); + strinbeg(1); + noaliases = 1; + noerrs = 1; + exlast = inbufct; + do { + ctxtlex(); + } while (tok != ENDINPUT && tok != LEXERR); + while (!lexstop) + hgetc(); + /* We have to save errflags because it's reset in lexrestore. Since * + * noerrs was set to 1 errflag is true if there was a habort() which * + * means that the expanded string is unusable. */ + err = errflag; + noerrs = ne; + noaliases = 0; + strinend(); + inpop(); + zleparse = 0; + lexrestore(); + expanding = 0; + + if (!err) { + cs = excs; + if (strcmp((char *)line, (char *)ol)) { + unmetafy_line(); + /* For vi mode -- reset the beginning-of-insertion pointer * + * to the beginning of the line. This seems a little silly, * + * if we are, for example, expanding "exec !!". */ + if (viinsbegin > findbol()) + viinsbegin = findbol(); + popheap(); + return 1; } + } - strcpy((char *)line, (char *)ol); - ll = oll; - cs = ocs; - unmetafy_line(); + strcpy((char *)line, (char *)ol); + ll = oll; + cs = ocs; + unmetafy_line(); + + popheap(); - popheap(); - } LASTALLOC; return 0; } /**/ -void -magicspace(void) +int +magicspace(char **args) { + int ret; c = ' '; - selfinsert(); - doexpandhist(); + if (!(ret = selfinsert(args))) + doexpandhist(); + return ret; } /**/ -void -expandhistory(void) +int +expandhistory(char **args) { if (!doexpandhist()) - feep(); + return 1; + return 0; } static int cmdwb, cmdwe; @@ -3914,65 +2164,62 @@ getcurcmd(void) int curlincmd; char *s = NULL; - DPUTS(useheap, "BUG: useheap in getcurcmd()"); - HEAPALLOC { - zleparse = 2; - lexsave(); - metafy_line(); - inpush(dupstrspace((char *) line), 0, NULL); - unmetafy_line(); - strinbeg(); - pushheap(); - do { - curlincmd = incmdpos; - ctxtlex(); - if (tok == ENDINPUT || tok == LEXERR) - break; - if (tok == STRING && curlincmd) { - zsfree(s); - s = ztrdup(tokstr); - cmdwb = ll - wordbeg; - cmdwe = ll + 1 - inbufct; - } + zleparse = 2; + lexsave(); + metafy_line(); + inpush(dupstrspace((char *) line), 0, NULL); + unmetafy_line(); + strinbeg(1); + pushheap(); + do { + curlincmd = incmdpos; + ctxtlex(); + if (tok == ENDINPUT || tok == LEXERR) + break; + if (tok == STRING && curlincmd) { + zsfree(s); + s = ztrdup(tokstr); + cmdwb = ll - wordbeg; + cmdwe = ll + 1 - inbufct; } - while (tok != ENDINPUT && tok != LEXERR && zleparse); - popheap(); - strinend(); - inpop(); - errflag = zleparse = 0; - lexrestore(); - } LASTALLOC; + } + while (tok != ENDINPUT && tok != LEXERR && zleparse); + popheap(); + strinend(); + inpop(); + errflag = zleparse = 0; + lexrestore(); + return s; } /**/ -void -processcmd(void) +int +processcmd(char **args) { char *s; int m = zmult; s = getcurcmd(); - if (!s) { - feep(); - return; - } + if (!s) + return 1; zmult = 1; - pushline(); + pushline(zlenoargs); zmult = m; inststr(bindk->nam); inststr(" "); untokenize(s); - HEAPALLOC { - inststr(quotename(s, NULL, NULL, NULL)); - } LASTALLOC; + + inststr(quotename(s, NULL)); + zsfree(s); done = 1; + return 0; } /**/ -void -expandcmdpath(void) +int +expandcmdpath(char **args) { int oldcs = cs, na = noaliases; char *s, *str; @@ -3980,16 +2227,12 @@ expandcmdpath(void) noaliases = 1; s = getcurcmd(); noaliases = na; - if (!s || cmdwb < 0 || cmdwe < cmdwb) { - feep(); - return; - } - str = findcmd(s); + if (!s || cmdwb < 0 || cmdwe < cmdwb) + return 1; + str = findcmd(s, 1); zsfree(s); - if (!str) { - feep(); - return; - } + if (!str) + return 1; cs = cmdwb; foredel(cmdwe - cmdwb); spaceinline(strlen(str)); @@ -3999,17 +2242,43 @@ expandcmdpath(void) cs += cmdwe - cmdwb + strlen(str); if (cs > ll) cs = ll; - zsfree(str); + return 0; } /* Extra function added by AR Iano-Fletcher. */ /* This is a expand/complete in the vein of wash. */ /**/ -void -expandorcompleteprefix(void) +int +expandorcompleteprefix(char **args) { + int ret; + comppref = 1; - expandorcomplete(); + ret = expandorcomplete(args); comppref = 0; + return ret; +} + +/**/ +int +endoflist(char **args) +{ + if (lastlistlen > 0) { + int i; + + clearflag = 0; + trashzle(); + + for (i = lastlistlen; i > 0; i--) + putc('\n', shout); + + showinglist = lastlistlen = 0; + + if (sfcontext) + zrefresh(); + + return 0; + } + return 1; } |