LLVM API Documentation

Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

Win32/Path.cpp

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 <fstream>
00024 #include <malloc.h>
00025 
00026 static void FlipBackSlashes(std::string& s) {
00027   for (size_t i = 0; i < s.size(); i++)
00028     if (s[i] == '\\')
00029       s[i] = '/';
00030 }
00031 
00032 namespace llvm {
00033 namespace sys {
00034 
00035 bool
00036 Path::isValid() const {
00037   if (path.empty())
00038     return false;
00039 
00040   // If there is a colon, it must be the second character, preceded by a letter
00041   // and followed by something.
00042   size_t len = path.size();
00043   size_t pos = path.rfind(':',len);
00044   if (pos != std::string::npos) {
00045     if (pos != 1 || !isalpha(path[0]) || len < 3)
00046       return false;
00047   }
00048 
00049   // Check for illegal characters.
00050   if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
00051                          "\013\014\015\016\017\020\021\022\023\024\025\026"
00052                          "\027\030\031\032\033\034\035\036\037")
00053       != std::string::npos)
00054     return false;
00055 
00056   // A file or directory name may not end in a period.
00057   if (path[len-1] == '.')
00058     return false;
00059   if (len >= 2 && path[len-2] == '.' && path[len-1] == '/')
00060     return false;
00061 
00062   // A file or directory name may not end in a space.
00063   if (path[len-1] == ' ')
00064     return false;
00065   if (len >= 2 && path[len-2] == ' ' && path[len-1] == '/')
00066     return false;
00067 
00068   return true;
00069 }
00070 
00071 static Path *TempDirectory = NULL;
00072 
00073 Path
00074 Path::GetTemporaryDirectory() {
00075   if (TempDirectory)
00076     return *TempDirectory;
00077 
00078   char pathname[MAX_PATH];
00079   if (!GetTempPath(MAX_PATH, pathname))
00080     throw std::string("Can't determine temporary directory");
00081 
00082   Path result;
00083   result.setDirectory(pathname);
00084 
00085   // Append a subdirectory passed on our process id so multiple LLVMs don't
00086   // step on each other's toes.
00087   sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
00088   result.appendDirectory(pathname);
00089 
00090   // If there's a directory left over from a previous LLVM execution that
00091   // happened to have the same process id, get rid of it.
00092   result.destroyDirectory(true);
00093 
00094   // And finally (re-)create the empty directory.
00095   result.createDirectory(false);
00096   TempDirectory = new Path(result);
00097   return *TempDirectory;
00098 }
00099 
00100 Path::Path(std::string unverified_path)
00101   : path(unverified_path)
00102 {
00103   FlipBackSlashes(path);
00104   if (unverified_path.empty())
00105     return;
00106   if (this->isValid())
00107     return;
00108   // oops, not valid.
00109   path.clear();
00110   throw std::string(unverified_path + ": path is not valid");
00111 }
00112 
00113 // FIXME: the following set of functions don't map to Windows very well.
00114 Path
00115 Path::GetRootDirectory() {
00116   Path result;
00117   result.setDirectory("/");
00118   return result;
00119 }
00120 
00121 std::string
00122 Path::GetDLLSuffix() {
00123   return "dll";
00124 }
00125 
00126 static inline bool IsLibrary(Path& path, const std::string& basename) {
00127   if (path.appendFile(std::string("lib") + basename)) {
00128     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
00129       return true;
00130     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
00131       return true;
00132     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
00133       return true;
00134     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
00135       return true;
00136   } else if (path.elideFile() && path.appendFile(basename)) {
00137     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
00138       return true;
00139     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
00140       return true;
00141     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
00142       return true;
00143     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
00144       return true;
00145   }
00146   path.clear();
00147   return false;
00148 }
00149 
00150 Path 
00151 Path::GetLibraryPath(const std::string& basename, 
00152                      const std::vector<std::string>& LibPaths) {
00153   Path result;
00154 
00155   // Try the paths provided
00156   for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
00157        E = LibPaths.end(); I != E; ++I ) {
00158     if (result.setDirectory(*I) && IsLibrary(result,basename))
00159       return result;
00160   }
00161 
00162   // Try the LLVM lib directory in the LLVM install area
00163   //if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
00164   //  return result;
00165 
00166   // Try /usr/lib
00167   if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
00168     return result;
00169 
00170   // Try /lib
00171   if (result.setDirectory("/lib/") && IsLibrary(result,basename))
00172     return result;
00173 
00174   // Can't find it, give up and return invalid path.
00175   result.clear();
00176   return result;
00177 }
00178 
00179 Path
00180 Path::GetSystemLibraryPath1() {
00181   return Path("/lib/");
00182 }
00183 
00184 Path
00185 Path::GetSystemLibraryPath2() {
00186   return Path("/usr/lib/");
00187 }
00188 
00189 Path
00190 Path::GetLLVMDefaultConfigDir() {
00191   return Path("/etc/llvm/");
00192 }
00193 
00194 Path
00195 Path::GetLLVMConfigDir() {
00196   return GetLLVMDefaultConfigDir();
00197 }
00198 
00199 Path
00200 Path::GetUserHomeDirectory() {
00201   const char* home = getenv("HOME");
00202   if (home) {
00203     Path result;
00204     if (result.setDirectory(home))
00205       return result;
00206   }
00207   return GetRootDirectory();
00208 }
00209 // FIXME: the above set of functions don't map to Windows very well.
00210 
00211 bool
00212 Path::isFile() const {
00213   return (isValid() && path[path.length()-1] != '/');
00214 }
00215 
00216 bool
00217 Path::isDirectory() const {
00218   return (isValid() && path[path.length()-1] == '/');
00219 }
00220 
00221 std::string
00222 Path::getBasename() const {
00223   // Find the last slash
00224   size_t slash = path.rfind('/');
00225   if (slash == std::string::npos)
00226     slash = 0;
00227   else
00228     slash++;
00229 
00230   return path.substr(slash, path.rfind('.'));
00231 }
00232 
00233 bool Path::hasMagicNumber(const std::string &Magic) const {
00234   size_t len = Magic.size();
00235   char *buf = reinterpret_cast<char *>(_alloca(len+1));
00236   std::ifstream f(path.c_str());
00237   f.read(buf, len);
00238   buf[len] = '\0';
00239   return Magic == buf;
00240 }
00241 
00242 bool 
00243 Path::isBytecodeFile() const {
00244   char buffer[ 4];
00245   buffer[0] = 0;
00246   std::ifstream f(path.c_str());
00247   f.read(buffer, 4);
00248   if (f.bad())
00249     ThrowErrno("can't read file signature");
00250   return 0 == memcmp(buffer,"llvc",4) || 0 == memcmp(buffer,"llvm",4);
00251 }
00252 
00253 bool
00254 Path::isArchive() const {
00255   if (readable()) {
00256     return hasMagicNumber("!<arch>\012");
00257   }
00258   return false;
00259 }
00260 
00261 bool
00262 Path::exists() const {
00263   DWORD attr = GetFileAttributes(path.c_str());
00264   return attr != INVALID_FILE_ATTRIBUTES;
00265 }
00266 
00267 bool
00268 Path::readable() const {
00269   // FIXME: take security attributes into account.
00270   DWORD attr = GetFileAttributes(path.c_str());
00271   return attr != INVALID_FILE_ATTRIBUTES;
00272 }
00273 
00274 bool
00275 Path::writable() const {
00276   // FIXME: take security attributes into account.
00277   DWORD attr = GetFileAttributes(path.c_str());
00278   return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
00279 }
00280 
00281 bool
00282 Path::executable() const {
00283   // FIXME: take security attributes into account.
00284   DWORD attr = GetFileAttributes(path.c_str());
00285   return attr != INVALID_FILE_ATTRIBUTES;
00286 }
00287 
00288 std::string
00289 Path::getLast() const {
00290   // Find the last slash
00291   size_t pos = path.rfind('/');
00292 
00293   // Handle the corner cases
00294   if (pos == std::string::npos)
00295     return path;
00296 
00297   // If the last character is a slash
00298   if (pos == path.length()-1) {
00299     // Find the second to last slash
00300     size_t pos2 = path.rfind('/', pos-1);
00301     if (pos2 == std::string::npos)
00302       return path.substr(0,pos);
00303     else
00304       return path.substr(pos2+1,pos-pos2-1);
00305   }
00306   // Return everything after the last slash
00307   return path.substr(pos+1);
00308 }
00309 
00310 bool
00311 Path::setDirectory(const std::string& a_path) {
00312   if (a_path.size() == 0)
00313     return false;
00314   Path save(*this);
00315   path = a_path;
00316   FlipBackSlashes(path);
00317   size_t last = a_path.size() -1;
00318   if (last != 0 && a_path[last] != '/')
00319     path += '/';
00320   if (!isValid()) {
00321     path = save.path;
00322     return false;
00323   }
00324   return true;
00325 }
00326 
00327 bool
00328 Path::setFile(const std::string& a_path) {
00329   if (a_path.size() == 0)
00330     return false;
00331   Path save(*this);
00332   path = a_path;
00333   FlipBackSlashes(path);
00334   size_t last = a_path.size() - 1;
00335   while (last > 0 && a_path[last] == '/')
00336     last--;
00337   path.erase(last+1);
00338   if (!isValid()) {
00339     path = save.path;
00340     return false;
00341   }
00342   return true;
00343 }
00344 
00345 bool
00346 Path::appendDirectory(const std::string& dir) {
00347   if (isFile())
00348     return false;
00349   Path save(*this);
00350   path += dir;
00351   path += "/";
00352   if (!isValid()) {
00353     path = save.path;
00354     return false;
00355   }
00356   return true;
00357 }
00358 
00359 bool
00360 Path::elideDirectory() {
00361   if (isFile())
00362     return false;
00363   size_t slashpos = path.rfind('/',path.size());
00364   if (slashpos == 0 || slashpos == std::string::npos)
00365     return false;
00366   if (slashpos == path.size() - 1)
00367     slashpos = path.rfind('/',slashpos-1);
00368   if (slashpos == std::string::npos)
00369     return false;
00370   path.erase(slashpos);
00371   return true;
00372 }
00373 
00374 bool
00375 Path::appendFile(const std::string& file) {
00376   if (!isDirectory())
00377     return false;
00378   Path save(*this);
00379   path += file;
00380   if (!isValid()) {
00381     path = save.path;
00382     return false;
00383   }
00384   return true;
00385 }
00386 
00387 bool
00388 Path::elideFile() {
00389   if (isDirectory())
00390     return false;
00391   size_t slashpos = path.rfind('/',path.size());
00392   if (slashpos == std::string::npos)
00393     return false;
00394   path.erase(slashpos+1);
00395   return true;
00396 }
00397 
00398 bool
00399 Path::appendSuffix(const std::string& suffix) {
00400   if (isDirectory())
00401     return false;
00402   Path save(*this);
00403   path.append(".");
00404   path.append(suffix);
00405   if (!isValid()) {
00406     path = save.path;
00407     return false;
00408   }
00409   return true;
00410 }
00411 
00412 bool
00413 Path::elideSuffix() {
00414   if (isDirectory()) return false;
00415   size_t dotpos = path.rfind('.',path.size());
00416   size_t slashpos = path.rfind('/',path.size());
00417   if (slashpos != std::string::npos && dotpos != std::string::npos &&
00418       dotpos > slashpos) {
00419     path.erase(dotpos, path.size()-dotpos);
00420     return true;
00421   }
00422   return false;
00423 }
00424 
00425 
00426 bool
00427 Path::createDirectory( bool create_parents) {
00428   // Make sure we're dealing with a directory
00429   if (!isDirectory()) return false;
00430 
00431   // Get a writeable copy of the path name
00432   char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
00433   path.copy(pathname,path.length());
00434   pathname[path.length()] = 0;
00435 
00436   // Determine starting point for initial / search.
00437   char *next = pathname;
00438   if (pathname[0] == '/' && pathname[1] == '/') {
00439     // Skip host name.
00440     next = strchr(pathname+2, '/');
00441     if (next == NULL)
00442       throw std::string(pathname) + ": badly formed remote directory";
00443     // Skip share name.
00444     next = strchr(next+1, '/');
00445     if (next == NULL)
00446       throw std::string(pathname) + ": badly formed remote directory";
00447     next++;
00448     if (*next == 0)
00449       throw std::string(pathname) + ": badly formed remote directory";
00450   } else {
00451     if (pathname[1] == ':')
00452       next += 2;    // skip drive letter
00453     if (*next == '/')
00454       next++;       // skip root directory
00455   }
00456 
00457   // If we're supposed to create intermediate directories
00458   if (create_parents) {
00459     // Loop through the directory components until we're done
00460     while (*next) {
00461       next = strchr(next, '/');
00462       *next = 0;
00463       if (!CreateDirectory(pathname, NULL))
00464           ThrowError(std::string(pathname) + ": Can't create directory: ");
00465       *next++ = '/';
00466     }
00467   } else {
00468     // Drop trailing slash.
00469     pathname[path.size()-1] = 0;
00470     if (!CreateDirectory(pathname, NULL)) {
00471       ThrowError(std::string(pathname) + ": Can't create directory: ");
00472     }
00473   }
00474   return true;
00475 }
00476 
00477 bool
00478 Path::createFile() {
00479   // Make sure we're dealing with a file
00480   if (!isFile()) return false;
00481 
00482   // Create the file
00483   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
00484                         FILE_ATTRIBUTE_NORMAL, NULL);
00485   if (h == INVALID_HANDLE_VALUE)
00486     ThrowError(std::string(path.c_str()) + ": Can't create file: ");
00487 
00488   CloseHandle(h);
00489   return true;
00490 }
00491 
00492 bool
00493 Path::destroyDirectory(bool remove_contents) {
00494   // Make sure we're dealing with a directory
00495   if (!isDirectory()) return false;
00496 
00497   // If it doesn't exist, we're done.
00498   if (!exists()) return true;
00499 
00500   char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
00501   path.copy(pathname,path.length()+1);
00502   int lastchar = path.length() - 1 ;
00503   if (pathname[lastchar] == '/')
00504     pathname[lastchar] = 0;
00505 
00506   if (remove_contents) {
00507     // Recursively descend the directory to remove its content
00508     // FIXME: The correct way of doing this on Windows isn't pretty...
00509     // but this may work if unix-like utils are present.
00510     std::string cmd("rm -rf ");
00511     cmd += path;
00512     system(cmd.c_str());
00513   } else {
00514     // Otherwise, try to just remove the one directory
00515     if (!RemoveDirectory(pathname))
00516       ThrowError(std::string(pathname) + ": Can't destroy directory: ");
00517   }
00518   return true;
00519 }
00520 
00521 bool
00522 Path::destroyFile() {
00523   if (!isFile()) return false;
00524 
00525   DWORD attr = GetFileAttributes(path.c_str());
00526 
00527   // If it doesn't exist, we're done.
00528   if (attr == INVALID_FILE_ATTRIBUTES)
00529     return true;
00530 
00531   // Read-only files cannot be deleted on Windows.  Must remove the read-only
00532   // attribute first.
00533   if (attr & FILE_ATTRIBUTE_READONLY) {
00534     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
00535       ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
00536   }
00537 
00538   if (!DeleteFile(path.c_str()))
00539     ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
00540   return true;
00541 }
00542 
00543 }
00544 }
00545 
00546 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab
00547