LLVM API Documentation

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

Unix/Path.cpp

Go to the documentation of this file.
00001 //===- llvm/System/Unix/Path.cpp - Unix 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 //===----------------------------------------------------------------------===//
00009 //
00010 // This file implements the Unix specific portion of the Path class.
00011 //
00012 //===----------------------------------------------------------------------===//
00013 
00014 //===----------------------------------------------------------------------===//
00015 //=== WARNING: Implementation here must contain only generic UNIX code that
00016 //===          is guaranteed to work on *all* UNIX variants.
00017 //===----------------------------------------------------------------------===//
00018 
00019 #include <llvm/Config/config.h>
00020 #include <llvm/Config/alloca.h>
00021 #include "Unix.h"
00022 #include <sys/stat.h>
00023 #include <fcntl.h>
00024 #include <fstream>
00025 #include <utime.h>
00026 #include <dirent.h>
00027 
00028 namespace llvm {
00029 using namespace sys;
00030 
00031 Path::Path(std::string unverified_path) 
00032   : path(unverified_path)
00033 {
00034   if (unverified_path.empty())
00035     return;
00036   if (this->isValid()) 
00037     return;
00038   // oops, not valid.
00039   path.clear();
00040   ThrowErrno(unverified_path + ": path is not valid");
00041 }
00042 
00043 Path
00044 Path::GetRootDirectory() {
00045   Path result;
00046   result.setDirectory("/");
00047   return result;
00048 }
00049 
00050 static inline bool IsLibrary(Path& path, const std::string& basename) {
00051   if (path.appendFile(std::string("lib") + basename)) {
00052     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
00053       return true;
00054     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
00055       return true;
00056     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
00057       return true;
00058     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
00059       return true;
00060   } else if (path.elideFile() && path.appendFile(basename)) {
00061     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
00062       return true;
00063     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
00064       return true;
00065     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
00066       return true;
00067     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
00068       return true;
00069   }
00070   path.clear();
00071   return false;
00072 }
00073 
00074 Path 
00075 Path::GetLibraryPath(const std::string& basename, 
00076                      const std::vector<std::string>& LibPaths) {
00077   Path result;
00078 
00079   // Try the paths provided
00080   for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
00081        E = LibPaths.end(); I != E; ++I ) {
00082     if (result.setDirectory(*I) && IsLibrary(result,basename))
00083       return result;
00084   }
00085 
00086   // Try the LLVM lib directory in the LLVM install area
00087   if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
00088     return result;
00089 
00090   // Try /usr/lib
00091   if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
00092     return result;
00093 
00094   // Try /lib
00095   if (result.setDirectory("/lib/") && IsLibrary(result,basename))
00096     return result;
00097 
00098   // Can't find it, give up and return invalid path.
00099   result.clear();
00100   return result;
00101 }
00102 
00103 Path 
00104 Path::GetSystemLibraryPath1() {
00105   return Path("/lib/");
00106 }
00107 
00108 Path 
00109 Path::GetSystemLibraryPath2() {
00110   return Path("/usr/lib/");
00111 }
00112 
00113 Path 
00114 Path::GetLLVMDefaultConfigDir() {
00115   return Path("/etc/llvm/");
00116 }
00117 
00118 Path 
00119 Path::GetLLVMConfigDir() {
00120   Path result;
00121   if (result.setDirectory(LLVM_ETCDIR))
00122     return result;
00123   return GetLLVMDefaultConfigDir();
00124 }
00125 
00126 Path
00127 Path::GetUserHomeDirectory() {
00128   const char* home = getenv("HOME");
00129   if (home) {
00130     Path result;
00131     if (result.setDirectory(home))
00132       return result;
00133   }
00134   return GetRootDirectory();
00135 }
00136 
00137 bool
00138 Path::isFile() const {
00139   return (isValid() && path[path.length()-1] != '/');
00140 }
00141 
00142 bool
00143 Path::isDirectory() const {
00144   return (isValid() && path[path.length()-1] == '/');
00145 }
00146 
00147 std::string
00148 Path::getBasename() const {
00149   // Find the last slash
00150   size_t slash = path.rfind('/');
00151   if (slash == std::string::npos)
00152     slash = 0;
00153   else
00154     slash++;
00155 
00156   return path.substr(slash, path.rfind('.'));
00157 }
00158 
00159 bool Path::hasMagicNumber(const std::string &Magic) const {
00160   size_t len = Magic.size();
00161   assert(len < 1024 && "Request for magic string too long");
00162   char* buf = (char*) alloca(1 + len);
00163   int fd = ::open(path.c_str(),O_RDONLY);
00164   if (fd < 0)
00165     return false;
00166   if (0 != ::read(fd, buf, len))
00167     return false;
00168   close(fd);
00169   buf[len] = '\0';
00170   return Magic == buf;
00171 }
00172 
00173 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
00174   if (!isFile())
00175     return false;
00176   assert(len < 1024 && "Request for magic string too long");
00177   char* buf = (char*) alloca(1 + len);
00178   int fd = ::open(path.c_str(),O_RDONLY);
00179   if (fd < 0)
00180     return false;
00181   ssize_t bytes_read = ::read(fd, buf, len);
00182   ::close(fd);
00183   if (ssize_t(len) != bytes_read) {
00184     Magic.clear();
00185     return false;
00186   }
00187   Magic.assign(buf,len);
00188   return true;
00189 }
00190 
00191 bool 
00192 Path::isBytecodeFile() const {
00193   char buffer[ 4];
00194   buffer[0] = 0;
00195   std::ifstream f(path.c_str());
00196   f.read(buffer, 4);
00197   if (f.bad())
00198     ThrowErrno("can't read file signature");
00199 
00200   return (buffer[0] == 'l' && buffer[1] == 'l' && buffer[2] == 'v' &&
00201       (buffer[3] == 'c' || buffer[3] == 'm'));
00202 }
00203 
00204 bool
00205 Path::isArchive() const {
00206   if (readable()) {
00207     return hasMagicNumber("!<arch>\012");
00208   }
00209   return false;
00210 }
00211 
00212 bool
00213 Path::exists() const {
00214   return 0 == access(path.c_str(), F_OK );
00215 }
00216 
00217 bool
00218 Path::readable() const {
00219   return 0 == access(path.c_str(), F_OK | R_OK );
00220 }
00221 
00222 bool
00223 Path::writable() const {
00224   return 0 == access(path.c_str(), F_OK | W_OK );
00225 }
00226 
00227 bool
00228 Path::executable() const {
00229   return 0 == access(path.c_str(), R_OK | X_OK );
00230 }
00231 
00232 std::string 
00233 Path::getLast() const {
00234   // Find the last slash
00235   size_t pos = path.rfind('/');
00236 
00237   // Handle the corner cases
00238   if (pos == std::string::npos)
00239     return path;
00240 
00241   // If the last character is a slash
00242   if (pos == path.length()-1) {
00243     // Find the second to last slash
00244     size_t pos2 = path.rfind('/', pos-1);
00245     if (pos2 == std::string::npos)
00246       return path.substr(0,pos);
00247     else
00248       return path.substr(pos2+1,pos-pos2-1);
00249   }
00250   // Return everything after the last slash
00251   return path.substr(pos+1);
00252 }
00253 
00254 void
00255 Path::getStatusInfo(StatusInfo& info) const {
00256   struct stat buf;
00257   if (0 != stat(path.c_str(), &buf)) {
00258     ThrowErrno(std::string("Can't get status: ")+path);
00259   }
00260   info.fileSize = buf.st_size;
00261   info.modTime.fromEpochTime(buf.st_mtime);
00262   info.mode = buf.st_mode;
00263   info.user = buf.st_uid;
00264   info.group = buf.st_gid;
00265   info.isDir = S_ISDIR(buf.st_mode);
00266   if (info.isDir && path[path.length()-1] != '/')
00267     path += '/';
00268 }
00269 
00270 bool
00271 Path::getDirectoryContents(std::set<Path>& result) const {
00272   if (!isDirectory())
00273     return false;
00274   DIR* direntries = ::opendir(path.c_str());
00275   if (direntries == 0)
00276     ThrowErrno(path + ": can't open directory");
00277 
00278   result.clear();
00279   struct dirent* de = ::readdir(direntries);
00280   while (de != 0) {
00281     if (de->d_name[0] != '.') {
00282       Path aPath(path + (const char*)de->d_name);
00283       struct stat buf;
00284       if (0 != stat(aPath.path.c_str(), &buf))
00285         ThrowErrno(aPath.path + ": can't get status");
00286       if (S_ISDIR(buf.st_mode))
00287         aPath.path += "/";
00288       result.insert(aPath);
00289     }
00290     de = ::readdir(direntries);
00291   }
00292   
00293   closedir(direntries);
00294   return true;
00295 }
00296 
00297 bool
00298 Path::setDirectory(const std::string& a_path) {
00299   if (a_path.size() == 0)
00300     return false;
00301   Path save(*this);
00302   path = a_path;
00303   size_t last = a_path.size() -1;
00304   if (last != 0 && a_path[last] != '/')
00305     path += '/';
00306   if (!isValid()) {
00307     path = save.path;
00308     return false;
00309   }
00310   return true;
00311 }
00312 
00313 bool
00314 Path::setFile(const std::string& a_path) {
00315   if (a_path.size() == 0)
00316     return false;
00317   Path save(*this);
00318   path = a_path;
00319   size_t last = a_path.size() - 1;
00320   while (last > 0 && a_path[last] == '/')
00321     last--;
00322   path.erase(last+1);
00323   if (!isValid()) {
00324     path = save.path;
00325     return false;
00326   }
00327   return true;
00328 }
00329 
00330 bool
00331 Path::appendDirectory(const std::string& dir) {
00332   if (isFile()) 
00333     return false;
00334   Path save(*this);
00335   path += dir;
00336   path += "/";
00337   if (!isValid()) {
00338     path = save.path;
00339     return false;
00340   }
00341   return true;
00342 }
00343 
00344 bool
00345 Path::elideDirectory() {
00346   if (isFile()) 
00347     return false;
00348   size_t slashpos = path.rfind('/',path.size());
00349   if (slashpos == 0 || slashpos == std::string::npos)
00350     return false;
00351   if (slashpos == path.size() - 1)
00352     slashpos = path.rfind('/',slashpos-1);
00353   if (slashpos == std::string::npos)
00354     return false;
00355   path.erase(slashpos);
00356   return true;
00357 }
00358 
00359 bool
00360 Path::appendFile(const std::string& file) {
00361   if (!isDirectory()) 
00362     return false;
00363   Path save(*this);
00364   path += file;
00365   if (!isValid()) {
00366     path = save.path;
00367     return false;
00368   }
00369   return true;
00370 }
00371 
00372 bool
00373 Path::elideFile() {
00374   if (isDirectory()) 
00375     return false;
00376   size_t slashpos = path.rfind('/',path.size());
00377   if (slashpos == std::string::npos)
00378     return false;
00379   path.erase(slashpos+1);
00380   return true;
00381 }
00382 
00383 bool
00384 Path::appendSuffix(const std::string& suffix) {
00385   if (isDirectory()) 
00386     return false;
00387   Path save(*this);
00388   path.append(".");
00389   path.append(suffix);
00390   if (!isValid()) {
00391     path = save.path;
00392     return false;
00393   }
00394   return true;
00395 }
00396 
00397 bool 
00398 Path::elideSuffix() {
00399   if (isDirectory()) return false;
00400   size_t dotpos = path.rfind('.',path.size());
00401   size_t slashpos = path.rfind('/',path.size());
00402   if (slashpos != std::string::npos && dotpos != std::string::npos &&
00403       dotpos > slashpos) {
00404     path.erase(dotpos, path.size()-dotpos);
00405     return true;
00406   }
00407   return false;
00408 }
00409 
00410 
00411 bool
00412 Path::createDirectory( bool create_parents) {
00413   // Make sure we're dealing with a directory
00414   if (!isDirectory()) return false;
00415 
00416   // Get a writeable copy of the path name
00417   char pathname[MAXPATHLEN];
00418   path.copy(pathname,MAXPATHLEN);
00419 
00420   // Null-terminate the last component
00421   int lastchar = path.length() - 1 ; 
00422   if (pathname[lastchar] == '/') 
00423     pathname[lastchar] = 0;
00424   else 
00425     pathname[lastchar+1] = 0;
00426 
00427   // If we're supposed to create intermediate directories
00428   if ( create_parents ) {
00429     // Find the end of the initial name component
00430     char * next = strchr(pathname,'/');
00431     if ( pathname[0] == '/') 
00432       next = strchr(&pathname[1],'/');
00433 
00434     // Loop through the directory components until we're done 
00435     while ( next != 0 ) {
00436       *next = 0;
00437       if (0 != access(pathname, F_OK | R_OK | W_OK))
00438         if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
00439           ThrowErrno(std::string(pathname) + ": Can't create directory");
00440       char* save = next;
00441       next = strchr(next+1,'/');
00442       *save = '/';
00443     }
00444   } 
00445 
00446   if (0 != access(pathname, F_OK | R_OK))
00447     if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
00448       ThrowErrno(std::string(pathname) + ": Can't create directory");
00449   return true;
00450 }
00451 
00452 bool
00453 Path::createFile() {
00454   // Make sure we're dealing with a file
00455   if (!isFile()) return false; 
00456 
00457   // Create the file
00458   int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
00459   if (fd < 0)
00460     ThrowErrno(path + ": Can't create file");
00461   ::close(fd);
00462 
00463   return true;
00464 }
00465 
00466 bool
00467 Path::createTemporaryFile() {
00468   // Make sure we're dealing with a file
00469   if (!isFile()) return false;
00470 
00471   // Append the filename filler
00472   char pathname[MAXPATHLEN];
00473   path.copy(pathname,MAXPATHLEN);
00474   pathname[path.length()] = 0;
00475   strcat(pathname,"XXXXXX");
00476   int fd = ::mkstemp(pathname);
00477   if (fd < 0) {
00478     ThrowErrno(path + ": Can't create temporary file");
00479   }
00480   path = pathname;
00481   ::close(fd);
00482   return true;
00483 }
00484 
00485 bool
00486 Path::destroyDirectory(bool remove_contents) {
00487   // Make sure we're dealing with a directory
00488   if (!isDirectory()) return false;
00489 
00490   // If it doesn't exist, we're done.
00491   if (!exists()) return true;
00492 
00493   if (remove_contents) {
00494     // Recursively descend the directory to remove its content
00495     std::string cmd("/bin/rm -rf ");
00496     cmd += path;
00497     system(cmd.c_str());
00498   } else {
00499     // Otherwise, try to just remove the one directory
00500     char pathname[MAXPATHLEN];
00501     path.copy(pathname,MAXPATHLEN);
00502     int lastchar = path.length() - 1 ; 
00503     if (pathname[lastchar] == '/') 
00504       pathname[lastchar] = 0;
00505     else
00506       pathname[lastchar+1] = 0;
00507     if ( 0 != rmdir(pathname))
00508       ThrowErrno(std::string(pathname) + ": Can't destroy directory");
00509   }
00510   return true;
00511 }
00512 
00513 bool
00514 Path::destroyFile() {
00515   if (!isFile()) return false;
00516   if (0 != unlink(path.c_str()))
00517     ThrowErrno(path + ": Can't destroy file");
00518   return true;
00519 }
00520 
00521 bool
00522 Path::renameFile(const Path& newName) {
00523   if (!isFile()) return false;
00524   if (0 != rename(path.c_str(), newName.c_str()))
00525     ThrowErrno(std::string("can't rename ") + path + " as " + newName.get());
00526   return true;
00527 }
00528 
00529 bool
00530 Path::setStatusInfo(const StatusInfo& si) const {
00531   if (!isFile()) return false;
00532   struct utimbuf utb;
00533   utb.actime = si.modTime.toPosixTime();
00534   utb.modtime = utb.actime;
00535   if (0 != ::utime(path.c_str(),&utb))
00536     ThrowErrno(path + ": can't set file modification time");
00537   if (0 != ::chmod(path.c_str(),si.mode))
00538     ThrowErrno(path + ": can't set mode");
00539   return true;
00540 }
00541 
00542 }
00543 
00544 // vim: sw=2