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
nextdir(glob_t * gp,char * dir)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
path_expand(const char * pattern,struct argnod ** arghead)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 */
scantree(Dt_t * tree,const char * pattern,struct argnod ** arghead)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
path_complete(const char * name,register const char * suffix,struct argnod ** arghead)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
checkfmt(Sfio_t * sp,void * vp,Sffmt_t * fp)2604887Schin static int checkfmt(Sfio_t* sp, void* vp, Sffmt_t* fp)
2614887Schin {
2624887Schin return -1;
2634887Schin }
2644887Schin
path_generate(struct argnod * todo,struct argnod ** arghead)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