This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Added win32/.gitignore to ignore stuff from the Windows build
[perl5.git] / win32 / vdir.h
index df9a10b..db7ec84 100644 (file)
 #ifndef ___VDir_H___
 #define ___VDir_H___
 
-const int driveCount = 30;
+/*
+ * Allow one slot for each possible drive letter
+ * and one additional slot for a UNC name
+ */
+const int driveCount = ('Z'-'A')+1+1;
 
 class VDir
 {
@@ -30,47 +34,48 @@ public:
     inline char* GetCurrentDirectoryA(int dwBufSize, char *lpBuffer)
     {
        char* ptr = dirTableA[nDefault];
-       while (dwBufSize--)
+       while (--dwBufSize)
        {
            if ((*lpBuffer++ = *ptr++) == '\0')
                break;
        }
-       return lpBuffer;
+        *lpBuffer = '\0';
+       return /* unused */ NULL;
     };
     inline WCHAR* GetCurrentDirectoryW(int dwBufSize, WCHAR *lpBuffer)
     {
        WCHAR* ptr = dirTableW[nDefault];
-       while (dwBufSize--)
+       while (--dwBufSize)
        {
            if ((*lpBuffer++ = *ptr++) == '\0')
                break;
        }
-       return lpBuffer;
+        *lpBuffer = '\0';
+       return /* unused */ NULL;
     };
 
-
     DWORD CalculateEnvironmentSpace(void);
     LPSTR BuildEnvironmentSpace(LPSTR lpStr);
 
 protected:
     int SetDirA(char const *pPath, int index);
+    int SetDirW(WCHAR const *pPath, int index);
     void FromEnvA(char *pEnv, int index);
+    void FromEnvW(WCHAR *pEnv, int index);
+
     inline const char *GetDefaultDirA(void)
     {
        return dirTableA[nDefault];
     };
-
     inline void SetDefaultDirA(char const *pPath, int index)
     {
        SetDirA(pPath, index);
        nDefault = index;
     };
-    int SetDirW(WCHAR const *pPath, int index);
     inline const WCHAR *GetDefaultDirW(void)
     {
        return dirTableW[nDefault];
     };
-
     inline void SetDefaultDirW(WCHAR const *pPath, int index)
     {
        SetDirW(pPath, index);
@@ -105,6 +110,8 @@ protected:
 
     inline int DriveIndex(char chr)
     {
+       if (chr == '\\' || chr == '/')
+           return ('Z'-'A')+1;
        return (chr | 0x20)-'a';
     };
 
@@ -128,9 +135,6 @@ VDir::VDir(int bManageDir /* = 1 */)
 void VDir::Init(VDir* pDir, VMem *p)
 {
     int index;
-    DWORD driveBits;
-    int nSave;
-    char szBuffer[MAX_PATH*driveCount];
 
     pMem = p;
     if (pDir) {
@@ -140,23 +144,47 @@ void VDir::Init(VDir* pDir, VMem *p)
        nDefault = pDir->GetDefault();
     }
     else {
-       nSave = bManageDirectory;
+       int bSave = bManageDirectory;
+       DWORD driveBits = GetLogicalDrives();
+        OSVERSIONINFO osver;
+
+        memset(&osver, 0, sizeof(osver));
+        osver.dwOSVersionInfoSize = sizeof(osver);
+        GetVersionEx(&osver);
+
        bManageDirectory = 0;
-       driveBits = GetLogicalDrives();
-       if (GetLogicalDriveStrings(sizeof(szBuffer), szBuffer)) {
-           char* pEnv = GetEnvironmentStrings();
-           char* ptr = szBuffer;
-           for (index = 0; index < driveCount; ++index) {
-               if (driveBits & (1<<index)) {
-                   ptr += SetDirA(ptr, index) + 1;
-                   FromEnvA(pEnv, index);
-               }
-           }
-           FreeEnvironmentStrings(pEnv);
-       }
-       SetDefaultA(".");
-       bManageDirectory = nSave;
-    }
+        if (osver.dwMajorVersion < 5) {
+            char szBuffer[MAX_PATH*driveCount];
+            if (GetLogicalDriveStringsA(sizeof(szBuffer), szBuffer)) {
+                char* pEnv = (char*)GetEnvironmentStringsA();
+                char* ptr = szBuffer;
+                for (index = 0; index < driveCount; ++index) {
+                    if (driveBits & (1<<index)) {
+                        ptr += SetDirA(ptr, index) + 1;
+                        FromEnvA(pEnv, index);
+                    }
+                }
+                FreeEnvironmentStringsA(pEnv);
+            }
+            SetDefaultA(".");
+        }
+        else { /* Windows 2000 or later */
+            WCHAR szBuffer[MAX_PATH*driveCount];
+            if (GetLogicalDriveStringsW(sizeof(szBuffer), szBuffer)) {
+                WCHAR* pEnv = GetEnvironmentStringsW();
+                WCHAR* ptr = szBuffer;
+                for (index = 0; index < driveCount; ++index) {
+                    if (driveBits & (1<<index)) {
+                        ptr += SetDirW(ptr, index) + 1;
+                        FromEnvW(pEnv, index);
+                    }
+                }
+                FreeEnvironmentStringsW(pEnv);
+            }
+            SetDefaultW(L".");
+        }
+       bManageDirectory = bSave;
+  }
 }
 
 int VDir::SetDirA(char const *pPath, int index)
@@ -205,6 +233,18 @@ void VDir::FromEnvA(char *pEnv, int index)
     }
 }
 
