exec, file: set umask before mkstemp
[jimtcl.git] / glob.tcl
blob6b1ad39a5fc9ba3a4db7169c1203f32188946e26
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} {
12 set result {}
13 set files [readdir $dir]
14 lappend files . ..
16 foreach name $files {
17 if {[string match $pattern $name]} {
18 # Starting dots match only explicitly
19 if {[string index $name 0] eq "." && [string index $pattern 0] ne "."} {
20 continue
22 lappend result $name
26 return $result
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} {
33 set oldexp {}
34 set newexp {""}
36 while 1 {
37 set oldexp $newexp
38 set newexp {}
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]
45 if {$pattern eq ""} {
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 ,] {
52 foreach old $oldexp {
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]
60 break
61 } else {
62 set suf $pattern
63 set rest ""
64 break
68 foreach old $oldexp {
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]} {
81 set dir ""
84 # Recursively expand the parent directory
85 set dirlist [glob.glob $base $dir]
86 set pattern [file tail $pattern]
88 # Collect the files/directories
89 set result {}
90 foreach {realdir dir} $dirlist {
91 if {![file isdir $realdir]} {
92 continue
94 if {[string index $dir end] ne "/" && $dir ne ""} {
95 append dir /
97 foreach name [glob.globdir $realdir $pattern] {
98 lappend result [file join $realdir $name] $dir$name
101 return $result
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 .
115 proc glob {args} {
116 set nocomplain 0
117 set base ""
119 set n 0
120 foreach arg $args {
121 if {[info exists param]} {
122 set $param $arg
123 unset param
124 incr n
125 continue
127 switch -glob -- $arg {
128 -d* {
129 set switch $arg
130 set param base
132 -n* {
133 set nocomplain 1
135 -t* {
136 # Ignored for Tcl compatibility
139 -* {
140 return -code error "bad option \"$switch\": must be -directory, -nocomplain, -tails, or --"
142 -- {
143 incr n
144 break
147 break
150 incr n
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]
161 set result {}
162 foreach pattern $args {
163 set pattern [string map {
164 \\\\ \x01 \\\{ \x02 \\\} \x03 \\, \x04
165 } $pattern]
166 set patexps [lassign [glob.explode $pattern] rest]
167 if {$rest ne ""} {
168 return -code error "unmatched close brace in glob pattern"
170 foreach patexp $patexps {
171 set patexp [string map {
172 \x01 \\\\ \x02 \{ \x03 \} \x04 ,
173 } $patexp]
174 foreach {realname name} [glob.glob $base $patexp] {
175 lappend result $name
180 if {!$nocomplain && [llength $result] == 0} {
181 return -code error "no files matched glob patterns"
184 return $result