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