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