diff options
Diffstat (limited to 'generic/tclIOUtil.c')
-rw-r--r-- | generic/tclIOUtil.c | 2330 |
1 files changed, 1251 insertions, 1079 deletions
diff --git a/generic/tclIOUtil.c b/generic/tclIOUtil.c index d2919fc..82ffd88 100644 --- a/generic/tclIOUtil.c +++ b/generic/tclIOUtil.c @@ -19,7 +19,7 @@ */ #include "tclInt.h" -#ifdef _WIN32 +#ifdef __WIN32__ # include "tclWinInt.h" #endif #include "tclFileSystem.h" @@ -40,7 +40,7 @@ typedef struct FilesystemRecord { ClientData clientData; /* Client specific data for the new filesystem * (can be NULL) */ - const Tcl_Filesystem *fsPtr;/* Pointer to filesystem dispatch table. */ + Tcl_Filesystem *fsPtr; /* Pointer to filesystem dispatch table. */ struct FilesystemRecord *nextPtr; /* The next filesystem registered to Tcl, or * NULL if no more. */ @@ -71,8 +71,6 @@ typedef struct 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); @@ -80,13 +78,11 @@ 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 @@ -95,170 +91,15 @@ static void DivertUnloadFile(Tcl_LoadHandle loadHandle); * they are not (and should not be) used anywhere else. */ -MODULE_SCOPE const char *const tclpFileAttrStrings[]; +MODULE_SCOPE const char * 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. - */ - -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( @@ -267,7 +108,7 @@ Tcl_Stat( { int ret; Tcl_StatBuf buf; - Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1); + Tcl_Obj *pathPtr = Tcl_NewStringObj(path, -1); Tcl_IncrRefCount(pathPtr); ret = Tcl_FSStat(pathPtr, &buf); @@ -275,7 +116,6 @@ Tcl_Stat( if (ret != -1) { #ifndef TCL_WIDE_INT_IS_LONG 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)) @@ -292,10 +132,10 @@ Tcl_Stat( * Tcl_WideInt. */ - tmp1 = (Tcl_WideInt) buf.st_ino; - tmp2 = (Tcl_WideInt) buf.st_size; + tmp1 = (Tcl_WideInt) buf.st_ino; + tmp2 = (Tcl_WideInt) buf.st_size; #ifdef HAVE_STRUCT_STAT_ST_BLOCKS - tmp3 = (Tcl_WideInt) buf.st_blocks; + tmp3 = (Tcl_WideInt) buf.st_blocks; #endif if (OUT_OF_URANGE(tmp1) || OUT_OF_RANGE(tmp2) || OUT_OF_RANGE(tmp3)) { @@ -402,15 +242,16 @@ Tcl_GetCwd( Tcl_Interp *interp, Tcl_DString *cwdPtr) { - Tcl_Obj *cwd = Tcl_FSGetCwd(interp); - + Tcl_Obj *cwd; + 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 */ @@ -422,7 +263,6 @@ Tcl_EvalFile( { int ret; Tcl_Obj *pathPtr = Tcl_NewStringObj(fileName,-1); - Tcl_IncrRefCount(pathPtr); ret = Tcl_FSEvalFile(interp, pathPtr); Tcl_DecrRefCount(pathPtr); @@ -430,14 +270,234 @@ Tcl_EvalFile( } /* - * Now move on to the basic filesystem implementation. + * 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. + */ + +#undef 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, + 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. + */ + +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; + +/* + * Now move on to the basic filesystem implementation */ static void FsThrExitProc( ClientData cd) { - ThreadSpecificData *tsdPtr = cd; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) cd; FilesystemRecord *fsRecPtr = NULL, *tmpFsRecPtr = NULL; /* @@ -460,7 +520,7 @@ FsThrExitProc( while (fsRecPtr != NULL) { tmpFsRecPtr = fsRecPtr->nextPtr; fsRecPtr->fsPtr = NULL; - ckfree(fsRecPtr); + ckfree((char *)fsRecPtr); fsRecPtr = tmpFsRecPtr; } tsdPtr->filesystemList = NULL; @@ -502,7 +562,7 @@ TclFSCwdIsNative(void) int TclFSCwdPointerEquals( - Tcl_Obj **pathPtrPtr) + Tcl_Obj** pathPtrPtr) { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey); @@ -531,7 +591,7 @@ TclFSCwdPointerEquals( Tcl_MutexUnlock(&cwdMutex); if (tsdPtr->initialized == 0) { - Tcl_CreateThreadExitHandler(FsThrExitProc, tsdPtr); + Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr); tsdPtr->initialized = 1; } @@ -547,7 +607,7 @@ TclFSCwdPointerEquals( str1 = Tcl_GetStringFromObj(tsdPtr->cwdPathPtr, &len1); str2 = Tcl_GetStringFromObj(*pathPtrPtr, &len2); - if ((len1 == len2) && !memcmp(str1, str2, len1)) { + if (len1 == len2 && !strcmp(str1,str2)) { /* * They are equal, but different objects. Update so they will be * the same object in the future. @@ -599,7 +659,7 @@ FsRecacheFilesystemList(void) list = NULL; fsRecPtr = tmpFsRecPtr; while (fsRecPtr != NULL) { - tmpFsRecPtr = ckalloc(sizeof(FilesystemRecord)); + tmpFsRecPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord)); *tmpFsRecPtr = *fsRecPtr; tmpFsRecPtr->nextPtr = list; tmpFsRecPtr->prevPtr = NULL; @@ -613,7 +673,7 @@ FsRecacheFilesystemList(void) while (toFree) { FilesystemRecord *next = toFree->nextPtr; toFree->fsPtr = NULL; - ckfree(toFree); + ckfree((char *)toFree); toFree = next; } @@ -622,7 +682,7 @@ FsRecacheFilesystemList(void) */ if (tsdPtr->initialized == 0) { - Tcl_CreateThreadExitHandler(FsThrExitProc, tsdPtr); + Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr); tsdPtr->initialized = 1; } } @@ -651,26 +711,23 @@ TclFSEpochOk( } static void -Claim(void) +Claim() { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey); - tsdPtr->claims++; } static void -Disclaim(void) +Disclaim() { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey); - tsdPtr->claims--; } int -TclFSEpoch(void) +TclFSEpoch() { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey); - return tsdPtr->filesystemEpoch; } @@ -685,7 +742,7 @@ FsUpdateCwd( ClientData clientData) { int len; - const char *str = NULL; + char *str = NULL; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey); if (cwdObj != NULL) { @@ -709,7 +766,7 @@ FsUpdateCwd( */ cwdPathPtr = Tcl_NewStringObj(str, len); - Tcl_IncrRefCount(cwdPathPtr); + Tcl_IncrRefCount(cwdPathPtr); cwdClientData = TclNativeDupInternalRep(clientData); } @@ -776,7 +833,7 @@ TclFinalizeFilesystem(void) /* * Remove all filesystems, freeing any allocated memory that is no longer - * needed. + * needed */ fsRecPtr = filesystemList; @@ -786,7 +843,7 @@ TclFinalizeFilesystem(void) /* The native filesystem is static, so we don't free it. */ if (fsRecPtr != &nativeFilesystemRecord) { - ckfree(fsRecPtr); + ckfree((char *)fsRecPtr); } fsRecPtr = tmpFsRecPtr; } @@ -798,7 +855,12 @@ TclFinalizeFilesystem(void) * filesystem is likely to fail. */ -#ifdef _WIN32 +#ifdef USE_OBSOLETE_FS_HOOKS + statProcList = NULL; + accessProcList = NULL; + openFileChannelProcList = NULL; +#endif +#ifdef __WIN32__ TclWinEncodingsCleanup(); #endif } @@ -825,7 +887,7 @@ TclResetFilesystem(void) filesystemList = &nativeFilesystemRecord; theFilesystemEpoch++; -#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. @@ -867,8 +929,8 @@ TclResetFilesystem(void) int Tcl_FSRegister( - ClientData clientData, /* Client specific data for this fs. */ - const Tcl_Filesystem *fsPtr)/* The filesystem record for the new fs. */ + ClientData clientData, /* Client specific data for this fs */ + Tcl_Filesystem *fsPtr) /* The filesystem record for the new fs. */ { FilesystemRecord *newFilesystemPtr; @@ -876,7 +938,7 @@ Tcl_FSRegister( return TCL_ERROR; } - newFilesystemPtr = ckalloc(sizeof(FilesystemRecord)); + newFilesystemPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord)); newFilesystemPtr->clientData = clientData; newFilesystemPtr->fsPtr = fsPtr; @@ -940,7 +1002,7 @@ Tcl_FSRegister( int Tcl_FSUnregister( - const Tcl_Filesystem *fsPtr) /* The filesystem record to remove. */ + Tcl_Filesystem *fsPtr) /* The filesystem record to remove. */ { int retVal = TCL_ERROR; FilesystemRecord *fsRecPtr; @@ -975,7 +1037,7 @@ Tcl_FSUnregister( theFilesystemEpoch++; - ckfree(fsRecPtr); + ckfree((char *)fsRecPtr); retVal = TCL_OK; } else { @@ -1031,7 +1093,7 @@ Tcl_FSUnregister( int Tcl_FSMatchInDirectory( Tcl_Interp *interp, /* Interpreter to receive error messages, but - * may be NULL. */ + * 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. */ @@ -1043,7 +1105,7 @@ Tcl_FSMatchInDirectory( 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 @@ -1070,8 +1132,8 @@ Tcl_FSMatchInDirectory( 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); } @@ -1080,7 +1142,7 @@ Tcl_FSMatchInDirectory( /* * 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') { @@ -1101,9 +1163,8 @@ Tcl_FSMatchInDirectory( cwd = Tcl_FSGetCwd(NULL); if (cwd == NULL) { if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "glob couldn't determine the current working directory", - -1)); + Tcl_SetResult(interp, "glob couldn't determine " + "the current working directory", TCL_STATIC); } return TCL_ERROR; } @@ -1112,8 +1173,8 @@ Tcl_FSMatchInDirectory( 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); @@ -1157,7 +1218,7 @@ static void FsAddMountsToGlobResult( Tcl_Obj *resultPtr, /* The current list of matching paths; must * not be shared! */ - Tcl_Obj *pathPtr, /* The directory in question. */ + 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 @@ -1198,7 +1259,7 @@ FsAddMountsToGlobResult( Tcl_ListObjReplace(NULL, resultPtr, j, 1, 0, NULL); gLength--; } - break; /* Break out of for loop. */ + break; /* Break out of for loop */ } } if (!found && dir) { @@ -1288,7 +1349,7 @@ FsAddMountsToGlobResult( void Tcl_FSMountsChanged( - const Tcl_Filesystem *fsPtr) + Tcl_Filesystem *fsPtr) { /* * We currently don't do anything with this parameter. We could in the @@ -1329,7 +1390,7 @@ Tcl_FSMountsChanged( ClientData Tcl_FSData( - const Tcl_Filesystem *fsPtr) /* The filesystem record to query. */ + Tcl_Filesystem *fsPtr) /* The filesystem record to query. */ { ClientData retVal = NULL; FilesystemRecord *fsRecPtr = FsGetFirstFilesystem(); @@ -1383,8 +1444,8 @@ Tcl_FSData( int TclFSNormalizeToUniquePath( Tcl_Interp *interp, /* Used for error messages. */ - Tcl_Obj *pathPtr, /* The path to normalize in place. */ - int startAt) /* Start at this char-offset. */ + Tcl_Obj *pathPtr, /* The path to normalize in place */ + int startAt) /* Start at this char-offset */ { FilesystemRecord *fsRecPtr, *firstFsRecPtr; @@ -1398,42 +1459,37 @@ TclFSNormalizeToUniquePath( firstFsRecPtr = FsGetFirstFilesystem(); Claim(); - for (fsRecPtr=firstFsRecPtr; fsRecPtr!=NULL; fsRecPtr=fsRecPtr->nextPtr) { - if (fsRecPtr->fsPtr != &tclNativeFilesystem) { - continue; - } - - /* - * 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); + fsRecPtr = firstFsRecPtr; + while (fsRecPtr != NULL) { + if (fsRecPtr->fsPtr == &tclNativeFilesystem) { + Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc; + if (proc != NULL) { + startAt = (*proc)(interp, pathPtr, startAt); + } + break; } - break; + fsRecPtr = fsRecPtr->nextPtr; } - for (fsRecPtr=firstFsRecPtr; fsRecPtr!=NULL; fsRecPtr=fsRecPtr->nextPtr) { + fsRecPtr = firstFsRecPtr; + while (fsRecPtr != NULL) { /* * Skip the native system next time through. */ - if (fsRecPtr->fsPtr == &tclNativeFilesystem) { - continue; - } + if (fsRecPtr->fsPtr != &tclNativeFilesystem) { + Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc; + if (proc != NULL) { + startAt = (*proc)(interp, pathPtr, startAt); + } - if (fsRecPtr->fsPtr->normalizePathProc != NULL) { - startAt = fsRecPtr->fsPtr->normalizePathProc(interp, pathPtr, - startAt); + /* + * We could add an efficiency check like this: + * if (retVal == length-of(pathPtr)) {break;} + * but there's not much benefit. + */ } - - /* - * We could add an efficiency check like this: - * if (retVal == length-of(pathPtr)) {break;} - * but there's not much benefit. - */ + fsRecPtr = fsRecPtr->nextPtr; } Disclaim(); @@ -1507,7 +1563,7 @@ TclGetOpenModeEx( * EOF during the opening of the file. */ int *binaryPtr) /* Set this to 1 if the caller should * configure the opened channel for binary - * operations. */ + * operations */ { int mode, modeArgc, c, i, gotRW; const char **modeArgv, *flag; @@ -1549,7 +1605,7 @@ TclGetOpenModeEx( default: goto error; } - i = 1; + i=1; while (i<3 && modeString[i]) { if (modeString[i] == modeString[i-1]) { goto error; @@ -1580,8 +1636,8 @@ TclGetOpenModeEx( *seekFlagPtr = 0; *binaryPtr = 0; if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "illegal access mode \"%s\"", modeString)); + Tcl_AppendResult(interp, "illegal access mode \"", modeString, + "\"", NULL); } return -1; } @@ -1630,11 +1686,10 @@ TclGetOpenModeEx( mode |= O_NOCTTY; #else if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "access mode \"%s\" not supported by this system", - flag)); + Tcl_AppendResult(interp, "access mode \"", flag, + "\" not supported by this system", NULL); } - ckfree(modeArgv); + ckfree((char *) modeArgv); return -1; #endif @@ -1643,11 +1698,10 @@ TclGetOpenModeEx( mode |= O_NONBLOCK; #else if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "access mode \"%s\" not supported by this system", - flag)); + Tcl_AppendResult(interp, "access mode \"", flag, + "\" not supported by this system", NULL); } - ckfree(modeArgv); + ckfree((char *) modeArgv); return -1; #endif @@ -1658,23 +1712,21 @@ TclGetOpenModeEx( } else { 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)); + Tcl_AppendResult(interp, "invalid access mode \"", flag, + "\": must be RDONLY, WRONLY, RDWR, APPEND, BINARY, " + "CREAT, EXCL, NOCTTY, NONBLOCK, or TRUNC", NULL); } - ckfree(modeArgv); + ckfree((char *) modeArgv); return -1; } } - ckfree(modeArgv); + ckfree((char *) modeArgv); if (!gotRW) { if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "access mode must include either RDONLY, WRONLY, or RDWR", - -1)); + Tcl_AppendResult(interp, "access mode must include either" + " RDONLY, WRONLY, or RDWR", NULL); } return -1; } @@ -1682,13 +1734,25 @@ TclGetOpenModeEx( } /* + * Tcl_FSEvalFile is Tcl_FSEvalFileEx without encoding argument. + */ + +int +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. */ +{ + return Tcl_FSEvalFileEx(interp, pathPtr, NULL); +} + +/* *---------------------------------------------------------------------- * - * Tcl_FSEvalFile, Tcl_FSEvalFileEx, TclNREvalFile -- + * Tcl_FSEvalFileEx -- * * Read in a file and process the entire file as one gigantic Tcl - * command. Tcl_FSEvalFile is Tcl_FSEvalFileEx without encoding argument. - * TclNREvalFile is an NRE-enabled version of Tcl_FSEvalFileEx. + * command. * * Results: * A standard Tcl result, which is either the result of executing the @@ -1703,15 +1767,6 @@ TclGetOpenModeEx( */ int -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. */ -{ - 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 @@ -1723,7 +1778,7 @@ Tcl_FSEvalFileEx( Tcl_StatBuf statBuf; Tcl_Obj *oldScriptFile; Interp *iPtr; - const char *string; + char *string; Tcl_Channel chan; Tcl_Obj *objPtr; @@ -1733,16 +1788,15 @@ Tcl_FSEvalFileEx( 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))); + Tcl_AppendResult(interp, "couldn't read file \"", + Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); return result; } 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))); + if (chan == (Tcl_Channel) NULL) { + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "couldn't read file \"", + Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); return result; } @@ -1768,32 +1822,25 @@ Tcl_FSEvalFileEx( 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. + /* 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_AppendResult(interp, "couldn't read file \"", + Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); goto end; } string = Tcl_GetString(objPtr); - /* * If first character is not a BOM, append the remaining characters, - * otherwise replace them. [Bug 3466099] + * 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_AppendResult(interp, "couldn't read file \"", + Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); goto end; } @@ -1806,13 +1853,10 @@ Tcl_FSEvalFileEx( iPtr->scriptFile = pathPtr; Tcl_IncrRefCount(iPtr->scriptFile); string = Tcl_GetStringFromObj(objPtr, &length); - - /* - * TIP #280 Force the evaluator to open a frame for a sourced file. - */ - + /* 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); + result = Tcl_EvalEx(interp, string, length, 0); /* * Now we have to be careful; the script may have changed the @@ -1839,163 +1883,13 @@ Tcl_FSEvalFileEx( Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (file \"%.*s%s\" line %d)", (overflow ? limit : length), pathString, - (overflow ? "..." : ""), Tcl_GetErrorLine(interp))); + (overflow ? "..." : ""), interp->errorLine)); } 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; -} /* *---------------------------------------------------------------------- @@ -2019,11 +1913,6 @@ EvalFileCallback( int Tcl_GetErrno(void) { - /* - * On some platforms, errno is really a thread local (implemented by the C - * library). - */ - return errno; } @@ -2032,9 +1921,7 @@ Tcl_GetErrno(void) * * Tcl_SetErrno -- * - * 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! + * Sets the Tcl error code variable to the supplied value. * * Results: * None. @@ -2049,11 +1936,6 @@ void Tcl_SetErrno( int err) /* The new value. */ { - /* - * On some platforms, errno is really a thread local (implemented by the C - * library). - */ - errno = err; } @@ -2115,10 +1997,72 @@ Tcl_FSStat( Tcl_Obj *pathPtr, /* Path of file to stat (in current CP). */ Tcl_StatBuf *buf) /* Filled with results of stat call. */ { - const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); + const 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); + } - if (fsPtr != NULL && fsPtr->statProc != NULL) { - return fsPtr->statProc(pathPtr, buf); + 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.) + */ + + 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_STRUCT_STAT_ST_BLKSIZE + buf->st_blksize = oldStyleStatBuffer.st_blksize; +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + 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); + } } Tcl_SetErrno(ENOENT); return -1; @@ -2149,13 +2093,15 @@ Tcl_FSLstat( Tcl_StatBuf *buf) /* Filled with results of stat call. */ { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - if (fsPtr != NULL) { - if (fsPtr->lstatProc != NULL) { - return fsPtr->lstatProc(pathPtr, buf); - } - if (fsPtr->statProc != NULL) { - return fsPtr->statProc(pathPtr, buf); + 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); + } } } Tcl_SetErrno(ENOENT); @@ -2184,11 +2130,51 @@ Tcl_FSAccess( Tcl_Obj *pathPtr, /* Path of file to access (in current CP). */ int mode) /* Permission setting. */ { - const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); + const Tcl_Filesystem *fsPtr; +#ifdef USE_OBSOLETE_FS_HOOKS + int retVal = -1; - if (fsPtr != NULL && fsPtr->accessProc != NULL) { - return fsPtr->accessProc(pathPtr, mode); + /* + * Call each of the "access" function in succession. A non-return value of + * -1 indicates the particular function has succeeded. + */ + + Tcl_MutexLock(&obsoleteFsHookMutex); + + 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); + } } + Tcl_SetErrno(ENOENT); return -1; } @@ -2224,6 +2210,41 @@ Tcl_FSOpenFileChannel( const Tcl_Filesystem *fsPtr; Tcl_Channel retVal = NULL; +#ifdef USE_OBSOLETE_FS_HOOKS + /* + * 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. @@ -2234,47 +2255,49 @@ Tcl_FSOpenFileChannel( } fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - if (fsPtr != NULL && fsPtr->openFileChannelProc != NULL) { - int mode, seekFlag, binary; + if (fsPtr != NULL) { + Tcl_FSOpenFileChannelProc *proc = fsPtr->openFileChannelProc; + if (proc != NULL) { + int mode, seekFlag, binary; - /* - * Parse the mode, picking up whether we want to seek to start with - * and/or set the channel automatically into binary mode. - */ + /* + * Parse the mode, picking up whether we want to seek to start + * with and/or set the channel automatically into binary mode. + */ - mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary); - if (mode == -1) { - return NULL; - } + mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary); + if (mode == -1) { + return NULL; + } - /* - * Do the actual open() call. - */ + /* + * Do the actual open() call. + */ - retVal = fsPtr->openFileChannelProc(interp, pathPtr, mode, - permissions); - if (retVal == NULL) { - return NULL; - } + retVal = (*proc)(interp, pathPtr, mode, permissions); + if (retVal == NULL) { + return NULL; + } - /* - * Apply appropriate flags parsed out above. - */ + /* + * 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))); + if (seekFlag && Tcl_Seek(retVal, (Tcl_WideInt)0, + SEEK_END) < (Tcl_WideInt)0) { + if (interp != NULL) { + Tcl_AppendResult(interp, "could not seek to end " + "of file while opening \"", Tcl_GetString(pathPtr), + "\": ", Tcl_PosixError(interp), NULL); + } + Tcl_Close(NULL, retVal); + return NULL; } - Tcl_Close(NULL, retVal); - return NULL; - } - if (binary) { - Tcl_SetChannelOption(interp, retVal, "-translation", "binary"); + if (binary) { + Tcl_SetChannelOption(interp, retVal, "-translation", "binary"); + } + return retVal; } - return retVal; } /* @@ -2283,9 +2306,8 @@ Tcl_FSOpenFileChannel( Tcl_SetErrno(ENOENT); if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't open \"%s\": %s", - Tcl_GetString(pathPtr), Tcl_PosixError(interp))); + Tcl_AppendResult(interp, "couldn't open \"", Tcl_GetString(pathPtr), + "\": ", Tcl_PosixError(interp), NULL); } return NULL; } @@ -2309,17 +2331,17 @@ Tcl_FSOpenFileChannel( int Tcl_FSUtime( - Tcl_Obj *pathPtr, /* File to change access/modification - * times. */ + Tcl_Obj *pathPtr, /* File to change access/modification times */ struct utimbuf *tval) /* Structure containing access/modification * times to use. Should not be modified. */ { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - - if (fsPtr != NULL && fsPtr->utimeProc != NULL) { - return fsPtr->utimeProc(pathPtr, tval); + if (fsPtr != NULL) { + Tcl_FSUtimeProc *proc = fsPtr->utimeProc; + if (proc != NULL) { + return (*proc)(pathPtr, tval); + } } - /* TODO: set errno here? Tcl_SetErrno(ENOENT); */ return -1; } @@ -2343,7 +2365,7 @@ Tcl_FSUtime( *---------------------------------------------------------------------- */ -static const char *const * +static const char ** NativeFileAttrStrings( Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef) @@ -2380,7 +2402,8 @@ NativeFileAttrsGet( 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); } /* @@ -2409,7 +2432,7 @@ NativeFileAttrsSet( 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); } /* @@ -2436,15 +2459,18 @@ NativeFileAttrsSet( *---------------------------------------------------------------------- */ -const char *const * +const char ** Tcl_FSFileAttrStrings( Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef) { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - if (fsPtr != NULL && fsPtr->fileAttrStringsProc != NULL) { - return fsPtr->fileAttrStringsProc(pathPtr, objPtrRef); + if (fsPtr != NULL) { + Tcl_FSFileAttrStringsProc *proc = fsPtr->fileAttrStringsProc; + if (proc != NULL) { + return (*proc)(pathPtr, objPtrRef); + } } Tcl_SetErrno(ENOENT); return NULL; @@ -2475,7 +2501,7 @@ TclFSFileAttrIndex( int *indexPtr) /* Where to write the found index. */ { Tcl_Obj *listObj = NULL; - const char *const *attrTable; + const char **attrTable; /* * Get the attribute table for the file. @@ -2557,8 +2583,11 @@ Tcl_FSFileAttrsGet( { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - if (fsPtr != NULL && fsPtr->fileAttrsGetProc != NULL) { - return fsPtr->fileAttrsGetProc(interp, index, pathPtr, objPtrRef); + if (fsPtr != NULL) { + Tcl_FSFileAttrsGetProc *proc = fsPtr->fileAttrsGetProc; + if (proc != NULL) { + return (*proc)(interp, index, pathPtr, objPtrRef); + } } Tcl_SetErrno(ENOENT); return -1; @@ -2591,8 +2620,11 @@ Tcl_FSFileAttrsSet( { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - if (fsPtr != NULL && fsPtr->fileAttrsSetProc != NULL) { - return fsPtr->fileAttrsSetProc(interp, index, pathPtr, objPtr); + if (fsPtr != NULL) { + Tcl_FSFileAttrsSetProc *proc = fsPtr->fileAttrsSetProc; + if (proc != NULL) { + return (*proc)(interp, index, pathPtr, objPtr); + } } Tcl_SetErrno(ENOENT); return -1; @@ -2655,58 +2687,55 @@ Tcl_FSGetCwd( fsRecPtr = FsGetFirstFilesystem(); 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); + 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); + 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 { + (*fsRecPtr->fsPtr->freeInternalRepProc)(retCd); + } + Tcl_DecrRefCount(retVal); + retVal = NULL; + Disclaim(); + goto cdDidNotChange; + } else if (interp != NULL) { + Tcl_AppendResult(interp, + "error getting working directory name: ", + Tcl_PosixError(interp), NULL); + } } else { - fsRecPtr->fsPtr->freeInternalRepProc(retCd); + retVal = (*proc)(interp); } - 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(); @@ -2721,7 +2750,6 @@ Tcl_FSGetCwd( if (retVal != NULL) { Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, retVal); - if (norm != NULL) { /* * We found a cwd, which is now in our global storage. We must @@ -2736,7 +2764,6 @@ Tcl_FSGetCwd( */ ClientData cd = (ClientData) Tcl_FSGetNativePath(norm); - FsUpdateCwd(norm, TclNativeDupInternalRep(cd)); Tcl_DecrRefCount(norm); } @@ -2750,10 +2777,7 @@ Tcl_FSGetCwd( * the permissions on that directory have changed. */ - const Tcl_Filesystem *fsPtr = - Tcl_FSGetFileSystemForPath(tsdPtr->cwdPathPtr); - ClientData retCd = NULL; - Tcl_Obj *retVal, *norm; + const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(tsdPtr->cwdPathPtr); /* * If the filesystem couldn't be found, or if no cwd function exists @@ -2764,98 +2788,93 @@ Tcl_FSGetCwd( * (This is tested for in the test suite on unix). */ - if (fsPtr == NULL || fsPtr->getCwdProc == NULL) { - goto cdDidNotChange; - } - - if (fsPtr->version == TCL_FILESYSTEM_VERSION_1) { - retVal = fsPtr->getCwdProc(interp); - } else { - /* - * New API. - */ - - TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2 *) fsPtr->getCwdProc; - - retCd = proc2(tsdPtr->cwdClientData); - if (retCd == NULL && interp != NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "error getting working directory name: %s", - Tcl_PosixError(interp))); - } - - if (retCd == tsdPtr->cwdClientData) { - goto cdDidNotChange; - } - - /* - * 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. - */ + 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), NULL); + } - norm = TclFSNormalizeAbsolutePath(interp, retVal); + if (retCd == tsdPtr->cwdClientData) { + goto cdDidNotChange; + } - /* - * Check whether cwd has changed from the value previously stored in - * cwdPathPtr. Really 'norm' shouldn't be NULL, but we are careful. - */ + /* + * Looks like a new current directory. + */ - 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. - */ + retVal = (*fsPtr->internalToNormalizedProc)(retCd); + Tcl_IncrRefCount(retVal); + } else { + retVal = (*proc)(interp); + } + if (retVal != NULL) { + Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, retVal); - int len1, len2; - const char *str1, *str2; + /* + * Check whether cwd has changed from the value previously + * stored in cwdPathPtr. Really 'norm' shouldn't be NULL, + * but we are careful. + */ - 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. - */ + 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. + */ - cdEqual: - Tcl_DecrRefCount(norm); - if (retCd != NULL) { - fsPtr->freeInternalRepProc(retCd); + FsUpdateCwd(NULL, NULL); } - } else { - FsUpdateCwd(norm, retCd); - Tcl_DecrRefCount(norm); } } - Tcl_DecrRefCount(retVal); } cdDidNotChange: @@ -2900,13 +2919,14 @@ Tcl_FSChdir( fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); if (fsPtr != NULL) { - if (fsPtr->chdirProc != NULL) { + Tcl_FSChdirProc *proc = fsPtr->chdirProc; + if (proc != NULL) { /* * If this fails, an appropriate errno will have been stored using * 'Tcl_SetErrno()'. */ - retVal = fsPtr->chdirProc(pathPtr); + retVal = (*proc)(pathPtr); } else { /* * Fallback on stat-based implementation. @@ -2918,7 +2938,7 @@ Tcl_FSChdir( * 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 * '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)) @@ -2940,7 +2960,9 @@ Tcl_FSChdir( * 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 @@ -3000,9 +3022,8 @@ Tcl_FSChdir( * Assumption we are using a filesystem version 2. */ - TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2 *) fsPtr->getCwdProc; - - cd = proc2(oldcd); + TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)fsPtr->getCwdProc; + cd = (*proc2)(oldcd); if (cd != oldcd) { FsUpdateCwd(normDirName, cd); } @@ -3061,8 +3082,9 @@ Tcl_FSLoadFile( * function which should be used for this * file. */ { - const char *symbols[3]; - void *procPtrs[2]; + const char *symbols[2]; + Tcl_PackageInitProc **procPtrs[2]; + ClientData clientData; int res; /* @@ -3071,27 +3093,35 @@ Tcl_FSLoadFile( symbols[0] = sym1; symbols[1] = sym2; - symbols[2] = NULL; + procPtrs[0] = proc1Ptr; + procPtrs[1] = proc2Ptr; /* * Perform the load. */ - 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; - } + res = TclLoadFile(interp, pathPtr, 2, symbols, procPtrs, handlePtr, + &clientData, unloadProcPtr); + + /* + * 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. + */ + *handlePtr = (Tcl_LoadHandle) clientData; return res; } /* *---------------------------------------------------------------------- * - * Tcl_LoadFile -- + * TclLoadFile -- * * Dynamically loads a binary code file into memory and returns the * addresses of a number of given functions within that file, if they are @@ -3105,17 +3135,26 @@ Tcl_FSLoadFile( * 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 * is left in the interp's result. * * Side effects: * New code suddenly appears in memory. This may later be unloaded by - * calling TclFS_UnloadFile. + * passing the clientData to the unloadProc. * *---------------------------------------------------------------------- */ +typedef int (Tcl_FSLoadFileProc2) (Tcl_Interp *interp, Tcl_Obj *pathPtr, + Tcl_LoadHandle *handlePtr, Tcl_FSUnloadFileProc **unloadProcPtr, int flags); + /* * Workaround for issue with modern HPUX which do allow the unlink (no ETXTBSY * error) yet somehow trash some internal data structures which prevents the @@ -3193,44 +3232,57 @@ TclSkipUnlink (Tcl_Obj* shlibFile) } int -Tcl_LoadFile( +TclLoadFile( Tcl_Interp *interp, /* Used for error reporting. */ Tcl_Obj *pathPtr, /* Name of the file containing the desired * code. */ - const char *const symbols[],/* Names of functions to look up in the file's + int symc, /* Number of symbols/procPtrs in the next two + * arrays. */ + const char *symbols[], /* Names of functions to look up in the file's * symbol table. */ - int flags, /* Flags */ - void *procVPtrs, /* Where to return the addresses corresponding + Tcl_PackageInitProc **procPtrs[], + /* Where to return the addresses corresponding * to symbols[]. */ - Tcl_LoadHandle *handlePtr) /* Filled with token for shared library + 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. */ { - void **procPtrs = (void **) procVPtrs; const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - const Tcl_Filesystem *copyFsPtr; - Tcl_FSUnloadFileProc *unloadProcPtr; + Tcl_FSLoadFileProc *proc; + Tcl_Filesystem *copyFsPtr; Tcl_Obj *copyToPtr; Tcl_LoadHandle newLoadHandle = NULL; - Tcl_LoadHandle divertedLoadHandle = NULL; + ClientData newClientData = NULL; Tcl_FSUnloadFileProc *newUnloadProcPtr = NULL; FsDivertLoad *tvdlPtr; int retVal; - int i; if (fsPtr == NULL) { Tcl_SetErrno(ENOENT); return TCL_ERROR; } - if (fsPtr->loadFileProc != NULL) { - int retVal = ((Tcl_FSLoadFileProc2 *)(fsPtr->loadFileProc)) - (interp, pathPtr, handlePtr, &unloadProcPtr, flags); - + proc = fsPtr->loadFileProc; + if (proc != NULL) { + int retVal = ((Tcl_FSLoadFileProc2 *)proc) + (interp, pathPtr, handlePtr, unloadProcPtr, 0); if (retVal == TCL_OK) { if (*handlePtr == NULL) { return TCL_ERROR; } + + /* + * Copy this across, since both are equal for the native fs. + */ + + *clientDataPtr = (ClientData)*handlePtr; Tcl_ResetResult(interp); goto resolveSymbols; } @@ -3247,9 +3299,8 @@ Tcl_LoadFile( */ if (Tcl_FSAccess(pathPtr, R_OK) != 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't load library \"%s\": %s", - Tcl_GetString(pathPtr), Tcl_PosixError(interp))); + Tcl_AppendResult(interp, "couldn't load library \"", + Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); return TCL_ERROR; } @@ -3291,23 +3342,26 @@ Tcl_LoadFile( ret = Tcl_Read(data, buffer, size); Tcl_Close(interp, data); ret = TclpLoadMemory(interp, buffer, size, ret, handlePtr, - &unloadProcPtr, flags); + unloadProcPtr); if (ret == TCL_OK && *handlePtr != NULL) { + *clientDataPtr = (ClientData) *handlePtr; goto resolveSymbols; } } mustCopyToTempAnyway: Tcl_ResetResult(interp); -#endif /* TCL_LOAD_FROM_MEMORY */ +#endif /* * Get a temporary filename to use, first to copy the file into, and then * to load. */ - copyToPtr = TclpTempFileNameForLibrary(interp, pathPtr); + copyToPtr = TclpTempFileName(); if (copyToPtr == NULL) { + Tcl_AppendResult(interp, "couldn't create temporary file: ", + Tcl_PosixError(interp), NULL); return TCL_ERROR; } Tcl_IncrRefCount(copyToPtr); @@ -3322,8 +3376,7 @@ Tcl_LoadFile( Tcl_FSDeleteFile(copyToPtr); Tcl_DecrRefCount(copyToPtr); - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "couldn't load from current filesystem", -1)); + Tcl_AppendResult(interp, "couldn't load from current filesystem",NULL); return TCL_ERROR; } @@ -3337,7 +3390,7 @@ Tcl_LoadFile( return TCL_ERROR; } -#ifndef _WIN32 +#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 @@ -3365,8 +3418,8 @@ Tcl_LoadFile( Tcl_ResetResult(interp); - retVal = Tcl_LoadFile(interp, copyToPtr, symbols, flags, procPtrs, - &newLoadHandle); + retVal = TclLoadFile(interp, copyToPtr, symc, symbols, procPtrs, + &newLoadHandle, &newClientData, &newUnloadProcPtr); if (retVal != TCL_OK) { /* * The file didn't load successfully. @@ -3394,7 +3447,9 @@ Tcl_LoadFile( * handle and unload proc ptr. */ - *handlePtr = newLoadHandle; + (*handlePtr) = newLoadHandle; + (*clientDataPtr) = newClientData; + (*unloadProcPtr) = newUnloadProcPtr; Tcl_ResetResult(interp); return TCL_OK; } @@ -3404,7 +3459,7 @@ Tcl_LoadFile( * unload and cleanup the temporary file correctly. */ - tvdlPtr = ckalloc(sizeof(FsDivertLoad)); + tvdlPtr = (FsDivertLoad *) ckalloc(sizeof(FsDivertLoad)); /* * Remember three pieces of information. This allows us to cleanup the @@ -3449,36 +3504,20 @@ Tcl_LoadFile( } copyToPtr = NULL; - - divertedLoadHandle = ckalloc(sizeof(struct Tcl_LoadHandle_)); - divertedLoadHandle->clientData = tvdlPtr; - divertedLoadHandle->findSymbolProcPtr = DivertFindSymbol; - divertedLoadHandle->unloadFileProcPtr = DivertUnloadFile; - *handlePtr = divertedLoadHandle; + (*handlePtr) = newLoadHandle; + (*clientDataPtr) = (ClientData) tvdlPtr; + (*unloadProcPtr) = TclFSUnloadTempFile; Tcl_ResetResult(interp); return retVal; resolveSymbols: - /* - * At this point, *handlePtr is already set up to the handle for the - * loaded library. We now try to resolve the symbols. - */ - - 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.) - */ + { + int i; - (*handlePtr)->unloadFileProcPtr(*handlePtr); - *handlePtr = NULL; - return TCL_ERROR; + for (i=0 ; i<symc ; i++) { + if (symbols[i] != NULL) { + *procPtrs[i] = TclpFindSymbol(interp, *handlePtr, symbols[i]); } } } @@ -3486,196 +3525,7 @@ Tcl_LoadFile( } /* - *---------------------------------------------------------------------- - * - * DivertFindSymbol -- - * - * Find a symbol in a shared library loaded by copy-from-VFS. - * - *---------------------------------------------------------------------- - */ - -static void * -DivertFindSymbol( - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_LoadHandle loadHandle, /* Handle to the diverted module */ - const char *symbol) /* Symbol to resolve */ -{ - 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; - - /* - * This test should never trigger, since we give the client data in the - * function above. - */ - - if (tvdlPtr == NULL) { - return; - } - originalHandle = tvdlPtr->loadHandle; - - /* - * 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); - } - - 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. - * - *---------------------------------------------------------------------- - */ - -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; -} - -/* - *---------------------------------------------------------------------- - * - * 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 -- * @@ -3690,7 +3540,7 @@ TclpUnloadFile( * The effects of the 'unload' function called, and of course the * temporary file will be deleted. * - *---------------------------------------------------------------------- + *--------------------------------------------------------------------------- */ void @@ -3718,7 +3568,7 @@ TclFSUnloadTempFile( */ if (tvdlPtr->unloadProcPtr != NULL) { - tvdlPtr->unloadProcPtr(tvdlPtr->loadHandle); + (*tvdlPtr->unloadProcPtr)(tvdlPtr->loadHandle); } if (tvdlPtr->divertedFilesystem == NULL) { @@ -3730,6 +3580,7 @@ TclFSUnloadTempFile( TclpDeleteFile(tvdlPtr->divertedFileNativeRep); NativeFreeInternalRep(tvdlPtr->divertedFileNativeRep); + } else { /* * Remove the temporary file we created. Note, we may crash here @@ -3764,7 +3615,7 @@ TclFSUnloadTempFile( Tcl_DecrRefCount(tvdlPtr->divertedFile); } - ckfree(tvdlPtr); + ckfree((char*)tvdlPtr); } /* @@ -3802,14 +3653,18 @@ TclFSUnloadTempFile( Tcl_Obj * 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_Obj *pathPtr, /* Path of file to readlink or link */ + Tcl_Obj *toPtr, /* NULL or path to be linked to */ + int linkAction) /* Action to perform */ { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - if (fsPtr != NULL && fsPtr->linkProc != NULL) { - return fsPtr->linkProc(pathPtr, toPtr, linkAction); + if (fsPtr != NULL) { + Tcl_FSLinkProc *proc = fsPtr->linkProc; + + if (proc != NULL) { + return (*proc)(pathPtr, toPtr, linkAction); + } } /* @@ -3821,7 +3676,7 @@ Tcl_FSLink( */ #ifndef S_IFLNK - errno = EINVAL; /* TODO: Change to Tcl_SetErrno()? */ + errno = EINVAL; #else Tcl_SetErrno(ENOENT); #endif /* S_IFLNK */ @@ -3853,7 +3708,7 @@ Tcl_FSLink( *--------------------------------------------------------------------------- */ -Tcl_Obj * +Tcl_Obj* Tcl_FSListVolumes(void) { FilesystemRecord *fsRecPtr; @@ -3869,9 +3724,9 @@ Tcl_FSListVolumes(void) fsRecPtr = FsGetFirstFilesystem(); Claim(); while (fsRecPtr != NULL) { - if (fsRecPtr->fsPtr->listVolumesProc != NULL) { - Tcl_Obj *thisFsVolumes = fsRecPtr->fsPtr->listVolumesProc(); - + Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc; + if (proc != NULL) { + Tcl_Obj *thisFsVolumes = (*proc)(); if (thisFsVolumes != NULL) { Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes); Tcl_DecrRefCount(thisFsVolumes); @@ -3921,13 +3776,15 @@ FsListMounts( fsRecPtr = FsGetFirstFilesystem(); Claim(); while (fsRecPtr != NULL) { - if (fsRecPtr->fsPtr != &tclNativeFilesystem && - fsRecPtr->fsPtr->matchInDirectoryProc != NULL) { - if (resultPtr == NULL) { - resultPtr = Tcl_NewObj(); + if (fsRecPtr->fsPtr != &tclNativeFilesystem) { + Tcl_FSMatchInDirectoryProc *proc = + fsRecPtr->fsPtr->matchInDirectoryProc; + if (proc != NULL) { + if (resultPtr == NULL) { + resultPtr = Tcl_NewObj(); + } + (*proc)(NULL, resultPtr, pathPtr, pattern, &mountsOnly); } - fsRecPtr->fsPtr->matchInDirectoryProc(NULL, resultPtr, pathPtr, - pattern, &mountsOnly); } fsRecPtr = fsRecPtr->nextPtr; } @@ -3962,10 +3819,10 @@ Tcl_FSSplitPath( int *lenPtr) /* int to store number of path elements. */ { Tcl_Obj *result = NULL; /* Needed only to prevent gcc warnings. */ - const Tcl_Filesystem *fsPtr; + Tcl_Filesystem *fsPtr; char separator = '/'; int driveNameLength; - const char *p; + char *p; /* * Perform platform specific splitting. @@ -3985,8 +3842,7 @@ Tcl_FSSplitPath( */ 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]; @@ -4011,16 +3867,14 @@ Tcl_FSSplitPath( */ for (;;) { - const char *elementStart = p; + char *elementStart = p; int length; - while ((*p != '\0') && (*p != separator)) { p++; } length = p - elementStart; if (length > 0) { Tcl_Obj *nextElt; - if (elementStart[0] == '~') { TclNewLiteralStringObj(nextElt, "./"); Tcl_AppendToObj(nextElt, elementStart, length); @@ -4043,6 +3897,7 @@ Tcl_FSSplitPath( } return result; } + /* *---------------------------------------------------------------------- * @@ -4064,8 +3919,8 @@ Tcl_FSSplitPath( Tcl_PathType TclGetPathType( - Tcl_Obj *pathPtr, /* Path to determine type for. */ - const Tcl_Filesystem **filesystemPtrPtr, + 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. */ @@ -4079,9 +3934,11 @@ TclGetPathType( * caller. */ { int pathLen; - const char *path = Tcl_GetStringFromObj(pathPtr, &pathLen); + char *path; Tcl_PathType type; + path = Tcl_GetStringFromObj(pathPtr, &pathLen); + type = TclFSNonnativePathType(path, pathLen, filesystemPtrPtr, driveNameLengthPtr, driveNameRef); @@ -4119,9 +3976,9 @@ TclGetPathType( Tcl_PathType TclFSNonnativePathType( - const char *path, /* Path to determine type for. */ - int pathLen, /* Length of the path. */ - const Tcl_Filesystem **filesystemPtrPtr, + 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. */ @@ -4146,37 +4003,39 @@ TclFSNonnativePathType( 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 - * 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. + * 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. * * 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) - && (fsRecPtr->fsPtr->listVolumesProc != NULL)) { + if ((fsRecPtr->fsPtr != &tclNativeFilesystem) && (proc != NULL)) { int numVolumes; - Tcl_Obj *thisFsVolumes = fsRecPtr->fsPtr->listVolumesProc(); + Tcl_Obj *thisFsVolumes = (*proc)(); if (thisFsVolumes != NULL) { if (Tcl_ListObjLength(NULL, thisFsVolumes, &numVolumes) != TCL_OK) { /* - * 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'. + * 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'. * * It would be better if we could signal an error here * (but Tcl_Panic seems a bit excessive). @@ -4187,7 +4046,7 @@ TclFSNonnativePathType( while (numVolumes > 0) { Tcl_Obj *vol; int len; - const char *strVol; + char *strVol; numVolumes--; Tcl_ListObjIndex(NULL, thisFsVolumes, numVolumes, &vol); @@ -4215,7 +4074,6 @@ TclFSNonnativePathType( /* * We don't need to examine any more filesystems. */ - break; } } @@ -4246,20 +4104,21 @@ TclFSNonnativePathType( int Tcl_FSRenameFile( - Tcl_Obj *srcPathPtr, /* Pathname of file or dir to be renamed + Tcl_Obj* srcPathPtr, /* Pathname of file or dir to be renamed * (UTF-8). */ Tcl_Obj *destPathPtr) /* New pathname of file or directory * (UTF-8). */ { int retVal = -1; const Tcl_Filesystem *fsPtr, *fsPtr2; - fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr); fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr); - if ((fsPtr == fsPtr2) && (fsPtr != NULL) - && (fsPtr->renameFileProc != NULL)) { - retVal = fsPtr->renameFileProc(srcPathPtr, destPathPtr); + if ((fsPtr == fsPtr2) && (fsPtr != NULL)) { + Tcl_FSRenameFileProc *proc = fsPtr->renameFileProc; + if (proc != NULL) { + retVal = (*proc)(srcPathPtr, destPathPtr); + } } if (retVal == -1) { Tcl_SetErrno(EXDEV); @@ -4296,12 +4155,14 @@ Tcl_FSCopyFile( { int retVal = -1; const Tcl_Filesystem *fsPtr, *fsPtr2; - fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr); fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr); - if (fsPtr == fsPtr2 && fsPtr != NULL && fsPtr->copyFileProc != NULL) { - retVal = fsPtr->copyFileProc(srcPathPtr, destPathPtr); + if (fsPtr == fsPtr2 && fsPtr != NULL) { + Tcl_FSCopyFileProc *proc = fsPtr->copyFileProc; + if (proc != NULL) { + retVal = (*proc)(srcPathPtr, destPathPtr); + } } if (retVal == -1) { Tcl_SetErrno(EXDEV); @@ -4326,10 +4187,9 @@ Tcl_FSCopyFile( * *--------------------------------------------------------------------------- */ - int TclCrossFilesystemCopy( - Tcl_Interp *interp, /* For error messages. */ + 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). */ { @@ -4409,9 +4269,11 @@ Tcl_FSDeleteFile( Tcl_Obj *pathPtr) /* Pathname of file to be removed (UTF-8). */ { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - - if (fsPtr != NULL && fsPtr->deleteFileProc != NULL) { - return fsPtr->deleteFileProc(pathPtr); + if (fsPtr != NULL) { + Tcl_FSDeleteFileProc *proc = fsPtr->deleteFileProc; + if (proc != NULL) { + return (*proc)(pathPtr); + } } Tcl_SetErrno(ENOENT); return -1; @@ -4439,9 +4301,11 @@ Tcl_FSCreateDirectory( Tcl_Obj *pathPtr) /* Pathname of directory to create (UTF-8). */ { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - - if (fsPtr != NULL && fsPtr->createDirectoryProc != NULL) { - return fsPtr->createDirectoryProc(pathPtr); + if (fsPtr != NULL) { + Tcl_FSCreateDirectoryProc *proc = fsPtr->createDirectoryProc; + if (proc != NULL) { + return (*proc)(pathPtr); + } } Tcl_SetErrno(ENOENT); return -1; @@ -4467,7 +4331,7 @@ Tcl_FSCreateDirectory( int Tcl_FSCopyDirectory( - Tcl_Obj *srcPathPtr, /* Pathname of directory to be copied + 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 @@ -4476,12 +4340,14 @@ Tcl_FSCopyDirectory( { int retVal = -1; const Tcl_Filesystem *fsPtr, *fsPtr2; - fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr); fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr); - if (fsPtr == fsPtr2 && fsPtr != NULL && fsPtr->copyDirectoryProc != NULL){ - retVal = fsPtr->copyDirectoryProc(srcPathPtr, destPathPtr, errorPtr); + if (fsPtr == fsPtr2 && fsPtr != NULL) { + Tcl_FSCopyDirectoryProc *proc = fsPtr->copyDirectoryProc; + if (proc != NULL) { + retVal = (*proc)(srcPathPtr, destPathPtr, errorPtr); + } } if (retVal == -1) { Tcl_SetErrno(EXDEV); @@ -4518,46 +4384,45 @@ Tcl_FSRemoveDirectory( * error, with refCount 1. */ { const 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. + */ - if (fsPtr == NULL || fsPtr->removeDirectoryProc == NULL) { - Tcl_SetErrno(ENOENT); - return -1; - } - - /* - * When working recursively, we check whether the cwd lies inside this - * directory and move it if it does. - */ - - if (recursive) { - Tcl_Obj *cwdPtr = Tcl_FSGetCwd(NULL); + Tcl_Obj *cwdPtr = Tcl_FSGetCwd(NULL); - if (cwdPtr != NULL) { - const char *cwdStr, *normPathStr; - int cwdLen, normLen; - Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr); + if (cwdPtr != NULL) { + char *cwdStr, *normPathStr; + int cwdLen, normLen; + Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr); - 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 (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_Obj *dirPtr = TclPathPart(NULL, pathPtr, - TCL_PATH_DIRNAME); + Tcl_Obj *dirPtr = TclPathPart(NULL, pathPtr, + TCL_PATH_DIRNAME); - Tcl_FSChdir(dirPtr); - Tcl_DecrRefCount(dirPtr); + Tcl_FSChdir(dirPtr); + Tcl_DecrRefCount(dirPtr); + } } + Tcl_DecrRefCount(cwdPtr); } - Tcl_DecrRefCount(cwdPtr); } + return (*proc)(pathPtr, recursive, errorPtr); } - return fsPtr->removeDirectoryProc(pathPtr, recursive, errorPtr); + Tcl_SetErrno(ENOENT); + return -1; } /* @@ -4579,12 +4444,12 @@ Tcl_FSRemoveDirectory( *--------------------------------------------------------------------------- */ -const Tcl_Filesystem * +Tcl_Filesystem * Tcl_FSGetFileSystemForPath( - Tcl_Obj *pathPtr) + Tcl_Obj* pathPtr) { FilesystemRecord *fsRecPtr; - const Tcl_Filesystem *retVal = NULL; + Tcl_Filesystem* retVal = NULL; if (pathPtr == NULL) { Tcl_Panic("Tcl_FSGetFileSystemForPath called with NULL object"); @@ -4615,10 +4480,6 @@ Tcl_FSGetFileSystemForPath( if (TclFSEnsureEpochOk(pathPtr, &retVal) != TCL_OK) { Disclaim(); return NULL; - } else if (retVal != NULL) { - /* TODO: Can this happen? */ - Disclaim(); - return retVal; } /* @@ -4626,27 +4487,27 @@ Tcl_FSGetFileSystemForPath( * non-return value of -1 indicates the particular function has succeeded. */ - for (; fsRecPtr!=NULL ; fsRecPtr=fsRecPtr->nextPtr) { - ClientData clientData = NULL; - - if (fsRecPtr->fsPtr->pathInFilesystemProc == NULL) { - continue; - } + while ((retVal == NULL) && (fsRecPtr != NULL)) { + Tcl_FSPathInFilesystemProc *proc = + fsRecPtr->fsPtr->pathInFilesystemProc; - if (fsRecPtr->fsPtr->pathInFilesystemProc(pathPtr, &clientData)!=-1) { - /* - * We assume the type of pathPtr hasn't been changed by the above - * call to the pathInFilesystemProc. - */ + if (proc != NULL) { + ClientData clientData = NULL; + if ((*proc)(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; + TclFSSetPathDetails(pathPtr, fsRecPtr->fsPtr, clientData); + retVal = fsRecPtr->fsPtr; + } } + fsRecPtr = fsRecPtr->nextPtr; } Disclaim(); - return NULL; + return retVal; } /* @@ -4664,7 +4525,7 @@ Tcl_FSGetFileSystemForPath( * 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 desirable to have separate versions + * Note: in the future it might be desireable to have separate versions * of this function with different signatures, for example * Tcl_FSGetNativeWinPath, Tcl_FSGetNativeUnixPath etc. Right now, since * native paths are all string based, we use just one function. @@ -4678,11 +4539,11 @@ Tcl_FSGetFileSystemForPath( *--------------------------------------------------------------------------- */ -const void * +const char * Tcl_FSGetNativePath( Tcl_Obj *pathPtr) { - return Tcl_FSGetInternalRep(pathPtr, &tclNativeFilesystem); + return (const char *) Tcl_FSGetInternalRep(pathPtr, &tclNativeFilesystem); } /* @@ -4705,7 +4566,7 @@ static void NativeFreeInternalRep( ClientData clientData) { - ckfree(clientData); + ckfree((char *) clientData); } /* @@ -4731,6 +4592,7 @@ Tcl_FSFileSystemInfo( Tcl_Obj *pathPtr) { Tcl_Obj *resPtr; + Tcl_FSFilesystemPathTypeProc *proc; const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); if (fsPtr == NULL) { @@ -4738,12 +4600,11 @@ Tcl_FSFileSystemInfo( } resPtr = Tcl_NewListObj(0, NULL); - Tcl_ListObjAppendElement(NULL, resPtr, - Tcl_NewStringObj(fsPtr->typeName, -1)); - - if (fsPtr->filesystemPathTypeProc != NULL) { - Tcl_Obj *typePtr = fsPtr->filesystemPathTypeProc(pathPtr); + Tcl_ListObjAppendElement(NULL,resPtr,Tcl_NewStringObj(fsPtr->typeName,-1)); + proc = fsPtr->filesystemPathTypeProc; + if (proc != NULL) { + Tcl_Obj *typePtr = (*proc)(pathPtr); if (typePtr != NULL) { Tcl_ListObjAppendElement(NULL, resPtr, typePtr); } @@ -4776,23 +4637,23 @@ Tcl_FSPathSeparator( Tcl_Obj *pathPtr) { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); - Tcl_Obj *resultObj; if (fsPtr == NULL) { return NULL; } - if (fsPtr->filesystemSeparatorProc != NULL) { - return fsPtr->filesystemSeparatorProc(pathPtr); - } + return (*fsPtr->filesystemSeparatorProc)(pathPtr); + } else { + Tcl_Obj *resultObj; - /* - * Allow filesystems not to provide a filesystemSeparatorProc if they wish - * to use the standard forward slash. - */ + /* + * Allow filesystems not to provide a filesystemSeparatorProc if they + * wish to use the standard forward slash. + */ - TclNewLiteralStringObj(resultObj, "/"); - return resultObj; + TclNewLiteralStringObj(resultObj, "/"); + return resultObj; + } } /* @@ -4817,7 +4678,6 @@ NativeFilesystemSeparator( Tcl_Obj *pathPtr) { const char *separator = NULL; /* lint */ - switch (tclPlatform) { case TCL_PLATFORM_UNIX: separator = "/"; @@ -4828,6 +4688,318 @@ NativeFilesystemSeparator( } return Tcl_NewStringObj(separator,1); } + +/* Everything from here on is contained in this obsolete ifdef */ +#ifdef USE_OBSOLETE_FS_HOOKS + +/* + *---------------------------------------------------------------------- + * + * TclStatInsertProc -- + * + * Insert the passed function 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( + 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 removable. + * + * Results: + * TCL_OK if the function pointer was successfully removed, TCL_ERROR + * otherwise. + * + * Side effects: + * Memory is deallocated and the respective list updated. + * + *---------------------------------------------------------------------- + */ + +int +TclStatDeleteProc( + 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 function 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( + 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 removable. + * + * Results: + * TCL_OK if the function pointer was successfully removed, TCL_ERROR + * otherwise. + * + * Side effects: + * Memory is deallocated and the respective list updated. + * + *---------------------------------------------------------------------- + */ + +int +TclAccessDeleteProc( + 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 function 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( + 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 function pointer was successfully removed, TCL_ERROR + * otherwise. + * + * Side effects: + * Memory is deallocated and the respective list updated. + * + *---------------------------------------------------------------------- + */ + +int +TclOpenFileChannelDeleteProc( + 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: |