16 #include "filename_assist.h"
17 #include "dSearchPath.h"
18 #include "executionEnvironment.h"
19 #include "vector_string.h"
20 #include "atomicAdjust.h"
38 #define GLOB_NOMATCH -3
52 TextEncoder::Encoding Filename::_filesystem_encoding = TextEncoder::E_utf8;
54 TVOLATILE AtomicAdjust::Pointer Filename::_home_directory;
55 TVOLATILE AtomicAdjust::Pointer Filename::_temp_directory;
56 TVOLATILE AtomicAdjust::Pointer Filename::_user_appdata_directory;
57 TVOLATILE AtomicAdjust::Pointer Filename::_common_appdata_directory;
61 string Filename::_internal_data_dir;
76 #ifndef FILE_ATTRIBUTE_DEVICE
77 #define FILE_ATTRIBUTE_DEVICE 0x00000040
86 extern "C" void cygwin_conv_to_win32_path(
const char *path,
char *win32);
87 extern "C" void cygwin_conv_to_posix_path(
const char *path,
char *posix);
113 static const char *hosts_prefix =
"/hosts/";
114 static size_t hosts_prefix_length = 7;
117 front_to_back_slash(
const string &str) {
120 for (si = result.begin(); si != result.end(); ++si) {
130 back_to_front_slash(
const string &str) {
133 for (si = result.begin(); si != result.end(); ++si) {
142 static const string &
144 static string *panda_root = NULL;
146 if (panda_root == NULL) {
147 panda_root =
new string;
148 const char *envvar = getenv(
"PANDA_ROOT");
149 if (envvar != (
const char *)NULL) {
150 (*panda_root) = front_to_back_slash(envvar);
157 if ((*panda_root).empty() || (*panda_root)[(*panda_root).length() - 1] !=
'\\') {
158 (*panda_root) +=
'\\';
162 return (*panda_root);
166 convert_pathname(
const string &unix_style_pathname) {
167 if (unix_style_pathname.empty()) {
183 string windows_pathname;
185 if (unix_style_pathname[0] !=
'/') {
189 windows_pathname = front_to_back_slash(unix_style_pathname);
191 }
else if (unix_style_pathname.length() >= 2 &&
192 isalpha(unix_style_pathname[1]) &&
193 (unix_style_pathname.length() == 2 || unix_style_pathname[2] ==
'/')) {
197 string remainder = unix_style_pathname.substr(2);
198 if (remainder.empty()) {
202 remainder = front_to_back_slash(remainder);
209 string(1, (
char)toupper(unix_style_pathname[1])) +
":" + remainder;
211 }
else if (unix_style_pathname.length() > hosts_prefix_length &&
212 unix_style_pathname.substr(0, hosts_prefix_length) == hosts_prefix) {
214 windows_pathname =
"\\\\" + front_to_back_slash(unix_style_pathname.substr(hosts_prefix_length));
222 char result[4096] =
"";
223 cygwin_conv_to_win32_path(unix_style_pathname.c_str(), result);
224 windows_pathname = result;
228 windows_pathname = get_panda_root();
229 windows_pathname += front_to_back_slash(unix_style_pathname.substr(1));
231 #endif // HAVE_CYGWIN
234 return windows_pathname;
238 convert_dso_pathname(
const string &unix_style_pathname) {
240 size_t dot = unix_style_pathname.rfind(
'.');
241 if (dot == string::npos ||
242 unix_style_pathname.find(
'/', dot) != string::npos) {
244 return convert_pathname(unix_style_pathname);
246 if (unix_style_pathname.substr(dot) !=
".so") {
248 return convert_pathname(unix_style_pathname);
251 string dll_basename = unix_style_pathname.substr(0, dot);
265 return convert_pathname(dll_basename +
"_d.dll");
267 return convert_pathname(dll_basename +
".dll");
272 convert_executable_pathname(
const string &unix_style_pathname) {
274 size_t dot = unix_style_pathname.rfind(
'.');
275 if (dot == string::npos ||
276 unix_style_pathname.find(
'/', dot) != string::npos) {
278 return convert_pathname(unix_style_pathname +
".exe");
280 if (unix_style_pathname.substr(dot) !=
".exe") {
282 return convert_pathname(unix_style_pathname +
".exe");
285 return convert_pathname(unix_style_pathname);
298 if (dirname.empty()) {
301 _flags = basename._flags;
303 if (dirpath[dirpath.length() - 1] ==
'/') {
334 string result = back_to_front_slash(os_specific);
335 const string &panda_root = get_panda_root();
338 if (!panda_root.empty() && panda_root != string(
"\\") &&
339 panda_root.length() < result.length()) {
342 for (p = 0; p < panda_root.length() && matches; ++p) {
343 char c = tolower(panda_root[p]);
347 matches = (c == tolower(result[p]));
353 result = result.substr(panda_root.length());
354 assert(!result.empty());
355 if (result[0] !=
'/') {
356 result =
'/' + result;
366 if (result.size() >= 3 && isalpha(result[0]) &&
367 result[1] ==
':' && result[2] ==
'/') {
368 result[1] = tolower(result[0]);
373 if (result.size() == 3) {
374 result = result.substr(0, 2);
377 }
else if (result.substr(0, 2) ==
"//") {
379 result = hosts_prefix + result.substr(2);
439 temporary(
const string &dirname,
const string &prefix,
const string &suffix,
442 #if defined(_WIN32) || defined(ANDROID)
445 if (fdirname.empty()) {
450 if (fdirname.empty()) {
453 char *name = tempnam(NULL, prefix.c_str());
456 result.set_type(type);
469 int hash = (clock() * time(NULL)) & 0xffffff;
471 sprintf(hex_code,
"%06x", hash);
474 }
while (result.
exists());
493 char *home = getenv(
"HOME");
494 if (home != (
char *)NULL) {
498 home_directory = dirname;
503 if (home_directory.empty()) {
505 wchar_t buffer[MAX_PATH];
508 if (SHGetSpecialFolderPathW(NULL, buffer, CSIDL_PERSONAL,
true)) {
512 home_directory = dirname;
517 #elif defined(ANDROID)
519 home_directory =
"/data/data/org.panda3d.sdk";
521 #elif defined(IS_OSX)
522 home_directory = get_osx_home_directory();
524 #elif defined(ANDROID)
525 home_directory = _internal_data_dir;
533 if (home_directory.empty()) {
541 assert(_home_directory != NULL);
546 return (*(
Filename *)_home_directory);
561 static const size_t buffer_size = 4096;
562 wchar_t buffer[buffer_size];
563 if (GetTempPathW(buffer_size, buffer) != 0) {
567 temp_directory = dirname;
572 #elif defined(IS_OSX)
573 temp_directory = get_osx_temp_directory();
575 #elif defined(ANDROID)
581 temp_directory =
"/tmp";
584 if (temp_directory.empty()) {
592 assert(_temp_directory != NULL);
597 return (*(
Filename *)_temp_directory);
614 wchar_t buffer[MAX_PATH];
616 if (SHGetSpecialFolderPathW(NULL, buffer, CSIDL_LOCAL_APPDATA,
true)) {
620 user_appdata_directory = dirname;
625 #elif defined(IS_OSX)
626 user_appdata_directory = get_osx_user_appdata_directory();
628 #elif defined(ANDROID)
629 user_appdata_directory.
set_dirname(_internal_data_dir);
638 if (user_appdata_directory.empty()) {
646 assert(_user_appdata_directory != NULL);
651 return (*(
Filename *)_user_appdata_directory);
667 wchar_t buffer[MAX_PATH];
669 if (SHGetSpecialFolderPathW(NULL, buffer, CSIDL_COMMON_APPDATA,
true)) {
673 common_appdata_directory = dirname;
678 #elif defined(IS_OSX)
679 common_appdata_directory = get_osx_common_appdata_directory();
681 #elif defined(ANDROID)
682 common_appdata_directory.
set_dirname(_internal_data_dir);
687 common_appdata_directory =
"/var";
690 if (common_appdata_directory.empty()) {
698 assert(_common_appdata_directory != NULL);
703 return (*(
Filename *)_common_appdata_directory);
729 _filename.replace(0, _basename_start,
"");
731 int length_change = - ((int)_basename_start);
734 _basename_start += length_change;
735 _basename_end += length_change;
736 _extension_start += length_change;
743 if (s[s.length()-1] ==
'/') {
749 int length_change = ss.length() - _basename_start;
751 _filename.replace(0, _basename_start, ss);
753 _dirname_end = ss.length() - 1;
757 if (ss.length() == 1) {
761 _basename_start += length_change;
763 if (_basename_end != string::npos) {
764 _basename_end += length_change;
765 _extension_start += length_change;
780 _filename.replace(_basename_start, string::npos, s);
794 int length_change = s.length() - _basename_end;
796 _filename.replace(0, _basename_end, s);
798 if (_basename_end != string::npos) {
799 _basename_end += length_change;
800 _extension_start += length_change;
814 int length_change = s.length() - (_basename_end - _basename_start);
816 if (_basename_end == string::npos) {
817 _filename.replace(_basename_start, string::npos, s);
820 _filename.replace(_basename_start, _basename_end - _basename_start, s);
822 _basename_end += length_change;
823 _extension_start += length_change;
840 if (_basename_end != string::npos) {
841 _filename.replace(_basename_end, string::npos,
"");
842 _basename_end = string::npos;
843 _extension_start = string::npos;
846 }
else if (_basename_end == string::npos) {
848 _basename_end = _filename.length();
849 _extension_start = _filename.length() + 1;
850 _filename +=
'.' + s;
854 _filename.replace(_extension_start, string::npos, s);
876 if (_hash_end != _hash_start) {
878 strm << _filename.substr(0, _hash_start)
879 << setw(_hash_end - _hash_start) << setfill(
'0') << index
880 << _filename.substr(_hash_end);
896 _filename.replace(_hash_start, string::npos, s);
918 if (!_filename.empty() && _filename[0] ==
'/') {
922 while (p < _filename.length()) {
923 size_t q = _filename.find(
'/', p);
924 if (q == string::npos) {
925 components.push_back(_filename.substr(p));
928 components.push_back(_filename.substr(p, q - p));
933 components.push_back(
string());
946 assert(!_filename.empty());
947 if (_filename ==
".") {
952 vector_string components;
955 bool global = (_filename[0] ==
'/');
958 while (p < _filename.length() && _filename[p] ==
'/') {
961 while (p < _filename.length()) {
962 size_t slash = _filename.find(
'/', p);
963 string component = _filename.substr(p, slash - p);
964 if (component ==
"." && p != 0) {
966 }
else if (component ==
".." && !components.empty() &&
967 !(components.back() ==
"..")) {
968 if (components.back() ==
".") {
971 components.pop_back();
972 components.push_back(component);
975 components.pop_back();
978 components.push_back(component);
982 while (p < _filename.length() && _filename[p] ==
'/') {
992 if (!components.empty()) {
993 result += components[0];
994 for (
int i = 1; i < (int)components.size(); i++) {
995 result +=
"/" + components[i];
1039 Filename new_filename(start_directory, _filename);
1040 new_filename._flags = _flags;
1041 (*this) = new_filename;
1086 char newpath [PATH_MAX + 1];
1087 if (realpath(c_str(), newpath) != NULL) {
1089 newpath_fn._flags = _flags;
1090 (*this) = newpath_fn;
1095 if (!r_make_canonical(cwd)) {
1134 wchar_t short_name[MAX_PATH + 1];
1135 DWORD l = GetShortPathNameW(os_specific.c_str(), short_name, MAX_PATH + 1);
1144 assert(l < MAX_PATH + 1);
1146 wchar_t long_name[MAX_PATH + 1];
1147 l = GetLongPathNameW(short_name, long_name, MAX_PATH + 1);
1153 assert(l < MAX_PATH + 1);
1161 bool match = (orig_filename.length() == new_filename.length());
1162 for (
size_t i = 0; i < orig_filename.length() && match; ++i) {
1172 true_case._flags = _flags;
1173 (*this) = true_case;
1208 size_t dot = workname.rfind(
'.');
1209 if (dot != string::npos) {
1210 if (workname.substr(dot) ==
".so") {
1211 string dyLibBase = workname.substr(0, dot)+
".dylib";
1223 return convert_executable_pathname(standard.
get_fullpath());
1228 return standard.c_str();
1291 wchar_t short_name[MAX_PATH + 1];
1292 DWORD l = GetShortPathNameW(os_specific.c_str(), short_name, MAX_PATH + 1);
1301 assert(l < MAX_PATH + 1);
1328 wchar_t long_name[MAX_PATH + 1];
1329 DWORD l = GetLongPathNameW(os_specific.c_str(), long_name, MAX_PATH + 1);
1335 assert(l < MAX_PATH + 1);
1362 DWORD results = GetFileAttributesW(os_specific.c_str());
1363 if (results != -1) {
1370 struct stat this_buf;
1371 bool exists =
false;
1373 if (stat(os_specific.c_str(), &this_buf) == 0) {
1395 DWORD results = GetFileAttributesW(os_specific.c_str());
1396 if (results != -1) {
1397 isreg = ((results & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0);
1403 struct stat this_buf;
1406 if (stat(os_specific.c_str(), &this_buf) == 0) {
1407 isreg = S_ISREG(this_buf.st_mode);
1423 bool writable =
false;
1428 DWORD results = GetFileAttributesW(os_specific.c_str());
1429 if (results != -1) {
1430 if ((results & FILE_ATTRIBUTE_DIRECTORY) != 0) {
1433 }
else if ((results & FILE_ATTRIBUTE_READONLY) == 0) {
1441 if (access(os_specific.c_str(), W_OK) == 0) {
1462 DWORD results = GetFileAttributesW(os_specific.c_str());
1463 if (results != -1) {
1464 isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
1469 struct stat this_buf;
1472 if (stat(os_specific.c_str(), &this_buf) == 0) {
1473 isdir = S_ISDIR(this_buf.st_mode);
1492 if (extension ==
"exe" || extension ==
"com") {
1498 if (access(os_specific.c_str(), X_OK) == 0) {
1522 bool this_missing_is_old,
1523 bool other_missing_is_old)
const {
1528 struct _stat this_buf;
1529 bool this_exists =
false;
1531 if (_wstat(os_specific.c_str(), &this_buf) == 0) {
1535 struct _stat other_buf;
1536 bool other_exists =
false;
1538 if (_wstat(other_os_specific.c_str(), &other_buf) == 0) {
1539 other_exists =
true;
1545 struct stat this_buf;
1546 bool this_exists =
false;
1548 if (stat(os_specific.c_str(), &this_buf) == 0) {
1552 struct stat other_buf;
1553 bool other_exists =
false;
1555 if (stat(other_os_specific.c_str(), &other_buf) == 0) {
1556 other_exists =
true;
1560 if (this_exists && other_exists) {
1562 return (
int)this_buf.st_mtime - (int)other_buf.st_mtime;
1564 }
else if (!this_exists && !other_exists) {
1566 if (this_missing_is_old == other_missing_is_old) {
1570 if (this_missing_is_old) {
1578 }
else if (!this_exists) {
1580 return this_missing_is_old ? -1 : 1;
1584 assert(!other_exists);
1587 return other_missing_is_old ? 1 : -1;
1609 struct _stat this_buf;
1611 if (_wstat(os_specific.c_str(), &this_buf) == 0) {
1612 return this_buf.st_mtime;
1617 struct stat this_buf;
1619 if (stat(os_specific.c_str(), &this_buf) == 0) {
1620 return this_buf.st_mtime;
1640 struct _stat this_buf;
1642 if (_wstat(os_specific.c_str(), &this_buf) == 0) {
1643 return this_buf.st_atime;
1648 struct stat this_buf;
1650 if (stat(os_specific.c_str(), &this_buf) == 0) {
1651 return this_buf.st_atime;
1669 struct _stat64 this_buf;
1674 if (_wstati64(os_specific.c_str(), &this_buf) == 0) {
1675 return this_buf.st_size;
1680 struct stat this_buf;
1682 if (stat(os_specific.c_str(), &this_buf) == 0) {
1683 return this_buf.st_size;
1700 const string &default_extension) {
1706 if (found.empty()) {
1709 if (
get_extension().empty() && !default_extension.empty()) {
1722 if (
get_extension().empty() && !default_extension.empty()) {
1732 if (!found.empty()) {
1767 if (_filename.empty() || directory.empty() ||
1768 _filename[0] !=
'/' || directory[0] !=
'/') {
1775 if (directory ==
"/") {
1782 size_t common = get_common_prefix(rel_to_file);
1789 int slashes = count_slashes(rel_to_file.substr(common));
1790 if (slashes > 0 && !allow_backups) {
1795 for (
int i = 0; i < slashes; i++) {
1798 result += _filename.substr(common);
1821 if (_filename.empty() || _filename[0] !=
'/') {
1826 for (
int i = 0; i < num_directories; i++) {
1857 #if defined(WIN32_VC)
1860 size_t orig_size = contents.size();
1868 WIN32_FIND_DATAW find_data;
1870 HANDLE handle = FindFirstFileW(match.c_str(), &find_data);
1871 if (handle == INVALID_HANDLE_VALUE) {
1872 if (GetLastError() == ERROR_NO_MORE_FILES) {
1882 thread_consider_yield();
1883 wstring filename = find_data.cFileName;
1884 if (filename != L
"." && filename != L
"..") {
1886 contents.push_back(encoder.
get_text());
1888 }
while (FindNextFileW(handle, &find_data));
1890 bool scan_ok = (GetLastError() == ERROR_NO_MORE_FILES);
1893 sort(contents.begin() + orig_size, contents.end());
1896 #elif defined(PHAVE_DIRENT_H)
1899 size_t orig_size = contents.size();
1905 dirname = _filename;
1907 DIR *root = opendir(dirname.c_str());
1908 if (root == (DIR *)NULL) {
1909 if (errno != ENOTDIR) {
1910 perror(dirname.c_str());
1917 while (d != (
struct dirent *)NULL) {
1918 thread_consider_yield();
1919 if (d->d_name[0] !=
'.') {
1920 contents.push_back(d->d_name);
1939 sort(contents.begin() + orig_size, contents.end());
1942 #elif defined(PHAVE_GLOB_H)
1950 }
else if (_filename[_filename.length() - 1] ==
'/') {
1951 dirname = _filename +
"*";
1953 dirname = _filename +
"/*";
1958 int r = glob(dirname.c_str(), GLOB_ERR, NULL, &globbuf);
1965 if (r != GLOB_NOMATCH) {
1966 perror(dirname.c_str());
1976 size_t offset = dirname.size() - 1;
1978 for (
int i = 0; globbuf.gl_pathv[i] != NULL; i++) {
1979 contents.push_back(globbuf.gl_pathv[i] + offset);
2007 ios_openmode open_mode = ios::in;
2009 #ifdef HAVE_IOS_BINARY
2013 open_mode |= ios::binary;
2020 stream.open(os_specific.c_str(), open_mode);
2023 stream.open(os_specific.c_str(), open_mode);
2026 return (!stream.fail());
2049 ios_openmode open_mode = ios::out;
2052 open_mode |= ios::trunc;
2061 open_mode |= ios::in;
2065 #ifdef HAVE_IOS_BINARY
2069 open_mode |= ios::binary;
2076 stream.open(os_specific.c_str(), open_mode);
2079 #ifdef HAVE_OPEN_MASK
2080 stream.open(os_specific.c_str(), open_mode, 0666);
2082 stream.open(os_specific.c_str(), open_mode);
2086 return (!stream.fail());
2105 ios_openmode open_mode = ios::app;
2107 #ifdef HAVE_IOS_BINARY
2111 open_mode |= ios::binary;
2118 stream.open(os_specific.c_str(), open_mode);
2121 #ifdef HAVE_OPEN_MASK
2122 stream.open(os_specific.c_str(), open_mode, 0666);
2124 stream.open(os_specific.c_str(), open_mode);
2128 return (!stream.fail());
2147 ios_openmode open_mode = ios::out | ios::in;
2150 open_mode |= ios::trunc;
2159 #ifdef HAVE_IOS_BINARY
2163 open_mode |= ios::binary;
2170 stream.open(os_specific.c_str(), open_mode);
2173 #ifdef HAVE_OPEN_MASK
2174 stream.open(os_specific.c_str(), open_mode, 0666);
2176 stream.open(os_specific.c_str(), open_mode);
2180 return (!stream.fail());
2200 ios_openmode open_mode = ios::app | ios::in;
2202 #ifdef HAVE_IOS_BINARY
2206 open_mode |= ios::binary;
2213 stream.open(os_specific.c_str(), open_mode);
2216 #ifdef HAVE_OPEN_MASK
2217 stream.open(os_specific.c_str(), open_mode, 0666);
2219 stream.open(os_specific.c_str(), open_mode);
2223 return (!stream.fail());
2226 #ifdef USE_PANDAFILESTREAM
2243 ios_openmode open_mode = ios::in;
2245 #ifdef HAVE_IOS_BINARY
2249 open_mode |= ios::binary;
2255 stream.open(os_specific.c_str(), open_mode);
2256 return (!stream.fail());
2258 #endif // USE_PANDAFILESTREAM
2260 #ifdef USE_PANDAFILESTREAM
2277 open_write(pofstream &stream,
bool truncate)
const {
2281 ios_openmode open_mode = ios::out;
2284 open_mode |= ios::trunc;
2293 open_mode |= ios::in;
2297 #ifdef HAVE_IOS_BINARY
2301 open_mode |= ios::binary;
2307 #ifdef HAVE_OPEN_MASK
2308 stream.open(os_specific.c_str(), open_mode, 0666);
2310 stream.open(os_specific.c_str(), open_mode);
2313 return (!stream.fail());
2315 #endif // USE_PANDAFILESTREAM
2317 #ifdef USE_PANDAFILESTREAM
2334 ios_openmode open_mode = ios::app;
2336 #ifdef HAVE_IOS_BINARY
2340 open_mode |= ios::binary;
2346 #ifdef HAVE_OPEN_MASK
2347 stream.open(os_specific.c_str(), open_mode, 0666);
2349 stream.open(os_specific.c_str(), open_mode);
2352 return (!stream.fail());
2354 #endif // USE_PANDAFILESTREAM
2356 #ifdef USE_PANDAFILESTREAM
2373 ios_openmode open_mode = ios::out | ios::in;
2376 open_mode |= ios::trunc;
2385 #ifdef HAVE_IOS_BINARY
2389 open_mode |= ios::binary;
2395 #ifdef HAVE_OPEN_MASK
2396 stream.open(os_specific.c_str(), open_mode, 0666);
2398 stream.open(os_specific.c_str(), open_mode);
2401 return (!stream.fail());
2403 #endif // USE_PANDAFILESTREAM
2405 #ifdef USE_PANDAFILESTREAM
2423 ios_openmode open_mode = ios::app | ios::in;
2425 #ifdef HAVE_IOS_BINARY
2429 open_mode |= ios::binary;
2435 #ifdef HAVE_OPEN_MASK
2436 stream.open(os_specific.c_str(), open_mode, 0666);
2438 stream.open(os_specific.c_str(), open_mode);
2441 return (!stream.fail());
2443 #endif // USE_PANDAFILESTREAM
2462 fhandle = CreateFileW(os_specific.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
2463 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2464 if (fhandle == INVALID_HANDLE_VALUE) {
2471 GetSystemTime(&sysnow);
2472 if (!SystemTimeToFileTime(&sysnow, &ftnow)) {
2473 CloseHandle(fhandle);
2477 if (!SetFileTime(fhandle, NULL, NULL, &ftnow)) {
2478 CloseHandle(fhandle);
2482 CloseHandle(fhandle);
2485 #elif defined(PHAVE_UTIME_H)
2497 char result[4096] =
"";
2498 cygwin_conv_to_posix_path(os_specific.c_str(), result);
2499 os_specific = result;
2501 #endif // HAVE_CYGWIN
2502 int result = utime(os_specific.c_str(), NULL);
2504 if (errno == ENOENT) {
2506 int fd = creat(os_specific.c_str(), 0666);
2508 perror(os_specific.c_str());
2514 perror(os_specific.c_str());
2518 #else // WIN32, PHAVE_UTIME_H
2525 #endif // WIN32, PHAVE_UTIME_H
2538 return (_wchdir(os_specific.c_str()) >= 0);
2541 return (::
chdir(os_specific.c_str()) >= 0);
2559 _wchmod(os_specific.c_str(), 0644);
2560 return (_wunlink(os_specific.c_str()) == 0);
2563 return (::
unlink(os_specific.c_str()) == 0);
2580 if (*
this == other) {
2589 if (_wrename(os_specific.c_str(),
2590 other_os_specific.c_str()) == 0) {
2599 if (dirname.empty()) {
2604 if (!Filename::binary_filename(*this).
copy_to(temp)) {
2609 if (_wrename(temp_os_specific.c_str(),
2610 other_os_specific.c_str()) == 0) {
2618 if (_wrename(temp_os_specific.c_str(),
2619 other_os_specific.c_str()) == 0) {
2628 if (rename(os_specific.c_str(),
2629 other_os_specific.c_str()) == 0) {
2638 if (dirname.empty()) {
2643 if (!Filename::binary_filename(*this).
copy_to(temp)) {
2648 if (rename(temp_os_specific.c_str(),
2649 other_os_specific.c_str()) == 0) {
2657 if (rename(temp_os_specific.c_str(),
2658 other_os_specific.c_str()) == 0) {
2681 Filename this_filename = Filename::binary_filename(*
this);
2687 Filename other_filename = Filename::binary_filename(other);
2693 static const size_t buffer_size = 4096;
2694 char buffer[buffer_size];
2696 in.read(buffer, buffer_size);
2697 size_t count = in.gcount();
2698 while (count != 0) {
2699 out.write(buffer, count);
2704 in.read(buffer, buffer_size);
2705 count = in.gcount();
2736 if (_filename[_filename.length() - 1] ==
'/') {
2753 size_t slash = dirname.find(
'/');
2754 while (slash != string::npos) {
2755 Filename component(dirname.substr(0, slash));
2758 _wmkdir(os_specific.c_str());
2760 string os_specific = component.to_os_specific();
2761 ::mkdir(os_specific.c_str(), 0777);
2763 slash = dirname.find(
'/', slash + 1);
2770 int result = _wmkdir(os_specific.c_str());
2773 int result =
::mkdir(os_specific.c_str(), 0777);
2776 return (result == 0);
2792 int result = _wmkdir(os_specific.c_str());
2795 int result =
::mkdir(os_specific.c_str(), 0777);
2798 return (result == 0);
2812 int result = _wrmdir(os_specific.c_str());
2816 _wchmod(os_specific.c_str(), 0777);
2817 result = _wrmdir(os_specific.c_str());
2822 int result =
::rmdir(os_specific.c_str());
2825 return (result == 0);
2836 static const int primes[] = {
2837 2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
2838 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
2839 73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
2840 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
2841 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
2842 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
2843 283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
2844 353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
2845 419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
2846 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
2847 547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
2848 607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
2849 661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
2850 739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
2851 811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
2852 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
2853 947, 953, 967, 971, 977, 983, 991, 997
2855 static const size_t num_primes =
sizeof(primes) /
sizeof(
int);
2858 for (
size_t i = 0; i < _filename.size(); ++i) {
2859 hash += (int)_filename[i] * primes[i % num_primes];
2907 const string &old_contents,
2908 const string &new_contents)
const {
2911 HANDLE hfile = CreateFileW(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE,
2912 0, NULL, OPEN_ALWAYS,
2913 FILE_ATTRIBUTE_NORMAL, NULL);
2914 while (hfile == INVALID_HANDLE_VALUE) {
2915 DWORD error = GetLastError();
2916 if (error == ERROR_SHARING_VIOLATION) {
2919 hfile = CreateFileW(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE,
2920 0, NULL, OPEN_ALWAYS,
2921 FILE_ATTRIBUTE_NORMAL, NULL);
2923 cerr <<
"Couldn't open file: " << os_specific
2924 <<
", error " << error <<
"\n";
2929 if (hfile == INVALID_HANDLE_VALUE) {
2930 cerr <<
"Couldn't open file: " << os_specific
2931 <<
", error " << GetLastError() <<
"\n";
2935 static const size_t buf_size = 512;
2938 orig_contents = string();
2941 if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
2942 cerr <<
"Error reading file: " << os_specific
2943 <<
", error " << GetLastError() <<
"\n";
2947 while (bytes_read > 0) {
2948 orig_contents += string(buf, bytes_read);
2950 if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
2951 cerr <<
"Error reading file: " << os_specific
2952 <<
", error " << GetLastError() <<
"\n";
2959 if (orig_contents == old_contents) {
2961 SetFilePointer(hfile, 0, 0, FILE_BEGIN);
2962 DWORD bytes_written;
2963 if (!WriteFile(hfile, new_contents.data(), new_contents.size(),
2964 &bytes_written, NULL)) {
2965 cerr <<
"Error writing file: " << os_specific
2966 <<
", error " << GetLastError() <<
"\n";
2977 int fd = open(os_specific.c_str(), O_RDWR | O_CREAT, 0666);
2979 perror(os_specific.c_str());
2983 static const size_t buf_size = 512;
2986 orig_contents = string();
2989 if (lockf(fd, F_LOCK, 0) != 0) {
2991 if (flock(fd, LOCK_EX) != 0) {
2993 perror(os_specific.c_str());
2998 ssize_t bytes_read = read(fd, buf, buf_size);
2999 while (bytes_read > 0) {
3000 orig_contents += string(buf, bytes_read);
3001 bytes_read = read(fd, buf, buf_size);
3004 if (bytes_read < 0) {
3005 perror(os_specific.c_str());
3011 if (orig_contents == old_contents) {
3013 lseek(fd, 0, SEEK_SET);
3014 ssize_t bytes_written = write(fd, new_contents.data(), new_contents.size());
3015 if (bytes_written < 0) {
3016 perror(os_specific.c_str());
3022 if (close(fd) < 0) {
3023 perror(os_specific.c_str());
3052 HANDLE hfile = CreateFileW(os_specific.c_str(), GENERIC_READ,
3053 FILE_SHARE_READ, NULL, OPEN_ALWAYS,
3054 FILE_ATTRIBUTE_NORMAL, NULL);
3055 while (hfile == INVALID_HANDLE_VALUE) {
3056 DWORD error = GetLastError();
3057 if (error == ERROR_SHARING_VIOLATION) {
3060 hfile = CreateFileW(os_specific.c_str(), GENERIC_READ,
3061 FILE_SHARE_READ, NULL, OPEN_ALWAYS,
3062 FILE_ATTRIBUTE_NORMAL, NULL);
3064 cerr <<
"Couldn't open file: " << os_specific
3065 <<
", error " << error <<
"\n";
3070 static const size_t buf_size = 512;
3073 contents = string();
3076 if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
3077 cerr <<
"Error reading file: " << os_specific
3078 <<
", error " << GetLastError() <<
"\n";
3082 while (bytes_read > 0) {
3083 contents += string(buf, bytes_read);
3085 if (!ReadFile(hfile, buf, buf_size, &bytes_read, NULL)) {
3086 cerr <<
"Error reading file: " << os_specific
3087 <<
", error " << GetLastError() <<
"\n";
3098 int fd = open(os_specific.c_str(), O_RDWR | O_CREAT, 0666);
3100 perror(os_specific.c_str());
3104 static const size_t buf_size = 512;
3107 contents = string();
3110 if (lockf(fd, F_LOCK, 0) != 0) {
3112 if (flock(fd, LOCK_EX) != 0) {
3114 perror(os_specific.c_str());
3119 ssize_t bytes_read = read(fd, buf, buf_size);
3120 while (bytes_read > 0) {
3121 contents += string(buf, bytes_read);
3122 bytes_read = read(fd, buf, buf_size);
3125 if (bytes_read < 0) {
3126 perror(os_specific.c_str());
3147 if (_filename.empty()) {
3149 _basename_start = 0;
3153 string::size_type slash = _filename.rfind(
'/');
3154 if (slash != string::npos) {
3155 _basename_start = slash + 1;
3156 _dirname_end = _basename_start;
3161 while (_dirname_end > 0 && _filename[_dirname_end-1] ==
'/') {
3168 if (_dirname_end == 0) {
3174 _basename_start = 0;
3198 locate_extension() {
3200 if (_filename.empty()) {
3201 _basename_end = string::npos;
3202 _extension_start = string::npos;
3205 string::size_type dot = _filename.length() - 1;
3207 while (dot+1 > _basename_start && _filename[dot] !=
'.') {
3211 if (dot+1 > _basename_start) {
3212 _basename_end = dot;
3213 _extension_start = dot + 1;
3215 _basename_end = string::npos;
3216 _extension_start = string::npos;
3239 _hash_end = string::npos;
3240 _hash_start = string::npos;
3246 _hash_end = _filename.rfind(
'#');
3247 if (_hash_end == string::npos) {
3248 _hash_end = string::npos;
3249 _hash_start = string::npos;
3252 _hash_start = _hash_end;
3254 while (_hash_start > 0 && _filename[_hash_start - 1] ==
'#') {
3271 get_common_prefix(
const string &other)
const {
3275 while (len < length() && len < other.length() &&
3276 _filename[len] == other[len]) {
3281 while (len > 0 && _filename[len-1] !=
'/') {
3295 count_slashes(
const string &str) {
3297 string::const_iterator si;
3300 while (si != str.end()) {
3306 while (*si ==
'/') {
3309 if (si == str.end()) {
3329 r_make_canonical(
const Filename &cwd) {
3339 if (_wchdir(os_specific.c_str()) >= 0) {
3345 if (_wchdir(osdir.c_str()) < 0) {
3346 cerr <<
"Error! Cannot change back to " << osdir <<
"\n";
3353 if (::
chdir(os_specific.c_str()) >= 0) {
3359 if (::
chdir(osdir.c_str()) < 0) {
3360 cerr <<
"Error! Cannot change back to " << osdir <<
"\n";
3377 if (!dir.r_make_canonical(cwd)) {
const Filename & get_directory(int n) const
Returns the nth directory on the search list.
bool mkdir() const
Creates the directory named by this filename.
bool open_append(ofstream &stream) const
Opens the indicated ofstream for writing the file, if possible.
string get_fullpath() const
Returns the entire filename: directory, basename, extension.
void set_hash_to_end(const string &s)
Replaces the part of the filename from the beginning of the hash sequence to the end of the filename...
void set_extension(const string &s)
Replaces the file extension.
bool atomic_read_contents(string &contents) const
Uses native file-locking mechanisms to atomically read the contents of a (small) file.
bool make_dir() const
Creates all the directories in the path to the file specified in the filename, except for the basenam...
const wstring & get_wtext() const
Returns the text associated with the TextEncoder, as a wide-character string.
bool unlink() const
Permanently deletes the file associated with the filename, if possible.
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
static Pointer get_ptr(const Pointer &var)
Atomically retrieves the snapshot value of the indicated variable.
void set_fullpath(const string &s)
Replaces the entire filename: directory, basename, extension.
This class can be used to convert text between multiple representations, e.g.
void set_type(Type type)
Sets the type of the file represented by the filename.
void set_binary()
Indicates that the filename represents a binary file.
bool chdir() const
Changes directory to the specified location.
bool open_read(ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
string get_text() const
Returns the current text, as encoded via the current encoding system.
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
bool make_true_case()
On a case-insensitive operating system (e.g.
bool open_read_write(fstream &stream, bool truncate=false) const
Opens the indicated fstream for read/write access to the file, if possible.
string get_dirname() const
Returns the directory part of the filename.
void set_basename_wo_extension(const string &s)
Replaces the basename part of the filename, without the file extension.
string to_os_short_name() const
This works like to_os_generic(), but it returns the "short name" version of the filename, if it exists, or the original filename otherwise.
static Filename expand_from(const string &user_string, Type type=T_general)
Returns the same thing as from_os_specific(), but embedded environment variable references (e...
static int unicode_tolower(int character)
Returns the uppercase equivalent of the given Unicode character.
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...
bool is_local() const
Returns true if the filename is local, e.g.
void standardize()
Converts the filename to standard form by replacing consecutive slashes with a single slash...
int get_num_directories() const
Returns the number of directories on the search list.
bool resolve_filename(const DSearchPath &searchpath, const string &default_extension=string())
Searches the given search path for the filename.
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...
static Filename temporary(const string &dirname, const string &prefix, const string &suffix=string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix...
wstring to_os_specific_w() const
The wide-string variant on to_os_specific().
void set_fullpath_wo_extension(const string &s)
Replaces the full filename–directory and basename parts–except for the extension. ...
bool is_writable() const
Returns true if the filename exists and is either a directory or a regular file that can be written t...
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...
Type get_type() const
Returns the type of the file represented by the filename, as previously set by set_type().
static TextEncoder::Encoding get_filesystem_encoding()
Specifies the default encoding to be used for all subsequent Filenames objects.
bool rename_to(const Filename &other) const
Renames the file to the indicated new filename.
The name of a file, such as a texture file or an Egg file.
string to_os_generic() const
This is similar to to_os_specific(), but it is designed to generate a filename that can be understood...
static const Filename & get_user_appdata_directory()
Returns a path to a system-defined directory appropriate for creating a subdirectory for storing appl...
void set_dirname(const string &s)
Replaces the directory part of the filename.
static const Filename & get_temp_directory()
Returns a path to a system-defined temporary directory.
string to_os_long_name() const
This is the opposite of to_os_short_name(): it returns the "long name" of the filename, if the filename exists.
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
bool make_relative_to(Filename directory, bool allow_backups=true)
Adjusts this filename, which must be a fully-specified pathname beginning with a slash, to make it a relative filename, relative to the fully-specified directory indicated (which must also begin with, and may or may not end with, a slash–a terminating slash is ignored).
bool touch() const
Updates the modification time of the file to the current time.
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...
bool open_read_append(fstream &stream) const
Opens the indicated ifstream for reading and writing the file, if possible; writes are appended to th...
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname)...
bool is_executable() const
Returns true if the filename exists and is executable.
void extract_components(vector_string &components) const
Extracts out the individual directory components of the path into a series of strings.
int find_on_searchpath(const DSearchPath &searchpath)
Performs the reverse of the resolve_filename() operation: assuming that the current filename is fully...
void set_encoding(Encoding encoding)
Specifies how the string set via set_text() is to be interpreted.
Filename find_file(const Filename &filename) const
Searches all the directories in the search list for the indicated file, in order. ...
static const Filename & get_common_appdata_directory()
Returns a path to a system-defined directory appropriate for creating a subdirectory for storing appl...
streamsize get_file_size() const
Returns the size of the file in bytes, or 0 if there is an error.
int get_hash() const
Returns a hash code that attempts to be mostly unique for different Filenames.
static Filename from_os_specific_w(const wstring &os_specific, Type type=T_general)
The wide-string variant of from_os_specific().
This class stores a list of directories that can be searched, in order, to locate a particular file...
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...
bool is_text() const
Returns true if the Filename has been indicated to represent a text file via a previous call to set_t...
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 ...
static Filename get_cwd()
Returns the name of the current working directory.
void set_text(const string &text)
Changes the text that is stored in the encoder.
bool is_regular_file() const
Returns true if the filename exists and is the name of a regular file (i.e.
bool open_write(ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
static string expand_string(const string &str)
Reads the string, looking for environment variable names marked by a $.
TypeHandle is the identifier used to differentiate C++ class types.
void set_wtext(const wstring &wtext)
Changes the text that is stored in the encoder.
bool get_pattern() const
Returns the flag indicating whether this is a filename pattern.
string get_extension() const
Returns the file extension.
time_t get_timestamp() const
Returns a time_t value that represents the time the file was last modified, to within whatever precis...
wstring get_fullpath_w() const
Returns the entire filename as a wide-character string.
bool rmdir() const
The inverse of mkdir(): this removes the directory named by this Filename, if it is in fact a directo...
bool atomic_compare_and_exchange_contents(string &orig_contents, const string &old_contents, const string &new_contents) const
Uses native file-locking mechanisms to atomically replace the contents of a (small) file with the spe...
void set_pattern(bool pattern)
Sets the flag indicating whether this is a filename pattern.
static Pointer compare_and_exchange_ptr(Pointer &mem, Pointer old_value, Pointer new_value)
Atomic compare and exchange.
bool is_binary_or_text() const
Returns true either is_binary() or is_text() is true; that is, that the filename has been specified a...
void set_basename(const string &s)
Replaces the basename part of the filename.
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).