X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=common%2Fos_string.hpp;h=996c6191e2313a7677024f703b05147aeee39651;hb=4dbf7c9e3f4b669ec38600c04ba8a67926d10cc7;hp=c92e6a88a7ede3fb58b4f39e08e1bae1f7b377ea;hpb=48412ffde3dd4710c96d5e8d9cfdf1789e4d703a;p=apitrace diff --git a/common/os_string.hpp b/common/os_string.hpp index c92e6a8..996c619 100644 --- a/common/os_string.hpp +++ b/common/os_string.hpp @@ -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, + * 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 + 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 */