This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Extend the test-reonly make target on windows (Yves Orton)
[perl5.git] / win32 / vdir.h
index a8a34e4..10119ea 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
 {
 public:
-    VDir();
+    VDir(int bManageDir = 1);
     ~VDir() {};
 
     void Init(VDir* pDir, VMem *pMem);
@@ -25,14 +29,6 @@ public:
     WCHAR* MapPathW(const WCHAR *pInName);
     int SetCurrentDirectoryA(char *lpBuffer);
     int SetCurrentDirectoryW(WCHAR *lpBuffer);
-    inline const char *GetDirA(int index)
-    {
-       return dirTableA[index];
-    };
-    inline const WCHAR *GetDirW(int index)
-    {
-       return dirTableW[index];
-    };
     inline int GetDefault(void) { return nDefault; };
 
     inline char* GetCurrentDirectoryA(int dwBufSize, char *lpBuffer)
@@ -84,14 +80,42 @@ protected:
        SetDirW(pPath, index);
        nDefault = index;
     };
+    inline const char *GetDirA(int index)
+    {
+       char *ptr = dirTableA[index];
+       if (!ptr) {
+           /* simulate the existance of this drive */
+           ptr = szLocalBufferA;
+           ptr[0] = 'A' + index;
+           ptr[1] = ':';
+           ptr[2] = '\\';
+           ptr[3] = 0;
+       }
+       return ptr;
+    };
+    inline const WCHAR *GetDirW(int index)
+    {
+       WCHAR *ptr = dirTableW[index];
+       if (!ptr) {
+           /* simulate the existance of this drive */
+           ptr = szLocalBufferW;
+           ptr[0] = 'A' + index;
+           ptr[1] = ':';
+           ptr[2] = '\\';
+           ptr[3] = 0;
+       }
+       return ptr;
+    };
 
     inline int DriveIndex(char chr)
     {
+       if (chr == '\\' || chr == '/')
+           return ('Z'-'A')+1;
        return (chr | 0x20)-'a';
     };
 
     VMem *pMem;
-    int nDefault;
+    int nDefault, bManageDirectory;
     char *dirTableA[driveCount];
     char szLocalBufferA[MAX_PATH+1];
     WCHAR *dirTableW[driveCount];
@@ -99,9 +123,10 @@ protected:
 };
 
 
-VDir::VDir()
+VDir::VDir(int bManageDir /* = 1 */)
 {
     nDefault = 0;
+    bManageDirectory = bManageDir;
     memset(dirTableA, 0, sizeof(dirTableA));
     memset(dirTableW, 0, sizeof(dirTableW));
 }
@@ -110,6 +135,7 @@ void VDir::Init(VDir* pDir, VMem *p)
 {
     int index;
     DWORD driveBits;
+    int nSave;
     char szBuffer[MAX_PATH*driveCount];
 
     pMem = p;
@@ -120,9 +146,11 @@ void VDir::Init(VDir* pDir, VMem *p)
        nDefault = pDir->GetDefault();
     }
     else {
+       nSave = bManageDirectory;
+       bManageDirectory = 0;
        driveBits = GetLogicalDrives();
        if (GetLogicalDriveStrings(sizeof(szBuffer), szBuffer)) {
-           char* pEnv = GetEnvironmentStrings();
+           char* pEnv = (char*)GetEnvironmentStrings();
            char* ptr = szBuffer;
            for (index = 0; index < driveCount; ++index) {
                if (driveBits & (1<<index)) {
@@ -133,6 +161,7 @@ void VDir::Init(VDir* pDir, VMem *p)
            FreeEnvironmentStrings(pEnv);
        }
        SetDefaultA(".");
+       bManageDirectory = nSave;
     }
 }
 
@@ -163,6 +192,10 @@ int VDir::SetDirA(char const *pPath, int index)
            }
        }
     }
+
+    if(bManageDirectory)
+       ::SetCurrentDirectoryA(pPath);
+
     return length;
 }
 
@@ -217,6 +250,10 @@ int VDir::SetDirW(WCHAR const *pPath, int index)
            }
        }
     }
+
+    if(bManageDirectory)
+       ::SetCurrentDirectoryW(pPath);
+
     return length;
 }
 
