home *** CD-ROM | disk | FTP | other *** search
- /*
- * $Id: zle_tricky.c,v 2.82 1996/10/24 10:02:00 hzoli Exp $
- *
- * zle_tricky.c - expansion and completion
- *
- * This file is part of zsh, the Z shell.
- *
- * Copyright (c) 1992-1996 Paul Falstad
- * All rights reserved.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and to distribute modified versions of this software for any
- * purpose, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * In no event shall Paul Falstad or the Zsh Development Group be liable
- * to any party for direct, indirect, special, incidental, or consequential
- * damages arising out of the use of this software and its documentation,
- * even if Paul Falstad and the Zsh Development Group have been advised of
- * the possibility of such damage.
- *
- * Paul Falstad and the Zsh Development Group specifically disclaim any
- * warranties, including, but not limited to, the implied warranties of
- * merchantability and fitness for a particular purpose. The software
- * provided hereunder is on an "as is" basis, and Paul Falstad and the
- * Zsh Development Group have no obligation to provide maintenance,
- * support, updates, enhancements, or modifications.
- *
- */
-
- #define ZLE
- #include "zsh.h"
-
- /* The main part of ZLE maintains the line being edited as binary data, *
- * but here, where we interface with the lexer and other bits of zsh, *
- * we need the line metafied. The technique used is quite simple: on *
- * entry to the expansion/completion system, we metafy the line in *
- * place, adjusting ll and cs to match. All completion and expansion *
- * is done on the metafied line. Immediately before returning, the *
- * line is unmetafied again, changing ll and cs back. (ll and cs might *
- * have changed during completion, so they can't be merely saved and *
- * restored.) The various indexes into the line that are used in this *
- * 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)
-
- /* Prefix and suffix for globbing, used as an optimisation. */
-
- extern char *glob_pre, *glob_suf;
-
- /* wb and we hold the beginning/end position of the word we are completing. */
-
- static int wb, we;
-
- /* 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;
-
- /* A pointer to the current position in the menu-completion array (the one *
- * that was put in the command line last). */
-
- static char **menucur;
-
- /* The point (in the command line) where the menu-completion strings are *
- * inserted, the length of the string that was inserted last, the end *
- * position of this string in the command line and two flags: menuwe is *
- * non-zero if the cursor was at the end of the word (which means that we *
- * can add suffixes, e.g. a slash for directories), and menuce is used to *
- * save/restore the value of complexpect during consecutive menu *
- * completions (complexpect is used when completing after `$' or `${' and *
- * says whether some characters should be treated specially, when the *
- * option autoparamkeys is set). menuinsc is nonzero if a trailing slash *
- * marking a directory is added. */
-
- static int menupos, menulen, menuend, menuwe, menuce, menuinsc;
-
- /* The list of matches. fmatches contains the matches we first ignore *
- * because of fignore. */
-
- static LinkList matches, fmatches;
-
- /* The list of matches turned into an array. This is used to sort this *
- * list and when menu-completion is used (directly or via automenu). */
-
- static char **amatches;
-
- /* The number of matches. */
-
- static int nmatches;
-
- /* !=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 */
-
- static char *parampre = NULL, *qparampre = NULL;
- static int qparprelen;
-
- /* This is either zero or equal to the special character the word we are *
- * trying to complete starts with (e.g. Tilde or Equals). */
-
- static char ic;
-
- /* These hold the minimum common prefix/suffix lengths (normal and for *
- * fignore ignored). */
-
- static int ab, ae, fab, fae;
-
- /* This variable says what we are currently adding to the list of matches. */
-
- static int addwhat;
-
- /* firstm hold the first match we found, shortest contains the shortest *
- * one (normal and for fignore ignored). */
-
- static char *firstm, *shortest, *ffirstm, *fshortest;
-
- /* This holds the word we are completing in quoted from. */
-
- static char *qword;
-
- /* This is the length of the shortest match we found (normal and for *
- * fignore ignored). */
-
- static int shortl, fshortl;
-
- /* This is non-zero if we are doing a menu-completion and this is not the *
- * first call (e.g. when automenu is set and menu-completion was entered *
- * due to this). */
-
- static int amenu;
-
- /* This is used by expandorcompleteprefix to save the position of the *
- * inserted space (so that we can remove it later). */
-
- static int remove_at = -1;
-
- /* Find out if we have to insert a tab (instead of trying to complete). */
-
- /**/
- int
- usetab(void)
- {
- unsigned char *s = line + cs - 1;
-
- for (; s >= line && *s != '\n'; s--)
- if (*s != '\t' && *s != ' ')
- return 0;
- 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)
-
- /**/
- void
- completeword(void)
- {
- usemenu = isset(MENUCOMPLETE);
- useglob = isset(GLOBCOMPLETE);
- if (c == '\t' && usetab())
- selfinsert();
- else
- docomplete(COMP_COMPLETE);
- }
-
- /**/
- void
- menucomplete(void)
- {
- usemenu = 1;
- useglob = isset(GLOBCOMPLETE);
- if (c == '\t' && usetab())
- selfinsert();
- else
- docomplete(COMP_COMPLETE);
- }
-
- /**/
- void
- listchoices(void)
- {
- usemenu = isset(MENUCOMPLETE);
- useglob = isset(GLOBCOMPLETE);
- docomplete(COMP_LIST_COMPLETE);
- }
-
- /**/
- void
- spellword(void)
- {
- usemenu = useglob = 0;
- docomplete(COMP_SPELL);
- }
-
- /**/
- void
- deletecharorlist(void)
- {
- char **mc = menucur;
-
- usemenu = isset(MENUCOMPLETE);
- useglob = isset(GLOBCOMPLETE);
- if (cs != ll)
- deletechar();
- else
- docomplete(COMP_LIST_COMPLETE);
-
- menucur = mc;
- }
-
- /**/
- void
- expandword(void)
- {
- usemenu = useglob = 0;
- if (c == '\t' && usetab())
- selfinsert();
- else
- docomplete(COMP_EXPAND);
- }
-
- /**/
- void
- expandorcomplete(void)
- {
- usemenu = isset(MENUCOMPLETE);
- useglob = isset(GLOBCOMPLETE);
- if (c == '\t' && usetab())
- selfinsert();
- else
- docomplete(COMP_EXPAND_COMPLETE);
- }
-
- /**/
- void
- menuexpandorcomplete(void)
- {
- usemenu = 1;
- useglob = isset(GLOBCOMPLETE);
- if (c == '\t' && usetab())
- selfinsert();
- else
- docomplete(COMP_EXPAND_COMPLETE);
- }
-
- /**/
- void
- listexpand(void)
- {
- usemenu = isset(MENUCOMPLETE);
- useglob = isset(GLOBCOMPLETE);
- docomplete(COMP_LIST_EXPAND);
- }
-
- /**/
- void
- reversemenucomplete(void)
- {
- if (!menucmp) {
- menucomplete();
- return;
- }
- HEAPALLOC {
- if (menucur == amatches)
- menucur = amatches + nmatches - 1;
- else
- menucur--;
- complexpect = menuce;
- metafy_line();
- do_single(*menucur);
- unmetafy_line();
- } LASTALLOC;
- }
-
- /* 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. */
-
- /**/
- void
- acceptandmenucomplete(void)
- {
- if (!menucmp) {
- feep();
- return;
- }
- cs = menuend;
- 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;
- menuwe = 1;
- menucomplete();
- }
-
- /* These are flags saying if we are completing in the command *
- * position or in a redirection. */
-
- static int lincmd, linredir;
-
- /* 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 (provided the text isn't *
- * changed in the meantime... AAAAAAAAAAUUUUGGGHH!). */
-
- static int lastambig;
-
- /* This describes some important things collected during the last *
- * completion. Its value is zero or the inclusive OR of some of *
- * the HAS_* things below. */
-
- static int haswhat;
-
- /* We have a suffix to add (given with compctl -S). */
-
- #define HAS_SUFFIX 1
-
- /* We have filenames in the completion list. */
-
- #define HAS_FILES 2
-
- /* We have other things than files in the completion list. If this is *
- * not set but HAS_FILES is, we probably put the file type characters *
- * in the completion list (if listtypes is set) and we attempt to add *
- * a slash to completed directories. */
-
- #define HAS_MISC 4
-
- /* This is set if we have filenames in the completion list that were *
- * generated by a globcompletion pattern. */
-
- #define HAS_PATHPAT 8
-
-
- /* This holds the naem of the current command (used to find the right *
- * compctl). */
-
- static char *cmdstr;
-
-
- /* Check if the given string is the name of a parameter and if this *
- * parameter is one worth expanding. */
-
- /**/
- int
- checkparams(char *p)
- {
- int t0, n, l = strlen(p), e = 0;
- struct hashnode *hn;
-
- for (t0 = paramtab->hsize - 1, n = 0; n < 2 && t0 >= 0; t0--)
- for (hn = paramtab->nodes[t0]; n < 2 && hn; hn = hn->next)
- if (pfxlen(p, hn->nam) == l) {
- n++;
- if (strlen(hn->nam) == l)
- e = 1;
- }
- return (n == 1) ? (getsparam(p) != NULL) :
- (!menucmp && e && isset(RECEXACT));
- }
-
- /* Check if the given string has wildcards. The difficulty is that we *
- * have to treat things like job specifications (%...) and parameter *
- * expressions correctly. */
-
- /**/
- int
- cmphaswilds(char *str)
- {
- if ((*str == Inbrack || *str == Outbrack) && !str[1])
- return 0;
-
- /* If a leading % is immediately followed by ?, then don't *
- * treat that ? as a wildcard. This is so you don't have *
- * to escape job references such as %?foo. */
- if (str[0] == '%' && str[1] ==Quest)
- str += 2;
-
- for (; *str;) {
- if (*str == String || *str == Qstring) {
- /* A parameter expression. */
-
- if (*++str == Inbrace)
- skipparens(Inbrace, Outbrace, &str);
- else if (*str == String || *str == Qstring)
- str++;
- else {
- /* Skip all the things a parameter expression might start *
- * with (before we come to the parameter name). */
- for (; *str; str++)
- if (*str != '^' && *str != Hat &&
- *str != '=' && *str != Equals &&
- *str != '~' && *str != Tilde)
- break;
- if (*str == '#' || *str == Pound)
- str++;
- /* Star and Quest are parameter names here, not wildcards */
- if (*str == Star || *str == Quest)
- str++;
- }
- } else {
- /* Not a parameter expression so we check for wildcards */
- if (((*str == Pound || *str == Hat) && isset(EXTENDEDGLOB)) ||
- *str == Star || *str == Bar || *str == Quest ||
- !skipparens(Inbrack, Outbrack, &str) ||
- !skipparens(Inang, Outang, &str) ||
- (unset(IGNOREBRACES) &&
- !skipparens(Inbrace, Outbrace, &str)) ||
- (*str == Inpar && str[1] == ':' &&
- !skipparens(Inpar, Outpar, &str)))
- return 1;
- if (*str)
- str++;
- }
- }
- return 0;
- }
-
- /* The main entry point for completion. */
-
- /**/
- void
- docomplete(int lst)
- {
- char *s, *ol;
- int olst = lst, chl = 0, ne = noerrs, ocs;
-
- /* If we are doing a menu-completion... */
-
- if (menucmp && lst != COMP_LIST_EXPAND) {
- do_menucmp(lst);
- return;
- }
-
- /* Check if we have to start a menu-completion (via automenu). */
-
- if ((amenu = (isset(AUTOMENU) &&
- (lastcmd & ZLE_MENUCMP) &&
- lastambig)))
- usemenu = 1;
-
- /* Expand history references before starting completion. If anything *
- * changed, do no more. */
-
- if (doexpandhist())
- return;
-
- metafy_line();
-
- ocs = cs;
- 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 *
- * in chline to the contents of line. */
-
- ol = dupstring((char *)line);
- /* Make sure that chline is zero-terminated. */
- *hptr = '\0';
- cs = 0;
- inststr(chline);
- chl = cs;
- cs += ocs;
- } else
- ol = NULL;
- inwhat = IN_NOTHING;
- qword = NULL;
- /* Get the word to complete. */
- noerrs = 1;
- s = get_comp_string();
- DPUTS(wb < 0 || cs < wb || cs > we,
- "BUG: 0 <= wb <= cs <= we is not true!");
- noerrs = ne;
- /* For vi mode, reset the start-of-insertion pointer to the beginning *
- * of the word being completed, if it is currently later. Vi itself *
- * would never change the pointer in the middle of an insertion, but *
- * then vi doesn't have completion. More to the point, this is only *
- * an emulation. */
- if (viinsbegin > ztrsub((char *) line + wb, (char *) line))
- viinsbegin = ztrsub((char *) line + wb, (char *) line);
- /* If we added chline to the line buffer, reset the original contents. */
- if (ol) {
- cs -= chl;
- wb -= chl;
- we -= chl;
- if (wb < 0) {
- strcpy((char *) line, ol);
- ll = strlen((char *) line);
- cs = ocs;
- unmetafy_line();
- feep();
- return;
- }
- ocs = cs;
- cs = 0;
- foredel(chl);
- cs = ocs;
- }
- freeheap();
- /* Save the lexer state, in case the completion code uses the lexer *
- * somewhere (e.g. when processing a compctl -s flag). */
- lexsave();
- if (inwhat == IN_ENV)
- lincmd = 0;
- if (s) {
- if (lst == COMP_EXPAND_COMPLETE) {
- /* Check if we have to do expansion or completion. */
- char *q = s;
-
- 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))
- 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);
- n++;
- }
- if (n == 2)
- break;
- }
-
- if (n == 1)
- lst = COMP_EXPAND;
- }
- }
- if (lst == COMP_EXPAND_COMPLETE)
- do {
- /* check if there is a parameter expresiion. */
- for (; *q && *q != String; q++);
- if (*q == String && q[1] != Inpar && q[1] != Inbrack) {
- if (*++q == Inbrace) {
- if (! skipparens(Inbrace, Outbrace, &q) &&
- q == s + cs - wb)
- lst = COMP_EXPAND;
- } else {
- char *t, sav, sav2;
-
- /* Skip the things parameter expressions might *
- * start with (the things before the parameter *
- * name). */
- for (; *q; q++)
- if (*q != '^' && *q != Hat &&
- *q != '=' && *q != Equals &&
- *q != '~' && *q != Tilde)
- break;
- if ((*q == '#' || *q == Pound || *q == '+') &&
- q[1] != String)
- q++;
-
- sav2 = *(t = q);
- if (*q == Quest || *q == Star || *q == String ||
- *q == Qstring)
- *q = ztokens[*q - Pound], ++q;
- else if (*q == '?' || *q == '*' || *q == '$' ||
- *q == '-' || *q == '!' || *q == '@')
- q++;
- else if (idigit(*q))
- do q++; while (idigit(*q));
- else
- while (iident(*q))
- q++;
- sav = *q;
- *q = '\0';
- if (cs - wb == q - s &&
- (idigit(sav2) || checkparams(t)))
- lst = COMP_EXPAND;
- *q = sav;
- *t = sav2;
- }
- if (lst != COMP_EXPAND)
- lst = COMP_COMPLETE;
- } else
- break;
- } while (q < s + cs - wb);
- if (lst == COMP_EXPAND_COMPLETE) {
- /* If it is still not clear if we should use expansion or *
- * completion and there is a `$' or a backtick in the word, *
- * than do expansion. */
- for (q = s; *q; q++)
- if (*q == Tick || *q == Qtick ||
- *q == String || *q == Qstring)
- break;
- lst = *q ? COMP_EXPAND : COMP_COMPLETE;
- }
- /* And do expansion if there are wildcards and globcomplete is *
- * not used. */
- if (unset(GLOBCOMPLETE) && cmphaswilds(s))
- lst = COMP_EXPAND;
- }
- if (lincmd && (inwhat == IN_NOTHING))
- inwhat = IN_CMD;
-
- if (lst == COMP_SPELL) {
- char *x, *q;
-
- for (q = s; *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);
- inststr(x);
- } else if (COMP_ISEXPAND(lst)) {
- /* Do expansion. */
- char *ol = (olst == COMP_EXPAND_COMPLETE) ?
- dupstring((char *)line) : (char *)line;
- int ocs = cs, ne = noerrs;
-
- noerrs = 1;
- doexpansion(s, lst, olst, lincmd);
- lastambig = 0;
- noerrs = ne;
-
- /* If expandorcomplete was invoked and the expansion didn't *
- * 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);
- }
- } else
- /* Just do completion. */
- docompletion(s, lst, lincmd, 0);
- zsfree(s);
- }
- /* Reset the lexer state, pop the heap. */
- lexrestore();
- popheap();
- zsfree(qword);
- menuce = complexpect;
- 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. */
-
- /**/
- 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;
- complexpect = menuce;
- /* ... and insert it into the command line. */
- metafy_line();
- do_single(*menucur);
- unmetafy_line();
- } LASTALLOC;
- }
-
- /* 1 if x added to complete in a blank between words */
- int addedx;
-
- /* 1 if we are completing in a string */
- int instring;
-
- /* This function inserts an `x' in the command line at the cursor position. *
- * *
- * Oh, you want to know why? Well, if completion is tried somewhere on an *
- * empty part of the command line, the lexer code would normally not be *
- * able to give us the `word' we want to complete, since there is no word. *
- * But we need to call the lexer to find out where we are (and for which *
- * command we are completing and such things). So we temporarily add a `x' *
- * (any character without special meaning would do the job) at the cursor *
- * position, than the lexer gives us the word `x' and its beginning and end *
- * positions and we can remove the `x'. */
-
- /**/
- void
- addx(char **ptmp)
- {
- if (!line[cs] || line[cs] == '\n' ||
- (iblank(line[cs]) && (!cs || line[cs-1] != '\\')) ||
- line[cs] == ')' || line[cs] == '`' ||
- (instring && (line[cs] == '"' || line[cs] == '\''))) {
- *ptmp = (char *)line;
- line = (unsigned char *)halloc(strlen((char *)line) + 3);
- memcpy(line, *ptmp, cs);
- line[cs] = 'x';
- strcpy((char *)line + cs + 1, (*ptmp) + cs);
- addedx = 1;
- } else {
- addedx = 0;
- *ptmp = NULL;
- }
- }
-
- /* Like dupstring, but add an extra space at the end of the string. */
-
- /**/
- char *
- dupstrspace(const char *str)
- {
- int len = strlen((char *)str);
- char *t = (char *)ncalloc(len + 2);
- strcpy(t, str);
- strcpy(t+len, " ");
- return t;
- }
-
- /* These functions metafy and unmetafy the ZLE buffer, as described at the *
- * top of this file. Note that ll and cs are translated. They *must* be *
- * called in matching pairs, around all the expansion/completion code. *
- * Currently, there are four pairs: in history expansion, in the main *
- * completion function, and one in each of the middle-of-menu-completion *
- * functions (there's one for each direction). */
-
- /**/
- void
- metafy_line(void)
- {
- int len = ll;
- char *s;
-
- for (s = (char *) line; s < (char *) line + ll;)
- if (imeta(*s++))
- len++;
- sizeline(len);
- (void) metafy((char *) line, ll, META_NOALLOC);
- ll = len;
- cs = metalen((char *) line, cs);
- }
-
- /**/
- void
- unmetafy_line(void)
- {
- cs = ztrsub((char *) line + cs, (char *) line);
- (void) unmetafy((char *) line, &ll);
- }
-
- /* 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 *
- * changes in the lexer code (and then in the parser, and then...). */
-
- /**/
- char *
- get_comp_string(void)
- {
- int t0, tt0, i, j, k, cp, rd, sl, ocs;
- char *s = NULL, *linptr, *tmp, *p, *tt = 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 ((...)). */
-
- for (i = j = k = 0, p = (char *)line; p < (char *)line + cs; p++)
- if (*p == '`' && !(k & 1))
- i++;
- else if (*p == '\"' && !(k & 1) && !(i & 1))
- j++;
- else if (*p == '\'' && !(j & 1))
- k++;
- else if (*p == '\\' && p[1] && !(k & 1))
- p++;
- instring = (j & 1) ? 2 : (k & 1);
- 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 - 1);
- tt0 = tok;
- /* Store the number of this word. */
- clwpos = i;
- cp = lincmd;
- rd = linredir;
- if (inwhat == IN_NOTHING && incond)
- inwhat = IN_COND;
- }
- 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;
- }
- 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 - 1) >= sl) ?
- (sl - 1) : (cs - wb - 1)]);
- } 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;
- lincmd = cp;
- linredir = rd;
- strinend();
- inpop();
- errflag = zleparse = 0;
- if (addedx)
- wb++;
- 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;
- if (parend >= 0) {
- ll -= parend;
- line[ll + addedx] = '\0';
- }
- 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) {
- /* 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;
- }
- 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) {
- tmp = NULL;
- linptr = (char *)line;
- lexrestore();
- goto start;
- }
- feep();
- noaliases = 0;
- lexrestore();
- LASTALLOC_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;
- }
- 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. */
- 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--;
- }
- } else {
- ocs = cs;
- *tt = '\0';
- cs = we;
- backdel(1);
- if (ocs == we)
- cs = we - 1;
- else
- cs = ocs;
- we--;
- }
- chuck(p--);
- }
- } LASTALLOC;
- lexrestore();
-
- return (char *)s;
- }
-
- /* Expand the current word. */
-
- /**/
- 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++] = ' ';
- }
- #endif
- if (olst != COMP_EXPAND_COMPLETE || nonempty(vl) ||
- (cs && line[cs-1] != '/')) {
- spaceinline(1);
- line[cs++] = ' ';
- }
- }
- end:
- popheap();
- } LASTALLOC;
- }
-
- /* This is called from the lexer to give us word positions. */
-
- /**/
- void
- gotword(void)
- {
- we = ll + 1 - inbufct;
- if (cs <= we) {
- wb = ll - wordbeg;
- zleparse = 0;
- }
- }
-
- /* 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). */
-
- /**/
- void
- inststrlen(char *str, int move, int len)
- {
- if (!len)
- return;
- if (len == -1)
- len = strlen(str);
- spaceinline(len);
- strncpy((char *)(line + cs), str, len);
- if (remove_at >= cs)
- remove_at += len;
- if (move)
- cs += 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. */
-
- /**/
- 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. */
-
- /**/
- 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))))))) {
- 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 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 */
-
- /**/
- 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);
- /* for (n = firstnode(matches); n; incnode(n))
- if (getpwnam(getdata(n)) == NULL)
- uremnode(matches, n);*/
- }
- # else /* HAVE_NIS_PLUS */
- /* Maybe we should turn this string into a #define'd constant...? */
-
- 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. */
-
- /**/
- 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. */
-
- /**/
- 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. */
-
- 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. */
-
- /**/
- 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. */
-
- /**/
- 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. */
-
- /**/
- 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. */
-
- /**/
- 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. */
-
- /**/
- 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)) && !errflag) {
- /* Ignore `.' and `..'. */
- if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
- continue;
- /* 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 && (buf.st_mode & S_IFMT) == S_IFDIR) ||
- (execs && ((buf.st_mode & (S_IFMT | S_IEXEC))
- == (S_IFREG | S_IEXEC)))) {
- /* 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. */
-
- char *expl;
-
- /* This holds the suffix to add (given with compctl -S). */
-
- 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). */
-
- int remsuffix;
-
- /**/
- void
- quotepresuf(char **ps)
- {
- if (*ps) {
- char *p = quotename(*ps, NULL, NULL, NULL);
-
- if (p != *ps) {
- zsfree(*ps);
- *ps = ztrdup(p);
- }
- }
- }
-
- /* This is non-zero if the cursor was moved up after showing a list *
- * of completions (with alwayslastprompt). */
-
- int clearflag;
-
- /**/
- void
- docompletion(char *s, int lst, int incmd, int untokenized)
- {
- static int delit, compadd;
-
- 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) && !gotmult)) ||
- (unset(ALWAYSLASTPROMPT) && gotmult);
-
- 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. */
-
- /**/
- int
- makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
- {
- 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... */
-
- complexpect = menuce = 0;
-
- /* 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, set complexpect. */
- if (offs == e - s)
- complexpect = br;
- /* 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;
- } else
- complexpect = 0;
- }
- 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_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;
- 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)
- /* 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;
- }
- }
- }
- /* 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) {
- /* 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 == '/')
- /* 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 == '/') {
- /* IF the pattern started with `~', *
- * `=', or `/', add the result only, *
- * if it realy matches what we have *
- * on the line. */
- 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. */
- scanhashtable(nameddirtab, 0, 0, 0, addhnmatch, 0);
- if (cc->mask & CC_OPTIONS) {
- /* Add option names. */
- struct option *o;
-
- for (o = optns + OPT_SIZE; (--o)->name; )
- addmatch(dupstring(o->name), NULL);
- }
- if (cc->mask & CC_VARS)
- /* And parameter names. */
- dumphashtable(paramtab, -9);
- if (cc->mask & CC_BINDINGS) {
- /* And zle function names... */
- int t0;
-
- for (t0 = 0; t0 != ZLECMDCOUNT; t0++)
- if (*zlecmds[t0].name)
- addmatch(dupstring(zlecmds[t0].name), NULL);
- }
- 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))) {
- /* 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. */
- inzlefunc = 1;
- /* Call the function. */
- doshfunc(list, args, 0, 1);
- inzlefunc = 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;
-
- 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;
-
- 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);
- }
- 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;
-
- /* Get the explanation string we will have to print. */
- expl = cc->explain;
-
- remsuffix = (cc->mask & CC_REMOVE);
- ccsuffix = cc->suffix;
-
- validlist = 1;
- if ((nmatches || expl) && !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);
- goto xorrec;
- }
-
- /* 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;
- }
-
- /* Invalidate the completion list. */
-
- /**/
- void
- invalidatelist(void)
- {
- if(showinglist == -2)
- listmatches();
- if(validlist) {
- freearray(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 (ccmain != &cc_dummy)
- freecompctl(ccmain);
- }
- menucmp = showinglist = validlist = 0;
- menucur = NULL;
- }
-
- /* Get the words from a variable or a compctl -k list. */
-
- /**/
- 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. */
- return getaparam(nam);
- }
-
- /* This is strcmp with ignoring backslashes. */
-
- /**/
- 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 */
-
- /**/
- 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. */
-
- /**/
- void
- do_ambiguous(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. */
- complexpect = 0;
- 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;
- }
- }
- /* 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(). */
-
- /**/
- int
- ztat(char *nam, struct stat *buf, int ls)
- {
- 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. */
-
- /**/
- void
- do_single(char *str)
- {
- int ccs, l, insc = 0, inscs = 0;
- char singlec = ' ';
-
- if (!ccsuffix || !(haswhat & HAS_SUFFIX) || !remsuffix)
- addedsuffix = 0;
-
- if (!menucur) {
- /* We are currently not in a menu-completion, *
- * so set the position variables. */
- menuinsc = 0;
- if (ispattern) {
- cs = we;
- menupos = wb;
- } else
- menupos = cs;
- menuwe = (cs == we);
- if (ccsuffix && !(haswhat & HAS_SUFFIX)) {
- /* Add a compctl -S suffix if we have one. */
- if (*ccsuffix) {
- ccs = cs;
- cs = we;
- inststrlen(ccsuffix, menuwe, -1);
- menuend = cs;
- cs = ccs;
- if (remsuffix)
- /* addedsuffix is used by the key input handling *
- * code to find out if it has to delete a suffix. */
- addedsuffix = strlen(ccsuffix);
- } else
- menuend = we;
-
- haswhat |= HAS_SUFFIX;
- } else
- menuend = we;
- }
- ccs = cs;
- /* 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;
- foredel(1);
- }
- l = menulen;
- } else if (ispattern)
- l = we - wb;
- else
- l = 0;
- cs = menupos;
- menuinsc = 0;
-
- if (l) {
- foredel(l);
- if (menuwe)
- ccs -= l;
- menuend -= l;
- }
- /* And than we insert the new string. */
- inststrlen(str, 1, menulen = strlen(str));
-
- /* And move the cursor and adjust the menuend variable. */
- if (menuwe)
- cs = ccs + menulen;
- menuend += menulen;
-
- if (!(haswhat & HAS_SUFFIX)) {
- /* There is no suffix, so we may add one. */
- 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. */
- 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) && (buf.st_mode & S_IFMT) == S_IFDIR) {
- /* It is a directory, so prepare to add *
- * the slash and set addedsuffix. */
- singlec = '/';
- if (menuwe || isset(ALWAYSTOEND))
- addedsuffix = isset(AUTOREMOVESLASH) ? 1 : 0;
- }
- }
- if (menuend > ll)
- menuend = ll;
- if (menuend && ((((char)line[menuend - 1]) != singlec) ||
- (menuend > 1 && singlec == ' ' &&
- (line[menuend - 2] == '\\' || line[menuend - 2] == STOUC(Meta)))))
- if (parampre && singlec == '/' && ((char)line[menuend]) == '/')
- addedsuffix = 0;
- /* Now insert the slash or space if there is none already. */
- else {
- ccs = cs;
- cs = menuend;
- inststrlen((char *)&singlec, 1, 1);
- insc = 1;
- if (singlec != ' ')
- menuinsc = 1;
- inscs = cs;
- if (!menuwe)
- cs = ccs;
- }
- }
- /* Move to the end of the word if requested. */
- if (isset(ALWAYSTOEND) || menuwe)
- cs = menuend + (!(haswhat & HAS_SUFFIX) && insc);
- if (menucmp && singlec == ' ' && !(haswhat & HAS_SUFFIX)) {
- /* Get rid of the added space if we are doing menucompletion. */
- if (insc) {
- ccs = cs;
- cs = inscs;
- backdel(1);
- if (ccs != inscs)
- cs = ccs;
- } else
- cs--;
- }
- }
-
- /* This handles the beginning of menu-completion. */
-
- /**/
- void
- do_ambig_menu(void)
- {
- menucmp = 1;
- menucur = NULL;
- do_single(amatches[0]);
- menucur = amatches;
- }
-
- /* Return non-zero if s is a prefix of t. */
-
- /**/
- int
- strpfx(char *s, char *t)
- {
- while (*s && *s == *t)
- s++, t++;
- return !*s;
- }
-
- /* Return non-zero if s is a suffix of t. */
-
- /**/
- int
- strsfx(char *s, char *t)
- {
- int ls = strlen(s), lt = strlen(t);
-
- if (ls <= lt)
- return !strcmp(t + lt - ls, s);
- return 0;
- }
-
- /* Return the length of the common prefix of s and t. */
-
- /**/
- int
- pfxlen(char *s, char *t)
- {
- int i = 0;
-
- while (*s && *s == *t)
- s++, t++, i++;
- return i;
- }
-
- /* Return the length of the common suffix of s and t. */
-
- /**/
- int
- sfxlen(char *s, char *t)
- {
- if (*s && *t) {
- int i = 0;
- char *s2 = s + strlen(s) - 1, *t2 = t + strlen(t) - 1;
-
- while (s2 >= s && t2 >= t && *s2 == *t2)
- s2--, t2--, i++;
-
- return i;
- } else
- return 0;
- }
-
- /* This is used to print the explanation string. *
- * It returns the number of lines printed. */
-
- /**/
- int
- printfmt(char *fmt, int n, int dopr)
- {
- char *p = fmt, nc[DIGBUFSIZE];
- int l = 0, cc = 0;
-
- for (; *p; p++) {
- /* Handle the `%' stuff (%% == %, %n == <number of matches>). */
- if (*p == '%') {
- if (*++p) {
- switch (*p) {
- case '%':
- if (dopr)
- putc('%', shout);
- cc++;
- break;
- case 'n':
- sprintf(nc, "%d", n);
- if (dopr)
- fprintf(shout, nc);
- cc += strlen(nc);
- break;
- }
- } else
- break;
- } else {
- cc++;
- if (*p == '\n') {
- l += 1 + (cc / columns);
- cc = 0;
- }
- if (dopr)
- putc(*p, shout);
- }
- }
-
- return l + (cc / columns);
- }
-
- /* List the matches. Note that the list entries are metafied. */
-
- /**/
- void
- listmatches(void)
- {
- int longest = 1, fct, fw, colsz, t0, t1, ct, up, cl, xup = 0;
- int off, boff, nboff;
- int of = (isset(LISTTYPES) && !(haswhat & HAS_MISC));
- char **arr, **ap, sav;
- int nfpl, nfsl, nlpl, nlsl;
- int listmax = getiparam("LISTMAX");
-
- #ifdef DEBUG
- /* Sanity check */
- if(!validlist) {
- trashzle();
- fputs("BUG: listmatches called with bogus list\n", shout);
- showinglist = 0;
- 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. */
- off = ispattern && ppre && *ppre &&
- !(haswhat & (HAS_MISC | HAS_PATHPAT)) ? strlen(ppre) : 0;
- boff = ispattern && psuf && *psuf &&
- !(haswhat & (HAS_MISC | HAS_PATHPAT)) ? strlen(psuf) : 0;
- nboff = ispattern && psuf && *psuf &&
- !(haswhat & (HAS_MISC | HAS_PATHPAT)) ? niceztrlen(psuf) : 0;
-
- /* When called from expandorcompleteprefix, we probably have to
- remove a space now. */
- if (remove_at >= 0) {
- int ocs = cs;
-
- cs = remove_at;
- deletechar();
- remove_at = -1;
- cs = ocs;
- }
-
- /* Set the cursor below the prompt. */
- trashzle();
- ct = nmatches;
- showinglist = 0;
-
- clearflag = (isset(USEZLE) && !termflags &&
- (isset(ALWAYSLASTPROMPT) && !gotmult)) ||
- (unset(ALWAYSLASTPROMPT) && gotmult);
-
- arr = amatches;
-
- /* Calculate the column width, the number of columns and the number
- of lines. */
- for (ap = arr; *ap; ap++)
- if ((cl = niceztrlen(*ap + off) - nboff +
- (ispattern ? 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 += (niceztrlen(*ap + off) - nboff + of +
- (ispattern ? 0 :
- (!(haswhat & HAS_MISC) ? nfpl + nfsl : nlpl + nlsl))) / columns;
- } else {
- colsz = (ct + fct - 1) / fct;
- up = colsz + nlnct - clearflag + (ct == 0);
- }
-
- /* Print the explanation string, if any. */
- if (expl) {
- xup = printfmt(expl, ct, 1) + 1;
- putc('\n', shout);
- up += xup;
- }
-
- /* Maybe we have to ask if the user wants to see the list. */
- if ((listmax && ct > listmax) || (!listmax && up >= lines)) {
- int qup;
- setterm();
- qup = printfmt("zsh: do you wish to see all %n possibilities? ", ct, 1);
- fflush(shout);
- if (getzlequery() != 'y') {
- if (clearflag) {
- putc('\r', shout);
- tcmultout(TCUP, TCMULTUP, qup);
- if (tccan(TCCLEAREOD))
- tcout(TCCLEAREOD);
- tcmultout(TCUP, TCMULTUP, nlnct + xup);
- } else
- putc('\n', shout);
- return;
- }
- if (clearflag) {
- putc('\r', shout);
- tcmultout(TCUP, TCMULTUP, qup);
- if (tccan(TCCLEAREOD))
- tcout(TCCLEAREOD);
- } else
- 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;
- } 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 (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 (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;
- }
- for (t0 = colsz; t0 && *ap; t0--, ap++);
- if (*ap)
- for (; t2 < fw; t2++)
- putc(' ', shout);
- }
- if (t1 != colsz - 1 || !clearflag)
- 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);
- showinglist = -1;
- } else
- clearflag = 0, putc('\n', shout);
- }
-
- /* This is used to print expansions. */
-
- /**/
- void
- listlist(LinkList l)
- {
- int hw = haswhat, ip = ispattern;
- char *lp = lpre, *ls = lsuf;
- int nm = nmatches, vl = validlist;
- char **am = amatches;
- char *ex = expl;
-
- haswhat = HAS_MISC;
- ispattern = 0;
- validlist = 1;
- lpre = lsuf = "";
- expl = NULL;
-
- makearray(l);
- listmatches();
- showinglist = 0;
-
- expl = ex;
- amatches = am;
- nmatches = nm;
- validlist = vl;
- lpre = lp;
- lsuf = ls;
- ispattern = ip;
- haswhat = hw;
- }
-
- /* And this is used to print select lists. Hm, this function should *
- * probably be move to loop.c. */
-
- /**/
- void
- selectlist(LinkList l)
- {
- size_t longest = 1, fct, fw = 0, colsz, t0, t1, ct;
- LinkNode n;
- char **arr, **ap;
-
- trashzle();
- ct = countlinknodes(l);
- ap = arr = (char **)alloc((countlinknodes(l) + 1) * sizeof(char **));
-
- for (n = (LinkNode) firstnode(l); n; incnode(n))
- *ap++ = (char *)getdata(n);
- *ap = NULL;
- for (ap = arr; *ap; ap++)
- if (strlen(*ap) > longest)
- longest = strlen(*ap);
- t0 = ct;
- longest++;
- while (t0)
- t0 /= 10, longest++;
- /* to compensate for added ')' */
- fct = (columns - 1) / (longest + 3);
- if (fct == 0)
- fct = 1;
- else
- fw = (columns - 1) / fct;
- colsz = (ct + fct - 1) / fct;
- for (t1 = 0; t1 != colsz; t1++) {
- ap = arr + t1;
- do {
- int t2 = strlen(*ap) + 2, t3;
-
- fprintf(stderr, "%d) %s", t3 = ap - arr + 1, *ap);
- while (t3)
- t2++, t3 /= 10;
- for (; t2 < fw; t2++)
- fputc(' ', stderr);
- for (t0 = colsz; t0 && *ap; t0--, ap++);
- }
- while (*ap);
- fputc('\n', stderr);
- }
-
- /* Below is a simple attempt at doing it the Korn Way..
- ap = arr;
- t0 = 0;
- do {
- t0++;
- fprintf(stderr,"%d) %s\n",t0,*ap);
- ap++;
- }
- while (*ap);*/
- fflush(stderr);
- }
-
- /* Expand the history references. */
-
- /**/
- int
- 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;
- }
- }
-
- strcpy((char *)line, (char *)ol);
- ll = oll;
- cs = ocs;
- unmetafy_line();
-
- popheap();
- } LASTALLOC;
- return 0;
- }
-
- /**/
- void
- magicspace(void)
- {
- c = ' ';
- selfinsert();
- doexpandhist();
- }
-
- /**/
- void
- expandhistory(void)
- {
- if (!doexpandhist())
- feep();
- }
-
- static int cmdwb, cmdwe;
-
- /**/
- char *
- 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;
- }
- }
- while (tok != ENDINPUT && tok != LEXERR && zleparse);
- popheap();
- strinend();
- inpop();
- errflag = zleparse = 0;
- lexrestore();
- } LASTALLOC;
- return s;
- }
-
- /**/
- void
- processcmd(void)
- {
- char *s, *t;
-
- s = getcurcmd();
- if (!s) {
- feep();
- return;
- }
- t = zlecmds[bindk].name;
- zmult = 1;
- pushline();
- inststr(t);
- inststr(" ");
- untokenize(s);
- HEAPALLOC {
- inststr(quotename(s, NULL, NULL, NULL));
- } LASTALLOC;
- zsfree(s);
- done = 1;
- }
-
- /**/
- void
- expandcmdpath(void)
- {
- int oldcs = cs, na = noaliases;
- char *s, *str;
-
- noaliases = 1;
- s = getcurcmd();
- noaliases = na;
- if (!s || cmdwb < 0 || cmdwe < cmdwb) {
- feep();
- return;
- }
- str = findcmd(s);
- zsfree(s);
- if (!str) {
- feep();
- return;
- }
- cs = cmdwb;
- foredel(cmdwe - cmdwb);
- spaceinline(strlen(str));
- strncpy((char *)line + cs, str, strlen(str));
- cs = oldcs;
- if (cs >= cmdwe - 1)
- cs += cmdwe - cmdwb + strlen(str);
- if (cs > ll)
- cs = ll;
- zsfree(str);
- }
-
- /* Extra function added by AR Iano-Fletcher. */
- /* This is a expand/complete in the vein of wash. */
-
- /**/
- void
- expandorcompleteprefix(void)
- {
- /* global c is the current character typed. */
- int csafe = c;
-
- /* insert a space and backspace. */
- c = ' ';
- selfinsert(); /* insert the extra character */
- forwardchar(); /* move towards beginning */
-
- remove_at = cs;
-
- /* do the expansion/completion. */
- c = csafe;
- zmult = 1;
- expandorcomplete(); /* complete. */
- zmult = -1;
-
- /* remove the inserted space. */
- if (remove_at >= 0) {
- backwardchar(); /* move towards ends */
- deletechar(); /* delete the added space. */
- }
- remove_at = -1;
- }
-