[1397] | 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 | } |
---|