1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                  David Korn <dgk@research.att.com>                   *
18*4887Schin *                                                                      *
19*4887Schin ***********************************************************************/
20*4887Schin #pragma prototyped
21*4887Schin /*
22*4887Schin  *	File name expansion
23*4887Schin  *
24*4887Schin  *	David Korn
25*4887Schin  *	AT&T Labs
26*4887Schin  *
27*4887Schin  */
28*4887Schin 
29*4887Schin #if KSHELL
30*4887Schin #   include	"defs.h"
31*4887Schin #   include	"variables.h"
32*4887Schin #   include	"test.h"
33*4887Schin #else
34*4887Schin #   include	<ast.h>
35*4887Schin #   include	<setjmp.h>
36*4887Schin #endif /* KSHELL */
37*4887Schin #include	<glob.h>
38*4887Schin #include	<ls.h>
39*4887Schin #include	<stak.h>
40*4887Schin #include	<ast_dir.h>
41*4887Schin #include	"io.h"
42*4887Schin #include	"path.h"
43*4887Schin 
44*4887Schin #if !SHOPT_BRACEPAT
45*4887Schin #   define SHOPT_BRACEPAT	0
46*4887Schin #endif
47*4887Schin 
48*4887Schin #if KSHELL
49*4887Schin #   define argbegin	argnxt.cp
50*4887Schin     static	const char	*sufstr;
51*4887Schin     static	int		suflen;
52*4887Schin     static int scantree(Dt_t*,const char*, struct argnod**);
53*4887Schin #else
54*4887Schin #   define sh_sigcheck()	(0)
55*4887Schin #   define sh_access		access
56*4887Schin #   define suflen		0
57*4887Schin #endif /* KSHELL */
58*4887Schin 
59*4887Schin 
60*4887Schin /*
61*4887Schin  * This routine builds a list of files that match a given pathname
62*4887Schin  * Uses external routine strgrpmatch() to match each component
63*4887Schin  * A leading . must match explicitly
64*4887Schin  *
65*4887Schin  */
66*4887Schin 
67*4887Schin #ifndef GLOB_AUGMENTED
68*4887Schin #   define GLOB_AUGMENTED	0
69*4887Schin #endif
70*4887Schin 
71*4887Schin #define GLOB_RESCAN 1
72*4887Schin #define globptr()	((struct glob*)membase)
73*4887Schin 
74*4887Schin static struct glob	 *membase;
75*4887Schin 
76*4887Schin #if GLOB_VERSION >= 20010916L
77*4887Schin static char *nextdir(glob_t *gp, char *dir)
78*4887Schin {
79*4887Schin 	Pathcomp_t *pp = (Pathcomp_t*)gp->gl_handle;
80*4887Schin 	if(!dir)
81*4887Schin 		pp = path_get("");
82*4887Schin 	else
83*4887Schin 		pp = pp->next;
84*4887Schin 	gp->gl_handle = (void*)pp;
85*4887Schin 	if(pp)
86*4887Schin 		return(pp->name);
87*4887Schin 	return(0);
88*4887Schin }
89*4887Schin #endif
90*4887Schin 
91*4887Schin int path_expand(const char *pattern, struct argnod **arghead)
92*4887Schin {
93*4887Schin 	glob_t gdata;
94*4887Schin 	register struct argnod *ap;
95*4887Schin 	register glob_t *gp= &gdata;
96*4887Schin 	register int flags,extra=0;
97*4887Schin #if SHOPT_BASH
98*4887Schin 	register int off;
99*4887Schin 	register char *sp, *cp, *cp2;
100*4887Schin #endif
101*4887Schin 	memset(gp,0,sizeof(gdata));
102*4887Schin 	flags = GLOB_AUGMENTED|GLOB_NOCHECK|GLOB_NOSORT|GLOB_STACK|GLOB_LIST|GLOB_DISC;
103*4887Schin 	if(sh_isoption(SH_MARKDIRS))
104*4887Schin 		flags |= GLOB_MARK;
105*4887Schin 	if(sh_isoption(SH_GLOBSTARS))
106*4887Schin 		flags |= GLOB_STARSTAR;
107*4887Schin #if SHOPT_BASH
108*4887Schin #if 0
109*4887Schin 	if(sh_isoption(SH_BASH) && !sh_isoption(SH_EXTGLOB))
110*4887Schin 		flags &= ~GLOB_AUGMENTED;
111*4887Schin #endif
112*4887Schin 	if(sh_isoption(SH_NULLGLOB))
113*4887Schin 		flags &= ~GLOB_NOCHECK;
114*4887Schin 	if(sh_isoption(SH_NOCASEGLOB))
115*4887Schin 		flags |= GLOB_ICASE;
116*4887Schin #endif
117*4887Schin 	if(sh_isstate(SH_COMPLETE))
118*4887Schin 	{
119*4887Schin #if KSHELL
120*4887Schin 		extra += scantree(sh.alias_tree,pattern,arghead);
121*4887Schin 		extra += scantree(sh.fun_tree,pattern,arghead);
122*4887Schin #   if GLOB_VERSION >= 20010916L
123*4887Schin 		gp->gl_nextdir = nextdir;
124*4887Schin #   endif
125*4887Schin #endif /* KSHELL */
126*4887Schin 		flags |= GLOB_COMPLETE;
127*4887Schin 		flags &= ~GLOB_NOCHECK;
128*4887Schin 	}
129*4887Schin #if SHOPT_BASH
130*4887Schin 	if(off = staktell())
131*4887Schin 		sp = stakfreeze(0);
132*4887Schin 	if(sh_isoption(SH_BASH))
133*4887Schin 	{
134*4887Schin 		/*
135*4887Schin 		 * For bash, FIGNORE is a colon separated list of suffixes to
136*4887Schin 		 * ignore when doing filename/command completion.
137*4887Schin 		 * GLOBIGNORE is similar to ksh FIGNORE, but colon separated
138*4887Schin 		 * instead of being an augmented shell pattern.
139*4887Schin 		 * Generate shell patterns out of those here.
140*4887Schin 		 */
141*4887Schin 		if(sh_isstate(SH_FCOMPLETE))
142*4887Schin 			cp=nv_getval(nv_scoped(FIGNORENOD));
143*4887Schin 		else
144*4887Schin 		{
145*4887Schin 			static Namval_t *GLOBIGNORENOD;
146*4887Schin 			if(!GLOBIGNORENOD)
147*4887Schin 				GLOBIGNORENOD = nv_open("GLOBIGNORE",sh.var_tree,0);
148*4887Schin 			cp=nv_getval(nv_scoped(GLOBIGNORENOD));
149*4887Schin 		}
150*4887Schin 		if(cp)
151*4887Schin 		{
152*4887Schin 			flags |= GLOB_AUGMENTED;
153*4887Schin 			stakputs("@(");
154*4887Schin 			if(!sh_isstate(SH_FCOMPLETE))
155*4887Schin 			{
156*4887Schin 				stakputs(cp);
157*4887Schin 				for(cp=stakptr(off); *cp; cp++)
158*4887Schin 					if(*cp == ':')
159*4887Schin 						*cp='|';
160*4887Schin 			}
161*4887Schin 			else
162*4887Schin 			{
163*4887Schin 				cp2 = strtok(cp, ":");
164*4887Schin 				if(!cp2)
165*4887Schin 					cp2=cp;
166*4887Schin 				do
167*4887Schin 				{
168*4887Schin 					stakputc('*');
169*4887Schin 					stakputs(cp2);
170*4887Schin 					if(cp2 = strtok(NULL, ":"))
171*4887Schin 					{
172*4887Schin 						*(cp2-1)=':';
173*4887Schin 						stakputc('|');
174*4887Schin 					}
175*4887Schin 				} while(cp2);
176*4887Schin 			}
177*4887Schin 			stakputc(')');
178*4887Schin 			gp->gl_fignore = stakfreeze(1);
179*4887Schin 		}
180*4887Schin 		else if(!sh_isstate(SH_FCOMPLETE) && sh_isoption(SH_DOTGLOB))
181*4887Schin 			gp->gl_fignore = "";
182*4887Schin 	}
183*4887Schin 	else
184*4887Schin #endif
185*4887Schin 	gp->gl_fignore = nv_getval(nv_scoped(FIGNORENOD));
186*4887Schin 	if(suflen)
187*4887Schin 		gp->gl_suffix = sufstr;
188*4887Schin 	gp->gl_intr = &sh.trapnote;
189*4887Schin 	suflen = 0;
190*4887Schin 	if(memcmp(pattern,"~(N",3)==0)
191*4887Schin 		flags &= ~GLOB_NOCHECK;
192*4887Schin 	glob(pattern, flags, 0, gp);
193*4887Schin #if SHOPT_BASH
194*4887Schin 	if(off)
195*4887Schin 		stakset(sp,off);
196*4887Schin 	else
197*4887Schin 		stakseek(0);
198*4887Schin #endif
199*4887Schin 	sh_sigcheck();
200*4887Schin 	for(ap= (struct argnod*)gp->gl_list; ap; ap = ap->argnxt.ap)
201*4887Schin 	{
202*4887Schin 		ap->argchn.ap = ap->argnxt.ap;
203*4887Schin 		if(!ap->argnxt.ap)
204*4887Schin 			ap->argchn.ap = *arghead;
205*4887Schin 	}
206*4887Schin 	if(gp->gl_list)
207*4887Schin 		*arghead = (struct argnod*)gp->gl_list;
208*4887Schin 	return(gp->gl_pathc+extra);
209*4887Schin }
210*4887Schin 
211*4887Schin #if KSHELL
212*4887Schin 
213*4887Schin /*
214*4887Schin  * scan tree and add each name that matches the given pattern
215*4887Schin  */
216*4887Schin static int scantree(Dt_t *tree, const char *pattern, struct argnod **arghead)
217*4887Schin {
218*4887Schin 	register Namval_t *np;
219*4887Schin 	register struct argnod *ap;
220*4887Schin 	register int nmatch=0;
221*4887Schin 	register char *cp;
222*4887Schin 	np = (Namval_t*)dtfirst(tree);
223*4887Schin 	for(;np && !nv_isnull(np);(np = (Namval_t*)dtnext(tree,np)))
224*4887Schin 	{
225*4887Schin 		if(strmatch(cp=nv_name(np),pattern))
226*4887Schin 		{
227*4887Schin 			ap = (struct argnod*)stakseek(ARGVAL);
228*4887Schin 			stakputs(cp);
229*4887Schin 			ap = (struct argnod*)stakfreeze(1);
230*4887Schin 			ap->argbegin = NIL(char*);
231*4887Schin 			ap->argchn.ap = *arghead;
232*4887Schin 			ap->argflag = ARG_RAW|ARG_MAKE;
233*4887Schin 			*arghead = ap;
234*4887Schin 			nmatch++;
235*4887Schin 		}
236*4887Schin 	}
237*4887Schin 	return(nmatch);
238*4887Schin }
239*4887Schin 
240*4887Schin /*
241*4887Schin  * file name completion
242*4887Schin  * generate the list of files found by adding an suffix to end of name
243*4887Schin  * The number of matches is returned
244*4887Schin  */
245*4887Schin 
246*4887Schin int path_complete(const char *name,register const char *suffix, struct argnod **arghead)
247*4887Schin {
248*4887Schin 	sufstr = suffix;
249*4887Schin 	suflen = strlen(suffix);
250*4887Schin 	return(path_expand(name,arghead));
251*4887Schin }
252*4887Schin 
253*4887Schin #endif
254*4887Schin 
255*4887Schin #if SHOPT_BRACEPAT
256*4887Schin 
257*4887Schin static int checkfmt(Sfio_t* sp, void* vp, Sffmt_t* fp)
258*4887Schin {
259*4887Schin 	return -1;
260*4887Schin }
261*4887Schin 
262*4887Schin int path_generate(struct argnod *todo, struct argnod **arghead)
263*4887Schin /*@
264*4887Schin 	assume todo!=0;
265*4887Schin 	return count satisfying count>=1;
266*4887Schin @*/
267*4887Schin {
268*4887Schin 	register char *cp;
269*4887Schin 	register int brace;
270*4887Schin 	register struct argnod *ap;
271*4887Schin 	struct argnod *top = 0;
272*4887Schin 	struct argnod *apin;
273*4887Schin 	char *pat, *rescan;
274*4887Schin 	char *format;
275*4887Schin 	char comma, range=0;
276*4887Schin 	int first, last, incr, count = 0;
277*4887Schin 	char tmp[32], end[1];
278*4887Schin 	todo->argchn.ap = 0;
279*4887Schin again:
280*4887Schin 	apin = ap = todo;
281*4887Schin 	todo = ap->argchn.ap;
282*4887Schin 	cp = ap->argval;
283*4887Schin 	range = comma = brace = 0;
284*4887Schin 	/* first search for {...,...} */
285*4887Schin 	while(1) switch(*cp++)
286*4887Schin 	{
287*4887Schin 		case '{':
288*4887Schin 			if(brace++==0)
289*4887Schin 				pat = cp;
290*4887Schin 			break;
291*4887Schin 		case '}':
292*4887Schin 			if(--brace>0)
293*4887Schin 				break;
294*4887Schin 			if(brace==0 && comma && *cp!='(')
295*4887Schin 				goto endloop1;
296*4887Schin 			comma = brace = 0;
297*4887Schin 			break;
298*4887Schin 		case '.':
299*4887Schin 			if(brace==1 && *cp=='.')
300*4887Schin 			{
301*4887Schin 				char *endc;
302*4887Schin 				incr = 1;
303*4887Schin 				if(isdigit(*pat) || *pat=='+' || *pat=='-')
304*4887Schin 				{
305*4887Schin 					first = strtol(pat,&endc,0);
306*4887Schin 					if(endc==(cp-1))
307*4887Schin 					{
308*4887Schin 						last = strtol(cp+1,&endc,0);
309*4887Schin 						if(*endc=='.' && endc[1]=='.')
310*4887Schin 							incr = strtol(endc+2,&endc,0);
311*4887Schin 						else if(last<first)
312*4887Schin 							incr = -1;
313*4887Schin 						if(incr)
314*4887Schin 						{
315*4887Schin 							if(*endc=='%')
316*4887Schin 							{
317*4887Schin 								Sffmt_t	fmt;
318*4887Schin 								memset(&fmt, 0, sizeof(fmt));
319*4887Schin 								fmt.version = SFIO_VERSION;
320*4887Schin 								fmt.form = endc;
321*4887Schin 								fmt.extf = checkfmt;
322*4887Schin 								sfprintf(sfstdout, "%!", &fmt);
323*4887Schin 								if(!(fmt.flags&(SFFMT_LLONG|SFFMT_LDOUBLE)))
324*4887Schin 									switch (fmt.fmt)
325*4887Schin 									{
326*4887Schin 									case 'c':
327*4887Schin 									case 'd':
328*4887Schin 									case 'i':
329*4887Schin 									case 'o':
330*4887Schin 									case 'u':
331*4887Schin 									case 'x':
332*4887Schin 									case 'X':
333*4887Schin 										format = endc;
334*4887Schin 										endc = fmt.form;
335*4887Schin 										break;
336*4887Schin 									}
337*4887Schin 							}
338*4887Schin 							else
339*4887Schin 								format = "%d";
340*4887Schin 							if(*endc=='}')
341*4887Schin 							{
342*4887Schin 								cp = endc+1;
343*4887Schin 								range = 2;
344*4887Schin 								goto endloop1;
345*4887Schin 							}
346*4887Schin 						}
347*4887Schin 					}
348*4887Schin 				}
349*4887Schin 				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')))
350*4887Schin 				{
351*4887Schin 					first = *pat;
352*4887Schin 					last = cp[1];
353*4887Schin 					cp += 2;
354*4887Schin 					if(*cp=='.')
355*4887Schin 					{
356*4887Schin 						incr = strtol(cp+2,&endc,0);
357*4887Schin 						cp = endc;
358*4887Schin 					}
359*4887Schin 					else if(first>last)
360*4887Schin 						incr = -1;
361*4887Schin 					if(incr && *cp=='}')
362*4887Schin 					{
363*4887Schin 						cp++;
364*4887Schin 						range = 1;
365*4887Schin 						goto endloop1;
366*4887Schin 					}
367*4887Schin 				}
368*4887Schin 				cp++;
369*4887Schin 			}
370*4887Schin 			break;
371*4887Schin 		case ',':
372*4887Schin 			if(brace==1)
373*4887Schin 				comma = 1;
374*4887Schin 			break;
375*4887Schin 		case '\\':
376*4887Schin 			cp++;
377*4887Schin 			break;
378*4887Schin 		case 0:
379*4887Schin 			/* insert on stack */
380*4887Schin 			ap->argchn.ap = top;
381*4887Schin 			top = ap;
382*4887Schin 			if(todo)
383*4887Schin 				goto again;
384*4887Schin 			for(; ap; ap=apin)
385*4887Schin 			{
386*4887Schin 				apin = ap->argchn.ap;
387*4887Schin 				if(!sh_isoption(SH_NOGLOB))
388*4887Schin 					brace=path_expand(ap->argval,arghead);
389*4887Schin 				else
390*4887Schin 				{
391*4887Schin 					ap->argchn.ap = *arghead;
392*4887Schin 					*arghead = ap;
393*4887Schin 					brace=1;
394*4887Schin 				}
395*4887Schin 				if(brace)
396*4887Schin 				{
397*4887Schin 					count += brace;
398*4887Schin 					(*arghead)->argflag |= ARG_MAKE;
399*4887Schin 				}
400*4887Schin 			}
401*4887Schin 			return(count);
402*4887Schin 	}
403*4887Schin endloop1:
404*4887Schin 	rescan = cp;
405*4887Schin 	cp = pat-1;
406*4887Schin 	*cp = 0;
407*4887Schin 	while(1)
408*4887Schin 	{
409*4887Schin 		brace = 0;
410*4887Schin 		if(range)
411*4887Schin 		{
412*4887Schin 			if(range==1)
413*4887Schin 			{
414*4887Schin 				pat[0] = first;
415*4887Schin 				cp = &pat[1];
416*4887Schin 			}
417*4887Schin 			else
418*4887Schin 			{
419*4887Schin 				*(rescan - 1) = 0;
420*4887Schin 				sfsprintf(pat=tmp,sizeof(tmp),format,first);
421*4887Schin 				*(rescan - 1) = '}';
422*4887Schin 				*(cp = end) = 0;
423*4887Schin 			}
424*4887Schin 			if(incr*(first+incr) > last*incr)
425*4887Schin 				*cp = '}';
426*4887Schin 			else
427*4887Schin 				first += incr;
428*4887Schin 		}
429*4887Schin 		/* generate each pattern and put on the todo list */
430*4887Schin 		else while(1) switch(*++cp)
431*4887Schin 		{
432*4887Schin 			case '\\':
433*4887Schin 				cp++;
434*4887Schin 				break;
435*4887Schin 			case '{':
436*4887Schin 				brace++;
437*4887Schin 				break;
438*4887Schin 			case ',':
439*4887Schin 				if(brace==0)
440*4887Schin 					goto endloop2;
441*4887Schin 				break;
442*4887Schin 			case '}':
443*4887Schin 				if(--brace<0)
444*4887Schin 					goto endloop2;
445*4887Schin 		}
446*4887Schin 	endloop2:
447*4887Schin 		brace = *cp;
448*4887Schin 		*cp = 0;
449*4887Schin 		sh_sigcheck();
450*4887Schin 		ap = (struct argnod*)stakseek(ARGVAL);
451*4887Schin 		ap->argflag = ARG_RAW;
452*4887Schin 		ap->argchn.ap = todo;
453*4887Schin 		stakputs(apin->argval);
454*4887Schin 		stakputs(pat);
455*4887Schin 		stakputs(rescan);
456*4887Schin 		todo = ap = (struct argnod*)stakfreeze(1);
457*4887Schin 		if(brace == '}')
458*4887Schin 			break;
459*4887Schin 		if(!range)
460*4887Schin 			pat = cp+1;
461*4887Schin 	}
462*4887Schin 	goto again;
463*4887Schin }
464*4887Schin 
465*4887Schin #endif /* SHOPT_BRACEPAT */
466