OSDN Git Service

For PR1291:
authorReid Spencer <rspencer@reidspencer.com>
Sat, 7 Apr 2007 18:52:17 +0000 (18:52 +0000)
committerReid Spencer <rspencer@reidspencer.com>
Sat, 7 Apr 2007 18:52:17 +0000 (18:52 +0000)
Implement the PathWithStatus class and its use throughout lib/System.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@35742 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/System/MappedFile.h
include/llvm/System/Path.h
lib/System/Unix/Path.inc
lib/System/Unix/Signals.inc
lib/System/Win32/Path.inc

index f82d6fe..6276bc3 100644 (file)
@@ -152,7 +152,7 @@ namespace sys {
   /// @name Data
   /// @{
   private:
-    sys::Path path_;       ///< Path to the file.
+    sys::PathWithStatus path_;       ///< Path to the file.
     int options_;          ///< Options used to create the mapping
     void* base_;           ///< Pointer to the base memory address
     mutable MappedFileInfo* info_; ///< Platform specific info for the mapping
index e251985..69c1b48 100644 (file)
@@ -29,9 +29,9 @@ namespace sys {
   /// platform independent and eliminates many of the unix-specific fields.
   /// However, to support llvm-ar, the mode, user, and group fields are
   /// retained. These pertain to unix security and may not have a meaningful
-  /// value on non-Unix platforms. However, the fileSize and modTime fields
-  /// should always be applicable on all platforms.  The structure is
-  /// filled in by the Path::getFileStatus method.
+  /// value on non-Unix platforms. However, the other fields fields should 
+  /// always be applicable on all platforms.  The structure is filled in by 
+  /// the PathWithStatus class.
   /// @brief File status structure
   class FileStatus {
   public:
@@ -164,16 +164,15 @@ namespace sys {
       /// provided so that they can be used to indicate null or error results in
       /// other lib/System functionality.
       /// @brief Construct an empty (and invalid) path.
-      Path() : path(), status(0) {}
-      ~Path() { delete status; }
-      Path(const Path &that) : path(that.path), status(0) {}
+      Path() : path() {}
+      Path(const Path &that) : path(that.path) {}
 
       /// This constructor will accept a std::string as a path. No checking is
       /// done on this path to determine if it is valid. To determine validity
       /// of the path, use the isValid method. 
       /// @param p The path to assign.
       /// @brief Construct a Path from a string.
-      explicit Path(const std::string& p) : path(p), status(0) {}
+      explicit Path(const std::string& p) : path(p) {}
 
     /// @}
     /// @name Operators
@@ -184,9 +183,6 @@ namespace sys {
       /// @brief Assignment Operator
       Path &operator=(const Path &that) {
         path = that.path;
-        if (status)
-          delete status;
-        status = 0;
         return *this;
       }
 
@@ -230,8 +226,8 @@ namespace sys {
       /// This function determines if the contents of the path name are empty. 
       /// That is, the path name has a zero length. This does NOT determine if
       /// if the file is empty. To get the length of the file itself, Use the 
-      /// getFileStatus() method and then the getSize() on the returned
-      /// FileStatus object
+      /// PathWithStatus::getFileStatus() method and then the getSize() method 
+      /// on the returned FileStatus object.
       /// @returns true iff the path is empty.
       /// @brief Determines if the path name is empty (invalid).
       bool isEmpty() const { return path.empty(); }
@@ -361,17 +357,6 @@ namespace sys {
         std::string* ErrMsg    ///< Optional place to return an error message.
       ) const;
 
-      /// This function returns status information about the file. The type of
-      /// path (file or directory) is updated to reflect the actual contents
-      /// of the file system.
-      /// @returns 0 on failure, with Error explaining why (if non-zero)
-      /// @returns a pointer to a FileStatus structure on success.
-      /// @brief Get file status.
-      const FileStatus *getFileStatus(
-        bool forceUpdate = false, ///< Force an update from the file system
-        std::string *Error = 0    ///< Optional place to return an error msg.
-      ) const;
-
     /// @}
     /// @name Path Mutators
     /// @{
@@ -527,9 +512,85 @@ namespace sys {
     /// @}
     /// @name Data
     /// @{
-    private:
+    protected:
       mutable std::string path;   ///< Storage for the path name.
-      mutable FileStatus *status; ///< Status information.
+
+    /// @}
+  };
+
+  /// This class is identical to Path class except it allows you to obtain the
+  /// file status of the Path as well. The reason for the distinction is one of
+  /// efficiency. First, the file status requires additional space and the space
+  /// is incorporated directly into PathWithStatus without an additional malloc.
+  /// Second, obtaining status information is an expensive operation on most
+  /// operating systems so we want to be careful and explicity about where we
+  /// allow this operation in LLVM.
+  /// @brief Path with file status class.
+  class PathWithStatus : public Path {
+    /// @name Constructors
+    /// @{
+    public: 
+      /// @brief Default constructor
+      PathWithStatus() : Path(), status(), fsIsValid(false) {}
+
+      /// @brief Copy constructor
+      PathWithStatus(const PathWithStatus &that) 
+        : Path(static_cast<const Path&>(that)), status(that.status), 
+           fsIsValid(that.fsIsValid) {}
+
+      /// This constructor allows construction from a Path object
+      /// @brief Path constructor
+      PathWithStatus(const Path &other) 
+        : Path(other), status(), fsIsValid(false) {}
+
+      /// This constructor will accept a std::string as a path. No checking is
+      /// done on this path to determine if it is valid. To determine validity
+      /// of the path, use the isValid method. 
+      /// @param p The path to assign.
+      /// @brief Construct a Path from a string.
+      explicit PathWithStatus(const std::string& p) 
+        : Path(p), status(), fsIsValid(false) {}
+
+      /// Makes a copy of \p that to \p this.
+      /// @returns \p this
+      /// @brief Assignment Operator
+      PathWithStatus &operator=(const PathWithStatus &that) {
+        static_cast<Path&>(*this) = static_cast<const Path&>(that);
+        status = that.status;
+        fsIsValid = that.fsIsValid;
+        return *this;
+      }
+
+      /// Makes a copy of \p that to \p this.
+      /// @returns \p this
+      /// @brief Assignment Operator
+      PathWithStatus &operator=(const Path &that) {
+        static_cast<Path&>(*this) = static_cast<const Path&>(that);
+        fsIsValid = false;
+        return *this;
+      }
+
+    /// @}
+    /// @name Methods
+    /// @{
+    public:
+      /// This function returns status information about the file. The type of
+      /// path (file or directory) is updated to reflect the actual contents
+      /// of the file system.
+      /// @returns 0 on failure, with Error explaining why (if non-zero)
+      /// @returns a pointer to a FileStatus structure on success.
+      /// @brief Get file status.
+      const FileStatus *getFileStatus(
+        bool forceUpdate = false, ///< Force an update from the file system
+        std::string *Error = 0    ///< Optional place to return an error msg.
+      ) const;
+
+    /// @}
+    /// @name Data
+    /// @{
+    private:
+      mutable FileStatus status; ///< Status information.
+      mutable bool fsIsValid;    ///< Whether we've obtained it or not
 
     /// @}
   };
@@ -556,7 +617,9 @@ namespace sys {
   bool CopyFile(const Path& Dest, const Path& Src, std::string* ErrMsg);
 }
 
+
 std::ostream& operator<<(std::ostream& strm, const sys::Path& aPath);
+std::ostream& operator<<(std::ostream& strm, const sys::PathWithStatus& aPath);
 
 }
 
index 6ceffcd..53906d9 100644 (file)
@@ -333,10 +333,10 @@ bool
 Path::canExecute() const {
   if (0 != access(path.c_str(), R_OK | X_OK ))
     return false;
-  if (const FileStatus *fs = getFileStatus(true, 0)) {
-    if (!S_ISREG(fs->mode))
-      return false;
-  } else
+  struct stat buf;
+  if (0 != stat(path.c_str(), &buf))
+    return false;
+  if (!S_ISREG(buf.st_mode))
     return false;
   return true;
 }
@@ -363,26 +363,25 @@ Path::getLast() const {
   return path.substr(pos+1);
 }
 
-const FileStatus*
-Path::getFileStatus(bool update, std::string *ErrStr) const {
-  if (status == 0 || update) {
+const FileStatus *
+PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const {
+  if (!fsIsValid || update) {
     struct stat buf;
     if (0 != stat(path.c_str(), &buf)) {
       MakeErrMsg(ErrStr, path + ": can't get status of file");
       return 0;
     }
-    if (status == 0)
-      status = new FileStatus;
-    status->fileSize = buf.st_size;
-    status->modTime.fromEpochTime(buf.st_mtime);
-    status->mode = buf.st_mode;
-    status->user = buf.st_uid;
-    status->group = buf.st_gid;
-    status->uniqueID = uint64_t(buf.st_ino);
-    status->isDir  = S_ISDIR(buf.st_mode);
-    status->isFile = S_ISREG(buf.st_mode);
+    status.fileSize = buf.st_size;
+    status.modTime.fromEpochTime(buf.st_mtime);
+    status.mode = buf.st_mode;
+    status.user = buf.st_uid;
+    status.group = buf.st_gid;
+    status.uniqueID = uint64_t(buf.st_ino);
+    status.isDir  = S_ISDIR(buf.st_mode);
+    status.isFile = S_ISREG(buf.st_mode);
+    fsIsValid = true;
   }
-  return status;
+  return &status;
 }
 
 static bool AddPermissionBits(const Path &File, int bits) {
@@ -394,14 +393,13 @@ static bool AddPermissionBits(const Path &File, int bits) {
   umask(mask);            // Restore the umask.
 
   // Get the file's current mode.
-  if (const FileStatus *fs = File.getFileStatus()) {
-    // Change the file to have whichever permissions bits from 'bits'
-    // that the umask would not disable.
-    if ((chmod(File.c_str(), (fs->getMode() | (bits & ~mask)))) == -1)
-      return false;
-  } else 
+  struct stat buf;
+  if (0 != stat(File.toString().c_str(), &buf)) 
     return false;
-
+  // Change the file to have whichever permissions bits from 'bits'
+  // that the umask would not disable.
+  if ((chmod(File.c_str(), (buf.st_mode | (bits & ~mask)))) == -1)
+      return false;
   return true;
 }
 
@@ -594,25 +592,28 @@ Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) {
 
 bool
 Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const {
-  FileStatus Status;
-  if (const FileStatus *Status = getFileStatus(false, ErrStr)) {
-    // 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 MakeErrMsg(ErrStr, path + ": can't destroy file");
-      return false;
-    }
-    
-    if (!Status->isDir) {
-      if (ErrStr) *ErrStr = "not a file or directory";
-      return true;
-    }
-  } else
+  // Get the status so we can determin if its a file or directory
+  struct stat buf;
+  if (0 != stat(path.c_str(), &buf)) {
+    MakeErrMsg(ErrStr, path + ": can't get status of file");
     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 (S_ISREG(buf.st_mode)) {
+    if (unlink(path.c_str()) != 0)
+      return MakeErrMsg(ErrStr, path + ": can't destroy file");
+    return false;
+  }
+  
+  if (!S_ISDIR(buf.st_mode)) {
+    if (ErrStr) *ErrStr = "not a file or directory";
+    return true;
+  }
 
   if (remove_contents) {
     // Recursively descend the directory to remove its contents.
@@ -644,7 +645,7 @@ Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) {
   return false;
 }
 
-bool
+bool 
 Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrStr) const {
   struct utimbuf utb;
   utb.actime = si.modTime.toPosixTime();
index a471b95..d1493a2 100644 (file)
@@ -21,6 +21,9 @@
 #if HAVE_SIGNAL_H
 #include <signal.h>
 #endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
 using namespace llvm;
 
 namespace {
@@ -168,10 +171,13 @@ bool sys::RemoveFileOnSignal(const sys::Path &Filename, std::string* ErrMsg) {
 // RemoveDirectoryOnSignal - The public API
 bool sys::RemoveDirectoryOnSignal(const sys::Path& path, std::string* ErrMsg) {
   // Not a directory?
-  const sys::FileStatus *Status = path.getFileStatus(false, ErrMsg);
-  if (!Status)
+  struct stat buf;
+  if (0 != stat(path.c_str(), &buf)) {
+    MakeErrMsg(ErrMsg, path.toString() + ": can't get status of file");
     return true;
-  if (!Status->isDir) {
+  }
+
+  if (!S_ISDIR(buf.st_mode)) {
     if (ErrMsg)
       *ErrMsg = path.toString() + " is not a directory";
     return true;
index 57d7535..937bb51 100644 (file)
@@ -307,8 +307,8 @@ Path::getLast() const {
 }
 
 const FileStatus *
-Path::getFileStatus(bool update, std::string *ErrStr) const {
-  if (status == 0 || update) {
+PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const {
+  if (!fsIsValid || update) {
     WIN32_FILE_ATTRIBUTE_DATA fi;
     if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) {
       MakeErrMsg(ErrStr, "getStatusInfo():" + std::string(path) +
@@ -316,30 +316,28 @@ Path::getFileStatus(bool update, std::string *ErrStr) const {
       return 0;
     }
 
-    if (status == 0)
-      status = new FileStatus;
+    status.fileSize = fi.nFileSizeHigh;
+    status.fileSize <<= sizeof(fi.nFileSizeHigh)*8;
+    status.fileSize += fi.nFileSizeLow;
 
-    status->fileSize = fi.nFileSizeHigh;
-    status->fileSize <<= sizeof(fi.nFileSizeHigh)*8;
-    status->fileSize += fi.nFileSizeLow;
-
-    status->mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
-    status->user = 9999;    // Not applicable to Windows, so...
-    status->group = 9999;   // Not applicable to Windows, so...
+    status.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
+    status.user = 9999;    // Not applicable to Windows, so...
+    status.group = 9999;   // Not applicable to Windows, so...
 
     // FIXME: this is only unique if the file is accessed by the same file path.
     // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode
     // numbers, but the concept doesn't exist in Windows.
-    status->uniqueID = 0;
+    status.uniqueID = 0;
     for (unsigned i = 0; i < path.length(); ++i)
-      status->uniqueID += path[i];
+      status.uniqueID += path[i];
 
     __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime);
-    status->modTime.fromWin32Time(ft);
+    status.modTime.fromWin32Time(ft);
 
-    status->isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+    status.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+    fsIsValid = true;
   }
-  return status;
+  return &status;
 }
 
 bool Path::makeReadableOnDisk(std::string* ErrMsg) {