@@ -252,6 +289,82 @@ inline void DoGetFullPathNameA(char* lpBuffer, DWORD dwSize, char* Dest)
     GetFullPathNameA(lpBuffer, dwSize, Dest, &pPtr);
 }
 
+inline bool IsSpecialFileName(const char* pName)
+{
+    /* specical file names are devices that the system can open
+     * these include AUX, CON, NUL, PRN, COMx, LPTx, CLOCK$, CONIN$, CONOUT$
+     * (x is a single digit, and names are case-insensitive)
+     */
+    char ch = (pName[0] & ~0x20);
+    switch (ch)
+    {
+       case 'A': /* AUX */
+           if (((pName[1] & ~0x20) == 'U')
+               && ((pName[2] & ~0x20) == 'X')
+               && !pName[3])
+                   return true;
+           break;
+       case 'C': /* CLOCK$, COMx,  CON, CONIN$ CONOUT$ */
+           ch = (pName[1] & ~0x20);
+           switch (ch)
+           {
+               case 'L': /* CLOCK$ */
+                   if (((pName[2] & ~0x20) == 'O')
+                       && ((pName[3] & ~0x20) == 'C')
+                       && ((pName[4] & ~0x20) == 'K')
+                       && (pName[5] == '$')
+                       && !pName[6])
+                           return true;
+                   break;
+               case 'O': /* COMx,  CON, CONIN$ CONOUT$ */
+                   if ((pName[2] & ~0x20) == 'M') {
+                       if ((pName[3] >= '1') && (pName[3] <= '9')
+                           && !pName[4])
+                           return true;
+                   }
+                   else if ((pName[2] & ~0x20) == 'N') {
+                       if (!pName[3])
+                           return true;
+                       else if ((pName[3] & ~0x20) == 'I') {
+                           if (((pName[4] & ~0x20) == 'N')
+                               && (pName[5] == '$')
+                               && !pName[6])
+                           return true;
+                       }
+                       else if ((pName[3] & ~0x20) == 'O') {
+                           if (((pName[4] & ~0x20) == 'U')
+                               && ((pName[5] & ~0x20) == 'T')
+                               && (pName[6] == '$')
+                               && !pName[7])
+                           return true;
+                       }
+                   }
+                   break;
+           }
+           break;
+       case 'L': /* LPTx */
+           if (((pName[1] & ~0x20) == 'U')
+               && ((pName[2] & ~0x20) == 'X')
+               && (pName[3] >= '1') && (pName[3] <= '9')
+               && !pName[4])
+                   return true;
+           break;
+       case 'N': /* NUL */
+           if (((pName[1] & ~0x20) == 'U')
+               && ((pName[2] & ~0x20) == 'L')
+               && !pName[3])
+                   return true;
+           break;
+       case 'P': /* PRN */
+           if (((pName[1] & ~0x20) == 'R')
+               && ((pName[2] & ~0x20) == 'N')
+               && !pName[3])
+                   return true;
+           break;
+    }
+    return false;
+}
+
 char *VDir::MapPathA(const char *pInName)
 {   /*
      * possiblities -- relative path or absolute path with or without drive letter
@@ -259,8 +372,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 */
@@ -276,7 +393,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 */
@@ -292,23 +409,27 @@ 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 */
-               strcat(szBuffer, pInName);
-               if (strlen(szBuffer) > MAX_PATH)
-                   szBuffer[MAX_PATH] = '\0';
+               if (IsSpecialFileName(pInName)) {
+                   return (char*)pInName;
+               }
+               else {
+                   strcat(szBuffer, pInName);
+                   if (strlen(szBuffer) > MAX_PATH)
+                       szBuffer[MAX_PATH] = '\0';
 
-               DoGetFullPathNameA(szBuffer, sizeof(szLocalBufferA), szLocalBufferA);
+                   DoGetFullPathNameA(szBuffer, sizeof(szLocalBufferA), szLocalBufferA);
+               }
            }
        }
     }
@@ -318,59 +439,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;
@@ -395,6 +517,82 @@ inline void DoGetFullPathNameW(WCHAR* lpBuffer, DWORD dwSize, WCHAR* Dest)
     GetFullPathNameW(lpBuffer, dwSize, Dest, &pPtr);
 }
 
