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