os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacFCmd.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacFCmd.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1652 @@
1.4 +/*
1.5 + * tclMacFCmd.c --
1.6 + *
1.7 + * Implements the Macintosh specific portions of the file manipulation
1.8 + * subcommands of the "file" command.
1.9 + *
1.10 + * Copyright (c) 1996-1998 Sun Microsystems, Inc.
1.11 + *
1.12 + * See the file "license.terms" for information on usage and redistribution
1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.14 + *
1.15 + * RCS: @(#) $Id: tclMacFCmd.c,v 1.19 2003/02/04 17:06:51 vincentdarley Exp $
1.16 + */
1.17 +
1.18 +#include "tclInt.h"
1.19 +#include "tclMac.h"
1.20 +#include "tclMacInt.h"
1.21 +#include "tclPort.h"
1.22 +#include <FSpCompat.h>
1.23 +#include <MoreFilesExtras.h>
1.24 +#include <Strings.h>
1.25 +#include <Errors.h>
1.26 +#include <FileCopy.h>
1.27 +#include <DirectoryCopy.h>
1.28 +#include <Script.h>
1.29 +#include <string.h>
1.30 +#include <Finder.h>
1.31 +#include <Aliases.h>
1.32 +
1.33 +/*
1.34 + * Callback for the file attributes code.
1.35 + */
1.36 +
1.37 +static int GetFileFinderAttributes _ANSI_ARGS_((Tcl_Interp *interp,
1.38 + int objIndex, Tcl_Obj *fileName,
1.39 + Tcl_Obj **attributePtrPtr));
1.40 +static int GetFileReadOnly _ANSI_ARGS_((Tcl_Interp *interp,
1.41 + int objIndex, Tcl_Obj *fileName,
1.42 + Tcl_Obj **readOnlyPtrPtr));
1.43 +static int SetFileFinderAttributes _ANSI_ARGS_((Tcl_Interp *interp,
1.44 + int objIndex, Tcl_Obj *fileName,
1.45 + Tcl_Obj *attributePtr));
1.46 +static int SetFileReadOnly _ANSI_ARGS_((Tcl_Interp *interp,
1.47 + int objIndex, Tcl_Obj *fileName,
1.48 + Tcl_Obj *readOnlyPtr));
1.49 +
1.50 +/*
1.51 + * These are indeces into the tclpFileAttrsStrings table below.
1.52 + */
1.53 +
1.54 +#define MAC_CREATOR_ATTRIBUTE 0
1.55 +#define MAC_HIDDEN_ATTRIBUTE 1
1.56 +#define MAC_READONLY_ATTRIBUTE 2
1.57 +#define MAC_TYPE_ATTRIBUTE 3
1.58 +
1.59 +/*
1.60 + * Global variables for the file attributes code.
1.61 + */
1.62 +
1.63 +CONST char *tclpFileAttrStrings[] = {"-creator", "-hidden", "-readonly",
1.64 + "-type", (char *) NULL};
1.65 +CONST TclFileAttrProcs tclpFileAttrProcs[] = {
1.66 + {GetFileFinderAttributes, SetFileFinderAttributes},
1.67 + {GetFileFinderAttributes, SetFileFinderAttributes},
1.68 + {GetFileReadOnly, SetFileReadOnly},
1.69 + {GetFileFinderAttributes, SetFileFinderAttributes}};
1.70 +
1.71 +/*
1.72 + * File specific static data
1.73 + */
1.74 +
1.75 +static long startSeed = 248923489;
1.76 +
1.77 +/*
1.78 + * Prototypes for procedure only used in this file
1.79 + */
1.80 +
1.81 +static pascal Boolean CopyErrHandler _ANSI_ARGS_((OSErr error,
1.82 + short failedOperation,
1.83 + short srcVRefNum, long srcDirID,
1.84 + ConstStr255Param srcName, short dstVRefNum,
1.85 + long dstDirID,ConstStr255Param dstName));
1.86 +static int DoCopyDirectory _ANSI_ARGS_((CONST char *src,
1.87 + CONST char *dst, Tcl_DString *errorPtr));
1.88 +static int DoCopyFile _ANSI_ARGS_((CONST char *src,
1.89 + CONST char *dst));
1.90 +static int DoCreateDirectory _ANSI_ARGS_((CONST char *path));
1.91 +static int DoRemoveDirectory _ANSI_ARGS_((CONST char *path,
1.92 + int recursive, Tcl_DString *errorPtr));
1.93 +static int DoRenameFile _ANSI_ARGS_((CONST char *src,
1.94 + CONST char *dst));
1.95 +OSErr FSpGetFLockCompat _ANSI_ARGS_((const FSSpec *specPtr,
1.96 + Boolean *lockedPtr));
1.97 +static OSErr GetFileSpecs _ANSI_ARGS_((CONST char *path,
1.98 + FSSpec *pathSpecPtr, FSSpec *dirSpecPtr,
1.99 + Boolean *pathExistsPtr,
1.100 + Boolean *pathIsDirectoryPtr));
1.101 +static OSErr MoveRename _ANSI_ARGS_((const FSSpec *srcSpecPtr,
1.102 + const FSSpec *dstSpecPtr, StringPtr copyName));
1.103 +static int Pstrequal _ANSI_ARGS_((ConstStr255Param stringA,
1.104 + ConstStr255Param stringB));
1.105 +
1.106 +/*
1.107 + *---------------------------------------------------------------------------
1.108 + *
1.109 + * TclpObjRenameFile, DoRenameFile --
1.110 + *
1.111 + * Changes the name of an existing file or directory, from src to dst.
1.112 + * If src and dst refer to the same file or directory, does nothing
1.113 + * and returns success. Otherwise if dst already exists, it will be
1.114 + * deleted and replaced by src subject to the following conditions:
1.115 + * If src is a directory, dst may be an empty directory.
1.116 + * If src is a file, dst may be a file.
1.117 + * In any other situation where dst already exists, the rename will
1.118 + * fail.
1.119 + *
1.120 + * Results:
1.121 + * If the directory was successfully created, returns TCL_OK.
1.122 + * Otherwise the return value is TCL_ERROR and errno is set to
1.123 + * indicate the error. Some possible values for errno are:
1.124 + *
1.125 + * EACCES: src or dst parent directory can't be read and/or written.
1.126 + * EEXIST: dst is a non-empty directory.
1.127 + * EINVAL: src is a root directory or dst is a subdirectory of src.
1.128 + * EISDIR: dst is a directory, but src is not.
1.129 + * ENOENT: src doesn't exist. src or dst is "".
1.130 + * ENOTDIR: src is a directory, but dst is not.
1.131 + * EXDEV: src and dst are on different filesystems.
1.132 + *
1.133 + * Side effects:
1.134 + * The implementation of rename may allow cross-filesystem renames,
1.135 + * but the caller should be prepared to emulate it with copy and
1.136 + * delete if errno is EXDEV.
1.137 + *
1.138 + *---------------------------------------------------------------------------
1.139 + */
1.140 +
1.141 +int
1.142 +TclpObjRenameFile(srcPathPtr, destPathPtr)
1.143 + Tcl_Obj *srcPathPtr;
1.144 + Tcl_Obj *destPathPtr;
1.145 +{
1.146 + return DoRenameFile(Tcl_FSGetNativePath(srcPathPtr),
1.147 + Tcl_FSGetNativePath(destPathPtr));
1.148 +}
1.149 +
1.150 +static int
1.151 +DoRenameFile(
1.152 + CONST char *src, /* Pathname of file or dir to be renamed
1.153 + * (native). */
1.154 + CONST char *dst) /* New pathname of file or directory
1.155 + * (native). */
1.156 +{
1.157 + FSSpec srcFileSpec, dstFileSpec, dstDirSpec;
1.158 + OSErr err;
1.159 + long srcID, dummy;
1.160 + Boolean srcIsDirectory, dstIsDirectory, dstExists, dstLocked;
1.161 +
1.162 + err = FSpLLocationFromPath(strlen(src), src, &srcFileSpec);
1.163 + if (err == noErr) {
1.164 + FSpGetDirectoryID(&srcFileSpec, &srcID, &srcIsDirectory);
1.165 + }
1.166 + if (err == noErr) {
1.167 + err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists,
1.168 + &dstIsDirectory);
1.169 + }
1.170 + if (err == noErr) {
1.171 + if (dstExists == 0) {
1.172 + err = MoveRename(&srcFileSpec, &dstDirSpec, dstFileSpec.name);
1.173 + goto end;
1.174 + }
1.175 + err = FSpGetFLockCompat(&dstFileSpec, &dstLocked);
1.176 + if (dstLocked) {
1.177 + FSpRstFLockCompat(&dstFileSpec);
1.178 + }
1.179 + }
1.180 + if (err == noErr) {
1.181 + if (srcIsDirectory) {
1.182 + if (dstIsDirectory) {
1.183 + /*
1.184 + * The following call will remove an empty directory. If it
1.185 + * fails, it's because it wasn't empty.
1.186 + */
1.187 +
1.188 + if (DoRemoveDirectory(dst, 0, NULL) != TCL_OK) {
1.189 + return TCL_ERROR;
1.190 + }
1.191 +
1.192 + /*
1.193 + * Now that that empty directory is gone, we can try
1.194 + * renaming src. If that fails, we'll put this empty
1.195 + * directory back, for completeness.
1.196 + */
1.197 +
1.198 + err = MoveRename(&srcFileSpec, &dstDirSpec, dstFileSpec.name);
1.199 + if (err != noErr) {
1.200 + FSpDirCreateCompat(&dstFileSpec, smSystemScript, &dummy);
1.201 + if (dstLocked) {
1.202 + FSpSetFLockCompat(&dstFileSpec);
1.203 + }
1.204 + }
1.205 + } else {
1.206 + errno = ENOTDIR;
1.207 + return TCL_ERROR;
1.208 + }
1.209 + } else {
1.210 + if (dstIsDirectory) {
1.211 + errno = EISDIR;
1.212 + return TCL_ERROR;
1.213 + } else {
1.214 + /*
1.215 + * Overwrite existing file by:
1.216 + *
1.217 + * 1. Rename existing file to temp name.
1.218 + * 2. Rename old file to new name.
1.219 + * 3. If success, delete temp file. If failure,
1.220 + * put temp file back to old name.
1.221 + */
1.222 +
1.223 + Str31 tmpName;
1.224 + FSSpec tmpFileSpec;
1.225 +
1.226 + err = GenerateUniqueName(dstFileSpec.vRefNum, &startSeed,
1.227 + dstFileSpec.parID, dstFileSpec.parID, tmpName);
1.228 + if (err == noErr) {
1.229 + err = FSpRenameCompat(&dstFileSpec, tmpName);
1.230 + }
1.231 + if (err == noErr) {
1.232 + err = FSMakeFSSpecCompat(dstFileSpec.vRefNum,
1.233 + dstFileSpec.parID, tmpName, &tmpFileSpec);
1.234 + }
1.235 + if (err == noErr) {
1.236 + err = MoveRename(&srcFileSpec, &dstDirSpec,
1.237 + dstFileSpec.name);
1.238 + }
1.239 + if (err == noErr) {
1.240 + FSpDeleteCompat(&tmpFileSpec);
1.241 + } else {
1.242 + FSpDeleteCompat(&dstFileSpec);
1.243 + FSpRenameCompat(&tmpFileSpec, dstFileSpec.name);
1.244 + if (dstLocked) {
1.245 + FSpSetFLockCompat(&dstFileSpec);
1.246 + }
1.247 + }
1.248 + }
1.249 + }
1.250 + }
1.251 +
1.252 + end:
1.253 + if (err != noErr) {
1.254 + errno = TclMacOSErrorToPosixError(err);
1.255 + return TCL_ERROR;
1.256 + }
1.257 + return TCL_OK;
1.258 +}
1.259 +
1.260 +/*
1.261 + *--------------------------------------------------------------------------
1.262 + *
1.263 + * MoveRename --
1.264 + *
1.265 + * Helper function for TclpRenameFile. Renames a file or directory
1.266 + * into the same directory or another directory. The target name
1.267 + * must not already exist in the destination directory.
1.268 + *
1.269 + * Don't use FSpMoveRenameCompat because it doesn't work with
1.270 + * directories or with locked files.
1.271 + *
1.272 + * Results:
1.273 + * Returns a mac error indicating the cause of the failure.
1.274 + *
1.275 + * Side effects:
1.276 + * Creates a temp file in the target directory to handle a rename
1.277 + * between directories.
1.278 + *
1.279 + *--------------------------------------------------------------------------
1.280 + */
1.281 +
1.282 +static OSErr
1.283 +MoveRename(
1.284 + const FSSpec *srcFileSpecPtr, /* Source object. */
1.285 + const FSSpec *dstDirSpecPtr, /* Destination directory. */
1.286 + StringPtr copyName) /* New name for object in destination
1.287 + * directory. */
1.288 +{
1.289 + OSErr err;
1.290 + long srcID, dstID;
1.291 + Boolean srcIsDir, dstIsDir;
1.292 + Str31 tmpName;
1.293 + FSSpec dstFileSpec, srcDirSpec, tmpSrcFileSpec, tmpDstFileSpec;
1.294 + Boolean locked;
1.295 +
1.296 + if (srcFileSpecPtr->parID == 1) {
1.297 + /*
1.298 + * Trying to rename a volume.
1.299 + */
1.300 +
1.301 + return badMovErr;
1.302 + }
1.303 + if (srcFileSpecPtr->vRefNum != dstDirSpecPtr->vRefNum) {
1.304 + /*
1.305 + * Renaming across volumes.
1.306 + */
1.307 +
1.308 + return diffVolErr;
1.309 + }
1.310 + err = FSpGetFLockCompat(srcFileSpecPtr, &locked);
1.311 + if (locked) {
1.312 + FSpRstFLockCompat(srcFileSpecPtr);
1.313 + }
1.314 + if (err == noErr) {
1.315 + err = FSpGetDirectoryID(dstDirSpecPtr, &dstID, &dstIsDir);
1.316 + }
1.317 + if (err == noErr) {
1.318 + if (srcFileSpecPtr->parID == dstID) {
1.319 + /*
1.320 + * Renaming object within directory.
1.321 + */
1.322 +
1.323 + err = FSpRenameCompat(srcFileSpecPtr, copyName);
1.324 + goto done;
1.325 + }
1.326 + if (Pstrequal(srcFileSpecPtr->name, copyName)) {
1.327 + /*
1.328 + * Moving object to another directory (under same name).
1.329 + */
1.330 +
1.331 + err = FSpCatMoveCompat(srcFileSpecPtr, dstDirSpecPtr);
1.332 + goto done;
1.333 + }
1.334 + err = FSpGetDirectoryID(srcFileSpecPtr, &srcID, &srcIsDir);
1.335 + }
1.336 + if (err == noErr) {
1.337 + /*
1.338 + * Fullblown: rename source object to temp name, move temp to
1.339 + * dest directory, and rename temp to target.
1.340 + */
1.341 +
1.342 + err = GenerateUniqueName(srcFileSpecPtr->vRefNum, &startSeed,
1.343 + srcFileSpecPtr->parID, dstID, tmpName);
1.344 + FSMakeFSSpecCompat(srcFileSpecPtr->vRefNum, srcFileSpecPtr->parID,
1.345 + tmpName, &tmpSrcFileSpec);
1.346 + FSMakeFSSpecCompat(dstDirSpecPtr->vRefNum, dstID, tmpName,
1.347 + &tmpDstFileSpec);
1.348 + }
1.349 + if (err == noErr) {
1.350 + err = FSpRenameCompat(srcFileSpecPtr, tmpName);
1.351 + }
1.352 + if (err == noErr) {
1.353 + err = FSpCatMoveCompat(&tmpSrcFileSpec, dstDirSpecPtr);
1.354 + if (err == noErr) {
1.355 + err = FSpRenameCompat(&tmpDstFileSpec, copyName);
1.356 + if (err == noErr) {
1.357 + goto done;
1.358 + }
1.359 + FSMakeFSSpecCompat(srcFileSpecPtr->vRefNum, srcFileSpecPtr->parID,
1.360 + NULL, &srcDirSpec);
1.361 + FSpCatMoveCompat(&tmpDstFileSpec, &srcDirSpec);
1.362 + }
1.363 + FSpRenameCompat(&tmpSrcFileSpec, srcFileSpecPtr->name);
1.364 + }
1.365 +
1.366 + done:
1.367 + if (locked != false) {
1.368 + if (err == noErr) {
1.369 + FSMakeFSSpecCompat(dstDirSpecPtr->vRefNum,
1.370 + dstID, copyName, &dstFileSpec);
1.371 + FSpSetFLockCompat(&dstFileSpec);
1.372 + } else {
1.373 + FSpSetFLockCompat(srcFileSpecPtr);
1.374 + }
1.375 + }
1.376 + return err;
1.377 +}
1.378 +
1.379 +/*
1.380 + *---------------------------------------------------------------------------
1.381 + *
1.382 + * TclpObjCopyFile, DoCopyFile --
1.383 + *
1.384 + * Copy a single file (not a directory). If dst already exists and
1.385 + * is not a directory, it is removed.
1.386 + *
1.387 + * Results:
1.388 + * If the file was successfully copied, returns TCL_OK. Otherwise
1.389 + * the return value is TCL_ERROR and errno is set to indicate the
1.390 + * error. Some possible values for errno are:
1.391 + *
1.392 + * EACCES: src or dst parent directory can't be read and/or written.
1.393 + * EISDIR: src or dst is a directory.
1.394 + * ENOENT: src doesn't exist. src or dst is "".
1.395 + *
1.396 + * Side effects:
1.397 + * This procedure will also copy symbolic links, block, and
1.398 + * character devices, and fifos. For symbolic links, the links
1.399 + * themselves will be copied and not what they point to. For the
1.400 + * other special file types, the directory entry will be copied and
1.401 + * not the contents of the device that it refers to.
1.402 + *
1.403 + *---------------------------------------------------------------------------
1.404 + */
1.405 +
1.406 +int
1.407 +TclpObjCopyFile(srcPathPtr, destPathPtr)
1.408 + Tcl_Obj *srcPathPtr;
1.409 + Tcl_Obj *destPathPtr;
1.410 +{
1.411 + return DoCopyFile(Tcl_FSGetNativePath(srcPathPtr),
1.412 + Tcl_FSGetNativePath(destPathPtr));
1.413 +}
1.414 +
1.415 +static int
1.416 +DoCopyFile(
1.417 + CONST char *src, /* Pathname of file to be copied (native). */
1.418 + CONST char *dst) /* Pathname of file to copy to (native). */
1.419 +{
1.420 + OSErr err, dstErr;
1.421 + Boolean dstExists, dstIsDirectory, dstLocked;
1.422 + FSSpec srcFileSpec, dstFileSpec, dstDirSpec, tmpFileSpec;
1.423 + Str31 tmpName;
1.424 +
1.425 + err = FSpLLocationFromPath(strlen(src), src, &srcFileSpec);
1.426 + if (err == noErr) {
1.427 + err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists,
1.428 + &dstIsDirectory);
1.429 + }
1.430 + if (dstExists) {
1.431 + if (dstIsDirectory) {
1.432 + errno = EISDIR;
1.433 + return TCL_ERROR;
1.434 + }
1.435 + err = FSpGetFLockCompat(&dstFileSpec, &dstLocked);
1.436 + if (dstLocked) {
1.437 + FSpRstFLockCompat(&dstFileSpec);
1.438 + }
1.439 +
1.440 + /*
1.441 + * Backup dest file.
1.442 + */
1.443 +
1.444 + dstErr = GenerateUniqueName(dstFileSpec.vRefNum, &startSeed, dstFileSpec.parID,
1.445 + dstFileSpec.parID, tmpName);
1.446 + if (dstErr == noErr) {
1.447 + dstErr = FSpRenameCompat(&dstFileSpec, tmpName);
1.448 + }
1.449 + }
1.450 + if (err == noErr) {
1.451 + err = FSpFileCopy(&srcFileSpec, &dstDirSpec,
1.452 + (StringPtr) dstFileSpec.name, NULL, 0, true);
1.453 + }
1.454 + if ((dstExists != false) && (dstErr == noErr)) {
1.455 + FSMakeFSSpecCompat(dstFileSpec.vRefNum, dstFileSpec.parID,
1.456 + tmpName, &tmpFileSpec);
1.457 + if (err == noErr) {
1.458 + /*
1.459 + * Delete backup file.
1.460 + */
1.461 +
1.462 + FSpDeleteCompat(&tmpFileSpec);
1.463 + } else {
1.464 +
1.465 + /*
1.466 + * Restore backup file.
1.467 + */
1.468 +
1.469 + FSpDeleteCompat(&dstFileSpec);
1.470 + FSpRenameCompat(&tmpFileSpec, dstFileSpec.name);
1.471 + if (dstLocked) {
1.472 + FSpSetFLockCompat(&dstFileSpec);
1.473 + }
1.474 + }
1.475 + }
1.476 +
1.477 + if (err != noErr) {
1.478 + errno = TclMacOSErrorToPosixError(err);
1.479 + return TCL_ERROR;
1.480 + }
1.481 + return TCL_OK;
1.482 +}
1.483 +
1.484 +/*
1.485 + *---------------------------------------------------------------------------
1.486 + *
1.487 + * TclpObjDeleteFile, TclpDeleteFile --
1.488 + *
1.489 + * Removes a single file (not a directory).
1.490 + *
1.491 + * Results:
1.492 + * If the file was successfully deleted, returns TCL_OK. Otherwise
1.493 + * the return value is TCL_ERROR and errno is set to indicate the
1.494 + * error. Some possible values for errno are:
1.495 + *
1.496 + * EACCES: a parent directory can't be read and/or written.
1.497 + * EISDIR: path is a directory.
1.498 + * ENOENT: path doesn't exist or is "".
1.499 + *
1.500 + * Side effects:
1.501 + * The file is deleted, even if it is read-only.
1.502 + *
1.503 + *---------------------------------------------------------------------------
1.504 + */
1.505 +
1.506 +int
1.507 +TclpObjDeleteFile(pathPtr)
1.508 + Tcl_Obj *pathPtr;
1.509 +{
1.510 + return TclpDeleteFile(Tcl_FSGetNativePath(pathPtr));
1.511 +}
1.512 +
1.513 +int
1.514 +TclpDeleteFile(
1.515 + CONST char *path) /* Pathname of file to be removed (native). */
1.516 +{
1.517 + OSErr err;
1.518 + FSSpec fileSpec;
1.519 + Boolean isDirectory;
1.520 + long dirID;
1.521 +
1.522 + err = FSpLLocationFromPath(strlen(path), path, &fileSpec);
1.523 + if (err == noErr) {
1.524 + /*
1.525 + * Since FSpDeleteCompat will delete an empty directory, make sure
1.526 + * that this isn't a directory first.
1.527 + */
1.528 +
1.529 + FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1.530 + if (isDirectory == true) {
1.531 + errno = EISDIR;
1.532 + return TCL_ERROR;
1.533 + }
1.534 + }
1.535 + err = FSpDeleteCompat(&fileSpec);
1.536 + if (err == fLckdErr) {
1.537 + FSpRstFLockCompat(&fileSpec);
1.538 + err = FSpDeleteCompat(&fileSpec);
1.539 + if (err != noErr) {
1.540 + FSpSetFLockCompat(&fileSpec);
1.541 + }
1.542 + }
1.543 + if (err != noErr) {
1.544 + errno = TclMacOSErrorToPosixError(err);
1.545 + return TCL_ERROR;
1.546 + }
1.547 + return TCL_OK;
1.548 +}
1.549 +
1.550 +/*
1.551 + *---------------------------------------------------------------------------
1.552 + *
1.553 + * TclpObjCreateDirectory, DoCreateDirectory --
1.554 + *
1.555 + * Creates the specified directory. All parent directories of the
1.556 + * specified directory must already exist. The directory is
1.557 + * automatically created with permissions so that user can access
1.558 + * the new directory and create new files or subdirectories in it.
1.559 + *
1.560 + * Results:
1.561 + * If the directory was successfully created, returns TCL_OK.
1.562 + * Otherwise the return value is TCL_ERROR and errno is set to
1.563 + * indicate the error. Some possible values for errno are:
1.564 + *
1.565 + * EACCES: a parent directory can't be read and/or written.
1.566 + * EEXIST: path already exists.
1.567 + * ENOENT: a parent directory doesn't exist.
1.568 + *
1.569 + * Side effects:
1.570 + * A directory is created with the current umask, except that
1.571 + * permission for u+rwx will always be added.
1.572 + *
1.573 + *---------------------------------------------------------------------------
1.574 + */
1.575 +
1.576 +int
1.577 +TclpObjCreateDirectory(pathPtr)
1.578 + Tcl_Obj *pathPtr;
1.579 +{
1.580 + return DoCreateDirectory(Tcl_FSGetNativePath(pathPtr));
1.581 +}
1.582 +
1.583 +static int
1.584 +DoCreateDirectory(
1.585 + CONST char *path) /* Pathname of directory to create (native). */
1.586 +{
1.587 + OSErr err;
1.588 + FSSpec dirSpec;
1.589 + long outDirID;
1.590 +
1.591 + err = FSpLocationFromPath(strlen(path), path, &dirSpec);
1.592 + if (err == noErr) {
1.593 + err = dupFNErr; /* EEXIST. */
1.594 + } else if (err == fnfErr) {
1.595 + err = FSpDirCreateCompat(&dirSpec, smSystemScript, &outDirID);
1.596 + }
1.597 +
1.598 + if (err != noErr) {
1.599 + errno = TclMacOSErrorToPosixError(err);
1.600 + return TCL_ERROR;
1.601 + }
1.602 + return TCL_OK;
1.603 +}
1.604 +
1.605 +/*
1.606 + *---------------------------------------------------------------------------
1.607 + *
1.608 + * TclpObjCopyDirectory, DoCopyDirectory --
1.609 + *
1.610 + * Recursively copies a directory. The target directory dst must
1.611 + * not already exist. Note that this function does not merge two
1.612 + * directory hierarchies, even if the target directory is an an
1.613 + * empty directory.
1.614 + *
1.615 + * Results:
1.616 + * If the directory was successfully copied, returns TCL_OK.
1.617 + * Otherwise the return value is TCL_ERROR, errno is set to indicate
1.618 + * the error, and the pathname of the file that caused the error
1.619 + * is stored in errorPtr. See TclpCreateDirectory and TclpCopyFile
1.620 + * for a description of possible values for errno.
1.621 + *
1.622 + * Side effects:
1.623 + * An exact copy of the directory hierarchy src will be created
1.624 + * with the name dst. If an error occurs, the error will
1.625 + * be returned immediately, and remaining files will not be
1.626 + * processed.
1.627 + *
1.628 + *---------------------------------------------------------------------------
1.629 + */
1.630 +
1.631 +int
1.632 +TclpObjCopyDirectory(srcPathPtr, destPathPtr, errorPtr)
1.633 + Tcl_Obj *srcPathPtr;
1.634 + Tcl_Obj *destPathPtr;
1.635 + Tcl_Obj **errorPtr;
1.636 +{
1.637 + Tcl_DString ds;
1.638 + int ret;
1.639 + ret = DoCopyDirectory(Tcl_FSGetNativePath(srcPathPtr),
1.640 + Tcl_FSGetNativePath(destPathPtr), &ds);
1.641 + if (ret != TCL_OK) {
1.642 + *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
1.643 + Tcl_DStringFree(&ds);
1.644 + Tcl_IncrRefCount(*errorPtr);
1.645 + }
1.646 + return ret;
1.647 +}
1.648 +
1.649 +static int
1.650 +DoCopyDirectory(
1.651 + CONST char *src, /* Pathname of directory to be copied
1.652 + * (Native). */
1.653 + CONST char *dst, /* Pathname of target directory (Native). */
1.654 + Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free
1.655 + * DString filled with UTF-8 name of file
1.656 + * causing error. */
1.657 +{
1.658 + OSErr err, saveErr;
1.659 + long srcID, tmpDirID;
1.660 + FSSpec srcFileSpec, dstFileSpec, dstDirSpec, tmpDirSpec, tmpFileSpec;
1.661 + Boolean srcIsDirectory, srcLocked;
1.662 + Boolean dstIsDirectory, dstExists;
1.663 + Str31 tmpName;
1.664 +
1.665 + err = FSpLocationFromPath(strlen(src), src, &srcFileSpec);
1.666 + if (err == noErr) {
1.667 + err = FSpGetDirectoryID(&srcFileSpec, &srcID, &srcIsDirectory);
1.668 + }
1.669 + if (err == noErr) {
1.670 + if (srcIsDirectory == false) {
1.671 + err = afpObjectTypeErr; /* ENOTDIR. */
1.672 + }
1.673 + }
1.674 + if (err == noErr) {
1.675 + err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists,
1.676 + &dstIsDirectory);
1.677 + }
1.678 + if (dstExists) {
1.679 + if (dstIsDirectory == false) {
1.680 + err = afpObjectTypeErr; /* ENOTDIR. */
1.681 + } else {
1.682 + err = dupFNErr; /* EEXIST. */
1.683 + }
1.684 + }
1.685 + if (err != noErr) {
1.686 + goto done;
1.687 + }
1.688 + if ((srcFileSpec.vRefNum == dstFileSpec.vRefNum) &&
1.689 + (srcFileSpec.parID == dstFileSpec.parID) &&
1.690 + (Pstrequal(srcFileSpec.name, dstFileSpec.name) != 0)) {
1.691 + /*
1.692 + * Copying on top of self. No-op.
1.693 + */
1.694 +
1.695 + goto done;
1.696 + }
1.697 +
1.698 + /*
1.699 + * This algorthm will work making a copy of the source directory in
1.700 + * the current directory with a new name, in a new directory with the
1.701 + * same name, and in a new directory with a new name:
1.702 + *
1.703 + * 1. Make dstDir/tmpDir.
1.704 + * 2. Copy srcDir/src to dstDir/tmpDir/src
1.705 + * 3. Rename dstDir/tmpDir/src to dstDir/tmpDir/dst (if necessary).
1.706 + * 4. CatMove dstDir/tmpDir/dst to dstDir/dst.
1.707 + * 5. Remove dstDir/tmpDir.
1.708 + */
1.709 +
1.710 + err = FSpGetFLockCompat(&srcFileSpec, &srcLocked);
1.711 + if (srcLocked) {
1.712 + FSpRstFLockCompat(&srcFileSpec);
1.713 + }
1.714 + if (err == noErr) {
1.715 + err = GenerateUniqueName(dstFileSpec.vRefNum, &startSeed, dstFileSpec.parID,
1.716 + dstFileSpec.parID, tmpName);
1.717 + }
1.718 + if (err == noErr) {
1.719 + FSMakeFSSpecCompat(dstFileSpec.vRefNum, dstFileSpec.parID,
1.720 + tmpName, &tmpDirSpec);
1.721 + err = FSpDirCreateCompat(&tmpDirSpec, smSystemScript, &tmpDirID);
1.722 + }
1.723 + if (err == noErr) {
1.724 + err = FSpDirectoryCopy(&srcFileSpec, &tmpDirSpec, NULL, NULL, 0, true,
1.725 + CopyErrHandler);
1.726 + }
1.727 +
1.728 + /*
1.729 + * Even if the Copy failed, Rename/Move whatever did get copied to the
1.730 + * appropriate final destination, if possible.
1.731 + */
1.732 +
1.733 + saveErr = err;
1.734 + err = noErr;
1.735 + if (Pstrequal(srcFileSpec.name, dstFileSpec.name) == 0) {
1.736 + err = FSMakeFSSpecCompat(tmpDirSpec.vRefNum, tmpDirID,
1.737 + srcFileSpec.name, &tmpFileSpec);
1.738 + if (err == noErr) {
1.739 + err = FSpRenameCompat(&tmpFileSpec, dstFileSpec.name);
1.740 + }
1.741 + }
1.742 + if (err == noErr) {
1.743 + err = FSMakeFSSpecCompat(tmpDirSpec.vRefNum, tmpDirID,
1.744 + dstFileSpec.name, &tmpFileSpec);
1.745 + }
1.746 + if (err == noErr) {
1.747 + err = FSpCatMoveCompat(&tmpFileSpec, &dstDirSpec);
1.748 + }
1.749 + if (err == noErr) {
1.750 + if (srcLocked) {
1.751 + FSpSetFLockCompat(&dstFileSpec);
1.752 + }
1.753 + }
1.754 +
1.755 + FSpDeleteCompat(&tmpDirSpec);
1.756 +
1.757 + if (saveErr != noErr) {
1.758 + err = saveErr;
1.759 + }
1.760 +
1.761 + done:
1.762 + if (err != noErr) {
1.763 + errno = TclMacOSErrorToPosixError(err);
1.764 + if (errorPtr != NULL) {
1.765 + Tcl_ExternalToUtfDString(NULL, dst, -1, errorPtr);
1.766 + }
1.767 + return TCL_ERROR;
1.768 + }
1.769 + return TCL_OK;
1.770 +}
1.771 +
1.772 +/*
1.773 + *----------------------------------------------------------------------
1.774 + *
1.775 + * CopyErrHandler --
1.776 + *
1.777 + * This procedure is called from the MoreFiles procedure
1.778 + * FSpDirectoryCopy whenever an error occurs.
1.779 + *
1.780 + * Results:
1.781 + * False if the condition should not be considered an error, true
1.782 + * otherwise.
1.783 + *
1.784 + * Side effects:
1.785 + * Since FSpDirectoryCopy() is called only after removing any
1.786 + * existing target directories, there shouldn't be any errors.
1.787 + *
1.788 + *----------------------------------------------------------------------
1.789 + */
1.790 +
1.791 +static pascal Boolean
1.792 +CopyErrHandler(
1.793 + OSErr error, /* Error that occured */
1.794 + short failedOperation, /* operation that caused the error */
1.795 + short srcVRefNum, /* volume ref number of source */
1.796 + long srcDirID, /* directory id of source */
1.797 + ConstStr255Param srcName, /* name of source */
1.798 + short dstVRefNum, /* volume ref number of dst */
1.799 + long dstDirID, /* directory id of dst */
1.800 + ConstStr255Param dstName) /* name of dst directory */
1.801 +{
1.802 + return true;
1.803 +}
1.804 +
1.805 +/*
1.806 + *---------------------------------------------------------------------------
1.807 + *
1.808 + * TclpObjRemoveDirectory, DoRemoveDirectory --
1.809 + *
1.810 + * Removes directory (and its contents, if the recursive flag is set).
1.811 + *
1.812 + * Results:
1.813 + * If the directory was successfully removed, returns TCL_OK.
1.814 + * Otherwise the return value is TCL_ERROR, errno is set to indicate
1.815 + * the error, and the pathname of the file that caused the error
1.816 + * is stored in errorPtr. Some possible values for errno are:
1.817 + *
1.818 + * EACCES: path directory can't be read and/or written.
1.819 + * EEXIST: path is a non-empty directory.
1.820 + * EINVAL: path is a root directory.
1.821 + * ENOENT: path doesn't exist or is "".
1.822 + * ENOTDIR: path is not a directory.
1.823 + *
1.824 + * Side effects:
1.825 + * Directory removed. If an error occurs, the error will be returned
1.826 + * immediately, and remaining files will not be deleted.
1.827 + *
1.828 + *---------------------------------------------------------------------------
1.829 + */
1.830 +
1.831 +int
1.832 +TclpObjRemoveDirectory(pathPtr, recursive, errorPtr)
1.833 + Tcl_Obj *pathPtr;
1.834 + int recursive;
1.835 + Tcl_Obj **errorPtr;
1.836 +{
1.837 + Tcl_DString ds;
1.838 + int ret;
1.839 + ret = DoRemoveDirectory(Tcl_FSGetNativePath(pathPtr),recursive, &ds);
1.840 + if (ret != TCL_OK) {
1.841 + *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
1.842 + Tcl_DStringFree(&ds);
1.843 + Tcl_IncrRefCount(*errorPtr);
1.844 + }
1.845 + return ret;
1.846 +}
1.847 +
1.848 +static int
1.849 +DoRemoveDirectory(
1.850 + CONST char *path, /* Pathname of directory to be removed
1.851 + * (native). */
1.852 + int recursive, /* If non-zero, removes directories that
1.853 + * are nonempty. Otherwise, will only remove
1.854 + * empty directories. */
1.855 + Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free
1.856 + * DString filled with UTF-8 name of file
1.857 + * causing error. */
1.858 +{
1.859 + OSErr err;
1.860 + FSSpec fileSpec;
1.861 + long dirID;
1.862 + int locked;
1.863 + Boolean isDirectory;
1.864 + CInfoPBRec pb;
1.865 + Str255 fileName;
1.866 +
1.867 +
1.868 + locked = 0;
1.869 + err = FSpLocationFromPath(strlen(path), path, &fileSpec);
1.870 + if (err != noErr) {
1.871 + goto done;
1.872 + }
1.873 +
1.874 + /*
1.875 + * Since FSpDeleteCompat will delete a file, make sure this isn't
1.876 + * a file first.
1.877 + */
1.878 +
1.879 + isDirectory = 1;
1.880 + FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1.881 + if (isDirectory == 0) {
1.882 + errno = ENOTDIR;
1.883 + return TCL_ERROR;
1.884 + }
1.885 +
1.886 + err = FSpDeleteCompat(&fileSpec);
1.887 + if (err == fLckdErr) {
1.888 + locked = 1;
1.889 + FSpRstFLockCompat(&fileSpec);
1.890 + err = FSpDeleteCompat(&fileSpec);
1.891 + }
1.892 + if (err == noErr) {
1.893 + return TCL_OK;
1.894 + }
1.895 + if (err != fBsyErr) {
1.896 + goto done;
1.897 + }
1.898 +
1.899 + if (recursive == 0) {
1.900 + /*
1.901 + * fBsyErr means one of three things: file busy, directory not empty,
1.902 + * or working directory control block open. Determine if directory
1.903 + * is empty. If directory is not empty, return EEXIST.
1.904 + */
1.905 +
1.906 + pb.hFileInfo.ioVRefNum = fileSpec.vRefNum;
1.907 + pb.hFileInfo.ioDirID = dirID;
1.908 + pb.hFileInfo.ioNamePtr = (StringPtr) fileName;
1.909 + pb.hFileInfo.ioFDirIndex = 1;
1.910 + if (PBGetCatInfoSync(&pb) == noErr) {
1.911 + err = dupFNErr; /* EEXIST */
1.912 + goto done;
1.913 + }
1.914 + }
1.915 +
1.916 + /*
1.917 + * DeleteDirectory removes a directory and all its contents, including
1.918 + * any locked files. There is no interface to get the name of the
1.919 + * file that caused the error, if an error occurs deleting this tree,
1.920 + * unless we rewrite DeleteDirectory ourselves.
1.921 + */
1.922 +
1.923 + err = DeleteDirectory(fileSpec.vRefNum, dirID, NULL);
1.924 +
1.925 + done:
1.926 + if (err != noErr) {
1.927 + if (errorPtr != NULL) {
1.928 + Tcl_UtfToExternalDString(NULL, path, -1, errorPtr);
1.929 + }
1.930 + if (locked) {
1.931 + FSpSetFLockCompat(&fileSpec);
1.932 + }
1.933 + errno = TclMacOSErrorToPosixError(err);
1.934 + return TCL_ERROR;
1.935 + }
1.936 + return TCL_OK;
1.937 +}
1.938 +
1.939 +/*
1.940 + *---------------------------------------------------------------------------
1.941 + *
1.942 + * GetFileSpecs --
1.943 + *
1.944 + * Gets FSSpecs for the specified path and its parent directory.
1.945 + *
1.946 + * Results:
1.947 + * The return value is noErr if there was no error getting FSSpecs,
1.948 + * otherwise it is an error describing the problem. Fills buffers
1.949 + * with information, as above.
1.950 + *
1.951 + * Side effects:
1.952 + * None.
1.953 + *
1.954 + *---------------------------------------------------------------------------
1.955 + */
1.956 +
1.957 +static OSErr
1.958 +GetFileSpecs(
1.959 + CONST char *path, /* The path to query. */
1.960 + FSSpec *pathSpecPtr, /* Filled with information about path. */
1.961 + FSSpec *dirSpecPtr, /* Filled with information about path's
1.962 + * parent directory. */
1.963 + Boolean *pathExistsPtr, /* Set to true if path actually exists,
1.964 + * false if it doesn't or there was an
1.965 + * error reading the specified path. */
1.966 + Boolean *pathIsDirectoryPtr)/* Set to true if path is itself a directory,
1.967 + * otherwise false. */
1.968 +{
1.969 + CONST char *dirName;
1.970 + OSErr err;
1.971 + int argc;
1.972 + CONST char **argv;
1.973 + long d;
1.974 + Tcl_DString buffer;
1.975 +
1.976 + *pathExistsPtr = false;
1.977 + *pathIsDirectoryPtr = false;
1.978 +
1.979 + Tcl_DStringInit(&buffer);
1.980 + Tcl_SplitPath(path, &argc, &argv);
1.981 + if (argc == 1) {
1.982 + dirName = ":";
1.983 + } else {
1.984 + dirName = Tcl_JoinPath(argc - 1, argv, &buffer);
1.985 + }
1.986 + err = FSpLocationFromPath(strlen(dirName), dirName, dirSpecPtr);
1.987 + Tcl_DStringFree(&buffer);
1.988 + ckfree((char *) argv);
1.989 +
1.990 + if (err == noErr) {
1.991 + err = FSpLocationFromPath(strlen(path), path, pathSpecPtr);
1.992 + if (err == noErr) {
1.993 + *pathExistsPtr = true;
1.994 + err = FSpGetDirectoryID(pathSpecPtr, &d, pathIsDirectoryPtr);
1.995 + } else if (err == fnfErr) {
1.996 + err = noErr;
1.997 + }
1.998 + }
1.999 + return err;
1.1000 +}
1.1001 +
1.1002 +/*
1.1003 + *-------------------------------------------------------------------------
1.1004 + *
1.1005 + * FSpGetFLockCompat --
1.1006 + *
1.1007 + * Determines if there exists a software lock on the specified
1.1008 + * file. The software lock could prevent the file from being
1.1009 + * renamed or moved.
1.1010 + *
1.1011 + * Results:
1.1012 + * Standard macintosh error code.
1.1013 + *
1.1014 + * Side effects:
1.1015 + * None.
1.1016 + *
1.1017 + *
1.1018 + *-------------------------------------------------------------------------
1.1019 + */
1.1020 +
1.1021 +OSErr
1.1022 +FSpGetFLockCompat(
1.1023 + const FSSpec *specPtr, /* File to query. */
1.1024 + Boolean *lockedPtr) /* Set to true if file is locked, false
1.1025 + * if it isn't or there was an error reading
1.1026 + * specified file. */
1.1027 +{
1.1028 + CInfoPBRec pb;
1.1029 + OSErr err;
1.1030 +
1.1031 + pb.hFileInfo.ioVRefNum = specPtr->vRefNum;
1.1032 + pb.hFileInfo.ioDirID = specPtr->parID;
1.1033 + pb.hFileInfo.ioNamePtr = (StringPtr) specPtr->name;
1.1034 + pb.hFileInfo.ioFDirIndex = 0;
1.1035 +
1.1036 + err = PBGetCatInfoSync(&pb);
1.1037 + if ((err == noErr) && (pb.hFileInfo.ioFlAttrib & 0x01)) {
1.1038 + *lockedPtr = true;
1.1039 + } else {
1.1040 + *lockedPtr = false;
1.1041 + }
1.1042 + return err;
1.1043 +}
1.1044 +
1.1045 +/*
1.1046 + *----------------------------------------------------------------------
1.1047 + *
1.1048 + * Pstrequal --
1.1049 + *
1.1050 + * Pascal string compare.
1.1051 + *
1.1052 + * Results:
1.1053 + * Returns 1 if strings equal, 0 otherwise.
1.1054 + *
1.1055 + * Side effects:
1.1056 + * None.
1.1057 + *
1.1058 + *----------------------------------------------------------------------
1.1059 + */
1.1060 +
1.1061 +static int
1.1062 +Pstrequal (
1.1063 + ConstStr255Param stringA, /* Pascal string A */
1.1064 + ConstStr255Param stringB) /* Pascal string B */
1.1065 +{
1.1066 + int i, len;
1.1067 +
1.1068 + len = *stringA;
1.1069 + for (i = 0; i <= len; i++) {
1.1070 + if (*stringA++ != *stringB++) {
1.1071 + return 0;
1.1072 + }
1.1073 + }
1.1074 + return 1;
1.1075 +}
1.1076 +
1.1077 +/*
1.1078 + *----------------------------------------------------------------------
1.1079 + *
1.1080 + * GetFileFinderAttributes --
1.1081 + *
1.1082 + * Returns a Tcl_Obj containing the value of a file attribute
1.1083 + * which is part of the FInfo record. Which attribute is controlled
1.1084 + * by objIndex.
1.1085 + *
1.1086 + * Results:
1.1087 + * Returns a standard TCL error. If the return value is TCL_OK,
1.1088 + * the new creator or file type object is put into attributePtrPtr.
1.1089 + * The object will have ref count 0. If there is an error,
1.1090 + * attributePtrPtr is not touched.
1.1091 + *
1.1092 + * Side effects:
1.1093 + * A new object is allocated if the file is valid.
1.1094 + *
1.1095 + *----------------------------------------------------------------------
1.1096 + */
1.1097 +
1.1098 +static int
1.1099 +GetFileFinderAttributes(
1.1100 + Tcl_Interp *interp, /* The interp to report errors with. */
1.1101 + int objIndex, /* The index of the attribute option. */
1.1102 + Tcl_Obj *fileName, /* The name of the file (UTF-8). */
1.1103 + Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
1.1104 +{
1.1105 + OSErr err;
1.1106 + FSSpec fileSpec;
1.1107 + FInfo finfo;
1.1108 + CONST char *native;
1.1109 +
1.1110 + native=Tcl_FSGetNativePath(fileName);
1.1111 + err = FSpLLocationFromPath(strlen(native),
1.1112 + native, &fileSpec);
1.1113 +
1.1114 + if (err == noErr) {
1.1115 + err = FSpGetFInfo(&fileSpec, &finfo);
1.1116 + }
1.1117 +
1.1118 + if (err == noErr) {
1.1119 + switch (objIndex) {
1.1120 + case MAC_CREATOR_ATTRIBUTE:
1.1121 + *attributePtrPtr = Tcl_NewOSTypeObj(finfo.fdCreator);
1.1122 + break;
1.1123 + case MAC_HIDDEN_ATTRIBUTE:
1.1124 + *attributePtrPtr = Tcl_NewBooleanObj(finfo.fdFlags
1.1125 + & kIsInvisible);
1.1126 + break;
1.1127 + case MAC_TYPE_ATTRIBUTE:
1.1128 + *attributePtrPtr = Tcl_NewOSTypeObj(finfo.fdType);
1.1129 + break;
1.1130 + }
1.1131 + } else if (err == fnfErr) {
1.1132 + long dirID;
1.1133 + Boolean isDirectory = 0;
1.1134 +
1.1135 + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1.1136 + if ((err == noErr) && isDirectory) {
1.1137 + if (objIndex == MAC_HIDDEN_ATTRIBUTE) {
1.1138 + *attributePtrPtr = Tcl_NewBooleanObj(0);
1.1139 + } else {
1.1140 + *attributePtrPtr = Tcl_NewOSTypeObj('Fldr');
1.1141 + }
1.1142 + }
1.1143 + }
1.1144 +
1.1145 + if (err != noErr) {
1.1146 + errno = TclMacOSErrorToPosixError(err);
1.1147 + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1.1148 + "could not read \"", Tcl_GetString(fileName), "\": ",
1.1149 + Tcl_PosixError(interp), (char *) NULL);
1.1150 + return TCL_ERROR;
1.1151 + }
1.1152 + return TCL_OK;
1.1153 +}
1.1154 +
1.1155 +/*
1.1156 + *----------------------------------------------------------------------
1.1157 + *
1.1158 + * GetFileReadOnly --
1.1159 + *
1.1160 + * Returns a Tcl_Obj containing a Boolean value indicating whether
1.1161 + * or not the file is read-only. The object will have ref count 0.
1.1162 + * This procedure just checks the Finder attributes; it does not
1.1163 + * check AppleShare sharing attributes.
1.1164 + *
1.1165 + * Results:
1.1166 + * Returns a standard TCL error. If the return value is TCL_OK,
1.1167 + * the new creator type object is put into readOnlyPtrPtr.
1.1168 + * If there is an error, readOnlyPtrPtr is not touched.
1.1169 + *
1.1170 + * Side effects:
1.1171 + * A new object is allocated if the file is valid.
1.1172 + *
1.1173 + *----------------------------------------------------------------------
1.1174 + */
1.1175 +
1.1176 +static int
1.1177 +GetFileReadOnly(
1.1178 + Tcl_Interp *interp, /* The interp to report errors with. */
1.1179 + int objIndex, /* The index of the attribute. */
1.1180 + Tcl_Obj *fileName, /* The name of the file (UTF-8). */
1.1181 + Tcl_Obj **readOnlyPtrPtr) /* A pointer to return the object with. */
1.1182 +{
1.1183 + OSErr err;
1.1184 + FSSpec fileSpec;
1.1185 + CInfoPBRec paramBlock;
1.1186 + CONST char *native;
1.1187 +
1.1188 + native=Tcl_FSGetNativePath(fileName);
1.1189 + err = FSpLLocationFromPath(strlen(native),
1.1190 + native, &fileSpec);
1.1191 +
1.1192 + if (err == noErr) {
1.1193 + if (err == noErr) {
1.1194 + paramBlock.hFileInfo.ioCompletion = NULL;
1.1195 + paramBlock.hFileInfo.ioNamePtr = fileSpec.name;
1.1196 + paramBlock.hFileInfo.ioVRefNum = fileSpec.vRefNum;
1.1197 + paramBlock.hFileInfo.ioFDirIndex = 0;
1.1198 + paramBlock.hFileInfo.ioDirID = fileSpec.parID;
1.1199 + err = PBGetCatInfo(¶mBlock, 0);
1.1200 + if (err == noErr) {
1.1201 +
1.1202 + /*
1.1203 + * For some unknown reason, the Mac does not give
1.1204 + * symbols for the bits in the ioFlAttrib field.
1.1205 + * 1 -> locked.
1.1206 + */
1.1207 +
1.1208 + *readOnlyPtrPtr = Tcl_NewBooleanObj(
1.1209 + paramBlock.hFileInfo.ioFlAttrib & 1);
1.1210 + }
1.1211 + }
1.1212 + }
1.1213 + if (err != noErr) {
1.1214 + errno = TclMacOSErrorToPosixError(err);
1.1215 + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1.1216 + "could not read \"", Tcl_GetString(fileName), "\": ",
1.1217 + Tcl_PosixError(interp), (char *) NULL);
1.1218 + return TCL_ERROR;
1.1219 + }
1.1220 + return TCL_OK;
1.1221 +}
1.1222 +
1.1223 +/*
1.1224 + *----------------------------------------------------------------------
1.1225 + *
1.1226 + * SetFileFinderAttributes --
1.1227 + *
1.1228 + * Sets the file to the creator or file type given by attributePtr.
1.1229 + * objIndex determines whether the creator or file type is set.
1.1230 + *
1.1231 + * Results:
1.1232 + * Returns a standard TCL error.
1.1233 + *
1.1234 + * Side effects:
1.1235 + * The file's attribute is set.
1.1236 + *
1.1237 + *----------------------------------------------------------------------
1.1238 + */
1.1239 +
1.1240 +static int
1.1241 +SetFileFinderAttributes(
1.1242 + Tcl_Interp *interp, /* The interp to report errors with. */
1.1243 + int objIndex, /* The index of the attribute. */
1.1244 + Tcl_Obj *fileName, /* The name of the file (UTF-8). */
1.1245 + Tcl_Obj *attributePtr) /* The command line object. */
1.1246 +{
1.1247 + OSErr err;
1.1248 + FSSpec fileSpec;
1.1249 + FInfo finfo;
1.1250 + CONST char *native;
1.1251 +
1.1252 + native=Tcl_FSGetNativePath(fileName);
1.1253 + err = FSpLLocationFromPath(strlen(native),
1.1254 + native, &fileSpec);
1.1255 +
1.1256 + if (err == noErr) {
1.1257 + err = FSpGetFInfo(&fileSpec, &finfo);
1.1258 + }
1.1259 +
1.1260 + if (err == noErr) {
1.1261 + switch (objIndex) {
1.1262 + case MAC_CREATOR_ATTRIBUTE:
1.1263 + if (Tcl_GetOSTypeFromObj(interp, attributePtr,
1.1264 + &finfo.fdCreator) != TCL_OK) {
1.1265 + return TCL_ERROR;
1.1266 + }
1.1267 + break;
1.1268 + case MAC_HIDDEN_ATTRIBUTE: {
1.1269 + int hidden;
1.1270 +
1.1271 + if (Tcl_GetBooleanFromObj(interp, attributePtr, &hidden)
1.1272 + != TCL_OK) {
1.1273 + return TCL_ERROR;
1.1274 + }
1.1275 + if (hidden) {
1.1276 + finfo.fdFlags |= kIsInvisible;
1.1277 + } else {
1.1278 + finfo.fdFlags &= ~kIsInvisible;
1.1279 + }
1.1280 + break;
1.1281 + }
1.1282 + case MAC_TYPE_ATTRIBUTE:
1.1283 + if (Tcl_GetOSTypeFromObj(interp, attributePtr,
1.1284 + &finfo.fdType) != TCL_OK) {
1.1285 + return TCL_ERROR;
1.1286 + }
1.1287 + break;
1.1288 + }
1.1289 + err = FSpSetFInfo(&fileSpec, &finfo);
1.1290 + } else if (err == fnfErr) {
1.1291 + long dirID;
1.1292 + Boolean isDirectory = 0;
1.1293 +
1.1294 + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1.1295 + if ((err == noErr) && isDirectory) {
1.1296 + Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
1.1297 + Tcl_AppendStringsToObj(resultPtr, "cannot set ",
1.1298 + tclpFileAttrStrings[objIndex], ": \"",
1.1299 + Tcl_GetString(fileName), "\" is a directory", (char *) NULL);
1.1300 + return TCL_ERROR;
1.1301 + }
1.1302 + }
1.1303 +
1.1304 + if (err != noErr) {
1.1305 + errno = TclMacOSErrorToPosixError(err);
1.1306 + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1.1307 + "could not read \"", Tcl_GetString(fileName), "\": ",
1.1308 + Tcl_PosixError(interp), (char *) NULL);
1.1309 + return TCL_ERROR;
1.1310 + }
1.1311 + return TCL_OK;
1.1312 +}
1.1313 +
1.1314 +/*
1.1315 + *----------------------------------------------------------------------
1.1316 + *
1.1317 + * SetFileReadOnly --
1.1318 + *
1.1319 + * Sets the file to be read-only according to the Boolean value
1.1320 + * given by hiddenPtr.
1.1321 + *
1.1322 + * Results:
1.1323 + * Returns a standard TCL error.
1.1324 + *
1.1325 + * Side effects:
1.1326 + * The file's attribute is set.
1.1327 + *
1.1328 + *----------------------------------------------------------------------
1.1329 + */
1.1330 +
1.1331 +static int
1.1332 +SetFileReadOnly(
1.1333 + Tcl_Interp *interp, /* The interp to report errors with. */
1.1334 + int objIndex, /* The index of the attribute. */
1.1335 + Tcl_Obj *fileName, /* The name of the file (UTF-8). */
1.1336 + Tcl_Obj *readOnlyPtr) /* The command line object. */
1.1337 +{
1.1338 + OSErr err;
1.1339 + FSSpec fileSpec;
1.1340 + HParamBlockRec paramBlock;
1.1341 + int hidden;
1.1342 + CONST char *native;
1.1343 +
1.1344 + native=Tcl_FSGetNativePath(fileName);
1.1345 + err = FSpLLocationFromPath(strlen(native),
1.1346 + native, &fileSpec);
1.1347 +
1.1348 + if (err == noErr) {
1.1349 + if (Tcl_GetBooleanFromObj(interp, readOnlyPtr, &hidden) != TCL_OK) {
1.1350 + return TCL_ERROR;
1.1351 + }
1.1352 +
1.1353 + paramBlock.fileParam.ioCompletion = NULL;
1.1354 + paramBlock.fileParam.ioNamePtr = fileSpec.name;
1.1355 + paramBlock.fileParam.ioVRefNum = fileSpec.vRefNum;
1.1356 + paramBlock.fileParam.ioDirID = fileSpec.parID;
1.1357 + if (hidden) {
1.1358 + err = PBHSetFLock(¶mBlock, 0);
1.1359 + } else {
1.1360 + err = PBHRstFLock(¶mBlock, 0);
1.1361 + }
1.1362 + }
1.1363 +
1.1364 + if (err == fnfErr) {
1.1365 + long dirID;
1.1366 + Boolean isDirectory = 0;
1.1367 + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1.1368 + if ((err == noErr) && isDirectory) {
1.1369 + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1.1370 + "cannot set a directory to read-only when File Sharing is turned off",
1.1371 + (char *) NULL);
1.1372 + return TCL_ERROR;
1.1373 + } else {
1.1374 + err = fnfErr;
1.1375 + }
1.1376 + }
1.1377 +
1.1378 + if (err != noErr) {
1.1379 + errno = TclMacOSErrorToPosixError(err);
1.1380 + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1.1381 + "could not read \"", Tcl_GetString(fileName), "\": ",
1.1382 + Tcl_PosixError(interp), (char *) NULL);
1.1383 + return TCL_ERROR;
1.1384 + }
1.1385 + return TCL_OK;
1.1386 +}
1.1387 +
1.1388 +/*
1.1389 + *---------------------------------------------------------------------------
1.1390 + *
1.1391 + * TclpObjListVolumes --
1.1392 + *
1.1393 + * Lists the currently mounted volumes
1.1394 + *
1.1395 + * Results:
1.1396 + * The list of volumes.
1.1397 + *
1.1398 + * Side effects:
1.1399 + * None
1.1400 + *
1.1401 + *---------------------------------------------------------------------------
1.1402 + */
1.1403 +Tcl_Obj*
1.1404 +TclpObjListVolumes(void)
1.1405 +{
1.1406 + HParamBlockRec pb;
1.1407 + Str255 name;
1.1408 + OSErr theError = noErr;
1.1409 + Tcl_Obj *resultPtr, *elemPtr;
1.1410 + short volIndex = 1;
1.1411 + Tcl_DString dstr;
1.1412 +
1.1413 + resultPtr = Tcl_NewObj();
1.1414 +
1.1415 + /*
1.1416 + * We use two facts:
1.1417 + * 1) The Mac volumes are enumerated by the ioVolIndex parameter of
1.1418 + * the HParamBlockRec. They run through the integers contiguously,
1.1419 + * starting at 1.
1.1420 + * 2) PBHGetVInfoSync returns an error when you ask for a volume index
1.1421 + * that does not exist.
1.1422 + *
1.1423 + */
1.1424 +
1.1425 + while ( 1 ) {
1.1426 + pb.volumeParam.ioNamePtr = (StringPtr) &name;
1.1427 + pb.volumeParam.ioVolIndex = volIndex;
1.1428 +
1.1429 + theError = PBHGetVInfoSync(&pb);
1.1430 +
1.1431 + if ( theError != noErr ) {
1.1432 + break;
1.1433 + }
1.1434 +
1.1435 + Tcl_ExternalToUtfDString(NULL, (CONST char *)&name[1], name[0], &dstr);
1.1436 + elemPtr = Tcl_NewStringObj(Tcl_DStringValue(&dstr),
1.1437 + Tcl_DStringLength(&dstr));
1.1438 + Tcl_AppendToObj(elemPtr, ":", 1);
1.1439 + Tcl_ListObjAppendElement(NULL, resultPtr, elemPtr);
1.1440 +
1.1441 + Tcl_DStringFree(&dstr);
1.1442 +
1.1443 + volIndex++;
1.1444 + }
1.1445 +
1.1446 + Tcl_IncrRefCount(resultPtr);
1.1447 + return resultPtr;
1.1448 +}
1.1449 +
1.1450 +/*
1.1451 + *---------------------------------------------------------------------------
1.1452 + *
1.1453 + * TclpObjNormalizePath --
1.1454 + *
1.1455 + * This function scans through a path specification and replaces
1.1456 + * it, in place, with a normalized version. On MacOS, this means
1.1457 + * resolving all aliases present in the path and replacing the head of
1.1458 + * pathPtr with the absolute case-sensitive path to the last file or
1.1459 + * directory that could be validated in the path.
1.1460 + *
1.1461 + * Results:
1.1462 + * The new 'nextCheckpoint' value, giving as far as we could
1.1463 + * understand in the path.
1.1464 + *
1.1465 + * Side effects:
1.1466 + * The pathPtr string, which must contain a valid path, is
1.1467 + * possibly modified in place.
1.1468 + *
1.1469 + *---------------------------------------------------------------------------
1.1470 + */
1.1471 +
1.1472 +int
1.1473 +TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
1.1474 + Tcl_Interp *interp;
1.1475 + Tcl_Obj *pathPtr;
1.1476 + int nextCheckpoint;
1.1477 +{
1.1478 + #define MAXMACFILENAMELEN 31 /* assumed to be < sizeof(StrFileName) */
1.1479 +
1.1480 + StrFileName fileName;
1.1481 + StringPtr fileNamePtr;
1.1482 + int fileNameLen,newPathLen;
1.1483 + Handle newPathHandle;
1.1484 + OSErr err;
1.1485 + short vRefNum;
1.1486 + long dirID;
1.1487 + Boolean isDirectory;
1.1488 + Boolean wasAlias=FALSE;
1.1489 + FSSpec fileSpec, lastFileSpec;
1.1490 +
1.1491 + Tcl_DString nativeds;
1.1492 +
1.1493 + char cur;
1.1494 + int firstCheckpoint=nextCheckpoint, lastCheckpoint;
1.1495 + int origPathLen;
1.1496 + char *path = Tcl_GetStringFromObj(pathPtr,&origPathLen);
1.1497 +
1.1498 + {
1.1499 + int currDirValid=0;
1.1500 + /*
1.1501 + * check if substring to first ':' after initial
1.1502 + * nextCheckpoint is a valid relative or absolute
1.1503 + * path to a directory, if not we return without
1.1504 + * normalizing anything
1.1505 + */
1.1506 +
1.1507 + while (1) {
1.1508 + cur = path[nextCheckpoint];
1.1509 + if (cur == ':' || cur == 0) {
1.1510 + if (cur == ':') {
1.1511 + /* jump over separator */
1.1512 + nextCheckpoint++; cur = path[nextCheckpoint];
1.1513 + }
1.1514 + Tcl_UtfToExternalDString(NULL,path,nextCheckpoint,&nativeds);
1.1515 + err = FSpLLocationFromPath(Tcl_DStringLength(&nativeds),
1.1516 + Tcl_DStringValue(&nativeds),
1.1517 + &fileSpec);
1.1518 + Tcl_DStringFree(&nativeds);
1.1519 + if (err == noErr) {
1.1520 + lastFileSpec=fileSpec;
1.1521 + err = ResolveAliasFile(&fileSpec, true, &isDirectory,
1.1522 + &wasAlias);
1.1523 + if (err == noErr) {
1.1524 + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1.1525 + currDirValid = ((err == noErr) && isDirectory);
1.1526 + vRefNum = fileSpec.vRefNum;
1.1527 + }
1.1528 + }
1.1529 + break;
1.1530 + }
1.1531 + nextCheckpoint++;
1.1532 + }
1.1533 +
1.1534 + if(!currDirValid) {
1.1535 + /* can't determine root dir, bail out */
1.1536 + return firstCheckpoint;
1.1537 + }
1.1538 + }
1.1539 +
1.1540 + /*
1.1541 + * Now vRefNum and dirID point to a valid
1.1542 + * directory, so walk the rest of the path
1.1543 + * ( code adapted from FSpLocationFromPath() )
1.1544 + */
1.1545 +
1.1546 + lastCheckpoint=nextCheckpoint;
1.1547 + while (1) {
1.1548 + cur = path[nextCheckpoint];
1.1549 + if (cur == ':' || cur == 0) {
1.1550 + fileNameLen=nextCheckpoint-lastCheckpoint;
1.1551 + fileNamePtr=fileName;
1.1552 + if(fileNameLen==0) {
1.1553 + if (cur == ':') {
1.1554 + /*
1.1555 + * special case for empty dirname i.e. encountered
1.1556 + * a '::' path component: get parent dir of currDir
1.1557 + */
1.1558 + fileName[0]=2;
1.1559 + strcpy((char *) fileName + 1, "::");
1.1560 + lastCheckpoint--;
1.1561 + } else {
1.1562 + /*
1.1563 + * empty filename, i.e. want FSSpec for currDir
1.1564 + */
1.1565 + fileNamePtr=NULL;
1.1566 + }
1.1567 + } else {
1.1568 + Tcl_UtfToExternalDString(NULL,&path[lastCheckpoint],
1.1569 + fileNameLen,&nativeds);
1.1570 + fileNameLen=Tcl_DStringLength(&nativeds);
1.1571 + if(fileNameLen > MAXMACFILENAMELEN) {
1.1572 + err = bdNamErr;
1.1573 + } else {
1.1574 + fileName[0]=fileNameLen;
1.1575 + strncpy((char *) fileName + 1, Tcl_DStringValue(&nativeds),
1.1576 + fileNameLen);
1.1577 + }
1.1578 + Tcl_DStringFree(&nativeds);
1.1579 + }
1.1580 + if(err == noErr)
1.1581 + err=FSMakeFSSpecCompat(vRefNum, dirID, fileNamePtr, &fileSpec);
1.1582 + if(err != noErr) {
1.1583 + if(err != fnfErr) {
1.1584 + /*
1.1585 + * this can occur if trying to get parent of a root
1.1586 + * volume via '::' or when using an illegal
1.1587 + * filename; revert to last checkpoint and stop
1.1588 + * processing path further
1.1589 + */
1.1590 + err=FSMakeFSSpecCompat(vRefNum, dirID, NULL, &fileSpec);
1.1591 + if(err != noErr) {
1.1592 + /* should never happen, bail out */
1.1593 + return firstCheckpoint;
1.1594 + }
1.1595 + nextCheckpoint=lastCheckpoint;
1.1596 + cur = path[lastCheckpoint];
1.1597 + }
1.1598 + break; /* arrived at nonexistent file or dir */
1.1599 + } else {
1.1600 + /* fileSpec could point to an alias, resolve it */
1.1601 + lastFileSpec=fileSpec;
1.1602 + err = ResolveAliasFile(&fileSpec, true, &isDirectory,
1.1603 + &wasAlias);
1.1604 + if (err != noErr || !isDirectory) {
1.1605 + break; /* fileSpec doesn't point to a dir */
1.1606 + }
1.1607 + }
1.1608 + if (cur == 0) break; /* arrived at end of path */
1.1609 +
1.1610 + /* fileSpec points to possibly nonexisting subdirectory; validate */
1.1611 + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
1.1612 + if (err != noErr || !isDirectory) {
1.1613 + break; /* fileSpec doesn't point to existing dir */
1.1614 + }
1.1615 + vRefNum = fileSpec.vRefNum;
1.1616 +
1.1617 + /* found a new valid subdir in path, continue processing path */
1.1618 + lastCheckpoint=nextCheckpoint+1;
1.1619 + }
1.1620 + wasAlias=FALSE;
1.1621 + nextCheckpoint++;
1.1622 + }
1.1623 +
1.1624 + if (wasAlias)
1.1625 + fileSpec=lastFileSpec;
1.1626 +
1.1627 + /*
1.1628 + * fileSpec now points to a possibly nonexisting file or dir
1.1629 + * inside a valid dir; get full path name to it
1.1630 + */
1.1631 +
1.1632 + err=FSpPathFromLocation(&fileSpec, &newPathLen, &newPathHandle);
1.1633 + if(err != noErr) {
1.1634 + return firstCheckpoint; /* should not see any errors here, bail out */
1.1635 + }
1.1636 +
1.1637 + HLock(newPathHandle);
1.1638 + Tcl_ExternalToUtfDString(NULL,*newPathHandle,newPathLen,&nativeds);
1.1639 + if (cur != 0) {
1.1640 + /* not at end, append remaining path */
1.1641 + if ( newPathLen==0 || (*(*newPathHandle+(newPathLen-1))!=':' && path[nextCheckpoint] !=':')) {
1.1642 + Tcl_DStringAppend(&nativeds, ":" , 1);
1.1643 + }
1.1644 + Tcl_DStringAppend(&nativeds, &path[nextCheckpoint],
1.1645 + strlen(&path[nextCheckpoint]));
1.1646 + }
1.1647 + DisposeHandle(newPathHandle);
1.1648 +
1.1649 + fileNameLen=Tcl_DStringLength(&nativeds);
1.1650 + Tcl_SetStringObj(pathPtr,Tcl_DStringValue(&nativeds),fileNameLen);
1.1651 + Tcl_DStringFree(&nativeds);
1.1652 +
1.1653 + return nextCheckpoint+(fileNameLen-origPathLen);
1.1654 +}
1.1655 +