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