os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacFCmd.c
Update contrib.
4 * Implements the Macintosh specific portions of the file manipulation
5 * subcommands of the "file" command.
7 * Copyright (c) 1996-1998 Sun Microsystems, Inc.
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 * RCS: @(#) $Id: tclMacFCmd.c,v 1.19 2003/02/04 17:06:51 vincentdarley Exp $
17 #include "tclMacInt.h"
19 #include <FSpCompat.h>
20 #include <MoreFilesExtras.h>
24 #include <DirectoryCopy.h>
31 * Callback for the file attributes code.
34 static int GetFileFinderAttributes _ANSI_ARGS_((Tcl_Interp *interp,
35 int objIndex, Tcl_Obj *fileName,
36 Tcl_Obj **attributePtrPtr));
37 static int GetFileReadOnly _ANSI_ARGS_((Tcl_Interp *interp,
38 int objIndex, Tcl_Obj *fileName,
39 Tcl_Obj **readOnlyPtrPtr));
40 static int SetFileFinderAttributes _ANSI_ARGS_((Tcl_Interp *interp,
41 int objIndex, Tcl_Obj *fileName,
42 Tcl_Obj *attributePtr));
43 static int SetFileReadOnly _ANSI_ARGS_((Tcl_Interp *interp,
44 int objIndex, Tcl_Obj *fileName,
45 Tcl_Obj *readOnlyPtr));
48 * These are indeces into the tclpFileAttrsStrings table below.
51 #define MAC_CREATOR_ATTRIBUTE 0
52 #define MAC_HIDDEN_ATTRIBUTE 1
53 #define MAC_READONLY_ATTRIBUTE 2
54 #define MAC_TYPE_ATTRIBUTE 3
57 * Global variables for the file attributes code.
60 CONST char *tclpFileAttrStrings[] = {"-creator", "-hidden", "-readonly",
61 "-type", (char *) NULL};
62 CONST TclFileAttrProcs tclpFileAttrProcs[] = {
63 {GetFileFinderAttributes, SetFileFinderAttributes},
64 {GetFileFinderAttributes, SetFileFinderAttributes},
65 {GetFileReadOnly, SetFileReadOnly},
66 {GetFileFinderAttributes, SetFileFinderAttributes}};
69 * File specific static data
72 static long startSeed = 248923489;
75 * Prototypes for procedure only used in this file
78 static pascal Boolean CopyErrHandler _ANSI_ARGS_((OSErr error,
79 short failedOperation,
80 short srcVRefNum, long srcDirID,
81 ConstStr255Param srcName, short dstVRefNum,
82 long dstDirID,ConstStr255Param dstName));
83 static int DoCopyDirectory _ANSI_ARGS_((CONST char *src,
84 CONST char *dst, Tcl_DString *errorPtr));
85 static int DoCopyFile _ANSI_ARGS_((CONST char *src,
87 static int DoCreateDirectory _ANSI_ARGS_((CONST char *path));
88 static int DoRemoveDirectory _ANSI_ARGS_((CONST char *path,
89 int recursive, Tcl_DString *errorPtr));
90 static int DoRenameFile _ANSI_ARGS_((CONST char *src,
92 OSErr FSpGetFLockCompat _ANSI_ARGS_((const FSSpec *specPtr,
94 static OSErr GetFileSpecs _ANSI_ARGS_((CONST char *path,
95 FSSpec *pathSpecPtr, FSSpec *dirSpecPtr,
96 Boolean *pathExistsPtr,
97 Boolean *pathIsDirectoryPtr));
98 static OSErr MoveRename _ANSI_ARGS_((const FSSpec *srcSpecPtr,
99 const FSSpec *dstSpecPtr, StringPtr copyName));
100 static int Pstrequal _ANSI_ARGS_((ConstStr255Param stringA,
101 ConstStr255Param stringB));
104 *---------------------------------------------------------------------------
106 * TclpObjRenameFile, DoRenameFile --
108 * Changes the name of an existing file or directory, from src to dst.
109 * If src and dst refer to the same file or directory, does nothing
110 * and returns success. Otherwise if dst already exists, it will be
111 * deleted and replaced by src subject to the following conditions:
112 * If src is a directory, dst may be an empty directory.
113 * If src is a file, dst may be a file.
114 * In any other situation where dst already exists, the rename will
118 * If the directory was successfully created, returns TCL_OK.
119 * Otherwise the return value is TCL_ERROR and errno is set to
120 * indicate the error. Some possible values for errno are:
122 * EACCES: src or dst parent directory can't be read and/or written.
123 * EEXIST: dst is a non-empty directory.
124 * EINVAL: src is a root directory or dst is a subdirectory of src.
125 * EISDIR: dst is a directory, but src is not.
126 * ENOENT: src doesn't exist. src or dst is "".
127 * ENOTDIR: src is a directory, but dst is not.
128 * EXDEV: src and dst are on different filesystems.
131 * The implementation of rename may allow cross-filesystem renames,
132 * but the caller should be prepared to emulate it with copy and
133 * delete if errno is EXDEV.
135 *---------------------------------------------------------------------------
139 TclpObjRenameFile(srcPathPtr, destPathPtr)
141 Tcl_Obj *destPathPtr;
143 return DoRenameFile(Tcl_FSGetNativePath(srcPathPtr),
144 Tcl_FSGetNativePath(destPathPtr));
149 CONST char *src, /* Pathname of file or dir to be renamed
151 CONST char *dst) /* New pathname of file or directory
154 FSSpec srcFileSpec, dstFileSpec, dstDirSpec;
157 Boolean srcIsDirectory, dstIsDirectory, dstExists, dstLocked;
159 err = FSpLLocationFromPath(strlen(src), src, &srcFileSpec);
161 FSpGetDirectoryID(&srcFileSpec, &srcID, &srcIsDirectory);
164 err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists,
168 if (dstExists == 0) {
169 err = MoveRename(&srcFileSpec, &dstDirSpec, dstFileSpec.name);
172 err = FSpGetFLockCompat(&dstFileSpec, &dstLocked);
174 FSpRstFLockCompat(&dstFileSpec);
178 if (srcIsDirectory) {
179 if (dstIsDirectory) {
181 * The following call will remove an empty directory. If it
182 * fails, it's because it wasn't empty.
185 if (DoRemoveDirectory(dst, 0, NULL) != TCL_OK) {
190 * Now that that empty directory is gone, we can try
191 * renaming src. If that fails, we'll put this empty
192 * directory back, for completeness.
195 err = MoveRename(&srcFileSpec, &dstDirSpec, dstFileSpec.name);
197 FSpDirCreateCompat(&dstFileSpec, smSystemScript, &dummy);
199 FSpSetFLockCompat(&dstFileSpec);
207 if (dstIsDirectory) {
212 * Overwrite existing file by:
214 * 1. Rename existing file to temp name.
215 * 2. Rename old file to new name.
216 * 3. If success, delete temp file. If failure,
217 * put temp file back to old name.
223 err = GenerateUniqueName(dstFileSpec.vRefNum, &startSeed,
224 dstFileSpec.parID, dstFileSpec.parID, tmpName);
226 err = FSpRenameCompat(&dstFileSpec, tmpName);
229 err = FSMakeFSSpecCompat(dstFileSpec.vRefNum,
230 dstFileSpec.parID, tmpName, &tmpFileSpec);
233 err = MoveRename(&srcFileSpec, &dstDirSpec,
237 FSpDeleteCompat(&tmpFileSpec);
239 FSpDeleteCompat(&dstFileSpec);
240 FSpRenameCompat(&tmpFileSpec, dstFileSpec.name);
242 FSpSetFLockCompat(&dstFileSpec);
251 errno = TclMacOSErrorToPosixError(err);
258 *--------------------------------------------------------------------------
262 * Helper function for TclpRenameFile. Renames a file or directory
263 * into the same directory or another directory. The target name
264 * must not already exist in the destination directory.
266 * Don't use FSpMoveRenameCompat because it doesn't work with
267 * directories or with locked files.
270 * Returns a mac error indicating the cause of the failure.
273 * Creates a temp file in the target directory to handle a rename
274 * between directories.
276 *--------------------------------------------------------------------------
281 const FSSpec *srcFileSpecPtr, /* Source object. */
282 const FSSpec *dstDirSpecPtr, /* Destination directory. */
283 StringPtr copyName) /* New name for object in destination
288 Boolean srcIsDir, dstIsDir;
290 FSSpec dstFileSpec, srcDirSpec, tmpSrcFileSpec, tmpDstFileSpec;
293 if (srcFileSpecPtr->parID == 1) {
295 * Trying to rename a volume.
300 if (srcFileSpecPtr->vRefNum != dstDirSpecPtr->vRefNum) {
302 * Renaming across volumes.
307 err = FSpGetFLockCompat(srcFileSpecPtr, &locked);
309 FSpRstFLockCompat(srcFileSpecPtr);
312 err = FSpGetDirectoryID(dstDirSpecPtr, &dstID, &dstIsDir);
315 if (srcFileSpecPtr->parID == dstID) {
317 * Renaming object within directory.
320 err = FSpRenameCompat(srcFileSpecPtr, copyName);
323 if (Pstrequal(srcFileSpecPtr->name, copyName)) {
325 * Moving object to another directory (under same name).
328 err = FSpCatMoveCompat(srcFileSpecPtr, dstDirSpecPtr);
331 err = FSpGetDirectoryID(srcFileSpecPtr, &srcID, &srcIsDir);
335 * Fullblown: rename source object to temp name, move temp to
336 * dest directory, and rename temp to target.
339 err = GenerateUniqueName(srcFileSpecPtr->vRefNum, &startSeed,
340 srcFileSpecPtr->parID, dstID, tmpName);
341 FSMakeFSSpecCompat(srcFileSpecPtr->vRefNum, srcFileSpecPtr->parID,
342 tmpName, &tmpSrcFileSpec);
343 FSMakeFSSpecCompat(dstDirSpecPtr->vRefNum, dstID, tmpName,
347 err = FSpRenameCompat(srcFileSpecPtr, tmpName);
350 err = FSpCatMoveCompat(&tmpSrcFileSpec, dstDirSpecPtr);
352 err = FSpRenameCompat(&tmpDstFileSpec, copyName);
356 FSMakeFSSpecCompat(srcFileSpecPtr->vRefNum, srcFileSpecPtr->parID,
358 FSpCatMoveCompat(&tmpDstFileSpec, &srcDirSpec);
360 FSpRenameCompat(&tmpSrcFileSpec, srcFileSpecPtr->name);
364 if (locked != false) {
366 FSMakeFSSpecCompat(dstDirSpecPtr->vRefNum,
367 dstID, copyName, &dstFileSpec);
368 FSpSetFLockCompat(&dstFileSpec);
370 FSpSetFLockCompat(srcFileSpecPtr);
377 *---------------------------------------------------------------------------
379 * TclpObjCopyFile, DoCopyFile --
381 * Copy a single file (not a directory). If dst already exists and
382 * is not a directory, it is removed.
385 * If the file was successfully copied, returns TCL_OK. Otherwise
386 * the return value is TCL_ERROR and errno is set to indicate the
387 * error. Some possible values for errno are:
389 * EACCES: src or dst parent directory can't be read and/or written.
390 * EISDIR: src or dst is a directory.
391 * ENOENT: src doesn't exist. src or dst is "".
394 * This procedure will also copy symbolic links, block, and
395 * character devices, and fifos. For symbolic links, the links
396 * themselves will be copied and not what they point to. For the
397 * other special file types, the directory entry will be copied and
398 * not the contents of the device that it refers to.
400 *---------------------------------------------------------------------------
404 TclpObjCopyFile(srcPathPtr, destPathPtr)
406 Tcl_Obj *destPathPtr;
408 return DoCopyFile(Tcl_FSGetNativePath(srcPathPtr),
409 Tcl_FSGetNativePath(destPathPtr));
414 CONST char *src, /* Pathname of file to be copied (native). */
415 CONST char *dst) /* Pathname of file to copy to (native). */
418 Boolean dstExists, dstIsDirectory, dstLocked;
419 FSSpec srcFileSpec, dstFileSpec, dstDirSpec, tmpFileSpec;
422 err = FSpLLocationFromPath(strlen(src), src, &srcFileSpec);
424 err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists,
428 if (dstIsDirectory) {
432 err = FSpGetFLockCompat(&dstFileSpec, &dstLocked);
434 FSpRstFLockCompat(&dstFileSpec);
441 dstErr = GenerateUniqueName(dstFileSpec.vRefNum, &startSeed, dstFileSpec.parID,
442 dstFileSpec.parID, tmpName);
443 if (dstErr == noErr) {
444 dstErr = FSpRenameCompat(&dstFileSpec, tmpName);
448 err = FSpFileCopy(&srcFileSpec, &dstDirSpec,
449 (StringPtr) dstFileSpec.name, NULL, 0, true);
451 if ((dstExists != false) && (dstErr == noErr)) {
452 FSMakeFSSpecCompat(dstFileSpec.vRefNum, dstFileSpec.parID,
453 tmpName, &tmpFileSpec);
456 * Delete backup file.
459 FSpDeleteCompat(&tmpFileSpec);
463 * Restore backup file.
466 FSpDeleteCompat(&dstFileSpec);
467 FSpRenameCompat(&tmpFileSpec, dstFileSpec.name);
469 FSpSetFLockCompat(&dstFileSpec);
475 errno = TclMacOSErrorToPosixError(err);
482 *---------------------------------------------------------------------------
484 * TclpObjDeleteFile, TclpDeleteFile --
486 * Removes a single file (not a directory).
489 * If the file was successfully deleted, returns TCL_OK. Otherwise
490 * the return value is TCL_ERROR and errno is set to indicate the
491 * error. Some possible values for errno are:
493 * EACCES: a parent directory can't be read and/or written.
494 * EISDIR: path is a directory.
495 * ENOENT: path doesn't exist or is "".
498 * The file is deleted, even if it is read-only.
500 *---------------------------------------------------------------------------
504 TclpObjDeleteFile(pathPtr)
507 return TclpDeleteFile(Tcl_FSGetNativePath(pathPtr));
512 CONST char *path) /* Pathname of file to be removed (native). */
519 err = FSpLLocationFromPath(strlen(path), path, &fileSpec);
522 * Since FSpDeleteCompat will delete an empty directory, make sure
523 * that this isn't a directory first.
526 FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
527 if (isDirectory == true) {
532 err = FSpDeleteCompat(&fileSpec);
533 if (err == fLckdErr) {
534 FSpRstFLockCompat(&fileSpec);
535 err = FSpDeleteCompat(&fileSpec);
537 FSpSetFLockCompat(&fileSpec);
541 errno = TclMacOSErrorToPosixError(err);
548 *---------------------------------------------------------------------------
550 * TclpObjCreateDirectory, DoCreateDirectory --
552 * Creates the specified directory. All parent directories of the
553 * specified directory must already exist. The directory is
554 * automatically created with permissions so that user can access
555 * the new directory and create new files or subdirectories in it.
558 * If the directory was successfully created, returns TCL_OK.
559 * Otherwise the return value is TCL_ERROR and errno is set to
560 * indicate the error. Some possible values for errno are:
562 * EACCES: a parent directory can't be read and/or written.
563 * EEXIST: path already exists.
564 * ENOENT: a parent directory doesn't exist.
567 * A directory is created with the current umask, except that
568 * permission for u+rwx will always be added.
570 *---------------------------------------------------------------------------
574 TclpObjCreateDirectory(pathPtr)
577 return DoCreateDirectory(Tcl_FSGetNativePath(pathPtr));
582 CONST char *path) /* Pathname of directory to create (native). */
588 err = FSpLocationFromPath(strlen(path), path, &dirSpec);
590 err = dupFNErr; /* EEXIST. */
591 } else if (err == fnfErr) {
592 err = FSpDirCreateCompat(&dirSpec, smSystemScript, &outDirID);
596 errno = TclMacOSErrorToPosixError(err);
603 *---------------------------------------------------------------------------
605 * TclpObjCopyDirectory, DoCopyDirectory --
607 * Recursively copies a directory. The target directory dst must
608 * not already exist. Note that this function does not merge two
609 * directory hierarchies, even if the target directory is an an
613 * If the directory was successfully copied, returns TCL_OK.
614 * Otherwise the return value is TCL_ERROR, errno is set to indicate
615 * the error, and the pathname of the file that caused the error
616 * is stored in errorPtr. See TclpCreateDirectory and TclpCopyFile
617 * for a description of possible values for errno.
620 * An exact copy of the directory hierarchy src will be created
621 * with the name dst. If an error occurs, the error will
622 * be returned immediately, and remaining files will not be
625 *---------------------------------------------------------------------------
629 TclpObjCopyDirectory(srcPathPtr, destPathPtr, errorPtr)
631 Tcl_Obj *destPathPtr;
636 ret = DoCopyDirectory(Tcl_FSGetNativePath(srcPathPtr),
637 Tcl_FSGetNativePath(destPathPtr), &ds);
639 *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
640 Tcl_DStringFree(&ds);
641 Tcl_IncrRefCount(*errorPtr);
648 CONST char *src, /* Pathname of directory to be copied
650 CONST char *dst, /* Pathname of target directory (Native). */
651 Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free
652 * DString filled with UTF-8 name of file
656 long srcID, tmpDirID;
657 FSSpec srcFileSpec, dstFileSpec, dstDirSpec, tmpDirSpec, tmpFileSpec;
658 Boolean srcIsDirectory, srcLocked;
659 Boolean dstIsDirectory, dstExists;
662 err = FSpLocationFromPath(strlen(src), src, &srcFileSpec);
664 err = FSpGetDirectoryID(&srcFileSpec, &srcID, &srcIsDirectory);
667 if (srcIsDirectory == false) {
668 err = afpObjectTypeErr; /* ENOTDIR. */
672 err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists,
676 if (dstIsDirectory == false) {
677 err = afpObjectTypeErr; /* ENOTDIR. */
679 err = dupFNErr; /* EEXIST. */
685 if ((srcFileSpec.vRefNum == dstFileSpec.vRefNum) &&
686 (srcFileSpec.parID == dstFileSpec.parID) &&
687 (Pstrequal(srcFileSpec.name, dstFileSpec.name) != 0)) {
689 * Copying on top of self. No-op.
696 * This algorthm will work making a copy of the source directory in
697 * the current directory with a new name, in a new directory with the
698 * same name, and in a new directory with a new name:
700 * 1. Make dstDir/tmpDir.
701 * 2. Copy srcDir/src to dstDir/tmpDir/src
702 * 3. Rename dstDir/tmpDir/src to dstDir/tmpDir/dst (if necessary).
703 * 4. CatMove dstDir/tmpDir/dst to dstDir/dst.
704 * 5. Remove dstDir/tmpDir.
707 err = FSpGetFLockCompat(&srcFileSpec, &srcLocked);
709 FSpRstFLockCompat(&srcFileSpec);
712 err = GenerateUniqueName(dstFileSpec.vRefNum, &startSeed, dstFileSpec.parID,
713 dstFileSpec.parID, tmpName);
716 FSMakeFSSpecCompat(dstFileSpec.vRefNum, dstFileSpec.parID,
717 tmpName, &tmpDirSpec);
718 err = FSpDirCreateCompat(&tmpDirSpec, smSystemScript, &tmpDirID);
721 err = FSpDirectoryCopy(&srcFileSpec, &tmpDirSpec, NULL, NULL, 0, true,
726 * Even if the Copy failed, Rename/Move whatever did get copied to the
727 * appropriate final destination, if possible.
732 if (Pstrequal(srcFileSpec.name, dstFileSpec.name) == 0) {
733 err = FSMakeFSSpecCompat(tmpDirSpec.vRefNum, tmpDirID,
734 srcFileSpec.name, &tmpFileSpec);
736 err = FSpRenameCompat(&tmpFileSpec, dstFileSpec.name);
740 err = FSMakeFSSpecCompat(tmpDirSpec.vRefNum, tmpDirID,
741 dstFileSpec.name, &tmpFileSpec);
744 err = FSpCatMoveCompat(&tmpFileSpec, &dstDirSpec);
748 FSpSetFLockCompat(&dstFileSpec);
752 FSpDeleteCompat(&tmpDirSpec);
754 if (saveErr != noErr) {
760 errno = TclMacOSErrorToPosixError(err);
761 if (errorPtr != NULL) {
762 Tcl_ExternalToUtfDString(NULL, dst, -1, errorPtr);
770 *----------------------------------------------------------------------
774 * This procedure is called from the MoreFiles procedure
775 * FSpDirectoryCopy whenever an error occurs.
778 * False if the condition should not be considered an error, true
782 * Since FSpDirectoryCopy() is called only after removing any
783 * existing target directories, there shouldn't be any errors.
785 *----------------------------------------------------------------------
788 static pascal Boolean
790 OSErr error, /* Error that occured */
791 short failedOperation, /* operation that caused the error */
792 short srcVRefNum, /* volume ref number of source */
793 long srcDirID, /* directory id of source */
794 ConstStr255Param srcName, /* name of source */
795 short dstVRefNum, /* volume ref number of dst */
796 long dstDirID, /* directory id of dst */
797 ConstStr255Param dstName) /* name of dst directory */
803 *---------------------------------------------------------------------------
805 * TclpObjRemoveDirectory, DoRemoveDirectory --
807 * Removes directory (and its contents, if the recursive flag is set).
810 * If the directory was successfully removed, returns TCL_OK.
811 * Otherwise the return value is TCL_ERROR, errno is set to indicate
812 * the error, and the pathname of the file that caused the error
813 * is stored in errorPtr. Some possible values for errno are:
815 * EACCES: path directory can't be read and/or written.
816 * EEXIST: path is a non-empty directory.
817 * EINVAL: path is a root directory.
818 * ENOENT: path doesn't exist or is "".
819 * ENOTDIR: path is not a directory.
822 * Directory removed. If an error occurs, the error will be returned
823 * immediately, and remaining files will not be deleted.
825 *---------------------------------------------------------------------------
829 TclpObjRemoveDirectory(pathPtr, recursive, errorPtr)
836 ret = DoRemoveDirectory(Tcl_FSGetNativePath(pathPtr),recursive, &ds);
838 *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
839 Tcl_DStringFree(&ds);
840 Tcl_IncrRefCount(*errorPtr);
847 CONST char *path, /* Pathname of directory to be removed
849 int recursive, /* If non-zero, removes directories that
850 * are nonempty. Otherwise, will only remove
851 * empty directories. */
852 Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free
853 * DString filled with UTF-8 name of file
866 err = FSpLocationFromPath(strlen(path), path, &fileSpec);
872 * Since FSpDeleteCompat will delete a file, make sure this isn't
877 FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
878 if (isDirectory == 0) {
883 err = FSpDeleteCompat(&fileSpec);
884 if (err == fLckdErr) {
886 FSpRstFLockCompat(&fileSpec);
887 err = FSpDeleteCompat(&fileSpec);
892 if (err != fBsyErr) {
896 if (recursive == 0) {
898 * fBsyErr means one of three things: file busy, directory not empty,
899 * or working directory control block open. Determine if directory
900 * is empty. If directory is not empty, return EEXIST.
903 pb.hFileInfo.ioVRefNum = fileSpec.vRefNum;
904 pb.hFileInfo.ioDirID = dirID;
905 pb.hFileInfo.ioNamePtr = (StringPtr) fileName;
906 pb.hFileInfo.ioFDirIndex = 1;
907 if (PBGetCatInfoSync(&pb) == noErr) {
908 err = dupFNErr; /* EEXIST */
914 * DeleteDirectory removes a directory and all its contents, including
915 * any locked files. There is no interface to get the name of the
916 * file that caused the error, if an error occurs deleting this tree,
917 * unless we rewrite DeleteDirectory ourselves.
920 err = DeleteDirectory(fileSpec.vRefNum, dirID, NULL);
924 if (errorPtr != NULL) {
925 Tcl_UtfToExternalDString(NULL, path, -1, errorPtr);
928 FSpSetFLockCompat(&fileSpec);
930 errno = TclMacOSErrorToPosixError(err);
937 *---------------------------------------------------------------------------
941 * Gets FSSpecs for the specified path and its parent directory.
944 * The return value is noErr if there was no error getting FSSpecs,
945 * otherwise it is an error describing the problem. Fills buffers
946 * with information, as above.
951 *---------------------------------------------------------------------------
956 CONST char *path, /* The path to query. */
957 FSSpec *pathSpecPtr, /* Filled with information about path. */
958 FSSpec *dirSpecPtr, /* Filled with information about path's
959 * parent directory. */
960 Boolean *pathExistsPtr, /* Set to true if path actually exists,
961 * false if it doesn't or there was an
962 * error reading the specified path. */
963 Boolean *pathIsDirectoryPtr)/* Set to true if path is itself a directory,
964 * otherwise false. */
973 *pathExistsPtr = false;
974 *pathIsDirectoryPtr = false;
976 Tcl_DStringInit(&buffer);
977 Tcl_SplitPath(path, &argc, &argv);
981 dirName = Tcl_JoinPath(argc - 1, argv, &buffer);
983 err = FSpLocationFromPath(strlen(dirName), dirName, dirSpecPtr);
984 Tcl_DStringFree(&buffer);
985 ckfree((char *) argv);
988 err = FSpLocationFromPath(strlen(path), path, pathSpecPtr);
990 *pathExistsPtr = true;
991 err = FSpGetDirectoryID(pathSpecPtr, &d, pathIsDirectoryPtr);
992 } else if (err == fnfErr) {
1000 *-------------------------------------------------------------------------
1002 * FSpGetFLockCompat --
1004 * Determines if there exists a software lock on the specified
1005 * file. The software lock could prevent the file from being
1009 * Standard macintosh error code.
1015 *-------------------------------------------------------------------------
1020 const FSSpec *specPtr, /* File to query. */
1021 Boolean *lockedPtr) /* Set to true if file is locked, false
1022 * if it isn't or there was an error reading
1023 * specified file. */
1028 pb.hFileInfo.ioVRefNum = specPtr->vRefNum;
1029 pb.hFileInfo.ioDirID = specPtr->parID;
1030 pb.hFileInfo.ioNamePtr = (StringPtr) specPtr->name;
1031 pb.hFileInfo.ioFDirIndex = 0;
1033 err = PBGetCatInfoSync(&pb);
1034 if ((err == noErr) && (pb.hFileInfo.ioFlAttrib & 0x01)) {
1043 *----------------------------------------------------------------------
1047 * Pascal string compare.
1050 * Returns 1 if strings equal, 0 otherwise.
1055 *----------------------------------------------------------------------
1060 ConstStr255Param stringA, /* Pascal string A */
1061 ConstStr255Param stringB) /* Pascal string B */
1066 for (i = 0; i <= len; i++) {
1067 if (*stringA++ != *stringB++) {
1075 *----------------------------------------------------------------------
1077 * GetFileFinderAttributes --
1079 * Returns a Tcl_Obj containing the value of a file attribute
1080 * which is part of the FInfo record. Which attribute is controlled
1084 * Returns a standard TCL error. If the return value is TCL_OK,
1085 * the new creator or file type object is put into attributePtrPtr.
1086 * The object will have ref count 0. If there is an error,
1087 * attributePtrPtr is not touched.
1090 * A new object is allocated if the file is valid.
1092 *----------------------------------------------------------------------
1096 GetFileFinderAttributes(
1097 Tcl_Interp *interp, /* The interp to report errors with. */
1098 int objIndex, /* The index of the attribute option. */
1099 Tcl_Obj *fileName, /* The name of the file (UTF-8). */
1100 Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
1107 native=Tcl_FSGetNativePath(fileName);
1108 err = FSpLLocationFromPath(strlen(native),
1112 err = FSpGetFInfo(&fileSpec, &finfo);
1117 case MAC_CREATOR_ATTRIBUTE:
1118 *attributePtrPtr = Tcl_NewOSTypeObj(finfo.fdCreator);
1120 case MAC_HIDDEN_ATTRIBUTE:
1121 *attributePtrPtr = Tcl_NewBooleanObj(finfo.fdFlags
1124 case MAC_TYPE_ATTRIBUTE:
1125 *attributePtrPtr = Tcl_NewOSTypeObj(finfo.fdType);
1128 } else if (err == fnfErr) {
1130 Boolean isDirectory = 0;
1132 err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1133 if ((err == noErr) && isDirectory) {
1134 if (objIndex == MAC_HIDDEN_ATTRIBUTE) {
1135 *attributePtrPtr = Tcl_NewBooleanObj(0);
1137 *attributePtrPtr = Tcl_NewOSTypeObj('Fldr');
1143 errno = TclMacOSErrorToPosixError(err);
1144 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1145 "could not read \"", Tcl_GetString(fileName), "\": ",
1146 Tcl_PosixError(interp), (char *) NULL);
1153 *----------------------------------------------------------------------
1155 * GetFileReadOnly --
1157 * Returns a Tcl_Obj containing a Boolean value indicating whether
1158 * or not the file is read-only. The object will have ref count 0.
1159 * This procedure just checks the Finder attributes; it does not
1160 * check AppleShare sharing attributes.
1163 * Returns a standard TCL error. If the return value is TCL_OK,
1164 * the new creator type object is put into readOnlyPtrPtr.
1165 * If there is an error, readOnlyPtrPtr is not touched.
1168 * A new object is allocated if the file is valid.
1170 *----------------------------------------------------------------------
1175 Tcl_Interp *interp, /* The interp to report errors with. */
1176 int objIndex, /* The index of the attribute. */
1177 Tcl_Obj *fileName, /* The name of the file (UTF-8). */
1178 Tcl_Obj **readOnlyPtrPtr) /* A pointer to return the object with. */
1182 CInfoPBRec paramBlock;
1185 native=Tcl_FSGetNativePath(fileName);
1186 err = FSpLLocationFromPath(strlen(native),
1191 paramBlock.hFileInfo.ioCompletion = NULL;
1192 paramBlock.hFileInfo.ioNamePtr = fileSpec.name;
1193 paramBlock.hFileInfo.ioVRefNum = fileSpec.vRefNum;
1194 paramBlock.hFileInfo.ioFDirIndex = 0;
1195 paramBlock.hFileInfo.ioDirID = fileSpec.parID;
1196 err = PBGetCatInfo(¶mBlock, 0);
1200 * For some unknown reason, the Mac does not give
1201 * symbols for the bits in the ioFlAttrib field.
1205 *readOnlyPtrPtr = Tcl_NewBooleanObj(
1206 paramBlock.hFileInfo.ioFlAttrib & 1);
1211 errno = TclMacOSErrorToPosixError(err);
1212 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1213 "could not read \"", Tcl_GetString(fileName), "\": ",
1214 Tcl_PosixError(interp), (char *) NULL);
1221 *----------------------------------------------------------------------
1223 * SetFileFinderAttributes --
1225 * Sets the file to the creator or file type given by attributePtr.
1226 * objIndex determines whether the creator or file type is set.
1229 * Returns a standard TCL error.
1232 * The file's attribute is set.
1234 *----------------------------------------------------------------------
1238 SetFileFinderAttributes(
1239 Tcl_Interp *interp, /* The interp to report errors with. */
1240 int objIndex, /* The index of the attribute. */
1241 Tcl_Obj *fileName, /* The name of the file (UTF-8). */
1242 Tcl_Obj *attributePtr) /* The command line object. */
1249 native=Tcl_FSGetNativePath(fileName);
1250 err = FSpLLocationFromPath(strlen(native),
1254 err = FSpGetFInfo(&fileSpec, &finfo);
1259 case MAC_CREATOR_ATTRIBUTE:
1260 if (Tcl_GetOSTypeFromObj(interp, attributePtr,
1261 &finfo.fdCreator) != TCL_OK) {
1265 case MAC_HIDDEN_ATTRIBUTE: {
1268 if (Tcl_GetBooleanFromObj(interp, attributePtr, &hidden)
1273 finfo.fdFlags |= kIsInvisible;
1275 finfo.fdFlags &= ~kIsInvisible;
1279 case MAC_TYPE_ATTRIBUTE:
1280 if (Tcl_GetOSTypeFromObj(interp, attributePtr,
1281 &finfo.fdType) != TCL_OK) {
1286 err = FSpSetFInfo(&fileSpec, &finfo);
1287 } else if (err == fnfErr) {
1289 Boolean isDirectory = 0;
1291 err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1292 if ((err == noErr) && isDirectory) {
1293 Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
1294 Tcl_AppendStringsToObj(resultPtr, "cannot set ",
1295 tclpFileAttrStrings[objIndex], ": \"",
1296 Tcl_GetString(fileName), "\" is a directory", (char *) NULL);
1302 errno = TclMacOSErrorToPosixError(err);
1303 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1304 "could not read \"", Tcl_GetString(fileName), "\": ",
1305 Tcl_PosixError(interp), (char *) NULL);
1312 *----------------------------------------------------------------------
1314 * SetFileReadOnly --
1316 * Sets the file to be read-only according to the Boolean value
1317 * given by hiddenPtr.
1320 * Returns a standard TCL error.
1323 * The file's attribute is set.
1325 *----------------------------------------------------------------------
1330 Tcl_Interp *interp, /* The interp to report errors with. */
1331 int objIndex, /* The index of the attribute. */
1332 Tcl_Obj *fileName, /* The name of the file (UTF-8). */
1333 Tcl_Obj *readOnlyPtr) /* The command line object. */
1337 HParamBlockRec paramBlock;
1341 native=Tcl_FSGetNativePath(fileName);
1342 err = FSpLLocationFromPath(strlen(native),
1346 if (Tcl_GetBooleanFromObj(interp, readOnlyPtr, &hidden) != TCL_OK) {
1350 paramBlock.fileParam.ioCompletion = NULL;
1351 paramBlock.fileParam.ioNamePtr = fileSpec.name;
1352 paramBlock.fileParam.ioVRefNum = fileSpec.vRefNum;
1353 paramBlock.fileParam.ioDirID = fileSpec.parID;
1355 err = PBHSetFLock(¶mBlock, 0);
1357 err = PBHRstFLock(¶mBlock, 0);
1361 if (err == fnfErr) {
1363 Boolean isDirectory = 0;
1364 err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1365 if ((err == noErr) && isDirectory) {
1366 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1367 "cannot set a directory to read-only when File Sharing is turned off",
1376 errno = TclMacOSErrorToPosixError(err);
1377 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1378 "could not read \"", Tcl_GetString(fileName), "\": ",
1379 Tcl_PosixError(interp), (char *) NULL);
1386 *---------------------------------------------------------------------------
1388 * TclpObjListVolumes --
1390 * Lists the currently mounted volumes
1393 * The list of volumes.
1398 *---------------------------------------------------------------------------
1401 TclpObjListVolumes(void)
1405 OSErr theError = noErr;
1406 Tcl_Obj *resultPtr, *elemPtr;
1410 resultPtr = Tcl_NewObj();
1414 * 1) The Mac volumes are enumerated by the ioVolIndex parameter of
1415 * the HParamBlockRec. They run through the integers contiguously,
1417 * 2) PBHGetVInfoSync returns an error when you ask for a volume index
1418 * that does not exist.
1423 pb.volumeParam.ioNamePtr = (StringPtr) &name;
1424 pb.volumeParam.ioVolIndex = volIndex;
1426 theError = PBHGetVInfoSync(&pb);
1428 if ( theError != noErr ) {
1432 Tcl_ExternalToUtfDString(NULL, (CONST char *)&name[1], name[0], &dstr);
1433 elemPtr = Tcl_NewStringObj(Tcl_DStringValue(&dstr),
1434 Tcl_DStringLength(&dstr));
1435 Tcl_AppendToObj(elemPtr, ":", 1);
1436 Tcl_ListObjAppendElement(NULL, resultPtr, elemPtr);
1438 Tcl_DStringFree(&dstr);
1443 Tcl_IncrRefCount(resultPtr);
1448 *---------------------------------------------------------------------------
1450 * TclpObjNormalizePath --
1452 * This function scans through a path specification and replaces
1453 * it, in place, with a normalized version. On MacOS, this means
1454 * resolving all aliases present in the path and replacing the head of
1455 * pathPtr with the absolute case-sensitive path to the last file or
1456 * directory that could be validated in the path.
1459 * The new 'nextCheckpoint' value, giving as far as we could
1460 * understand in the path.
1463 * The pathPtr string, which must contain a valid path, is
1464 * possibly modified in place.
1466 *---------------------------------------------------------------------------
1470 TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
1475 #define MAXMACFILENAMELEN 31 /* assumed to be < sizeof(StrFileName) */
1477 StrFileName fileName;
1478 StringPtr fileNamePtr;
1479 int fileNameLen,newPathLen;
1480 Handle newPathHandle;
1484 Boolean isDirectory;
1485 Boolean wasAlias=FALSE;
1486 FSSpec fileSpec, lastFileSpec;
1488 Tcl_DString nativeds;
1491 int firstCheckpoint=nextCheckpoint, lastCheckpoint;
1493 char *path = Tcl_GetStringFromObj(pathPtr,&origPathLen);
1498 * check if substring to first ':' after initial
1499 * nextCheckpoint is a valid relative or absolute
1500 * path to a directory, if not we return without
1501 * normalizing anything
1505 cur = path[nextCheckpoint];
1506 if (cur == ':' || cur == 0) {
1508 /* jump over separator */
1509 nextCheckpoint++; cur = path[nextCheckpoint];
1511 Tcl_UtfToExternalDString(NULL,path,nextCheckpoint,&nativeds);
1512 err = FSpLLocationFromPath(Tcl_DStringLength(&nativeds),
1513 Tcl_DStringValue(&nativeds),
1515 Tcl_DStringFree(&nativeds);
1517 lastFileSpec=fileSpec;
1518 err = ResolveAliasFile(&fileSpec, true, &isDirectory,
1521 err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1522 currDirValid = ((err == noErr) && isDirectory);
1523 vRefNum = fileSpec.vRefNum;
1532 /* can't determine root dir, bail out */
1533 return firstCheckpoint;
1538 * Now vRefNum and dirID point to a valid
1539 * directory, so walk the rest of the path
1540 * ( code adapted from FSpLocationFromPath() )
1543 lastCheckpoint=nextCheckpoint;
1545 cur = path[nextCheckpoint];
1546 if (cur == ':' || cur == 0) {
1547 fileNameLen=nextCheckpoint-lastCheckpoint;
1548 fileNamePtr=fileName;
1549 if(fileNameLen==0) {
1552 * special case for empty dirname i.e. encountered
1553 * a '::' path component: get parent dir of currDir
1556 strcpy((char *) fileName + 1, "::");
1560 * empty filename, i.e. want FSSpec for currDir
1565 Tcl_UtfToExternalDString(NULL,&path[lastCheckpoint],
1566 fileNameLen,&nativeds);
1567 fileNameLen=Tcl_DStringLength(&nativeds);
1568 if(fileNameLen > MAXMACFILENAMELEN) {
1571 fileName[0]=fileNameLen;
1572 strncpy((char *) fileName + 1, Tcl_DStringValue(&nativeds),
1575 Tcl_DStringFree(&nativeds);
1578 err=FSMakeFSSpecCompat(vRefNum, dirID, fileNamePtr, &fileSpec);
1582 * this can occur if trying to get parent of a root
1583 * volume via '::' or when using an illegal
1584 * filename; revert to last checkpoint and stop
1585 * processing path further
1587 err=FSMakeFSSpecCompat(vRefNum, dirID, NULL, &fileSpec);
1589 /* should never happen, bail out */
1590 return firstCheckpoint;
1592 nextCheckpoint=lastCheckpoint;
1593 cur = path[lastCheckpoint];
1595 break; /* arrived at nonexistent file or dir */
1597 /* fileSpec could point to an alias, resolve it */
1598 lastFileSpec=fileSpec;
1599 err = ResolveAliasFile(&fileSpec, true, &isDirectory,
1601 if (err != noErr || !isDirectory) {
1602 break; /* fileSpec doesn't point to a dir */
1605 if (cur == 0) break; /* arrived at end of path */
1607 /* fileSpec points to possibly nonexisting subdirectory; validate */
1608 err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1609 if (err != noErr || !isDirectory) {
1610 break; /* fileSpec doesn't point to existing dir */
1612 vRefNum = fileSpec.vRefNum;
1614 /* found a new valid subdir in path, continue processing path */
1615 lastCheckpoint=nextCheckpoint+1;
1622 fileSpec=lastFileSpec;
1625 * fileSpec now points to a possibly nonexisting file or dir
1626 * inside a valid dir; get full path name to it
1629 err=FSpPathFromLocation(&fileSpec, &newPathLen, &newPathHandle);
1631 return firstCheckpoint; /* should not see any errors here, bail out */
1634 HLock(newPathHandle);
1635 Tcl_ExternalToUtfDString(NULL,*newPathHandle,newPathLen,&nativeds);
1637 /* not at end, append remaining path */
1638 if ( newPathLen==0 || (*(*newPathHandle+(newPathLen-1))!=':' && path[nextCheckpoint] !=':')) {
1639 Tcl_DStringAppend(&nativeds, ":" , 1);
1641 Tcl_DStringAppend(&nativeds, &path[nextCheckpoint],
1642 strlen(&path[nextCheckpoint]));
1644 DisposeHandle(newPathHandle);
1646 fileNameLen=Tcl_DStringLength(&nativeds);
1647 Tcl_SetStringObj(pathPtr,Tcl_DStringValue(&nativeds),fileNameLen);
1648 Tcl_DStringFree(&nativeds);
1650 return nextCheckpoint+(fileNameLen-origPathLen);