Panda3D

filename.cxx

00001 // Filename: filename.cxx
00002 // Created by:  drose (18Jan99)
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 "filename.h"
00016 #include "filename_assist.h"
00017 #include "dSearchPath.h"
00018 #include "executionEnvironment.h"
00019 #include "vector_string.h"
00020 #include "atomicAdjust.h"
00021 
00022 #include <stdio.h>  // For rename() and tempnam()
00023 #include <time.h>   // for clock() and time()
00024 #include <sys/stat.h>
00025 #include <algorithm>
00026 
00027 #ifdef PHAVE_UTIME_H
00028 #include <utime.h>
00029 
00030 // We assume we have these too.
00031 #include <errno.h>
00032 #include <fcntl.h>
00033 #endif
00034 
00035 #ifdef PHAVE_GLOB_H
00036   #include <glob.h>
00037   #ifndef GLOB_NOMATCH
00038     #define GLOB_NOMATCH -3
00039   #endif
00040 #endif
00041 
00042 #ifdef PHAVE_DIRENT_H
00043 #include <dirent.h>
00044 #endif
00045 
00046 // It's true that dtoolbase.h includes this already, but we include
00047 // this again in case we are building this file within ppremake.
00048 #ifdef PHAVE_UNISTD_H
00049 #include <unistd.h>
00050 #endif
00051 
00052 Filename *Filename::_home_directory;
00053 Filename *Filename::_temp_directory;
00054 Filename *Filename::_user_appdata_directory;
00055 Filename *Filename::_common_appdata_directory;
00056 TypeHandle Filename::_type_handle;
00057 
00058 #ifdef WIN32
00059 /* begin Win32-specific code */
00060 
00061 #ifdef WIN32_VC
00062 #include <direct.h>
00063 #include <windows.h>
00064 #include <shlobj.h>
00065 #include <io.h>
00066 #endif
00067 
00068 // The MSVC 6.0 Win32 SDK lacks the following definitions, so we define them
00069 // here for compatibility.
00070 #ifndef FILE_ATTRIBUTE_DEVICE
00071 #define FILE_ATTRIBUTE_DEVICE 0x00000040
00072 #endif
00073 
00074 // We might have been linked with the Cygwin dll.  This is ideal if it
00075 // is available, because it allows Panda to access all the Cygwin
00076 // mount definitions if they are in use.  If the Cygwin dll is not
00077 // available, we fall back to our own convention for converting
00078 // pathnames.
00079 #ifdef HAVE_CYGWIN
00080 extern "C" void cygwin_conv_to_win32_path(const char *path, char *win32);
00081 extern "C" void cygwin_conv_to_posix_path(const char *path, char *posix);
00082 #endif
00083 
00084 // Windows uses the convention \\hostname\path\to\file to represent a
00085 // pathname to a file on another share.  This redefines a pathname to
00086 // be something more complicated than a sequence of directory names
00087 // separated by slashes.  The Unix convention to represent the same
00088 // thing is, like everything else, to graft the reference to the
00089 // remote hostname into the one global filesystem, with something like
00090 // /hosts/hostname/path/to/file.  We observe the Unix convention for
00091 // internal names used in Panda; this makes operations like
00092 // Filename::get_dirname() simpler and more internally consistent.
00093 
00094 // This string hard-defines the prefix that we use internally to
00095 // indicate that the next directory component name should be treated
00096 // as a hostname.  It might be nice to use a ConfigVariable for this,
00097 // except that we haven't defined ConfigVariable by this point (and
00098 // indeed we can't, since we need to have a Filename class already
00099 // created in order to read the first config file).  Windows purists
00100 // might be tempted to define this to a double slash so that internal
00101 // Panda filenames more closely resemble their Windows counterparts.
00102 // That might actually work, but it will cause problems with
00103 // Filename::standardize().
00104 
00105 // We use const char * instead of string to avoid static-init ordering
00106 // issues.
00107 static const char *hosts_prefix = "/hosts/";
00108 static size_t hosts_prefix_length = 7;
00109 
00110 static string
00111 front_to_back_slash(const string &str) {
00112   string result = str;
00113   string::iterator si;
00114   for (si = result.begin(); si != result.end(); ++si) {
00115     if ((*si) == '/') {
00116       (*si) = '\\';
00117     }
00118   }
00119 
00120   return result;
00121 }
00122 
00123 static string
00124 back_to_front_slash(const string &str) {
00125   string result = str;
00126   string::iterator si;
00127   for (si = result.begin(); si != result.end(); ++si) {
00128     if ((*si) == '\\') {
00129       (*si) = '/';
00130     }
00131   }
00132 
00133   return result;
00134 }
00135 
00136 static const string &
00137 get_panda_root() {
00138   static string *panda_root = NULL;
00139 
00140   if (panda_root == NULL) {
00141     panda_root = new string;
00142     const char *envvar = getenv("PANDA_ROOT");
00143     if (envvar != (const char *)NULL) {
00144       (*panda_root) = front_to_back_slash(envvar);
00145     }
00146 
00147     // Ensure the string ends in a backslash.  If PANDA_ROOT is empty
00148     // or undefined, this function must return a single backslash--not
00149     // an empty string--since this prefix is used to replace a leading
00150     // slash in Filename::to_os_specific().
00151     if ((*panda_root).empty() || (*panda_root)[(*panda_root).length() - 1] != '\\') {
00152       (*panda_root) += '\\';
00153     }
00154   }
00155 
00156   return (*panda_root);
00157 }
00158 
00159 static string
00160 convert_pathname(const string &unix_style_pathname) {
00161   if (unix_style_pathname.empty()) {
00162     return string();
00163   }
00164 
00165   // To convert from a Unix-style pathname to a Windows-style
00166   // pathname, we need to change all forward slashes to backslashes.
00167   // We might need to add a prefix as well, since Windows pathnames
00168   // typically begin with a drive letter.
00169 
00170   // By convention, if the top directory name consists of just one
00171   // letter, we treat that as a drive letter and map the rest of the
00172   // filename accordingly.  On the other hand, if the top directory
00173   // name consists of more than one letter, we assume this is a file
00174   // within some predefined tree whose root is given by the
00175   // environment variable "PANDA_ROOT", or if that is not defined,
00176   // "CYGWIN_ROOT" (for backward compatibility).
00177   string windows_pathname;
00178 
00179   if (unix_style_pathname[0] != '/') {
00180     // It doesn't even start from the root, so we don't have to do
00181     // anything fancy--relative pathnames are the same in Windows as
00182     // in Unix, except for the direction of the slashes.
00183     windows_pathname = front_to_back_slash(unix_style_pathname);
00184 
00185   } else if (unix_style_pathname.length() >= 2 &&
00186              isalpha(unix_style_pathname[1]) &&
00187              (unix_style_pathname.length() == 2 || unix_style_pathname[2] == '/')) {
00188     // This pathname begins with a slash and a single letter.  That
00189     // must be the drive letter.
00190 
00191     string remainder = unix_style_pathname.substr(2);
00192     if (remainder.empty()) {
00193       // There's a difference between "C:" and "C:/".
00194       remainder = "/";
00195     }
00196     remainder = front_to_back_slash(remainder);
00197 
00198     // We have to cast the result of toupper() to (char) to help some
00199     // compilers (e.g. Cygwin's gcc 2.95.3) happy; so that they do not
00200     // confuse this string constructor with one that takes two
00201     // iterators.
00202     windows_pathname =
00203       string(1, (char)toupper(unix_style_pathname[1])) + ":" + remainder;
00204 
00205   } else if (unix_style_pathname.length() > hosts_prefix_length &&
00206              unix_style_pathname.substr(0, hosts_prefix_length) == hosts_prefix) {
00207     // A filename like /hosts/fooby gets turned into \\fooby.
00208     windows_pathname = "\\\\" + front_to_back_slash(unix_style_pathname.substr(hosts_prefix_length));
00209     
00210   } else {
00211     // It starts with a slash, but the first part is not a single
00212     // letter.
00213 
00214 #ifdef HAVE_CYGWIN
00215     // Use Cygwin to convert it if possible.
00216     char result[4096] = "";
00217     cygwin_conv_to_win32_path(unix_style_pathname.c_str(), result);
00218     windows_pathname = result;
00219 
00220 #else  // HAVE_CYGWIN
00221     // Without Cygwin, just prefix $PANDA_ROOT.
00222     windows_pathname = get_panda_root();
00223     windows_pathname += front_to_back_slash(unix_style_pathname.substr(1));
00224 
00225 #endif  // HAVE_CYGWIN
00226   }
00227 
00228   return windows_pathname;
00229 }
00230 
00231 static string
00232 convert_dso_pathname(const string &unix_style_pathname) {
00233   // If the extension is .so, change it to .dll.
00234   size_t dot = unix_style_pathname.rfind('.');
00235   if (dot == string::npos ||
00236       unix_style_pathname.find('/', dot) != string::npos) {
00237     // No filename extension.
00238     return convert_pathname(unix_style_pathname);
00239   }
00240   if (unix_style_pathname.substr(dot) != ".so") {
00241     // Some other extension.
00242     return convert_pathname(unix_style_pathname);
00243   }
00244 
00245   string dll_basename = unix_style_pathname.substr(0, dot);
00246 
00247 #ifdef _DEBUG
00248   // If we're building a debug version, all the dso files we link in
00249   // must be named file_d.dll.  This does prohibit us from linking in
00250   // external dso files, generated outside of the Panda build system,
00251   // that don't follow this _d convention.  Maybe we need a separate
00252   // convert_system_dso_pathname() function.
00253 
00254   // We can't simply check to see if the file exists, because this
00255   // might not be a full path to the dso filename--it might be
00256   // somewhere on the LD_LIBRARY_PATH, or on PATH, or any of a number
00257   // of nutty places.
00258 
00259   return convert_pathname(dll_basename + "_d.dll");
00260 #else
00261   return convert_pathname(dll_basename + ".dll");
00262 #endif
00263 }
00264 
00265 static string
00266 convert_executable_pathname(const string &unix_style_pathname) {
00267   // If the extension is not .exe, append .exe.
00268   size_t dot = unix_style_pathname.rfind('.');
00269   if (dot == string::npos ||
00270       unix_style_pathname.find('/', dot) != string::npos) {
00271     // No filename extension.
00272     return convert_pathname(unix_style_pathname + ".exe");
00273   }
00274   if (unix_style_pathname.substr(dot) != ".exe") {
00275     // Some other extension.
00276     return convert_pathname(unix_style_pathname + ".exe");
00277   }
00278 
00279   return convert_pathname(unix_style_pathname);
00280 }
00281 #endif //WIN32
00282 
00283 ////////////////////////////////////////////////////////////////////
00284 //     Function: Filename::Constructor
00285 //       Access: Published
00286 //  Description: This constructor composes the filename out of a
00287 //               directory part and a basename part.  It will insert
00288 //               an intervening '/' if necessary.
00289 ////////////////////////////////////////////////////////////////////
00290 Filename::
00291 Filename(const Filename &dirname, const Filename &basename) {
00292   if (dirname.empty()) {
00293     (*this) = basename;
00294   } else {
00295     _flags = basename._flags;
00296     string dirpath = dirname.get_fullpath();
00297     if (dirpath[dirpath.length() - 1] == '/') {
00298       (*this) = dirpath + basename.get_fullpath();
00299     } else {
00300       (*this) = dirpath + "/" + basename.get_fullpath();
00301     }
00302   }
00303 }
00304 
00305 #ifdef HAVE_PYTHON
00306 ////////////////////////////////////////////////////////////////////
00307 //     Function: Filename::__reduce__
00308 //       Access: Published
00309 //  Description: This special Python method is implement to provide
00310 //               support for the pickle module.
00311 ////////////////////////////////////////////////////////////////////
00312 PyObject *Filename::
00313 __reduce__(PyObject *self) const {
00314   // We should return at least a 2-tuple, (Class, (args)): the
00315   // necessary class object whose constructor we should call
00316   // (e.g. this), and the arguments necessary to reconstruct this
00317   // object.
00318   PyObject *this_class = PyObject_Type(self);
00319   if (this_class == NULL) {
00320     return NULL;
00321   }
00322 
00323   PyObject *result = Py_BuildValue("(O(s))", this_class, c_str());
00324   Py_DECREF(this_class);
00325   return result;
00326 }
00327 #endif  // HAVE_PYTHON
00328 
00329 ////////////////////////////////////////////////////////////////////
00330 //     Function: Filename::from_os_specific
00331 //       Access: Published, Static
00332 //  Description: This named constructor returns a Panda-style filename
00333 //               (that is, using forward slashes, and no drive letter)
00334 //               based on the supplied filename string that describes
00335 //               a filename in the local system conventions (for
00336 //               instance, on Windows, it may use backslashes or begin
00337 //               with a drive letter and a colon).
00338 //
00339 //               Use this function to create a Filename from an
00340 //               externally-given filename string.  Use
00341 //               to_os_specific() again later to reconvert it back to
00342 //               the local operating system's conventions.
00343 //
00344 //               This function will do the right thing even if the
00345 //               filename is partially local conventions and partially
00346 //               Panda conventions; e.g. some backslashes and some
00347 //               forward slashes.
00348 ////////////////////////////////////////////////////////////////////
00349 Filename Filename::
00350 from_os_specific(const string &os_specific, Filename::Type type) {
00351 #ifdef WIN32
00352   string result = back_to_front_slash(os_specific);
00353   const string &panda_root = get_panda_root();
00354 
00355   // If the initial prefix is the same as panda_root, remove it.
00356   if (!panda_root.empty() && panda_root != string("\\") && 
00357       panda_root.length() < result.length()) {
00358     bool matches = true;
00359     size_t p;
00360     for (p = 0; p < panda_root.length() && matches; ++p) {
00361       char c = tolower(panda_root[p]);
00362       if (c == '\\') {
00363         c = '/';
00364       }
00365       matches = (c == tolower(result[p]));
00366     }
00367 
00368     if (matches) {
00369       // The initial prefix matches!  Replace the initial bit with a
00370       // leading slash.
00371       result = result.substr(panda_root.length());
00372       assert(!result.empty());
00373       if (result[0] != '/') {
00374         result = '/' + result;
00375       }
00376       Filename filename(result);
00377       filename.set_type(type);
00378       return filename;
00379     }
00380   }
00381 
00382   // All right, the initial prefix was not under panda_root.  But
00383   // maybe it begins with a drive letter.
00384   if (result.size() >= 3 && isalpha(result[0]) &&
00385       result[1] == ':' && result[2] == '/') {
00386     result[1] = tolower(result[0]);
00387     result[0] = '/';
00388 
00389     // If there's *just* a slash following the drive letter, go ahead
00390     // and trim it.
00391     if (result.size() == 3) {
00392       result = result.substr(0, 2);
00393     }
00394 
00395   } else if (result.substr(0, 2) == "//") {
00396     // If the initial prefix is a double slash, convert it to /hosts/.
00397     result = hosts_prefix + result.substr(2);
00398   }
00399 
00400   Filename filename(result);
00401   filename.set_type(type);
00402   return filename;
00403 #else  // WIN32
00404   // Generic Unix-style filenames--no conversion necessary.
00405   Filename filename(os_specific);
00406   filename.set_type(type);
00407   return filename;
00408 #endif  // WIN32
00409 }
00410 
00411 ////////////////////////////////////////////////////////////////////
00412 //     Function: Filename::expand_from
00413 //       Access: Published, Static
00414 //  Description: Returns the same thing as from_os_specific(), but
00415 //               embedded environment variable references
00416 //               (e.g. "$DMODELS/foo.txt") are expanded out.  It also
00417 //               automatically elevates the file to its true case if
00418 //               needed.
00419 ////////////////////////////////////////////////////////////////////
00420 Filename Filename::
00421 expand_from(const string &os_specific, Filename::Type type) {
00422   Filename file = from_os_specific(ExecutionEnvironment::expand_string(os_specific),
00423                                    type);
00424   file.make_true_case();
00425   return file;
00426 }
00427 
00428 ////////////////////////////////////////////////////////////////////
00429 //     Function: Filename::temporary
00430 //       Access: Published, Static
00431 //  Description: Generates a temporary filename within the indicated
00432 //               directory, using the indicated prefix.  If the
00433 //               directory is empty, a system-defined directory is
00434 //               chosen instead.
00435 //
00436 //               The generated filename did not exist when the
00437 //               Filename checked, but since it does not specifically
00438 //               create the file, it is possible that another process
00439 //               could simultaneously create a file by the same name.
00440 ////////////////////////////////////////////////////////////////////
00441 Filename Filename::
00442 temporary(const string &dirname, const string &prefix, const string &suffix,
00443           Type type) {
00444   Filename fdirname = dirname;
00445 #ifdef WIN32
00446   // The Windows tempnam() function doesn't do a good job of choosing
00447   // a temporary directory.  Choose one ourselves.
00448   if (fdirname.empty()) {
00449     fdirname = Filename::get_temp_directory();
00450   }
00451 #endif
00452 
00453   if (fdirname.empty()) {
00454     // If we are not given a dirname, use the system tempnam()
00455     // function to create a system-defined temporary filename.
00456     char *name = tempnam(NULL, prefix.c_str());
00457     Filename result = Filename::from_os_specific(name);
00458     free(name);
00459     result.set_type(type);
00460     return result;
00461   }
00462 
00463   // If we *are* given a dirname, then use our own algorithm to make
00464   // up a filename within that dirname.  We do that because the system
00465   // tempnam() (for instance, under Windows) may ignore the dirname.
00466 
00467   Filename result;
00468   do {
00469     // We take the time of day and multiply it by the process time.
00470     // This will give us a very large number, of which we take the
00471     // bottom 24 bits and generate a 6-character hex code.
00472     int hash = (clock() * time(NULL)) & 0xffffff;
00473     char hex_code[10];
00474     sprintf(hex_code, "%06x", hash);
00475     result = Filename(fdirname, Filename(prefix + hex_code + suffix));
00476     result.set_type(type);
00477   } while (result.exists());
00478 
00479   return result;
00480 }
00481 
00482 ////////////////////////////////////////////////////////////////////
00483 //     Function: Filename::get_home_directory
00484 //       Access: Published
00485 //  Description: Returns a path to the user's home directory, if such
00486 //               a thing makes sense in the current OS, or to the
00487 //               nearest equivalent.  This may or may not be directly
00488 //               writable by the application.
00489 ////////////////////////////////////////////////////////////////////
00490 const Filename &Filename::
00491 get_home_directory() {
00492   if (AtomicAdjust::get_ptr((void * TVOLATILE &)_home_directory) == NULL) {
00493     Filename home_directory;
00494 
00495     // In all environments, check $HOME first.
00496     char *home = getenv("HOME");
00497     if (home != (char *)NULL) {
00498       Filename dirname = from_os_specific(home);
00499       if (dirname.is_directory()) {
00500         if (dirname.make_canonical()) {
00501           home_directory = dirname;
00502         }
00503       }
00504     }
00505 
00506     if (home_directory.empty()) {
00507 #ifdef WIN32
00508       char buffer[MAX_PATH];
00509       
00510       // On Windows, fall back to the "My Documents" folder.
00511       if (SHGetSpecialFolderPath(NULL, buffer, CSIDL_PERSONAL, true)) {
00512         Filename dirname = from_os_specific(buffer);
00513         if (dirname.is_directory()) {
00514           if (dirname.make_canonical()) {
00515             home_directory = dirname;
00516           }
00517         }
00518       }
00519 
00520 #elif defined(IS_OSX)
00521       home_directory = get_osx_home_directory();
00522       
00523 #else
00524       // Posix case: check /etc/passwd?
00525       
00526 #endif  // WIN32
00527     }
00528       
00529     if (home_directory.empty()) {
00530       // Fallback case.
00531       home_directory = ExecutionEnvironment::get_cwd();
00532     }
00533 
00534     Filename *newdir = new Filename(home_directory);
00535     if (AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)_home_directory, NULL, newdir) != NULL) {
00536       // Didn't store it.  Must have been stored by someone else.
00537       assert(_home_directory != NULL);
00538       delete newdir;
00539     }
00540   }
00541   
00542   return (*_home_directory);
00543 }
00544 
00545 ////////////////////////////////////////////////////////////////////
00546 //     Function: Filename::get_temp_directory
00547 //       Access: Published
00548 //  Description: Returns a path to a system-defined temporary
00549 //               directory.
00550 ////////////////////////////////////////////////////////////////////
00551 const Filename &Filename::
00552 get_temp_directory() {
00553   if (AtomicAdjust::get_ptr((void * TVOLATILE &)_temp_directory) == NULL) {
00554     Filename temp_directory;
00555 
00556 #ifdef WIN32
00557     static const size_t buffer_size = 4096;
00558     char buffer[buffer_size];
00559     if (GetTempPath(buffer_size, buffer) != 0) {
00560       Filename dirname = from_os_specific(buffer);
00561       if (dirname.is_directory()) {
00562         if (dirname.make_canonical()) {
00563           temp_directory = dirname;
00564         }
00565       }
00566     }
00567 
00568 #elif defined(IS_OSX)
00569     temp_directory = get_osx_temp_directory();
00570 
00571 #else
00572     // Posix case.
00573     temp_directory = "/tmp";
00574 #endif  // WIN32
00575 
00576     if (temp_directory.empty()) {
00577       // Fallback case.
00578       temp_directory = ExecutionEnvironment::get_cwd();
00579     }
00580 
00581     Filename *newdir = new Filename(temp_directory);
00582     if (AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)_temp_directory, NULL, newdir) != NULL) {
00583       // Didn't store it.  Must have been stored by someone else.
00584       assert(_temp_directory != NULL);
00585       delete newdir;
00586     }
00587   }
00588 
00589   return (*_temp_directory);
00590 }
00591 
00592 ////////////////////////////////////////////////////////////////////
00593 //     Function: Filename::get_user_appdata_directory
00594 //       Access: Published
00595 //  Description: Returns a path to a system-defined directory
00596 //               appropriate for creating a subdirectory for storing
00597 //               application-specific data, specific to the current
00598 //               user.
00599 ////////////////////////////////////////////////////////////////////
00600 const Filename &Filename::
00601 get_user_appdata_directory() {
00602   if (AtomicAdjust::get_ptr((void * TVOLATILE &)_user_appdata_directory) == NULL) {
00603     Filename user_appdata_directory;
00604 
00605 #ifdef WIN32
00606     char buffer[MAX_PATH];
00607 
00608     if (SHGetSpecialFolderPath(NULL, buffer, CSIDL_LOCAL_APPDATA, true)) {
00609       Filename dirname = from_os_specific(buffer);
00610       if (dirname.is_directory()) {
00611         if (dirname.make_canonical()) {
00612           user_appdata_directory = dirname;
00613         }
00614       }
00615     }
00616 
00617 #elif defined(IS_OSX)
00618     user_appdata_directory = get_osx_user_appdata_directory();
00619 
00620 #else
00621     // Posix case.
00622     user_appdata_directory = get_home_directory();
00623 
00624 #endif  // WIN32
00625 
00626     if (user_appdata_directory.empty()) {
00627       // Fallback case.
00628       user_appdata_directory = ExecutionEnvironment::get_cwd();
00629     }
00630 
00631     Filename *newdir = new Filename(user_appdata_directory);
00632     if (AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)_user_appdata_directory, NULL, newdir) != NULL) {
00633       // Didn't store it.  Must have been stored by someone else.
00634       assert(_user_appdata_directory != NULL);
00635       delete newdir;
00636     }
00637   }
00638 
00639   return (*_user_appdata_directory);
00640 }
00641 
00642 ////////////////////////////////////////////////////////////////////
00643 //     Function: Filename::get_common_appdata_directory
00644 //       Access: Published
00645 //  Description: Returns a path to a system-defined directory
00646 //               appropriate for creating a subdirectory for storing
00647 //               application-specific data, common to all users.
00648 ////////////////////////////////////////////////////////////////////
00649 const Filename &Filename::
00650 get_common_appdata_directory() {
00651   if (AtomicAdjust::get_ptr((void * TVOLATILE &)_common_appdata_directory) == NULL) {
00652     Filename common_appdata_directory;
00653 
00654 #ifdef WIN32
00655     char buffer[MAX_PATH];
00656 
00657     if (SHGetSpecialFolderPath(NULL, buffer, CSIDL_COMMON_APPDATA, true)) {
00658       Filename dirname = from_os_specific(buffer);
00659       if (dirname.is_directory()) {
00660         if (dirname.make_canonical()) {
00661           common_appdata_directory = dirname;
00662         }
00663       }
00664     }
00665 
00666 #elif defined(IS_OSX)
00667     common_appdata_directory = get_osx_common_appdata_directory();
00668 
00669 #else
00670     // Posix case.
00671     common_appdata_directory = "/var";
00672 #endif  // WIN32
00673 
00674     if (common_appdata_directory.empty()) {
00675       // Fallback case.
00676       common_appdata_directory = ExecutionEnvironment::get_cwd();
00677     }
00678 
00679     Filename *newdir = new Filename(common_appdata_directory);
00680     if (AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)_common_appdata_directory, NULL, newdir) != NULL) {
00681       // Didn't store it.  Must have been stored by someone else.
00682       assert(_common_appdata_directory != NULL);
00683       delete newdir;
00684     }
00685   }
00686 
00687   return (*_common_appdata_directory);
00688 }
00689 
00690 ////////////////////////////////////////////////////////////////////
00691 //     Function: Filename::set_fullpath
00692 //       Access: Published
00693 //  Description: Replaces the entire filename: directory, basename,
00694 //               extension.  This can also be achieved with the
00695 //               assignment operator.
00696 ////////////////////////////////////////////////////////////////////
00697 void Filename::
00698 set_fullpath(const string &s) {
00699   (*this) = s;
00700 }
00701 
00702 ////////////////////////////////////////////////////////////////////
00703 //     Function: Filename::set_dirname
00704 //       Access: Published
00705 //  Description: Replaces the directory part of the filename.  This is
00706 //               everything in the filename up to, but not including
00707 //               the rightmost slash.
00708 ////////////////////////////////////////////////////////////////////
00709 void Filename::
00710 set_dirname(const string &s) {
00711   if (s.empty()) {
00712     // Remove the directory prefix altogether.
00713     _filename.replace(0, _basename_start, "");
00714 
00715     int length_change = - ((int)_basename_start);
00716 
00717     _dirname_end = 0;
00718     _basename_start += length_change;
00719     _basename_end += length_change;
00720     _extension_start += length_change;
00721 
00722   } else {
00723     // Replace the existing directory prefix, or insert a new one.
00724 
00725     // We build the string ss to include the terminal slash.
00726     string ss;
00727     if (s[s.length()-1] == '/') {
00728       ss = s;
00729     } else {
00730       ss = s+'/';
00731     }
00732 
00733     int length_change = ss.length() - _basename_start;
00734 
00735     _filename.replace(0, _basename_start, ss);
00736 
00737     _dirname_end = ss.length() - 1;
00738 
00739     // An exception: if the dirname string was the single slash, the
00740     // dirname includes that slash.
00741     if (ss.length() == 1) {
00742       _dirname_end = 1;
00743     }
00744 
00745     _basename_start += length_change;
00746 
00747     if (_basename_end != string::npos) {
00748       _basename_end += length_change;
00749       _extension_start += length_change;
00750     }
00751   }
00752   locate_hash();
00753 }
00754 
00755 ////////////////////////////////////////////////////////////////////
00756 //     Function: Filename::set_basename
00757 //       Access: Published
00758 //  Description: Replaces the basename part of the filename.  This is
00759 //               everything in the filename after the rightmost slash,
00760 //               including any extensions.
00761 ////////////////////////////////////////////////////////////////////
00762 void Filename::
00763 set_basename(const string &s) {
00764   _filename.replace(_basename_start, string::npos, s);
00765   locate_extension();
00766   locate_hash();
00767 }
00768 
00769 
00770 ////////////////////////////////////////////////////////////////////
00771 //     Function: Filename::set_fullpath_wo_extension
00772 //       Access: Published
00773 //  Description: Replaces the full filename--directory and basename
00774 //               parts--except for the extension.
00775 ////////////////////////////////////////////////////////////////////
00776 void Filename::
00777 set_fullpath_wo_extension(const string &s) {
00778   int length_change = s.length() - _basename_end;
00779 
00780   _filename.replace(0, _basename_end, s);
00781 
00782   if (_basename_end != string::npos) {
00783     _basename_end += length_change;
00784     _extension_start += length_change;
00785   }
00786   locate_hash();
00787 }
00788 
00789 
00790 ////////////////////////////////////////////////////////////////////
00791 //     Function: Filename::set_basename_wo_extension
00792 //       Access: Published
00793 //  Description: Replaces the basename part of the filename, without
00794 //               the file extension.
00795 ////////////////////////////////////////////////////////////////////
00796 void Filename::
00797 set_basename_wo_extension(const string &s) {
00798   int length_change = s.length() - (_basename_end - _basename_start);
00799 
00800   if (_basename_end == string::npos) {
00801     _filename.replace(_basename_start, string::npos, s);
00802 
00803   } else {
00804     _filename.replace(_basename_start, _basename_end - _basename_start, s);
00805 
00806     _basename_end += length_change;
00807     _extension_start += length_change;
00808   }
00809   locate_hash();
00810 }
00811 
00812 
00813 ////////////////////////////////////////////////////////////////////
00814 //     Function: Filename::set_extension
00815 //       Access: Published
00816 //  Description: Replaces the file extension.  This is everything after
00817 //               the rightmost dot, if there is one, or the empty
00818 //               string if there is not.
00819 ////////////////////////////////////////////////////////////////////
00820 void Filename::
00821 set_extension(const string &s) {
00822   if (s.empty()) {
00823     // Remove the extension altogether.
00824     if (_basename_end != string::npos) {
00825       _filename.replace(_basename_end, string::npos, "");
00826       _basename_end = string::npos;
00827       _extension_start = string::npos;
00828     }
00829 
00830   } else if (_basename_end == string::npos) {
00831     // Insert an extension where there was none before.
00832     _basename_end = _filename.length();
00833     _extension_start = _filename.length() + 1;
00834     _filename += '.' + s;
00835 
00836   } else {
00837     // Replace an existing extension.
00838     _filename.replace(_extension_start, string::npos, s);
00839   }
00840   locate_hash();
00841 }
00842 
00843 ////////////////////////////////////////////////////////////////////
00844 //     Function: Filename::get_filename_index
00845 //       Access: Published
00846 //  Description: If the pattern flag is set for this Filename and the
00847 //               filename string actually includes a sequence of hash
00848 //               marks, then this returns a new Filename with the
00849 //               sequence of hash marks replaced by the indicated
00850 //               index number.
00851 //
00852 //               If the pattern flag is not set for this Filename or
00853 //               it does not contain a sequence of hash marks, this
00854 //               quietly returns the original filename.
00855 ////////////////////////////////////////////////////////////////////
00856 Filename Filename::
00857 get_filename_index(int index) const {
00858   Filename file(*this);
00859 
00860   if (_hash_end != _hash_start) {
00861     ostringstream strm;
00862     strm << _filename.substr(0, _hash_start) 
00863          << setw(_hash_end - _hash_start) << setfill('0') << index
00864          << _filename.substr(_hash_end);
00865     file.set_fullpath(strm.str());
00866   }
00867   file.set_pattern(false);
00868 
00869   return file;
00870 }
00871 
00872 ////////////////////////////////////////////////////////////////////
00873 //     Function: Filename::set_hash_to_end
00874 //       Access: Published
00875 //  Description: Replaces the part of the filename from the beginning
00876 //               of the hash sequence to the end of the filename.
00877 ////////////////////////////////////////////////////////////////////
00878 void Filename::
00879 set_hash_to_end(const string &s) {
00880   _filename.replace(_hash_start, string::npos, s);
00881 
00882   locate_basename();
00883   locate_extension();
00884   locate_hash();
00885 }
00886 
00887 ////////////////////////////////////////////////////////////////////
00888 //     Function: Filename::extract_components
00889 //       Access: Published
00890 //  Description: Extracts out the individual directory components of
00891 //               the path into a series of strings.  get_basename()
00892 //               will be the last component stored in the vector.
00893 //               Note that no distinction is made by this method
00894 //               between a leading slash and no leading slash, but you
00895 //               can call is_local() to differentiate the two cases.
00896 ////////////////////////////////////////////////////////////////////
00897 void Filename::
00898 extract_components(vector_string &components) const {
00899   components.clear();
00900 
00901   size_t p = 0;
00902   if (!_filename.empty() && _filename[0] == '/') {
00903     // Skip the leading slash.
00904     p = 1;
00905   }
00906   while (p < _filename.length()) {
00907     size_t q = _filename.find('/', p);
00908     if (q == string::npos) {
00909       components.push_back(_filename.substr(p));
00910       return;
00911     }
00912     components.push_back(_filename.substr(p, q - p));
00913     p = q + 1;
00914   }
00915 
00916   // A trailing slash means we have an empty get_basename().
00917   components.push_back(string());
00918 }
00919 
00920 ////////////////////////////////////////////////////////////////////
00921 //     Function: Filename::standardize
00922 //       Access: Published
00923 //  Description: Converts the filename to standard form by replacing
00924 //               consecutive slashes with a single slash, removing a
00925 //               trailing slash if present, and backing up over ../
00926 //               sequences within the filename where possible.
00927 ////////////////////////////////////////////////////////////////////
00928 void Filename::
00929 standardize() {
00930   assert(!_filename.empty());
00931   if (_filename == ".") {
00932     // Don't change a single dot; this refers to the current directory.
00933     return;
00934   }
00935 
00936   vector_string components;
00937 
00938   // Pull off the components of the filename one at a time.
00939   bool global = (_filename[0] == '/');
00940 
00941   size_t p = 0;
00942   while (p < _filename.length() && _filename[p] == '/') {
00943     p++;
00944   }
00945   while (p < _filename.length()) {
00946     size_t slash = _filename.find('/', p);
00947     string component = _filename.substr(p, slash - p);
00948     if (component == "." && p != 0) {
00949       // Ignore /./.
00950     } else if (component == ".." && !components.empty() &&
00951                !(components.back() == "..")) {
00952       if (components.back() == ".") {
00953         // To "back up" over a leading ./ means simply to remove the
00954         // leading ./
00955         components.pop_back();
00956         components.push_back(component);
00957       } else {
00958         // Back up normally.
00959         components.pop_back();
00960       }
00961     } else {
00962       components.push_back(component);
00963     }
00964 
00965     p = slash;
00966     while (p < _filename.length() && _filename[p] == '/') {
00967       p++;
00968     }
00969   }
00970 
00971   // Now reassemble the filename.
00972   string result;
00973   if (global) {
00974     result = "/";
00975   }
00976   if (!components.empty()) {
00977     result += components[0];
00978     for (int i = 1; i < (int)components.size(); i++) {
00979       result += "/" + components[i];
00980     }
00981   }
00982 
00983   (*this) = result;
00984 }
00985 
00986 ////////////////////////////////////////////////////////////////////
00987 //     Function: Filename::make_absolute
00988 //       Access: Published
00989 //  Description: Converts the filename to a fully-qualified pathname
00990 //               from the root (if it is a relative pathname), and
00991 //               then standardizes it (see standardize()).
00992 //
00993 //               This is sometimes a little problematic, since it may
00994 //               convert the file to its 'true' absolute pathname,
00995 //               which could be an ugly NFS-named file, irrespective
00996 //               of symbolic links
00997 //               (e.g. /.automount/dimbo/root/usr2/fit/people/drose
00998 //               instead of /fit/people/drose); besides being ugly,
00999 //               filenames like this may not be consistent across
01000 //               multiple different platforms.
01001 ////////////////////////////////////////////////////////////////////
01002 void Filename::
01003 make_absolute() {
01004   if (is_local()) {
01005     make_absolute(ExecutionEnvironment::get_cwd());
01006   } else {
01007     standardize();
01008   }
01009 }
01010 
01011 ////////////////////////////////////////////////////////////////////
01012 //     Function: Filename::make_absolute
01013 //       Access: Published
01014 //  Description: Converts the filename to a fully-qualified filename
01015 //               from the root (if it is a relative filename), and
01016 //               then standardizes it (see standardize()).  This
01017 //               flavor accepts a specific starting directory that the
01018 //               filename is known to be relative to.
01019 ////////////////////////////////////////////////////////////////////
01020 void Filename::
01021 make_absolute(const Filename &start_directory) {
01022   if (is_local()) {
01023     Filename new_filename(start_directory, _filename);
01024     new_filename._flags = _flags;
01025     (*this) = new_filename;
01026   }
01027 
01028   standardize();
01029 }
01030 
01031 ////////////////////////////////////////////////////////////////////
01032 //     Function: Filename::make_canonical
01033 //       Access: Published
01034 //  Description: Converts this filename to a canonical name by
01035 //               replacing the directory part with the fully-qualified
01036 //               directory part.  This is done by changing to that
01037 //               directory and calling getcwd().
01038 //
01039 //               This has the effect of (a) converting relative paths
01040 //               to absolute paths (but see make_absolute() if this is
01041 //               the only effect you want), and (b) always resolving a
01042 //               given directory name to the same string, even if
01043 //               different symbolic links are traversed, and (c)
01044 //               changing nice symbolic-link paths like
01045 //               /fit/people/drose to ugly NFS automounter names like
01046 //               /hosts/dimbo/usr2/fit/people/drose.  This can be
01047 //               troubling, but sometimes this is exactly what you
01048 //               want, particularly if you're about to call
01049 //               make_relative_to() between two filenames.
01050 //
01051 //               The return value is true if successful, or false on
01052 //               failure (usually because the directory name does not
01053 //               exist or cannot be chdir'ed into).
01054 ////////////////////////////////////////////////////////////////////
01055 bool Filename::
01056 make_canonical() {
01057   if (empty()) {
01058     // An empty filename is a special case.  This doesn't name
01059     // anything.
01060     return false;
01061   }
01062 
01063   if (get_fullpath() == "/") {
01064     // The root directory is a special case.
01065     return true;
01066   }
01067   
01068 #ifndef WIN32
01069   // Use realpath in order to resolve symlinks properly
01070   char newpath [PATH_MAX + 1];
01071   if (realpath(c_str(), newpath) != NULL) {
01072     (*this) = newpath;
01073   }
01074 #endif
01075 
01076   Filename cwd = ExecutionEnvironment::get_cwd();
01077   if (!r_make_canonical(cwd)) {
01078     return false;
01079   }
01080 
01081   return make_true_case();
01082 }
01083 
01084 ////////////////////////////////////////////////////////////////////
01085 //     Function: Filename::make_true_case
01086 //       Access: Published
01087 //  Description: On a case-insensitive operating system
01088 //               (e.g. Windows), this method looks up the file in the
01089 //               file system and resets the Filename to represent the
01090 //               actual case of the file as it exists on the disk.
01091 //               The return value is true if the file exists and the
01092 //               conversion can be made, or false if there is some
01093 //               error.
01094 //
01095 //               On a case-sensitive operating system, this method
01096 //               does nothing and always returns true.
01097 //
01098 //               An empty filename is considered to exist in this
01099 //               case.
01100 ////////////////////////////////////////////////////////////////////
01101 bool Filename::
01102 make_true_case() {
01103   assert(!get_pattern());
01104 
01105   if (empty()) {
01106     return true;
01107   }
01108 
01109 #ifdef WIN32
01110   string os_specific = to_os_specific();
01111 
01112   // First, we have to convert it to its short name, then back to its
01113   // long name--that seems to be the trick to force Windows to throw
01114   // away the case we give it and get the actual file case.
01115   
01116   char short_name[MAX_PATH + 1];
01117   DWORD l = GetShortPathName(os_specific.c_str(), short_name, MAX_PATH + 1);
01118   if (l == 0) {
01119     // Couldn't query the path name for some reason.  Probably the
01120     // file didn't exist.
01121     return false;
01122   }
01123   // According to the Windows docs, l will return a value greater than
01124   // the specified length if the short_name length wasn't enough--but also
01125   // according to the Windows docs, MAX_PATH will always be enough.
01126   assert(l < MAX_PATH + 1);
01127   
01128   char long_name[MAX_PATH + 1];
01129   l = GetLongPathName(short_name, long_name, MAX_PATH + 1);
01130   if (l == 0) {
01131     // Couldn't query the path name for some reason.  Probably the
01132     // file didn't exist.
01133     return false;
01134   }
01135   assert(l < MAX_PATH + 1);
01136 
01137   Filename true_case = Filename::from_os_specific(long_name);
01138 
01139   // Now sanity-check the true-case filename.  If it's not the same as
01140   // the source file, except for case, reject it.
01141   string orig_filename = get_fullpath();
01142   string new_filename = true_case.get_fullpath();
01143   bool match = (orig_filename.length() == new_filename.length());
01144   for (size_t i = 0; i < orig_filename.length() && match; ++i) {
01145     match = (tolower(orig_filename[i]) == tolower(new_filename[i]));
01146   }
01147   if (!match) {
01148     // Something went wrong.  Keep the original filename, assume it
01149     // was the correct case after all.  We return true because the
01150     // filename is good.
01151     return true;
01152   }
01153 
01154   (*this) = true_case;
01155   return true;
01156 
01157 #else  // WIN32
01158   return true;
01159 #endif  // WIN32
01160 }
01161 
01162 ////////////////////////////////////////////////////////////////////
01163 //     Function: Filename::to_os_specific
01164 //       Access: Published
01165 //  Description: Converts the filename from our generic Unix-like
01166 //               convention (forward slashes starting with the root at
01167 //               '/') to the corresponding filename in the local
01168 //               operating system (slashes in the appropriate
01169 //               direction, starting with the root at C:\, for
01170 //               instance).  Returns the string representing the
01171 //               converted filename, but does not change the Filename
01172 //               itself.
01173 //
01174 //               See also from_os_specific().
01175 ////////////////////////////////////////////////////////////////////
01176 string Filename::
01177 to_os_specific() const {
01178   assert(!get_pattern());
01179 
01180   if (empty()) {
01181     return string();
01182   }
01183   Filename standard(*this);
01184   standard.standardize();
01185 
01186 #ifdef IS_OSX 
01187   if (get_type() == T_dso) {
01188     std::string workname = standard.get_fullpath();
01189     size_t dot = workname.rfind('.');
01190     if (dot != string::npos) {
01191       if (workname.substr(dot) == ".so") {
01192         string dyLibBase = workname.substr(0, dot)+".dylib";
01193         return dyLibBase; 
01194       }
01195     }
01196   }
01197 #endif
01198 
01199 #ifdef WIN32
01200   switch (get_type()) {
01201   case T_dso:
01202     return convert_dso_pathname(standard.get_fullpath());
01203   case T_executable:
01204     return convert_executable_pathname(standard.get_fullpath());
01205   default:
01206     return convert_pathname(standard.get_fullpath());
01207   }
01208 #else // WIN32
01209   return standard.c_str();
01210 #endif // WIN32
01211 }
01212 
01213 ////////////////////////////////////////////////////////////////////
01214 //     Function: Filename::to_os_generic
01215 //       Access: Published
01216 //  Description: This is similar to to_os_specific(), but it is
01217 //               designed to generate a filename that can be
01218 //               understood on as many platforms as possible.  Since
01219 //               Windows can usually understand a
01220 //               forward-slash-delimited filename, this means it does
01221 //               the same thing as to_os_specific(), but it uses
01222 //               forward slashes instead of backslashes.
01223 //
01224 //               This method has a pretty limited use; it should
01225 //               generally be used for writing file references to a
01226 //               file that might be read on any operating system.
01227 ////////////////////////////////////////////////////////////////////
01228 string Filename::
01229 to_os_generic() const {
01230   assert(!get_pattern());
01231 
01232 #ifdef WIN32
01233   return back_to_front_slash(to_os_specific());
01234 #else // WIN32
01235   return to_os_specific();
01236 #endif // WIN32
01237 }
01238 
01239 ////////////////////////////////////////////////////////////////////
01240 //     Function: Filename::to_os_short_name
01241 //       Access: Published
01242 //  Description: This works like to_os_generic(), but it returns the
01243 //               "short name" version of the filename, if it exists,
01244 //               or the original filename otherwise.
01245 //
01246 //               On Windows platforms, this returns the 8.3 filename
01247 //               version of the given filename, if the file exists,
01248 //               and the same thing as to_os_specific() otherwise.  On
01249 //               non-Windows platforms, this always returns the same
01250 //               thing as to_os_specific().
01251 ////////////////////////////////////////////////////////////////////
01252 string Filename::
01253 to_os_short_name() const {
01254   assert(!get_pattern());
01255 
01256 #ifdef WIN32
01257   string os_specific = to_os_specific();
01258   
01259   char short_name[MAX_PATH + 1];
01260   DWORD l = GetShortPathName(os_specific.c_str(), short_name, MAX_PATH + 1);
01261   if (l == 0) {
01262     // Couldn't query the path name for some reason.  Probably the
01263     // file didn't exist.
01264     return os_specific;
01265   }
01266   // According to the Windows docs, l will return a value greater than
01267   // the specified length if the short_name length wasn't enough--but also
01268   // according to the Windows docs, MAX_PATH will always be enough.
01269   assert(l < MAX_PATH + 1);
01270 
01271   return string(short_name);
01272 
01273 #else // WIN32
01274   return to_os_specific();
01275 #endif // WIN32
01276 }
01277 
01278 ////////////////////////////////////////////////////////////////////
01279 //     Function: Filename::to_os_long_name
01280 //       Access: Published
01281 //  Description: This is the opposite of to_os_short_name(): it
01282 //               returns the "long name" of the filename, if the
01283 //               filename exists.  On non-Windows platforms, this
01284 //               returns the same thing as to_os_specific().
01285 ////////////////////////////////////////////////////////////////////
01286 string Filename::
01287 to_os_long_name() const {
01288   assert(!get_pattern());
01289 
01290 #ifdef WIN32
01291   string os_specific = to_os_specific();
01292   
01293   char long_name[MAX_PATH + 1];
01294   DWORD l = GetLongPathName(os_specific.c_str(), long_name, MAX_PATH + 1);
01295   if (l == 0) {
01296     // Couldn't query the path name for some reason.  Probably the
01297     // file didn't exist.
01298     return os_specific;
01299   }
01300   assert(l < MAX_PATH + 1);
01301 
01302   return string(long_name);
01303 
01304 #else // WIN32
01305   return to_os_specific();
01306 #endif // WIN32
01307 }
01308 
01309 ////////////////////////////////////////////////////////////////////
01310 //     Function: Filename::exists
01311 //       Access: Published
01312 //  Description: Returns true if the filename exists on the disk,
01313 //               false otherwise.  If the type is indicated to be
01314 //               executable, this also tests that the file has execute
01315 //               permission.
01316 ////////////////////////////////////////////////////////////////////
01317 bool Filename::
01318 exists() const {
01319   string os_specific = get_filename_index(0).to_os_specific();
01320 
01321 #ifdef WIN32_VC
01322   bool exists = false;
01323 
01324   DWORD results = GetFileAttributes(os_specific.c_str());
01325   if (results != -1) {
01326     exists = true;
01327   }
01328 
01329 #else  // WIN32_VC
01330   struct stat this_buf;
01331   bool exists = false;
01332 
01333   if (stat(os_specific.c_str(), &this_buf) == 0) {
01334     exists = true;
01335   }
01336 #endif
01337 
01338   return exists;
01339 }
01340 
01341 ////////////////////////////////////////////////////////////////////
01342 //     Function: Filename::is_regular_file
01343 //       Access: Published
01344 //  Description: Returns true if the filename exists and is the
01345 //               name of a regular file (i.e. not a directory or
01346 //               device), false otherwise.
01347 ////////////////////////////////////////////////////////////////////
01348 bool Filename::
01349 is_regular_file() const {
01350   string os_specific = get_filename_index(0).to_os_specific();
01351 
01352 #ifdef WIN32_VC
01353   bool isreg = false;
01354 
01355   DWORD results = GetFileAttributes(os_specific.c_str());
01356   if (results != -1) {
01357     isreg = ((results & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0);
01358   }
01359 
01360 #else  // WIN32_VC
01361   struct stat this_buf;
01362   bool isreg = false;
01363 
01364   if (stat(os_specific.c_str(), &this_buf) == 0) {
01365     isreg = S_ISREG(this_buf.st_mode);
01366   }
01367 #endif
01368 
01369   return isreg;
01370 }
01371 
01372 ////////////////////////////////////////////////////////////////////
01373 //     Function: Filename::is_directory
01374 //       Access: Published
01375 //  Description: Returns true if the filename exists and is a
01376 //               directory name, false otherwise.
01377 ////////////////////////////////////////////////////////////////////
01378 bool Filename::
01379 is_directory() const {
01380   string os_specific = get_filename_index(0).to_os_specific();
01381 
01382 #ifdef WIN32_VC
01383   bool isdir = false;
01384 
01385   DWORD results = GetFileAttributes(os_specific.c_str());
01386   if (results != -1) {
01387     isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
01388   }
01389 #else  // WIN32_VC
01390   struct stat this_buf;
01391   bool isdir = false;
01392 
01393   if (stat(os_specific.c_str(), &this_buf) == 0) {
01394     isdir = S_ISDIR(this_buf.st_mode);
01395   }
01396 #endif
01397 
01398   return isdir;
01399 }
01400 
01401 ////////////////////////////////////////////////////////////////////
01402 //     Function: Filename::is_executable
01403 //       Access: Published
01404 //  Description: Returns true if the filename exists and is
01405 //               executable
01406 ////////////////////////////////////////////////////////////////////
01407 bool Filename::
01408 is_executable() const {
01409 #ifdef WIN32_VC
01410   // no access() in windows, but to our advantage executables can only
01411   // end in .exe or .com
01412   string extension = get_extension();
01413   if (extension == "exe" || extension == "com") {
01414     return exists();
01415   }
01416 
01417 #else /* WIN32_VC */
01418   string os_specific = get_filename_index(0).to_os_specific();
01419   if (access(os_specific.c_str(), X_OK) == 0) {
01420     return true;
01421   }
01422 #endif /* WIN32_VC */
01423 
01424   return false;
01425 }
01426 
01427 ////////////////////////////////////////////////////////////////////
01428 //     Function: Filename::compare_timestamps
01429 //       Access: Published
01430 //  Description: Returns a number less than zero if the file named by
01431 //               this object is older than the given file, zero if
01432 //               they have the same timestamp, or greater than zero if
01433 //               this one is newer.
01434 //
01435 //               If this_missing_is_old is true, it indicates that a
01436 //               missing file will be treated as if it were older than
01437 //               any other file; otherwise, a missing file will be
01438 //               treated as if it were newer than any other file.
01439 //               Similarly for other_missing_is_old.
01440 ////////////////////////////////////////////////////////////////////
01441 int Filename::
01442 compare_timestamps(const Filename &other,
01443                    bool this_missing_is_old,
01444                    bool other_missing_is_old) const {
01445   string os_specific = get_filename_index(0).to_os_specific();
01446   string other_os_specific = other.get_filename_index(0).to_os_specific();
01447 
01448 #ifdef WIN32_VC
01449   struct _stat this_buf;
01450   bool this_exists = false;
01451 
01452   if (_stat(os_specific.c_str(), &this_buf) == 0) {
01453     this_exists = true;
01454   }
01455 
01456   struct _stat other_buf;
01457   bool other_exists = false;
01458 
01459   if (_stat(other_os_specific.c_str(), &other_buf) == 0) {
01460     other_exists = true;
01461   }
01462 #else  // WIN32_VC
01463   struct stat this_buf;
01464   bool this_exists = false;
01465 
01466   if (stat(os_specific.c_str(), &this_buf) == 0) {
01467     this_exists = true;
01468   }
01469 
01470   struct stat other_buf;
01471   bool other_exists = false;
01472 
01473   if (stat(other_os_specific.c_str(), &other_buf) == 0) {
01474     other_exists = true;
01475   }
01476 #endif
01477 
01478   if (this_exists && other_exists) {
01479     // Both files exist, return the honest time comparison.
01480     return (int)this_buf.st_mtime - (int)other_buf.st_mtime;
01481 
01482   } else if (!this_exists && !other_exists) {
01483     // Neither file exists.
01484     if (this_missing_is_old == other_missing_is_old) {
01485       // Both files are either "very old" or "very new".
01486       return 0;
01487     }
01488     if (this_missing_is_old) {
01489       // This file is "very old", the other is "very new".
01490       return -1;
01491     } else {
01492       // This file is "very new", the other is "very old".
01493       return 1;
01494     }
01495 
01496   } else if (!this_exists) {
01497     // This file doesn't, the other one does.
01498     return this_missing_is_old ? -1 : 1;
01499 
01500   }
01501   // !other_exists
01502   assert(!other_exists);
01503   
01504   // This file exists, the other one doesn't.
01505   return other_missing_is_old ? 1 : -1;
01506 }
01507 
01508 ////////////////////////////////////////////////////////////////////
01509 //     Function: Filename::get_timestamp
01510 //       Access: Published
01511 //  Description: Returns a time_t value that represents the time the
01512 //               file was last modified, to within whatever precision
01513 //               the operating system records this information (on a
01514 //               Windows95 system, for instance, this may only be
01515 //               accurate to within 2 seconds).
01516 //
01517 //               If the timestamp cannot be determined, either because
01518 //               it is not supported by the operating system or
01519 //               because there is some error (such as file not found),
01520 //               returns 0.
01521 ////////////////////////////////////////////////////////////////////
01522 time_t Filename::
01523 get_timestamp() const {
01524   string os_specific = get_filename_index(0).to_os_specific();
01525 
01526 #ifdef WIN32_VC
01527   struct _stat this_buf;
01528 
01529   if (_stat(os_specific.c_str(), &this_buf) == 0) {
01530     return this_buf.st_mtime;
01531   }
01532 #else  // WIN32_VC
01533   struct stat this_buf;
01534 
01535   if (stat(os_specific.c_str(), &this_buf) == 0) {
01536     return this_buf.st_mtime;
01537   }
01538 #endif
01539 
01540   return 0;
01541 }
01542 
01543 ////////////////////////////////////////////////////////////////////
01544 //     Function: Filename::get_access_timestamp
01545 //       Access: Published
01546 //  Description: Returns a time_t value that represents the time the
01547 //               file was last accessed, if this information is
01548 //               available.  See also get_timestamp(), which returns
01549 //               the last modification time.
01550 ////////////////////////////////////////////////////////////////////
01551 time_t Filename::
01552 get_access_timestamp() const {
01553   string os_specific = get_filename_index(0).to_os_specific();
01554 
01555 #ifdef WIN32_VC
01556   struct _stat this_buf;
01557 
01558   if (_stat(os_specific.c_str(), &this_buf) == 0) {
01559     return this_buf.st_atime;
01560   }
01561 #else  // WIN32_VC
01562   struct stat this_buf;
01563 
01564   if (stat(os_specific.c_str(), &this_buf) == 0) {
01565     return this_buf.st_atime;
01566   }
01567 #endif
01568 
01569   return 0;
01570 }
01571 
01572 ////////////////////////////////////////////////////////////////////
01573 //     Function: Filename::get_file_size
01574 //       Access: Published
01575 //  Description: Returns the size of the file in bytes, or 0 if there
01576 //               is an error.
01577 ////////////////////////////////////////////////////////////////////
01578 off_t Filename::
01579 get_file_size() const {
01580   string os_specific = get_filename_index(0).to_os_specific();
01581 
01582 #ifdef WIN32_VC
01583   struct _stat this_buf;
01584 
01585   if (_stat(os_specific.c_str(), &this_buf) == 0) {
01586     return this_buf.st_size;
01587   }
01588 #else  // WIN32_VC
01589   struct stat this_buf;
01590 
01591   if (stat(os_specific.c_str(), &this_buf) == 0) {
01592     return this_buf.st_size;
01593   }
01594 #endif
01595 
01596   return 0;
01597 }
01598 
01599 ////////////////////////////////////////////////////////////////////
01600 //     Function: Filename::resolve_filename
01601 //       Access: Published
01602 //  Description: Searches the given search path for the filename.  If
01603 //               it is found, updates the filename to the full
01604 //               pathname found and returns true; otherwise, returns
01605 //               false.
01606 ////////////////////////////////////////////////////////////////////
01607 bool Filename::
01608 resolve_filename(const DSearchPath &searchpath,
01609                  const string &default_extension) {
01610   Filename found;
01611 
01612   if (is_local()) {
01613     found = searchpath.find_file(*this);
01614 
01615     if (found.empty()) {
01616       // We didn't find it with the given extension; can we try the
01617       // default extension?
01618       if (get_extension().empty() && !default_extension.empty()) {
01619         Filename try_ext = *this;
01620         try_ext.set_extension(default_extension);
01621         found = searchpath.find_file(try_ext);
01622       }
01623     }
01624   } else {
01625     if (exists()) {
01626       // The full pathname exists.  Return true.
01627       return true;
01628     } else {
01629       // The full pathname doesn't exist with the given extension;
01630       // does it exist with the default extension?
01631       if (get_extension().empty() && !default_extension.empty()) {
01632         Filename try_ext = *this;
01633         try_ext.set_extension(default_extension);
01634         if (try_ext.exists()) {
01635           found = try_ext;
01636         }
01637       }
01638     }
01639   }
01640 
01641   if (!found.empty()) {
01642     (*this) = found;
01643     return true;
01644   }
01645 
01646   return false;
01647 }
01648 
01649 ////////////////////////////////////////////////////////////////////
01650 //     Function: Filename::make_relative_to
01651 //       Access: Published
01652 //  Description: Adjusts this filename, which must be a
01653 //               fully-specified pathname beginning with a slash, to
01654 //               make it a relative filename, relative to the
01655 //               fully-specified directory indicated (which must also
01656 //               begin with, and may or may not end with, a slash--a
01657 //               terminating slash is ignored).
01658 //
01659 //               This only performs a string comparsion, so it may be
01660 //               wise to call make_canonical() on both filenames
01661 //               before calling make_relative_to().
01662 //
01663 //               If allow_backups is false, the filename will only be
01664 //               adjusted to be made relative if it is already
01665 //               somewhere within or below the indicated directory.
01666 //               If allow_backups is true, it will be adjusted in all
01667 //               cases, even if this requires putting a series of ../
01668 //               characters before the filename--unless it would have
01669 //               to back all the way up to the root.
01670 //
01671 //               Returns true if the file was adjusted, false if it
01672 //               was not.
01673 ////////////////////////////////////////////////////////////////////
01674 bool Filename::
01675 make_relative_to(Filename directory, bool allow_backups) {
01676   if (_filename.empty() || directory.empty() ||
01677       _filename[0] != '/' || directory[0] != '/') {
01678     return false;
01679   }
01680 
01681   standardize();
01682   directory.standardize();
01683 
01684   if (directory == "/") {
01685     // Don't be silly.
01686     return false;
01687   }
01688 
01689   string rel_to_file = directory.get_fullpath() + "/.";
01690 
01691   size_t common = get_common_prefix(rel_to_file);
01692   if (common < 2) {
01693     // Oh, never mind.
01694     return false;
01695   }
01696 
01697   string result;
01698   int slashes = count_slashes(rel_to_file.substr(common));
01699   if (slashes > 0 && !allow_backups) {
01700     // Too bad; the file's not under the indicated directory.
01701     return false;
01702   }
01703 
01704   for (int i = 0; i < slashes; i++) {
01705     result += "../";
01706   }
01707   result += _filename.substr(common);
01708   (*this) = result;
01709 
01710   return true;
01711 }
01712 
01713 ////////////////////////////////////////////////////////////////////
01714 //     Function: Filename::find_on_searchpath
01715 //       Access: Published
01716 //  Description: Performs the reverse of the resolve_filename()
01717 //               operation: assuming that the current filename is
01718 //               fully-specified pathname (i.e. beginning with '/'),
01719 //               look on the indicated search path for a directory
01720 //               under which the file can be found.  When found,
01721 //               adjust the Filename to be relative to the indicated
01722 //               directory name.
01723 //
01724 //               Returns the index of the directory on the searchpath
01725 //               at which the file was found, or -1 if it was not
01726 //               found.
01727 ////////////////////////////////////////////////////////////////////
01728 int Filename::
01729 find_on_searchpath(const DSearchPath &searchpath) {
01730   if (_filename.empty() || _filename[0] != '/') {
01731     return -1;
01732   }
01733 
01734   int num_directories = searchpath.get_num_directories();
01735   for (int i = 0; i < num_directories; i++) {
01736     Filename directory = searchpath.get_directory(i);
01737     directory.make_absolute();
01738     if (make_relative_to(directory, false)) {
01739       return i;
01740     }
01741   }
01742 
01743   return -1;
01744 }
01745 
01746 ////////////////////////////////////////////////////////////////////
01747 //     Function: Filename::scan_directory
01748 //       Access: Published
01749 //  Description: Attempts to open the named filename as if it were a
01750 //               directory and looks for the non-hidden files within
01751 //               the directory.  Fills the given vector up with the
01752 //               sorted list of filenames that are local to this
01753 //               directory.
01754 //
01755 //               It is the user's responsibility to ensure that the
01756 //               contents vector is empty before making this call;
01757 //               otherwise, the new files will be appended to it.
01758 //
01759 //               Returns true on success, false if the directory could
01760 //               not be read for some reason.
01761 ////////////////////////////////////////////////////////////////////
01762 bool Filename::
01763 scan_directory(vector_string &contents) const {
01764   assert(!get_pattern());
01765 
01766 #if defined(WIN32_VC)
01767   // Use Windows' FindFirstFile() / FindNextFile() to walk through the
01768   // list of files in a directory.
01769   size_t orig_size = contents.size();
01770 
01771   string match;
01772   if (empty()) {
01773     match = "*.*";
01774   } else {
01775     match = to_os_specific() + "\\*.*";
01776   }
01777   WIN32_FIND_DATA find_data;
01778 
01779   HANDLE handle = FindFirstFile(match.c_str(), &find_data);
01780   if (handle == INVALID_HANDLE_VALUE) {
01781     if (GetLastError() == ERROR_NO_MORE_FILES) {
01782       // No matching files is not an error.
01783       return true;
01784     }
01785     return false;
01786   }
01787 
01788   do {
01789     thread_consider_yield();
01790     string filename = find_data.cFileName;
01791     if (filename != "." && filename != "..") {
01792       contents.push_back(filename);
01793     }
01794   } while (FindNextFile(handle, &find_data));
01795 
01796   bool scan_ok = (GetLastError() == ERROR_NO_MORE_FILES);
01797   FindClose(handle);
01798 
01799   sort(contents.begin() + orig_size, contents.end());
01800   return scan_ok;
01801 
01802 #elif defined(PHAVE_DIRENT_H)
01803   // Use Posix's opendir() / readdir() to walk through the list of
01804   // files in a directory.
01805   size_t orig_size = contents.size();
01806 
01807   string dirname;
01808   if (empty()) {
01809     dirname = ".";
01810   } else {
01811     dirname = _filename;
01812   }
01813   DIR *root = opendir(dirname.c_str());
01814   if (root == (DIR *)NULL) {
01815     if (errno != ENOTDIR) {
01816       perror(dirname.c_str());
01817     }
01818     return false;
01819   }
01820 
01821   struct dirent *d;
01822   d = readdir(root);
01823   while (d != (struct dirent *)NULL) {
01824     thread_consider_yield();
01825     if (d->d_name[0] != '.') {
01826       contents.push_back(d->d_name);
01827     }
01828     d = readdir(root);
01829   }
01830 
01831   // It turns out to be a mistake to check the value of errno after
01832   // calling readdir(), since it might have been set to non-zero
01833   // during some internal operation of readdir(), even though there
01834   // wasn't really a problem with scanning the directory itself.
01835   /*
01836   if (errno != 0 && errno != ENOENT && errno != ENOTDIR) {
01837     cerr << "Error occurred while scanning directory " << dirname << "\n";
01838     perror(dirname.c_str());
01839     closedir(root);
01840     return false;
01841   }
01842   */
01843   closedir(root);
01844 
01845   sort(contents.begin() + orig_size, contents.end());
01846   return true;
01847 
01848 #elif defined(PHAVE_GLOB_H)
01849   // It's hard to imagine a system that provides glob.h but does not
01850   // provide openddir() .. readdir(), but this code is leftover from a
01851   // time when there was an undetected bug in the above readdir()
01852   // loop, and it works, so we might as well keep it around for now.
01853   string dirname;
01854   if (empty()) {
01855     dirname = "*";
01856   } else if (_filename[_filename.length() - 1] == '/') {
01857     dirname = _filename + "*";
01858   } else {
01859     dirname = _filename + "/*";   /* comment to fix emacs syntax hilight */
01860   }
01861 
01862   glob_t globbuf;
01863 
01864   int r = glob(dirname.c_str(), GLOB_ERR, NULL, &globbuf);
01865 
01866   if (r != 0) {
01867     // Some error processing the match string.  If our version of
01868     // glob.h defines GLOB_NOMATCH, then we can differentiate an empty
01869     // return result from some other kind of error.
01870 #ifdef GLOB_NOMATCH
01871     if (r != GLOB_NOMATCH) {
01872       perror(dirname.c_str());
01873       return false;
01874     }
01875 #endif
01876 
01877     // Otherwise, all errors mean the same thing: no matches, but
01878     // otherwise no problem.
01879     return true;
01880   }
01881 
01882   size_t offset = dirname.size() - 1;
01883 
01884   for (int i = 0; globbuf.gl_pathv[i] != NULL; i++) {
01885     contents.push_back(globbuf.gl_pathv[i] + offset);
01886   }
01887   globfree(&globbuf);
01888 
01889   return true;
01890   
01891 #else
01892   // Don't know how to scan directories!
01893   return false;
01894 #endif
01895 }
01896 
01897 #ifdef HAVE_PYTHON
01898 ////////////////////////////////////////////////////////////////////
01899 //     Function: Filename::scan_directory
01900 //       Access: Published
01901 //  Description: This variant on scan_directory returns a Python list
01902 //               of strings on success, or None on failure.
01903 ////////////////////////////////////////////////////////////////////
01904 PyObject *Filename::
01905 scan_directory() const {
01906   vector_string contents;
01907   if (!scan_directory(contents)) {
01908     PyObject *result = Py_None;
01909     Py_INCREF(result);
01910     return result;
01911   }
01912 
01913   PyObject *result = PyList_New(contents.size());
01914   for (size_t i = 0; i < contents.size(); ++i) {
01915     const string &filename = contents[i];
01916     PyObject *str = PyString_FromStringAndSize(filename.data(), filename.size());
01917     PyList_SET_ITEM(result, i, str);
01918   }
01919 
01920   return result;
01921 }
01922 #endif  // HAVE_PYTHON
01923 
01924 ////////////////////////////////////////////////////////////////////
01925 //     Function: Filename::open_read
01926 //       Access: Published
01927 //  Description: Opens the indicated ifstream for reading the file, if
01928 //               possible.  Returns true if successful, false
01929 //               otherwise.  This requires the setting of the
01930 //               set_text()/set_binary() flags to open the file
01931 //               appropriately as indicated; it is an error to call
01932 //               open_read() without first calling one of set_text()
01933 //               or set_binary().
01934 ////////////////////////////////////////////////////////////////////
01935 bool Filename::
01936 open_read(ifstream &stream) const {
01937   assert(!get_pattern());
01938   assert(is_text() || is_binary());
01939 
01940   ios_openmode open_mode = ios::in;
01941 
01942 #ifdef HAVE_IOS_BINARY
01943   // For some reason, some systems (like Irix) don't define
01944   // ios::binary.
01945   if (!is_text()) {
01946     open_mode |= ios::binary;
01947   }
01948 #endif
01949 
01950   string os_specific = to_os_specific();
01951   stream.clear();
01952   stream.open(os_specific.c_str(), open_mode);
01953   return (!stream.fail());
01954 }
01955 
01956 ////////////////////////////////////////////////////////////////////
01957 //     Function: Filename::open_write
01958 //       Access: Published
01959 //  Description: Opens the indicated ifstream for writing the file, if
01960 //               possible.  Returns true if successful, false
01961 //               otherwise.  This requires the setting of the
01962 //               set_text()/set_binary() flags to open the file
01963 //               appropriately as indicated; it is an error to call
01964 //               open_read() without first calling one of set_text()
01965 //               or set_binary().
01966 //
01967 //               If truncate is true, the file is truncated to zero
01968 //               length upon opening it, if it already exists.
01969 //               Otherwise, the file is kept at its original length.
01970 ////////////////////////////////////////////////////////////////////
01971 bool Filename::
01972 open_write(ofstream &stream, bool truncate) const {
01973   assert(!get_pattern());
01974   assert(is_text() || is_binary());
01975 
01976   ios_openmode open_mode = ios::out;
01977 
01978   if (truncate) {
01979     open_mode |= ios::trunc;
01980 
01981   } else {
01982     // Some systems insist on having ios::in set to prevent the file
01983     // from being truncated when we open it.  Makes ios::trunc kind of
01984     // pointless, doesn't it?  On the other hand, setting ios::in also
01985     // seems to imply ios::nocreate (!), so we should only set this if
01986     // the file already exists.
01987     if (exists()) {
01988       open_mode |= ios::in;
01989     }
01990   }
01991 
01992 #ifdef HAVE_IOS_BINARY
01993   // For some reason, some systems (like Irix) don't define
01994   // ios::binary.
01995   if (!is_text()) {
01996     open_mode |= ios::binary;
01997   }
01998 #endif
01999 
02000   stream.clear();
02001   string os_specific = to_os_specific();
02002 #ifdef HAVE_OPEN_MASK
02003   stream.open(os_specific.c_str(), open_mode, 0666);
02004 #else
02005   stream.open(os_specific.c_str(), open_mode);
02006 #endif
02007 
02008   return (!stream.fail());
02009 }
02010 
02011 ////////////////////////////////////////////////////////////////////
02012 //     Function: Filename::open_append
02013 //       Access: Published
02014 //  Description: Opens the indicated ofstream for writing the file, if
02015 //               possible.  Returns true if successful, false
02016 //               otherwise.  This requires the setting of the
02017 //               set_text()/set_binary() flags to open the file
02018 //               appropriately as indicated; it is an error to call
02019 //               open_read() without first calling one of set_text()
02020 //               or set_binary().
02021 ////////////////////////////////////////////////////////////////////
02022 bool Filename::
02023 open_append(ofstream &stream) const {
02024   assert(!get_pattern());
02025   assert(is_text() || is_binary());
02026 
02027   ios_openmode open_mode = ios::app;
02028 
02029 #ifdef HAVE_IOS_BINARY
02030   // For some reason, some systems (like Irix) don't define
02031   // ios::binary.
02032   if (!is_text()) {
02033     open_mode |= ios::binary;
02034   }
02035 #endif
02036 
02037   stream.clear();
02038   string os_specific = to_os_specific();
02039 #ifdef HAVE_OPEN_MASK
02040   stream.open(os_specific.c_str(), open_mode, 0666);
02041 #else
02042   stream.open(os_specific.c_str(), open_mode);
02043 #endif
02044 
02045   return (!stream.fail());
02046 }
02047 
02048 ////////////////////////////////////////////////////////////////////
02049 //     Function: Filename::open_read_write
02050 //       Access: Published
02051 //  Description: Opens the indicated fstream for read/write access to
02052 //               the file, if possible.  Returns true if successful,
02053 //               false otherwise.  This requires the setting of the
02054 //               set_text()/set_binary() flags to open the file
02055 //               appropriately as indicated; it is an error to call
02056 //               open_read_write() without first calling one of
02057 //               set_text() or set_binary().
02058 ////////////////////////////////////////////////////////////////////
02059 bool Filename::
02060 open_read_write(fstream &stream, bool truncate) const {
02061   assert(!get_pattern());
02062   assert(is_text() || is_binary());
02063 
02064   ios_openmode open_mode = ios::out | ios::in;
02065 
02066   if (truncate) {
02067     open_mode |= ios::trunc;
02068   }
02069 
02070   // Since ios::in also seems to imply ios::nocreate (!), we must
02071   // guarantee the file already exists before we try to open it.
02072   if (!exists()) {
02073     touch();
02074   }
02075 
02076 #ifdef HAVE_IOS_BINARY
02077   // For some reason, some systems (like Irix) don't define
02078   // ios::binary.
02079   if (!is_text()) {
02080     open_mode |= ios::binary;
02081   }
02082 #endif
02083 
02084   stream.clear();
02085   string os_specific = to_os_specific();
02086 #ifdef HAVE_OPEN_MASK
02087   stream.open(os_specific.c_str(), open_mode, 0666);
02088 #else
02089   stream.open(os_specific.c_str(), open_mode);
02090 #endif
02091 
02092   return (!stream.fail());
02093 }
02094 
02095 ////////////////////////////////////////////////////////////////////
02096 //     Function: Filename::open_read_append
02097 //       Access: Published
02098 //  Description: Opens the indicated ifstream for reading and writing
02099 //               the file, if possible; writes are appended to the end
02100 //               of the file.  Returns true if successful, false
02101 //               otherwise.  This requires the setting of the
02102 //               set_text()/set_binary() flags to open the file
02103 //               appropriately as indicated; it is an error to call
02104 //               open_read() without first calling one of set_text()
02105 //               or set_binary().
02106 ////////////////////////////////////////////////////////////////////
02107 bool Filename::
02108 open_read_append(fstream &stream) const {
02109   assert(!get_pattern());
02110   assert(is_text() || is_binary());
02111 
02112   ios_openmode open_mode = ios::app | ios::in;
02113 
02114 #ifdef HAVE_IOS_BINARY
02115   // For some reason, some systems (like Irix) don't define
02116   // ios::binary.
02117   if (!is_text()) {
02118     open_mode |= ios::binary;
02119   }
02120 #endif
02121 
02122   stream.clear();
02123   string os_specific = to_os_specific();
02124 #ifdef HAVE_OPEN_MASK
02125   stream.open(os_specific.c_str(), open_mode, 0666);
02126 #else
02127   stream.open(os_specific.c_str(), open_mode);
02128 #endif
02129 
02130   return (!stream.fail());
02131 }
02132 
02133 #ifdef USE_PANDAFILESTREAM
02134 ////////////////////////////////////////////////////////////////////
02135 //     Function: Filename::open_read
02136 //       Access: Published
02137 //  Description: Opens the indicated pifstream for reading the file, if
02138 //               possible.  Returns true if successful, false
02139 //               otherwise.  This requires the setting of the
02140 //               set_text()/set_binary() flags to open the file
02141 //               appropriately as indicated; it is an error to call
02142 //               open_read() without first calling one of set_text()
02143 //               or set_binary().
02144 ////////////////////////////////////////////////////////////////////
02145 bool Filename::
02146 open_read(pifstream &stream) const {
02147   assert(!get_pattern());
02148   assert(is_text() || is_binary());
02149 
02150   ios_openmode open_mode = ios::in;
02151 
02152 #ifdef HAVE_IOS_BINARY
02153   // For some reason, some systems (like Irix) don't define
02154   // ios::binary.
02155   if (!is_text()) {
02156     open_mode |= ios::binary;
02157   }
02158 #endif
02159 
02160   string os_specific = to_os_specific();
02161   stream.clear();
02162   stream.open(os_specific.c_str(), open_mode);
02163   return (!stream.fail());
02164 }
02165 #endif  // USE_PANDAFILESTREAM
02166 
02167 #ifdef USE_PANDAFILESTREAM
02168 ////////////////////////////////////////////////////////////////////
02169 //     Function: Filename::open_write
02170 //       Access: Published
02171 //  Description: Opens the indicated pifstream for writing the file, if
02172 //               possible.  Returns true if successful, false
02173 //               otherwise.  This requires the setting of the
02174 //               set_text()/set_binary() flags to open the file
02175 //               appropriately as indicated; it is an error to call
02176 //               open_read() without first calling one of set_text()
02177 //               or set_binary().
02178 //
02179 //               If truncate is true, the file is truncated to zero
02180 //               length upon opening it, if it already exists.
02181 //               Otherwise, the file is kept at its original length.
02182 ////////////////////////////////////////////////////////////////////
02183 bool Filename::
02184 open_write(pofstream &stream, bool truncate) const {
02185   assert(!get_pattern());
02186   assert(is_text() || is_binary());
02187 
02188   ios_openmode open_mode = ios::out;
02189 
02190   if (truncate) {
02191     open_mode |= ios::trunc;
02192 
02193   } else {
02194     // Some systems insist on having ios::in set to prevent the file
02195     // from being truncated when we open it.  Makes ios::trunc kind of
02196     // pointless, doesn't it?  On the other hand, setting ios::in also
02197     // seems to imply ios::nocreate (!), so we should only set this if
02198     // the file already exists.
02199     if (exists()) {
02200       open_mode |= ios::in;
02201     }
02202   }
02203 
02204 #ifdef HAVE_IOS_BINARY
02205   // For some reason, some systems (like Irix) don't define
02206   // ios::binary.
02207   if (!is_text()) {
02208     open_mode |= ios::binary;
02209   }
02210 #endif
02211 
02212   stream.clear();
02213   string os_specific = to_os_specific();
02214 #ifdef HAVE_OPEN_MASK
02215   stream.open(os_specific.c_str(), open_mode, 0666);
02216 #else
02217   stream.open(os_specific.c_str(), open_mode);
02218 #endif
02219 
02220   return (!stream.fail());
02221 }
02222 #endif  // USE_PANDAFILESTREAM
02223 
02224 #ifdef USE_PANDAFILESTREAM
02225 ////////////////////////////////////////////////////////////////////
02226 //     Function: Filename::open_append
02227 //       Access: Published
02228 //  Description: Opens the indicated pifstream for writing the file, if
02229 //               possible.  Returns true if successful, false
02230 //               otherwise.  This requires the setting of the
02231 //               set_text()/set_binary() flags to open the file
02232 //               appropriately as indicated; it is an error to call
02233 //               open_read() without first calling one of set_text()
02234 //               or set_binary().
02235 ////////////////////////////////////////////////////////////////////
02236 bool Filename::
02237 open_append(pofstream &stream) const {
02238   assert(!get_pattern());
02239   assert(is_text() || is_binary());
02240 
02241   ios_openmode open_mode = ios::app;
02242 
02243 #ifdef HAVE_IOS_BINARY
02244   // For some reason, some systems (like Irix) don't define
02245   // ios::binary.
02246   if (!is_text()) {
02247     open_mode |= ios::binary;
02248   }
02249 #endif
02250 
02251   stream.clear();
02252   string os_specific = to_os_specific();
02253 #ifdef HAVE_OPEN_MASK
02254   stream.open(os_specific.c_str(), open_mode, 0666);
02255 #else
02256   stream.open(os_specific.c_str(), open_mode);
02257 #endif
02258 
02259   return (!stream.fail());
02260 }
02261 #endif  // USE_PANDAFILESTREAM
02262 
02263 #ifdef USE_PANDAFILESTREAM
02264 ////////////////////////////////////////////////////////////////////
02265 //     Function: Filename::open_read_write
02266 //       Access: Published
02267 //  Description: Opens the indicated fstream for read/write access to
02268 //               the file, if possible.  Returns true if successful,
02269 //               false otherwise.  This requires the setting of the
02270 //               set_text()/set_binary() flags to open the file
02271 //               appropriately as indicated; it is an error to call
02272 //               open_read_write() without first calling one of
02273 //               set_text() or set_binary().
02274 ////////////////////////////////////////////////////////////////////
02275 bool Filename::
02276 open_read_write(pfstream &stream, bool truncate) const {
02277   assert(!get_pattern());
02278   assert(is_text() || is_binary());
02279 
02280   ios_openmode open_mode = ios::out | ios::in;
02281 
02282   if (truncate) {
02283     open_mode |= ios::trunc;
02284   }
02285 
02286   // Since ios::in also seems to imply ios::nocreate (!), we must
02287   // guarantee the file already exists before we try to open it.
02288   if (!exists()) {
02289     touch();
02290   }
02291 
02292 #ifdef HAVE_IOS_BINARY
02293   // For some reason, some systems (like Irix) don't define
02294   // ios::binary.
02295   if (!is_text()) {
02296     open_mode |= ios::binary;
02297   }
02298 #endif
02299 
02300   stream.clear();
02301   string os_specific = to_os_specific();
02302 #ifdef HAVE_OPEN_MASK
02303   stream.open(os_specific.c_str(), open_mode, 0666);
02304 #else
02305   stream.open(os_specific.c_str(), open_mode);
02306 #endif
02307 
02308   return (!stream.fail());
02309 }
02310 #endif  // USE_PANDAFILESTREAM
02311 
02312 #ifdef USE_PANDAFILESTREAM
02313 ////////////////////////////////////////////////////////////////////
02314 //     Function: Filename::open_read_append
02315 //       Access: Published
02316 //  Description: Opens the indicated pfstream for reading and writing
02317 //               the file, if possible; writes are appended to the end
02318 //               of the file.  Returns true if successful, false
02319 //               otherwise.  This requires the setting of the
02320 //               set_text()/set_binary() flags to open the file
02321 //               appropriately as indicated; it is an error to call
02322 //               open_read() without first calling one of set_text()
02323 //               or set_binary().
02324 ////////////////////////////////////////////////////////////////////
02325 bool Filename::
02326 open_read_append(pfstream &stream) const {
02327   assert(!get_pattern());
02328   assert(is_text() || is_binary());
02329 
02330   ios_openmode open_mode = ios::app | ios::in;
02331 
02332 #ifdef HAVE_IOS_BINARY
02333   // For some reason, some systems (like Irix) don't define
02334   // ios::binary.
02335   if (!is_text()) {
02336     open_mode |= ios::binary;
02337   }
02338 #endif
02339 
02340   stream.clear();
02341   string os_specific = to_os_specific();
02342 #ifdef HAVE_OPEN_MASK
02343   stream.open(os_specific.c_str(), open_mode, 0666);
02344 #else
02345   stream.open(os_specific.c_str(), open_mode);
02346 #endif
02347 
02348   return (!stream.fail());
02349 }
02350 #endif  // USE_PANDAFILESTREAM
02351 
02352 ////////////////////////////////////////////////////////////////////
02353 //     Function: Filename::touch
02354 //       Access: Published
02355 //  Description: Updates the modification time of the file to the
02356 //               current time.  If the file does not already exist, it
02357 //               will be created.  Returns true if successful, false
02358 //               if there is an error.
02359 ////////////////////////////////////////////////////////////////////
02360 bool Filename::
02361 touch() const {
02362   assert(!get_pattern());
02363 #ifdef WIN32_VC
02364   // In Windows, we have to use the Windows API to do this reliably.
02365 
02366   // First, guarantee the file exists (and also get its handle).
02367   string os_specific = to_os_specific();
02368   HANDLE fhandle;
02369   fhandle = CreateFile(os_specific.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
02370                        NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
02371   if (fhandle == INVALID_HANDLE_VALUE) {
02372     return false;
02373   }
02374 
02375   // Now update the file time and date.
02376   SYSTEMTIME sysnow;
02377   FILETIME ftnow;
02378   GetSystemTime(&sysnow);
02379   if (!SystemTimeToFileTime(&sysnow, &ftnow)) {
02380     CloseHandle(fhandle);
02381     return false;
02382   }
02383   
02384   if (!SetFileTime(fhandle, NULL, NULL, &ftnow)) {
02385     CloseHandle(fhandle);
02386     return false;
02387   }
02388 
02389   CloseHandle(fhandle);
02390   return true;
02391 
02392 #elif defined(PHAVE_UTIME_H)
02393   // Most Unix systems can do this explicitly.
02394 
02395   string os_specific = to_os_specific();
02396 #ifdef HAVE_CYGWIN
02397   // In the Cygwin case, it seems we need to be sure to use the
02398   // Cygwin-style name; some broken utime() implementation.  That's
02399   // almost the same thing as the original Panda-style name, but not
02400   // exactly, so we first convert the Panda name to a Windows name,
02401   // then convert it back to Cygwin, to ensure we get it exactly right
02402   // by Cygwin rules.
02403   {
02404     char result[4096] = "";
02405     cygwin_conv_to_posix_path(os_specific.c_str(), result);
02406     os_specific = result;
02407   }
02408 #endif  // HAVE_CYGWIN
02409   int result = utime(os_specific.c_str(), NULL);
02410   if (result < 0) {
02411     if (errno == ENOENT) {
02412       // So the file doesn't already exist; create it.
02413       int fd = creat(os_specific.c_str(), 0666);
02414       if (fd < 0) {
02415         perror(os_specific.c_str());
02416         return false;
02417       }
02418       close(fd);
02419       return true;
02420     }
02421     perror(os_specific.c_str());
02422     return false;
02423   }
02424   return true;
02425 #else  // WIN32, PHAVE_UTIME_H
02426   // Other systems may not have an explicit control over the
02427   // modification time.  For these systems, we'll just temporarily
02428   // open the file in append mode, then close it again (it gets closed
02429   // when the pfstream goes out of scope).
02430   pfstream file;
02431   return open_append(file);
02432 #endif  // WIN32, PHAVE_UTIME_H
02433 }
02434 
02435 ////////////////////////////////////////////////////////////////////
02436 //     Function: Filename::chdir
02437 //       Access: Published
02438 //  Description: Changes directory to the specified location.
02439 //               Returns true if successful, false if failure.
02440 ////////////////////////////////////////////////////////////////////
02441 bool Filename::
02442 chdir() const {
02443   string os_specific = to_os_specific();
02444   return (::chdir(os_specific.c_str()) >= 0);
02445 }
02446 
02447 ////////////////////////////////////////////////////////////////////
02448 //     Function: Filename::unlink
02449 //       Access: Published
02450 //  Description: Permanently deletes the file associated with the
02451 //               filename, if possible.  Returns true if successful,
02452 //               false if failure (for instance, because the file did
02453 //               not exist, or because permissions were inadequate).
02454 ////////////////////////////////////////////////////////////////////
02455 bool Filename::
02456 unlink() const {
02457   assert(!get_pattern());
02458   string os_specific = to_os_specific();
02459 #ifdef _WIN32
02460   // Windows can't delete a file if it's read-only.  Weird.
02461   chmod(os_specific.c_str(), 0644);
02462 #endif
02463   return (::unlink(os_specific.c_str()) == 0);
02464 }
02465 
02466 
02467 ////////////////////////////////////////////////////////////////////
02468 //     Function: Filename::rename_to
02469 //       Access: Published
02470 //  Description: Renames the file to the indicated new filename.  If
02471 //               the new filename is in a different directory, this
02472 //               will perform a move.  Returns true if successful,
02473 //               false on failure.
02474 ////////////////////////////////////////////////////////////////////
02475 bool Filename::
02476 rename_to(const Filename &other) const {
02477   assert(!get_pattern());
02478 
02479   if (*this == other) {
02480     // Trivial success.
02481     return true;
02482   }
02483 
02484   string os_specific = to_os_specific();
02485   string other_os_specific = other.to_os_specific();
02486 
02487   if (rename(os_specific.c_str(),
02488              other_os_specific.c_str()) == 0) {
02489     // Successfully renamed.
02490     return true;
02491   }
02492 
02493   // The above might fail if we have tried to move a file to a
02494   // different filesystem.  In this case, copy the file into the same
02495   // directory first, and then rename it.
02496   string dirname = other.get_dirname();
02497   if (dirname.empty()) {
02498     dirname = ".";
02499   }
02500   Filename temp = Filename::temporary(dirname, "");
02501   temp.set_binary();
02502   if (!Filename::binary_filename(*this).copy_to(temp)) {
02503     return false;
02504   }
02505 
02506   string temp_os_specific = temp.to_os_specific();
02507   if (rename(temp_os_specific.c_str(),
02508              other_os_specific.c_str()) == 0) {
02509     // Successfully renamed.
02510     unlink();
02511     return true;
02512   }
02513 
02514   // Try unlinking the target first.
02515   other.unlink();
02516   if (rename(temp_os_specific.c_str(),
02517              other_os_specific.c_str()) == 0) {
02518     // Successfully renamed.
02519     unlink();
02520     return true;
02521   }
02522 
02523   // Failed.
02524   temp.unlink();
02525   return false;
02526 }
02527 
02528 ////////////////////////////////////////////////////////////////////
02529 //     Function: Filename::copy_to
02530 //       Access: Published
02531 //  Description: Copies the file to the indicated new filename, by
02532 //               reading the contents and writing it to the new file.
02533 //               Returns true if successful, false on failure.
02534 ////////////////////////////////////////////////////////////////////
02535 bool Filename::
02536 copy_to(const Filename &other) const {
02537   Filename this_filename = Filename::binary_filename(*this);
02538   pifstream in;
02539   if (!this_filename.open_read(in)) {
02540     return false;
02541   }
02542 
02543   Filename other_filename = Filename::binary_filename(other);
02544   pofstream out;
02545   if (!other_filename.open_write(out)) {
02546     return false;
02547   }
02548         
02549   static const size_t buffer_size = 4096;
02550   char buffer[buffer_size];
02551   
02552   in.read(buffer, buffer_size);
02553   size_t count = in.gcount();
02554   while (count != 0) {
02555     out.write(buffer, count);
02556     if (out.fail()) {
02557       other.unlink();
02558       return false;
02559     }
02560     in.read(buffer, buffer_size);
02561     count = in.gcount();
02562   }
02563 
02564   if (!in.eof()) {
02565     other.unlink();
02566     return false;
02567   }
02568 
02569   return true;
02570 }
02571 
02572 ////////////////////////////////////////////////////////////////////
02573 //     Function: Filename::make_dir
02574 //       Access: Published
02575 //  Description: Creates all the directories in the path to the file
02576 //               specified in the filename, except for the basename
02577 //               itself.  This assumes that the Filename contains the
02578 //               name of a file, not a directory name; it ensures that
02579 //               the directory containing the file exists.
02580 //
02581 //               However, if the filename ends in a slash, it assumes
02582 //               the Filename represents the name of a directory, and
02583 //               creates all the paths.
02584 ////////////////////////////////////////////////////////////////////
02585 bool Filename::
02586 make_dir() const {
02587   assert(!get_pattern());
02588   if (empty()) {
02589     return false;
02590   }
02591   Filename path;
02592   if (_filename[_filename.length() - 1] == '/') {
02593     // The Filename ends in a slash; it represents a directory.
02594     path = (*this);
02595 
02596   } else {
02597     // The Filename does not end in a slash; it represents a file.
02598     path = get_dirname();
02599   }
02600 
02601   if (path.empty()) {
02602     return false;
02603   }
02604   string dirname = path.get_fullpath();
02605 
02606   // First, make sure everything up to the last path is known.  We
02607   // don't care too much if any of these fail; maybe they failed
02608   // because the directory was already there.
02609   size_t slash = dirname.find('/');
02610   while (slash != string::npos) {
02611     Filename component(dirname.substr(0, slash));
02612     string os_specific = component.to_os_specific();
02613 #ifndef WIN32_VC
02614     ::mkdir(os_specific.c_str(), 0777);
02615 #else
02616     ::mkdir(os_specific.c_str());
02617 #endif
02618     slash = dirname.find('/', slash + 1);
02619   }
02620 
02621   // Now make the last one, and check the return value.
02622   Filename component(dirname);
02623   string os_specific = component.to_os_specific();
02624 #ifndef WIN32_VC
02625   int result = ::mkdir(os_specific.c_str(), 0777);
02626 #else
02627   int result = ::mkdir(os_specific.c_str());
02628 #endif
02629 
02630   return (result == 0);
02631 }
02632 
02633 ////////////////////////////////////////////////////////////////////
02634 //     Function: Filename::mkdir
02635 //       Access: Published
02636 //  Description: Creates the directory named by this filename.  Unlike
02637 //               make_dir(), this assumes that the Filename contains
02638 //               the directory name itself.  Also, parent directories
02639 //               are not automatically created; this function fails if
02640 //               any parent directory is missing.
02641 ////////////////////////////////////////////////////////////////////
02642 bool Filename::
02643 mkdir() const {
02644   string os_specific = to_os_specific();
02645 #ifndef WIN32_VC
02646   int result = ::mkdir(os_specific.c_str(), 0777);
02647 #else
02648   int result = ::mkdir(os_specific.c_str());
02649 #endif
02650 
02651   return (result == 0);
02652 }
02653 
02654 ////////////////////////////////////////////////////////////////////
02655 //     Function: Filename::rmdir
02656 //       Access: Published
02657 //  Description: The inverse of mkdir(): this removes the directory
02658 //               named by this Filename, if it is in fact a directory.
02659 ////////////////////////////////////////////////////////////////////
02660 bool Filename::
02661 rmdir() const {
02662   string os_specific = to_os_specific();
02663 
02664   int result = ::rmdir(os_specific.c_str());
02665 
02666 #ifdef WIN32
02667   if (result != 0) {
02668     // Windows may require the directory to be writable before we can
02669     // remove it.
02670     chmod(os_specific.c_str(), 0777);
02671     result = ::rmdir(os_specific.c_str());
02672   }
02673 #endif
02674 
02675   return (result == 0);
02676 }
02677 
02678 ////////////////////////////////////////////////////////////////////
02679 //     Function: Filename::get_hash
02680 //       Access: Published
02681 //  Description: Returns a hash code that attempts to be mostly unique
02682 //               for different Filenames.
02683 ////////////////////////////////////////////////////////////////////
02684 int Filename::
02685 get_hash() const {
02686   static const int primes[] = {
02687       2,    3,    5,    7,   11,   13,   17,   19,   23,   29,
02688      31,   37,   41,   43,   47,   53,   59,   61,   67,   71,
02689      73,   79,   83,   89,   97,  101,  103,  107,  109,  113,
02690     127,  131,  137,  139,  149,  151,  157,  163,  167,  173,
02691     179,  181,  191,  193,  197,  199,  211,  223,  227,  229,
02692     233,  239,  241,  251,  257,  263,  269,  271,  277,  281,
02693     283,  293,  307,  311,  313,  317,  331,  337,  347,  349,
02694     353,  359,  367,  373,  379,  383,  389,  397,  401,  409,
02695     419,  421,  431,  433,  439,  443,  449,  457,  461,  463,
02696     467,  479,  487,  491,  499,  503,  509,  521,  523,  541,
02697     547,  557,  563,  569,  571,  577,  587,  593,  599,  601,
02698     607,  613,  617,  619,  631,  641,  643,  647,  653,  659,
02699     661,  673,  677,  683,  691,  701,  709,  719,  727,  733,
02700     739,  743,  751,  757,  761,  769,  773,  787,  797,  809,
02701     811,  821,  823,  827,  829,  839,  853,  857,  859,  863,
02702     877,  881,  883,  887,  907,  911,  919,  929,  937,  941,
02703     947,  953,  967,  971,  977,  983,  991,  997
02704   };
02705   static const size_t num_primes = sizeof(primes) / sizeof(int);
02706 
02707   int hash = 0;
02708   for (size_t i = 0; i < _filename.size(); ++i) {
02709     hash += (int)_filename[i] * primes[i % num_primes];
02710   }
02711 
02712   return hash;
02713 }
02714 
02715 
02716 ////////////////////////////////////////////////////////////////////
02717 //     Function: Filename::atomic_compare_and_exchange_contents
02718 //       Access: Public
02719 //  Description: Uses native file-locking mechanisms to atomically
02720 //               replace the contents of a (small) file with the
02721 //               specified contents, assuming it hasn't changed since
02722 //               the last time the file was read.
02723 //
02724 //               This is designed to be similar to
02725 //               AtomicAdjust::compare_and_exchange().  The method
02726 //               writes new_contents to the file, completely replacing
02727 //               the original contents; but only if the original
02728 //               contents exactly matched old_contents.  If the file
02729 //               was modified, returns true.  If, however, the
02730 //               original contents of the file did not exactly match
02731 //               old_contents, then the file is not modified, and
02732 //               false is returned.  In either case, orig_contents is
02733 //               filled with the original contents of the file.
02734 //
02735 //               If the file does not exist, it is implicitly created,
02736 //               and its original contents are empty.
02737 //
02738 //               If an I/O error occurs on write, some of the file may
02739 //               or may not have been written, and false is returned.
02740 //
02741 //               Expressed in pseudo-code, the logic is:
02742 //
02743 //                 orig_contents = file.read();
02744 //                 if (orig_contents == old_contents) {
02745 //                   file.write(new_contents);
02746 //                   return true;
02747 //                 }
02748 //                 return false;
02749 //
02750 //               The operation is guaranteed to be atomic only if the
02751 //               only operations that read and write to this file are
02752 //               atomic_compare_and_exchange_contents() and
02753 //               atomic_read_contents().
02754 ////////////////////////////////////////////////////////////////////
02755 bool Filename::
02756 atomic_compare_and_exchange_contents(string &orig_contents, 
02757                                      const string &old_contents, 
02758                                      const string &new_contents) const {
02759 #ifdef WIN32_VC
02760   string os_specific = to_os_specific();
02761   HANDLE hfile = CreateFile(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE, 
02762                             0, NULL, OPEN_ALWAYS,
02763                             FILE_ATTRIBUTE_NORMAL, NULL);
02764   while (hfile == INVALID_HANDLE_VALUE) {
02765     DWORD error = GetLastError();
02766     if (error == ERROR_SHARING_VIOLATION) {
02767       // If the file is locked by another process, yield and try again.
02768       Sleep(0);
02769       hfile = CreateFile(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE, 
02770                          0, NULL, OPEN_ALWAYS,
02771                          FILE_ATTRIBUTE_NORMAL, NULL);
02772     } else {
02773       cerr << "Couldn't open file: " << os_specific 
02774            << ", error " << error << "\n";
02775       return false;
02776     }
02777   }
02778 
02779   if (hfile == INVALID_HANDLE_VALUE) {
02780     cerr << "Couldn't open file: " << os_specific 
02781          << ", error " << GetLastError() << "\n";
02782     return false;
02783   }
02784 
02785   static const size_t buf_size = 512;
02786   char buf[buf_size];
02787 
02788   orig_contents = string();
02789 
02790   DWORD bytes_read;
02791   if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
02792     cerr << "Error reading file: " << os_specific 
02793          << ", error " << GetLastError() << "\n";
02794     CloseHandle(hfile);
02795     return false;
02796   }
02797   while (bytes_read > 0) {
02798     orig_contents += string(buf, bytes_read);
02799 
02800     if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
02801       cerr << "Error reading file: " << os_specific 
02802            << ", error " << GetLastError() << "\n";
02803       CloseHandle(hfile);
02804       return false;
02805     }
02806   }
02807 
02808   bool match = false;
02809   if (orig_contents == old_contents) {
02810     match = true;
02811     SetFilePointer(hfile, 0, 0, FILE_BEGIN);
02812     DWORD bytes_written;
02813     if (!WriteFile(hfile, new_contents.data(), new_contents.size(),
02814                    &bytes_written, NULL)) {
02815       cerr << "Error writing file: " << os_specific 
02816            << ", error " << GetLastError() << "\n";
02817       CloseHandle(hfile);
02818       return false;
02819     }
02820   }
02821 
02822   CloseHandle(hfile);
02823   return match;
02824 
02825 #else  // WIN32_VC
02826   string os_specific = to_os_specific();
02827   int fd = open(os_specific.c_str(), O_RDWR | O_CREAT, 0666);
02828   if (fd < 0) {
02829     perror(os_specific.c_str());
02830     return false;
02831   }
02832 
02833   static const size_t buf_size = 512;
02834   char buf[buf_size];
02835 
02836   orig_contents = string();
02837 
02838   if (lockf(fd, F_LOCK, 0) != 0) {
02839     perror(os_specific.c_str());
02840     close(fd);
02841     return false;
02842   }
02843     
02844   size_t bytes_read = read(fd, buf, buf_size);
02845   while (bytes_read > 0) {
02846     orig_contents += string(buf, bytes_read);
02847     bytes_read = read(fd, buf, buf_size);
02848   }
02849 
02850   if (bytes_read < 0) {
02851     perror(os_specific.c_str());
02852     close(fd);
02853     return false;
02854   }
02855 
02856   bool match = false;
02857   if (orig_contents == old_contents) {
02858     match = true;
02859     lseek(fd, 0, SEEK_SET);
02860     ssize_t bytes_written = write(fd, new_contents.data(), new_contents.size());
02861     if (bytes_written < 0) {
02862       perror(os_specific.c_str());
02863       close(fd);
02864       return false;
02865     }
02866   }
02867 
02868   if (close(fd) < 0) {
02869     perror(os_specific.c_str());
02870     return false;
02871   }
02872   
02873   return match;
02874 #endif  // WIN32_VC
02875 }
02876 
02877 ////////////////////////////////////////////////////////////////////
02878 //     Function: Filename::atomic_read_contents
02879 //       Access: Public
02880 //  Description: Uses native file-locking mechanisms to atomically
02881 //               read the contents of a (small) file.  This is the
02882 //               only way to read a file protected by
02883 //               atomic_compare_and_exchange_contents(), and be
02884 //               confident that the read operation is actually atomic
02885 //               with respect to that method.
02886 //
02887 //               If the file does not exist, it is implicitly created,
02888 //               and its contents are empty.
02889 //
02890 //               If the file is read successfully, fills its contents
02891 //               in the indicated string, and returns true.  If the
02892 //               file cannot be read, returns false.
02893 ////////////////////////////////////////////////////////////////////
02894 bool Filename::
02895 atomic_read_contents(string &contents) const {
02896 #ifdef WIN32_VC
02897   string os_specific = to_os_specific();
02898   HANDLE hfile = CreateFile(os_specific.c_str(), GENERIC_READ, 
02899                             FILE_SHARE_READ, NULL, OPEN_ALWAYS,
02900                             FILE_ATTRIBUTE_NORMAL, NULL);
02901   while (hfile == INVALID_HANDLE_VALUE) {
02902     DWORD error = GetLastError();
02903     if (error == ERROR_SHARING_VIOLATION) {
02904       // If the file is locked by another process, yield and try again.
02905       Sleep(0);
02906       hfile = CreateFile(os_specific.c_str(), GENERIC_READ, 
02907                          FILE_SHARE_READ, NULL, OPEN_ALWAYS,
02908                          FILE_ATTRIBUTE_NORMAL, NULL);      
02909     } else {
02910       cerr << "Couldn't open file: " << os_specific 
02911            << ", error " << error << "\n";
02912       return false;
02913     }
02914   }
02915 
02916   static const size_t buf_size = 512;
02917   char buf[buf_size];
02918 
02919   contents = string();
02920 
02921   DWORD bytes_read;
02922   if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
02923     cerr << "Error reading file: " << os_specific 
02924          << ", error " << GetLastError() << "\n";
02925     CloseHandle(hfile);
02926     return false;
02927   }
02928   while (bytes_read > 0) {
02929     contents += string(buf, bytes_read);
02930 
02931     if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
02932       cerr << "Error reading file: " << os_specific 
02933            << ", error " << GetLastError() << "\n";
02934       CloseHandle(hfile);
02935       return false;
02936     }
02937   }
02938 
02939   CloseHandle(hfile);
02940   return true;
02941 
02942 #else  // WIN32_VC
02943   string os_specific = to_os_specific();
02944   int fd = open(os_specific.c_str(), O_RDWR | O_CREAT, 0666);
02945   if (fd < 0) {
02946     perror(os_specific.c_str());
02947     return false;
02948   }
02949 
02950   static const size_t buf_size = 512;
02951   char buf[buf_size];
02952 
02953   contents = string();
02954 
02955   if (lockf(fd, F_LOCK, 0) != 0) {
02956     perror(os_specific.c_str());
02957     close(fd);
02958     return false;
02959   }
02960     
02961   size_t bytes_read = read(fd, buf, buf_size);
02962   while (bytes_read > 0) {
02963     contents += string(buf, bytes_read);
02964     bytes_read = read(fd, buf, buf_size);
02965   }
02966 
02967   if (bytes_read < 0) {
02968     perror(os_specific.c_str());
02969     close(fd);
02970     return false;
02971   }
02972 
02973   close(fd);
02974   return true;
02975 #endif  // WIN32_VC
02976 }
02977 
02978 ////////////////////////////////////////////////////////////////////
02979 //     Function: Filename::locate_basename
02980 //       Access: Protected
02981 //  Description: After the string has been reassigned, search for the
02982 //               slash marking the beginning of the basename, and set
02983 //               _dirname_end and _basename_start correctly.
02984 ////////////////////////////////////////////////////////////////////
02985 void Filename::
02986 locate_basename() {
02987   // Scan for the last slash, which marks the end of the directory
02988   // part.
02989   if (_filename.empty()) {
02990     _dirname_end = 0;
02991     _basename_start = 0;
02992 
02993   } else {
02994 
02995     string::size_type slash = _filename.rfind('/');
02996     if (slash != string::npos) {
02997       _basename_start = slash + 1;
02998       _dirname_end = _basename_start;
02999 
03000       // One exception: in case there are multiple slashes in a row,
03001       // we want to treat them as a single slash.  The directory
03002       // therefore actually ends at the first of these; back up a bit.
03003       while (_dirname_end > 0 && _filename[_dirname_end-1] == '/') {
03004         _dirname_end--;
03005       }
03006 
03007       // Another exception: if the dirname was nothing but slashes, it
03008       // was the root directory, or / itself.  In this case the dirname
03009       // does include the terminal slash (of course).
03010       if (_dirname_end == 0) {
03011         _dirname_end = 1;
03012       }
03013 
03014     } else {
03015       _dirname_end = 0;
03016       _basename_start = 0;
03017     }
03018   }
03019 
03020   // Now:
03021 
03022   // _dirname_end is the last slash character, or 0 if there are no
03023   // slash characters.
03024 
03025   // _basename_start is the character after the last slash character,
03026   // or 0 if there are no slash characters.
03027 }
03028 
03029 
03030 ////////////////////////////////////////////////////////////////////
03031 //     Function: Filename::locate_extension
03032 //       Access: Protected
03033 //  Description: Once the end of the directory prefix has been found,
03034 //               and _dirname_end and _basename_start are set
03035 //               correctly, search for the dot marking the beginning
03036 //               of the extension, and set _basename_end and
03037 //               _extension_start correctly.
03038 ////////////////////////////////////////////////////////////////////
03039 void Filename::
03040 locate_extension() {
03041   // Now scan for the last dot after that slash.
03042   if (_filename.empty()) {
03043     _basename_end = string::npos;
03044     _extension_start = string::npos;
03045 
03046   } else {
03047     string::size_type dot = _filename.length() - 1;
03048 
03049     while (dot+1 > _basename_start && _filename[dot] != '.') {
03050       --dot;
03051     }
03052 
03053     if (dot+1 > _basename_start) {
03054       _basename_end = dot;
03055       _extension_start = dot + 1;
03056     } else {
03057       _basename_end = string::npos;
03058       _extension_start = string::npos;
03059     }
03060   }
03061 
03062   // Now:
03063 
03064   // _basename_end is the last dot, or npos if there is no dot.
03065 
03066   // _extension_start is the character after the last dot, or npos if
03067   // there is no dot.
03068 }
03069 
03070 ////////////////////////////////////////////////////////////////////
03071 //     Function: Filename::locate_hash
03072 //       Access: Protected
03073 //  Description: Identifies the part of the filename that contains the
03074 //               sequence of hash marks, if any.
03075 ////////////////////////////////////////////////////////////////////
03076 void Filename::
03077 locate_hash() {
03078   if (!get_pattern()) {
03079     // If it's not a pattern-type filename, these are always set to
03080     // the end of the string.
03081     _hash_end = string::npos;
03082     _hash_start = string::npos;
03083 
03084   } else {
03085     // If it is a pattern-type filename, we must search for the hash
03086     // marks, which could be anywhere (but are usually toward the
03087     // end).
03088     _hash_end = _filename.rfind('#');
03089     if (_hash_end == string::npos) {
03090       _hash_end = string::npos;
03091       _hash_start = string::npos;
03092       
03093     } else {
03094       _hash_start = _hash_end;
03095       ++_hash_end;
03096       while (_hash_start > 0 && _filename[_hash_start - 1] == '#') {
03097         --_hash_start;
03098       }
03099     }
03100   }
03101 }
03102 
03103 
03104 ////////////////////////////////////////////////////////////////////
03105 //     Function: Filename::get_common_prefix
03106 //       Access: Protected
03107 //  Description: Returns the length of the longest common initial
03108 //               substring of this string and the other one that ends
03109 //               in a slash.  This is the lowest directory common to
03110 //               both filenames.
03111 ////////////////////////////////////////////////////////////////////
03112 size_t Filename::
03113 get_common_prefix(const string &other) const {
03114   size_t len = 0;
03115 
03116   // First, get the length of the common initial substring.
03117   while (len < length() && len < other.length() &&
03118          _filename[len] == other[len]) {
03119     len++;
03120   }
03121 
03122   // Now insist that it ends in a slash.
03123   while (len > 0 && _filename[len-1] != '/') {
03124     len--;
03125   }
03126 
03127   return len;
03128 }
03129 
03130 ////////////////////////////////////////////////////////////////////
03131 //     Function: Filename::count_slashes
03132 //       Access: Protected, Static
03133 //  Description: Returns the number of non-consecutive slashes in the
03134 //               indicated string, not counting a terminal slash.
03135 ////////////////////////////////////////////////////////////////////
03136 int Filename::
03137 count_slashes(const string &str) {
03138   int count = 0;
03139   string::const_iterator si;
03140   si = str.begin();
03141 
03142   while (si != str.end()) {
03143     if (*si == '/') {
03144       count++;
03145 
03146       // Skip consecutive slashes.
03147       ++si;
03148       while (*si == '/') {
03149         ++si;
03150       }
03151       if (si == str.end()) {
03152         // Oops, that was a terminal slash.  Don't count it.
03153         count--;
03154       }
03155 
03156     } else {
03157       ++si;
03158     }
03159   }
03160 
03161   return count;
03162 }
03163 
03164 
03165 ////////////////////////////////////////////////////////////////////
03166 //     Function: Filename::r_make_canonical
03167 //       Access: Protected
03168 //  Description: The recursive implementation of make_canonical().
03169 ////////////////////////////////////////////////////////////////////
03170 bool Filename::
03171 r_make_canonical(const Filename &cwd) {
03172   if (get_fullpath() == "/") {
03173     // If we reached the root, the whole path doesn't exist.  Report
03174     // failure.
03175     return false;
03176   }
03177 
03178   // First, try to cd to the filename directly.
03179   string os_specific = to_os_specific();
03180 
03181   if (::chdir(os_specific.c_str()) >= 0) {
03182     // That worked, save the full path string.
03183     (*this) = ExecutionEnvironment::get_cwd();
03184 
03185     // And restore the current working directory.
03186     string osdir = cwd.to_os_specific();
03187     if (::chdir(osdir.c_str()) < 0) {
03188       cerr << "Error!  Cannot change back to " << osdir << "\n";
03189     }
03190     return true;
03191   }
03192 
03193   // That didn't work; maybe it's not a directory.  Recursively go to
03194   // the directory above.
03195 
03196   Filename dir(get_dirname());
03197   
03198   if (dir.empty()) {
03199     // No dirname means the file is in this directory.
03200     set_dirname(cwd);
03201     return true;
03202   }
03203   
03204   if (!dir.r_make_canonical(cwd)) {
03205     return false;
03206   }
03207   set_dirname(dir);
03208   return true;
03209 }
03210 
 All Classes Functions Variables Enumerations