summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--doc/filename.n9
-rw-r--r--tests/fileSystem.test67
-rw-r--r--win/tclWinFile.c126
4 files changed, 173 insertions, 37 deletions
diff --git a/ChangeLog b/ChangeLog
index f36f3ed..ee3a241 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2004-06-30 Vince Darley <vincentdarley@users.sourceforge.net>
+
+ * doc/filename.n: clarified behaviour concerning trailing
+ slashes in filenames [Bug 971976]
+
+ * win/tclWinFile.c:
+ * tests/fileSystem.test: fix and tests for [Bug 979879]
+
2004-06-30 Donal K. Fellows <donal.k.fellows@man.ac.uk>
TIP#188 IMPLEMENTATION
diff --git a/doc/filename.n b/doc/filename.n
index ef217e6..c2787ec 100644
--- a/doc/filename.n
+++ b/doc/filename.n
@@ -4,7 +4,7 @@
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
-'\" RCS: @(#) $Id: filename.n,v 1.12 2004/03/17 18:14:12 das Exp $
+'\" RCS: @(#) $Id: filename.n,v 1.13 2004/06/30 14:46:10 vincentdarley Exp $
'\"
.so man.macros
.TH filename n 7.5 Tcl "Tcl Built-In Commands"
@@ -51,7 +51,12 @@ absolute, and file names may contain any character other than slash.
The file names \fB\&.\fR and \fB\&..\fR are special and refer to the
current directory and the parent of the current directory respectively.
Multiple adjacent slash characters are interpreted as a single
-separator. The following examples illustrate various forms of path
+separator. Any number of trailing slash characters at the end of a
+path are simply ignored, so the paths \fBfoo\fR, \fBfoo/\fR and
+\fBfoo//\fR are all identical, and in particular \fBfoo/\fR does not
+necessarily mean a directory is being referred.
+.PP
+The following examples illustrate various forms of path
names:
.RS
.TP 15
diff --git a/tests/fileSystem.test b/tests/fileSystem.test
index 6ae080d..45fb97d 100644
--- a/tests/fileSystem.test
+++ b/tests/fileSystem.test
@@ -942,6 +942,73 @@ test filesystem-8.3 {path objects and empty string} {
lappend res $dst $yyy
} {foo foo {}}
+proc TestFind1 {d f} {
+ set r1 [file exists [file join $d $f]]
+ lappend res "[file join $d $f] found: $r1"
+ lappend res "is dir a dir? [file isdirectory $d]"
+ set r2 [file exists [file join $d $f]]
+ lappend res "[file join $d $f] found: $r2"
+ set res
+}
+proc TestFind2 {d f} {
+ set r1 [file exists [file join $d $f]]
+ lappend res "[file join $d $f] found: $r1"
+ lappend res "is dir a dir? [file isdirectory [file join $d]]"
+ set r2 [file exists [file join $d $f]]
+ lappend res "[file join $d $f] found: $r2"
+ set res
+}
+
+test filesystem-9.1 {path objects and join and object rep} {
+ set origdir [pwd]
+ cd [tcltest::temporaryDirectory]
+ file mkdir [file join a b c]
+ set res [TestFind1 a [file join b . c]]
+ file delete -force [file join a b c]
+ cd $origdir
+ set res
+} {{a/b/./c found: 1} {is dir a dir? 1} {a/b/./c found: 1}}
+
+test filesystem-9.2 {path objects and join and object rep} {
+ set origdir [pwd]
+ cd [tcltest::temporaryDirectory]
+ file mkdir [file join a b c]
+ set res [TestFind2 a [file join b . c]]
+ file delete -force [file join a b c]
+ cd $origdir
+ set res
+} {{a/b/./c found: 1} {is dir a dir? 1} {a/b/./c found: 1}}
+
+test filesystem-9.2.1 {path objects and join and object rep} {
+ set origdir [pwd]
+ cd [tcltest::temporaryDirectory]
+ file mkdir [file join a b c]
+ set res [TestFind2 a [file join b .]]
+ file delete -force [file join a b c]
+ cd $origdir
+ set res
+} {{a/b/. found: 1} {is dir a dir? 1} {a/b/. found: 1}}
+
+test filesystem-9.3 {path objects and join and object rep} {
+ set origdir [pwd]
+ cd [tcltest::temporaryDirectory]
+ file mkdir [file join a b c]
+ set res [TestFind1 a [file join b .. b c]]
+ file delete -force [file join a b c]
+ cd $origdir
+ set res
+} {{a/b/../b/c found: 1} {is dir a dir? 1} {a/b/../b/c found: 1}}
+
+test filesystem-9.4 {path objects and join and object rep} {
+ set origdir [pwd]
+ cd [tcltest::temporaryDirectory]
+ file mkdir [file join a b c]
+ set res [TestFind2 a [file join b .. b c]]
+ file delete -force [file join a b c]
+ cd $origdir
+ set res
+} {{a/b/../b/c found: 1} {is dir a dir? 1} {a/b/../b/c found: 1}}
+
cleanupTests
unset -nocomplain drive
}
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index 05462ed..6113b4e 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinFile.c,v 1.65 2004/06/02 23:29:30 hobbs Exp $
+ * RCS: @(#) $Id: tclWinFile.c,v 1.66 2004/06/30 14:46:11 vincentdarley Exp $
*/
//#define _WIN32_WINNT 0x0500
@@ -2366,29 +2366,56 @@ TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
}
Tcl_DStringAppend(&dsNorm,nativePath,Tcl_DStringLength(&ds));
} else {
- WIN32_FIND_DATA fData;
- HANDLE handle;
+ char *checkDots = NULL;
- handle = FindFirstFileA(nativePath, &fData);
- if (handle == INVALID_HANDLE_VALUE) {
- if (GetFileAttributesA(nativePath)
- == 0xffffffff) {
- /* File doesn't exist */
- Tcl_DStringFree(&ds);
- break;
+ if (lastValidPathEnd[1] == '.') {
+ checkDots = lastValidPathEnd + 1;
+ while (checkDots < currentPathEndPosition) {
+ if (*checkDots != '.') {
+ checkDots = NULL;
+ break;
+ }
+ checkDots++;
}
- /* This is usually the '/' in 'c:/' at end of string */
- Tcl_DStringAppend(&dsNorm,"/", 1);
+ }
+ if (checkDots != NULL) {
+ int dotLen = currentPathEndPosition - lastValidPathEnd;
+ /*
+ * Path is just dots. We shouldn't really
+ * ever see a path like that. However, to be
+ * nice we at least don't mangle the path --
+ * we just add the dots as a path segment and
+ * continue
+ */
+ Tcl_DStringAppend(&dsNorm, (TCHAR*)(nativePath
+ + Tcl_DStringLength(&ds)
+ - dotLen), dotLen);
} else {
- char *nativeName;
- if (fData.cFileName[0] != '\0') {
- nativeName = fData.cFileName;
+ /* Normal path */
+ WIN32_FIND_DATA fData;
+ HANDLE handle;
+
+ handle = FindFirstFileA(nativePath, &fData);
+ if (handle == INVALID_HANDLE_VALUE) {
+ if (GetFileAttributesA(nativePath)
+ == 0xffffffff) {
+ /* File doesn't exist */
+ Tcl_DStringFree(&ds);
+ break;
+ }
+ /* This is usually the '/' in 'c:/' at end of string */
+ Tcl_DStringAppend(&dsNorm,"/", 1);
} else {
- nativeName = fData.cAlternateFileName;
+ char *nativeName;
+ if (fData.cFileName[0] != '\0') {
+ nativeName = fData.cFileName;
+ } else {
+ nativeName = fData.cAlternateFileName;
+ }
+ FindClose(handle);
+ Tcl_DStringAppend(&dsNorm,"/", 1);
+ Tcl_DStringAppend(&dsNorm,nativeName,-1);
}
- FindClose(handle);
- Tcl_DStringAppend(&dsNorm,"/", 1);
- Tcl_DStringAppend(&dsNorm,nativeName,-1);
}
}
Tcl_DStringFree(&ds);
@@ -2491,26 +2518,55 @@ TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
}
Tcl_DStringAppend(&dsNorm,nativePath,Tcl_DStringLength(&ds));
} else {
- WIN32_FIND_DATAW fData;
- HANDLE handle;
+ char *checkDots = NULL;
- handle = FindFirstFileW((WCHAR*)nativePath, &fData);
- if (handle == INVALID_HANDLE_VALUE) {
- /* This is usually the '/' in 'c:/' at end of string */
- Tcl_DStringAppend(&dsNorm,(CONST char*)L"/",
- sizeof(WCHAR));
+ if (lastValidPathEnd[1] == '.') {
+ checkDots = lastValidPathEnd + 1;
+ while (checkDots < currentPathEndPosition) {
+ if (*checkDots != '.') {
+ checkDots = NULL;
+ break;
+ }
+ checkDots++;
+ }
+ }
+ if (checkDots != NULL) {
+ int dotLen = currentPathEndPosition - lastValidPathEnd;
+ /*
+ * Path is just dots. We shouldn't really
+ * ever see a path like that. However, to be
+ * nice we at least don't mangle the path --
+ * we just add the dots as a path segment and
+ * continue
+ */
+ Tcl_DStringAppend(&dsNorm,
+ (TCHAR*)((WCHAR*)(nativePath
+ + Tcl_DStringLength(&ds))
+ - dotLen),
+ (int)(dotLen * sizeof(WCHAR)));
} else {
- WCHAR *nativeName;
- if (fData.cFileName[0] != '\0') {
- nativeName = fData.cFileName;
+ /* Normal path */
+ WIN32_FIND_DATAW fData;
+ HANDLE handle;
+
+ handle = FindFirstFileW((WCHAR*)nativePath, &fData);
+ if (handle == INVALID_HANDLE_VALUE) {
+ /* This is usually the '/' in 'c:/' at end of string */
+ Tcl_DStringAppend(&dsNorm,(CONST char*)L"/",
+ sizeof(WCHAR));
} else {
- nativeName = fData.cAlternateFileName;
+ WCHAR *nativeName;
+ if (fData.cFileName[0] != '\0') {
+ nativeName = fData.cFileName;
+ } else {
+ nativeName = fData.cAlternateFileName;
+ }
+ FindClose(handle);
+ Tcl_DStringAppend(&dsNorm,(CONST char*)L"/",
+ sizeof(WCHAR));
+ Tcl_DStringAppend(&dsNorm,(TCHAR*)nativeName,
+ (int) (wcslen(nativeName)*sizeof(WCHAR)));
}
- FindClose(handle);
- Tcl_DStringAppend(&dsNorm,(CONST char*)L"/",
- sizeof(WCHAR));
- Tcl_DStringAppend(&dsNorm,(TCHAR*)nativeName,
- (int) (wcslen(nativeName)*sizeof(WCHAR)));
}
}
#endif