Tk Library Source Code

Artifact [00b03096ef]
Login

Artifact 00b03096ef6937d89bfab790818040ab6c436f45:

Attachment "futil.diff" to ticket [410106ffff] added by andreas_kupries 2001-03-21 02:07:34.
--- ./modules/fileutil/pkgIndex.tcl.orig	Thu Mar  2 00:27:00 2000
+++ ./modules/fileutil/pkgIndex.tcl	Tue Mar 20 19:29:05 2001
@@ -8,4 +8,4 @@
 # script is sourced, the variable $dir must contain the
 # full path name of this file's directory.
 
-package ifneeded fileutil 1.0 [list source [file join $dir fileutil.tcl]]
+package ifneeded fileutil 1.1 [list source [file join $dir fileutil.tcl]]
--- ./modules/fileutil/ChangeLog.orig	Fri Mar 10 23:59:17 2000
+++ ./modules/fileutil/ChangeLog	Tue Mar 20 18:51:34 2001
@@ -1,11 +1,21 @@
+2001-03-20  Andreas Kupries <[email protected]>
+
+	* fileutil.tcl: New implementation of fileutil::find for unixoid
+	  OSs using stat and device/inode configuration to detect and
+	  break circular softlink structures. This implementation also
+	  skips un'stat'able files and directories.
+
+	* fileutil.test: Added fileutil-1.4 testing the circle breaker
+	  (only under unix).
+
 2000-03-10  Eric Melski  <[email protected]>
 
 	* fileutil.test:
 	* fileutil.tcl: Added cat function, duplicates standard UNIX "cat"
-	utility.
+	  utility.
 
 2000-03-09  Eric Melski  <[email protected]>
 
 	* fileutil.test: Collected tests into one file; adapted tests for
-	use in/out of tcllib test framework.
+	  use in/out of tcllib test framework.
 
--- ./modules/fileutil/fileutil.tcl.orig	Fri Jun  2 20:43:54 2000
+++ ./modules/fileutil/fileutil.tcl	Tue Mar 20 19:29:17 2001
@@ -9,7 +9,7 @@
 # 
 # RCS: @(#) $Id: fileutil.tcl,v 1.4 2000/06/02 18:43:54 ericm Exp $
 
-package provide fileutil 1.0
+package provide fileutil 1.1
 
 namespace eval ::fileutil {
     namespace export *
@@ -53,43 +53,130 @@
     return $result
 }
 
-# ::fileutil::find --
+# ::fileutil::find ==
 #
