home *** CD-ROM | disk | FTP | other *** search
- /*
- * tclOS2FCmd.c
- *
- * This file implements the OS/2 specific portion of file manipulation
- * subcommands of the "file" command.
- *
- * Copyright (c) 1996-1997 Sun Microsystems, Inc.
- * Copyright (c) 1996-2001 Illya Vaes
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- */
-
- #include "tclOS2Int.h"
-
- /*
- * The following constants specify the type of callback when
- * TraverseOS2Tree() calls the traverseProc()
- */
-
- #define DOTREE_PRED 1 /* pre-order directory */
- #define DOTREE_POSTD 2 /* post-order directory */
- #define DOTREE_F 3 /* regular file */
-
- /*
- * Callbacks for file attributes code.
- */
-
- static int CopyFileAtts _ANSI_ARGS_((char *src, char *dst,
- FILESTATUS3 *fsSource));
- static int GetOS2FileAttributes _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, char *fileName,
- Tcl_Obj **attributePtrPtr));
- #ifdef 0
- static int GetOS2FileLongName _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, char *fileName,
- Tcl_Obj **attributePtrPtr));
- static int GetOS2FileShortName _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, char *fileName,
- Tcl_Obj **attributePtrPtr));
- #endif
- static int SetOS2FileAttributes _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, char *fileName,
- Tcl_Obj *attributePtr));
- #if 0
- static int CannotGetAttribute _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, char *fileName,
- Tcl_Obj **attributePtr));
- static int CannotSetAttribute _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, char *fileName,
- Tcl_Obj *attributePtr));
- #endif
-
- /*
- * Constants and variables necessary for file attributes subcommand.
- */
-
- /*
- enum {
- OS2_ARCHIVE_ATTRIBUTE,
- OS2_HIDDEN_ATTRIBUTE,
- OS2_LONGNAME_ATTRIBUTE,
- OS2_READONLY_ATTRIBUTE,
- OS2_SHORTNAME_ATTRIBUTE,
- OS2_SYSTEM_ATTRIBUTE
- };
-
- static int attributeArray[] = {FILE_ARCHIVED, FILE_HIDDEN, 0, FILE_READONLY, 0,
- FILE_SYSTEM};
-
- char *tclpFileAttrStrings[] = {"-archive", "-hidden", "-longname", "-readonly",
- "-shortname", "-system", (char *) NULL};
- CONST TclFileAttrProcs tclpFileAttrProcs[] = {
- {GetOS2FileAttributes, SetOS2FileAttributes},
- {GetOS2FileAttributes, SetOS2FileAttributes},
- {GetOS2FileLongName, CannotSetAttribute},
- {CannotGetAttribute, CannotSetAttribute},
- {GetOS2FileAttributes, SetOS2FileAttributes},
- {GetOS2FileShortName, CannotSetAttribute},
- {CannotGetAttribute, CannotSetAttribute},
- {GetOS2FileAttributes, SetOS2FileAttributes}
- };
- */
- enum {
- OS2_ARCHIVE_ATTRIBUTE,
- OS2_HIDDEN_ATTRIBUTE,
- OS2_READONLY_ATTRIBUTE,
- OS2_SYSTEM_ATTRIBUTE
- };
-
- static int attributeArray[] = {FILE_ARCHIVED, FILE_HIDDEN, FILE_READONLY,
- FILE_SYSTEM};
-
- char *tclpFileAttrStrings[] = {"-archive", "-hidden", "-readonly", "-system",
- (char *) NULL};
- CONST TclFileAttrProcs tclpFileAttrProcs[] = {
- {GetOS2FileAttributes, SetOS2FileAttributes},
- {GetOS2FileAttributes, SetOS2FileAttributes},
- {GetOS2FileAttributes, SetOS2FileAttributes},
- {GetOS2FileAttributes, SetOS2FileAttributes}
- };
-
- /*
- * Prototype for the TraverseOS2Tree callback function.
- */
-
- typedef int (TraversalProc)(char *src, char *dst, FILESTATUS3 *fsSource,
- int type, Tcl_DString *errorPtr);
-
- /*
- * Declarations for local procedures defined in this file:
- */
-
- static void AttributesPosixError _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, char *fileName, int getOrSet));
- #if 0
- static int ConvertFileNameFormat _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, char *fileName, int longShort,
- Tcl_Obj **attributePtrPtr));
- #endif
- static int TraversalCopy(char *src, char *dst, FILESTATUS3 *fsSrc,
- int type, Tcl_DString *errorPtr);
- static int TraversalDelete(char *src, char *dst,
- FILESTATUS3 *fsSrce, int type,
- Tcl_DString *errorPtr);
- static int TraverseOS2Tree(TraversalProc *traverseProc,
- Tcl_DString *sourcePtr, Tcl_DString *destPtr,
- Tcl_DString *errorPtr);
-
-
- /*
- *---------------------------------------------------------------------------
- *
- * TclpRenameFile --
- *
- * Changes the name of an existing file or directory, from src to dst.
- * If src and dst refer to the same file or directory, does nothing
- * and returns success. Otherwise if dst already exists, it will be
- * deleted and replaced by src subject to the following conditions:
- * If src is a directory, dst may be an empty directory.
- * If src is a file, dst may be a file.
- * In any other situation where dst already exists, the rename will
- * fail.
- *
- * Results:
- * If the directory was successfully created, returns TCL_OK.
- * Otherwise the return value is TCL_ERROR and errno is set to
- * indicate the error. Some possible values for errno are:
- *
- * EACCES: src or dst parent directory can't be read and/or written.
- * EEXIST: dst is a non-empty directory.
- * EINVAL: src is a root directory or dst is a subdirectory of src.
- * EISDIR: dst is a directory, but src is not.
- * ENOENT: src doesn't exist. src or dst is "".
- * ENOTDIR: src is a directory, but dst is not.
- * EXDEV: src and dst are on different filesystems.
- *
- * EACCES: exists an open file already referring to src or dst.
- * EACCES: src or dst specify the current working directory (NT).
- * EACCES: src specifies a char device (nul:, com1:, etc.)
- * EEXIST: dst specifies a char device (nul:, com1:, etc.) (NT)
- * EACCES: dst specifies a char device (nul:, com1:, etc.) (95)
- *
- * Side effects:
- * The implementation supports cross-filesystem renames of files,
- * but the caller should be prepared to emulate cross-filesystem
- * renames of directories if errno is EXDEV.
- *
- *---------------------------------------------------------------------------
- */
-
- int
- TclpRenameFile(
- char *src, /* Pathname of file or dir to be renamed. */
- char *dst) /* New pathname for file or directory. */
- {
- FILESTATUS3 filestatSrc, filestatDst;
- ULONG srcAttr = 0, dstAttr = 0;
- Tcl_PathType srcPathType, dstPathType;
-
- rc = DosMove(src, dst);
- if (rc == NO_ERROR) {
- return TCL_OK;
- #ifdef VERBOSE
- printf("TclpRenameFile DosMove [%s] -> [%s] OK", src, dst);
- fflush(stdout);
- #endif
- }
- #ifdef VERBOSE
- printf("TclpRenameFile DosMove [%s] -> [%s] ERROR %d\n", src, dst, rc);
- fflush(stdout);
- #endif
-
- TclOS2ConvertError(rc);
-
- rc = DosQueryPathInfo(src, FIL_STANDARD, &filestatSrc, sizeof(FILESTATUS3));
- if (rc == NO_ERROR) {
- srcAttr = filestatSrc.attrFile;
- }
- #ifdef VERBOSE
- else {
- printf("TclpRenameFile DosQueryPathInfo src %s ERROR %d\n", src, rc);
- fflush(stdout);
- }
- #endif
- srcPathType = Tcl_GetPathType(src);
- rc = DosQueryPathInfo(dst, FIL_STANDARD, &filestatDst, sizeof(FILESTATUS3));
- if (rc == NO_ERROR) {
- dstAttr = filestatDst.attrFile;
- }
- #ifdef VERBOSE
- else {
- printf("TclpRenameFile DosQueryPathInfo dst %s ERROR %d\n", dst, rc);
- fflush(stdout);
- }
- #endif
- dstPathType = Tcl_GetPathType(dst);
-
- #ifdef VERBOSE
- printf(" srcAttr %x, dstAttr %x, errno %s\n", srcAttr, dstAttr,
- errno == EBADF ? "EBADF" : (errno==EACCES ? "EACCES" : "?"));
- #endif
- if (errno == EBADF) {
- errno = EACCES;
- return TCL_ERROR;
- }
- if (errno == EACCES) {
- decode:
- if (srcAttr & FILE_DIRECTORY) {
- char srcPath[MAX_PATH], dstPath[MAX_PATH];
- int srcArgc, dstArgc, len;
- char **srcArgv, **dstArgv;
-
- /* Get full paths */
- if (srcPathType == TCL_PATH_ABSOLUTE) {
- strcpy(srcPath, src);
- } else {
- /* TCL_PATH_RELATIVE or TCL_PATH_VOLUME_RELATIVE */
- ULONG len = MAX_PATH - 3;
- ULONG diskNum = 3;
- if (srcPathType == TCL_PATH_VOLUME_RELATIVE) {
- srcPath[0] = src[0];
- srcPath[1] = src[1];
- srcPath[2] = '\\';
- diskNum = src[0] - 'A' + 1;
- if (src[0] >= 'a') {
- diskNum -= ('a' - 'A');
- }
- rc = DosQueryCurrentDir(diskNum, srcPath+3, &len);
- if (rc != NO_ERROR) {
- #ifdef VERBOSE
- printf(" DosQueryCurrentDir src ERROR %d\n", rc);
- fflush(stdout);
- #endif
- return TCL_ERROR;
- }
- #ifdef VERBOSE
- printf(" DosQueryCurrentDir src [%s] OK\n", srcPath);
- fflush(stdout);
- #endif
- strcat(srcPath, "\\");
- strcat(srcPath, src);
- } else {
- ULONG logical;
- rc = DosQueryCurrentDisk(&diskNum, &logical);
- if (rc != NO_ERROR) {
- #ifdef VERBOSE
- printf(" DosQueryCurrentDisk src ERROR %d\n", rc);
- fflush(stdout);
- #endif
- return TCL_ERROR;
- }
- #ifdef VERBOSE
- printf(" DosQueryCurrentDisk src OK %d\n", diskNum);
- fflush(stdout);
- #endif
- srcPath[0] = diskNum + 'A' - 1;
- srcPath[1] = ':';
- srcPath[2] = '\\';
- rc = DosQueryCurrentDir(0, srcPath+3, &len);
- if (rc != NO_ERROR) {
- #ifdef VERBOSE
- printf(" DosQueryCurrentDir src ERROR %d\n", rc);
- fflush(stdout);
- #endif
- return TCL_ERROR;
- }
- #ifdef VERBOSE
- printf(" DosQueryCurrentDir src [%s] OK\n", srcPath);
- fflush(stdout);
- #endif
- strcat(srcPath, "\\");
- strcat(srcPath, src);
- }
- }
- if (dstPathType == TCL_PATH_ABSOLUTE) {
- strcpy(dstPath, dst);
- } else {
- /* TCL_PATH_RELATIVE or TCL_PATH_VOLUME_RELATIVE */
- ULONG len = MAX_PATH - 3;
- ULONG diskNum = 3;
- if (dstPathType == TCL_PATH_VOLUME_RELATIVE) {
- dstPath[0] = dst[0];
- dstPath[1] = dst[1];
- dstPath[2] = '\\';
- diskNum = dst[0] - 'A' + 1;
- if (dst[0] >= 'a') {
- diskNum -= ('a' - 'A');
- }
- rc = DosQueryCurrentDir(diskNum, dstPath+3, &len);
- if (rc != NO_ERROR) {
- #ifdef VERBOSE
- printf(" DosQueryCurrentDir dst ERROR %d\n", rc);
- fflush(stdout);
- #endif
- return TCL_ERROR;
- }
- #ifdef VERBOSE
- printf(" DosQueryCurrentDir dst [%s] OK\n", dstPath);
- fflush(stdout);
- #endif
- strcat(dstPath, "\\");
- strcat(dstPath, dst);
- } else {
- ULONG logical;
- rc = DosQueryCurrentDisk(&diskNum, &logical);
- if (rc != NO_ERROR) {
- #ifdef VERBOSE
- printf(" DosQueryCurrentDisk dst ERROR %d\n", rc);
- fflush(stdout);
- #endif
- return TCL_ERROR;
- }
- #ifdef VERBOSE
- printf(" DosQueryCurrentDisk dst OK %d\n", diskNum);
- fflush(stdout);
- #endif
- dstPath[0] = diskNum + 'A' - 1;
- dstPath[1] = ':';
- dstPath[2] = '\\';
- rc = DosQueryCurrentDir(0, dstPath+3, &len);
- if (rc != NO_ERROR) {
- #ifdef VERBOSE
- printf(" DosQueryCurrentDir dst ERROR %d\n", rc);
- fflush(stdout);
- #endif
- return TCL_ERROR;
- }
- #ifdef VERBOSE
- printf(" DosQueryCurrentDir dst [%s] OK\n", dstPath);
- fflush(stdout);
- #endif
- strcat(dstPath, "\\");
- strcat(dstPath, dst);
- }
- }
- len = strlen(srcPath);
- if (strnicmp(srcPath, dstPath, len) == 0 &&
- (*(dstPath+len) == '\0' || *(dstPath+len) == '\\' )) {
- /*
- * Trying to move a directory into itself.
- */
-
- #ifdef VERBOSE
- printf(" strnicmp(%s,%s)==0 ==> EINVAL\n", srcPath, dstPath);
- fflush(stdout);
- #endif
- errno = EINVAL;
- return TCL_ERROR;
- }
- Tcl_SplitPath(srcPath, &srcArgc, &srcArgv);
- #ifdef VERBOSE
- printf(" strnicmp(%s,%s) != 0\n", srcPath, dstPath);
- fflush(stdout);
- #endif
- Tcl_SplitPath(dstPath, &dstArgc, &dstArgv);
- if (srcArgc == 1) {
- /*
- * They are trying to move a root directory. Whether
- * or not it is across filesystems, this cannot be
- * done.
- */
-
- #ifdef VERBOSE
- printf(" srcArgc == 1 ==> EINVAL\n");
- fflush(stdout);
- #endif
-
- errno = EINVAL;
- return TCL_ERROR;
- } else if ((srcArgc > 0) && (dstArgc > 0) &&
- (stricmp(srcArgv[0], dstArgv[0]) != 0)) {
- /*
- * If src is a directory and dst filesystem != src
- * filesystem, errno should be EXDEV. It is very
- * important to get this behavior, so that the caller
- * can respond to a cross filesystem rename by
- * simulating it with copy and delete. The DosMove
- * system call already returns EXDEV (ERROR_NOT_SAME_DEVICE)
- * when moving a file between filesystems.
- */
-
- #ifdef VERBOSE
- printf(" EXDEV\n");
- fflush(stdout);
- #endif
-
- errno = EXDEV;
- }
-
- ckfree((char *) srcArgv);
- ckfree((char *) dstArgv);
- }
- if (!(dstAttr & FILE_DIRECTORY)) {
- /* Destination exists and is not a directory */
- #ifdef VERBOSE
- printf(" dst exists, not a directory ==> EEXIST\n");
- fflush(stdout);
- #endif
- errno = EEXIST;
- }
- errno = EEXIST;
-
- /*
- * Other types of access failure is that dst is a read-only
- * filesystem, that an open file referred to src or dest, or that
- * src or dest specified the current working directory on the
- * current filesystem. EACCES is returned for those cases.
- */
-
- }
- if (errno == EEXIST) {
- /*
- * Reports EEXIST any time the target already exists. If it makes
- * sense, remove the old file and try renaming again.
- */
- #ifdef VERBOSE
- printf(" EEXIST\n");
- fflush(stdout);
- #endif
-
- if (srcAttr & FILE_DIRECTORY) {
- if (dstAttr & FILE_DIRECTORY) {
- /*
- * Overwrite empty dst directory with src directory. The
- * following call will remove an empty directory. If it
- * fails, it's because it wasn't empty.
- */
-
- if (TclpRemoveDirectory(dst, 0, NULL) == TCL_OK) {
- /*
- * Now that that empty directory is gone, we can try
- * renaming again. If that fails, we'll put this empty
- * directory back, for completeness.
- */
-
- rc = DosMove(src, dst);
- if (rc == NO_ERROR) {
- #ifdef VERBOSE
- printf(" retry DosMove [%s]->[%s] OK\n", src, dst);
- fflush(stdout);
- #endif
- return TCL_OK;
- }
- #ifdef VERBOSE
- printf(" retry DosMove [%s]->[%s] ERROR %d\n", src, dst,
- rc);
- fflush(stdout);
- #endif
-
- /*
- * Some new error has occurred. Don't know what it
- * could be, but report this one.
- */
-
- TclOS2ConvertError(rc);
- rc = DosCreateDir(dst, (PEAOP2)NULL);
- #ifdef VERBOSE
- printf(" DosCreateDir %s returns %d\n", dst, rc);
- fflush(stdout);
- #endif
- rc = DosSetPathInfo(dst, FIL_STANDARD, &filestatDst,
- sizeof(FILESTATUS3), (ULONG)0);
- #ifdef VERBOSE
- printf(" DosSetPathInfo %s returns %d\n", dst, rc);
- fflush(stdout);
- #endif
- if (errno == EACCES) {
- /*
- * Decode the EACCES to a more meaningful error.
- */
-
- goto decode;
- }
- }
- } else { /* (dstAttr & FILE_DIRECTORY) == 0 */
- errno = ENOTDIR;
- }
- } else { /* (srcAttr & FILE_DIRECTORY) == 0 */
- if (dstAttr & FILE_DIRECTORY) {
- errno = EISDIR;
- } else {
- /*
- * Overwrite existing file by:
- *
- * 1. Rename existing file to temp name.
- * 2. Rename old file to new name.
- * 3. If success, delete temp file. If failure,
- * put temp file back to old name.
- */
-
- char tempName[MAX_PATH];
- int result;
- ULONG timeVal[2];
-
- if (dstPathType == TCL_PATH_ABSOLUTE) {
- strcpy(tempName, dst);
- } else {
- /* TCL_PATH_RELATIVE or TCL_PATH_VOLUME_RELATIVE */
- ULONG len = MAX_PATH - 3;
- ULONG diskNum = 3;
- if (dstPathType == TCL_PATH_VOLUME_RELATIVE) {
- #ifdef VERBOSE
- printf(" dst TCL_PATH_VOLUME_RELATIVE\n");
- #endif
- tempName[0] = dst[0];
- tempName[1] = dst[1];
- tempName[2] = '\\';
- diskNum = dst[0] - 'A' + 1;
- if (dst[0] >= 'a') {
- diskNum -= ('a' - 'A');
- }
- rc = DosQueryCurrentDir(diskNum, tempName+3, &len);
- if (rc != NO_ERROR) {
- return TCL_ERROR;
- }
- } else {
- ULONG logical;
- #ifdef VERBOSE
- printf(" dst != TCL_PATH_VOLUME_RELATIVE\n");
- #endif
- rc = DosQueryCurrentDisk(&diskNum, &logical);
- if (rc != NO_ERROR) {
- return TCL_ERROR;
- }
- tempName[0] = diskNum + 'A' - 1;
- tempName[1] = ':';
- tempName[2] = '\\';
- rc = DosQueryCurrentDir(0, tempName+3, &len);
- if (rc != NO_ERROR) {
- return TCL_ERROR;
- }
- }
- }
- result = TCL_ERROR;
- /* Determine unique value from time */
- rc = DosQuerySysInfo(QSV_TIME_LOW - 1, QSV_TIME_HIGH - 1,
- (PVOID)timeVal, sizeof(timeVal));
- if (rc == NO_ERROR) {
- /* Add unique name to path */
- sprintf(tempName, "%s\\tclr%04hx.TMP", tempName,
- (SHORT)timeVal[0]);
- /*
- * Strictly speaking, need the following DosDelete and
- * DosMove to be joined as an atomic operation so no
- * other app comes along in the meantime and creates the
- * same temp file.
- */
-
- DosDelete(tempName);
- rc = DosMove(dst, tempName);
- #ifdef VERBOSE
- printf(" DosMove %s->%s returns %d\n", dst, tempName, rc);
- #endif
- if (rc == NO_ERROR) {
- rc = DosMove(src, dst);
- #ifdef VERBOSE
- printf(" DosMove %s->%s returns %d\n", src, dst, rc);
- #endif
- if (rc == NO_ERROR) {
- filestatDst.attrFile = FILE_NORMAL;
- rc = DosSetPathInfo(tempName, FIL_STANDARD,
- &filestatDst,
- sizeof(FILESTATUS3), (ULONG)0);
- DosDelete(tempName);
- return TCL_OK;
- } else {
- DosDelete(dst);
- rc = DosMove(tempName, dst);
- #ifdef VERBOSE
- printf(" DosMove %s->%s (restore) returns %d\n",
- tempName, dst, rc);
- #endif
- }
- }
-
- /*
- * Can't backup dst file or move src file. Return that
- * error. Could happen if an open file refers to dst.
- */
-
- TclOS2ConvertError(rc);
- if (errno == EACCES) {
- /*
- * Decode the EACCES to a more meaningful error.
- */
-
- goto decode;
- }
- }
- return result;
- }
- }
- }
- return TCL_ERROR;
- }
-
- /*
- *---------------------------------------------------------------------------
- *
- * TclpCopyFile --
- *
- * Copy a single file (not a directory). If dst already exists and
- * is not a directory, it is removed.
- *
- * Results:
- * If the file was successfully copied, returns TCL_OK. Otherwise
- * the return value is TCL_ERROR and errno is set to indicate the
- * error. Some possible values for errno are:
- *
- * EACCES: src or dst parent directory can't be read and/or written.
- * EISDIR: src or dst is a directory.
- * ENOENT: src doesn't exist. src or dst is "".
- *
- * EACCES: exists an open file already referring to dst (95).
- * EACCES: src specifies a char device (nul:, com1:, etc.) (NT)
- * ENOENT: src specifies a char device (nul:, com1:, etc.) (95)
- *
- * Side effects:
- * It is not an error to copy to a char device.
- *
- *---------------------------------------------------------------------------
- */
-
- int
- TclpCopyFile(
- char *src, /* Pathname of file to be copied. */
- char *dst) /* Pathname of file to copy to. */
- {
- FILESTATUS3 filestatSrc, filestatDst;
- #ifdef VERBOSE
- printf("TclpCopyFile [%s] -> [%s]\n", src, dst);
- fflush(stdout);
- #endif
-
- rc = DosCopy(src, dst, DCPY_EXISTING);
- if (rc == NO_ERROR) {
- return TCL_OK;
- }
- #ifdef VERBOSE
- printf("TclpCopyFile DosCopy %s->%s ERROR %d\n", src, dst, rc);
- fflush(stdout);
- #endif
-
- TclOS2ConvertError(rc);
- #ifdef VERBOSE
- printf(" errno %s\n",
- errno == EBADF ? "EBADF" : (errno==EACCES ? "EACCES" : "?"));
- #endif
- if (errno == EBADF) {
- errno = EACCES;
- return TCL_ERROR;
- }
- if (errno == EACCES) {
- ULONG srcAttr = 0, dstAttr = 0;
-
- rc = DosQueryPathInfo(src, FIL_STANDARD, &filestatSrc,
- sizeof(FILESTATUS3));
- #ifdef VERBOSE
- printf(" DosQueryPathInfo src [%s] returns %d\n", src, rc);
- fflush(stdout);
- #endif
- if (rc == NO_ERROR) {
- srcAttr = filestatSrc.attrFile;
- rc = DosQueryPathInfo(dst, FIL_STANDARD, &filestatDst,
- sizeof(FILESTATUS3));
- #ifdef VERBOSE
- printf(" DosQueryPathInfo dst [%s] returns %d\n", dst, rc);
- fflush(stdout);
- #endif
- if (rc == NO_ERROR) {
- dstAttr = filestatDst.attrFile;
- }
- if ((srcAttr & FILE_DIRECTORY) ||
- (dstAttr & FILE_DIRECTORY)) {
- #ifdef VERBOSE
- printf(" errno => EISDIR\n");
- fflush(stdout);
- #endif
- errno = EISDIR;
- }
- if (dstAttr & FILE_READONLY) {
- filestatDst.attrFile = dstAttr & ~FILE_READONLY;
- rc = DosSetPathInfo(dst, FIL_STANDARD, &filestatDst,
- sizeof(FILESTATUS3), (ULONG)0);
- #ifdef VERBOSE
- printf(" DosSetPathInfo dst [%s] returns %d\n", dst, rc);
- fflush(stdout);
- #endif
- rc = DosCopy(src, dst, DCPY_EXISTING);
- #ifdef VERBOSE
- printf(" DosCopy [%s]->[%s] returns %d\n", src, dst, rc);
- fflush(stdout);
- #endif
- if (rc == NO_ERROR) {
- return TCL_OK;
- }
- /*
- * Still can't copy onto dst. Return that error, and
- * restore attributes of dst.
- */
-
- TclOS2ConvertError(rc);
- filestatDst.attrFile = dstAttr;
- rc = DosSetPathInfo(dst, FIL_STANDARD, &filestatDst,
- sizeof(FILESTATUS3), (ULONG)0);
- }
- }
- }
- return TCL_ERROR;
- }
-
- /*
- *---------------------------------------------------------------------------
- *
- * TclpDeleteFile --
- *
- * Removes a single file (not a directory).
- *
- * Results:
- * If the file was successfully deleted, returns TCL_OK. Otherwise
- * the return value is TCL_ERROR and errno is set to indicate the
- * error. Some possible values for errno are:
- *
- * EACCES: a parent directory can't be read and/or written.
- * EISDIR: path is a directory.
- * ENOENT: path doesn't exist or is "".
- *
- * EACCES: exists an open file already referring to path.
- * EACCES: path is a char device (nul:, com1:, etc.)
- *
- * Side effects:
- * The file is deleted, even if it is read-only.
- *
- *---------------------------------------------------------------------------
- */
-
- int
- TclpDeleteFile(
- char *path) /* Pathname of file to be removed. */
- {
- FILESTATUS3 filestat;
- ULONG attr = 0;
- #ifdef VERBOSE
- printf("TclpDeleteFile [%s]\n", path);
- fflush(stdout);
- #endif
-
- rc = DosDelete(path);
- if (rc == NO_ERROR) {
- return TCL_OK;
- }
- #ifdef VERBOSE
- printf("TclpDeleteFile DosDelete %s ERROR %d\n", path, rc);
- fflush(stdout);
- #endif
- TclOS2ConvertError(rc);
- if (errno == EACCES) {
- rc = DosQueryPathInfo(path, FIL_STANDARD, &filestat,
- sizeof(FILESTATUS3));
- #ifdef VERBOSE
- printf(" DosQueryPathInfo [%s] returns %d\n", path, rc);
- fflush(stdout);
- #endif
- if (rc == NO_ERROR) {
- attr = filestat.attrFile;
- if (attr & FILE_DIRECTORY) {
- /*
- * OS/2 reports removing a directory (with DosDelete) as
- * EACCES instead of EISDIR.
- */
-
- errno = EISDIR;
- } else if (attr & FILE_READONLY) {
- filestat.attrFile = attr & ~FILE_READONLY;
- rc = DosSetPathInfo(path, FIL_STANDARD, &filestat,
- sizeof(FILESTATUS3), (ULONG)0);
- #ifdef VERBOSE
- printf(" DosSetPathInfo [%s] returns %d\n", path, rc);
- fflush(stdout);
- #endif
- rc = DosDelete(path);
- #ifdef VERBOSE
- printf(" DosDelete [%s] returns %d\n", path, rc);
- fflush(stdout);
- #endif
- if (rc == NO_ERROR) {
- return TCL_OK;
- }
- TclOS2ConvertError(rc);
- filestat.attrFile = attr;
- rc = DosSetPathInfo(path, FIL_STANDARD, &filestat,
- sizeof(FILESTATUS3), (ULONG)0);
- #ifdef VERBOSE
- printf(" DosSetPathInfo [%s] returns %d\n", path, rc);
- fflush(stdout);
- #endif
- }
- }
- }
-
- return TCL_ERROR;
- }
-
- /*
- *---------------------------------------------------------------------------
- *
- * TclpCreateDirectory --
- *
- * Creates the specified directory. All parent directories of the
- * specified directory must already exist. The directory is
- * automatically created with permissions so that user can access
- * the new directory and create new files or subdirectories in it.
- *
- * Results:
- * If the directory was successfully created, returns TCL_OK.
- * Otherwise the return value is TCL_ERROR and errno is set to
- * indicate the error. Some possible values for errno are:
- *
- * EACCES: a parent directory can't be read and/or written.
- * EEXIST: path already exists.
- * ENOENT: a parent directory doesn't exist.
- *
- * Side effects:
- * A directory is created.
- *
- *---------------------------------------------------------------------------
- */
-
- int
- TclpCreateDirectory(
- char *path) /* Pathname of directory to create */
- {
- FILESTATUS3 filestat;
- #ifdef VERBOSE
- printf("TclpCreateDirectory [%s]\n", path);
- fflush(stdout);
- #endif
- rc = DosCreateDir(path, (PEAOP2)NULL);
- if (rc != NO_ERROR) {
- #ifdef VERBOSE
- printf("TclpCreateDirectory DosCreateDir %s ERROR %d\n", path, rc);
- fflush(stdout);
- #endif
- TclOS2ConvertError(rc);
- /*
- * fCmd.test 10.5 shows we have to generate an EEXIST in case this
- * directory already exists (OS/2 generates EACCES).
- */
- if (errno == EACCES) {
- rc = DosQueryPathInfo(path, FIL_STANDARD, &filestat,
- sizeof(FILESTATUS3));
- #ifdef VERBOSE
- printf(" DosQueryPathInfo [%s] returns %d\n", path, rc);
- fflush(stdout);
- #endif
- if (rc == NO_ERROR) {
- errno = EEXIST;
- #ifdef VERBOSE
- printf(" errno => EEXIST\n");
- fflush(stdout);
- #endif
- }
- }
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-
- /*
- *---------------------------------------------------------------------------
- *
- * TclpCopyDirectory --
- *
- * Recursively copies a directory. The target directory dst must
- * not already exist. Note that this function does not merge two
- * directory hierarchies, even if the target directory is an an
- * empty directory.
- *
- * Results:
- * If the directory was successfully copied, returns TCL_OK.
- * Otherwise the return value is TCL_ERROR, errno is set to indicate
- * the error, and the pathname of the file that caused the error
- * is stored in errorPtr. See TclpCreateDirectory and TclpCopyFile
- * for a description of possible values for errno.
- *
- * Side effects:
- * An exact copy of the directory hierarchy src will be created
- * with the name dst. If an error occurs, the error will
- * be returned immediately, and remaining files will not be
- * processed.
- *
- *---------------------------------------------------------------------------
- */
-
- int
- TclpCopyDirectory(
- char *src, /* Pathname of directory to be copied. */
- char *dst, /* Pathname of target directory. */
- Tcl_DString *errorPtr) /* If non-NULL, initialized DString for
- * error reporting. */
- {
- int result;
- Tcl_DString srcBuffer;
- Tcl_DString dstBuffer;
- #ifdef VERBOSE
- printf("TclpCopyDirectory [%s] -> [%s]\n", src, dst);
- fflush(stdout);
- #endif
-
- Tcl_DStringInit(&srcBuffer);
- Tcl_DStringInit(&dstBuffer);
- Tcl_DStringAppend(&srcBuffer, src, -1);
- Tcl_DStringAppend(&dstBuffer, dst, -1);
- result = TraverseOS2Tree(TraversalCopy, &srcBuffer, &dstBuffer,
- errorPtr);
- Tcl_DStringFree(&srcBuffer);
- Tcl_DStringFree(&dstBuffer);
- return result;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TclpRemoveDirectory --
- *
- * Removes directory (and its contents, if the recursive flag is set).
- *
- * Results:
- * If the directory was successfully removed, returns TCL_OK.
- * Otherwise the return value is TCL_ERROR, errno is set to indicate
- * the error, and the pathname of the file that caused the error
- * is stored in errorPtr. Some possible values for errno are:
- *
- * EACCES: path directory can't be read and/or written.
- * EEXIST: path is a non-empty directory.
- * EINVAL: path is root directory or current directory.
- * ENOENT: path doesn't exist or is "".
- * ENOTDIR: path is not a directory.
- *
- * EACCES: path is a char device (nul:, com1:, etc.) (95)
- * EINVAL: path is a char device (nul:, com1:, etc.) (NT)
- *
- * Side effects:
- * Directory removed. If an error occurs, the error will be returned
- * immediately, and remaining files will not be deleted.
- *
- *----------------------------------------------------------------------
- */
-
- int
- TclpRemoveDirectory(
- char *path, /* Pathname of directory to be removed. */
- int recursive, /* If non-zero, removes directories that
- * are nonempty. Otherwise, will only remove
- * empty directories. */
- Tcl_DString *errorPtr) /* If non-NULL, initialized DString for
- * error reporting. */
- {
- int result;
- Tcl_DString buffer;
- FILESTATUS3 filestat;
- ULONG attr = 0;
- #ifdef VERBOSE
- printf("TclpRemoveDirectory [%s] (recursive %d)\n", path, recursive);
- fflush(stdout);
- #endif
-
- rc = DosDeleteDir(path);
- if (rc == NO_ERROR) {
- return TCL_OK;
- }
- #ifdef VERBOSE
- printf("TclpRemoveDirectory DosDeleteDir %s ERROR %d\n", path, rc);
- fflush(stdout);
- #endif
- TclOS2ConvertError(rc);
- if (errno == EACCES) {
- rc = DosQueryPathInfo(path, FIL_STANDARD, &filestat,
- sizeof(FILESTATUS3));
- #ifdef VERBOSE
- printf(" DosQueryPathInfo [%s] returns %d\n", path, rc);
- fflush(stdout);
- #endif
- if (rc == NO_ERROR) {
- Tcl_DString buffer;
- char *find;
- int len;
- HDIR handle;
- FILEFINDBUF3 data;
- ULONG filesAtATime = 1;
-
- attr = filestat.attrFile;
- if ((attr & FILE_DIRECTORY) == 0) {
- /*
- * OS/2 reports calling DosDeleteDir on a file as an
- * EACCES, not an ENOTDIR.
- */
-
- errno = ENOTDIR;
- goto end;
- }
-
- if (attr & FILE_READONLY) {
- filestat.attrFile = attr & ~FILE_READONLY;
- rc = DosSetPathInfo(path, FIL_STANDARD, &filestat,
- sizeof(FILESTATUS3), (ULONG)0);
- rc = DosDeleteDir(path);
- if (rc == NO_ERROR) {
- return TCL_OK;
- }
- TclOS2ConvertError(rc);
- filestat.attrFile = attr;
- rc = DosSetPathInfo(path, FIL_STANDARD, &filestat,
- sizeof(FILESTATUS3), (ULONG)0);
- }
-
- /*
- * OS/2 reports removing a non-empty directory as
- * an EACCES, not an EEXIST. If the directory is not empty,
- * change errno so caller knows what's going on.
- */
-
- Tcl_DStringInit(&buffer);
- find = Tcl_DStringAppend(&buffer, path, -1);
- len = Tcl_DStringLength(&buffer);
- if ((len > 0) && (find[len - 1] != '\\')) {
- Tcl_DStringAppend(&buffer, "\\", 1);
- }
- find = Tcl_DStringAppend(&buffer, "*.*", 3);
- /* Use a new handle since we don't know if another find is active */
- handle = HDIR_CREATE;
- rc = DosFindFirst(find, &handle, FILE_NORMAL | FILE_DIRECTORY,
- &data, sizeof(data), &filesAtATime, FIL_STANDARD);
- #ifdef VERBOSE
- printf(" DosFindFirst %s returns %x (%s) (%d)\n", find, rc,
- data.achName, filesAtATime);
- #endif
- if (rc == NO_ERROR) {
- while (1) {
- if ((strcmp(data.achName, ".") != 0)
- && (strcmp(data.achName, "..") != 0)) {
- /*
- * Found something in this directory.
- */
-
- errno = EEXIST;
- break;
- }
- rc = DosFindNext(handle, &data, sizeof(data),
- &filesAtATime);
- #ifdef VERBOSE
- printf(" DosFindNext returns %x (%s) (%d)\n", rc,
- data.achName, filesAtATime);
- #endif
- if (rc != NO_ERROR) {
- break;
- }
- }
- DosFindClose(handle);
- }
- Tcl_DStringFree(&buffer);
- }
- }
- if (errno == ENOTEMPTY) {
- /*
- * The caller depends on EEXIST to signify that the directory is
- * not empty, not ENOTEMPTY.
- */
-
- errno = EEXIST;
- }
- if ((recursive != 0) && (errno == EEXIST)) {
- /*
- * The directory is nonempty, but the recursive flag has been
- * specified, so we recursively remove all the files in the directory.
- */
-
- Tcl_DStringInit(&buffer);
- Tcl_DStringAppend(&buffer, path, -1);
- result = TraverseOS2Tree(TraversalDelete, &buffer, NULL, errorPtr);
- Tcl_DStringFree(&buffer);
- return result;
- }
-
- end:
- if (errorPtr != NULL) {
- Tcl_DStringAppend(errorPtr, path, -1);
- }
- return TCL_ERROR;
- }
-
- /*
- *---------------------------------------------------------------------------
- *
- * TraverseOS2Tree --
- *
- * Traverse directory tree specified by sourcePtr, calling the function
- * traverseProc for each file and directory encountered. If destPtr
- * is non-null, each of name in the sourcePtr directory is appended to
- * the directory specified by destPtr and passed as the second argument
- * to traverseProc() .
- *
- * Results:
- * Standard Tcl result.
- *
- * Side effects:
- * None caused by TraverseOS2Tree, however the user specified
- * traverseProc() may change state. If an error occurs, the error will
- * be returned immediately, and remaining files will not be processed.
- *
- *---------------------------------------------------------------------------
- */
-
- static int
- TraverseOS2Tree(
- TraversalProc *traverseProc,/* Function to call for every file and
- * directory in source hierarchy. */
- Tcl_DString *sourcePtr, /* Pathname of source directory to be
- * traversed. */
- Tcl_DString *targetPtr, /* Pathname of directory to traverse in
- * parallel with source directory. */
- Tcl_DString *errorPtr) /* If non-NULL, an initialized DString for
- * error reporting. */
- {
- FILESTATUS3 filestatSrc;
- ULONG sourceAttr = 0;
- char *source, *target, *errfile;
- int result, sourceLen, targetLen = 0, sourceLenOriginal, targetLenOriginal;
- HDIR handle;
- FILEFINDBUF3 data;
- ULONG filesAtATime = 1;
- #ifdef VERBOSE
- printf("TraverseOS2Tree [%s] -> [%s]\n", Tcl_DStringValue(sourcePtr),
- targetPtr ? Tcl_DStringValue(targetPtr) : "NULL");
- fflush(stdout);
- #endif
-
- result = TCL_OK;
- source = Tcl_DStringValue(sourcePtr);
- sourceLenOriginal = Tcl_DStringLength(sourcePtr);
- if (targetPtr != NULL) {
- target = Tcl_DStringValue(targetPtr);
- targetLenOriginal = Tcl_DStringLength(targetPtr);
- } else {
- target = NULL;
- targetLenOriginal = 0;
- }
-
- errfile = NULL;
-
- rc = DosQueryPathInfo(source, FIL_STANDARD, &filestatSrc,
- sizeof(FILESTATUS3));
- #ifdef VERBOSE
- printf(" DosQueryPathInfo source [%s] returns %d\n", source, rc);
- fflush(stdout);
- #endif
- if (rc == NO_ERROR) {
- sourceAttr = filestatSrc.attrFile;
- } else {
- errfile = source;
- goto end;
- }
- if ((sourceAttr & FILE_DIRECTORY) == 0) {
- /*
- * Process the regular file
- */
-
- return (*traverseProc)(source, target, &filestatSrc, DOTREE_F,
- errorPtr);
- }
-
- /*
- * When given the pathname of the form "c:\" (one that already ends
- * with a backslash), must make sure not to add another "\" to the end
- * otherwise it will try to access a network drive.
- */
-
- sourceLen = sourceLenOriginal;
- if ((sourceLen > 0) && (source[sourceLen - 1] != '\\')) {
- Tcl_DStringAppend(sourcePtr, "\\", 1);
- sourceLen++;
- }
- source = Tcl_DStringAppend(sourcePtr, "*.*", 3);
- /* Use a new handle since we can be doing this recursively */
- handle = HDIR_CREATE;
- rc = DosFindFirst(source, &handle, FILE_NORMAL | FILE_DIRECTORY,
- &data, sizeof(data), &filesAtATime, FIL_STANDARD);
- #ifdef VERBOSE
- printf(" DosFindFirst %s returns %x (%s) (%d)\n", source, rc,
- data.achName, filesAtATime);
- #endif
- Tcl_DStringSetLength(sourcePtr, sourceLen);
- if (rc != NO_ERROR) {
- /*
- * Can't read directory
- */
-
- TclOS2ConvertError(rc);
- errfile = source;
- goto end;
- }
-
- result = (*traverseProc)(source, target, &filestatSrc, DOTREE_PRED,
- errorPtr);
- if (result != TCL_OK) {
- DosFindClose(handle);
- return result;
- }
-
- if (targetPtr != NULL) {
- targetLen = targetLenOriginal;
- if ((targetLen > 0) && (target[targetLen - 1] != '\\')) {
- target = Tcl_DStringAppend(targetPtr, "\\", 1);
- targetLen++;
- }
- }
-
- while (1) {
- if ((strcmp(data.achName, ".") != 0)
- && (strcmp(data.achName, "..") != 0)) {
- /*
- * Append name after slash, and recurse on the file.
- */
-
- Tcl_DStringAppend(sourcePtr, data.achName, -1);
- if (targetPtr != NULL) {
- Tcl_DStringAppend(targetPtr, data.achName, -1);
- }
- result = TraverseOS2Tree(traverseProc, sourcePtr, targetPtr,
- errorPtr);
- if (result != TCL_OK) {
- break;
- }
-
- /*
- * Remove name after slash.
- */
-
- Tcl_DStringSetLength(sourcePtr, sourceLen);
- if (targetPtr != NULL) {
- Tcl_DStringSetLength(targetPtr, targetLen);
- }
- }
- rc = DosFindNext(handle, &data, sizeof(data), &filesAtATime);
- #ifdef VERBOSE
- printf(" DosFindNext returns %x (%s) (%d)\n", rc, data.achName,
- filesAtATime);
- #endif
- if (rc != NO_ERROR) {
- break;
- }
- }
- DosFindClose(handle);
-
- /*
- * Strip off the trailing slash we added
- */
-
- Tcl_DStringSetLength(sourcePtr, sourceLenOriginal);
- source = Tcl_DStringValue(sourcePtr);
- if (targetPtr != NULL) {
- Tcl_DStringSetLength(targetPtr, targetLenOriginal);
- target = Tcl_DStringValue(targetPtr);
- }
-
- if (result == TCL_OK) {
- /*
- * Call traverseProc() on a directory after visiting all the
- * files in that directory.
- */
-
- result = (*traverseProc)(source, target, &filestatSrc, DOTREE_POSTD,
- errorPtr);
- }
- end:
- if (errfile != NULL) {
- TclOS2ConvertError(rc);
- if (errorPtr != NULL) {
- Tcl_DStringAppend(errorPtr, errfile, -1);
- }
- result = TCL_ERROR;
- }
-
- return result;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TraversalCopy
- *
- * Called from TraverseOS2Tree in order to execute a recursive
- * copy of a directory.
- *
- * Results:
- * Standard Tcl result.
- *
- * Side effects:
- * Depending on the value of type, src may be copied to dst.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- TraversalCopy(
- char *src, /* Source pathname to copy. */
- char *dst, /* Destination pathname of copy. */
- FILESTATUS3 *fsSource, /* File status for src. */
- int type, /* Reason for call - see TraverseOS2Tree() */
- Tcl_DString *errorPtr) /* If non-NULL, initialized DString for
- * error return. */
- {
- #ifdef VERBOSE
- printf("TraversalCopy [%s] -> [%s] (type %s)\n", src, dst,
- type == DOTREE_PRED ? "DOTREE_PRED"
- : (type == DOTREE_POSTD ? "DOTREE_POSTD"
- : (type == DOTREE_F ? "DOTREE_F"
- : "???")));
- fflush(stdout);
- #endif
- switch (type) {
- case DOTREE_F:
- if (TclpCopyFile(src, dst) == TCL_OK) {
- return TCL_OK;
- }
- break;
-
- case DOTREE_PRED:
- if (TclpCreateDirectory(dst) == TCL_OK) {
- return TCL_OK;
- }
- break;
-
- case DOTREE_POSTD:
- if (CopyFileAtts(src, dst, fsSource) == TCL_OK) {
- return TCL_OK;
- }
- break;
-
- }
-
- /*
- * There shouldn't be a problem with src, because we already
- * checked it to get here.
- */
-
- if (errorPtr != NULL) {
- Tcl_DStringAppend(errorPtr, dst, -1);
- }
- return TCL_ERROR;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TraversalDelete --
- *
- * Called by procedure TraverseOS2Tree for every file and
- * directory that it encounters in a directory hierarchy. This
- * procedure unlinks files, and removes directories after all the
- * containing files have been processed.
- *
- * Results:
- * Standard Tcl result.
- *
- * Side effects:
- * Files or directory specified by src will be deleted. If an
- * error occurs, the windows error is converted to a Posix error
- * and errno is set accordingly.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- TraversalDelete(
- char *src, /* Source pathname. */
- char *ignore, /* Destination pathname (not used). */
- FILESTATUS3 *fsSource, /* File status for src (not used). */
- int type, /* Reason for call - see TraverseOS2Tree(). */
- Tcl_DString *errorPtr) /* If non-NULL, initialized DString for
- * error return. */
- {
- #ifdef VERBOSE
- printf("TraversalDelete [%s] -> [%s] (type %s)\n", src, ignore,
- type == DOTREE_PRED ? "DOTREE_PRED"
- : (type == DOTREE_POSTD ? "DOTREE_POSTD"
- : (type == DOTREE_F ? "DOTREE_F"
- : "???")));
- fflush(stdout);
- #endif
- switch (type) {
- case DOTREE_F:
- if (TclpDeleteFile(src) == TCL_OK) {
- return TCL_OK;
- }
- break;
-
- case DOTREE_PRED:
- return TCL_OK;
-
- case DOTREE_POSTD:
- if (TclpRemoveDirectory(src, 0, NULL) == TCL_OK) {
- return TCL_OK;
- }
- break;
-
- }
-
- if (errorPtr != NULL) {
- Tcl_DStringAppend(errorPtr, src, -1);
- }
- return TCL_ERROR;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * AttributesPosixError --
- *
- * Sets the object result with the appropriate error.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The interp's object result is set with an error message
- * based on the objIndex, fileName and errno.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- AttributesPosixError(
- Tcl_Interp *interp, /* The interp that has the error */
- int objIndex, /* The attribute which caused the problem. */
- char *fileName, /* The name of the file which caused the
- * error. */
- int getOrSet) /* 0 for get; 1 for set */
- {
- TclOS2ConvertError(rc);
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "cannot ", getOrSet ? "set" : "get", " attribute \"",
- tclpFileAttrStrings[objIndex], "\" for file \"", fileName,
- "\": ", Tcl_PosixError(interp), (char *) NULL);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetOS2FileAttributes --
- *
- * Returns a Tcl_Obj containing the value of a file attribute.
- * This routine gets the -hidden, -readonly or -system attribute.
- *
- * Results:
- * Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
- * will have ref count 0. If the return value is not TCL_OK,
- * attributePtrPtr is not touched.
- *
- * Side effects:
- * A new object is allocated if the file is valid.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- GetOS2FileAttributes(
- Tcl_Interp *interp, /* The interp we are using for errors. */
- int objIndex, /* The index of the attribute. */
- char *fileName, /* The name of the file. */
- Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
- {
- FILESTATUS3 fileStatus;
-
- rc = DosQueryPathInfo(fileName, FIL_STANDARD, &fileStatus,
- sizeof(FILESTATUS3));
- #ifdef VERBOSE
- printf("GetOS2FileAttributes [%s] returns %d\n", fileName, rc);
- #endif
- if (rc != NO_ERROR) {
- AttributesPosixError(interp, objIndex, fileName, 0);
- return TCL_ERROR;
- }
-
- *attributePtrPtr = Tcl_NewBooleanObj(fileStatus.attrFile
- & attributeArray[objIndex]);
- return TCL_OK;
- }
-
- #if 0
- /*
- *----------------------------------------------------------------------
- *
- * ConvertFileNameFormat --
- *
- * Returns a Tcl_Obj containing either the long or short version of the
- * file name.
- *
- * Results:
- * Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
- * will have ref count 0. If the return value is not TCL_OK,
- * attributePtrPtr is not touched.
- *
- * Side effects:
- * A new object is allocated if the file is valid.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- ConvertFileNameFormat(
- Tcl_Interp *interp, /* The interp we are using for errors. */
- int objIndex, /* The index of the attribute. */
- char *fileName, /* The name of the file. */
- int longShort, /* 0 to short name, 1 to long name. */
- Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
- {
- HDIR findHandle;
- FILEFINDBUF3 findData;
- ULONG filesAtATime = 1;
- int pathArgc, i;
- char **pathArgv, **newPathArgv;
- char *currentElement, *resultStr;
- Tcl_DString resultDString;
- int result = TCL_OK;
-
- Tcl_SplitPath(fileName, &pathArgc, &pathArgv);
- newPathArgv = (char **) ckalloc(pathArgc * sizeof(char *));
-
- i = 0;
- if ((pathArgv[0][0] == '/')
- || ((strlen(pathArgv[0]) == 3) && (pathArgv[0][1] == ':'))) {
- newPathArgv[0] = (char *) ckalloc(strlen(pathArgv[0]) + 1);
- strcpy(newPathArgv[0], pathArgv[0]);
- i = 1;
- }
- for ( ; i < pathArgc; i++) {
- if (strcmp(pathArgv[i], ".") == 0) {
- currentElement = ckalloc(2);
- strcpy(currentElement, ".");
- } else if (strcmp(pathArgv[i], "..") == 0) {
- currentElement = ckalloc(3);
- strcpy(currentElement, "..");
- } else {
- int useLong;
-
- Tcl_DStringInit(&resultDString);
- resultStr = Tcl_JoinPath(i + 1, pathArgv, &resultDString);
- /* Use a new handle since we don't know if another find is active */
- findHandle = HDIR_CREATE;
- rc = DosFindFirst(resultStr, &findHandle,
- FILE_NORMAL | FILE_DIRECTORY,
- &findData, sizeof(findData), &filesAtATime,
- FIL_STANDARD);
- if (rc != NO_ERROR && rc != ERROR_NO_MORE_FILES) {
- pathArgc = i - 1;
- AttributesPosixError(interp, objIndex, fileName, 0);
- result = TCL_ERROR;
- Tcl_DStringFree(&resultDString);
- goto cleanup;
- }
- /*
- * If rc == ERROR_NO_MORE_FILES, we might have a case where we're
- * trying to find a long-name file on a short-name File System
- * such as DOS, where the long name is kept in the .LONGNAME
- * extended attribute by the WPS and EA-aware applications.
- * This is the only thing comparable to the Windows 95/NT long
- * name-to-alternate name mapping.
- * In that case, retrieve/set the long name from/in the EA.
- */
- if (longShort) {
- if (findData.achName[0] != '\0') {
- useLong = 1;
- } else {
- useLong = 0;
- }
- } else {
- if (findData.cAlternateFileName[0] == '\0') {
- useLong = 1;
- } else {
- useLong = 0;
- }
- }
- if (useLong) {
- currentElement = ckalloc(strlen(findData.achName) + 1);
- strcpy(currentElement, findData.achName);
- } else {
- currentElement = ckalloc(strlen(findData.cAlternateFileName)
- + 1);
- strcpy(currentElement, findData.cAlternateFileName);
- }
- Tcl_DStringFree(&resultDString);
- FindClose(findHandle);
- }
- newPathArgv[i] = currentElement;
- }
-
- Tcl_DStringInit(&resultDString);
- resultStr = Tcl_JoinPath(pathArgc, newPathArgv, &resultDString);
- *attributePtrPtr = Tcl_NewStringObj(resultStr,
- Tcl_DStringLength(&resultDString));
- Tcl_DStringFree(&resultDString);
-
- cleanup:
- for (i = 0; i < pathArgc; i++) {
- ckfree(newPathArgv[i]);
- }
- ckfree((char *) newPathArgv);
- return result;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetOS2FileLongName --
- *
- * Returns a Tcl_Obj containing the long version of the file name.
- *
- * Results:
- * Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
- * will have ref count 0. If the return value is not TCL_OK,
- * attributePtrPtr is not touched.
- *
- * Side effects:
- * A new object is allocated if the file is valid.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- GetOS2FileLongName(
- Tcl_Interp *interp, /* The interp we are using for errors. */
- int objIndex, /* The index of the attribute. */
- char *fileName, /* The name of the file. */
- Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
- {
- return ConvertFileNameFormat(interp, objIndex, fileName, 1,
- attributePtrPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetOS2FileShortName --
- *
- * Returns a Tcl_Obj containing the short version of the file
- * name.
- *
- * Results:
- * Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
- * will have ref count 0. If the return value is not TCL_OK,
- * attributePtrPtr is not touched.
- *
- * Side effects:
- * A new object is allocated if the file is valid.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- GetOS2FileShortName(
- Tcl_Interp *interp, /* The interp we are using for errors. */
- int objIndex, /* The index of the attribute. */
- char *fileName, /* The name of the file. */
- Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
- {
- return ConvertFileNameFormat(interp, objIndex, fileName, 0,
- attributePtrPtr);
- }
- #endif
-
- /*
- *----------------------------------------------------------------------
- *
- * SetOS2FileAttributes --
- *
- * Set the file attributes to the value given by attributePtr.
- * This routine sets the -hidden, -readonly, or -system attributes.
- *
- * Results:
- * Standard TCL error.
- *
- * Side effects:
- * The file's attribute is set.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- SetOS2FileAttributes(
- Tcl_Interp *interp, /* The interp we are using for errors. */
- int objIndex, /* The index of the attribute. */
- char *fileName, /* The name of the file. */
- Tcl_Obj *attributePtr) /* The new value of the attribute. */
- {
- FILESTATUS3 fileStatus;
- int yesNo;
- int result;
-
- rc = DosQueryPathInfo(fileName, FIL_STANDARD, &fileStatus,
- sizeof(FILESTATUS3));
- if (rc != NO_ERROR) {
- AttributesPosixError(interp, objIndex, fileName, 1);
- return TCL_ERROR;
- }
-
- result = Tcl_GetBooleanFromObj(interp, attributePtr, &yesNo);
- if (result != TCL_OK) {
- return result;
- }
-
- if (yesNo) {
- fileStatus.attrFile |= (attributeArray[objIndex]);
- } else {
- fileStatus.attrFile &= ~(attributeArray[objIndex]);
- }
-
- rc = DosSetPathInfo(fileName, FIL_STANDARD, &fileStatus,
- sizeof(FILESTATUS3), (ULONG)0);
- if (rc != NO_ERROR) {
- AttributesPosixError(interp, objIndex, fileName, 1);
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-
- #if 0
- /*
- *----------------------------------------------------------------------
- *
- * CannotGetAttribute --
- *
- * The attribute in question cannot be gotten.
- *
- * Results:
- * TCL_ERROR
- *
- * Side effects:
- * The object result is set to a pertinent error message.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- CannotGetAttribute(
- Tcl_Interp *interp, /* The interp we are using for errors. */
- int objIndex, /* The index of the attribute. */
- char *fileName, /* The name of the file. */
- Tcl_Obj **attributePtr) /* The value of the attribute. */
- {
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "cannot get attribute \"", tclpFileAttrStrings[objIndex],
- "\" for file \"", fileName, "\" : attribute is unavailable",
- (char *) NULL);
- return TCL_ERROR;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * CannotSetAttribute --
- *
- * The attribute in question cannot be set.
- *
- * Results:
- * TCL_ERROR
- *
- * Side effects:
- * The object result is set to a pertinent error message.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- CannotSetAttribute(
- Tcl_Interp *interp, /* The interp we are using for errors. */
- int objIndex, /* The index of the attribute. */
- char *fileName, /* The name of the file. */
- Tcl_Obj *attributePtr) /* The new value of the attribute. */
- {
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "cannot set attribute \"", tclpFileAttrStrings[objIndex],
- "\" for file \"", fileName, "\" : attribute is unavailable",
- (char *) NULL);
- return TCL_ERROR;
- }
-
- #endif
- /*
- *---------------------------------------------------------------------------
- *
- * TclpListVolumes --
- *
- * Lists the currently mounted volumes
- *
- * Results:
- * A standard Tcl result. Will always be TCL_OK, since there is no way
- * that this command can fail. Also, the interpreter's result is set to
- * the list of volumes.
- *
- * Side effects:
- * None
- *
- *---------------------------------------------------------------------------
- */
-
- int
- TclpListVolumes(
- Tcl_Interp *interp) /* Interpreter to which to pass the volume list */
- {
- Tcl_Obj *resultPtr, *elemPtr;
- char buf[4];
- int i;
- FSINFO infoBuf;
-
- resultPtr = Tcl_GetObjResult(interp);
-
- buf[1] = ':';
- buf[2] = '/';
- buf[3] = '\0';
-
- for (i = 0; i < 26; i++) {
- buf[0] = (char) ('a' + i);
- rc = DosQueryFSInfo(i+1, FSIL_VOLSER, &infoBuf, sizeof(infoBuf));
- if ( rc == NO_ERROR || rc == ERROR_NOT_READY) {
- elemPtr = Tcl_NewStringObj(buf, -1);
- Tcl_ListObjAppendElement(NULL, resultPtr, elemPtr);
- }
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * CopyFileAtts
- *
- * Copy the file attributes such as owner, group, permissions, and
- * modification date from one file to another.
- *
- * Results:
- * Standard Tcl result.
- *
- * Side effects:
- * user id, group id, permission bits, last modification time, and
- * last access time are updated in the new file to reflect the old
- * file.
- *
- *----------------------------------------------------------------------
- */
-
- int
- CopyFileAtts (
- char *src, /* Path name of source file */
- char *dst, /* Path name of target file */
- FILESTATUS3 *fsSource) /* File status of source file */
- {
- rc = DosSetPathInfo(dst, FIL_STANDARD, fsSource, sizeof (*fsSource), 0L);
- if (rc != NO_ERROR) {
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-