os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinFCmd.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
/*
sl@0
     2
 * tclWinFCmd.c
sl@0
     3
 *
sl@0
     4
 *      This file implements the Windows specific portion of file manipulation 
sl@0
     5
 *      subcommands of the "file" command. 
sl@0
     6
 *
sl@0
     7
 * Copyright (c) 1996-1998 Sun Microsystems, Inc.
sl@0
     8
 *
sl@0
     9
 * See the file "license.terms" for information on usage and redistribution
sl@0
    10
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    11
 *
sl@0
    12
 * RCS: @(#) $Id: tclWinFCmd.c,v 1.35.2.5 2006/08/30 17:48:48 hobbs Exp $
sl@0
    13
 */
sl@0
    14
sl@0
    15
#include "tclWinInt.h"
sl@0
    16
sl@0
    17
/*
sl@0
    18
 * The following constants specify the type of callback when
sl@0
    19
 * TraverseWinTree() calls the traverseProc()
sl@0
    20
 */
sl@0
    21
sl@0
    22
#define DOTREE_PRED   1     /* pre-order directory  */
sl@0
    23
#define DOTREE_POSTD  2     /* post-order directory */
sl@0
    24
#define DOTREE_F      3     /* regular file */
sl@0
    25
sl@0
    26
/*
sl@0
    27
 * Callbacks for file attributes code.
sl@0
    28
 */
sl@0
    29
sl@0
    30
static int		GetWinFileAttributes _ANSI_ARGS_((Tcl_Interp *interp,
sl@0
    31
			    int objIndex, Tcl_Obj *fileName,
sl@0
    32
			    Tcl_Obj **attributePtrPtr));
sl@0
    33
static int		GetWinFileLongName _ANSI_ARGS_((Tcl_Interp *interp,
sl@0
    34
			    int objIndex, Tcl_Obj *fileName,
sl@0
    35
			    Tcl_Obj **attributePtrPtr));
sl@0
    36
static int		GetWinFileShortName _ANSI_ARGS_((Tcl_Interp *interp,
sl@0
    37
			    int objIndex, Tcl_Obj *fileName,
sl@0
    38
			    Tcl_Obj **attributePtrPtr));
sl@0
    39
static int		SetWinFileAttributes _ANSI_ARGS_((Tcl_Interp *interp,
sl@0
    40
			    int objIndex, Tcl_Obj *fileName,
sl@0
    41
			    Tcl_Obj *attributePtr));
sl@0
    42
static int		CannotSetAttribute _ANSI_ARGS_((Tcl_Interp *interp,
sl@0
    43
			    int objIndex, Tcl_Obj *fileName,
sl@0
    44
			    Tcl_Obj *attributePtr));
sl@0
    45
sl@0
    46
/*
sl@0
    47
 * Constants and variables necessary for file attributes subcommand.
sl@0
    48
 */
sl@0
    49
sl@0
    50
enum {
sl@0
    51
    WIN_ARCHIVE_ATTRIBUTE,
sl@0
    52
    WIN_HIDDEN_ATTRIBUTE,
sl@0
    53
    WIN_LONGNAME_ATTRIBUTE,
sl@0
    54
    WIN_READONLY_ATTRIBUTE,
sl@0
    55
    WIN_SHORTNAME_ATTRIBUTE,
sl@0
    56
    WIN_SYSTEM_ATTRIBUTE
sl@0
    57
};
sl@0
    58
sl@0
    59
static int attributeArray[] = {FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_HIDDEN,
sl@0
    60
	0, FILE_ATTRIBUTE_READONLY, 0, FILE_ATTRIBUTE_SYSTEM};
sl@0
    61
sl@0
    62
sl@0
    63
CONST char *tclpFileAttrStrings[] = {
sl@0
    64
	"-archive", "-hidden", "-longname", "-readonly",
sl@0
    65
	"-shortname", "-system", (char *) NULL
sl@0
    66
};
sl@0
    67
sl@0
    68
CONST TclFileAttrProcs tclpFileAttrProcs[] = {
sl@0
    69
	{GetWinFileAttributes, SetWinFileAttributes},
sl@0
    70
	{GetWinFileAttributes, SetWinFileAttributes},
sl@0
    71
	{GetWinFileLongName, CannotSetAttribute},
sl@0
    72
	{GetWinFileAttributes, SetWinFileAttributes},
sl@0
    73
	{GetWinFileShortName, CannotSetAttribute},
sl@0
    74
	{GetWinFileAttributes, SetWinFileAttributes}};
sl@0
    75
sl@0
    76
#ifdef HAVE_NO_SEH
sl@0
    77
sl@0
    78
/*
sl@0
    79
 * Unlike Borland and Microsoft, we don't register exception handlers
sl@0
    80
 * by pushing registration records onto the runtime stack.  Instead, we
sl@0
    81
 * register them by creating an EXCEPTION_REGISTRATION within the activation
sl@0
    82
 * record.
sl@0
    83
 */
sl@0
    84
sl@0
    85
typedef struct EXCEPTION_REGISTRATION {
sl@0
    86
    struct EXCEPTION_REGISTRATION* link;
sl@0
    87
    EXCEPTION_DISPOSITION (*handler)( struct _EXCEPTION_RECORD*, void*,
sl@0
    88
				      struct _CONTEXT*, void* );
sl@0
    89
    void* ebp;
sl@0
    90
    void* esp;
sl@0
    91
    int status;
sl@0
    92
} EXCEPTION_REGISTRATION;
sl@0
    93
sl@0
    94
#endif
sl@0
    95
sl@0
    96
/*
sl@0
    97
 * Prototype for the TraverseWinTree callback function.
sl@0
    98
 */
sl@0
    99
sl@0
   100
typedef int (TraversalProc)(CONST TCHAR *srcPtr, CONST TCHAR *dstPtr, 
sl@0
   101
	int type, Tcl_DString *errorPtr);
sl@0
   102
sl@0
   103
/*
sl@0
   104
 * Declarations for local procedures defined in this file:
sl@0
   105
 */
sl@0
   106
sl@0
   107
static void		StatError(Tcl_Interp *interp, Tcl_Obj *fileName);
sl@0
   108
static int		ConvertFileNameFormat(Tcl_Interp *interp, 
sl@0
   109
			    int objIndex, Tcl_Obj *fileName, int longShort,
sl@0
   110
			    Tcl_Obj **attributePtrPtr);
sl@0
   111
static int		DoCopyFile(CONST TCHAR *srcPtr, CONST TCHAR *dstPtr);
sl@0
   112
static int		DoCreateDirectory(CONST TCHAR *pathPtr);
sl@0
   113
static int		DoRemoveJustDirectory(CONST TCHAR *nativeSrc, 
sl@0
   114
			    int ignoreError, Tcl_DString *errorPtr);
sl@0
   115
static int		DoRemoveDirectory(Tcl_DString *pathPtr, int recursive, 
sl@0
   116
			    Tcl_DString *errorPtr);
sl@0
   117
static int		DoRenameFile(CONST TCHAR *nativeSrc, CONST TCHAR *dstPtr);
sl@0
   118
static int		TraversalCopy(CONST TCHAR *srcPtr, CONST TCHAR *dstPtr, 
sl@0
   119
			    int type, Tcl_DString *errorPtr);
sl@0
   120
static int		TraversalDelete(CONST TCHAR *srcPtr, CONST TCHAR *dstPtr, 
sl@0
   121
			    int type, Tcl_DString *errorPtr);
sl@0
   122
static int		TraverseWinTree(TraversalProc *traverseProc,
sl@0
   123
			    Tcl_DString *sourcePtr, Tcl_DString *dstPtr, 
sl@0
   124
			    Tcl_DString *errorPtr);
sl@0
   125
sl@0
   126

sl@0
   127
/*
sl@0
   128
 *---------------------------------------------------------------------------
sl@0
   129
 *
sl@0
   130
 * TclpObjRenameFile, DoRenameFile --
sl@0
   131
 *
sl@0
   132
 *      Changes the name of an existing file or directory, from src to dst.
sl@0
   133
 *	If src and dst refer to the same file or directory, does nothing
sl@0
   134
 *	and returns success.  Otherwise if dst already exists, it will be
sl@0
   135
 *	deleted and replaced by src subject to the following conditions:
sl@0
   136
 *	    If src is a directory, dst may be an empty directory.
sl@0
   137
 *	    If src is a file, dst may be a file.
sl@0
   138
 *	In any other situation where dst already exists, the rename will
sl@0
   139
 *	fail.  
sl@0
   140
 *
sl@0
   141
 * Results:
sl@0
   142
 *	If the file or directory was successfully renamed, returns TCL_OK.
sl@0
   143
 *	Otherwise the return value is TCL_ERROR and errno is set to
sl@0
   144
 *	indicate the error.  Some possible values for errno are:
sl@0
   145
 *
sl@0
   146
 *	ENAMETOOLONG: src or dst names are too long.
sl@0
   147
 *	EACCES:     src or dst parent directory can't be read and/or written.
sl@0
   148
 *	EEXIST:	    dst is a non-empty directory.
sl@0
   149
 *	EINVAL:	    src is a root directory or dst is a subdirectory of src.
sl@0
   150
 *	EISDIR:	    dst is a directory, but src is not.
sl@0
   151
 *	ENOENT:	    src doesn't exist.  src or dst is "".
sl@0
   152
 *	ENOTDIR:    src is a directory, but dst is not.  
sl@0
   153
 *	EXDEV:	    src and dst are on different filesystems.
sl@0
   154
 *
sl@0
   155
 *	EACCES:     exists an open file already referring to src or dst.
sl@0
   156
 *	EACCES:     src or dst specify the current working directory (NT).
sl@0
   157
 *	EACCES:	    src specifies a char device (nul:, com1:, etc.) 
sl@0
   158
 *	EEXIST:	    dst specifies a char device (nul:, com1:, etc.) (NT)
sl@0
   159
 *	EACCES:	    dst specifies a char device (nul:, com1:, etc.) (95)
sl@0
   160
 *	
sl@0
   161
 * Side effects:
sl@0
   162
 *	The implementation supports cross-filesystem renames of files,
sl@0
   163
 *	but the caller should be prepared to emulate cross-filesystem
sl@0
   164
 *	renames of directories if errno is EXDEV.
sl@0
   165
 *
sl@0
   166
 *---------------------------------------------------------------------------
sl@0
   167
 */
sl@0
   168
sl@0
   169
int 
sl@0
   170
TclpObjRenameFile(srcPathPtr, destPathPtr)
sl@0
   171
    Tcl_Obj *srcPathPtr;
sl@0
   172
    Tcl_Obj *destPathPtr;
sl@0
   173
{
sl@0
   174
    return DoRenameFile(Tcl_FSGetNativePath(srcPathPtr), 
sl@0
   175
			Tcl_FSGetNativePath(destPathPtr));
sl@0
   176
}
sl@0
   177
sl@0
   178
static int
sl@0
   179
DoRenameFile(
sl@0
   180
    CONST TCHAR *nativeSrc,	/* Pathname of file or dir to be renamed
sl@0
   181
				 * (native). */ 
sl@0
   182
    CONST TCHAR *nativeDst)	/* New pathname for file or directory
sl@0
   183
				 * (native). */
