]> git.cworth.org Git - apitrace/blob - common/os_path.hpp
Split common functionality out of cli.
[apitrace] / common / os_path.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  * Path manipulation.
28  */
29
30 #ifndef _OS_PATH_HPP_
31 #define _OS_PATH_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 class Path {
69 protected:
70     typedef std::vector<char> Buffer;
71     Buffer buffer;
72
73     Buffer::iterator find(char c) {
74         Buffer::iterator it = buffer.begin();
75         /* Why do we make these functions fail on empty paths? */
76         assert(it != buffer.end());
77         while (it != buffer.end()) {
78             if (*it == c) {
79                 return it;
80             }
81             ++it;
82         }
83         return buffer.end();
84     }
85
86     Buffer::iterator rfind(char c) {
87         Buffer::iterator it = buffer.end();
88         assert(it != buffer.begin());
89         --it; // skip null
90         while (it != buffer.begin()) {
91             --it;
92             if (*it == c) {
93                 return it;
94             }
95         }
96         return buffer.end();
97     }
98
99     Path(size_t size) :
100         buffer(size) {
101     }
102
103     char *buf(void) {
104         return &buffer[0];
105     }
106
107 public:
108     Path() {
109         buffer.push_back(0);
110     }
111
112     Path(const char *s) :
113         buffer(s, s + strlen(s) + 1)
114     {}
115
116     template <class InputIterator>
117     Path(InputIterator first, InputIterator last) :
118         buffer(first, last)
119     {
120         buffer.push_back(0);
121     }
122
123     char *buf(size_t size) {
124         buffer.resize(size);
125         return &buffer[0];
126     }
127
128     void trimDirectory(void) {
129         Buffer::iterator sep = rfind(OS_DIR_SEP);
130         if (sep != buffer.end()) {
131             buffer.erase(buffer.begin(), sep + 1);
132         }
133     }
134
135     /* Trim filename component (leaving containing directory).
136      *
137      * This function removes everything after the final path
138      * separator, as well as that separator itself if it is not the
139      * only remaining separator.
140      *
141      * Some specific consequences of the above:
142      *
143      * 1. A path with no separator at all is unchanged.
144      * 2. A path with a trailing separator has only that separator removed
145      * 3. A path of just the root directory is unchaged.
146      */
147     void trimFilename(void) {
148         Buffer::iterator first = find(OS_DIR_SEP);
149         Buffer::iterator last = rfind(OS_DIR_SEP);
150         if (last == buffer.end()) {
151             return;
152         }
153         if (last == first) {
154             buffer.erase(first + 1, buffer.end() - 1);
155         } else {
156             buffer.erase(last, buffer.end() - 1);
157         }
158     }
159
160     void trimExtension(void) {
161         Buffer::iterator dot = rfind('.');
162         if (dot != buffer.end()) {
163             buffer.erase(dot, buffer.end() - 1);
164         }
165     }
166
167     size_t length(void) const {
168         size_t size = buffer.size();
169         assert(size > 0);
170         assert(buffer[size - 1] == 0);
171         return size - 1;
172     }
173
174     void truncate(size_t length) {
175         assert(length < buffer.size());
176         buffer[length] = 0;
177         buffer.resize(length + 1);
178     }
179
180     void truncate(void) {
181         truncate(strlen(str()));
182     }
183
184     const char *str(void) const {
185         assert(buffer[buffer.size() - 1] == 0);
186         return &buffer[0];
187     }
188
189     operator const char *(void) const {
190         return str();
191     }
192
193     void join(const Path & other) {
194         size_t len = length();
195         if (len > 0 && buffer[len - 1] != OS_DIR_SEP) {
196             buffer.insert(buffer.begin() + len++, OS_DIR_SEP);
197         }
198         buffer.insert(buffer.begin() + len, other.buffer.begin(), other.buffer.end() - 1);
199     }
200
201     /**
202      * Create a path from a printf-like format string
203      */
204     static Path
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 _vcsprintf 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         Path 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     bool exists(void) const;
242 };
243
244
245 Path getProcessName();
246 Path getCurrentDir();
247
248
249 } /* namespace os */
250
251 #endif /* _OS_PATH_HPP_ */