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