#ifdef _WIN32
#define OS_DIR_SEP '\\'
+#define OS_PATH_SEP ';'
#else /* !_WIN32 */
#define OS_DIR_SEP '/'
+#define OS_PATH_SEP ':'
#endif /* !_WIN32 */
/**
- * 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:
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();
}
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
*/
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);
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];
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) {
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 */