summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixFCmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixFCmd.c')
-rw-r--r--unix/tclUnixFCmd.c794
1 files changed, 566 insertions, 228 deletions
diff --git a/unix/tclUnixFCmd.c b/unix/tclUnixFCmd.c
index 13a2a6e..3b1b6ca 100644
--- a/unix/tclUnixFCmd.c
+++ b/unix/tclUnixFCmd.c
@@ -2,7 +2,7 @@
* tclUnixFCmd.c
*
* This file implements the unix specific portion of file manipulation
- * subcommands of the "file" command. All filename arguments should
+ * subcommands of the "file" command. All filename arguments should
* already be translated to native format.
*
* Copyright (c) 1996-1998 Sun Microsystems, Inc.
@@ -10,13 +10,11 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclUnixFCmd.c,v 1.60 2006/11/15 20:08:45 dgp Exp $
- *
* Portions of this code were derived from NetBSD source code which has the
* following copyright notice:
*
* Copyright (c) 1988, 1993, 1994
- * The Regents of the University of California. All rights reserved.
+ * The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -25,11 +23,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors may
+ * 3. Neither the name of the University nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
@@ -49,11 +43,11 @@
#include "tclInt.h"
#include <utime.h>
#include <grp.h>
-#ifndef HAVE_ST_BLKSIZE
+#ifndef HAVE_STRUCT_STAT_ST_BLKSIZE
#ifndef NO_FSTATFS
#include <sys/statfs.h>
#endif
-#endif
+#endif /* !HAVE_STRUCT_STAT_ST_BLKSIZE */
#ifdef HAVE_FTS
#include <fts.h>
#endif
@@ -63,11 +57,21 @@
* TraverseUnixTree() calls the traverseProc()
*/
-#define DOTREE_PRED 1 /* pre-order directory */
+#define DOTREE_PRED 1 /* pre-order directory */
#define DOTREE_POSTD 2 /* post-order directory */
#define DOTREE_F 3 /* regular file */
/*
+ * Fallback temporary file location the temporary file generation code. Can be
+ * overridden at compile time for when it is known that temp files can't be
+ * written to /tmp (hello, iOS!).
+ */
+
+#ifndef TCL_TEMPORARY_FILE_DIRECTORY
+#define TCL_TEMPORARY_FILE_DIRECTORY "/tmp"
+#endif
+
+/*
* Callbacks for file attributes code.
*/
@@ -86,11 +90,11 @@ static int SetPermissionsAttribute(Tcl_Interp *interp,
int objIndex, Tcl_Obj *fileName,
Tcl_Obj *attributePtr);
static int GetModeFromPermString(Tcl_Interp *interp,
- char *modeStringPtr, mode_t *modePtr);
-#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE)
-static int GetReadOnlyAttribute(Tcl_Interp *interp, int objIndex,
+ const char *modeStringPtr, mode_t *modePtr);
+#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) || defined(__CYGWIN__)
+static int GetUnixFileAttributes(Tcl_Interp *interp, int objIndex,
Tcl_Obj *fileName, Tcl_Obj **attributePtrPtr);
-static int SetReadOnlyAttribute(Tcl_Interp *interp, int objIndex,
+static int SetUnixFileAttributes(Tcl_Interp *interp, int objIndex,
Tcl_Obj *fileName, Tcl_Obj *attributePtr);
#endif
@@ -99,7 +103,7 @@ static int SetReadOnlyAttribute(Tcl_Interp *interp, int objIndex,
*/
typedef int (TraversalProc)(Tcl_DString *srcPtr, Tcl_DString *dstPtr,
- CONST Tcl_StatBuf *statBufPtr, int type, Tcl_DString *errorPtr);
+ const Tcl_StatBuf *statBufPtr, int type, Tcl_DString *errorPtr);
/*
* Constants and variables necessary for file attributes subcommand.
@@ -116,14 +120,24 @@ typedef int (TraversalProc)(Tcl_DString *srcPtr, Tcl_DString *dstPtr,
*/
extern TclFileAttrProcs tclpFileAttrProcs[];
-extern char *tclpFileAttrStrings[];
+extern const char *const tclpFileAttrStrings[];
-#else
+#else /* !DJGPP */
enum {
- UNIX_GROUP_ATTRIBUTE, UNIX_OWNER_ATTRIBUTE, UNIX_PERMISSIONS_ATTRIBUTE,
-#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE)
+#if defined(__CYGWIN__)
+ UNIX_ARCHIVE_ATTRIBUTE,
+#endif
+ UNIX_GROUP_ATTRIBUTE,
+#if defined(__CYGWIN__)
+ UNIX_HIDDEN_ATTRIBUTE,
+#endif
+ UNIX_OWNER_ATTRIBUTE, UNIX_PERMISSIONS_ATTRIBUTE,
+#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) || defined(__CYGWIN__)
UNIX_READONLY_ATTRIBUTE,
#endif
+#if defined(__CYGWIN__)
+ UNIX_SYSTEM_ATTRIBUTE,
+#endif
#ifdef MAC_OSX_TCL
MACOSX_CREATOR_ATTRIBUTE, MACOSX_TYPE_ATTRIBUTE, MACOSX_HIDDEN_ATTRIBUTE,
MACOSX_RSRCLENGTH_ATTRIBUTE,
@@ -131,25 +145,44 @@ enum {
UNIX_INVALID_ATTRIBUTE /* lint - last enum value needs no trailing , */
};
-MODULE_SCOPE CONST char *tclpFileAttrStrings[];
-CONST char *tclpFileAttrStrings[] = {
- "-group", "-owner", "-permissions",
-#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE)
+MODULE_SCOPE const char *const tclpFileAttrStrings[];
+const char *const tclpFileAttrStrings[] = {
+#if defined(__CYGWIN__)
+ "-archive",
+#endif
+ "-group",
+#if defined(__CYGWIN__)
+ "-hidden",
+#endif
+ "-owner", "-permissions",
+#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) || defined(__CYGWIN__)
"-readonly",
#endif
+#if defined(__CYGWIN__)
+ "-system",
+#endif
#ifdef MAC_OSX_TCL
"-creator", "-type", "-hidden", "-rsrclength",
#endif
NULL
};
-MODULE_SCOPE CONST TclFileAttrProcs tclpFileAttrProcs[];
-CONST TclFileAttrProcs tclpFileAttrProcs[] = {
+MODULE_SCOPE const TclFileAttrProcs tclpFileAttrProcs[];
+const TclFileAttrProcs tclpFileAttrProcs[] = {
+#if defined(__CYGWIN__)
+ {GetUnixFileAttributes, SetUnixFileAttributes},
+#endif
{GetGroupAttribute, SetGroupAttribute},
+#if defined(__CYGWIN__)
+ {GetUnixFileAttributes, SetUnixFileAttributes},
+#endif
{GetOwnerAttribute, SetOwnerAttribute},
{GetPermissionsAttribute, SetPermissionsAttribute},
-#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE)
- {GetReadOnlyAttribute, SetReadOnlyAttribute},
+#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) || defined(__CYGWIN__)
+ {GetUnixFileAttributes, SetUnixFileAttributes},
+#endif
+#if defined(__CYGWIN__)
+ {GetUnixFileAttributes, SetUnixFileAttributes},
#endif
#ifdef MAC_OSX_TCL
{TclMacOSXGetFileAttribute, TclMacOSXSetFileAttribute},
@@ -158,17 +191,17 @@ CONST TclFileAttrProcs tclpFileAttrProcs[] = {
{TclMacOSXGetFileAttribute, TclMacOSXSetFileAttribute},
#endif
};
-#endif
+#endif /* DJGPP */
/*
* This is the maximum number of consecutive readdir/unlink calls that can be
* made (with no intervening rewinddir or closedir/opendir) before triggering
* a bug that makes readdir return NULL even though some directory entries
- * have not been processed. The bug afflicts SunOS's readdir when applied to
- * ufs file systems and Darwin 6.5's (and OSX v.10.3.8's) HFS+. JH found the
- * Darwin readdir to reset at 147, so 130 is chosen to be conservative. We
+ * have not been processed. The bug afflicts SunOS's readdir when applied to
+ * ufs file systems and Darwin 6.5's (and OSX v.10.3.8's) HFS+. JH found the
+ * Darwin readdir to reset at 147, so 130 is chosen to be conservative. We
* can't do a general rewind on failure as NFS can create special files that
- * recreate themselves when you try and delete them. 8.4.8 added a solution
+ * recreate themselves when you try and delete them. 8.4.8 added a solution
* that was affected by a single such NFS file, this solution should not be
* affected by less than THRESHOLD such files. [Bug 1034337]
*/
@@ -179,20 +212,23 @@ CONST TclFileAttrProcs tclpFileAttrProcs[] = {
* Declarations for local procedures defined in this file:
*/
-static int CopyFileAtts(CONST char *src,
- CONST char *dst, CONST Tcl_StatBuf *statBufPtr);
-static int DoCopyFile(CONST char *srcPtr, CONST char *dstPtr,
- CONST Tcl_StatBuf *statBufPtr);
-static int DoCreateDirectory(CONST char *pathPtr);
+static int CopyFileAtts(const char *src,
+ const char *dst, const Tcl_StatBuf *statBufPtr);
+static const char * DefaultTempDir(void);
+static int DoCopyFile(const char *srcPtr, const char *dstPtr,
+ const Tcl_StatBuf *statBufPtr);
+static int DoCreateDirectory(const char *pathPtr);
static int DoRemoveDirectory(Tcl_DString *pathPtr,
int recursive, Tcl_DString *errorPtr);
-static int DoRenameFile(CONST char *src, CONST char *dst);
+static int DoRenameFile(const char *src, const char *dst);
static int TraversalCopy(Tcl_DString *srcPtr,
- Tcl_DString *dstPtr, CONST Tcl_StatBuf *statBufPtr,
- int type, Tcl_DString *errorPtr);
+ Tcl_DString *dstPtr,
+ const Tcl_StatBuf *statBufPtr, int type,
+ Tcl_DString *errorPtr);
static int TraversalDelete(Tcl_DString *srcPtr,
- Tcl_DString *dstPtr, CONST Tcl_StatBuf *statBufPtr,
- int type, Tcl_DString *errorPtr);
+ Tcl_DString *dstPtr,
+ const Tcl_StatBuf *statBufPtr, int type,
+ Tcl_DString *errorPtr);
static int TraverseUnixTree(TraversalProc *traversalProc,
Tcl_DString *sourcePtr, Tcl_DString *destPtr,
Tcl_DString *errorPtr, int doRewind);
@@ -205,43 +241,64 @@ static int TraverseUnixTree(TraversalProc *traversalProc,
* passing the standard MAXPATHLEN size resolved arg.
*/
-static char * Realpath(CONST char *path, char *resolved);
+static char * Realpath(const char *path, char *resolved);
char *
Realpath(
- CONST char *path,
+ const char *path,
char *resolved)
{
memset(resolved, 0, MAXPATHLEN);
return realpath(path, resolved);
}
#else
-#define Realpath realpath
-#endif
+# define Realpath realpath
+#endif /* PURIFY */
#ifndef NO_REALPATH
#if defined(__APPLE__) && defined(TCL_THREADS) && \
defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
MAC_OS_X_VERSION_MIN_REQUIRED < 1030
/*
- * prior to Darwin 7, realpath is not threadsafe, c.f. bug 711232;
- * if we might potentially be running on pre-10.3 OSX,
- * check Darwin release at runtime before using realpath.
+ * Prior to Darwin 7, realpath is not thread-safe, c.f. Bug 711232; if we
+ * might potentially be running on pre-10.3 OSX, check Darwin release at
+ * runtime before using realpath.
*/
+
MODULE_SCOPE long tclMacOSXDarwinRelease;
-#define haveRealpath (tclMacOSXDarwinRelease >= 7)
+# define haveRealpath (tclMacOSXDarwinRelease >= 7)
#else
-#define haveRealpath 1
+# define haveRealpath 1
#endif
#endif /* NO_REALPATH */
+
+#ifdef HAVE_FTS
+#if defined(HAVE_STRUCT_STAT64) && !defined(__APPLE__)
+/* fts doesn't do stat64 */
+# define noFtsStat 1
+#elif defined(__APPLE__) && defined(__LP64__) && \
+ defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+/*
+ * Prior to Darwin 9, 64bit fts_open() without FTS_NOSTAT may crash (due to a
+ * 64bit-unsafe ALIGN macro); if we could be running on pre-10.5 OSX, check
+ * Darwin release at runtime and do a separate stat() if necessary.
+ */
+
+MODULE_SCOPE long tclMacOSXDarwinRelease;
+# define noFtsStat (tclMacOSXDarwinRelease < 9)
+#else
+# define noFtsStat 0
+#endif
+#endif /* HAVE_FTS */
/*
*---------------------------------------------------------------------------
*
* TclpObjRenameFile, DoRenameFile --
*
- * 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
+ * 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.
@@ -249,7 +306,7 @@ MODULE_SCOPE long tclMacOSXDarwinRelease;
* In any other situation where dst already exists, the rename will fail.
*
* Results:
- * If the directory was successfully created, returns TCL_OK. Otherwise
+ * 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:
*
@@ -280,9 +337,9 @@ TclpObjRenameFile(
static int
DoRenameFile(
- CONST char *src, /* Pathname of file or dir to be renamed
+ const char *src, /* Pathname of file or dir to be renamed
* (native). */
- CONST char *dst) /* New pathname of file or directory
+ const char *dst) /* New pathname of file or directory
* (native). */
{
if (rename(src, dst) == 0) { /* INTL: Native. */
@@ -368,12 +425,12 @@ DoRenameFile(
*
* 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:
+ * 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 "".
+ * ENOENT: src doesn't exist. src or dst is "".
*
* Side effects:
* This procedure will also copy symbolic links, block, and character
@@ -390,7 +447,7 @@ TclpObjCopyFile(
Tcl_Obj *srcPathPtr,
Tcl_Obj *destPathPtr)
{
- CONST char *src = Tcl_FSGetNativePath(srcPathPtr);
+ const char *src = Tcl_FSGetNativePath(srcPathPtr);
Tcl_StatBuf srcStatBuf;
if (TclOSlstat(src, &srcStatBuf) != 0) { /* INTL: Native. */
@@ -402,9 +459,9 @@ TclpObjCopyFile(
static int
DoCopyFile(
- CONST char *src, /* Pathname of file to be copied (native). */
- CONST char *dst, /* Pathname of file to copy to (native). */
- CONST Tcl_StatBuf *statBufPtr)
+ const char *src, /* Pathname of file to be copied (native). */
+ const char *dst, /* Pathname of file to copy to (native). */
+ const Tcl_StatBuf *statBufPtr)
/* Used to determine filetype. */
{
Tcl_StatBuf dstStatBuf;
@@ -415,7 +472,7 @@ DoCopyFile(
}
/*
- * symlink, and some of the other calls will fail if the target exists, so
+ * Symlink, and some of the other calls will fail if the target exists, so
* we remove it first.
*/
@@ -434,15 +491,16 @@ DoCopyFile(
switch ((int) (statBufPtr->st_mode & S_IFMT)) {
#ifndef DJGPP
case S_IFLNK: {
- char link[MAXPATHLEN];
+ char linkBuf[MAXPATHLEN+1];
int length;
- length = readlink(src, link, sizeof(link)); /* INTL: Native. */
+ length = readlink(src, linkBuf, MAXPATHLEN);
+ /* INTL: Native. */
if (length == -1) {
return TCL_ERROR;
}
- link[length] = '\0';
- if (symlink(link, dst) < 0) { /* INTL: Native. */
+ linkBuf[length] = '\0';
+ if (symlink(linkBuf, dst) < 0) { /* INTL: Native. */
return TCL_ERROR;
}
#ifdef MAC_OSX_TCL
@@ -450,7 +508,7 @@ DoCopyFile(
#endif
break;
}
-#endif
+#endif /* !DJGPP */
case S_IFBLK:
case S_IFCHR:
if (mknod(dst, statBufPtr->st_mode, /* INTL: Native. */
@@ -481,17 +539,17 @@ DoCopyFile(
* A standard Tcl result.
*
* Side effects:
- * A file is copied. Dst will be overwritten if it exists.
+ * A file is copied. Dst will be overwritten if it exists.
*
*----------------------------------------------------------------------
*/
int
TclUnixCopyFile(
- CONST char *src, /* Pathname of file to copy (native). */
- CONST char *dst, /* Pathname of file to create/overwrite
+ const char *src, /* Pathname of file to copy (native). */
+ const char *dst, /* Pathname of file to create/overwrite
* (native). */
- CONST Tcl_StatBuf *statBufPtr,
+ const Tcl_StatBuf *statBufPtr,
/* Used to determine mode and blocksize. */
int dontCopyAtts) /* If flag set, don't copy attributes. */
{
@@ -504,7 +562,9 @@ TclUnixCopyFile(
#define BINMODE |O_BINARY
#else
#define BINMODE
-#endif
+#endif /* DJGPP */
+
+#define DEFAULT_COPY_BLOCK_SIZE 4069
if ((srcFd = TclOSopen(src, O_RDONLY BINMODE, 0)) < 0) { /* INTL: Native */
return TCL_ERROR;
@@ -523,33 +583,32 @@ TclUnixCopyFile(
* that's likely to be fairly efficient anyway.
*/
-#ifdef HAVE_ST_BLKSIZE
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
blockSize = statBufPtr->st_blksize;
-#else
-#ifndef NO_FSTATFS
+#elif !defined(NO_FSTATFS)
{
struct statfs fs;
- if (fstatfs(srcFd, &fs, sizeof(fs), 0) == 0) {
+ if (fstatfs(srcFd, &fs) == 0) {
blockSize = fs.f_bsize;
} else {
- blockSize = 4096;
+ blockSize = DEFAULT_COPY_BLOCK_SIZE;
}
}
#else
- blockSize = 4096;
-#endif
-#endif
+ blockSize = DEFAULT_COPY_BLOCK_SIZE;
+#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
- /* [SF Tcl Bug 1586470] Even if we HAVE_ST_BLKSIZE, there are
- * filesystems which report a bogus value for the blocksize. An
- * example is the Andrew Filesystem (afs), reporting a blocksize
- * of 0. When detecting such a situation we now simply fall back
- * to a hardwired default size.
+ /*
+ * [SF Tcl Bug 1586470] Even if we HAVE_STRUCT_STAT_ST_BLKSIZE, there are
+ * filesystems which report a bogus value for the blocksize. An example
+ * is the Andrew Filesystem (afs), reporting a blocksize of 0. When
+ * detecting such a situation we now simply fall back to a hardwired
+ * default size.
*/
if (blockSize <= 0) {
- blockSize = 4096;
+ blockSize = DEFAULT_COPY_BLOCK_SIZE;
}
buffer = ckalloc(blockSize);
while (1) {
@@ -590,8 +649,8 @@ TclUnixCopyFile(
*
* 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:
+ * 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.
@@ -612,9 +671,9 @@ TclpObjDeleteFile(
int
TclpDeleteFile(
- CONST char *path) /* Pathname of file to be removed (native). */
+ const void *path) /* Pathname of file to be removed (native). */
{
- if (unlink(path) != 0) { /* INTL: Native. */
+ if (unlink((const char *)path) != 0) {
return TCL_ERROR;
}
return TCL_OK;
@@ -655,7 +714,7 @@ TclpObjCreateDirectory(
static int
DoCreateDirectory(
- CONST char *path) /* Pathname of directory to create (native). */
+ const char *path) /* Pathname of directory to create (native). */
{
mode_t mode;
@@ -736,7 +795,6 @@ TclpObjCopyDirectory(
}
return ret;
}
-
/*
*---------------------------------------------------------------------------
@@ -803,7 +861,7 @@ DoRemoveDirectory(
* filled with UTF-8 name of file causing
* error. */
{
- CONST char *path;
+ const char *path;
mode_t oldPerm = 0;
int result;
@@ -901,7 +959,7 @@ TraverseUnixTree(
* files. */
{
Tcl_StatBuf statBuf;
- CONST char *source, *errfile;
+ const char *source, *errfile;
int result, sourceLen;
int targetLen;
#ifndef HAVE_FTS
@@ -909,7 +967,7 @@ TraverseUnixTree(
Tcl_DirEntry *dirEntPtr;
DIR *dirPtr;
#else
- CONST char *paths[2] = {NULL, NULL};
+ const char *paths[2] = {NULL, NULL};
FTS *fts = NULL;
FTSENT *ent;
#endif
@@ -928,7 +986,7 @@ TraverseUnixTree(
* Process the regular file
*/
- return (*traverseProc)(sourcePtr, targetPtr, &statBuf, DOTREE_F,
+ return traverseProc(sourcePtr, targetPtr, &statBuf, DOTREE_F,
errorPtr);
}
#ifndef HAVE_FTS
@@ -941,18 +999,18 @@ TraverseUnixTree(
errfile = source;
goto end;
}
- result = (*traverseProc)(sourcePtr, targetPtr, &statBuf, DOTREE_PRED,
+ result = traverseProc(sourcePtr, targetPtr, &statBuf, DOTREE_PRED,
errorPtr);
if (result != TCL_OK) {
closedir(dirPtr);
return result;
}
- Tcl_DStringAppend(sourcePtr, "/", 1);
+ TclDStringAppendLiteral(sourcePtr, "/");
sourceLen = Tcl_DStringLength(sourcePtr);
if (targetPtr != NULL) {
- Tcl_DStringAppend(targetPtr, "/", 1);
+ TclDStringAppendLiteral(targetPtr, "/");
targetLen = Tcl_DStringLength(targetPtr);
}
@@ -990,8 +1048,8 @@ TraverseUnixTree(
if (doRewind && (numProcessed > MAX_READDIR_UNLINK_THRESHOLD)) {
/*
* Call rewinddir if we've called unlink or rmdir so many times
- * (since the opendir or the previous rewinddir), to avoid
- * a NULL-return that may a symptom of a buggy readdir.
+ * (since the opendir or the previous rewinddir), to avoid a
+ * NULL-return that may a symptom of a buggy readdir.
*/
rewinddir(dirPtr);
@@ -1015,18 +1073,13 @@ TraverseUnixTree(
* that directory.
*/
- result = (*traverseProc)(sourcePtr, targetPtr, &statBuf, DOTREE_POSTD,
+ result = traverseProc(sourcePtr, targetPtr, &statBuf, DOTREE_POSTD,
errorPtr);
}
#else /* HAVE_FTS */
paths[0] = source;
- fts = fts_open((char**)paths, FTS_PHYSICAL|FTS_NOCHDIR|
-#ifdef HAVE_STRUCT_STAT64
- FTS_NOSTAT, /* fts doesn't do stat64 */
-#else
- (doRewind ? FTS_NOSTAT : 0), /* no need to stat for delete */
-#endif
- NULL);
+ fts = fts_open((char **) paths, FTS_PHYSICAL | FTS_NOCHDIR |
+ (noFtsStat || doRewind ? FTS_NOSTAT : 0), NULL);
if (fts == NULL) {
errfile = source;
goto end;
@@ -1039,11 +1092,11 @@ TraverseUnixTree(
while ((ent = fts_read(fts)) != NULL) {
unsigned short info = ent->fts_info;
- char * path = ent->fts_path + sourceLen;
+ char *path = ent->fts_path + sourceLen;
unsigned short pathlen = ent->fts_pathlen - sourceLen;
int type;
Tcl_StatBuf *statBufPtr = NULL;
-
+
if (info == FTS_DNR || info == FTS_ERR || info == FTS_NS) {
errfile = ent->fts_path;
break;
@@ -1053,28 +1106,28 @@ TraverseUnixTree(
Tcl_DStringAppend(targetPtr, path, pathlen);
}
switch (info) {
- case FTS_D:
- type = DOTREE_PRED;
- break;
- case FTS_DP:
- type = DOTREE_POSTD;
- break;
- default:
- type = DOTREE_F;
- break;
+ case FTS_D:
+ type = DOTREE_PRED;
+ break;
+ case FTS_DP:
+ type = DOTREE_POSTD;
+ break;
+ default:
+ type = DOTREE_F;
+ break;
}
if (!doRewind) { /* no need to stat for delete */
-#ifdef HAVE_STRUCT_STAT64
- statBufPtr = &statBuf;
- if (TclOSlstat(ent->fts_path, statBufPtr) != 0) {
- errfile = ent->fts_path;
- break;
+ if (noFtsStat) {
+ statBufPtr = &statBuf;
+ if (TclOSlstat(ent->fts_path, statBufPtr) != 0) {
+ errfile = ent->fts_path;
+ break;
+ }
+ } else {
+ statBufPtr = (Tcl_StatBuf *) ent->fts_statp;
}
-#else
- statBufPtr = ent->fts_statp;
-#endif
}
- result = (*traverseProc)(sourcePtr, targetPtr, statBufPtr, type,
+ result = traverseProc(sourcePtr, targetPtr, statBufPtr, type,
errorPtr);
if (result != TCL_OK) {
break;
@@ -1084,7 +1137,7 @@ TraverseUnixTree(
Tcl_DStringSetLength(targetPtr, targetLen);
}
}
-#endif /* HAVE_FTS */
+#endif /* !HAVE_FTS */
end:
if (errfile != NULL) {
@@ -1124,7 +1177,7 @@ static int
TraversalCopy(
Tcl_DString *srcPtr, /* Source pathname to copy (native). */
Tcl_DString *dstPtr, /* Destination pathname of copy (native). */
- CONST Tcl_StatBuf *statBufPtr,
+ const Tcl_StatBuf *statBufPtr,
/* Stat info for file specified by srcPtr. */
int type, /* Reason for call - see TraverseUnixTree(). */
Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free DString
@@ -1188,7 +1241,7 @@ static int
TraversalDelete(
Tcl_DString *srcPtr, /* Source pathname (native). */
Tcl_DString *ignore, /* Destination pathname (not used). */
- CONST Tcl_StatBuf *statBufPtr,
+ const Tcl_StatBuf *statBufPtr,
/* Stat info for file specified by srcPtr. */
int type, /* Reason for call - see TraverseUnixTree(). */
Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free DString
@@ -1236,9 +1289,9 @@ TraversalDelete(
static int
CopyFileAtts(
- CONST char *src, /* Path name of source file (native). */
- CONST char *dst, /* Path name of target file (native). */
- CONST Tcl_StatBuf *statBufPtr)
+ const char *src, /* Path name of source file (native). */
+ const char *dst, /* Path name of target file (native). */
+ const Tcl_StatBuf *statBufPtr)
/* Stat info for source file */
{
struct utimbuf tval;
@@ -1273,7 +1326,6 @@ CopyFileAtts(
#endif
return TCL_OK;
}
-
/*
*----------------------------------------------------------------------
@@ -1296,7 +1348,7 @@ static int
GetGroupAttribute(
Tcl_Interp *interp, /* The interp we are using for errors. */
int objIndex, /* The index of the attribute. */
- Tcl_Obj *fileName, /* The name of the file (UTF-8). */
+ Tcl_Obj *fileName, /* The name of the file (UTF-8). */
Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
{
Tcl_StatBuf statBuf;
@@ -1307,9 +1359,9 @@ GetGroupAttribute(
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not read \"",
- TclGetString(fileName), "\": ",
- Tcl_PosixError(interp), NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not read \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}
@@ -1320,13 +1372,12 @@ GetGroupAttribute(
*attributePtrPtr = Tcl_NewIntObj((int) statBuf.st_gid);
} else {
Tcl_DString ds;
- CONST char *utf;
+ const char *utf;
utf = Tcl_ExternalToUtfDString(NULL, groupPtr->gr_name, -1, &ds);
*attributePtrPtr = Tcl_NewStringObj(utf, -1);
Tcl_DStringFree(&ds);
}
- endgrent();
return TCL_OK;
}
@@ -1351,7 +1402,7 @@ static int
GetOwnerAttribute(
Tcl_Interp *interp, /* The interp we are using for errors. */
int objIndex, /* The index of the attribute. */
- Tcl_Obj *fileName, /* The name of the file (UTF-8). */
+ Tcl_Obj *fileName, /* The name of the file (UTF-8). */
Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
{
Tcl_StatBuf statBuf;
@@ -1362,9 +1413,9 @@ GetOwnerAttribute(
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not read \"",
- TclGetString(fileName), "\": ",
- Tcl_PosixError(interp), NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not read \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}
@@ -1375,13 +1426,10 @@ GetOwnerAttribute(
*attributePtrPtr = Tcl_NewIntObj((int) statBuf.st_uid);
} else {
Tcl_DString ds;
- CONST char *utf;
- utf = Tcl_ExternalToUtfDString(NULL, pwPtr->pw_name, -1, &ds);
- *attributePtrPtr = Tcl_NewStringObj(utf, Tcl_DStringLength(&ds));
- Tcl_DStringFree(&ds);
+ (void) Tcl_ExternalToUtfDString(NULL, pwPtr->pw_name, -1, &ds);
+ *attributePtrPtr = TclDStringToObj(&ds);
}
- endpwent();
return TCL_OK;
}
@@ -1406,7 +1454,7 @@ static int
GetPermissionsAttribute(
Tcl_Interp *interp, /* The interp we are using for errors. */
int objIndex, /* The index of the attribute. */
- Tcl_Obj *fileName, /* The name of the file (UTF-8). */
+ Tcl_Obj *fileName, /* The name of the file (UTF-8). */
Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
{
Tcl_StatBuf statBuf;
@@ -1416,9 +1464,9 @@ GetPermissionsAttribute(
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not read \"",
- TclGetString(fileName), "\": ",
- Tcl_PosixError(interp), NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not read \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}
@@ -1453,12 +1501,12 @@ SetGroupAttribute(
{
long gid;
int result;
- CONST char *native;
+ const char *native;
if (Tcl_GetLongFromObj(NULL, attributePtr, &gid) != TCL_OK) {
Tcl_DString ds;
struct group *groupPtr = NULL;
- CONST char *string;
+ const char *string;
int length;
string = Tcl_GetStringFromObj(attributePtr, &length);
@@ -1468,11 +1516,13 @@ SetGroupAttribute(
Tcl_DStringFree(&ds);
if (groupPtr == NULL) {
- endgrent();
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not set group for file \"",
- TclGetString(fileName), "\": group \"", string,
- "\" does not exist", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not set group for file \"%s\":"
+ " group \"%s\" does not exist",
+ TclGetString(fileName), string));
+ Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SETGRP",
+ "NO_GROUP", NULL);
}
return TCL_ERROR;
}
@@ -1482,12 +1532,11 @@ SetGroupAttribute(
native = Tcl_FSGetNativePath(fileName);
result = chown(native, (uid_t) -1, (gid_t) gid); /* INTL: Native. */
- endgrent();
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not set group for file \"",
- TclGetString(fileName), "\": ", Tcl_PosixError(interp),
- NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not set group for file \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}
@@ -1519,25 +1568,28 @@ SetOwnerAttribute(
{
long uid;
int result;
- CONST char *native;
+ const char *native;
if (Tcl_GetLongFromObj(NULL, attributePtr, &uid) != TCL_OK) {
Tcl_DString ds;
struct passwd *pwPtr = NULL;
- CONST char *string;
+ const char *string;
int length;
string = Tcl_GetStringFromObj(attributePtr, &length);
native = Tcl_UtfToExternalDString(NULL, string, length, &ds);
- pwPtr = TclpGetPwNam(native); /* INTL: Native. */
+ pwPtr = TclpGetPwNam(native); /* INTL: Native. */
Tcl_DStringFree(&ds);
if (pwPtr == NULL) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not set owner for file \"",
- TclGetString(fileName), "\": user \"", string,
- "\" does not exist", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not set owner for file \"%s\":"
+ " user \"%s\" does not exist",
+ TclGetString(fileName), string));
+ Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SETOWN",
+ "NO_USER", NULL);
}
return TCL_ERROR;
}
@@ -1545,13 +1597,13 @@ SetOwnerAttribute(
}
native = Tcl_FSGetNativePath(fileName);
- result = chown(native, (uid_t) uid, (gid_t) -1); /* INTL: Native. */
+ result = chown(native, (uid_t) uid, (gid_t) -1); /* INTL: Native. */
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not set owner for file \"",
- TclGetString(fileName), "\": ", Tcl_PosixError(interp),
- NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not set owner for file \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}
@@ -1583,18 +1635,31 @@ SetPermissionsAttribute(
{
long mode;
mode_t newMode;
- int result;
- CONST char *native;
+ int result = TCL_ERROR;
+ const char *native;
+ const char *modeStringPtr = TclGetString(attributePtr);
+ int scanned = TclParseAllWhiteSpace(modeStringPtr, -1);
/*
- * First try if the string is a number
+ * First supply support for octal number format
*/
- if (Tcl_GetLongFromObj(NULL, attributePtr, &mode) == TCL_OK) {
+ if ((modeStringPtr[scanned] == '0')
+ && (modeStringPtr[scanned+1] >= '0')
+ && (modeStringPtr[scanned+1] <= '7')) {
+ /* Leading zero - attempt octal interpretation */
+ Tcl_Obj *modeObj;
+
+ TclNewLiteralStringObj(modeObj, "0o");
+ Tcl_AppendToObj(modeObj, modeStringPtr+scanned+1, -1);
+ result = Tcl_GetLongFromObj(NULL, modeObj, &mode);
+ Tcl_DecrRefCount(modeObj);
+ }
+ if (result == TCL_OK
+ || Tcl_GetLongFromObj(NULL, attributePtr, &mode) == TCL_OK) {
newMode = (mode_t) (mode & 0x00007FFF);
} else {
Tcl_StatBuf buf;
- char *modeStringPtr = TclGetString(attributePtr);
/*
* Try the forms "rwxrwxrwx" and "ugo=rwx"
@@ -1606,9 +1671,9 @@ SetPermissionsAttribute(
result = TclpObjStat(fileName, &buf);
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not read \"",
- TclGetString(fileName), "\": ",
- Tcl_PosixError(interp), NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not read \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}
@@ -1616,8 +1681,10 @@ SetPermissionsAttribute(
if (GetModeFromPermString(NULL, modeStringPtr, &newMode) != TCL_OK) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "unknown permission string format \"",
- modeStringPtr, "\"", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "unknown permission string format \"%s\"",
+ modeStringPtr));
+ Tcl_SetErrorCode(interp, "TCL", "VALUE", "PERMISSION", NULL);
}
return TCL_ERROR;
}
@@ -1627,9 +1694,9 @@ SetPermissionsAttribute(
result = chmod(native, newMode); /* INTL: Native. */
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not set permissions for file \"",
- TclGetString(fileName), "\": ",
- Tcl_PosixError(interp), NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not set permissions for file \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}
@@ -1656,7 +1723,8 @@ SetPermissionsAttribute(
Tcl_Obj *
TclpObjListVolumes(void)
{
- Tcl_Obj *resultPtr = Tcl_NewStringObj("/", 1);
+ Tcl_Obj *resultPtr;
+ TclNewLiteralStringObj(resultPtr, "/");
Tcl_IncrRefCount(resultPtr);
return resultPtr;
@@ -1684,7 +1752,7 @@ TclpObjListVolumes(void)
static int
GetModeFromPermString(
Tcl_Interp *interp, /* The interp we are using for errors. */
- char *modeStringPtr, /* Permissions string */
+ const char *modeStringPtr, /* Permissions string */
mode_t *modePtr) /* pointer to the mode value */
{
mode_t newMode;
@@ -1837,13 +1905,13 @@ GetModeFromPermString(
}
}
switch (op) {
- case 1 :
+ case 1:
*modePtr = oldMode | (who & what);
continue;
- case 2 :
+ case 2:
*modePtr = oldMode & ~(who & what);
continue;
- case 3 :
+ case 3:
*modePtr = (oldMode & ~who) | (who & what);
continue;
}
@@ -1877,14 +1945,14 @@ TclpObjNormalizePath(
Tcl_Obj *pathPtr,
int nextCheckpoint)
{
- char *currentPathEndPosition;
+ const char *currentPathEndPosition;
int pathLen;
char cur;
- char *path = Tcl_GetStringFromObj(pathPtr, &pathLen);
+ const char *path = Tcl_GetStringFromObj(pathPtr, &pathLen);
+ Tcl_DString ds;
+ const char *nativePath;
#ifndef NO_REALPATH
char normPath[MAXPATHLEN];
- Tcl_DString ds;
- CONST char *nativePath;
#endif
/*
@@ -1936,8 +2004,6 @@ TclpObjNormalizePath(
* Reached directory separator.
*/
- Tcl_DString ds;
- CONST char *nativePath;
int accessOk;
nativePath = Tcl_UtfToExternalDString(NULL, path,
@@ -1988,7 +2054,7 @@ TclpObjNormalizePath(
return 0;
}
- nativePath = Tcl_UtfToExternalDString(NULL, path, nextCheckpoint, &ds);
+ nativePath = Tcl_UtfToExternalDString(NULL, path,nextCheckpoint, &ds);
if (Realpath(nativePath, normPath) != NULL) {
int newNormLen;
@@ -2003,12 +2069,12 @@ TclpObjNormalizePath(
Tcl_DStringFree(&ds);
/*
- * Enable this to have the native FS claim normalization of the
- * whole path for existing files. That would permit the caller
- * to declare normalization complete without calls to additional
- * filesystems. Saving lots of calls is probably worth the extra
- * access() time here. When no other FS's are registered though,
- * things are less clear.
+ * Enable this to have the native FS claim normalization of
+ * the whole path for existing files. That would permit the
+ * caller to declare normalization complete without calls to
+ * additional filesystems. Saving lots of calls is probably
+ * worth the extra access() time here. When no other FS's are
+ * registered though, things are less clear.
*
if (0 == access(normPath, F_OK)) {
return pathLen;
@@ -2063,11 +2129,284 @@ TclpObjNormalizePath(
return nextCheckpoint;
}
-#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE)
/*
*----------------------------------------------------------------------
*
- * GetReadOnlyAttribute
+ * TclpOpenTemporaryFile, TclUnixOpenTemporaryFile --
+ *
+ * Creates a temporary file, possibly based on the supplied bits and
+ * pieces of template supplied in the first three arguments. If the
+ * fourth argument is non-NULL, it contains a Tcl_Obj to store the name
+ * of the temporary file in (and it is caller's responsibility to clean
+ * up). If the fourth argument is NULL, try to arrange for the temporary
+ * file to go away once it is no longer needed.
+ *
+ * Results:
+ * A read-write Tcl Channel open on the file for TclpOpenTemporaryFile,
+ * or a file descriptor (or -1 on failure) for TclUnixOpenTemporaryFile.
+ *
+ * Side effects:
+ * Accesses the filesystem. Will set the contents of the Tcl_Obj fourth
+ * argument (if that is non-NULL).
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Channel
+TclpOpenTemporaryFile(
+ Tcl_Obj *dirObj,
+ Tcl_Obj *basenameObj,
+ Tcl_Obj *extensionObj,
+ Tcl_Obj *resultingNameObj)
+{
+ int fd = TclUnixOpenTemporaryFile(dirObj, basenameObj, extensionObj,
+ resultingNameObj);
+
+ if (fd == -1) {
+ return NULL;
+ }
+ return Tcl_MakeFileChannel(INT2PTR(fd), TCL_READABLE|TCL_WRITABLE);
+}
+
+int
+TclUnixOpenTemporaryFile(
+ Tcl_Obj *dirObj,
+ Tcl_Obj *basenameObj,
+ Tcl_Obj *extensionObj,
+ Tcl_Obj *resultingNameObj)
+{
+ Tcl_DString template, tmp;
+ const char *string;
+ int len, fd;
+
+ /*
+ * We should also check against making more then TMP_MAX of these.
+ */
+
+ if (dirObj) {
+ string = Tcl_GetStringFromObj(dirObj, &len);
+ Tcl_UtfToExternalDString(NULL, string, len, &template);
+ } else {
+ Tcl_DStringInit(&template);
+ Tcl_DStringAppend(&template, DefaultTempDir(), -1); /* INTL: native */
+ }
+
+ TclDStringAppendLiteral(&template, "/");
+
+ if (basenameObj) {
+ string = Tcl_GetStringFromObj(basenameObj, &len);
+ Tcl_UtfToExternalDString(NULL, string, len, &tmp);
+ TclDStringAppendDString(&template, &tmp);
+ Tcl_DStringFree(&tmp);
+ } else {
+ TclDStringAppendLiteral(&template, "tcl");
+ }
+
+ TclDStringAppendLiteral(&template, "_XXXXXX");
+
+#ifdef HAVE_MKSTEMPS
+ if (extensionObj) {
+ string = Tcl_GetStringFromObj(extensionObj, &len);
+ Tcl_UtfToExternalDString(NULL, string, len, &tmp);
+ TclDStringAppendDString(&template, &tmp);
+ fd = mkstemps(Tcl_DStringValue(&template), Tcl_DStringLength(&tmp));
+ Tcl_DStringFree(&tmp);
+ } else
+#endif
+ {
+ fd = mkstemp(Tcl_DStringValue(&template));
+ }
+
+ if (fd == -1) {
+ Tcl_DStringFree(&template);
+ return -1;
+ }
+
+ if (resultingNameObj) {
+ Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&template),
+ Tcl_DStringLength(&template), &tmp);
+ Tcl_SetStringObj(resultingNameObj, Tcl_DStringValue(&tmp),
+ Tcl_DStringLength(&tmp));
+ Tcl_DStringFree(&tmp);
+ } else {
+ /*
+ * Try to delete the file immediately since we're not reporting the
+ * name to anyone. Note that we're *not* handling any errors from
+ * this!
+ */
+
+ unlink(Tcl_DStringValue(&template));
+ errno = 0;
+ }
+ Tcl_DStringFree(&template);
+
+ return fd;
+}
+
+/*
+ * Helper that does *part* of what tempnam() does.
+ */
+
+static const char *
+DefaultTempDir(void)
+{
+ const char *dir;
+ struct stat buf;
+
+ dir = getenv("TMPDIR");
+ if (dir && dir[0] && stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode)
+ && access(dir, W_OK) == 0) {
+ return dir;
+ }
+
+#ifdef P_tmpdir
+ dir = P_tmpdir;
+ if (stat(dir, &buf)==0 && S_ISDIR(buf.st_mode) && access(dir, W_OK)==0) {
+ return dir;
+ }
+#endif
+
+ /*
+ * Assume that the default location ("/tmp" if not overridden) is always
+ * an existing writable directory; we've no recovery mechanism if it
+ * isn't.
+ */
+
+ return TCL_TEMPORARY_FILE_DIRECTORY;
+}
+
+#if defined(__CYGWIN__)
+
+static void
+StatError(
+ Tcl_Interp *interp, /* The interp that has the error */
+ Tcl_Obj *fileName) /* The name of the file which caused the
+ * error. */
+{
+ TclWinConvertError(GetLastError());
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf("could not read \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
+}
+
+static WCHAR *
+winPathFromObj(
+ Tcl_Obj *fileName)
+{
+ int size;
+ const char *native = Tcl_FSGetNativePath(fileName);
+ WCHAR *winPath;
+
+ size = cygwin_conv_path(1, native, NULL, 0);
+ winPath = ckalloc(size);
+ cygwin_conv_path(1, native, winPath, size);
+
+ return winPath;
+}
+
+static const int attributeArray[] = {
+ 0x20, 0, 2, 0, 0, 1, 4};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetUnixFileAttributes
+ *
+ * Gets the readonly attribute of a file.
+ *
+ * Results:
+ * Standard TCL result. Returns a new Tcl_Obj in attributePtrPtr if there
+ * is no error. The object will have ref count 0.
+ *
+ * Side effects:
+ * A new object is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetUnixFileAttributes(
+ Tcl_Interp *interp, /* The interp we are using for errors. */
+ int objIndex, /* The index of the attribute. */
+ Tcl_Obj *fileName, /* The name of the file (UTF-8). */
+ Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
+{
+ int fileAttributes;
+ WCHAR *winPath = winPathFromObj(fileName);
+
+ fileAttributes = GetFileAttributesW(winPath);
+ ckfree(winPath);
+
+ if (fileAttributes == -1) {
+ StatError(interp, fileName);
+ return TCL_ERROR;
+ }
+
+ *attributePtrPtr = Tcl_NewIntObj((fileAttributes&attributeArray[objIndex])!=0);
+
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SetUnixFileAttributes
+ *
+ * Sets the readonly attribute of a file.
+ *
+ * Results:
+ * Standard TCL result.
+ *
+ * Side effects:
+ * The readonly attribute of the file is changed.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+SetUnixFileAttributes(
+ Tcl_Interp *interp, /* The interp we are using for errors. */
+ int objIndex, /* The index of the attribute. */
+ Tcl_Obj *fileName, /* The name of the file (UTF-8). */
+ Tcl_Obj *attributePtr) /* The attribute to set. */
+{
+ int yesNo, fileAttributes, old;
+ WCHAR *winPath;
+
+ if (Tcl_GetBooleanFromObj(interp, attributePtr, &yesNo) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ winPath = winPathFromObj(fileName);
+
+ fileAttributes = old = GetFileAttributesW(winPath);
+
+ if (fileAttributes == -1) {
+ ckfree(winPath);
+ StatError(interp, fileName);
+ return TCL_ERROR;
+ }
+
+ if (yesNo) {
+ fileAttributes |= attributeArray[objIndex];
+ } else {
+ fileAttributes &= ~attributeArray[objIndex];
+ }
+
+ if ((fileAttributes != old)
+ && !SetFileAttributesW(winPath, fileAttributes)) {
+ ckfree(winPath);
+ StatError(interp, fileName);
+ return TCL_ERROR;
+ }
+
+ ckfree(winPath);
+ return TCL_OK;
+}
+#elif defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE)
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetUnixFileAttributes
*
* Gets the readonly attribute (user immutable flag) of a file.
*
@@ -2082,7 +2421,7 @@ TclpObjNormalizePath(
*/
static int
-GetReadOnlyAttribute(
+GetUnixFileAttributes(
Tcl_Interp *interp, /* The interp we are using for errors. */
int objIndex, /* The index of the attribute. */
Tcl_Obj *fileName, /* The name of the file (UTF-8). */
@@ -2095,14 +2434,14 @@ GetReadOnlyAttribute(
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not read \"",
- TclGetString(fileName), "\": ", Tcl_PosixError(interp),
- NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not read \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}
- *attributePtrPtr = Tcl_NewBooleanObj((statBuf.st_flags&UF_IMMUTABLE) != 0);
+ *attributePtrPtr = Tcl_NewBooleanObj(statBuf.st_flags&UF_IMMUTABLE);
return TCL_OK;
}
@@ -2110,7 +2449,7 @@ GetReadOnlyAttribute(
/*
*---------------------------------------------------------------------------
*
- * SetReadOnlyAttribute
+ * SetUnixFileAttributes
*
* Sets the readonly attribute (user immutable flag) of a file.
*
@@ -2124,16 +2463,15 @@ GetReadOnlyAttribute(
*/
static int
-SetReadOnlyAttribute(
+SetUnixFileAttributes(
Tcl_Interp *interp, /* The interp we are using for errors. */
int objIndex, /* The index of the attribute. */
Tcl_Obj *fileName, /* The name of the file (UTF-8). */
Tcl_Obj *attributePtr) /* The attribute to set. */
{
Tcl_StatBuf statBuf;
- int result;
- int readonly;
- CONST char *native;
+ int result, readonly;
+ const char *native;
if (Tcl_GetBooleanFromObj(interp, attributePtr, &readonly) != TCL_OK) {
return TCL_ERROR;
@@ -2143,9 +2481,9 @@ SetReadOnlyAttribute(
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not read \"",
- TclGetString(fileName), "\": ", Tcl_PosixError(interp),
- NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not read \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}
@@ -2160,9 +2498,9 @@ SetReadOnlyAttribute(
result = chflags(native, statBuf.st_flags); /* INTL: Native. */
if (result != 0) {
if (interp != NULL) {
- Tcl_AppendResult(interp, "could not set flags for file \"",
- TclGetString(fileName), "\": ", Tcl_PosixError(interp),
- NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "could not set flags for file \"%s\": %s",
+ TclGetString(fileName), Tcl_PosixError(interp)));
}
return TCL_ERROR;
}