+inline bool IsSpecialFileName(const WCHAR* pName)
+{
+    /* specical file names are devices that the system can open
+     * these include AUX, CON, NUL, PRN, COMx, LPTx, CLOCK$, CONIN$, CONOUT$
+     * (x is a single digit, and names are case-insensitive)
+     */
+    WCHAR ch = (pName[0] & ~0x20);
+    switch (ch)
+    {
+       case 'A': /* AUX */
+           if (((pName[1] & ~0x20) == 'U')
+               && ((pName[2] & ~0x20) == 'X')
+               && !pName[3])
+                   return true;
+           break;
+       case 'C': /* CLOCK$, COMx,  CON, CONIN$ CONOUT$ */
+           ch = (pName[1] & ~0x20);
+           switch (ch)
+           {
+               case 'L': /* CLOCK$ */
+                   if (((pName[2] & ~0x20) == 'O')
+                       && ((pName[3] & ~0x20) == 'C')
+                       && ((pName[4] & ~0x20) == 'K')
+                       && (pName[5] == '$')
+                       && !pName[6])
+                           return true;
+                   break;
+               case 'O': /* COMx,  CON, CONIN$ CONOUT$ */
+                   if ((pName[2] & ~0x20) == 'M') {
+                       if ((pName[3] >= '1') && (pName[3] <= '9')
+                           && !pName[4])
+                           return true;
+                   }
+                   else if ((pName[2] & ~0x20) == 'N') {
+                       if (!pName[3])
+                           return true;
+                       else if ((pName[3] & ~0x20) == 'I') {
+                           if (((pName[4] & ~0x20) == 'N')
+                               && (pName[5] == '$')
+                               && !pName[6])
+                           return true;
+                       }
+                       else if ((pName[3] & ~0x20) == 'O') {
+                           if (((pName[4] & ~0x20) == 'U')
+                               && ((pName[5] & ~0x20) == 'T')
+                               && (pName[6] == '$')
+                               && !pName[7])
+                           return true;
+                       }
+                   }
+                   break;
+           }
+           break;
+       case 'L': /* LPTx */
+           if (((pName[1] & ~0x20) == 'U')
+               && ((pName[2] & ~0x20) == 'X')
+               && (pName[3] >= '1') && (pName[3] <= '9')
+               && !pName[4])
+                   return true;
+           break;
+       case 'N': /* NUL */
+           if (((pName[1] & ~0x20) == 'U')
+               && ((pName[2] & ~0x20) == 'L')
+               && !pName[3])
+                   return true;
+           break;
+       case 'P': /* PRN */
+           if (((pName[1] & ~0x20) == 'R')
+               && ((pName[2] & ~0x20) == 'N')
+               && !pName[3])
+                   return true;
+           break;
+    }
+    return false;
+}
+
 WCHAR* VDir::MapPathW(const WCHAR *pInName)
 {   /*
      * possiblities -- relative path or absolute path with or without drive letter
@@ -402,8 +600,12 @@ WCHAR* VDir::MapPathW(const WCHAR *pInName)
      */
     WCHAR szBuffer[(MAX_PATH+1)*2];
     WCHAR szlBuf[MAX_PATH+1];
+    int length = wcslen(pInName);
 
-    if (wcslen(pInName) > MAX_PATH) {
+    if (!length)
+       return (WCHAR*)pInName;
+
+    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 */
@@ -419,7 +621,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 */
@@ -435,23 +637,27 @@ 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 */
-               wcscat(szBuffer, pInName);
-               if (wcslen(szBuffer) > MAX_PATH)
-                   szBuffer[MAX_PATH] = '\0';
+               if (IsSpecialFileName(pInName)) {
+                   return (WCHAR*)pInName;
+               }
+               else {
+                   wcscat(szBuffer, pInName);
+                   if (wcslen(szBuffer) > MAX_PATH)
+                       szBuffer[MAX_PATH] = '\0';
 
-               DoGetFullPathNameW(szBuffer, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
+                   DoGetFullPathNameW(szBuffer, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
+               }
            }
        }
     }
@@ -460,32 +666,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;
 }