Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
path.h
Go to the documentation of this file.
1 /* Copyright (c) 2013 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * Path string class, similar to boost::filesystem::basic_path.
25  */
26 
27 // notes:
28 // - this module is independent of lib/file so that it can be used from
29 // other code without pulling in the entire file manager.
30 // - there is no restriction on buffer lengths except the underlying OS.
31 // input buffers must not exceed PATH_MAX chars, while outputs
32 // must hold at least that much.
33 // - unless otherwise mentioned, all functions are intended to work with
34 // native and VFS paths.
35 // when reading, both '/' and SYS_DIR_SEP are accepted; '/' is written.
36 
37 #ifndef INCLUDED_PATH
38 #define INCLUDED_PATH
39 
40 #if CONFIG_ENABLE_BOOST
41 # include "boost/functional/hash.hpp"
42 #endif
43 
44 #include <cstring>
45 
46 namespace ERR
47 {
48  const Status PATH_CHARACTER_ILLEGAL = -100300;
49  const Status PATH_CHARACTER_UNSAFE = -100301;
50  const Status PATH_NOT_FOUND = -100302;
51  const Status PATH_MIXED_SEPARATORS = -100303;
52 }
53 
54 /**
55  * is s2 a subpath of s1, or vice versa? (equal counts as subpath)
56  *
57  * @param s1, s2 comparand strings
58  * @return bool
59  **/
60 LIB_API bool path_is_subpath(const wchar_t* s1, const wchar_t* s2);
61 
62 /**
63  * Get the path component of a path.
64  * Skips over all characters up to the last dir separator, if any.
65  *
66  * @param path Input path.
67  * @return pointer to path component within <path>.
68  **/
69 LIB_API const wchar_t* path_name_only(const wchar_t* path);
70 
71 
72 // NB: there is a need for 'generic' paths (e.g. for Trace entry / archive pathnames).
73 // converting between specialized variants via c_str would be inefficient, and the
74 // Os/VfsPath typedefs are hopefully sufficient to avoid errors.
75 class Path
76 {
77 public:
78  typedef std::wstring String;
79 
80  Path()
81  {
83  }
84 
85  Path(const char* p)
86  : path((const unsigned char*)p, (const unsigned char*)p+strlen(p))
87  // interpret bytes as unsigned; makes no difference for ASCII,
88  // and ensures OsPath on Unix will only contain values 0 <= c < 0x100
89  {
91  }
92 
93  Path(const wchar_t* p)
94  : path(p, p+wcslen(p))
95  {
97  }
98 
99  Path(const std::string& s)
100  : path((const unsigned char*)s.c_str(), (const unsigned char*)s.c_str()+s.length())
101  {
102  DetectSeparator();
103  }
104 
105  Path(const std::wstring& s)
106  : path(s)
107  {
108  DetectSeparator();
109  }
110 
111  Path& operator=(const Path& rhs)
112  {
113  path = rhs.path;
114  DetectSeparator(); // (warns if separators differ)
115  return *this;
116  }
117 
118  bool empty() const
119  {
120  return path.empty();
121  }
122 
123  const String& string() const
124  {
125  return path;
126  }
127 
128  bool operator<(const Path& rhs) const
129  {
130  return path < rhs.path;
131  }
132 
133  bool operator==(const Path& rhs) const
134  {
135  return path == rhs.path;
136  }
137 
138  bool operator!=(const Path& rhs) const
139  {
140  return !operator==(rhs);
141  }
142 
143  bool IsDirectory() const
144  {
145  if(empty()) // (ensure length()-1 is safe)
146  return true; // (the VFS root directory is represented as an empty string)
147  return path[path.length()-1] == separator;
148  }
149 
150  Path Parent() const
151  {
152  const size_t idxSlash = path.find_last_of(separator);
153  if(idxSlash == String::npos)
154  return L"";
155  return path.substr(0, idxSlash);
156  }
157 
158  Path Filename() const
159  {
160  const size_t idxSlash = path.find_last_of(separator);
161  if(idxSlash == String::npos)
162  return path;
163  return path.substr(idxSlash+1);
164  }
165 
166  Path Basename() const
167  {
168  const Path filename = Filename();
169  const size_t idxDot = filename.string().find_last_of('.');
170  if(idxDot == String::npos)
171  return filename;
172  return filename.string().substr(0, idxDot);
173  }
174 
175  // (Path return type allows callers to use our operator==)
176  Path Extension() const
177  {
178  const Path filename = Filename();
179  const size_t idxDot = filename.string().find_last_of('.');
180  if(idxDot == String::npos)
181  return Path();
182  return filename.string().substr(idxDot);
183  }
184 
186  {
187  return Parent() / Path(Basename().string() + extension.string());
188  }
189 
190  Path operator/(Path rhs) const
191  {
192  Path ret = *this;
193  if(ret.path.empty()) // (empty paths assume '/')
194  ret.separator = rhs.separator;
195  if(!ret.IsDirectory())
196  ret.path += ret.separator;
197 
198  if(rhs.path.find((ret.separator == '/')? '\\' : '/') != String::npos)
199  {
201  rhs.PrintToDebugOutput();
203  }
204  ret.path += rhs.path;
205  return ret;
206  }
207 
208  /**
209  * Return the path before the common part of both paths
210  * @param other Indicates the start of the path which should be removed
211  * @note other should be a VfsPath, while this should be an OsPath
212  */
213  Path BeforeCommon(Path other) const
214  {
215  Path ret = *this;
216  if(ret.empty() || other.empty())
217  return L"";
218 
219  // Convert the separator to allow for string comparison
220  if(other.separator != ret.separator)
221  replace(other.path.begin(), other.path.end(), other.separator, ret.separator);
222 
223  const size_t idx = ret.path.rfind(other.path);
224  if(idx == String::npos)
225  return L"";
226 
227  return path.substr(0, idx);
228  }
229 
230  static Status Validate(String::value_type c);
231 
232 private:
233  void PrintToDebugOutput() const
234  {
235  debug_printf(L"Path %ls, separator %c\n", path.c_str(), separator);
236  }
237 
239  {
240  const size_t idxBackslash = path.find('\\');
241 
242  if(path.find('/') != String::npos && idxBackslash != String::npos)
243  {
246  }
247 
248  // (default to '/' for empty strings)
249  separator = (idxBackslash == String::npos)? '/' : '\\';
250  }
251 
253 
254  // note: ideally, path strings would only contain '/' or even SYS_DIR_SEP.
255  // however, Windows-specific code (e.g. the sound driver detection)
256  // uses these routines with '\\' strings. the boost::filesystem approach of
257  // converting them all to '/' and then back via external_file_string is
258  // annoying and inefficient. we allow either type of separators,
259  // appending whichever was first encountered. when modifying the path,
260  // we ensure the same separator is used.
261  wchar_t separator;
262 };
263 
264 static inline std::wostream& operator<<(std::wostream& s, const Path& path)
265 {
266  s << path.string();
267  return s;
268 }
269 
270 static inline std::wistream& operator>>(std::wistream& s, Path& path)
271 {
272  Path::String string;
273  s >> string;
274  path = Path(string);
275  return s;
276 }
277 
278 #if CONFIG_ENABLE_BOOST
279 
280 namespace boost {
281 
282 template<>
283 struct hash<Path> : std::unary_function<Path, std::size_t>
284 {
285  std::size_t operator()(const Path& path) const
286  {
287  return hash_value(path.string());
288  }
289 };
290 
291 }
292 
293 #endif // #if CONFIG_ENABLE_BOOST
294 
295 #endif // #ifndef INCLUDED_PATH
void DetectSeparator()
Definition: path.h:238
Path Filename() const
Definition: path.h:158
bool IsDirectory() const
Definition: path.h:143
size_t hash_value(const CStrIntern &v)
const Status PATH_CHARACTER_ILLEGAL
Definition: path.h:48
Path Parent() const
Definition: path.h:150
Path(const char *p)
Definition: path.h:85
void PrintToDebugOutput() const
Definition: path.h:233
Path BeforeCommon(Path other) const
Return the path before the common part of both paths.
Definition: path.h:213
Path Basename() const
Definition: path.h:166
const Status PATH_CHARACTER_UNSAFE
Definition: path.h:49
wchar_t separator
Definition: path.h:261
bool operator!=(const Path &rhs) const
Definition: path.h:138
Definition: path.h:75
String path
Definition: path.h:252
const String & string() const
Definition: path.h:123
Path(const wchar_t *p)
Definition: path.h:93
std::wstring String
Definition: path.h:78
bool operator==(const Path &rhs) const
Definition: path.h:133
i64 Status
Error handling system.
Definition: status.h:171
Path operator/(Path rhs) const
Definition: path.h:190
const Status PATH_MIXED_SEPARATORS
Definition: path.h:51
Path(const std::wstring &s)
Definition: path.h:105
Path(const std::string &s)
Definition: path.h:99
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Definition: debug.h:331
bool path_is_subpath(const wchar_t *s1, const wchar_t *s2)
is s2 a subpath of s1, or vice versa? (equal counts as subpath)
Definition: path.cpp:51
static Status Validate(String::value_type c)
Definition: path.cpp:99
const char * extension
Definition: mongoose.cpp:1736
bool empty() const
Definition: path.h:118
Path ChangeExtension(Path extension) const
Definition: path.h:185
Path Extension() const
Definition: path.h:176
static std::wistream & operator>>(std::wistream &s, Path &path)
Definition: path.h:270
const wchar_t * path_name_only(const wchar_t *path)
Get the path component of a path.
Definition: path.cpp:85
bool operator<(const Path &rhs) const
Definition: path.h:128
Path & operator=(const Path &rhs)
Definition: path.h:111
Path()
Definition: path.h:80
static std::wostream & operator<<(std::wostream &s, const Path &path)
Definition: path.h:264
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
const Status PATH_NOT_FOUND
Definition: path.h:50