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