1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1982-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
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
55da2e3ebdSchin # define sh_sigcheck() (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 {
80da2e3ebdSchin Pathcomp_t *pp = (Pathcomp_t*)gp->gl_handle;
81da2e3ebdSchin if(!dir)
82da2e3ebdSchin pp = path_get("");
83da2e3ebdSchin else
84da2e3ebdSchin pp = pp->next;
85da2e3ebdSchin gp->gl_handle = (void*)pp;
86da2e3ebdSchin if(pp)
87da2e3ebdSchin return(pp->name);
88da2e3ebdSchin return(0);
89da2e3ebdSchin }
90da2e3ebdSchin #endif
91da2e3ebdSchin
path_expand(const char * pattern,struct argnod ** arghead)92da2e3ebdSchin int path_expand(const char *pattern, struct argnod **arghead)
93da2e3ebdSchin {
947c2fbfb3SApril Chin Shell_t *shp = &sh;
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));
105da2e3ebdSchin flags = 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
202da2e3ebdSchin sh_sigcheck();
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(const char * name,register const char * suffix,struct argnod ** arghead)249da2e3ebdSchin int path_complete(const char *name,register const char *suffix, struct argnod **arghead)
250da2e3ebdSchin {
251da2e3ebdSchin sufstr = suffix;
252da2e3ebdSchin suflen = strlen(suffix);
253da2e3ebdSchin return(path_expand(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(struct argnod * todo,struct argnod ** arghead)265da2e3ebdSchin int path_generate(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))
391da2e3ebdSchin brace=path_expand(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;
452da2e3ebdSchin sh_sigcheck();
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