14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*10898Sroland.mainz@nrubsig.org * Copyright (c) 1982-2009 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 34*10898Sroland.mainz@nrubsig.org #if !SHOPT_MULTIBYTE 35*10898Sroland.mainz@nrubsig.org #define mbchar(p) (*(unsigned char*)p++) 36*10898Sroland.mainz@nrubsig.org #endif 37*10898Sroland.mainz@nrubsig.org 38*10898Sroland.mainz@nrubsig.org static char *fmtx(const char *string) 39*10898Sroland.mainz@nrubsig.org { 40*10898Sroland.mainz@nrubsig.org register const char *cp = string; 41*10898Sroland.mainz@nrubsig.org register int n,c; 42*10898Sroland.mainz@nrubsig.org unsigned char *state = (unsigned char*)sh_lexstates[2]; 43*10898Sroland.mainz@nrubsig.org int offset; 44*10898Sroland.mainz@nrubsig.org while((c=mbchar(cp)),(c>UCHAR_MAX)||(n=state[c])==0); 45*10898Sroland.mainz@nrubsig.org if(n==S_EOF) 46*10898Sroland.mainz@nrubsig.org return((char*)string); 47*10898Sroland.mainz@nrubsig.org offset = staktell(); 48*10898Sroland.mainz@nrubsig.org stakwrite(string,--cp-string); 49*10898Sroland.mainz@nrubsig.org while(c=mbchar(cp)) 50*10898Sroland.mainz@nrubsig.org { 51*10898Sroland.mainz@nrubsig.org if(state[c]) 52*10898Sroland.mainz@nrubsig.org stakputc('\\'); 53*10898Sroland.mainz@nrubsig.org stakputc(c); 54*10898Sroland.mainz@nrubsig.org } 55*10898Sroland.mainz@nrubsig.org stakputc(0); 56*10898Sroland.mainz@nrubsig.org return(stakptr(offset)); 57*10898Sroland.mainz@nrubsig.org } 58*10898Sroland.mainz@nrubsig.org 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 */ 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 */ 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 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) 364*10898Sroland.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 391*10898Sroland.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 */ 421*10898Sroland.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 } 429*10898Sroland.mainz@nrubsig.org else if((cp=fmtx(begin))!=begin) 4304887Schin { 4314887Schin out = strcopy(begin,cp); 4324887Schin if(out[-1] =='"' || out[-1]=='\'') 433*10898Sroland.mainz@nrubsig.org *--out = 0; 4344887Schin } 4354887Schin if(*begin==0) 4364887Schin ed_ringbell(); 4374887Schin } 4384887Schin else 4394887Schin { 4404887Schin while (*com) 4414887Schin { 4424887Schin *out++ = ' '; 443*10898Sroland.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 */ 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 */ 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