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