From 48525452555a24b9d41748f26b4b77f160f01220 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sat, 1 Apr 2000 20:49:47 +0000 Subject: Updated from list as far as 10376 --- Src/Zle/comp.h | 449 +++++++++++---- Src/Zle/compcore.c | 1500 +++++++++++++++++++++++++++---------------------- Src/Zle/complete.c | 436 +++++++------- Src/Zle/compmatch.c | 124 ++-- Src/Zle/compresult.c | 515 +++++++++-------- Src/Zle/computil.c | 1498 +++++++++++++++++++++++++++++++++++------------- Src/Zle/iwidgets.list | 43 +- Src/Zle/zle_utils.c | 92 +-- 8 files changed, 2949 insertions(+), 1708 deletions(-) (limited to 'Src/Zle') diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 5d7ef74e2..f513d4a5a 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -27,111 +27,362 @@ * */ -#undef compctlread +typedef struct cmatcher *Cmatcher; +typedef struct cmlist *Cmlist; +typedef struct cpattern *Cpattern; +typedef struct menuinfo *Menuinfo; +typedef struct cexpl *Cexpl; +typedef struct cmgroup *Cmgroup; +typedef struct cmatch *Cmatch; -typedef struct compctlp *Compctlp; -typedef struct compctl *Compctl; -typedef struct compcond *Compcond; +/* This is for explantion strings. */ -/* node for compctl hash table (compctltab) */ +struct cexpl { + char *str; /* the string */ + int count; /* the number of matches */ + int fcount; /* number of matches with fignore ignored */ +}; + +/* This describes a group of matches. */ + +struct cmgroup { + char *name; /* the name of this group */ + Cmgroup prev; /* previous on the list */ + Cmgroup next; /* next one in list */ + int flags; /* see CGF_* below */ + int mcount; /* number of matches */ + Cmatch *matches; /* the matches */ + int lcount; /* number of things to list here */ + int llcount; /* number of line-displays */ + char **ylist; /* things to list */ + int ecount; /* number of explanation string */ + Cexpl *expls; /* explanation strings */ + int ccount; /* number of compctls used */ + LinkList lexpls; /* list of explanation string while building */ + LinkList lmatches; /* list of matches */ + LinkList lfmatches; /* list of matches without fignore */ + LinkList lallccs; /* list of used compctls */ + int num; /* number of this group */ + int nbrbeg; /* number of opened braces */ + int nbrend; /* number of closed braces */ + /* The following is collected/used during listing. */ + int dcount; /* number of matches to list in columns */ + int cols; /* number of columns */ + int lins; /* number of lines */ + int width; /* column width */ + int *widths; /* column widths for listpacked */ + int totl; /* total length */ + int shortest; /* length of shortest match */ + Cmgroup perm; /* perm. alloced version of this group */ + int new; /* new matches since last permalloc() */ +}; + + +#define CGF_NOSORT 1 /* don't sort this group */ +#define CGF_LINES 2 /* these are to be printed on different lines */ +#define CGF_HASDL 4 /* has display strings printed on separate lines */ +#define CGF_UNIQALL 8 /* remove all duplicates */ +#define CGF_UNIQCON 16 /* remove consecutive duplicates */ +#define CGF_PACKED 32 /* LIST_PACKED for this group */ +#define CGF_ROWS 64 /* LIST_ROWS_FIRST for this group */ + +/* This is the struct used to hold matches. */ + +struct cmatch { + char *str; /* the match itself */ + char *ipre; /* ignored prefix, has to be re-inserted */ + char *ripre; /* ignored prefix, unquoted */ + char *isuf; /* ignored suffix */ + char *ppre; /* the path prefix */ + char *psuf; /* the path suffix */ + char *prpre; /* path prefix for opendir */ + char *pre; /* prefix string from -P */ + char *suf; /* suffix string from -S */ + char *disp; /* string to display (compadd -d) */ + char *autoq; /* closing quote to add automatically */ + int flags; /* see CMF_* below */ + int *brpl; /* places where to put the brace prefixes */ + int *brsl; /* ...and the suffixes */ + char *rems; /* when to remove the suffix */ + char *remf; /* shell function to call for suffix-removal */ + int qipl; /* length of quote-prefix */ + int qisl; /* length of quote-suffix */ + int rnum; /* group relative number */ + int gnum; /* global number */ +}; + +#define CMF_FILE (1<< 0) /* this is a file */ +#define CMF_REMOVE (1<< 1) /* remove the suffix */ +#define CMF_ISPAR (1<< 2) /* is paramter expansion */ +#define CMF_PARBR (1<< 3) /* paramter expansion with a brace */ +#define CMF_PARNEST (1<< 4) /* nested paramter expansion */ +#define CMF_NOLIST (1<< 5) /* should not be listed */ +#define CMF_DISPLINE (1<< 6) /* display strings one per line */ +#define CMF_HIDE (1<< 7) /* temporarily hide this one */ +#define CMF_NOSPACE (1<< 8) /* don't add a space */ +#define CMF_PACKED (1<< 9) /* prefer LIST_PACKED */ +#define CMF_ROWS (1<<10) /* prefer LIST_ROWS_FIRST */ +#define CMF_MULT (1<<11) /* string appears more than once */ +#define CMF_FMULT (1<<12) /* first of multiple equal strings */ + +/* Stuff for completion matcher control. */ + +struct cmlist { + Cmlist next; /* next one in the list of global matchers */ + Cmatcher matcher; /* the matcher definition */ + char *str; /* the string for it */ +}; + +struct cmatcher { + int refc; /* reference counter */ + Cmatcher next; /* next matcher */ + int flags; /* see CMF_* below */ + Cpattern line; /* what matches on the line */ + int llen; /* length of line pattern */ + Cpattern word; /* what matches in the word */ + int wlen; /* length of word pattern */ + Cpattern left; /* left anchor */ + int lalen; /* length of left anchor */ + Cpattern right; /* right anchor */ + int ralen; /* length of right anchor */ +}; + +#define CMF_LINE 1 +#define CMF_LEFT 2 +#define CMF_RIGHT 4 + +struct cpattern { + Cpattern next; /* next sub-pattern */ + unsigned char tab[256]; /* table of matched characters */ + int equiv; /* if this is a {...} class */ +}; + +/* This is a special return value for parse_cmatcher(), * + * signalling an error. */ + +#define pcm_err ((Cmatcher) 1) + +/* Information about what to put on the line as the unambiguous string. + * The code always keeps lists of these structs up to date while + * matches are added (in the aminfo structs below). + * The lists have two levels: in the first one we have one struct per + * word-part, where parts are separated by the anchors of `*' patterns. + * These structs have pointers (in the prefix and suffix fields) to + * lists of cline structs describing the strings before or after the + * the anchor. */ + +typedef struct cline *Cline; +typedef struct clsub Clsub; + +struct cline { + Cline next; + int flags; + char *line; + int llen; + char *word; + int wlen; + char *orig; + int olen; + int slen; + Cline prefix, suffix; + int min, max; +}; + +#define CLF_MISS 1 +#define CLF_DIFF 2 +#define CLF_SUF 4 +#define CLF_MID 8 +#define CLF_NEW 16 +#define CLF_LINE 32 +#define CLF_JOIN 64 +#define CLF_MATCHED 128 + +/* Information for ambiguous completions. One for fignore ignored and * + * one for normal completion. */ + +typedef struct aminfo *Aminfo; -struct compctlp { - HashNode next; /* next in hash chain */ - char *nam; /* command name */ - int flags; /* CURRENTLY UNUSED */ - Compctl cc; /* pointer to the compctl desc. */ +struct aminfo { + Cmatch firstm; /* the first match */ + int exact; /* if there was an exact match */ + Cmatch exactm; /* the exact match (if any) */ + int count; /* number of matches */ + Cline line; /* unambiguous line string */ }; -/* compctl -x condition */ - -struct compcond { - Compcond and, or; /* the next or'ed/and'ed conditions */ - int type; /* the type (CCT_*) */ - int n; /* the array length */ - union { /* these structs hold the data used to */ - struct { /* test this condition */ - int *a, *b; /* CCT_POS, CCT_NUMWORDS */ - } - r; - struct { /* CCT_CURSTR, CCT_CURPAT,... */ - int *p; - char **s; - } - s; - struct { /* CCT_RANGESTR,... */ - char **a, **b; - } - l; - } - u; +/* Information about menucompletion stuff. */ + +struct menuinfo { + Cmgroup group; /* position in the group list */ + Cmatch *cur; /* match currently inserted */ + int pos; /* begin on line */ + int len; /* length of inserted string */ + int end; /* end on the line */ + int we; /* non-zero if the cursor was at the end */ + int insc; /* length of suffix inserted */ + int asked; /* we asked if the list should be shown */ + char *prebr; /* prefix before a brace, if any */ + char *postbr; /* suffix after a brace */ +}; + +/* Flags for compadd and addmatches(). */ + +#define CAF_QUOTE 1 +#define CAF_NOSORT 2 +#define CAF_MATCH 4 +#define CAF_UNIQCON 8 +#define CAF_UNIQALL 16 + +/* Data for compadd and addmatches() */ + +typedef struct cadata *Cadata; + +struct cadata { + char *ipre; /* ignored prefix (-i) */ + char *isuf; /* ignored suffix (-I) */ + char *ppre; /* `path' prefix (-p) */ + char *psuf; /* `path' suffix (-s) */ + char *prpre; /* expanded `path' prefix (-W) */ + char *pre; /* prefix to insert (-P) */ + char *suf; /* suffix to insert (-S) */ + char *group; /* name of the group (-[JV]) */ + char *rems; /* remove suffix on chars... (-r) */ + char *remf; /* function to remove suffix (-R) */ + char *ign; /* ignored suffixes (-F) */ + int flags; /* CMF_* flags (-[fqn]) */ + int aflags; /* CAF_* flags (-[QUa]) */ + Cmatcher match; /* match spec (parsed from -M) */ + char *exp; /* explanation (-X) */ + char *apar; /* array to store matches in (-A) */ + char *opar; /* array to store originals in (-O) */ + char *dpar; /* array to delete non-matches in (-D) */ + char *disp; /* array with display lists (-d) */ }; -#define CCT_UNUSED 0 -#define CCT_POS 1 -#define CCT_CURSTR 2 -#define CCT_CURPAT 3 -#define CCT_WORDSTR 4 -#define CCT_WORDPAT 5 -#define CCT_CURSUF 6 -#define CCT_CURPRE 7 -#define CCT_CURSUB 8 -#define CCT_CURSUBC 9 -#define CCT_NUMWORDS 10 -#define CCT_RANGESTR 11 -#define CCT_RANGEPAT 12 - -/* Contains the real description for compctls */ - -struct compctl { - int refc; /* reference count */ - Compctl next; /* next compctl for -x */ - unsigned long mask; /* mask of things to complete (CC_*) */ - char *keyvar; /* for -k (variable) */ - char *glob; /* for -g (globbing) */ - char *str; /* for -s (expansion) */ - char *func; /* for -K (function) */ - char *explain; /* for -X (explanation) */ - char *ylist; /* for -y (user-defined desc. for listing) */ - char *prefix, *suffix; /* for -P and -S (prefix, suffix) */ - char *subcmd; /* for -l (command name to use) */ - char *withd; /* for -w (with directory */ - char *hpat; /* for -H (history pattern) */ - int hnum; /* for -H (number of events to search) */ - Compctl ext; /* for -x (first of the compctls after -x) */ - Compcond cond; /* for -x (condition for this compctl) */ - Compctl xor; /* for + (next of the xor'ed compctls) */ +/* List data. */ + +typedef struct cldata *Cldata; + +struct cldata { + int columns; /* screen width */ + int lines; /* screen height */ + int menuacc; /* value of global menuacc */ + int valid; /* no need to calculate anew */ + int nlist; /* number of matches to list */ + int nlines; /* number of lines needed */ + int hidden; /* != 0 if there are hidden matches */ + int onlyexpl; /* != 0 if only explanations to print */ + int showall; /* != 0 if hidden matches should be shown */ +}; + +typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int, + char *, struct stat *); + +/* Flags for fromcomp. */ + +#define FC_LINE 1 +#define FC_INWORD 2 + +/* Flags for special parameters. */ + +#define CPN_WORDS 0 +#define CP_WORDS (1 << CPN_WORDS) +#define CPN_CURRENT 1 +#define CP_CURRENT (1 << CPN_CURRENT) +#define CPN_PREFIX 2 +#define CP_PREFIX (1 << CPN_PREFIX) +#define CPN_SUFFIX 3 +#define CP_SUFFIX (1 << CPN_SUFFIX) +#define CPN_IPREFIX 4 +#define CP_IPREFIX (1 << CPN_IPREFIX) +#define CPN_ISUFFIX 5 +#define CP_ISUFFIX (1 << CPN_ISUFFIX) +#define CPN_QIPREFIX 6 +#define CP_QIPREFIX (1 << CPN_QIPREFIX) +#define CPN_QISUFFIX 7 +#define CP_QISUFFIX (1 << CPN_QISUFFIX) +#define CPN_COMPSTATE 8 +#define CP_COMPSTATE (1 << CPN_COMPSTATE) + +#define CP_REALPARAMS 9 +#define CP_ALLREALS ((unsigned int) 0x1ff) + + +#define CPN_NMATCHES 0 +#define CP_NMATCHES (1 << CPN_NMATCHES) +#define CPN_CONTEXT 1 +#define CP_CONTEXT (1 << CPN_CONTEXT) +#define CPN_PARAMETER 2 +#define CP_PARAMETER (1 << CPN_PARAMETER) +#define CPN_REDIRECT 3 +#define CP_REDIRECT (1 << CPN_REDIRECT) +#define CPN_QUOTE 4 +#define CP_QUOTE (1 << CPN_QUOTE) +#define CPN_QUOTING 5 +#define CP_QUOTING (1 << CPN_QUOTING) +#define CPN_RESTORE 6 +#define CP_RESTORE (1 << CPN_RESTORE) +#define CPN_LIST 7 +#define CP_LIST (1 << CPN_LIST) +#define CPN_INSERT 8 +#define CP_INSERT (1 << CPN_INSERT) +#define CPN_EXACT 9 +#define CP_EXACT (1 << CPN_EXACT) +#define CPN_EXACTSTR 10 +#define CP_EXACTSTR (1 << CPN_EXACTSTR) +#define CPN_PATMATCH 11 +#define CP_PATMATCH (1 << CPN_PATMATCH) +#define CPN_PATINSERT 12 +#define CP_PATINSERT (1 << CPN_PATINSERT) +#define CPN_UNAMBIG 13 +#define CP_UNAMBIG (1 << CPN_UNAMBIG) +#define CPN_UNAMBIGC 14 +#define CP_UNAMBIGC (1 << CPN_UNAMBIGC) +#define CPN_LISTMAX 15 +#define CP_LISTMAX (1 << CPN_LISTMAX) +#define CPN_LASTPROMPT 16 +#define CP_LASTPROMPT (1 << CPN_LASTPROMPT) +#define CPN_TOEND 17 +#define CP_TOEND (1 << CPN_TOEND) +#define CPN_OLDLIST 18 +#define CP_OLDLIST (1 << CPN_OLDLIST) +#define CPN_OLDINS 19 +#define CP_OLDINS (1 << CPN_OLDINS) +#define CPN_VARED 20 +#define CP_VARED (1 << CPN_VARED) +#define CPN_LISTLINES 21 +#define CP_LISTLINES (1 << CPN_LISTLINES) +#define CPN_QUOTES 22 +#define CP_QUOTES (1 << CPN_QUOTES) +#define CPN_IGNORED 23 +#define CP_IGNORED (1 << CPN_IGNORED) + +#define CP_KEYPARAMS 24 +#define CP_ALLKEYS ((unsigned int) 0xffffff) + +/* Hooks. */ + +#define INSERTMATCHHOOK (comphooks + 0) +#define MENUSTARTHOOK (comphooks + 1) +#define COMPCTLMAKEHOOK (comphooks + 2) +#define COMPCTLCLEANUPHOOK (comphooks + 3) +#define COMPLISTMATCHESHOOK (comphooks + 4) + +/* compctl hook data struct */ + +struct ccmakedat { + char *str; + int incmd; + int lst; +}; + +/* Data given to offered hooks. */ + +typedef struct chdata *Chdata; + +struct chdata { + Cmgroup matches; /* the matches generated */ + int num; /* the number of matches */ + Cmatch cur; /* current match or NULL */ }; -/* objects to complete */ -#define CC_FILES (1<<0) -#define CC_COMMPATH (1<<1) -#define CC_REMOVE (1<<2) -#define CC_OPTIONS (1<<3) -#define CC_VARS (1<<4) -#define CC_BINDINGS (1<<5) -#define CC_ARRAYS (1<<6) -#define CC_INTVARS (1<<7) -#define CC_SHFUNCS (1<<8) -#define CC_PARAMS (1<<9) -#define CC_ENVVARS (1<<10) -#define CC_JOBS (1<<11) -#define CC_RUNNING (1<<12) -#define CC_STOPPED (1<<13) -#define CC_BUILTINS (1<<14) -#define CC_ALREG (1<<15) -#define CC_ALGLOB (1<<16) -#define CC_USERS (1<<17) -#define CC_DISCMDS (1<<18) -#define CC_EXCMDS (1<<19) -#define CC_SCALARS (1<<20) -#define CC_READONLYS (1<<21) -#define CC_SPECIALS (1<<22) -#define CC_DELETE (1<<23) -#define CC_NAMED (1<<24) -#define CC_QUOTEFLAG (1<<25) -#define CC_EXTCMDS (1<<26) -#define CC_RESWDS (1<<27) -#define CC_DIRS (1<<28) - -#define CC_EXPANDEXPL (1<<30) -#define CC_RESERVED (1<<31) diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index ea9ff5b20..3cc080cac 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -28,9 +28,6 @@ */ #include "complete.mdh" -#define GLOBAL_PROTOTYPES -#include "zle_tricky.pro" -#undef GLOBAL_PROTOTYPES #include "compcore.pro" /* The last completion widget called. */ @@ -40,11 +37,17 @@ static Widget lastcompwidget; /* Flags saying what we have to do with the result. */ /**/ -int useexact, useline, uselist; +int useexact, useline, uselist, forcelist, startauto; + +/* Non-zero if we should go back to the last prompt. */ + +/**/ +int dolastprompt; /* Non-zero if we should keep an old list. */ /**/ +mod_export int oldlist, oldins; /* This is used to decide when the cursor should be moved to the end of * @@ -57,17 +60,17 @@ int movetoend; /* The match and group number to insert when starting menucompletion. */ /**/ -int insmnum, insgnum, insgroup, insspace; +mod_export int insmnum, insgnum, insgroup, insspace; /* Information about menucompletion. */ /**/ -struct menuinfo minfo; +mod_export struct menuinfo minfo; /* Number of matches accepted with accept-and-menu-complete */ /**/ -int menuacc; +mod_export int menuacc; /* Brace insertion stuff. */ @@ -77,7 +80,7 @@ int hasunqu, useqbr, brpcs, brscs; /* Flags saying in what kind of string we are. */ /**/ -int ispar, linwhat; +mod_export int ispar, linwhat; /* A parameter expansion prefix (like ${). */ @@ -92,7 +95,7 @@ int parflags; /* Match flags for all matches in this group. */ /**/ -int mflags; +mod_export int mflags; /* Flags saying how the parameter expression we are in is quoted. */ @@ -101,17 +104,18 @@ int parq, eparq; /* We store the following prefixes/suffixes: * * ipre,ripre -- the ignored prefix (quoted and unquoted) * - * isuf -- the ignored suffix * - * autoq -- quotes to automatically insert */ + * isuf -- the ignored suffix */ /**/ -char *ipre, *ripre, *isuf; +mod_export char *ipre, *ripre, *isuf; /* The list of matches. fmatches contains the matches we first ignore * * because of fignore. */ /**/ -LinkList matches, fmatches; +mod_export LinkList matches; +/**/ +LinkList fmatches; /* This holds the list of matches-groups. lastmatches holds the last list of * permanently allocated matches, pmatches is the same for the list @@ -120,47 +124,49 @@ LinkList matches, fmatches; * lmatches/lastlmatches is a pointer to the last element in the lists. */ /**/ -Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches; +mod_export Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches; /* Non-zero if we have permanently allocated matches (old and new). */ /**/ -int hasoldlist, hasperm; +mod_export int hasoldlist, hasperm; /* Non-zero if we have newly added matches. */ /**/ -int newmatches; +mod_export int newmatches; /* Number of permanently allocated matches and groups. */ /**/ -int permmnum, permgnum, lastpermmnum, lastpermgnum; +mod_export int permmnum, permgnum, lastpermmnum, lastpermgnum; /* The total number of matches and the number of matches to be listed. */ /**/ -int nmatches, smatches; +mod_export int nmatches; +/**/ +mod_export int smatches; /* != 0 if only explanation strings should be printed */ /**/ -int onlyexpl; +mod_export int onlyexpl; /* Information about the matches for listing. */ /**/ -struct cldata listdat; +mod_export struct cldata listdat; /* This flag is non-zero if we are completing a pattern (with globcomplete) */ /**/ -int ispattern, haspattern; +mod_export int ispattern, haspattern; -/* Non-zero if at least one match was added without -U. */ +/* Non-zero if at least one match was added without/with -U. */ /**/ -int hasmatched; +mod_export int hasmatched, hasunmatched; /* The current group of matches. */ @@ -170,12 +176,12 @@ Cmgroup mgroup; /* Match counter: all matches. */ /**/ -int mnum; +mod_export int mnum; /* The match counter when unambig_data() was called. */ /**/ -int unambig_mnum; +mod_export int unambig_mnum; /* Length of longest/shortest match. */ @@ -189,37 +195,37 @@ int maxmlen, minmlen; LinkList expls; /**/ -Cexpl curexpl; +mod_export Cexpl curexpl; /* A stack of completion matchers to be used. */ /**/ -Cmlist mstack; +mod_export Cmlist mstack; /* The completion matchers used when building new stuff for the line. */ /**/ -Cmlist bmatchers; +mod_export Cmlist bmatchers; /* A list with references to all matchers we used. */ /**/ -LinkList matchers; +mod_export LinkList matchers; /* A heap of free Cline structures. */ /**/ -Cline freecl; +mod_export Cline freecl; /* Ambiguous information. */ /**/ -Aminfo ainfo, fainfo; +mod_export Aminfo ainfo, fainfo; /* The memory heap to use for new style completion generation. */ /**/ -Heap compheap; +mod_export Heap compheap; /* A list of some data. * @@ -227,7 +233,7 @@ Heap compheap; * conceptually we don't know anything about compctls here... */ /**/ -LinkList allccs; +mod_export LinkList allccs; /* This says what of the state the line is in when completion is started * * came from a previous completion. If the FC_LINE bit is set, the * @@ -246,10 +252,6 @@ int fromcomp; /**/ int lastend; -/* Convenience macro for calling bslashquote() (formerly quotename()). */ - -#define quotename(s, e) bslashquote(s, e, instring) - #define inststr(X) inststrlen((X),1,-1) /* Main completion entry point, called from zle. */ @@ -260,153 +262,165 @@ do_completion(Hookdef dummy, Compldat dat) { int ret = 0, lst = dat->lst, incmd = dat->incmd; char *s = dat->s; + char *opm; + LinkNode n; - HEAPALLOC { - char *opm; - LinkNode n; - - pushheap(); - - ainfo = fainfo = NULL; - matchers = newlinklist(); - - hasunqu = 0; - useline = (lst != COMP_LIST_COMPLETE); - useexact = isset(RECEXACT); - uselist = (useline ? - ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? - (isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1); - zsfree(comppatmatch); - opm = comppatmatch = ztrdup(useglob ? "*" : ""); - zsfree(comppatinsert); - comppatinsert = ztrdup("menu"); - zsfree(compforcelist); - compforcelist = ztrdup(""); - haspattern = 0; - complistmax = getiparam("LISTMAX"); - zsfree(complastprompt); - complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) || - (unset(ALWAYSLASTPROMPT) && zmult != 1)) ? - "yes" : ""); - movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1); - showinglist = 0; - hasmatched = 0; - minmlen = 1000000; - maxmlen = -1; - - /* Make sure we have the completion list and compctl. */ - if (makecomplist(s, incmd, lst)) { - /* Error condition: feeeeeeeeeeeeep(). */ - cs = 0; - foredel(ll); - inststr(origline); - cs = origcs; - clearlist = 1; - ret = 1; - minfo.cur = NULL; - goto compend; - } - zsfree(lastprebr); - zsfree(lastpostbr); - lastprebr = lastpostbr = NULL; - - if (comppatmatch && *comppatmatch && comppatmatch != opm) - haspattern = 1; - if (!useline && uselist) { - /* All this and the guy only wants to see the list, sigh. */ - cs = 0; - foredel(ll); - inststr(origline); - cs = origcs; - showinglist = -2; - } else if (useline == 2 && nmatches > 1) { - int first = 1, nm = nmatches; - Cmatch *mc; + pushheap(); + + ainfo = fainfo = NULL; + matchers = newlinklist(); + + zsfree(compqstack); + compqstack = ztrdup("\\"); + if (instring == 2) + compqstack[0] = '"'; + else if (instring) + compqstack[0] = '\''; + + hasunqu = 0; + useline = (lst != COMP_LIST_COMPLETE); + useexact = isset(RECEXACT); + zsfree(compexactstr); + compexactstr = ztrdup(""); + uselist = (useline ? + ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? + (isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1); + zsfree(comppatmatch); + opm = comppatmatch = ztrdup(useglob ? "*" : ""); + zsfree(comppatinsert); + comppatinsert = ztrdup("menu"); + forcelist = 0; + haspattern = 0; + complistmax = getiparam("LISTMAX"); + zsfree(complastprompt); + complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) || + (unset(ALWAYSLASTPROMPT) && zmult != 1)) ? + "yes" : ""); + dolastprompt = 1; + zsfree(complist); + complist = ztrdup(isset(LISTROWSFIRST) ? + (isset(LISTPACKED) ? "packed rows" : "rows") : + (isset(LISTPACKED) ? "packed" : "")); + startauto = isset(AUTOMENU); + movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1); + showinglist = 0; + hasmatched = hasunmatched = 0; + minmlen = 1000000; + maxmlen = -1; + compignored = 0; + + /* Make sure we have the completion list and compctl. */ + if (makecomplist(s, incmd, lst)) { + /* Error condition: feeeeeeeeeeeeep(). */ + cs = 0; + foredel(ll); + inststr(origline); + cs = origcs; + clearlist = 1; + ret = 1; + minfo.cur = NULL; + goto compend; + } + zsfree(lastprebr); + zsfree(lastpostbr); + lastprebr = lastpostbr = NULL; + + if (comppatmatch && *comppatmatch && comppatmatch != opm) + haspattern = 1; + if (!useline && uselist) { + /* All this and the guy only wants to see the list, sigh. */ + cs = 0; + foredel(ll); + inststr(origline); + cs = origcs; + showinglist = -2; + } else if (useline == 2 && nmatches > 1) { + int first = 1, nm = nmatches; + Cmatch *mc; - menucmp = 1; - menuacc = 0; + menucmp = 1; + menuacc = 0; - for (minfo.group = amatches; - minfo.group && !(minfo.group)->mcount; - minfo.group = (minfo.group)->next); + for (minfo.group = amatches; + minfo.group && !(minfo.group)->mcount; + minfo.group = (minfo.group)->next); - mc = (minfo.group)->matches; + mc = (minfo.group)->matches; - while (1) { - if (!first) - acceptlast(); - first = 0; + while (1) { + if (!first) + accept_last(); + first = 0; - if (!--nm) - menucmp = 0; + if (!--nm) + menucmp = 0; - do_single(*mc); - minfo.cur = mc; + do_single(*mc); + minfo.cur = mc; - if (!*++(minfo.cur)) { - do { - if (!(minfo.group = (minfo.group)->next)) - break; - } while (!(minfo.group)->mcount); - if (!minfo.group) + if (!*++(minfo.cur)) { + do { + if (!(minfo.group = (minfo.group)->next)) break; - minfo.cur = minfo.group->matches; - } - mc = minfo.cur; + } while (!(minfo.group)->mcount); + if (!minfo.group) + break; + minfo.cur = minfo.group->matches; } - menucmp = 0; - minfo.cur = NULL; - - if (compforcelist && *compforcelist && uselist) - showinglist = -2; - else - invalidatelist(); - } else if (useline) { - /* We have matches. */ - if (nmatches > 1) { - /* There is more than one match. */ - ret = do_ambiguous(); - } else if (nmatches == 1) { - /* Only one match. */ - Cmgroup m = amatches; - - while (!m->mcount) - m = m->next; - minfo.cur = NULL; - minfo.asked = 0; - do_single(m->matches[0]); - if (compforcelist && *compforcelist) { - if (uselist) - showinglist = -2; - else - clearlist = 1; - } else - invalidatelist(); - } - } else { - invalidatelist(); - if (compforcelist && *compforcelist) - clearlist = 1; - cs = 0; - foredel(ll); - inststr(origline); - cs = origcs; + mc = minfo.cur; } - /* Print the explanation strings if needed. */ - if (!showinglist && validlist && usemenu != 2 && nmatches != 1 && - useline != 2 && (!oldlist || !listshown)) { - onlyexpl = 1; + menucmp = 0; + minfo.cur = NULL; + + if (forcelist) showinglist = -2; + else + invalidatelist(); + } else if (useline) { + /* We have matches. */ + if (nmatches > 1) { + /* There is more than one match. */ + ret = do_ambiguous(); + } else if (nmatches == 1) { + /* Only one match. */ + Cmgroup m = amatches; + + while (!m->mcount) + m = m->next; + minfo.cur = NULL; + minfo.asked = 0; + do_single(m->matches[0]); + if (forcelist) { + if (uselist) + showinglist = -2; + else + clearlist = 1; + } else + invalidatelist(); } - compend: - for (n = firstnode(matchers); n; incnode(n)) - freecmatcher((Cmatcher) getdata(n)); + } else { + invalidatelist(); + if (forcelist) + clearlist = 1; + cs = 0; + foredel(ll); + inststr(origline); + cs = origcs; + } + /* Print the explanation strings if needed. */ + if (!showinglist && validlist && usemenu != 2 && nmatches != 1 && + useline != 2 && (!oldlist || !listshown)) { + onlyexpl = 1; + showinglist = -2; + } + compend: + for (n = firstnode(matchers); n; incnode(n)) + freecmatcher((Cmatcher) getdata(n)); - ll = strlen((char *)line); - if (cs > ll) - cs = ll; - popheap(); - } LASTALLOC; + ll = strlen((char *)line); + if (cs > ll) + cs = ll; + popheap(); return ret; } @@ -428,7 +442,7 @@ before_complete(Hookdef dummy, int *lst) /* If we are doing a menu-completion... */ if (menucmp && *lst != COMP_LIST_EXPAND && - (!compwidget || compwidget == lastcompwidget)) { + (menucmp != 1 || !compwidget || compwidget == lastcompwidget)) { do_menucmp(*lst); return 1; } @@ -442,13 +456,12 @@ before_complete(Hookdef dummy, int *lst) /* We may have to reset the cursor to its position after the * * string inserted by the last completion. */ - if (fromcomp & FC_INWORD) - if ((cs = lastend) > ll) - cs = ll; + if ((fromcomp & FC_INWORD) && (cs = lastend) > ll) + cs = ll; /* Check if we have to start a menu-completion (via automenu). */ - if (isset(AUTOMENU) && lastambig && + if (startauto && lastambig && (!isset(BASHAUTOLIST) || lastambig == 2)) usemenu = 2; @@ -477,11 +490,11 @@ after_complete(Hookdef dummy, Compldat dat) static void callcompfunc(char *s, char *fn) { - List list; + Eprog prog; int lv = lastval; char buf[20]; - if ((list = getshfunc(fn)) != &dummy_list) { + if ((prog = getshfunc(fn)) != &dummy_eprog) { char **p, *tmp; int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext; unsigned int rset, kset; @@ -493,7 +506,7 @@ callcompfunc(char *s, char *fn) rset = CP_ALLREALS; kset = CP_ALLKEYS & ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING | - CP_EXACTSTR | CP_FORCELIST | CP_OLDLIST | CP_OLDINS | + CP_EXACTSTR | CP_OLDLIST | CP_OLDINS | (useglob ? 0 : CP_PATMATCH)); zsfree(compvared); if (varedarg) { @@ -566,16 +579,11 @@ callcompfunc(char *s, char *fn) if (usea && (!aadd || clwords[0])) { char **q; - PERMALLOC { - q = compwords = (char **) - zalloc((clwnum + 1) * sizeof(char *)); - for (p = clwords + aadd; *p; p++, q++) { - tmp = dupstring(*p); - untokenize(tmp); - *q = ztrdup(tmp); - } - *q = NULL; - } LASTALLOC; + q = compwords = (char **) + zalloc((clwnum + 1) * sizeof(char *)); + for (p = clwords + aadd; *p; p++, q++) + untokenize(*q = ztrdup(*p)); + *q = NULL; } else compwords = (char **) zcalloc(sizeof(char *)); @@ -603,7 +611,7 @@ callcompfunc(char *s, char *fn) zsfree(compprefix); zsfree(compsuffix); if (unset(COMPLETEINWORD)) { - tmp = quotename(s, NULL); + tmp = multiquote(s, 0); untokenize(tmp); compprefix = ztrdup(tmp); compsuffix = ztrdup(""); @@ -614,11 +622,11 @@ callcompfunc(char *s, char *fn) sav = *ss; *ss = '\0'; - tmp = quotename(s, NULL); + tmp = multiquote(s, 0); untokenize(tmp); compprefix = ztrdup(tmp); *ss = sav; - ss = quotename(ss, NULL); + ss = multiquote(ss, 0); untokenize(ss); compsuffix = ztrdup(ss); } @@ -639,11 +647,20 @@ callcompfunc(char *s, char *fn) case 2: complist = "autolist"; break; case 3: complist = "ambiguous"; break; } + if (isset(LISTPACKED)) + complist = dyncat(complist, " packed"); + if (isset(LISTROWSFIRST)) + complist = dyncat(complist, " rows"); + complist = ztrdup(complist); zsfree(compinsert); if (useline) { switch (usemenu) { - case 0: compinsert = "unambiguous"; break; + case 0: + compinsert = (isset(AUTOMENU) ? + "automenu-unambiguous" : + "unambiguous"); + break; case 1: compinsert = "menu"; break; case 2: compinsert = "automenu"; break; } @@ -701,7 +718,7 @@ callcompfunc(char *s, char *fn) while (*p) addlinknode(largs, dupstring(*p++)); } - doshfunc(fn, list, largs, 0, 0); + doshfunc(fn, prog, largs, 0, 0); cfret = lastval; lastval = olv; } OLDHEAPS; @@ -720,13 +737,14 @@ callcompfunc(char *s, char *fn) uselist = 3; else uselist = 0; - + forcelist = (complist && strstr(complist, "force")); onlyexpl = (complist && strstr(complist, "expl")); if (!compinsert) useline = 0; else if (!strcmp(compinsert, "unambig") || - !strcmp(compinsert, "unambiguous")) + !strcmp(compinsert, "unambiguous") || + !strcmp(compinsert, "automenu-unambiguous")) useline = 1, usemenu = 0; else if (!strcmp(compinsert, "menu")) useline = 1, usemenu = 1; @@ -747,6 +765,8 @@ callcompfunc(char *s, char *fn) insspace = (compinsert[strlen(compinsert) - 1] == ' '); } else useline = usemenu = 0; + startauto = (compinsert && + !strcmp(compinsert, "automenu-unambiguous")); useexact = (compexact && !strcmp(compexact, "accept")); if (!comptoend || !*comptoend) @@ -782,60 +802,21 @@ callcompfunc(char *s, char *fn) static int makecomplist(char *s, int incmd, int lst) { - struct cmlist ms; - Cmlist m; - char *p, *os = s; - int onm = nmatches, osi = movefd(0); + char *p; /* Inside $... ? */ if (compfunc && (p = check_param(s, 0, 0))) - os = s = p; - - /* We build a copy of the list of matchers to use to make sure that this - * works even if a shell function called from the completion code changes - * the global matchers. */ - - if ((m = cmatcher)) { - Cmlist mm, *mp = &mm; - int n; - - for (n = 0; m; m = m->next, n++) { - if (m->matcher) { - *mp = (Cmlist) zhalloc(sizeof(struct cmlist)); - (*mp)->matcher = m->matcher; - (*mp)->next = NULL; - (*mp)->str = dupstring(m->str); - mp = &((*mp)->next); - addlinknode(matchers, m->matcher); - m->matcher->refc++; - } - } - m = mm; - compmatcher = 1; - compmatchertot = n; - } else - compmatcher = 0; + s = p; linwhat = inwhat; - /* Walk through the global matchers. */ - for (;;) { + if (compfunc) { + char *os = s; + int onm = nmatches, osi = movefd(0); + bmatchers = NULL; - zsfree(compmatcherstr); - if (m) { - ms.next = NULL; - ms.matcher = m->matcher; - mstack = &ms; - - /* Store the matchers used in the bmatchers list which is used - * when building new parts for the string to insert into the - * line. */ - add_bmatchers(m->matcher); - compmatcherstr = ztrdup(m->str); - } else { - mstack = NULL; - compmatcherstr = ztrdup(""); - } + mstack = NULL; + ainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); fainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); @@ -852,23 +833,12 @@ makecomplist(char *s, int incmd, int lst) begcmgroup("default", 0); menucmp = menuacc = newmatches = onlyexpl = 0; - runhookdef(COMPCTLBEFOREHOOK, NULL); - s = dupstring(os); - if (compfunc) - callcompfunc(s, compfunc); - else { - struct ccmakedat dat; - - dat.str = s; - dat.incmd = incmd; - dat.lst = lst; - runhookdef(COMPCTLMAKEHOOK, (void *) &dat); - } + callcompfunc(s, compfunc); endcmgroup(NULL); - runhookdef(COMPCTLAFTERHOOK, - (void *) ((amatches && !oldlist) ? 1L : 0L)); + /* Needed for compcall. */ + runhookdef(COMPCTLCLEANUPHOOK, NULL); if (oldlist) { nmatches = onm; @@ -884,16 +854,14 @@ makecomplist(char *s, int incmd, int lst) return 0; } - PERMALLOC { - if (lastmatches) { - freematches(lastmatches); - lastmatches = NULL; - } - permmatches(1); - amatches = pmatches; - lastpermmnum = permmnum; - lastpermgnum = permgnum; - } LASTALLOC; + if (lastmatches) { + freematches(lastmatches); + lastmatches = NULL; + } + permmatches(1); + amatches = pmatches; + lastpermmnum = permmnum; + lastpermgnum = permgnum; lastmatches = pmatches; lastlmatches = lmatches; @@ -908,20 +876,69 @@ makecomplist(char *s, int incmd, int lst) return 0; } - if (!m || !(m = m->next)) - break; + redup(osi, 0); + return 1; + } else { + struct ccmakedat dat; + + dat.str = s; + dat.incmd = incmd; + dat.lst = lst; + runhookdef(COMPCTLMAKEHOOK, (void *) &dat); + + /* Needed for compcall. */ + runhookdef(COMPCTLCLEANUPHOOK, NULL); + + return dat.lst; + } +} + +/**/ +mod_export char * +multiquote(char *s, int ign) +{ + if (s) { + char *os = s, *p = compqstack; + + if (p && *p && (ign < 1 || p[ign])) { + if (ign > 0) + p += ign; + while (*p) { + if (ign >= 0 || p[1]) + s = bslashquote(s, NULL, + (*p == '\'' ? 1 : (*p == '"' ? 2 : 0))); + p++; + } + } + return (s == os ? dupstring(s) : s); + } + DPUTS(1, "BUG: null pointer in multiquote()"); + return NULL; +} - errflag = 0; - compmatcher++; +/**/ +mod_export char * +tildequote(char *s, int ign) +{ + if (s) { + int tilde; + + if ((tilde = (*s == '~'))) + *s = 'x'; + s = multiquote(s, ign); + if (tilde) + *s = '~'; + + return s; } - redup(osi, 0); - return 1; + DPUTS(1, "BUG: null pointer in tildequote()"); + return NULL; } /* Check if we have to complete a parameter name. */ /**/ -char * +mod_export char * check_param(char *s, int set, int test) { char *p; @@ -1046,7 +1063,7 @@ check_param(char *s, int set, int test) /* Copy the given string and remove backslashes from the copy and return it. */ /**/ -char * +mod_export char * rembslash(char *s) { char *t = s = dupstring(s); @@ -1065,7 +1082,7 @@ rembslash(char *s) /* This should probably be moved into tokenize(). */ /**/ -char * +mod_export char * ctokenize(char *p) { char *r = p; @@ -1091,7 +1108,7 @@ ctokenize(char *p) } /**/ -char * +mod_export char * comp_str(int *ipl, int *pl, int untok) { char *p = dupstring(compprefix); @@ -1122,23 +1139,21 @@ comp_str(int *ipl, int *pl, int untok) return str; } -/* This is for compset -q. */ - /**/ int set_comp_sep(void) { int lip, lp; - char *s = comp_str(&lip, &lp, 0); + char *s = comp_str(&lip, &lp, 1); LinkList foo = newlinklist(); LinkNode n; int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs; - int tl, got = 0, i = 0, cur = -1, oll = ll, sl; - int ois = instring, oib = inbackt, noffs = lip + lp; - char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs; + int tl, got = 0, i = 0, cur = -1, oll = ll, sl, remq; + int ois = instring, oib = inbackt, noffs = lp; + char *tmp, *p, *ns, *ol = (char *) line, sav, *qp, *qs, *ts, qc = '\0'; - if (compisuffix) - s = dyncat(s, compisuffix); + s += lip; + wb += lip; untokenize(s); swb = swe = soffs = 0; @@ -1155,7 +1170,8 @@ set_comp_sep(void) memcpy(tmp + 1, s, noffs); tmp[(scs = cs = 1 + noffs)] = 'x'; strcpy(tmp + 2 + noffs, s + noffs); - tmp = rembslash(tmp); + if ((remq = (*compqstack == '\\'))) + tmp = rembslash(tmp); inpush(dupstrspace(tmp), 0, NULL); line = (unsigned char *) tmp; ll = tl - 1; @@ -1218,21 +1234,31 @@ set_comp_sep(void) *p = '\''; } offs = owb; + + untokenize(ts = dupstring(ns)); + if (*ns == Snull || *ns == Dnull) { instring = (*ns == Snull ? 1 : 2); inbackt = 0; swb++; if (ns[strlen(ns) - 1] == *ns && ns[1]) swe--; - autoq = (*ns == Snull ? '\'' : '"'); + zsfree(autoq); + autoq = ztrdup(compqstack[1] ? "" : + multiquote(*ns == Snull ? "'" : "\"", 1)); + qc = (*ns == Snull ? '\'' : '"'); + ts++; } else { instring = 0; - autoq = '\0'; + zsfree(autoq); + autoq = NULL; } for (p = ns, i = swb; *p; p++, i++) { if (INULL(*p)) { - if (i < scs) - soffs--; + if (i < scs) { + if (remq && *p == Bnull && p[1]) + swb -= 2; + } if (p[1] || *p != Bnull) { if (*p == Bnull) { if (scs == i + 1) @@ -1248,17 +1274,28 @@ set_comp_sep(void) chuck(p--); } } + ns = ts; + + if (instring && strchr(compqstack, '\\')) { + int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1])); + + if (ql > rl) + swb -= ql - rl; + } sav = s[(i = swb - 1)]; s[i] = '\0'; - qp = tricat(qipre, rembslash(s), ""); + qp = rembslash(s); s[i] = sav; if (swe < swb) swe = swb; swe--; sl = strlen(s); - if (swe > sl) - swe = sl, ns[swe - swb + 1] = '\0'; - qs = tricat(rembslash(s + swe), qisuf, ""); + if (swe > sl) { + swe = sl; + if (strlen(ns) > swe - swb + 1) + ns[swe - swb + 1] = '\0'; + } + qs = rembslash(s + swe); sl = strlen(ns); if (soffs > sl) soffs = sl; @@ -1266,6 +1303,11 @@ set_comp_sep(void) { int set = CP_QUOTE | CP_QUOTING, unset = 0; + p = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"), + compqstack, ""); + zsfree(compqstack); + compqstack = p; + zsfree(compquote); zsfree(compquoting); if (instring == 2) { @@ -1283,11 +1325,11 @@ set_comp_sep(void) compquoting = ztrdup(compquoting); comp_setunset(0, 0, set, unset); + zsfree(compprefix); + zsfree(compsuffix); if (unset(COMPLETEINWORD)) { untokenize(ns); - zsfree(compprefix); compprefix = ztrdup(ns); - zsfree(compsuffix); compsuffix = ztrdup(""); } else { char *ss, sav; @@ -1302,21 +1344,16 @@ set_comp_sep(void) untokenize(ss); compsuffix = ztrdup(ss); } + tmp = tricat(compqiprefix, compiprefix, multiquote(qp, 1)); + zsfree(compqiprefix); + compqiprefix = tmp; + tmp = tricat(multiquote(qs, 1), compisuffix, compqisuffix); + zsfree(compqisuffix); + compqisuffix = tmp; zsfree(compiprefix); compiprefix = ztrdup(""); zsfree(compisuffix); compisuffix = ztrdup(""); - zsfree(compqiprefix); - zsfree(compqisuffix); - if (ois) { - compqiprefix = qp; - compqisuffix = qs; - } else { - compqiprefix = ztrdup(quotename(qp, NULL)); - zsfree(qp); - compqisuffix = ztrdup(quotename(qs, NULL)); - zsfree(qs); - } freearray(compwords); i = countlinknodes(foo); compwords = (char **) zalloc((i + 1) * sizeof(char *)); @@ -1327,7 +1364,6 @@ set_comp_sep(void) compcurrent = cur + 1; compwords[i] = NULL; } - autoq = oaq; instring = ois; inbackt = oib; @@ -1337,7 +1373,7 @@ set_comp_sep(void) /* This stores the strings from the list in an array. */ /**/ -void +mod_export void set_list_array(char *name, LinkList l) { char **a, **p; @@ -1354,7 +1390,7 @@ set_list_array(char *name, LinkList l) /* Get the words from a variable or a (list of words). */ /**/ -char ** +mod_export char ** get_user_var(char *nam) { if (!nam) @@ -1423,19 +1459,37 @@ int addmatches(Cadata dat, char **argv) { char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL; - char **aign = NULL, **dparr = NULL, oaq = autoq, *oppre = dat->ppre; - char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL; + char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre; + char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL; int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0; + int ppl = 0, psl = 0; int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern; - int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt; + int isexact, doadd, ois = instring, oib = inbackt; Cline lc = NULL, pline = NULL, sline = NULL; Cmatch cm; struct cmlist mst; Cmlist oms = mstack; - Patprog cp = NULL; + Patprog cp = NULL, *pign = NULL; LinkList aparl = NULL, oparl = NULL, dparl = NULL; Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl; + if (!*argv) { + SWITCHHEAPS(compheap) { + /* Select the group in which to store the matches. */ + gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) | + ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) | + ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0)); + if (dat->group) { + endcmgroup(NULL); + begcmgroup(dat->group, gflags); + } else { + endcmgroup(NULL); + begcmgroup("default", 0); + } + } SWITCHBACKHEAPS; + + return 1; + } for (bp = brbeg; bp; bp = bp->next) bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos); for (bp = brend; bp; bp = bp->next) @@ -1447,300 +1501,373 @@ addmatches(Cadata dat, char **argv) if (qc == '`') { instring = 0; inbackt = 0; - autoq = '\0'; + autoq = ""; } else { + char buf[2]; + instring = (qc == '\'' ? 1 : 2); inbackt = 0; - autoq = qc; + buf[0] = qc; + buf[1] = '\0'; + autoq = multiquote(buf, 1); } } else { instring = inbackt = 0; - autoq = '\0'; + autoq = NULL; } qipre = ztrdup(compqiprefix ? compqiprefix : ""); qisuf = ztrdup(compqisuffix ? compqisuffix : ""); + useexact = (compexact && !strcmp(compexact, "accept")); + /* Switch back to the heap that was used when the completion widget * was invoked. */ SWITCHHEAPS(compheap) { - HEAPALLOC { - if ((doadd = (!dat->apar && !dat->opar && !dat->dpar)) && - (dat->aflags & CAF_MATCH)) + if ((doadd = (!dat->apar && !dat->opar && !dat->dpar))) { + if (dat->aflags & CAF_MATCH) hasmatched = 1; - if (dat->apar) - aparl = newlinklist(); - if (dat->opar) - oparl = newlinklist(); - if (dat->dpar) { - if (*(dat->dpar) == '(') - dparr = NULL; - else if ((dparr = get_user_var(dat->dpar)) && !*dparr) - dparr = NULL; - dparl = newlinklist(); - } - if (dat->exp) { - curexpl = (Cexpl) zhalloc(sizeof(struct cexpl)); - curexpl->count = curexpl->fcount = 0; - curexpl->str = dupstring(dat->exp); - } else - curexpl = NULL; + else + hasunmatched = 1; + } + if (dat->apar) + aparl = newlinklist(); + if (dat->opar) + oparl = newlinklist(); + if (dat->dpar) { + if (*(dat->dpar) == '(') + dparr = NULL; + else if ((dparr = get_user_var(dat->dpar)) && !*dparr) + dparr = NULL; + dparl = newlinklist(); + } + if (dat->exp) { + curexpl = (Cexpl) zhalloc(sizeof(struct cexpl)); + curexpl->count = curexpl->fcount = 0; + curexpl->str = dupstring(dat->exp); + } else + curexpl = NULL; - /* Store the matcher in our stack of matchers. */ - if (dat->match) { - mst.next = mstack; - mst.matcher = dat->match; - mstack = &mst; + /* Store the matcher in our stack of matchers. */ + if (dat->match) { + mst.next = mstack; + mst.matcher = dat->match; + mstack = &mst; - if (!mnum) - add_bmatchers(dat->match); + add_bmatchers(dat->match); - addlinknode(matchers, dat->match); - dat->match->refc++; + addlinknode(matchers, dat->match); + dat->match->refc++; + } + if (mnum && (mstack || bmatchers)) + update_bmatchers(); + + /* Get the suffixes to ignore. */ + if (dat->ign && (aign = get_user_var(dat->ign))) { + char **ap, **sp, *tmp; + Patprog *pp, prog; + + pign = (Patprog *) zhalloc((arrlen(aign) + 1) * sizeof(Patprog)); + + for (ap = sp = aign, pp = pign; (tmp = *ap); ap++) { + tokenize(tmp); + remnulargs(tmp); + if (((tmp[0] == Quest && tmp[1] == Star) || + (tmp[1] == Quest && tmp[0] == Star)) && + tmp[2] && !haswilds(tmp + 2)) + untokenize(*sp++ = tmp + 2); + else if ((prog = patcompile(tmp, 0, NULL))) + *pp++ = prog; } - if (mnum && (mstack || bmatchers)) - update_bmatchers(); - - /* Get the suffixes to ignore. */ - if (dat->ign) - aign = get_user_var(dat->ign); - /* Get the display strings. */ - if (dat->disp) - if ((disp = get_user_var(dat->disp))) - disp--; - /* Get the contents of the completion variables if we have - * to perform matching. */ - if (dat->aflags & CAF_MATCH) { - lipre = dupstring(compiprefix); - lisuf = dupstring(compisuffix); - lpre = dupstring(compprefix); - lsuf = dupstring(compsuffix); - llpl = strlen(lpre); - llsl = strlen(lsuf); - /* Test if there is an existing -P prefix. */ - if (dat->pre && *dat->pre) { - char *dp = rembslash(dat->pre); - - pl = pfxlen(dp, lpre); - llpl -= pl; - lpre += pl; + *sp = NULL; + *pp = NULL; + if (!*aign) + aign = NULL; + if (!*pign) + pign = NULL; + } + /* Get the display strings. */ + if (dat->disp) + if ((disp = get_user_var(dat->disp))) + disp--; + /* Get the contents of the completion variables if we have + * to perform matching. */ + if (dat->aflags & CAF_MATCH) { + lipre = dupstring(compiprefix); + lisuf = dupstring(compisuffix); + lpre = dupstring(compprefix); + lsuf = dupstring(compsuffix); + llpl = strlen(lpre); + llsl = strlen(lsuf); + /* Test if there is an existing -P prefix. */ + if (dat->pre && *dat->pre) { + pl = pfxlen(dat->pre, lpre); + llpl -= pl; + lpre += pl; + } + } + /* Now duplicate the strings we have from the command line. */ + if (dat->ipre) + dat->ipre = (lipre ? dyncat(lipre, dat->ipre) : + dupstring(dat->ipre)); + else if (lipre) + dat->ipre = lipre; + if (dat->isuf) + dat->isuf = (lisuf ? dyncat(lisuf, dat->isuf) : + dupstring(dat->isuf)); + else if (lisuf) + dat->isuf = lisuf; + if (dat->ppre) { + dat->ppre = ((dat->flags & CMF_FILE) ? + tildequote(dat->ppre, !!(dat->aflags & CAF_QUOTE)) : + multiquote(dat->ppre, !!(dat->aflags & CAF_QUOTE))); + lpl = strlen(dat->ppre); + } else + lpl = 0; + if (dat->psuf) { + dat->psuf = multiquote(dat->psuf, !!(dat->aflags & CAF_QUOTE)); + lsl = strlen(dat->psuf); + } else + lsl = 0; + if (dat->aflags & CAF_MATCH) { + int ml, gfl = 0; + char *globflag = NULL; + + if (comppatmatch && *comppatmatch && + dat->ppre && lpre[0] == '(' && lpre[1] == '#') { + char *p; + + for (p = lpre + 2; *p && *p != ')'; p++); + + if (*p == ')') { + char sav = p[1]; + + p[1] = '\0'; + globflag = dupstring(lpre); + gfl = p - lpre + 1; + p[1] = sav; + + lpre = p + 1; + llpl -= gfl; } } - /* Now duplicate the strings we have from the command line. */ - if (dat->ipre) - dat->ipre = (lipre ? dyncat(lipre, dat->ipre) : - dupstring(dat->ipre)); - else if (lipre) - dat->ipre = lipre; - if (dat->isuf) - dat->isuf = (lisuf ? dyncat(lisuf, dat->isuf) : - dupstring(dat->isuf)); - else if (lisuf) - dat->isuf = lisuf; - if (dat->ppre) { - if (!(dat->aflags & CAF_QUOTE)) { - dat->ppre = quotename(dat->ppre, NULL); - if ((dat->flags & CMF_FILE) && - dat->ppre[0] == '\\' && dat->ppre[1] == '~') - chuck(dat->ppre); - } else - dat->ppre = dupstring(dat->ppre); - lpl = strlen(dat->ppre); - } else - lpl = 0; - if (dat->psuf) { - if (!(dat->aflags & CAF_QUOTE)) - dat->psuf = quotename(dat->psuf, NULL); - else - dat->psuf = dupstring(dat->psuf); - lsl = strlen(dat->psuf); - } else - lsl = 0; - if (dat->aflags & CAF_MATCH) { - int ml; - - s = dat->ppre ? dat->ppre : ""; - if ((ml = match_str(lpre, s, &bpl, 0, NULL, 0, 0, 1)) >= 0) { - if (matchsubs) { - Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0); - - tmp->prefix = matchsubs; - if (matchlastpart) - matchlastpart->next = tmp; - else - matchparts = tmp; - } - pline = matchparts; - lpre += ml; - bcp = ml; - bpadd = strlen(s) - ml; - } else { - if (llpl <= lpl && strpfx(lpre, s)) - lpre = ""; - else if (llpl > lpl && strpfx(s, lpre)) - lpre += lpl; + s = dat->ppre ? dat->ppre : ""; + if ((ml = match_str(lpre, s, &bpl, 0, NULL, 0, 0, 1)) >= 0) { + if (matchsubs) { + Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0); + + tmp->prefix = matchsubs; + if (matchlastpart) + matchlastpart->next = tmp; else - *argv = NULL; - bcp = lpl; + matchparts = tmp; } - - s = dat->psuf ? dat->psuf : ""; - if ((ml = match_str(lsuf, s, &bsl, 0, NULL, 1, 0, 1)) >= 0) { - if (matchsubs) { - Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF); - - tmp->suffix = matchsubs; - if (matchlastpart) - matchlastpart->next = tmp; - else - matchparts = tmp; - } - sline = revert_cline(matchparts); - lsuf[llsl - ml] = '\0'; - bcs = ml; - bsadd = strlen(s) - ml; - } else { - if (llsl <= lsl && strsfx(lsuf, s)) - lsuf = ""; - else if (llsl > lsl && strsfx(s, lsuf)) - lsuf[llsl - lsl] = '\0'; + pline = matchparts; + lpre += ml; + llpl -= ml; + bcp = ml; + bpadd = strlen(s) - ml; + } else { + if (llpl <= lpl && strpfx(lpre, s)) + lpre = ""; + else if (llpl > lpl && strpfx(s, lpre)) + lpre += lpl; + else + *argv = NULL; + bcp = lpl; + } + s = dat->psuf ? dat->psuf : ""; + if ((ml = match_str(lsuf, s, &bsl, 0, NULL, 1, 0, 1)) >= 0) { + if (matchsubs) { + Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF); + + tmp->suffix = matchsubs; + if (matchlastpart) + matchlastpart->next = tmp; else - *argv = NULL; - bcs = lsl; + matchparts = tmp; } - if (comppatmatch && *comppatmatch) { - int is = (*comppatmatch == '*'); - char *tmp = (char *) zhalloc(2 + llpl + llsl); + sline = revert_cline(matchparts); + lsuf[llsl - ml] = '\0'; + llsl -= ml; + bcs = ml; + bsadd = strlen(s) - ml; + } else { + if (llsl <= lsl && strsfx(lsuf, s)) + lsuf = ""; + else if (llsl > lsl && strsfx(s, lsuf)) + lsuf[llsl - lsl] = '\0'; + else + *argv = NULL; + bcs = lsl; + } + if (comppatmatch && *comppatmatch) { + int is = (*comppatmatch == '*'); + char *tmp = (char *) zhalloc(2 + llpl + llsl + gfl); + if (gfl) { + strcpy(tmp, globflag); + strcat(tmp, lpre); + } else strcpy(tmp, lpre); - tmp[llpl] = 'x'; - strcpy(tmp + llpl + is, lsuf); - - tokenize(tmp); - remnulargs(tmp); - if (haswilds(tmp)) { - if (is) - tmp[llpl] = Star; - if ((cp = patcompile(tmp, 0, NULL))) - haspattern = 1; - } + tmp[llpl + gfl] = 'x'; + strcpy(tmp + llpl + gfl + is, lsuf); + + tokenize(tmp); + remnulargs(tmp); + if (haswilds(tmp)) { + if (is) + tmp[llpl + gfl] = Star; + if ((cp = patcompile(tmp, 0, NULL))) + haspattern = 1; } } - if (*argv) { - if (dat->pre) - dat->pre = dupstring(dat->pre); - if (dat->suf) - dat->suf = dupstring(dat->suf); - if (!dat->prpre && (dat->prpre = oppre)) { - singsub(&(dat->prpre)); - untokenize(dat->prpre); - } else - dat->prpre = dupstring(dat->prpre); - /* Select the group in which to store the matches. */ - gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) | - ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) | - ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0)); - if (dat->group) { - endcmgroup(NULL); - begcmgroup(dat->group, gflags); - } else { - endcmgroup(NULL); - begcmgroup("default", 0); - } - /* Select the set of matches. */ - oisalt = (dat->aflags & CAF_ALT); - - if (dat->remf) { - dat->remf = dupstring(dat->remf); - dat->rems = NULL; - } else if (dat->rems) - dat->rems = dupstring(dat->rems); + } + /* Select the group in which to store the matches. */ + gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) | + ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) | + ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0)); + if (dat->group) { + endcmgroup(NULL); + begcmgroup(dat->group, gflags); + } else { + endcmgroup(NULL); + begcmgroup("default", 0); + } + if (*argv) { + if (dat->pre) + dat->pre = dupstring(dat->pre); + if (dat->suf) + dat->suf = dupstring(dat->suf); + if (!dat->prpre && (dat->prpre = oppre)) { + singsub(&(dat->prpre)); + untokenize(dat->prpre); + } else + dat->prpre = dupstring(dat->prpre); + /* Select the set of matches. */ + + if (dat->remf) { + dat->remf = dupstring(dat->remf); + dat->rems = NULL; + } else if (dat->rems) + dat->rems = dupstring(dat->rems); + + if (lpre) + lpre = ((!(dat->aflags & CAF_QUOTE) && + (!dat->ppre && (dat->flags & CMF_FILE))) ? + tildequote(lpre, 1) : multiquote(lpre, 1)); + if (lsuf) + lsuf = multiquote(lsuf, 1); + } + /* Walk through the matches given. */ + obpl = bpl; + obsl = bsl; + if (aign || pign) { + int max = 0; + char **ap = argv; + + ppl = (dat->ppre ? strlen(dat->ppre) : 0); + while ((s = *ap++)) + if ((sl = strlen(s)) > max) + max = sl; + psl = (dat->psuf ? strlen(dat->psuf) : 0); + ibuf = (char *) zhalloc(1 + ppl + max + psl); + } + for (; (s = *argv); argv++) { + bpl = obpl; + bsl = obsl; + if (disp) { + if (!*++disp) + disp = NULL; } - /* Walk through the matches given. */ - obpl = bpl; - obsl = bsl; - for (; (s = *argv); argv++) { - bpl = obpl; - bsl = obsl; - if (disp) { - if (!*++disp) - disp = NULL; - } - sl = strlen(s); - isalt = oisalt; - if ((!dat->psuf || !*(dat->psuf)) && aign) { + sl = strlen(s); + if (aign || pign) { + int il = ppl + sl + psl, addit = 1; + + if (ppl) + memcpy(ibuf, dat->ppre, ppl); + strcpy(ibuf + ppl, s); + if (psl) + strcpy(ibuf + ppl + sl, dat->psuf); + + if (aign) { /* Do the suffix-test. If the match has one of the - * suffixes from ign, we put it in the alternate set. */ + * suffixes from aign, we put it in the alternate set. */ char **pt = aign; int filell; - for (isalt = 0; !isalt && *pt; pt++) - if ((filell = strlen(*pt)) < sl - && !strcmp(*pt, s + sl - filell)) - isalt = 1; + for (; addit && *pt; pt++) + addit = !((filell = strlen(*pt)) < il && + !strcmp(*pt, ibuf + il - filell)); + } + if (addit && pign) { + Patprog *pt = pign; - if (isalt && !doadd) { - if (dparr && !*++dparr) - dparr = NULL; - continue; - } + for (; addit && *pt; pt++) + addit = !pattry(*pt, ibuf); } - if (!(dat->aflags & CAF_MATCH)) { - if (dat->aflags & CAF_QUOTE) - ms = dupstring(s); - else - sl = strlen(ms = quotename(s, NULL)); - lc = bld_parts(ms, sl, -1, NULL); - isexact = 0; - } else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc, - (!(dat->aflags & CAF_QUOTE) ? - ((dat->ppre && dat->ppre) || - !(dat->flags & CMF_FILE) ? 1 : 2) : 0), - &bpl, bcp, &bsl, bcs, - &isexact))) { + if (!addit) { + compignored++; if (dparr && !*++dparr) dparr = NULL; continue; } - if (doadd) { - Brinfo bp; - - for (bp = obpl; bp; bp = bp->next) - bp->curpos += bpadd; - for (bp = obsl; bp; bp = bp->next) - bp->curpos += bsadd; - - if ((cm = add_match_data(isalt, ms, lc, dat->ipre, NULL, - dat->isuf, dat->pre, dat->prpre, - dat->ppre, pline, - dat->psuf, sline, - dat->suf, dat->flags, isexact))) { - cm->rems = dat->rems; - cm->remf = dat->remf; - if (disp) - cm->disp = dupstring(*disp); - } - } else { - if (dat->apar) - addlinknode(aparl, ms); - if (dat->opar) - addlinknode(oparl, s); - if (dat->dpar && dparr) { - addlinknode(dparl, *dparr); - if (!*++dparr) - dparr = NULL; - } - free_cline(lc); + } + if (!(dat->aflags & CAF_MATCH)) { + if (dat->aflags & CAF_QUOTE) + ms = dupstring(s); + else + sl = strlen(ms = multiquote(s, 0)); + lc = bld_parts(ms, sl, -1, NULL); + isexact = 0; + } else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc, + (!(dat->aflags & CAF_QUOTE) ? + (dat->ppre || + !(dat->flags & CMF_FILE) ? 1 : 2) : 0), + &bpl, bcp, &bsl, bcs, + &isexact))) { + if (dparr && !*++dparr) + dparr = NULL; + continue; + } + if (doadd) { + Brinfo bp; + + for (bp = obpl; bp; bp = bp->next) + bp->curpos += bpadd; + for (bp = obsl; bp; bp = bp->next) + bp->curpos += bsadd; + + if ((cm = add_match_data(0, ms, lc, dat->ipre, NULL, + dat->isuf, dat->pre, dat->prpre, + dat->ppre, pline, + dat->psuf, sline, + dat->suf, dat->flags, isexact))) { + cm->rems = dat->rems; + cm->remf = dat->remf; + if (disp) + cm->disp = dupstring(*disp); } + } else { + if (dat->apar) + addlinknode(aparl, ms); + if (dat->opar) + addlinknode(oparl, s); + if (dat->dpar && dparr) { + addlinknode(dparl, *dparr); + if (!*++dparr) + dparr = NULL; + } + free_cline(lc); } - if (dat->apar) - set_list_array(dat->apar, aparl); - if (dat->opar) - set_list_array(dat->opar, oparl); - if (dat->dpar) - set_list_array(dat->dpar, dparl); - if (dat->exp) - addexpl(); - } LASTALLOC; + } + if (dat->apar) + set_list_array(dat->apar, aparl); + if (dat->opar) + set_list_array(dat->opar, oparl); + if (dat->dpar) + set_list_array(dat->dpar, dparl); + if (dat->exp) + addexpl(); } SWITCHBACKHEAPS; /* We switched back to the current heap, now restore the stack of @@ -1764,7 +1891,7 @@ addmatches(Cadata dat, char **argv) /* This adds all the data we have for a match. */ /**/ -Cmatch +mod_export Cmatch add_match_data(int alt, char *str, Cline line, char *ipre, char *ripre, char *isuf, char *pre, char *prpre, @@ -1797,21 +1924,12 @@ add_match_data(int alt, char *str, Cline line, salen += (qisl = strlen(qisuf)); if (salen) { - char *asuf = (char *) zhalloc(salen); Cline pp, p, s, sl = NULL; - - - if (psl) - memcpy(asuf, psuf, psl); - if (isl) - memcpy(asuf + psl, isuf, isl); - if (qisl) - memcpy(asuf + psl + isl, qisuf, qisl); for (pp = NULL, p = line; p->next; pp = p, p = p->next); - if (salen > qisl) { - s = bld_parts(asuf, salen - qisl, salen - qisl, &sl); + if (psl) { + s = bld_parts(psuf, psl, psl, &sl); if (sline) { Cline sp; @@ -1821,6 +1939,7 @@ add_match_data(int alt, char *str, Cline line, for (sp = sline; sp->next; sp = sp->next); sp->next = s; s = sline; + sline = NULL; } if (!(p->flags & (CLF_SUF | CLF_MID)) && !p->llen && !p->wlen && !p->olen) { @@ -1832,7 +1951,7 @@ add_match_data(int alt, char *str, Cline line, s->prefix = p->prefix; p->prefix = NULL; } - s->flags |= (p->flags & CLF_MATCHED); + s->flags |= (p->flags & CLF_MATCHED) | CLF_MID; free_cline(p); if (pp) pp->next = s; @@ -1841,8 +1960,29 @@ add_match_data(int alt, char *str, Cline line, } else p->next = s; } + if (isl) { + Cline tsl; + + s = bld_parts(isuf, isl, isl, &tsl); + + if (sl) + sl->next = s; + else if (sline) { + Cline sp; + + sline = cp_cline(sline, 1); + + for (sp = sline; sp->next; sp = sp->next); + sp->next = s; + p->next = sline; + sline = NULL; + } else + p->next = s; + + sl = tsl; + } if (qisl) { - Cline qsl = bld_parts(asuf + psl + isl, qisl, qisl, NULL); + Cline qsl = bld_parts(qisuf, qisl, qisl, NULL); qsl->flags |= CLF_SUF; qsl->suffix = qsl->prefix; @@ -1888,7 +2028,7 @@ add_match_data(int alt, char *str, Cline line, p = bld_parts(ppre, ppl, ppl, &lp); if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) && - !p->llen && !p->wlen && !p->olen) { + !lp->llen && !lp->wlen && !lp->olen) { Cline lpp; for (lpp = lp->prefix; lpp->next; lpp = lpp->next); @@ -1952,8 +2092,8 @@ add_match_data(int alt, char *str, Cline line, } else for (p = lp = cp_cline(pline, 1); lp->next; lp = lp->next); - if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) && - !p->llen && !p->wlen && !p->olen) { + if (lp->prefix && !(line->flags & CLF_SUF) && + !lp->llen && !lp->wlen && !lp->olen) { Cline lpp; for (lpp = lp->prefix; lpp->next; lpp = lpp->next); @@ -1994,7 +2134,14 @@ add_match_data(int alt, char *str, Cline line, cm->isuf = (isuf && *isuf ? isuf : NULL); cm->pre = pre; cm->suf = suf; - cm->flags = flags; + cm->flags = (flags | + (complist ? + ((strstr(complist, "packed") ? CMF_PACKED : 0) | + (strstr(complist, "rows") ? CMF_ROWS : 0)) : 0)); + + if ((*compqstack == '\\' && compqstack[1]) || + (autoq && *compqstack && compqstack[1] == '\\')) + cm->flags |= CMF_NOSPACE; if (nbrbeg) { int *p; Brinfo bp; @@ -2017,7 +2164,7 @@ add_match_data(int alt, char *str, Cline line, cm->brsl = NULL; cm->qipl = qipl; cm->qisl = qisl; - cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0')); + cm->autoq = dupstring(autoq ? autoq : (inbackt ? "`" : NULL)); cm->rems = cm->remf = cm->disp = NULL; if ((lastprebr || lastpostbr) && !hasbrpsfx(cm, lastprebr, lastpostbr)) @@ -2032,7 +2179,12 @@ add_match_data(int alt, char *str, Cline line, addlinknode((alt ? fmatches : matches), cm); newmatches = 1; + mgroup->new = 1; + if (alt) + compignored++; + if (!complastprompt || !*complastprompt) + dolastprompt = 0; /* One more match for this explanation. */ if (curexpl) { if (alt) @@ -2056,8 +2208,8 @@ add_match_data(int alt, char *str, Cline line, /* Do we have an exact match? More than one? */ if (exact) { if (!ai->exact) { - ai->exact = 1; - if (incompfunc) { + ai->exact = useexact; + if (incompfunc && (!compexactstr || !*compexactstr)) { /* If a completion widget is active, we make the exact * string available in `compstate'. */ @@ -2076,7 +2228,7 @@ add_match_data(int alt, char *str, Cline line, comp_setunset(0, 0, CP_EXACTSTR, 0); } ai->exactm = cm; - } else { + } else if (useexact) { ai->exact = 2; ai->exactm = NULL; if (incompfunc) @@ -2089,7 +2241,7 @@ add_match_data(int alt, char *str, Cline line, /* This begins a new group of matches. */ /**/ -void +mod_export void begcmgroup(char *n, int flags) { if (n) { @@ -2118,6 +2270,8 @@ begcmgroup(char *n, int flags) mgroup->matches = NULL; mgroup->ylist = NULL; mgroup->expls = NULL; + mgroup->perm = NULL; + mgroup->new = 0; mgroup->lexpls = expls = newlinklist(); mgroup->lmatches = matches = newlinklist(); @@ -2132,7 +2286,7 @@ begcmgroup(char *n, int flags) /* End the current group for now. */ /**/ -void +mod_export void endcmgroup(char **ylist) { mgroup->ylist = ylist; @@ -2141,7 +2295,7 @@ endcmgroup(char **ylist) /* Add an explanation string to the current group, joining duplicates. */ /**/ -void +mod_export void addexpl(void) { LinkNode n; @@ -2221,7 +2375,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) int n, nl = 0, ll = 0; /* Build an array for the matches. */ - rp = ap = (Cmatch *) ncalloc(((n = countlinknodes(l)) + 1) * + rp = ap = (Cmatch *) hcalloc(((n = countlinknodes(l)) + 1) * sizeof(Cmatch)); /* And copy them into it. */ @@ -2252,22 +2406,28 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) (int (*) _((const void *, const void *)))matchcmp); if (!(flags & CGF_UNIQCON)) { + int dup; + /* And delete the ones that occur more than once. */ for (ap = cp = rp; *ap; ap++) { *cp++ = *ap; for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--); ap = bp; /* Mark those, that would show the same string in the list. */ - for (; bp[1] && !(*ap)->disp && !(bp[1])->disp && - !strcmp((*ap)->str, (bp[1])->str); bp++) - (bp[1])->flags |= CMF_NOLIST; + for (dup = 0; bp[1] && !(*ap)->disp && !(bp[1])->disp && + !strcmp((*ap)->str, (bp[1])->str); bp++) { + (bp[1])->flags |= CMF_MULT; + dup = 1; + } + if (dup) + (*ap)->flags |= CMF_FMULT; } *cp = NULL; } for (ap = rp; *ap; ap++) { if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE)) ll++; - if ((*ap)->flags & CMF_NOLIST) + if ((*ap)->flags & (CMF_NOLIST | CMF_MULT)) nl++; } } else { @@ -2282,20 +2442,26 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) *cp = NULL; } } else if (!(flags & CGF_UNIQCON)) { + int dup; + for (ap = cp = rp; *ap; ap++) { *cp++ = *ap; for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--); ap = bp; - for (; bp[1] && !(*ap)->disp && !(bp[1])->disp && - !strcmp((*ap)->str, (bp[1])->str); bp++) - (bp[1])->flags |= CMF_NOLIST; + for (dup = 0; bp[1] && !(*ap)->disp && !(bp[1])->disp && + !strcmp((*ap)->str, (bp[1])->str); bp++) { + (bp[1])->flags |= CMF_MULT; + dup = 1; + } + if (dup) + (*ap)->flags |= CMF_FMULT; } *cp = NULL; } for (ap = rp; *ap; ap++) { if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE)) ll++; - if ((*ap)->flags & CMF_NOLIST) + if ((*ap)->flags & (CMF_NOLIST | CMF_MULT)) nl++; } } @@ -2317,7 +2483,7 @@ dupmatch(Cmatch m, int nbeg, int nend) { Cmatch r; - r = (Cmatch) ncalloc(sizeof(struct cmatch)); + r = (Cmatch) zcalloc(sizeof(struct cmatch)); r->str = ztrdup(m->str); r->ipre = ztrdup(m->ipre); @@ -2349,10 +2515,10 @@ dupmatch(Cmatch m, int nbeg, int nend) r->brsl = NULL; r->rems = ztrdup(m->rems); r->remf = ztrdup(m->remf); - r->autoq = m->autoq; + r->autoq = ztrdup(m->autoq); r->qipl = m->qipl; r->qisl = m->qisl; - r->disp = dupstring(m->disp); + r->disp = ztrdup(m->disp); return r; } @@ -2360,24 +2526,24 @@ dupmatch(Cmatch m, int nbeg, int nend) /* This duplicates all groups of matches. */ /**/ -int +mod_export int permmatches(int last) { - Cmgroup g = amatches, n; + Cmgroup g = amatches, n, opm; Cmatch *p, *q; Cexpl *ep, *eq, e, o; LinkList mlist; static int fi = 0; - int nn, nl, ll, gn = 1, mn = 1, rn; + int nn, nl, ll, gn = 1, mn = 1, rn, ofi = fi; - if (pmatches && !newmatches) + if (pmatches && !newmatches) { + if (last && fi) + ainfo = fainfo; return fi; - + } newmatches = fi = 0; - if (pmatches) - freematches(pmatches); - + opm = pmatches; pmatches = lmatches = NULL; nmatches = smatches = 0; @@ -2387,8 +2553,8 @@ permmatches(int last) fi = 1; } while (g) { - HEAPALLOC { - if (empty(g->lmatches)) + if (fi != ofi || !g->perm || g->new) { + if (fi) /* We have no matches, try ignoring fignore. */ mlist = g->lfmatches; else @@ -2407,51 +2573,67 @@ permmatches(int last) NULL, NULL); g->ccount = 0; - } LASTALLOC; - - nmatches += g->mcount; - smatches += g->lcount; - - n = (Cmgroup) ncalloc(sizeof(struct cmgroup)); - - if (!lmatches) - lmatches = n; - if (pmatches) - pmatches->prev = n; - n->next = pmatches; - pmatches = n; - n->prev = 0; - n->num = gn++; - - n->flags = g->flags; - n->mcount = g->mcount; - n->matches = p = (Cmatch *) ncalloc((n->mcount + 1) * - sizeof(Cmatch)); - for (q = g->matches; *q; q++, p++) - *p = dupmatch(*q, nbrbeg, nbrend); - *p = NULL; - - n->lcount = g->lcount; - n->llcount = g->llcount; - if (g->ylist) - n->ylist = arrdup(g->ylist); - else - n->ylist = NULL; - - if ((n->ecount = g->ecount)) { - n->expls = ep = (Cexpl *) ncalloc((n->ecount + 1) * - sizeof(Cexpl)); - for (eq = g->expls; (o = *eq); eq++, ep++) { - *ep = e = (Cexpl) ncalloc(sizeof(struct cexpl)); - e->count = (fi ? o->fcount : o->count); - e->str = ztrdup(o->str); - } - *ep = NULL; - } else - n->expls = NULL; - n->widths = NULL; + nmatches += g->mcount; + smatches += g->lcount; + + n = (Cmgroup) zcalloc(sizeof(struct cmgroup)); + + if (g->perm) { + g->perm->next = NULL; + freematches(g->perm); + } + g->perm = n; + + if (!lmatches) + lmatches = n; + if (pmatches) + pmatches->prev = n; + n->next = pmatches; + pmatches = n; + n->prev = NULL; + n->num = gn++; + n->flags = g->flags; + n->mcount = g->mcount; + n->matches = p = (Cmatch *) zcalloc((n->mcount + 1) * sizeof(Cmatch)); + n->name = ztrdup(g->name); + for (q = g->matches; *q; q++, p++) + *p = dupmatch(*q, nbrbeg, nbrend); + *p = NULL; + + n->lcount = g->lcount; + n->llcount = g->llcount; + if (g->ylist) + n->ylist = zarrdup(g->ylist); + else + n->ylist = NULL; + + if ((n->ecount = g->ecount)) { + n->expls = ep = (Cexpl *) zcalloc((n->ecount + 1) * sizeof(Cexpl)); + for (eq = g->expls; (o = *eq); eq++, ep++) { + *ep = e = (Cexpl) zcalloc(sizeof(struct cexpl)); + e->count = (fi ? o->fcount : o->count); + e->str = ztrdup(o->str); + } + *ep = NULL; + } else + n->expls = NULL; + n->widths = NULL; + } else { + if (!lmatches) + lmatches = g->perm; + if (pmatches) + pmatches->prev = g->perm; + g->perm->next = pmatches; + pmatches = g->perm; + g->perm->prev = NULL; + + nmatches += g->mcount; + smatches += g->lcount; + g->num = gn++; + } + g->new = 0; g = g->next; } for (g = pmatches; g; g = g->next) { @@ -2490,6 +2672,7 @@ freematch(Cmatch m, int nbeg, int nend) zsfree(m->rems); zsfree(m->remf); zsfree(m->disp); + zsfree(m->autoq); zfree(m->brpl, nbeg * sizeof(int)); zfree(m->brsl, nend * sizeof(int)); @@ -2499,7 +2682,7 @@ freematch(Cmatch m, int nbeg, int nend) /* This frees the groups of matches. */ /**/ -void +mod_export void freematches(Cmgroup g) { Cmgroup n; @@ -2508,7 +2691,7 @@ freematches(Cmgroup g) while (g) { n = g->next; - + for (m = g->matches; *m; m++) freematch(*m, g->nbrbeg, g->nbrend); @@ -2523,6 +2706,7 @@ freematches(Cmgroup g) } free(g->expls); } + zsfree(g->name); free(g); g = n; diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 283b8de62..ef961eeba 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -1,5 +1,5 @@ /* - * complete.c - the complete module + * complete.c - the complete module, interface part * * This file is part of zsh, the Z shell. * @@ -29,12 +29,50 @@ #include "complete.mdh" #include "complete.pro" -#define GLOBAL_PROTOTYPES -#include "zle_tricky.pro" -#undef GLOBAL_PROTOTYPES + +/* global variables for shell parameters in new style completion */ /**/ -void +mod_export zlong compcurrent; +/**/ +zlong complistmax, + complistlines, + compignored; + +/**/ +mod_export +char **compwords, + *compprefix, + *compsuffix, + *compisuffix, + *compqiprefix, + *compqisuffix, + *compquote, + *compqstack, + *comppatmatch, + *complastprompt; +/**/ +char *compiprefix, + *compcontext, + *compparameter, + *compredirect, + *compquoting, + *comprestore, + *complist, + *compinsert, + *compexact, + *compexactstr, + *comppatinsert, + *comptoend, + *compoldlist, + *compoldins, + *compvared; + +/**/ +Param *comprpms, *compkpms; + +/**/ +mod_export void freecmlist(Cmlist l) { Cmlist n; @@ -51,7 +89,7 @@ freecmlist(Cmlist l) } /**/ -void +mod_export void freecmatcher(Cmatcher m) { Cmatcher n; @@ -86,29 +124,10 @@ freecpattern(Cpattern p) } } -/* Copy a list of completion matchers. */ - -static Cmlist -cpcmlist(Cmlist l) -{ - Cmlist r = NULL, *p = &r, n; - - while (l) { - *p = n = (Cmlist) zalloc(sizeof(struct cmlist)); - n->next = NULL; - n->matcher = cpcmatcher(l->matcher); - n->str = ztrdup(l->str); - - p = &(n->next); - l = l->next; - } - return r; -} - /* Copy a completion matcher list. */ /**/ -Cmatcher +mod_export Cmatcher cpcmatcher(Cmatcher m) { Cmatcher r = NULL, *p = &r, n; @@ -155,37 +174,10 @@ cpcpattern(Cpattern o) return r; } -/* Set the global match specs. */ - -/**/ -int -set_gmatcher(char *name, char **argv) -{ - Cmlist l = NULL, *q = &l, n; - Cmatcher m; - - while (*argv) { - if ((m = parse_cmatcher(name, *argv)) == pcm_err) - return 1; - *q = n = (Cmlist) zhalloc(sizeof(struct cmlist)); - n->next = NULL; - n->matcher = m; - n->str = *argv++; - - q = &(n->next); - } - freecmlist(cmatcher); - PERMALLOC { - cmatcher = cpcmlist(l); - } LASTALLOC; - - return 1; -} - /* Parse a string for matcher control, containing multiple matchers. */ /**/ -Cmatcher +mod_export Cmatcher parse_cmatcher(char *name, char *s) { Cmatcher ret = NULL, r = NULL, n; @@ -262,8 +254,11 @@ parse_cmatcher(char *name, char *s) return pcm_err; } word = NULL; - wl = -1; - s++; + if (*++s == '*') { + s++; + wl = -2; + } else + wl = -1; } else { word = parse_pattern(name, &s, &wl, 0, &err); @@ -389,17 +384,17 @@ static int bin_compadd(char *name, char **argv, char *ops, int func) { struct cadata dat; - char *p, **sp, *e, *m = NULL; + char *p, **sp, *e, *m = NULL, *mstr = NULL; int dm; Cmatcher match = NULL; if (incompfunc != 1) { - zerrnam(name, "can only be called from completion function", NULL, 0); + zwarnnam(name, "can only be called from completion function", NULL, 0); return 1; } dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = - dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL; + dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL; dat.match = NULL; dat.flags = 0; dat.aflags = CAF_MATCH; @@ -462,10 +457,6 @@ bin_compadd(char *name, char **argv, char *ops, int func) if (!(dat.aflags & CAF_UNIQALL)) dat.aflags |= CAF_UNIQCON; break; - case 'y': - sp = &(dat.ylist); - e = "string expected after -%c"; - break; case 'i': sp = &(dat.ipre); e = "string expected after -%c"; @@ -486,9 +477,6 @@ bin_compadd(char *name, char **argv, char *ops, int func) sp = &(dat.prpre); e = "string expected after -%c"; break; - case 'a': - dat.aflags |= CAF_ALT; - break; case 'M': sp = &m; e = "matching specification expected after -%c"; @@ -531,7 +519,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) argv++; goto ca_args; default: - zerrnam(name, "bad option: -%c", NULL, *p); + zwarnnam(name, "bad option: -%c", NULL, *p); return 1; } if (sp) { @@ -545,18 +533,29 @@ bin_compadd(char *name, char **argv, char *ops, int func) *sp = *argv; p = "" - 1; } else { - zerrnam(name, e, NULL, *p); + zwarnnam(name, e, NULL, *p); return 1; } - if (dm && (match = parse_cmatcher(name, m)) == pcm_err) { - match = NULL; - return 1; + if (dm) { + if (mstr) + mstr = tricat(mstr, " ", m); + else + mstr = ztrdup(m); + m = NULL; } } } } + if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) { + zsfree(mstr); + return 1; + } + zsfree(mstr); + ca_args: - if (!*argv) + + if (!*argv && !dat.group && + !(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON))) return 1; dat.match = match = cpcmatcher(match); @@ -574,7 +573,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) #define CVT_SUFPAT 5 /**/ -void +mod_export void ignore_prefix(int l) { if (l) { @@ -598,7 +597,7 @@ ignore_prefix(int l) } /**/ -void +mod_export void ignore_suffix(int l) { if (l) { @@ -621,7 +620,7 @@ ignore_suffix(int l) } /**/ -void +mod_export void restrict_range(int b, int e) { int wl = arrlen(compwords) - 1; @@ -644,6 +643,7 @@ restrict_range(int b, int e) } } +/**/ static int do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) { @@ -740,7 +740,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) char *p, sav; if (!(l = strlen(compprefix))) - return 0; + return ((na == 1 || na == -1) && pattry(pp, compprefix)); if (na < 0) { p = compprefix + l; na = -na; @@ -766,7 +766,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) char *p; if (!(ol = l = strlen(compsuffix))) - return 0; + return ((na == 1 || na == -1) && pattry(pp, compsuffix)); if (na < 0) { p = compsuffix; na = -na; @@ -798,11 +798,11 @@ bin_compset(char *name, char **argv, char *ops, int func) char *sa = NULL, *sb = NULL; if (incompfunc != 1) { - zerrnam(name, "can only be called from completion function", NULL, 0); + zwarnnam(name, "can only be called from completion function", NULL, 0); return 1; } if (argv[0][0] != '-') { - zerrnam(name, "missing option", NULL, 0); + zwarnnam(name, "missing option", NULL, 0); return 1; } switch (argv[0][1]) { @@ -814,7 +814,7 @@ bin_compset(char *name, char **argv, char *ops, int func) case 'S': test = CVT_SUFPAT; break; case 'q': return set_comp_sep(); default: - zerrnam(name, "bad option -%c", NULL, argv[0][1]); + zwarnnam(name, "bad option -%c", NULL, argv[0][1]); return 1; } if (argv[0][2]) { @@ -823,7 +823,7 @@ bin_compset(char *name, char **argv, char *ops, int func) na = 2; } else { if (!(sa = argv[1])) { - zerrnam(name, "missing string for option -%c", NULL, argv[0][1]); + zwarnnam(name, "missing string for option -%c", NULL, argv[0][1]); return 1; } sb = argv[2]; @@ -831,7 +831,7 @@ bin_compset(char *name, char **argv, char *ops, int func) } if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb : (sb && argv[na]))) { - zerrnam(name, "too many arguments", NULL, 0); + zwarnnam(name, "too many arguments", NULL, 0); return 1; } switch (test) { @@ -841,11 +841,9 @@ bin_compset(char *name, char **argv, char *ops, int func) break; case CVT_RANGEPAT: tokenize(sa); - sa = rembslash(sa); remnulargs(sa); if (sb) { tokenize(sb); - sb = rembslash(sb); remnulargs(sb); } break; @@ -861,7 +859,6 @@ bin_compset(char *name, char **argv, char *ops, int func) } else na = -1; tokenize(sa); - sa = rembslash(sa); remnulargs(sa); break; } @@ -892,9 +889,6 @@ static struct compparam comprparams[] = { static struct compparam compkparams[] = { { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) }, - { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL }, - { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL }, - { "total_matchers", PM_INTEGER, VAL(compmatchertot), NULL, NULL }, { "context", PM_SCALAR, VAL(compcontext), NULL, NULL }, { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL }, { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL }, @@ -902,7 +896,6 @@ static struct compparam compkparams[] = { { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL }, { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL }, { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) }, - { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL }, { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL }, { "exact", PM_SCALAR, VAL(compexact), NULL, NULL }, { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL }, @@ -917,8 +910,9 @@ static struct compparam compkparams[] = { { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL }, { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL }, { "vared", PM_SCALAR, VAL(compvared), NULL, NULL }, - { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) }, { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) }, + { "all_quotes", PM_SCALAR | PM_READONLY, VAL(compqstack), NULL, NULL }, + { "ignored", PM_INTEGER | PM_READONLY, VAL(compignored), NULL, NULL }, { NULL, 0, NULL, NULL, NULL } }; @@ -976,7 +970,7 @@ makecompparams(void) comprpms[CPN_COMPSTATE] = cpm; tht = paramtab; - cpm->level = locallevel; + cpm->level = locallevel + 1; cpm->gets.hfn = get_compstate; cpm->sets.hfn = set_compstate; cpm->unsetfn = compunsetfn; @@ -1029,14 +1023,7 @@ set_compstate(Param pm, HashTable ht) static zlong get_nmatches(Param pm) { - return num_matches(1); -} - -/**/ -static zlong -get_anmatches(Param pm) -{ - return num_matches(0); + return (permmatches(0) ? 0 : nmatches); } /**/ @@ -1083,14 +1070,37 @@ static void compunsetfn(Param pm, int exp) { if (exp) { - if (PM_TYPE(pm->flags) == PM_SCALAR) { - zsfree(*((char **) pm->u.data)); - *((char **) pm->u.data) = ztrdup(""); - } else if (PM_TYPE(pm->flags) == PM_ARRAY) { - freearray(*((char ***) pm->u.data)); - *((char ***) pm->u.data) = zcalloc(sizeof(char *)); + if (pm->u.data) { + if (PM_TYPE(pm->flags) == PM_SCALAR) { + zsfree(*((char **) pm->u.data)); + *((char **) pm->u.data) = ztrdup(""); + } else if (PM_TYPE(pm->flags) == PM_ARRAY) { + freearray(*((char ***) pm->u.data)); + *((char ***) pm->u.data) = zcalloc(sizeof(char *)); + } else if (PM_TYPE(pm->flags) == PM_HASHED) { + deleteparamtable(pm->u.hash); + pm->u.hash = NULL; + } } - pm->flags |= PM_UNSET; + } else if (PM_TYPE(pm->flags) == PM_HASHED) { + Param *p; + int i; + + deletehashtable(pm->u.hash); + pm->u.hash = NULL; + + for (p = compkpms, i = CP_KEYPARAMS; i--; p++) + *p = NULL; + } + if (!exp) { + Param *p; + int i; + + for (p = comprpms, i = CP_REALPARAMS; i; p++, i--) + if (*p == pm) { + *p = NULL; + break; + } } } @@ -1102,31 +1112,35 @@ comp_setunset(int rset, int runset, int kset, int kunset) if (comprpms && (rset >= 0 || runset >= 0)) { for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) { - if (rset & 1) - (*p)->flags &= ~PM_UNSET; - if (runset & 1) - (*p)->flags |= PM_UNSET; + if (*p) { + if (rset & 1) + (*p)->flags &= ~PM_UNSET; + if (runset & 1) + (*p)->flags |= PM_UNSET; + } } } - if (comprpms && (kset >= 0 || kunset >= 0)) { + if (compkpms && (kset >= 0 || kunset >= 0)) { for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) { - if (kset & 1) - (*p)->flags &= ~PM_UNSET; - if (kunset & 1) - (*p)->flags |= PM_UNSET; + if (*p) { + if (kset & 1) + (*p)->flags &= ~PM_UNSET; + if (kunset & 1) + (*p)->flags |= PM_UNSET; + } } } } /**/ static int -comp_wrapper(List list, FuncWrap w, char *name) +comp_wrapper(Eprog prog, FuncWrap w, char *name) { if (incompfunc != 1) return 1; else { char *orest, *opre, *osuf, *oipre, *oisuf, **owords; - char *oqipre, *oqisuf, *oq, *oqi; + char *oqipre, *oqisuf, *oq, *oqi, *oqs, *oaq; zlong ocur; unsigned int runset = 0, kunset = 0, m, sm; Param *pp; @@ -1142,52 +1156,65 @@ comp_wrapper(List list, FuncWrap w, char *name) orest = comprestore; comprestore = ztrdup("auto"); ocur = compcurrent; - opre = dupstring(compprefix); - osuf = dupstring(compsuffix); - oipre = dupstring(compiprefix); - oisuf = dupstring(compisuffix); - oqipre = dupstring(compqiprefix); - oqisuf = dupstring(compqisuffix); - oq = dupstring(compquote); - oqi = dupstring(compquoting); - - HEAPALLOC { - owords = arrdup(compwords); - } LASTALLOC; - - runshfunc(list, w, name); + opre = ztrdup(compprefix); + osuf = ztrdup(compsuffix); + oipre = ztrdup(compiprefix); + oisuf = ztrdup(compisuffix); + oqipre = ztrdup(compqiprefix); + oqisuf = ztrdup(compqisuffix); + oq = ztrdup(compquote); + oqi = ztrdup(compquoting); + oqs = ztrdup(compqstack); + oaq = ztrdup(autoq); + owords = zarrdup(compwords); + + runshfunc(prog, w, name); if (comprestore && !strcmp(comprestore, "auto")) { compcurrent = ocur; zsfree(compprefix); - compprefix = ztrdup(opre); + compprefix = opre; zsfree(compsuffix); - compsuffix = ztrdup(osuf); + compsuffix = osuf; zsfree(compiprefix); - compiprefix = ztrdup(oipre); + compiprefix = oipre; zsfree(compisuffix); - compisuffix = ztrdup(oisuf); + compisuffix = oisuf; zsfree(compqiprefix); - compqiprefix = ztrdup(oqipre); + compqiprefix = oqipre; zsfree(compqisuffix); - compqisuffix = ztrdup(oqisuf); + compqisuffix = oqisuf; zsfree(compquote); - compquote = ztrdup(oq); + compquote = oq; zsfree(compquoting); - compquoting = ztrdup(oqi); + compquoting = oqi; + zsfree(compqstack); + compqstack = oqs; + zsfree(autoq); + autoq = oaq; freearray(compwords); - PERMALLOC { - compwords = arrdup(owords); - } LASTALLOC; + compwords = owords; comp_setunset(CP_COMPSTATE | (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX)), (runset & CP_ALLREALS), (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS)); - } else + } else { comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE), (kunset & CP_RESTORE)); + zsfree(opre); + zsfree(osuf); + zsfree(oipre); + zsfree(oisuf); + zsfree(oqipre); + zsfree(oqisuf); + zsfree(oq); + zsfree(oqi); + zsfree(oqs); + zsfree(oaq); + freearray(owords); + } zsfree(comprestore); comprestore = orest; @@ -1228,42 +1255,6 @@ cond_range(char **a, int id) (id ? cond_str(a, 1, 1) : NULL), 0); } -/**/ -static void -cmsetfn(Param pm, char **v) -{ - set_gmatcher(pm->nam, v); -} - -/**/ -static char ** -cmgetfn(Param pm) -{ - int num; - Cmlist p; - char **ret, **q; - - for (num = 0, p = cmatcher; p; p = p->next, num++); - - ret = (char **) zhalloc((num + 1) * sizeof(char *)); - - for (q = ret, p = cmatcher; p; p = p->next, q++) - *q = dupstring(p->str); - *q = NULL; - - return ret; -} - -/**/ -static void -cmunsetfn(Param pm, int exp) -{ - char *dummy[1]; - - dummy[0] = NULL; - set_gmatcher(pm->nam, dummy); -} - static struct builtin bintab[] = { BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL), BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL), @@ -1280,53 +1271,108 @@ static struct funcwrap wrapper[] = { WRAPDEF(comp_wrapper), }; -static struct paramdef patab[] = { - PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn) +/* The order of the entries in this table has to match the *HOOK + * macros in comp.h */ + +/**/ +struct hookdef comphooks[] = { + HOOKDEF("insert_match", NULL, HOOKF_ALL), + HOOKDEF("menu_start", NULL, HOOKF_ALL), + HOOKDEF("compctl_make", NULL, 0), + HOOKDEF("compctl_cleanup", NULL, 0), + HOOKDEF("comp_list_matches", ilistmatches, 0), }; /**/ int -setup_complete(Module m) +setup_(Module m) { - makecompparamsptr = makecompparams; - comp_setunsetptr = comp_setunset; + hasperm = 0; + + comprpms = compkpms = NULL; + compwords = NULL; + compprefix = compsuffix = compiprefix = compisuffix = + compqiprefix = compqisuffix = + compcontext = compparameter = compredirect = compquote = + compquoting = comprestore = complist = compinsert = + compexact = compexactstr = comppatmatch = comppatinsert = + complastprompt = comptoend = compoldlist = compoldins = + compvared = compqstack = NULL; + + hascompmod = 1; return 0; } /**/ int -boot_complete(Module m) +boot_(Module m) { + addhookfunc("complete", (Hookfn) do_completion); + addhookfunc("before_complete", (Hookfn) before_complete); + addhookfunc("after_complete", (Hookfn) after_complete); + addhookfunc("accept_completion", (Hookfn) accept_last); + addhookfunc("reverse_menu", (Hookfn) reverse_menu); + addhookfunc("list_matches", (Hookfn) list_matches); + addhookfunc("invalidate_list", (Hookfn) invalidate_list); + addhookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks)); if (!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) | addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) | - addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) | !addwrapper(m, wrapper))) return 1; return 0; } -#ifdef MODULE - /**/ int -cleanup_complete(Module m) +cleanup_(Module m) { + deletehookfunc("complete", (Hookfn) do_completion); + deletehookfunc("before_complete", (Hookfn) before_complete); + deletehookfunc("after_complete", (Hookfn) after_complete); + deletehookfunc("accept_completion", (Hookfn) accept_last); + deletehookfunc("reverse_menu", (Hookfn) reverse_menu); + deletehookfunc("list_matches", (Hookfn) list_matches); + deletehookfunc("invalidate_list", (Hookfn) invalidate_list); + deletehookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks)); deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)); - deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)); deletewrapper(m, wrapper); return 0; } /**/ int -finish_complete(Module m) +finish_(Module m) { - makecompparamsptr = NULL; - comp_setunsetptr = NULL; + if (compwords) + freearray(compwords); + zsfree(compprefix); + zsfree(compsuffix); + zsfree(compiprefix); + zsfree(compisuffix); + zsfree(compqiprefix); + zsfree(compqisuffix); + zsfree(compcontext); + zsfree(compparameter); + zsfree(compredirect); + zsfree(compquote); + zsfree(compqstack); + zsfree(compquoting); + zsfree(comprestore); + zsfree(complist); + zsfree(compinsert); + zsfree(compexact); + zsfree(compexactstr); + zsfree(comppatmatch); + zsfree(comppatinsert); + zsfree(complastprompt); + zsfree(comptoend); + zsfree(compoldlist); + zsfree(compoldins); + zsfree(compvared); + + hascompmod = 0; return 0; } - -#endif diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index 32e0c3a68..3dda28e11 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -28,15 +28,8 @@ */ #include "complete.mdh" -#define GLOBAL_PROTOTYPES -#include "zle_tricky.pro" -#undef GLOBAL_PROTOTYPES #include "compmatch.pro" -/* Convenience macro for calling bslashquote() (formerly quotename()). */ - -#define quotename(s, e) bslashquote(s, e, instring) - /* This compares two cpattern lists and returns non-zero if they are * equal. */ @@ -75,14 +68,14 @@ cmp_cmatchers(Cmatcher a, Cmatcher b) /* Add the given matchers to the bmatcher list. */ /**/ -void +mod_export void add_bmatchers(Cmatcher m) { Cmlist old = bmatchers, *q = &bmatchers, n; for (; m; m = m->next) { if ((!m->flags && m->wlen > 0 && m->llen > 0) || - (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) { + (m->flags == CMF_RIGHT && m->wlen < 0 && !m->llen)) { *q = n = (Cmlist) zhalloc(sizeof(struct cmlist)); n->matcher = m; q = &(n->next); @@ -95,7 +88,7 @@ add_bmatchers(Cmatcher m) * ensure that the bmatchers list contains no matchers not in mstack. */ /**/ -void +mod_export void update_bmatchers(void) { Cmlist p = bmatchers, q = NULL, ms; @@ -141,6 +134,7 @@ get_cline(char *l, int ll, char *w, int wl, char *o, int ol, int fl) r->slen = 0; r->flags = fl; r->prefix = r->suffix = NULL; + r->min = r->max = 0; return r; } @@ -443,7 +437,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, int sfx, int test, int part) { int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw; - int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc; + int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc, bslash; VARARR(unsigned char, ea, ll + 1); char *ow; Cmlist ms; @@ -553,7 +547,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, } else t = match_str(l + llen + moff, tp + moff, NULL, 0, NULL, 0, 1, part); - if (t || !both) + if (t || (mp->wlen == -1 && !both)) break; } } @@ -743,12 +737,15 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, if (mp) continue; - if (l[ind] == w[ind]) { + bslash = 0; + if (l[ind] == w[ind] || + (bslash = (lw > 1 && w[ind] == '\\' && + (ind ? (w[0] == l[0]) : (w[1] == l[0]))))) { /* No matcher could be used, but the strings have the same * character here, skip over it. */ - l += add; w += add; - il++; iw++; - ll--; lw--; + l += add; w += (bslash ? (add + add ) : add); + il++; iw += 1 + bslash; + ll--; lw -= 1 + bslash; bc++; if (!test) while (bp && bc >= (useqbr ? bp->qpos : bp->pos)) { @@ -839,7 +836,7 @@ match_parts(char *l, char *w, int n, int part) * and the suffix don't match the word w. */ /**/ -char * +mod_export char * comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu, Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact) { @@ -853,9 +850,8 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu, if (!pattry(cp, r)) return NULL; - r = (qu ? quotename(r, NULL) : dupstring(r)); - if (qu == 2 && r[0] == '\\' && r[1] == '~') - chuck(r); + r = (qu == 2 ? tildequote(r, 0) : multiquote(r, !qu)); + /* We still break it into parts here, trying to build a sensible * cline list for these matches, too. */ w = dupstring(w); @@ -866,10 +862,7 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu, Cline pli, plil; int mpl, rpl, wl; - w = (qu ? quotename(w, NULL) : dupstring(w)); - if (qu == 2 && w[0] == '\\' && w[1] == '~') - chuck(w); - + w = (qu == 2 ? tildequote(w, 0) : multiquote(w, !qu)); wl = strlen(w); /* Always try to match the prefix. */ @@ -1016,7 +1009,7 @@ bld_parts(char *str, int len, int plen, Cline *lp) while (len) { for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { mp = ms->matcher; - if (mp->flags == CMF_RIGHT && mp->wlen == -1 && + if (mp && mp->flags == CMF_RIGHT && mp->wlen < 0 && !mp->llen && len >= mp->ralen && mp->ralen && pattern_match(mp->right, str, NULL, NULL)) { int olen = str - p, llen; @@ -1136,7 +1129,7 @@ bld_line(Cpattern pat, char *line, char *lp, t = 0; for (ms = bmatchers; ms && !t; ms = ms->next) { mp = ms->matcher; - if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && + if (mp && !mp->flags && mp->wlen <= wlen && mp->llen <= l && pattern_match(mp->line, (sfx ? line - mp->llen : line), NULL, ea) && pattern_match(mp->word, (sfx ? word - mp->wlen : word), @@ -1186,7 +1179,7 @@ join_strs(int la, char *sa, int lb, char *sb) /* Different characters, try the matchers. */ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { mp = ms->matcher; - if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + if (mp && !mp->flags && mp->wlen > 0 && mp->llen > 0 && mp->wlen <= la && mp->wlen <= lb) { /* The pattern has no anchors and the word * pattern fits, try it. */ @@ -1373,7 +1366,7 @@ join_sub(Cmdata md, char *str, int len, int *mlen, int sfx, int join) /* We use only those patterns that match a non-empty * string in both the line and the word and that have * no anchors. */ - if (!mp->flags && mp->wlen > 0 && mp->llen > 0) { + if (mp && !mp->flags && mp->wlen > 0 && mp->llen > 0) { /* We first test, if the old string matches already the * new one. */ if (mp->llen <= ol && mp->wlen <= nl && @@ -1684,7 +1677,7 @@ join_mid(Cline o, Cline n) * didn't. */ /**/ -static void +static int sub_join(Cline a, Cline b, Cline e, int anew) { if (!e->suffix && a->prefix) { @@ -1707,31 +1700,28 @@ sub_join(Cline a, Cline b, Cline e, int anew) *p = e->prefix; ca = a->prefix; - while (n != op) { + while (n) { e->prefix = cp_cline(n, 0); a->prefix = cp_cline(ca, 0); if (anew) { join_psfx(e, a, NULL, NULL, 0); - if (e->prefix) { - e->min += min; - e->max += max; - break; - } + if (e->prefix) + return max - min; } else { - join_psfx(e, a, NULL, NULL, 0); - if (a->prefix) { - a->min += min; - a->max += max; - break; - } + join_psfx(a, e, NULL, NULL, 0); + if (a->prefix) + return max - min; } min -= n->min; - max -= n->max; + if (n == op) + break; n = n->next; } + return max - min; } + return 0; } /* This simplifies the cline list given as the first argument so that @@ -1748,7 +1738,8 @@ join_clines(Cline o, Cline n) if (!o) return n; else { - Cline oo = o, nn = n, po = NULL, pn = NULL; + Cline oo = o, nn = n, po = NULL, pn = NULL, x; + int diff; /* Walk through the lists. */ while (o && n) { @@ -1760,7 +1751,7 @@ join_clines(Cline o, Cline n) for (t = o; (tn = t->next) && (tn->flags & CLF_NEW); t = tn); if (tn && cmp_anchors(tn, n, 0)) { - sub_join(n, o, tn, 1); + diff = sub_join(n, o, tn, 1); if (po) po->next = tn; @@ -1768,8 +1759,15 @@ join_clines(Cline o, Cline n) oo = tn; t->next = NULL; free_cline(o); + x = o; o = tn; - o->flags |= CLF_MISS; + if (po && cmp_anchors(x, po, 0)) { + po->flags |= CLF_MISS; + po->max += diff; + } else { + o->flags |= CLF_MISS; + o->max += diff; + } continue; } } @@ -1778,10 +1776,16 @@ join_clines(Cline o, Cline n) for (t = n; (tn = t->next) && (tn->flags & CLF_NEW); t = tn); if (tn && cmp_anchors(o, tn, 0)) { - sub_join(o, n, tn, 0); + diff = sub_join(o, n, tn, 0); + if (po && cmp_anchors(n, pn, 0)) { + po->flags |= CLF_MISS; + po->max += diff; + } else { + o->flags |= CLF_MISS; + o->max += diff; + } n = tn; - o->flags |= CLF_MISS; continue; } } @@ -1809,6 +1813,7 @@ join_clines(Cline o, Cline n) t = tn); if (tn && cmp_anchors(tn, n, 1)) { sub_join(n, o, tn, 1); + if (po) po->next = tn; else @@ -1837,24 +1842,41 @@ join_clines(Cline o, Cline n) for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn); if (tn) { - sub_join(o, n, tn, 0); + diff = sub_join(o, n, tn, 0); + if (po && cmp_anchors(n, pn, 0)) { + po->flags |= CLF_MISS; + po->max += diff; + } else { + o->flags |= CLF_MISS; + o->max += diff; + } n = tn; - o->flags |= CLF_MISS; + po = o; + o = o->next; + pn = n; + n = n->next; continue; } else { for (t = o; (tn = t->next) && !cmp_anchors(n, tn, 1); t = tn); if (tn) { - sub_join(n, o, tn, 1); + diff = sub_join(n, o, tn, 1); if (po) po->next = tn; else oo = tn; + x = o; o = tn; - o->flags |= CLF_MISS; + if (po && cmp_anchors(x, po, 0)) { + po->flags |= CLF_MISS; + po->max += diff; + } else { + o->flags |= CLF_MISS; + o->max += diff; + } continue; } else { if (o->flags & CLF_SUF) diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index fe997b12b..0d93b8727 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -28,16 +28,8 @@ */ #include "complete.mdh" -#define GLOBAL_PROTOTYPES -#include "zle_tricky.pro" -#undef GLOBAL_PROTOTYPES #include "compresult.pro" -/* Convenience macro for calling bslashquote() (formerly quotename()). * - * This uses the instring variable above. */ - -#define quotename(s, e) bslashquote(s, e, instring) - #define inststr(X) inststrlen((X),1,-1) /* This cuts the cline list before the stuff that isn't worth @@ -72,7 +64,8 @@ cut_cline(Cline l) q = p; } if (!e && q && !q->orig && !q->olen && (q->flags & CLF_MISS) && - !(q->flags & CLF_MATCHED) && (q->word ? q->wlen : q->llen) < 3) { + (!(q->flags & CLF_MATCHED) || (!q->prefix && !q->suffix)) && + (q->word ? q->wlen : q->llen) < 3) { q->word = q->line = NULL; q->wlen = q->llen = 0; } @@ -143,8 +136,9 @@ cline_str(Cline l, int ins, int *csp) l = cut_cline(l); - pmm = smm = dm = 0; + pmm = smm = dm = pcs = scs = 0; pm = pmax = sm = smax = d = mid = cbr = -1; + brp = brs = NULL; /* Get the information about the brace beginning and end we have * to re-insert. */ @@ -594,7 +588,7 @@ do_ambiguous(void) /* If we have to insert the first match, call do_single(). This is * * how REC_EXACT takes effect. We effectively turn the ambiguous * * completion into an unambiguous one. */ - if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) { + if (ainfo && ainfo->exact == 1 && !(fromcomp & FC_LINE)) { minfo.cur = NULL; do_single(ainfo->exactm); invalidatelist(); @@ -616,6 +610,7 @@ do_ambiguous(void) do_ambig_menu(); } else if (ainfo) { int atend = (cs == we), la, eq, tcs; + VARARR(char, old, we - wb); minfo.cur = NULL; minfo.asked = 0; @@ -623,11 +618,24 @@ do_ambiguous(void) fixsuffix(); /* First remove the old string from the line. */ + tcs = cs; cs = wb; + memcpy(old, (char *) line + wb, we - wb); foredel(we - wb); /* Now get the unambiguous string and insert it into the line. */ cline_str(ainfo->line, 1, NULL); + + /* Sometimes the different match specs used may result in a cline + * that gives an empty string. If that happened, we re-insert the + * old string. Unless there were matches added with -U, that is. */ + if (!(lastend - wb) && !hasunmatched) { + cs = wb; + foredel(lastend - wb); + inststrlen(old, 0, we - wb); + lastend = we; + cs = tcs; + } if (eparq) { tcs = cs; cs = lastend; @@ -677,7 +685,7 @@ do_ambiguous(void) if (uselist && (usemenu != 2 || (!listshown && !oldlist)) && ((!showinglist && (!listshown || !oldlist)) || (usemenu == 3 && !oldlist)) && - (smatches >= 2 || (compforcelist && *compforcelist))) + (smatches >= 2 || forcelist)) showinglist = -2; return ret; @@ -690,7 +698,7 @@ do_ambiguous(void) * (l)stat(). */ /**/ -int +mod_export int ztat(char *nam, struct stat *buf, int ls) { char b[PATH_MAX], *p; @@ -708,7 +716,7 @@ ztat(char *nam, struct stat *buf, int ls) /* Insert a single match in the command line. */ /**/ -void +mod_export void do_single(Cmatch m) { int l, sr = 0, scs; @@ -726,7 +734,8 @@ do_single(Cmatch m) /* We are currently not in a menu-completion, * * so set the position variables. */ minfo.pos = wb; - minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp)); + minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp) || + (!movetoend && cs == we)); minfo.end = we; } /* If we are already in a menu-completion or if we have done a * @@ -792,19 +801,40 @@ do_single(Cmatch m) else { /* Build the path name. */ if (partest && !*psuf && !(m->flags & CMF_PARNEST)) { - int ne = noerrs; + int ne = noerrs, tryit = 1; p = (char *) zhalloc(strlen((m->flags & CMF_ISPAR) ? parpre : m->ripre) + strlen(str) + 2); sprintf(p, "%s%s%c", ((m->flags & CMF_ISPAR) ? parpre : m->ripre), str, - ((m->flags & CMF_PARBR) ? Outbrace : '\0')); - noerrs = 1; - parsestr(p); - singsub(&p); - errflag = 0; - noerrs = ne; + ((m->flags & CMF_PARBR) ? '}' : '\0')); + if (*p == '$') { + char *n; + Param pm; + + if (p[1] == '{') { + char *e; + + n = dupstring(p + 2); + e = n + strlen(n) - 1; + + if (*e == '}') + *e = '\0'; + } else + n = p + 1; + + if ((pm = (Param) paramtab->getnode(paramtab, n)) && + PM_TYPE(pm->flags) != PM_SCALAR) + tryit = 0; + } + if (tryit) { + noerrs = 1; + parsestr(p); + singsub(&p); + errflag = 0; + noerrs = ne; + } } else { p = (char *) zhalloc(strlen(prpre) + strlen(str) + strlen(psuf) + 3); @@ -857,11 +887,13 @@ do_single(Cmatch m) /* If we didn't add a suffix, add a space, unless we are * * doing menu completion or we are completing files and * * the string doesn't name an existing file. */ - if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) { - inststrlen(&(m->autoq), 1, 1); - minfo.insc++; + if (m->autoq && (!m->isuf || !strpfx(m->autoq, m->isuf))) { + int al = strlen(m->autoq); + inststrlen(m->autoq, 1, al); + minfo.insc += al; } - if (!menucmp && (usemenu != 3 || insspace)) { + if (!menucmp && !(m->flags & CMF_NOSPACE) && + (usemenu != 3 || insspace)) { inststrlen(" ", 1, 1); minfo.insc++; if (minfo.we) @@ -897,7 +929,7 @@ do_single(Cmatch m) * insert the next completion. */ /**/ -void +mod_export void do_menucmp(int lst) { /* Just list the matches if the list was requested. */ @@ -906,44 +938,40 @@ do_menucmp(int lst) return; } /* Otherwise go to the next match in the array... */ - HEAPALLOC { - do { - if (!*++(minfo.cur)) { - do { - if (!(minfo.group = (minfo.group)->next)) - minfo.group = amatches; - } while (!(minfo.group)->mcount); - minfo.cur = minfo.group->matches; - } - } while (menuacc && - !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)); - /* ... and insert it into the command line. */ - metafy_line(); - do_single(*(minfo.cur)); - unmetafy_line(); - } LASTALLOC; + do { + if (!*++(minfo.cur)) { + do { + if (!(minfo.group = (minfo.group)->next)) + minfo.group = amatches; + } while (!(minfo.group)->mcount); + minfo.cur = minfo.group->matches; + } + } while (menuacc && + !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)); + /* ... and insert it into the command line. */ + metafy_line(); + do_single(*(minfo.cur)); + unmetafy_line(); } /**/ int reverse_menu(Hookdef dummy, void *dummy2) { - HEAPALLOC { - do { - if (minfo.cur == (minfo.group)->matches) { - do { - if (!(minfo.group = (minfo.group)->prev)) - minfo.group = lmatches; - } while (!(minfo.group)->mcount); - minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1; - } else - minfo.cur--; - } while (menuacc && - !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)); - metafy_line(); - do_single(*(minfo.cur)); - unmetafy_line(); - } LASTALLOC; + do { + if (minfo.cur == (minfo.group)->matches) { + do { + if (!(minfo.group = (minfo.group)->prev)) + minfo.group = lmatches; + } while (!(minfo.group)->mcount); + minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1; + } else + minfo.cur--; + } while (menuacc && + !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)); + metafy_line(); + do_single(*(minfo.cur)); + unmetafy_line(); return 0; } @@ -953,7 +981,7 @@ reverse_menu(Hookdef dummy, void *dummy2) * accept several selections from the list of matches. */ /**/ -int +mod_export int accept_last(void) { if (!menuacc) { @@ -1042,7 +1070,7 @@ do_ambig_menu(void) } else { if (oldlist) { if (oldins && minfo.cur) - acceptlast(); + accept_last(); } else minfo.cur = NULL; } @@ -1074,24 +1102,6 @@ do_ambig_menu(void) minfo.cur = mc; } -/* Return the real number of matches. */ - -/**/ -zlong -num_matches(int normal) -{ - int alt; - - PERMALLOC { - alt = permmatches(0); - } LASTALLOC; - - if (normal) - return (alt ? 0 : nmatches); - else - return (alt ? nmatches : 0); -} - /* Return the number of screen lines needed for the list. */ /**/ @@ -1100,14 +1110,12 @@ list_lines(void) { Cmgroup oam; - PERMALLOC { - permmatches(0); - } LASTALLOC; + permmatches(0); oam = amatches; amatches = pmatches; listdat.valid = 0; - calclist(); + calclist(0); listdat.valid = 0; amatches = oam; @@ -1124,132 +1132,25 @@ comp_list(char *v) onlyexpl = (v && strstr(v, "expl")); } -/* This is used to print the explanation string. * - * It returns the number of lines printed. */ - -/**/ -int -printfmt(char *fmt, int n, int dopr, int doesc) -{ - char *p = fmt, nc[DIGBUFSIZE]; - int l = 0, cc = 0, b = 0, s = 0, u = 0, m; - - for (; *p; p++) { - /* Handle the `%' stuff (%% == %, %n == ). */ - if (doesc && *p == '%') { - if (*++p) { - m = 0; - switch (*p) { - case '%': - if (dopr) - putc('%', shout); - cc++; - break; - case 'n': - sprintf(nc, "%d", n); - if (dopr) - fprintf(shout, nc); - cc += strlen(nc); - break; - case 'B': - b = 1; - if (dopr) - tcout(TCBOLDFACEBEG); - break; - case 'b': - b = 0; m = 1; - if (dopr) - tcout(TCALLATTRSOFF); - break; - case 'S': - s = 1; - if (dopr) - tcout(TCSTANDOUTBEG); - break; - case 's': - s = 0; m = 1; - if (dopr) - tcout(TCSTANDOUTEND); - break; - case 'U': - u = 1; - if (dopr) - tcout(TCUNDERLINEBEG); - break; - case 'u': - u = 0; m = 1; - if (dopr) - tcout(TCUNDERLINEEND); - break; - case '{': - for (p++; *p && (*p != '%' || p[1] != '}'); p++, cc++) - if (dopr) - putc(*p, shout); - if (*p) - p++; - else - p--; - break; - } - if (dopr && m) { - if (b) - tcout(TCBOLDFACEBEG); - if (s) - tcout(TCSTANDOUTBEG); - if (u) - tcout(TCUNDERLINEBEG); - } - } else - break; - } else { - cc++; - if (*p == '\n') { - if (dopr) { - if (tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); - else { - int s = columns - 1 - (cc % columns); - - while (s-- > 0) - putc(' ', shout); - } - } - l += 1 + (cc / columns); - cc = 0; - } - if (dopr) - putc(*p, shout); - } - } - if (dopr) { - if (tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); - else { - int s = columns - 1 - (cc % columns); - - while (s-- > 0) - putc(' ', shout); - } - } - return l + (cc / columns); -} - /* This skips over matches that are not to be listed. */ /**/ Cmatch * -skipnolist(Cmatch *p) +skipnolist(Cmatch *p, int showall) { - while (*p && (((*p)->flags & (CMF_NOLIST | CMF_HIDE)) || - ((*p)->disp && ((*p)->flags & (CMF_DISPLINE | CMF_HIDE))))) + int mask = (showall ? 0 : (CMF_NOLIST | CMF_MULT)) | CMF_HIDE; + + while (*p && (((*p)->flags & mask) || + ((*p)->disp && + ((*p)->flags & (CMF_DISPLINE | CMF_HIDE))))) p++; return p; } /**/ -void -calclist(void) +mod_export void +calclist(int showall) { Cmgroup g; Cmatch *p, m; @@ -1259,7 +1160,7 @@ calclist(void) VARARR(int, mlens, nmatches + 1); if (listdat.valid && onlyexpl == listdat.onlyexpl && - menuacc == listdat.menuacc && + menuacc == listdat.menuacc && showall == listdat.showall && lines == listdat.lines && columns == listdat.columns) return; @@ -1267,6 +1168,8 @@ calclist(void) char **pp = g->ylist; int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0; + g->flags |= CGF_PACKED | CGF_ROWS; + if (!onlyexpl && pp) { /* We have an ylist, lets see, if it contains newlines. */ hidden = 1; @@ -1283,8 +1186,8 @@ calclist(void) while ((sptr = *pp)) { while (sptr && *sptr) { nlines += (nlptr = strchr(sptr, '\n')) - ? 1 + (nlptr-sptr)/columns - : strlen(sptr)/columns; + ? 1 + (nlptr-sptr) / columns + : strlen(sptr) / columns; sptr = nlptr ? nlptr+1 : NULL; } nlines++; @@ -1327,7 +1230,11 @@ calclist(void) mlens[m->gnum] = l; } nlist++; - } else if (!(m->flags & CMF_NOLIST)) { + if (!(m->flags & CMF_PACKED)) + g->flags &= ~CGF_PACKED; + if (!(m->flags & CMF_ROWS)) + g->flags &= ~CGF_ROWS; + } else if (showall || !(m->flags & (CMF_NOLIST | CMF_MULT))) { l = niceztrlen(m->str); ndisp++; if (l > glong) @@ -1337,6 +1244,10 @@ calclist(void) totl += l; mlens[m->gnum] = l; nlist++; + if (!(m->flags & CMF_PACKED)) + g->flags &= ~CGF_PACKED; + if (!(m->flags & CMF_ROWS)) + g->flags &= ~CGF_ROWS; } else hidden = 1; } @@ -1361,9 +1272,11 @@ calclist(void) } } if (!onlyexpl) { + char **pp; + int *ws, tlines, tline, tcols, maxlen, nth, width, glines; + for (g = amatches; g; g = g->next) { - char **pp; - int glines = 0; + glines = 0; zfree(g->widths, 0); g->widths = NULL; @@ -1396,20 +1309,19 @@ calclist(void) if (m->disp) { if (!(m->flags & CMF_DISPLINE)) glines += 1 + (mlens[m->gnum] / columns); - } else if (!(m->flags & CMF_NOLIST)) - glines += 1 + ((1 + mlens[m->gnum]) / columns); + } else if (showall || + !(m->flags & (CMF_NOLIST | CMF_MULT))) + glines += 1 + ((mlens[m->gnum]) / columns); } } } g->lins = glines; nlines += glines; } - } - if (!onlyexpl && isset(LISTPACKED)) { - char **pp; - int *ws, tlines, tline, tcols, maxlen, nth, width; - for (g = amatches; g; g = g->next) { + if (!(g->flags & CGF_PACKED)) + continue; + ws = g->widths = (int *) zalloc(columns * sizeof(int)); memset(ws, 0, columns * sizeof(int)); tlines = g->lins; @@ -1424,11 +1336,13 @@ calclist(void) for (i = 0; *pp; i++, pp++) ylens[i] = strlen(*pp) + add; - if (isset(LISTROWSFIRST)) { + if (g->flags & CGF_ROWS) { int count, tcol, first, maxlines = 0, llines; + int beg = columns / g->shortest, end = g->cols; + + while (1) { + tcols = (beg + end) >> 1; - for (tcols = columns / g->shortest; tcols > g->cols; - tcols--) { for (nth = first = maxlen = width = maxlines = llines = tcol = 0, count = g->dcount; @@ -1452,17 +1366,33 @@ calclist(void) ws[tcol++] = maxlen; width += maxlen; } - if (!count && width < columns) + if (!count && width < columns && + (tcols <= 0 || beg == end)) break; + + if (beg == end) { + beg--; + end--; + } else if (width < columns) { + if ((end = tcols) == beg - 1) + end++; + } else { + if ((beg = tcols) - 1 == end) + end++; + } } if (tcols > g->cols) tlines = maxlines; } else { - for (tlines = ((g->totl + columns) / columns); - tlines < g->lins; tlines++) { + int beg = ((g->totl + columns) / columns); + int end = g->lins; + + while (1) { + tlines = (beg + end) >> 1; + for (pp = g->ylist, nth = tline = width = maxlen = tcols = 0; - *pp; nth++, pp++) { + *pp; pp++) { if (ylens[nth] > maxlen) maxlen = ylens[nth]; if (++tline == tlines) { @@ -1471,24 +1401,41 @@ calclist(void) ws[tcols++] = maxlen; maxlen = tline = 0; } + nth++; } if (tline) { ws[tcols++] = maxlen; width += maxlen; } - if (nth == yl && width < columns) + if (nth == yl && width < columns && + (beg == end || tlines >= g->lins)) break; + + if (beg == end) { + beg++; + end++; + } else if (width < columns) { + if ((end = tlines) == beg + 1) + end--; + } else { + if ((beg = tlines) + 1 == end) + end--; + } } + if (tlines > g->lins) + tlines = g->lins; } } } else if (g->width) { - if (isset(LISTROWSFIRST)) { + if (g->flags & CGF_ROWS) { int addlen, count, tcol, maxlines = 0, llines, i; + int beg = columns / g->shortest, end = g->cols; Cmatch *first; - for (tcols = columns / g->shortest; tcols > g->cols; - tcols--) { - p = first = skipnolist(g->matches); + while (1) { + tcols = (beg + end) >> 1; + + p = first = skipnolist(g->matches, showall); for (maxlen = width = maxlines = llines = tcol = 0, count = g->dcount; count > 0; count--) { @@ -1497,7 +1444,7 @@ calclist(void) if (addlen > maxlen) maxlen = addlen; for (i = tcols; i && *p; i--) - p = skipnolist(p + 1); + p = skipnolist(p + 1, showall); llines++; if (!*p) { @@ -1510,29 +1457,46 @@ calclist(void) ws[tcol++] = maxlen; maxlen = 0; - p = first = skipnolist(first + 1); + p = first = skipnolist(first + 1, showall); } } if (tlines) { ws[tcol++] = maxlen; width += maxlen; } - if (!count && width < columns) + if (!count && width < columns && + (tcols <= 0 || beg == end)) break; + + if (beg == end) { + beg--; + end--; + } else if (width < columns) { + if ((end = tcols) == beg - 1) + end++; + } else { + if ((beg = tcols) - 1 == end) + end++; + } } if (tcols > g->cols) tlines = maxlines; } else { int addlen; + int smask = ((showall ? 0 : (CMF_NOLIST | CMF_MULT)) | + CMF_HIDE); + int beg = ((g->totl + columns) / columns); + int end = g->lins; + + while (1) { + tlines = (beg + end) >> 1; - for (tlines = ((g->totl + columns) / columns); - tlines < g->lins; tlines++) { for (p = g->matches, nth = tline = width = maxlen = tcols = 0; - (m = *p); p++, nth++) { + (m = *p); p++) { if (!(m->flags & (m->disp ? (CMF_DISPLINE | CMF_HIDE) : - (CMF_NOLIST | CMF_HIDE)))) { + smask))) { addlen = mlens[m->gnum] + add; if (addlen > maxlen) maxlen = addlen; @@ -1542,15 +1506,30 @@ calclist(void) ws[tcols++] = maxlen; maxlen = tline = 0; } + nth++; } } if (tline) { ws[tcols++] = maxlen; width += maxlen; } - if (nth == g->dcount && width < columns) + if (nth == g->dcount && width < columns && + (beg == end || tlines >= g->lins)) break; + + if (beg == end) { + beg++; + end++; + } else if (width < columns) { + if ((end = tlines) == beg + 1) + end--; + } else { + if ((beg = tlines) + 1 == end) + end--; + } } + if (tlines > g->lins) + tlines = g->lins; } } if (tlines == g->lins) { @@ -1584,21 +1563,23 @@ calclist(void) listdat.onlyexpl = onlyexpl; listdat.columns = columns; listdat.lines = lines; + listdat.showall = showall; } /**/ -int asklist(void) +mod_export int asklist(void) { /* Set the cursor below the prompt. */ trashzle(); showinglist = listshown = 0; - clearflag = (isset(USEZLE) && !termflags && - complastprompt && *complastprompt); + clearflag = (isset(USEZLE) && !termflags && dolastprompt); + lastlistlen = 0; /* Maybe we have to ask if the user wants to see the list. */ if ((!minfo.cur || !minfo.asked) && - ((complistmax && listdat.nlist > complistmax) || + ((complistmax > 0 && listdat.nlist >= complistmax) || + (complistmax < 0 && listdat.nlines <= -complistmax) || (!complistmax && listdat.nlines >= lines))) { int qup; zsetterm(); @@ -1614,8 +1595,7 @@ int asklist(void) tcmultout(TCUP, TCMULTUP, nlnct); } else putc('\n', shout); - if (minfo.cur) - minfo.asked = 2; + minfo.asked = 2; return 1; } if (clearflag) { @@ -1626,15 +1606,14 @@ int asklist(void) } else putc('\n', shout); settyinfo(&shttyinfo); - if (minfo.cur) - minfo.asked = 1; + minfo.asked = 1; } - return 0; + return (minfo.asked ? minfo.asked - 1 : 0); } /**/ -int -printlist(int over, CLPrintFunc printm) +mod_export int +printlist(int over, CLPrintFunc printm, int showall) { Cmgroup g; Cmatch *p, m; @@ -1715,7 +1694,7 @@ printlist(int over, CLPrintFunc printm) while (a--) putc(' ', shout); } - pq += (isset(LISTROWSFIRST) ? 1 : nc); + pq += ((g->flags & CGF_ROWS) ? 1 : nc); mc++; n--; } @@ -1728,10 +1707,11 @@ printlist(int over, CLPrintFunc printm) tcout(TCCLEAREOD); } } - pp += (isset(LISTROWSFIRST) ? g->cols : 1); + pp += ((g->flags & CGF_ROWS) ? g->cols : 1); } } - } else if (!listdat.onlyexpl && g->lcount) { + } else if (!listdat.onlyexpl && + (g->lcount || (showall && g->mcount))) { int n = g->dcount, nl, nc, i, j, wid; Cmatch *q; @@ -1765,7 +1745,7 @@ printlist(int over, CLPrintFunc printm) tcout(TCCLEAREOD); } } - for (p = skipnolist(g->matches); n && nl--;) { + for (p = skipnolist(g->matches, showall); n && nl--;) { i = g->cols; mc = 0; q = p; @@ -1794,14 +1774,16 @@ printlist(int over, CLPrintFunc printm) printed++; if (--n) - for (j = (isset(LISTROWSFIRST) ? 1 : nc); j && *q; j--) - q = skipnolist(q + 1); + for (j = ((g->flags & CGF_ROWS) ? 1 : nc); + j && *q; j--) + q = skipnolist(q + 1, showall); mc++; } - while (i-- > 0) - printm(g, NULL, mc++, ml, (!i), + while (i-- > 0) { + printm(g, NULL, mc, ml, (!i), (g->widths ? g->widths[mc] : g->width), NULL, NULL); - + mc++; + } if (n) { putc('\n', shout); ml++; @@ -1811,25 +1793,30 @@ printlist(int over, CLPrintFunc printm) tcout(TCCLEAREOD); } if (nl) - for (j = (isset(LISTROWSFIRST) ? g->cols : 1); j && *p; j--) - p = skipnolist(p + 1); + for (j = ((g->flags & CGF_ROWS) ? g->cols : 1); + j && *p; j--) + p = skipnolist(p + 1, showall); } } } - if (g->lcount) + if (g->lcount || (showall && g->mcount)) pnl = 1; g = g->next; } + lastlistlen = 0; if (clearflag) { /* Move the cursor up to the prompt, if always_last_prompt * * is set and all that... */ if ((ml = listdat.nlines + nlnct - 1) < lines) { tcmultout(TCUP, TCMULTUP, ml); showinglist = -1; + + lastlistlen = listdat.nlines; } else clearflag = 0, putc('\n', shout); } else putc('\n', shout); + listshown = (clearflag ? 1 : -1); return printed; @@ -1858,9 +1845,8 @@ iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, nicezputs(m->str, shout); len = niceztrlen(m->str); - if (isset(LISTTYPES)) { - if (buf) - putc(file_type(buf->st_mode), shout); + if (isset(LISTTYPES) && buf) { + putc(file_type(buf->st_mode), shout); len++; } } @@ -1876,7 +1862,7 @@ iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, int ilistmatches(Hookdef dummy, Chdata dat) { - calclist(); + calclist(0); if (!listdat.nlines) { showinglist = listshown = 0; @@ -1885,7 +1871,7 @@ ilistmatches(Hookdef dummy, Chdata dat) if (asklist()) return 0; - printlist(0, iprintm); + printlist(0, iprintm, 0); return 0; } @@ -1897,6 +1883,7 @@ int list_matches(Hookdef dummy, void *dummy2) { struct chdata dat; + int ret; #ifdef DEBUG /* Sanity check */ @@ -1909,18 +1896,20 @@ list_matches(Hookdef dummy, void *dummy2) dat.matches = amatches; dat.num = nmatches; dat.cur = NULL; - return runhookdef(COMPLISTMATCHESHOOK, (void *) &dat); + ret = runhookdef(COMPLISTMATCHESHOOK, (void *) &dat); + + return ret; } /* Invalidate the completion list. */ /**/ -int +mod_export int invalidate_list(void) { - if (showinglist == -2) - listmatches(); if (validlist) { + if (showinglist == -2) + zrefresh(); freematches(lastmatches); lastmatches = NULL; hasoldlist = 0; diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index aed3d9808..a844ee1ef 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -33,22 +33,32 @@ /* Help for `_display'. */ +/* Calculation state. */ + typedef struct cdisp *Cdisp; struct cdisp { - int pre, suf, colon; + int pre; /* prefix length */ + int suf; /* suffix length */ + int colon; /* number of strings with descriptions */ }; +/* Calculate longest prefix and suffix and count the strings with + * descriptions. */ + static void cdisp_calc(Cdisp disp, char **args) { char *cp; - int i; + int i, nbc; for (; *args; args++) { - if ((cp = strchr(*args, ':')) && cp[1]) { + for (nbc = 0, cp = *args; *cp && *cp != ':'; cp++) + if (*cp == '\\' && cp[1]) + cp++, nbc++; + if (*cp == ':' && cp[1]) { disp->colon++; - if ((i = cp - *args) > disp->pre) + if ((i = cp - *args - nbc) > disp->pre) disp->pre = i; if ((i = strlen(cp + 1)) > disp->suf) disp->suf = i; @@ -56,78 +66,29 @@ cdisp_calc(Cdisp disp, char **args) } } -static char ** -cdisp_build(Cdisp disp, char *sep, char **args) -{ - int sl = strlen(sep), pre = disp->pre, suf; - VARARR(char, buf, disp->pre + disp->suf + sl + 1); - char **ret, **rp, *cp; - - ret = (char **) zalloc((arrlen(args) + 1) * sizeof(char *)); - - memcpy(buf + pre, sep, sl); - suf = pre + sl; - - for (rp = ret; *args; args++) { - if ((cp = strchr(*args, ':')) && cp[1]) { - memset(buf, ' ', pre); - memcpy(buf, *args, (cp - *args)); - strcpy(buf + suf, cp + 1); - *rp++ = ztrdup(buf); - } else { - if (cp) - *cp = '\0'; - *rp++ = ztrdup(*args); - if (cp) - *cp = ':'; - } - } - *rp = NULL; - - return ret; -} - -/**/ -static int -bin_compdisplay(char *nam, char **args, char *ops, int func) -{ - struct cdisp disp; - - if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); - return 1; - } - disp.pre = disp.suf = disp.colon = 0; - - cdisp_calc(&disp, args + 2); - setaparam(args[0], cdisp_build(&disp, args[1], args + 2)); - - return !disp.colon; -} - /* Help fuer `_describe'. */ typedef struct cdset *Cdset; struct cdstate { - int showd; - char *sep; - Cdset sets; - struct cdisp disp; + int showd; /* != 0 if descriptions should be shown */ + char *sep; /* the separator string */ + Cdset sets; /* the sets of matches */ + struct cdisp disp; /* used to calculate the alignment */ }; struct cdset { - Cdset next; - char **opts; - char **strs; - char **matches; + Cdset next; /* guess what */ + char **opts; /* the compadd-options */ + char **strs; /* the display-strings */ + char **matches; /* the matches (or NULL) */ }; static struct cdstate cd_state; static int cd_parsed = 0; static void -free_cdsets(Cdset p) +freecdsets(Cdset p) { Cdset n; @@ -143,6 +104,8 @@ free_cdsets(Cdset p) } } +/* Initialisation. Store and calculate the string and matches and so on. */ + static int cd_init(char *nam, char *sep, char **args, int disp) { @@ -151,7 +114,7 @@ cd_init(char *nam, char *sep, char **args, int disp) if (cd_parsed) { zsfree(cd_state.sep); - free_cdsets(cd_state.sets); + freecdsets(cd_state.sets); } setp = &(cd_state.sets); cd_state.sep = ztrdup(sep); @@ -164,24 +127,20 @@ cd_init(char *nam, char *sep, char **args, int disp) setp = &(set->next); if (!(ap = get_user_var(*args))) { - zerrnam(nam, "invalid argument: %s", *args, 0); + zwarnnam(nam, "invalid argument: %s", *args, 0); return 1; } - PERMALLOC { - set->strs = arrdup(ap); - } LASTALLOC; + set->strs = zarrdup(ap); if (disp) cdisp_calc(&(cd_state.disp), set->strs); if (*++args && **args != '-') { if (!(ap = get_user_var(*args))) { - zerrnam(nam, "invalid argument: %s", *args, 0); + zwarnnam(nam, "invalid argument: %s", *args, 0); return 1; } - PERMALLOC { - set->matches = arrdup(ap); - } LASTALLOC; + set->matches = zarrdup(ap); args++; } for (ap = args; *args && @@ -190,15 +149,15 @@ cd_init(char *nam, char *sep, char **args, int disp) tmp = *args; *args = NULL; - PERMALLOC { - set->opts = arrdup(ap); - } LASTALLOC; + set->opts = zarrdup(ap); if ((*args = tmp)) args++; } return 0; } +/* Get the next set. */ + static int cd_get(char **params) { @@ -206,15 +165,21 @@ cd_get(char **params) if ((set = cd_state.sets)) { char **sd, **sdp, **md, **mdp, **ss, **ssp, **ms, **msp; - char **p, **mp, *cp; + char **p, **mp, *cp, *copy, *cpp, oldc; int dl = 1, sl = 1, sepl = strlen(cd_state.sep); int pre = cd_state.disp.pre, suf = cd_state.disp.suf; VARARR(char, buf, pre + suf + sepl + 1); for (p = set->strs; *p; p++) - if (cd_state.showd && (cp = strchr(*p, ':')) && cp[1]) - dl++; - else + if (cd_state.showd) { + for (cp = *p; *cp && *cp != ':'; cp++) + if (*cp == '\\' && cp[1]) + cp++; + if (*cp == ':' && cp[1]) + dl++; + else + sl++; + } else sl++; sd = (char **) zalloc(dl * sizeof(char *)); @@ -226,41 +191,44 @@ cd_get(char **params) memcpy(buf + pre, cd_state.sep, sepl); suf = pre + sepl; } + + /* Build the aligned display strings. */ + for (sdp = sd, ssp = ss, mdp = md, msp = ms, p = set->strs, mp = set->matches; *p; p++) { - if ((cp = strchr(*p, ':')) && cp[1] && cd_state.showd) { + copy = dupstring(*p); + for (cp = cpp = copy; *cp && *cp != ':'; cp++) { + if (*cp == '\\' && cp[1]) + cp++; + *cpp++ = *cp; + } + oldc = *cpp; + *cpp = '\0'; + if (((cpp == cp && oldc == ':') || *cp == ':') && cp[1] && + cd_state.showd) { memset(buf, ' ', pre); - memcpy(buf, *p, (cp - *p)); + memcpy(buf, copy, (cpp - copy)); strcpy(buf + suf, cp + 1); *sdp++ = ztrdup(buf); if (mp) { *mdp++ = ztrdup(*mp); if (*mp) mp++; - } else { - *cp = '\0'; - *mdp++ = ztrdup(*p); - *cp = ':'; - } + } else + *mdp++ = ztrdup(copy); } else { - if (cp) - *cp = '\0'; - *ssp++ = ztrdup(*p); + *ssp++ = ztrdup(copy); if (mp) { *msp++ = ztrdup(*mp); if (*mp) mp++; } else - *msp++ = ztrdup(*p); - if (cp) - *cp = ':'; + *msp++ = ztrdup(copy); } } *sdp = *ssp = *mdp = *msp = NULL; - PERMALLOC { - p = arrdup(set->opts); - } LASTALLOC; + p = zarrdup(set->opts); setaparam(params[0], p); setaparam(params[1], sd); @@ -270,7 +238,7 @@ cd_get(char **params) cd_state.sets = set->next; set->next = NULL; - free_cdsets(set); + freecdsets(set); return 0; } @@ -282,34 +250,36 @@ static int bin_compdescribe(char *nam, char **args, char *ops, int func) { if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); + zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; } if (!args[0][0] || !args[0][1] || args[0][2]) { - zerrnam(nam, "invalid argument: %s", args[0], 0); + zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } switch (args[0][1]) { case 'i': + cd_parsed = 1; + return cd_init(nam, "", args + 1, 0); case 'I': cd_parsed = 1; - return cd_init(nam, args[1], args + 2, (args[0][1] == 'I')); + return cd_init(nam, args[1], args + 2, 1); case 'g': if (cd_parsed) { int n = arrlen(args); if (n != 6) { - zerrnam(nam, (n < 6 ? "not enough arguments" : + zwarnnam(nam, (n < 6 ? "not enough arguments" : "too many arguments"), NULL, 0); return 1; } return cd_get(args + 1); } else { - zerrnam(nam, "no parsed state", NULL, 0); + zwarnnam(nam, "no parsed state", NULL, 0); return 1; } } - zerrnam(nam, "invalid option: %s", args[0], 0); + zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; } @@ -319,29 +289,34 @@ typedef struct cadef *Cadef; typedef struct caopt *Caopt; typedef struct caarg *Caarg; +/* Cache for a set of _arguments-definitions. */ + struct cadef { - Cadef next; - Caopt opts; - int nopts, ndopts, nodopts; - Caarg args; - Caarg rest; - char **defs; - int ndefs; - int lastt; - Caopt *single; - char *match; - int argsactive; + Cadef next; /* next in cache */ + Caopt opts; /* the options */ + int nopts, ndopts, nodopts; /* number of options/direct/optional direct */ + Caarg args; /* the normal arguments */ + Caarg rest; /* the rest-argument */ + char **defs; /* the original strings */ + int ndefs; /* number of ... */ + int lastt; /* last time this was used */ + Caopt *single; /* array of single-letter options */ + char *match; /* -M spec to use */ + int argsactive; /* if arguments are still allowed */ + /* used while parsing a command line */ }; +/* Description for an option. */ + struct caopt { Caopt next; - char *name; - char *descr; - char **xor; - int type; - Caarg args; - int active; - int num; + char *name; /* option name */ + char *descr; /* the description */ + char **xor; /* if this, then not ... */ + int type; /* type, CAO_* */ + Caarg args; /* option arguments */ + int active; /* still allowed on command line */ + int num; /* it's the num'th option */ }; #define CAO_NEXT 1 @@ -349,13 +324,18 @@ struct caopt { #define CAO_ODIRECT 3 #define CAO_EQUAL 4 +/* Description for an argument */ + struct caarg { Caarg next; - char *descr; - char *action; - int type; - char *end; - int num; + char *descr; /* description */ + char **xor; /* if this, then not ... */ + char *action; /* what to do for it */ + int type; /* CAA_* below */ + char *end; /* end-pattern for :::... */ + char *opt; /* option name if for an option */ + int num; /* it's the num'th argument */ + int active; /* still allowed on command line */ }; #define CAA_NORMAL 1 @@ -364,9 +344,13 @@ struct caarg { #define CAA_RARGS 4 #define CAA_RREST 5 +/* The cache of parsed descriptons. */ + #define MAX_CACACHE 8 static Cadef cadef_cache[MAX_CACACHE]; +/* Compare two arrays of strings for equality. */ + static int arrcmp(char **a, char **b) { @@ -383,45 +367,54 @@ arrcmp(char **a, char **b) } } +/* Memory stuff. Obviously. */ + static void -free_caargs(Caarg a) +freecaargs(Caarg a) { Caarg n; for (; a; a = n) { n = a->next; zsfree(a->descr); + if (a->xor) + freearray(a->xor); zsfree(a->action); zsfree(a->end); + zsfree(a->opt); zfree(a, sizeof(*a)); } } static void -free_cadef(Cadef d) +freecadef(Cadef d) { if (d) { Caopt p, n; zsfree(d->match); - freearray(d->defs); + if (d->defs) + freearray(d->defs); for (p = d->opts; p; p = n) { n = p->next; zsfree(p->name); zsfree(p->descr); - freearray(p->xor); - free_caargs(p->args); + if (p->xor) + freearray(p->xor); + freecaargs(p->args); zfree(p, sizeof(*p)); } - free_caargs(d->args); - free_caargs(d->rest); + freecaargs(d->args); + freecaargs(d->rest); if (d->single) zfree(d->single, 256 * sizeof(Caopt)); zfree(d, sizeof(*d)); } } +/* Remove backslashes before colons. */ + static char * rembslashcolon(char *s) { @@ -439,16 +432,41 @@ rembslashcolon(char *s) return r; } +/* Add backslashes before colons. */ + +static char * +bslashcolon(char *s) +{ + char *p, *r; + + r = p = zhalloc((2 * strlen(s)) + 1); + + while (*s) { + if (*s == ':') + *p++ = '\\'; + *p++ = *s++; + } + *p = '\0'; + + return r; +} + +/* Parse an argument definition. */ + static Caarg -parse_caarg(int mult, int type, int num, char **def) +parse_caarg(int mult, int type, int num, char *oname, char **def) { Caarg ret = (Caarg) zalloc(sizeof(*ret)); char *p = *def, *d, sav; ret->next = NULL; ret->descr = ret->action = ret->end = NULL; + ret->xor = NULL; ret->num = num; ret->type = type; + ret->opt = ztrdup(oname); + + /* Get the description. */ for (d = p; *p && *p != ':'; p++) if (*p == '\\' && p[1]) @@ -456,6 +474,9 @@ parse_caarg(int mult, int type, int num, char **def) sav = *p; *p = '\0'; ret->descr = ztrdup(rembslashcolon(d)); + + /* Get the action if there is one. */ + if (sav) { if (mult) { for (d = ++p; *p && *p != ':'; p++) @@ -474,6 +495,8 @@ parse_caarg(int mult, int type, int num, char **def) return ret; } +/* Parse an array of definitions. */ + static Cadef parse_cadef(char *nam, char **args) { @@ -485,6 +508,8 @@ parse_cadef(char *nam, char **args) nopts = ndopts = nodopts = 0; + /* First string is the auto-description definition. */ + for (p = args[0]; *p && (p[0] != '%' || p[1] != 'd'); p++); if (*p) { @@ -495,6 +520,8 @@ parse_cadef(char *nam, char **args) } else adpre = adsuf = NULL; + /* Now get the -s and -M options. */ + args++; while ((p = *args)) { if (!strcmp(p, "-s")) @@ -515,26 +542,30 @@ parse_cadef(char *nam, char **args) if (!*args) return NULL; - PERMALLOC { - ret = (Cadef) zalloc(sizeof(*ret)); - ret->next = NULL; - ret->opts = NULL; - ret->args = ret->rest = NULL; - ret->defs = arrdup(oargs); - ret->ndefs = arrlen(oargs); - ret->lastt = time(0); - if (single) { - ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); - memset(ret->single, 0, 256 * sizeof(Caopt)); - } else - ret->single = NULL; - ret->match = ztrdup(match); - } LASTALLOC; + /* Looks good. Optimistically allocate the cadef structure. */ + + ret = (Cadef) zalloc(sizeof(*ret)); + ret->next = NULL; + ret->opts = NULL; + ret->args = ret->rest = NULL; + ret->defs = zarrdup(oargs); + ret->ndefs = arrlen(oargs); + ret->lastt = time(0); + if (single) { + ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); + memset(ret->single, 0, 256 * sizeof(Caopt)); + } else + ret->single = NULL; + ret->match = ztrdup(match); + + /* Get the definitions. */ for (optp = &(ret->opts); *args; args++) { p = dupstring(*args); xnum = 0; if (*p == '(') { + /* There is a xor list, get it. */ + LinkList list = newlinklist(); LinkNode node; char **xp, sav; @@ -555,9 +586,10 @@ parse_cadef(char *nam, char **args) xnum++; *p = sav; } + /* Oops, end-of-string. */ if (*p != ')') { - free_cadef(ret); - zerrnam(nam, "invalid argument: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } xor = (char **) zalloc((xnum + 2) * sizeof(char *)); @@ -568,9 +600,10 @@ parse_cadef(char *nam, char **args) p++; } else xor = NULL; - + if (*p == '-' || *p == '+' || (*p == '*' && (p[1] == '-' || p[1] == '+'))) { + /* It's an option. */ Caopt opt; Caarg oargs = NULL; int multi, otype = CAO_NEXT, again = 0; @@ -578,25 +611,39 @@ parse_cadef(char *nam, char **args) rec: + /* Allowed more than once? */ if ((multi = (*p == '*'))) p++; - if ((p[0] == '-' && p[1] == '+') || - (p[0] == '+' && p[1] == '-')) { + if (((p[0] == '-' && p[1] == '+') || + (p[0] == '+' && p[1] == '-')) && + p[2] && p[2] != ':' && p[2] != '[' && + p[2] != '=' && p[2] != '-' && p[2] != '+') { + /* It's a -+ or +- definition. We just execute the whole + * stuff twice for such things. */ name = ++p; *p = (again ? '-' : '+'); again = 1 - again; } else { name = p; + /* If it's a long option skip over the first `-'. */ if (p[0] == '-' && p[1] == '-') p++; } + if (!p[1]) { + freecadef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); + return NULL; + } + + /* Skip over the name. */ for (p++; *p && *p != ':' && *p != '[' && ((*p != '-' && *p != '+' && *p != '=') || (p[1] != ':' && p[1] != '[')); p++) if (*p == '\\' && p[1]) p++; + /* The character after the option name specifies the type. */ c = *p; *p = '\0'; if (c == '-') { @@ -609,14 +656,15 @@ parse_cadef(char *nam, char **args) otype = CAO_EQUAL; c = *++p; } + /* Get the optional description, if any. */ if (c == '[') { for (descr = ++p; *p && *p != ']'; p++) if (*p == '\\' && p[1]) p++; if (!*p) { - free_cadef(ret); - zerrnam(nam, "invalid option definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } *p++ = '\0'; @@ -625,10 +673,11 @@ parse_cadef(char *nam, char **args) descr = NULL; if (c && c != ':') { - free_cadef(ret); - zerrnam(nam, "invalid option definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } + /* Add the option name to the xor list if not `*-...'. */ if (!multi) { if (!xor) { xor = (char **) zalloc(2 * sizeof(char *)); @@ -637,27 +686,39 @@ parse_cadef(char *nam, char **args) xor[xnum] = ztrdup(name); } if (c == ':') { + /* There's at least one argument. */ + Caarg *oargp = &oargs; - int atype, rest; + int atype, rest, oanum = 1; char *end; + /* Loop over the arguments. */ + while (c == ':') { rest = 0; end = NULL; + /* Get the argument type. */ if (*++p == ':') { atype = CAA_OPT; p++; } else if (*p == '*') { if (*++p != ':') { - for (end = ++p; *p && *p != ':'; p++) + char sav; + + for (end = p++; *p && *p != ':'; p++) if (*p == '\\' && p[1]) p++; + sav = *p; + *p = '\0'; + end = dupstring(end); + tokenize(end); + *p = sav; } if (*p != ':') { - free_cadef(ret); - free_caargs(oargs); - zerrnam(nam, "invalid option definition: %s", + freecadef(ret); + freecaargs(oargs); + zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } @@ -672,54 +733,74 @@ parse_cadef(char *nam, char **args) rest = 1; } else atype = CAA_NORMAL; - *oargp = parse_caarg(!rest, atype, 0, &p); + + /* And the definition. */ + + *oargp = parse_caarg(!rest, atype, oanum++, name, &p); + if (end) + (*oargp)->end = ztrdup(end); oargp = &((*oargp)->next); if (rest) break; c = *p; } } - PERMALLOC { - *optp = opt = (Caopt) zalloc(sizeof(*opt)); - optp = &((*optp)->next); - - opt->next = NULL; - opt->name = ztrdup(name); - if (descr) - opt->descr = ztrdup(descr); - else if (adpre && oargs && !oargs->next) + /* Store the option definition. */ + + *optp = opt = (Caopt) zalloc(sizeof(*opt)); + optp = &((*optp)->next); + + opt->next = NULL; + opt->name = ztrdup(rembslashcolon(name)); + if (descr) + opt->descr = ztrdup(descr); + else if (adpre && oargs && !oargs->next) { + char *d; + + for (d = oargs->descr; *d; d++) + if (!iblank(*d)) + break; + + if (*d) opt->descr = tricat(adpre, oargs->descr, adsuf); else opt->descr = NULL; - opt->xor = xor; - opt->type = otype; - opt->args = oargs; - opt->num = nopts++; - } LASTALLOC; + } else + opt->descr = NULL; + opt->xor = xor; + opt->type = otype; + opt->args = oargs; + opt->num = nopts++; if (otype == CAO_DIRECT) ndopts++; else if (otype == CAO_ODIRECT || otype == CAO_EQUAL) nodopts++; + /* If this is for single-letter option we also store a + * pointer for the definition in the array for fast lookup. */ + if (single && name[1] && !name[2]) ret->single[STOUC(name[1])] = opt; if (again) { + /* Do it all again for `*-...'. */ p = dupstring(*args); goto rec; } } else if (*p == '*') { + /* It's a rest-argument definition. */ + int type = CAA_REST; if (*++p != ':') { - free_cadef(ret); - zerrnam(nam, "invalid rest argument definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid rest argument definition: %s", *args, 0); return NULL; } if (ret->rest) { - free_cadef(ret); - zerrnam(nam, "doubled rest argument definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "doubled rest argument definition: %s", *args, 0); return NULL; } if (*++p == ':') { @@ -729,40 +810,49 @@ parse_cadef(char *nam, char **args) } else type = CAA_RARGS; } - ret->rest = parse_caarg(0, type, -1, &p); + ret->rest = parse_caarg(0, type, -1, NULL, &p); + ret->rest->xor = xor; } else { + /* It's a normal argument definition. */ + int type = CAA_NORMAL; Caarg arg, tmp, pre; if (idigit(*p)) { + /* Argment number is given. */ int num = 0; while (*p && idigit(*p)) - num = (num * 10) + ((int) *p++); + num = (num * 10) + (((int) *p++) - '0'); anum = num + 1; } else + /* Default number. */ anum++; if (*p != ':') { - free_cadef(ret); - zerrnam(nam, "invalid argument: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } if (*++p == ':') { + /* Optional argument. */ type = CAA_OPT; p++; } - arg = parse_caarg(0, type, anum - 1, &p); + arg = parse_caarg(0, type, anum - 1, NULL, &p); + arg->xor = xor; + + /* Sort the new definition into the existing list. */ for (tmp = ret->args, pre = NULL; tmp && tmp->num < anum - 1; pre = tmp, tmp = tmp->next); if (tmp && tmp->num == anum - 1) { - free_cadef(ret); - free_caargs(arg); - zerrnam(nam, "doubled argument definition: %s", *args, 0); + freecadef(ret); + freecaargs(arg); + zwarnnam(nam, "doubled argument definition: %s", *args, 0); return NULL; } arg->next = tmp; @@ -779,13 +869,16 @@ parse_cadef(char *nam, char **args) return ret; } +/* Given an array of definitions, return the cadef for it. From the cache + * are newly built. */ + static Cadef get_cadef(char *nam, char **args) { Cadef *p, *min, new; int i, na = arrlen(args); - for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i--; p++) + for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i; p++, i--) if (*p && na == (*p)->ndefs && arrcmp(args, (*p)->defs)) { (*p)->lastt = time(0); @@ -795,26 +888,36 @@ get_cadef(char *nam, char **args) if (i) min = p; if ((new = parse_cadef(nam, args))) { - free_cadef(*min); + freecadef(*min); *min = new; } return new; } +/* Get the option used in a word from the line, if any. */ + static Caopt ca_get_opt(Cadef d, char *line, int full, char **end) { Caopt p; - if (full) { - for (p = d->opts; p; p = p->next) - if (p->active && !strcmp(p->name, line)) - return p; - } else { + /* The full string may be an option. */ + + for (p = d->opts; p; p = p->next) + if (p->active && !strcmp(p->name, line)) { + if (end) + *end = line + strlen(line); + + return p; + } + + if (!full) { + /* The string from the line probably only begins with an option. */ for (p = d->opts; p; p = p->next) - if (p->active && p->args && p->type != CAO_NEXT && - strpfx(p->name, line)) { + if (p->active && ((!p->args || p->type == CAO_NEXT) ? + !strcmp(p->name, line) : strpfx(p->name, line))) { if (end) { + /* Return a pointer to the end of the option. */ int l = strlen(p->name); if (p->type == CAO_EQUAL && line[l] == '=') @@ -828,12 +931,13 @@ ca_get_opt(Cadef d, char *line, int full, char **end) return NULL; } +/* Same as above, only for single-letter-style. */ + static Caopt ca_get_sopt(Cadef d, char *line, int full, char **end) { Caopt p; - - line++; + char pre = *line++; if (full) { for (p = NULL; *line; line++) @@ -844,7 +948,7 @@ ca_get_sopt(Cadef d, char *line, int full, char **end) } else { for (p = NULL; *line; line++) if ((p = d->single[STOUC(*line)]) && p->active && - p->args && p->type != CAO_NEXT) { + p->args && p->type != CAO_NEXT && p->name[0] == pre) { if (end) { line++; if (p->type == CAO_EQUAL && *line == '=') @@ -852,13 +956,18 @@ ca_get_sopt(Cadef d, char *line, int full, char **end) *end = line; } break; - } else if (!p || !p->active || (line[1] && p->args)) + } else if (!p || !p->active || (line[1] && p->args) || + p->name[0] != pre) return NULL; + if (p && end) + *end = line; return p; } return NULL; } +/* Return the n'th argument definition. */ + static Caarg ca_get_arg(Cadef d, int n) { @@ -868,14 +977,16 @@ ca_get_arg(Cadef d, int n) while (a && a->num < n) a = a->next; - if (a && a->num == n) + if (a && a->num == n && a->active) return a; - return d->rest; + return (d->rest && d->rest->active ? d->rest : NULL); } return NULL; } +/* Use a xor list, marking options as inactive. */ + static void ca_inactive(Cadef d, char **xor) { @@ -885,17 +996,32 @@ ca_inactive(Cadef d, char **xor) for (; *xor; xor++) { if (xor[0][0] == ':' && !xor[0][1]) d->argsactive = 0; - else if ((opt = ca_get_opt(d, *xor, 1, NULL))) + else if (xor[0][0] == '*' && !xor[0][1]) { + if (d->rest) + d->rest->active = 0; + } else if (xor[0][0] >= '0' && xor[0][0] <= '9') { + int n = atoi(xor[0]); + Caarg a = d->args; + + while (a && a->num < n) + a = a->next; + + if (a && a->num == n) + a->active = 0; + } else if ((opt = ca_get_opt(d, *xor, 1, NULL))) opt->active = 0; } } } +/* State when parsing a command line. */ + struct castate { Cadef d; + int nopts; Caarg def, ddef; Caopt curopt; - int opt, arg, argbeg, optbeg, nargbeg, restbeg; + int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos; int inopt, inrest, inarg, nth, doff, singles; LinkList args; LinkList *oargs; @@ -904,40 +1030,55 @@ struct castate { static struct castate ca_laststate; static int ca_parsed = 0, ca_alloced = 0; +/* Pars a command line. */ + static void ca_parse_line(Cadef d) { Caarg adef, ddef; - Caopt ptr; + Caopt ptr, wasopt; struct castate state; - char *line, *pe; + char *line, *pe, **argxor = NULL; int cur, doff; Patprog endpat = NULL; + /* Free old state. */ + if (ca_alloced) { - int i = ca_laststate.d->nopts; + int i = ca_laststate.nopts; LinkList *p = ca_laststate.oargs; freelinklist(ca_laststate.args, freestr); while (i--) if (*p++) freelinklist(p[-1], freestr); + + zfree(ca_laststate.oargs, ca_laststate.d->nopts * sizeof(LinkList)); } + /* Mark everything as active. */ + for (ptr = d->opts; ptr; ptr = ptr->next) ptr->active = 1; d->argsactive = 1; + if (d->rest) + d->rest->active = 1; + for (adef = d->args; adef; adef = adef->next) + adef->active = 1; + + /* Default values for the state. */ state.d = d; + state.nopts = d->nopts; state.def = state.ddef = NULL; state.curopt = NULL; state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.nth = state.inopt = state.inarg = state.opt = state.arg = 1; state.inrest = state.doff = state.singles = state.doff = 0; - PERMALLOC { - state.args = newlinklist(); - state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); - memset(state.oargs, 0, d->nopts * sizeof(LinkList)); - } LASTALLOC; + state.curpos = compcurrent; + state.args = znewlinklist(); + state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); + memset(state.oargs, 0, d->nopts * sizeof(LinkList)); + ca_alloced = 1; memcpy(&ca_laststate, &state, sizeof(state)); @@ -947,46 +1088,67 @@ ca_parse_line(Cadef d) return; } + /* Loop over the words from the line. */ + for (line = compwords[1], cur = 2, state.curopt = NULL, state.def = NULL; line; line = compwords[cur++]) { ddef = adef = NULL; doff = state.singles = 0; + + ca_inactive(d, argxor); + + /* We've a definition for an argument, skip to the next. */ + if (state.def) { state.arg = 0; - if (state.curopt) { - PERMALLOC { - addlinknode(state.oargs[state.curopt->num], ztrdup(line)); - } LASTALLOC; - } - state.opt = (state.def->type == CAA_OPT && line[0] && line[1]); + if (state.curopt) + zaddlinknode(state.oargs[state.curopt->num], ztrdup(line)); + + state.opt = (state.def->type == CAA_OPT); if (state.def->type == CAA_REST || state.def->type == CAA_RARGS || state.def->type == CAA_RREST) { if (state.def->end && pattry(endpat, line)) { state.def = NULL; state.curopt = NULL; + state.opt = state.arg = 1; continue; } } else if ((state.def = state.def->next)) state.argbeg = cur; - else + else { state.curopt = NULL; + state.opt = 1; + } } else { - state.opt = (line[0] && line[1]); - state.arg = 1; + state.opt = state.arg = 1; state.curopt = NULL; } + if (state.opt) + state.opt = (line[0] ? (line[1] ? 2 : 1) : 0); + pe = NULL; - if (state.opt && (state.curopt = ca_get_opt(d, line, 0, &pe))) { + wasopt = NULL; + + /* See if it's an option. */ + + if (state.opt == 2 && (state.curopt = ca_get_opt(d, line, 0, &pe)) && + (state.curopt->type != CAO_EQUAL || + compwords[cur] || pe[-1] == '=')) { + ddef = state.def = state.curopt->args; doff = pe - line; state.optbeg = state.argbeg = state.inopt = cur; - PERMALLOC { - state.oargs[state.curopt->num] = newlinklist(); - } LASTALLOC; + state.singles = (d->single && (!pe || !*pe) && + state.curopt->name[1] && !state.curopt->name[2]); + + state.oargs[state.curopt->num] = znewlinklist(); + ca_inactive(d, state.curopt->xor); + /* Collect the argument strings. Maybe. */ + if (state.def && (state.curopt->type == CAO_DIRECT || (state.curopt->type == CAO_ODIRECT && pe[0]) || @@ -996,27 +1158,32 @@ ca_parse_line(Cadef d) state.def->type != CAA_RARGS && state.def->type != CAA_RREST) state.def = state.def->next; - PERMALLOC { - addlinknode(state.oargs[state.curopt->num], ztrdup(pe)); - } LASTALLOC; + + zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe)); } - if (!state.def) + if (state.def) + state.opt = 0; + else { + if (!d->single || (state.curopt->name[1] && state.curopt->name[2])) + wasopt = state.curopt; state.curopt = NULL; - } else if (state.opt && d->single && + } + } else if (state.opt == 2 && d->single && (state.curopt = ca_get_sopt(d, line, 0, &pe))) { + /* Or maybe it's a single-letter option? */ + char *p; Caopt tmpopt; ddef = state.def = state.curopt->args; doff = pe - line; state.optbeg = state.argbeg = state.inopt = cur; - state.singles = !*pe; + state.singles = (!pe || !*pe); - for (p = line + 1; p <= pe; p++) { + for (p = line + 1; p < pe; p++) { if ((tmpopt = d->single[STOUC(*p)])) { - PERMALLOC { - state.oargs[tmpopt->num] = newlinklist(); - } LASTALLOC; + state.oargs[tmpopt->num] = znewlinklist(); + ca_inactive(d, tmpopt->xor); } } @@ -1029,31 +1196,40 @@ ca_parse_line(Cadef d) state.def->type != CAA_RARGS && state.def->type != CAA_RREST) state.def = state.def->next; - PERMALLOC { - addlinknode(state.oargs[state.curopt->num], ztrdup(pe)); - } LASTALLOC; + + zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe)); } - if (!state.def) + if (state.def) + state.opt = 0; + else state.curopt = NULL; } else if (state.arg) { - PERMALLOC { - addlinknode(state.args, ztrdup(line)); - } LASTALLOC; + /* Otherwise it's a normal argument. */ + if (state.inopt) { + state.inopt = 0; + state.nargbeg = cur - 1; + } if ((adef = state.def = ca_get_arg(d, state.nth)) && (state.def->type == CAA_RREST || state.def->type == CAA_RARGS)) { state.inrest = 0; - for (; line; line = compwords[cur++]) { - PERMALLOC { - addlinknode(state.args, ztrdup(line)); - } LASTALLOC; - } + state.opt = (cur == state.nargbeg + 1); + state.optbeg = state.nargbeg; + state.argbeg = cur - 1; + + for (; line; line = compwords[cur++]) + zaddlinknode(state.args, ztrdup(line)); + + memcpy(&ca_laststate, &state, sizeof(state)); + ca_laststate.ddef = NULL; + ca_laststate.doff = 0; break; } - if (state.inopt) { - state.inopt = 0; - state.nargbeg = cur - 1; - } + zaddlinknode(state.args, ztrdup(line)); + + if (state.def) + argxor = state.def->xor; + if (state.def && state.def->type != CAA_NORMAL && state.def->type != CAA_OPT && state.inarg) { state.restbeg = cur; @@ -1064,6 +1240,8 @@ ca_parse_line(Cadef d) state.nth++; state.def = NULL; } + /* Do the end-pattern test if needed. */ + if (state.def && state.curopt && (state.def->type == CAA_RREST || state.def->type == CAA_RARGS)) { if (state.def->end) @@ -1071,52 +1249,77 @@ ca_parse_line(Cadef d) else { LinkList l = state.oargs[state.curopt->num]; - for (; line; line = compwords[cur++]) { - PERMALLOC { - addlinknode(l, line); - } LASTALLOC; - } + if (cur < compcurrent) + memcpy(&ca_laststate, &state, sizeof(state)); + + for (; line; line = compwords[cur++]) + zaddlinknode(l, ztrdup(line)); + + ca_laststate.ddef = NULL; + ca_laststate.doff = 0; break; } - } + } else if (state.def && state.def->end) + endpat = patcompile(state.def->end, 0, NULL); + + /* Copy the state into the global one. */ + if (cur + 1 == compcurrent) { memcpy(&ca_laststate, &state, sizeof(state)); ca_laststate.ddef = NULL; ca_laststate.doff = 0; } else if (cur == compcurrent && !ca_laststate.def) { - if ((ca_laststate.def = ddef)) - ca_laststate.doff = doff; - else { + if ((ca_laststate.def = ddef)) { + ca_laststate.singles = state.singles; + if (state.curopt && state.curopt->type == CAO_NEXT) { + ca_laststate.ddef = ddef; + ca_laststate.def = NULL; + ca_laststate.opt = 1; + state.curopt->active = 1; + } else { + ca_laststate.doff = doff; + ca_laststate.opt = 0; + } + } else { ca_laststate.def = adef; ca_laststate.ddef = NULL; - ca_laststate.argbeg = state.nargbeg; - ca_laststate.optbeg = state.restbeg; + ca_laststate.optbeg = state.nargbeg; + ca_laststate.argbeg = state.restbeg; ca_laststate.singles = state.singles; + if (wasopt) + wasopt->active = 1; } } } } +/* Build a colon-list from a list. */ + static char * ca_colonlist(LinkList l) { if (l) { LinkNode n; - int len = 1; + int len = 0; char *p, *ret, *q; - for (n = firstnode(l); n; incnode(n)) + for (n = firstnode(l); n; incnode(n)) { + len++; for (p = (char *) getdata(n); *p; p++) len += (*p == ':' ? 2 : 1); - + } ret = q = (char *) zalloc(len); - for (n = firstnode(l); n; incnode(n)) + for (n = firstnode(l); n;) { for (p = (char *) getdata(n); *p; p++) { if (*p == ':') *q++ = '\\'; *q++ = *p; } + incnode(n); + if (n) + *q++ = ':'; + } *q = '\0'; return ret; @@ -1130,36 +1333,37 @@ bin_comparguments(char *nam, char **args, char *ops, int func) int min, max, n; if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); + zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; } if (args[0][0] != '-' || !args[0][1] || args[0][2]) { - zerrnam(nam, "invalid argument: %s", args[0], 0); + zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } if (args[0][1] != 'i' && !ca_parsed) { - zerrnam(nam, "no parsed state", NULL, 0); + zwarnnam(nam, "no parsed state", NULL, 0); return 1; } switch (args[0][1]) { case 'i': min = 2; max = -1; break; case 'D': min = 2; max = 2; break; + case 'C': min = 1; max = 1; break; case 'O': min = 4; max = 4; break; - case 'L': min = 3; max = 3; break; + case 'L': min = 3; max = 4; break; case 's': min = 1; max = 1; break; case 'M': min = 1; max = 1; break; case 'a': min = 0; max = 0; break; case 'W': min = 2; max = 2; break; default: - zerrnam(nam, "invalid option: %s", args[0], 0); + zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; } n = arrlen(args) - 1; if (n < min) { - zerrnam(nam, "not enough arguments", NULL, 0); + zwarnnam(nam, "not enough arguments", NULL, 0); return 1; } else if (max >= 0 && n > max) { - zerrnam(nam, "too many arguments", NULL, 0); + zwarnnam(nam, "too many arguments", NULL, 0); return 1; } switch (args[0][1]) { @@ -1189,19 +1393,45 @@ bin_comparguments(char *nam, char **args, char *ops, int func) setsparam(args[1], ztrdup(arg->descr)); setsparam(args[2], ztrdup(arg->action)); - ignore_prefix(ca_laststate.doff); + if (ca_laststate.doff > 0) + ignore_prefix(ca_laststate.doff); if (arg->type == CAA_RARGS) - restrict_range(ca_laststate.argbeg - 1, + restrict_range(ca_laststate.optbeg, arrlen(compwords) - 1); else if (arg->type == CAA_RREST) - restrict_range(ca_laststate.optbeg - 1, + restrict_range(ca_laststate.argbeg, arrlen(compwords) - 1); return 0; } return 1; } + case 'C': + { + Caarg arg = ca_laststate.def; + + if (arg) { + char buf[20]; + + if (arg->num > 0) + sprintf(buf, "%d", arg->num); + else + strcpy(buf, "rest"); + + setsparam(args[1], (arg->opt ? tricat(arg->opt, "-", buf) : + tricat("argument-", buf, ""))); + return 0; + } + return 1; + } case 'O': - if (ca_laststate.opt) { + if ((ca_laststate.opt || (ca_laststate.doff && ca_laststate.def) || + (ca_laststate.def && + (ca_laststate.def->type == CAA_OPT || + ca_laststate.def->type >= CAA_RARGS))) && + (!ca_laststate.def || ca_laststate.def->type < CAA_RARGS || + (ca_laststate.def->type == CAA_RARGS ? + (ca_laststate.curpos == ca_laststate.argbeg + 1) : + (compcurrent == 1)))) { LinkList next = newlinklist(); LinkList direct = newlinklist(); LinkList odirect = newlinklist(); @@ -1218,14 +1448,15 @@ bin_comparguments(char *nam, char **args, char *ops, int func) default: l = equal; break; } if (p->descr) { - int len = strlen(p->name) + strlen(p->descr) + 2; + char *n = bslashcolon(p->name); + int len = strlen(n) + strlen(p->descr) + 2; str = (char *) zhalloc(len); - strcpy(str, p->name); + strcpy(str, n); strcat(str, ":"); strcat(str, p->descr); } else - str = p->name; + str = bslashcolon(p->name); addlinknode(l, str); } } @@ -1235,8 +1466,8 @@ bin_comparguments(char *nam, char **args, char *ops, int func) set_list_array(args[4], equal); return 0; - } else - return 1; + } + return 1; case 'L': { Caopt opt = ca_get_opt(ca_laststate.d, args[1], 1, NULL); @@ -1245,12 +1476,16 @@ bin_comparguments(char *nam, char **args, char *ops, int func) setsparam(args[2], ztrdup(opt->args->descr)); setsparam(args[3], ztrdup(opt->args->action)); + if (args[4]) + setsparam(args[4], tricat(opt->name, "-1", "")); + return 0; } return 1; } case 's': - if (ca_laststate.d->single && ca_laststate.singles) { + if (ca_laststate.d->single && ca_laststate.singles && + ca_laststate.opt) { setsparam(args[1], ztrdup(ca_laststate.ddef ? (ca_laststate.ddef->type == CAO_DIRECT ? @@ -1258,8 +1493,8 @@ bin_comparguments(char *nam, char **args, char *ops, int func) (ca_laststate.ddef->type == CAO_EQUAL ? "equal" : "next")) : "")); return 0; - } else - return 1; + } + return 1; case 'M': setsparam(args[1], ztrdup(ca_laststate.d->match)); return 0; @@ -1310,67 +1545,79 @@ bin_comparguments(char *nam, char **args, char *ops, int func) typedef struct cvdef *Cvdef; typedef struct cvval *Cvval; +/* Definitions for _values. */ + struct cvdef { - char *descr; - int hassep; - char sep; - Cvdef next; - Cvval vals; - char **defs; - int ndefs; - int lastt; + char *descr; /* global description */ + int hassep; /* multiple values allowed */ + char sep; /* separator character */ + Cvdef next; /* next in cache */ + Cvval vals; /* value definitions */ + char **defs; /* original strings */ + int ndefs; /* number of ... */ + int lastt; /* last time used */ }; +/* One value definition. */ + struct cvval { Cvval next; - char *name; - char *descr; - char **xor; - int type; - Caarg arg; - int active; + char *name; /* value name */ + char *descr; /* description */ + char **xor; /* xor-list */ + int type; /* CVV_* below */ + Caarg arg; /* argument definition */ + int active; /* still allowed */ }; #define CVV_NOARG 0 #define CVV_ARG 1 #define CVV_OPT 2 +/* Cache. */ + #define MAX_CVCACHE 8 static Cvdef cvdef_cache[MAX_CVCACHE]; +/* Memory stuff. */ + static void -free_cvdef(Cvdef d) +freecvdef(Cvdef d) { if (d) { Cvval p, n; zsfree(d->descr); - freearray(d->defs); + if (d->defs) + freearray(d->defs); for (p = d->vals; p; p = n) { n = p->next; zsfree(p->name); zsfree(p->descr); - freearray(p->xor); - free_caargs(p->arg); + if (p->xor) + freearray(p->xor); + freecaargs(p->arg); zfree(p, sizeof(*p)); } zfree(d, sizeof(*d)); } } +/* Parse option definitions. */ + static Cvdef parse_cvdef(char *nam, char **args) { Cvdef ret; Cvval val, *valp; Caarg arg; - char **oargs = args, sep, *name, *descr, *p, *q, **xor, c; - int xnum, multi, vtype, hassep; + char **oargs = args, sep = '\0', *name, *descr, *p, *q, **xor, c; + int xnum, multi, vtype, hassep = 0; if (args[0][0] == '-' && args[0][1] == 's' && !args[0][2]) { if (args[1][0] && args[1][1]) { - zerrnam(nam, "invalid separator: %s", args[1], 0); + zwarnnam(nam, "invalid separator: %s", args[1], 0); return NULL; } hassep = 1; @@ -1378,26 +1625,26 @@ parse_cvdef(char *nam, char **args) args += 2; } if (!args[0] || !args[1]) { - zerrnam(nam, "not enough arguments", NULL, 0); + zwarnnam(nam, "not enough arguments", NULL, 0); return NULL; } descr = *args++; - PERMALLOC { - ret = (Cvdef) zalloc(sizeof(*ret)); - ret->descr = ztrdup(descr); - ret->hassep = hassep; - ret->sep = sep; - ret->next = NULL; - ret->vals = NULL; - ret->defs = arrdup(oargs); - ret->ndefs = arrlen(oargs); - ret->lastt = time(0); - } LASTALLOC; + ret = (Cvdef) zalloc(sizeof(*ret)); + ret->descr = ztrdup(descr); + ret->hassep = hassep; + ret->sep = sep; + ret->next = NULL; + ret->vals = NULL; + ret->defs = zarrdup(oargs); + ret->ndefs = arrlen(oargs); + ret->lastt = time(0); for (valp = &(ret->vals); *args; args++) { p = dupstring(*args); xnum = 0; + + /* xor list? */ if (*p == '(') { LinkList list = newlinklist(); LinkNode node; @@ -1420,8 +1667,8 @@ parse_cvdef(char *nam, char **args) *p = sav; } if (*p != ')') { - free_cvdef(ret); - zerrnam(nam, "invalid argument: %s", *args, 0); + freecvdef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } xor = (char **) zalloc((xnum + 2) * sizeof(char *)); @@ -1433,18 +1680,23 @@ parse_cvdef(char *nam, char **args) } else xor = NULL; + /* More than once allowed? */ if ((multi = (*p == '*'))) p++; + /* Skip option name. */ + for (name = p; *p && *p != ':' && *p != '['; p++) if (*p == '\\' && p[1]) p++; if (hassep && !sep && name + 1 != p) { - free_cvdef(ret); - zerrnam(nam, "no multi-letter values with empty separator allowed", NULL, 0); + freecvdef(ret); + zwarnnam(nam, "no multi-letter values with empty separator allowed", NULL, 0); return NULL; } + /* Optional description? */ + if ((c = *p) == '[') { *p = '\0'; for (descr = ++p; *p && *p != ']'; p++) @@ -1452,8 +1704,8 @@ parse_cvdef(char *nam, char **args) p++; if (!*p) { - free_cvdef(ret); - zerrnam(nam, "invalid value definition: %s", *args, 0); + freecvdef(ret); + zwarnnam(nam, "invalid value definition: %s", *args, 0); return NULL; } *p++ = '\0'; @@ -1463,8 +1715,8 @@ parse_cvdef(char *nam, char **args) descr = NULL; } if (c && c != ':') { - free_cvdef(ret); - zerrnam(nam, "invalid value definition: %s", *args, 0); + freecvdef(ret); + zwarnnam(nam, "invalid value definition: %s", *args, 0); return NULL; } if (!multi) { @@ -1474,10 +1726,12 @@ parse_cvdef(char *nam, char **args) } xor[xnum] = ztrdup(name); } + /* Get argument? */ + if (c == ':') { if (hassep && !sep) { - free_cvdef(ret); - zerrnam(nam, "no value with argument with empty separator allowed", NULL, 0); + freecvdef(ret); + zwarnnam(nam, "no value with argument with empty separator allowed", NULL, 0); return NULL; } if (*++p == ':') { @@ -1485,26 +1739,26 @@ parse_cvdef(char *nam, char **args) vtype = CVV_OPT; } else vtype = CVV_ARG; - arg = parse_caarg(0, 0, 0, &p); + arg = parse_caarg(0, 0, 0, name, &p); } else { vtype = CVV_NOARG; arg = NULL; } - PERMALLOC { - *valp = val = (Cvval) zalloc(sizeof(*val)); - valp = &((*valp)->next); - - val->next = NULL; - val->name = ztrdup(name); - val->descr = ztrdup(descr); - val->xor = xor; - val->type = vtype; - val->arg = arg; - } LASTALLOC; + *valp = val = (Cvval) zalloc(sizeof(*val)); + valp = &((*valp)->next); + + val->next = NULL; + val->name = ztrdup(name); + val->descr = ztrdup(descr); + val->xor = xor; + val->type = vtype; + val->arg = arg; } return ret; } +/* Get the definition from the cache or newly built. */ + static Cvdef get_cvdef(char *nam, char **args) { @@ -1521,12 +1775,14 @@ get_cvdef(char *nam, char **args) if (i) min = p; if ((new = parse_cvdef(nam, args))) { - free_cvdef(*min); + freecvdef(*min); *min = new; } return new; } +/* Get the definition for a value. */ + static Cvval cv_get_val(Cvdef d, char *name) { @@ -1539,6 +1795,8 @@ cv_get_val(Cvdef d, char *name) return NULL; } +/* Handle a xor list. */ + static void cv_inactive(Cvdef d, char **xor) { @@ -1551,6 +1809,8 @@ cv_inactive(Cvdef d, char **xor) } } +/* Parse state. */ + struct cvstate { Cvdef d; Caarg def; @@ -1561,6 +1821,8 @@ struct cvstate { static struct cvstate cv_laststate; static int cv_parsed = 0, cv_alloced = 0; +/* Parse the current word. */ + static void cv_parse_word(Cvdef d) { @@ -1577,9 +1839,8 @@ cv_parse_word(Cvdef d) state.d = d; state.def = NULL; state.val = NULL; - PERMALLOC { - state.vals = (LinkList) newlinklist(); - } LASTALLOC; + state.vals = (LinkList) znewlinklist(); + cv_alloced = 1; if (d->hassep) { @@ -1596,10 +1857,8 @@ cv_parse_word(Cvdef d) eq = ""; if ((ptr = cv_get_val(d, str))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(str)); - addlinknode(state.vals, ztrdup(eq)); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(str)); + zaddlinknode(state.vals, ztrdup(eq)); cv_inactive(d, ptr->xor); } @@ -1625,10 +1884,8 @@ cv_parse_word(Cvdef d) eq = ""; if ((ptr = cv_get_val(d, str))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(str)); - addlinknode(state.vals, ztrdup(eq)); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(str)); + zaddlinknode(state.vals, ztrdup(eq)); cv_inactive(d, ptr->xor); } @@ -1647,10 +1904,8 @@ cv_parse_word(Cvdef d) for (str = compprefix; *str; str++) { tmp[0] = *str; if ((ptr = cv_get_val(d, tmp))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(tmp)); - addlinknode(state.vals, ztrdup("")); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(tmp)); + zaddlinknode(state.vals, ztrdup("")); cv_inactive(d, ptr->xor); } @@ -1658,10 +1913,8 @@ cv_parse_word(Cvdef d) for (str = compsuffix; *str; str++) { tmp[0] = *str; if ((ptr = cv_get_val(d, tmp))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(tmp)); - addlinknode(state.vals, ztrdup("")); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(tmp)); + zaddlinknode(state.vals, ztrdup("")); cv_inactive(d, ptr->xor); } @@ -1696,35 +1949,36 @@ bin_compvalues(char *nam, char **args, char *ops, int func) int min, max, n; if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); + zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; } if (args[0][0] != '-' || !args[0][1] || args[0][2]) { - zerrnam(nam, "invalid argument: %s", args[0], 0); + zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } if (args[0][1] != 'i' && !cv_parsed) { - zerrnam(nam, "no parsed state", NULL, 0); + zwarnnam(nam, "no parsed state", NULL, 0); return 1; } switch (args[0][1]) { case 'i': min = 2; max = -1; break; case 'D': min = 2; max = 2; break; + case 'C': min = 1; max = 1; break; case 'V': min = 3; max = 3; break; case 's': min = 1; max = 1; break; case 'd': min = 1; max = 1; break; - case 'L': min = 3; max = 3; break; + case 'L': min = 3; max = 4; break; case 'v': min = 1; max = 1; break; default: - zerrnam(nam, "invalid option: %s", args[0], 0); + zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; } n = arrlen(args) - 1; if (n < min) { - zerrnam(nam, "not enough arguments", NULL, 0); + zwarnnam(nam, "not enough arguments", NULL, 0); return 1; } else if (max >= 0 && n > max) { - zerrnam(nam, "too many arguments", NULL, 0); + zwarnnam(nam, "too many arguments", NULL, 0); return 1; } switch (args[0][1]) { @@ -1758,6 +2012,17 @@ bin_compvalues(char *nam, char **args, char *ops, int func) } return 1; } + case 'C': + { + Caarg arg = cv_laststate.def; + + if (arg) { + setsparam(args[1], ztrdup(arg->opt)); + + return 0; + } + return 1; + } case 'V': { LinkList noarg = newlinklist(); @@ -1813,6 +2078,9 @@ bin_compvalues(char *nam, char **args, char *ops, int func) setsparam(args[2], val->arg->descr); setsparam(args[3], val->arg->action); + if (args[4]) + setsparam(args[4], ztrdup(val->name)); + return 0; } return 1; @@ -1838,37 +2106,508 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 1; } +static int +bin_compquote(char *nam, char **args, char *ops, int func) +{ + char *name; + struct value vbuf; + Value v; + + /* Anything to do? */ + + if (!compqstack || !*compqstack) + return 0; + + /* For all parameters given... */ + + while ((name = *args++)) { + name = dupstring(name); + if ((v = getvalue(&vbuf, &name, 0))) { + switch (PM_TYPE(v->pm->flags)) { + case PM_SCALAR: + { + char *val = getstrvalue(v); + + val = bslashquote(val, NULL, + (*compqstack == '\'' ? 1 : + (*compqstack == '"' ? 2 : 0))); + + setstrvalue(v, ztrdup(val)); + } + break; + case PM_ARRAY: + { + char **val = v->pm->gets.afn(v->pm); + char **new = (char **) zalloc((arrlen(val) + 1) * + sizeof(char *)); + char **p = new; + + for (; *val; val++, p++) + *p = ztrdup(bslashquote(*val, NULL, + (*compqstack == '\'' ? 1 : + (*compqstack == '"' ? 2 : + 0)))); + *p = NULL; + + setarrvalue(v, new); + } + break; + default: + zwarnnam(nam, "invalid parameter type: %s", args[-1], 0); + } + } else + zwarnnam(nam, "unknown parameter: %s", args[-1], 0); + } + return 0; +} + +/* Tags stuff. */ + +typedef struct ctags *Ctags; +typedef struct ctset *Ctset; + +/* A bunch of tag sets. */ + +struct ctags { + char **all; /* all tags offered */ + char *context; /* the current context */ + int init; /* not yet used */ + Ctset sets; /* the tag sets */ +}; + +/* A tag set. */ + +struct ctset { + Ctset next; + char **tags; /* the tags */ + char *tag; /* last tag checked for -A */ + char **ptr; /* ptr into tags for -A */ +}; + +/* Array of tag-set infos. Index is the locallevel. */ + +#define MAX_TAGS 256 +static Ctags comptags[MAX_TAGS]; + +/* locallevel at last comptags -i */ + +static int lasttaglevel; + +static void +freectset(Ctset s) +{ + Ctset n; + + while (s) { + n = s->next; + + if (s->tags) + freearray(s->tags); + zsfree(s->tag); + zfree(s, sizeof(*s)); + + s = n; + } +} + +static void +freectags(Ctags t) +{ + if (t) { + if (t->all) + freearray(t->all); + zsfree(t->context); + freectset(t->sets); + zfree(t, sizeof(*t)); + } +} + +/* Set the tags for the current local level. */ + +static void +settags(int level, char **tags) +{ + Ctags t; + + if (comptags[level]) + freectags(comptags[level]); + + comptags[level] = t = (Ctags) zalloc(sizeof(*t)); + + t->all = zarrdup(tags + 1); + t->context = ztrdup(*tags); + t->sets = NULL; + t->init = 1; +} + +/* Check if an array contains a string. */ + +static int +arrcontains(char **a, char *s, int colon) +{ + char *p, *q; + + while (*a) { + if (colon) { + for (p = s, q = *a++; *p && *q && *p != ':' && *q != ':'; p++, q++) + if (*p != *q) + break; + if ((!*p || *p == ':') && (!*q || *q == ':')) + return 1; + } else if (!strcmp(*a++, s)) + return 1; + } + return 0; +} + +static int +bin_comptags(char *nam, char **args, char *ops, int func) +{ + int min, max, n, level; + + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (args[0][0] != '-' || !args[0][1] || + (args[0][2] && (args[0][2] != '-' || args[0][3]))) { + zwarnnam(nam, "invalid argument: %s", args[0], 0); + return 1; + } + level = locallevel - (args[0][2] ? 1 : 0); + if (level >= MAX_TAGS) { + zwarnnam(nam, "nesting level too deep", NULL, 0); + return 1; + } + if (args[0][1] != 'i' && args[0][1] != 'I' && !comptags[level]) { + zwarnnam(nam, "no tags registered", NULL, 0); + return 1; + } + switch (args[0][1]) { + case 'i': min = 2; max = -1; break; + case 'C': min = 1; max = 1; break; + case 'T': min = 0; max = 0; break; + case 'N': min = 0; max = 0; break; + case 'R': min = 1; max = 1; break; + case 'S': min = 1; max = 1; break; + case 'A': min = 2; max = 2; break; + default: + zwarnnam(nam, "invalid option: %s", args[0], 0); + return 1; + } + n = arrlen(args) - 1; + if (n < min) { + zwarnnam(nam, "not enough arguments", NULL, 0); + return 1; + } else if (max >= 0 && n > max) { + zwarnnam(nam, "too many arguments", NULL, 0); + return 1; + } + switch (args[0][1]) { + case 'i': + settags(level, args + 1); + lasttaglevel = level; + break; + case 'C': + setsparam(args[1], ztrdup(comptags[level]->context)); + break; + case 'T': + return !comptags[level]->sets; + case 'N': + { + Ctset s; + + if (comptags[level]->init) + comptags[level]->init = 0; + else if ((s = comptags[level]->sets)) { + comptags[level]->sets = s->next; + s->next = NULL; + freectset(s); + } + return !comptags[level]->sets; + } + case 'R': + { + Ctset s; + + return !((s = comptags[level]->sets) && + arrcontains(s->tags, args[1], 1)); + } + case 'A': + { + Ctset s; + + if (comptags[level] && (s = comptags[level]->sets)) { + char **q, *v = NULL; + int l = strlen(args[1]); + + if (!s->tag || strcmp(s->tag, args[1])) { + zsfree(s->tag); + s->tag = ztrdup(args[1]); + s->ptr = s->tags; + } + for (q = s->ptr; *q; q++) { + if (strpfx(args[1], *q)) { + if (!(*q)[l]) { + v = *q; + break; + } else if ((*q)[l] == ':') { + v = (*q) + l + 1; + break; + } + } + } + if (!v) { + zsfree(s->tag); + s->tag = NULL; + return 1; + } + s->ptr = q + 1; + setsparam(args[2], ztrdup(*v == '-' ? dyncat(args[1], v) : v)); + return 0; + } + return 1; + } + case 'S': + if (comptags[level]->sets) { + char **ret; + + ret = zarrdup(comptags[level]->sets->tags); + setaparam(args[1], ret); + } else + return 1; + + break; + } + return 0; +} + +static int +bin_comptry(char *nam, char **args, char *ops, int func) +{ + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (!lasttaglevel || !comptags[lasttaglevel]) { + zwarnnam(nam, "no tags registered", NULL, 0); + return 1; + } + if (*args) { + if (!strcmp(*args, "-m")) { + char *s, *p, *q, *c, **all = comptags[lasttaglevel]->all; + LinkList list = newlinklist(); + LinkNode node; + int num = 0; + Ctset set; + + while ((s = *++args)) { + while (*s) { + while (*s && iblank(*s)) + s++; + for (p = q = s, c = NULL; *s && !iblank(*s); s++) { + if (!c && *s == ':') + c = p; + if (*s == '\\' && s[1]) + s++; + *p++ = *s; + } + if (*s) + s++; + *p = '\0'; + if (*q) { + char *qq = dupstring(q); + if (c) + *c = '\0'; + + tokenize(qq); + if (haswilds(qq)) { + Patprog prog; + + if ((prog = patcompile(qq, PAT_STATIC, NULL))) { + char **a, *n; + int l = (c ? strlen(c + 1) + 2 : 1), al; + + for (a = all; *a; a++) { + if (pattry(prog, *a)) { + n = (char *) zhalloc((al = strlen(*a)) + l); + strcpy(n, *a); + if (c) { + n[al] = ':'; + strcpy(n + al + 1, c + 1); + } + addlinknode(list, n); + num++; + } + } + } + } else if (arrcontains(all, q, 0)) { + for (set = comptags[lasttaglevel]->sets; set; + set = set->next) + if (arrcontains(set->tags, q, 0)) + break; + if (!set) { + addlinknode(list, q); + num++; + } + } + if (c) + *c = ':'; + } + } + if (num) { + char **a; + Ctset l; + + set = (Ctset) zalloc(sizeof(*set)); + + a = set->tags = (char **) zalloc((num + 1) * sizeof(char *)); + for (node = firstnode(list); node; incnode(node)) + *a++ = ztrdup((char *) getdata(node)); + + *a = NULL; + set->next = NULL; + set->ptr = NULL; + set->tag = NULL; + + if ((l = comptags[lasttaglevel]->sets)) { + while (l->next) + l = l->next; + + l->next = set; + } else + comptags[lasttaglevel]->sets = set; + } + } + } else { + char **p, **q, **all; + int sep = 0; + + if ((sep = !strcmp(*args, "-s"))) + args++; + + for (p = q = args, all = comptags[lasttaglevel]->all; *p; p++) + if (arrcontains(all, *p, 1)) { + Ctset s; + + for (s = comptags[lasttaglevel]->sets; s; s = s->next) + if (arrcontains(s->tags, *p, 0)) + break; + + if (!s) + *q++ = *p; + } + *q = NULL; + + if (*args) { + char *dummy[2]; + + do { + Ctset s = (Ctset) zalloc(sizeof(*s)), l; + + if (sep) { + dummy[0] = *args++; + dummy[1] = NULL; + s->tags = zarrdup(dummy); + } else + s->tags = zarrdup(args); + s->next = NULL; + s->ptr = NULL; + s->tag = NULL; + + if ((l = comptags[lasttaglevel]->sets)) { + while (l->next) + l = l->next; + + l->next = s; + } else + comptags[lasttaglevel]->sets = s; + } while (sep && *args); + } + } + } + return 0; +} + +static char * +fmtstr(char *str, char c, char *repl) +{ + int len, num, rlen; + char *s, *ret, *rp; + + len = strlen(str); + rlen = strlen(repl); + + for (num = 0, s = str; *s; s++) + if (*s == '%' && s[1] == c) + num++, s++; + + ret = (char *) zhalloc((num * (rlen - 2)) + len + 1); + + for (s = str, rp = ret; *s; s++) { + if (*s == '%' && s[1] == c) { + strcpy(rp, repl); + rp += rlen; + s++; + } else + *rp++ = *s; + } + *rp = '\0'; + + return ret; +} + +static int +bin_compfmt(char *nam, char **args, char *ops, int func) +{ + char *param = args[0], *str = args[1]; + + for (args += 2; *args; args++) { + if (args[0][1] != ':') { + zwarnnam(nam, "invalid argument `%s'", args[0], 0); + return 1; + } + str = fmtstr(str, **args, *args + 2); + } + setsparam(param, ztrdup(str)); + return 0; +} static struct builtin bintab[] = { - BUILTIN("compdisplay", 0, bin_compdisplay, 2, -1, 0, NULL, NULL), BUILTIN("compdescribe", 0, bin_compdescribe, 3, -1, 0, NULL, NULL), BUILTIN("comparguments", 0, bin_comparguments, 1, -1, 0, NULL, NULL), BUILTIN("compvalues", 0, bin_compvalues, 1, -1, 0, NULL, NULL), + BUILTIN("compquote", 0, bin_compquote, 1, -1, 0, NULL, NULL), + BUILTIN("comptags", 0, bin_comptags, 1, -1, 0, NULL, NULL), + BUILTIN("comptry", 0, bin_comptry, 0, -1, 0, NULL, NULL), + BUILTIN("compfmt", 0, bin_compfmt, 2, -1, 0, NULL, NULL), }; /**/ int -setup_computil(Module m) +setup_(Module m) { memset(cadef_cache, 0, sizeof(cadef_cache)); memset(cvdef_cache, 0, sizeof(cvdef_cache)); + memset(comptags, 0, sizeof(comptags)); + + lasttaglevel = 0; + return 0; } /**/ int -boot_computil(Module m) +boot_(Module m) { return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); } -#ifdef MODULE - /**/ int -cleanup_computil(Module m) +cleanup_(Module m) { deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); return 0; @@ -1876,16 +2615,17 @@ cleanup_computil(Module m) /**/ int -finish_computil(Module m) +finish_(Module m) { int i; for (i = 0; i < MAX_CACACHE; i++) - free_cadef(cadef_cache[i]); + freecadef(cadef_cache[i]); for (i = 0; i < MAX_CVCACHE; i++) - free_cvdef(cvdef_cache[i]); + freecvdef(cvdef_cache[i]); + + for (i = 0; i < MAX_TAGS; i++) + freectags(comptags[i]); return 0; } - -#endif diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index 01862160e..33d36a18c 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -19,20 +19,21 @@ "backward-kill-line", backwardkillline, ZLE_KILL | ZLE_KEEPSUFFIX "backward-kill-word", backwardkillword, ZLE_KILL | ZLE_KEEPSUFFIX "backward-word", backwardword, 0 +"beep", handlefeep, 0 "beginning-of-buffer-or-history", beginningofbufferorhistory, 0 "beginning-of-history", beginningofhistory, 0 "beginning-of-line", beginningofline, 0 "beginning-of-line-hist", beginningoflinehist, 0 "capitalize-word", capitalizeword, 0 -"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"copy-prev-word", copyprevword, 0 +"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND +"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"copy-prev-word", copyprevword, ZLE_KEEPSUFFIX "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX "delete-char", deletechar, ZLE_KEEPSUFFIX -"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "delete-word", deleteword, ZLE_KEEPSUFFIX "describe-key-briefly", describekeybriefly, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "down-case-word", downcaseword, 0 "down-history", downhistory, 0 "down-line-or-history", downlineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL @@ -43,13 +44,14 @@ "end-of-history", endofhistory, 0 "end-of-line", endofline, 0 "end-of-line-hist", endoflinehist, 0 +"end-of-list", endoflist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "exchange-point-and-mark", exchangepointandmark, 0 "execute-last-named-cmd", NULL, 0 "execute-named-cmd", NULL, 0 "expand-cmd-path", expandcmdpath, 0 "expand-history", expandhistory, 0 -"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "expand-word", expandword, 0 "forward-char", forwardchar, 0 "forward-word", forwardword, 0 @@ -68,12 +70,12 @@ "kill-region", killregion, ZLE_KILL | ZLE_KEEPSUFFIX "kill-whole-line", killwholeline, ZLE_KILL | ZLE_KEEPSUFFIX "kill-word", killword, ZLE_KILL | ZLE_KEEPSUFFIX -"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_ISCOMP "list-expand", listexpand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "magic-space", magicspace, 0 -"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "overwrite-mode", overwritemode, 0 "pound-insert", poundinsert, 0 "push-input", pushinput, 0 @@ -83,19 +85,20 @@ "quote-line", quoteline, 0 "quote-region", quoteregion, 0 "redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"redo", redo, 0 -"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"redo", redo, ZLE_KEEPSUFFIX +"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "self-insert", selfinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX "self-insert-unmeta", selfinsertunmeta, ZLE_MENUCMP | ZLE_KEEPSUFFIX "send-break", sendbreak, 0 "set-mark-command", setmarkcommand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "spell-word", spellword, 0 +"set-local-history", setlocalhistory, 0 "transpose-chars", transposechars, 0 "transpose-words", transposewords, 0 "undefined-key", undefinedkey, 0 -"undo", undo, 0 -"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"undo", undo, ZLE_KEEPSUFFIX +"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "up-case-word", upcaseword, 0 "up-history", uphistory, 0 "up-line-or-history", uplineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL @@ -145,8 +148,8 @@ "vi-open-line-below", viopenlinebelow, 0 "vi-oper-swap-case", vioperswapcase, 0 "vi-pound-insert", vipoundinsert, 0 -"vi-put-after", viputafter, ZLE_YANK -"vi-put-before", viputbefore, ZLE_YANK +"vi-put-after", viputafter, ZLE_YANK | ZLE_KEEPSUFFIX +"vi-put-before", viputbefore, ZLE_YANK | ZLE_KEEPSUFFIX "vi-quoted-insert", viquotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX "vi-repeat-change", virepeatchange, 0 "vi-repeat-find", virepeatfind, 0 @@ -159,7 +162,7 @@ "vi-set-mark", visetmark, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "vi-substitute", visubstitute, 0 "vi-swap-case", viswapcase, 0 -"vi-undo-change", viundochange, 0 +"vi-undo-change", viundochange, ZLE_KEEPSUFFIX "vi-unindent", viunindent, 0 "vi-up-line-or-history", viuplineorhistory, ZLE_LINEMOVE "vi-yank", viyank, 0 @@ -168,5 +171,5 @@ "what-cursor-position", whatcursorposition, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "where-is", whereis, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "which-command", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"yank", yank, ZLE_YANK -"yank-pop", yankpop, ZLE_YANK +"yank", yank, ZLE_YANK | ZLE_KEEPSUFFIX +"yank-pop", yankpop, ZLE_YANK | ZLE_KEEPSUFFIX diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 8fe3e7f0b..51af32e0b 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -53,7 +53,7 @@ struct cutbuffer vibuf[35]; /**/ char *lastline; /**/ -int lastlinesz, lastll; +int lastlinesz, lastll, lastcs; /* size of line buffer */ @@ -73,7 +73,7 @@ sizeline(int sz) /* insert space for ct chars at cursor position */ /**/ -void +mod_export void spaceinline(int ct) { int i; @@ -105,7 +105,7 @@ shiftchars(int to, int cnt) } /**/ -void +mod_export void backkill(int ct, int dir) { int i = (cs -= ct); @@ -115,7 +115,7 @@ backkill(int ct, int dir) } /**/ -void +mod_export void forekill(int ct, int dir) { int i = cs; @@ -191,14 +191,14 @@ cut(int i, int ct, int dir) } /**/ -void +mod_export void backdel(int ct) { shiftchars(cs -= ct, ct); } /**/ -void +mod_export void foredel(int ct) { shiftchars(cs, ct); @@ -283,7 +283,7 @@ hstrnstr(char *haystack, int pos, char *needle, int len, int dir, int sens) * characters are read. Case is folded. */ /**/ -int +mod_export int getzlequery(void) { int c; @@ -409,19 +409,11 @@ showmsg(char const *msg) /* handle the error flag */ /**/ -void -feep(void) -{ - feepflag = 1; -} - -/**/ -void -handlefeep(void) +int +handlefeep(char **args) { - if(feepflag) - beep(); - feepflag = 0; + zbeep(); + return 0; } /***************/ @@ -446,6 +438,7 @@ initundo(void) curchange->del = curchange->ins = NULL; lastline = zalloc(lastlinesz = linesz); memcpy(lastline, line, lastll = ll); + lastcs = cs; } /**/ @@ -519,6 +512,8 @@ mkundoent(void) ch->next = NULL; ch->hist = histline; ch->off = pre; + ch->old_cs = lastcs; + ch->new_cs = cs; if(suf + pre == lastll) ch->del = NULL; else @@ -549,32 +544,36 @@ setlastline(void) if(lastlinesz != linesz) lastline = realloc(lastline, lastlinesz = linesz); memcpy(lastline, line, lastll = ll); + lastcs = cs; } /* move backwards through the change list */ /**/ -void -undo(void) +int +undo(char **args) { handleundo(); do { - if(!curchange->prev) { - feep(); - return; - } - unapplychange(curchange = curchange->prev); + if(!curchange->prev) + return 1; + if (unapplychange(curchange->prev)) + curchange = curchange->prev; + else + break; } while(curchange->flags & CH_PREV); setlastline(); + return 0; } /**/ -static void +static int unapplychange(struct change *ch) { if(ch->hist != histline) { - remember_edits(); - setline(zle_get_event(histline = ch->hist)); + zle_setline(quietgethist(ch->hist)); + cs = ch->new_cs; + return 0; } cs = ch->off; if(ch->ins) @@ -589,33 +588,37 @@ unapplychange(struct change *ch) else line[cs++] = STOUC(*c); } + cs = ch->old_cs; + return 1; } /* move forwards through the change list */ /**/ -void -redo(void) +int +redo(char **args) { handleundo(); do { - if(!curchange->next) { - feep(); - return; - } - applychange(curchange); - curchange = curchange->next; + if(!curchange->next) + return 1; + if (applychange(curchange)) + curchange = curchange->next; + else + break; } while(curchange->prev->flags & CH_NEXT); setlastline(); + return 0; } /**/ -static void +static int applychange(struct change *ch) { if(ch->hist != histline) { - remember_edits(); - setline(zle_get_event(histline = ch->hist)); + zle_setline(quietgethist(ch->hist)); + cs = ch->old_cs; + return 0; } cs = ch->off; if(ch->del) @@ -630,13 +633,15 @@ applychange(struct change *ch) else line[cs++] = STOUC(*c); } + cs = ch->new_cs; + return 1; } /* vi undo: toggle between the end of the undo list and the preceding point */ /**/ -void -viundochange(void) +int +viundochange(char **args) { handleundo(); if(curchange->next) { @@ -645,6 +650,7 @@ viundochange(void) curchange = curchange->next; } while(curchange->next); setlastline(); + return 0; } else - undo(); + return undo(args); } -- cgit 1.4.1