summaryrefslogtreecommitdiffstats
path: root/generic/tclIOUtil.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclIOUtil.c')
-rw-r--r--generic/tclIOUtil.c746
1 files changed, 615 insertions, 131 deletions
diff --git a/generic/tclIOUtil.c b/generic/tclIOUtil.c
index 4dd0cfa..d191758 100644
--- a/generic/tclIOUtil.c
+++ b/generic/tclIOUtil.c
@@ -17,7 +17,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclIOUtil.c,v 1.14 2001/08/11 18:43:21 vincentdarley Exp $
+ * RCS: @(#) $Id: tclIOUtil.c,v 1.15 2001/08/23 17:37:08 vincentdarley Exp $
*/
#include "tclInt.h"
@@ -35,7 +35,7 @@ static void FreeFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *listPtr));
static int SetFsPathFromAny _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Obj *objPtr));
static Tcl_Obj* FSNormalizeAbsolutePath
- _ANSI_ARGS_((Tcl_Interp* interp, char *path));
+ _ANSI_ARGS_((Tcl_Interp* interp, Tcl_Obj *pathPtr));
static int TclNormalizeToUniquePath
_ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *pathPtr));
static int SetFsPathFromAbsoluteNormalized
@@ -43,6 +43,9 @@ static int SetFsPathFromAbsoluteNormalized
static int FindSplitPos _ANSI_ARGS_((char *path, char *separator));
static Tcl_Filesystem* Tcl_FSGetFileSystemForPath
_ANSI_ARGS_((Tcl_Obj* pathObjPtr));
+static Tcl_PathType GetPathType _ANSI_ARGS_((Tcl_Obj *pathObjPtr,
+ Tcl_Filesystem **filesystemPtrPtr,
+ int *driveNameLengthPtr, Tcl_Obj **driveNameRef));
/*
* Define the 'path' object type, which Tcl uses to represent
@@ -184,6 +187,17 @@ Tcl_EvalFile(interp, fileName)
return ret;
}
+/* Obsolete */
+int
+TclpListVolumes(
+ Tcl_Interp *interp) /* Interpreter for returning volume list. */
+{
+ Tcl_Obj *resultPtr = TclpObjListVolumes();
+ Tcl_SetObjResult(interp, resultPtr);
+ Tcl_DecrRefCount(resultPtr);
+ return TCL_OK;
+}
+
/*
* The 3 hooks for Stat, Access and OpenFileChannel are obsolete. The
@@ -313,8 +327,8 @@ Tcl_FSCreateDirectoryProc TclpObjCreateDirectory;
Tcl_FSCopyDirectoryProc TclpObjCopyDirectory;
Tcl_FSRemoveDirectoryProc TclpObjRemoveDirectory;
Tcl_FSUnloadFileProc TclpUnloadFile;
-Tcl_FSReadlinkProc TclpObjReadlink;
-Tcl_FSListVolumesProc TclpListVolumes;
+Tcl_FSLinkProc TclpObjLink;
+Tcl_FSListVolumesProc TclpObjListVolumes;
/* Define the native filesystem dispatch table */
static Tcl_Filesystem nativeFilesystem = {
@@ -337,9 +351,9 @@ static Tcl_Filesystem nativeFilesystem = {
#ifndef S_IFLNK
NULL,
#else
- &TclpObjReadlink,
+ &TclpObjLink,
#endif /* S_IFLNK */
- &TclpListVolumes,
+ &TclpObjListVolumes,
&NativeFileAttrStrings,
&NativeFileAttrsGet,
&NativeFileAttrsSet,
@@ -406,7 +420,7 @@ TCL_DECLARE_MUTEX(filesystemMutex)
* container Tcl_Obj of this FsPath.
*/
typedef struct FsPath {
- char *translatedPathPtr; /* Name without any ~user sequences.
+ Tcl_Obj *translatedPathPtr; /* Name without any ~user sequences.
* If this is NULL, then this is a
* pure normalized, absolute path
* object, in which the parent Tcl_Obj's
@@ -731,31 +745,42 @@ Tcl_FSData(fsPtr)
*---------------------------------------------------------------------------
*/
static Tcl_Obj*
-FSNormalizeAbsolutePath(interp, path)
+FSNormalizeAbsolutePath(interp, pathPtr)
Tcl_Interp* interp; /* Interpreter to use */
- char *path; /* Absolute path to normalize (UTF-8) */
+ Tcl_Obj *pathPtr; /* Absolute path to normalize */
{
- char **sp = NULL, *np[BUFSIZ];
int splen = 0, nplen, i;
Tcl_Obj *retVal;
+ Tcl_Obj *split;
- Tcl_SplitPath(path, &splen, &sp);
-
+ /* Split has refCount zero */
+ split = Tcl_FSSplitPath(pathPtr, &splen);
+
+ /*
+ * Modify the list of entries in place, by removing '.', and
+ * removing '..' and the entry before -- unless that entry before
+ * is the top-level entry, i.e. the name of a volume.
+ */
nplen = 0;
for (i = 0;i < splen;i++) {
- if (strcmp(sp[i], ".") == 0)
- continue;
-
- if (strcmp(sp[i], "..") == 0) {
- if (nplen > 1) nplen--;
+ Tcl_Obj *elt;
+ Tcl_ListObjIndex(NULL, split, nplen, &elt);
+
+ if (strcmp(Tcl_GetString(elt), ".") == 0) {
+ Tcl_ListObjReplace(NULL, split, nplen, 1, 0, NULL);
+ } else if (strcmp(Tcl_GetString(elt), "..") == 0) {
+ if (nplen > 1) {
+ nplen--;
+ Tcl_ListObjReplace(NULL, split, nplen, 2, 0, NULL);
+ } else {
+ Tcl_ListObjReplace(NULL, split, nplen, 1, 0, NULL);
+ }
} else {
- np[nplen++] = sp[i];
+ nplen++;
}
}
if (nplen > 0) {
- Tcl_DString dtemp;
- Tcl_DStringInit(&dtemp);
- Tcl_JoinPath(nplen, np, &dtemp);
+ retVal = Tcl_FSJoinPath(split, nplen);
/*
* Now we have an absolute path, with no '..', '.' sequences,
* but it still may not be in 'unique' form, depending on the
@@ -767,8 +792,6 @@ FSNormalizeAbsolutePath(interp, path)
* Virtual file systems which may be registered may have
* other criteria for normalizing a path.
*/
- retVal = Tcl_NewStringObj(Tcl_DStringValue(&dtemp),-1);
- Tcl_DStringFree(&dtemp);
Tcl_IncrRefCount(retVal);
TclNormalizeToUniquePath(interp, retVal);
/*
@@ -782,7 +805,17 @@ FSNormalizeAbsolutePath(interp, path)
retVal = Tcl_NewStringObj("",0);
Tcl_IncrRefCount(retVal);
}
- ckfree((char*) sp);
+ /*
+ * We increment and then decrement the refCount of split to free
+ * it. We do this right at the end, in case there are
+ * optimisations in Tcl_FSJoinPath(split, nplen) above which would
+ * let it make use of split more effectively if it has a refCount
+ * of zero. Also we can't just decrement the ref count, in case
+ * 'split' was actually returned by the join call above, in a
+ * single-element optimisation when nplen == 1.
+ */
+ Tcl_IncrRefCount(split);
+ Tcl_DecrRefCount(split);
/* This has a refCount of 1 for the caller */
return retVal;
@@ -1258,12 +1291,18 @@ Tcl_FSStat(pathPtr, buf)
Tcl_Obj *pathPtr; /* Path of file to stat (in current CP). */
struct stat *buf; /* Filled with results of stat call. */
{
+ Tcl_Filesystem *fsPtr;
#ifdef USE_OBSOLETE_FS_HOOKS
StatProc *statProcPtr;
int retVal = -1;
+ char *path;
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
+ if (transPtr == NULL) {
+ path = NULL;
+ } else {
+ path = Tcl_GetString(transPtr);
+ }
#endif /* USE_OBSOLETE_FS_HOOKS */
- Tcl_Filesystem *fsPtr;
- char *path = Tcl_FSGetTranslatedPath(NULL, pathPtr);
/*
* Call each of the "stat" function in succession. A non-return
@@ -1357,12 +1396,18 @@ Tcl_FSAccess(pathPtr, mode)
Tcl_Obj *pathPtr; /* Path of file to access (in current CP). */
int mode; /* Permission setting. */
{
+ Tcl_Filesystem *fsPtr;
#ifdef USE_OBSOLETE_FS_HOOKS
AccessProc *accessProcPtr;
int retVal = -1;
+ char *path;
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
+ if (transPtr == NULL) {
+ path = NULL;
+ } else {
+ path = Tcl_GetString(transPtr);
+ }
#endif /* USE_OBSOLETE_FS_HOOKS */
- Tcl_Filesystem *fsPtr;
- char *path = Tcl_FSGetTranslatedPath(NULL, pathPtr);
/*
* Call each of the "access" function in succession. A non-return
@@ -1422,15 +1467,23 @@ Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions)
* file, with what modes to create
* it? */
{
+ Tcl_Filesystem *fsPtr;
#ifdef USE_OBSOLETE_FS_HOOKS
OpenFileChannelProc *openFileChannelProcPtr;
Tcl_Channel retVal = NULL;
+ char *path;
#endif /* USE_OBSOLETE_FS_HOOKS */
- Tcl_Filesystem *fsPtr;
- char *path = Tcl_FSGetTranslatedPath(interp, pathPtr);
- if (path == NULL) {
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
+ if (transPtr == NULL) {
return NULL;
}
+#ifdef USE_OBSOLETE_FS_HOOKS
+ if (transPtr == NULL) {
+ path = NULL;
+ } else {
+ path = Tcl_GetString(transPtr);
+ }
+#endif /* USE_OBSOLETE_FS_HOOKS */
/*
* Call each of the "Tcl_OpenFileChannel" function in succession.
@@ -1672,8 +1725,7 @@ Tcl_FSGetCwd(interp)
* could be problematic.
*/
if (retVal != NULL) {
- Tcl_Obj *norm = FSNormalizeAbsolutePath(interp,
- Tcl_GetString(retVal));
+ Tcl_Obj *norm = FSNormalizeAbsolutePath(interp, retVal);
if (norm != NULL) {
/*
* We found a cwd, which is now in our global storage.
@@ -1722,8 +1774,7 @@ Tcl_FSGetCwd(interp)
if (proc != NULL) {
Tcl_Obj *retVal = (*proc)(interp);
if (retVal != NULL) {
- Tcl_Obj *norm = FSNormalizeAbsolutePath(interp,
- Tcl_GetString(retVal));
+ Tcl_Obj *norm = FSNormalizeAbsolutePath(interp, retVal);
/*
* Check whether cwd has changed from the value
* previously stored in cwdPathPtr. Really 'norm'
@@ -1833,7 +1884,7 @@ Tcl_FSUtime (pathPtr, tval)
*----------------------------------------------------------------------
*/
-char**
+static char**
NativeFileAttrStrings(pathPtr, objPtrRef)
Tcl_Obj *pathPtr;
Tcl_Obj** objPtrRef;
@@ -1865,16 +1916,16 @@ NativeFileAttrStrings(pathPtr, objPtrRef)
*----------------------------------------------------------------------
*/
-int
+static int
NativeFileAttrsGet(interp, index, fileName, objPtrRef)
Tcl_Interp *interp; /* The interpreter for error reporting. */
int index; /* index of the attribute command. */
Tcl_Obj *fileName; /* filename we are operating on. */
Tcl_Obj **objPtrRef; /* for output. */
{
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, fileName);
return (*tclpFileAttrProcs[index].getProc)(interp, index,
- Tcl_FSGetTranslatedPath(NULL, fileName),
- objPtrRef);
+ transPtr, objPtrRef);
}
/*
@@ -1897,16 +1948,16 @@ NativeFileAttrsGet(interp, index, fileName, objPtrRef)
*----------------------------------------------------------------------
*/
-int
+static int
NativeFileAttrsSet(interp, index, fileName, objPtr)
Tcl_Interp *interp; /* The interpreter for error reporting. */
int index; /* index of the attribute command. */
Tcl_Obj *fileName; /* filename we are operating on. */
Tcl_Obj *objPtr; /* set to this value. */
{
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, fileName);
return (*tclpFileAttrProcs[index].setProc)(interp, index,
- Tcl_FSGetTranslatedPath(NULL, fileName),
- objPtr);
+ transPtr, objPtr);
}
/*
@@ -2280,18 +2331,29 @@ FSUnloadTempFile(clientData)
/*
*---------------------------------------------------------------------------
*
- * Tcl_FSReadlink --
+ * Tcl_FSLink --
*
- * This function replaces the library version of readlink().
- * The appropriate function for the filesystem to which pathPtr
- * belongs will be called.
+ * This function replaces the library version of readlink() and
+ * can also be used to make links. The appropriate function for
+ * the filesystem to which pathPtr belongs will be called.
*
* Results:
- * The result is a Tcl_Obj specifying the contents
- * of the symbolic link given by 'path', or NULL if the symbolic
- * link could not be read. The result is owned by the caller,
- * which should call Tcl_DecrRefCount when the result is no longer
- * needed.
+ * If toPtr is NULL, then the result is a Tcl_Obj specifying the
+ * contents of the symbolic link given by 'pathPtr', or NULL if
+ * the symbolic link could not be read. The result is owned by
+ * the caller, which should call Tcl_DecrRefCount when the result
+ * is no longer needed.
+ *
+ * If toPtr is non-NULL, then the result is toPtr if the link
+ * was successful, or NULL if not. In this case the result has no
+ * additional reference count, and need not be freed.
+ *
+ * Note that most filesystems will not support linking across
+ * to different filesystems, so this function will usually
+ * fail unless toPtr is in the same FS as pathPtr.
+ *
+ * Note: currently no Tcl filesystems support the 'link' action,
+ * so we actually always return an error for that call.
*
* Side effects:
* See readlink() documentation.
@@ -2300,14 +2362,15 @@ FSUnloadTempFile(clientData)
*/
Tcl_Obj *
-Tcl_FSReadlink(pathPtr)
- Tcl_Obj *pathPtr; /* Path of file to readlink (UTF-8). */
+Tcl_FSLink(pathPtr, toPtr)
+ Tcl_Obj *pathPtr; /* Path of file to readlink or link */
+ Tcl_Obj *toPtr; /* NULL or path to be linked to */
{
Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
if (fsPtr != NULL) {
- Tcl_FSReadlinkProc *proc = fsPtr->readlinkProc;
+ Tcl_FSLinkProc *proc = fsPtr->linkProc;
if (proc != NULL) {
- return (*proc)(pathPtr);
+ return (*proc)(pathPtr, toPtr);
}
}
/*
@@ -2328,15 +2391,13 @@ Tcl_FSReadlink(pathPtr)
*
* Tcl_FSListVolumes --
*
- * Lists the currently mounted volumes.
- * The chain of functions that have been "inserted" into the
- * filesystem will be called in succession; each may add to
- * the Tcl result, until all mounted file systems are listed.
+ * Lists the currently mounted volumes. The chain of functions
+ * that have been "inserted" into the filesystem will be called in
+ * succession; each may return a list of volumes, all of which are
+ * added to the result until all mounted file systems are listed.
*
* Results:
- * A standard Tcl result. Will always be TCL_OK, since there is no way
- * that this command can fail. Also, the interpreter's result is set to
- * the list of volumes.
+ * The list of volumes, in an object which has refCount 0.
*
* Side effects:
* None
@@ -2344,12 +2405,12 @@ Tcl_FSReadlink(pathPtr)
*---------------------------------------------------------------------------
*/
-int
-Tcl_FSListVolumes(interp)
- Tcl_Interp *interp; /* Interpreter for returning volume list. */
+Tcl_Obj*
+Tcl_FSListVolumes(void)
{
FilesystemRecord *fsRecPtr;
-
+ Tcl_Obj *resultPtr = Tcl_NewObj();
+
/*
* Call each of the "listVolumes" function in succession.
* A non-NULL return value indicates the particular function has
@@ -2361,14 +2422,407 @@ Tcl_FSListVolumes(interp)
while (fsRecPtr != NULL) {
Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc;
if (proc != NULL) {
- /* Ignore return value */
- (*proc)(interp);
+ Tcl_Obj *thisFsVolumes = (*proc)();
+ if (thisFsVolumes != NULL) {
+ Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes);
+ Tcl_DecrRefCount(thisFsVolumes);
+ }
}
fsRecPtr = fsRecPtr->nextPtr;
}
FsReleaseIterator();
- return TCL_OK;
+ return resultPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSGetPathType --
+ *
+ * Determines whether a given path is relative to the current
+ * directory, relative to the current volume, or absolute. If the
+ * caller wishes to know which filesystem claimed the path (in the
+ * case for which the path is absolute), then a reference to a
+ * filesystem pointer can be passed in (but passing NULL is
+ * acceptable).
+ *
+ * Results:
+ * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
+ * TCL_PATH_VOLUME_RELATIVE. The filesystem reference will
+ * be set if and only if it is non-NULL and the function's
+ * return value is TCL_PATH_ABSOLUTE.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_PathType
+Tcl_FSGetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr)
+ Tcl_Obj *pathObjPtr;
+ Tcl_Filesystem **filesystemPtrPtr;
+ int *driveNameLengthPtr;
+{
+ if (Tcl_FSConvertToPathType(NULL, pathObjPtr) != TCL_OK) {
+ return GetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr, NULL);
+ } else {
+ FsPath *fsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+ if (fsPathPtr->cwdPtr != NULL) {
+ return TCL_PATH_RELATIVE;
+ } else {
+ return GetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr, NULL);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSSplitPath --
+ *
+ * This function takes the given Tcl_Obj, which should be a valid
+ * path, and returns a Tcl List object containing each segment
+ * of that path as an element.
+ *
+ * Note this function currently calls the older Split(Plat)Path
+ * functions, which require more memory allocation than is
+ * desirable.
+ *
+ * Results:
+ * Returns list object with refCount of zero. If the passed in
+ * lenPtr is non-NULL, we use it to return the number of elements
+ * in the returned list.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+Tcl_FSSplitPath(pathPtr, lenPtr)
+ Tcl_Obj *pathPtr; /* Path to split. */
+ int *lenPtr; /* int to store number of path elements. */
+{
+ Tcl_Obj *result = NULL; /* Needed only to prevent gcc warnings. */
+ Tcl_Filesystem *fsPtr;
+ char separator = '/';
+ int driveNameLength;
+ char *p;
+
+ /*
+ * Perform platform specific splitting.
+ */
+
+ if (Tcl_FSGetPathType(pathPtr, &fsPtr, &driveNameLength)
+ == TCL_PATH_ABSOLUTE) {
+ if (fsPtr == &nativeFilesystem) {
+ return TclpNativeSplitPath(pathPtr, lenPtr);
+ }
+ } else {
+ return TclpNativeSplitPath(pathPtr, lenPtr);
+ }
+
+ /* We assume separators are single characters */
+ if (fsPtr->filesystemSeparatorProc != NULL) {
+ Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(pathPtr);
+ if (sep != NULL) {
+ separator = Tcl_GetString(sep)[0];
+ }
+ }
+
+ /*
+ * Place the drive name as first element of the
+ * result list. The drive name may contain strange
+ * characters, like colons and multiple forward slashes
+ * (for example 'ftp://' is a valid vfs drive name)
+ */
+ result = Tcl_NewObj();
+ p = Tcl_GetString(pathPtr);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(p, driveNameLength));
+ p+= driveNameLength;
+
+ /* Add the remaining path elements to the list */
+ for (;;) {
+ char *elementStart = p;
+ int length;
+ while ((*p != '\0') && (*p != separator)) {
+ p++;
+ }
+ length = p - elementStart;
+ if (length > 0) {
+ Tcl_Obj *nextElt;
+ if (elementStart[0] == '~') {
+ nextElt = Tcl_NewStringObj("./",2);
+ Tcl_AppendToObj(nextElt, elementStart, length);
+ } else {
+ nextElt = Tcl_NewStringObj(elementStart, length);
+ }
+ Tcl_ListObjAppendElement(NULL, result, nextElt);
+ }
+ if (*p++ == '\0') {
+ break;
+ }
+ }
+
+ /*
+ * Compute the number of elements in the result.
+ */
+
+ if (lenPtr != NULL) {
+ Tcl_ListObjLength(NULL, result, lenPtr);
+ }
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSJoinPath --
+ *
+ * This function takes the given Tcl_Obj, which should be a valid
+ * list, and returns the path object given by considering the
+ * first 'elements' elements as valid path segments. If elements < 0,
+ * we use the entire list.
+ *
+ * Note this function currently calls the older Tcl_JoinPath
+ * routine, which therefore requires more memory allocation and
+ * deallocation than necessary. We could easily rewrite this for
+ * greater efficiency.
+ *
+ * Results:
+ * Returns object with refCount of zero.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tcl_Obj*
+Tcl_FSJoinPath(listObj, elements)
+ Tcl_Obj *listObj;
+ int elements;
+{
+ Tcl_Obj *res;
+ int i;
+ Tcl_Filesystem *fsPtr = NULL;
+
+ if (elements < 0) {
+ if (Tcl_ListObjLength(NULL, listObj, &elements) != TCL_OK) {
+ return NULL;
+ }
+ } else {
+ /* Just make sure it is a valid list */
+ int listTest;
+ if (Tcl_ListObjLength(NULL, listObj, &listTest) != TCL_OK) {
+ return NULL;
+ }
+ /*
+ * Correct this if it is too large, otherwise we will
+ * waste our timing joining null elements to the path
+ */
+ if (elements > listTest) {
+ elements = listTest;
+ }
+ }
+
+ res = Tcl_NewObj();
+
+ for (i = 0; i < elements; i++) {
+ Tcl_Obj *elt;
+ int driveNameLength;
+ Tcl_PathType type;
+ char *strElt;
+ Tcl_Obj *driveName = NULL;
+
+ Tcl_ListObjIndex(NULL, listObj, i, &elt);
+ strElt = Tcl_GetString(elt);
+ type = GetPathType(elt, &fsPtr, &driveNameLength, &driveName);
+ if (type != TCL_PATH_RELATIVE) {
+ /* Zero out the current result */
+ Tcl_DecrRefCount(res);
+ if (driveName != NULL) {
+ res = Tcl_DuplicateObj(driveName);
+ Tcl_DecrRefCount(driveName);
+ } else {
+ res = Tcl_NewStringObj(strElt, driveNameLength);
+ }
+ strElt += driveNameLength;
+ }
+
+ /*
+ * A NULL value for fsPtr at this stage basically means
+ * we're trying to join a relative path onto something
+ * which is also relative (or empty). There's nothing
+ * particularly wrong with that.
+ */
+ if (*strElt == '\0') continue;
+
+ if (fsPtr == &nativeFilesystem || fsPtr == NULL) {
+ TclpNativeJoinPath(res, strElt);
+ } else {
+ int length;
+ char separator = '/';
+ char *ptr;
+ int needsSep = 0;
+
+ if (fsPtr->filesystemSeparatorProc != NULL) {
+ Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(res);
+ if (sep != NULL) {
+ separator = Tcl_GetString(sep)[0];
+ }
+ }
+ ptr = Tcl_GetStringFromObj(res, &length);
+ if (length > 0 && ptr[length -1] != '/') {
+ Tcl_AppendToObj(res, &separator, 1);
+ length++;
+ }
+ Tcl_SetObjLength(res, length + strlen(strElt));
+
+ ptr = Tcl_GetString(res) + length;
+ for (; *strElt != '\0'; strElt++) {
+ if (*strElt == separator) {
+ while (strElt[1] == separator) {
+ strElt++;
+ }
+ if (strElt[1] != '\0') {
+ if (needsSep) {
+ *ptr++ = separator;
+ }
+ }
+ } else {
+ *ptr++ = *strElt;
+ needsSep = 1;
+ }
+ }
+ length = ptr - Tcl_GetString(res);
+ Tcl_SetObjLength(res, length);
+ }
+ }
+ return res;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetPathType --
+ *
+ * Helper function used by Tcl_FSGetPathType.
+ *
+ * Results:
+ * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
+ * TCL_PATH_VOLUME_RELATIVE. The filesystem reference will
+ * be set if and only if it is non-NULL and the function's
+ * return value is TCL_PATH_ABSOLUTE.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_PathType
+GetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr, driveNameRef)
+ Tcl_Obj *pathObjPtr;
+ Tcl_Filesystem **filesystemPtrPtr;
+ int *driveNameLengthPtr;
+ Tcl_Obj **driveNameRef;
+{
+ FilesystemRecord *fsRecPtr;
+ int pathLen;
+ char *path;
+ Tcl_PathType type = TCL_PATH_RELATIVE;
+
+ path = Tcl_GetStringFromObj(pathObjPtr, &pathLen);
+
+ /*
+ * Call each of the "listVolumes" function in succession, checking
+ * whether the given path is an absolute path on any of the volumes
+ * returned (this is done by checking whether the path's prefix
+ * matches).
+ */
+
+ fsRecPtr = FsGetIterator();
+ while (fsRecPtr != NULL) {
+ Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc;
+ /*
+ * We want to skip the native filesystem in this loop because
+ * otherwise we won't necessarily pass all the Tcl testsuite --
+ * this is because some of the tests artificially change the
+ * current platform (between mac, win, unix) but the list
+ * of volumes we get by calling (*proc) will reflect the current
+ * (real) platform only and this may cause some tests to fail.
+ * In particular, on unix '/' will match the beginning of
+ * certain absolute Windows paths starting '//' and those tests
+ * will go wrong.
+ *
+ * Besides these test-suite issues, there is actually no
+ * reason to skip the native filesystem.
+ */
+ if ((fsRecPtr->fsPtr != &nativeFilesystem) && (proc != NULL)) {
+ int numVolumes;
+ Tcl_Obj *thisFsVolumes = (*proc)();
+ if (thisFsVolumes != NULL) {
+ if (Tcl_ListObjLength(NULL, thisFsVolumes,
+ &numVolumes) != TCL_OK) {
+ /*
+ * This is VERY bad; Tcl_FSListVolumes didn't
+ * return a valid list. Set numVolumes to -1
+ * so that we skip the while loop below and
+ * just return with the current value of 'type'.
+ *
+ * It would be better if we could signal an error
+ * here (but panic seems a bit excessive).
+ */
+ numVolumes = -1;
+ }
+ while (numVolumes > 0) {
+ Tcl_Obj *vol;
+ int len;
+ char *strVol;
+
+ numVolumes--;
+ Tcl_ListObjIndex(NULL, thisFsVolumes, numVolumes, &vol);
+ strVol = Tcl_GetStringFromObj(vol,&len);
+ if (pathLen < len) {
+ continue;
+ }
+ if (strncmp(strVol, path, len) == 0) {
+ type = TCL_PATH_ABSOLUTE;
+ if (filesystemPtrPtr != NULL) {
+ *filesystemPtrPtr = fsRecPtr->fsPtr;
+ }
+ if (driveNameLengthPtr != NULL) {
+ *driveNameLengthPtr = len;
+ }
+ if (driveNameRef != NULL) {
+ *driveNameRef = vol;
+ Tcl_IncrRefCount(vol);
+ }
+ break;
+ }
+ }
+ Tcl_DecrRefCount(thisFsVolumes);
+ if (type == TCL_PATH_ABSOLUTE) {
+ /* We don't need to examine any more filesystems */
+ break;
+ }
+ }
+ }
+ fsRecPtr = fsRecPtr->nextPtr;
+ }
+ FsReleaseIterator();
+
+ if (type != TCL_PATH_ABSOLUTE) {
+ type = TclpGetNativePathType(pathObjPtr, driveNameLengthPtr, driveNameRef);
+ if ((type == TCL_PATH_ABSOLUTE) && (filesystemPtrPtr != NULL)) {
+ *filesystemPtrPtr = &nativeFilesystem;
+ }
+ }
+ return type;
}
/*
@@ -2769,7 +3223,7 @@ SetFsPathFromAny(interp, objPtr)
{
int len;
FsPath *fsPathPtr;
- Tcl_DString buffer;
+ Tcl_Obj *transPtr;
char *name;
if (objPtr->typePtr == &tclFsPathType) {
@@ -2813,7 +3267,7 @@ SetFsPathFromAny(interp, objPtr)
char separator='/';
if (tclPlatform==TCL_PLATFORM_MAC) {
- if (strchr(name, ':') != NULL) separator = ':';
+ if (strchr(name, ':') != NULL) separator = ':';
}
split = FindSplitPos(name, &separator);
@@ -2855,40 +3309,31 @@ SetFsPathFromAny(interp, objPtr)
}
if (split != len) { name[split] = separator; }
}
+
expandedUser = Tcl_DStringValue(&temp);
+ transPtr = Tcl_NewStringObj(expandedUser, Tcl_DStringLength(&temp));
- Tcl_DStringInit(&buffer);
- if (split == len) {
- /* We have the result we need in the wrong DString */
- Tcl_DStringAppend(&buffer, expandedUser, Tcl_DStringLength(&temp));
- } else {
+ if (split != len) {
/*
- * Build a simple 2 element list and join it up with
- * the tilde substitution in place
+ * Join up the tilde substitution with the rest
*/
- char *argv[2];
- argv[0] = expandedUser;
- argv[1] = name+split+1;
- Tcl_JoinPath(2, argv, &buffer);
+ Tcl_Obj *rest = Tcl_NewStringObj(name+split+1,-1);
+ transPtr = Tcl_FSJoinToPath(transPtr, 1, &rest);
}
Tcl_DStringFree(&temp);
} else {
- Tcl_DStringInit(&buffer);
- Tcl_JoinPath(1, &name, &buffer);
+ transPtr = Tcl_FSJoinToPath(objPtr,0,NULL);
}
- len = Tcl_DStringLength(&buffer);
-
/*
- * Now we have a translated filename in 'buffer', of
- * length 'len'. This will have forward slashes on
- * Windows, and will not contain any ~user sequences.
+ * Now we have a translated filename in 'transPtr'. This will have
+ * forward slashes on Windows, and will not contain any ~user
+ * sequences.
*/
fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
- fsPathPtr->translatedPathPtr = ckalloc((unsigned)(1+len));
- strcpy(fsPathPtr->translatedPathPtr, Tcl_DStringValue(&buffer));
- Tcl_DStringFree(&buffer);
+ fsPathPtr->translatedPathPtr = transPtr;
+ Tcl_IncrRefCount(fsPathPtr->translatedPathPtr);
fsPathPtr->normPathPtr = NULL;
fsPathPtr->cwdPtr = NULL;
fsPathPtr->nativePathPtr = NULL;
@@ -2983,7 +3428,7 @@ FreeFsPathInternalRep(pathObjPtr)
(FsPath*) pathObjPtr->internalRep.otherValuePtr;
if (fsPathPtr->translatedPathPtr != NULL) {
- ckfree((char *) fsPathPtr->translatedPathPtr);
+ Tcl_DecrRefCount(fsPathPtr->translatedPathPtr);
}
if (fsPathPtr->normPathPtr != NULL) {
if (fsPathPtr->normPathPtr != pathObjPtr) {
@@ -3024,10 +3469,8 @@ DupFsPathInternalRep(srcPtr, copyPtr)
copyPtr->internalRep.otherValuePtr = copyFsPathPtr;
if (srcFsPathPtr->translatedPathPtr != NULL) {
- copyFsPathPtr->translatedPathPtr =
- ckalloc(1+strlen(srcFsPathPtr->translatedPathPtr));
- strcpy(copyFsPathPtr->translatedPathPtr,
- srcFsPathPtr->translatedPathPtr);
+ copyFsPathPtr->translatedPathPtr = srcFsPathPtr->translatedPathPtr;
+ Tcl_IncrRefCount(copyFsPathPtr->translatedPathPtr);
} else {
copyFsPathPtr->translatedPathPtr = NULL;
}
@@ -3074,14 +3517,14 @@ DupFsPathInternalRep(srcPtr, copyPtr)
*
* Tcl_FSGetTranslatedPath --
*
- * This function attempts to extract the translated path string
+ * This function attempts to extract the translated path
* from the given Tcl_Obj. If the translation succeeds (i.e. the
* object is a valid path), then it is returned. Otherwise NULL
* will be returned, and an error message may be left in the
- * interpreter.
+ * interpreter (if it is non-NULL)
*
* Results:
- * NULL or a valid string.
+ * NULL or a valid Tcl_Obj pointer.
*
* Side effects:
* Only those of 'Tcl_FSConvertToPathType'
@@ -3089,7 +3532,7 @@ DupFsPathInternalRep(srcPtr, copyPtr)
*---------------------------------------------------------------------------
*/
-char*
+Tcl_Obj*
Tcl_FSGetTranslatedPath(interp, pathPtr)
Tcl_Interp *interp;
Tcl_Obj* pathPtr;
@@ -3106,7 +3549,7 @@ Tcl_FSGetTranslatedPath(interp, pathPtr)
* object's string, translatedPath and normalizedPath
* are all identical.
*/
- return Tcl_GetString(srcFsPathPtr->normPathPtr);
+ return srcFsPathPtr->normPathPtr;
} else {
/* It is an ordinary path object */
return srcFsPathPtr->translatedPathPtr;
@@ -3116,6 +3559,38 @@ Tcl_FSGetTranslatedPath(interp, pathPtr)
/*
*---------------------------------------------------------------------------
*
+ * Tcl_FSGetTranslatedStringPath --
+ *
+ * This function attempts to extract the translated path
+ * from the given Tcl_Obj. If the translation succeeds (i.e. the
+ * object is a valid path), then the path is returned. Otherwise NULL
+ * will be returned, and an error message may be left in the
+ * interpreter (if it is non-NULL)
+ *
+ * Results:
+ * NULL or a valid string.
+ *
+ * Side effects:
+ * Only those of 'Tcl_FSConvertToPathType'
+ *
+ *---------------------------------------------------------------------------
+ */
+char*
+Tcl_FSGetTranslatedStringPath(interp, pathPtr)
+Tcl_Interp *interp;
+Tcl_Obj* pathPtr;
+{
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
+ if (transPtr == NULL) {
+ return NULL;
+ } else {
+ return Tcl_GetString(transPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
* Tcl_FSGetNormalizedPath --
*
* This important function attempts to extract from the given Tcl_Obj
@@ -3144,34 +3619,35 @@ Tcl_FSGetNormalizedPath(interp, pathObjPtr)
srcFsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
if (srcFsPathPtr->normPathPtr == NULL) {
int relative = 0;
- char *path = srcFsPathPtr->translatedPathPtr;
- Tcl_DString atemp;
+ /*
+ * Since normPathPtr is NULL, but this is a valid path
+ * object, we know that the translatedPathPtr cannot be NULL.
+ */
+ Tcl_Obj *absolutePath = srcFsPathPtr->translatedPathPtr;
+ char *path = Tcl_GetString(absolutePath);
- if ((path[0] != '\0') && (Tcl_GetPathType(path) == TCL_PATH_RELATIVE)) {
- char * pair[2];
+ /*
+ * We have to be a little bit careful here to avoid infinite loops
+ * we're asking Tcl_FSGetPathType to return the path's type, but
+ * that call can actually result in a lot of other filesystem
+ * action, which might loop back through here.
+ */
+ if ((path[0] != '\0') &&
+ (Tcl_FSGetPathType(pathObjPtr, NULL, NULL) == TCL_PATH_RELATIVE)) {
Tcl_Obj *cwd = Tcl_FSGetCwd(interp);
if (cwd == NULL) {
return NULL;
}
-
- /*
- * The efficiency of this piece of code could
- * be improved, given the new object interfaces.
- */
- pair[0] = Tcl_GetString(cwd);
- pair[1] = path;
- Tcl_DStringInit(&atemp);
- Tcl_JoinPath(2, pair, &atemp);
- path = Tcl_DStringValue(&atemp);
+ absolutePath = Tcl_FSJoinToPath(cwd, 1, &absolutePath);
+ Tcl_IncrRefCount(absolutePath);
Tcl_DecrRefCount(cwd);
relative = 1;
}
-
/* Already has refCount incremented */
- srcFsPathPtr->normPathPtr = FSNormalizeAbsolutePath(interp, path);
+ srcFsPathPtr->normPathPtr = FSNormalizeAbsolutePath(interp, absolutePath);
if (!strcmp(Tcl_GetString(srcFsPathPtr->normPathPtr),
Tcl_GetString(pathObjPtr))) {
/*
@@ -3186,7 +3662,8 @@ Tcl_FSGetNormalizedPath(interp, pathObjPtr)
srcFsPathPtr->normPathPtr = pathObjPtr;
}
if (relative) {
- Tcl_DStringFree(&atemp);
+ /* This was returned by Tcl_FSJoinToPath above */
+ Tcl_DecrRefCount(absolutePath);
/* Get a quick, temporary lock on the cwd while we copy it */
Tcl_MutexLock(&cwdMutex);
@@ -3330,7 +3807,7 @@ Tcl_FSGetNativePath(pathObjPtr)
*
*---------------------------------------------------------------------------
*/
-ClientData
+static ClientData
NativeCreateNativeRep(pathObjPtr)
Tcl_Obj* pathObjPtr;
{
@@ -3411,7 +3888,7 @@ TclpNativeToNormalized(clientData)
*
*---------------------------------------------------------------------------
*/
-ClientData
+static ClientData
NativeDupInternalRep(clientData)
ClientData clientData;
{
@@ -3447,7 +3924,7 @@ NativeDupInternalRep(clientData)
*
*---------------------------------------------------------------------------
*/
-int
+static int
NativePathInFilesystem(pathPtr, clientDataPtr)
Tcl_Obj *pathPtr;
ClientData *clientDataPtr;
@@ -3477,7 +3954,7 @@ NativePathInFilesystem(pathPtr, clientDataPtr)
*
*---------------------------------------------------------------------------
*/
-void
+static void
NativeFreeInternalRep(clientData)
ClientData clientData;
{
@@ -3580,7 +4057,7 @@ Tcl_FSPathSeparator(pathObjPtr)
*
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
+static Tcl_Obj*
NativeFilesystemSeparator(pathObjPtr)
Tcl_Obj* pathObjPtr;
{
@@ -3617,7 +4094,7 @@ NativeFilesystemSeparator(pathObjPtr)
*
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
+static Tcl_Obj*
NativeFilesystemPathType(pathObjPtr)
Tcl_Obj* pathObjPtr;
{
@@ -3787,18 +4264,18 @@ Tcl_FSEqualPaths(firstPtr, secondPtr)
/* Wrappers */
-Tcl_Channel
+static Tcl_Channel
NativeOpenFileChannel(interp, pathPtr, modeString, permissions)
Tcl_Interp *interp;
Tcl_Obj *pathPtr;
char *modeString;
int permissions;
{
- char *trans = Tcl_FSGetTranslatedPath(interp, pathPtr);
+ Tcl_Obj *trans = Tcl_FSGetTranslatedPath(interp, pathPtr);
if (trans == NULL) {
return NULL;
}
- return TclpOpenFileChannel(interp, trans, modeString, permissions);
+ return TclpOpenFileChannel(interp, Tcl_GetString(trans), modeString, permissions);
}
/*
@@ -3811,7 +4288,7 @@ NativeOpenFileChannel(interp, pathPtr, modeString, permissions)
* This seems rather strange when compared with stat, lstat, access, etc.
* all of which want a native path.
*/
-int
+static int
NativeUtime(pathPtr, tval)
Tcl_Obj *pathPtr;
struct utimbuf *tval;
@@ -3827,7 +4304,7 @@ NativeUtime(pathPtr, tval)
#endif
}
-int
+static int
NativeLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr, clientDataPtr)
Tcl_Interp * interp;
Tcl_Obj *pathPtr;
@@ -3837,7 +4314,14 @@ NativeLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr, clientDataPtr)
Tcl_PackageInitProc ** proc2Ptr;
ClientData * clientDataPtr;
{
- return TclpLoadFile(interp, Tcl_FSGetTranslatedPath(NULL, pathPtr),
+ char *path;
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
+ if (transPtr == NULL) {
+ path = NULL;
+ } else {
+ path = Tcl_GetString(transPtr);
+ }
+ return TclpLoadFile(interp, path,
sym1, sym2, proc1Ptr, proc2Ptr, clientDataPtr);
}