14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1982-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * David Korn <dgk@research.att.com> * 184887Schin * * 194887Schin ***********************************************************************/ 204887Schin #pragma prototyped 214887Schin /* 224887Schin * File name expansion 234887Schin * 244887Schin * David Korn 254887Schin * AT&T Labs 264887Schin * 274887Schin */ 284887Schin 294887Schin #if KSHELL 304887Schin # include "defs.h" 314887Schin # include "variables.h" 324887Schin # include "test.h" 334887Schin #else 344887Schin # include <ast.h> 354887Schin # include <setjmp.h> 364887Schin #endif /* KSHELL */ 374887Schin #include <glob.h> 384887Schin #include <ls.h> 394887Schin #include <stak.h> 404887Schin #include <ast_dir.h> 414887Schin #include "io.h" 424887Schin #include "path.h" 434887Schin 444887Schin #if !SHOPT_BRACEPAT 454887Schin # define SHOPT_BRACEPAT 0 464887Schin #endif 474887Schin 484887Schin #if KSHELL 494887Schin # define argbegin argnxt.cp 504887Schin static const char *sufstr; 514887Schin static int suflen; 524887Schin static int scantree(Dt_t*,const char*, struct argnod**); 534887Schin #else 544887Schin # define sh_sigcheck() (0) 554887Schin # define sh_access access 564887Schin # define suflen 0 574887Schin #endif /* KSHELL */ 584887Schin 594887Schin 604887Schin /* 614887Schin * This routine builds a list of files that match a given pathname 624887Schin * Uses external routine strgrpmatch() to match each component 634887Schin * A leading . must match explicitly 644887Schin * 654887Schin */ 664887Schin 674887Schin #ifndef GLOB_AUGMENTED 684887Schin # define GLOB_AUGMENTED 0 694887Schin #endif 704887Schin 714887Schin #define GLOB_RESCAN 1 724887Schin #define globptr() ((struct glob*)membase) 734887Schin 744887Schin static struct glob *membase; 754887Schin 764887Schin #if GLOB_VERSION >= 20010916L 774887Schin static char *nextdir(glob_t *gp, char *dir) 784887Schin { 794887Schin Pathcomp_t *pp = (Pathcomp_t*)gp->gl_handle; 804887Schin if(!dir) 814887Schin pp = path_get(""); 824887Schin else 834887Schin pp = pp->next; 844887Schin gp->gl_handle = (void*)pp; 854887Schin if(pp) 864887Schin return(pp->name); 874887Schin return(0); 884887Schin } 894887Schin #endif 904887Schin 914887Schin int path_expand(const char *pattern, struct argnod **arghead) 924887Schin { 93*8462SApril.Chin@Sun.COM Shell_t *shp = &sh; 944887Schin glob_t gdata; 954887Schin register struct argnod *ap; 964887Schin register glob_t *gp= &gdata; 974887Schin register int flags,extra=0; 984887Schin #if SHOPT_BASH 994887Schin register int off; 1004887Schin register char *sp, *cp, *cp2; 1014887Schin #endif 102*8462SApril.Chin@Sun.COM sh_stats(STAT_GLOBS); 1034887Schin memset(gp,0,sizeof(gdata)); 1044887Schin flags = GLOB_AUGMENTED|GLOB_NOCHECK|GLOB_NOSORT|GLOB_STACK|GLOB_LIST|GLOB_DISC; 1054887Schin if(sh_isoption(SH_MARKDIRS)) 1064887Schin flags |= GLOB_MARK; 1074887Schin if(sh_isoption(SH_GLOBSTARS)) 1084887Schin flags |= GLOB_STARSTAR; 1094887Schin #if SHOPT_BASH 1104887Schin #if 0 1114887Schin if(sh_isoption(SH_BASH) && !sh_isoption(SH_EXTGLOB)) 1124887Schin flags &= ~GLOB_AUGMENTED; 1134887Schin #endif 1144887Schin if(sh_isoption(SH_NULLGLOB)) 1154887Schin flags &= ~GLOB_NOCHECK; 1164887Schin if(sh_isoption(SH_NOCASEGLOB)) 1174887Schin flags |= GLOB_ICASE; 1184887Schin #endif 1194887Schin if(sh_isstate(SH_COMPLETE)) 1204887Schin { 1214887Schin #if KSHELL 122*8462SApril.Chin@Sun.COM extra += scantree(shp->alias_tree,pattern,arghead); 123*8462SApril.Chin@Sun.COM extra += scantree(shp->fun_tree,pattern,arghead); 1244887Schin # if GLOB_VERSION >= 20010916L 1254887Schin gp->gl_nextdir = nextdir; 1264887Schin # endif 1274887Schin #endif /* KSHELL */ 1284887Schin flags |= GLOB_COMPLETE; 1294887Schin flags &= ~GLOB_NOCHECK; 1304887Schin } 1314887Schin #if SHOPT_BASH 1324887Schin if(off = staktell()) 1334887Schin sp = stakfreeze(0); 1344887Schin if(sh_isoption(SH_BASH)) 1354887Schin { 1364887Schin /* 1374887Schin * For bash, FIGNORE is a colon separated list of suffixes to 1384887Schin * ignore when doing filename/command completion. 1394887Schin * GLOBIGNORE is similar to ksh FIGNORE, but colon separated 1404887Schin * instead of being an augmented shell pattern. 1414887Schin * Generate shell patterns out of those here. 1424887Schin */ 1434887Schin if(sh_isstate(SH_FCOMPLETE)) 144*8462SApril.Chin@Sun.COM cp=nv_getval(sh_scoped(shp,FIGNORENOD)); 1454887Schin else 1464887Schin { 1474887Schin static Namval_t *GLOBIGNORENOD; 1484887Schin if(!GLOBIGNORENOD) 149*8462SApril.Chin@Sun.COM GLOBIGNORENOD = nv_open("GLOBIGNORE",shp->var_tree,0); 150*8462SApril.Chin@Sun.COM cp=nv_getval(sh_scoped(shp,GLOBIGNORENOD)); 1514887Schin } 1524887Schin if(cp) 1534887Schin { 1544887Schin flags |= GLOB_AUGMENTED; 1554887Schin stakputs("@("); 1564887Schin if(!sh_isstate(SH_FCOMPLETE)) 1574887Schin { 1584887Schin stakputs(cp); 1594887Schin for(cp=stakptr(off); *cp; cp++) 1604887Schin if(*cp == ':') 1614887Schin *cp='|'; 1624887Schin } 1634887Schin else 1644887Schin { 1654887Schin cp2 = strtok(cp, ":"); 1664887Schin if(!cp2) 1674887Schin cp2=cp; 1684887Schin do 1694887Schin { 1704887Schin stakputc('*'); 1714887Schin stakputs(cp2); 1724887Schin if(cp2 = strtok(NULL, ":")) 1734887Schin { 1744887Schin *(cp2-1)=':'; 1754887Schin stakputc('|'); 1764887Schin } 1774887Schin } while(cp2); 1784887Schin } 1794887Schin stakputc(')'); 1804887Schin gp->gl_fignore = stakfreeze(1); 1814887Schin } 1824887Schin else if(!sh_isstate(SH_FCOMPLETE) && sh_isoption(SH_DOTGLOB)) 1834887Schin gp->gl_fignore = ""; 1844887Schin } 1854887Schin else 1864887Schin #endif 187*8462SApril.Chin@Sun.COM gp->gl_fignore = nv_getval(sh_scoped(shp,FIGNORENOD)); 1884887Schin if(suflen) 1894887Schin gp->gl_suffix = sufstr; 190*8462SApril.Chin@Sun.COM gp->gl_intr = &shp->trapnote; 1914887Schin suflen = 0; 1924887Schin if(memcmp(pattern,"~(N",3)==0) 1934887Schin flags &= ~GLOB_NOCHECK; 1944887Schin glob(pattern, flags, 0, gp); 1954887Schin #if SHOPT_BASH 1964887Schin if(off) 1974887Schin stakset(sp,off); 1984887Schin else 1994887Schin stakseek(0); 2004887Schin #endif 2014887Schin sh_sigcheck(); 2024887Schin for(ap= (struct argnod*)gp->gl_list; ap; ap = ap->argnxt.ap) 2034887Schin { 2044887Schin ap->argchn.ap = ap->argnxt.ap; 2054887Schin if(!ap->argnxt.ap) 2064887Schin ap->argchn.ap = *arghead; 2074887Schin } 2084887Schin if(gp->gl_list) 2094887Schin *arghead = (struct argnod*)gp->gl_list; 2104887Schin return(gp->gl_pathc+extra); 2114887Schin } 2124887Schin 2134887Schin #if KSHELL 2144887Schin 2154887Schin /* 2164887Schin * scan tree and add each name that matches the given pattern 2174887Schin */ 2184887Schin static int scantree(Dt_t *tree, const char *pattern, struct argnod **arghead) 2194887Schin { 2204887Schin register Namval_t *np; 2214887Schin register struct argnod *ap; 2224887Schin register int nmatch=0; 2234887Schin register char *cp; 2244887Schin np = (Namval_t*)dtfirst(tree); 2254887Schin for(;np && !nv_isnull(np);(np = (Namval_t*)dtnext(tree,np))) 2264887Schin { 2274887Schin if(strmatch(cp=nv_name(np),pattern)) 2284887Schin { 2294887Schin ap = (struct argnod*)stakseek(ARGVAL); 2304887Schin stakputs(cp); 2314887Schin ap = (struct argnod*)stakfreeze(1); 2324887Schin ap->argbegin = NIL(char*); 2334887Schin ap->argchn.ap = *arghead; 2344887Schin ap->argflag = ARG_RAW|ARG_MAKE; 2354887Schin *arghead = ap; 2364887Schin nmatch++; 2374887Schin } 2384887Schin } 2394887Schin return(nmatch); 2404887Schin } 2414887Schin 2424887Schin /* 2434887Schin * file name completion 2444887Schin * generate the list of files found by adding an suffix to end of name 2454887Schin * The number of matches is returned 2464887Schin */ 2474887Schin 2484887Schin int path_complete(const char *name,register const char *suffix, struct argnod **arghead) 2494887Schin { 2504887Schin sufstr = suffix; 2514887Schin suflen = strlen(suffix); 2524887Schin return(path_expand(name,arghead)); 2534887Schin } 2544887Schin 2554887Schin #endif 2564887Schin 2574887Schin #if SHOPT_BRACEPAT 2584887Schin 2594887Schin static int checkfmt(Sfio_t* sp, void* vp, Sffmt_t* fp) 2604887Schin { 2614887Schin return -1; 2624887Schin } 2634887Schin 2644887Schin int path_generate(struct argnod *todo, struct argnod **arghead) 2654887Schin /*@ 2664887Schin assume todo!=0; 2674887Schin return count satisfying count>=1; 2684887Schin @*/ 2694887Schin { 2704887Schin register char *cp; 2714887Schin register int brace; 2724887Schin register struct argnod *ap; 2734887Schin struct argnod *top = 0; 2744887Schin struct argnod *apin; 2754887Schin char *pat, *rescan; 2764887Schin char *format; 2774887Schin char comma, range=0; 2784887Schin int first, last, incr, count = 0; 2794887Schin char tmp[32], end[1]; 2804887Schin todo->argchn.ap = 0; 2814887Schin again: 2824887Schin apin = ap = todo; 2834887Schin todo = ap->argchn.ap; 2844887Schin cp = ap->argval; 2854887Schin range = comma = brace = 0; 2864887Schin /* first search for {...,...} */ 2874887Schin while(1) switch(*cp++) 2884887Schin { 2894887Schin case '{': 2904887Schin if(brace++==0) 2914887Schin pat = cp; 2924887Schin break; 2934887Schin case '}': 2944887Schin if(--brace>0) 2954887Schin break; 2964887Schin if(brace==0 && comma && *cp!='(') 2974887Schin goto endloop1; 2984887Schin comma = brace = 0; 2994887Schin break; 3004887Schin case '.': 3014887Schin if(brace==1 && *cp=='.') 3024887Schin { 3034887Schin char *endc; 3044887Schin incr = 1; 3054887Schin if(isdigit(*pat) || *pat=='+' || *pat=='-') 3064887Schin { 3074887Schin first = strtol(pat,&endc,0); 3084887Schin if(endc==(cp-1)) 3094887Schin { 3104887Schin last = strtol(cp+1,&endc,0); 3114887Schin if(*endc=='.' && endc[1]=='.') 3124887Schin incr = strtol(endc+2,&endc,0); 3134887Schin else if(last<first) 3144887Schin incr = -1; 3154887Schin if(incr) 3164887Schin { 3174887Schin if(*endc=='%') 3184887Schin { 3194887Schin Sffmt_t fmt; 3204887Schin memset(&fmt, 0, sizeof(fmt)); 3214887Schin fmt.version = SFIO_VERSION; 3224887Schin fmt.form = endc; 3234887Schin fmt.extf = checkfmt; 3244887Schin sfprintf(sfstdout, "%!", &fmt); 3254887Schin if(!(fmt.flags&(SFFMT_LLONG|SFFMT_LDOUBLE))) 3264887Schin switch (fmt.fmt) 3274887Schin { 3284887Schin case 'c': 3294887Schin case 'd': 3304887Schin case 'i': 3314887Schin case 'o': 3324887Schin case 'u': 3334887Schin case 'x': 3344887Schin case 'X': 3354887Schin format = endc; 3364887Schin endc = fmt.form; 3374887Schin break; 3384887Schin } 3394887Schin } 3404887Schin else 3414887Schin format = "%d"; 3424887Schin if(*endc=='}') 3434887Schin { 3444887Schin cp = endc+1; 3454887Schin range = 2; 3464887Schin goto endloop1; 3474887Schin } 3484887Schin } 3494887Schin } 3504887Schin } 3514887Schin 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'))) 3524887Schin { 3534887Schin first = *pat; 3544887Schin last = cp[1]; 3554887Schin cp += 2; 3564887Schin if(*cp=='.') 3574887Schin { 3584887Schin incr = strtol(cp+2,&endc,0); 3594887Schin cp = endc; 3604887Schin } 3614887Schin else if(first>last) 3624887Schin incr = -1; 3634887Schin if(incr && *cp=='}') 3644887Schin { 3654887Schin cp++; 3664887Schin range = 1; 3674887Schin goto endloop1; 3684887Schin } 3694887Schin } 3704887Schin cp++; 3714887Schin } 3724887Schin break; 3734887Schin case ',': 3744887Schin if(brace==1) 3754887Schin comma = 1; 3764887Schin break; 3774887Schin case '\\': 3784887Schin cp++; 3794887Schin break; 3804887Schin case 0: 3814887Schin /* insert on stack */ 3824887Schin ap->argchn.ap = top; 3834887Schin top = ap; 3844887Schin if(todo) 3854887Schin goto again; 3864887Schin for(; ap; ap=apin) 3874887Schin { 3884887Schin apin = ap->argchn.ap; 3894887Schin if(!sh_isoption(SH_NOGLOB)) 3904887Schin brace=path_expand(ap->argval,arghead); 3914887Schin else 3924887Schin { 3934887Schin ap->argchn.ap = *arghead; 3944887Schin *arghead = ap; 3954887Schin brace=1; 3964887Schin } 3974887Schin if(brace) 3984887Schin { 3994887Schin count += brace; 4004887Schin (*arghead)->argflag |= ARG_MAKE; 4014887Schin } 4024887Schin } 4034887Schin return(count); 4044887Schin } 4054887Schin endloop1: 4064887Schin rescan = cp; 4074887Schin cp = pat-1; 4084887Schin *cp = 0; 4094887Schin while(1) 4104887Schin { 4114887Schin brace = 0; 4124887Schin if(range) 4134887Schin { 4144887Schin if(range==1) 4154887Schin { 4164887Schin pat[0] = first; 4174887Schin cp = &pat[1]; 4184887Schin } 4194887Schin else 4204887Schin { 4214887Schin *(rescan - 1) = 0; 4224887Schin sfsprintf(pat=tmp,sizeof(tmp),format,first); 4234887Schin *(rescan - 1) = '}'; 4244887Schin *(cp = end) = 0; 4254887Schin } 4264887Schin if(incr*(first+incr) > last*incr) 4274887Schin *cp = '}'; 4284887Schin else 4294887Schin first += incr; 4304887Schin } 4314887Schin /* generate each pattern and put on the todo list */ 4324887Schin else while(1) switch(*++cp) 4334887Schin { 4344887Schin case '\\': 4354887Schin cp++; 4364887Schin break; 4374887Schin case '{': 4384887Schin brace++; 4394887Schin break; 4404887Schin case ',': 4414887Schin if(brace==0) 4424887Schin goto endloop2; 4434887Schin break; 4444887Schin case '}': 4454887Schin if(--brace<0) 4464887Schin goto endloop2; 4474887Schin } 4484887Schin endloop2: 4494887Schin brace = *cp; 4504887Schin *cp = 0; 4514887Schin sh_sigcheck(); 4524887Schin ap = (struct argnod*)stakseek(ARGVAL); 4534887Schin ap->argflag = ARG_RAW; 4544887Schin ap->argchn.ap = todo; 4554887Schin stakputs(apin->argval); 4564887Schin stakputs(pat); 4574887Schin stakputs(rescan); 4584887Schin todo = ap = (struct argnod*)stakfreeze(1); 4594887Schin if(brace == '}') 4604887Schin break; 4614887Schin if(!range) 4624887Schin pat = cp+1; 4634887Schin } 4644887Schin goto again; 4654887Schin } 4664887Schin 4674887Schin #endif /* SHOPT_BRACEPAT */ 468