Version 6 of Matthias Hoffmann - Tcl-Code-Snippets - misc - globx

Updated 2013-03-07 23:41:16 by HJG

Version history

  • 2009/06/14: globx2 minimally returns the name of the requested folder itself (before, only subfolders where returned, if some exist) -- Att: incompatibility!
  • 2006/07/26: exposed glob's possibility of specifying more than one search mask to avoid double calls to globx. Backward compatible.
 package provide Globx 0.05

 proc globx {startDir {search *} {cb ""}} {
      set dirStack [list [file normalize $startDir]]
      set files {}
      set fc    0
      while {[llength $dirStack]} {
            set newStack {}
            foreach dir $dirStack {
                    # temporary var's only because eventually using CallBack
                    set c [list glob -noc -typ f          -dir $dir --]; eval lappend c $search; set fn [eval $c]
                    set c [list glob -noc -typ {f hidden} -dir $dir --]; eval lappend c $search; set fh [eval $c]
                    if {[string equal $cb ""]} {
                       eval lappend files $fn $fh
                    } else {
                       foreach f [concat $fn $fh] {
                               incr fc
                               uplevel [list $cb $f]
                       }
                    }
                    set dn [glob -noc -typ d          -dir $dir *]
                    set dh [glob -noc -typ {d hidden} -dir $dir *]
                    # eval lappend newStack $dn $dh; # v0.01
                    # Wikipatch Start v0.02 ---
                    foreach newDir [concat $dn $dh] {
                            set theDir [file tail $newDir]
                            if {[string equal $theDir "." ] || \
                                [string equal $theDir ".."]} {
                               # Don't push this, otherwise entering an endless
                               # loop (on UNIX, at least)
                            } else {
                               lappend newStack $newDir
                            }
                    }
                    # Wikipatch Ende ---
            }
            set dirStack $newStack
            update; # keep Background alive
      }
      if {[string equal $cb ""]} {
         return [lsort $files]
      } else {
         return $fc
      }
 }

 # Die Anwendung von Wildcards hier wäre zwar möglich, aber erst 
 # auf UNTERSTER EBENE sinnvoll bzw. wäre ganz am Ende
 # eine Filterung des Gesamtpfads mittels string match besser.

 proc globx2 {startDir {cb ""}} {
      set dirStack [list [file normalize $startDir]]
      set dirs $dirStack; # bis v0.04: {} (ACHTUNG: potentielle Inkompatibilität!)
      set dc   0
      while {[llength $dirStack]} {
            set newStack {}
            foreach dir $dirStack {
                    set dn [glob -noc -typ d          -dir $dir -- *]
                    set dh [glob -noc -typ {d hidden} -dir $dir -- *]
                    if {[string equal $cb ""]} {
                       eval lappend dirs $dn $dh
                    } else {
                       foreach d [concat $dn $dh] {
                               incr dc
                               uplevel [list $cb $d]
                       }
                    }
                    foreach newDir [concat $dn $dh] {
                            set theDir [file tail $newDir]
                            if {[string equal $theDir "." ] || \
                                [string equal $theDir ".."]} {
                               # Don't push this, otherwise entering an endless
                               # loop (on UNIX, at least)
                            } else {
                               lappend newStack $newDir
                            }
                    }
            }
            set dirStack $newStack
            update
      }
      if {[string equal $cb ""]} {
         return [lsort $dirs]
      } else {
         return $dc
      }
 }

 #*******************************************************************************

Examples:

Without a callback, directly returning the filenames as a list:

 puts [globx c:/winnt]
 puts [globx c:/winnt *.dll]

Returning the filenames unsorted name-by-name via callback:

 proc callback file {
      puts $file
 }

 puts [globx c:/winnt * callback]; # will return the number of files read

This is to save memory!

Specifying search masks (v0.04)

 puts [globx c:/winnt {*.dll *.sys *.exe}]

ECS: I had to include some lines to test for "." and ".." otherwise the routine loops.

MH: On my platform (W2k, Tcl 8.4.6), the original routine does not loop; the glob command never returns '..' and '.'. Which platform did you test the code on?

ECS: Debian Linux: Linux babylon 2.4.26-ow2 #1 Fri Jul 9 15:19:06 BRT 2004 i686 GNU/Linux TCL is 8.4.7 (samething happens with 8.4.6). In any case it is better to be safe than sorry :-)