LLVM API Documentation

Win32/Path.inc

Go to the documentation of this file.
00001 //===- llvm/System/Linux/Path.cpp - Linux Path Implementation ---*- C++ -*-===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file was developed by Reid Spencer and is distributed under the
00006 // University of Illinois Open Source License. See LICENSE.TXT for details.
00007 //
00008 // Modified by Henrik Bach to comply with at least MinGW.
00009 // Ported to Win32 by Jeff Cohen.
00010 //
00011 //===----------------------------------------------------------------------===//
00012 //
00013 // This file provides the Win32 specific implementation of the Path class.
00014 //
00015 //===----------------------------------------------------------------------===//
00016 
00017 //===----------------------------------------------------------------------===//
00018 //=== WARNING: Implementation here must contain only generic Win32 code that
00019 //===          is guaranteed to work on *all* Win32 variants.
00020 //===----------------------------------------------------------------------===//
00021 
00022 #include "Win32.h"
00023 #include <malloc.h>
00024 
00025 // We need to undo a macro defined in Windows.h, otherwise we won't compile:
00026 #undef CopyFile
00027 
00028 // Windows happily accepts either forward or backward slashes, though any path
00029 // returned by a Win32 API will have backward slashes.  As LLVM code basically
00030 // assumes forward slashes are used, backward slashs are converted where they
00031 // can be introduced into a path.
00032 //
00033 // Another invariant is that a path ends with a slash if and only if the path
00034 // is a root directory.  Any other use of a trailing slash is stripped.  Unlike
00035 // in Unix, Windows has a rather complicated notion of a root path and this
00036 // invariant helps simply the code.
00037 
00038 static void FlipBackSlashes(std::string& s) {
00039   for (size_t i = 0; i < s.size(); i++)
00040     if (s[i] == '\\')
00041       s[i] = '/';
00042 }
00043 
00044 namespace llvm {
00045 namespace sys {
00046 
00047 bool
00048 Path::isValid() const {
00049   if (path.empty())
00050     return false;
00051 
00052   // If there is a colon, it must be the second character, preceded by a letter
00053   // and followed by something.
00054   size_t len = path.size();
00055   size_t pos = path.rfind(':',len);
00056   size_t rootslash = 0;
00057   if (pos != std::string::npos) {
00058     if (pos != 1 || !isalpha(path[0]) || len < 3)
00059       return false;
00060       rootslash = 2;
00061   }
00062 
00063   // Look for a UNC path, and if found adjust our notion of the root slash.
00064   if (len > 3 && path[0] == '/' && path[1] == '/') {
00065     rootslash = path.find('/', 2);
00066     if (rootslash == std::string::npos)
00067       rootslash = 0;
00068   }
00069 
00070   // Check for illegal characters.
00071   if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
00072                          "\013\014\015\016\017\020\021\022\023\024\025\026"
00073                          "\027\030\031\032\033\034\035\036\037")
00074       != std::string::npos)
00075     return false;
00076 
00077   // Remove trailing slash, unless it's a root slash.
00078   if (len > rootslash+1 && path[len-1] == '/')
00079     path.erase(--len);
00080 
00081   // Check each component for legality.
00082   for (pos = 0; pos < len; ++pos) {
00083     // A component may not end in a space.
00084     if (path[pos] == ' ') {
00085       if (path[pos+1] == '/' || path[pos+1] == '\0')
00086         return false;
00087     }
00088 
00089     // A component may not end in a period.
00090     if (path[pos] == '.') {
00091       if (path[pos+1] == '/' || path[pos+1] == '\0') {
00092         // Unless it is the pseudo-directory "."...
00093         if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':')
00094           return true;
00095         // or "..".
00096         if (pos > 0 && path[pos-1] == '.') {
00097           if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':')
00098             return true;
00099         }
00100         return false;
00101       }
00102     }
00103   }
00104 
00105   return true;
00106 }
00107 
00108 static Path *TempDirectory = NULL;
00109 
00110 Path
00111 Path::GetTemporaryDirectory() {
00112   if (TempDirectory)
00113     return *TempDirectory;
00114 
00115   char pathname[MAX_PATH];
00116   if (!GetTempPath(MAX_PATH, pathname))
00117     throw std::string("Can't determine temporary directory");
00118 
00119   Path result;
00120   result.set(pathname);
00121 
00122   // Append a subdirectory passed on our process id so multiple LLVMs don't
00123   // step on each other's toes.
00124   sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId()));
00125   result.appendComponent(pathname);
00126 
00127   // If there's a directory left over from a previous LLVM execution that
00128   // happened to have the same process id, get rid of it.
00129   result.eraseFromDisk(true);
00130 
00131   // And finally (re-)create the empty directory.
00132   result.createDirectoryOnDisk(false);
00133   TempDirectory = new Path(result);
00134   return *TempDirectory;
00135 }
00136 
00137 Path::Path(const std::string& unverified_path)
00138   : path(unverified_path)
00139 {
00140   FlipBackSlashes(path);
00141   if (unverified_path.empty())
00142     return;
00143   if (this->isValid())
00144     return;
00145   // oops, not valid.
00146   path.clear();
00147   throw std::string(unverified_path + ": path is not valid");
00148 }
00149 
00150 // FIXME: the following set of functions don't map to Windows very well.
00151 Path
00152 Path::GetRootDirectory() {
00153   Path result;
00154   result.set("C:/");
00155   return result;
00156 }
00157 
00158 static void getPathList(const char*path, std::vector<sys::Path>& Paths) {
00159   const char* at = path;
00160   const char* delim = strchr(at, ';');
00161   Path tmpPath;
00162   while (delim != 0) {
00163     std::string tmp(at, size_t(delim-at));
00164     if (tmpPath.set(tmp))
00165       if (tmpPath.canRead())
00166         Paths.push_back(tmpPath);
00167     at = delim + 1;
00168     delim = strchr(at, ';');
00169   }
00170 
00171   if (*at != 0)
00172     if (tmpPath.set(std::string(at)))
00173       if (tmpPath.canRead())
00174         Paths.push_back(tmpPath);
00175 }
00176 
00177 void
00178 Path::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) {
00179   Paths.push_back(sys::Path("C:/WINDOWS/SYSTEM32"));
00180   Paths.push_back(sys::Path("C:/WINDOWS"));
00181 }
00182 
00183 void
00184 Path::GetBytecodeLibraryPaths(std::vector<sys::Path>& Paths) {
00185   char * env_var = getenv("LLVM_LIB_SEARCH_PATH");
00186   if (env_var != 0) {
00187     getPathList(env_var,Paths);
00188   }
00189 #ifdef LLVM_LIBDIR
00190   {
00191     Path tmpPath;
00192     if (tmpPath.set(LLVM_LIBDIR))
00193       if (tmpPath.canRead())
00194         Paths.push_back(tmpPath);
00195   }
00196 #endif
00197   GetSystemLibraryPaths(Paths);
00198 }
00199 
00200 Path
00201 Path::GetLLVMDefaultConfigDir() {
00202   // TODO: this isn't going to fly on Windows
00203   return Path("/etc/llvm");
00204 }
00205 
00206 Path
00207 Path::GetUserHomeDirectory() {
00208   // TODO: Typical Windows setup doesn't define HOME.
00209   const char* home = getenv("HOME");
00210   if (home) {
00211     Path result;
00212     if (result.set(home))
00213       return result;
00214   }
00215   return GetRootDirectory();
00216 }
00217 // FIXME: the above set of functions don't map to Windows very well.
00218 
00219 bool
00220 Path::isFile() const {
00221   WIN32_FILE_ATTRIBUTE_DATA fi;
00222   BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi);
00223   if (rc)
00224     return !(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
00225   else if (GetLastError() != ERROR_FILE_NOT_FOUND) {
00226     ThrowError("isFile(): " + std::string(path) + ": Can't get status: ");
00227   }
00228   return false;
00229 }
00230 
00231 bool
00232 Path::isDirectory() const {
00233   WIN32_FILE_ATTRIBUTE_DATA fi;
00234   BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi);
00235   if (rc)
00236     return fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
00237   else if (GetLastError() != ERROR_FILE_NOT_FOUND)
00238     ThrowError("isDirectory(): " + std::string(path) + ": Can't get status: ");
00239   return false;
00240 }
00241 
00242 bool
00243 Path::isHidden() const {
00244   WIN32_FILE_ATTRIBUTE_DATA fi;
00245   BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi);
00246   if (rc)
00247     return fi.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN;
00248   else if (GetLastError() != ERROR_FILE_NOT_FOUND)
00249     ThrowError("isHidden(): " + std::string(path) + ": Can't get status: ");
00250   return false;
00251 }
00252 
00253 bool
00254 Path::isRootDirectory() const {
00255   size_t len = path.size();
00256   return len > 0 && path[len-1] == '/';
00257 }
00258 
00259 std::string
00260 Path::getBasename() const {
00261   // Find the last slash
00262   size_t slash = path.rfind('/');
00263   if (slash == std::string::npos)
00264     slash = 0;
00265   else
00266     slash++;
00267 
00268   size_t dot = path.rfind('.');
00269   if (dot == std::string::npos || dot < slash)
00270     return path.substr(slash);
00271   else
00272     return path.substr(slash, dot - slash);
00273 }
00274 
00275 bool Path::hasMagicNumber(const std::string &Magic) const {
00276   std::string actualMagic;
00277   if (getMagicNumber(actualMagic, Magic.size()))
00278     return Magic == actualMagic;
00279   return false;
00280 }
00281 
00282 bool
00283 Path::isBytecodeFile() const {
00284   if (!isFile())
00285     return false;
00286   std::string actualMagic;
00287   if (!getMagicNumber(actualMagic, 4))
00288     return false;
00289   return actualMagic == "llvc" || actualMagic == "llvm";
00290 }
00291 
00292 bool
00293 Path::exists() const {
00294   DWORD attr = GetFileAttributes(path.c_str());
00295   return attr != INVALID_FILE_ATTRIBUTES;
00296 }
00297 
00298 bool
00299 Path::canRead() const {
00300   // FIXME: take security attributes into account.
00301   DWORD attr = GetFileAttributes(path.c_str());
00302   return attr != INVALID_FILE_ATTRIBUTES;
00303 }
00304 
00305 bool
00306 Path::canWrite() const {
00307   // FIXME: take security attributes into account.
00308   DWORD attr = GetFileAttributes(path.c_str());
00309   return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
00310 }
00311 
00312 bool
00313 Path::canExecute() const {
00314   // FIXME: take security attributes into account.
00315   DWORD attr = GetFileAttributes(path.c_str());
00316   return attr != INVALID_FILE_ATTRIBUTES;
00317 }
00318 
00319 std::string
00320 Path::getLast() const {
00321   // Find the last slash
00322   size_t pos = path.rfind('/');
00323 
00324   // Handle the corner cases
00325   if (pos == std::string::npos)
00326     return path;
00327 
00328   // If the last character is a slash, we have a root directory
00329   if (pos == path.length()-1)
00330     return path;
00331 
00332   // Return everything after the last slash
00333   return path.substr(pos+1);
00334 }
00335 
00336 void
00337 Path::getStatusInfo(StatusInfo& info) const {
00338   WIN32_FILE_ATTRIBUTE_DATA fi;
00339   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
00340     ThrowError("getStatusInfo():" + std::string(path) + ": Can't get status: ");
00341 
00342   info.fileSize = fi.nFileSizeHigh;
00343   info.fileSize <<= sizeof(fi.nFileSizeHigh)*8;
00344   info.fileSize += fi.nFileSizeLow;
00345 
00346   info.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
00347   info.user = 9999;    // Not applicable to Windows, so...
00348   info.group = 9999;   // Not applicable to Windows, so...
00349 
00350   __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime);
00351   info.modTime.fromWin32Time(ft);
00352 
00353   info.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
00354 }
00355 
00356 static bool AddPermissionBits(const std::string& Filename, int bits) {
00357   DWORD attr = GetFileAttributes(Filename.c_str());
00358 
00359   // If it doesn't exist, we're done.
00360   if (attr == INVALID_FILE_ATTRIBUTES)
00361     return false;
00362 
00363   // The best we can do to interpret Unix permission bits is to use
00364   // the owner writable bit.
00365   if ((attr & FILE_ATTRIBUTE_READONLY) && (bits & 0200)) {
00366     if (!SetFileAttributes(Filename.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
00367       ThrowError(Filename + ": SetFileAttributes: ");
00368   }
00369   return true;
00370 }
00371 
00372 void Path::makeReadableOnDisk() {
00373   // All files are readable on Windows (ignoring security attributes).
00374 }
00375 
00376 void Path::makeWriteableOnDisk() {
00377   DWORD attr = GetFileAttributes(path.c_str());
00378 
00379   // If it doesn't exist, we're done.
00380   if (attr == INVALID_FILE_ATTRIBUTES)
00381     return;
00382 
00383   if (attr & FILE_ATTRIBUTE_READONLY) {
00384     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
00385       ThrowError(std::string(path) + ": Can't make file writable: ");
00386   }
00387 }
00388 
00389 void Path::makeExecutableOnDisk() {
00390   // All files are executable on Windows (ignoring security attributes).
00391 }
00392 
00393 bool
00394 Path::getDirectoryContents(std::set<Path>& result) const {
00395   if (!isDirectory())
00396     return false;
00397 
00398   result.clear();
00399   WIN32_FIND_DATA fd;
00400   std::string searchpath = path;
00401   if (path.size() == 0 || searchpath[path.size()-1] == '/')
00402     searchpath += "*";
00403   else
00404     searchpath += "/*";
00405 
00406   HANDLE h = FindFirstFile(searchpath.c_str(), &fd);
00407   if (h == INVALID_HANDLE_VALUE) {
00408     if (GetLastError() == ERROR_FILE_NOT_FOUND)
00409       return true; // not really an error, now is it?
00410     ThrowError(path + ": Can't read directory: ");
00411   }
00412 
00413   do {
00414     if (fd.cFileName[0] == '.')
00415       continue;
00416     Path aPath(path);
00417     aPath.appendComponent(&fd.cFileName[0]);
00418     result.insert(aPath);
00419   } while (FindNextFile(h, &fd));
00420 
00421   DWORD err = GetLastError();
00422   FindClose(h);
00423   if (err != ERROR_NO_MORE_FILES) {
00424     SetLastError(err);
00425     ThrowError(path + ": Can't read directory: ");
00426   }
00427   return true;
00428 }
00429 
00430 bool
00431 Path::set(const std::string& a_path) {
00432   if (a_path.size() == 0)
00433     return false;
00434   std::string save(path);
00435   path = a_path;
00436   FlipBackSlashes(path);
00437   if (!isValid()) {
00438     path = save;
00439     return false;
00440   }
00441   return true;
00442 }
00443 
00444 bool
00445 Path::appendComponent(const std::string& name) {
00446   if (name.empty())
00447     return false;
00448   std::string save(path);
00449   if (!path.empty()) {
00450     size_t last = path.size() - 1;
00451     if (path[last] != '/')
00452       path += '/';
00453   }
00454   path += name;
00455   if (!isValid()) {
00456     path = save;
00457     return false;
00458   }
00459   return true;
00460 }
00461 
00462 bool
00463 Path::eraseComponent() {
00464   size_t slashpos = path.rfind('/',path.size());
00465   if (slashpos == path.size() - 1 || slashpos == std::string::npos)
00466     return false;
00467   std::string save(path);
00468   path.erase(slashpos);
00469   if (!isValid()) {
00470     path = save;
00471     return false;
00472   }
00473   return true;
00474 }
00475 
00476 bool
00477 Path::appendSuffix(const std::string& suffix) {
00478   std::string save(path);
00479   path.append(".");
00480   path.append(suffix);
00481   if (!isValid()) {
00482     path = save;
00483     return false;
00484   }
00485   return true;
00486 }
00487 
00488 bool
00489 Path::eraseSuffix() {
00490   size_t dotpos = path.rfind('.',path.size());
00491   size_t slashpos = path.rfind('/',path.size());
00492   if (dotpos != std::string::npos) {
00493     if (slashpos == std::string::npos || dotpos > slashpos+1) {
00494       std::string save(path);
00495       path.erase(dotpos, path.size()-dotpos);
00496       if (!isValid()) {
00497         path = save;
00498         return false;
00499       }
00500       return true;
00501     }
00502   }
00503   return false;
00504 }
00505 
00506 bool
00507 Path::createDirectoryOnDisk(bool create_parents) {
00508   // Get a writeable copy of the path name
00509   size_t len = path.length();
00510   char *pathname = reinterpret_cast<char *>(_alloca(len+2));
00511   path.copy(pathname, len);
00512   pathname[len] = 0;
00513 
00514   // Make sure it ends with a slash.
00515   if (len == 0 || pathname[len - 1] != '/') {
00516     pathname[len] = '/';
00517     pathname[++len] = 0;
00518   }
00519 
00520   // Determine starting point for initial / search.
00521   char *next = pathname;
00522   if (pathname[0] == '/' && pathname[1] == '/') {
00523     // Skip host name.
00524     next = strchr(pathname+2, '/');
00525     if (next == NULL)
00526       throw std::string(pathname) + ": badly formed remote directory";
00527     // Skip share name.
00528     next = strchr(next+1, '/');
00529     if (next == NULL)
00530       throw std::string(pathname) + ": badly formed remote directory";
00531     next++;
00532     if (*next == 0)
00533       throw std::string(pathname) + ": badly formed remote directory";
00534   } else {
00535     if (pathname[1] == ':')
00536       next += 2;    // skip drive letter
00537     if (*next == '/')
00538       next++;       // skip root directory
00539   }
00540 
00541   // If we're supposed to create intermediate directories
00542   if (create_parents) {
00543     // Loop through the directory components until we're done
00544     while (*next) {
00545       next = strchr(next, '/');
00546       *next = 0;
00547       if (!CreateDirectory(pathname, NULL))
00548           ThrowError(std::string(pathname) + ": Can't create directory: ");
00549       *next++ = '/';
00550     }
00551   } else {
00552     // Drop trailing slash.
00553     pathname[len-1] = 0;
00554     if (!CreateDirectory(pathname, NULL)) {
00555       ThrowError(std::string(pathname) + ": Can't create directory: ");
00556     }
00557   }
00558   return true;
00559 }
00560 
00561 bool
00562 Path::createFileOnDisk() {
00563   // Create the file
00564   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
00565                         FILE_ATTRIBUTE_NORMAL, NULL);
00566   if (h == INVALID_HANDLE_VALUE)
00567     ThrowError(path + ": Can't create file: ");
00568 
00569   CloseHandle(h);
00570   return true;
00571 }
00572 
00573 bool
00574 Path::eraseFromDisk(bool remove_contents) const {
00575   if (isFile()) {
00576     DWORD attr = GetFileAttributes(path.c_str());
00577 
00578     // If it doesn't exist, we're done.
00579     if (attr == INVALID_FILE_ATTRIBUTES)
00580       return true;
00581 
00582     // Read-only files cannot be deleted on Windows.  Must remove the read-only
00583     // attribute first.
00584     if (attr & FILE_ATTRIBUTE_READONLY) {
00585       if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
00586         ThrowError(path + ": Can't destroy file: ");
00587     }
00588 
00589     if (!DeleteFile(path.c_str()))
00590       ThrowError(path + ": Can't destroy file: ");
00591     return true;
00592   } else if (isDirectory()) {
00593     // If it doesn't exist, we're done.
00594     if (!exists())
00595       return true;
00596 
00597     char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3));
00598     int lastchar = path.length() - 1 ;
00599     path.copy(pathname, lastchar+1);
00600 
00601     // Make path end with '/*'.
00602     if (pathname[lastchar] != '/')
00603       pathname[++lastchar] = '/';
00604     pathname[lastchar+1] = '*';
00605     pathname[lastchar+2] = 0;
00606 
00607     if (remove_contents) {
00608       WIN32_FIND_DATA fd;
00609       HANDLE h = FindFirstFile(pathname, &fd);
00610 
00611       // It's a bad idea to alter the contents of a directory while enumerating
00612       // its contents. So build a list of its contents first, then destroy them.
00613 
00614       if (h != INVALID_HANDLE_VALUE) {
00615         std::vector<Path> list;
00616 
00617         do {
00618           if (strcmp(fd.cFileName, ".") == 0)
00619             continue;
00620           if (strcmp(fd.cFileName, "..") == 0)
00621             continue;
00622 
00623           Path aPath(path);
00624           aPath.appendComponent(&fd.cFileName[0]);
00625           list.push_back(aPath);
00626         } while (FindNextFile(h, &fd));
00627 
00628         DWORD err = GetLastError();
00629         FindClose(h);
00630         if (err != ERROR_NO_MORE_FILES) {
00631           SetLastError(err);
00632           ThrowError(path + ": Can't read directory: ");
00633         }
00634 
00635         for (std::vector<Path>::iterator I = list.begin(); I != list.end();
00636              ++I) {
00637           Path &aPath = *I;
00638           aPath.eraseFromDisk(true);
00639         }
00640       } else {
00641         if (GetLastError() != ERROR_FILE_NOT_FOUND)
00642           ThrowError(path + ": Can't read directory: ");
00643       }
00644     }
00645 
00646     pathname[lastchar] = 0;
00647     if (!RemoveDirectory(pathname))
00648       ThrowError(std::string(pathname) + ": Can't destroy directory: ");
00649     return true;
00650   } else {
00651     // It appears the path doesn't exist.
00652     return false;
00653   }
00654 }
00655 
00656 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
00657   if (!isFile())
00658     return false;
00659   assert(len < 1024 && "Request for magic string too long");
00660   char* buf = (char*) alloca(1 + len);
00661 
00662   HANDLE h = CreateFile(path.c_str(),
00663                         GENERIC_READ,
00664                         FILE_SHARE_READ,
00665                         NULL,
00666                         OPEN_EXISTING,
00667                         FILE_ATTRIBUTE_NORMAL,
00668                         NULL);
00669   if (h == INVALID_HANDLE_VALUE)
00670     return false;
00671 
00672   DWORD nRead = 0;
00673   BOOL ret = ReadFile(h, buf, len, &nRead, NULL);
00674   CloseHandle(h);
00675 
00676   if (!ret || nRead != len)
00677     return false;
00678 
00679   buf[len] = '\0';
00680   Magic = buf;
00681   return true;
00682 }
00683 
00684 bool
00685 Path::renamePathOnDisk(const Path& newName) {
00686   if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING))
00687     ThrowError("Can't move '" + path +
00688                "' to '" + newName.path + "': ");
00689   return true;
00690 }
00691 
00692 bool
00693 Path::setStatusInfoOnDisk(const StatusInfo& si) const {
00694   // FIXME: should work on directories also.
00695   if (!isFile()) return false;
00696 
00697   HANDLE h = CreateFile(path.c_str(),
00698                         FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
00699                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
00700                         NULL,
00701                         OPEN_EXISTING,
00702                         FILE_ATTRIBUTE_NORMAL,
00703                         NULL);
00704   if (h == INVALID_HANDLE_VALUE)
00705     return false;
00706 
00707   BY_HANDLE_FILE_INFORMATION bhfi;
00708   if (!GetFileInformationByHandle(h, &bhfi)) {
00709     DWORD err = GetLastError();
00710     CloseHandle(h);
00711     SetLastError(err);
00712     ThrowError(path + ": GetFileInformationByHandle: ");
00713   }
00714 
00715   FILETIME ft;
00716   (uint64_t&)ft = si.modTime.toWin32Time();
00717   BOOL ret = SetFileTime(h, NULL, &ft, &ft);
00718   DWORD err = GetLastError();
00719   CloseHandle(h);
00720   if (!ret) {
00721     SetLastError(err);
00722     ThrowError(path + ": SetFileTime: ");
00723   }
00724 
00725   // Best we can do with Unix permission bits is to interpret the owner
00726   // writable bit.
00727   if (si.mode & 0200) {
00728     if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
00729       if (!SetFileAttributes(path.c_str(),
00730               bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
00731         ThrowError(path + ": SetFileAttributes: ");
00732     }
00733   } else {
00734     if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
00735       if (!SetFileAttributes(path.c_str(),
00736               bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY))
00737         ThrowError(path + ": SetFileAttributes: ");
00738     }
00739   }
00740 
00741   return true;
00742 }
00743 
00744 void
00745 CopyFile(const sys::Path &Dest, const sys::Path &Src) {
00746   // Can't use CopyFile macro defined in Windows.h because it would mess up the
00747   // above line.  We use the expansion it would have in a non-UNICODE build.
00748   if (!::CopyFileA(Src.c_str(), Dest.c_str(), false))
00749     ThrowError("Can't copy '" + Src.toString() +
00750                "' to '" + Dest.toString() + "': ");
00751 }
00752 
00753 void
00754 Path::makeUnique(bool reuse_current) {
00755   if (reuse_current && !exists())
00756     return; // File doesn't exist already, just use it!
00757 
00758   // Reserve space for -XXXXXX at the end.
00759   char *FNBuffer = (char*) alloca(path.size()+8);
00760   unsigned offset = path.size();
00761   path.copy(FNBuffer, offset);
00762 
00763   // Find a numeric suffix that isn't used by an existing file.  Assume there
00764   // won't be more than 1 million files with the same prefix.  Probably a safe
00765   // bet.
00766   static unsigned FCounter = 0;
00767   do {
00768     sprintf(FNBuffer+offset, "-%06u", FCounter);
00769     if (++FCounter > 999999)
00770       FCounter = 0;
00771     path = FNBuffer;
00772   } while (exists());
00773 }
00774 
00775 bool
00776 Path::createTemporaryFileOnDisk(bool reuse_current) {
00777   // Make this into a unique file name
00778   makeUnique(reuse_current);
00779 
00780   // Now go and create it
00781   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
00782                         FILE_ATTRIBUTE_NORMAL, NULL);
00783   if (h == INVALID_HANDLE_VALUE)
00784     return false;
00785 
00786   CloseHandle(h);
00787   return true;
00788 }
00789 
00790 }
00791 }
00792 
00793