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