Path.inc revision 256281
1238106Sdes//===- llvm/Support/Win32/Path.cpp - Win32 Path Implementation ---*- C++ -*-===// 2238106Sdes// 3238106Sdes// The LLVM Compiler Infrastructure 4238106Sdes// 5238106Sdes// This file is distributed under the University of Illinois Open Source 6238106Sdes// License. See LICENSE.TXT for details. 7238106Sdes// 8238106Sdes//===----------------------------------------------------------------------===// 9238106Sdes// 10238106Sdes// This file provides the Win32 specific implementation of the Path class. 11238106Sdes// 12238106Sdes//===----------------------------------------------------------------------===// 13238106Sdes 14238106Sdes//===----------------------------------------------------------------------===// 15238106Sdes//=== WARNING: Implementation here must contain only generic Win32 code that 16238106Sdes//=== is guaranteed to work on *all* Win32 variants. 17238106Sdes//===----------------------------------------------------------------------===// 18238106Sdes 19238106Sdes#include "Windows.h" 20238106Sdes#include <cstdio> 21238106Sdes#include <malloc.h> 22238106Sdes 23238106Sdes// We need to undo a macro defined in Windows.h, otherwise we won't compile: 24269257Sdes#undef CopyFile 25269257Sdes#undef GetCurrentDirectory 26269257Sdes 27269257Sdes// Windows happily accepts either forward or backward slashes, though any path 28269257Sdes// returned by a Win32 API will have backward slashes. As LLVM code basically 29269257Sdes// assumes forward slashes are used, backward slashs are converted where they 30269257Sdes// can be introduced into a path. 31269257Sdes// 32269257Sdes// Another invariant is that a path ends with a slash if and only if the path 33269257Sdes// is a root directory. Any other use of a trailing slash is stripped. Unlike 34238106Sdes// in Unix, Windows has a rather complicated notion of a root path and this 35238106Sdes// invariant helps simply the code. 36238106Sdes 37238106Sdesstatic void FlipBackSlashes(std::string& s) { 38238106Sdes for (size_t i = 0; i < s.size(); i++) 39238106Sdes if (s[i] == '\\') 40238106Sdes s[i] = '/'; 41238106Sdes} 42238106Sdes 43238106Sdesnamespace llvm { 44238106Sdesnamespace sys { 45238106Sdes 46238106Sdesconst char PathSeparator = ';'; 47238106Sdes 48238106SdesStringRef Path::GetEXESuffix() { 49238106Sdes return "exe"; 50238106Sdes} 51295691Sdes 52291767SdesPath::Path(llvm::StringRef p) 53291767Sdes : path(p) { 54291767Sdes FlipBackSlashes(path); 55238106Sdes} 56238106Sdes 57238106SdesPath::Path(const char *StrStart, unsigned StrLen) 58238106Sdes : path(StrStart, StrLen) { 59238106Sdes FlipBackSlashes(path); 60238106Sdes} 61238106Sdes 62238106SdesPath& 63238106SdesPath::operator=(StringRef that) { 64238106Sdes path.assign(that.data(), that.size()); 65238106Sdes FlipBackSlashes(path); 66238106Sdes return *this; 67238106Sdes} 68238106Sdes 69238106Sdesbool 70238106SdesPath::isValid() const { 71238106Sdes if (path.empty()) 72238106Sdes return false; 73238106Sdes 74238106Sdes size_t len = path.size(); 75238106Sdes // If there is a null character, it and all its successors are ignored. 76238106Sdes size_t pos = path.find_first_of('\0'); 77238106Sdes if (pos != std::string::npos) 78238106Sdes len = pos; 79238106Sdes 80238106Sdes // If there is a colon, it must be the second character, preceded by a letter 81238106Sdes // and followed by something. 82238106Sdes pos = path.rfind(':',len); 83238106Sdes size_t rootslash = 0; 84238106Sdes if (pos != std::string::npos) { 85238106Sdes if (pos != 1 || !isalpha(static_cast<unsigned char>(path[0])) || len < 3) 86238106Sdes return false; 87238106Sdes rootslash = 2; 88238106Sdes } 89238106Sdes 90238106Sdes // Look for a UNC path, and if found adjust our notion of the root slash. 91238106Sdes if (len > 3 && path[0] == '/' && path[1] == '/') { 92238106Sdes rootslash = path.find('/', 2); 93238106Sdes if (rootslash == std::string::npos) 94238106Sdes rootslash = 0; 95238106Sdes } 96238106Sdes 97238106Sdes // Check for illegal characters. 98238106Sdes if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012" 99238106Sdes "\013\014\015\016\017\020\021\022\023\024\025\026" 100238106Sdes "\027\030\031\032\033\034\035\036\037") 101238106Sdes != std::string::npos) 102238106Sdes return false; 103238106Sdes 104238106Sdes // Remove trailing slash, unless it's a root slash. 105238106Sdes if (len > rootslash+1 && path[len-1] == '/') 106238106Sdes path.erase(--len); 107238106Sdes 108238106Sdes // Check each component for legality. 109238106Sdes for (pos = 0; pos < len; ++pos) { 110238106Sdes // A component may not end in a space. 111238106Sdes if (path[pos] == ' ') { 112238106Sdes if (pos+1 == len || path[pos+1] == '/' || path[pos+1] == '\0') 113238106Sdes return false; 114238106Sdes } 115238106Sdes 116238106Sdes // A component may not end in a period. 117238106Sdes if (path[pos] == '.') { 118238106Sdes if (pos+1 == len || path[pos+1] == '/') { 119238106Sdes // Unless it is the pseudo-directory "."... 120238106Sdes if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':') 121238106Sdes return true; 122238106Sdes // or "..". 123238106Sdes if (pos > 0 && path[pos-1] == '.') { 124238106Sdes if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':') 125238106Sdes return true; 126238106Sdes } 127238106Sdes return false; 128238106Sdes } 129238106Sdes } 130238106Sdes } 131238106Sdes 132238106Sdes return true; 133238106Sdes} 134238106Sdes 135238106Sdesvoid Path::makeAbsolute() { 136238106Sdes TCHAR FullPath[MAX_PATH + 1] = {0}; 137238106Sdes LPTSTR FilePart = NULL; 138238106Sdes 139238106Sdes DWORD RetLength = ::GetFullPathNameA(path.c_str(), 140238106Sdes sizeof(FullPath)/sizeof(FullPath[0]), 141238106Sdes FullPath, &FilePart); 142238106Sdes 143238106Sdes if (0 == RetLength) { 144238106Sdes // FIXME: Report the error GetLastError() 145238106Sdes assert(0 && "Unable to make absolute path!"); 146238106Sdes } else if (RetLength > MAX_PATH) { 147238106Sdes // FIXME: Report too small buffer (needed RetLength bytes). 148238106Sdes assert(0 && "Unable to make absolute path!"); 149238106Sdes } else { 150238106Sdes path = FullPath; 151238106Sdes } 152238106Sdes} 153238106Sdes 154238106Sdesbool 155238106SdesPath::isAbsolute(const char *NameStart, unsigned NameLen) { 156238106Sdes assert(NameStart); 157238106Sdes // FIXME: This does not handle correctly an absolute path starting from 158238106Sdes // a drive letter or in UNC format. 159238106Sdes switch (NameLen) { 160238106Sdes case 0: 161238106Sdes return false; 162238106Sdes case 1: 163238106Sdes case 2: 164238106Sdes return NameStart[0] == '/'; 165238106Sdes default: 166238106Sdes return 167238106Sdes (NameStart[0] == '/' || (NameStart[1] == ':' && NameStart[2] == '/')) || 168238106Sdes (NameStart[0] == '\\' || (NameStart[1] == ':' && NameStart[2] == '\\')); 169238106Sdes } 170238106Sdes} 171238106Sdes 172238106Sdesbool 173238106SdesPath::isAbsolute() const { 174238106Sdes // FIXME: This does not handle correctly an absolute path starting from 175238106Sdes // a drive letter or in UNC format. 176238106Sdes switch (path.length()) { 177238106Sdes case 0: 178238106Sdes return false; 179238106Sdes case 1: 180238106Sdes case 2: 181238106Sdes return path[0] == '/'; 182238106Sdes default: 183238106Sdes return path[0] == '/' || (path[1] == ':' && path[2] == '/'); 184238106Sdes } 185238106Sdes} 186238106Sdes 187238106Sdesstatic Path *TempDirectory; 188238106Sdes 189238106SdesPath 190238106SdesPath::GetTemporaryDirectory(std::string* ErrMsg) { 191238106Sdes if (TempDirectory) { 192238106Sdes#if defined(_MSC_VER) 193238106Sdes // Visual Studio gets confused and emits a diagnostic about calling exists, 194238106Sdes // even though this is the implementation for PathV1. Temporarily 195238106Sdes // disable the deprecated warning message 196238106Sdes #pragma warning(push) 197238106Sdes #pragma warning(disable:4996) 198238106Sdes#endif 199238106Sdes assert(TempDirectory->exists() && "Who has removed TempDirectory?"); 200238106Sdes#if defined(_MSC_VER) 201238106Sdes #pragma warning(pop) 202238106Sdes#endif 203238106Sdes return *TempDirectory; 204238106Sdes } 205238106Sdes 206238106Sdes char pathname[MAX_PATH]; 207238106Sdes if (!GetTempPath(MAX_PATH, pathname)) { 208238106Sdes if (ErrMsg) 209238106Sdes *ErrMsg = "Can't determine temporary directory"; 210238106Sdes return Path(); 211238106Sdes } 212238106Sdes 213238106Sdes Path result; 214238106Sdes result.set(pathname); 215238106Sdes 216238106Sdes // Append a subdirectory based on our process id so multiple LLVMs don't 217238106Sdes // step on each other's toes. 218238106Sdes#ifdef __MINGW32__ 219238106Sdes // Mingw's Win32 header files are broken. 220238106Sdes sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId())); 221238106Sdes#else 222238106Sdes sprintf(pathname, "LLVM_%u", GetCurrentProcessId()); 223238106Sdes#endif 224238106Sdes result.appendComponent(pathname); 225238106Sdes 226238106Sdes // If there's a directory left over from a previous LLVM execution that 227238106Sdes // happened to have the same process id, get rid of it. 228238106Sdes result.eraseFromDisk(true); 229238106Sdes 230238106Sdes // And finally (re-)create the empty directory. 231238106Sdes result.createDirectoryOnDisk(false); 232238106Sdes TempDirectory = new Path(result); 233238106Sdes return *TempDirectory; 234238106Sdes} 235238106Sdes 236238106Sdes// FIXME: the following set of functions don't map to Windows very well. 237238106SdesPath 238238106SdesPath::GetRootDirectory() { 239238106Sdes // This is the only notion that that Windows has of a root directory. Nothing 240238106Sdes // is here except for drives. 241238106Sdes return Path("file:///"); 242238106Sdes} 243238106Sdes 244238106Sdesvoid 245238106SdesPath::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) { 246238106Sdes char buff[MAX_PATH]; 247269257Sdes // Generic form of C:\Windows\System32 248269257Sdes HRESULT res = SHGetFolderPathA(NULL, 249238106Sdes CSIDL_FLAG_CREATE | CSIDL_SYSTEM, 250238106Sdes NULL, 251238106Sdes SHGFP_TYPE_CURRENT, 252238106Sdes buff); 253238106Sdes if (res != S_OK) { 254238106Sdes assert(0 && "Failed to get system directory"); 255238106Sdes return; 256238106Sdes } 257238106Sdes Paths.push_back(sys::Path(buff)); 258238106Sdes 259238106Sdes // Reset buff. 260238106Sdes buff[0] = 0; 261238106Sdes // Generic form of C:\Windows 262238106Sdes res = SHGetFolderPathA(NULL, 263238106Sdes CSIDL_FLAG_CREATE | CSIDL_WINDOWS, 264238106Sdes NULL, 265238106Sdes SHGFP_TYPE_CURRENT, 266238106Sdes buff); 267238106Sdes if (res != S_OK) { 268238106Sdes assert(0 && "Failed to get windows directory"); 269238106Sdes return; 270238106Sdes } 271238106Sdes Paths.push_back(sys::Path(buff)); 272238106Sdes} 273238106Sdes 274238106Sdesvoid 275238106SdesPath::GetBitcodeLibraryPaths(std::vector<sys::Path>& Paths) { 276238106Sdes char * env_var = getenv("LLVM_LIB_SEARCH_PATH"); 277238106Sdes if (env_var != 0) { 278238106Sdes getPathList(env_var,Paths); 279238106Sdes } 280238106Sdes#ifdef LLVM_LIBDIR 281238106Sdes { 282238106Sdes Path tmpPath; 283238106Sdes if (tmpPath.set(LLVM_LIBDIR)) 284238106Sdes if (tmpPath.canRead()) 285238106Sdes Paths.push_back(tmpPath); 286238106Sdes } 287238106Sdes#endif 288238106Sdes GetSystemLibraryPaths(Paths); 289238106Sdes} 290238106Sdes 291238106SdesPath 292238106SdesPath::GetUserHomeDirectory() { 293238106Sdes char buff[MAX_PATH]; 294238106Sdes HRESULT res = SHGetFolderPathA(NULL, 295238106Sdes CSIDL_FLAG_CREATE | CSIDL_APPDATA, 296238106Sdes NULL, 297238106Sdes SHGFP_TYPE_CURRENT, 298238106Sdes buff); 299238106Sdes if (res != S_OK) 300238106Sdes assert(0 && "Failed to get user home directory"); 301238106Sdes return Path(buff); 302238106Sdes} 303238106Sdes 304238106SdesPath 305238106SdesPath::GetCurrentDirectory() { 306238106Sdes char pathname[MAX_PATH]; 307238106Sdes ::GetCurrentDirectoryA(MAX_PATH,pathname); 308238106Sdes return Path(pathname); 309238106Sdes} 310238106Sdes 311238106Sdes/// GetMainExecutable - Return the path to the main executable, given the 312238106Sdes/// value of argv[0] from program startup. 313238106SdesPath Path::GetMainExecutable(const char *argv0, void *MainAddr) { 314238106Sdes char pathname[MAX_PATH]; 315238106Sdes DWORD ret = ::GetModuleFileNameA(NULL, pathname, MAX_PATH); 316238106Sdes return ret != MAX_PATH ? Path(pathname) : Path(); 317238106Sdes} 318238106Sdes 319238106Sdes 320238106Sdes// FIXME: the above set of functions don't map to Windows very well. 321238106Sdes 322238106Sdes 323238106SdesStringRef Path::getDirname() const { 324238106Sdes return getDirnameCharSep(path, "/"); 325238106Sdes} 326238106Sdes 327238106SdesStringRef 328238106SdesPath::getBasename() const { 329238106Sdes // Find the last slash 330238106Sdes size_t slash = path.rfind('/'); 331238106Sdes if (slash == std::string::npos) 332238106Sdes slash = 0; 333238106Sdes else 334238106Sdes slash++; 335238106Sdes 336238106Sdes size_t dot = path.rfind('.'); 337238106Sdes if (dot == std::string::npos || dot < slash) 338238106Sdes return StringRef(path).substr(slash); 339238106Sdes else 340238106Sdes return StringRef(path).substr(slash, dot - slash); 341238106Sdes} 342238106Sdes 343238106SdesStringRef 344238106SdesPath::getSuffix() const { 345238106Sdes // Find the last slash 346238106Sdes size_t slash = path.rfind('/'); 347238106Sdes if (slash == std::string::npos) 348238106Sdes slash = 0; 349238106Sdes else 350238106Sdes slash++; 351269257Sdes 352269257Sdes size_t dot = path.rfind('.'); 353269257Sdes if (dot == std::string::npos || dot < slash) 354238106Sdes return StringRef(""); 355238106Sdes else 356238106Sdes return StringRef(path).substr(dot + 1); 357269257Sdes} 358269257Sdes 359238106Sdesbool 360238106SdesPath::exists() const { 361269257Sdes DWORD attr = GetFileAttributes(path.c_str()); 362269257Sdes return attr != INVALID_FILE_ATTRIBUTES; 363269257Sdes} 364269257Sdes 365269257Sdesbool 366238106SdesPath::isDirectory() const { 367238106Sdes DWORD attr = GetFileAttributes(path.c_str()); 368238106Sdes return (attr != INVALID_FILE_ATTRIBUTES) && 369269257Sdes (attr & FILE_ATTRIBUTE_DIRECTORY); 370269257Sdes} 371238106Sdes 372238106Sdesbool 373238106SdesPath::isSymLink() const { 374238106Sdes DWORD attributes = GetFileAttributes(path.c_str()); 375238106Sdes 376238106Sdes if (attributes == INVALID_FILE_ATTRIBUTES) 377238106Sdes // There's no sane way to report this :(. 378238106Sdes assert(0 && "GetFileAttributes returned INVALID_FILE_ATTRIBUTES"); 379238106Sdes 380238106Sdes // This isn't exactly what defines a NTFS symlink, but it is only true for 381238106Sdes // paths that act like a symlink. 382238106Sdes return attributes & FILE_ATTRIBUTE_REPARSE_POINT; 383238106Sdes} 384269257Sdes 385269257Sdesbool 386238106SdesPath::canRead() const { 387238106Sdes // FIXME: take security attributes into account. 388238106Sdes DWORD attr = GetFileAttributes(path.c_str()); 389238106Sdes return attr != INVALID_FILE_ATTRIBUTES; 390269257Sdes} 391238106Sdes 392269257Sdesbool 393238106SdesPath::canWrite() const { 394238106Sdes // FIXME: take security attributes into account. 395238106Sdes DWORD attr = GetFileAttributes(path.c_str()); 396238106Sdes return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY); 397269257Sdes} 398238106Sdes 399238106Sdesbool 400238106SdesPath::canExecute() const { 401269257Sdes // FIXME: take security attributes into account. 402269257Sdes DWORD attr = GetFileAttributes(path.c_str()); 403269257Sdes return attr != INVALID_FILE_ATTRIBUTES; 404269257Sdes} 405269257Sdes 406269257Sdesbool 407269257SdesPath::isRegularFile() const { 408269257Sdes bool res; 409238106Sdes if (fs::is_regular_file(path, res)) 410238106Sdes return false; 411269257Sdes return res; 412238106Sdes} 413238106Sdes 414238106SdesStringRef 415238106SdesPath::getLast() const { 416238106Sdes // Find the last slash 417238106Sdes size_t pos = path.rfind('/'); 418238106Sdes 419238106Sdes // Handle the corner cases 420238106Sdes if (pos == std::string::npos) 421238106Sdes return path; 422238106Sdes 423238106Sdes // If the last character is a slash, we have a root directory 424238106Sdes if (pos == path.length()-1) 425238106Sdes return path; 426238106Sdes 427269257Sdes // Return everything after the last slash 428238106Sdes return StringRef(path).substr(pos+1); 429238106Sdes} 430238106Sdes 431269257Sdesconst FileStatus * 432269257SdesPathWithStatus::getFileStatus(bool update, std::string *ErrStr) const { 433269257Sdes if (!fsIsValid || update) { 434269257Sdes WIN32_FILE_ATTRIBUTE_DATA fi; 435238106Sdes if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { 436238106Sdes MakeErrMsg(ErrStr, "getStatusInfo():" + std::string(path) + 437238106Sdes ": Can't get status: "); 438238106Sdes return 0; 439238106Sdes } 440238106Sdes 441269257Sdes status.fileSize = fi.nFileSizeHigh; 442269257Sdes status.fileSize <<= sizeof(fi.nFileSizeHigh)*8; 443269257Sdes status.fileSize += fi.nFileSizeLow; 444238106Sdes 445269257Sdes status.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777; 446269257Sdes status.user = 9999; // Not applicable to Windows, so... 447269257Sdes status.group = 9999; // Not applicable to Windows, so... 448269257Sdes 449238106Sdes // FIXME: this is only unique if the file is accessed by the same file path. 450269257Sdes // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode 451269257Sdes // numbers, but the concept doesn't exist in Windows. 452269257Sdes status.uniqueID = 0; 453269257Sdes for (unsigned i = 0; i < path.length(); ++i) 454238106Sdes status.uniqueID += path[i]; 455238106Sdes 456238106Sdes ULARGE_INTEGER ui; 457269257Sdes ui.LowPart = fi.ftLastWriteTime.dwLowDateTime; 458269257Sdes ui.HighPart = fi.ftLastWriteTime.dwHighDateTime; 459269257Sdes status.modTime.fromWin32Time(ui.QuadPart); 460238106Sdes 461238106Sdes status.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; 462269257Sdes fsIsValid = true; 463269257Sdes } 464238106Sdes return &status; 465238106Sdes} 466238106Sdes 467238106Sdesbool Path::makeReadableOnDisk(std::string* ErrMsg) { 468238106Sdes // All files are readable on Windows (ignoring security attributes). 469238106Sdes return false; 470238106Sdes} 471269257Sdes 472238106Sdesbool Path::makeWriteableOnDisk(std::string* ErrMsg) { 473238106Sdes DWORD attr = GetFileAttributes(path.c_str()); 474238106Sdes 475238106Sdes // If it doesn't exist, we're done. 476238106Sdes if (attr == INVALID_FILE_ATTRIBUTES) 477238106Sdes return false; 478238106Sdes 479238106Sdes if (attr & FILE_ATTRIBUTE_READONLY) { 480238106Sdes if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) { 481238106Sdes MakeErrMsg(ErrMsg, std::string(path) + ": Can't make file writable: "); 482238106Sdes return true; 483238106Sdes } 484238106Sdes } 485238106Sdes return false; 486238106Sdes} 487238106Sdes 488238106Sdesbool Path::makeExecutableOnDisk(std::string* ErrMsg) { 489238106Sdes // All files are executable on Windows (ignoring security attributes). 490238106Sdes return false; 491238106Sdes} 492238106Sdes 493238106Sdesbool 494238106SdesPath::getDirectoryContents(std::set& result, std::string* ErrMsg) const { 495238106Sdes WIN32_FILE_ATTRIBUTE_DATA fi; 496238106Sdes if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { 497238106Sdes MakeErrMsg(ErrMsg, path + ": can't get status of file"); 498238106Sdes return true; 499238106Sdes } 500238106Sdes 501238106Sdes if (!(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { 502238106Sdes if (ErrMsg) 503238106Sdes *ErrMsg = path + ": not a directory"; 504238106Sdes return true; 505238106Sdes } 506238106Sdes 507238106Sdes result.clear(); 508238106Sdes WIN32_FIND_DATA fd; 509238106Sdes std::string searchpath = path; 510238106Sdes if (path.size() == 0 || searchpath[path.size()-1] == '/') 511238106Sdes searchpath += "*"; 512238106Sdes else 513238106Sdes searchpath += "/*"; 514238106Sdes 515238106Sdes HANDLE h = FindFirstFile(searchpath.c_str(), &fd); 516238106Sdes if (h == INVALID_HANDLE_VALUE) { 517238106Sdes if (GetLastError() == ERROR_FILE_NOT_FOUND) 518238106Sdes return true; // not really an error, now is it? 519238106Sdes MakeErrMsg(ErrMsg, path + ": Can't read directory: "); 520238106Sdes return true; 521238106Sdes } 522238106Sdes 523238106Sdes do { 524238106Sdes if (fd.cFileName[0] == '.') 525238106Sdes continue; 526238106Sdes Path aPath(path); 527238106Sdes aPath.appendComponent(&fd.cFileName[0]); 528238106Sdes result.insert(aPath); 529238106Sdes } while (FindNextFile(h, &fd)); 530238106Sdes 531269257Sdes DWORD err = GetLastError(); 532238106Sdes FindClose(h); 533238106Sdes if (err != ERROR_NO_MORE_FILES) { 534238106Sdes SetLastError(err); 535238106Sdes MakeErrMsg(ErrMsg, path + ": Can't read directory: "); 536238106Sdes return true; 537238106Sdes } 538238106Sdes return false; 539238106Sdes} 540238106Sdes 541269257Sdesbool 542269257SdesPath::set(StringRef a_path) { 543269257Sdes if (a_path.empty()) 544238106Sdes return false; 545238106Sdes std::string save(path); 546238106Sdes path = a_path; 547238106Sdes FlipBackSlashes(path); 548238106Sdes if (!isValid()) { 549269257Sdes path = save; 550269257Sdes return false; 551269257Sdes } 552238106Sdes return true; 553238106Sdes} 554238106Sdes 555238106Sdesbool 556238106SdesPath::appendComponent(StringRef name) { 557238106Sdes if (name.empty()) 558238106Sdes return false; 559238106Sdes std::string save(path); 560238106Sdes if (!path.empty()) { 561238106Sdes size_t last = path.size() - 1; 562238106Sdes if (path[last] != '/') 563238106Sdes path += '/'; 564238106Sdes } 565238106Sdes path += name; 566238106Sdes if (!isValid()) { 567285206Sdes path = save; 568238106Sdes return false; 569238106Sdes } 570238106Sdes return true; 571238106Sdes} 572238106Sdes 573238106Sdesbool 574238106SdesPath::eraseComponent() { 575238106Sdes size_t slashpos = path.rfind('/',path.size()); 576238106Sdes if (slashpos == path.size() - 1 || slashpos == std::string::npos) 577238106Sdes return false; 578238106Sdes std::string save(path); 579238106Sdes path.erase(slashpos); 580238106Sdes if (!isValid()) { 581269257Sdes path = save; 582238106Sdes return false; 583238106Sdes } 584269257Sdes return true; 585238106Sdes} 586285206Sdes 587238106Sdesbool 588238106SdesPath::eraseSuffix() { 589238106Sdes size_t dotpos = path.rfind('.',path.size()); 590238106Sdes size_t slashpos = path.rfind('/',path.size()); 591285206Sdes if (dotpos != std::string::npos) { 592238106Sdes if (slashpos == std::string::npos || dotpos > slashpos+1) { 593238106Sdes std::string save(path); 594238106Sdes path.erase(dotpos, path.size()-dotpos); 595238106Sdes if (!isValid()) { 596238106Sdes path = save; 597238106Sdes return false; 598238106Sdes } 599238106Sdes return true; 600238106Sdes } 601238106Sdes } 602238106Sdes return false; 603238106Sdes} 604238106Sdes 605238106Sdesinline bool PathMsg(std::string* ErrMsg, const char* pathname, const char*msg) { 606269257Sdes if (ErrMsg) 607238106Sdes *ErrMsg = std::string(pathname) + ": " + std::string(msg); 608238106Sdes return true; 609269257Sdes} 610238106Sdes 611285206Sdesbool 612269257SdesPath::createDirectoryOnDisk(bool create_parents, std::string* ErrMsg) { 613238106Sdes // Get a writeable copy of the path name 614238106Sdes size_t len = path.length(); 615269257Sdes char *pathname = reinterpret_cast<char *>(_alloca(len+2)); 616269257Sdes path.copy(pathname, len); 617238106Sdes pathname[len] = 0; 618238106Sdes 619238106Sdes // Make sure it ends with a slash. 620238106Sdes if (len == 0 || pathname[len - 1] != '/') { 621238106Sdes pathname[len] = '/'; 622238106Sdes pathname[++len] = 0; 623238106Sdes } 624238106Sdes 625238106Sdes // Determine starting point for initial / search. 626238106Sdes char *next = pathname; 627238106Sdes if (pathname[0] == '/' && pathname[1] == '/') { 628238106Sdes // Skip host name. 629238106Sdes next = strchr(pathname+2, '/'); 630238106Sdes if (next == NULL) 631238106Sdes return PathMsg(ErrMsg, pathname, "badly formed remote directory"); 632238106Sdes 633238106Sdes // Skip share name. 634238106Sdes next = strchr(next+1, '/'); 635238106Sdes if (next == NULL) 636269257Sdes return PathMsg(ErrMsg, pathname,"badly formed remote directory"); 637238106Sdes 638238106Sdes next++; 639238106Sdes if (*next == 0) 640238106Sdes return PathMsg(ErrMsg, pathname, "badly formed remote directory"); 641238106Sdes 642238106Sdes } else { 643238106Sdes if (pathname[1] == ':') 644238106Sdes next += 2; // skip drive letter 645238106Sdes if (*next == '/') 646238106Sdes next++; // skip root directory 647238106Sdes } 648238106Sdes 649269257Sdes // If we're supposed to create intermediate directories 650238106Sdes if (create_parents) { 651269257Sdes // Loop through the directory components until we're done 652285206Sdes while (*next) { 653238106Sdes next = strchr(next, '/'); 654269257Sdes *next = 0; 655238106Sdes if (!CreateDirectory(pathname, NULL) && 656269257Sdes GetLastError() != ERROR_ALREADY_EXISTS) 657238106Sdes return MakeErrMsg(ErrMsg, 658238106Sdes std::string(pathname) + ": Can't create directory: "); 659238106Sdes *next++ = '/'; 660238106Sdes } 661238106Sdes } else { 662269257Sdes // Drop trailing slash. 663238106Sdes pathname[len-1] = 0; 664269257Sdes if (!CreateDirectory(pathname, NULL) && 665238106Sdes GetLastError() != ERROR_ALREADY_EXISTS) { 666238106Sdes return MakeErrMsg(ErrMsg, std::string(pathname) + 667238106Sdes ": Can't create directory: "); 668269257Sdes } 669238106Sdes } 670238106Sdes return false; 671238106Sdes} 672238106Sdes 673238106Sdesbool 674269257SdesPath::createFileOnDisk(std::string* ErrMsg) { 675238106Sdes // Create the file 676238106Sdes HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, 677269257Sdes FILE_ATTRIBUTE_NORMAL, NULL); 678238106Sdes if (h == INVALID_HANDLE_VALUE) 679238106Sdes return MakeErrMsg(ErrMsg, path + ": Can't create file: "); 680238106Sdes 681269257Sdes CloseHandle(h); 682269257Sdes return false; 683269257Sdes} 684238106Sdes 685238106Sdesbool 686238106SdesPath::eraseFromDisk(bool remove_contents, std::string *ErrStr) const { 687238106Sdes WIN32_FILE_ATTRIBUTE_DATA fi; 688238106Sdes if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) 689238106Sdes return true; 690238106Sdes 691238106Sdes if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 692238106Sdes // If it doesn't exist, we're done. 693238106Sdes bool Exists; 694269257Sdes if (fs::exists(path, Exists) || !Exists) 695238106Sdes return false; 696238106Sdes 697238106Sdes char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3)); 698238106Sdes int lastchar = path.length() - 1 ; 699269257Sdes path.copy(pathname, lastchar+1); 700238106Sdes 701269257Sdes // Make path end with '/*'. 702238106Sdes if (pathname[lastchar] != '/') 703238106Sdes pathname[++lastchar] = '/'; 704238106Sdes pathname[lastchar+1] = '*'; 705238106Sdes pathname[lastchar+2] = 0; 706238106Sdes 707285206Sdes if (remove_contents) { 708238106Sdes WIN32_FIND_DATA fd; 709238106Sdes HANDLE h = FindFirstFile(pathname, &fd); 710238106Sdes 711238106Sdes // It's a bad idea to alter the contents of a directory while enumerating 712238106Sdes // its contents. So build a list of its contents first, then destroy them. 713269257Sdes 714238106Sdes if (h != INVALID_HANDLE_VALUE) { 715238106Sdes std::vector<Path> list; 716238106Sdes 717269257Sdes do { 718238106Sdes if (strcmp(fd.cFileName, ".") == 0) 719238106Sdes continue; 720238106Sdes if (strcmp(fd.cFileName, "..") == 0) 721238106Sdes continue; 722238106Sdes 723238106Sdes Path aPath(path); 724238106Sdes aPath.appendComponent(&fd.cFileName[0]); 725238106Sdes list.push_back(aPath); 726238106Sdes } while (FindNextFile(h, &fd)); 727238106Sdes 728238106Sdes DWORD err = GetLastError(); 729238106Sdes FindClose(h); 730238106Sdes if (err != ERROR_NO_MORE_FILES) { 731238106Sdes SetLastError(err); 732238106Sdes return MakeErrMsg(ErrStr, path + ": Can't read directory: "); 733238106Sdes } 734269257Sdes 735238106Sdes for (std::vector<Path>::iterator I = list.begin(); I != list.end(); 736238106Sdes ++I) { 737238106Sdes Path &aPath = *I; 738238106Sdes aPath.eraseFromDisk(true); 739238106Sdes } 740238106Sdes } else { 741238106Sdes if (GetLastError() != ERROR_FILE_NOT_FOUND) 742238106Sdes return MakeErrMsg(ErrStr, path + ": Can't read directory: "); 743238106Sdes } 744238106Sdes } 745238106Sdes 746269257Sdes pathname[lastchar] = 0; 747238106Sdes if (!RemoveDirectory(pathname)) 748269257Sdes return MakeErrMsg(ErrStr, 749238106Sdes std::string(pathname) + ": Can't destroy directory: "); 750269257Sdes return false; 751238106Sdes } else { 752238106Sdes // Read-only files cannot be deleted on Windows. Must remove the read-only 753238106Sdes // attribute first. 754238106Sdes if (fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { 755238106Sdes if (!SetFileAttributes(path.c_str(), 756238106Sdes fi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) 757238106Sdes return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); 758238106Sdes } 759238106Sdes 760238106Sdes if (!DeleteFile(path.c_str())) 761238106Sdes return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); 762238106Sdes return false; 763238106Sdes } 764238106Sdes} 765238106Sdes 766238106Sdesbool Path::getMagicNumber(std::string& Magic, unsigned len) const { 767238106Sdes assert(len < 1024 && "Request for magic string too long"); 768238106Sdes char* buf = reinterpret_cast<char*>(alloca(len)); 769238106Sdes 770269257Sdes HANDLE h = CreateFile(path.c_str(), 771238106Sdes GENERIC_READ, 772238106Sdes FILE_SHARE_READ, 773238106Sdes NULL, 774238106Sdes OPEN_EXISTING, 775238106Sdes FILE_ATTRIBUTE_NORMAL, 776238106Sdes NULL); 777238106Sdes if (h == INVALID_HANDLE_VALUE) 778238106Sdes return false; 779238106Sdes 780238106Sdes DWORD nRead = 0; 781238106Sdes BOOL ret = ReadFile(h, buf, len, &nRead, NULL); 782238106Sdes CloseHandle(h); 783238106Sdes 784238106Sdes if (!ret || nRead != len) 785269257Sdes return false; 786238106Sdes 787238106Sdes Magic = std::string(buf, len); 788238106Sdes return true; 789238106Sdes} 790238106Sdes 791238106Sdesbool 792238106SdesPath::renamePathOnDisk(const Path& newName, std::string* ErrMsg) { 793238106Sdes if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING)) 794238106Sdes return MakeErrMsg(ErrMsg, "Can't move '" + path + "' to '" + newName.path 795238106Sdes + "': "); 796238106Sdes return false; 797238106Sdes} 798238106Sdes 799238106Sdesbool 800238106SdesPath::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrMsg) const { 801238106Sdes // FIXME: should work on directories also. 802238106Sdes if (!si.isFile) { 803238106Sdes return true; 804238106Sdes } 805238106Sdes 806238106Sdes HANDLE h = CreateFile(path.c_str(), 807238106Sdes FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 808238106Sdes FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 809238106Sdes NULL, 810238106Sdes OPEN_EXISTING, 811238106Sdes FILE_ATTRIBUTE_NORMAL, 812238106Sdes NULL); 813238106Sdes if (h == INVALID_HANDLE_VALUE) 814238106Sdes return true; 815238106Sdes 816238106Sdes BY_HANDLE_FILE_INFORMATION bhfi; 817238106Sdes if (!GetFileInformationByHandle(h, &bhfi)) { 818238106Sdes DWORD err = GetLastError(); 819238106Sdes CloseHandle(h); 820238106Sdes SetLastError(err); 821238106Sdes return MakeErrMsg(ErrMsg, path + ": GetFileInformationByHandle: "); 822238106Sdes } 823238106Sdes 824238106Sdes ULARGE_INTEGER ui; 825238106Sdes ui.QuadPart = si.modTime.toWin32Time(); 826238106Sdes FILETIME ft; 827238106Sdes ft.dwLowDateTime = ui.LowPart; 828238106Sdes ft.dwHighDateTime = ui.HighPart; 829249141Sdes BOOL ret = SetFileTime(h, NULL, &ft, &ft); 830249141Sdes DWORD err = GetLastError(); 831238106Sdes CloseHandle(h); 832238106Sdes if (!ret) { 833238106Sdes SetLastError(err); 834238106Sdes return MakeErrMsg(ErrMsg, path + ": SetFileTime: "); 835238106Sdes } 836238106Sdes 837238106Sdes // Best we can do with Unix permission bits is to interpret the owner 838238106Sdes // writable bit. 839238106Sdes if (si.mode & 0200) { 840238106Sdes if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { 841238106Sdes if (!SetFileAttributes(path.c_str(), 842238106Sdes bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) 843238106Sdes return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); 844238106Sdes } 845238106Sdes } else { 846238106Sdes if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { 847238106Sdes if (!SetFileAttributes(path.c_str(), 848238106Sdes bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY)) 849238106Sdes return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); 850238106Sdes } 851238106Sdes } 852238106Sdes 853238106Sdes return false; 854238106Sdes} 855238106Sdes 856238106Sdesbool 857238106SdesCopyFile(const sys::Path &Dest, const sys::Path &Src, std::string* ErrMsg) { 858238106Sdes // Can't use CopyFile macro defined in Windows.h because it would mess up the 859238106Sdes // above line. We use the expansion it would have in a non-UNICODE build. 860238106Sdes if (!::CopyFileA(Src.c_str(), Dest.c_str(), false)) 861238106Sdes return MakeErrMsg(ErrMsg, "Can't copy '" + Src.str() + 862238106Sdes "' to '" + Dest.str() + "': "); 863238106Sdes return false; 864238106Sdes} 865238106Sdes 866238106Sdesbool 867238106SdesPath::makeUnique(bool reuse_current, std::string* ErrMsg) { 868238106Sdes bool Exists; 869238106Sdes if (reuse_current && (fs::exists(path, Exists) || !Exists)) 870238106Sdes return false; // File doesn't exist already, just use it! 871238106Sdes 872238106Sdes // Reserve space for -XXXXXX at the end. 873238106Sdes char *FNBuffer = (char*) alloca(path.size()+8); 874238106Sdes unsigned offset = path.size(); 875238106Sdes path.copy(FNBuffer, offset); 876238106Sdes 877238106Sdes // Find a numeric suffix that isn't used by an existing file. Assume there 878238106Sdes // won't be more than 1 million files with the same prefix. Probably a safe 879238106Sdes // bet. 880238106Sdes static int FCounter = -1; 881238106Sdes if (FCounter < 0) { 882238106Sdes // Give arbitrary initial seed. 883238106Sdes // FIXME: We should use sys::fs::unique_file() in future. 884238106Sdes LARGE_INTEGER cnt64; 885238106Sdes DWORD x = GetCurrentProcessId(); 886291767Sdes x = (x << 16) | (x >> 16); 887238106Sdes if (QueryPerformanceCounter(&cnt64)) // RDTSC 888238106Sdes x ^= cnt64.HighPart ^ cnt64.LowPart; 889238106Sdes FCounter = x % 1000000; 890238106Sdes } 891238106Sdes do { 892238106Sdes sprintf(FNBuffer+offset, "-%06u", FCounter); 893291767Sdes if (++FCounter > 999999) 894238106Sdes FCounter = 0; 895238106Sdes path = FNBuffer; 896238106Sdes } while (!fs::exists(path, Exists) && Exists); 897238106Sdes return false; 898238106Sdes} 899238106Sdes 900238106Sdesbool 901291767SdesPath::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) { 902238106Sdes // Make this into a unique file name 903238106Sdes makeUnique(reuse_current, ErrMsg); 904238106Sdes 905238106Sdes // Now go and create it 906238106Sdes HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, 907238106Sdes FILE_ATTRIBUTE_NORMAL, NULL); 908238106Sdes if (h == INVALID_HANDLE_VALUE) 909238106Sdes return MakeErrMsg(ErrMsg, path + ": can't create file"); 910238106Sdes 911238106Sdes CloseHandle(h); 912238106Sdes return false; 913238106Sdes} 914238106Sdes 915238106Sdes/// MapInFilePages - Not yet implemented on win32. 916238106Sdesconst char *Path::MapInFilePages(int FD, size_t FileSize, off_t Offset) { 917238106Sdes return 0; 918238106Sdes} 919238106Sdes 920238106Sdes/// MapInFilePages - Not yet implemented on win32. 921238106Sdesvoid Path::UnMapFilePages(const char *Base, size_t FileSize) { 922238106Sdes assert(0 && "NOT IMPLEMENTED"); 923238106Sdes} 924238106Sdes 925238106Sdes} 926238106Sdes} 927238106Sdes