1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
25 * file name expansion - posix.2 glob with gnu and ast extensions
40 #define GLOB_MAGIC 0xaaaa0000
46 #define MATCHPATH(g) (offsetof(globlist_t,gl_path)+(g)->gl_extra)
48 typedef int (*GL_error_f
)(const char*, int);
49 typedef void* (*GL_opendir_f
)(const char*);
50 typedef struct dirent
* (*GL_readdir_f
)(void*);
51 typedef void (*GL_closedir_f
)(void*);
52 typedef int (*GL_stat_f
)(const char*, struct stat
*);
54 #define _GLOB_PRIVATE_ \
55 GL_error_f gl_errfn; \
58 globlist_t* gl_rescan; \
59 globlist_t* gl_match; \
63 regex_t* gl_ignorei; \
66 unsigned long gl_starstar; \
78 gl_diropen(glob_t
* gp
, const char* path
)
80 return (*gp
->gl_opendir
)(path
);
88 gl_dirnext(glob_t
* gp
, void* handle
)
92 while (dp
= (struct dirent
*)(*gp
->gl_readdir
)(handle
))
98 if (D_TYPE(dp
) != DT_UNKNOWN
&& D_TYPE(dp
) != DT_DIR
&& D_TYPE(dp
) != DT_LNK
)
99 gp
->gl_status
|= GLOB_NOTDIR
;
107 * default gl_dirclose
111 gl_dirclose(glob_t
* gp
, void* handle
)
113 (gp
->gl_closedir
)(handle
);
121 gl_type(glob_t
* gp
, const char* path
, int flags
)
126 if ((flags
& GLOB_STARSTAR
) ? (*gp
->gl_lstat
)(path
, &st
) : (*gp
->gl_stat
)(path
, &st
))
128 else if (S_ISDIR(st
.st_mode
))
130 else if (!S_ISREG(st
.st_mode
))
132 else if (st
.st_mode
& (S_IXUSR
|S_IXGRP
|S_IXOTH
))
144 gl_attr(glob_t
* gp
, const char* path
, int flags
)
146 return strchr(astconf("PATH_ATTRIBUTES", path
, NiL
), 'c') ? GLOB_ICASE
: 0;
154 gl_nextdir(glob_t
* gp
, char* dir
)
156 if (!(dir
= gp
->gl_nextpath
))
157 dir
= gp
->gl_nextpath
= stakcopy(pathbin());
158 switch (*gp
->gl_nextpath
)
164 while (*gp
->gl_nextpath
== ':')
169 while (*gp
->gl_nextpath
)
170 if (*gp
->gl_nextpath
++ == ':')
172 *(gp
->gl_nextpath
- 1) = 0;
185 errorcheck(register glob_t
* gp
, const char* path
)
190 r
= (*gp
->gl_errfn
)(path
, errno
);
191 if (gp
->gl_flags
& GLOB_ERR
)
194 gp
->gl_error
= GLOB_ABORTED
;
203 trim(register char* sp
, register char* p1
, int* n1
, register char* p2
, int* n2
)
205 register char* dp
= sp
;
215 if ((c
= *sp
++) == '\\' && (c
= *sp
++))
231 addmatch(register glob_t
* gp
, const char* dir
, const char* pat
, register const char* rescan
, char* endslash
, int meta
)
233 register globlist_t
* ap
;
237 stakseek(MATCHPATH(gp
));
241 stakputc(gp
->gl_delim
);
248 if ((*gp
->gl_type
)(gp
, stakptr(MATCHPATH(gp
)), 0) != GLOB_DIR
)
250 stakputc(gp
->gl_delim
);
252 /* if null, reserve room for . */
258 rescan
= stakptr(offset
);
259 ap
= (globlist_t
*)stakfreeze(0);
260 ap
->gl_begin
= (char*)rescan
;
261 ap
->gl_next
= gp
->gl_rescan
;
266 if (!endslash
&& (gp
->gl_flags
& GLOB_MARK
) && (type
= (*gp
->gl_type
)(gp
, stakptr(MATCHPATH(gp
)), 0)))
268 if ((gp
->gl_flags
& GLOB_COMPLETE
) && type
!= GLOB_EXE
)
273 else if (type
== GLOB_DIR
&& (gp
->gl_flags
& GLOB_MARK
))
274 stakputc(gp
->gl_delim
);
276 ap
= (globlist_t
*)stakfreeze(1);
277 ap
->gl_next
= gp
->gl_match
;
281 ap
->gl_flags
= MATCH_RAW
|meta
;
282 if (gp
->gl_flags
& GLOB_COMPLETE
)
283 ap
->gl_flags
|= MATCH_MAKE
;
287 * this routine builds a list of files that match a given pathname
288 * uses REG_SHELL of <regex> to match each component
289 * a leading . must match explicitly
293 glob_dir(glob_t
* gp
, globlist_t
* ap
)
295 register char* rescan
;
296 register char* prefix
;
312 int anymeta
= ap
->gl_flags
& MATCH_META
;
315 int meta
= ((gp
->re_flags
& REG_ICASE
) && *ap
->gl_begin
!= '/') ? MATCH_META
: 0;
327 gp
->gl_error
= GLOB_INTR
;
330 pat
= rescan
= ap
->gl_begin
;
331 prefix
= dirname
= ap
->gl_path
+ gp
->gl_extra
;
332 first
= (rescan
== prefix
);
337 switch (c
= *rescan
++)
347 trim(ap
->gl_begin
, rescan
, &t1
, NiL
, NiL
);
350 if (!first
&& !*rescan
&& *(rescan
- 2) == gp
->gl_delim
)
353 c
= (*gp
->gl_type
)(gp
, prefix
, 0);
354 *(rescan
- 2) = gp
->gl_delim
;
356 addmatch(gp
, NiL
, prefix
, NiL
, rescan
- 1, anymeta
);
358 else if ((anymeta
|| !(gp
->gl_flags
& GLOB_NOCHECK
)) && (*gp
->gl_type
)(gp
, prefix
, 0))
359 addmatch(gp
, NiL
, prefix
, NiL
, NiL
, anymeta
);
364 bracket
= MATCH_META
;
365 if (*rescan
== '!' || *rescan
== '^')
375 if (!(gp
->gl_flags
& GLOB_AUGMENTED
))
382 if (!(gp
->gl_flags
& GLOB_NOESCAPE
))
390 if (c
== gp
->gl_delim
)
408 if (!rescan
&& (gp
->gl_flags
& GLOB_COMPLETE
))
418 if (pat
== prefix
+ 1)
423 trim(ap
->gl_begin
, pat
, &t1
, rescan
, &t2
);
428 *(restore1
= pat
- 1) = 0;
430 if (!complete
&& (gp
->gl_flags
& GLOB_STARSTAR
))
431 while (pat
[0] == '*' && pat
[1] == '*' && (pat
[2] == '/' || pat
[2]==0))
437 while (*pat
=='/') pat
++;
452 *(restore2
= rescan
- 1) = 0;
453 if (rescan
&& !complete
&& (gp
->gl_flags
& GLOB_STARSTAR
))
455 register char *p
= rescan
;
456 while (p
[0] == '*' && p
[1] == '*' && (p
[2] == '/' || p
[2]==0))
459 if (starstar
= (p
[2]==0))
474 pat
= strcpy(gp
->gl_opt
, pat
);
479 if (!(dirname
= (*gp
->gl_nextdir
)(gp
, dirname
)))
481 prefix
= streq(dirname
, ".") ? (char*)0 : dirname
;
483 if ((!starstar
&& !gp
->gl_starstar
|| (*gp
->gl_type
)(gp
, dirname
, GLOB_STARSTAR
) == GLOB_DIR
) && (dirf
= (*gp
->gl_diropen
)(gp
, dirname
)))
485 if (!(gp
->re_flags
& REG_ICASE
) && ((*gp
->gl_attr
)(gp
, dirname
, 0) & GLOB_ICASE
))
489 if (err
= regcomp(&rei
, pat
, gp
->re_flags
|REG_ICASE
))
498 if (regcomp(&gp
->re_ignorei
, gp
->gl_fignore
, gp
->re_flags
|REG_ICASE
))
500 gp
->gl_error
= GLOB_APPERR
;
503 gp
->gl_ignorei
= &gp
->re_ignorei
;
505 ire
= gp
->gl_ignorei
;
514 if (err
= regcomp(&rec
, pat
, gp
->re_flags
))
522 *restore2
= gp
->gl_delim
;
523 while ((name
= (*gp
->gl_dirnext
)(gp
, dirf
)) && !*gp
->gl_intr
)
525 if (notdir
= (gp
->gl_status
& GLOB_NOTDIR
))
526 gp
->gl_status
&= ~GLOB_NOTDIR
;
527 if (ire
&& !regexec(ire
, name
, 0, NiL
, 0))
529 if (matchdir
&& (name
[0] != '.' || name
[1] && (name
[1] != '.' || name
[2])) && !notdir
)
530 addmatch(gp
, prefix
, name
, matchdir
, NiL
, anymeta
);
531 if (!regexec(pre
, name
, 0, NiL
, 0))
533 if (!rescan
|| !notdir
)
534 addmatch(gp
, prefix
, name
, rescan
, NiL
, anymeta
);
535 if (starstar
==1 || (starstar
==2 && !notdir
))
536 addmatch(gp
, prefix
, name
, starstar
==2?"":NiL
, NiL
, anymeta
);
540 (*gp
->gl_dirclose
)(gp
, dirf
);
541 if (err
|| errno
&& !errorcheck(gp
, dirname
))
544 else if (!complete
&& !errorcheck(gp
, dirname
))
550 gp
->gl_error
= GLOB_INTR
;
555 *restore1
= gp
->gl_delim
;
557 *restore2
= gp
->gl_delim
;
562 if (err
== REG_ESPACE
)
563 gp
->gl_error
= GLOB_NOSPACE
;
567 glob(const char* pattern
, int flags
, int (*errfn
)(const char*, int), register glob_t
* gp
)
569 register globlist_t
* ap
;
580 const char* nocheck
= pattern
;
584 unsigned char intr
= 0;
588 gp
->gl_errfn
= errfn
;
589 if (flags
& GLOB_APPEND
)
591 if ((gp
->gl_flags
|= GLOB_APPEND
) ^ (flags
|GLOB_MAGIC
))
593 if (((gp
->gl_flags
& GLOB_STACK
) == 0) == (gp
->gl_stak
== 0))
595 if (gp
->gl_starstar
> 1)
596 gp
->gl_flags
|= GLOB_STARSTAR
;
602 gp
->gl_flags
= (flags
&0xffff)|GLOB_MAGIC
;
603 gp
->re_flags
= REG_SHELL
|REG_NOSUB
|REG_LEFT
|REG_RIGHT
|((flags
&GLOB_AUGMENTED
)?REG_AUGMENTED
:0);
608 if (!(flags
& GLOB_DISC
))
625 if (!(flags
& GLOB_ALTDIRFUNC
))
627 gp
->gl_opendir
= (GL_opendir_f
)opendir
;
628 gp
->gl_readdir
= (GL_readdir_f
)readdir
;
629 gp
->gl_closedir
= (GL_closedir_f
)closedir
;
631 gp
->gl_stat
= (GL_stat_f
)pathstat
;
634 gp
->gl_lstat
= (GL_stat_f
)lstat
;
640 gp
->gl_diropen
= gl_diropen
;
642 gp
->gl_dirnext
= gl_dirnext
;
643 if (!gp
->gl_dirclose
)
644 gp
->gl_dirclose
= gl_dirclose
;
646 gp
->gl_type
= gl_type
;
648 gp
->gl_attr
= gl_attr
;
649 if (flags
& GLOB_ICASE
)
650 gp
->re_flags
|= REG_ICASE
;
652 gp
->re_flags
|= REG_SHELL_DOT
;
653 else if (*gp
->gl_fignore
)
655 if (regcomp(&gp
->re_ignore
, gp
->gl_fignore
, gp
->re_flags
))
657 gp
->gl_ignore
= &gp
->re_ignore
;
659 if (gp
->gl_flags
& GLOB_STACK
)
661 else if (!(gp
->gl_stak
= stakcreate(0)))
663 if ((gp
->gl_flags
& GLOB_COMPLETE
) && !gp
->gl_nextdir
)
664 gp
->gl_nextdir
= gl_nextdir
;
668 oldstak
= stakinstall(gp
->gl_stak
, 0);
669 if (flags
& GLOB_DOOFFS
)
670 extra
+= gp
->gl_offs
;
672 suflen
= strlen(gp
->gl_suffix
);
673 if (*(pat
= (char*)pattern
) == '~' && *(pat
+ 1) == '(')
717 flags
= (gp
->gl_flags
= f
) & 0xffff;
719 gp
->re_flags
|= REG_ICASE
;
721 gp
->re_flags
&= ~REG_ICASE
;
723 optlen
= pat
- (char*)pattern
;
732 top
= ap
= (globlist_t
*)stakalloc((optlen
? 2 : 1) * strlen(pattern
) + sizeof(globlist_t
) + suflen
+ gp
->gl_extra
);
735 ap
->gl_begin
= ap
->gl_path
+ gp
->gl_extra
;
736 pat
= strcopy(ap
->gl_begin
, pattern
+ optlen
);
738 pat
= strcopy(pat
, gp
->gl_suffix
);
739 gp
->gl_pat
= optlen
? strncpy(gp
->gl_opt
= pat
+ 1, pattern
, optlen
) : (char*)0;
741 if (!(flags
& GLOB_LIST
))
745 gp
->gl_rescan
= ap
->gl_next
;
747 } while (!gp
->gl_error
&& (ap
= gp
->gl_rescan
));
748 if (gp
->gl_pathc
== skip
)
750 if (flags
& GLOB_NOCHECK
)
753 top
->gl_next
= gp
->gl_match
;
755 strcopy(top
->gl_path
+ gp
->gl_extra
, nocheck
);
758 gp
->gl_error
= GLOB_NOMATCH
;
760 if (flags
& GLOB_LIST
)
761 gp
->gl_list
= gp
->gl_match
;
764 argv
= (char**)stakalloc((gp
->gl_pathc
+ extra
) * sizeof(char*));
765 if (gp
->gl_flags
& GLOB_APPEND
)
768 memcpy(argv
, gp
->gl_pathv
, skip
* sizeof(char*));
782 *argv
++ = ap
->gl_path
+ gp
->gl_extra
;
786 if (!(flags
& GLOB_NOSORT
) && (argv
- av
) > 1)
788 strsort(av
, argv
- av
, strcoll
);
789 if (gp
->gl_starstar
> 1)
790 av
[gp
->gl_pathc
= struniq(av
, argv
- av
)] = 0;
794 if (gp
->gl_starstar
> 1)
795 gp
->gl_flags
&= ~GLOB_STARSTAR
;
797 stakinstall(oldstak
, 0);
804 if ((gp
->gl_flags
& GLOB_MAGIC
) == GLOB_MAGIC
)
806 gp
->gl_flags
&= ~GLOB_MAGIC
;
808 stkclose(gp
->gl_stak
);
810 regfree(gp
->gl_ignore
);
812 regfree(gp
->gl_ignorei
);