Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

dtool/src/dtoolutil/filename.cxx

Go to the documentation of this file.
00001 // Filename: filename.cxx
00002 // Created by:  drose (18Jan99)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "filename.h"
00020 #include "dSearchPath.h"
00021 #include "executionEnvironment.h"
00022 
00023 #include <stdio.h>  // For rename() and tempnam()
00024 #include <time.h>   // for clock() and time()
00025 #include <sys/stat.h>
00026 #include <algorithm>
00027 
00028 #ifdef HAVE_UTIME_H
00029 #include <utime.h>
00030 
00031 // We assume we have these too.
00032 #include <errno.h>
00033 #include <fcntl.h>
00034 #endif
00035 
00036 #ifdef HAVE_DIRENT_H
00037 #include <dirent.h>
00038 #endif
00039 
00040 // It's true that dtoolbase.h includes this already, but we include
00041 // this again in case we are building this file within ppremake.
00042 #ifdef HAVE_UNISTD_H
00043 #include <unistd.h>
00044 #endif
00045 
00046 
00047 #ifdef WIN32
00048 /* begin Win32-specific code */
00049 
00050 #ifdef WIN32_VC
00051 #include <direct.h>
00052 #include <windows.h>
00053 #endif
00054 
00055 // The MSVC 6.0 Win32 SDK lacks the following definitions, so we define them
00056 // here for compatibility.
00057 #ifndef FILE_ATTRIBUTE_DEVICE
00058 #define FILE_ATTRIBUTE_DEVICE   0x00000040
00059 #endif
00060 
00061 // We might have been linked with the Cygwin dll.  This is ideal if it
00062 // is available, because it allows Panda to access all the Cygwin
00063 // mount definitions if they are in use.  If the Cygwin dll is not
00064 // available, we fall back to our own convention for converting
00065 // pathnames.
00066 #ifdef HAVE_CYGWIN
00067 extern "C" void cygwin_conv_to_win32_path(const char *path, char *win32);
00068 extern "C" void cygwin_conv_to_posix_path(const char *path, char *posix);
00069 #endif
00070 
00071 static string
00072 front_to_back_slash(const string &str) {
00073   string result = str;
00074   string::iterator si;
00075   for (si = result.begin(); si != result.end(); ++si) {
00076     if ((*si) == '/') {
00077       (*si) = '\\';
00078     }
00079   }
00080 
00081   return result;
00082 }
00083 
00084 static string
00085 back_to_front_slash(const string &str) {
00086   string result = str;
00087   string::iterator si;
00088   for (si = result.begin(); si != result.end(); ++si) {
00089     if ((*si) == '\\') {
00090       (*si) = '/';
00091     }
00092   }
00093 
00094   return result;
00095 }
00096 
00097 static const string &
00098 get_panda_root() {
00099   static string panda_root;
00100   static bool got_panda_root = false;
00101 
00102   if (!got_panda_root) {
00103     const char *envvar = getenv("PANDA_ROOT");
00104     if (envvar != (const char *)NULL) {
00105       panda_root = front_to_back_slash(envvar);
00106     }
00107 
00108     if (panda_root.empty() || panda_root[panda_root.length() - 1] != '\\') {
00109       panda_root += '\\';
00110     }
00111 
00112     got_panda_root = true;
00113   }
00114 
00115   return panda_root;
00116 }
00117 
00118 static string
00119 convert_pathname(const string &unix_style_pathname, bool use_backslash) {
00120   if (unix_style_pathname.empty()) {
00121     return string();
00122   }
00123 
00124   // To convert from a Unix-style pathname to a Windows-style
00125   // pathname, we need to change all forward slashes to backslashes.
00126   // We might need to add a prefix as well, since Windows pathnames
00127   // typically begin with a drive letter.
00128 
00129   // By convention, if the top directory name consists of just one
00130   // letter, we treat that as a drive letter and map the rest of the
00131   // filename accordingly.  On the other hand, if the top directory
00132   // name consists of more than one letter, we assume this is a file
00133   // within some predefined tree whose root is given by the
00134   // environment variable "PANDA_ROOT", or if that is not defined,
00135   // "CYGWIN_ROOT" (for backward compatibility).
00136   string windows_pathname;
00137 
00138   if (unix_style_pathname[0] != '/') {
00139     // It doesn't even start from the root, so we don't have to do
00140     // anything fancy--relative pathnames are the same in Windows as
00141     // in Unix, except for the direction of the slashes.
00142     if (use_backslash) {
00143       windows_pathname = front_to_back_slash(unix_style_pathname);
00144     } else {
00145       windows_pathname = unix_style_pathname;
00146     }
00147 
00148   } else if (unix_style_pathname.length() > 3 &&
00149              isalpha(unix_style_pathname[1]) &&
00150              unix_style_pathname[2] == '/') {
00151     // This pathname begins with a slash and a single letter.  That
00152     // must be the drive letter.
00153 
00154     // We have to cast the result of toupper() to (char) to help some
00155     // compilers (e.g. Cygwin's gcc 2.95.3) happy; so that they do not
00156     // confuse this string constructor with one that takes two
00157     // iterators.
00158     if (use_backslash) {
00159       windows_pathname =
00160     string(1, (char)toupper(unix_style_pathname[1])) + ":" +
00161     front_to_back_slash(unix_style_pathname.substr(2));
00162     } else {
00163       windows_pathname =
00164     string(1, (char)toupper(unix_style_pathname[1])) + ":" +
00165     unix_style_pathname.substr(2);
00166     }
00167 
00168   } else {
00169     // It starts with a slash, but the first part is not a single
00170     // letter.
00171 
00172 #ifdef HAVE_CYGWIN
00173     // Use Cygwin to convert it if possible.
00174     char result[4096] = "";
00175     cygwin_conv_to_win32_path(unix_style_pathname.c_str(), result);
00176     if (use_backslash) {
00177       windows_pathname = result;
00178     } else {
00179       windows_pathname = back_to_front_slash(result);
00180     }
00181 #else  // HAVE_CYGWIN
00182     // Without Cygwin, just prefix $PANDA_ROOT.
00183     windows_pathname = get_panda_root();
00184     if (use_backslash) {
00185       windows_pathname += front_to_back_slash(unix_style_pathname.substr(1));
00186     } else {
00187       windows_pathname += unix_style_pathname.substr(1);
00188     }
00189 #endif  // HAVE_CYGWIN
00190   }
00191 
00192   return windows_pathname;
00193 }
00194 
00195 static string
00196 convert_dso_pathname(const string &unix_style_pathname, bool use_backslash) {
00197   // If the extension is .so, change it to .dll.
00198   size_t dot = unix_style_pathname.rfind('.');
00199   if (dot == string::npos ||
00200       unix_style_pathname.find('/', dot) != string::npos) {
00201     // No filename extension.
00202     return convert_pathname(unix_style_pathname, use_backslash);
00203   }
00204   if (unix_style_pathname.substr(dot) != ".so") {
00205     // Some other extension.
00206     return convert_pathname(unix_style_pathname, use_backslash);
00207   }
00208 
00209   string dll_basename = unix_style_pathname.substr(0, dot);
00210 
00211 #ifdef _DEBUG
00212   // If we're building a debug version, all the dso files we link in
00213   // must be named file_d.dll.  This does prohibit us from linking in
00214   // external dso files, generated outside of the Panda build system,
00215   // that don't follow this _d convention.  Maybe we need a separate
00216   // convert_system_dso_pathname() function.
00217 
00218   // We can't simply check to see if the file exists, because this
00219   // might not be a full path to the dso filename--it might be
00220   // somewhere on the LD_LIBRARY_PATH, or on PATH, or any of a number
00221   // of nutty places.
00222 
00223   return convert_pathname(dll_basename + "_d.dll", use_backslash);
00224 #else
00225   return convert_pathname(dll_basename + ".dll", use_backslash);
00226 #endif
00227 }
00228 
00229 static string
00230 convert_executable_pathname(const string &unix_style_pathname, bool use_backslash) {
00231   // If the extension is not .exe, append .exe.
00232   size_t dot = unix_style_pathname.rfind('.');
00233   if (dot == string::npos ||
00234       unix_style_pathname.find('/', dot) != string::npos) {
00235     // No filename extension.
00236     return convert_pathname(unix_style_pathname + ".exe", use_backslash);
00237   }
00238   if (unix_style_pathname.substr(dot) != ".exe") {
00239     // Some other extension.
00240     return convert_pathname(unix_style_pathname + ".exe", use_backslash);
00241   }
00242 
00243   return convert_pathname(unix_style_pathname, use_backslash);
00244 }
00245 #endif //WIN32
00246 
00247 ////////////////////////////////////////////////////////////////////
00248 //     Function: Filename::Constructor
00249 //       Access: Public
00250 //  Description: This constructor composes the filename out of a
00251 //               directory part and a basename part.  It will insert
00252 //               an intervening '/' if necessary.
00253 ////////////////////////////////////////////////////////////////////
00254 Filename::
00255 Filename(const Filename &dirname, const Filename &basename) {
00256   if (dirname.empty()) {
00257     (*this) = basename;
00258   } else {
00259     string dirpath = dirname.get_fullpath();
00260     if (dirpath[dirpath.length() - 1] == '/') {
00261       (*this) = dirpath + basename.get_fullpath();
00262     } else {
00263       (*this) = dirpath + "/" + basename.get_fullpath();
00264     }
00265   }
00266   _flags = 0;
00267 }
00268 
00269 ////////////////////////////////////////////////////////////////////
00270 //     Function: Filename::from_os_specific
00271 //       Access: Public, Static
00272 //  Description: This named constructor returns a Panda-style filename
00273 //               (that is, using forward slashes, and no drive letter)
00274 //               based on the supplied filename string that describes
00275 //               a filename in the local system conventions (for
00276 //               instance, on Windows, it may use backslashes or begin
00277 //               with a drive letter and a colon).
00278 //
00279 //               Use this function to create a Filename from an
00280 //               externally-given filename string.  Use
00281 //               to_os_specific() again later to reconvert it back to
00282 //               the local operating system's conventions.
00283 //
00284 //               This function will do the right thing even if the
00285 //               filename is partially local conventions and partially
00286 //               Panda conventions; e.g. some backslashes and some
00287 //               forward slashes.
00288 ////////////////////////////////////////////////////////////////////
00289 Filename Filename::
00290 from_os_specific(const string &os_specific, Filename::Type type) {
00291 #ifdef WIN32
00292   string result = back_to_front_slash(os_specific);
00293   const string &panda_root = get_panda_root();
00294 
00295   // If the initial prefix is the same as panda_root, remove it.
00296   if (!panda_root.empty() && panda_root.length() < result.length()) {
00297     bool matches = true;
00298     size_t p;
00299     for (p = 0; p < panda_root.length() && matches; p++) {
00300       char c = tolower(panda_root[p]);
00301       if (c == '\\') {
00302         c = '/';
00303       }
00304       matches = (c == tolower(result[p]));
00305     }
00306 
00307     if (matches) {
00308       // The initial prefix matches!  Replace the initial bit with a
00309       // leading slash.
00310       result = result.substr(panda_root.length());
00311       assert(!result.empty());
00312       if (result[0] != '/') {
00313         result = '/' + result;
00314       }
00315       Filename filename(result);
00316       filename.set_type(type);
00317       return filename;
00318     }
00319   }
00320 
00321   // All right, the initial prefix was not under panda_root.  But
00322   // maybe it begins with a drive letter.
00323   if (result.size() >= 3 && isalpha(result[0]) &&
00324       result[1] == ':' && result[2] == '/') {
00325     result[1] = tolower(result[0]);
00326     result[0] = '/';
00327   }
00328 
00329   Filename filename(result);
00330   filename.set_type(type);
00331   return filename;
00332 #else  // WIN32
00333   // Generic Unix-style filenames--no conversion necessary.
00334   Filename filename(os_specific);
00335   filename.set_type(type);
00336   return filename;
00337 #endif  // WIN32
00338 }
00339 
00340 ////////////////////////////////////////////////////////////////////
00341 //     Function: Filename::expand_from
00342 //       Access: Public, Static
00343 //  Description: Returns the same thing as from_os_specific(), but
00344 //               embedded environment variable references
00345 //               (e.g. "$DMODELS/foo.txt") are expanded out.
00346 ////////////////////////////////////////////////////////////////////
00347 Filename Filename::
00348 expand_from(const string &os_specific, Filename::Type type) {
00349   return from_os_specific(ExecutionEnvironment::expand_string(os_specific),
00350                           type);
00351 }
00352 
00353 ////////////////////////////////////////////////////////////////////
00354 //     Function: Filename::temporary
00355 //       Access: Public
00356 //  Description: Generates a temporary filename within the indicated
00357 //               directory, using the indicated prefix.  If the
00358 //               directory is empty, a system-defined directory is
00359 //               chosen instead.
00360 //
00361 //               The generated filename did not exist when the
00362 //               Filename checked, but since it does not specifically
00363 //               create the file, it is possible that another process
00364 //               could simultaneously create a file by the same name.
00365 ////////////////////////////////////////////////////////////////////
00366 Filename Filename::
00367 temporary(const string &dirname, const string &prefix, Type type) {
00368   if (dirname.empty()) {
00369     // If we are not given a dirname, use the system tempnam()
00370     // function to create a system-defined temporary filename.
00371     char *name = tempnam(NULL, prefix.c_str());
00372     Filename result(name);
00373     free(name);
00374     result.set_type(type);
00375     return result;
00376   }
00377 
00378   // If we *are* given a dirname, then use our own algorithm to make
00379   // up a filename within that dirname.  We do that because the system
00380   // tempnam() (for instance, under Windows) may ignore the dirname.
00381 
00382   Filename result(dirname, "");
00383   result.set_type(type);
00384   do {
00385     // We take the time of day and multiply it by the process time.
00386     // This will give us a very large number, of which we take the
00387     // bottom 24 bits and generate a 6-character hex code.
00388     int hash = (clock() * time(NULL)) & 0xffffff;
00389     char hex_code[10];
00390     sprintf(hex_code, "%06x", hash);
00391     result.set_basename(prefix + hex_code);
00392   } while (result.exists());
00393 
00394   return result;
00395 }
00396 
00397 ////////////////////////////////////////////////////////////////////
00398 //     Function: Filename::set_fullpath
00399 //       Access: Public
00400 //  Description: Replaces the entire filename: directory, basename,
00401 //               extension.  This can also be achieved with the
00402 //               assignment operator.
00403 ////////////////////////////////////////////////////////////////////
00404 void Filename::
00405 set_fullpath(const string &s) {
00406   (*this) = s;
00407 }
00408 
00409 ////////////////////////////////////////////////////////////////////
00410 //     Function: Filename::set_dirname
00411 //       Access: Public
00412 //  Description: Replaces the directory part of the filename.  This is
00413 //               everything in the filename up to, but not including
00414 //               the rightmost slash.
00415 ////////////////////////////////////////////////////////////////////
00416 void Filename::
00417 set_dirname(const string &s) {
00418   if (s.empty()) {
00419     // Remove the directory prefix altogether.
00420     _filename.replace(0, _basename_start, "");
00421 
00422     int length_change = - ((int)_basename_start);
00423 
00424     _dirname_end = 0;
00425     _basename_start += length_change;
00426     _basename_end += length_change;
00427     _extension_start += length_change;
00428 
00429   } else {
00430     // Replace the existing directory prefix, or insert a new one.
00431 
00432     // We build the string ss to include the terminal slash.
00433     string ss;
00434     if (s[s.length()-1] == '/') {
00435       ss = s;
00436     } else {
00437       ss = s+'/';
00438     }
00439 
00440     int length_change = ss.length() - _basename_start;
00441 
00442     _filename.replace(0, _basename_start, ss);
00443 
00444     _dirname_end = ss.length() - 1;
00445 
00446     // An exception: if the dirname string was the single slash, the
00447     // dirname includes that slash.
00448     if (ss.length() == 1) {
00449       _dirname_end = 1;
00450     }
00451 
00452     _basename_start += length_change;
00453 
00454     if (_basename_end != string::npos) {
00455       _basename_end += length_change;
00456       _extension_start += length_change;
00457     }
00458   }
00459 }
00460 
00461 ////////////////////////////////////////////////////////////////////
00462 //     Function: Filename::set_basename
00463 //       Access: Public
00464 //  Description: Replaces the basename part of the filename.  This is
00465 //               everything in the filename after the rightmost slash,
00466 //               including any extensions.
00467 ////////////////////////////////////////////////////////////////////
00468 void Filename::
00469 set_basename(const string &s) {
00470   _filename.replace(_basename_start, string::npos, s);
00471   locate_extension();
00472 }
00473 
00474 
00475 ////////////////////////////////////////////////////////////////////
00476 //     Function: Filename::set_fullpath_wo_extension
00477 //       Access: Public
00478 //  Description: Replaces the full filename--directory and basename
00479 //               parts--except for the extension.
00480 ////////////////////////////////////////////////////////////////////
00481 void Filename::
00482 set_fullpath_wo_extension(const string &s) {
00483   int length_change = s.length() - _basename_end;
00484 
00485   _filename.replace(0, _basename_end, s);
00486 
00487   if (_basename_end != string::npos) {
00488     _basename_end += length_change;
00489     _extension_start += length_change;
00490   }
00491 }
00492 
00493 
00494 ////////////////////////////////////////////////////////////////////
00495 //     Function: Filename::set_basename_wo_extension
00496 //       Access: Public
00497 //  Description: Replaces the basename part of the filename, without
00498 //               the file extension.
00499 ////////////////////////////////////////////////////////////////////
00500 void Filename::
00501 set_basename_wo_extension(const string &s) {
00502   int length_change = s.length() - (_basename_end - _basename_start);
00503 
00504   if (_basename_end == string::npos) {
00505     _filename.replace(_basename_start, string::npos, s);
00506 
00507   } else {
00508     _filename.replace(_basename_start, _basename_end - _basename_start, s);
00509 
00510     _basename_end += length_change;
00511     _extension_start += length_change;
00512   }
00513 }
00514 
00515 
00516 ////////////////////////////////////////////////////////////////////
00517 //     Function: Filename::set_extension
00518 //       Access: Public
00519 //  Description: Replaces the file extension.  This is everything after
00520 //               the rightmost dot, if there is one, or the empty
00521 //               string if there is not.
00522 ////////////////////////////////////////////////////////////////////
00523 void Filename::
00524 set_extension(const string &s) {
00525   if (s.empty()) {
00526     // Remove the extension altogether.
00527     if (_basename_end != string::npos) {
00528       _filename.replace(_basename_end, string::npos, "");
00529       _basename_end = string::npos;
00530       _extension_start = string::npos;
00531     }
00532 
00533   } else if (_basename_end == string::npos) {
00534     // Insert an extension where there was none before.
00535     _basename_end = _filename.length();
00536     _extension_start = _filename.length() + 1;
00537     _filename += '.' + s;
00538 
00539   } else {
00540     // Replace an existing extension.
00541     _filename.replace(_extension_start, string::npos, s);
00542   }
00543 }
00544 
00545 ////////////////////////////////////////////////////////////////////
00546 //     Function: Filename::extract_components
00547 //       Access: Public
00548 //  Description: Extracts out the individual directory components of
00549 //               the path into a series of strings.  get_basename()
00550 //               will be the last component stored in the vector.
00551 //               Note that no distinction is made by this method
00552 //               between a leading slash and no leading slash, but you
00553 //               can call is_local() to differentiate the two cases.
00554 ////////////////////////////////////////////////////////////////////
00555 void Filename::
00556 extract_components(vector_string &components) const {
00557   components.clear();
00558 
00559   size_t p = 0;
00560   if (!_filename.empty() && _filename[0] == '/') {
00561     // Skip the leading slash.
00562     p = 1;
00563   }
00564   while (p < _filename.length()) {
00565     size_t q = _filename.find('/', p);
00566     if (q == string::npos) {
00567       components.push_back(_filename.substr(p));
00568       return;
00569     }
00570     components.push_back(_filename.substr(p, q - p));
00571     p = q + 1;
00572   }
00573 
00574   // A trailing slash means we have an empty get_basename().
00575   components.push_back(string());
00576 }
00577 
00578 ////////////////////////////////////////////////////////////////////
00579 //     Function: Filename::standardize
00580 //       Access: Public
00581 //  Description: Converts the filename to standard form by replacing
00582 //               consecutive slashes with a single slash, removing a
00583 //               trailing slash if present, and backing up over ../
00584 //               sequences within the filename where possible.
00585 ////////////////////////////////////////////////////////////////////
00586 void Filename::
00587 standardize() {
00588   assert(!_filename.empty());
00589   if (_filename == ".") {
00590     // Don't change a single dot; this refers to the current directory.
00591     return;
00592   }
00593 
00594   vector<string> components;
00595 
00596   // Pull off the components of the filename one at a time.
00597   bool global = (_filename[0] == '/');
00598 
00599   size_t p = 0;
00600   while (p < _filename.length() && _filename[p] == '/') {
00601     p++;
00602   }
00603   while (p < _filename.length()) {
00604     size_t slash = _filename.find('/', p);
00605     string component = _filename.substr(p, slash - p);
00606     if (component == ".") {
00607       // Ignore /./.
00608     } else if (component == ".." && !components.empty() &&
00609                !(components.back() == "..")) {
00610       // Back up.
00611       components.pop_back();
00612     } else {
00613       components.push_back(component);
00614     }
00615 
00616     p = slash;
00617     while (p < _filename.length() && _filename[p] == '/') {
00618       p++;
00619     }
00620   }
00621 
00622   // Now reassemble the filename.
00623   string result;
00624   if (global) {
00625     result = "/";
00626   }
00627   if (!components.empty()) {
00628     result += components[0];
00629     for (int i = 1; i < (int)components.size(); i++) {
00630       result += "/" + components[i];
00631     }
00632   }
00633 
00634   (*this) = result;
00635 }
00636 
00637 ////////////////////////////////////////////////////////////////////
00638 //     Function: Filename::make_absolute
00639 //       Access: Public
00640 //  Description: Converts the filename to a fully-qualified pathname
00641 //               from the root (if it is a relative pathname), and
00642 //               then standardizes it (see standardize()).
00643 //
00644 //               This is sometimes a little problematic, since it may
00645 //               convert the file to its 'true' absolute pathname,
00646 //               which could be an ugly NFS-named file, irrespective
00647 //               of symbolic links
00648 //               (e.g. /.automount/dimbo/root/usr2/fit/people/drose
00649 //               instead of /fit/people/drose); besides being ugly,
00650 //               filenames like this may not be consistent across
00651 //               multiple different platforms.
00652 ////////////////////////////////////////////////////////////////////
00653 void Filename::
00654 make_absolute() {
00655   make_absolute(ExecutionEnvironment::get_cwd());
00656 }
00657 
00658 ////////////////////////////////////////////////////////////////////
00659 //     Function: Filename::make_absolute
00660 //       Access: Public
00661 //  Description: Converts the filename to a fully-qualified filename
00662 //               from the root (if it is a relative filename), and
00663 //               then standardizes it (see standardize()).  This
00664 //               flavor accepts a specific starting directory that the
00665 //               filename is known to be relative to.
00666 ////////////////////////////////////////////////////////////////////
00667 void Filename::
00668 make_absolute(const Filename &start_directory) {
00669   if (is_local()) {
00670     (*this) = Filename(start_directory, _filename);
00671   }
00672 
00673   standardize();
00674 }
00675 
00676 ////////////////////////////////////////////////////////////////////
00677 //     Function: Filename::make_canonical
00678 //       Access: Public
00679 //  Description: Converts this filename to a canonical name by
00680 //               replacing the directory part with the fully-qualified
00681 //               directory part.  This is done by changing to that
00682 //               directory and calling getcwd().
00683 //
00684 //               This has the effect of (a) converting relative paths
00685 //               to absolute paths (but see make_absolute() if this is
00686 //               the only effect you want), and (b) always resolving a
00687 //               given directory name to the same string, even if
00688 //               different symbolic links are traversed, and (c)
00689 //               changing nice symbolic-link paths like
00690 //               /fit/people/drose to ugly NFS automounter names like
00691 //               /hosts/dimbo/usr2/fit/people/drose.  This can be
00692 //               troubling, but sometimes this is exactly what you
00693 //               want, particularly if you're about to call
00694 //               make_relative_to() between two filenames.
00695 //
00696 //               The return value is true if successful, or false on
00697 //               failure (usually because the directory name does not
00698 //               exist or cannot be chdir'ed into).
00699 ////////////////////////////////////////////////////////////////////
00700 bool Filename::
00701 make_canonical() {
00702   if (empty()) {
00703     // An empty filename is a special case.  This doesn't name
00704     // anything.
00705     return false;
00706   }
00707 
00708   // Temporarily save the current working directory.
00709   Filename cwd = ExecutionEnvironment::get_cwd();
00710 
00711   if (is_directory()) {
00712     // If the filename itself represents a directory and not a
00713     // filename, cd to the named directory, not the one above it.
00714     string dirname = to_os_specific();
00715 
00716     if (chdir(dirname.c_str()) < 0) {
00717       return false;
00718     }
00719     (*this) = ExecutionEnvironment::get_cwd();
00720 
00721   } else {
00722     // Otherwise, if the filename represents a regular file (or
00723     // doesn't even exist), cd to the directory above.
00724     Filename dir(get_dirname());
00725 
00726     if (dir.empty()) {
00727       // No dirname means the file is in this directory.
00728       set_dirname(cwd);
00729       return true;
00730     }
00731 
00732     string dirname = dir.to_os_specific();
00733     if (chdir(dirname.c_str()) < 0) {
00734       return false;
00735     }
00736     set_dirname(ExecutionEnvironment::get_cwd().get_fullpath());
00737   }
00738 
00739   // Now restore the current working directory.
00740   string osdir = cwd.to_os_specific();
00741   if (chdir(osdir.c_str()) < 0) {
00742     cerr << "Error!  Cannot change back to " << cwd << "\n";
00743   }
00744 
00745   return true;
00746 }
00747 
00748 ////////////////////////////////////////////////////////////////////
00749 //     Function: Filename::to_os_specific
00750 //       Access: Public
00751 //  Description: Converts the filename from our generic Unix-like
00752 //               convention (forward slashes starting with the root at
00753 //               '/') to the corresponding filename in the local
00754 //               operating system (slashes in the appropriate
00755 //               direction, starting with the root at C:\, for
00756 //               instance).  Returns the string representing the
00757 //               converted filename, but does not change the Filename
00758 //               itself.
00759 //
00760 //               See also from_os_specific().
00761 ////////////////////////////////////////////////////////////////////
00762 string Filename::
00763 to_os_specific() const {
00764   if (empty()) {
00765     return string();
00766   }
00767   Filename standard(*this);
00768   standard.standardize();
00769 
00770 #ifdef WIN32
00771   switch (get_type()) {
00772   case T_dso:
00773     return convert_dso_pathname(standard.get_fullpath(), true);
00774   case T_executable:
00775     return convert_executable_pathname(standard.get_fullpath(), true);
00776   default:
00777     return convert_pathname(standard.get_fullpath(), true);
00778   }
00779 #else // WIN32
00780   return standard;
00781 #endif // WIN32
00782 }
00783 
00784 ////////////////////////////////////////////////////////////////////
00785 //     Function: Filename::to_os_generic
00786 //       Access: Public
00787 //  Description: This is similar to to_os_specific(), but it is
00788 //               designed to generate a filename that can be
00789 //               understood on as many platforms as possible.  Since
00790 //               Windows can usually understand a
00791 //               forward-slash-delimited filename, this means it does
00792 //               the same thing as to_os_specific(), but it uses
00793 //               forward slashes instead of backslashes.
00794 //
00795 //               This method has a pretty limited use; it should
00796 //               generally be used for writing file references to a
00797 //               file that might be read on any operating system.
00798 ////////////////////////////////////////////////////////////////////
00799 string Filename::
00800 to_os_generic() const {
00801   if (empty()) {
00802     return string();
00803   }
00804   Filename standard(*this);
00805   standard.standardize();
00806 
00807 #ifdef WIN32
00808   switch (get_type()) {
00809   case T_dso:
00810     return convert_dso_pathname(standard.get_fullpath(), false);
00811   case T_executable:
00812     return convert_executable_pathname(standard.get_fullpath(), false);
00813   default:
00814     return convert_pathname(standard.get_fullpath(), false);
00815   }
00816 #else // WIN32
00817   return standard;
00818 #endif // WIN32
00819 }
00820 
00821 ////////////////////////////////////////////////////////////////////
00822 //     Function: Filename::exists
00823 //       Access: Public
00824 //  Description: Returns true if the filename exists on the disk,
00825 //               false otherwise.  If the type is indicated to be
00826 //               executable, this also tests that the file has execute
00827 //               permission.
00828 ////////////////////////////////////////////////////////////////////
00829 bool Filename::
00830 exists() const {
00831   string os_specific = to_os_specific();
00832 
00833 #ifdef WIN32_VC
00834   bool exists = false;
00835 
00836   DWORD results = GetFileAttributes(os_specific.c_str());
00837   if (results != -1) {
00838     exists = true;
00839   }
00840 
00841 #else  // WIN32_VC
00842   struct stat this_buf;
00843   bool exists = false;
00844 
00845   if (stat(os_specific.c_str(), &this_buf) == 0) {
00846     exists = true;
00847   }
00848 #endif
00849 
00850   return exists;
00851 }
00852 
00853 ////////////////////////////////////////////////////////////////////
00854 //     Function: Filename::is_regular_file
00855 //       Access: Public
00856 //  Description: Returns true if the filename exists and is the
00857 //               name of a regular file (i.e. not a directory or
00858 //               device), false otherwise.
00859 ////////////////////////////////////////////////////////////////////
00860 bool Filename::
00861 is_regular_file() const {
00862   string os_specific = to_os_specific();
00863 
00864 #ifdef WIN32_VC
00865   bool isreg = false;
00866 
00867   DWORD results = GetFileAttributes(os_specific.c_str());
00868   if (results != -1) {
00869     isreg = ((results & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0);
00870   }
00871 
00872 #else  // WIN32_VC
00873   struct stat this_buf;
00874   bool isreg = false;
00875 
00876   if (stat(os_specific.c_str(), &this_buf) == 0) {
00877     isreg = S_ISREG(this_buf.st_mode);
00878   }
00879 #endif
00880 
00881   return isreg;
00882 }
00883 
00884 ////////////////////////////////////////////////////////////////////
00885 //     Function: Filename::is_directory
00886 //       Access: Public
00887 //  Description: Returns true if the filename exists and is a
00888 //               directory name, false otherwise.
00889 ////////////////////////////////////////////////////////////////////
00890 bool Filename::
00891 is_directory() const {
00892   string os_specific = to_os_specific();
00893 
00894 #ifdef WIN32_VC
00895   bool isdir = false;
00896 
00897   DWORD results = GetFileAttributes(os_specific.c_str());
00898   if (results != -1) {
00899     isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
00900   }
00901 #else  // WIN32_VC
00902   struct stat this_buf;
00903   bool isdir = false;
00904 
00905   if (stat(os_specific.c_str(), &this_buf) == 0) {
00906     isdir = S_ISDIR(this_buf.st_mode);
00907   }
00908 #endif
00909 
00910   return isdir;
00911 }
00912 
00913 ////////////////////////////////////////////////////////////////////
00914 //     Function: Filename::is_executable
00915 //       Access: Public
00916 //  Description: Returns true if the filename exists and is
00917 //               executable
00918 ////////////////////////////////////////////////////////////////////
00919 bool Filename::
00920 is_executable() const {
00921 #ifdef WIN32_VC
00922   // no access() in windows, but to our advantage executables can only
00923   // end in .exe or .com
00924   string extension = get_extension();
00925   if (extension == "exe" || extension == "com") {
00926     return exists();
00927   }
00928 
00929 #else /* WIN32_VC */
00930   string os_specific = to_os_specific();
00931   if (access(os_specific.c_str(), X_OK) == 0) {
00932     return true;
00933   }
00934 #endif /* WIN32_VC */
00935 
00936   return false;
00937 }
00938 
00939 ////////////////////////////////////////////////////////////////////
00940 //     Function: Filename::compare_timestamps
00941 //       Access: Public
00942 //  Description: Returns a number less than zero if the file named by
00943 //               this object is older than the given file, zero if
00944 //               they have the same timestamp, or greater than zero if
00945 //               this one is newer.
00946 //
00947 //               If this_missing_is_old is true, it indicates that a
00948 //               missing file will be treated as if it were older than
00949 //               any other file; otherwise, a missing file will be
00950 //               treated as if it were newer than any other file.
00951 //               Similarly for other_missing_is_old.
00952 ////////////////////////////////////////////////////////////////////
00953 int Filename::
00954 compare_timestamps(const Filename &other,
00955                    bool this_missing_is_old,
00956                    bool other_missing_is_old) const {
00957   string os_specific = to_os_specific();
00958   string other_os_specific = other.to_os_specific();
00959 
00960 #ifdef WIN32_VC
00961   struct _stat this_buf;
00962   bool this_exists = false;
00963 
00964   if (_stat(os_specific.c_str(), &this_buf) == 0) {
00965     this_exists = true;
00966   }
00967 
00968   struct _stat other_buf;
00969   bool other_exists = false;
00970 
00971   if (_stat(other_os_specific.c_str(), &other_buf) == 0) {
00972     other_exists = true;
00973   }
00974 #else  // WIN32_VC
00975   struct stat this_buf;
00976   bool this_exists = false;
00977 
00978   if (stat(os_specific.c_str(), &this_buf) == 0) {
00979     this_exists = true;
00980   }
00981 
00982   struct stat other_buf;
00983   bool other_exists = false;
00984 
00985   if (stat(other_os_specific.c_str(), &other_buf) == 0) {
00986     other_exists = true;
00987   }
00988 #endif
00989 
00990   if (this_exists && other_exists) {
00991     // Both files exist, return the honest time comparison.
00992     return (int)this_buf.st_mtime - (int)other_buf.st_mtime;
00993 
00994   } else if (!this_exists && !other_exists) {
00995     // Neither file exists.
00996     if (this_missing_is_old == other_missing_is_old) {
00997       // Both files are either "very old" or "very new".
00998       return 0;
00999     }
01000     if (this_missing_is_old) {
01001       // This file is "very old", the other is "very new".
01002       return -1;
01003     } else {
01004       // This file is "very new", the other is "very old".
01005       return 1;
01006     }
01007 
01008   } else if (!this_exists) {
01009     // This file doesn't, the other one does.
01010     return this_missing_is_old ? -1 : 1;
01011 
01012   } else { // !other_exists
01013     assert(!other_exists);
01014 
01015     // This file exists, the other one doesn't.
01016     return other_missing_is_old ? 1 : -1;
01017   }
01018 }
01019 
01020 ////////////////////////////////////////////////////////////////////
01021 //     Function: Filename::resolve_filename
01022 //       Access: Public
01023 //  Description: Searches the given search path for the filename.  If
01024 //               it is found, updates the filename to the full
01025 //               pathname found and returns true; otherwise, returns
01026 //               false.
01027 ////////////////////////////////////////////////////////////////////
01028 bool Filename::
01029 resolve_filename(const DSearchPath &searchpath,
01030                  const string &default_extension) {
01031   string found;
01032 
01033   if (is_local()) {
01034     found = searchpath.find_file(get_fullpath());
01035 
01036     if (found.empty()) {
01037       // We didn't find it with the given extension; can we try the
01038       // default extension?
01039       if (get_extension().empty() && !default_extension.empty()) {
01040         Filename try_ext = *this;
01041         try_ext.set_extension(default_extension);
01042         found = searchpath.find_file(try_ext.get_fullpath());
01043       }
01044     }
01045 
01046   } else {
01047     if (exists()) {
01048       // The full pathname exists.  Return true.
01049       return true;
01050 
01051     } else {
01052       // The full pathname doesn't exist with the given extension;
01053       // does it exist with the default extension?
01054       if (get_extension().empty() && !default_extension.empty()) {
01055         Filename try_ext = *this;
01056         try_ext.set_extension(default_extension);
01057         if (try_ext.exists()) {
01058           found = try_ext;
01059         }
01060       }
01061     }
01062   }
01063 
01064   if (!found.empty()) {
01065     (*this) = found;
01066     return true;
01067   }
01068 
01069   return false;
01070 }
01071 
01072 ////////////////////////////////////////////////////////////////////
01073 //     Function: Filename::make_relative_to
01074 //       Access: Public
01075 //  Description: Adjusts this filename, which must be a
01076 //               fully-specified pathname beginning with a slash, to
01077 //               make it a relative filename, relative to the
01078 //               fully-specified directory indicated (which must also
01079 //               begin with, and may or may not end with, a slash--a
01080 //               terminating slash is ignored).
01081 //
01082 //               This only performs a string comparsion, so it may be
01083 //               wise to call make_canonical() on both filenames
01084 //               before calling make_relative_to().
01085 //
01086 //               If allow_backups is false, the filename will only be
01087 //               adjusted to be made relative if it is already
01088 //               somewhere within or below the indicated directory.
01089 //               If allow_backups is true, it will be adjusted in all
01090 //               cases, even if this requires putting a series of ../
01091 //               characters before the filename--unless it would have
01092 //               to back all the way up to the root.
01093 //
01094 //               Returns true if the file was adjusted, false if it
01095 //               was not.
01096 ////////////////////////////////////////////////////////////////////
01097 bool Filename::
01098 make_relative_to(Filename directory, bool allow_backups) {
01099   if (_filename.empty() || directory.empty() ||
01100       _filename[0] != '/' || directory[0] != '/') {
01101     return false;
01102   }
01103 
01104   standardize();
01105   directory.standardize();
01106 
01107   if (directory == "/") {
01108     // Don't be silly.
01109     return false;
01110   }
01111 
01112   string rel_to_file = directory.get_fullpath() + "/.";
01113 
01114   size_t common = get_common_prefix(rel_to_file);
01115   if (common < 2) {
01116     // Oh, never mind.
01117     return false;
01118   }
01119 
01120   string result;
01121   int slashes = count_slashes(rel_to_file.substr(common));
01122   if (slashes > 0 && !allow_backups) {
01123     // Too bad; the file's not under the indicated directory.
01124     return false;
01125   }
01126 
01127   for (int i = 0; i < slashes; i++) {
01128     result += "../";
01129   }
01130   result += _filename.substr(common);
01131   (*this) = result;
01132 
01133   return true;
01134 }
01135 
01136 ////////////////////////////////////////////////////////////////////
01137 //     Function: Filename::find_on_searchpath
01138 //       Access: Public
01139 //  Description: Performs the reverse of the resolve_filename()
01140 //               operation: assuming that the current filename is
01141 //               fully-specified pathname (i.e. beginning with '/'),
01142 //               look on the indicated search path for a directory
01143 //               under which the file can be found.  When found,
01144 //               adjust the Filename to be relative to the indicated
01145 //               directory name.
01146 //
01147 //               Returns the index of the directory on the searchpath
01148 //               at which the file was found, or -1 if it was not
01149 //               found.
01150 ////////////////////////////////////////////////////////////////////
01151 int Filename::
01152 find_on_searchpath(const DSearchPath &searchpath) {
01153   if (_filename.empty() || _filename[0] != '/') {
01154     return -1;
01155   }
01156 
01157   int num_directories = searchpath.get_num_directories();
01158   for (int i = 0; i < num_directories; i++) {
01159     if (make_relative_to(searchpath.get_directory(i), false)) {
01160       return i;
01161     }
01162   }
01163 
01164   return -1;
01165 }
01166 
01167 ////////////////////////////////////////////////////////////////////
01168 //     Function: Filename::scan_directory
01169 //       Access: Public
01170 //  Description: Attempts to open the named filename as if it were a
01171 //               directory and looks for the non-hidden files within
01172 //               the directory.  Fills the given vector up with the
01173 //               sorted list of filenames that are local to this
01174 //               directory.
01175 //
01176 //               It is the user's responsibility to ensure that the
01177 //               contents vector is empty before making this call;
01178 //               otherwise, the new files will be appended to it.
01179 //
01180 //               Returns true on success, false if the directory could
01181 //               not be read for some reason.
01182 ////////////////////////////////////////////////////////////////////
01183 bool Filename::
01184 scan_directory(vector_string &contents) const {
01185 #if defined(HAVE_DIRENT_H)
01186   size_t orig_size = contents.size();
01187 
01188   string dirname;
01189   if (empty()) {
01190     dirname = ".";
01191   } else {
01192     dirname = _filename;
01193   }
01194   DIR *root = opendir(dirname.c_str());
01195   if (root == (DIR *)NULL) {
01196     return false;
01197   }
01198 
01199   struct dirent *d;
01200   d = readdir(root);
01201   while (d != (struct dirent *)NULL) {
01202     if (d->d_name[0] != '.') {
01203       contents.push_back(d->d_name);
01204     }
01205     d = readdir(root);
01206   }
01207   closedir(root);
01208 
01209   sort(contents.begin() + orig_size, contents.end());
01210   return true;
01211 
01212 #elif defined(WIN32_VC)
01213   // Use FindFirstFile()/FindNextFile() to walk through the list of
01214   // files in a directory.
01215   size_t orig_size = contents.size();
01216 
01217   string match;
01218   if (empty()) {
01219     match = "*.*";
01220   } else {
01221     match = to_os_specific() + "\\*.*";
01222   }
01223   WIN32_FIND_DATA find_data;
01224 
01225   HANDLE handle = FindFirstFile(match.c_str(), &find_data);
01226   if (handle == INVALID_HANDLE_VALUE) {
01227     if (GetLastError() == ERROR_NO_MORE_FILES) {
01228       // No matching files is not an error.
01229       return true;
01230     }
01231     return false;
01232   }
01233 
01234   do {
01235     string filename = find_data.cFileName;
01236     if (filename != "." && filename != "..") {
01237       contents.push_back(filename);
01238     }
01239   } while (FindNextFile(handle, &find_data));
01240 
01241   bool scan_ok = (GetLastError() == ERROR_NO_MORE_FILES);
01242   FindClose(handle);
01243 
01244   sort(contents.begin() + orig_size, contents.end());
01245   return scan_ok;
01246   
01247 #else
01248   // Don't know how to scan directories!
01249   return false;
01250 #endif
01251 }
01252 
01253 ////////////////////////////////////////////////////////////////////
01254 //     Function: Filename::open_read
01255 //       Access: Public
01256 //  Description: Opens the indicated ifstream for reading the file, if
01257 //               possible.  Returns true if successful, false
01258 //               otherwise.  This requires the setting of the
01259 //               set_text()/set_binary() flags to open the file
01260 //               appropriately as indicated; it is an error to call
01261 //               open_read() without first calling one of set_text()
01262 //               or set_binary().
01263 ////////////////////////////////////////////////////////////////////
01264 bool Filename::
01265 open_read(ifstream &stream) const {
01266   assert(is_text() || is_binary());
01267 
01268   ios_openmode open_mode = ios::in;
01269 
01270 #ifdef HAVE_IOS_BINARY
01271   // For some reason, some systems (like Irix) don't define
01272   // ios::binary.
01273   if (!is_text()) {
01274     open_mode |= ios::binary;
01275   }
01276 #endif
01277 
01278   string os_specific = to_os_specific();
01279   stream.clear();
01280   stream.open(os_specific.c_str(), open_mode);
01281   return (!stream.fail());
01282 }
01283 
01284 ////////////////////////////////////////////////////////////////////
01285 //     Function: Filename::open_write
01286 //       Access: Public
01287 //  Description: Opens the indicated ifstream for writing the file, if
01288 //               possible.  Returns true if successful, false
01289 //               otherwise.  This requires the setting of the
01290 //               set_text()/set_binary() flags to open the file
01291 //               appropriately as indicated; it is an error to call
01292 //               open_read() without first calling one of set_text()
01293 //               or set_binary().
01294 //
01295 //               If truncate is true, the file is truncated to zero
01296 //               length upon opening it, if it already exists.
01297 //               Otherwise, the file is kept at its original length.
01298 ////////////////////////////////////////////////////////////////////
01299 bool Filename::
01300 open_write(ofstream &stream, bool truncate) const {
01301   assert(is_text() || is_binary());
01302 
01303   ios_openmode open_mode = ios::out;
01304 
01305   if (truncate) {
01306     open_mode |= ios::trunc;
01307 
01308   } else {
01309     // Some systems insist on having ios::in set to prevent the file
01310     // from being truncated when we open it.  Makes ios::trunc kind of
01311     // pointless, doesn't it?  On the other hand, setting ios::in also
01312     // seems to imply ios::nocreate (!), so we should only set this if
01313     // the file already exists.
01314     if (exists()) {
01315       open_mode |= ios::in;
01316     }
01317   }
01318 
01319 #ifdef HAVE_IOS_BINARY
01320   // For some reason, some systems (like Irix) don't define
01321   // ios::binary.
01322   if (!is_text()) {
01323     open_mode |= ios::binary;
01324   }
01325 #endif
01326 
01327   stream.clear();
01328   string os_specific = to_os_specific();
01329 #ifdef HAVE_OPEN_MASK
01330   stream.open(os_specific.c_str(), open_mode, 0666);
01331 #else
01332   stream.open(os_specific.c_str(), open_mode);
01333 #endif
01334 
01335   return (!stream.fail());
01336 }
01337 
01338 ////////////////////////////////////////////////////////////////////
01339 //     Function: Filename::open_append
01340 //       Access: Public
01341 //  Description: Opens the indicated ifstream for writing the file, if
01342 //               possible.  Returns true if successful, false
01343 //               otherwise.  This requires the setting of the
01344 //               set_text()/set_binary() flags to open the file
01345 //               appropriately as indicated; it is an error to call
01346 //               open_read() without first calling one of set_text()
01347 //               or set_binary().
01348 ////////////////////////////////////////////////////////////////////
01349 bool Filename::
01350 open_append(ofstream &stream) const {
01351   assert(is_text() || is_binary());
01352 
01353   ios_openmode open_mode = ios::app;
01354 
01355 #ifdef HAVE_IOS_BINARY
01356   // For some reason, some systems (like Irix) don't define
01357   // ios::binary.
01358   if (!is_text()) {
01359     open_mode |= ios::binary;
01360   }
01361 #endif
01362 
01363   stream.clear();
01364   string os_specific = to_os_specific();
01365 #ifdef HAVE_OPEN_MASK
01366   stream.open(os_specific.c_str(), open_mode, 0666);
01367 #else
01368   stream.open(os_specific.c_str(), open_mode);
01369 #endif
01370 
01371   return (!stream.fail());
01372 }
01373 
01374 ////////////////////////////////////////////////////////////////////
01375 //     Function: Filename::open_read_write
01376 //       Access: Public
01377 //  Description: Opens the indicated fstream for read/write access to
01378 //               the file, if possible.  Returns true if successful,
01379 //               false otherwise.  This requires the setting of the
01380 //               set_text()/set_binary() flags to open the file
01381 //               appropriately as indicated; it is an error to call
01382 //               open_read() without first calling one of set_text()
01383 //               or set_binary().
01384 ////////////////////////////////////////////////////////////////////
01385 bool Filename::
01386 open_read_write(fstream &stream) const {
01387   assert(is_text() || is_binary());
01388 
01389   ios_openmode open_mode = ios::in | ios::out;
01390 
01391 #ifdef HAVE_IOS_BINARY
01392   // For some reason, some systems (like Irix) don't define
01393   // ios::binary.
01394   if (!is_text()) {
01395     open_mode |= ios::binary;
01396   }
01397 #endif
01398 
01399   stream.clear();
01400   string os_specific = to_os_specific();
01401 #ifdef HAVE_OPEN_MASK
01402   stream.open(os_specific.c_str(), open_mode, 0666);
01403 #else
01404   stream.open(os_specific.c_str(), open_mode);
01405 #endif
01406 
01407   return (!stream.fail());
01408 }
01409 
01410 ////////////////////////////////////////////////////////////////////
01411 //     Function: Filename::touch
01412 //       Access: Public
01413 //  Description: Updates the modification time of the file to the
01414 //               current time.  If the file does not already exist, it
01415 //               will be created.  Returns true if successful, false
01416 //               if there is an error.
01417 ////////////////////////////////////////////////////////////////////
01418 bool Filename::
01419 touch() const {
01420 #ifdef WIN32_VC
01421   // In Windows, we have to use the Windows API to do this reliably.
01422 
01423   // First, guarantee the file exists (and also get its handle).
01424   string os_specific = to_os_specific();
01425   HANDLE fhandle;
01426   fhandle = CreateFile(os_specific.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
01427                        NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
01428   if (fhandle == INVALID_HANDLE_VALUE) {
01429     return false;
01430   }
01431 
01432   // Now update the file time and date.
01433   SYSTEMTIME sysnow;
01434   FILETIME ftnow;
01435   GetSystemTime(&sysnow);
01436   if (!SystemTimeToFileTime(&sysnow, &ftnow)) {
01437     CloseHandle(fhandle);
01438     return false;
01439   }
01440   
01441   if (!SetFileTime(fhandle, NULL, NULL, &ftnow)) {
01442     CloseHandle(fhandle);
01443     return false;
01444   }
01445 
01446   CloseHandle(fhandle);
01447   return true;
01448 
01449 #elif defined(HAVE_UTIME_H)
01450   // Most Unix systems can do this explicitly.
01451 
01452   string os_specific = to_os_specific();
01453 #ifdef HAVE_CYGWIN
01454   // In the Cygwin case, it seems we need to be sure to use the
01455   // Cygwin-style name; some broken utime() implementation.  That's
01456   // almost the same thing as the original Panda-style name, but not
01457   // exactly, so we first convert the Panda name to a Windows name,
01458   // then convert it back to Cygwin, to ensure we get it exactly right
01459   // by Cygwin rules.
01460   {
01461     char result[4096] = "";
01462     cygwin_conv_to_posix_path(os_specific.c_str(), result);
01463     os_specific = result;
01464   }
01465 #endif  // HAVE_CYGWIN
01466   int result = utime(os_specific.c_str(), NULL);
01467   if (result < 0) {
01468     if (errno == ENOENT) {
01469       // So the file doesn't already exist; create it.
01470       int fd = creat(os_specific.c_str(), 0666);
01471       if (fd < 0) {
01472         perror(os_specific.c_str());
01473         return false;
01474       }
01475       close(fd);
01476       return true;
01477     }
01478     perror(os_specific.c_str());
01479     return false;
01480   }
01481   return true;
01482 #else  // WIN32, HAVE_UTIME_H
01483   // Other systems may not have an explicit control over the
01484   // modification time.  For these systems, we'll just temporarily
01485   // open the file in append mode, then close it again (it gets closed
01486   // when the ofstream goes out of scope).
01487   ofstream file;
01488   return open_append(file);
01489 #endif  // WIN32, HAVE_UTIME_H
01490 }
01491 
01492 ////////////////////////////////////////////////////////////////////
01493 //     Function: Filename::unlink
01494 //       Access: Public
01495 //  Description: Permanently deletes the file associated with the
01496 //               filename, if possible.  Returns true if successful,
01497 //               false if failure (for instance, because the file did
01498 //               not exist, or because permissions were inadequate).
01499 ////////////////////////////////////////////////////////////////////
01500 bool Filename::
01501 unlink() const {
01502   string os_specific = to_os_specific();
01503   return (::unlink(os_specific.c_str()) == 0);
01504 }
01505 
01506 
01507 ////////////////////////////////////////////////////////////////////
01508 //     Function: Filename::rename_to
01509 //       Access: Public
01510 //  Description: Renames the file to the indicated new filename.  If
01511 //               the new filename is in a different directory, this
01512 //               will perform a move.  Returns true if successful,
01513 //               false if failure.
01514 ////////////////////////////////////////////////////////////////////
01515 bool Filename::
01516 rename_to(const Filename &other) const {
01517   string os_specific = to_os_specific();
01518   string other_os_specific = other.to_os_specific();
01519   return (rename(os_specific.c_str(),
01520                  other_os_specific.c_str()) == 0);
01521 }
01522 
01523 ////////////////////////////////////////////////////////////////////
01524 //     Function: Filename::make_dir
01525 //       Access: Public
01526 //  Description: Creates all the directories in the path to the file
01527 //               specified in the filename, except for the basename
01528 //               itself.  This assumes that the Filename contains the
01529 //               name of a file, not a directory name; it ensures that
01530 //               the directory containing the file exists.
01531 //
01532 //               However, if the filename ends in a slash, it assumes
01533 //               the Filename represents the name of a directory, and
01534 //               creates all the paths.
01535 ////////////////////////////////////////////////////////////////////
01536 bool Filename::
01537 make_dir() const {
01538   if (empty()) {
01539     return false;
01540   }
01541   Filename path = *this;
01542   path.standardize();
01543   string dirname;
01544   if (_filename[_filename.length() - 1] == '/') {
01545     // The Filename ends in a slash; it represents a directory.
01546     dirname = path.get_fullpath();
01547 
01548   } else {
01549     // The Filename does not end in a slash; it represents a file.
01550     dirname = path.get_dirname();
01551   }
01552 
01553   // First, make sure everything up to the last path is known.  We
01554   // don't care too much if any of these fail; maybe they failed
01555   // because the directory was already there.
01556   size_t slash = dirname.find('/');
01557   while (slash != string::npos) {
01558     Filename component(dirname.substr(0, slash));
01559     string os_specific = component.to_os_specific();
01560 #ifndef WIN32_VC
01561     mkdir(os_specific.c_str(), 0777);
01562 #else
01563     mkdir(os_specific.c_str());
01564 #endif
01565     slash = dirname.find('/', slash + 1);
01566   }
01567 
01568   // Now make the last one, and check the return value.
01569   Filename component(dirname);
01570   string os_specific = component.to_os_specific();
01571 #ifndef WIN32_VC
01572   int result = mkdir(os_specific.c_str(), 0777);
01573 #else
01574   int result = mkdir(os_specific.c_str());
01575 #endif
01576 
01577   return (result == 0);
01578 }
01579 
01580 
01581 ////////////////////////////////////////////////////////////////////
01582 //     Function: Filename::locate_basename
01583 //       Access: Private
01584 //  Description: After the string has been reassigned, search for the
01585 //               slash marking the beginning of the basename, and set
01586 //               _dirname_end and _basename_start correctly.
01587 ////////////////////////////////////////////////////////////////////
01588 void Filename::
01589 locate_basename() {
01590   // Scan for the last slash, which marks the end of the directory
01591   // part.
01592   if (_filename.empty()) {
01593     _dirname_end = 0;
01594     _basename_start = 0;
01595 
01596   } else {
01597 
01598     string::size_type slash = _filename.rfind('/');
01599     if (slash != string::npos) {
01600       _basename_start = slash + 1;
01601       _dirname_end = _basename_start;
01602 
01603       // One exception: in case there are multiple slashes in a row,
01604       // we want to treat them as a single slash.  The directory
01605       // therefore actually ends at the first of these; back up a bit.
01606       while (_dirname_end > 0 && _filename[_dirname_end-1] == '/') {
01607         _dirname_end--;
01608       }
01609 
01610       // Another exception: if the dirname was nothing but slashes, it
01611       // was the root directory, or / itself.  In this case the dirname
01612       // does include the terminal slash (of course).
01613       if (_dirname_end == 0) {
01614         _dirname_end = 1;
01615       }
01616 
01617     } else {
01618       _dirname_end = 0;
01619       _basename_start = 0;
01620     }
01621   }
01622 
01623   // Now:
01624 
01625   // _dirname_end is the last slash character, or 0 if there are no
01626   // slash characters.
01627 
01628   // _basename_start is the character after the last slash character,
01629   // or 0 if there are no slash characters.
01630 }
01631 
01632 
01633 ////////////////////////////////////////////////////////////////////
01634 //     Function: Filename::locate_extension
01635 //       Access: Private
01636 //  Description: Once the end of the directory prefix has been found,
01637 //               and _dirname_end and _basename_start are set
01638 //               correctly, search for the dot marking the beginning
01639 //               of the extension, and set _basename_end and
01640 //               _extension_start correctly.
01641 ////////////////////////////////////////////////////////////////////
01642 void Filename::
01643 locate_extension() {
01644   // Now scan for the last dot after that slash.
01645   if (_filename.empty()) {
01646     _basename_end = string::npos;
01647     _extension_start = string::npos;
01648 
01649   } else {
01650     string::size_type dot = _filename.length() - 1;
01651 
01652     while (dot+1 > _basename_start && _filename[dot] != '.') {
01653       --dot;
01654     }
01655 
01656     if (dot+1 > _basename_start) {
01657       _basename_end = dot;
01658       _extension_start = dot + 1;
01659     } else {
01660       _basename_end = string::npos;
01661       _extension_start = string::npos;
01662     }
01663   }
01664 
01665   // Now:
01666 
01667   // _basename_end is the last dot, or npos if there is no dot.
01668 
01669   // _extension_start is the character after the last dot, or npos if
01670   // there is no dot.
01671 }
01672 
01673 
01674 ////////////////////////////////////////////////////////////////////
01675 //     Function: Filename::get_common_prefix
01676 //       Access: Private
01677 //  Description: Returns the length of the longest common initial
01678 //               substring of this string and the other one that ends
01679 //               in a slash.  This is the lowest directory common to
01680 //               both filenames.
01681 ////////////////////////////////////////////////////////////////////
01682 size_t Filename::
01683 get_common_prefix(const string &other) const {
01684   size_t len = 0;
01685 
01686   // First, get the length of the common initial substring.
01687   while (len < length() && len < other.length() &&
01688          _filename[len] == other[len]) {
01689     len++;
01690   }
01691 
01692   // Now insist that it ends in a slash.
01693   while (len > 0 && _filename[len-1] != '/') {
01694     len--;
01695   }
01696 
01697   return len;
01698 }
01699 
01700 ////////////////////////////////////////////////////////////////////
01701 //     Function: Filename::count_slashes
01702 //       Access: Private, Static
01703 //  Description: Returns the number of non-consecutive slashes in the
01704 //               indicated string, not counting a terminal slash.
01705 ////////////////////////////////////////////////////////////////////
01706 int Filename::
01707 count_slashes(const string &str) {
01708   int count = 0;
01709   string::const_iterator si;
01710   si = str.begin();
01711 
01712   while (si != str.end()) {
01713     if (*si == '/') {
01714       count++;
01715 
01716       // Skip consecutive slashes.
01717       ++si;
01718       while (*si == '/') {
01719         ++si;
01720       }
01721       if (si == str.end()) {
01722         // Oops, that was a terminal slash.  Don't count it.
01723         count--;
01724       }
01725 
01726     } else {
01727       ++si;
01728     }
01729   }
01730 
01731   return count;
01732 }
01733 

Generated on Thu May 1 22:12:59 2003 for DTool by doxygen1.3