Vault  4.1
vfsnode.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved.
00003 This file is part of the Code Vault version 4.1
00004 http://www.bombaydigital.com/
00005 License: MIT. See LICENSE.md in the Vault top level directory.
00006 */
00007 
00010 #include "vfsnode.h"
00011 
00012 #include "vexception.h"
00013 #include "vinstant.h"
00014 #include "vbufferedfilestream.h"
00015 #include "vtextiostream.h"
00016 #include "vbinaryiostream.h"
00017 
00018 // VListNodeInfoCallback -----------------------------------------------------
00019 
00023 class VFSNodeListCallback : public VDirectoryIterationCallback {
00024     public:
00025 
00026         VFSNodeListCallback(VFSNodeVector& nodeList) : VDirectoryIterationCallback(), mNodeList(nodeList) {}
00027         virtual ~VFSNodeListCallback() {}
00028 
00029         virtual bool handleNextNode(const VFSNode& node);
00030 
00031     private:
00032 
00033         // Prevent copy construction and assignment.
00034         VFSNodeListCallback(const VFSNodeListCallback& other);
00035         VFSNodeListCallback& operator=(const VFSNodeListCallback& other);
00036 
00037         VFSNodeVector& mNodeList;
00038 };
00039 
00040 bool VFSNodeListCallback::handleNextNode(const VFSNode& node) {
00041     mNodeList.push_back(node);
00042     return true;
00043 }
00044 
00045 // VFSNodeNameCallback -----------------------------------------------------
00046 
00050 class VFSNodeNameCallback : public VDirectoryIterationCallback {
00051     public:
00052 
00053         VFSNodeNameCallback(VStringVector& nameList) : VDirectoryIterationCallback(), mNameList(nameList) {}
00054         virtual ~VFSNodeNameCallback() {}
00055 
00056         virtual bool handleNextNode(const VFSNode& node);
00057 
00058     private:
00059 
00060         // Prevent copy construction and assignment.
00061         VFSNodeNameCallback(const VFSNodeNameCallback& other);
00062         VFSNodeNameCallback& operator=(const VFSNodeNameCallback& other);
00063 
00064         VStringVector& mNameList;
00065 };
00066 
00067 bool VFSNodeNameCallback::handleNextNode(const VFSNode& node) {
00068     VString nodeName;
00069     node.getName(nodeName);
00070     mNameList.push_back(nodeName);
00071     return true;
00072 }
00073 
00074 // VFSNodeFindCallback -----------------------------------------------------
00075 
00080 class VFSNodeFindCallback : public VDirectoryIterationCallback {
00081     public:
00082 
00083         VFSNodeFindCallback(const VString& nameToMatch);
00084         virtual ~VFSNodeFindCallback() {}
00085 
00086         virtual bool handleNextNode(const VFSNode& node);
00087 
00088         bool found() const { return mFound; }
00089         void getMatchedNode(VFSNode& node) const { node = mMatchedNode; }
00090 
00091     private:
00092 
00093         bool    mFound;
00094         VString mNameToMatchLowerCase;
00095         VFSNode mMatchedNode;
00096 };
00097 
00098 VFSNodeFindCallback::VFSNodeFindCallback(const VString& nameToMatch)
00099     : VDirectoryIterationCallback()
00100     , mFound(false)
00101     , mNameToMatchLowerCase(nameToMatch)
00102     , mMatchedNode()
00103     {
00104     mNameToMatchLowerCase.toLowerCase();
00105 }
00106 
00107 bool VFSNodeFindCallback::handleNextNode(const VFSNode& node) {
00108     VString nodeNameLowerCase;
00109     node.getName(nodeNameLowerCase);
00110     nodeNameLowerCase.toLowerCase();
00111 
00112     if (nodeNameLowerCase == mNameToMatchLowerCase) {
00113         mFound = true;
00114         mMatchedNode = node;
00115         return false; // we found a match, so stop looking
00116     }
00117 
00118     return true;
00119 }
00120 
00121 // VFSNode -------------------------------------------------------------------
00122 
00123 // static
00124 VString VFSNode::normalizePath(const VString& path) {
00125     return VFSNode::_platform_normalizePath(path);
00126 }
00127 
00128 // static
00129 VString VFSNode::denormalizePath(const VString& path) {
00130     return VFSNode::_platform_denormalizePath(path);
00131 }
00132 
00133 // static
00134 VFSNode VFSNode::getKnownDirectoryNode(KnownDirectoryIdentifier id, const VString& companyName, const VString& appName) {
00135     return VFSNode::_platform_getKnownDirectoryNode(id, companyName, appName);
00136 }
00137 
00138 // static
00139 VFSNode VFSNode::getCurrentWorkingDirectory() {
00140     return VFSNode::getKnownDirectoryNode(CURRENT_WORKING_DIRECTORY, VString::EMPTY(), VString::EMPTY());
00141 }
00142 
00143 // static
00144 VFSNode VFSNode::getExecutableDirectory() {
00145     return VFSNode::getKnownDirectoryNode(EXECUTABLE_DIRECTORY, VString::EMPTY(), VString::EMPTY());
00146 }
00147 
00148 // static
00149 VFSNode VFSNode::getExecutable() {
00150     return VFSNode::_platform_getExecutable();
00151 }
00152 
00153 static VInstantFormatter VFSNODE_SAFE_FILE_NAME_INSTANT_FORMATTER("yMMddHHmmssSSS");
00154 
00155 // static
00156 void VFSNode::safelyOverwriteFile(const VFSNode& target, Vs64 dataLength, VBinaryIOStream& dataStream) {
00157     bool success = true;
00158     VString errorMessage;
00159 
00160     VString targetFileName = target.getName();
00161 
00162     VInstant now;
00163     VString temporaryFileName = now.getLocalString(VFSNODE_SAFE_FILE_NAME_INSTANT_FORMATTER) + "_tmp_" + targetFileName;
00164 
00165     VFSNode directoryNode;
00166     target.getParentNode(directoryNode);
00167     VFSNode temporaryFileNode(directoryNode, temporaryFileName);
00168 
00169     // Create and write to the temp file within a scope block to ensure file is closed when scope is exited.
00170     /* stream scope */ {
00171         VBufferedFileStream tempFileStream(temporaryFileNode);
00172         VBinaryIOStream tempOutputStream(tempFileStream);
00173 
00174         try {
00175             tempFileStream.openWrite();
00176         } catch (const VException& ex) {
00177             success = false;
00178             errorMessage = VSTRING_FORMAT("Unable to open temporary file '%s': %s", target.getPath().chars(), ex.what());
00179         }
00180 
00181         if (success) {
00182             try {
00183                 VStream::streamCopy(dataStream, tempOutputStream, dataLength);
00184                 tempOutputStream.flush();
00185             } catch (const VException& ex) {
00186                 success = false;
00187                 errorMessage = VSTRING_FORMAT("Unable to write to temporary file '%s': %s", target.getPath().chars(), ex.what());
00188             }
00189         }
00190     }
00191 
00192     /*
00193     If we succeeded, delete the original file and rename the temporary file to replace it.
00194     If we failed, delete the temporary file.
00195     Do this itself in separate phases, so that if the delete/rename fails, we still delete the temporary file.
00196     */
00197     // 1. Remove target. (It might not exist yet.)
00198     if (success && target.exists()) {
00199         if (! target.rm()) {
00200             success = false;
00201             errorMessage = VSTRING_FORMAT("Unable to remove target file '%s'.", target.getPath().chars());
00202         }
00203     }
00204 
00205     // 2. Rename temporary to target.
00206     if (success) {
00207         try {
00208             temporaryFileNode.renameToNode(target);
00209         } catch (const VException& ex) {
00210             success = false;
00211             errorMessage = VSTRING_FORMAT("Failed renaming '%s' to '%s': %s", temporaryFileNode.getPath().chars(), target.getPath().chars(), ex.what());
00212         }
00213     }
00214 
00215     // 3. Remove temporary if unsuccessful.
00216     if (! success) {
00217         if (! temporaryFileNode.rm()) {
00218             errorMessage += VSTRING_FORMAT(" Removal of temporary file '%s' failed.", temporaryFileNode.getPath().chars());
00219         }
00220     }
00221 
00222     // If we failed, throw an exception with the error message we built wherever we encountered errors.
00223     if (! success) {
00224         throw VException(errorMessage);
00225     }
00226 }
00227 
00228 VFSNode::VFSNode()
00229     : mPath()
00230     {
00231 }
00232 
00233 VFSNode::VFSNode(const VFSNode& node)
00234     : mPath(node.mPath)
00235     {
00236 }
00237 
00238 VFSNode::VFSNode(const VString& path)
00239     : mPath(path)
00240     {
00241 
00242     if (path.isEmpty()) {
00243         mPath = ".";
00244     }
00245 }
00246 
00247 VFSNode::VFSNode(const VFSNode& directory, const VString& childName)
00248     : mPath()
00249     {
00250     directory.getChildNode(childName, *this);
00251 }
00252 
00253 VFSNode& VFSNode::operator=(const VFSNode& other) {
00254     mPath = other.mPath;
00255     return *this;
00256 }
00257 
00258 void VFSNode::setPath(const VString& path) {
00259     if (path.isEmpty())
00260         mPath = ".";
00261     else
00262         mPath = path;
00263 }
00264 
00265 void VFSNode::getPath(VString& path) const {
00266     path = mPath;
00267 }
00268 
00269 const VString& VFSNode::getPath() const {
00270     return mPath;
00271 }
00272 
00273 void VFSNode::getName(VString& name) const {
00274     // The following works even if lastIndexOf returns -1 "not found",
00275     // because we add 1 to get the correct startIndex parameter.
00276     name.copyFromBuffer(mPath.chars(), mPath.lastIndexOf('/') + 1, mPath.length());
00277 }
00278 
00279 VString VFSNode::getName() const {
00280     VString name;
00281     this->getName(name);
00282     return name;
00283 }
00284 
00285 void VFSNode::setName(const VString& name) {
00286     VString parentPath;
00287     this->getParentPath(parentPath);
00288 
00289     VString newPath(VSTRING_ARGS("%s/%s", parentPath.chars(), name.chars()));
00290     this->setPath(newPath);
00291 }
00292 
00293 void VFSNode::getParentPath(VString& parentPath) const {
00294     parentPath.copyFromBuffer(mPath.chars(), 0, mPath.lastIndexOf('/'));
00295 }
00296 
00297 void VFSNode::getParentNode(VFSNode& parent) const {
00298     VString parentPath;
00299 
00300     this->getParentPath(parentPath);
00301     parent.setPath(parentPath);
00302 }
00303 
00304 void VFSNode::getChildPath(const VString& childName, VString& childPath) const {
00305     childPath.format("%s/%s", mPath.chars(), childName.chars());
00306 }
00307 
00308 void VFSNode::getChildNode(const VString& childName, VFSNode& child) const {
00309     VString childPath;
00310 
00311     this->getChildPath(childName, childPath);
00312     child.setPath(childPath);
00313 }
00314 
00315 void VFSNode::mkdirs() const {
00316     // If this directory already exists, we are done.
00317     if (this->exists())
00318         return;
00319 
00320     // Create the parent directory (and its parents etc.) if necessary.
00321     VFSNode    parentNode;
00322     this->getParentNode(parentNode);
00323 
00324     if (! parentNode.getPath().isEmpty())    // root or parent of supplied path must be assumed to exist
00325         parentNode.mkdirs();
00326 
00327     // Create this directory specifically.
00328     this->mkdir();
00329 }
00330 
00331 void VFSNode::mkdir() const {
00332     this->_platform_createDirectory();
00333 }
00334 
00335 bool VFSNode::rm() const {
00336     /*
00337     This could be optimized for Mac APIs which do a fast delete of
00338     the directory and its contents in one swipe. The following way
00339     is required on Unix file systems and is slower because we must
00340     delete a directory's contents before deleting it.
00341     */
00342     if (! this->exists()) {
00343         return false;
00344     }
00345 
00346     bool success = true;
00347     bool isDir = this->isDirectory();
00348 
00349     if (isDir) {
00350         success = this->rmDirContents();
00351     }
00352 
00353     if (success) {
00354         if (isDir) {
00355             success = this->_platform_removeDirectory();
00356         } else {
00357             success = this->_platform_removeFile();
00358         }
00359     }
00360 
00361     return success;
00362 }
00363 
00364 bool VFSNode::rmDirContents() const {
00365     bool            allSucceeded = true;
00366     VFSNodeVector   children;
00367 
00368     this->list(children);
00369 
00370     for (VSizeType i = 0; i < children.size(); ++i) {
00371         allSucceeded = allSucceeded && children[i].rm();
00372     }
00373 
00374     return allSucceeded;
00375 }
00376 
00377 void VFSNode::renameToPath(const VString& newPath) const {
00378     this->_platform_renameNode(newPath);
00379 }
00380 
00381 void VFSNode::renameToName(const VString& newName) const {
00382     VFSNode destinationNode; // not used
00383     this->renameToName(newName, destinationNode);
00384 }
00385 
00386 void VFSNode::renameToName(const VString& newName, VFSNode& nodeToUpdate) const {
00387     VFSNode parentNode;
00388     this->getParentNode(parentNode);
00389 
00390     VString newPath;
00391     parentNode.getChildPath(newName, newPath);
00392 
00393     this->_platform_renameNode(newPath);
00394 
00395     nodeToUpdate.setPath(newPath);    // it IS allowed for nodeToUpdate to be this
00396 }
00397 
00398 void VFSNode::renameToNode(const VFSNode& newNode) const {
00399     VString newPath;
00400     newNode.getPath(newPath);
00401 
00402     this->_platform_renameNode(newPath);
00403 }
00404 
00405 void VFSNode::list(VStringVector& children) const {
00406     VFSNodeNameCallback callback(children);
00407     this->_platform_directoryIterate(callback);
00408 }
00409 
00410 void VFSNode::list(VFSNodeVector& children) const {
00411     VFSNodeListCallback callback(children);
00412     this->_platform_directoryIterate(callback);
00413 }
00414 
00415 void VFSNode::iterate(VDirectoryIterationCallback& callback) const {
00416     this->_platform_directoryIterate(callback);
00417 }
00418 
00419 bool VFSNode::find(const VString& name, VFSNode& node) const {
00420     VFSNodeFindCallback callback(name);
00421     this->_platform_directoryIterate(callback);
00422 
00423     bool found = callback.found();
00424     if (found) {
00425         callback.getMatchedNode(node);
00426     }
00427 
00428     return found;
00429 }
00430 
00431 void VFSNode::readAll(VString& s, bool includeLineEndings) {
00432     VBufferedFileStream fs(*this);
00433     fs.openReadOnly();
00434     VTextIOStream in(fs);
00435     in.readAll(s, includeLineEndings);
00436 }
00437 
00438 void VFSNode::readAll(VStringVector& lines) {
00439     VBufferedFileStream fs(*this);
00440     fs.openReadOnly();
00441     VTextIOStream in(fs);
00442     in.readAll(lines);
00443 }
00444 
00445 bool VFSNode::exists() const {
00446     VFSNodeInfo info;
00447     return this->_platform_getNodeInfo(info); // only the function result is needed
00448 }
00449 
00450 // static
00451 VString VFSNode::readTextFile(const VString& path, bool includeLineEndings) {
00452     VFSNode node(path);
00453     VString text;
00454     node.readAll(text, includeLineEndings);
00455     return text;
00456 }
00457 
00458 // static
00459 void VFSNode::readTextFile(const VString& path, VStringVector& lines) {
00460     VFSNode node(path);
00461     node.readAll(lines);
00462 }
00463 
00464 VInstant VFSNode::creationDate() const {
00465     VFSNodeInfo info;
00466     bool nodeExists = this->_platform_getNodeInfo(info);
00467 
00468     if (!nodeExists)
00469         throw VException(VSystemError(info.mErrNo), VSTRING_FORMAT("VFSNode::creationDate failed for '%s'.", mPath.chars()));
00470 
00471     return VInstant::instantFromRawValue(info.mCreationDate);
00472 }
00473 
00474 VInstant VFSNode::modificationDate() const {
00475     VFSNodeInfo info;
00476     bool nodeExists = this->_platform_getNodeInfo(info);
00477 
00478     if (!nodeExists)
00479         throw VException(VSystemError(info.mErrNo), VSTRING_FORMAT("VFSNode::modificationDate failed for '%s'.", mPath.chars()));
00480 
00481     return VInstant::instantFromRawValue(info.mModificationDate);
00482 }
00483 
00484 VFSize VFSNode::size() const {
00485     VFSNodeInfo info;
00486     bool nodeExists = this->_platform_getNodeInfo(info);
00487 
00488     if (!nodeExists)
00489         throw VException(VSystemError(info.mErrNo), VSTRING_FORMAT("VFSNode::size failed for '%s'.", mPath.chars()));
00490 
00491     return info.mFileSize;
00492 }
00493 
00494 bool VFSNode::isFile() const {
00495     VFSNodeInfo info;
00496     bool nodeExists = this->_platform_getNodeInfo(info);
00497 
00498     return nodeExists && info.mIsFile;
00499 }
00500 
00501 bool VFSNode::isDirectory() const {
00502     VFSNodeInfo info;
00503     bool nodeExists = this->_platform_getNodeInfo(info);
00504 
00505     return nodeExists && info.mIsDirectory;
00506 }
00507 
00508 // VFSNodeInfo ---------------------------------------------------------------
00509 
00510 VFSNodeInfo::VFSNodeInfo()
00511     : mCreationDate(0)
00512     , mModificationDate(0)
00513     , mFileSize(0)
00514     , mIsFile(false)
00515     , mIsDirectory(false)
00516     , mErrNo(0)
00517     {
00518 }
00519 

Copyright ©1997-2014 Trygve Isaacson. All rights reserved. This documentation was generated with Doxygen.