home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
tcltk805.zip
/
tcl805s.zip
/
tcl8.0.5
/
os2
/
tclOS2FCmd.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-02-09
|
61KB
|
1,886 lines
/*
* 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;
}