Panda3D
 All Classes Functions Variables Enumerations
virtualFileSystem.cxx
00001 // Filename: virtualFileSystem.cxx
00002 // Created by:  drose (03Aug02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "virtualFileSystem.h"
00016 #include "virtualFileSimple.h"
00017 #include "virtualFileComposite.h"
00018 #include "virtualFileMount.h"
00019 #include "virtualFileMountMultifile.h"
00020 #include "virtualFileMountRamdisk.h"
00021 #include "virtualFileMountSystem.h"
00022 #include "streamWrapper.h"
00023 #include "dSearchPath.h"
00024 #include "dcast.h"
00025 #include "config_express.h"
00026 #include "executionEnvironment.h"
00027 #include "pset.h"
00028 
00029 VirtualFileSystem *VirtualFileSystem::_global_ptr = NULL;
00030 
00031 
00032 ////////////////////////////////////////////////////////////////////
00033 //     Function: VirtualFileSystem::Constructor
00034 //       Access: Published
00035 //  Description: 
00036 ////////////////////////////////////////////////////////////////////
00037 VirtualFileSystem::
00038 VirtualFileSystem() :
00039   vfs_case_sensitive
00040 ("vfs-case-sensitive", 
00041 #ifdef NDEBUG
00042  false,  // The default for a production build is not case-sensitive;
00043          // this avoids runtime overhead to verify case sensitivity.
00044 #else
00045  true,
00046 #endif
00047  PRC_DESC("Set this true to make the VirtualFileSystem present the native "
00048           "OS-provided filesystem as if it were a case-sensitive file "
00049           "system, even if it is not (e.g. on Windows).  This variable "
00050           "has no effect if the native filesystem is already case-sensitive, "
00051           "and it has no effect on mounted multifile systems, which are "
00052           "always case-sensitive.")),
00053   vfs_implicit_pz
00054   ("vfs-implicit-pz", true,
00055    PRC_DESC("When this is true, the VirtualFileSystem will pretend a named "
00056             "file exists even if it doesn't, as long as a filename with the "
00057             "same name and the additional extension .pz does exist.  In this "
00058             "case, the VirtualFileSystem will implicitly open the .pz file "
00059             "and decompress it on-the-fly.")),
00060   vfs_implicit_mf
00061   ("vfs-implicit-mf", false,
00062    PRC_DESC("When this is true, the VirtualFileSystem will automatically "
00063             "mount multifiles on-the-fly when they are used as directories.  "
00064             "For instance, opening the file /c/files/foo.mf/dirname/mytex.jpg "
00065             "will implicitly retrieve a file named 'dirname/mytex.jpg' "
00066             "within the multifile /c/files/foo.mf, even if the multifile "
00067             "has not already been mounted.  This makes all of your multifiles "
00068             "act like directories."))
00069 {
00070   _cwd = "/";
00071   _mount_seq = 0;
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: VirtualFileSystem::Destructor
00076 //       Access: Published
00077 //  Description: 
00078 ////////////////////////////////////////////////////////////////////
00079 VirtualFileSystem::
00080 ~VirtualFileSystem() {
00081   unmount_all();
00082 }
00083 
00084 ////////////////////////////////////////////////////////////////////
00085 //     Function: VirtualFileSystem::mount
00086 //       Access: Published
00087 //  Description: Mounts the indicated Multifile at the given mount
00088 //               point.
00089 ////////////////////////////////////////////////////////////////////
00090 bool VirtualFileSystem::
00091 mount(Multifile *multifile, const Filename &mount_point, int flags) {
00092   PT(VirtualFileMountMultifile) new_mount = 
00093     new VirtualFileMountMultifile(multifile);
00094   return mount(new_mount, mount_point, flags);
00095 }
00096 
00097 ////////////////////////////////////////////////////////////////////
00098 //     Function: VirtualFileSystem::mount
00099 //       Access: Published
00100 //  Description: Mounts the indicated system file or directory at the
00101 //               given mount point.  If the named file is a directory,
00102 //               mounts the directory.  If the named file is a
00103 //               Multifile, mounts it as a Multifile.  Returns true on
00104 //               success, false on failure.
00105 //
00106 //               A given system directory may be mounted to multiple
00107 //               different mount point, and the same mount point may
00108 //               share multiple system directories.  In the case of
00109 //               ambiguities (that is, two different files with
00110 //               exactly the same full pathname), the most-recently
00111 //               mounted system wins.
00112 //
00113 //               The filename specified as the first parameter must
00114 //               refer to a real, physical filename on disk; it cannot
00115 //               be a virtual file already appearing within the vfs
00116 //               filespace.  However, it is possible to mount such a
00117 //               file; see mount_loop() for this.
00118 ////
00119 //               Note that a mounted VirtualFileSystem directory is
00120 //               fully case-sensitive, unlike the native Windows file
00121 //               system, so you must refer to files within the virtual
00122 //               file system with exactly the right case.
00123 ////////////////////////////////////////////////////////////////////
00124 bool VirtualFileSystem::
00125 mount(const Filename &physical_filename, const Filename &mount_point, 
00126       int flags, const string &password) {
00127   if (!physical_filename.exists()) {
00128     express_cat->warning()
00129       << "Attempt to mount " << physical_filename << ", not found.\n";
00130     return false;
00131   }
00132 
00133   if (physical_filename.is_directory()) {
00134     PT(VirtualFileMountSystem) new_mount =
00135       new VirtualFileMountSystem(physical_filename);
00136     return mount(new_mount, mount_point, flags);
00137   } else {
00138     // It's not a directory; it must be a Multifile.
00139     PT(Multifile) multifile = new Multifile;
00140     multifile->set_encryption_password(password);
00141 
00142     // For now these are always opened read only.  Maybe later we'll
00143     // support read-write on Multifiles.
00144     flags |= MF_read_only;
00145     if (!multifile->open_read(physical_filename)) {
00146       return false;
00147     }
00148 
00149     return mount(multifile, mount_point, flags);
00150   }
00151 }
00152 
00153 ////////////////////////////////////////////////////////////////////
00154 //     Function: VirtualFileSystem::mount_loop
00155 //       Access: Published
00156 //  Description: This is similar to mount(), but it receives the name
00157 //               of a Multifile that already appears within the
00158 //               virtual file system.  It can be used to mount a
00159 //               Multifile that is itself hosted within a
00160 //               virtually-mounted Multifile.
00161 //
00162 //               This interface can also be used to mount physical
00163 //               files (that appear within the virtual filespace), but
00164 //               it cannot be used to mount directories.  Use mount()
00165 //               if you need to mount a directory.
00166 //
00167 //               Note that there is additional overhead, in the form
00168 //               of additional buffer copies of the data, for
00169 //               recursively mounting a multifile like this.
00170 ////////////////////////////////////////////////////////////////////
00171 bool VirtualFileSystem::
00172 mount_loop(const Filename &virtual_filename, const Filename &mount_point, 
00173            int flags, const string &password) {
00174   PT(VirtualFile) file = get_file(virtual_filename, false);
00175   if (file == NULL) {
00176     express_cat->warning()
00177       << "Attempt to mount " << virtual_filename << ", not found.\n";
00178     return false;
00179   }
00180 
00181   if (file->is_directory()) {
00182     PT(VirtualFileMountSystem) new_mount =
00183       new VirtualFileMountSystem(virtual_filename);
00184     return mount(new_mount, mount_point, flags);
00185 
00186   } else {
00187     // It's not a directory; it must be a Multifile.
00188     PT(Multifile) multifile = new Multifile;
00189     multifile->set_encryption_password(password);
00190 
00191     // For now these are always opened read only.  Maybe later we'll
00192     // support read-write on Multifiles.
00193     flags |= MF_read_only;
00194     if (!multifile->open_read(virtual_filename)) {
00195       return false;
00196     }
00197 
00198     return mount(multifile, mount_point, flags);
00199   }
00200 }
00201 
00202 ////////////////////////////////////////////////////////////////////
00203 //     Function: VirtualFileSystem::mount
00204 //       Access: Published
00205 //  Description: Adds the given VirtualFileMount object to the mount
00206 //               list.  This is a lower-level function that the other
00207 //               flavors of mount(); it requires you to create a
00208 //               VirtualFileMount object specifically.
00209 ////////////////////////////////////////////////////////////////////
00210 bool VirtualFileSystem::
00211 mount(VirtualFileMount *mount, const Filename &mount_point, int flags) {
00212   if (express_cat->is_debug()) {
00213     express_cat->debug()
00214       << "mount " << *mount << " under " << mount_point << "\n";
00215   }
00216 
00217   _lock.acquire();
00218   bool result = do_mount(mount, mount_point, flags);
00219   _lock.release();
00220   return result;
00221 }
00222 
00223 ////////////////////////////////////////////////////////////////////
00224 //     Function: VirtualFileSystem::unmount
00225 //       Access: Published
00226 //  Description: Unmounts all appearances of the indicated Multifile
00227 //               from the file system.  Returns the number of
00228 //               appearances unmounted.
00229 ////////////////////////////////////////////////////////////////////
00230 int VirtualFileSystem::
00231 unmount(Multifile *multifile) {
00232   _lock.acquire();
00233   Mounts::iterator ri, wi;
00234   wi = ri = _mounts.begin();
00235   while (ri != _mounts.end()) {
00236     VirtualFileMount *mount = (*ri);
00237     (*wi) = mount;
00238 
00239     if (mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) {
00240       VirtualFileMountMultifile *mmount = 
00241         DCAST(VirtualFileMountMultifile, mount);
00242       if (mmount->get_multifile() == multifile) {
00243         // Remove this one.  Don't increment wi.
00244         if (express_cat->is_debug()) {
00245           express_cat->debug()
00246             << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
00247         }
00248         mount->_file_system = NULL;
00249 
00250       } else {
00251         // Don't remove this one.
00252         ++wi;
00253       }
00254     } else {
00255       // Don't remove this one.
00256       ++wi;
00257     }
00258     ++ri;
00259   }
00260 
00261   int num_removed = _mounts.end() - wi;
00262   _mounts.erase(wi, _mounts.end());
00263   ++_mount_seq;
00264   _lock.release();
00265   return num_removed;
00266 }
00267 
00268 ////////////////////////////////////////////////////////////////////
00269 //     Function: VirtualFileSystem::unmount
00270 //       Access: Published
00271 //  Description: Unmounts all appearances of the indicated directory
00272 //               name or multifile name from the file system.  Returns
00273 //               the number of appearances unmounted.
00274 ////////////////////////////////////////////////////////////////////
00275 int VirtualFileSystem::
00276 unmount(const Filename &physical_filename) {
00277   _lock.acquire();
00278   Mounts::iterator ri, wi;
00279   wi = ri = _mounts.begin();
00280   while (ri != _mounts.end()) {
00281     VirtualFileMount *mount = (*ri);
00282     (*wi) = mount;
00283 
00284     if (mount->is_exact_type(VirtualFileMountSystem::get_class_type())) {
00285       VirtualFileMountSystem *smount = 
00286         DCAST(VirtualFileMountSystem, mount);
00287       if (smount->get_physical_filename() == physical_filename) {
00288         // Remove this one.  Don't increment wi.
00289         if (express_cat->is_debug()) {
00290           express_cat->debug()
00291             << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
00292         }
00293         mount->_file_system = NULL;
00294         
00295       } else {
00296         // Don't remove this one.
00297         ++wi;
00298       }
00299 
00300     } else if (mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) {
00301       VirtualFileMountMultifile *mmount = 
00302         DCAST(VirtualFileMountMultifile, mount);
00303       if (mmount->get_multifile()->get_multifile_name() == physical_filename) {
00304         // Remove this one.  Don't increment wi.
00305         if (express_cat->is_debug()) {
00306           express_cat->debug()
00307             << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
00308         }
00309         mount->_file_system = NULL;
00310 
00311       } else {
00312         // Don't remove this one.
00313         ++wi;
00314       }
00315 
00316     } else {
00317       // Don't remove this one.
00318       ++wi;
00319     }
00320     ++ri;
00321   }
00322 
00323   int num_removed = _mounts.end() - wi;
00324   _mounts.erase(wi, _mounts.end());
00325   ++_mount_seq;
00326   _lock.release();
00327   return num_removed;
00328 }
00329 
00330 ////////////////////////////////////////////////////////////////////
00331 //     Function: VirtualFileSystem::unmount
00332 //       Access: Published
00333 //  Description: Unmounts the indicated VirtualFileMount object
00334 //               from the file system.  Returns the number of
00335 //               appearances unmounted.
00336 ////////////////////////////////////////////////////////////////////
00337 int VirtualFileSystem::
00338 unmount(VirtualFileMount *mount) {
00339   _lock.acquire();
00340   Mounts::iterator ri, wi;
00341   wi = ri = _mounts.begin();
00342   while (ri != _mounts.end()) {
00343     (*wi) = (*ri);
00344     if ((*ri) == mount) {
00345       // Remove this one.  Don't increment wi.
00346       if (express_cat->is_debug()) {
00347         express_cat->debug()
00348           << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
00349       }
00350       (*ri)->_file_system = NULL;
00351 
00352     } else {
00353       // Don't remove this one.
00354       ++wi;
00355     }
00356     ++ri;
00357   }
00358 
00359   int num_removed = _mounts.end() - wi;
00360   _mounts.erase(wi, _mounts.end());
00361   ++_mount_seq;
00362   _lock.release();
00363   return num_removed;
00364 }
00365 
00366 ////////////////////////////////////////////////////////////////////
00367 //     Function: VirtualFileSystem::unmount_point
00368 //       Access: Published
00369 //  Description: Unmounts all systems attached to the given mount
00370 //               point from the file system.  Returns the number of
00371 //               appearances unmounted.
00372 ////////////////////////////////////////////////////////////////////
00373 int VirtualFileSystem::
00374 unmount_point(const Filename &mount_point) {
00375   _lock.acquire();
00376   Filename nmp = normalize_mount_point(mount_point);
00377   Mounts::iterator ri, wi;
00378   wi = ri = _mounts.begin();
00379   while (ri != _mounts.end()) {
00380     VirtualFileMount *mount = (*ri);
00381     (*wi) = mount;
00382 
00383     if (mount->get_mount_point() == nmp) {
00384       // Remove this one.  Don't increment wi.
00385       if (express_cat->is_debug()) {
00386         express_cat->debug()
00387           << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
00388       }
00389       mount->_file_system = NULL;
00390 
00391     } else {
00392       // Don't remove this one.
00393       ++wi;
00394     }
00395     ++ri;
00396   }
00397 
00398   int num_removed = _mounts.end() - wi;
00399   _mounts.erase(wi, _mounts.end());
00400   ++_mount_seq;
00401   _lock.release();
00402   return num_removed;
00403 }
00404 
00405 ////////////////////////////////////////////////////////////////////
00406 //     Function: VirtualFileSystem::unmount_all
00407 //       Access: Published
00408 //  Description: Unmounts all files from the file system.  Returns the
00409 //               number of systems unmounted.
00410 ////////////////////////////////////////////////////////////////////
00411 int VirtualFileSystem::
00412 unmount_all() {
00413   _lock.acquire();
00414   Mounts::iterator ri;
00415   for (ri = _mounts.begin(); ri != _mounts.end(); ++ri) {
00416     VirtualFileMount *mount = (*ri);
00417     if (express_cat->is_debug()) {
00418       express_cat->debug()
00419         << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
00420     }
00421     mount->_file_system = NULL;
00422   }
00423 
00424   int num_removed = _mounts.size();
00425   _mounts.clear();
00426   ++_mount_seq;
00427   _lock.release();
00428   return num_removed;
00429 }
00430 
00431 ////////////////////////////////////////////////////////////////////
00432 //     Function: VirtualFileSystem::get_num_mounts
00433 //       Access: Published
00434 //  Description: Returns the number of individual mounts in the
00435 //               system.
00436 ////////////////////////////////////////////////////////////////////
00437 int VirtualFileSystem::
00438 get_num_mounts() const {
00439   ((VirtualFileSystem *)this)->_lock.acquire();
00440   int result = _mounts.size();
00441   ((VirtualFileSystem *)this)->_lock.release();
00442   return result;
00443 }
00444 
00445 ////////////////////////////////////////////////////////////////////
00446 //     Function: VirtualFileSystem::get_mount
00447 //       Access: Published
00448 //  Description: Returns the nth mount in the system.
00449 ////////////////////////////////////////////////////////////////////
00450 PT(VirtualFileMount) VirtualFileSystem::
00451 get_mount(int n) const {
00452   ((VirtualFileSystem *)this)->_lock.acquire();
00453   nassertd(n >= 0 && n < (int)_mounts.size()) {
00454     ((VirtualFileSystem *)this)->_lock.release();
00455     return NULL;
00456   }
00457   PT(VirtualFileMount) result = _mounts[n];
00458   ((VirtualFileSystem *)this)->_lock.release();
00459   return result;
00460 }
00461 
00462 ////////////////////////////////////////////////////////////////////
00463 //     Function: VirtualFileSystem::chdir
00464 //       Access: Published
00465 //  Description: Changes the current directory.  This is used to
00466 //               resolve relative pathnames in get_file() and/or
00467 //               find_file().  Returns true if successful, false
00468 //               otherwise.
00469 ////////////////////////////////////////////////////////////////////
00470 bool VirtualFileSystem::
00471 chdir(const Filename &new_directory) {
00472   _lock.acquire();
00473   if (new_directory == "/") {
00474     // We can always return to the root.
00475     _cwd = new_directory;
00476     _lock.release();
00477     return true;
00478   }
00479 
00480   PT(VirtualFile) file = do_get_file(new_directory, OF_status_only);
00481   if (file != (VirtualFile *)NULL && file->is_directory()) {
00482     _cwd = file->get_filename();
00483     _lock.release();
00484     return true;
00485   }
00486   _lock.release();
00487   return false;
00488 }
00489 
00490 ////////////////////////////////////////////////////////////////////
00491 //     Function: VirtualFileSystem::get_cwd
00492 //       Access: Published
00493 //  Description: Returns the current directory name.  See chdir().
00494 ////////////////////////////////////////////////////////////////////
00495 Filename VirtualFileSystem::
00496 get_cwd() const {
00497   ((VirtualFileSystem *)this)->_lock.acquire();
00498   Filename result = _cwd;
00499   ((VirtualFileSystem *)this)->_lock.release();
00500   return result;
00501 }
00502 
00503 ////////////////////////////////////////////////////////////////////
00504 //     Function: VirtualFileSystem::make_directory
00505 //       Access: Published
00506 //  Description: Attempts to create a directory within the file
00507 //               system.  Returns true on success, false on failure
00508 //               (for instance, because the parent directory does not
00509 //               exist, or is read-only).  If the directory already
00510 //               existed prior to this call, returns true.
00511 ////////////////////////////////////////////////////////////////////
00512 bool VirtualFileSystem::
00513 make_directory(const Filename &filename) {
00514   _lock.acquire();
00515   PT(VirtualFile) result = do_get_file(filename, OF_make_directory);
00516   _lock.release();
00517   return result->is_directory();
00518 }
00519 
00520 ////////////////////////////////////////////////////////////////////
00521 //     Function: VirtualFileSystem::make_directory_full
00522 //       Access: Published
00523 //  Description: Attempts to create a directory within the file
00524 //               system.  Will also create any intervening directories
00525 //               needed.  Returns true on success, false on failure.
00526 ////////////////////////////////////////////////////////////////////
00527 bool VirtualFileSystem::
00528 make_directory_full(const Filename &filename) {
00529   _lock.acquire();
00530 
00531   // First, make sure everything up to the last path is known.  We
00532   // don't care too much if any of these fail; maybe they failed
00533   // because the directory was already there.
00534   string dirname = filename;
00535   size_t slash = dirname.find('/', 1);
00536   while (slash != string::npos) {
00537     Filename component(dirname.substr(0, slash));
00538     do_get_file(component, OF_make_directory);
00539     slash = dirname.find('/', slash + 1);
00540   }
00541 
00542   // Now make the last one, and check the return value.
00543   PT(VirtualFile) result = do_get_file(filename, OF_make_directory);
00544   _lock.release();
00545   return result->is_directory();
00546 }
00547 
00548 ////////////////////////////////////////////////////////////////////
00549 //     Function: VirtualFileSystem::get_file
00550 //       Access: Published
00551 //  Description: Looks up the file by the indicated name in the file
00552 //               system.  Returns a VirtualFile pointer representing
00553 //               the file if it is found, or NULL if it is not.
00554 //
00555 //               If status_only is true, the file will be checked for
00556 //               existence and length and so on, but the returned
00557 //               file's contents cannot be read.  This is an
00558 //               optimization which is especially important for
00559 //               certain mount types, for instance HTTP, for which
00560 //               opening a file to determine its status is
00561 //               substantially less expensive than opening it to read
00562 //               its contents.
00563 ////////////////////////////////////////////////////////////////////
00564 PT(VirtualFile) VirtualFileSystem::
00565 get_file(const Filename &filename, bool status_only) const {
00566   int open_flags = status_only ? 0 : OF_status_only;
00567   ((VirtualFileSystem *)this)->_lock.acquire();
00568   PT(VirtualFile) result = do_get_file(filename, open_flags);
00569   ((VirtualFileSystem *)this)->_lock.release();
00570   return result;
00571 }
00572 
00573 ////////////////////////////////////////////////////////////////////
00574 //     Function: VirtualFileSystem::create_file
00575 //       Access: Published
00576 //  Description: Attempts to create a file by the indicated name in
00577 //               the filesystem, if possible, and returns it.  If a
00578 //               file by this name already exists, returns the same
00579 //               thing as get_file().  If the filename is located
00580 //               within a read-only directory, or the directory
00581 //               doesn't exist, returns NULL.
00582 ////////////////////////////////////////////////////////////////////
00583 PT(VirtualFile) VirtualFileSystem::
00584 create_file(const Filename &filename) {
00585   ((VirtualFileSystem *)this)->_lock.acquire();
00586   PT(VirtualFile) result = do_get_file(filename, OF_create_file);
00587   ((VirtualFileSystem *)this)->_lock.release();
00588   return result;
00589 }
00590 
00591 ////////////////////////////////////////////////////////////////////
00592 //     Function: VirtualFileSystem::find_file
00593 //       Access: Published
00594 //  Description: Uses the indicated search path to find the file
00595 //               within the file system.  Returns the first occurrence
00596 //               of the file found, or NULL if the file cannot be
00597 //               found.
00598 ////////////////////////////////////////////////////////////////////
00599 PT(VirtualFile) VirtualFileSystem::
00600 find_file(const Filename &filename, const DSearchPath &searchpath,
00601           bool status_only) const {
00602   if (!filename.is_local()) {
00603     return get_file(filename, status_only);
00604   }
00605 
00606   int num_directories = searchpath.get_num_directories();
00607   for (int i = 0; i < num_directories; ++i) {
00608     Filename match(searchpath.get_directory(i), filename);
00609     if (searchpath.get_directory(i) == "." && 
00610         filename.is_fully_qualified()) {
00611       // A special case for the "." directory: to avoid prefixing an
00612       // endless stream of ./ in front of files, if the filename
00613       // already has a ./ prefixed (i.e. is_fully_qualified() is
00614       // true), we don't prefix another one.
00615       match = filename;
00616     }
00617     PT(VirtualFile) found_file = get_file(match, status_only);
00618     if (found_file != (VirtualFile *)NULL) {
00619       return found_file;
00620     }
00621   }
00622 
00623   return NULL;
00624 }
00625 
00626 ////////////////////////////////////////////////////////////////////
00627 //     Function: VirtualFileSystem::delete_file
00628 //       Access: Public
00629 //  Description: Attempts to delete the indicated file or directory.
00630 //               This can remove a single file or an empty directory.
00631 //               It will not remove a nonempty directory.  Returns
00632 //               true on success, false on failure.
00633 ////////////////////////////////////////////////////////////////////
00634 bool VirtualFileSystem::
00635 delete_file(const Filename &filename) {
00636   PT(VirtualFile) file = get_file(filename, true);
00637   if (file == (VirtualFile *)NULL) {
00638     return false;
00639   }
00640 
00641   return file->delete_file();
00642 }
00643 
00644 ////////////////////////////////////////////////////////////////////
00645 //     Function: VirtualFileSystem::rename_file
00646 //       Access: Public
00647 //  Description: Attempts to move or rename the indicated file or
00648 //               directory.  If the original file is an ordinary file,
00649 //               it will quietly replace any already-existing file in
00650 //               the new filename (but not a directory).  If the
00651 //               original file is a directory, the new filename must
00652 //               not already exist.
00653 //
00654 //               If the file is a directory, the new filename must be
00655 //               within the same mount point.  If the file is an
00656 //               ordinary file, the new filename may be anywhere; but
00657 //               if it is not within the same mount point then the
00658 //               rename operation is automatically performed as a
00659 //               two-step copy-and-delete operation.
00660 ////////////////////////////////////////////////////////////////////
00661 bool VirtualFileSystem::
00662 rename_file(const Filename &orig_filename, const Filename &new_filename) {
00663   _lock.acquire();
00664   PT(VirtualFile) orig_file = do_get_file(orig_filename, OF_status_only);
00665   if (orig_file == (VirtualFile *)NULL) {
00666     _lock.release();
00667     return false;
00668   }
00669 
00670   PT(VirtualFile) new_file = do_get_file(new_filename, OF_status_only | OF_allow_nonexist);
00671   if (new_file == (VirtualFile *)NULL) {
00672     _lock.release();
00673     return false;
00674   }
00675 
00676   _lock.release();
00677 
00678   return orig_file->rename_file(new_file);
00679 }
00680 
00681 ////////////////////////////////////////////////////////////////////
00682 //     Function: VirtualFileSystem::copy_file
00683 //       Access: Public
00684 //  Description: Attempts to copy the contents of the indicated file
00685 //               to the indicated file.  Returns true on success,
00686 //               false on failure.
00687 ////////////////////////////////////////////////////////////////////
00688 bool VirtualFileSystem::
00689 copy_file(const Filename &orig_filename, const Filename &new_filename) {
00690   PT(VirtualFile) orig_file = get_file(orig_filename, true);
00691   if (orig_file == (VirtualFile *)NULL) {
00692     return false;
00693   }
00694 
00695   PT(VirtualFile) new_file = create_file(new_filename);
00696   if (new_file == (VirtualFile *)NULL) {
00697     return false;
00698   }
00699 
00700   return orig_file->copy_file(new_file);
00701 }
00702 
00703 ////////////////////////////////////////////////////////////////////
00704 //     Function: VirtualFileSystem::resolve_filename
00705 //       Access: Public
00706 //  Description: Searches the given search path for the filename.  If
00707 //               it is found, updates the filename to the full
00708 //               pathname found and returns true; otherwise, returns
00709 //               false.
00710 ////////////////////////////////////////////////////////////////////
00711 bool VirtualFileSystem::
00712 resolve_filename(Filename &filename,
00713                  const DSearchPath &searchpath,
00714                  const string &default_extension) const {
00715   PT(VirtualFile) found;
00716 
00717   if (filename.is_local()) {
00718     found = find_file(filename, searchpath, true);
00719 
00720     if (found.is_null()) {
00721       // We didn't find it with the given extension; can we try the
00722       // default extension?
00723       if (filename.get_extension().empty() && !default_extension.empty()) {
00724         Filename try_ext = filename;
00725         try_ext.set_extension(default_extension);
00726         found = find_file(try_ext, searchpath, true);
00727       }
00728     }
00729   } else {
00730     if (exists(filename)) {
00731       // The full pathname exists.  Return true.
00732       return true;
00733     } else {
00734       // The full pathname doesn't exist with the given extension;
00735       // does it exist with the default extension?
00736       if (filename.get_extension().empty() && !default_extension.empty()) {
00737         Filename try_ext = filename;
00738         try_ext.set_extension(default_extension);
00739         found = get_file(try_ext, true);
00740       }
00741     }
00742   }
00743 
00744   if (!found.is_null()) {
00745     filename = found->get_original_filename();
00746     return true;
00747   }
00748 
00749   return false;
00750 }
00751 
00752 ////////////////////////////////////////////////////////////////////
00753 //     Function: VirtualFileSystem::find_all_files
00754 //       Access: Public
00755 //  Description: Searches all the directories in the search list for
00756 //               the indicated file, in order.  Fills up the results
00757 //               list with *all* of the matching filenames found, if
00758 //               any.  Returns the number of matches found.
00759 //
00760 //               It is the responsibility of the the caller to clear
00761 //               the results list first; otherwise, the newly-found
00762 //               files will be appended to the list.
00763 ////////////////////////////////////////////////////////////////////
00764 int VirtualFileSystem::
00765 find_all_files(const Filename &filename, const DSearchPath &searchpath,
00766                DSearchPath::Results &results) const {
00767   int num_added = 0;
00768 
00769   if (filename.is_local()) {
00770     int num_directories = searchpath.get_num_directories();
00771     for (int i = 0; i < num_directories; ++i) {
00772       Filename match(searchpath.get_directory(i), filename);
00773       if (exists(match)) {
00774         if (searchpath.get_directory(i) == "." &&
00775             filename.is_fully_qualified()) {
00776           // A special case for the "." directory: to avoid prefixing
00777           // an endless stream of ./ in front of files, if the
00778           // filename already has a ./ prefixed
00779           // (i.e. is_fully_fully_qualified() is true), we don't
00780           // prefix another one.
00781           results.add_file(filename);
00782         } else {
00783           results.add_file(match);
00784         }
00785         ++num_added;
00786       }
00787     }
00788   }
00789 
00790   return num_added;
00791 }
00792 
00793 ////////////////////////////////////////////////////////////////////
00794 //     Function: VirtualFileSystem::write
00795 //       Access: Published
00796 //  Description: Print debugging information.
00797 //               (e.g. from Python or gdb prompt).
00798 ////////////////////////////////////////////////////////////////////
00799 void VirtualFileSystem::
00800 write(ostream &out) const {
00801   ((VirtualFileSystem *)this)->_lock.acquire();
00802   Mounts::const_iterator mi;
00803   for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
00804     VirtualFileMount *mount = (*mi);
00805     mount->write(out);
00806   }
00807   ((VirtualFileSystem *)this)->_lock.release();
00808 }
00809 
00810 
00811 ////////////////////////////////////////////////////////////////////
00812 //     Function: VirtualFileSystem::get_global_ptr
00813 //       Access: Published, Static
00814 //  Description: Returns the default global VirtualFileSystem.  You
00815 //               may create your own personal VirtualFileSystem
00816 //               objects and use them for whatever you like, but Panda
00817 //               will attempt to load models and stuff from this
00818 //               default object.
00819 //
00820 //               Initially, the global VirtualFileSystem is set up to
00821 //               mount the OS filesystem to root; i.e. it is
00822 //               equivalent to the OS filesystem.  This may be
00823 //               subsequently adjusted by the user.
00824 ////////////////////////////////////////////////////////////////////
00825 VirtualFileSystem *VirtualFileSystem::
00826 get_global_ptr() {
00827   if (_global_ptr == (VirtualFileSystem *)NULL) {
00828     // Make sure this is initialized.
00829     init_libexpress();
00830 
00831     _global_ptr = new VirtualFileSystem;
00832     
00833     // Set up the default mounts.  First, there is always the root
00834     // mount.
00835     _global_ptr->mount("/", "/", 0);
00836 
00837     // And our initial cwd comes from the environment.
00838     _global_ptr->chdir(ExecutionEnvironment::get_cwd());
00839 
00840     // Then, we add whatever mounts are listed in the Configrc file.
00841     ConfigVariableList mounts
00842       ("vfs-mount",
00843        PRC_DESC("vfs-mount system-filename mount-point [options]"));
00844 
00845     int num_unique_values = mounts.get_num_unique_values();
00846     for (int i = 0; i < num_unique_values; i++) {
00847       string mount_desc = mounts.get_unique_value(i);
00848 
00849       // The vfs-mount syntax is:
00850       
00851       // vfs-mount system-filename mount-point [options]
00852       
00853       // The last two spaces mark the beginning of the mount point,
00854       // and of the options, respectively.  There might be multiple
00855       // spaces in the system filename, which are part of the
00856       // filename.
00857       
00858       // The last space marks the beginning of the mount point.
00859       // Spaces before that are part of the system filename.
00860       size_t space = mount_desc.rfind(' ');
00861       if (space == string::npos) {
00862         express_cat.warning()
00863           << "No space in vfs-mount descriptor: " << mount_desc << "\n";
00864         
00865       } else {
00866         string mount_point = mount_desc.substr(space + 1);
00867         while (space > 0 && isspace(mount_desc[space - 1])) {
00868           space--;
00869         }
00870         mount_desc = mount_desc.substr(0, space);
00871         string options;
00872         
00873         space = mount_desc.rfind(' ');
00874         if (space != string::npos) {
00875           // If there's another space, we have the optional options field.
00876           options = mount_point;
00877           mount_point = mount_desc.substr(space + 1);
00878           while (space > 0 && isspace(mount_desc[space - 1])) {
00879             --space;
00880           }
00881           mount_desc = mount_desc.substr(0, space);
00882         }
00883         
00884         mount_desc = ExecutionEnvironment::expand_string(mount_desc);
00885         Filename physical_filename = Filename::from_os_specific(mount_desc);
00886         
00887         int flags;
00888         string password;
00889         parse_options(options, flags, password);
00890         _global_ptr->mount(physical_filename, mount_point, flags, password);
00891       }
00892     }
00893 
00894     ConfigVariableString vfs_mount_ramdisk
00895       ("vfs-mount-ramdisk", "",
00896        PRC_DESC("vfs-mount-ramdisk mount-point [options]"));
00897     if (!vfs_mount_ramdisk.empty()) {
00898       string mount_point = vfs_mount_ramdisk;
00899       string options;
00900         
00901       size_t space = mount_point.rfind(' ');
00902       if (space != string::npos) {
00903         // If there's a space, we have the optional options field.
00904         options = mount_point.substr(space + 1);
00905         while (space > 0 && isspace(mount_point[space - 1])) {
00906           --space;
00907         }
00908         mount_point = mount_point.substr(0, space);
00909       }
00910         
00911       int flags;
00912       string password;
00913       parse_options(options, flags, password);
00914 
00915       PT(VirtualFileMount) ramdisk = new VirtualFileMountRamdisk;
00916       _global_ptr->mount(ramdisk, mount_point, flags);
00917     }
00918   }
00919 
00920   return _global_ptr;
00921 }
00922 
00923 #ifdef HAVE_PYTHON
00924 ////////////////////////////////////////////////////////////////////
00925 //     Function: VirtualFileSystem::__py__read_file
00926 //       Access: Published
00927 //  Description: Convenience function; returns the entire contents of
00928 //               the indicated file as a string.
00929 //
00930 //               This variant on read_file() is implemented directly
00931 //               for Python, as a small optimization, to avoid the
00932 //               double-construction of a string object that would be
00933 //               otherwise required for the return value.
00934 ////////////////////////////////////////////////////////////////////
00935 PyObject *VirtualFileSystem::
00936 __py__read_file(const Filename &filename, bool auto_unwrap) const {
00937   pvector<unsigned char> pv;
00938   bool okflag = read_file(filename, pv, auto_unwrap);
00939   nassertr(okflag, NULL);
00940 
00941   if (pv.empty()) {
00942     return PyString_FromStringAndSize("", 0);
00943   } else {
00944     return PyString_FromStringAndSize((const char *)&pv[0], pv.size());
00945   }
00946 }
00947 #endif  // HAVE_PYTHON
00948 
00949 ////////////////////////////////////////////////////////////////////
00950 //     Function: VirtualFileSystem::open_read_file
00951 //       Access: Published
00952 //  Description: Convenience function; returns a newly allocated
00953 //               istream if the file exists and can be read, or NULL
00954 //               otherwise.  Does not return an invalid istream.
00955 //
00956 //               If auto_unwrap is true, an explicitly-named .pz file
00957 //               is automatically decompressed and the decompressed
00958 //               contents are returned.  This is different than
00959 //               vfs-implicit-pz, which will automatically decompress
00960 //               a file if the extension .pz is *not* given.
00961 ////////////////////////////////////////////////////////////////////
00962 istream *VirtualFileSystem::
00963 open_read_file(const Filename &filename, bool auto_unwrap) const {
00964   PT(VirtualFile) file = get_file(filename, false);
00965   if (file == (VirtualFile *)NULL) {
00966     return NULL;
00967   }
00968   istream *str = file->open_read_file(auto_unwrap);
00969   if (str != (istream *)NULL && str->fail()) {
00970     close_read_file(str);
00971     str = (istream *)NULL;
00972   }
00973   return str;
00974 }
00975 
00976 ////////////////////////////////////////////////////////////////////
00977 //     Function: VirtualFileSystem::close_read_file
00978 //       Access: Published, Static
00979 //  Description: Closes a file opened by a previous call to
00980 //               open_read_file().  This really just deletes the
00981 //               istream pointer, but it is recommended to use this
00982 //               interface instead of deleting it explicitly, to help
00983 //               work around compiler issues.
00984 ////////////////////////////////////////////////////////////////////
00985 void VirtualFileSystem::
00986 close_read_file(istream *stream) {
00987   if (stream != (istream *)NULL) {
00988     // For some reason--compiler bug in gcc 3.2?--explicitly deleting
00989     // the stream pointer does not call the appropriate global delete
00990     // function; instead apparently calling the system delete
00991     // function.  So we call the delete function by hand instead.
00992 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
00993     stream->~istream();
00994     (*global_operator_delete)(stream);
00995 #else
00996     delete stream;
00997 #endif
00998   }
00999 }
01000 
01001 #ifdef HAVE_PYTHON
01002 ////////////////////////////////////////////////////////////////////
01003 //     Function: VirtualFileSystem::__py__write_file
01004 //       Access: Published
01005 //  Description: Convenience function; writes the entire contents of
01006 //               the indicated file as a string.
01007 //
01008 //               This variant on write_file() is implemented directly
01009 //               for Python, as a small optimization, to avoid the
01010 //               double-construction of a string object that would be
01011 //               otherwise required.
01012 ////////////////////////////////////////////////////////////////////
01013 PyObject *VirtualFileSystem::
01014 __py__write_file(const Filename &filename, PyObject *data, bool auto_wrap) {
01015   char *buffer;
01016   Py_ssize_t length;
01017   if (PyString_AsStringAndSize(data, &buffer, &length) == -1) {
01018     return NULL;
01019   }
01020    
01021   bool result = write_file(filename, (const unsigned char *)buffer, length, auto_wrap);
01022   return PyBool_FromLong(result);
01023 }
01024 #endif  // HAVE_PYTHON
01025 
01026 ////////////////////////////////////////////////////////////////////
01027 //     Function: VirtualFileSystem::open_write_file
01028 //       Access: Published
01029 //  Description: Convenience function; returns a newly allocated
01030 //               ostream if the file exists and can be written, or
01031 //               NULL otherwise.  Does not return an invalid ostream.
01032 //
01033 //               If auto_wrap is true, an explicitly-named .pz file is
01034 //               automatically compressed while writing.  If truncate
01035 //               is true, the file is truncated to zero length before
01036 //               writing.
01037 ////////////////////////////////////////////////////////////////////
01038 ostream *VirtualFileSystem::
01039 open_write_file(const Filename &filename, bool auto_wrap, bool truncate) {
01040   PT(VirtualFile) file = create_file(filename);
01041   if (file == (VirtualFile *)NULL) {
01042     return NULL;
01043   }
01044   ostream *str = file->open_write_file(auto_wrap, truncate);
01045   if (str != (ostream *)NULL && str->fail()) {
01046     close_write_file(str);
01047     str = (ostream *)NULL;
01048   }
01049   return str;
01050 }
01051 
01052 ////////////////////////////////////////////////////////////////////
01053 //     Function: VirtualFileSystem::open_append_file
01054 //       Access: Published
01055 //  Description: Works like open_write_file(), but the file is opened
01056 //               in append mode.  Like open_write_file, the returned
01057 //               pointer should eventually be passed to
01058 //               close_write_file().
01059 ////////////////////////////////////////////////////////////////////
01060 ostream *VirtualFileSystem::
01061 open_append_file(const Filename &filename) {
01062   PT(VirtualFile) file = create_file(filename);
01063   if (file == (VirtualFile *)NULL) {
01064     return NULL;
01065   }
01066   ostream *str = file->open_append_file();
01067   if (str != (ostream *)NULL && str->fail()) {
01068     close_write_file(str);
01069     str = (ostream *)NULL;
01070   }
01071   return str;
01072 }
01073 
01074 ////////////////////////////////////////////////////////////////////
01075 //     Function: VirtualFileSystem::close_write_file
01076 //       Access: Published, Static
01077 //  Description: Closes a file opened by a previous call to
01078 //               open_write_file().  This really just deletes the
01079 //               ostream pointer, but it is recommended to use this
01080 //               interface instead of deleting it explicitly, to help
01081 //               work around compiler issues.
01082 ////////////////////////////////////////////////////////////////////
01083 void VirtualFileSystem::
01084 close_write_file(ostream *stream) {
01085   if (stream != (ostream *)NULL) {
01086 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
01087     stream->~ostream();
01088     (*global_operator_delete)(stream);
01089 #else
01090     delete stream;
01091 #endif
01092   }
01093 }
01094 
01095 ////////////////////////////////////////////////////////////////////
01096 //     Function: VirtualFileSystem::open_read_write_file
01097 //       Access: Published
01098 //  Description: Convenience function; returns a newly allocated
01099 //               iostream if the file exists and can be written, or
01100 //               NULL otherwise.  Does not return an invalid iostream.
01101 ////////////////////////////////////////////////////////////////////
01102 iostream *VirtualFileSystem::
01103 open_read_write_file(const Filename &filename, bool truncate) {
01104   PT(VirtualFile) file = create_file(filename);
01105   if (file == (VirtualFile *)NULL) {
01106     return NULL;
01107   }
01108   iostream *str = file->open_read_write_file(truncate);
01109   if (str != (iostream *)NULL && str->fail()) {
01110     close_read_write_file(str);
01111     str = (iostream *)NULL;
01112   }
01113   return str;
01114 }
01115 
01116 ////////////////////////////////////////////////////////////////////
01117 //     Function: VirtualFileSystem::open_read_append_file
01118 //       Access: Published
01119 //  Description: Works like open_read_write_file(), but the file is opened
01120 //               in append mode.  Like open_read_write_file, the returned
01121 //               pointer should eventually be passed to
01122 //               close_read_write_file().
01123 ////////////////////////////////////////////////////////////////////
01124 iostream *VirtualFileSystem::
01125 open_read_append_file(const Filename &filename) {
01126   PT(VirtualFile) file = create_file(filename);
01127   if (file == (VirtualFile *)NULL) {
01128     return NULL;
01129   }
01130   iostream *str = file->open_read_append_file();
01131   if (str != (iostream *)NULL && str->fail()) {
01132     close_read_write_file(str);
01133     str = (iostream *)NULL;
01134   }
01135   return str;
01136 }
01137 
01138 ////////////////////////////////////////////////////////////////////
01139 //     Function: VirtualFileSystem::close_read_write_file
01140 //       Access: Published, Static
01141 //  Description: Closes a file opened by a previous call to
01142 //               open_read_write_file().  This really just deletes the
01143 //               iostream pointer, but it is recommended to use this
01144 //               interface instead of deleting it explicitly, to help
01145 //               work around compiler issues.
01146 ////////////////////////////////////////////////////////////////////
01147 void VirtualFileSystem::
01148 close_read_write_file(iostream *stream) {
01149   if (stream != (iostream *)NULL) {
01150 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
01151     stream->~iostream();
01152     (*global_operator_delete)(stream);
01153 #else
01154     delete stream;
01155 #endif
01156   }
01157 }
01158 
01159 ////////////////////////////////////////////////////////////////////
01160 //     Function: VirtualFileSystem::atomic_compare_and_exchange_contents
01161 //       Access: Public
01162 //  Description: See Filename::atomic_compare_and_exchange_contents().
01163 ////////////////////////////////////////////////////////////////////
01164 bool VirtualFileSystem::
01165 atomic_compare_and_exchange_contents(const Filename &filename, string &orig_contents,
01166                                      const string &old_contents, 
01167                                      const string &new_contents) {
01168   PT(VirtualFile) file = create_file(filename);
01169   if (file == NULL) {
01170     return false;
01171   }
01172 
01173   return file->atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents);
01174 }
01175 
01176 ////////////////////////////////////////////////////////////////////
01177 //     Function: VirtualFileSystem::atomic_read_contents
01178 //       Access: Public
01179 //  Description: See Filename::atomic_read_contents().
01180 ////////////////////////////////////////////////////////////////////
01181 bool VirtualFileSystem::
01182 atomic_read_contents(const Filename &filename, string &contents) const {
01183   PT(VirtualFile) file = get_file(filename, false);
01184   if (file == NULL) {
01185     return false;
01186   }
01187 
01188   return file->atomic_read_contents(contents);
01189 }
01190 
01191 ////////////////////////////////////////////////////////////////////
01192 //     Function: VirtualFileSystem::scan_mount_points
01193 //       Access: Public
01194 //  Description: Adds to names a list of all the mount points in use
01195 //               that are one directory below path, if any.  That is,
01196 //               these are the external files or directories mounted
01197 //               directly to the indicated path.
01198 //
01199 //               The names vector is filled with a set of basenames,
01200 //               the basename part of the mount point.
01201 ////////////////////////////////////////////////////////////////////
01202 void VirtualFileSystem::
01203 scan_mount_points(vector_string &names, const Filename &path) const {
01204   nassertv(!path.empty() && !path.is_local());
01205   string prefix = path.get_fullpath().substr(1);
01206   Mounts::const_iterator mi;
01207   for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
01208     VirtualFileMount *mount = (*mi);
01209     
01210     string mount_point = mount->get_mount_point();
01211     if (prefix.empty()) {
01212       // The indicated path is the root.  Is the mount point on the
01213       // root?
01214       if (mount_point.find('/') == string::npos) {
01215         // No embedded slashes, so the mount point is only one
01216         // directory below the root.
01217         names.push_back(mount_point);
01218       }
01219     } else {
01220       if (mount_point.substr(0, prefix.length()) == prefix &&
01221           mount_point.length() > prefix.length() &&
01222           mount_point[prefix.length()] == '/') {
01223         // This mount point is below the indicated path.  Is it only one
01224         // directory below?
01225         string basename = mount_point.substr(prefix.length());
01226         if (basename.find('/') == string::npos) {
01227           // No embedded slashes, so it's only one directory below.
01228           names.push_back(basename);
01229         }
01230       }
01231     }
01232   }
01233 }
01234 
01235         
01236 ////////////////////////////////////////////////////////////////////
01237 //     Function: VirtualFileSystem::parse_options
01238 //       Access: Public, Static
01239 //  Description: Parses all of the option flags in the options list on
01240 //               the vfs-mount Config.prc line.
01241 ////////////////////////////////////////////////////////////////////
01242 void VirtualFileSystem::
01243 parse_options(const string &options, int &flags, string &password) {
01244   flags = 0;
01245   password = string();
01246 
01247   // Split the options up by commas.
01248   size_t p = 0;
01249   size_t q = options.find(',', p);
01250   while (q != string::npos) {
01251     parse_option(options.substr(p, q - p),
01252                  flags, password);
01253     p = q + 1;
01254     q = options.find(',', p);
01255   }
01256   parse_option(options.substr(p), flags, password);
01257 }      
01258 
01259 ////////////////////////////////////////////////////////////////////
01260 //     Function: VirtualFileSystem::parse_option
01261 //       Access: Public, Static
01262 //  Description: Parses one of the option flags in the options list on
01263 //               the vfs-mount Config.prc line.
01264 ////////////////////////////////////////////////////////////////////
01265 void VirtualFileSystem::
01266 parse_option(const string &option, int &flags, string &password) {
01267   if (option == "0" || option.empty()) {
01268     // 0 is the null option.
01269   } else if (option == "ro") {
01270     flags |= MF_read_only;
01271   } else if (option.substr(0, 3) == "pw:") {
01272     password = option.substr(3);
01273   } else {
01274     express_cat.warning()
01275       << "Invalid option on vfs-mount: \"" << option << "\"\n";
01276   }
01277 }
01278 
01279 ////////////////////////////////////////////////////////////////////
01280 //     Function: VirtualFileSystem::normalize_mount_point
01281 //       Access: Private
01282 //  Description: Converts the mount point string supplied by the user
01283 //               to standard form (relative to the current directory,
01284 //               with no double slashes, and not terminating with a
01285 //               slash).  The initial slash is removed.
01286 //
01287 //               Assumes the lock is already held.
01288 ////////////////////////////////////////////////////////////////////
01289 Filename VirtualFileSystem::
01290 normalize_mount_point(const Filename &mount_point) const {
01291   Filename nmp = mount_point;
01292   if (nmp.is_local()) {
01293     nmp = Filename(_cwd, mount_point);
01294   }
01295   nmp.standardize();
01296   nassertr(!nmp.empty() && nmp[0] == '/', nmp);
01297   return nmp.get_fullpath().substr(1);
01298 }
01299 
01300 ////////////////////////////////////////////////////////////////////
01301 //     Function: VirtualFileSystem::do_mount
01302 //       Access: Private
01303 //  Description: The private implementation of mount().  Assumes the
01304 //               lock is already held.
01305 ////////////////////////////////////////////////////////////////////
01306 bool VirtualFileSystem::
01307 do_mount(VirtualFileMount *mount, const Filename &mount_point, int flags) {
01308   nassertr(mount->_file_system == NULL, false);
01309   mount->_file_system = this;
01310   mount->_mount_point = normalize_mount_point(mount_point);
01311   mount->_mount_flags = flags;
01312   _mounts.push_back(mount);
01313   ++_mount_seq;
01314   return true;
01315 }
01316 
01317 ////////////////////////////////////////////////////////////////////
01318 //     Function: VirtualFileSystem::do_get_file
01319 //       Access: Private
01320 //  Description: The private implementation of get_file(),
01321 //               create_file(), and make_directory().  Assumes the
01322 //               lock is already held.
01323 ////////////////////////////////////////////////////////////////////
01324 PT(VirtualFile) VirtualFileSystem::
01325 do_get_file(const Filename &filename, int open_flags) const {
01326   if (filename.empty()) {
01327     return NULL;
01328   }
01329   Filename pathname(filename);
01330   if (pathname.is_local()) {
01331     pathname = Filename(_cwd, filename);
01332     if (filename.is_text()) {
01333       pathname.set_text();
01334     }
01335   }
01336   pathname.standardize();
01337   Filename strpath = pathname.get_filename_index(0).get_fullpath().substr(1);
01338   strpath.set_type(filename.get_type());
01339   // Also transparently look for a regular file suffixed .pz.
01340   Filename strpath_pz = strpath + ".pz";
01341 
01342   // Now scan all the mount points, from the back (since later mounts
01343   // override more recent ones), until a match is found.
01344   PT(VirtualFile) found_file = NULL;
01345   VirtualFileComposite *composite_file = NULL;
01346 
01347   // We use an index instead of an iterator, since the vector might
01348   // change if implicit mounts are added during this loop.
01349   unsigned int start_seq = _mount_seq;
01350 
01351   size_t i = _mounts.size();
01352   while (i > 0) {
01353     --i;
01354     VirtualFileMount *mount = _mounts[i];
01355     Filename mount_point = mount->get_mount_point();
01356     if (strpath == mount_point) {
01357       // Here's an exact match on the mount point.  This filename is
01358       // the root directory of this mount object.
01359       if (consider_match(found_file, composite_file, mount, "", pathname,
01360                          false, open_flags)) {
01361         return found_file;
01362       }
01363     } else if (mount_point.empty()) {
01364       // This is the root mount point; all files are in here.
01365       if (consider_match(found_file, composite_file, mount, strpath, 
01366                          pathname, false, open_flags)) {
01367         return found_file;
01368       }
01369 #ifdef HAVE_ZLIB
01370       if (vfs_implicit_pz) {
01371         if (consider_match(found_file, composite_file, mount, strpath_pz, 
01372                            pathname, true, open_flags)) {
01373           return found_file;
01374         }
01375       }
01376 #endif  // HAVE_ZLIB
01377 
01378     } else if (strpath.length() > mount_point.length() &&
01379                mount_point == strpath.substr(0, mount_point.length()) &&
01380                strpath[mount_point.length()] == '/') {
01381       // This pathname falls within this mount system.
01382       Filename local_filename = strpath.substr(mount_point.length() + 1);
01383       Filename local_filename_pz = strpath_pz.substr(mount_point.length() + 1);
01384       if (consider_match(found_file, composite_file, mount, local_filename, 
01385                          pathname, false, open_flags)) {
01386         return found_file;
01387       }
01388 #ifdef HAVE_ZLIB
01389       if (vfs_implicit_pz) {
01390         // Bingo!
01391         if (consider_match(found_file, composite_file, mount, local_filename_pz,
01392                            pathname, true, open_flags)) {
01393           return found_file;
01394         }
01395       }
01396 #endif  // HAVE_ZLIB
01397     }
01398 
01399     // If we discover that a file has been implicitly mounted during
01400     // one of the above operations, start over from the beginning of
01401     // the loop.
01402     if (start_seq != _mount_seq) {
01403       start_seq = _mount_seq;
01404       i = _mounts.size();
01405     }
01406   }
01407 
01408   if (found_file == (VirtualFile *)NULL && vfs_implicit_mf) {
01409     // The file wasn't found, as-is.  Does it appear to be an implicit
01410     // .mf file reference?
01411     ((VirtualFileSystem *)this)->consider_mount_mf(filename);
01412 
01413     if (start_seq != _mount_seq) {
01414       // Yes, it was, or some nested file was.  Now that we've
01415       // implicitly mounted the .mf file, go back and look again.
01416       return do_get_file(filename, open_flags);
01417     }
01418   }
01419 
01420   return found_file;
01421 }
01422 
01423 ////////////////////////////////////////////////////////////////////
01424 //     Function: VirtualFileSystem::consider_match
01425 //       Access: Private
01426 //  Description: Evaluates one possible filename match found during a
01427 //               get_file() operation.  There may be multiple matches
01428 //               for a particular filename due to the ambiguities
01429 //               introduced by allowing multiple mount points, so we
01430 //               may have to keep searching even after the first match
01431 //               is found.
01432 //
01433 //               Returns true if the search should terminate now, or
01434 //               false if it should keep iterating.
01435 ////////////////////////////////////////////////////////////////////
01436 bool VirtualFileSystem::
01437 consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
01438                VirtualFileMount *mount, const Filename &local_filename,
01439                const Filename &original_filename, bool implicit_pz_file,
01440                int open_flags) const {
01441   PT(VirtualFile) vfile = 
01442     mount->make_virtual_file(local_filename, original_filename, false, open_flags);
01443   if (!vfile->has_file() && ((open_flags & OF_allow_nonexist) == 0)) {
01444     // Keep looking.
01445     return false;
01446   }
01447 
01448   if (found_file == (VirtualFile *)NULL) {
01449     // This was our first match.  Save it.
01450     found_file = vfile;
01451     if (!found_file->is_directory() || ((open_flags & OF_make_directory) != 0)) {
01452       // If it's not a directory (or we wanted to make a directory), we're done.
01453       return true;
01454     }
01455     // It is a directory, so save it for later.
01456     if (implicit_pz_file) {
01457       // Don't look for directories named file.pz.
01458       found_file = NULL;
01459     }
01460 
01461   } else {
01462     // This was our second match.  The previous match(es) must
01463     // have been directories.
01464     if (!vfile->is_directory()) {
01465       // However, this one isn't a directory.  We're done.
01466       return true;
01467     }
01468 
01469     if (!implicit_pz_file) {
01470       // At least two directories matched to the same path.  We
01471       // need a composite directory.
01472       if (composite_file == (VirtualFileComposite *)NULL) {
01473         composite_file =
01474           new VirtualFileComposite((VirtualFileSystem *)this, found_file->get_original_filename());
01475         composite_file->set_original_filename(original_filename);
01476         composite_file->add_component(found_file);
01477         found_file = composite_file;
01478       }
01479 
01480       composite_file->add_component(vfile);
01481     }
01482   }
01483 
01484   // Keep going, looking for more directories.
01485   return false;
01486 }
01487 
01488 ////////////////////////////////////////////////////////////////////
01489 //     Function: VirtualFileSystem::consider_mount_mf
01490 //       Access: Private
01491 //  Description: The indicated filename was not found.  Check to see
01492 //               if it is using an implicit reference to a .mf file as
01493 //               a directory, that hasn't already been mounted.  If it
01494 //               is, mount the .mf file in-place, and return true; if
01495 //               it is not, or if its .mf file is already mounted
01496 //               in-place, return false.
01497 //
01498 //               Assumes the lock is already held.
01499 ////////////////////////////////////////////////////////////////////
01500 bool VirtualFileSystem::
01501 consider_mount_mf(const Filename &filename) {
01502   Filename dirname = filename.get_dirname();
01503   if (dirname.empty() || dirname == filename) {
01504     // Reached the top directory; no .mf file references.
01505     return false;
01506   }
01507   if (is_directory(dirname)) {
01508     // Reached a real (or already-mounted) directory; no unmounted .mf
01509     // file references.
01510     return false;
01511   }
01512   if (dirname.get_extension() == "mf") {
01513     // Hey, here's a multifile reference!
01514     dirname.set_binary();
01515     PT(VirtualFile) file = do_get_file(dirname, false);
01516     if (file == (VirtualFile *)NULL || !file->is_regular_file()) {
01517       // Oh, never mind.  Not a real file.
01518       return false;
01519     }
01520 
01521     PT(Multifile) multifile = new Multifile;
01522    
01523     istream *stream = file->open_read_file(false);
01524     if (stream == (istream *)NULL) {
01525       // Couldn't read file.
01526       return false;
01527     }
01528 
01529     // Wrap a thread-safe wrapper around that stream, so multiple
01530     // threads can safely read the multifile simultaneously.
01531     IStreamWrapper *streamw = new IStreamWrapper(stream, true);
01532 
01533     if (!multifile->open_read(streamw, true)) {
01534       // Invalid multifile.
01535       return false;
01536     }
01537 
01538     multifile->set_multifile_name(dirname.get_basename());
01539     express_cat->info()
01540       << "Implicitly mounting " << dirname << "\n";
01541 
01542     PT(VirtualFileMountMultifile) new_mount = 
01543       new VirtualFileMountMultifile(multifile);
01544     return do_mount(new_mount, dirname, MF_read_only);
01545   }
01546 
01547   // Recurse.
01548   return consider_mount_mf(dirname);
01549 }
 All Classes Functions Variables Enumerations