1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*b30d1939SAndy Fiddaman *          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6*b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10*b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11*b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
18da2e3ebdSchin *                                                                      *
19da2e3ebdSchin ***********************************************************************/
20da2e3ebdSchin #pragma prototyped
21da2e3ebdSchin /*
22da2e3ebdSchin  *  completion.c - command and file completion for shell editors
23da2e3ebdSchin  *
24da2e3ebdSchin  */
25da2e3ebdSchin 
26da2e3ebdSchin #include	"defs.h"
27da2e3ebdSchin #include	<ast_wchar.h>
28da2e3ebdSchin #include	"lexstates.h"
29da2e3ebdSchin #include	"path.h"
30da2e3ebdSchin #include	"io.h"
31da2e3ebdSchin #include	"edit.h"
32da2e3ebdSchin #include	"history.h"
33da2e3ebdSchin 
3434f9b3eeSRoland Mainz #if !SHOPT_MULTIBYTE
3534f9b3eeSRoland Mainz #define mbchar(p)       (*(unsigned char*)p++)
3634f9b3eeSRoland Mainz #endif
3734f9b3eeSRoland Mainz 
fmtx(const char * string)3834f9b3eeSRoland Mainz static char *fmtx(const char *string)
3934f9b3eeSRoland Mainz {
4034f9b3eeSRoland Mainz 	register const char	*cp = string;
4134f9b3eeSRoland Mainz 	register int	 	n,c;
4234f9b3eeSRoland Mainz 	unsigned char 		*state = (unsigned char*)sh_lexstates[2];
43*b30d1939SAndy Fiddaman 	int offset = staktell();
44*b30d1939SAndy Fiddaman 	if(*cp=='#' || *cp=='~')
45*b30d1939SAndy Fiddaman 		stakputc('\\');
46*b30d1939SAndy Fiddaman 	while((c=mbchar(cp)),(c>UCHAR_MAX)||(n=state[c])==0 || n==S_EPAT);
47*b30d1939SAndy Fiddaman 	if(n==S_EOF && *string!='#')
4834f9b3eeSRoland Mainz 		return((char*)string);
4934f9b3eeSRoland Mainz 	stakwrite(string,--cp-string);
50*b30d1939SAndy Fiddaman 	for(string=cp;c=mbchar(cp);string=cp)
5134f9b3eeSRoland Mainz 	{
52*b30d1939SAndy Fiddaman 		if((n=cp-string)==1)
53*b30d1939SAndy Fiddaman 		{
54*b30d1939SAndy Fiddaman 			if((n=state[c]) && n!=S_EPAT)
55*b30d1939SAndy Fiddaman 				stakputc('\\');
56*b30d1939SAndy Fiddaman 			stakputc(c);
57*b30d1939SAndy Fiddaman 		}
58*b30d1939SAndy Fiddaman 		else
59*b30d1939SAndy Fiddaman 			stakwrite(string,n);
6034f9b3eeSRoland Mainz 	}
6134f9b3eeSRoland Mainz 	stakputc(0);
6234f9b3eeSRoland Mainz 	return(stakptr(offset));
6334f9b3eeSRoland Mainz }
6434f9b3eeSRoland Mainz 
charcmp(int a,int b,int nocase)65da2e3ebdSchin static int charcmp(int a, int b, int nocase)
66da2e3ebdSchin {
67da2e3ebdSchin 	if(nocase)
68da2e3ebdSchin 	{
69da2e3ebdSchin 		if(isupper(a))
70da2e3ebdSchin 			a = tolower(a);
71da2e3ebdSchin 		if(isupper(b))
72da2e3ebdSchin 			b = tolower(b);
73da2e3ebdSchin 	}
74da2e3ebdSchin 	return(a==b);
75da2e3ebdSchin }
76da2e3ebdSchin 
77da2e3ebdSchin /*
78da2e3ebdSchin  *  overwrites <str> to common prefix of <str> and <newstr>
79da2e3ebdSchin  *  if <str> is equal to <newstr> returns  <str>+strlen(<str>)+1
80da2e3ebdSchin  *  otherwise returns <str>+strlen(<str>)
81da2e3ebdSchin  */
overlaid(register char * str,register const char * newstr,int nocase)82da2e3ebdSchin static char *overlaid(register char *str,register const char *newstr,int nocase)
83da2e3ebdSchin {
84da2e3ebdSchin 	register int c,d;
85da2e3ebdSchin 	while((c= *(unsigned char *)str) && ((d= *(unsigned char*)newstr++),charcmp(c,d,nocase)))
86da2e3ebdSchin 		str++;
87da2e3ebdSchin 	if(*str)
88da2e3ebdSchin 		*str = 0;
89da2e3ebdSchin 	else if(*newstr==0)
90da2e3ebdSchin 		str++;
91da2e3ebdSchin 	return(str);
92da2e3ebdSchin }
93da2e3ebdSchin 
94da2e3ebdSchin 
95da2e3ebdSchin /*
96da2e3ebdSchin  * returns pointer to beginning of expansion and sets type of expansion
97da2e3ebdSchin  */
find_begin(char outbuff[],char * last,int endchar,int * type)98da2e3ebdSchin static char *find_begin(char outbuff[], char *last, int endchar, int *type)
99da2e3ebdSchin {
100da2e3ebdSchin 	register char	*cp=outbuff, *bp, *xp;
1017c2fbfb3SApril Chin 	register int 	c,inquote = 0, inassign=0;
1027c2fbfb3SApril Chin 	int		mode=*type;
103da2e3ebdSchin 	bp = outbuff;
104da2e3ebdSchin 	*type = 0;
105da2e3ebdSchin 	while(cp < last)
106da2e3ebdSchin 	{
107da2e3ebdSchin 		xp = cp;
108da2e3ebdSchin 		switch(c= mbchar(cp))
109da2e3ebdSchin 		{
110da2e3ebdSchin 		    case '\'': case '"':
111da2e3ebdSchin 			if(!inquote)
112da2e3ebdSchin 			{
113da2e3ebdSchin 				inquote = c;
114da2e3ebdSchin 				bp = xp;
115da2e3ebdSchin 				break;
116da2e3ebdSchin 			}
117da2e3ebdSchin 			if(inquote==c)
118da2e3ebdSchin 				inquote = 0;
119da2e3ebdSchin 			break;
120da2e3ebdSchin 		    case '\\':
121da2e3ebdSchin 			if(inquote != '\'')
122da2e3ebdSchin 				mbchar(cp);
123da2e3ebdSchin 			break;
124da2e3ebdSchin 		    case '$':
125da2e3ebdSchin 			if(inquote == '\'')
126da2e3ebdSchin 				break;
127da2e3ebdSchin 			c = *(unsigned char*)cp;
1287c2fbfb3SApril Chin 			if(mode!='*' && (isaletter(c) || c=='{'))
129da2e3ebdSchin 			{
130da2e3ebdSchin 				int dot = '.';
131da2e3ebdSchin 				if(c=='{')
132da2e3ebdSchin 				{
133da2e3ebdSchin 					xp = cp;
134da2e3ebdSchin 					mbchar(cp);
135da2e3ebdSchin 					c = *(unsigned char*)cp;
136da2e3ebdSchin 					if(c!='.' && !isaletter(c))
137da2e3ebdSchin 						break;
138da2e3ebdSchin 				}
139da2e3ebdSchin 				else
140da2e3ebdSchin 					dot = 'a';
141da2e3ebdSchin 				while(cp < last)
142da2e3ebdSchin 				{
143da2e3ebdSchin 					if((c= mbchar(cp)) , c!=dot && !isaname(c))
144da2e3ebdSchin 						break;
145da2e3ebdSchin 				}
146*b30d1939SAndy Fiddaman 				if(cp>=last)
147da2e3ebdSchin 				{
148*b30d1939SAndy Fiddaman 					if(c==dot || isaname(c))
149*b30d1939SAndy Fiddaman 					{
150*b30d1939SAndy Fiddaman 						*type='$';
151*b30d1939SAndy Fiddaman 						return(++xp);
152*b30d1939SAndy Fiddaman 					}
153*b30d1939SAndy Fiddaman 					if(c!='}')
154*b30d1939SAndy Fiddaman 						bp = cp;
155da2e3ebdSchin 				}
156da2e3ebdSchin 			}
157da2e3ebdSchin 			else if(c=='(')
158da2e3ebdSchin 			{
1597c2fbfb3SApril Chin 				*type = mode;
160da2e3ebdSchin 				xp = find_begin(cp,last,')',type);
161da2e3ebdSchin 				if(*(cp=xp)!=')')
162da2e3ebdSchin 					bp = xp;
163da2e3ebdSchin 				else
164da2e3ebdSchin 					cp++;
165da2e3ebdSchin 			}
166da2e3ebdSchin 			break;
167da2e3ebdSchin 		    case '=':
168da2e3ebdSchin 			if(!inquote)
1697c2fbfb3SApril Chin 			{
1707c2fbfb3SApril Chin 				bp = cp;
1717c2fbfb3SApril Chin 				inassign = 1;
1727c2fbfb3SApril Chin 			}
1737c2fbfb3SApril Chin 			break;
1747c2fbfb3SApril Chin 		    case ':':
1757c2fbfb3SApril Chin 			if(!inquote && inassign)
176da2e3ebdSchin 				bp = cp;
177da2e3ebdSchin 			break;
178da2e3ebdSchin 		    case '~':
179da2e3ebdSchin 			if(*cp=='(')
180da2e3ebdSchin 				break;
181da2e3ebdSchin 			/* fall through */
182da2e3ebdSchin 		    default:
183da2e3ebdSchin 			if(c && c==endchar)
184da2e3ebdSchin 				return(xp);
185da2e3ebdSchin 			if(!inquote && ismeta(c))
1867c2fbfb3SApril Chin 			{
187da2e3ebdSchin 				bp = cp;
1887c2fbfb3SApril Chin 				inassign = 0;
1897c2fbfb3SApril Chin 			}
190da2e3ebdSchin 			break;
191da2e3ebdSchin 		}
192da2e3ebdSchin 	}
193da2e3ebdSchin 	if(inquote && *bp==inquote)
194da2e3ebdSchin 		*type = *bp++;
195da2e3ebdSchin 	return(bp);
196da2e3ebdSchin }
197da2e3ebdSchin 
198da2e3ebdSchin /*
199da2e3ebdSchin  * file name generation for edit modes
200da2e3ebdSchin  * non-zero exit for error, <0 ring bell
201da2e3ebdSchin  * don't search back past beginning of the buffer
202da2e3ebdSchin  * mode is '*' for inline expansion,
203da2e3ebdSchin  * mode is '\' for filename completion
204da2e3ebdSchin  * mode is '=' cause files to be listed in select format
205da2e3ebdSchin  */
206da2e3ebdSchin 
ed_expand(Edit_t * ep,char outbuff[],int * cur,int * eol,int mode,int count)207da2e3ebdSchin int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count)
208da2e3ebdSchin {
209da2e3ebdSchin 	struct comnod	*comptr;
210da2e3ebdSchin 	struct argnod	*ap;
211da2e3ebdSchin 	register char	*out;
212da2e3ebdSchin 	char 		*av[2], *begin , *dir=0;
213da2e3ebdSchin 	int		addstar=0, rval=0, var=0, strip=1;
214da2e3ebdSchin 	int 		nomarkdirs = !sh_isoption(SH_MARKDIRS);
215da2e3ebdSchin 	sh_onstate(SH_FCOMPLETE);
216da2e3ebdSchin 	if(ep->e_nlist)
217da2e3ebdSchin 	{
218da2e3ebdSchin 		if(mode=='=' && count>0)
219da2e3ebdSchin 		{
220da2e3ebdSchin 			if(count> ep->e_nlist)
221da2e3ebdSchin 				return(-1);
2227c2fbfb3SApril Chin 			mode = '?';
223da2e3ebdSchin 			av[0] = ep->e_clist[count-1];
224da2e3ebdSchin 			av[1] = 0;
225da2e3ebdSchin 		}
226da2e3ebdSchin 		else
227da2e3ebdSchin 		{
228da2e3ebdSchin 			stakset(ep->e_stkptr,ep->e_stkoff);
229da2e3ebdSchin 			ep->e_nlist = 0;
230da2e3ebdSchin 		}
231da2e3ebdSchin 	}
232da2e3ebdSchin 	comptr = (struct comnod*)stakalloc(sizeof(struct comnod));
233da2e3ebdSchin 	ap = (struct argnod*)stakseek(ARGVAL);
234da2e3ebdSchin #if SHOPT_MULTIBYTE
235da2e3ebdSchin 	{
236da2e3ebdSchin 		register int c = *cur;
237da2e3ebdSchin 		register genchar *cp;
238da2e3ebdSchin 		/* adjust cur */
239da2e3ebdSchin 		cp = (genchar *)outbuff + *cur;
240da2e3ebdSchin 		c = *cp;
241da2e3ebdSchin 		*cp = 0;
242da2e3ebdSchin 		*cur = ed_external((genchar*)outbuff,(char*)stakptr(0));
243da2e3ebdSchin 		*cp = c;
244da2e3ebdSchin 		*eol = ed_external((genchar*)outbuff,outbuff);
245da2e3ebdSchin 	}
246da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
247da2e3ebdSchin 	out = outbuff + *cur + (sh_isoption(SH_VI)!=0);
248*b30d1939SAndy Fiddaman 	if(out[-1]=='"' || out[-1]=='\'')
249*b30d1939SAndy Fiddaman 	{
250*b30d1939SAndy Fiddaman 		rval = -(sh_isoption(SH_VI)!=0);
251*b30d1939SAndy Fiddaman 		goto done;
252*b30d1939SAndy Fiddaman 	}
253da2e3ebdSchin 	comptr->comtyp = COMSCAN;
254da2e3ebdSchin 	comptr->comarg = ap;
255da2e3ebdSchin 	ap->argflag = (ARG_MAC|ARG_EXP);
256da2e3ebdSchin 	ap->argnxt.ap = 0;
257da2e3ebdSchin 	ap->argchn.cp = 0;
258da2e3ebdSchin 	{
259da2e3ebdSchin 		register int c;
260da2e3ebdSchin 		char *last = out;
261da2e3ebdSchin 		c =  *(unsigned char*)out;
2627c2fbfb3SApril Chin 		var = mode;
263da2e3ebdSchin 		begin = out = find_begin(outbuff,last,0,&var);
264da2e3ebdSchin 		/* addstar set to zero if * should not be added */
265da2e3ebdSchin 		if(var=='$')
266da2e3ebdSchin 		{
267da2e3ebdSchin 			stakputs("${!");
268da2e3ebdSchin 			stakwrite(out,last-out);
269da2e3ebdSchin 			stakputs("@}");
270da2e3ebdSchin 			out = last;
271da2e3ebdSchin 		}
272da2e3ebdSchin 		else
273da2e3ebdSchin 		{
274da2e3ebdSchin 			addstar = '*';
275da2e3ebdSchin 			while(out < last)
276da2e3ebdSchin 			{
277da2e3ebdSchin 				c = *(unsigned char*)out;
278da2e3ebdSchin 				if(isexp(c))
279da2e3ebdSchin 					addstar = 0;
280da2e3ebdSchin 				if (c == '/')
281da2e3ebdSchin 				{
282da2e3ebdSchin 					if(addstar == 0)
283da2e3ebdSchin 						strip = 0;
284da2e3ebdSchin 					dir = out+1;
285da2e3ebdSchin 				}
286da2e3ebdSchin 				stakputc(c);
287da2e3ebdSchin 				out++;
288da2e3ebdSchin 			}
289da2e3ebdSchin 		}
2907c2fbfb3SApril Chin 		if(mode=='?')
2917c2fbfb3SApril Chin 			mode = '*';
292da2e3ebdSchin 		if(var!='$' && mode=='\\' && out[-1]!='*')
293da2e3ebdSchin 			addstar = '*';
294da2e3ebdSchin 		if(*begin=='~' && !strchr(begin,'/'))
295da2e3ebdSchin 			addstar = 0;
296da2e3ebdSchin 		stakputc(addstar);
297da2e3ebdSchin 		ap = (struct argnod*)stakfreeze(1);
298da2e3ebdSchin 	}
299da2e3ebdSchin 	if(mode!='*')
300da2e3ebdSchin 		sh_onoption(SH_MARKDIRS);
301da2e3ebdSchin 	{
302da2e3ebdSchin 		register char	**com;
303da2e3ebdSchin 		char		*cp=begin, *left=0, *saveout=".";
304da2e3ebdSchin 		int	 	nocase=0,narg,cmd_completion=0;
305da2e3ebdSchin 		register 	int size='x';
306da2e3ebdSchin 		while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t'))
307da2e3ebdSchin 			cp--;
308*b30d1939SAndy Fiddaman 		if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&ep->sh->nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' )))
309da2e3ebdSchin 		{
310da2e3ebdSchin 			cmd_completion=1;
311da2e3ebdSchin 			sh_onstate(SH_COMPLETE);
312da2e3ebdSchin 		}
313da2e3ebdSchin 		if(ep->e_nlist)
314da2e3ebdSchin 		{
315da2e3ebdSchin 			narg = 1;
316da2e3ebdSchin 			com = av;
317da2e3ebdSchin 			if(dir)
318da2e3ebdSchin 				begin += (dir-begin);
319da2e3ebdSchin 		}
320da2e3ebdSchin 		else
321da2e3ebdSchin 		{
3227c2fbfb3SApril Chin 			com = sh_argbuild(ep->sh,&narg,comptr,0);
323da2e3ebdSchin 			/* special handling for leading quotes */
324da2e3ebdSchin 			if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\''))
325da2e3ebdSchin 			begin--;
326da2e3ebdSchin 		}
327da2e3ebdSchin 		sh_offstate(SH_COMPLETE);
328da2e3ebdSchin                 /* allow a search to be aborted */
329*b30d1939SAndy Fiddaman 		if(ep->sh->trapnote&SH_SIGSET)
330da2e3ebdSchin 		{
331da2e3ebdSchin 			rval = -1;
332da2e3ebdSchin 			goto done;
333da2e3ebdSchin 		}
334da2e3ebdSchin 		/*  match? */
335da2e3ebdSchin 		if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*')))
336da2e3ebdSchin 		{
337da2e3ebdSchin 			rval = -1;
338da2e3ebdSchin 			goto done;
339da2e3ebdSchin 		}
340*b30d1939SAndy Fiddaman 		if(mode=='\\' && out[-1]=='/'  && narg>1)
341*b30d1939SAndy Fiddaman 			mode = '=';
342da2e3ebdSchin 		if(mode=='=')
343da2e3ebdSchin 		{
344da2e3ebdSchin 			if (strip && !cmd_completion)
345da2e3ebdSchin 			{
346da2e3ebdSchin 				register char **ptrcom;
347da2e3ebdSchin 				for(ptrcom=com;*ptrcom;ptrcom++)
348da2e3ebdSchin 					/* trim directory prefix */
349da2e3ebdSchin 					*ptrcom = path_basename(*ptrcom);
350da2e3ebdSchin 			}
351da2e3ebdSchin 			sfputc(sfstderr,'\n');
352da2e3ebdSchin 			sh_menu(sfstderr,narg,com);
353da2e3ebdSchin 			sfsync(sfstderr);
354da2e3ebdSchin 			ep->e_nlist = narg;
355da2e3ebdSchin 			ep->e_clist = com;
356da2e3ebdSchin 			goto done;
357da2e3ebdSchin 		}
358da2e3ebdSchin 		/* see if there is enough room */
359da2e3ebdSchin 		size = *eol - (out-begin);
360da2e3ebdSchin 		if(mode=='\\')
361da2e3ebdSchin 		{
362da2e3ebdSchin 			int c;
363da2e3ebdSchin 			if(dir)
364da2e3ebdSchin 			{
365da2e3ebdSchin 				c = *dir;
366da2e3ebdSchin 				*dir = 0;
367da2e3ebdSchin 				saveout = begin;
368da2e3ebdSchin 			}
369da2e3ebdSchin 			if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0))
370da2e3ebdSchin 				nocase = (strchr(saveout,'c')!=0);
371da2e3ebdSchin 			if(dir)
372da2e3ebdSchin 				*dir = c;
373da2e3ebdSchin 			/* just expand until name is unique */
374da2e3ebdSchin 			size += strlen(*com);
375da2e3ebdSchin 		}
376da2e3ebdSchin 		else
377da2e3ebdSchin 		{
378da2e3ebdSchin 			size += narg;
379da2e3ebdSchin 			{
380da2e3ebdSchin 				char **savcom = com;
381da2e3ebdSchin 				while (*com)
38234f9b3eeSRoland Mainz 					size += strlen(cp=fmtx(*com++));
383da2e3ebdSchin 				com = savcom;
384da2e3ebdSchin 			}
385da2e3ebdSchin 		}
386da2e3ebdSchin 		/* see if room for expansion */
387da2e3ebdSchin 		if(outbuff+size >= &outbuff[MAXLINE])
388da2e3ebdSchin 		{
389da2e3ebdSchin 			com[0] = ap->argval;
390da2e3ebdSchin 			com[1] = 0;
391da2e3ebdSchin 		}
392da2e3ebdSchin 		/* save remainder of the buffer */
393da2e3ebdSchin 		if(*out)
394da2e3ebdSchin 			left=stakcopy(out);
395da2e3ebdSchin 		if(cmd_completion && mode=='\\')
396da2e3ebdSchin 			out = strcopy(begin,path_basename(cp= *com++));
397da2e3ebdSchin 		else if(mode=='*')
398da2e3ebdSchin 		{
399da2e3ebdSchin 			if(ep->e_nlist && dir && var)
400da2e3ebdSchin 			{
401da2e3ebdSchin 				if(*cp==var)
402da2e3ebdSchin 					cp++;
403da2e3ebdSchin 				else
404da2e3ebdSchin 					*begin++ = var;
405da2e3ebdSchin 				out = strcopy(begin,cp);
406da2e3ebdSchin 				var = 0;
407da2e3ebdSchin 			}
408da2e3ebdSchin 			else
40934f9b3eeSRoland Mainz 				out = strcopy(begin,fmtx(*com));
410da2e3ebdSchin 			com++;
411da2e3ebdSchin 		}
412da2e3ebdSchin 		else
413da2e3ebdSchin 			out = strcopy(begin,*com++);
414da2e3ebdSchin 		if(mode=='\\')
415da2e3ebdSchin 		{
416da2e3ebdSchin 			saveout= ++out;
417da2e3ebdSchin 			while (*com && *begin)
418da2e3ebdSchin 			{
419da2e3ebdSchin 				if(cmd_completion)
420da2e3ebdSchin 					out = overlaid(begin,path_basename(*com++),nocase);
421da2e3ebdSchin 				else
422da2e3ebdSchin 					out = overlaid(begin,*com++,nocase);
423da2e3ebdSchin 			}
424da2e3ebdSchin 			mode = (out==saveout);
425da2e3ebdSchin 			if(out[-1]==0)
426da2e3ebdSchin 				out--;
427da2e3ebdSchin 			if(mode && out[-1]!='/')
428da2e3ebdSchin 			{
429da2e3ebdSchin 				if(cmd_completion)
430da2e3ebdSchin 				{
431da2e3ebdSchin 					Namval_t *np;
432da2e3ebdSchin 					/* add as tracked alias */
433da2e3ebdSchin 					Pathcomp_t *pp;
434*b30d1939SAndy Fiddaman 					if(*cp=='/' && (pp=path_dirfind(ep->sh->pathlist,cp,'/')) && (np=nv_search(begin,ep->sh->track_tree,NV_ADD)))
435da2e3ebdSchin 						path_alias(np,pp);
436da2e3ebdSchin 					out = strcopy(begin,cp);
437da2e3ebdSchin 				}
438da2e3ebdSchin 				/* add quotes if necessary */
43934f9b3eeSRoland Mainz 				if((cp=fmtx(begin))!=begin)
440da2e3ebdSchin 					out = strcopy(begin,cp);
441da2e3ebdSchin 				if(var=='$' && begin[-1]=='{')
442da2e3ebdSchin 					*out = '}';
443da2e3ebdSchin 				else
444da2e3ebdSchin 					*out = ' ';
445da2e3ebdSchin 				*++out = 0;
446da2e3ebdSchin 			}
44734f9b3eeSRoland Mainz 			else if((cp=fmtx(begin))!=begin)
448da2e3ebdSchin 			{
449da2e3ebdSchin 				out = strcopy(begin,cp);
450da2e3ebdSchin 				if(out[-1] =='"' || out[-1]=='\'')
45134f9b3eeSRoland Mainz 					  *--out = 0;
452da2e3ebdSchin 			}
453da2e3ebdSchin 			if(*begin==0)
454da2e3ebdSchin 				ed_ringbell();
455da2e3ebdSchin 		}
456da2e3ebdSchin 		else
457da2e3ebdSchin 		{
458da2e3ebdSchin 			while (*com)
459da2e3ebdSchin 			{
460da2e3ebdSchin 				*out++  = ' ';
46134f9b3eeSRoland Mainz 				out = strcopy(out,fmtx(*com++));
462da2e3ebdSchin 			}
463da2e3ebdSchin 		}
464da2e3ebdSchin 		if(ep->e_nlist)
465da2e3ebdSchin 		{
466da2e3ebdSchin 			cp = com[-1];
467da2e3ebdSchin 			if(cp[strlen(cp)-1]!='/')
468da2e3ebdSchin 			{
469da2e3ebdSchin 				if(var=='$' && begin[-1]=='{')
470da2e3ebdSchin 					*out = '}';
471da2e3ebdSchin 				else
472da2e3ebdSchin 					*out = ' ';
473da2e3ebdSchin 				out++;
474da2e3ebdSchin 			}
475da2e3ebdSchin 			else if(out[-1] =='"' || out[-1]=='\'')
476da2e3ebdSchin 				out--;
477da2e3ebdSchin 			*out = 0;
478da2e3ebdSchin 		}
479da2e3ebdSchin 		*cur = (out-outbuff);
480da2e3ebdSchin 		/* restore rest of buffer */
481da2e3ebdSchin 		if(left)
482da2e3ebdSchin 			out = strcopy(out,left);
483da2e3ebdSchin 		*eol = (out-outbuff);
484da2e3ebdSchin 	}
485da2e3ebdSchin  done:
486da2e3ebdSchin 	sh_offstate(SH_FCOMPLETE);
487da2e3ebdSchin 	if(!ep->e_nlist)
488da2e3ebdSchin 		stakset(ep->e_stkptr,ep->e_stkoff);
489da2e3ebdSchin 	if(nomarkdirs)
490da2e3ebdSchin 		sh_offoption(SH_MARKDIRS);
491da2e3ebdSchin #if SHOPT_MULTIBYTE
492da2e3ebdSchin 	{
493da2e3ebdSchin 		register int c,n=0;
494da2e3ebdSchin 		/* first re-adjust cur */
495da2e3ebdSchin 		c = outbuff[*cur];
496da2e3ebdSchin 		outbuff[*cur] = 0;
497da2e3ebdSchin 		for(out=outbuff; *out;n++)
498da2e3ebdSchin 			mbchar(out);
499da2e3ebdSchin 		outbuff[*cur] = c;
500da2e3ebdSchin 		*cur = n;
501da2e3ebdSchin 		outbuff[*eol+1] = 0;
502da2e3ebdSchin 		*eol = ed_internal(outbuff,(genchar*)outbuff);
503da2e3ebdSchin 	}
504da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
505da2e3ebdSchin 	return(rval);
506da2e3ebdSchin }
507da2e3ebdSchin 
508da2e3ebdSchin /*
509da2e3ebdSchin  * look for edit macro named _i
510da2e3ebdSchin  * if found, puts the macro definition into lookahead buffer and returns 1
511da2e3ebdSchin  */
ed_macro(Edit_t * ep,register int i)512da2e3ebdSchin int ed_macro(Edit_t *ep, register int i)
513da2e3ebdSchin {
514da2e3ebdSchin 	register char *out;
515da2e3ebdSchin 	Namval_t *np;
516da2e3ebdSchin 	genchar buff[LOOKAHEAD+1];
517da2e3ebdSchin 	if(i != '@')
518da2e3ebdSchin 		ep->e_macro[1] = i;
519da2e3ebdSchin 	/* undocumented feature, macros of the form <ESC>[c evoke alias __c */
520da2e3ebdSchin 	if(i=='_')
521da2e3ebdSchin 		ep->e_macro[2] = ed_getchar(ep,1);
522da2e3ebdSchin 	else
523da2e3ebdSchin 		ep->e_macro[2] = 0;
524*b30d1939SAndy Fiddaman 	if (isalnum(i)&&(np=nv_search(ep->e_macro,ep->sh->alias_tree,HASH_SCOPE))&&(out=nv_getval(np)))
525da2e3ebdSchin 	{
526da2e3ebdSchin #if SHOPT_MULTIBYTE
527da2e3ebdSchin 		/* copy to buff in internal representation */
528da2e3ebdSchin 		int c = 0;
529da2e3ebdSchin 		if( strlen(out) > LOOKAHEAD )
530da2e3ebdSchin 		{
531da2e3ebdSchin 			c = out[LOOKAHEAD];
532da2e3ebdSchin 			out[LOOKAHEAD] = 0;
533da2e3ebdSchin 		}
534da2e3ebdSchin 		i = ed_internal(out,buff);
535da2e3ebdSchin 		if(c)
536da2e3ebdSchin 			out[LOOKAHEAD] = c;
537da2e3ebdSchin #else
538da2e3ebdSchin 		strncpy((char*)buff,out,LOOKAHEAD);
539da2e3ebdSchin 		buff[LOOKAHEAD] = 0;
540da2e3ebdSchin 		i = strlen((char*)buff);
541da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
542da2e3ebdSchin 		while(i-- > 0)
543da2e3ebdSchin 			ed_ungetchar(ep,buff[i]);
544da2e3ebdSchin 		return(1);
545da2e3ebdSchin 	}
546da2e3ebdSchin 	return(0);
547da2e3ebdSchin }
548da2e3ebdSchin 
549da2e3ebdSchin /*
550da2e3ebdSchin  * Enter the fc command on the current history line
551da2e3ebdSchin  */
ed_fulledit(Edit_t * ep)552da2e3ebdSchin int ed_fulledit(Edit_t *ep)
553da2e3ebdSchin {
554da2e3ebdSchin 	register char *cp;
555*b30d1939SAndy Fiddaman 	if(!shgd->hist_ptr)
556da2e3ebdSchin 		return(-1);
557da2e3ebdSchin 	/* use EDITOR on current command */
558da2e3ebdSchin 	if(ep->e_hline == ep->e_hismax)
559da2e3ebdSchin 	{
560da2e3ebdSchin 		if(ep->e_eol<0)
561da2e3ebdSchin 			return(-1);
562da2e3ebdSchin #if SHOPT_MULTIBYTE
563da2e3ebdSchin 		ep->e_inbuf[ep->e_eol+1] = 0;
564da2e3ebdSchin 		ed_external(ep->e_inbuf, (char *)ep->e_inbuf);
565da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
566*b30d1939SAndy Fiddaman 		sfwrite(shgd->hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1);
567da2e3ebdSchin 		sh_onstate(SH_HISTORY);
568*b30d1939SAndy Fiddaman 		hist_flush(shgd->hist_ptr);
569da2e3ebdSchin 	}
570da2e3ebdSchin 	cp = strcopy((char*)ep->e_inbuf,e_runvi);
571da2e3ebdSchin 	cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0));
572da2e3ebdSchin 	ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0);
573da2e3ebdSchin 	return(0);
574da2e3ebdSchin }
575