1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1982-2010 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 75static struct glob *membase; 76 77#if GLOB_VERSION >= 20010916L 78static 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 92int 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 */ 219static 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 249int 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 260static int checkfmt(Sfio_t* sp, void* vp, Sffmt_t* fp) 261{ 262 return -1; 263} 264 265int 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; 282again: 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 } 406endloop1: 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