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