sl@0
   184
{    
sl@0
   185
#ifdef HAVE_NO_SEH
sl@0
   186
    EXCEPTION_REGISTRATION registration;
sl@0
   187
#endif
sl@0
   188
    DWORD srcAttr, dstAttr;
sl@0
   189
    int retval = -1;
sl@0
   190
sl@0
   191
    /*
sl@0
   192
     * The MoveFile API acts differently under Win95/98 and NT
sl@0
   193
     * WRT NULL and "". Avoid passing these values.
sl@0
   194
     */
sl@0
   195
sl@0
   196
    if (nativeSrc == NULL || nativeSrc[0] == '\0' ||
sl@0
   197
        nativeDst == NULL || nativeDst[0] == '\0') {
sl@0
   198
	Tcl_SetErrno(ENOENT);
sl@0
   199
	return TCL_ERROR;
sl@0
   200
    }
sl@0
   201
sl@0
   202
    /*
sl@0
   203
     * The MoveFile API would throw an exception under NT
sl@0
   204
     * if one of the arguments is a char block device.
sl@0
   205
     */
sl@0
   206
sl@0
   207
#ifndef HAVE_NO_SEH
sl@0
   208
    __try {
sl@0
   209
	if ((*tclWinProcs->moveFileProc)(nativeSrc, nativeDst) != FALSE) {
sl@0
   210
	    retval = TCL_OK;
sl@0
   211
	}
sl@0
   212
    } __except (EXCEPTION_EXECUTE_HANDLER) {}
sl@0
   213
#else
sl@0
   214
sl@0
   215
    /*
sl@0
   216
     * Don't have SEH available, do things the hard way.
sl@0
   217
     * Note that this needs to be one block of asm, to avoid stack
sl@0
   218
     * imbalance; also, it is illegal for one asm block to contain 
sl@0
   219
     * a jump to another.
sl@0
   220
     */
sl@0
   221
sl@0
   222
    __asm__ __volatile__ (
sl@0
   223
	/*
sl@0
   224
	 * Pick up params before messing with the stack */
sl@0
   225
sl@0
   226
	"movl	    %[nativeDst],   %%ebx"	    "\n\t"
sl@0
   227
	"movl       %[nativeSrc],   %%ecx"          "\n\t"
sl@0
   228
sl@0
   229
	/*
sl@0
   230
	 * Construct an EXCEPTION_REGISTRATION to protect the
sl@0
   231
	 * call to MoveFile
sl@0
   232
	 */
sl@0
   233
	"leal       %[registration], %%edx"         "\n\t"
sl@0
   234
	"movl       %%fs:0,         %%eax"          "\n\t"
sl@0
   235
	"movl       %%eax,          0x0(%%edx)"     "\n\t" /* link */
sl@0
   236
	"leal       1f,             %%eax"          "\n\t"
sl@0
   237
	"movl       %%eax,          0x4(%%edx)"     "\n\t" /* handler */
sl@0
   238
	"movl       %%ebp,          0x8(%%edx)"     "\n\t" /* ebp */
sl@0
   239
	"movl       %%esp,          0xc(%%edx)"     "\n\t" /* esp */
sl@0
   240
	"movl       $0,             0x10(%%edx)"    "\n\t" /* status */
sl@0
   241
	
sl@0
   242
	/* Link the EXCEPTION_REGISTRATION on the chain */
sl@0
   243
	
sl@0
   244
	"movl       %%edx,          %%fs:0"         "\n\t"
sl@0
   245
	
sl@0
   246
	/* Call MoveFile( nativeSrc, nativeDst ) */
sl@0
   247
	
sl@0
   248
	"pushl	    %%ebx"			    "\n\t"
sl@0
   249
	"pushl	    %%ecx"			    "\n\t"
sl@0
   250
	"movl	    %[moveFile],    %%eax"	    "\n\t"
sl@0
   251
	"call	    *%%eax"			    "\n\t"
sl@0
   252
	
sl@0
   253
	/* 
sl@0
   254
	 * Come here on normal exit.  Recover the EXCEPTION_REGISTRATION
sl@0
   255
	 * and put the status return from MoveFile into it.
sl@0
   256
	 */
sl@0
   257
	
sl@0
   258
	"movl	    %%fs:0,	    %%edx"	    "\n\t"
sl@0
   259
	"movl	    %%eax,	    0x10(%%edx)"    "\n\t"
sl@0
   260
	"jmp	    2f"				    "\n"
sl@0
   261
	
sl@0
   262
	/*
sl@0
   263
	 * Come here on an exception.  Recover the EXCEPTION_REGISTRATION
sl@0
   264
	 */
sl@0
   265
	
sl@0
   266
	"1:"					    "\t"
sl@0
   267
	"movl       %%fs:0,         %%edx"          "\n\t"
sl@0
   268
	"movl       0x8(%%edx),     %%edx"          "\n\t"
sl@0
   269
	
sl@0
   270
	/* 
sl@0
   271
	 * Come here however we exited.  Restore context from the
sl@0
   272
	 * EXCEPTION_REGISTRATION in case the stack is unbalanced.
sl@0
   273
	 */
sl@0
   274
	
sl@0
   275
	"2:"                                        "\t"
sl@0
   276
	"movl       0xc(%%edx),     %%esp"          "\n\t"
sl@0
   277
	"movl       0x8(%%edx),     %%ebp"          "\n\t"
sl@0
   278
	"movl       0x0(%%edx),     %%eax"          "\n\t"
sl@0
   279
	"movl       %%eax,          %%fs:0"         "\n\t"
sl@0
   280
	
sl@0
   281
	:
sl@0
   282
	/* No outputs */
sl@0
   283
        :
sl@0
   284
	[registration]  "m"     (registration),
sl@0
   285
	[nativeDst]	"m"     (nativeDst),
sl@0
   286
	[nativeSrc]     "m"     (nativeSrc),
sl@0
   287
	[moveFile]      "r"     (tclWinProcs->moveFileProc)
sl@0
   288
        :
sl@0
   289
	"%eax", "%ebx", "%ecx", "%edx", "memory"
sl@0
   290
        );
sl@0
   291
    if (registration.status != FALSE) {
sl@0
   292
	retval = TCL_OK;
sl@0
   293
    }
sl@0
   294
#endif
sl@0
   295
sl@0
   296
    if (retval != -1)
sl@0
   297
        return retval;
sl@0
   298
sl@0
   299
    TclWinConvertError(GetLastError());
sl@0
   300
sl@0
   301
    srcAttr = (*tclWinProcs->getFileAttributesProc)(nativeSrc);
sl@0
   302
    dstAttr = (*tclWinProcs->getFileAttributesProc)(nativeDst);
sl@0
   303
    if (srcAttr == 0xffffffff) {
sl@0
   304
	if ((*tclWinProcs->getFullPathNameProc)(nativeSrc, 0, NULL, NULL) >= MAX_PATH) {
sl@0
   305
	    errno = ENAMETOOLONG;
sl@0
   306
	    return TCL_ERROR;
sl@0
   307
	}
sl@0
   308
	srcAttr = 0;
sl@0
   309
    }
sl@0
   310
    if (dstAttr == 0xffffffff) {
sl@0
   311
	if ((*tclWinProcs->getFullPathNameProc)(nativeDst, 0, NULL, NULL) >= MAX_PATH) {
sl@0
   312
	    errno = ENAMETOOLONG;
sl@0
   313
	    return TCL_ERROR;
sl@0
   314
	}
sl@0
   315
	dstAttr = 0;
sl@0
   316
    }
sl@0
   317
sl@0
   318
    if (errno == EBADF) {
sl@0
   319
	errno = EACCES;
sl@0
   320
	return TCL_ERROR;
sl@0
   321
    }
sl@0
   322
    if (errno == EACCES) {
sl@0
   323
	decode:
sl@0
   324
	if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
sl@0
   325
	    TCHAR *nativeSrcRest, *nativeDstRest;
sl@0
   326
	    CONST char **srcArgv, **dstArgv;
sl@0
   327
	    int size, srcArgc, dstArgc;
sl@0
   328
	    WCHAR nativeSrcPath[MAX_PATH];
sl@0
   329
	    WCHAR nativeDstPath[MAX_PATH];
sl@0
   330
	    Tcl_DString srcString, dstString;
sl@0
   331
	    CONST char *src, *dst;
sl@0
   332
sl@0
   333
	    size = (*tclWinProcs->getFullPathNameProc)(nativeSrc, MAX_PATH, 
sl@0
   334
		    nativeSrcPath, &nativeSrcRest);
sl@0
   335
	    if ((size == 0) || (size > MAX_PATH)) {
sl@0
   336
		return TCL_ERROR;
sl@0
   337
	    }
sl@0
   338
	    size = (*tclWinProcs->getFullPathNameProc)(nativeDst, MAX_PATH, 
sl@0
   339
		    nativeDstPath, &nativeDstRest);
sl@0
   340
	    if ((size == 0) || (size > MAX_PATH)) {
sl@0
   341
		return TCL_ERROR;
sl@0
   342
	    }
sl@0
   343
	    (*tclWinProcs->charLowerProc)((TCHAR *) nativeSrcPath);
sl@0
   344
	    (*tclWinProcs->charLowerProc)((TCHAR *) nativeDstPath);
sl@0
   345
sl@0
   346
	    src = Tcl_WinTCharToUtf((TCHAR *) nativeSrcPath, -1, &srcString);
sl@0
   347
	    dst = Tcl_WinTCharToUtf((TCHAR *) nativeDstPath, -1, &dstString);
sl@0
   348
	    if (strncmp(src, dst, (size_t) Tcl_DStringLength(&srcString)) == 0) {
sl@0
   349
		/*
sl@0
   350
		 * Trying to move a directory into itself.
sl@0
   351
		 */
sl@0
   352
sl@0
   353
		errno = EINVAL;
sl@0
   354
		Tcl_DStringFree(&srcString);
sl@0
   355
		Tcl_DStringFree(&dstString);
sl@0
   356
		return TCL_ERROR;
sl@0
   357
	    }
sl@0
   358
	    Tcl_SplitPath(src, &srcArgc, &srcArgv);
sl@0
   359
	    Tcl_SplitPath(dst, &dstArgc, &dstArgv);
sl@0
   360
	    Tcl_DStringFree(&srcString);
sl@0
   361
	    Tcl_DStringFree(&dstString);
sl@0
   362
sl@0
   363
	    if (srcArgc == 1) {
sl@0
   364
		/*
sl@0
   365
		 * They are trying to move a root directory.  Whether
sl@0
   366
		 * or not it is across filesystems, this cannot be
sl@0
   367
		 * done.
sl@0
   368
		 */
sl@0
   369
sl@0
   370
		Tcl_SetErrno(EINVAL);
sl@0
   371
	    } else if ((srcArgc > 0) && (dstArgc > 0) &&
sl@0
   372
		    (strcmp(srcArgv[0], dstArgv[0]) != 0)) {
sl@0
   373
		/*
sl@0
   374
		 * If src is a directory and dst filesystem != src
sl@0
   375
		 * filesystem, errno should be EXDEV.  It is very
sl@0
   376
		 * important to get this behavior, so that the caller
sl@0
   377
		 * can respond to a cross filesystem rename by
sl@0
   378
		 * simulating it with copy and delete.  The MoveFile
sl@0
   379
		 * system call already handles the case of moving a
sl@0
   380
		 * file between filesystems.
sl@0
   381
		 */
sl@0
   382
sl@0
   383
		Tcl_SetErrno(EXDEV);
sl@0
   384
	    }
sl@0
   385
sl@0
   386
	    ckfree((char *) srcArgv);
sl@0
   387
	    ckfree((char *) dstArgv);
sl@0
   388
	}
sl@0
   389
sl@0
   390
	/*
sl@0
   391
	 * Other types of access failure is that dst is a read-only
sl@0
   392
	 * filesystem, that an open file referred to src or dest, or that
sl@0
   393
	 * src or dest specified the current working directory on the
sl@0
   394
	 * current filesystem.  EACCES is returned for those cases.
sl@0
   395
	 */
sl@0
   396
sl@0
   397
    } else if (Tcl_GetErrno() == EEXIST) {
sl@0
   398
	/*
sl@0
   399
	 * Reports EEXIST any time the target already exists.  If it makes
sl@0
   400
	 * sense, remove the old file and try renaming again.
sl@0
   401
	 */
sl@0
   402
sl@0
   403
	if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
sl@0
   404
	    if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
sl@0
   405
		/*
sl@0
   406
		 * Overwrite empty dst directory with src directory.  The
sl@0
   407
		 * following call will remove an empty directory.  If it
sl@0
   408
		 * fails, it's because it wasn't empty.
sl@0
   409
		 */
sl@0
   410
sl@0
   411
		if (DoRemoveJustDirectory(nativeDst, 0, NULL) == TCL_OK) {
sl@0
   412
		    /*
sl@0
   413
		     * Now that that empty directory is gone, we can try
sl@0
   414
		     * renaming again.  If that fails, we'll put this empty
sl@0
   415
		     * directory back, for completeness.
sl@0
   416
		     */
sl@0
   417
sl@0
   418
		    if ((*tclWinProcs->moveFileProc)(nativeSrc, nativeDst) != FALSE) {
sl@0
   419
			return TCL_OK;
sl@0
   420
		    }
sl@0
   421
sl@0
   422
		    /*
sl@0
   423
		     * Some new error has occurred.  Don't know what it
sl@0
   424
		     * could be, but report this one.
sl@0
   425
		     */
sl@0
   426
sl@0
   427
		    TclWinConvertError(GetLastError());
sl@0
   428
		    (*tclWinProcs->createDirectoryProc)(nativeDst, NULL);
sl@0
   429
		    (*tclWinProcs->setFileAttributesProc)(nativeDst, dstAttr);
sl@0
   430
		    if (Tcl_GetErrno() == EACCES) {
sl@0
   431
			/*
sl@0
   432
			 * Decode the EACCES to a more meaningful error.
sl@0
   433
			 */
sl@0
   434
sl@0
   435
			goto decode;
sl@0
   436
		    }
sl@0
   437
		}
sl@0
   438
	    } else {	/* (dstAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
sl@0
   439
		Tcl_SetErrno(ENOTDIR);
sl@0
   440
	    }
sl@0
   441
	} else {    /* (srcAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
sl@0
   442
	    if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
sl@0
   443
		Tcl_SetErrno(EISDIR);
sl@0
   444
	    } else {
sl@0
   445
		/*
sl@0
   446
		 * Overwrite existing file by:
sl@0
   447
		 * 
sl@0
   448
		 * 1. Rename existing file to temp name.
sl@0
   449
		 * 2. Rename old file to new name.
sl@0
   450
		 * 3. If success, delete temp file.  If failure,
sl@0
   451
		 *    put temp file back to old name.
sl@0
   452
		 */
sl@0
   453
sl@0
   454
		TCHAR *nativeRest, *nativeTmp, *nativePrefix;
sl@0
   455
		int result, size;
sl@0
   456
		WCHAR tempBuf[MAX_PATH];
sl@0
   457
		
sl@0
   458
		size = (*tclWinProcs->getFullPathNameProc)(nativeDst, MAX_PATH, 
sl@0
   459
			tempBuf, &nativeRest);
sl@0
   460
		if ((size == 0) || (size > MAX_PATH) || (nativeRest == NULL)) {
sl@0
   461
		    return TCL_ERROR;
sl@0
   462
		}
sl@0
   463
		nativeTmp = (TCHAR *) tempBuf;
sl@0
   464
		((char *) nativeRest)[0] = '\0';
sl@0
   465
		((char *) nativeRest)[1] = '\0';    /* In case it's Unicode. */
sl@0
   466
sl@0
   467
		result = TCL_ERROR;
sl@0
   468
		nativePrefix = (tclWinProcs->useWide) 
sl@0
   469
			? (TCHAR *) L"tclr" : (TCHAR *) "tclr";
sl@0
   470
		if ((*tclWinProcs->getTempFileNameProc)(nativeTmp, 
sl@0
   471
			nativePrefix, 0, tempBuf) != 0) {
sl@0
   472
		    /*
sl@0
   473
		     * Strictly speaking, need the following DeleteFile and
sl@0
   474
		     * MoveFile to be joined as an atomic operation so no
sl@0
   475
		     * other app comes along in the meantime and creates the
sl@0
   476
		     * same temp file.
sl@0
   477
		     */
sl@0
   478
		     
sl@0
   479
		    nativeTmp = (TCHAR *) tempBuf;
sl@0
   480
		    (*tclWinProcs->deleteFileProc)(nativeTmp);
sl@0
   481
		    if ((*tclWinProcs->moveFileProc)(nativeDst, nativeTmp) != FALSE) {
sl@0
   482
			if ((*tclWinProcs->moveFileProc)(nativeSrc, nativeDst) != FALSE) {
sl@0
   483
			    (*tclWinProcs->setFileAttributesProc)(nativeTmp, 
sl@0
   484
				    FILE_ATTRIBUTE_NORMAL);
sl@0
   485
			    (*tclWinProcs->deleteFileProc)(nativeTmp);
sl@0
   486
			    return TCL_OK;
sl@0
   487
			} else {
sl@0
   488
			    (*tclWinProcs->deleteFileProc)(nativeDst);
sl@0
   489
			    (*tclWinProcs->moveFileProc)(nativeTmp, nativeDst);
sl@0
   490
			}
sl@0
   491
		    } 
sl@0
   492
sl@0
   493
		    /*
sl@0
   494
		     * Can't backup dst file or move src file.  Return that
sl@0
   495
		     * error.  Could happen if an open file refers to dst.
sl@0
   496
		     */
sl@0
   497
sl@0
   498
		    TclWinConvertError(GetLastError());
sl@0
   499
		    if (Tcl_GetErrno() == EACCES) {
sl@0
   500
			/*
sl@0
   501
			 * Decode the EACCES to a more meaningful error.
sl@0
   502
			 */
sl@0
   503
sl@0
   504
			goto decode;
sl@0
   505
		    }
sl@0
   506
		}
sl@0
   507
		return result;
sl@0
   508
	    }
