X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FSystem%2FWin32%2FPath.inc;h=15c686d2640d5c2643e9d9ef4beadcd5416ab14b;hb=0c33231eff17edbdabdb186e1fae718cf94b58b2;hp=3e179eaf48a3a7c06f77e2fb1762a70fa340aa02;hpb=00e89302069fe55e1dae58360bb9f7fd243d0b2b;p=oota-llvm.git diff --git a/lib/System/Win32/Path.inc b/lib/System/Win32/Path.inc index 3e179eaf48a..15c686d2640 100644 --- a/lib/System/Win32/Path.inc +++ b/lib/System/Win32/Path.inc @@ -20,9 +20,21 @@ //===----------------------------------------------------------------------===// #include "Win32.h" -#include #include +// We need to undo a macro defined in Windows.h, otherwise we won't compile: +#undef CopyFile + +// Windows happily accepts either forward or backward slashes, though any path +// returned by a Win32 API will have backward slashes. As LLVM code basically +// assumes forward slashes are used, backward slashs are converted where they +// can be introduced into a path. +// +// Another invariant is that a path ends with a slash if and only if the path +// is a root directory. Any other use of a trailing slash is stripped. Unlike +// in Unix, Windows has a rather complicated notion of a root path and this +// invariant helps simply the code. + static void FlipBackSlashes(std::string& s) { for (size_t i = 0; i < s.size(); i++) if (s[i] == '\\') @@ -41,9 +53,18 @@ Path::isValid() const { // and followed by something. size_t len = path.size(); size_t pos = path.rfind(':',len); + size_t rootslash = 0; if (pos != std::string::npos) { if (pos != 1 || !isalpha(path[0]) || len < 3) return false; + rootslash = 2; + } + + // Look for a UNC path, and if found adjust our notion of the root slash. + if (len > 3 && path[0] == '/' && path[1] == '/') { + rootslash = path.find('/', 2); + if (rootslash == std::string::npos) + rootslash = 0; } // Check for illegal characters. @@ -53,17 +74,33 @@ Path::isValid() const { != std::string::npos) return false; - // A file or directory name may not end in a period. - if (path[len-1] == '.') - return false; - if (len >= 2 && path[len-2] == '.' && path[len-1] == '/') - return false; + // Remove trailing slash, unless it's a root slash. + if (len > rootslash+1 && path[len-1] == '/') + path.erase(--len); - // A file or directory name may not end in a space. - if (path[len-1] == ' ') - return false; - if (len >= 2 && path[len-2] == ' ' && path[len-1] == '/') - return false; + // Check each component for legality. + for (pos = 0; pos < len; ++pos) { + // A component may not end in a space. + if (path[pos] == ' ') { + if (path[pos+1] == '/' || path[pos+1] == '\0') + return false; + } + + // A component may not end in a period. + if (path[pos] == '.') { + if (path[pos+1] == '/' || path[pos+1] == '\0') { + // Unless it is the pseudo-directory "."... + if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':') + return true; + // or "..". + if (pos > 0 && path[pos-1] == '.') { + if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':') + return true; + } + return false; + } + } + } return true; } @@ -80,19 +117,19 @@ Path::GetTemporaryDirectory() { throw std::string("Can't determine temporary directory"); Path result; - result.setDirectory(pathname); + result.set(pathname); // Append a subdirectory passed on our process id so multiple LLVMs don't // step on each other's toes. - sprintf(pathname, "LLVM_%u", GetCurrentProcessId()); - result.appendDirectory(pathname); + sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId())); + result.appendComponent(pathname); // If there's a directory left over from a previous LLVM execution that // happened to have the same process id, get rid of it. - result.destroyDirectory(true); + result.eraseFromDisk(true); // And finally (re-)create the empty directory. - result.createDirectory(false); + result.createDirectoryOnDisk(false); TempDirectory = new Path(result); return *TempDirectory; } @@ -114,7 +151,7 @@ Path::Path(const std::string& unverified_path) Path Path::GetRootDirectory() { Path result; - result.setDirectory("/"); + result.set("C:/"); return result; } @@ -122,32 +159,25 @@ static void getPathList(const char*path, std::vector& Paths) { const char* at = path; const char* delim = strchr(at, ';'); Path tmpPath; - while( delim != 0 ) { + while (delim != 0) { std::string tmp(at, size_t(delim-at)); - if (tmpPath.setDirectory(tmp)) - if (tmpPath.readable()) + if (tmpPath.set(tmp)) + if (tmpPath.canRead()) Paths.push_back(tmpPath); at = delim + 1; delim = strchr(at, ';'); } + if (*at != 0) - if (tmpPath.setDirectory(std::string(at))) - if (tmpPath.readable()) + if (tmpPath.set(std::string(at))) + if (tmpPath.canRead()) Paths.push_back(tmpPath); - } -void +void Path::GetSystemLibraryPaths(std::vector& Paths) { -#ifdef LTDL_SHLIBPATH_VAR - char* env_var = getenv(LTDL_SHLIBPATH_VAR); - if (env_var != 0) { - getPathList(env_var,Paths); - } -#endif - // FIXME: Should this look at LD_LIBRARY_PATH too? - Paths.push_back(sys::Path("C:\\WINDOWS\\SYSTEM32\\")); - Paths.push_back(sys::Path("C:\\WINDOWS\\")); + Paths.push_back(sys::Path("C:/WINDOWS/SYSTEM32")); + Paths.push_back(sys::Path("C:/WINDOWS")); } void @@ -159,8 +189,8 @@ Path::GetBytecodeLibraryPaths(std::vector& Paths) { #ifdef LLVM_LIBDIR { Path tmpPath; - if (tmpPath.setDirectory(LLVM_LIBDIR)) - if (tmpPath.readable()) + if (tmpPath.set(LLVM_LIBDIR)) + if (tmpPath.canRead()) Paths.push_back(tmpPath); } #endif @@ -169,15 +199,17 @@ Path::GetBytecodeLibraryPaths(std::vector& Paths) { Path Path::GetLLVMDefaultConfigDir() { - return Path("/etc/llvm/"); + // TODO: this isn't going to fly on Windows + return Path("/etc/llvm"); } Path Path::GetUserHomeDirectory() { + // TODO: Typical Windows setup doesn't define HOME. const char* home = getenv("HOME"); if (home) { Path result; - if (result.setDirectory(home)) + if (result.set(home)) return result; } return GetRootDirectory(); @@ -186,12 +218,42 @@ Path::GetUserHomeDirectory() { bool Path::isFile() const { - return (isValid() && path[path.length()-1] != '/'); + WIN32_FILE_ATTRIBUTE_DATA fi; + BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi); + if (rc) + return !(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + else if (GetLastError() != ERROR_FILE_NOT_FOUND) { + ThrowError("isFile(): " + std::string(path) + ": Can't get status: "); + } + return false; } bool Path::isDirectory() const { - return (isValid() && path[path.length()-1] == '/'); + WIN32_FILE_ATTRIBUTE_DATA fi; + BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi); + if (rc) + return fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + else if (GetLastError() != ERROR_FILE_NOT_FOUND) + ThrowError("isDirectory(): " + std::string(path) + ": Can't get status: "); + return false; +} + +bool +Path::isHidden() const { + WIN32_FILE_ATTRIBUTE_DATA fi; + BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi); + if (rc) + return fi.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN; + else if (GetLastError() != ERROR_FILE_NOT_FOUND) + ThrowError("isHidden(): " + std::string(path) + ": Can't get status: "); + return false; +} + +bool +Path::isRootDirectory() const { + size_t len = path.size(); + return len > 0 && path[len-1] == '/'; } std::string @@ -203,27 +265,28 @@ Path::getBasename() const { else slash++; - return path.substr(slash, path.rfind('.')); + size_t dot = path.rfind('.'); + if (dot == std::string::npos || dot < slash) + return path.substr(slash); + else + return path.substr(slash, dot - slash); } bool Path::hasMagicNumber(const std::string &Magic) const { - size_t len = Magic.size(); - char *buf = reinterpret_cast(_alloca(len+1)); - std::ifstream f(path.c_str()); - f.read(buf, len); - buf[len] = '\0'; - return Magic == buf; + std::string actualMagic; + if (getMagicNumber(actualMagic, Magic.size())) + return Magic == actualMagic; + return false; } -bool +bool Path::isBytecodeFile() const { - char buffer[ 4]; - buffer[0] = 0; - std::ifstream f(path.c_str()); - f.read(buffer, 4); - if (f.bad()) - ThrowErrno("can't read file signature"); - return 0 == memcmp(buffer,"llvc",4) || 0 == memcmp(buffer,"llvm",4); + if (!isFile()) + return false; + std::string actualMagic; + if (!getMagicNumber(actualMagic, 4)) + return false; + return actualMagic == "llvc" || actualMagic == "llvm"; } bool @@ -233,21 +296,21 @@ Path::exists() const { } bool -Path::readable() const { +Path::canRead() const { // FIXME: take security attributes into account. DWORD attr = GetFileAttributes(path.c_str()); return attr != INVALID_FILE_ATTRIBUTES; } bool -Path::writable() const { +Path::canWrite() const { // FIXME: take security attributes into account. DWORD attr = GetFileAttributes(path.c_str()); return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY); } bool -Path::executable() const { +Path::canExecute() const { // FIXME: take security attributes into account. DWORD attr = GetFileAttributes(path.c_str()); return attr != INVALID_FILE_ATTRIBUTES; @@ -262,15 +325,10 @@ Path::getLast() const { if (pos == std::string::npos) return path; - // If the last character is a slash - if (pos == path.length()-1) { - // Find the second to last slash - size_t pos2 = path.rfind('/', pos-1); - if (pos2 == std::string::npos) - return path.substr(0,pos); - else - return path.substr(pos2+1,pos-pos2-1); - } + // If the last character is a slash, we have a root directory + if (pos == path.length()-1) + return path; + // Return everything after the last slash return path.substr(pos+1); } @@ -279,13 +337,13 @@ void Path::getStatusInfo(StatusInfo& info) const { WIN32_FILE_ATTRIBUTE_DATA fi; if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) - ThrowError(std::string(path) + ": Can't get status: "); + ThrowError("getStatusInfo():" + std::string(path) + ": Can't get status: "); info.fileSize = fi.nFileSizeHigh; - info.fileSize <<= 32; + info.fileSize <<= sizeof(fi.nFileSizeHigh)*8; info.fileSize += fi.nFileSizeLow; - info.mode = 0777; // Not applicable to Windows, so... + info.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777; info.user = 9999; // Not applicable to Windows, so... info.group = 9999; // Not applicable to Windows, so... @@ -293,17 +351,29 @@ Path::getStatusInfo(StatusInfo& info) const { info.modTime.fromWin32Time(ft); info.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - if (info.isDir && path[path.length() - 1] != '/') - path += '/'; - else if (!info.isDir && path[path.length() - 1] == '/') - path.erase(path.length() - 1); } -void Path::makeReadable() { +static bool AddPermissionBits(const std::string& Filename, int bits) { + DWORD attr = GetFileAttributes(Filename.c_str()); + + // If it doesn't exist, we're done. + if (attr == INVALID_FILE_ATTRIBUTES) + return false; + + // The best we can do to interpret Unix permission bits is to use + // the owner writable bit. + if ((attr & FILE_ATTRIBUTE_READONLY) && (bits & 0200)) { + if (!SetFileAttributes(Filename.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) + ThrowError(Filename + ": SetFileAttributes: "); + } + return true; +} + +void Path::makeReadableOnDisk() { // All files are readable on Windows (ignoring security attributes). } -void Path::makeWriteable() { +void Path::makeWriteableOnDisk() { DWORD attr = GetFileAttributes(path.c_str()); // If it doesn't exist, we're done. @@ -316,135 +386,136 @@ void Path::makeWriteable() { } } -void Path::makeExecutable() { +void Path::makeExecutableOnDisk() { // All files are executable on Windows (ignoring security attributes). } bool -Path::setDirectory(const std::string& a_path) { - if (a_path.size() == 0) - return false; - Path save(*this); - path = a_path; - FlipBackSlashes(path); - size_t last = a_path.size() -1; - if (a_path[last] != '/') - path += '/'; - if (!isValid()) { - path = save.path; +Path::getDirectoryContents(std::set& result) const { + if (!isDirectory()) return false; + + result.clear(); + WIN32_FIND_DATA fd; + std::string searchpath = path; + if (path.size() == 0 || searchpath[path.size()-1] == '/') + searchpath += "*"; + else + searchpath += "/*"; + + HANDLE h = FindFirstFile(searchpath.c_str(), &fd); + if (h == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + return true; // not really an error, now is it? + ThrowError(path + ": Can't read directory: "); + } + + do { + if (fd.cFileName[0] == '.') + continue; + Path aPath(path); + aPath.appendComponent(&fd.cFileName[0]); + result.insert(aPath); + } while (FindNextFile(h, &fd)); + + DWORD err = GetLastError(); + FindClose(h); + if (err != ERROR_NO_MORE_FILES) { + SetLastError(err); + ThrowError(path + ": Can't read directory: "); } return true; } bool -Path::setFile(const std::string& a_path) { +Path::set(const std::string& a_path) { if (a_path.size() == 0) return false; - Path save(*this); + std::string save(path); path = a_path; FlipBackSlashes(path); - size_t last = a_path.size() - 1; - while (last > 0 && a_path[last] == '/') - last--; - path.erase(last+1); if (!isValid()) { - path = save.path; + path = save; return false; } return true; } bool -Path::appendDirectory(const std::string& dir) { - if (isFile()) +Path::appendComponent(const std::string& name) { + if (name.empty()) return false; - Path save(*this); - path += dir; - path += "/"; + std::string save(path); + if (!path.empty()) { + size_t last = path.size() - 1; + if (path[last] != '/') + path += '/'; + } + path += name; if (!isValid()) { - path = save.path; + path = save; return false; } return true; } bool -Path::elideDirectory() { - if (isFile()) - return false; +Path::eraseComponent() { size_t slashpos = path.rfind('/',path.size()); - if (slashpos == 0 || slashpos == std::string::npos) - return false; - if (slashpos == path.size() - 1) - slashpos = path.rfind('/',slashpos-1); - if (slashpos == std::string::npos) + if (slashpos == path.size() - 1 || slashpos == std::string::npos) return false; + std::string save(path); path.erase(slashpos); - return true; -} - -bool -Path::appendFile(const std::string& file) { - if (!isDirectory()) - return false; - Path save(*this); - path += file; if (!isValid()) { - path = save.path; + path = save; return false; } return true; } -bool -Path::elideFile() { - if (isDirectory()) - return false; - size_t slashpos = path.rfind('/',path.size()); - if (slashpos == std::string::npos) - return false; - path.erase(slashpos+1); - return true; -} - bool Path::appendSuffix(const std::string& suffix) { - if (isDirectory()) - return false; - Path save(*this); + std::string save(path); path.append("."); path.append(suffix); if (!isValid()) { - path = save.path; + path = save; return false; } return true; } bool -Path::elideSuffix() { - if (isDirectory()) return false; +Path::eraseSuffix() { size_t dotpos = path.rfind('.',path.size()); size_t slashpos = path.rfind('/',path.size()); - if (slashpos != std::string::npos && dotpos != std::string::npos && - dotpos > slashpos) { - path.erase(dotpos, path.size()-dotpos); - return true; + if (dotpos != std::string::npos) { + if (slashpos == std::string::npos || dotpos > slashpos+1) { + std::string save(path); + path.erase(dotpos, path.size()-dotpos); + if (!isValid()) { + path = save; + return false; + } + return true; + } } return false; } - bool -Path::createDirectory( bool create_parents) { - // Make sure we're dealing with a directory - if (!isDirectory()) return false; - +Path::createDirectoryOnDisk(bool create_parents) { // Get a writeable copy of the path name - char *pathname = reinterpret_cast(_alloca(path.length()+1)); - path.copy(pathname,path.length()); - pathname[path.length()] = 0; + size_t len = path.length(); + char *pathname = reinterpret_cast(_alloca(len+2)); + path.copy(pathname, len); + pathname[len] = 0; + + // Make sure it ends with a slash. + if (len == 0 || pathname[len - 1] != '/') { + pathname[len] = '/'; + pathname[++len] = 0; + } // Determine starting point for initial / search. char *next = pathname; @@ -479,7 +550,7 @@ Path::createDirectory( bool create_parents) { } } else { // Drop trailing slash. - pathname[path.size()-1] = 0; + pathname[len-1] = 0; if (!CreateDirectory(pathname, NULL)) { ThrowError(std::string(pathname) + ": Can't create directory: "); } @@ -488,69 +559,99 @@ Path::createDirectory( bool create_parents) { } bool -Path::createFile() { - // Make sure we're dealing with a file - if (!isFile()) return false; - +Path::createFileOnDisk() { // Create the file HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) - ThrowError(std::string(path.c_str()) + ": Can't create file: "); + ThrowError(path + ": Can't create file: "); CloseHandle(h); return true; } bool -Path::destroyDirectory(bool remove_contents) const { - // Make sure we're dealing with a directory - if (!isDirectory()) return false; - - // If it doesn't exist, we're done. - if (!exists()) return true; +Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const { + if (isFile()) { + DWORD attr = GetFileAttributes(path.c_str()); - char *pathname = reinterpret_cast(_alloca(path.length()+1)); - path.copy(pathname,path.length()+1); - int lastchar = path.length() - 1 ; - if (pathname[lastchar] == '/') - pathname[lastchar] = 0; + // If it doesn't exist, we're done. + if (attr == INVALID_FILE_ATTRIBUTES) + return false; - if (remove_contents) { - // Recursively descend the directory to remove its content - // FIXME: The correct way of doing this on Windows isn't pretty... - // but this may work if unix-like utils are present. - std::string cmd("rm -rf "); - cmd += path; - system(cmd.c_str()); - } else { - // Otherwise, try to just remove the one directory - if (!RemoveDirectory(pathname)) - ThrowError(std::string(pathname) + ": Can't destroy directory: "); - } - return true; -} + // Read-only files cannot be deleted on Windows. Must remove the read-only + // attribute first. + if (attr & FILE_ATTRIBUTE_READONLY) { + if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) + return GetError(path + ": Can't destroy file: ", ErrStr); + } -bool -Path::destroyFile() const { - if (!isFile()) return false; + if (!DeleteFile(path.c_str())) + ThrowError(path + ": Can't destroy file: "); + return true; + } else if (isDirectory()) { + // If it doesn't exist, we're done. + if (!exists()) + return false; - DWORD attr = GetFileAttributes(path.c_str()); + char *pathname = reinterpret_cast(_alloca(path.length()+3)); + int lastchar = path.length() - 1 ; + path.copy(pathname, lastchar+1); + + // Make path end with '/*'. + if (pathname[lastchar] != '/') + pathname[++lastchar] = '/'; + pathname[lastchar+1] = '*'; + pathname[lastchar+2] = 0; + + if (remove_contents) { + WIN32_FIND_DATA fd; + HANDLE h = FindFirstFile(pathname, &fd); + + // It's a bad idea to alter the contents of a directory while enumerating + // its contents. So build a list of its contents first, then destroy them. + + if (h != INVALID_HANDLE_VALUE) { + std::vector list; + + do { + if (strcmp(fd.cFileName, ".") == 0) + continue; + if (strcmp(fd.cFileName, "..") == 0) + continue; + + Path aPath(path); + aPath.appendComponent(&fd.cFileName[0]); + list.push_back(aPath); + } while (FindNextFile(h, &fd)); + + DWORD err = GetLastError(); + FindClose(h); + if (err != ERROR_NO_MORE_FILES) { + SetLastError(err); + return GetError(path + ": Can't read directory: ", ErrStr); + } + + for (std::vector::iterator I = list.begin(); I != list.end(); + ++I) { + Path &aPath = *I; + aPath.eraseFromDisk(true); + } + } else { + if (GetLastError() != ERROR_FILE_NOT_FOUND) + return GetError(path + ": Can't read directory: ", ErrStr); + } + } - // If it doesn't exist, we're done. - if (attr == INVALID_FILE_ATTRIBUTES) + pathname[lastchar] = 0; + if (!RemoveDirectory(pathname)) + return GetError(std::string(pathname) + ": Can't destroy directory: ", + ErrStr); + return false; + } else { + // It appears the path doesn't exist. return true; - - // Read-only files cannot be deleted on Windows. Must remove the read-only - // attribute first. - if (attr & FILE_ATTRIBUTE_READONLY) { - if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) - ThrowError(std::string(path.c_str()) + ": Can't destroy file: "); } - - if (!DeleteFile(path.c_str())) - ThrowError(std::string(path.c_str()) + ": Can't destroy file: "); - return true; } bool Path::getMagicNumber(std::string& Magic, unsigned len) const { @@ -558,55 +659,134 @@ bool Path::getMagicNumber(std::string& Magic, unsigned len) const { return false; assert(len < 1024 && "Request for magic string too long"); char* buf = (char*) alloca(1 + len); - std::ofstream ofs(path.c_str(),std::ofstream::in); - if (!ofs.is_open()) + + HANDLE h = CreateFile(path.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (h == INVALID_HANDLE_VALUE) return false; - std::ifstream ifs(path.c_str()); - if (!ifs.is_open()) + + DWORD nRead = 0; + BOOL ret = ReadFile(h, buf, len, &nRead, NULL); + CloseHandle(h); + + if (!ret || nRead != len) return false; - ifs.read(buf, len); - ofs.close(); - ifs.close(); + buf[len] = '\0'; Magic = buf; return true; } -void +bool +Path::renamePathOnDisk(const Path& newName) { + if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING)) + ThrowError("Can't move '" + path + + "' to '" + newName.path + "': "); + return true; +} + +bool +Path::setStatusInfoOnDisk(const StatusInfo& si) const { + // FIXME: should work on directories also. + if (!isFile()) return false; + + HANDLE h = CreateFile(path.c_str(), + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (h == INVALID_HANDLE_VALUE) + return false; + + BY_HANDLE_FILE_INFORMATION bhfi; + if (!GetFileInformationByHandle(h, &bhfi)) { + DWORD err = GetLastError(); + CloseHandle(h); + SetLastError(err); + ThrowError(path + ": GetFileInformationByHandle: "); + } + + FILETIME ft; + (uint64_t&)ft = si.modTime.toWin32Time(); + BOOL ret = SetFileTime(h, NULL, &ft, &ft); + DWORD err = GetLastError(); + CloseHandle(h); + if (!ret) { + SetLastError(err); + ThrowError(path + ": SetFileTime: "); + } + + // Best we can do with Unix permission bits is to interpret the owner + // writable bit. + if (si.mode & 0200) { + if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { + if (!SetFileAttributes(path.c_str(), + bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) + ThrowError(path + ": SetFileAttributes: "); + } + } else { + if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { + if (!SetFileAttributes(path.c_str(), + bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY)) + ThrowError(path + ": SetFileAttributes: "); + } + } + + return true; +} + +void CopyFile(const sys::Path &Dest, const sys::Path &Src) { - if (!::CopyFile(Src.c_str(), Dest.c_str(), false)) - ThrowError("Can't copy '" + Src.toString() + - "' to '" + Dest.toString() + "'"); + // Can't use CopyFile macro defined in Windows.h because it would mess up the + // above line. We use the expansion it would have in a non-UNICODE build. + if (!::CopyFileA(Src.c_str(), Dest.c_str(), false)) + ThrowError("Can't copy '" + Src.toString() + + "' to '" + Dest.toString() + "': "); } -void -Path::makeUnique( bool reuse_current ) { +void +Path::makeUnique(bool reuse_current) { if (reuse_current && !exists()) return; // File doesn't exist already, just use it! - Path dir (*this); - dir.elideFile(); - std::string fname = this->getLast(); + // Reserve space for -XXXXXX at the end. + char *FNBuffer = (char*) alloca(path.size()+8); + unsigned offset = path.size(); + path.copy(FNBuffer, offset); - char newName[MAX_PATH + 1]; - if (!GetTempFileName(dir.c_str(), fname.c_str(), 0, newName)) - ThrowError("Cannot make unique filename for '" + path + "'"); - - path = newName; + // Find a numeric suffix that isn't used by an existing file. Assume there + // won't be more than 1 million files with the same prefix. Probably a safe + // bet. + static unsigned FCounter = 0; + do { + sprintf(FNBuffer+offset, "-%06u", FCounter); + if (++FCounter > 999999) + FCounter = 0; + path = FNBuffer; + } while (exists()); } bool -Path::createTemporaryFile(bool reuse_current) { - // Make sure we're dealing with a file - if (!isFile()) +Path::createTemporaryFileOnDisk(bool reuse_current) { + // Make this into a unique file name + makeUnique(reuse_current); + + // Now go and create it + HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) return false; - // Make this into a unique file name - makeUnique( reuse_current ); + CloseHandle(h); + return true; } } } - -// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab -