FileSystem.cpp

Go to the documentation of this file.
00001 /*
00002  *      FileSystem.cpp - The Pentagram File System
00003  *
00004  *  Copyright (C) 2002-2005  The Pentagram Team
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #include "pent_include.h"
00022 
00023 #include "FileSystem.h"
00024 
00025 #ifdef HAVE_SYS_STAT_H
00026 #include <sys/stat.h>
00027 #endif
00028 #ifdef HAVE_SYS_TYPES_H
00029 #include <sys/types.h>
00030 #endif
00031 #ifdef HAVE_UNISTD_H
00032 #include <unistd.h>
00033 #endif
00034 
00035 #include <string>
00036 using   std::string;
00037 
00038 #include "ListFiles.h"
00039 
00040 
00041 FileSystem* FileSystem::filesystem = 0;
00042 
00043 FileSystem::FileSystem(bool noforced)
00044         : noforcedvpaths(noforced), allowdataoverride(true)
00045 {
00046         con.Print(MM_INFO, "Creating FileSystem...\n");
00047 
00048         assert(filesystem == 0);
00049         filesystem = this;
00050 
00051 #ifdef UNDER_CE
00052         TCHAR module_filename[256];
00053         TCHAR *c = module_filename;
00054         TCHAR *last = NULL;
00055         GetModuleFileName(NULL, module_filename, 256);
00056 
00057         while (*c)
00058         {
00059                 if (*c == '/' || *c == '\\')
00060                         last = c;
00061 
00062                 c++;
00063         }
00064 
00065         if (last)
00066         {
00067                 *last = 0;
00068         }
00069         else
00070         {
00071                 module_filename[0] = '\\';
00072                 module_filename[1] = 0;
00073         }
00074 
00075         size_t len = _tcslen (module_filename) + 1;
00076         char *str = (char*) _alloca(len);
00077         WideCharToMultiByte(CP_ACP, 0, module_filename, -1, str, len, NULL, NULL);
00078 
00079         AddVirtualPath(".", str);
00080 
00081 #endif
00082 
00083 }
00084 
00085 FileSystem::~FileSystem()
00086 {
00087         con.Print(MM_INFO, "Destroying FileSystem...\n");
00088 
00089         filesystem = 0;
00090 }
00091 
00092 
00093 // Open a streaming file as readable. Streamed (0 on failure)
00094 IDataSource* FileSystem::ReadFile(const string &vfn, bool is_text)
00095 {
00096         string filename = vfn;
00097 
00098         IDataSource* data = checkBuiltinData(vfn, is_text);
00099 
00100         // allow data-override?
00101         if (!allowdataoverride && data) return data;
00102 
00103         std::ifstream *f = new std::ifstream();
00104         if(!rawopen(*f, filename, is_text)) {
00105                 delete f;
00106                 return data;
00107         }
00108 
00109         return new IFileDataSource(f);
00110 }
00111 
00112 // Open a streaming file as readable. Streamed (0 on failure)
00113 ODataSource* FileSystem::WriteFile(const string &vfn, bool is_text)
00114 {
00115         string filename = vfn;
00116         std::ofstream *f = new std::ofstream();
00117         if(!rawopen(*f, filename, is_text)) {
00118                 delete f;
00119                 return 0;
00120         }
00121         return new OFileDataSource(f);
00122 }
00123 
00124 /*
00125  *      Open a file for input,
00126  *      trying the original name (lower case), and the upper case version
00127  *      of the name.
00128  *
00129  *      Output: 0 if couldn't open.
00130  */
00131 
00132 bool FileSystem::rawopen
00133         (
00134         std::ifstream& in,                      // Input stream to open.
00135         const string &fname,            // May be converted to upper-case.
00136         bool is_text                            // Should the file be opened in text mode
00137         )
00138 {
00139         string name = fname;
00140         if (!rewrite_virtual_path(name)) {
00141 //              perr << "Illegal file access" << std::endl;
00142                 return false;
00143         }
00144 
00145 #if defined(MACOS) || (__GNUG__ > 2)
00146         std::ios_base::openmode mode = std::ios::in;
00147         if (!is_text) mode |= std::ios::binary;
00148 #elif defined(UNIX)
00149         int mode = std::ios::in;
00150 #else
00151         int mode = std::ios::in;
00152         if (!is_text) mode |= std::ios::binary;
00153 #endif
00154         switch_slashes(name);
00155 
00156         int uppercasecount = 0;
00157         do {
00158                 // We first "clear" the stream object. This is done to prevent
00159                 // problems when re-using stream objects
00160                 in.clear();
00161                 try {
00162                         in.open(name.c_str(), mode);                    // Try to open
00163                 } catch (std::exception &)
00164                 {}
00165                 if (in.good() && !in.fail()) return true;       // found it!
00166         } while (base_to_uppercase(name, ++uppercasecount));
00167 
00168         // file not found.
00169         return false;
00170 }
00171 
00172 /*
00173  *      Open a file for output,
00174  *      trying the original name (lower case), and the upper case version
00175  *      of the name.
00176  *
00177  *      Output: 0 if couldn't open.
00178  */
00179 
00180 bool FileSystem::rawopen
00181         (
00182         std::ofstream& out,                     // Output stream to open.
00183         const string &fname,                    // May be converted to upper-case.
00184         bool is_text                            // Should the file be opened in text mode
00185         )
00186 {
00187         string name = fname;
00188         if (!rewrite_virtual_path(name)) {
00189                 con.Print_err(MM_MAJOR_WARN, "Illegal file access\n");
00190                 return false;
00191         }
00192 
00193 #if defined(MACOS) || (__GNUG__ > 2)
00194         std::ios_base::openmode mode = std::ios::out | std::ios::trunc;
00195         if (!is_text) mode |= std::ios::binary;
00196 #elif defined(UNIX)
00197         int mode = std::ios::out | std::ios::trunc;
00198 #else
00199         int mode = std::ios::out | std::ios::trunc;
00200         if (!is_text) mode |= std::ios::binary;
00201 #endif
00202         switch_slashes(name);
00203 
00204         // We first "clear" the stream object. This is done to prevent
00205         // problems when re-using stream objects
00206         out.clear();
00207 
00208         int uppercasecount = 0;
00209         do {
00210                 out.open(name.c_str(), mode);           // Try to open
00211                 if (out.good()) return true;            // found it!
00212                 out.clear();                                            // Forget ye not
00213         } while (base_to_uppercase(name, ++uppercasecount));
00214 
00215         // file not found.
00216         return false;
00217 }
00218 
00219 void FileSystem::switch_slashes(string &name)
00220 {
00221 #ifdef WIN32
00222         for(string::iterator X = name.begin(); X != name.end(); ++X)
00223         {
00224                 if(*X == '/' )
00225                         *X =  '\\';
00226         }
00227 #elif defined(MACOS)
00228         // We use a component-wise algorithm (suggested by Yorick)
00229         // Basically, split the path along the "/" seperators
00230         // If a component is empty or '.', remove it. If it's '..', replace it
00231         // with the empty string. convert all / to :
00232         string::size_type       begIdx, endIdx;;
00233         string  component;
00234         string  new_name;
00235 
00236         if( name.at(0) != '/' )
00237                 new_name = ":";
00238 
00239         begIdx = name.find_first_not_of('/');
00240         while( begIdx != string::npos )
00241         {
00242                 endIdx = name.find_first_of('/', begIdx);
00243                 if( endIdx == std::string::npos )
00244                         component = name.substr(begIdx);
00245                 else
00246                         component = name.substr(begIdx, endIdx-begIdx);
00247                 if( component == ".." )
00248                         new_name += ":";
00249                 else if( !component.empty() && component != "." )
00250                 {
00251                         new_name += component;
00252                         if( endIdx != std::string::npos )
00253                                 new_name += ":";
00254                 }
00255                 begIdx = name.find_first_not_of('/', endIdx);
00256         }
00257 
00258         name = new_name;
00259 #else
00260         // do nothing
00261 #endif
00262 }
00263 
00264 /*
00265  *      Convert just the last 'count' parts of a filename to uppercase.
00266  *  returns false if there are less than 'count' parts
00267  */
00268 
00269 bool FileSystem::base_to_uppercase(string& str, int count)
00270 {
00271         if (count <= 0) return true;
00272 
00273         int todo = count;
00274                                         // Go backwards.
00275         string::reverse_iterator X;
00276         for(X = str.rbegin(); X != str.rend(); ++X)
00277         {
00278                                         // Stop at separator.
00279                 if (*X == '/' || *X == '\\' || *X == ':')
00280                         todo--;
00281                 if (todo <= 0)
00282                         break;
00283 
00284 #if (defined(BEOS) || defined(OPENBSD) || defined(CYGWIN) || defined(__MORPHOS__))
00285                 if ((*X >= 'a') && (*X <= 'z')) *X -= 32;
00286 #else
00287                 *X = static_cast<char>(std::toupper(*X));
00288 #endif
00289         }
00290         if (X == str.rend())
00291                 todo--; // start of pathname counts as separator too
00292 
00293         // false if it didn't reach 'count' parts
00294         return (todo <= 0);
00295 }
00296 
00297 bool FileSystem::AddVirtualPath(const string &vpath, const string &realpath, const bool create)
00298 {
00299         string vp = vpath, rp = realpath;
00300 
00301         // remove trailing slash
00302         if (vp.rfind('/') == vp.size() - 1)
00303                 vp.erase(vp.rfind('/'));
00304 
00305         if (rp.rfind('/') == rp.size() - 1)
00306                 rp.erase(rp.rfind('/'));
00307 
00308         if (rp.find("..") != string::npos) {
00309                 con.Printf_err(MM_MINOR_ERR,
00310                         "Error mounting virtual path \"%s\": \"..\" not allowed.\n",
00311                         vp.c_str());
00312                 return false;
00313         }
00314 
00315         // Finding Reserved Virtual Path Names
00316         // memory path is reserved
00317         if (vp == "@memory" || vp.substr(0, 8) == "@memory/")
00318         {
00319                 con.Printf_err(MM_MINOR_ERR,
00320                         "Error mounting virtual path \"%s\": %s\"@memory\" is a reserved virtual path name.\n",
00321                         vp.c_str());
00322                 return false;
00323         }
00324 
00325         string fullpath = rp;
00326         rewrite_virtual_path(fullpath);
00327         // When mounting a memory file, it wont exist, so don't attempt to create the dir
00328 #ifdef DEBUG
00329         con.Printf(MM_INFO, "virtual path \"%s\": %s\n", vp.c_str(), fullpath.c_str());
00330 #endif
00331         if (!(fullpath.substr(0, 8) == "@memory/"))
00332         {
00333                 if (!IsDir(fullpath)) {
00334                         if(!create) {
00335 #ifdef DEBUG
00336                                 con.Printf_err(MM_MINOR_WARN,
00337                                         "Problem mounting virtual path \"%s\": directory not found: %s\n",
00338                                         vp.c_str(), fullpath.c_str());
00339 #endif
00340                                 return false;
00341                         }
00342                         else {
00343                                 MkDir(fullpath);
00344                         }
00345                 }
00346         }
00347 
00348         virtualpaths[vp] = rp;
00349         return true;
00350 }
00351 
00352 bool FileSystem::RemoveVirtualPath(const string &vpath)
00353 {
00354         string vp = vpath;
00355 
00356         // remove trailing slash
00357         if (vp.rfind('/') == vp.size() - 1)
00358                 vp.erase(vp.rfind('/'));
00359 
00360         std::map<string, string>::iterator i = virtualpaths.find(vp);
00361 
00362         if (i == virtualpaths.end()) {
00363                 return false;
00364         } else {
00365                 virtualpaths.erase(vp);
00366                 return true;
00367         }
00368 }
00369 
00370 IDataSource* FileSystem::checkBuiltinData(const std::string& vfn, bool is_text)
00371 {
00372         // Is it a Memory file?
00373         std::map<string, MemoryFile*>::iterator mf = memoryfiles.find(vfn);
00374 
00375         if (mf != memoryfiles.end())
00376                 return new IBufferDataSource(mf->second->data,
00377                                                                          mf->second->len, is_text);
00378 
00379         return 0;
00380 }
00381 
00382 bool FileSystem::rewrite_virtual_path(string &vfn)
00383 {
00384         bool ret = false;
00385         string::size_type pos = vfn.size();
00386 
00387         while ((pos = vfn.rfind('/', pos)) != std::string::npos) {
00388 //              perr << vfn << ", " << vfn.substr(0, pos) << ", " << pos << std::endl;
00389                 std::map<string, string>::iterator p = virtualpaths.find(
00390                         vfn.substr(0, pos));
00391 
00392                 if (p != virtualpaths.end()) {
00393                         ret = true;
00394                         // rewrite first part of path
00395                         vfn = p->second + vfn.substr(pos);
00396                         pos = string::npos;
00397                 } else {
00398                         if (pos == 0)
00399                                 break;
00400                         --pos;
00401                 }
00402         }
00403 
00404         // We will allow all paths to work
00405         if (noforcedvpaths) ret = true;
00406 
00407         return ret;
00408 }
00409 
00410 
00411 bool FileSystem::IsDir(const string &path)
00412 {
00413 #ifndef UNDER_CE
00414         bool exists;
00415         struct stat sbuf;
00416 
00417         string name = path;
00418 
00419         int uppercasecount = 0;
00420         do {
00421                 exists = (stat(name.c_str(), &sbuf) == 0);
00422                 if (exists) {
00423                         if (S_ISDIR(sbuf.st_mode))
00424                                 return true;  // exists, and is a directory
00425                         else
00426                                 return false; // exists, but not a directory
00427                 }
00428         } while (base_to_uppercase(name, ++uppercasecount));
00429 #else
00430 
00431         // A little hack
00432         if (path == "." || path == "/" || path == "\\" ) return true;
00433 
00434         const TCHAR             *lpszT;
00435         WIN32_FIND_DATA fileinfo;
00436         HANDLE                  handle;
00437 
00438 #ifdef UNICODE
00439         const char *name = path.c_str();
00440         std::size_t nLen = strlen(name)+1;
00441         LPTSTR lpszT2 = (LPTSTR) _alloca(nLen*2);
00442         lpszT = lpszT2;
00443         MultiByteToWideChar(CP_ACP, 0, name, -1, lpszT2, nLen);
00444 #else
00445         lpszT = path.c_str();
00446 #endif
00447 
00448         handle = FindFirstFile (lpszT, &fileinfo);
00449 
00450         if (handle != INVALID_HANDLE_VALUE)
00451         {
00452                 FindClose (handle);
00453 
00454                 if (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return true;
00455         }
00456 #endif
00457 
00458         return false; // not found
00459 }
00460 
00461 /*
00462  *      Create a directory
00463  */
00464 
00465 int FileSystem::MkDir(const string &path)
00466 {
00467         string name = path;
00468         if(name[0]=='@')
00469                 rewrite_virtual_path(name);
00470 
00471 #if (defined(MACOSX) || defined(BEOS))
00472         // remove any trailing slashes
00473         string::size_type pos = name.find_last_not_of('/');
00474         if (pos != string::npos)
00475           name.resize(pos+1);
00476 #endif
00477 #if defined(WIN32) && defined(UNICODE)
00478         const char *n = name.c_str();
00479         int nLen = std::strlen(n)+1;
00480         LPTSTR lpszT = (LPTSTR) alloca(nLen*2);
00481         MultiByteToWideChar(CP_ACP, 0, n, -1, lpszT, nLen);
00482         return CreateDirectory(lpszT, NULL);
00483 #elif defined(WIN32)
00484         return mkdir(name.c_str());
00485 #else
00486         return mkdir(name.c_str(), 0750); // Create dir. if not already there.
00487 #endif
00488 }
00489 
00490 

Generated on Fri Jul 27 22:27:14 2007 for pentagram by  doxygen 1.4.7