#include "FilePath.h" #include "File.h" #include #include #include #include #include static char divider = '\\'; static char dividerStr[] = "\\"; bool __pathIsAValidRoot(string path) { DWORD driveMask; int driveNum; if (path.length() > 3) { return false; } if (path[1] != ':' || !isalpha(path[0])) { return false; } if (isupper(path[0])) { driveNum = path[0] - 'A'; } else { driveNum = path[0] - 'a'; } if (driveNum > 25) { return false; } driveMask = GetLogicalDrives(); if ((driveMask & (1 << driveNum)) != (1 << driveNum)) { return false; } return true; } FilePath::FilePath() { } FilePath::FilePath(int npaths, ...) { va_list list; string* pathArray; pathArray = new string[npaths]; // initialise use of variable parameters va_start(list, npaths); if (pathArray) { for (int i = 0; i < npaths; i ++) { // get next parameter (guaranteed to be a char * by caller!!!) pathArray[i] = va_arg(list, char *); // discount leading slashes if (pathArray[i][0] == divider) { pathArray[i] = pathArray[i].substr(1); } // get rid of terminating slashes if present if (pathArray[i][pathArray[i].length() - 1] == divider) { pathArray[i] = pathArray[i].substr(0, pathArray[i].length() - 1); } } } // end of variable arguments va_end(list); // construct path in the usual manner if (npaths > 0) { this->path = pathArray[0]; } for (int i = 1; i < npaths; i ++) { // don't add slashes if suppressed if (pathArray[i][0] != '!') { this->path.append("\\"); this->path.append(pathArray[i]); } // if slashes suppressed, skip the magic character else { this->path.append(pathArray[i].substr(1)); } } // destroy temporary array if (pathArray) { delete[] pathArray; } // set if this is a valid root (unlikely in this circumstance) this->is_aRoot = __pathIsAValidRoot(this->path); } /** * Construct a Filepath from a directory and a sub-directory path */ FilePath::FilePath(string filePath, string subPath) { // copy the directory path this->path = filePath; // if it didn't have a concluding divider, then add one if (this->path[filePath.length()-1] != divider) { this->path.append(dividerStr); } // copy in the subdirectory path, eliminating any leading divider if (subPath[0] != divider) { this->path.append(subPath); } else { this->path.append(subPath.substr(1)); } // set if this is a valid root (unlikely when joining two paths) this->is_aRoot = __pathIsAValidRoot(this->path); } FilePath::FilePath(string filePath) { if (filePath.length() < 3) { this->path = filePath; this->path.append("\\"); } else { this->path = filePath; } // check for a valid root this->is_aRoot = __pathIsAValidRoot(this->path); } bool FilePath::isEmpty() { return this->path.length() == 0; } bool FilePath::isRoot() { return this->is_aRoot; } bool FilePath::equals(FilePath &other) { if (this->path.compare(other.path) == 0) { return true; } return false; } bool FilePath::equals(const char *other) { if (strcmp(this->path.c_str(), other) == 0) { return true; } return false; } /** * Create a filepath of the drive letter/colon only */ FilePath *FilePath::rootDrive() { FilePath *reply = this->root(); if (reply->path.length() >= 2 && reply->path[1] == ':' && isalpha(reply->path[0])) // if this is a drive letter destination { reply->path = reply->path.substr(0, 2); } else { reply->path = ""; } return reply; } /** * Create a filepath of the root of the given location */ FilePath *FilePath::root() { // TODO: cope with network destinations FilePath *reply = new FilePath(this->path); unsigned int truncate = reply->path.find(divider); if (truncate != -1) // if it's got a divider then skip past it; // we will assume that it's a drive letter first { truncate ++; reply->path = reply->path.substr(0, truncate); } // if we've got a drive, then we'll use it plus the divider else if (reply->path[1] == ':' && reply->path.length() == 2 && isalpha(reply->path[0])) { reply->path = reply->path + dividerStr; } else { reply->path = ""; } return reply; } /** * Just a quick check on the existence of a file. */ bool FilePath::exists() { File f(this->path); return f.exists(); } /** * Get the immediate parent of the given filepath */ FilePath *FilePath::parent() { // TODO: cope with network roots FilePath *reply = new FilePath(this->path); // get the last divider unsigned int truncate = reply->path.find_last_of(divider); // if it exists, then we can do a normal get parent if (truncate != -1) { // if it's the leftmost one as well then we'll skip it to // give a parent of :\ as this patches some problems // with windows calls! Note that a parent call to the returned // object in this case will return itself if (truncate == reply->path.find(divider)) { truncate ++; } reply->path = reply->path.substr(0, truncate); } // if we've got a drive letter only, then append the divider to // patch problematic windows calls; again subsequent parent // calls to our returned object would in effect return itself else if (reply->path[1] == ':' && reply->path.length() == 2 && isalpha(reply->path[0])) { reply->path = reply->path + dividerStr; } // a non-rooted object; return blank else { reply->path = ""; } reply->is_aRoot = __pathIsAValidRoot(reply->path); return reply; } FilePath *FilePath::append(FilePath &subPath) { return new FilePath(this->path, subPath.path); } FilePath *FilePath::append(char *subPath) { return new FilePath(this->path, subPath); } const char *FilePath::cString() { return this->path.c_str(); } string FilePath::pathString() { return this->path; } bool FilePath::ensureWriteablePath() { //TODO: don't assume that all roots are writeable! if (this->isRoot()) { return true; } // does this path exist? File thisFile(this->path); bool reply = false; // if not, ensure writeable parent if (thisFile.exists() == false) { FilePath *parent; // create parent reference parent = this->parent(); if (parent->equals(thisFile.getFileName())) { delete parent; return true; } // if parent is writeable then try to make the directory if (parent->ensureWriteablePath()) { // make this directory; if it succeeded we reply positively if (mkdir(this->path.c_str()) == 0) { reply = true; } } // destroy parent reference delete parent; } else if (thisFile.isDirectory() == true) { // is this writeable by user?; if so we're okay if (thisFile.isWriteable()) { reply = true; } } return reply; }