add a small buffer to gv_stash_name
[perl.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             && pEnv[2] == ':' && pEnv[3] == '=') {
208             SetDirA(&pEnv[4], index);
209             break;
210         }
211         else
212             pEnv += strlen(pEnv)+1;
213     }
214 }
215
216 void VDir::FromEnvW(WCHAR *pEnv, int index)
217 {   /* gets the directory for index from the environment variable. */
218     while (*pEnv != '\0') {
219         if ((pEnv[0] == '=') && (DriveIndex((char)pEnv[1]) == index)
220             && pEnv[2] == ':' && pEnv[3] == '=') {
221             SetDirW(&pEnv[4], index);
222             break;
223         }
224         else
225             pEnv += wcslen(pEnv)+1;
226     }
227 }
228
229 void VDir::SetDefaultA(char const *pDefault)
230 {
231     char szBuffer[MAX_PATH+1];
232     char *pPtr;
233
234     if (GetFullPathNameA(pDefault, sizeof(szBuffer), szBuffer, &pPtr)) {
235         if (*pDefault != '.' && pPtr != NULL)
236             *pPtr = '\0';
237
238         SetDefaultDirA(szBuffer, DriveIndex(szBuffer[0]));
239     }
240 }
241
242 int VDir::SetDirW(WCHAR const *pPath, int index)
243 {
244     WCHAR chr, *ptr;
245     int length = 0;
246     if (index < driveCount && pPath != NULL) {
247         length = wcslen(pPath);
248         pMem->Free(dirTableW[index]);
249         ptr = dirTableW[index] = (WCHAR*)pMem->Malloc((length+2)*2);
250         if (ptr != NULL) {
251             char *ansi;
252             wcscpy(ptr, pPath);
253             ptr += length-1;
254             chr = *ptr++;
255             if (chr != '\\' && chr != '/') {
256                 *ptr++ = '\\';
257                 *ptr = '\0';
258             }
259             ansi = win32_ansipath(dirTableW[index]);
260             length = strlen(ansi);
261             pMem->Free(dirTableA[index]);
262             dirTableA[index] = (char*)pMem->Malloc(length+1);
263             if (dirTableA[index] != NULL) {
264                 strcpy(dirTableA[index], ansi);
265             }
266             win32_free(ansi);
267         }
268     }
269
270     if(bManageDirectory)
271         ::SetCurrentDirectoryW(pPath);
272
273     return length;
274 }
275
276 void VDir::SetDefaultW(WCHAR const *pDefault)
277 {
278     WCHAR szBuffer[MAX_PATH+1];
279     WCHAR *pPtr;
280
281     if (GetFullPathNameW(pDefault, (sizeof(szBuffer)/sizeof(WCHAR)), szBuffer, &pPtr)) {
282         if (*pDefault != '.' && pPtr != NULL)
283             *pPtr = '\0';
284
285         SetDefaultDirW(szBuffer, DriveIndex((char)szBuffer[0]));
286     }
287 }
288
289 inline BOOL IsPathSep(char ch)
290 {
291     return (ch == '\\' || ch == '/');
292 }
293
294 inline void DoGetFullPathNameA(char* lpBuffer, DWORD dwSize, char* Dest)
295 {
296     char *pPtr;
297
298     /*
299      * On WinNT GetFullPathName does not fail, (or at least always
300      * succeeds when the drive is valid) WinNT does set *Dest to NULL
301      * On Win98 GetFullPathName will set last error if it fails, but
302      * does not touch *Dest
303      */
304     *Dest = '\0';
305     GetFullPathNameA(lpBuffer, dwSize, Dest, &pPtr);
306 }
307
308 inline bool IsSpecialFileName(const char* pName)
309 {
310     /* specical file names are devices that the system can open
311      * these include AUX, CON, NUL, PRN, COMx, LPTx, CLOCK$, CONIN$, CONOUT$
312      * (x is a single digit, and names are case-insensitive)
313      */
314     char ch = (pName[0] & ~0x20);
315     switch (ch)
316     {
317         case 'A': /* AUX */
318             if (((pName[1] & ~0x20) == 'U')
319                 && ((pName[2] & ~0x20) == 'X')
320                 && !pName[3])
321                     return true;
322             break;
323         case 'C': /* CLOCK$, COMx,  CON, CONIN$ CONOUT$ */
324             ch = (pName[1] & ~0x20);
325             switch (ch)
326             {
327                 case 'L': /* CLOCK$ */
328                     if (((pName[2] & ~0x20) == 'O')
329                         && ((pName[3] & ~0x20) == 'C')
330                         && ((pName[4] & ~0x20) == 'K')
331                         && (pName[5] == '$')
332                         && !pName[6])
333                             return true;
334                     break;
335                 case 'O': /* COMx,  CON, CONIN$ CONOUT$ */
336                     if ((pName[2] & ~0x20) == 'M') {
337                         if ((pName[3] >= '1') && (pName[3] <= '9')
338                             && !pName[4])
339                             return true;
340                     }
341                     else if ((pName[2] & ~0x20) == 'N') {
342                         if (!pName[3])
343                             return true;
344                         else if ((pName[3] & ~0x20) == 'I') {
345                             if (((pName[4] & ~0x20) == 'N')
346                                 && (pName[5] == '$')
347                                 && !pName[6])
348                             return true;
349                         }
350                         else if ((pName[3] & ~0x20) == 'O') {
351                             if (((pName[4] & ~0x20) == 'U')
352                                 && ((pName[5] & ~0x20) == 'T')
353                                 && (pName[6] == '$')
354                                 && !pName[7])
355                             return true;
356                         }
357                     }
358                     break;
359             }
360             break;
361         case 'L': /* LPTx */
362             if (((pName[1] & ~0x20) == 'U')
363                 && ((pName[2] & ~0x20) == 'X')
364                 && (pName[3] >= '1') && (pName[3] <= '9')
365                 && !pName[4])
366                     return true;
367             break;
368         case 'N': /* NUL */
369             if (((pName[1] & ~0x20) == 'U')
370                 && ((pName[2] & ~0x20) == 'L')
371                 && !pName[3])
372                     return true;
373             break;
374         case 'P': /* PRN */
375             if (((pName[1] & ~0x20) == 'R')
376                 && ((pName[2] & ~0x20) == 'N')
377                 && !pName[3])
378                     return true;
379             break;
380     }
381     return false;
382 }
383
384 char *VDir::MapPathA(const char *pInName)
385 {   /*
386      * possiblities -- relative path or absolute path with or without drive letter
387      * OR UNC name
388      */
389     int driveIndex;
390     char szBuffer[(MAX_PATH+1)*2];
391     char szlBuf[MAX_PATH+1];
392     int length = strlen(pInName);
393
394     if (!length)
395         return (char*)pInName;
396
397     if (length > MAX_PATH) {
398         strncpy(szlBuf, pInName, MAX_PATH);
399         if (IsPathSep(pInName[0]) && !IsPathSep(pInName[1])) {   
400             /* absolute path - reduce length by 2 for drive specifier */
401             szlBuf[MAX_PATH-2] = '\0';
402         }
403         else
404             szlBuf[MAX_PATH] = '\0';
405         pInName = szlBuf;
406     }
407     /* strlen(pInName) is now <= MAX_PATH */
408
409     if (length > 1 && pInName[1] == ':') {
410         /* has drive letter */
411         if (length > 2 && IsPathSep(pInName[2])) {
412             /* absolute with drive letter */
413             DoGetFullPathNameA((char*)pInName, sizeof(szLocalBufferA), szLocalBufferA);
414         }
415         else {
416             /* relative path with drive letter */
417             driveIndex = DriveIndex(*pInName);
418             if (driveIndex < 0 || driveIndex >= driveLetterCount)
419                 return (char *)pInName;
420             strcpy(szBuffer, GetDirA(driveIndex));
421             strcat(szBuffer, &pInName[2]);
422             if(strlen(szBuffer) > MAX_PATH)
423                 szBuffer[MAX_PATH] = '\0';
424
425             DoGetFullPathNameA(szBuffer, sizeof(szLocalBufferA), szLocalBufferA);
426         }
427     }
428     else {
429         /* no drive letter */
430         if (length > 1 && IsPathSep(pInName[1]) && IsPathSep(pInName[0])) {
431             /* UNC name */
432             DoGetFullPathNameA((char*)pInName, sizeof(szLocalBufferA), szLocalBufferA);
433         }
434         else {
435             strcpy(szBuffer, GetDefaultDirA());
436             if (IsPathSep(pInName[0])) {
437                 /* absolute path */
438                 strcpy(&szBuffer[2], pInName);
439                 DoGetFullPathNameA(szBuffer, sizeof(szLocalBufferA), szLocalBufferA);
440             }
441             else {
442                 /* relative path */
443                 if (IsSpecialFileName(pInName)) {
444                     return (char*)pInName;
445                 }
446                 else {
447                     strcat(szBuffer, pInName);
448                     if (strlen(szBuffer) > MAX_PATH)
449                         szBuffer[MAX_PATH] = '\0';
450
451                     DoGetFullPathNameA(szBuffer, sizeof(szLocalBufferA), szLocalBufferA);
452                 }
453             }
454         }
455     }
456
457     return szLocalBufferA;
458 }
459
460 int VDir::SetCurrentDirectoryA(char *lpBuffer)
461 {
462     char *pPtr;
463     int length, nRet = -1;
464
465     pPtr = MapPathA(lpBuffer);
466     length = strlen(pPtr);
467     if(length > 3 && IsPathSep(pPtr[length-1])) {
468         /* don't remove the trailing slash from 'x:\'  */
469         pPtr[length-1] = '\0';
470     }
471
472     DWORD r = GetFileAttributesA(pPtr);
473     if ((r != 0xffffffff) && (r & FILE_ATTRIBUTE_DIRECTORY))
474     {
475         char szBuffer[(MAX_PATH+1)*2];
476         DoGetFullPathNameA(pPtr, sizeof(szBuffer), szBuffer);
477         SetDefaultDirA(szBuffer, DriveIndex(szBuffer[0]));
478         nRet = 0;
479     }
480
481     return nRet;
482 }
483
484 DWORD VDir::CalculateEnvironmentSpace(void)
485 {   /* the current directory environment strings are stored as '=D:=d:\path' */
486     int index;
487     DWORD dwSize = 0;
488     for (index = 0; index < driveCount; ++index) {
489         if (dirTableA[index] != NULL) {
490             dwSize += strlen(dirTableA[index]) + 5;  /* add 1 for trailing NULL and 4 for '=D:=' */
491         }
492     }
493     return dwSize;
494 }
495
496 LPSTR VDir::BuildEnvironmentSpace(LPSTR lpStr)
497 {   /* store the current directory environment strings as '=D:=d:\path' */
498     int index, length;
499     LPSTR lpDirStr;
500     for (index = 0; index < driveCount; ++index) {
501         lpDirStr = dirTableA[index];
502         if (lpDirStr != NULL) {
503             lpStr[0] = '=';
504             lpStr[1] = lpDirStr[0];
505             lpStr[2] = '\0';
506             CharUpper(&lpStr[1]);
507             lpStr[2] = ':';
508             lpStr[3] = '=';
509             strcpy(&lpStr[4], lpDirStr);
510             length = strlen(lpDirStr);
511             lpStr += length + 5; /* add 1 for trailing NULL and 4 for '=D:=' */
512             if (length > 3 && IsPathSep(lpStr[-2])) {
513                 lpStr[-2] = '\0';   /* remove the trailing path separator */
514                 --lpStr;
515             }
516         }
517     }
518     return lpStr;
519 }
520
521 inline BOOL IsPathSep(WCHAR ch)
522 {
523     return (ch == '\\' || ch == '/');
524 }
525
526 inline void DoGetFullPathNameW(WCHAR* lpBuffer, DWORD dwSize, WCHAR* Dest)
527 {
528     WCHAR *pPtr;
529
530     /*
531      * On WinNT GetFullPathName does not fail, (or at least always
532      * succeeds when the drive is valid) WinNT does set *Dest to NULL
533      * On Win98 GetFullPathName will set last error if it fails, but
534      * does not touch *Dest
535      */
536     *Dest = '\0';
537     GetFullPathNameW(lpBuffer, dwSize, Dest, &pPtr);
538 }
539
540 inline bool IsSpecialFileName(const WCHAR* pName)
541 {
542     /* specical file names are devices that the system can open
543      * these include AUX, CON, NUL, PRN, COMx, LPTx, CLOCK$, CONIN$, CONOUT$
544      * (x is a single digit, and names are case-insensitive)
545      */
546     WCHAR ch = (pName[0] & ~0x20);
547     switch (ch)
548     {
549         case 'A': /* AUX */
550             if (((pName[1] & ~0x20) == 'U')
551                 && ((pName[2] & ~0x20) == 'X')
552                 && !pName[3])
553                     return true;
554             break;
555         case 'C': /* CLOCK$, COMx,  CON, CONIN$ CONOUT$ */
556             ch = (pName[1] & ~0x20);
557             switch (ch)
558             {
559                 case 'L': /* CLOCK$ */
560                     if (((pName[2] & ~0x20) == 'O')
561                         && ((pName[3] & ~0x20) == 'C')
562                         && ((pName[4] & ~0x20) == 'K')
563                         && (pName[5] == '$')
564                         && !pName[6])
565                             return true;
566                     break;
567                 case 'O': /* COMx,  CON, CONIN$ CONOUT$ */
568                     if ((pName[2] & ~0x20) == 'M') {
569                         if ((pName[3] >= '1') && (pName[3] <= '9')
570                             && !pName[4])
571                             return true;
572                     }
573                     else if ((pName[2] & ~0x20) == 'N') {
574                         if (!pName[3])
575                             return true;
576                         else if ((pName[3] & ~0x20) == 'I') {
577                             if (((pName[4] & ~0x20) == 'N')
578                                 && (pName[5] == '$')
579                                 && !pName[6])
580                             return true;
581                         }
582                         else if ((pName[3] & ~0x20) == 'O') {
583                             if (((pName[4] & ~0x20) == 'U')
584                                 && ((pName[5] & ~0x20) == 'T')
585                                 && (pName[6] == '$')
586                                 && !pName[7])
587                             return true;
588                         }
589                     }
590                     break;
591             }
592             break;
593         case 'L': /* LPTx */
594             if (((pName[1] & ~0x20) == 'U')
595                 && ((pName[2] & ~0x20) == 'X')
596                 && (pName[3] >= '1') && (pName[3] <= '9')
597                 && !pName[4])
598                     return true;
599             break;
600         case 'N': /* NUL */
601             if (((pName[1] & ~0x20) == 'U')
602                 && ((pName[2] & ~0x20) == 'L')
603                 && !pName[3])
604                     return true;
605             break;
606         case 'P': /* PRN */
607             if (((pName[1] & ~0x20) == 'R')
608                 && ((pName[2] & ~0x20) == 'N')
609                 && !pName[3])
610                     return true;
611             break;
612     }
613     return false;
614 }
615
616 WCHAR* VDir::MapPathW(const WCHAR *pInName)
617 {   /*
618      * possiblities -- relative path or absolute path with or without drive letter
619      * OR UNC name
620      */
621     int driveIndex;
622     WCHAR szBuffer[(MAX_PATH+1)*2];
623     WCHAR szlBuf[MAX_PATH+1];
624     int length = wcslen(pInName);
625
626     if (!length)
627         return (WCHAR*)pInName;
628
629     if (length > MAX_PATH) {
630         wcsncpy(szlBuf, pInName, MAX_PATH);
631         if (IsPathSep(pInName[0]) && !IsPathSep(pInName[1])) {   
632             /* absolute path - reduce length by 2 for drive specifier */
633             szlBuf[MAX_PATH-2] = '\0';
634         }
635         else
636             szlBuf[MAX_PATH] = '\0';
637         pInName = szlBuf;
638     }
639     /* strlen(pInName) is now <= MAX_PATH */
640
641     if (length > 1 && pInName[1] == ':') {
642         /* has drive letter */
643         if (IsPathSep(pInName[2])) {
644             /* absolute with drive letter */
645             DoGetFullPathNameW((WCHAR*)pInName, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
646         }
647         else {
648             /* relative path with drive letter */
649             driveIndex = DriveIndex(*pInName);
650             if (driveIndex < 0 || driveIndex >= driveLetterCount)
651                 return (WCHAR *)pInName;
652             wcscpy(szBuffer, GetDirW(driveIndex));
653             wcscat(szBuffer, &pInName[2]);
654             if(wcslen(szBuffer) > MAX_PATH)
655                 szBuffer[MAX_PATH] = '\0';
656
657             DoGetFullPathNameW(szBuffer, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
658         }
659     }
660     else {
661         /* no drive letter */
662         if (length > 1 && IsPathSep(pInName[1]) && IsPathSep(pInName[0])) {
663             /* UNC name */
664             DoGetFullPathNameW((WCHAR*)pInName, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
665         }
666         else {
667             wcscpy(szBuffer, GetDefaultDirW());
668             if (IsPathSep(pInName[0])) {
669                 /* absolute path */
670                 wcscpy(&szBuffer[2], pInName);
671                 DoGetFullPathNameW(szBuffer, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
672             }
673             else {
674                 /* relative path */
675                 if (IsSpecialFileName(pInName)) {
676                     return (WCHAR*)pInName;
677                 }
678                 else {
679                     wcscat(szBuffer, pInName);
680                     if (wcslen(szBuffer) > MAX_PATH)
681                         szBuffer[MAX_PATH] = '\0';
682
683                     DoGetFullPathNameW(szBuffer, (sizeof(szLocalBufferW)/sizeof(WCHAR)), szLocalBufferW);
684                 }
685             }
686         }
687     }
688     return szLocalBufferW;
689 }
690
691 int VDir::SetCurrentDirectoryW(WCHAR *lpBuffer)
692 {
693     WCHAR *pPtr;
694     int length, nRet = -1;
695
696     pPtr = MapPathW(lpBuffer);
697     length = wcslen(pPtr);
698     if(length > 3 && IsPathSep(pPtr[length-1])) {
699         /* don't remove the trailing slash from 'x:\'  */
700         pPtr[length-1] = '\0';
701     }
702
703     DWORD r = GetFileAttributesW(pPtr);
704     if ((r != 0xffffffff) && (r & FILE_ATTRIBUTE_DIRECTORY))
705     {
706         WCHAR wBuffer[(MAX_PATH+1)*2];
707         DoGetFullPathNameW(pPtr, (sizeof(wBuffer)/sizeof(WCHAR)), wBuffer);
708         SetDefaultDirW(wBuffer, DriveIndex((char)wBuffer[0]));
709         nRet = 0;
710     }
711
712     return nRet;
713 }
714
715 #endif  /* ___VDir_H___ */