/[dtapublic]/projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinfile.c
ViewVC logotype

Diff of /projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinfile.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 67 by dashley, Mon Oct 31 00:57:34 2016 UTC revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
1  /* $Header$ */  /* $Header$ */
2  /*  /*
3   * tclWinFile.c --   * tclWinFile.c --
4   *   *
5   *      This file contains temporary wrappers around UNIX file handling   *      This file contains temporary wrappers around UNIX file handling
6   *      functions. These wrappers map the UNIX functions to Win32 HANDLE-style   *      functions. These wrappers map the UNIX functions to Win32 HANDLE-style
7   *      files, which can be manipulated through the Win32 console redirection   *      files, which can be manipulated through the Win32 console redirection
8   *      interfaces.   *      interfaces.
9   *   *
10   * Copyright (c) 1995-1998 Sun Microsystems, Inc.   * Copyright (c) 1995-1998 Sun Microsystems, Inc.
11   *   *
12   * See the file "license.terms" for information on usage and redistribution   * See the file "license.terms" for information on usage and redistribution
13   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14   *   *
15   * RCS: @(#) $Id: tclwinfile.c,v 1.1.1.1 2001/06/13 04:49:10 dtashley Exp $   * RCS: @(#) $Id: tclwinfile.c,v 1.1.1.1 2001/06/13 04:49:10 dtashley Exp $
16   */   */
17    
18  #include "tclWinInt.h"  #include "tclWinInt.h"
19  #include <sys/stat.h>  #include <sys/stat.h>
20  #include <shlobj.h>  #include <shlobj.h>
21  #include <lmaccess.h>           /* For TclpGetUserHome(). */  #include <lmaccess.h>           /* For TclpGetUserHome(). */
22    
23  static time_t           ToCTime(FILETIME fileTime);  static time_t           ToCTime(FILETIME fileTime);
24    
25  typedef NET_API_STATUS NET_API_FUNCTION NETUSERGETINFOPROC  typedef NET_API_STATUS NET_API_FUNCTION NETUSERGETINFOPROC
26          (LPWSTR servername, LPWSTR username, DWORD level, LPBYTE *bufptr);          (LPWSTR servername, LPWSTR username, DWORD level, LPBYTE *bufptr);
27    
28  typedef NET_API_STATUS NET_API_FUNCTION NETAPIBUFFERFREEPROC  typedef NET_API_STATUS NET_API_FUNCTION NETAPIBUFFERFREEPROC
29          (LPVOID Buffer);          (LPVOID Buffer);
30    
31  typedef NET_API_STATUS NET_API_FUNCTION NETGETDCNAMEPROC  typedef NET_API_STATUS NET_API_FUNCTION NETGETDCNAMEPROC
32          (LPWSTR servername, LPWSTR domainname, LPBYTE *bufptr);          (LPWSTR servername, LPWSTR domainname, LPBYTE *bufptr);
33    
34    
35  /*  /*
36   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
37   *   *
38   * TclpFindExecutable --   * TclpFindExecutable --
39   *   *
40   *      This procedure computes the absolute path name of the current   *      This procedure computes the absolute path name of the current
41   *      application, given its argv[0] value.   *      application, given its argv[0] value.
42   *   *
43   * Results:   * Results:
44   *      A dirty UTF string that is the path to the executable.  At this   *      A dirty UTF string that is the path to the executable.  At this
45   *      point we may not know the system encoding.  Convert the native   *      point we may not know the system encoding.  Convert the native
46   *      string value to UTF using the default encoding.  The assumption   *      string value to UTF using the default encoding.  The assumption
47   *      is that we will still be able to parse the path given the path   *      is that we will still be able to parse the path given the path
48   *      name contains ASCII string and '/' chars do not conflict with   *      name contains ASCII string and '/' chars do not conflict with
49   *      other UTF chars.   *      other UTF chars.
50   *   *
51   * Side effects:   * Side effects:
52   *      The variable tclNativeExecutableName gets filled in with the file   *      The variable tclNativeExecutableName gets filled in with the file
53   *      name for the application, if we figured it out.  If we couldn't   *      name for the application, if we figured it out.  If we couldn't
54   *      figure it out, tclNativeExecutableName is set to NULL.   *      figure it out, tclNativeExecutableName is set to NULL.
55   *   *
56   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
57   */   */
58    
59  char *  char *
60  TclpFindExecutable(argv0)  TclpFindExecutable(argv0)
61      CONST char *argv0;          /* The value of the application's argv[0]      CONST char *argv0;          /* The value of the application's argv[0]
62                                   * (native). */                                   * (native). */
63  {  {
64      Tcl_DString ds;      Tcl_DString ds;
65      WCHAR wName[MAX_PATH];      WCHAR wName[MAX_PATH];
66    
67      if (argv0 == NULL) {      if (argv0 == NULL) {
68          return NULL;          return NULL;
69      }      }
70      if (tclNativeExecutableName != NULL) {      if (tclNativeExecutableName != NULL) {
71          return tclNativeExecutableName;          return tclNativeExecutableName;
72      }      }
73    
74      /*      /*
75       * Under Windows we ignore argv0, and return the path for the file used to       * Under Windows we ignore argv0, and return the path for the file used to
76       * create this process.       * create this process.
77       */       */
78    
79      (*tclWinProcs->getModuleFileNameProc)(NULL, wName, MAX_PATH);      (*tclWinProcs->getModuleFileNameProc)(NULL, wName, MAX_PATH);
80      Tcl_WinTCharToUtf((TCHAR *) wName, -1, &ds);      Tcl_WinTCharToUtf((TCHAR *) wName, -1, &ds);
81    
82      tclNativeExecutableName = ckalloc((unsigned) (Tcl_DStringLength(&ds) + 1));      tclNativeExecutableName = ckalloc((unsigned) (Tcl_DStringLength(&ds) + 1));
83      strcpy(tclNativeExecutableName, Tcl_DStringValue(&ds));      strcpy(tclNativeExecutableName, Tcl_DStringValue(&ds));
84      Tcl_DStringFree(&ds);      Tcl_DStringFree(&ds);
85    
86      TclWinNoBackslash(tclNativeExecutableName);      TclWinNoBackslash(tclNativeExecutableName);
87      return tclNativeExecutableName;      return tclNativeExecutableName;
88  }  }
89    
90  /*  /*
91   *----------------------------------------------------------------------   *----------------------------------------------------------------------
92   *   *
93   * TclpMatchFilesTypes --   * TclpMatchFilesTypes --
94   *   *
95   *      This routine is used by the globbing code to search a   *      This routine is used by the globbing code to search a
96   *      directory for all files which match a given pattern.   *      directory for all files which match a given pattern.
97   *   *
98   * Results:   * Results:
99   *      If the tail argument is NULL, then the matching files are   *      If the tail argument is NULL, then the matching files are
100   *      added to the the interp's result.  Otherwise, TclDoGlob is called   *      added to the the interp's result.  Otherwise, TclDoGlob is called
101   *      recursively for each matching subdirectory.  The return value   *      recursively for each matching subdirectory.  The return value
102   *      is a standard Tcl result indicating whether an error occurred   *      is a standard Tcl result indicating whether an error occurred
103   *      in globbing.   *      in globbing.
104   *   *
105   * Side effects:   * Side effects:
106   *      None.   *      None.
107   *   *
108   *---------------------------------------------------------------------- */   *---------------------------------------------------------------------- */
109    
110  int  int
111  TclpMatchFilesTypes(  TclpMatchFilesTypes(
112      Tcl_Interp *interp,         /* Interpreter to receive results. */      Tcl_Interp *interp,         /* Interpreter to receive results. */
113      char *separators,           /* Directory separators to pass to TclDoGlob. */      char *separators,           /* Directory separators to pass to TclDoGlob. */
114      Tcl_DString *dirPtr,        /* Contains path to directory to search. */      Tcl_DString *dirPtr,        /* Contains path to directory to search. */
115      char *pattern,              /* Pattern to match against. */      char *pattern,              /* Pattern to match against. */
116      char *tail,                 /* Pointer to end of pattern.  Tail must      char *tail,                 /* Pointer to end of pattern.  Tail must
117                                   * point to a location in pattern and must                                   * point to a location in pattern and must
118                                   * not be static.*/                                   * not be static.*/
119      GlobTypeData *types)        /* Object containing list of acceptable types.      GlobTypeData *types)        /* Object containing list of acceptable types.
120                                   * May be NULL. */                                   * May be NULL. */
121  {  {
122      char drivePat[] = "?:\\";      char drivePat[] = "?:\\";
123      const char *message;      const char *message;
124      char *dir, *newPattern, *root;      char *dir, *newPattern, *root;
125      int matchDotFiles;      int matchDotFiles;
126      int dirLength, result = TCL_OK;      int dirLength, result = TCL_OK;
127      Tcl_DString dirString, patternString;      Tcl_DString dirString, patternString;
128      DWORD attr, volFlags;      DWORD attr, volFlags;
129      HANDLE handle;      HANDLE handle;
130      WIN32_FIND_DATAT data;      WIN32_FIND_DATAT data;
131      BOOL found;      BOOL found;
132      Tcl_DString ds;      Tcl_DString ds;
133      TCHAR *nativeName;      TCHAR *nativeName;
134      Tcl_Obj *resultPtr;      Tcl_Obj *resultPtr;
135    
136      /*      /*
137       * Convert the path to normalized form since some interfaces only       * Convert the path to normalized form since some interfaces only
138       * accept backslashes.  Also, ensure that the directory ends with a       * accept backslashes.  Also, ensure that the directory ends with a
139       * separator character.       * separator character.
140       */       */
141    
142      dirLength = Tcl_DStringLength(dirPtr);      dirLength = Tcl_DStringLength(dirPtr);
143      Tcl_DStringInit(&dirString);      Tcl_DStringInit(&dirString);
144      if (dirLength == 0) {      if (dirLength == 0) {
145          Tcl_DStringAppend(&dirString, ".\\", 2);          Tcl_DStringAppend(&dirString, ".\\", 2);
146      } else {      } else {
147          char *p;          char *p;
148    
149          Tcl_DStringAppend(&dirString, Tcl_DStringValue(dirPtr),          Tcl_DStringAppend(&dirString, Tcl_DStringValue(dirPtr),
150                  Tcl_DStringLength(dirPtr));                  Tcl_DStringLength(dirPtr));
151          for (p = Tcl_DStringValue(&dirString); *p != '\0'; p++) {          for (p = Tcl_DStringValue(&dirString); *p != '\0'; p++) {
152              if (*p == '/') {              if (*p == '/') {
153                  *p = '\\';                  *p = '\\';
154              }              }
155          }          }
156          p--;          p--;
157          if ((*p != '\\') && (*p != ':')) {          if ((*p != '\\') && (*p != ':')) {
158              Tcl_DStringAppend(&dirString, "\\", 1);              Tcl_DStringAppend(&dirString, "\\", 1);
159          }          }
160      }      }
161      dir = Tcl_DStringValue(&dirString);      dir = Tcl_DStringValue(&dirString);
162    
163      /*      /*
164       * First verify that the specified path is actually a directory.       * First verify that the specified path is actually a directory.
165       */       */
166    
167      nativeName = Tcl_WinUtfToTChar(dir, Tcl_DStringLength(&dirString), &ds);      nativeName = Tcl_WinUtfToTChar(dir, Tcl_DStringLength(&dirString), &ds);
168      attr = (*tclWinProcs->getFileAttributesProc)(nativeName);      attr = (*tclWinProcs->getFileAttributesProc)(nativeName);
169      Tcl_DStringFree(&ds);      Tcl_DStringFree(&ds);
170    
171      if ((attr == 0xffffffff) || ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)) {      if ((attr == 0xffffffff) || ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)) {
172          Tcl_DStringFree(&dirString);          Tcl_DStringFree(&dirString);
173          return TCL_OK;          return TCL_OK;
174      }      }
175    
176      /*      /*
177       * Next check the volume information for the directory to see whether       * Next check the volume information for the directory to see whether
178       * comparisons should be case sensitive or not.  If the root is null, then       * comparisons should be case sensitive or not.  If the root is null, then
179       * we use the root of the current directory.  If the root is just a drive       * we use the root of the current directory.  If the root is just a drive
180       * specifier, we use the root directory of the given drive.       * specifier, we use the root directory of the given drive.
181       */       */
182    
183      switch (Tcl_GetPathType(dir)) {      switch (Tcl_GetPathType(dir)) {
184          case TCL_PATH_RELATIVE:          case TCL_PATH_RELATIVE:
185              found = GetVolumeInformationA(NULL, NULL, 0, NULL, NULL,              found = GetVolumeInformationA(NULL, NULL, 0, NULL, NULL,
186                      &volFlags, NULL, 0);                      &volFlags, NULL, 0);
187              break;              break;
188          case TCL_PATH_VOLUME_RELATIVE:          case TCL_PATH_VOLUME_RELATIVE:
189              if (dir[0] == '\\') {              if (dir[0] == '\\') {
190                  root = NULL;                  root = NULL;
191              } else {              } else {
192                  root = drivePat;                  root = drivePat;
193                  *root = dir[0];                  *root = dir[0];
194              }              }
195              found = GetVolumeInformationA(root, NULL, 0, NULL, NULL,              found = GetVolumeInformationA(root, NULL, 0, NULL, NULL,
196                      &volFlags, NULL, 0);                      &volFlags, NULL, 0);
197              break;              break;
198          case TCL_PATH_ABSOLUTE:          case TCL_PATH_ABSOLUTE:
199              if (dir[1] == ':') {              if (dir[1] == ':') {
200                  root = drivePat;                  root = drivePat;
201                  *root = dir[0];                  *root = dir[0];
202                  found = GetVolumeInformationA(root, NULL, 0, NULL, NULL,                  found = GetVolumeInformationA(root, NULL, 0, NULL, NULL,
203                          &volFlags, NULL, 0);                          &volFlags, NULL, 0);
204              } else if (dir[1] == '\\') {              } else if (dir[1] == '\\') {
205                  char *p;                  char *p;
206    
207                  p = strchr(dir + 2, '\\');                  p = strchr(dir + 2, '\\');
208                  p = strchr(p + 1, '\\');                  p = strchr(p + 1, '\\');
209                  p++;                  p++;
210                  nativeName = Tcl_WinUtfToTChar(dir, p - dir, &ds);                  nativeName = Tcl_WinUtfToTChar(dir, p - dir, &ds);
211                  found = (*tclWinProcs->getVolumeInformationProc)(nativeName,                  found = (*tclWinProcs->getVolumeInformationProc)(nativeName,
212                          NULL, 0, NULL, NULL, &volFlags, NULL, 0);                          NULL, 0, NULL, NULL, &volFlags, NULL, 0);
213                  Tcl_DStringFree(&ds);                  Tcl_DStringFree(&ds);
214              }              }
215              break;              break;
216      }      }
217    
218      if (found == 0) {      if (found == 0) {
219          message = "couldn't read volume information for \"";          message = "couldn't read volume information for \"";
220          goto error;          goto error;
221      }      }
222    
223      /*      /*
224       * In Windows, although some volumes may support case sensitivity, Windows       * In Windows, although some volumes may support case sensitivity, Windows
225       * doesn't honor case.  So in globbing we need to ignore the case       * doesn't honor case.  So in globbing we need to ignore the case
226       * of file names.       * of file names.
227       */       */
228    
229      Tcl_DStringInit(&patternString);      Tcl_DStringInit(&patternString);
230      newPattern = Tcl_DStringAppend(&patternString, pattern, tail - pattern);      newPattern = Tcl_DStringAppend(&patternString, pattern, tail - pattern);
231      Tcl_UtfToLower(newPattern);      Tcl_UtfToLower(newPattern);
232    
233      /*      /*
234       * We need to check all files in the directory, so append a *.*       * We need to check all files in the directory, so append a *.*
235       * to the path.       * to the path.
236       */       */
237    
238      dir = Tcl_DStringAppend(&dirString, "*.*", 3);      dir = Tcl_DStringAppend(&dirString, "*.*", 3);
239      nativeName = Tcl_WinUtfToTChar(dir, -1, &ds);      nativeName = Tcl_WinUtfToTChar(dir, -1, &ds);
240      handle = (*tclWinProcs->findFirstFileProc)(nativeName, &data);      handle = (*tclWinProcs->findFirstFileProc)(nativeName, &data);
241      Tcl_DStringFree(&ds);      Tcl_DStringFree(&ds);
242    
243      if (handle == INVALID_HANDLE_VALUE) {      if (handle == INVALID_HANDLE_VALUE) {
244          message = "couldn't read directory \"";          message = "couldn't read directory \"";
245          goto error;          goto error;
246      }      }
247    
248      /*      /*
249       * Clean up the tail pointer.  Leave the tail pointing to the       * Clean up the tail pointer.  Leave the tail pointing to the
250       * first character after the path separator or NULL.       * first character after the path separator or NULL.
251       */       */
252    
253      if (*tail == '\\') {      if (*tail == '\\') {
254          tail++;          tail++;
255      }      }
256      if (*tail == '\0') {      if (*tail == '\0') {
257          tail = NULL;          tail = NULL;
258      } else {      } else {
259          tail++;          tail++;
260      }      }
261    
262      /*      /*
263       * Check to see if the pattern needs to compare with dot files.       * Check to see if the pattern needs to compare with dot files.
264       */       */
265    
266      if ((newPattern[0] == '.')      if ((newPattern[0] == '.')
267              || ((pattern[0] == '\\') && (pattern[1] == '.'))) {              || ((pattern[0] == '\\') && (pattern[1] == '.'))) {
268          matchDotFiles = 1;          matchDotFiles = 1;
269      } else {      } else {
270          matchDotFiles = 0;          matchDotFiles = 0;
271      }      }
272    
273      /*      /*
274       * Now iterate over all of the files in the directory.       * Now iterate over all of the files in the directory.
275       */       */
276    
277      resultPtr = Tcl_GetObjResult(interp);      resultPtr = Tcl_GetObjResult(interp);
278      for (found = 1; found != 0;      for (found = 1; found != 0;
279              found = (*tclWinProcs->findNextFileProc)(handle, &data)) {              found = (*tclWinProcs->findNextFileProc)(handle, &data)) {
280          TCHAR *nativeMatchResult;          TCHAR *nativeMatchResult;
281          char *name, *fname;          char *name, *fname;
282    
283          if (tclWinProcs->useWide) {          if (tclWinProcs->useWide) {
284              nativeName = (TCHAR *) data.w.cFileName;              nativeName = (TCHAR *) data.w.cFileName;
285          } else {          } else {
286              nativeName = (TCHAR *) data.a.cFileName;              nativeName = (TCHAR *) data.a.cFileName;
287          }          }
288          name = Tcl_WinTCharToUtf(nativeName, -1, &ds);          name = Tcl_WinTCharToUtf(nativeName, -1, &ds);
289    
290          /*          /*
291           * Check to see if the file matches the pattern.  We need to convert           * Check to see if the file matches the pattern.  We need to convert
292           * the file name to lower case for comparison purposes.  Note that we           * the file name to lower case for comparison purposes.  Note that we
293           * are ignoring the case sensitivity flag because Windows doesn't honor           * are ignoring the case sensitivity flag because Windows doesn't honor
294           * case even if the volume is case sensitive.  If the volume also           * case even if the volume is case sensitive.  If the volume also
295           * doesn't preserve case, then we previously returned the lower case           * doesn't preserve case, then we previously returned the lower case
296           * form of the name.  This didn't seem quite right since there are           * form of the name.  This didn't seem quite right since there are
297           * non-case-preserving volumes that actually return mixed case.  So now           * non-case-preserving volumes that actually return mixed case.  So now
298           * we are returning exactly what we get from the system.           * we are returning exactly what we get from the system.
299           */           */
300    
301          Tcl_UtfToLower(name);          Tcl_UtfToLower(name);
302          nativeMatchResult = NULL;          nativeMatchResult = NULL;
303    
304          if ((matchDotFiles == 0) && (name[0] == '.')) {          if ((matchDotFiles == 0) && (name[0] == '.')) {
305              /*              /*
306               * Ignore hidden files.               * Ignore hidden files.
307               */               */
308          } else if (Tcl_StringMatch(name, newPattern) != 0) {          } else if (Tcl_StringMatch(name, newPattern) != 0) {
309              nativeMatchResult = nativeName;              nativeMatchResult = nativeName;
310          }          }
311          Tcl_DStringFree(&ds);          Tcl_DStringFree(&ds);
312    
313          if (nativeMatchResult == NULL) {          if (nativeMatchResult == NULL) {
314              continue;              continue;
315          }          }
316    
317          /*          /*
318           * If the file matches, then we need to process the remainder of the           * If the file matches, then we need to process the remainder of the
319           * path.  If there are more characters to process, then ensure matching           * path.  If there are more characters to process, then ensure matching
320           * files are directories and call TclDoGlob. Otherwise, just add the           * files are directories and call TclDoGlob. Otherwise, just add the
321           * file to the result.           * file to the result.
322           */           */
323    
324          name = Tcl_WinTCharToUtf(nativeMatchResult, -1, &ds);          name = Tcl_WinTCharToUtf(nativeMatchResult, -1, &ds);
325          Tcl_DStringAppend(dirPtr, name, -1);          Tcl_DStringAppend(dirPtr, name, -1);
326          Tcl_DStringFree(&ds);          Tcl_DStringFree(&ds);
327    
328          fname = Tcl_DStringValue(dirPtr);          fname = Tcl_DStringValue(dirPtr);
329          nativeName = Tcl_WinUtfToTChar(fname, Tcl_DStringLength(dirPtr), &ds);          nativeName = Tcl_WinUtfToTChar(fname, Tcl_DStringLength(dirPtr), &ds);
330          attr = (*tclWinProcs->getFileAttributesProc)(nativeName);          attr = (*tclWinProcs->getFileAttributesProc)(nativeName);
331          Tcl_DStringFree(&ds);          Tcl_DStringFree(&ds);
332    
333          if (tail == NULL) {          if (tail == NULL) {
334              int typeOk = 1;              int typeOk = 1;
335              if (types != NULL) {              if (types != NULL) {
336                  if (types->perm != 0) {                  if (types->perm != 0) {
337                      if (                      if (
338                          ((types->perm & TCL_GLOB_PERM_RONLY) &&                          ((types->perm & TCL_GLOB_PERM_RONLY) &&
339                                  !(attr & FILE_ATTRIBUTE_READONLY)) ||                                  !(attr & FILE_ATTRIBUTE_READONLY)) ||
340                          ((types->perm & TCL_GLOB_PERM_HIDDEN) &&                          ((types->perm & TCL_GLOB_PERM_HIDDEN) &&
341                                  !(attr & FILE_ATTRIBUTE_HIDDEN)) ||                                  !(attr & FILE_ATTRIBUTE_HIDDEN)) ||
342                          ((types->perm & TCL_GLOB_PERM_R) &&                          ((types->perm & TCL_GLOB_PERM_R) &&
343                                  (TclpAccess(fname, R_OK) != 0)) ||                                  (TclpAccess(fname, R_OK) != 0)) ||
344                          ((types->perm & TCL_GLOB_PERM_W) &&                          ((types->perm & TCL_GLOB_PERM_W) &&
345                                  (TclpAccess(fname, W_OK) != 0)) ||                                  (TclpAccess(fname, W_OK) != 0)) ||
346                          ((types->perm & TCL_GLOB_PERM_X) &&                          ((types->perm & TCL_GLOB_PERM_X) &&
347                                  (TclpAccess(fname, X_OK) != 0))                                  (TclpAccess(fname, X_OK) != 0))
348                          ) {                          ) {
349                          typeOk = 0;                          typeOk = 0;
350                      }                      }
351                  }                  }
352                  if (typeOk && types->type != 0) {                  if (typeOk && types->type != 0) {
353                      struct stat buf;                      struct stat buf;
354                      /*                      /*
355                       * We must match at least one flag to be listed                       * We must match at least one flag to be listed
356                       */                       */
357                      typeOk = 0;                      typeOk = 0;
358                      if (TclpLstat(fname, &buf) >= 0) {                      if (TclpLstat(fname, &buf) >= 0) {
359                          /*                          /*
360                           * In order bcdpfls as in 'find -t'                           * In order bcdpfls as in 'find -t'
361                           */                           */
362                          if (                          if (
363                              ((types->type & TCL_GLOB_TYPE_BLOCK) &&                              ((types->type & TCL_GLOB_TYPE_BLOCK) &&
364                                      S_ISBLK(buf.st_mode)) ||                                      S_ISBLK(buf.st_mode)) ||
365                              ((types->type & TCL_GLOB_TYPE_CHAR) &&                              ((types->type & TCL_GLOB_TYPE_CHAR) &&
366                                      S_ISCHR(buf.st_mode)) ||                                      S_ISCHR(buf.st_mode)) ||
367                              ((types->type & TCL_GLOB_TYPE_DIR) &&                              ((types->type & TCL_GLOB_TYPE_DIR) &&
368                                      S_ISDIR(buf.st_mode)) ||                                      S_ISDIR(buf.st_mode)) ||
369                              ((types->type & TCL_GLOB_TYPE_PIPE) &&                              ((types->type & TCL_GLOB_TYPE_PIPE) &&
370                                      S_ISFIFO(buf.st_mode)) ||                                      S_ISFIFO(buf.st_mode)) ||
371                              ((types->type & TCL_GLOB_TYPE_FILE) &&                              ((types->type & TCL_GLOB_TYPE_FILE) &&
372                                      S_ISREG(buf.st_mode))                                      S_ISREG(buf.st_mode))
373  #ifdef S_ISLNK  #ifdef S_ISLNK
374                              || ((types->type & TCL_GLOB_TYPE_LINK) &&                              || ((types->type & TCL_GLOB_TYPE_LINK) &&
375                                      S_ISLNK(buf.st_mode))                                      S_ISLNK(buf.st_mode))
376  #endif  #endif
377  #ifdef S_ISSOCK  #ifdef S_ISSOCK
378                              || ((types->type & TCL_GLOB_TYPE_SOCK) &&                              || ((types->type & TCL_GLOB_TYPE_SOCK) &&
379                                      S_ISSOCK(buf.st_mode))                                      S_ISSOCK(buf.st_mode))
380  #endif  #endif
381                              ) {                              ) {
382                              typeOk = 1;                              typeOk = 1;
383                          }                          }
384                      } else {                      } else {
385                          /* Posix error occurred */                          /* Posix error occurred */
386                      }                      }
387                  }                                }              
388              }              }
389              if (typeOk) {              if (typeOk) {
390                  Tcl_ListObjAppendElement(interp, resultPtr,                  Tcl_ListObjAppendElement(interp, resultPtr,
391                          Tcl_NewStringObj(fname, Tcl_DStringLength(dirPtr)));                          Tcl_NewStringObj(fname, Tcl_DStringLength(dirPtr)));
392              }              }
393          } else if (attr & FILE_ATTRIBUTE_DIRECTORY) {          } else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
394              Tcl_DStringAppend(dirPtr, "/", 1);              Tcl_DStringAppend(dirPtr, "/", 1);
395              result = TclDoGlob(interp, separators, dirPtr, tail, types);              result = TclDoGlob(interp, separators, dirPtr, tail, types);
396              if (result != TCL_OK) {              if (result != TCL_OK) {
397                  break;                  break;
398              }              }
399          }          }
400          Tcl_DStringSetLength(dirPtr, dirLength);          Tcl_DStringSetLength(dirPtr, dirLength);
401      }      }
402    
403      FindClose(handle);      FindClose(handle);
404      Tcl_DStringFree(&dirString);      Tcl_DStringFree(&dirString);
405      Tcl_DStringFree(&patternString);      Tcl_DStringFree(&patternString);
406    
407      return result;      return result;
408    
409      error:      error:
410      Tcl_DStringFree(&dirString);      Tcl_DStringFree(&dirString);
411      TclWinConvertError(GetLastError());      TclWinConvertError(GetLastError());
412      Tcl_ResetResult(interp);      Tcl_ResetResult(interp);
413      Tcl_AppendResult(interp, message, Tcl_DStringValue(dirPtr), "\": ",      Tcl_AppendResult(interp, message, Tcl_DStringValue(dirPtr), "\": ",
414              Tcl_PosixError(interp), (char *) NULL);              Tcl_PosixError(interp), (char *) NULL);
415      return TCL_ERROR;      return TCL_ERROR;
416  }  }
417    
418  /*  /*
419   * TclpMatchFiles --   * TclpMatchFiles --
420   *   *
421   * This function is now obsolete.  Call the above function   * This function is now obsolete.  Call the above function
422   * 'TclpMatchFilesTypes' instead.   * 'TclpMatchFilesTypes' instead.
423   */   */
424  int  int
425  TclpMatchFiles(  TclpMatchFiles(
426      Tcl_Interp *interp,         /* Interpreter to receive results. */      Tcl_Interp *interp,         /* Interpreter to receive results. */
427      char *separators,           /* Directory separators to pass to TclDoGlob. */      char *separators,           /* Directory separators to pass to TclDoGlob. */
428      Tcl_DString *dirPtr,        /* Contains path to directory to search. */      Tcl_DString *dirPtr,        /* Contains path to directory to search. */
429      char *pattern,              /* Pattern to match against. */      char *pattern,              /* Pattern to match against. */
430      char *tail)                 /* Pointer to end of pattern.  Tail must      char *tail)                 /* Pointer to end of pattern.  Tail must
431                                   * point to a location in pattern and must                                   * point to a location in pattern and must
432                                   * not be static.*/                                   * not be static.*/
433  {  {
434      return TclpMatchFilesTypes(interp,separators,dirPtr,pattern,tail,NULL);      return TclpMatchFilesTypes(interp,separators,dirPtr,pattern,tail,NULL);
435  }  }
436    
437  /*  /*
438   *----------------------------------------------------------------------   *----------------------------------------------------------------------
439   *   *
440   * TclpGetUserHome --   * TclpGetUserHome --
441   *   *
442   *      This function takes the passed in user name and finds the   *      This function takes the passed in user name and finds the
443   *      corresponding home directory specified in the password file.   *      corresponding home directory specified in the password file.
444   *   *
445   * Results:   * Results:
446   *      The result is a pointer to a string specifying the user's home   *      The result is a pointer to a string specifying the user's home
447   *      directory, or NULL if the user's home directory could not be   *      directory, or NULL if the user's home directory could not be
448   *      determined.  Storage for the result string is allocated in   *      determined.  Storage for the result string is allocated in
449   *      bufferPtr; the caller must call Tcl_DStringFree() when the result   *      bufferPtr; the caller must call Tcl_DStringFree() when the result
450   *      is no longer needed.   *      is no longer needed.
451   *   *
452   * Side effects:   * Side effects:
453   *      None.   *      None.
454   *   *
455   *----------------------------------------------------------------------   *----------------------------------------------------------------------
456   */   */
457    
458  char *  char *
459  TclpGetUserHome(name, bufferPtr)  TclpGetUserHome(name, bufferPtr)
460      CONST char *name;           /* User name for desired home directory. */      CONST char *name;           /* User name for desired home directory. */
461      Tcl_DString *bufferPtr;     /* Uninitialized or free DString filled      Tcl_DString *bufferPtr;     /* Uninitialized or free DString filled
462                                   * with name of user's home directory. */                                   * with name of user's home directory. */
463  {  {
464      char *result;      char *result;
465      HINSTANCE netapiInst;      HINSTANCE netapiInst;
466    
467      result = NULL;      result = NULL;
468    
469      Tcl_DStringInit(bufferPtr);      Tcl_DStringInit(bufferPtr);
470    
471      netapiInst = LoadLibraryA("netapi32.dll");      netapiInst = LoadLibraryA("netapi32.dll");
472      if (netapiInst != NULL) {      if (netapiInst != NULL) {
473          NETAPIBUFFERFREEPROC *netApiBufferFreeProc;          NETAPIBUFFERFREEPROC *netApiBufferFreeProc;
474          NETGETDCNAMEPROC *netGetDCNameProc;          NETGETDCNAMEPROC *netGetDCNameProc;
475          NETUSERGETINFOPROC *netUserGetInfoProc;          NETUSERGETINFOPROC *netUserGetInfoProc;
476    
477          netApiBufferFreeProc = (NETAPIBUFFERFREEPROC *)          netApiBufferFreeProc = (NETAPIBUFFERFREEPROC *)
478                  GetProcAddress(netapiInst, "NetApiBufferFree");                  GetProcAddress(netapiInst, "NetApiBufferFree");
479          netGetDCNameProc = (NETGETDCNAMEPROC *)          netGetDCNameProc = (NETGETDCNAMEPROC *)
480                  GetProcAddress(netapiInst, "NetGetDCName");                  GetProcAddress(netapiInst, "NetGetDCName");
481          netUserGetInfoProc = (NETUSERGETINFOPROC *)          netUserGetInfoProc = (NETUSERGETINFOPROC *)
482                  GetProcAddress(netapiInst, "NetUserGetInfo");                  GetProcAddress(netapiInst, "NetUserGetInfo");
483          if ((netUserGetInfoProc != NULL) && (netGetDCNameProc != NULL)          if ((netUserGetInfoProc != NULL) && (netGetDCNameProc != NULL)
484                  && (netApiBufferFreeProc != NULL)) {                  && (netApiBufferFreeProc != NULL)) {
485              USER_INFO_1 *uiPtr;              USER_INFO_1 *uiPtr;
486              Tcl_DString ds;              Tcl_DString ds;
487              int nameLen, badDomain;              int nameLen, badDomain;
488              char *domain;              char *domain;
489              WCHAR *wName, *wHomeDir, *wDomain;              WCHAR *wName, *wHomeDir, *wDomain;
490              WCHAR buf[MAX_PATH];              WCHAR buf[MAX_PATH];
491    
492              badDomain = 0;              badDomain = 0;
493              nameLen = -1;              nameLen = -1;
494              wDomain = NULL;              wDomain = NULL;
495              domain = strchr(name, '@');              domain = strchr(name, '@');
496              if (domain != NULL) {              if (domain != NULL) {
497                  Tcl_DStringInit(&ds);                  Tcl_DStringInit(&ds);
498                  wName = Tcl_UtfToUniCharDString(domain + 1, -1, &ds);                  wName = Tcl_UtfToUniCharDString(domain + 1, -1, &ds);
499                  badDomain = (*netGetDCNameProc)(NULL, wName,                  badDomain = (*netGetDCNameProc)(NULL, wName,
500                          (LPBYTE *) &wDomain);                          (LPBYTE *) &wDomain);
501                  Tcl_DStringFree(&ds);                  Tcl_DStringFree(&ds);
502                  nameLen = domain - name;                  nameLen = domain - name;
503              }              }
504              if (badDomain == 0) {              if (badDomain == 0) {
505                  Tcl_DStringInit(&ds);                  Tcl_DStringInit(&ds);
506                  wName = Tcl_UtfToUniCharDString(name, nameLen, &ds);                  wName = Tcl_UtfToUniCharDString(name, nameLen, &ds);
507                  if ((*netUserGetInfoProc)(wDomain, wName, 1,                  if ((*netUserGetInfoProc)(wDomain, wName, 1,
508                          (LPBYTE *) &uiPtr) == 0) {                          (LPBYTE *) &uiPtr) == 0) {
509                      wHomeDir = uiPtr->usri1_home_dir;                      wHomeDir = uiPtr->usri1_home_dir;
510                      if ((wHomeDir != NULL) && (wHomeDir[0] != L'\0')) {                      if ((wHomeDir != NULL) && (wHomeDir[0] != L'\0')) {
511                          Tcl_UniCharToUtfDString(wHomeDir, lstrlenW(wHomeDir),                          Tcl_UniCharToUtfDString(wHomeDir, lstrlenW(wHomeDir),
512                                  bufferPtr);                                  bufferPtr);
513                      } else {                      } else {
514                          /*                          /*
515                           * User exists but has no home dir.  Return                           * User exists but has no home dir.  Return
516                           * "{Windows Drive}:/users/default".                           * "{Windows Drive}:/users/default".
517                           */                           */
518    
519                          GetWindowsDirectoryW(buf, MAX_PATH);                          GetWindowsDirectoryW(buf, MAX_PATH);
520                          Tcl_UniCharToUtfDString(buf, 2, bufferPtr);                          Tcl_UniCharToUtfDString(buf, 2, bufferPtr);
521                          Tcl_DStringAppend(bufferPtr, "/users/default", -1);                          Tcl_DStringAppend(bufferPtr, "/users/default", -1);
522                      }                      }
523                      result = Tcl_DStringValue(bufferPtr);                      result = Tcl_DStringValue(bufferPtr);
524                      (*netApiBufferFreeProc)((void *) uiPtr);                      (*netApiBufferFreeProc)((void *) uiPtr);
525                  }                  }
526                  Tcl_DStringFree(&ds);                  Tcl_DStringFree(&ds);
527              }              }
528              if (wDomain != NULL) {              if (wDomain != NULL) {
529                  (*netApiBufferFreeProc)((void *) wDomain);                  (*netApiBufferFreeProc)((void *) wDomain);
530              }              }
531          }          }
532          FreeLibrary(netapiInst);          FreeLibrary(netapiInst);
533      }      }
534      if (result == NULL) {      if (result == NULL) {
535          /*          /*
536           * Look in the "Password Lists" section of system.ini for the           * Look in the "Password Lists" section of system.ini for the
537           * local user.  There are also entries in that section that begin           * local user.  There are also entries in that section that begin
538           * with a "*" character that are used by Windows for other           * with a "*" character that are used by Windows for other
539           * purposes; ignore user names beginning with a "*".           * purposes; ignore user names beginning with a "*".
540           */           */
541    
542          char buf[MAX_PATH];          char buf[MAX_PATH];
543    
544          if (name[0] != '*') {          if (name[0] != '*') {
545              if (GetPrivateProfileStringA("Password Lists", name, "", buf,              if (GetPrivateProfileStringA("Password Lists", name, "", buf,
546                      MAX_PATH, "system.ini") > 0) {                      MAX_PATH, "system.ini") > 0) {
547                  /*                  /*
548                   * User exists, but there is no such thing as a home                   * User exists, but there is no such thing as a home
549                   * directory in system.ini.  Return "{Windows drive}:/".                   * directory in system.ini.  Return "{Windows drive}:/".
550                   */                   */
551    
552                  GetWindowsDirectoryA(buf, MAX_PATH);                  GetWindowsDirectoryA(buf, MAX_PATH);
553                  Tcl_DStringAppend(bufferPtr, buf, 3);                  Tcl_DStringAppend(bufferPtr, buf, 3);
554                  result = Tcl_DStringValue(bufferPtr);                  result = Tcl_DStringValue(bufferPtr);
555              }              }
556          }          }
557      }      }
558    
559      return result;      return result;
560  }  }
561    
562  /*  /*
563   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
564   *   *
565   * TclpAccess --   * TclpAccess --
566   *   *
567   *      This function replaces the library version of access(), fixing the   *      This function replaces the library version of access(), fixing the
568   *      following bugs:   *      following bugs:
569   *   *
570   *      1. access() returns that all files have execute permission.   *      1. access() returns that all files have execute permission.
571   *   *
572   * Results:   * Results:
573   *      See access documentation.   *      See access documentation.
574   *   *
575   * Side effects:   * Side effects:
576   *      See access documentation.   *      See access documentation.
577   *   *
578   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
579   */   */
580    
581  int  int
582  TclpAccess(  TclpAccess(
583      CONST char *path,           /* Path of file to access (UTF-8). */      CONST char *path,           /* Path of file to access (UTF-8). */
584      int mode)                   /* Permission setting. */      int mode)                   /* Permission setting. */
585  {  {
586      Tcl_DString ds;      Tcl_DString ds;
587      TCHAR *nativePath;      TCHAR *nativePath;
588      DWORD attr;      DWORD attr;
589    
590      nativePath = Tcl_WinUtfToTChar(path, -1, &ds);      nativePath = Tcl_WinUtfToTChar(path, -1, &ds);
591      attr = (*tclWinProcs->getFileAttributesProc)(nativePath);      attr = (*tclWinProcs->getFileAttributesProc)(nativePath);
592      Tcl_DStringFree(&ds);      Tcl_DStringFree(&ds);
593    
594      if (attr == 0xffffffff) {      if (attr == 0xffffffff) {
595          /*          /*
596           * File doesn't exist.           * File doesn't exist.
597           */           */
598    
599          TclWinConvertError(GetLastError());          TclWinConvertError(GetLastError());
600          return -1;          return -1;
601      }      }
602    
603      if ((mode & W_OK) && (attr & FILE_ATTRIBUTE_READONLY)) {      if ((mode & W_OK) && (attr & FILE_ATTRIBUTE_READONLY)) {
604          /*          /*
605           * File is not writable.           * File is not writable.
606           */           */
607    
608          Tcl_SetErrno(EACCES);          Tcl_SetErrno(EACCES);
609          return -1;          return -1;
610      }      }
611    
612      if (mode & X_OK) {      if (mode & X_OK) {
613          CONST char *p;          CONST char *p;
614    
615          if (attr & FILE_ATTRIBUTE_DIRECTORY) {          if (attr & FILE_ATTRIBUTE_DIRECTORY) {
616              /*              /*
617               * Directories are always executable.               * Directories are always executable.
618               */               */
619                            
620              return 0;              return 0;
621          }          }
622          p = strrchr(path, '.');          p = strrchr(path, '.');
623          if (p != NULL) {          if (p != NULL) {
624              p++;              p++;
625              if ((stricmp(p, "exe") == 0)              if ((stricmp(p, "exe") == 0)
626                      || (stricmp(p, "com") == 0)                      || (stricmp(p, "com") == 0)
627                      || (stricmp(p, "bat") == 0)) {                      || (stricmp(p, "bat") == 0)) {
628                  /*                  /*
629                   * File that ends with .exe, .com, or .bat is executable.                   * File that ends with .exe, .com, or .bat is executable.
630                   */                   */
631    
632                  return 0;                  return 0;
633              }              }
634          }          }
635          Tcl_SetErrno(EACCES);          Tcl_SetErrno(EACCES);
636          return -1;          return -1;
637      }      }
638    
639      return 0;      return 0;
640  }  }
641    
642  /*  /*
643   *----------------------------------------------------------------------   *----------------------------------------------------------------------
644   *   *
645   * TclpChdir --   * TclpChdir --
646   *   *
647   *      This function replaces the library version of chdir().   *      This function replaces the library version of chdir().
648   *   *
649   * Results:   * Results:
650   *      See chdir() documentation.   *      See chdir() documentation.
651   *   *
652   * Side effects:   * Side effects:
653   *      See chdir() documentation.     *      See chdir() documentation.  
654   *   *
655   *----------------------------------------------------------------------   *----------------------------------------------------------------------
656   */   */
657    
658  int  int
659  TclpChdir(path)  TclpChdir(path)
660      CONST char *path;           /* Path to new working directory (UTF-8). */      CONST char *path;           /* Path to new working directory (UTF-8). */
661  {  {
662      int result;      int result;
663      Tcl_DString ds;      Tcl_DString ds;
664      TCHAR *nativePath;      TCHAR *nativePath;
665    
666      nativePath = Tcl_WinUtfToTChar(path, -1, &ds);      nativePath = Tcl_WinUtfToTChar(path, -1, &ds);
667      result = (*tclWinProcs->setCurrentDirectoryProc)(nativePath);      result = (*tclWinProcs->setCurrentDirectoryProc)(nativePath);
668      Tcl_DStringFree(&ds);      Tcl_DStringFree(&ds);
669    
670      if (result == 0) {      if (result == 0) {
671          TclWinConvertError(GetLastError());          TclWinConvertError(GetLastError());
672          return -1;          return -1;
673      }      }
674      return 0;      return 0;
675  }  }
676    
677  /*  /*
678   *----------------------------------------------------------------------   *----------------------------------------------------------------------
679   *   *
680   * TclpGetCwd --   * TclpGetCwd --
681   *   *
682   *      This function replaces the library version of getcwd().   *      This function replaces the library version of getcwd().
683   *   *
684   * Results:   * Results:
685   *      The result is a pointer to a string specifying the current   *      The result is a pointer to a string specifying the current
686   *      directory, or NULL if the current directory could not be   *      directory, or NULL if the current directory could not be
687   *      determined.  If NULL is returned, an error message is left in the   *      determined.  If NULL is returned, an error message is left in the
688   *      interp's result.  Storage for the result string is allocated in   *      interp's result.  Storage for the result string is allocated in
689   *      bufferPtr; the caller must call Tcl_DStringFree() when the result   *      bufferPtr; the caller must call Tcl_DStringFree() when the result
690   *      is no longer needed.   *      is no longer needed.
691   *   *
692   * Side effects:   * Side effects:
693   *      None.   *      None.
694   *   *
695   *----------------------------------------------------------------------   *----------------------------------------------------------------------
696   */   */
697    
698  char *  char *
699  TclpGetCwd(interp, bufferPtr)  TclpGetCwd(interp, bufferPtr)
700      Tcl_Interp *interp;         /* If non-NULL, used for error reporting. */      Tcl_Interp *interp;         /* If non-NULL, used for error reporting. */
701      Tcl_DString *bufferPtr;     /* Uninitialized or free DString filled      Tcl_DString *bufferPtr;     /* Uninitialized or free DString filled
702                                   * with name of current directory. */                                   * with name of current directory. */
703  {  {
704      WCHAR buffer[MAX_PATH];      WCHAR buffer[MAX_PATH];
705      char *p;      char *p;
706    
707      if ((*tclWinProcs->getCurrentDirectoryProc)(MAX_PATH, buffer) == 0) {      if ((*tclWinProcs->getCurrentDirectoryProc)(MAX_PATH, buffer) == 0) {
708          TclWinConvertError(GetLastError());          TclWinConvertError(GetLastError());
709          if (interp != NULL) {          if (interp != NULL) {
710              Tcl_AppendResult(interp,              Tcl_AppendResult(interp,
711                      "error getting working directory name: ",                      "error getting working directory name: ",
712                      Tcl_PosixError(interp), (char *) NULL);                      Tcl_PosixError(interp), (char *) NULL);
713          }          }
714          return NULL;          return NULL;
715      }      }
716    
717      /*      /*
718       * Watch for the wierd Windows c:\\UNC syntax.       * Watch for the wierd Windows c:\\UNC syntax.
719       */       */
720    
721      if (tclWinProcs->useWide) {      if (tclWinProcs->useWide) {
722          WCHAR *native;          WCHAR *native;
723    
724          native = (WCHAR *) buffer;          native = (WCHAR *) buffer;
725          if ((native[0] != '\0') && (native[1] == ':')          if ((native[0] != '\0') && (native[1] == ':')
726                  && (native[2] == '\\') && (native[3] == '\\')) {                  && (native[2] == '\\') && (native[3] == '\\')) {
727              native += 2;              native += 2;
728          }          }
729          Tcl_WinTCharToUtf((TCHAR *) native, -1, bufferPtr);          Tcl_WinTCharToUtf((TCHAR *) native, -1, bufferPtr);
730      } else {      } else {
731          char *native;          char *native;
732    
733          native = (char *) buffer;          native = (char *) buffer;
734          if ((native[0] != '\0') && (native[1] == ':')          if ((native[0] != '\0') && (native[1] == ':')
735                  && (native[2] == '\\') && (native[3] == '\\')) {                  && (native[2] == '\\') && (native[3] == '\\')) {
736              native += 2;              native += 2;
737          }          }
738          Tcl_WinTCharToUtf((TCHAR *) native, -1, bufferPtr);          Tcl_WinTCharToUtf((TCHAR *) native, -1, bufferPtr);
739      }      }
740    
741      /*      /*
742       * Convert to forward slashes for easier use in scripts.       * Convert to forward slashes for easier use in scripts.
743       */       */
744                                
745      for (p = Tcl_DStringValue(bufferPtr); *p != '\0'; p++) {      for (p = Tcl_DStringValue(bufferPtr); *p != '\0'; p++) {
746          if (*p == '\\') {          if (*p == '\\') {
747              *p = '/';              *p = '/';
748          }          }
749      }      }
750      return Tcl_DStringValue(bufferPtr);      return Tcl_DStringValue(bufferPtr);
751  }  }
752    
753  /*  /*
754   *----------------------------------------------------------------------   *----------------------------------------------------------------------
755   *   *
756   * TclpStat --   * TclpStat --
757   *   *
758   *      This function replaces the library version of stat(), fixing   *      This function replaces the library version of stat(), fixing
759   *      the following bugs:   *      the following bugs:
760   *   *
761   *      1. stat("c:") returns an error.   *      1. stat("c:") returns an error.
762   *      2. Borland stat() return time in GMT instead of localtime.   *      2. Borland stat() return time in GMT instead of localtime.
763   *      3. stat("\\server\mount") would return error.   *      3. stat("\\server\mount") would return error.
764   *      4. Accepts slashes or backslashes.   *      4. Accepts slashes or backslashes.
765   *      5. st_dev and st_rdev were wrong for UNC paths.   *      5. st_dev and st_rdev were wrong for UNC paths.
766   *   *
767   * Results:   * Results:
768   *      See stat documentation.   *      See stat documentation.
769   *   *
770   * Side effects:   * Side effects:
771   *      See stat documentation.   *      See stat documentation.
772   *   *
773   *----------------------------------------------------------------------   *----------------------------------------------------------------------
774   */   */
775    
776  int  int
777  TclpStat(path, statPtr)  TclpStat(path, statPtr)
778      CONST char *path;           /* Path of file to stat (UTF-8). */      CONST char *path;           /* Path of file to stat (UTF-8). */
779      struct stat *statPtr;       /* Filled with results of stat call. */      struct stat *statPtr;       /* Filled with results of stat call. */
780  {  {
781      Tcl_DString ds;      Tcl_DString ds;
782      TCHAR *nativePath;      TCHAR *nativePath;
783      WIN32_FIND_DATAT data;      WIN32_FIND_DATAT data;
784      HANDLE handle;      HANDLE handle;
785      DWORD attr;      DWORD attr;
786      WCHAR nativeFullPath[MAX_PATH];      WCHAR nativeFullPath[MAX_PATH];
787      TCHAR *nativePart;      TCHAR *nativePart;
788      char *p, *fullPath;      char *p, *fullPath;
789      int dev, mode;      int dev, mode;
790    
791      /*      /*
792       * Eliminate file names containing wildcard characters, or subsequent       * Eliminate file names containing wildcard characters, or subsequent
793       * call to FindFirstFile() will expand them, matching some other file.       * call to FindFirstFile() will expand them, matching some other file.
794       */       */
795    
796      if (strpbrk(path, "?*") != NULL) {      if (strpbrk(path, "?*") != NULL) {
797          Tcl_SetErrno(ENOENT);          Tcl_SetErrno(ENOENT);
798          return -1;          return -1;
799      }      }
800    
801      nativePath = Tcl_WinUtfToTChar(path, -1, &ds);      nativePath = Tcl_WinUtfToTChar(path, -1, &ds);
802      handle = (*tclWinProcs->findFirstFileProc)(nativePath, &data);      handle = (*tclWinProcs->findFirstFileProc)(nativePath, &data);
803      if (handle == INVALID_HANDLE_VALUE) {      if (handle == INVALID_HANDLE_VALUE) {
804          /*          /*
805           * FindFirstFile() doesn't work on root directories, so call           * FindFirstFile() doesn't work on root directories, so call
806           * GetFileAttributes() to see if the specified file exists.           * GetFileAttributes() to see if the specified file exists.
807           */           */
808    
809          attr = (*tclWinProcs->getFileAttributesProc)(nativePath);          attr = (*tclWinProcs->getFileAttributesProc)(nativePath);
810          if (attr == 0xffffffff) {          if (attr == 0xffffffff) {
811              Tcl_DStringFree(&ds);              Tcl_DStringFree(&ds);
812              Tcl_SetErrno(ENOENT);              Tcl_SetErrno(ENOENT);
813              return -1;              return -1;
814          }          }
815    
816          /*          /*
817           * Make up some fake information for this file.  It has the           * Make up some fake information for this file.  It has the
818           * correct file attributes and a time of 0.           * correct file attributes and a time of 0.
819           */           */
820    
821          memset(&data, 0, sizeof(data));          memset(&data, 0, sizeof(data));
822          data.a.dwFileAttributes = attr;          data.a.dwFileAttributes = attr;
823      } else {      } else {
824          FindClose(handle);          FindClose(handle);
825      }      }
826    
827      (*tclWinProcs->getFullPathNameProc)(nativePath, MAX_PATH, nativeFullPath,      (*tclWinProcs->getFullPathNameProc)(nativePath, MAX_PATH, nativeFullPath,
828              &nativePart);              &nativePart);
829    
830      Tcl_DStringFree(&ds);      Tcl_DStringFree(&ds);
831      fullPath = Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds);      fullPath = Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds);
832    
833      dev = -1;      dev = -1;
834      if ((fullPath[0] == '\\') && (fullPath[1] == '\\')) {      if ((fullPath[0] == '\\') && (fullPath[1] == '\\')) {
835          char *p;          char *p;
836          DWORD dw;          DWORD dw;
837          TCHAR *nativeVol;          TCHAR *nativeVol;
838          Tcl_DString volString;          Tcl_DString volString;
839    
840          p = strchr(fullPath + 2, '\\');          p = strchr(fullPath + 2, '\\');
841          p = strchr(p + 1, '\\');          p = strchr(p + 1, '\\');
842          if (p == NULL) {          if (p == NULL) {
843              /*              /*
844               * Add terminating backslash to fullpath or               * Add terminating backslash to fullpath or
845               * GetVolumeInformation() won't work.               * GetVolumeInformation() won't work.
846               */               */
847    
848              fullPath = Tcl_DStringAppend(&ds, "\\", 1);              fullPath = Tcl_DStringAppend(&ds, "\\", 1);
849              p = fullPath + Tcl_DStringLength(&ds);              p = fullPath + Tcl_DStringLength(&ds);
850          } else {          } else {
851              p++;              p++;
852          }          }
853          nativeVol = Tcl_WinUtfToTChar(fullPath, p - fullPath, &volString);          nativeVol = Tcl_WinUtfToTChar(fullPath, p - fullPath, &volString);
854          dw = (DWORD) -1;          dw = (DWORD) -1;
855          (*tclWinProcs->getVolumeInformationProc)(nativeVol, NULL, 0, &dw,          (*tclWinProcs->getVolumeInformationProc)(nativeVol, NULL, 0, &dw,
856                  NULL, NULL, NULL, 0);                  NULL, NULL, NULL, 0);
857          /*          /*
858           * GetFullPathName() turns special devices like "NUL" into "\\.\NUL",           * GetFullPathName() turns special devices like "NUL" into "\\.\NUL",
859           * but GetVolumeInformation() returns failure for "\\.\NUL".  This           * but GetVolumeInformation() returns failure for "\\.\NUL".  This
860           * will cause "NUL" to get a drive number of -1, which makes about           * will cause "NUL" to get a drive number of -1, which makes about
861           * as much sense as anything since the special devices don't live on           * as much sense as anything since the special devices don't live on
862           * any drive.           * any drive.
863           */           */
864    
865          dev = dw;          dev = dw;
866          Tcl_DStringFree(&volString);          Tcl_DStringFree(&volString);
867      } else if ((fullPath[0] != '\0') && (fullPath[1] == ':')) {      } else if ((fullPath[0] != '\0') && (fullPath[1] == ':')) {
868          dev = Tcl_UniCharToLower(fullPath[0]) - 'a';          dev = Tcl_UniCharToLower(fullPath[0]) - 'a';
869      }      }
870      Tcl_DStringFree(&ds);      Tcl_DStringFree(&ds);
871    
872      attr = data.a.dwFileAttributes;      attr = data.a.dwFileAttributes;
873      mode  = (attr & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR | S_IEXEC : S_IFREG;      mode  = (attr & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR | S_IEXEC : S_IFREG;
874      mode |= (attr & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;      mode |= (attr & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
875      p = strrchr(path, '.');      p = strrchr(path, '.');
876      if (p != NULL) {      if (p != NULL) {
877          if ((lstrcmpiA(p, ".exe") == 0)          if ((lstrcmpiA(p, ".exe") == 0)
878                  || (lstrcmpiA(p, ".com") == 0)                  || (lstrcmpiA(p, ".com") == 0)
879                  || (lstrcmpiA(p, ".bat") == 0)                  || (lstrcmpiA(p, ".bat") == 0)
880                  || (lstrcmpiA(p, ".pif") == 0)) {                  || (lstrcmpiA(p, ".pif") == 0)) {
881              mode |= S_IEXEC;              mode |= S_IEXEC;
882          }          }
883      }      }
884    
885      /*      /*
886       * Propagate the S_IREAD, S_IWRITE, S_IEXEC bits to the group and       * Propagate the S_IREAD, S_IWRITE, S_IEXEC bits to the group and
887       * other positions.       * other positions.
888       */       */
889    
890      mode |= (mode & 0x0700) >> 3;      mode |= (mode & 0x0700) >> 3;
891      mode |= (mode & 0x0700) >> 6;      mode |= (mode & 0x0700) >> 6;
892            
893      statPtr->st_dev     = (dev_t) dev;      statPtr->st_dev     = (dev_t) dev;
894      statPtr->st_ino     = 0;      statPtr->st_ino     = 0;
895      statPtr->st_mode    = (unsigned short) mode;      statPtr->st_mode    = (unsigned short) mode;
896      statPtr->st_nlink   = 1;      statPtr->st_nlink   = 1;
897      statPtr->st_uid     = 0;      statPtr->st_uid     = 0;
898      statPtr->st_gid     = 0;      statPtr->st_gid     = 0;
899      statPtr->st_rdev    = (dev_t) dev;      statPtr->st_rdev    = (dev_t) dev;
900      statPtr->st_size    = data.a.nFileSizeLow;      statPtr->st_size    = data.a.nFileSizeLow;
901      statPtr->st_atime   = ToCTime(data.a.ftLastAccessTime);      statPtr->st_atime   = ToCTime(data.a.ftLastAccessTime);
902      statPtr->st_mtime   = ToCTime(data.a.ftLastWriteTime);      statPtr->st_mtime   = ToCTime(data.a.ftLastWriteTime);
903      statPtr->st_ctime   = ToCTime(data.a.ftCreationTime);      statPtr->st_ctime   = ToCTime(data.a.ftCreationTime);
904      return 0;      return 0;
905  }  }
906    
907  static time_t  static time_t
908  ToCTime(  ToCTime(
909      FILETIME fileTime)          /* UTC Time to convert to local time_t. */      FILETIME fileTime)          /* UTC Time to convert to local time_t. */
910  {  {
911      FILETIME localFileTime;      FILETIME localFileTime;
912      SYSTEMTIME systemTime;      SYSTEMTIME systemTime;
913      struct tm tm;      struct tm tm;
914    
915      if (FileTimeToLocalFileTime(&fileTime, &localFileTime) == 0) {      if (FileTimeToLocalFileTime(&fileTime, &localFileTime) == 0) {
916          return 0;          return 0;
917      }      }
918      if (FileTimeToSystemTime(&localFileTime, &systemTime) == 0) {      if (FileTimeToSystemTime(&localFileTime, &systemTime) == 0) {
919          return 0;          return 0;
920      }      }
921      tm.tm_sec = systemTime.wSecond;      tm.tm_sec = systemTime.wSecond;
922      tm.tm_min = systemTime.wMinute;      tm.tm_min = systemTime.wMinute;
923      tm.tm_hour = systemTime.wHour;      tm.tm_hour = systemTime.wHour;
924      tm.tm_mday = systemTime.wDay;      tm.tm_mday = systemTime.wDay;
925      tm.tm_mon = systemTime.wMonth - 1;      tm.tm_mon = systemTime.wMonth - 1;
926      tm.tm_year = systemTime.wYear - 1900;      tm.tm_year = systemTime.wYear - 1900;
927      tm.tm_wday = 0;      tm.tm_wday = 0;
928      tm.tm_yday = 0;      tm.tm_yday = 0;
929      tm.tm_isdst = -1;      tm.tm_isdst = -1;
930    
931      return mktime(&tm);      return mktime(&tm);
932  }  }
933    
934  #if 0  #if 0
935    
936      /*      /*
937       * Borland's stat doesn't take into account localtime.       * Borland's stat doesn't take into account localtime.
938       */       */
939    
940      if ((result == 0) && (buf->st_mtime != 0)) {      if ((result == 0) && (buf->st_mtime != 0)) {
941          TIME_ZONE_INFORMATION tz;          TIME_ZONE_INFORMATION tz;
942          int time, bias;          int time, bias;
943    
944          time = GetTimeZoneInformation(&tz);          time = GetTimeZoneInformation(&tz);
945          bias = tz.Bias;          bias = tz.Bias;
946          if (time == TIME_ZONE_ID_DAYLIGHT) {          if (time == TIME_ZONE_ID_DAYLIGHT) {
947              bias += tz.DaylightBias;              bias += tz.DaylightBias;
948          }          }
949          bias *= 60;          bias *= 60;
950          buf->st_atime -= bias;          buf->st_atime -= bias;
951          buf->st_ctime -= bias;          buf->st_ctime -= bias;
952          buf->st_mtime -= bias;          buf->st_mtime -= bias;
953      }      }
954    
955  #endif  #endif
956    
957    
958  #if 0  #if 0
959  /*  /*
960   *-------------------------------------------------------------------------   *-------------------------------------------------------------------------
961   *   *
962   * TclWinResolveShortcut --   * TclWinResolveShortcut --
963   *   *
964   *      Resolve a potential Windows shortcut to get the actual file or   *      Resolve a potential Windows shortcut to get the actual file or
965   *      directory in question.     *      directory in question.  
966   *   *
967   * Results:   * Results:
968   *      Returns 1 if the shortcut could be resolved, or 0 if there was   *      Returns 1 if the shortcut could be resolved, or 0 if there was
969   *      an error or if the filename was not a shortcut.   *      an error or if the filename was not a shortcut.
970   *      If bufferPtr did hold the name of a shortcut, it is modified to   *      If bufferPtr did hold the name of a shortcut, it is modified to
971   *      hold the resolved target of the shortcut instead.   *      hold the resolved target of the shortcut instead.
972   *   *
973   * Side effects:   * Side effects:
974   *      Loads and unloads OLE package to determine if filename refers to   *      Loads and unloads OLE package to determine if filename refers to
975   *      a shortcut.   *      a shortcut.
976   *   *
977   *-------------------------------------------------------------------------   *-------------------------------------------------------------------------
978   */   */
979    
980  int  int
981  TclWinResolveShortcut(bufferPtr)  TclWinResolveShortcut(bufferPtr)
982      Tcl_DString *bufferPtr;     /* Holds name of file to resolve.  On      Tcl_DString *bufferPtr;     /* Holds name of file to resolve.  On
983                                   * return, holds resolved file name. */                                   * return, holds resolved file name. */
984  {  {
985      HRESULT hres;      HRESULT hres;
986      IShellLink *psl;      IShellLink *psl;
987      IPersistFile *ppf;      IPersistFile *ppf;
988      WIN32_FIND_DATA wfd;      WIN32_FIND_DATA wfd;
989      WCHAR wpath[MAX_PATH];      WCHAR wpath[MAX_PATH];
990      char *path, *ext;      char *path, *ext;
991      char realFileName[MAX_PATH];      char realFileName[MAX_PATH];
992    
993      /*      /*
994       * Windows system calls do not automatically resolve       * Windows system calls do not automatically resolve
995       * shortcuts like UNIX automatically will with symbolic links.       * shortcuts like UNIX automatically will with symbolic links.
996       */       */
997    
998      path = Tcl_DStringValue(bufferPtr);      path = Tcl_DStringValue(bufferPtr);
999      ext = strrchr(path, '.');      ext = strrchr(path, '.');
1000      if ((ext == NULL) || (stricmp(ext, ".lnk") != 0)) {      if ((ext == NULL) || (stricmp(ext, ".lnk") != 0)) {
1001          return 0;          return 0;
1002      }      }
1003    
1004      CoInitialize(NULL);      CoInitialize(NULL);
1005      path = Tcl_DStringValue(bufferPtr);      path = Tcl_DStringValue(bufferPtr);
1006      realFileName[0] = '\0';      realFileName[0] = '\0';
1007      hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,      hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1008              &IID_IShellLink, &psl);              &IID_IShellLink, &psl);
1009      if (SUCCEEDED(hres)) {      if (SUCCEEDED(hres)) {
1010          hres = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf);          hres = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf);
1011          if (SUCCEEDED(hres)) {          if (SUCCEEDED(hres)) {
1012              MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, sizeof(wpath));              MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, sizeof(wpath));
1013              hres = ppf->lpVtbl->Load(ppf, wpath, STGM_READ);              hres = ppf->lpVtbl->Load(ppf, wpath, STGM_READ);
1014              if (SUCCEEDED(hres)) {              if (SUCCEEDED(hres)) {
1015                  hres = psl->lpVtbl->Resolve(psl, NULL,                  hres = psl->lpVtbl->Resolve(psl, NULL,
1016                          SLR_ANY_MATCH | SLR_NO_UI);                          SLR_ANY_MATCH | SLR_NO_UI);
1017                  if (SUCCEEDED(hres)) {                  if (SUCCEEDED(hres)) {
1018                      hres = psl->lpVtbl->GetPath(psl, realFileName, MAX_PATH,                      hres = psl->lpVtbl->GetPath(psl, realFileName, MAX_PATH,
1019                              &wfd, 0);                              &wfd, 0);
1020                  }                  }
1021              }              }
1022              ppf->lpVtbl->Release(ppf);              ppf->lpVtbl->Release(ppf);
1023          }          }
1024          psl->lpVtbl->Release(psl);          psl->lpVtbl->Release(psl);
1025      }      }
1026      CoUninitialize();      CoUninitialize();
1027    
1028      if (realFileName[0] != '\0') {      if (realFileName[0] != '\0') {
1029          Tcl_DStringSetLength(bufferPtr, 0);          Tcl_DStringSetLength(bufferPtr, 0);
1030          Tcl_DStringAppend(bufferPtr, realFileName, -1);          Tcl_DStringAppend(bufferPtr, realFileName, -1);
1031          return 1;          return 1;
1032      }      }
1033      return 0;      return 0;
1034  }  }
1035  #endif  #endif
1036    
1037  /* End of tclwinfile.c */  /* End of tclwinfile.c */

Legend:
Removed from v.67  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25