1 # Implements a mostly Tcl-compatible glob command based on readdir
3 # (c) 2008 Steve Bennett <steveb@workware.net.au>
4 # (c) 2012 Alexander Shpilkin <ashpilkin@gmail.com>
6 # See LICENCE in this directory for licensing.
8 package require readdir
10 # Return a list of all entries in $dir that match the pattern.
11 proc glob.globdir
{dir pattern
} {
13 set files
[readdir
$dir]
17 if {[string match
$pattern $name]} {
18 # Starting dots match only explicitly
19 if {[string index
$name 0] eq
"." && [string index
$pattern 0] ne
"."} {
29 # Return the list of patterns resulting from expanding any braced
30 # alternatives inside the given pattern, prepending the unprocessed
31 # part of the pattern. Does _not_ handle escaped braces or commas.
32 proc glob.explode
{pattern
} {
39 set ob
[string first
\{ $pattern]
40 set cb
[string first
\} $pattern]
42 if {$ob < $cb && $ob != -1} {
43 set mid
[string range
$pattern 0 $ob-1]
44 set subexp
[lassign
[glob.explode
[string range
$pattern $ob+1 end
]] pattern
]
46 error "unmatched open brace in glob pattern"
48 set pattern
[string range
$pattern 1 end
]
50 foreach subs
$subexp {
51 foreach sub
[split $subs ,] {
53 lappend newexp
$old$mid$sub
57 } elseif
{$cb != -1} {
58 set suf
[string range
$pattern 0 $cb-1]
59 set rest
[string range
$pattern $cb end
]
69 lappend newexp
$old$suf
71 linsert $newexp 0 $rest
74 # Core glob implementation. Returns a list of files/directories inside
75 # base matching pattern, in {realname name} pairs.
76 proc glob.
glob {base pattern
} {
77 set dir
[file dirname
$pattern]
78 if {$pattern eq
$dir ||
$pattern eq
""} {
79 return [list [file join $base $dir] $pattern]
80 } elseif
{$pattern eq
[file tail
$pattern]} {
84 # Recursively expand the parent directory
85 set dirlist
[glob.
glob $base $dir]
86 set pattern
[file tail
$pattern]
88 # Collect the files/directories
90 foreach {realdir dir
} $dirlist {
91 if {![file isdir
$realdir]} {
94 if {[string index
$dir end
] ne
"/" && $dir ne
""} {
97 foreach name
[glob.globdir
$realdir $pattern] {
98 lappend result
[file join $realdir $name] $dir$name
104 # Implements the Tcl glob command
106 # Usage: glob ?-nocomplain? ?-directory dir? ?--? pattern ...
108 # Patterns use 'string match' (glob) pattern matching for each
109 # directory level, plus support for braced alternations.
111 # e.g. glob {te[a-e]*/*.{c,tcl}}
113 # Note: files starting with . will only be returned if matching component
114 # of the pattern starts with .
121 if {[info exists param
]} {
127 switch -glob -- $arg {
136 # Ignored for Tcl compatibility
140 return -code error "bad option \"$switch\": must be -directory, -nocomplain, -tails, or --"
152 if {[info exists param
]} {
153 return -code error "missing argument to \"$switch\""
155 if {[llength $args] <= $n} {
156 return -code error "wrong # args: should be \"glob ?options? pattern ?pattern ...?\""
159 set args
[lrange $args $n end
]
162 foreach pattern
$args {
163 set pattern
[string map
{
164 \\\\ \x01 \\\{ \x02 \\\} \x03 \\, \x04
166 set patexps
[lassign
[glob.explode
$pattern] rest
]
168 return -code error "unmatched close brace in glob pattern"
170 foreach patexp
$patexps {
171 set patexp
[string map
{
172 \x01 \\\\ \x02 \{ \x03 \} \x04 ,
174 foreach {realname name
} [glob.
glob $base $patexp] {
180 if {!$nocomplain && [llength $result] == 0} {
181 return -code error "no files matched glob patterns"