source: trunk/gsinstaller/FilePath.cpp@ 1537

Last change on this file since 1537 was 1536, checked in by sjboddie, 24 years ago

Changes to get compiling on VC++ and gcc

  • Property svn:keywords set to Author Date Id Revision
File size: 7.5 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
197bool FilePath::equals(FilePath &other)
198{
199 if (this->path.compare(other.path) == 0)
200 {
201 return true;
202 }
203
204 return false;
205}
206
207bool FilePath::equals(const char *other)
208{
209 if (strcmp(this->path.c_str(), other) == 0)
210 {
211 return true;
212 }
213
214 return false;
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{
271 File f(this->path);
272 return f.exists();
273}
274
275/**
276 * Get the immediate parent of the given filepath
277 */
278FilePath *FilePath::parent()
279{
280 // TODO: cope with network roots
281 FilePath *reply = new FilePath(this->path);
282
283 // get the last divider
284 unsigned int truncate = reply->path.find_last_of(divider);
285
286 // if it exists, then we can do a normal get parent
287 if (truncate != -1)
288 {
289 // if it's the leftmost one as well then we'll skip it to
290 // give a parent of <drive>:\ as this patches some problems
291 // with windows calls! Note that a parent call to the returned
292 // object in this case will return itself
293 if (truncate == reply->path.find(divider))
294 {
295 truncate ++;
296 }
297 reply->path = reply->path.substr(0, truncate);
298 }
299
300 // if we've got a drive letter only, then append the divider to
301 // patch problematic windows calls; again subsequent parent
302 // calls to our returned object would in effect return itself
303 else if (reply->path[1] == ':' && reply->path.length() == 2 &&
304 isalpha(reply->path[0]))
305 {
306 reply->path = reply->path + dividerStr;
307 }
308
309 // a non-rooted object; return blank
310 else
311 {
312 reply->path = "";
313 }
314
315 reply->is_aRoot = __pathIsAValidRoot(reply->path);
316 return reply;
317}
318
319FilePath *FilePath::append(FilePath &subPath)
320{
321 return new FilePath(this->path, subPath.path);
322}
323
324FilePath *FilePath::append(char *subPath)
325{
326 return new FilePath(this->path, subPath);
327}
328
329const char *FilePath::cString()
330{
331 return this->path.c_str();
332}
333
334string FilePath::pathString()
335{
336 return this->path;
337}
338
339bool FilePath::ensureWriteablePath()
340{
341 //TODO: don't assume that all roots are writeable!
342 if (this->isRoot())
343 {
344 return true;
345 }
346
347 // does this path exist?
348 File thisFile(this->path);
349 bool reply = false;
350
351 // if not, ensure writeable parent
352 if (thisFile.exists() == false)
353 {
354 FilePath *parent;
355
356 // create parent reference
357 parent = this->parent();
358
359 if (parent->equals(thisFile.getFileName()))
360 {
361 delete parent;
362 return true;
363 }
364
365 // if parent is writeable then try to make the directory
366 if (parent->ensureWriteablePath())
367 {
368 // make this directory; if it succeeded we reply positively
369 if (_mkdir(this->path.c_str()) == 0)
370 {
371 reply = true;
372 }
373 }
374
375 // destroy parent reference
376 delete parent;
377 }
378
379 else if (thisFile.isDirectory() == true)
380 {
381 // is this writeable by user?; if so we're okay
382 if (thisFile.isWriteable())
383 {
384 reply = true;
385 }
386 }
387 return reply;
388}
Note: See TracBrowser for help on using the repository browser.