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