1 /**************************************************************************
3 * Copyright 2011 Jose Fonseca
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 **************************************************************************/
27 * String manipulation.
30 #ifndef _OS_STRING_HPP_
31 #define _OS_STRING_HPP_
40 // Some versions of MinGW are missing _vscprintf's declaration, although they
41 // still provide the symbol in the import library.
42 extern "C" _CRTIMP int _vscprintf(const char *format, va_list argptr);
47 #define va_copy(dest, src) __va_copy((dest), (src))
49 #define va_copy(dest, src) (dest) = (src)
59 #define OS_DIR_SEP '\\'
60 #define OS_PATH_SEP ';'
62 #define OS_DIR_SEP '/'
63 #define OS_PATH_SEP ':'
71 * Class to represent zero-terminated strings, based upon std::vector<char>,
72 * suitable for passing strings or paths to/from OS calls.
74 * Both Win32 and POSIX APIs return strings as zero length buffers. Although
75 * std::string provides an easy method to obtain a read-only pointer to a zero
76 * terminated string, it lacks the ability to return a read-write pointer. So
77 * there is no way to tell OS calls to write into a std::string directly -- a
78 * temporary malloc'ed string would be necessary --, which would be
79 * unnecessarily inefficient, specially considering that these strings would
80 * ultimately passed back to the OS, which would again expect zero-terminated
83 * This class is not, however, a full replacement for std::string, which should
84 * be otherwise used whenever possible.
88 typedef std::vector<char> Buffer;
91 * The buffer's last element is always the '\0' character, therefore the
92 * buffer must never be empty.
96 Buffer::iterator find(char c) {
97 Buffer::iterator it = buffer.begin();
98 assert(it != buffer.end());
99 while (it != buffer.end()) {
108 Buffer::iterator rfind(char c) {
109 Buffer::iterator it = buffer.end();
111 // Skip trailing '\0'
112 assert(it != buffer.begin());
116 while (it != buffer.begin()) {
126 String(size_t size) :
149 Buffer::iterator rfindSep(bool skipTrailing = true) {
150 Buffer::iterator it = end();
152 // Skip trailing separators
154 while (it != buffer.begin()) {
157 // Halt if find the root
158 if (it == buffer.begin()) {
167 // Advance to the last separator
168 while (it != buffer.begin()) {
186 String(const char *s) :
187 buffer(s, s + strlen(s) + 1)
190 String(const String &other) :
194 template <class InputIterator>
195 String(InputIterator first, InputIterator last) :
202 * From a printf-like format string
205 format(const char *format, ...)
207 __attribute__ ((format (printf, 1, 2)))
213 va_start(args, format);
217 va_copy(args_copy, args);
219 /* We need to use _vscprintf to calculate the length as vsnprintf returns -1
220 * if the number of characters to write is greater than count.
222 length = _vscprintf(format, args_copy);
225 length = vsnprintf(&dummy, sizeof dummy, format, args_copy);
230 size_t size = length + 1;
234 va_start(args, format);
235 vsnprintf(path.buf(), size, format, args);
242 * Conversion to ordinary C strings.
245 const char *str(void) const {
246 assert(buffer.back() == 0);
250 operator const char *(void) const {
258 typedef Buffer::const_iterator const_iterator;
259 typedef Buffer::iterator iterator;
261 const_iterator begin(void) const {
262 return buffer.begin();
265 iterator begin(void) {
266 return buffer.begin();
269 const_iterator end(void) const {
270 const_iterator it = buffer.end();
271 assert(it != buffer.begin());
277 iterator it = buffer.end();
278 assert(it != buffer.begin());
287 void insert(iterator position, char c) {
288 buffer.insert(position, c);
291 template <class InputIterator>
292 void insert(iterator position, InputIterator first, InputIterator last) {
293 buffer.insert(position, first, last);
296 void insert(iterator position, const char *s) {
298 insert(position, s, s + strlen(s));
301 void insert(iterator position, const String & other) {
302 insert(position, other.begin(), other.end());
305 void append(char c) {
309 template <class InputIterator>
310 void append(InputIterator first, InputIterator last) {
311 insert(end(), first, last);
314 void append(const char *s) {
318 void append(const String & other) {
319 insert(end(), other);
322 template <class InputIterator>
323 void erase(InputIterator first, InputIterator last) {
324 buffer.erase(first, last);
328 * Get a writable buffer with the specified size.
330 * truncate() must be called after the buffer is written, and before any other
333 * Between the call to buf() and truncate() methods, the `buffer.back() ==
334 * 0` invariant will not hold true.
336 char *buf(size_t size) {
341 size_t length(void) const {
342 size_t size = buffer.size();
344 assert(buffer[size - 1] == 0);
349 * Truncate the string to the specified length.
351 void truncate(size_t length) {
352 assert(length < buffer.size());
354 assert(strlen(&buffer[0]) == length);
355 buffer.resize(length + 1);
359 * Truncate the string to the first zero character.
361 void truncate(void) {
362 truncate(strlen(&buffer[0]));
373 /* Trim directory (leaving base filename).
375 void trimDirectory(void) {
376 iterator sep = rfindSep();
378 buffer.erase(buffer.begin(), sep + 1);
382 /* Trim filename component (leaving containing directory).
384 * - trailing separators are ignored
385 * - a path with no separator at all yields "."
386 * - a path consisting of just the root directory is left unchanged
388 void trimFilename(void) {
389 iterator sep = rfindSep();
391 // No separator found, so return '.'
399 // Root. Nothing to do.
400 if (sep == buffer.begin()) {
405 buffer.erase(sep, end());
408 void trimExtension(void) {
409 iterator dot = rfind('.');
410 if (dot != buffer.end()) {
411 buffer.erase(dot, end());
415 void join(const String & other) {
416 if (length() && end()[-1] != OS_DIR_SEP) {
419 append(other.begin(), other.end());
424 String getProcessName();
425 String getCurrentDir();
427 bool createDirectory(const String &path);
429 bool copyFile(const String &srcFileName, const String &dstFileName, bool override = true);
431 bool removeFile(const String &fileName);
435 #endif /* _OS_STRING_HPP_ */