Path.inc revision 5d0700786d53046b3d5d7fe0d8d207290a13872c
1//===- llvm/Support/Win32/Path.cpp - Win32 Path Implementation ---*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file provides the Win32 specific implementation of the Path class. 11// 12//===----------------------------------------------------------------------===// 13 14//===----------------------------------------------------------------------===// 15//=== WARNING: Implementation here must contain only generic Win32 code that 16//=== is guaranteed to work on *all* Win32 variants. 17//===----------------------------------------------------------------------===// 18 19#include "Windows.h" 20#include <malloc.h> 21#include <cstdio> 22 23// We need to undo a macro defined in Windows.h, otherwise we won't compile: 24#undef CopyFile 25#undef GetCurrentDirectory 26 27// Windows happily accepts either forward or backward slashes, though any path 28// returned by a Win32 API will have backward slashes. As LLVM code basically 29// assumes forward slashes are used, backward slashs are converted where they 30// can be introduced into a path. 31// 32// Another invariant is that a path ends with a slash if and only if the path 33// is a root directory. Any other use of a trailing slash is stripped. Unlike 34// in Unix, Windows has a rather complicated notion of a root path and this 35// invariant helps simply the code. 36 37static void FlipBackSlashes(std::string& s) { 38 for (size_t i = 0; i < s.size(); i++) 39 if (s[i] == '\\') 40 s[i] = '/'; 41} 42 43namespace llvm { 44namespace sys { 45 46const char PathSeparator = ';'; 47 48StringRef Path::GetEXESuffix() { 49 return "exe"; 50} 51 52Path::Path(llvm::StringRef p) 53 : path(p) { 54 FlipBackSlashes(path); 55} 56 57Path::Path(const char *StrStart, unsigned StrLen) 58 : path(StrStart, StrLen) { 59 FlipBackSlashes(path); 60} 61 62Path& 63Path::operator=(StringRef that) { 64 path.assign(that.data(), that.size()); 65 FlipBackSlashes(path); 66 return *this; 67} 68 69bool 70Path::isValid() const { 71 if (path.empty()) 72 return false; 73 74 size_t len = path.size(); 75 // If there is a null character, it and all its successors are ignored. 76 size_t pos = path.find_first_of('\0'); 77 if (pos != std::string::npos) 78 len = pos; 79 80 // If there is a colon, it must be the second character, preceded by a letter 81 // and followed by something. 82 pos = path.rfind(':',len); 83 size_t rootslash = 0; 84 if (pos != std::string::npos) { 85 if (pos != 1 || !isalpha(path[0]) || len < 3) 86 return false; 87 rootslash = 2; 88 } 89 90 // Look for a UNC path, and if found adjust our notion of the root slash. 91 if (len > 3 && path[0] == '/' && path[1] == '/') { 92 rootslash = path.find('/', 2); 93 if (rootslash == std::string::npos) 94 rootslash = 0; 95 } 96 97 // Check for illegal characters. 98 if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012" 99 "\013\014\015\016\017\020\021\022\023\024\025\026" 100 "\027\030\031\032\033\034\035\036\037") 101 != std::string::npos) 102 return false; 103 104 // Remove trailing slash, unless it's a root slash. 105 if (len > rootslash+1 && path[len-1] == '/') 106 path.erase(--len); 107 108 // Check each component for legality. 109 for (pos = 0; pos < len; ++pos) { 110 // A component may not end in a space. 111 if (path[pos] == ' ') { 112 if (pos+1 == len || path[pos+1] == '/' || path[pos+1] == '\0') 113 return false; 114 } 115 116 // A component may not end in a period. 117 if (path[pos] == '.') { 118 if (pos+1 == len || path[pos+1] == '/') { 119 // Unless it is the pseudo-directory "."... 120 if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':') 121 return true; 122 // or "..". 123 if (pos > 0 && path[pos-1] == '.') { 124 if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':') 125 return true; 126 } 127 return false; 128 } 129 } 130 } 131 132 return true; 133} 134 135void Path::makeAbsolute() { 136 TCHAR FullPath[MAX_PATH + 1] = {0}; 137 LPTSTR FilePart = NULL; 138 139 DWORD RetLength = ::GetFullPathNameA(path.c_str(), 140 sizeof(FullPath)/sizeof(FullPath[0]), 141 FullPath, &FilePart); 142 143 if (0 == RetLength) { 144 // FIXME: Report the error GetLastError() 145 assert(0 && "Unable to make absolute path!"); 146 } else if (RetLength > MAX_PATH) { 147 // FIXME: Report too small buffer (needed RetLength bytes). 148 assert(0 && "Unable to make absolute path!"); 149 } else { 150 path = FullPath; 151 } 152} 153 154bool 155Path::isAbsolute(const char *NameStart, unsigned NameLen) { 156 assert(NameStart); 157 // FIXME: This does not handle correctly an absolute path starting from 158 // a drive letter or in UNC format. 159 switch (NameLen) { 160 case 0: 161 return false; 162 case 1: 163 case 2: 164 return NameStart[0] == '/'; 165 default: 166 return 167 (NameStart[0] == '/' || (NameStart[1] == ':' && NameStart[2] == '/')) || 168 (NameStart[0] == '\\' || (NameStart[1] == ':' && NameStart[2] == '\\')); 169 } 170} 171 172bool 173Path::isAbsolute() const { 174 // FIXME: This does not handle correctly an absolute path starting from 175 // a drive letter or in UNC format. 176 switch (path.length()) { 177 case 0: 178 return false; 179 case 1: 180 case 2: 181 return path[0] == '/'; 182 default: 183 return path[0] == '/' || (path[1] == ':' && path[2] == '/'); 184 } 185} 186 187static Path *TempDirectory; 188 189Path 190Path::GetTemporaryDirectory(std::string* ErrMsg) { 191 if (TempDirectory) 192 return *TempDirectory; 193 194 char pathname[MAX_PATH]; 195 if (!GetTempPath(MAX_PATH, pathname)) { 196 if (ErrMsg) 197 *ErrMsg = "Can't determine temporary directory"; 198 return Path(); 199 } 200 201 Path result; 202 result.set(pathname); 203 204 // Append a subdirectory passed on our process id so multiple LLVMs don't 205 // step on each other's toes. 206#ifdef __MINGW32__ 207 // Mingw's Win32 header files are broken. 208 sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId())); 209#else 210 sprintf(pathname, "LLVM_%u", GetCurrentProcessId()); 211#endif 212 result.appendComponent(pathname); 213 214 // If there's a directory left over from a previous LLVM execution that 215 // happened to have the same process id, get rid of it. 216 result.eraseFromDisk(true); 217 218 // And finally (re-)create the empty directory. 219 result.createDirectoryOnDisk(false); 220 TempDirectory = new Path(result); 221 return *TempDirectory; 222} 223 224// FIXME: the following set of functions don't map to Windows very well. 225Path 226Path::GetRootDirectory() { 227 // This is the only notion that that Windows has of a root directory. Nothing 228 // is here except for drives. 229 return Path("file:///"); 230} 231 232void 233Path::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) { 234 char buff[MAX_PATH]; 235 // Generic form of C:\Windows\System32 236 HRESULT res = SHGetFolderPathA(NULL, 237 CSIDL_FLAG_CREATE | CSIDL_SYSTEM, 238 NULL, 239 SHGFP_TYPE_CURRENT, 240 buff); 241 if (res != S_OK) { 242 assert(0 && "Failed to get system directory"); 243 return; 244 } 245 Paths.push_back(sys::Path(buff)); 246 247 // Reset buff. 248 buff[0] = 0; 249 // Generic form of C:\Windows 250 res = SHGetFolderPathA(NULL, 251 CSIDL_FLAG_CREATE | CSIDL_WINDOWS, 252 NULL, 253 SHGFP_TYPE_CURRENT, 254 buff); 255 if (res != S_OK) { 256 assert(0 && "Failed to get windows directory"); 257 return; 258 } 259 Paths.push_back(sys::Path(buff)); 260} 261 262void 263Path::GetBitcodeLibraryPaths(std::vector<sys::Path>& Paths) { 264 char * env_var = getenv("LLVM_LIB_SEARCH_PATH"); 265 if (env_var != 0) { 266 getPathList(env_var,Paths); 267 } 268#ifdef LLVM_LIBDIR 269 { 270 Path tmpPath; 271 if (tmpPath.set(LLVM_LIBDIR)) 272 if (tmpPath.canRead()) 273 Paths.push_back(tmpPath); 274 } 275#endif 276 GetSystemLibraryPaths(Paths); 277} 278 279Path 280Path::GetLLVMDefaultConfigDir() { 281 Path ret = GetUserHomeDirectory(); 282 if (!ret.appendComponent(".llvm")) 283 assert(0 && "Failed to append .llvm"); 284 return ret; 285} 286 287Path 288Path::GetUserHomeDirectory() { 289 char buff[MAX_PATH]; 290 HRESULT res = SHGetFolderPathA(NULL, 291 CSIDL_FLAG_CREATE | CSIDL_APPDATA, 292 NULL, 293 SHGFP_TYPE_CURRENT, 294 buff); 295 if (res != S_OK) 296 assert(0 && "Failed to get user home directory"); 297 return Path(buff); 298} 299 300Path 301Path::GetCurrentDirectory() { 302 char pathname[MAX_PATH]; 303 ::GetCurrentDirectoryA(MAX_PATH,pathname); 304 return Path(pathname); 305} 306 307/// GetMainExecutable - Return the path to the main executable, given the 308/// value of argv[0] from program startup. 309Path Path::GetMainExecutable(const char *argv0, void *MainAddr) { 310 char pathname[MAX_PATH]; 311 DWORD ret = ::GetModuleFileNameA(NULL, pathname, MAX_PATH); 312 return ret != MAX_PATH ? Path(pathname) : Path(); 313} 314 315 316// FIXME: the above set of functions don't map to Windows very well. 317 318 319StringRef Path::getDirname() const { 320 return getDirnameCharSep(path, "/"); 321} 322 323StringRef 324Path::getBasename() const { 325 // Find the last slash 326 size_t slash = path.rfind('/'); 327 if (slash == std::string::npos) 328 slash = 0; 329 else 330 slash++; 331 332 size_t dot = path.rfind('.'); 333 if (dot == std::string::npos || dot < slash) 334 return StringRef(path).substr(slash); 335 else 336 return StringRef(path).substr(slash, dot - slash); 337} 338 339StringRef 340Path::getSuffix() const { 341 // Find the last slash 342 size_t slash = path.rfind('/'); 343 if (slash == std::string::npos) 344 slash = 0; 345 else 346 slash++; 347 348 size_t dot = path.rfind('.'); 349 if (dot == std::string::npos || dot < slash) 350 return StringRef(""); 351 else 352 return StringRef(path).substr(dot + 1); 353} 354 355bool 356Path::exists() const { 357 DWORD attr = GetFileAttributes(path.c_str()); 358 return attr != INVALID_FILE_ATTRIBUTES; 359} 360 361bool 362Path::isDirectory() const { 363 DWORD attr = GetFileAttributes(path.c_str()); 364 return (attr != INVALID_FILE_ATTRIBUTES) && 365 (attr & FILE_ATTRIBUTE_DIRECTORY); 366} 367 368bool 369Path::isSymLink() const { 370 DWORD attributes = GetFileAttributes(path.c_str()); 371 372 if (attributes == INVALID_FILE_ATTRIBUTES) 373 // There's no sane way to report this :(. 374 assert(0 && "GetFileAttributes returned INVALID_FILE_ATTRIBUTES"); 375 376 // This isn't exactly what defines a NTFS symlink, but it is only true for 377 // paths that act like a symlink. 378 return attributes & FILE_ATTRIBUTE_REPARSE_POINT; 379} 380 381bool 382Path::canRead() const { 383 // FIXME: take security attributes into account. 384 DWORD attr = GetFileAttributes(path.c_str()); 385 return attr != INVALID_FILE_ATTRIBUTES; 386} 387 388bool 389Path::canWrite() const { 390 // FIXME: take security attributes into account. 391 DWORD attr = GetFileAttributes(path.c_str()); 392 return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY); 393} 394 395bool 396Path::canExecute() const { 397 // FIXME: take security attributes into account. 398 DWORD attr = GetFileAttributes(path.c_str()); 399 return attr != INVALID_FILE_ATTRIBUTES; 400} 401 402bool 403Path::isRegularFile() const { 404 bool res; 405 if (fs::is_regular_file(path, res)) 406 return false; 407 return res; 408} 409 410StringRef 411Path::getLast() const { 412 // Find the last slash 413 size_t pos = path.rfind('/'); 414 415 // Handle the corner cases 416 if (pos == std::string::npos) 417 return path; 418 419 // If the last character is a slash, we have a root directory 420 if (pos == path.length()-1) 421 return path; 422 423 // Return everything after the last slash 424 return StringRef(path).substr(pos+1); 425} 426 427const FileStatus * 428PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const { 429 if (!fsIsValid || update) { 430 WIN32_FILE_ATTRIBUTE_DATA fi; 431 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { 432 MakeErrMsg(ErrStr, "getStatusInfo():" + std::string(path) + 433 ": Can't get status: "); 434 return 0; 435 } 436 437 status.fileSize = fi.nFileSizeHigh; 438 status.fileSize <<= sizeof(fi.nFileSizeHigh)*8; 439 status.fileSize += fi.nFileSizeLow; 440 441 status.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777; 442 status.user = 9999; // Not applicable to Windows, so... 443 status.group = 9999; // Not applicable to Windows, so... 444 445 // FIXME: this is only unique if the file is accessed by the same file path. 446 // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode 447 // numbers, but the concept doesn't exist in Windows. 448 status.uniqueID = 0; 449 for (unsigned i = 0; i < path.length(); ++i) 450 status.uniqueID += path[i]; 451 452 ULARGE_INTEGER ui; 453 ui.LowPart = fi.ftLastWriteTime.dwLowDateTime; 454 ui.HighPart = fi.ftLastWriteTime.dwHighDateTime; 455 status.modTime.fromWin32Time(ui.QuadPart); 456 457 status.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; 458 fsIsValid = true; 459 } 460 return &status; 461} 462 463bool Path::makeReadableOnDisk(std::string* ErrMsg) { 464 // All files are readable on Windows (ignoring security attributes). 465 return false; 466} 467 468bool Path::makeWriteableOnDisk(std::string* ErrMsg) { 469 DWORD attr = GetFileAttributes(path.c_str()); 470 471 // If it doesn't exist, we're done. 472 if (attr == INVALID_FILE_ATTRIBUTES) 473 return false; 474 475 if (attr & FILE_ATTRIBUTE_READONLY) { 476 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) { 477 MakeErrMsg(ErrMsg, std::string(path) + ": Can't make file writable: "); 478 return true; 479 } 480 } 481 return false; 482} 483 484bool Path::makeExecutableOnDisk(std::string* ErrMsg) { 485 // All files are executable on Windows (ignoring security attributes). 486 return false; 487} 488 489bool 490Path::getDirectoryContents(std::set& result, std::string* ErrMsg) const { 491 WIN32_FILE_ATTRIBUTE_DATA fi; 492 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { 493 MakeErrMsg(ErrMsg, path + ": can't get status of file"); 494 return true; 495 } 496 497 if (!(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { 498 if (ErrMsg) 499 *ErrMsg = path + ": not a directory"; 500 return true; 501 } 502 503 result.clear(); 504 WIN32_FIND_DATA fd; 505 std::string searchpath = path; 506 if (path.size() == 0 || searchpath[path.size()-1] == '/') 507 searchpath += "*"; 508 else 509 searchpath += "/*"; 510 511 HANDLE h = FindFirstFile(searchpath.c_str(), &fd); 512 if (h == INVALID_HANDLE_VALUE) { 513 if (GetLastError() == ERROR_FILE_NOT_FOUND) 514 return true; // not really an error, now is it? 515 MakeErrMsg(ErrMsg, path + ": Can't read directory: "); 516 return true; 517 } 518 519 do { 520 if (fd.cFileName[0] == '.') 521 continue; 522 Path aPath(path); 523 aPath.appendComponent(&fd.cFileName[0]); 524 result.insert(aPath); 525 } while (FindNextFile(h, &fd)); 526 527 DWORD err = GetLastError(); 528 FindClose(h); 529 if (err != ERROR_NO_MORE_FILES) { 530 SetLastError(err); 531 MakeErrMsg(ErrMsg, path + ": Can't read directory: "); 532 return true; 533 } 534 return false; 535} 536 537bool 538Path::set(StringRef a_path) { 539 if (a_path.empty()) 540 return false; 541 std::string save(path); 542 path = a_path; 543 FlipBackSlashes(path); 544 if (!isValid()) { 545 path = save; 546 return false; 547 } 548 return true; 549} 550 551bool 552Path::appendComponent(StringRef name) { 553 if (name.empty()) 554 return false; 555 std::string save(path); 556 if (!path.empty()) { 557 size_t last = path.size() - 1; 558 if (path[last] != '/') 559 path += '/'; 560 } 561 path += name; 562 if (!isValid()) { 563 path = save; 564 return false; 565 } 566 return true; 567} 568 569bool 570Path::eraseComponent() { 571 size_t slashpos = path.rfind('/',path.size()); 572 if (slashpos == path.size() - 1 || slashpos == std::string::npos) 573 return false; 574 std::string save(path); 575 path.erase(slashpos); 576 if (!isValid()) { 577 path = save; 578 return false; 579 } 580 return true; 581} 582 583bool 584Path::eraseSuffix() { 585 size_t dotpos = path.rfind('.',path.size()); 586 size_t slashpos = path.rfind('/',path.size()); 587 if (dotpos != std::string::npos) { 588 if (slashpos == std::string::npos || dotpos > slashpos+1) { 589 std::string save(path); 590 path.erase(dotpos, path.size()-dotpos); 591 if (!isValid()) { 592 path = save; 593 return false; 594 } 595 return true; 596 } 597 } 598 return false; 599} 600 601inline bool PathMsg(std::string* ErrMsg, const char* pathname, const char*msg) { 602 if (ErrMsg) 603 *ErrMsg = std::string(pathname) + ": " + std::string(msg); 604 return true; 605} 606 607bool 608Path::createDirectoryOnDisk(bool create_parents, std::string* ErrMsg) { 609 // Get a writeable copy of the path name 610 size_t len = path.length(); 611 char *pathname = reinterpret_cast<char *>(_alloca(len+2)); 612 path.copy(pathname, len); 613 pathname[len] = 0; 614 615 // Make sure it ends with a slash. 616 if (len == 0 || pathname[len - 1] != '/') { 617 pathname[len] = '/'; 618 pathname[++len] = 0; 619 } 620 621 // Determine starting point for initial / search. 622 char *next = pathname; 623 if (pathname[0] == '/' && pathname[1] == '/') { 624 // Skip host name. 625 next = strchr(pathname+2, '/'); 626 if (next == NULL) 627 return PathMsg(ErrMsg, pathname, "badly formed remote directory"); 628 629 // Skip share name. 630 next = strchr(next+1, '/'); 631 if (next == NULL) 632 return PathMsg(ErrMsg, pathname,"badly formed remote directory"); 633 634 next++; 635 if (*next == 0) 636 return PathMsg(ErrMsg, pathname, "badly formed remote directory"); 637 638 } else { 639 if (pathname[1] == ':') 640 next += 2; // skip drive letter 641 if (*next == '/') 642 next++; // skip root directory 643 } 644 645 // If we're supposed to create intermediate directories 646 if (create_parents) { 647 // Loop through the directory components until we're done 648 while (*next) { 649 next = strchr(next, '/'); 650 *next = 0; 651 if (!CreateDirectory(pathname, NULL) && 652 GetLastError() != ERROR_ALREADY_EXISTS) 653 return MakeErrMsg(ErrMsg, 654 std::string(pathname) + ": Can't create directory: "); 655 *next++ = '/'; 656 } 657 } else { 658 // Drop trailing slash. 659 pathname[len-1] = 0; 660 if (!CreateDirectory(pathname, NULL) && 661 GetLastError() != ERROR_ALREADY_EXISTS) { 662 return MakeErrMsg(ErrMsg, std::string(pathname) + 663 ": Can't create directory: "); 664 } 665 } 666 return false; 667} 668 669bool 670Path::createFileOnDisk(std::string* ErrMsg) { 671 // Create the file 672 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, 673 FILE_ATTRIBUTE_NORMAL, NULL); 674 if (h == INVALID_HANDLE_VALUE) 675 return MakeErrMsg(ErrMsg, path + ": Can't create file: "); 676 677 CloseHandle(h); 678 return false; 679} 680 681bool 682Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const { 683 WIN32_FILE_ATTRIBUTE_DATA fi; 684 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) 685 return true; 686 687 if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 688 // If it doesn't exist, we're done. 689 bool Exists; 690 if (fs::exists(path, Exists) || !Exists) 691 return false; 692 693 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3)); 694 int lastchar = path.length() - 1 ; 695 path.copy(pathname, lastchar+1); 696 697 // Make path end with '/*'. 698 if (pathname[lastchar] != '/') 699 pathname[++lastchar] = '/'; 700 pathname[lastchar+1] = '*'; 701 pathname[lastchar+2] = 0; 702 703 if (remove_contents) { 704 WIN32_FIND_DATA fd; 705 HANDLE h = FindFirstFile(pathname, &fd); 706 707 // It's a bad idea to alter the contents of a directory while enumerating 708 // its contents. So build a list of its contents first, then destroy them. 709 710 if (h != INVALID_HANDLE_VALUE) { 711 std::vector<Path> list; 712 713 do { 714 if (strcmp(fd.cFileName, ".") == 0) 715 continue; 716 if (strcmp(fd.cFileName, "..") == 0) 717 continue; 718 719 Path aPath(path); 720 aPath.appendComponent(&fd.cFileName[0]); 721 list.push_back(aPath); 722 } while (FindNextFile(h, &fd)); 723 724 DWORD err = GetLastError(); 725 FindClose(h); 726 if (err != ERROR_NO_MORE_FILES) { 727 SetLastError(err); 728 return MakeErrMsg(ErrStr, path + ": Can't read directory: "); 729 } 730 731 for (std::vector<Path>::iterator I = list.begin(); I != list.end(); 732 ++I) { 733 Path &aPath = *I; 734 aPath.eraseFromDisk(true); 735 } 736 } else { 737 if (GetLastError() != ERROR_FILE_NOT_FOUND) 738 return MakeErrMsg(ErrStr, path + ": Can't read directory: "); 739 } 740 } 741 742 pathname[lastchar] = 0; 743 if (!RemoveDirectory(pathname)) 744 return MakeErrMsg(ErrStr, 745 std::string(pathname) + ": Can't destroy directory: "); 746 return false; 747 } else { 748 // Read-only files cannot be deleted on Windows. Must remove the read-only 749 // attribute first. 750 if (fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { 751 if (!SetFileAttributes(path.c_str(), 752 fi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) 753 return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); 754 } 755 756 if (!DeleteFile(path.c_str())) 757 return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); 758 return false; 759 } 760} 761 762bool Path::getMagicNumber(std::string& Magic, unsigned len) const { 763 assert(len < 1024 && "Request for magic string too long"); 764 char* buf = reinterpret_cast<char*>(alloca(len)); 765 766 HANDLE h = CreateFile(path.c_str(), 767 GENERIC_READ, 768 FILE_SHARE_READ, 769 NULL, 770 OPEN_EXISTING, 771 FILE_ATTRIBUTE_NORMAL, 772 NULL); 773 if (h == INVALID_HANDLE_VALUE) 774 return false; 775 776 DWORD nRead = 0; 777 BOOL ret = ReadFile(h, buf, len, &nRead, NULL); 778 CloseHandle(h); 779 780 if (!ret || nRead != len) 781 return false; 782 783 Magic = std::string(buf, len); 784 return true; 785} 786 787bool 788Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) { 789 if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING)) 790 return MakeErrMsg(ErrMsg, "Can't move '" + path + "' to '" + newName.path 791 + "': "); 792 return false; 793} 794 795bool 796Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrMsg) const { 797 // FIXME: should work on directories also. 798 if (!si.isFile) { 799 return true; 800 } 801 802 HANDLE h = CreateFile(path.c_str(), 803 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 804 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 805 NULL, 806 OPEN_EXISTING, 807 FILE_ATTRIBUTE_NORMAL, 808 NULL); 809 if (h == INVALID_HANDLE_VALUE) 810 return true; 811 812 BY_HANDLE_FILE_INFORMATION bhfi; 813 if (!GetFileInformationByHandle(h, &bhfi)) { 814 DWORD err = GetLastError(); 815 CloseHandle(h); 816 SetLastError(err); 817 return MakeErrMsg(ErrMsg, path + ": GetFileInformationByHandle: "); 818 } 819 820 ULARGE_INTEGER ui; 821 ui.QuadPart = si.modTime.toWin32Time(); 822 FILETIME ft; 823 ft.dwLowDateTime = ui.LowPart; 824 ft.dwHighDateTime = ui.HighPart; 825 BOOL ret = SetFileTime(h, NULL, &ft, &ft); 826 DWORD err = GetLastError(); 827 CloseHandle(h); 828 if (!ret) { 829 SetLastError(err); 830 return MakeErrMsg(ErrMsg, path + ": SetFileTime: "); 831 } 832 833 // Best we can do with Unix permission bits is to interpret the owner 834 // writable bit. 835 if (si.mode & 0200) { 836 if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { 837 if (!SetFileAttributes(path.c_str(), 838 bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) 839 return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); 840 } 841 } else { 842 if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { 843 if (!SetFileAttributes(path.c_str(), 844 bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY)) 845 return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); 846 } 847 } 848 849 return false; 850} 851 852bool 853CopyFile(const sys::Path &Dest, const sys::Path &Src, std::string* ErrMsg) { 854 // Can't use CopyFile macro defined in Windows.h because it would mess up the 855 // above line. We use the expansion it would have in a non-UNICODE build. 856 if (!::CopyFileA(Src.c_str(), Dest.c_str(), false)) 857 return MakeErrMsg(ErrMsg, "Can't copy '" + Src.str() + 858 "' to '" + Dest.str() + "': "); 859 return false; 860} 861 862bool 863Path::makeUnique(bool reuse_current, std::string* ErrMsg) { 864 bool Exists; 865 if (reuse_current && (fs::exists(path, Exists) || !Exists)) 866 return false; // File doesn't exist already, just use it! 867 868 // Reserve space for -XXXXXX at the end. 869 char *FNBuffer = (char*) alloca(path.size()+8); 870 unsigned offset = path.size(); 871 path.copy(FNBuffer, offset); 872 873 // Find a numeric suffix that isn't used by an existing file. Assume there 874 // won't be more than 1 million files with the same prefix. Probably a safe 875 // bet. 876 static int FCounter = -1; 877 if (FCounter < 0) { 878 // Give arbitrary initial seed. 879 // FIXME: We should use sys::fs::unique_file() in future. 880 LARGE_INTEGER cnt64; 881 DWORD x = GetCurrentProcessId(); 882 x = (x << 16) | (x >> 16); 883 if (QueryPerformanceCounter(&cnt64)) // RDTSC 884 x ^= cnt64.HighPart ^ cnt64.LowPart; 885 FCounter = x % 1000000; 886 } 887 do { 888 sprintf(FNBuffer+offset, "-%06u", FCounter); 889 if (++FCounter > 999999) 890 FCounter = 0; 891 path = FNBuffer; 892 } while (!fs::exists(path, Exists) && Exists); 893 return false; 894} 895 896bool 897Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) { 898 // Make this into a unique file name 899 makeUnique(reuse_current, ErrMsg); 900 901 // Now go and create it 902 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, 903 FILE_ATTRIBUTE_NORMAL, NULL); 904 if (h == INVALID_HANDLE_VALUE) 905 return MakeErrMsg(ErrMsg, path + ": can't create file"); 906 907 CloseHandle(h); 908 return false; 909} 910 911/// MapInFilePages - Not yet implemented on win32. 912const char *Path::MapInFilePages(int FD, size_t FileSize, off_t Offset) { 913 return 0; 914} 915 916/// MapInFilePages - Not yet implemented on win32. 917void Path::UnMapFilePages(const char *Base, size_t FileSize) { 918 assert(0 && "NOT IMPLEMENTED"); 919} 920 921} 922} 923