Panda3D
|
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