AaronCameron.net
Because you all make me very, very tired.
Not a Member? - Login or Create an Account
Wednesday the 23rd of May 2012 @ 11:22pm
Front Page Journal Projects Your Profile About
[]

LibN2L-4 Library Code Reference

Classes
Compounds
Files
Members
Method Index
Full Reference

cVfsDirectory.cpp

Go to the documentation of this file.
00001 /************************************************************************
00002 Nova-2 Library (libN2L, or simply n2l) Game development C++ Library
00003 Copyright (C) 2002  Aaron Cameron
00004 
00005 This library is free software; you can redistribute it and/or
00006 modify it under the terms of the GNU Lesser General Public
00007 License as published by the Free Software Foundation; either
00008 version 2.1 of the License, or (at your option) any later version.
00009 
00010 This library is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 Lesser General Public License for more details.
00014 
00015 You should have received a copy of the GNU Lesser General Public
00016 License along with this library; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00018 
00019 A copy of the GNU Lesser General Public License has been provided with
00020 this library in the file 'COPYING'.
00021 
00022 Contact information for the author of this library has been provided
00023 with this library in the file 'AUTHOR'.
00024 ************************************************************************/
00025 #include "cVfsDirectory.h"
00026 #include "cVfsFile.h"
00027 //
00028 #include "vfsConstants.h"
00029 #include "vfsUtilities.h"
00030 
00031 #include "cVfsOpenException.h"
00032 #include "cVfsReadException.h"
00033 #include "cVfsNoPermissionException.h"
00034 
00035 #ifdef WIN32
00036 //#include "win32/include/dirent.h"
00037 #include <dirent.h>
00038 #include <direct.h>
00039 #else
00040 #include <dirent.h>
00041 #include <unistd.h>
00042 #endif
00043 
00044 #include <stdio.h>
00045 #include <sys/stat.h>
00046 #include <sys/types.h>
00047 
00048 /*
00049 #include <iostream>
00050 using namespace std;
00051 #define endl '\n'
00052 */
00053 
00054 /******************************************************************************/
00055 namespace n2l
00056 {
00057 
00058     /**************************************************************************/
00059     cVfsDirectory::cVfsDirectory( const tFsNodeName & iName ) :
00060         mScanned(false),
00061         mVfsNodeName(""),   // Assume this is the root node
00062         mFsNodeName( cleanFsPath(iName) ),
00063         mStat(iName)
00064     {
00065         // Check to make sure this node is allowed
00066         if (!mStat.isDir())
00067             throw cVfsOpenException(
00068                 "cVfsDirectory::cVfsDirectory(name)",
00069                 "Not a directory node", iName);
00070     }
00071 
00072 
00073     /**************************************************************************/
00074     cVfsDirectory::cVfsDirectory(   const tFsNodeName & iName,
00075                                     const tVfsNodeName & iVfsName ) :
00076         mScanned(false),
00077         mVfsNodeName(iVfsName),
00078         mFsNodeName( cleanFsPath(iName) ),
00079         mStat(iName)
00080     {
00081         if (!mStat.isDir())
00082             throw cVfsOpenException(
00083                 "cVfsDirectory::cVfsDirectory(name,vfsName)",
00084                 "Not a directory node", iName);
00085     }
00086 
00087 
00088     /**************************************************************************/
00089     cVfsDirectory::~cVfsDirectory()
00090     {
00091     }
00092 
00093     /**************************************************************************/
00094     const tUint cVfsDirectory::permissions() const
00095     {
00096         return mStat.permissions();
00097     }
00098 
00099 
00100     /**************************************************************************/
00101     const tBool cVfsDirectory::isSymLink() const
00102     {
00103         return mStat.isSymLink();
00104     }
00105 
00106     /**************************************************************************/
00107     const tFsNodeName cVfsDirectory::readLink() const
00108     {
00109         return mStat.readLink();
00110     }
00111 
00112     /**************************************************************************/
00113     const cAutoPtr<const cVfsDirectory::tValue>
00114         cVfsDirectory::newFileNode(
00115         const tVfsNodeName &iName ) const
00116     {
00117 #       ifndef WIN32
00118             // Windows lies about the writeable status of
00119             // its folders, so we'll not check and just
00120             // hope everything works out.
00121             if (!mStat.writeable())
00122                 throw cVfsNoPermissionException(
00123                     "cVfsDirectory::newFileNode",
00124                     "No write permission to create node named: \"" +
00125                     iName + "\"", name() );
00126 #       endif
00127         tVfsNodeName cleanName = cleanVfsPath(iName);
00128 
00129         // Check for path elements, make sure this is a root 
00130         if (tVfsNodeName::npos !=
00131             cleanName.findFirstOf(VfsPathSeperatorSymbol) ||
00132             tVfsNodeName::npos !=
00133             cleanName.findFirstOf(_n2l::FsPathSeperatorSymbol))
00134             throw cVfsException( "cVfsDirectory::newFileNode",
00135                 "Unclean vfs name in new node creation" );
00136 
00137         tVfsNodeName newName( mFsNodeName );
00138         newName += _n2l::FsPathSeperatorSymbol;
00139         newName += cleanName;
00140         {
00141             tConstIterator i = find(cleanName);
00142             if (i!=end())
00143                 throw cVfsException( "cVfsDirectory::newFileNode",
00144                     "Requested node for creation already exists: " +
00145                     newName );
00146         }
00147 
00148         FILE *fp;
00149         fp = fopen(newName.c_str(),"wb");
00150         fclose(fp);
00151         
00152         mScanned = false;
00153         mContainer.clear();
00154 
00155         // Find the node.
00156         tConstIterator i = find(cleanName);
00157         if (i==end())
00158             throw cVfsException( "cVfsDirectory::newFileNode",
00159                 "Newly created node couldn\'t be found: " +
00160                 newName );
00161         return i->second;
00162     }
00163 
00164     /**************************************************************************/
00165     const cAutoPtr<const cVfsDirectory::tValue>
00166         cVfsDirectory::newDirectoryNode(
00167         const tVfsNodeName &iName ) const
00168     {
00169 #       ifndef WIN32
00170             // Windows lies about the writeable status of
00171             // its folders, so we'll not check and just
00172             // hope everything works out.
00173             if (!mStat.writeable())
00174                 throw cVfsNoPermissionException(
00175                     "cVfsDirectory::newDirectoryNode",
00176                     "No write permission to create node named: \"" +
00177                     iName + "\"", name() );
00178 #       endif
00179 
00180         tVfsNodeName cleanName = cleanVfsPath(iName);
00181 
00182         // Check for path elements, make sure this is a root 
00183         if (tVfsNodeName::npos !=
00184             cleanName.findFirstOf(VfsPathSeperatorSymbol) ||
00185             tVfsNodeName::npos !=
00186             cleanName.findFirstOf(_n2l::FsPathSeperatorSymbol))
00187             throw cVfsException( "cVfsDirectory::newDirectoryNode",
00188                 "Unclean vfs name in new node creation" );
00189 
00190         tVfsNodeName newName( mFsNodeName );
00191         newName += _n2l::FsPathSeperatorSymbol;
00192         newName += cleanName;
00193         {
00194             tConstIterator i = find(cleanName);
00195             if (i!=end())
00196                 throw cVfsException( "cVfsDirectory::newDirectoryNode",
00197                     "Requested node for creation already exists: " +
00198                     newName );
00199         }
00200 
00201         // Create the actual directory
00202 #       ifdef WIN32
00203             tSint r = _mkdir( newName.c_str() );
00204             if (0!=r)
00205                 throw cVfsException( "cVfsDirectory::newDirectoryNode",
00206                     "Requested node could not be created: " + newName );
00207 #       else
00208             tSint r = mkdir( newName.c_str(), S_IRWXU | S_IRGRP | S_IXGRP
00209                 | S_IROTH | S_IXOTH );
00210             if (0!=r)
00211                 throw cVfsException( "cVfsDirectory::newDirectoryNode",
00212                     "Requested node could not be created: " + newName );
00213 #       endif
00214         
00215         mScanned = false;
00216         mContainer.clear();
00217 
00218         // Find the node.
00219         tConstIterator i = find(cleanName);
00220         if (i==end())
00221             throw cVfsException( "cVfsDirectory::newDirectoryNode",
00222                 "Newly created node couldn\'t be found: " +
00223                 newName );
00224         return i->second;
00225     }
00226 
00227     /**************************************************************************/
00228     void cVfsDirectory::unlinkNode( const tConstIterator &iNodeI ) const
00229     {
00230         // I don't know how important this is, but make sure the 
00231         // node provided belongs to this directory.
00232         tConstIterator i = find(iNodeI->first);
00233         if (i!=iNodeI)
00234             throw cVfsException("cVfsDirectory::unlinkNode",
00235                 "May not unlink a node not from this directory.");
00236         // Permissions.
00237         if (!mStat.writeable())
00238             throw cVfsNoPermissionException(
00239                 "cVfsDirectory::unlinkNode",
00240                 "No write in directory", name() );
00241 
00242         // What kind of node is it?
00243         tBool isFile( true );
00244         try { cAutoPtr<const cVfsFile>(iNodeI->second); }
00245         catch (const cBadCastException&) { isFile = false; }
00246         if (!isFile) {
00247             try { cAutoPtr<const cVfsDirectory>(iNodeI->second);    }
00248             catch (const cBadCastException&)
00249             {
00250                 // You've got me.
00251                 throw cVfsException("cVfsDirectory::unlinkNode",
00252                     "Have no idea what this node is.");
00253             }
00254         }
00255         // Actually unlink
00256         if (isFile) {
00257             tSint r = unlink( iNodeI->second->fsName().c_str() );
00258             if (r!=0)
00259                 throw cVfsException( "cVfsDirectory::unlinkNode",
00260                     "Unlink command failed with error: " +
00261                     asString(r) );
00262         } else {
00263             if (!cAutoPtr<const cVfsDirectory>(iNodeI->second)->empty())
00264                 throw cVfsException( "cVfsDirectory::unlinkNode",
00265                     "Unlink command executed on non-empty directory." );
00266 #           ifdef WIN32
00267                 tSint r = _rmdir( iNodeI->second->fsName().c_str() );
00268                 if (r!=0)
00269                     throw cVfsException( "cVfsDirectory::unlinkNode",
00270                         "Rmdir command failed with error: " +
00271                         asString(r) );
00272 #           else
00273                 tSint r = rmdir( iNodeI->second->fsName().c_str() );
00274                 if (r!=0)
00275                     throw cVfsException( "cVfsDirectory::unlinkNode",
00276                         "Rmdir command failed with error: " +
00277                         asString(r) );
00278 #           endif
00279         }
00280         mScanned = false;
00281         mContainer.clear();
00282     }
00283 
00284     /**************************************************************************/
00285     void cVfsDirectory::copyNode( const tConstIterator &iNodeI,
00286         const tVfsNodeName &iNewName,
00287         const cVfsDirectoryInterface &oDir ) const
00288     {
00289         if (iNodeI->second->isDirectory())
00290             throw cVfsException( "cVfsDirectory::copyNode",
00291                     "Copying of directories is currently unsupported" );
00292 
00293         tVfsNodeName cleanName = cleanVfsPath(iNewName);
00294 
00295         if (tVfsNodeName::npos !=
00296             cleanName.findFirstOf(VfsPathSeperatorSymbol) ||
00297             tVfsNodeName::npos !=
00298             cleanName.findFirstOf(_n2l::FsPathSeperatorSymbol))
00299             throw cVfsException( "cVfsDirectory::copyNode",
00300                 "Unclean vfs name in copy" );
00301 
00302 
00303         if (oDir.end() != oDir.find(cleanName))
00304             throw cVfsException( "cVfsDirectory::copyNode",
00305                     "Destination node already has item named: " +
00306                     cleanName );
00307 
00308         try
00309         {
00310             cAutoPtr<const cVfsFileInterface> newNode = oDir.newFileNode(
00311                 cleanName );
00312             newNode->buffer( iNodeI->second->buffer() );
00313         }
00314         catch ( const cVfsException &iE )
00315         {
00316             tConstIterator staleNode = 
00317                 oDir.find( cleanName );
00318             if (staleNode!=oDir.end())
00319                 oDir.unlinkNode( staleNode );
00320             iE.rethrow();
00321         }
00322     }
00323 
00324     /**************************************************************************/
00325     const cVfsDirectory::tConstIterator cVfsDirectory::find(
00326         const tKey & iName ) const
00327     {
00328         // See what we're actually looking for.
00329         if (iName.empty()) return end();
00330 
00331         tVfsNodeName localNodeName;
00332         tVfsNodeName remainingSearch = stripFrontLiteralVfsName(iName,
00333             localNodeName);
00334 
00335         const tConstIterator LocalNode = container().find(localNodeName);
00336         if (LocalNode==end() || remainingSearch.empty()) return LocalNode;
00341         if (!LocalNode->second->likeDirectory())
00342             throw cVfsOpenException( "cVfsDirectory::find",
00343                 "Tried to seek into a non-directory node", iName);
00344 
00345         // Since 'end()' for our sub nodes might not match OUR end, we have to 
00346         // detect the end, and replace it with ours.
00347         const cAutoPtr<const cVfsDirectoryInterface> TempSubNode =
00348             LocalNode->second;
00349 
00350         const tConstIterator SubSearch( TempSubNode->find(remainingSearch) );
00351         if (SubSearch==TempSubNode->end()) return end();
00352         else return SubSearch;
00353     }
00354 
00355 
00356     /**************************************************************************/
00357     const cAutoPtr<const cVfsDirectory::tValue> &cVfsDirectory::mustFind(
00358         const tKey &iName ) const
00359     {
00360         if (iName.empty())
00361             throw cOutOfBoundsException("cVfsDirectory::mustFind",
00362                 "No key provided?");
00363         const tConstIterator NodeIT( find(iName) );
00364         if (NodeIT==end()) {
00365             throw cOutOfBoundsException("cVfsDirectory::mustFind",
00366                 "Required vfs node not found in " + mVfsNodeName, iName);
00367         }
00368         return (NodeIT->second);
00369     }
00370 
00371 
00372     /**************************************************************************/
00373     const tBool cVfsDirectory::hasChild( const tKey & iName ) const
00374     {
00375         return (find(iName)!=end());
00376     }
00377 
00378     /**************************************************************************/
00379     const tVfsFileBuffer &cVfsDirectory::buffer() const
00380     {
00381         throw cUnsupportedMethodException( "cVfsDirectory::buffer",
00382             "May not read a buffer from a directory node." );
00383     }
00384 
00385     /**************************************************************************/
00386     void cVfsDirectory::buffer( const tVfsFileBuffer &iBuffer ) const
00387     {
00388         throw cUnsupportedMethodException( "cVfsDirectory::buffer",
00389             "May not write a buffer to a directory node." );
00390     }
00391 
00392     /**************************************************************************/
00393     void cVfsDirectory::getBuffer( tVfsFileBuffer & oBuffer ) const
00394     {
00395         oBuffer = "";
00396     }
00397 
00398     /**************************************************************************/
00399     const tVfsFileBuffer & cVfsDirectory::firstLine() const
00400     {
00401         static const tVfsFileBuffer NoBuffer("");
00402         return NoBuffer;
00403     }
00404     
00405 
00406     /**************************************************************************/
00407     SDL_RWops * cVfsDirectory::getRWops() const
00408     {
00409         return 0;
00410     }
00411 
00412 
00413     /**************************************************************************/
00414     void cVfsDirectory::add( const tKey & iName,
00415         const cAutoPtr<const tValue> i_iNode ) 
00416     {
00417         if (end()!=find(iName))
00418             throw cDuplicateKeyException(   "cVfsDirectory::add",
00419                 "Duplicate file name in dir", iName );
00420         mContainer[iName] = i_iNode;
00421     }
00422 
00423 
00424     /**************************************************************************/
00425     void cVfsDirectory::clear()
00426     {
00427         // Dont' use container() here to avoid scanning the local
00428         // directory just to clear it.
00429         mContainer.clear();
00430         mFsNodeName.clear();
00431         mVfsNodeName.clear();
00432     }
00433 
00434     /**************************************************************************/
00435     const tUint cVfsDirectory::size() const
00436     {
00437         return container().size();
00438     }
00439 
00440 
00441     /**************************************************************************/
00442     const tVfsNodeName & cVfsDirectory::name() const
00443     {
00444         return mVfsNodeName;
00445     }
00446 
00447 
00448     /**************************************************************************/
00449     const tBool cVfsDirectory::supportsFsName() const
00450     {
00451         return true;
00452     }
00453 
00454 
00455     /**************************************************************************/
00456     const tFsNodeName cVfsDirectory::fsName() const
00457     {
00458         return mFsNodeName;
00459     }
00460 
00461     /**************************************************************************/
00462     void cVfsDirectory::clearCache() const
00463     {
00464         mContainer.clear();
00465         mScanned = false;
00466     }
00467 
00468     /**************************************************************************/
00469     const tBool cVfsDirectory::empty() const
00470     {
00471         return container().empty();
00472     }
00473 
00474 
00475     /**************************************************************************/
00476     const cVfsDirectory::tConstIterator cVfsDirectory::begin() const
00477     {
00478         return container().begin();
00479     }
00480 
00481 
00482     /**************************************************************************/
00483     const cVfsDirectory::tConstIterator cVfsDirectory::end() const
00484     {
00485         return container().end();
00486     }
00487 
00488 
00489     /**************************************************************************/
00490     cVfsDirectory::tContainer & cVfsDirectory::container()
00491     {
00492         if (!mScanned) refreshNodeCache();
00493         return mContainer;
00494     }
00495 
00496 
00497     /**************************************************************************/
00498     const cVfsDirectory::tContainer & cVfsDirectory::container() const
00499     {
00500         if (!mScanned) refreshNodeCache();
00501         return mContainer;
00502     }
00503 
00504 
00505     /**************************************************************************/
00506     void cVfsDirectory::refreshNodeCache() const
00507     {
00508         mContainer.clear();
00509         DIR * dir;
00510         if ( 0 == (dir=opendir(mFsNodeName.c_str())) )
00511             throw cVfsOpenException("cVfsDirectory::refreshNodeCache",
00512                                     "Open dir failed",mFsNodeName);
00513 //cout << "opendir: " << mFsNodeName.c_str() << endl;
00514 //cout << "opendir: " << cleanFsPath(mFsNodeName.c_str()) << endl;
00516         dirent * entry;
00517         while ( 0!=(entry=readdir(dir)) ) {
00518 //cout << "got: " << entry->d_name << endl;
00519             if (strcmp(entry->d_name,".")==0) continue;
00520             if (strcmp(entry->d_name,"..")==0) continue;
00521 
00522             const tString FullFsName = mFsNodeName +
00523                 _n2l::FsPathSeperatorSymbol+entry->d_name;
00524 
00525             // Some things explode this method, like bad symlinks for
00526             // example.  We want to skip those, not fail opening the
00527             // directory.
00528             tFsNodeType type;
00529             try {
00530                 type = getFsNodeType(FullFsName);
00531             }
00532             catch (...)
00533             {
00534                 type = FsNodeType_Unknown;
00535             }
00536 
00537             switch (type)
00538             {
00539                 case FsNodeType_File:
00540                     mContainer[entry->d_name] = new cVfsFile(FullFsName);
00541                     break;
00542 
00543                 case FsNodeType_Directory:
00544                     mContainer[entry->d_name] = new cVfsDirectory(FullFsName,
00545                         entry->d_name);
00546                     break;
00547 
00548                 default:
00549                     //skippedEntries += tString("\"")+FullFsName+"\"";
00550                     continue;
00551             } // switch
00552 
00553          }
00554         closedir(dir);
00555         mScanned = true;
00556 //      if (!skippedEntries.empty())
00557 //          throw cVfsReadException( "cVfsDirectory::refreshNodeCache",
00558 //              "Skipped bad or unrecognized nodes", skippedEntries );
00559     }
00560 
00561 } // namespace
©2012 Aaron Cameron