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