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
|
---|
106 | if (this->path[filePath.length()-1] != divider)
|
---|
107 | { this->path.append(dividerStr);
|
---|
108 | }
|
---|
109 |
|
---|
110 | // copy in the subdirectory path, eliminating any leading divider
|
---|
111 | if (subPath[0] != divider)
|
---|
112 | { this->path.append(subPath);
|
---|
113 | }
|
---|
114 | else
|
---|
115 | { this->path.append(subPath.substr(1));
|
---|
116 | }
|
---|
117 |
|
---|
118 | // set if this is a valid root (unlikely when joining two paths)
|
---|
119 | this->is_aRoot = __pathIsAValidRoot(this->path);
|
---|
120 | }
|
---|
121 |
|
---|
122 | FilePath::FilePath(string filePath)
|
---|
123 | { if (filePath.length() < 3)
|
---|
124 | { this->path = filePath;
|
---|
125 | this->path.append("\\");
|
---|
126 | }
|
---|
127 | else
|
---|
128 | { this->path = filePath;
|
---|
129 | }
|
---|
130 |
|
---|
131 | // check for a valid root
|
---|
132 | this->is_aRoot = __pathIsAValidRoot(this->path);
|
---|
133 | }
|
---|
134 |
|
---|
135 | bool FilePath::isEmpty()
|
---|
136 | { return this->path.length() == 0;
|
---|
137 | }
|
---|
138 |
|
---|
139 | bool FilePath::isRoot()
|
---|
140 | { return this->is_aRoot;
|
---|
141 | }
|
---|
142 |
|
---|
143 | bool FilePath::equals(FilePath &other)
|
---|
144 | { if (this->path.compare(other.path) == 0)
|
---|
145 | { return true;
|
---|
146 | }
|
---|
147 | return false;
|
---|
148 | }
|
---|
149 |
|
---|
150 | bool FilePath::equals(const char *other)
|
---|
151 | { if (strcmp(this->path.c_str(), other) == 0)
|
---|
152 | { return true;
|
---|
153 | }
|
---|
154 | return false;
|
---|
155 | }
|
---|
156 |
|
---|
157 | /**
|
---|
158 | * Create a filepath of the drive letter/colon only
|
---|
159 | */
|
---|
160 | FilePath *FilePath::rootDrive()
|
---|
161 | { FilePath *reply = this->root();
|
---|
162 |
|
---|
163 | if (reply->path.length() >= 2 &&
|
---|
164 | reply->path[1] == ':' &&
|
---|
165 | isalpha(reply->path[0])) // if this is a drive letter destination
|
---|
166 | { reply->path = reply->path.substr(0, 2);
|
---|
167 | }
|
---|
168 | else
|
---|
169 | { reply->path = "";
|
---|
170 | }
|
---|
171 | return reply;
|
---|
172 | }
|
---|
173 |
|
---|
174 | /**
|
---|
175 | * Create a filepath of the root of the given location
|
---|
176 | */
|
---|
177 | FilePath *FilePath::root()
|
---|
178 | { // TODO: cope with network destinations
|
---|
179 | FilePath *reply = new FilePath(this->path);
|
---|
180 |
|
---|
181 | unsigned int truncate = reply->path.find(divider);
|
---|
182 | if (truncate != -1) // if it's got a divider then skip past it;
|
---|
183 | // we will assume that it's a drive letter first
|
---|
184 | { truncate ++;
|
---|
185 | reply->path = reply->path.substr(0, truncate);
|
---|
186 | }
|
---|
187 | // if we've got a drive, then we'll use it plus the divider
|
---|
188 | else if (reply->path[1] == ':' && reply->path.length() == 2 &&
|
---|
189 | isalpha(reply->path[0]))
|
---|
190 | { reply->path = reply->path + dividerStr;
|
---|
191 | }
|
---|
192 | else
|
---|
193 | { reply->path = "";
|
---|
194 | }
|
---|
195 | return reply;
|
---|
196 | }
|
---|
197 |
|
---|
198 | /**
|
---|
199 | * Just a quick check on the existence of a file.
|
---|
200 | */
|
---|
201 | bool FilePath::exists()
|
---|
202 | { File f(this->path);
|
---|
203 |
|
---|
204 | return f.exists();
|
---|
205 | }
|
---|
206 |
|
---|
207 | /**
|
---|
208 | * Get the immediate parent of the given filepath
|
---|
209 | */
|
---|
210 | FilePath *FilePath::parent()
|
---|
211 | { // TODO: cope with network roots
|
---|
212 | FilePath *reply = new FilePath(this->path);
|
---|
213 |
|
---|
214 | // get the last divider
|
---|
215 | unsigned int truncate = reply->path.find_last_of(divider);
|
---|
216 |
|
---|
217 | // if it exists, then we can do a normal get parent
|
---|
218 | if (truncate != -1)
|
---|
219 | { // if it's the leftmost one as well then we'll skip it to
|
---|
220 | // give a parent of <drive>:\ as this patches some problems
|
---|
221 | // with windows calls! Note that a parent call to the returned
|
---|
222 | // object in this case will return itself
|
---|
223 | if (truncate == reply->path.find(divider))
|
---|
224 | { truncate ++;
|
---|
225 | }
|
---|
226 | reply->path = reply->path.substr(0, truncate);
|
---|
227 | }
|
---|
228 | // if we've got a drive letter only, then append the divider to
|
---|
229 | // patch problematic windows calls; again subsequent parent
|
---|
230 | // calls to our returned object would in effect return itself
|
---|
231 | else if (reply->path[1] == ':' && reply->path.length() == 2 &&
|
---|
232 | isalpha(reply->path[0]))
|
---|
233 | { reply->path = reply->path + dividerStr;
|
---|
234 | }
|
---|
235 | // a non-rooted object; return blank
|
---|
236 | else
|
---|
237 | { reply->path = "";
|
---|
238 | }
|
---|
239 | reply->is_aRoot = __pathIsAValidRoot(reply->path);
|
---|
240 | return reply;
|
---|
241 | }
|
---|
242 |
|
---|
243 | FilePath *FilePath::append(FilePath &subPath)
|
---|
244 | { return new FilePath(this->path, subPath.path);
|
---|
245 | }
|
---|
246 |
|
---|
247 | FilePath *FilePath::append(char *subPath)
|
---|
248 | { return new FilePath(this->path, subPath);
|
---|
249 | }
|
---|
250 |
|
---|
251 | const char *FilePath::cString()
|
---|
252 | { return this->path.c_str();
|
---|
253 | }
|
---|
254 |
|
---|
255 | string FilePath::pathString()
|
---|
256 | { return this->path;
|
---|
257 | }
|
---|
258 |
|
---|
259 | bool FilePath::ensureWriteablePath()
|
---|
260 | { //TODO: don't assume that all roots are writeable!
|
---|
261 | if (this->isRoot())
|
---|
262 | { return true;
|
---|
263 | }
|
---|
264 |
|
---|
265 | // does this path exist?
|
---|
266 | File thisFile(this->path);
|
---|
267 | bool reply = false;
|
---|
268 |
|
---|
269 | // if not, ensure writeable parent
|
---|
270 | if (thisFile.exists() == false)
|
---|
271 | { FilePath *parent;
|
---|
272 |
|
---|
273 | // create parent reference
|
---|
274 | parent = this->parent();
|
---|
275 | if (parent->equals(thisFile.getFileName()))
|
---|
276 | { delete parent;
|
---|
277 | return true;
|
---|
278 | }
|
---|
279 |
|
---|
280 | // if parent is writeable then try to make the directory
|
---|
281 | if (parent->ensureWriteablePath())
|
---|
282 | { // make this directory; if it succeeded we reply positively
|
---|
283 | if (mkdir(this->path.c_str()) == 0)
|
---|
284 | { reply = true;
|
---|
285 | }
|
---|
286 | }
|
---|
287 | // destroy parent reference
|
---|
288 | delete parent;
|
---|
289 | }
|
---|
290 | else if (thisFile.isDirectory() == true)
|
---|
291 | { // is this writeable by user?; if so we're okay
|
---|
292 | if (thisFile.isWriteable())
|
---|
293 | { reply = true;
|
---|
294 | }
|
---|
295 | }
|
---|
296 |
|
---|
297 | return reply;
|
---|
298 | } |
---|