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 * File name expansion 23*4887Schin * 24*4887Schin * David Korn 25*4887Schin * AT&T Labs 26*4887Schin * 27*4887Schin */ 28*4887Schin 29*4887Schin #if KSHELL 30*4887Schin # include "defs.h" 31*4887Schin # include "variables.h" 32*4887Schin # include "test.h" 33*4887Schin #else 34*4887Schin # include <ast.h> 35*4887Schin # include <setjmp.h> 36*4887Schin #endif /* KSHELL */ 37*4887Schin #include <glob.h> 38*4887Schin #include <ls.h> 39*4887Schin #include <stak.h> 40*4887Schin #include <ast_dir.h> 41*4887Schin #include "io.h" 42*4887Schin #include "path.h" 43*4887Schin 44*4887Schin #if !SHOPT_BRACEPAT 45*4887Schin # define SHOPT_BRACEPAT 0 46*4887Schin #endif 47*4887Schin 48*4887Schin #if KSHELL 49*4887Schin # define argbegin argnxt.cp 50*4887Schin static const char *sufstr; 51*4887Schin static int suflen; 52*4887Schin static int scantree(Dt_t*,const char*, struct argnod**); 53*4887Schin #else 54*4887Schin # define sh_sigcheck() (0) 55*4887Schin # define sh_access access 56*4887Schin # define suflen 0 57*4887Schin #endif /* KSHELL */ 58*4887Schin 59*4887Schin 60*4887Schin /* 61*4887Schin * This routine builds a list of files that match a given pathname 62*4887Schin * Uses external routine strgrpmatch() to match each component 63*4887Schin * A leading . must match explicitly 64*4887Schin * 65*4887Schin */ 66*4887Schin 67*4887Schin #ifndef GLOB_AUGMENTED 68*4887Schin # define GLOB_AUGMENTED 0 69*4887Schin #endif 70*4887Schin 71*4887Schin #define GLOB_RESCAN 1 72*4887Schin #define globptr() ((struct glob*)membase) 73*4887Schin 74*4887Schin static struct glob *membase; 75*4887Schin 76*4887Schin #if GLOB_VERSION >= 20010916L 77*4887Schin static char *nextdir(glob_t *gp, char *dir) 78*4887Schin { 79*4887Schin Pathcomp_t *pp = (Pathcomp_t*)gp->gl_handle; 80*4887Schin if(!dir) 81*4887Schin pp = path_get(""); 82*4887Schin else 83*4887Schin pp = pp->next; 84*4887Schin gp->gl_handle = (void*)pp; 85*4887Schin if(pp) 86*4887Schin return(pp->name); 87*4887Schin return(0); 88*4887Schin } 89*4887Schin #endif 90*4887Schin 91*4887Schin int path_expand(const char *pattern, struct argnod **arghead) 92*4887Schin { 93*4887Schin glob_t gdata; 94*4887Schin register struct argnod *ap; 95*4887Schin register glob_t *gp= &gdata; 96*4887Schin register int flags,extra=0; 97*4887Schin #if SHOPT_BASH 98*4887Schin register int off; 99*4887Schin register char *sp, *cp, *cp2; 100*4887Schin #endif 101*4887Schin memset(gp,0,sizeof(gdata)); 102*4887Schin flags = GLOB_AUGMENTED|GLOB_NOCHECK|GLOB_NOSORT|GLOB_STACK|GLOB_LIST|GLOB_DISC; 103*4887Schin if(sh_isoption(SH_MARKDIRS)) 104*4887Schin flags |= GLOB_MARK; 105*4887Schin if(sh_isoption(SH_GLOBSTARS)) 106*4887Schin flags |= GLOB_STARSTAR; 107*4887Schin #if SHOPT_BASH 108*4887Schin #if 0 109*4887Schin if(sh_isoption(SH_BASH) && !sh_isoption(SH_EXTGLOB)) 110*4887Schin flags &= ~GLOB_AUGMENTED; 111*4887Schin #endif 112*4887Schin if(sh_isoption(SH_NULLGLOB)) 113*4887Schin flags &= ~GLOB_NOCHECK; 114*4887Schin if(sh_isoption(SH_NOCASEGLOB)) 115*4887Schin flags |= GLOB_ICASE; 116*4887Schin #endif 117*4887Schin if(sh_isstate(SH_COMPLETE)) 118*4887Schin { 119*4887Schin #if KSHELL 120*4887Schin extra += scantree(sh.alias_tree,pattern,arghead); 121*4887Schin extra += scantree(sh.fun_tree,pattern,arghead); 122*4887Schin # if GLOB_VERSION >= 20010916L 123*4887Schin gp->gl_nextdir = nextdir; 124*4887Schin # endif 125*4887Schin #endif /* KSHELL */ 126*4887Schin flags |= GLOB_COMPLETE; 127*4887Schin flags &= ~GLOB_NOCHECK; 128*4887Schin } 129*4887Schin #if SHOPT_BASH 130*4887Schin if(off = staktell()) 131*4887Schin sp = stakfreeze(0); 132*4887Schin if(sh_isoption(SH_BASH)) 133*4887Schin { 134*4887Schin /* 135*4887Schin * For bash, FIGNORE is a colon separated list of suffixes to 136*4887Schin * ignore when doing filename/command completion. 137*4887Schin * GLOBIGNORE is similar to ksh FIGNORE, but colon separated 138*4887Schin * instead of being an augmented shell pattern. 139*4887Schin * Generate shell patterns out of those here. 140*4887Schin */ 141*4887Schin if(sh_isstate(SH_FCOMPLETE)) 142*4887Schin cp=nv_getval(nv_scoped(FIGNORENOD)); 143*4887Schin else 144*4887Schin { 145*4887Schin static Namval_t *GLOBIGNORENOD; 146*4887Schin if(!GLOBIGNORENOD) 147*4887Schin GLOBIGNORENOD = nv_open("GLOBIGNORE",sh.var_tree,0); 148*4887Schin cp=nv_getval(nv_scoped(GLOBIGNORENOD)); 149*4887Schin } 150*4887Schin if(cp) 151*4887Schin { 152*4887Schin flags |= GLOB_AUGMENTED; 153*4887Schin stakputs("@("); 154*4887Schin if(!sh_isstate(SH_FCOMPLETE)) 155*4887Schin { 156*4887Schin stakputs(cp); 157*4887Schin for(cp=stakptr(off); *cp; cp++) 158*4887Schin if(*cp == ':') 159*4887Schin *cp='|'; 160*4887Schin } 161*4887Schin else 162*4887Schin { 163*4887Schin cp2 = strtok(cp, ":"); 164*4887Schin if(!cp2) 165*4887Schin cp2=cp; 166*4887Schin do 167*4887Schin { 168*4887Schin stakputc('*'); 169*4887Schin stakputs(cp2); 170*4887Schin if(cp2 = strtok(NULL, ":")) 171*4887Schin { 172*4887Schin *(cp2-1)=':'; 173*4887Schin stakputc('|'); 174*4887Schin } 175*4887Schin } while(cp2); 176*4887Schin } 177*4887Schin stakputc(')'); 178*4887Schin gp->gl_fignore = stakfreeze(1); 179*4887Schin } 180*4887Schin else if(!sh_isstate(SH_FCOMPLETE) && sh_isoption(SH_DOTGLOB)) 181*4887Schin gp->gl_fignore = ""; 182*4887Schin } 183*4887Schin else 184*4887Schin #endif 185*4887Schin gp->gl_fignore = nv_getval(nv_scoped(FIGNORENOD)); 186*4887Schin if(suflen) 187*4887Schin gp->gl_suffix = sufstr; 188*4887Schin gp->gl_intr = &sh.trapnote; 189*4887Schin suflen = 0; 190*4887Schin if(memcmp(pattern,"~(N",3)==0) 191*4887Schin flags &= ~GLOB_NOCHECK; 192*4887Schin glob(pattern, flags, 0, gp); 193*4887Schin #if SHOPT_BASH 194*4887Schin if(off) 195*4887Schin stakset(sp,off); 196*4887Schin else 197*4887Schin stakseek(0); 198*4887Schin #endif 199*4887Schin sh_sigcheck(); 200*4887Schin for(ap= (struct argnod*)gp->gl_list; ap; ap = ap->argnxt.ap) 201*4887Schin { 202*4887Schin ap->argchn.ap = ap->argnxt.ap; 203*4887Schin if(!ap->argnxt.ap) 204*4887Schin ap->argchn.ap = *arghead; 205*4887Schin } 206*4887Schin if(gp->gl_list) 207*4887Schin *arghead = (struct argnod*)gp->gl_list; 208*4887Schin return(gp->gl_pathc+extra); 209*4887Schin } 210*4887Schin 211*4887Schin #if KSHELL 212*4887Schin 213*4887Schin /* 214*4887Schin * scan tree and add each name that matches the given pattern 215*4887Schin */ 216*4887Schin static int scantree(Dt_t *tree, const char *pattern, struct argnod **arghead) 217*4887Schin { 218*4887Schin register Namval_t *np; 219*4887Schin register struct argnod *ap; 220*4887Schin register int nmatch=0; 221*4887Schin register char *cp; 222*4887Schin np = (Namval_t*)dtfirst(tree); 223*4887Schin for(;np && !nv_isnull(np);(np = (Namval_t*)dtnext(tree,np))) 224*4887Schin { 225*4887Schin if(strmatch(cp=nv_name(np),pattern)) 226*4887Schin { 227*4887Schin ap = (struct argnod*)stakseek(ARGVAL); 228*4887Schin stakputs(cp); 229*4887Schin ap = (struct argnod*)stakfreeze(1); 230*4887Schin ap->argbegin = NIL(char*); 231*4887Schin ap->argchn.ap = *arghead; 232*4887Schin ap->argflag = ARG_RAW|ARG_MAKE; 233*4887Schin *arghead = ap; 234*4887Schin nmatch++; 235*4887Schin } 236*4887Schin } 237*4887Schin return(nmatch); 238*4887Schin } 239*4887Schin 240*4887Schin /* 241*4887Schin * file name completion 242*4887Schin * generate the list of files found by adding an suffix to end of name 243*4887Schin * The number of matches is returned 244*4887Schin */ 245*4887Schin 246*4887Schin int path_complete(const char *name,register const char *suffix, struct argnod **arghead) 247*4887Schin { 248*4887Schin sufstr = suffix; 249*4887Schin suflen = strlen(suffix); 250*4887Schin return(path_expand(name,arghead)); 251*4887Schin } 252*4887Schin 253*4887Schin #endif 254*4887Schin 255*4887Schin #if SHOPT_BRACEPAT 256*4887Schin 257*4887Schin static int checkfmt(Sfio_t* sp, void* vp, Sffmt_t* fp) 258*4887Schin { 259*4887Schin return -1; 260*4887Schin } 261*4887Schin 262*4887Schin int path_generate(struct argnod *todo, struct argnod **arghead) 263*4887Schin /*@ 264*4887Schin assume todo!=0; 265*4887Schin return count satisfying count>=1; 266*4887Schin @*/ 267*4887Schin { 268*4887Schin register char *cp; 269*4887Schin register int brace; 270*4887Schin register struct argnod *ap; 271*4887Schin struct argnod *top = 0; 272*4887Schin struct argnod *apin; 273*4887Schin char *pat, *rescan; 274*4887Schin char *format; 275*4887Schin char comma, range=0; 276*4887Schin int first, last, incr, count = 0; 277*4887Schin char tmp[32], end[1]; 278*4887Schin todo->argchn.ap = 0; 279*4887Schin again: 280*4887Schin apin = ap = todo; 281*4887Schin todo = ap->argchn.ap; 282*4887Schin cp = ap->argval; 283*4887Schin range = comma = brace = 0; 284*4887Schin /* first search for {...,...} */ 285*4887Schin while(1) switch(*cp++) 286*4887Schin { 287*4887Schin case '{': 288*4887Schin if(brace++==0) 289*4887Schin pat = cp; 290*4887Schin break; 291*4887Schin case '}': 292*4887Schin if(--brace>0) 293*4887Schin break; 294*4887Schin if(brace==0 && comma && *cp!='(') 295*4887Schin goto endloop1; 296*4887Schin comma = brace = 0; 297*4887Schin break; 298*4887Schin case '.': 299*4887Schin if(brace==1 && *cp=='.') 300*4887Schin { 301*4887Schin char *endc; 302*4887Schin incr = 1; 303*4887Schin if(isdigit(*pat) || *pat=='+' || *pat=='-') 304*4887Schin { 305*4887Schin first = strtol(pat,&endc,0); 306*4887Schin if(endc==(cp-1)) 307*4887Schin { 308*4887Schin last = strtol(cp+1,&endc,0); 309*4887Schin if(*endc=='.' && endc[1]=='.') 310*4887Schin incr = strtol(endc+2,&endc,0); 311*4887Schin else if(last<first) 312*4887Schin incr = -1; 313*4887Schin if(incr) 314*4887Schin { 315*4887Schin if(*endc=='%') 316*4887Schin { 317*4887Schin Sffmt_t fmt; 318*4887Schin memset(&fmt, 0, sizeof(fmt)); 319*4887Schin fmt.version = SFIO_VERSION; 320*4887Schin fmt.form = endc; 321*4887Schin fmt.extf = checkfmt; 322*4887Schin sfprintf(sfstdout, "%!", &fmt); 323*4887Schin if(!(fmt.flags&(SFFMT_LLONG|SFFMT_LDOUBLE))) 324*4887Schin switch (fmt.fmt) 325*4887Schin { 326*4887Schin case 'c': 327*4887Schin case 'd': 328*4887Schin case 'i': 329*4887Schin case 'o': 330*4887Schin case 'u': 331*4887Schin case 'x': 332*4887Schin case 'X': 333*4887Schin format = endc; 334*4887Schin endc = fmt.form; 335*4887Schin break; 336*4887Schin } 337*4887Schin } 338*4887Schin else 339*4887Schin format = "%d"; 340*4887Schin if(*endc=='}') 341*4887Schin { 342*4887Schin cp = endc+1; 343*4887Schin range = 2; 344*4887Schin goto endloop1; 345*4887Schin } 346*4887Schin } 347*4887Schin } 348*4887Schin } 349*4887Schin else if((cp[2]=='}' || cp[2]=='.' && cp[3]=='.') && ((*pat>='a' && *pat<='z' && cp[1]>='a' && cp[1]<='z') || (*pat>='A' && *pat<='Z' && cp[1]>='A' && cp[1]<='Z'))) 350*4887Schin { 351*4887Schin first = *pat; 352*4887Schin last = cp[1]; 353*4887Schin cp += 2; 354*4887Schin if(*cp=='.') 355*4887Schin { 356*4887Schin incr = strtol(cp+2,&endc,0); 357*4887Schin cp = endc; 358*4887Schin } 359*4887Schin else if(first>last) 360*4887Schin incr = -1; 361*4887Schin if(incr && *cp=='}') 362*4887Schin { 363*4887Schin cp++; 364*4887Schin range = 1; 365*4887Schin goto endloop1; 366*4887Schin } 367*4887Schin } 368*4887Schin cp++; 369*4887Schin } 370*4887Schin break; 371*4887Schin case ',': 372*4887Schin if(brace==1) 373*4887Schin comma = 1; 374*4887Schin break; 375*4887Schin case '\\': 376*4887Schin cp++; 377*4887Schin break; 378*4887Schin case 0: 379*4887Schin /* insert on stack */ 380*4887Schin ap->argchn.ap = top; 381*4887Schin top = ap; 382*4887Schin if(todo) 383*4887Schin goto again; 384*4887Schin for(; ap; ap=apin) 385*4887Schin { 386*4887Schin apin = ap->argchn.ap; 387*4887Schin if(!sh_isoption(SH_NOGLOB)) 388*4887Schin brace=path_expand(ap->argval,arghead); 389*4887Schin else 390*4887Schin { 391*4887Schin ap->argchn.ap = *arghead; 392*4887Schin *arghead = ap; 393*4887Schin brace=1; 394*4887Schin } 395*4887Schin if(brace) 396*4887Schin { 397*4887Schin count += brace; 398*4887Schin (*arghead)->argflag |= ARG_MAKE; 399*4887Schin } 400*4887Schin } 401*4887Schin return(count); 402*4887Schin } 403*4887Schin endloop1: 404*4887Schin rescan = cp; 405*4887Schin cp = pat-1; 406*4887Schin *cp = 0; 407*4887Schin while(1) 408*4887Schin { 409*4887Schin brace = 0; 410*4887Schin if(range) 411*4887Schin { 412*4887Schin if(range==1) 413*4887Schin { 414*4887Schin pat[0] = first; 415*4887Schin cp = &pat[1]; 416*4887Schin } 417*4887Schin else 418*4887Schin { 419*4887Schin *(rescan - 1) = 0; 420*4887Schin sfsprintf(pat=tmp,sizeof(tmp),format,first); 421*4887Schin *(rescan - 1) = '}'; 422*4887Schin *(cp = end) = 0; 423*4887Schin } 424*4887Schin if(incr*(first+incr) > last*incr) 425*4887Schin *cp = '}'; 426*4887Schin else 427*4887Schin first += incr; 428*4887Schin } 429*4887Schin /* generate each pattern and put on the todo list */ 430*4887Schin else while(1) switch(*++cp) 431*4887Schin { 432*4887Schin case '\\': 433*4887Schin cp++; 434*4887Schin break; 435*4887Schin case '{': 436*4887Schin brace++; 437*4887Schin break; 438*4887Schin case ',': 439*4887Schin if(brace==0) 440*4887Schin goto endloop2; 441*4887Schin break; 442*4887Schin case '}': 443*4887Schin if(--brace<0) 444*4887Schin goto endloop2; 445*4887Schin } 446*4887Schin endloop2: 447*4887Schin brace = *cp; 448*4887Schin *cp = 0; 449*4887Schin sh_sigcheck(); 450*4887Schin ap = (struct argnod*)stakseek(ARGVAL); 451*4887Schin ap->argflag = ARG_RAW; 452*4887Schin ap->argchn.ap = todo; 453*4887Schin stakputs(apin->argval); 454*4887Schin stakputs(pat); 455*4887Schin stakputs(rescan); 456*4887Schin todo = ap = (struct argnod*)stakfreeze(1); 457*4887Schin if(brace == '}') 458*4887Schin break; 459*4887Schin if(!range) 460*4887Schin pat = cp+1; 461*4887Schin } 462*4887Schin goto again; 463*4887Schin } 464*4887Schin 465*4887Schin #endif /* SHOPT_BRACEPAT */ 466