xref: /titanic_50/usr/src/lib/libshell/common/edit/completion.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*3e14f97fSRoger A. Faulkner *          Copyright (c) 1982-2010 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6da2e3ebdSchin *                  Common Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10da2e3ebdSchin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11da2e3ebdSchin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
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];
4334f9b3eeSRoland Mainz 	int offset;
4434f9b3eeSRoland Mainz 	while((c=mbchar(cp)),(c>UCHAR_MAX)||(n=state[c])==0);
4534f9b3eeSRoland Mainz 	if(n==S_EOF)
4634f9b3eeSRoland Mainz 		return((char*)string);
4734f9b3eeSRoland Mainz 	offset = staktell();
4834f9b3eeSRoland Mainz 	stakwrite(string,--cp-string);
4934f9b3eeSRoland Mainz 	while(c=mbchar(cp))
5034f9b3eeSRoland Mainz 	{
5134f9b3eeSRoland Mainz 		if(state[c])
5234f9b3eeSRoland Mainz 			stakputc('\\');
5334f9b3eeSRoland Mainz 		stakputc(c);
5434f9b3eeSRoland Mainz 	}
5534f9b3eeSRoland Mainz 	stakputc(0);
5634f9b3eeSRoland Mainz 	return(stakptr(offset));
5734f9b3eeSRoland Mainz }
5834f9b3eeSRoland Mainz 
charcmp(int a,int b,int nocase)59da2e3ebdSchin static int charcmp(int a, int b, int nocase)
60da2e3ebdSchin {
61da2e3ebdSchin 	if(nocase)
62da2e3ebdSchin 	{
63da2e3ebdSchin 		if(isupper(a))
64da2e3ebdSchin 			a = tolower(a);
65da2e3ebdSchin 		if(isupper(b))
66da2e3ebdSchin 			b = tolower(b);
67da2e3ebdSchin 	}
68da2e3ebdSchin 	return(a==b);
69da2e3ebdSchin }
70da2e3ebdSchin 
71da2e3ebdSchin /*
72da2e3ebdSchin  *  overwrites <str> to common prefix of <str> and <newstr>
73da2e3ebdSchin  *  if <str> is equal to <newstr> returns  <str>+strlen(<str>)+1
74da2e3ebdSchin  *  otherwise returns <str>+strlen(<str>)
75da2e3ebdSchin  */
overlaid(register char * str,register const char * newstr,int nocase)76da2e3ebdSchin static char *overlaid(register char *str,register const char *newstr,int nocase)
77da2e3ebdSchin {
78da2e3ebdSchin 	register int c,d;
79da2e3ebdSchin 	while((c= *(unsigned char *)str) && ((d= *(unsigned char*)newstr++),charcmp(c,d,nocase)))
80da2e3ebdSchin 		str++;
81da2e3ebdSchin 	if(*str)
82da2e3ebdSchin 		*str = 0;
83da2e3ebdSchin 	else if(*newstr==0)
84da2e3ebdSchin 		str++;
85da2e3ebdSchin 	return(str);
86da2e3ebdSchin }
87da2e3ebdSchin 
88da2e3ebdSchin 
89da2e3ebdSchin /*
90da2e3ebdSchin  * returns pointer to beginning of expansion and sets type of expansion
91da2e3ebdSchin  */
find_begin(char outbuff[],char * last,int endchar,int * type)92da2e3ebdSchin static char *find_begin(char outbuff[], char *last, int endchar, int *type)
93da2e3ebdSchin {
94da2e3ebdSchin 	register char	*cp=outbuff, *bp, *xp;
957c2fbfb3SApril Chin 	register int 	c,inquote = 0, inassign=0;
967c2fbfb3SApril Chin 	int		mode=*type;
97da2e3ebdSchin 	bp = outbuff;
98da2e3ebdSchin 	*type = 0;
99da2e3ebdSchin 	while(cp < last)
100da2e3ebdSchin 	{
101da2e3ebdSchin 		xp = cp;
102da2e3ebdSchin 		switch(c= mbchar(cp))
103da2e3ebdSchin 		{
104da2e3ebdSchin 		    case '\'': case '"':
105da2e3ebdSchin 			if(!inquote)
106da2e3ebdSchin 			{
107da2e3ebdSchin 				inquote = c;
108da2e3ebdSchin 				bp = xp;
109da2e3ebdSchin 				break;
110da2e3ebdSchin 			}
111da2e3ebdSchin 			if(inquote==c)
112da2e3ebdSchin 				inquote = 0;
113da2e3ebdSchin 			break;
114da2e3ebdSchin 		    case '\\':
115da2e3ebdSchin 			if(inquote != '\'')
116da2e3ebdSchin 				mbchar(cp);
117da2e3ebdSchin 			break;
118da2e3ebdSchin 		    case '$':
119da2e3ebdSchin 			if(inquote == '\'')
120da2e3ebdSchin 				break;
121da2e3ebdSchin 			c = *(unsigned char*)cp;
1227c2fbfb3SApril Chin 			if(mode!='*' && (isaletter(c) || c=='{'))
123da2e3ebdSchin 			{
124da2e3ebdSchin 				int dot = '.';
125da2e3ebdSchin 				if(c=='{')
126da2e3ebdSchin 				{
127da2e3ebdSchin 					xp = cp;
128da2e3ebdSchin 					mbchar(cp);
129da2e3ebdSchin 					c = *(unsigned char*)cp;
130da2e3ebdSchin 					if(c!='.' && !isaletter(c))
131da2e3ebdSchin 						break;
132da2e3ebdSchin 				}
133da2e3ebdSchin 				else
134da2e3ebdSchin 					dot = 'a';
135da2e3ebdSchin 				while(cp < last)
136da2e3ebdSchin 				{
137da2e3ebdSchin 					if((c= mbchar(cp)) , c!=dot && !isaname(c))
138da2e3ebdSchin 						break;
139da2e3ebdSchin 				}
1407c2fbfb3SApril Chin 				if(cp>=last && c!= '}')
141da2e3ebdSchin 				{
142da2e3ebdSchin 					*type='$';
143da2e3ebdSchin 					return(++xp);
144da2e3ebdSchin 				}
145da2e3ebdSchin 			}
146da2e3ebdSchin 			else if(c=='(')
147da2e3ebdSchin 			{
1487c2fbfb3SApril Chin 				*type = mode;
149da2e3ebdSchin 				xp = find_begin(cp,last,')',type);
150da2e3ebdSchin 				if(*(cp=xp)!=')')
151da2e3ebdSchin 					bp = xp;
152da2e3ebdSchin 				else
153da2e3ebdSchin 					cp++;
154da2e3ebdSchin 			}
155da2e3ebdSchin 			break;
156da2e3ebdSchin 		    case '=':
157da2e3ebdSchin 			if(!inquote)
1587c2fbfb3SApril Chin 			{
1597c2fbfb3SApril Chin 				bp = cp;
1607c2fbfb3SApril Chin 				inassign = 1;
1617c2fbfb3SApril Chin 			}
1627c2fbfb3SApril Chin 			break;
1637c2fbfb3SApril Chin 		    case ':':
1647c2fbfb3SApril Chin 			if(!inquote && inassign)
165da2e3ebdSchin 				bp = cp;
166da2e3ebdSchin 			break;
167da2e3ebdSchin 		    case '~':
168da2e3ebdSchin 			if(*cp=='(')
169da2e3ebdSchin 				break;
170da2e3ebdSchin 			/* fall through */
171da2e3ebdSchin 		    default:
172da2e3ebdSchin 			if(c && c==endchar)
173da2e3ebdSchin 				return(xp);
174da2e3ebdSchin 			if(!inquote && ismeta(c))
1757c2fbfb3SApril Chin 			{
176da2e3ebdSchin 				bp = cp;
1777c2fbfb3SApril Chin 				inassign = 0;
1787c2fbfb3SApril Chin 			}
179da2e3ebdSchin 			break;
180da2e3ebdSchin 		}
181da2e3ebdSchin 	}
182da2e3ebdSchin 	if(inquote && *bp==inquote)
183da2e3ebdSchin 		*type = *bp++;
184da2e3ebdSchin 	return(bp);
185da2e3ebdSchin }
186da2e3ebdSchin 
187da2e3ebdSchin /*
188da2e3ebdSchin  * file name generation for edit modes
189da2e3ebdSchin  * non-zero exit for error, <0 ring bell
190da2e3ebdSchin  * don't search back past beginning of the buffer
191da2e3ebdSchin  * mode is '*' for inline expansion,
192da2e3ebdSchin  * mode is '\' for filename completion
193da2e3ebdSchin  * mode is '=' cause files to be listed in select format
194da2e3ebdSchin  */
195da2e3ebdSchin 
ed_expand(Edit_t * ep,char outbuff[],int * cur,int * eol,int mode,int count)196da2e3ebdSchin int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count)
197da2e3ebdSchin {
198da2e3ebdSchin 	struct comnod	*comptr;
199da2e3ebdSchin 	struct argnod	*ap;
200da2e3ebdSchin 	register char	*out;
201da2e3ebdSchin 	char 		*av[2], *begin , *dir=0;
202da2e3ebdSchin 	int		addstar=0, rval=0, var=0, strip=1;
203da2e3ebdSchin 	int 		nomarkdirs = !sh_isoption(SH_MARKDIRS);
204da2e3ebdSchin 	sh_onstate(SH_FCOMPLETE);
205da2e3ebdSchin 	if(ep->e_nlist)
206da2e3ebdSchin 	{
207da2e3ebdSchin 		if(mode=='=' && count>0)
208da2e3ebdSchin 		{
209da2e3ebdSchin 			if(count> ep->e_nlist)
210da2e3ebdSchin 				return(-1);
2117c2fbfb3SApril Chin 			mode = '?';
212da2e3ebdSchin 			av[0] = ep->e_clist[count-1];
213da2e3ebdSchin 			av[1] = 0;
214da2e3ebdSchin 		}
215da2e3ebdSchin 		else
216da2e3ebdSchin 		{
217da2e3ebdSchin 			stakset(ep->e_stkptr,ep->e_stkoff);
218da2e3ebdSchin 			ep->e_nlist = 0;
219da2e3ebdSchin 		}
220da2e3ebdSchin 	}
221da2e3ebdSchin 	comptr = (struct comnod*)stakalloc(sizeof(struct comnod));
222da2e3ebdSchin 	ap = (struct argnod*)stakseek(ARGVAL);
223da2e3ebdSchin #if SHOPT_MULTIBYTE
224da2e3ebdSchin 	{
225da2e3ebdSchin 		register int c = *cur;
226da2e3ebdSchin 		register genchar *cp;
227da2e3ebdSchin 		/* adjust cur */
228da2e3ebdSchin 		cp = (genchar *)outbuff + *cur;
229da2e3ebdSchin 		c = *cp;
230da2e3ebdSchin 		*cp = 0;
231da2e3ebdSchin 		*cur = ed_external((genchar*)outbuff,(char*)stakptr(0));
232da2e3ebdSchin 		*cp = c;
233da2e3ebdSchin 		*eol = ed_external((genchar*)outbuff,outbuff);
234da2e3ebdSchin 	}
235da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
236da2e3ebdSchin 	out = outbuff + *cur + (sh_isoption(SH_VI)!=0);
237da2e3ebdSchin 	comptr->comtyp = COMSCAN;
238da2e3ebdSchin 	comptr->comarg = ap;
239da2e3ebdSchin 	ap->argflag = (ARG_MAC|ARG_EXP);
240da2e3ebdSchin 	ap->argnxt.ap = 0;
241da2e3ebdSchin 	ap->argchn.cp = 0;
242da2e3ebdSchin 	{
243da2e3ebdSchin 		register int c;
244da2e3ebdSchin 		char *last = out;
245da2e3ebdSchin 		c =  *(unsigned char*)out;
2467c2fbfb3SApril Chin 		var = mode;
247da2e3ebdSchin 		begin = out = find_begin(outbuff,last,0,&var);
248da2e3ebdSchin 		/* addstar set to zero if * should not be added */
249da2e3ebdSchin 		if(var=='$')
250da2e3ebdSchin 		{
251da2e3ebdSchin 			stakputs("${!");
252da2e3ebdSchin 			stakwrite(out,last-out);
253da2e3ebdSchin 			stakputs("@}");
254da2e3ebdSchin 			out = last;
255da2e3ebdSchin 		}
256da2e3ebdSchin 		else
257da2e3ebdSchin 		{
258da2e3ebdSchin 			addstar = '*';
259da2e3ebdSchin 			while(out < last)
260da2e3ebdSchin 			{
261da2e3ebdSchin 				c = *(unsigned char*)out;
262da2e3ebdSchin 				if(isexp(c))
263da2e3ebdSchin 					addstar = 0;
264da2e3ebdSchin 				if (c == '/')
265da2e3ebdSchin 				{
266da2e3ebdSchin 					if(addstar == 0)
267da2e3ebdSchin 						strip = 0;
268da2e3ebdSchin 					dir = out+1;
269da2e3ebdSchin 				}
270da2e3ebdSchin 				stakputc(c);
271da2e3ebdSchin 				out++;
272da2e3ebdSchin 			}
273da2e3ebdSchin 		}
2747c2fbfb3SApril Chin 		if(mode=='?')
2757c2fbfb3SApril Chin 			mode = '*';
276da2e3ebdSchin 		if(var!='$' && mode=='\\' && out[-1]!='*')
277da2e3ebdSchin 			addstar = '*';
278da2e3ebdSchin 		if(*begin=='~' && !strchr(begin,'/'))
279da2e3ebdSchin 			addstar = 0;
280da2e3ebdSchin 		stakputc(addstar);
281da2e3ebdSchin 		ap = (struct argnod*)stakfreeze(1);
282da2e3ebdSchin 	}
283da2e3ebdSchin 	if(mode!='*')
284da2e3ebdSchin 		sh_onoption(SH_MARKDIRS);
285da2e3ebdSchin 	{
286da2e3ebdSchin 		register char	**com;
287da2e3ebdSchin 		char		*cp=begin, *left=0, *saveout=".";
288da2e3ebdSchin 		int	 	nocase=0,narg,cmd_completion=0;
289da2e3ebdSchin 		register 	int size='x';
290da2e3ebdSchin 		while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t'))
291da2e3ebdSchin 			cp--;
292da2e3ebdSchin 		if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&sh.nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' )))
293da2e3ebdSchin 		{
294da2e3ebdSchin 			cmd_completion=1;
295da2e3ebdSchin 			sh_onstate(SH_COMPLETE);
296da2e3ebdSchin 		}
297da2e3ebdSchin 		if(ep->e_nlist)
298da2e3ebdSchin 		{
299da2e3ebdSchin 			narg = 1;
300da2e3ebdSchin 			com = av;
301da2e3ebdSchin 			if(dir)
302da2e3ebdSchin 				begin += (dir-begin);
303da2e3ebdSchin 		}
304da2e3ebdSchin 		else
305da2e3ebdSchin 		{
3067c2fbfb3SApril Chin 			com = sh_argbuild(ep->sh,&narg,comptr,0);
307da2e3ebdSchin 			/* special handling for leading quotes */
308da2e3ebdSchin 			if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\''))
309da2e3ebdSchin 			begin--;
310da2e3ebdSchin 		}
311da2e3ebdSchin 		sh_offstate(SH_COMPLETE);
312da2e3ebdSchin                 /* allow a search to be aborted */
313da2e3ebdSchin 		if(sh.trapnote&SH_SIGSET)
314da2e3ebdSchin 		{
315da2e3ebdSchin 			rval = -1;
316da2e3ebdSchin 			goto done;
317da2e3ebdSchin 		}
318da2e3ebdSchin 		/*  match? */
319da2e3ebdSchin 		if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*')))
320da2e3ebdSchin 		{
321da2e3ebdSchin 			rval = -1;
322da2e3ebdSchin 			goto done;
323da2e3ebdSchin 		}
324da2e3ebdSchin 		if(mode=='=')
325da2e3ebdSchin 		{
326da2e3ebdSchin 			if (strip && !cmd_completion)
327da2e3ebdSchin 			{
328da2e3ebdSchin 				register char **ptrcom;
329da2e3ebdSchin 				for(ptrcom=com;*ptrcom;ptrcom++)
330da2e3ebdSchin 					/* trim directory prefix */
331da2e3ebdSchin 					*ptrcom = path_basename(*ptrcom);
332da2e3ebdSchin 			}
333da2e3ebdSchin 			sfputc(sfstderr,'\n');
334da2e3ebdSchin 			sh_menu(sfstderr,narg,com);
335da2e3ebdSchin 			sfsync(sfstderr);
336da2e3ebdSchin 			ep->e_nlist = narg;
337da2e3ebdSchin 			ep->e_clist = com;
338da2e3ebdSchin 			goto done;
339da2e3ebdSchin 		}
340da2e3ebdSchin 		/* see if there is enough room */
341da2e3ebdSchin 		size = *eol - (out-begin);
342da2e3ebdSchin 		if(mode=='\\')
343da2e3ebdSchin 		{
344da2e3ebdSchin 			int c;
345da2e3ebdSchin 			if(dir)
346da2e3ebdSchin 			{
347da2e3ebdSchin 				c = *dir;
348da2e3ebdSchin 				*dir = 0;
349da2e3ebdSchin 				saveout = begin;
350da2e3ebdSchin 			}
351da2e3ebdSchin 			if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0))
352da2e3ebdSchin 				nocase = (strchr(saveout,'c')!=0);
353da2e3ebdSchin 			if(dir)
354da2e3ebdSchin 				*dir = c;
355da2e3ebdSchin 			/* just expand until name is unique */
356da2e3ebdSchin 			size += strlen(*com);
357da2e3ebdSchin 		}
358da2e3ebdSchin 		else
359da2e3ebdSchin 		{
360da2e3ebdSchin 			size += narg;
361da2e3ebdSchin 			{
362da2e3ebdSchin 				char **savcom = com;
363da2e3ebdSchin 				while (*com)
36434f9b3eeSRoland Mainz 					size += strlen(cp=fmtx(*com++));
365da2e3ebdSchin 				com = savcom;
366da2e3ebdSchin 			}
367da2e3ebdSchin 		}
368da2e3ebdSchin 		/* see if room for expansion */
369da2e3ebdSchin 		if(outbuff+size >= &outbuff[MAXLINE])
370da2e3ebdSchin 		{
371da2e3ebdSchin 			com[0] = ap->argval;
372da2e3ebdSchin 			com[1] = 0;
373da2e3ebdSchin 		}
374da2e3ebdSchin 		/* save remainder of the buffer */
375da2e3ebdSchin 		if(*out)
376da2e3ebdSchin 			left=stakcopy(out);
377da2e3ebdSchin 		if(cmd_completion && mode=='\\')
378da2e3ebdSchin 			out = strcopy(begin,path_basename(cp= *com++));
379da2e3ebdSchin 		else if(mode=='*')
380da2e3ebdSchin 		{
381da2e3ebdSchin 			if(ep->e_nlist && dir && var)
382da2e3ebdSchin 			{
383da2e3ebdSchin 				if(*cp==var)
384da2e3ebdSchin 					cp++;
385da2e3ebdSchin 				else
386da2e3ebdSchin 					*begin++ = var;
387da2e3ebdSchin 				out = strcopy(begin,cp);
388da2e3ebdSchin 				var = 0;
389da2e3ebdSchin 			}
390da2e3ebdSchin 			else
39134f9b3eeSRoland Mainz 				out = strcopy(begin,fmtx(*com));
392da2e3ebdSchin 			com++;
393da2e3ebdSchin 		}
394da2e3ebdSchin 		else
395da2e3ebdSchin 			out = strcopy(begin,*com++);
396da2e3ebdSchin 		if(mode=='\\')
397da2e3ebdSchin 		{
398da2e3ebdSchin 			saveout= ++out;
399da2e3ebdSchin 			while (*com && *begin)
400da2e3ebdSchin 			{
401da2e3ebdSchin 				if(cmd_completion)
402da2e3ebdSchin 					out = overlaid(begin,path_basename(*com++),nocase);
403da2e3ebdSchin 				else
404da2e3ebdSchin 					out = overlaid(begin,*com++,nocase);
405da2e3ebdSchin 			}
406da2e3ebdSchin 			mode = (out==saveout);
407da2e3ebdSchin 			if(out[-1]==0)
408da2e3ebdSchin 				out--;
409da2e3ebdSchin 			if(mode && out[-1]!='/')
410da2e3ebdSchin 			{
411da2e3ebdSchin 				if(cmd_completion)
412da2e3ebdSchin 				{
413da2e3ebdSchin 					Namval_t *np;
414da2e3ebdSchin 					/* add as tracked alias */
415da2e3ebdSchin 					Pathcomp_t *pp;
416da2e3ebdSchin 					if(*cp=='/' && (pp=path_dirfind(sh.pathlist,cp,'/')) && (np=nv_search(begin,sh.track_tree,NV_ADD)))
417da2e3ebdSchin 						path_alias(np,pp);
418da2e3ebdSchin 					out = strcopy(begin,cp);
419da2e3ebdSchin 				}
420da2e3ebdSchin 				/* add quotes if necessary */
42134f9b3eeSRoland Mainz 				if((cp=fmtx(begin))!=begin)
422da2e3ebdSchin 					out = strcopy(begin,cp);
423da2e3ebdSchin 				if(var=='$' && begin[-1]=='{')
424da2e3ebdSchin 					*out = '}';
425da2e3ebdSchin 				else
426da2e3ebdSchin 					*out = ' ';
427da2e3ebdSchin 				*++out = 0;
428da2e3ebdSchin 			}
42934f9b3eeSRoland Mainz 			else if((cp=fmtx(begin))!=begin)
430da2e3ebdSchin 			{
431da2e3ebdSchin 				out = strcopy(begin,cp);
432da2e3ebdSchin 				if(out[-1] =='"' || out[-1]=='\'')
43334f9b3eeSRoland Mainz 					  *--out = 0;
434da2e3ebdSchin 			}
435da2e3ebdSchin 			if(*begin==0)
436da2e3ebdSchin 				ed_ringbell();
437da2e3ebdSchin 		}
438da2e3ebdSchin 		else
439da2e3ebdSchin 		{
440da2e3ebdSchin 			while (*com)
441da2e3ebdSchin 			{
442da2e3ebdSchin 				*out++  = ' ';
44334f9b3eeSRoland Mainz 				out = strcopy(out,fmtx(*com++));
444da2e3ebdSchin 			}
445da2e3ebdSchin 		}
446da2e3ebdSchin 		if(ep->e_nlist)
447da2e3ebdSchin 		{
448da2e3ebdSchin 			cp = com[-1];
449da2e3ebdSchin 			if(cp[strlen(cp)-1]!='/')
450da2e3ebdSchin 			{
451da2e3ebdSchin 				if(var=='$' && begin[-1]=='{')
452da2e3ebdSchin 					*out = '}';
453da2e3ebdSchin 				else
454da2e3ebdSchin 					*out = ' ';
455da2e3ebdSchin 				out++;
456da2e3ebdSchin 			}
457da2e3ebdSchin 			else if(out[-1] =='"' || out[-1]=='\'')
458da2e3ebdSchin 				out--;
459da2e3ebdSchin 			*out = 0;
460da2e3ebdSchin 		}
461da2e3ebdSchin 		*cur = (out-outbuff);
462da2e3ebdSchin 		/* restore rest of buffer */
463da2e3ebdSchin 		if(left)
464da2e3ebdSchin 			out = strcopy(out,left);
465da2e3ebdSchin 		*eol = (out-outbuff);
466da2e3ebdSchin 	}
467da2e3ebdSchin  done:
468da2e3ebdSchin 	sh_offstate(SH_FCOMPLETE);
469da2e3ebdSchin 	if(!ep->e_nlist)
470da2e3ebdSchin 		stakset(ep->e_stkptr,ep->e_stkoff);
471da2e3ebdSchin 	if(nomarkdirs)
472da2e3ebdSchin 		sh_offoption(SH_MARKDIRS);
473da2e3ebdSchin #if SHOPT_MULTIBYTE
474da2e3ebdSchin 	{
475da2e3ebdSchin 		register int c,n=0;
476da2e3ebdSchin 		/* first re-adjust cur */
477da2e3ebdSchin 		c = outbuff[*cur];
478da2e3ebdSchin 		outbuff[*cur] = 0;
479da2e3ebdSchin 		for(out=outbuff; *out;n++)
480da2e3ebdSchin 			mbchar(out);
481da2e3ebdSchin 		outbuff[*cur] = c;
482da2e3ebdSchin 		*cur = n;
483da2e3ebdSchin 		outbuff[*eol+1] = 0;
484da2e3ebdSchin 		*eol = ed_internal(outbuff,(genchar*)outbuff);
485da2e3ebdSchin 	}
486da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
487da2e3ebdSchin 	return(rval);
488da2e3ebdSchin }
489da2e3ebdSchin 
490da2e3ebdSchin /*
491da2e3ebdSchin  * look for edit macro named _i
492da2e3ebdSchin  * if found, puts the macro definition into lookahead buffer and returns 1
493da2e3ebdSchin  */
ed_macro(Edit_t * ep,register int i)494da2e3ebdSchin int ed_macro(Edit_t *ep, register int i)
495da2e3ebdSchin {
496da2e3ebdSchin 	register char *out;
497da2e3ebdSchin 	Namval_t *np;
498da2e3ebdSchin 	genchar buff[LOOKAHEAD+1];
499da2e3ebdSchin 	if(i != '@')
500da2e3ebdSchin 		ep->e_macro[1] = i;
501da2e3ebdSchin 	/* undocumented feature, macros of the form <ESC>[c evoke alias __c */
502da2e3ebdSchin 	if(i=='_')
503da2e3ebdSchin 		ep->e_macro[2] = ed_getchar(ep,1);
504da2e3ebdSchin 	else
505da2e3ebdSchin 		ep->e_macro[2] = 0;
506da2e3ebdSchin 	if (isalnum(i)&&(np=nv_search(ep->e_macro,sh.alias_tree,HASH_SCOPE))&&(out=nv_getval(np)))
507da2e3ebdSchin 	{
508da2e3ebdSchin #if SHOPT_MULTIBYTE
509da2e3ebdSchin 		/* copy to buff in internal representation */
510da2e3ebdSchin 		int c = 0;
511da2e3ebdSchin 		if( strlen(out) > LOOKAHEAD )
512da2e3ebdSchin 		{
513da2e3ebdSchin 			c = out[LOOKAHEAD];
514da2e3ebdSchin 			out[LOOKAHEAD] = 0;
515da2e3ebdSchin 		}
516da2e3ebdSchin 		i = ed_internal(out,buff);
517da2e3ebdSchin 		if(c)
518da2e3ebdSchin 			out[LOOKAHEAD] = c;
519da2e3ebdSchin #else
520da2e3ebdSchin 		strncpy((char*)buff,out,LOOKAHEAD);
521da2e3ebdSchin 		buff[LOOKAHEAD] = 0;
522da2e3ebdSchin 		i = strlen((char*)buff);
523da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
524da2e3ebdSchin 		while(i-- > 0)
525da2e3ebdSchin 			ed_ungetchar(ep,buff[i]);
526da2e3ebdSchin 		return(1);
527da2e3ebdSchin 	}
528da2e3ebdSchin 	return(0);
529da2e3ebdSchin }
530da2e3ebdSchin 
531da2e3ebdSchin /*
532da2e3ebdSchin  * Enter the fc command on the current history line
533da2e3ebdSchin  */
ed_fulledit(Edit_t * ep)534da2e3ebdSchin int ed_fulledit(Edit_t *ep)
535da2e3ebdSchin {
536da2e3ebdSchin 	register char *cp;
537da2e3ebdSchin 	if(!sh.hist_ptr)
538da2e3ebdSchin 		return(-1);
539da2e3ebdSchin 	/* use EDITOR on current command */
540da2e3ebdSchin 	if(ep->e_hline == ep->e_hismax)
541da2e3ebdSchin 	{
542da2e3ebdSchin 		if(ep->e_eol<0)
543da2e3ebdSchin 			return(-1);
544da2e3ebdSchin #if SHOPT_MULTIBYTE
545da2e3ebdSchin 		ep->e_inbuf[ep->e_eol+1] = 0;
546da2e3ebdSchin 		ed_external(ep->e_inbuf, (char *)ep->e_inbuf);
547da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
548da2e3ebdSchin 		sfwrite(sh.hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1);
549da2e3ebdSchin 		sh_onstate(SH_HISTORY);
550da2e3ebdSchin 		hist_flush(sh.hist_ptr);
551da2e3ebdSchin 	}
552da2e3ebdSchin 	cp = strcopy((char*)ep->e_inbuf,e_runvi);
553da2e3ebdSchin 	cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0));
554da2e3ebdSchin 	ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0);
555da2e3ebdSchin 	return(0);
556da2e3ebdSchin }
557