source: other-projects/gs2-export-cdrom-installer/trunk/FilePath.cpp@ 29004

Last change on this file since 29004 was 11664, checked in by mdewsnip, 18 years ago

Fixed the line endings... for real this time, I hope.

  • Property svn:keywords set to Author Date Id Revision
File size: 8.8 KB
Line 
1#include "FilePath.h"
2#include "File.h"
3
4#include <string.h>
5#include <ctype.h>
6#include <stdarg.h>
7#include <stdio.h>
8//#include <dir.h>
9#include <direct.h>
10
11static char divider = '\\';
12static char dividerStr[] = "\\";
13
14bool __pathIsAValidRoot(string path)
15{
16 DWORD driveMask;
17 int driveNum;
18
19 if (path.length() > 3)
20 {
21 return false;
22 }
23
24 if (path[1] != ':' || !isalpha(path[0]))
25 {
26 return false;
27 }
28
29 if (isupper(path[0]))
30 {
31 driveNum = path[0] - 'A';
32 }
33
34 else
35 {
36 driveNum = path[0] - 'a';
37 }
38
39 if (driveNum > 25)
40 {
41 return false;
42 }
43
44 driveMask = GetLogicalDrives();
45
46 if ((driveMask & (1 << driveNum)) != (1 << driveNum))
47 {
48 return false;
49 }
50
51 return true;
52}
53
54FilePath::FilePath() {}
55
56FilePath::FilePath(int npaths, ...)
57{
58 va_list list;
59 string* pathArray;
60
61 pathArray = new string[npaths];
62
63 // initialise use of variable parameters
64 va_start(list, npaths);
65
66 if (pathArray)
67 {
68 for (int i = 0; i < npaths; i ++)
69 {
70 // get next parameter (guaranteed to be a char * by caller!!!)
71 pathArray[i] = va_arg(list, char *);
72
73 // discount leading slashes
74 if (pathArray[i][0] == divider)
75 {
76 pathArray[i] = pathArray[i].substr(1);
77 }
78
79 // get rid of terminating slashes if present
80 if (pathArray[i][pathArray[i].length() - 1] == divider)
81 {
82 pathArray[i] = pathArray[i].substr(0, pathArray[i].length() - 1);
83 }
84 }
85 }
86
87 // end of variable arguments
88 va_end(list);
89
90 // construct path in the usual manner
91 if (npaths > 0)
92 {
93 this->path = pathArray[0];
94 }
95
96 for (int i = 1; i < npaths; i ++)
97 {
98 // don't add slashes if suppressed
99 if (pathArray[i][0] != '!')
100 {
101 this->path.append("\\");
102 this->path.append(pathArray[i]);
103 }
104
105 // if slashes suppressed, skip the magic character
106 else
107 {
108 this->path.append(pathArray[i].substr(1));
109 }
110 }
111
112 // destroy temporary array
113 if (pathArray)
114 {
115 delete[] pathArray;
116 }
117
118 // set if this is a valid root (unlikely in this circumstance)
119 this->is_aRoot = __pathIsAValidRoot(this->path);
120}
121
122void FilePath::_init(string filePath, string subPath)
123{
124 // copy the directory path
125 this->path = filePath;
126
127 // if it didn't have a concluding divider, then add one; note that if
128 // filePath is empty, we're not actually going to add any divider at
129 // all
130
131 if (filePath.length() > 0 &&
132 this->path[filePath.length()-1] != divider)
133 {
134 this->path.append(dividerStr);
135 }
136
137 // copy in the subdirectory path, eliminating any leading divider
138 if (subPath[0] != divider)
139 {
140 this->path.append(subPath);
141 }
142 else
143 {
144 this->path.append(subPath.substr(1));
145 }
146
147 // set if this is a valid root (unlikely when joining two paths)
148 this->is_aRoot = __pathIsAValidRoot(this->path);
149}
150
151/**
152 * Construct a Filepath from a directory and a sub-directory path
153 */
154
155FilePath::FilePath(string filePath, string subPath)
156{
157 this->_init(filePath, subPath);
158}
159
160FilePath::FilePath(FilePath &path, string subPath)
161{
162 this->_init(path.pathString(), subPath);
163}
164
165FilePath::FilePath(string filePath)
166{
167 if (filePath.length() < 3)
168 {
169 this->path = filePath;
170 this->path.append("\\");
171 }
172 else
173 {
174 this->path = filePath;
175
176 // trim trailing directory separators
177 if (this->path[this->path.length() - 1] == '\\')
178 {
179 this->path = this->path.substr(0, this->path.length() - 1);
180 }
181 }
182
183 // check for a valid root
184 this->is_aRoot = __pathIsAValidRoot(this->path);
185}
186
187bool FilePath::isEmpty()
188{
189 return this->path.length() == 0;
190}
191
192bool FilePath::isRoot()
193{
194 return this->is_aRoot;
195}
196
197int FilePath::compare(const FilePath &other) const
198{ string::const_iterator thisc = this->path.begin();
199 string::const_iterator otherc = other.path.begin();
200
201 while (thisc != this->path.end() && otherc != other.path.end())
202 { if (toupper(*thisc) != toupper(*otherc))
203 { return (toupper(*thisc) - toupper(*otherc));
204 }
205 thisc ++;
206 otherc ++;
207 }
208
209 return this->path.size() - other.path.size();
210}
211
212int FilePath::compare(const char *other) const
213{
214 return strcmpi(this->path.c_str(), other);
215}
216
217/**
218 * Create a filepath of the drive letter/colon only
219 */
220FilePath *FilePath::rootDrive()
221{
222 FilePath *reply = this->root();
223
224 if (reply->path.length() >= 2 &&
225 reply->path[1] == ':' &&
226 isalpha(reply->path[0])) // if this is a drive letter destination
227 {
228 reply->path = reply->path.substr(0, 2);
229 }
230 else
231 {
232 reply->path = "";
233 }
234 return reply;
235}
236
237/**
238 * Create a filepath of the root of the given location
239 */
240FilePath *FilePath::root()
241{
242 // TODO: cope with network destinations
243 FilePath *reply = new FilePath(this->path);
244
245 unsigned int truncate = reply->path.find(divider);
246 if (truncate != -1) // if it's got a divider then skip past it;
247 // we will assume that it's a drive letter first
248 {
249 truncate ++;
250 reply->path = reply->path.substr(0, truncate);
251 }
252
253 // if we've got a drive, then we'll use it plus the divider
254 else if (reply->path[1] == ':' && reply->path.length() == 2 &&
255 isalpha(reply->path[0]))
256 {
257 reply->path = reply->path + dividerStr;
258 }
259 else
260 {
261 reply->path = "";
262 }
263 return reply;
264}
265
266/**
267 * Just a quick check on the existence of a file.
268 */
269bool FilePath::exists()
270{ OFSTRUCT of;
271 string testpath;
272 bool reply;
273 DWORD attrs;
274
275 if (this->path.length() < 5)
276 { return true;
277 }
278 if (this->path[this->path.length()-1] == '\\')
279 { testpath = this->path.substr(0, this->path.length()-1);
280 }
281 else
282 { testpath = this->path;
283 }
284 attrs = GetFileAttributes((LPCSTR) testpath.c_str());
285 if ((attrs == 0xffffffff) || ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0))
286 { reply = !(OpenFile((LPCSTR) testpath.c_str(), (LPOFSTRUCT) &of, OF_EXIST) == HFILE_ERROR);
287 }
288 else
289 { reply = true;
290 }
291 return reply;
292}
293
294/**
295 * Get the immediate parent of the given filepath
296 */
297FilePath *FilePath::parent()
298{
299 // TODO: cope with network roots
300 FilePath *reply = new FilePath(this->path);
301
302 // get the last divider
303 unsigned int truncate = reply->path.find_last_of(divider);
304
305 // if it exists, then we can do a normal get parent
306 if (truncate != -1)
307 {
308 // if it's the leftmost one as well then we'll skip it to
309 // give a parent of <drive>:\ as this patches some problems
310 // with windows calls! Note that a parent call to the returned
311 // object in this case will return itself
312 if (truncate == reply->path.find(divider))
313 {
314 truncate ++;
315 }
316 reply->path = reply->path.substr(0, truncate);
317 }
318
319 // if we've got a drive letter only, then append the divider to
320 // patch problematic windows calls; again subsequent parent
321 // calls to our returned object would in effect return itself
322 else if (reply->path[1] == ':' && reply->path.length() == 2 &&
323 isalpha(reply->path[0]))
324 {
325 reply->path = reply->path + dividerStr;
326 }
327
328 // a non-rooted object; return blank
329 else
330 {
331 reply->path = "";
332 }
333
334 reply->is_aRoot = __pathIsAValidRoot(reply->path);
335 return reply;
336}
337
338/**
339 * Check that the given path is a child/descendant of this path
340 */
341bool FilePath::isAncestorOf(string pathString)
342{
343 if (this->path == "")
344 return false;
345
346 if (pathString.length() < this->path.length())
347 { return false;
348 }
349 if (pathString.substr(0, this->path.length()) != this->path)
350 { return false;
351 }
352 if (pathString.length() == this->path.length())
353 { return false;
354 }
355 if (pathString[this->path.length()-1] != '\\' &&
356 pathString[this->path.length()] != '\\')
357 { return false;
358 }
359 // can't be a proper ancestor of itself
360 return true;
361}
362
363FilePath *FilePath::append(FilePath &subPath)
364{
365 return new FilePath(this->path, subPath.path);
366}
367
368FilePath *FilePath::append(char *subPath)
369{
370 return new FilePath(this->path, subPath);
371}
372
373const char *FilePath::cString()
374{
375 return this->path.c_str();
376}
377
378string FilePath::pathString()
379{
380 return this->path;
381}
382
383bool FilePath::ensureWriteablePath()
384{
385 //TODO: don't assume that all roots are writeable!
386 if (this->isRoot())
387 {
388 return true;
389 }
390
391 // does this path exist?
392 File thisFile(this->path);
393 bool reply = false;
394
395 // if not, ensure writeable parent
396 if (thisFile.exists() == false)
397 {
398 FilePath *parent;
399
400 // create parent reference
401 parent = this->parent();
402
403 if (*parent == thisFile)
404 {
405 delete parent;
406 return true;
407 }
408
409 // if parent is writeable then try to make the directory
410 if (parent->ensureWriteablePath())
411 {
412 // make this directory; if it succeeded we reply positively
413 if (_mkdir(this->path.c_str()) == 0)
414 {
415 reply = true;
416 }
417 }
418
419 // destroy parent reference
420 delete parent;
421 }
422
423 else if (thisFile.isDirectory() == true)
424 {
425 // is this writeable by user?; if so we're okay
426 if (thisFile.isWriteable() || true)
427 {
428 reply = true;
429 }
430 }
431 return reply;
432}
Note: See TracBrowser for help on using the repository browser.