sl@0
   509
	}
sl@0
   510
    }
sl@0
   511
    return TCL_ERROR;
sl@0
   512
}
sl@0
   513

sl@0
   514
/*
sl@0
   515
 *---------------------------------------------------------------------------
sl@0
   516
 *
sl@0
   517
 * TclpObjCopyFile, DoCopyFile --
sl@0
   518
 *
sl@0
   519
 *      Copy a single file (not a directory).  If dst already exists and
sl@0
   520
 *	is not a directory, it is removed.
sl@0
   521
 *
sl@0
   522
 * Results:
sl@0
   523
 *	If the file was successfully copied, returns TCL_OK.  Otherwise
sl@0
   524
 *	the return value is TCL_ERROR and errno is set to indicate the
sl@0
   525
 *	error.  Some possible values for errno are:
sl@0
   526
 *
sl@0
   527
 *	EACCES:     src or dst parent directory can't be read and/or written.
sl@0
   528
 *	EISDIR:	    src or dst is a directory.
sl@0
   529
 *	ENOENT:	    src doesn't exist.  src or dst is "".
sl@0
   530
 *
sl@0
   531
 *	EACCES:     exists an open file already referring to dst (95).
sl@0
   532
 *	EACCES:	    src specifies a char device (nul:, com1:, etc.) (NT)
sl@0
   533
 *	ENOENT:	    src specifies a char device (nul:, com1:, etc.) (95)
sl@0
   534
 *
sl@0
   535
 * Side effects:
sl@0
   536
 *	It is not an error to copy to a char device.
sl@0
   537
 *
sl@0
   538
 *---------------------------------------------------------------------------
sl@0
   539
 */
sl@0
   540
sl@0
   541
int 
sl@0
   542
TclpObjCopyFile(srcPathPtr, destPathPtr)
sl@0
   543
    Tcl_Obj *srcPathPtr;
sl@0
   544
    Tcl_Obj *destPathPtr;
sl@0
   545
{
sl@0
   546
    return DoCopyFile(Tcl_FSGetNativePath(srcPathPtr),
sl@0
   547
		      Tcl_FSGetNativePath(destPathPtr));
sl@0
   548
}
sl@0
   549
sl@0
   550
static int
sl@0
   551
DoCopyFile(
sl@0
   552
   CONST TCHAR *nativeSrc,	/* Pathname of file to be copied (native). */
sl@0
   553
   CONST TCHAR *nativeDst)	/* Pathname of file to copy to (native). */
sl@0
   554
{
sl@0
   555
#ifdef HAVE_NO_SEH
sl@0
   556
    EXCEPTION_REGISTRATION registration;
sl@0
   557
#endif
sl@0
   558
    int retval = -1;
sl@0
   559
sl@0
   560
    /*
sl@0
   561
     * The CopyFile API acts differently under Win95/98 and NT
sl@0
   562
     * WRT NULL and "". Avoid passing these values.
sl@0
   563
     */
sl@0
   564
sl@0
   565
    if (nativeSrc == NULL || nativeSrc[0] == '\0' ||
sl@0
   566
        nativeDst == NULL || nativeDst[0] == '\0') {
sl@0
   567
	Tcl_SetErrno(ENOENT);
sl@0
   568
	return TCL_ERROR;
sl@0
   569
    }
sl@0
   570
    
sl@0
   571
    /*
sl@0
   572
     * The CopyFile API would throw an exception under NT if one
sl@0
   573
     * of the arguments is a char block device.
sl@0
   574
     */
sl@0
   575
sl@0
   576
#ifndef HAVE_NO_SEH
sl@0
   577
    __try {
sl@0
   578
	if ((*tclWinProcs->copyFileProc)(nativeSrc, nativeDst, 0) != FALSE) {
sl@0
   579
	    retval = TCL_OK;
sl@0
   580
	}
sl@0
   581
    } __except (EXCEPTION_EXECUTE_HANDLER) {}
sl@0
   582
#else
sl@0
   583
sl@0
   584
    /*
sl@0
   585
     * Don't have SEH available, do things the hard way.
sl@0
   586
     * Note that this needs to be one block of asm, to avoid stack
sl@0
   587
     * imbalance; also, it is illegal for one asm block to contain 
sl@0
   588
     * a jump to another.
sl@0
   589
     */
sl@0
   590
sl@0
   591
    __asm__ __volatile__ (
sl@0
   592
sl@0
   593
	/*
sl@0
   594
	 * Pick up parameters before messing with the stack
sl@0
   595
	 */
sl@0
   596
sl@0
   597
	"movl       %[nativeDst],   %%ebx"          "\n\t"
sl@0
   598
        "movl       %[nativeSrc],   %%ecx"          "\n\t"
sl@0
   599
	/*
sl@0
   600
	 * Construct an EXCEPTION_REGISTRATION to protect the
sl@0
   601
	 * call to CopyFile
sl@0
   602
	 */
sl@0
   603
	"leal       %[registration], %%edx"         "\n\t"
sl@0
   604
	"movl       %%fs:0,         %%eax"          "\n\t"
sl@0
   605
	"movl       %%eax,          0x0(%%edx)"     "\n\t" /* link */
sl@0
   606
	"leal       1f,             %%eax"          "\n\t"
sl@0
   607
	"movl       %%eax,          0x4(%%edx)"     "\n\t" /* handler */
sl@0
   608
	"movl       %%ebp,          0x8(%%edx)"     "\n\t" /* ebp */
sl@0
   609
	"movl       %%esp,          0xc(%%edx)"     "\n\t" /* esp */
sl@0
   610
	"movl       $0,             0x10(%%edx)"    "\n\t" /* status */
sl@0
   611
	
sl@0
   612
	/* Link the EXCEPTION_REGISTRATION on the chain */
sl@0
   613
	
sl@0
   614
	"movl       %%edx,          %%fs:0"         "\n\t"
sl@0
   615
	
sl@0
   616
	/* Call CopyFile( nativeSrc, nativeDst, 0 ) */
sl@0
   617
	
sl@0
   618
	"movl	    %[copyFile],    %%eax"	    "\n\t"
sl@0
   619
	"pushl	    $0" 			    "\n\t"
sl@0
   620
	"pushl	    %%ebx"			    "\n\t"
sl@0
   621
	"pushl	    %%ecx"			    "\n\t"
sl@0
   622
	"call	    *%%eax"			    "\n\t"
sl@0
   623
	
sl@0
   624
	/* 
sl@0
   625
	 * Come here on normal exit.  Recover the EXCEPTION_REGISTRATION
sl@0
   626
	 * and put the status return from CopyFile into it.
sl@0
   627
	 */
sl@0
   628
	
sl@0
   629
	"movl	    %%fs:0,	    %%edx"	    "\n\t"
sl@0
   630
	"movl	    %%eax,	    0x10(%%edx)"    "\n\t"
sl@0
   631
	"jmp	    2f"				    "\n"
sl@0
   632
	
sl@0
   633
	/*
sl@0
   634
	 * Come here on an exception.  Recover the EXCEPTION_REGISTRATION
sl@0
   635
	 */
sl@0
   636
	
sl@0
   637
	"1:"					    "\t"
sl@0
   638
	"movl       %%fs:0,         %%edx"          "\n\t"
sl@0
   639
	"movl       0x8(%%edx),     %%edx"          "\n\t"
sl@0
   640
	
sl@0
   641
	/* 
sl@0
   642
	 * Come here however we exited.  Restore context from the
sl@0
   643
	 * EXCEPTION_REGISTRATION in case the stack is unbalanced.
sl@0
   644
	 */
sl@0
   645
	
sl@0
   646
	"2:"                                        "\t"
sl@0
   647
	"movl       0xc(%%edx),     %%esp"          "\n\t"
sl@0
   648
	"movl       0x8(%%edx),     %%ebp"          "\n\t"
sl@0
   649
	"movl       0x0(%%edx),     %%eax"          "\n\t"
sl@0
   650
	"movl       %%eax,          %%fs:0"         "\n\t"
sl@0
   651
	
sl@0
   652
	:
sl@0
   653
	/* No outputs */
sl@0
   654
        :
sl@0
   655
	[registration]  "m"     (registration),
sl@0
   656
	[nativeDst]	"m"     (nativeDst),
sl@0
   657
	[nativeSrc]     "m"     (nativeSrc),
sl@0
   658
	[copyFile]      "r"     (tclWinProcs->copyFileProc)
sl@0
   659
        :
sl@0
   660
	"%eax", "%ebx", "%ecx", "%edx", "memory"
sl@0
   661
        );
sl@0
   662
    if (registration.status != FALSE) {
sl@0
   663
	retval = TCL_OK;
sl@0
   664
    }
sl@0
   665
#endif
sl@0
   666
sl@0
   667
    if (retval != -1)
sl@0
   668
        return retval;
sl@0
   669
sl@0
   670
    TclWinConvertError(GetLastError());
sl@0
   671
    if (Tcl_GetErrno() == EBADF) {
sl@0
   672
	Tcl_SetErrno(EACCES);
sl@0
   673
	return TCL_ERROR;
sl@0
   674
    }
sl@0
   675
    if (Tcl_GetErrno() == EACCES) {
sl@0
   676
	DWORD srcAttr, dstAttr;
sl@0
   677
sl@0
   678
	srcAttr = (*tclWinProcs->getFileAttributesProc)(nativeSrc);
sl@0
   679
	dstAttr = (*tclWinProcs->getFileAttributesProc)(nativeDst);
sl@0
   680
	if (srcAttr != 0xffffffff) {
sl@0
   681
	    if (dstAttr == 0xffffffff) {
sl@0
   682
		dstAttr = 0;
sl@0
   683
	    }
sl@0
   684
	    if ((srcAttr & FILE_ATTRIBUTE_DIRECTORY) ||
sl@0
   685
		    (dstAttr & FILE_ATTRIBUTE_DIRECTORY)) {
sl@0
   686
		if (srcAttr & FILE_ATTRIBUTE_REPARSE_POINT) {
sl@0
   687
		    /* Source is a symbolic link -- copy it */
sl@0
   688
		    if (TclWinSymLinkCopyDirectory(nativeSrc, nativeDst) == 0) {
sl@0
   689
		        return TCL_OK;
sl@0
   690
		    }
sl@0
   691
		}
sl@0
   692
		Tcl_SetErrno(EISDIR);
sl@0
   693
	    }
sl@0
   694
	    if (dstAttr & FILE_ATTRIBUTE_READONLY) {
sl@0
   695
		(*tclWinProcs->setFileAttributesProc)(nativeDst, 
sl@0
   696
			dstAttr & ~((DWORD)FILE_ATTRIBUTE_READONLY));
sl@0
   697
		if ((*tclWinProcs->copyFileProc)(nativeSrc, nativeDst, 0) != FALSE) {
sl@0
   698
		    return TCL_OK;
sl@0
   699
		}
sl@0
   700
		/*
sl@0
   701
		 * Still can't copy onto dst.  Return that error, and
sl@0
   702
		 * restore attributes of dst.
sl@0
   703
		 */
sl@0
   704
sl@0
   705
		TclWinConvertError(GetLastError());
sl@0
   706
		(*tclWinProcs->setFileAttributesProc)(nativeDst, dstAttr);
sl@0
   707
	    }
sl@0
   708
	}
sl@0
   709
    }
sl@0
   710
    return TCL_ERROR;
sl@0
   711
}
sl@0
   712

