Path.inc revision 87d0b9ed1462705dd9bf1cb7f67d0bf03af776c8
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 <cstdio> 21#include <malloc.h> 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(static_cast<unsigned char>(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#if defined(_MSC_VER) 193 // Visual Studio gets confused and emits a diagnostic about calling exists, 194 // even though this is the implementation for PathV1. Temporarily 195 // disable the deprecated warning message 196 #pragma warning(push) 197 #pragma warning(disable:4996) 198#endif 199 assert(TempDirectory->exists() && "Who has removed TempDirectory?"); 200#if defined(_MSC_VER) 201 #pragma warning(pop) 202#endif 203 return *TempDirectory; 204 } 205 206 char pathname[MAX_PATH]; 207 if (!GetTempPath(MAX_PATH, pathname)) { 208 if (ErrMsg) 209 *ErrMsg = "Can't determine temporary directory"; 210 return Path(); 211 } 212 213 Path result; 214 result.set(pathname); 215 216 // Append a subdirectory based on our process id so multiple LLVMs don't 217 // step on each other's toes. 218#ifdef __MINGW32__ 219 // Mingw's Win32 header files are broken. 220 sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId())); 221#else 222 sprintf(pathname, "LLVM_%u", GetCurrentProcessId()); 223#endif 224 result.appendComponent(pathname); 225 226 // If there's a directory left over from a previous LLVM execution that 227 // happened to have the same process id, get rid of it. 228 result.eraseFromDisk(true); 229 230 // And finally (re-)create the empty directory. 231 result.createDirectoryOnDisk(false); 232 TempDirectory = new Path(result); 233 return *TempDirectory; 234} 235 236// FIXME: the following set of functions don't map to Windows very well. 237Path 238Path::GetRootDirectory() { 239 // This is the only notion that that Windows has of a root directory. Nothing 240 // is here except for drives. 241 return Path("file:///"); 242} 243 244void 245Path::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) { 246 char buff[MAX_PATH]; 247 // Generic form of C:\Windows\System32 248 HRESULT res = SHGetFolderPathA(NULL, 249 CSIDL_FLAG_CREATE | CSIDL_SYSTEM, 250 NULL, 251 SHGFP_TYPE_CURRENT, 252 buff); 253 if (res != S_OK) { 254 assert(0 && "Failed to get system directory"); 255 return; 256 } 257 Paths.push_back(sys::Path(buff)); 258 259 // Reset buff. 260 buff[0] = 0; 261 // Generic form of C:\Windows 262 res = SHGetFolderPathA(NULL, 263 CSIDL_FLAG_CREATE | CSIDL_WINDOWS, 264 NULL, 265 SHGFP_TYPE_CURRENT, 266 buff); 267 if (res != S_OK) { 268 assert(0 && "Failed to get windows directory"); 269 return; 270 } 271 Paths.push_back(sys::Path(buff)); 272} 273 274void 275Path::GetBitcodeLibraryPaths(std::vector<sys::Path>& Paths) { 276 char * env_var = getenv("LLVM_LIB_SEARCH_PATH"); 277 if (env_var != 0) { 278 getPathList(env_var,Paths); 279 } 280#ifdef LLVM_LIBDIR 281 { 282 Path tmpPath; 283 if (tmpPath.set(LLVM_LIBDIR)) 284 if (tmpPath.canRead()) 285 Paths.push_back(tmpPath); 286 } 287#endif 288 GetSystemLibraryPaths(Paths); 289} 290 291Path 292Path::GetUserHomeDirectory() { 293 char buff[MAX_PATH]; 294 HRESULT res = SHGetFolderPathA(NULL, 295 CSIDL_FLAG_CREATE | CSIDL_APPDATA, 296 NULL, 297 SHGFP_TYPE_CURRENT, 298 buff); 299 if (res != S_OK) 300 assert(0 && "Failed to get user home directory"); 301 return Path(buff); 302} 303 304Path 305Path::GetCurrentDirectory() { 306 char pathname[MAX_PATH]; 307 ::GetCurrentDirectoryA(MAX_PATH,pathname); 308 return Path(pathname); 309} 310 311/// GetMainExecutable - Return the path to the main executable, given the 312/// value of argv[0] from program startup. 313Path Path::GetMainExecutable(const char *argv0, void *MainAddr) { 314 char pathname[MAX_PATH]; 315 DWORD ret = ::GetModuleFileNameA(NULL, pathname, MAX_PATH); 316 return ret != MAX_PATH ? Path(pathname) : Path(); 317} 318 319 320// FIXME: the above set of functions don't map to Windows very well. 321 322 323StringRef Path::getDirname() const { 324 return getDirnameCharSep(path, "/"); 325} 326 327StringRef 328Path::getBasename() const { 329 // Find the last slash 330 size_t slash = path.rfind('/'); 331 if (slash == std::string::npos) 332 slash = 0; 333 else 334 slash++; 335 336 size_t dot = path.rfind('.'); 337 if (dot == std::string::npos || dot < slash) 338 return StringRef(path).substr(slash); 339 else 340 return StringRef(path).substr(slash, dot - slash); 341} 342 343StringRef 344Path::getSuffix() const { 345 // Find the last slash 346 size_t slash = path.rfind('/'); 347 if (slash == std::string::npos) 348 slash = 0; 349 else 350 slash++; 351 352 size_t dot = path.rfind('.'); 353 if (dot == std::string::npos || dot < slash) 354 return StringRef(""); 355 else 356 return StringRef(path).substr(dot + 1); 357} 358 359bool 360Path::exists() const { 361 DWORD attr = GetFileAttributes(path.c_str()); 362 return attr != INVALID_FILE_ATTRIBUTES; 363} 364 365bool 366Path::isDirectory() const { 367 DWORD attr = GetFileAttributes(path.c_str()); 368 return (attr != INVALID_FILE_ATTRIBUTES) && 369 (attr & FILE_ATTRIBUTE_DIRECTORY); 370} 371 372bool 373Path::isSymLink() const { 374 DWORD attributes = GetFileAttributes(path.c_str()); 375 376 if (attributes == INVALID_FILE_ATTRIBUTES) 377 // There's no sane way to report this :(. 378 assert(0 && "GetFileAttributes returned INVALID_FILE_ATTRIBUTES"); 379 380 // This isn't exactly what defines a NTFS symlink, but it is only true for 381 // paths that act like a symlink. 382 return attributes & FILE_ATTRIBUTE_REPARSE_POINT; 383} 384 385bool 386Path::canRead() const { 387 // FIXME: take security attributes into account. 388 DWORD attr = GetFileAttributes(path.c_str()); 389 return attr != INVALID_FILE_ATTRIBUTES; 390} 391 392bool 393Path::canWrite() const { 394 // FIXME: take security attributes into account. 395 DWORD attr = GetFileAttributes(path.c_str()); 396 return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY); 397} 398 399bool 400Path::canExecute() const { 401 // FIXME: take security attributes into account. 402 DWORD attr = GetFileAttributes(path.c_str()); 403 return attr != INVALID_FILE_ATTRIBUTES; 404} 405 406bool 407Path::isRegularFile() const { 408 bool res; 409 if (fs::is_regular_file(path, res)) 410 return false; 411 return res; 412} 413 414StringRef 415Path::getLast() const { 416 // Find the last slash 417 size_t pos = path.rfind('/'); 418 419 // Handle the corner cases 420 if (pos == std::string::npos) 421 return path; 422 423 // If the last character is a slash, we have a root directory 424 if (pos == path.length()-1) 425 return path; 426 427 // Return everything after the last slash 428 return StringRef(path).substr(pos+1); 429} 430 431const FileStatus * 432PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const { 433 if (!fsIsValid || update) { 434 WIN32_FILE_ATTRIBUTE_DATA fi; 435 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { 436 MakeErrMsg(ErrStr, "getStatusInfo():" + std::string(path) + 437 ": Can't get status: "); 438 return 0; 439 } 440 441 status.fileSize = fi.nFileSizeHigh; 442 status.fileSize <<= sizeof(fi.nFileSizeHigh)*8; 443 status.fileSize += fi.nFileSizeLow; 444 445 status.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777; 446 status.user = 9999; // Not applicable to Windows, so... 447 status.group = 9999; // Not applicable to Windows, so... 448 449 // FIXME: this is only unique if the file is accessed by the same file path. 450 // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode 451 // numbers, but the concept doesn't exist in Windows. 452 status.uniqueID = 0; 453 for (unsigned i = 0; i < path.length(); ++i) 454 status.uniqueID += path[i]; 455 456 ULARGE_INTEGER ui; 457 ui.LowPart = fi.ftLastWriteTime.dwLowDateTime; 458 ui.HighPart = fi.ftLastWriteTime.dwHighDateTime; 459 status.modTime.fromWin32Time(ui.QuadPart); 460 461 status.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; 462 fsIsValid = true; 463 } 464 return &status; 465} 466 467bool Path::makeReadableOnDisk(std::string* ErrMsg) { 468 // All files are readable on Windows (ignoring security attributes). 469 return false; 470} 471 472bool Path::makeWriteableOnDisk(std::string* ErrMsg) { 473 DWORD attr = GetFileAttributes(path.c_str()); 474 475 // If it doesn't exist, we're done. 476 if (attr == INVALID_FILE_ATTRIBUTES) 477 return false; 478 479 if (attr & FILE_ATTRIBUTE_READONLY) { 480 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) { 481 MakeErrMsg(ErrMsg, std::string(path) + ": Can't make file writable: "); 482 return true; 483 } 484 } 485 return false; 486} 487 488bool Path::makeExecutableOnDisk(std::string* ErrMsg) { 489 // All files are executable on Windows (ignoring security attributes). 490 return false; 491} 492 493bool 494Path::getDirectoryContents(std::set& result, std::string* ErrMsg) const { 495 WIN32_FILE_ATTRIBUTE_DATA fi; 496 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { 497 MakeErrMsg(ErrMsg, path + ": can't get status of file"); 498 return true; 499 } 500 501 if (!(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { 502 if (ErrMsg) 503 *ErrMsg = path + ": not a directory"; 504 return true; 505 } 506 507 result.clear(); 508 WIN32_FIND_DATA fd; 509 std::string searchpath = path; 510 if (path.size() == 0 || searchpath[path.size()-1] == '/') 511 searchpath += "*"; 512 else 513 searchpath += "/*"; 514 515 HANDLE h = FindFirstFile(searchpath.c_str(), &fd); 516 if (h == INVALID_HANDLE_VALUE) { 517 if (GetLastError() == ERROR_FILE_NOT_FOUND) 518 return true; // not really an error, now is it? 519 MakeErrMsg(ErrMsg, path + ": Can't read directory: "); 520 return true; 521 } 522 523 do { 524 if (fd.cFileName[0] == '.') 525 continue; 526 Path aPath(path); 527 aPath.appendComponent(&fd.cFileName[0]); 528 result.insert(aPath); 529 } while (FindNextFile(h, &fd)); 530 531 DWORD err = GetLastError(); 532 FindClose(h); 533 if (err != ERROR_NO_MORE_FILES) { 534 SetLastError(err); 535 MakeErrMsg(ErrMsg, path + ": Can't read directory: "); 536 return true; 537 } 538 return false; 539} 540 541bool 542Path::set(StringRef a_path) { 543 if (a_path.empty()) 544 return false; 545 std::string save(path); 546 path = a_path; 547 FlipBackSlashes(path); 548 if (!isValid()) { 549 path = save; 550 return false; 551 } 552 return true; 553} 554 555bool 556Path::appendComponent(StringRef name) { 557 if (name.empty()) 558 return false; 559 std::string save(path); 560 if (!path.empty()) { 561 size_t last = path.size() - 1; 562 if (path[last] != '/') 563 path += '/'; 564 } 565 path += name; 566 if (!isValid()) { 567 path = save; 568 return false; 569 } 570 return true; 571} 572 573bool 574Path::eraseComponent() { 575 size_t slashpos = path.rfind('/',path.size()); 576 if (slashpos == path.size() - 1 || slashpos == std::string::npos) 577 return false; 578 std::string save(path); 579 path.erase(slashpos); 580 if (!isValid()) { 581 path = save; 582 return false; 583 } 584 return true; 585} 586 587bool 588Path::eraseSuffix() { 589 size_t dotpos = path.rfind('.',path.size()); 590 size_t slashpos = path.rfind('/',path.size()); 591 if (dotpos != std::string::npos) { 592 if (slashpos == std::string::npos || dotpos > slashpos+1) { 593 std::string save(path); 594 path.erase(dotpos, path.size()-dotpos); 595 if (!isValid()) { 596 path = save; 597 return false; 598 } 599 return true; 600 } 601 } 602 return false; 603} 604 605inline bool PathMsg(std::string* ErrMsg, const char* pathname, const char*msg) { 606 if (ErrMsg) 607 *ErrMsg = std::string(pathname) + ": " + std::string(msg); 608 return true; 609} 610 611bool 612Path::createDirectoryOnDisk(bool create_parents, std::string* ErrMsg) { 613 // Get a writeable copy of the path name 614 size_t len = path.length(); 615 char *pathname = reinterpret_cast<char *>(_alloca(len+2)); 616 path.copy(pathname, len); 617 pathname[len] = 0; 618 619 // Make sure it ends with a slash. 620 if (len == 0 || pathname[len - 1] != '/') { 621 pathname[len] = '/'; 622 pathname[++len] = 0; 623 } 624 625 // Determine starting point for initial / search. 626 char *next = pathname; 627 if (pathname[0] == '/' && pathname[1] == '/') { 628 // Skip host name. 629 next = strchr(pathname+2, '/'); 630 if (next == NULL) 631 return PathMsg(ErrMsg, pathname, "badly formed remote directory"); 632 633 // Skip share name. 634 next = strchr(next+1, '/'); 635 if (next == NULL) 636 return PathMsg(ErrMsg, pathname,"badly formed remote directory"); 637 638 next++; 639 if (*next == 0) 640 return PathMsg(ErrMsg, pathname, "badly formed remote directory"); 641 642 } else { 643 if (pathname[1] == ':') 644 next += 2; // skip drive letter 645 if (*next == '/') 646 next++; // skip root directory 647 } 648 649 // If we're supposed to create intermediate directories 650 if (create_parents) { 651 // Loop through the directory components until we're done 652 while (*next) { 653 next = strchr(next, '/'); 654 *next = 0; 655 if (!CreateDirectory(pathname, NULL) && 656 GetLastError() != ERROR_ALREADY_EXISTS) 657 return MakeErrMsg(ErrMsg, 658 std::string(pathname) + ": Can't create directory: "); 659 *next++ = '/'; 660 } 661 } else { 662 // Drop trailing slash. 663 pathname[len-1] = 0; 664 if (!CreateDirectory(pathname, NULL) && 665 GetLastError() != ERROR_ALREADY_EXISTS) { 666 return MakeErrMsg(ErrMsg, std::string(pathname) + 667 ": Can't create directory: "); 668 } 669 } 670 return false; 671} 672 673bool 674Path::createFileOnDisk(std::string* ErrMsg) { 675 // Create the file 676 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, 677 FILE_ATTRIBUTE_NORMAL, NULL); 678 if (h == INVALID_HANDLE_VALUE) 679 return MakeErrMsg(ErrMsg, path + ": Can't create file: "); 680 681 CloseHandle(h); 682 return false; 683} 684 685bool 686Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const { 687 WIN32_FILE_ATTRIBUTE_DATA fi; 688 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) 689 return true; 690 691 if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 692 // If it doesn't exist, we're done. 693 bool Exists; 694 if (fs::exists(path, Exists) || !Exists) 695 return false; 696 697 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3)); 698 int lastchar = path.length() - 1 ; 699 path.copy(pathname, lastchar+1); 700 701 // Make path end with '/*'. 702 if (pathname[lastchar] != '/') 703 pathname[++lastchar] = '/'; 704 pathname[lastchar+1] = '*'; 705 pathname[lastchar+2] = 0; 706 707 if (remove_contents) { 708 WIN32_FIND_DATA fd; 709 HANDLE h = FindFirstFile(pathname, &fd); 710 711 // It's a bad idea to alter the contents of a directory while enumerating 712 // its contents. So build a list of its contents first, then destroy them. 713 714 if (h != INVALID_HANDLE_VALUE) { 715 std::vector<Path> list; 716 717 do { 718 if (strcmp(fd.cFileName, ".") == 0) 719 continue; 720 if (strcmp(fd.cFileName, "..") == 0) 721 continue; 722 723 Path aPath(path); 724 aPath.appendComponent(&fd.cFileName[0]); 725 list.push_back(aPath); 726 } while (FindNextFile(h, &fd)); 727 728 DWORD err = GetLastError(); 729 FindClose(h); 730 if (err != ERROR_NO_MORE_FILES) { 731 SetLastError(err); 732 return MakeErrMsg(ErrStr, path + ": Can't read directory: "); 733 } 734 735 for (std::vector<Path>::iterator I = list.begin(); I != list.end(); 736 ++I) { 737 Path &aPath = *I; 738 aPath.eraseFromDisk(true); 739 } 740 } else { 741 if (GetLastError() != ERROR_FILE_NOT_FOUND) 742 return MakeErrMsg(ErrStr, path + ": Can't read directory: "); 743 } 744 } 745 746 pathname[lastchar] = 0; 747 if (!RemoveDirectory(pathname)) 748 return MakeErrMsg(ErrStr, 749 std::string(pathname) + ": Can't destroy directory: "); 750 return false; 751 } else { 752 // Read-only files cannot be deleted on Windows. Must remove the read-only 753 // attribute first. 754 if (fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { 755 if (!SetFileAttributes(path.c_str(), 756 fi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) 757 return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); 758 } 759 760 if (!DeleteFile(path.c_str())) 761 return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); 762 return false; 763 } 764} 765 766bool Path::getMagicNumber(std::string& Magic, unsigned len) const { 767 assert(len < 1024 && "Request for magic string too long"); 768 char* buf = reinterpret_cast<char*>(alloca(len)); 769 770 HANDLE h = CreateFile(path.c_str(), 771 GENERIC_READ, 772 FILE_SHARE_READ, 773 NULL, 774 OPEN_EXISTING, 775 FILE_ATTRIBUTE_NORMAL, 776 NULL); 777 if (h == INVALID_HANDLE_VALUE) 778 return false; 779 780 DWORD nRead = 0; 781 BOOL ret = ReadFile(h, buf, len, &nRead, NULL); 782 CloseHandle(h); 783 784 if (!ret || nRead != len) 785 return false; 786 787 Magic = std::string(buf, len); 788 return true; 789} 790 791bool 792Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) { 793 if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING)) 794 return MakeErrMsg(ErrMsg, "Can't move '" + path + "' to '" + newName.path 795 + "': "); 796 return false; 797} 798 799bool 800Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrMsg) const { 801 // FIXME: should work on directories also. 802 if (!si.isFile) { 803 return true; 804 } 805 806 HANDLE h = CreateFile(path.c_str(), 807 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 808 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 809 NULL, 810 OPEN_EXISTING, 811 FILE_ATTRIBUTE_NORMAL, 812 NULL); 813 if (h == INVALID_HANDLE_VALUE) 814 return true; 815 816 BY_HANDLE_FILE_INFORMATION bhfi; 817 if (!GetFileInformationByHandle(h, &bhfi)) { 818 DWORD err = GetLastError(); 819 CloseHandle(h); 820 SetLastError(err); 821 return MakeErrMsg(ErrMsg, path + ": GetFileInformationByHandle: "); 822 } 823 824 ULARGE_INTEGER ui; 825 ui.QuadPart = si.modTime.toWin32Time(); 826 FILETIME ft; 827 ft.dwLowDateTime = ui.LowPart; 828 ft.dwHighDateTime = ui.HighPart; 829 BOOL ret = SetFileTime(h, NULL, &ft, &ft); 830 DWORD err = GetLastError(); 831 CloseHandle(h); 832 if (!ret) { 833 SetLastError(err); 834 return MakeErrMsg(ErrMsg, path + ": SetFileTime: "); 835 } 836 837 // Best we can do with Unix permission bits is to interpret the owner 838 // writable bit. 839 if (si.mode & 0200) { 840 if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { 841 if (!SetFileAttributes(path.c_str(), 842 bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) 843 return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); 844 } 845 } else { 846 if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { 847 if (!SetFileAttributes(path.c_str(), 848 bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY)) 849 return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); 850 } 851 } 852 853 return false; 854} 855 856bool 857CopyFile(const sys::Path &Dest, const sys::Path &Src, std::string* ErrMsg) { 858 // Can't use CopyFile macro defined in Windows.h because it would mess up the 859 // above line. We use the expansion it would have in a non-UNICODE build. 860 if (!::CopyFileA(Src.c_str(), Dest.c_str(), false)) 861 return MakeErrMsg(ErrMsg, "Can't copy '" + Src.str() + 862 "' to '" + Dest.str() + "': "); 863 return false; 864} 865 866bool 867Path::makeUnique(bool reuse_current, std::string* ErrMsg) { 868 bool Exists; 869 if (reuse_current && (fs::exists(path, Exists) || !Exists)) 870 return false; // File doesn't exist already, just use it! 871 872 // Reserve space for -XXXXXX at the end. 873 char *FNBuffer = (char*) alloca(path.size()+8); 874 unsigned offset = path.size(); 875 path.copy(FNBuffer, offset); 876 877 // Find a numeric suffix that isn't used by an existing file. Assume there 878 // won't be more than 1 million files with the same prefix. Probably a safe 879 // bet. 880 static int FCounter = -1; 881 if (FCounter < 0) { 882 // Give arbitrary initial seed. 883 // FIXME: We should use sys::fs::unique_file() in future. 884 LARGE_INTEGER cnt64; 885 DWORD x = GetCurrentProcessId(); 886 x = (x << 16) | (x >> 16); 887 if (QueryPerformanceCounter(&cnt64)) // RDTSC 888 x ^= cnt64.HighPart ^ cnt64.LowPart; 889 FCounter = x % 1000000; 890 } 891 do { 892 sprintf(FNBuffer+offset, "-%06u", FCounter); 893 if (++FCounter > 999999) 894 FCounter = 0; 895 path = FNBuffer; 896 } while (!fs::exists(path, Exists) && Exists); 897 return false; 898} 899 900bool 901Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) { 902 // Make this into a unique file name 903 makeUnique(reuse_current, ErrMsg); 904 905 // Now go and create it 906 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, 907 FILE_ATTRIBUTE_NORMAL, NULL); 908 if (h == INVALID_HANDLE_VALUE) 909 return MakeErrMsg(ErrMsg, path + ": can't create file"); 910 911 CloseHandle(h); 912 return false; 913} 914 915/// MapInFilePages - Not yet implemented on win32. 916const char *Path::MapInFilePages(int FD, size_t FileSize, off_t Offset) { 917 return 0; 918} 919 920/// MapInFilePages - Not yet implemented on win32. 921void Path::UnMapFilePages(const char *Base, size_t FileSize) { 922 assert(0 && "NOT IMPLEMENTED"); 923} 924 925} 926} 927