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