sl@0
   713
/*
sl@0
   714
 *---------------------------------------------------------------------------
sl@0
   715
 *
sl@0
   716
 * TclpObjDeleteFile, TclpDeleteFile --
sl@0
   717
 *
sl@0
   718
 *      Removes a single file (not a directory).
sl@0
   719
 *
sl@0
   720
 * Results:
sl@0
   721
 *	If the file was successfully deleted, returns TCL_OK.  Otherwise
sl@0
   722
 *	the return value is TCL_ERROR and errno is set to indicate the
sl@0
   723
 *	error.  Some possible values for errno are:
sl@0
   724
 *
sl@0
   725
 *	EACCES:     a parent directory can't be read and/or written.
sl@0
   726
 *	EISDIR:	    path is a directory.
sl@0
   727
 *	ENOENT:	    path doesn't exist or is "".
sl@0
   728
 *
sl@0
   729
 *	EACCES:     exists an open file already referring to path.
sl@0
   730
 *	EACCES:	    path is a char device (nul:, com1:, etc.)
sl@0
   731
 *
sl@0
   732
 * Side effects:
sl@0
   733
 *      The file is deleted, even if it is read-only.
sl@0
   734
 *
sl@0
   735
 *---------------------------------------------------------------------------
sl@0
   736
 */
sl@0
   737
sl@0
   738
int 
sl@0
   739
TclpObjDeleteFile(pathPtr)
sl@0
   740
    Tcl_Obj *pathPtr;
sl@0
   741
{
sl@0
   742
    return TclpDeleteFile(Tcl_FSGetNativePath(pathPtr));
sl@0
   743
}
sl@0
   744
sl@0
   745
int
sl@0
   746
TclpDeleteFile(
sl@0
   747
    CONST TCHAR *nativePath)	/* Pathname of file to be removed (native). */
sl@0
   748
{
sl@0
   749
    DWORD attr;
sl@0
   750
sl@0
   751
    /*
sl@0
   752
     * The DeleteFile API acts differently under Win95/98 and NT
sl@0
   753
     * WRT NULL and "". Avoid passing these values.
sl@0
   754
     */
sl@0
   755
sl@0
   756
    if (nativePath == NULL || nativePath[0] == '\0') {
sl@0
   757
	Tcl_SetErrno(ENOENT);
sl@0
   758
	return TCL_ERROR;
sl@0
   759
    }
sl@0
   760
sl@0
   761
    if ((*tclWinProcs->deleteFileProc)(nativePath) != FALSE) {
sl@0
   762
	return TCL_OK;
sl@0
   763
    }
sl@0
   764
    TclWinConvertError(GetLastError());
sl@0
   765
sl@0
   766
    if (Tcl_GetErrno() == EACCES) {
sl@0
   767
        attr = (*tclWinProcs->getFileAttributesProc)(nativePath);
sl@0
   768
	if (attr != 0xffffffff) {
sl@0
   769
	    if (attr & FILE_ATTRIBUTE_DIRECTORY) {
sl@0
   770
		if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
sl@0
   771
		    /* It is a symbolic link -- remove it */
sl@0
   772
		    if (TclWinSymLinkDelete(nativePath, 0) == 0) {
sl@0
   773
		        return TCL_OK;
sl@0
   774
		    }
sl@0
   775
		}
sl@0
   776
		
sl@0
   777
		/* 
sl@0
   778
		 * If we fall through here, it is a directory.
sl@0
   779
		 * 
sl@0
   780
		 * Windows NT reports removing a directory as EACCES instead
sl@0
   781
		 * of EISDIR.
sl@0
   782
		 */
sl@0
   783
sl@0
   784
		Tcl_SetErrno(EISDIR);
sl@0
   785
	    } else if (attr & FILE_ATTRIBUTE_READONLY) {
sl@0
   786
		int res = (*tclWinProcs->setFileAttributesProc)(nativePath, 
sl@0
   787
			attr & ~((DWORD)FILE_ATTRIBUTE_READONLY));
sl@0
   788
		if ((res != 0) && ((*tclWinProcs->deleteFileProc)(nativePath)
sl@0
   789
			!= FALSE)) {
sl@0
   790
		    return TCL_OK;
sl@0
   791
		}
sl@0
   792
		TclWinConvertError(GetLastError());
sl@0
   793
		if (res != 0) {
sl@0
   794
		    (*tclWinProcs->setFileAttributesProc)(nativePath, attr);
sl@0
   795
		}
sl@0
   796
	    }
sl@0
   797
	}
sl@0
   798
    } else if (Tcl_GetErrno() == ENOENT) {
sl@0
   799
        attr = (*tclWinProcs->getFileAttributesProc)(nativePath);
sl@0
   800
	if (attr != 0xffffffff) {
sl@0
   801
	    if (attr & FILE_ATTRIBUTE_DIRECTORY) {
sl@0
   802
	    	/*
sl@0
   803
		 * Windows 95 reports removing a directory as ENOENT instead 
sl@0
   804
		 * of EISDIR. 
sl@0
   805
		 */
sl@0
   806
sl@0
   807
		Tcl_SetErrno(EISDIR);
sl@0
   808
	    }
sl@0
   809
	}
sl@0
   810
    } else if (Tcl_GetErrno() == EINVAL) {
sl@0
   811
	/*
sl@0
   812
	 * Windows NT reports removing a char device as EINVAL instead of
sl@0
   813
	 * EACCES.
sl@0
   814
	 */
sl@0
   815
sl@0
   816
	Tcl_SetErrno(EACCES);
sl@0
   817
    }
sl@0
   818
sl@0
   819
    return TCL_ERROR;
sl@0
   820
}
sl@0
   821

sl@0
   822
/*
sl@0
   823
 *---------------------------------------------------------------------------
sl@0
   824
 *
sl@0
   825
 * TclpObjCreateDirectory --
sl@0
   826
 *
sl@0
   827
 *      Creates the specified directory.  All parent directories of the
sl@0
   828
 *	specified directory must already exist.  The directory is
sl@0
   829
 *	automatically created with permissions so that user can access
sl@0
   830
 *	the new directory and create new files or subdirectories in it.
sl@0
   831
 *
sl@0
   832
 * Results:
sl@0
   833
 *	If the directory was successfully created, returns TCL_OK.
sl@0
   834
 *	Otherwise the return value is TCL_ERROR and errno is set to
sl@0
   835
 *	indicate the error.  Some possible values for errno are:
sl@0
   836
 *
sl@0
   837
 *	EACCES:     a parent directory can't be read and/or written.
sl@0
   838
 *	EEXIST:	    path already exists.
sl@0
   839
 *	ENOENT:	    a parent directory doesn't exist.
sl@0
   840
 *
sl@0
   841
 * Side effects:
sl@0
   842
 *      A directory is created.
sl@0
   843
 *
sl@0
   844
 *---------------------------------------------------------------------------
sl@0
   845
 */
sl@0
   846
sl@0
   847
int 
sl@0
   848
TclpObjCreateDirectory(pathPtr)
sl@0
   849
    Tcl_Obj *pathPtr;
sl@0
   850
{
sl@0
   851
    return DoCreateDirectory(Tcl_FSGetNativePath(pathPtr));
sl@0
   852
}
sl@0
   853
sl@0
   854
static int
sl@0
   855
DoCreateDirectory(
sl@0
   856
    CONST TCHAR *nativePath)	/* Pathname of directory to create (native). */
sl@0
   857
{
sl@0
   858
    DWORD error;
sl@0
   859
    if ((*tclWinProcs->createDirectoryProc)(nativePath, NULL) == 0) {
sl@0
   860
	error = GetLastError();
sl@0
   861
	TclWinConvertError(error);
sl@0
   862
	return TCL_ERROR;
sl@0
   863
    }   
sl@0
   864
    return TCL_OK;
sl@0
   865
}
sl@0
   866

sl@0
   867
/*
sl@0
   868
 *---------------------------------------------------------------------------
sl@0
   869
 *
sl@0
   870
 * TclpObjCopyDirectory --
sl@0
   871
 *
sl@0
   872
 *      Recursively copies a directory.  The target directory dst must
sl@0
   873
 *	not already exist.  Note that this function does not merge two
sl@0
   874
 *	directory hierarchies, even if the target directory is an an
sl@0
   875
 *	empty directory.
sl@0
   876
 *
sl@0
   877
 * Results:
sl@0
   878
 *	If the directory was successfully copied, returns TCL_OK.
sl@0
   879
 *	Otherwise the return value is TCL_ERROR, errno is set to indicate
sl@0
   880
 *	the error, and the pathname of the file that caused the error
sl@0
   881
 *	is stored in errorPtr.  See TclpCreateDirectory and TclpCopyFile
sl@0
   882
 *	for a description of possible values for errno.
sl@0
   883
 *
sl@0
   884
 * Side effects:
sl@0
   885
 *      An exact copy of the directory hierarchy src will be created
sl@0
   886
 *	with the name dst.  If an error occurs, the error will
sl@0
   887
 *      be returned immediately, and remaining files will not be
sl@0
   888
 *	processed.
sl@0
   889
 *
sl@0
   890
 *---------------------------------------------------------------------------
sl@0
   891
 */
sl@0
   892
sl@0
   893
int 
sl@0
   894
TclpObjCopyDirectory(srcPathPtr, destPathPtr, errorPtr)
sl@0
   895
    Tcl_Obj *srcPathPtr;
sl@0
   896
    Tcl_Obj *destPathPtr;
sl@0
   897
    Tcl_Obj **errorPtr;
sl@0
   898
{
sl@0
   899
    Tcl_DString ds;
sl@0
   900
    Tcl_DString srcString, dstString;
sl@0
   901
    Tcl_Obj *normSrcPtr, *normDestPtr;
sl@0
   902
    int ret;
sl@0
   903
sl@0
   904
    normSrcPtr = Tcl_FSGetNormalizedPath(NULL,srcPathPtr);
sl@0
   905
    if (normSrcPtr == NULL) {
sl@0
   906
	return TCL_ERROR;
sl@0
   907
    }
sl@0
   908
    Tcl_WinUtfToTChar(Tcl_GetString(normSrcPtr), -1, &srcString);
sl@0
   909
    normDestPtr = Tcl_FSGetNormalizedPath(NULL,destPathPtr);
sl@0
   910
    if (normDestPtr == NULL) {
sl@0
   911
	return TCL_ERROR;
sl@0
   912
    }
sl@0
   913
    Tcl_WinUtfToTChar(Tcl_GetString(normDestPtr), -1, &dstString);
sl@0
   914
sl@0
   915
    ret = TraverseWinTree(TraversalCopy, &srcString, &dstString, &ds);
sl@0
   916
sl@0
   917
    Tcl_DStringFree(&srcString);
sl@0
   918
    Tcl_DStringFree(&dstString);
sl@0
   919
sl@0
   920
    if (ret != TCL_OK) {
sl@0
   921
	if (!strcmp(Tcl_DStringValue(&ds), Tcl_GetString(normSrcPtr))) {
sl@0
   922
	    *errorPtr = srcPathPtr;
sl@0
   923
	} else if (!strcmp(Tcl_DStringValue(&ds), Tcl_GetString(normDestPtr))) {
sl@0
   924
	    *errorPtr = destPathPtr;
sl@0
   925
	} else {
sl@0
   926
	    *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
sl@0
   927
	}
sl@0
   928
	Tcl_DStringFree(&ds);
sl@0
   929
	Tcl_IncrRefCount(*errorPtr);
sl@0
   930
    }
sl@0
   931
    return ret;
sl@0
   932
}
sl@0
   933

sl@0
   934
/*
sl@0
   935
 *----------------------------------------------------------------------
sl@0
   936
 *
sl@0
   937
 * TclpObjRemoveDirectory, DoRemoveDirectory -- 
sl@0
   938
 *
sl@0
   939
 *	Removes directory (and its contents, if the recursive flag is set).
sl@0
   940
 *
sl@0
   941
 * Results:
sl@0
   942
 *	If the directory was successfully removed, returns TCL_OK.
sl@0
   943
 *	Otherwise the return value is TCL_ERROR, errno is set to indicate
sl@0
   944
 *	the error, and the pathname of the file that caused the error
sl@0
   945
 *	is stored in errorPtr.  Some possible values for errno are:
sl@0
   946
 *
sl@0
   947
 *	EACCES:     path directory can't be read and/or written.
sl@0
   948
 *	EEXIST:	    path is a non-empty directory.
sl@0
   949
 *	EINVAL:	    path is root directory or current directory.
sl@0
   950
 *	ENOENT:	    path doesn't exist or is "".
sl@0
   951
 * 	ENOTDIR:    path is not a directory.
sl@0
   952
 *
sl@0
   953
 *	EACCES:	    path is a char device (nul:, com1:, etc.) (95)
sl@0
   954
 *	EINVAL:	    path is a char device (nul:, com1:, etc.) (NT)
sl@0
   955
 *
sl@0
   956
 * Side effects:
sl@0
   957
 *	Directory removed.  If an error occurs, the error will be returned
sl@0
   958
 *	immediately, and remaining files will not be deleted.
sl@0
   959
 *
sl@0
   960
 *----------------------------------------------------------------------
sl@0
   961
 */
sl@0
   962
sl@0
   963
int 
sl@0
   964
