1 # Implements a Tcl-compatible glob command based on readdir
3 # (c) 2008 Steve Bennett <steveb@workware.net.au>
5 # See LICENCE in this directory for licensing.
7 package require readdir
9 # Implements the Tcl glob command
11 # Usage: glob ?-nocomplain? pattern ...
13 # Patterns use 'string match' (glob) pattern matching for each
14 # directory level, plus support for braced alternations.
16 # e.g. glob "te[a-e]*/*.{c,tcl}"
18 # Note: files starting with . will only be returned if matching component
19 # of the pattern starts with .
22 # If $dir is a directory, return a list of all entries
23 # it contains which match $pattern
25 local
proc glob.readdir_pattern
{dir pattern
} {
28 # readdir doesn't return . or .., so simulate it here
29 if {$pattern in
{. ..
}} {
33 # If the pattern isn't actually a pattern...
34 if {[string match
{*[*?
]*} $pattern]} {
35 # Use -nocomplain here to return nothing if $dir is not a directory
36 set files
[readdir
-nocomplain $dir]
37 } elseif
{[file isdir
$dir] && [file exists
$dir/$pattern]} {
38 set files
[list $pattern]
44 if {[string match
$pattern $name]} {
45 # Only include entries starting with . if the pattern starts with .
46 if {[string index
$name 0] eq
"." && [string index
$pattern 0] ne
"."} {
56 # If the pattern contains a braced expression, return a list of
57 # patterns with the braces expanded. {c,b}* => c* b*
58 # Otherwise just return the pattern
59 # Note: Only supports one braced expression. i.e. not {a,b}*{c,d}*
60 proc glob.expandbraces
{pattern
} {
61 # Avoid regexp for dependency reasons.
62 # XXX: Doesn't handle backslashed braces
63 if {[set fb
[string first
"\{" $pattern]] < 0} {
66 if {[set nb
[string first
"\}" $pattern $fb]] < 0} {
69 set before
[string range
$pattern 0 $fb-1]
70 set braced
[string range
$pattern $fb+1 $nb-1]
71 set after [string range
$pattern $nb+1 end
]
73 lmap part
[split $braced ,] {
74 set pat
$before$part$after
78 # Core glob implementation. Returns a list of files/directories matching the pattern
79 proc glob.
glob {pattern
} {
80 set dir
[file dirname
$pattern]
81 if {$dir eq
$pattern} {
86 # Recursively expand the parent directory
87 set dirlist
[glob.
glob $dir]
88 set pattern
[file tail
$pattern]
90 # Now collect the fiels/directories
92 foreach dir
$dirlist {
94 if {[string match
"*/" $dir]} {
96 } elseif
{$dir eq
"."} {
102 foreach pat
[glob.expandbraces
$pattern] {
103 foreach name
[glob.readdir_pattern
$dir $pat] {
104 lappend result
$globdir$sep$name
114 if {[lindex $args 0] eq
"-nocomplain"} {
116 set args
[lrange $args 1 end
]
120 foreach pattern
$args {
121 lappend result
{*}[glob.
glob $pattern]
124 if {$nocomplain == 0 && [llength $result] == 0} {
125 return -code error "no files matched glob patterns"