14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1982-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * David Korn <dgk@research.att.com> *
184887Schin * *
194887Schin ***********************************************************************/
204887Schin #pragma prototyped
214887Schin /*
224887Schin * completion.c - command and file completion for shell editors
234887Schin *
244887Schin */
254887Schin
264887Schin #include "defs.h"
274887Schin #include <ast_wchar.h>
284887Schin #include "lexstates.h"
294887Schin #include "path.h"
304887Schin #include "io.h"
314887Schin #include "edit.h"
324887Schin #include "history.h"
334887Schin
3410898Sroland.mainz@nrubsig.org #if !SHOPT_MULTIBYTE
3510898Sroland.mainz@nrubsig.org #define mbchar(p) (*(unsigned char*)p++)
3610898Sroland.mainz@nrubsig.org #endif
3710898Sroland.mainz@nrubsig.org
fmtx(const char * string)3810898Sroland.mainz@nrubsig.org static char *fmtx(const char *string)
3910898Sroland.mainz@nrubsig.org {
4010898Sroland.mainz@nrubsig.org register const char *cp = string;
4110898Sroland.mainz@nrubsig.org register int n,c;
4210898Sroland.mainz@nrubsig.org unsigned char *state = (unsigned char*)sh_lexstates[2];
4310898Sroland.mainz@nrubsig.org int offset;
4410898Sroland.mainz@nrubsig.org while((c=mbchar(cp)),(c>UCHAR_MAX)||(n=state[c])==0);
4510898Sroland.mainz@nrubsig.org if(n==S_EOF)
4610898Sroland.mainz@nrubsig.org return((char*)string);
4710898Sroland.mainz@nrubsig.org offset = staktell();
4810898Sroland.mainz@nrubsig.org stakwrite(string,--cp-string);
4910898Sroland.mainz@nrubsig.org while(c=mbchar(cp))
5010898Sroland.mainz@nrubsig.org {
5110898Sroland.mainz@nrubsig.org if(state[c])
5210898Sroland.mainz@nrubsig.org stakputc('\\');
5310898Sroland.mainz@nrubsig.org stakputc(c);
5410898Sroland.mainz@nrubsig.org }
5510898Sroland.mainz@nrubsig.org stakputc(0);
5610898Sroland.mainz@nrubsig.org return(stakptr(offset));
5710898Sroland.mainz@nrubsig.org }
5810898Sroland.mainz@nrubsig.org
charcmp(int a,int b,int nocase)594887Schin static int charcmp(int a, int b, int nocase)
604887Schin {
614887Schin if(nocase)
624887Schin {
634887Schin if(isupper(a))
644887Schin a = tolower(a);
654887Schin if(isupper(b))
664887Schin b = tolower(b);
674887Schin }
684887Schin return(a==b);
694887Schin }
704887Schin
714887Schin /*
724887Schin * overwrites <str> to common prefix of <str> and <newstr>
734887Schin * if <str> is equal to <newstr> returns <str>+strlen(<str>)+1
744887Schin * otherwise returns <str>+strlen(<str>)
754887Schin */
overlaid(register char * str,register const char * newstr,int nocase)764887Schin static char *overlaid(register char *str,register const char *newstr,int nocase)
774887Schin {
784887Schin register int c,d;
794887Schin while((c= *(unsigned char *)str) && ((d= *(unsigned char*)newstr++),charcmp(c,d,nocase)))
804887Schin str++;
814887Schin if(*str)
824887Schin *str = 0;
834887Schin else if(*newstr==0)
844887Schin str++;
854887Schin return(str);
864887Schin }
874887Schin
884887Schin
894887Schin /*
904887Schin * returns pointer to beginning of expansion and sets type of expansion
914887Schin */
find_begin(char outbuff[],char * last,int endchar,int * type)924887Schin static char *find_begin(char outbuff[], char *last, int endchar, int *type)
934887Schin {
944887Schin register char *cp=outbuff, *bp, *xp;
958462SApril.Chin@Sun.COM register int c,inquote = 0, inassign=0;
968462SApril.Chin@Sun.COM int mode=*type;
974887Schin bp = outbuff;
984887Schin *type = 0;
994887Schin while(cp < last)
1004887Schin {
1014887Schin xp = cp;
1024887Schin switch(c= mbchar(cp))
1034887Schin {
1044887Schin case '\'': case '"':
1054887Schin if(!inquote)
1064887Schin {
1074887Schin inquote = c;
1084887Schin bp = xp;
1094887Schin break;
1104887Schin }
1114887Schin if(inquote==c)
1124887Schin inquote = 0;
1134887Schin break;
1144887Schin case '\\':
1154887Schin if(inquote != '\'')
1164887Schin mbchar(cp);
1174887Schin break;
1184887Schin case '$':
1194887Schin if(inquote == '\'')
1204887Schin break;
1214887Schin c = *(unsigned char*)cp;
1228462SApril.Chin@Sun.COM if(mode!='*' && (isaletter(c) || c=='{'))
1234887Schin {
1244887Schin int dot = '.';
1254887Schin if(c=='{')
1264887Schin {
1274887Schin xp = cp;
1284887Schin mbchar(cp);
1294887Schin c = *(unsigned char*)cp;
1304887Schin if(c!='.' && !isaletter(c))
1314887Schin break;
1324887Schin }
1334887Schin else
1344887Schin dot = 'a';
1354887Schin while(cp < last)
1364887Schin {
1374887Schin if((c= mbchar(cp)) , c!=dot && !isaname(c))
1384887Schin break;
1394887Schin }
1408462SApril.Chin@Sun.COM if(cp>=last && c!= '}')
1414887Schin {
1424887Schin *type='$';
1434887Schin return(++xp);
1444887Schin }
1454887Schin }
1464887Schin else if(c=='(')
1474887Schin {
1488462SApril.Chin@Sun.COM *type = mode;
1494887Schin xp = find_begin(cp,last,')',type);
1504887Schin if(*(cp=xp)!=')')
1514887Schin bp = xp;
1524887Schin else
1534887Schin cp++;
1544887Schin }
1554887Schin break;
1564887Schin case '=':
1574887Schin if(!inquote)
1588462SApril.Chin@Sun.COM {
1598462SApril.Chin@Sun.COM bp = cp;
1608462SApril.Chin@Sun.COM inassign = 1;
1618462SApril.Chin@Sun.COM }
1628462SApril.Chin@Sun.COM break;
1638462SApril.Chin@Sun.COM case ':':
1648462SApril.Chin@Sun.COM if(!inquote && inassign)
1654887Schin bp = cp;
1664887Schin break;
1674887Schin case '~':
1684887Schin if(*cp=='(')
1694887Schin break;
1704887Schin /* fall through */
1714887Schin default:
1724887Schin if(c && c==endchar)
1734887Schin return(xp);
1744887Schin if(!inquote && ismeta(c))
1758462SApril.Chin@Sun.COM {
1764887Schin bp = cp;
1778462SApril.Chin@Sun.COM inassign = 0;
1788462SApril.Chin@Sun.COM }
1794887Schin break;
1804887Schin }
1814887Schin }
1824887Schin if(inquote && *bp==inquote)
1834887Schin *type = *bp++;
1844887Schin return(bp);
1854887Schin }
1864887Schin
1874887Schin /*
1884887Schin * file name generation for edit modes
1894887Schin * non-zero exit for error, <0 ring bell
1904887Schin * don't search back past beginning of the buffer
1914887Schin * mode is '*' for inline expansion,
1924887Schin * mode is '\' for filename completion
1934887Schin * mode is '=' cause files to be listed in select format
1944887Schin */
1954887Schin
ed_expand(Edit_t * ep,char outbuff[],int * cur,int * eol,int mode,int count)1964887Schin int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count)
1974887Schin {
1984887Schin struct comnod *comptr;
1994887Schin struct argnod *ap;
2004887Schin register char *out;
2014887Schin char *av[2], *begin , *dir=0;
2024887Schin int addstar=0, rval=0, var=0, strip=1;
2034887Schin int nomarkdirs = !sh_isoption(SH_MARKDIRS);
2044887Schin sh_onstate(SH_FCOMPLETE);
2054887Schin if(ep->e_nlist)
2064887Schin {
2074887Schin if(mode=='=' && count>0)
2084887Schin {
2094887Schin if(count> ep->e_nlist)
2104887Schin return(-1);
2118462SApril.Chin@Sun.COM mode = '?';
2124887Schin av[0] = ep->e_clist[count-1];
2134887Schin av[1] = 0;
2144887Schin }
2154887Schin else
2164887Schin {
2174887Schin stakset(ep->e_stkptr,ep->e_stkoff);
2184887Schin ep->e_nlist = 0;
2194887Schin }
2204887Schin }
2214887Schin comptr = (struct comnod*)stakalloc(sizeof(struct comnod));
2224887Schin ap = (struct argnod*)stakseek(ARGVAL);
2234887Schin #if SHOPT_MULTIBYTE
2244887Schin {
2254887Schin register int c = *cur;
2264887Schin register genchar *cp;
2274887Schin /* adjust cur */
2284887Schin cp = (genchar *)outbuff + *cur;
2294887Schin c = *cp;
2304887Schin *cp = 0;
2314887Schin *cur = ed_external((genchar*)outbuff,(char*)stakptr(0));
2324887Schin *cp = c;
2334887Schin *eol = ed_external((genchar*)outbuff,outbuff);
2344887Schin }
2354887Schin #endif /* SHOPT_MULTIBYTE */
2364887Schin out = outbuff + *cur + (sh_isoption(SH_VI)!=0);
2374887Schin comptr->comtyp = COMSCAN;
2384887Schin comptr->comarg = ap;
2394887Schin ap->argflag = (ARG_MAC|ARG_EXP);
2404887Schin ap->argnxt.ap = 0;
2414887Schin ap->argchn.cp = 0;
2424887Schin {
2434887Schin register int c;
2444887Schin char *last = out;
2454887Schin c = *(unsigned char*)out;
2468462SApril.Chin@Sun.COM var = mode;
2474887Schin begin = out = find_begin(outbuff,last,0,&var);
2484887Schin /* addstar set to zero if * should not be added */
2494887Schin if(var=='$')
2504887Schin {
2514887Schin stakputs("${!");
2524887Schin stakwrite(out,last-out);
2534887Schin stakputs("@}");
2544887Schin out = last;
2554887Schin }
2564887Schin else
2574887Schin {
2584887Schin addstar = '*';
2594887Schin while(out < last)
2604887Schin {
2614887Schin c = *(unsigned char*)out;
2624887Schin if(isexp(c))
2634887Schin addstar = 0;
2644887Schin if (c == '/')
2654887Schin {
2664887Schin if(addstar == 0)
2674887Schin strip = 0;
2684887Schin dir = out+1;
2694887Schin }
2704887Schin stakputc(c);
2714887Schin out++;
2724887Schin }
2734887Schin }
2748462SApril.Chin@Sun.COM if(mode=='?')
2758462SApril.Chin@Sun.COM mode = '*';
2764887Schin if(var!='$' && mode=='\\' && out[-1]!='*')
2774887Schin addstar = '*';
2784887Schin if(*begin=='~' && !strchr(begin,'/'))
2794887Schin addstar = 0;
2804887Schin stakputc(addstar);
2814887Schin ap = (struct argnod*)stakfreeze(1);
2824887Schin }
2834887Schin if(mode!='*')
2844887Schin sh_onoption(SH_MARKDIRS);
2854887Schin {
2864887Schin register char **com;
2874887Schin char *cp=begin, *left=0, *saveout=".";
2884887Schin int nocase=0,narg,cmd_completion=0;
2894887Schin register int size='x';
2904887Schin while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t'))
2914887Schin cp--;
2924887Schin if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&sh.nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' )))
2934887Schin {
2944887Schin cmd_completion=1;
2954887Schin sh_onstate(SH_COMPLETE);
2964887Schin }
2974887Schin if(ep->e_nlist)
2984887Schin {
2994887Schin narg = 1;
3004887Schin com = av;
3014887Schin if(dir)
3024887Schin begin += (dir-begin);
3034887Schin }
3044887Schin else
3054887Schin {
3068462SApril.Chin@Sun.COM com = sh_argbuild(ep->sh,&narg,comptr,0);
3074887Schin /* special handling for leading quotes */
3084887Schin if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\''))
3094887Schin begin--;
3104887Schin }
3114887Schin sh_offstate(SH_COMPLETE);
3124887Schin /* allow a search to be aborted */
3134887Schin if(sh.trapnote&SH_SIGSET)
3144887Schin {
3154887Schin rval = -1;
3164887Schin goto done;
3174887Schin }
3184887Schin /* match? */
3194887Schin if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*')))
3204887Schin {
3214887Schin rval = -1;
3224887Schin goto done;
3234887Schin }
3244887Schin if(mode=='=')
3254887Schin {
3264887Schin if (strip && !cmd_completion)
3274887Schin {
3284887Schin register char **ptrcom;
3294887Schin for(ptrcom=com;*ptrcom;ptrcom++)
3304887Schin /* trim directory prefix */
3314887Schin *ptrcom = path_basename(*ptrcom);
3324887Schin }
3334887Schin sfputc(sfstderr,'\n');
3344887Schin sh_menu(sfstderr,narg,com);
3354887Schin sfsync(sfstderr);
3364887Schin ep->e_nlist = narg;
3374887Schin ep->e_clist = com;
3384887Schin goto done;
3394887Schin }
3404887Schin /* see if there is enough room */
3414887Schin size = *eol - (out-begin);
3424887Schin if(mode=='\\')
3434887Schin {
3444887Schin int c;
3454887Schin if(dir)
3464887Schin {
3474887Schin c = *dir;
3484887Schin *dir = 0;
3494887Schin saveout = begin;
3504887Schin }
3514887Schin if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0))
3524887Schin nocase = (strchr(saveout,'c')!=0);
3534887Schin if(dir)
3544887Schin *dir = c;
3554887Schin /* just expand until name is unique */
3564887Schin size += strlen(*com);
3574887Schin }
3584887Schin else
3594887Schin {
3604887Schin size += narg;
3614887Schin {
3624887Schin char **savcom = com;
3634887Schin while (*com)
36410898Sroland.mainz@nrubsig.org size += strlen(cp=fmtx(*com++));
3654887Schin com = savcom;
3664887Schin }
3674887Schin }
3684887Schin /* see if room for expansion */
3694887Schin if(outbuff+size >= &outbuff[MAXLINE])
3704887Schin {
3714887Schin com[0] = ap->argval;
3724887Schin com[1] = 0;
3734887Schin }
3744887Schin /* save remainder of the buffer */
3754887Schin if(*out)
3764887Schin left=stakcopy(out);
3774887Schin if(cmd_completion && mode=='\\')
3784887Schin out = strcopy(begin,path_basename(cp= *com++));
3794887Schin else if(mode=='*')
3804887Schin {
3814887Schin if(ep->e_nlist && dir && var)
3824887Schin {
3834887Schin if(*cp==var)
3844887Schin cp++;
3854887Schin else
3864887Schin *begin++ = var;
3874887Schin out = strcopy(begin,cp);
3884887Schin var = 0;
3894887Schin }
3904887Schin else
39110898Sroland.mainz@nrubsig.org out = strcopy(begin,fmtx(*com));
3924887Schin com++;
3934887Schin }
3944887Schin else
3954887Schin out = strcopy(begin,*com++);
3964887Schin if(mode=='\\')
3974887Schin {
3984887Schin saveout= ++out;
3994887Schin while (*com && *begin)
4004887Schin {
4014887Schin if(cmd_completion)
4024887Schin out = overlaid(begin,path_basename(*com++),nocase);
4034887Schin else
4044887Schin out = overlaid(begin,*com++,nocase);
4054887Schin }
4064887Schin mode = (out==saveout);
4074887Schin if(out[-1]==0)
4084887Schin out--;
4094887Schin if(mode && out[-1]!='/')
4104887Schin {
4114887Schin if(cmd_completion)
4124887Schin {
4134887Schin Namval_t *np;
4144887Schin /* add as tracked alias */
4154887Schin Pathcomp_t *pp;
4164887Schin if(*cp=='/' && (pp=path_dirfind(sh.pathlist,cp,'/')) && (np=nv_search(begin,sh.track_tree,NV_ADD)))
4174887Schin path_alias(np,pp);
4184887Schin out = strcopy(begin,cp);
4194887Schin }
4204887Schin /* add quotes if necessary */
42110898Sroland.mainz@nrubsig.org if((cp=fmtx(begin))!=begin)
4224887Schin out = strcopy(begin,cp);
4234887Schin if(var=='$' && begin[-1]=='{')
4244887Schin *out = '}';
4254887Schin else
4264887Schin *out = ' ';
4274887Schin *++out = 0;
4284887Schin }
42910898Sroland.mainz@nrubsig.org else if((cp=fmtx(begin))!=begin)
4304887Schin {
4314887Schin out = strcopy(begin,cp);
4324887Schin if(out[-1] =='"' || out[-1]=='\'')
43310898Sroland.mainz@nrubsig.org *--out = 0;
4344887Schin }
4354887Schin if(*begin==0)
4364887Schin ed_ringbell();
4374887Schin }
4384887Schin else
4394887Schin {
4404887Schin while (*com)
4414887Schin {
4424887Schin *out++ = ' ';
44310898Sroland.mainz@nrubsig.org out = strcopy(out,fmtx(*com++));
4444887Schin }
4454887Schin }
4464887Schin if(ep->e_nlist)
4474887Schin {
4484887Schin cp = com[-1];
4494887Schin if(cp[strlen(cp)-1]!='/')
4504887Schin {
4514887Schin if(var=='$' && begin[-1]=='{')
4524887Schin *out = '}';
4534887Schin else
4544887Schin *out = ' ';
4554887Schin out++;
4564887Schin }
4574887Schin else if(out[-1] =='"' || out[-1]=='\'')
4584887Schin out--;
4594887Schin *out = 0;
4604887Schin }
4614887Schin *cur = (out-outbuff);
4624887Schin /* restore rest of buffer */
4634887Schin if(left)
4644887Schin out = strcopy(out,left);
4654887Schin *eol = (out-outbuff);
4664887Schin }
4674887Schin done:
4684887Schin sh_offstate(SH_FCOMPLETE);
4694887Schin if(!ep->e_nlist)
4704887Schin stakset(ep->e_stkptr,ep->e_stkoff);
4714887Schin if(nomarkdirs)
4724887Schin sh_offoption(SH_MARKDIRS);
4734887Schin #if SHOPT_MULTIBYTE
4744887Schin {
4754887Schin register int c,n=0;
4764887Schin /* first re-adjust cur */
4774887Schin c = outbuff[*cur];
4784887Schin outbuff[*cur] = 0;
4794887Schin for(out=outbuff; *out;n++)
4804887Schin mbchar(out);
4814887Schin outbuff[*cur] = c;
4824887Schin *cur = n;
4834887Schin outbuff[*eol+1] = 0;
4844887Schin *eol = ed_internal(outbuff,(genchar*)outbuff);
4854887Schin }
4864887Schin #endif /* SHOPT_MULTIBYTE */
4874887Schin return(rval);
4884887Schin }
4894887Schin
4904887Schin /*
4914887Schin * look for edit macro named _i
4924887Schin * if found, puts the macro definition into lookahead buffer and returns 1
4934887Schin */
ed_macro(Edit_t * ep,register int i)4944887Schin int ed_macro(Edit_t *ep, register int i)
4954887Schin {
4964887Schin register char *out;
4974887Schin Namval_t *np;
4984887Schin genchar buff[LOOKAHEAD+1];
4994887Schin if(i != '@')
5004887Schin ep->e_macro[1] = i;
5014887Schin /* undocumented feature, macros of the form <ESC>[c evoke alias __c */
5024887Schin if(i=='_')
5034887Schin ep->e_macro[2] = ed_getchar(ep,1);
5044887Schin else
5054887Schin ep->e_macro[2] = 0;
5064887Schin if (isalnum(i)&&(np=nv_search(ep->e_macro,sh.alias_tree,HASH_SCOPE))&&(out=nv_getval(np)))
5074887Schin {
5084887Schin #if SHOPT_MULTIBYTE
5094887Schin /* copy to buff in internal representation */
5104887Schin int c = 0;
5114887Schin if( strlen(out) > LOOKAHEAD )
5124887Schin {
5134887Schin c = out[LOOKAHEAD];
5144887Schin out[LOOKAHEAD] = 0;
5154887Schin }
5164887Schin i = ed_internal(out,buff);
5174887Schin if(c)
5184887Schin out[LOOKAHEAD] = c;
5194887Schin #else
5204887Schin strncpy((char*)buff,out,LOOKAHEAD);
5214887Schin buff[LOOKAHEAD] = 0;
5224887Schin i = strlen((char*)buff);
5234887Schin #endif /* SHOPT_MULTIBYTE */
5244887Schin while(i-- > 0)
5254887Schin ed_ungetchar(ep,buff[i]);
5264887Schin return(1);
5274887Schin }
5284887Schin return(0);
5294887Schin }
5304887Schin
5314887Schin /*
5324887Schin * Enter the fc command on the current history line
5334887Schin */
ed_fulledit(Edit_t * ep)5344887Schin int ed_fulledit(Edit_t *ep)
5354887Schin {
5364887Schin register char *cp;
5374887Schin if(!sh.hist_ptr)
5384887Schin return(-1);
5394887Schin /* use EDITOR on current command */
5404887Schin if(ep->e_hline == ep->e_hismax)
5414887Schin {
5424887Schin if(ep->e_eol<0)
5434887Schin return(-1);
5444887Schin #if SHOPT_MULTIBYTE
5454887Schin ep->e_inbuf[ep->e_eol+1] = 0;
5464887Schin ed_external(ep->e_inbuf, (char *)ep->e_inbuf);
5474887Schin #endif /* SHOPT_MULTIBYTE */
5484887Schin sfwrite(sh.hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1);
5494887Schin sh_onstate(SH_HISTORY);
5504887Schin hist_flush(sh.hist_ptr);
5514887Schin }
5524887Schin cp = strcopy((char*)ep->e_inbuf,e_runvi);
5534887Schin cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0));
5544887Schin ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0);
5554887Schin return(0);
5564887Schin }
557