TclpObjRemoveDirectory(pathPtr, recursive, errorPtr)
sl@0
   965
    Tcl_Obj *pathPtr;
sl@0
   966
    int recursive;
sl@0
   967
    Tcl_Obj **errorPtr;
sl@0
   968
{
sl@0
   969
    Tcl_DString ds;
sl@0
   970
    Tcl_Obj *normPtr = NULL;
sl@0
   971
    int ret;
sl@0
   972
    if (recursive) {
sl@0
   973
	/* 
sl@0
   974
	 * In the recursive case, the string rep is used to construct a
sl@0
   975
	 * Tcl_DString which may be used extensively, so we can't
sl@0
   976
	 * optimize this case easily.
sl@0
   977
	 */
sl@0
   978
	Tcl_DString native;
sl@0
   979
	normPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
sl@0
   980
	if (normPtr == NULL) {
sl@0
   981
	    return TCL_ERROR;
sl@0
   982
	}
sl@0
   983
	Tcl_WinUtfToTChar(Tcl_GetString(normPtr), -1, &native);
sl@0
   984
	ret = DoRemoveDirectory(&native, recursive, &ds);
sl@0
   985
	Tcl_DStringFree(&native);
sl@0
   986
    } else {
sl@0
   987
	ret = DoRemoveJustDirectory(Tcl_FSGetNativePath(pathPtr), 
sl@0
   988
				    0, &ds);
sl@0
   989
    }
sl@0
   990
    if (ret != TCL_OK) {
sl@0
   991
	int len = Tcl_DStringLength(&ds);
sl@0
   992
	if (len > 0) {
sl@0
   993
	    if (normPtr != NULL 
sl@0
   994
	      && !strcmp(Tcl_DStringValue(&ds), Tcl_GetString(normPtr))) {
sl@0
   995
		*errorPtr = pathPtr;
sl@0
   996
	    } else {
sl@0
   997
		*errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
sl@0
   998
	    }
sl@0
   999
	    Tcl_IncrRefCount(*errorPtr);
sl@0
  1000
	}
sl@0
  1001
	Tcl_DStringFree(&ds);
sl@0
  1002
    }
sl@0
  1003
    return ret;
sl@0
  1004
}
sl@0
  1005
sl@0
  1006
static int
sl@0
  1007
DoRemoveJustDirectory(
sl@0
  1008
    CONST TCHAR *nativePath,	/* Pathname of directory to be removed
sl@0
  1009
				 * (native). */
sl@0
  1010
    int ignoreError,		/* If non-zero, don't initialize the
sl@0
  1011
                  		 * errorPtr under some circumstances
sl@0
  1012
                  		 * on return. */
sl@0
  1013
    Tcl_DString *errorPtr)	/* If non-NULL, uninitialized or free
sl@0
  1014
				 * DString filled with UTF-8 name of file
sl@0
  1015
				 * causing error. */
sl@0
  1016
{
sl@0
  1017
    /*
sl@0
  1018
     * The RemoveDirectory API acts differently under Win95/98 and NT
sl@0
  1019
     * WRT NULL and "". Avoid passing these values.
sl@0
  1020
     */
sl@0
  1021
sl@0
  1022
    if (nativePath == NULL || nativePath[0] == '\0') {
sl@0
  1023
	Tcl_SetErrno(ENOENT);
sl@0
  1024
	goto end;
sl@0
  1025
    }
sl@0
  1026
sl@0
  1027
    if ((*tclWinProcs->removeDirectoryProc)(nativePath) != FALSE) {
sl@0
  1028
	return TCL_OK;
sl@0
  1029
    }
sl@0
  1030
    TclWinConvertError(GetLastError());
sl@0
  1031
sl@0
  1032
    if (Tcl_GetErrno() == EACCES) {
sl@0
  1033
	DWORD attr = (*tclWinProcs->getFileAttributesProc)(nativePath);
sl@0
  1034
	if (attr != 0xffffffff) {
sl@0
  1035
	    if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
sl@0
  1036
		/* 
sl@0
  1037
		 * Windows 95 reports calling RemoveDirectory on a file as an 
sl@0
  1038
		 * EACCES, not an ENOTDIR.
sl@0
  1039
		 */
sl@0
  1040
		
sl@0
  1041
		Tcl_SetErrno(ENOTDIR);
sl@0
  1042
		goto end;
sl@0
  1043
	    }
sl@0
  1044
sl@0
  1045
	    if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
sl@0
  1046
		/* It is a symbolic link -- remove it */
sl@0
  1047
		if (TclWinSymLinkDelete(nativePath, 1) != 0) {
sl@0
  1048
		    goto end;
sl@0
  1049
		}
sl@0
  1050
	    }
sl@0
  1051
	    
sl@0
  1052
	    if (attr & FILE_ATTRIBUTE_READONLY) {
sl@0
  1053
		attr &= ~FILE_ATTRIBUTE_READONLY;
sl@0
  1054
		if ((*tclWinProcs->setFileAttributesProc)(nativePath, attr) == FALSE) {
sl@0
  1055
		    goto end;
sl@0
  1056
		}
sl@0
  1057
		if ((*tclWinProcs->removeDirectoryProc)(nativePath) != FALSE) {
sl@0
  1058
		    return TCL_OK;
sl@0
  1059
		}
sl@0
  1060
		TclWinConvertError(GetLastError());
sl@0
  1061
		(*tclWinProcs->setFileAttributesProc)(nativePath, 
sl@0
  1062
			attr | FILE_ATTRIBUTE_READONLY);
sl@0
  1063
	    }
sl@0
  1064
sl@0
  1065
	    /* 
sl@0
  1066
	     * Windows 95 and Win32s report removing a non-empty directory 
sl@0
  1067
	     * as EACCES, not EEXIST.  If the directory is not empty,
sl@0
  1068
	     * change errno so caller knows what's going on.
sl@0
  1069
	     */
sl@0
  1070
sl@0
  1071
	    if (TclWinGetPlatformId() != VER_PLATFORM_WIN32_NT) {
sl@0
  1072
		CONST char *path, *find;
sl@0
  1073
		HANDLE handle;
sl@0
  1074
		WIN32_FIND_DATAA data;
sl@0
  1075
		Tcl_DString buffer;
sl@0
  1076
		int len;
sl@0
  1077
sl@0
  1078
		path = (CONST char *) nativePath;
sl@0
  1079
sl@0
  1080
		Tcl_DStringInit(&buffer);
sl@0
  1081
		len = strlen(path);
sl@0
  1082
		find = Tcl_DStringAppend(&buffer, path, len);
sl@0
  1083
		if ((len > 0) && (find[len - 1] != '\\')) {
sl@0
  1084
		    Tcl_DStringAppend(&buffer, "\\", 1);
sl@0
  1085
		}
sl@0
  1086
		find = Tcl_DStringAppend(&buffer, "*.*", 3);
sl@0
  1087
		handle = FindFirstFileA(find, &data);
sl@0
  1088
		if (handle != INVALID_HANDLE_VALUE) {
sl@0
  1089
		    while (1) {
sl@0
  1090
			if ((strcmp(data.cFileName, ".") != 0)
sl@0
  1091
				&& (strcmp(data.cFileName, "..") != 0)) {
sl@0
  1092
			    /*
sl@0
  1093
			     * Found something in this directory.
sl@0
  1094
			     */
sl@0
  1095
sl@0
  1096
			    Tcl_SetErrno(EEXIST);
sl@0
  1097
			    break;
sl@0
  1098
			}
sl@0
  1099
			if (FindNextFileA(handle, &data) == FALSE) {
sl@0
  1100
			    break;
sl@0
  1101
			}
sl@0
  1102
		    }
sl@0
  1103
		    FindClose(handle);
sl@0
  1104
		}
sl@0
  1105
		Tcl_DStringFree(&buffer);
sl@0
  1106
	    }
sl@0
  1107
	}
sl@0
  1108
    }
sl@0
  1109
    if (Tcl_GetErrno() == ENOTEMPTY) {
sl@0
  1110
	/* 
sl@0
  1111
	 * The caller depends on EEXIST to signify that the directory is
sl@0
  1112
	 * not empty, not ENOTEMPTY. 
sl@0
  1113
	 */
sl@0
  1114
sl@0
  1115
	Tcl_SetErrno(EEXIST);
sl@0
  1116
    }
sl@0
  1117
    if ((ignoreError != 0) && (Tcl_GetErrno() == EEXIST)) {
sl@0
  1118
	/* 
sl@0
  1119
	 * If we're being recursive, this error may actually
sl@0
  1120
	 * be ok, so we don't want to initialise the errorPtr
sl@0
  1121
	 * yet.
sl@0
  1122
	 */
sl@0
  1123
	return TCL_ERROR;
sl@0
  1124
    }
sl@0
  1125
sl@0
  1126
    end:
sl@0
  1127
    if (errorPtr != NULL) {
sl@0
  1128
	Tcl_WinTCharToUtf(nativePath, -1, errorPtr);
sl@0
  1129
    }
sl@0
  1130
    return TCL_ERROR;
sl@0
  1131
sl@0
  1132
}
sl@0
  1133
sl@0
  1134
static int
sl@0
  1135
DoRemoveDirectory(
sl@0
  1136
    Tcl_DString *pathPtr,	/* Pathname of directory to be removed
sl@0
  1137
				 * (native). */
sl@0
  1138
    int recursive,		/* If non-zero, removes directories that
sl@0
  1139
				 * are nonempty.  Otherwise, will only remove
sl@0
  1140
				 * empty directories. */
sl@0
  1141
    Tcl_DString *errorPtr)	/* If non-NULL, uninitialized or free
sl@0
  1142
				 * DString filled with UTF-8 name of file
sl@0
  1143
				 * causing error. */
sl@0
  1144
{
sl@0
  1145
    int res = DoRemoveJustDirectory(Tcl_DStringValue(pathPtr), recursive, 
sl@0
  1146
				    errorPtr);
sl@0
  1147
    
sl@0
  1148
    if ((res == TCL_ERROR) && (recursive != 0) && (Tcl_GetErrno() == EEXIST)) {
sl@0
  1149
	/*
sl@0
  1150
	 * The directory is nonempty, but the recursive flag has been
sl@0
  1151
	 * specified, so we recursively remove all the files in the directory.
sl@0
  1152
	 */
sl@0
  1153
	return TraverseWinTree(TraversalDelete, pathPtr, NULL, errorPtr);
sl@0
  1154
    } else {
sl@0
  1155
	return res;
sl@0
  1156
    }
sl@0
  1157
}
sl@0
  1158

sl@0
  1159
/*
sl@0
  1160
 *---------------------------------------------------------------------------
sl@0
  1161
 *
sl@0
  1162
 * TraverseWinTree --
sl@0
  1163
 *
sl@0
  1164
 *      Traverse directory tree specified by sourcePtr, calling the function 
sl@0
  1165
 *	traverseProc for each file and directory encountered.  If destPtr 
sl@0
  1166
 *	is non-null, each of name in the sourcePtr directory is appended to 
sl@0
  1167
 *	the directory specified by destPtr and passed as the second argument 
sl@0
  1168
 *	to traverseProc() .
sl@0
  1169
 *
sl@0
  1170
 * Results:
sl@0
  1171
 *      Standard Tcl result.
sl@0
  1172
 *
sl@0
  1173
 * Side effects:
sl@0
  1174
 *      None caused by TraverseWinTree, however the user specified 
sl@0
  1175
 *	traverseProc() may change state.  If an error occurs, the error will
sl@0
  1176
 *      be returned immediately, and remaining files will not be processed.
sl@0
  1177
 *
sl@0
  1178
 *---------------------------------------------------------------------------
sl@0
  1179
 */
sl@0
  1180
sl@0
  1181
static int 
sl@0
  1182
TraverseWinTree(
sl@0
  1183
    TraversalProc *traverseProc,/* Function to call for every file and
sl@0
  1184
				 * directory in source hierarchy. */
sl@0
  1185
    Tcl_DString *sourcePtr,	/* Pathname of source directory to be
sl@0
  1186
				 * traversed (native). */
sl@0
  1187
    Tcl_DString *targetPtr,	/* Pathname of directory to traverse in
sl@0
  1188
				 * parallel with source directory (native),
sl@0
  1189
				 * may be NULL. */
sl@0
  1190
    Tcl_DString *errorPtr)	/* If non-NULL, uninitialized or free
sl@0
  1191
				 * DString filled with UTF-8 name of file
sl@0
  1192
				 * causing error. */