+void VDir::FromEnvW(WCHAR *pEnv, int index)
+{   /* gets the directory for index from the environment variable. */
+    while (*pEnv != '\0') {
+       if ((pEnv[0] == '=') && (DriveIndex((char)pEnv[1]) == index)) {
+           SetDirW(&pEnv[4], index);
+           break;
+       }
+       else
+           pEnv += wcslen(pEnv)+1;
+    }
+}
+
 void VDir::SetDefaultA(char const *pDefault)
 {
     char szBuffer[MAX_PATH+1];
@@ -221,13 +261,13 @@ void VDir::SetDefaultA(char const *pDefault)
 int VDir::SetDirW(WCHAR const *pPath, int index)
 {
     WCHAR chr, *ptr;
-    char szBuffer[MAX_PATH+1];
     int length = 0;
     if (index < driveCount && pPath != NULL) {
        length = wcslen(pPath);
        pMem->Free(dirTableW[index]);
        ptr = dirTableW[index] = (WCHAR*)pMem->Malloc((length+2)*2);
        if (ptr != NULL) {
+            char *ansi;
            wcscpy(ptr, pPath);
            ptr += length-1;
            chr = *ptr++;
@@ -235,13 +275,14 @@ int VDir::SetDirW(WCHAR const *pPath, int index)
                *ptr++ = '\\';
                *ptr = '\0';
            }
-           WideCharToMultiByte(CP_ACP, 0, dirTableW[index], -1, szBuffer, sizeof(szBuffer), NULL, NULL);
-           length = strlen(szBuffer);
+            ansi = win32_ansipath(dirTableW[index]);
+           length = strlen(ansi);
            pMem->Free(dirTableA[index]);
            dirTableA[index] = (char*)pMem->Malloc(length+1);
            if (dirTableA[index] != NULL) {
-               strcpy(dirTableA[index], szBuffer);
+               strcpy(dirTableA[index], ansi);
            }
+            win32_free(ansi);
        }
     }
 
@@ -275,7 +316,7 @@ inline void DoGetFullPathNameA(char* lpBuffer, DWORD dwSize, char* Dest)
 
     /*
      * On WinNT GetFullPathName does not fail, (or at least always
-     * succeeds when the drive is valid) WinNT does set *Dest to Nullch
+     * succeeds when the drive is valid) WinNT does set *Dest to NULL
      * On Win98 GetFullPathName will set last error if it fails, but
      * does not touch *Dest
      */
@@ -366,8 +407,12 @@ char *VDir::MapPathA(const char *pInName)
      */
     char szBuffer[(MAX_PATH+1)*2];
     char szlBuf[MAX_PATH+1];
+    int length = strlen(pInName);
+
+    if (!length)
+       return (char*)pInName;
 
-    if (strlen(pInName) > MAX_PATH) {
+    if (length > MAX_PATH) {
        strncpy(szlBuf, pInName, MAX_PATH);
        if (IsPathSep(pInName[0]) && !IsPathSep(pInName[1])) {   
            /* absolute path - reduce length by 2 for drive specifier */
@@ -383,7 +428,7 @@ char *VDir::MapPathA(const char *pInName)
        /* has drive letter */
        if (IsPathSep(pInName[2])) {
            /* absolute with drive letter */
-           strcpy(szLocalBufferA, pInName);
+           DoGetFullPathNameA((char*)pInName, sizeof(szLocalBufferA), szLocalBufferA);
        }
        else {
            /* relative path with drive letter */
@@ -399,15 +444,14 @@ char *VDir::MapPathA(const char *pInName)
        /* no drive letter */
        if (IsPathSep(pInName[1]) && IsPathSep(pInName[0])) {
            /* UNC name */
-           strcpy(szLocalBufferA, pInName);
+           DoGetFullPathNameA((char*)pInName, sizeof(szLocalBufferA), szLocalBufferA);
        }
        else {
            strcpy(szBuffer, GetDefaultDirA());
            if (IsPathSep(pInName[0])) {
                /* absolute path */
-               szLocalBufferA[0] = szBuffer[0];
-               szLocalBufferA[1] = szBuffer[1];
-               strcpy(&szLocalBufferA[2], pInName);
+               strcpy(&szBuffer[2], pInName);
+               DoGetFullPathNameA(szBuffer, sizeof(szLocalBufferA), szLocalBufferA);
            }
            else {
                /* relative path */
@@ -430,59 +474,60 @@ char *VDir::MapPathA(const char *pInName)
 
 int VDir::SetCurrentDirectoryA(char *lpBuffer)
 {
-    HANDLE hHandle;
-    WIN32_FIND_DATA win32FD;
-    char szBuffer[MAX_PATH+1], *pPtr;
+    char *pPtr;
     int length, nRet = -1;
 
-    GetFullPathNameA(MapPathA(lpBuffer), sizeof(szBuffer), szBuffer, &pPtr);
-    /* if the last char is a '\\' or a '/' then add
-     * an '*' before calling FindFirstFile
-     */
-    length = strlen(szBuffer);
-    if(length > 0 && IsPathSep(szBuffer[length-1])) {
-       szBuffer[length] = '*';
-       szBuffer[length+1] = '\0';
+    pPtr = MapPathA(lpBuffer);
+    length = strlen(pPtr);
+    if(length > 3 && IsPathSep(pPtr[length-1])) {
+       /* don't remove the trailing slash from 'x:\'  */
+       pPtr[length-1] = '\0';
     }
 
-    hHandle = FindFirstFileA(szBuffer, &win32FD);
-    if (hHandle != INVALID_HANDLE_VALUE) {
-        FindClose(hHandle);
-
-       /* if an '*' was added remove it */
-       if(szBuffer[length] == '*')
-           szBuffer[length] = '\0';
-
+    DWORD r = GetFileAttributesA(pPtr);
+    if ((r != 0xffffffff) && (r & FILE_ATTRIBUTE_DIRECTORY))
+    {
+       char szBuffer[(MAX_PATH+1)*2];
+       DoGetFullPathNameA(pPtr, sizeof(szBuffer), szBuffer);
        SetDefaultDirA(szBuffer, DriveIndex(szBuffer[0]));
        nRet = 0;
     }
+
     return nRet;
 }
 
 DWORD VDir::CalculateEnvironmentSpace(void)
-{   /* the current directory environment strings are stored as '=d=d:\path' */
+{   /* the current directory environment strings are stored as '=D:=d:\path' */
     int index;
     DWORD dwSize = 0;
     for (index = 0; index < driveCount; ++index) {
        if (dirTableA[index] != NULL) {
-           dwSize += strlen(dirTableA[index]) + 4;  /* add 1 for trailing NULL and 3 for '=d=' */
+           dwSize += strlen(dirTableA[index]) + 5;  /* add 1 for trailing NULL and 4 for '=D:=' */
        }
     }
     return dwSize;
 }
 
 LPSTR VDir::BuildEnvironmentSpace(LPSTR lpStr)
-{   /* store the current directory environment strings as '=d=d:\path' */
-    int index;
+{   /* store the current directory environment strings as '=D:=d:\path' */
+    int index, length;
     LPSTR lpDirStr;
     for (index = 0; index < driveCount; ++index) {
        lpDirStr = dirTableA[index];
        if (lpDirStr != NULL) {
            lpStr[0] = '=';
            lpStr[1] = lpDirStr[0];
-           lpStr[2] = '=';
-           strcpy(&lpStr[3], lpDirStr);
-           lpStr += strlen(lpDirStr) + 4; /* add 1 for trailing NULL and 3 for '=d=' */
+           lpStr[2] = '\0';
+           CharUpper(&lpStr[1]);
+           lpStr[2] = ':';
+           lpStr[3] = '=';
+           strcpy(&lpStr[4], lpDirStr);
+           length = strlen(lpDirStr);
+           lpStr += length + 5; /* add 1 for trailing NULL and 4 for '=D:=' */
+           if (length > 3 && IsPathSep(lpStr[-2])) {
+               lpStr[-2] = '\0';   /* remove the trailing path separator */
+               --lpStr;
+           }
        }
     }
     return lpStr;
@@ -499,7 +544,7 @@ inline void DoGetFullPathNameW(WCHAR* lpBuffer, DWORD dwSize, WCHAR* Dest)
 
     /*
      * On WinNT GetFullPathName does not fail, (or at least always
-     * succeeds when the drive is valid) WinNT does set *Dest to Nullch
+     * succeeds when the drive is valid) WinNT does set *Dest to NULL
      * On Win98 GetFullPathName will set last error if it fails, but
      * does not touch *Dest
      */
@@ -590,8 +635,12 @@ WCHAR* VDir::MapPathW(const WCHAR *pInName)
      */
     WCHAR szBuffer[(MAX_PATH+1)*2];
     WCHAR szlBuf[MAX_PATH+1];
+    int length = wcslen(pInName);
+
+    if (!length)
+       return (WCHAR*)pInName;
 
-    if (wcslen(pInName) > MAX_PATH) {
+    if (length > MAX_PATH) {
        wcsncpy(szlBuf, pInName, MAX_PATH);
        if (IsPathSep(pInName[0]) && !IsPathSep(pInName[1])) {   
            /* absolute path - reduce length by 2 for drive specifier */
@@ -607,7 +656,7 @@ WCHAR* VDir::MapPathW(const WCHAR *pInName)
        /* has drive letter */
        if (IsPathSep(pInName[2])) {
            /* absolute with drive letter */
-           wcscpy(szLocalBufferW, pInName);
+           DoGetFullPathNameW((WCHAR*)pInName, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
        }
        else {
            /* relative path with drive letter */
@@ -623,15 +672,14 @@ WCHAR* VDir::MapPathW(const WCHAR *pInName)
        /* no drive letter */
        if (IsPathSep(pInName[1]) && IsPathSep(pInName[0])) {
            /* UNC name */
-           wcscpy(szLocalBufferW, pInName);
+           DoGetFullPathNameW((WCHAR*)pInName, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
        }
        else {
            wcscpy(szBuffer, GetDefaultDirW());
            if (IsPathSep(pInName[0])) {
                /* absolute path */
-               szLocalBufferW[0] = szBuffer[0];
-               szLocalBufferW[1] = szBuffer[1];
-               wcscpy(&szLocalBufferW[2], pInName);
+               wcscpy(&szBuffer[2], pInName);
+               DoGetFullPathNameW(szBuffer, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
            }
            else {
                /* relative path */
@@ -653,32 +701,25 @@ WCHAR* VDir::MapPathW(const WCHAR *pInName)
 
 int VDir::SetCurrentDirectoryW(WCHAR *lpBuffer)
 {
-    HANDLE hHandle;
-    WIN32_FIND_DATAW win32FD;
-    WCHAR szBuffer[MAX_PATH+1], *pPtr;
+    WCHAR *pPtr;
     int length, nRet = -1;
 
-    GetFullPathNameW(MapPathW(lpBuffer), (sizeof(szBuffer)/sizeof(WCHAR)), szBuffer, &pPtr);
-    /* if the last char is a '\\' or a '/' then add
-     * an '*' before calling FindFirstFile
-     */
-    length = wcslen(szBuffer);
-    if(length > 0 && IsPathSep(szBuffer[length-1])) {
-       szBuffer[length] = '*';
-       szBuffer[length+1] = '\0';
+    pPtr = MapPathW(lpBuffer);
+    length = wcslen(pPtr);
+    if(length > 3 && IsPathSep(pPtr[length-1])) {
+       /* don't remove the trailing slash from 'x:\'  */
+       pPtr[length-1] = '\0';
     }
 
-    hHandle = FindFirstFileW(szBuffer, &win32FD);
-    if (hHandle != INVALID_HANDLE_VALUE) {
-        FindClose(hHandle);
-
-       /* if an '*' was added remove it */
-       if(szBuffer[length] == '*')
-           szBuffer[length] = '\0';
-
-       SetDefaultDirW(szBuffer, DriveIndex((char)szBuffer[0]));
+    DWORD r = GetFileAttributesW(pPtr);
+    if ((r != 0xffffffff) && (r & FILE_ATTRIBUTE_DIRECTORY))
+    {
+       WCHAR wBuffer[(MAX_PATH+1)*2];
+       DoGetFullPathNameW(pPtr, (sizeof(wBuffer)/sizeof(WCHAR)), wBuffer);
+       SetDefaultDirW(wBuffer, DriveIndex((char)wBuffer[0]));
        nRet = 0;
     }
+
     return nRet;
 }