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