AaronCameron.net
I care not for your petty politics.
Not a Member? - Login or Create an Account
Tuesday the 22nd of May 2012 @ 06:14pm
Front Page Journal Projects Your Profile About
[]

LibN2L-4 Library Code Reference

Classes
Compounds
Files
Members
Method Index
Full Reference

vfsUtilities.cpp

Go to the documentation of this file.
00001 /************************************************************************
00002 Nova-2 Library (libN2L, or simply n2l) Game development C++ Library
00003 Copyright (C) 2003  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 "vfsUtilities.h"
00026 
00027 #include "n2l/n2l.h"
00028 #include "n2l/dynVars.h"
00029 
00030 #include "vfsConstants.h"
00031 
00032 #include "cVfsOpenException.h"
00033 #include "cVfsReadException.h"
00034 
00035 #include "vfsFileTypes.h"
00036 
00037 #include "cVfsNodeInterface.h"
00038 #include "cVfsDirectory.h"
00039 #include "cVfsFile.h"
00040 
00041 #include <map>
00042 #include <vector>
00043 
00044 #include <sys/stat.h>
00045 #include <stdio.h>
00046 
00047 #include <dirent.h>
00048 #include <errno.h>
00049 
00050 #ifdef WIN32
00051 #   include <windows.h>
00052 #   define SHGFP_TYPE_CURRENT   0
00053 #   define CSIDL_APPDATA        0x0001a
00054 #   define CSIDL_CREATE         0x8000
00055 #   ifndef S_ISDIR
00056 #       define S_ISDIR(mode)        (((mode) & _S_IFDIR) != 0)
00057 #   endif
00058 #   ifndef S_ISREG
00059 #       define S_ISREG(mode)        (((mode) & _S_IFREG) != 0)
00060 #   endif
00061 #   define getcwd _getcwd
00062 #else
00063 #   include <sys/types.h>
00064 #   include <pwd.h>
00065 #   include <unistd.h>
00066 #   include <libgen.h>
00067 #endif
00068 
00069 using namespace std;
00070 
00071 /******************************************************************************/
00072 namespace n2l
00073 {
00074 
00075     /**************************************************************************/
00076     const tString vfsPermissionsAsString( const tUint &iPerms )
00077     {
00078         tString temp( "---" );
00079         if (iPerms&VfsPermission_Readable) temp[0] = 'r';
00080         if (iPerms&VfsPermission_Writeable) temp[1] = 'w';
00081         if (iPerms&VfsPermission_Executable) temp[2] = 'x';
00082         return temp;
00083     }
00084 
00085     /**************************************************************************/
00086     const tFsNodeType getFsNodeType( const tFsNodeName &iName )
00087     {
00088         tFsNodeName name = cleanFsPath(iName);
00089         struct stat statBuffer;
00090         if (0!=stat(name.c_str(),&statBuffer))
00091             throw cVfsOpenException("getFsNodeType","Stat failed",name);
00092 
00093         if (S_ISDIR(statBuffer.st_mode)) return FsNodeType_Directory;
00094         if (S_ISREG(statBuffer.st_mode)) return FsNodeType_File;
00095 
00096         return FsNodeType_Unknown;
00097     }
00098 
00099     /**************************************************************************/
00100     const tUint getVfsNodePermissions( const tVfsNodeName &iName )
00101     {
00102         return 0;
00103     }
00104 
00105     /**************************************************************************/
00106     const tUint getFsNodePermissions( const tFsNodeName &  ) // i_name
00107     {
00108         return 0;
00109     }
00110 
00111     /**************************************************************************/
00112     const tFsFileSize getFsNodeSize( const tFsNodeName &i_name )
00113     {
00114         FILE * fp = fopen( i_name.c_str(), "rb" );
00115         if (0==fp)
00116             throw cVfsOpenException("getFsNodeSize","Open failed",i_name);
00117 
00118         const tSint OldFpPos = ftell(fp);
00119         if (-1==OldFpPos) {
00120             fclose(fp);
00121             throw cVfsReadException( "getFsNodeSize",
00122                 "Tell failed getting origin", i_name);
00123          }
00124         if (-1==fseek(fp,0,SEEK_END)) {
00125             fclose(fp);
00126             throw cVfsReadException("getFsNodeSize", "Seek end failed", i_name);
00127          }
00128         const tSint TotalSize = ftell(fp);
00129         if (-1==TotalSize) {
00130             fclose(fp);
00131             throw cVfsReadException( "getFsNodeSize",
00132                 "Tell failed getting size", i_name);
00133          }
00134         if (-1==fseek(fp,OldFpPos,SEEK_SET)) {
00135             fclose(fp);
00136             throw cVfsReadException( "getFsNodeSize", "Seek origin failed",
00137                 i_name);
00138          }
00139          
00140         fclose(fp);
00141 
00142         return tFsFileSize(TotalSize);
00143     }
00144 
00145     /**************************************************************************/
00146     void getFsNodeDump( const tFsNodeName &i_name, tString &o_buffer )
00147     {
00148         o_buffer.clear();
00149 
00150         const tFsFileSize FileSize = getFsNodeSize(i_name);
00151         if (0==FileSize) return;
00152 
00153         o_buffer.reserve( FileSize );
00154         FILE * fp = fopen( i_name.c_str(), "rb" );
00155         if (0==fp) 
00156             throw cVfsReadException("getFsNodeDump", "Open failed", i_name);
00157 
00158         const tUint ReadBufferSize = 1024*64;   // 64k read buffer
00159         tUint8 readBuffer[ReadBufferSize];
00160 
00161         tUint fileBytesLeftToRead = FileSize;
00162         tUint bytesToRead =
00163             (fileBytesLeftToRead >
00164             ReadBufferSize?ReadBufferSize:fileBytesLeftToRead);
00165 
00166         while (!feof(fp) && bytesToRead) {
00167             if ( 1!=fread((void*)(readBuffer), bytesToRead,1, fp) &&
00168                 !feof(fp) )
00169             {
00170                 fclose(fp);
00171                 throw cVfsReadException( "getFsNodeDump",
00172                     "Out of bytes before eof", i_name);
00173             }
00174 
00175             o_buffer.append( (char*)(readBuffer),
00176                 tString::size_type(bytesToRead) );
00177 
00178             fileBytesLeftToRead -= bytesToRead;
00179             bytesToRead = (fileBytesLeftToRead >
00180                 ReadBufferSize?ReadBufferSize:fileBytesLeftToRead);
00181          }
00182         fclose(fp);
00183     }
00184 
00185     /**************************************************************************/
00186     const tVfsNodeName convertFsLiteralNameToVfs( const tFsNodeName &i_name )
00187     {
00188         const char FsPathSepArray[] = { _n2l::FsPathSeperatorSymbol,'\0' };
00189         const char VfsPathSepArray[] = { VfsPathSeperatorSymbol,'\0' };
00190         return cleanVfsPath( substringReplace( FsPathSepArray,
00191             VfsPathSepArray, i_name) );
00192     }
00193 
00194     /**************************************************************************/
00195     const tVfsNodeName cleanVfsPath( const tVfsNodeName &iName )
00196     {
00197         return _n2l::cleanPath(iName,VfsPathSeperatorSymbol);
00198     }
00199 
00200     /**************************************************************************/
00201     const tFsNodeName cleanFsPath(  const tFsNodeName & iName )
00202     {
00203         return _n2l::cleanPath( _n2l::cleanPath( iName,
00204             _n2l::FsPathSeperatorSymbol), _n2l::AltFsPathSeperatorSymbol );
00205     }
00206 
00207     /**************************************************************************/
00208     const vector<tVfsNodeName> splitVfsPathBlocks( const tVfsNodeName &i_name )
00209     {
00210         vector<tVfsNodeName> temp;
00211 
00212         explode(i_name,temp,VfsPathSeperatorSymbol,'\0','\0');
00213 
00214         // Remove blank entries
00215         for (vector<tVfsNodeName>::iterator blockIt = temp.begin();
00216             blockIt!=temp.end();)
00217         {
00218             if (!blockIt->size()) blockIt = temp.erase(blockIt);
00219             else  ++blockIt;
00220         }
00221         return temp;
00222     }
00223 
00224     /**************************************************************************/
00225     const tVfsNodeName stripFrontLiteralVfsName( const tVfsNodeName &iName,
00226         tVfsNodeName &oStrippedName )
00227     {
00228         const tVfsNodeName::size_type StartPos =
00229             iName.find_first_not_of(VfsPathSeperatorSymbol);
00230 
00231         if (StartPos == tVfsNodeName::npos) {
00232             oStrippedName = "";
00233             return "";
00234         }
00235         tVfsNodeName::size_type breakPos = iName.find(
00236             VfsPathSeperatorSymbol,StartPos+1);
00237 
00238         if (breakPos == tVfsNodeName::npos ) {
00239             oStrippedName = iName.substr(StartPos,iName.size()-StartPos);
00240             return "";
00241         }
00242 
00243         const tVfsNodeName::difference_type StrippedNameSize =
00244             breakPos-StartPos;
00245 
00246         if (StrippedNameSize<=0) {
00247             oStrippedName = "";
00248             return "";
00249         }
00250         const tVfsNodeName::size_type RemainStrSize = iName.size()-breakPos;
00251 
00252         oStrippedName = iName.substr(StartPos,StrippedNameSize);
00253         if (RemainStrSize==1) return "";
00254         return iName.substr(breakPos+1,RemainStrSize);
00255     }
00256 
00257     /**************************************************************************/
00258     const tString & simpleGuessVfsFileType( const tVfsNodeName &iName )
00259     {
00260         static map<tString,const tString*> extLookup;
00261         // Get an extension if there is one
00262         const tString::size_type LastDotPos = iName.find_last_of(".");
00263         if (LastDotPos==tString::npos) return VfsFileType_Unknown;
00264 
00265         // Get the extension
00266         const tString extStr = iName.substr( (LastDotPos+1),
00267             iName.size()-(LastDotPos+1) );
00268     
00269         // Build the extension list if we haven't already
00270         if (extLookup.empty()) {
00271             extLookup["bmp"] =  &VfsFileType_BMP;
00272             extLookup["gif"] =  &VfsFileType_GIF;
00273             extLookup["jpeg"] = &VfsFileType_JPEG;
00274             extLookup["jpg"] =  &VfsFileType_JPEG;
00275             extLookup["jpe"] =  &VfsFileType_JPEG;
00276             extLookup["png"] =  &VfsFileType_PNG;
00277             extLookup["tiff"] = &VfsFileType_TIFF;
00278             extLookup["tif"] =  &VfsFileType_TIFF;
00279             extLookup["tga"] =  &VfsFileType_TGA;
00280             extLookup["pcx"] =  &VfsFileType_PCX;
00281             
00282             extLookup["wav"] =  &VfsFileType_WAV;   /**************************************************************************/
00283             extLookup["mp3"] =  &VfsFileType_MP3;
00284             extLookup["ogg"] =  &VfsFileType_OGG;
00285             
00286             extLookup["gz"] =   &VfsFileType_GZIP;
00287             extLookup["tgz"] =  &VfsFileType_GZIP;
00288             extLookup["tar"] =  &VfsFileType_TAR;
00289             extLookup["bz2"] =  &VfsFileType_BZIP2;
00290             extLookup["zip"] =  &VfsFileType_ZIP;
00291         }
00292 
00293         // Do the lookup
00294         const map<tString,const tString*>::const_iterator ExtPos =
00295             extLookup.find( extStr );
00296 
00297         if (ExtPos == extLookup.end()) return VfsFileType_Unknown;
00298 
00299         return *(ExtPos->second);
00300     }
00301 
00302     /**************************************************************************/
00303     const tUint vfsNodeFileWithHeader( const cVfsNodeInterface &iNode,
00304         const tString &iHeader, tVfsFileBuffer &oBuffer )
00305     {
00306         oBuffer.clear();
00307         if (!iNode.likeFile())
00308             throw cBadDataUseException( "vfsNodeFileWithHeader",
00309                 "Node is not like a file" );
00310         if (iNode.firstLine()!=iHeader)
00311             throw cBadDataUseException( "vfsNodeFileWithHeader",
00312                 tString("File header: \"") + iNode.firstLine() + "\" not "
00313                     " the required header: \"" + iHeader + "\"" );
00314 
00315         iNode.getBuffer( oBuffer );
00316         const tUint DataOffset = iNode.firstLine().size()+1;
00317         if (oBuffer.size()<=DataOffset)
00318             throw cBadDataUseException( "vfsNodeFileWithHeader",
00319                 "No data after header" );
00320         return DataOffset;
00321     }
00322 
00323     /**************************************************************************/
00324     const tUint vfsNodeFileWithHeader(  const cVfsNodeInterface &iNode,
00325         const tString &iHeader )
00326     {
00327         if (!iNode.likeFile())
00328             throw cBadDataUseException( "vfsNodeFileWithHeader",
00329                 "Node is not like a file" );
00330 
00331         if (iNode.firstLine()!=iHeader)
00332             throw cBadDataUseException( "vfsNodeFileWithHeader",
00333                 tString("File header: \"") + iNode.firstLine() + 
00334                 "\" not the required header: \"" + iHeader + "\"" );
00335 
00336         const tUint DataOffset = iNode.firstLine().size()+1;
00337         if (iNode.size()<=DataOffset)
00338             throw cBadDataUseException( "vfsNodeFileWithHeader",
00339                 "No data after header" );
00340         return DataOffset;
00341     }
00342 
00343     /**************************************************************************/
00344     void vfsNodeFileWithHeader( const cVfsNodeInterface &iNode,
00345         const tString &iHeader, cDynVar &oDef )
00346     {
00347         if (!iNode.likeFile())
00348             throw cBadDataUseException( "vfsNodeFileWithHeader",
00349                 "Provided node isn\'t like a file" );
00350         // Load it.
00351         if (iNode.firstLine()!=iHeader)
00352             throw cParsingException( "vfsNodeFileWithHeader",
00353                 tString("This file isn\'t the right type.  "
00354                 "Wanted: \"") + iHeader + "\" but got: \"" + 
00355                 iNode.firstLine() + "\"" );
00356 
00357         if (iNode.buffer().size()<=(iNode.firstLine().size()+1))
00358             throw cParsingException( "vfsNodeFileWithHeader",
00359                 "No data after header" );
00360 
00361         // The type is ok, unserialize it for the caller to work with.
00362         oDef.unserialize( iNode.buffer().c_str() +
00363             iNode.firstLine().size()+1 );
00364     }
00365 
00366     /**************************************************************************/
00367     const tVfsNodeName vfsGetCWD()
00368     {
00369 #       ifdef WIN32
00370         tUint bufSize = MAX_PATH;
00371 #       else
00372         tUint bufSize = 1024;
00373 #       endif
00374         for (;;) {
00375             char *buf = new char[bufSize];
00376             if (NULL == getcwd(buf, bufSize)) {
00377                 delete []buf;
00378                 if (errno == ERANGE && (bufSize*2>bufSize)) {
00379                     bufSize *= 2;
00380                 } else throw cException( "vfsGetCWD",
00381                     "Failed, though I have no clue why.  Errno was: " +
00382                     asString(errno) );
00383             } else {
00384                 tVfsNodeName tmp = buf;
00385                 delete []buf;
00386                 return tmp;
00387             }
00388         }
00389     }
00390 
00391     /**************************************************************************/
00392     const tVfsNodeName vfsGetHomeDir()
00393     {
00394         tFsNodeName temp;
00395 #       ifdef WIN32
00396             static HMODULE hSHFolderDLL = NULL;
00397             hSHFolderDLL = LoadLibrary("SHFOLDER.DLL");
00398 
00399             typedef HRESULT (WINAPI *SHGetFolderPathADDR)(HWND, int,
00400                 HANDLE, DWORD, LPTSTR);
00401             static SHGetFolderPathADDR SHGetFolderPath = 0;
00402 
00403             SHGetFolderPath = (SHGetFolderPathADDR)GetProcAddress(
00404                 hSHFolderDLL, "SHGetFolderPathA" );
00405             if (!SHGetFolderPath) throw cVfsException("vfsGetHomeDir",
00406                 "Could not get proc addr for SHGetFolderPathA" );
00407 
00408             char path[MAX_PATH];
00409             HRESULT r = SHGetFolderPath(0, CSIDL_APPDATA|CSIDL_CREATE,
00410                 0, SHGFP_TYPE_CURRENT, path);
00411             if (r == S_FALSE || r == E_FAIL || r == E_INVALIDARG)
00412                 throw cVfsException( "vfsGetHomeDir",
00413                     "Failed calling ShGetFolderPath" );
00414             temp = path;
00415 #       else
00416             struct passwd *passWDLine = getpwuid( getuid() );
00417             if (!passWDLine)
00418                 throw cVfsException("vfsGetHomeDir",
00419                     "Cannot get user passwd line");
00420             temp = tFsNodeName( passWDLine->pw_dir );
00421 #       endif
00422 
00423         return convertFsLiteralNameToVfs( temp );
00424     }
00425 
00426     /**************************************************************************/
00427     const tString vfsGetExecBinaryDir( const tString &iArgV0 )
00428     {
00429         if (iArgV0.empty())
00430             throw cBadDataUseException( "vfsGetExecBinaryDir",
00431                 "Called without an argv0." );
00432 
00433 #       ifdef WIN32
00434             throw cUnsupportedMethodException( "vfsGetExecBinaryDir",
00435                 "No implementation exists for this method.  Actually, "
00436                 "this method may work exactly the same as in Linux, it just "
00437                 "hasn\'t been tested." );
00438 #       else
00439             vector<tString> path;
00440             tString binName;
00441 
00442             // Figure out the binary name and the path described by iArgV0
00443             {
00444                 char *tmp = new char[iArgV0.size()+1];
00445                 strcpy( tmp, iArgV0.c_str() );
00446                 path.push_back( dirname( tmp ) );
00447                 
00448                 strcpy( tmp, iArgV0.c_str() );
00449                 binName = basename( tmp );
00450                 delete []tmp;
00451             }
00452 
00453             try {
00454                 explode( n2lGetEnv("PATH"), path, ':', '\\', '\0', true );
00455             }
00456             catch ( const cOutOfBoundsException & )
00457             {
00458                 // This is ok.  Continue with no path defined.
00459             }
00460 
00461             for (tUint i=0; i!=path.size(); ++i) {
00462                 cAutoPtr<cVfsDirectory> sDir = 0;
00463                 try {
00464                     sDir = new cVfsDirectory( path[i] );
00465 
00466                     cVfsDirectory::tConstIterator f = sDir->find( binName );
00467                     if (f==sDir->end()) continue;
00468 
00469                     cAutoPtr<const cVfsFile> file =
00470                         f->second.as<const cVfsFile>();
00471                     if (!file.isSet()) continue;
00472 
00473                     if (file->isSymLink()) {
00474                         // Let's give them this and be done with it.
00475                         tString retV;
00476                         {
00477                             retV = file->readLink();
00478                             char *tmp = new char[retV.size()+1];
00479                             strcpy( tmp, retV.c_str() );
00480                             retV = dirname(tmp);
00481                             delete []tmp;
00482                             return retV;
00483                         }
00484                     
00485                     } else return path[i];
00486                 }
00487                 catch ( const cException & ) { }
00488             }
00489             return "";
00490 #       endif
00491         }
00492 
00493 } // namespace n2l
00494 
00495 namespace _n2l
00496 {
00497     /**************************************************************************/
00498     const n2l::tString cleanPath(const n2l::tString &iName,
00499         const char iPathSplit )
00500     {
00501         // This is a big hacky, but it sort of fits in the scope of
00502         // what we're supposed to do so I don't feel TOO terrible about it.
00503         if (iName==".") return _n2l::cleanPath( n2l::vfsGetCWD(), iPathSplit );
00504 
00505         vector<n2l::tString> parts;
00506         vector<n2l::tString> cleanParts;
00507         n2l::explode(iName,parts,iPathSplit,'\0','\0');
00508         for(vector<n2l::tString>::const_iterator it = parts.begin();
00509             it!=parts.end(); ++it)
00510         {
00511             if (!it->empty() && *it!="." && *it!="..")
00512                 cleanParts.push_back(*it);
00513         }
00514         n2l::tString finalStr;
00515         if (!iName.empty() && iName[0]==iPathSplit)
00516             finalStr.assign( 1, iPathSplit );
00517         finalStr += n2l::implode(cleanParts,iPathSplit,'\0','\0');
00518         return finalStr;
00519     }
00520 
00521 }
©2012 Aaron Cameron