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 |
|
---|
10 | static char divider = '\\';
|
---|
11 | static char dividerStr[] = "\\";
|
---|
12 |
|
---|
13 | bool __pathIsAValidRoot(string path)
|
---|
14 | { DWORD driveMask;
|
---|
15 | int driveNum;
|
---|
16 |
|
---|
17 | if (path.length() > 3)
|
---|
18 | { return false;
|
---|
19 | }
|
---|
20 |
|
---|
21 | if (path[1] != ':' || !isalpha(path[0]))
|
---|
22 | { return false;
|
---|
23 | }
|
---|
24 | if (isupper(path[0]))
|
---|
25 | { driveNum = path[0] - 'A';
|
---|
26 | }
|
---|
27 | else
|
---|
28 | { driveNum = path[0] - 'a';
|
---|
29 | }
|
---|
30 | if (driveNum > 25)
|
---|
31 | { return false;
|
---|
32 | }
|
---|
33 | driveMask = GetLogicalDrives();
|
---|
34 | if ((driveMask & (1 << driveNum)) != (1 << driveNum))
|
---|
35 | { return false;
|
---|
36 | }
|
---|
37 | return true;
|
---|
38 | }
|
---|
39 |
|
---|
40 | FilePath::FilePath()
|
---|
41 | {
|
---|
42 | }
|
---|
43 |
|
---|
44 | FilePath::FilePath(int npaths, ...)
|
---|
45 | { va_list list;
|
---|
46 | string* pathArray;
|
---|
47 |
|
---|
48 | pathArray = new string[npaths];
|
---|
49 |
|
---|
50 | // initialise use of variable parameters
|
---|
51 | va_start(list, npaths);
|
---|
52 |
|
---|
53 | if (pathArray)
|
---|
54 | { for (int i = 0; i < npaths; i ++)
|
---|
55 | { // get next parameter (guaranteed to be a char * by caller!!!)
|
---|
56 | pathArray[i] = va_arg(list, char *);
|
---|
57 |
|
---|
58 | // discount leading slashes
|
---|
59 | if (pathArray[i][0] == divider)
|
---|
60 | { pathArray[i] = pathArray[i].substr(1);
|
---|
61 | }
|
---|
62 |
|
---|
63 | // get rid of terminating slashes if present
|
---|
64 | if (pathArray[i][pathArray[i].length() - 1] == divider)
|
---|
65 | { pathArray[i] = pathArray[i].substr(0, pathArray[i].length() - 1);
|
---|
66 | }
|
---|
67 | }
|
---|
68 | }
|
---|
69 |
|
---|
70 | // end of variable arguments
|
---|
71 | va_end(list);
|
---|
72 |
|
---|
73 | // construct path in the usual manner
|
---|
74 | if (npaths > 0)
|
---|
75 | { this->path = pathArray[0];
|
---|
76 | }
|
---|
77 | for (int i = 1; i < npaths; i ++)
|
---|
78 | { // don't add slashes if suppressed
|
---|
79 | if (pathArray[i][0] != '!')
|
---|
80 | { this->path.append("\\");
|
---|
81 | this->path.append(pathArray[i]);
|
---|
82 | }
|
---|
83 | // if slashes suppressed, skip the magic character
|
---|
84 | else
|
---|
85 | { this->path.append(pathArray[i].substr(1));
|
---|
86 | }
|
---|
87 | }
|
---|
88 |
|
---|
89 | // destroy temporary array
|
---|
90 | if (pathArray)
|
---|
91 | { delete[] pathArray;
|
---|
92 | }
|
---|
93 |
|
---|
94 | // set if this is a valid root (unlikely in this circumstance)
|
---|
95 | this->is_aRoot = __pathIsAValidRoot(this->path);
|
---|
96 | }
|
---|
97 |
|
---|
98 | /**
|
---|
99 | * Construct a Filepath from a directory and a sub-directory path
|
---|
100 | */
|
---|
101 | FilePath::FilePath(string filePath, string subPath)
|
---|
102 | { // copy the directory path
|
---|
103 | this->path = filePath;
|
---|
104 |
|
---|
105 | // if it didn't have a concluding divider, then add one; note that if
|
---|
106 | // filePath is empty, we're not actually going to add any divider at
|
---|
107 | // all
|
---|
108 | if (filePath.length() > 0 &&
|
---|
109 | this->path[filePath.length()-1] != divider)
|
---|
110 | { this->path.append(dividerStr);
|
---|
111 | }
|
---|
112 |
|
---|
113 | // copy in the subdirectory path, eliminating any leading divider
|
---|
114 | if (subPath[0] != divider)
|
---|
115 | { this->path.append(subPath);
|
---|
116 | }
|
---|
117 | else
|
---|
118 | { this->path.append(subPath.substr(1));
|
---|
119 | }
|
---|
120 |
|
---|
121 | // set if this is a valid root (unlikely when joining two paths)
|
---|
122 | this->is_aRoot = __pathIsAValidRoot(this->path);
|
---|
123 | }
|
---|
124 |
|
---|
125 | FilePath::FilePath(string filePath)
|
---|
126 | { if (filePath.length() < 3)
|
---|
127 | { this->path = filePath;
|
---|
128 | this->path.append("\\");
|
---|
129 | }
|
---|
130 | else
|
---|
131 | { this->path = filePath;
|
---|
132 | }
|
---|
133 |
|
---|
134 | // check for a valid root
|
---|
135 | this->is_aRoot = __pathIsAValidRoot(this->path);
|
---|
136 | }
|
---|
137 |
|
---|
138 | bool FilePath::isEmpty()
|
---|
139 | { return this->path.length() == 0;
|
---|
140 | }
|
---|
141 |
|
---|
142 | bool FilePath::isRoot()
|
---|
143 | { return this->is_aRoot;
|
---|
144 | }
|
---|
145 |
|
---|
146 | bool FilePath::equals(FilePath &other)
|
---|
147 | { if (this->path.compare(other.path) == 0)
|
---|
148 | { return true;
|
---|
149 | }
|
---|
150 | return false;
|
---|
151 | }
|
---|
152 |
|
---|
153 | bool FilePath::equals(const char *other)
|
---|
154 | { if (strcmp(this->path.c_str(), other) == 0)
|
---|
155 | { return true;
|
---|
156 | }
|
---|
157 | return false;
|
---|
158 | }
|
---|
159 |
|
---|
160 | /**
|
---|
161 | * Create a filepath of the drive letter/colon only
|
---|
162 | */
|
---|
163 | FilePath *FilePath::rootDrive()
|
---|
164 | { FilePath *reply = this->root();
|
---|
165 |
|
---|
166 | if (reply->path.length() >= 2 &&
|
---|
167 | reply->path[1] == ':' &&
|
---|
168 | isalpha(reply->path[0])) // if this is a drive letter destination
|
---|
169 | { reply->path = reply->path.substr(0, 2);
|
---|
170 | }
|
---|
171 | else
|
---|
172 | { reply->path = "";
|
---|
173 | }
|
---|
174 | return reply;
|
---|
175 | }
|
---|
176 |
|
---|
177 | /**
|
---|
178 | * Create a filepath of the root of the given location
|
---|
179 | */
|
---|
180 | FilePath *FilePath::root()
|
---|
181 | { // TODO: cope with network destinations
|
---|
182 | FilePath *reply = new FilePath(this->path);
|
---|
183 |
|
---|
184 | unsigned int truncate = reply->path.find(divider);
|
---|
185 | if (truncate != -1) // if it's got a divider then skip past it;
|
---|
186 | // we will assume that it's a drive letter first
|
---|
187 | { truncate ++;
|
---|
188 | reply->path = reply->path.substr(0, truncate);
|
---|
189 | }
|
---|
190 | // if we've got a drive, then we'll use it plus the divider
|
---|
191 | else if (reply->path[1] == ':' && reply->path.length() == 2 &&
|
---|
192 | isalpha(reply->path[0]))
|
---|
193 | { reply->path = reply->path + dividerStr;
|
---|
194 | }
|
---|
195 | else
|
---|
196 | { reply->path = "";
|
---|
197 | }
|
---|
198 | return reply;
|
---|
199 | }
|
---|
200 |
|
---|
201 | /**
|
---|
202 | * Just a quick check on the existence of a file.
|
---|
203 | */
|
---|
204 | bool FilePath::exists()
|
---|
205 | { File f(this->path);
|
---|
206 |
|
---|
207 | return f.exists();
|
---|
208 | }
|
---|
209 |
|
---|
210 | /**
|
---|
211 | * Get the immediate parent of the given filepath
|
---|
212 | */
|
---|
213 | FilePath *FilePath::parent()
|
---|
214 | { // TODO: cope with network roots
|
---|
215 | FilePath *reply = new FilePath(this->path);
|
---|
216 |
|
---|
217 | // get the last divider
|
---|
218 | unsigned int truncate = reply->path.find_last_of(divider);
|
---|
219 |
|
---|
220 | // if it exists, then we can do a normal get parent
|
---|
221 | if (truncate != -1)
|
---|
222 | { // if it's the leftmost one as well then we'll skip it to
|
---|
223 | // give a parent of <drive>:\ as this patches some problems
|
---|
224 | // with windows calls! Note that a parent call to the returned
|
---|
225 | // object in this case will return itself
|
---|
226 | if (truncate == reply->path.find(divider))
|
---|
227 | { truncate ++;
|
---|
228 | }
|
---|
229 | reply->path = reply->path.substr(0, truncate);
|
---|
230 | }
|
---|
231 | // if we've got a drive letter only, then append the divider to
|
---|
232 | // patch problematic windows calls; again subsequent parent
|
---|
233 | // calls to our returned object would in effect return itself
|
---|
234 | else if (reply->path[1] == ':' && reply->path.length() == 2 &&
|
---|
235 | isalpha(reply->path[0]))
|
---|
236 | { reply->path = reply->path + dividerStr;
|
---|
237 | }
|
---|
238 | // a non-rooted object; return blank
|
---|
239 | else
|
---|
240 | { reply->path = "";
|
---|
241 | }
|
---|
242 | reply->is_aRoot = __pathIsAValidRoot(reply->path);
|
---|
243 | return reply;
|
---|
244 | }
|
---|
245 |
|
---|
246 | FilePath *FilePath::append(FilePath &subPath)
|
---|
247 | { return new FilePath(this->path, subPath.path);
|
---|
248 | }
|
---|
249 |
|
---|
250 | FilePath *FilePath::append(char *subPath)
|
---|
251 | { return new FilePath(this->path, subPath);
|
---|
252 | }
|
---|
253 |
|
---|
254 | const char *FilePath::cString()
|
---|
255 | { return this->path.c_str();
|
---|
256 | }
|
---|
257 |
|
---|
258 | string FilePath::pathString()
|
---|
259 | { return this->path;
|
---|
260 | }
|
---|
261 |
|
---|
262 | bool FilePath::ensureWriteablePath()
|
---|
263 | { //TODO: don't assume that all roots are writeable!
|
---|
264 | if (this->isRoot())
|
---|
265 | { return true;
|
---|
266 | }
|
---|
267 |
|
---|
268 | // does this path exist?
|
---|
269 | File thisFile(this->path);
|
---|
270 | bool reply = false;
|
---|
271 |
|
---|
272 | // if not, ensure writeable parent
|
---|
273 | if (thisFile.exists() == false)
|
---|
274 | { FilePath *parent;
|
---|
275 |
|
---|
276 | // create parent reference
|
---|
277 | parent = this->parent();
|
---|
278 | if (parent->equals(thisFile.getFileName()))
|
---|
279 | { delete parent;
|
---|
280 | return true;
|
---|
281 | }
|
---|
282 |
|
---|
283 | // if parent is writeable then try to make the directory
|
---|
284 | if (parent->ensureWriteablePath())
|
---|
285 | { // make this directory; if it succeeded we reply positively
|
---|
286 | if (mkdir(this->path.c_str()) == 0)
|
---|
287 | { reply = true;
|
---|
288 | }
|
---|
289 | }
|
---|
290 | // destroy parent reference
|
---|
291 | delete parent;
|
---|
292 | }
|
---|
293 | else if (thisFile.isDirectory() == true)
|
---|
294 | { // is this writeable by user?; if so we're okay
|
---|
295 | if (thisFile.isWriteable())
|
---|
296 | { reply = true;
|
---|
297 | }
|
---|
298 | }
|
---|
299 |
|
---|
300 | return reply;
|
---|
301 | } |
---|