xref: /onnv-gate/usr/src/lib/libshell/common/sh/expand.c (revision 12068:08a39a083754)
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