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