]> git.cworth.org Git - apitrace/blob - common/os_string.hpp
Tweak path splitting.
[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 #else /* !_WIN32 */
61 #define OS_DIR_SEP '/'
62 #endif /* !_WIN32 */
63
64
65 namespace os {
66
67
68 /**
69  * Vector based zero-terminate string, suitable for passing strings or paths
70  * to/from OS calls.
71  */
72 class String {
73 protected:
74     typedef std::vector<char> Buffer;
75
76     /**
77      * The buffer's last element is always the '\0' character, therefore the
78      * buffer must never be empty.
79      */
80     Buffer buffer;
81
82     Buffer::iterator find(char c) {
83         Buffer::iterator it = buffer.begin();
84         assert(it != buffer.end());
85         while (it != buffer.end()) {
86             if (*it == c) {
87                 return it;
88             }
89             ++it;
90         }
91         return buffer.end();
92     }
93
94     Buffer::iterator rfind(char c) {
95         Buffer::iterator it = buffer.end();
96         while (it != buffer.begin()) {
97             --it;
98             if (*it == c) {
99                 return it;
100             }
101         }
102         return buffer.end();
103     }
104
105     String(size_t size) :
106         buffer(size) {
107     }
108
109     char *buf(void) {
110         return &buffer[0];
111     }
112
113     inline bool
114     isSep(char c) {
115         if (c == '/') {
116             return true;
117         }
118 #ifdef _WIN32
119         if (c == '\\') {
120             return true;
121         }
122 #endif
123         return false;
124     }
125
126     Buffer::iterator rfindSep(void) {
127         Buffer::iterator it = buffer.end();
128
129         // Skip trailing separators
130         while (it != buffer.begin() && isSep(*it)) {
131             --it;
132         }
133
134         // Advance to the last separator
135         while (it != buffer.begin()) {
136             if (isSep(*it)) {
137                 return it;
138             }
139             --it;
140         }
141
142         return buffer.end();
143     }
144
145
146 public:
147
148     /*
149      * Constructors
150      */
151
152     String() {
153         buffer.push_back(0);
154     }
155
156     String(const char *s) :
157         buffer(s, s + strlen(s) + 1)
158     {}
159
160     String(const String &other) :
161         buffer(other.buffer)
162     {}
163
164     template <class InputIterator>
165     String(InputIterator first, InputIterator last) :
166         buffer(first, last)
167     {
168         buffer.push_back(0);
169     }
170
171     /**
172      * From a printf-like format string
173      */
174     static String
175     format(const char *format, ...)
176 #ifdef __GNUC__
177     __attribute__ ((format (printf, 1, 2)))
178 #endif
179     {
180
181         va_list args;
182
183         va_start(args, format);
184
185         int length;
186         va_list args_copy;
187         va_copy(args_copy, args);
188 #ifdef _WIN32
189         /* We need to use _vcsprintf to calculate the length as vsnprintf returns -1
190          * if the number of characters to write is greater than count.
191          */
192         length = _vscprintf(format, args_copy);
193 #else
194         char dummy;
195         length = vsnprintf(&dummy, sizeof dummy, format, args_copy);
196 #endif
197         va_end(args_copy);
198
199         assert(length >= 0);
200         size_t size = length + 1;
201
202         String path(size);
203
204         va_start(args, format);
205         vsnprintf(path.buf(), size, format, args);
206         va_end(args);
207
208         return path;
209     }
210
211     /*
212      * Conversion to ordinary C strings.
213      */
214
215     const char *str(void) const {
216         assert(buffer.back() == 0);
217         return &buffer[0];
218     }
219
220     operator const char *(void) const {
221         return str();
222     }
223
224     /*
225      * Iterators
226      */
227
228     typedef Buffer::const_iterator const_iterator;
229     typedef Buffer::iterator iterator;
230
231     const_iterator begin(void) const {
232         return buffer.begin();
233     }
234
235     iterator begin(void) {
236         return buffer.begin();
237     }
238
239     const_iterator end(void) const {
240         const_iterator it = buffer.end();
241         assert(it != buffer.begin());
242         --it; // skip null
243         return it;
244     }
245
246     iterator end(void) {
247         iterator it = buffer.end();
248         assert(it != buffer.begin());
249         --it; // skip null
250         return it;
251     }
252
253     /*
254      * Operations
255      */
256
257     void insert(iterator position, char c) {
258         buffer.insert(position, c);
259     }
260
261     template <class InputIterator>
262     void insert(iterator position, InputIterator first, InputIterator last) {
263         buffer.insert(position, first, last);
264     }
265
266     void insert(iterator position, const char *s) {
267         assert(s);
268         insert(position, s, s + strlen(s));
269     }
270
271     void insert(iterator position, const String & other) {
272         insert(position, other.begin(), other.end());
273     }
274
275     void append(char c) {
276         insert(end(), c);
277     }
278
279     template <class InputIterator>
280     void append(InputIterator first, InputIterator last) {
281         insert(end(), first, last);
282     }
283
284     void append(const char *s) {
285         insert(end(), s);
286     }
287
288     void append(const String & other) {
289         insert(end(), other);
290     }
291
292     char *buf(size_t size) {
293         buffer.resize(size);
294         return &buffer[0];
295     }
296
297     size_t length(void) const {
298         size_t size = buffer.size();
299         assert(size > 0);
300         assert(buffer[size - 1] == 0);
301         return size - 1;
302     }
303
304     void truncate(size_t length) {
305         assert(length < buffer.size());
306         buffer[length] = 0;
307         buffer.resize(length + 1);
308     }
309
310     void truncate(void) {
311         truncate(strlen(str()));
312     }
313
314
315     /*
316      * Path manipulation
317      */
318
319     bool
320     exists(void) const;
321
322     /* Trim directory (leaving base filename).
323      */
324     void trimDirectory(void) {
325         iterator sep = rfindSep();
326         if (sep != buffer.end()) {
327             buffer.erase(buffer.begin(), sep + 1);
328         }
329     }
330
331     /* Trim filename component (leaving containing directory).
332      *
333      * - trailing separators are ignored
334      * - a path with no separator at all yields "."
335      * - a path consisting of just the root directory is left unchanged
336      */
337     void trimFilename(void) {
338         iterator sep = rfindSep();
339
340         // No separator found, so return '.'
341         if (sep == buffer.end()) {
342             buffer.resize(2);
343             buffer[0] = '.';
344             buffer[1] = 0;
345             return;
346         }
347
348         // Root. Nothing to do.
349         if (sep == buffer.begin()) {
350             return;
351         }
352
353         // Trim filename
354         buffer.erase(sep, end());
355     }
356
357     void trimExtension(void) {
358         iterator dot = rfind('.');
359         if (dot != buffer.end()) {
360             buffer.erase(dot, end());
361         }
362     }
363
364     void join(const String & other) {
365         if (length() && end()[-1] != OS_DIR_SEP) {
366             append(OS_DIR_SEP);
367         }
368         append(other.begin(), other.end());
369     }
370 };
371
372
373 String getProcessName();
374 String getCurrentDir();
375
376
377 } /* namespace os */
378
379 #endif /* _OS_STRING_HPP_ */