mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 06:00:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			3632 lines
		
	
	
	
		
			88 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3632 lines
		
	
	
	
		
			88 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * compcore.c - the complete module, completion core code
 | |
|  *
 | |
|  * This file is part of zsh, the Z shell.
 | |
|  *
 | |
|  * Copyright (c) 1999 Sven Wischnowsky
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, without written agreement and without
 | |
|  * license or royalty fees, to use, copy, modify, and distribute this
 | |
|  * software and to distribute modified versions of this software for any
 | |
|  * purpose, provided that the above copyright notice and the following
 | |
|  * two paragraphs appear in all copies of this software.
 | |
|  *
 | |
|  * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
 | |
|  * to any party for direct, indirect, special, incidental, or consequential
 | |
|  * damages arising out of the use of this software and its documentation,
 | |
|  * even if Sven Wischnowsky and the Zsh Development Group have been advised of
 | |
|  * the possibility of such damage.
 | |
|  *
 | |
|  * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
 | |
|  * warranties, including, but not limited to, the implied warranties of
 | |
|  * merchantability and fitness for a particular purpose.  The software
 | |
|  * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
 | |
|  * Zsh Development Group have no obligation to provide maintenance,
 | |
|  * support, updates, enhancements, or modifications.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "complete.mdh"
 | |
| #include "compcore.pro"
 | |
| 
 | |
| /* Flags saying what we have to do with the result. */
 | |
| 
 | |
| /**/
 | |
| int useexact, useline, uselist, forcelist, startauto;
 | |
| 
 | |
| /**/
 | |
| mod_export int iforcemenu;
 | |
| 
 | |
| /* Non-zero if we should go back to the last prompt. */
 | |
| 
 | |
| /**/
 | |
| mod_export int dolastprompt;
 | |
| 
 | |
| /* Non-zero if we should keep an old list. */
 | |
| 
 | |
| /**/
 | |
| mod_export int oldlist, oldins;
 | |
| 
 | |
| /* Original prefix/suffix lengths. Flag saying if they changed. */
 | |
| 
 | |
| /**/
 | |
| int origlpre, origlsuf, lenchanged;
 | |
| 
 | |
| /* This is used to decide when the cursor should be moved to the end of    *
 | |
|  * the inserted word: 0 - never, 1 - only when a single match is inserted, *
 | |
|  * 2 - when a full match is inserted (single or menu), 3 - always.         */
 | |
| 
 | |
| /**/
 | |
| int movetoend;
 | |
| 
 | |
| /* The match and group number to insert when starting menucompletion.   */
 | |
| 
 | |
| /**/
 | |
| mod_export int insmnum, insspace;
 | |
| 
 | |
| #if 0
 | |
| /* group-numbers in compstate[insert] */
 | |
| int insgnum, insgroup; /* mod_export */
 | |
| #endif
 | |
| 
 | |
| /* Information about menucompletion. */
 | |
| 
 | |
| /**/
 | |
| mod_export struct menuinfo minfo;
 | |
| 
 | |
| /* Number of matches accepted with accept-and-menu-complete */
 | |
| 
 | |
| /**/
 | |
| mod_export int menuacc;
 | |
| 
 | |
| /* Brace insertion stuff. */
 | |
| 
 | |
| /**/
 | |
| int hasunqu, useqbr, brpcs, brscs;
 | |
| 
 | |
| /* Flags saying in what kind of string we are. */
 | |
| 
 | |
| /**/
 | |
| mod_export int ispar, linwhat;
 | |
| 
 | |
| /* A parameter expansion prefix (like ${). */
 | |
| 
 | |
| /**/
 | |
| char *parpre;
 | |
| 
 | |
| /* Flags for parameter expansions for new style completion. */
 | |
| 
 | |
| /**/
 | |
| int parflags;
 | |
| 
 | |
| /* Match flags for all matches in this group. */
 | |
| 
 | |
| /**/
 | |
| mod_export int mflags;
 | |
| 
 | |
| /* Flags saying how the parameter expression we are in is quoted. */
 | |
| 
 | |
| /**/
 | |
| int parq, eparq;
 | |
| 
 | |
| /* We store the following prefixes/suffixes:                               *
 | |
|  * ipre,ripre  -- the ignored prefix (quoted and unquoted)                 *
 | |
|  * isuf        -- the ignored suffix                                       */
 | |
| 
 | |
| /**/
 | |
| mod_export char *ipre, *ripre, *isuf;
 | |
| 
 | |
| /* The list of matches.  fmatches contains the matches we first ignore *
 | |
|  * because of fignore.                                                 */
 | |
| 
 | |
| /**/
 | |
| 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
 | |
|  * currently built, amatches is the heap allocated stuff during completion
 | |
|  * (after all matches have been generated it is an alias for pmatches), and
 | |
|  * lmatches/lastlmatches is a pointer to the last element in the lists. */
 | |
| 
 | |
| /**/
 | |
| mod_export Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches;
 | |
| 
 | |
| /* Non-zero if we have permanently allocated matches (old and new). */
 | |
| 
 | |
| /**/
 | |
| mod_export int hasoldlist, hasperm;
 | |
| 
 | |
| /* Non-zero if we have a match representing all other matches. */
 | |
| 
 | |
| /**/
 | |
| int hasallmatch;
 | |
| 
 | |
| /* Non-zero if we have newly added matches. */
 | |
| 
 | |
| /**/
 | |
| mod_export int newmatches;
 | |
| 
 | |
| /* Number of permanently allocated matches and groups. */
 | |
| 
 | |
| /**/
 | |
| mod_export int permmnum, permgnum, lastpermmnum, lastpermgnum;
 | |
| 
 | |
| /* The total number of matches and the number of matches to be listed. */
 | |
| 
 | |
| /**/
 | |
| mod_export int nmatches;
 | |
| /**/
 | |
| mod_export int smatches;
 | |
| 
 | |
| /* != 0 if more than one match and at least two different matches */
 | |
| 
 | |
| /**/
 | |
| mod_export int diffmatches;
 | |
| 
 | |
| /* The number of messages. */
 | |
| 
 | |
| /**/
 | |
| mod_export int nmessages;
 | |
| 
 | |
| /* != 0 if only explanation strings should be printed */
 | |
| 
 | |
| /**/
 | |
| mod_export int onlyexpl;
 | |
| 
 | |
| /* Information about the matches for listing. */
 | |
| 
 | |
| /**/
 | |
| mod_export struct cldata listdat;
 | |
| 
 | |
| /* This flag is non-zero if we are completing a pattern (with globcomplete) */
 | |
| 
 | |
| /**/
 | |
| mod_export int ispattern, haspattern;
 | |
| 
 | |
| /* Non-zero if at least one match was added without/with -U. */
 | |
| 
 | |
| /**/
 | |
| mod_export int hasmatched, hasunmatched;
 | |
| 
 | |
| /* The current group of matches. */
 | |
| 
 | |
| /**/
 | |
| Cmgroup mgroup;
 | |
| 
 | |
| /* Match counter: all matches. */
 | |
| 
 | |
| /**/
 | |
| mod_export int mnum;
 | |
| 
 | |
| /* The match counter when unambig_data() was called. */
 | |
| 
 | |
| /**/
 | |
| mod_export int unambig_mnum;
 | |
| 
 | |
| /* Length of longest/shortest match. */
 | |
| 
 | |
| /**/
 | |
| int maxmlen, minmlen;
 | |
| 
 | |
| /* This holds the explanation strings we have to print in this group and *
 | |
|  * a pointer to the current cexpl structure. */
 | |
| 
 | |
| /**/
 | |
| LinkList expls;
 | |
| 
 | |
| /**/
 | |
| mod_export Cexpl curexpl;
 | |
| 
 | |
| /* A stack of completion matchers to be used. */
 | |
| 
 | |
| /**/
 | |
| mod_export Cmlist mstack;
 | |
| 
 | |
| /* The completion matchers used when building new stuff for the line. */
 | |
| 
 | |
| /**/
 | |
| mod_export Cmlist bmatchers;
 | |
| 
 | |
| /* A list with references to all matchers we used. */
 | |
| 
 | |
| /**/
 | |
| mod_export LinkList matchers;
 | |
| 
 | |
| /* A heap of free Cline structures. */
 | |
| 
 | |
| /**/
 | |
| mod_export Cline freecl;
 | |
| 
 | |
| /* Ambiguous information. */
 | |
| 
 | |
| /**/
 | |
| mod_export Aminfo ainfo, fainfo;
 | |
| 
 | |
| /* The memory heap to use for new style completion generation. */
 | |
| 
 | |
| /**/
 | |
| mod_export Heap compheap;
 | |
| 
 | |
| /* A list of some data.
 | |
|  *
 | |
|  * Well, actually, it's the list of all compctls used so far, but since
 | |
|  * conceptually we don't know anything about compctls here... */
 | |
| 
 | |
| /**/
 | |
| 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       *
 | |
|  * string was inserted. If FC_INWORD is set, the last completion moved   *
 | |
|  * the cursor into the word although it was at the end of it when the    *
 | |
|  * last completion was invoked.                                          *
 | |
|  * This is used to detect if the string should be taken as an exact      *
 | |
|  * match (see do_ambiguous()) and if the cursor has to be moved to the   *
 | |
|  * end of the word before generating the completions.                    */
 | |
| 
 | |
| /**/
 | |
| int fromcomp;
 | |
| 
 | |
| /* This holds the end-position of the last string inserted into the line. */
 | |
| 
 | |
| /**/
 | |
| mod_export int lastend;
 | |
| 
 | |
| #define inststr(X) inststrlen((X),1,-1)
 | |
| 
 | |
| /*
 | |
|  * Main completion entry point, called from zle. 
 | |
|  * At this point the line is already metafied.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| do_completion(UNUSED(Hookdef dummy), Compldat dat)
 | |
| {
 | |
|     int ret = 0, lst = dat->lst, incmd = dat->incmd, osl = showinglist;
 | |
|     char *s = dat->s;
 | |
|     char *opm;
 | |
|     LinkNode n;
 | |
| 
 | |
|     METACHECK();
 | |
| 
 | |
|     pushheap();
 | |
| 
 | |
|     ainfo = fainfo = NULL;
 | |
|     matchers = newlinklist();
 | |
| 
 | |
|     zsfree(compqstack);
 | |
|     compqstack = zalloc(2);
 | |
|     /*
 | |
|      * It looks like we may need to do stuff with backslashes even
 | |
|      * if instring is QT_NONE.
 | |
|      */
 | |
|     *compqstack = (instring == QT_NONE) ? QT_BACKSLASH : (char)instring;
 | |
|     compqstack[1] = '\0';
 | |
| 
 | |
|     hasunqu = 0;
 | |
|     useline = (wouldinstab ? -1 : (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) ? "yes" : "");
 | |
|     dolastprompt = 1;
 | |
|     zsfree(complist);
 | |
|     complist = ztrdup(isset(LISTROWSFIRST) ?
 | |
| 		      (isset(LISTPACKED) ? "packed rows" : "rows") :
 | |
| 		      (isset(LISTPACKED) ? "packed" : ""));
 | |
|     startauto = isset(AUTOMENU);
 | |
|     movetoend = ((zlemetacs == we || isset(ALWAYSTOEND)) ? 2 : 1);
 | |
|     showinglist = 0;
 | |
|     hasmatched = hasunmatched = 0;
 | |
|     minmlen = 1000000;
 | |
|     maxmlen = -1;
 | |
|     compignored = 0;
 | |
|     nmessages = 0;
 | |
|     hasallmatch = 0;
 | |
| 
 | |
|     /* Make sure we have the completion list and compctl. */
 | |
|     if (makecomplist(s, incmd, lst)) {
 | |
| 	/* Error condition: feeeeeeeeeeeeep(). */
 | |
| 	zlemetacs = 0;
 | |
| 	foredel(zlemetall, CUT_RAW);
 | |
| 	inststr(origline);
 | |
| 	zlemetacs = origcs;
 | |
| 	clearlist = 1;
 | |
| 	ret = 1;
 | |
| 	minfo.cur = NULL;
 | |
| 	if (useline < 0) {
 | |
| 	    /* unmetafy line before calling ZLE */
 | |
| 	    unmetafy_line();
 | |
| 	    ret = selfinsert(zlenoargs);
 | |
| 	    metafy_line();
 | |
| 	}
 | |
| 	goto compend;
 | |
|     }
 | |
|     zsfree(lastprebr);
 | |
|     zsfree(lastpostbr);
 | |
|     lastprebr = lastpostbr = NULL;
 | |
| 
 | |
|     if (comppatmatch && *comppatmatch && comppatmatch != opm)
 | |
| 	haspattern = 1;
 | |
|     if (iforcemenu) {
 | |
| 	if (nmatches)
 | |
|             do_ambig_menu();
 | |
| 	ret = !nmatches;
 | |
|     } else if (useline < 0) {
 | |
| 	/* unmetafy line before calling ZLE */
 | |
| 	unmetafy_line();
 | |
| 	ret = selfinsert(zlenoargs);
 | |
| 	metafy_line();
 | |
|     } else if (!useline && uselist) {
 | |
| 	/* All this and the guy only wants to see the list, sigh. */
 | |
| 	zlemetacs = 0;
 | |
| 	foredel(zlemetall, CUT_RAW);
 | |
| 	inststr(origline);
 | |
| 	zlemetacs = origcs;
 | |
| 	showinglist = -2;
 | |
|     } else if (useline == 2 && nmatches > 1) {
 | |
| 	do_allmatches(1);
 | |
| 
 | |
| 	minfo.cur = NULL;
 | |
| 
 | |
| 	if (forcelist)
 | |
| 	    showinglist = -2;
 | |
| 	else
 | |
| 	    invalidatelist();
 | |
|     } else if (useline) {
 | |
| 	/* We have matches. */
 | |
| 	if (nmatches > 1 && diffmatches) {
 | |
| 	    /* There is more than one match. */
 | |
| 	    ret = do_ambiguous();
 | |
| 
 | |
| 	    if (!showinglist && uselist && listshown && (usemenu == 2 || oldlist))
 | |
| 		showinglist = osl;
 | |
| 	} else if (nmatches == 1 || (nmatches > 1 && !diffmatches)) {
 | |
| 	    /* Only one match. */
 | |
| 	    Cmgroup m = amatches;
 | |
| #ifdef ZSH_HEAP_DEBUG
 | |
| 	    if (memory_validate(m->heap_id)) {
 | |
| 		HEAP_ERROR(m->heap_id);
 | |
| 	    }
 | |
| #endif
 | |
| 
 | |
| 	    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();
 | |
| 	} else if (nmessages && forcelist) {
 | |
| 	    if (uselist)
 | |
| 		showinglist = -2;
 | |
| 	    else
 | |
| 		clearlist = 1;
 | |
| 	}
 | |
|     } else {
 | |
| 	invalidatelist();
 | |
| 	lastambig = isset(BASHAUTOLIST);
 | |
| 	if (forcelist)
 | |
| 	    clearlist = 1;
 | |
| 	zlemetacs = 0;
 | |
| 	foredel(zlemetall, CUT_RAW);
 | |
| 	inststr(origline);
 | |
| 	zlemetacs = origcs;
 | |
|     }
 | |
|     /* Print the explanation strings if needed. */
 | |
|     if (!showinglist && validlist && usemenu != 2 && uselist &&
 | |
| 	(nmatches != 1 || diffmatches) &&
 | |
| 	useline >= 0 && useline != 2 && (!oldlist || !listshown)) {
 | |
| 	onlyexpl = 3;
 | |
| 	showinglist = -2;
 | |
|     }
 | |
|  compend:
 | |
|     for (n = firstnode(matchers); n; incnode(n))
 | |
| 	freecmatcher((Cmatcher) getdata(n));
 | |
| 
 | |
|     zlemetall = strlen(zlemetaline);
 | |
|     if (zlemetacs > zlemetall)
 | |
| 	zlemetacs = zlemetall;
 | |
|     popheap();
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* Before and after hooks called by zle. */
 | |
| 
 | |
| static int oldmenucmp;
 | |
| 
 | |
| /**/
 | |
| int
 | |
| before_complete(UNUSED(Hookdef dummy), int *lst)
 | |
