1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1982-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * David Korn <dgk@research.att.com> * 18*4887Schin * * 19*4887Schin ***********************************************************************/ 20*4887Schin #pragma prototyped 21*4887Schin /* 22*4887Schin * completion.c - command and file completion for shell editors 23*4887Schin * 24*4887Schin */ 25*4887Schin 26*4887Schin #include "defs.h" 27*4887Schin #include <ctype.h> 28*4887Schin #include <ast_wchar.h> 29*4887Schin #include "lexstates.h" 30*4887Schin #include "path.h" 31*4887Schin #include "io.h" 32*4887Schin #include "edit.h" 33*4887Schin #include "history.h" 34*4887Schin 35*4887Schin static int charcmp(int a, int b, int nocase) 36*4887Schin { 37*4887Schin if(nocase) 38*4887Schin { 39*4887Schin if(isupper(a)) 40*4887Schin a = tolower(a); 41*4887Schin if(isupper(b)) 42*4887Schin b = tolower(b); 43*4887Schin } 44*4887Schin return(a==b); 45*4887Schin } 46*4887Schin 47*4887Schin /* 48*4887Schin * overwrites <str> to common prefix of <str> and <newstr> 49*4887Schin * if <str> is equal to <newstr> returns <str>+strlen(<str>)+1 50*4887Schin * otherwise returns <str>+strlen(<str>) 51*4887Schin */ 52*4887Schin static char *overlaid(register char *str,register const char *newstr,int nocase) 53*4887Schin { 54*4887Schin register int c,d; 55*4887Schin while((c= *(unsigned char *)str) && ((d= *(unsigned char*)newstr++),charcmp(c,d,nocase))) 56*4887Schin str++; 57*4887Schin if(*str) 58*4887Schin *str = 0; 59*4887Schin else if(*newstr==0) 60*4887Schin str++; 61*4887Schin return(str); 62*4887Schin } 63*4887Schin 64*4887Schin 65*4887Schin /* 66*4887Schin * returns pointer to beginning of expansion and sets type of expansion 67*4887Schin */ 68*4887Schin static char *find_begin(char outbuff[], char *last, int endchar, int *type) 69*4887Schin { 70*4887Schin register char *cp=outbuff, *bp, *xp; 71*4887Schin register int c,inquote = 0; 72*4887Schin bp = outbuff; 73*4887Schin *type = 0; 74*4887Schin while(cp < last) 75*4887Schin { 76*4887Schin xp = cp; 77*4887Schin switch(c= mbchar(cp)) 78*4887Schin { 79*4887Schin case '\'': case '"': 80*4887Schin if(!inquote) 81*4887Schin { 82*4887Schin inquote = c; 83*4887Schin bp = xp; 84*4887Schin break; 85*4887Schin } 86*4887Schin if(inquote==c) 87*4887Schin inquote = 0; 88*4887Schin break; 89*4887Schin case '\\': 90*4887Schin if(inquote != '\'') 91*4887Schin mbchar(cp); 92*4887Schin break; 93*4887Schin case '$': 94*4887Schin if(inquote == '\'') 95*4887Schin break; 96*4887Schin c = *(unsigned char*)cp; 97*4887Schin if(isaletter(c) || c=='{') 98*4887Schin { 99*4887Schin int dot = '.'; 100*4887Schin if(c=='{') 101*4887Schin { 102*4887Schin xp = cp; 103*4887Schin mbchar(cp); 104*4887Schin c = *(unsigned char*)cp; 105*4887Schin if(c!='.' && !isaletter(c)) 106*4887Schin break; 107*4887Schin } 108*4887Schin else 109*4887Schin dot = 'a'; 110*4887Schin while(cp < last) 111*4887Schin { 112*4887Schin if((c= mbchar(cp)) , c!=dot && !isaname(c)) 113*4887Schin break; 114*4887Schin } 115*4887Schin if(cp>=last) 116*4887Schin { 117*4887Schin *type='$'; 118*4887Schin return(++xp); 119*4887Schin } 120*4887Schin } 121*4887Schin else if(c=='(') 122*4887Schin { 123*4887Schin xp = find_begin(cp,last,')',type); 124*4887Schin if(*(cp=xp)!=')') 125*4887Schin bp = xp; 126*4887Schin else 127*4887Schin cp++; 128*4887Schin } 129*4887Schin break; 130*4887Schin case '=': 131*4887Schin if(!inquote) 132*4887Schin bp = cp; 133*4887Schin break; 134*4887Schin case '~': 135*4887Schin if(*cp=='(') 136*4887Schin break; 137*4887Schin /* fall through */ 138*4887Schin default: 139*4887Schin if(c && c==endchar) 140*4887Schin return(xp); 141*4887Schin if(!inquote && ismeta(c)) 142*4887Schin bp = cp; 143*4887Schin break; 144*4887Schin } 145*4887Schin } 146*4887Schin if(inquote && *bp==inquote) 147*4887Schin *type = *bp++; 148*4887Schin return(bp); 149*4887Schin } 150*4887Schin 151*4887Schin /* 152*4887Schin * file name generation for edit modes 153*4887Schin * non-zero exit for error, <0 ring bell 154*4887Schin * don't search back past beginning of the buffer 155*4887Schin * mode is '*' for inline expansion, 156*4887Schin * mode is '\' for filename completion 157*4887Schin * mode is '=' cause files to be listed in select format 158*4887Schin */ 159*4887Schin 160*4887Schin int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) 161*4887Schin { 162*4887Schin struct comnod *comptr; 163*4887Schin struct argnod *ap; 164*4887Schin register char *out; 165*4887Schin char *av[2], *begin , *dir=0; 166*4887Schin int addstar=0, rval=0, var=0, strip=1; 167*4887Schin int nomarkdirs = !sh_isoption(SH_MARKDIRS); 168*4887Schin sh_onstate(SH_FCOMPLETE); 169*4887Schin if(ep->e_nlist) 170*4887Schin { 171*4887Schin if(mode=='=' && count>0) 172*4887Schin { 173*4887Schin if(count> ep->e_nlist) 174*4887Schin return(-1); 175*4887Schin mode = '*'; 176*4887Schin av[0] = ep->e_clist[count-1]; 177*4887Schin av[1] = 0; 178*4887Schin } 179*4887Schin else 180*4887Schin { 181*4887Schin stakset(ep->e_stkptr,ep->e_stkoff); 182*4887Schin ep->e_nlist = 0; 183*4887Schin } 184*4887Schin } 185*4887Schin comptr = (struct comnod*)stakalloc(sizeof(struct comnod)); 186*4887Schin ap = (struct argnod*)stakseek(ARGVAL); 187*4887Schin #if SHOPT_MULTIBYTE 188*4887Schin { 189*4887Schin register int c = *cur; 190*4887Schin register genchar *cp; 191*4887Schin /* adjust cur */ 192*4887Schin cp = (genchar *)outbuff + *cur; 193*4887Schin c = *cp; 194*4887Schin *cp = 0; 195*4887Schin *cur = ed_external((genchar*)outbuff,(char*)stakptr(0)); 196*4887Schin *cp = c; 197*4887Schin *eol = ed_external((genchar*)outbuff,outbuff); 198*4887Schin } 199*4887Schin #endif /* SHOPT_MULTIBYTE */ 200*4887Schin out = outbuff + *cur + (sh_isoption(SH_VI)!=0); 201*4887Schin comptr->comtyp = COMSCAN; 202*4887Schin comptr->comarg = ap; 203*4887Schin ap->argflag = (ARG_MAC|ARG_EXP); 204*4887Schin ap->argnxt.ap = 0; 205*4887Schin ap->argchn.cp = 0; 206*4887Schin { 207*4887Schin register int c; 208*4887Schin char *last = out; 209*4887Schin c = *(unsigned char*)out; 210*4887Schin begin = out = find_begin(outbuff,last,0,&var); 211*4887Schin /* addstar set to zero if * should not be added */ 212*4887Schin if(var=='$') 213*4887Schin { 214*4887Schin stakputs("${!"); 215*4887Schin stakwrite(out,last-out); 216*4887Schin stakputs("@}"); 217*4887Schin out = last; 218*4887Schin } 219*4887Schin else 220*4887Schin { 221*4887Schin addstar = '*'; 222*4887Schin while(out < last) 223*4887Schin { 224*4887Schin c = *(unsigned char*)out; 225*4887Schin if(isexp(c)) 226*4887Schin addstar = 0; 227*4887Schin if (c == '/') 228*4887Schin { 229*4887Schin if(addstar == 0) 230*4887Schin strip = 0; 231*4887Schin dir = out+1; 232*4887Schin } 233*4887Schin stakputc(c); 234*4887Schin out++; 235*4887Schin } 236*4887Schin } 237*4887Schin if(var!='$' && mode=='\\' && out[-1]!='*') 238*4887Schin addstar = '*'; 239*4887Schin if(*begin=='~' && !strchr(begin,'/')) 240*4887Schin addstar = 0; 241*4887Schin stakputc(addstar); 242*4887Schin ap = (struct argnod*)stakfreeze(1); 243*4887Schin } 244*4887Schin if(mode!='*') 245*4887Schin sh_onoption(SH_MARKDIRS); 246*4887Schin { 247*4887Schin register char **com; 248*4887Schin char *cp=begin, *left=0, *saveout="."; 249*4887Schin int nocase=0,narg,cmd_completion=0; 250*4887Schin register int size='x'; 251*4887Schin while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t')) 252*4887Schin cp--; 253*4887Schin if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&sh.nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' ))) 254*4887Schin { 255*4887Schin cmd_completion=1; 256*4887Schin sh_onstate(SH_COMPLETE); 257*4887Schin } 258*4887Schin if(ep->e_nlist) 259*4887Schin { 260*4887Schin narg = 1; 261*4887Schin com = av; 262*4887Schin if(dir) 263*4887Schin begin += (dir-begin); 264*4887Schin } 265*4887Schin else 266*4887Schin { 267*4887Schin com = sh_argbuild(&narg,comptr,0); 268*4887Schin /* special handling for leading quotes */ 269*4887Schin if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\'')) 270*4887Schin begin--; 271*4887Schin } 272*4887Schin sh_offstate(SH_COMPLETE); 273*4887Schin /* allow a search to be aborted */ 274*4887Schin if(sh.trapnote&SH_SIGSET) 275*4887Schin { 276*4887Schin rval = -1; 277*4887Schin goto done; 278*4887Schin } 279*4887Schin /* match? */ 280*4887Schin if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*'))) 281*4887Schin { 282*4887Schin rval = -1; 283*4887Schin goto done; 284*4887Schin } 285*4887Schin if(mode=='=') 286*4887Schin { 287*4887Schin if (strip && !cmd_completion) 288*4887Schin { 289*4887Schin register char **ptrcom; 290*4887Schin for(ptrcom=com;*ptrcom;ptrcom++) 291*4887Schin /* trim directory prefix */ 292*4887Schin *ptrcom = path_basename(*ptrcom); 293*4887Schin } 294*4887Schin sfputc(sfstderr,'\n'); 295*4887Schin sh_menu(sfstderr,narg,com); 296*4887Schin sfsync(sfstderr); 297*4887Schin ep->e_nlist = narg; 298*4887Schin ep->e_clist = com; 299*4887Schin goto done; 300*4887Schin } 301*4887Schin /* see if there is enough room */ 302*4887Schin size = *eol - (out-begin); 303*4887Schin if(mode=='\\') 304*4887Schin { 305*4887Schin int c; 306*4887Schin if(dir) 307*4887Schin { 308*4887Schin c = *dir; 309*4887Schin *dir = 0; 310*4887Schin saveout = begin; 311*4887Schin } 312*4887Schin if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0)) 313*4887Schin nocase = (strchr(saveout,'c')!=0); 314*4887Schin if(dir) 315*4887Schin *dir = c; 316*4887Schin /* just expand until name is unique */ 317*4887Schin size += strlen(*com); 318*4887Schin } 319*4887Schin else 320*4887Schin { 321*4887Schin size += narg; 322*4887Schin { 323*4887Schin char **savcom = com; 324*4887Schin while (*com) 325*4887Schin size += strlen(cp=sh_fmtq(*com++)); 326*4887Schin com = savcom; 327*4887Schin } 328*4887Schin } 329*4887Schin /* see if room for expansion */ 330*4887Schin if(outbuff+size >= &outbuff[MAXLINE]) 331*4887Schin { 332*4887Schin com[0] = ap->argval; 333*4887Schin com[1] = 0; 334*4887Schin } 335*4887Schin /* save remainder of the buffer */ 336*4887Schin if(*out) 337*4887Schin left=stakcopy(out); 338*4887Schin if(cmd_completion && mode=='\\') 339*4887Schin out = strcopy(begin,path_basename(cp= *com++)); 340*4887Schin else if(mode=='*') 341*4887Schin { 342*4887Schin if(ep->e_nlist && dir && var) 343*4887Schin { 344*4887Schin if(*cp==var) 345*4887Schin cp++; 346*4887Schin else 347*4887Schin *begin++ = var; 348*4887Schin out = strcopy(begin,cp); 349*4887Schin var = 0; 350*4887Schin } 351*4887Schin else 352*4887Schin out = strcopy(begin,sh_fmtq(*com)); 353*4887Schin com++; 354*4887Schin } 355*4887Schin else 356*4887Schin out = strcopy(begin,*com++); 357*4887Schin if(mode=='\\') 358*4887Schin { 359*4887Schin saveout= ++out; 360*4887Schin while (*com && *begin) 361*4887Schin { 362*4887Schin if(cmd_completion) 363*4887Schin out = overlaid(begin,path_basename(*com++),nocase); 364*4887Schin else 365*4887Schin out = overlaid(begin,*com++,nocase); 366*4887Schin } 367*4887Schin mode = (out==saveout); 368*4887Schin if(out[-1]==0) 369*4887Schin out--; 370*4887Schin if(mode && out[-1]!='/') 371*4887Schin { 372*4887Schin if(cmd_completion) 373*4887Schin { 374*4887Schin Namval_t *np; 375*4887Schin /* add as tracked alias */ 376*4887Schin #ifdef PATH_BFPATH 377*4887Schin Pathcomp_t *pp; 378*4887Schin if(*cp=='/' && (pp=path_dirfind(sh.pathlist,cp,'/')) && (np=nv_search(begin,sh.track_tree,NV_ADD))) 379*4887Schin path_alias(np,pp); 380*4887Schin #else 381*4887Schin if(*cp=='/' && (np=nv_search(begin,sh.track_tree,NV_ADD))) 382*4887Schin path_alias(np,cp); 383*4887Schin #endif 384*4887Schin out = strcopy(begin,cp); 385*4887Schin } 386*4887Schin /* add quotes if necessary */ 387*4887Schin if((cp=sh_fmtq(begin))!=begin) 388*4887Schin out = strcopy(begin,cp); 389*4887Schin if(var=='$' && begin[-1]=='{') 390*4887Schin *out = '}'; 391*4887Schin else 392*4887Schin *out = ' '; 393*4887Schin *++out = 0; 394*4887Schin } 395*4887Schin else if(out[-1]=='/' && (cp=sh_fmtq(begin))!=begin) 396*4887Schin { 397*4887Schin out = strcopy(begin,cp); 398*4887Schin if(out[-1] =='"' || out[-1]=='\'') 399*4887Schin *--out = 0;; 400*4887Schin } 401*4887Schin if(*begin==0) 402*4887Schin ed_ringbell(); 403*4887Schin } 404*4887Schin else 405*4887Schin { 406*4887Schin while (*com) 407*4887Schin { 408*4887Schin *out++ = ' '; 409*4887Schin out = strcopy(out,sh_fmtq(*com++)); 410*4887Schin } 411*4887Schin } 412*4887Schin if(ep->e_nlist) 413*4887Schin { 414*4887Schin cp = com[-1]; 415*4887Schin if(cp[strlen(cp)-1]!='/') 416*4887Schin { 417*4887Schin if(var=='$' && begin[-1]=='{') 418*4887Schin *out = '}'; 419*4887Schin else 420*4887Schin *out = ' '; 421*4887Schin out++; 422*4887Schin } 423*4887Schin else if(out[-1] =='"' || out[-1]=='\'') 424*4887Schin out--; 425*4887Schin *out = 0; 426*4887Schin } 427*4887Schin *cur = (out-outbuff); 428*4887Schin /* restore rest of buffer */ 429*4887Schin if(left) 430*4887Schin out = strcopy(out,left); 431*4887Schin *eol = (out-outbuff); 432*4887Schin } 433*4887Schin done: 434*4887Schin sh_offstate(SH_FCOMPLETE); 435*4887Schin if(!ep->e_nlist) 436*4887Schin stakset(ep->e_stkptr,ep->e_stkoff); 437*4887Schin if(nomarkdirs) 438*4887Schin sh_offoption(SH_MARKDIRS); 439*4887Schin #if SHOPT_MULTIBYTE 440*4887Schin { 441*4887Schin register int c,n=0; 442*4887Schin /* first re-adjust cur */ 443*4887Schin c = outbuff[*cur]; 444*4887Schin outbuff[*cur] = 0; 445*4887Schin for(out=outbuff; *out;n++) 446*4887Schin mbchar(out); 447*4887Schin outbuff[*cur] = c; 448*4887Schin *cur = n; 449*4887Schin outbuff[*eol+1] = 0; 450*4887Schin *eol = ed_internal(outbuff,(genchar*)outbuff); 451*4887Schin } 452*4887Schin #endif /* SHOPT_MULTIBYTE */ 453*4887Schin return(rval); 454*4887Schin } 455*4887Schin 456*4887Schin /* 457*4887Schin * look for edit macro named _i 458*4887Schin * if found, puts the macro definition into lookahead buffer and returns 1 459*4887Schin */ 460*4887Schin int ed_macro(Edit_t *ep, register int i) 461*4887Schin { 462*4887Schin register char *out; 463*4887Schin Namval_t *np; 464*4887Schin genchar buff[LOOKAHEAD+1]; 465*4887Schin if(i != '@') 466*4887Schin ep->e_macro[1] = i; 467*4887Schin /* undocumented feature, macros of the form <ESC>[c evoke alias __c */ 468*4887Schin if(i=='_') 469*4887Schin ep->e_macro[2] = ed_getchar(ep,1); 470*4887Schin else 471*4887Schin ep->e_macro[2] = 0; 472*4887Schin if (isalnum(i)&&(np=nv_search(ep->e_macro,sh.alias_tree,HASH_SCOPE))&&(out=nv_getval(np))) 473*4887Schin { 474*4887Schin #if SHOPT_MULTIBYTE 475*4887Schin /* copy to buff in internal representation */ 476*4887Schin int c = 0; 477*4887Schin if( strlen(out) > LOOKAHEAD ) 478*4887Schin { 479*4887Schin c = out[LOOKAHEAD]; 480*4887Schin out[LOOKAHEAD] = 0; 481*4887Schin } 482*4887Schin i = ed_internal(out,buff); 483*4887Schin if(c) 484*4887Schin out[LOOKAHEAD] = c; 485*4887Schin #else 486*4887Schin strncpy((char*)buff,out,LOOKAHEAD); 487*4887Schin buff[LOOKAHEAD] = 0; 488*4887Schin i = strlen((char*)buff); 489*4887Schin #endif /* SHOPT_MULTIBYTE */ 490*4887Schin while(i-- > 0) 491*4887Schin ed_ungetchar(ep,buff[i]); 492*4887Schin return(1); 493*4887Schin } 494*4887Schin return(0); 495*4887Schin } 496*4887Schin 497*4887Schin /* 498*4887Schin * Enter the fc command on the current history line 499*4887Schin */ 500*4887Schin int ed_fulledit(Edit_t *ep) 501*4887Schin { 502*4887Schin register char *cp; 503*4887Schin if(!sh.hist_ptr) 504*4887Schin return(-1); 505*4887Schin /* use EDITOR on current command */ 506*4887Schin if(ep->e_hline == ep->e_hismax) 507*4887Schin { 508*4887Schin if(ep->e_eol<0) 509*4887Schin return(-1); 510*4887Schin #if SHOPT_MULTIBYTE 511*4887Schin ep->e_inbuf[ep->e_eol+1] = 0; 512*4887Schin ed_external(ep->e_inbuf, (char *)ep->e_inbuf); 513*4887Schin #endif /* SHOPT_MULTIBYTE */ 514*4887Schin sfwrite(sh.hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1); 515*4887Schin sh_onstate(SH_HISTORY); 516*4887Schin hist_flush(sh.hist_ptr); 517*4887Schin } 518*4887Schin cp = strcopy((char*)ep->e_inbuf,e_runvi); 519*4887Schin cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0)); 520*4887Schin ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0); 521*4887Schin return(0); 522*4887Schin } 523