]> git.cworth.org Git - apitrace/blob - common/os_string.hpp
retrace: Implement glxCopySubBufferMESA
[apitrace] / common / os_string.hpp
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 #ifndef _OS_STRING_HPP_
31 #define _OS_STRING_HPP_
32
33
34 #include <assert.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stddef.h>
38
39 #ifdef __MINGW32__
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);
43 #endif
44
45 #ifndef va_copy
46 #ifdef __va_copy
47 #define va_copy(dest, src) __va_copy((dest), (src))
48 #else
49 #define va_copy(dest, src) (dest) = (src)
50 #endif
51 #endif
52
53 #include <vector>
54
55 #include "os.hpp"
56
57
58 #ifdef _WIN32
59 #define OS_DIR_SEP '\\'
60 #define OS_PATH_SEP ';'
61 #else /* !_WIN32 */
62 #define OS_DIR_SEP '/'
63 #define OS_PATH_SEP ':'
64 #endif /* !_WIN32 */
65
66
67 namespace os {
68
69
70 /**
71  * Class to represent zero-terminated strings, based upon std::vector<char>,
72  * suitable for passing strings or paths to/from OS calls.
73  *
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
81  * strings.
82  *
83  * This class is not, however, a full replacement for std::string, which should
84  * be otherwise used whenever possible.
85  */
86 class String {
87 protected:
88     typedef std::vector<char> Buffer;
89
90     /**
91      * The buffer's last element is always the '\0' character, therefore the
92      * buffer must never be empty.
93      */
94     Buffer buffer;
95
96     Buffer::iterator find(char c) {
97         Buffer::iterator it = buffer.begin();
98         assert(it != buffer.end());
99         while (it != buffer.end()) {
100             if (*it == c) {
101                 return it;
102             }
103             ++it;
104         }
105         return buffer.end();
106     }
107
108     Buffer::iterator rfind(char c) {
109         Buffer::iterator it = buffer.end();
110
111         // Skip trailing '\0'
112         assert(it != buffer.begin());
113         --it;
114         assert(*it == '\0');
115
116         while (it != buffer.begin()) {
117             --it;
118             if (*it == c) {
119                 return it;
120             }
121         }
122
123         return buffer.end();
124     }
125
126     String(size_t size) :
127         buffer(size) {
128     }
129
130     char *buf(void) {
131         return &buffer[0];
132     }
133
134     inline bool
135     isSep(char c) {
136         if (c == '/') {
137             return true;
138         }
139 #ifdef _WIN32
140         if (c == '\\') {
141             return true;
142         }
143 #endif
144         return false;
145     }
146
147 public:
148
149     Buffer::iterator rfindSep(bool skipTrailing = true) {
150         Buffer::iterator it = end();
151
152         // Skip trailing separators
153         if (skipTrailing) {
154             while (it != buffer.begin()) {
155                 --it;
156                 if (isSep(*it)) {
157                     // Halt if find the root
158                     if (it == buffer.begin()) {
159                         return it;
160                     }
161                 } else {
162                     break;
163                 }
164             }
165         }
166
167         // Advance to the last separator
168         while (it != buffer.begin()) {
169             --it;
170             if (isSep(*it)) {
171                 return it;
172             }
173         }
174
175         return end();
176     }
177
178     /*
179      * Constructors
180      */
181
182     String() {
183         buffer.push_back(0);
184     }
185
186     String(const char *s) :
187         buffer(s, s + strlen(s) + 1)
188     {}
189
190     String(const String &other) :
191         buffer(other.buffer)
192     {}
193
194     template <class InputIterator>
195     String(InputIterator first, InputIterator last) :
196         buffer(first, last)
197     {
198         buffer.push_back(0);
199     }
200
201     /**
202      * From a printf-like format string
203      */
204     static String
205     format(const char *format, ...)
206 #ifdef __GNUC__
207     __attribute__ ((format (printf, 1, 2)))
208 #endif
209     {
210
211         va_list args;
212
213         va_start(args, format);
214
215         int length;
216         va_list args_copy;
217         va_copy(args_copy, args);
218 #ifdef _WIN32
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.
221          */
222         length = _vscprintf(format, args_copy);
223 #else
224         char dummy;
225         length = vsnprintf(&dummy, sizeof dummy, format, args_copy);
226 #endif
227         va_end(args_copy);
228
229         assert(length >= 0);
230         size_t size = length + 1;
231
232         String path(size);
233
234         va_start(args, format);
235         vsnprintf(path.buf(), size, format, args);
236         va_end(args);
237
238         return path;
239     }
240
241     /*
242      * Conversion to ordinary C strings.
243      */
244
245     const char *str(void) const {
246         assert(buffer.back() == 0);
247         return &buffer[0];
248     }
249
250     operator const char *(void) const {
251         return str();
252     }
253
254     /*
255      * Iterators
256      */
257
258     typedef Buffer::const_iterator const_iterator;
259     typedef Buffer::iterator iterator;
260
261     const_iterator begin(void) const {
262         return buffer.begin();
263     }
264
265     iterator begin(void) {
266         return buffer.begin();
267     }
268
269     const_iterator end(void) const {
270         const_iterator it = buffer.end();
271         assert(it != buffer.begin());
272         --it; // skip null
273         return it;
274     }
275
276     iterator end(void) {
277         iterator it = buffer.end();
278         assert(it != buffer.begin());
279         --it; // skip null
280         return it;
281     }
282
283     /*
284      * Operations
285      */
286
287     void insert(iterator position, char c) {
288         buffer.insert(position, c);
289     }
290
291     template <class InputIterator>
292     void insert(iterator position, InputIterator first, InputIterator last) {
293         buffer.insert(position, first, last);
294     }
295
296     void insert(iterator position, const char *s) {
297         assert(s);
298         insert(position, s, s + strlen(s));
299     }
300
301     void insert(iterator position, const String & other) {
302         insert(position, other.begin(), other.end());
303     }
304
305     void append(char c) {
306         insert(end(), c);
307     }
308
309     template <class InputIterator>
310     void append(InputIterator first, InputIterator last) {
311         insert(end(), first, last);
312     }
313
314     void append(const char *s) {
315         insert(end(), s);
316     }
317
318     void append(const String & other) {
319         insert(end(), other);
320     }
321
322     template <class InputIterator>
323     void erase(InputIterator first, InputIterator last) {
324         buffer.erase(first, last);
325     }
326
327     /**
328      * Get a writable buffer with the specified size.
329      *
330      * truncate() must be called after the buffer is written, and before any other
331      * method is called.
332      *
333      * Between the call to buf() and truncate() methods, the `buffer.back() ==
334      * 0` invariant will not hold true.
335      */
336     char *buf(size_t size) {
337         buffer.resize(size);
338         return &buffer[0];
339     }
340
341     size_t length(void) const {
342         size_t size = buffer.size();
343         assert(size > 0);
344         assert(buffer[size - 1] == 0);
345         return size - 1;
346     }
347
348     /**
349      * Truncate the string to the specified length.
350      */
351     void truncate(size_t length) {
352         assert(length < buffer.size());
353         buffer[length] = 0;
354         assert(strlen(&buffer[0]) == length);
355         buffer.resize(length + 1);
356     }
357
358     /**
359      * Truncate the string to the first zero character.
360      */
361     void truncate(void) {
362         truncate(strlen(&buffer[0]));
363     }
364
365
366     /*
367      * Path manipulation
368      */
369
370     bool
371     exists(void) const;
372
373     /* Trim directory (leaving base filename).
374      */
375     void trimDirectory(void) {
376         iterator sep = rfindSep();
377         if (sep != end()) {
378             buffer.erase(buffer.begin(), sep + 1);
379         }
380     }
381
382     /* Trim filename component (leaving containing directory).
383      *
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
387      */
388     void trimFilename(void) {
389         iterator sep = rfindSep();
390
391         // No separator found, so return '.'
392         if (sep == end()) {
393             buffer.resize(2);
394             buffer[0] = '.';
395             buffer[1] = 0;
396             return;
397         }
398
399         // Root. Nothing to do.
400         if (sep == buffer.begin()) {
401             return;
402         }
403
404         // Trim filename
405         buffer.erase(sep, end());
406     }
407
408     void trimExtension(void) {
409         iterator dot = rfind('.');
410         if (dot != buffer.end()) {
411             buffer.erase(dot, end());
412         }
413     }
414
415     void join(const String & other) {
416         if (length() && end()[-1] != OS_DIR_SEP) {
417             append(OS_DIR_SEP);
418         }
419         append(other.begin(), other.end());
420     }
421 };
422
423
424 String getProcessName();
425 String getCurrentDir();
426
427 bool createDirectory(const String &path);
428
429 bool copyFile(const String &srcFileName, const String &dstFileName, bool override = true);
430
431 bool removeFile(const String &fileName);
432
433 } /* namespace os */
434
435 #endif /* _OS_STRING_HPP_ */