| {
 | |
|     oldmenucmp = menucmp;
 | |
| 
 | |
|     if (showagain && validlist)
 | |
| 	showinglist = -2;
 | |
|     showagain = 0;
 | |
| 
 | |
|     /* If we are doing a menu-completion... */
 | |
| 
 | |
|     if (minfo.cur && menucmp && *lst != COMP_LIST_EXPAND) {
 | |
| 	do_menucmp(*lst);
 | |
| 	return 1;
 | |
|     }
 | |
|     if (minfo.cur && menucmp && validlist && *lst == COMP_LIST_COMPLETE) {
 | |
| 	showinglist = -2;
 | |
| 	onlyexpl = listdat.valid = 0;
 | |
| 	return 1;
 | |
|     }
 | |
| 
 | |
|     /* We may have to reset the cursor to its position after the   *
 | |
|      * string inserted by the last completion. */
 | |
| 
 | |
|     /*
 | |
|      * Currently this hook runs before metafication.
 | |
|      * This is the only hook of the three defined here of
 | |
|      * which that is true.
 | |
|      */
 | |
|     if ((fromcomp & FC_INWORD) && (zlecs = lastend) > zlell)
 | |
| 	zlecs = zlell;
 | |
| 
 | |
|     /* Check if we have to start a menu-completion (via automenu). */
 | |
| 
 | |
|     if (startauto && lastambig &&
 | |
| 	(!isset(BASHAUTOLIST) || lastambig == 2))
 | |
| 	usemenu = 2;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| after_complete(UNUSED(Hookdef dummy), int *dat)
 | |
| {
 | |
|     if (menucmp && !oldmenucmp) {
 | |
| 	struct chdata cdat;
 | |
| 	int ret;
 | |
| 
 | |
| 	cdat.matches = amatches;
 | |
| #ifdef ZSH_HEAP_DEBUG
 | |
| 	if (memory_validate(cdat.matches->heap_id)) {
 | |
| 	    HEAP_ERROR(cdat.matches->heap_id);
 | |
| 	}
 | |
| #endif
 | |
| 	cdat.num = nmatches;
 | |
| 	cdat.nmesg = nmessages;
 | |
| 	cdat.cur = NULL;
 | |
| 	if ((ret = runhookdef(MENUSTARTHOOK, (void *) &cdat))) {
 | |
| 	    dat[1] = 0;
 | |
| 	    menucmp = menuacc = 0;
 | |
| 	    minfo.cur = NULL;
 | |
| 	    if (ret >= 2) {
 | |
| 		fixsuffix();
 | |
| 		zlemetacs = 0;
 | |
| 		foredel(zlemetall, CUT_RAW);
 | |
| 		inststr(origline);
 | |
| 		zlemetacs = origcs;
 | |
| 		if (ret == 2) {
 | |
| 		    clearlist = 1;
 | |
| 		    invalidatelist();
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* This calls the given completion widget function. */
 | |
| 
 | |
| static int parwb, parwe, paroffs;
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| callcompfunc(char *s, char *fn)
 | |
| {
 | |
|     Shfunc shfunc;
 | |
|     int lv = lastval;
 | |
|     char buf[20];
 | |
| 
 | |
|     METACHECK();
 | |
| 
 | |
|     if ((shfunc = getshfunc(fn))) {
 | |
| 	char **p, *tmp;
 | |
| 	int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
 | |
| 	unsigned int rset, kset;
 | |
| 	Param *ocrpms = comprpms, *ockpms = compkpms;
 | |
| 
 | |
| 	comprpms = (Param *) zalloc(CP_REALPARAMS * sizeof(Param));
 | |
| 	compkpms = (Param *) zalloc(CP_KEYPARAMS * sizeof(Param));
 | |
| 
 | |
| 	rset = CP_ALLREALS;
 | |
| 	kset = CP_ALLKEYS &
 | |
| 	    ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING |
 | |
| 	      CP_EXACTSTR | CP_OLDLIST | CP_OLDINS |
 | |
| 	      (useglob ? 0 : CP_PATMATCH));
 | |
| 	zsfree(compvared);
 | |
| 	if (varedarg) {
 | |
| 	    compvared = ztrdup(varedarg);
 | |
| 	    kset |= CP_VARED;
 | |
| 	} else
 | |
| 	    compvared = ztrdup("");
 | |
| 	if (!*complastprompt)
 | |
| 	    kset &= ~CP_LASTPROMPT;
 | |
| 	zsfree(compcontext);
 | |
| 	zsfree(compparameter);
 | |
| 	zsfree(compredirect);
 | |
| 	compparameter = compredirect = "";
 | |
| 	if (ispar)
 | |
| 	    compcontext = (ispar == 2 ? "brace_parameter" : "parameter");
 | |
|         else if (linwhat == IN_PAR)
 | |
|             compcontext = "assign_parameter";
 | |
| 	else if (linwhat == IN_MATH) {
 | |
| 	    if (insubscr) {
 | |
| 		compcontext = "subscript";
 | |
| 		if (varname) {
 | |
| 		    compparameter = varname;
 | |
| 		    kset |= CP_PARAMETER;
 | |
| 		}
 | |
| 	    } else
 | |
| 		compcontext = "math";
 | |
| 	    usea = 0;
 | |
| 	} else if (lincmd) {
 | |
| 	    if (insubscr) {
 | |
| 		compcontext = "subscript";
 | |
| 		kset |= CP_PARAMETER;
 | |
| 	    } else
 | |
| 		compcontext = "command";
 | |
| 	} else if (linredir) {
 | |
| 	    compcontext = "redirect";
 | |
| 	    if (rdstr)
 | |
| 		compredirect = rdstr;
 | |
| 	    kset |= CP_REDIRECT;
 | |
| 	} else {
 | |
| 	    switch (linwhat) {
 | |
| 	    case IN_ENV:
 | |
| 		compcontext = (linarr ? "array_value" : "value");
 | |
| 		compparameter = varname;
 | |
| 		kset |= CP_PARAMETER;
 | |
| 		if (!clwpos) {
 | |
| 		    clwpos = 1;
 | |
| 		    clwnum = 2;
 | |
| 		    zsfree(clwords[1]);
 | |
| 		    clwords[1] = ztrdup(s);
 | |
| 		    zsfree(clwords[2]);
 | |
| 		    clwords[2] = NULL;
 | |
| 		}
 | |
| 		aadd = 1;
 | |
| 		break;
 | |
| 	    case IN_COND:
 | |
| 		compcontext = "condition";
 | |
| 		break;
 | |
| 	    default:
 | |
| 		if (cmdstr)
 | |
| 		    compcontext = "command";
 | |
| 		else {
 | |
| 		    compcontext = "value";
 | |
| 		    kset |= CP_PARAMETER;
 | |
| 		    if (clwords[0])
 | |
| 			compparameter = clwords[0];
 | |
| 		    aadd = 1;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	compcontext = ztrdup(compcontext);
 | |
| 	if (compwords)
 | |
| 	    freearray(compwords);
 | |
| 	if (usea && (!aadd || clwords[0])) {
 | |
| 	    char **q;
 | |
| 
 | |
| 	    q = compwords = (char **)
 | |
| 		zalloc((clwnum + 1) * sizeof(char *));
 | |
| 	    for (p = clwords + aadd; *p; p++, q++)
 | |
| 		untokenize(*q = ztrdup(*p));
 | |
| 	    *q = NULL;
 | |
| 	} else
 | |
| 	    compwords = (char **) zshcalloc(sizeof(char *));
 | |
| 
 | |
| 	if (compredirs)
 | |
| 	    freearray(compredirs);
 | |
|         if (rdstrs)
 | |
|             compredirs = zlinklist2array(rdstrs, 1);
 | |
|         else
 | |
|             compredirs = (char **) zshcalloc(sizeof(char *));
 | |
| 
 | |
| 	/*
 | |
| 	 * We need to untokenize compparameter which is the
 | |
| 	 * raw internals of a parameter subscript.
 | |
| 	 *
 | |
| 	 * The double memory duplication is a bit ugly: the additional
 | |
| 	 * dupstring() is necessary because untokenize() might change
 | |
| 	 * the string length and so later zsfree() would get the wrong
 | |
| 	 * length of the string.
 | |
| 	 */
 | |
| 	compparameter = dupstring(compparameter);
 | |
| 	untokenize(compparameter);
 | |
| 	compparameter = ztrdup(compparameter);
 | |
| 	compredirect = ztrdup(compredirect);
 | |
| 	zsfree(compquote);
 | |
| 	zsfree(compquoting);
 | |
| 	if (instring > QT_BACKSLASH) {
 | |
| 	    switch (instring) {
 | |
| 	    case QT_SINGLE:
 | |
| 		compquote = ztrdup("\'");
 | |
| 		compquoting = ztrdup("single");
 | |
| 		break;
 | |
| 
 | |
| 	    case QT_DOUBLE:
 | |
| 		compquote = ztrdup("\"");
 | |
| 		compquoting = ztrdup("double");
 | |
| 		break;
 | |
| 
 | |
| 	    case QT_DOLLARS:
 | |
| 		compquote = ztrdup("$'");
 | |
| 		compquoting = ztrdup("dollars");
 | |
| 		break;
 | |
| 	    }
 | |
| 	    kset |= CP_QUOTE | CP_QUOTING;
 | |
| 	} else if (inbackt) {
 | |
| 	    compquote = ztrdup("`");
 | |
| 	    compquoting = ztrdup("backtick");
 | |
| 	    kset |= CP_QUOTE | CP_QUOTING;
 | |
| 	} else {
 | |
| 	    compquote = ztrdup("");
 | |
| 	    compquoting = ztrdup("");
 | |
| 	}
 | |
| 	zsfree(compprefix);
 | |
| 	zsfree(compsuffix);
 | |
| 	makebangspecial(0);
 | |
| 	if (unset(COMPLETEINWORD)) {
 | |
| 	    tmp = (linwhat == IN_MATH ? dupstring(s) : multiquote(s, 0));
 | |
| 	    untokenize(tmp);
 | |
| 	    compprefix = ztrdup(tmp);
 | |
| 	    compsuffix = ztrdup("");
 | |
| 	} else {
 | |
| 	    char *ss, sav;
 | |
| 	    
 | |
| 	    ss = s + offs;
 | |
| 
 | |
| 	    sav = *ss;
 | |
| 	    *ss = '\0';
 | |
| 	    tmp = (linwhat == IN_MATH ? dupstring(s) : multiquote(s, 0));
 | |
| 	    untokenize(tmp);
 | |
| 	    compprefix = ztrdup(tmp);
 | |
| 	    *ss = sav;
 | |
| 	    ss = (linwhat == IN_MATH ? dupstring(ss) : multiquote(ss, 0));
 | |
| 	    untokenize(ss);
 | |
| 	    compsuffix = ztrdup(ss);
 | |
| 	}
 | |
| 	makebangspecial(1);
 | |
|         zsfree(complastprefix);
 | |
|         zsfree(complastsuffix);
 | |
|         complastprefix = ztrdup(compprefix);
 | |
|         complastsuffix = ztrdup(compsuffix);
 | |
| 	zsfree(compiprefix);
 | |
| 	zsfree(compisuffix);
 | |
| 	if (parwb < 0) {
 | |
| 	    compiprefix = ztrdup("");
 | |
| 	    compisuffix = ztrdup("");
 | |
| 	} else {
 | |
| 	    int l;
 | |
| 
 | |
| 	    compiprefix = (char *) zalloc((l = wb - parwb) + 1);
 | |
| 	    memcpy(compiprefix, zlemetaline + parwb, l);
 | |
| 	    compiprefix[l] = '\0';
 | |
| 	    compisuffix = (char *) zalloc((l = parwe - we) + 1);
 | |
| 	    memcpy(compisuffix, zlemetaline + we, l);
 | |
| 	    compisuffix[l] = '\0';
 | |
| 
 | |
| 	    wb = parwb;
 | |
| 	    we = parwe;
 | |
| 	    offs = paroffs;
 | |
| 	}
 | |
| 	zsfree(compqiprefix);
 | |
| 	compqiprefix = ztrdup(qipre ? qipre : "");
 | |
| 	zsfree(compqisuffix);
 | |
| 	compqisuffix = ztrdup(qisuf ? qisuf : "");
 | |
| 	origlpre = (strlen(compqiprefix) + strlen(compiprefix) +
 | |
| 		    strlen(compprefix));
 | |
| 	origlsuf = (strlen(compqisuffix) + strlen(compisuffix) +
 | |
| 		    strlen(compsuffix));
 | |
| 	lenchanged = 0;
 | |
| 	compcurrent = (usea ? (clwpos + 1 - aadd) : 0);
 | |
| 
 | |
| 	zsfree(complist);
 | |
| 	switch (uselist) {
 | |
| 	case 0: complist = ""; kset &= ~CP_LIST; break;
 | |
| 	case 1: complist = "list"; break;
 | |
| 	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 = (isset(AUTOMENU) ?
 | |
| 			      "automenu-unambiguous" :
 | |
| 			      "unambiguous");
 | |
| 		break;
 | |
| 	    case 1: compinsert = "menu"; break;
 | |
| 	    case 2: compinsert = "automenu"; break;
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    compinsert = "";
 | |
| 	    kset &= ~CP_INSERT;
 | |
| 	}
 | |
| 	compinsert = (useline < 0 ? tricat("tab ", "", compinsert) :
 | |
| 		      ztrdup(compinsert));
 | |
| 	zsfree(compexact);
 | |
| 	if (useexact)
 | |
| 	    compexact = ztrdup("accept");
 | |
| 	else {
 | |
| 	    compexact = ztrdup("");
 | |
| 	    kset &= ~CP_EXACT;
 | |
| 	}
 | |
| 	zsfree(comptoend);
 | |
| 	if (movetoend == 1)
 | |
| 	    comptoend = ztrdup("single");
 | |
| 	else
 | |
| 	    comptoend = ztrdup("match");
 | |
| 	zsfree(compoldlist);
 | |
| 	zsfree(compoldins);
 | |
| 	if (hasoldlist && lastpermmnum) {
 | |
| 	    if (listshown)
 | |
| 		compoldlist = "shown";
 | |
| 	    else
 | |
| 		compoldlist = "yes";
 | |
| 	    kset |= CP_OLDLIST;
 | |
| 	    if (minfo.cur) {
 | |
| 		sprintf(buf, "%d", (*(minfo.cur))->gnum);
 | |
| 		compoldins = buf;
 | |
| 		kset |= CP_OLDINS;
 | |
| 	    } else
 | |
| 		compoldins = "";
 | |
| 	} else
 | |
| 	    compoldlist = compoldins = "";
 | |
| 	compoldlist = ztrdup(compoldlist);
 | |
| 	compoldins = ztrdup(compoldins);
 | |
| 
 | |
| 	incompfunc = 1;
 | |
| 	startparamscope();
 | |
| 	makecompparams();
 | |
| 	comp_setunset(rset, (~rset & CP_ALLREALS),
 | |
| 		      kset, (~kset & CP_ALLKEYS));
 | |
| 	makezleparams(1);
 | |
| 	sfcontext = SFC_CWIDGET;
 | |
| 	NEWHEAPS(compheap) {
 | |
| 	    LinkList largs = NULL;
 | |
| 	    int oxt = isset(XTRACE);
 | |
| 
 | |
| 	    if (*cfargs) {
 | |
| 		char **p = cfargs;
 | |
| 
 | |
| 		largs = newlinklist();
 | |
| 		addlinknode(largs, dupstring(fn));
 | |
| 		while (*p)
 | |
| 		    addlinknode(largs, dupstring(*p++));
 | |
| 	    }
 | |
| 	    opts[XTRACE] = 0;
 | |
| 	    cfret = doshfunc(shfunc, largs, 1);
 | |
| 	    opts[XTRACE] = oxt;
 | |
| 	} OLDHEAPS;
 | |
| 	sfcontext = osc;
 | |
| 	endparamscope();
 | |
| 	lastcmd = 0;
 | |
| 	incompfunc = icf;
 | |
| 	startauto = 0;
 | |
| 
 | |
| 	if (!complist)
 | |
| 	    uselist = 0;
 | |
| 	else if (!strncmp(complist, "list", 4))
 | |
| 	    uselist = 1;
 | |
| 	else if (!strncmp(complist, "auto", 4))
 | |
| 	    uselist = 2;
 | |
| 	else if (!strncmp(complist, "ambig", 5))
 | |
| 	    uselist = 3;
 | |
| 	else
 | |
| 	    uselist = 0;
 | |
| 	forcelist = (complist && strstr(complist, "force"));
 | |
| 	onlyexpl = (complist ? ((strstr(complist, "expl") ? 1 : 0) |
 | |
| 				(strstr(complist, "messages") ? 2 : 0)) : 0);
 | |
| 
 | |
| 	if (!compinsert)
 | |
| 	    useline = 0;
 | |
| 	else if (strstr(compinsert, "tab"))
 | |
| 	    useline = -1;
 | |
| 	else if (!strcmp(compinsert, "unambig") ||
 | |
| 		 !strcmp(compinsert, "unambiguous") ||
 | |
| 		 !strcmp(compinsert, "automenu-unambiguous"))
 | |
| 	    useline = 1, usemenu = 0;
 | |
| 	else if (!strcmp(compinsert, "all"))
 | |
| 	    useline = 2, usemenu = 0;
 | |
| 	else if (idigit(*compinsert)) {
 | |
| #if 0
 | |
| 	    /* group-numbers in compstate[insert] */
 | |
| 	    char *m;
 | |
| #endif
 | |
| 	    useline = 1; usemenu = 3;
 | |
| 	    insmnum = atoi(compinsert);
 | |
| #if 0
 | |
| 	    /* group-numbers in compstate[insert] */
 | |
| 	    if ((m = strchr(compinsert, ':'))) {
 | |
| 		insgroup = 1;
 | |
| 		insgnum = atoi(m + 1);
 | |
| 	    }
 | |
| #endif
 | |
| 	    insspace = (compinsert[strlen(compinsert) - 1] == ' ');
 | |
| 	} else {
 | |
| 	    char *p;
 | |
| 
 | |
| 	    if (strpfx("menu", compinsert))
 | |
| 		useline = 1, usemenu = 1;
 | |
| 	    else if (strpfx("auto", compinsert))
 | |
| 		useline = 1, usemenu = 2;
 | |
| 	    else {
 | |
| 		useline = usemenu = 0;
 | |
| 		/* if compstate[insert] was emptied, no unambiguous prefix
 | |
| 		 * ever gets inserted so allow the next tab to already start
 | |
| 		 * menu completion */
 | |
| 		startauto = lastambig = isset(AUTOMENU);
 | |
| 	    }
 | |
| 
 | |
| 	    if (useline && (p = strchr(compinsert, ':'))) {
 | |
| 		insmnum = atoi(++p);
 | |
| #if 0
 | |
| 		/* group-numbers in compstate[insert] */
 | |
| 		if ((p = strchr(p, ':'))) {
 | |
| 		    insgroup = 1;
 | |
| 		    insgnum = atoi(p + 1);
 | |
| 		}
 | |
| #endif
 | |
| 	    }
 | |
| 	}
 | |
| 	startauto = startauto || ((compinsert &&
 | |
| 		      !strcmp(compinsert, "automenu-unambiguous")) ||
 | |
| 		     (bashlistfirst && isset(AUTOMENU) &&
 | |
|                       (!compinsert || !*compinsert)));
 | |
| 	useexact = (compexact && !strcmp(compexact, "accept"));
 | |
| 
 | |
| 	if (!comptoend || !*comptoend)
 | |
| 	    movetoend = 0;
 | |
| 	else if (!strcmp(comptoend, "single"))
 | |
| 	    movetoend = 1;
 | |
| 	else if (!strcmp(comptoend, "always"))
 | |
| 	    movetoend = 3;
 | |
| 	else
 | |
| 	    movetoend = 2;
 | |
| 
 | |
| 	oldlist = (hasoldlist && compoldlist && !strcmp(compoldlist, "keep"));
 | |
| 	oldins = (hasoldlist && minfo.cur &&
 | |
| 		  compoldins && !strcmp(compoldins, "keep"));
 | |
| 
 | |
| 	zfree(comprpms, CP_REALPARAMS * sizeof(Param));
 | |
| 	zfree(compkpms, CP_KEYPARAMS * sizeof(Param));
 | |
| 	comprpms = ocrpms;
 | |
| 	compkpms = ockpms;
 | |
|     }
 | |
|     lastval = lv;
 | |
| }
 | |
| 
 | |
| /* Create the completion list.  This is called whenever some bit of   *
 | |
|  * completion code needs the list.                                    *
 | |
|  * Along with the list is maintained the prefixes/suffixes etc.  When *
 | |
|  * any of this becomes invalid -- e.g. if some text is changed on the *
 | |
|  * command line -- invalidatelist() should be called, to set          *
 | |
|  * validlist to zero and free up the memory used.  This function      *
 | |
|  * returns non-zero on error.                                         */
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| makecomplist(char *s, int incmd, int lst)
 | |
| {
 | |
|     char *p;
 | |
|     int owb = wb, owe = we, ooffs = offs;
 | |
| 
 | |
|     /* Inside $... ? */
 | |
|     if (compfunc && (p = check_param(s, 0, 0))) {
 | |
| 	s = p;
 | |
| 	parwb = owb;
 | |
| 	parwe = owe;
 | |
| 	paroffs = ooffs;
 | |
|     } else
 | |
| 	parwb = -1;
 | |
| 
 | |
|     linwhat = inwhat;
 | |
| 
 | |
|     if (compfunc) {
 | |
| 	char *os = s;
 | |
| 	int onm = nmatches, odm = diffmatches, osi = movefd(0);
 | |
| 
 | |
| 	bmatchers = NULL;
 | |
| 	mstack = NULL;
 | |
| 
 | |
| 	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 | |
| 	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 | |
| 
 | |
| 	freecl = NULL;
 | |
| 
 | |
| 	if (!validlist)
 | |
| 	    lastambig = 0;
 | |
| 	amatches = NULL;
 | |
| 	mnum = 0;
 | |
| 	unambig_mnum = -1;
 | |
| 	isuf = NULL;
 | |
| 	insmnum = zmult;
 | |
| #if 0
 | |
| 	/* group-numbers in compstate[insert] */
 | |
| 	insgnum = 1;
 | |
| 	insgroup = 0;
 | |
| #endif
 | |
| 	oldlist = oldins = 0;
 | |
| 	begcmgroup("default", 0);
 | |
| 	menucmp = menuacc = newmatches = onlyexpl = 0;
 | |
| 
 | |
| 	s = dupstring(os);
 | |
| 	callcompfunc(s, compfunc);
 | |
| 	endcmgroup(NULL);
 | |
| 
 | |
| 	/* Needed for compcall. */
 | |
| 	runhookdef(COMPCTLCLEANUPHOOK, NULL);
 | |
| 
 | |
| 	if (oldlist) {
 | |
| 	    nmatches = onm;
 | |
| 	    diffmatches = odm;
 | |
| 	    validlist = 1;
 | |
| 	    amatches = lastmatches;
 | |
| #ifdef ZSH_HEAP_DEBUG
 | |
| 	    if (memory_validate(amatches->heap_id)) {
 | |
| 		HEAP_ERROR(amatches->heap_id);
 | |
| 	    }
 | |
| #endif
 | |
| 	    lmatches = lastlmatches;
 | |
| 	    if (pmatches) {
 | |
| 		freematches(pmatches, 1);
 | |
| 		pmatches = NULL;
 | |
| 		hasperm = 0;
 | |
| 	    }
 | |
| 	    redup(osi, 0);
 | |
| 
 | |
| 	    return 0;
 | |
| 	}
 | |
| 	if (lastmatches) {
 | |
| 	    freematches(lastmatches, 1);
 | |
| 	    lastmatches = NULL;
 | |
| 	}
 | |
| 	permmatches(1);
 | |
| 	amatches = pmatches;
 | |
| 	lastpermmnum = permmnum;
 | |
| 	lastpermgnum = permgnum;
 | |
| 
 | |
| 	lastmatches = pmatches;
 | |
| 	lastlmatches = lmatches;
 | |
| 	pmatches = NULL;
 | |
| 	hasperm = 0;
 | |
| 	hasoldlist = 1;
 | |
| 
 | |
| 	if ((nmatches || nmessages) && !errflag) {
 | |
| 	    validlist = 1;
 | |
| 
 | |
| 	    redup(osi, 0);
 | |
| 
 | |
| 	    return 0;
 | |
| 	}
 | |
| 	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;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Quote 's' according to compqstack, aka $compstate[all_quotes].
 | |
|  *
 | |
|  * If 'ign' is 1, skip the innermost quoting level.  Otherwise 'ign'
 | |
|  * must be 0.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export char *
 | |
| multiquote(char *s, int ign)
 | |
| {
 | |
|     if (s) {
 | |
| 	char *os = s, *p = compqstack;
 | |
| 
 | |
| 	if (p && *p && (ign == 0 || p[1])) {
 | |
| 	    if (ign)
 | |
| 		p++;
 | |
| 	    while (*p) {
 | |
| 		s = quotestring(s, *p);
 | |
| 		p++;
 | |
| 	    }
 | |
| 	}
 | |
| 	return (s == os ? dupstring(s) : s);
 | |
|     }
 | |
|     DPUTS(1, "BUG: null pointer in multiquote()");
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * tildequote(s, ign): Equivalent to multiquote(s, ign), except that if
 | |
|  * compqstack[0] == QT_BACKSLASH and s[0] == '~', then that tilde is not
 | |
|  * quoted.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| 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;
 | |
|     }
 | |
|     DPUTS(1, "BUG: null pointer in tildequote()");
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /* Check if we have to complete a parameter name. */
 | |
| 
 | |
| /**/
 | |
| mod_export char *
 | |
| check_param(char *s, int set, int test)
 | |
| {
 | |
|     char *p;
 | |
|     int found = 0, qstring = 0;
 | |
| 
 | |
|     zsfree(parpre);
 | |
|     parpre = NULL;
 | |
| 
 | |
|     if (!test)
 | |
| 	ispar = parq = eparq = 0;
 | |
|     /*
 | |
|      * Try to find a `$'.
 | |
|      *
 | |
|      * TODO: passing s as a parameter while we get some mysterious
 | |
|      * offset "offs" into it via a global sucks badly.
 | |
|      *
 | |
|      * From ../lex.c we know:
 | |
|      * wb is the beginning position of the current word in the line
 | |
|      * we is the position of the end of the current word in the line
 | |
|      * From zle_tricky.c we know:
 | |
|      * offs is position within the word where we are completing
 | |
|      *
 | |
|      * So wb + offs is the current cursor position if COMPLETE_IN_WORD
 | |
|      * is set, otherwise it is the end of the word (same as we).
 | |
|      * 
 | |
|      * Note below we are thus stepping backward from our completion
 | |
|      * position to find a '$' in the current word (if any).
 | |
|      */ 
 | |
|     for (p = s + offs; ; p--) {
 | |
| 	if (*p == String || *p == Qstring) {
 | |
| 	    /*
 | |
| 	     * String followed by Snull (unquoted) or
 | |
| 	     * QString followed by ' (quoted) indicate a nested
 | |
| 	     * $'...', not a substitution.
 | |
| 	     *
 | |
| 	     * TODO: the argument passing is obscure, no idea if
 | |
| 	     * it's safe to test for the "'" at the end.
 | |
| 	     */
 | |
| 	    if (p < s + offs &&
 | |
| 		!(*p == String && p[1] == Snull) &&
 | |
| 		!(*p == Qstring && p[1] == '\'')) {
 | |
| 		found = 1;
 | |
| 		qstring = (*p == Qstring);
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (p == s)
 | |
| 	    break;
 | |
|     }
 | |
|     if (found) {
 | |
| 	/*
 | |
| 	 * Handle $$'s
 | |
| 	 *
 | |
| 	 * TODO: this is already bad enough, so I haven't tried
 | |
| 	 * testing for $'...' here.  If we parsed this forwards
 | |
| 	 * it wouldn't be quite so bad.
 | |
| 	 */
 | |
| 	while (p > s && (p[-1] == String || p[-1] == Qstring))
 | |
| 	    p--;
 | |
| 	while ((p[1] == String || p[1] == Qstring) &&
 | |
| 	       (p[2] == String || p[2] == Qstring))
 | |
| 	    p += 2;
 | |
|     }
 | |
|     if (found &&
 | |
| 	p[1] != Inpar && p[1] != Inbrack && p[1] != Snull) {
 | |
| 	/* This is a parameter expression, not $(...), $[...], $'...'. */
 | |
| 	char *b = p + 1, *e = b, *ie;
 | |
| 	int br = 1, nest = 0;
 | |
| 
 | |
| 	if (*b == Inbrace) {
 | |
| 	    char *tb = b;
 | |
| 
 | |
| 	    /* If this is a ${...}, see if we are before the '}'. */
 | |
| 	    if (!skipparens(Inbrace, Outbrace, &tb) && tb - s <= offs)
 | |
| 		return NULL;
 | |
| 
 | |
| 	    /* Ignore the possible (...) flags. */
 | |
| 	    b++, br++;
 | |
| 	    if ((qstring ? skipparens('(', ')', &b) :
 | |
| 		 skipparens(Inpar, Outpar, &b)) > 0 || b - s > offs) {
 | |
| 		/*
 | |
| 		 * We are still within the parameter flags.  There's no
 | |
| 		 * point trying to do anything clever here with
 | |
| 		 * parameter names.  Instead, just report that we are in
 | |
| 		 * a brace parameter but let the completion function
 | |
| 		 * decide what to do about it.
 | |
| 		 */
 | |
| 		ispar = 2;
 | |
| 		return NULL;
 | |
| 	    }
 | |
| 
 | |
| 	    for (tb = p - 1; tb > s && *tb != Outbrace && *tb != Inbrace; tb--);
 | |
| 	    if (tb > s && *tb == Inbrace && (tb[-1] == String || *tb == Qstring))
 | |
| 		nest = 1;
 | |
| 	}
 | |
| 
 | |
| 	/* Ignore the stuff before the parameter name. */
 | |
| 	for (; *b; b++)
 | |
| 	    if (*b != '^' && *b != Hat &&
 | |
| 		*b != '=' && *b != Equals &&
 | |
| 		*b != '~' && *b != Tilde)
 | |
| 		break;
 | |
| 	if (*b == '#' || *b == Pound || *b == '+')
 | |
| 	    b++;
 | |
| 
 | |
| 	e = b;
 | |
| 	if (br) {
 | |
| 	    while (*e == (test ? Dnull : '"'))
 | |
| 		e++, parq++;
 | |
| 	    if (!test)
 | |
| 		b = e;
 | |
| 	}
 | |
| 	/* Find the end of the name. */
 | |
| 	if (*e == Quest || *e == Star || *e == String || *e == Qstring ||
 | |
| 	    *e == '?'   || *e == '*'  || *e == '$'    ||
 | |
| 	    *e == '-'   || *e == '!'  || *e == '@')
 | |
| 	    e++;
 | |
| 	else if (idigit(*e))
 | |
| 	    while (idigit(*e))
 | |
| 		e++;
 | |
| 	else if ((ie = itype_end(e, IIDENT, 0)) != e) {
 | |
| 	    do {
 | |
| 		e = ie;
 | |
| 		if (comppatmatch && *comppatmatch &&
 | |
| 		    (*e == Star || *e == Quest))
 | |
| 		    ie = e + 1;
 | |
| 		else
 | |
| 		    ie = itype_end(e, IIDENT, 0);
 | |
| 	    } while (ie != e);
 | |
| 	}
 | |
| 
 | |
| 	/* Now make sure that the cursor is inside the name. */
 | |
| 	if (offs <= e - s && offs >= b - s) {
 | |
| 	    char sav;
 | |
| 
 | |
| 	    if (br) {
 | |
| 		p = e;
 | |
| 		while (*p == (test ? Dnull : '"'))
 | |
| 		    p++, parq--, eparq++;
 | |
| 	    }
 | |
| 	    /* It is. */
 | |
| 	    if (test)
 | |
| 		return b;
 | |
| 	    /* If we were called from makecomplistflags(), we have to set the
 | |
| 	     * global variables. */
 | |
| 
 | |
| 	    if (set) {
 | |
| 		if (br >= 2) {
 | |
| 		    mflags |= CMF_PARBR;
 | |
| 		    if (nest)
 | |
| 			mflags |= CMF_PARNEST;
 | |
| 		}
 | |
| 		/* Get the prefix (anything up to the character before the name). */
 | |
| 		isuf = dupstring(e);
 | |
| 		untokenize(isuf);
 | |
| 		sav = *b;
 | |
| 		*b = *e = '\0';
 | |
| 		ripre = dyncat((ripre ? ripre : ""), s);
 | |
| 		ipre = dyncat((ipre ? ipre : ""), s);
 | |
| 		*b = sav;
 | |
| 
 | |
| 		untokenize(ipre);
 | |
| 	    }
 | |
| 	    /* Save the prefix. */
 | |
| 	    if (compfunc) {
 | |
| 		parflags = (br >= 2 ? CMF_PARBR | (nest ? CMF_PARNEST : 0) : 0);
 | |
| 		sav = *b;
 | |
| 		*b = '\0';
 | |
| 		untokenize(parpre = ztrdup(s));
 | |
| 		*b = sav;
 | |
| 	    }
 | |
| 	    /* And adjust wb, we, and offs again. */
 | |
| 	    offs -= b - s;
 | |
| 	    wb = zlemetacs - offs;
 | |
| 	    we = wb + e - b;
 | |
| 	    ispar = (br >= 2 ? 2 : 1);
 | |
| 	    b[we-wb] = '\0';
 | |
| 	    return b;
 | |
| 	} else if (offs > e - s && *e == ':') {
 | |
| 	    /*
 | |
| 	     * Guess whether we are in modifiers.
 | |
| 	     * If the name is followed by a : and the stuff after
 | |
| 	     * that is either colons or alphanumerics we probably are.
 | |
| 	     * This is a very rough guess.
 | |
| 	     */
 | |
| 	    char *offsptr = s + offs;
 | |
| 	    for (; e < offsptr; e++) {
 | |
| 		if (*e != ':' && !ialnum(*e))
 | |
| 		    break;
 | |
| 	    }
 | |
| 	    ispar = (br >= 2 ? 2 : 1);
 | |
| 	    return NULL;
 | |
| 	}
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /* Copy the given string and remove backslashes from the copy and return it. */
 | |
| 
 | |
| /**/
 | |
| mod_export char *
 | |
| rembslash(char *s)
 | |
| {
 | |
|     char *t = s = dupstring(s);
 | |
| 
 | |
|     while (*s)
 | |
| 	if (*s == '\\') {
 | |
| 	    chuck(s);
 | |
| 	    if (*s)
 | |
| 		s++;
 | |
| 	} else
 | |
| 	    s++;
 | |
| 
 | |
|     return t;
 | |
| }
 | |
| 
 | |
| /* Remove one of every pair of single quotes, without copying. Return
 | |
|  * the number of removed quotes. */
 | |
| 
 | |
| /**/
 | |
| mod_export int
 | |
| remsquote(char *s)
 | |
| {
 | |
|     int ret = 0, qa = (isset(RCQUOTES) ? 1 : 3);
 | |
|     char *t = s;
 | |
| 
 | |
|     while (*s)
 | |
| 	if (qa == 1 ?
 | |
|             (s[0] == '\'' && s[1] == '\'') :
 | |
|             (s[0] == '\'' && s[1] == '\\' && s[2] == '\'' && s[3] == '\'')) {
 | |
|             ret += qa;
 | |
|             *t++ = '\'';
 | |
|             s += qa + 1;
 | |
| 	} else
 | |
| 	    *t++ = *s++;
 | |
|     *t = '\0';
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* This should probably be moved into tokenize(). */
 | |
| 
 | |
| /**/
 | |
| mod_export char *
 | |
| ctokenize(char *p)
 | |
| {
 | |
|     char *r = p;
 | |
|     int bslash = 0;
 | |
| 
 | |
|     tokenize(p);
 | |
| 
 | |
|     for (p = r; *p; p++) {
 | |
| 	if (*p == '\\')
 | |
| 	    bslash = 1;
 | |
| 	else {
 | |
| 	    if (*p == '$' || *p == '{' || *p == '}') {
 | |
| 		if (bslash)
 | |
| 		    p[-1] = Bnull;
 | |
| 		else
 | |
| 		    *p = (*p == '$' ? String :
 | |
| 			  (*p == '{' ? Inbrace : Outbrace));
 | |
| 	    }
 | |
| 	    bslash = 0;
 | |
| 	}
 | |
|     }
 | |
|     return r;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * This function reconstructs the full completion argument in
 | |
|  * heap memory by concatenating and, if untok is non-zero, untokenizing
 | |
|  * the ignored prefix and the active prefix and suffix.
 | |
|  * (It appears from the function that the ignored prefix won't
 | |
|  * be tokenized but I haven't checked this.)
 | |
|  * ipl and/or pl may be passed and if so will be set to the ignored
 | |
|  * prefix length and active prefix length respectively.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export char *
 | |
| comp_str(int *ipl, int *pl, int untok)
 | |
| {
 | |
|     char *p = dupstring(compprefix);
 | |
|     char *s = dupstring(compsuffix);
 | |
|     char *ip = dupstring(compiprefix);
 | |
|     char *str;
 | |
|     int lp, ls, lip;
 | |
| 
 | |
|     if (!untok) {
 | |
| 	ctokenize(p);
 | |
| 	remnulargs(p);
 | |
| 	ctokenize(s);
 | |
| 	remnulargs(s);
 | |
|     }
 | |
|     lp = strlen(p);
 | |
|     ls = strlen(s);
 | |
|     lip = strlen(ip);
 | |
|     str = zhalloc(lip + lp + ls + 1);
 | |
|     strcpy(str, ip);
 | |
|     strcat(str, p);
 | |
|     strcat(str, s);
 | |
| 
 | |
|     if (ipl)
 | |
| 	*ipl = lip;
 | |
|     if (pl)
 | |
| 	*pl = lp;
 | |
| 
 | |
|     return str;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export char *
 | |
| comp_quoting_string(int stype)
 | |
| {
 | |
|     switch (stype)
 | |
|     {
 | |
|     case QT_SINGLE:
 | |
| 	return "'";
 | |
|     case QT_DOUBLE:
 | |
| 	return "\"";
 | |
|     case QT_DOLLARS:
 | |
| 	return "$'";
 | |
|     default:			/* shuts up compiler */
 | |
| 	return "\\";
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This is the code behind compset -q, which splits the
 | |
|  * the current word as if it were a command line.
 | |
|  *
 | |
|  * This is one of those completion functions that merits the
 | |
|  * coveted title "not just ordinarily horrific".
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| set_comp_sep(void)
 | |
| {
 | |
|     /*
 | |
|      * s: full (reconstructed) completion argument
 | |
|      * lip: ignored prefix length
 | |
|      * lp: active prefix length
 | |
|      * 1: the number "one" => untokenize
 | |
|      */
 | |
|     int lip, lp;
 | |
|     char *s = comp_str(&lip, &lp, 1);
 | |
|     LinkList foo = newlinklist();
 | |
|     LinkNode n;
 | |
|     /* Save word position */
 | |
|     int owe = we, owb = wb;
 | |
|     /*
 | |
|      * Values of word beginning and end and cursor after subtractions
 | |
|      * due to separators.   I think these are indexes into zlemetaline,
 | |
|      * but with some subtractions; they don't see to be indexes into
 | |
|      * s, which is the current argument before quote stripping.
 | |
|      */
 | |
|     int swb, swe, scs;
 | |
|     /* Offset into current word after subtractions. */
 | |
|     int soffs;
 | |
|     /* Current state of error suppression. */
 | |
|     int ne = noerrs;
 | |
|     /* Length of tmp string */
 | |
|     int tl;
 | |
|     /* flag that we've got the current completion word, perhaps? */
 | |
|     int got = 0;
 | |
|     /*
 | |
|      * i starts off as the number of the completion word we're looking at,
 | |
|      * which is why it's initialised, but is then recycled as a
 | |
|      * loop variable.  j is always a loop variable.
 | |
|      */
 | |
|     int i = 0, j;
 | |
|     /*
 | |
|      * cur: completion word currently being completed (0 offset).
 | |
|      * sl: length of string s, the string we're manipulating.
 | |
|      * css: modification of offset into current word beyond cursor
 | |
|      * position due to the effects of backslashing, counted during our first
 | |
|      * examination of compqstack for double quotes and dollar quotes.
 | |
|      * However, for some reason, when the current quoting scheme is
 | |
|      * backslashing we modify swb directly later rather than counting it at
 | |
|      * the point we remove the backquotes.
 | |
|      */
 | |
|     int cur = -1, sl, css = 0;
 | |
|     /*
 | |
|      * Flag that we're doing the thing with backslashes mentioned
 | |
|      * for css.
 | |
|      */
 | |
|     int remq = 0;
 | |
|     /*
 | |
|      * dq: backslash-removals for double quotes
 | |
|      * odq: value of dq before modification for active (Bnull'ed)
 | |
|      *      backslashes, or something.
 | |
|      * sq: quote-removals for single quotes; either RCQUOTES or '\'' which
 | |
|      *     are specially handled (but currently only if RCQUOTES is not
 | |
|      *     set, which isn't necessarily correct if the quotes were typed by
 | |
|      *     the user).
 | |
|      * osq: c.f. odq, taking account of Snull's and embedded "'"'s.
 | |
|      * qttype: type of quotes using standard QT_* definitions.
 | |
|      * lsq: when quoting is single quotes (QT_SINGLE), counts the offset
 | |
|      *      adjustment needed in the word being examined in the lexer loop.
 | |
|      * sqq: the value of lsq for the current completion word.
 | |
|      * qa:  not, unfortunately, a question and answer session with the
 | |
|      *      original author, but the number of characters being removed
 | |
|      *      when stripping single quotes: 1 for RCQUOTES, 3 otherwise
 | |
|      *      (because we leave a "'" in the final string).
 | |
|      */
 | |
|     int dq = 0, odq, sq = 0, qttype, sqq = 0, lsq = 0, qa = 0;
 | |
|     /* dolq: like sq and dq but for dollars quoting. */
 | |
|     int dolq = 0;
 | |
|     /* remember some global variable values (except lp is local) */
 | |
|     int ois = instring, oib = inbackt, noffs = lp, ona = noaliases;
 | |
|     /*
 | |
|      * tmp: used for temporary processing of strings
 | |
|      * p: loop pointer for tmp etc.
 | |
|      * ns: holds yet another version of the current completion string,
 | |
|      *     goodness knows how it differs from s, tmp, ts, ...
 | |
|      * ts: untokenized ns
 | |
|      * ol: saves old metafied editing line
 | |
|      * sav: save character when NULLed; careful, there's a nested
 | |
|      *      definition of sav just to keep you on your toes
 | |
|      * qp, qs: prefix and suffix strings deduced from s.
 | |
|      */
 | |
|     char *tmp, *p, *ns, *ts, *ol, sav, *qp, *qs;
 | |
| 
 | |
|     METACHECK();
 | |
| 
 | |
|     s += lip;
 | |
|     wb += lip;
 | |
|     untokenize(s);
 | |
| 
 | |
|     swb = swe = soffs = 0;
 | |
|     ns = NULL;
 | |
| 
 | |
|     /* Put the string in the lexer buffer and call the lexer to *
 | |
|      * get the words we have to expand.                        */
 | |
|     zle_save_positions();
 | |
|     ol = zlemetaline;
 | |
|     addedx = 1;
 | |
|     noerrs = 1;
 | |
|     zcontext_save();
 | |
|     lexflags = LEXFLAGS_ZLE;
 | |
|     /*
 | |
|      * tl is the length of the temporary string including
 | |
|      * the space at the start and the x at the cursor position,
 | |
|      * but not the NULL byte.
 | |
|      */
 | |
|     tl = strlen(s) + 2;
 | |
|     tmp = (char *) zhalloc(tl + 1);
 | |
|     tmp[0] = ' ';
 | |
|     memcpy(tmp + 1, s, noffs);
 | |
|     tmp[(scs = zlemetacs = 1 + noffs)] = 'x';
 | |
|     strcpy(tmp + 2 + noffs, s + noffs);
 | |
| 
 | |
|     switch ((qttype = *compqstack)) {
 | |
|     case QT_BACKSLASH:
 | |
|         remq = 1;
 | |
| 	tmp = rembslash(tmp);
 | |
|         break;
 | |
| 
 | |
|     case QT_SINGLE:
 | |
|         if (isset(RCQUOTES))
 | |
|             qa = 1;
 | |
|         else
 | |
|             qa = 3;
 | |
|         sq = remsquote(tmp);
 | |
|         break;
 | |
| 
 | |
|     case QT_DOUBLE:
 | |
|         for (j = 0, p = tmp; *p; p++, j++)
 | |
| 	    /*
 | |
| 	     * I added the handling for " here: before it just handled
 | |
| 	     * backslashes.  This meant that a \" inside a " wasn't
 | |
| 	     * handled properly.  I presume that was an oversight.
 | |
| 	     * I don't know if this is the right place to fix this
 | |
| 	     * particular problem because I'm utterly confused by
 | |
| 	     * the structure of the code in this function.
 | |
| 	     */
 | |
|             if (*p == '\\' && (p[1] == '\\' || p[1] == '"')) {
 | |
| 		dq++;
 | |
|                 chuck(p);
 | |
| 		if (*p == '"')
 | |
| 		    zlemetacs--;
 | |
| 		else if (j > zlemetacs) {
 | |
|                     zlemetacs++;
 | |
|                     css++;
 | |
|                 }
 | |
|                 if (!*p)
 | |
|                     break;
 | |
|             }
 | |
| 	break;
 | |
| 
 | |
|     case QT_DOLLARS:
 | |
| 	j = zlemetacs;
 | |
| 	tmp = getkeystring(tmp, &sl,
 | |
| 			   GETKEY_DOLLAR_QUOTE|GETKEY_UPDATE_OFFSET,
 | |
| 			   &zlemetacs);
 | |
| 	/* The number of bytes we removed because of $' quoting */
 | |
| 	dolq = tl - sl;
 | |
| 	/* Offset into the word is modified, too... */
 | |
| 	css += zlemetacs - j;
 | |
| 	break;
 | |
| 
 | |
|     case QT_NONE:
 | |
|     default: /* to silence compiler warnings */
 | |
| #ifdef DEBUG
 | |
| 	dputs("BUG: head of compqstack is NULL");
 | |
| #endif
 | |
| 	break;
 | |
| 
 | |
|     }
 | |
|     odq = dq;
 | |
|     inpush(dupstrspace(tmp), 0, NULL);
 | |
|     zlemetaline = tmp;
 | |
|     /*
 | |
|      * tl is the length of temporary string, calculated above.
 | |
|      * It seems zlemetall need not include the 'x' added at the cursor.
 | |
|      * addedx is taken care of in function gotword() (lex.c).
 | |
|      */
 | |
|     zlemetall = tl - addedx;
 | |
|     strinbeg(0);
 | |
|     noaliases = 1;
 | |
|     do {
 | |
| 	ctxtlex();
 | |
| 	if (tok == LEXERR) {
 | |
| 	    int j;
 | |
| 
 | |
| 	    if (!tokstr)
 | |
| 		break;
 | |
| 	    /*
 | |
| 	     * If there was an error, it may be because we're in
 | |
| 	     * an unterminated string.  Count the active quote
 | |
| 	     * characters to see.  We need an odd number.
 | |
| 	     * This works for $', too, since the ' there is an Snull.
 | |
| 	     */
 | |
| 	    for (j = 0, p = tokstr; *p; p++) {
 | |
| 		if (*p == Snull || *p == Dnull)
 | |
| 		    j++;
 | |
|             }
 | |
| 	    if (j & 1) {
 | |
| 		tok = STRING;
 | |
| 		if (p > tokstr && p[-1] == ' ')
 | |
| 		    p[-1] = '\0';
 | |
| 	    }
 | |
| 	}
 | |
| 	if (tok == ENDINPUT)
 | |
| 	    break;
 | |
| 	if (tokstr && *tokstr) {
 | |
|             for (p = tokstr; dq && *p; p++) {
 | |
|                 if (*p == Bnull) {
 | |
|                     dq--;
 | |
|                     if (p[1] == '\\')
 | |
|                         dq--;
 | |
|                 }
 | |
|             }
 | |
|             if (qttype == QT_SINGLE) {
 | |
|                 for (p = tokstr, lsq = 0; *p; p++) {
 | |
|                     if (sq && *p == Snull)
 | |
|                         sq -= qa;
 | |
|                     if (*p == '\'') {
 | |
|                         sq -= qa;
 | |
|                         lsq += qa;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|                 lsq = 0;
 | |
| 	    addlinknode(foo, (p = ztrdup(tokstr)));
 | |
|         }
 | |
| 	else
 | |
| 	    p = NULL;
 | |
| 	if (!got && !lexflags) {
 | |
| 	    DPUTS(!p, "no current word in substr");
 | |
| 	    got = 1;
 | |
| 	    cur = countlinknodes(foo) - 1;  /* cur is 0 offset */
 | |
| 	    swb = wb - dq - sq - dolq;
 | |
| 	    swe = we - dq - sq - dolq;
 | |
|             sqq = lsq;
 | |
| 	    soffs = zlemetacs - swb - css;
 | |
| 	    DPUTS2(p[soffs] != 'x', "expecting 'x' at offset %d of \"%s\"",
 | |
| 		   soffs, p);
 | |
| 	    chuck(p + soffs);
 | |
| 	    ns = dupstring(p);
 | |
| 	}
 | |
| 	i++;
 | |
|     } while (tok != ENDINPUT && tok != LEXERR);
 | |
|     noaliases = ona;
 | |
|     strinend();
 | |
|     inpop();
 | |
|     errflag &= ~ERRFLAG_ERROR;
 | |
|     noerrs = ne;
 | |
|     zcontext_restore();
 | |
|     wb = owb;
 | |
|     we = owe;
 | |
|     zlemetaline = ol;
 | |
|     zle_restore_positions();
 | |
|     if (cur < 0 || i < 1)
 | |
| 	return 1;
 | |
|     owb = offs;
 | |
|     offs = soffs;
 | |
|     if ((p = check_param(ns, 0, 1))) {
 | |
| 	for (p = ns; *p; p++)
 | |
| 	    if (*p == Dnull)
 | |
| 		*p = '"';
 | |
| 	    else if (*p == Snull)
 | |
| 		*p = '\'';
 | |
|     }
 | |
|     offs = owb;
 | |
| 
 | |
|     untokenize(ts = dupstring(ns));
 | |
| 
 | |
|     if (*ns == Snull || *ns == Dnull ||
 | |
| 	((*ns == String || *ns == Qstring) && ns[1] == Snull)) {
 | |
| 	char *tsptr = ts, *nsptr = ns, sav;
 | |
| 	switch (*ns) {
 | |
| 	case Snull:
 | |
| 	    instring = QT_SINGLE;
 | |
| 	    break;
 | |
| 
 | |
| 	case Dnull:
 | |
| 	    instring = QT_DOUBLE;
 | |
| 	    break;
 | |
| 
 | |
| 	default:
 | |
| 	    instring = QT_DOLLARS;
 | |
| 	    nsptr++;
 | |
| 	    tsptr++;
 | |
| 	    swb++;
 | |
| 	    break;
 | |
| 	}
 | |
| 
 | |
| 	inbackt = 0;
 | |
| 	swb++;
 | |
| 	if (nsptr[strlen(nsptr) - 1] == *nsptr && nsptr[1])
 | |
| 	    swe--;
 | |
| 	zsfree(autoq);
 | |
| 	sav = *++tsptr;
 | |
| 	*tsptr = '\0';
 | |
| 	autoq = ztrdup(compqstack[1] ? "" : multiquote(ts, 1));
 | |
| 	*(ts = tsptr) = sav;
 | |
|     } else {
 | |
| 	instring = QT_NONE;
 | |
| 	zsfree(autoq);
 | |
| 	autoq = NULL;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * In the following loop we look for parse quotes yet again.
 | |
|      * I don't really have the faintest idea why, but given that
 | |
|      * ns is immediately reassigned from ts afterwards (why? what's
 | |
|      * wrong with it being in ts?) and scs isn't used again, I
 | |
|      * presume it's in aid of getting the indexes for word beginning
 | |
|      * (swb) and start offset (soffs) into s correct.
 | |
|      *
 | |
|      * I think soffs is an index into s, while swb and scs are indexes
 | |
|      * into the full line but with some jiggery pokery for quote removal.
 | |
|      */
 | |
|     for (p = ns, i = swb; *p; p++, i++) {
 | |
| 	if (inull(*p)) {
 | |
| 	    if (i < scs) {
 | |
| 		if (*p == Bnull) {
 | |
|                     if (p[1] && remq)
 | |
|                         swb -= 2;
 | |
|                     if (odq) {
 | |
|                         swb--;
 | |
|                         if (p[1] == '\\')
 | |
|                             swb--;
 | |
|                     }
 | |
|                 }
 | |
| 	    }
 | |
| 	    if (p[1] || *p != Bnull) {
 | |
| 		if (*p == Bnull) {
 | |
| 		    if (scs == i + 1)
 | |
| 			scs++, soffs++;
 | |
| 		} else {
 | |
| 		    if (scs > i--)
 | |
| 			scs--;
 | |
| 		}
 | |
| 	    } else {
 | |
| 		if (scs == swe)
 | |
| 		    scs--;
 | |
| 	    }
 | |
| 	    chuck(p--);
 | |
| 	}
 | |
|     }
 | |
|     ns = ts;
 | |
| 
 | |
|     if (instring && strchr(compqstack, QT_BACKSLASH)) {
 | |
| 	int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1]));
 | |
| 
 | |
| 	if (ql > rl)
 | |
| 	    swb -= ql - rl;
 | |
|     }
 | |
|     /*
 | |
|      * Using the word beginning and end as an index into the reconstructed
 | |
|      * string s, swb and swe, we can get the strings before and after
 | |
|      * the word we're considering.
 | |
|      *
 | |
|      * Because it would be too easy otherwise, there are random
 | |
|      * additional subtractions to be made.  The 1 might be something
 | |
|      * to do with the space that appeared mysteriously at the start of the
 | |
|      * line when we passed it through the lexer.  The sqq is to do with
 | |
|      * the single quote quoting when we passed it through the lexer.
 | |
|      *
 | |
|      * TODO: I added the "+ dq" because it seemed to improve matters for
 | |
|      * double quoting but the fact it's arrived at in a rather different way
 | |
|      * from sqq may indicate this is wrong.  $'...' may need something, too.
 | |
|      */
 | |
|     sav = s[(i = swb - 1 - sqq + dq)];
 | |
|     s[i] = '\0';
 | |
|     qp = (qttype == QT_SINGLE) ? dupstring(s) : rembslash(s);
 | |
|     s[i] = sav;
 | |
|     if (swe < swb)
 | |
| 	swe = swb;
 | |
|     swe--;
 | |
|     sl = strlen(s);
 | |
|     if (swe > sl) {
 | |
| 	swe = sl;
 | |
| 	if ((int)strlen(ns) > swe - swb + 1)
 | |
| 	    ns[swe - swb + 1] = '\0';
 | |
|     }
 | |
|     qs = (qttype == QT_SINGLE) ? dupstring(s + swe) : rembslash(s + swe);
 | |
|     sl = strlen(ns);
 | |
|     if (soffs > sl)
 | |
| 	soffs = sl;
 | |
|     if (qttype == QT_SINGLE) {
 | |
|         remsquote(qp);
 | |
|         remsquote(qs);
 | |
|     }
 | |
|     {
 | |
| 	int set = CP_QUOTE | CP_QUOTING, unset = 0;
 | |
| 
 | |
| 	tl = strlen(compqstack);
 | |
| 	p = zalloc(tl + 2);
 | |
| 	*p = (char)(instring == QT_NONE ? QT_BACKSLASH : instring);
 | |
| 	memcpy(p+1, compqstack, tl);
 | |
| 	p[tl+1] = '\0';
 | |
| 	zsfree(compqstack);
 | |
| 	compqstack = p;
 | |
| 
 | |
| 	zsfree(compquote);
 | |
| 	zsfree(compquoting);
 | |
| 	switch (instring) {
 | |
| 	case QT_DOUBLE:
 | |
| 	    compquote = "\"";
 | |
| 	    compquoting = "double";
 | |
| 	    break;
 | |
| 
 | |
| 	case QT_SINGLE:
 | |
| 	    compquote = "'";
 | |
| 	    compquoting = "single";
 | |
| 	    break;
 | |
| 
 | |
| 	case QT_DOLLARS:
 | |
| 	    compquote = "$'";
 | |
| 	    compquoting = "dollars";
 | |
| 	    break;
 | |
| 
 | |
| 	default:
 | |
| 	    compquote = compquoting = "";
 | |
| 	    unset = set;
 | |
| 	    set = 0;
 | |
| 	    break;
 | |
| 	}
 | |
| 	compquote = ztrdup(compquote);
 | |
| 	compquoting = ztrdup(compquoting);
 | |
| 	comp_setunset(0, 0, set, unset);
 | |
| 
 | |
| 	zsfree(compprefix);
 | |
| 	zsfree(compsuffix);
 | |
| 	if (unset(COMPLETEINWORD)) {
 | |
| 	    untokenize(ns);
 | |
| 	    compprefix = ztrdup(ns);
 | |
| 	    compsuffix = ztrdup("");
 | |
| 	} else {
 | |
| 	    char *ss, sav;
 | |
| 	    
 | |
| 	    ss = ns + soffs;
 | |
| 
 | |
| 	    sav = *ss;
 | |
| 	    *ss = '\0';
 | |
| 	    untokenize(ns);
 | |
| 	    compprefix = ztrdup(ns);
 | |
| 	    *ss = sav;
 | |
| 	    untokenize(ss);
 | |
| 	    compsuffix = ztrdup(ss);
 | |
| 	}
 | |
|         if ((i = strlen(compprefix)) > 1 && compprefix[i - 1] == '\\' &&
 | |
| 	    compprefix[i - 2] != '\\' && compprefix[i - 2] != Meta)
 | |
|             compprefix[i - 1] = '\0';
 | |
|         
 | |
| 	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("");
 | |
| 	freearray(compwords);
 | |
| 	i = countlinknodes(foo);
 | |
| 	compwords = (char **) zalloc((i + 1) * sizeof(char *));
 | |
| 	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
 | |
| 	    p = compwords[i] = (char *) getdata(n);
 | |
| 	    untokenize(p);
 | |
| 	}
 | |
| 	/* The current position shouldn't exceed the new word count */
 | |
| 	if ((compcurrent = cur + 1) > i) {
 | |
| 	    DPUTS2(1, "compcurrent=%d > number_of_words=%d", compcurrent, i);
 | |
| 	    compcurrent = i;
 | |
| 	}
 | |
| 	compwords[i] = NULL;
 | |
|     }
 | |
|     instring = ois;
 | |
|     inbackt = oib;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* This stores the strings from the list in an array. */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| set_list_array(char *name, LinkList l)
 | |
| {
 | |
|     setaparam(name, zlinklist2array(l, 1));
 | |
| }
 | |
| 
 | |
| /* Get the words from a variable or a (list of words). */
 | |
| 
 | |
| /**/
 | |
| mod_export char **
 | |
| get_user_var(char *nam)
 | |
| {
 | |
|     if (!nam)
 | |
| 	return NULL;
 | |
|     else if (*nam == '(') {
 | |
| 	/* It's a (...) list, not a parameter name. */
 | |
| 	char *ptr, *s, **uarr, **aptr;
 | |
| 	int count = 0, notempty = 0, brk = 0;
 | |
| 	LinkList arrlist = newlinklist();
 | |
| 
 | |
| 	ptr = dupstring(nam);
 | |
| 	s = ptr + 1;
 | |
| 	while (*++ptr) {
 | |
| 	    if (*ptr == '\\' && ptr[1])
 | |
| 		chuck(ptr), notempty = 1;
 | |
| 	    else if (*ptr == ',' || inblank(*ptr) || *ptr == ')') {
 | |
| 		if (*ptr == ')')
 | |
| 		    brk++;
 | |
| 		if (notempty) {
 | |
| 		    *ptr = '\0';
 | |
| 		    count++;
 | |
| 		    if (*s == '\n')
 | |
| 			s++;
 | |
| 		    addlinknode(arrlist, s);
 | |
| 		}
 | |
| 		s = ptr + 1;
 | |
| 		notempty = 0;
 | |
| 	    } else {
 | |
| 		notempty = 1;
 | |
| 		if (*ptr == Meta)
 | |
| 		    ptr++;
 | |
| 	    }
 | |
| 	    if (brk)
 | |
| 		break;
 | |
| 	}
 | |
| 	if (!brk || !count)
 | |
| 	    return NULL;
 | |
| 	*ptr = '\0';
 | |
| 	aptr = uarr = (char **) zhalloc(sizeof(char *) * (count + 1));
 | |
| 
 | |
| 	while ((*aptr++ = (char *)ugetnode(arrlist)));
 | |
| 	uarr[count] = NULL;
 | |
| 	return uarr;
 | |
|     } else {
 | |
| 	/* Otherwise it should be a parameter name. */
 | |
| 	char **arr = NULL, *val;
 | |
| 
 | |
| 	queue_signals();
 | |
| 	if ((arr = getaparam(nam)) || (arr = gethparam(nam)))
 | |
| 	    arr = (incompfunc ? arrdup(arr) : arr);
 | |
| 	else if ((val = getsparam(nam))) {
 | |
| 	    arr = (char **) zhalloc(2*sizeof(char *));
 | |
| 	    arr[0] = (incompfunc ? dupstring(val) : val);
 | |
| 	    arr[1] = NULL;
 | |
| 	}
 | |
| 	unqueue_signals();
 | |
| 	return arr;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * If KEYS, then NAME is an associative array; return its keys.
 | |
|  * Else, NAME is a plain array; return its elements.
 | |
|  */
 | |
| 
 | |
| static char **
 | |
| get_data_arr(char *name, int keys)
 | |
| {
 | |
|     struct value vbuf;
 | |
|     char **ret;
 | |
|     Value v;
 | |
| 
 | |
|     queue_signals();
 | |
|     if (!(v = fetchvalue(&vbuf, &name, 1,
 | |
| 			 (keys ? SCANPM_WANTKEYS : SCANPM_WANTVALS) |
 | |
| 			 SCANPM_MATCHMANY)))
 | |
| 	ret = NULL;
 | |
|     else
 | |
| 	ret = getarrvalue(v);
 | |
|     unqueue_signals();
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void
 | |
| addmatch(char *str, int flags, char ***dispp, int line)
 | |
| {
 | |
|     Cmatch cm = (Cmatch) zhalloc(sizeof(struct cmatch));
 | |
|     char **disp = *dispp;
 | |
| 
 | |
|     memset(cm, 0, sizeof(struct cmatch));
 | |
|     cm->str = dupstring(str);
 | |
|     cm->flags = (flags |
 | |
|                  (complist ?
 | |
|                   ((strstr(complist, "packed") ? CMF_PACKED : 0) |
 | |
|                    (strstr(complist, "rows")   ? CMF_ROWS   : 0)) : 0));
 | |
|     if (disp) {
 | |
|         if (!*++disp)
 | |
|             disp = NULL;
 | |
|         if (disp)
 | |
|             cm->disp = dupstring(*disp);
 | |
|     } else if (line) {
 | |
|         cm->disp = dupstring("");
 | |
|         cm->flags |= CMF_DISPLINE;
 | |
|     }
 | |
|     mnum++;
 | |
|     ainfo->count++;
 | |
|     if (curexpl)
 | |
|         curexpl->count++;
 | |
| 
 | |
|     addlinknode(matches, cm);
 | |
| 
 | |
|     newmatches = 1;
 | |
|     mgroup->new = 1;
 | |
| 
 | |
|     *dispp = disp;
 | |
| }
 | |
| 
 | |
| /* This is used by compadd to add a couple of matches. The arguments are
 | |
|  * the strings given via options. The last argument is the array with
 | |
|  * the matches. */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| addmatches(Cadata dat, char **argv)
 | |
| {
 | |
|     /* ms: "match string" - string to use as completion.
 | |
|      * Overloaded at one place as a temporary. */
 | |
|     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, *ibuf = NULL;
 | |
|     char **arrays = NULL;
 | |
|     int dind, lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
 | |
|     int ppl = 0, psl = 0, ilen = 0;
 | |
|     int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
 | |
|     int isexact, doadd, ois = instring, oib = inbackt;
 | |
|     Cline lc = NULL, pline = NULL, sline = NULL;
 | |
|     struct cmlist mst;
 | |
|     Cmlist oms = mstack;
 | |
|     Patprog cp = NULL, *pign = NULL;
 | |
|     LinkList aparl = NULL, oparl = NULL, *dparl = NULL;
 | |
|     Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl;
 | |
|     Heap oldheap;
 | |
| 
 | |
|     SWITCHHEAPS(oldheap, compheap) {
 | |
|         if (dat->dummies >= 0)
 | |
|             dat->aflags = ((dat->aflags | CAF_NOSORT | CAF_UNIQCON) &
 | |
|                            ~CAF_UNIQALL);
 | |
| 
 | |
|         /* Select the group in which to store the matches. */
 | |
|         gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT  : 0) |
 | |
|                   ((dat->aflags & CAF_MATSORT) ? CGF_MATSORT : 0) |
 | |
|                   ((dat->aflags & CAF_NUMSORT) ? CGF_NUMSORT : 0) |
 | |
|                   ((dat->aflags & CAF_REVSORT) ? CGF_REVSORT : 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 (dat->mesg || dat->exp) {
 | |
|             curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
 | |
|             curexpl->always = !!dat->mesg;
 | |
|             curexpl->count = curexpl->fcount = 0;
 | |
|             curexpl->str = dupstring(dat->mesg ? dat->mesg : dat->exp);
 | |
|             if (dat->mesg && !dat->dpar && !dat->opar && !dat->apar)
 | |
|                 addexpl(1);
 | |
|         } else
 | |
|             curexpl = NULL;
 | |
|     } SWITCHBACKHEAPS(oldheap);
 | |
| 
 | |
|     if (!*argv && !dat->dummies && !(dat->aflags & CAF_ALL))
 | |
| 	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)
 | |
| 	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
 | |
| 
 | |
|     if (dat->flags & CMF_ISPAR)
 | |
| 	dat->flags |= parflags;
 | |
|     if (compquote && (qc = *compquote)) {
 | |
| 	if (qc == '`') {
 | |
| 	    instring = QT_NONE;
 | |
| 	    /*
 | |
| 	     * Yes, inbackt has always been set to zero here.  I'm
 | |
| 	     * sure there's a simple explanation.
 | |
| 	     */
 | |
| 	    inbackt = 0;
 | |
| 	    autoq = "";
 | |
| 	} else {
 | |
| 	    switch (qc) {
 | |
| 	    case '\'':
 | |
| 		instring = QT_SINGLE;
 | |
| 		break;
 | |
| 
 | |
| 	    case '"':
 | |
| 		instring = QT_DOUBLE;
 | |
| 		break;
 | |
| 
 | |
| 	    case '$':
 | |
| 		instring = QT_DOLLARS;
 | |
| 		break;
 | |
| 	    }
 | |
| 	    inbackt = 0;
 | |
| 	    autoq = multiquote(*compquote == '$' ? compquote+1 : compquote, 1);
 | |
| 	}
 | |
|     } else {
 | |
| 	instring = QT_NONE;
 | |
| 	inbackt = 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(oldheap, compheap) {
 | |
| 	if ((doadd = (!dat->apar && !dat->opar && !dat->dpar))) {
 | |
| 	    if (dat->aflags & CAF_MATCH)
 | |
| 		hasmatched = 1;
 | |
| 	    else
 | |
| 		hasunmatched = 1;
 | |
| 	}
 | |
| 	if (dat->apar)
 | |
| 	    aparl = newlinklist();
 | |
| 	if (dat->opar)
 | |
| 	    oparl = newlinklist();
 | |
| 	if (dat->dpar) {
 | |
| 	    int darr = 0, dparlen = arrlen(dat->dpar);
 | |
| 	    char **tail = dat->dpar + dparlen;
 | |
| 
 | |
| 	    dparr = (char ***)hcalloc((1 + dparlen) * sizeof(char **));
 | |
| 	    dparl = (LinkList *)hcalloc((1 + dparlen) * sizeof(LinkList));
 | |
| 	    queue_signals();
 | |
| 	    while (darr < dparlen) {
 | |
| 		if ((dparr[darr] = getaparam(dat->dpar[darr])) && *dparr[darr]) {
 | |
| 		    dparr[darr] = arrdup(dparr[darr]);
 | |
| 		    dparl[darr++] = newlinklist();
 | |
| 		} else {
 | |
| 		    /* swap in the last -D argument if we didn't get a non-empty array */
 | |
| 		    dat->dpar[darr] = *--tail;
 | |
| 		    *tail = NULL;
 | |
| 		    --dparlen;
 | |
| 	        }
 | |
| 	    }
 | |
| 	    unqueue_signals();
 | |
| 	}
 | |
| 	/* Store the matcher in our stack of matchers. */
 | |
| 	if (dat->match) {
 | |
| 	    mst.next = mstack;
 | |
| 	    mst.matcher = dat->match;
 | |
| 	    mstack = &mst;
 | |
| 
 | |
| 	    add_bmatchers(dat->match);
 | |
| 
 | |
| 	    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;
 | |
| 	    }
 | |
| 	    *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);
 | |
| 
 | |
| 	    if (llpl + (int)strlen(compqiprefix) + (int)strlen(lipre) != origlpre
 | |
| 	     || llsl + (int)strlen(compqisuffix) + (int)strlen(lisuf) != origlsuf)
 | |
| 		lenchanged = 1;
 | |
| 
 | |
| 	    /* Test if there is an existing -P prefix. */
 | |
| 	    if (dat->pre && *dat->pre) {
 | |
| 		int prefix_length = pfxlen(dat->pre, lpre);
 | |
| 		if (dat->pre[prefix_length] == '\0' ||
 | |
| 		    lpre[prefix_length] == '\0') {
 | |
| 		    /* $compadd_args[-P] is a prefix of ${PREFIX}, or
 | |
| 		     * vice-versa. */
 | |
| 		    llpl -= prefix_length;
 | |
| 		    lpre += prefix_length;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	/* 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;
 | |
| 		}
 | |
| 	    }
 | |
| 	    if ((s = 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;
 | |
| 		    llpl -= ml;
 | |
| 		    bcp = ml;
 | |
| 		    bpadd = strlen(s) - ml;
 | |
| 		} else {
 | |
| 		    if (llpl <= lpl && strpfx(lpre, s))
 | |
| 			lpre = dupstring("");
 | |
| 		    else if (llpl > lpl && strpfx(s, lpre))
 | |
| 			lpre += lpl;
 | |
| 		    else
 | |
| 			*argv = NULL;
 | |
| 		    bcp = lpl;
 | |
| 		}
 | |
| 	    }
 | |
| 	    if ((s = 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';
 | |
| 		    llsl -= ml;
 | |
| 		    bcs = ml;
 | |
| 		    bsadd = strlen(s) - ml;
 | |
| 		} else {
 | |
| 		    if (llsl <= lsl && strsfx(lsuf, s))
 | |
| 			lsuf = dupstring("");
 | |
| 		    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 + gfl] = 'x';
 | |
| 		strcpy(tmp + llpl + gfl + is, lsuf);
 | |
| 
 | |
| 		tokenize(tmp);
 | |
| 		if (haswilds(tmp)) {
 | |
| 		    if (is)
 | |
| 			tmp[llpl + gfl] = Star;
 | |
| 		    remnulargs(tmp);
 | |
| 		    if ((cp = patcompile(tmp, 0, NULL)))
 | |
| 			haspattern = 1;
 | |
| 		}
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    /*
 | |
| 	     * (This is called a "comment".  Given you've been
 | |
| 	     * spending your time reading the completion code, you
 | |
| 	     * may have forgotten what one is.  It's used to deconfuse
 | |
| 	     * the poor so-and-so who's landed up having to maintain
 | |
| 	     * the code.)
 | |
| 	     *
 | |
| 	     * So what's going on here then?  I'm glad you asked.  To test
 | |
| 	     * whether we should start menu completion, we test whether
 | |
| 	     * compstate[insert] has been set to "menu", but only if we found
 | |
| 	     * patterns in the code.  It's not clear to me from the
 | |
| 	     * documentation why the second condition would apply, but sure
 | |
| 	     * enough if I remove it the test suite falls over.  (Testing
 | |
| 	     * comppatmatch at the later point doesn't work because compstate
 | |
| 	     * is likely to have been reset by the point we actually insert
 | |
| 	     * the completions, after all functions have exited; this is at
 | |
| 	     * least part of the problem.)  In the present case, we are not
 | |
| 	     * doing matching on the code because all the clever stuff has
 | |
| 	     * been done over our heads and we've simply between told to
 | |
| 	     * insert it.  However, we still need to take account of ambiguous
 | |
| 	     * completions properly.  To do this, we rely on the caller to
 | |
| 	     * pass down the same prefix/suffix with the patterns that we
 | |
| 	     * would get if we were doing matching, and test those for
 | |
| 	     * patterns.  This gets us out of the hole apparently without
 | |
| 	     * breaking anything.  The particular case where this is needed is
 | |
| 	     * approximate file completion: this does its own matching but
 | |
| 	     * _approximate still sets the prefix to include the pattern.
 | |
| 	     */
 | |
| 	    if (comppatmatch && *comppatmatch) {
 | |
| 		int pflen = strlen(compprefix);
 | |
| 		char *tmp = zhalloc(pflen + strlen(compsuffix) + 1);
 | |
| 		strcpy(tmp, compprefix);
 | |
| 		strcpy(tmp + pflen, compsuffix);
 | |
| 		tokenize(tmp);
 | |
| 		remnulargs(tmp);
 | |
| 		if (haswilds(tmp))
 | |
| 		    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 = dupstring(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 (dat->aflags & CAF_ARRAYS) {
 | |
| 	    Heap oldheap2;
 | |
| 
 | |
| 	    SWITCHHEAPS(oldheap2, oldheap) {
 | |
| 		arrays = argv;
 | |
| 		argv = NULL;
 | |
| 		while (*arrays &&
 | |
| 		       (!(argv = get_data_arr(*arrays,
 | |
| 					      (dat->aflags & CAF_KEYS))) ||
 | |
| 			!*argv))
 | |
| 		    arrays++;
 | |
| 		arrays++;
 | |
| 		if (!argv) {
 | |
| 		    ms = NULL;
 | |
| 		    argv = &ms;
 | |
| 		}
 | |
| 	    } SWITCHBACKHEAPS(oldheap2);
 | |
| 	}
 | |
| 	if (dat->ppre)
 | |
| 	    ppl = strlen(dat->ppre);
 | |
| 	if (dat->psuf)
 | |
| 	    psl = strlen(dat->psuf);
 | |
| 	for (; (s = *argv); argv++) {
 | |
| 	    int sl;
 | |
| 	    bpl = obpl;
 | |
| 	    bsl = obsl;
 | |
| 	    if (disp) {
 | |
| 		if (!*++disp)
 | |
| 		    disp = NULL;
 | |
| 	    }
 | |
| 	    sl = strlen(s);
 | |
| 	    if (aign || pign) {
 | |
| 		int il = ppl + sl + psl, addit = 1;
 | |
| 
 | |
| 		if (il + 1> ilen)
 | |
| 		    ibuf = (char *) zhalloc((ilen = il) + 2);
 | |
| 
 | |
| 		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 aign, we put it in the alternate set. */
 | |
| 		    char **pt = aign;
 | |
| 		    int filell;
 | |
| 
 | |
| 		    for (; addit && *pt; pt++)
 | |
| 			addit = !((filell = strlen(*pt)) < il &&
 | |
| 				  !strcmp(*pt, ibuf + il - filell));
 | |
| 		}
 | |
| 		if (addit && pign) {
 | |
| 		    Patprog *pt = pign;
 | |
| 
 | |
| 		    for (; addit && *pt; pt++)
 | |
| 			addit = !pattry(*pt, ibuf);
 | |
| 		}
 | |
| 		if (!addit) {
 | |
| 		    compignored++;
 | |
| 		    for (dind = 0; dparl && dparl[dind]; dind++) {
 | |
| 			if (dparr[dind] && !*++dparr[dind])
 | |
| 			    dparr[dind] = NULL;
 | |
| 		    }
 | |
| 		    goto next_array;
 | |
| 		}
 | |
| 	    }
 | |
| 	    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, 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))) {
 | |
| 		for (dind = 0; dparl && dparl[dind]; dind++) {
 | |
| 		    if (dparr[dind] && !*++dparr[dind])
 | |
| 			dparr[dind] = NULL;
 | |
| 		}
 | |
| 		goto next_array;
 | |
| 	    }
 | |
| 	    if (doadd) {
 | |
| 		Cmatch cm;
 | |
| 		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, s, 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) {
 | |
| 		    for (dind = 0; dparl[dind]; dind++) {
 | |
| 			if (dparr[dind]) {
 | |
| 			    addlinknode(dparl[dind], *dparr[dind]);
 | |
| 			    if (!*++dparr[dind])
 | |
| 				dparr[dind] = NULL;
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 		free_cline(lc);
 | |
| 	    }
 | |
| 	next_array:
 | |
| 	    if ((dat->aflags & CAF_ARRAYS) && !argv[1]) {
 | |
| 		Heap oldheap2;
 | |
| 
 | |
| 		SWITCHHEAPS(oldheap2, oldheap) {
 | |
| 		    argv = NULL;
 | |
| 		    while (*arrays &&
 | |
| 			   (!(argv = get_data_arr(*arrays,
 | |
| 						  (dat->aflags & CAF_KEYS))) ||
 | |
| 			    !*argv))
 | |
| 			arrays++;
 | |
| 		    arrays++;
 | |
| 		    if (!argv) {
 | |
| 			ms = NULL;
 | |
| 			argv = &ms;
 | |
| 		    }
 | |
| 		    argv--;
 | |
| 		} SWITCHBACKHEAPS(oldheap2);
 | |
| 	    }
 | |
| 	}
 | |
| 	if (dat->apar)
 | |
| 	    set_list_array(dat->apar, aparl);
 | |
| 	if (dat->opar)
 | |
| 	    set_list_array(dat->opar, oparl);
 | |
| 	if (dat->dpar) {
 | |
| 	    for (dind = 0; dparl[dind]; dind++)
 | |
| 		set_list_array(dat->dpar[dind], dparl[dind]);
 | |
| 	}
 | |
| 	if (dat->exp)
 | |
| 	    addexpl(0);
 | |
| 	if (!hasallmatch && (dat->aflags & CAF_ALL)) {
 | |
|             addmatch("<all>", dat->flags | CMF_ALL, &disp, 1);
 | |
| 	    hasallmatch = 1;
 | |
| 	}
 | |
|         while (dat->dummies-- > 0)
 | |
|             addmatch("", dat->flags | CMF_DUMMY, &disp, 0);
 | |
| 
 | |
|     } SWITCHBACKHEAPS(oldheap);
 | |
| 
 | |
|     /* We switched back to the current heap, now restore the stack of
 | |
|      * matchers. */
 | |
|     mstack = oms;
 | |
| 
 | |
|     instring = ois;
 | |
|     inbackt = oib;
 | |
|     autoq = oaq;
 | |
|     zsfree(qipre);
 | |
|     zsfree(qisuf);
 | |
|     qipre = oqp;
 | |
|     qisuf = oqs;
 | |
| 
 | |
|     if (mnum == nm)
 | |
| 	haspattern = ohp;
 | |
| 
 | |
|     return (mnum == nm);
 | |
| }
 | |
| 
 | |
| /* This adds all the data we have for a match. */
 | |
| 
 | |
| /**/
 | |
| mod_export Cmatch
 | |
| add_match_data(int alt, char *str, char *orig, Cline line,
 | |
| 	       char *ipre, char *ripre, char *isuf,
 | |
| 	       char *pre, char *prpre,
 | |
| 	       char *ppre, Cline pline,
 | |
| 	       char *psuf, Cline sline,
 | |
| 	       char *suf, int flags, int exact)
 | |
| {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     mbstate_t mbs;
 | |
|     char curchar, *t, *f, *fs, *fe, *new_str = NULL;
 | |
|     size_t cnt;
 | |
|     wchar_t wc;
 | |
| #endif
 | |
|     Cmatch cm;
 | |
|     Aminfo ai = (alt ? fainfo : ainfo);
 | |
|     int palen, salen, qipl, ipl, pl, ppl, qisl, isl, psl;
 | |
|     int stl, lpl, lsl, ml;
 | |
| 
 | |
|     palen = salen = qipl = ipl = pl = ppl = qisl = isl = psl = 0;
 | |
| 
 | |
|     DPUTS(!line, "BUG: add_match_data() without cline");
 | |
| 
 | |
|     cline_matched(line);
 | |
|     if (pline)
 | |
| 	cline_matched(pline);
 | |
|     if (sline)
 | |
| 	cline_matched(sline);
 | |
| 
 | |
|     /* If there is a path suffix, we build a cline list for it and
 | |
|      * append it to the list for the match itself. */
 | |
|     if (!sline && psuf)
 | |
| 	salen = (psl = strlen(psuf));
 | |
|     if (isuf)
 | |
| 	salen += (isl = strlen(isuf));
 | |
|     if (qisuf)
 | |
| 	salen += (qisl = strlen(qisuf));
 | |
| 
 | |
|     if (salen) {
 | |
| 	Cline pp, p, s, sl = NULL;
 | |
| 
 | |
| 	for (pp = NULL, p = line; p->next; pp = p, p = p->next);
 | |
| 
 | |
| 	if (psl) {
 | |
| 	    s = bld_parts(psuf, psl, psl, &sl, NULL);
 | |
| 
 | |
| 	    if (sline) {
 | |
| 		Cline sp;
 | |
| 
 | |
| 		sline = cp_cline(sline, 1);
 | |
| 
 | |
| 		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) {
 | |
| 		if (p->prefix) {
 | |
| 		    Cline q;
 | |
| 
 | |
| 		    for (q = p->prefix; q->next; q = q->next);
 | |
| 		    q->next = s->prefix;
 | |
| 		    s->prefix = p->prefix;
 | |
| 		    p->prefix = NULL;
 | |
| 		}
 | |
| 		s->flags |= (p->flags & CLF_MATCHED) | CLF_MID;
 | |
| 		free_cline(p);
 | |
| 		if (pp)
 | |
| 		    pp->next = s;
 | |
| 		else
 | |
| 		    line = s;
 | |
| 	    } else
 | |
| 		p->next = s;
 | |
| 	}
 | |
| 	if (isl) {
 | |
| 	    Cline tsl;
 | |
| 
 | |
| 	    s = bld_parts(isuf, isl, isl, &tsl, NULL);
 | |
| 
 | |
| 	    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(dupstring(qisuf), qisl, qisl, NULL, NULL);
 | |
| 
 | |
| 	    qsl->flags |= CLF_SUF;
 | |
| 	    qsl->suffix = qsl->prefix;
 | |
| 	    qsl->prefix = NULL;
 | |
| 	    if (sl)
 | |
| 		sl->next = qsl;
 | |
| 	    else if (sline) {
 | |
| 		Cline sp;
 | |
| 
 | |
| 		sline = cp_cline(sline, 1);
 | |
| 
 | |
| 		for (sp = sline; sp->next; sp = sp->next);
 | |
| 		sp->next = qsl;
 | |
| 		p->next = sline;
 | |
| 	    } else
 | |
| 		p->next = qsl;
 | |
| 	}
 | |
|     } else if (sline) {
 | |
| 	Cline p;
 | |
| 
 | |
| 	for (p = line; p->next; p = p->next);
 | |
| 	p->next = cp_cline(sline, 1);
 | |
|     }
 | |
|     /* The prefix is handled differently because the completion code
 | |
|      * is much more eager to insert the -P prefix than it is to insert
 | |
|      * the -S suffix. */
 | |
|     if (qipre)
 | |
| 	palen = (qipl = strlen(qipre));
 | |
|     if (ipre)
 | |
| 	palen += (ipl = strlen(ipre));
 | |
|     if (pre)
 | |
| 	palen += (pl = strlen(pre));
 | |
|     if (!pline && ppre)
 | |
| 	palen += (ppl = strlen(ppre));
 | |
| 
 | |
|     if (pl) {
 | |
| 	if (ppl || pline) {
 | |
| 	    Cline lp, p;
 | |
| 
 | |
| 	    if (pline)
 | |
| 		for (p = cp_cline(pline, 1), lp = p; lp->next; lp = lp->next);
 | |
| 	    else
 | |
| 		p = bld_parts(ppre, ppl, ppl, &lp, NULL);
 | |
| 
 | |
| 	    if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
 | |
| 		!lp->llen && !lp->wlen && !lp->olen) {
 | |
| 		Cline lpp;
 | |
| 
 | |
| 		for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
 | |
| 
 | |
| 		lpp->next = line->prefix;
 | |
| 		line->prefix = lp->prefix;
 | |
| 		lp->prefix = NULL;
 | |
| 
 | |
| 		free_cline(lp);
 | |
| 
 | |
| 		if (p != lp) {
 | |
| 		    Cline q;
 | |
| 
 | |
| 		    for (q = p; q->next != lp; q = q->next);
 | |
| 
 | |
| 		    q->next = line;
 | |
| 		    line = p;
 | |
| 		}
 | |
| 	    } else {
 | |
| 		lp->next = line;
 | |
| 		line = p;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (pl) {
 | |
| 	    Cline lp, p = bld_parts(pre, pl, pl, &lp, NULL);
 | |
| 
 | |
| 	    lp->next = line;
 | |
| 	    line = p;
 | |
| 	}
 | |
| 	if (ipl) {
 | |
| 	    Cline lp, p = bld_parts(ipre, ipl, ipl, &lp, NULL);
 | |
| 
 | |
| 	    lp->next = line;
 | |
| 	    line = p;
 | |
| 	}
 | |
| 	if (qipl) {
 | |
| 	    Cline lp, p = bld_parts(dupstring(qipre), qipl, qipl, &lp, NULL);
 | |
| 
 | |
| 	    lp->next = line;
 | |
| 	    line = p;
 | |
| 	}
 | |
|     } else if (palen || pline) {
 | |
| 	Cline p, lp;
 | |
| 
 | |
| 	if (palen) {
 | |
| 	    char *apre = (char *) zhalloc(palen);
 | |
| 
 | |
| 	    if (qipl)
 | |
| 		memcpy(apre, qipre, qipl);
 | |
| 	    if (ipl)
 | |
| 		memcpy(apre + qipl, ipre, ipl);
 | |
| 	    if (pl)
 | |
| 		memcpy(apre + qipl + ipl, pre, pl);
 | |
| 	    if (ppl)
 | |
| 		memcpy(apre + qipl + ipl + pl, ppre, ppl);
 | |
| 
 | |
| 	    p = bld_parts(apre, palen, palen, &lp, NULL);
 | |
| 
 | |
| 	    if (pline)
 | |
| 		for (lp->next = cp_cline(pline, 1); lp->next; lp = lp->next);
 | |
| 	} else
 | |
| 	    for (p = lp = cp_cline(pline, 1); lp->next; lp = lp->next);
 | |
| 
 | |
| 	if (lp->prefix && !(line->flags & CLF_SUF) &&
 | |
| 	    !lp->llen && !lp->wlen && !lp->olen) {
 | |
| 	    Cline lpp;
 | |
| 
 | |
| 	    for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
 | |
| 
 | |
| 	    lpp->next = line->prefix;
 | |
| 	    line->prefix = lp->prefix;
 | |
| 	    lp->prefix = NULL;
 | |
| 
 | |
| 	    free_cline(lp);
 | |
| 
 | |
| 	    if (p != lp) {
 | |
| 		Cline q;
 | |
| 
 | |
| 		for (q = p; q->next != lp; q = q->next);
 | |
| 
 | |
| 		q->next = line;
 | |
| 		line = p;
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    lp->next = line;
 | |
| 	    line = p;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     stl = strlen(str);
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     /* If "str" contains a character that won't convert into a wide
 | |
|      * character, change it into a $'\123' sequence. */
 | |
|     memset(&mbs, '\0', sizeof mbs);
 | |
|     for (t = f = fs = str, fe = f + stl; fs < fe; ) {
 | |
| 	if ((curchar = *f++) == Meta)
 | |
| 	    curchar = *f++ ^ 32;
 | |
| 	cnt = mbrtowc(&wc, &curchar, 1, &mbs);
 | |
| 	switch (cnt) {
 | |
| 	case MB_INCOMPLETE:
 | |
| 	    if (f < fe)
 | |
| 		continue;
 | |
| 	    /* FALL THROUGH */
 | |
| 	case MB_INVALID:
 | |
| 	    /* Get mbs out of its undefined state. */
 | |
| 	    memset(&mbs, '\0', sizeof mbs);
 | |
| 	    if (!new_str) {
 | |
| 		/* Be very pessimistic about how much space we'll need. */
 | |
| 		new_str = zhalloc((t - str) + (fe - fs)*7 + 1);
 | |
| 		memcpy(new_str, str, t - str);
 | |
| 		t = new_str + (t - str);
 | |
| 	    }
 | |
| 	    /* Output one byte from the start of this invalid multibyte
 | |
| 	     * sequence unless we got MB_INCOMPLETE at the end of the
 | |
| 	     * string, in which case we output all the incomplete bytes. */
 | |
| 	    do {
 | |
| 		if ((curchar = *fs++) == Meta)
 | |
| 		    curchar = *fs++ ^ 32;
 | |
| 		*t++ = '$';
 | |
| 		*t++ = '\'';
 | |
| 		*t++ = '\\';
 | |
| 		*t++ = '0' + ((STOUC(curchar) >> 6) & 7);
 | |
| 		*t++ = '0' + ((STOUC(curchar) >> 3) & 7);
 | |
| 		*t++ = '0' + (STOUC(curchar) & 7);
 | |
| 		*t++ = '\'';
 | |
| 	    } while (cnt == MB_INCOMPLETE && fs < fe);
 | |
| 	    /* Scanning restarts from the spot after the char we skipped. */
 | |
| 	    f = fs;
 | |
| 	    break;
 | |
| 	default:
 | |
| 	    while (fs < f)
 | |
| 		*t++ = *fs++;
 | |
| 	    break;
 | |
| 	}
 | |
|     }
 | |
|     if (new_str) {
 | |
| 	*t = '\0';
 | |
| 	str = new_str;
 | |
| 	stl = t - str;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     /* Allocate and fill the match structure. */
 | |
|     cm = (Cmatch) zhalloc(sizeof(struct cmatch));
 | |
|     cm->str = str;
 | |
|     cm->orig = dupstring(orig);
 | |
|     cm->ppre = (ppre && *ppre ? ppre : NULL);
 | |
|     cm->psuf = (psuf && *psuf ? psuf : NULL);
 | |
|     cm->prpre = ((flags & CMF_FILE) && prpre && *prpre ? prpre : NULL);
 | |
|     if (qipre && *qipre)
 | |
| 	cm->ipre = (ipre && *ipre ? dyncat(qipre, ipre) : dupstring(qipre));
 | |
|     else
 | |
| 	cm->ipre = (ipre && *ipre ? ipre : NULL);
 | |
|     cm->ripre = (ripre && *ripre ? ripre : NULL);
 | |
|     if (qisuf && *qisuf)
 | |
| 	cm->isuf = (isuf && *isuf ? dyncat(isuf, qisuf) : dupstring(qisuf));
 | |
|     else
 | |
| 	cm->isuf = (isuf && *isuf ? isuf : NULL);
 | |
|     cm->pre = pre;
 | |
|     cm->suf = suf;
 | |
|     cm->flags = (flags |
 | |
| 		 (complist ?
 | |
| 		  ((strstr(complist, "packed") ? CMF_PACKED : 0) |
 | |
| 		   (strstr(complist, "rows")   ? CMF_ROWS   : 0)) : 0));
 | |
|     cm->mode = cm->fmode = 0;
 | |
|     cm->modec = cm->fmodec = '\0';
 | |
|     if ((flags & CMF_FILE) && orig[0] && orig[strlen(orig) - 1] != '/') {
 | |
|         struct stat buf;
 | |
| 	char *pb;
 | |
| 
 | |
|         pb = (char *) zhalloc((cm->prpre ? strlen(cm->prpre) : 0) +
 | |
|                               3 + strlen(orig));
 | |
|         sprintf(pb, "%s%s", (cm->prpre ? cm->prpre : "./"), orig);
 | |
| 
 | |
|         if (!ztat(pb, &buf, 1)) {
 | |
|             cm->mode = buf.st_mode;
 | |
|             if ((cm->modec = file_type(buf.st_mode)) == ' ')
 | |
|                 cm->modec = '\0';
 | |
|         }
 | |
|         if (!ztat(pb, &buf, 0)) {
 | |
|             cm->fmode = buf.st_mode;
 | |
|             if ((cm->fmodec = file_type(buf.st_mode)) == ' ')
 | |
|                 cm->fmodec = '\0';
 | |
|         }
 | |
|     }
 | |
|     if ((*compqstack == QT_BACKSLASH && compqstack[1]) ||
 | |
| 	(autoq && *compqstack && compqstack[1] == QT_BACKSLASH))
 | |
| 	cm->flags |= CMF_NOSPACE;
 | |
|     if (nbrbeg) {
 | |
| 	int *p;
 | |
| 	Brinfo bp;
 | |
| 
 | |
| 	cm->brpl = (int *) zhalloc(nbrbeg * sizeof(int));
 | |
| 
 | |
| 	for (p = cm->brpl, bp = brbeg; bp; p++, bp = bp->next)
 | |
| 	    *p = bp->curpos;
 | |
|     } else
 | |
| 	cm->brpl = NULL;
 | |
|     if (nbrend) {
 | |
| 	int *p;
 | |
| 	Brinfo bp;
 | |
| 
 | |
| 	cm->brsl = (int *) zhalloc(nbrend * sizeof(int));
 | |
| 
 | |
| 	for (p = cm->brsl, bp = brend; bp; p++, bp = bp->next)
 | |
| 	    *p = bp->curpos;
 | |
|     } else
 | |
| 	cm->brsl = NULL;
 | |
|     cm->qipl = qipl;
 | |
|     cm->qisl = qisl;
 | |
|     cm->autoq = dupstring(autoq ? autoq : (inbackt ? "`" : NULL));
 | |
|     cm->rems = cm->remf = cm->disp = NULL;
 | |
| 
 | |
|     if ((lastprebr || lastpostbr) && !hasbrpsfx(cm, lastprebr, lastpostbr))
 | |
| 	return NULL;
 | |
| 
 | |
|     /* Then build the unambiguous cline list. */
 | |
|     ai->line = join_clines(ai->line, line);
 | |
| 
 | |
|     mnum++;
 | |
|     ai->count++;
 | |
| 
 | |
|     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)
 | |
| 	    curexpl->fcount++;
 | |
| 	else
 | |
| 	    curexpl->count++;
 | |
|     }
 | |
|     if (!ai->firstm)
 | |
| 	ai->firstm = cm;
 | |
| 
 | |
|     lpl = (cm->ppre ? strlen(cm->ppre) : 0);
 | |
|     lsl = (cm->psuf ? strlen(cm->psuf) : 0);
 | |
|     ml = stl + lpl + lsl;
 | |
| 
 | |
|     if (ml < minmlen)
 | |
| 	minmlen = ml;
 | |
|     if (ml > maxmlen)
 | |
| 	maxmlen = ml;
 | |
| 
 | |
|     /* Do we have an exact match? More than one? */
 | |
|     if (exact) {
 | |
| 	if (!ai->exact) {
 | |
| 	    ai->exact = useexact;
 | |
| 	    if (incompfunc && (!compexactstr || !*compexactstr)) {
 | |
| 		/* If a completion widget is active, we make the exact
 | |
| 		 * string available in `compstate'. */
 | |
| 
 | |
| 		char *e;
 | |
| 
 | |
| 		zsfree(compexactstr);
 | |
| 		compexactstr = e = (char *) zalloc(ml + 1);
 | |
| 		if (cm->ppre) {
 | |
| 		    strcpy(e, cm->ppre);
 | |
| 		    e += lpl;
 | |
| 		}
 | |
| 		strcpy(e, str);
 | |
| 		e += stl;
 | |
| 		if (cm->psuf)
 | |
| 		    strcpy(e, cm->psuf);
 | |
| 		comp_setunset(0, 0, CP_EXACTSTR, 0);
 | |
| 	    }
 | |
| 	    ai->exactm = cm;
 | |
| 	} else if (useexact && (!ai->exactm || !matcheq(cm, ai->exactm))) {
 | |
| 	    ai->exact = 2;
 | |
| 	    ai->exactm = NULL;
 | |
| 	    if (incompfunc)
 | |
| 		comp_setunset(0, 0, 0, CP_EXACTSTR);
 | |
| 	}
 | |
|     }
 | |
|     return cm;
 | |
| }
 | |
| 
 | |
| /* This begins a new group of matches. */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| begcmgroup(char *n, int flags)
 | |
| {
 | |
|     if (n) {
 | |
| 	/* If a group named <n> already exists, reuse it. */
 | |
| 	Cmgroup p;
 | |
| 	for (p = amatches; p; p = p->next) {
 | |
| #ifdef ZSH_HEAP_DEBUG
 | |
| 	    if (memory_validate(p->heap_id)) {
 | |
| 		HEAP_ERROR(p->heap_id);
 | |
| 	    }
 | |
| #endif
 | |
| 	    if (p->name && flags ==
 | |
| 		(p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON|
 | |
| 			     CGF_MATSORT|CGF_NUMSORT|CGF_REVSORT)) &&
 | |
| 		!strcmp(n, p->name)) {
 | |
| 		mgroup = p;
 | |
| 
 | |
| 		expls = p->lexpls;
 | |
| 		matches = p->lmatches;
 | |
| 		fmatches = p->lfmatches;
 | |
| 		allccs = p->lallccs;
 | |
| 
 | |
| 		return;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     /* Create a new group. */
 | |
|     mgroup = (Cmgroup) zhalloc(sizeof(struct cmgroup));
 | |
| #ifdef ZSH_HEAP_DEBUG
 | |
|     mgroup->heap_id = last_heap_id;
 | |
| #endif
 | |
|     mgroup->name = dupstring(n);
 | |
|     mgroup->lcount = mgroup->llcount = mgroup->mcount = mgroup->ecount = 
 | |
| 	mgroup->ccount = 0;
 | |
|     mgroup->flags = flags;
 | |
|     mgroup->matches = NULL;
 | |
|     mgroup->ylist = NULL;
 | |
|     mgroup->expls = NULL;
 | |
|     mgroup->perm = NULL;
 | |
|     mgroup->new = mgroup->num = mgroup->nbrbeg = mgroup->nbrend = 0;
 | |
| 
 | |
|     mgroup->lexpls = expls = newlinklist();
 | |
|     mgroup->lmatches = matches = newlinklist();
 | |
|     mgroup->lfmatches = fmatches = newlinklist();
 | |
| 
 | |
|     mgroup->lallccs = allccs = ((flags & CGF_NOSORT) ? NULL : newlinklist());
 | |
| 
 | |
|     if ((mgroup->next = amatches))
 | |
| 	amatches->prev = mgroup;
 | |
|     mgroup->prev = NULL;
 | |
|     amatches = mgroup;
 | |
| }
 | |
| 
 | |
| /* End the current group for now. */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| endcmgroup(char **ylist)
 | |
| {
 | |
|     mgroup->ylist = ylist;
 | |
| }
 | |
| 
 | |
| /* Add an explanation string to the current group, joining duplicates. */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| addexpl(int always)
 | |
| {
 | |
|     LinkNode n;
 | |
|     Cexpl e;
 | |
| 
 | |
|     for (n = firstnode(expls); n; incnode(n)) {
 | |
| 	e = (Cexpl) getdata(n);
 | |
| 	if (!strcmp(curexpl->str, e->str)) {
 | |
| 	    e->count += curexpl->count;
 | |
| 	    e->fcount += curexpl->fcount;
 | |
|             if (always) {
 | |
|                 e->always = 1;
 | |
|                 nmessages++;
 | |
|                 newmatches = 1;
 | |
|                 mgroup->new = 1;
 | |
|             }
 | |
| 	    return;
 | |
| 	}
 | |
|     }
 | |
|     addlinknode(expls, curexpl);
 | |
|     newmatches = 1;
 | |
|     if (always) {
 | |
|         mgroup->new = 1;
 | |
|         nmessages++;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* The comparison function for matches (used for sorting). */
 | |
| 
 | |
| static int matchorder;
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| matchcmp(Cmatch *a, Cmatch *b)
 | |
| {
 | |
|     const char *as, *bs;
 | |
|     int cmp = !!(*b)->disp - !!(*a)->disp;
 | |
|     int sortdir = (matchorder & CGF_REVSORT) ? -1 : 1;
 | |
| 
 | |
|     /* if match sorting selected or we have no display strings */
 | |
|     if ((matchorder & CGF_MATSORT) || (!cmp && !(*a)->disp)) {
 | |
| 	as = (*a)->str;
 | |
| 	bs = (*b)->str;
 | |
|     } else {
 | |
|         if (cmp) /* matches with display strings come first */
 | |
| 	    return cmp;
 | |
| 
 | |
| 	cmp = ((*b)->flags & CMF_DISPLINE) - ((*a)->flags & CMF_DISPLINE);
 | |
|         if (cmp) /* sort one-per-line display strings first */
 | |
| 	    return cmp;
 | |
| 
 | |
| 	as = (*a)->disp;
 | |
| 	bs = (*b)->disp;
 | |
|     }
 | |
| 
 | |
|     return sortdir * zstrcmp(as, bs, SORTIT_IGNORING_BACKSLASHES|
 | |
| 	    ((isset(NUMERICGLOBSORT) ||
 | |
| 	    matchorder & CGF_NUMSORT) ? SORTIT_NUMERICALLY : 0));
 | |
| }
 | |
| 
 | |
| /* This tests whether two matches are equal (would produce the same
 | |
|  * strings on the command line). */
 | |
| 
 | |
| #define matchstreq(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| matcheq(Cmatch a, Cmatch b)
 | |
| {
 | |
|     return matchstreq(a->ipre, b->ipre) &&
 | |
| 	matchstreq(a->pre, b->pre) &&
 | |
| 	matchstreq(a->ppre, b->ppre) &&
 | |
| 	matchstreq(a->psuf, b->psuf) &&
 | |
| 	matchstreq(a->suf, b->suf) &&
 | |
| 	  matchstreq(a->str, b->str);
 | |
| }
 | |
| 
 | |
| /* Make an array from a linked list. The second argument says whether *
 | |
|  * the array should be sorted. The third argument is used to return   *
 | |
|  * the number of elements in the resulting array. The fourth argument *
 | |
|  * is used to return the number of NOLIST elements. */
 | |
| 
 | |
| /**/
 | |
| static Cmatch *
 | |
| makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
 | |
| {
 | |
|     Cmatch *ap, *bp, *cp, *rp;
 | |
|     LinkNode nod;
 | |
|     int n, nl = 0, ll = 0;
 | |
| 
 | |
|     /* Build an array for the matches. */
 | |
|     rp = ap = (Cmatch *) hcalloc(((n = countlinknodes(l)) + 1) *
 | |
| 				 sizeof(Cmatch));
 | |
| 
 | |
|     /* And copy them into it. */
 | |
|     for (nod = firstnode(l); nod; incnode(nod))
 | |
| 	*ap++ = (Cmatch) getdata(nod);
 | |
|     *ap = NULL;
 | |
| 
 | |
|     if (!type) {
 | |
| 	if (flags) {
 | |
| 	    char **ap, **bp, **cp;
 | |
| 
 | |
| 	    /* Now sort the array (it contains strings). */
 | |
| 	    strmetasort((char **)rp, SORTIT_IGNORING_BACKSLASHES |
 | |
| 			(isset(NUMERICGLOBSORT) ? SORTIT_NUMERICALLY : 0),
 | |
| 			NULL);
 | |
| 
 | |
| 	    /* And delete the ones that occur more than once. */
 | |
| 	    for (ap = cp = (char **) rp; *ap; ap++) {
 | |
| 		*cp++ = *ap;
 | |
| 		for (bp = ap; bp[1] && !strcmp(*ap, bp[1]); bp++, n--);
 | |
| 		ap = bp;
 | |
| 	    }
 | |
| 	    *cp = NULL;
 | |
| 	}
 | |
|     } else if (n > 0) {
 | |
| 	if (!(flags & CGF_NOSORT)) {
 | |
| 	    /* Now sort the array (it contains matches). */
 | |
| 	    matchorder = flags;
 | |
| 	    qsort((void *) rp, n, sizeof(Cmatch),
 | |
| 		  (int (*) _((const void *, const void *)))matchcmp);
 | |
| 
 | |
| 	    /* since the matches are sorted and the default is to remove
 | |
| 	     * all duplicates, -1 (remove only consecutive dupes) is a no-op,
 | |
| 	     * so this condition only checks for -2 */
 | |
| 	    if (!(flags & CGF_UNIQCON)) {
 | |
| 		int dup;
 | |
| 
 | |
| 		/* we did not pass -2 so go ahead and remove those dupes */
 | |
| 		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 (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 | CMF_MULT))
 | |
| 		    nl++;
 | |
| 	    }
 | |
| 	/* used -O nosort or -V, don't sort */
 | |
| 	} else {
 | |
| 	    /* didn't use -1 or -2, so remove all duplicates (efficient) */
 | |
| 	    if (!(flags & CGF_UNIQALL) && !(flags & CGF_UNIQCON)) {
 | |
|                 int dup, del = 0;
 | |
| 
 | |
| 		/* To avoid O(n^2) here, sort a copy of the list, then remove marked elements */
 | |
| 		matchorder = flags;
 | |
| 		Cmatch *sp, *asp;
 | |
| 		sp = (Cmatch *) zhalloc((n + 1) * sizeof(Cmatch));
 | |
| 		memcpy(sp, rp, (n + 1) * sizeof(Cmatch));
 | |
| 		qsort((void *) sp, n, sizeof(Cmatch),
 | |
| 		      (int (*) _((const void *, const void *)))matchcmp);
 | |
| 		for (asp = sp + 1; *asp; asp++) {
 | |
| 		    Cmatch *ap = asp - 1, *bp = asp;
 | |
| 		    if (matcheq(*ap, *bp)) {
 | |
| 			bp[0]->flags = CMF_DELETE;
 | |
| 			del = 1;
 | |
| 		    } else if (!ap[0]->disp) {
 | |
| 			/* Mark those, that would show the same string in the list. */
 | |
| 			for (dup = 0; bp[0] && !(bp[0])->disp &&
 | |
| 				 !strcmp((*ap)->str, (bp[0])->str); bp = ++sp) {
 | |
| 			    (bp[0])->flags |= CMF_MULT;
 | |
| 			    dup = 1;
 | |
| 			}
 | |
| 			if (dup)
 | |
| 			    (*ap)->flags |= CMF_FMULT;
 | |
| 		    }
 | |
| 		}
 | |
| 		if (del) {
 | |
| 		    int n_orig = n;
 | |
| 		    for (bp = rp, ap = rp; bp < rp + n_orig; ap++, bp++) {
 | |
| 			while (bp < rp + n_orig && (bp[0]->flags & CMF_DELETE)) {
 | |
| 			    bp++;
 | |
| 			    n--;
 | |
| 			}
 | |
| 			*ap = *bp;
 | |
| 		    }
 | |
| 		    *ap = NULL;
 | |
| 		}
 | |
| 	    /* passed -1 but not -2, so remove consecutive duplicates (efficient) */
 | |
| 	    } 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 (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 | CMF_MULT))
 | |
| 		    nl++;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     if (np)
 | |
| 	*np = n;
 | |
|     if (nlp)
 | |
| 	*nlp = nl;
 | |
|     if (llp)
 | |
| 	*llp = ll;
 | |
|     return rp;
 | |
| }
 | |
| 
 | |
| /* This duplicates one match. */
 | |
| 
 | |
| /**/
 | |
| static Cmatch
 | |
| dupmatch(Cmatch m, int nbeg, int nend)
 | |
| {
 | |
|     Cmatch r;
 | |
| 
 | |
|     r = (Cmatch) zshcalloc(sizeof(struct cmatch));
 | |
| 
 | |
|     r->str = ztrdup(m->str);
 | |
|     r->orig = ztrdup(m->orig);
 | |
|     r->ipre = ztrdup(m->ipre);
 | |
|     r->ripre = ztrdup(m->ripre);
 | |
|     r->isuf = ztrdup(m->isuf);
 | |
|     r->ppre = ztrdup(m->ppre);
 | |
|     r->psuf = ztrdup(m->psuf);
 | |
|     r->prpre = ztrdup(m->prpre);
 | |
|     r->pre = ztrdup(m->pre);
 | |
|     r->suf = ztrdup(m->suf);
 | |
|     r->flags = m->flags;
 | |
|     if (m->brpl) {
 | |
| 	int *p, *q, i;
 | |
| 
 | |
| 	r->brpl = (int *) zalloc(nbeg * sizeof(int));
 | |
| 
 | |
| 	for (p = r->brpl, q = m->brpl, i = nbeg; i--; p++, q++)
 | |
| 	    *p = *q;
 | |
|     } else
 | |
| 	r->brpl = NULL;
 | |
|     if (m->brsl) {
 | |
| 	int *p, *q, i;
 | |
| 
 | |
| 	r->brsl = (int *) zalloc(nend * sizeof(int));
 | |
| 
 | |
| 	for (p = r->brsl, q = m->brsl, i = nend; i--; p++, q++)
 | |
| 	    *p = *q;
 | |
|     } else
 | |
| 	r->brsl = NULL;
 | |
|     r->rems = ztrdup(m->rems);
 | |
|     r->remf = ztrdup(m->remf);
 | |
|     r->autoq = ztrdup(m->autoq);
 | |
|     r->qipl = m->qipl;
 | |
|     r->qisl = m->qisl;
 | |
|     r->disp = ztrdup(m->disp);
 | |
|     r->mode = m->mode;
 | |
|     r->modec = m->modec;
 | |
|     r->fmode = m->fmode;
 | |
|     r->fmodec = m->fmodec;
 | |
| 
 | |
|     return r;
 | |
| }
 | |
| 
 | |
| /* This duplicates all groups of matches. */
 | |
| 
 | |
| /**/
 | |
| mod_export int
 | |
| permmatches(int last)
 | |
| {
 | |
|     Cmgroup g = amatches, n;
 | |
|     Cmatch *p, *q;
 | |
|     Cexpl *ep, *eq, e, o;
 | |
|     LinkList mlist;
 | |
|     static int fi = 0;
 | |
|     int nn, nl, ll, gn = 1, mn = 1, rn, ofi = fi;
 | |
| 
 | |
|     if (pmatches && !newmatches) {
 | |
| 	if (last && fi)
 | |
| 	    ainfo = fainfo;
 | |
| 	return fi;
 | |
|     }
 | |
|     newmatches = fi = 0;
 | |
| 
 | |
|     pmatches = lmatches = NULL;
 | |
|     nmatches = smatches = diffmatches = 0;
 | |
| 
 | |
|     if (!ainfo->count) {
 | |
| 	if (last)
 | |
| 	    ainfo = fainfo;
 | |
| 	fi = 1;
 | |
|     }
 | |
|     while (g) {
 | |
| #ifdef ZSH_HEAP_DEBUG
 | |
| 	if (memory_validate(g->heap_id)) {
 | |
| 	    HEAP_ERROR(g->heap_id);
 | |
| 	}
 | |
| #endif
 | |
| 	if (fi != ofi || !g->perm || g->new) {
 | |
| 	    if (fi)
 | |
| 		/* We have no matches, try ignoring fignore. */
 | |
| 		mlist = g->lfmatches;
 | |
| 	    else
 | |
| 		mlist = g->lmatches;
 | |
| 
 | |
| 	    g->matches = makearray(mlist, 1, g->flags, &nn, &nl, &ll);
 | |
| 	    g->mcount = nn;
 | |
| 	    if ((g->lcount = nn - nl) < 0)
 | |
| 		g->lcount = 0;
 | |
| 	    g->llcount = ll;
 | |
| 	    if (g->ylist) {
 | |
| 		g->lcount = arrlen(g->ylist);
 | |
| 		smatches = 2;
 | |
| 	    }
 | |
| 	    g->expls = (Cexpl *) makearray(g->lexpls, 0, 0, &(g->ecount),
 | |
| 					   NULL, NULL);
 | |
| 
 | |
| 	    g->ccount = 0;
 | |
| 
 | |
| 	    nmatches += g->mcount;
 | |
| 	    smatches += g->lcount;
 | |
| 
 | |
| 	    if (g->mcount > 1)
 | |
| 		diffmatches = 1;
 | |
| 
 | |
| 	    n = (Cmgroup) zshcalloc(sizeof(struct cmgroup));
 | |
| #ifdef ZSH_HEAP_DEBUG
 | |
| 	    n->heap_id = HEAPID_PERMANENT;
 | |
| #endif
 | |
| 
 | |
| 	    if (g->perm) {
 | |
| 		g->perm->next = NULL;
 | |
| 		freematches(g->perm, 0);
 | |
| 	    }
 | |
| 	    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 *) zshcalloc((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 *) zshcalloc((n->ecount + 1) * sizeof(Cexpl));
 | |
| 		for (eq = g->expls; (o = *eq); eq++, ep++) {
 | |
| 		    *ep = e = (Cexpl) zshcalloc(sizeof(struct cexpl));
 | |
| 		    e->count = (fi ? o->fcount : o->count);
 | |
|                     e->always = o->always;
 | |
| 		    e->fcount = 0;
 | |
| 		    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;
 | |
| 
 | |
| 	    if (g->mcount > 1)
 | |
| 		diffmatches = 1;
 | |
| 
 | |
| 	    g->num = gn++;
 | |
| 	}
 | |
| 	g->new = 0;
 | |
| 	g = g->next;
 | |
|     }
 | |
|     for (g = pmatches, p = NULL; g; g = g->next) {
 | |
| 	g->nbrbeg = nbrbeg;
 | |
| 	g->nbrend = nbrend;
 | |
| 	for (rn = 1, q = g->matches; *q; q++) {
 | |
| 	    (*q)->rnum = rn++;
 | |
| 	    (*q)->gnum = mn++;
 | |
| 	}
 | |
| 	if (!diffmatches && *g->matches) {
 | |
| 	    if (p) {
 | |
| 		if (!matcheq(*g->matches, *p))
 | |
| 		    diffmatches = 1;
 | |
| 	    } else
 | |
| 		p = g->matches;
 | |
| 	}
 | |
|     }
 | |
|     hasperm = 1;
 | |
|     permmnum = mn - 1;
 | |
|     permgnum = gn - 1;
 | |
|     listdat.valid = 0;
 | |
| 
 | |
|     return fi;
 | |
| }
 | |
| 
 | |
| /* This frees one match. */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| freematch(Cmatch m, int nbeg, int nend)
 | |
| {
 | |
|     if (!m) return;
 | |
| 
 | |
|     zsfree(m->str);
 | |
|     zsfree(m->orig);
 | |
|     zsfree(m->ipre);
 | |
|     zsfree(m->ripre);
 | |
|     zsfree(m->isuf);
 | |
|     zsfree(m->ppre);
 | |
|     zsfree(m->psuf);
 | |
|     zsfree(m->pre);
 | |
|     zsfree(m->suf);
 | |
|     zsfree(m->prpre);
 | |
|     zsfree(m->rems);
 | |
|     zsfree(m->remf);
 | |
|     zsfree(m->disp);
 | |
|     zsfree(m->autoq);
 | |
|     if (m->brpl)
 | |
| 	zfree(m->brpl, nbeg * sizeof(int));
 | |
|     if (m->brsl)
 | |
| 	zfree(m->brsl, nend * sizeof(int));
 | |
| 
 | |
|     zfree(m, sizeof(*m));
 | |
| }
 | |
| 
 | |
| /* This frees the groups of matches. */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| freematches(Cmgroup g, int cm)
 | |
| {
 | |
|     Cmgroup n;
 | |
|     Cmatch *m;
 | |
|     Cexpl *e;
 | |
| 
 | |
|     while (g) {
 | |
| 	n = g->next;
 | |
| 
 | |
| 	for (m = g->matches; *m; m++)
 | |
| 	    freematch(*m, g->nbrbeg, g->nbrend);
 | |
| 	free(g->matches);
 | |
| 
 | |
| 	if (g->ylist)
 | |
| 	    freearray(g->ylist);
 | |
| 
 | |
| 	if ((e = g->expls)) {
 | |
| 	    while (*e) {
 | |
| 		zsfree((*e)->str);
 | |
| 		free(*e);
 | |
| 		e++;
 | |
| 	    }
 | |
| 	    free(g->expls);
 | |
| 	}
 | |
| 	if (g->widths)
 | |
| 	    free(g->widths);
 | |
| 	zsfree(g->name);
 | |
| 	free(g);
 | |
| 
 | |
| 	g = n;
 | |
|     }
 | |
|     if (cm)
 | |
| 	minfo.cur = NULL;
 | |
| }
 |