[25] | 1 | /* |
---|
| 2 | * tclIOUtil.c -- |
---|
| 3 | * |
---|
| 4 | * This file contains the implementation of Tcl's generic filesystem |
---|
| 5 | * code, which supports a pluggable filesystem architecture allowing both |
---|
| 6 | * platform specific filesystems and 'virtual filesystems'. All |
---|
| 7 | * filesystem access should go through the functions defined in this |
---|
| 8 | * file. Most of this code was contributed by Vince Darley. |
---|
| 9 | * |
---|
| 10 | * Parts of this file are based on code contributed by Karl Lehenbauer, |
---|
| 11 | * Mark Diekhans and Peter da Silva. |
---|
| 12 | * |
---|
| 13 | * Copyright (c) 1991-1994 The Regents of the University of California. |
---|
| 14 | * Copyright (c) 1994-1997 Sun Microsystems, Inc. |
---|
| 15 | * Copyright (c) 2001-2004 Vincent Darley. |
---|
| 16 | * |
---|
| 17 | * See the file "license.terms" for information on usage and redistribution of |
---|
| 18 | * this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
| 19 | * |
---|
| 20 | * RCS: @(#) $Id: tclIOUtil.c,v 1.151 2008/02/27 03:35:49 jenglish Exp $ |
---|
| 21 | */ |
---|
| 22 | |
---|
| 23 | #include "tclInt.h" |
---|
| 24 | #ifdef __WIN32__ |
---|
| 25 | # include "tclWinInt.h" |
---|
| 26 | #endif |
---|
| 27 | #include "tclFileSystem.h" |
---|
| 28 | |
---|
| 29 | /* |
---|
| 30 | * Prototypes for functions defined later in this file. |
---|
| 31 | */ |
---|
| 32 | |
---|
| 33 | static FilesystemRecord*FsGetFirstFilesystem(void); |
---|
| 34 | static void FsThrExitProc(ClientData cd); |
---|
| 35 | static Tcl_Obj * FsListMounts(Tcl_Obj *pathPtr, const char *pattern); |
---|
| 36 | static void FsAddMountsToGlobResult(Tcl_Obj *resultPtr, |
---|
| 37 | Tcl_Obj *pathPtr, const char *pattern, |
---|
| 38 | Tcl_GlobTypeData *types); |
---|
| 39 | static void FsUpdateCwd(Tcl_Obj *cwdObj, ClientData clientData); |
---|
| 40 | |
---|
| 41 | #ifdef TCL_THREADS |
---|
| 42 | static void FsRecacheFilesystemList(void); |
---|
| 43 | #endif |
---|
| 44 | |
---|
| 45 | /* |
---|
| 46 | * These form part of the native filesystem support. They are needed here |
---|
| 47 | * because we have a few native filesystem functions (which are the same for |
---|
| 48 | * win/unix) in this file. There is no need to place them in tclInt.h, because |
---|
| 49 | * they are not (and should not be) used anywhere else. |
---|
| 50 | */ |
---|
| 51 | |
---|
| 52 | MODULE_SCOPE const char * tclpFileAttrStrings[]; |
---|
| 53 | MODULE_SCOPE const TclFileAttrProcs tclpFileAttrProcs[]; |
---|
| 54 | |
---|
| 55 | /* |
---|
| 56 | * The following functions are obsolete string based APIs, and should be |
---|
| 57 | * removed in a future release (Tcl 9 would be a good time). |
---|
| 58 | */ |
---|
| 59 | |
---|
| 60 | |
---|
| 61 | /* Obsolete */ |
---|
| 62 | int |
---|
| 63 | Tcl_Stat( |
---|
| 64 | const char *path, /* Path of file to stat (in current CP). */ |
---|
| 65 | struct stat *oldStyleBuf) /* Filled with results of stat call. */ |
---|
| 66 | { |
---|
| 67 | int ret; |
---|
| 68 | Tcl_StatBuf buf; |
---|
| 69 | Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1); |
---|
| 70 | |
---|
| 71 | #ifndef TCL_WIDE_INT_IS_LONG |
---|
| 72 | Tcl_WideInt tmp1, tmp2; |
---|
| 73 | #ifdef HAVE_ST_BLOCKS |
---|
| 74 | Tcl_WideInt tmp3; |
---|
| 75 | #endif |
---|
| 76 | #endif |
---|
| 77 | |
---|
| 78 | Tcl_IncrRefCount(pathPtr); |
---|
| 79 | ret = Tcl_FSStat(pathPtr, &buf); |
---|
| 80 | Tcl_DecrRefCount(pathPtr); |
---|
| 81 | if (ret != -1) { |
---|
| 82 | #ifndef TCL_WIDE_INT_IS_LONG |
---|
| 83 | # define OUT_OF_RANGE(x) \ |
---|
| 84 | (((Tcl_WideInt)(x)) < Tcl_LongAsWide(LONG_MIN) || \ |
---|
| 85 | ((Tcl_WideInt)(x)) > Tcl_LongAsWide(LONG_MAX)) |
---|
| 86 | # define OUT_OF_URANGE(x) \ |
---|
| 87 | (((Tcl_WideUInt)(x)) > ((Tcl_WideUInt)ULONG_MAX)) |
---|
| 88 | |
---|
| 89 | /* |
---|
| 90 | * Perform the result-buffer overflow check manually. |
---|
| 91 | * |
---|
| 92 | * Note that ino_t/ino64_t is unsigned... |
---|
| 93 | * |
---|
| 94 | * Workaround gcc warning of "comparison is always false due to limited range of |
---|
| 95 | * data type" by assigning to tmp var of type Tcl_WideInt. |
---|
| 96 | */ |
---|
| 97 | |
---|
| 98 | tmp1 = (Tcl_WideInt) buf.st_ino; |
---|
| 99 | tmp2 = (Tcl_WideInt) buf.st_size; |
---|
| 100 | #ifdef HAVE_ST_BLOCKS |
---|
| 101 | tmp3 = (Tcl_WideInt) buf.st_blocks; |
---|
| 102 | #endif |
---|
| 103 | |
---|
| 104 | if (OUT_OF_URANGE(tmp1) || OUT_OF_RANGE(tmp2) |
---|
| 105 | #ifdef HAVE_ST_BLOCKS |
---|
| 106 | || OUT_OF_RANGE(tmp3) |
---|
| 107 | #endif |
---|
| 108 | ) { |
---|
| 109 | #ifdef EFBIG |
---|
| 110 | errno = EFBIG; |
---|
| 111 | #else |
---|
| 112 | # ifdef EOVERFLOW |
---|
| 113 | errno = EOVERFLOW; |
---|
| 114 | # else |
---|
| 115 | # error "What status should be returned for file size out of range?" |
---|
| 116 | # endif |
---|
| 117 | #endif |
---|
| 118 | return -1; |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | # undef OUT_OF_RANGE |
---|
| 122 | # undef OUT_OF_URANGE |
---|
| 123 | #endif /* !TCL_WIDE_INT_IS_LONG */ |
---|
| 124 | |
---|
| 125 | /* |
---|
| 126 | * Copy across all supported fields, with possible type coercions on |
---|
| 127 | * those fields that change between the normal and lf64 versions of |
---|
| 128 | * the stat structure (on Solaris at least). This is slow when the |
---|
| 129 | * structure sizes coincide, but that's what you get for using an |
---|
| 130 | * obsolete interface. |
---|
| 131 | */ |
---|
| 132 | |
---|
| 133 | oldStyleBuf->st_mode = buf.st_mode; |
---|
| 134 | oldStyleBuf->st_ino = (ino_t) buf.st_ino; |
---|
| 135 | oldStyleBuf->st_dev = buf.st_dev; |
---|
| 136 | oldStyleBuf->st_rdev = buf.st_rdev; |
---|
| 137 | oldStyleBuf->st_nlink = buf.st_nlink; |
---|
| 138 | oldStyleBuf->st_uid = buf.st_uid; |
---|
| 139 | oldStyleBuf->st_gid = buf.st_gid; |
---|
| 140 | oldStyleBuf->st_size = (off_t) buf.st_size; |
---|
| 141 | oldStyleBuf->st_atime = buf.st_atime; |
---|
| 142 | oldStyleBuf->st_mtime = buf.st_mtime; |
---|
| 143 | oldStyleBuf->st_ctime = buf.st_ctime; |
---|
| 144 | #ifdef HAVE_ST_BLOCKS |
---|
| 145 | oldStyleBuf->st_blksize = buf.st_blksize; |
---|
| 146 | oldStyleBuf->st_blocks = (blkcnt_t) buf.st_blocks; |
---|
| 147 | #endif |
---|
| 148 | } |
---|
| 149 | return ret; |
---|
| 150 | } |
---|
| 151 | |
---|
| 152 | /* Obsolete */ |
---|
| 153 | int |
---|
| 154 | Tcl_Access( |
---|
| 155 | const char *path, /* Path of file to access (in current CP). */ |
---|
| 156 | int mode) /* Permission setting. */ |
---|
| 157 | { |
---|
| 158 | int ret; |
---|
| 159 | Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1); |
---|
| 160 | |
---|
| 161 | Tcl_IncrRefCount(pathPtr); |
---|
| 162 | ret = Tcl_FSAccess(pathPtr,mode); |
---|
| 163 | Tcl_DecrRefCount(pathPtr); |
---|
| 164 | |
---|
| 165 | return ret; |
---|
| 166 | } |
---|
| 167 | |
---|
| 168 | /* Obsolete */ |
---|
| 169 | Tcl_Channel |
---|
| 170 | Tcl_OpenFileChannel( |
---|
| 171 | Tcl_Interp *interp, /* Interpreter for error reporting; can be |
---|
| 172 | * NULL. */ |
---|
| 173 | const char *path, /* Name of file to open. */ |
---|
| 174 | const char *modeString, /* A list of POSIX open modes or a string such |
---|
| 175 | * as "rw". */ |
---|
| 176 | int permissions) /* If the open involves creating a file, with |
---|
| 177 | * what modes to create it? */ |
---|
| 178 | { |
---|
| 179 | Tcl_Channel ret; |
---|
| 180 | Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1); |
---|
| 181 | |
---|
| 182 | Tcl_IncrRefCount(pathPtr); |
---|
| 183 | ret = Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions); |
---|
| 184 | Tcl_DecrRefCount(pathPtr); |
---|
| 185 | |
---|
| 186 | return ret; |
---|
| 187 | } |
---|
| 188 | |
---|
| 189 | /* Obsolete */ |
---|
| 190 | int |
---|
| 191 | Tcl_Chdir( |
---|
| 192 | const char *dirName) |
---|
| 193 | { |
---|
| 194 | int ret; |
---|
| 195 | Tcl_Obj *pathPtr = Tcl_NewStringObj(dirName,-1); |
---|
| 196 | Tcl_IncrRefCount(pathPtr); |
---|
| 197 | ret = Tcl_FSChdir(pathPtr); |
---|
| 198 | Tcl_DecrRefCount(pathPtr); |
---|
| 199 | return ret; |
---|
| 200 | } |
---|
| 201 | |
---|
| 202 | /* Obsolete */ |
---|
| 203 | char * |
---|
| 204 | Tcl_GetCwd( |
---|
| 205 | Tcl_Interp *interp, |
---|
| 206 | Tcl_DString *cwdPtr) |
---|
| 207 | { |
---|
| 208 | Tcl_Obj *cwd; |
---|
| 209 | cwd = Tcl_FSGetCwd(interp); |
---|
| 210 | if (cwd == NULL) { |
---|
| 211 | return NULL; |
---|
| 212 | } else { |
---|
| 213 | Tcl_DStringInit(cwdPtr); |
---|
| 214 | Tcl_DStringAppend(cwdPtr, Tcl_GetString(cwd), -1); |
---|
| 215 | Tcl_DecrRefCount(cwd); |
---|
| 216 | return Tcl_DStringValue(cwdPtr); |
---|
| 217 | } |
---|
| 218 | } |
---|
| 219 | |
---|
| 220 | /* Obsolete */ |
---|
| 221 | int |
---|
| 222 | Tcl_EvalFile( |
---|
| 223 | Tcl_Interp *interp, /* Interpreter in which to process file. */ |
---|
| 224 | const char *fileName) /* Name of file to process. Tilde-substitution |
---|
| 225 | * will be performed on this name. */ |
---|
| 226 | { |
---|
| 227 | int ret; |
---|
| 228 | Tcl_Obj *pathPtr = Tcl_NewStringObj(fileName,-1); |
---|
| 229 | Tcl_IncrRefCount(pathPtr); |
---|
| 230 | ret = Tcl_FSEvalFile(interp, pathPtr); |
---|
| 231 | Tcl_DecrRefCount(pathPtr); |
---|
| 232 | return ret; |
---|
| 233 | } |
---|
| 234 | |
---|
| 235 | /* |
---|
| 236 | * The 3 hooks for Stat, Access and OpenFileChannel are obsolete. The |
---|
| 237 | * complete, general hooked filesystem APIs should be used instead. This |
---|
| 238 | * define decides whether to include the obsolete hooks and related code. If |
---|
| 239 | * these are removed, we'll also want to remove them from stubs/tclInt. The |
---|
| 240 | * only known users of these APIs are prowrap and mktclapp. New |
---|
| 241 | * code/extensions should not use them, since they do not provide as full |
---|
| 242 | * support as the full filesystem API. |
---|
| 243 | * |
---|
| 244 | * As soon as prowrap and mktclapp are updated to use the full filesystem |
---|
| 245 | * support, I suggest all these hooks are removed. |
---|
| 246 | */ |
---|
| 247 | |
---|
| 248 | #undef USE_OBSOLETE_FS_HOOKS |
---|
| 249 | |
---|
| 250 | #ifdef USE_OBSOLETE_FS_HOOKS |
---|
| 251 | |
---|
| 252 | /* |
---|
| 253 | * The following typedef declarations allow for hooking into the chain of |
---|
| 254 | * functions maintained for 'Tcl_Stat(...)', 'Tcl_Access(...)' & |
---|
| 255 | * 'Tcl_OpenFileChannel(...)'. Basically for each hookable function a linked |
---|
| 256 | * list is defined. |
---|
| 257 | */ |
---|
| 258 | |
---|
| 259 | typedef struct StatProc { |
---|
| 260 | TclStatProc_ *proc; /* Function to process a 'stat()' call */ |
---|
| 261 | struct StatProc *nextPtr; /* The next 'stat()' function to call */ |
---|
| 262 | } StatProc; |
---|
| 263 | |
---|
| 264 | typedef struct AccessProc { |
---|
| 265 | TclAccessProc_ *proc; /* Function to process a 'access()' call */ |
---|
| 266 | struct AccessProc *nextPtr; /* The next 'access()' function to call */ |
---|
| 267 | } AccessProc; |
---|
| 268 | |
---|
| 269 | typedef struct OpenFileChannelProc { |
---|
| 270 | TclOpenFileChannelProc_ *proc; |
---|
| 271 | /* Function to process a |
---|
| 272 | * 'Tcl_OpenFileChannel()' call */ |
---|
| 273 | struct OpenFileChannelProc *nextPtr; |
---|
| 274 | /* The next 'Tcl_OpenFileChannel()' function |
---|
| 275 | * to call */ |
---|
| 276 | } OpenFileChannelProc; |
---|
| 277 | |
---|
| 278 | /* |
---|
| 279 | * For each type of (obsolete) hookable function, a static node is declared to |
---|
| 280 | * hold the function pointer for the "built-in" routine (e.g. 'TclpStat(...)') |
---|
| 281 | * and the respective list is initialized as a pointer to that node. |
---|
| 282 | * |
---|
| 283 | * The "delete" functions (e.g. 'TclStatDeleteProc(...)') ensure that these |
---|
| 284 | * statically declared list entry cannot be inadvertently removed. |
---|
| 285 | * |
---|
| 286 | * This method avoids the need to call any sort of "initialization" function. |
---|
| 287 | * |
---|
| 288 | * All three lists are protected by a global obsoleteFsHookMutex. |
---|
| 289 | */ |
---|
| 290 | |
---|
| 291 | static StatProc *statProcList = NULL; |
---|
| 292 | static AccessProc *accessProcList = NULL; |
---|
| 293 | static OpenFileChannelProc *openFileChannelProcList = NULL; |
---|
| 294 | |
---|
| 295 | TCL_DECLARE_MUTEX(obsoleteFsHookMutex) |
---|
| 296 | |
---|
| 297 | #endif /* USE_OBSOLETE_FS_HOOKS */ |
---|
| 298 | |
---|
| 299 | /* |
---|
| 300 | * Declare the native filesystem support. These functions should be considered |
---|
| 301 | * private to Tcl, and should really not be called directly by any code other |
---|
| 302 | * than this file (i.e. neither by Tcl's core nor by extensions). Similarly, |
---|
| 303 | * the old string-based Tclp... native filesystem functions should not be |
---|
| 304 | * called. |
---|
| 305 | * |
---|
| 306 | * The correct API to use now is the Tcl_FS... set of functions, which ensure |
---|
| 307 | * correct and complete virtual filesystem support. |
---|
| 308 | * |
---|
| 309 | * We cannot make all of these static, since some of them are implemented in |
---|
| 310 | * the platform-specific directories. |
---|
| 311 | */ |
---|
| 312 | |
---|
| 313 | static Tcl_FSFilesystemSeparatorProc NativeFilesystemSeparator; |
---|
| 314 | static Tcl_FSFreeInternalRepProc NativeFreeInternalRep; |
---|
| 315 | static Tcl_FSFileAttrStringsProc NativeFileAttrStrings; |
---|
| 316 | static Tcl_FSFileAttrsGetProc NativeFileAttrsGet; |
---|
| 317 | static Tcl_FSFileAttrsSetProc NativeFileAttrsSet; |
---|
| 318 | |
---|
| 319 | /* |
---|
| 320 | * The only reason these functions are not static is that they are either |
---|
| 321 | * called by code in the native (win/unix) directories or they are actually |
---|
| 322 | * implemented in those directories. They should simply not be called by code |
---|
| 323 | * outside Tcl's native filesystem core i.e. they should be considered |
---|
| 324 | * 'static' to Tcl's filesystem code (if we ever built the native filesystem |
---|
| 325 | * support into a separate code library, this could actually be enforced). |
---|
| 326 | */ |
---|
| 327 | |
---|
| 328 | Tcl_FSFilesystemPathTypeProc TclpFilesystemPathType; |
---|
| 329 | Tcl_FSInternalToNormalizedProc TclpNativeToNormalized; |
---|
| 330 | Tcl_FSStatProc TclpObjStat; |
---|
| 331 | Tcl_FSAccessProc TclpObjAccess; |
---|
| 332 | Tcl_FSMatchInDirectoryProc TclpMatchInDirectory; |
---|
| 333 | Tcl_FSChdirProc TclpObjChdir; |
---|
| 334 | Tcl_FSLstatProc TclpObjLstat; |
---|
| 335 | Tcl_FSCopyFileProc TclpObjCopyFile; |
---|
| 336 | Tcl_FSDeleteFileProc TclpObjDeleteFile; |
---|
| 337 | Tcl_FSRenameFileProc TclpObjRenameFile; |
---|
| 338 | Tcl_FSCreateDirectoryProc TclpObjCreateDirectory; |
---|
| 339 | Tcl_FSCopyDirectoryProc TclpObjCopyDirectory; |
---|
| 340 | Tcl_FSRemoveDirectoryProc TclpObjRemoveDirectory; |
---|
| 341 | Tcl_FSUnloadFileProc TclpUnloadFile; |
---|
| 342 | Tcl_FSLinkProc TclpObjLink; |
---|
| 343 | Tcl_FSListVolumesProc TclpObjListVolumes; |
---|
| 344 | |
---|
| 345 | /* |
---|
| 346 | * Define the native filesystem dispatch table. If necessary, it is ok to make |
---|
| 347 | * this non-static, but it should only be accessed by the functions actually |
---|
| 348 | * listed within it (or perhaps other helper functions of them). Anything |
---|
| 349 | * which is not part of this 'native filesystem implementation' should not be |
---|
| 350 | * delving inside here! |
---|
| 351 | */ |
---|
| 352 | |
---|
| 353 | Tcl_Filesystem tclNativeFilesystem = { |
---|
| 354 | "native", |
---|
| 355 | sizeof(Tcl_Filesystem), |
---|
| 356 | TCL_FILESYSTEM_VERSION_2, |
---|
| 357 | &TclNativePathInFilesystem, |
---|
| 358 | &TclNativeDupInternalRep, |
---|
| 359 | &NativeFreeInternalRep, |
---|
| 360 | &TclpNativeToNormalized, |
---|
| 361 | &TclNativeCreateNativeRep, |
---|
| 362 | &TclpObjNormalizePath, |
---|
| 363 | &TclpFilesystemPathType, |
---|
| 364 | &NativeFilesystemSeparator, |
---|
| 365 | &TclpObjStat, |
---|
| 366 | &TclpObjAccess, |
---|
| 367 | &TclpOpenFileChannel, |
---|
| 368 | &TclpMatchInDirectory, |
---|
| 369 | &TclpUtime, |
---|
| 370 | #ifndef S_IFLNK |
---|
| 371 | NULL, |
---|
| 372 | #else |
---|
| 373 | &TclpObjLink, |
---|
| 374 | #endif /* S_IFLNK */ |
---|
| 375 | &TclpObjListVolumes, |
---|
| 376 | &NativeFileAttrStrings, |
---|
| 377 | &NativeFileAttrsGet, |
---|
| 378 | &NativeFileAttrsSet, |
---|
| 379 | &TclpObjCreateDirectory, |
---|
| 380 | &TclpObjRemoveDirectory, |
---|
| 381 | &TclpObjDeleteFile, |
---|
| 382 | &TclpObjCopyFile, |
---|
| 383 | &TclpObjRenameFile, |
---|
| 384 | &TclpObjCopyDirectory, |
---|
| 385 | &TclpObjLstat, |
---|
| 386 | &TclpDlopen, |
---|
| 387 | /* Needs a cast since we're using version_2 */ |
---|
| 388 | (Tcl_FSGetCwdProc *) &TclpGetNativeCwd, |
---|
| 389 | &TclpObjChdir |
---|
| 390 | }; |
---|
| 391 | |
---|
| 392 | /* |
---|
| 393 | * Define the tail of the linked list. Note that for unconventional uses of |
---|
| 394 | * Tcl without a native filesystem, we may in the future wish to modify the |
---|
| 395 | * current approach of hard-coding the native filesystem in the lookup list |
---|
| 396 | * 'filesystemList' below. |
---|
| 397 | * |
---|
| 398 | * We initialize the record so that it thinks one file uses it. This means it |
---|
| 399 | * will never be freed. |
---|
| 400 | */ |
---|
| 401 | |
---|
| 402 | static FilesystemRecord nativeFilesystemRecord = { |
---|
| 403 | NULL, |
---|
| 404 | &tclNativeFilesystem, |
---|
| 405 | 1, |
---|
| 406 | NULL |
---|
| 407 | }; |
---|
| 408 | |
---|
| 409 | /* |
---|
| 410 | * This is incremented each time we modify the linked list of filesystems. Any |
---|
| 411 | * time it changes, all cached filesystem representations are suspect and must |
---|
| 412 | * be freed. For multithreading builds, change of the filesystem epoch will |
---|
| 413 | * trigger cache cleanup in all threads. |
---|
| 414 | */ |
---|
| 415 | |
---|
| 416 | static int theFilesystemEpoch = 0; |
---|
| 417 | |
---|
| 418 | /* |
---|
| 419 | * Stores the linked list of filesystems. A 1:1 copy of this list is also |
---|
| 420 | * maintained in the TSD for each thread. This is to avoid synchronization |
---|
| 421 | * issues. |
---|
| 422 | */ |
---|
| 423 | |
---|
| 424 | static FilesystemRecord *filesystemList = &nativeFilesystemRecord; |
---|
| 425 | TCL_DECLARE_MUTEX(filesystemMutex) |
---|
| 426 | |
---|
| 427 | /* |
---|
| 428 | * Used to implement Tcl_FSGetCwd in a file-system independent way. |
---|
| 429 | */ |
---|
| 430 | |
---|
| 431 | static Tcl_Obj* cwdPathPtr = NULL; |
---|
| 432 | static int cwdPathEpoch = 0; |
---|
| 433 | static ClientData cwdClientData = NULL; |
---|
| 434 | TCL_DECLARE_MUTEX(cwdMutex) |
---|
| 435 | |
---|
| 436 | Tcl_ThreadDataKey tclFsDataKey; |
---|
| 437 | |
---|
| 438 | /* |
---|
| 439 | * Declare fallback support function and information for Tcl_FSLoadFile |
---|
| 440 | */ |
---|
| 441 | |
---|
| 442 | static Tcl_FSUnloadFileProc FSUnloadTempFile; |
---|
| 443 | |
---|
| 444 | /* |
---|
| 445 | * One of these structures is used each time we successfully load a file from |
---|
| 446 | * a file system by way of making a temporary copy of the file on the native |
---|
| 447 | * filesystem. We need to store both the actual unloadProc/clientData |
---|
| 448 | * combination which was used, and the original and modified filenames, so |
---|
| 449 | * that we can correctly undo the entire operation when we want to unload the |
---|
| 450 | * code. |
---|
| 451 | */ |
---|
| 452 | |
---|
| 453 | typedef struct FsDivertLoad { |
---|
| 454 | Tcl_LoadHandle loadHandle; |
---|
| 455 | Tcl_FSUnloadFileProc *unloadProcPtr; |
---|
| 456 | Tcl_Obj *divertedFile; |
---|
| 457 | const Tcl_Filesystem *divertedFilesystem; |
---|
| 458 | ClientData divertedFileNativeRep; |
---|
| 459 | } FsDivertLoad; |
---|
| 460 | |
---|
| 461 | /* |
---|
| 462 | * Now move on to the basic filesystem implementation |
---|
| 463 | */ |
---|
| 464 | |
---|
| 465 | static void |
---|
| 466 | FsThrExitProc( |
---|
| 467 | ClientData cd) |
---|
| 468 | { |
---|
| 469 | ThreadSpecificData *tsdPtr = (ThreadSpecificData *) cd; |
---|
| 470 | FilesystemRecord *fsRecPtr = NULL, *tmpFsRecPtr = NULL; |
---|
| 471 | |
---|
| 472 | /* |
---|
| 473 | * Trash the cwd copy. |
---|
| 474 | */ |
---|
| 475 | |
---|
| 476 | if (tsdPtr->cwdPathPtr != NULL) { |
---|
| 477 | Tcl_DecrRefCount(tsdPtr->cwdPathPtr); |
---|
| 478 | tsdPtr->cwdPathPtr = NULL; |
---|
| 479 | } |
---|
| 480 | if (tsdPtr->cwdClientData != NULL) { |
---|
| 481 | NativeFreeInternalRep(tsdPtr->cwdClientData); |
---|
| 482 | } |
---|
| 483 | |
---|
| 484 | /* |
---|
| 485 | * Trash the filesystems cache. |
---|
| 486 | */ |
---|
| 487 | |
---|
| 488 | fsRecPtr = tsdPtr->filesystemList; |
---|
| 489 | while (fsRecPtr != NULL) { |
---|
| 490 | tmpFsRecPtr = fsRecPtr->nextPtr; |
---|
| 491 | if (--fsRecPtr->fileRefCount <= 0) { |
---|
| 492 | ckfree((char *)fsRecPtr); |
---|
| 493 | } |
---|
| 494 | fsRecPtr = tmpFsRecPtr; |
---|
| 495 | } |
---|
| 496 | tsdPtr->initialized = 0; |
---|
| 497 | } |
---|
| 498 | |
---|
| 499 | int |
---|
| 500 | TclFSCwdIsNative(void) |
---|
| 501 | { |
---|
| 502 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); |
---|
| 503 | |
---|
| 504 | if (tsdPtr->cwdClientData != NULL) { |
---|
| 505 | return 1; |
---|
| 506 | } else { |
---|
| 507 | return 0; |
---|
| 508 | } |
---|
| 509 | } |
---|
| 510 | |
---|
| 511 | /* |
---|
| 512 | *---------------------------------------------------------------------- |
---|
| 513 | * |
---|
| 514 | * TclFSCwdPointerEquals -- |
---|
| 515 | * |
---|
| 516 | * Check whether the current working directory is equal to the path |
---|
| 517 | * given. |
---|
| 518 | * |
---|
| 519 | * Results: |
---|
| 520 | * 1 (equal) or 0 (un-equal) as appropriate. |
---|
| 521 | * |
---|
| 522 | * Side effects: |
---|
| 523 | * If the paths are equal, but are not the same object, this method will |
---|
| 524 | * modify the given pathPtrPtr to refer to the same object. In this case |
---|
| 525 | * the object pointed to by pathPtrPtr will have its refCount |
---|
| 526 | * decremented, and it will be adjusted to point to the cwd (with a new |
---|
| 527 | * refCount). |
---|
| 528 | * |
---|
| 529 | *---------------------------------------------------------------------- |
---|
| 530 | */ |
---|
| 531 | |
---|
| 532 | int |
---|
| 533 | TclFSCwdPointerEquals( |
---|
| 534 | Tcl_Obj** pathPtrPtr) |
---|
| 535 | { |
---|
| 536 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); |
---|
| 537 | |
---|
| 538 | Tcl_MutexLock(&cwdMutex); |
---|
| 539 | if (tsdPtr->cwdPathPtr == NULL |
---|
| 540 | || tsdPtr->cwdPathEpoch != cwdPathEpoch) { |
---|
| 541 | if (tsdPtr->cwdPathPtr != NULL) { |
---|
| 542 | Tcl_DecrRefCount(tsdPtr->cwdPathPtr); |
---|
| 543 | } |
---|
| 544 | if (tsdPtr->cwdClientData != NULL) { |
---|
| 545 | NativeFreeInternalRep(tsdPtr->cwdClientData); |
---|
| 546 | } |
---|
| 547 | if (cwdPathPtr == NULL) { |
---|
| 548 | tsdPtr->cwdPathPtr = NULL; |
---|
| 549 | } else { |
---|
| 550 | tsdPtr->cwdPathPtr = Tcl_DuplicateObj(cwdPathPtr); |
---|
| 551 | Tcl_IncrRefCount(tsdPtr->cwdPathPtr); |
---|
| 552 | } |
---|
| 553 | if (cwdClientData == NULL) { |
---|
| 554 | tsdPtr->cwdClientData = NULL; |
---|
| 555 | } else { |
---|
| 556 | tsdPtr->cwdClientData = TclNativeDupInternalRep(cwdClientData); |
---|
| 557 | } |
---|
| 558 | tsdPtr->cwdPathEpoch = cwdPathEpoch; |
---|
| 559 | } |
---|
| 560 | Tcl_MutexUnlock(&cwdMutex); |
---|
| 561 | |
---|
| 562 | if (tsdPtr->initialized == 0) { |
---|
| 563 | Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr); |
---|
| 564 | tsdPtr->initialized = 1; |
---|
| 565 | } |
---|
| 566 | |
---|
| 567 | if (pathPtrPtr == NULL) { |
---|
| 568 | return (tsdPtr->cwdPathPtr == NULL); |
---|
| 569 | } |
---|
| 570 | |
---|
| 571 | if (tsdPtr->cwdPathPtr == *pathPtrPtr) { |
---|
| 572 | return 1; |
---|
| 573 | } else { |
---|
| 574 | int len1, len2; |
---|
| 575 | const char *str1, *str2; |
---|
| 576 | |
---|
| 577 | str1 = Tcl_GetStringFromObj(tsdPtr->cwdPathPtr, &len1); |
---|
| 578 | str2 = Tcl_GetStringFromObj(*pathPtrPtr, &len2); |
---|
| 579 | if (len1 == len2 && !strcmp(str1,str2)) { |
---|
| 580 | /* |
---|
| 581 | * They are equal, but different objects. Update so they will be |
---|
| 582 | * the same object in the future. |
---|
| 583 | */ |
---|
| 584 | |
---|
| 585 | Tcl_DecrRefCount(*pathPtrPtr); |
---|
| 586 | *pathPtrPtr = tsdPtr->cwdPathPtr; |
---|
| 587 | Tcl_IncrRefCount(*pathPtrPtr); |
---|
| 588 | return 1; |
---|
| 589 | } else { |
---|
| 590 | return 0; |
---|
| 591 | } |
---|
| 592 | } |
---|
| 593 | } |
---|
| 594 | |
---|
| 595 | #ifdef TCL_THREADS |
---|
| 596 | static void |
---|
| 597 | FsRecacheFilesystemList(void) |
---|
| 598 | { |
---|
| 599 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); |
---|
| 600 | FilesystemRecord *fsRecPtr, *tmpFsRecPtr = NULL; |
---|
| 601 | |
---|
| 602 | /* |
---|
| 603 | * Trash the current cache. |
---|
| 604 | */ |
---|
| 605 | |
---|
| 606 | fsRecPtr = tsdPtr->filesystemList; |
---|
| 607 | while (fsRecPtr != NULL) { |
---|
| 608 | tmpFsRecPtr = fsRecPtr->nextPtr; |
---|
| 609 | if (--fsRecPtr->fileRefCount <= 0) { |
---|
| 610 | ckfree((char *)fsRecPtr); |
---|
| 611 | } |
---|
| 612 | fsRecPtr = tmpFsRecPtr; |
---|
| 613 | } |
---|
| 614 | tsdPtr->filesystemList = NULL; |
---|
| 615 | |
---|
| 616 | /* |
---|
| 617 | * Code below operates on shared data. We are already called under mutex |
---|
| 618 | * lock so we can safely proceed. |
---|
| 619 | * |
---|
| 620 | * Locate tail of the global filesystem list. |
---|
| 621 | */ |
---|
| 622 | |
---|
| 623 | fsRecPtr = filesystemList; |
---|
| 624 | while (fsRecPtr != NULL) { |
---|
| 625 | tmpFsRecPtr = fsRecPtr; |
---|
| 626 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 627 | } |
---|
| 628 | |
---|
| 629 | /* |
---|
| 630 | * Refill the cache honouring the order. |
---|
| 631 | */ |
---|
| 632 | |
---|
| 633 | fsRecPtr = tmpFsRecPtr; |
---|
| 634 | while (fsRecPtr != NULL) { |
---|
| 635 | tmpFsRecPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord)); |
---|
| 636 | *tmpFsRecPtr = *fsRecPtr; |
---|
| 637 | tmpFsRecPtr->nextPtr = tsdPtr->filesystemList; |
---|
| 638 | tmpFsRecPtr->prevPtr = NULL; |
---|
| 639 | if (tsdPtr->filesystemList) { |
---|
| 640 | tsdPtr->filesystemList->prevPtr = tmpFsRecPtr; |
---|
| 641 | } |
---|
| 642 | tsdPtr->filesystemList = tmpFsRecPtr; |
---|
| 643 | fsRecPtr = fsRecPtr->prevPtr; |
---|
| 644 | } |
---|
| 645 | |
---|
| 646 | /* |
---|
| 647 | * Make sure the above gets released on thread exit. |
---|
| 648 | */ |
---|
| 649 | |
---|
| 650 | if (tsdPtr->initialized == 0) { |
---|
| 651 | Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr); |
---|
| 652 | tsdPtr->initialized = 1; |
---|
| 653 | } |
---|
| 654 | } |
---|
| 655 | #endif /* TCL_THREADS */ |
---|
| 656 | |
---|
| 657 | static FilesystemRecord * |
---|
| 658 | FsGetFirstFilesystem(void) |
---|
| 659 | { |
---|
| 660 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); |
---|
| 661 | FilesystemRecord *fsRecPtr; |
---|
| 662 | #ifndef TCL_THREADS |
---|
| 663 | tsdPtr->filesystemEpoch = theFilesystemEpoch; |
---|
| 664 | fsRecPtr = filesystemList; |
---|
| 665 | #else |
---|
| 666 | Tcl_MutexLock(&filesystemMutex); |
---|
| 667 | if (tsdPtr->filesystemList == NULL |
---|
| 668 | || (tsdPtr->filesystemEpoch != theFilesystemEpoch)) { |
---|
| 669 | FsRecacheFilesystemList(); |
---|
| 670 | tsdPtr->filesystemEpoch = theFilesystemEpoch; |
---|
| 671 | } |
---|
| 672 | Tcl_MutexUnlock(&filesystemMutex); |
---|
| 673 | fsRecPtr = tsdPtr->filesystemList; |
---|
| 674 | #endif |
---|
| 675 | return fsRecPtr; |
---|
| 676 | } |
---|
| 677 | |
---|
| 678 | /* |
---|
| 679 | * The epoch can be changed both by filesystems being added or removed and by |
---|
| 680 | * env(HOME) changing. |
---|
| 681 | */ |
---|
| 682 | |
---|
| 683 | int |
---|
| 684 | TclFSEpochOk( |
---|
| 685 | int filesystemEpoch) |
---|
| 686 | { |
---|
| 687 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); |
---|
| 688 | (void) FsGetFirstFilesystem(); |
---|
| 689 | return (filesystemEpoch == tsdPtr->filesystemEpoch); |
---|
| 690 | } |
---|
| 691 | |
---|
| 692 | /* |
---|
| 693 | * If non-NULL, clientData is owned by us and must be freed later. |
---|
| 694 | */ |
---|
| 695 | |
---|
| 696 | static void |
---|
| 697 | FsUpdateCwd( |
---|
| 698 | Tcl_Obj *cwdObj, |
---|
| 699 | ClientData clientData) |
---|
| 700 | { |
---|
| 701 | int len; |
---|
| 702 | char *str = NULL; |
---|
| 703 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); |
---|
| 704 | |
---|
| 705 | if (cwdObj != NULL) { |
---|
| 706 | str = Tcl_GetStringFromObj(cwdObj, &len); |
---|
| 707 | } |
---|
| 708 | |
---|
| 709 | Tcl_MutexLock(&cwdMutex); |
---|
| 710 | if (cwdPathPtr != NULL) { |
---|
| 711 | Tcl_DecrRefCount(cwdPathPtr); |
---|
| 712 | } |
---|
| 713 | if (cwdClientData != NULL) { |
---|
| 714 | NativeFreeInternalRep(cwdClientData); |
---|
| 715 | } |
---|
| 716 | |
---|
| 717 | if (cwdObj == NULL) { |
---|
| 718 | cwdPathPtr = NULL; |
---|
| 719 | cwdClientData = NULL; |
---|
| 720 | } else { |
---|
| 721 | /* |
---|
| 722 | * This must be stored as string obj! |
---|
| 723 | */ |
---|
| 724 | |
---|
| 725 | cwdPathPtr = Tcl_NewStringObj(str, len); |
---|
| 726 | Tcl_IncrRefCount(cwdPathPtr); |
---|
| 727 | cwdClientData = TclNativeDupInternalRep(clientData); |
---|
| 728 | } |
---|
| 729 | |
---|
| 730 | cwdPathEpoch++; |
---|
| 731 | tsdPtr->cwdPathEpoch = cwdPathEpoch; |
---|
| 732 | Tcl_MutexUnlock(&cwdMutex); |
---|
| 733 | |
---|
| 734 | if (tsdPtr->cwdPathPtr) { |
---|
| 735 | Tcl_DecrRefCount(tsdPtr->cwdPathPtr); |
---|
| 736 | } |
---|
| 737 | if (tsdPtr->cwdClientData) { |
---|
| 738 | NativeFreeInternalRep(tsdPtr->cwdClientData); |
---|
| 739 | } |
---|
| 740 | |
---|
| 741 | if (cwdObj == NULL) { |
---|
| 742 | tsdPtr->cwdPathPtr = NULL; |
---|
| 743 | tsdPtr->cwdClientData = NULL; |
---|
| 744 | } else { |
---|
| 745 | tsdPtr->cwdPathPtr = Tcl_NewStringObj(str, len); |
---|
| 746 | tsdPtr->cwdClientData = clientData; |
---|
| 747 | Tcl_IncrRefCount(tsdPtr->cwdPathPtr); |
---|
| 748 | } |
---|
| 749 | } |
---|
| 750 | |
---|
| 751 | /* |
---|
| 752 | *---------------------------------------------------------------------- |
---|
| 753 | * |
---|
| 754 | * TclFinalizeFilesystem -- |
---|
| 755 | * |
---|
| 756 | * Clean up the filesystem. After this, calls to all Tcl_FS... functions |
---|
| 757 | * will fail. |
---|
| 758 | * |
---|
| 759 | * We will later call TclResetFilesystem to restore the FS to a pristine |
---|
| 760 | * state. |
---|
| 761 | * |
---|
| 762 | * Results: |
---|
| 763 | * None. |
---|
| 764 | * |
---|
| 765 | * Side effects: |
---|
| 766 | * Frees any memory allocated by the filesystem. |
---|
| 767 | * |
---|
| 768 | *---------------------------------------------------------------------- |
---|
| 769 | */ |
---|
| 770 | |
---|
| 771 | void |
---|
| 772 | TclFinalizeFilesystem(void) |
---|
| 773 | { |
---|
| 774 | FilesystemRecord *fsRecPtr; |
---|
| 775 | |
---|
| 776 | /* |
---|
| 777 | * Assumption that only one thread is active now. Otherwise we would need |
---|
| 778 | * to put various mutexes around this code. |
---|
| 779 | */ |
---|
| 780 | |
---|
| 781 | if (cwdPathPtr != NULL) { |
---|
| 782 | Tcl_DecrRefCount(cwdPathPtr); |
---|
| 783 | cwdPathPtr = NULL; |
---|
| 784 | cwdPathEpoch = 0; |
---|
| 785 | } |
---|
| 786 | if (cwdClientData != NULL) { |
---|
| 787 | NativeFreeInternalRep(cwdClientData); |
---|
| 788 | cwdClientData = NULL; |
---|
| 789 | } |
---|
| 790 | |
---|
| 791 | /* |
---|
| 792 | * Remove all filesystems, freeing any allocated memory that is no longer |
---|
| 793 | * needed |
---|
| 794 | */ |
---|
| 795 | |
---|
| 796 | fsRecPtr = filesystemList; |
---|
| 797 | while (fsRecPtr != NULL) { |
---|
| 798 | FilesystemRecord *tmpFsRecPtr = fsRecPtr->nextPtr; |
---|
| 799 | if (fsRecPtr->fileRefCount <= 0) { |
---|
| 800 | /* |
---|
| 801 | * The native filesystem is static, so we don't free it. |
---|
| 802 | */ |
---|
| 803 | |
---|
| 804 | if (fsRecPtr->fsPtr != &tclNativeFilesystem) { |
---|
| 805 | ckfree((char *)fsRecPtr); |
---|
| 806 | } |
---|
| 807 | } |
---|
| 808 | fsRecPtr = tmpFsRecPtr; |
---|
| 809 | } |
---|
| 810 | filesystemList = NULL; |
---|
| 811 | |
---|
| 812 | /* |
---|
| 813 | * Now filesystemList is NULL. This means that any attempt to use the |
---|
| 814 | * filesystem is likely to fail. |
---|
| 815 | */ |
---|
| 816 | |
---|
| 817 | #ifdef USE_OBSOLETE_FS_HOOKS |
---|
| 818 | statProcList = NULL; |
---|
| 819 | accessProcList = NULL; |
---|
| 820 | openFileChannelProcList = NULL; |
---|
| 821 | #endif |
---|
| 822 | #ifdef __WIN32__ |
---|
| 823 | TclWinEncodingsCleanup(); |
---|
| 824 | #endif |
---|
| 825 | } |
---|
| 826 | |
---|
| 827 | /* |
---|
| 828 | *---------------------------------------------------------------------- |
---|
| 829 | * |
---|
| 830 | * TclResetFilesystem -- |
---|
| 831 | * |
---|
| 832 | * Restore the filesystem to a pristine state. |
---|
| 833 | * |
---|
| 834 | * Results: |
---|
| 835 | * None. |
---|
| 836 | * |
---|
| 837 | * Side effects: |
---|
| 838 | * None. |
---|
| 839 | * |
---|
| 840 | *---------------------------------------------------------------------- |
---|
| 841 | */ |
---|
| 842 | |
---|
| 843 | void |
---|
| 844 | TclResetFilesystem(void) |
---|
| 845 | { |
---|
| 846 | filesystemList = &nativeFilesystemRecord; |
---|
| 847 | |
---|
| 848 | /* |
---|
| 849 | * Note, at this point, I believe nativeFilesystemRecord -> fileRefCount |
---|
| 850 | * should equal 1 and if not, we should try to track down the cause. |
---|
| 851 | */ |
---|
| 852 | |
---|
| 853 | #ifdef __WIN32__ |
---|
| 854 | /* |
---|
| 855 | * Cleans up the win32 API filesystem proc lookup table. This must happen |
---|
| 856 | * very late in finalization so that deleting of copied dlls can occur. |
---|
| 857 | */ |
---|
| 858 | |
---|
| 859 | TclWinResetInterfaces(); |
---|
| 860 | #endif |
---|
| 861 | } |
---|
| 862 | |
---|
| 863 | /* |
---|
| 864 | *---------------------------------------------------------------------- |
---|
| 865 | * |
---|
| 866 | * Tcl_FSRegister -- |
---|
| 867 | * |
---|
| 868 | * Insert the filesystem function table at the head of the list of |
---|
| 869 | * functions which are used during calls to all file-system operations. |
---|
| 870 | * The filesystem will be added even if it is already in the list. (You |
---|
| 871 | * can use Tcl_FSData to check if it is in the list, provided the |
---|
| 872 | * ClientData used was not NULL). |
---|
| 873 | * |
---|
| 874 | * Note that the filesystem handling is head-to-tail of the list. Each |
---|
| 875 | * filesystem is asked in turn whether it can handle a particular |
---|
| 876 | * request, until one of them says 'yes'. At that point no further |
---|
| 877 | * filesystems are asked. |
---|
| 878 | * |
---|
| 879 | * In particular this means if you want to add a diagnostic filesystem |
---|
| 880 | * (which simply reports all fs activity), it must be at the head of the |
---|
| 881 | * list: i.e. it must be the last registered. |
---|
| 882 | * |
---|
| 883 | * Results: |
---|
| 884 | * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could |
---|
| 885 | * not be allocated. |
---|
| 886 | * |
---|
| 887 | * Side effects: |
---|
| 888 | * Memory allocated and modifies the link list for filesystems. |
---|
| 889 | * |
---|
| 890 | *---------------------------------------------------------------------- |
---|
| 891 | */ |
---|
| 892 | |
---|
| 893 | int |
---|
| 894 | Tcl_FSRegister( |
---|
| 895 | ClientData clientData, /* Client specific data for this fs */ |
---|
| 896 | Tcl_Filesystem *fsPtr) /* The filesystem record for the new fs. */ |
---|
| 897 | { |
---|
| 898 | FilesystemRecord *newFilesystemPtr; |
---|
| 899 | |
---|
| 900 | if (fsPtr == NULL) { |
---|
| 901 | return TCL_ERROR; |
---|
| 902 | } |
---|
| 903 | |
---|
| 904 | newFilesystemPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord)); |
---|
| 905 | |
---|
| 906 | newFilesystemPtr->clientData = clientData; |
---|
| 907 | newFilesystemPtr->fsPtr = fsPtr; |
---|
| 908 | |
---|
| 909 | /* |
---|
| 910 | * We start with a refCount of 1. If this drops to zero, then anyone is |
---|
| 911 | * welcome to ckfree us. |
---|
| 912 | */ |
---|
| 913 | |
---|
| 914 | newFilesystemPtr->fileRefCount = 1; |
---|
| 915 | |
---|
| 916 | /* |
---|
| 917 | * Is this lock and wait strictly speaking necessary? Since any iterators |
---|
| 918 | * out there will have grabbed a copy of the head of the list and be |
---|
| 919 | * iterating away from that, if we add a new element to the head of the |
---|
| 920 | * list, it can't possibly have any effect on any of their loops. In fact |
---|
| 921 | * it could be better not to wait, since we are adjusting the filesystem |
---|
| 922 | * epoch, any cached representations calculated by existing iterators are |
---|
| 923 | * going to have to be thrown away anyway. |
---|
| 924 | * |
---|
| 925 | * However, since registering and unregistering filesystems is a very rare |
---|
| 926 | * action, this is not a very important point. |
---|
| 927 | */ |
---|
| 928 | |
---|
| 929 | Tcl_MutexLock(&filesystemMutex); |
---|
| 930 | |
---|
| 931 | newFilesystemPtr->nextPtr = filesystemList; |
---|
| 932 | newFilesystemPtr->prevPtr = NULL; |
---|
| 933 | if (filesystemList) { |
---|
| 934 | filesystemList->prevPtr = newFilesystemPtr; |
---|
| 935 | } |
---|
| 936 | filesystemList = newFilesystemPtr; |
---|
| 937 | |
---|
| 938 | /* |
---|
| 939 | * Increment the filesystem epoch counter, since existing paths might |
---|
| 940 | * conceivably now belong to different filesystems. |
---|
| 941 | */ |
---|
| 942 | |
---|
| 943 | theFilesystemEpoch++; |
---|
| 944 | Tcl_MutexUnlock(&filesystemMutex); |
---|
| 945 | |
---|
| 946 | return TCL_OK; |
---|
| 947 | } |
---|
| 948 | |
---|
| 949 | /* |
---|
| 950 | *---------------------------------------------------------------------- |
---|
| 951 | * |
---|
| 952 | * Tcl_FSUnregister -- |
---|
| 953 | * |
---|
| 954 | * Remove the passed filesystem from the list of filesystem function |
---|
| 955 | * tables. It also ensures that the built-in (native) filesystem is not |
---|
| 956 | * removable, although we may wish to change that decision in the future |
---|
| 957 | * to allow a smaller Tcl core, in which the native filesystem is not |
---|
| 958 | * used at all (we could, say, initialise Tcl completely over a network |
---|
| 959 | * connection). |
---|
| 960 | * |
---|
| 961 | * Results: |
---|
| 962 | * TCL_OK if the function pointer was successfully removed, TCL_ERROR |
---|
| 963 | * otherwise. |
---|
| 964 | * |
---|
| 965 | * Side effects: |
---|
| 966 | * Memory may be deallocated (or will be later, once no "path" objects |
---|
| 967 | * refer to this filesystem), but the list of registered filesystems is |
---|
| 968 | * updated immediately. |
---|
| 969 | * |
---|
| 970 | *---------------------------------------------------------------------- |
---|
| 971 | */ |
---|
| 972 | |
---|
| 973 | int |
---|
| 974 | Tcl_FSUnregister( |
---|
| 975 | Tcl_Filesystem *fsPtr) /* The filesystem record to remove. */ |
---|
| 976 | { |
---|
| 977 | int retVal = TCL_ERROR; |
---|
| 978 | FilesystemRecord *fsRecPtr; |
---|
| 979 | |
---|
| 980 | Tcl_MutexLock(&filesystemMutex); |
---|
| 981 | |
---|
| 982 | /* |
---|
| 983 | * Traverse the 'filesystemList' looking for the particular node whose |
---|
| 984 | * 'fsPtr' member matches 'fsPtr' and remove that one from the list. |
---|
| 985 | * Ensure that the "default" node cannot be removed. |
---|
| 986 | */ |
---|
| 987 | |
---|
| 988 | fsRecPtr = filesystemList; |
---|
| 989 | while ((retVal == TCL_ERROR) && (fsRecPtr->fsPtr != &tclNativeFilesystem)) { |
---|
| 990 | if (fsRecPtr->fsPtr == fsPtr) { |
---|
| 991 | if (fsRecPtr->prevPtr) { |
---|
| 992 | fsRecPtr->prevPtr->nextPtr = fsRecPtr->nextPtr; |
---|
| 993 | } else { |
---|
| 994 | filesystemList = fsRecPtr->nextPtr; |
---|
| 995 | } |
---|
| 996 | if (fsRecPtr->nextPtr) { |
---|
| 997 | fsRecPtr->nextPtr->prevPtr = fsRecPtr->prevPtr; |
---|
| 998 | } |
---|
| 999 | |
---|
| 1000 | /* |
---|
| 1001 | * Increment the filesystem epoch counter, since existing paths |
---|
| 1002 | * might conceivably now belong to different filesystems. This |
---|
| 1003 | * should also ensure that paths which have cached the filesystem |
---|
| 1004 | * which is about to be deleted do not reference that filesystem |
---|
| 1005 | * (which would of course lead to memory exceptions). |
---|
| 1006 | */ |
---|
| 1007 | |
---|
| 1008 | theFilesystemEpoch++; |
---|
| 1009 | |
---|
| 1010 | fsRecPtr->fileRefCount--; |
---|
| 1011 | if (fsRecPtr->fileRefCount <= 0) { |
---|
| 1012 | ckfree((char *)fsRecPtr); |
---|
| 1013 | } |
---|
| 1014 | |
---|
| 1015 | retVal = TCL_OK; |
---|
| 1016 | } else { |
---|
| 1017 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 1018 | } |
---|
| 1019 | } |
---|
| 1020 | |
---|
| 1021 | Tcl_MutexUnlock(&filesystemMutex); |
---|
| 1022 | return retVal; |
---|
| 1023 | } |
---|
| 1024 | |
---|
| 1025 | /* |
---|
| 1026 | *---------------------------------------------------------------------- |
---|
| 1027 | * |
---|
| 1028 | * Tcl_FSMatchInDirectory -- |
---|
| 1029 | * |
---|
| 1030 | * This routine is used by the globbing code to search a directory for |
---|
| 1031 | * all files which match a given pattern. The appropriate function for |
---|
| 1032 | * the filesystem to which pathPtr belongs will be called. If pathPtr |
---|
| 1033 | * does not belong to any filesystem and if it is NULL or the empty |
---|
| 1034 | * string, then we assume the pattern is to be matched in the current |
---|
| 1035 | * working directory. To avoid have the Tcl_FSMatchInDirectoryProc for |
---|
| 1036 | * each filesystem from having to deal with this issue, we create a |
---|
| 1037 | * pathPtr on the fly (equal to the cwd), and then remove it from the |
---|
| 1038 | * results returned. This makes filesystems easy to write, since they can |
---|
| 1039 | * assume the pathPtr passed to them is an ordinary path. In fact this |
---|
| 1040 | * means we could remove such special case handling from Tcl's native |
---|
| 1041 | * filesystems. |
---|
| 1042 | * |
---|
| 1043 | * If 'pattern' is NULL, then pathPtr is assumed to be a fully specified |
---|
| 1044 | * path of a single file/directory which must be checked for existence |
---|
| 1045 | * and correct type. |
---|
| 1046 | * |
---|
| 1047 | * Results: |
---|
| 1048 | * |
---|
| 1049 | * The return value is a standard Tcl result indicating whether an error |
---|
| 1050 | * occurred in globbing. Error messages are placed in interp, but good |
---|
| 1051 | * results are placed in the resultPtr given. |
---|
| 1052 | * |
---|
| 1053 | * Recursive searches, e.g. |
---|
| 1054 | * glob -dir $dir -join * pkgIndex.tcl |
---|
| 1055 | * which must recurse through each directory matching '*' are handled |
---|
| 1056 | * internally by Tcl, by passing specific flags in a modified 'types' |
---|
| 1057 | * parameter. This means the actual filesystem only ever sees patterns |
---|
| 1058 | * which match in a single directory. |
---|
| 1059 | * |
---|
| 1060 | * Side effects: |
---|
| 1061 | * The interpreter may have an error message inserted into it. |
---|
| 1062 | * |
---|
| 1063 | *---------------------------------------------------------------------- |
---|
| 1064 | */ |
---|
| 1065 | |
---|
| 1066 | int |
---|
| 1067 | Tcl_FSMatchInDirectory( |
---|
| 1068 | Tcl_Interp *interp, /* Interpreter to receive error messages, but |
---|
| 1069 | * may be NULL. */ |
---|
| 1070 | Tcl_Obj *resultPtr, /* List object to receive results. */ |
---|
| 1071 | Tcl_Obj *pathPtr, /* Contains path to directory to search. */ |
---|
| 1072 | const char *pattern, /* Pattern to match against. */ |
---|
| 1073 | Tcl_GlobTypeData *types) /* Object containing list of acceptable types. |
---|
| 1074 | * May be NULL. In particular the directory |
---|
| 1075 | * flag is very important. */ |
---|
| 1076 | { |
---|
| 1077 | const Tcl_Filesystem *fsPtr; |
---|
| 1078 | Tcl_Obj *cwd, *tmpResultPtr, **elemsPtr; |
---|
| 1079 | int resLength, i, ret = -1; |
---|
| 1080 | |
---|
| 1081 | if (types != NULL && types->type & TCL_GLOB_TYPE_MOUNT) { |
---|
| 1082 | /* |
---|
| 1083 | * We don't currently allow querying of mounts by external code (a |
---|
| 1084 | * valuable future step), so since we're the only function that |
---|
| 1085 | * actually knows about mounts, this means we're being called |
---|
| 1086 | * recursively by ourself. Return no matches. |
---|
| 1087 | */ |
---|
| 1088 | |
---|
| 1089 | return TCL_OK; |
---|
| 1090 | } |
---|
| 1091 | |
---|
| 1092 | if (pathPtr != NULL) { |
---|
| 1093 | fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 1094 | } else { |
---|
| 1095 | fsPtr = NULL; |
---|
| 1096 | } |
---|
| 1097 | |
---|
| 1098 | /* |
---|
| 1099 | * Check if we've successfully mapped the path to a filesystem within |
---|
| 1100 | * which to search. |
---|
| 1101 | */ |
---|
| 1102 | |
---|
| 1103 | if (fsPtr != NULL) { |
---|
| 1104 | if (fsPtr->matchInDirectoryProc == NULL) { |
---|
| 1105 | Tcl_SetErrno(ENOENT); |
---|
| 1106 | return -1; |
---|
| 1107 | } |
---|
| 1108 | ret = (*fsPtr->matchInDirectoryProc)(interp, resultPtr, pathPtr, |
---|
| 1109 | pattern, types); |
---|
| 1110 | if (ret == TCL_OK && pattern != NULL) { |
---|
| 1111 | FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types); |
---|
| 1112 | } |
---|
| 1113 | return ret; |
---|
| 1114 | } |
---|
| 1115 | |
---|
| 1116 | /* |
---|
| 1117 | * If the path isn't empty, we have no idea how to match files in a |
---|
| 1118 | * directory which belongs to no known filesystem |
---|
| 1119 | */ |
---|
| 1120 | |
---|
| 1121 | if (pathPtr != NULL && TclGetString(pathPtr)[0] != '\0') { |
---|
| 1122 | Tcl_SetErrno(ENOENT); |
---|
| 1123 | return -1; |
---|
| 1124 | } |
---|
| 1125 | |
---|
| 1126 | /* |
---|
| 1127 | * We have an empty or NULL path. This is defined to mean we must search |
---|
| 1128 | * for files within the current 'cwd'. We therefore use that, but then |
---|
| 1129 | * since the proc we call will return results which include the cwd we |
---|
| 1130 | * must then trim it off the front of each path in the result. We choose |
---|
| 1131 | * to deal with this here (in the generic code), since if we don't, every |
---|
| 1132 | * single filesystem's implementation of Tcl_FSMatchInDirectory will have |
---|
| 1133 | * to deal with it for us. |
---|
| 1134 | */ |
---|
| 1135 | |
---|
| 1136 | cwd = Tcl_FSGetCwd(NULL); |
---|
| 1137 | if (cwd == NULL) { |
---|
| 1138 | if (interp != NULL) { |
---|
| 1139 | Tcl_SetResult(interp, "glob couldn't determine " |
---|
| 1140 | "the current working directory", TCL_STATIC); |
---|
| 1141 | } |
---|
| 1142 | return TCL_ERROR; |
---|
| 1143 | } |
---|
| 1144 | |
---|
| 1145 | fsPtr = Tcl_FSGetFileSystemForPath(cwd); |
---|
| 1146 | if (fsPtr != NULL && fsPtr->matchInDirectoryProc != NULL) { |
---|
| 1147 | TclNewObj(tmpResultPtr); |
---|
| 1148 | Tcl_IncrRefCount(tmpResultPtr); |
---|
| 1149 | ret = (*fsPtr->matchInDirectoryProc)(interp, tmpResultPtr, cwd, |
---|
| 1150 | pattern, types); |
---|
| 1151 | if (ret == TCL_OK) { |
---|
| 1152 | FsAddMountsToGlobResult(tmpResultPtr, cwd, pattern, types); |
---|
| 1153 | |
---|
| 1154 | /* |
---|
| 1155 | * Note that we know resultPtr and tmpResultPtr are distinct. |
---|
| 1156 | */ |
---|
| 1157 | |
---|
| 1158 | ret = Tcl_ListObjGetElements(interp, tmpResultPtr, |
---|
| 1159 | &resLength, &elemsPtr); |
---|
| 1160 | for (i=0 ; ret==TCL_OK && i<resLength ; i++) { |
---|
| 1161 | ret = Tcl_ListObjAppendElement(interp, resultPtr, |
---|
| 1162 | TclFSMakePathRelative(interp, elemsPtr[i], cwd)); |
---|
| 1163 | } |
---|
| 1164 | } |
---|
| 1165 | TclDecrRefCount(tmpResultPtr); |
---|
| 1166 | } |
---|
| 1167 | Tcl_DecrRefCount(cwd); |
---|
| 1168 | return ret; |
---|
| 1169 | } |
---|
| 1170 | |
---|
| 1171 | /* |
---|
| 1172 | *---------------------------------------------------------------------- |
---|
| 1173 | * |
---|
| 1174 | * FsAddMountsToGlobResult -- |
---|
| 1175 | * |
---|
| 1176 | * This routine is used by the globbing code to take the results of a |
---|
| 1177 | * directory listing and add any mounted paths to that listing. This is |
---|
| 1178 | * required so that simple things like 'glob *' merge mounts and listings |
---|
| 1179 | * correctly. |
---|
| 1180 | * |
---|
| 1181 | * Results: |
---|
| 1182 | * None. |
---|
| 1183 | * |
---|
| 1184 | * Side effects: |
---|
| 1185 | * Modifies the resultPtr. |
---|
| 1186 | * |
---|
| 1187 | *---------------------------------------------------------------------- |
---|
| 1188 | */ |
---|
| 1189 | |
---|
| 1190 | static void |
---|
| 1191 | FsAddMountsToGlobResult( |
---|
| 1192 | Tcl_Obj *resultPtr, /* The current list of matching paths; must |
---|
| 1193 | * not be shared! */ |
---|
| 1194 | Tcl_Obj *pathPtr, /* The directory in question */ |
---|
| 1195 | const char *pattern, /* Pattern to match against. */ |
---|
| 1196 | Tcl_GlobTypeData *types) /* Object containing list of acceptable types. |
---|
| 1197 | * May be NULL. In particular the directory |
---|
| 1198 | * flag is very important. */ |
---|
| 1199 | { |
---|
| 1200 | int mLength, gLength, i; |
---|
| 1201 | int dir = (types == NULL || (types->type & TCL_GLOB_TYPE_DIR)); |
---|
| 1202 | Tcl_Obj *mounts = FsListMounts(pathPtr, pattern); |
---|
| 1203 | |
---|
| 1204 | if (mounts == NULL) { |
---|
| 1205 | return; |
---|
| 1206 | } |
---|
| 1207 | |
---|
| 1208 | if (Tcl_ListObjLength(NULL, mounts, &mLength) != TCL_OK || mLength == 0) { |
---|
| 1209 | goto endOfMounts; |
---|
| 1210 | } |
---|
| 1211 | if (Tcl_ListObjLength(NULL, resultPtr, &gLength) != TCL_OK) { |
---|
| 1212 | goto endOfMounts; |
---|
| 1213 | } |
---|
| 1214 | for (i=0 ; i<mLength ; i++) { |
---|
| 1215 | Tcl_Obj *mElt; |
---|
| 1216 | int j; |
---|
| 1217 | int found = 0; |
---|
| 1218 | |
---|
| 1219 | Tcl_ListObjIndex(NULL, mounts, i, &mElt); |
---|
| 1220 | |
---|
| 1221 | for (j=0 ; j<gLength ; j++) { |
---|
| 1222 | Tcl_Obj *gElt; |
---|
| 1223 | |
---|
| 1224 | Tcl_ListObjIndex(NULL, resultPtr, j, &gElt); |
---|
| 1225 | if (Tcl_FSEqualPaths(mElt, gElt)) { |
---|
| 1226 | found = 1; |
---|
| 1227 | if (!dir) { |
---|
| 1228 | /* |
---|
| 1229 | * We don't want to list this. |
---|
| 1230 | */ |
---|
| 1231 | |
---|
| 1232 | Tcl_ListObjReplace(NULL, resultPtr, j, 1, 0, NULL); |
---|
| 1233 | gLength--; |
---|
| 1234 | } |
---|
| 1235 | break; /* Break out of for loop */ |
---|
| 1236 | } |
---|
| 1237 | } |
---|
| 1238 | if (!found && dir) { |
---|
| 1239 | Tcl_Obj *norm; |
---|
| 1240 | int len, mlen; |
---|
| 1241 | |
---|
| 1242 | /* |
---|
| 1243 | * We know mElt is absolute normalized and lies inside pathPtr, so |
---|
| 1244 | * now we must add to the result the right representation of mElt, |
---|
| 1245 | * i.e. the representation which is relative to pathPtr. |
---|
| 1246 | */ |
---|
| 1247 | |
---|
| 1248 | norm = Tcl_FSGetNormalizedPath(NULL, pathPtr); |
---|
| 1249 | if (norm != NULL) { |
---|
| 1250 | const char *path, *mount; |
---|
| 1251 | |
---|
| 1252 | mount = Tcl_GetStringFromObj(mElt, &mlen); |
---|
| 1253 | path = Tcl_GetStringFromObj(norm, &len); |
---|
| 1254 | if (path[len-1] == '/') { |
---|
| 1255 | /* |
---|
| 1256 | * Deal with the root of the volume. |
---|
| 1257 | */ |
---|
| 1258 | |
---|
| 1259 | len--; |
---|
| 1260 | } |
---|
| 1261 | len++; /* account for '/' in the mElt [Bug 1602539] */ |
---|
| 1262 | mElt = TclNewFSPathObj(pathPtr, mount + len, mlen - len); |
---|
| 1263 | Tcl_ListObjAppendElement(NULL, resultPtr, mElt); |
---|
| 1264 | } |
---|
| 1265 | /* |
---|
| 1266 | * No need to increment gLength, since we don't want to compare |
---|
| 1267 | * mounts against mounts. |
---|
| 1268 | */ |
---|
| 1269 | } |
---|
| 1270 | } |
---|
| 1271 | |
---|
| 1272 | endOfMounts: |
---|
| 1273 | Tcl_DecrRefCount(mounts); |
---|
| 1274 | } |
---|
| 1275 | |
---|
| 1276 | /* |
---|
| 1277 | *---------------------------------------------------------------------- |
---|
| 1278 | * |
---|
| 1279 | * Tcl_FSMountsChanged -- |
---|
| 1280 | * |
---|
| 1281 | * Notify the filesystem that the available mounted filesystems (or |
---|
| 1282 | * within any one filesystem type, the number or location of mount |
---|
| 1283 | * points) have changed. |
---|
| 1284 | * |
---|
| 1285 | * Results: |
---|
| 1286 | * None. |
---|
| 1287 | * |
---|
| 1288 | * Side effects: |
---|
| 1289 | * The global filesystem variable 'theFilesystemEpoch' is incremented. |
---|
| 1290 | * The effect of this is to make all cached path representations invalid. |
---|
| 1291 | * Clearly it should only therefore be called when it is really required! |
---|
| 1292 | * There are a few circumstances when it should be called: |
---|
| 1293 | * |
---|
| 1294 | * (1) when a new filesystem is registered or unregistered. Strictly |
---|
| 1295 | * speaking this is only necessary if the new filesystem accepts file |
---|
| 1296 | * paths as is (normally the filesystem itself is really a shell which |
---|
| 1297 | * hasn't yet had any mount points established and so its |
---|
| 1298 | * 'pathInFilesystem' proc will always fail). However, for safety, Tcl |
---|
| 1299 | * always calls this for you in these circumstances. |
---|
| 1300 | * |
---|
| 1301 | * (2) when additional mount points are established inside any existing |
---|
| 1302 | * filesystem (except the native fs) |
---|
| 1303 | * |
---|
| 1304 | * (3) when any filesystem (except the native fs) changes the list of |
---|
| 1305 | * available volumes. |
---|
| 1306 | * |
---|
| 1307 | * (4) when the mapping from a string representation of a file to a full, |
---|
| 1308 | * normalized path changes. For example, if 'env(HOME)' is modified, then |
---|
| 1309 | * any path containing '~' will map to a different filesystem location. |
---|
| 1310 | * Therefore all such paths need to have their internal representation |
---|
| 1311 | * invalidated. |
---|
| 1312 | * |
---|
| 1313 | * Tcl has no control over (2) and (3), so any registered filesystem must |
---|
| 1314 | * make sure it calls this function when those situations occur. |
---|
| 1315 | * |
---|
| 1316 | * (Note: the reason for the exception in 2,3 for the native filesystem |
---|
| 1317 | * is that the native filesystem by default claims all unknown files even |
---|
| 1318 | * if it really doesn't understand them or if they don't exist). |
---|
| 1319 | * |
---|
| 1320 | *---------------------------------------------------------------------- |
---|
| 1321 | */ |
---|
| 1322 | |
---|
| 1323 | void |
---|
| 1324 | Tcl_FSMountsChanged( |
---|
| 1325 | Tcl_Filesystem *fsPtr) |
---|
| 1326 | { |
---|
| 1327 | /* |
---|
| 1328 | * We currently don't do anything with this parameter. We could in the |
---|
| 1329 | * future only invalidate files for this filesystem or otherwise take more |
---|
| 1330 | * advanced action. |
---|
| 1331 | */ |
---|
| 1332 | |
---|
| 1333 | (void)fsPtr; |
---|
| 1334 | |
---|
| 1335 | /* |
---|
| 1336 | * Increment the filesystem epoch counter, since existing paths might now |
---|
| 1337 | * belong to different filesystems. |
---|
| 1338 | */ |
---|
| 1339 | |
---|
| 1340 | Tcl_MutexLock(&filesystemMutex); |
---|
| 1341 | theFilesystemEpoch++; |
---|
| 1342 | Tcl_MutexUnlock(&filesystemMutex); |
---|
| 1343 | } |
---|
| 1344 | |
---|
| 1345 | /* |
---|
| 1346 | *---------------------------------------------------------------------- |
---|
| 1347 | * |
---|
| 1348 | * Tcl_FSData -- |
---|
| 1349 | * |
---|
| 1350 | * Retrieve the clientData field for the filesystem given, or NULL if |
---|
| 1351 | * that filesystem is not registered. |
---|
| 1352 | * |
---|
| 1353 | * Results: |
---|
| 1354 | * A clientData value, or NULL. Note that if the filesystem was |
---|
| 1355 | * registered with a NULL clientData field, this function will return |
---|
| 1356 | * that NULL value. |
---|
| 1357 | * |
---|
| 1358 | * Side effects: |
---|
| 1359 | * None. |
---|
| 1360 | * |
---|
| 1361 | *---------------------------------------------------------------------- |
---|
| 1362 | */ |
---|
| 1363 | |
---|
| 1364 | ClientData |
---|
| 1365 | Tcl_FSData( |
---|
| 1366 | Tcl_Filesystem *fsPtr) /* The filesystem record to query. */ |
---|
| 1367 | { |
---|
| 1368 | ClientData retVal = NULL; |
---|
| 1369 | FilesystemRecord *fsRecPtr = FsGetFirstFilesystem(); |
---|
| 1370 | |
---|
| 1371 | /* |
---|
| 1372 | * Traverse the list of filesystems look for a particular one. If found, |
---|
| 1373 | * return that filesystem's clientData (originally provided when calling |
---|
| 1374 | * Tcl_FSRegister). |
---|
| 1375 | */ |
---|
| 1376 | |
---|
| 1377 | while ((retVal == NULL) && (fsRecPtr != NULL)) { |
---|
| 1378 | if (fsRecPtr->fsPtr == fsPtr) { |
---|
| 1379 | retVal = fsRecPtr->clientData; |
---|
| 1380 | } |
---|
| 1381 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 1382 | } |
---|
| 1383 | |
---|
| 1384 | return retVal; |
---|
| 1385 | } |
---|
| 1386 | |
---|
| 1387 | /* |
---|
| 1388 | *--------------------------------------------------------------------------- |
---|
| 1389 | * |
---|
| 1390 | * TclFSNormalizeToUniquePath -- |
---|
| 1391 | * |
---|
| 1392 | * Takes a path specification containing no ../, ./ sequences, and |
---|
| 1393 | * converts it into a unique path for the given platform. On Unix, this |
---|
| 1394 | * means the path must be free of symbolic links/aliases, and on Windows |
---|
| 1395 | * it means we want the long form, with that long form's case-dependence |
---|
| 1396 | * (which gives us a unique, case-dependent path). |
---|
| 1397 | * |
---|
| 1398 | * Results: |
---|
| 1399 | * The pathPtr is modified in place. The return value is the last byte |
---|
| 1400 | * offset which was recognised in the path string. |
---|
| 1401 | * |
---|
| 1402 | * Side effects: |
---|
| 1403 | * None (beyond the memory allocation for the result). |
---|
| 1404 | * |
---|
| 1405 | * Special notes: |
---|
| 1406 | * If the filesystem-specific normalizePathProcs can re-introduce ../, ./ |
---|
| 1407 | * sequences into the path, then this function will not return the |
---|
| 1408 | * correct result. This may be possible with symbolic links on unix. |
---|
| 1409 | * |
---|
| 1410 | * Important assumption: if startAt is non-zero, it must point to a |
---|
| 1411 | * directory separator that we know exists and is already normalized (so |
---|
| 1412 | * it is important not to point to the char just after the separator). |
---|
| 1413 | * |
---|
| 1414 | *--------------------------------------------------------------------------- |
---|
| 1415 | */ |
---|
| 1416 | |
---|
| 1417 | int |
---|
| 1418 | TclFSNormalizeToUniquePath( |
---|
| 1419 | Tcl_Interp *interp, /* Used for error messages. */ |
---|
| 1420 | Tcl_Obj *pathPtr, /* The path to normalize in place */ |
---|
| 1421 | int startAt, /* Start at this char-offset */ |
---|
| 1422 | ClientData *clientDataPtr) /* If we generated a complete normalized path |
---|
| 1423 | * for a given filesystem, we can optionally |
---|
| 1424 | * return an fs-specific clientdata here. */ |
---|
| 1425 | { |
---|
| 1426 | FilesystemRecord *fsRecPtr, *firstFsRecPtr; |
---|
| 1427 | /* Ignore this variable */ |
---|
| 1428 | (void) clientDataPtr; |
---|
| 1429 | |
---|
| 1430 | /* |
---|
| 1431 | * Call each of the "normalise path" functions in succession. This is a |
---|
| 1432 | * special case, in which if we have a native filesystem handler, we call |
---|
| 1433 | * it first. This is because the root of Tcl's filesystem is always a |
---|
| 1434 | * native filesystem (i.e. '/' on unix is native). |
---|
| 1435 | */ |
---|
| 1436 | |
---|
| 1437 | firstFsRecPtr = FsGetFirstFilesystem(); |
---|
| 1438 | |
---|
| 1439 | fsRecPtr = firstFsRecPtr; |
---|
| 1440 | while (fsRecPtr != NULL) { |
---|
| 1441 | if (fsRecPtr->fsPtr == &tclNativeFilesystem) { |
---|
| 1442 | Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc; |
---|
| 1443 | if (proc != NULL) { |
---|
| 1444 | startAt = (*proc)(interp, pathPtr, startAt); |
---|
| 1445 | } |
---|
| 1446 | break; |
---|
| 1447 | } |
---|
| 1448 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 1449 | } |
---|
| 1450 | |
---|
| 1451 | fsRecPtr = firstFsRecPtr; |
---|
| 1452 | while (fsRecPtr != NULL) { |
---|
| 1453 | /* |
---|
| 1454 | * Skip the native system next time through. |
---|
| 1455 | */ |
---|
| 1456 | |
---|
| 1457 | if (fsRecPtr->fsPtr != &tclNativeFilesystem) { |
---|
| 1458 | Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc; |
---|
| 1459 | if (proc != NULL) { |
---|
| 1460 | startAt = (*proc)(interp, pathPtr, startAt); |
---|
| 1461 | } |
---|
| 1462 | |
---|
| 1463 | /* |
---|
| 1464 | * We could add an efficiency check like this: |
---|
| 1465 | * if (retVal == length-of(pathPtr)) {break;} |
---|
| 1466 | * but there's not much benefit. |
---|
| 1467 | */ |
---|
| 1468 | } |
---|
| 1469 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 1470 | } |
---|
| 1471 | |
---|
| 1472 | return startAt; |
---|
| 1473 | } |
---|
| 1474 | |
---|
| 1475 | /* |
---|
| 1476 | *--------------------------------------------------------------------------- |
---|
| 1477 | * |
---|
| 1478 | * TclGetOpenMode -- |
---|
| 1479 | * |
---|
| 1480 | * This routine is an obsolete, limited version of TclGetOpenModeEx() |
---|
| 1481 | * below. It exists only to satisfy any extensions imprudently using it |
---|
| 1482 | * via Tcl's internal stubs table. |
---|
| 1483 | * |
---|
| 1484 | * Results: |
---|
| 1485 | * Same as TclGetOpenModeEx(). |
---|
| 1486 | * |
---|
| 1487 | * Side effects: |
---|
| 1488 | * Same as TclGetOpenModeEx(). |
---|
| 1489 | * |
---|
| 1490 | *--------------------------------------------------------------------------- |
---|
| 1491 | */ |
---|
| 1492 | |
---|
| 1493 | int |
---|
| 1494 | TclGetOpenMode( |
---|
| 1495 | Tcl_Interp *interp, /* Interpreter to use for error reporting - |
---|
| 1496 | * may be NULL. */ |
---|
| 1497 | const char *modeString, /* Mode string, e.g. "r+" or "RDONLY CREAT" */ |
---|
| 1498 | int *seekFlagPtr) /* Set this to 1 if the caller should seek to |
---|
| 1499 | * EOF during the opening of the file. */ |
---|
| 1500 | { |
---|
| 1501 | int binary = 0; |
---|
| 1502 | return TclGetOpenModeEx(interp, modeString, seekFlagPtr, &binary); |
---|
| 1503 | } |
---|
| 1504 | |
---|
| 1505 | /* |
---|
| 1506 | *--------------------------------------------------------------------------- |
---|
| 1507 | * |
---|
| 1508 | * TclGetOpenModeEx -- |
---|
| 1509 | * |
---|
| 1510 | * Computes a POSIX mode mask for opening a file, from a given string, |
---|
| 1511 | * and also sets flags to indicate whether the caller should seek to EOF |
---|
| 1512 | * after opening the file, and whether the caller should configure the |
---|
| 1513 | * channel for binary data. |
---|
| 1514 | * |
---|
| 1515 | * Results: |
---|
| 1516 | * On success, returns mode to pass to "open". If an error occurs, the |
---|
| 1517 | * return value is -1 and if interp is not NULL, sets interp's result |
---|
| 1518 | * object to an error message. |
---|
| 1519 | * |
---|
| 1520 | * Side effects: |
---|
| 1521 | * Sets the integer referenced by seekFlagPtr to 1 to tell the caller to |
---|
| 1522 | * seek to EOF after opening the file, or to 0 otherwise. Sets the |
---|
| 1523 | * integer referenced by binaryPtr to 1 to tell the caller to seek to |
---|
| 1524 | * configure the channel for binary data, or to 0 otherwise. |
---|
| 1525 | * |
---|
| 1526 | * Special note: |
---|
| 1527 | * This code is based on a prototype implementation contributed by Mark |
---|
| 1528 | * Diekhans. |
---|
| 1529 | * |
---|
| 1530 | *--------------------------------------------------------------------------- |
---|
| 1531 | */ |
---|
| 1532 | |
---|
| 1533 | int |
---|
| 1534 | TclGetOpenModeEx( |
---|
| 1535 | Tcl_Interp *interp, /* Interpreter to use for error reporting - |
---|
| 1536 | * may be NULL. */ |
---|
| 1537 | const char *modeString, /* Mode string, e.g. "r+" or "RDONLY CREAT" */ |
---|
| 1538 | int *seekFlagPtr, /* Set this to 1 if the caller should seek to |
---|
| 1539 | * EOF during the opening of the file. */ |
---|
| 1540 | int *binaryPtr) /* Set this to 1 if the caller should |
---|
| 1541 | * configure the opened channel for binary |
---|
| 1542 | * operations */ |
---|
| 1543 | { |
---|
| 1544 | int mode, modeArgc, c, i, gotRW; |
---|
| 1545 | const char **modeArgv, *flag; |
---|
| 1546 | #define RW_MODES (O_RDONLY|O_WRONLY|O_RDWR) |
---|
| 1547 | |
---|
| 1548 | /* |
---|
| 1549 | * Check for the simpler fopen-like access modes (e.g. "r"). They are |
---|
| 1550 | * distinguished from the POSIX access modes by the presence of a |
---|
| 1551 | * lower-case first letter. |
---|
| 1552 | */ |
---|
| 1553 | |
---|
| 1554 | *seekFlagPtr = 0; |
---|
| 1555 | *binaryPtr = 0; |
---|
| 1556 | mode = 0; |
---|
| 1557 | |
---|
| 1558 | /* |
---|
| 1559 | * Guard against international characters before using byte oriented |
---|
| 1560 | * routines. |
---|
| 1561 | */ |
---|
| 1562 | |
---|
| 1563 | if (!(modeString[0] & 0x80) |
---|
| 1564 | && islower(UCHAR(modeString[0]))) { /* INTL: ISO only. */ |
---|
| 1565 | switch (modeString[0]) { |
---|
| 1566 | case 'r': |
---|
| 1567 | mode = O_RDONLY; |
---|
| 1568 | break; |
---|
| 1569 | case 'w': |
---|
| 1570 | mode = O_WRONLY|O_CREAT|O_TRUNC; |
---|
| 1571 | break; |
---|
| 1572 | case 'a': |
---|
| 1573 | /* |
---|
| 1574 | * Added O_APPEND for proper automatic seek-to-end-on-write by the |
---|
| 1575 | * OS. [Bug 680143] |
---|
| 1576 | */ |
---|
| 1577 | |
---|
| 1578 | mode = O_WRONLY|O_CREAT|O_APPEND; |
---|
| 1579 | *seekFlagPtr = 1; |
---|
| 1580 | break; |
---|
| 1581 | default: |
---|
| 1582 | goto error; |
---|
| 1583 | } |
---|
| 1584 | i=1; |
---|
| 1585 | while (i<3 && modeString[i]) { |
---|
| 1586 | if (modeString[i] == modeString[i-1]) { |
---|
| 1587 | goto error; |
---|
| 1588 | } |
---|
| 1589 | switch (modeString[i++]) { |
---|
| 1590 | case '+': |
---|
| 1591 | /* |
---|
| 1592 | * Must remove the O_APPEND flag so that the seek command |
---|
| 1593 | * works. [Bug 1773127] |
---|
| 1594 | */ |
---|
| 1595 | |
---|
| 1596 | mode &= ~(O_RDONLY|O_WRONLY|O_APPEND); |
---|
| 1597 | mode |= O_RDWR; |
---|
| 1598 | break; |
---|
| 1599 | case 'b': |
---|
| 1600 | *binaryPtr = 1; |
---|
| 1601 | break; |
---|
| 1602 | default: |
---|
| 1603 | goto error; |
---|
| 1604 | } |
---|
| 1605 | } |
---|
| 1606 | if (modeString[i] != 0) { |
---|
| 1607 | goto error; |
---|
| 1608 | } |
---|
| 1609 | return mode; |
---|
| 1610 | |
---|
| 1611 | error: |
---|
| 1612 | *seekFlagPtr = 0; |
---|
| 1613 | *binaryPtr = 0; |
---|
| 1614 | if (interp != NULL) { |
---|
| 1615 | Tcl_AppendResult(interp, "illegal access mode \"", modeString, |
---|
| 1616 | "\"", NULL); |
---|
| 1617 | } |
---|
| 1618 | return -1; |
---|
| 1619 | } |
---|
| 1620 | |
---|
| 1621 | /* |
---|
| 1622 | * The access modes are specified using a list of POSIX modes such as |
---|
| 1623 | * O_CREAT. |
---|
| 1624 | * |
---|
| 1625 | * IMPORTANT NOTE: We rely on Tcl_SplitList working correctly when a NULL |
---|
| 1626 | * interpreter is passed in. |
---|
| 1627 | */ |
---|
| 1628 | |
---|
| 1629 | if (Tcl_SplitList(interp, modeString, &modeArgc, &modeArgv) != TCL_OK) { |
---|
| 1630 | if (interp != NULL) { |
---|
| 1631 | Tcl_AddErrorInfo(interp, |
---|
| 1632 | "\n while processing open access modes \""); |
---|
| 1633 | Tcl_AddErrorInfo(interp, modeString); |
---|
| 1634 | Tcl_AddErrorInfo(interp, "\""); |
---|
| 1635 | } |
---|
| 1636 | return -1; |
---|
| 1637 | } |
---|
| 1638 | |
---|
| 1639 | gotRW = 0; |
---|
| 1640 | for (i = 0; i < modeArgc; i++) { |
---|
| 1641 | flag = modeArgv[i]; |
---|
| 1642 | c = flag[0]; |
---|
| 1643 | if ((c == 'R') && (strcmp(flag, "RDONLY") == 0)) { |
---|
| 1644 | mode = (mode & ~RW_MODES) | O_RDONLY; |
---|
| 1645 | gotRW = 1; |
---|
| 1646 | } else if ((c == 'W') && (strcmp(flag, "WRONLY") == 0)) { |
---|
| 1647 | mode = (mode & ~RW_MODES) | O_WRONLY; |
---|
| 1648 | gotRW = 1; |
---|
| 1649 | } else if ((c == 'R') && (strcmp(flag, "RDWR") == 0)) { |
---|
| 1650 | mode = (mode & ~RW_MODES) | O_RDWR; |
---|
| 1651 | gotRW = 1; |
---|
| 1652 | } else if ((c == 'A') && (strcmp(flag, "APPEND") == 0)) { |
---|
| 1653 | mode |= O_APPEND; |
---|
| 1654 | *seekFlagPtr = 1; |
---|
| 1655 | } else if ((c == 'C') && (strcmp(flag, "CREAT") == 0)) { |
---|
| 1656 | mode |= O_CREAT; |
---|
| 1657 | } else if ((c == 'E') && (strcmp(flag, "EXCL") == 0)) { |
---|
| 1658 | mode |= O_EXCL; |
---|
| 1659 | |
---|
| 1660 | } else if ((c == 'N') && (strcmp(flag, "NOCTTY") == 0)) { |
---|
| 1661 | #ifdef O_NOCTTY |
---|
| 1662 | mode |= O_NOCTTY; |
---|
| 1663 | #else |
---|
| 1664 | if (interp != NULL) { |
---|
| 1665 | Tcl_AppendResult(interp, "access mode \"", flag, |
---|
| 1666 | "\" not supported by this system", NULL); |
---|
| 1667 | } |
---|
| 1668 | ckfree((char *) modeArgv); |
---|
| 1669 | return -1; |
---|
| 1670 | #endif |
---|
| 1671 | |
---|
| 1672 | } else if ((c == 'N') && (strcmp(flag, "NONBLOCK") == 0)) { |
---|
| 1673 | #ifdef O_NONBLOCK |
---|
| 1674 | mode |= O_NONBLOCK; |
---|
| 1675 | #else |
---|
| 1676 | if (interp != NULL) { |
---|
| 1677 | Tcl_AppendResult(interp, "access mode \"", flag, |
---|
| 1678 | "\" not supported by this system", NULL); |
---|
| 1679 | } |
---|
| 1680 | ckfree((char *) modeArgv); |
---|
| 1681 | return -1; |
---|
| 1682 | #endif |
---|
| 1683 | |
---|
| 1684 | } else if ((c == 'T') && (strcmp(flag, "TRUNC") == 0)) { |
---|
| 1685 | mode |= O_TRUNC; |
---|
| 1686 | } else if ((c == 'B') && (strcmp(flag, "BINARY") == 0)) { |
---|
| 1687 | *binaryPtr = 1; |
---|
| 1688 | } else { |
---|
| 1689 | |
---|
| 1690 | if (interp != NULL) { |
---|
| 1691 | Tcl_AppendResult(interp, "invalid access mode \"", flag, |
---|
| 1692 | "\": must be RDONLY, WRONLY, RDWR, APPEND, BINARY, " |
---|
| 1693 | "CREAT, EXCL, NOCTTY, NONBLOCK, or TRUNC", NULL); |
---|
| 1694 | } |
---|
| 1695 | ckfree((char *) modeArgv); |
---|
| 1696 | return -1; |
---|
| 1697 | } |
---|
| 1698 | } |
---|
| 1699 | |
---|
| 1700 | ckfree((char *) modeArgv); |
---|
| 1701 | |
---|
| 1702 | if (!gotRW) { |
---|
| 1703 | if (interp != NULL) { |
---|
| 1704 | Tcl_AppendResult(interp, "access mode must include either" |
---|
| 1705 | " RDONLY, WRONLY, or RDWR", NULL); |
---|
| 1706 | } |
---|
| 1707 | return -1; |
---|
| 1708 | } |
---|
| 1709 | return mode; |
---|
| 1710 | } |
---|
| 1711 | |
---|
| 1712 | /* |
---|
| 1713 | * Tcl_FSEvalFile is Tcl_FSEvalFileEx without encoding argument. |
---|
| 1714 | */ |
---|
| 1715 | |
---|
| 1716 | int |
---|
| 1717 | Tcl_FSEvalFile( |
---|
| 1718 | Tcl_Interp *interp, /* Interpreter in which to process file. */ |
---|
| 1719 | Tcl_Obj *pathPtr) /* Path of file to process. Tilde-substitution |
---|
| 1720 | * will be performed on this name. */ |
---|
| 1721 | { |
---|
| 1722 | return Tcl_FSEvalFileEx(interp, pathPtr, NULL); |
---|
| 1723 | } |
---|
| 1724 | |
---|
| 1725 | /* |
---|
| 1726 | *---------------------------------------------------------------------- |
---|
| 1727 | * |
---|
| 1728 | * Tcl_FSEvalFileEx -- |
---|
| 1729 | * |
---|
| 1730 | * Read in a file and process the entire file as one gigantic Tcl |
---|
| 1731 | * command. |
---|
| 1732 | * |
---|
| 1733 | * Results: |
---|
| 1734 | * A standard Tcl result, which is either the result of executing the |
---|
| 1735 | * file or an error indicating why the file couldn't be read. |
---|
| 1736 | * |
---|
| 1737 | * Side effects: |
---|
| 1738 | * Depends on the commands in the file. During the evaluation of the |
---|
| 1739 | * contents of the file, iPtr->scriptFile is made to point to pathPtr |
---|
| 1740 | * (the old value is cached and replaced when this function returns). |
---|
| 1741 | * |
---|
| 1742 | *---------------------------------------------------------------------- |
---|
| 1743 | */ |
---|
| 1744 | |
---|
| 1745 | int |
---|
| 1746 | Tcl_FSEvalFileEx( |
---|
| 1747 | Tcl_Interp *interp, /* Interpreter in which to process file. */ |
---|
| 1748 | Tcl_Obj *pathPtr, /* Path of file to process. Tilde-substitution |
---|
| 1749 | * will be performed on this name. */ |
---|
| 1750 | const char *encodingName) /* If non-NULL, then use this encoding for the |
---|
| 1751 | * file. NULL means use the system encoding. */ |
---|
| 1752 | { |
---|
| 1753 | int length, result = TCL_ERROR; |
---|
| 1754 | Tcl_StatBuf statBuf; |
---|
| 1755 | Tcl_Obj *oldScriptFile; |
---|
| 1756 | Interp *iPtr; |
---|
| 1757 | char *string; |
---|
| 1758 | Tcl_Channel chan; |
---|
| 1759 | Tcl_Obj *objPtr; |
---|
| 1760 | |
---|
| 1761 | if (Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL) { |
---|
| 1762 | return result; |
---|
| 1763 | } |
---|
| 1764 | |
---|
| 1765 | if (Tcl_FSStat(pathPtr, &statBuf) == -1) { |
---|
| 1766 | Tcl_SetErrno(errno); |
---|
| 1767 | Tcl_AppendResult(interp, "couldn't read file \"", |
---|
| 1768 | Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); |
---|
| 1769 | return result; |
---|
| 1770 | } |
---|
| 1771 | chan = Tcl_FSOpenFileChannel(interp, pathPtr, "r", 0644); |
---|
| 1772 | if (chan == (Tcl_Channel) NULL) { |
---|
| 1773 | Tcl_ResetResult(interp); |
---|
| 1774 | Tcl_AppendResult(interp, "couldn't read file \"", |
---|
| 1775 | Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); |
---|
| 1776 | return result; |
---|
| 1777 | } |
---|
| 1778 | |
---|
| 1779 | /* |
---|
| 1780 | * The eofchar is \32 (^Z). This is the usual on Windows, but we effect |
---|
| 1781 | * this cross-platform to allow for scripted documents. [Bug: 2040] |
---|
| 1782 | */ |
---|
| 1783 | |
---|
| 1784 | Tcl_SetChannelOption(interp, chan, "-eofchar", "\32"); |
---|
| 1785 | |
---|
| 1786 | /* |
---|
| 1787 | * If the encoding is specified, set it for the channel. Else don't touch |
---|
| 1788 | * it (and use the system encoding) Report error on unknown encoding. |
---|
| 1789 | */ |
---|
| 1790 | |
---|
| 1791 | if (encodingName != NULL) { |
---|
| 1792 | if (Tcl_SetChannelOption(interp, chan, "-encoding", encodingName) |
---|
| 1793 | != TCL_OK) { |
---|
| 1794 | Tcl_Close(interp,chan); |
---|
| 1795 | return result; |
---|
| 1796 | } |
---|
| 1797 | } |
---|
| 1798 | |
---|
| 1799 | objPtr = Tcl_NewObj(); |
---|
| 1800 | Tcl_IncrRefCount(objPtr); |
---|
| 1801 | if (Tcl_ReadChars(chan, objPtr, -1, 0) < 0) { |
---|
| 1802 | Tcl_Close(interp, chan); |
---|
| 1803 | Tcl_AppendResult(interp, "couldn't read file \"", |
---|
| 1804 | Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); |
---|
| 1805 | goto end; |
---|
| 1806 | } |
---|
| 1807 | |
---|
| 1808 | if (Tcl_Close(interp, chan) != TCL_OK) { |
---|
| 1809 | goto end; |
---|
| 1810 | } |
---|
| 1811 | |
---|
| 1812 | iPtr = (Interp *) interp; |
---|
| 1813 | oldScriptFile = iPtr->scriptFile; |
---|
| 1814 | iPtr->scriptFile = pathPtr; |
---|
| 1815 | Tcl_IncrRefCount(iPtr->scriptFile); |
---|
| 1816 | string = Tcl_GetStringFromObj(objPtr, &length); |
---|
| 1817 | /* TIP #280 Force the evaluator to open a frame for a sourced |
---|
| 1818 | * file. */ |
---|
| 1819 | iPtr->evalFlags |= TCL_EVAL_FILE; |
---|
| 1820 | result = Tcl_EvalEx(interp, string, length, 0); |
---|
| 1821 | |
---|
| 1822 | /* |
---|
| 1823 | * Now we have to be careful; the script may have changed the |
---|
| 1824 | * iPtr->scriptFile value, so we must reset it without assuming it still |
---|
| 1825 | * points to 'pathPtr'. |
---|
| 1826 | */ |
---|
| 1827 | |
---|
| 1828 | if (iPtr->scriptFile != NULL) { |
---|
| 1829 | Tcl_DecrRefCount(iPtr->scriptFile); |
---|
| 1830 | } |
---|
| 1831 | iPtr->scriptFile = oldScriptFile; |
---|
| 1832 | |
---|
| 1833 | if (result == TCL_RETURN) { |
---|
| 1834 | result = TclUpdateReturnInfo(iPtr); |
---|
| 1835 | } else if (result == TCL_ERROR) { |
---|
| 1836 | /* |
---|
| 1837 | * Record information telling where the error occurred. |
---|
| 1838 | */ |
---|
| 1839 | |
---|
| 1840 | const char *pathString = Tcl_GetStringFromObj(pathPtr, &length); |
---|
| 1841 | int limit = 150; |
---|
| 1842 | int overflow = (length > limit); |
---|
| 1843 | |
---|
| 1844 | Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( |
---|
| 1845 | "\n (file \"%.*s%s\" line %d)", |
---|
| 1846 | (overflow ? limit : length), pathString, |
---|
| 1847 | (overflow ? "..." : ""), interp->errorLine)); |
---|
| 1848 | } |
---|
| 1849 | |
---|
| 1850 | end: |
---|
| 1851 | Tcl_DecrRefCount(objPtr); |
---|
| 1852 | return result; |
---|
| 1853 | } |
---|
| 1854 | |
---|
| 1855 | /* |
---|
| 1856 | *---------------------------------------------------------------------- |
---|
| 1857 | * |
---|
| 1858 | * Tcl_GetErrno -- |
---|
| 1859 | * |
---|
| 1860 | * Gets the current value of the Tcl error code variable. This is |
---|
| 1861 | * currently the global variable "errno" but could in the future change |
---|
| 1862 | * to something else. |
---|
| 1863 | * |
---|
| 1864 | * Results: |
---|
| 1865 | * The value of the Tcl error code variable. |
---|
| 1866 | * |
---|
| 1867 | * Side effects: |
---|
| 1868 | * None. Note that the value of the Tcl error code variable is UNDEFINED |
---|
| 1869 | * if a call to Tcl_SetErrno did not precede this call. |
---|
| 1870 | * |
---|
| 1871 | *---------------------------------------------------------------------- |
---|
| 1872 | */ |
---|
| 1873 | |
---|
| 1874 | int |
---|
| 1875 | Tcl_GetErrno(void) |
---|
| 1876 | { |
---|
| 1877 | return errno; |
---|
| 1878 | } |
---|
| 1879 | |
---|
| 1880 | /* |
---|
| 1881 | *---------------------------------------------------------------------- |
---|
| 1882 | * |
---|
| 1883 | * Tcl_SetErrno -- |
---|
| 1884 | * |
---|
| 1885 | * Sets the Tcl error code variable to the supplied value. |
---|
| 1886 | * |
---|
| 1887 | * Results: |
---|
| 1888 | * None. |
---|
| 1889 | * |
---|
| 1890 | * Side effects: |
---|
| 1891 | * Modifies the value of the Tcl error code variable. |
---|
| 1892 | * |
---|
| 1893 | *---------------------------------------------------------------------- |
---|
| 1894 | */ |
---|
| 1895 | |
---|
| 1896 | void |
---|
| 1897 | Tcl_SetErrno( |
---|
| 1898 | int err) /* The new value. */ |
---|
| 1899 | { |
---|
| 1900 | errno = err; |
---|
| 1901 | } |
---|
| 1902 | |
---|
| 1903 | /* |
---|
| 1904 | *---------------------------------------------------------------------- |
---|
| 1905 | * |
---|
| 1906 | * Tcl_PosixError -- |
---|
| 1907 | * |
---|
| 1908 | * This function is typically called after UNIX kernel calls return |
---|
| 1909 | * errors. It stores machine-readable information about the error in |
---|
| 1910 | * errorCode field of interp and returns an information string for the |
---|
| 1911 | * caller's use. |
---|
| 1912 | * |
---|
| 1913 | * Results: |
---|
| 1914 | * The return value is a human-readable string describing the error. |
---|
| 1915 | * |
---|
| 1916 | * Side effects: |
---|
| 1917 | * The errorCode field of the interp is set. |
---|
| 1918 | * |
---|
| 1919 | *---------------------------------------------------------------------- |
---|
| 1920 | */ |
---|
| 1921 | |
---|
| 1922 | const char * |
---|
| 1923 | Tcl_PosixError( |
---|
| 1924 | Tcl_Interp *interp) /* Interpreter whose errorCode field is to be |
---|
| 1925 | * set. */ |
---|
| 1926 | { |
---|
| 1927 | const char *id, *msg; |
---|
| 1928 | |
---|
| 1929 | msg = Tcl_ErrnoMsg(errno); |
---|
| 1930 | id = Tcl_ErrnoId(); |
---|
| 1931 | if (interp) { |
---|
| 1932 | Tcl_SetErrorCode(interp, "POSIX", id, msg, NULL); |
---|
| 1933 | } |
---|
| 1934 | return msg; |
---|
| 1935 | } |
---|
| 1936 | |
---|
| 1937 | /* |
---|
| 1938 | *---------------------------------------------------------------------- |
---|
| 1939 | * |
---|
| 1940 | * Tcl_FSStat -- |
---|
| 1941 | * |
---|
| 1942 | * This function replaces the library version of stat and lsat. |
---|
| 1943 | * |
---|
| 1944 | * The appropriate function for the filesystem to which pathPtr belongs |
---|
| 1945 | * will be called. |
---|
| 1946 | * |
---|
| 1947 | * Results: |
---|
| 1948 | * See stat documentation. |
---|
| 1949 | * |
---|
| 1950 | * Side effects: |
---|
| 1951 | * See stat documentation. |
---|
| 1952 | * |
---|
| 1953 | *---------------------------------------------------------------------- |
---|
| 1954 | */ |
---|
| 1955 | |
---|
| 1956 | int |
---|
| 1957 | Tcl_FSStat( |
---|
| 1958 | Tcl_Obj *pathPtr, /* Path of file to stat (in current CP). */ |
---|
| 1959 | Tcl_StatBuf *buf) /* Filled with results of stat call. */ |
---|
| 1960 | { |
---|
| 1961 | const Tcl_Filesystem *fsPtr; |
---|
| 1962 | #ifdef USE_OBSOLETE_FS_HOOKS |
---|
| 1963 | struct stat oldStyleStatBuffer; |
---|
| 1964 | int retVal = -1; |
---|
| 1965 | |
---|
| 1966 | /* |
---|
| 1967 | * Call each of the "stat" function in succession. A non-return value of |
---|
| 1968 | * -1 indicates the particular function has succeeded. |
---|
| 1969 | */ |
---|
| 1970 | |
---|
| 1971 | Tcl_MutexLock(&obsoleteFsHookMutex); |
---|
| 1972 | |
---|
| 1973 | if (statProcList != NULL) { |
---|
| 1974 | StatProc *statProcPtr; |
---|
| 1975 | char *path; |
---|
| 1976 | Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); |
---|
| 1977 | if (transPtr == NULL) { |
---|
| 1978 | path = NULL; |
---|
| 1979 | } else { |
---|
| 1980 | path = Tcl_GetString(transPtr); |
---|
| 1981 | } |
---|
| 1982 | |
---|
| 1983 | statProcPtr = statProcList; |
---|
| 1984 | while ((retVal == -1) && (statProcPtr != NULL)) { |
---|
| 1985 | retVal = (*statProcPtr->proc)(path, &oldStyleStatBuffer); |
---|
| 1986 | statProcPtr = statProcPtr->nextPtr; |
---|
| 1987 | } |
---|
| 1988 | if (transPtr != NULL) { |
---|
| 1989 | Tcl_DecrRefCount(transPtr); |
---|
| 1990 | } |
---|
| 1991 | } |
---|
| 1992 | |
---|
| 1993 | Tcl_MutexUnlock(&obsoleteFsHookMutex); |
---|
| 1994 | if (retVal != -1) { |
---|
| 1995 | /* |
---|
| 1996 | * Note that EOVERFLOW is not a problem here, and these assignments |
---|
| 1997 | * should all be widening (if not identity.) |
---|
| 1998 | */ |
---|
| 1999 | |
---|
| 2000 | buf->st_mode = oldStyleStatBuffer.st_mode; |
---|
| 2001 | buf->st_ino = oldStyleStatBuffer.st_ino; |
---|
| 2002 | buf->st_dev = oldStyleStatBuffer.st_dev; |
---|
| 2003 | buf->st_rdev = oldStyleStatBuffer.st_rdev; |
---|
| 2004 | buf->st_nlink = oldStyleStatBuffer.st_nlink; |
---|
| 2005 | buf->st_uid = oldStyleStatBuffer.st_uid; |
---|
| 2006 | buf->st_gid = oldStyleStatBuffer.st_gid; |
---|
| 2007 | buf->st_size = Tcl_LongAsWide(oldStyleStatBuffer.st_size); |
---|
| 2008 | buf->st_atime = oldStyleStatBuffer.st_atime; |
---|
| 2009 | buf->st_mtime = oldStyleStatBuffer.st_mtime; |
---|
| 2010 | buf->st_ctime = oldStyleStatBuffer.st_ctime; |
---|
| 2011 | #ifdef HAVE_ST_BLOCKS |
---|
| 2012 | buf->st_blksize = oldStyleStatBuffer.st_blksize; |
---|
| 2013 | buf->st_blocks = Tcl_LongAsWide(oldStyleStatBuffer.st_blocks); |
---|
| 2014 | #endif |
---|
| 2015 | return retVal; |
---|
| 2016 | } |
---|
| 2017 | #endif /* USE_OBSOLETE_FS_HOOKS */ |
---|
| 2018 | |
---|
| 2019 | fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 2020 | if (fsPtr != NULL) { |
---|
| 2021 | Tcl_FSStatProc *proc = fsPtr->statProc; |
---|
| 2022 | if (proc != NULL) { |
---|
| 2023 | return (*proc)(pathPtr, buf); |
---|
| 2024 | } |
---|
| 2025 | } |
---|
| 2026 | Tcl_SetErrno(ENOENT); |
---|
| 2027 | return -1; |
---|
| 2028 | } |
---|
| 2029 | |
---|
| 2030 | /* |
---|
| 2031 | *---------------------------------------------------------------------- |
---|
| 2032 | * |
---|
| 2033 | * Tcl_FSLstat -- |
---|
| 2034 | * |
---|
| 2035 | * This function replaces the library version of lstat. The appropriate |
---|
| 2036 | * function for the filesystem to which pathPtr belongs will be called. |
---|
| 2037 | * If no 'lstat' function is listed, but a 'stat' function is, then Tcl |
---|
| 2038 | * will fall back on the stat function. |
---|
| 2039 | * |
---|
| 2040 | * Results: |
---|
| 2041 | * See lstat documentation. |
---|
| 2042 | * |
---|
| 2043 | * Side effects: |
---|
| 2044 | * See lstat documentation. |
---|
| 2045 | * |
---|
| 2046 | *---------------------------------------------------------------------- |
---|
| 2047 | */ |
---|
| 2048 | |
---|
| 2049 | int |
---|
| 2050 | Tcl_FSLstat( |
---|
| 2051 | Tcl_Obj *pathPtr, /* Path of file to stat (in current CP). */ |
---|
| 2052 | Tcl_StatBuf *buf) /* Filled with results of stat call. */ |
---|
| 2053 | { |
---|
| 2054 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 2055 | if (fsPtr != NULL) { |
---|
| 2056 | Tcl_FSLstatProc *proc = fsPtr->lstatProc; |
---|
| 2057 | if (proc != NULL) { |
---|
| 2058 | return (*proc)(pathPtr, buf); |
---|
| 2059 | } else { |
---|
| 2060 | Tcl_FSStatProc *sproc = fsPtr->statProc; |
---|
| 2061 | if (sproc != NULL) { |
---|
| 2062 | return (*sproc)(pathPtr, buf); |
---|
| 2063 | } |
---|
| 2064 | } |
---|
| 2065 | } |
---|
| 2066 | Tcl_SetErrno(ENOENT); |
---|
| 2067 | return -1; |
---|
| 2068 | } |
---|
| 2069 | |
---|
| 2070 | /* |
---|
| 2071 | *---------------------------------------------------------------------- |
---|
| 2072 | * |
---|
| 2073 | * Tcl_FSAccess -- |
---|
| 2074 | * |
---|
| 2075 | * This function replaces the library version of access. The appropriate |
---|
| 2076 | * function for the filesystem to which pathPtr belongs will be called. |
---|
| 2077 | * |
---|
| 2078 | * Results: |
---|
| 2079 | * See access documentation. |
---|
| 2080 | * |
---|
| 2081 | * Side effects: |
---|
| 2082 | * See access documentation. |
---|
| 2083 | * |
---|
| 2084 | *---------------------------------------------------------------------- |
---|
| 2085 | */ |
---|
| 2086 | |
---|
| 2087 | int |
---|
| 2088 | Tcl_FSAccess( |
---|
| 2089 | Tcl_Obj *pathPtr, /* Path of file to access (in current CP). */ |
---|
| 2090 | int mode) /* Permission setting. */ |
---|
| 2091 | { |
---|
| 2092 | const Tcl_Filesystem *fsPtr; |
---|
| 2093 | #ifdef USE_OBSOLETE_FS_HOOKS |
---|
| 2094 | int retVal = -1; |
---|
| 2095 | |
---|
| 2096 | /* |
---|
| 2097 | * Call each of the "access" function in succession. A non-return value of |
---|
| 2098 | * -1 indicates the particular function has succeeded. |
---|
| 2099 | */ |
---|
| 2100 | |
---|
| 2101 | Tcl_MutexLock(&obsoleteFsHookMutex); |
---|
| 2102 | |
---|
| 2103 | if (accessProcList != NULL) { |
---|
| 2104 | AccessProc *accessProcPtr; |
---|
| 2105 | char *path; |
---|
| 2106 | Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); |
---|
| 2107 | if (transPtr == NULL) { |
---|
| 2108 | path = NULL; |
---|
| 2109 | } else { |
---|
| 2110 | path = Tcl_GetString(transPtr); |
---|
| 2111 | } |
---|
| 2112 | |
---|
| 2113 | accessProcPtr = accessProcList; |
---|
| 2114 | while ((retVal == -1) && (accessProcPtr != NULL)) { |
---|
| 2115 | retVal = (*accessProcPtr->proc)(path, mode); |
---|
| 2116 | accessProcPtr = accessProcPtr->nextPtr; |
---|
| 2117 | } |
---|
| 2118 | if (transPtr != NULL) { |
---|
| 2119 | Tcl_DecrRefCount(transPtr); |
---|
| 2120 | } |
---|
| 2121 | } |
---|
| 2122 | |
---|
| 2123 | Tcl_MutexUnlock(&obsoleteFsHookMutex); |
---|
| 2124 | if (retVal != -1) { |
---|
| 2125 | return retVal; |
---|
| 2126 | } |
---|
| 2127 | #endif /* USE_OBSOLETE_FS_HOOKS */ |
---|
| 2128 | |
---|
| 2129 | fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 2130 | if (fsPtr != NULL) { |
---|
| 2131 | Tcl_FSAccessProc *proc = fsPtr->accessProc; |
---|
| 2132 | if (proc != NULL) { |
---|
| 2133 | return (*proc)(pathPtr, mode); |
---|
| 2134 | } |
---|
| 2135 | } |
---|
| 2136 | |
---|
| 2137 | Tcl_SetErrno(ENOENT); |
---|
| 2138 | return -1; |
---|
| 2139 | } |
---|
| 2140 | |
---|
| 2141 | /* |
---|
| 2142 | *---------------------------------------------------------------------- |
---|
| 2143 | * |
---|
| 2144 | * Tcl_FSOpenFileChannel -- |
---|
| 2145 | * |
---|
| 2146 | * The appropriate function for the filesystem to which pathPtr belongs |
---|
| 2147 | * will be called. |
---|
| 2148 | * |
---|
| 2149 | * Results: |
---|
| 2150 | * The new channel or NULL, if the named file could not be opened. |
---|
| 2151 | * |
---|
| 2152 | * Side effects: |
---|
| 2153 | * May open the channel and may cause creation of a file on the file |
---|
| 2154 | * system. |
---|
| 2155 | * |
---|
| 2156 | *---------------------------------------------------------------------- |
---|
| 2157 | */ |
---|
| 2158 | |
---|
| 2159 | Tcl_Channel |
---|
| 2160 | Tcl_FSOpenFileChannel( |
---|
| 2161 | Tcl_Interp *interp, /* Interpreter for error reporting; can be |
---|
| 2162 | * NULL. */ |
---|
| 2163 | Tcl_Obj *pathPtr, /* Name of file to open. */ |
---|
| 2164 | const char *modeString, /* A list of POSIX open modes or a string such |
---|
| 2165 | * as "rw". */ |
---|
| 2166 | int permissions) /* If the open involves creating a file, with |
---|
| 2167 | * what modes to create it? */ |
---|
| 2168 | { |
---|
| 2169 | const Tcl_Filesystem *fsPtr; |
---|
| 2170 | Tcl_Channel retVal = NULL; |
---|
| 2171 | |
---|
| 2172 | #ifdef USE_OBSOLETE_FS_HOOKS |
---|
| 2173 | /* |
---|
| 2174 | * Call each of the "Tcl_OpenFileChannel" functions in succession. A |
---|
| 2175 | * non-NULL return value indicates the particular function has succeeded. |
---|
| 2176 | */ |
---|
| 2177 | |
---|
| 2178 | Tcl_MutexLock(&obsoleteFsHookMutex); |
---|
| 2179 | if (openFileChannelProcList != NULL) { |
---|
| 2180 | OpenFileChannelProc *openFileChannelProcPtr; |
---|
| 2181 | char *path; |
---|
| 2182 | Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr); |
---|
| 2183 | |
---|
| 2184 | if (transPtr == NULL) { |
---|
| 2185 | path = NULL; |
---|
| 2186 | } else { |
---|
| 2187 | path = Tcl_GetString(transPtr); |
---|
| 2188 | } |
---|
| 2189 | |
---|
| 2190 | openFileChannelProcPtr = openFileChannelProcList; |
---|
| 2191 | |
---|
| 2192 | while ((retVal == NULL) && (openFileChannelProcPtr != NULL)) { |
---|
| 2193 | retVal = (*openFileChannelProcPtr->proc)(interp, path, |
---|
| 2194 | modeString, permissions); |
---|
| 2195 | openFileChannelProcPtr = openFileChannelProcPtr->nextPtr; |
---|
| 2196 | } |
---|
| 2197 | if (transPtr != NULL) { |
---|
| 2198 | Tcl_DecrRefCount(transPtr); |
---|
| 2199 | } |
---|
| 2200 | } |
---|
| 2201 | Tcl_MutexUnlock(&obsoleteFsHookMutex); |
---|
| 2202 | if (retVal != NULL) { |
---|
| 2203 | return retVal; |
---|
| 2204 | } |
---|
| 2205 | #endif /* USE_OBSOLETE_FS_HOOKS */ |
---|
| 2206 | |
---|
| 2207 | /* |
---|
| 2208 | * We need this just to ensure we return the correct error messages under |
---|
| 2209 | * some circumstances. |
---|
| 2210 | */ |
---|
| 2211 | |
---|
| 2212 | if (Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL) { |
---|
| 2213 | return NULL; |
---|
| 2214 | } |
---|
| 2215 | |
---|
| 2216 | fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 2217 | if (fsPtr != NULL) { |
---|
| 2218 | Tcl_FSOpenFileChannelProc *proc = fsPtr->openFileChannelProc; |
---|
| 2219 | if (proc != NULL) { |
---|
| 2220 | int mode, seekFlag, binary; |
---|
| 2221 | |
---|
| 2222 | /* |
---|
| 2223 | * Parse the mode, picking up whether we want to seek to start |
---|
| 2224 | * with and/or set the channel automatically into binary mode. |
---|
| 2225 | */ |
---|
| 2226 | |
---|
| 2227 | mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary); |
---|
| 2228 | if (mode == -1) { |
---|
| 2229 | return NULL; |
---|
| 2230 | } |
---|
| 2231 | |
---|
| 2232 | /* |
---|
| 2233 | * Do the actual open() call. |
---|
| 2234 | */ |
---|
| 2235 | |
---|
| 2236 | retVal = (*proc)(interp, pathPtr, mode, permissions); |
---|
| 2237 | if (retVal == NULL) { |
---|
| 2238 | return NULL; |
---|
| 2239 | } |
---|
| 2240 | |
---|
| 2241 | /* |
---|
| 2242 | * Apply appropriate flags parsed out above. |
---|
| 2243 | */ |
---|
| 2244 | |
---|
| 2245 | if (seekFlag && Tcl_Seek(retVal, (Tcl_WideInt)0, |
---|
| 2246 | SEEK_END) < (Tcl_WideInt)0) { |
---|
| 2247 | if (interp != NULL) { |
---|
| 2248 | Tcl_AppendResult(interp, "could not seek to end " |
---|
| 2249 | "of file while opening \"", Tcl_GetString(pathPtr), |
---|
| 2250 | "\": ", Tcl_PosixError(interp), NULL); |
---|
| 2251 | } |
---|
| 2252 | Tcl_Close(NULL, retVal); |
---|
| 2253 | return NULL; |
---|
| 2254 | } |
---|
| 2255 | if (binary) { |
---|
| 2256 | Tcl_SetChannelOption(interp, retVal, "-translation", "binary"); |
---|
| 2257 | } |
---|
| 2258 | return retVal; |
---|
| 2259 | } |
---|
| 2260 | } |
---|
| 2261 | |
---|
| 2262 | /* |
---|
| 2263 | * File doesn't belong to any filesystem that can open it. |
---|
| 2264 | */ |
---|
| 2265 | |
---|
| 2266 | Tcl_SetErrno(ENOENT); |
---|
| 2267 | if (interp != NULL) { |
---|
| 2268 | Tcl_AppendResult(interp, "couldn't open \"", Tcl_GetString(pathPtr), |
---|
| 2269 | "\": ", Tcl_PosixError(interp), NULL); |
---|
| 2270 | } |
---|
| 2271 | return NULL; |
---|
| 2272 | } |
---|
| 2273 | |
---|
| 2274 | /* |
---|
| 2275 | *---------------------------------------------------------------------- |
---|
| 2276 | * |
---|
| 2277 | * Tcl_FSUtime -- |
---|
| 2278 | * |
---|
| 2279 | * This function replaces the library version of utime. The appropriate |
---|
| 2280 | * function for the filesystem to which pathPtr belongs will be called. |
---|
| 2281 | * |
---|
| 2282 | * Results: |
---|
| 2283 | * See utime documentation. |
---|
| 2284 | * |
---|
| 2285 | * Side effects: |
---|
| 2286 | * See utime documentation. |
---|
| 2287 | * |
---|
| 2288 | *---------------------------------------------------------------------- |
---|
| 2289 | */ |
---|
| 2290 | |
---|
| 2291 | int |
---|
| 2292 | Tcl_FSUtime( |
---|
| 2293 | Tcl_Obj *pathPtr, /* File to change access/modification times */ |
---|
| 2294 | struct utimbuf *tval) /* Structure containing access/modification |
---|
| 2295 | * times to use. Should not be modified. */ |
---|
| 2296 | { |
---|
| 2297 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 2298 | if (fsPtr != NULL) { |
---|
| 2299 | Tcl_FSUtimeProc *proc = fsPtr->utimeProc; |
---|
| 2300 | if (proc != NULL) { |
---|
| 2301 | return (*proc)(pathPtr, tval); |
---|
| 2302 | } |
---|
| 2303 | } |
---|
| 2304 | return -1; |
---|
| 2305 | } |
---|
| 2306 | |
---|
| 2307 | /* |
---|
| 2308 | *---------------------------------------------------------------------- |
---|
| 2309 | * |
---|
| 2310 | * NativeFileAttrStrings -- |
---|
| 2311 | * |
---|
| 2312 | * This function implements the platform dependent 'file attributes' |
---|
| 2313 | * subcommand, for the native filesystem, for listing the set of possible |
---|
| 2314 | * attribute strings. This function is part of Tcl's native filesystem |
---|
| 2315 | * support, and is placed here because it is shared by Unix and Windows |
---|
| 2316 | * code. |
---|
| 2317 | * |
---|
| 2318 | * Results: |
---|
| 2319 | * An array of strings |
---|
| 2320 | * |
---|
| 2321 | * Side effects: |
---|
| 2322 | * None. |
---|
| 2323 | * |
---|
| 2324 | *---------------------------------------------------------------------- |
---|
| 2325 | */ |
---|
| 2326 | |
---|
| 2327 | static const char ** |
---|
| 2328 | NativeFileAttrStrings( |
---|
| 2329 | Tcl_Obj *pathPtr, |
---|
| 2330 | Tcl_Obj **objPtrRef) |
---|
| 2331 | { |
---|
| 2332 | return tclpFileAttrStrings; |
---|
| 2333 | } |
---|
| 2334 | |
---|
| 2335 | /* |
---|
| 2336 | *---------------------------------------------------------------------- |
---|
| 2337 | * |
---|
| 2338 | * NativeFileAttrsGet -- |
---|
| 2339 | * |
---|
| 2340 | * This function implements the platform dependent 'file attributes' |
---|
| 2341 | * subcommand, for the native filesystem, for 'get' operations. This |
---|
| 2342 | * function is part of Tcl's native filesystem support, and is placed |
---|
| 2343 | * here because it is shared by Unix and Windows code. |
---|
| 2344 | * |
---|
| 2345 | * Results: |
---|
| 2346 | * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK |
---|
| 2347 | * was returned) is likely to have a refCount of zero. Either way we must |
---|
| 2348 | * either store it somewhere (e.g. the Tcl result), or Incr/Decr its |
---|
| 2349 | * refCount to ensure it is properly freed. |
---|
| 2350 | * |
---|
| 2351 | * Side effects: |
---|
| 2352 | * None. |
---|
| 2353 | * |
---|
| 2354 | *---------------------------------------------------------------------- |
---|
| 2355 | */ |
---|
| 2356 | |
---|
| 2357 | static int |
---|
| 2358 | NativeFileAttrsGet( |
---|
| 2359 | Tcl_Interp *interp, /* The interpreter for error reporting. */ |
---|
| 2360 | int index, /* index of the attribute command. */ |
---|
| 2361 | Tcl_Obj *pathPtr, /* path of file we are operating on. */ |
---|
| 2362 | Tcl_Obj **objPtrRef) /* for output. */ |
---|
| 2363 | { |
---|
| 2364 | return (*tclpFileAttrProcs[index].getProc)(interp, index, pathPtr, |
---|
| 2365 | objPtrRef); |
---|
| 2366 | } |
---|
| 2367 | |
---|
| 2368 | /* |
---|
| 2369 | *---------------------------------------------------------------------- |
---|
| 2370 | * |
---|
| 2371 | * NativeFileAttrsSet -- |
---|
| 2372 | * |
---|
| 2373 | * This function implements the platform dependent 'file attributes' |
---|
| 2374 | * subcommand, for the native filesystem, for 'set' operations. This |
---|
| 2375 | * function is part of Tcl's native filesystem support, and is placed |
---|
| 2376 | * here because it is shared by Unix and Windows code. |
---|
| 2377 | * |
---|
| 2378 | * Results: |
---|
| 2379 | * Standard Tcl return code. |
---|
| 2380 | * |
---|
| 2381 | * Side effects: |
---|
| 2382 | * None. |
---|
| 2383 | * |
---|
| 2384 | *---------------------------------------------------------------------- |
---|
| 2385 | */ |
---|
| 2386 | |
---|
| 2387 | static int |
---|
| 2388 | NativeFileAttrsSet( |
---|
| 2389 | Tcl_Interp *interp, /* The interpreter for error reporting. */ |
---|
| 2390 | int index, /* index of the attribute command. */ |
---|
| 2391 | Tcl_Obj *pathPtr, /* path of file we are operating on. */ |
---|
| 2392 | Tcl_Obj *objPtr) /* set to this value. */ |
---|
| 2393 | { |
---|
| 2394 | return (*tclpFileAttrProcs[index].setProc)(interp, index, pathPtr, objPtr); |
---|
| 2395 | } |
---|
| 2396 | |
---|
| 2397 | /* |
---|
| 2398 | *---------------------------------------------------------------------- |
---|
| 2399 | * |
---|
| 2400 | * Tcl_FSFileAttrStrings -- |
---|
| 2401 | * |
---|
| 2402 | * This function implements part of the hookable 'file attributes' |
---|
| 2403 | * subcommand. The appropriate function for the filesystem to which |
---|
| 2404 | * pathPtr belongs will be called. |
---|
| 2405 | * |
---|
| 2406 | * Results: |
---|
| 2407 | * The called function may either return an array of strings, or may |
---|
| 2408 | * instead return NULL and place a Tcl list into the given objPtrRef. |
---|
| 2409 | * Tcl will take that list and first increment its refCount before using |
---|
| 2410 | * it. On completion of that use, Tcl will decrement its refCount. Hence |
---|
| 2411 | * if the list should be disposed of by Tcl when done, it should have a |
---|
| 2412 | * refCount of zero, and if the list should not be disposed of, the |
---|
| 2413 | * filesystem should ensure it retains a refCount on the object. |
---|
| 2414 | * |
---|
| 2415 | * Side effects: |
---|
| 2416 | * None. |
---|
| 2417 | * |
---|
| 2418 | *---------------------------------------------------------------------- |
---|
| 2419 | */ |
---|
| 2420 | |
---|
| 2421 | const char ** |
---|
| 2422 | Tcl_FSFileAttrStrings( |
---|
| 2423 | Tcl_Obj *pathPtr, |
---|
| 2424 | Tcl_Obj **objPtrRef) |
---|
| 2425 | { |
---|
| 2426 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 2427 | |
---|
| 2428 | if (fsPtr != NULL) { |
---|
| 2429 | Tcl_FSFileAttrStringsProc *proc = fsPtr->fileAttrStringsProc; |
---|
| 2430 | if (proc != NULL) { |
---|
| 2431 | return (*proc)(pathPtr, objPtrRef); |
---|
| 2432 | } |
---|
| 2433 | } |
---|
| 2434 | Tcl_SetErrno(ENOENT); |
---|
| 2435 | return NULL; |
---|
| 2436 | } |
---|
| 2437 | |
---|
| 2438 | /* |
---|
| 2439 | *---------------------------------------------------------------------- |
---|
| 2440 | * |
---|
| 2441 | * TclFSFileAttrIndex -- |
---|
| 2442 | * |
---|
| 2443 | * Helper function for converting an attribute name to an index into the |
---|
| 2444 | * attribute table. |
---|
| 2445 | * |
---|
| 2446 | * Results: |
---|
| 2447 | * Tcl result code, index written to *indexPtr on result==TCL_OK |
---|
| 2448 | * |
---|
| 2449 | * Side effects: |
---|
| 2450 | * None. |
---|
| 2451 | * |
---|
| 2452 | *---------------------------------------------------------------------- |
---|
| 2453 | */ |
---|
| 2454 | |
---|
| 2455 | int |
---|
| 2456 | TclFSFileAttrIndex( |
---|
| 2457 | Tcl_Obj *pathPtr, /* File whose attributes are to be indexed |
---|
| 2458 | * into. */ |
---|
| 2459 | const char *attributeName, /* The attribute being looked for. */ |
---|
| 2460 | int *indexPtr) /* Where to write the found index. */ |
---|
| 2461 | { |
---|
| 2462 | Tcl_Obj *listObj = NULL; |
---|
| 2463 | const char **attrTable; |
---|
| 2464 | |
---|
| 2465 | /* |
---|
| 2466 | * Get the attribute table for the file. |
---|
| 2467 | */ |
---|
| 2468 | |
---|
| 2469 | attrTable = Tcl_FSFileAttrStrings(pathPtr, &listObj); |
---|
| 2470 | if (listObj != NULL) { |
---|
| 2471 | Tcl_IncrRefCount(listObj); |
---|
| 2472 | } |
---|
| 2473 | |
---|
| 2474 | if (attrTable != NULL) { |
---|
| 2475 | /* |
---|
| 2476 | * It's a constant attribute table, so use T_GIFO. |
---|
| 2477 | */ |
---|
| 2478 | |
---|
| 2479 | Tcl_Obj *tmpObj = Tcl_NewStringObj(attributeName, -1); |
---|
| 2480 | int result; |
---|
| 2481 | |
---|
| 2482 | result = Tcl_GetIndexFromObj(NULL, tmpObj, attrTable, NULL, TCL_EXACT, |
---|
| 2483 | indexPtr); |
---|
| 2484 | TclDecrRefCount(tmpObj); |
---|
| 2485 | if (listObj != NULL) { |
---|
| 2486 | TclDecrRefCount(listObj); |
---|
| 2487 | } |
---|
| 2488 | return result; |
---|
| 2489 | } else if (listObj != NULL) { |
---|
| 2490 | /* |
---|
| 2491 | * It's a non-constant attribute list, so do a literal search. |
---|
| 2492 | */ |
---|
| 2493 | |
---|
| 2494 | int i, objc; |
---|
| 2495 | Tcl_Obj **objv; |
---|
| 2496 | |
---|
| 2497 | if (Tcl_ListObjGetElements(NULL, listObj, &objc, &objv) != TCL_OK) { |
---|
| 2498 | TclDecrRefCount(listObj); |
---|
| 2499 | return TCL_ERROR; |
---|
| 2500 | } |
---|
| 2501 | for (i=0 ; i<objc ; i++) { |
---|
| 2502 | if (!strcmp(attributeName, TclGetString(objv[i]))) { |
---|
| 2503 | TclDecrRefCount(listObj); |
---|
| 2504 | *indexPtr = i; |
---|
| 2505 | return TCL_OK; |
---|
| 2506 | } |
---|
| 2507 | } |
---|
| 2508 | TclDecrRefCount(listObj); |
---|
| 2509 | return TCL_ERROR; |
---|
| 2510 | } else { |
---|
| 2511 | return TCL_ERROR; |
---|
| 2512 | } |
---|
| 2513 | } |
---|
| 2514 | |
---|
| 2515 | /* |
---|
| 2516 | *---------------------------------------------------------------------- |
---|
| 2517 | * |
---|
| 2518 | * Tcl_FSFileAttrsGet -- |
---|
| 2519 | * |
---|
| 2520 | * This function implements read access for the hookable 'file |
---|
| 2521 | * attributes' subcommand. The appropriate function for the filesystem to |
---|
| 2522 | * which pathPtr belongs will be called. |
---|
| 2523 | * |
---|
| 2524 | * Results: |
---|
| 2525 | * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK |
---|
| 2526 | * was returned) is likely to have a refCount of zero. Either way we must |
---|
| 2527 | * either store it somewhere (e.g. the Tcl result), or Incr/Decr its |
---|
| 2528 | * refCount to ensure it is properly freed. |
---|
| 2529 | * |
---|
| 2530 | * Side effects: |
---|
| 2531 | * None. |
---|
| 2532 | * |
---|
| 2533 | *---------------------------------------------------------------------- |
---|
| 2534 | */ |
---|
| 2535 | |
---|
| 2536 | int |
---|
| 2537 | Tcl_FSFileAttrsGet( |
---|
| 2538 | Tcl_Interp *interp, /* The interpreter for error reporting. */ |
---|
| 2539 | int index, /* index of the attribute command. */ |
---|
| 2540 | Tcl_Obj *pathPtr, /* filename we are operating on. */ |
---|
| 2541 | Tcl_Obj **objPtrRef) /* for output. */ |
---|
| 2542 | { |
---|
| 2543 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 2544 | |
---|
| 2545 | if (fsPtr != NULL) { |
---|
| 2546 | Tcl_FSFileAttrsGetProc *proc = fsPtr->fileAttrsGetProc; |
---|
| 2547 | if (proc != NULL) { |
---|
| 2548 | return (*proc)(interp, index, pathPtr, objPtrRef); |
---|
| 2549 | } |
---|
| 2550 | } |
---|
| 2551 | Tcl_SetErrno(ENOENT); |
---|
| 2552 | return -1; |
---|
| 2553 | } |
---|
| 2554 | |
---|
| 2555 | /* |
---|
| 2556 | *---------------------------------------------------------------------- |
---|
| 2557 | * |
---|
| 2558 | * Tcl_FSFileAttrsSet -- |
---|
| 2559 | * |
---|
| 2560 | * This function implements write access for the hookable 'file |
---|
| 2561 | * attributes' subcommand. The appropriate function for the filesystem to |
---|
| 2562 | * which pathPtr belongs will be called. |
---|
| 2563 | * |
---|
| 2564 | * Results: |
---|
| 2565 | * Standard Tcl return code. |
---|
| 2566 | * |
---|
| 2567 | * Side effects: |
---|
| 2568 | * None. |
---|
| 2569 | * |
---|
| 2570 | *---------------------------------------------------------------------- |
---|
| 2571 | */ |
---|
| 2572 | |
---|
| 2573 | int |
---|
| 2574 | Tcl_FSFileAttrsSet( |
---|
| 2575 | Tcl_Interp *interp, /* The interpreter for error reporting. */ |
---|
| 2576 | int index, /* index of the attribute command. */ |
---|
| 2577 | Tcl_Obj *pathPtr, /* filename we are operating on. */ |
---|
| 2578 | Tcl_Obj *objPtr) /* Input value. */ |
---|
| 2579 | { |
---|
| 2580 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 2581 | |
---|
| 2582 | if (fsPtr != NULL) { |
---|
| 2583 | Tcl_FSFileAttrsSetProc *proc = fsPtr->fileAttrsSetProc; |
---|
| 2584 | if (proc != NULL) { |
---|
| 2585 | return (*proc)(interp, index, pathPtr, objPtr); |
---|
| 2586 | } |
---|
| 2587 | } |
---|
| 2588 | Tcl_SetErrno(ENOENT); |
---|
| 2589 | return -1; |
---|
| 2590 | } |
---|
| 2591 | |
---|
| 2592 | /* |
---|
| 2593 | *---------------------------------------------------------------------- |
---|
| 2594 | * |
---|
| 2595 | * Tcl_FSGetCwd -- |
---|
| 2596 | * |
---|
| 2597 | * This function replaces the library version of getcwd(). |
---|
| 2598 | * |
---|
| 2599 | * Most VFS's will *not* implement a 'cwdProc'. Tcl now maintains its own |
---|
| 2600 | * record (in a Tcl_Obj) of the cwd, and an attempt is made to synch this |
---|
| 2601 | * with the cwd's containing filesystem, if that filesystem provides a |
---|
| 2602 | * cwdProc (e.g. the native filesystem). |
---|
| 2603 | * |
---|
| 2604 | * Note that if Tcl's cwd is not in the native filesystem, then of course |
---|
| 2605 | * Tcl's cwd and the native cwd are different: extensions should |
---|
| 2606 | * therefore ensure they only access the cwd through this function to |
---|
| 2607 | * avoid confusion. |
---|
| 2608 | * |
---|
| 2609 | * If a global cwdPathPtr already exists, it is cached in the thread's |
---|
| 2610 | * private data structures and reference to the cached copy is returned, |
---|
| 2611 | * subject to a synchronisation attempt in that cwdPathPtr's fs. |
---|
| 2612 | * |
---|
| 2613 | * Otherwise, the chain of functions that have been "inserted" into the |
---|
| 2614 | * filesystem will be called in succession until either a value other |
---|
| 2615 | * than NULL is returned, or the entire list is visited. |
---|
| 2616 | * |
---|
| 2617 | * Results: |
---|
| 2618 | * The result is a pointer to a Tcl_Obj specifying the current directory, |
---|
| 2619 | * or NULL if the current directory could not be determined. If NULL is |
---|
| 2620 | * returned, an error message is left in the interp's result. |
---|
| 2621 | * |
---|
| 2622 | * The result already has its refCount incremented for the caller. When |
---|
| 2623 | * it is no longer needed, that refCount should be decremented. |
---|
| 2624 | * |
---|
| 2625 | * Side effects: |
---|
| 2626 | * Various objects may be freed and allocated. |
---|
| 2627 | * |
---|
| 2628 | *---------------------------------------------------------------------- |
---|
| 2629 | */ |
---|
| 2630 | |
---|
| 2631 | Tcl_Obj * |
---|
| 2632 | Tcl_FSGetCwd( |
---|
| 2633 | Tcl_Interp *interp) |
---|
| 2634 | { |
---|
| 2635 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); |
---|
| 2636 | |
---|
| 2637 | if (TclFSCwdPointerEquals(NULL)) { |
---|
| 2638 | FilesystemRecord *fsRecPtr; |
---|
| 2639 | Tcl_Obj *retVal = NULL; |
---|
| 2640 | |
---|
| 2641 | /* |
---|
| 2642 | * We've never been called before, try to find a cwd. Call each of the |
---|
| 2643 | * "Tcl_GetCwd" function in succession. A non-NULL return value |
---|
| 2644 | * indicates the particular function has succeeded. |
---|
| 2645 | */ |
---|
| 2646 | |
---|
| 2647 | fsRecPtr = FsGetFirstFilesystem(); |
---|
| 2648 | while ((retVal == NULL) && (fsRecPtr != NULL)) { |
---|
| 2649 | Tcl_FSGetCwdProc *proc = fsRecPtr->fsPtr->getCwdProc; |
---|
| 2650 | if (proc != NULL) { |
---|
| 2651 | if (fsRecPtr->fsPtr->version != TCL_FILESYSTEM_VERSION_1) { |
---|
| 2652 | ClientData retCd; |
---|
| 2653 | TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)proc; |
---|
| 2654 | |
---|
| 2655 | retCd = (*proc2)(NULL); |
---|
| 2656 | if (retCd != NULL) { |
---|
| 2657 | Tcl_Obj *norm; |
---|
| 2658 | /* Looks like a new current directory */ |
---|
| 2659 | retVal = (*fsRecPtr->fsPtr->internalToNormalizedProc)( |
---|
| 2660 | retCd); |
---|
| 2661 | Tcl_IncrRefCount(retVal); |
---|
| 2662 | norm = TclFSNormalizeAbsolutePath(interp,retVal,NULL); |
---|
| 2663 | if (norm != NULL) { |
---|
| 2664 | /* |
---|
| 2665 | * We found a cwd, which is now in our global |
---|
| 2666 | * storage. We must make a copy. Norm already has |
---|
| 2667 | * a refCount of 1. |
---|
| 2668 | * |
---|
| 2669 | * Threading issue: note that multiple threads at |
---|
| 2670 | * system startup could in principle call this |
---|
| 2671 | * function simultaneously. They will therefore |
---|
| 2672 | * each set the cwdPathPtr independently. That |
---|
| 2673 | * behaviour is a bit peculiar, but should be |
---|
| 2674 | * fine. Once we have a cwd, we'll always be in |
---|
| 2675 | * the 'else' branch below which is simpler. |
---|
| 2676 | */ |
---|
| 2677 | |
---|
| 2678 | FsUpdateCwd(norm, retCd); |
---|
| 2679 | Tcl_DecrRefCount(norm); |
---|
| 2680 | } else { |
---|
| 2681 | (*fsRecPtr->fsPtr->freeInternalRepProc)(retCd); |
---|
| 2682 | } |
---|
| 2683 | Tcl_DecrRefCount(retVal); |
---|
| 2684 | retVal = NULL; |
---|
| 2685 | goto cdDidNotChange; |
---|
| 2686 | } else if (interp != NULL) { |
---|
| 2687 | Tcl_AppendResult(interp, |
---|
| 2688 | "error getting working directory name: ", |
---|
| 2689 | Tcl_PosixError(interp), NULL); |
---|
| 2690 | } |
---|
| 2691 | } else { |
---|
| 2692 | retVal = (*proc)(interp); |
---|
| 2693 | } |
---|
| 2694 | } |
---|
| 2695 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 2696 | } |
---|
| 2697 | |
---|
| 2698 | /* |
---|
| 2699 | * Now the 'cwd' may NOT be normalized, at least on some platforms. |
---|
| 2700 | * For the sake of efficiency, we want a completely normalized cwd at |
---|
| 2701 | * all times. |
---|
| 2702 | * |
---|
| 2703 | * Finally, if retVal is NULL, we do not have a cwd, which could be |
---|
| 2704 | * problematic. |
---|
| 2705 | */ |
---|
| 2706 | |
---|
| 2707 | if (retVal != NULL) { |
---|
| 2708 | Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, retVal, NULL); |
---|
| 2709 | if (norm != NULL) { |
---|
| 2710 | /* |
---|
| 2711 | * We found a cwd, which is now in our global storage. We must |
---|
| 2712 | * make a copy. Norm already has a refCount of 1. |
---|
| 2713 | * |
---|
| 2714 | * Threading issue: note that multiple threads at system |
---|
| 2715 | * startup could in principle call this function |
---|
| 2716 | * simultaneously. They will therefore each set the cwdPathPtr |
---|
| 2717 | * independently. That behaviour is a bit peculiar, but should |
---|
| 2718 | * be fine. Once we have a cwd, we'll always be in the 'else' |
---|
| 2719 | * branch below which is simpler. |
---|
| 2720 | */ |
---|
| 2721 | |
---|
| 2722 | ClientData cd = (ClientData) Tcl_FSGetNativePath(norm); |
---|
| 2723 | FsUpdateCwd(norm, TclNativeDupInternalRep(cd)); |
---|
| 2724 | Tcl_DecrRefCount(norm); |
---|
| 2725 | } |
---|
| 2726 | Tcl_DecrRefCount(retVal); |
---|
| 2727 | } |
---|
| 2728 | } else { |
---|
| 2729 | /* |
---|
| 2730 | * We already have a cwd cached, but we want to give the filesystem it |
---|
| 2731 | * is in a chance to check whether that cwd has changed, or is perhaps |
---|
| 2732 | * no longer accessible. This allows an error to be thrown if, say, |
---|
| 2733 | * the permissions on that directory have changed. |
---|
| 2734 | */ |
---|
| 2735 | |
---|
| 2736 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(tsdPtr->cwdPathPtr); |
---|
| 2737 | |
---|
| 2738 | /* |
---|
| 2739 | * If the filesystem couldn't be found, or if no cwd function exists |
---|
| 2740 | * for this filesystem, then we simply assume the cached cwd is ok. |
---|
| 2741 | * If we do call a cwd, we must watch for errors (if the cwd returns |
---|
| 2742 | * NULL). This ensures that, say, on Unix if the permissions of the |
---|
| 2743 | * cwd change, 'pwd' does actually throw the correct error in Tcl. |
---|
| 2744 | * (This is tested for in the test suite on unix). |
---|
| 2745 | */ |
---|
| 2746 | |
---|
| 2747 | if (fsPtr != NULL) { |
---|
| 2748 | Tcl_FSGetCwdProc *proc = fsPtr->getCwdProc; |
---|
| 2749 | ClientData retCd = NULL; |
---|
| 2750 | if (proc != NULL) { |
---|
| 2751 | Tcl_Obj *retVal; |
---|
| 2752 | if (fsPtr->version != TCL_FILESYSTEM_VERSION_1) { |
---|
| 2753 | TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)proc; |
---|
| 2754 | |
---|
| 2755 | retCd = (*proc2)(tsdPtr->cwdClientData); |
---|
| 2756 | if (retCd == NULL && interp != NULL) { |
---|
| 2757 | Tcl_AppendResult(interp, |
---|
| 2758 | "error getting working directory name: ", |
---|
| 2759 | Tcl_PosixError(interp), NULL); |
---|
| 2760 | } |
---|
| 2761 | |
---|
| 2762 | if (retCd == tsdPtr->cwdClientData) { |
---|
| 2763 | goto cdDidNotChange; |
---|
| 2764 | } |
---|
| 2765 | |
---|
| 2766 | /* |
---|
| 2767 | * Looks like a new current directory. |
---|
| 2768 | */ |
---|
| 2769 | |
---|
| 2770 | retVal = (*fsPtr->internalToNormalizedProc)(retCd); |
---|
| 2771 | Tcl_IncrRefCount(retVal); |
---|
| 2772 | } else { |
---|
| 2773 | retVal = (*proc)(interp); |
---|
| 2774 | } |
---|
| 2775 | if (retVal != NULL) { |
---|
| 2776 | Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, |
---|
| 2777 | retVal, NULL); |
---|
| 2778 | |
---|
| 2779 | /* |
---|
| 2780 | * Check whether cwd has changed from the value previously |
---|
| 2781 | * stored in cwdPathPtr. Really 'norm' shouldn't be NULL, |
---|
| 2782 | * but we are careful. |
---|
| 2783 | */ |
---|
| 2784 | |
---|
| 2785 | if (norm == NULL) { |
---|
| 2786 | /* Do nothing */ |
---|
| 2787 | if (retCd != NULL) { |
---|
| 2788 | (*fsPtr->freeInternalRepProc)(retCd); |
---|
| 2789 | } |
---|
| 2790 | } else if (norm == tsdPtr->cwdPathPtr) { |
---|
| 2791 | goto cdEqual; |
---|
| 2792 | } else { |
---|
| 2793 | /* |
---|
| 2794 | * Note that both 'norm' and 'tsdPtr->cwdPathPtr' are |
---|
| 2795 | * normalized paths. Therefore we can be more |
---|
| 2796 | * efficient than calling 'Tcl_FSEqualPaths', and in |
---|
| 2797 | * addition avoid a nasty infinite loop bug when |
---|
| 2798 | * trying to normalize tsdPtr->cwdPathPtr. |
---|
| 2799 | */ |
---|
| 2800 | |
---|
| 2801 | int len1, len2; |
---|
| 2802 | char *str1, *str2; |
---|
| 2803 | |
---|
| 2804 | str1 = Tcl_GetStringFromObj(tsdPtr->cwdPathPtr, &len1); |
---|
| 2805 | str2 = Tcl_GetStringFromObj(norm, &len2); |
---|
| 2806 | if ((len1 == len2) && (strcmp(str1, str2) == 0)) { |
---|
| 2807 | /* |
---|
| 2808 | * If the paths were equal, we can be more |
---|
| 2809 | * efficient and retain the old path object which |
---|
| 2810 | * will probably already be shared. In this case |
---|
| 2811 | * we can simply free the normalized path we just |
---|
| 2812 | * calculated. |
---|
| 2813 | */ |
---|
| 2814 | |
---|
| 2815 | cdEqual: |
---|
| 2816 | Tcl_DecrRefCount(norm); |
---|
| 2817 | if (retCd != NULL) { |
---|
| 2818 | (*fsPtr->freeInternalRepProc)(retCd); |
---|
| 2819 | } |
---|
| 2820 | } else { |
---|
| 2821 | FsUpdateCwd(norm, retCd); |
---|
| 2822 | Tcl_DecrRefCount(norm); |
---|
| 2823 | } |
---|
| 2824 | } |
---|
| 2825 | Tcl_DecrRefCount(retVal); |
---|
| 2826 | } else { |
---|
| 2827 | /* |
---|
| 2828 | * The 'cwd' function returned an error; reset the cwd. |
---|
| 2829 | */ |
---|
| 2830 | |
---|
| 2831 | FsUpdateCwd(NULL, NULL); |
---|
| 2832 | } |
---|
| 2833 | } |
---|
| 2834 | } |
---|
| 2835 | } |
---|
| 2836 | |
---|
| 2837 | cdDidNotChange: |
---|
| 2838 | if (tsdPtr->cwdPathPtr != NULL) { |
---|
| 2839 | Tcl_IncrRefCount(tsdPtr->cwdPathPtr); |
---|
| 2840 | } |
---|
| 2841 | |
---|
| 2842 | return tsdPtr->cwdPathPtr; |
---|
| 2843 | } |
---|
| 2844 | |
---|
| 2845 | /* |
---|
| 2846 | *---------------------------------------------------------------------- |
---|
| 2847 | * |
---|
| 2848 | * Tcl_FSChdir -- |
---|
| 2849 | * |
---|
| 2850 | * This function replaces the library version of chdir(). |
---|
| 2851 | * |
---|
| 2852 | * The path is normalized and then passed to the filesystem which claims |
---|
| 2853 | * it. |
---|
| 2854 | * |
---|
| 2855 | * Results: |
---|
| 2856 | * See chdir() documentation. If successful, we keep a record of the |
---|
| 2857 | * successful path in cwdPathPtr for subsequent calls to getcwd. |
---|
| 2858 | * |
---|
| 2859 | * Side effects: |
---|
| 2860 | * See chdir() documentation. The global cwdPathPtr may change value. |
---|
| 2861 | * |
---|
| 2862 | *---------------------------------------------------------------------- |
---|
| 2863 | */ |
---|
| 2864 | |
---|
| 2865 | int |
---|
| 2866 | Tcl_FSChdir( |
---|
| 2867 | Tcl_Obj *pathPtr) |
---|
| 2868 | { |
---|
| 2869 | const Tcl_Filesystem *fsPtr; |
---|
| 2870 | int retVal = -1; |
---|
| 2871 | |
---|
| 2872 | if (Tcl_FSGetNormalizedPath(NULL, pathPtr) == NULL) { |
---|
| 2873 | Tcl_SetErrno(ENOENT); |
---|
| 2874 | return retVal; |
---|
| 2875 | } |
---|
| 2876 | |
---|
| 2877 | fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 2878 | if (fsPtr != NULL) { |
---|
| 2879 | Tcl_FSChdirProc *proc = fsPtr->chdirProc; |
---|
| 2880 | if (proc != NULL) { |
---|
| 2881 | /* |
---|
| 2882 | * If this fails, an appropriate errno will have been stored using |
---|
| 2883 | * 'Tcl_SetErrno()'. |
---|
| 2884 | */ |
---|
| 2885 | |
---|
| 2886 | retVal = (*proc)(pathPtr); |
---|
| 2887 | } else { |
---|
| 2888 | /* |
---|
| 2889 | * Fallback on stat-based implementation. |
---|
| 2890 | */ |
---|
| 2891 | |
---|
| 2892 | Tcl_StatBuf buf; |
---|
| 2893 | |
---|
| 2894 | /* |
---|
| 2895 | * If the file can be stat'ed and is a directory and is readable, |
---|
| 2896 | * then we can chdir. If any of these actions fail, then |
---|
| 2897 | * 'Tcl_SetErrno()' should automatically have been called to set |
---|
| 2898 | * an appropriate error code |
---|
| 2899 | */ |
---|
| 2900 | |
---|
| 2901 | if ((Tcl_FSStat(pathPtr, &buf) == 0) && (S_ISDIR(buf.st_mode)) |
---|
| 2902 | && (Tcl_FSAccess(pathPtr, R_OK) == 0)) { |
---|
| 2903 | /* |
---|
| 2904 | * We allow the chdir. |
---|
| 2905 | */ |
---|
| 2906 | |
---|
| 2907 | retVal = 0; |
---|
| 2908 | } |
---|
| 2909 | } |
---|
| 2910 | } else { |
---|
| 2911 | Tcl_SetErrno(ENOENT); |
---|
| 2912 | } |
---|
| 2913 | |
---|
| 2914 | /* |
---|
| 2915 | * The cwd changed, or an error was thrown. If an error was thrown, we can |
---|
| 2916 | * just continue (and that will report the error to the user). If there |
---|
| 2917 | * was no error we must assume that the cwd was actually changed to the |
---|
| 2918 | * normalized value we calculated above, and we must therefore cache that |
---|
| 2919 | * information. |
---|
| 2920 | */ |
---|
| 2921 | |
---|
| 2922 | /* |
---|
| 2923 | * If the filesystem in question has a getCwdProc, then the correct logic |
---|
| 2924 | * which performs the part below is already part of the Tcl_FSGetCwd() |
---|
| 2925 | * call, so no need to replicate it again. This will have a side effect |
---|
| 2926 | * though. The private authoritative representation of the current working |
---|
| 2927 | * directory stored in cwdPathPtr in static memory will be out-of-sync |
---|
| 2928 | * with the real OS-maintained value. The first call to Tcl_FSGetCwd will |
---|
| 2929 | * however recalculate the private copy to match the OS-value so |
---|
| 2930 | * everything will work right. |
---|
| 2931 | * |
---|
| 2932 | * However, if there is no getCwdProc, then we _must_ update our private |
---|
| 2933 | * storage of the cwd, since this is the only opportunity to do that! |
---|
| 2934 | * |
---|
| 2935 | * Note: We currently call this block of code irrespective of whether |
---|
| 2936 | * there was a getCwdProc or not, but the code should all in principle |
---|
| 2937 | * work if we only call this block if fsPtr->getCwdProc == NULL. |
---|
| 2938 | */ |
---|
| 2939 | |
---|
| 2940 | if (retVal == 0) { |
---|
| 2941 | /* |
---|
| 2942 | * Note that this normalized path may be different to what we found |
---|
| 2943 | * above (or at least a different object), if the filesystem epoch |
---|
| 2944 | * changed recently. This can actually happen with scripted documents |
---|
| 2945 | * very easily. Therefore we ask for the normalized path again (the |
---|
| 2946 | * correct value will have been cached as a result of the |
---|
| 2947 | * Tcl_FSGetFileSystemForPath call above anyway). |
---|
| 2948 | */ |
---|
| 2949 | |
---|
| 2950 | Tcl_Obj *normDirName = Tcl_FSGetNormalizedPath(NULL, pathPtr); |
---|
| 2951 | |
---|
| 2952 | if (normDirName == NULL) { |
---|
| 2953 | /* Not really true, but what else to do? */ |
---|
| 2954 | Tcl_SetErrno(ENOENT); |
---|
| 2955 | return -1; |
---|
| 2956 | } |
---|
| 2957 | |
---|
| 2958 | if (fsPtr == &tclNativeFilesystem) { |
---|
| 2959 | /* |
---|
| 2960 | * For the native filesystem, we keep a cache of the native |
---|
| 2961 | * representation of the cwd. But, we want to do that for the |
---|
| 2962 | * exact format that is returned by 'getcwd' (so that we can later |
---|
| 2963 | * compare the two representations for equality), which might not |
---|
| 2964 | * be exactly the same char-string as the native representation of |
---|
| 2965 | * the fully normalized path (e.g. on Windows there's a |
---|
| 2966 | * forward-slash vs backslash difference). Hence we ask for this |
---|
| 2967 | * again here. On Unix it might actually be true that we always |
---|
| 2968 | * have the correct form in the native rep in which case we could |
---|
| 2969 | * simply use: |
---|
| 2970 | * cd = Tcl_FSGetNativePath(pathPtr); |
---|
| 2971 | * instead. This should be examined by someone on Unix. |
---|
| 2972 | */ |
---|
| 2973 | |
---|
| 2974 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); |
---|
| 2975 | ClientData cd; |
---|
| 2976 | ClientData oldcd = tsdPtr->cwdClientData; |
---|
| 2977 | |
---|
| 2978 | /* |
---|
| 2979 | * Assumption we are using a filesystem version 2. |
---|
| 2980 | */ |
---|
| 2981 | |
---|
| 2982 | TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)fsPtr->getCwdProc; |
---|
| 2983 | cd = (*proc2)(oldcd); |
---|
| 2984 | if (cd != oldcd) { |
---|
| 2985 | FsUpdateCwd(normDirName, cd); |
---|
| 2986 | } |
---|
| 2987 | } else { |
---|
| 2988 | FsUpdateCwd(normDirName, NULL); |
---|
| 2989 | } |
---|
| 2990 | } |
---|
| 2991 | |
---|
| 2992 | return retVal; |
---|
| 2993 | } |
---|
| 2994 | |
---|
| 2995 | /* |
---|
| 2996 | *---------------------------------------------------------------------- |
---|
| 2997 | * |
---|
| 2998 | * Tcl_FSLoadFile -- |
---|
| 2999 | * |
---|
| 3000 | * Dynamically loads a binary code file into memory and returns the |
---|
| 3001 | * addresses of two functions within that file, if they are defined. The |
---|
| 3002 | * appropriate function for the filesystem to which pathPtr belongs will |
---|
| 3003 | * be called. |
---|
| 3004 | * |
---|
| 3005 | * Note that the native filesystem doesn't actually assume 'pathPtr' is a |
---|
| 3006 | * path. Rather it assumes pathPtr is either a path or just the name |
---|
| 3007 | * (tail) of a file which can be found somewhere in the environment's |
---|
| 3008 | * loadable path. This behaviour is not very compatible with virtual |
---|
| 3009 | * filesystems (and has other problems documented in the load man-page), |
---|
| 3010 | * so it is advised that full paths are always used. |
---|
| 3011 | * |
---|
| 3012 | * Results: |
---|
| 3013 | * A standard Tcl completion code. If an error occurs, an error message |
---|
| 3014 | * is left in the interp's result. |
---|
| 3015 | * |
---|
| 3016 | * Side effects: |
---|
| 3017 | * New code suddenly appears in memory. This may later be unloaded by |
---|
| 3018 | * passing the clientData to the unloadProc. |
---|
| 3019 | * |
---|
| 3020 | *---------------------------------------------------------------------- |
---|
| 3021 | */ |
---|
| 3022 | |
---|
| 3023 | int |
---|
| 3024 | Tcl_FSLoadFile( |
---|
| 3025 | Tcl_Interp *interp, /* Used for error reporting. */ |
---|
| 3026 | Tcl_Obj *pathPtr, /* Name of the file containing the desired |
---|
| 3027 | * code. */ |
---|
| 3028 | const char *sym1, const char *sym2, |
---|
| 3029 | /* Names of two functions to look up in the |
---|
| 3030 | * file's symbol table. */ |
---|
| 3031 | Tcl_PackageInitProc **proc1Ptr, Tcl_PackageInitProc **proc2Ptr, |
---|
| 3032 | /* Where to return the addresses corresponding |
---|
| 3033 | * to sym1 and sym2. */ |
---|
| 3034 | Tcl_LoadHandle *handlePtr, /* Filled with token for dynamically loaded |
---|
| 3035 | * file which will be passed back to |
---|
| 3036 | * (*unloadProcPtr)() to unload the file. */ |
---|
| 3037 | Tcl_FSUnloadFileProc **unloadProcPtr) |
---|
| 3038 | /* Filled with address of Tcl_FSUnloadFileProc |
---|
| 3039 | * function which should be used for this |
---|
| 3040 | * file. */ |
---|
| 3041 | { |
---|
| 3042 | const char *symbols[2]; |
---|
| 3043 | Tcl_PackageInitProc **procPtrs[2]; |
---|
| 3044 | ClientData clientData; |
---|
| 3045 | int res; |
---|
| 3046 | |
---|
| 3047 | /* |
---|
| 3048 | * Initialize the arrays. |
---|
| 3049 | */ |
---|
| 3050 | |
---|
| 3051 | symbols[0] = sym1; |
---|
| 3052 | symbols[1] = sym2; |
---|
| 3053 | procPtrs[0] = proc1Ptr; |
---|
| 3054 | procPtrs[1] = proc2Ptr; |
---|
| 3055 | |
---|
| 3056 | /* |
---|
| 3057 | * Perform the load. |
---|
| 3058 | */ |
---|
| 3059 | |
---|
| 3060 | res = TclLoadFile(interp, pathPtr, 2, symbols, procPtrs, handlePtr, |
---|
| 3061 | &clientData, unloadProcPtr); |
---|
| 3062 | |
---|
| 3063 | /* |
---|
| 3064 | * Due to an unfortunate mis-design in Tcl 8.4 fs, when loading a shared |
---|
| 3065 | * library, we don't keep the loadHandle (for TclpFindSymbol) and the |
---|
| 3066 | * clientData (for the unloadProc) separately. In fact we effectively |
---|
| 3067 | * throw away the loadHandle and only use the clientData. It just so |
---|
| 3068 | * happens, for the native filesystem only, that these two are identical. |
---|
| 3069 | * |
---|
| 3070 | * This also means that the signatures Tcl_FSUnloadFileProc and |
---|
| 3071 | * Tcl_FSLoadFileProc are both misleading. |
---|
| 3072 | */ |
---|
| 3073 | |
---|
| 3074 | *handlePtr = (Tcl_LoadHandle) clientData; |
---|
| 3075 | return res; |
---|
| 3076 | } |
---|
| 3077 | |
---|
| 3078 | /* |
---|
| 3079 | *---------------------------------------------------------------------- |
---|
| 3080 | * |
---|
| 3081 | * TclLoadFile -- |
---|
| 3082 | * |
---|
| 3083 | * Dynamically loads a binary code file into memory and returns the |
---|
| 3084 | * addresses of a number of given functions within that file, if they are |
---|
| 3085 | * defined. The appropriate function for the filesystem to which pathPtr |
---|
| 3086 | * belongs will be called. |
---|
| 3087 | * |
---|
| 3088 | * Note that the native filesystem doesn't actually assume 'pathPtr' is a |
---|
| 3089 | * path. Rather it assumes pathPtr is either a path or just the name |
---|
| 3090 | * (tail) of a file which can be found somewhere in the environment's |
---|
| 3091 | * loadable path. This behaviour is not very compatible with virtual |
---|
| 3092 | * filesystems (and has other problems documented in the load man-page), |
---|
| 3093 | * so it is advised that full paths are always used. |
---|
| 3094 | * |
---|
| 3095 | * This function is currently private to Tcl. It may be exported in the |
---|
| 3096 | * future and its interface fixed (but we should clean up the |
---|
| 3097 | * loadHandle/clientData confusion at that time -- see the above comments |
---|
| 3098 | * in Tcl_FSLoadFile for details). For a public function, see |
---|
| 3099 | * Tcl_FSLoadFile. |
---|
| 3100 | * |
---|
| 3101 | * Results: |
---|
| 3102 | * A standard Tcl completion code. If an error occurs, an error message |
---|
| 3103 | * is left in the interp's result. |
---|
| 3104 | * |
---|
| 3105 | * Side effects: |
---|
| 3106 | * New code suddenly appears in memory. This may later be unloaded by |
---|
| 3107 | * passing the clientData to the unloadProc. |
---|
| 3108 | * |
---|
| 3109 | *---------------------------------------------------------------------- |
---|
| 3110 | */ |
---|
| 3111 | |
---|
| 3112 | int |
---|
| 3113 | TclLoadFile( |
---|
| 3114 | Tcl_Interp *interp, /* Used for error reporting. */ |
---|
| 3115 | Tcl_Obj *pathPtr, /* Name of the file containing the desired |
---|
| 3116 | * code. */ |
---|
| 3117 | int symc, /* Number of symbols/procPtrs in the next two |
---|
| 3118 | * arrays. */ |
---|
| 3119 | const char *symbols[], /* Names of functions to look up in the file's |
---|
| 3120 | * symbol table. */ |
---|
| 3121 | Tcl_PackageInitProc **procPtrs[], |
---|
| 3122 | /* Where to return the addresses corresponding |
---|
| 3123 | * to symbols[]. */ |
---|
| 3124 | Tcl_LoadHandle *handlePtr, /* Filled with token for shared library |
---|
| 3125 | * information which can be used in |
---|
| 3126 | * TclpFindSymbol. */ |
---|
| 3127 | ClientData *clientDataPtr, /* Filled with token for dynamically loaded |
---|
| 3128 | * file which will be passed back to |
---|
| 3129 | * (*unloadProcPtr)() to unload the file. */ |
---|
| 3130 | Tcl_FSUnloadFileProc **unloadProcPtr) |
---|
| 3131 | /* Filled with address of Tcl_FSUnloadFileProc |
---|
| 3132 | * function which should be used for this |
---|
| 3133 | * file. */ |
---|
| 3134 | { |
---|
| 3135 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 3136 | Tcl_FSLoadFileProc *proc; |
---|
| 3137 | Tcl_Filesystem *copyFsPtr; |
---|
| 3138 | Tcl_Obj *copyToPtr; |
---|
| 3139 | Tcl_LoadHandle newLoadHandle = NULL; |
---|
| 3140 | ClientData newClientData = NULL; |
---|
| 3141 | Tcl_FSUnloadFileProc *newUnloadProcPtr = NULL; |
---|
| 3142 | FsDivertLoad *tvdlPtr; |
---|
| 3143 | int retVal; |
---|
| 3144 | |
---|
| 3145 | if (fsPtr == NULL) { |
---|
| 3146 | Tcl_SetErrno(ENOENT); |
---|
| 3147 | return TCL_ERROR; |
---|
| 3148 | } |
---|
| 3149 | |
---|
| 3150 | proc = fsPtr->loadFileProc; |
---|
| 3151 | if (proc != NULL) { |
---|
| 3152 | int retVal = (*proc)(interp, pathPtr, handlePtr, unloadProcPtr); |
---|
| 3153 | if (retVal == TCL_OK) { |
---|
| 3154 | if (*handlePtr == NULL) { |
---|
| 3155 | return TCL_ERROR; |
---|
| 3156 | } |
---|
| 3157 | |
---|
| 3158 | /* |
---|
| 3159 | * Copy this across, since both are equal for the native fs. |
---|
| 3160 | */ |
---|
| 3161 | |
---|
| 3162 | *clientDataPtr = (ClientData)*handlePtr; |
---|
| 3163 | Tcl_ResetResult(interp); |
---|
| 3164 | goto resolveSymbols; |
---|
| 3165 | } |
---|
| 3166 | if (Tcl_GetErrno() != EXDEV) { |
---|
| 3167 | return retVal; |
---|
| 3168 | } |
---|
| 3169 | } |
---|
| 3170 | |
---|
| 3171 | /* |
---|
| 3172 | * The filesystem doesn't support 'load', so we fall back on the following |
---|
| 3173 | * technique: |
---|
| 3174 | * |
---|
| 3175 | * First check if it is readable -- and exists! |
---|
| 3176 | */ |
---|
| 3177 | |
---|
| 3178 | if (Tcl_FSAccess(pathPtr, R_OK) != 0) { |
---|
| 3179 | Tcl_AppendResult(interp, "couldn't load library \"", |
---|
| 3180 | Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); |
---|
| 3181 | return TCL_ERROR; |
---|
| 3182 | } |
---|
| 3183 | |
---|
| 3184 | #ifdef TCL_LOAD_FROM_MEMORY |
---|
| 3185 | /* |
---|
| 3186 | * The platform supports loading code from memory, so ask for a buffer of |
---|
| 3187 | * the appropriate size, read the file into it and load the code from the |
---|
| 3188 | * buffer: |
---|
| 3189 | */ |
---|
| 3190 | |
---|
| 3191 | { |
---|
| 3192 | int ret, size; |
---|
| 3193 | void *buffer; |
---|
| 3194 | Tcl_StatBuf statBuf; |
---|
| 3195 | Tcl_Channel data; |
---|
| 3196 | |
---|
| 3197 | ret = Tcl_FSStat(pathPtr, &statBuf); |
---|
| 3198 | if (ret < 0) { |
---|
| 3199 | goto mustCopyToTempAnyway; |
---|
| 3200 | } |
---|
| 3201 | size = (int) statBuf.st_size; |
---|
| 3202 | |
---|
| 3203 | /* |
---|
| 3204 | * Tcl_Read takes an int: check that file size isn't wide. |
---|
| 3205 | */ |
---|
| 3206 | |
---|
| 3207 | if (size != (Tcl_WideInt) statBuf.st_size) { |
---|
| 3208 | goto mustCopyToTempAnyway; |
---|
| 3209 | } |
---|
| 3210 | data = Tcl_FSOpenFileChannel(interp, pathPtr, "rb", 0666); |
---|
| 3211 | if (!data) { |
---|
| 3212 | goto mustCopyToTempAnyway; |
---|
| 3213 | } |
---|
| 3214 | buffer = TclpLoadMemoryGetBuffer(interp, size); |
---|
| 3215 | if (!buffer) { |
---|
| 3216 | Tcl_Close(interp, data); |
---|
| 3217 | goto mustCopyToTempAnyway; |
---|
| 3218 | } |
---|
| 3219 | ret = Tcl_Read(data, buffer, size); |
---|
| 3220 | Tcl_Close(interp, data); |
---|
| 3221 | ret = TclpLoadMemory(interp, buffer, size, ret, handlePtr, |
---|
| 3222 | unloadProcPtr); |
---|
| 3223 | if (ret == TCL_OK && *handlePtr != NULL) { |
---|
| 3224 | *clientDataPtr = (ClientData) *handlePtr; |
---|
| 3225 | goto resolveSymbols; |
---|
| 3226 | } |
---|
| 3227 | } |
---|
| 3228 | |
---|
| 3229 | mustCopyToTempAnyway: |
---|
| 3230 | Tcl_ResetResult(interp); |
---|
| 3231 | #endif |
---|
| 3232 | |
---|
| 3233 | /* |
---|
| 3234 | * Get a temporary filename to use, first to copy the file into, and then |
---|
| 3235 | * to load. |
---|
| 3236 | */ |
---|
| 3237 | |
---|
| 3238 | copyToPtr = TclpTempFileName(); |
---|
| 3239 | if (copyToPtr == NULL) { |
---|
| 3240 | Tcl_AppendResult(interp, "couldn't create temporary file: ", |
---|
| 3241 | Tcl_PosixError(interp), NULL); |
---|
| 3242 | return TCL_ERROR; |
---|
| 3243 | } |
---|
| 3244 | Tcl_IncrRefCount(copyToPtr); |
---|
| 3245 | |
---|
| 3246 | copyFsPtr = Tcl_FSGetFileSystemForPath(copyToPtr); |
---|
| 3247 | if ((copyFsPtr == NULL) || (copyFsPtr == fsPtr)) { |
---|
| 3248 | /* |
---|
| 3249 | * We already know we can't use Tcl_FSLoadFile from this filesystem, |
---|
| 3250 | * and we must avoid a possible infinite loop. Try to delete the file |
---|
| 3251 | * we probably created, and then exit. |
---|
| 3252 | */ |
---|
| 3253 | |
---|
| 3254 | Tcl_FSDeleteFile(copyToPtr); |
---|
| 3255 | Tcl_DecrRefCount(copyToPtr); |
---|
| 3256 | Tcl_AppendResult(interp, "couldn't load from current filesystem",NULL); |
---|
| 3257 | return TCL_ERROR; |
---|
| 3258 | } |
---|
| 3259 | |
---|
| 3260 | if (TclCrossFilesystemCopy(interp, pathPtr, copyToPtr) != TCL_OK) { |
---|
| 3261 | /* |
---|
| 3262 | * Cross-platform copy failed. |
---|
| 3263 | */ |
---|
| 3264 | |
---|
| 3265 | Tcl_FSDeleteFile(copyToPtr); |
---|
| 3266 | Tcl_DecrRefCount(copyToPtr); |
---|
| 3267 | return TCL_ERROR; |
---|
| 3268 | } |
---|
| 3269 | |
---|
| 3270 | #if !defined(__WIN32__) |
---|
| 3271 | /* |
---|
| 3272 | * Do we need to set appropriate permissions on the file? This may be |
---|
| 3273 | * required on some systems. On Unix we could loop over the file |
---|
| 3274 | * attributes, and set any that are called "-permissions" to 0700. However |
---|
| 3275 | * we just do this directly, like this: |
---|
| 3276 | */ |
---|
| 3277 | |
---|
| 3278 | { |
---|
| 3279 | int index; |
---|
| 3280 | Tcl_Obj *perm; |
---|
| 3281 | |
---|
| 3282 | TclNewLiteralStringObj(perm, "0700"); |
---|
| 3283 | Tcl_IncrRefCount(perm); |
---|
| 3284 | if (TclFSFileAttrIndex(copyToPtr, "-permissions", &index) == TCL_OK) { |
---|
| 3285 | Tcl_FSFileAttrsSet(NULL, index, copyToPtr, perm); |
---|
| 3286 | } |
---|
| 3287 | Tcl_DecrRefCount(perm); |
---|
| 3288 | } |
---|
| 3289 | #endif |
---|
| 3290 | |
---|
| 3291 | /* |
---|
| 3292 | * We need to reset the result now, because the cross-filesystem copy may |
---|
| 3293 | * have stored the number of bytes in the result. |
---|
| 3294 | */ |
---|
| 3295 | |
---|
| 3296 | Tcl_ResetResult(interp); |
---|
| 3297 | |
---|
| 3298 | retVal = TclLoadFile(interp, copyToPtr, symc, symbols, procPtrs, |
---|
| 3299 | &newLoadHandle, &newClientData, &newUnloadProcPtr); |
---|
| 3300 | if (retVal != TCL_OK) { |
---|
| 3301 | /* |
---|
| 3302 | * The file didn't load successfully. |
---|
| 3303 | */ |
---|
| 3304 | |
---|
| 3305 | Tcl_FSDeleteFile(copyToPtr); |
---|
| 3306 | Tcl_DecrRefCount(copyToPtr); |
---|
| 3307 | return retVal; |
---|
| 3308 | } |
---|
| 3309 | |
---|
| 3310 | /* |
---|
| 3311 | * Try to delete the file immediately - this is possible in some OSes, and |
---|
| 3312 | * avoids any worries about leaving the copy laying around on exit. |
---|
| 3313 | */ |
---|
| 3314 | |
---|
| 3315 | if (Tcl_FSDeleteFile(copyToPtr) == TCL_OK) { |
---|
| 3316 | Tcl_DecrRefCount(copyToPtr); |
---|
| 3317 | |
---|
| 3318 | /* |
---|
| 3319 | * We tell our caller about the real shared library which was loaded. |
---|
| 3320 | * Note that this does mean that the package list maintained by 'load' |
---|
| 3321 | * will store the original (vfs) path alongside the temporary load |
---|
| 3322 | * handle and unload proc ptr. |
---|
| 3323 | */ |
---|
| 3324 | |
---|
| 3325 | (*handlePtr) = newLoadHandle; |
---|
| 3326 | (*clientDataPtr) = newClientData; |
---|
| 3327 | (*unloadProcPtr) = newUnloadProcPtr; |
---|
| 3328 | Tcl_ResetResult(interp); |
---|
| 3329 | return TCL_OK; |
---|
| 3330 | } |
---|
| 3331 | |
---|
| 3332 | /* |
---|
| 3333 | * When we unload this file, we need to divert the unloading so we can |
---|
| 3334 | * unload and cleanup the temporary file correctly. |
---|
| 3335 | */ |
---|
| 3336 | |
---|
| 3337 | tvdlPtr = (FsDivertLoad *) ckalloc(sizeof(FsDivertLoad)); |
---|
| 3338 | |
---|
| 3339 | /* |
---|
| 3340 | * Remember three pieces of information. This allows us to cleanup the |
---|
| 3341 | * diverted load completely, on platforms which allow proper unloading of |
---|
| 3342 | * code. |
---|
| 3343 | */ |
---|
| 3344 | |
---|
| 3345 | tvdlPtr->loadHandle = newLoadHandle; |
---|
| 3346 | tvdlPtr->unloadProcPtr = newUnloadProcPtr; |
---|
| 3347 | |
---|
| 3348 | if (copyFsPtr != &tclNativeFilesystem) { |
---|
| 3349 | /* |
---|
| 3350 | * copyToPtr is already incremented for this reference. |
---|
| 3351 | */ |
---|
| 3352 | |
---|
| 3353 | tvdlPtr->divertedFile = copyToPtr; |
---|
| 3354 | |
---|
| 3355 | /* |
---|
| 3356 | * This is the filesystem we loaded it into. Since we have a reference |
---|
| 3357 | * to 'copyToPtr', we already have a refCount on this filesystem, so |
---|
| 3358 | * we don't need to worry about it disappearing on us. |
---|
| 3359 | */ |
---|
| 3360 | |
---|
| 3361 | tvdlPtr->divertedFilesystem = copyFsPtr; |
---|
| 3362 | tvdlPtr->divertedFileNativeRep = NULL; |
---|
| 3363 | } else { |
---|
| 3364 | /* |
---|
| 3365 | * We need the native rep. |
---|
| 3366 | */ |
---|
| 3367 | |
---|
| 3368 | tvdlPtr->divertedFileNativeRep = TclNativeDupInternalRep( |
---|
| 3369 | Tcl_FSGetInternalRep(copyToPtr, copyFsPtr)); |
---|
| 3370 | |
---|
| 3371 | /* |
---|
| 3372 | * We don't need or want references to the copied Tcl_Obj or the |
---|
| 3373 | * filesystem if it is the native one. |
---|
| 3374 | */ |
---|
| 3375 | |
---|
| 3376 | tvdlPtr->divertedFile = NULL; |
---|
| 3377 | tvdlPtr->divertedFilesystem = NULL; |
---|
| 3378 | Tcl_DecrRefCount(copyToPtr); |
---|
| 3379 | } |
---|
| 3380 | |
---|
| 3381 | copyToPtr = NULL; |
---|
| 3382 | (*handlePtr) = newLoadHandle; |
---|
| 3383 | (*clientDataPtr) = (ClientData) tvdlPtr; |
---|
| 3384 | (*unloadProcPtr) = &FSUnloadTempFile; |
---|
| 3385 | |
---|
| 3386 | Tcl_ResetResult(interp); |
---|
| 3387 | return retVal; |
---|
| 3388 | |
---|
| 3389 | resolveSymbols: |
---|
| 3390 | { |
---|
| 3391 | int i; |
---|
| 3392 | |
---|
| 3393 | for (i=0 ; i<symc ; i++) { |
---|
| 3394 | if (symbols[i] != NULL) { |
---|
| 3395 | *procPtrs[i] = TclpFindSymbol(interp, *handlePtr, symbols[i]); |
---|
| 3396 | } |
---|
| 3397 | } |
---|
| 3398 | } |
---|
| 3399 | return TCL_OK; |
---|
| 3400 | } |
---|
| 3401 | /* |
---|
| 3402 | * This function used to be in the platform specific directories, but it has |
---|
| 3403 | * now been made to work cross-platform |
---|
| 3404 | */ |
---|
| 3405 | |
---|
| 3406 | int |
---|
| 3407 | TclpLoadFile( |
---|
| 3408 | Tcl_Interp *interp, /* Used for error reporting. */ |
---|
| 3409 | Tcl_Obj *pathPtr, /* Name of the file containing the desired |
---|
| 3410 | * code (UTF-8). */ |
---|
| 3411 | const char *sym1, CONST char *sym2, |
---|
| 3412 | /* Names of two functions to look up in the |
---|
| 3413 | * file's symbol table. */ |
---|
| 3414 | Tcl_PackageInitProc **proc1Ptr, Tcl_PackageInitProc **proc2Ptr, |
---|
| 3415 | /* Where to return the addresses corresponding |
---|
| 3416 | * to sym1 and sym2. */ |
---|
| 3417 | ClientData *clientDataPtr, /* Filled with token for dynamically loaded |
---|
| 3418 | * file which will be passed back to |
---|
| 3419 | * (*unloadProcPtr)() to unload the file. */ |
---|
| 3420 | Tcl_FSUnloadFileProc **unloadProcPtr) |
---|
| 3421 | /* Filled with address of Tcl_FSUnloadFileProc |
---|
| 3422 | * function which should be used for this |
---|
| 3423 | * file. */ |
---|
| 3424 | { |
---|
| 3425 | Tcl_LoadHandle handle = NULL; |
---|
| 3426 | int res; |
---|
| 3427 | |
---|
| 3428 | res = TclpDlopen(interp, pathPtr, &handle, unloadProcPtr); |
---|
| 3429 | |
---|
| 3430 | if (res != TCL_OK) { |
---|
| 3431 | return res; |
---|
| 3432 | } |
---|
| 3433 | |
---|
| 3434 | if (handle == NULL) { |
---|
| 3435 | return TCL_ERROR; |
---|
| 3436 | } |
---|
| 3437 | |
---|
| 3438 | *clientDataPtr = (ClientData) handle; |
---|
| 3439 | |
---|
| 3440 | *proc1Ptr = TclpFindSymbol(interp, handle, sym1); |
---|
| 3441 | *proc2Ptr = TclpFindSymbol(interp, handle, sym2); |
---|
| 3442 | return TCL_OK; |
---|
| 3443 | } |
---|
| 3444 | |
---|
| 3445 | /* |
---|
| 3446 | *--------------------------------------------------------------------------- |
---|
| 3447 | * |
---|
| 3448 | * FSUnloadTempFile -- |
---|
| 3449 | * |
---|
| 3450 | * This function is called when we loaded a library of code via an |
---|
| 3451 | * intermediate temporary file. This function ensures the library is |
---|
| 3452 | * correctly unloaded and the temporary file is correctly deleted. |
---|
| 3453 | * |
---|
| 3454 | * Results: |
---|
| 3455 | * None. |
---|
| 3456 | * |
---|
| 3457 | * Side effects: |
---|
| 3458 | * The effects of the 'unload' function called, and of course the |
---|
| 3459 | * temporary file will be deleted. |
---|
| 3460 | * |
---|
| 3461 | *--------------------------------------------------------------------------- |
---|
| 3462 | */ |
---|
| 3463 | |
---|
| 3464 | static void |
---|
| 3465 | FSUnloadTempFile( |
---|
| 3466 | Tcl_LoadHandle loadHandle) /* loadHandle returned by a previous call to |
---|
| 3467 | * Tcl_FSLoadFile(). The loadHandle is a token |
---|
| 3468 | * that represents the loaded file. */ |
---|
| 3469 | { |
---|
| 3470 | FsDivertLoad *tvdlPtr = (FsDivertLoad *) loadHandle; |
---|
| 3471 | |
---|
| 3472 | /* |
---|
| 3473 | * This test should never trigger, since we give the client data in the |
---|
| 3474 | * function above. |
---|
| 3475 | */ |
---|
| 3476 | |
---|
| 3477 | if (tvdlPtr == NULL) { |
---|
| 3478 | return; |
---|
| 3479 | } |
---|
| 3480 | |
---|
| 3481 | /* |
---|
| 3482 | * Call the real 'unloadfile' proc we actually used. It is very important |
---|
| 3483 | * that we call this first, so that the shared library is actually |
---|
| 3484 | * unloaded by the OS. Otherwise, the following 'delete' may well fail |
---|
| 3485 | * because the shared library is still in use. |
---|
| 3486 | */ |
---|
| 3487 | |
---|
| 3488 | if (tvdlPtr->unloadProcPtr != NULL) { |
---|
| 3489 | (*tvdlPtr->unloadProcPtr)(tvdlPtr->loadHandle); |
---|
| 3490 | } |
---|
| 3491 | |
---|
| 3492 | if (tvdlPtr->divertedFilesystem == NULL) { |
---|
| 3493 | /* |
---|
| 3494 | * It was the native filesystem, and we have a special function |
---|
| 3495 | * available just for this purpose, which we know works even at this |
---|
| 3496 | * late stage. |
---|
| 3497 | */ |
---|
| 3498 | |
---|
| 3499 | TclpDeleteFile(tvdlPtr->divertedFileNativeRep); |
---|
| 3500 | NativeFreeInternalRep(tvdlPtr->divertedFileNativeRep); |
---|
| 3501 | |
---|
| 3502 | } else { |
---|
| 3503 | /* |
---|
| 3504 | * Remove the temporary file we created. Note, we may crash here |
---|
| 3505 | * because encodings have been taken down already. |
---|
| 3506 | */ |
---|
| 3507 | |
---|
| 3508 | if (tvdlPtr->divertedFilesystem->deleteFileProc(tvdlPtr->divertedFile) |
---|
| 3509 | != TCL_OK) { |
---|
| 3510 | /* |
---|
| 3511 | * The above may have failed because the filesystem, or something |
---|
| 3512 | * it depends upon (e.g. encodings) have been taken down because |
---|
| 3513 | * Tcl is exiting. |
---|
| 3514 | * |
---|
| 3515 | * We may need to work out how to delete this file more robustly |
---|
| 3516 | * (or give the filesystem the information it needs to delete the |
---|
| 3517 | * file more robustly). |
---|
| 3518 | * |
---|
| 3519 | * In particular, one problem might be that the filesystem cannot |
---|
| 3520 | * extract the information it needs from the above path object |
---|
| 3521 | * because Tcl's entire filesystem apparatus (the code in this |
---|
| 3522 | * file) has been finalized, and it refuses to pass the internal |
---|
| 3523 | * representation to the filesystem. |
---|
| 3524 | */ |
---|
| 3525 | } |
---|
| 3526 | |
---|
| 3527 | /* |
---|
| 3528 | * And free up the allocations. This will also of course remove a |
---|
| 3529 | * refCount from the Tcl_Filesystem to which this file belongs, which |
---|
| 3530 | * could then free up the filesystem if we are exiting. |
---|
| 3531 | */ |
---|
| 3532 | |
---|
| 3533 | Tcl_DecrRefCount(tvdlPtr->divertedFile); |
---|
| 3534 | } |
---|
| 3535 | |
---|
| 3536 | ckfree((char*)tvdlPtr); |
---|
| 3537 | } |
---|
| 3538 | |
---|
| 3539 | /* |
---|
| 3540 | *--------------------------------------------------------------------------- |
---|
| 3541 | * |
---|
| 3542 | * Tcl_FSLink -- |
---|
| 3543 | * |
---|
| 3544 | * This function replaces the library version of readlink() and can also |
---|
| 3545 | * be used to make links. The appropriate function for the filesystem to |
---|
| 3546 | * which pathPtr belongs will be called. |
---|
| 3547 | * |
---|
| 3548 | * Results: |
---|
| 3549 | * If toPtr is NULL, then the result is a Tcl_Obj specifying the contents |
---|
| 3550 | * of the symbolic link given by 'pathPtr', or NULL if the symbolic link |
---|
| 3551 | * could not be read. The result is owned by the caller, which should |
---|
| 3552 | * call Tcl_DecrRefCount when the result is no longer needed. |
---|
| 3553 | * |
---|
| 3554 | * If toPtr is non-NULL, then the result is toPtr if the link action was |
---|
| 3555 | * successful, or NULL if not. In this case the result has no additional |
---|
| 3556 | * reference count, and need not be freed. The actual action to perform |
---|
| 3557 | * is given by the 'linkAction' flags, which is an or'd combination of: |
---|
| 3558 | * |
---|
| 3559 | * TCL_CREATE_SYMBOLIC_LINK |
---|
| 3560 | * TCL_CREATE_HARD_LINK |
---|
| 3561 | * |
---|
| 3562 | * Note that most filesystems will not support linking across to |
---|
| 3563 | * different filesystems, so this function will usually fail unless toPtr |
---|
| 3564 | * is in the same FS as pathPtr. |
---|
| 3565 | * |
---|
| 3566 | * Side effects: |
---|
| 3567 | * See readlink() documentation. A new filesystem link object may appear. |
---|
| 3568 | * |
---|
| 3569 | *--------------------------------------------------------------------------- |
---|
| 3570 | */ |
---|
| 3571 | |
---|
| 3572 | Tcl_Obj * |
---|
| 3573 | Tcl_FSLink( |
---|
| 3574 | Tcl_Obj *pathPtr, /* Path of file to readlink or link */ |
---|
| 3575 | Tcl_Obj *toPtr, /* NULL or path to be linked to */ |
---|
| 3576 | int linkAction) /* Action to perform */ |
---|
| 3577 | { |
---|
| 3578 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 3579 | |
---|
| 3580 | if (fsPtr != NULL) { |
---|
| 3581 | Tcl_FSLinkProc *proc = fsPtr->linkProc; |
---|
| 3582 | |
---|
| 3583 | if (proc != NULL) { |
---|
| 3584 | return (*proc)(pathPtr, toPtr, linkAction); |
---|
| 3585 | } |
---|
| 3586 | } |
---|
| 3587 | |
---|
| 3588 | /* |
---|
| 3589 | * If S_IFLNK isn't defined it means that the machine doesn't support |
---|
| 3590 | * symbolic links, so the file can't possibly be a symbolic link. Generate |
---|
| 3591 | * an EINVAL error, which is what happens on machines that do support |
---|
| 3592 | * symbolic links when you invoke readlink on a file that isn't a symbolic |
---|
| 3593 | * link. |
---|
| 3594 | */ |
---|
| 3595 | |
---|
| 3596 | #ifndef S_IFLNK |
---|
| 3597 | errno = EINVAL; |
---|
| 3598 | #else |
---|
| 3599 | Tcl_SetErrno(ENOENT); |
---|
| 3600 | #endif /* S_IFLNK */ |
---|
| 3601 | return NULL; |
---|
| 3602 | } |
---|
| 3603 | |
---|
| 3604 | /* |
---|
| 3605 | *--------------------------------------------------------------------------- |
---|
| 3606 | * |
---|
| 3607 | * Tcl_FSListVolumes -- |
---|
| 3608 | * |
---|
| 3609 | * Lists the currently mounted volumes. The chain of functions that have |
---|
| 3610 | * been "inserted" into the filesystem will be called in succession; each |
---|
| 3611 | * may return a list of volumes, all of which are added to the result |
---|
| 3612 | * until all mounted file systems are listed. |
---|
| 3613 | * |
---|
| 3614 | * Notice that we assume the lists returned by each filesystem (if non |
---|
| 3615 | * NULL) have been given a refCount for us already. However, we are NOT |
---|
| 3616 | * allowed to hang on to the list itself (it belongs to the filesystem we |
---|
| 3617 | * called). Therefore we quite naturally add its contents to the result |
---|
| 3618 | * we are building, and then decrement the refCount. |
---|
| 3619 | * |
---|
| 3620 | * Results: |
---|
| 3621 | * The list of volumes, in an object which has refCount 0. |
---|
| 3622 | * |
---|
| 3623 | * Side effects: |
---|
| 3624 | * None |
---|
| 3625 | * |
---|
| 3626 | *--------------------------------------------------------------------------- |
---|
| 3627 | */ |
---|
| 3628 | |
---|
| 3629 | Tcl_Obj* |
---|
| 3630 | Tcl_FSListVolumes(void) |
---|
| 3631 | { |
---|
| 3632 | FilesystemRecord *fsRecPtr; |
---|
| 3633 | Tcl_Obj *resultPtr = Tcl_NewObj(); |
---|
| 3634 | |
---|
| 3635 | /* |
---|
| 3636 | * Call each of the "listVolumes" function in succession. A non-NULL |
---|
| 3637 | * return value indicates the particular function has succeeded. We call |
---|
| 3638 | * all the functions registered, since we want a list of all drives from |
---|
| 3639 | * all filesystems. |
---|
| 3640 | */ |
---|
| 3641 | |
---|
| 3642 | fsRecPtr = FsGetFirstFilesystem(); |
---|
| 3643 | while (fsRecPtr != NULL) { |
---|
| 3644 | Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc; |
---|
| 3645 | if (proc != NULL) { |
---|
| 3646 | Tcl_Obj *thisFsVolumes = (*proc)(); |
---|
| 3647 | if (thisFsVolumes != NULL) { |
---|
| 3648 | Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes); |
---|
| 3649 | Tcl_DecrRefCount(thisFsVolumes); |
---|
| 3650 | } |
---|
| 3651 | } |
---|
| 3652 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 3653 | } |
---|
| 3654 | |
---|
| 3655 | return resultPtr; |
---|
| 3656 | } |
---|
| 3657 | |
---|
| 3658 | /* |
---|
| 3659 | *--------------------------------------------------------------------------- |
---|
| 3660 | * |
---|
| 3661 | * FsListMounts -- |
---|
| 3662 | * |
---|
| 3663 | * List all mounts within the given directory, which match the given |
---|
| 3664 | * pattern. |
---|
| 3665 | * |
---|
| 3666 | * Results: |
---|
| 3667 | * The list of mounts, in a list object which has refCount 0, or NULL if |
---|
| 3668 | * we didn't even find any filesystems to try to list mounts. |
---|
| 3669 | * |
---|
| 3670 | * Side effects: |
---|
| 3671 | * None |
---|
| 3672 | * |
---|
| 3673 | *--------------------------------------------------------------------------- |
---|
| 3674 | */ |
---|
| 3675 | |
---|
| 3676 | static Tcl_Obj * |
---|
| 3677 | FsListMounts( |
---|
| 3678 | Tcl_Obj *pathPtr, /* Contains path to directory to search. */ |
---|
| 3679 | const char *pattern) /* Pattern to match against. */ |
---|
| 3680 | { |
---|
| 3681 | FilesystemRecord *fsRecPtr; |
---|
| 3682 | Tcl_GlobTypeData mountsOnly = { TCL_GLOB_TYPE_MOUNT, 0, NULL, NULL }; |
---|
| 3683 | Tcl_Obj *resultPtr = NULL; |
---|
| 3684 | |
---|
| 3685 | /* |
---|
| 3686 | * Call each of the "matchInDirectory" functions in succession, with the |
---|
| 3687 | * specific type information 'mountsOnly'. A non-NULL return value |
---|
| 3688 | * indicates the particular function has succeeded. We call all the |
---|
| 3689 | * functions registered, since we want a list from each filesystems. |
---|
| 3690 | */ |
---|
| 3691 | |
---|
| 3692 | fsRecPtr = FsGetFirstFilesystem(); |
---|
| 3693 | while (fsRecPtr != NULL) { |
---|
| 3694 | if (fsRecPtr->fsPtr != &tclNativeFilesystem) { |
---|
| 3695 | Tcl_FSMatchInDirectoryProc *proc = |
---|
| 3696 | fsRecPtr->fsPtr->matchInDirectoryProc; |
---|
| 3697 | if (proc != NULL) { |
---|
| 3698 | if (resultPtr == NULL) { |
---|
| 3699 | resultPtr = Tcl_NewObj(); |
---|
| 3700 | } |
---|
| 3701 | (*proc)(NULL, resultPtr, pathPtr, pattern, &mountsOnly); |
---|
| 3702 | } |
---|
| 3703 | } |
---|
| 3704 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 3705 | } |
---|
| 3706 | |
---|
| 3707 | return resultPtr; |
---|
| 3708 | } |
---|
| 3709 | |
---|
| 3710 | /* |
---|
| 3711 | *--------------------------------------------------------------------------- |
---|
| 3712 | * |
---|
| 3713 | * Tcl_FSSplitPath -- |
---|
| 3714 | * |
---|
| 3715 | * This function takes the given Tcl_Obj, which should be a valid path, |
---|
| 3716 | * and returns a Tcl List object containing each segment of that path as |
---|
| 3717 | * an element. |
---|
| 3718 | * |
---|
| 3719 | * Results: |
---|
| 3720 | * Returns list object with refCount of zero. If the passed in lenPtr is |
---|
| 3721 | * non-NULL, we use it to return the number of elements in the returned |
---|
| 3722 | * list. |
---|
| 3723 | * |
---|
| 3724 | * Side effects: |
---|
| 3725 | * None. |
---|
| 3726 | * |
---|
| 3727 | *--------------------------------------------------------------------------- |
---|
| 3728 | */ |
---|
| 3729 | |
---|
| 3730 | Tcl_Obj * |
---|
| 3731 | Tcl_FSSplitPath( |
---|
| 3732 | Tcl_Obj *pathPtr, /* Path to split. */ |
---|
| 3733 | int *lenPtr) /* int to store number of path elements. */ |
---|
| 3734 | { |
---|
| 3735 | Tcl_Obj *result = NULL; /* Needed only to prevent gcc warnings. */ |
---|
| 3736 | Tcl_Filesystem *fsPtr; |
---|
| 3737 | char separator = '/'; |
---|
| 3738 | int driveNameLength; |
---|
| 3739 | char *p; |
---|
| 3740 | |
---|
| 3741 | /* |
---|
| 3742 | * Perform platform specific splitting. |
---|
| 3743 | */ |
---|
| 3744 | |
---|
| 3745 | if (TclFSGetPathType(pathPtr, &fsPtr, |
---|
| 3746 | &driveNameLength) == TCL_PATH_ABSOLUTE) { |
---|
| 3747 | if (fsPtr == &tclNativeFilesystem) { |
---|
| 3748 | return TclpNativeSplitPath(pathPtr, lenPtr); |
---|
| 3749 | } |
---|
| 3750 | } else { |
---|
| 3751 | return TclpNativeSplitPath(pathPtr, lenPtr); |
---|
| 3752 | } |
---|
| 3753 | |
---|
| 3754 | /* |
---|
| 3755 | * We assume separators are single characters. |
---|
| 3756 | */ |
---|
| 3757 | |
---|
| 3758 | if (fsPtr->filesystemSeparatorProc != NULL) { |
---|
| 3759 | Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(pathPtr); |
---|
| 3760 | if (sep != NULL) { |
---|
| 3761 | Tcl_IncrRefCount(sep); |
---|
| 3762 | separator = Tcl_GetString(sep)[0]; |
---|
| 3763 | Tcl_DecrRefCount(sep); |
---|
| 3764 | } |
---|
| 3765 | } |
---|
| 3766 | |
---|
| 3767 | /* |
---|
| 3768 | * Place the drive name as first element of the result list. The drive |
---|
| 3769 | * name may contain strange characters, like colons and multiple forward |
---|
| 3770 | * slashes (for example 'ftp://' is a valid vfs drive name) |
---|
| 3771 | */ |
---|
| 3772 | |
---|
| 3773 | result = Tcl_NewObj(); |
---|
| 3774 | p = Tcl_GetString(pathPtr); |
---|
| 3775 | Tcl_ListObjAppendElement(NULL, result, |
---|
| 3776 | Tcl_NewStringObj(p, driveNameLength)); |
---|
| 3777 | p += driveNameLength; |
---|
| 3778 | |
---|
| 3779 | /* |
---|
| 3780 | * Add the remaining path elements to the list. |
---|
| 3781 | */ |
---|
| 3782 | |
---|
| 3783 | for (;;) { |
---|
| 3784 | char *elementStart = p; |
---|
| 3785 | int length; |
---|
| 3786 | while ((*p != '\0') && (*p != separator)) { |
---|
| 3787 | p++; |
---|
| 3788 | } |
---|
| 3789 | length = p - elementStart; |
---|
| 3790 | if (length > 0) { |
---|
| 3791 | Tcl_Obj *nextElt; |
---|
| 3792 | if (elementStart[0] == '~') { |
---|
| 3793 | TclNewLiteralStringObj(nextElt, "./"); |
---|
| 3794 | Tcl_AppendToObj(nextElt, elementStart, length); |
---|
| 3795 | } else { |
---|
| 3796 | nextElt = Tcl_NewStringObj(elementStart, length); |
---|
| 3797 | } |
---|
| 3798 | Tcl_ListObjAppendElement(NULL, result, nextElt); |
---|
| 3799 | } |
---|
| 3800 | if (*p++ == '\0') { |
---|
| 3801 | break; |
---|
| 3802 | } |
---|
| 3803 | } |
---|
| 3804 | |
---|
| 3805 | /* |
---|
| 3806 | * Compute the number of elements in the result. |
---|
| 3807 | */ |
---|
| 3808 | |
---|
| 3809 | if (lenPtr != NULL) { |
---|
| 3810 | TclListObjLength(NULL, result, lenPtr); |
---|
| 3811 | } |
---|
| 3812 | return result; |
---|
| 3813 | } |
---|
| 3814 | |
---|
| 3815 | /* Simple helper function */ |
---|
| 3816 | Tcl_Obj * |
---|
| 3817 | TclFSInternalToNormalized( |
---|
| 3818 | Tcl_Filesystem *fromFilesystem, |
---|
| 3819 | ClientData clientData, |
---|
| 3820 | FilesystemRecord **fsRecPtrPtr) |
---|
| 3821 | { |
---|
| 3822 | FilesystemRecord *fsRecPtr = FsGetFirstFilesystem(); |
---|
| 3823 | |
---|
| 3824 | while (fsRecPtr != NULL) { |
---|
| 3825 | if (fsRecPtr->fsPtr == fromFilesystem) { |
---|
| 3826 | *fsRecPtrPtr = fsRecPtr; |
---|
| 3827 | break; |
---|
| 3828 | } |
---|
| 3829 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 3830 | } |
---|
| 3831 | |
---|
| 3832 | if ((fsRecPtr != NULL) |
---|
| 3833 | && (fromFilesystem->internalToNormalizedProc != NULL)) { |
---|
| 3834 | return (*fromFilesystem->internalToNormalizedProc)(clientData); |
---|
| 3835 | } else { |
---|
| 3836 | return NULL; |
---|
| 3837 | } |
---|
| 3838 | } |
---|
| 3839 | |
---|
| 3840 | /* |
---|
| 3841 | *---------------------------------------------------------------------- |
---|
| 3842 | * |
---|
| 3843 | * TclGetPathType -- |
---|
| 3844 | * |
---|
| 3845 | * Helper function used by FSGetPathType. |
---|
| 3846 | * |
---|
| 3847 | * Results: |
---|
| 3848 | * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or |
---|
| 3849 | * TCL_PATH_VOLUME_RELATIVE. The filesystem reference will be set if and |
---|
| 3850 | * only if it is non-NULL and the function's return value is |
---|
| 3851 | * TCL_PATH_ABSOLUTE. |
---|
| 3852 | * |
---|
| 3853 | * Side effects: |
---|
| 3854 | * None. |
---|
| 3855 | * |
---|
| 3856 | *---------------------------------------------------------------------- |
---|
| 3857 | */ |
---|
| 3858 | |
---|
| 3859 | Tcl_PathType |
---|
| 3860 | TclGetPathType( |
---|
| 3861 | Tcl_Obj *pathPtr, /* Path to determine type for */ |
---|
| 3862 | Tcl_Filesystem **filesystemPtrPtr, |
---|
| 3863 | /* If absolute path and this is not NULL, then |
---|
| 3864 | * set to the filesystem which claims this |
---|
| 3865 | * path. */ |
---|
| 3866 | int *driveNameLengthPtr, /* If the path is absolute, and this is |
---|
| 3867 | * non-NULL, then set to the length of the |
---|
| 3868 | * driveName. */ |
---|
| 3869 | Tcl_Obj **driveNameRef) /* If the path is absolute, and this is |
---|
| 3870 | * non-NULL, then set to the name of the |
---|
| 3871 | * drive, network-volume which contains the |
---|
| 3872 | * path, already with a refCount for the |
---|
| 3873 | * caller. */ |
---|
| 3874 | { |
---|
| 3875 | int pathLen; |
---|
| 3876 | char *path; |
---|
| 3877 | Tcl_PathType type; |
---|
| 3878 | |
---|
| 3879 | path = Tcl_GetStringFromObj(pathPtr, &pathLen); |
---|
| 3880 | |
---|
| 3881 | type = TclFSNonnativePathType(path, pathLen, filesystemPtrPtr, |
---|
| 3882 | driveNameLengthPtr, driveNameRef); |
---|
| 3883 | |
---|
| 3884 | if (type != TCL_PATH_ABSOLUTE) { |
---|
| 3885 | type = TclpGetNativePathType(pathPtr, driveNameLengthPtr, |
---|
| 3886 | driveNameRef); |
---|
| 3887 | if ((type == TCL_PATH_ABSOLUTE) && (filesystemPtrPtr != NULL)) { |
---|
| 3888 | *filesystemPtrPtr = &tclNativeFilesystem; |
---|
| 3889 | } |
---|
| 3890 | } |
---|
| 3891 | return type; |
---|
| 3892 | } |
---|
| 3893 | |
---|
| 3894 | /* |
---|
| 3895 | *---------------------------------------------------------------------- |
---|
| 3896 | * |
---|
| 3897 | * TclFSNonnativePathType -- |
---|
| 3898 | * |
---|
| 3899 | * Helper function used by TclGetPathType. Its purpose is to check |
---|
| 3900 | * whether the given path starts with a string which corresponds to a |
---|
| 3901 | * file volume in any registered filesystem except the native one. For |
---|
| 3902 | * speed and historical reasons the native filesystem has special |
---|
| 3903 | * hard-coded checks dotted here and there in the filesystem code. |
---|
| 3904 | * |
---|
| 3905 | * Results: |
---|
| 3906 | * Returns one of TCL_PATH_ABSOLUTE or TCL_PATH_RELATIVE. The filesystem |
---|
| 3907 | * reference will be set if and only if it is non-NULL and the function's |
---|
| 3908 | * return value is TCL_PATH_ABSOLUTE. |
---|
| 3909 | * |
---|
| 3910 | * Side effects: |
---|
| 3911 | * None. |
---|
| 3912 | * |
---|
| 3913 | *---------------------------------------------------------------------- |
---|
| 3914 | */ |
---|
| 3915 | |
---|
| 3916 | Tcl_PathType |
---|
| 3917 | TclFSNonnativePathType( |
---|
| 3918 | const char *path, /* Path to determine type for */ |
---|
| 3919 | int pathLen, /* Length of the path */ |
---|
| 3920 | Tcl_Filesystem **filesystemPtrPtr, |
---|
| 3921 | /* If absolute path and this is not NULL, then |
---|
| 3922 | * set to the filesystem which claims this |
---|
| 3923 | * path. */ |
---|
| 3924 | int *driveNameLengthPtr, /* If the path is absolute, and this is |
---|
| 3925 | * non-NULL, then set to the length of the |
---|
| 3926 | * driveName. */ |
---|
| 3927 | Tcl_Obj **driveNameRef) /* If the path is absolute, and this is |
---|
| 3928 | * non-NULL, then set to the name of the |
---|
| 3929 | * drive, network-volume which contains the |
---|
| 3930 | * path, already with a refCount for the |
---|
| 3931 | * caller. */ |
---|
| 3932 | { |
---|
| 3933 | FilesystemRecord *fsRecPtr; |
---|
| 3934 | Tcl_PathType type = TCL_PATH_RELATIVE; |
---|
| 3935 | |
---|
| 3936 | /* |
---|
| 3937 | * Call each of the "listVolumes" function in succession, checking whether |
---|
| 3938 | * the given path is an absolute path on any of the volumes returned (this |
---|
| 3939 | * is done by checking whether the path's prefix matches). |
---|
| 3940 | */ |
---|
| 3941 | |
---|
| 3942 | fsRecPtr = FsGetFirstFilesystem(); |
---|
| 3943 | while (fsRecPtr != NULL) { |
---|
| 3944 | Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc; |
---|
| 3945 | |
---|
| 3946 | /* |
---|
| 3947 | * We want to skip the native filesystem in this loop because |
---|
| 3948 | * otherwise we won't necessarily pass all the Tcl testsuite -- this |
---|
| 3949 | * is because some of the tests artificially change the current |
---|
| 3950 | * platform (between win, unix) but the list of volumes we get by |
---|
| 3951 | * calling (*proc) will reflect the current (real) platform only and |
---|
| 3952 | * this may cause some tests to fail. In particular, on unix '/' will |
---|
| 3953 | * match the beginning of certain absolute Windows paths starting '//' |
---|
| 3954 | * and those tests will go wrong. |
---|
| 3955 | * |
---|
| 3956 | * Besides these test-suite issues, there is one other reason to skip |
---|
| 3957 | * the native filesystem --- since the tclFilename.c code has nice |
---|
| 3958 | * fast 'absolute path' checkers, we don't want to waste time |
---|
| 3959 | * repeating that effort here, and this function is actually called |
---|
| 3960 | * quite often, so if we can save the overhead of the native |
---|
| 3961 | * filesystem returning us a list of volumes all the time, it is |
---|
| 3962 | * better. |
---|
| 3963 | */ |
---|
| 3964 | |
---|
| 3965 | if ((fsRecPtr->fsPtr != &tclNativeFilesystem) && (proc != NULL)) { |
---|
| 3966 | int numVolumes; |
---|
| 3967 | Tcl_Obj *thisFsVolumes = (*proc)(); |
---|
| 3968 | |
---|
| 3969 | if (thisFsVolumes != NULL) { |
---|
| 3970 | if (Tcl_ListObjLength(NULL, thisFsVolumes, &numVolumes) |
---|
| 3971 | != TCL_OK) { |
---|
| 3972 | /* |
---|
| 3973 | * This is VERY bad; the Tcl_FSListVolumesProc didn't |
---|
| 3974 | * return a valid list. Set numVolumes to -1 so that we |
---|
| 3975 | * skip the while loop below and just return with the |
---|
| 3976 | * current value of 'type'. |
---|
| 3977 | * |
---|
| 3978 | * It would be better if we could signal an error here |
---|
| 3979 | * (but Tcl_Panic seems a bit excessive). |
---|
| 3980 | */ |
---|
| 3981 | |
---|
| 3982 | numVolumes = -1; |
---|
| 3983 | } |
---|
| 3984 | while (numVolumes > 0) { |
---|
| 3985 | Tcl_Obj *vol; |
---|
| 3986 | int len; |
---|
| 3987 | char *strVol; |
---|
| 3988 | |
---|
| 3989 | numVolumes--; |
---|
| 3990 | Tcl_ListObjIndex(NULL, thisFsVolumes, numVolumes, &vol); |
---|
| 3991 | strVol = Tcl_GetStringFromObj(vol,&len); |
---|
| 3992 | if (pathLen < len) { |
---|
| 3993 | continue; |
---|
| 3994 | } |
---|
| 3995 | if (strncmp(strVol, path, (size_t) len) == 0) { |
---|
| 3996 | type = TCL_PATH_ABSOLUTE; |
---|
| 3997 | if (filesystemPtrPtr != NULL) { |
---|
| 3998 | *filesystemPtrPtr = fsRecPtr->fsPtr; |
---|
| 3999 | } |
---|
| 4000 | if (driveNameLengthPtr != NULL) { |
---|
| 4001 | *driveNameLengthPtr = len; |
---|
| 4002 | } |
---|
| 4003 | if (driveNameRef != NULL) { |
---|
| 4004 | *driveNameRef = vol; |
---|
| 4005 | Tcl_IncrRefCount(vol); |
---|
| 4006 | } |
---|
| 4007 | break; |
---|
| 4008 | } |
---|
| 4009 | } |
---|
| 4010 | Tcl_DecrRefCount(thisFsVolumes); |
---|
| 4011 | if (type == TCL_PATH_ABSOLUTE) { |
---|
| 4012 | /* |
---|
| 4013 | * We don't need to examine any more filesystems. |
---|
| 4014 | */ |
---|
| 4015 | break; |
---|
| 4016 | } |
---|
| 4017 | } |
---|
| 4018 | } |
---|
| 4019 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 4020 | } |
---|
| 4021 | return type; |
---|
| 4022 | } |
---|
| 4023 | |
---|
| 4024 | /* |
---|
| 4025 | *--------------------------------------------------------------------------- |
---|
| 4026 | * |
---|
| 4027 | * Tcl_FSRenameFile -- |
---|
| 4028 | * |
---|
| 4029 | * If the two paths given belong to the same filesystem, we call that |
---|
| 4030 | * filesystems rename function. Otherwise we simply return the POSIX |
---|
| 4031 | * error 'EXDEV', and -1. |
---|
| 4032 | * |
---|
| 4033 | * Results: |
---|
| 4034 | * Standard Tcl error code if a function was called. |
---|
| 4035 | * |
---|
| 4036 | * Side effects: |
---|
| 4037 | * A file may be renamed. |
---|
| 4038 | * |
---|
| 4039 | *--------------------------------------------------------------------------- |
---|
| 4040 | */ |
---|
| 4041 | |
---|
| 4042 | int |
---|
| 4043 | Tcl_FSRenameFile( |
---|
| 4044 | Tcl_Obj* srcPathPtr, /* Pathname of file or dir to be renamed |
---|
| 4045 | * (UTF-8). */ |
---|
| 4046 | Tcl_Obj *destPathPtr) /* New pathname of file or directory |
---|
| 4047 | * (UTF-8). */ |
---|
| 4048 | { |
---|
| 4049 | int retVal = -1; |
---|
| 4050 | const Tcl_Filesystem *fsPtr, *fsPtr2; |
---|
| 4051 | fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr); |
---|
| 4052 | fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr); |
---|
| 4053 | |
---|
| 4054 | if ((fsPtr == fsPtr2) && (fsPtr != NULL)) { |
---|
| 4055 | Tcl_FSRenameFileProc *proc = fsPtr->renameFileProc; |
---|
| 4056 | if (proc != NULL) { |
---|
| 4057 | retVal = (*proc)(srcPathPtr, destPathPtr); |
---|
| 4058 | } |
---|
| 4059 | } |
---|
| 4060 | if (retVal == -1) { |
---|
| 4061 | Tcl_SetErrno(EXDEV); |
---|
| 4062 | } |
---|
| 4063 | return retVal; |
---|
| 4064 | } |
---|
| 4065 | |
---|
| 4066 | /* |
---|
| 4067 | *--------------------------------------------------------------------------- |
---|
| 4068 | * |
---|
| 4069 | * Tcl_FSCopyFile -- |
---|
| 4070 | * |
---|
| 4071 | * If the two paths given belong to the same filesystem, we call that |
---|
| 4072 | * filesystem's copy function. Otherwise we simply return the POSIX error |
---|
| 4073 | * 'EXDEV', and -1. |
---|
| 4074 | * |
---|
| 4075 | * Note that in the native filesystems, 'copyFileProc' is defined to copy |
---|
| 4076 | * soft links (i.e. it copies the links themselves, not the things they |
---|
| 4077 | * point to). |
---|
| 4078 | * |
---|
| 4079 | * Results: |
---|
| 4080 | * Standard Tcl error code if a function was called. |
---|
| 4081 | * |
---|
| 4082 | * Side effects: |
---|
| 4083 | * A file may be copied. |
---|
| 4084 | * |
---|
| 4085 | *--------------------------------------------------------------------------- |
---|
| 4086 | */ |
---|
| 4087 | |
---|
| 4088 | int |
---|
| 4089 | Tcl_FSCopyFile( |
---|
| 4090 | Tcl_Obj *srcPathPtr, /* Pathname of file to be copied (UTF-8). */ |
---|
| 4091 | Tcl_Obj *destPathPtr) /* Pathname of file to copy to (UTF-8). */ |
---|
| 4092 | { |
---|
| 4093 | int retVal = -1; |
---|
| 4094 | const Tcl_Filesystem *fsPtr, *fsPtr2; |
---|
| 4095 | fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr); |
---|
| 4096 | fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr); |
---|
| 4097 | |
---|
| 4098 | if (fsPtr == fsPtr2 && fsPtr != NULL) { |
---|
| 4099 | Tcl_FSCopyFileProc *proc = fsPtr->copyFileProc; |
---|
| 4100 | if (proc != NULL) { |
---|
| 4101 | retVal = (*proc)(srcPathPtr, destPathPtr); |
---|
| 4102 | } |
---|
| 4103 | } |
---|
| 4104 | if (retVal == -1) { |
---|
| 4105 | Tcl_SetErrno(EXDEV); |
---|
| 4106 | } |
---|
| 4107 | return retVal; |
---|
| 4108 | } |
---|
| 4109 | |
---|
| 4110 | /* |
---|
| 4111 | *--------------------------------------------------------------------------- |
---|
| 4112 | * |
---|
| 4113 | * TclCrossFilesystemCopy -- |
---|
| 4114 | * |
---|
| 4115 | * Helper for above function, and for Tcl_FSLoadFile, to copy files from |
---|
| 4116 | * one filesystem to another. This function will overwrite the target |
---|
| 4117 | * file if it already exists. |
---|
| 4118 | * |
---|
| 4119 | * Results: |
---|
| 4120 | * Standard Tcl error code. |
---|
| 4121 | * |
---|
| 4122 | * Side effects: |
---|
| 4123 | * A file may be created. |
---|
| 4124 | * |
---|
| 4125 | *--------------------------------------------------------------------------- |
---|
| 4126 | */ |
---|
| 4127 | int |
---|
| 4128 | TclCrossFilesystemCopy( |
---|
| 4129 | Tcl_Interp *interp, /* For error messages */ |
---|
| 4130 | Tcl_Obj *source, /* Pathname of file to be copied (UTF-8). */ |
---|
| 4131 | Tcl_Obj *target) /* Pathname of file to copy to (UTF-8). */ |
---|
| 4132 | { |
---|
| 4133 | int result = TCL_ERROR; |
---|
| 4134 | int prot = 0666; |
---|
| 4135 | Tcl_Channel in, out; |
---|
| 4136 | Tcl_StatBuf sourceStatBuf; |
---|
| 4137 | struct utimbuf tval; |
---|
| 4138 | |
---|
| 4139 | out = Tcl_FSOpenFileChannel(interp, target, "wb", prot); |
---|
| 4140 | if (out == NULL) { |
---|
| 4141 | /* |
---|
| 4142 | * It looks like we cannot copy it over. Bail out... |
---|
| 4143 | */ |
---|
| 4144 | goto done; |
---|
| 4145 | } |
---|
| 4146 | |
---|
| 4147 | in = Tcl_FSOpenFileChannel(interp, source, "rb", prot); |
---|
| 4148 | if (in == NULL) { |
---|
| 4149 | /* |
---|
| 4150 | * This is very strange, caller should have checked this... |
---|
| 4151 | */ |
---|
| 4152 | |
---|
| 4153 | Tcl_Close(interp, out); |
---|
| 4154 | goto done; |
---|
| 4155 | } |
---|
| 4156 | |
---|
| 4157 | /* |
---|
| 4158 | * Copy it synchronously. We might wish to add an asynchronous option to |
---|
| 4159 | * support vfs's which are slow (e.g. network sockets). |
---|
| 4160 | */ |
---|
| 4161 | |
---|
| 4162 | if (TclCopyChannel(interp, in, out, -1, NULL) == TCL_OK) { |
---|
| 4163 | result = TCL_OK; |
---|
| 4164 | } |
---|
| 4165 | |
---|
| 4166 | /* |
---|
| 4167 | * If the copy failed, assume that copy channel left a good error message. |
---|
| 4168 | */ |
---|
| 4169 | |
---|
| 4170 | Tcl_Close(interp, in); |
---|
| 4171 | Tcl_Close(interp, out); |
---|
| 4172 | |
---|
| 4173 | /* |
---|
| 4174 | * Set modification date of copied file. |
---|
| 4175 | */ |
---|
| 4176 | |
---|
| 4177 | if (Tcl_FSLstat(source, &sourceStatBuf) == 0) { |
---|
| 4178 | tval.actime = sourceStatBuf.st_atime; |
---|
| 4179 | tval.modtime = sourceStatBuf.st_mtime; |
---|
| 4180 | Tcl_FSUtime(target, &tval); |
---|
| 4181 | } |
---|
| 4182 | |
---|
| 4183 | done: |
---|
| 4184 | return result; |
---|
| 4185 | } |
---|
| 4186 | |
---|
| 4187 | /* |
---|
| 4188 | *--------------------------------------------------------------------------- |
---|
| 4189 | * |
---|
| 4190 | * Tcl_FSDeleteFile -- |
---|
| 4191 | * |
---|
| 4192 | * The appropriate function for the filesystem to which pathPtr belongs |
---|
| 4193 | * will be called. |
---|
| 4194 | * |
---|
| 4195 | * Results: |
---|
| 4196 | * Standard Tcl error code. |
---|
| 4197 | * |
---|
| 4198 | * Side effects: |
---|
| 4199 | * A file may be deleted. |
---|
| 4200 | * |
---|
| 4201 | *--------------------------------------------------------------------------- |
---|
| 4202 | */ |
---|
| 4203 | |
---|
| 4204 | int |
---|
| 4205 | Tcl_FSDeleteFile( |
---|
| 4206 | Tcl_Obj *pathPtr) /* Pathname of file to be removed (UTF-8). */ |
---|
| 4207 | { |
---|
| 4208 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 4209 | if (fsPtr != NULL) { |
---|
| 4210 | Tcl_FSDeleteFileProc *proc = fsPtr->deleteFileProc; |
---|
| 4211 | if (proc != NULL) { |
---|
| 4212 | return (*proc)(pathPtr); |
---|
| 4213 | } |
---|
| 4214 | } |
---|
| 4215 | Tcl_SetErrno(ENOENT); |
---|
| 4216 | return -1; |
---|
| 4217 | } |
---|
| 4218 | |
---|
| 4219 | /* |
---|
| 4220 | *--------------------------------------------------------------------------- |
---|
| 4221 | * |
---|
| 4222 | * Tcl_FSCreateDirectory -- |
---|
| 4223 | * |
---|
| 4224 | * The appropriate function for the filesystem to which pathPtr belongs |
---|
| 4225 | * will be called. |
---|
| 4226 | * |
---|
| 4227 | * Results: |
---|
| 4228 | * Standard Tcl error code. |
---|
| 4229 | * |
---|
| 4230 | * Side effects: |
---|
| 4231 | * A directory may be created. |
---|
| 4232 | * |
---|
| 4233 | *--------------------------------------------------------------------------- |
---|
| 4234 | */ |
---|
| 4235 | |
---|
| 4236 | int |
---|
| 4237 | Tcl_FSCreateDirectory( |
---|
| 4238 | Tcl_Obj *pathPtr) /* Pathname of directory to create (UTF-8). */ |
---|
| 4239 | { |
---|
| 4240 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 4241 | if (fsPtr != NULL) { |
---|
| 4242 | Tcl_FSCreateDirectoryProc *proc = fsPtr->createDirectoryProc; |
---|
| 4243 | if (proc != NULL) { |
---|
| 4244 | return (*proc)(pathPtr); |
---|
| 4245 | } |
---|
| 4246 | } |
---|
| 4247 | Tcl_SetErrno(ENOENT); |
---|
| 4248 | return -1; |
---|
| 4249 | } |
---|
| 4250 | |
---|
| 4251 | /* |
---|
| 4252 | *--------------------------------------------------------------------------- |
---|
| 4253 | * |
---|
| 4254 | * Tcl_FSCopyDirectory -- |
---|
| 4255 | * |
---|
| 4256 | * If the two paths given belong to the same filesystem, we call that |
---|
| 4257 | * filesystems copy-directory function. Otherwise we simply return the |
---|
| 4258 | * POSIX error 'EXDEV', and -1. |
---|
| 4259 | * |
---|
| 4260 | * Results: |
---|
| 4261 | * Standard Tcl error code if a function was called. |
---|
| 4262 | * |
---|
| 4263 | * Side effects: |
---|
| 4264 | * A directory may be copied. |
---|
| 4265 | * |
---|
| 4266 | *--------------------------------------------------------------------------- |
---|
| 4267 | */ |
---|
| 4268 | |
---|
| 4269 | int |
---|
| 4270 | Tcl_FSCopyDirectory( |
---|
| 4271 | Tcl_Obj* srcPathPtr, /* Pathname of directory to be copied |
---|
| 4272 | * (UTF-8). */ |
---|
| 4273 | Tcl_Obj *destPathPtr, /* Pathname of target directory (UTF-8). */ |
---|
| 4274 | Tcl_Obj **errorPtr) /* If non-NULL, then will be set to a new |
---|
| 4275 | * object containing name of file causing |
---|
| 4276 | * error, with refCount 1. */ |
---|
| 4277 | { |
---|
| 4278 | int retVal = -1; |
---|
| 4279 | const Tcl_Filesystem *fsPtr, *fsPtr2; |
---|
| 4280 | fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr); |
---|
| 4281 | fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr); |
---|
| 4282 | |
---|
| 4283 | if (fsPtr == fsPtr2 && fsPtr != NULL) { |
---|
| 4284 | Tcl_FSCopyDirectoryProc *proc = fsPtr->copyDirectoryProc; |
---|
| 4285 | if (proc != NULL) { |
---|
| 4286 | retVal = (*proc)(srcPathPtr, destPathPtr, errorPtr); |
---|
| 4287 | } |
---|
| 4288 | } |
---|
| 4289 | if (retVal == -1) { |
---|
| 4290 | Tcl_SetErrno(EXDEV); |
---|
| 4291 | } |
---|
| 4292 | return retVal; |
---|
| 4293 | } |
---|
| 4294 | |
---|
| 4295 | /* |
---|
| 4296 | *--------------------------------------------------------------------------- |
---|
| 4297 | * |
---|
| 4298 | * Tcl_FSRemoveDirectory -- |
---|
| 4299 | * |
---|
| 4300 | * The appropriate function for the filesystem to which pathPtr belongs |
---|
| 4301 | * will be called. |
---|
| 4302 | * |
---|
| 4303 | * Results: |
---|
| 4304 | * Standard Tcl error code. |
---|
| 4305 | * |
---|
| 4306 | * Side effects: |
---|
| 4307 | * A directory may be deleted. |
---|
| 4308 | * |
---|
| 4309 | *--------------------------------------------------------------------------- |
---|
| 4310 | */ |
---|
| 4311 | |
---|
| 4312 | int |
---|
| 4313 | Tcl_FSRemoveDirectory( |
---|
| 4314 | Tcl_Obj *pathPtr, /* Pathname of directory to be removed |
---|
| 4315 | * (UTF-8). */ |
---|
| 4316 | int recursive, /* If non-zero, removes directories that are |
---|
| 4317 | * nonempty. Otherwise, will only remove empty |
---|
| 4318 | * directories. */ |
---|
| 4319 | Tcl_Obj **errorPtr) /* If non-NULL, then will be set to a new |
---|
| 4320 | * object containing name of file causing |
---|
| 4321 | * error, with refCount 1. */ |
---|
| 4322 | { |
---|
| 4323 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 4324 | if (fsPtr != NULL && fsPtr->removeDirectoryProc != NULL) { |
---|
| 4325 | Tcl_FSRemoveDirectoryProc *proc = fsPtr->removeDirectoryProc; |
---|
| 4326 | if (recursive) { |
---|
| 4327 | /* |
---|
| 4328 | * We check whether the cwd lies inside this directory and move it |
---|
| 4329 | * if it does. |
---|
| 4330 | */ |
---|
| 4331 | |
---|
| 4332 | Tcl_Obj *cwdPtr = Tcl_FSGetCwd(NULL); |
---|
| 4333 | |
---|
| 4334 | if (cwdPtr != NULL) { |
---|
| 4335 | char *cwdStr, *normPathStr; |
---|
| 4336 | int cwdLen, normLen; |
---|
| 4337 | Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr); |
---|
| 4338 | |
---|
| 4339 | if (normPath != NULL) { |
---|
| 4340 | normPathStr = Tcl_GetStringFromObj(normPath, &normLen); |
---|
| 4341 | cwdStr = Tcl_GetStringFromObj(cwdPtr, &cwdLen); |
---|
| 4342 | if ((cwdLen >= normLen) && (strncmp(normPathStr, cwdStr, |
---|
| 4343 | (size_t) normLen) == 0)) { |
---|
| 4344 | /* |
---|
| 4345 | * The cwd is inside the directory, so we perform a |
---|
| 4346 | * 'cd [file dirname $path]'. |
---|
| 4347 | */ |
---|
| 4348 | |
---|
| 4349 | Tcl_Obj *dirPtr = TclPathPart(NULL, pathPtr, |
---|
| 4350 | TCL_PATH_DIRNAME); |
---|
| 4351 | |
---|
| 4352 | Tcl_FSChdir(dirPtr); |
---|
| 4353 | Tcl_DecrRefCount(dirPtr); |
---|
| 4354 | } |
---|
| 4355 | } |
---|
| 4356 | Tcl_DecrRefCount(cwdPtr); |
---|
| 4357 | } |
---|
| 4358 | } |
---|
| 4359 | return (*proc)(pathPtr, recursive, errorPtr); |
---|
| 4360 | } |
---|
| 4361 | Tcl_SetErrno(ENOENT); |
---|
| 4362 | return -1; |
---|
| 4363 | } |
---|
| 4364 | |
---|
| 4365 | /* |
---|
| 4366 | *--------------------------------------------------------------------------- |
---|
| 4367 | * |
---|
| 4368 | * Tcl_FSGetFileSystemForPath -- |
---|
| 4369 | * |
---|
| 4370 | * This function determines which filesystem to use for a particular path |
---|
| 4371 | * object, and returns the filesystem which accepts this file. If no |
---|
| 4372 | * filesystem will accept this object as a valid file path, then NULL is |
---|
| 4373 | * returned. |
---|
| 4374 | * |
---|
| 4375 | * Results: |
---|
| 4376 | * NULL or a filesystem which will accept this path. |
---|
| 4377 | * |
---|
| 4378 | * Side effects: |
---|
| 4379 | * The object may be converted to a path type. |
---|
| 4380 | * |
---|
| 4381 | *--------------------------------------------------------------------------- |
---|
| 4382 | */ |
---|
| 4383 | |
---|
| 4384 | Tcl_Filesystem * |
---|
| 4385 | Tcl_FSGetFileSystemForPath( |
---|
| 4386 | Tcl_Obj* pathPtr) |
---|
| 4387 | { |
---|
| 4388 | FilesystemRecord *fsRecPtr; |
---|
| 4389 | Tcl_Filesystem* retVal = NULL; |
---|
| 4390 | |
---|
| 4391 | if (pathPtr == NULL) { |
---|
| 4392 | Tcl_Panic("Tcl_FSGetFileSystemForPath called with NULL object"); |
---|
| 4393 | return NULL; |
---|
| 4394 | } |
---|
| 4395 | |
---|
| 4396 | /* |
---|
| 4397 | * If the object has a refCount of zero, we reject it. This is to avoid |
---|
| 4398 | * possible segfaults or nondeterministic memory leaks (i.e. the user |
---|
| 4399 | * doesn't know if they should decrement the ref count on return or not). |
---|
| 4400 | */ |
---|
| 4401 | |
---|
| 4402 | if (pathPtr->refCount == 0) { |
---|
| 4403 | Tcl_Panic("Tcl_FSGetFileSystemForPath called with object with refCount == 0"); |
---|
| 4404 | return NULL; |
---|
| 4405 | } |
---|
| 4406 | |
---|
| 4407 | /* |
---|
| 4408 | * Check if the filesystem has changed in some way since this object's |
---|
| 4409 | * internal representation was calculated. Before doing that, assure we |
---|
| 4410 | * have the most up-to-date copy of the master filesystem. This is |
---|
| 4411 | * accomplished by the FsGetFirstFilesystem() call. |
---|
| 4412 | */ |
---|
| 4413 | |
---|
| 4414 | fsRecPtr = FsGetFirstFilesystem(); |
---|
| 4415 | |
---|
| 4416 | if (TclFSEnsureEpochOk(pathPtr, &retVal) != TCL_OK) { |
---|
| 4417 | return NULL; |
---|
| 4418 | } |
---|
| 4419 | |
---|
| 4420 | /* |
---|
| 4421 | * Call each of the "pathInFilesystem" functions in succession. A |
---|
| 4422 | * non-return value of -1 indicates the particular function has succeeded. |
---|
| 4423 | */ |
---|
| 4424 | |
---|
| 4425 | while ((retVal == NULL) && (fsRecPtr != NULL)) { |
---|
| 4426 | Tcl_FSPathInFilesystemProc *proc = |
---|
| 4427 | fsRecPtr->fsPtr->pathInFilesystemProc; |
---|
| 4428 | |
---|
| 4429 | if (proc != NULL) { |
---|
| 4430 | ClientData clientData = NULL; |
---|
| 4431 | if ((*proc)(pathPtr, &clientData) != -1) { |
---|
| 4432 | /* |
---|
| 4433 | * We assume the type of pathPtr hasn't been changed by the |
---|
| 4434 | * above call to the pathInFilesystemProc. |
---|
| 4435 | */ |
---|
| 4436 | |
---|
| 4437 | TclFSSetPathDetails(pathPtr, fsRecPtr, clientData); |
---|
| 4438 | retVal = fsRecPtr->fsPtr; |
---|
| 4439 | } |
---|
| 4440 | } |
---|
| 4441 | fsRecPtr = fsRecPtr->nextPtr; |
---|
| 4442 | } |
---|
| 4443 | |
---|
| 4444 | return retVal; |
---|
| 4445 | } |
---|
| 4446 | |
---|
| 4447 | /* |
---|
| 4448 | *--------------------------------------------------------------------------- |
---|
| 4449 | * |
---|
| 4450 | * Tcl_FSGetNativePath -- |
---|
| 4451 | * |
---|
| 4452 | * This function is for use by the Win/Unix native filesystems, so that |
---|
| 4453 | * they can easily retrieve the native (char* or TCHAR*) representation |
---|
| 4454 | * of a path. Other filesystems will probably want to implement similar |
---|
| 4455 | * functions. They basically act as a safety net around |
---|
| 4456 | * Tcl_FSGetInternalRep. Normally your file-system functions will always |
---|
| 4457 | * be called with path objects already converted to the correct |
---|
| 4458 | * filesystem, but if for some reason they are called directly (i.e. by |
---|
| 4459 | * functions not in this file), then one cannot necessarily guarantee |
---|
| 4460 | * that the path object pointer is from the correct filesystem. |
---|
| 4461 | * |
---|
| 4462 | * Note: in the future it might be desireable to have separate versions |
---|
| 4463 | * of this function with different signatures, for example |
---|
| 4464 | * Tcl_FSGetNativeWinPath, Tcl_FSGetNativeUnixPath etc. Right now, since |
---|
| 4465 | * native paths are all string based, we use just one function. |
---|
| 4466 | * |
---|
| 4467 | * Results: |
---|
| 4468 | * NULL or a valid native path. |
---|
| 4469 | * |
---|
| 4470 | * Side effects: |
---|
| 4471 | * See Tcl_FSGetInternalRep. |
---|
| 4472 | * |
---|
| 4473 | *--------------------------------------------------------------------------- |
---|
| 4474 | */ |
---|
| 4475 | |
---|
| 4476 | const char * |
---|
| 4477 | Tcl_FSGetNativePath( |
---|
| 4478 | Tcl_Obj *pathPtr) |
---|
| 4479 | { |
---|
| 4480 | return (const char *) Tcl_FSGetInternalRep(pathPtr, &tclNativeFilesystem); |
---|
| 4481 | } |
---|
| 4482 | |
---|
| 4483 | /* |
---|
| 4484 | *--------------------------------------------------------------------------- |
---|
| 4485 | * |
---|
| 4486 | * NativeFreeInternalRep -- |
---|
| 4487 | * |
---|
| 4488 | * Free a native internal representation, which will be non-NULL. |
---|
| 4489 | * |
---|
| 4490 | * Results: |
---|
| 4491 | * None. |
---|
| 4492 | * |
---|
| 4493 | * Side effects: |
---|
| 4494 | * Memory is released. |
---|
| 4495 | * |
---|
| 4496 | *--------------------------------------------------------------------------- |
---|
| 4497 | */ |
---|
| 4498 | |
---|
| 4499 | static void |
---|
| 4500 | NativeFreeInternalRep( |
---|
| 4501 | ClientData clientData) |
---|
| 4502 | { |
---|
| 4503 | ckfree((char *) clientData); |
---|
| 4504 | } |
---|
| 4505 | |
---|
| 4506 | /* |
---|
| 4507 | *--------------------------------------------------------------------------- |
---|
| 4508 | * |
---|
| 4509 | * Tcl_FSFileSystemInfo -- |
---|
| 4510 | * |
---|
| 4511 | * This function returns a list of two elements. The first element is the |
---|
| 4512 | * name of the filesystem (e.g. "native" or "vfs"), and the second is the |
---|
| 4513 | * particular type of the given path within that filesystem. |
---|
| 4514 | * |
---|
| 4515 | * Results: |
---|
| 4516 | * A list of two elements. |
---|
| 4517 | * |
---|
| 4518 | * Side effects: |
---|
| 4519 | * The object may be converted to a path type. |
---|
| 4520 | * |
---|
| 4521 | *--------------------------------------------------------------------------- |
---|
| 4522 | */ |
---|
| 4523 | |
---|
| 4524 | Tcl_Obj * |
---|
| 4525 | Tcl_FSFileSystemInfo( |
---|
| 4526 | Tcl_Obj *pathPtr) |
---|
| 4527 | { |
---|
| 4528 | Tcl_Obj *resPtr; |
---|
| 4529 | Tcl_FSFilesystemPathTypeProc *proc; |
---|
| 4530 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 4531 | |
---|
| 4532 | if (fsPtr == NULL) { |
---|
| 4533 | return NULL; |
---|
| 4534 | } |
---|
| 4535 | |
---|
| 4536 | resPtr = Tcl_NewListObj(0, NULL); |
---|
| 4537 | Tcl_ListObjAppendElement(NULL,resPtr,Tcl_NewStringObj(fsPtr->typeName,-1)); |
---|
| 4538 | |
---|
| 4539 | proc = fsPtr->filesystemPathTypeProc; |
---|
| 4540 | if (proc != NULL) { |
---|
| 4541 | Tcl_Obj *typePtr = (*proc)(pathPtr); |
---|
| 4542 | if (typePtr != NULL) { |
---|
| 4543 | Tcl_ListObjAppendElement(NULL, resPtr, typePtr); |
---|
| 4544 | } |
---|
| 4545 | } |
---|
| 4546 | |
---|
| 4547 | return resPtr; |
---|
| 4548 | } |
---|
| 4549 | |
---|
| 4550 | /* |
---|
| 4551 | *--------------------------------------------------------------------------- |
---|
| 4552 | * |
---|
| 4553 | * Tcl_FSPathSeparator -- |
---|
| 4554 | * |
---|
| 4555 | * This function returns the separator to be used for a given path. The |
---|
| 4556 | * object returned should have a refCount of zero |
---|
| 4557 | * |
---|
| 4558 | * Results: |
---|
| 4559 | * A Tcl object, with a refCount of zero. If the caller needs to retain a |
---|
| 4560 | * reference to the object, it should call Tcl_IncrRefCount, and should |
---|
| 4561 | * otherwise free the object. |
---|
| 4562 | * |
---|
| 4563 | * Side effects: |
---|
| 4564 | * The path object may be converted to a path type. |
---|
| 4565 | * |
---|
| 4566 | *--------------------------------------------------------------------------- |
---|
| 4567 | */ |
---|
| 4568 | |
---|
| 4569 | Tcl_Obj * |
---|
| 4570 | Tcl_FSPathSeparator( |
---|
| 4571 | Tcl_Obj *pathPtr) |
---|
| 4572 | { |
---|
| 4573 | const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); |
---|
| 4574 | |
---|
| 4575 | if (fsPtr == NULL) { |
---|
| 4576 | return NULL; |
---|
| 4577 | } |
---|
| 4578 | if (fsPtr->filesystemSeparatorProc != NULL) { |
---|
| 4579 | return (*fsPtr->filesystemSeparatorProc)(pathPtr); |
---|
| 4580 | } else { |
---|
| 4581 | Tcl_Obj *resultObj; |
---|
| 4582 | |
---|
| 4583 | /* |
---|
| 4584 | * Allow filesystems not to provide a filesystemSeparatorProc if they |
---|
| 4585 | * wish to use the standard forward slash. |
---|
| 4586 | */ |
---|
| 4587 | |
---|
| 4588 | TclNewLiteralStringObj(resultObj, "/"); |
---|
| 4589 | return resultObj; |
---|
| 4590 | } |
---|
| 4591 | } |
---|
| 4592 | |
---|
| 4593 | /* |
---|
| 4594 | *--------------------------------------------------------------------------- |
---|
| 4595 | * |
---|
| 4596 | * NativeFilesystemSeparator -- |
---|
| 4597 | * |
---|
| 4598 | * This function is part of the native filesystem support, and returns |
---|
| 4599 | * the separator for the given path. |
---|
| 4600 | * |
---|
| 4601 | * Results: |
---|
| 4602 | * String object containing the separator character. |
---|
| 4603 | * |
---|
| 4604 | * Side effects: |
---|
| 4605 | * None. |
---|
| 4606 | * |
---|
| 4607 | *--------------------------------------------------------------------------- |
---|
| 4608 | */ |
---|
| 4609 | |
---|
| 4610 | static Tcl_Obj * |
---|
| 4611 | NativeFilesystemSeparator( |
---|
| 4612 | Tcl_Obj *pathPtr) |
---|
| 4613 | { |
---|
| 4614 | const char *separator = NULL; /* lint */ |
---|
| 4615 | switch (tclPlatform) { |
---|
| 4616 | case TCL_PLATFORM_UNIX: |
---|
| 4617 | separator = "/"; |
---|
| 4618 | break; |
---|
| 4619 | case TCL_PLATFORM_WINDOWS: |
---|
| 4620 | separator = "\\"; |
---|
| 4621 | break; |
---|
| 4622 | } |
---|
| 4623 | return Tcl_NewStringObj(separator,1); |
---|
| 4624 | } |
---|
| 4625 | |
---|
| 4626 | /* Everything from here on is contained in this obsolete ifdef */ |
---|
| 4627 | #ifdef USE_OBSOLETE_FS_HOOKS |
---|
| 4628 | |
---|
| 4629 | /* |
---|
| 4630 | *---------------------------------------------------------------------- |
---|
| 4631 | * |
---|
| 4632 | * TclStatInsertProc -- |
---|
| 4633 | * |
---|
| 4634 | * Insert the passed function pointer at the head of the list of |
---|
| 4635 | * functions which are used during a call to 'TclStat(...)'. The passed |
---|
| 4636 | * function should behave exactly like 'TclStat' when called during that |
---|
| 4637 | * time (see 'TclStat(...)' for more information). The function will be |
---|
| 4638 | * added even if it already in the list. |
---|
| 4639 | * |
---|
| 4640 | * Results: |
---|
| 4641 | * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could |
---|
| 4642 | * not be allocated. |
---|
| 4643 | * |
---|
| 4644 | * Side effects: |
---|
| 4645 | * Memory allocated and modifies the link list for 'TclStat' functions. |
---|
| 4646 | * |
---|
| 4647 | *---------------------------------------------------------------------- |
---|
| 4648 | */ |
---|
| 4649 | |
---|
| 4650 | int |
---|
| 4651 | TclStatInsertProc( |
---|
| 4652 | TclStatProc_ *proc) |
---|
| 4653 | { |
---|
| 4654 | int retVal = TCL_ERROR; |
---|
| 4655 | |
---|
| 4656 | if (proc != NULL) { |
---|
| 4657 | StatProc *newStatProcPtr; |
---|
| 4658 | |
---|
| 4659 | newStatProcPtr = (StatProc *)ckalloc(sizeof(StatProc)); |
---|
| 4660 | |
---|
| 4661 | if (newStatProcPtr != NULL) { |
---|
| 4662 | newStatProcPtr->proc = proc; |
---|
| 4663 | Tcl_MutexLock(&obsoleteFsHookMutex); |
---|
| 4664 | newStatProcPtr->nextPtr = statProcList; |
---|
| 4665 | statProcList = newStatProcPtr; |
---|
| 4666 | Tcl_MutexUnlock(&obsoleteFsHookMutex); |
---|
| 4667 | |
---|
| 4668 | retVal = TCL_OK; |
---|
| 4669 | } |
---|
| 4670 | } |
---|
| 4671 | |
---|
| 4672 | return retVal; |
---|
| 4673 | } |
---|
| 4674 | |
---|
| 4675 | /* |
---|
| 4676 | *---------------------------------------------------------------------- |
---|
| 4677 | * |
---|
| 4678 | * TclStatDeleteProc -- |
---|
| 4679 | * |
---|
| 4680 | * Removed the passed function pointer from the list of 'TclStat' |
---|
| 4681 | * functions. Ensures that the built-in stat function is not removable. |
---|
| 4682 | * |
---|
| 4683 | * Results: |
---|
| 4684 | * TCL_OK if the function pointer was successfully removed, TCL_ERROR |
---|
| 4685 | * otherwise. |
---|
| 4686 | * |
---|
| 4687 | * Side effects: |
---|
| 4688 | * Memory is deallocated and the respective list updated. |
---|
| 4689 | * |
---|
| 4690 | *---------------------------------------------------------------------- |
---|
| 4691 | */ |
---|
| 4692 | |
---|
| 4693 | int |
---|
| 4694 | TclStatDeleteProc( |
---|
| 4695 | TclStatProc_ *proc) |
---|
| 4696 | { |
---|
| 4697 | int retVal = TCL_ERROR; |
---|
| 4698 | StatProc *tmpStatProcPtr; |
---|
| 4699 | StatProc *prevStatProcPtr = NULL; |
---|
| 4700 | |
---|
| 4701 | Tcl_MutexLock(&obsoleteFsHookMutex); |
---|
| 4702 | tmpStatProcPtr = statProcList; |
---|
| 4703 | |
---|
| 4704 | /* |
---|
| 4705 | * Traverse the 'statProcList' looking for the particular node whose |
---|
| 4706 | * 'proc' member matches 'proc' and remove that one from the list. Ensure |
---|
| 4707 | * that the "default" node cannot be removed. |
---|
| 4708 | */ |
---|
| 4709 | |
---|
| 4710 | while ((retVal == TCL_ERROR) && (tmpStatProcPtr != NULL)) { |
---|
| 4711 | if (tmpStatProcPtr->proc == proc) { |
---|
| 4712 | if (prevStatProcPtr == NULL) { |
---|
| 4713 | statProcList = tmpStatProcPtr->nextPtr; |
---|
| 4714 | } else { |
---|
| 4715 | prevStatProcPtr->nextPtr = tmpStatProcPtr->nextPtr; |
---|
| 4716 | } |
---|
| 4717 | |
---|
| 4718 | ckfree((char *)tmpStatProcPtr); |
---|
| 4719 | |
---|
| 4720 | retVal = TCL_OK; |
---|
| 4721 | } else { |
---|
| 4722 | prevStatProcPtr = tmpStatProcPtr; |
---|
| 4723 | tmpStatProcPtr = tmpStatProcPtr->nextPtr; |
---|
| 4724 | } |
---|
| 4725 | } |
---|
| 4726 | |
---|
| 4727 | Tcl_MutexUnlock(&obsoleteFsHookMutex); |
---|
| 4728 | |
---|
| 4729 | return retVal; |
---|
| 4730 | } |
---|
| 4731 | |
---|
| 4732 | /* |
---|
| 4733 | *---------------------------------------------------------------------- |
---|
| 4734 | * |
---|
| 4735 | * TclAccessInsertProc -- |
---|
| 4736 | * |
---|
| 4737 | * Insert the passed function pointer at the head of the list of |
---|
| 4738 | * functions which are used during a call to 'TclAccess(...)'. The passed |
---|
| 4739 | * function should behave exactly like 'TclAccess' when called during |
---|
| 4740 | * that time (see 'TclAccess(...)' for more information). The function |
---|
| 4741 | * will be added even if it already in the list. |
---|
| 4742 | * |
---|
| 4743 | * Results: |
---|
| 4744 | * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could |
---|
| 4745 | * not be allocated. |
---|
| 4746 | * |
---|
| 4747 | * Side effects: |
---|
| 4748 | * Memory allocated and modifies the link list for 'TclAccess' functions. |
---|
| 4749 | * |
---|
| 4750 | *---------------------------------------------------------------------- |
---|
| 4751 | */ |
---|
| 4752 | |
---|
| 4753 | int |
---|
| 4754 | TclAccessInsertProc( |
---|
| 4755 | TclAccessProc_ *proc) |
---|
| 4756 | { |
---|
| 4757 | int retVal = TCL_ERROR; |
---|
| 4758 | |
---|
| 4759 | if (proc != NULL) { |
---|
| 4760 | AccessProc *newAccessProcPtr; |
---|
| 4761 | |
---|
| 4762 | newAccessProcPtr = (AccessProc *)ckalloc(sizeof(AccessProc)); |
---|
| 4763 | |
---|
| 4764 | if (newAccessProcPtr != NULL) { |
---|
| 4765 | newAccessProcPtr->proc = proc; |
---|
| 4766 | Tcl_MutexLock(&obsoleteFsHookMutex); |
---|
| 4767 | newAccessProcPtr->nextPtr = accessProcList; |
---|
| 4768 | accessProcList = newAccessProcPtr; |
---|
| 4769 | Tcl_MutexUnlock(&obsoleteFsHookMutex); |
---|
| 4770 | |
---|
| 4771 | retVal = TCL_OK; |
---|
| 4772 | } |
---|
| 4773 | } |
---|
| 4774 | |
---|
| 4775 | return retVal; |
---|
| 4776 | } |
---|
| 4777 | |
---|
| 4778 | /* |
---|
| 4779 | *---------------------------------------------------------------------- |
---|
| 4780 | * |
---|
| 4781 | * TclAccessDeleteProc -- |
---|
| 4782 | * |
---|
| 4783 | * Removed the passed function pointer from the list of 'TclAccess' |
---|
| 4784 | * functions. Ensures that the built-in access function is not removable. |
---|
| 4785 | * |
---|
| 4786 | * Results: |
---|
| 4787 | * TCL_OK if the function pointer was successfully removed, TCL_ERROR |
---|
| 4788 | * otherwise. |
---|
| 4789 | * |
---|
| 4790 | * Side effects: |
---|
| 4791 | * Memory is deallocated and the respective list updated. |
---|
| 4792 | * |
---|
| 4793 | *---------------------------------------------------------------------- |
---|
| 4794 | */ |
---|
| 4795 | |
---|
| 4796 | int |
---|
| 4797 | TclAccessDeleteProc( |
---|
| 4798 | TclAccessProc_ *proc) |
---|
| 4799 | { |
---|
| 4800 | int retVal = TCL_ERROR; |
---|
| 4801 | AccessProc *tmpAccessProcPtr; |
---|
| 4802 | AccessProc *prevAccessProcPtr = NULL; |
---|
| 4803 | |
---|
| 4804 | /* |
---|
| 4805 | * Traverse the 'accessProcList' looking for the particular node whose |
---|
| 4806 | * 'proc' member matches 'proc' and remove that one from the list. Ensure |
---|
| 4807 | * that the "default" node cannot be removed. |
---|
| 4808 | */ |
---|
| 4809 | |
---|
| 4810 | Tcl_MutexLock(&obsoleteFsHookMutex); |
---|
| 4811 | tmpAccessProcPtr = accessProcList; |
---|
| 4812 | while ((retVal == TCL_ERROR) && (tmpAccessProcPtr != NULL)) { |
---|
| 4813 | if (tmpAccessProcPtr->proc == proc) { |
---|
| 4814 | if (prevAccessProcPtr == NULL) { |
---|
| 4815 | accessProcList = tmpAccessProcPtr->nextPtr; |
---|
| 4816 | } else { |
---|
| 4817 | prevAccessProcPtr->nextPtr = tmpAccessProcPtr->nextPtr; |
---|
| 4818 | } |
---|
| 4819 | |
---|
| 4820 | ckfree((char *)tmpAccessProcPtr); |
---|
| 4821 | |
---|
| 4822 | retVal = TCL_OK; |
---|
| 4823 | } else { |
---|
| 4824 | prevAccessProcPtr = tmpAccessProcPtr; |
---|
| 4825 | tmpAccessProcPtr = tmpAccessProcPtr->nextPtr; |
---|
| 4826 | } |
---|
| 4827 | } |
---|
| 4828 | Tcl_MutexUnlock(&obsoleteFsHookMutex); |
---|
| 4829 | |
---|
| 4830 | return retVal; |
---|
| 4831 | } |
---|
| 4832 | |
---|
| 4833 | /* |
---|
| 4834 | *---------------------------------------------------------------------- |
---|
| 4835 | * |
---|
| 4836 | * TclOpenFileChannelInsertProc -- |
---|
| 4837 | * |
---|
| 4838 | * Insert the passed function pointer at the head of the list of |
---|
| 4839 | * functions which are used during a call to 'Tcl_OpenFileChannel(...)'. |
---|
| 4840 | * The passed function should behave exactly like 'Tcl_OpenFileChannel' |
---|
| 4841 | * when called during that time (see 'Tcl_OpenFileChannel(...)' for more |
---|
| 4842 | * information). The function will be added even if it already in the |
---|
| 4843 | * list. |
---|
| 4844 | * |
---|
| 4845 | * Results: |
---|
| 4846 | * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could |
---|
| 4847 | * not be allocated. |
---|
| 4848 | * |
---|
| 4849 | * Side effects: |
---|
| 4850 | * Memory allocated and modifies the link list for 'Tcl_OpenFileChannel' |
---|
| 4851 | * functions. |
---|
| 4852 | * |
---|
| 4853 | *---------------------------------------------------------------------- |
---|
| 4854 | */ |
---|
| 4855 | |
---|
| 4856 | int |
---|
| 4857 | TclOpenFileChannelInsertProc( |
---|
| 4858 | TclOpenFileChannelProc_ *proc) |
---|
| 4859 | { |
---|
| 4860 | int retVal = TCL_ERROR; |
---|
| 4861 | |
---|
| 4862 | if (proc != NULL) { |
---|
| 4863 | OpenFileChannelProc *newOpenFileChannelProcPtr; |
---|
| 4864 | |
---|
| 4865 | newOpenFileChannelProcPtr = (OpenFileChannelProc *) |
---|
| 4866 | ckalloc(sizeof(OpenFileChannelProc)); |
---|
| 4867 | |
---|
| 4868 | newOpenFileChannelProcPtr->proc = proc; |
---|
| 4869 | Tcl_MutexLock(&obsoleteFsHookMutex); |
---|
| 4870 | newOpenFileChannelProcPtr->nextPtr = openFileChannelProcList; |
---|
| 4871 | openFileChannelProcList = newOpenFileChannelProcPtr; |
---|
| 4872 | Tcl_MutexUnlock(&obsoleteFsHookMutex); |
---|
| 4873 | |
---|
| 4874 | retVal = TCL_OK; |
---|
| 4875 | } |
---|
| 4876 | |
---|
| 4877 | return retVal; |
---|
| 4878 | } |
---|
| 4879 | |
---|
| 4880 | /* |
---|
| 4881 | *---------------------------------------------------------------------- |
---|
| 4882 | * |
---|
| 4883 | * TclOpenFileChannelDeleteProc -- |
---|
| 4884 | * |
---|
| 4885 | * Removed the passed function pointer from the list of |
---|
| 4886 | * 'Tcl_OpenFileChannel' functions. Ensures that the built-in open file |
---|
| 4887 | * channel function is not removable. |
---|
| 4888 | * |
---|
| 4889 | * Results: |
---|
| 4890 | * TCL_OK if the function pointer was successfully removed, TCL_ERROR |
---|
| 4891 | * otherwise. |
---|
| 4892 | * |
---|
| 4893 | * Side effects: |
---|
| 4894 | * Memory is deallocated and the respective list updated. |
---|
| 4895 | * |
---|
| 4896 | *---------------------------------------------------------------------- |
---|
| 4897 | */ |
---|
| 4898 | |
---|
| 4899 | int |
---|
| 4900 | TclOpenFileChannelDeleteProc( |
---|
| 4901 | TclOpenFileChannelProc_ *proc) |
---|
| 4902 | { |
---|
| 4903 | int retVal = TCL_ERROR; |
---|
| 4904 | OpenFileChannelProc *tmpOpenFileChannelProcPtr = openFileChannelProcList; |
---|
| 4905 | OpenFileChannelProc *prevOpenFileChannelProcPtr = NULL; |
---|
| 4906 | |
---|
| 4907 | /* |
---|
| 4908 | * Traverse the 'openFileChannelProcList' looking for the particular node |
---|
| 4909 | * whose 'proc' member matches 'proc' and remove that one from the list. |
---|
| 4910 | */ |
---|
| 4911 | |
---|
| 4912 | Tcl_MutexLock(&obsoleteFsHookMutex); |
---|
| 4913 | tmpOpenFileChannelProcPtr = openFileChannelProcList; |
---|
| 4914 | while ((retVal == TCL_ERROR) && |
---|
| 4915 | (tmpOpenFileChannelProcPtr != NULL)) { |
---|
| 4916 | if (tmpOpenFileChannelProcPtr->proc == proc) { |
---|
| 4917 | if (prevOpenFileChannelProcPtr == NULL) { |
---|
| 4918 | openFileChannelProcList = tmpOpenFileChannelProcPtr->nextPtr; |
---|
| 4919 | } else { |
---|
| 4920 | prevOpenFileChannelProcPtr->nextPtr = |
---|
| 4921 | tmpOpenFileChannelProcPtr->nextPtr; |
---|
| 4922 | } |
---|
| 4923 | |
---|
| 4924 | ckfree((char *) tmpOpenFileChannelProcPtr); |
---|
| 4925 | |
---|
| 4926 | retVal = TCL_OK; |
---|
| 4927 | } else { |
---|
| 4928 | prevOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr; |
---|
| 4929 | tmpOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr->nextPtr; |
---|
| 4930 | } |
---|
| 4931 | } |
---|
| 4932 | Tcl_MutexUnlock(&obsoleteFsHookMutex); |
---|
| 4933 | |
---|
| 4934 | return retVal; |
---|
| 4935 | } |
---|
| 4936 | #endif /* USE_OBSOLETE_FS_HOOKS */ |
---|
| 4937 | |
---|
| 4938 | /* |
---|
| 4939 | * Local Variables: |
---|
| 4940 | * mode: c |
---|
| 4941 | * c-basic-offset: 4 |
---|
| 4942 | * fill-column: 78 |
---|
| 4943 | * End: |
---|
| 4944 | */ |
---|