Integrate with Sarathy.
[p5sagit/p5-mst-13.2.git] / win32 / vdir.h
1 /* vdir.h
2  *
3  * (c) 1999 Microsoft Corporation. All rights reserved. 
4  * Portions (c) 1999 ActiveState Tool Corp, http://www.ActiveState.com/
5  *
6  *    You may distribute under the terms of either the GNU General Public
7  *    License or the Artistic License, as specified in the README file.
8  */
9
10 #ifndef ___VDir_H___
11 #define ___VDir_H___
12
13 const int driveCount = 30;
14
15 class VDir
16 {
17 public:
18     VDir(int bManageDir = 1);
19     ~VDir() {};
20
21     void Init(VDir* pDir, VMem *pMem);
22     void SetDefaultA(char const *pDefault);
23     void SetDefaultW(WCHAR const *pDefault);
24     char* MapPathA(const char *pInName);
25     WCHAR* MapPathW(const WCHAR *pInName);
26     int SetCurrentDirectoryA(char *lpBuffer);
27     int SetCurrentDirectoryW(WCHAR *lpBuffer);
28     inline int GetDefault(void) { return nDefault; };
29
30     inline char* GetCurrentDirectoryA(int dwBufSize, char *lpBuffer)
31     {
32         char* ptr = dirTableA[nDefault];
33         while (dwBufSize--)
34         {
35             if ((*lpBuffer++ = *ptr++) == '\0')
36                 break;
37         }
38         return lpBuffer;
39     };
40     inline WCHAR* GetCurrentDirectoryW(int dwBufSize, WCHAR *lpBuffer)
41     {
42         WCHAR* ptr = dirTableW[nDefault];
43         while (dwBufSize--)
44         {
45             if ((*lpBuffer++ = *ptr++) == '\0')
46                 break;
47         }
48         return lpBuffer;
49     };
50
51
52     DWORD CalculateEnvironmentSpace(void);
53     LPSTR BuildEnvironmentSpace(LPSTR lpStr);
54
55 protected:
56     int SetDirA(char const *pPath, int index);
57     void FromEnvA(char *pEnv, int index);
58     inline const char *GetDefaultDirA(void)
59     {
60         return dirTableA[nDefault];
61     };
62
63     inline void SetDefaultDirA(char const *pPath, int index)
64     {
65         SetDirA(pPath, index);
66         nDefault = index;
67     };
68     int SetDirW(WCHAR const *pPath, int index);
69     inline const WCHAR *GetDefaultDirW(void)
70     {
71         return dirTableW[nDefault];
72     };
73
74     inline void SetDefaultDirW(WCHAR const *pPath, int index)
75     {
76         SetDirW(pPath, index);
77         nDefault = index;
78     };
79     inline const char *GetDirA(int index)
80     {
81         char *ptr = dirTableA[index];
82         if (!ptr) {
83             /* simulate the existance of this drive */
84             ptr = szLocalBufferA;
85             ptr[0] = 'A' + index;
86             ptr[1] = ':';
87             ptr[2] = '\\';
88             ptr[3] = 0;
89         }
90         return ptr;
91     };
92     inline const WCHAR *GetDirW(int index)
93     {
94         WCHAR *ptr = dirTableW[index];
95         if (!ptr) {
96             /* simulate the existance of this drive */
97             ptr = szLocalBufferW;
98             ptr[0] = 'A' + index;
99             ptr[1] = ':';
100             ptr[2] = '\\';
101             ptr[3] = 0;
102         }
103         return ptr;
104     };
105
106     inline int DriveIndex(char chr)
107     {
108         return (chr | 0x20)-'a';
109     };
110
111     VMem *pMem;
112     int nDefault, bManageDirectory;
113     char *dirTableA[driveCount];
114     char szLocalBufferA[MAX_PATH+1];
115     WCHAR *dirTableW[driveCount];
116     WCHAR szLocalBufferW[MAX_PATH+1];
117 };
118
119
120 VDir::VDir(int bManageDir /* = 1 */)
121 {
122     nDefault = 0;
123     bManageDirectory = bManageDir;
124     memset(dirTableA, 0, sizeof(dirTableA));
125     memset(dirTableW, 0, sizeof(dirTableW));
126 }
127
128 void VDir::Init(VDir* pDir, VMem *p)
129 {
130     int index;
131     DWORD driveBits;
132     int nSave;
133     char szBuffer[MAX_PATH*driveCount];
134
135     pMem = p;
136     if (pDir) {
137         for (index = 0; index < driveCount; ++index) {
138             SetDirW(pDir->GetDirW(index), index);
139         }
140         nDefault = pDir->GetDefault();
141     }
142     else {
143         nSave = bManageDirectory;
144         bManageDirectory = 0;
145         driveBits = GetLogicalDrives();
146         if (GetLogicalDriveStrings(sizeof(szBuffer), szBuffer)) {
147             char* pEnv = GetEnvironmentStrings();
148             char* ptr = szBuffer;
149             for (index = 0; index < driveCount; ++index) {
150                 if (driveBits & (1<<index)) {
151                     ptr += SetDirA(ptr, index) + 1;
152                     FromEnvA(pEnv, index);
153                 }
154             }
155             FreeEnvironmentStrings(pEnv);
156         }
157         SetDefaultA(".");
158         bManageDirectory = nSave;
159     }
160 }
161
162 int VDir::SetDirA(char const *pPath, int index)
163 {
164     char chr, *ptr;
165     int length = 0;
166     WCHAR wBuffer[MAX_PATH+1];
167     if (index < driveCount && pPath != NULL) {
168         length = strlen(pPath);
169         pMem->Free(dirTableA[index]);
170         ptr = dirTableA[index] = (char*)pMem->Malloc(length+2);
171         if (ptr != NULL) {
172             strcpy(ptr, pPath);
173             ptr += length-1;
174             chr = *ptr++;
175             if (chr != '\\' && chr != '/') {
176                 *ptr++ = '\\';
177                 *ptr = '\0';
178             }
179             MultiByteToWideChar(CP_ACP, 0, dirTableA[index], -1,
180                     wBuffer, (sizeof(wBuffer)/sizeof(WCHAR)));
181             length = wcslen(wBuffer);
182             pMem->Free(dirTableW[index]);
183             dirTableW[index] = (WCHAR*)pMem->Malloc((length+1)*2);
184             if (dirTableW[index] != NULL) {
185                 wcscpy(dirTableW[index], wBuffer);
186             }
187         }
188     }
189
190     if(bManageDirectory)
191         ::SetCurrentDirectoryA(pPath);
192
193     return length;
194 }
195
196 void VDir::FromEnvA(char *pEnv, int index)
197 {   /* gets the directory for index from the environment variable. */
198     while (*pEnv != '\0') {
199         if ((pEnv[0] == '=') && (DriveIndex(pEnv[1]) == index)) {
200             SetDirA(&pEnv[4], index);
201             break;
202         }
203         else
204             pEnv += strlen(pEnv)+1;
205     }
206 }
207
208 void VDir::SetDefaultA(char const *pDefault)
209 {
210     char szBuffer[MAX_PATH+1];
211     char *pPtr;
212
213     if (GetFullPathNameA(pDefault, sizeof(szBuffer), szBuffer, &pPtr)) {
214         if (*pDefault != '.' && pPtr != NULL)
215             *pPtr = '\0';
216
217         SetDefaultDirA(szBuffer, DriveIndex(szBuffer[0]));
218     }
219 }
220
221 int VDir::SetDirW(WCHAR const *pPath, int index)
222 {
223     WCHAR chr, *ptr;
224     char szBuffer[MAX_PATH+1];
225     int length = 0;
226     if (index < driveCount && pPath != NULL) {
227         length = wcslen(pPath);
228         pMem->Free(dirTableW[index]);
229         ptr = dirTableW[index] = (WCHAR*)pMem->Malloc((length+2)*2);
230         if (ptr != NULL) {
231             wcscpy(ptr, pPath);
232             ptr += length-1;
233             chr = *ptr++;
234             if (chr != '\\' && chr != '/') {
235                 *ptr++ = '\\';
236                 *ptr = '\0';
237             }
238             WideCharToMultiByte(CP_ACP, 0, dirTableW[index], -1, szBuffer, sizeof(szBuffer), NULL, NULL);
239             length = strlen(szBuffer);
240             pMem->Free(dirTableA[index]);
241             dirTableA[index] = (char*)pMem->Malloc(length+1);
242             if (dirTableA[index] != NULL) {
243                 strcpy(dirTableA[index], szBuffer);
244             }
245         }
246     }
247
248     if(bManageDirectory)
249         ::SetCurrentDirectoryW(pPath);
250
251     return length;
252 }
253
254 void VDir::SetDefaultW(WCHAR const *pDefault)
255 {
256     WCHAR szBuffer[MAX_PATH+1];
257     WCHAR *pPtr;
258
259     if (GetFullPathNameW(pDefault, (sizeof(szBuffer)/sizeof(WCHAR)), szBuffer, &pPtr)) {
260         if (*pDefault != '.' && pPtr != NULL)
261             *pPtr = '\0';
262
263         SetDefaultDirW(szBuffer, DriveIndex((char)szBuffer[0]));
264     }
265 }
266
267 inline BOOL IsPathSep(char ch)
268 {
269     return (ch == '\\' || ch == '/');
270 }
271
272 inline void DoGetFullPathNameA(char* lpBuffer, DWORD dwSize, char* Dest)
273 {
274     char *pPtr;
275
276     /*
277      * On WinNT GetFullPathName does not fail, (or at least always
278      * succeeds when the drive is valid) WinNT does set *Dest to Nullch
279      * On Win98 GetFullPathName will set last error if it fails, but
280      * does not touch *Dest
281      */
282     *Dest = '\0';
283     GetFullPathNameA(lpBuffer, dwSize, Dest, &pPtr);
284 }
285
286 inline bool IsSpecialFileName(const char* pName)
287 {
288     /* specical file names are devices that the system can open
289      * these include AUX, CON, NUL, PRN, COMx, LPTx, CLOCK$, CONIN$, CONOUT$
290      * (x is a single digit, and names are case-insensitive)
291      */
292     char ch = (pName[0] & ~0x20);
293     switch (ch)
294     {
295         case 'A': /* AUX */
296             if (((pName[1] & ~0x20) == 'U')
297                 && ((pName[2] & ~0x20) == 'X')
298                 && !pName[3])
299                     return true;
300             break;
301         case 'C': /* CLOCK$, COMx,  CON, CONIN$ CONOUT$ */
302             ch = (pName[1] & ~0x20);
303             switch (ch)
304             {
305                 case 'L': /* CLOCK$ */
306                     if (((pName[2] & ~0x20) == 'O')
307                         && ((pName[3] & ~0x20) == 'C')
308                         && ((pName[4] & ~0x20) == 'K')
309                         && (pName[5] == '$')
310                         && !pName[6])
311                             return true;
312                     break;
313                 case 'O': /* COMx,  CON, CONIN$ CONOUT$ */
314                     if ((pName[2] & ~0x20) == 'M') {
315                         if ((pName[3] >= '1') && (pName[3] <= '9')
316                             && !pName[4])
317                             return true;
318                     }
319                     else if ((pName[2] & ~0x20) == 'N') {
320                         if (!pName[3])
321                             return true;
322                         else if ((pName[3] & ~0x20) == 'I') {
323                             if (((pName[4] & ~0x20) == 'N')
324                                 && (pName[5] == '$')
325                                 && !pName[6])
326                             return true;
327                         }
328                         else if ((pName[3] & ~0x20) == 'O') {
329                             if (((pName[4] & ~0x20) == 'U')
330                                 && ((pName[5] & ~0x20) == 'T')
331                                 && (pName[6] == '$')
332                                 && !pName[7])
333                             return true;
334                         }
335                     }
336                     break;
337             }
338             break;
339         case 'L': /* LPTx */
340             if (((pName[1] & ~0x20) == 'U')
341                 && ((pName[2] & ~0x20) == 'X')
342                 && (pName[3] >= '1') && (pName[3] <= '9')
343                 && !pName[4])
344                     return true;
345             break;
346         case 'N': /* NUL */
347             if (((pName[1] & ~0x20) == 'U')
348                 && ((pName[2] & ~0x20) == 'L')
349                 && !pName[3])
350                     return true;
351             break;
352         case 'P': /* PRN */
353             if (((pName[1] & ~0x20) == 'R')
354                 && ((pName[2] & ~0x20) == 'N')
355                 && !pName[3])
356                     return true;
357             break;
358     }
359     return false;
360 }
361
362 char *VDir::MapPathA(const char *pInName)
363 {   /*
364      * possiblities -- relative path or absolute path with or without drive letter
365      * OR UNC name
366      */
367     char szBuffer[(MAX_PATH+1)*2];
368     char szlBuf[MAX_PATH+1];
369
370     if (strlen(pInName) > MAX_PATH) {
371         strncpy(szlBuf, pInName, MAX_PATH);
372         if (IsPathSep(pInName[0]) && !IsPathSep(pInName[1])) {   
373             /* absolute path - reduce length by 2 for drive specifier */
374             szlBuf[MAX_PATH-2] = '\0';
375         }
376         else
377             szlBuf[MAX_PATH] = '\0';
378         pInName = szlBuf;
379     }
380     /* strlen(pInName) is now <= MAX_PATH */
381
382     if (pInName[1] == ':') {
383         /* has drive letter */
384         if (IsPathSep(pInName[2])) {
385             /* absolute with drive letter */
386             strcpy(szLocalBufferA, pInName);
387         }
388         else {
389             /* relative path with drive letter */
390             strcpy(szBuffer, GetDirA(DriveIndex(*pInName)));
391             strcat(szBuffer, &pInName[2]);
392             if(strlen(szBuffer) > MAX_PATH)
393                 szBuffer[MAX_PATH] = '\0';
394
395             DoGetFullPathNameA(szBuffer, sizeof(szLocalBufferA), szLocalBufferA);
396         }
397     }
398     else {
399         /* no drive letter */
400         if (IsPathSep(pInName[1]) && IsPathSep(pInName[0])) {
401             /* UNC name */
402             strcpy(szLocalBufferA, pInName);
403         }
404         else {
405             strcpy(szBuffer, GetDefaultDirA());
406             if (IsPathSep(pInName[0])) {
407                 /* absolute path */
408                 szLocalBufferA[0] = szBuffer[0];
409                 szLocalBufferA[1] = szBuffer[1];
410                 strcpy(&szLocalBufferA[2], pInName);
411             }
412             else {
413                 /* relative path */
414                 if (IsSpecialFileName(pInName)) {
415                     return (char*)pInName;
416                 }
417                 else {
418                     strcat(szBuffer, pInName);
419                     if (strlen(szBuffer) > MAX_PATH)
420                         szBuffer[MAX_PATH] = '\0';
421
422                     DoGetFullPathNameA(szBuffer, sizeof(szLocalBufferA), szLocalBufferA);
423                 }
424             }
425         }
426     }
427
428     return szLocalBufferA;
429 }
430
431 int VDir::SetCurrentDirectoryA(char *lpBuffer)
432 {
433     HANDLE hHandle;
434     WIN32_FIND_DATA win32FD;
435     char szBuffer[MAX_PATH+1], *pPtr;
436     int length, nRet = -1;
437
438     GetFullPathNameA(MapPathA(lpBuffer), sizeof(szBuffer), szBuffer, &pPtr);
439     /* if the last char is a '\\' or a '/' then add
440      * an '*' before calling FindFirstFile
441      */
442     length = strlen(szBuffer);
443     if(length > 0 && IsPathSep(szBuffer[length-1])) {
444         szBuffer[length] = '*';
445         szBuffer[length+1] = '\0';
446     }
447
448     hHandle = FindFirstFileA(szBuffer, &win32FD);
449     if (hHandle != INVALID_HANDLE_VALUE) {
450         FindClose(hHandle);
451
452         /* if an '*' was added remove it */
453         if(szBuffer[length] == '*')
454             szBuffer[length] = '\0';
455
456         SetDefaultDirA(szBuffer, DriveIndex(szBuffer[0]));
457         nRet = 0;
458     }
459     return nRet;
460 }
461
462 DWORD VDir::CalculateEnvironmentSpace(void)
463 {   /* the current directory environment strings are stored as '=d=d:\path' */
464     int index;
465     DWORD dwSize = 0;
466     for (index = 0; index < driveCount; ++index) {
467         if (dirTableA[index] != NULL) {
468             dwSize += strlen(dirTableA[index]) + 4;  /* add 1 for trailing NULL and 3 for '=d=' */
469         }
470     }
471     return dwSize;
472 }
473
474 LPSTR VDir::BuildEnvironmentSpace(LPSTR lpStr)
475 {   /* store the current directory environment strings as '=d=d:\path' */
476     int index;
477     LPSTR lpDirStr;
478     for (index = 0; index < driveCount; ++index) {
479         lpDirStr = dirTableA[index];
480         if (lpDirStr != NULL) {
481             lpStr[0] = '=';
482             lpStr[1] = lpDirStr[0];
483             lpStr[2] = '=';
484             strcpy(&lpStr[3], lpDirStr);
485             lpStr += strlen(lpDirStr) + 4; /* add 1 for trailing NULL and 3 for '=d=' */
486         }
487     }
488     return lpStr;
489 }
490
491 inline BOOL IsPathSep(WCHAR ch)
492 {
493     return (ch == '\\' || ch == '/');
494 }
495
496 inline void DoGetFullPathNameW(WCHAR* lpBuffer, DWORD dwSize, WCHAR* Dest)
497 {
498     WCHAR *pPtr;
499
500     /*
501      * On WinNT GetFullPathName does not fail, (or at least always
502      * succeeds when the drive is valid) WinNT does set *Dest to Nullch
503      * On Win98 GetFullPathName will set last error if it fails, but
504      * does not touch *Dest
505      */
506     *Dest = '\0';
507     GetFullPathNameW(lpBuffer, dwSize, Dest, &pPtr);
508 }
509
510 inline bool IsSpecialFileName(const WCHAR* pName)
511 {
512     /* specical file names are devices that the system can open
513      * these include AUX, CON, NUL, PRN, COMx, LPTx, CLOCK$, CONIN$, CONOUT$
514      * (x is a single digit, and names are case-insensitive)
515      */
516     WCHAR ch = (pName[0] & ~0x20);
517     switch (ch)
518     {
519         case 'A': /* AUX */
520             if (((pName[1] & ~0x20) == 'U')
521                 && ((pName[2] & ~0x20) == 'X')
522                 && !pName[3])
523                     return true;
524             break;
525         case 'C': /* CLOCK$, COMx,  CON, CONIN$ CONOUT$ */
526             ch = (pName[1] & ~0x20);
527             switch (ch)
528             {
529                 case 'L': /* CLOCK$ */
530                     if (((pName[2] & ~0x20) == 'O')
531                         && ((pName[3] & ~0x20) == 'C')
532                         && ((pName[4] & ~0x20) == 'K')
533                         && (pName[5] == '$')
534                         && !pName[6])
535                             return true;
536                     break;
537                 case 'O': /* COMx,  CON, CONIN$ CONOUT$ */
538                     if ((pName[2] & ~0x20) == 'M') {
539                         if ((pName[3] >= '1') && (pName[3] <= '9')
540                             && !pName[4])
541                             return true;
542                     }
543                     else if ((pName[2] & ~0x20) == 'N') {
544                         if (!pName[3])
545                             return true;
546                         else if ((pName[3] & ~0x20) == 'I') {
547                             if (((pName[4] & ~0x20) == 'N')
548                                 && (pName[5] == '$')
549                                 && !pName[6])
550                             return true;
551                         }
552                         else if ((pName[3] & ~0x20) == 'O') {
553                             if (((pName[4] & ~0x20) == 'U')
554                                 && ((pName[5] & ~0x20) == 'T')
555                                 && (pName[6] == '$')
556                                 && !pName[7])
557                             return true;
558                         }
559                     }
560                     break;
561             }
562             break;
563         case 'L': /* LPTx */
564             if (((pName[1] & ~0x20) == 'U')
565                 && ((pName[2] & ~0x20) == 'X')
566                 && (pName[3] >= '1') && (pName[3] <= '9')
567                 && !pName[4])
568                     return true;
569             break;
570         case 'N': /* NUL */
571             if (((pName[1] & ~0x20) == 'U')
572                 && ((pName[2] & ~0x20) == 'L')
573                 && !pName[3])
574                     return true;
575             break;
576         case 'P': /* PRN */
577             if (((pName[1] & ~0x20) == 'R')
578                 && ((pName[2] & ~0x20) == 'N')
579                 && !pName[3])
580                     return true;
581             break;
582     }
583     return false;
584 }
585
586 WCHAR* VDir::MapPathW(const WCHAR *pInName)
587 {   /*
588      * possiblities -- relative path or absolute path with or without drive letter
589      * OR UNC name
590      */
591     WCHAR szBuffer[(MAX_PATH+1)*2];
592     WCHAR szlBuf[MAX_PATH+1];
593
594     if (wcslen(pInName) > MAX_PATH) {
595         wcsncpy(szlBuf, pInName, MAX_PATH);
596         if (IsPathSep(pInName[0]) && !IsPathSep(pInName[1])) {   
597             /* absolute path - reduce length by 2 for drive specifier */
598             szlBuf[MAX_PATH-2] = '\0';
599         }
600         else
601             szlBuf[MAX_PATH] = '\0';
602         pInName = szlBuf;
603     }
604     /* strlen(pInName) is now <= MAX_PATH */
605
606     if (pInName[1] == ':') {
607         /* has drive letter */
608         if (IsPathSep(pInName[2])) {
609             /* absolute with drive letter */
610             wcscpy(szLocalBufferW, pInName);
611         }
612         else {
613             /* relative path with drive letter */
614             wcscpy(szBuffer, GetDirW(DriveIndex((char)*pInName)));
615             wcscat(szBuffer, &pInName[2]);
616             if(wcslen(szBuffer) > MAX_PATH)
617                 szBuffer[MAX_PATH] = '\0';
618
619             DoGetFullPathNameW(szBuffer, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
620         }
621     }
622     else {
623         /* no drive letter */
624         if (IsPathSep(pInName[1]) && IsPathSep(pInName[0])) {
625             /* UNC name */
626             wcscpy(szLocalBufferW, pInName);
627         }
628         else {
629             wcscpy(szBuffer, GetDefaultDirW());
630             if (IsPathSep(pInName[0])) {
631                 /* absolute path */
632                 szLocalBufferW[0] = szBuffer[0];
633                 szLocalBufferW[1] = szBuffer[1];
634                 wcscpy(&szLocalBufferW[2], pInName);
635             }
636             else {
637                 /* relative path */
638                 if (IsSpecialFileName(pInName)) {
639                     return (WCHAR*)pInName;
640                 }
641                 else {
642                     wcscat(szBuffer, pInName);
643                     if (wcslen(szBuffer) > MAX_PATH)
644                         szBuffer[MAX_PATH] = '\0';
645
646                     DoGetFullPathNameW(szBuffer, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
647                 }
648             }
649         }
650     }
651     return szLocalBufferW;
652 }
653
654 int VDir::SetCurrentDirectoryW(WCHAR *lpBuffer)
655 {
656     HANDLE hHandle;
657     WIN32_FIND_DATAW win32FD;
658     WCHAR szBuffer[MAX_PATH+1], *pPtr;
659     int length, nRet = -1;
660
661     GetFullPathNameW(MapPathW(lpBuffer), (sizeof(szBuffer)/sizeof(WCHAR)), szBuffer, &pPtr);
662     /* if the last char is a '\\' or a '/' then add
663      * an '*' before calling FindFirstFile
664      */
665     length = wcslen(szBuffer);
666     if(length > 0 && IsPathSep(szBuffer[length-1])) {
667         szBuffer[length] = '*';
668         szBuffer[length+1] = '\0';
669     }
670
671     hHandle = FindFirstFileW(szBuffer, &win32FD);
672     if (hHandle != INVALID_HANDLE_VALUE) {
673         FindClose(hHandle);
674
675         /* if an '*' was added remove it */
676         if(szBuffer[length] == '*')
677             szBuffer[length] = '\0';
678
679         SetDefaultDirW(szBuffer, DriveIndex((char)szBuffer[0]));
680         nRet = 0;
681     }
682     return nRet;
683 }
684
685 #endif  /* ___VDir_H___ */