14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1982-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.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 <ctype.h> 284887Schin #include <ast_wchar.h> 294887Schin #include "lexstates.h" 304887Schin #include "path.h" 314887Schin #include "io.h" 324887Schin #include "edit.h" 334887Schin #include "history.h" 344887Schin 354887Schin static int charcmp(int a, int b, int nocase) 364887Schin { 374887Schin if(nocase) 384887Schin { 394887Schin if(isupper(a)) 404887Schin a = tolower(a); 414887Schin if(isupper(b)) 424887Schin b = tolower(b); 434887Schin } 444887Schin return(a==b); 454887Schin } 464887Schin 474887Schin /* 484887Schin * overwrites <str> to common prefix of <str> and <newstr> 494887Schin * if <str> is equal to <newstr> returns <str>+strlen(<str>)+1 504887Schin * otherwise returns <str>+strlen(<str>) 514887Schin */ 524887Schin static char *overlaid(register char *str,register const char *newstr,int nocase) 534887Schin { 544887Schin register int c,d; 554887Schin while((c= *(unsigned char *)str) && ((d= *(unsigned char*)newstr++),charcmp(c,d,nocase))) 564887Schin str++; 574887Schin if(*str) 584887Schin *str = 0; 594887Schin else if(*newstr==0) 604887Schin str++; 614887Schin return(str); 624887Schin } 634887Schin 644887Schin 654887Schin /* 664887Schin * returns pointer to beginning of expansion and sets type of expansion 674887Schin */ 684887Schin static char *find_begin(char outbuff[], char *last, int endchar, int *type) 694887Schin { 704887Schin register char *cp=outbuff, *bp, *xp; 71*8462SApril.Chin@Sun.COM register int c,inquote = 0, inassign=0; 72*8462SApril.Chin@Sun.COM int mode=*type; 734887Schin bp = outbuff; 744887Schin *type = 0; 754887Schin while(cp < last) 764887Schin { 774887Schin xp = cp; 784887Schin switch(c= mbchar(cp)) 794887Schin { 804887Schin case '\'': case '"': 814887Schin if(!inquote) 824887Schin { 834887Schin inquote = c; 844887Schin bp = xp; 854887Schin break; 864887Schin } 874887Schin if(inquote==c) 884887Schin inquote = 0; 894887Schin break; 904887Schin case '\\': 914887Schin if(inquote != '\'') 924887Schin mbchar(cp); 934887Schin break; 944887Schin case '$': 954887Schin if(inquote == '\'') 964887Schin break; 974887Schin c = *(unsigned char*)cp; 98*8462SApril.Chin@Sun.COM if(mode!='*' && (isaletter(c) || c=='{')) 994887Schin { 1004887Schin int dot = '.'; 1014887Schin if(c=='{') 1024887Schin { 1034887Schin xp = cp; 1044887Schin mbchar(cp); 1054887Schin c = *(unsigned char*)cp; 1064887Schin if(c!='.' && !isaletter(c)) 1074887Schin break; 1084887Schin } 1094887Schin else 1104887Schin dot = 'a'; 1114887Schin while(cp < last) 1124887Schin { 1134887Schin if((c= mbchar(cp)) , c!=dot && !isaname(c)) 1144887Schin break; 1154887Schin } 116*8462SApril.Chin@Sun.COM if(cp>=last && c!= '}') 1174887Schin { 1184887Schin *type='$'; 1194887Schin return(++xp); 1204887Schin } 1214887Schin } 1224887Schin else if(c=='(') 1234887Schin { 124*8462SApril.Chin@Sun.COM *type = mode; 1254887Schin xp = find_begin(cp,last,')',type); 1264887Schin if(*(cp=xp)!=')') 1274887Schin bp = xp; 1284887Schin else 1294887Schin cp++; 1304887Schin } 1314887Schin break; 1324887Schin case '=': 1334887Schin if(!inquote) 134*8462SApril.Chin@Sun.COM { 135*8462SApril.Chin@Sun.COM bp = cp; 136*8462SApril.Chin@Sun.COM inassign = 1; 137*8462SApril.Chin@Sun.COM } 138*8462SApril.Chin@Sun.COM break; 139*8462SApril.Chin@Sun.COM case ':': 140*8462SApril.Chin@Sun.COM if(!inquote && inassign) 1414887Schin bp = cp; 1424887Schin break; 1434887Schin case '~': 1444887Schin if(*cp=='(') 1454887Schin break; 1464887Schin /* fall through */ 1474887Schin default: 1484887Schin if(c && c==endchar) 1494887Schin return(xp); 1504887Schin if(!inquote && ismeta(c)) 151*8462SApril.Chin@Sun.COM { 1524887Schin bp = cp; 153*8462SApril.Chin@Sun.COM inassign = 0; 154*8462SApril.Chin@Sun.COM } 1554887Schin break; 1564887Schin } 1574887Schin } 1584887Schin if(inquote && *bp==inquote) 1594887Schin *type = *bp++; 1604887Schin return(bp); 1614887Schin } 1624887Schin 1634887Schin /* 1644887Schin * file name generation for edit modes 1654887Schin * non-zero exit for error, <0 ring bell 1664887Schin * don't search back past beginning of the buffer 1674887Schin * mode is '*' for inline expansion, 1684887Schin * mode is '\' for filename completion 1694887Schin * mode is '=' cause files to be listed in select format 1704887Schin */ 1714887Schin 1724887Schin int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) 1734887Schin { 1744887Schin struct comnod *comptr; 1754887Schin struct argnod *ap; 1764887Schin register char *out; 1774887Schin char *av[2], *begin , *dir=0; 1784887Schin int addstar=0, rval=0, var=0, strip=1; 1794887Schin int nomarkdirs = !sh_isoption(SH_MARKDIRS); 1804887Schin sh_onstate(SH_FCOMPLETE); 1814887Schin if(ep->e_nlist) 1824887Schin { 1834887Schin if(mode=='=' && count>0) 1844887Schin { 1854887Schin if(count> ep->e_nlist) 1864887Schin return(-1); 187*8462SApril.Chin@Sun.COM mode = '?'; 1884887Schin av[0] = ep->e_clist[count-1]; 1894887Schin av[1] = 0; 1904887Schin } 1914887Schin else 1924887Schin { 1934887Schin stakset(ep->e_stkptr,ep->e_stkoff); 1944887Schin ep->e_nlist = 0; 1954887Schin } 1964887Schin } 1974887Schin comptr = (struct comnod*)stakalloc(sizeof(struct comnod)); 1984887Schin ap = (struct argnod*)stakseek(ARGVAL); 1994887Schin #if SHOPT_MULTIBYTE 2004887Schin { 2014887Schin register int c = *cur; 2024887Schin register genchar *cp; 2034887Schin /* adjust cur */ 2044887Schin cp = (genchar *)outbuff + *cur; 2054887Schin c = *cp; 2064887Schin *cp = 0; 2074887Schin *cur = ed_external((genchar*)outbuff,(char*)stakptr(0)); 2084887Schin *cp = c; 2094887Schin *eol = ed_external((genchar*)outbuff,outbuff); 2104887Schin } 2114887Schin #endif /* SHOPT_MULTIBYTE */ 2124887Schin out = outbuff + *cur + (sh_isoption(SH_VI)!=0); 2134887Schin comptr->comtyp = COMSCAN; 2144887Schin comptr->comarg = ap; 2154887Schin ap->argflag = (ARG_MAC|ARG_EXP); 2164887Schin ap->argnxt.ap = 0; 2174887Schin ap->argchn.cp = 0; 2184887Schin { 2194887Schin register int c; 2204887Schin char *last = out; 2214887Schin c = *(unsigned char*)out; 222*8462SApril.Chin@Sun.COM var = mode; 2234887Schin begin = out = find_begin(outbuff,last,0,&var); 2244887Schin /* addstar set to zero if * should not be added */ 2254887Schin if(var=='$') 2264887Schin { 2274887Schin stakputs("${!"); 2284887Schin stakwrite(out,last-out); 2294887Schin stakputs("@}"); 2304887Schin out = last; 2314887Schin } 2324887Schin else 2334887Schin { 2344887Schin addstar = '*'; 2354887Schin while(out < last) 2364887Schin { 2374887Schin c = *(unsigned char*)out; 2384887Schin if(isexp(c)) 2394887Schin addstar = 0; 2404887Schin if (c == '/') 2414887Schin { 2424887Schin if(addstar == 0) 2434887Schin strip = 0; 2444887Schin dir = out+1; 2454887Schin } 2464887Schin stakputc(c); 2474887Schin out++; 2484887Schin } 2494887Schin } 250*8462SApril.Chin@Sun.COM if(mode=='?') 251*8462SApril.Chin@Sun.COM mode = '*'; 2524887Schin if(var!='$' && mode=='\\' && out[-1]!='*') 2534887Schin addstar = '*'; 2544887Schin if(*begin=='~' && !strchr(begin,'/')) 2554887Schin addstar = 0; 2564887Schin stakputc(addstar); 2574887Schin ap = (struct argnod*)stakfreeze(1); 2584887Schin } 2594887Schin if(mode!='*') 2604887Schin sh_onoption(SH_MARKDIRS); 2614887Schin { 2624887Schin register char **com; 2634887Schin char *cp=begin, *left=0, *saveout="."; 2644887Schin int nocase=0,narg,cmd_completion=0; 2654887Schin register int size='x'; 2664887Schin while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t')) 2674887Schin cp--; 2684887Schin if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&sh.nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' ))) 2694887Schin { 2704887Schin cmd_completion=1; 2714887Schin sh_onstate(SH_COMPLETE); 2724887Schin } 2734887Schin if(ep->e_nlist) 2744887Schin { 2754887Schin narg = 1; 2764887Schin com = av; 2774887Schin if(dir) 2784887Schin begin += (dir-begin); 2794887Schin } 2804887Schin else 2814887Schin { 282*8462SApril.Chin@Sun.COM com = sh_argbuild(ep->sh,&narg,comptr,0); 2834887Schin /* special handling for leading quotes */ 2844887Schin if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\'')) 2854887Schin begin--; 2864887Schin } 2874887Schin sh_offstate(SH_COMPLETE); 2884887Schin /* allow a search to be aborted */ 2894887Schin if(sh.trapnote&SH_SIGSET) 2904887Schin { 2914887Schin rval = -1; 2924887Schin goto done; 2934887Schin } 2944887Schin /* match? */ 2954887Schin if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*'))) 2964887Schin { 2974887Schin rval = -1; 2984887Schin goto done; 2994887Schin } 3004887Schin if(mode=='=') 3014887Schin { 3024887Schin if (strip && !cmd_completion) 3034887Schin { 3044887Schin register char **ptrcom; 3054887Schin for(ptrcom=com;*ptrcom;ptrcom++) 3064887Schin /* trim directory prefix */ 3074887Schin *ptrcom = path_basename(*ptrcom); 3084887Schin } 3094887Schin sfputc(sfstderr,'\n'); 3104887Schin sh_menu(sfstderr,narg,com); 3114887Schin sfsync(sfstderr); 3124887Schin ep->e_nlist = narg; 3134887Schin ep->e_clist = com; 3144887Schin goto done; 3154887Schin } 3164887Schin /* see if there is enough room */ 3174887Schin size = *eol - (out-begin); 3184887Schin if(mode=='\\') 3194887Schin { 3204887Schin int c; 3214887Schin if(dir) 3224887Schin { 3234887Schin c = *dir; 3244887Schin *dir = 0; 3254887Schin saveout = begin; 3264887Schin } 3274887Schin if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0)) 3284887Schin nocase = (strchr(saveout,'c')!=0); 3294887Schin if(dir) 3304887Schin *dir = c; 3314887Schin /* just expand until name is unique */ 3324887Schin size += strlen(*com); 3334887Schin } 3344887Schin else 3354887Schin { 3364887Schin size += narg; 3374887Schin { 3384887Schin char **savcom = com; 3394887Schin while (*com) 3404887Schin size += strlen(cp=sh_fmtq(*com++)); 3414887Schin com = savcom; 3424887Schin } 3434887Schin } 3444887Schin /* see if room for expansion */ 3454887Schin if(outbuff+size >= &outbuff[MAXLINE]) 3464887Schin { 3474887Schin com[0] = ap->argval; 3484887Schin com[1] = 0; 3494887Schin } 3504887Schin /* save remainder of the buffer */ 3514887Schin if(*out) 3524887Schin left=stakcopy(out); 3534887Schin if(cmd_completion && mode=='\\') 3544887Schin out = strcopy(begin,path_basename(cp= *com++)); 3554887Schin else if(mode=='*') 3564887Schin { 3574887Schin if(ep->e_nlist && dir && var) 3584887Schin { 3594887Schin if(*cp==var) 3604887Schin cp++; 3614887Schin else 3624887Schin *begin++ = var; 3634887Schin out = strcopy(begin,cp); 3644887Schin var = 0; 3654887Schin } 3664887Schin else 3674887Schin out = strcopy(begin,sh_fmtq(*com)); 3684887Schin com++; 3694887Schin } 3704887Schin else 3714887Schin out = strcopy(begin,*com++); 3724887Schin if(mode=='\\') 3734887Schin { 3744887Schin saveout= ++out; 3754887Schin while (*com && *begin) 3764887Schin { 3774887Schin if(cmd_completion) 3784887Schin out = overlaid(begin,path_basename(*com++),nocase); 3794887Schin else 3804887Schin out = overlaid(begin,*com++,nocase); 3814887Schin } 3824887Schin mode = (out==saveout); 3834887Schin if(out[-1]==0) 3844887Schin out--; 3854887Schin if(mode && out[-1]!='/') 3864887Schin { 3874887Schin if(cmd_completion) 3884887Schin { 3894887Schin Namval_t *np; 3904887Schin /* add as tracked alias */ 3914887Schin Pathcomp_t *pp; 3924887Schin if(*cp=='/' && (pp=path_dirfind(sh.pathlist,cp,'/')) && (np=nv_search(begin,sh.track_tree,NV_ADD))) 3934887Schin path_alias(np,pp); 3944887Schin out = strcopy(begin,cp); 3954887Schin } 3964887Schin /* add quotes if necessary */ 3974887Schin if((cp=sh_fmtq(begin))!=begin) 3984887Schin out = strcopy(begin,cp); 3994887Schin if(var=='$' && begin[-1]=='{') 4004887Schin *out = '}'; 4014887Schin else 4024887Schin *out = ' '; 4034887Schin *++out = 0; 4044887Schin } 4054887Schin else if(out[-1]=='/' && (cp=sh_fmtq(begin))!=begin) 4064887Schin { 4074887Schin out = strcopy(begin,cp); 4084887Schin if(out[-1] =='"' || out[-1]=='\'') 4094887Schin *--out = 0;; 4104887Schin } 4114887Schin if(*begin==0) 4124887Schin ed_ringbell(); 4134887Schin } 4144887Schin else 4154887Schin { 4164887Schin while (*com) 4174887Schin { 4184887Schin *out++ = ' '; 4194887Schin out = strcopy(out,sh_fmtq(*com++)); 4204887Schin } 4214887Schin } 4224887Schin if(ep->e_nlist) 4234887Schin { 4244887Schin cp = com[-1]; 4254887Schin if(cp[strlen(cp)-1]!='/') 4264887Schin { 4274887Schin if(var=='$' && begin[-1]=='{') 4284887Schin *out = '}'; 4294887Schin else 4304887Schin *out = ' '; 4314887Schin out++; 4324887Schin } 4334887Schin else if(out[-1] =='"' || out[-1]=='\'') 4344887Schin out--; 4354887Schin *out = 0; 4364887Schin } 4374887Schin *cur = (out-outbuff); 4384887Schin /* restore rest of buffer */ 4394887Schin if(left) 4404887Schin out = strcopy(out,left); 4414887Schin *eol = (out-outbuff); 4424887Schin } 4434887Schin done: 4444887Schin sh_offstate(SH_FCOMPLETE); 4454887Schin if(!ep->e_nlist) 4464887Schin stakset(ep->e_stkptr,ep->e_stkoff); 4474887Schin if(nomarkdirs) 4484887Schin sh_offoption(SH_MARKDIRS); 4494887Schin #if SHOPT_MULTIBYTE 4504887Schin { 4514887Schin register int c,n=0; 4524887Schin /* first re-adjust cur */ 4534887Schin c = outbuff[*cur]; 4544887Schin outbuff[*cur] = 0; 4554887Schin for(out=outbuff; *out;n++) 4564887Schin mbchar(out); 4574887Schin outbuff[*cur] = c; 4584887Schin *cur = n; 4594887Schin outbuff[*eol+1] = 0; 4604887Schin *eol = ed_internal(outbuff,(genchar*)outbuff); 4614887Schin } 4624887Schin #endif /* SHOPT_MULTIBYTE */ 4634887Schin return(rval); 4644887Schin } 4654887Schin 4664887Schin /* 4674887Schin * look for edit macro named _i 4684887Schin * if found, puts the macro definition into lookahead buffer and returns 1 4694887Schin */ 4704887Schin int ed_macro(Edit_t *ep, register int i) 4714887Schin { 4724887Schin register char *out; 4734887Schin Namval_t *np; 4744887Schin genchar buff[LOOKAHEAD+1]; 4754887Schin if(i != '@') 4764887Schin ep->e_macro[1] = i; 4774887Schin /* undocumented feature, macros of the form <ESC>[c evoke alias __c */ 4784887Schin if(i=='_') 4794887Schin ep->e_macro[2] = ed_getchar(ep,1); 4804887Schin else 4814887Schin ep->e_macro[2] = 0; 4824887Schin if (isalnum(i)&&(np=nv_search(ep->e_macro,sh.alias_tree,HASH_SCOPE))&&(out=nv_getval(np))) 4834887Schin { 4844887Schin #if SHOPT_MULTIBYTE 4854887Schin /* copy to buff in internal representation */ 4864887Schin int c = 0; 4874887Schin if( strlen(out) > LOOKAHEAD ) 4884887Schin { 4894887Schin c = out[LOOKAHEAD]; 4904887Schin out[LOOKAHEAD] = 0; 4914887Schin } 4924887Schin i = ed_internal(out,buff); 4934887Schin if(c) 4944887Schin out[LOOKAHEAD] = c; 4954887Schin #else 4964887Schin strncpy((char*)buff,out,LOOKAHEAD); 4974887Schin buff[LOOKAHEAD] = 0; 4984887Schin i = strlen((char*)buff); 4994887Schin #endif /* SHOPT_MULTIBYTE */ 5004887Schin while(i-- > 0) 5014887Schin ed_ungetchar(ep,buff[i]); 5024887Schin return(1); 5034887Schin } 5044887Schin return(0); 5054887Schin } 5064887Schin 5074887Schin /* 5084887Schin * Enter the fc command on the current history line 5094887Schin */ 5104887Schin int ed_fulledit(Edit_t *ep) 5114887Schin { 5124887Schin register char *cp; 5134887Schin if(!sh.hist_ptr) 5144887Schin return(-1); 5154887Schin /* use EDITOR on current command */ 5164887Schin if(ep->e_hline == ep->e_hismax) 5174887Schin { 5184887Schin if(ep->e_eol<0) 5194887Schin return(-1); 5204887Schin #if SHOPT_MULTIBYTE 5214887Schin ep->e_inbuf[ep->e_eol+1] = 0; 5224887Schin ed_external(ep->e_inbuf, (char *)ep->e_inbuf); 5234887Schin #endif /* SHOPT_MULTIBYTE */ 5244887Schin sfwrite(sh.hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1); 5254887Schin sh_onstate(SH_HISTORY); 5264887Schin hist_flush(sh.hist_ptr); 5274887Schin } 5284887Schin cp = strcopy((char*)ep->e_inbuf,e_runvi); 5294887Schin cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0)); 5304887Schin ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0); 5314887Schin return(0); 5324887Schin } 533