-#	Implementation of find.  Adapted from the Tcler's Wiki.
-#
-# Arguments:
-#	basedir		directory to start searching from; default is .
-#	filtercmd	command to use to evaluate interest in each file.
-#			If NULL, all files are interesting.
-#
-# Results:
-#	files		a list of interesting files.
+# Two different implementations of this command, one for unix with its
+# softlinks, the other for the Win* platform. The trouble with
+# softlink is that they can generate circles in the directory and/or
+# file structure, leading a simple recursion into infinity. So we
+# record device/inode information for each file and directory we touch
+# to be able to skip it should we happen to visit it again.
+
+# Note about the general implementation: The tcl interpreter sets a
+# tcl stack limit of 1000 levels to prevent infinite recursions from
+# running out of bounds. As this command is implemented recursively it
+# will fail for very deeply nested directory structures.
+
+if {[string compare unix $tcl_platform(platform)]} {
+    # Not a unix platform => Original implementation
+    # Note: This may still fail for directories mounted via SAMBA,
+    # i.e. coming from a unix server.
 
-proc ::fileutil::find {{basedir .} {filtercmd {}}} {
-    set oldwd [pwd]
-    cd $basedir
-    set cwd [pwd]
-    set filenames [glob -nocomplain * .*]
-    set files {}
-    set filt [string length $filtercmd]
-    # If we don't remove . and .. from the file list, we'll get stuck in
-    # an infinite loop in an infinite loop in an infinite loop in an inf...
-    foreach special [list "." ".."] {
-	set index [lsearch -exact $filenames $special]
-	set filenames [lreplace $filenames $index $index]
+    # ::fileutil::find --
+    #
+    #	Implementation of find.  Adapted from the Tcler's Wiki.
+    #
+    # Arguments:
+    #	basedir		directory to start searching from; default is .
+    #	filtercmd	command to use to evaluate interest in each file.
+    #			If NULL, all files are interesting.
+    #
+    # Results:
+    #	files		a list of interesting files.
+
+    proc ::fileutil::find {{basedir .} {filtercmd {}}} {
+	set oldwd [pwd]
+	cd $basedir
+	set cwd [pwd]
+	set filenames [glob -nocomplain * .*]
+	set files {}
+	set filt [string length $filtercmd]
+	# If we don't remove . and .. from the file list, we'll get stuck in
+	# an infinite loop in an infinite loop in an infinite loop in an inf...
+	foreach special [list "." ".."] {
+	    set index [lsearch -exact $filenames $special]
+	    set filenames [lreplace $filenames $index $index]
+	}
+	foreach filename $filenames {
+	    # Use uplevel to eval the command, not eval, so that variable 
+	    # substitutions occur in the right context.
+	    if {!$filt || [uplevel $filtercmd [list $filename]]} {
+		lappend files [file join $cwd $filename]
+	    }
+	    if {[file isdirectory $filename]} {
+		set files [concat $files [find $filename $filtercmd]]
+	    }
+	}
+	cd $oldwd
+	return $files
     }
-    foreach filename $filenames {
-	# Use uplevel to eval the command, not eval, so that variable 
-	# substitutions occur in the right context.
-	if {!$filt || [uplevel $filtercmd [list $filename]]} {
-	    lappend files [file join $cwd $filename]
+} else {
+    # Unix, record dev/inode to detect and break circles
+
+    # ::fileutil::find --
+    #
+    #	Implementation of find.  Adapted from the Tcler's Wiki.
+    #
+    # Arguments:
+    #	basedir		directory to start searching from; default is .
+    #	filtercmd	command to use to evaluate interest in each file.
+    #			If NULL, all files are interesting.
+    #
+    # Results:
+    #	files		a list of interesting files.
+
+    proc ::fileutil::find {{basedir .} {filtercmd {}} {nodeVar {}}} {
+	if {$nodeVar == {}} {
+	    # Main call, setup the device/inode structure
+	    array set inodes {}
+	} else {
+	    # Recursive call, import the device/inode record from the caller.
+	    upvar $nodeVar inodes
+	}
+
+	set oldwd [pwd]
+	cd $basedir
+	set cwd [pwd]
+	set filenames [glob -nocomplain * .*]
+	set files {}
+	set filt [string length $filtercmd]
+	# If we don't remove . and .. from the file list, we'll get stuck in
+	# an infinite loop in an infinite loop in an infinite loop in an inf...
+	foreach special [list "." ".."] {
+	    set index [lsearch -exact $filenames $special]
+	    set filenames [lreplace $filenames $index $index]
 	}
-	if {[file isdirectory $filename]} {
-	    set files [concat $files [find $filename $filtercmd]]
+	foreach filename $filenames {
+	    # Stat each file/directory get exact information about its identity
+	    # (device, inode). Non-'stat'able files are either junk (link to
+	    # non-existing target) or not readable, i.e. inaccessible. In both
+	    # cases it makes sense to ignore them.
+
+	    if {[catch {file stat [file join $cwd $filename] stat}]} {
+		continue
+	    }
+
+	    # No skip over previously recorded files/directories and
+	    # record the new files/directories.
+
+	    set key "$stat(dev),$stat(ino)"
+	    if {[info exists inodes($key)]} {
+		continue
+	    }
+	    set inodes($key) 1
+
+	    # Use uplevel to eval the command, not eval, so that variable 
+	    # substitutions occur in the right context.
+	    if {!$filt || [uplevel $filtercmd [list $filename]]} {
+		lappend files [file join $cwd $filename]
+	    }
+	    if {[file isdirectory $filename]} {
+		set files [concat $files [find $filename $filtercmd inodes]]
+	    }
 	}
+	cd $oldwd
+	return $files
     }
-    cd $oldwd
-    return $files
 }
 
 # ::fileutil::cat --
--- ./modules/fileutil/fileutil.test.orig	Wed May 31 02:00:00 2000
+++ ./modules/fileutil/fileutil.test	Tue Mar 20 19:30:14 2001
@@ -1,9 +1,11 @@
+# -*- tcl -*-
 # Tests for the find function.
 #
 # Sourcing this file into Tcl runs the tests and generates output for errors.
 # No output means no errors were found.
 #
 # Copyright (c) 1998-2000 by Ajuba Solutions.
+# Copyright (c) 2001 by ActiveState Tool Corp.
 # All rights reserved.
 #
 # RCS: @(#) $Id: fileutil.test,v 1.3 2000/05/31 00:00:00 ericm Exp $
@@ -18,8 +20,19 @@
 }
 
 package require fileutil
+puts "fileutil [package present fileutil]"
 
 # Build a sample tree to search
+# Structure
+#
+#	dir
+#	+--find1
+#          +--find2
+#          |  +--file2
+#          +--file1
+
+catch {removeDirectory find1} ; # start with a clean structure!
+
 makeDirectory find1
 makeDirectory [file join find1 find2]
 makeFile "" [file join find1 file1]
@@ -40,6 +53,29 @@
 test find-1.3 {find files bigger than a given size} {
     fileutil::find [file join $dir find1] {fileIsBiggerThan 1}
 } [list [file join $dir find1 find2 file2]]
+
+
+# Extend the previous sample tree
+# Extended structure:
+#
+#	dir
+#	+--find1
+#          +--find2       <----------+
+#          |  +--file2		     |
+#          |  +--file3 --> ../find2 -+
+#          +--file1
+
+test find-1.4 {handling of circular links} {unix} {
+    catch {file delete -force [file join $dir find1 find2 file3]}
+    exec ln -s ../find2 [file join $dir find1 find2 file3]
+
+    # Find has to skip 'file3'
+    lsort [fileutil::find [file join $dir find1]]
+} [list [file join $dir find1 file1] [file join $dir find1 find2] \
+	[file join $dir find1 find2 file2]]
+
+
+catch {removeDirectory grepTest} ; # start with a clean structure!
 
 # Build a sample tree to search
 makeDirectory grepTest