tclIOUtil.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:187k
- * succeeded. We call all the functions registered, since we want
- * a list of all drives from all filesystems.
- */
- fsRecPtr = FsGetFirstFilesystem();
- while (fsRecPtr != NULL) {
- Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc;
- if (proc != NULL) {
- Tcl_Obj *thisFsVolumes = (*proc)();
- if (thisFsVolumes != NULL) {
- Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes);
- Tcl_DecrRefCount(thisFsVolumes);
- }
- }
- fsRecPtr = fsRecPtr->nextPtr;
- }
-
- return resultPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * FsListMounts --
- *
- * List all mounts within the given directory, which match the
- * given pattern.
- *
- * Results:
- * The list of mounts, in a list object which has refCount 0, or
- * NULL if we didn't even find any filesystems to try to list
- * mounts.
- *
- * Side effects:
- * None
- *
- *---------------------------------------------------------------------------
- */
- static Tcl_Obj*
- FsListMounts(pathPtr, pattern)
- Tcl_Obj *pathPtr; /* Contains path to directory to search. */
- CONST char *pattern; /* Pattern to match against. */
- {
- FilesystemRecord *fsRecPtr;
- Tcl_GlobTypeData mountsOnly = { TCL_GLOB_TYPE_MOUNT, 0, NULL, NULL };
- Tcl_Obj *resultPtr = NULL;
-
- /*
- * Call each of the "listMounts" functions in succession.
- * A non-NULL return value indicates the particular function has
- * succeeded. We call all the functions registered, since we want
- * a list from each filesystems.
- */
- fsRecPtr = FsGetFirstFilesystem();
- while (fsRecPtr != NULL) {
- 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 = fsRecPtr->nextPtr;
- }
-
- return resultPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSSplitPath --
- *
- * This function takes the given Tcl_Obj, which should be a valid
- * path, and returns a Tcl List object containing each segment of
- * that path as an element.
- *
- * Results:
- * Returns list object with refCount of zero. If the passed in
- * lenPtr is non-NULL, we use it to return the number of elements
- * in the returned list.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj*
- Tcl_FSSplitPath(pathPtr, lenPtr)
- Tcl_Obj *pathPtr; /* Path to split. */
- int *lenPtr; /* int to store number of path elements. */
- {
- Tcl_Obj *result = NULL; /* Needed only to prevent gcc warnings. */
- Tcl_Filesystem *fsPtr;
- char separator = '/';
- int driveNameLength;
- char *p;
-
- /*
- * Perform platform specific splitting.
- */
- if (FSGetPathType(pathPtr, &fsPtr, &driveNameLength)
- == TCL_PATH_ABSOLUTE) {
- if (fsPtr == &tclNativeFilesystem) {
- return TclpNativeSplitPath(pathPtr, lenPtr);
- }
- } else {
- return TclpNativeSplitPath(pathPtr, lenPtr);
- }
- /* We assume separators are single characters */
- if (fsPtr->filesystemSeparatorProc != NULL) {
- Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(pathPtr);
- if (sep != NULL) {
- separator = Tcl_GetString(sep)[0];
- }
- }
-
- /*
- * Place the drive name as first element of the
- * result list. The drive name may contain strange
- * characters, like colons and multiple forward slashes
- * (for example 'ftp://' is a valid vfs drive name)
- */
- result = Tcl_NewObj();
- p = Tcl_GetString(pathPtr);
- Tcl_ListObjAppendElement(NULL, result,
- Tcl_NewStringObj(p, driveNameLength));
- p+= driveNameLength;
-
- /* Add the remaining path elements to the list */
- for (;;) {
- char *elementStart = p;
- int length;
- while ((*p != ' ') && (*p != separator)) {
- p++;
- }
- length = p - elementStart;
- if (length > 0) {
- Tcl_Obj *nextElt;
- if (elementStart[0] == '~') {
- nextElt = Tcl_NewStringObj("./",2);
- Tcl_AppendToObj(nextElt, elementStart, length);
- } else {
- nextElt = Tcl_NewStringObj(elementStart, length);
- }
- Tcl_ListObjAppendElement(NULL, result, nextElt);
- }
- if (*p++ == ' ') {
- break;
- }
- }
-
- /*
- * Compute the number of elements in the result.
- */
- if (lenPtr != NULL) {
- Tcl_ListObjLength(NULL, result, lenPtr);
- }
- return result;
- }
- /* Simple helper function */
- Tcl_Obj*
- TclFSInternalToNormalized(fromFilesystem, clientData, fsRecPtrPtr)
- Tcl_Filesystem *fromFilesystem;
- ClientData clientData;
- FilesystemRecord **fsRecPtrPtr;
- {
- FilesystemRecord *fsRecPtr = FsGetFirstFilesystem();
- while (fsRecPtr != NULL) {
- if (fsRecPtr->fsPtr == fromFilesystem) {
- *fsRecPtrPtr = fsRecPtr;
- break;
- }
- fsRecPtr = fsRecPtr->nextPtr;
- }
-
- if ((fsRecPtr != NULL)
- && (fromFilesystem->internalToNormalizedProc != NULL)) {
- return (*fromFilesystem->internalToNormalizedProc)(clientData);
- } else {
- return NULL;
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * GetPathType --
- *
- * Helper function used by FSGetPathType.
- *
- * Results:
- * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
- * TCL_PATH_VOLUME_RELATIVE. The filesystem reference will
- * be set if and only if it is non-NULL and the function's
- * return value is TCL_PATH_ABSOLUTE.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static Tcl_PathType
- GetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr, driveNameRef)
- Tcl_Obj *pathObjPtr;
- Tcl_Filesystem **filesystemPtrPtr;
- int *driveNameLengthPtr;
- Tcl_Obj **driveNameRef;
- {
- FilesystemRecord *fsRecPtr;
- int pathLen;
- char *path;
- Tcl_PathType type = TCL_PATH_RELATIVE;
-
- path = Tcl_GetStringFromObj(pathObjPtr, &pathLen);
- /*
- * Call each of the "listVolumes" function in succession, checking
- * whether the given path is an absolute path on any of the volumes
- * returned (this is done by checking whether the path's prefix
- * matches).
- */
- fsRecPtr = FsGetFirstFilesystem();
- while (fsRecPtr != NULL) {
- Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc;
- /*
- * We want to skip the native filesystem in this loop because
- * otherwise we won't necessarily pass all the Tcl testsuite --
- * this is because some of the tests artificially change the
- * current platform (between mac, win, unix) but the list
- * of volumes we get by calling (*proc) will reflect the current
- * (real) platform only and this may cause some tests to fail.
- * In particular, on unix '/' will match the beginning of
- * certain absolute Windows paths starting '//' and those tests
- * will go wrong.
- *
- * Besides these test-suite issues, there is 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.
- */
- if ((fsRecPtr->fsPtr != &tclNativeFilesystem) && (proc != NULL)) {
- int numVolumes;
- Tcl_Obj *thisFsVolumes = (*proc)();
- if (thisFsVolumes != NULL) {
- if (Tcl_ListObjLength(NULL, thisFsVolumes,
- &numVolumes) != TCL_OK) {
- /*
- * This is VERY bad; the Tcl_FSListVolumesProc
- * didn't return a valid list. Set numVolumes to
- * -1 so that we skip the while loop below and just
- * return with the current value of 'type'.
- *
- * It would be better if we could signal an error
- * here (but panic seems a bit excessive).
- */
- numVolumes = -1;
- }
- while (numVolumes > 0) {
- Tcl_Obj *vol;
- int len;
- char *strVol;
- numVolumes--;
- Tcl_ListObjIndex(NULL, thisFsVolumes, numVolumes, &vol);
- strVol = Tcl_GetStringFromObj(vol,&len);
- if (pathLen < len) {
- continue;
- }
- if (strncmp(strVol, path, (size_t) len) == 0) {
- type = TCL_PATH_ABSOLUTE;
- if (filesystemPtrPtr != NULL) {
- *filesystemPtrPtr = fsRecPtr->fsPtr;
- }
- if (driveNameLengthPtr != NULL) {
- *driveNameLengthPtr = len;
- }
- if (driveNameRef != NULL) {
- *driveNameRef = vol;
- Tcl_IncrRefCount(vol);
- }
- break;
- }
- }
- Tcl_DecrRefCount(thisFsVolumes);
- if (type == TCL_PATH_ABSOLUTE) {
- /* We don't need to examine any more filesystems */
- break;
- }
- }
- }
- fsRecPtr = fsRecPtr->nextPtr;
- }
-
- if (type != TCL_PATH_ABSOLUTE) {
- type = TclpGetNativePathType(pathObjPtr, driveNameLengthPtr,
- driveNameRef);
- if ((type == TCL_PATH_ABSOLUTE) && (filesystemPtrPtr != NULL)) {
- *filesystemPtrPtr = &tclNativeFilesystem;
- }
- }
- return type;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSRenameFile --
- *
- * If the two paths given belong to the same filesystem, we call
- * that filesystems rename function. Otherwise we simply
- * return the posix error 'EXDEV', and -1.
- *
- * Results:
- * Standard Tcl error code if a function was called.
- *
- * Side effects:
- * A file may be renamed.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_FSRenameFile(srcPathPtr, destPathPtr)
- 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;
- Tcl_Filesystem *fsPtr, *fsPtr2;
- fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
- fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
- if (fsPtr == fsPtr2 && fsPtr != NULL) {
- Tcl_FSRenameFileProc *proc = fsPtr->renameFileProc;
- if (proc != NULL) {
- retVal = (*proc)(srcPathPtr, destPathPtr);
- }
- }
- if (retVal == -1) {
- Tcl_SetErrno(EXDEV);
- }
- return retVal;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSCopyFile --
- *
- * If the two paths given belong to the same filesystem, we call
- * that filesystem's copy function. Otherwise we simply
- * return the posix error 'EXDEV', and -1.
- *
- * Note that in the native filesystems, 'copyFileProc' is defined
- * to copy soft links (i.e. it copies the links themselves, not
- * the things they point to).
- *
- * Results:
- * Standard Tcl error code if a function was called.
- *
- * Side effects:
- * A file may be copied.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_FSCopyFile(srcPathPtr, destPathPtr)
- Tcl_Obj* srcPathPtr; /* Pathname of file to be copied (UTF-8). */
- Tcl_Obj *destPathPtr; /* Pathname of file to copy to (UTF-8). */
- {
- int retVal = -1;
- Tcl_Filesystem *fsPtr, *fsPtr2;
- fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
- fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
- if (fsPtr == fsPtr2 && fsPtr != NULL) {
- Tcl_FSCopyFileProc *proc = fsPtr->copyFileProc;
- if (proc != NULL) {
- retVal = (*proc)(srcPathPtr, destPathPtr);
- }
- }
- if (retVal == -1) {
- Tcl_SetErrno(EXDEV);
- }
- return retVal;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TclCrossFilesystemCopy --
- *
- * Helper for above function, and for Tcl_FSLoadFile, to copy
- * files from one filesystem to another. This function will
- * overwrite the target file if it already exists.
- *
- * Results:
- * Standard Tcl error code.
- *
- * Side effects:
- * A file may be created.
- *
- *---------------------------------------------------------------------------
- */
- int
- TclCrossFilesystemCopy(interp, source, target)
- Tcl_Interp *interp; /* For error messages */
- Tcl_Obj *source; /* Pathname of file to be copied (UTF-8). */
- Tcl_Obj *target; /* Pathname of file to copy to (UTF-8). */
- {
- int result = TCL_ERROR;
- int prot = 0666;
-
- Tcl_Channel out = Tcl_FSOpenFileChannel(interp, target, "w", prot);
- if (out != NULL) {
- /* It looks like we can copy it over */
- Tcl_Channel in = Tcl_FSOpenFileChannel(interp, source,
- "r", prot);
- if (in == NULL) {
- /* This is very strange, we checked this above */
- Tcl_Close(interp, out);
- } else {
- Tcl_StatBuf sourceStatBuf;
- struct utimbuf tval;
- /*
- * Copy it synchronously. We might wish to add an
- * asynchronous option to support vfs's which are
- * slow (e.g. network sockets).
- */
- Tcl_SetChannelOption(interp, in, "-translation", "binary");
- Tcl_SetChannelOption(interp, out, "-translation", "binary");
-
- if (TclCopyChannel(interp, in, out, -1, NULL) == TCL_OK) {
- result = TCL_OK;
- }
- /*
- * If the copy failed, assume that copy channel left
- * a good error message.
- */
- Tcl_Close(interp, in);
- Tcl_Close(interp, out);
-
- /* Set modification date of copied file */
- if (Tcl_FSLstat(source, &sourceStatBuf) == 0) {
- tval.actime = sourceStatBuf.st_atime;
- tval.modtime = sourceStatBuf.st_mtime;
- Tcl_FSUtime(target, &tval);
- }
- }
- }
- return result;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSDeleteFile --
- *
- * The appropriate function for the filesystem to which pathPtr
- * belongs will be called.
- *
- * Results:
- * Standard Tcl error code.
- *
- * Side effects:
- * A file may be deleted.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_FSDeleteFile(pathPtr)
- Tcl_Obj *pathPtr; /* Pathname of file to be removed (UTF-8). */
- {
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSDeleteFileProc *proc = fsPtr->deleteFileProc;
- if (proc != NULL) {
- return (*proc)(pathPtr);
- }
- }
- Tcl_SetErrno(ENOENT);
- return -1;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSCreateDirectory --
- *
- * The appropriate function for the filesystem to which pathPtr
- * belongs will be called.
- *
- * Results:
- * Standard Tcl error code.
- *
- * Side effects:
- * A directory may be created.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_FSCreateDirectory(pathPtr)
- Tcl_Obj *pathPtr; /* Pathname of directory to create (UTF-8). */
- {
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSCreateDirectoryProc *proc = fsPtr->createDirectoryProc;
- if (proc != NULL) {
- return (*proc)(pathPtr);
- }
- }
- Tcl_SetErrno(ENOENT);
- return -1;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSCopyDirectory --
- *
- * If the two paths given belong to the same filesystem, we call
- * that filesystems copy-directory function. Otherwise we simply
- * return the posix error 'EXDEV', and -1.
- *
- * Results:
- * Standard Tcl error code if a function was called.
- *
- * Side effects:
- * A directory may be copied.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_FSCopyDirectory(srcPathPtr, destPathPtr, errorPtr)
- 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 object containing name of file
- * causing error, with refCount 1. */
- {
- int retVal = -1;
- Tcl_Filesystem *fsPtr, *fsPtr2;
- fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
- fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
- if (fsPtr == fsPtr2 && fsPtr != NULL) {
- Tcl_FSCopyDirectoryProc *proc = fsPtr->copyDirectoryProc;
- if (proc != NULL) {
- retVal = (*proc)(srcPathPtr, destPathPtr, errorPtr);
- }
- }
- if (retVal == -1) {
- Tcl_SetErrno(EXDEV);
- }
- return retVal;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSRemoveDirectory --
- *
- * The appropriate function for the filesystem to which pathPtr
- * belongs will be called.
- *
- * Results:
- * Standard Tcl error code.
- *
- * Side effects:
- * A directory may be deleted.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_FSRemoveDirectory(pathPtr, recursive, errorPtr)
- Tcl_Obj *pathPtr; /* Pathname of directory to be removed
- * (UTF-8). */
- int recursive; /* If non-zero, removes directories that
- * are nonempty. Otherwise, will only remove
- * empty directories. */
- Tcl_Obj **errorPtr; /* If non-NULL, then will be set to a
- * new object containing name of file
- * causing error, with refCount 1. */
- {
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
- if (fsPtr != NULL) {
- Tcl_FSRemoveDirectoryProc *proc = fsPtr->removeDirectoryProc;
- if (proc != NULL) {
- if (recursive) {
- /*
- * We check whether the cwd lies inside this directory
- * and move it if it does.
- */
- Tcl_Obj *cwdPtr = Tcl_FSGetCwd(NULL);
- 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]'
- */
- Tcl_Obj *dirPtr = TclFileDirname(NULL, pathPtr);
- Tcl_FSChdir(dirPtr);
- Tcl_DecrRefCount(dirPtr);
- }
- }
- Tcl_DecrRefCount(cwdPtr);
- }
- }
- return (*proc)(pathPtr, recursive, errorPtr);
- }
- }
- Tcl_SetErrno(ENOENT);
- return -1;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSGetFileSystemForPath --
- *
- * This function determines which filesystem to use for a
- * particular path object, and returns the filesystem which
- * accepts this file. If no filesystem will accept this object
- * as a valid file path, then NULL is returned.
- *
- * Results:
- .* NULL or a filesystem which will accept this path.
- *
- * Side effects:
- * The object may be converted to a path type.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Filesystem*
- Tcl_FSGetFileSystemForPath(pathObjPtr)
- Tcl_Obj* pathObjPtr;
- {
- FilesystemRecord *fsRecPtr;
- Tcl_Filesystem* retVal = NULL;
-
- /*
- * If the object has a refCount of zero, we reject it. This
- * is to avoid possible segfaults or nondeterministic memory
- * leaks (i.e. the user doesn't know if they should decrement
- * the ref count on return or not).
- */
-
- if (pathObjPtr->refCount == 0) {
- panic("Tcl_FSGetFileSystemForPath called with object with refCount == 0");
- return NULL;
- }
-
- /*
- * Check if the filesystem has changed in some way since
- * this object's internal representation was calculated.
- * Before doing that, assure we have the most up-to-date
- * copy of the master filesystem. This is accomplished
- * by the FsGetFirstFilesystem() call.
- */
- fsRecPtr = FsGetFirstFilesystem();
- if (TclFSEnsureEpochOk(pathObjPtr, &retVal) != TCL_OK) {
- return NULL;
- }
- /*
- * Call each of the "pathInFilesystem" functions in succession. A
- * non-return value of -1 indicates the particular function has
- * succeeded.
- */
- while ((retVal == NULL) && (fsRecPtr != NULL)) {
- Tcl_FSPathInFilesystemProc *proc = fsRecPtr->fsPtr->pathInFilesystemProc;
- if (proc != NULL) {
- ClientData clientData = NULL;
- int ret = (*proc)(pathObjPtr, &clientData);
- if (ret != -1) {
- /*
- * We assume the type of pathObjPtr hasn't been changed
- * by the above call to the pathInFilesystemProc.
- */
- TclFSSetPathDetails(pathObjPtr, fsRecPtr, clientData);
- retVal = fsRecPtr->fsPtr;
- }
- }
- fsRecPtr = fsRecPtr->nextPtr;
- }
- return retVal;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSGetNativePath --
- *
- * This function is for use by the Win/Unix/MacOS native filesystems,
- * so that they can easily retrieve the native (char* or TCHAR*)
- * representation of a path. Other filesystems will probably
- * want to implement similar functions. They basically act as a
- * safety net around Tcl_FSGetInternalRep. Normally your file-
- * system procedures will always be called with path objects
- * already converted to the correct filesystem, but if for
- * some reason they are called directly (i.e. by procedures
- * not in this file), then one cannot necessarily guarantee that
- * the path object pointer is from the correct filesystem.
- *
- * Note: in the future it might be desireable to have separate
- * versions of this function with different signatures, for
- * example Tcl_FSGetNativeMacPath, Tcl_FSGetNativeUnixPath etc.
- * Right now, since native paths are all string based, we use just
- * one function. On MacOS we could possibly use an FSSpec or
- * FSRef as the native representation.
- *
- * Results:
- * NULL or a valid native path.
- *
- * Side effects:
- * See Tcl_FSGetInternalRep.
- *
- *---------------------------------------------------------------------------
- */
- CONST char *
- Tcl_FSGetNativePath(pathObjPtr)
- Tcl_Obj *pathObjPtr;
- {
- return (CONST char *)Tcl_FSGetInternalRep(pathObjPtr, &tclNativeFilesystem);
- }
- /*
- *---------------------------------------------------------------------------
- *
- * NativeCreateNativeRep --
- *
- * Create a native representation for the given path.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- static ClientData
- NativeCreateNativeRep(pathObjPtr)
- Tcl_Obj* pathObjPtr;
- {
- char *nativePathPtr;
- Tcl_DString ds;
- Tcl_Obj* validPathObjPtr;
- int len;
- char *str;
- /* Make sure the normalized path is set */
- validPathObjPtr = Tcl_FSGetNormalizedPath(NULL, pathObjPtr);
- if (validPathObjPtr == NULL) {
- return NULL;
- }
- str = Tcl_GetStringFromObj(validPathObjPtr, &len);
- #ifdef __WIN32__
- Tcl_WinUtfToTChar(str, len, &ds);
- if (tclWinProcs->useWide) {
- len = Tcl_DStringLength(&ds) + sizeof(WCHAR);
- } else {
- len = Tcl_DStringLength(&ds) + sizeof(char);
- }
- #else
- Tcl_UtfToExternalDString(NULL, str, len, &ds);
- len = Tcl_DStringLength(&ds) + sizeof(char);
- #endif
- nativePathPtr = ckalloc((unsigned) len);
- memcpy((VOID*)nativePathPtr, (VOID*)Tcl_DStringValue(&ds), (size_t) len);
- Tcl_DStringFree(&ds);
- return (ClientData)nativePathPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TclpNativeToNormalized --
- *
- * Convert native format to a normalized path object, with refCount
- * of zero.
- *
- * Results:
- * A valid normalized path.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj*
- TclpNativeToNormalized(clientData)
- ClientData clientData;
- {
- Tcl_DString ds;
- Tcl_Obj *objPtr;
- CONST char *copy;
- int len;
-
- #ifdef __WIN32__
- Tcl_WinTCharToUtf((CONST char*)clientData, -1, &ds);
- #else
- Tcl_ExternalToUtfDString(NULL, (CONST char*)clientData, -1, &ds);
- #endif
-
- copy = Tcl_DStringValue(&ds);
- len = Tcl_DStringLength(&ds);
- #ifdef __WIN32__
- /*
- * Certain native path representations on Windows have this special
- * prefix to indicate that they are to be treated specially. For
- * example extremely long paths, or symlinks
- */
- if (*copy == '\') {
- if (0 == strncmp(copy,"\??\",4)) {
- copy += 4;
- len -= 4;
- } else if (0 == strncmp(copy,"\\?\",4)) {
- copy += 4;
- len -= 4;
- }
- }
- #endif
- objPtr = Tcl_NewStringObj(copy,len);
- Tcl_DStringFree(&ds);
-
- return objPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TclNativeDupInternalRep --
- *
- * Duplicate the native representation.
- *
- * Results:
- * The copied native representation, or NULL if it is not possible
- * to copy the representation.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- ClientData
- TclNativeDupInternalRep(clientData)
- ClientData clientData;
- {
- ClientData copy;
- size_t len;
- if (clientData == NULL) {
- return NULL;
- }
- #ifdef __WIN32__
- if (tclWinProcs->useWide) {
- /* unicode representation when running on NT/2K/XP */
- len = sizeof(WCHAR) + (wcslen((CONST WCHAR*)clientData) * sizeof(WCHAR));
- } else {
- /* ansi representation when running on 95/98/ME */
- len = sizeof(char) + (strlen((CONST char*)clientData) * sizeof(char));
- }
- #else
- /* ansi representation when running on Unix/MacOS */
- len = sizeof(char) + (strlen((CONST char*)clientData) * sizeof(char));
- #endif
-
- copy = (ClientData) ckalloc(len);
- memcpy((VOID*)copy, (VOID*)clientData, len);
- return copy;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * NativeFreeInternalRep --
- *
- * Free a native internal representation, which will be non-NULL.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Memory is released.
- *
- *---------------------------------------------------------------------------
- */
- static void
- NativeFreeInternalRep(clientData)
- ClientData clientData;
- {
- ckfree((char*)clientData);
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSFileSystemInfo --
- *
- * This function returns a list of two elements. The first
- * element is the name of the filesystem (e.g. "native" or "vfs"),
- * and the second is the particular type of the given path within
- * that filesystem.
- *
- * Results:
- * A list of two elements.
- *
- * Side effects:
- * The object may be converted to a path type.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj*
- Tcl_FSFileSystemInfo(pathObjPtr)
- Tcl_Obj* pathObjPtr;
- {
- Tcl_Obj *resPtr;
- Tcl_FSFilesystemPathTypeProc *proc;
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathObjPtr);
-
- if (fsPtr == NULL) {
- return NULL;
- }
-
- resPtr = Tcl_NewListObj(0,NULL);
-
- Tcl_ListObjAppendElement(NULL, resPtr,
- Tcl_NewStringObj(fsPtr->typeName,-1));
- proc = fsPtr->filesystemPathTypeProc;
- if (proc != NULL) {
- Tcl_Obj *typePtr = (*proc)(pathObjPtr);
- if (typePtr != NULL) {
- Tcl_ListObjAppendElement(NULL, resPtr, typePtr);
- }
- }
-
- return resPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSPathSeparator --
- *
- * This function returns the separator to be used for a given
- * path. The object returned should have a refCount of zero
- *
- * Results:
- * A Tcl object, with a refCount of zero. If the caller
- * needs to retain a reference to the object, it should
- * call Tcl_IncrRefCount.
- *
- * Side effects:
- * The path object may be converted to a path type.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj*
- Tcl_FSPathSeparator(pathObjPtr)
- Tcl_Obj* pathObjPtr;
- {
- Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathObjPtr);
-
- if (fsPtr == NULL) {
- return NULL;
- }
- if (fsPtr->filesystemSeparatorProc != NULL) {
- return (*fsPtr->filesystemSeparatorProc)(pathObjPtr);
- }
-
- return NULL;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * NativeFilesystemSeparator --
- *
- * This function is part of the native filesystem support, and
- * returns the separator for the given path.
- *
- * Results:
- * String object containing the separator character.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- static Tcl_Obj*
- NativeFilesystemSeparator(pathObjPtr)
- Tcl_Obj* pathObjPtr;
- {
- char *separator = NULL; /* lint */
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- separator = "/";
- break;
- case TCL_PLATFORM_WINDOWS:
- separator = "\";
- break;
- case TCL_PLATFORM_MAC:
- separator = ":";
- break;
- }
- return Tcl_NewStringObj(separator,1);
- }
- /* Everything from here on is contained in this obsolete ifdef */
- #ifdef USE_OBSOLETE_FS_HOOKS
- /*
- *----------------------------------------------------------------------
- *
- * TclStatInsertProc --
- *
- * Insert the passed procedure pointer at the head of the list of
- * functions which are used during a call to 'TclStat(...)'. The
- * passed function should behave exactly like 'TclStat' when called
- * during that time (see 'TclStat(...)' for more information).
- * The function will be added even if it already in the list.
- *
- * Results:
- * Normally TCL_OK; TCL_ERROR if memory for a new node in the list
- * could not be allocated.
- *
- * Side effects:
- * Memory allocated and modifies the link list for 'TclStat'
- * functions.
- *
- *----------------------------------------------------------------------
- */
- int
- TclStatInsertProc (proc)
- TclStatProc_ *proc;
- {
- int retVal = TCL_ERROR;
- if (proc != NULL) {
- StatProc *newStatProcPtr;
- newStatProcPtr = (StatProc *)ckalloc(sizeof(StatProc));
- if (newStatProcPtr != NULL) {
- newStatProcPtr->proc = proc;
- Tcl_MutexLock(&obsoleteFsHookMutex);
- newStatProcPtr->nextPtr = statProcList;
- statProcList = newStatProcPtr;
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
- retVal = TCL_OK;
- }
- }
- return retVal;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclStatDeleteProc --
- *
- * Removed the passed function pointer from the list of 'TclStat'
- * functions. Ensures that the built-in stat function is not
- * removvable.
- *
- * Results:
- * TCL_OK if the procedure pointer was successfully removed,
- * TCL_ERROR otherwise.
- *
- * Side effects:
- * Memory is deallocated and the respective list updated.
- *
- *----------------------------------------------------------------------
- */
- int
- TclStatDeleteProc (proc)
- TclStatProc_ *proc;
- {
- int retVal = TCL_ERROR;
- StatProc *tmpStatProcPtr;
- StatProc *prevStatProcPtr = NULL;
- Tcl_MutexLock(&obsoleteFsHookMutex);
- tmpStatProcPtr = statProcList;
- /*
- * Traverse the 'statProcList' looking for the particular node
- * whose 'proc' member matches 'proc' and remove that one from
- * the list. Ensure that the "default" node cannot be removed.
- */
- while ((retVal == TCL_ERROR) && (tmpStatProcPtr != NULL)) {
- if (tmpStatProcPtr->proc == proc) {
- if (prevStatProcPtr == NULL) {
- statProcList = tmpStatProcPtr->nextPtr;
- } else {
- prevStatProcPtr->nextPtr = tmpStatProcPtr->nextPtr;
- }
- ckfree((char *)tmpStatProcPtr);
- retVal = TCL_OK;
- } else {
- prevStatProcPtr = tmpStatProcPtr;
- tmpStatProcPtr = tmpStatProcPtr->nextPtr;
- }
- }
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
- return retVal;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclAccessInsertProc --
- *
- * Insert the passed procedure pointer at the head of the list of
- * functions which are used during a call to 'TclAccess(...)'.
- * The passed function should behave exactly like 'TclAccess' when
- * called during that time (see 'TclAccess(...)' for more
- * information). The function will be added even if it already in
- * the list.
- *
- * Results:
- * Normally TCL_OK; TCL_ERROR if memory for a new node in the list
- * could not be allocated.
- *
- * Side effects:
- * Memory allocated and modifies the link list for 'TclAccess'
- * functions.
- *
- *----------------------------------------------------------------------
- */
- int
- TclAccessInsertProc(proc)
- TclAccessProc_ *proc;
- {
- int retVal = TCL_ERROR;
- if (proc != NULL) {
- AccessProc *newAccessProcPtr;
- newAccessProcPtr = (AccessProc *)ckalloc(sizeof(AccessProc));
- if (newAccessProcPtr != NULL) {
- newAccessProcPtr->proc = proc;
- Tcl_MutexLock(&obsoleteFsHookMutex);
- newAccessProcPtr->nextPtr = accessProcList;
- accessProcList = newAccessProcPtr;
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
- retVal = TCL_OK;
- }
- }
- return retVal;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclAccessDeleteProc --
- *
- * Removed the passed function pointer from the list of 'TclAccess'
- * functions. Ensures that the built-in access function is not
- * removvable.
- *
- * Results:
- * TCL_OK if the procedure pointer was successfully removed,
- * TCL_ERROR otherwise.
- *
- * Side effects:
- * Memory is deallocated and the respective list updated.
- *
- *----------------------------------------------------------------------
- */
- int
- TclAccessDeleteProc(proc)
- TclAccessProc_ *proc;
- {
- int retVal = TCL_ERROR;
- AccessProc *tmpAccessProcPtr;
- AccessProc *prevAccessProcPtr = NULL;
- /*
- * Traverse the 'accessProcList' looking for the particular node
- * whose 'proc' member matches 'proc' and remove that one from
- * the list. Ensure that the "default" node cannot be removed.
- */
- Tcl_MutexLock(&obsoleteFsHookMutex);
- tmpAccessProcPtr = accessProcList;
- while ((retVal == TCL_ERROR) && (tmpAccessProcPtr != NULL)) {
- if (tmpAccessProcPtr->proc == proc) {
- if (prevAccessProcPtr == NULL) {
- accessProcList = tmpAccessProcPtr->nextPtr;
- } else {
- prevAccessProcPtr->nextPtr = tmpAccessProcPtr->nextPtr;
- }
- ckfree((char *)tmpAccessProcPtr);
- retVal = TCL_OK;
- } else {
- prevAccessProcPtr = tmpAccessProcPtr;
- tmpAccessProcPtr = tmpAccessProcPtr->nextPtr;
- }
- }
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
- return retVal;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclOpenFileChannelInsertProc --
- *
- * Insert the passed procedure pointer at the head of the list of
- * functions which are used during a call to
- * 'Tcl_OpenFileChannel(...)'. The passed function should behave
- * exactly like 'Tcl_OpenFileChannel' when called during that time
- * (see 'Tcl_OpenFileChannel(...)' for more information). The
- * function will be added even if it already in the list.
- *
- * Results:
- * Normally TCL_OK; TCL_ERROR if memory for a new node in the list
- * could not be allocated.
- *
- * Side effects:
- * Memory allocated and modifies the link list for
- * 'Tcl_OpenFileChannel' functions.
- *
- *----------------------------------------------------------------------
- */
- int
- TclOpenFileChannelInsertProc(proc)
- TclOpenFileChannelProc_ *proc;
- {
- int retVal = TCL_ERROR;
- if (proc != NULL) {
- OpenFileChannelProc *newOpenFileChannelProcPtr;
- newOpenFileChannelProcPtr =
- (OpenFileChannelProc *)ckalloc(sizeof(OpenFileChannelProc));
- if (newOpenFileChannelProcPtr != NULL) {
- newOpenFileChannelProcPtr->proc = proc;
- Tcl_MutexLock(&obsoleteFsHookMutex);
- newOpenFileChannelProcPtr->nextPtr = openFileChannelProcList;
- openFileChannelProcList = newOpenFileChannelProcPtr;
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
- retVal = TCL_OK;
- }
- }
- return retVal;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclOpenFileChannelDeleteProc --
- *
- * Removed the passed function pointer from the list of
- * 'Tcl_OpenFileChannel' functions. Ensures that the built-in
- * open file channel function is not removable.
- *
- * Results:
- * TCL_OK if the procedure pointer was successfully removed,
- * TCL_ERROR otherwise.
- *
- * Side effects:
- * Memory is deallocated and the respective list updated.
- *
- *----------------------------------------------------------------------
- */
- int
- TclOpenFileChannelDeleteProc(proc)
- TclOpenFileChannelProc_ *proc;
- {
- int retVal = TCL_ERROR;
- OpenFileChannelProc *tmpOpenFileChannelProcPtr = openFileChannelProcList;
- OpenFileChannelProc *prevOpenFileChannelProcPtr = NULL;
- /*
- * Traverse the 'openFileChannelProcList' looking for the particular
- * node whose 'proc' member matches 'proc' and remove that one from
- * the list.
- */
- Tcl_MutexLock(&obsoleteFsHookMutex);
- tmpOpenFileChannelProcPtr = openFileChannelProcList;
- while ((retVal == TCL_ERROR) &&
- (tmpOpenFileChannelProcPtr != NULL)) {
- if (tmpOpenFileChannelProcPtr->proc == proc) {
- if (prevOpenFileChannelProcPtr == NULL) {
- openFileChannelProcList = tmpOpenFileChannelProcPtr->nextPtr;
- } else {
- prevOpenFileChannelProcPtr->nextPtr =
- tmpOpenFileChannelProcPtr->nextPtr;
- }
- ckfree((char *)tmpOpenFileChannelProcPtr);
- retVal = TCL_OK;
- } else {
- prevOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr;
- tmpOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr->nextPtr;
- }
- }
- Tcl_MutexUnlock(&obsoleteFsHookMutex);
- return retVal;
- }
- #endif /* USE_OBSOLETE_FS_HOOKS */
- /*
- * Prototypes for procedures defined later in this file.
- */
- static void DupFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
- Tcl_Obj *copyPtr));
- static void FreeFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *listPtr));
- static void UpdateStringOfFsPath _ANSI_ARGS_((Tcl_Obj *objPtr));
- static int SetFsPathFromAny _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *objPtr));
- static int FindSplitPos _ANSI_ARGS_((char *path, char *separator));
- /*
- * Define the 'path' object type, which Tcl uses to represent
- * file paths internally.
- */
- static Tcl_ObjType tclFsPathType = {
- "path", /* name */
- FreeFsPathInternalRep, /* freeIntRepProc */
- DupFsPathInternalRep, /* dupIntRepProc */
- UpdateStringOfFsPath, /* updateStringProc */
- SetFsPathFromAny /* setFromAnyProc */
- };
- /*
- * struct FsPath --
- *
- * Internal representation of a Tcl_Obj of "path" type. This
- * can be used to represent relative or absolute paths, and has
- * certain optimisations when used to represent paths which are
- * already normalized and absolute.
- *
- * Note that 'normPathPtr' can be a circular reference to the
- * container Tcl_Obj of this FsPath.
- */
- typedef struct FsPath {
- Tcl_Obj *translatedPathPtr; /* Name without any ~user sequences.
- * If this is NULL, then this is a
- * pure normalized, absolute path
- * object, in which the parent Tcl_Obj's
- * string rep is already both translated
- * and normalized. */
- Tcl_Obj *normPathPtr; /* Normalized absolute path, without
- * ., .. or ~user sequences. If the
- * Tcl_Obj containing
- * this FsPath is already normalized,
- * this may be a circular reference back
- * to the container. If that is NOT the
- * case, we have a refCount on the object. */
- Tcl_Obj *cwdPtr; /* If null, path is absolute, else
- * this points to the cwd object used
- * for this path. We have a refCount
- * on the object. */
- int flags; /* Flags to describe interpretation */
- ClientData nativePathPtr; /* Native representation of this path,
- * which is filesystem dependent. */
- int filesystemEpoch; /* Used to ensure the path representation
- * was generated during the correct
- * filesystem epoch. The epoch changes
- * when filesystem-mounts are changed. */
- struct FilesystemRecord *fsRecPtr;
- /* Pointer to the filesystem record
- * entry to use for this path. */
- } FsPath;
- /*
- * Define some macros to give us convenient access to path-object
- * specific fields.
- */
- #define PATHOBJ(objPtr) (objPtr->internalRep.otherValuePtr)
- #define PATHFLAGS(objPtr)
- (((FsPath*)(objPtr->internalRep.otherValuePtr))->flags)
- #define TCLPATH_APPENDED 1
- #define TCLPATH_RELATIVE 2
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_FSGetPathType --
- *
- * Determines whether a given path is relative to the current
- * directory, relative to the current volume, or absolute.
- *
- * Results:
- * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
- * TCL_PATH_VOLUME_RELATIVE.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- Tcl_PathType
- Tcl_FSGetPathType(pathObjPtr)
- Tcl_Obj *pathObjPtr;
- {
- return FSGetPathType(pathObjPtr, NULL, NULL);
- }
- /*
- *----------------------------------------------------------------------
- *
- * FSGetPathType --
- *
- * Determines whether a given path is relative to the current
- * directory, relative to the current volume, or absolute. If the
- * caller wishes to know which filesystem claimed the path (in the
- * case for which the path is absolute), then a reference to a
- * filesystem pointer can be passed in (but passing NULL is
- * acceptable).
- *
- * Results:
- * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
- * TCL_PATH_VOLUME_RELATIVE. The filesystem reference will
- * be set if and only if it is non-NULL and the function's
- * return value is TCL_PATH_ABSOLUTE.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static Tcl_PathType
- FSGetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr)
- Tcl_Obj *pathObjPtr;
- Tcl_Filesystem **filesystemPtrPtr;
- int *driveNameLengthPtr;
- {
- if (Tcl_FSConvertToPathType(NULL, pathObjPtr) != TCL_OK) {
- return GetPathType(pathObjPtr, filesystemPtrPtr,
- driveNameLengthPtr, NULL);
- } else {
- FsPath *fsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
- if (fsPathPtr->cwdPtr != NULL) {
- if (PATHFLAGS(pathObjPtr) == 0) {
- return TCL_PATH_RELATIVE;
- }
- return FSGetPathType(fsPathPtr->cwdPtr, filesystemPtrPtr,
- driveNameLengthPtr);
- } else {
- return GetPathType(pathObjPtr, filesystemPtrPtr,
- driveNameLengthPtr, NULL);
- }
- }
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSJoinPath --
- *
- * This function takes the given Tcl_Obj, which should be a valid
- * list, and returns the path object given by considering the
- * first 'elements' elements as valid path segments. If elements < 0,
- * we use the entire list.
- *
- * Results:
- * Returns object with refCount of zero, (or if non-zero, it has
- * references elsewhere in Tcl). Either way, the caller must
- * increment its refCount before use.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj*
- Tcl_FSJoinPath(listObj, elements)
- Tcl_Obj *listObj;
- int elements;
- {
- Tcl_Obj *res;
- int i;
- Tcl_Filesystem *fsPtr = NULL;
-
- if (elements < 0) {
- if (Tcl_ListObjLength(NULL, listObj, &elements) != TCL_OK) {
- return NULL;
- }
- } else {
- /* Just make sure it is a valid list */
- int listTest;
- if (Tcl_ListObjLength(NULL, listObj, &listTest) != TCL_OK) {
- return NULL;
- }
- /*
- * Correct this if it is too large, otherwise we will
- * waste our time joining null elements to the path
- */
- if (elements > listTest) {
- elements = listTest;
- }
- }
-
- res = Tcl_NewObj();
-
- for (i = 0; i < elements; i++) {
- Tcl_Obj *elt;
- int driveNameLength;
- Tcl_PathType type;
- char *strElt;
- int strEltLen;
- int length;
- char *ptr;
- Tcl_Obj *driveName = NULL;
-
- Tcl_ListObjIndex(NULL, listObj, i, &elt);
-
- /*
- * This is a special case where we can be much more
- * efficient, where we are joining a single relative path
- * onto an object that is already of path type. The
- * 'TclNewFSPathObj' call below creates an object which
- * can be normalized more efficiently. Currently we only
- * use the special case when we have exactly two elements,
- * but we could expand that in the future.
- */
- if ((i == (elements-2)) && (i == 0) && (elt->typePtr == &tclFsPathType)
- && !(elt->bytes != NULL && (elt->bytes[0] == ' '))) {
- Tcl_Obj *tail;
- Tcl_PathType type;
- Tcl_ListObjIndex(NULL, listObj, i+1, &tail);
- type = GetPathType(tail, NULL, NULL, NULL);
- if (type == TCL_PATH_RELATIVE) {
- CONST char *str;
- int len;
- str = Tcl_GetStringFromObj(tail,&len);
- if (len == 0) {
- /*
- * This happens if we try to handle the root volume
- * '/'. There's no need to return a special path
- * object, when the base itself is just fine!
- */
- Tcl_DecrRefCount(res);
- return elt;
- }
- /*
- * If it doesn't begin with '.' and is a mac or unix
- * path or it a windows path without backslashes, then we
- * can be very efficient here. (In fact even a windows
- * path with backslashes can be joined efficiently, but
- * the path object would not have forward slashes only,
- * and this would therefore contradict our 'file join'
- * documentation).
- */
- if (str[0] != '.' && ((tclPlatform != TCL_PLATFORM_WINDOWS)
- || (strchr(str, '\') == NULL))) {
- /*
- * Finally, on Windows, 'file join' is defined to
- * convert all backslashes to forward slashes,
- * so the base part cannot have backslashes either.
- */
- if ((tclPlatform != TCL_PLATFORM_WINDOWS)
- || (strchr(Tcl_GetString(elt), '\') == NULL)) {
- if (res != NULL) {
- TclDecrRefCount(res);
- }
- return TclNewFSPathObj(elt, str, len);
- }
- }
- /*
- * Otherwise we don't have an easy join, and
- * we must let the more general code below handle
- * things
- */
- } else {
- if (tclPlatform == TCL_PLATFORM_UNIX) {
- Tcl_DecrRefCount(res);
- return tail;
- } else {
- CONST char *str;
- int len;
- str = Tcl_GetStringFromObj(tail,&len);
- if (tclPlatform == TCL_PLATFORM_WINDOWS) {
- if (strchr(str, '\') == NULL) {
- Tcl_DecrRefCount(res);
- return tail;
- }
- } else if (tclPlatform == TCL_PLATFORM_MAC) {
- if (strchr(str, '/') == NULL) {
- Tcl_DecrRefCount(res);
- return tail;
- }
- }
- }
- }
- }
- strElt = Tcl_GetStringFromObj(elt, &strEltLen);
- type = GetPathType(elt, &fsPtr, &driveNameLength, &driveName);
- if (type != TCL_PATH_RELATIVE) {
- /* Zero out the current result */
- Tcl_DecrRefCount(res);
- if (driveName != NULL) {
- res = Tcl_DuplicateObj(driveName);
- Tcl_DecrRefCount(driveName);
- } else {
- res = Tcl_NewStringObj(strElt, driveNameLength);
- }
- strElt += driveNameLength;
- }
-
- ptr = Tcl_GetStringFromObj(res, &length);
-
- /*
- * Strip off any './' before a tilde, unless this is the
- * beginning of the path.
- */
- if (length > 0 && strEltLen > 0) {
- if ((strElt[0] == '.') && (strElt[1] == '/')
- && (strElt[2] == '~')) {
- strElt += 2;
- }
- }
- /*
- * A NULL value for fsPtr at this stage basically means
- * we're trying to join a relative path onto something
- * which is also relative (or empty). There's nothing
- * particularly wrong with that.
- */
- if (*strElt == ' ') continue;
-
- if (fsPtr == &tclNativeFilesystem || fsPtr == NULL) {
- TclpNativeJoinPath(res, strElt);
- } else {
- char separator = '/';
- int needsSep = 0;
-
- if (fsPtr->filesystemSeparatorProc != NULL) {
- Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(res);
- if (sep != NULL) {
- separator = Tcl_GetString(sep)[0];
- }
- }
- if (length > 0 && ptr[length -1] != '/') {
- Tcl_AppendToObj(res, &separator, 1);
- length++;
- }
- Tcl_SetObjLength(res, length + (int) strlen(strElt));
-
- ptr = Tcl_GetString(res) + length;
- for (; *strElt != ' '; strElt++) {
- if (*strElt == separator) {
- while (strElt[1] == separator) {
- strElt++;
- }
- if (strElt[1] != ' ') {
- if (needsSep) {
- *ptr++ = separator;
- }
- }
- } else {
- *ptr++ = *strElt;
- needsSep = 1;
- }
- }
- length = ptr - Tcl_GetString(res);
- Tcl_SetObjLength(res, length);
- }
- }
- return res;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSConvertToPathType --
- *
- * This function tries to convert the given Tcl_Obj to a valid
- * Tcl path type, taking account of the fact that the cwd may
- * have changed even if this object is already supposedly of
- * the correct type.
- *
- * The filename may begin with "~" (to indicate current user's
- * home directory) or "~<user>" (to indicate any user's home
- * directory).
- *
- * Results:
- * Standard Tcl error code.
- *
- * Side effects:
- * The old representation may be freed, and new memory allocated.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_FSConvertToPathType(interp, objPtr)
- Tcl_Interp *interp; /* Interpreter in which to store error
- * message (if necessary). */
- Tcl_Obj *objPtr; /* Object to convert to a valid, current
- * path type. */
- {
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- /*
- * While it is bad practice to examine an object's type directly,
- * this is actually the best thing to do here. The reason is that
- * if we are converting this object to FsPath type for the first
- * time, we don't need to worry whether the 'cwd' has changed.
- * On the other hand, if this object is already of FsPath type,
- * and is a relative path, we do have to worry about the cwd.
- * If the cwd has changed, we must recompute the path.
- */
- if (objPtr->typePtr == &tclFsPathType) {
- FsPath *fsPathPtr = (FsPath*) PATHOBJ(objPtr);
- if (fsPathPtr->filesystemEpoch != tsdPtr->filesystemEpoch) {
- if (objPtr->bytes == NULL) {
- UpdateStringOfFsPath(objPtr);
- }
- FreeFsPathInternalRep(objPtr);
- objPtr->typePtr = NULL;
- return Tcl_ConvertToType(interp, objPtr, &tclFsPathType);
- }
- return TCL_OK;
- } else {
- return Tcl_ConvertToType(interp, objPtr, &tclFsPathType);
- }
- }
- /*
- * Helper function for SetFsPathFromAny. Returns position of first
- * directory delimiter in the path.
- */
- static int
- FindSplitPos(path, separator)
- char *path;
- char *separator;
- {
- int count = 0;
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- case TCL_PLATFORM_MAC:
- while (path[count] != 0) {
- if (path[count] == *separator) {
- return count;
- }
- count++;
- }
- break;
- case TCL_PLATFORM_WINDOWS:
- while (path[count] != 0) {
- if (path[count] == *separator || path[count] == '\') {
- return count;
- }
- count++;
- }
- break;
- }
- return count;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TclNewFSPathObj --
- *
- * Creates a path object whose string representation is
- * '[file join dirPtr addStrRep]', but does so in a way that
- * allows for more efficient caching of normalized paths.
- *
- * Assumptions:
- * 'dirPtr' must be an absolute path.
- * 'len' may not be zero.
- *
- * Results:
- * The new Tcl object, with refCount zero.
- *
- * Side effects:
- * Memory is allocated. 'dirPtr' gets an additional refCount.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj*
- TclNewFSPathObj(Tcl_Obj *dirPtr, CONST char *addStrRep, int len)
- {
- FsPath *fsPathPtr;
- Tcl_Obj *objPtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- objPtr = Tcl_NewObj();
- fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
-
- if (tclPlatform == TCL_PLATFORM_MAC) {
- /*
- * Mac relative paths may begin with a directory separator ':'.
- * If present, we need to skip this ':' because we assume that
- * we can join dirPtr and addStrRep by concatenating them as
- * strings (and we ensure that dirPtr is terminated by a ':').
- */
- if (addStrRep[0] == ':') {
- addStrRep++;
- len--;
- }
- }
- /* Setup the path */
- fsPathPtr->translatedPathPtr = NULL;
- fsPathPtr->normPathPtr = Tcl_NewStringObj(addStrRep, len);
- Tcl_IncrRefCount(fsPathPtr->normPathPtr);
- fsPathPtr->cwdPtr = dirPtr;
- Tcl_IncrRefCount(dirPtr);
- fsPathPtr->nativePathPtr = NULL;
- fsPathPtr->fsRecPtr = NULL;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
- PATHOBJ(objPtr) = (VOID *) fsPathPtr;
- PATHFLAGS(objPtr) = TCLPATH_RELATIVE | TCLPATH_APPENDED;
- objPtr->typePtr = &tclFsPathType;
- objPtr->bytes = NULL;
- objPtr->length = 0;
- return objPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TclFSMakePathRelative --
- *
- * Only for internal use.
- *
- * Takes a path and a directory, where we _assume_ both path and
- * directory are absolute, normalized and that the path lies
- * inside the directory. Returns a Tcl_Obj representing filename
- * of the path relative to the directory.
- *
- * In the case where the resulting path would start with a '~', we
- * take special care to return an ordinary string. This means to
- * use that path (and not have it interpreted as a user name),
- * one must prepend './'. This may seem strange, but that is how
- * 'glob' is currently defined.
- *
- * Results:
- * NULL on error, otherwise a valid object, typically with
- * refCount of zero, which it is assumed the caller will
- * increment.
- *
- * Side effects:
- * The old representation may be freed, and new memory allocated.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj*
- TclFSMakePathRelative(interp, objPtr, cwdPtr)
- Tcl_Interp *interp; /* Used for error reporting if not NULL. */
- Tcl_Obj *objPtr; /* The object we have. */
- Tcl_Obj *cwdPtr; /* Make it relative to this. */
- {
- int cwdLen, len;
- CONST char *tempStr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (objPtr->typePtr == &tclFsPathType) {
- FsPath *fsPathPtr = (FsPath*) PATHOBJ(objPtr);
- if (PATHFLAGS(objPtr) != 0
- && fsPathPtr->cwdPtr == cwdPtr) {
- objPtr = fsPathPtr->normPathPtr;
- /* Free old representation */
- if (objPtr->typePtr != NULL) {
- if (objPtr->bytes == NULL) {
- if (objPtr->typePtr->updateStringProc == NULL) {
- if (interp != NULL) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "can't find object",
- "string representation", (char *) NULL);
- }
- return NULL;
- }
- objPtr->typePtr->updateStringProc(objPtr);
- }
- if ((objPtr->typePtr->freeIntRepProc) != NULL) {
- (*objPtr->typePtr->freeIntRepProc)(objPtr);
- }
- }
- /* Now objPtr is a string object */
-
- if (Tcl_GetString(objPtr)[0] == '~') {
- /*
- * If the first character of the path is a tilde,
- * we must just return the path as is, to agree
- * with the defined behaviour of 'glob'.
- */
- return objPtr;
- }
- fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
- /* Circular reference, by design */
- fsPathPtr->translatedPathPtr = objPtr;
- fsPathPtr->normPathPtr = NULL;
- fsPathPtr->cwdPtr = cwdPtr;
- Tcl_IncrRefCount(cwdPtr);
- fsPathPtr->nativePathPtr = NULL;
- fsPathPtr->fsRecPtr = NULL;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
- PATHOBJ(objPtr) = (VOID *) fsPathPtr;
- PATHFLAGS(objPtr) = 0;
- objPtr->typePtr = &tclFsPathType;
- return objPtr;
- }
- }
- /*
- * We know the cwd is a normalised object which does
- * not end in a directory delimiter, unless the cwd
- * is the name of a volume, in which case it will
- * end in a delimiter! We handle this situation here.
- * A better test than the '!= sep' might be to simply
- * check if 'cwd' is a root volume.
- *
- * Note that if we get this wrong, we will strip off
- * either too much or too little below, leading to
- * wrong answers returned by glob.
- */
- tempStr = Tcl_GetStringFromObj(cwdPtr, &cwdLen);
- /*
- * Should we perhaps use 'Tcl_FSPathSeparator'?
- * But then what about the Windows special case?
- * Perhaps we should just check if cwd is a root
- * volume.
- */
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- if (tempStr[cwdLen-1] != '/') {
- cwdLen++;
- }
- break;
- case TCL_PLATFORM_WINDOWS:
- if (tempStr[cwdLen-1] != '/'
- && tempStr[cwdLen-1] != '\') {
- cwdLen++;
- }
- break;
- case TCL_PLATFORM_MAC:
- if (tempStr[cwdLen-1] != ':') {
- cwdLen++;
- }
- break;
- }
- tempStr = Tcl_GetStringFromObj(objPtr, &len);
- return Tcl_NewStringObj(tempStr + cwdLen, len - cwdLen);
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TclFSMakePathFromNormalized --
- *
- * Like SetFsPathFromAny, but assumes the given object is an
- * absolute normalized path. Only for internal use.
- *
- * Results:
- * Standard Tcl error code.
- *
- * Side effects:
- * The old representation may be freed, and new memory allocated.
- *
- *---------------------------------------------------------------------------
- */
- int
- TclFSMakePathFromNormalized(interp, objPtr, nativeRep)
- Tcl_Interp *interp; /* Used for error reporting if not NULL. */
- Tcl_Obj *objPtr; /* The object to convert. */
- ClientData nativeRep; /* The native rep for the object, if known
- * else NULL. */
- {
- FsPath *fsPathPtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- if (objPtr->typePtr == &tclFsPathType) {
- return TCL_OK;
- }
-
- /* Free old representation */
- if (objPtr->typePtr != NULL) {
- if (objPtr->bytes == NULL) {
- if (objPtr->typePtr->updateStringProc == NULL) {
- if (interp != NULL) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "can't find object",
- "string representation", (char *) NULL);
- }
- return TCL_ERROR;
- }
- objPtr->typePtr->updateStringProc(objPtr);
- }
- if ((objPtr->typePtr->freeIntRepProc) != NULL) {
- (*objPtr->typePtr->freeIntRepProc)(objPtr);
- }
- }
- fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
- /* It's a pure normalized absolute path */
- fsPathPtr->translatedPathPtr = NULL;
- fsPathPtr->normPathPtr = objPtr;
- fsPathPtr->cwdPtr = NULL;
- fsPathPtr->nativePathPtr = nativeRep;
- fsPathPtr->fsRecPtr = NULL;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
- PATHOBJ(objPtr) = (VOID *) fsPathPtr;
- PATHFLAGS(objPtr) = 0;
- objPtr->typePtr = &tclFsPathType;
- return TCL_OK;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSNewNativePath --
- *
- * This function performs the something like that reverse of the
- * usual obj->path->nativerep conversions. If some code retrieves
- * a path in native form (from, e.g. readlink or a native dialog),
- * and that path is to be used at the Tcl level, then calling
- * this function is an efficient way of creating the appropriate
- * path object type.
- *
- * Any memory which is allocated for 'clientData' should be retained
- * until clientData is passed to the filesystem's freeInternalRepProc
- * when it can be freed. The built in platform-specific filesystems
- * use 'ckalloc' to allocate clientData, and ckfree to free it.
- *
- * Results:
- * NULL or a valid path object pointer, with refCount zero.
- *
- * Side effects:
- * New memory may be allocated.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj *
- Tcl_FSNewNativePath(fromFilesystem, clientData)
- Tcl_Filesystem* fromFilesystem;
- ClientData clientData;
- {
- Tcl_Obj *objPtr;
- FsPath *fsPathPtr;
- FilesystemRecord *fsFromPtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- objPtr = TclFSInternalToNormalized(fromFilesystem, clientData, &fsFromPtr);
- if (objPtr == NULL) {
- return NULL;
- }
-
- /*
- * Free old representation; shouldn't normally be any,
- * but best to be safe.
- */
- if (objPtr->typePtr != NULL) {
- if (objPtr->bytes == NULL) {
- if (objPtr->typePtr->updateStringProc == NULL) {
- return NULL;
- }
- objPtr->typePtr->updateStringProc(objPtr);
- }
- if ((objPtr->typePtr->freeIntRepProc) != NULL) {
- (*objPtr->typePtr->freeIntRepProc)(objPtr);
- }
- }
-
- fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
- fsPathPtr->translatedPathPtr = NULL;
- /* Circular reference, by design */
- fsPathPtr->normPathPtr = objPtr;
- fsPathPtr->cwdPtr = NULL;
- fsPathPtr->nativePathPtr = clientData;
- fsPathPtr->fsRecPtr = fsFromPtr;
- fsPathPtr->fsRecPtr->fileRefCount++;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
- PATHOBJ(objPtr) = (VOID *) fsPathPtr;
- PATHFLAGS(objPtr) = 0;
- objPtr->typePtr = &tclFsPathType;
- return objPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSGetTranslatedPath --
- *
- * This function attempts to extract the translated path
- * from the given Tcl_Obj. If the translation succeeds (i.e. the
- * object is a valid path), then it is returned. Otherwise NULL
- * will be returned, and an error message may be left in the
- * interpreter (if it is non-NULL)
- *
- * Results:
- * NULL or a valid Tcl_Obj pointer.
- *
- * Side effects:
- * Only those of 'Tcl_FSConvertToPathType'
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj*
- Tcl_FSGetTranslatedPath(interp, pathPtr)
- Tcl_Interp *interp;
- Tcl_Obj* pathPtr;
- {
- Tcl_Obj *retObj = NULL;
- FsPath *srcFsPathPtr;
- if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
- return NULL;
- }
- srcFsPathPtr = (FsPath*) PATHOBJ(pathPtr);
- if (srcFsPathPtr->translatedPathPtr == NULL) {
- if (PATHFLAGS(pathPtr) != 0) {
- retObj = Tcl_FSGetNormalizedPath(interp, pathPtr);
- } else {
- /*
- * It is a pure absolute, normalized path object.
- * This is something like being a 'pure list'. The
- * object's string, translatedPath and normalizedPath
- * are all identical.
- */
- retObj = srcFsPathPtr->normPathPtr;
- }
- } else {
- /* It is an ordinary path object */
- retObj = srcFsPathPtr->translatedPathPtr;
- }
- if (retObj) {
- Tcl_IncrRefCount(retObj);
- }
- return retObj;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSGetTranslatedStringPath --
- *
- * This function attempts to extract the translated path
- * from the given Tcl_Obj. If the translation succeeds (i.e. the
- * object is a valid path), then the path is returned. Otherwise NULL
- * will be returned, and an error message may be left in the
- * interpreter (if it is non-NULL)
- *
- * Results:
- * NULL or a valid string.
- *
- * Side effects:
- * Only those of 'Tcl_FSConvertToPathType'
- *
- *---------------------------------------------------------------------------
- */
- CONST char*
- Tcl_FSGetTranslatedStringPath(interp, pathPtr)
- Tcl_Interp *interp;
- Tcl_Obj* pathPtr;
- {
- Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
- if (transPtr != NULL) {
- int len;
- CONST char *result, *orig;
- orig = Tcl_GetStringFromObj(transPtr, &len);
- result = (char*) ckalloc((unsigned)(len+1));
- memcpy((VOID*) result, (VOID*) orig, (size_t) (len+1));
- Tcl_DecrRefCount(transPtr);
- return result;
- }
- return NULL;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSGetNormalizedPath --
- *
- * This important function attempts to extract from the given Tcl_Obj
- * a unique normalised path representation, whose string value can
- * be used as a unique identifier for the file.
- *
- * Results:
- * NULL or a valid path object pointer.
- *
- * Side effects:
- * New memory may be allocated. The Tcl 'errno' may be modified
- * in the process of trying to examine various path possibilities.
- *
- *---------------------------------------------------------------------------
- */
- Tcl_Obj*
- Tcl_FSGetNormalizedPath(interp, pathObjPtr)
- Tcl_Interp *interp;
- Tcl_Obj* pathObjPtr;
- {
- FsPath *fsPathPtr;
- if (Tcl_FSConvertToPathType(interp, pathObjPtr) != TCL_OK) {
- return NULL;
- }
- fsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
- if (PATHFLAGS(pathObjPtr) != 0) {
- /*
- * This is a special path object which is the result of
- * something like 'file join'
- */
- Tcl_Obj *dir, *copy;
- int cwdLen;
- int pathType;
- CONST char *cwdStr;
- ClientData clientData = NULL;
-
- pathType = Tcl_FSGetPathType(fsPathPtr->cwdPtr);
- dir = Tcl_FSGetNormalizedPath(interp, fsPathPtr->cwdPtr);
- if (dir == NULL) {
- return NULL;
- }
- if (pathObjPtr->bytes == NULL) {
- UpdateStringOfFsPath(pathObjPtr);
- }
- copy = Tcl_DuplicateObj(dir);
- Tcl_IncrRefCount(copy);
- Tcl_IncrRefCount(dir);
- /* We now own a reference on both 'dir' and 'copy' */
-
- cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
- /*
- * Should we perhaps use 'Tcl_FSPathSeparator'?
- * But then what about the Windows special case?
- * Perhaps we should just check if cwd is a root volume.
- * We should never get cwdLen == 0 in this code path.
- */
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- if (cwdStr[cwdLen-1] != '/') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
- case TCL_PLATFORM_WINDOWS:
- if (cwdStr[cwdLen-1] != '/'
- && cwdStr[cwdLen-1] != '\') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
- case TCL_PLATFORM_MAC:
- if (cwdStr[cwdLen-1] != ':') {
- Tcl_AppendToObj(copy, ":", 1);
- cwdLen++;
- }
- break;
- }
- Tcl_AppendObjToObj(copy, fsPathPtr->normPathPtr);
- /*
- * Normalize the combined string, but only starting after
- * the end of the previously normalized 'dir'. This should
- * be much faster! We use 'cwdLen-1' so that we are
- * already pointing at the dir-separator that we know about.
- * The normalization code will actually start off directly
- * after that separator.
- */
- TclFSNormalizeToUniquePath(interp, copy, cwdLen-1,
- (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL));
- /* Now we need to construct the new path object */
-
- if (pathType == TCL_PATH_RELATIVE) {
- FsPath* origDirFsPathPtr;
- Tcl_Obj *origDir = fsPathPtr->cwdPtr;
- origDirFsPathPtr = (FsPath*) PATHOBJ(origDir);
-
- fsPathPtr->cwdPtr = origDirFsPathPtr->cwdPtr;
- Tcl_IncrRefCount(fsPathPtr->cwdPtr);
-
- Tcl_DecrRefCount(fsPathPtr->normPathPtr);
- fsPathPtr->normPathPtr = copy;
- /* That's our reference to copy used */
- Tcl_DecrRefCount(dir);
- Tcl_DecrRefCount(origDir);
- } else {
- Tcl_DecrRefCount(fsPathPtr->cwdPtr);
- fsPathPtr->cwdPtr = NULL;
- Tcl_DecrRefCount(fsPathPtr->normPathPtr);
- fsPathPtr->normPathPtr = copy;
- /* That's our reference to copy used */
- Tcl_DecrRefCount(dir);
- }
- if (clientData != NULL) {
- fsPathPtr->nativePathPtr = clientData;
- }
- PATHFLAGS(pathObjPtr) = 0;
- }
- /* Ensure cwd hasn't changed */
- if (fsPathPtr->cwdPtr != NULL) {
- if (!TclFSCwdPointerEquals(fsPathPtr->cwdPtr)) {
- if (pathObjPtr->bytes == NULL) {
- UpdateStringOfFsPath(pathObjPtr);
- }
- FreeFsPathInternalRep(pathObjPtr);
- pathObjPtr->typePtr = NULL;
- if (Tcl_ConvertToType(interp, pathObjPtr,
- &tclFsPathType) != TCL_OK) {
- return NULL;
- }
- fsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
- } else if (fsPathPtr->normPathPtr == NULL) {
- int cwdLen;
- Tcl_Obj *copy;
- CONST char *cwdStr;
- ClientData clientData = NULL;
-
- copy = Tcl_DuplicateObj(fsPathPtr->cwdPtr);
- Tcl_IncrRefCount(copy);
- cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
- /*
- * Should we perhaps use 'Tcl_FSPathSeparator'?
- * But then what about the Windows special case?
- * Perhaps we should just check if cwd is a root volume.
- * We should never get cwdLen == 0 in this code path.
- */
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- if (cwdStr[cwdLen-1] != '/') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
- case TCL_PLATFORM_WINDOWS:
- if (cwdStr[cwdLen-1] != '/'
- && cwdStr[cwdLen-1] != '\') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
- case TCL_PLATFORM_MAC:
- if (cwdStr[cwdLen-1] != ':') {
- Tcl_AppendToObj(copy, ":", 1);
- cwdLen++;
- }
- break;
- }
- Tcl_AppendObjToObj(copy, pathObjPtr);
- /*
- * Normalize the combined string, but only starting after
- * the end of the previously normalized 'dir'. This should
- * be much faster!
- */
- TclFSNormalizeToUniquePath(interp, copy, cwdLen-1,
- (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL));
- fsPathPtr->normPathPtr = copy;
- if (clientData != NULL) {
- fsPathPtr->nativePathPtr = clientData;
- }
- }
- }
- if (fsPathPtr->normPathPtr == NULL) {
- ClientData clientData = NULL;
- Tcl_Obj *useThisCwd = NULL;
- /*
- * Since normPathPtr is NULL, but this is a valid path
- * object, we know that the translatedPathPtr cannot be NULL.
- */
- Tcl_Obj *absolutePath = fsPathPtr->translatedPathPtr;
- char *path = Tcl_GetString(absolutePath);
-
- /*
- * We have to be a little bit careful here to avoid infinite loops
- * we're asking Tcl_FSGetPathType to return the path's type, but
- * that call can actually result in a lot of other filesystem
- * action, which might loop back through here.
- */
- if (path[0] != ' ') {
- Tcl_PathType type = Tcl_FSGetPathType(pathObjPtr);
- if (type == TCL_PATH_RELATIVE) {
- useThisCwd = Tcl_FSGetCwd(interp);
- if (useThisCwd == NULL) return NULL;
- absolutePath = Tcl_FSJoinToPath(useThisCwd, 1, &absolutePath);
- Tcl_IncrRefCount(absolutePath);
- /* We have a refCount on the cwd */
- #ifdef __WIN32__
- } else if (type == TCL_PATH_VOLUME_RELATIVE) {
- /*
- * Only Windows has volume-relative paths. These
- * paths are rather rare, but is is nice if Tcl can
- * handle them. It is much better if we can
- * handle them here, rather than in the native fs code,
- * because we really need to have a real absolute path
- * just below.
- *
- * We do not let this block compile on non-Windows
- * platforms because the test suite's manual forcing
- * of tclPlatform can otherwise cause this code path
- * to be executed, causing various errors because
- * volume-relative paths really do not exist.
- */
- useThisCwd = Tcl_FSGetCwd(interp);
- if (useThisCwd == NULL) return NULL;
-
- if (path[0] == '/') {
- /*
- * Path of form /foo/bar which is a path in the
- * root directory of the current volume.
- */
- CONST char *drive = Tcl_GetString(useThisCwd);
- absolutePath = Tcl_NewStringObj(drive,2);
- Tcl_AppendToObj(absolutePath, path, -1);
- Tcl_IncrRefCount(absolutePath);
- /* We have a refCount on the cwd */
- } else {
- /*
- * Path of form C:foo/bar, but this only makes
- * sense if the cwd is also on drive C.
- */
- CONST char *drive = Tcl_GetString(useThisCwd);
- char drive_c = path[0];
- if (drive_c >= 'a') {
- drive_c -= ('a' - 'A');
- }
- if (drive[0] == drive_c) {
- absolutePath = Tcl_DuplicateObj(useThisCwd);
- /* We have a refCount on the cwd */
- } else {
- Tcl_DecrRefCount(useThisCwd);
- useThisCwd = NULL;
- /*
- * The path is not in the current drive, but
- * is volume-relative. The way Tcl 8.3 handles
- * this is that it treats such a path as
- * relative to the root of the drive. We
- * therefore behave the same here.
- */
- absolutePath = Tcl_NewStringObj(path, 2);
- }
- Tcl_IncrRefCount(absolutePath);
- Tcl_AppendToObj(absolutePath, "/", 1);
- Tcl_AppendToObj(absolutePath, path+2, -1);
- }
- #endif /* __WIN32__ */
- }
- }
- /* Already has refCount incremented */
- fsPathPtr->normPathPtr = TclFSNormalizeAbsolutePath(interp, absolutePath,
- (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL));
- if (0 && (clientData != NULL)) {
- fsPathPtr->nativePathPtr =
- (*fsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc)(clientData);
- }
- if (!strcmp(Tcl_GetString(fsPathPtr->normPathPtr),
- Tcl_GetString(pathObjPtr))) {
- /*
- * The path was already normalized.
- * Get rid of the duplicate.
- */
- Tcl_DecrRefCount(fsPathPtr->normPathPtr);
- /*
- * We do *not* increment the refCount for
- * this circular reference
- */
- fsPathPtr->normPathPtr = pathObjPtr;
- }
- if (useThisCwd != NULL) {
- /* This was returned by Tcl_FSJoinToPath above */
- Tcl_DecrRefCount(absolutePath);
- fsPathPtr->cwdPtr = useThisCwd;
- }
- }
- return fsPathPtr->normPathPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSGetInternalRep --
- *
- * Extract the internal representation of a given path object,
- * in the given filesystem. If the path object belongs to a
- * different filesystem, we return NULL.
- *
- * If the internal representation is currently NULL, we attempt
- * to generate it, by calling the filesystem's
- * 'Tcl_FSCreateInternalRepProc'.
- *
- * Results:
- * NULL or a valid internal representation.
- *
- * Side effects:
- * An attempt may be made to convert the object.
- *
- *---------------------------------------------------------------------------
- */
- ClientData
- Tcl_FSGetInternalRep(pathObjPtr, fsPtr)
- Tcl_Obj* pathObjPtr;
- Tcl_Filesystem *fsPtr;
- {
- FsPath *srcFsPathPtr;
-
- if (Tcl_FSConvertToPathType(NULL, pathObjPtr) != TCL_OK) {
- return NULL;
- }
- srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
-
- /*
- * We will only return the native representation for the caller's
- * filesystem. Otherwise we will simply return NULL. This means
- * that there must be a unique bi-directional mapping between paths
- * and filesystems, and that this mapping will not allow 'remapped'
- * files -- files which are in one filesystem but mapped into
- * another. Another way of putting this is that 'stacked'
- * filesystems are not allowed. We recognise that this is a
- * potentially useful feature for the future.
- *
- * Even something simple like a 'pass through' filesystem which
- * logs all activity and passes the calls onto the native system
- * would be nice, but not easily achievable with the current
- * implementation.
- */
- if (srcFsPathPtr->fsRecPtr == NULL) {
- /*
- * This only usually happens in wrappers like TclpStat which
- * create a string object and pass it to TclpObjStat. Code
- * which calls the Tcl_FS.. functions should always have a
- * filesystem already set. Whether this code path is legal or
- * not depends on whether we decide to allow external code to
- * call the native filesystem directly. It is at least safer
- * to allow this sub-optimal routing.
- */
- Tcl_FSGetFileSystemForPath(pathObjPtr);
-
- /*
- * If we fail through here, then the path is probably not a
- * valid path in the filesystsem, and is most likely to be a
- * use of the empty path "" via a direct call to one of the
- * objectified interfaces (e.g. from the Tcl testsuite).
- */
- srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
- if (srcFsPathPtr->fsRecPtr == NULL) {
- return NULL;
- }
- }
- if (fsPtr != srcFsPathPtr->fsRecPtr->fsPtr) {
- /*
- * There is still one possibility we should consider; if the
- * file belongs to a different filesystem, perhaps it is
- * actually linked through to a file in our own filesystem
- * which we do care about. The way we can check for this
- * is we ask what filesystem this path belongs to.
- */
- Tcl_Filesystem *actualFs = Tcl_FSGetFileSystemForPath(pathObjPtr);
- if (actualFs == fsPtr) {
- return Tcl_FSGetInternalRep(pathObjPtr, fsPtr);
- }
- return NULL;
- }
- if (srcFsPathPtr->nativePathPtr == NULL) {
- Tcl_FSCreateInternalRepProc *proc;
- char *nativePathPtr;
- proc = srcFsPathPtr->fsRecPtr->fsPtr->createInternalRepProc;
- if (proc == NULL) {
- return NULL;
- }
- nativePathPtr = (*proc)(pathObjPtr);
- srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
- srcFsPathPtr->nativePathPtr = nativePathPtr;
- }
- return srcFsPathPtr->nativePathPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TclFSEnsureEpochOk --
- *
- * This will ensure the pathObjPtr is up to date and can be
- * converted into a "path" type, and that we are able to generate a
- * complete normalized path which is used to determine the
- * filesystem match.
- *
- * Results:
- * Standard Tcl return code.
- *
- * Side effects:
- * An attempt may be made to convert the object.
- *
- *---------------------------------------------------------------------------
- */
- int
- TclFSEnsureEpochOk(pathObjPtr, fsPtrPtr)
- Tcl_Obj* pathObjPtr;
- Tcl_Filesystem **fsPtrPtr;
- {
- FsPath *srcFsPathPtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- /*
- * SHOULD BE ABLE TO IMPROVE EFFICIENCY HERE.
- */
- if (Tcl_FSGetNormalizedPath(NULL, pathObjPtr) == NULL) {
- return TCL_ERROR;
- }
- srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
- /*
- * Check if the filesystem has changed in some way since
- * this object's internal representation was calculated.
- */
- if (srcFsPathPtr->filesystemEpoch != tsdPtr->filesystemEpoch) {
- /*
- * We have to discard the stale representation and
- * recalculate it
- */
- if (pathObjPtr->bytes == NULL) {
- UpdateStringOfFsPath(pathObjPtr);
- }
- FreeFsPathInternalRep(pathObjPtr);
- pathObjPtr->typePtr = NULL;
- if (SetFsPathFromAny(NULL, pathObjPtr) != TCL_OK) {
- return TCL_ERROR;
- }
- srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
- }
- /* Check whether the object is already assigned to a fs */
- if (srcFsPathPtr->fsRecPtr != NULL) {
- *fsPtrPtr = srcFsPathPtr->fsRecPtr->fsPtr;
- }
- return TCL_OK;
- }
- void
- TclFSSetPathDetails(pathObjPtr, fsRecPtr, clientData)
- Tcl_Obj *pathObjPtr;
- FilesystemRecord *fsRecPtr;
- ClientData clientData;
- {
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- /* We assume pathObjPtr is already of the correct type */
- FsPath *srcFsPathPtr;
-
- srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
- srcFsPathPtr->fsRecPtr = fsRecPtr;
- srcFsPathPtr->nativePathPtr = clientData;
- srcFsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
- fsRecPtr->fileRefCount++;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_FSEqualPaths --
- *
- * This function tests whether the two paths given are equal path
- * objects. If either or both is NULL, 0 is always returned.
- *
- * Results:
- * 1 or 0.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_FSEqualPaths(firstPtr, secondPtr)
- Tcl_Obj* firstPtr;
- Tcl_Obj* secondPtr;
- {
- if (firstPtr == secondPtr) {
- return 1;
- } else {
- char *firstStr, *secondStr;
- int firstLen, secondLen, tempErrno;
- if (firstPtr == NULL || secondPtr == NULL) {
- return 0;
- }
- firstStr = Tcl_GetStringFromObj(firstPtr, &firstLen);
- secondStr = Tcl_GetStringFromObj(secondPtr, &secondLen);
- if ((firstLen == secondLen) && (strcmp(firstStr, secondStr) == 0)) {
- return 1;
- }
- /*
- * Try the most thorough, correct method of comparing fully
- * normalized paths
- */
- tempErrno = Tcl_GetErrno();
- firstPtr = Tcl_FSGetNormalizedPath(NULL, firstPtr);
- secondPtr = Tcl_FSGetNormalizedPath(NULL, secondPtr);
- Tcl_SetErrno(tempErrno);
- if (firstPtr == NULL || secondPtr == NULL) {
- return 0;
- }
- firstStr = Tcl_GetStringFromObj(firstPtr, &firstLen);
- secondStr = Tcl_GetStringFromObj(secondPtr, &secondLen);
- if ((firstLen == secondLen) && (strcmp(firstStr, secondStr) == 0)) {
- return 1;
- }
- }
- return 0;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * SetFsPathFromAny --
- *
- * This function tries to convert the given Tcl_Obj to a valid
- * Tcl path type.
- *
- * The filename may begin with "~" (to indicate current user's
- * home directory) or "~<user>" (to indicate any user's home
- * directory).
- *
- * Results:
- * Standard Tcl error code.
- *
- * Side effects:
- * The old representation may be freed, and new memory allocated.
- *
- *---------------------------------------------------------------------------
- */
- static int
- SetFsPathFromAny(interp, objPtr)
- Tcl_Interp *interp; /* Used for error reporting if not NULL. */
- Tcl_Obj *objPtr; /* The object to convert. */
- {
- int len;
- FsPath *fsPathPtr;
- Tcl_Obj *transPtr;
- char *name;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (objPtr->typePtr == &tclFsPathType) {
- return TCL_OK;
- }
-
- /*
- * First step is to translate the filename. This is similar to
- * Tcl_TranslateFilename, but shouldn't convert everything to
- * windows backslashes on that platform. The current
- * implementation of this piece is a slightly optimised version
- * of the various Tilde/Split/Join stuff to avoid multiple
- * split/join operations.
- *
- * We remove any trailing directory separator.
- *
- * However, the split/join routines are quite complex, and
- * one has to make sure not to break anything on Unix, Win
- * or MacOS (fCmd.test, fileName.test and cmdAH.test exercise
- * most of the code).
- */
- name = Tcl_GetStringFromObj(objPtr,&len);
- /*
- * Handle tilde substitutions, if needed.
- */
- if (name[0] == '~') {
- char *expandedUser;
- Tcl_DString temp;
- int split;
- char separator='/';
-
- if (tclPlatform==TCL_PLATFORM_MAC) {
- if (strchr(name, ':') != NULL) separator = ':';
- }
-
- split = FindSplitPos(name, &separator);
- if (split != len) {
- /* We have multiple pieces '~user/foo/bar...' */
- name[split] = ' ';
- }
- /* Do some tilde substitution */
- if (name[1] == ' ') {
- /* We have just '~' */
- CONST char *dir;
- Tcl_DString dirString;
- if (split != len) { name[split] = separator; }
-
- dir = TclGetEnv("HOME", &dirString);
- if (dir == NULL) {
- if (interp) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "couldn't find HOME environment ",
- "variable to expand path", (char *) NULL);
- }
- return TCL_ERROR;
- }
- Tcl_DStringInit(&temp);
- Tcl_JoinPath(1, &dir, &temp);
- Tcl_DStringFree(&dirString);
- } else {
- /* We have a user name '~user' */
- Tcl_DStringInit(&temp);
- if (TclpGetUserHome(name+1, &temp) == NULL) {
- if (interp != NULL) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "user "", (name+1),
- "" doesn't exist", (char *) NULL);
- }
- Tcl_DStringFree(&temp);
- if (split != len) { name[split] = separator; }
- return TCL_ERROR;
- }
- if (split != len) { name[split] = separator; }
- }
-
- expandedUser = Tcl_DStringValue(&temp);
- transPtr = Tcl_NewStringObj(expandedUser, Tcl_DStringLength(&temp));
- if (split != len) {
- /* Join up the tilde substitution with the rest */
- if (name[split+1] == separator) {
- /*
- * Somewhat tricky case like ~//foo/bar.
- * Make use of Split/Join machinery to get it right.
- * Assumes all paths beginning with ~ are part of the
- * native filesystem.
- */
- int objc;
- Tcl_Obj **objv;
- Tcl_Obj *parts = TclpNativeSplitPath(objPtr, NULL);
- Tcl_ListObjGetElements(NULL, parts, &objc, &objv);
- /* Skip '~'. It's replaced by its expansion */
- objc--; objv++;
- while (objc--) {
- TclpNativeJoinPath(transPtr, Tcl_GetString(*objv++));
- }
- Tcl_DecrRefCount(parts);
- } else {
- /* Simple case. "rest" is relative path. Just join it. */
- Tcl_Obj *rest = Tcl_NewStringObj(name+split+1,-1);
- transPtr = Tcl_FSJoinToPath(transPtr, 1, &rest);
- }
- }
- Tcl_DStringFree(&temp);
- } else {
- transPtr = Tcl_FSJoinToPath(objPtr,0,NULL);
- }
- #if defined(__CYGWIN__) && defined(__WIN32__)
- {
- extern int cygwin_conv_to_win32_path
- _ANSI_ARGS_((CONST char *, char *));
- char winbuf[MAX_PATH+1];
- /*
- * In the Cygwin world, call conv_to_win32_path in order to use the
- * mount table to translate the file name into something Windows will
- * understand. Take care when converting empty strings!
- */
- name = Tcl_GetStringFromObj(transPtr, &len);
- if (len > 0) {
- cygwin_conv_to_win32_path(name, winbuf);
- TclWinNoBackslash(winbuf);
- Tcl_SetStringObj(transPtr, winbuf, -1);
- }
- }
- #endif /* __CYGWIN__ && __WIN32__ */
- /*
- * Now we have a translated filename in 'transPtr'. This will have
- * forward slashes on Windows, and will not contain any ~user
- * sequences.
- */
-
- fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
- fsPathPtr->translatedPathPtr = transPtr;
- Tcl_IncrRefCount(fsPathPtr->translatedPathPtr);
- fsPathPtr->normPathPtr = NULL;
- fsPathPtr->cwdPtr = NULL;
- fsPathPtr->nativePathPtr = NULL;
- fsPathPtr->fsRecPtr = NULL;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
- /*
- * Free old representation before installing our new one.
- */
- if (objPtr->typePtr != NULL && objPtr->typePtr->freeIntRepProc != NULL) {
- (objPtr->typePtr->freeIntRepProc)(objPtr);
- }
- PATHOBJ(objPtr) = (VOID *) fsPathPtr;
- PATHFLAGS(objPtr) = 0;
- objPtr->typePtr = &tclFsPathType;
- return TCL_OK;
- }
- static void
- FreeFsPathInternalRep(pathObjPtr)
- Tcl_Obj *pathObjPtr; /* Path object with internal rep to free. */
- {
- FsPath *fsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
- if (fsPathPtr->translatedPathPtr != NULL) {
- if (fsPathPtr->translatedPathPtr != pathObjPtr) {
- Tcl_DecrRefCount(fsPathPtr->translatedPathPtr);
- }
- }
- if (fsPathPtr->normPathPtr != NULL) {
- if (fsPathPtr->normPathPtr != pathObjPtr) {
- Tcl_DecrRefCount(fsPathPtr->normPathPtr);
- }
- fsPathPtr->normPathPtr = NULL;
- }
- if (fsPathPtr->cwdPtr != NULL) {
- Tcl_DecrRefCount(fsPathPtr->cwdPtr);
- }
- if (fsPathPtr->nativePathPtr != NULL) {
- if (fsPathPtr->fsRecPtr != NULL) {
- if (fsPathPtr->fsRecPtr->fsPtr->freeInternalRepProc != NULL) {
- (*fsPathPtr->fsRecPtr->fsPtr
- ->freeInternalRepProc)(fsPathPtr->nativePathPtr);
- fsPathPtr->nativePathPtr = NULL;
- }
- }
- }
- if (fsPathPtr->fsRecPtr != NULL) {
- fsPathPtr->fsRecPtr->fileRefCount--;
- if (fsPathPtr->fsRecPtr->fileRefCount <= 0) {
- /* It has been unregistered already, so simply free it */
- ckfree((char *)fsPathPtr->fsRecPtr);
- }
- }
- ckfree((char*) fsPathPtr);
- }
- static void
- DupFsPathInternalRep(srcPtr, copyPtr)
- Tcl_Obj *srcPtr; /* Path obj with internal rep to copy. */
- Tcl_Obj *copyPtr; /* Path obj with internal rep to set. */
- {
- FsPath *srcFsPathPtr = (FsPath*) PATHOBJ(srcPtr);
- FsPath *copyFsPathPtr = (FsPath*) ckalloc((unsigned)sizeof(FsPath));
-
- Tcl_FSDupInternalRepProc *dupProc;
-
- PATHOBJ(copyPtr) = (VOID *) copyFsPathPtr;
- if (srcFsPathPtr->translatedPathPtr != NULL) {
- copyFsPathPtr->translatedPathPtr = srcFsPathPtr->translatedPathPtr;
- if (copyFsPathPtr->translatedPathPtr != copyPtr) {
- Tcl_IncrRefCount(copyFsPathPtr->translatedPathPtr);
- }
- } else {
- copyFsPathPtr->translatedPathPtr = NULL;
- }
-
- if (srcFsPathPtr->normPathPtr != NULL) {
- copyFsPathPtr->normPathPtr = srcFsPathPtr->normPathPtr;
- if (copyFsPathPtr->normPathPtr != copyPtr) {
- Tcl_IncrRefCount(copyFsPathPtr->normPathPtr);
- }
- } else {
- copyFsPathPtr->normPathPtr = NULL;
- }
-
- if (srcFsPathPtr->cwdPtr != NULL) {
- copyFsPathPtr->cwdPtr = srcFsPathPtr->cwdPtr;
- Tcl_IncrRefCount(copyFsPathPtr->cwdPtr);
- } else {
- copyFsPathPtr->cwdPtr = NULL;
- }
- copyFsPathPtr->flags = srcFsPathPtr->flags;
-
- if (srcFsPathPtr->fsRecPtr != NULL
- && srcFsPathPtr->nativePathPtr != NULL) {
- dupProc = srcFsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc;
- if (dupProc != NULL) {
- copyFsPathPtr->nativePathPtr =
- (*dupProc)(srcFsPathPtr->nativePathPtr);
- } else {
- copyFsPathPtr->nativePathPtr = NULL;
- }
- } else {
- copyFsPathPtr->nativePathPtr = NULL;
- }
- copyFsPathPtr->fsRecPtr = srcFsPathPtr->fsRecPtr;
- copyFsPathPtr->filesystemEpoch = srcFsPathPtr->filesystemEpoch;
- if (copyFsPathPtr->fsRecPtr != NULL) {
- copyFsPathPtr->fsRecPtr->fileRefCount++;
- }
- copyPtr->typePtr = &tclFsPathType;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * UpdateStringOfFsPath --
- *
- * Gives an object a valid string rep.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Memory may be allocated.
- *
- *---------------------------------------------------------------------------
- */
- static void
- UpdateStringOfFsPath(objPtr)
- register Tcl_Obj *objPtr; /* path obj with string rep to update. */
- {
- FsPath *fsPathPtr = (FsPath*) PATHOBJ(objPtr);
- CONST char *cwdStr;
- int cwdLen;
- Tcl_Obj *copy;
-
- if (PATHFLAGS(objPtr) == 0 || fsPathPtr->cwdPtr == NULL) {
- panic("Called UpdateStringOfFsPath with invalid object");
- }
-
- copy = Tcl_DuplicateObj(fsPathPtr->cwdPtr);
- Tcl_IncrRefCount(copy);
-
- cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
- /*
- * Should we perhaps use 'Tcl_FSPathSeparator'?
- * But then what about the Windows special case?
- * Perhaps we should just check if cwd is a root volume.
- * We should never get cwdLen == 0 in this code path.
- */
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- if (cwdStr[cwdLen-1] != '/') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
- case TCL_PLATFORM_WINDOWS:
- /*
- * We need the extra 'cwdLen != 2', and ':' checks because
- * a volume relative path doesn't get a '/'. For example
- * 'glob C:*cat*.exe' will return 'C:cat32.exe'
- */
- if (cwdStr[cwdLen-1] != '/'
- && cwdStr[cwdLen-1] != '\') {
- if (cwdLen != 2 || cwdStr[1] != ':') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- }
- break;
- case TCL_PLATFORM_MAC:
- if (cwdStr[cwdLen-1] != ':') {
- Tcl_AppendToObj(copy, ":", 1);
- cwdLen++;
- }
- break;
- }
- Tcl_AppendObjToObj(copy, fsPathPtr->normPathPtr);
- objPtr->bytes = Tcl_GetStringFromObj(copy, &cwdLen);
- objPtr->length = cwdLen;
- copy->bytes = tclEmptyStringRep;
- copy->length = 0;
- Tcl_DecrRefCount(copy);
- }
- /*
- *---------------------------------------------------------------------------
- *
- * NativePathInFilesystem --
- *
- * Any path object is acceptable to the native filesystem, by
- * default (we will throw errors when illegal paths are actually
- * tried to be used).
- *
- * However, this behavior means the native filesystem must be
- * the last filesystem in the lookup list (otherwise it will
- * claim all files belong to it, and other filesystems will
- * never get a look in).
- *
- * Results:
- * TCL_OK, to indicate 'yes', -1 to indicate no.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- static int
- NativePathInFilesystem(pathPtr, clientDataPtr)
- Tcl_Obj *pathPtr;
- ClientData *clientDataPtr;
- {
- /*
- * A special case is required to handle the empty path "".
- * This is a valid path (i.e. the user should be able
- * to do 'file exists ""' without throwing an error), but
- * equally the path doesn't exist. Those are the semantics
- * of Tcl (at present anyway), so we have to abide by them
- * here.
- */
- if (pathPtr->typePtr == &tclFsPathType) {
- if (pathPtr->bytes != NULL && pathPtr->bytes[0] == ' ') {
- /* We reject the empty path "" */
- return -1;
- }
- /* Otherwise there is no way this path can be empty */
- } else {
- /*
- * It is somewhat unusual to reach this code path without
- * the object being of tclFsPathType. However, we do
- * our best to deal with the situation.
- */
- int len;
- Tcl_GetStringFromObj(pathPtr,&len);
- if (len == 0) {
- /* We reject the empty path "" */
- return -1;
- }
- }
- /*
- * Path is of correct type, or is of non-zero length,
- * so we accept it.
- */
- return TCL_OK;
- }