1 # (c) 2008 Steve Bennett <steveb@workware.net.au>
3 # Implements a Tcl-compatible glob command based on readdir
5 # This file is licenced under the FreeBSD license
6 # See LICENCE in this directory for full details.
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 # Use -nocomplain here to return nothing if $dir is not a directory
34 foreach name
[readdir
-nocomplain $dir] {
35 if {[string match
$pattern $name]} {
36 # Only include entries starting with . if the pattern starts with .
37 if {[string index
$name 0] eq
"." && [string index
$pattern 0] ne
"."} {
47 # glob entries in directory $dir and pattern $rem
49 local
proc glob.do
{dir rem
} {
50 # Take one level from rem
52 set i
[string first
/ $rem]
57 set pattern
[string range
$rem 0 $i-1]
58 set rempattern
[string range
$rem $i+1 end
]
61 # Determine the appropriate separator and globbing dir
64 if {[string match
"*/" $dir]} {
66 } elseif
{$dir eq
""} {
73 # If the pattern contains a braced expression, recursively call glob.do
74 # to expand the alternations. Avoid regexp for dependency reasons.
75 # XXX: Doesn't handle backslashed braces
76 if {[set fb
[string first
"\{" $pattern]] >= 0} {
77 if {[set nb
[string first
"\}" $pattern $fb]] >= 0} {
78 set before
[string range
$pattern 0 $fb-1]
79 set braced
[string range
$pattern $fb+1 $nb-1]
80 set after [string range
$pattern $nb+1 end
]
82 foreach part
[split $braced ,] {
83 lappend result
{*}[glob.do
$dir $before$part$after]
89 # Use readdir and select all files which match the pattern
90 foreach f
[glob.readdir_pattern
$globdir $pattern] {
91 if {$rempattern eq
""} {
92 # This is a terminal entry, so add it
93 lappend result
$dir$sep$f
95 # Expany any entries at this level and add them
96 lappend result
{*}[glob.do
$dir$sep$f $rempattern]
105 if {[lindex $args 0] eq
"-nocomplain"} {
107 set args
[lrange $args 1 end
]
111 foreach pattern
$args {
112 if {$pattern eq
"/"} {
114 } elseif
{[string match
"/*" $pattern]} {
115 lappend result
{*}[glob.do
/ [string range
$pattern 1 end
]]
117 lappend result
{*}[glob.do
"" $pattern]
121 if {$nocomplain == 0 && [llength $result] == 0} {
122 return -code error "no files matched glob patterns"