xoreos  0.0.5
filepath.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include <list>
26 
27 #include <boost/algorithm/string.hpp>
28 #include <boost/filesystem.hpp>
29 #include <boost/regex.hpp>
30 
31 #include "src/common/filepath.h"
32 #include "src/common/util.h"
33 #include "src/common/error.h"
34 #include "src/common/encoding.h"
35 #include "src/common/platform.h"
36 
37 // boost-filesystem stuff
38 using boost::filesystem::path;
39 using boost::filesystem::exists;
40 using boost::filesystem::is_regular_file;
41 using boost::filesystem::is_directory;
42 using boost::filesystem::file_size;
43 using boost::filesystem::directory_iterator;
44 using boost::filesystem::create_directories;
45 
46 // boost-string_algo
47 using boost::equals;
48 using boost::iequals;
49 
50 namespace Common {
51 
53  return (exists(p.c_str()) && is_regular_file(p.c_str()));
54 }
55 
57  return (exists(p.c_str()) && is_directory(p.c_str()));
58 }
59 
60 size_t FilePath::getFileSize(const UString &p) {
61  uintmax_t size = (uintmax_t) -1;
62 
63  try {
64  size = file_size(p.c_str());
65  } catch (...) {
66  }
67 
68  if (size == ((uintmax_t) -1)) {
69  warning("Failed to get size of file \"%s\"", p.c_str());
70  return kFileInvalid;
71  }
72 
73  if (size > 0x7FFFFFFF) {
74  warning("Size of file \"%s\" too large", p.c_str());
75  return kFileInvalid;
76  }
77 
78  return size;
79 }
80 
82  path file(p.c_str());
83 
84  return file.filename().string();
85 }
86 
88  path file(p.c_str());
89 
90  return file.stem().string();
91 }
92 
94  path file(p.c_str());
95 
96  return file.extension().string();
97 }
98 
100  path file(p.c_str());
101 
102  file.replace_extension(ext.c_str());
103 
104  return file.string();
105 }
106 
108  path file(p.c_str());
109 
110  return file.parent_path().string();
111 }
112 
114  return boost::filesystem::path(p.c_str()).is_absolute();
115 }
116 
117 static path convertToSlash(const path &p) {
118  const boost::regex bSlash("\\\\");
119  const std::string fSlash("/");
120  const boost::match_flag_type flags(boost::match_default | boost::format_sed);
121 
122  return path(boost::regex_replace(p.string(), bSlash, fSlash, flags));
123 }
124 
126  path source = convertToSlash(p.c_str()).native();
127 
128  // Resolve ~ as the user's home directory
129  path::iterator itr = source.begin();
130  if ((itr != source.end()) && (itr->string() == "~")) {
131  path newSource = getHomeDirectory().c_str();
132 
133  for (++itr; itr != source.end(); ++itr)
134  newSource /= *itr;
135 
136  source = newSource;
137  }
138 
139  return convertToSlash(boost::filesystem::absolute(source)).string().c_str();
140 }
141 
142 UString FilePath::relativize(const UString &basePath, const UString &path) {
143  const Common::UString normPath = normalize(path, false);
144  const Common::UString normBase = normalize(basePath, false);
145 
146  UString relative = "";
147 
148  if (normPath.beginsWith(normBase))
149  relative = normPath.substr(normPath.getPosition(normBase.size() + 1), normPath.end());
150 
151  return relative;
152 }
153 
154 UString FilePath::normalize(const UString &p, bool resolveSymLinks) {
155  boost::filesystem::path source = convertToSlash(p.c_str()).native();
156  boost::filesystem::path result;
157 
158  // Resolve ~ as the user's home directory
159  boost::filesystem::path::iterator itr = source.begin();
160  if ((itr != source.end()) && (itr->string() == "~")) {
161  boost::filesystem::path newSource = getHomeDirectory().c_str();
162 
163  for (++itr; itr != source.end(); ++itr)
164  newSource /= *itr;
165 
166  source = newSource;
167  }
168 
169  bool scan = true;
170  while (scan) {
171  scan = false;
172  result.clear();
173 
174  for (itr = source.begin(); itr != source.end(); ++itr) {
175  // Resolve .
176  if (itr->string() == ".")
177  continue;
178 
179  // Resolve .., but only if symbolic links are also resolved
180  if (resolveSymLinks && (itr->string() == "..")) {
181  if (result != source.root_path())
182  result.remove_filename();
183  continue;
184  }
185 
186  result /= *itr;
187 
188  /* If the resolving of symbolic links was requested, check if the current path
189  * is one. If it is, resolve it and restart normalization.
190  */
191 
192  if (!resolveSymLinks)
193  continue;
194 
195  boost::system::error_code ec;
196  boost::filesystem::path link = boost::filesystem::read_symlink(result, ec);
197 
198  if (!link.empty()) {
199  result.remove_filename();
200 
201  if (link.is_absolute()) {
202  for (++itr; itr != source.end(); ++itr)
203  link /= *itr;
204 
205  source = link;
206 
207  } else {
208  boost::filesystem::path newSource = result;
209 
210  newSource /= link;
211 
212  for (++itr; itr != source.end(); ++itr)
213  newSource /= *itr;
214 
215  source = newSource;
216  }
217 
218  scan = true;
219  break;
220  }
221  }
222  }
223 
224  if (!result.has_root_directory())
225  result = boost::filesystem::absolute(result);
226 
227  return convertToSlash(result).string().c_str();
228 }
229 
230 UString FilePath::canonicalize(const UString &p, bool resolveSymLinks) {
231  return normalize(absolutize(p), resolveSymLinks);
232 }
233 
234 bool FilePath::getSubDirectories(const UString &directory, std::list<UString> &subDirectories) {
235  path dirPath(directory.c_str());
236 
237  try {
238  // Iterate over the directory's contents
239  directory_iterator itEnd;
240  for (directory_iterator itDir(dirPath); itDir != itEnd; ++itDir) {
241  if (is_directory(itDir->status())) {
242  subDirectories.push_back(itDir->path().generic_string());
243  }
244  }
245  } catch (...) {
246  return false;
247  }
248 
249  return true;
250 }
251 
252 static void splitDirectories(const UString &directory, std::list<UString> &dirs) {
253  UString curDir;
254 
255  for (UString::iterator it = directory.begin(); it != directory.end(); ++it) {
256  uint32 c = *it;
257 
258  if (c == '/') {
259  // Found a directory separator, split here
260 
261  // Got a real directory, add it to our list
262  if (!curDir.empty())
263  dirs.push_back(curDir);
264 
265  curDir.clear();
266 
267  } else
268  // Otherwise, just append the current character
269  curDir += c;
270 
271  }
272 
273  // Got trailing data, add it to our list
274  if (!curDir.empty())
275  dirs.push_back(curDir);
276 }
277 
278 static UString findSubDirectory_internal(const UString &directory, const UString &subDirectory,
279  bool caseInsensitive) {
280 
281  try {
282  // Special case: . is the same, current directory
283  if (subDirectory == UString("."))
284  return directory;
285 
286  const path dirPath(directory.c_str());
287 
288  // Special case: .. is the parent directory
289  if (subDirectory == UString("..")) {
290  path parent = dirPath.parent_path();
291  if (parent == dirPath)
292  return "";
293 
294  return parent.generic_string();
295  }
296 
297  // Iterator over the directory's contents
298  directory_iterator itEnd;
299  for (directory_iterator itDir(dirPath); itDir != itEnd; ++itDir) {
300  if (is_directory(itDir->status())) {
301  // It's a directory. Check if it's the one we're looking for
302 
303  if (caseInsensitive) {
304  if (iequals(itDir->path().filename().string(), subDirectory.c_str()))
305  return itDir->path().generic_string();
306  } else {
307  if (equals(itDir->path().filename().string(), subDirectory.c_str()))
308  return itDir->path().generic_string();
309  }
310  }
311  }
312  } catch (...) {
313  }
314 
315  return "";
316 }
317 
318 UString FilePath::findSubDirectory(const UString &directory, const UString &subDirectory,
319  bool caseInsensitive) {
320 
321  if (!exists(directory.c_str()) || !is_directory(directory.c_str()))
322  // Path is either no directory or doesn't exist
323  return "";
324 
325  if (subDirectory.empty())
326  // Subdirectory to look for is empty, return the directory instead
327  return directory;
328 
329  // Split the subDirectory string into actual directories
330  std::list<UString> dirs;
331  splitDirectories(subDirectory, dirs);
332 
333  // Iterate over the directory list to find each successive subdirectory
334  UString curDir = directory;
335  for (std::list<UString>::iterator it = dirs.begin(); it != dirs.end(); ++it)
336  if ((curDir = findSubDirectory_internal(curDir, *it, caseInsensitive)).empty())
337  return "";
338 
339  return curDir;
340 }
341 
343  try {
344  return create_directories(path.c_str());
345  } catch (std::exception &se) {
346  throw Exception(se);
347  }
348 }
349 
351  const boost::regex esc("[\\^\\.\\$\\|\\(\\)\\[\\]\\*\\+\\?\\/\\\\]");
352  const std::string rep("\\\\\\1&");
353 
354  return boost::regex_replace(std::string(str.c_str()), esc, rep, boost::match_default | boost::format_sed);
355 }
356 
358  static const char * const sizes[] = {"B", "K", "M", "G"};
359 
360  double s = size;
361  size_t n = 0;
362 
363  while ((s >= 1024) && ((n + 1) < ARRAYSIZE(sizes))) {
364  n++;
365  s /= 1024;
366  }
367 
368  return UString::format("%.2lf%s", s, sizes[n]);
369 }
370 
373 }
374 
377 }
378 
381 }
382 
384  if (!isAbsolute(file))
385  file = getUserDataDirectory() + "/" + file;
386 
387  return canonicalize(file);
388 }
389 
390 } // End of namespace Common
static UString getUserDataDirectory()
Return the OS-specific path of the user data directory.
Definition: filepath.cpp:379
static UString getHomeDirectory()
Return the OS-specific path of the user&#39;s home directory.
Definition: platform.cpp:180
static UString getHumanReadableSize(size_t size)
Format this file size into a human readable string.
Definition: filepath.cpp:357
static void splitDirectories(const UString &directory, std::list< UString > &dirs)
Definition: filepath.cpp:252
Definition: 2dafile.h:39
A class holding an UTF-8 string.
Definition: ustring.h:48
static UString absolutize(const UString &p)
Return the absolute path.
Definition: filepath.cpp:125
static UString getExtension(const UString &p)
Return a file name&#39;s extension.
Definition: filepath.cpp:93
static bool getSubDirectories(const UString &directory, std::list< UString > &subDirectories)
Collect all direct subdirectories of a directory in a list.
Definition: filepath.cpp:234
bool beginsWith(const UString &with) const
Definition: ustring.cpp:295
static path convertToSlash(const path &p)
Definition: filepath.cpp:117
static UString getUserDataDirectory()
Return the OS-specific path of the user data directory.
Definition: platform.cpp:266
static UString findSubDirectory_internal(const UString &directory, const UString &subDirectory, bool caseInsensitive)
Definition: filepath.cpp:278
static bool isDirectory(const UString &p)
Does specified path exist and is it a directory?
Definition: filepath.cpp:56
iterator getPosition(size_t n) const
Convert a numerical position into an iterator.
Definition: ustring.cpp:501
iterator begin() const
Definition: ustring.cpp:253
#define ARRAYSIZE(x)
Macro which determines the number of entries in a fixed size array.
Definition: util.h:131
Platform-dependant functions, mostly for internal use in the Common namespace.
static UString getConfigDirectory()
Return the OS-specific path of the config directory.
Definition: platform.cpp:206
Basic exceptions to throw.
UString substr(iterator from, iterator to) const
Definition: ustring.cpp:706
utf8::iterator< std::string::const_iterator > iterator
Definition: ustring.h:50
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
static UString getUserDataFile(UString file)
Return a path suitable for writing into.
Definition: filepath.cpp:383
static UString format(const char *s,...) GCC_PRINTF(1
Print formatted data into an UString object, similar to sprintf().
Definition: ustring.cpp:718
static bool isRegularFile(const UString &p)
Does specified path exist and is it a regular file?
Definition: filepath.cpp:52
Utility templates and functions.
static UString escapeStringLiteral(const UString &str)
Escape a string literal for use in a regexp.
Definition: filepath.cpp:350
static UString canonicalize(const UString &p, bool resolveSymLinks=true)
Return the canonical, absolutized and normalized path.
Definition: filepath.cpp:230
Utility functions for working with differing string encodings.
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
StackException Exception
Definition: error.h:59
static UString getDirectory(const UString &p)
Return a path&#39;s directory.
Definition: filepath.cpp:107
void warning(const char *s,...)
Definition: util.cpp:33
static UString getConfigDirectory()
Return the OS-specific path of the config directory.
Definition: filepath.cpp:375
static UString getStem(const UString &p)
Return a file name&#39;s stem.
Definition: filepath.cpp:87
size_t size() const
Return the size of the string, in characters.
Definition: ustring.cpp:241
static bool createDirectories(const UString &path)
Create all directories in this path.
Definition: filepath.cpp:342
static bool isAbsolute(const UString &p)
Is the given string an absolute path?
Definition: filepath.cpp:113
static UString relativize(const UString &basePath, const UString &path)
Return the path relative to the base path.
Definition: filepath.cpp:142
uint32_t uint32
Definition: types.h:204
static size_t getFileSize(const UString &p)
Return a file&#39;s size.
Definition: filepath.cpp:60
static UString normalize(const UString &p, bool resolveSymLinks=true)
Normalize a path.
Definition: filepath.cpp:154
static const size_t kFileInvalid
Definition: filepath.h:35
iterator end() const
Definition: ustring.cpp:257
static UString getHomeDirectory()
Return the OS-specific path of the user&#39;s home directory.
Definition: filepath.cpp:371
void clear()
Clear the string&#39;s contents.
Definition: ustring.cpp:236
static UString getFile(const UString &p)
Return a file name without its path.
Definition: filepath.cpp:81
Utility class for manipulating file paths.
static UString changeExtension(const UString &p, const UString &ext="")
Change a file name&#39;s extension.
Definition: filepath.cpp:99
static UString findSubDirectory(const UString &directory, const UString &subDirectory, bool caseInsensitive=false)
Find a directory&#39;s subdirectory.
Definition: filepath.cpp:318