![[]](/images/special/trans.gif)
LibN2L-4 Library Code ReferenceClassesCompounds Files Members Method Index Full Reference cVfsDirectory.cppGo 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 |