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