1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
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
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25 
26 /*
27  * String manipulation.
28  */
29 
30 #pragma once
31 
32 
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stddef.h>
37 
38 #ifdef __MINGW32__
39 // Some versions of MinGW are missing _vscprintf's declaration, although they
40 // still provide the symbol in the import library.
41 extern "C" _CRTIMP int _vscprintf(const char *format, va_list argptr);
42 #endif
43 
44 #ifndef va_copy
45 #ifdef __va_copy
46 #define va_copy(dest, src) __va_copy((dest), (src))
47 #else
48 #define va_copy(dest, src) (dest) = (src)
49 #endif
50 #endif
51 
52 #include <vector>
53 
54 #include "os.hpp"
55 
56 
57 #ifdef _WIN32
58 #define OS_DIR_SEP '\\'
59 #define OS_PATH_SEP ';'
60 #else /* !_WIN32 */
61 #define OS_DIR_SEP '/'
62 #define OS_PATH_SEP ':'
63 #endif /* !_WIN32 */
64 
65 
66 namespace os {
67 
68 
69 /**
70  * Class to represent zero-terminated strings, based upon std::vector<char>,
71  * suitable for passing strings or paths to/from OS calls.
72  *
73  * Both Win32 and POSIX APIs return strings as zero length buffers.  Although
74  * std::string provides an easy method to obtain a read-only pointer to a zero
75  * terminated string, it lacks the ability to return a read-write pointer. So
76  * there is no way to tell OS calls to write into a std::string directly -- a
77  * temporary malloc'ed string would be necessary --, which would be
78  * unnecessarily inefficient, specially considering that these strings would
79  * ultimately passed back to the OS, which would again expect zero-terminated
80  * strings.
81  *
82  * This class is not, however, a full replacement for std::string, which should
83  * be otherwise used whenever possible.
84  */
85 class String {
86 protected:
87     typedef std::vector<char> Buffer;
88 
89     /**
90      * The buffer's last element is always the '\0' character, therefore the
91      * buffer must never be empty.
92      */
93     Buffer buffer;
94 
find(char c)95     Buffer::iterator find(char c) {
96         Buffer::iterator it = buffer.begin();
97         assert(it != buffer.end());
98         while (it != buffer.end()) {
99             if (*it == c) {
100                 return it;
101             }
102             ++it;
103         }
104         return buffer.end();
105     }
106 
rfind(char c)107     Buffer::iterator rfind(char c) {
108         Buffer::iterator it = buffer.end();
109 
110         // Skip trailing '\0'
111         assert(it != buffer.begin());
112         --it;
113         assert(*it == '\0');
114 
115         while (it != buffer.begin()) {
116             --it;
117             if (*it == c) {
118                 return it;
119             }
120         }
121 
122         return buffer.end();
123     }
124 
String(size_t size)125     String(size_t size) :
126         buffer(size) {
127     }
128 
buf(void)129     char *buf(void) {
130         return &buffer[0];
131     }
132 
133     inline bool
isSep(char c)134     isSep(char c) {
135         if (c == '/') {
136             return true;
137         }
138 #ifdef _WIN32
139         if (c == '\\') {
140             return true;
141         }
142 #endif
143         return false;
144     }
145 
146 public:
147 
rfindSep(bool skipTrailing=true)148     Buffer::iterator rfindSep(bool skipTrailing = true) {
149         Buffer::iterator it = end();
150 
151         // Skip trailing separators
152         if (skipTrailing) {
153             while (it != buffer.begin()) {
154                 --it;
155                 if (isSep(*it)) {
156                     // Halt if find the root
157                     if (it == buffer.begin()) {
158                         return it;
159                     }
160                 } else {
161                     break;
162                 }
163             }
164         }
165 
166         // Advance to the last separator
167         while (it != buffer.begin()) {
168             --it;
169             if (isSep(*it)) {
170                 return it;
171             }
172         }
173 
174         return end();
175     }
176 
177     /*
178      * Constructors
179      */
180 
String(void)181     String(void) {
182         buffer.push_back(0);
183     }
184 
String(const char * s)185     String(const char *s) :
186         buffer(s, s + strlen(s) + 1)
187     {}
188 
String(const String & other)189     String(const String &other) :
190         buffer(other.buffer)
191     {}
192 
193     template <class InputIterator>
String(InputIterator first,InputIterator last)194     String(InputIterator first, InputIterator last) :
195         buffer(first, last)
196     {
197         buffer.push_back(0);
198     }
199 
200     /**
201      * From a printf-like format string
202      */
203     static String
format(const char * format,...)204     format(const char *format, ...)
205 #if defined(__MINGW32__)
206     __attribute__ ((format (__MINGW_PRINTF_FORMAT, 1, 2)))
207 #elif  defined(__GNUC__)
208     __attribute__ ((format (printf, 1, 2)))
209 #endif
210     {
211 
212         va_list args;
213 
214         va_start(args, format);
215 
216         int length;
217         va_list args_copy;
218         va_copy(args_copy, args);
219 #ifdef _WIN32
220         /* We need to use _vscprintf to calculate the length as vsnprintf returns -1
221          * if the number of characters to write is greater than count.
222          */
223         length = _vscprintf(format, args_copy);
224 #else
225         char dummy;
226         length = vsnprintf(&dummy, sizeof dummy, format, args_copy);
227 #endif
228         va_end(args_copy);
229 
230         assert(length >= 0);
231         size_t size = length + 1;
232 
233         String path(size);
234 
235         va_start(args, format);
236         vsnprintf(path.buf(), size, format, args);
237         va_end(args);
238 
239         return path;
240     }
241 
242     /*
243      * Conversion to ordinary C strings.
244      */
245 
str(void) const246     const char *str(void) const {
247         assert(buffer.back() == 0);
248         return &buffer[0];
249     }
250 
operator const char*(void) const251     operator const char *(void) const {
252         return str();
253     }
254 
255     /*
256      * Iterators
257      */
258 
259     typedef Buffer::const_iterator const_iterator;
260     typedef Buffer::iterator iterator;
261 
begin(void) const262     const_iterator begin(void) const {
263         return buffer.begin();
264     }
265 
begin(void)266     iterator begin(void) {
267         return buffer.begin();
268     }
269 
end(void) const270     const_iterator end(void) const {
271         const_iterator it = buffer.end();
272         assert(it != buffer.begin());
273         --it; // skip null
274         return it;
275     }
276 
end(void)277     iterator end(void) {
278         iterator it = buffer.end();
279         assert(it != buffer.begin());
280         --it; // skip null
281         return it;
282     }
283 
284     /*
285      * Operations
286      */
287 
insert(iterator position,char c)288     void insert(iterator position, char c) {
289         buffer.insert(position, c);
290     }
291 
292     template <class InputIterator>
insert(iterator position,InputIterator first,InputIterator last)293     void insert(iterator position, InputIterator first, InputIterator last) {
294         buffer.insert(position, first, last);
295     }
296 
insert(iterator position,const char * s)297     void insert(iterator position, const char *s) {
298         assert(s);
299         insert(position, s, s + strlen(s));
300     }
301 
insert(iterator position,const String & other)302     void insert(iterator position, const String & other) {
303         insert(position, other.begin(), other.end());
304     }
305 
append(char c)306     void append(char c) {
307         insert(end(), c);
308     }
309 
310     template <class InputIterator>
append(InputIterator first,InputIterator last)311     void append(InputIterator first, InputIterator last) {
312         insert(end(), first, last);
313     }
314 
append(const char * s)315     void append(const char *s) {
316         insert(end(), s);
317     }
318 
append(const String & other)319     void append(const String & other) {
320         insert(end(), other);
321     }
322 
323     template <class InputIterator>
erase(InputIterator first,InputIterator last)324     void erase(InputIterator first, InputIterator last) {
325         buffer.erase(first, last);
326     }
327 
328     /**
329      * Get a writable buffer with the specified size.
330      *
331      * truncate() must be called after the buffer is written, and before any other
332      * method is called.
333      *
334      * Between the call to buf() and truncate() methods, the `buffer.back() ==
335      * 0` invariant will not hold true.
336      */
buf(size_t size)337     char *buf(size_t size) {
338         buffer.resize(size);
339         return &buffer[0];
340     }
341 
length(void) const342     size_t length(void) const {
343         size_t size = buffer.size();
344         assert(size > 0);
345         assert(buffer[size - 1] == 0);
346         return size - 1;
347     }
348 
349     /**
350      * Truncate the string to the specified length.
351      */
truncate(size_t length)352     void truncate(size_t length) {
353         assert(length < buffer.size());
354         buffer[length] = 0;
355         assert(strlen(&buffer[0]) == length);
356         buffer.resize(length + 1);
357     }
358 
359     /**
360      * Truncate the string to the first zero character.
361      */
truncate(void)362     void truncate(void) {
363         truncate(strlen(&buffer[0]));
364     }
365 
366 
367     /*
368      * Path manipulation
369      */
370 
371     bool
372     exists(void) const;
373 
374     /* Trim directory (leaving base filename).
375      */
trimDirectory(void)376     void trimDirectory(void) {
377         iterator sep = rfindSep();
378         if (sep != end()) {
379             buffer.erase(buffer.begin(), sep + 1);
380         }
381     }
382 
383     /* Trim filename component (leaving containing directory).
384      *
385      * - trailing separators are ignored
386      * - a path with no separator at all yields "."
387      * - a path consisting of just the root directory is left unchanged
388      */
trimFilename(void)389     void trimFilename(void) {
390         iterator sep = rfindSep();
391 
392         // No separator found, so return '.'
393         if (sep == end()) {
394             buffer.resize(2);
395             buffer[0] = '.';
396             buffer[1] = 0;
397             return;
398         }
399 
400         // Root. Nothing to do.
401         if (sep == buffer.begin()) {
402             return;
403         }
404 
405         // Trim filename
406         buffer.erase(sep, end());
407     }
408 
trimExtension(void)409     void trimExtension(void) {
410         iterator dot = rfind('.');
411         if (dot != buffer.end()) {
412             buffer.erase(dot, end());
413         }
414     }
415 
join(const String & other)416     void join(const String & other) {
417         if (length() && end()[-1] != OS_DIR_SEP) {
418             append(OS_DIR_SEP);
419         }
420         append(other.begin(), other.end());
421     }
422 };
423 
424 
425 String getProcessName();
426 String getCurrentDir();
427 
428 String getConfigDir();
429 
430 bool createDirectory(const String &path);
431 
432 bool copyFile(const String &srcFileName, const String &dstFileName, bool override = true);
433 
434 bool removeFile(const String &fileName);
435 
436 String getTemporaryDirectoryPath(void);
437 
438 } /* namespace os */
439 
440