Panda3D
 All Classes Functions Variables Enumerations
filename.cxx
1 // Filename: filename.cxx
2 // Created by: drose (18Jan99)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "filename.h"
16 #include "filename_assist.h"
17 #include "dSearchPath.h"
18 #include "executionEnvironment.h"
19 #include "vector_string.h"
20 #include "atomicAdjust.h"
21 
22 #include <stdio.h> // For rename() and tempnam()
23 #include <time.h> // for clock() and time()
24 #include <sys/stat.h>
25 #include <algorithm>
26 
27 #ifdef PHAVE_UTIME_H
28 #include <utime.h>
29 
30 // We assume we have these too.
31 #include <errno.h>
32 #include <fcntl.h>
33 #endif
34 
35 #ifdef PHAVE_GLOB_H
36  #include <glob.h>
37  #ifndef GLOB_NOMATCH
38  #define GLOB_NOMATCH -3
39  #endif
40 #endif
41 
42 #ifdef PHAVE_DIRENT_H
43 #include <dirent.h>
44 #endif
45 
46 // It's true that dtoolbase.h includes this already, but we include
47 // this again in case we are building this file within ppremake.
48 #ifdef PHAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 
52 TextEncoder::Encoding Filename::_filesystem_encoding = TextEncoder::E_utf8;
53 
54 TVOLATILE AtomicAdjust::Pointer Filename::_home_directory;
55 TVOLATILE AtomicAdjust::Pointer Filename::_temp_directory;
56 TVOLATILE AtomicAdjust::Pointer Filename::_user_appdata_directory;
57 TVOLATILE AtomicAdjust::Pointer Filename::_common_appdata_directory;
58 TypeHandle Filename::_type_handle;
59 
60 #ifdef ANDROID
61 string Filename::_internal_data_dir;
62 #endif
63 
64 #ifdef WIN32
65 /* begin Win32-specific code */
66 
67 #ifdef WIN32_VC
68 #include <direct.h>
69 #include <windows.h>
70 #include <shlobj.h>
71 #include <io.h>
72 #endif
73 
74 // The MSVC 6.0 Win32 SDK lacks the following definitions, so we define them
75 // here for compatibility.
76 #ifndef FILE_ATTRIBUTE_DEVICE
77 #define FILE_ATTRIBUTE_DEVICE 0x00000040
78 #endif
79 
80 // We might have been linked with the Cygwin dll. This is ideal if it
81 // is available, because it allows Panda to access all the Cygwin
82 // mount definitions if they are in use. If the Cygwin dll is not
83 // available, we fall back to our own convention for converting
84 // pathnames.
85 #ifdef HAVE_CYGWIN
86 extern "C" void cygwin_conv_to_win32_path(const char *path, char *win32);
87 extern "C" void cygwin_conv_to_posix_path(const char *path, char *posix);
88 #endif
89 
90 // Windows uses the convention \\hostname\path\to\file to represent a
91 // pathname to a file on another share. This redefines a pathname to
92 // be something more complicated than a sequence of directory names
93 // separated by slashes. The Unix convention to represent the same
94 // thing is, like everything else, to graft the reference to the
95 // remote hostname into the one global filesystem, with something like
96 // /hosts/hostname/path/to/file. We observe the Unix convention for
97 // internal names used in Panda; this makes operations like
98 // Filename::get_dirname() simpler and more internally consistent.
99 
100 // This string hard-defines the prefix that we use internally to
101 // indicate that the next directory component name should be treated
102 // as a hostname. It might be nice to use a ConfigVariable for this,
103 // except that we haven't defined ConfigVariable by this point (and
104 // indeed we can't, since we need to have a Filename class already
105 // created in order to read the first config file). Windows purists
106 // might be tempted to define this to a double slash so that internal
107 // Panda filenames more closely resemble their Windows counterparts.
108 // That might actually work, but it will cause problems with
109 // Filename::standardize().
110 
111 // We use const char * instead of string to avoid static-init ordering
112 // issues.
113 static const char *hosts_prefix = "/hosts/";
114 static size_t hosts_prefix_length = 7;
115 
116 static string
117 front_to_back_slash(const string &str) {
118  string result = str;
119  string::iterator si;
120  for (si = result.begin(); si != result.end(); ++si) {
121  if ((*si) == '/') {
122  (*si) = '\\';
123  }
124  }
125 
126  return result;
127 }
128 
129 static string
130 back_to_front_slash(const string &str) {
131  string result = str;
132  string::iterator si;
133  for (si = result.begin(); si != result.end(); ++si) {
134  if ((*si) == '\\') {
135  (*si) = '/';
136  }
137  }
138 
139  return result;
140 }
141 
142 static const string &
143 get_panda_root() {
144  static string *panda_root = NULL;
145 
146  if (panda_root == NULL) {
147  panda_root = new string;
148  const char *envvar = getenv("PANDA_ROOT");
149  if (envvar != (const char *)NULL) {
150  (*panda_root) = front_to_back_slash(envvar);
151  }
152 
153  // Ensure the string ends in a backslash. If PANDA_ROOT is empty
154  // or undefined, this function must return a single backslash--not
155  // an empty string--since this prefix is used to replace a leading
156  // slash in Filename::to_os_specific().
157  if ((*panda_root).empty() || (*panda_root)[(*panda_root).length() - 1] != '\\') {
158  (*panda_root) += '\\';
159  }
160  }
161 
162  return (*panda_root);
163 }
164 
165 static string
166 convert_pathname(const string &unix_style_pathname) {
167  if (unix_style_pathname.empty()) {
168  return string();
169  }
170 
171  // To convert from a Unix-style pathname to a Windows-style
172  // pathname, we need to change all forward slashes to backslashes.
173  // We might need to add a prefix as well, since Windows pathnames
174  // typically begin with a drive letter.
175 
176  // By convention, if the top directory name consists of just one
177  // letter, we treat that as a drive letter and map the rest of the
178  // filename accordingly. On the other hand, if the top directory
179  // name consists of more than one letter, we assume this is a file
180  // within some predefined tree whose root is given by the
181  // environment variable "PANDA_ROOT", or if that is not defined,
182  // "CYGWIN_ROOT" (for backward compatibility).
183  string windows_pathname;
184 
185  if (unix_style_pathname[0] != '/') {
186  // It doesn't even start from the root, so we don't have to do
187  // anything fancy--relative pathnames are the same in Windows as
188  // in Unix, except for the direction of the slashes.
189  windows_pathname = front_to_back_slash(unix_style_pathname);
190 
191  } else if (unix_style_pathname.length() >= 2 &&
192  isalpha(unix_style_pathname[1]) &&
193  (unix_style_pathname.length() == 2 || unix_style_pathname[2] == '/')) {
194  // This pathname begins with a slash and a single letter. That
195  // must be the drive letter.
196 
197  string remainder = unix_style_pathname.substr(2);
198  if (remainder.empty()) {
199  // There's a difference between "C:" and "C:/".
200  remainder = "/";
201  }
202  remainder = front_to_back_slash(remainder);
203 
204  // We have to cast the result of toupper() to (char) to help some
205  // compilers (e.g. Cygwin's gcc 2.95.3) happy; so that they do not
206  // confuse this string constructor with one that takes two
207  // iterators.
208  windows_pathname =
209  string(1, (char)toupper(unix_style_pathname[1])) + ":" + remainder;
210 
211  } else if (unix_style_pathname.length() > hosts_prefix_length &&
212  unix_style_pathname.substr(0, hosts_prefix_length) == hosts_prefix) {
213  // A filename like /hosts/fooby gets turned into \\fooby.
214  windows_pathname = "\\\\" + front_to_back_slash(unix_style_pathname.substr(hosts_prefix_length));
215 
216  } else {
217  // It starts with a slash, but the first part is not a single
218  // letter.
219 
220 #ifdef HAVE_CYGWIN
221  // Use Cygwin to convert it if possible.
222  char result[4096] = "";
223  cygwin_conv_to_win32_path(unix_style_pathname.c_str(), result);
224  windows_pathname = result;
225 
226 #else // HAVE_CYGWIN
227  // Without Cygwin, just prefix $PANDA_ROOT.
228  windows_pathname = get_panda_root();
229  windows_pathname += front_to_back_slash(unix_style_pathname.substr(1));
230 
231 #endif // HAVE_CYGWIN
232  }
233 
234  return windows_pathname;
235 }
236 
237 static string
238 convert_dso_pathname(const string &unix_style_pathname) {
239  // If the extension is .so, change it to .dll.
240  size_t dot = unix_style_pathname.rfind('.');
241  if (dot == string::npos ||
242  unix_style_pathname.find('/', dot) != string::npos) {
243  // No filename extension.
244  return convert_pathname(unix_style_pathname);
245  }
246  if (unix_style_pathname.substr(dot) != ".so") {
247  // Some other extension.
248  return convert_pathname(unix_style_pathname);
249  }
250 
251  string dll_basename = unix_style_pathname.substr(0, dot);
252 
253 #ifdef _DEBUG
254  // If we're building a debug version, all the dso files we link in
255  // must be named file_d.dll. This does prohibit us from linking in
256  // external dso files, generated outside of the Panda build system,
257  // that don't follow this _d convention. Maybe we need a separate
258  // convert_system_dso_pathname() function.
259 
260  // We can't simply check to see if the file exists, because this
261  // might not be a full path to the dso filename--it might be
262  // somewhere on the LD_LIBRARY_PATH, or on PATH, or any of a number
263  // of nutty places.
264 
265  return convert_pathname(dll_basename + "_d.dll");
266 #else
267  return convert_pathname(dll_basename + ".dll");
268 #endif
269 }
270 
271 static string
272 convert_executable_pathname(const string &unix_style_pathname) {
273  // If the extension is not .exe, append .exe.
274  size_t dot = unix_style_pathname.rfind('.');
275  if (dot == string::npos ||
276  unix_style_pathname.find('/', dot) != string::npos) {
277  // No filename extension.
278  return convert_pathname(unix_style_pathname + ".exe");
279  }
280  if (unix_style_pathname.substr(dot) != ".exe") {
281  // Some other extension.
282  return convert_pathname(unix_style_pathname + ".exe");
283  }
284 
285  return convert_pathname(unix_style_pathname);
286 }
287 #endif //WIN32
288 
289 ////////////////////////////////////////////////////////////////////
290 // Function: Filename::Constructor
291 // Access: Published
292 // Description: This constructor composes the filename out of a
293 // directory part and a basename part. It will insert
294 // an intervening '/' if necessary.
295 ////////////////////////////////////////////////////////////////////
296 Filename::
297 Filename(const Filename &dirname, const Filename &basename) {
298  if (dirname.empty()) {
299  (*this) = basename;
300  } else {
301  _flags = basename._flags;
302  string dirpath = dirname.get_fullpath();
303  if (dirpath[dirpath.length() - 1] == '/') {
304  (*this) = dirpath + basename.get_fullpath();
305  } else {
306  (*this) = dirpath + "/" + basename.get_fullpath();
307  }
308  }
309 }
310 
311 ////////////////////////////////////////////////////////////////////
312 // Function: Filename::from_os_specific
313 // Access: Published, Static
314 // Description: This named constructor returns a Panda-style filename
315 // (that is, using forward slashes, and no drive letter)
316 // based on the supplied filename string that describes
317 // a filename in the local system conventions (for
318 // instance, on Windows, it may use backslashes or begin
319 // with a drive letter and a colon).
320 //
321 // Use this function to create a Filename from an
322 // externally-given filename string. Use
323 // to_os_specific() again later to reconvert it back to
324 // the local operating system's conventions.
325 //
326 // This function will do the right thing even if the
327 // filename is partially local conventions and partially
328 // Panda conventions; e.g. some backslashes and some
329 // forward slashes.
330 ////////////////////////////////////////////////////////////////////
332 from_os_specific(const string &os_specific, Filename::Type type) {
333 #ifdef WIN32
334  string result = back_to_front_slash(os_specific);
335  const string &panda_root = get_panda_root();
336 
337  // If the initial prefix is the same as panda_root, remove it.
338  if (!panda_root.empty() && panda_root != string("\\") &&
339  panda_root.length() < result.length()) {
340  bool matches = true;
341  size_t p;
342  for (p = 0; p < panda_root.length() && matches; ++p) {
343  char c = tolower(panda_root[p]);
344  if (c == '\\') {
345  c = '/';
346  }
347  matches = (c == tolower(result[p]));
348  }
349 
350  if (matches) {
351  // The initial prefix matches! Replace the initial bit with a
352  // leading slash.
353  result = result.substr(panda_root.length());
354  assert(!result.empty());
355  if (result[0] != '/') {
356  result = '/' + result;
357  }
358  Filename filename(result);
359  filename.set_type(type);
360  return filename;
361  }
362  }
363 
364  // All right, the initial prefix was not under panda_root. But
365  // maybe it begins with a drive letter.
366  if (result.size() >= 3 && isalpha(result[0]) &&
367  result[1] == ':' && result[2] == '/') {
368  result[1] = tolower(result[0]);
369  result[0] = '/';
370 
371  // If there's *just* a slash following the drive letter, go ahead
372  // and trim it.
373  if (result.size() == 3) {
374  result = result.substr(0, 2);
375  }
376 
377  } else if (result.substr(0, 2) == "//") {
378  // If the initial prefix is a double slash, convert it to /hosts/.
379  result = hosts_prefix + result.substr(2);
380  }
381 
382  Filename filename(result);
383  filename.set_type(type);
384  return filename;
385 #else // WIN32
386  // Generic Unix-style filenames--no conversion necessary.
387  Filename filename(os_specific);
388  filename.set_type(type);
389  return filename;
390 #endif // WIN32
391 }
392 
393 ////////////////////////////////////////////////////////////////////
394 // Function: Filename::from_os_specific_w
395 // Access: Published, Static
396 // Description: The wide-string variant of from_os_specific().
397 // Returns a new Filename, converted from an os-specific
398 // wide-character string.
399 ////////////////////////////////////////////////////////////////////
401 from_os_specific_w(const wstring &os_specific, Filename::Type type) {
402  TextEncoder encoder;
404  encoder.set_wtext(os_specific);
405  return from_os_specific(encoder.get_text(), type);
406 }
407 
408 ////////////////////////////////////////////////////////////////////
409 // Function: Filename::expand_from
410 // Access: Published, Static
411 // Description: Returns the same thing as from_os_specific(), but
412 // embedded environment variable references
413 // (e.g. "$DMODELS/foo.txt") are expanded out. It also
414 // automatically elevates the file to its true case if
415 // needed.
416 ////////////////////////////////////////////////////////////////////
418 expand_from(const string &os_specific, Filename::Type type) {
420  type);
421  file.make_true_case();
422  return file;
423 }
424 
425 ////////////////////////////////////////////////////////////////////
426 // Function: Filename::temporary
427 // Access: Published, Static
428 // Description: Generates a temporary filename within the indicated
429 // directory, using the indicated prefix. If the
430 // directory is empty, a system-defined directory is
431 // chosen instead.
432 //
433 // The generated filename did not exist when the
434 // Filename checked, but since it does not specifically
435 // create the file, it is possible that another process
436 // could simultaneously create a file by the same name.
437 ////////////////////////////////////////////////////////////////////
439 temporary(const string &dirname, const string &prefix, const string &suffix,
440  Type type) {
441  Filename fdirname = dirname;
442 #if defined(_WIN32) || defined(ANDROID)
443  // The Windows tempnam() function doesn't do a good job of choosing
444  // a temporary directory. Choose one ourselves.
445  if (fdirname.empty()) {
446  fdirname = Filename::get_temp_directory();
447  }
448 #endif
449 
450  if (fdirname.empty()) {
451  // If we are not given a dirname, use the system tempnam()
452  // function to create a system-defined temporary filename.
453  char *name = tempnam(NULL, prefix.c_str());
454  Filename result = Filename::from_os_specific(name);
455  free(name);
456  result.set_type(type);
457  return result;
458  }
459 
460  // If we *are* given a dirname, then use our own algorithm to make
461  // up a filename within that dirname. We do that because the system
462  // tempnam() (for instance, under Windows) may ignore the dirname.
463 
464  Filename result;
465  do {
466  // We take the time of day and multiply it by the process time.
467  // This will give us a very large number, of which we take the
468  // bottom 24 bits and generate a 6-character hex code.
469  int hash = (clock() * time(NULL)) & 0xffffff;
470  char hex_code[10];
471  sprintf(hex_code, "%06x", hash);
472  result = Filename(fdirname, Filename(prefix + hex_code + suffix));
473  result.set_type(type);
474  } while (result.exists());
475 
476  return result;
477 }
478 
479 ////////////////////////////////////////////////////////////////////
480 // Function: Filename::get_home_directory
481 // Access: Published
482 // Description: Returns a path to the user's home directory, if such
483 // a thing makes sense in the current OS, or to the
484 // nearest equivalent. This may or may not be directly
485 // writable by the application.
486 ////////////////////////////////////////////////////////////////////
487 const Filename &Filename::
489  if (AtomicAdjust::get_ptr(_home_directory) == NULL) {
490  Filename home_directory;
491 
492  // In all environments, check $HOME first.
493  char *home = getenv("HOME");
494  if (home != (char *)NULL) {
495  Filename dirname = from_os_specific(home);
496  if (dirname.is_directory()) {
497  if (dirname.make_canonical()) {
498  home_directory = dirname;
499  }
500  }
501  }
502 
503  if (home_directory.empty()) {
504 #ifdef WIN32
505  wchar_t buffer[MAX_PATH];
506 
507  // On Windows, fall back to the "My Documents" folder.
508  if (SHGetSpecialFolderPathW(NULL, buffer, CSIDL_PERSONAL, true)) {
509  Filename dirname = from_os_specific_w(buffer);
510  if (dirname.is_directory()) {
511  if (dirname.make_canonical()) {
512  home_directory = dirname;
513  }
514  }
515  }
516 
517 #elif defined(ANDROID)
518  // Temporary hack.
519  home_directory = "/data/data/org.panda3d.sdk";
520 
521 #elif defined(IS_OSX)
522  home_directory = get_osx_home_directory();
523 
524 #elif defined(ANDROID)
525  home_directory = _internal_data_dir;
526 
527 #else
528  // Posix case: check /etc/passwd?
529 
530 #endif // WIN32
531  }
532 
533  if (home_directory.empty()) {
534  // Fallback case.
535  home_directory = ExecutionEnvironment::get_cwd();
536  }
537 
538  Filename *newdir = new Filename(home_directory);
539  if (AtomicAdjust::compare_and_exchange_ptr(_home_directory, NULL, newdir) != NULL) {
540  // Didn't store it. Must have been stored by someone else.
541  assert(_home_directory != NULL);
542  delete newdir;
543  }
544  }
545 
546  return (*(Filename *)_home_directory);
547 }
548 
549 ////////////////////////////////////////////////////////////////////
550 // Function: Filename::get_temp_directory
551 // Access: Published
552 // Description: Returns a path to a system-defined temporary
553 // directory.
554 ////////////////////////////////////////////////////////////////////
555 const Filename &Filename::
557  if (AtomicAdjust::get_ptr(_temp_directory) == NULL) {
558  Filename temp_directory;
559 
560 #ifdef WIN32
561  static const size_t buffer_size = 4096;
562  wchar_t buffer[buffer_size];
563  if (GetTempPathW(buffer_size, buffer) != 0) {
564  Filename dirname = from_os_specific_w(buffer);
565  if (dirname.is_directory()) {
566  if (dirname.make_canonical()) {
567  temp_directory = dirname;
568  }
569  }
570  }
571 
572 #elif defined(IS_OSX)
573  temp_directory = get_osx_temp_directory();
574 
575 #elif defined(ANDROID)
576  temp_directory.set_dirname(_internal_data_dir);
577  temp_directory.set_basename("cache");
578 
579 #else
580  // Posix case.
581  temp_directory = "/tmp";
582 #endif // WIN32
583 
584  if (temp_directory.empty()) {
585  // Fallback case.
586  temp_directory = ExecutionEnvironment::get_cwd();
587  }
588 
589  Filename *newdir = new Filename(temp_directory);
590  if (AtomicAdjust::compare_and_exchange_ptr(_temp_directory, NULL, newdir) != NULL) {
591  // Didn't store it. Must have been stored by someone else.
592  assert(_temp_directory != NULL);
593  delete newdir;
594  }
595  }
596 
597  return (*(Filename *)_temp_directory);
598 }
599 
600 ////////////////////////////////////////////////////////////////////
601 // Function: Filename::get_user_appdata_directory
602 // Access: Published
603 // Description: Returns a path to a system-defined directory
604 // appropriate for creating a subdirectory for storing
605 // application-specific data, specific to the current
606 // user.
607 ////////////////////////////////////////////////////////////////////
608 const Filename &Filename::
610  if (AtomicAdjust::get_ptr(_user_appdata_directory) == NULL) {
611  Filename user_appdata_directory;
612 
613 #ifdef WIN32
614  wchar_t buffer[MAX_PATH];
615 
616  if (SHGetSpecialFolderPathW(NULL, buffer, CSIDL_LOCAL_APPDATA, true)) {
617  Filename dirname = from_os_specific_w(buffer);
618  if (dirname.is_directory()) {
619  if (dirname.make_canonical()) {
620  user_appdata_directory = dirname;
621  }
622  }
623  }
624 
625 #elif defined(IS_OSX)
626  user_appdata_directory = get_osx_user_appdata_directory();
627 
628 #elif defined(ANDROID)
629  user_appdata_directory.set_dirname(_internal_data_dir);
630  user_appdata_directory.set_basename("files");
631 
632 #else
633  // Posix case.
634  user_appdata_directory = get_home_directory();
635 
636 #endif // WIN32
637 
638  if (user_appdata_directory.empty()) {
639  // Fallback case.
640  user_appdata_directory = ExecutionEnvironment::get_cwd();
641  }
642 
643  Filename *newdir = new Filename(user_appdata_directory);
644  if (AtomicAdjust::compare_and_exchange_ptr(_user_appdata_directory, NULL, newdir) != NULL) {
645  // Didn't store it. Must have been stored by someone else.
646  assert(_user_appdata_directory != NULL);
647  delete newdir;
648  }
649  }
650 
651  return (*(Filename *)_user_appdata_directory);
652 }
653 
654 ////////////////////////////////////////////////////////////////////
655 // Function: Filename::get_common_appdata_directory
656 // Access: Published
657 // Description: Returns a path to a system-defined directory
658 // appropriate for creating a subdirectory for storing
659 // application-specific data, common to all users.
660 ////////////////////////////////////////////////////////////////////
661 const Filename &Filename::
663  if (AtomicAdjust::get_ptr(_common_appdata_directory) == NULL) {
664  Filename common_appdata_directory;
665 
666 #ifdef WIN32
667  wchar_t buffer[MAX_PATH];
668 
669  if (SHGetSpecialFolderPathW(NULL, buffer, CSIDL_COMMON_APPDATA, true)) {
670  Filename dirname = from_os_specific_w(buffer);
671  if (dirname.is_directory()) {
672  if (dirname.make_canonical()) {
673  common_appdata_directory = dirname;
674  }
675  }
676  }
677 
678 #elif defined(IS_OSX)
679  common_appdata_directory = get_osx_common_appdata_directory();
680 
681 #elif defined(ANDROID)
682  common_appdata_directory.set_dirname(_internal_data_dir);
683  common_appdata_directory.set_basename("files");
684 
685 #else
686  // Posix case.
687  common_appdata_directory = "/var";
688 #endif // WIN32
689 
690  if (common_appdata_directory.empty()) {
691  // Fallback case.
692  common_appdata_directory = ExecutionEnvironment::get_cwd();
693  }
694 
695  Filename *newdir = new Filename(common_appdata_directory);
696  if (AtomicAdjust::compare_and_exchange_ptr(_common_appdata_directory, NULL, newdir) != NULL) {
697  // Didn't store it. Must have been stored by someone else.
698  assert(_common_appdata_directory != NULL);
699  delete newdir;
700  }
701  }
702 
703  return (*(Filename *)_common_appdata_directory);
704 }
705 
706 ////////////////////////////////////////////////////////////////////
707 // Function: Filename::set_fullpath
708 // Access: Published
709 // Description: Replaces the entire filename: directory, basename,
710 // extension. This can also be achieved with the
711 // assignment operator.
712 ////////////////////////////////////////////////////////////////////
713 void Filename::
714 set_fullpath(const string &s) {
715  (*this) = s;
716 }
717 
718 ////////////////////////////////////////////////////////////////////
719 // Function: Filename::set_dirname
720 // Access: Published
721 // Description: Replaces the directory part of the filename. This is
722 // everything in the filename up to, but not including
723 // the rightmost slash.
724 ////////////////////////////////////////////////////////////////////
725 void Filename::
726 set_dirname(const string &s) {
727  if (s.empty()) {
728  // Remove the directory prefix altogether.
729  _filename.replace(0, _basename_start, "");
730 
731  int length_change = - ((int)_basename_start);
732 
733  _dirname_end = 0;
734  _basename_start += length_change;
735  _basename_end += length_change;
736  _extension_start += length_change;
737 
738  } else {
739  // Replace the existing directory prefix, or insert a new one.
740 
741  // We build the string ss to include the terminal slash.
742  string ss;
743  if (s[s.length()-1] == '/') {
744  ss = s;
745  } else {
746  ss = s+'/';
747  }
748 
749  int length_change = ss.length() - _basename_start;
750 
751  _filename.replace(0, _basename_start, ss);
752 
753  _dirname_end = ss.length() - 1;
754 
755  // An exception: if the dirname string was the single slash, the
756  // dirname includes that slash.
757  if (ss.length() == 1) {
758  _dirname_end = 1;
759  }
760 
761  _basename_start += length_change;
762 
763  if (_basename_end != string::npos) {
764  _basename_end += length_change;
765  _extension_start += length_change;
766  }
767  }
768  locate_hash();
769 }
770 
771 ////////////////////////////////////////////////////////////////////
772 // Function: Filename::set_basename
773 // Access: Published
774 // Description: Replaces the basename part of the filename. This is
775 // everything in the filename after the rightmost slash,
776 // including any extensions.
777 ////////////////////////////////////////////////////////////////////
778 void Filename::
779 set_basename(const string &s) {
780  _filename.replace(_basename_start, string::npos, s);
781  locate_extension();
782  locate_hash();
783 }
784 
785 
786 ////////////////////////////////////////////////////////////////////
787 // Function: Filename::set_fullpath_wo_extension
788 // Access: Published
789 // Description: Replaces the full filename--directory and basename
790 // parts--except for the extension.
791 ////////////////////////////////////////////////////////////////////
792 void Filename::
793 set_fullpath_wo_extension(const string &s) {
794  int length_change = s.length() - _basename_end;
795 
796  _filename.replace(0, _basename_end, s);
797 
798  if (_basename_end != string::npos) {
799  _basename_end += length_change;
800  _extension_start += length_change;
801  }
802  locate_hash();
803 }
804 
805 
806 ////////////////////////////////////////////////////////////////////
807 // Function: Filename::set_basename_wo_extension
808 // Access: Published
809 // Description: Replaces the basename part of the filename, without
810 // the file extension.
811 ////////////////////////////////////////////////////////////////////
812 void Filename::
813 set_basename_wo_extension(const string &s) {
814  int length_change = s.length() - (_basename_end - _basename_start);
815 
816  if (_basename_end == string::npos) {
817  _filename.replace(_basename_start, string::npos, s);
818 
819  } else {
820  _filename.replace(_basename_start, _basename_end - _basename_start, s);
821 
822  _basename_end += length_change;
823  _extension_start += length_change;
824  }
825  locate_hash();
826 }
827 
828 
829 ////////////////////////////////////////////////////////////////////
830 // Function: Filename::set_extension
831 // Access: Published
832 // Description: Replaces the file extension. This is everything after
833 // the rightmost dot, if there is one, or the empty
834 // string if there is not.
835 ////////////////////////////////////////////////////////////////////
836 void Filename::
837 set_extension(const string &s) {
838  if (s.empty()) {
839  // Remove the extension altogether.
840  if (_basename_end != string::npos) {
841  _filename.replace(_basename_end, string::npos, "");
842  _basename_end = string::npos;
843  _extension_start = string::npos;
844  }
845 
846  } else if (_basename_end == string::npos) {
847  // Insert an extension where there was none before.
848  _basename_end = _filename.length();
849  _extension_start = _filename.length() + 1;
850  _filename += '.' + s;
851 
852  } else {
853  // Replace an existing extension.
854  _filename.replace(_extension_start, string::npos, s);
855  }
856  locate_hash();
857 }
858 
859 ////////////////////////////////////////////////////////////////////
860 // Function: Filename::get_filename_index
861 // Access: Published
862 // Description: If the pattern flag is set for this Filename and the
863 // filename string actually includes a sequence of hash
864 // marks, then this returns a new Filename with the
865 // sequence of hash marks replaced by the indicated
866 // index number.
867 //
868 // If the pattern flag is not set for this Filename or
869 // it does not contain a sequence of hash marks, this
870 // quietly returns the original filename.
871 ////////////////////////////////////////////////////////////////////
873 get_filename_index(int index) const {
874  Filename file(*this);
875 
876  if (_hash_end != _hash_start) {
877  ostringstream strm;
878  strm << _filename.substr(0, _hash_start)
879  << setw(_hash_end - _hash_start) << setfill('0') << index
880  << _filename.substr(_hash_end);
881  file.set_fullpath(strm.str());
882  }
883  file.set_pattern(false);
884 
885  return file;
886 }
887 
888 ////////////////////////////////////////////////////////////////////
889 // Function: Filename::set_hash_to_end
890 // Access: Published
891 // Description: Replaces the part of the filename from the beginning
892 // of the hash sequence to the end of the filename.
893 ////////////////////////////////////////////////////////////////////
894 void Filename::
895 set_hash_to_end(const string &s) {
896  _filename.replace(_hash_start, string::npos, s);
897 
898  locate_basename();
899  locate_extension();
900  locate_hash();
901 }
902 
903 ////////////////////////////////////////////////////////////////////
904 // Function: Filename::extract_components
905 // Access: Published
906 // Description: Extracts out the individual directory components of
907 // the path into a series of strings. get_basename()
908 // will be the last component stored in the vector.
909 // Note that no distinction is made by this method
910 // between a leading slash and no leading slash, but you
911 // can call is_local() to differentiate the two cases.
912 ////////////////////////////////////////////////////////////////////
913 void Filename::
914 extract_components(vector_string &components) const {
915  components.clear();
916 
917  size_t p = 0;
918  if (!_filename.empty() && _filename[0] == '/') {
919  // Skip the leading slash.
920  p = 1;
921  }
922  while (p < _filename.length()) {
923  size_t q = _filename.find('/', p);
924  if (q == string::npos) {
925  components.push_back(_filename.substr(p));
926  return;
927  }
928  components.push_back(_filename.substr(p, q - p));
929  p = q + 1;
930  }
931 
932  // A trailing slash means we have an empty get_basename().
933  components.push_back(string());
934 }
935 
936 ////////////////////////////////////////////////////////////////////
937 // Function: Filename::standardize
938 // Access: Published
939 // Description: Converts the filename to standard form by replacing
940 // consecutive slashes with a single slash, removing a
941 // trailing slash if present, and backing up over ../
942 // sequences within the filename where possible.
943 ////////////////////////////////////////////////////////////////////
944 void Filename::
946  assert(!_filename.empty());
947  if (_filename == ".") {
948  // Don't change a single dot; this refers to the current directory.
949  return;
950  }
951 
952  vector_string components;
953 
954  // Pull off the components of the filename one at a time.
955  bool global = (_filename[0] == '/');
956 
957  size_t p = 0;
958  while (p < _filename.length() && _filename[p] == '/') {
959  p++;
960  }
961  while (p < _filename.length()) {
962  size_t slash = _filename.find('/', p);
963  string component = _filename.substr(p, slash - p);
964  if (component == "." && p != 0) {
965  // Ignore /./.
966  } else if (component == ".." && !components.empty() &&
967  !(components.back() == "..")) {
968  if (components.back() == ".") {
969  // To "back up" over a leading ./ means simply to remove the
970  // leading ./
971  components.pop_back();
972  components.push_back(component);
973  } else {
974  // Back up normally.
975  components.pop_back();
976  }
977  } else {
978  components.push_back(component);
979  }
980 
981  p = slash;
982  while (p < _filename.length() && _filename[p] == '/') {
983  p++;
984  }
985  }
986 
987  // Now reassemble the filename.
988  string result;
989  if (global) {
990  result = "/";
991  }
992  if (!components.empty()) {
993  result += components[0];
994  for (int i = 1; i < (int)components.size(); i++) {
995  result += "/" + components[i];
996  }
997  }
998 
999  (*this) = result;
1000 }
1001 
1002 ////////////////////////////////////////////////////////////////////
1003 // Function: Filename::make_absolute
1004 // Access: Published
1005 // Description: Converts the filename to a fully-qualified pathname
1006 // from the root (if it is a relative pathname), and
1007 // then standardizes it (see standardize()).
1008 //
1009 // This is sometimes a little problematic, since it may
1010 // convert the file to its 'true' absolute pathname,
1011 // which could be an ugly NFS-named file, irrespective
1012 // of symbolic links
1013 // (e.g. /.automount/dimbo/root/usr2/fit/people/drose
1014 // instead of /fit/people/drose); besides being ugly,
1015 // filenames like this may not be consistent across
1016 // multiple different platforms.
1017 ////////////////////////////////////////////////////////////////////
1018 void Filename::
1020  if (is_local()) {
1022  } else {
1023  standardize();
1024  }
1025 }
1026 
1027 ////////////////////////////////////////////////////////////////////
1028 // Function: Filename::make_absolute
1029 // Access: Published
1030 // Description: Converts the filename to a fully-qualified filename
1031 // from the root (if it is a relative filename), and
1032 // then standardizes it (see standardize()). This
1033 // flavor accepts a specific starting directory that the
1034 // filename is known to be relative to.
1035 ////////////////////////////////////////////////////////////////////
1036 void Filename::
1037 make_absolute(const Filename &start_directory) {
1038  if (is_local()) {
1039  Filename new_filename(start_directory, _filename);
1040  new_filename._flags = _flags;
1041  (*this) = new_filename;
1042  }
1043 
1044  standardize();
1045 }
1046 
1047 ////////////////////////////////////////////////////////////////////
1048 // Function: Filename::make_canonical
1049 // Access: Published
1050 // Description: Converts this filename to a canonical name by
1051 // replacing the directory part with the fully-qualified
1052 // directory part. This is done by changing to that
1053 // directory and calling getcwd().
1054 //
1055 // This has the effect of (a) converting relative paths
1056 // to absolute paths (but see make_absolute() if this is
1057 // the only effect you want), and (b) always resolving a
1058 // given directory name to the same string, even if
1059 // different symbolic links are traversed, and (c)
1060 // changing nice symbolic-link paths like
1061 // /fit/people/drose to ugly NFS automounter names like
1062 // /hosts/dimbo/usr2/fit/people/drose. This can be
1063 // troubling, but sometimes this is exactly what you
1064 // want, particularly if you're about to call
1065 // make_relative_to() between two filenames.
1066 //
1067 // The return value is true if successful, or false on
1068 // failure (usually because the directory name does not
1069 // exist or cannot be chdir'ed into).
1070 ////////////////////////////////////////////////////////////////////
1071 bool Filename::
1073  if (empty()) {
1074  // An empty filename is a special case. This doesn't name
1075  // anything.
1076  return false;
1077  }
1078 
1079  if (get_fullpath() == "/") {
1080  // The root directory is a special case.
1081  return true;
1082  }
1083 
1084 #ifndef WIN32
1085  // Use realpath in order to resolve symlinks properly
1086  char newpath [PATH_MAX + 1];
1087  if (realpath(c_str(), newpath) != NULL) {
1088  Filename newpath_fn(newpath);
1089  newpath_fn._flags = _flags;
1090  (*this) = newpath_fn;
1091  }
1092 #endif
1093 
1095  if (!r_make_canonical(cwd)) {
1096  return false;
1097  }
1098 
1099  return make_true_case();
1100 }
1101 
1102 ////////////////////////////////////////////////////////////////////
1103 // Function: Filename::make_true_case
1104 // Access: Published
1105 // Description: On a case-insensitive operating system
1106 // (e.g. Windows), this method looks up the file in the
1107 // file system and resets the Filename to represent the
1108 // actual case of the file as it exists on the disk.
1109 // The return value is true if the file exists and the
1110 // conversion can be made, or false if there is some
1111 // error.
1112 //
1113 // On a case-sensitive operating system, this method
1114 // does nothing and always returns true.
1115 //
1116 // An empty filename is considered to exist in this
1117 // case.
1118 ////////////////////////////////////////////////////////////////////
1119 bool Filename::
1121  assert(!get_pattern());
1122 
1123  if (empty()) {
1124  return true;
1125  }
1126 
1127 #ifdef WIN32
1128  wstring os_specific = to_os_specific_w();
1129 
1130  // First, we have to convert it to its short name, then back to its
1131  // long name--that seems to be the trick to force Windows to throw
1132  // away the case we give it and get the actual file case.
1133 
1134  wchar_t short_name[MAX_PATH + 1];
1135  DWORD l = GetShortPathNameW(os_specific.c_str(), short_name, MAX_PATH + 1);
1136  if (l == 0) {
1137  // Couldn't query the path name for some reason. Probably the
1138  // file didn't exist.
1139  return false;
1140  }
1141  // According to the Windows docs, l will return a value greater than
1142  // the specified length if the short_name length wasn't enough--but also
1143  // according to the Windows docs, MAX_PATH will always be enough.
1144  assert(l < MAX_PATH + 1);
1145 
1146  wchar_t long_name[MAX_PATH + 1];
1147  l = GetLongPathNameW(short_name, long_name, MAX_PATH + 1);
1148  if (l == 0) {
1149  // Couldn't query the path name for some reason. Probably the
1150  // file didn't exist.
1151  return false;
1152  }
1153  assert(l < MAX_PATH + 1);
1154 
1155  Filename true_case = Filename::from_os_specific_w(long_name);
1156 
1157  // Now sanity-check the true-case filename. If it's not the same as
1158  // the source file, except for case, reject it.
1159  wstring orig_filename = get_fullpath_w();
1160  wstring new_filename = true_case.get_fullpath_w();
1161  bool match = (orig_filename.length() == new_filename.length());
1162  for (size_t i = 0; i < orig_filename.length() && match; ++i) {
1163  match = (TextEncoder::unicode_tolower(orig_filename[i]) == TextEncoder::unicode_tolower(new_filename[i]));
1164  }
1165  if (!match) {
1166  // Something went wrong. Keep the original filename, assume it
1167  // was the correct case after all. We return true because the
1168  // filename is good.
1169  return true;
1170  }
1171 
1172  true_case._flags = _flags;
1173  (*this) = true_case;
1174  return true;
1175 
1176 #else // WIN32
1177  return true;
1178 #endif // WIN32
1179 }
1180 
1181 ////////////////////////////////////////////////////////////////////
1182 // Function: Filename::to_os_specific
1183 // Access: Published
1184 // Description: Converts the filename from our generic Unix-like
1185 // convention (forward slashes starting with the root at
1186 // '/') to the corresponding filename in the local
1187 // operating system (slashes in the appropriate
1188 // direction, starting with the root at C:\, for
1189 // instance). Returns the string representing the
1190 // converted filename, but does not change the Filename
1191 // itself.
1192 //
1193 // See also from_os_specific().
1194 ////////////////////////////////////////////////////////////////////
1195 string Filename::
1197  assert(!get_pattern());
1198 
1199  if (empty()) {
1200  return string();
1201  }
1202  Filename standard(*this);
1203  standard.standardize();
1204 
1205 #ifdef IS_OSX
1206  if (get_type() == T_dso) {
1207  std::string workname = standard.get_fullpath();
1208  size_t dot = workname.rfind('.');
1209  if (dot != string::npos) {
1210  if (workname.substr(dot) == ".so") {
1211  string dyLibBase = workname.substr(0, dot)+".dylib";
1212  return dyLibBase;
1213  }
1214  }
1215  }
1216 #endif
1217 
1218 #ifdef WIN32
1219  switch (get_type()) {
1220  case T_dso:
1221  return convert_dso_pathname(standard.get_fullpath());
1222  case T_executable:
1223  return convert_executable_pathname(standard.get_fullpath());
1224  default:
1225  return convert_pathname(standard.get_fullpath());
1226  }
1227 #else // WIN32
1228  return standard.c_str();
1229 #endif // WIN32
1230 }
1231 
1232 ////////////////////////////////////////////////////////////////////
1233 // Function: Filename::to_os_specific_w
1234 // Access: Published
1235 // Description: The wide-string variant on to_os_specific().
1236 ////////////////////////////////////////////////////////////////////
1237 wstring Filename::
1239  TextEncoder encoder;
1241  encoder.set_text(to_os_specific());
1242  return encoder.get_wtext();
1243 }
1244 
1245 ////////////////////////////////////////////////////////////////////
1246 // Function: Filename::to_os_generic
1247 // Access: Published
1248 // Description: This is similar to to_os_specific(), but it is
1249 // designed to generate a filename that can be
1250 // understood on as many platforms as possible. Since
1251 // Windows can usually understand a
1252 // forward-slash-delimited filename, this means it does
1253 // the same thing as to_os_specific(), but it uses
1254 // forward slashes instead of backslashes.
1255 //
1256 // This method has a pretty limited use; it should
1257 // generally be used for writing file references to a
1258 // file that might be read on any operating system.
1259 ////////////////////////////////////////////////////////////////////
1260 string Filename::
1261 to_os_generic() const {
1262  assert(!get_pattern());
1263 
1264 #ifdef WIN32
1265  return back_to_front_slash(to_os_specific());
1266 #else // WIN32
1267  return to_os_specific();
1268 #endif // WIN32
1269 }
1270 
1271 ////////////////////////////////////////////////////////////////////
1272 // Function: Filename::to_os_short_name
1273 // Access: Published
1274 // Description: This works like to_os_generic(), but it returns the
1275 // "short name" version of the filename, if it exists,
1276 // or the original filename otherwise.
1277 //
1278 // On Windows platforms, this returns the 8.3 filename
1279 // version of the given filename, if the file exists,
1280 // and the same thing as to_os_specific() otherwise. On
1281 // non-Windows platforms, this always returns the same
1282 // thing as to_os_specific().
1283 ////////////////////////////////////////////////////////////////////
1284 string Filename::
1286  assert(!get_pattern());
1287 
1288 #ifdef WIN32
1289  wstring os_specific = to_os_specific_w();
1290 
1291  wchar_t short_name[MAX_PATH + 1];
1292  DWORD l = GetShortPathNameW(os_specific.c_str(), short_name, MAX_PATH + 1);
1293  if (l == 0) {
1294  // Couldn't query the path name for some reason. Probably the
1295  // file didn't exist.
1296  return to_os_specific();
1297  }
1298  // According to the Windows docs, l will return a value greater than
1299  // the specified length if the short_name length wasn't enough--but also
1300  // according to the Windows docs, MAX_PATH will always be enough.
1301  assert(l < MAX_PATH + 1);
1302 
1303  TextEncoder encoder;
1305  encoder.set_wtext(short_name);
1306  return encoder.get_text();
1307 
1308 #else // WIN32
1309  return to_os_specific();
1310 #endif // WIN32
1311 }
1312 
1313 ////////////////////////////////////////////////////////////////////
1314 // Function: Filename::to_os_long_name
1315 // Access: Published
1316 // Description: This is the opposite of to_os_short_name(): it
1317 // returns the "long name" of the filename, if the
1318 // filename exists. On non-Windows platforms, this
1319 // returns the same thing as to_os_specific().
1320 ////////////////////////////////////////////////////////////////////
1321 string Filename::
1323  assert(!get_pattern());
1324 
1325 #ifdef WIN32
1326  wstring os_specific = to_os_specific_w();
1327 
1328  wchar_t long_name[MAX_PATH + 1];
1329  DWORD l = GetLongPathNameW(os_specific.c_str(), long_name, MAX_PATH + 1);
1330  if (l == 0) {
1331  // Couldn't query the path name for some reason. Probably the
1332  // file didn't exist.
1333  return to_os_specific();
1334  }
1335  assert(l < MAX_PATH + 1);
1336 
1337  TextEncoder encoder;
1339  encoder.set_wtext(long_name);
1340  return encoder.get_text();
1341 
1342 #else // WIN32
1343  return to_os_specific();
1344 #endif // WIN32
1345 }
1346 
1347 ////////////////////////////////////////////////////////////////////
1348 // Function: Filename::exists
1349 // Access: Published
1350 // Description: Returns true if the filename exists on the disk,
1351 // false otherwise. If the type is indicated to be
1352 // executable, this also tests that the file has execute
1353 // permission.
1354 ////////////////////////////////////////////////////////////////////
1355 bool Filename::
1356 exists() const {
1357 #ifdef WIN32_VC
1358  wstring os_specific = get_filename_index(0).to_os_specific_w();
1359 
1360  bool exists = false;
1361 
1362  DWORD results = GetFileAttributesW(os_specific.c_str());
1363  if (results != -1) {
1364  exists = true;
1365  }
1366 
1367 #else // WIN32_VC
1368  string os_specific = get_filename_index(0).to_os_specific();
1369 
1370  struct stat this_buf;
1371  bool exists = false;
1372 
1373  if (stat(os_specific.c_str(), &this_buf) == 0) {
1374  exists = true;
1375  }
1376 #endif
1377 
1378  return exists;
1379 }
1380 
1381 ////////////////////////////////////////////////////////////////////
1382 // Function: Filename::is_regular_file
1383 // Access: Published
1384 // Description: Returns true if the filename exists and is the
1385 // name of a regular file (i.e. not a directory or
1386 // device), false otherwise.
1387 ////////////////////////////////////////////////////////////////////
1388 bool Filename::
1390 #ifdef WIN32_VC
1391  wstring os_specific = get_filename_index(0).to_os_specific_w();
1392 
1393  bool isreg = false;
1394 
1395  DWORD results = GetFileAttributesW(os_specific.c_str());
1396  if (results != -1) {
1397  isreg = ((results & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0);
1398  }
1399 
1400 #else // WIN32_VC
1401  string os_specific = get_filename_index(0).to_os_specific();
1402 
1403  struct stat this_buf;
1404  bool isreg = false;
1405 
1406  if (stat(os_specific.c_str(), &this_buf) == 0) {
1407  isreg = S_ISREG(this_buf.st_mode);
1408  }
1409 #endif
1410 
1411  return isreg;
1412 }
1413 
1414 ////////////////////////////////////////////////////////////////////
1415 // Function: Filename::is_writable
1416 // Access: Published
1417 // Description: Returns true if the filename exists and is either a
1418 // directory or a regular file that can be written to,
1419 // or false otherwise.
1420 ////////////////////////////////////////////////////////////////////
1421 bool Filename::
1422 is_writable() const {
1423  bool writable = false;
1424 
1425 #ifdef WIN32_VC
1426  wstring os_specific = get_filename_index(0).to_os_specific_w();
1427 
1428  DWORD results = GetFileAttributesW(os_specific.c_str());
1429  if (results != -1) {
1430  if ((results & FILE_ATTRIBUTE_DIRECTORY) != 0) {
1431  // Assume directories are writable.
1432  writable = true;
1433  } else if ((results & FILE_ATTRIBUTE_READONLY) == 0) {
1434  // Not read-only means writable.
1435  writable = true;
1436  }
1437  }
1438 #else // WIN32_VC
1439  string os_specific = get_filename_index(0).to_os_specific();
1440 
1441  if (access(os_specific.c_str(), W_OK) == 0) {
1442  writable = true;
1443  }
1444 #endif
1445 
1446  return writable;
1447 }
1448 
1449 ////////////////////////////////////////////////////////////////////
1450 // Function: Filename::is_directory
1451 // Access: Published
1452 // Description: Returns true if the filename exists and is a
1453 // directory name, false otherwise.
1454 ////////////////////////////////////////////////////////////////////
1455 bool Filename::
1456 is_directory() const {
1457 #ifdef WIN32_VC
1458  wstring os_specific = get_filename_index(0).to_os_specific_w();
1459 
1460  bool isdir = false;
1461 
1462  DWORD results = GetFileAttributesW(os_specific.c_str());
1463  if (results != -1) {
1464  isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
1465  }
1466 #else // WIN32_VC
1467  string os_specific = get_filename_index(0).to_os_specific();
1468 
1469  struct stat this_buf;
1470  bool isdir = false;
1471 
1472  if (stat(os_specific.c_str(), &this_buf) == 0) {
1473  isdir = S_ISDIR(this_buf.st_mode);
1474  }
1475 #endif
1476 
1477  return isdir;
1478 }
1479 
1480 ////////////////////////////////////////////////////////////////////
1481 // Function: Filename::is_executable
1482 // Access: Published
1483 // Description: Returns true if the filename exists and is
1484 // executable
1485 ////////////////////////////////////////////////////////////////////
1486 bool Filename::
1487 is_executable() const {
1488 #ifdef WIN32_VC
1489  // no access() in windows, but to our advantage executables can only
1490  // end in .exe or .com
1491  string extension = get_extension();
1492  if (extension == "exe" || extension == "com") {
1493  return exists();
1494  }
1495 
1496 #else /* WIN32_VC */
1497  string os_specific = get_filename_index(0).to_os_specific();
1498  if (access(os_specific.c_str(), X_OK) == 0) {
1499  return true;
1500  }
1501 #endif /* WIN32_VC */
1502 
1503  return false;
1504 }
1505 
1506 ////////////////////////////////////////////////////////////////////
1507 // Function: Filename::compare_timestamps
1508 // Access: Published
1509 // Description: Returns a number less than zero if the file named by
1510 // this object is older than the given file, zero if
1511 // they have the same timestamp, or greater than zero if
1512 // this one is newer.
1513 //
1514 // If this_missing_is_old is true, it indicates that a
1515 // missing file will be treated as if it were older than
1516 // any other file; otherwise, a missing file will be
1517 // treated as if it were newer than any other file.
1518 // Similarly for other_missing_is_old.
1519 ////////////////////////////////////////////////////////////////////
1520 int Filename::
1522  bool this_missing_is_old,
1523  bool other_missing_is_old) const {
1524 #ifdef WIN32_VC
1525  wstring os_specific = get_filename_index(0).to_os_specific_w();
1526  wstring other_os_specific = other.get_filename_index(0).to_os_specific_w();
1527 
1528  struct _stat this_buf;
1529  bool this_exists = false;
1530 
1531  if (_wstat(os_specific.c_str(), &this_buf) == 0) {
1532  this_exists = true;
1533  }
1534 
1535  struct _stat other_buf;
1536  bool other_exists = false;
1537 
1538  if (_wstat(other_os_specific.c_str(), &other_buf) == 0) {
1539  other_exists = true;
1540  }
1541 #else // WIN32_VC
1542  string os_specific = get_filename_index(0).to_os_specific();
1543  string other_os_specific = other.get_filename_index(0).to_os_specific();
1544 
1545  struct stat this_buf;
1546  bool this_exists = false;
1547 
1548  if (stat(os_specific.c_str(), &this_buf) == 0) {
1549  this_exists = true;
1550  }
1551 
1552  struct stat other_buf;
1553  bool other_exists = false;
1554 
1555  if (stat(other_os_specific.c_str(), &other_buf) == 0) {
1556  other_exists = true;
1557  }
1558 #endif
1559 
1560  if (this_exists && other_exists) {
1561  // Both files exist, return the honest time comparison.
1562  return (int)this_buf.st_mtime - (int)other_buf.st_mtime;
1563 
1564  } else if (!this_exists && !other_exists) {
1565  // Neither file exists.
1566  if (this_missing_is_old == other_missing_is_old) {
1567  // Both files are either "very old" or "very new".
1568  return 0;
1569  }
1570  if (this_missing_is_old) {
1571  // This file is "very old", the other is "very new".
1572  return -1;
1573  } else {
1574  // This file is "very new", the other is "very old".
1575  return 1;
1576  }
1577 
1578  } else if (!this_exists) {
1579  // This file doesn't, the other one does.
1580  return this_missing_is_old ? -1 : 1;
1581 
1582  }
1583  // !other_exists
1584  assert(!other_exists);
1585 
1586  // This file exists, the other one doesn't.
1587  return other_missing_is_old ? 1 : -1;
1588 }
1589 
1590 ////////////////////////////////////////////////////////////////////
1591 // Function: Filename::get_timestamp
1592 // Access: Published
1593 // Description: Returns a time_t value that represents the time the
1594 // file was last modified, to within whatever precision
1595 // the operating system records this information (on a
1596 // Windows95 system, for instance, this may only be
1597 // accurate to within 2 seconds).
1598 //
1599 // If the timestamp cannot be determined, either because
1600 // it is not supported by the operating system or
1601 // because there is some error (such as file not found),
1602 // returns 0.
1603 ////////////////////////////////////////////////////////////////////
1604 time_t Filename::
1605 get_timestamp() const {
1606 #ifdef WIN32_VC
1607  wstring os_specific = get_filename_index(0).to_os_specific_w();
1608 
1609  struct _stat this_buf;
1610 
1611  if (_wstat(os_specific.c_str(), &this_buf) == 0) {
1612  return this_buf.st_mtime;
1613  }
1614 #else // WIN32_VC
1615  string os_specific = get_filename_index(0).to_os_specific();
1616 
1617  struct stat this_buf;
1618 
1619  if (stat(os_specific.c_str(), &this_buf) == 0) {
1620  return this_buf.st_mtime;
1621  }
1622 #endif
1623 
1624  return 0;
1625 }
1626 
1627 ////////////////////////////////////////////////////////////////////
1628 // Function: Filename::get_access_timestamp
1629 // Access: Published
1630 // Description: Returns a time_t value that represents the time the
1631 // file was last accessed, if this information is
1632 // available. See also get_timestamp(), which returns
1633 // the last modification time.
1634 ////////////////////////////////////////////////////////////////////
1635 time_t Filename::
1637 #ifdef WIN32_VC
1638  wstring os_specific = get_filename_index(0).to_os_specific_w();
1639 
1640  struct _stat this_buf;
1641 
1642  if (_wstat(os_specific.c_str(), &this_buf) == 0) {
1643  return this_buf.st_atime;
1644  }
1645 #else // WIN32_VC
1646  string os_specific = get_filename_index(0).to_os_specific();
1647 
1648  struct stat this_buf;
1649 
1650  if (stat(os_specific.c_str(), &this_buf) == 0) {
1651  return this_buf.st_atime;
1652  }
1653 #endif
1654 
1655  return 0;
1656 }
1657 
1658 ////////////////////////////////////////////////////////////////////
1659 // Function: Filename::get_file_size
1660 // Access: Published
1661 // Description: Returns the size of the file in bytes, or 0 if there
1662 // is an error.
1663 ////////////////////////////////////////////////////////////////////
1664 streamsize Filename::
1665 get_file_size() const {
1666 #ifdef WIN32_VC
1667  wstring os_specific = get_filename_index(0).to_os_specific_w();
1668 
1669  struct _stat64 this_buf;
1670 
1671  // _wstat() only returns the lower 32 bits of the file size (!). We
1672  // have to call _wstati64() if we actually want the full 64-bit file
1673  // size.
1674  if (_wstati64(os_specific.c_str(), &this_buf) == 0) {
1675  return this_buf.st_size;
1676  }
1677 #else // WIN32_VC
1678  string os_specific = get_filename_index(0).to_os_specific();
1679 
1680  struct stat this_buf;
1681 
1682  if (stat(os_specific.c_str(), &this_buf) == 0) {
1683  return this_buf.st_size;
1684  }
1685 #endif
1686 
1687  return 0;
1688 }
1689 
1690 ////////////////////////////////////////////////////////////////////
1691 // Function: Filename::resolve_filename
1692 // Access: Published
1693 // Description: Searches the given search path for the filename. If
1694 // it is found, updates the filename to the full
1695 // pathname found and returns true; otherwise, returns
1696 // false.
1697 ////////////////////////////////////////////////////////////////////
1698 bool Filename::
1699 resolve_filename(const DSearchPath &searchpath,
1700  const string &default_extension) {
1701  Filename found;
1702 
1703  if (is_local()) {
1704  found = searchpath.find_file(*this);
1705 
1706  if (found.empty()) {
1707  // We didn't find it with the given extension; can we try the
1708  // default extension?
1709  if (get_extension().empty() && !default_extension.empty()) {
1710  Filename try_ext = *this;
1711  try_ext.set_extension(default_extension);
1712  found = searchpath.find_file(try_ext);
1713  }
1714  }
1715  } else {
1716  if (exists()) {
1717  // The full pathname exists. Return true.
1718  return true;
1719  } else {
1720  // The full pathname doesn't exist with the given extension;
1721  // does it exist with the default extension?
1722  if (get_extension().empty() && !default_extension.empty()) {
1723  Filename try_ext = *this;
1724  try_ext.set_extension(default_extension);
1725  if (try_ext.exists()) {
1726  found = try_ext;
1727  }
1728  }
1729  }
1730  }
1731 
1732  if (!found.empty()) {
1733  (*this) = found;
1734  return true;
1735  }
1736 
1737  return false;
1738 }
1739 
1740 ////////////////////////////////////////////////////////////////////
1741 // Function: Filename::make_relative_to
1742 // Access: Published
1743 // Description: Adjusts this filename, which must be a
1744 // fully-specified pathname beginning with a slash, to
1745 // make it a relative filename, relative to the
1746 // fully-specified directory indicated (which must also
1747 // begin with, and may or may not end with, a slash--a
1748 // terminating slash is ignored).
1749 //
1750 // This only performs a string comparsion, so it may be
1751 // wise to call make_canonical() on both filenames
1752 // before calling make_relative_to().
1753 //
1754 // If allow_backups is false, the filename will only be
1755 // adjusted to be made relative if it is already
1756 // somewhere within or below the indicated directory.
1757 // If allow_backups is true, it will be adjusted in all
1758 // cases, even if this requires putting a series of ../
1759 // characters before the filename--unless it would have
1760 // to back all the way up to the root.
1761 //
1762 // Returns true if the file was adjusted, false if it
1763 // was not.
1764 ////////////////////////////////////////////////////////////////////
1765 bool Filename::
1766 make_relative_to(Filename directory, bool allow_backups) {
1767  if (_filename.empty() || directory.empty() ||
1768  _filename[0] != '/' || directory[0] != '/') {
1769  return false;
1770  }
1771 
1772  standardize();
1773  directory.standardize();
1774 
1775  if (directory == "/") {
1776  // Don't be silly.
1777  return false;
1778  }
1779 
1780  string rel_to_file = directory.get_fullpath() + "/.";
1781 
1782  size_t common = get_common_prefix(rel_to_file);
1783  if (common < 2) {
1784  // Oh, never mind.
1785  return false;
1786  }
1787 
1788  string result;
1789  int slashes = count_slashes(rel_to_file.substr(common));
1790  if (slashes > 0 && !allow_backups) {
1791  // Too bad; the file's not under the indicated directory.
1792  return false;
1793  }
1794 
1795  for (int i = 0; i < slashes; i++) {
1796  result += "../";
1797  }
1798  result += _filename.substr(common);
1799  (*this) = result;
1800 
1801  return true;
1802 }
1803 
1804 ////////////////////////////////////////////////////////////////////
1805 // Function: Filename::find_on_searchpath
1806 // Access: Published
1807 // Description: Performs the reverse of the resolve_filename()
1808 // operation: assuming that the current filename is
1809 // fully-specified pathname (i.e. beginning with '/'),
1810 // look on the indicated search path for a directory
1811 // under which the file can be found. When found,
1812 // adjust the Filename to be relative to the indicated
1813 // directory name.
1814 //
1815 // Returns the index of the directory on the searchpath
1816 // at which the file was found, or -1 if it was not
1817 // found.
1818 ////////////////////////////////////////////////////////////////////
1819 int Filename::
1820 find_on_searchpath(const DSearchPath &searchpath) {
1821  if (_filename.empty() || _filename[0] != '/') {
1822  return -1;
1823  }
1824 
1825  int num_directories = searchpath.get_num_directories();
1826  for (int i = 0; i < num_directories; i++) {
1827  Filename directory = searchpath.get_directory(i);
1828  directory.make_absolute();
1829  if (make_relative_to(directory, false)) {
1830  return i;
1831  }
1832  }
1833 
1834  return -1;
1835 }
1836 
1837 ////////////////////////////////////////////////////////////////////
1838 // Function: Filename::scan_directory
1839 // Access: Published
1840 // Description: Attempts to open the named filename as if it were a
1841 // directory and looks for the non-hidden files within
1842 // the directory. Fills the given vector up with the
1843 // sorted list of filenames that are local to this
1844 // directory.
1845 //
1846 // It is the user's responsibility to ensure that the
1847 // contents vector is empty before making this call;
1848 // otherwise, the new files will be appended to it.
1849 //
1850 // Returns true on success, false if the directory could
1851 // not be read for some reason.
1852 ////////////////////////////////////////////////////////////////////
1853 bool Filename::
1854 scan_directory(vector_string &contents) const {
1855  assert(!get_pattern());
1856 
1857 #if defined(WIN32_VC)
1858  // Use Windows' FindFirstFile() / FindNextFile() to walk through the
1859  // list of files in a directory.
1860  size_t orig_size = contents.size();
1861 
1862  wstring match;
1863  if (empty()) {
1864  match = L"*.*";
1865  } else {
1866  match = to_os_specific_w() + L"\\*.*";
1867  }
1868  WIN32_FIND_DATAW find_data;
1869 
1870  HANDLE handle = FindFirstFileW(match.c_str(), &find_data);
1871  if (handle == INVALID_HANDLE_VALUE) {
1872  if (GetLastError() == ERROR_NO_MORE_FILES) {
1873  // No matching files is not an error.
1874  return true;
1875  }
1876  return false;
1877  }
1878 
1879  TextEncoder encoder;
1881  do {
1882  thread_consider_yield();
1883  wstring filename = find_data.cFileName;
1884  if (filename != L"." && filename != L"..") {
1885  encoder.set_wtext(filename);
1886  contents.push_back(encoder.get_text());
1887  }
1888  } while (FindNextFileW(handle, &find_data));
1889 
1890  bool scan_ok = (GetLastError() == ERROR_NO_MORE_FILES);
1891  FindClose(handle);
1892 
1893  sort(contents.begin() + orig_size, contents.end());
1894  return scan_ok;
1895 
1896 #elif defined(PHAVE_DIRENT_H)
1897  // Use Posix's opendir() / readdir() to walk through the list of
1898  // files in a directory.
1899  size_t orig_size = contents.size();
1900 
1901  string dirname;
1902  if (empty()) {
1903  dirname = ".";
1904  } else {
1905  dirname = _filename;
1906  }
1907  DIR *root = opendir(dirname.c_str());
1908  if (root == (DIR *)NULL) {
1909  if (errno != ENOTDIR) {
1910  perror(dirname.c_str());
1911  }
1912  return false;
1913  }
1914 
1915  struct dirent *d;
1916  d = readdir(root);
1917  while (d != (struct dirent *)NULL) {
1918  thread_consider_yield();
1919  if (d->d_name[0] != '.') {
1920  contents.push_back(d->d_name);
1921  }
1922  d = readdir(root);
1923  }
1924 
1925  // It turns out to be a mistake to check the value of errno after
1926  // calling readdir(), since it might have been set to non-zero
1927  // during some internal operation of readdir(), even though there
1928  // wasn't really a problem with scanning the directory itself.
1929  /*
1930  if (errno != 0 && errno != ENOENT && errno != ENOTDIR) {
1931  cerr << "Error occurred while scanning directory " << dirname << "\n";
1932  perror(dirname.c_str());
1933  closedir(root);
1934  return false;
1935  }
1936  */
1937  closedir(root);
1938 
1939  sort(contents.begin() + orig_size, contents.end());
1940  return true;
1941 
1942 #elif defined(PHAVE_GLOB_H)
1943  // It's hard to imagine a system that provides glob.h but does not
1944  // provide openddir() .. readdir(), but this code is leftover from a
1945  // time when there was an undetected bug in the above readdir()
1946  // loop, and it works, so we might as well keep it around for now.
1947  string dirname;
1948  if (empty()) {
1949  dirname = "*";
1950  } else if (_filename[_filename.length() - 1] == '/') {
1951  dirname = _filename + "*";
1952  } else {
1953  dirname = _filename + "/*"; /* comment to fix emacs syntax hilight */
1954  }
1955 
1956  glob_t globbuf;
1957 
1958  int r = glob(dirname.c_str(), GLOB_ERR, NULL, &globbuf);
1959 
1960  if (r != 0) {
1961  // Some error processing the match string. If our version of
1962  // glob.h defines GLOB_NOMATCH, then we can differentiate an empty
1963  // return result from some other kind of error.
1964 #ifdef GLOB_NOMATCH
1965  if (r != GLOB_NOMATCH) {
1966  perror(dirname.c_str());
1967  return false;
1968  }
1969 #endif
1970 
1971  // Otherwise, all errors mean the same thing: no matches, but
1972  // otherwise no problem.
1973  return true;
1974  }
1975 
1976  size_t offset = dirname.size() - 1;
1977 
1978  for (int i = 0; globbuf.gl_pathv[i] != NULL; i++) {
1979  contents.push_back(globbuf.gl_pathv[i] + offset);
1980  }
1981  globfree(&globbuf);
1982 
1983  return true;
1984 
1985 #else
1986  // Don't know how to scan directories!
1987  return false;
1988 #endif
1989 }
1990 
1991 ////////////////////////////////////////////////////////////////////
1992 // Function: Filename::open_read
1993 // Access: Published
1994 // Description: Opens the indicated ifstream for reading the file, if
1995 // possible. Returns true if successful, false
1996 // otherwise. This requires the setting of the
1997 // set_text()/set_binary() flags to open the file
1998 // appropriately as indicated; it is an error to call
1999 // open_read() without first calling one of set_text()
2000 // or set_binary().
2001 ////////////////////////////////////////////////////////////////////
2002 bool Filename::
2003 open_read(ifstream &stream) const {
2004  assert(!get_pattern());
2005  assert(is_binary_or_text());
2006 
2007  ios_openmode open_mode = ios::in;
2008 
2009 #ifdef HAVE_IOS_BINARY
2010  // For some reason, some systems (like Irix) don't define
2011  // ios::binary.
2012  if (!is_text()) {
2013  open_mode |= ios::binary;
2014  }
2015 #endif
2016 
2017  stream.clear();
2018 #ifdef WIN32_VC
2019  wstring os_specific = to_os_specific_w();
2020  stream.open(os_specific.c_str(), open_mode);
2021 #else
2022  string os_specific = to_os_specific();
2023  stream.open(os_specific.c_str(), open_mode);
2024 #endif // WIN32_VC
2025 
2026  return (!stream.fail());
2027 }
2028 
2029 ////////////////////////////////////////////////////////////////////
2030 // Function: Filename::open_write
2031 // Access: Published
2032 // Description: Opens the indicated ifstream for writing the file, if
2033 // possible. Returns true if successful, false
2034 // otherwise. This requires the setting of the
2035 // set_text()/set_binary() flags to open the file
2036 // appropriately as indicated; it is an error to call
2037 // open_read() without first calling one of set_text()
2038 // or set_binary().
2039 //
2040 // If truncate is true, the file is truncated to zero
2041 // length upon opening it, if it already exists.
2042 // Otherwise, the file is kept at its original length.
2043 ////////////////////////////////////////////////////////////////////
2044 bool Filename::
2045 open_write(ofstream &stream, bool truncate) const {
2046  assert(!get_pattern());
2047  assert(is_binary_or_text());
2048 
2049  ios_openmode open_mode = ios::out;
2050 
2051  if (truncate) {
2052  open_mode |= ios::trunc;
2053 
2054  } else {
2055  // Some systems insist on having ios::in set to prevent the file
2056  // from being truncated when we open it. Makes ios::trunc kind of
2057  // pointless, doesn't it? On the other hand, setting ios::in also
2058  // seems to imply ios::nocreate (!), so we should only set this if
2059  // the file already exists.
2060  if (exists()) {
2061  open_mode |= ios::in;
2062  }
2063  }
2064 
2065 #ifdef HAVE_IOS_BINARY
2066  // For some reason, some systems (like Irix) don't define
2067  // ios::binary.
2068  if (!is_text()) {
2069  open_mode |= ios::binary;
2070  }
2071 #endif
2072 
2073  stream.clear();
2074 #ifdef WIN32_VC
2075  wstring os_specific = to_os_specific_w();
2076  stream.open(os_specific.c_str(), open_mode);
2077 #else
2078  string os_specific = to_os_specific();
2079 #ifdef HAVE_OPEN_MASK
2080  stream.open(os_specific.c_str(), open_mode, 0666);
2081 #else
2082  stream.open(os_specific.c_str(), open_mode);
2083 #endif
2084 #endif // WIN32_VC
2085 
2086  return (!stream.fail());
2087 }
2088 
2089 ////////////////////////////////////////////////////////////////////
2090 // Function: Filename::open_append
2091 // Access: Published
2092 // Description: Opens the indicated ofstream for writing the file, if
2093 // possible. Returns true if successful, false
2094 // otherwise. This requires the setting of the
2095 // set_text()/set_binary() flags to open the file
2096 // appropriately as indicated; it is an error to call
2097 // open_read() without first calling one of set_text()
2098 // or set_binary().
2099 ////////////////////////////////////////////////////////////////////
2100 bool Filename::
2101 open_append(ofstream &stream) const {
2102  assert(!get_pattern());
2103  assert(is_binary_or_text());
2104 
2105  ios_openmode open_mode = ios::app;
2106 
2107 #ifdef HAVE_IOS_BINARY
2108  // For some reason, some systems (like Irix) don't define
2109  // ios::binary.
2110  if (!is_text()) {
2111  open_mode |= ios::binary;
2112  }
2113 #endif
2114 
2115  stream.clear();
2116 #ifdef WIN32_VC
2117  wstring os_specific = to_os_specific_w();
2118  stream.open(os_specific.c_str(), open_mode);
2119 #else
2120  string os_specific = to_os_specific();
2121 #ifdef HAVE_OPEN_MASK
2122  stream.open(os_specific.c_str(), open_mode, 0666);
2123 #else
2124  stream.open(os_specific.c_str(), open_mode);
2125 #endif
2126 #endif // WIN32_VC
2127 
2128  return (!stream.fail());
2129 }
2130 
2131 ////////////////////////////////////////////////////////////////////
2132 // Function: Filename::open_read_write
2133 // Access: Published
2134 // Description: Opens the indicated fstream for read/write access to
2135 // the file, if possible. Returns true if successful,
2136 // false otherwise. This requires the setting of the
2137 // set_text()/set_binary() flags to open the file
2138 // appropriately as indicated; it is an error to call
2139 // open_read_write() without first calling one of
2140 // set_text() or set_binary().
2141 ////////////////////////////////////////////////////////////////////
2142 bool Filename::
2143 open_read_write(fstream &stream, bool truncate) const {
2144  assert(!get_pattern());
2145  assert(is_binary_or_text());
2146 
2147  ios_openmode open_mode = ios::out | ios::in;
2148 
2149  if (truncate) {
2150  open_mode |= ios::trunc;
2151  }
2152 
2153  // Since ios::in also seems to imply ios::nocreate (!), we must
2154  // guarantee the file already exists before we try to open it.
2155  if (!exists()) {
2156  touch();
2157  }
2158 
2159 #ifdef HAVE_IOS_BINARY
2160  // For some reason, some systems (like Irix) don't define
2161  // ios::binary.
2162  if (!is_text()) {
2163  open_mode |= ios::binary;
2164  }
2165 #endif
2166 
2167  stream.clear();
2168 #ifdef WIN32_VC
2169  wstring os_specific = to_os_specific_w();
2170  stream.open(os_specific.c_str(), open_mode);
2171 #else
2172  string os_specific = to_os_specific();
2173 #ifdef HAVE_OPEN_MASK
2174  stream.open(os_specific.c_str(), open_mode, 0666);
2175 #else
2176  stream.open(os_specific.c_str(), open_mode);
2177 #endif
2178 #endif // WIN32_VC
2179 
2180  return (!stream.fail());
2181 }
2182 
2183 ////////////////////////////////////////////////////////////////////
2184 // Function: Filename::open_read_append
2185 // Access: Published
2186 // Description: Opens the indicated ifstream for reading and writing
2187 // the file, if possible; writes are appended to the end
2188 // of the file. Returns true if successful, false
2189 // otherwise. This requires the setting of the
2190 // set_text()/set_binary() flags to open the file
2191 // appropriately as indicated; it is an error to call
2192 // open_read() without first calling one of set_text()
2193 // or set_binary().
2194 ////////////////////////////////////////////////////////////////////
2195 bool Filename::
2196 open_read_append(fstream &stream) const {
2197  assert(!get_pattern());
2198  assert(is_binary_or_text());
2199 
2200  ios_openmode open_mode = ios::app | ios::in;
2201 
2202 #ifdef HAVE_IOS_BINARY
2203  // For some reason, some systems (like Irix) don't define
2204  // ios::binary.
2205  if (!is_text()) {
2206  open_mode |= ios::binary;
2207  }
2208 #endif
2209 
2210  stream.clear();
2211 #ifdef WIN32_VC
2212  wstring os_specific = to_os_specific_w();
2213  stream.open(os_specific.c_str(), open_mode);
2214 #else
2215  string os_specific = to_os_specific();
2216 #ifdef HAVE_OPEN_MASK
2217  stream.open(os_specific.c_str(), open_mode, 0666);
2218 #else
2219  stream.open(os_specific.c_str(), open_mode);
2220 #endif
2221 #endif // WIN32_VC
2222 
2223  return (!stream.fail());
2224 }
2225 
2226 #ifdef USE_PANDAFILESTREAM
2227 ////////////////////////////////////////////////////////////////////
2228 // Function: Filename::open_read
2229 // Access: Published
2230 // Description: Opens the indicated pifstream for reading the file, if
2231 // possible. Returns true if successful, false
2232 // otherwise. This requires the setting of the
2233 // set_text()/set_binary() flags to open the file
2234 // appropriately as indicated; it is an error to call
2235 // open_read() without first calling one of set_text()
2236 // or set_binary().
2237 ////////////////////////////////////////////////////////////////////
2238 bool Filename::
2239 open_read(pifstream &stream) const {
2240  assert(!get_pattern());
2241  assert(is_binary_or_text());
2242 
2243  ios_openmode open_mode = ios::in;
2244 
2245 #ifdef HAVE_IOS_BINARY
2246  // For some reason, some systems (like Irix) don't define
2247  // ios::binary.
2248  if (!is_text()) {
2249  open_mode |= ios::binary;
2250  }
2251 #endif
2252 
2253  string os_specific = to_os_specific();
2254  stream.clear();
2255  stream.open(os_specific.c_str(), open_mode);
2256  return (!stream.fail());
2257 }
2258 #endif // USE_PANDAFILESTREAM
2259 
2260 #ifdef USE_PANDAFILESTREAM
2261 ////////////////////////////////////////////////////////////////////
2262 // Function: Filename::open_write
2263 // Access: Published
2264 // Description: Opens the indicated pifstream for writing the file, if
2265 // possible. Returns true if successful, false
2266 // otherwise. This requires the setting of the
2267 // set_text()/set_binary() flags to open the file
2268 // appropriately as indicated; it is an error to call
2269 // open_read() without first calling one of set_text()
2270 // or set_binary().
2271 //
2272 // If truncate is true, the file is truncated to zero
2273 // length upon opening it, if it already exists.
2274 // Otherwise, the file is kept at its original length.
2275 ////////////////////////////////////////////////////////////////////
2276 bool Filename::
2277 open_write(pofstream &stream, bool truncate) const {
2278  assert(!get_pattern());
2279  assert(is_binary_or_text());
2280 
2281  ios_openmode open_mode = ios::out;
2282 
2283  if (truncate) {
2284  open_mode |= ios::trunc;
2285 
2286  } else {
2287  // Some systems insist on having ios::in set to prevent the file
2288  // from being truncated when we open it. Makes ios::trunc kind of
2289  // pointless, doesn't it? On the other hand, setting ios::in also
2290  // seems to imply ios::nocreate (!), so we should only set this if
2291  // the file already exists.
2292  if (exists()) {
2293  open_mode |= ios::in;
2294  }
2295  }
2296 
2297 #ifdef HAVE_IOS_BINARY
2298  // For some reason, some systems (like Irix) don't define
2299  // ios::binary.
2300  if (!is_text()) {
2301  open_mode |= ios::binary;
2302  }
2303 #endif
2304 
2305  stream.clear();
2306  string os_specific = to_os_specific();
2307 #ifdef HAVE_OPEN_MASK
2308  stream.open(os_specific.c_str(), open_mode, 0666);
2309 #else
2310  stream.open(os_specific.c_str(), open_mode);
2311 #endif
2312 
2313  return (!stream.fail());
2314 }
2315 #endif // USE_PANDAFILESTREAM
2316 
2317 #ifdef USE_PANDAFILESTREAM
2318 ////////////////////////////////////////////////////////////////////
2319 // Function: Filename::open_append
2320 // Access: Published
2321 // Description: Opens the indicated pifstream for writing the file, if
2322 // possible. Returns true if successful, false
2323 // otherwise. This requires the setting of the
2324 // set_text()/set_binary() flags to open the file
2325 // appropriately as indicated; it is an error to call
2326 // open_read() without first calling one of set_text()
2327 // or set_binary().
2328 ////////////////////////////////////////////////////////////////////
2329 bool Filename::
2330 open_append(pofstream &stream) const {
2331  assert(!get_pattern());
2332  assert(is_binary_or_text());
2333 
2334  ios_openmode open_mode = ios::app;
2335 
2336 #ifdef HAVE_IOS_BINARY
2337  // For some reason, some systems (like Irix) don't define
2338  // ios::binary.
2339  if (!is_text()) {
2340  open_mode |= ios::binary;
2341  }
2342 #endif
2343 
2344  stream.clear();
2345  string os_specific = to_os_specific();
2346 #ifdef HAVE_OPEN_MASK
2347  stream.open(os_specific.c_str(), open_mode, 0666);
2348 #else
2349  stream.open(os_specific.c_str(), open_mode);
2350 #endif
2351 
2352  return (!stream.fail());
2353 }
2354 #endif // USE_PANDAFILESTREAM
2355 
2356 #ifdef USE_PANDAFILESTREAM
2357 ////////////////////////////////////////////////////////////////////
2358 // Function: Filename::open_read_write
2359 // Access: Published
2360 // Description: Opens the indicated fstream for read/write access to
2361 // the file, if possible. Returns true if successful,
2362 // false otherwise. This requires the setting of the
2363 // set_text()/set_binary() flags to open the file
2364 // appropriately as indicated; it is an error to call
2365 // open_read_write() without first calling one of
2366 // set_text() or set_binary().
2367 ////////////////////////////////////////////////////////////////////
2368 bool Filename::
2369 open_read_write(pfstream &stream, bool truncate) const {
2370  assert(!get_pattern());
2371  assert(is_binary_or_text());
2372 
2373  ios_openmode open_mode = ios::out | ios::in;
2374 
2375  if (truncate) {
2376  open_mode |= ios::trunc;
2377  }
2378 
2379  // Since ios::in also seems to imply ios::nocreate (!), we must
2380  // guarantee the file already exists before we try to open it.
2381  if (!exists()) {
2382  touch();
2383  }
2384 
2385 #ifdef HAVE_IOS_BINARY
2386  // For some reason, some systems (like Irix) don't define
2387  // ios::binary.
2388  if (!is_text()) {
2389  open_mode |= ios::binary;
2390  }
2391 #endif
2392 
2393  stream.clear();
2394  string os_specific = to_os_specific();
2395 #ifdef HAVE_OPEN_MASK
2396  stream.open(os_specific.c_str(), open_mode, 0666);
2397 #else
2398  stream.open(os_specific.c_str(), open_mode);
2399 #endif
2400 
2401  return (!stream.fail());
2402 }
2403 #endif // USE_PANDAFILESTREAM
2404 
2405 #ifdef USE_PANDAFILESTREAM
2406 ////////////////////////////////////////////////////////////////////
2407 // Function: Filename::open_read_append
2408 // Access: Published
2409 // Description: Opens the indicated pfstream for reading and writing
2410 // the file, if possible; writes are appended to the end
2411 // of the file. Returns true if successful, false
2412 // otherwise. This requires the setting of the
2413 // set_text()/set_binary() flags to open the file
2414 // appropriately as indicated; it is an error to call
2415 // open_read() without first calling one of set_text()
2416 // or set_binary().
2417 ////////////////////////////////////////////////////////////////////
2418 bool Filename::
2419 open_read_append(pfstream &stream) const {
2420  assert(!get_pattern());
2421  assert(is_binary_or_text());
2422 
2423  ios_openmode open_mode = ios::app | ios::in;
2424 
2425 #ifdef HAVE_IOS_BINARY
2426  // For some reason, some systems (like Irix) don't define
2427  // ios::binary.
2428  if (!is_text()) {
2429  open_mode |= ios::binary;
2430  }
2431 #endif
2432 
2433  stream.clear();
2434  string os_specific = to_os_specific();
2435 #ifdef HAVE_OPEN_MASK
2436  stream.open(os_specific.c_str(), open_mode, 0666);
2437 #else
2438  stream.open(os_specific.c_str(), open_mode);
2439 #endif
2440 
2441  return (!stream.fail());
2442 }
2443 #endif // USE_PANDAFILESTREAM
2444 
2445 ////////////////////////////////////////////////////////////////////
2446 // Function: Filename::touch
2447 // Access: Published
2448 // Description: Updates the modification time of the file to the
2449 // current time. If the file does not already exist, it
2450 // will be created. Returns true if successful, false
2451 // if there is an error.
2452 ////////////////////////////////////////////////////////////////////
2453 bool Filename::
2454 touch() const {
2455  assert(!get_pattern());
2456 #ifdef WIN32_VC
2457  // In Windows, we have to use the Windows API to do this reliably.
2458 
2459  // First, guarantee the file exists (and also get its handle).
2460  wstring os_specific = to_os_specific_w();
2461  HANDLE fhandle;
2462  fhandle = CreateFileW(os_specific.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
2463  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2464  if (fhandle == INVALID_HANDLE_VALUE) {
2465  return false;
2466  }
2467 
2468  // Now update the file time and date.
2469  SYSTEMTIME sysnow;
2470  FILETIME ftnow;
2471  GetSystemTime(&sysnow);
2472  if (!SystemTimeToFileTime(&sysnow, &ftnow)) {
2473  CloseHandle(fhandle);
2474  return false;
2475  }
2476 
2477  if (!SetFileTime(fhandle, NULL, NULL, &ftnow)) {
2478  CloseHandle(fhandle);
2479  return false;
2480  }
2481 
2482  CloseHandle(fhandle);
2483  return true;
2484 
2485 #elif defined(PHAVE_UTIME_H)
2486  // Most Unix systems can do this explicitly.
2487 
2488  string os_specific = to_os_specific();
2489 #ifdef HAVE_CYGWIN
2490  // In the Cygwin case, it seems we need to be sure to use the
2491  // Cygwin-style name; some broken utime() implementation. That's
2492  // almost the same thing as the original Panda-style name, but not
2493  // exactly, so we first convert the Panda name to a Windows name,
2494  // then convert it back to Cygwin, to ensure we get it exactly right
2495  // by Cygwin rules.
2496  {
2497  char result[4096] = "";
2498  cygwin_conv_to_posix_path(os_specific.c_str(), result);
2499  os_specific = result;
2500  }
2501 #endif // HAVE_CYGWIN
2502  int result = utime(os_specific.c_str(), NULL);
2503  if (result < 0) {
2504  if (errno == ENOENT) {
2505  // So the file doesn't already exist; create it.
2506  int fd = creat(os_specific.c_str(), 0666);
2507  if (fd < 0) {
2508  perror(os_specific.c_str());
2509  return false;
2510  }
2511  close(fd);
2512  return true;
2513  }
2514  perror(os_specific.c_str());
2515  return false;
2516  }
2517  return true;
2518 #else // WIN32, PHAVE_UTIME_H
2519  // Other systems may not have an explicit control over the
2520  // modification time. For these systems, we'll just temporarily
2521  // open the file in append mode, then close it again (it gets closed
2522  // when the pfstream goes out of scope).
2523  pfstream file;
2524  return open_append(file);
2525 #endif // WIN32, PHAVE_UTIME_H
2526 }
2527 
2528 ////////////////////////////////////////////////////////////////////
2529 // Function: Filename::chdir
2530 // Access: Published
2531 // Description: Changes directory to the specified location.
2532 // Returns true if successful, false if failure.
2533 ////////////////////////////////////////////////////////////////////
2534 bool Filename::
2535 chdir() const {
2536 #ifdef WIN32_VC
2537  wstring os_specific = to_os_specific_w();
2538  return (_wchdir(os_specific.c_str()) >= 0);
2539 #else
2540  string os_specific = to_os_specific();
2541  return (::chdir(os_specific.c_str()) >= 0);
2542 #endif // WIN32_VC
2543 }
2544 
2545 ////////////////////////////////////////////////////////////////////
2546 // Function: Filename::unlink
2547 // Access: Published
2548 // Description: Permanently deletes the file associated with the
2549 // filename, if possible. Returns true if successful,
2550 // false if failure (for instance, because the file did
2551 // not exist, or because permissions were inadequate).
2552 ////////////////////////////////////////////////////////////////////
2553 bool Filename::
2554 unlink() const {
2555  assert(!get_pattern());
2556 #ifdef WIN32_VC
2557  // Windows can't delete a file if it's read-only. Weird.
2558  wstring os_specific = to_os_specific_w();
2559  _wchmod(os_specific.c_str(), 0644);
2560  return (_wunlink(os_specific.c_str()) == 0);
2561 #else
2562  string os_specific = to_os_specific();
2563  return (::unlink(os_specific.c_str()) == 0);
2564 #endif // WIN32_VC
2565 }
2566 
2567 
2568 ////////////////////////////////////////////////////////////////////
2569 // Function: Filename::rename_to
2570 // Access: Published
2571 // Description: Renames the file to the indicated new filename. If
2572 // the new filename is in a different directory, this
2573 // will perform a move. Returns true if successful,
2574 // false on failure.
2575 ////////////////////////////////////////////////////////////////////
2576 bool Filename::
2577 rename_to(const Filename &other) const {
2578  assert(!get_pattern());
2579 
2580  if (*this == other) {
2581  // Trivial success.
2582  return true;
2583  }
2584 
2585 #ifdef WIN32_VC
2586  wstring os_specific = to_os_specific_w();
2587  wstring other_os_specific = other.to_os_specific_w();
2588 
2589  if (_wrename(os_specific.c_str(),
2590  other_os_specific.c_str()) == 0) {
2591  // Successfully renamed.
2592  return true;
2593  }
2594 
2595  // The above might fail if we have tried to move a file to a
2596  // different filesystem. In this case, copy the file into the same
2597  // directory first, and then rename it.
2598  string dirname = other.get_dirname();
2599  if (dirname.empty()) {
2600  dirname = ".";
2601  }
2602  Filename temp = Filename::temporary(dirname, "");
2603  temp.set_binary();
2604  if (!Filename::binary_filename(*this).copy_to(temp)) {
2605  return false;
2606  }
2607 
2608  wstring temp_os_specific = temp.to_os_specific_w();
2609  if (_wrename(temp_os_specific.c_str(),
2610  other_os_specific.c_str()) == 0) {
2611  // Successfully renamed.
2612  unlink();
2613  return true;
2614  }
2615 
2616  // Try unlinking the target first.
2617  other.unlink();
2618  if (_wrename(temp_os_specific.c_str(),
2619  other_os_specific.c_str()) == 0) {
2620  // Successfully renamed.
2621  unlink();
2622  return true;
2623  }
2624 #else // WIN32_VC
2625  string os_specific = to_os_specific();
2626  string other_os_specific = other.to_os_specific();
2627 
2628  if (rename(os_specific.c_str(),
2629  other_os_specific.c_str()) == 0) {
2630  // Successfully renamed.
2631  return true;
2632  }
2633 
2634  // The above might fail if we have tried to move a file to a
2635  // different filesystem. In this case, copy the file into the same
2636  // directory first, and then rename it.
2637  string dirname = other.get_dirname();
2638  if (dirname.empty()) {
2639  dirname = ".";
2640  }
2641  Filename temp = Filename::temporary(dirname, "");
2642  temp.set_binary();
2643  if (!Filename::binary_filename(*this).copy_to(temp)) {
2644  return false;
2645  }
2646 
2647  string temp_os_specific = temp.to_os_specific();
2648  if (rename(temp_os_specific.c_str(),
2649  other_os_specific.c_str()) == 0) {
2650  // Successfully renamed.
2651  unlink();
2652  return true;
2653  }
2654 
2655  // Try unlinking the target first.
2656  other.unlink();
2657  if (rename(temp_os_specific.c_str(),
2658  other_os_specific.c_str()) == 0) {
2659  // Successfully renamed.
2660  unlink();
2661  return true;
2662  }
2663 #endif // WIN32_VC
2664 
2665  // Failed.
2666  temp.unlink();
2667  return false;
2668 }
2669 
2670 ////////////////////////////////////////////////////////////////////
2671 // Function: Filename::copy_to
2672 // Access: Published
2673 // Description: Copies the file to the indicated new filename, by
2674 // reading the contents and writing it to the new file.
2675 // Returns true if successful, false on failure. The
2676 // copy is always binary, regardless of the filename
2677 // settings.
2678 ////////////////////////////////////////////////////////////////////
2679 bool Filename::
2680 copy_to(const Filename &other) const {
2681  Filename this_filename = Filename::binary_filename(*this);
2682  pifstream in;
2683  if (!this_filename.open_read(in)) {
2684  return false;
2685  }
2686 
2687  Filename other_filename = Filename::binary_filename(other);
2688  pofstream out;
2689  if (!other_filename.open_write(out)) {
2690  return false;
2691  }
2692 
2693  static const size_t buffer_size = 4096;
2694  char buffer[buffer_size];
2695 
2696  in.read(buffer, buffer_size);
2697  size_t count = in.gcount();
2698  while (count != 0) {
2699  out.write(buffer, count);
2700  if (out.fail()) {
2701  other.unlink();
2702  return false;
2703  }
2704  in.read(buffer, buffer_size);
2705  count = in.gcount();
2706  }
2707 
2708  if (!in.eof()) {
2709  other.unlink();
2710  return false;
2711  }
2712 
2713  return true;
2714 }
2715 
2716 ////////////////////////////////////////////////////////////////////
2717 // Function: Filename::make_dir
2718 // Access: Published
2719 // Description: Creates all the directories in the path to the file
2720 // specified in the filename, except for the basename
2721 // itself. This assumes that the Filename contains the
2722 // name of a file, not a directory name; it ensures that
2723 // the directory containing the file exists.
2724 //
2725 // However, if the filename ends in a slash, it assumes
2726 // the Filename represents the name of a directory, and
2727 // creates all the paths.
2728 ////////////////////////////////////////////////////////////////////
2729 bool Filename::
2730 make_dir() const {
2731  assert(!get_pattern());
2732  if (empty()) {
2733  return false;
2734  }
2735  Filename path;
2736  if (_filename[_filename.length() - 1] == '/') {
2737  // The Filename ends in a slash; it represents a directory.
2738  path = (*this);
2739 
2740  } else {
2741  // The Filename does not end in a slash; it represents a file.
2742  path = get_dirname();
2743  }
2744 
2745  if (path.empty()) {
2746  return false;
2747  }
2748  string dirname = path.get_fullpath();
2749 
2750  // First, make sure everything up to the last path is known. We
2751  // don't care too much if any of these fail; maybe they failed
2752  // because the directory was already there.
2753  size_t slash = dirname.find('/');
2754  while (slash != string::npos) {
2755  Filename component(dirname.substr(0, slash));
2756 #ifdef WIN32_VC
2757  wstring os_specific = component.to_os_specific_w();
2758  _wmkdir(os_specific.c_str());
2759 #else
2760  string os_specific = component.to_os_specific();
2761  ::mkdir(os_specific.c_str(), 0777);
2762 #endif // WIN32_VC
2763  slash = dirname.find('/', slash + 1);
2764  }
2765 
2766  // Now make the last one, and check the return value.
2767  Filename component(dirname);
2768 #ifdef WIN32_VC
2769  wstring os_specific = component.to_os_specific_w();
2770  int result = _wmkdir(os_specific.c_str());
2771 #else
2772  string os_specific = component.to_os_specific();
2773  int result = ::mkdir(os_specific.c_str(), 0777);
2774 #endif // WIN32_VC
2775 
2776  return (result == 0);
2777 }
2778 
2779 ////////////////////////////////////////////////////////////////////
2780 // Function: Filename::mkdir
2781 // Access: Published
2782 // Description: Creates the directory named by this filename. Unlike
2783 // make_dir(), this assumes that the Filename contains
2784 // the directory name itself. Also, parent directories
2785 // are not automatically created; this function fails if
2786 // any parent directory is missing.
2787 ////////////////////////////////////////////////////////////////////
2788 bool Filename::
2789 mkdir() const {
2790 #ifdef WIN32_VC
2791  wstring os_specific = to_os_specific_w();
2792  int result = _wmkdir(os_specific.c_str());
2793 #else
2794  string os_specific = to_os_specific();
2795  int result = ::mkdir(os_specific.c_str(), 0777);
2796 #endif // WIN32_VC
2797 
2798  return (result == 0);
2799 }
2800 
2801 ////////////////////////////////////////////////////////////////////
2802 // Function: Filename::rmdir
2803 // Access: Published
2804 // Description: The inverse of mkdir(): this removes the directory
2805 // named by this Filename, if it is in fact a directory.
2806 ////////////////////////////////////////////////////////////////////
2807 bool Filename::
2808 rmdir() const {
2809 #ifdef WIN32_VC
2810  wstring os_specific = to_os_specific_w();
2811 
2812  int result = _wrmdir(os_specific.c_str());
2813  if (result != 0) {
2814  // Windows may require the directory to be writable before we can
2815  // remove it.
2816  _wchmod(os_specific.c_str(), 0777);
2817  result = _wrmdir(os_specific.c_str());
2818  }
2819 
2820 #else // WIN32_VC
2821  string os_specific = to_os_specific();
2822  int result = ::rmdir(os_specific.c_str());
2823 #endif // WIN32_VC
2824 
2825  return (result == 0);
2826 }
2827 
2828 ////////////////////////////////////////////////////////////////////
2829 // Function: Filename::get_hash
2830 // Access: Published
2831 // Description: Returns a hash code that attempts to be mostly unique
2832 // for different Filenames.
2833 ////////////////////////////////////////////////////////////////////
2834 int Filename::
2835 get_hash() const {
2836  static const int primes[] = {
2837  2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
2838  31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
2839  73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
2840  127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
2841  179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
2842  233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
2843  283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
2844  353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
2845  419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
2846  467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
2847  547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
2848  607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
2849  661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
2850  739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
2851  811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
2852  877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
2853  947, 953, 967, 971, 977, 983, 991, 997
2854  };
2855  static const size_t num_primes = sizeof(primes) / sizeof(int);
2856 
2857  int hash = 0;
2858  for (size_t i = 0; i < _filename.size(); ++i) {
2859  hash += (int)_filename[i] * primes[i % num_primes];
2860  }
2861 
2862  return hash;
2863 }
2864 
2865 
2866 ////////////////////////////////////////////////////////////////////
2867 // Function: Filename::atomic_compare_and_exchange_contents
2868 // Access: Public
2869 // Description: Uses native file-locking mechanisms to atomically
2870 // replace the contents of a (small) file with the
2871 // specified contents, assuming it hasn't changed since
2872 // the last time the file was read.
2873 //
2874 // This is designed to be similar to
2875 // AtomicAdjust::compare_and_exchange(). The method
2876 // writes new_contents to the file, completely replacing
2877 // the original contents; but only if the original
2878 // contents exactly matched old_contents. If the file
2879 // was modified, returns true. If, however, the
2880 // original contents of the file did not exactly match
2881 // old_contents, then the file is not modified, and
2882 // false is returned. In either case, orig_contents is
2883 // filled with the original contents of the file.
2884 //
2885 // If the file does not exist, it is implicitly created,
2886 // and its original contents are empty.
2887 //
2888 // If an I/O error occurs on write, some of the file may
2889 // or may not have been written, and false is returned.
2890 //
2891 // Expressed in pseudo-code, the logic is:
2892 //
2893 // orig_contents = file.read();
2894 // if (orig_contents == old_contents) {
2895 // file.write(new_contents);
2896 // return true;
2897 // }
2898 // return false;
2899 //
2900 // The operation is guaranteed to be atomic only if the
2901 // only operations that read and write to this file are
2902 // atomic_compare_and_exchange_contents() and
2903 // atomic_read_contents().
2904 ////////////////////////////////////////////////////////////////////
2905 bool Filename::
2907  const string &old_contents,
2908  const string &new_contents) const {
2909 #ifdef WIN32_VC
2910  wstring os_specific = to_os_specific_w();
2911  HANDLE hfile = CreateFileW(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE,
2912  0, NULL, OPEN_ALWAYS,
2913  FILE_ATTRIBUTE_NORMAL, NULL);
2914  while (hfile == INVALID_HANDLE_VALUE) {
2915  DWORD error = GetLastError();
2916  if (error == ERROR_SHARING_VIOLATION) {
2917  // If the file is locked by another process, yield and try again.
2918  Sleep(0);
2919  hfile = CreateFileW(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE,
2920  0, NULL, OPEN_ALWAYS,
2921  FILE_ATTRIBUTE_NORMAL, NULL);
2922  } else {
2923  cerr << "Couldn't open file: " << os_specific
2924  << ", error " << error << "\n";
2925  return false;
2926  }
2927  }
2928 
2929  if (hfile == INVALID_HANDLE_VALUE) {
2930  cerr << "Couldn't open file: " << os_specific
2931  << ", error " << GetLastError() << "\n";
2932  return false;
2933  }
2934 
2935  static const size_t buf_size = 512;
2936  char buf[buf_size];
2937 
2938  orig_contents = string();
2939 
2940  DWORD bytes_read;
2941  if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
2942  cerr << "Error reading file: " << os_specific
2943  << ", error " << GetLastError() << "\n";
2944  CloseHandle(hfile);
2945  return false;
2946  }
2947  while (bytes_read > 0) {
2948  orig_contents += string(buf, bytes_read);
2949 
2950  if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
2951  cerr << "Error reading file: " << os_specific
2952  << ", error " << GetLastError() << "\n";
2953  CloseHandle(hfile);
2954  return false;
2955  }
2956  }
2957 
2958  bool match = false;
2959  if (orig_contents == old_contents) {
2960  match = true;
2961  SetFilePointer(hfile, 0, 0, FILE_BEGIN);
2962  DWORD bytes_written;
2963  if (!WriteFile(hfile, new_contents.data(), new_contents.size(),
2964  &bytes_written, NULL)) {
2965  cerr << "Error writing file: " << os_specific
2966  << ", error " << GetLastError() << "\n";
2967  CloseHandle(hfile);
2968  return false;
2969  }
2970  }
2971 
2972  CloseHandle(hfile);
2973  return match;
2974 
2975 #else // WIN32_VC
2976  string os_specific = to_os_specific();
2977  int fd = open(os_specific.c_str(), O_RDWR | O_CREAT, 0666);
2978  if (fd < 0) {
2979  perror(os_specific.c_str());
2980  return false;
2981  }
2982 
2983  static const size_t buf_size = 512;
2984  char buf[buf_size];
2985 
2986  orig_contents = string();
2987 
2988 #ifdef HAVE_LOCKF
2989  if (lockf(fd, F_LOCK, 0) != 0) {
2990 #else
2991  if (flock(fd, LOCK_EX) != 0) {
2992 #endif
2993  perror(os_specific.c_str());
2994  close(fd);
2995  return false;
2996  }
2997 
2998  ssize_t bytes_read = read(fd, buf, buf_size);
2999  while (bytes_read > 0) {
3000  orig_contents += string(buf, bytes_read);
3001  bytes_read = read(fd, buf, buf_size);
3002  }
3003 
3004  if (bytes_read < 0) {
3005  perror(os_specific.c_str());
3006  close(fd);
3007  return false;
3008  }
3009 
3010  bool match = false;
3011  if (orig_contents == old_contents) {
3012  match = true;
3013  lseek(fd, 0, SEEK_SET);
3014  ssize_t bytes_written = write(fd, new_contents.data(), new_contents.size());
3015  if (bytes_written < 0) {
3016  perror(os_specific.c_str());
3017  close(fd);
3018  return false;
3019  }
3020  }
3021 
3022  if (close(fd) < 0) {
3023  perror(os_specific.c_str());
3024  return false;
3025  }
3026 
3027  return match;
3028 #endif // WIN32_VC
3029 }
3030 
3031 ////////////////////////////////////////////////////////////////////
3032 // Function: Filename::atomic_read_contents
3033 // Access: Public
3034 // Description: Uses native file-locking mechanisms to atomically
3035 // read the contents of a (small) file. This is the
3036 // only way to read a file protected by
3037 // atomic_compare_and_exchange_contents(), and be
3038 // confident that the read operation is actually atomic
3039 // with respect to that method.
3040 //
3041 // If the file does not exist, it is implicitly created,
3042 // and its contents are empty.
3043 //
3044 // If the file is read successfully, fills its contents
3045 // in the indicated string, and returns true. If the
3046 // file cannot be read, returns false.
3047 ////////////////////////////////////////////////////////////////////
3048 bool Filename::
3049 atomic_read_contents(string &contents) const {
3050 #ifdef WIN32_VC
3051  wstring os_specific = to_os_specific_w();
3052  HANDLE hfile = CreateFileW(os_specific.c_str(), GENERIC_READ,
3053  FILE_SHARE_READ, NULL, OPEN_ALWAYS,
3054  FILE_ATTRIBUTE_NORMAL, NULL);
3055  while (hfile == INVALID_HANDLE_VALUE) {
3056  DWORD error = GetLastError();
3057  if (error == ERROR_SHARING_VIOLATION) {
3058  // If the file is locked by another process, yield and try again.
3059  Sleep(0);
3060  hfile = CreateFileW(os_specific.c_str(), GENERIC_READ,
3061  FILE_SHARE_READ, NULL, OPEN_ALWAYS,
3062  FILE_ATTRIBUTE_NORMAL, NULL);
3063  } else {
3064  cerr << "Couldn't open file: " << os_specific
3065  << ", error " << error << "\n";
3066  return false;
3067  }
3068  }
3069 
3070  static const size_t buf_size = 512;
3071  char buf[buf_size];
3072 
3073  contents = string();
3074 
3075  DWORD bytes_read;
3076  if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
3077  cerr << "Error reading file: " << os_specific
3078  << ", error " << GetLastError() << "\n";
3079  CloseHandle(hfile);
3080  return false;
3081  }
3082  while (bytes_read > 0) {
3083  contents += string(buf, bytes_read);
3084 
3085  if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
3086  cerr << "Error reading file: " << os_specific
3087  << ", error " << GetLastError() << "\n";
3088  CloseHandle(hfile);
3089  return false;
3090  }
3091  }
3092 
3093  CloseHandle(hfile);
3094  return true;
3095 
3096 #else // WIN32_VC
3097  string os_specific = to_os_specific();
3098  int fd = open(os_specific.c_str(), O_RDWR | O_CREAT, 0666);
3099  if (fd < 0) {
3100  perror(os_specific.c_str());
3101  return false;
3102  }
3103 
3104  static const size_t buf_size = 512;
3105  char buf[buf_size];
3106 
3107  contents = string();
3108 
3109 #ifdef HAVE_LOCKF
3110  if (lockf(fd, F_LOCK, 0) != 0) {
3111 #else
3112  if (flock(fd, LOCK_EX) != 0) {
3113 #endif
3114  perror(os_specific.c_str());
3115  close(fd);
3116  return false;
3117  }
3118 
3119  ssize_t bytes_read = read(fd, buf, buf_size);
3120  while (bytes_read > 0) {
3121  contents += string(buf, bytes_read);
3122  bytes_read = read(fd, buf, buf_size);
3123  }
3124 
3125  if (bytes_read < 0) {
3126  perror(os_specific.c_str());
3127  close(fd);
3128  return false;
3129  }
3130 
3131  close(fd);
3132  return true;
3133 #endif // WIN32_VC
3134 }
3135 
3136 ////////////////////////////////////////////////////////////////////
3137 // Function: Filename::locate_basename
3138 // Access: Protected
3139 // Description: After the string has been reassigned, search for the
3140 // slash marking the beginning of the basename, and set
3141 // _dirname_end and _basename_start correctly.
3142 ////////////////////////////////////////////////////////////////////
3143 void Filename::
3144 locate_basename() {
3145  // Scan for the last slash, which marks the end of the directory
3146  // part.
3147  if (_filename.empty()) {
3148  _dirname_end = 0;
3149  _basename_start = 0;
3150 
3151  } else {
3152 
3153  string::size_type slash = _filename.rfind('/');
3154  if (slash != string::npos) {
3155  _basename_start = slash + 1;
3156  _dirname_end = _basename_start;
3157 
3158  // One exception: in case there are multiple slashes in a row,
3159  // we want to treat them as a single slash. The directory
3160  // therefore actually ends at the first of these; back up a bit.
3161  while (_dirname_end > 0 && _filename[_dirname_end-1] == '/') {
3162  _dirname_end--;
3163  }
3164 
3165  // Another exception: if the dirname was nothing but slashes, it
3166  // was the root directory, or / itself. In this case the dirname
3167  // does include the terminal slash (of course).
3168  if (_dirname_end == 0) {
3169  _dirname_end = 1;
3170  }
3171 
3172  } else {
3173  _dirname_end = 0;
3174  _basename_start = 0;
3175  }
3176  }
3177 
3178  // Now:
3179 
3180  // _dirname_end is the last slash character, or 0 if there are no
3181  // slash characters.
3182 
3183  // _basename_start is the character after the last slash character,
3184  // or 0 if there are no slash characters.
3185 }
3186 
3187 
3188 ////////////////////////////////////////////////////////////////////
3189 // Function: Filename::locate_extension
3190 // Access: Protected
3191 // Description: Once the end of the directory prefix has been found,
3192 // and _dirname_end and _basename_start are set
3193 // correctly, search for the dot marking the beginning
3194 // of the extension, and set _basename_end and
3195 // _extension_start correctly.
3196 ////////////////////////////////////////////////////////////////////
3197 void Filename::
3198 locate_extension() {
3199  // Now scan for the last dot after that slash.
3200  if (_filename.empty()) {
3201  _basename_end = string::npos;
3202  _extension_start = string::npos;
3203 
3204  } else {
3205  string::size_type dot = _filename.length() - 1;
3206 
3207  while (dot+1 > _basename_start && _filename[dot] != '.') {
3208  --dot;
3209  }
3210 
3211  if (dot+1 > _basename_start) {
3212  _basename_end = dot;
3213  _extension_start = dot + 1;
3214  } else {
3215  _basename_end = string::npos;
3216  _extension_start = string::npos;
3217  }
3218  }
3219 
3220  // Now:
3221 
3222  // _basename_end is the last dot, or npos if there is no dot.
3223 
3224  // _extension_start is the character after the last dot, or npos if
3225  // there is no dot.
3226 }
3227 
3228 ////////////////////////////////////////////////////////////////////
3229 // Function: Filename::locate_hash
3230 // Access: Protected
3231 // Description: Identifies the part of the filename that contains the
3232 // sequence of hash marks, if any.
3233 ////////////////////////////////////////////////////////////////////
3234 void Filename::
3235 locate_hash() {
3236  if (!get_pattern()) {
3237  // If it's not a pattern-type filename, these are always set to
3238  // the end of the string.
3239  _hash_end = string::npos;
3240  _hash_start = string::npos;
3241 
3242  } else {
3243  // If it is a pattern-type filename, we must search for the hash
3244  // marks, which could be anywhere (but are usually toward the
3245  // end).
3246  _hash_end = _filename.rfind('#');
3247  if (_hash_end == string::npos) {
3248  _hash_end = string::npos;
3249  _hash_start = string::npos;
3250 
3251  } else {
3252  _hash_start = _hash_end;
3253  ++_hash_end;
3254  while (_hash_start > 0 && _filename[_hash_start - 1] == '#') {
3255  --_hash_start;
3256  }
3257  }
3258  }
3259 }
3260 
3261 
3262 ////////////////////////////////////////////////////////////////////
3263 // Function: Filename::get_common_prefix
3264 // Access: Protected
3265 // Description: Returns the length of the longest common initial
3266 // substring of this string and the other one that ends
3267 // in a slash. This is the lowest directory common to
3268 // both filenames.
3269 ////////////////////////////////////////////////////////////////////
3270 size_t Filename::
3271 get_common_prefix(const string &other) const {
3272  size_t len = 0;
3273 
3274  // First, get the length of the common initial substring.
3275  while (len < length() && len < other.length() &&
3276  _filename[len] == other[len]) {
3277  len++;
3278  }
3279 
3280  // Now insist that it ends in a slash.
3281  while (len > 0 && _filename[len-1] != '/') {
3282  len--;
3283  }
3284 
3285  return len;
3286 }
3287 
3288 ////////////////////////////////////////////////////////////////////
3289 // Function: Filename::count_slashes
3290 // Access: Protected, Static
3291 // Description: Returns the number of non-consecutive slashes in the
3292 // indicated string, not counting a terminal slash.
3293 ////////////////////////////////////////////////////////////////////
3294 int Filename::
3295 count_slashes(const string &str) {
3296  int count = 0;
3297  string::const_iterator si;
3298  si = str.begin();
3299 
3300  while (si != str.end()) {
3301  if (*si == '/') {
3302  count++;
3303 
3304  // Skip consecutive slashes.
3305  ++si;
3306  while (*si == '/') {
3307  ++si;
3308  }
3309  if (si == str.end()) {
3310  // Oops, that was a terminal slash. Don't count it.
3311  count--;
3312  }
3313 
3314  } else {
3315  ++si;
3316  }
3317  }
3318 
3319  return count;
3320 }
3321 
3322 
3323 ////////////////////////////////////////////////////////////////////
3324 // Function: Filename::r_make_canonical
3325 // Access: Protected
3326 // Description: The recursive implementation of make_canonical().
3327 ////////////////////////////////////////////////////////////////////
3328 bool Filename::
3329 r_make_canonical(const Filename &cwd) {
3330  if (get_fullpath() == "/") {
3331  // If we reached the root, the whole path doesn't exist. Report
3332  // failure.
3333  return false;
3334  }
3335 
3336 #ifdef WIN32_VC
3337  // First, try to cd to the filename directly.
3338  wstring os_specific = to_os_specific_w();
3339  if (_wchdir(os_specific.c_str()) >= 0) {
3340  // That worked, save the full path string.
3341  (*this) = ExecutionEnvironment::get_cwd();
3342 
3343  // And restore the current working directory.
3344  wstring osdir = cwd.to_os_specific_w();
3345  if (_wchdir(osdir.c_str()) < 0) {
3346  cerr << "Error! Cannot change back to " << osdir << "\n";
3347  }
3348  return true;
3349  }
3350 #else // WIN32_VC
3351  // First, try to cd to the filename directly.
3352  string os_specific = to_os_specific();
3353  if (::chdir(os_specific.c_str()) >= 0) {
3354  // That worked, save the full path string.
3355  (*this) = ExecutionEnvironment::get_cwd();
3356 
3357  // And restore the current working directory.
3358  string osdir = cwd.to_os_specific();
3359  if (::chdir(osdir.c_str()) < 0) {
3360  cerr << "Error! Cannot change back to " << osdir << "\n";
3361  }
3362  return true;
3363  }
3364 #endif // WIN32_VC
3365 
3366  // That didn't work; maybe it's not a directory. Recursively go to
3367  // the directory above.
3368 
3369  Filename dir(get_dirname());
3370 
3371  if (dir.empty()) {
3372  // No dirname means the file is in this directory.
3373  set_dirname(cwd);
3374  return true;
3375  }
3376 
3377  if (!dir.r_make_canonical(cwd)) {
3378  return false;
3379  }
3380  set_dirname(dir);
3381  return true;
3382 }
3383 
const Filename & get_directory(int n) const
Returns the nth directory on the search list.
bool mkdir() const
Creates the directory named by this filename.
Definition: filename.cxx:2789
bool open_append(ofstream &stream) const
Opens the indicated ofstream for writing the file, if possible.
Definition: filename.cxx:2101
string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:398
void set_hash_to_end(const string &s)
Replaces the part of the filename from the beginning of the hash sequence to the end of the filename...
Definition: filename.cxx:895
void set_extension(const string &s)
Replaces the file extension.
Definition: filename.cxx:837
bool atomic_read_contents(string &contents) const
Uses native file-locking mechanisms to atomically read the contents of a (small) file.
Definition: filename.cxx:3049
bool make_dir() const
Creates all the directories in the path to the file specified in the filename, except for the basenam...
Definition: filename.cxx:2730
const wstring & get_wtext() const
Returns the text associated with the TextEncoder, as a wide-character string.
Definition: textEncoder.I:530
bool unlink() const
Permanently deletes the file associated with the filename, if possible.
Definition: filename.cxx:2554
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1456
static Pointer get_ptr(const Pointer &var)
Atomically retrieves the snapshot value of the indicated variable.
void set_fullpath(const string &s)
Replaces the entire filename: directory, basename, extension.
Definition: filename.cxx:714
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:37
void set_type(Type type)
Sets the type of the file represented by the filename.
Definition: filename.I:564
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:494
bool chdir() const
Changes directory to the specified location.
Definition: filename.cxx:2535
bool open_read(ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:2003
string get_text() const
Returns the current text, as encoded via the current encoding system.
Definition: textEncoder.I:167
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
Definition: filename.cxx:1072
bool make_true_case()
On a case-insensitive operating system (e.g.
Definition: filename.cxx:1120
bool open_read_write(fstream &stream, bool truncate=false) const
Opens the indicated fstream for read/write access to the file, if possible.
Definition: filename.cxx:2143
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
void set_basename_wo_extension(const string &s)
Replaces the basename part of the filename, without the file extension.
Definition: filename.cxx:813
string to_os_short_name() const
This works like to_os_generic(), but it returns the &quot;short name&quot; version of the filename, if it exists, or the original filename otherwise.
Definition: filename.cxx:1285
static Filename expand_from(const string &user_string, Type type=T_general)
Returns the same thing as from_os_specific(), but embedded environment variable references (e...
Definition: filename.cxx:418
static int unicode_tolower(int character)
Returns the uppercase equivalent of the given Unicode character.
Definition: textEncoder.I:447
bool scan_directory(vector_string &contents) const
Attempts to open the named filename as if it were a directory and looks for the non-hidden files with...
Definition: filename.cxx:1854
bool is_local() const
Returns true if the filename is local, e.g.
Definition: filename.I:664
void standardize()
Converts the filename to standard form by replacing consecutive slashes with a single slash...
Definition: filename.cxx:945
int get_num_directories() const
Returns the number of directories on the search list.
bool resolve_filename(const DSearchPath &searchpath, const string &default_extension=string())
Searches the given search path for the filename.
Definition: filename.cxx:1699
bool copy_to(const Filename &other) const
Copies the file to the indicated new filename, by reading the contents and writing it to the new file...
Definition: filename.cxx:2680
static Filename temporary(const string &dirname, const string &prefix, const string &suffix=string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix...
Definition: filename.cxx:439
wstring to_os_specific_w() const
The wide-string variant on to_os_specific().
Definition: filename.cxx:1238
void set_fullpath_wo_extension(const string &s)
Replaces the full filename–directory and basename parts–except for the extension. ...
Definition: filename.cxx:793
bool is_writable() const
Returns true if the filename exists and is either a directory or a regular file that can be written t...
Definition: filename.cxx:1422
static const Filename & get_home_directory()
Returns a path to the user&#39;s home directory, if such a thing makes sense in the current OS...
Definition: filename.cxx:488
Type get_type() const
Returns the type of the file represented by the filename, as previously set by set_type().
Definition: filename.I:583
static TextEncoder::Encoding get_filesystem_encoding()
Specifies the default encoding to be used for all subsequent Filenames objects.
Definition: filename.I:778
bool rename_to(const Filename &other) const
Renames the file to the indicated new filename.
Definition: filename.cxx:2577
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
string to_os_generic() const
This is similar to to_os_specific(), but it is designed to generate a filename that can be understood...
Definition: filename.cxx:1261
static const Filename & get_user_appdata_directory()
Returns a path to a system-defined directory appropriate for creating a subdirectory for storing appl...
Definition: filename.cxx:609
void set_dirname(const string &s)
Replaces the directory part of the filename.
Definition: filename.cxx:726
static const Filename & get_temp_directory()
Returns a path to a system-defined temporary directory.
Definition: filename.cxx:556
string to_os_long_name() const
This is the opposite of to_os_short_name(): it returns the &quot;long name&quot; of the filename, if the filename exists.
Definition: filename.cxx:1322
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1196
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1356
bool make_relative_to(Filename directory, bool allow_backups=true)
Adjusts this filename, which must be a fully-specified pathname beginning with a slash, to make it a relative filename, relative to the fully-specified directory indicated (which must also begin with, and may or may not end with, a slash–a terminating slash is ignored).
Definition: filename.cxx:1766
bool touch() const
Updates the modification time of the file to the current time.
Definition: filename.cxx:2454
int compare_timestamps(const Filename &other, bool this_missing_is_old=true, bool other_missing_is_old=true) const
Returns a number less than zero if the file named by this object is older than the given file...
Definition: filename.cxx:1521
bool open_read_append(fstream &stream) const
Opens the indicated ifstream for reading and writing the file, if possible; writes are appended to th...
Definition: filename.cxx:2196
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname)...
Definition: filename.cxx:1019
bool is_executable() const
Returns true if the filename exists and is executable.
Definition: filename.cxx:1487
void extract_components(vector_string &components) const
Extracts out the individual directory components of the path into a series of strings.
Definition: filename.cxx:914
int find_on_searchpath(const DSearchPath &searchpath)
Performs the reverse of the resolve_filename() operation: assuming that the current filename is fully...
Definition: filename.cxx:1820
void set_encoding(Encoding encoding)
Specifies how the string set via set_text() is to be interpreted.
Definition: textEncoder.I:59
Filename find_file(const Filename &filename) const
Searches all the directories in the search list for the indicated file, in order. ...
static const Filename & get_common_appdata_directory()
Returns a path to a system-defined directory appropriate for creating a subdirectory for storing appl...
Definition: filename.cxx:662
streamsize get_file_size() const
Returns the size of the file in bytes, or 0 if there is an error.
Definition: filename.cxx:1665
int get_hash() const
Returns a hash code that attempts to be mostly unique for different Filenames.
Definition: filename.cxx:2835
static Filename from_os_specific_w(const wstring &os_specific, Type type=T_general)
The wide-string variant of from_os_specific().
Definition: filename.cxx:401
This class stores a list of directories that can be searched, in order, to locate a particular file...
Definition: dSearchPath.h:32
time_t get_access_timestamp() const
Returns a time_t value that represents the time the file was last accessed, if this information is av...
Definition: filename.cxx:1636
bool is_text() const
Returns true if the Filename has been indicated to represent a text file via a previous call to set_t...
Definition: filename.I:548
Filename get_filename_index(int index) const
If the pattern flag is set for this Filename and the filename string actually includes a sequence of ...
Definition: filename.cxx:873
static Filename get_cwd()
Returns the name of the current working directory.
void set_text(const string &text)
Changes the text that is stored in the encoder.
Definition: textEncoder.I:112
bool is_regular_file() const
Returns true if the filename exists and is the name of a regular file (i.e.
Definition: filename.cxx:1389
bool open_write(ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:2045
static string expand_string(const string &str)
Reads the string, looking for environment variable names marked by a $.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
void set_wtext(const wstring &wtext)
Changes the text that is stored in the encoder.
Definition: textEncoder.I:516
bool get_pattern() const
Returns the flag indicating whether this is a filename pattern.
Definition: filename.I:624
string get_extension() const
Returns the file extension.
Definition: filename.I:477
time_t get_timestamp() const
Returns a time_t value that represents the time the file was last modified, to within whatever precis...
Definition: filename.cxx:1605
wstring get_fullpath_w() const
Returns the entire filename as a wide-character string.
Definition: filename.I:409
bool rmdir() const
The inverse of mkdir(): this removes the directory named by this Filename, if it is in fact a directo...
Definition: filename.cxx:2808
bool atomic_compare_and_exchange_contents(string &orig_contents, const string &old_contents, const string &new_contents) const
Uses native file-locking mechanisms to atomically replace the contents of a (small) file with the spe...
Definition: filename.cxx:2906
void set_pattern(bool pattern)
Sets the flag indicating whether this is a filename pattern.
Definition: filename.I:606
static Pointer compare_and_exchange_ptr(Pointer &mem, Pointer old_value, Pointer new_value)
Atomic compare and exchange.
bool is_binary_or_text() const
Returns true either is_binary() or is_text() is true; that is, that the filename has been specified a...
Definition: filename.I:534
void set_basename(const string &s)
Replaces the basename part of the filename.
Definition: filename.cxx:779
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:332