sl@0
  1193
{
sl@0
  1194
    DWORD sourceAttr;
sl@0
  1195
    TCHAR *nativeSource, *nativeTarget, *nativeErrfile;
sl@0
  1196
    int result, found, sourceLen, targetLen, oldSourceLen, oldTargetLen;
sl@0
  1197
    HANDLE handle;
sl@0
  1198
    WIN32_FIND_DATAT data;
sl@0
  1199
sl@0
  1200
    nativeErrfile = NULL;
sl@0
  1201
    result = TCL_OK;
sl@0
  1202
    oldTargetLen = 0;		/* lint. */
sl@0
  1203
sl@0
  1204
    nativeSource = (TCHAR *) Tcl_DStringValue(sourcePtr);
sl@0
  1205
    nativeTarget = (TCHAR *) (targetPtr == NULL 
sl@0
  1206
			      ? NULL : Tcl_DStringValue(targetPtr));
sl@0
  1207
    
sl@0
  1208
    oldSourceLen = Tcl_DStringLength(sourcePtr);
sl@0
  1209
    sourceAttr = (*tclWinProcs->getFileAttributesProc)(nativeSource);
sl@0
  1210
    if (sourceAttr == 0xffffffff) {
sl@0
  1211
	nativeErrfile = nativeSource;
sl@0
  1212
	goto end;
sl@0
  1213
    }
sl@0
  1214
    if ((sourceAttr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
sl@0
  1215
	/*
sl@0
  1216
	 * Process the regular file
sl@0
  1217
	 */
sl@0
  1218
sl@0
  1219
	return (*traverseProc)(nativeSource, nativeTarget, DOTREE_F, errorPtr);
sl@0
  1220
    }
sl@0
  1221
sl@0
  1222
    if (tclWinProcs->useWide) {
sl@0
  1223
	Tcl_DStringAppend(sourcePtr, (char *) L"\\*.*", 4 * sizeof(WCHAR) + 1);
sl@0
  1224
	Tcl_DStringSetLength(sourcePtr, Tcl_DStringLength(sourcePtr) - 1);
sl@0
  1225
    } else {
sl@0
  1226
	Tcl_DStringAppend(sourcePtr, "\\*.*", 4);
sl@0
  1227
    }
sl@0
  1228
    nativeSource = (TCHAR *) Tcl_DStringValue(sourcePtr);
sl@0
  1229
    handle = (*tclWinProcs->findFirstFileProc)(nativeSource, &data);
sl@0
  1230
    if (handle == INVALID_HANDLE_VALUE) {      
sl@0
  1231
	/* 
sl@0
  1232
	 * Can't read directory
sl@0
  1233
	 */
sl@0
  1234
sl@0
  1235
	TclWinConvertError(GetLastError());
sl@0
  1236
	nativeErrfile = nativeSource;
sl@0
  1237
	goto end;
sl@0
  1238
    }
sl@0
  1239
sl@0
  1240
    nativeSource[oldSourceLen + 1] = '\0';
sl@0
  1241
    Tcl_DStringSetLength(sourcePtr, oldSourceLen);
sl@0
  1242
    result = (*traverseProc)(nativeSource, nativeTarget, DOTREE_PRED, errorPtr);
sl@0
  1243
    if (result != TCL_OK) {
sl@0
  1244
	FindClose(handle);
sl@0
  1245
	return result;
sl@0
  1246
    }
sl@0
  1247
sl@0
  1248
    sourceLen = oldSourceLen;
sl@0
  1249
sl@0
  1250
    if (tclWinProcs->useWide) {
sl@0
  1251
	sourceLen += sizeof(WCHAR);
sl@0
  1252
	Tcl_DStringAppend(sourcePtr, (char *) L"\\", sizeof(WCHAR) + 1);
sl@0
  1253
	Tcl_DStringSetLength(sourcePtr, sourceLen);
sl@0
  1254
    } else {
sl@0
  1255
	sourceLen += 1;
sl@0
  1256
	Tcl_DStringAppend(sourcePtr, "\\", 1);
sl@0
  1257
    }
sl@0
  1258
    if (targetPtr != NULL) {
sl@0
  1259
	oldTargetLen = Tcl_DStringLength(targetPtr);
sl@0
  1260
sl@0
  1261
	targetLen = oldTargetLen;
sl@0
  1262
	if (tclWinProcs->useWide) {
sl@0
  1263
	    targetLen += sizeof(WCHAR);
sl@0
  1264
	    Tcl_DStringAppend(targetPtr, (char *) L"\\", sizeof(WCHAR) + 1);
sl@0
  1265
	    Tcl_DStringSetLength(targetPtr, targetLen);
sl@0
  1266
	} else {
sl@0
  1267
	    targetLen += 1;
sl@0
  1268
	    Tcl_DStringAppend(targetPtr, "\\", 1);
sl@0
  1269
	}
sl@0
  1270
    }
sl@0
  1271
sl@0
  1272
    found = 1;
sl@0
  1273
    for ( ; found; found = (*tclWinProcs->findNextFileProc)(handle, &data)) {
sl@0
  1274
	TCHAR *nativeName;
sl@0
  1275
	int len;
sl@0
  1276
sl@0
  1277
	if (tclWinProcs->useWide) {
sl@0
  1278
	    WCHAR *wp;
sl@0
  1279
sl@0
  1280
	    wp = data.w.cFileName;
sl@0
  1281
	    if (*wp == '.') {
sl@0
  1282
		wp++;
sl@0
  1283
		if (*wp == '.') {
sl@0
  1284
		    wp++;
sl@0
  1285
		}
sl@0
  1286
		if (*wp == '\0') {
sl@0
  1287
		    continue;
sl@0
  1288
		}
sl@0
  1289
	    }
sl@0
  1290
	    nativeName = (TCHAR *) data.w.cFileName;
sl@0
  1291
	    len = wcslen(data.w.cFileName) * sizeof(WCHAR);
sl@0
  1292
	} else {
sl@0
  1293
	    if ((strcmp(data.a.cFileName, ".") == 0) 
sl@0
  1294
		    || (strcmp(data.a.cFileName, "..") == 0)) {
sl@0
  1295
		continue;
sl@0
  1296
	    }
sl@0
  1297
	    nativeName = (TCHAR *) data.a.cFileName;
sl@0
  1298
	    len = strlen(data.a.cFileName);
sl@0
  1299
	}
sl@0
  1300
sl@0
  1301
	/* 
sl@0
  1302
	 * Append name after slash, and recurse on the file. 
sl@0
  1303
	 */
sl@0
  1304
sl@0
  1305
	Tcl_DStringAppend(sourcePtr, (char *) nativeName, len + 1);
sl@0
  1306
	Tcl_DStringSetLength(sourcePtr, Tcl_DStringLength(sourcePtr) - 1);
sl@0
  1307
	if (targetPtr != NULL) {
sl@0
  1308
	    Tcl_DStringAppend(targetPtr, (char *) nativeName, len + 1);
sl@0
  1309
	    Tcl_DStringSetLength(targetPtr, Tcl_DStringLength(targetPtr) - 1);
sl@0
  1310
	}
sl@0
  1311
	result = TraverseWinTree(traverseProc, sourcePtr, targetPtr, 
sl@0
  1312
		errorPtr);
sl@0
  1313
	if (result != TCL_OK) {
sl@0
  1314
	    break;
sl@0
  1315
	}
sl@0
  1316
sl@0
  1317
	/*
sl@0
  1318
	 * Remove name after slash.
sl@0
  1319
	 */
sl@0
  1320
sl@0
  1321
	Tcl_DStringSetLength(sourcePtr, sourceLen);
sl@0
  1322
	if (targetPtr != NULL) {
sl@0
  1323
	    Tcl_DStringSetLength(targetPtr, targetLen);
sl@0
  1324
	}
sl@0
  1325
    }
sl@0
  1326
    FindClose(handle);
sl@0
  1327
sl@0
  1328
    /*
sl@0
  1329
     * Strip off the trailing slash we added
sl@0
  1330
     */
sl@0
  1331
sl@0
  1332
    Tcl_DStringSetLength(sourcePtr, oldSourceLen + 1);
sl@0
  1333
    Tcl_DStringSetLength(sourcePtr, oldSourceLen);
sl@0
  1334
    if (targetPtr != NULL) {
sl@0
  1335
	Tcl_DStringSetLength(targetPtr, oldTargetLen + 1);
sl@0
  1336
	Tcl_DStringSetLength(targetPtr, oldTargetLen);
sl@0
  1337
    }
sl@0
  1338
    if (result == TCL_OK) {
sl@0
  1339
	/*
sl@0
  1340
	 * Call traverseProc() on a directory after visiting all the
sl@0
  1341
	 * files in that directory.
sl@0
  1342
	 */
sl@0
  1343
sl@0
  1344
	result = (*traverseProc)(Tcl_DStringValue(sourcePtr), 
sl@0
  1345
			(targetPtr == NULL ? NULL : Tcl_DStringValue(targetPtr)), 
sl@0
  1346
			DOTREE_POSTD, errorPtr);
sl@0
  1347
    }
sl@0
  1348
    end:
sl@0
  1349
    if (nativeErrfile != NULL) {
sl@0
  1350
	TclWinConvertError(GetLastError());
sl@0
  1351
	if (errorPtr != NULL) {
sl@0
  1352
	    Tcl_WinTCharToUtf(nativeErrfile, -1, errorPtr);
sl@0
  1353
	}
sl@0
  1354
	result = TCL_ERROR;
sl@0
  1355
    }
sl@0
  1356
sl@0
  1357
    return result;
sl@0
  1358
}
sl@0
  1359

sl@0
  1360
/*
sl@0
  1361
 *----------------------------------------------------------------------
sl@0
  1362
 *
sl@0
  1363
 * TraversalCopy
sl@0
  1364
 *
sl@0
  1365
 *      Called from TraverseUnixTree in order to execute a recursive
sl@0
  1366
 *      copy of a directory.
sl@0
  1367
 *
sl@0
  1368
 * Results:
sl@0
  1369
 *      Standard Tcl result.
sl@0
  1370
 *
sl@0
  1371
 * Side effects:
sl@0
  1372
 *      Depending on the value of type, src may be copied to dst.
sl@0
  1373
 *      
sl@0
  1374
 *----------------------------------------------------------------------
sl@0
  1375
 */
sl@0
  1376
sl@0
  1377
static int 
sl@0
  1378
TraversalCopy(
sl@0
  1379
    CONST TCHAR *nativeSrc,	/* Source pathname to copy. */
sl@0
  1380
    CONST TCHAR *nativeDst,	/* Destination pathname of copy. */
sl@0
  1381
    int type,			/* Reason for call - see TraverseWinTree() */
sl@0
  1382
    Tcl_DString *errorPtr)	/* If non-NULL, initialized DString filled
sl@0
  1383
				 * with UTF-8 name of file causing error. */
sl@0
  1384
{
sl@0
  1385
    switch (type) {
sl@0
  1386
	case DOTREE_F: {
sl@0
  1387
	    if (DoCopyFile(nativeSrc, nativeDst) == TCL_OK) {
sl@0
  1388
		return TCL_OK;
sl@0
  1389
	    }
sl@0
  1390
	    break;
sl@0
  1391
	}
sl@0
  1392
	case DOTREE_PRED: {
sl@0
  1393
	    if (DoCreateDirectory(nativeDst) == TCL_OK) {
sl@0
  1394
		DWORD attr = (*tclWinProcs->getFileAttributesProc)(nativeSrc);
sl@0
  1395
		if ((*tclWinProcs->setFileAttributesProc)(nativeDst, attr) != FALSE) {
sl@0
  1396
		    return TCL_OK;
sl@0
  1397
		}
sl@0
  1398
		TclWinConvertError(GetLastError());
sl@0
  1399
	    }
sl@0
  1400
	    break;
sl@0
  1401
	}
sl@0
  1402
        case DOTREE_POSTD: {
sl@0
  1403
	    return TCL_OK;
sl@0
  1404
	}
sl@0
  1405
    }
sl@0
  1406
sl@0
  1407
    /*
sl@0
  1408
     * There shouldn't be a problem with src, because we already
sl@0
  1409
     * checked it to get here.
sl@0
  1410
     */
sl@0
  1411
sl@0
  1412
    if (errorPtr != NULL) {
sl@0
  1413
	Tcl_WinTCharToUtf(nativeDst, -1, errorPtr);
sl@0
  1414
    }
sl@0
  1415
    return TCL_ERROR;
sl@0
  1416
}
sl@0
  1417

sl@0
  1418
/*
sl@0
  1419
 *----------------------------------------------------------------------
sl@0
  1420
 *
sl@0
  1421
 * TraversalDelete --
sl@0
  1422
 *
sl@0
  1423
 *      Called by procedure TraverseWinTree for every file and
sl@0
  1424
 *      directory that it encounters in a directory hierarchy. This
sl@0
  1425
 *      procedure unlinks files, and removes directories after all the
sl@0
  1426
 *      containing files have been processed.
sl@0
  1427
 *
sl@0
  1428
 * Results:
sl@0
  1429
 *      Standard Tcl result.
sl@0
  1430
 *
sl@0
  1431
 * Side effects:
sl@0
  1432
 *      Files or directory specified by src will be deleted. If an
sl@0
  1433
 *      error occurs, the windows error is converted to a Posix error
sl@0
  1434
 *      and errno is set accordingly.
sl@0
  1435
 *
sl@0
  1436
 *----------------------------------------------------------------------
sl@0
  1437
 */
sl@0
  1438
sl@0
  1439
static int
sl@0
  1440
TraversalDelete( 
sl@0
  1441
    CONST TCHAR *nativeSrc,	/* Source pathname to delete. */
sl@0
  1442
    CONST TCHAR *dstPtr,	/* Not used. */
sl@0
  1443
    int type,			/* Reason for call - see TraverseWinTree() */
sl@0
  1444
    Tcl_DString *errorPtr)	/* If non-NULL, initialized DString filled
sl@0
  1445
				 * with UTF-8 name of file causing error. */
sl@0
  1446
{
sl@0
  1447
    switch (type) {
sl@0
  1448
	case DOTREE_F: {
sl@0
  1449
	    if (TclpDeleteFile(nativeSrc) == TCL_OK) {
sl@0
  1450
		return TCL_OK;
sl@0
  1451
	    }
sl@0
  1452
	    break;
sl@0
  1453
	}
sl@0
  1454
	case DOTREE_PRED: {
sl@0
  1455
	    return TCL_OK;
sl@0
  1456
	}
sl@0
  1457
	case DOTREE_POSTD: {
sl@0
  1458
	    if (DoRemoveJustDirectory(nativeSrc, 0, NULL) == TCL_OK) {
sl@0
  1459
		return TCL_OK;
sl@0
  1460
	    }
sl@0
  1461
	    break;
sl@0
  1462
	}
sl@0
  1463
    }
sl@0
  1464
sl@0
  1465
    if (errorPtr != NULL) {
sl@0
  1466
	Tcl_WinTCharToUtf(nativeSrc, -1, errorPtr);
sl@0
  1467
    }
sl@0
  1468
    return TCL_ERROR;
sl@0
  1469
}
sl@0
  1470

sl@0
  1471
/*
sl@0
  1472
 *----------------------------------------------------------------------
sl@0
  1473
 *
sl@0
  1474
 * StatError --
sl@0
  1475
 *
sl@0
  1476
 *	Sets the object result with the appropriate error.
sl@0
  1477
 *
sl@0
  1478
 * Results:
sl@0
  1479
 *      None.
sl@0
  1480
 *
sl@0
  1481
 * Side effects:
sl@0
  1482
 *      The interp's object result is set with an error message
sl@0
  1483
 *	based on the objIndex, fileName and errno.
sl@0
  1484
 *
sl@0
  1485
 *----------------------------------------------------------------------
sl@0
  1486
 */
sl@0
  1487
sl@0
  1488
static void
sl@0
  1489
StatError(
sl@0
  1490
    Tcl_Interp *interp,		/* The interp that has the error */
sl@0
  1491
    Tcl_Obj *fileName)	        /* The name of the file which caused the 
sl@0
  1492
				 * error. */
sl@0
  1493
{
sl@0
  1494
    TclWinConvertError(GetLastError());
sl@0
  1495
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), 
sl@0
  1496
			   "could not read \"", Tcl_GetString(fileName), 
sl@0
  1497
			   "\": ", Tcl_PosixError(interp), 
sl@0
  1498
			   (char *) NULL);
