For PR797:
[oota-llvm.git] / lib / System / Unix / Path.inc
index ffa1d174fd8ac94da4d7e0e8924889d75e93b7a0..2b9f1263ee34501bda35063009f593820d10e073 100644 (file)
 # undef HAVE_MKDTEMP
 #endif
 
-namespace llvm {
-using namespace sys;
+namespace {
+inline bool lastIsSlash(const std::string& path) {
+  return !path.empty() && path[path.length() - 1] == '/';
+}
 
-Path::Path(const std::string& unverified_path) : path(unverified_path) {
-  if (unverified_path.empty())
-    return;
-  if (this->isValid()) 
-    return;
-  // oops, not valid.
-  path.clear();
-  ThrowErrno(unverified_path + ": path is not valid");
 }
 
+namespace llvm {
+using namespace sys;
+
 bool 
 Path::isValid() const {
   // Check some obvious things
@@ -90,14 +87,17 @@ Path::GetRootDirectory() {
 }
 
 Path
-Path::GetTemporaryDirectory() {
+Path::GetTemporaryDirectory(std::string* ErrMsg ) {
 #if defined(HAVE_MKDTEMP)
   // The best way is with mkdtemp but that's not available on many systems, 
   // Linux and FreeBSD have it. Others probably won't.
   char pathname[MAXPATHLEN];
   strcpy(pathname,"/tmp/llvm_XXXXXX");
-  if (0 == mkdtemp(pathname))
-    ThrowErrno(std::string(pathname) + ": can't create temporary directory");
+  if (0 == mkdtemp(pathname)) {
+    MakeErrMsg(ErrMsg, 
+      std::string(pathname) + ": can't create temporary directory");
+    return Path();
+  }
   Path result;
   result.set(pathname);
   assert(result.isValid() && "mkdtemp didn't create a valid pathname!");
@@ -111,12 +111,18 @@ Path::GetTemporaryDirectory() {
   char pathname[MAXPATHLEN];
   strcpy(pathname, "/tmp/llvm_XXXXXX");
   int fd = 0;
-  if (-1 == (fd = mkstemp(pathname)))
-    ThrowErrno(std::string(pathname) + ": can't create temporary directory");
+  if (-1 == (fd = mkstemp(pathname))) {
+    MakeErrMsg(ErrMsg, 
+      std::string(pathname) + ": can't create temporary directory");
+    return Path();
+  }
   ::close(fd);
   ::unlink(pathname); // start race condition, ignore errors
-  if (-1 == ::mkdir(pathname, S_IRWXU)) // end race condition
-    ThrowErrno(std::string(pathname) + ": can't create temporary directory");
+  if (-1 == ::mkdir(pathname, S_IRWXU)) { // end race condition
+    MakeErrMsg(ErrMsg, 
+      std::string(pathname) + ": can't create temporary directory");
+    return Path();
+  }
   Path result;
   result.set(pathname);
   assert(result.isValid() && "mkstemp didn't create a valid pathname!");
@@ -130,10 +136,16 @@ Path::GetTemporaryDirectory() {
   char pathname[MAXPATHLEN];
   strcpy(pathname, "/tmp/llvm_XXXXXX");
   char *TmpName = ::mktemp(pathname);
-  if (TmpName == 0)
-    ThrowErrno(std::string(TmpName) + ": can't create unique directory name");
-  if (-1 == ::mkdir(TmpName, S_IRWXU))
-    ThrowErrno(std::string(TmpName) + ": can't create temporary directory");
+  if (TmpName == 0) {
+    MakeErrMsg(ErrMsg, 
+      std::string(TmpName) + ": can't create unique directory name");
+    return Path();
+  }
+  if (-1 == ::mkdir(TmpName, S_IRWXU)) {
+    MakeErrMsg(ErrMsg, 
+        std::string(TmpName) + ": can't create temporary directory");
+    return Path();
+  }
   Path result;
   result.set(TmpName);
   assert(result.isValid() && "mktemp didn't create a valid pathname!");
@@ -153,8 +165,10 @@ Path::GetTemporaryDirectory() {
     num++;
     sprintf(pathname, "/tmp/llvm_%010u", unsigned(num));
   } while ( 0 == access(pathname, F_OK ) );
-  if (-1 == ::mkdir(pathname, S_IRWXU))
-    ThrowErrno(std::string(pathname) + ": can't create temporary directory");
+  if (-1 == ::mkdir(pathname, S_IRWXU)) {
+    MakeErrMsg(ErrMsg, 
+      std::string(pathname) + ": can't create temporary directory");
+    return Path();
   Path result;
   result.set(pathname);
   assert(result.isValid() && "mkstemp didn't create a valid pathname!");
@@ -229,32 +243,6 @@ Path::GetUserHomeDirectory() {
   return GetRootDirectory();
 }
 
-bool
-Path::isFile() const {
-  struct stat buf;
-  if (0 != stat(path.c_str(), &buf)) {
-    ThrowErrno(path + ": can't determine type of path object: ");
-  }
-  return S_ISREG(buf.st_mode);
-}
-
-bool
-Path::isDirectory() const {
-  struct stat buf;
-  if (0 != stat(path.c_str(), &buf)) {
-    ThrowErrno(path + ": can't determine type of path object: ");
-  }
-  return S_ISDIR(buf.st_mode);
-}
-
-bool
-Path::isHidden() const {
-  size_t slash = path.rfind('/');
-  return (slash != std::string::npos && 
-          slash < path.length()-1 && 
-          path[slash+1] == '.') || 
-         (!path.empty() && slash == std::string::npos && path[0] == '.');
-}
 
 std::string
 Path::getBasename() const {
@@ -265,14 +253,18 @@ 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();
   assert(len < 1024 && "Request for magic string too long");
   char* buf = (char*) alloca(1 + len);
-  int fd = ::open(path.c_str(),O_RDONLY);
+  int fd = ::open(path.c_str(), O_RDONLY);
   if (fd < 0)
     return false;
   size_t read_len = ::read(fd, buf, len);
@@ -284,11 +276,9 @@ bool Path::hasMagicNumber(const std::string &Magic) const {
 }
 
 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
-  if (!isFile())
-    return false;
   assert(len < 1024 && "Request for magic string too long");
   char* buf = (char*) alloca(1 + len);
-  int fd = ::open(path.c_str(),O_RDONLY);
+  int fd = ::open(path.c_str(), O_RDONLY);
   if (fd < 0)
     return false;
   ssize_t bytes_read = ::read(fd, buf, len);
@@ -303,9 +293,9 @@ bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
 
 bool 
 Path::isBytecodeFile() const {
-  char buffer[ 4];
+  char buffer[4];
   buffer[0] = 0;
-  int fd = ::open(path.c_str(),O_RDONLY);
+  int fd = ::open(path.c_str(), O_RDONLY);
   if (fd < 0)
     return false;
   ssize_t bytes_read = ::read(fd, buffer, 4);
@@ -314,7 +304,7 @@ Path::isBytecodeFile() const {
     return false;
 
   return (buffer[0] == 'l' && buffer[1] == 'l' && buffer[2] == 'v' &&
-      (buffer[3] == 'c' || buffer[3] == 'm'));
+         (buffer[3] == 'c' || buffer[3] == 'm'));
 }
 
 bool
@@ -334,11 +324,13 @@ Path::canWrite() const {
 
 bool
 Path::canExecute() const {
+  if (0 != access(path.c_str(), R_OK | X_OK ))
+    return false;
   struct stat st;
   int r = stat(path.c_str(), &st);
   if (r != 0 || !S_ISREG(st.st_mode))
     return false;
-  return 0 == access(path.c_str(), R_OK | X_OK );
+  return true;
 }
 
 std::string 
@@ -363,21 +355,22 @@ Path::getLast() const {
   return path.substr(pos+1);
 }
 
-void
-Path::getStatusInfo(StatusInfo& info) const {
+bool
+Path::getFileStatus(FileStatus &info, std::string *ErrStr) const {
   struct stat buf;
-  if (0 != stat(path.c_str(), &buf)) {
-    ThrowErrno(path + ": can't determine type of path object: ");
-  }
+  if (0 != stat(path.c_str(), &buf))
+    return GetErrno(path + ": can't get status of file '" + path + "'", ErrStr);
   info.fileSize = buf.st_size;
   info.modTime.fromEpochTime(buf.st_mtime);
   info.mode = buf.st_mode;
   info.user = buf.st_uid;
   info.group = buf.st_gid;
-  info.isDir = S_ISDIR(buf.st_mode);
+  info.isDir  = S_ISDIR(buf.st_mode);
+  info.isFile = S_ISREG(buf.st_mode);
+  return false;
 }
 
-static bool AddPermissionBits(const std::string& Filename, int bits) {
+static bool AddPermissionBits(const Path &File, int bits) {
   // Get the umask value from the operating system.  We want to use it
   // when changing the file's permissions. Since calling umask() sets
   // the umask and returns its old value, we must call it a second
@@ -386,61 +379,71 @@ static bool AddPermissionBits(const std::string& Filename, int bits) {
   umask(mask);            // Restore the umask.
 
   // Get the file's current mode.
-  struct stat st;
-  if ((stat(Filename.c_str(), &st)) == -1)
-    return false;
+  FileStatus Stat;
+  if (File.getFileStatus(Stat)) return false;
 
   // Change the file to have whichever permissions bits from 'bits'
   // that the umask would not disable.
-  if ((chmod(Filename.c_str(), (st.st_mode | (bits & ~mask)))) == -1)
+  if ((chmod(File.c_str(), (Stat.getMode() | (bits & ~mask)))) == -1)
     return false;
 
   return true;
 }
 
-void Path::makeReadableOnDisk() {
-  if (!AddPermissionBits(path,0444))
-    ThrowErrno(path + ": can't make file readable");
+bool Path::makeReadableOnDisk(std::string* ErrMsg) {
+  if (!AddPermissionBits(*this, 0444)) {
+    MakeErrMsg(ErrMsg, path + ": can't make file readable");
+    return true;
+  }
+  return false;
 }
 
-void Path::makeWriteableOnDisk() {
-  if (!AddPermissionBits(path,0222))
-    ThrowErrno(path + ": can't make file writable");
+bool Path::makeWriteableOnDisk(std::string* ErrMsg) {
+  if (!AddPermissionBits(*this, 0222)) {
+    MakeErrMsg(ErrMsg, path + ": can't make file writable");
+    return true;
+  }
+  return false;
 }
 
-void Path::makeExecutableOnDisk() {
-  if (!AddPermissionBits(path,0111))
-    ThrowErrno(path + ": can't make file executable");
+bool Path::makeExecutableOnDisk(std::string* ErrMsg) {
+  if (!AddPermissionBits(*this, 0111)) {
+    MakeErrMsg(ErrMsg, path + ": can't make file executable");
+    return true;
+  }
+  return false;
 }
 
 bool
-Path::getDirectoryContents(std::set<Path>& result) const {
-  if (!isDirectory())
-    return false;
+Path::getDirectoryContents(std::set<Path>& result, std::string* ErrMsg) const {
   DIR* direntries = ::opendir(path.c_str());
-  if (direntries == 0)
-    ThrowErrno(path + ": can't open directory");
+  if (direntries == 0) {
+    MakeErrMsg(ErrMsg, path + ": can't open directory");
+    return true;
+  }
+
+  std::string dirPath = path;
+  if (!lastIsSlash(dirPath))
+    dirPath += '/';
 
   result.clear();
   struct dirent* de = ::readdir(direntries);
   for ( ; de != 0; de = ::readdir(direntries)) {
     if (de->d_name[0] != '.') {
-      Path aPath(path + (const char*)de->d_name);
-      struct stat buf;
-      if (0 != stat(aPath.path.c_str(), &buf)) {
-        int stat_errno = errno;
-        struct stat st;
-        if (0 == lstat(aPath.path.c_str(), &st) && S_ISLNK(st.st_mode))
+      Path aPath(dirPath + (const char*)de->d_name);
+      struct stat st;
+      if (0 != lstat(aPath.path.c_str(), &st)) {
+        if (S_ISLNK(st.st_mode))
           continue; // dangling symlink -- ignore
-        ThrowErrno(aPath.path + 
-          ": can't determine file object type", stat_errno);
+        MakeErrMsg(ErrMsg, aPath.path +  ": can't determine file object type");
+        return true;
       }
       result.insert(aPath);
     }
   }
   
   closedir(direntries);
-  return true;
+  return false;
 }
 
 bool
@@ -461,11 +464,8 @@ Path::appendComponent(const std::string& name) {
   if (name.empty())
     return false;
   std::string save(path);
-  if (!path.empty()) {
-    size_t last = path.size() - 1;
-    if (path[last] != '/') 
-      path += '/';
-  }
+  if (!lastIsSlash(path))
+    path += '/';
   path += name;
   if (!isValid()) {
     path = save;
@@ -509,7 +509,7 @@ Path::eraseSuffix() {
   size_t dotpos = path.rfind('.',path.size());
   size_t slashpos = path.rfind('/',path.size());
   if (dotpos != std::string::npos) {
-    if (slashpos == std::string::npos || dotpos > slashpos) {
+    if (slashpos == std::string::npos || dotpos > slashpos+1) {
       path.erase(dotpos, path.size()-dotpos);
       return true;
     }
@@ -520,7 +520,7 @@ Path::eraseSuffix() {
 }
 
 bool
-Path::createDirectoryOnDisk( bool create_parents) {
+Path::createDirectoryOnDisk( bool create_parents, std::string* ErrMsg ) {
   // Get a writeable copy of the path name
   char pathname[MAXPATHLEN];
   path.copy(pathname,MAXPATHLEN);
@@ -543,8 +543,11 @@ Path::createDirectoryOnDisk( bool create_parents) {
     while ( next != 0 ) {
       *next = 0;
       if (0 != access(pathname, F_OK | R_OK | W_OK))
-        if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
-          ThrowErrno(std::string(pathname) + ": can't create directory");
+        if (0 != mkdir(pathname, S_IRWXU | S_IRWXG)) {
+          MakeErrMsg(ErrMsg, 
+            std::string(pathname) + ": can't create directory");
+          return true;
+        }
       char* save = next;
       next = strchr(next+1,'/');
       *save = '/';
@@ -552,64 +555,81 @@ Path::createDirectoryOnDisk( bool create_parents) {
   } 
 
   if (0 != access(pathname, F_OK | R_OK))
-    if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
-      ThrowErrno(std::string(pathname) + ": can't create directory");
-  return true;
+    if (0 != mkdir(pathname, S_IRWXU | S_IRWXG)) {
+      MakeErrMsg(ErrMsg, std::string(pathname) + ": can't create directory");
+      return true;
+    }
+  return false;
 }
 
 bool
-Path::createFileOnDisk() {
+Path::createFileOnDisk(std::string* ErrMsg) {
   // Create the file
   int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
-  if (fd < 0)
-    ThrowErrno(path + ": can't create file");
+  if (fd < 0) {
+    MakeErrMsg(ErrMsg, path + ": can't create file");
+    return true;
+  }
   ::close(fd);
-
-  return true;
+  return false;
 }
 
 bool
-Path::createTemporaryFileOnDisk(bool reuse_current) {
+Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) {
   // Make this into a unique file name
   makeUnique( reuse_current );
 
   // create the file
-  int outFile = ::open(path.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);
-  if (outFile != -1) {
-    ::close(outFile);
+  int fd = ::open(path.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);
+  if (fd < 0) {
+    MakeErrMsg(ErrMsg, path + ": can't create temporary file");
     return true;
   }
+  ::close(fd);
   return false;
 }
 
 bool
-Path::eraseFromDisk(bool remove_contents) const {
-  // Make sure we're dealing with a directory
-  if (isFile()) {
-    if (0 != unlink(path.c_str()))
-      ThrowErrno(path + ": can't destroy file");
-  } else if (isDirectory()) {
-    if (remove_contents) {
-      // Recursively descend the directory to remove its content
-      std::string cmd("/bin/rm -rf ");
-      cmd += path;
-      system(cmd.c_str());
-    } else {
-      // Otherwise, try to just remove the one directory
-      char pathname[MAXPATHLEN];
-      path.copy(pathname,MAXPATHLEN);
-      int lastchar = path.length() - 1 ; 
-      if (pathname[lastchar] == '/') 
-        pathname[lastchar] = 0;
-      else
-        pathname[lastchar+1] = 0;
-      if ( 0 != rmdir(pathname))
-        ThrowErrno(std::string(pathname) + ": can't destroy directory");
-    }
+Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const {
+  FileStatus Status;
+  if (getFileStatus(Status, ErrStr))
+    return true;
+    
+  // Note: this check catches strange situations. In all cases, LLVM should only
+  // be involved in the creation and deletion of regular files.  This check 
+  // ensures that what we're trying to erase is a regular file. It effectively
+  // prevents LLVM from erasing things like /dev/null, any block special file,
+  // or other things that aren't "regular" files. 
+  if (Status.isFile) {
+    if (unlink(path.c_str()) != 0)
+      return GetErrno(path + ": can't destroy file", ErrStr);
+    return false;
   }
-  else
+  
+  if (!Status.isDir) {
+    if (ErrStr) *ErrStr = "not a file or directory";
+    return true;
+  }
+  if (remove_contents) {
+    // Recursively descend the directory to remove its contents.
+    std::string cmd = "/bin/rm -rf " + path;
+    system(cmd.c_str());
     return false;
-  return true;
+  }
+
+  // Otherwise, try to just remove the one directory.
+  char pathname[MAXPATHLEN];
+  path.copy(pathname, MAXPATHLEN);
+  int lastchar = path.length() - 1 ; 
+  if (pathname[lastchar] == '/') 
+    pathname[lastchar] = 0;
+  else
+    pathname[lastchar+1] = 0;
+    
+  if (rmdir(pathname) != 0)
+    return GetErrno(std::string(pathname) + ": can't destroy directory",
+                    ErrStr);
+  return false;
 }
 
 bool
@@ -621,15 +641,15 @@ Path::renamePathOnDisk(const Path& newName) {
 }
 
 bool
-Path::setStatusInfoOnDisk(const StatusInfo& si) const {
+Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrStr) const {
   struct utimbuf utb;
   utb.actime = si.modTime.toPosixTime();
   utb.modtime = utb.actime;
   if (0 != ::utime(path.c_str(),&utb))
-    ThrowErrno(path + ": can't set file modification time");
+    return GetErrno(path + ": can't set file modification time", ErrStr);
   if (0 != ::chmod(path.c_str(),si.mode))
-    ThrowErrno(path + ": can't set mode");
-  return true;
+    return GetErrno(path + ": can't set mode", ErrStr);
+  return false;
 }
 
 void