LLVM API Documentation

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", 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_NOT_FOUND)
00226     ThrowError(std::string(path) + ": Can't get status: ");
00227   return false;
00228 }
00229 
00230 bool
00231 Path::isDirectory() const {
00232   WIN32_FILE_ATTRIBUTE_DATA fi;
00233   BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi);
00234   if (rc)
00235     return fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
00236   else if (GetLastError() != ERROR_NOT_FOUND)
00237     ThrowError(std::string(path) + ": Can't get status: ");
00238   return false;
00239 }
00240 
00241 bool
00242 Path::isHidden() const {
00243   WIN32_FILE_ATTRIBUTE_DATA fi;
00244   BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi);
00245   if (rc)
00246     return fi.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN;
00247   else if (GetLastError() != ERROR_NOT_FOUND)
00248     ThrowError(std::string(path) + ": Can't get status: ");
00249   return false;
00250 }
00251 
00252 bool
00253 Path::isRootDirectory() const {
00254   size_t len = path.size();
00255   return len > 0 && path[len-1] == '/';
00256 }
00257 
00258 std::string
00259 Path::getBasename() const {
00260   // Find the last slash
00261   size_t slash = path.rfind('/');
00262   if (slash == std::string::npos)
00263     slash = 0;
00264   else
00265     slash++;
00266 
00267   size_t dot = path.rfind('.');
00268   if (dot == std::string::npos || dot < slash)
00269     return path.substr(slash);
00270   else
00271     return path.substr(slash, dot - slash);
00272 }
00273 
00274 bool Path::hasMagicNumber(const std::string &Magic) const {
00275   std::string actualMagic;
00276   if (getMagicNumber(actualMagic, Magic.size()))
00277     return Magic == actualMagic;
00278   return false;
00279 }
00280 
00281 bool
00282 Path::isBytecodeFile() const {
00283   if (!isFile())
00284     return false;
00285   std::string actualMagic;
00286   if (!getMagicNumber(actualMagic, 4))
00287     return false;
00288   return actualMagic == "llvc" || actualMagic == "llvm";
00289 }
00290 
00291 bool
00292 Path::exists() const {
00293   DWORD attr = GetFileAttributes(path.c_str());
00294   return attr != INVALID_FILE_ATTRIBUTES;
00295 }
00296 
00297 bool
00298 Path::canRead() const {
00299   // FIXME: take security attributes into account.
00300   DWORD attr = GetFileAttributes(path.c_str());
00301   return attr != INVALID_FILE_ATTRIBUTES;
00302 }
00303 
00304 bool
00305 Path::canWrite() const {
00306   // FIXME: take security attributes into account.
00307   DWORD attr = GetFileAttributes(path.c_str());
00308   return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
00309 }
00310 
00311 bool
00312 Path::canExecute() const {
00313   // FIXME: take security attributes into account.
00314   DWORD attr = GetFileAttributes(path.c_str());
00315   return attr != INVALID_FILE_ATTRIBUTES;
00316 }
00317 
00318 std::string
00319 Path::getLast() const {
00320   // Find the last slash
00321   size_t pos = path.rfind('/');
00322 
00323   // Handle the corner cases
00324   if (pos == std::string::npos)
00325     return path;
00326 
00327   // If the last character is a slash, we have a root directory
00328   if (pos == path.length()-1)
00329     return path;
00330 
00331   // Return everything after the last slash
00332   return path.substr(pos+1);
00333 }
00334 
00335 void
00336 Path::getStatusInfo(StatusInfo& info) const {
00337   WIN32_FILE_ATTRIBUTE_DATA fi;
00338   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
00339     ThrowError(std::string(path) + ": Can't get status: ");
00340 
00341   info.fileSize = fi.nFileSizeHigh;
00342   info.fileSize <<= 32;
00343   info.fileSize += fi.nFileSizeLow;
00344 
00345   info.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
00346   info.user = 9999;    // Not applicable to Windows, so...
00347   info.group = 9999;   // Not applicable to Windows, so...
00348 
00349   __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime);
00350   info.modTime.fromWin32Time(ft);
00351 
00352   info.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
00353 }
00354 
00355 static bool AddPermissionBits(const std::string& Filename, int bits) {
00356   DWORD attr = GetFileAttributes(Filename.c_str());
00357 
00358   // If it doesn't exist, we're done.
00359   if (attr == INVALID_FILE_ATTRIBUTES)
00360     return false;
00361 
00362   // The best we can do to interpret Unix permission bits is to use
00363   // the owner writable bit.
00364   if ((attr & FILE_ATTRIBUTE_READONLY) && (bits & 0200)) {
00365     if (!SetFileAttributes(Filename.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
00366       ThrowError(Filename + ": SetFileAttributes: ");
00367   }
00368   return true;
00369 }
00370 
00371 void Path::makeReadableOnDisk() {
00372   // All files are readable on Windows (ignoring security attributes).
00373 }
00374 
00375 void Path::makeWriteableOnDisk() {
00376   DWORD attr = GetFileAttributes(path.c_str());
00377 
00378   // If it doesn't exist, we're done.
00379   if (attr == INVALID_FILE_ATTRIBUTES)
00380     return;
00381 
00382   if (attr & FILE_ATTRIBUTE_READONLY) {
00383     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
00384       ThrowError(std::string(path) + ": Can't make file writable: ");
00385   }
00386 }
00387 
00388 void Path::makeExecutableOnDisk() {
00389   // All files are executable on Windows (ignoring security attributes).
00390 }
00391 
00392 bool
00393 Path::getDirectoryContents(std::set<Path>& result) const {
00394   if (!isDirectory())
00395     return false;
00396 
00397   result.clear();
00398   WIN32_FIND_DATA fd;
00399   std::string searchpath = path;
00400   if (path.size() == 0 || searchpath[path.size()-1] == '/')
00401     searchpath += "*";
00402   else
00403     searchpath += "/*";
00404 
00405   HANDLE h = FindFirstFile(searchpath.c_str(), &fd);
00406   if (h == INVALID_HANDLE_VALUE) {
00407     if (GetLastError() == ERROR_FILE_NOT_FOUND)
00408       return true; // not really an error, now is it?
00409     ThrowError(path + ": Can't read directory: ");
00410   }
00411 
00412   do {
00413     if (fd.cFileName[0] == '.')
00414       continue;
00415     Path aPath(path);
00416     aPath.appendComponent(&fd.cFileName[0]);
00417     result.insert(aPath);
00418   } while (FindNextFile(h, &fd));
00419 
00420   DWORD err = GetLastError();
00421   FindClose(h);
00422   if (err != ERROR_NO_MORE_FILES) {
00423     SetLastError(err);
00424     ThrowError(path + ": Can't read directory: ");
00425   }
00426   return true;
00427 }
00428 
00429 bool
00430 Path::set(const std::string& a_path) {
00431   if (a_path.size() == 0)
00432     return false;
00433   std::string save(path);
00434   path = a_path;
00435   FlipBackSlashes(path);
00436   if (!isValid()) {
00437     path = save;
00438     return false;
00439   }
00440   return true;
00441 }
00442 
00443 bool
00444 Path::appendComponent(const std::string& name) {
00445   if (name.empty())
00446     return false;
00447   std::string save(path);
00448   if (!path.empty()) {
00449     size_t last = path.size() - 1;
00450     if (path[last] != '/')
00451       path += '/';
00452   }
00453   path += name;
00454   if (!isValid()) {
00455     path = save;
00456     return false;
00457   }
00458   return true;
00459 }
00460 
00461 bool
00462 Path::eraseComponent() {
00463   size_t slashpos = path.rfind('/',path.size());
00464   if (slashpos == path.size() - 1 || slashpos == std::string::npos)
00465     return false;
00466   std::string save(path);
00467   path.erase(slashpos);
00468   if (!isValid()) {
00469     path = save;
00470     return false;
00471   }
00472   return true;
00473 }
00474 
00475 bool
00476 Path::appendSuffix(const std::string& suffix) {
00477   std::string save(path);
00478   path.append(".");
00479   path.append(suffix);
00480   if (!isValid()) {
00481     path = save;
00482     return false;
00483   }
00484   return true;
00485 }
00486 
00487 bool
00488 Path::eraseSuffix() {
00489   size_t dotpos = path.rfind('.',path.size());
00490   size_t slashpos = path.rfind('/',path.size());
00491   if (dotpos != std::string::npos) {
00492     if (slashpos == std::string::npos || dotpos > slashpos+1) {
00493       std::string save(path);
00494       path.erase(dotpos, path.size()-dotpos);
00495       if (!isValid()) {
00496         path = save;
00497         return false;
00498       }
00499       return true;
00500     }
00501   }
00502   return false;
00503 }
00504 
00505 bool
00506 Path::createDirectoryOnDisk(bool create_parents) {
00507   // Get a writeable copy of the path name
00508   size_t len = path.length();
00509   char *pathname = reinterpret_cast<char *>(_alloca(len+2));
00510   path.copy(pathname, len);
00511   pathname[len] = 0;
00512 
00513   // Make sure it ends with a slash.
00514   if (len == 0 || pathname[len - 1] != '/') {
00515     pathname[len] = '/';
00516     pathname[++len] = 0;
00517   }
00518 
00519   // Determine starting point for initial / search.
00520   char *next = pathname;
00521   if (pathname[0] == '/' && pathname[1] == '/') {
00522     // Skip host name.
00523     next = strchr(pathname+2, '/');
00524     if (next == NULL)
00525       throw std::string(pathname) + ": badly formed remote directory";
00526     // Skip share name.
00527     next = strchr(next+1, '/');
00528     if (next == NULL)
00529       throw std::string(pathname) + ": badly formed remote directory";
00530     next++;
00531     if (*next == 0)
00532       throw std::string(pathname) + ": badly formed remote directory";
00533   } else {
00534     if (pathname[1] == ':')
00535       next += 2;    // skip drive letter
00536     if (*next == '/')
00537       next++;       // skip root directory
00538   }
00539 
00540   // If we're supposed to create intermediate directories
00541   if (create_parents) {
00542     // Loop through the directory components until we're done
00543     while (*next) {
00544       next = strchr(next, '/');
00545       *next = 0;
00546       if (!CreateDirectory(pathname, NULL))
00547           ThrowError(std::string(pathname) + ": Can't create directory: ");
00548       *next++ = '/';
00549     }
00550   } else {
00551     // Drop trailing slash.
00552     pathname[len-1] = 0;
00553     if (!CreateDirectory(pathname, NULL)) {
00554       ThrowError(std::string(pathname) + ": Can't create directory: ");
00555     }
00556   }
00557   return true;
00558 }
00559 
00560 bool
00561 Path::createFileOnDisk() {
00562   // Create the file
00563   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
00564                         FILE_ATTRIBUTE_NORMAL, NULL);
00565   if (h == INVALID_HANDLE_VALUE)
00566     ThrowError(path + ": Can't create file: ");
00567 
00568   CloseHandle(h);
00569   return true;
00570 }
00571 
00572 bool
00573 Path::eraseFromDisk(bool remove_contents) const {
00574   if (isFile()) {
00575     DWORD attr = GetFileAttributes(path.c_str());
00576 
00577     // If it doesn't exist, we're done.
00578     if (attr == INVALID_FILE_ATTRIBUTES)
00579       return true;
00580 
00581     // Read-only files cannot be deleted on Windows.  Must remove the read-only
00582     // attribute first.
00583     if (attr & FILE_ATTRIBUTE_READONLY) {
00584       if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
00585         ThrowError(path + ": Can't destroy file: ");
00586     }
00587 
00588     if (!DeleteFile(path.c_str()))
00589       ThrowError(path + ": Can't destroy file: ");
00590     return true;
00591   } else if (isDirectory()) {
00592     // If it doesn't exist, we're done.
00593     if (!exists())
00594       return true;
00595 
00596     char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3));
00597     int lastchar = path.length() - 1 ;
00598     path.copy(pathname, lastchar+1);
00599 
00600     // Make path end with '/*'.
00601     if (pathname[lastchar] != '/')
00602       pathname[++lastchar] = '/';
00603     pathname[lastchar+1] = '*';
00604     pathname[lastchar+2] = 0;
00605 
00606     if (remove_contents) {
00607       WIN32_FIND_DATA fd;
00608       HANDLE h = FindFirstFile(pathname, &fd);
00609 
00610       // It's a bad idea to alter the contents of a directory while enumerating
00611       // its contents. So build a list of its contents first, then destroy them.
00612 
00613       if (h != INVALID_HANDLE_VALUE) {
00614         std::vector<Path> list;
00615 
00616         do {
00617           if (strcmp(fd.cFileName, ".") == 0)
00618             continue;
00619           if (strcmp(fd.cFileName, "..") == 0)
00620             continue;
00621 
00622           Path aPath(path);
00623           aPath.appendComponent(&fd.cFileName[0]);
00624           list.push_back(aPath);
00625         } while (FindNextFile(h, &fd));
00626 
00627         DWORD err = GetLastError();
00628         FindClose(h);
00629         if (err != ERROR_NO_MORE_FILES) {
00630           SetLastError(err);
00631           ThrowError(path + ": Can't read directory: ");
00632         }
00633 
00634         for (std::vector<Path>::iterator I = list.begin(); I != list.end();
00635              ++I) {
00636           Path &aPath = *I;
00637           aPath.eraseFromDisk(true);
00638         }
00639       } else {
00640         if (GetLastError() != ERROR_FILE_NOT_FOUND)
00641           ThrowError(path + ": Can't read directory: ");
00642       }
00643     }
00644 
00645     pathname[lastchar] = 0;
00646     if (!RemoveDirectory(pathname))
00647       ThrowError(std::string(pathname) + ": Can't destroy directory: ");
00648     return true;
00649   } else {
00650     // It appears the path doesn't exist.
00651     return false;
00652   }
00653 }
00654 
00655 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
00656   if (!isFile())
00657     return false;
00658   assert(len < 1024 && "Request for magic string too long");
00659   char* buf = (char*) alloca(1 + len);
00660 
00661   HANDLE h = CreateFile(path.c_str(),
00662                         GENERIC_READ,
00663                         FILE_SHARE_READ,
00664                         NULL,
00665                         OPEN_EXISTING,
00666                         FILE_ATTRIBUTE_NORMAL,
00667                         NULL);
00668   if (h == INVALID_HANDLE_VALUE)
00669     return false;
00670 
00671   DWORD nRead = 0;
00672   BOOL ret = ReadFile(h, buf, len, &nRead, NULL);
00673   CloseHandle(h);
00674 
00675   if (!ret || nRead != len)
00676     return false;
00677 
00678   buf[len] = '\0';
00679   Magic = buf;
00680   return true;
00681 }
00682 
00683 bool
00684 Path::renamePathOnDisk(const Path& newName) {
00685   if (!MoveFile(path.c_str(), newName.c_str()))
00686     ThrowError("Can't move '" + path +
00687                "' to '" + newName.path + "': ");
00688   return true;
00689 }
00690 
00691 bool
00692 Path::setStatusInfoOnDisk(const StatusInfo& si) const {
00693   // FIXME: should work on directories also.
00694   if (!isFile()) return false;
00695 
00696   HANDLE h = CreateFile(path.c_str(),
00697                         FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
00698                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
00699                         NULL,
00700                         OPEN_EXISTING,
00701                         FILE_ATTRIBUTE_NORMAL,
00702                         NULL);
00703   if (h == INVALID_HANDLE_VALUE)
00704     return false;
00705 
00706   BY_HANDLE_FILE_INFORMATION bhfi;
00707   if (!GetFileInformationByHandle(h, &bhfi)) {
00708     DWORD err = GetLastError();
00709     CloseHandle(h);
00710     SetLastError(err);
00711     ThrowError(path + ": GetFileInformationByHandle: ");
00712   }
00713 
00714   FILETIME ft;
00715   (uint64_t&)ft = si.modTime.toWin32Time();
00716   BOOL ret = SetFileTime(h, NULL, &ft, &ft);
00717   DWORD err = GetLastError();
00718   CloseHandle(h);
00719   if (!ret) {
00720     SetLastError(err);
00721     ThrowError(path + ": SetFileTime: ");
00722   }
00723 
00724   // Best we can do with Unix permission bits is to interpret the owner
00725   // writable bit.
00726   if (si.mode & 0200) {
00727     if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
00728       if (!SetFileAttributes(path.c_str(),
00729               bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
00730         ThrowError(path + ": SetFileAttributes: ");
00731     }
00732   } else {
00733     if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
00734       if (!SetFileAttributes(path.c_str(),
00735               bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY))
00736         ThrowError(path + ": SetFileAttributes: ");
00737     }
00738   }
00739 
00740   return true;
00741 }
00742 
00743 void
00744 sys::CopyFile(const sys::Path &Dest, const sys::Path &Src) {
00745   // Can't use CopyFile macro defined in Windows.h because it would mess up the
00746   // above line.  We use the expansion it would have in a non-UNICODE build.
00747   if (!::CopyFileA(Src.c_str(), Dest.c_str(), false))
00748     ThrowError("Can't copy '" + Src.toString() +
00749                "' to '" + Dest.toString() + "': ");
00750 }
00751 
00752 void
00753 Path::makeUnique(bool reuse_current) {
00754   if (reuse_current && !exists())
00755     return; // File doesn't exist already, just use it!
00756 
00757   // Reserve space for -XXXXXX at the end.
00758   char *FNBuffer = (char*) alloca(path.size()+8);
00759   unsigned offset = path.size();
00760   path.copy(FNBuffer, offset);
00761 
00762   // Find a numeric suffix that isn't used by an existing file.  Assume there
00763   // won't be more than 1 million files with the same prefix.  Probably a safe
00764   // bet.
00765   static unsigned FCounter = 0;
00766   do {
00767     sprintf(FNBuffer+offset, "-%06u", FCounter);
00768     if (++FCounter > 999999)
00769       FCounter = 0;
00770     path = FNBuffer;
00771   } while (exists());
00772 }
00773 
00774 bool
00775 Path::createTemporaryFileOnDisk(bool reuse_current) {
00776   // Make this into a unique file name
00777   makeUnique(reuse_current);
00778 
00779   // Now go and create it
00780   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
00781                         FILE_ATTRIBUTE_NORMAL, NULL);
00782   if (h == INVALID_HANDLE_VALUE)
00783     return false;
00784 
00785   CloseHandle(h);
00786   return true;
00787 }
00788 
00789 }
00790 }
00791 
00792