sl@0
  1499
}
sl@0
  1500

sl@0
  1501
/*
sl@0
  1502
 *----------------------------------------------------------------------
sl@0
  1503
 *
sl@0
  1504
 * GetWinFileAttributes --
sl@0
  1505
 *
sl@0
  1506
 *      Returns a Tcl_Obj containing the value of a file attribute.
sl@0
  1507
 *	This routine gets the -hidden, -readonly or -system attribute.
sl@0
  1508
 *
sl@0
  1509
 * Results:
sl@0
  1510
 *      Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
sl@0
  1511
 *	will have ref count 0. If the return value is not TCL_OK,
sl@0
  1512
 *	attributePtrPtr is not touched.
sl@0
  1513
 *
sl@0
  1514
 * Side effects:
sl@0
  1515
 *      A new object is allocated if the file is valid.
sl@0
  1516
 *
sl@0
  1517
 *----------------------------------------------------------------------
sl@0
  1518
 */
sl@0
  1519
sl@0
  1520
static int
sl@0
  1521
GetWinFileAttributes(
sl@0
  1522
    Tcl_Interp *interp,		/* The interp we are using for errors. */
sl@0
  1523
    int objIndex,		/* The index of the attribute. */
sl@0
  1524
    Tcl_Obj *fileName,	        /* The name of the file. */
sl@0
  1525
    Tcl_Obj **attributePtrPtr)	/* A pointer to return the object with. */
sl@0
  1526
{
sl@0
  1527
    DWORD result;
sl@0
  1528
    CONST TCHAR *nativeName;
sl@0
  1529
    int attr;
sl@0
  1530
    
sl@0
  1531
    nativeName = Tcl_FSGetNativePath(fileName);
sl@0
  1532
    result = (*tclWinProcs->getFileAttributesProc)(nativeName);
sl@0
  1533
sl@0
  1534
    if (result == 0xffffffff) {
sl@0
  1535
	StatError(interp, fileName);
sl@0
  1536
	return TCL_ERROR;
sl@0
  1537
    }
sl@0
  1538
sl@0
  1539
    attr = (int)(result & attributeArray[objIndex]);
sl@0
  1540
    if ((objIndex == WIN_HIDDEN_ATTRIBUTE) && (attr != 0)) {
sl@0
  1541
	/* 
sl@0
  1542
	 * It is hidden.  However there is a bug on some Windows
sl@0
  1543
	 * OSes in which root volumes (drives) formatted as NTFS
sl@0
  1544
	 * are declared hidden when they are not (and cannot be).
sl@0
  1545
	 * 
sl@0
  1546
	 * We test for, and fix that case, here.
sl@0
  1547
	 */
sl@0
  1548
	int len;
sl@0
  1549
	char *str = Tcl_GetStringFromObj(fileName,&len);
sl@0
  1550
	if (len < 4) {
sl@0
  1551
	    if (len == 0) {
sl@0
  1552
		/* 
sl@0
  1553
		 * Not sure if this is possible, but we pass it on
sl@0
  1554
		 * anyway 
sl@0
  1555
		 */
sl@0
  1556
	    } else if (len == 1 && (str[0] == '/' || str[0] == '\\')) {
sl@0
  1557
		/* Path is pointing to the root volume */
sl@0
  1558
		attr = 0;
sl@0
  1559
	    } else if ((str[1] == ':') 
sl@0
  1560
		       && (len == 2 || (str[2] == '/' || str[2] == '\\'))) {
sl@0
  1561
		/* Path is of the form 'x:' or 'x:/' or 'x:\' */
sl@0
  1562
		attr = 0;
sl@0
  1563
	    }
sl@0
  1564
	}
sl@0
  1565
    }
sl@0
  1566
    *attributePtrPtr = Tcl_NewBooleanObj(attr);
sl@0
  1567
    return TCL_OK;
sl@0
  1568
}
sl@0
  1569

sl@0
  1570
/*
sl@0
  1571
 *----------------------------------------------------------------------
sl@0
  1572
 *
sl@0
  1573
 * ConvertFileNameFormat --
sl@0
  1574
 *
sl@0
  1575
 *      Returns a Tcl_Obj containing either the long or short version of the 
sl@0
  1576
 *	file name.
sl@0
  1577
 *
sl@0
  1578
 * Results:
sl@0
  1579
 *      Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
sl@0
  1580
 *	will have ref count 0. If the return value is not TCL_OK,
sl@0
  1581
 *	attributePtrPtr is not touched.
sl@0
  1582
 *	
sl@0
  1583
 *	Warning: if you pass this function a drive name like 'c:' it
sl@0
  1584
 *	will actually return the current working directory on that
sl@0
  1585
 *	drive.  To avoid this, make sure the drive name ends in a
sl@0
  1586
 *	slash, like this 'c:/'.
sl@0
  1587
 *
sl@0
  1588
 * Side effects:
sl@0
  1589
 *      A new object is allocated if the file is valid.
sl@0
  1590
 *
sl@0
  1591
 *----------------------------------------------------------------------
sl@0
  1592
 */
sl@0
  1593
sl@0
  1594
static int
sl@0
  1595
ConvertFileNameFormat(
sl@0
  1596
    Tcl_Interp *interp,		/* The interp we are using for errors. */
sl@0
  1597
    int objIndex,		/* The index of the attribute. */
sl@0
  1598
    Tcl_Obj *fileName,   	/* The name of the file. */
sl@0
  1599
    int longShort,		/* 0 to short name, 1 to long name. */
sl@0
  1600
    Tcl_Obj **attributePtrPtr)	/* A pointer to return the object with. */
sl@0
  1601
{
sl@0
  1602
    int pathc, i;
sl@0
  1603
    Tcl_Obj *splitPath;
sl@0
  1604
    int result = TCL_OK;
sl@0
  1605
sl@0
  1606
    splitPath = Tcl_FSSplitPath(fileName, &pathc);
sl@0
  1607
sl@0
  1608
    if (splitPath == NULL || pathc == 0) {
sl@0
  1609
	if (interp != NULL) {
sl@0
  1610
	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), 
sl@0
  1611
		"could not read \"", Tcl_GetString(fileName),
sl@0
  1612
		"\": no such file or directory", 
sl@0
  1613
		(char *) NULL);
sl@0
  1614
	}
sl@0
  1615
	result = TCL_ERROR;
sl@0
  1616
	goto cleanup;
sl@0
  1617
    }
sl@0
  1618
    
sl@0
  1619
    for (i = 0; i < pathc; i++) {
sl@0
  1620
	Tcl_Obj *elt;
sl@0
  1621
	char *pathv;
sl@0
  1622
	int pathLen;
sl@0
  1623
	Tcl_ListObjIndex(NULL, splitPath, i, &elt);
sl@0
  1624
	
sl@0
  1625
	pathv = Tcl_GetStringFromObj(elt, &pathLen);
sl@0
  1626
	if ((pathv[0] == '/')
sl@0
  1627
		|| ((pathLen == 3) && (pathv[1] == ':'))
sl@0
  1628
		|| (strcmp(pathv, ".") == 0)
sl@0
  1629
		|| (strcmp(pathv, "..") == 0)) {
sl@0
  1630
	    /*
sl@0
  1631
	     * Handle "/", "//machine/export", "c:/", "." or ".." by just
sl@0
  1632
	     * copying the string literally.  Uppercase the drive letter,
sl@0
  1633
	     * just because it looks better under Windows to do so.
sl@0
  1634
	     */
sl@0
  1635
sl@0
  1636
	    simple:
sl@0
  1637
	    /* Here we are modifying the string representation in place */
sl@0
  1638
	    /* I believe this is legal, since this won't affect any 
sl@0
  1639
	     * file representation this thing may have. */
sl@0
  1640
	    pathv[0] = (char) Tcl_UniCharToUpper(UCHAR(pathv[0]));
sl@0
  1641
	} else {
sl@0
  1642
	    Tcl_Obj *tempPath;
sl@0
  1643
	    Tcl_DString ds;
sl@0
  1644
	    Tcl_DString dsTemp;
sl@0
  1645
	    TCHAR *nativeName;
sl@0
  1646
	    char *tempString;
sl@0
  1647
	    int tempLen;
sl@0
  1648
	    WIN32_FIND_DATAT data;
sl@0
  1649
	    HANDLE handle;
sl@0
  1650
	    DWORD attr;
sl@0
  1651
sl@0
  1652
	    tempPath = Tcl_FSJoinPath(splitPath, i+1);
sl@0
  1653
	    Tcl_IncrRefCount(tempPath);
sl@0
  1654
	    /* 
sl@0
  1655
	     * We'd like to call Tcl_FSGetNativePath(tempPath)
sl@0
  1656
	     * but that is likely to lead to infinite loops 
sl@0
  1657
	     */
sl@0
  1658
	    Tcl_DStringInit(&ds);
sl@0
  1659
	    tempString = Tcl_GetStringFromObj(tempPath,&tempLen);
sl@0
  1660
	    nativeName = Tcl_WinUtfToTChar(tempString, tempLen, &ds);
sl@0
  1661
	    Tcl_DecrRefCount(tempPath);
sl@0
  1662
	    handle = (*tclWinProcs->findFirstFileProc)(nativeName, &data);
sl@0
  1663
	    if (handle == INVALID_HANDLE_VALUE) {
sl@0
  1664
		/*
sl@0
  1665
		 * FindFirstFile() doesn't like root directories.  We 
sl@0
  1666
		 * would only get a root directory here if the caller
sl@0
  1667
		 * specified "c:" or "c:." and the current directory on the
sl@0
  1668
		 * drive was the root directory
sl@0
  1669
		 */
sl@0
  1670
sl@0
  1671
		attr = (*tclWinProcs->getFileAttributesProc)(nativeName);
sl@0
  1672
		if ((attr != 0xFFFFFFFF) && (attr & FILE_ATTRIBUTE_DIRECTORY)) {
sl@0
  1673
		    Tcl_DStringFree(&ds);
sl@0
  1674
		    goto simple;
sl@0
  1675
		}
sl@0
  1676
	    }
sl@0
  1677
sl@0
  1678
	    if (handle == INVALID_HANDLE_VALUE) {
sl@0
  1679
		Tcl_DStringFree(&ds);
sl@0
  1680
		if (interp != NULL) {
sl@0
  1681
		    StatError(interp, fileName);
sl@0
  1682
		}
sl@0
  1683
		result = TCL_ERROR;
sl@0
  1684
		goto cleanup;
sl@0
  1685
	    }
sl@0
  1686
	    if (tclWinProcs->useWide) {
sl@0
  1687
		nativeName = (TCHAR *) data.w.cAlternateFileName;
sl@0
  1688
		if (longShort) {
sl@0
  1689
		    if (data.w.cFileName[0] != '\0') {
sl@0
  1690
			nativeName = (TCHAR *) data.w.cFileName;
sl@0
  1691
		    } 
sl@0
  1692
		} else {
sl@0
  1693
		    if (data.w.cAlternateFileName[0] == '\0') {
sl@0
  1694
			nativeName = (TCHAR *) data.w.cFileName;
sl@0
  1695
		    }
sl@0
  1696
		}
sl@0
  1697
	    } else {
sl@0
  1698
		nativeName = (TCHAR *) data.a.cAlternateFileName;
sl@0
  1699
		if (longShort) {
sl@0
  1700
		    if (data.a.cFileName[0] != '\0') {
sl@0
  1701
			nativeName = (TCHAR *) data.a.cFileName;
sl@0
  1702
		    } 
sl@0
  1703
		} else {
sl@0
  1704
		    if (data.a.cAlternateFileName[0] == '\0') {
sl@0
  1705
			nativeName = (TCHAR *) data.a.cFileName;
sl@0
  1706
		    }
sl@0
  1707
		}
sl@0
  1708
	    }
sl@0
  1709
sl@0
  1710
	    /*
sl@0
  1711
	     * Purify reports a extraneous UMR in Tcl_WinTCharToUtf() trying 
sl@0
  1712
	     * to dereference nativeName as a Unicode string.  I have proven 
sl@0
  1713
	     * to myself that purify is wrong by running the following 
sl@0
  1714
	     * example when nativeName == data.w.cAlternateFileName and 
sl@0
  1715
	     * noting that purify doesn't complain about the first line,
sl@0
  1716
	     * but does complain about the second.
sl@0
  1717
	     *
sl@0
  1718
	     *	fprintf(stderr, "%d\n", data.w.cAlternateFileName[0]);
sl@0
  1719
	     *	fprintf(stderr, "%d\n", ((WCHAR *) nativeName)[0]);
sl@0
  1720
	     */
sl@0
  1721
sl@0
  1722
	    Tcl_DStringInit(&dsTemp);
sl@0
  1723
	    Tcl_WinTCharToUtf(nativeName, -1, &dsTemp);
sl@0
  1724
	    /* Deal with issues of tildes being absolute */
sl@0
  1725
	    if (Tcl_DStringValue(&dsTemp)[0] == '~') {
sl@0
  1726
		tempPath = Tcl_NewStringObj("./",2);
sl@0
  1727
		Tcl_AppendToObj(tempPath, Tcl_DStringValue(&dsTemp), 
sl@0
  1728
				Tcl_DStringLength(&dsTemp));
sl@0
  1729
	    } else {
sl@0
  1730
		tempPath = Tcl_NewStringObj(Tcl_DStringValue(&dsTemp), 
sl@0
  1731
					    Tcl_DStringLength(&dsTemp));
sl@0
  1732
	    }
sl@0
  1733
	    Tcl_ListObjReplace(NULL, splitPath, i, 1, 1, &tempPath);
sl@0
  1734
	    Tcl_DStringFree(&ds);
sl@0
  1735
	    Tcl_DStringFree(&dsTemp);
sl@0
  1736
	    FindClose(handle);
sl@0
  1737
	}
sl@0
  1738
    }
sl@0
  1739
sl@0
  1740
    *attributePtrPtr = Tcl_FSJoinPath(splitPath, -1);
sl@0
  1741
sl@0
  1742
cleanup:
sl@0
  1743
    if (splitPath != NULL) {
sl@0
  1744
	Tcl_DecrRefCount(splitPath);
sl@0
  1745
    }
sl@0
  1746
  
sl@0
  1747
    return result;
sl@0
  1748
}
sl@0
  1749

sl@0
  1750
/*
sl@0
  1751
 *----------------------------------------------------------------------
sl@0
  1752
 *
sl@0
  1753
 * GetWinFileLongName --
sl@0
  1754
 *
sl@0
  1755
 *      Returns a Tcl_Obj containing the long version of the file
sl@0
  1756
 *	name.
sl@0
  1757
 *
sl@0
  1758
 * Results:
sl@0
  1759
 *      Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
sl@0
  1760
 *	will have ref count 0. If the return value is not TCL_OK,
sl@0
  1761
 *	attributePtrPtr is not touched.
sl@0
  1762
 *
sl@0
  1763
 * Side effects:
sl@0
  1764
 *      A new object is allocated if the file is valid.
sl@0
  1765
 *
sl@0
  1766
 *----------------------------------------------------------------------
sl@0
  1767
 */
sl@0
  1768
sl@0
  1769
static int
sl@0
  1770
GetWinFileLongName(
sl@0
  1771
    Tcl_Interp *interp,		/* The interp we are using for errors. */
sl@0
  1772
    int objIndex,		/* The index of the attribute. */
sl@0
  1773
    Tcl_Obj *fileName,  	/* The name of the file. */
sl@0
  1774
    Tcl_Obj **attributePtrPtr)	/* A pointer to return the object with. */
sl@0
  1775
{
sl@0
  1776
    return ConvertFileNameFormat(interp, objIndex, fileName, 1, attributePtrPtr);
sl@0
  1777
}
sl@0
  1778

sl@0
  1779
/*
sl@0
  1780
 *----------------------------------------------------------------------
sl@0
  1781
 *
sl@0
  1782
 * GetWinFileShortName --
sl@0
  1783
 *
sl@0
  1784
 *      Returns a Tcl_Obj containing the short version of the file
sl@0
  1785
 *	name.
sl@0
  1786
 *
sl@0
  1787
 * Results:
sl@0
  1788
 *      Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
sl@0
  1789
 *	will have ref count 0. If the return value is not TCL_OK,
sl@0
  1790
 *	attributePtrPtr is not touched.
sl@0
  1791
 *
sl@0
  1792
 * Side effects:
sl@0
  1793
 *      A new object is allocated if the file is valid.
sl@0
  1794
 *
sl@0
  1795
 *----------------------------------------------------------------------
sl@0
  1796
 */
sl@0
  1797
sl@0
  1798
static int
sl@0
  1799
GetWinFileShortName(
sl@0
  1800
    Tcl_Interp *interp,		/* The interp we are using for errors. */
sl@0
  1801
    int objIndex,		/* The index of the attribute. */
sl@0
  1802
    Tcl_Obj *fileName,  	/* The name of the file. */
sl@0
  1803
    Tcl_Obj **attributePtrPtr)	/* A pointer to return the object with. */
sl@0
  1804
{
sl@0
  1805
    return ConvertFileNameFormat(interp, objIndex, fileName, 0, attributePtrPtr);
sl@0
  1806
}
sl@0
  1807

sl@0
  1808
/*
sl@0
  1809
 *----------------------------------------------------------------------
sl@0
  1810
 *
sl@0
  1811
 * SetWinFileAttributes --
sl@0
  1812
 *
sl@0
  1813
 *	Set the file attributes to the value given by attributePtr.
sl@0
  1814
 *	This routine sets the -hidden, -readonly, or -system attributes.
sl@0
  1815
 *
sl@0
  1816
 * Results:
sl@0
  1817
 *      Standard TCL error.
sl@0
  1818
 *
sl@0
  1819
 * Side effects:
sl@0
  1820
 *      The file's attribute is set.
sl@0
  1821
 *
sl@0
  1822
 *----------------------------------------------------------------------
sl@0
  1823
 */
sl@0
  1824
sl@0
  1825
static int
sl@0
  1826
SetWinFileAttributes(
sl@0
  1827
    Tcl_Interp *interp,		/* The interp we are using for errors. */
sl@0
  1828
    int objIndex,		/* The index of the attribute. */
sl@0
  1829
    Tcl_Obj *fileName,  	/* The name of the file. */
sl@0
  1830
    Tcl_Obj *attributePtr)	/* The new value of the attribute. */
sl@0
  1831
{
sl@0
  1832
    DWORD fileAttributes;
sl@0
  1833
    int yesNo;
sl@0
  1834
    int result;
sl@0
  1835
    CONST TCHAR *nativeName;
sl@0
  1836
sl@0
  1837
    nativeName = Tcl_FSGetNativePath(fileName);
sl@0
  1838
    fileAttributes = (*tclWinProcs->getFileAttributesProc)(nativeName);
sl@0
  1839
sl@0
  1840
    if (fileAttributes == 0xffffffff) {
sl@0
  1841
	StatError(interp, fileName);
sl@0
  1842
	return TCL_ERROR;
sl@0
  1843
    }
sl@0
  1844
sl@0
  1845
    result = Tcl_GetBooleanFromObj(interp, attributePtr, &yesNo);
sl@0
  1846
    if (result != TCL_OK) {
sl@0
  1847
	return result;
sl@0
  1848
    }
sl@0
  1849
sl@0
  1850
    if (yesNo) {
sl@0
  1851
	fileAttributes |= (attributeArray[objIndex]);
sl@0
  1852
    } else {
sl@0
  1853
	fileAttributes &= ~(attributeArray[objIndex]);
sl@0
  1854
    }
sl@0
  1855
sl@0
  1856
    if (!(*tclWinProcs->setFileAttributesProc)(nativeName, fileAttributes)) {
sl@0
  1857
	StatError(interp, fileName);
sl@0
  1858
	return TCL_ERROR;
sl@0
  1859
    }
sl@0
  1860
sl@0
  1861
    return result;
sl@0
  1862
}
sl@0
  1863

sl@0
  1864
/*
sl@0
  1865
 *----------------------------------------------------------------------
sl@0
  1866
 *
sl@0
  1867
 * SetWinFileLongName --
sl@0
  1868
 *
sl@0
  1869
 *	The attribute in question is a readonly attribute and cannot
sl@0
  1870
 *	be set.
sl@0
  1871
 *
sl@0
  1872
 * Results:
sl@0
  1873
 *      TCL_ERROR
sl@0
  1874
 *
sl@0
  1875
 * Side effects:
sl@0
  1876
 *      The object result is set to a pertinent error message.
sl@0
  1877
 *
sl@0
  1878
 *----------------------------------------------------------------------
sl@0
  1879
 */
sl@0
  1880
sl@0
  1881
static int
sl@0
  1882
CannotSetAttribute(
sl@0
  1883
    Tcl_Interp *interp,		/* The interp we are using for errors. */
sl@0
  1884
    int objIndex,		/* The index of the attribute. */
sl@0
  1885
    Tcl_Obj *fileName,	        /* The name of the file. */
sl@0
  1886
    Tcl_Obj *attributePtr)	/* The new value of the attribute. */
sl@0
  1887
{
sl@0
  1888
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), 
sl@0
  1889
	    "cannot set attribute \"", tclpFileAttrStrings[objIndex],
sl@0
  1890
	    "\" for file \"", Tcl_GetString(fileName), 
sl@0
  1891
	    "\": attribute is readonly", 
sl@0
  1892
	    (char *) NULL);
sl@0
  1893
    return TCL_ERROR;
sl@0
  1894
}
sl@0
  1895
sl@0
  1896

sl@0
  1897
/*
sl@0
  1898
 *---------------------------------------------------------------------------
sl@0
  1899
 *
sl@0
  1900
 * TclpObjListVolumes --
sl@0
  1901
 *
sl@0
  1902
 *	Lists the currently mounted volumes
sl@0
  1903
 *
sl@0
  1904
 * Results:
sl@0
  1905
 *	The list of volumes.
sl@0
  1906
 *
sl@0
  1907
 * Side effects:
sl@0
  1908
 *	None
sl@0
  1909
 *
sl@0
  1910
 *---------------------------------------------------------------------------
sl@0
  1911
 */
sl@0
  1912
sl@0
  1913
Tcl_Obj*
sl@0
  1914
TclpObjListVolumes(void)
sl@0
  1915
{
sl@0
  1916
    Tcl_Obj *resultPtr, *elemPtr;
sl@0
  1917
    char buf[40 * 4];		/* There couldn't be more than 30 drives??? */
sl@0
  1918
    int i;
sl@0
  1919
    char *p;
sl@0
  1920
sl@0
  1921
    resultPtr = Tcl_NewObj();
sl@0
  1922
sl@0
  1923
    /*
sl@0
  1924
     * On Win32s:
sl@0
  1925
     * GetLogicalDriveStrings() isn't implemented.
sl@0
  1926
     * GetLogicalDrives() returns incorrect information.
sl@0
  1927
     */
sl@0
  1928
sl@0
  1929
    if (GetLogicalDriveStringsA(sizeof(buf), buf) == 0) {
sl@0
  1930
	/*
sl@0
  1931
	 * GetVolumeInformation() will detects all drives, but causes
sl@0
  1932
	 * chattering on empty floppy drives.  We only do this if 
sl@0
  1933
	 * GetLogicalDriveStrings() didn't work.  It has also been reported
sl@0
  1934
	 * that on some laptops it takes a while for GetVolumeInformation()
sl@0
  1935
	 * to return when pinging an empty floppy drive, another reason to 
sl@0
  1936
	 * try to avoid calling it.
sl@0
  1937
	 */
sl@0
  1938
sl@0
  1939
	buf[1] = ':';
sl@0
  1940
	buf[2] = '/';
sl@0
  1941
	buf[3] = '\0';
sl@0
  1942
sl@0
  1943
	for (i = 0; i < 26; i++) {
sl@0
  1944
	    buf[0] = (char) ('a' + i);
sl@0
  1945
	    if (GetVolumeInformationA(buf, NULL, 0, NULL, NULL, NULL, NULL, 0)  
sl@0
  1946
		    || (GetLastError() == ERROR_NOT_READY)) {
sl@0
  1947
		elemPtr = Tcl_NewStringObj(buf, -1);
sl@0
  1948
		Tcl_ListObjAppendElement(NULL, resultPtr, elemPtr);
sl@0
  1949
	    }
sl@0
  1950
	}
sl@0
  1951
    } else {
sl@0
  1952
	for (p = buf; *p != '\0'; p += 4) {
sl@0
  1953
	    p[2] = '/';
sl@0
  1954
	    elemPtr = Tcl_NewStringObj(p, -1);
sl@0
  1955
	    Tcl_ListObjAppendElement(NULL, resultPtr, elemPtr);
sl@0
  1956
	}
sl@0
  1957
    }
sl@0
  1958
    
sl@0
  1959
    Tcl_IncrRefCount(resultPtr);
sl@0
  1960
    return resultPtr;
sl@0
  1961
}