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 | */ |
---|