]> git.cworth.org Git - apitrace/blobdiff - common/os_string.hpp
retrace: Implement glxCopySubBufferMESA
[apitrace] / common / os_string.hpp
index c92e6a88a7ede3fb58b4f39e08e1bae1f7b377ea..996c6191e2313a7677024f703b05147aeee39651 100644 (file)
@@ -57,8 +57,10 @@ extern "C" _CRTIMP int _vscprintf(const char *format, va_list argptr);
 
 #ifdef _WIN32
 #define OS_DIR_SEP '\\'
+#define OS_PATH_SEP ';'
 #else /* !_WIN32 */
 #define OS_DIR_SEP '/'
+#define OS_PATH_SEP ':'
 #endif /* !_WIN32 */
 
 
@@ -66,8 +68,20 @@ namespace os {
 
 
 /**
- * Vector based zero-terminate string, suitable for passing strings or paths
- * to/from OS calls.
+ * Class to represent zero-terminated strings, based upon std::vector<char>,
+ * suitable for passing strings or paths to/from OS calls.
+ *
+ * Both Win32 and POSIX APIs return strings as zero length buffers.  Although
+ * std::string provides an easy method to obtain a read-only pointer to a zero
+ * terminated string, it lacks the ability to return a read-write pointer. So
+ * there is no way to tell OS calls to write into a std::string directly -- a
+ * temporary malloc'ed string would be necessary --, which would be
+ * unnecessarily inefficient, specially considering that these strings would
+ * ultimately passed back to the OS, which would again expect zero-terminated
+ * strings.
+ *
+ * This class is not, however, a full replacement for std::string, which should
+ * be otherwise used whenever possible.
  */
 class String {
 protected:
@@ -93,12 +107,19 @@ protected:
 
     Buffer::iterator rfind(char c) {
         Buffer::iterator it = buffer.end();
+
+        // Skip trailing '\0'
+        assert(it != buffer.begin());
+        --it;
+        assert(*it == '\0');
+
         while (it != buffer.begin()) {
             --it;
             if (*it == c) {
                 return it;
             }
         }
+
         return buffer.end();
     }
 
@@ -110,8 +131,50 @@ protected:
         return &buffer[0];
     }
 
+    inline bool
+    isSep(char c) {
+        if (c == '/') {
+            return true;
+        }
+#ifdef _WIN32
+        if (c == '\\') {
+            return true;
+        }
+#endif
+        return false;
+    }
+
 public:
 
+    Buffer::iterator rfindSep(bool skipTrailing = true) {
+        Buffer::iterator it = end();
+
+        // Skip trailing separators
+        if (skipTrailing) {
+            while (it != buffer.begin()) {
+                --it;
+                if (isSep(*it)) {
+                    // Halt if find the root
+                    if (it == buffer.begin()) {
+                        return it;
+                    }
+                } else {
+                    break;
+                }
+            }
+        }
+
+        // Advance to the last separator
+        while (it != buffer.begin()) {
+            --it;
+            if (isSep(*it)) {
+                return it;
+            }
+        }
+
+        return end();
+    }
+
     /*
      * Constructors
      */
@@ -153,7 +216,7 @@ public:
         va_list args_copy;
         va_copy(args_copy, args);
 #ifdef _WIN32
-        /* We need to use _vcsprintf to calculate the length as vsnprintf returns -1
+        /* We need to use _vscprintf to calculate the length as vsnprintf returns -1
          * if the number of characters to write is greater than count.
          */
         length = _vscprintf(format, args_copy);
@@ -256,6 +319,20 @@ public:
         insert(end(), other);
     }
 
+    template <class InputIterator>
+    void erase(InputIterator first, InputIterator last) {
+        buffer.erase(first, last);
+    }
+
+    /**
+     * Get a writable buffer with the specified size.
+     *
+     * truncate() must be called after the buffer is written, and before any other
+     * method is called.
+     *
+     * Between the call to buf() and truncate() methods, the `buffer.back() ==
+     * 0` invariant will not hold true.
+     */
     char *buf(size_t size) {
         buffer.resize(size);
         return &buffer[0];
@@ -268,54 +345,64 @@ public:
         return size - 1;
     }
 
+    /**
+     * Truncate the string to the specified length.
+     */
     void truncate(size_t length) {
         assert(length < buffer.size());
         buffer[length] = 0;
+        assert(strlen(&buffer[0]) == length);
         buffer.resize(length + 1);
     }
 
+    /**
+     * Truncate the string to the first zero character.
+     */
     void truncate(void) {
-        truncate(strlen(str()));
+        truncate(strlen(&buffer[0]));
     }
 
 
     /*
-     * String manipulation
+     * Path manipulation
      */
 
     bool
     exists(void) const;
 
+    /* Trim directory (leaving base filename).
+     */
     void trimDirectory(void) {
-        iterator sep = rfind(OS_DIR_SEP);
-        if (sep != buffer.end()) {
+        iterator sep = rfindSep();
+        if (sep != end()) {
             buffer.erase(buffer.begin(), sep + 1);
         }
     }
 
     /* Trim filename component (leaving containing directory).
      *
-     * This function removes everything after the final path
-     * separator, as well as that separator itself if it is not the
-     * only remaining separator.
-     *
-     * Some specific consequences of the above:
-     *
-     * 1. A path with no separator at all is unchanged.
-     * 2. A path with a trailing separator has only that separator removed
-     * 3. A path of just the root directory is unchaged.
+     * - trailing separators are ignored
+     * - a path with no separator at all yields "."
+     * - a path consisting of just the root directory is left unchanged
      */
     void trimFilename(void) {
-        iterator first = find(OS_DIR_SEP);
-        iterator last = rfind(OS_DIR_SEP);
-        if (last == buffer.end()) {
+        iterator sep = rfindSep();
+
+        // No separator found, so return '.'
+        if (sep == end()) {
+            buffer.resize(2);
+            buffer[0] = '.';
+            buffer[1] = 0;
             return;
         }
-        if (last == first) {
-            buffer.erase(first + 1, end());
-        } else {
-            buffer.erase(last, end());
+
+        // Root. Nothing to do.
+        if (sep == buffer.begin()) {
+            return;
         }
+
+        // Trim filename
+        buffer.erase(sep, end());
     }
 
     void trimExtension(void) {
@@ -337,6 +424,11 @@ public:
 String getProcessName();
 String getCurrentDir();
 
+bool createDirectory(const String &path);
+
+bool copyFile(const String &srcFileName, const String &dstFileName, bool override = true);
+
+bool removeFile(const String &fileName);
 
 } /* namespace os */