1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 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
nextdir(glob_t * gp,char * dir)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
path_expand(const char * pattern,struct argnod ** arghead)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 */
scantree(Dt_t * tree,const char * pattern,struct argnod ** arghead)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
path_complete(const char * name,register const char * suffix,struct argnod ** arghead)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
checkfmt(Sfio_t * sp,void * vp,Sffmt_t * fp)260 static int checkfmt(Sfio_t* sp, void* vp, Sffmt_t* fp)
261 {
262 return -1;
263 }
264
path_generate(struct argnod * todo,struct argnod ** arghead)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