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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclWinFile.c --
  3.  *
  4.  *      This file contains temporary wrappers around UNIX file handling
  5.  *      functions. These wrappers map the UNIX functions to Win32 HANDLE-style
  6.  *      files, which can be manipulated through the Win32 console redirection
  7.  *      interfaces.
  8.  *
  9.  * Copyright (c) 1995-1998 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * RCS: @(#) $Id: tclWinFile.c,v 1.44.2.18 2006/10/17 04:36:45 dgp Exp $
  15.  */
  16. //#define _WIN32_WINNT  0x0500
  17. #include "tclWinInt.h"
  18. #include <winioctl.h>
  19. #include <sys/stat.h>
  20. #include <shlobj.h>
  21. #include <lmaccess.h> /* For TclpGetUserHome(). */
  22. /*
  23.  * The number of 100-ns intervals between the Windows system epoch (1601-01-01
  24.  * on the proleptic Gregorian calendar) and the Posix epoch (1970-01-01).
  25.  */
  26. #define POSIX_EPOCH_AS_FILETIME 116444736000000000
  27. /*
  28.  * Declarations for 'link' related information.  This information
  29.  * should come with VC++ 6.0, but is not in some older SDKs.
  30.  * In any case it is not well documented.
  31.  */
  32. #ifndef IO_REPARSE_TAG_RESERVED_ONE
  33. #  define IO_REPARSE_TAG_RESERVED_ONE 0x000000001
  34. #endif
  35. #ifndef IO_REPARSE_TAG_RESERVED_RANGE
  36. #  define IO_REPARSE_TAG_RESERVED_RANGE 0x000000001
  37. #endif
  38. #ifndef IO_REPARSE_TAG_VALID_VALUES
  39. #  define IO_REPARSE_TAG_VALID_VALUES 0x0E000FFFF
  40. #endif
  41. #ifndef IO_REPARSE_TAG_HSM
  42. #  define IO_REPARSE_TAG_HSM 0x0C0000004
  43. #endif
  44. #ifndef IO_REPARSE_TAG_NSS
  45. #  define IO_REPARSE_TAG_NSS 0x080000005
  46. #endif
  47. #ifndef IO_REPARSE_TAG_NSSRECOVER
  48. #  define IO_REPARSE_TAG_NSSRECOVER 0x080000006
  49. #endif
  50. #ifndef IO_REPARSE_TAG_SIS
  51. #  define IO_REPARSE_TAG_SIS 0x080000007
  52. #endif
  53. #ifndef IO_REPARSE_TAG_DFS
  54. #  define IO_REPARSE_TAG_DFS 0x080000008
  55. #endif
  56. #ifndef IO_REPARSE_TAG_RESERVED_ZERO
  57. #  define IO_REPARSE_TAG_RESERVED_ZERO 0x00000000
  58. #endif
  59. #ifndef FILE_FLAG_OPEN_REPARSE_POINT
  60. #  define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000
  61. #endif
  62. #ifndef IO_REPARSE_TAG_MOUNT_POINT
  63. #  define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
  64. #endif
  65. #ifndef IsReparseTagValid
  66. #  define IsReparseTagValid(x) (!((x)&~IO_REPARSE_TAG_VALID_VALUES)&&((x)>IO_REPARSE_TAG_RESERVED_RANGE))
  67. #endif
  68. #ifndef IO_REPARSE_TAG_SYMBOLIC_LINK
  69. #  define IO_REPARSE_TAG_SYMBOLIC_LINK IO_REPARSE_TAG_RESERVED_ZERO
  70. #endif
  71. #ifndef FILE_SPECIAL_ACCESS
  72. #  define FILE_SPECIAL_ACCESS         (FILE_ANY_ACCESS)
  73. #endif
  74. #ifndef FSCTL_SET_REPARSE_POINT
  75. #  define FSCTL_SET_REPARSE_POINT    CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
  76. #  define FSCTL_GET_REPARSE_POINT    CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) 
  77. #  define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) 
  78. #endif
  79. #ifndef INVALID_FILE_ATTRIBUTES
  80. #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
  81. #endif
  82. /* 
  83.  * Maximum reparse buffer info size. The max user defined reparse
  84.  * data is 16KB, plus there's a header.
  85.  */
  86. #define MAX_REPARSE_SIZE 17000
  87. /*
  88.  * Undocumented REPARSE_MOUNTPOINT_HEADER_SIZE structure definition.
  89.  * This is found in winnt.h.
  90.  * 
  91.  * IMPORTANT: caution when using this structure, since the actual
  92.  * structures used will want to store a full path in the 'PathBuffer'
  93.  * field, but there isn't room (there's only a single WCHAR!).  Therefore
  94.  * one must artificially create a larger space of memory and then cast it
  95.  * to this type.  We use the 'DUMMY_REPARSE_BUFFER' struct just below to
  96.  * deal with this problem.
  97.  */
  98. #define REPARSE_MOUNTPOINT_HEADER_SIZE   8
  99. #ifndef REPARSE_DATA_BUFFER_HEADER_SIZE
  100. typedef struct _REPARSE_DATA_BUFFER {
  101.     DWORD  ReparseTag;
  102.     WORD   ReparseDataLength;
  103.     WORD   Reserved;
  104.     union {
  105.         struct {
  106.             WORD   SubstituteNameOffset;
  107.             WORD   SubstituteNameLength;
  108.             WORD   PrintNameOffset;
  109.             WORD   PrintNameLength;
  110.             WCHAR PathBuffer[1];
  111.         } SymbolicLinkReparseBuffer;
  112.         struct {
  113.             WORD   SubstituteNameOffset;
  114.             WORD   SubstituteNameLength;
  115.             WORD   PrintNameOffset;
  116.             WORD   PrintNameLength;
  117.             WCHAR PathBuffer[1];
  118.         } MountPointReparseBuffer;
  119.         struct {
  120.             BYTE   DataBuffer[1];
  121.         } GenericReparseBuffer;
  122.     };
  123. } REPARSE_DATA_BUFFER;
  124. #endif
  125. typedef struct {
  126.     REPARSE_DATA_BUFFER dummy;
  127.     WCHAR  dummyBuf[MAX_PATH*3];
  128. } DUMMY_REPARSE_BUFFER;
  129. #if defined(_MSC_VER) && ( _MSC_VER <= 1100 )
  130. #define HAVE_NO_FINDEX_ENUMS
  131. #elif !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0400)
  132. #define HAVE_NO_FINDEX_ENUMS
  133. #endif
  134. #ifdef HAVE_NO_FINDEX_ENUMS
  135. /* These two aren't in VC++ 5.2 headers */
  136. typedef enum _FINDEX_INFO_LEVELS {
  137. FindExInfoStandard,
  138. FindExInfoMaxInfoLevel
  139. } FINDEX_INFO_LEVELS;
  140. typedef enum _FINDEX_SEARCH_OPS {
  141. FindExSearchNameMatch,
  142. FindExSearchLimitToDirectories,
  143. FindExSearchLimitToDevices,
  144. FindExSearchMaxSearchOp
  145. } FINDEX_SEARCH_OPS;
  146. #endif /* HAVE_NO_FINDEX_ENUMS */
  147. /* Other typedefs required by this code */
  148. static time_t ToCTime(FILETIME fileTime);
  149. static void FromCTime(time_t posixTime, FILETIME *fileTime);
  150. typedef NET_API_STATUS NET_API_FUNCTION NETUSERGETINFOPROC
  151. (LPWSTR servername, LPWSTR username, DWORD level, LPBYTE *bufptr);
  152. typedef NET_API_STATUS NET_API_FUNCTION NETAPIBUFFERFREEPROC
  153. (LPVOID Buffer);
  154. typedef NET_API_STATUS NET_API_FUNCTION NETGETDCNAMEPROC
  155. (LPWSTR servername, LPWSTR domainname, LPBYTE *bufptr);
  156. /*
  157.  * Declarations for local procedures defined in this file:
  158.  */
  159. static int NativeAccess(CONST TCHAR *path, int mode);
  160. static int NativeStat(CONST TCHAR *path, Tcl_StatBuf *statPtr, int checkLinks);
  161. static unsigned short NativeStatMode(DWORD attr, int checkLinks, int isExec);
  162. static int NativeIsExec(CONST TCHAR *path);
  163. static int NativeReadReparse(CONST TCHAR* LinkDirectory, 
  164.      REPARSE_DATA_BUFFER* buffer);
  165. static int NativeWriteReparse(CONST TCHAR* LinkDirectory, 
  166.       REPARSE_DATA_BUFFER* buffer);
  167. static int NativeMatchType(int isDrive, DWORD attr, CONST TCHAR* nativeName, 
  168.    Tcl_GlobTypeData *types);
  169. static int WinIsDrive(CONST char *name, int nameLen);
  170. static int WinIsReserved(CONST char *path);
  171. static Tcl_Obj* WinReadLink(CONST TCHAR* LinkSource);
  172. static Tcl_Obj* WinReadLinkDirectory(CONST TCHAR* LinkDirectory);
  173. static int WinLink(CONST TCHAR* LinkSource, CONST TCHAR* LinkTarget, 
  174.    int linkAction);
  175. static int WinSymLinkDirectory(CONST TCHAR* LinkDirectory, 
  176.        CONST TCHAR* LinkTarget);
  177. /*
  178.  *--------------------------------------------------------------------
  179.  *
  180.  * WinLink
  181.  *
  182.  * Make a link from source to target. 
  183.  *--------------------------------------------------------------------
  184.  */
  185. static int 
  186. WinLink(LinkSource, LinkTarget, linkAction)
  187.     CONST TCHAR* LinkSource;
  188.     CONST TCHAR* LinkTarget;
  189.     int linkAction;
  190. {
  191.     WCHAR tempFileName[MAX_PATH];
  192.     TCHAR* tempFilePart;
  193.     int         attr;
  194.     
  195.     /* Get the full path referenced by the target */
  196.     if (!(*tclWinProcs->getFullPathNameProc)(LinkTarget, 
  197.   MAX_PATH, tempFileName, &tempFilePart)) {
  198. /* Invalid file */
  199. TclWinConvertError(GetLastError());
  200. return -1;
  201.     }
  202.     /* Make sure source file doesn't exist */
  203.     attr = (*tclWinProcs->getFileAttributesProc)(LinkSource);
  204.     if (attr != 0xffffffff) {
  205. Tcl_SetErrno(EEXIST);
  206. return -1;
  207.     }
  208.     /* Get the full path referenced by the directory */
  209.     if (!(*tclWinProcs->getFullPathNameProc)(LinkSource, 
  210.   MAX_PATH, tempFileName, &tempFilePart)) {
  211. /* Invalid file */
  212. TclWinConvertError(GetLastError());
  213. return -1;
  214.     }
  215.     /* Check the target */
  216.     attr = (*tclWinProcs->getFileAttributesProc)(LinkTarget);
  217.     if (attr == 0xffffffff) {
  218. /* The target doesn't exist */
  219. TclWinConvertError(GetLastError());
  220. return -1;
  221.     } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  222. /* It is a file */
  223. if (tclWinProcs->createHardLinkProc == NULL) {
  224.     Tcl_SetErrno(ENOTDIR);
  225.     return -1;
  226. }
  227. if (linkAction & TCL_CREATE_HARD_LINK) {
  228.     if (!(*tclWinProcs->createHardLinkProc)(LinkSource, LinkTarget, NULL)) {
  229. TclWinConvertError(GetLastError());
  230. return -1;
  231.     }
  232.     return 0;
  233. } else if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {
  234.     /* Can't symlink files */
  235.     Tcl_SetErrno(ENOTDIR);
  236.     return -1;
  237. } else {
  238.     Tcl_SetErrno(ENODEV);
  239.     return -1;
  240. }
  241.     } else {
  242. if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {
  243.     return WinSymLinkDirectory(LinkSource, LinkTarget);
  244. } else if (linkAction & TCL_CREATE_HARD_LINK) {
  245.     /* Can't hard link directories */
  246.     Tcl_SetErrno(EISDIR);
  247.     return -1;
  248. } else {
  249.     Tcl_SetErrno(ENODEV);
  250.     return -1;
  251. }
  252.     }
  253. }
  254. /*
  255.  *--------------------------------------------------------------------
  256.  *
  257.  * WinReadLink
  258.  *
  259.  * What does 'LinkSource' point to? 
  260.  *--------------------------------------------------------------------
  261.  */
  262. static Tcl_Obj* 
  263. WinReadLink(LinkSource)
  264.     CONST TCHAR* LinkSource;
  265. {
  266.     WCHAR tempFileName[MAX_PATH];
  267.     TCHAR* tempFilePart;
  268.     int         attr;
  269.     
  270.     /* Get the full path referenced by the target */
  271.     if (!(*tclWinProcs->getFullPathNameProc)(LinkSource, 
  272.   MAX_PATH, tempFileName, &tempFilePart)) {
  273. /* Invalid file */
  274. TclWinConvertError(GetLastError());
  275. return NULL;
  276.     }
  277.     /* Make sure source file does exist */
  278.     attr = (*tclWinProcs->getFileAttributesProc)(LinkSource);
  279.     if (attr == 0xffffffff) {
  280. /* The source doesn't exist */
  281. TclWinConvertError(GetLastError());
  282. return NULL;
  283.     } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  284. /* It is a file - this is not yet supported */
  285. Tcl_SetErrno(ENOTDIR);
  286. return NULL;
  287.     } else {
  288. return WinReadLinkDirectory(LinkSource);
  289.     }
  290. }
  291. /*
  292.  *--------------------------------------------------------------------
  293.  *
  294.  * WinSymLinkDirectory
  295.  *
  296.  * This routine creates a NTFS junction, using the undocumented
  297.  * FSCTL_SET_REPARSE_POINT structure Win2K uses for mount points
  298.  * and junctions.
  299.  *
  300.  * Assumption that LinkTarget is a valid, existing directory.
  301.  * 
  302.  * Returns zero on success.
  303.  *--------------------------------------------------------------------
  304.  */
  305. static int 
  306. WinSymLinkDirectory(LinkDirectory, LinkTarget)
  307.     CONST TCHAR* LinkDirectory;
  308.     CONST TCHAR* LinkTarget;
  309. {
  310.     DUMMY_REPARSE_BUFFER dummy;
  311.     REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy;
  312.     int         len;
  313.     WCHAR       nativeTarget[MAX_PATH];
  314.     WCHAR       *loop;
  315.     
  316.     /* Make the native target name */
  317.     memcpy((VOID*)nativeTarget, (VOID*)L"\??\", 4*sizeof(WCHAR));
  318.     memcpy((VOID*)(nativeTarget + 4), (VOID*)LinkTarget, 
  319.    sizeof(WCHAR)*(1+wcslen((WCHAR*)LinkTarget)));
  320.     len = wcslen(nativeTarget);
  321.     /* 
  322.      * We must have backslashes only.  This is VERY IMPORTANT.
  323.      * If we have any forward slashes everything appears to work,
  324.      * but the resulting symlink is useless!
  325.      */
  326.     for (loop = nativeTarget; *loop != 0; loop++) {
  327. if (*loop == L'/') *loop = L'\';
  328.     }
  329.     if ((nativeTarget[len-1] == L'\') && (nativeTarget[len-2] != L':')) {
  330. nativeTarget[len-1] = 0;
  331.     }
  332.     
  333.     /* Build the reparse info */
  334.     memset(reparseBuffer, 0, sizeof(DUMMY_REPARSE_BUFFER));
  335.     reparseBuffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
  336.     reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength = 
  337.       wcslen(nativeTarget) * sizeof(WCHAR);
  338.     reparseBuffer->Reserved = 0;
  339.     reparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength = 0;
  340.     reparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset = 
  341.       reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength 
  342.       + sizeof(WCHAR);
  343.     memcpy(reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer, nativeTarget, 
  344.       sizeof(WCHAR) 
  345.       + reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength);
  346.     reparseBuffer->ReparseDataLength = 
  347.       reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + 12;
  348.     return NativeWriteReparse(LinkDirectory, reparseBuffer);
  349. }
  350. /*
  351.  *--------------------------------------------------------------------
  352.  *
  353.  * TclWinSymLinkCopyDirectory
  354.  *
  355.  * Copy a Windows NTFS junction.  This function assumes that
  356.  * LinkOriginal exists and is a valid junction point, and that
  357.  * LinkCopy does not exist.
  358.  * 
  359.  * Returns zero on success.
  360.  *--------------------------------------------------------------------
  361.  */
  362. int 
  363. TclWinSymLinkCopyDirectory(LinkOriginal, LinkCopy)
  364.     CONST TCHAR* LinkOriginal;  /* Existing junction - reparse point */
  365.     CONST TCHAR* LinkCopy;      /* Will become a duplicate junction */
  366. {
  367.     DUMMY_REPARSE_BUFFER dummy;
  368.     REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy;
  369.     
  370.     if (NativeReadReparse(LinkOriginal, reparseBuffer)) {
  371. return -1;
  372.     }
  373.     return NativeWriteReparse(LinkCopy, reparseBuffer);
  374. }
  375. /*
  376.  *--------------------------------------------------------------------
  377.  *
  378.  * TclWinSymLinkDelete
  379.  *
  380.  * Delete a Windows NTFS junction.  Once the junction information
  381.  * is deleted, the filesystem object becomes an ordinary directory.
  382.  * Unless 'linkOnly' is given, that directory is also removed.
  383.  * 
  384.  * Assumption that LinkOriginal is a valid, existing junction.
  385.  * 
  386.  * Returns zero on success.
  387.  *--------------------------------------------------------------------
  388.  */
  389. int 
  390. TclWinSymLinkDelete(LinkOriginal, linkOnly)
  391.     CONST TCHAR* LinkOriginal;
  392.     int linkOnly;
  393. {
  394.     /* It is a symbolic link -- remove it */
  395.     DUMMY_REPARSE_BUFFER dummy;
  396.     REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy;
  397.     HANDLE hFile;
  398.     DWORD returnedLength;
  399.     memset(reparseBuffer, 0, sizeof(DUMMY_REPARSE_BUFFER));
  400.     reparseBuffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
  401.     hFile = (*tclWinProcs->createFileProc)(LinkOriginal, GENERIC_WRITE, 0,
  402. NULL, OPEN_EXISTING, 
  403. FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
  404.     if (hFile != INVALID_HANDLE_VALUE) {
  405. if (!DeviceIoControl(hFile, FSCTL_DELETE_REPARSE_POINT, reparseBuffer, 
  406.      REPARSE_MOUNTPOINT_HEADER_SIZE,
  407.      NULL, 0, &returnedLength, NULL)) {
  408.     /* Error setting junction */
  409.     TclWinConvertError(GetLastError());
  410.     CloseHandle(hFile);
  411. } else {
  412.     CloseHandle(hFile);
  413.     if (!linkOnly) {
  414.         (*tclWinProcs->removeDirectoryProc)(LinkOriginal);
  415.     }
  416.     return 0;
  417. }
  418.     }
  419.     return -1;
  420. }
  421. /*
  422.  *--------------------------------------------------------------------
  423.  *
  424.  * WinReadLinkDirectory
  425.  *
  426.  * This routine reads a NTFS junction, using the undocumented
  427.  * FSCTL_GET_REPARSE_POINT structure Win2K uses for mount points
  428.  * and junctions.
  429.  *
  430.  * Assumption that LinkDirectory is a valid, existing directory.
  431.  * 
  432.  * Returns a Tcl_Obj with refCount of 1 (i.e. owned by the caller),
  433.  * or NULL if anything went wrong.
  434.  * 
  435.  * In the future we should enhance this to return a path object
  436.  * rather than a string.
  437.  *--------------------------------------------------------------------
  438.  */
  439. static Tcl_Obj* 
  440. WinReadLinkDirectory(LinkDirectory)
  441.     CONST TCHAR* LinkDirectory;
  442. {
  443.     int attr;
  444.     DUMMY_REPARSE_BUFFER dummy;
  445.     REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy;
  446.     
  447.     attr = (*tclWinProcs->getFileAttributesProc)(LinkDirectory);
  448.     if (!(attr & FILE_ATTRIBUTE_REPARSE_POINT)) {
  449. Tcl_SetErrno(EINVAL);
  450. return NULL;
  451.     }
  452.     if (NativeReadReparse(LinkDirectory, reparseBuffer)) {
  453.         return NULL;
  454.     }
  455.     
  456.     switch (reparseBuffer->ReparseTag) {
  457. case 0x80000000|IO_REPARSE_TAG_SYMBOLIC_LINK: 
  458. case IO_REPARSE_TAG_SYMBOLIC_LINK: 
  459. case IO_REPARSE_TAG_MOUNT_POINT: {
  460.     Tcl_Obj *retVal;
  461.     Tcl_DString ds;
  462.     CONST char *copy;
  463.     int len;
  464.     int offset = 0;
  465.     
  466.     /* 
  467.      * Certain native path representations on Windows have a
  468.      * special prefix to indicate that they are to be treated
  469.      * specially.  For example extremely long paths, or symlinks,
  470.      * or volumes mounted inside directories.
  471.      * 
  472.      * There is an assumption in this code that 'wide' interfaces
  473.      * are being used (see tclWin32Dll.c), which is true for the
  474.      * only systems which support reparse tags at present.  If
  475.      * that changes in the future, this code will have to be
  476.      * generalised.
  477.      */
  478.     if (reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer[0] 
  479.                                                  == L'\') {
  480. /* Check whether this is a mounted volume */
  481. if (wcsncmp(reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer, 
  482.     L"\??\Volume{",11) == 0) {
  483.     char drive;
  484.     /* 
  485.      * There is some confusion between ?? and \? which
  486.      * we have to fix here.  It doesn't seem very well
  487.      * documented.
  488.      */
  489.     reparseBuffer->SymbolicLinkReparseBuffer
  490.                                       .PathBuffer[1] = L'\';
  491.     /* 
  492.      * Check if a corresponding drive letter exists, and
  493.      * use that if it is found
  494.      */
  495.     drive = TclWinDriveLetterForVolMountPoint(reparseBuffer
  496. ->SymbolicLinkReparseBuffer.PathBuffer);
  497.     if (drive != -1) {
  498. char driveSpec[3] = {
  499.     drive, ':', ''
  500. };
  501. retVal = Tcl_NewStringObj(driveSpec,2);
  502. Tcl_IncrRefCount(retVal);
  503. return retVal;
  504.     }
  505.     /* 
  506.      * This is actually a mounted drive, which doesn't
  507.      * exists as a DOS drive letter.  This means the path
  508.      * isn't actually a link, although we partially treat
  509.      * it like one ('file type' will return 'link'), but
  510.      * then the link will actually just be treated like
  511.      * an ordinary directory.  I don't believe any
  512.      * serious inconsistency will arise from this, but it
  513.      * is something to be aware of.
  514.      */
  515.     Tcl_SetErrno(EINVAL);
  516.     return NULL;
  517. } else if (wcsncmp(reparseBuffer->SymbolicLinkReparseBuffer
  518.    .PathBuffer, L"\\?\",4) == 0) {
  519.     /* Strip off the prefix */
  520.     offset = 4;
  521. } else if (wcsncmp(reparseBuffer->SymbolicLinkReparseBuffer
  522.    .PathBuffer, L"\??\",4) == 0) {
  523.     /* Strip off the prefix */
  524.     offset = 4;
  525. }
  526.     }
  527.     
  528.     Tcl_WinTCharToUtf(
  529. (CONST char*)reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer, 
  530. (int)reparseBuffer->SymbolicLinkReparseBuffer
  531. .SubstituteNameLength, &ds);
  532.     copy = Tcl_DStringValue(&ds)+offset;
  533.     len = Tcl_DStringLength(&ds)-offset;
  534.     retVal = Tcl_NewStringObj(copy,len);
  535.     Tcl_IncrRefCount(retVal);
  536.     Tcl_DStringFree(&ds);
  537.     return retVal;
  538. }
  539.     }
  540.     Tcl_SetErrno(EINVAL);
  541.     return NULL;
  542. }
  543. /*
  544.  *--------------------------------------------------------------------
  545.  *
  546.  * NativeReadReparse
  547.  *
  548.  * Read the junction/reparse information from a given NTFS directory.
  549.  *
  550.  * Assumption that LinkDirectory is a valid, existing directory.
  551.  * 
  552.  * Returns zero on success.
  553.  *--------------------------------------------------------------------
  554.  */
  555. static int 
  556. NativeReadReparse(LinkDirectory, buffer)
  557.     CONST TCHAR* LinkDirectory;   /* The junction to read */
  558.     REPARSE_DATA_BUFFER* buffer;  /* Pointer to buffer. Cannot be NULL */
  559. {
  560.     HANDLE hFile;
  561.     DWORD returnedLength;
  562.    
  563.     hFile = (*tclWinProcs->createFileProc)(LinkDirectory, GENERIC_READ, 0,
  564. NULL, OPEN_EXISTING, 
  565. FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
  566.     if (hFile == INVALID_HANDLE_VALUE) {
  567. /* Error creating directory */
  568. TclWinConvertError(GetLastError());
  569. return -1;
  570.     }
  571.     /* Get the link */
  572.     if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 
  573.  0, buffer, sizeof(DUMMY_REPARSE_BUFFER), 
  574.  &returnedLength, NULL)) {
  575. /* Error setting junction */
  576. TclWinConvertError(GetLastError());
  577. CloseHandle(hFile);
  578. return -1;
  579.     }
  580.     CloseHandle(hFile);
  581.     
  582.     if (!IsReparseTagValid(buffer->ReparseTag)) {
  583. Tcl_SetErrno(EINVAL);
  584. return -1;
  585.     }
  586.     return 0;
  587. }
  588. /*
  589.  *--------------------------------------------------------------------
  590.  *
  591.  * NativeWriteReparse
  592.  *
  593.  * Write the reparse information for a given directory.
  594.  * 
  595.  * Assumption that LinkDirectory does not exist.
  596.  *--------------------------------------------------------------------
  597.  */
  598. static int 
  599. NativeWriteReparse(LinkDirectory, buffer)
  600.     CONST TCHAR* LinkDirectory;
  601.     REPARSE_DATA_BUFFER* buffer;
  602. {
  603.     HANDLE hFile;
  604.     DWORD returnedLength;
  605.     
  606.     /* Create the directory - it must not already exist */
  607.     if ((*tclWinProcs->createDirectoryProc)(LinkDirectory, NULL) == 0) {
  608. /* Error creating directory */
  609. TclWinConvertError(GetLastError());
  610. return -1;
  611.     }
  612.     hFile = (*tclWinProcs->createFileProc)(LinkDirectory, GENERIC_WRITE, 0,
  613. NULL, OPEN_EXISTING, 
  614. FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
  615.     if (hFile == INVALID_HANDLE_VALUE) {
  616. /* Error creating directory */
  617. TclWinConvertError(GetLastError());
  618. return -1;
  619.     }
  620.     /* Set the link */
  621.     if (!DeviceIoControl(hFile, FSCTL_SET_REPARSE_POINT, buffer, 
  622.  (DWORD) buffer->ReparseDataLength 
  623.  + REPARSE_MOUNTPOINT_HEADER_SIZE,
  624.  NULL, 0, &returnedLength, NULL)) {
  625. /* Error setting junction */
  626. TclWinConvertError(GetLastError());
  627. CloseHandle(hFile);
  628. (*tclWinProcs->removeDirectoryProc)(LinkDirectory);
  629. return -1;
  630.     }
  631.     CloseHandle(hFile);
  632.     /* We succeeded */
  633.     return 0;
  634. }
  635. /*
  636.  *---------------------------------------------------------------------------
  637.  *
  638.  * TclpFindExecutable --
  639.  *
  640.  * This procedure computes the absolute path name of the current
  641.  * application, given its argv[0] value.
  642.  *
  643.  * Results:
  644.  * A clean UTF string that is the path to the executable.  At this
  645.  * point we may not know the system encoding, but we convert the
  646.  * string value to UTF-8 using core Windows functions.  The path name
  647.  * contains ASCII string and '/' chars do not conflict with other UTF
  648.  * chars.
  649.  *
  650.  * Side effects:
  651.  * The variable tclNativeExecutableName gets filled in with the file
  652.  * name for the application, if we figured it out.  If we couldn't
  653.  * figure it out, tclNativeExecutableName is set to NULL.
  654.  *
  655.  *---------------------------------------------------------------------------
  656.  */
  657. char *
  658. TclpFindExecutable(argv0)
  659.     CONST char *argv0; /* The value of the application's argv[0]
  660.  * (native). */
  661. {
  662.     WCHAR wName[MAX_PATH];
  663.     char name[MAX_PATH * TCL_UTF_MAX];
  664.     if (argv0 == NULL) {
  665. return NULL;
  666.     }
  667.     if (tclNativeExecutableName != NULL) {
  668. return tclNativeExecutableName;
  669.     }
  670.     /*
  671.      * Under Windows we ignore argv0, and return the path for the file used to
  672.      * create this process.
  673.      */
  674.     if (GetModuleFileNameW(NULL, wName, MAX_PATH) == 0) {
  675. GetModuleFileNameA(NULL, name, sizeof(name));
  676.     } else {
  677. WideCharToMultiByte(CP_UTF8, 0, wName, -1, 
  678. name, sizeof(name), NULL, NULL);
  679.     }
  680.     tclNativeExecutableName = ckalloc((unsigned) (strlen(name) + 1));
  681.     strcpy(tclNativeExecutableName, name);
  682.     TclWinNoBackslash(tclNativeExecutableName);
  683.     return tclNativeExecutableName;
  684. }
  685. /*
  686.  *----------------------------------------------------------------------
  687.  *
  688.  * TclpMatchInDirectory --
  689.  *
  690.  * This routine is used by the globbing code to search a
  691.  * directory for all files which match a given pattern.
  692.  *
  693.  * Results: 
  694.  *
  695.  * The return value is a standard Tcl result indicating whether an
  696.  * error occurred in globbing.  Errors are left in interp, good
  697.  * results are lappended to resultPtr (which must be a valid object)
  698.  *
  699.  * Side effects:
  700.  * None.
  701.  *
  702.  *---------------------------------------------------------------------- */
  703. int
  704. TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
  705.     Tcl_Interp *interp; /* Interpreter to receive errors. */
  706.     Tcl_Obj *resultPtr; /* List object to lappend results. */
  707.     Tcl_Obj *pathPtr;         /* Contains path to directory to search. */
  708.     CONST char *pattern; /* Pattern to match against. */
  709.     Tcl_GlobTypeData *types; /* Object containing list of acceptable types.
  710.  * May be NULL. In particular the directory
  711.  * flag is very important. */
  712. {
  713.     CONST TCHAR *native;
  714.     if (pattern == NULL || (*pattern == '')) {
  715. Tcl_Obj *norm = Tcl_FSGetNormalizedPath(NULL, pathPtr);
  716. if (norm != NULL) {
  717.     /* Match a single file directly */
  718.     int len;
  719.     DWORD attr;
  720.     CONST char *str = Tcl_GetStringFromObj(norm,&len);
  721.     native = (CONST TCHAR*) Tcl_FSGetNativePath(pathPtr);
  722.     
  723.     if (tclWinProcs->getFileAttributesExProc == NULL) {
  724. attr = (*tclWinProcs->getFileAttributesProc)(native);
  725. if (attr == 0xffffffff) {
  726.     return TCL_OK;
  727. }
  728.     } else {
  729. WIN32_FILE_ATTRIBUTE_DATA data;
  730. if ((*tclWinProcs->getFileAttributesExProc)(native,
  731. GetFileExInfoStandard, &data) != TRUE) {
  732.     return TCL_OK;
  733. }
  734. attr = data.dwFileAttributes;
  735.     }
  736.     if (NativeMatchType(WinIsDrive(str,len), attr, 
  737. native, types)) {
  738. Tcl_ListObjAppendElement(interp, resultPtr, pathPtr);
  739.     }
  740. }
  741. return TCL_OK;
  742.     } else {
  743. DWORD attr;
  744. HANDLE handle;
  745. WIN32_FIND_DATAT data;
  746. CONST char *dirName;
  747. int dirLength;
  748. int matchSpecialDots;
  749. Tcl_DString ds;        /* native encoding of dir */
  750. Tcl_DString dsOrig;    /* utf-8 encoding of dir */
  751. Tcl_DString dirString; /* utf-8 encoding of dir with 's */
  752. Tcl_Obj *fileNamePtr;
  753. /*
  754.  * Convert the path to normalized form since some interfaces only
  755.  * accept backslashes.  Also, ensure that the directory ends with a
  756.  * separator character.
  757.  */
  758. fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
  759. if (fileNamePtr == NULL) {
  760.     return TCL_ERROR;
  761. }
  762. Tcl_DStringInit(&dsOrig);
  763. dirName = Tcl_GetStringFromObj(fileNamePtr, &dirLength);
  764. Tcl_DStringAppend(&dsOrig, dirName, dirLength);
  765. Tcl_DStringInit(&dirString);
  766. if (dirLength == 0) {
  767.     Tcl_DStringAppend(&dirString, ".\", 2);
  768. } else {
  769.     char *p;
  770.     Tcl_DStringAppend(&dirString, dirName, dirLength);
  771.     for (p = Tcl_DStringValue(&dirString); *p != ''; p++) {
  772. if (*p == '/') {
  773.     *p = '\';
  774. }
  775.     }
  776.     p--;
  777.     /* Make sure we have a trailing directory delimiter */
  778.     if ((*p != '\') && (*p != ':')) {
  779. Tcl_DStringAppend(&dirString, "\", 1);
  780. Tcl_DStringAppend(&dsOrig, "/", 1);
  781. dirLength++;
  782.     }
  783. }
  784. dirName = Tcl_DStringValue(&dirString);
  785. Tcl_DecrRefCount(fileNamePtr);
  786. /*
  787.  * First verify that the specified path is actually a directory.
  788.  */
  789. native = Tcl_WinUtfToTChar(dirName, Tcl_DStringLength(&dirString),
  790. &ds);
  791. attr = (*tclWinProcs->getFileAttributesProc)(native);
  792. Tcl_DStringFree(&ds);
  793. if ((attr == 0xffffffff) || ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)) {
  794.     Tcl_DStringFree(&dirString);
  795.     return TCL_OK;
  796. }
  797. /*
  798.  * We need to check all files in the directory, so append a *.*
  799.  * to the path. 
  800.  */
  801. dirName = Tcl_DStringAppend(&dirString, "*.*", 3);
  802. native = Tcl_WinUtfToTChar(dirName, -1, &ds);
  803. handle = (*tclWinProcs->findFirstFileProc)(native, &data);
  804. if (handle == INVALID_HANDLE_VALUE) {
  805.     TclWinConvertError(GetLastError());
  806.     Tcl_DStringFree(&ds);
  807.     Tcl_DStringFree(&dirString);
  808.     Tcl_ResetResult(interp);
  809.     Tcl_AppendResult(interp, "couldn't read directory "",
  810.     Tcl_DStringValue(&dsOrig), "": ", 
  811.     Tcl_PosixError(interp), (char *) NULL);
  812.     Tcl_DStringFree(&dsOrig);
  813.     return TCL_ERROR;
  814. }
  815. Tcl_DStringFree(&ds);
  816. /*
  817.  * Check to see if the pattern should match the special
  818.  * . and .. names, referring to the current directory,
  819.  * or the directory above.  We need a special check for
  820.  * this because paths beginning with a dot are not considered
  821.  * hidden on Windows, and so otherwise a relative glob like
  822.  * 'glob -join * *' will actually return './. ../..' etc.
  823.  */
  824. if ((pattern[0] == '.')
  825. || ((pattern[0] == '\') && (pattern[1] == '.'))) {
  826.     matchSpecialDots = 1;
  827. } else {
  828.     matchSpecialDots = 0;
  829. }
  830. /*
  831.  * Now iterate over all of the files in the directory, starting
  832.  * with the first one we found.
  833.  */
  834. do {
  835.     CONST char *utfname;
  836.     int checkDrive = 0;
  837.     int isDrive;
  838.     DWORD attr;
  839.     
  840.     if (tclWinProcs->useWide) {
  841. native = (CONST TCHAR *) data.w.cFileName;
  842. attr = data.w.dwFileAttributes;
  843.     } else {
  844. native = (CONST TCHAR *) data.a.cFileName;
  845. attr = data.a.dwFileAttributes;
  846.     }
  847.     
  848.     utfname = Tcl_WinTCharToUtf(native, -1, &ds);
  849.     if (!matchSpecialDots) {
  850. /* If it is exactly '.' or '..' then we ignore it */
  851. if ((utfname[0] == '.') && (utfname[1] == '' 
  852. || (utfname[1] == '.' && utfname[2] == ''))) {
  853.     Tcl_DStringFree(&ds);
  854.     continue;
  855. }
  856.     } else if (utfname[0] == '.' && utfname[1] == '.'
  857.     && utfname[2] == '') {
  858. /* 
  859.  * Have to check if this is a drive below, so we can
  860.  * correctly match 'hidden' and not hidden files.
  861.  */
  862. checkDrive = 1;
  863.     }
  864.     
  865.     /*
  866.      * Check to see if the file matches the pattern.  Note that
  867.      * we are ignoring the case sensitivity flag because Windows
  868.      * doesn't honor case even if the volume is case sensitive.
  869.      * If the volume also doesn't preserve case, then we
  870.      * previously returned the lower case form of the name.  This
  871.      * didn't seem quite right since there are
  872.      * non-case-preserving volumes that actually return mixed
  873.      * case.  So now we are returning exactly what we get from
  874.      * the system.
  875.      */
  876.     if (Tcl_StringCaseMatch(utfname, pattern, 1)) {
  877. /*
  878.  * If the file matches, then we need to process the remainder
  879.  * of the path.
  880.  */
  881. if (checkDrive) {
  882.     CONST char *fullname = Tcl_DStringAppend(&dsOrig, utfname,
  883.     Tcl_DStringLength(&ds));
  884.     isDrive = WinIsDrive(fullname, Tcl_DStringLength(&dsOrig));
  885.     Tcl_DStringSetLength(&dsOrig, dirLength);
  886. } else {
  887.     isDrive = 0;
  888. }
  889. if (NativeMatchType(isDrive, attr, native, types)) {
  890.     Tcl_ListObjAppendElement(interp, resultPtr, 
  891.     TclNewFSPathObj(pathPtr, utfname,
  892.     Tcl_DStringLength(&ds)));
  893. }
  894.     }
  895.     /*
  896.      * Free ds here to ensure that native is valid above.
  897.      */
  898.     Tcl_DStringFree(&ds);
  899. } while ((*tclWinProcs->findNextFileProc)(handle, &data) == TRUE);
  900. FindClose(handle);
  901. Tcl_DStringFree(&dirString);
  902. Tcl_DStringFree(&dsOrig);
  903. return TCL_OK;
  904.     }
  905. }
  906. /* 
  907.  * Does the given path represent a root volume?  We need this special
  908.  * case because for NTFS root volumes, the getFileAttributesProc returns
  909.  * a 'hidden' attribute when it should not.
  910.  */
  911. static int
  912. WinIsDrive(
  913.     CONST char *name,     /* Name (UTF-8) */
  914.     int len)              /* Length of name */
  915. {
  916.     int remove = 0;
  917.     while (len > 4) {
  918.         if ((name[len-1] != '.' || name[len-2] != '.') 
  919.     || (name[len-3] != '/' && name[len-3] != '\')) {
  920.             /* We don't have '/..' at the end */
  921.     if (remove == 0) {
  922.         break;
  923.     }
  924.     remove--;
  925.     while (len > 0) {
  926. len--;
  927. if (name[len] == '/' || name[len] == '\') {
  928.     break;
  929. }
  930.     }
  931.     if (len < 4) {
  932.         len++;
  933. break;
  934.     }
  935.         } else {
  936.     /* We do have '/..' */
  937.     len -= 3;
  938.     remove++;
  939.         }
  940.     }
  941.     if (len < 4) {
  942. if (len == 0) {
  943.     /* 
  944.      * Not sure if this is possible, but we pass it on
  945.      * anyway 
  946.      */
  947. } else if (len == 1 && (name[0] == '/' || name[0] == '\')) {
  948.     /* Path is pointing to the root volume */
  949.     return 1;
  950. } else if ((name[1] == ':') 
  951.    && (len == 2 || (name[2] == '/' || name[2] == '\'))) {
  952.     /* Path is of the form 'x:' or 'x:/' or 'x:' */
  953.     return 1;
  954. }
  955.     }
  956.     return 0;
  957. }
  958. /* 
  959.  * Does the given path represent a reserved window path name?  If not
  960.  * return 0, if true, return the number of characters of the path that
  961.  * we actually want (not any trailing :).
  962.  */
  963. static int WinIsReserved(
  964.    CONST char *path)    /* Path in UTF-8  */
  965. {
  966.     if ((path[0] == 'c' || path[0] == 'C') 
  967. && (path[1] == 'o' || path[1] == 'O')) {
  968. if ((path[2] == 'm' || path[2] == 'M')
  969.     && path[3] >= '1' && path[3] <= '4') {
  970.     /* May have match for 'com[1-4]:?', which is a serial port */
  971.     if (path[4] == '') {
  972. return 4;
  973.     } else if (path [4] == ':' && path[5] == '') {
  974. return 4;
  975.     }
  976. } else if ((path[2] == 'n' || path[2] == 'N') && path[3] == '') {
  977.     /* Have match for 'con' */
  978.     return 3;
  979. }
  980.     } else if ((path[0] == 'l' || path[0] == 'L')
  981.        && (path[1] == 'p' || path[1] == 'P')
  982.        && (path[2] == 't' || path[2] == 'T')) {
  983. if (path[3] >= '1' && path[3] <= '3') {
  984.     /* May have match for 'lpt[1-3]:?' */
  985.     if (path[4] == '') {
  986. return 4;
  987.     } else if (path [4] == ':' && path[5] == '') {
  988. return 4;
  989.     }
  990. }
  991.     } else if (stricmp(path, "prn") == 0) {
  992. /* Have match for 'prn' */
  993. return 3;
  994.     } else if (stricmp(path, "nul") == 0) {
  995. /* Have match for 'nul' */
  996. return 3;
  997.     } else if (stricmp(path, "aux") == 0) {
  998. /* Have match for 'aux' */
  999. return 3;
  1000.     }
  1001.     return 0;
  1002. }
  1003. /*
  1004.  *----------------------------------------------------------------------
  1005.  * 
  1006.  * NativeMatchType --
  1007.  * 
  1008.  * This function needs a special case for a path which is a root
  1009.  * volume, because for NTFS root volumes, the getFileAttributesProc
  1010.  * returns a 'hidden' attribute when it should not.
  1011.  * 
  1012.  * We never make any calss to a 'get attributes' routine here,
  1013.  * since we have arranged things so that our caller already knows
  1014.  * such information.
  1015.  * 
  1016.  * Results:
  1017.  *  0 = file doesn't match
  1018.  *  1 = file matches
  1019.  * 
  1020.  *----------------------------------------------------------------------
  1021.  */
  1022. static int 
  1023. NativeMatchType(
  1024.     int isDrive,              /* Is this a drive */
  1025.     DWORD attr,               /* We already know the attributes 
  1026.                                * for the file */
  1027.     CONST TCHAR* nativeName,  /* Native path to check */
  1028.     Tcl_GlobTypeData *types)  /* Type description to match against */
  1029. {
  1030.     /*
  1031.      * 'attr' represents the attributes of the file, but we only
  1032.      * want to retrieve this info if it is absolutely necessary
  1033.      * because it is an expensive call.  Unfortunately, to deal
  1034.      * with hidden files properly, we must always retrieve it.
  1035.      */
  1036.     if (types == NULL) {
  1037. /* If invisible, don't return the file */
  1038. if (attr & FILE_ATTRIBUTE_HIDDEN && !isDrive) {
  1039.     return 0;
  1040. }
  1041.     } else {
  1042. if (attr & FILE_ATTRIBUTE_HIDDEN && !isDrive) {
  1043.     /* If invisible */
  1044.     if ((types->perm == 0) || 
  1045.     !(types->perm & TCL_GLOB_PERM_HIDDEN)) {
  1046. return 0;
  1047.     }
  1048. } else {
  1049.     /* Visible */
  1050.     if (types->perm & TCL_GLOB_PERM_HIDDEN) {
  1051. return 0;
  1052.     }
  1053. }
  1054. if (types->perm != 0) {
  1055.     if (
  1056. ((types->perm & TCL_GLOB_PERM_RONLY) &&
  1057. !(attr & FILE_ATTRIBUTE_READONLY)) ||
  1058. ((types->perm & TCL_GLOB_PERM_R) &&
  1059. (0 /* File exists => R_OK on Windows */)) ||
  1060. ((types->perm & TCL_GLOB_PERM_W) &&
  1061. (attr & FILE_ATTRIBUTE_READONLY)) ||
  1062. ((types->perm & TCL_GLOB_PERM_X) &&
  1063. (!(attr & FILE_ATTRIBUTE_DIRECTORY)
  1064.  && !NativeIsExec(nativeName)))
  1065. ) {
  1066. return 0;
  1067.     }
  1068. }
  1069. if ((types->type & TCL_GLOB_TYPE_DIR) 
  1070.     && (attr & FILE_ATTRIBUTE_DIRECTORY)) {
  1071.     /* Quicker test for directory, which is a common case */
  1072.     return 1;
  1073. } else if (types->type != 0) {
  1074.     unsigned short st_mode;
  1075.     int isExec = NativeIsExec(nativeName);
  1076.     
  1077.     st_mode = NativeStatMode(attr, 0, isExec);
  1078.     /*
  1079.      * In order bcdpfls as in 'find -t'
  1080.      */
  1081.     if (
  1082. ((types->type & TCL_GLOB_TYPE_BLOCK) &&
  1083. S_ISBLK(st_mode)) ||
  1084. ((types->type & TCL_GLOB_TYPE_CHAR) &&
  1085. S_ISCHR(st_mode)) ||
  1086. ((types->type & TCL_GLOB_TYPE_DIR) &&
  1087. S_ISDIR(st_mode)) ||
  1088. ((types->type & TCL_GLOB_TYPE_PIPE) &&
  1089. S_ISFIFO(st_mode)) ||
  1090. ((types->type & TCL_GLOB_TYPE_FILE) &&
  1091. S_ISREG(st_mode))
  1092. #ifdef S_ISSOCK
  1093. || ((types->type & TCL_GLOB_TYPE_SOCK) &&
  1094. S_ISSOCK(st_mode))
  1095. #endif
  1096. ) {
  1097. /* Do nothing -- this file is ok */
  1098.     } else {
  1099. #ifdef S_ISLNK
  1100. if (types->type & TCL_GLOB_TYPE_LINK) {
  1101.     st_mode = NativeStatMode(attr, 1, isExec);
  1102.     if (S_ISLNK(st_mode)) {
  1103. return 1;
  1104.     }
  1105. }
  1106. #endif
  1107. return 0;
  1108.     }
  1109. }
  1110.     } 
  1111.     return 1;
  1112. }
  1113. /*
  1114.  *----------------------------------------------------------------------
  1115.  *
  1116.  * TclpGetUserHome --
  1117.  *
  1118.  * This function takes the passed in user name and finds the
  1119.  * corresponding home directory specified in the password file.
  1120.  *
  1121.  * Results:
  1122.  * The result is a pointer to a string specifying the user's home
  1123.  * directory, or NULL if the user's home directory could not be
  1124.  * determined.  Storage for the result string is allocated in
  1125.  * bufferPtr; the caller must call Tcl_DStringFree() when the result
  1126.  * is no longer needed.
  1127.  *
  1128.  * Side effects:
  1129.  * None.
  1130.  *
  1131.  *----------------------------------------------------------------------
  1132.  */
  1133. char *
  1134. TclpGetUserHome(name, bufferPtr)
  1135.     CONST char *name; /* User name for desired home directory. */
  1136.     Tcl_DString *bufferPtr; /* Uninitialized or free DString filled
  1137.  * with name of user's home directory. */
  1138. {
  1139.     char *result;
  1140.     HINSTANCE netapiInst;
  1141.     result = NULL;
  1142.     Tcl_DStringInit(bufferPtr);
  1143.     netapiInst = LoadLibraryA("netapi32.dll");
  1144.     if (netapiInst != NULL) {
  1145. NETAPIBUFFERFREEPROC *netApiBufferFreeProc;
  1146. NETGETDCNAMEPROC *netGetDCNameProc;
  1147. NETUSERGETINFOPROC *netUserGetInfoProc;
  1148. netApiBufferFreeProc = (NETAPIBUFFERFREEPROC *)
  1149. GetProcAddress(netapiInst, "NetApiBufferFree");
  1150. netGetDCNameProc = (NETGETDCNAMEPROC *) 
  1151. GetProcAddress(netapiInst, "NetGetDCName");
  1152. netUserGetInfoProc = (NETUSERGETINFOPROC *) 
  1153. GetProcAddress(netapiInst, "NetUserGetInfo");
  1154. if ((netUserGetInfoProc != NULL) && (netGetDCNameProc != NULL)
  1155. && (netApiBufferFreeProc != NULL)) {
  1156.     USER_INFO_1 *uiPtr;
  1157.     Tcl_DString ds;
  1158.     int nameLen, badDomain;
  1159.     char *domain;
  1160.     WCHAR *wName, *wHomeDir, *wDomain;
  1161.     WCHAR buf[MAX_PATH];
  1162.     badDomain = 0;
  1163.     nameLen = -1;
  1164.     wDomain = NULL;
  1165.     domain = strchr(name, '@');
  1166.     if (domain != NULL) {
  1167. Tcl_DStringInit(&ds);
  1168. wName = Tcl_UtfToUniCharDString(domain + 1, -1, &ds);
  1169. badDomain = (*netGetDCNameProc)(NULL, wName,
  1170. (LPBYTE *) &wDomain);
  1171. Tcl_DStringFree(&ds);
  1172. nameLen = domain - name;
  1173.     }
  1174.     if (badDomain == 0) {
  1175. Tcl_DStringInit(&ds);
  1176. wName = Tcl_UtfToUniCharDString(name, nameLen, &ds);
  1177. if ((*netUserGetInfoProc)(wDomain, wName, 1,
  1178. (LPBYTE *) &uiPtr) == 0) {
  1179.     wHomeDir = uiPtr->usri1_home_dir;
  1180.     if ((wHomeDir != NULL) && (wHomeDir[0] != L'')) {
  1181. Tcl_UniCharToUtfDString(wHomeDir, lstrlenW(wHomeDir),
  1182. bufferPtr);
  1183.     } else {
  1184. /* 
  1185.  * User exists but has no home dir.  Return
  1186.  * "{Windows Drive}:/users/default".
  1187.  */
  1188. GetWindowsDirectoryW(buf, MAX_PATH);
  1189. Tcl_UniCharToUtfDString(buf, 2, bufferPtr);
  1190. Tcl_DStringAppend(bufferPtr, "/users/default", -1);
  1191.     }
  1192.     result = Tcl_DStringValue(bufferPtr);
  1193.     (*netApiBufferFreeProc)((void *) uiPtr);
  1194. }
  1195. Tcl_DStringFree(&ds);
  1196.     }
  1197.     if (wDomain != NULL) {
  1198. (*netApiBufferFreeProc)((void *) wDomain);
  1199.     }
  1200. }
  1201. FreeLibrary(netapiInst);
  1202.     }
  1203.     if (result == NULL) {
  1204. /*
  1205.  * Look in the "Password Lists" section of system.ini for the 
  1206.  * local user.  There are also entries in that section that begin 
  1207.  * with a "*" character that are used by Windows for other 
  1208.  * purposes; ignore user names beginning with a "*".
  1209.  */
  1210. char buf[MAX_PATH];
  1211. if (name[0] != '*') {
  1212.     if (GetPrivateProfileStringA("Password Lists", name, "", buf, 
  1213.     MAX_PATH, "system.ini") > 0) {
  1214. /* 
  1215.  * User exists, but there is no such thing as a home 
  1216.  * directory in system.ini.  Return "{Windows drive}:/".
  1217.  */
  1218. GetWindowsDirectoryA(buf, MAX_PATH);
  1219. Tcl_DStringAppend(bufferPtr, buf, 3);
  1220. result = Tcl_DStringValue(bufferPtr);
  1221.     }
  1222. }
  1223.     }
  1224.     return result;
  1225. }
  1226. /*
  1227.  *---------------------------------------------------------------------------
  1228.  *
  1229.  * NativeAccess --
  1230.  *
  1231.  * This function replaces the library version of access(), fixing the
  1232.  * following bugs:
  1233.  * 
  1234.  * 1. access() returns that all files have execute permission.
  1235.  *
  1236.  * Results:
  1237.  * See access documentation.
  1238.  *
  1239.  * Side effects:
  1240.  * See access documentation.
  1241.  *
  1242.  *---------------------------------------------------------------------------
  1243.  */
  1244. static int
  1245. NativeAccess(
  1246.     CONST TCHAR *nativePath, /* Path of file to access (UTF-8). */
  1247.     int mode) /* Permission setting. */
  1248. {
  1249.     DWORD attr;
  1250.     attr = (*tclWinProcs->getFileAttributesProc)(nativePath);
  1251.     if (attr == 0xffffffff) {
  1252. /*
  1253.  * File doesn't exist.
  1254.  */
  1255. TclWinConvertError(GetLastError());
  1256. return -1;
  1257.     }
  1258.     if ((mode & W_OK) 
  1259.       && (tclWinProcs->getFileSecurityProc == NULL)
  1260.       && (attr & FILE_ATTRIBUTE_READONLY)) {
  1261. /*
  1262.  * We don't have the advanced 'getFileSecurityProc', and
  1263.  * our attributes say the file is not writable.  If we
  1264.  * do have 'getFileSecurityProc', we'll do a more
  1265.  * robust XP-related check below.
  1266.  */
  1267. Tcl_SetErrno(EACCES);
  1268. return -1;
  1269.     }
  1270.     if (mode & X_OK) {
  1271. if (!(attr & FILE_ATTRIBUTE_DIRECTORY) && !NativeIsExec(nativePath)) {
  1272.     /*
  1273.      * It's not a directory and doesn't have the correct extension.
  1274.      * Therefore it can't be executable
  1275.      */
  1276.     Tcl_SetErrno(EACCES);
  1277.     return -1;
  1278. }
  1279.     }
  1280.     /*
  1281.      * It looks as if the permissions are ok, but if we are on NT, 2000 or XP,
  1282.      * we have a more complex permissions structure so we try to check that.
  1283.      * The code below is remarkably complex for such a simple thing as finding
  1284.      * what permissions the OS has set for a file.
  1285.      *
  1286.      * If we are simply checking for file existence, then we don't need all
  1287.      * these complications (which are really quite slow: with this code 'file
  1288.      * readable' is 5-6 times slower than 'file exists').
  1289.      */
  1290.     if ((mode != F_OK) && (tclWinProcs->getFileSecurityProc != NULL)) {
  1291. SECURITY_DESCRIPTOR *sdPtr = NULL;
  1292. unsigned long size;
  1293. GENERIC_MAPPING genMap;
  1294. HANDLE hToken = NULL;
  1295. DWORD desiredAccess = 0;
  1296. DWORD grantedAccess = 0;
  1297. BOOL accessYesNo = FALSE;
  1298. PRIVILEGE_SET privSet;
  1299. DWORD privSetSize = sizeof(PRIVILEGE_SET);
  1300. int error;
  1301. /*
  1302.  * First find out how big the buffer needs to be
  1303.  */
  1304. size = 0;
  1305. (*tclWinProcs->getFileSecurityProc)(nativePath,
  1306. OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
  1307. | DACL_SECURITY_INFORMATION, 0, 0, &size);
  1308. /*
  1309.  * Should have failed with ERROR_INSUFFICIENT_BUFFER
  1310.  */
  1311. error = GetLastError();
  1312. if (error != ERROR_INSUFFICIENT_BUFFER) {
  1313.     /*
  1314.      * Most likely case is ERROR_ACCESS_DENIED, which we will convert
  1315.      * to EACCES - just what we want!
  1316.      */
  1317.     TclWinConvertError((DWORD)error);
  1318.     return -1;
  1319. }
  1320. /*
  1321.  * Now size contains the size of buffer needed
  1322.  */
  1323. sdPtr = (SECURITY_DESCRIPTOR *) HeapAlloc(GetProcessHeap(), 0, size);
  1324. if (sdPtr == NULL) {
  1325.     goto accessError;
  1326. }
  1327. /*
  1328.  * Call GetFileSecurity() for real
  1329.  */
  1330. if (!(*tclWinProcs->getFileSecurityProc)(nativePath,
  1331. OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
  1332. | DACL_SECURITY_INFORMATION, sdPtr, size, &size)) {
  1333.     /*
  1334.      * Error getting owner SD
  1335.      */
  1336.     goto accessError;
  1337. }
  1338. /*
  1339.  * Perform security impersonation of the user and open the
  1340.  * resulting thread token.
  1341.  */
  1342. if (!(*tclWinProcs->impersonateSelfProc)(SecurityImpersonation)) {
  1343.     /*
  1344.      * Unable to perform security impersonation.
  1345.      */
  1346.     
  1347.     goto accessError;
  1348. }
  1349. if (!(*tclWinProcs->openThreadTokenProc)(GetCurrentThread (),
  1350. TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken)) {
  1351.     /*
  1352.      * Unable to get current thread's token.
  1353.      */
  1354.     
  1355.     goto accessError;
  1356. }
  1357. (*tclWinProcs->revertToSelfProc)();
  1358. /*
  1359.  * Setup desiredAccess according to the access priveleges we are
  1360.  * checking.
  1361.  */
  1362. if (mode & R_OK) {
  1363.     desiredAccess |= FILE_GENERIC_READ;
  1364. }
  1365. if (mode & W_OK) {
  1366.     desiredAccess |= FILE_GENERIC_WRITE;
  1367. }
  1368. if (mode & X_OK) {
  1369.     desiredAccess |= FILE_GENERIC_EXECUTE;
  1370. }
  1371. memset (&genMap, 0x0, sizeof (GENERIC_MAPPING));
  1372. genMap.GenericRead = FILE_GENERIC_READ;
  1373. genMap.GenericWrite = FILE_GENERIC_WRITE;
  1374. genMap.GenericExecute = FILE_GENERIC_EXECUTE;
  1375. genMap.GenericAll = FILE_ALL_ACCESS;
  1376. /*
  1377.  * Perform access check using the token.
  1378.  */
  1379. if (!(*tclWinProcs->accessCheckProc)(sdPtr, hToken, desiredAccess,
  1380. &genMap, &privSet, &privSetSize, &grantedAccess,
  1381. &accessYesNo)) {
  1382.     /*
  1383.      * Unable to perform access check.
  1384.      */
  1385. accessError:
  1386.     TclWinConvertError(GetLastError());
  1387.     if (sdPtr != NULL) {
  1388. HeapFree(GetProcessHeap(), 0, sdPtr);
  1389.     }
  1390.     if (hToken != NULL) {
  1391. CloseHandle(hToken);
  1392.     }
  1393.     return -1;
  1394. }
  1395. /*
  1396.  * Clean up.
  1397.  */
  1398. HeapFree(GetProcessHeap (), 0, sdPtr);
  1399. CloseHandle(hToken);
  1400. if (!accessYesNo) {
  1401.     Tcl_SetErrno(EACCES);
  1402.     return -1;
  1403. }
  1404. /*
  1405.  * For directories the above checks are ok.  For files, though,
  1406.  * we must still check the 'attr' value.
  1407.  */
  1408. if ((mode & W_OK)
  1409.   && !(attr & FILE_ATTRIBUTE_DIRECTORY)
  1410.   && (attr & FILE_ATTRIBUTE_READONLY)) {
  1411.     Tcl_SetErrno(EACCES);
  1412.     return -1;
  1413. }
  1414.     }
  1415.     return 0;
  1416. }
  1417. /*
  1418.  *----------------------------------------------------------------------
  1419.  *
  1420.  * NativeIsExec --
  1421.  *
  1422.  * Determines if a path is executable.  On windows this is 
  1423.  * simply defined by whether the path ends in any of ".exe",
  1424.  * ".com", or ".bat"
  1425.  *
  1426.  * Results:
  1427.  * 1 = executable, 0 = not.
  1428.  *
  1429.  *----------------------------------------------------------------------
  1430.  */
  1431. static int
  1432. NativeIsExec(nativePath)
  1433.     CONST TCHAR *nativePath;
  1434. {
  1435.     if (tclWinProcs->useWide) {
  1436. CONST WCHAR *path;
  1437. int len;
  1438. path = (CONST WCHAR*)nativePath;
  1439. len = wcslen(path);
  1440. if (len < 5) {
  1441.     return 0;
  1442. }
  1443. if (path[len-4] != L'.') {
  1444.     return 0;
  1445. }
  1446. /*
  1447.  * Use wide-char case-insensitive comparison
  1448.  */
  1449. if ((_wcsicmp(path+len-3,L"exe") == 0)
  1450. || (_wcsicmp(path+len-3,L"com") == 0)
  1451. || (_wcsicmp(path+len-3,L"bat") == 0)) {
  1452.     return 1;
  1453. }
  1454.     } else {
  1455. CONST char *p;
  1456. /* We are only looking for pure ascii */
  1457. p = strrchr((CONST char*)nativePath, '.');
  1458. if (p != NULL) {
  1459.     p++;
  1460.     /* 
  1461.      * Note: in the old code, stat considered '.pif' files as
  1462.      * executable, whereas access did not.
  1463.      */
  1464.     if ((stricmp(p, "exe") == 0)
  1465.     || (stricmp(p, "com") == 0)
  1466.     || (stricmp(p, "bat") == 0)) {
  1467. /*
  1468.  * File that ends with .exe, .com, or .bat is executable.
  1469.  */
  1470. return 1;
  1471.     }
  1472. }
  1473.     }
  1474.     return 0;
  1475. }
  1476. /*
  1477.  *----------------------------------------------------------------------
  1478.  *
  1479.  * TclpObjChdir --
  1480.  *
  1481.  * This function replaces the library version of chdir().
  1482.  *
  1483.  * Results:
  1484.  * See chdir() documentation.
  1485.  *
  1486.  * Side effects:
  1487.  * See chdir() documentation.  
  1488.  *
  1489.  *----------------------------------------------------------------------
  1490.  */
  1491. int 
  1492. TclpObjChdir(pathPtr)
  1493.     Tcl_Obj *pathPtr;  /* Path to new working directory. */
  1494. {
  1495.     int result;
  1496.     CONST TCHAR *nativePath;
  1497. #ifdef __CYGWIN__
  1498.     extern int cygwin_conv_to_posix_path 
  1499. _ANSI_ARGS_((CONST char *, char *));
  1500.     char posixPath[MAX_PATH+1];
  1501.     CONST char *path;
  1502.     Tcl_DString ds;
  1503. #endif /* __CYGWIN__ */
  1504.     nativePath = (CONST TCHAR *) Tcl_FSGetNativePath(pathPtr);
  1505. #ifdef __CYGWIN__
  1506.     /* Cygwin chdir only groks POSIX path. */
  1507.     path = Tcl_WinTCharToUtf(nativePath, -1, &ds);
  1508.     cygwin_conv_to_posix_path(path, posixPath);
  1509.     result = (chdir(posixPath) == 0 ? 1 : 0);
  1510.     Tcl_DStringFree(&ds);
  1511. #else /* __CYGWIN__ */
  1512.     result = (*tclWinProcs->setCurrentDirectoryProc)(nativePath);
  1513. #endif /* __CYGWIN__ */
  1514.     if (result == 0) {
  1515. TclWinConvertError(GetLastError());
  1516. return -1;
  1517.     }
  1518.     return 0;
  1519. }
  1520. #ifdef __CYGWIN__
  1521. /*
  1522.  *---------------------------------------------------------------------------
  1523.  *
  1524.  * TclpReadlink --
  1525.  *
  1526.  *     This function replaces the library version of readlink().
  1527.  *
  1528.  * Results:
  1529.  *     The result is a pointer to a string specifying the contents
  1530.  *     of the symbolic link given by 'path', or NULL if the symbolic
  1531.  *     link could not be read.  Storage for the result string is
  1532.  *     allocated in bufferPtr; the caller must call Tcl_DStringFree()
  1533.  *     when the result is no longer needed.
  1534.  *
  1535.  * Side effects:
  1536.  *     See readlink() documentation.
  1537.  *
  1538.  *---------------------------------------------------------------------------
  1539.  */
  1540. char *
  1541. TclpReadlink(path, linkPtr)
  1542.     CONST char *path;          /* Path of file to readlink (UTF-8). */
  1543.     Tcl_DString *linkPtr;      /* Uninitialized or free DString filled
  1544.                                 * with contents of link (UTF-8). */
  1545. {
  1546.     char link[MAXPATHLEN];
  1547.     int length;
  1548.     char *native;
  1549.     Tcl_DString ds;
  1550.     native = Tcl_UtfToExternalDString(NULL, path, -1, &ds);
  1551.     length = readlink(native, link, sizeof(link));     /* INTL: Native. */
  1552.     Tcl_DStringFree(&ds);
  1553.     
  1554.     if (length < 0) {
  1555. return NULL;
  1556.     }
  1557.     Tcl_ExternalToUtfDString(NULL, link, length, linkPtr);
  1558.     return Tcl_DStringValue(linkPtr);
  1559. }
  1560. #endif /* __CYGWIN__ */
  1561. /*
  1562.  *----------------------------------------------------------------------
  1563.  *
  1564.  * TclpGetCwd --
  1565.  *
  1566.  * This function replaces the library version of getcwd().
  1567.  *
  1568.  * Results:
  1569.  * The result is a pointer to a string specifying the current
  1570.  * directory, or NULL if the current directory could not be
  1571.  * determined.  If NULL is returned, an error message is left in the
  1572.  * interp's result.  Storage for the result string is allocated in
  1573.  * bufferPtr; the caller must call Tcl_DStringFree() when the result
  1574.  * is no longer needed.
  1575.  *
  1576.  * Side effects:
  1577.  * None.
  1578.  *
  1579.  *----------------------------------------------------------------------
  1580.  */
  1581. CONST char *
  1582. TclpGetCwd(interp, bufferPtr)
  1583.     Tcl_Interp *interp; /* If non-NULL, used for error reporting. */
  1584.     Tcl_DString *bufferPtr; /* Uninitialized or free DString filled
  1585.  * with name of current directory. */
  1586. {
  1587.     WCHAR buffer[MAX_PATH];
  1588.     char *p;
  1589.     if ((*tclWinProcs->getCurrentDirectoryProc)(MAX_PATH, buffer) == 0) {
  1590. TclWinConvertError(GetLastError());
  1591. if (interp != NULL) {
  1592.     Tcl_AppendResult(interp,
  1593.     "error getting working directory name: ",
  1594.     Tcl_PosixError(interp), (char *) NULL);
  1595. }
  1596. return NULL;
  1597.     }
  1598.     /*
  1599.      * Watch for the weird Windows c:\UNC syntax.
  1600.      */
  1601.     if (tclWinProcs->useWide) {
  1602. WCHAR *native;
  1603. native = (WCHAR *) buffer;
  1604. if ((native[0] != '') && (native[1] == ':') 
  1605. && (native[2] == '\') && (native[3] == '\')) {
  1606.     native += 2;
  1607. }
  1608. Tcl_WinTCharToUtf((TCHAR *) native, -1, bufferPtr);
  1609.     } else {
  1610. char *native;
  1611. native = (char *) buffer;
  1612. if ((native[0] != '') && (native[1] == ':') 
  1613. && (native[2] == '\') && (native[3] == '\')) {
  1614.     native += 2;
  1615. }
  1616. Tcl_WinTCharToUtf((TCHAR *) native, -1, bufferPtr);
  1617.     }
  1618.     /*
  1619.      * Convert to forward slashes for easier use in scripts.
  1620.      */
  1621.       
  1622.     for (p = Tcl_DStringValue(bufferPtr); *p != ''; p++) {
  1623. if (*p == '\') {
  1624.     *p = '/';
  1625. }
  1626.     }
  1627.     return Tcl_DStringValue(bufferPtr);
  1628. }
  1629. int 
  1630. TclpObjStat(pathPtr, statPtr)
  1631.     Tcl_Obj *pathPtr;          /* Path of file to stat */
  1632.     Tcl_StatBuf *statPtr;      /* Filled with results of stat call. */
  1633. {
  1634. #ifdef OLD_API
  1635.     Tcl_Obj *transPtr;
  1636.     /*
  1637.      * Eliminate file names containing wildcard characters, or subsequent 
  1638.      * call to FindFirstFile() will expand them, matching some other file.
  1639.      */
  1640.     transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
  1641.     if (transPtr == NULL || (strpbrk(Tcl_GetString(transPtr), "?*") != NULL)) {
  1642. if (transPtr != NULL) {
  1643.     Tcl_DecrRefCount(transPtr);
  1644. }
  1645. Tcl_SetErrno(ENOENT);
  1646. return -1;
  1647.     }
  1648.     Tcl_DecrRefCount(transPtr);
  1649. #endif
  1650.     
  1651.     /*
  1652.      * Ensure correct file sizes by forcing the OS to write any
  1653.      * pending data to disk. This is done only for channels which are
  1654.      * dirty, i.e. have been written to since the last flush here.
  1655.      */
  1656.     TclWinFlushDirtyChannels ();
  1657.     return NativeStat((CONST TCHAR*) Tcl_FSGetNativePath(pathPtr), statPtr, 0);
  1658. }
  1659. /*
  1660.  *----------------------------------------------------------------------
  1661.  *
  1662.  * NativeStat --
  1663.  *
  1664.  * This function replaces the library version of stat(), fixing 
  1665.  * the following bugs:
  1666.  *
  1667.  * 1. stat("c:") returns an error.
  1668.  * 2. Borland stat() return time in GMT instead of localtime.
  1669.  * 3. stat("\servermount") would return error.
  1670.  * 4. Accepts slashes or backslashes.
  1671.  * 5. st_dev and st_rdev were wrong for UNC paths.
  1672.  *
  1673.  * Results:
  1674.  * See stat documentation.
  1675.  *
  1676.  * Side effects:
  1677.  * See stat documentation.
  1678.  *
  1679.  *----------------------------------------------------------------------
  1680.  */
  1681. static int 
  1682. NativeStat(nativePath, statPtr, checkLinks)
  1683.     CONST TCHAR *nativePath;   /* Path of file to stat */
  1684.     Tcl_StatBuf *statPtr;      /* Filled with results of stat call. */
  1685.     int checkLinks;            /* If non-zero, behave like 'lstat' */
  1686. {
  1687.     Tcl_DString ds;
  1688.     DWORD attr;
  1689.     WCHAR nativeFullPath[MAX_PATH];
  1690.     TCHAR *nativePart;
  1691.     CONST char *fullPath;
  1692.     int dev;
  1693.     unsigned short mode;
  1694.     
  1695.     if (tclWinProcs->getFileAttributesExProc == NULL) {
  1696.         /* 
  1697.          * We don't have the faster attributes proc, so we're
  1698.          * probably running on Win95
  1699.          */
  1700. WIN32_FIND_DATAT data;
  1701. HANDLE handle;
  1702. handle = (*tclWinProcs->findFirstFileProc)(nativePath, &data);
  1703. if (handle == INVALID_HANDLE_VALUE) {
  1704.     /* 
  1705.      * FindFirstFile() doesn't work on root directories, so call
  1706.      * GetFileAttributes() to see if the specified file exists.
  1707.      */
  1708.     attr = (*tclWinProcs->getFileAttributesProc)(nativePath);
  1709.     if (attr == INVALID_FILE_ATTRIBUTES) {
  1710. Tcl_SetErrno(ENOENT);
  1711. return -1;
  1712.     }
  1713.     /* 
  1714.      * Make up some fake information for this file.  It has the 
  1715.      * correct file attributes and a time of 0.
  1716.      */
  1717.     memset(&data, 0, sizeof(data));
  1718.     data.a.dwFileAttributes = attr;
  1719. } else {
  1720.     FindClose(handle);
  1721. }
  1722.     
  1723. (*tclWinProcs->getFullPathNameProc)(nativePath, MAX_PATH, nativeFullPath,
  1724. &nativePart);
  1725. fullPath = Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds);
  1726. dev = -1;
  1727. if ((fullPath[0] == '\') && (fullPath[1] == '\')) {
  1728.     CONST char *p;
  1729.     DWORD dw;
  1730.     CONST TCHAR *nativeVol;
  1731.     Tcl_DString volString;
  1732.     p = strchr(fullPath + 2, '\');
  1733.     p = strchr(p + 1, '\');
  1734.     if (p == NULL) {
  1735. /*
  1736.  * Add terminating backslash to fullpath or 
  1737.  * GetVolumeInformation() won't work.
  1738.  */
  1739. fullPath = Tcl_DStringAppend(&ds, "\", 1);
  1740. p = fullPath + Tcl_DStringLength(&ds);
  1741.     } else {
  1742. p++;
  1743.     }
  1744.     nativeVol = Tcl_WinUtfToTChar(fullPath, p - fullPath, &volString);
  1745.     dw = (DWORD) -1;
  1746.     (*tclWinProcs->getVolumeInformationProc)(nativeVol, NULL, 0, &dw,
  1747.     NULL, NULL, NULL, 0);
  1748.     /*
  1749.      * GetFullPathName() turns special devices like "NUL" into
  1750.      * "\.NUL", but GetVolumeInformation() returns failure for
  1751.      * "\.NUL".  This will cause "NUL" to get a drive number of
  1752.      * -1, which makes about as much sense as anything since the
  1753.      * special devices don't live on any drive.
  1754.      */
  1755.     dev = dw;
  1756.     Tcl_DStringFree(&volString);
  1757. } else if ((fullPath[0] != '') && (fullPath[1] == ':')) {
  1758.     dev = Tcl_UniCharToLower(fullPath[0]) - 'a';
  1759. }
  1760. Tcl_DStringFree(&ds);
  1761. attr = data.a.dwFileAttributes;
  1762. statPtr->st_size  = ((Tcl_WideInt)data.a.nFileSizeLow) |
  1763. (((Tcl_WideInt)data.a.nFileSizeHigh) << 32);
  1764. statPtr->st_atime = ToCTime(data.a.ftLastAccessTime);
  1765. statPtr->st_mtime = ToCTime(data.a.ftLastWriteTime);
  1766. statPtr->st_ctime = ToCTime(data.a.ftCreationTime);
  1767.     } else {
  1768. WIN32_FILE_ATTRIBUTE_DATA data;
  1769. if((*tclWinProcs->getFileAttributesExProc)(nativePath,
  1770.    GetFileExInfoStandard,
  1771.    &data) != TRUE) {
  1772.     Tcl_SetErrno(ENOENT);
  1773.     return -1;
  1774. }
  1775.     
  1776. (*tclWinProcs->getFullPathNameProc)(nativePath, MAX_PATH, 
  1777.     nativeFullPath, &nativePart);
  1778. fullPath = Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds);
  1779. dev = -1;
  1780. if ((fullPath[0] == '\') && (fullPath[1] == '\')) {
  1781.     CONST char *p;
  1782.     DWORD dw;
  1783.     CONST TCHAR *nativeVol;
  1784.     Tcl_DString volString;
  1785.     p = strchr(fullPath + 2, '\');
  1786.     p = strchr(p + 1, '\');
  1787.     if (p == NULL) {
  1788. /*
  1789.  * Add terminating backslash to fullpath or 
  1790.  * GetVolumeInformation() won't work.
  1791.  */
  1792. fullPath = Tcl_DStringAppend(&ds, "\", 1);
  1793. p = fullPath + Tcl_DStringLength(&ds);
  1794.     } else {
  1795. p++;
  1796.     }
  1797.     nativeVol = Tcl_WinUtfToTChar(fullPath, p - fullPath, &volString);
  1798.     dw = (DWORD) -1;
  1799.     (*tclWinProcs->getVolumeInformationProc)(nativeVol, NULL, 0, &dw,
  1800.     NULL, NULL, NULL, 0);
  1801.     /*
  1802.      * GetFullPathName() turns special devices like "NUL" into
  1803.      * "\.NUL", but GetVolumeInformation() returns failure for
  1804.      * "\.NUL".  This will cause "NUL" to get a drive number of
  1805.      * -1, which makes about as much sense as anything since the
  1806.      * special devices don't live on any drive.
  1807.      */
  1808.     dev = dw;
  1809.     Tcl_DStringFree(&volString);
  1810. } else if ((fullPath[0] != '') && (fullPath[1] == ':')) {
  1811.     dev = Tcl_UniCharToLower(fullPath[0]) - 'a';
  1812. }
  1813. Tcl_DStringFree(&ds);
  1814. attr = data.dwFileAttributes;
  1815. statPtr->st_size  = ((Tcl_WideInt)data.nFileSizeLow) |
  1816. (((Tcl_WideInt)data.nFileSizeHigh) << 32);
  1817. statPtr->st_atime = ToCTime(data.ftLastAccessTime);
  1818. statPtr->st_mtime = ToCTime(data.ftLastWriteTime);
  1819. statPtr->st_ctime = ToCTime(data.ftCreationTime);
  1820.     }
  1821.     mode = NativeStatMode(attr, checkLinks, NativeIsExec(nativePath));
  1822.     
  1823.     statPtr->st_dev = (dev_t) dev;
  1824.     statPtr->st_ino = 0;
  1825.     statPtr->st_mode = mode;
  1826.     statPtr->st_nlink = 1;
  1827.     statPtr->st_uid = 0;
  1828.     statPtr->st_gid = 0;
  1829.     statPtr->st_rdev = (dev_t) dev;
  1830.     return 0;
  1831. }
  1832. /*
  1833.  *----------------------------------------------------------------------
  1834.  *
  1835.  * NativeStatMode --
  1836.  *
  1837.  * Calculate just the 'st_mode' field of a 'stat' structure.
  1838.  *
  1839.  *----------------------------------------------------------------------
  1840.  */
  1841. static unsigned short
  1842. NativeStatMode(DWORD attr, int checkLinks, int isExec) 
  1843. {
  1844.     int mode;
  1845.     if (checkLinks && (attr & FILE_ATTRIBUTE_REPARSE_POINT)) {
  1846. /* It is a link */
  1847. mode = S_IFLNK;
  1848.     } else {
  1849. mode  = (attr & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR | S_IEXEC : S_IFREG;
  1850.     }
  1851.     mode |= (attr & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
  1852.     if (isExec) {
  1853. mode |= S_IEXEC;
  1854.     }
  1855.     
  1856.     /*
  1857.      * Propagate the S_IREAD, S_IWRITE, S_IEXEC bits to the group and 
  1858.      * other positions.
  1859.      */
  1860.     mode |= (mode & 0x0700) >> 3;
  1861.     mode |= (mode & 0x0700) >> 6;
  1862.     return (unsigned short)mode;
  1863. }
  1864. /*
  1865.  *------------------------------------------------------------------------
  1866.  *
  1867.  * ToCTime --
  1868.  *
  1869.  * Converts a Windows FILETIME to a time_t in UTC.
  1870.  *
  1871.  * Results:
  1872.  * Returns the count of seconds from the Posix epoch.
  1873.  *
  1874.  *------------------------------------------------------------------------
  1875.  */
  1876. static time_t
  1877. ToCTime(
  1878.     FILETIME fileTime) /* UTC time */
  1879. {
  1880.     LARGE_INTEGER convertedTime;
  1881.     convertedTime.LowPart = fileTime.dwLowDateTime;
  1882.     convertedTime.HighPart = (LONG) fileTime.dwHighDateTime;
  1883.     return (time_t) ((convertedTime.QuadPart
  1884.     - (Tcl_WideInt) POSIX_EPOCH_AS_FILETIME) / (Tcl_WideInt) 10000000);
  1885. }
  1886. /*
  1887.  *------------------------------------------------------------------------
  1888.  *
  1889.  * FromCTime --
  1890.  *
  1891.  * Converts a time_t to a Windows FILETIME
  1892.  *
  1893.  * Results:
  1894.  * Returns the count of 100-ns ticks seconds from the Windows epoch.
  1895.  *
  1896.  *------------------------------------------------------------------------
  1897.  */
  1898. static void
  1899. FromCTime(
  1900.     time_t posixTime,
  1901.     FILETIME* fileTime) /* UTC Time */
  1902. {
  1903.     LARGE_INTEGER convertedTime;
  1904.     convertedTime.QuadPart = ((LONGLONG) posixTime) * 10000000
  1905. + POSIX_EPOCH_AS_FILETIME;
  1906.     fileTime->dwLowDateTime = convertedTime.LowPart;
  1907.     fileTime->dwHighDateTime = convertedTime.HighPart;
  1908. }
  1909. #if 0
  1910. /*
  1911.  *-------------------------------------------------------------------------
  1912.  *
  1913.  * TclWinResolveShortcut --
  1914.  *
  1915.  * Resolve a potential Windows shortcut to get the actual file or 
  1916.  * directory in question.  
  1917.  *
  1918.  * Results:
  1919.  * Returns 1 if the shortcut could be resolved, or 0 if there was
  1920.  * an error or if the filename was not a shortcut.
  1921.  * If bufferPtr did hold the name of a shortcut, it is modified to
  1922.  * hold the resolved target of the shortcut instead.
  1923.  *
  1924.  * Side effects:
  1925.  * Loads and unloads OLE package to determine if filename refers to
  1926.  * a shortcut.
  1927.  *
  1928.  *-------------------------------------------------------------------------
  1929.  */
  1930. int
  1931. TclWinResolveShortcut(bufferPtr)
  1932.     Tcl_DString *bufferPtr; /* Holds name of file to resolve.  On 
  1933.  * return, holds resolved file name. */
  1934. {
  1935.     HRESULT hres; 
  1936.     IShellLink *psl; 
  1937.     IPersistFile *ppf; 
  1938.     WIN32_FIND_DATA wfd; 
  1939.     WCHAR wpath[MAX_PATH];
  1940.     char *path, *ext;
  1941.     char realFileName[MAX_PATH];
  1942.     /*
  1943.      * Windows system calls do not automatically resolve
  1944.      * shortcuts like UNIX automatically will with symbolic links.
  1945.      */
  1946.     path = Tcl_DStringValue(bufferPtr);
  1947.     ext = strrchr(path, '.');
  1948.     if ((ext == NULL) || (stricmp(ext, ".lnk") != 0)) {
  1949. return 0;
  1950.     }
  1951.     CoInitialize(NULL);
  1952.     path = Tcl_DStringValue(bufferPtr);
  1953.     realFileName[0] = '';
  1954.     hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, 
  1955.     &IID_IShellLink, &psl); 
  1956.     if (SUCCEEDED(hres)) { 
  1957. hres = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf);
  1958. if (SUCCEEDED(hres)) { 
  1959.     MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, sizeof(wpath));
  1960.     hres = ppf->lpVtbl->Load(ppf, wpath, STGM_READ); 
  1961.     if (SUCCEEDED(hres)) {
  1962. hres = psl->lpVtbl->Resolve(psl, NULL, 
  1963. SLR_ANY_MATCH | SLR_NO_UI); 
  1964. if (SUCCEEDED(hres)) { 
  1965.     hres = psl->lpVtbl->GetPath(psl, realFileName, MAX_PATH, 
  1966.     &wfd, 0);
  1967.     } 
  1968.     ppf->lpVtbl->Release(ppf); 
  1969. psl->lpVtbl->Release(psl); 
  1970.     } 
  1971.     CoUninitialize();
  1972.     if (realFileName[0] != '') {
  1973. Tcl_DStringSetLength(bufferPtr, 0);
  1974. Tcl_DStringAppend(bufferPtr, realFileName, -1);
  1975. return 1;
  1976.     }
  1977.     return 0;
  1978. }
  1979. #endif
  1980. Tcl_Obj* 
  1981. TclpObjGetCwd(interp)
  1982.     Tcl_Interp *interp;
  1983. {
  1984.     Tcl_DString ds;
  1985.     if (TclpGetCwd(interp, &ds) != NULL) {
  1986. Tcl_Obj *cwdPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
  1987. Tcl_IncrRefCount(cwdPtr);
  1988. Tcl_DStringFree(&ds);
  1989. return cwdPtr;
  1990.     } else {
  1991. return NULL;
  1992.     }
  1993. }
  1994. int 
  1995. TclpObjAccess(pathPtr, mode)
  1996.     Tcl_Obj *pathPtr;
  1997.     int mode;
  1998. {
  1999.     return NativeAccess((CONST TCHAR*) Tcl_FSGetNativePath(pathPtr), mode);
  2000. }
  2001. int 
  2002. TclpObjLstat(pathPtr, statPtr)
  2003.     Tcl_Obj *pathPtr;
  2004.     Tcl_StatBuf *statPtr; 
  2005. {
  2006.     /*
  2007.      * Ensure correct file sizes by forcing the OS to write any
  2008.      * pending data to disk. This is done only for channels which are
  2009.      * dirty, i.e. have been written to since the last flush here.
  2010.      */
  2011.     TclWinFlushDirtyChannels ();
  2012.     return NativeStat((CONST TCHAR*) Tcl_FSGetNativePath(pathPtr), statPtr, 1);
  2013. }
  2014. #ifdef S_IFLNK
  2015. Tcl_Obj* 
  2016. TclpObjLink(pathPtr, toPtr, linkAction)
  2017.     Tcl_Obj *pathPtr;
  2018.     Tcl_Obj *toPtr;
  2019.     int linkAction;
  2020. {
  2021.     if (toPtr != NULL) {
  2022. int res;
  2023. TCHAR* LinkTarget = (TCHAR*)Tcl_FSGetNativePath(toPtr);
  2024. TCHAR* LinkSource = (TCHAR*)Tcl_FSGetNativePath(pathPtr);
  2025. if (LinkSource == NULL || LinkTarget == NULL) {
  2026.     return NULL;
  2027. }
  2028. res = WinLink(LinkSource, LinkTarget, linkAction);
  2029. if (res == 0) {
  2030.     return toPtr;
  2031. } else {
  2032.     return NULL;
  2033. }
  2034.     } else {
  2035. TCHAR* LinkSource = (TCHAR*)Tcl_FSGetNativePath(pathPtr);
  2036. if (LinkSource == NULL) {
  2037.     return NULL;
  2038. }
  2039. return WinReadLink(LinkSource);
  2040.     }
  2041. }
  2042. #endif
  2043. /*
  2044.  *---------------------------------------------------------------------------
  2045.  *
  2046.  * TclpFilesystemPathType --
  2047.  *
  2048.  *      This function is part of the native filesystem support, and
  2049.  *      returns the path type of the given path.  Returns NTFS or FAT
  2050.  *      or whatever is returned by the 'volume information' proc.
  2051.  *
  2052.  * Results:
  2053.  *      NULL at present.
  2054.  *
  2055.  * Side effects:
  2056.  * None.
  2057.  *
  2058.  *---------------------------------------------------------------------------
  2059.  */
  2060. Tcl_Obj*
  2061. TclpFilesystemPathType(pathObjPtr)
  2062.     Tcl_Obj* pathObjPtr;
  2063. {
  2064. #define VOL_BUF_SIZE 32
  2065.     int found;
  2066.     WCHAR volType[VOL_BUF_SIZE];
  2067.     char* firstSeparator;
  2068.     CONST char *path;
  2069.     
  2070.     Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathObjPtr);
  2071.     if (normPath == NULL) return NULL;
  2072.     path = Tcl_GetString(normPath);
  2073.     if (path == NULL) return NULL;
  2074.     
  2075.     firstSeparator = strchr(path, '/');
  2076.     if (firstSeparator == NULL) {
  2077. found = tclWinProcs->getVolumeInformationProc(
  2078. Tcl_FSGetNativePath(pathObjPtr), NULL, 0, NULL, NULL, 
  2079. NULL, (WCHAR *)volType, VOL_BUF_SIZE);
  2080.     } else {
  2081. Tcl_Obj *driveName = Tcl_NewStringObj(path, firstSeparator - path+1);
  2082. Tcl_IncrRefCount(driveName);
  2083. found = tclWinProcs->getVolumeInformationProc(
  2084. Tcl_FSGetNativePath(driveName), NULL, 0, NULL, NULL, 
  2085. NULL, (WCHAR *)volType, VOL_BUF_SIZE);
  2086. Tcl_DecrRefCount(driveName);
  2087.     }
  2088.     if (found == 0) {
  2089. return NULL;
  2090.     } else {
  2091. Tcl_DString ds;
  2092. Tcl_Obj *objPtr;
  2093. Tcl_WinTCharToUtf((CONST char *)volType, -1, &ds);
  2094. objPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds),Tcl_DStringLength(&ds));
  2095. Tcl_DStringFree(&ds);
  2096. return objPtr;
  2097.     }
  2098. #undef VOL_BUF_SIZE
  2099. }
  2100. /*
  2101.  *---------------------------------------------------------------------------
  2102.  *
  2103.  * TclpObjNormalizePath --
  2104.  *
  2105.  * This function scans through a path specification and replaces it,
  2106.  * in place, with a normalized version.  This means using the
  2107.  * 'longname', and expanding any symbolic links contained within the
  2108.  * path.
  2109.  *
  2110.  * Results:
  2111.  * The new 'nextCheckpoint' value, giving as far as we could
  2112.  * understand in the path.
  2113.  *
  2114.  * Side effects:
  2115.  * The pathPtr string, which must contain a valid path, is
  2116.  * possibly modified in place.
  2117.  *
  2118.  *---------------------------------------------------------------------------
  2119.  */
  2120. int
  2121. TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
  2122.     Tcl_Interp *interp;
  2123.     Tcl_Obj *pathPtr;
  2124.     int nextCheckpoint;
  2125. {
  2126.     char *lastValidPathEnd = NULL;
  2127.     /* This will hold the normalized string */
  2128.     Tcl_DString dsNorm;
  2129.     char *path;
  2130.     char *currentPathEndPosition;
  2131.     Tcl_DStringInit(&dsNorm);
  2132.     path = Tcl_GetString(pathPtr);
  2133.     if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS) {
  2134. /* 
  2135.  * We're on Win95, 98 or ME.  There are two assumptions
  2136.  * in this block of code.  First that the native (NULL)
  2137.  * encoding is basically ascii, and second that symbolic
  2138.  * links are not possible.  Both of these assumptions
  2139.  * appear to be true of these operating systems.
  2140.  */
  2141. int isDrive = 1;
  2142. Tcl_DString ds;
  2143. currentPathEndPosition = path + nextCheckpoint;
  2144.         if (*currentPathEndPosition == '/') {
  2145.     currentPathEndPosition++;
  2146.         }
  2147. while (1) {
  2148.     char cur = *currentPathEndPosition;
  2149.     if ((cur == '/' || cur == 0) && (path != currentPathEndPosition)) {
  2150. /* Reached directory separator, or end of string */
  2151. CONST char *nativePath = Tcl_UtfToExternalDString(NULL, path, 
  2152.     currentPathEndPosition - path, &ds);
  2153. /*
  2154.  * Now we convert the tail of the current path to its
  2155.  * 'long form', and append it to 'dsNorm' which holds
  2156.  * the current normalized path, if the file exists.
  2157.  */
  2158. if (isDrive) {
  2159.     if (GetFileAttributesA(nativePath) == INVALID_FILE_ATTRIBUTES) {
  2160. /* File doesn't exist */
  2161. if (isDrive) {
  2162.     int len = WinIsReserved(path);
  2163.     if (len > 0) {
  2164. /* Actually it does exist - COM1, etc */
  2165. int i;
  2166. for (i=0;i<len;i++) {
  2167.     if (nativePath[i] >= 'a') {
  2168. ((char*)nativePath)[i] -= ('a' - 'A');
  2169.     }
  2170. }
  2171. Tcl_DStringAppend(&dsNorm, nativePath, len);
  2172. lastValidPathEnd = currentPathEndPosition;
  2173.     }
  2174. }
  2175. Tcl_DStringFree(&ds);
  2176. break;
  2177.     }
  2178.     if (nativePath[0] >= 'a') {
  2179. ((char*)nativePath)[0] -= ('a' - 'A');
  2180.     }
  2181.     Tcl_DStringAppend(&dsNorm,nativePath,Tcl_DStringLength(&ds));
  2182. } else {
  2183.     WIN32_FIND_DATA fData;
  2184.     HANDLE handle;
  2185.     
  2186.     handle = FindFirstFileA(nativePath, &fData);
  2187.     if (handle == INVALID_HANDLE_VALUE) {
  2188. if (GetFileAttributesA(nativePath) 
  2189.     == INVALID_FILE_ATTRIBUTES) {
  2190.     /* File doesn't exist */
  2191.     Tcl_DStringFree(&ds);
  2192.     break;
  2193. }
  2194. /* This is usually the '/' in 'c:/' at end of string */
  2195. Tcl_DStringAppend(&dsNorm,"/", 1);
  2196.     } else {
  2197. char *nativeName;
  2198. if (fData.cFileName[0] != '') {
  2199.     nativeName = fData.cFileName;
  2200. } else {
  2201.     nativeName = fData.cAlternateFileName;
  2202. }
  2203. FindClose(handle);
  2204. Tcl_DStringAppend(&dsNorm,"/", 1);
  2205. Tcl_DStringAppend(&dsNorm,nativeName,-1);
  2206.     }
  2207. }
  2208. Tcl_DStringFree(&ds);
  2209. lastValidPathEnd = currentPathEndPosition;
  2210. if (cur == 0) {
  2211.     break;
  2212. }
  2213. /* 
  2214.  * If we get here, we've got past one directory
  2215.  * delimiter, so we know it is no longer a drive 
  2216.  */
  2217. isDrive = 0;
  2218.     }
  2219.     currentPathEndPosition++;
  2220. }
  2221.     } else {
  2222. /* We're on WinNT or 2000 or XP */
  2223. Tcl_Obj *temp = NULL;
  2224. int isDrive = 1;
  2225. Tcl_DString ds;
  2226. currentPathEndPosition = path + nextCheckpoint;
  2227. if (*currentPathEndPosition == '/') {
  2228.     currentPathEndPosition++;
  2229. }
  2230. while (1) {
  2231.     char cur = *currentPathEndPosition;
  2232.     if ((cur == '/' || cur == 0) && (path != currentPathEndPosition)) {
  2233. /* Reached directory separator, or end of string */
  2234. WIN32_FILE_ATTRIBUTE_DATA data;
  2235. CONST char *nativePath = Tcl_WinUtfToTChar(path, 
  2236.     currentPathEndPosition - path, &ds);
  2237. if ((*tclWinProcs->getFileAttributesExProc)(nativePath,
  2238.     GetFileExInfoStandard, &data) != TRUE) {
  2239.     /* File doesn't exist */
  2240.     if (isDrive) {
  2241. int len = WinIsReserved(path);
  2242. if (len > 0) {
  2243.     /* Actually it does exist - COM1, etc */
  2244.     int i;
  2245.     for (i=0;i<len;i++) {
  2246. WCHAR wc = ((WCHAR*)nativePath)[i];
  2247. if (wc >= L'a') {
  2248.     wc -= (L'a' - L'A');
  2249.     ((WCHAR*)nativePath)[i] = wc;
  2250. }
  2251.     }
  2252.     Tcl_DStringAppend(&dsNorm, nativePath,
  2253.       sizeof(WCHAR)*len);
  2254.     lastValidPathEnd = currentPathEndPosition;
  2255. }
  2256.     }
  2257.     Tcl_DStringFree(&ds);
  2258.     break;
  2259. }
  2260. /* 
  2261.  * File 'nativePath' does exist if we get here.  We
  2262.  * now want to check if it is a symlink and otherwise
  2263.  * continue with the rest of the path.
  2264.  */
  2265. /* 
  2266.  * Check for symlinks, except at last component
  2267.  * of path (we don't follow final symlinks). Also
  2268.  * a drive (C:/) for example, may sometimes have
  2269.  * the reparse flag set for some reason I don't
  2270.  * understand.  We therefore don't perform this
  2271.  * check for drives.
  2272.  */
  2273. if (cur != 0 && !isDrive && (data.dwFileAttributes 
  2274.  & FILE_ATTRIBUTE_REPARSE_POINT)) {
  2275.     Tcl_Obj *to = WinReadLinkDirectory(nativePath);
  2276.     if (to != NULL) {
  2277. /* Read the reparse point ok */
  2278. /* Tcl_GetStringFromObj(to, &pathLen); */
  2279. nextCheckpoint = 0; /* pathLen */
  2280. Tcl_AppendToObj(to, currentPathEndPosition, -1);
  2281. /* Convert link to forward slashes */
  2282. for (path = Tcl_GetString(to); *path != 0; path++) {
  2283.     if (*path == '\') *path = '/';
  2284. }
  2285. path = Tcl_GetString(to);
  2286. currentPathEndPosition = path + nextCheckpoint;
  2287. if (temp != NULL) {
  2288.     Tcl_DecrRefCount(temp);
  2289. }
  2290. temp = to;
  2291. /* Reset variables so we can restart normalization */
  2292. isDrive = 1;
  2293. Tcl_DStringFree(&dsNorm);
  2294. Tcl_DStringInit(&dsNorm);
  2295. Tcl_DStringFree(&ds);
  2296. continue;
  2297.     }
  2298. }
  2299. /*
  2300.  * Now we convert the tail of the current path to its
  2301.  * 'long form', and append it to 'dsNorm' which holds
  2302.  * the current normalized path
  2303.  */
  2304. if (isDrive) {
  2305.     WCHAR drive = ((WCHAR*)nativePath)[0];
  2306.     if (drive >= L'a') {
  2307.         drive -= (L'a' - L'A');
  2308. ((WCHAR*)nativePath)[0] = drive;
  2309.     }
  2310.     Tcl_DStringAppend(&dsNorm,nativePath,Tcl_DStringLength(&ds));
  2311. } else {
  2312.     char *checkDots = NULL;
  2313.     
  2314.     if (lastValidPathEnd[1] == '.') {
  2315. checkDots = lastValidPathEnd + 1;
  2316. while (checkDots < currentPathEndPosition) {
  2317.     if (*checkDots != '.') {
  2318. checkDots = NULL;
  2319. break;
  2320.     }
  2321.     checkDots++;
  2322. }
  2323.     }
  2324.     if (checkDots != NULL) {
  2325. int dotLen = currentPathEndPosition - lastValidPathEnd;
  2326. /* 
  2327.  * Path is just dots.  We shouldn't really
  2328.  * ever see a path like that.  However, to be
  2329.  * nice we at least don't mangle the path -- 
  2330.  * we just add the dots as a path segment and
  2331.  * continue
  2332.  */
  2333. Tcl_DStringAppend(&dsNorm,
  2334.   (TCHAR*)((WCHAR*)(nativePath 
  2335. + Tcl_DStringLength(&ds)) 
  2336. - dotLen),
  2337.   (int)(dotLen * sizeof(WCHAR)));
  2338.     } else {
  2339. /* Normal path */
  2340. WIN32_FIND_DATAW fData;
  2341. HANDLE handle;
  2342. handle = FindFirstFileW((WCHAR*)nativePath, &fData);
  2343. if (handle == INVALID_HANDLE_VALUE) {
  2344.     /* This is usually the '/' in 'c:/' at end of string */
  2345.     Tcl_DStringAppend(&dsNorm,(CONST char*)L"/", 
  2346.       sizeof(WCHAR));
  2347. } else {
  2348.     WCHAR *nativeName;
  2349.     if (fData.cFileName[0] != '') {
  2350. nativeName = fData.cFileName;
  2351.     } else {
  2352. nativeName = fData.cAlternateFileName;
  2353.     }
  2354.     FindClose(handle);
  2355.     Tcl_DStringAppend(&dsNorm,(CONST char*)L"/", 
  2356.       sizeof(WCHAR));
  2357.     Tcl_DStringAppend(&dsNorm,(TCHAR*)nativeName, 
  2358.       (int) (wcslen(nativeName)*sizeof(WCHAR)));
  2359. }
  2360.     }
  2361. }
  2362. Tcl_DStringFree(&ds);
  2363. lastValidPathEnd = currentPathEndPosition;
  2364. if (cur == 0) {
  2365.     break;
  2366. }
  2367. /* 
  2368.  * If we get here, we've got past one directory
  2369.  * delimiter, so we know it is no longer a drive 
  2370.  */
  2371. isDrive = 0;
  2372.     }
  2373.     currentPathEndPosition++;
  2374. }
  2375.     }
  2376.     /* Common code path for all Windows platforms */
  2377.     nextCheckpoint = currentPathEndPosition - path;
  2378.     if (lastValidPathEnd != NULL) {
  2379. /* 
  2380.  * Concatenate the normalized string in dsNorm with the
  2381.  * tail of the path which we didn't recognise.  The
  2382.  * string in dsNorm is in the native encoding, so we
  2383.  * have to convert it to Utf.
  2384.  */
  2385. Tcl_DString dsTemp;
  2386. Tcl_WinTCharToUtf(Tcl_DStringValue(&dsNorm), 
  2387.   Tcl_DStringLength(&dsNorm), &dsTemp);
  2388. nextCheckpoint = Tcl_DStringLength(&dsTemp);
  2389. if (*lastValidPathEnd != 0) {
  2390.     /* Not the end of the string */
  2391.     int len;
  2392.     char *path;
  2393.     Tcl_Obj *tmpPathPtr;
  2394.     tmpPathPtr = Tcl_NewStringObj(Tcl_DStringValue(&dsTemp), 
  2395.   nextCheckpoint);
  2396.     Tcl_AppendToObj(tmpPathPtr, lastValidPathEnd, -1);
  2397.     path = Tcl_GetStringFromObj(tmpPathPtr, &len);
  2398.     Tcl_SetStringObj(pathPtr, path, len);
  2399.     Tcl_DecrRefCount(tmpPathPtr);
  2400. } else {
  2401.     /* End of string was reached above */
  2402.     Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&dsTemp),
  2403.      nextCheckpoint);
  2404. }
  2405. Tcl_DStringFree(&dsTemp);
  2406.     }
  2407.     Tcl_DStringFree(&dsNorm);
  2408.     return nextCheckpoint;
  2409. }
  2410. /*
  2411.  *---------------------------------------------------------------------------
  2412.  *
  2413.  * TclpUtime --
  2414.  *
  2415.  * Set the modification date for a file.
  2416.  *
  2417.  * Results:
  2418.  * 0 on success, -1 on error.
  2419.  *
  2420.  * Side effects:
  2421.  * Sets errno to a representation of any Windows problem that's observed
  2422.  * in the process.
  2423.  *
  2424.  *---------------------------------------------------------------------------
  2425.  */
  2426. int
  2427. TclpUtime(
  2428.     Tcl_Obj *pathPtr, /* File to modify */
  2429.     struct utimbuf *tval) /* New modification date structure */
  2430. {
  2431.     int res = 0;
  2432.     HANDLE fileHandle;
  2433.     CONST TCHAR *native;
  2434.     DWORD attr = 0;
  2435.     DWORD flags = FILE_ATTRIBUTE_NORMAL;
  2436.     FILETIME lastAccessTime, lastModTime;
  2437.     FromCTime(tval->actime, &lastAccessTime);
  2438.     FromCTime(tval->modtime, &lastModTime);
  2439.     native = (CONST TCHAR *)Tcl_FSGetNativePath(pathPtr);
  2440.     attr = (*tclWinProcs->getFileAttributesProc)(native);
  2441.     if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY) {
  2442. flags = FILE_FLAG_BACKUP_SEMANTICS;
  2443.     }
  2444.     /*
  2445.      * We use the native APIs (not 'utime') because there are some daylight
  2446.      * savings complications that utime gets wrong.
  2447.      */
  2448.     fileHandle = (tclWinProcs->createFileProc) (
  2449.     native, FILE_WRITE_ATTRIBUTES, 0, NULL,
  2450.     OPEN_EXISTING, flags, NULL);
  2451.     if (fileHandle == INVALID_HANDLE_VALUE ||
  2452.     !SetFileTime(fileHandle, NULL, &lastAccessTime, &lastModTime)) {
  2453. TclWinConvertError(GetLastError());
  2454. res = -1;
  2455.     }
  2456.     if (fileHandle != INVALID_HANDLE_VALUE) {
  2457. CloseHandle(fileHandle);
  2458.     }
  2459.     return res;
  2460. }