summaryrefslogtreecommitdiffstats
path: root/generic/tclIOUtil.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclIOUtil.c')
-rw-r--r--generic/tclIOUtil.c3994
1 files changed, 1930 insertions, 2064 deletions
diff --git a/generic/tclIOUtil.c b/generic/tclIOUtil.c
index 43b8b31..f624cb7 100644
--- a/generic/tclIOUtil.c
+++ b/generic/tclIOUtil.c
@@ -5,7 +5,7 @@
* code, which supports a pluggable filesystem architecture allowing both
* platform specific filesystems and 'virtual filesystems'. All
* filesystem access should go through the functions defined in this
- * file. Most of this code was contributed by Vince Darley.
+ * file. Most of this code was contributed by Vince Darley.
*
* Parts of this file are based on code contributed by Karl Lehenbauer,
* Mark Diekhans and Peter da Silva.
@@ -16,55 +16,248 @@
*
* 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.121 2005/07/17 22:04:50 dkf Exp $
*/
#include "tclInt.h"
-#ifdef __WIN32__
+#ifdef _WIN32
# include "tclWinInt.h"
#endif
#include "tclFileSystem.h"
/*
- * Prototypes for procedures defined later in this file.
+ * struct FilesystemRecord --
+ *
+ * A filesystem record is used to keep track of each filesystem currently
+ * registered with the core, in a linked list.
*/
-static FilesystemRecord * FsGetFirstFilesystem _ANSI_ARGS_((void));
-static void FsThrExitProc _ANSI_ARGS_((ClientData cd));
-static Tcl_Obj* FsListMounts _ANSI_ARGS_((Tcl_Obj *pathPtr,
- CONST char *pattern));
-static void FsAddMountsToGlobResult _ANSI_ARGS_((
- Tcl_Obj *resultPtr, Tcl_Obj *pathPtr,
- CONST char *pattern,
- Tcl_GlobTypeData *types));
-static void FsUpdateCwd _ANSI_ARGS_((Tcl_Obj *cwdObj,
- ClientData clientData));
-
-#ifdef TCL_THREADS
-static void FsRecacheFilesystemList(void);
-#endif
+typedef struct FilesystemRecord {
+ ClientData clientData; /* Client specific data for the new filesystem
+ * (can be NULL) */
+ const Tcl_Filesystem *fsPtr;/* Pointer to filesystem dispatch table. */
+ struct FilesystemRecord *nextPtr;
+ /* The next filesystem registered to Tcl, or
+ * NULL if no more. */
+ struct FilesystemRecord *prevPtr;
+ /* The previous filesystem registered to Tcl,
+ * or NULL if no more. */
+} FilesystemRecord;
+
+/*
+ * This structure holds per-thread private copy of the current directory
+ * maintained by the global cwdPathPtr. This structure holds per-thread
+ * private copies of some global data. This way we avoid most of the
+ * synchronization calls which boosts performance, at cost of having to update
+ * this information each time the corresponding epoch counter changes.
+ */
+
+typedef struct ThreadSpecificData {
+ int initialized;
+ int cwdPathEpoch;
+ int filesystemEpoch;
+ Tcl_Obj *cwdPathPtr;
+ ClientData cwdClientData;
+ FilesystemRecord *filesystemList;
+ int claims;
+} ThreadSpecificData;
+
+/*
+ * Prototypes for functions defined later in this file.
+ */
+
+static int EvalFileCallback(ClientData data[],
+ Tcl_Interp *interp, int result);
+static FilesystemRecord*FsGetFirstFilesystem(void);
+static void FsThrExitProc(ClientData cd);
+static Tcl_Obj * FsListMounts(Tcl_Obj *pathPtr, const char *pattern);
+static void FsAddMountsToGlobResult(Tcl_Obj *resultPtr,
+ Tcl_Obj *pathPtr, const char *pattern,
+ Tcl_GlobTypeData *types);
+static void FsUpdateCwd(Tcl_Obj *cwdObj, ClientData clientData);
+static void FsRecacheFilesystemList(void);
+static void Claim(void);
+static void Disclaim(void);
+
+static void * DivertFindSymbol(Tcl_Interp *interp,
+ Tcl_LoadHandle loadHandle, const char *symbol);
+static void DivertUnloadFile(Tcl_LoadHandle loadHandle);
/*
- * These form part of the native filesystem support. They are needed here
+ * These form part of the native filesystem support. They are needed here
* because we have a few native filesystem functions (which are the same for
- * win/unix) in this file. There is no need to place them in tclInt.h,
- * because they are not (and should not be) used anywhere else.
+ * win/unix) in this file. There is no need to place them in tclInt.h, because
+ * they are not (and should not be) used anywhere else.
+ */
+
+MODULE_SCOPE const char *const tclpFileAttrStrings[];
+MODULE_SCOPE const TclFileAttrProcs tclpFileAttrProcs[];
+
+/*
+ * Declare the native filesystem support. These functions should be considered
+ * private to Tcl, and should really not be called directly by any code other
+ * than this file (i.e. neither by Tcl's core nor by extensions). Similarly,
+ * the old string-based Tclp... native filesystem functions should not be
+ * called.
+ *
+ * The correct API to use now is the Tcl_FS... set of functions, which ensure
+ * correct and complete virtual filesystem support.
+ *
+ * We cannot make all of these static, since some of them are implemented in
+ * the platform-specific directories.
+ */
+
+static Tcl_FSFilesystemSeparatorProc NativeFilesystemSeparator;
+static Tcl_FSFreeInternalRepProc NativeFreeInternalRep;
+static Tcl_FSFileAttrStringsProc NativeFileAttrStrings;
+static Tcl_FSFileAttrsGetProc NativeFileAttrsGet;
+static Tcl_FSFileAttrsSetProc NativeFileAttrsSet;
+
+/*
+ * The only reason these functions are not static is that they are either
+ * called by code in the native (win/unix) directories or they are actually
+ * implemented in those directories. They should simply not be called by code
+ * outside Tcl's native filesystem core i.e. they should be considered
+ * 'static' to Tcl's filesystem code (if we ever built the native filesystem
+ * support into a separate code library, this could actually be enforced).
+ */
+
+Tcl_FSFilesystemPathTypeProc TclpFilesystemPathType;
+Tcl_FSInternalToNormalizedProc TclpNativeToNormalized;
+Tcl_FSStatProc TclpObjStat;
+Tcl_FSAccessProc TclpObjAccess;
+Tcl_FSMatchInDirectoryProc TclpMatchInDirectory;
+Tcl_FSChdirProc TclpObjChdir;
+Tcl_FSLstatProc TclpObjLstat;
+Tcl_FSCopyFileProc TclpObjCopyFile;
+Tcl_FSDeleteFileProc TclpObjDeleteFile;
+Tcl_FSRenameFileProc TclpObjRenameFile;
+Tcl_FSCreateDirectoryProc TclpObjCreateDirectory;
+Tcl_FSCopyDirectoryProc TclpObjCopyDirectory;
+Tcl_FSRemoveDirectoryProc TclpObjRemoveDirectory;
+Tcl_FSUnloadFileProc TclpUnloadFile;
+Tcl_FSLinkProc TclpObjLink;
+Tcl_FSListVolumesProc TclpObjListVolumes;
+
+/*
+ * Define the native filesystem dispatch table. If necessary, it is ok to make
+ * this non-static, but it should only be accessed by the functions actually
+ * listed within it (or perhaps other helper functions of them). Anything
+ * which is not part of this 'native filesystem implementation' should not be
+ * delving inside here!
+ */
+
+const Tcl_Filesystem tclNativeFilesystem = {
+ "native",
+ sizeof(Tcl_Filesystem),
+ TCL_FILESYSTEM_VERSION_2,
+ TclNativePathInFilesystem,
+ TclNativeDupInternalRep,
+ NativeFreeInternalRep,
+ TclpNativeToNormalized,
+ TclNativeCreateNativeRep,
+ TclpObjNormalizePath,
+ TclpFilesystemPathType,
+ NativeFilesystemSeparator,
+ TclpObjStat,
+ TclpObjAccess,
+ TclpOpenFileChannel,
+ TclpMatchInDirectory,
+ TclpUtime,
+#ifndef S_IFLNK
+ NULL,
+#else
+ TclpObjLink,
+#endif /* S_IFLNK */
+ TclpObjListVolumes,
+ NativeFileAttrStrings,
+ NativeFileAttrsGet,
+ NativeFileAttrsSet,
+ TclpObjCreateDirectory,
+ TclpObjRemoveDirectory,
+ TclpObjDeleteFile,
+ TclpObjCopyFile,
+ TclpObjRenameFile,
+ TclpObjCopyDirectory,
+ TclpObjLstat,
+ /* Needs casts since we're using version_2. */
+ (Tcl_FSLoadFileProc *) TclpDlopen,
+ (Tcl_FSGetCwdProc *) TclpGetNativeCwd,
+ TclpObjChdir
+};
+
+/*
+ * Define the tail of the linked list. Note that for unconventional uses of
+ * Tcl without a native filesystem, we may in the future wish to modify the
+ * current approach of hard-coding the native filesystem in the lookup list
+ * 'filesystemList' below.
+ *
+ * We initialize the record so that it thinks one file uses it. This means it
+ * will never be freed.
+ */
+
+static FilesystemRecord nativeFilesystemRecord = {
+ NULL,
+ &tclNativeFilesystem,
+ NULL,
+ NULL
+};
+
+/*
+ * This is incremented each time we modify the linked list of filesystems. Any
+ * time it changes, all cached filesystem representations are suspect and must
+ * be freed. For multithreading builds, change of the filesystem epoch will
+ * trigger cache cleanup in all threads.
+ */
+
+static int theFilesystemEpoch = 1;
+
+/*
+ * Stores the linked list of filesystems. A 1:1 copy of this list is also
+ * maintained in the TSD for each thread. This is to avoid synchronization
+ * issues.
+ */
+
+static FilesystemRecord *filesystemList = &nativeFilesystemRecord;
+TCL_DECLARE_MUTEX(filesystemMutex)
+
+/*
+ * Used to implement Tcl_FSGetCwd in a file-system independent way.
*/
-extern CONST char * tclpFileAttrStrings[];
-extern CONST TclFileAttrProcs tclpFileAttrProcs[];
+static Tcl_Obj *cwdPathPtr = NULL;
+static int cwdPathEpoch = 0;
+static ClientData cwdClientData = NULL;
+TCL_DECLARE_MUTEX(cwdMutex)
+
+static Tcl_ThreadDataKey fsDataKey;
/*
+ * One of these structures is used each time we successfully load a file from
+ * a file system by way of making a temporary copy of the file on the native
+ * filesystem. We need to store both the actual unloadProc/clientData
+ * combination which was used, and the original and modified filenames, so
+ * that we can correctly undo the entire operation when we want to unload the
+ * code.
+ */
+
+typedef struct FsDivertLoad {
+ Tcl_LoadHandle loadHandle;
+ Tcl_FSUnloadFileProc *unloadProcPtr;
+ Tcl_Obj *divertedFile;
+ const Tcl_Filesystem *divertedFilesystem;
+ ClientData divertedFileNativeRep;
+} FsDivertLoad;
+
+/*
* The following functions are obsolete string based APIs, and should be
* removed in a future release (Tcl 9 would be a good time).
*/
-
+
/* Obsolete */
int
-Tcl_Stat(path, oldStyleBuf)
- CONST char *path; /* Path of file to stat (in current CP). */
- struct stat *oldStyleBuf; /* Filled with results of stat call. */
+Tcl_Stat(
+ const char *path, /* Path of file to stat (in current CP). */
+ struct stat *oldStyleBuf) /* Filled with results of stat call. */
{
int ret;
Tcl_StatBuf buf;
@@ -75,31 +268,37 @@ Tcl_Stat(path, oldStyleBuf)
Tcl_DecrRefCount(pathPtr);
if (ret != -1) {
#ifndef TCL_WIDE_INT_IS_LONG
-# define OUT_OF_RANGE(x) \
+ Tcl_WideInt tmp1, tmp2, tmp3 = 0;
+
+# define OUT_OF_RANGE(x) \
(((Tcl_WideInt)(x)) < Tcl_LongAsWide(LONG_MIN) || \
((Tcl_WideInt)(x)) > Tcl_LongAsWide(LONG_MAX))
-# define OUT_OF_URANGE(x) \
- (((Tcl_WideUInt)(x)) > (Tcl_WideUInt)ULONG_MAX)
+# define OUT_OF_URANGE(x) \
+ (((Tcl_WideUInt)(x)) > ((Tcl_WideUInt)ULONG_MAX))
/*
* Perform the result-buffer overflow check manually.
*
* Note that ino_t/ino64_t is unsigned...
+ *
+ * Workaround gcc warning of "comparison is always false due to
+ * limited range of data type" by assigning to tmp var of type
+ * Tcl_WideInt.
*/
- if (OUT_OF_URANGE(buf.st_ino) || OUT_OF_RANGE(buf.st_size)
-#ifdef HAVE_ST_BLOCKS
- || OUT_OF_RANGE(buf.st_blocks)
+ tmp1 = (Tcl_WideInt) buf.st_ino;
+ tmp2 = (Tcl_WideInt) buf.st_size;
+#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
+ tmp3 = (Tcl_WideInt) buf.st_blocks;
#endif
- ) {
-#ifdef EFBIG
+
+ if (OUT_OF_URANGE(tmp1) || OUT_OF_RANGE(tmp2) || OUT_OF_RANGE(tmp3)) {
+#if defined(EFBIG)
errno = EFBIG;
-#else
-# ifdef EOVERFLOW
+#elif defined(EOVERFLOW)
errno = EOVERFLOW;
-# else
-# error "What status should be returned for file size out of range?"
-# endif
+#else
+#error "What status should be returned for file size out of range?"
#endif
return -1;
}
@@ -111,7 +310,7 @@ Tcl_Stat(path, oldStyleBuf)
/*
* Copy across all supported fields, with possible type coercions on
* those fields that change between the normal and lf64 versions of
- * the stat structure (on Solaris at least.) This is slow when the
+ * the stat structure (on Solaris at least). This is slow when the
* structure sizes coincide, but that's what you get for using an
* obsolete interface.
*/
@@ -127,9 +326,15 @@ Tcl_Stat(path, oldStyleBuf)
oldStyleBuf->st_atime = buf.st_atime;
oldStyleBuf->st_mtime = buf.st_mtime;
oldStyleBuf->st_ctime = buf.st_ctime;
-#ifdef HAVE_ST_BLOCKS
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
oldStyleBuf->st_blksize = buf.st_blksize;
+#endif
+#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
+#ifdef HAVE_BLKCNT_T
oldStyleBuf->st_blocks = (blkcnt_t) buf.st_blocks;
+#else
+ oldStyleBuf->st_blocks = (unsigned long) buf.st_blocks;
+#endif
#endif
}
return ret;
@@ -137,9 +342,9 @@ Tcl_Stat(path, oldStyleBuf)
/* Obsolete */
int
-Tcl_Access(path, mode)
- CONST char *path; /* Path of file to access (in current CP). */
- int mode; /* Permission setting. */
+Tcl_Access(
+ const char *path, /* Path of file to access (in current CP). */
+ int mode) /* Permission setting. */
{
int ret;
Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1);
@@ -153,13 +358,13 @@ Tcl_Access(path, mode)
/* Obsolete */
Tcl_Channel
-Tcl_OpenFileChannel(interp, path, modeString, permissions)
- Tcl_Interp *interp; /* Interpreter for error reporting; can be
+Tcl_OpenFileChannel(
+ Tcl_Interp *interp, /* Interpreter for error reporting; can be
* NULL. */
- CONST char *path; /* Name of file to open. */
- CONST char *modeString; /* A list of POSIX open modes or a string such
+ const char *path, /* Name of file to open. */
+ const char *modeString, /* A list of POSIX open modes or a string such
* as "rw". */
- int permissions; /* If the open involves creating a file, with
+ int permissions) /* If the open involves creating a file, with
* what modes to create it? */
{
Tcl_Channel ret;
@@ -174,8 +379,8 @@ Tcl_OpenFileChannel(interp, path, modeString, permissions)
/* Obsolete */
int
-Tcl_Chdir(dirName)
- CONST char *dirName;
+Tcl_Chdir(
+ const char *dirName)
{
int ret;
Tcl_Obj *pathPtr = Tcl_NewStringObj(dirName,-1);
@@ -187,31 +392,31 @@ Tcl_Chdir(dirName)
/* Obsolete */
char *
-Tcl_GetCwd(interp, cwdPtr)
- Tcl_Interp *interp;
- Tcl_DString *cwdPtr;
+Tcl_GetCwd(
+ Tcl_Interp *interp,
+ Tcl_DString *cwdPtr)
{
- Tcl_Obj *cwd;
- cwd = Tcl_FSGetCwd(interp);
+ Tcl_Obj *cwd = Tcl_FSGetCwd(interp);
+
if (cwd == NULL) {
return NULL;
- } else {
- Tcl_DStringInit(cwdPtr);
- Tcl_DStringAppend(cwdPtr, Tcl_GetString(cwd), -1);
- Tcl_DecrRefCount(cwd);
- return Tcl_DStringValue(cwdPtr);
}
+ Tcl_DStringInit(cwdPtr);
+ TclDStringAppendObj(cwdPtr, cwd);
+ Tcl_DecrRefCount(cwd);
+ return Tcl_DStringValue(cwdPtr);
}
/* Obsolete */
int
-Tcl_EvalFile(interp, fileName)
- Tcl_Interp *interp; /* Interpreter in which to process file. */
- CONST char *fileName; /* Name of file to process. Tilde-substitution
+Tcl_EvalFile(
+ Tcl_Interp *interp, /* Interpreter in which to process file. */
+ const char *fileName) /* Name of file to process. Tilde-substitution
* will be performed on this name. */
{
int ret;
Tcl_Obj *pathPtr = Tcl_NewStringObj(fileName,-1);
+
Tcl_IncrRefCount(pathPtr);
ret = Tcl_FSEvalFile(interp, pathPtr);
Tcl_DecrRefCount(pathPtr);
@@ -219,239 +424,14 @@ Tcl_EvalFile(interp, fileName)
}
/*
- * The 3 hooks for Stat, Access and OpenFileChannel are obsolete. The
- * complete, general hooked filesystem APIs should be used instead. This
- * define decides whether to include the obsolete hooks and related code. If
- * these are removed, we'll also want to remove them from stubs/tclInt. The
- * only known users of these APIs are prowrap and mktclapp. New
- * code/extensions should not use them, since they do not provide as full
- * support as the full filesystem API.
- *
- * As soon as prowrap and mktclapp are updated to use the full filesystem
- * support, I suggest all these hooks are removed.
- */
-
-#define USE_OBSOLETE_FS_HOOKS
-
-#ifdef USE_OBSOLETE_FS_HOOKS
-
-/*
- * The following typedef declarations allow for hooking into the chain of
- * functions maintained for 'Tcl_Stat(...)', 'Tcl_Access(...)' &
- * 'Tcl_OpenFileChannel(...)'. Basically for each hookable function a linked
- * list is defined.
- */
-
-typedef struct StatProc {
- TclStatProc_ *proc; /* Function to process a 'stat()' call */
- struct StatProc *nextPtr; /* The next 'stat()' function to call */
-} StatProc;
-
-typedef struct AccessProc {
- TclAccessProc_ *proc; /* Function to process a 'access()' call */
- struct AccessProc *nextPtr; /* The next 'access()' function to call */
-} AccessProc;
-
-typedef struct OpenFileChannelProc {
- TclOpenFileChannelProc_ *proc; /* Function to process a
- * 'Tcl_OpenFileChannel()' call */
- struct OpenFileChannelProc *nextPtr;
- /* The next 'Tcl_OpenFileChannel()'
- * function to call */
-} OpenFileChannelProc;
-
-/*
- * For each type of (obsolete) hookable function, a static node is declared to
- * hold the function pointer for the "built-in" routine (e.g. 'TclpStat(...)')
- * and the respective list is initialized as a pointer to that node.
- *
- * The "delete" functions (e.g. 'TclStatDeleteProc(...)') ensure that these
- * statically declared list entry cannot be inadvertently removed.
- *
- * This method avoids the need to call any sort of "initialization" function.
- *
- * All three lists are protected by a global obsoleteFsHookMutex.
- */
-
-static StatProc *statProcList = NULL;
-static AccessProc *accessProcList = NULL;
-static OpenFileChannelProc *openFileChannelProcList = NULL;
-
-TCL_DECLARE_MUTEX(obsoleteFsHookMutex)
-
-#endif /* USE_OBSOLETE_FS_HOOKS */
-
-/*
- * Declare the native filesystem support. These functions should be
- * considered private to Tcl, and should really not be called directly by any
- * code other than this file (i.e. neither by Tcl's core nor by extensions).
- * Similarly, the old string-based Tclp... native filesystem functions should
- * not be called.
- *
- * The correct API to use now is the Tcl_FS... set of functions, which ensure
- * correct and complete virtual filesystem support.
- *
- * We cannot make all of these static, since some of them are implemented in
- * the platform-specific directories.
- */
-
-static Tcl_FSFilesystemSeparatorProc NativeFilesystemSeparator;
-static Tcl_FSFreeInternalRepProc NativeFreeInternalRep;
-static Tcl_FSFileAttrStringsProc NativeFileAttrStrings;
-static Tcl_FSFileAttrsGetProc NativeFileAttrsGet;
-static Tcl_FSFileAttrsSetProc NativeFileAttrsSet;
-
-/*
- * The only reason these functions are not static is that they are either
- * called by code in the native (win/unix) directories or they are actually
- * implemented in those directories. They should simply not be called by code
- * outside Tcl's native filesystem core i.e. they should be considered
- * 'static' to Tcl's filesystem code (if we ever built the native filesystem
- * support into a separate code library, this could actually be enforced).
- */
-
-Tcl_FSFilesystemPathTypeProc TclpFilesystemPathType;
-Tcl_FSInternalToNormalizedProc TclpNativeToNormalized;
-Tcl_FSStatProc TclpObjStat;
-Tcl_FSAccessProc TclpObjAccess;
-Tcl_FSMatchInDirectoryProc TclpMatchInDirectory;
-Tcl_FSChdirProc TclpObjChdir;
-Tcl_FSLstatProc TclpObjLstat;
-Tcl_FSCopyFileProc TclpObjCopyFile;
-Tcl_FSDeleteFileProc TclpObjDeleteFile;
-Tcl_FSRenameFileProc TclpObjRenameFile;
-Tcl_FSCreateDirectoryProc TclpObjCreateDirectory;
-Tcl_FSCopyDirectoryProc TclpObjCopyDirectory;
-Tcl_FSRemoveDirectoryProc TclpObjRemoveDirectory;
-Tcl_FSUnloadFileProc TclpUnloadFile;
-Tcl_FSLinkProc TclpObjLink;
-Tcl_FSListVolumesProc TclpObjListVolumes;
-
-/*
- * Define the native filesystem dispatch table. If necessary, it is ok to
- * make this non-static, but it should only be accessed by the functions
- * actually listed within it (or perhaps other helper functions of them).
- * Anything which is not part of this 'native filesystem implementation'
- * should not be delving inside here!
- */
-
-Tcl_Filesystem tclNativeFilesystem = {
- "native",
- sizeof(Tcl_Filesystem),
- TCL_FILESYSTEM_VERSION_2,
- &TclNativePathInFilesystem,
- &TclNativeDupInternalRep,
- &NativeFreeInternalRep,
- &TclpNativeToNormalized,
- &TclNativeCreateNativeRep,
- &TclpObjNormalizePath,
- &TclpFilesystemPathType,
- &NativeFilesystemSeparator,
- &TclpObjStat,
- &TclpObjAccess,
- &TclpOpenFileChannel,
- &TclpMatchInDirectory,
- &TclpUtime,
-#ifndef S_IFLNK
- NULL,
-#else
- &TclpObjLink,
-#endif /* S_IFLNK */
- &TclpObjListVolumes,
- &NativeFileAttrStrings,
- &NativeFileAttrsGet,
- &NativeFileAttrsSet,
- &TclpObjCreateDirectory,
- &TclpObjRemoveDirectory,
- &TclpObjDeleteFile,
- &TclpObjCopyFile,
- &TclpObjRenameFile,
- &TclpObjCopyDirectory,
- &TclpObjLstat,
- &TclpDlopen,
- /* Needs a cast since we're using version_2 */
- (Tcl_FSGetCwdProc*)&TclpGetNativeCwd,
- &TclpObjChdir
-};
-
-/*
- * Define the tail of the linked list. Note that for unconventional uses of
- * Tcl without a native filesystem, we may in the future wish to modify the
- * current approach of hard-coding the native filesystem in the lookup list
- * 'filesystemList' below.
- *
- * We initialize the record so that it thinks one file uses it. This means it
- * will never be freed.
- */
-
-static FilesystemRecord nativeFilesystemRecord = {
- NULL,
- &tclNativeFilesystem,
- 1,
- NULL
-};
-
-/*
- * This is incremented each time we modify the linked list of filesystems.
- * Any time it changes, all cached filesystem representations are suspect and
- * must be freed. For multithreading builds, change of the filesystem epoch
- * will trigger cache cleanup in all threads.
- */
-
-static int theFilesystemEpoch = 0;
-
-/*
- * Stores the linked list of filesystems. A 1:1 copy of this list is also
- * maintained in the TSD for each thread. This is to avoid synchronization
- * issues.
- */
-
-static FilesystemRecord *filesystemList = &nativeFilesystemRecord;
-TCL_DECLARE_MUTEX(filesystemMutex)
-
-/*
- * Used to implement Tcl_FSGetCwd in a file-system independent way.
- */
-
-static Tcl_Obj* cwdPathPtr = NULL;
-static int cwdPathEpoch = 0;
-static ClientData cwdClientData = NULL;
-TCL_DECLARE_MUTEX(cwdMutex)
-
-Tcl_ThreadDataKey tclFsDataKey;
-
-/*
- * Declare fallback support function and information for Tcl_FSLoadFile
- */
-
-static Tcl_FSUnloadFileProc FSUnloadTempFile;
-
-/*
- * One of these structures is used each time we successfully load a file from
- * a file system by way of making a temporary copy of the file on the native
- * filesystem. We need to store both the actual unloadProc/clientData
- * combination which was used, and the original and modified filenames, so
- * that we can correctly undo the entire operation when we want to unload the
- * code.
- */
-
-typedef struct FsDivertLoad {
- Tcl_LoadHandle loadHandle;
- Tcl_FSUnloadFileProc *unloadProcPtr;
- Tcl_Obj *divertedFile;
- Tcl_Filesystem *divertedFilesystem;
- ClientData divertedFileNativeRep;
-} FsDivertLoad;
-
-/*
- * Now move on to the basic filesystem implementation
+ * Now move on to the basic filesystem implementation.
*/
static void
-FsThrExitProc(cd)
- ClientData cd;
+FsThrExitProc(
+ ClientData cd)
{
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *) cd;
+ ThreadSpecificData *tsdPtr = cd;
FilesystemRecord *fsRecPtr = NULL, *tmpFsRecPtr = NULL;
/*
@@ -460,6 +440,7 @@ FsThrExitProc(cd)
if (tsdPtr->cwdPathPtr != NULL) {
Tcl_DecrRefCount(tsdPtr->cwdPathPtr);
+ tsdPtr->cwdPathPtr = NULL;
}
if (tsdPtr->cwdClientData != NULL) {
NativeFreeInternalRep(tsdPtr->cwdClientData);
@@ -472,17 +453,18 @@ FsThrExitProc(cd)
fsRecPtr = tsdPtr->filesystemList;
while (fsRecPtr != NULL) {
tmpFsRecPtr = fsRecPtr->nextPtr;
- if (--fsRecPtr->fileRefCount <= 0) {
- ckfree((char *)fsRecPtr);
- }
+ fsRecPtr->fsPtr = NULL;
+ ckfree(fsRecPtr);
fsRecPtr = tmpFsRecPtr;
}
+ tsdPtr->filesystemList = NULL;
+ tsdPtr->initialized = 0;
}
int
-TclFSCwdIsNative()
+TclFSCwdIsNative(void)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
if (tsdPtr->cwdClientData != NULL) {
return 1;
@@ -504,7 +486,7 @@ TclFSCwdIsNative()
*
* Side effects:
* If the paths are equal, but are not the same object, this method will
- * modify the given pathPtrPtr to refer to the same object. In this case
+ * modify the given pathPtrPtr to refer to the same object. In this case
* the object pointed to by pathPtrPtr will have its refCount
* decremented, and it will be adjusted to point to the cwd (with a new
* refCount).
@@ -513,10 +495,10 @@ TclFSCwdIsNative()
*/
int
-TclFSCwdPointerEquals(pathPtrPtr)
- Tcl_Obj** pathPtrPtr;
+TclFSCwdPointerEquals(
+ Tcl_Obj **pathPtrPtr)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
Tcl_MutexLock(&cwdMutex);
if (tsdPtr->cwdPathPtr == NULL
@@ -543,7 +525,7 @@ TclFSCwdPointerEquals(pathPtrPtr)
Tcl_MutexUnlock(&cwdMutex);
if (tsdPtr->initialized == 0) {
- Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr);
+ Tcl_CreateThreadExitHandler(FsThrExitProc, tsdPtr);
tsdPtr->initialized = 1;
}
@@ -555,13 +537,13 @@ TclFSCwdPointerEquals(pathPtrPtr)
return 1;
} else {
int len1, len2;
- CONST char *str1, *str2;
+ const char *str1, *str2;
str1 = Tcl_GetStringFromObj(tsdPtr->cwdPathPtr, &len1);
str2 = Tcl_GetStringFromObj(*pathPtrPtr, &len2);
- if (len1 == len2 && !strcmp(str1,str2)) {
+ if ((len1 == len2) && !memcmp(str1, str2, len1)) {
/*
- * They are equal, but different objects. Update so they will be
+ * They are equal, but different objects. Update so they will be
* the same object in the future.
*/
@@ -575,12 +557,11 @@ TclFSCwdPointerEquals(pathPtrPtr)
}
}
-#ifdef TCL_THREADS
static void
FsRecacheFilesystemList(void)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
- FilesystemRecord *fsRecPtr, *tmpFsRecPtr = NULL;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
+ FilesystemRecord *fsRecPtr, *tmpFsRecPtr = NULL, *toFree = NULL, *list;
/*
* Trash the current cache.
@@ -589,20 +570,16 @@ FsRecacheFilesystemList(void)
fsRecPtr = tsdPtr->filesystemList;
while (fsRecPtr != NULL) {
tmpFsRecPtr = fsRecPtr->nextPtr;
- if (--fsRecPtr->fileRefCount <= 0) {
- ckfree((char *)fsRecPtr);
- }
+ fsRecPtr->nextPtr = toFree;
+ toFree = fsRecPtr;
fsRecPtr = tmpFsRecPtr;
}
- tsdPtr->filesystemList = NULL;
/*
- * Code below operates on shared data. We are already called under mutex
- * lock so we can safely proceede.
- *
* Locate tail of the global filesystem list.
*/
+ Tcl_MutexLock(&filesystemMutex);
fsRecPtr = filesystemList;
while (fsRecPtr != NULL) {
tmpFsRecPtr = fsRecPtr;
@@ -613,48 +590,46 @@ FsRecacheFilesystemList(void)
* Refill the cache honouring the order.
*/
+ list = NULL;
fsRecPtr = tmpFsRecPtr;
while (fsRecPtr != NULL) {
- tmpFsRecPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord));
+ tmpFsRecPtr = ckalloc(sizeof(FilesystemRecord));
*tmpFsRecPtr = *fsRecPtr;
- tmpFsRecPtr->nextPtr = tsdPtr->filesystemList;
+ tmpFsRecPtr->nextPtr = list;
tmpFsRecPtr->prevPtr = NULL;
- if (tsdPtr->filesystemList) {
- tsdPtr->filesystemList->prevPtr = tmpFsRecPtr;
- }
- tsdPtr->filesystemList = tmpFsRecPtr;
+ list = tmpFsRecPtr;
fsRecPtr = fsRecPtr->prevPtr;
}
+ tsdPtr->filesystemList = list;
+ tsdPtr->filesystemEpoch = theFilesystemEpoch;
+ Tcl_MutexUnlock(&filesystemMutex);
+
+ while (toFree) {
+ FilesystemRecord *next = toFree->nextPtr;
+ toFree->fsPtr = NULL;
+ ckfree(toFree);
+ toFree = next;
+ }
/*
* Make sure the above gets released on thread exit.
*/
if (tsdPtr->initialized == 0) {
- Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr);
+ Tcl_CreateThreadExitHandler(FsThrExitProc, tsdPtr);
tsdPtr->initialized = 1;
}
}
-#endif /* TCL_THREADS */
static FilesystemRecord *
-FsGetFirstFilesystem(void) {
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
- FilesystemRecord *fsRecPtr;
-#ifndef TCL_THREADS
- tsdPtr->filesystemEpoch = theFilesystemEpoch;
- fsRecPtr = filesystemList;
-#else
- Tcl_MutexLock(&filesystemMutex);
- if (tsdPtr->filesystemList == NULL
- || (tsdPtr->filesystemEpoch != theFilesystemEpoch)) {
+FsGetFirstFilesystem(void)
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
+ if (tsdPtr->filesystemList == NULL || ((tsdPtr->claims == 0)
+ && (tsdPtr->filesystemEpoch != theFilesystemEpoch))) {
FsRecacheFilesystemList();
- tsdPtr->filesystemEpoch = theFilesystemEpoch;
}
- Tcl_MutexUnlock(&filesystemMutex);
- fsRecPtr = tsdPtr->filesystemList;
-#endif
- return fsRecPtr;
+ return tsdPtr->filesystemList;
}
/*
@@ -663,26 +638,49 @@ FsGetFirstFilesystem(void) {
*/
int
-TclFSEpochOk(filesystemEpoch)
- int filesystemEpoch;
+TclFSEpochOk(
+ int filesystemEpoch)
+{
+ return (filesystemEpoch == 0 || filesystemEpoch == theFilesystemEpoch);
+}
+
+static void
+Claim(void)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
- (void) FsGetFirstFilesystem();
- return (filesystemEpoch == tsdPtr->filesystemEpoch);
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
+
+ tsdPtr->claims++;
}
+
+static void
+Disclaim(void)
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
+
+ tsdPtr->claims--;
+}
+
+int
+TclFSEpoch(void)
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
+
+ return tsdPtr->filesystemEpoch;
+}
+
/*
* If non-NULL, clientData is owned by us and must be freed later.
*/
static void
-FsUpdateCwd(cwdObj, clientData)
- Tcl_Obj *cwdObj;
- ClientData clientData;
+FsUpdateCwd(
+ Tcl_Obj *cwdObj,
+ ClientData clientData)
{
int len;
- char *str = NULL;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
+ const char *str = NULL;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
if (cwdObj != NULL) {
str = Tcl_GetStringFromObj(cwdObj, &len);
@@ -705,7 +703,7 @@ FsUpdateCwd(cwdObj, clientData)
*/
cwdPathPtr = Tcl_NewStringObj(str, len);
- Tcl_IncrRefCount(cwdPathPtr);
+ Tcl_IncrRefCount(cwdPathPtr);
cwdClientData = TclNativeDupInternalRep(clientData);
}
@@ -735,8 +733,8 @@ FsUpdateCwd(cwdObj, clientData)
*
* TclFinalizeFilesystem --
*
- * Clean up the filesystem. After this, calls to all Tcl_FS...
- * functions will fail.
+ * Clean up the filesystem. After this, calls to all Tcl_FS... functions
+ * will fail.
*
* We will later call TclResetFilesystem to restore the FS to a pristine
* state.
@@ -751,12 +749,12 @@ FsUpdateCwd(cwdObj, clientData)
*/
void
-TclFinalizeFilesystem()
+TclFinalizeFilesystem(void)
{
FilesystemRecord *fsRecPtr;
/*
- * Assumption that only one thread is active now. Otherwise we would need
+ * Assumption that only one thread is active now. Otherwise we would need
* to put various mutexes around this code.
*/
@@ -772,23 +770,21 @@ TclFinalizeFilesystem()
/*
* Remove all filesystems, freeing any allocated memory that is no longer
- * needed
+ * needed.
*/
fsRecPtr = filesystemList;
while (fsRecPtr != NULL) {
FilesystemRecord *tmpFsRecPtr = fsRecPtr->nextPtr;
- if (fsRecPtr->fileRefCount <= 0) {
- /*
- * The native filesystem is static, so we don't free it.
- */
- if (fsRecPtr != &nativeFilesystemRecord) {
- ckfree((char *)fsRecPtr);
- }
+ /* The native filesystem is static, so we don't free it. */
+
+ if (fsRecPtr != &nativeFilesystemRecord) {
+ ckfree(fsRecPtr);
}
fsRecPtr = tmpFsRecPtr;
}
+ theFilesystemEpoch++;
filesystemList = NULL;
/*
@@ -796,10 +792,7 @@ TclFinalizeFilesystem()
* filesystem is likely to fail.
*/
- statProcList = NULL;
- accessProcList = NULL;
- openFileChannelProcList = NULL;
-#ifdef __WIN32__
+#ifdef _WIN32
TclWinEncodingsCleanup();
#endif
}
@@ -821,16 +814,12 @@ TclFinalizeFilesystem()
*/
void
-TclResetFilesystem()
+TclResetFilesystem(void)
{
filesystemList = &nativeFilesystemRecord;
+ theFilesystemEpoch++;
- /*
- * Note, at this point, I believe nativeFilesystemRecord -> fileRefCount
- * should equal 1 and if not, we should try to track down the cause.
- */
-
-#ifdef __WIN32__
+#ifdef _WIN32
/*
* Cleans up the win32 API filesystem proc lookup table. This must happen
* very late in finalization so that deleting of copied dlls can occur.
@@ -851,7 +840,7 @@ TclResetFilesystem()
* can use Tcl_FSData to check if it is in the list, provided the
* ClientData used was not NULL).
*
- * Note that the filesystem handling is head-to-tail of the list. Each
+ * Note that the filesystem handling is head-to-tail of the list. Each
* filesystem is asked in turn whether it can handle a particular
* request, until one of them says 'yes'. At that point no further
* filesystems are asked.
@@ -871,9 +860,9 @@ TclResetFilesystem()
*/
int
-Tcl_FSRegister(clientData, fsPtr)
- ClientData clientData; /* Client specific data for this fs */
- Tcl_Filesystem *fsPtr; /* The filesystem record for the new fs. */
+Tcl_FSRegister(
+ ClientData clientData, /* Client specific data for this fs. */
+ const Tcl_Filesystem *fsPtr)/* The filesystem record for the new fs. */
{
FilesystemRecord *newFilesystemPtr;
@@ -881,23 +870,16 @@ Tcl_FSRegister(clientData, fsPtr)
return TCL_ERROR;
}
- newFilesystemPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord));
+ newFilesystemPtr = ckalloc(sizeof(FilesystemRecord));
newFilesystemPtr->clientData = clientData;
newFilesystemPtr->fsPtr = fsPtr;
/*
- * We start with a refCount of 1. If this drops to zero, then anyone is
- * welcome to ckfree us.
- */
-
- newFilesystemPtr->fileRefCount = 1;
-
- /*
- * Is this lock and wait strictly speaking necessary? Since any iterators
+ * Is this lock and wait strictly speaking necessary? Since any iterators
* out there will have grabbed a copy of the head of the list and be
* iterating away from that, if we add a new element to the head of the
- * list, it can't possibly have any effect on any of their loops. In fact
+ * list, it can't possibly have any effect on any of their loops. In fact
* it could be better not to wait, since we are adjusting the filesystem
* epoch, any cached representations calculated by existing iterators are
* going to have to be thrown away anyway.
@@ -932,14 +914,14 @@ Tcl_FSRegister(clientData, fsPtr)
* Tcl_FSUnregister --
*
* Remove the passed filesystem from the list of filesystem function
- * tables. It also ensures that the built-in (native) filesystem is not
+ * tables. It also ensures that the built-in (native) filesystem is not
* removable, although we may wish to change that decision in the future
* to allow a smaller Tcl core, in which the native filesystem is not
* used at all (we could, say, initialise Tcl completely over a network
* connection).
*
* Results:
- * TCL_OK if the procedure pointer was successfully removed, TCL_ERROR
+ * TCL_OK if the function pointer was successfully removed, TCL_ERROR
* otherwise.
*
* Side effects:
@@ -951,8 +933,8 @@ Tcl_FSRegister(clientData, fsPtr)
*/
int
-Tcl_FSUnregister(fsPtr)
- Tcl_Filesystem *fsPtr; /* The filesystem record to remove. */
+Tcl_FSUnregister(
+ const Tcl_Filesystem *fsPtr) /* The filesystem record to remove. */
{
int retVal = TCL_ERROR;
FilesystemRecord *fsRecPtr;
@@ -979,7 +961,7 @@ Tcl_FSUnregister(fsPtr)
/*
* Increment the filesystem epoch counter, since existing paths
- * might conceivably now belong to different filesystems. This
+ * might conceivably now belong to different filesystems. This
* should also ensure that paths which have cached the filesystem
* which is about to be deleted do not reference that filesystem
* (which would of course lead to memory exceptions).
@@ -987,10 +969,7 @@ Tcl_FSUnregister(fsPtr)
theFilesystemEpoch++;
- fsRecPtr->fileRefCount--;
- if (fsRecPtr->fileRefCount <= 0) {
- ckfree((char *)fsRecPtr);
- }
+ ckfree(fsRecPtr);
retVal = TCL_OK;
} else {
@@ -1008,17 +987,17 @@ Tcl_FSUnregister(fsPtr)
* Tcl_FSMatchInDirectory --
*
* This routine is used by the globbing code to search a directory for
- * all files which match a given pattern. The appropriate function for
- * the filesystem to which pathPtr belongs will be called. If pathPtr
+ * all files which match a given pattern. The appropriate function for
+ * the filesystem to which pathPtr belongs will be called. If pathPtr
* does not belong to any filesystem and if it is NULL or the empty
* string, then we assume the pattern is to be matched in the current
- * working directory. To avoid have the Tcl_FSMatchInDirectoryProc for
+ * working directory. To avoid have the Tcl_FSMatchInDirectoryProc for
* each filesystem from having to deal with this issue, we create a
* pathPtr on the fly (equal to the cwd), and then remove it from the
- * results returned. This makes filesystems easy to write, since they
- * can assume the pathPtr passed to them is an ordinary path. In fact
- * this means we could remove such special case handling from Tcl's
- * native filesystems.
+ * results returned. This makes filesystems easy to write, since they can
+ * assume the pathPtr passed to them is an ordinary path. In fact this
+ * means we could remove such special case handling from Tcl's native
+ * filesystems.
*
* If 'pattern' is NULL, then pathPtr is assumed to be a fully specified
* path of a single file/directory which must be checked for existence
@@ -1027,14 +1006,14 @@ Tcl_FSUnregister(fsPtr)
* Results:
*
* The return value is a standard Tcl result indicating whether an error
- * occurred in globbing. Error messages are placed in interp, but good
+ * occurred in globbing. Error messages are placed in interp, but good
* results are placed in the resultPtr given.
*
* Recursive searches, e.g.
* glob -dir $dir -join * pkgIndex.tcl
* which must recurse through each directory matching '*' are handled
* internally by Tcl, by passing specific flags in a modified 'types'
- * parameter. This means the actual filesystem only ever sees patterns
+ * parameter. This means the actual filesystem only ever sees patterns
* which match in a single directory.
*
* Side effects:
@@ -1044,25 +1023,26 @@ Tcl_FSUnregister(fsPtr)
*/
int
-Tcl_FSMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
- Tcl_Interp *interp; /* Interpreter to receive error messages. */
- Tcl_Obj *resultPtr; /* List object to receive results. */
- Tcl_Obj *pathPtr; /* Contains path to directory to search. */
- CONST char *pattern; /* Pattern to match against. */
- Tcl_GlobTypeData *types; /* Object containing list of acceptable types.
+Tcl_FSMatchInDirectory(
+ Tcl_Interp *interp, /* Interpreter to receive error messages, but
+ * may be NULL. */
+ Tcl_Obj *resultPtr, /* List object to receive results. */
+ Tcl_Obj *pathPtr, /* Contains path to directory to search. */
+ const char *pattern, /* Pattern to match against. */
+ Tcl_GlobTypeData *types) /* Object containing list of acceptable types.
* May be NULL. In particular the directory
* flag is very important. */
{
- Tcl_Filesystem *fsPtr;
+ const Tcl_Filesystem *fsPtr;
Tcl_Obj *cwd, *tmpResultPtr, **elemsPtr;
int resLength, i, ret = -1;
- if (types != NULL && types->type & TCL_GLOB_TYPE_MOUNT) {
+ if (types != NULL && (types->type & TCL_GLOB_TYPE_MOUNT)) {
/*
* We don't currently allow querying of mounts by external code (a
* valuable future step), so since we're the only function that
* actually knows about mounts, this means we're being called
- * recursively by ourself. Return no matches.
+ * recursively by ourself. Return no matches.
*/
return TCL_OK;
@@ -1084,8 +1064,8 @@ Tcl_FSMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
Tcl_SetErrno(ENOENT);
return -1;
}
- ret = (*fsPtr->matchInDirectoryProc)(interp, resultPtr, pathPtr,
- pattern, types);
+ ret = fsPtr->matchInDirectoryProc(interp, resultPtr, pathPtr, pattern,
+ types);
if (ret == TCL_OK && pattern != NULL) {
FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types);
}
@@ -1094,7 +1074,7 @@ Tcl_FSMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
/*
* If the path isn't empty, we have no idea how to match files in a
- * directory which belongs to no known filesystem
+ * directory which belongs to no known filesystem.
*/
if (pathPtr != NULL && TclGetString(pathPtr)[0] != '\0') {
@@ -1115,8 +1095,9 @@ Tcl_FSMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
cwd = Tcl_FSGetCwd(NULL);
if (cwd == NULL) {
if (interp != NULL) {
- Tcl_SetResult(interp, "glob couldn't determine "
- "the current working directory", TCL_STATIC);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "glob couldn't determine the current working directory",
+ -1));
}
return TCL_ERROR;
}
@@ -1125,8 +1106,8 @@ Tcl_FSMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
if (fsPtr != NULL && fsPtr->matchInDirectoryProc != NULL) {
TclNewObj(tmpResultPtr);
Tcl_IncrRefCount(tmpResultPtr);
- ret = (*fsPtr->matchInDirectoryProc)(interp, tmpResultPtr, cwd,
- pattern, types);
+ ret = fsPtr->matchInDirectoryProc(interp, tmpResultPtr, cwd, pattern,
+ types);
if (ret == TCL_OK) {
FsAddMountsToGlobResult(tmpResultPtr, cwd, pattern, types);
@@ -1153,7 +1134,7 @@ Tcl_FSMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
* FsAddMountsToGlobResult --
*
* This routine is used by the globbing code to take the results of a
- * directory listing and add any mounted paths to that listing. This is
+ * directory listing and add any mounted paths to that listing. This is
* required so that simple things like 'glob *' merge mounts and listings
* correctly.
*
@@ -1167,12 +1148,12 @@ Tcl_FSMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
*/
static void
-FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types)
- Tcl_Obj *resultPtr; /* The current list of matching paths; must
+FsAddMountsToGlobResult(
+ Tcl_Obj *resultPtr, /* The current list of matching paths; must
* not be shared! */
- Tcl_Obj *pathPtr; /* The directory in question */
- CONST char *pattern; /* Pattern to match against. */
- Tcl_GlobTypeData *types; /* Object containing list of acceptable types.
+ Tcl_Obj *pathPtr, /* The directory in question. */
+ const char *pattern, /* Pattern to match against. */
+ Tcl_GlobTypeData *types) /* Object containing list of acceptable types.
* May be NULL. In particular the directory
* flag is very important. */
{
@@ -1211,13 +1192,12 @@ FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types)
Tcl_ListObjReplace(NULL, resultPtr, j, 1, 0, NULL);
gLength--;
}
- break; /* Break out of for loop */
+ break; /* Break out of for loop. */
}
}
if (!found && dir) {
+ Tcl_Obj *norm;
int len, mlen;
- CONST char *path;
- CONST char *mount;
/*
* We know mElt is absolute normalized and lies inside pathPtr, so
@@ -1225,19 +1205,23 @@ FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types)
* i.e. the representation which is relative to pathPtr.
*/
- mount = Tcl_GetStringFromObj(mElt, &mlen);
- path = Tcl_GetStringFromObj(Tcl_FSGetNormalizedPath(NULL, pathPtr),
- &len);
- if (path[len-1] == '/') {
- /*
- * Deal with the root of the volume.
- */
+ norm = Tcl_FSGetNormalizedPath(NULL, pathPtr);
+ if (norm != NULL) {
+ const char *path, *mount;
- len--;
- }
- mElt = TclNewFSPathObj(pathPtr, mount + len + 1, mlen - len);
- Tcl_ListObjAppendElement(NULL, resultPtr, mElt);
+ mount = Tcl_GetStringFromObj(mElt, &mlen);
+ path = Tcl_GetStringFromObj(norm, &len);
+ if (path[len-1] == '/') {
+ /*
+ * Deal with the root of the volume.
+ */
+ len--;
+ }
+ len++; /* account for '/' in the mElt [Bug 1602539] */
+ mElt = TclNewFSPathObj(pathPtr, mount + len, mlen - len);
+ Tcl_ListObjAppendElement(NULL, resultPtr, mElt);
+ }
/*
* No need to increment gLength, since we don't want to compare
* mounts against mounts.
@@ -1267,7 +1251,7 @@ FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types)
* Clearly it should only therefore be called when it is really required!
* There are a few circumstances when it should be called:
*
- * (1) when a new filesystem is registered or unregistered. Strictly
+ * (1) when a new filesystem is registered or unregistered. Strictly
* speaking this is only necessary if the new filesystem accepts file
* paths as is (normally the filesystem itself is really a shell which
* hasn't yet had any mount points established and so its
@@ -1297,11 +1281,11 @@ FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types)
*/
void
-Tcl_FSMountsChanged(fsPtr)
- Tcl_Filesystem *fsPtr;
+Tcl_FSMountsChanged(
+ const Tcl_Filesystem *fsPtr)
{
/*
- * We currently don't do anything with this parameter. We could in the
+ * We currently don't do anything with this parameter. We could in the
* future only invalidate files for this filesystem or otherwise take more
* advanced action.
*/
@@ -1327,7 +1311,7 @@ Tcl_FSMountsChanged(fsPtr)
* that filesystem is not registered.
*
* Results:
- * A clientData value, or NULL. Note that if the filesystem was
+ * A clientData value, or NULL. Note that if the filesystem was
* registered with a NULL clientData field, this function will return
* that NULL value.
*
@@ -1338,14 +1322,14 @@ Tcl_FSMountsChanged(fsPtr)
*/
ClientData
-Tcl_FSData(fsPtr)
- Tcl_Filesystem *fsPtr; /* The filesystem record to query. */
+Tcl_FSData(
+ const Tcl_Filesystem *fsPtr) /* The filesystem record to query. */
{
ClientData retVal = NULL;
FilesystemRecord *fsRecPtr = FsGetFirstFilesystem();
/*
- * Traverse the list of filesystems look for a particular one. If found,
+ * Traverse the list of filesystems look for a particular one. If found,
* return that filesystem's clientData (originally provided when calling
* Tcl_FSRegister).
*/
@@ -1366,13 +1350,13 @@ Tcl_FSData(fsPtr)
* TclFSNormalizeToUniquePath --
*
* Takes a path specification containing no ../, ./ sequences, and
- * converts it into a unique path for the given platform. On Unix, this
+ * converts it into a unique path for the given platform. On Unix, this
* means the path must be free of symbolic links/aliases, and on Windows
* it means we want the long form, with that long form's case-dependence
* (which gives us a unique, case-dependent path).
*
* Results:
- * The pathPtr is modified in place. The return value is the last byte
+ * The pathPtr is modified in place. The return value is the last byte
* offset which was recognised in the path string.
*
* Side effects:
@@ -1381,7 +1365,7 @@ Tcl_FSData(fsPtr)
* Special notes:
* If the filesystem-specific normalizePathProcs can re-introduce ../, ./
* sequences into the path, then this function will not return the
- * correct result. This may be possible with symbolic links on unix.
+ * correct result. This may be possible with symbolic links on unix.
*
* Important assumption: if startAt is non-zero, it must point to a
* directory separator that we know exists and is already normalized (so
@@ -1391,59 +1375,61 @@ Tcl_FSData(fsPtr)
*/
int
-TclFSNormalizeToUniquePath(interp, pathPtr, startAt, clientDataPtr)
- Tcl_Interp *interp; /* Used for error messages. */
- Tcl_Obj *pathPtr; /* The path to normalize in place */
- int startAt; /* Start at this char-offset */
- ClientData *clientDataPtr; /* If we generated a complete normalized path
- * for a given filesystem, we can optionally
- * return an fs-specific clientdata here. */
+TclFSNormalizeToUniquePath(
+ Tcl_Interp *interp, /* Used for error messages. */
+ Tcl_Obj *pathPtr, /* The path to normalize in place. */
+ int startAt) /* Start at this char-offset. */
{
FilesystemRecord *fsRecPtr, *firstFsRecPtr;
- /* Ignore this variable */
- (void) clientDataPtr;
/*
* Call each of the "normalise path" functions in succession. This is a
* special case, in which if we have a native filesystem handler, we call
- * it first. This is because the root of Tcl's filesystem is always a
+ * it first. This is because the root of Tcl's filesystem is always a
* native filesystem (i.e. '/' on unix is native).
*/
firstFsRecPtr = FsGetFirstFilesystem();
- fsRecPtr = firstFsRecPtr;
- while (fsRecPtr != NULL) {
- if (fsRecPtr == &nativeFilesystemRecord) {
- Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc;
- if (proc != NULL) {
- startAt = (*proc)(interp, pathPtr, startAt);
- }
- break;
+ Claim();
+ for (fsRecPtr=firstFsRecPtr; fsRecPtr!=NULL; fsRecPtr=fsRecPtr->nextPtr) {
+ if (fsRecPtr->fsPtr != &tclNativeFilesystem) {
+ continue;
}
- fsRecPtr = fsRecPtr->nextPtr;
+
+ /*
+ * TODO: Assume that we always find the native file system; it should
+ * always be there...
+ */
+
+ if (fsRecPtr->fsPtr->normalizePathProc != NULL) {
+ startAt = fsRecPtr->fsPtr->normalizePathProc(interp, pathPtr,
+ startAt);
+ }
+ break;
}
- fsRecPtr = firstFsRecPtr;
- while (fsRecPtr != NULL) {
+ for (fsRecPtr=firstFsRecPtr; fsRecPtr!=NULL; fsRecPtr=fsRecPtr->nextPtr) {
/*
* Skip the native system next time through.
*/
- if (fsRecPtr != &nativeFilesystemRecord) {
- Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc;
- if (proc != NULL) {
- startAt = (*proc)(interp, pathPtr, startAt);
- }
+ if (fsRecPtr->fsPtr == &tclNativeFilesystem) {
+ continue;
+ }
- /*
- * We could add an efficiency check like this:
- * if (retVal == length-of(pathPtr)) {break;}
- * but there's not much benefit.
- */
+ if (fsRecPtr->fsPtr->normalizePathProc != NULL) {
+ startAt = fsRecPtr->fsPtr->normalizePathProc(interp, pathPtr,
+ startAt);
}
- fsRecPtr = fsRecPtr->nextPtr;
+
+ /*
+ * We could add an efficiency check like this:
+ * if (retVal == length-of(pathPtr)) {break;}
+ * but there's not much benefit.
+ */
}
+ Disclaim();
return startAt;
}
@@ -1454,7 +1440,7 @@ TclFSNormalizeToUniquePath(interp, pathPtr, startAt, clientDataPtr)
* TclGetOpenMode --
*
* This routine is an obsolete, limited version of TclGetOpenModeEx()
- * below. It exists only to satisfy any extensions imprudently using it
+ * below. It exists only to satisfy any extensions imprudently using it
* via Tcl's internal stubs table.
*
* Results:
@@ -1467,14 +1453,12 @@ TclFSNormalizeToUniquePath(interp, pathPtr, startAt, clientDataPtr)
*/
int
-TclGetOpenMode(interp, modeString, seekFlagPtr)
- Tcl_Interp *interp; /* Interpreter to use for error
- * reporting - may be NULL. */
- CONST char *modeString; /* Mode string, e.g. "r+" or "RDONLY
- * CREAT". */
- int *seekFlagPtr; /* Set this to 1 if the caller should
- * seek to EOF during the opening of
- * the file. */
+TclGetOpenMode(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting -
+ * may be NULL. */
+ const char *modeString, /* Mode string, e.g. "r+" or "RDONLY CREAT" */
+ int *seekFlagPtr) /* Set this to 1 if the caller should seek to
+ * EOF during the opening of the file. */
{
int binary = 0;
return TclGetOpenModeEx(interp, modeString, seekFlagPtr, &binary);
@@ -1497,7 +1481,7 @@ TclGetOpenMode(interp, modeString, seekFlagPtr)
*
* Side effects:
* Sets the integer referenced by seekFlagPtr to 1 to tell the caller to
- * seek to EOF after opening the file, or to 0 otherwise. Sets the
+ * seek to EOF after opening the file, or to 0 otherwise. Sets the
* integer referenced by binaryPtr to 1 to tell the caller to seek to
* configure the channel for binary data, or to 0 otherwise.
*
@@ -1509,24 +1493,22 @@ TclGetOpenMode(interp, modeString, seekFlagPtr)
*/
int
-TclGetOpenModeEx(interp, modeString, seekFlagPtr, binaryPtr)
- Tcl_Interp *interp; /* Interpreter to use for error
- * reporting - may be NULL. */
- CONST char *modeString; /* Mode string, e.g. "r+" or "RDONLY
- * CREAT". */
- int *seekFlagPtr; /* Set this to 1 if the caller should
- * seek to EOF during the opening of
- * the file. */
- int *binaryPtr; /* Set this to 1 if the caller should
- * configure the opened channel for
- * binary operations */
+TclGetOpenModeEx(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting -
+ * may be NULL. */
+ const char *modeString, /* Mode string, e.g. "r+" or "RDONLY CREAT" */
+ int *seekFlagPtr, /* Set this to 1 if the caller should seek to
+ * EOF during the opening of the file. */
+ int *binaryPtr) /* Set this to 1 if the caller should
+ * configure the opened channel for binary
+ * operations. */
{
int mode, modeArgc, c, i, gotRW;
- CONST char **modeArgv, *flag;
+ const char **modeArgv, *flag;
#define RW_MODES (O_RDONLY|O_WRONLY|O_RDWR)
/*
- * Check for the simpler fopen-like access modes (e.g. "r"). They are
+ * Check for the simpler fopen-like access modes (e.g. "r"). They are
* distinguished from the POSIX access modes by the presence of a
* lower-case first letter.
*/
@@ -1550,27 +1532,30 @@ TclGetOpenModeEx(interp, modeString, seekFlagPtr, binaryPtr)
mode = O_WRONLY|O_CREAT|O_TRUNC;
break;
case 'a':
- mode = O_WRONLY|O_CREAT;
+ /*
+ * Added O_APPEND for proper automatic seek-to-end-on-write by the
+ * OS. [Bug 680143]
+ */
+
+ mode = O_WRONLY|O_CREAT|O_APPEND;
*seekFlagPtr = 1;
break;
default:
- error:
- *seekFlagPtr = 0;
- *binaryPtr = 0;
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "illegal access mode \"", modeString,
- "\"", (char *) NULL);
- }
- return -1;
+ goto error;
}
- i=1;
+ i = 1;
while (i<3 && modeString[i]) {
if (modeString[i] == modeString[i-1]) {
goto error;
}
switch (modeString[i++]) {
case '+':
- mode &= ~(O_RDONLY|O_WRONLY);
+ /*
+ * Must remove the O_APPEND flag so that the seek command
+ * works. [Bug 1773127]
+ */
+
+ mode &= ~(O_RDONLY|O_WRONLY|O_APPEND);
mode |= O_RDWR;
break;
case 'b':
@@ -1584,6 +1569,15 @@ TclGetOpenModeEx(interp, modeString, seekFlagPtr, binaryPtr)
goto error;
}
return mode;
+
+ error:
+ *seekFlagPtr = 0;
+ *binaryPtr = 0;
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "illegal access mode \"%s\"", modeString));
+ }
+ return -1;
}
/*
@@ -1595,7 +1589,7 @@ TclGetOpenModeEx(interp, modeString, seekFlagPtr, binaryPtr)
*/
if (Tcl_SplitList(interp, modeString, &modeArgc, &modeArgv) != TCL_OK) {
- if (interp != (Tcl_Interp *) NULL) {
+ if (interp != NULL) {
Tcl_AddErrorInfo(interp,
"\n while processing open access modes \"");
Tcl_AddErrorInfo(interp, modeString);
@@ -1629,28 +1623,25 @@ TclGetOpenModeEx(interp, modeString, seekFlagPtr, binaryPtr)
#ifdef O_NOCTTY
mode |= O_NOCTTY;
#else
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "access mode \"", flag,
- "\" not supported by this system", (char *) NULL);
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "access mode \"%s\" not supported by this system",
+ flag));
}
- ckfree((char *) modeArgv);
+ ckfree(modeArgv);
return -1;
#endif
} else if ((c == 'N') && (strcmp(flag, "NONBLOCK") == 0)) {
-#if defined(O_NDELAY) || defined(O_NONBLOCK)
-# ifdef O_NONBLOCK
+#ifdef O_NONBLOCK
mode |= O_NONBLOCK;
-# else
- mode |= O_NDELAY;
-# endif
-
#else
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "access mode \"", flag,
- "\" not supported by this system", (char *) NULL);
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "access mode \"%s\" not supported by this system",
+ flag));
}
- ckfree((char *) modeArgv);
+ ckfree(modeArgv);
return -1;
#endif
@@ -1660,23 +1651,24 @@ TclGetOpenModeEx(interp, modeString, seekFlagPtr, binaryPtr)
*binaryPtr = 1;
} else {
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "invalid access mode \"", flag,
- "\": must be RDONLY, WRONLY, RDWR, APPEND, BINARY, "
- "CREAT, EXCL, NOCTTY, NONBLOCK, or TRUNC",
- (char *) NULL);
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "invalid access mode \"%s\": must be RDONLY, WRONLY, "
+ "RDWR, APPEND, BINARY, CREAT, EXCL, NOCTTY, NONBLOCK,"
+ " or TRUNC", flag));
}
- ckfree((char *) modeArgv);
+ ckfree(modeArgv);
return -1;
}
}
- ckfree((char *) modeArgv);
+ ckfree(modeArgv);
if (!gotRW) {
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "access mode must include either",
- " RDONLY, WRONLY, or RDWR", (char *) NULL);
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "access mode must include either RDONLY, WRONLY, or RDWR",
+ -1));
}
return -1;
}
@@ -1684,32 +1676,20 @@ TclGetOpenModeEx(interp, modeString, seekFlagPtr, binaryPtr)
}
/*
- * Tcl_FSEvalFile is Tcl_FSEvalFileEx without encoding argument.
- */
-
-int
-Tcl_FSEvalFile(interp, pathPtr)
- Tcl_Interp *interp; /* Interpreter in which to process file. */
- Tcl_Obj *pathPtr; /* Path of file to process. Tilde-substitution
- * will be performed on this name. */
-{
- return Tcl_FSEvalFileEx(interp, pathPtr, NULL);
-}
-
-/*
*----------------------------------------------------------------------
*
- * Tcl_FSEvalFileEx --
+ * Tcl_FSEvalFile, Tcl_FSEvalFileEx, TclNREvalFile --
*
* Read in a file and process the entire file as one gigantic Tcl
- * command.
+ * command. Tcl_FSEvalFile is Tcl_FSEvalFileEx without encoding argument.
+ * TclNREvalFile is an NRE-enabled version of Tcl_FSEvalFileEx.
*
* Results:
* A standard Tcl result, which is either the result of executing the
* file or an error indicating why the file couldn't be read.
*
* Side effects:
- * Depends on the commands in the file. During the evaluation of the
+ * Depends on the commands in the file. During the evaluation of the
* contents of the file, iPtr->scriptFile is made to point to pathPtr
* (the old value is cached and replaced when this function returns).
*
@@ -1717,53 +1697,58 @@ Tcl_FSEvalFile(interp, pathPtr)
*/
int
-Tcl_FSEvalFileEx(interp, pathPtr, encodingName)
- Tcl_Interp *interp; /* Interpreter in which to process file. */
- Tcl_Obj *pathPtr; /* Path of file to process. Tilde-substitution
+Tcl_FSEvalFile(
+ Tcl_Interp *interp, /* Interpreter in which to process file. */
+ Tcl_Obj *pathPtr) /* Path of file to process. Tilde-substitution
* will be performed on this name. */
- CONST char *encodingName; /* If non-NULL, then use this encoding for the
- * file. */
{
- int result, length;
+ return Tcl_FSEvalFileEx(interp, pathPtr, NULL);
+}
+
+int
+Tcl_FSEvalFileEx(
+ Tcl_Interp *interp, /* Interpreter in which to process file. */
+ Tcl_Obj *pathPtr, /* Path of file to process. Tilde-substitution
+ * will be performed on this name. */
+ const char *encodingName) /* If non-NULL, then use this encoding for the
+ * file. NULL means use the system encoding. */
+{
+ int length, result = TCL_ERROR;
Tcl_StatBuf statBuf;
Tcl_Obj *oldScriptFile;
Interp *iPtr;
- char *string;
+ const char *string;
Tcl_Channel chan;
Tcl_Obj *objPtr;
if (Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL) {
- return TCL_ERROR;
+ return result;
}
- result = TCL_ERROR;
- objPtr = Tcl_NewObj();
-
if (Tcl_FSStat(pathPtr, &statBuf) == -1) {
Tcl_SetErrno(errno);
- Tcl_AppendResult(interp, "couldn't read file \"",
- Tcl_GetString(pathPtr),
- "\": ", Tcl_PosixError(interp), (char *) NULL);
- goto end;
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't read file \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
+ return result;
}
chan = Tcl_FSOpenFileChannel(interp, pathPtr, "r", 0644);
- if (chan == (Tcl_Channel) NULL) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "couldn't read file \"",
- Tcl_GetString(pathPtr),
- "\": ", Tcl_PosixError(interp), (char *) NULL);
- goto end;
+ if (chan == NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't read file \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
+ return result;
}
/*
- * The eofchar is \32 (^Z). This is the usual on Windows, but we effect
- * this cross-platform to allow for scripted documents. [Bug: 2040]
+ * The eofchar is \32 (^Z). This is the usual on Windows, but we effect
+ * this cross-platform to allow for scripted documents. [Bug: 2040]
*/
Tcl_SetChannelOption(interp, chan, "-eofchar", "\32");
/*
- * If the encoding is specified, set it for the channel. Else don't touch
+ * If the encoding is specified, set it for the channel. Else don't touch
* it (and use the system encoding) Report error on unknown encoding.
*/
@@ -1771,15 +1756,38 @@ Tcl_FSEvalFileEx(interp, pathPtr, encodingName)
if (Tcl_SetChannelOption(interp, chan, "-encoding", encodingName)
!= TCL_OK) {
Tcl_Close(interp,chan);
- goto end;
+ return result;
}
}
- if (Tcl_ReadChars(chan, objPtr, -1, 0) < 0) {
+ objPtr = Tcl_NewObj();
+ Tcl_IncrRefCount(objPtr);
+
+ /*
+ * Try to read first character of stream, so we can check for utf-8 BOM to
+ * be handled especially.
+ */
+
+ if (Tcl_ReadChars(chan, objPtr, 1, 0) < 0) {
+ Tcl_Close(interp, chan);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't read file \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
+ goto end;
+ }
+ string = Tcl_GetString(objPtr);
+
+ /*
+ * If first character is not a BOM, append the remaining characters,
+ * otherwise replace them. [Bug 3466099]
+ */
+
+ if (Tcl_ReadChars(chan, objPtr, -1,
+ memcmp(string, "\xef\xbb\xbf", 3)) < 0) {
Tcl_Close(interp, chan);
- Tcl_AppendResult(interp, "couldn't read file \"",
- Tcl_GetString(pathPtr),
- "\": ", Tcl_PosixError(interp), (char *) NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't read file \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
goto end;
}
@@ -1792,7 +1800,13 @@ Tcl_FSEvalFileEx(interp, pathPtr, encodingName)
iPtr->scriptFile = pathPtr;
Tcl_IncrRefCount(iPtr->scriptFile);
string = Tcl_GetStringFromObj(objPtr, &length);
- result = Tcl_EvalEx(interp, string, length, 0);
+
+ /*
+ * TIP #280 Force the evaluator to open a frame for a sourced file.
+ */
+
+ iPtr->evalFlags |= TCL_EVAL_FILE;
+ result = TclEvalEx(interp, string, length, 0, 1, NULL, string);
/*
* Now we have to be careful; the script may have changed the
@@ -1812,24 +1826,170 @@ Tcl_FSEvalFileEx(interp, pathPtr, encodingName)
* Record information telling where the error occurred.
*/
- Tcl_Obj *errorLine = Tcl_NewIntObj(interp->errorLine);
- Tcl_Obj *msg = Tcl_NewStringObj("\n (file \"", -1);
- CONST char *pathString = Tcl_GetStringFromObj(pathPtr, &length);
- Tcl_IncrRefCount(msg);
- Tcl_IncrRefCount(errorLine);
- TclAppendLimitedToObj(msg, pathString, length, 150, "");
- Tcl_AppendToObj(msg, "\" line ", -1);
- Tcl_AppendObjToObj(msg, errorLine);
- Tcl_DecrRefCount(errorLine);
- Tcl_AppendToObj(msg, ")", -1);
- TclAppendObjToErrorInfo(interp, msg);
- Tcl_DecrRefCount(msg);
+ const char *pathString = Tcl_GetStringFromObj(pathPtr, &length);
+ int limit = 150;
+ int overflow = (length > limit);
+
+ Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
+ "\n (file \"%.*s%s\" line %d)",
+ (overflow ? limit : length), pathString,
+ (overflow ? "..." : ""), Tcl_GetErrorLine(interp)));
}
end:
Tcl_DecrRefCount(objPtr);
return result;
}
+
+int
+TclNREvalFile(
+ Tcl_Interp *interp, /* Interpreter in which to process file. */
+ Tcl_Obj *pathPtr, /* Path of file to process. Tilde-substitution
+ * will be performed on this name. */
+ const char *encodingName) /* If non-NULL, then use this encoding for the
+ * file. NULL means use the system encoding. */
+{
+ Tcl_StatBuf statBuf;
+ Tcl_Obj *oldScriptFile, *objPtr;
+ Interp *iPtr;
+ Tcl_Channel chan;
+ const char *string;
+
+ if (Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL) {
+ return TCL_ERROR;
+ }
+
+ if (Tcl_FSStat(pathPtr, &statBuf) == -1) {
+ Tcl_SetErrno(errno);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't read file \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
+ return TCL_ERROR;
+ }
+ chan = Tcl_FSOpenFileChannel(interp, pathPtr, "r", 0644);
+ if (chan == NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't read file \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
+ return TCL_ERROR;
+ }
+
+ /*
+ * The eofchar is \32 (^Z). This is the usual on Windows, but we effect
+ * this cross-platform to allow for scripted documents. [Bug: 2040]
+ */
+
+ Tcl_SetChannelOption(interp, chan, "-eofchar", "\32");
+
+ /*
+ * If the encoding is specified, set it for the channel. Else don't touch
+ * it (and use the system encoding) Report error on unknown encoding.
+ */
+
+ if (encodingName != NULL) {
+ if (Tcl_SetChannelOption(interp, chan, "-encoding", encodingName)
+ != TCL_OK) {
+ Tcl_Close(interp,chan);
+ return TCL_ERROR;
+ }
+ }
+
+ objPtr = Tcl_NewObj();
+ Tcl_IncrRefCount(objPtr);
+
+ /*
+ * Try to read first character of stream, so we can check for utf-8 BOM to
+ * be handled especially.
+ */
+
+ if (Tcl_ReadChars(chan, objPtr, 1, 0) < 0) {
+ Tcl_Close(interp, chan);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't read file \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
+ Tcl_DecrRefCount(objPtr);
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objPtr);
+
+ /*
+ * If first character is not a BOM, append the remaining characters,
+ * otherwise replace them. [Bug 3466099]
+ */
+
+ if (Tcl_ReadChars(chan, objPtr, -1,
+ memcmp(string, "\xef\xbb\xbf", 3)) < 0) {
+ Tcl_Close(interp, chan);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't read file \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
+ Tcl_DecrRefCount(objPtr);
+ return TCL_ERROR;
+ }
+
+ if (Tcl_Close(interp, chan) != TCL_OK) {
+ Tcl_DecrRefCount(objPtr);
+ return TCL_ERROR;
+ }
+
+ iPtr = (Interp *) interp;
+ oldScriptFile = iPtr->scriptFile;
+ iPtr->scriptFile = pathPtr;
+ Tcl_IncrRefCount(iPtr->scriptFile);
+
+ /*
+ * TIP #280: Force the evaluator to open a frame for a sourced file.
+ */
+
+ iPtr->evalFlags |= TCL_EVAL_FILE;
+ TclNRAddCallback(interp, EvalFileCallback, oldScriptFile, pathPtr, objPtr,
+ NULL);
+ return TclNREvalObjEx(interp, objPtr, 0, NULL, INT_MIN);
+}
+
+static int
+EvalFileCallback(
+ ClientData data[],
+ Tcl_Interp *interp,
+ int result)
+{
+ Interp *iPtr = (Interp *) interp;
+ Tcl_Obj *oldScriptFile = data[0];
+ Tcl_Obj *pathPtr = data[1];
+ Tcl_Obj *objPtr = data[2];
+
+ /*
+ * Now we have to be careful; the script may have changed the
+ * iPtr->scriptFile value, so we must reset it without assuming it still
+ * points to 'pathPtr'.
+ */
+
+ if (iPtr->scriptFile != NULL) {
+ Tcl_DecrRefCount(iPtr->scriptFile);
+ }
+ iPtr->scriptFile = oldScriptFile;
+
+ if (result == TCL_RETURN) {
+ result = TclUpdateReturnInfo(iPtr);
+ } else if (result == TCL_ERROR) {
+ /*
+ * Record information telling where the error occurred.
+ */
+
+ int length;
+ const char *pathString = Tcl_GetStringFromObj(pathPtr, &length);
+ const int limit = 150;
+ int overflow = (length > limit);
+
+ Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
+ "\n (file \"%.*s%s\" line %d)",
+ (overflow ? limit : length), pathString,
+ (overflow ? "..." : ""), Tcl_GetErrorLine(interp)));
+ }
+
+ Tcl_DecrRefCount(objPtr);
+ return result;
+}
/*
*----------------------------------------------------------------------
@@ -1851,8 +2011,13 @@ Tcl_FSEvalFileEx(interp, pathPtr, encodingName)
*/
int
-Tcl_GetErrno()
+Tcl_GetErrno(void)
{
+ /*
+ * On some platforms, errno is really a thread local (implemented by the C
+ * library).
+ */
+
return errno;
}
@@ -1861,7 +2026,9 @@ Tcl_GetErrno()
*
* Tcl_SetErrno --
*
- * Sets the Tcl error code variable to the supplied value.
+ * Sets the Tcl error code variable to the supplied value. On some saner
+ * platforms this is actually a thread-local (this is implemented in the
+ * C library) but this is *really* unsafe to assume!
*
* Results:
* None.
@@ -1873,9 +2040,14 @@ Tcl_GetErrno()
*/
void
-Tcl_SetErrno(err)
- int err; /* The new value. */
+Tcl_SetErrno(
+ int err) /* The new value. */
{
+ /*
+ * On some platforms, errno is really a thread local (implemented by the C
+ * library).
+ */
+
errno = err;
}
@@ -1884,8 +2056,8 @@ Tcl_SetErrno(err)
*
* Tcl_PosixError --
*
- * This procedure is typically called after UNIX kernel calls return
- * errors. It stores machine-readable information about the error in
+ * This function is typically called after UNIX kernel calls return
+ * errors. It stores machine-readable information about the error in
* errorCode field of interp and returns an information string for the
* caller's use.
*
@@ -1898,16 +2070,18 @@ Tcl_SetErrno(err)
*----------------------------------------------------------------------
*/
-CONST char *
-Tcl_PosixError(interp)
- Tcl_Interp *interp; /* Interpreter whose errorCode field
- * is to be set. */
+const char *
+Tcl_PosixError(
+ Tcl_Interp *interp) /* Interpreter whose errorCode field is to be
+ * set. */
{
- CONST char *id, *msg;
+ const char *id, *msg;
msg = Tcl_ErrnoMsg(errno);
id = Tcl_ErrnoId();
- Tcl_SetErrorCode(interp, "POSIX", id, msg, (char *) NULL);
+ if (interp) {
+ Tcl_SetErrorCode(interp, "POSIX", id, msg, NULL);
+ }
return msg;
}
@@ -1916,7 +2090,7 @@ Tcl_PosixError(interp)
*
* Tcl_FSStat --
*
- * This procedure replaces the library version of stat and lsat.
+ * This function replaces the library version of stat and lsat.
*
* The appropriate function for the filesystem to which pathPtr belongs
* will be called.
@@ -1931,74 +2105,14 @@ Tcl_PosixError(interp)
*/
int
-Tcl_FSStat(pathPtr, buf)
- Tcl_Obj *pathPtr; /* Path of file to stat (in current CP). */
- Tcl_StatBuf *buf; /* Filled with results of stat call. */
+Tcl_FSStat(
+ Tcl_Obj *pathPtr, /* Path of file to stat (in current CP). */
+ Tcl_StatBuf *buf) /* Filled with results of stat call. */
{
- Tcl_Filesystem *fsPtr;
-#ifdef USE_OBSOLETE_FS_HOOKS
- struct stat oldStyleStatBuffer;
- int retVal = -1;
-
- /*
- * Call each of the "stat" function in succession. A non-return value of
- * -1 indicates the particular function has succeeded.
- */
-
- Tcl_MutexLock(&obsoleteFsHookMutex);
-
- if (statProcList != NULL) {
- StatProc *statProcPtr;
- char *path;
- Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
- if (transPtr == NULL) {
- path = NULL;
- } else {
- path = Tcl_GetString(transPtr);
- }
-
- statProcPtr = statProcList;
- while ((retVal == -1) && (statProcPtr != NULL)) {
- retVal = (*statProcPtr->proc)(path, &oldStyleStatBuffer);
- statProcPtr = statProcPtr->nextPtr;
- }
- if (transPtr != NULL) {
- Tcl_DecrRefCount(transPtr);
- }
- }
-
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
- if (retVal != -1) {
- /*
- * Note that EOVERFLOW is not a problem here, and these assignments
- * should all be widening (if not identity.)
- */
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- buf->st_mode = oldStyleStatBuffer.st_mode;
- buf->st_ino = oldStyleStatBuffer.st_ino;
- buf->st_dev = oldStyleStatBuffer.st_dev;
- buf->st_rdev = oldStyleStatBuffer.st_rdev;
- buf->st_nlink = oldStyleStatBuffer.st_nlink;
- buf->st_uid = oldStyleStatBuffer.st_uid;
- buf->st_gid = oldStyleStatBuffer.st_gid;
- buf->st_size = Tcl_LongAsWide(oldStyleStatBuffer.st_size);
- buf->st_atime = oldStyleStatBuffer.st_atime;
- buf->st_mtime = oldStyleStatBuffer.st_mtime;
- buf->st_ctime = oldStyleStatBuffer.st_ctime;
-#ifdef HAVE_ST_BLOCKS
- buf->st_blksize = oldStyleStatBuffer.st_blksize;
- buf->st_blocks = Tcl_LongAsWide(oldStyleStatBuffer.st_blocks);
-#endif
- return retVal;
- }
-#endif /* USE_OBSOLETE_FS_HOOKS */
-
- fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSStatProc *proc = fsPtr->statProc;
- if (proc != NULL) {
- return (*proc)(pathPtr, buf);
- }
+ if (fsPtr != NULL && fsPtr->statProc != NULL) {
+ return fsPtr->statProc(pathPtr, buf);
}
Tcl_SetErrno(ENOENT);
return -1;
@@ -2009,7 +2123,7 @@ Tcl_FSStat(pathPtr, buf)
*
* Tcl_FSLstat --
*
- * This procedure replaces the library version of lstat. The appropriate
+ * This function replaces the library version of lstat. The appropriate
* function for the filesystem to which pathPtr belongs will be called.
* If no 'lstat' function is listed, but a 'stat' function is, then Tcl
* will fall back on the stat function.
@@ -2024,20 +2138,18 @@ Tcl_FSStat(pathPtr, buf)
*/
int
-Tcl_FSLstat(pathPtr, buf)
- Tcl_Obj *pathPtr; /* Path of file to stat (in current CP). */
- Tcl_StatBuf *buf; /* Filled with results of stat call. */
+Tcl_FSLstat(
+ Tcl_Obj *pathPtr, /* Path of file to stat (in current CP). */
+ Tcl_StatBuf *buf) /* Filled with results of stat call. */
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
if (fsPtr != NULL) {
- Tcl_FSLstatProc *proc = fsPtr->lstatProc;
- if (proc != NULL) {
- return (*proc)(pathPtr, buf);
- } else {
- Tcl_FSStatProc *sproc = fsPtr->statProc;
- if (sproc != NULL) {
- return (*sproc)(pathPtr, buf);
- }
+ if (fsPtr->lstatProc != NULL) {
+ return fsPtr->lstatProc(pathPtr, buf);
+ }
+ if (fsPtr->statProc != NULL) {
+ return fsPtr->statProc(pathPtr, buf);
}
}
Tcl_SetErrno(ENOENT);
@@ -2049,9 +2161,8 @@ Tcl_FSLstat(pathPtr, buf)
*
* Tcl_FSAccess --
*
- * This procedure replaces the library version of access. The
- * appropriate function for the filesystem to which pathPtr belongs will
- * be called.
+ * This function replaces the library version of access. The appropriate
+ * function for the filesystem to which pathPtr belongs will be called.
*
* Results:
* See access documentation.
@@ -2063,55 +2174,15 @@ Tcl_FSLstat(pathPtr, buf)
*/
int
-Tcl_FSAccess(pathPtr, mode)
- Tcl_Obj *pathPtr; /* Path of file to access (in current CP). */
- int mode; /* Permission setting. */
+Tcl_FSAccess(
+ Tcl_Obj *pathPtr, /* Path of file to access (in current CP). */
+ int mode) /* Permission setting. */
{
- Tcl_Filesystem *fsPtr;
-#ifdef USE_OBSOLETE_FS_HOOKS
- int retVal = -1;
-
- /*
- * Call each of the "access" function in succession. A non-return value
- * of -1 indicates the particular function has succeeded.
- */
-
- Tcl_MutexLock(&obsoleteFsHookMutex);
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (accessProcList != NULL) {
- AccessProc *accessProcPtr;
- char *path;
- Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
- if (transPtr == NULL) {
- path = NULL;
- } else {
- path = Tcl_GetString(transPtr);
- }
-
- accessProcPtr = accessProcList;
- while ((retVal == -1) && (accessProcPtr != NULL)) {
- retVal = (*accessProcPtr->proc)(path, mode);
- accessProcPtr = accessProcPtr->nextPtr;
- }
- if (transPtr != NULL) {
- Tcl_DecrRefCount(transPtr);
- }
- }
-
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
- if (retVal != -1) {
- return retVal;
- }
-#endif /* USE_OBSOLETE_FS_HOOKS */
-
- fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSAccessProc *proc = fsPtr->accessProc;
- if (proc != NULL) {
- return (*proc)(pathPtr, mode);
- }
+ if (fsPtr != NULL && fsPtr->accessProc != NULL) {
+ return fsPtr->accessProc(pathPtr, mode);
}
-
Tcl_SetErrno(ENOENT);
return -1;
}
@@ -2135,54 +2206,19 @@ Tcl_FSAccess(pathPtr, mode)
*/
Tcl_Channel
-Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions)
- Tcl_Interp *interp; /* Interpreter for error reporting; can be
+Tcl_FSOpenFileChannel(
+ Tcl_Interp *interp, /* Interpreter for error reporting; can be
* NULL. */
- Tcl_Obj *pathPtr; /* Name of file to open. */
- CONST char *modeString; /* A list of POSIX open modes or a string such
+ Tcl_Obj *pathPtr, /* Name of file to open. */
+ const char *modeString, /* A list of POSIX open modes or a string such
* as "rw". */
- int permissions; /* If the open involves creating a file, with
+ int permissions) /* If the open involves creating a file, with
* what modes to create it? */
{
- Tcl_Filesystem *fsPtr;
-#ifdef USE_OBSOLETE_FS_HOOKS
+ const Tcl_Filesystem *fsPtr;
Tcl_Channel retVal = NULL;
/*
- * Call each of the "Tcl_OpenFileChannel" functions in succession. A
- * non-NULL return value indicates the particular function has succeeded.
- */
-
- Tcl_MutexLock(&obsoleteFsHookMutex);
- if (openFileChannelProcList != NULL) {
- OpenFileChannelProc *openFileChannelProcPtr;
- char *path;
- Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
-
- if (transPtr == NULL) {
- path = NULL;
- } else {
- path = Tcl_GetString(transPtr);
- }
-
- openFileChannelProcPtr = openFileChannelProcList;
-
- while ((retVal == NULL) && (openFileChannelProcPtr != NULL)) {
- retVal = (*openFileChannelProcPtr->proc)(interp, path,
- modeString, permissions);
- openFileChannelProcPtr = openFileChannelProcPtr->nextPtr;
- }
- if (transPtr != NULL) {
- Tcl_DecrRefCount(transPtr);
- }
- }
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
- if (retVal != NULL) {
- return retVal;
- }
-#endif /* USE_OBSOLETE_FS_HOOKS */
-
- /*
* We need this just to ensure we return the correct error messages under
* some circumstances.
*/
@@ -2192,38 +2228,47 @@ Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions)
}
fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSOpenFileChannelProc *proc = fsPtr->openFileChannelProc;
- if (proc != NULL) {
- int mode, seekFlag, binary;
+ if (fsPtr != NULL && fsPtr->openFileChannelProc != NULL) {
+ int mode, seekFlag, binary;
- mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary);
- if (mode == -1) {
- return NULL;
- }
+ /*
+ * Parse the mode, picking up whether we want to seek to start with
+ * and/or set the channel automatically into binary mode.
+ */
- retVal = (*proc)(interp, pathPtr, mode, permissions);
- if (retVal != NULL) {
- if (seekFlag) {
- if (Tcl_Seek(retVal, (Tcl_WideInt)0,
- SEEK_END) < (Tcl_WideInt)0) {
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp,
- "could not seek to end of file while opening \"",
- Tcl_GetString(pathPtr), "\": ",
- Tcl_PosixError(interp), (char *) NULL);
- }
- Tcl_Close(NULL, retVal);
- return NULL;
- }
- }
- if (binary) {
- Tcl_SetChannelOption(interp, retVal,
- "-translation", "binary");
- }
+ mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary);
+ if (mode == -1) {
+ return NULL;
+ }
+
+ /*
+ * Do the actual open() call.
+ */
+
+ retVal = fsPtr->openFileChannelProc(interp, pathPtr, mode,
+ permissions);
+ if (retVal == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Apply appropriate flags parsed out above.
+ */
+
+ if (seekFlag && Tcl_Seek(retVal, (Tcl_WideInt) 0, SEEK_END)
+ < (Tcl_WideInt) 0) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not seek to end of file while opening \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
}
- return retVal;
+ Tcl_Close(NULL, retVal);
+ return NULL;
}
+ if (binary) {
+ Tcl_SetChannelOption(interp, retVal, "-translation", "binary");
+ }
+ return retVal;
}
/*
@@ -2232,8 +2277,9 @@ Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions)
Tcl_SetErrno(ENOENT);
if (interp != NULL) {
- Tcl_AppendResult(interp, "couldn't open \"", Tcl_GetString(pathPtr),
- "\": ", Tcl_PosixError(interp), (char *) NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't open \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
}
return NULL;
}
@@ -2243,7 +2289,7 @@ Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions)
*
* Tcl_FSUtime --
*
- * This procedure replaces the library version of utime. The appropriate
+ * This function replaces the library version of utime. The appropriate
* function for the filesystem to which pathPtr belongs will be called.
*
* Results:
@@ -2256,18 +2302,18 @@ Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions)
*/
int
-Tcl_FSUtime(pathPtr, tval)
- Tcl_Obj *pathPtr; /* File to change access/modification times */
- struct utimbuf *tval; /* Structure containing access/modification
- * times to use. Should not be modified. */
+Tcl_FSUtime(
+ Tcl_Obj *pathPtr, /* File to change access/modification
+ * times. */
+ struct utimbuf *tval) /* Structure containing access/modification
+ * times to use. Should not be modified. */
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSUtimeProc *proc = fsPtr->utimeProc;
- if (proc != NULL) {
- return (*proc)(pathPtr, tval);
- }
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
+ if (fsPtr != NULL && fsPtr->utimeProc != NULL) {
+ return fsPtr->utimeProc(pathPtr, tval);
}
+ /* TODO: set errno here? Tcl_SetErrno(ENOENT); */
return -1;
}
@@ -2276,9 +2322,9 @@ Tcl_FSUtime(pathPtr, tval)
*
* NativeFileAttrStrings --
*
- * This procedure implements the platform dependent 'file attributes'
+ * This function implements the platform dependent 'file attributes'
* subcommand, for the native filesystem, for listing the set of possible
- * attribute strings. This function is part of Tcl's native filesystem
+ * attribute strings. This function is part of Tcl's native filesystem
* support, and is placed here because it is shared by Unix and Windows
* code.
*
@@ -2291,10 +2337,10 @@ Tcl_FSUtime(pathPtr, tval)
*----------------------------------------------------------------------
*/
-static CONST char**
-NativeFileAttrStrings(pathPtr, objPtrRef)
- Tcl_Obj *pathPtr;
- Tcl_Obj** objPtrRef;
+static const char *const *
+NativeFileAttrStrings(
+ Tcl_Obj *pathPtr,
+ Tcl_Obj **objPtrRef)
{
return tclpFileAttrStrings;
}
@@ -2304,15 +2350,15 @@ NativeFileAttrStrings(pathPtr, objPtrRef)
*
* NativeFileAttrsGet --
*
- * This procedure implements the platform dependent 'file attributes'
- * subcommand, for the native filesystem, for 'get' operations. This
+ * This function implements the platform dependent 'file attributes'
+ * subcommand, for the native filesystem, for 'get' operations. This
* function is part of Tcl's native filesystem support, and is placed
* here because it is shared by Unix and Windows code.
*
* Results:
- * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK
- * was returned) is likely to have a refCount of zero. Either way we
- * must either store it somewhere (e.g. the Tcl result), or Incr/Decr its
+ * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK
+ * was returned) is likely to have a refCount of zero. Either way we must
+ * either store it somewhere (e.g. the Tcl result), or Incr/Decr its
* refCount to ensure it is properly freed.
*
* Side effects:
@@ -2322,14 +2368,13 @@ NativeFileAttrStrings(pathPtr, objPtrRef)
*/
static int
-NativeFileAttrsGet(interp, index, pathPtr, objPtrRef)
- Tcl_Interp *interp; /* The interpreter for error reporting. */
- int index; /* index of the attribute command. */
- Tcl_Obj *pathPtr; /* path of file we are operating on. */
- Tcl_Obj **objPtrRef; /* for output. */
+NativeFileAttrsGet(
+ Tcl_Interp *interp, /* The interpreter for error reporting. */
+ int index, /* index of the attribute command. */
+ Tcl_Obj *pathPtr, /* path of file we are operating on. */
+ Tcl_Obj **objPtrRef) /* for output. */
{
- return (*tclpFileAttrProcs[index].getProc)(interp, index, pathPtr,
- objPtrRef);
+ return tclpFileAttrProcs[index].getProc(interp, index, pathPtr,objPtrRef);
}
/*
@@ -2337,7 +2382,7 @@ NativeFileAttrsGet(interp, index, pathPtr, objPtrRef)
*
* NativeFileAttrsSet --
*
- * This procedure implements the platform dependent 'file attributes'
+ * This function implements the platform dependent 'file attributes'
* subcommand, for the native filesystem, for 'set' operations. This
* function is part of Tcl's native filesystem support, and is placed
* here because it is shared by Unix and Windows code.
@@ -2352,13 +2397,13 @@ NativeFileAttrsGet(interp, index, pathPtr, objPtrRef)
*/
static int
-NativeFileAttrsSet(interp, index, pathPtr, objPtr)
- Tcl_Interp *interp; /* The interpreter for error reporting. */
- int index; /* index of the attribute command. */
- Tcl_Obj *pathPtr; /* path of file we are operating on. */
- Tcl_Obj *objPtr; /* set to this value. */
+NativeFileAttrsSet(
+ Tcl_Interp *interp, /* The interpreter for error reporting. */
+ int index, /* index of the attribute command. */
+ Tcl_Obj *pathPtr, /* path of file we are operating on. */
+ Tcl_Obj *objPtr) /* set to this value. */
{
- return (*tclpFileAttrProcs[index].setProc)(interp, index, pathPtr, objPtr);
+ return tclpFileAttrProcs[index].setProc(interp, index, pathPtr, objPtr);
}
/*
@@ -2366,12 +2411,12 @@ NativeFileAttrsSet(interp, index, pathPtr, objPtr)
*
* Tcl_FSFileAttrStrings --
*
- * This procedure implements part of the hookable 'file attributes'
- * subcommand. The appropriate function for the filesystem to which
+ * This function implements part of the hookable 'file attributes'
+ * subcommand. The appropriate function for the filesystem to which
* pathPtr belongs will be called.
*
* Results:
- * The called procedure may either return an array of strings, or may
+ * The called function may either return an array of strings, or may
* instead return NULL and place a Tcl list into the given objPtrRef.
* Tcl will take that list and first increment its refCount before using
* it. On completion of that use, Tcl will decrement its refCount. Hence
@@ -2385,17 +2430,15 @@ NativeFileAttrsSet(interp, index, pathPtr, objPtr)
*----------------------------------------------------------------------
*/
-CONST char **
-Tcl_FSFileAttrStrings(pathPtr, objPtrRef)
- Tcl_Obj *pathPtr;
- Tcl_Obj **objPtrRef;
+const char *const *
+Tcl_FSFileAttrStrings(
+ Tcl_Obj *pathPtr,
+ Tcl_Obj **objPtrRef)
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSFileAttrStringsProc *proc = fsPtr->fileAttrStringsProc;
- if (proc != NULL) {
- return (*proc)(pathPtr, objPtrRef);
- }
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
+ if (fsPtr != NULL && fsPtr->fileAttrStringsProc != NULL) {
+ return fsPtr->fileAttrStringsProc(pathPtr, objPtrRef);
}
Tcl_SetErrno(ENOENT);
return NULL;
@@ -2419,14 +2462,14 @@ Tcl_FSFileAttrStrings(pathPtr, objPtrRef)
*/
int
-TclFSFileAttrIndex(pathPtr, attributeName, indexPtr)
- Tcl_Obj *pathPtr; /* File whose attributes are to be
- * indexed into. */
- CONST char *attributeName; /* The attribute being looked for. */
- int *indexPtr; /* Where to write the found index. */
+TclFSFileAttrIndex(
+ Tcl_Obj *pathPtr, /* File whose attributes are to be indexed
+ * into. */
+ const char *attributeName, /* The attribute being looked for. */
+ int *indexPtr) /* Where to write the found index. */
{
Tcl_Obj *listObj = NULL;
- CONST char **attrTable;
+ const char *const *attrTable;
/*
* Get the attribute table for the file.
@@ -2483,14 +2526,14 @@ TclFSFileAttrIndex(pathPtr, attributeName, indexPtr)
*
* Tcl_FSFileAttrsGet --
*
- * This procedure implements read access for the hookable 'file
- * attributes' subcommand. The appropriate function for the filesystem
- * to which pathPtr belongs will be called.
+ * This function implements read access for the hookable 'file
+ * attributes' subcommand. The appropriate function for the filesystem to
+ * which pathPtr belongs will be called.
*
* Results:
- * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK
- * was returned) is likely to have a refCount of zero. Either way we
- * must either store it somewhere (e.g. the Tcl result), or Incr/Decr its
+ * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK
+ * was returned) is likely to have a refCount of zero. Either way we must
+ * either store it somewhere (e.g. the Tcl result), or Incr/Decr its
* refCount to ensure it is properly freed.
*
* Side effects:
@@ -2500,18 +2543,16 @@ TclFSFileAttrIndex(pathPtr, attributeName, indexPtr)
*/
int
-Tcl_FSFileAttrsGet(interp, index, pathPtr, objPtrRef)
- Tcl_Interp *interp; /* The interpreter for error reporting. */
- int index; /* index of the attribute command. */
- Tcl_Obj *pathPtr; /* filename we are operating on. */
- Tcl_Obj **objPtrRef; /* for output. */
+Tcl_FSFileAttrsGet(
+ Tcl_Interp *interp, /* The interpreter for error reporting. */
+ int index, /* index of the attribute command. */
+ Tcl_Obj *pathPtr, /* filename we are operating on. */
+ Tcl_Obj **objPtrRef) /* for output. */
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSFileAttrsGetProc *proc = fsPtr->fileAttrsGetProc;
- if (proc != NULL) {
- return (*proc)(interp, index, pathPtr, objPtrRef);
- }
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
+ if (fsPtr != NULL && fsPtr->fileAttrsGetProc != NULL) {
+ return fsPtr->fileAttrsGetProc(interp, index, pathPtr, objPtrRef);
}
Tcl_SetErrno(ENOENT);
return -1;
@@ -2522,9 +2563,9 @@ Tcl_FSFileAttrsGet(interp, index, pathPtr, objPtrRef)
*
* Tcl_FSFileAttrsSet --
*
- * This procedure implements write access for the hookable 'file
- * attributes' subcommand. The appropriate function for the filesystem
- * to which pathPtr belongs will be called.
+ * This function implements write access for the hookable 'file
+ * attributes' subcommand. The appropriate function for the filesystem to
+ * which pathPtr belongs will be called.
*
* Results:
* Standard Tcl return code.
@@ -2536,18 +2577,16 @@ Tcl_FSFileAttrsGet(interp, index, pathPtr, objPtrRef)
*/
int
-Tcl_FSFileAttrsSet(interp, index, pathPtr, objPtr)
- Tcl_Interp *interp; /* The interpreter for error reporting. */
- int index; /* index of the attribute command. */
- Tcl_Obj *pathPtr; /* filename we are operating on. */
- Tcl_Obj *objPtr; /* Input value. */
+Tcl_FSFileAttrsSet(
+ Tcl_Interp *interp, /* The interpreter for error reporting. */
+ int index, /* index of the attribute command. */
+ Tcl_Obj *pathPtr, /* filename we are operating on. */
+ Tcl_Obj *objPtr) /* Input value. */
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSFileAttrsSetProc *proc = fsPtr->fileAttrsSetProc;
- if (proc != NULL) {
- return (*proc)(interp, index, pathPtr, objPtr);
- }
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
+ if (fsPtr != NULL && fsPtr->fileAttrsSetProc != NULL) {
+ return fsPtr->fileAttrsSetProc(interp, index, pathPtr, objPtr);
}
Tcl_SetErrno(ENOENT);
return -1;
@@ -2560,10 +2599,10 @@ Tcl_FSFileAttrsSet(interp, index, pathPtr, objPtr)
*
* This function replaces the library version of getcwd().
*
- * Most VFS's will *not* implement a 'cwdProc'. Tcl now maintains its
- * own record (in a Tcl_Obj) of the cwd, and an attempt is made to
- * synchronise this with the cwd's containing filesystem, if that
- * filesystem provides a cwdProc (e.g. the native filesystem).
+ * Most VFS's will *not* implement a 'cwdProc'. Tcl now maintains its own
+ * record (in a Tcl_Obj) of the cwd, and an attempt is made to synch this
+ * with the cwd's containing filesystem, if that filesystem provides a
+ * cwdProc (e.g. the native filesystem).
*
* Note that if Tcl's cwd is not in the native filesystem, then of course
* Tcl's cwd and the native cwd are different: extensions should
@@ -2580,10 +2619,10 @@ Tcl_FSFileAttrsSet(interp, index, pathPtr, objPtr)
*
* Results:
* The result is a pointer to a Tcl_Obj specifying the current directory,
- * or NULL if the current directory could not be determined. If NULL is
+ * or NULL if the current directory could not be determined. If NULL is
* returned, an error message is left in the interp's result.
*
- * The result already has its refCount incremented for the caller. When
+ * The result already has its refCount incremented for the caller. When
* it is no longer needed, that refCount should be decremented.
*
* Side effects:
@@ -2592,74 +2631,78 @@ Tcl_FSFileAttrsSet(interp, index, pathPtr, objPtr)
*----------------------------------------------------------------------
*/
-Tcl_Obj*
-Tcl_FSGetCwd(interp)
- Tcl_Interp *interp;
+Tcl_Obj *
+Tcl_FSGetCwd(
+ Tcl_Interp *interp)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
if (TclFSCwdPointerEquals(NULL)) {
FilesystemRecord *fsRecPtr;
Tcl_Obj *retVal = NULL;
/*
- * We've never been called before, try to find a cwd. Call each of
- * the "Tcl_GetCwd" function in succession. A non-NULL return value
+ * We've never been called before, try to find a cwd. Call each of the
+ * "Tcl_GetCwd" function in succession. A non-NULL return value
* indicates the particular function has succeeded.
*/
fsRecPtr = FsGetFirstFilesystem();
- while ((retVal == NULL) && (fsRecPtr != NULL)) {
- Tcl_FSGetCwdProc *proc = fsRecPtr->fsPtr->getCwdProc;
- if (proc != NULL) {
- if (fsRecPtr->fsPtr->version != TCL_FILESYSTEM_VERSION_1) {
- ClientData retCd;
- TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)proc;
-
- retCd = (*proc2)(NULL);
- if (retCd != NULL) {
- Tcl_Obj *norm;
- /* Looks like a new current directory */
- retVal = (*fsRecPtr->fsPtr->internalToNormalizedProc)(
- retCd);
- Tcl_IncrRefCount(retVal);
- norm = TclFSNormalizeAbsolutePath(interp,retVal,NULL);
- if (norm != NULL) {
- /*
- * We found a cwd, which is now in our global
- * storage. We must make a copy. Norm already has
- * a refCount of 1.
- *
- * Threading issue: note that multiple threads at
- * system startup could in principle call this
- * procedure simultaneously. They will therefore
- * each set the cwdPathPtr independently. That
- * behaviour is a bit peculiar, but should be
- * fine. Once we have a cwd, we'll always be in
- * the 'else' branch below which is simpler.
- */
-
- FsUpdateCwd(norm, retCd);
- Tcl_DecrRefCount(norm);
- } else {
- (*fsRecPtr->fsPtr->freeInternalRepProc)(retCd);
- }
- Tcl_DecrRefCount(retVal);
- retVal = NULL;
- goto cdDidNotChange;
- } else {
- if (interp != NULL) {
- Tcl_AppendResult(interp,
- "error getting working directory name: ",
- Tcl_PosixError(interp), (char *) NULL);
- }
- }
+ Claim();
+ for (; (retVal == NULL) && (fsRecPtr != NULL);
+ fsRecPtr = fsRecPtr->nextPtr) {
+ ClientData retCd;
+ TclFSGetCwdProc2 *proc2;
+ if (fsRecPtr->fsPtr->getCwdProc == NULL) {
+ continue;
+ }
+
+ if (fsRecPtr->fsPtr->version == TCL_FILESYSTEM_VERSION_1) {
+ retVal = fsRecPtr->fsPtr->getCwdProc(interp);
+ continue;
+ }
+
+ proc2 = (TclFSGetCwdProc2 *) fsRecPtr->fsPtr->getCwdProc;
+ retCd = proc2(NULL);
+ if (retCd != NULL) {
+ Tcl_Obj *norm;
+
+ /*
+ * Looks like a new current directory.
+ */
+
+ retVal = fsRecPtr->fsPtr->internalToNormalizedProc(retCd);
+ Tcl_IncrRefCount(retVal);
+ norm = TclFSNormalizeAbsolutePath(interp,retVal);
+ if (norm != NULL) {
+ /*
+ * We found a cwd, which is now in our global storage. We
+ * must make a copy. Norm already has a refCount of 1.
+ *
+ * Threading issue: note that multiple threads at system
+ * startup could in principle call this function
+ * simultaneously. They will therefore each set the
+ * cwdPathPtr independently. That behaviour is a bit
+ * peculiar, but should be fine. Once we have a cwd, we'll
+ * always be in the 'else' branch below which is simpler.
+ */
+
+ FsUpdateCwd(norm, retCd);
+ Tcl_DecrRefCount(norm);
} else {
- retVal = (*proc)(interp);
+ fsRecPtr->fsPtr->freeInternalRepProc(retCd);
}
+ Tcl_DecrRefCount(retVal);
+ retVal = NULL;
+ Disclaim();
+ goto cdDidNotChange;
+ } else if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "error getting working directory name: %s",
+ Tcl_PosixError(interp)));
}
- fsRecPtr = fsRecPtr->nextPtr;
}
+ Disclaim();
/*
* Now the 'cwd' may NOT be normalized, at least on some platforms.
@@ -2671,21 +2714,23 @@ Tcl_FSGetCwd(interp)
*/
if (retVal != NULL) {
- Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, retVal, NULL);
+ Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, retVal);
+
if (norm != NULL) {
/*
- * We found a cwd, which is now in our global storage. We
- * must make a copy. Norm already has a refCount of 1.
+ * We found a cwd, which is now in our global storage. We must
+ * make a copy. Norm already has a refCount of 1.
*
* Threading issue: note that multiple threads at system
- * startup could in principle call this procedure
- * simultaneously. They will therefore each set the
- * cwdPathPtr independently. That behaviour is a bit
- * peculiar, but should be fine. Once we have a cwd, we'll
- * always be in the 'else' branch below which is simpler.
+ * startup could in principle call this function
+ * simultaneously. They will therefore each set the cwdPathPtr
+ * independently. That behaviour is a bit peculiar, but should
+ * be fine. Once we have a cwd, we'll always be in the 'else'
+ * branch below which is simpler.
*/
ClientData cd = (ClientData) Tcl_FSGetNativePath(norm);
+
FsUpdateCwd(norm, TclNativeDupInternalRep(cd));
Tcl_DecrRefCount(norm);
}
@@ -2695,106 +2740,116 @@ Tcl_FSGetCwd(interp)
/*
* We already have a cwd cached, but we want to give the filesystem it
* is in a chance to check whether that cwd has changed, or is perhaps
- * no longer accessible. This allows an error to be thrown if, say,
+ * no longer accessible. This allows an error to be thrown if, say,
* the permissions on that directory have changed.
*/
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(tsdPtr->cwdPathPtr);
+ const Tcl_Filesystem *fsPtr =
+ Tcl_FSGetFileSystemForPath(tsdPtr->cwdPathPtr);
+ ClientData retCd = NULL;
+ Tcl_Obj *retVal, *norm;
/*
* If the filesystem couldn't be found, or if no cwd function exists
* for this filesystem, then we simply assume the cached cwd is ok.
* If we do call a cwd, we must watch for errors (if the cwd returns
- * NULL). This ensures that, say, on Unix if the permissions of the
+ * NULL). This ensures that, say, on Unix if the permissions of the
* cwd change, 'pwd' does actually throw the correct error in Tcl.
* (This is tested for in the test suite on unix).
*/
- if (fsPtr != NULL) {
- Tcl_FSGetCwdProc *proc = fsPtr->getCwdProc;
- ClientData retCd = NULL;
- if (proc != NULL) {
- Tcl_Obj *retVal;
- if (fsPtr->version != TCL_FILESYSTEM_VERSION_1) {
- TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)proc;
-
- retCd = (*proc2)(tsdPtr->cwdClientData);
- if (retCd == NULL && interp != NULL) {
- Tcl_AppendResult(interp,
- "error getting working directory name: ",
- Tcl_PosixError(interp), (char *) NULL);
- }
+ if (fsPtr == NULL || fsPtr->getCwdProc == NULL) {
+ goto cdDidNotChange;
+ }
- if (retCd == tsdPtr->cwdClientData) {
- goto cdDidNotChange;
- }
+ if (fsPtr->version == TCL_FILESYSTEM_VERSION_1) {
+ retVal = fsPtr->getCwdProc(interp);
+ } else {
+ /*
+ * New API.
+ */
- /*
- * Looks like a new current directory.
- */
+ TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2 *) fsPtr->getCwdProc;
- retVal = (*fsPtr->internalToNormalizedProc)(retCd);
- Tcl_IncrRefCount(retVal);
- } else {
- retVal = (*proc)(interp);
- }
- if (retVal != NULL) {
- Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp,
- retVal, NULL);
+ retCd = proc2(tsdPtr->cwdClientData);
+ if (retCd == NULL && interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "error getting working directory name: %s",
+ Tcl_PosixError(interp)));
+ }
- /*
- * Check whether cwd has changed from the value previously
- * stored in cwdPathPtr. Really 'norm' shouldn't be null,
- * but we are careful.
- */
+ if (retCd == tsdPtr->cwdClientData) {
+ goto cdDidNotChange;
+ }
- if (norm == NULL) {
- /* Do nothing */
- if (retCd != NULL) {
- (*fsPtr->freeInternalRepProc)(retCd);
- }
- } else if (norm == tsdPtr->cwdPathPtr) {
- goto cdEqual;
- } else {
- /*
- * Note that both 'norm' and 'tsdPtr->cwdPathPtr' are
- * normalized paths. Therefore we can be more
- * efficient than calling 'Tcl_FSEqualPaths', and in
- * addition avoid a nasty infinite loop bug when
- * trying to normalize tsdPtr->cwdPathPtr.
- */
-
- int len1, len2;
- char *str1, *str2;
-
- str1 = Tcl_GetStringFromObj(tsdPtr->cwdPathPtr, &len1);
- str2 = Tcl_GetStringFromObj(norm, &len2);
- if ((len1 == len2) && (strcmp(str1, str2) == 0)) {
- /*
- * If the paths were equal, we can be more
- * efficient and retain the old path object which
- * will probably already be shared. In this case
- * we can simply free the normalized path we just
- * calculated.
- */
-
- cdEqual:
- Tcl_DecrRefCount(norm);
- if (retCd != NULL) {
- (*fsPtr->freeInternalRepProc)(retCd);
- }
- } else {
- FsUpdateCwd(norm, retCd);
- Tcl_DecrRefCount(norm);
- }
- }
- Tcl_DecrRefCount(retVal);
- } else {
- /* The 'cwd' function returned an error; reset the cwd */
- FsUpdateCwd(NULL, NULL);
+ /*
+ * Looks like a new current directory.
+ */
+
+ retVal = fsPtr->internalToNormalizedProc(retCd);
+ Tcl_IncrRefCount(retVal);
+ }
+
+ /*
+ * Check if the 'cwd' function returned an error; if so, reset the
+ * cwd.
+ */
+
+ if (retVal == NULL) {
+ FsUpdateCwd(NULL, NULL);
+ goto cdDidNotChange;
+ }
+
+ /*
+ * Normalize the path.
+ */
+
+ norm = TclFSNormalizeAbsolutePath(interp, retVal);
+
+ /*
+ * Check whether cwd has changed from the value previously stored in
+ * cwdPathPtr. Really 'norm' shouldn't be NULL, but we are careful.
+ */
+
+ if (norm == NULL) {
+ /* Do nothing */
+ if (retCd != NULL) {
+ fsPtr->freeInternalRepProc(retCd);
+ }
+ } else if (norm == tsdPtr->cwdPathPtr) {
+ goto cdEqual;
+ } else {
+ /*
+ * Note that both 'norm' and 'tsdPtr->cwdPathPtr' are normalized
+ * paths. Therefore we can be more efficient than calling
+ * 'Tcl_FSEqualPaths', and in addition avoid a nasty infinite loop
+ * bug when trying to normalize tsdPtr->cwdPathPtr.
+ */
+
+ int len1, len2;
+ const char *str1, *str2;
+
+ str1 = Tcl_GetStringFromObj(tsdPtr->cwdPathPtr, &len1);
+ str2 = Tcl_GetStringFromObj(norm, &len2);
+ if ((len1 == len2) && (strcmp(str1, str2) == 0)) {
+ /*
+ * If the paths were equal, we can be more efficient and
+ * retain the old path object which will probably already be
+ * shared. In this case we can simply free the normalized path
+ * we just calculated.
+ */
+
+ cdEqual:
+ Tcl_DecrRefCount(norm);
+ if (retCd != NULL) {
+ fsPtr->freeInternalRepProc(retCd);
}
+ } else {
+ FsUpdateCwd(norm, retCd);
+ Tcl_DecrRefCount(norm);
}
}
+ Tcl_DecrRefCount(retVal);
}
cdDidNotChange:
@@ -2816,20 +2871,20 @@ Tcl_FSGetCwd(interp)
* it.
*
* Results:
- * See chdir() documentation. If successful, we keep a record of the
+ * See chdir() documentation. If successful, we keep a record of the
* successful path in cwdPathPtr for subsequent calls to getcwd.
*
* Side effects:
- * See chdir() documentation. The global cwdPathPtr may change value.
+ * See chdir() documentation. The global cwdPathPtr may change value.
*
*----------------------------------------------------------------------
*/
int
-Tcl_FSChdir(pathPtr)
- Tcl_Obj *pathPtr;
+Tcl_FSChdir(
+ Tcl_Obj *pathPtr)
{
- Tcl_Filesystem *fsPtr;
+ const Tcl_Filesystem *fsPtr;
int retVal = -1;
if (Tcl_FSGetNormalizedPath(NULL, pathPtr) == NULL) {
@@ -2839,14 +2894,13 @@ Tcl_FSChdir(pathPtr)
fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
if (fsPtr != NULL) {
- Tcl_FSChdirProc *proc = fsPtr->chdirProc;
- if (proc != NULL) {
+ if (fsPtr->chdirProc != NULL) {
/*
* If this fails, an appropriate errno will have been stored using
* 'Tcl_SetErrno()'.
*/
- retVal = (*proc)(pathPtr);
+ retVal = fsPtr->chdirProc(pathPtr);
} else {
/*
* Fallback on stat-based implementation.
@@ -2856,9 +2910,9 @@ Tcl_FSChdir(pathPtr)
/*
* If the file can be stat'ed and is a directory and is readable,
- * then we can chdir. If any of these actions fail, then
+ * then we can chdir. If any of these actions fail, then
* 'Tcl_SetErrno()' should automatically have been called to set
- * an appropriate error code
+ * an appropriate error code.
*/
if ((Tcl_FSStat(pathPtr, &buf) == 0) && (S_ISDIR(buf.st_mode))
@@ -2875,22 +2929,20 @@ Tcl_FSChdir(pathPtr)
}
/*
- * The cwd changed, or an error was thrown. If an error was thrown, we
- * can just continue (and that will report the error to the user). If
- * there was no error we must assume that the cwd was actually changed to
- * the normalized value we calculated above, and we must therefore cache
- * that information.
- */
-
- /*
+ * The cwd changed, or an error was thrown. If an error was thrown, we can
+ * just continue (and that will report the error to the user). If there
+ * was no error we must assume that the cwd was actually changed to the
+ * normalized value we calculated above, and we must therefore cache that
+ * information.
+ *
* If the filesystem in question has a getCwdProc, then the correct logic
* which performs the part below is already part of the Tcl_FSGetCwd()
- * call, so no need to replicate it again. This will have a side effect
- * though. The private authoritative representation of the current
- * working directory stored in cwdPathPtr in static memory will be
- * out-of-sync with the real OS-maintained value. The first call to
- * Tcl_FSGetCwd will however recalculate the private copy to match the
- * OS-value so everything will work right.
+ * call, so no need to replicate it again. This will have a side effect
+ * though. The private authoritative representation of the current working
+ * directory stored in cwdPathPtr in static memory will be out-of-sync
+ * with the real OS-maintained value. The first call to Tcl_FSGetCwd will
+ * however recalculate the private copy to match the OS-value so
+ * everything will work right.
*
* However, if there is no getCwdProc, then we _must_ update our private
* storage of the cwd, since this is the only opportunity to do that!
@@ -2904,8 +2956,8 @@ Tcl_FSChdir(pathPtr)
/*
* Note that this normalized path may be different to what we found
* above (or at least a different object), if the filesystem epoch
- * changed recently. This can actually happen with scripted documents
- * very easily. Therefore we ask for the normalized path again (the
+ * changed recently. This can actually happen with scripted documents
+ * very easily. Therefore we ask for the normalized path again (the
* correct value will have been cached as a result of the
* Tcl_FSGetFileSystemForPath call above anyway).
*/
@@ -2921,29 +2973,33 @@ Tcl_FSChdir(pathPtr)
if (fsPtr == &tclNativeFilesystem) {
/*
* For the native filesystem, we keep a cache of the native
- * representation of the cwd. But, we want to do that for the
+ * representation of the cwd. But, we want to do that for the
* exact format that is returned by 'getcwd' (so that we can later
* compare the two representations for equality), which might not
* be exactly the same char-string as the native representation of
* the fully normalized path (e.g. on Windows there's a
- * forward-slash vs backslash difference). Hence we ask for this
- * again here. On Unix it might actually be true that we always
+ * forward-slash vs backslash difference). Hence we ask for this
+ * again here. On Unix it might actually be true that we always
* have the correct form in the native rep in which case we could
* simply use:
* cd = Tcl_FSGetNativePath(pathPtr);
- * instead. This should be examined by someone on Unix.
+ * instead. This should be examined by someone on Unix.
*/
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
ClientData cd;
+ ClientData oldcd = tsdPtr->cwdClientData;
/*
* Assumption we are using a filesystem version 2.
*/
- TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)fsPtr->getCwdProc;
- cd = (*proc2)(tsdPtr->cwdClientData);
- FsUpdateCwd(normDirName, TclNativeDupInternalRep(cd));
+ TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2 *) fsPtr->getCwdProc;
+
+ cd = proc2(oldcd);
+ if (cd != oldcd) {
+ FsUpdateCwd(normDirName, cd);
+ }
} else {
FsUpdateCwd(normDirName, NULL);
}
@@ -2958,447 +3014,589 @@ Tcl_FSChdir(pathPtr)
* Tcl_FSLoadFile --
*
* Dynamically loads a binary code file into memory and returns the
- * addresses of two procedures within that file, if they are defined.
- * The appropriate function for the filesystem to which pathPtr belongs
- * will be called.
+ * addresses of two functions within that file, if they are defined. The
+ * appropriate function for the filesystem to which pathPtr belongs will
+ * be called.
*
* Note that the native filesystem doesn't actually assume 'pathPtr' is a
- * path. Rather it assumes pathPtr is either a path or just the name
+ * path. Rather it assumes pathPtr is either a path or just the name
* (tail) of a file which can be found somewhere in the environment's
- * loadable path. This behaviour is not very compatible with virtual
+ * loadable path. This behaviour is not very compatible with virtual
* filesystems (and has other problems documented in the load man-page),
* so it is advised that full paths are always used.
*
* Results:
- * A standard Tcl completion code. If an error occurs, an error message
+ * A standard Tcl completion code. If an error occurs, an error message
* is left in the interp's result.
*
* Side effects:
- * New code suddenly appears in memory. This may later be unloaded by
+ * New code suddenly appears in memory. This may later be unloaded by
* passing the clientData to the unloadProc.
*
*----------------------------------------------------------------------
*/
int
-Tcl_FSLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr,
- handlePtr, unloadProcPtr)
- Tcl_Interp *interp; /* Used for error reporting. */
- Tcl_Obj *pathPtr; /* Name of the file containing the desired
+Tcl_FSLoadFile(
+ Tcl_Interp *interp, /* Used for error reporting. */
+ Tcl_Obj *pathPtr, /* Name of the file containing the desired
* code. */
- CONST char *sym1, *sym2; /* Names of two procedures to look up in the
+ const char *sym1, const char *sym2,
+ /* Names of two functions to look up in the
* file's symbol table. */
- Tcl_PackageInitProc **proc1Ptr, **proc2Ptr;
+ Tcl_PackageInitProc **proc1Ptr, Tcl_PackageInitProc **proc2Ptr,
/* Where to return the addresses corresponding
* to sym1 and sym2. */
- Tcl_LoadHandle *handlePtr; /* Filled with token for dynamically loaded
+ Tcl_LoadHandle *handlePtr, /* Filled with token for dynamically loaded
* file which will be passed back to
* (*unloadProcPtr)() to unload the file. */
- Tcl_FSUnloadFileProc **unloadProcPtr;
+ Tcl_FSUnloadFileProc **unloadProcPtr)
/* Filled with address of Tcl_FSUnloadFileProc
* function which should be used for this
* file. */
{
- CONST char *symbols[2];
- Tcl_PackageInitProc **procPtrs[2];
- ClientData clientData;
+ const char *symbols[3];
+ void *procPtrs[2];
int res;
- /* Initialize the arrays */
+ /*
+ * Initialize the arrays.
+ */
+
symbols[0] = sym1;
symbols[1] = sym2;
- procPtrs[0] = proc1Ptr;
- procPtrs[1] = proc2Ptr;
-
- /* Perform the load */
- res = TclLoadFile(interp, pathPtr, 2, symbols, procPtrs,
- handlePtr, &clientData, unloadProcPtr);
+ symbols[2] = NULL;
/*
- * Due to an unfortunate mis-design in Tcl 8.4 fs, when loading a shared
- * library, we don't keep the loadHandle (for TclpFindSymbol) and the
- * clientData (for the unloadProc) separately. In fact we effectively
- * throw away the loadHandle and only use the clientData. It just so
- * happens, for the native filesystem only, that these two are identical.
- *
- * This also means that the signatures Tcl_FSUnloadFileProc and
- * Tcl_FSLoadFileProc are both misleading.
+ * Perform the load.
*/
- *handlePtr = (Tcl_LoadHandle) clientData;
+ res = Tcl_LoadFile(interp, pathPtr, symbols, 0, procPtrs, handlePtr);
+ if (res == TCL_OK) {
+ *proc1Ptr = (Tcl_PackageInitProc *) procPtrs[0];
+ *proc2Ptr = (Tcl_PackageInitProc *) procPtrs[1];
+ } else {
+ *proc1Ptr = *proc2Ptr = NULL;
+ }
+
return res;
}
/*
*----------------------------------------------------------------------
*
- * TclLoadFile --
+ * Tcl_LoadFile --
*
* Dynamically loads a binary code file into memory and returns the
- * addresses of a number of given procedures within that file, if they
- * are defined. The appropriate function for the filesystem to which
- * pathPtr belongs will be called.
+ * addresses of a number of given functions within that file, if they are
+ * defined. The appropriate function for the filesystem to which pathPtr
+ * belongs will be called.
*
* Note that the native filesystem doesn't actually assume 'pathPtr' is a
- * path. Rather it assumes pathPtr is either a path or just the name
+ * path. Rather it assumes pathPtr is either a path or just the name
* (tail) of a file which can be found somewhere in the environment's
- * loadable path. This behaviour is not very compatible with virtual
+ * loadable path. This behaviour is not very compatible with virtual
* filesystems (and has other problems documented in the load man-page),
* so it is advised that full paths are always used.
*
- * This function is currently private to Tcl. It may be exported in the
- * future and its interface fixed (but we should clean up the
- * loadHandle/clientData confusion at that time -- see the above comments
- * in Tcl_FSLoadFile for details). For a public function, see
- * Tcl_FSLoadFile.
- *
* Results:
- * A standard Tcl completion code. If an error occurs, an error message
+ * A standard Tcl completion code. If an error occurs, an error message
* is left in the interp's result.
*
* Side effects:
- * New code suddenly appears in memory. This may later be unloaded by
- * passing the clientData to the unloadProc.
+ * New code suddenly appears in memory. This may later be unloaded by
+ * calling TclFS_UnloadFile.
*
*----------------------------------------------------------------------
*/
int
-TclLoadFile(interp, pathPtr, symc, symbols, procPtrs,
- handlePtr, clientDataPtr, unloadProcPtr)
- Tcl_Interp *interp; /* Used for error reporting. */
- Tcl_Obj *pathPtr; /* Name of the file containing the desired
+Tcl_LoadFile(
+ Tcl_Interp *interp, /* Used for error reporting. */
+ Tcl_Obj *pathPtr, /* Name of the file containing the desired
* code. */
- int symc; /* Number of symbols/procPtrs in the next two
- * arrays. */
- CONST char *symbols[]; /* Names of procedures to look up in the
- * file's symbol table. */
- Tcl_PackageInitProc **procPtrs[];
- /* Where to return the addresses corresponding
- * to symbols[]. */
- Tcl_LoadHandle *handlePtr; /* Filled with token for shared library
+ const char *const symbols[],/* Names of functions to look up in the file's
+ * symbol table. */
+ int flags, /* Flags */
+ void *procVPtrs, /* Where to return the addresses corresponding
+ * to symbols[]. */
+ Tcl_LoadHandle *handlePtr) /* Filled with token for shared library
* information which can be used in
* TclpFindSymbol. */
- ClientData *clientDataPtr; /* Filled with token for dynamically loaded
- * file which will be passed back to
- * (*unloadProcPtr)() to unload the file. */
- Tcl_FSUnloadFileProc **unloadProcPtr;
- /* Filled with address of Tcl_FSUnloadFileProc
- * function which should be used for this
- * file. */
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSLoadFileProc *proc = fsPtr->loadFileProc;
- Tcl_Filesystem *copyFsPtr;
- Tcl_Obj *copyToPtr;
-
- if (proc != NULL) {
- int retVal = (*proc)(interp, pathPtr, handlePtr, unloadProcPtr);
- if (retVal == TCL_OK) {
- int i;
- if (*handlePtr == NULL) {
- return TCL_ERROR;
- }
- for (i=0 ; i<symc ; i++) {
- if (symbols[i] != NULL) {
- *procPtrs[i] = TclpFindSymbol(interp, *handlePtr,
- symbols[i]);
- }
- }
+ void **procPtrs = (void **) procVPtrs;
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ const Tcl_Filesystem *copyFsPtr;
+ Tcl_FSUnloadFileProc *unloadProcPtr;
+ Tcl_Obj *copyToPtr;
+ Tcl_LoadHandle newLoadHandle = NULL;
+ Tcl_LoadHandle divertedLoadHandle = NULL;
+ Tcl_FSUnloadFileProc *newUnloadProcPtr = NULL;
+ FsDivertLoad *tvdlPtr;
+ int retVal;
+ int i;
- /*
- * Copy this across, since both are equal for the native fs.
- */
+ if (fsPtr == NULL) {
+ Tcl_SetErrno(ENOENT);
+ return TCL_ERROR;
+ }
- *clientDataPtr = (ClientData)*handlePtr;
- Tcl_ResetResult(interp);
- return TCL_OK;
- }
- if (Tcl_GetErrno() != EXDEV) {
- return retVal;
+ if (fsPtr->loadFileProc != NULL) {
+ int retVal = ((Tcl_FSLoadFileProc2 *)(fsPtr->loadFileProc))
+ (interp, pathPtr, handlePtr, &unloadProcPtr, flags);
+
+ if (retVal == TCL_OK) {
+ if (*handlePtr == NULL) {
+ return TCL_ERROR;
}
+ Tcl_ResetResult(interp);
+ goto resolveSymbols;
+ }
+ if (Tcl_GetErrno() != EXDEV) {
+ return retVal;
+ }
+ }
+
+ /*
+ * The filesystem doesn't support 'load', so we fall back on the following
+ * technique:
+ *
+ * First check if it is readable -- and exists!
+ */
+
+ if (Tcl_FSAccess(pathPtr, R_OK) != 0) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't load library \"%s\": %s",
+ Tcl_GetString(pathPtr), Tcl_PosixError(interp)));
+ return TCL_ERROR;
+ }
+
+#ifdef TCL_LOAD_FROM_MEMORY
+ /*
+ * The platform supports loading code from memory, so ask for a buffer of
+ * the appropriate size, read the file into it and load the code from the
+ * buffer:
+ */
+
+ {
+ int ret, size;
+ void *buffer;
+ Tcl_StatBuf statBuf;
+ Tcl_Channel data;
+
+ ret = Tcl_FSStat(pathPtr, &statBuf);
+ if (ret < 0) {
+ goto mustCopyToTempAnyway;
}
+ size = (int) statBuf.st_size;
/*
- * The filesystem doesn't support 'load', so we fall back on the
- * following technique:
- *
- * First check if it is readable -- and exists!
+ * Tcl_Read takes an int: check that file size isn't wide.
*/
- if (Tcl_FSAccess(pathPtr, R_OK) != 0) {
- Tcl_AppendResult(interp, "couldn't load library \"",
- Tcl_GetString(pathPtr), "\": ",
- Tcl_PosixError(interp), (char *) NULL);
- return TCL_ERROR;
+ if (size != (Tcl_WideInt) statBuf.st_size) {
+ goto mustCopyToTempAnyway;
+ }
+ data = Tcl_FSOpenFileChannel(interp, pathPtr, "rb", 0666);
+ if (!data) {
+ goto mustCopyToTempAnyway;
+ }
+ buffer = TclpLoadMemoryGetBuffer(interp, size);
+ if (!buffer) {
+ Tcl_Close(interp, data);
+ goto mustCopyToTempAnyway;
+ }
+ ret = Tcl_Read(data, buffer, size);
+ Tcl_Close(interp, data);
+ ret = TclpLoadMemory(interp, buffer, size, ret, handlePtr,
+ &unloadProcPtr, flags);
+ if (ret == TCL_OK && *handlePtr != NULL) {
+ goto resolveSymbols;
}
+ }
-#ifdef TCL_LOAD_FROM_MEMORY
+ mustCopyToTempAnyway:
+ Tcl_ResetResult(interp);
+#endif /* TCL_LOAD_FROM_MEMORY */
+
+ /*
+ * Get a temporary filename to use, first to copy the file into, and then
+ * to load.
+ */
+
+ copyToPtr = TclpTempFileNameForLibrary(interp, pathPtr);
+ if (copyToPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_IncrRefCount(copyToPtr);
+
+ copyFsPtr = Tcl_FSGetFileSystemForPath(copyToPtr);
+ if ((copyFsPtr == NULL) || (copyFsPtr == fsPtr)) {
/*
- * The platform supports loading code from memory, so ask for a buffer
- * of the appropriate size, read the file into it and load the code
- * from the buffer:
+ * We already know we can't use Tcl_FSLoadFile from this filesystem,
+ * and we must avoid a possible infinite loop. Try to delete the file
+ * we probably created, and then exit.
*/
- do {
- int ret, size;
- void *buffer;
- Tcl_StatBuf statBuf;
- Tcl_Channel data;
+ Tcl_FSDeleteFile(copyToPtr);
+ Tcl_DecrRefCount(copyToPtr);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "couldn't load from current filesystem", -1));
+ return TCL_ERROR;
+ }
- ret = Tcl_FSStat(pathPtr, &statBuf);
- if (ret < 0) {
- break;
- }
- size = (int) statBuf.st_size;
+ if (TclCrossFilesystemCopy(interp, pathPtr, copyToPtr) != TCL_OK) {
+ /*
+ * Cross-platform copy failed.
+ */
- /*
- * Tcl_Read takes an int: check that file size isn't wide.
- */
+ Tcl_FSDeleteFile(copyToPtr);
+ Tcl_DecrRefCount(copyToPtr);
+ return TCL_ERROR;
+ }
- if (size != (Tcl_WideInt) statBuf.st_size) {
- break;
- }
- data = Tcl_FSOpenFileChannel(interp, pathPtr, "r", 0666);
- if (!data) {
- break;
- }
- buffer = TclpLoadMemoryGetBuffer(interp, size);
- if (!buffer) {
- Tcl_Close(interp, data);
- break;
- }
- Tcl_SetChannelOption(interp, data, "-translation", "binary");
- ret = Tcl_Read(data, buffer, size);
- Tcl_Close(interp, data);
- ret = TclpLoadMemory(interp, buffer, size, ret, handlePtr,
- unloadProcPtr);
- if (ret == TCL_OK) {
- int i;
- if (*handlePtr == NULL) {
- break;
- }
- for (i = 0;i < symc;i++) {
- if (symbols[i] != NULL) {
- *procPtrs[i] = TclpFindSymbol(interp, *handlePtr,
- symbols[i]);
- }
- }
- *clientDataPtr = (ClientData) *handlePtr;
- return TCL_OK;
- }
- } while (0);
- Tcl_ResetResult(interp);
+#ifndef _WIN32
+ /*
+ * Do we need to set appropriate permissions on the file? This may be
+ * required on some systems. On Unix we could loop over the file
+ * attributes, and set any that are called "-permissions" to 0700. However
+ * we just do this directly, like this:
+ */
+
+ {
+ int index;
+ Tcl_Obj *perm;
+
+ TclNewLiteralStringObj(perm, "0700");
+ Tcl_IncrRefCount(perm);
+ if (TclFSFileAttrIndex(copyToPtr, "-permissions", &index) == TCL_OK) {
+ Tcl_FSFileAttrsSet(NULL, index, copyToPtr, perm);
+ }
+ Tcl_DecrRefCount(perm);
+ }
#endif
+ /*
+ * We need to reset the result now, because the cross-filesystem copy may
+ * have stored the number of bytes in the result.
+ */
+
+ Tcl_ResetResult(interp);
+
+ retVal = Tcl_LoadFile(interp, copyToPtr, symbols, flags, procPtrs,
+ &newLoadHandle);
+ if (retVal != TCL_OK) {
/*
- * Get a temporary filename to use, first to copy the file into, and
- * then to load.
+ * The file didn't load successfully.
*/
- copyToPtr = TclpTempFileName();
- if (copyToPtr == NULL) {
- Tcl_AppendResult(interp, "couldn't create temporary file: ",
- Tcl_PosixError(interp), (char *) NULL);
- return TCL_ERROR;
- }
- Tcl_IncrRefCount(copyToPtr);
+ Tcl_FSDeleteFile(copyToPtr);
+ Tcl_DecrRefCount(copyToPtr);
+ return retVal;
+ }
- copyFsPtr = Tcl_FSGetFileSystemForPath(copyToPtr);
- if ((copyFsPtr == NULL) || (copyFsPtr == fsPtr)) {
- /*
- * We already know we can't use Tcl_FSLoadFile from this
- * filesystem, and we must avoid a possible infinite loop. Try to
- * delete the file we probably created, and then exit.
- */
+ /*
+ * Try to delete the file immediately - this is possible in some OSes, and
+ * avoids any worries about leaving the copy laying around on exit.
+ */
- Tcl_FSDeleteFile(copyToPtr);
- Tcl_DecrRefCount(copyToPtr);
- Tcl_AppendResult(interp, "couldn't load from current filesystem",
- (char *) NULL);
- return TCL_ERROR;
- }
+ if (Tcl_FSDeleteFile(copyToPtr) == TCL_OK) {
+ Tcl_DecrRefCount(copyToPtr);
- if (TclCrossFilesystemCopy(interp, pathPtr, copyToPtr) == TCL_OK) {
- Tcl_LoadHandle newLoadHandle = NULL;
- ClientData newClientData = NULL;
- Tcl_FSUnloadFileProc *newUnloadProcPtr = NULL;
- FsDivertLoad *tvdlPtr;
- int retVal;
+ /*
+ * We tell our caller about the real shared library which was loaded.
+ * Note that this does mean that the package list maintained by 'load'
+ * will store the original (vfs) path alongside the temporary load
+ * handle and unload proc ptr.
+ */
-#if !defined(__WIN32__)
- /*
- * Do we need to set appropriate permissions on the file? This
- * may be required on some systems. On Unix we could loop over
- * the file attributes, and set any that are called "-permissions"
- * to 0700. However, we just do this directly, like this:
- */
+ *handlePtr = newLoadHandle;
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+ }
- int index;
- Tcl_Obj* perm = Tcl_NewStringObj("0700",-1);
- Tcl_IncrRefCount(perm);
- if (TclFSFileAttrIndex(copyToPtr, "-permissions",
- &index) == TCL_OK) {
- Tcl_FSFileAttrsSet(NULL, index, copyToPtr, perm);
- }
- Tcl_DecrRefCount(perm);
-#endif
+ /*
+ * When we unload this file, we need to divert the unloading so we can
+ * unload and cleanup the temporary file correctly.
+ */
- /*
- * We need to reset the result now, because the cross- filesystem
- * copy may have stored the number of bytes in the result.
- */
+ tvdlPtr = ckalloc(sizeof(FsDivertLoad));
- Tcl_ResetResult(interp);
+ /*
+ * Remember three pieces of information. This allows us to cleanup the
+ * diverted load completely, on platforms which allow proper unloading of
+ * code.
+ */
- retVal = TclLoadFile(interp, copyToPtr, symc, symbols, procPtrs,
- &newLoadHandle, &newClientData, &newUnloadProcPtr);
- if (retVal != TCL_OK) {
- /* The file didn't load successfully */
- Tcl_FSDeleteFile(copyToPtr);
- Tcl_DecrRefCount(copyToPtr);
- return retVal;
- }
+ tvdlPtr->loadHandle = newLoadHandle;
+ tvdlPtr->unloadProcPtr = newUnloadProcPtr;
- /*
- * Try to delete the file immediately - this is possible in some
- * OSes, and avoids any worries about leaving the copy laying
- * around on exit.
- */
+ if (copyFsPtr != &tclNativeFilesystem) {
+ /*
+ * copyToPtr is already incremented for this reference.
+ */
- if (Tcl_FSDeleteFile(copyToPtr) == TCL_OK) {
- Tcl_DecrRefCount(copyToPtr);
+ tvdlPtr->divertedFile = copyToPtr;
- /*
- * We tell our caller about the real shared library which was
- * loaded. Note that this does mean that the package list
- * maintained by 'load' will store the original (vfs) path
- * alongside the temporary load handle and unload proc ptr.
- */
+ /*
+ * This is the filesystem we loaded it into. Since we have a reference
+ * to 'copyToPtr', we already have a refCount on this filesystem, so
+ * we don't need to worry about it disappearing on us.
+ */
- (*handlePtr) = newLoadHandle;
- (*clientDataPtr) = newClientData;
- (*unloadProcPtr) = newUnloadProcPtr;
- Tcl_ResetResult(interp);
- return TCL_OK;
- }
+ tvdlPtr->divertedFilesystem = copyFsPtr;
+ tvdlPtr->divertedFileNativeRep = NULL;
+ } else {
+ /*
+ * We need the native rep.
+ */
- /*
- * When we unload this file, we need to divert the unloading so we
- * can unload and cleanup the temporary file correctly.
- */
+ tvdlPtr->divertedFileNativeRep = TclNativeDupInternalRep(
+ Tcl_FSGetInternalRep(copyToPtr, copyFsPtr));
- tvdlPtr = (FsDivertLoad *) ckalloc(sizeof(FsDivertLoad));
+ /*
+ * We don't need or want references to the copied Tcl_Obj or the
+ * filesystem if it is the native one.
+ */
- /*
- * Remember three pieces of information. This allows us to
- * cleanup the diverted load completely, on platforms which allow
- * proper unloading of code.
- */
+ tvdlPtr->divertedFile = NULL;
+ tvdlPtr->divertedFilesystem = NULL;
+ Tcl_DecrRefCount(copyToPtr);
+ }
- tvdlPtr->loadHandle = newLoadHandle;
- tvdlPtr->unloadProcPtr = newUnloadProcPtr;
+ copyToPtr = NULL;
- if (copyFsPtr != &tclNativeFilesystem) {
- /* copyToPtr is already incremented for this reference */
- tvdlPtr->divertedFile = copyToPtr;
+ divertedLoadHandle = ckalloc(sizeof(struct Tcl_LoadHandle_));
+ divertedLoadHandle->clientData = tvdlPtr;
+ divertedLoadHandle->findSymbolProcPtr = DivertFindSymbol;
+ divertedLoadHandle->unloadFileProcPtr = DivertUnloadFile;
+ *handlePtr = divertedLoadHandle;
- /*
- * This is the filesystem we loaded it into. Since we have a
- * reference to 'copyToPtr', we already have a refCount on
- * this filesystem, so we don't need to worry about it
- * disappearing on us.
- */
+ Tcl_ResetResult(interp);
+ return retVal;
- tvdlPtr->divertedFilesystem = copyFsPtr;
- tvdlPtr->divertedFileNativeRep = NULL;
- } else {
- /* We need the native rep */
- tvdlPtr->divertedFileNativeRep = TclNativeDupInternalRep(
- Tcl_FSGetInternalRep(copyToPtr, copyFsPtr));
+ resolveSymbols:
+ /*
+ * At this point, *handlePtr is already set up to the handle for the
+ * loaded library. We now try to resolve the symbols.
+ */
- /*
- * We don't need or want references to the copied Tcl_Obj or
- * the filesystem if it is the native one.
+ if (symbols != NULL) {
+ for (i=0 ; symbols[i] != NULL; i++) {
+ procPtrs[i] = Tcl_FindSymbol(interp, *handlePtr, symbols[i]);
+ if (procPtrs[i] == NULL) {
+ /*
+ * At least one symbol in the list was not found. Unload the
+ * file, and report the problem back to the caller.
+ * (Tcl_FindSymbol should already have left an appropriate
+ * error message.)
*/
- tvdlPtr->divertedFile = NULL;
- tvdlPtr->divertedFilesystem = NULL;
- Tcl_DecrRefCount(copyToPtr);
+ (*handlePtr)->unloadFileProcPtr(*handlePtr);
+ *handlePtr = NULL;
+ return TCL_ERROR;
}
-
- copyToPtr = NULL;
- (*handlePtr) = newLoadHandle;
- (*clientDataPtr) = (ClientData) tvdlPtr;
- (*unloadProcPtr) = &FSUnloadTempFile;
- Tcl_ResetResult(interp);
- return retVal;
-
- } else {
- /*
- * Cross-platform copy failed.
- */
-
- Tcl_FSDeleteFile(copyToPtr);
- Tcl_DecrRefCount(copyToPtr);
- return TCL_ERROR;
}
}
- Tcl_SetErrno(ENOENT);
- return TCL_ERROR;
+ return TCL_OK;
}
+
/*
- * This function used to be in the platform specific directories, but it has
- * now been made to work cross-platform
+ *----------------------------------------------------------------------
+ *
+ * DivertFindSymbol --
+ *
+ * Find a symbol in a shared library loaded by copy-from-VFS.
+ *
+ *----------------------------------------------------------------------
*/
-int
-TclpLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr,
- clientDataPtr, unloadProcPtr)
- Tcl_Interp *interp; /* Used for error reporting. */
- Tcl_Obj *pathPtr; /* Name of the file containing the desired
- * code (UTF-8). */
- CONST char *sym1, *sym2; /* Names of two procedures to look up in the
- * file's symbol table. */
- Tcl_PackageInitProc **proc1Ptr, **proc2Ptr;
- /* Where to return the addresses corresponding
- * to sym1 and sym2. */
- ClientData *clientDataPtr; /* Filled with token for dynamically loaded
- * file which will be passed back to
- * (*unloadProcPtr)() to unload the file. */
- Tcl_FSUnloadFileProc **unloadProcPtr;
- /* Filled with address of Tcl_FSUnloadFileProc
- * function which should be used for this
- * file. */
+static void *
+DivertFindSymbol(
+ Tcl_Interp *interp, /* Tcl interpreter */
+ Tcl_LoadHandle loadHandle, /* Handle to the diverted module */
+ const char *symbol) /* Symbol to resolve */
{
- Tcl_LoadHandle handle = NULL;
- int res;
+ FsDivertLoad *tvdlPtr = (FsDivertLoad *) loadHandle->clientData;
+ Tcl_LoadHandle originalHandle = tvdlPtr->loadHandle;
+
+ return originalHandle->findSymbolProcPtr(interp, originalHandle, symbol);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DivertUnloadFile --
+ *
+ * Unloads a file that has been loaded by copying from VFS to the native
+ * filesystem.
+ *
+ * Parameters:
+ * loadHandle -- Handle of the file to unload
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DivertUnloadFile(
+ Tcl_LoadHandle loadHandle)
+{
+ FsDivertLoad *tvdlPtr = (FsDivertLoad *) loadHandle->clientData;
+ Tcl_LoadHandle originalHandle;
- res = TclpDlopen(interp, pathPtr, &handle, unloadProcPtr);
+ /*
+ * This test should never trigger, since we give the client data in the
+ * function above.
+ */
- if (res != TCL_OK) {
- return res;
+ if (tvdlPtr == NULL) {
+ return;
}
+ originalHandle = tvdlPtr->loadHandle;
- if (handle == NULL) {
- return TCL_ERROR;
+ /*
+ * Call the real 'unloadfile' proc we actually used. It is very important
+ * that we call this first, so that the shared library is actually
+ * unloaded by the OS. Otherwise, the following 'delete' may well fail
+ * because the shared library is still in use.
+ */
+
+ originalHandle->unloadFileProcPtr(originalHandle);
+
+ /*
+ * What filesystem contains the temp copy of the library?
+ */
+
+ if (tvdlPtr->divertedFilesystem == NULL) {
+ /*
+ * It was the native filesystem, and we have a special function
+ * available just for this purpose, which we know works even at this
+ * late stage.
+ */
+
+ TclpDeleteFile(tvdlPtr->divertedFileNativeRep);
+ NativeFreeInternalRep(tvdlPtr->divertedFileNativeRep);
+ } else {
+ /*
+ * Remove the temporary file we created. Note, we may crash here
+ * because encodings have been taken down already.
+ */
+
+ if (tvdlPtr->divertedFilesystem->deleteFileProc(tvdlPtr->divertedFile)
+ != TCL_OK) {
+ /*
+ * The above may have failed because the filesystem, or something
+ * it depends upon (e.g. encodings) have been taken down because
+ * Tcl is exiting.
+ *
+ * We may need to work out how to delete this file more robustly
+ * (or give the filesystem the information it needs to delete the
+ * file more robustly).
+ *
+ * In particular, one problem might be that the filesystem cannot
+ * extract the information it needs from the above path object
+ * because Tcl's entire filesystem apparatus (the code in this
+ * file) has been finalized, and it refuses to pass the internal
+ * representation to the filesystem.
+ */
+ }
+
+ /*
+ * And free up the allocations. This will also of course remove a
+ * refCount from the Tcl_Filesystem to which this file belongs, which
+ * could then free up the filesystem if we are exiting.
+ */
+
+ Tcl_DecrRefCount(tvdlPtr->divertedFile);
}
- *clientDataPtr = (ClientData)handle;
+ ckfree(tvdlPtr);
+ ckfree(loadHandle);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FindSymbol --
+ *
+ * Find a symbol in a loaded library
+ *
+ * Results:
+ * Returns a pointer to the symbol if found. If not found, returns NULL
+ * and leaves an error message in the interpreter result.
+ *
+ * This function was once filesystem-specific, but has been made portable by
+ * having TclpDlopen return a structure that includes procedure pointers.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void *
+Tcl_FindSymbol(
+ Tcl_Interp *interp, /* Tcl interpreter */
+ Tcl_LoadHandle loadHandle, /* Handle to the loaded library */
+ const char *symbol) /* Name of the symbol to resolve */
+{
+ return loadHandle->findSymbolProcPtr(interp, loadHandle, symbol);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSUnloadFile --
+ *
+ * Unloads a library given its handle. Checks first that the library
+ * supports unloading.
+ *
+ *----------------------------------------------------------------------
+ */
- *proc1Ptr = TclpFindSymbol(interp, handle, sym1);
- *proc2Ptr = TclpFindSymbol(interp, handle, sym2);
+int
+Tcl_FSUnloadFile(
+ Tcl_Interp *interp, /* Tcl interpreter */
+ Tcl_LoadHandle handle) /* Handle of the file to unload */
+{
+ if (handle->unloadFileProcPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "cannot unload: filesystem does not support unloading",
+ -1));
+ }
+ return TCL_ERROR;
+ }
+ TclpUnloadFile(handle);
return TCL_OK;
}
/*
- *---------------------------------------------------------------------------
+ *----------------------------------------------------------------------
*
- * FSUnloadTempFile --
+ * TclpUnloadFile --
+ *
+ * Unloads a library given its handle
+ *
+ * This function was once filesystem-specific, but has been made portable by
+ * having TclpDlopen return a structure that includes procedure pointers.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpUnloadFile(
+ Tcl_LoadHandle handle)
+{
+ if (handle->unloadFileProcPtr != NULL) {
+ handle->unloadFileProcPtr(handle);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclFSUnloadTempFile --
*
* This function is called when we loaded a library of code via an
- * intermediate temporary file. This function ensures the library is
+ * intermediate temporary file. This function ensures the library is
* correctly unloaded and the temporary file is correctly deleted.
*
* Results:
@@ -3408,32 +3606,35 @@ TclpLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr,
* The effects of the 'unload' function called, and of course the
* temporary file will be deleted.
*
- *---------------------------------------------------------------------------
+ *----------------------------------------------------------------------
*/
-static void
-FSUnloadTempFile(loadHandle)
- Tcl_LoadHandle loadHandle; /* loadHandle returned by a previous call to
- * Tcl_FSLoadFile(). The loadHandle is a token
- * that represents the loaded file. */
+
+void
+TclFSUnloadTempFile(
+ Tcl_LoadHandle loadHandle) /* loadHandle returned by a previous call to
+ * Tcl_FSLoadFile(). The loadHandle is a token
+ * that represents the loaded file. */
{
- FsDivertLoad *tvdlPtr = (FsDivertLoad*)loadHandle;
+ FsDivertLoad *tvdlPtr = (FsDivertLoad *) loadHandle;
/*
* This test should never trigger, since we give the client data in the
* function above.
*/
- if (tvdlPtr == NULL) { return; }
+ if (tvdlPtr == NULL) {
+ return;
+ }
/*
* Call the real 'unloadfile' proc we actually used. It is very important
* that we call this first, so that the shared library is actually
- * unloaded by the OS. Otherwise, the following 'delete' may well fail
+ * unloaded by the OS. Otherwise, the following 'delete' may well fail
* because the shared library is still in use.
*/
if (tvdlPtr->unloadProcPtr != NULL) {
- (*tvdlPtr->unloadProcPtr)(tvdlPtr->loadHandle);
+ tvdlPtr->unloadProcPtr(tvdlPtr->loadHandle);
}
if (tvdlPtr->divertedFilesystem == NULL) {
@@ -3445,10 +3646,9 @@ FSUnloadTempFile(loadHandle)
TclpDeleteFile(tvdlPtr->divertedFileNativeRep);
NativeFreeInternalRep(tvdlPtr->divertedFileNativeRep);
-
} else {
/*
- * Remove the temporary file we created. Note, we may crash here
+ * Remove the temporary file we created. Note, we may crash here
* because encodings have been taken down already.
*/
@@ -3472,7 +3672,7 @@ FSUnloadTempFile(loadHandle)
}
/*
- * And free up the allocations. This will also of course remove a
+ * And free up the allocations. This will also of course remove a
* refCount from the Tcl_Filesystem to which this file belongs, which
* could then free up the filesystem if we are exiting.
*/
@@ -3480,7 +3680,7 @@ FSUnloadTempFile(loadHandle)
Tcl_DecrRefCount(tvdlPtr->divertedFile);
}
- ckfree((char*)tvdlPtr);
+ ckfree(tvdlPtr);
}
/*
@@ -3489,18 +3689,18 @@ FSUnloadTempFile(loadHandle)
* Tcl_FSLink --
*
* This function replaces the library version of readlink() and can also
- * be used to make links. The appropriate function for the filesystem to
+ * be used to make links. The appropriate function for the filesystem to
* which pathPtr belongs will be called.
*
* Results:
* 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
+ * 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 action was
- * successful, or NULL if not. In this case the result has no additional
- * reference count, and need not be freed. The actual action to perform
+ * successful, or NULL if not. In this case the result has no additional
+ * reference count, and need not be freed. The actual action to perform
* is given by the 'linkAction' flags, which is an or'd combination of:
*
* TCL_CREATE_SYMBOLIC_LINK
@@ -3511,35 +3711,33 @@ FSUnloadTempFile(loadHandle)
* is in the same FS as pathPtr.
*
* Side effects:
- * See readlink() documentation. A new filesystem link object may appear
+ * See readlink() documentation. A new filesystem link object may appear.
*
*---------------------------------------------------------------------------
*/
Tcl_Obj *
-Tcl_FSLink(pathPtr, toPtr, linkAction)
- Tcl_Obj *pathPtr; /* Path of file to readlink or link */
- Tcl_Obj *toPtr; /* NULL or path to be linked to */
- int linkAction; /* Action to perform */
+Tcl_FSLink(
+ Tcl_Obj *pathPtr, /* Path of file to readlink or link. */
+ Tcl_Obj *toPtr, /* NULL or path to be linked to. */
+ int linkAction) /* Action to perform. */
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSLinkProc *proc = fsPtr->linkProc;
- if (proc != NULL) {
- return (*proc)(pathPtr, toPtr, linkAction);
- }
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
+ if (fsPtr != NULL && fsPtr->linkProc != NULL) {
+ return fsPtr->linkProc(pathPtr, toPtr, linkAction);
}
/*
* If S_IFLNK isn't defined it means that the machine doesn't support
- * symbolic links, so the file can't possibly be a symbolic link.
- * Generate an EINVAL error, which is what happens on machines that do
- * support symbolic links when you invoke readlink on a file that isn't a
- * symbolic link.
+ * symbolic links, so the file can't possibly be a symbolic link. Generate
+ * an EINVAL error, which is what happens on machines that do support
+ * symbolic links when you invoke readlink on a file that isn't a symbolic
+ * link.
*/
#ifndef S_IFLNK
- errno = EINVAL;
+ errno = EINVAL; /* TODO: Change to Tcl_SetErrno()? */
#else
Tcl_SetErrno(ENOENT);
#endif /* S_IFLNK */
@@ -3551,15 +3749,15 @@ Tcl_FSLink(pathPtr, toPtr, linkAction)
*
* Tcl_FSListVolumes --
*
- * Lists the currently mounted volumes. The chain of functions that have
+ * 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.
*
* Notice that we assume the lists returned by each filesystem (if non
- * NULL) have been given a refCount for us already. However, we are NOT
+ * NULL) have been given a refCount for us already. However, we are NOT
* allowed to hang on to the list itself (it belongs to the filesystem we
- * called). Therefore we quite naturally add its contents to the result
+ * called). Therefore we quite naturally add its contents to the result
* we are building, and then decrement the refCount.
*
* Results:
@@ -3571,24 +3769,25 @@ Tcl_FSLink(pathPtr, toPtr, linkAction)
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
+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 succeeded. We call
+ * Call each of the "listVolumes" function in succession. A non-NULL
+ * return value indicates the particular function has succeeded. We call
* all the functions registered, since we want a list of all drives from
* all filesystems.
*/
fsRecPtr = FsGetFirstFilesystem();
+ Claim();
while (fsRecPtr != NULL) {
- Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc;
- if (proc != NULL) {
- Tcl_Obj *thisFsVolumes = (*proc)();
+ if (fsRecPtr->fsPtr->listVolumesProc != NULL) {
+ Tcl_Obj *thisFsVolumes = fsRecPtr->fsPtr->listVolumesProc();
+
if (thisFsVolumes != NULL) {
Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes);
Tcl_DecrRefCount(thisFsVolumes);
@@ -3596,6 +3795,7 @@ Tcl_FSListVolumes(void)
}
fsRecPtr = fsRecPtr->nextPtr;
}
+ Disclaim();
return resultPtr;
}
@@ -3618,10 +3818,10 @@ Tcl_FSListVolumes(void)
*---------------------------------------------------------------------------
*/
-static Tcl_Obj*
-FsListMounts(pathPtr, pattern)
- Tcl_Obj *pathPtr; /* Contains path to directory to search. */
- CONST char *pattern; /* Pattern to match against. */
+static Tcl_Obj *
+FsListMounts(
+ Tcl_Obj *pathPtr, /* Contains path to directory to search. */
+ const char *pattern) /* Pattern to match against. */
{
FilesystemRecord *fsRecPtr;
Tcl_GlobTypeData mountsOnly = { TCL_GLOB_TYPE_MOUNT, 0, NULL, NULL };
@@ -3629,25 +3829,25 @@ FsListMounts(pathPtr, pattern)
/*
* Call each of the "matchInDirectory" functions in succession, with the
- * specific type information 'mountsOnly'. A non-NULL return value
- * indicates the particular function has succeeded. We call all the
+ * specific type information 'mountsOnly'. A non-NULL return value
+ * indicates the particular function has succeeded. We call all the
* functions registered, since we want a list from each filesystems.
*/
fsRecPtr = FsGetFirstFilesystem();
+ Claim();
while (fsRecPtr != NULL) {
- if (fsRecPtr != &nativeFilesystemRecord) {
- Tcl_FSMatchInDirectoryProc *proc =
- fsRecPtr->fsPtr->matchInDirectoryProc;
- if (proc != NULL) {
- if (resultPtr == NULL) {
- resultPtr = Tcl_NewObj();
- }
- (*proc)(NULL, resultPtr, pathPtr, pattern, &mountsOnly);
+ if (fsRecPtr->fsPtr != &tclNativeFilesystem &&
+ fsRecPtr->fsPtr->matchInDirectoryProc != NULL) {
+ if (resultPtr == NULL) {
+ resultPtr = Tcl_NewObj();
}
+ fsRecPtr->fsPtr->matchInDirectoryProc(NULL, resultPtr, pathPtr,
+ pattern, &mountsOnly);
}
fsRecPtr = fsRecPtr->nextPtr;
}
+ Disclaim();
return resultPtr;
}
@@ -3662,7 +3862,7 @@ FsListMounts(pathPtr, pattern)
* an element.
*
* Results:
- * Returns list object with refCount of zero. If the passed in lenPtr is
+ * 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.
*
@@ -3672,16 +3872,16 @@ FsListMounts(pathPtr, pattern)
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-Tcl_FSSplitPath(pathPtr, lenPtr)
- Tcl_Obj *pathPtr; /* Path to split. */
- int *lenPtr; /* int to store number of path elements. */
+Tcl_Obj *
+Tcl_FSSplitPath(
+ 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;
+ const Tcl_Filesystem *fsPtr;
char separator = '/';
int driveNameLength;
- char *p;
+ const char *p;
/*
* Perform platform specific splitting.
@@ -3701,7 +3901,8 @@ Tcl_FSSplitPath(pathPtr, lenPtr)
*/
if (fsPtr->filesystemSeparatorProc != NULL) {
- Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(pathPtr);
+ Tcl_Obj *sep = fsPtr->filesystemSeparatorProc(pathPtr);
+
if (sep != NULL) {
Tcl_IncrRefCount(sep);
separator = Tcl_GetString(sep)[0];
@@ -3710,7 +3911,7 @@ Tcl_FSSplitPath(pathPtr, lenPtr)
}
/*
- * Place the drive name as first element of the result list. The drive
+ * 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)
*/
@@ -3726,16 +3927,18 @@ Tcl_FSSplitPath(pathPtr, lenPtr)
*/
for (;;) {
- char *elementStart = p;
+ const 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);
+ TclNewLiteralStringObj(nextElt, "./");
Tcl_AppendToObj(nextElt, elementStart, length);
} else {
nextElt = Tcl_NewStringObj(elementStart, length);
@@ -3752,36 +3955,10 @@ Tcl_FSSplitPath(pathPtr, lenPtr)
*/
if (lenPtr != NULL) {
- Tcl_ListObjLength(NULL, result, lenPtr);
+ TclListObjLength(NULL, result, lenPtr);
}
return result;
}
-
-/* Simple helper function */
-Tcl_Obj*
-TclFSInternalToNormalized(fromFilesystem, clientData, fsRecPtrPtr)
- Tcl_Filesystem *fromFilesystem;
- ClientData clientData;
- FilesystemRecord **fsRecPtrPtr;
-{
- FilesystemRecord *fsRecPtr = FsGetFirstFilesystem();
-
- while (fsRecPtr != NULL) {
- if (fsRecPtr->fsPtr == fromFilesystem) {
- *fsRecPtrPtr = fsRecPtr;
- break;
- }
- fsRecPtr = fsRecPtr->nextPtr;
- }
-
- if ((fsRecPtr != NULL)
- && (fromFilesystem->internalToNormalizedProc != NULL)) {
- return (*fromFilesystem->internalToNormalizedProc)(clientData);
- } else {
- return NULL;
- }
-}
-
/*
*----------------------------------------------------------------------
*
@@ -3791,7 +3968,7 @@ TclFSInternalToNormalized(fromFilesystem, clientData, fsRecPtrPtr)
*
* Results:
* Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
- * TCL_PATH_VOLUME_RELATIVE. The filesystem reference will be set if and
+ * 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.
*
@@ -3802,26 +3979,25 @@ TclFSInternalToNormalized(fromFilesystem, clientData, fsRecPtrPtr)
*/
Tcl_PathType
-TclGetPathType(pathPtr, filesystemPtrPtr, driveNameLengthPtr, driveNameRef)
- Tcl_Obj *pathPtr; /* Path to determine type for */
- Tcl_Filesystem **filesystemPtrPtr; /* If absolute path and this is not
- * NULL, then set to the filesystem
- * which claims this path. */
- int *driveNameLengthPtr; /* If the path is absolute, and this
- * is non-NULL, then set to the length
- * of the driveName. */
- Tcl_Obj **driveNameRef; /* If the path is absolute, and this
- * is non-NULL, then set to the name
- * of the drive, network-volume which
- * contains the path, already with a
- * refCount for the caller. */
+TclGetPathType(
+ Tcl_Obj *pathPtr, /* Path to determine type for. */
+ const Tcl_Filesystem **filesystemPtrPtr,
+ /* If absolute path and this is not NULL, then
+ * set to the filesystem which claims this
+ * path. */
+ int *driveNameLengthPtr, /* If the path is absolute, and this is
+ * non-NULL, then set to the length of the
+ * driveName. */
+ Tcl_Obj **driveNameRef) /* If the path is absolute, and this is
+ * non-NULL, then set to the name of the
+ * drive, network-volume which contains the
+ * path, already with a refCount for the
+ * caller. */
{
int pathLen;
- char *path;
+ const char *path = Tcl_GetStringFromObj(pathPtr, &pathLen);
Tcl_PathType type;
- path = Tcl_GetStringFromObj(pathPtr, &pathLen);
-
type = TclFSNonnativePathType(path, pathLen, filesystemPtrPtr,
driveNameLengthPtr, driveNameRef);
@@ -3840,14 +4016,14 @@ TclGetPathType(pathPtr, filesystemPtrPtr, driveNameLengthPtr, driveNameRef)
*
* TclFSNonnativePathType --
*
- * Helper function used by TclGetPathType. Its purpose is to check
+ * Helper function used by TclGetPathType. Its purpose is to check
* whether the given path starts with a string which corresponds to a
- * file volume in any registered filesystem except the native one. For
+ * file volume in any registered filesystem except the native one. For
* speed and historical reasons the native filesystem has special
* hard-coded checks dotted here and there in the filesystem code.
*
* Results:
- * Returns one of TCL_PATH_ABSOLUTE or TCL_PATH_RELATIVE. The filesystem
+ * Returns one of TCL_PATH_ABSOLUTE or TCL_PATH_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.
*
@@ -3858,21 +4034,21 @@ TclGetPathType(pathPtr, filesystemPtrPtr, driveNameLengthPtr, driveNameRef)
*/
Tcl_PathType
-TclFSNonnativePathType(path, pathLen, filesystemPtrPtr, driveNameLengthPtr,
- driveNameRef)
- CONST char *path; /* Path to determine type for */
- int pathLen; /* Length of the path */
- Tcl_Filesystem **filesystemPtrPtr; /* If absolute path and this is not
- * NULL, then set to the filesystem
- * which claims this path. */
- int *driveNameLengthPtr; /* If the path is absolute, and this
- * is non-NULL, then set to the length
- * of the driveName. */
- Tcl_Obj **driveNameRef; /* If the path is absolute, and this
- * is non-NULL, then set to the name
- * of the drive, network-volume which
- * contains the path, already with a
- * refCount for the caller. */
+TclFSNonnativePathType(
+ const char *path, /* Path to determine type for. */
+ int pathLen, /* Length of the path. */
+ const Tcl_Filesystem **filesystemPtrPtr,
+ /* If absolute path and this is not NULL, then
+ * set to the filesystem which claims this
+ * path. */
+ int *driveNameLengthPtr, /* If the path is absolute, and this is
+ * non-NULL, then set to the length of the
+ * driveName. */
+ Tcl_Obj **driveNameRef) /* If the path is absolute, and this is
+ * non-NULL, then set to the name of the
+ * drive, network-volume which contains the
+ * path, already with a refCount for the
+ * caller. */
{
FilesystemRecord *fsRecPtr;
Tcl_PathType type = TCL_PATH_RELATIVE;
@@ -3884,39 +4060,39 @@ TclFSNonnativePathType(path, pathLen, filesystemPtrPtr, driveNameLengthPtr,
*/
fsRecPtr = FsGetFirstFilesystem();
+ Claim();
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 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.
+ * otherwise we won't necessarily pass all the Tcl testsuite - this is
+ * because some of the tests artificially change the current platform
+ * (between win, unix) but the list of volumes we get by calling
+ * fsRecPtr->fsPtr->listVolumesProc 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 one other reason to skip
- * the native filesystem --- since the tclFilename.c code has nice
- * fast 'absolute path' checkers, we don't want to waste time
- * repeating that effort here, and this function is actually called
- * quite often, so if we can save the overhead of the native
- * filesystem returning us a list of volumes all the time, it is
- * better.
+ * the native filesystem - since the tclFilename.c code has nice fast
+ * 'absolute path' checkers, we don't want to waste time repeating
+ * that effort here, and this function is actually called quite often,
+ * so if we can save the overhead of the native filesystem returning
+ * us a list of volumes all the time, it is better.
*/
- if ((fsRecPtr->fsPtr != &tclNativeFilesystem) && (proc != NULL)) {
+ if ((fsRecPtr->fsPtr != &tclNativeFilesystem)
+ && (fsRecPtr->fsPtr->listVolumesProc != NULL)) {
int numVolumes;
- Tcl_Obj *thisFsVolumes = (*proc)();
+ Tcl_Obj *thisFsVolumes = fsRecPtr->fsPtr->listVolumesProc();
+
if (thisFsVolumes != NULL) {
- if (Tcl_ListObjLength(NULL, thisFsVolumes,
- &numVolumes) != TCL_OK) {
+ if (Tcl_ListObjLength(NULL, thisFsVolumes, &numVolumes)
+ != TCL_OK) {
/*
- * This is VERY bad; the Tcl_FSListVolumesProc 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'.
+ * This is VERY bad; the listVolumesProc 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 Tcl_Panic seems a bit excessive).
@@ -3927,7 +4103,7 @@ TclFSNonnativePathType(path, pathLen, filesystemPtrPtr, driveNameLengthPtr,
while (numVolumes > 0) {
Tcl_Obj *vol;
int len;
- char *strVol;
+ const char *strVol;
numVolumes--;
Tcl_ListObjIndex(NULL, thisFsVolumes, numVolumes, &vol);
@@ -3952,13 +4128,17 @@ TclFSNonnativePathType(path, pathLen, filesystemPtrPtr, driveNameLengthPtr,
}
Tcl_DecrRefCount(thisFsVolumes);
if (type == TCL_PATH_ABSOLUTE) {
- /* We don't need to examine any more filesystems */
+ /*
+ * We don't need to examine any more filesystems.
+ */
+
break;
}
}
}
fsRecPtr = fsRecPtr->nextPtr;
}
+ Disclaim();
return type;
}
@@ -3968,7 +4148,7 @@ TclFSNonnativePathType(path, pathLen, filesystemPtrPtr, driveNameLengthPtr,
* Tcl_FSRenameFile --
*
* If the two paths given belong to the same filesystem, we call that
- * filesystems rename function. Otherwise we simply return the posix
+ * filesystems rename function. Otherwise we simply return the POSIX
* error 'EXDEV', and -1.
*
* Results:
@@ -3981,22 +4161,21 @@ TclFSNonnativePathType(path, pathLen, filesystemPtrPtr, driveNameLengthPtr,
*/
int
-Tcl_FSRenameFile(srcPathPtr, destPathPtr)
- Tcl_Obj* srcPathPtr; /* Pathname of file or dir to be renamed
+Tcl_FSRenameFile(
+ Tcl_Obj *srcPathPtr, /* Pathname of file or dir to be renamed
* (UTF-8). */
- Tcl_Obj *destPathPtr; /* New pathname of file or directory
+ Tcl_Obj *destPathPtr) /* New pathname of file or directory
* (UTF-8). */
{
int retVal = -1;
- Tcl_Filesystem *fsPtr, *fsPtr2;
+ const Tcl_Filesystem *fsPtr, *fsPtr2;
+
fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
- if (fsPtr == fsPtr2 && fsPtr != NULL) {
- Tcl_FSRenameFileProc *proc = fsPtr->renameFileProc;
- if (proc != NULL) {
- retVal = (*proc)(srcPathPtr, destPathPtr);
- }
+ if ((fsPtr == fsPtr2) && (fsPtr != NULL)
+ && (fsPtr->renameFileProc != NULL)) {
+ retVal = fsPtr->renameFileProc(srcPathPtr, destPathPtr);
}
if (retVal == -1) {
Tcl_SetErrno(EXDEV);
@@ -4010,8 +4189,8 @@ Tcl_FSRenameFile(srcPathPtr, destPathPtr)
* Tcl_FSCopyFile --
*
* If the two paths given belong to the same filesystem, we call that
- * filesystem's copy function. Otherwise we simply return the posix
- * error 'EXDEV', and -1.
+ * filesystem's copy function. Otherwise we simply return the POSIX error
+ * 'EXDEV', and -1.
*
* Note that in the native filesystems, 'copyFileProc' is defined to copy
* soft links (i.e. it copies the links themselves, not the things they
@@ -4027,20 +4206,18 @@ Tcl_FSRenameFile(srcPathPtr, destPathPtr)
*/
int
-Tcl_FSCopyFile(srcPathPtr, destPathPtr)
- Tcl_Obj* srcPathPtr; /* Pathname of file to be copied (UTF-8). */
- Tcl_Obj *destPathPtr; /* Pathname of file to copy to (UTF-8). */
+Tcl_FSCopyFile(
+ Tcl_Obj *srcPathPtr, /* Pathname of file to be copied (UTF-8). */
+ Tcl_Obj *destPathPtr) /* Pathname of file to copy to (UTF-8). */
{
int retVal = -1;
- Tcl_Filesystem *fsPtr, *fsPtr2;
+ const Tcl_Filesystem *fsPtr, *fsPtr2;
+
fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
- if (fsPtr == fsPtr2 && fsPtr != NULL) {
- Tcl_FSCopyFileProc *proc = fsPtr->copyFileProc;
- if (proc != NULL) {
- retVal = (*proc)(srcPathPtr, destPathPtr);
- }
+ if (fsPtr == fsPtr2 && fsPtr != NULL && fsPtr->copyFileProc != NULL) {
+ retVal = fsPtr->copyFileProc(srcPathPtr, destPathPtr);
}
if (retVal == -1) {
Tcl_SetErrno(EXDEV);
@@ -4054,7 +4231,7 @@ Tcl_FSCopyFile(srcPathPtr, destPathPtr)
* TclCrossFilesystemCopy --
*
* Helper for above function, and for Tcl_FSLoadFile, to copy files from
- * one filesystem to another. This function will overwrite the target
+ * one filesystem to another. This function will overwrite the target
* file if it already exists.
*
* Results:
@@ -4065,65 +4242,64 @@ Tcl_FSCopyFile(srcPathPtr, destPathPtr)
*
*---------------------------------------------------------------------------
*/
+
int
-TclCrossFilesystemCopy(interp, source, target)
- Tcl_Interp *interp; /* For error messages */
- Tcl_Obj *source; /* Pathname of file to be copied (UTF-8). */
- Tcl_Obj *target; /* Pathname of file to copy to (UTF-8). */
+TclCrossFilesystemCopy(
+ Tcl_Interp *interp, /* For error messages. */
+ Tcl_Obj *source, /* Pathname of file to be copied (UTF-8). */
+ Tcl_Obj *target) /* Pathname of file to copy to (UTF-8). */
{
int result = TCL_ERROR;
int prot = 0666;
+ Tcl_Channel in, out;
+ Tcl_StatBuf sourceStatBuf;
+ struct utimbuf tval;
- Tcl_Channel out = Tcl_FSOpenFileChannel(interp, target, "w", prot);
- if (out != NULL) {
+ out = Tcl_FSOpenFileChannel(interp, target, "wb", prot);
+ if (out == NULL) {
/*
- * It looks like we can copy it over.
+ * It looks like we cannot copy it over. Bail out...
*/
+ goto done;
+ }
- Tcl_Channel in = Tcl_FSOpenFileChannel(interp, source, "r", prot);
-
- if (in == NULL) {
- /*
- * This is very strange, we checked this above
- */
-
- Tcl_Close(interp, out);
-
- } else {
- Tcl_StatBuf sourceStatBuf;
- struct utimbuf tval;
+ in = Tcl_FSOpenFileChannel(interp, source, "rb", prot);
+ if (in == NULL) {
+ /*
+ * This is very strange, caller should have checked this...
+ */
- /*
- * Copy it synchronously. We might wish to add an asynchronous
- * option to support vfs's which are slow (e.g. network sockets).
- */
+ Tcl_Close(interp, out);
+ goto done;
+ }
- Tcl_SetChannelOption(interp, in, "-translation", "binary");
- Tcl_SetChannelOption(interp, out, "-translation", "binary");
+ /*
+ * Copy it synchronously. We might wish to add an asynchronous option to
+ * support vfs's which are slow (e.g. network sockets).
+ */
- if (TclCopyChannel(interp, in, out, -1, NULL) == TCL_OK) {
- result = TCL_OK;
- }
+ if (TclCopyChannel(interp, in, out, -1, NULL) == TCL_OK) {
+ result = TCL_OK;
+ }
- /*
- * If the copy failed, assume that copy channel left a good error
- * message.
- */
+ /*
+ * If the copy failed, assume that copy channel left a good error message.
+ */
- Tcl_Close(interp, in);
- Tcl_Close(interp, out);
+ Tcl_Close(interp, in);
+ Tcl_Close(interp, out);
- /*
- * Set modification date of copied file.
- */
+ /*
+ * Set modification date of copied file.
+ */
- if (Tcl_FSLstat(source, &sourceStatBuf) == 0) {
- tval.actime = sourceStatBuf.st_atime;
- tval.modtime = sourceStatBuf.st_mtime;
- Tcl_FSUtime(target, &tval);
- }
- }
+ if (Tcl_FSLstat(source, &sourceStatBuf) == 0) {
+ tval.actime = sourceStatBuf.st_atime;
+ tval.modtime = sourceStatBuf.st_mtime;
+ Tcl_FSUtime(target, &tval);
}
+
+ done:
return result;
}
@@ -4145,15 +4321,13 @@ TclCrossFilesystemCopy(interp, source, target)
*/
int
-Tcl_FSDeleteFile(pathPtr)
- Tcl_Obj *pathPtr; /* Pathname of file to be removed (UTF-8). */
+Tcl_FSDeleteFile(
+ Tcl_Obj *pathPtr) /* Pathname of file to be removed (UTF-8). */
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSDeleteFileProc *proc = fsPtr->deleteFileProc;
- if (proc != NULL) {
- return (*proc)(pathPtr);
- }
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
+ if (fsPtr != NULL && fsPtr->deleteFileProc != NULL) {
+ return fsPtr->deleteFileProc(pathPtr);
}
Tcl_SetErrno(ENOENT);
return -1;
@@ -4177,15 +4351,13 @@ Tcl_FSDeleteFile(pathPtr)
*/
int
-Tcl_FSCreateDirectory(pathPtr)
- Tcl_Obj *pathPtr; /* Pathname of directory to create (UTF-8). */
+Tcl_FSCreateDirectory(
+ Tcl_Obj *pathPtr) /* Pathname of directory to create (UTF-8). */
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSCreateDirectoryProc *proc = fsPtr->createDirectoryProc;
- if (proc != NULL) {
- return (*proc)(pathPtr);
- }
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
+ if (fsPtr != NULL && fsPtr->createDirectoryProc != NULL) {
+ return fsPtr->createDirectoryProc(pathPtr);
}
Tcl_SetErrno(ENOENT);
return -1;
@@ -4197,8 +4369,8 @@ Tcl_FSCreateDirectory(pathPtr)
* Tcl_FSCopyDirectory --
*
* If the two paths given belong to the same filesystem, we call that
- * filesystems copy-directory function. Otherwise we simply return the
- * posix error 'EXDEV', and -1.
+ * filesystems copy-directory function. Otherwise we simply return the
+ * POSIX error 'EXDEV', and -1.
*
* Results:
* Standard Tcl error code if a function was called.
@@ -4210,24 +4382,22 @@ Tcl_FSCreateDirectory(pathPtr)
*/
int
-Tcl_FSCopyDirectory(srcPathPtr, destPathPtr, errorPtr)
- Tcl_Obj* srcPathPtr; /* Pathname of directory to be copied
+Tcl_FSCopyDirectory(
+ Tcl_Obj *srcPathPtr, /* Pathname of directory to be copied
* (UTF-8). */
- Tcl_Obj *destPathPtr; /* Pathname of target directory (UTF-8). */
- Tcl_Obj **errorPtr; /* If non-NULL, then will be set to a new
+ Tcl_Obj *destPathPtr, /* Pathname of target directory (UTF-8). */
+ Tcl_Obj **errorPtr) /* If non-NULL, then will be set to a new
* object containing name of file causing
* error, with refCount 1. */
{
int retVal = -1;
- Tcl_Filesystem *fsPtr, *fsPtr2;
+ const Tcl_Filesystem *fsPtr, *fsPtr2;
+
fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
- if (fsPtr == fsPtr2 && fsPtr != NULL) {
- Tcl_FSCopyDirectoryProc *proc = fsPtr->copyDirectoryProc;
- if (proc != NULL) {
- retVal = (*proc)(srcPathPtr, destPathPtr, errorPtr);
- }
+ if (fsPtr == fsPtr2 && fsPtr != NULL && fsPtr->copyDirectoryProc != NULL){
+ retVal = fsPtr->copyDirectoryProc(srcPathPtr, destPathPtr, errorPtr);
}
if (retVal == -1) {
Tcl_SetErrno(EXDEV);
@@ -4253,56 +4423,57 @@ Tcl_FSCopyDirectory(srcPathPtr, destPathPtr, errorPtr)
*/
int
-Tcl_FSRemoveDirectory(pathPtr, recursive, errorPtr)
- Tcl_Obj *pathPtr; /* Pathname of directory to be removed
+Tcl_FSRemoveDirectory(
+ Tcl_Obj *pathPtr, /* Pathname of directory to be removed
* (UTF-8). */
- int recursive; /* If non-zero, removes directories that are
- * nonempty. Otherwise, will only remove
- * empty directories. */
- Tcl_Obj **errorPtr; /* If non-NULL, then will be set to a new
+ int recursive, /* If non-zero, removes directories that are
+ * nonempty. Otherwise, will only remove empty
+ * directories. */
+ Tcl_Obj **errorPtr) /* If non-NULL, then will be set to a new
* object containing name of file causing
* error, with refCount 1. */
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL && fsPtr->removeDirectoryProc != NULL) {
- Tcl_FSRemoveDirectoryProc *proc = fsPtr->removeDirectoryProc;
- if (recursive) {
- /*
- * We check whether the cwd lies inside this directory and move it
- * if it does.
- */
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
+ if (fsPtr == NULL || fsPtr->removeDirectoryProc == NULL) {
+ Tcl_SetErrno(ENOENT);
+ return -1;
+ }
- Tcl_Obj *cwdPtr = Tcl_FSGetCwd(NULL);
+ /*
+ * When working recursively, we check whether the cwd lies inside this
+ * directory and move it if it does.
+ */
- if (cwdPtr != NULL) {
- char *cwdStr, *normPathStr;
- int cwdLen, normLen;
- Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr);
+ if (recursive) {
+ Tcl_Obj *cwdPtr = Tcl_FSGetCwd(NULL);
- if (normPath != NULL) {
- normPathStr = Tcl_GetStringFromObj(normPath, &normLen);
- cwdStr = Tcl_GetStringFromObj(cwdPtr, &cwdLen);
- if ((cwdLen >= normLen) && (strncmp(normPathStr, cwdStr,
- (size_t) normLen) == 0)) {
- /*
- * The cwd is inside the directory, so we perform a
- * 'cd [file dirname $path]'.
- */
+ if (cwdPtr != NULL) {
+ const char *cwdStr, *normPathStr;
+ int cwdLen, normLen;
+ Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr);
- Tcl_Obj *dirPtr = TclPathPart(NULL, pathPtr,
- TCL_PATH_DIRNAME);
+ if (normPath != NULL) {
+ normPathStr = Tcl_GetStringFromObj(normPath, &normLen);
+ cwdStr = Tcl_GetStringFromObj(cwdPtr, &cwdLen);
+ if ((cwdLen >= normLen) && (strncmp(normPathStr, cwdStr,
+ (size_t) normLen) == 0)) {
+ /*
+ * The cwd is inside the directory, so we perform a 'cd
+ * [file dirname $path]'.
+ */
- Tcl_FSChdir(dirPtr);
- Tcl_DecrRefCount(dirPtr);
- }
+ Tcl_Obj *dirPtr = TclPathPart(NULL, pathPtr,
+ TCL_PATH_DIRNAME);
+
+ Tcl_FSChdir(dirPtr);
+ Tcl_DecrRefCount(dirPtr);
}
- Tcl_DecrRefCount(cwdPtr);
}
+ Tcl_DecrRefCount(cwdPtr);
}
- return (*proc)(pathPtr, recursive, errorPtr);
}
- Tcl_SetErrno(ENOENT);
- return -1;
+ return fsPtr->removeDirectoryProc(pathPtr, recursive, errorPtr);
}
/*
@@ -4311,7 +4482,7 @@ Tcl_FSRemoveDirectory(pathPtr, recursive, errorPtr)
* Tcl_FSGetFileSystemForPath --
*
* This function determines which filesystem to use for a particular path
- * object, and returns the filesystem which accepts this file. If no
+ * object, and returns the filesystem which accepts this file. If no
* filesystem will accept this object as a valid file path, then NULL is
* returned.
*
@@ -4324,12 +4495,12 @@ Tcl_FSRemoveDirectory(pathPtr, recursive, errorPtr)
*---------------------------------------------------------------------------
*/
-Tcl_Filesystem*
-Tcl_FSGetFileSystemForPath(pathPtr)
- Tcl_Obj* pathPtr;
+const Tcl_Filesystem *
+Tcl_FSGetFileSystemForPath(
+ Tcl_Obj *pathPtr)
{
FilesystemRecord *fsRecPtr;
- Tcl_Filesystem* retVal = NULL;
+ const Tcl_Filesystem *retVal = NULL;
if (pathPtr == NULL) {
Tcl_Panic("Tcl_FSGetFileSystemForPath called with NULL object");
@@ -4337,7 +4508,7 @@ Tcl_FSGetFileSystemForPath(pathPtr)
}
/*
- * If the object has a refCount of zero, we reject it. This is to avoid
+ * If the object has a refCount of zero, we reject it. This is to avoid
* possible segfaults or nondeterministic memory leaks (i.e. the user
* doesn't know if they should decrement the ref count on return or not).
*/
@@ -4349,43 +4520,49 @@ Tcl_FSGetFileSystemForPath(pathPtr)
/*
* Check if the filesystem has changed in some way since this object's
- * internal representation was calculated. Before doing that, assure we
+ * internal representation was calculated. Before doing that, assure we
* have the most up-to-date copy of the master filesystem. This is
* accomplished by the FsGetFirstFilesystem() call.
*/
fsRecPtr = FsGetFirstFilesystem();
+ Claim();
if (TclFSEnsureEpochOk(pathPtr, &retVal) != TCL_OK) {
+ Disclaim();
return NULL;
+ } else if (retVal != NULL) {
+ /* TODO: Can this happen? */
+ Disclaim();
+ return retVal;
}
/*
- * Call each of the "pathInFilesystem" functions in succession. A
+ * Call each of the "pathInFilesystem" functions in succession. A
* non-return value of -1 indicates the particular function has succeeded.
*/
- while ((retVal == NULL) && (fsRecPtr != NULL)) {
- Tcl_FSPathInFilesystemProc *proc =
- fsRecPtr->fsPtr->pathInFilesystemProc;
+ for (; fsRecPtr!=NULL ; fsRecPtr=fsRecPtr->nextPtr) {
+ ClientData clientData = NULL;
- if (proc != NULL) {
- ClientData clientData = NULL;
- int ret = (*proc)(pathPtr, &clientData);
- if (ret != -1) {
- /*
- * We assume the type of pathPtr hasn't been changed by the
- * above call to the pathInFilesystemProc.
- */
+ if (fsRecPtr->fsPtr->pathInFilesystemProc == NULL) {
+ continue;
+ }
- TclFSSetPathDetails(pathPtr, fsRecPtr, clientData);
- retVal = fsRecPtr->fsPtr;
- }
+ if (fsRecPtr->fsPtr->pathInFilesystemProc(pathPtr, &clientData)!=-1) {
+ /*
+ * We assume the type of pathPtr hasn't been changed by the above
+ * call to the pathInFilesystemProc.
+ */
+
+ TclFSSetPathDetails(pathPtr, fsRecPtr->fsPtr, clientData);
+ Disclaim();
+ return fsRecPtr->fsPtr;
}
- fsRecPtr = fsRecPtr->nextPtr;
}
+ Disclaim();
- return retVal;
+ return NULL;
}
/*
@@ -4395,17 +4572,17 @@ Tcl_FSGetFileSystemForPath(pathPtr)
*
* This function is for use by the Win/Unix native filesystems, so that
* they can easily retrieve the native (char* or TCHAR*) representation
- * of a path. Other filesystems will probably want to implement similar
- * functions. They basically act as a safety net around
- * Tcl_FSGetInternalRep. Normally your file- system procedures will
- * always be called with path objects already converted to the correct
+ * of a path. Other filesystems will probably want to implement similar
+ * functions. They basically act as a safety net around
+ * Tcl_FSGetInternalRep. Normally your file-system functions will always
+ * be called with path objects already converted to the correct
* filesystem, but if for some reason they are called directly (i.e. by
- * procedures not in this file), then one cannot necessarily guarantee
+ * functions not in this file), then one cannot necessarily guarantee
* that the path object pointer is from the correct filesystem.
*
- * Note: in the future it might be desireable to have separate versions
+ * Note: in the future it might be desirable to have separate versions
* of this function with different signatures, for example
- * Tcl_FSGetNativeWinPath, Tcl_FSGetNativeUnixPath etc. Right now, since
+ * Tcl_FSGetNativeWinPath, Tcl_FSGetNativeUnixPath etc. Right now, since
* native paths are all string based, we use just one function.
*
* Results:
@@ -4417,11 +4594,11 @@ Tcl_FSGetFileSystemForPath(pathPtr)
*---------------------------------------------------------------------------
*/
-CONST char *
-Tcl_FSGetNativePath(pathPtr)
- Tcl_Obj *pathPtr;
+const void *
+Tcl_FSGetNativePath(
+ Tcl_Obj *pathPtr)
{
- return (CONST char *) Tcl_FSGetInternalRep(pathPtr, &tclNativeFilesystem);
+ return Tcl_FSGetInternalRep(pathPtr, &tclNativeFilesystem);
}
/*
@@ -4441,10 +4618,10 @@ Tcl_FSGetNativePath(pathPtr)
*/
static void
-NativeFreeInternalRep(clientData)
- ClientData clientData;
+NativeFreeInternalRep(
+ ClientData clientData)
{
- ckfree((char *) clientData);
+ ckfree(clientData);
}
/*
@@ -4452,9 +4629,9 @@ NativeFreeInternalRep(clientData)
*
* Tcl_FSFileSystemInfo --
*
- * This function returns a list of two elements. The first element is
- * the name of the filesystem (e.g. "native" or "vfs"), and the second is
- * the particular type of the given path within that filesystem.
+ * This function returns a list of two elements. The first element is the
+ * name of the filesystem (e.g. "native" or "vfs"), and the second is the
+ * particular type of the given path within that filesystem.
*
* Results:
* A list of two elements.
@@ -4465,26 +4642,24 @@ NativeFreeInternalRep(clientData)
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-Tcl_FSFileSystemInfo(pathPtr)
- Tcl_Obj* pathPtr;
+Tcl_Obj *
+Tcl_FSFileSystemInfo(
+ Tcl_Obj *pathPtr)
{
Tcl_Obj *resPtr;
- Tcl_FSFilesystemPathTypeProc *proc;
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
if (fsPtr == NULL) {
return NULL;
}
- resPtr = Tcl_NewListObj(0,NULL);
-
+ resPtr = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(NULL, resPtr,
- Tcl_NewStringObj(fsPtr->typeName,-1));
+ Tcl_NewStringObj(fsPtr->typeName, -1));
+
+ if (fsPtr->filesystemPathTypeProc != NULL) {
+ Tcl_Obj *typePtr = fsPtr->filesystemPathTypeProc(pathPtr);
- proc = fsPtr->filesystemPathTypeProc;
- if (proc != NULL) {
- Tcl_Obj *typePtr = (*proc)(pathPtr);
if (typePtr != NULL) {
Tcl_ListObjAppendElement(NULL, resPtr, typePtr);
}
@@ -4498,7 +4673,7 @@ Tcl_FSFileSystemInfo(pathPtr)
*
* Tcl_FSPathSeparator --
*
- * This function returns the separator to be used for a given path. The
+ * This function returns the separator to be used for a given path. The
* object returned should have a refCount of zero
*
* Results:
@@ -4512,25 +4687,28 @@ Tcl_FSFileSystemInfo(pathPtr)
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-Tcl_FSPathSeparator(pathPtr)
- Tcl_Obj* pathPtr;
+Tcl_Obj *
+Tcl_FSPathSeparator(
+ Tcl_Obj *pathPtr)
{
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ Tcl_Obj *resultObj;
if (fsPtr == NULL) {
return NULL;
}
- if (fsPtr->filesystemSeparatorProc != NULL) {
- return (*fsPtr->filesystemSeparatorProc)(pathPtr);
- } else {
- /*
- * Allow filesystems not to provide a filesystemSeparatorProc if they
- * wish to use the standard forward slash.
- */
- return Tcl_NewStringObj("/", 1);
+ if (fsPtr->filesystemSeparatorProc != NULL) {
+ return fsPtr->filesystemSeparatorProc(pathPtr);
}
+
+ /*
+ * Allow filesystems not to provide a filesystemSeparatorProc if they wish
+ * to use the standard forward slash.
+ */
+
+ TclNewLiteralStringObj(resultObj, "/");
+ return resultObj;
}
/*
@@ -4550,11 +4728,12 @@ Tcl_FSPathSeparator(pathPtr)
*---------------------------------------------------------------------------
*/
-static Tcl_Obj*
-NativeFilesystemSeparator(pathPtr)
- Tcl_Obj* pathPtr;
+static Tcl_Obj *
+NativeFilesystemSeparator(
+ Tcl_Obj *pathPtr)
{
- char *separator = NULL; /* lint */
+ const char *separator = NULL; /* lint */
+
switch (tclPlatform) {
case TCL_PLATFORM_UNIX:
separator = "/";
@@ -4565,319 +4744,6 @@ NativeFilesystemSeparator(pathPtr)
}
return Tcl_NewStringObj(separator,1);
}
-
-/* Everything from here on is contained in this obsolete ifdef */
-#ifdef USE_OBSOLETE_FS_HOOKS
-
-/*
- *----------------------------------------------------------------------
- *
- * TclStatInsertProc --
- *
- * Insert the passed procedure pointer at the head of the list of
- * functions which are used during a call to 'TclStat(...)'. The passed
- * function should behave exactly like 'TclStat' when called during that
- * time (see 'TclStat(...)' for more information). The function will be
- * added even if it already in the list.
- *
- * Results:
- * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could
- * not be allocated.
- *
- * Side effects:
- * Memory allocated and modifies the link list for 'TclStat' functions.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclStatInsertProc (proc)
- TclStatProc_ *proc;
-{
- int retVal = TCL_ERROR;
-
- if (proc != NULL) {
- StatProc *newStatProcPtr;
-
- newStatProcPtr = (StatProc *)ckalloc(sizeof(StatProc));
-
- if (newStatProcPtr != NULL) {
- newStatProcPtr->proc = proc;
- Tcl_MutexLock(&obsoleteFsHookMutex);
- newStatProcPtr->nextPtr = statProcList;
- statProcList = newStatProcPtr;
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
-
- retVal = TCL_OK;
- }
- }
-
- return retVal;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclStatDeleteProc --
- *
- * Removed the passed function pointer from the list of 'TclStat'
- * functions. Ensures that the built-in stat function is not removvable.
- *
- * Results:
- * TCL_OK if the procedure pointer was successfully removed, TCL_ERROR
- * otherwise.
- *
- * Side effects:
- * Memory is deallocated and the respective list updated.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclStatDeleteProc (proc)
- TclStatProc_ *proc;
-{
- int retVal = TCL_ERROR;
- StatProc *tmpStatProcPtr;
- StatProc *prevStatProcPtr = NULL;
-
- Tcl_MutexLock(&obsoleteFsHookMutex);
- tmpStatProcPtr = statProcList;
-
- /*
- * Traverse the 'statProcList' looking for the particular node whose
- * 'proc' member matches 'proc' and remove that one from the list. Ensure
- * that the "default" node cannot be removed.
- */
-
- while ((retVal == TCL_ERROR) && (tmpStatProcPtr != NULL)) {
- if (tmpStatProcPtr->proc == proc) {
- if (prevStatProcPtr == NULL) {
- statProcList = tmpStatProcPtr->nextPtr;
- } else {
- prevStatProcPtr->nextPtr = tmpStatProcPtr->nextPtr;
- }
-
- ckfree((char *)tmpStatProcPtr);
-
- retVal = TCL_OK;
- } else {
- prevStatProcPtr = tmpStatProcPtr;
- tmpStatProcPtr = tmpStatProcPtr->nextPtr;
- }
- }
-
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
-
- return retVal;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclAccessInsertProc --
- *
- * Insert the passed procedure pointer at the head of the list of
- * functions which are used during a call to 'TclAccess(...)'. The
- * passed function should behave exactly like 'TclAccess' when called
- * during that time (see 'TclAccess(...)' for more information). The
- * function will be added even if it already in the list.
- *
- * Results:
- * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could
- * not be allocated.
- *
- * Side effects:
- * Memory allocated and modifies the link list for 'TclAccess' functions.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclAccessInsertProc(proc)
- TclAccessProc_ *proc;
-{
- int retVal = TCL_ERROR;
-
- if (proc != NULL) {
- AccessProc *newAccessProcPtr;
-
- newAccessProcPtr = (AccessProc *)ckalloc(sizeof(AccessProc));
-
- if (newAccessProcPtr != NULL) {
- newAccessProcPtr->proc = proc;
- Tcl_MutexLock(&obsoleteFsHookMutex);
- newAccessProcPtr->nextPtr = accessProcList;
- accessProcList = newAccessProcPtr;
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
-
- retVal = TCL_OK;
- }
- }
-
- return retVal;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclAccessDeleteProc --
- *
- * Removed the passed function pointer from the list of 'TclAccess'
- * functions. Ensures that the built-in access function is not
- * removvable.
- *
- * Results:
- * TCL_OK if the procedure pointer was successfully removed, TCL_ERROR
- * otherwise.
- *
- * Side effects:
- * Memory is deallocated and the respective list updated.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclAccessDeleteProc(proc)
- TclAccessProc_ *proc;
-{
- int retVal = TCL_ERROR;
- AccessProc *tmpAccessProcPtr;
- AccessProc *prevAccessProcPtr = NULL;
-
- /*
- * Traverse the 'accessProcList' looking for the particular node whose
- * 'proc' member matches 'proc' and remove that one from the list. Ensure
- * that the "default" node cannot be removed.
- */
-
- Tcl_MutexLock(&obsoleteFsHookMutex);
- tmpAccessProcPtr = accessProcList;
- while ((retVal == TCL_ERROR) && (tmpAccessProcPtr != NULL)) {
- if (tmpAccessProcPtr->proc == proc) {
- if (prevAccessProcPtr == NULL) {
- accessProcList = tmpAccessProcPtr->nextPtr;
- } else {
- prevAccessProcPtr->nextPtr = tmpAccessProcPtr->nextPtr;
- }
-
- ckfree((char *)tmpAccessProcPtr);
-
- retVal = TCL_OK;
- } else {
- prevAccessProcPtr = tmpAccessProcPtr;
- tmpAccessProcPtr = tmpAccessProcPtr->nextPtr;
- }
- }
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
-
- return retVal;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclOpenFileChannelInsertProc --
- *
- * Insert the passed procedure pointer at the head of the list of
- * functions which are used during a call to
- * 'Tcl_OpenFileChannel(...)'. The passed function should behave exactly
- * like 'Tcl_OpenFileChannel' when called during that time (see
- * 'Tcl_OpenFileChannel(...)' for more information). The function will be
- * added even if it already in the list.
- *
- * Results:
- * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could
- * not be allocated.
- *
- * Side effects:
- * Memory allocated and modifies the link list for 'Tcl_OpenFileChannel'
- * functions.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclOpenFileChannelInsertProc(proc)
- TclOpenFileChannelProc_ *proc;
-{
- int retVal = TCL_ERROR;
-
- if (proc != NULL) {
- OpenFileChannelProc *newOpenFileChannelProcPtr;
-
- newOpenFileChannelProcPtr = (OpenFileChannelProc *)
- ckalloc(sizeof(OpenFileChannelProc));
-
- newOpenFileChannelProcPtr->proc = proc;
- Tcl_MutexLock(&obsoleteFsHookMutex);
- newOpenFileChannelProcPtr->nextPtr = openFileChannelProcList;
- openFileChannelProcList = newOpenFileChannelProcPtr;
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
-
- retVal = TCL_OK;
- }
-
- return retVal;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclOpenFileChannelDeleteProc --
- *
- * Removed the passed function pointer from the list of
- * 'Tcl_OpenFileChannel' functions. Ensures that the built-in open file
- * channel function is not removable.
- *
- * Results:
- * TCL_OK if the procedure pointer was successfully removed, TCL_ERROR
- * otherwise.
- *
- * Side effects:
- * Memory is deallocated and the respective list updated.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclOpenFileChannelDeleteProc(proc)
- TclOpenFileChannelProc_ *proc;
-{
- int retVal = TCL_ERROR;
- OpenFileChannelProc *tmpOpenFileChannelProcPtr = openFileChannelProcList;
- OpenFileChannelProc *prevOpenFileChannelProcPtr = NULL;
-
- /*
- * Traverse the 'openFileChannelProcList' looking for the particular node
- * whose 'proc' member matches 'proc' and remove that one from the list.
- */
-
- Tcl_MutexLock(&obsoleteFsHookMutex);
- tmpOpenFileChannelProcPtr = openFileChannelProcList;
- while ((retVal == TCL_ERROR) &&
- (tmpOpenFileChannelProcPtr != NULL)) {
- if (tmpOpenFileChannelProcPtr->proc == proc) {
- if (prevOpenFileChannelProcPtr == NULL) {
- openFileChannelProcList = tmpOpenFileChannelProcPtr->nextPtr;
- } else {
- prevOpenFileChannelProcPtr->nextPtr =
- tmpOpenFileChannelProcPtr->nextPtr;
- }
-
- ckfree((char *) tmpOpenFileChannelProcPtr);
-
- retVal = TCL_OK;
- } else {
- prevOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr;
- tmpOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr->nextPtr;
- }
- }
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
-
- return retVal;
-}
-#endif /* USE_OBSOLETE_FS_HOOKS */
/*
* Local Variables: