tclWinFCmd.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:55k
源码类别:

通讯编程

开发平台:

Visual C++

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