1///////////////////////////////////////////////////////////////////////////// 2// Name: src/common/filename.cpp 3// Purpose: wxFileName - encapsulates a file path 4// Author: Robert Roebling, Vadim Zeitlin 5// Modified by: 6// Created: 28.12.2000 7// RCS-ID: $Id: filename.cpp 66915 2011-02-16 21:46:49Z JS $ 8// Copyright: (c) 2000 Robert Roebling 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12/* 13 Here are brief descriptions of the filename formats supported by this class: 14 15 wxPATH_UNIX: standard Unix format, used under Darwin as well, absolute file 16 names have the form: 17 /dir1/dir2/.../dirN/filename, "." and ".." stand for the 18 current and parent directory respectively, "~" is parsed as the 19 user HOME and "~username" as the HOME of that user 20 21 wxPATH_DOS: DOS/Windows format, absolute file names have the form: 22 drive:\dir1\dir2\...\dirN\filename.ext where drive is a single 23 letter. "." and ".." as for Unix but no "~". 24 25 There are also UNC names of the form \\share\fullpath 26 27 wxPATH_MAC: Mac OS 8/9 and Mac OS X under CodeWarrior 7 format, absolute file 28 names have the form 29 volume:dir1:...:dirN:filename 30 and the relative file names are either 31 :dir1:...:dirN:filename 32 or just 33 filename 34 (although :filename works as well). 35 Since the volume is just part of the file path, it is not 36 treated like a separate entity as it is done under DOS and 37 VMS, it is just treated as another dir. 38 39 wxPATH_VMS: VMS native format, absolute file names have the form 40 <device>:[dir1.dir2.dir3]file.txt 41 or 42 <device>:[000000.dir1.dir2.dir3]file.txt 43 44 the <device> is the physical device (i.e. disk). 000000 is the 45 root directory on the device which can be omitted. 46 47 Note that VMS uses different separators unlike Unix: 48 : always after the device. If the path does not contain : than 49 the default (the device of the current directory) is assumed. 50 [ start of directory specification 51 . separator between directory and subdirectory 52 ] between directory and file 53 */ 54 55// ============================================================================ 56// declarations 57// ============================================================================ 58 59// ---------------------------------------------------------------------------- 60// headers 61// ---------------------------------------------------------------------------- 62 63// For compilers that support precompilation, includes "wx.h". 64#include "wx/wxprec.h" 65 66#ifdef __BORLANDC__ 67#pragma hdrstop 68#endif 69 70#ifndef WX_PRECOMP 71 #ifdef __WXMSW__ 72 #include "wx/msw/wrapwin.h" // For GetShort/LongPathName 73 #endif 74 #include "wx/dynarray.h" 75 #include "wx/intl.h" 76 #include "wx/log.h" 77 #include "wx/utils.h" 78#endif 79 80#include "wx/filename.h" 81#include "wx/private/filename.h" 82#include "wx/tokenzr.h" 83#include "wx/config.h" // for wxExpandEnvVars 84#include "wx/dynlib.h" 85 86#if defined(__WIN32__) && defined(__MINGW32__) 87 #include "wx/msw/gccpriv.h" 88#endif 89 90#ifdef __WXWINCE__ 91#include "wx/msw/private.h" 92#endif 93 94#if defined(__WXMAC__) 95 #include "wx/mac/private.h" // includes mac headers 96#endif 97 98// utime() is POSIX so should normally be available on all Unices 99#ifdef __UNIX_LIKE__ 100#include <sys/types.h> 101#include <utime.h> 102#include <sys/stat.h> 103#include <unistd.h> 104#endif 105 106#ifdef __DJGPP__ 107#include <unistd.h> 108#endif 109 110#ifdef __MWERKS__ 111#ifdef __MACH__ 112#include <sys/types.h> 113#include <utime.h> 114#include <sys/stat.h> 115#include <unistd.h> 116#else 117#include <stat.h> 118#include <unistd.h> 119#include <unix.h> 120#endif 121#endif 122 123#ifdef __WATCOMC__ 124#include <io.h> 125#include <sys/utime.h> 126#include <sys/stat.h> 127#endif 128 129#ifdef __VISAGECPP__ 130#ifndef MAX_PATH 131#define MAX_PATH 256 132#endif 133#endif 134 135#ifdef __EMX__ 136#include <os2.h> 137#define MAX_PATH _MAX_PATH 138#endif 139 140 141wxULongLong wxInvalidSize = (unsigned)-1; 142 143 144// ---------------------------------------------------------------------------- 145// private classes 146// ---------------------------------------------------------------------------- 147 148// small helper class which opens and closes the file - we use it just to get 149// a file handle for the given file name to pass it to some Win32 API function 150#if defined(__WIN32__) && !defined(__WXMICROWIN__) 151 152class wxFileHandle 153{ 154public: 155 enum OpenMode 156 { 157 Read, 158 Write 159 }; 160 161 wxFileHandle(const wxString& filename, OpenMode mode, int flags = 0) 162 { 163 m_hFile = ::CreateFile 164 ( 165 filename, // name 166 mode == Read ? GENERIC_READ // access mask 167 : GENERIC_WRITE, 168 FILE_SHARE_READ | // sharing mode 169 FILE_SHARE_WRITE, // (allow everything) 170 NULL, // no secutity attr 171 OPEN_EXISTING, // creation disposition 172 flags, // flags 173 NULL // no template file 174 ); 175 176 if ( m_hFile == INVALID_HANDLE_VALUE ) 177 { 178 wxLogSysError(_("Failed to open '%s' for %s"), 179 filename.c_str(), 180 mode == Read ? _("reading") : _("writing")); 181 } 182 } 183 184 ~wxFileHandle() 185 { 186 if ( m_hFile != INVALID_HANDLE_VALUE ) 187 { 188 if ( !::CloseHandle(m_hFile) ) 189 { 190 wxLogSysError(_("Failed to close file handle")); 191 } 192 } 193 } 194 195 // return true only if the file could be opened successfully 196 bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; } 197 198 // get the handle 199 operator HANDLE() const { return m_hFile; } 200 201private: 202 HANDLE m_hFile; 203}; 204 205#endif // __WIN32__ 206 207// ---------------------------------------------------------------------------- 208// private functions 209// ---------------------------------------------------------------------------- 210 211#if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__) 212 213// convert between wxDateTime and FILETIME which is a 64-bit value representing 214// the number of 100-nanosecond intervals since January 1, 1601. 215 216static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft) 217{ 218 FILETIME ftcopy = ft; 219 FILETIME ftLocal; 220 if ( !::FileTimeToLocalFileTime(&ftcopy, &ftLocal) ) 221 { 222 wxLogLastError(_T("FileTimeToLocalFileTime")); 223 } 224 225 SYSTEMTIME st; 226 if ( !::FileTimeToSystemTime(&ftLocal, &st) ) 227 { 228 wxLogLastError(_T("FileTimeToSystemTime")); 229 } 230 231 dt->Set(st.wDay, wxDateTime::Month(st.wMonth - 1), st.wYear, 232 st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); 233} 234 235static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt) 236{ 237 SYSTEMTIME st; 238 st.wDay = dt.GetDay(); 239 st.wMonth = (WORD)(dt.GetMonth() + 1); 240 st.wYear = (WORD)dt.GetYear(); 241 st.wHour = dt.GetHour(); 242 st.wMinute = dt.GetMinute(); 243 st.wSecond = dt.GetSecond(); 244 st.wMilliseconds = dt.GetMillisecond(); 245 246 FILETIME ftLocal; 247 if ( !::SystemTimeToFileTime(&st, &ftLocal) ) 248 { 249 wxLogLastError(_T("SystemTimeToFileTime")); 250 } 251 252 if ( !::LocalFileTimeToFileTime(&ftLocal, ft) ) 253 { 254 wxLogLastError(_T("LocalFileTimeToFileTime")); 255 } 256} 257 258#endif // wxUSE_DATETIME && __WIN32__ 259 260// return a string with the volume par 261static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format) 262{ 263 wxString path; 264 265 if ( !volume.empty() ) 266 { 267 format = wxFileName::GetFormat(format); 268 269 // Special Windows UNC paths hack, part 2: undo what we did in 270 // SplitPath() and make an UNC path if we have a drive which is not a 271 // single letter (hopefully the network shares can't be one letter only 272 // although I didn't find any authoritative docs on this) 273 if ( format == wxPATH_DOS && volume.length() > 1 ) 274 { 275 path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume; 276 } 277 else if ( format == wxPATH_DOS || format == wxPATH_VMS ) 278 { 279 path << volume << wxFileName::GetVolumeSeparator(format); 280 } 281 // else ignore 282 } 283 284 return path; 285} 286 287// return true if the character is a DOS path separator i.e. either a slash or 288// a backslash 289inline bool IsDOSPathSep(wxChar ch) 290{ 291 return ch == wxFILE_SEP_PATH_DOS || ch == wxFILE_SEP_PATH_UNIX; 292} 293 294// return true if the format used is the DOS/Windows one and the string looks 295// like a UNC path 296static bool IsUNCPath(const wxString& path, wxPathFormat format) 297{ 298 return format == wxPATH_DOS && 299 path.length() >= 4 && // "\\a" can't be a UNC path 300 IsDOSPathSep(path[0u]) && 301 IsDOSPathSep(path[1u]) && 302 !IsDOSPathSep(path[2u]); 303} 304 305// ============================================================================ 306// implementation 307// ============================================================================ 308 309// ---------------------------------------------------------------------------- 310// wxFileName construction 311// ---------------------------------------------------------------------------- 312 313void wxFileName::Assign( const wxFileName &filepath ) 314{ 315 if ( &filepath == this ) 316 return; 317 318 m_volume = filepath.GetVolume(); 319 m_dirs = filepath.GetDirs(); 320 m_name = filepath.GetName(); 321 m_ext = filepath.GetExt(); 322 m_relative = filepath.m_relative; 323 m_hasExt = filepath.m_hasExt; 324} 325 326void wxFileName::Assign(const wxString& volume, 327 const wxString& path, 328 const wxString& name, 329 const wxString& ext, 330 bool hasExt, 331 wxPathFormat format) 332{ 333 // we should ignore paths which look like UNC shares because we already 334 // have the volume here and the UNC notation (\\server\path) is only valid 335 // for paths which don't start with a volume, so prevent SetPath() from 336 // recognizing "\\foo\bar" in "c:\\foo\bar" as an UNC path 337 // 338 // note also that this is a rather ugly way to do what we want (passing 339 // some kind of flag telling to ignore UNC paths to SetPath() would be 340 // better) but this is the safest thing to do to avoid breaking backwards 341 // compatibility in 2.8 342 if ( IsUNCPath(path, format) ) 343 { 344 // remove one of the 2 leading backslashes to ensure that it's not 345 // recognized as an UNC path by SetPath() 346 wxString pathNonUNC(path, 1, wxString::npos); 347 SetPath(pathNonUNC, format); 348 } 349 else // no UNC complications 350 { 351 SetPath(path, format); 352 } 353 354 m_volume = volume; 355 m_ext = ext; 356 m_name = name; 357 358 m_hasExt = hasExt; 359} 360 361void wxFileName::SetPath( const wxString& pathOrig, wxPathFormat format ) 362{ 363 m_dirs.Clear(); 364 365 if ( pathOrig.empty() ) 366 { 367 // no path at all 368 m_relative = true; 369 370 return; 371 } 372 373 format = GetFormat( format ); 374 375 // 0) deal with possible volume part first 376 wxString volume, 377 path; 378 SplitVolume(pathOrig, &volume, &path, format); 379 if ( !volume.empty() ) 380 { 381 m_relative = false; 382 383 SetVolume(volume); 384 } 385 386 // 1) Determine if the path is relative or absolute. 387 wxChar leadingChar = path[0u]; 388 389 switch (format) 390 { 391 case wxPATH_MAC: 392 m_relative = leadingChar == wxT(':'); 393 394 // We then remove a leading ":". The reason is in our 395 // storage form for relative paths: 396 // ":dir:file.txt" actually means "./dir/file.txt" in 397 // DOS notation and should get stored as 398 // (relative) (dir) (file.txt) 399 // "::dir:file.txt" actually means "../dir/file.txt" 400 // stored as (relative) (..) (dir) (file.txt) 401 // This is important only for the Mac as an empty dir 402 // actually means <UP>, whereas under DOS, double 403 // slashes can be ignored: "\\\\" is the same as "\\". 404 if (m_relative) 405 path.erase( 0, 1 ); 406 break; 407 408 case wxPATH_VMS: 409 // TODO: what is the relative path format here? 410 m_relative = false; 411 break; 412 413 default: 414 wxFAIL_MSG( _T("Unknown path format") ); 415 // !! Fall through !! 416 417 case wxPATH_UNIX: 418 // the paths of the form "~" or "~username" are absolute 419 m_relative = leadingChar != wxT('/') && leadingChar != _T('~'); 420 break; 421 422 case wxPATH_DOS: 423 m_relative = !IsPathSeparator(leadingChar, format); 424 break; 425 426 } 427 428 // 2) Break up the path into its members. If the original path 429 // was just "/" or "\\", m_dirs will be empty. We know from 430 // the m_relative field, if this means "nothing" or "root dir". 431 432 wxStringTokenizer tn( path, GetPathSeparators(format) ); 433 434 while ( tn.HasMoreTokens() ) 435 { 436 wxString token = tn.GetNextToken(); 437 438 // Remove empty token under DOS and Unix, interpret them 439 // as .. under Mac. 440 if (token.empty()) 441 { 442 if (format == wxPATH_MAC) 443 m_dirs.Add( wxT("..") ); 444 // else ignore 445 } 446 else 447 { 448 m_dirs.Add( token ); 449 } 450 } 451} 452 453void wxFileName::Assign(const wxString& fullpath, 454 wxPathFormat format) 455{ 456 wxString volume, path, name, ext; 457 bool hasExt; 458 SplitPath(fullpath, &volume, &path, &name, &ext, &hasExt, format); 459 460 Assign(volume, path, name, ext, hasExt, format); 461} 462 463void wxFileName::Assign(const wxString& fullpathOrig, 464 const wxString& fullname, 465 wxPathFormat format) 466{ 467 // always recognize fullpath as directory, even if it doesn't end with a 468 // slash 469 wxString fullpath = fullpathOrig; 470 if ( !fullpath.empty() && !wxEndsWithPathSeparator(fullpath) ) 471 { 472 fullpath += GetPathSeparator(format); 473 } 474 475 wxString volume, path, name, ext; 476 bool hasExt; 477 478 // do some consistency checks in debug mode: the name should be really just 479 // the filename and the path should be really just a path 480#ifdef __WXDEBUG__ 481 wxString volDummy, pathDummy, nameDummy, extDummy; 482 483 SplitPath(fullname, &volDummy, &pathDummy, &name, &ext, &hasExt, format); 484 485 wxASSERT_MSG( volDummy.empty() && pathDummy.empty(), 486 _T("the file name shouldn't contain the path") ); 487 488 SplitPath(fullpath, &volume, &path, &nameDummy, &extDummy, format); 489 490 wxASSERT_MSG( nameDummy.empty() && extDummy.empty(), 491 _T("the path shouldn't contain file name nor extension") ); 492 493#else // !__WXDEBUG__ 494 SplitPath(fullname, NULL /* no volume */, NULL /* no path */, 495 &name, &ext, &hasExt, format); 496 SplitPath(fullpath, &volume, &path, NULL, NULL, format); 497#endif // __WXDEBUG__/!__WXDEBUG__ 498 499 Assign(volume, path, name, ext, hasExt, format); 500} 501 502void wxFileName::Assign(const wxString& pathOrig, 503 const wxString& name, 504 const wxString& ext, 505 wxPathFormat format) 506{ 507 wxString volume, 508 path; 509 SplitVolume(pathOrig, &volume, &path, format); 510 511 Assign(volume, path, name, ext, format); 512} 513 514void wxFileName::AssignDir(const wxString& dir, wxPathFormat format) 515{ 516 Assign(dir, wxEmptyString, format); 517} 518 519void wxFileName::Clear() 520{ 521 m_dirs.Clear(); 522 523 m_volume = 524 m_name = 525 m_ext = wxEmptyString; 526 527 // we don't have any absolute path for now 528 m_relative = true; 529 530 // nor any extension 531 m_hasExt = false; 532} 533 534/* static */ 535wxFileName wxFileName::FileName(const wxString& file, wxPathFormat format) 536{ 537 return wxFileName(file, format); 538} 539 540/* static */ 541wxFileName wxFileName::DirName(const wxString& dir, wxPathFormat format) 542{ 543 wxFileName fn; 544 fn.AssignDir(dir, format); 545 return fn; 546} 547 548// ---------------------------------------------------------------------------- 549// existence tests 550// ---------------------------------------------------------------------------- 551 552bool wxFileName::FileExists() const 553{ 554 return wxFileName::FileExists( GetFullPath() ); 555} 556 557bool wxFileName::FileExists( const wxString &file ) 558{ 559 return ::wxFileExists( file ); 560} 561 562bool wxFileName::DirExists() const 563{ 564 return wxFileName::DirExists( GetPath() ); 565} 566 567bool wxFileName::DirExists( const wxString &dir ) 568{ 569 return ::wxDirExists( dir ); 570} 571 572// ---------------------------------------------------------------------------- 573// CWD and HOME stuff 574// ---------------------------------------------------------------------------- 575 576void wxFileName::AssignCwd(const wxString& volume) 577{ 578 AssignDir(wxFileName::GetCwd(volume)); 579} 580 581/* static */ 582wxString wxFileName::GetCwd(const wxString& volume) 583{ 584 // if we have the volume, we must get the current directory on this drive 585 // and to do this we have to chdir to this volume - at least under Windows, 586 // I don't know how to get the current drive on another volume elsewhere 587 // (TODO) 588 wxString cwdOld; 589 if ( !volume.empty() ) 590 { 591 cwdOld = wxGetCwd(); 592 SetCwd(volume + GetVolumeSeparator()); 593 } 594 595 wxString cwd = ::wxGetCwd(); 596 597 if ( !volume.empty() ) 598 { 599 SetCwd(cwdOld); 600 } 601 602 return cwd; 603} 604 605bool wxFileName::SetCwd() 606{ 607 return wxFileName::SetCwd( GetPath() ); 608} 609 610bool wxFileName::SetCwd( const wxString &cwd ) 611{ 612 return ::wxSetWorkingDirectory( cwd ); 613} 614 615void wxFileName::AssignHomeDir() 616{ 617 AssignDir(wxFileName::GetHomeDir()); 618} 619 620wxString wxFileName::GetHomeDir() 621{ 622 return ::wxGetHomeDir(); 623} 624 625 626// ---------------------------------------------------------------------------- 627// CreateTempFileName 628// ---------------------------------------------------------------------------- 629 630#if wxUSE_FILE || wxUSE_FFILE 631 632 633#if !defined wx_fdopen && defined HAVE_FDOPEN 634 #define wx_fdopen fdopen 635#endif 636 637// NB: GetTempFileName() under Windows creates the file, so using 638// O_EXCL there would fail 639#ifdef __WINDOWS__ 640 #define wxOPEN_EXCL 0 641#else 642 #define wxOPEN_EXCL O_EXCL 643#endif 644 645 646#ifdef wxOpenOSFHandle 647#define WX_HAVE_DELETE_ON_CLOSE 648// On Windows create a file with the FILE_FLAGS_DELETE_ON_CLOSE flags. 649// 650static int wxOpenWithDeleteOnClose(const wxString& filename) 651{ 652 DWORD access = GENERIC_READ | GENERIC_WRITE; 653 654 DWORD disposition = OPEN_ALWAYS; 655 656 DWORD attributes = FILE_ATTRIBUTE_TEMPORARY | 657 FILE_FLAG_DELETE_ON_CLOSE; 658 659 HANDLE h = ::CreateFile(filename, access, 0, NULL, 660 disposition, attributes, NULL); 661 662 return wxOpenOSFHandle(h, wxO_BINARY); 663} 664#endif // wxOpenOSFHandle 665 666 667// Helper to open the file 668// 669static int wxTempOpen(const wxString& path, bool *deleteOnClose) 670{ 671#ifdef WX_HAVE_DELETE_ON_CLOSE 672 if (*deleteOnClose) 673 return wxOpenWithDeleteOnClose(path); 674#endif 675 676 *deleteOnClose = false; 677 678 return wxOpen(path, wxO_BINARY | O_RDWR | O_CREAT | wxOPEN_EXCL, 0600); 679} 680 681 682#if wxUSE_FFILE 683// Helper to open the file and attach it to the wxFFile 684// 685static bool wxTempOpen(wxFFile *file, const wxString& path, bool *deleteOnClose) 686{ 687#ifndef wx_fdopen 688 *deleteOnClose = false; 689 return file->Open(path, _T("w+b")); 690#else // wx_fdopen 691 int fd = wxTempOpen(path, deleteOnClose); 692 if (fd == -1) 693 return false; 694 file->Attach(wx_fdopen(fd, "w+b")); 695 return file->IsOpened(); 696#endif // wx_fdopen 697} 698#endif // wxUSE_FFILE 699 700 701#if !wxUSE_FILE 702 #define WXFILEARGS(x, y) y 703#elif !wxUSE_FFILE 704 #define WXFILEARGS(x, y) x 705#else 706 #define WXFILEARGS(x, y) x, y 707#endif 708 709 710// Implementation of wxFileName::CreateTempFileName(). 711// 712static wxString wxCreateTempImpl( 713 const wxString& prefix, 714 WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp), 715 bool *deleteOnClose = NULL) 716{ 717#if wxUSE_FILE && wxUSE_FFILE 718 wxASSERT(fileTemp == NULL || ffileTemp == NULL); 719#endif 720 wxString path, dir, name; 721 bool wantDeleteOnClose = false; 722 723 if (deleteOnClose) 724 { 725 // set the result to false initially 726 wantDeleteOnClose = *deleteOnClose; 727 *deleteOnClose = false; 728 } 729 else 730 { 731 // easier if it alwasys points to something 732 deleteOnClose = &wantDeleteOnClose; 733 } 734 735 // use the directory specified by the prefix 736 wxFileName::SplitPath(prefix, &dir, &name, NULL /* extension */); 737 738 if (dir.empty()) 739 { 740 dir = wxFileName::GetTempDir(); 741 } 742 743#if defined(__WXWINCE__) 744 path = dir + wxT("\\") + name; 745 int i = 1; 746 while (wxFileName::FileExists(path)) 747 { 748 path = dir + wxT("\\") + name ; 749 path << i; 750 i ++; 751 } 752 753#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__) 754 if ( !::GetTempFileName(dir, name, 0, wxStringBuffer(path, MAX_PATH + 1)) ) 755 { 756 wxLogLastError(_T("GetTempFileName")); 757 758 path.clear(); 759 } 760 761#else // !Windows 762 path = dir; 763 764 if ( !wxEndsWithPathSeparator(dir) && 765 (name.empty() || !wxIsPathSeparator(name[0u])) ) 766 { 767 path += wxFILE_SEP_PATH; 768 } 769 770 path += name; 771 772#if defined(HAVE_MKSTEMP) 773 // scratch space for mkstemp() 774 path += _T("XXXXXX"); 775 776 // we need to copy the path to the buffer in which mkstemp() can modify it 777 wxCharBuffer buf( wxConvFile.cWX2MB( path ) ); 778 779 // cast is safe because the string length doesn't change 780 int fdTemp = mkstemp( (char*)(const char*) buf ); 781 if ( fdTemp == -1 ) 782 { 783 // this might be not necessary as mkstemp() on most systems should have 784 // already done it but it doesn't hurt neither... 785 path.clear(); 786 } 787 else // mkstemp() succeeded 788 { 789 path = wxConvFile.cMB2WX( (const char*) buf ); 790 791 #if wxUSE_FILE 792 // avoid leaking the fd 793 if ( fileTemp ) 794 { 795 fileTemp->Attach(fdTemp); 796 } 797 else 798 #endif 799 800 #if wxUSE_FFILE 801 if ( ffileTemp ) 802 { 803 #ifdef wx_fdopen 804 ffileTemp->Attach(wx_fdopen(fdTemp, "r+b")); 805 #else 806 ffileTemp->Open(path, _T("r+b")); 807 close(fdTemp); 808 #endif 809 } 810 else 811 #endif 812 813 { 814 close(fdTemp); 815 } 816 } 817#else // !HAVE_MKSTEMP 818 819#ifdef HAVE_MKTEMP 820 // same as above 821 path += _T("XXXXXX"); 822 823 wxCharBuffer buf = wxConvFile.cWX2MB( path ); 824 if ( !mktemp( (char*)(const char*) buf ) ) 825 { 826 path.clear(); 827 } 828 else 829 { 830 path = wxConvFile.cMB2WX( (const char*) buf ); 831 } 832#else // !HAVE_MKTEMP (includes __DOS__) 833 // generate the unique file name ourselves 834 #if !defined(__DOS__) && !defined(__PALMOS__) && (!defined(__MWERKS__) || defined(__DARWIN__) ) 835 path << (unsigned int)getpid(); 836 #endif 837 838 wxString pathTry; 839 840 static const size_t numTries = 1000; 841 for ( size_t n = 0; n < numTries; n++ ) 842 { 843 // 3 hex digits is enough for numTries == 1000 < 4096 844 pathTry = path + wxString::Format(_T("%.03x"), (unsigned int) n); 845 if ( !wxFileName::FileExists(pathTry) ) 846 { 847 break; 848 } 849 850 pathTry.clear(); 851 } 852 853 path = pathTry; 854#endif // HAVE_MKTEMP/!HAVE_MKTEMP 855 856#endif // HAVE_MKSTEMP/!HAVE_MKSTEMP 857 858#endif // Windows/!Windows 859 860 if ( path.empty() ) 861 { 862 wxLogSysError(_("Failed to create a temporary file name")); 863 } 864 else 865 { 866 bool ok = true; 867 868 // open the file - of course, there is a race condition here, this is 869 // why we always prefer using mkstemp()... 870 #if wxUSE_FILE 871 if ( fileTemp && !fileTemp->IsOpened() ) 872 { 873 *deleteOnClose = wantDeleteOnClose; 874 int fd = wxTempOpen(path, deleteOnClose); 875 if (fd != -1) 876 fileTemp->Attach(fd); 877 else 878 ok = false; 879 } 880 #endif 881 882 #if wxUSE_FFILE 883 if ( ffileTemp && !ffileTemp->IsOpened() ) 884 { 885 *deleteOnClose = wantDeleteOnClose; 886 ok = wxTempOpen(ffileTemp, path, deleteOnClose); 887 } 888 #endif 889 890 if ( !ok ) 891 { 892 // FIXME: If !ok here should we loop and try again with another 893 // file name? That is the standard recourse if open(O_EXCL) 894 // fails, though of course it should be protected against 895 // possible infinite looping too. 896 897 wxLogError(_("Failed to open temporary file.")); 898 899 path.clear(); 900 } 901 } 902 903 return path; 904} 905 906 907static bool wxCreateTempImpl( 908 const wxString& prefix, 909 WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp), 910 wxString *name) 911{ 912 bool deleteOnClose = true; 913 914 *name = wxCreateTempImpl(prefix, 915 WXFILEARGS(fileTemp, ffileTemp), 916 &deleteOnClose); 917 918 bool ok = !name->empty(); 919 920 if (deleteOnClose) 921 name->clear(); 922#ifdef __UNIX__ 923 else if (ok && wxRemoveFile(*name)) 924 name->clear(); 925#endif 926 927 return ok; 928} 929 930 931static void wxAssignTempImpl( 932 wxFileName *fn, 933 const wxString& prefix, 934 WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp)) 935{ 936 wxString tempname; 937 tempname = wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, ffileTemp)); 938 939 if ( tempname.empty() ) 940 { 941 // error, failed to get temp file name 942 fn->Clear(); 943 } 944 else // ok 945 { 946 fn->Assign(tempname); 947 } 948} 949 950 951void wxFileName::AssignTempFileName(const wxString& prefix) 952{ 953 wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, NULL)); 954} 955 956/* static */ 957wxString wxFileName::CreateTempFileName(const wxString& prefix) 958{ 959 return wxCreateTempImpl(prefix, WXFILEARGS(NULL, NULL)); 960} 961 962#endif // wxUSE_FILE || wxUSE_FFILE 963 964 965#if wxUSE_FILE 966 967wxString wxCreateTempFileName(const wxString& prefix, 968 wxFile *fileTemp, 969 bool *deleteOnClose) 970{ 971 return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), deleteOnClose); 972} 973 974bool wxCreateTempFile(const wxString& prefix, 975 wxFile *fileTemp, 976 wxString *name) 977{ 978 return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), name); 979} 980 981void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp) 982{ 983 wxAssignTempImpl(this, prefix, WXFILEARGS(fileTemp, NULL)); 984} 985 986/* static */ 987wxString 988wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) 989{ 990 return wxCreateTempFileName(prefix, fileTemp); 991} 992 993#endif // wxUSE_FILE 994 995 996#if wxUSE_FFILE 997 998wxString wxCreateTempFileName(const wxString& prefix, 999 wxFFile *fileTemp, 1000 bool *deleteOnClose) 1001{ 1002 return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), deleteOnClose); 1003} 1004 1005bool wxCreateTempFile(const wxString& prefix, 1006 wxFFile *fileTemp, 1007 wxString *name) 1008{ 1009 return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), name); 1010 1011} 1012 1013void wxFileName::AssignTempFileName(const wxString& prefix, wxFFile *fileTemp) 1014{ 1015 wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, fileTemp)); 1016} 1017 1018/* static */ 1019wxString 1020wxFileName::CreateTempFileName(const wxString& prefix, wxFFile *fileTemp) 1021{ 1022 return wxCreateTempFileName(prefix, fileTemp); 1023} 1024 1025#endif // wxUSE_FFILE 1026 1027 1028// ---------------------------------------------------------------------------- 1029// directory operations 1030// ---------------------------------------------------------------------------- 1031 1032wxString wxFileName::GetTempDir() 1033{ 1034 wxString dir; 1035 dir = wxGetenv(_T("TMPDIR")); 1036 if (dir.empty()) 1037 { 1038 dir = wxGetenv(_T("TMP")); 1039 if (dir.empty()) 1040 { 1041 dir = wxGetenv(_T("TEMP")); 1042 } 1043 } 1044 1045#if defined(__WXWINCE__) 1046 if (dir.empty()) 1047 { 1048 // FIXME. Create \temp dir? 1049 if (DirExists(wxT("\\temp"))) 1050 dir = wxT("\\temp"); 1051 } 1052#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__) 1053 1054 if ( dir.empty() ) 1055 { 1056 if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) ) 1057 { 1058 wxLogLastError(_T("GetTempPath")); 1059 } 1060 1061 if ( dir.empty() ) 1062 { 1063 // GetTempFileName() fails if we pass it an empty string 1064 dir = _T('.'); 1065 } 1066 } 1067#else // !Windows 1068 1069 if ( dir.empty() ) 1070 { 1071 // default 1072#if defined(__DOS__) || defined(__OS2__) 1073 dir = _T("."); 1074#elif defined(__WXMAC__) 1075 dir = wxMacFindFolder(short(kOnSystemDisk), kTemporaryFolderType, kCreateFolder); 1076#else 1077 dir = _T("/tmp"); 1078#endif 1079 } 1080#endif 1081 1082 return dir; 1083} 1084 1085bool wxFileName::Mkdir( int perm, int flags ) 1086{ 1087 return wxFileName::Mkdir(GetPath(), perm, flags); 1088} 1089 1090bool wxFileName::Mkdir( const wxString& dir, int perm, int flags ) 1091{ 1092 if ( flags & wxPATH_MKDIR_FULL ) 1093 { 1094 // split the path in components 1095 wxFileName filename; 1096 filename.AssignDir(dir); 1097 1098 wxString currPath; 1099 if ( filename.HasVolume()) 1100 { 1101 currPath << wxGetVolumeString(filename.GetVolume(), wxPATH_NATIVE); 1102 } 1103 1104 wxArrayString dirs = filename.GetDirs(); 1105 size_t count = dirs.GetCount(); 1106 for ( size_t i = 0; i < count; i++ ) 1107 { 1108 if ( i > 0 || 1109#if defined(__WXMAC__) && !defined(__DARWIN__) 1110 // relative pathnames are exactely the other way round under mac... 1111 !filename.IsAbsolute() 1112#else 1113 filename.IsAbsolute() 1114#endif 1115 ) 1116 currPath += wxFILE_SEP_PATH; 1117 currPath += dirs[i]; 1118 1119 if (!DirExists(currPath)) 1120 { 1121 if (!wxMkdir(currPath, perm)) 1122 { 1123 // no need to try creating further directories 1124 return false; 1125 } 1126 } 1127 } 1128 1129 return true; 1130 1131 } 1132 1133 return ::wxMkdir( dir, perm ); 1134} 1135 1136bool wxFileName::Rmdir() 1137{ 1138 return wxFileName::Rmdir( GetPath() ); 1139} 1140 1141bool wxFileName::Rmdir( const wxString &dir ) 1142{ 1143 return ::wxRmdir( dir ); 1144} 1145 1146// ---------------------------------------------------------------------------- 1147// path normalization 1148// ---------------------------------------------------------------------------- 1149 1150bool wxFileName::Normalize(int flags, 1151 const wxString& cwd, 1152 wxPathFormat format) 1153{ 1154 // deal with env vars renaming first as this may seriously change the path 1155 if ( flags & wxPATH_NORM_ENV_VARS ) 1156 { 1157 wxString pathOrig = GetFullPath(format); 1158 wxString path = wxExpandEnvVars(pathOrig); 1159 if ( path != pathOrig ) 1160 { 1161 Assign(path); 1162 } 1163 } 1164 1165 1166 // the existing path components 1167 wxArrayString dirs = GetDirs(); 1168 1169 // the path to prepend in front to make the path absolute 1170 wxFileName curDir; 1171 1172 format = GetFormat(format); 1173 1174 // set up the directory to use for making the path absolute later 1175 if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute(format) ) 1176 { 1177 if ( cwd.empty() ) 1178 { 1179 curDir.AssignCwd(GetVolume()); 1180 } 1181 else // cwd provided 1182 { 1183 curDir.AssignDir(cwd); 1184 } 1185 } 1186 1187 // handle ~ stuff under Unix only 1188 if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) ) 1189 { 1190 if ( !dirs.IsEmpty() ) 1191 { 1192 wxString dir = dirs[0u]; 1193 if ( !dir.empty() && dir[0u] == _T('~') ) 1194 { 1195 // to make the path absolute use the home directory 1196 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1)); 1197 1198 // if we are expanding the tilde, then this path 1199 // *should* be already relative (since we checked for 1200 // the tilde only in the first char of the first dir); 1201 // if m_relative==false, it's because it was initialized 1202 // from a string which started with /~; in that case 1203 // we reach this point but then need m_relative=true 1204 // for relative->absolute expansion later 1205 m_relative = true; 1206 1207 dirs.RemoveAt(0u); 1208 } 1209 } 1210 } 1211 1212 // transform relative path into abs one 1213 if ( curDir.IsOk() ) 1214 { 1215 // this path may be relative because it doesn't have the volume name 1216 // and still have m_relative=true; in this case we shouldn't modify 1217 // our directory components but just set the current volume 1218 if ( !HasVolume() && curDir.HasVolume() ) 1219 { 1220 SetVolume(curDir.GetVolume()); 1221 1222 if ( !m_relative ) 1223 { 1224 // yes, it was the case - we don't need curDir then 1225 curDir.Clear(); 1226 } 1227 } 1228 1229 // finally, prepend curDir to the dirs array 1230 wxArrayString dirsNew = curDir.GetDirs(); 1231 WX_PREPEND_ARRAY(dirs, dirsNew); 1232 1233 // if we used e.g. tilde expansion previously and wxGetUserHome didn't 1234 // return for some reason an absolute path, then curDir maybe not be absolute! 1235 if ( curDir.IsAbsolute(format) ) 1236 { 1237 // we have prepended an absolute path and thus we are now an absolute 1238 // file name too 1239 m_relative = false; 1240 } 1241 // else if (flags & wxPATH_NORM_ABSOLUTE): 1242 // should we warn the user that we didn't manage to make the path absolute? 1243 } 1244 1245 // now deal with ".", ".." and the rest 1246 m_dirs.Empty(); 1247 size_t count = dirs.GetCount(); 1248 for ( size_t n = 0; n < count; n++ ) 1249 { 1250 wxString dir = dirs[n]; 1251 1252 if ( flags & wxPATH_NORM_DOTS ) 1253 { 1254 if ( dir == wxT(".") ) 1255 { 1256 // just ignore 1257 continue; 1258 } 1259 1260 if ( dir == wxT("..") ) 1261 { 1262 if ( m_dirs.IsEmpty() ) 1263 { 1264 wxLogError(_("The path '%s' contains too many \"..\"!"), 1265 GetFullPath().c_str()); 1266 return false; 1267 } 1268 1269 m_dirs.RemoveAt(m_dirs.GetCount() - 1); 1270 continue; 1271 } 1272 } 1273 1274 m_dirs.Add(dir); 1275 } 1276 1277#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE 1278 if ( (flags & wxPATH_NORM_SHORTCUT) ) 1279 { 1280 wxString filename; 1281 if (GetShortcutTarget(GetFullPath(format), filename)) 1282 { 1283 m_relative = false; 1284 Assign(filename); 1285 } 1286 } 1287#endif 1288 1289#if defined(__WIN32__) 1290 if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) ) 1291 { 1292 Assign(GetLongPath()); 1293 } 1294#endif // Win32 1295 1296 // Change case (this should be kept at the end of the function, to ensure 1297 // that the path doesn't change any more after we normalize its case) 1298 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) ) 1299 { 1300 m_volume.MakeLower(); 1301 m_name.MakeLower(); 1302 m_ext.MakeLower(); 1303 1304 // directory entries must be made lower case as well 1305 count = m_dirs.GetCount(); 1306 for ( size_t i = 0; i < count; i++ ) 1307 { 1308 m_dirs[i].MakeLower(); 1309 } 1310 } 1311 1312 return true; 1313} 1314 1315// ---------------------------------------------------------------------------- 1316// get the shortcut target 1317// ---------------------------------------------------------------------------- 1318 1319// WinCE (3) doesn't have CLSID_ShellLink, IID_IShellLink definitions. 1320// The .lnk file is a plain text file so it should be easy to 1321// make it work. Hint from Google Groups: 1322// "If you open up a lnk file, you'll see a 1323// number, followed by a pound sign (#), followed by more text. The 1324// number is the number of characters that follows the pound sign. The 1325// characters after the pound sign are the command line (which _can_ 1326// include arguments) to be executed. Any path (e.g. \windows\program 1327// files\myapp.exe) that includes spaces needs to be enclosed in 1328// quotation marks." 1329 1330#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE 1331// The following lines are necessary under WinCE 1332// #include "wx/msw/private.h" 1333// #include <ole2.h> 1334#include <shlobj.h> 1335#if defined(__WXWINCE__) 1336#include <shlguid.h> 1337#endif 1338 1339bool wxFileName::GetShortcutTarget(const wxString& shortcutPath, 1340 wxString& targetFilename, 1341 wxString* arguments) 1342{ 1343 wxString path, file, ext; 1344 wxSplitPath(shortcutPath, & path, & file, & ext); 1345 1346 HRESULT hres; 1347 IShellLink* psl; 1348 bool success = false; 1349 1350 // Assume it's not a shortcut if it doesn't end with lnk 1351 if (ext.CmpNoCase(wxT("lnk"))!=0) 1352 return false; 1353 1354 // create a ShellLink object 1355 hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, 1356 IID_IShellLink, (LPVOID*) &psl); 1357 1358 if (SUCCEEDED(hres)) 1359 { 1360 IPersistFile* ppf; 1361 hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf); 1362 if (SUCCEEDED(hres)) 1363 { 1364 WCHAR wsz[MAX_PATH]; 1365 1366 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, shortcutPath.mb_str(), -1, wsz, 1367 MAX_PATH); 1368 1369 hres = ppf->Load(wsz, 0); 1370 ppf->Release(); 1371 1372 if (SUCCEEDED(hres)) 1373 { 1374 wxChar buf[2048]; 1375 // Wrong prototype in early versions 1376#if defined(__MINGW32__) && !wxCHECK_W32API_VERSION(2, 2) 1377 psl->GetPath((CHAR*) buf, 2048, NULL, SLGP_UNCPRIORITY); 1378#else 1379 psl->GetPath(buf, 2048, NULL, SLGP_UNCPRIORITY); 1380#endif 1381 targetFilename = wxString(buf); 1382 success = (shortcutPath != targetFilename); 1383 1384 psl->GetArguments(buf, 2048); 1385 wxString args(buf); 1386 if (!args.empty() && arguments) 1387 { 1388 *arguments = args; 1389 } 1390 } 1391 } 1392 1393 psl->Release(); 1394 } 1395 return success; 1396} 1397 1398#endif // __WIN32__ && !__WXWINCE__ 1399 1400 1401// ---------------------------------------------------------------------------- 1402// absolute/relative paths 1403// ---------------------------------------------------------------------------- 1404 1405bool wxFileName::IsAbsolute(wxPathFormat format) const 1406{ 1407 // if our path doesn't start with a path separator, it's not an absolute 1408 // path 1409 if ( m_relative ) 1410 return false; 1411 1412 if ( !GetVolumeSeparator(format).empty() ) 1413 { 1414 // this format has volumes and an absolute path must have one, it's not 1415 // enough to have the full path to bean absolute file under Windows 1416 if ( GetVolume().empty() ) 1417 return false; 1418 } 1419 1420 return true; 1421} 1422 1423bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format) 1424{ 1425 wxFileName fnBase = wxFileName::DirName(pathBase, format); 1426 1427 // get cwd only once - small time saving 1428 wxString cwd = wxGetCwd(); 1429 Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format); 1430 fnBase.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format); 1431 1432 bool withCase = IsCaseSensitive(format); 1433 1434 // we can't do anything if the files live on different volumes 1435 if ( !GetVolume().IsSameAs(fnBase.GetVolume(), withCase) ) 1436 { 1437 // nothing done 1438 return false; 1439 } 1440 1441 // same drive, so we don't need our volume 1442 m_volume.clear(); 1443 1444 // remove common directories starting at the top 1445 while ( !m_dirs.IsEmpty() && !fnBase.m_dirs.IsEmpty() && 1446 m_dirs[0u].IsSameAs(fnBase.m_dirs[0u], withCase) ) 1447 { 1448 m_dirs.RemoveAt(0); 1449 fnBase.m_dirs.RemoveAt(0); 1450 } 1451 1452 // add as many ".." as needed 1453 size_t count = fnBase.m_dirs.GetCount(); 1454 for ( size_t i = 0; i < count; i++ ) 1455 { 1456 m_dirs.Insert(wxT(".."), 0u); 1457 } 1458 1459 if ( format == wxPATH_UNIX || format == wxPATH_DOS ) 1460 { 1461 // a directory made relative with respect to itself is '.' under Unix 1462 // and DOS, by definition (but we don't have to insert "./" for the 1463 // files) 1464 if ( m_dirs.IsEmpty() && IsDir() ) 1465 { 1466 m_dirs.Add(_T('.')); 1467 } 1468 } 1469 1470 m_relative = true; 1471 1472 // we were modified 1473 return true; 1474} 1475 1476// ---------------------------------------------------------------------------- 1477// filename kind tests 1478// ---------------------------------------------------------------------------- 1479 1480bool wxFileName::SameAs(const wxFileName& filepath, wxPathFormat format) const 1481{ 1482 wxFileName fn1 = *this, 1483 fn2 = filepath; 1484 1485 // get cwd only once - small time saving 1486 wxString cwd = wxGetCwd(); 1487 fn1.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format); 1488 fn2.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format); 1489 1490 if ( fn1.GetFullPath() == fn2.GetFullPath() ) 1491 return true; 1492 1493 // TODO: compare inodes for Unix, this works even when filenames are 1494 // different but files are the same (symlinks) (VZ) 1495 1496 return false; 1497} 1498 1499/* static */ 1500bool wxFileName::IsCaseSensitive( wxPathFormat format ) 1501{ 1502 // only Unix filenames are truely case-sensitive 1503 return GetFormat(format) == wxPATH_UNIX; 1504} 1505 1506/* static */ 1507wxString wxFileName::GetForbiddenChars(wxPathFormat format) 1508{ 1509 // Inits to forbidden characters that are common to (almost) all platforms. 1510 wxString strForbiddenChars = wxT("*?"); 1511 1512 // If asserts, wxPathFormat has been changed. In case of a new path format 1513 // addition, the following code might have to be updated. 1514 wxCOMPILE_TIME_ASSERT(wxPATH_MAX == 5, wxPathFormatChanged); 1515 switch ( GetFormat(format) ) 1516 { 1517 default : 1518 wxFAIL_MSG( wxT("Unknown path format") ); 1519 // !! Fall through !! 1520 1521 case wxPATH_UNIX: 1522 break; 1523 1524 case wxPATH_MAC: 1525 // On a Mac even names with * and ? are allowed (Tested with OS 1526 // 9.2.1 and OS X 10.2.5) 1527 strForbiddenChars = wxEmptyString; 1528 break; 1529 1530 case wxPATH_DOS: 1531 strForbiddenChars += wxT("\\/:\"<>|"); 1532 break; 1533 1534 case wxPATH_VMS: 1535 break; 1536 } 1537 1538 return strForbiddenChars; 1539} 1540 1541/* static */ 1542wxString wxFileName::GetVolumeSeparator(wxPathFormat WXUNUSED_IN_WINCE(format)) 1543{ 1544#ifdef __WXWINCE__ 1545 return wxEmptyString; 1546#else 1547 wxString sepVol; 1548 1549 if ( (GetFormat(format) == wxPATH_DOS) || 1550 (GetFormat(format) == wxPATH_VMS) ) 1551 { 1552 sepVol = wxFILE_SEP_DSK; 1553 } 1554 //else: leave empty 1555 1556 return sepVol; 1557#endif 1558} 1559 1560/* static */ 1561wxString wxFileName::GetPathSeparators(wxPathFormat format) 1562{ 1563 wxString seps; 1564 switch ( GetFormat(format) ) 1565 { 1566 case wxPATH_DOS: 1567 // accept both as native APIs do but put the native one first as 1568 // this is the one we use in GetFullPath() 1569 seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX; 1570 break; 1571 1572 default: 1573 wxFAIL_MSG( _T("Unknown wxPATH_XXX style") ); 1574 // fall through 1575 1576 case wxPATH_UNIX: 1577 seps = wxFILE_SEP_PATH_UNIX; 1578 break; 1579 1580 case wxPATH_MAC: 1581 seps = wxFILE_SEP_PATH_MAC; 1582 break; 1583 1584 case wxPATH_VMS: 1585 seps = wxFILE_SEP_PATH_VMS; 1586 break; 1587 } 1588 1589 return seps; 1590} 1591 1592/* static */ 1593wxString wxFileName::GetPathTerminators(wxPathFormat format) 1594{ 1595 format = GetFormat(format); 1596 1597 // under VMS the end of the path is ']', not the path separator used to 1598 // separate the components 1599 return format == wxPATH_VMS ? wxString(_T(']')) : GetPathSeparators(format); 1600} 1601 1602/* static */ 1603bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format) 1604{ 1605 // wxString::Find() doesn't work as expected with NUL - it will always find 1606 // it, so test for it separately 1607 return ch != _T('\0') && GetPathSeparators(format).Find(ch) != wxNOT_FOUND; 1608} 1609 1610// ---------------------------------------------------------------------------- 1611// path components manipulation 1612// ---------------------------------------------------------------------------- 1613 1614/* static */ bool wxFileName::IsValidDirComponent(const wxString& dir) 1615{ 1616 if ( dir.empty() ) 1617 { 1618 wxFAIL_MSG( _T("empty directory passed to wxFileName::InsertDir()") ); 1619 1620 return false; 1621 } 1622 1623 const size_t len = dir.length(); 1624 for ( size_t n = 0; n < len; n++ ) 1625 { 1626 if ( dir[n] == GetVolumeSeparator() || IsPathSeparator(dir[n]) ) 1627 { 1628 wxFAIL_MSG( _T("invalid directory component in wxFileName") ); 1629 1630 return false; 1631 } 1632 } 1633 1634 return true; 1635} 1636 1637void wxFileName::AppendDir( const wxString& dir ) 1638{ 1639 if ( IsValidDirComponent(dir) ) 1640 m_dirs.Add( dir ); 1641} 1642 1643void wxFileName::PrependDir( const wxString& dir ) 1644{ 1645 InsertDir(0, dir); 1646} 1647 1648void wxFileName::InsertDir(size_t before, const wxString& dir) 1649{ 1650 if ( IsValidDirComponent(dir) ) 1651 m_dirs.Insert(dir, before); 1652} 1653 1654void wxFileName::RemoveDir(size_t pos) 1655{ 1656 m_dirs.RemoveAt(pos); 1657} 1658 1659// ---------------------------------------------------------------------------- 1660// accessors 1661// ---------------------------------------------------------------------------- 1662 1663void wxFileName::SetFullName(const wxString& fullname) 1664{ 1665 SplitPath(fullname, NULL /* no volume */, NULL /* no path */, 1666 &m_name, &m_ext, &m_hasExt); 1667} 1668 1669wxString wxFileName::GetFullName() const 1670{ 1671 wxString fullname = m_name; 1672 if ( m_hasExt ) 1673 { 1674 fullname << wxFILE_SEP_EXT << m_ext; 1675 } 1676 1677 return fullname; 1678} 1679 1680wxString wxFileName::GetPath( int flags, wxPathFormat format ) const 1681{ 1682 format = GetFormat( format ); 1683 1684 wxString fullpath; 1685 1686 // return the volume with the path as well if requested 1687 if ( flags & wxPATH_GET_VOLUME ) 1688 { 1689 fullpath += wxGetVolumeString(GetVolume(), format); 1690 } 1691 1692 // the leading character 1693 switch ( format ) 1694 { 1695 case wxPATH_MAC: 1696 if ( m_relative ) 1697 fullpath += wxFILE_SEP_PATH_MAC; 1698 break; 1699 1700 case wxPATH_DOS: 1701 if ( !m_relative ) 1702 fullpath += wxFILE_SEP_PATH_DOS; 1703 break; 1704 1705 default: 1706 wxFAIL_MSG( wxT("Unknown path format") ); 1707 // fall through 1708 1709 case wxPATH_UNIX: 1710 if ( !m_relative ) 1711 { 1712 // normally the absolute file names start with a slash 1713 // with one exception: the ones like "~/foo.bar" don't 1714 // have it 1715 if ( m_dirs.IsEmpty() || m_dirs[0u] != _T('~') ) 1716 { 1717 fullpath += wxFILE_SEP_PATH_UNIX; 1718 } 1719 } 1720 break; 1721 1722 case wxPATH_VMS: 1723 // no leading character here but use this place to unset 1724 // wxPATH_GET_SEPARATOR flag: under VMS it doesn't make sense 1725 // as, if I understand correctly, there should never be a dot 1726 // before the closing bracket 1727 flags &= ~wxPATH_GET_SEPARATOR; 1728 } 1729 1730 if ( m_dirs.empty() ) 1731 { 1732 // there is nothing more 1733 return fullpath; 1734 } 1735 1736 // then concatenate all the path components using the path separator 1737 if ( format == wxPATH_VMS ) 1738 { 1739 fullpath += wxT('['); 1740 } 1741 1742 const size_t dirCount = m_dirs.GetCount(); 1743 for ( size_t i = 0; i < dirCount; i++ ) 1744 { 1745 switch (format) 1746 { 1747 case wxPATH_MAC: 1748 if ( m_dirs[i] == wxT(".") ) 1749 { 1750 // skip appending ':', this shouldn't be done in this 1751 // case as "::" is interpreted as ".." under Unix 1752 continue; 1753 } 1754 1755 // convert back from ".." to nothing 1756 if ( !m_dirs[i].IsSameAs(wxT("..")) ) 1757 fullpath += m_dirs[i]; 1758 break; 1759 1760 default: 1761 wxFAIL_MSG( wxT("Unexpected path format") ); 1762 // still fall through 1763 1764 case wxPATH_DOS: 1765 case wxPATH_UNIX: 1766 fullpath += m_dirs[i]; 1767 break; 1768 1769 case wxPATH_VMS: 1770 // TODO: What to do with ".." under VMS 1771 1772 // convert back from ".." to nothing 1773 if ( !m_dirs[i].IsSameAs(wxT("..")) ) 1774 fullpath += m_dirs[i]; 1775 break; 1776 } 1777 1778 if ( (flags & wxPATH_GET_SEPARATOR) || (i != dirCount - 1) ) 1779 fullpath += GetPathSeparator(format); 1780 } 1781 1782 if ( format == wxPATH_VMS ) 1783 { 1784 fullpath += wxT(']'); 1785 } 1786 1787 return fullpath; 1788} 1789 1790wxString wxFileName::GetFullPath( wxPathFormat format ) const 1791{ 1792 // we already have a function to get the path 1793 wxString fullpath = GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR, 1794 format); 1795 1796 // now just add the file name and extension to it 1797 fullpath += GetFullName(); 1798 1799 return fullpath; 1800} 1801 1802// Return the short form of the path (returns identity on non-Windows platforms) 1803wxString wxFileName::GetShortPath() const 1804{ 1805 wxString path(GetFullPath()); 1806 1807#if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__) 1808 DWORD sz = ::GetShortPathName(path, NULL, 0); 1809 if ( sz != 0 ) 1810 { 1811 wxString pathOut; 1812 if ( ::GetShortPathName 1813 ( 1814 path, 1815 wxStringBuffer(pathOut, sz), 1816 sz 1817 ) != 0 ) 1818 { 1819 return pathOut; 1820 } 1821 } 1822#endif // Windows 1823 1824 return path; 1825} 1826 1827// Return the long form of the path (returns identity on non-Windows platforms) 1828wxString wxFileName::GetLongPath() const 1829{ 1830 wxString pathOut, 1831 path = GetFullPath(); 1832 1833#if defined(__WIN32__) && !defined(__WXWINCE__) && !defined(__WXMICROWIN__) 1834 1835#if wxUSE_DYNLIB_CLASS 1836 typedef DWORD (WINAPI *GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD); 1837 1838 // this is MT-safe as in the worst case we're going to resolve the function 1839 // twice -- but as the result is the same in both threads, it's ok 1840 static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL; 1841 if ( !s_pfnGetLongPathName ) 1842 { 1843 static bool s_triedToLoad = false; 1844 1845 if ( !s_triedToLoad ) 1846 { 1847 s_triedToLoad = true; 1848 1849 wxDynamicLibrary dllKernel(_T("kernel32")); 1850 1851 const wxChar* GetLongPathName = _T("GetLongPathName") 1852#if wxUSE_UNICODE 1853 _T("W"); 1854#else // ANSI 1855 _T("A"); 1856#endif // Unicode/ANSI 1857 1858 if ( dllKernel.HasSymbol(GetLongPathName) ) 1859 { 1860 s_pfnGetLongPathName = (GET_LONG_PATH_NAME) 1861 dllKernel.GetSymbol(GetLongPathName); 1862 } 1863 1864 // note that kernel32.dll can be unloaded, it stays in memory 1865 // anyhow as all Win32 programs link to it and so it's safe to call 1866 // GetLongPathName() even after unloading it 1867 } 1868 } 1869 1870 if ( s_pfnGetLongPathName ) 1871 { 1872 DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0); 1873 if ( dwSize > 0 ) 1874 { 1875 if ( (*s_pfnGetLongPathName) 1876 ( 1877 path, 1878 wxStringBuffer(pathOut, dwSize), 1879 dwSize 1880 ) != 0 ) 1881 { 1882 return pathOut; 1883 } 1884 } 1885 } 1886#endif // wxUSE_DYNLIB_CLASS 1887 1888 // The OS didn't support GetLongPathName, or some other error. 1889 // We need to call FindFirstFile on each component in turn. 1890 1891 WIN32_FIND_DATA findFileData; 1892 HANDLE hFind; 1893 1894 if ( HasVolume() ) 1895 pathOut = GetVolume() + 1896 GetVolumeSeparator(wxPATH_DOS) + 1897 GetPathSeparator(wxPATH_DOS); 1898 else 1899 pathOut = wxEmptyString; 1900 1901 wxArrayString dirs = GetDirs(); 1902 dirs.Add(GetFullName()); 1903 1904 wxString tmpPath; 1905 1906 size_t count = dirs.GetCount(); 1907 for ( size_t i = 0; i < count; i++ ) 1908 { 1909 // We're using pathOut to collect the long-name path, but using a 1910 // temporary for appending the last path component which may be 1911 // short-name 1912 tmpPath = pathOut + dirs[i]; 1913 1914 if ( tmpPath.empty() ) 1915 continue; 1916 1917 // can't see this being necessary? MF 1918 if ( tmpPath.Last() == GetVolumeSeparator(wxPATH_DOS) ) 1919 { 1920 // Can't pass a drive and root dir to FindFirstFile, 1921 // so continue to next dir 1922 tmpPath += wxFILE_SEP_PATH; 1923 pathOut = tmpPath; 1924 continue; 1925 } 1926 1927 hFind = ::FindFirstFile(tmpPath, &findFileData); 1928 if (hFind == INVALID_HANDLE_VALUE) 1929 { 1930 // Error: most likely reason is that path doesn't exist, so 1931 // append any unprocessed parts and return 1932 for ( i += 1; i < count; i++ ) 1933 tmpPath += wxFILE_SEP_PATH + dirs[i]; 1934 1935 return tmpPath; 1936 } 1937 1938 pathOut += findFileData.cFileName; 1939 if ( (i < (count-1)) ) 1940 pathOut += wxFILE_SEP_PATH; 1941 1942 ::FindClose(hFind); 1943 } 1944#else // !Win32 1945 pathOut = path; 1946#endif // Win32/!Win32 1947 1948 return pathOut; 1949} 1950 1951wxPathFormat wxFileName::GetFormat( wxPathFormat format ) 1952{ 1953 if (format == wxPATH_NATIVE) 1954 { 1955#if defined(__WXMSW__) || defined(__OS2__) || defined(__DOS__) 1956 format = wxPATH_DOS; 1957#elif defined(__WXMAC__) && !defined(__DARWIN__) 1958 format = wxPATH_MAC; 1959#elif defined(__VMS) 1960 format = wxPATH_VMS; 1961#else 1962 format = wxPATH_UNIX; 1963#endif 1964 } 1965 return format; 1966} 1967 1968// ---------------------------------------------------------------------------- 1969// path splitting function 1970// ---------------------------------------------------------------------------- 1971 1972/* static */ 1973void 1974wxFileName::SplitVolume(const wxString& fullpathWithVolume, 1975 wxString *pstrVolume, 1976 wxString *pstrPath, 1977 wxPathFormat format) 1978{ 1979 format = GetFormat(format); 1980 1981 wxString fullpath = fullpathWithVolume; 1982 1983 // special Windows UNC paths hack: transform \\share\path into share:path 1984 if ( IsUNCPath(fullpath, format) ) 1985 { 1986 fullpath.erase(0, 2); 1987 1988 size_t posFirstSlash = 1989 fullpath.find_first_of(GetPathTerminators(format)); 1990 if ( posFirstSlash != wxString::npos ) 1991 { 1992 fullpath[posFirstSlash] = wxFILE_SEP_DSK; 1993 1994 // UNC paths are always absolute, right? (FIXME) 1995 fullpath.insert(posFirstSlash + 1, 1, wxFILE_SEP_PATH_DOS); 1996 } 1997 } 1998 1999 // We separate the volume here 2000 if ( format == wxPATH_DOS || format == wxPATH_VMS ) 2001 { 2002 wxString sepVol = GetVolumeSeparator(format); 2003 2004 size_t posFirstColon = fullpath.find_first_of(sepVol); 2005 if ( posFirstColon != wxString::npos ) 2006 { 2007 if ( pstrVolume ) 2008 { 2009 *pstrVolume = fullpath.Left(posFirstColon); 2010 } 2011 2012 // remove the volume name and the separator from the full path 2013 fullpath.erase(0, posFirstColon + sepVol.length()); 2014 } 2015 } 2016 2017 if ( pstrPath ) 2018 *pstrPath = fullpath; 2019} 2020 2021/* static */ 2022void wxFileName::SplitPath(const wxString& fullpathWithVolume, 2023 wxString *pstrVolume, 2024 wxString *pstrPath, 2025 wxString *pstrName, 2026 wxString *pstrExt, 2027 bool *hasExt, 2028 wxPathFormat format) 2029{ 2030 format = GetFormat(format); 2031 2032 wxString fullpath; 2033 SplitVolume(fullpathWithVolume, pstrVolume, &fullpath, format); 2034 2035 // find the positions of the last dot and last path separator in the path 2036 size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT); 2037 size_t posLastSlash = fullpath.find_last_of(GetPathTerminators(format)); 2038 2039 // check whether this dot occurs at the very beginning of a path component 2040 if ( (posLastDot != wxString::npos) && 2041 (posLastDot == 0 || 2042 IsPathSeparator(fullpath[posLastDot - 1]) || 2043 (format == wxPATH_VMS && fullpath[posLastDot - 1] == _T(']'))) ) 2044 { 2045 // dot may be (and commonly -- at least under Unix -- is) the first 2046 // character of the filename, don't treat the entire filename as 2047 // extension in this case 2048 posLastDot = wxString::npos; 2049 } 2050 2051 // if we do have a dot and a slash, check that the dot is in the name part 2052 if ( (posLastDot != wxString::npos) && 2053 (posLastSlash != wxString::npos) && 2054 (posLastDot < posLastSlash) ) 2055 { 2056 // the dot is part of the path, not the start of the extension 2057 posLastDot = wxString::npos; 2058 } 2059 2060 // now fill in the variables provided by user 2061 if ( pstrPath ) 2062 { 2063 if ( posLastSlash == wxString::npos ) 2064 { 2065 // no path at all 2066 pstrPath->Empty(); 2067 } 2068 else 2069 { 2070 // take everything up to the path separator but take care to make 2071 // the path equal to something like '/', not empty, for the files 2072 // immediately under root directory 2073 size_t len = posLastSlash; 2074 2075 // this rule does not apply to mac since we do not start with colons (sep) 2076 // except for relative paths 2077 if ( !len && format != wxPATH_MAC) 2078 len++; 2079 2080 *pstrPath = fullpath.Left(len); 2081 2082 // special VMS hack: remove the initial bracket 2083 if ( format == wxPATH_VMS ) 2084 { 2085 if ( (*pstrPath)[0u] == _T('[') ) 2086 pstrPath->erase(0, 1); 2087 } 2088 } 2089 } 2090 2091 if ( pstrName ) 2092 { 2093 // take all characters starting from the one after the last slash and 2094 // up to, but excluding, the last dot 2095 size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1; 2096 size_t count; 2097 if ( posLastDot == wxString::npos ) 2098 { 2099 // take all until the end 2100 count = wxString::npos; 2101 } 2102 else if ( posLastSlash == wxString::npos ) 2103 { 2104 count = posLastDot; 2105 } 2106 else // have both dot and slash 2107 { 2108 count = posLastDot - posLastSlash - 1; 2109 } 2110 2111 *pstrName = fullpath.Mid(nStart, count); 2112 } 2113 2114 // finally deal with the extension here: we have an added complication that 2115 // extension may be empty (but present) as in "foo." where trailing dot 2116 // indicates the empty extension at the end -- and hence we must remember 2117 // that we have it independently of pstrExt 2118 if ( posLastDot == wxString::npos ) 2119 { 2120 // no extension 2121 if ( pstrExt ) 2122 pstrExt->clear(); 2123 if ( hasExt ) 2124 *hasExt = false; 2125 } 2126 else 2127 { 2128 // take everything after the dot 2129 if ( pstrExt ) 2130 *pstrExt = fullpath.Mid(posLastDot + 1); 2131 if ( hasExt ) 2132 *hasExt = true; 2133 } 2134} 2135 2136/* static */ 2137void wxFileName::SplitPath(const wxString& fullpath, 2138 wxString *path, 2139 wxString *name, 2140 wxString *ext, 2141 wxPathFormat format) 2142{ 2143 wxString volume; 2144 SplitPath(fullpath, &volume, path, name, ext, format); 2145 2146 if ( path ) 2147 { 2148 path->Prepend(wxGetVolumeString(volume, format)); 2149 } 2150} 2151 2152/* static */ 2153wxString wxFileName::StripExtension(const wxString& fullpath) 2154{ 2155 wxFileName fn(fullpath); 2156 fn.SetExt(_T("")); 2157 return fn.GetFullPath(); 2158} 2159 2160// ---------------------------------------------------------------------------- 2161// time functions 2162// ---------------------------------------------------------------------------- 2163 2164#if wxUSE_DATETIME 2165 2166bool wxFileName::SetTimes(const wxDateTime *dtAccess, 2167 const wxDateTime *dtMod, 2168 const wxDateTime *dtCreate) 2169{ 2170#if defined(__WIN32__) 2171 FILETIME ftAccess, ftCreate, ftWrite; 2172 2173 if ( dtCreate ) 2174 ConvertWxToFileTime(&ftCreate, *dtCreate); 2175 if ( dtAccess ) 2176 ConvertWxToFileTime(&ftAccess, *dtAccess); 2177 if ( dtMod ) 2178 ConvertWxToFileTime(&ftWrite, *dtMod); 2179 2180 wxString path; 2181 int flags; 2182 if ( IsDir() ) 2183 { 2184 if ( wxGetOsVersion() == wxOS_WINDOWS_9X ) 2185 { 2186 wxLogError(_("Setting directory access times is not supported under this OS version")); 2187 return false; 2188 } 2189 2190 path = GetPath(); 2191 flags = FILE_FLAG_BACKUP_SEMANTICS; 2192 } 2193 else // file 2194 { 2195 path = GetFullPath(); 2196 flags = 0; 2197 } 2198 2199 wxFileHandle fh(path, wxFileHandle::Write, flags); 2200 if ( fh.IsOk() ) 2201 { 2202 if ( ::SetFileTime(fh, 2203 dtCreate ? &ftCreate : NULL, 2204 dtAccess ? &ftAccess : NULL, 2205 dtMod ? &ftWrite : NULL) ) 2206 { 2207 return true; 2208 } 2209 } 2210#elif defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__)) 2211 wxUnusedVar(dtCreate); 2212 2213 if ( !dtAccess && !dtMod ) 2214 { 2215 // can't modify the creation time anyhow, don't try 2216 return true; 2217 } 2218 2219 // if dtAccess or dtMod is not specified, use the other one (which must be 2220 // non NULL because of the test above) for both times 2221 utimbuf utm; 2222 utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks(); 2223 utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks(); 2224 if ( utime(GetFullPath().fn_str(), &utm) == 0 ) 2225 { 2226 return true; 2227 } 2228#else // other platform 2229 wxUnusedVar(dtAccess); 2230 wxUnusedVar(dtMod); 2231 wxUnusedVar(dtCreate); 2232#endif // platforms 2233 2234 wxLogSysError(_("Failed to modify file times for '%s'"), 2235 GetFullPath().c_str()); 2236 2237 return false; 2238} 2239 2240bool wxFileName::Touch() 2241{ 2242#if defined(__UNIX_LIKE__) 2243 // under Unix touching file is simple: just pass NULL to utime() 2244 if ( utime(GetFullPath().fn_str(), NULL) == 0 ) 2245 { 2246 return true; 2247 } 2248 2249 wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str()); 2250 2251 return false; 2252#else // other platform 2253 wxDateTime dtNow = wxDateTime::Now(); 2254 2255 return SetTimes(&dtNow, &dtNow, NULL /* don't change create time */); 2256#endif // platforms 2257} 2258 2259#ifdef wxNEED_WX_UNISTD_H 2260 2261static int wxStat( const char *file_name, wxStructStat *buf ) 2262{ 2263 return stat( file_name , buf ); 2264} 2265 2266#endif 2267 2268bool wxFileName::GetTimes(wxDateTime *dtAccess, 2269 wxDateTime *dtMod, 2270 wxDateTime *dtCreate) const 2271{ 2272#if defined(__WIN32__) 2273 // we must use different methods for the files and directories under 2274 // Windows as CreateFile(GENERIC_READ) doesn't work for the directories and 2275 // CreateFile(FILE_FLAG_BACKUP_SEMANTICS) works -- but only under NT and 2276 // not 9x 2277 bool ok; 2278 FILETIME ftAccess, ftCreate, ftWrite; 2279 if ( IsDir() ) 2280 { 2281 // implemented in msw/dir.cpp 2282 extern bool wxGetDirectoryTimes(const wxString& dirname, 2283 FILETIME *, FILETIME *, FILETIME *); 2284 2285 // we should pass the path without the trailing separator to 2286 // wxGetDirectoryTimes() 2287 ok = wxGetDirectoryTimes(GetPath(wxPATH_GET_VOLUME), 2288 &ftAccess, &ftCreate, &ftWrite); 2289 } 2290 else // file 2291 { 2292 wxFileHandle fh(GetFullPath(), wxFileHandle::Read); 2293 if ( fh.IsOk() ) 2294 { 2295 ok = ::GetFileTime(fh, 2296 dtCreate ? &ftCreate : NULL, 2297 dtAccess ? &ftAccess : NULL, 2298 dtMod ? &ftWrite : NULL) != 0; 2299 } 2300 else 2301 { 2302 ok = false; 2303 } 2304 } 2305 2306 if ( ok ) 2307 { 2308 if ( dtCreate ) 2309 ConvertFileTimeToWx(dtCreate, ftCreate); 2310 if ( dtAccess ) 2311 ConvertFileTimeToWx(dtAccess, ftAccess); 2312 if ( dtMod ) 2313 ConvertFileTimeToWx(dtMod, ftWrite); 2314 2315 return true; 2316 } 2317#elif defined(__UNIX_LIKE__) || defined(__WXMAC__) || defined(__OS2__) || (defined(__DOS__) && defined(__WATCOMC__)) 2318 // no need to test for IsDir() here 2319 wxStructStat stBuf; 2320 if ( wxStat( GetFullPath().fn_str(), &stBuf) == 0 ) 2321 { 2322 if ( dtAccess ) 2323 dtAccess->Set(stBuf.st_atime); 2324 if ( dtMod ) 2325 dtMod->Set(stBuf.st_mtime); 2326 if ( dtCreate ) 2327 dtCreate->Set(stBuf.st_ctime); 2328 2329 return true; 2330 } 2331#else // other platform 2332 wxUnusedVar(dtAccess); 2333 wxUnusedVar(dtMod); 2334 wxUnusedVar(dtCreate); 2335#endif // platforms 2336 2337 wxLogSysError(_("Failed to retrieve file times for '%s'"), 2338 GetFullPath().c_str()); 2339 2340 return false; 2341} 2342 2343#endif // wxUSE_DATETIME 2344 2345 2346// ---------------------------------------------------------------------------- 2347// file size functions 2348// ---------------------------------------------------------------------------- 2349 2350/* static */ 2351wxULongLong wxFileName::GetSize(const wxString &filename) 2352{ 2353 if (!wxFileExists(filename)) 2354 return wxInvalidSize; 2355 2356#if defined(__WXPALMOS__) 2357 // TODO 2358 return wxInvalidSize; 2359#elif defined(__WIN32__) 2360 wxFileHandle f(filename, wxFileHandle::Read); 2361 if (!f.IsOk()) 2362 return wxInvalidSize; 2363 2364 DWORD lpFileSizeHigh; 2365 DWORD ret = GetFileSize(f, &lpFileSizeHigh); 2366 if ( ret == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR ) 2367 return wxInvalidSize; 2368 2369 return wxULongLong(lpFileSizeHigh, ret); 2370#else // ! __WIN32__ 2371 wxStructStat st; 2372#ifndef wxNEED_WX_UNISTD_H 2373 if (wxStat( filename.fn_str() , &st) != 0) 2374#else 2375 if (wxStat( filename, &st) != 0) 2376#endif 2377 return wxInvalidSize; 2378 return wxULongLong(st.st_size); 2379#endif 2380} 2381 2382/* static */ 2383wxString wxFileName::GetHumanReadableSize(const wxULongLong &bs, 2384 const wxString &nullsize, 2385 int precision) 2386{ 2387 static const double KILOBYTESIZE = 1024.0; 2388 static const double MEGABYTESIZE = 1024.0*KILOBYTESIZE; 2389 static const double GIGABYTESIZE = 1024.0*MEGABYTESIZE; 2390 static const double TERABYTESIZE = 1024.0*GIGABYTESIZE; 2391 2392 if (bs == 0 || bs == wxInvalidSize) 2393 return nullsize; 2394 2395 double bytesize = bs.ToDouble(); 2396 if (bytesize < KILOBYTESIZE) 2397 return wxString::Format(_("%s B"), bs.ToString().c_str()); 2398 if (bytesize < MEGABYTESIZE) 2399 return wxString::Format(_("%.*f kB"), precision, bytesize/KILOBYTESIZE); 2400 if (bytesize < GIGABYTESIZE) 2401 return wxString::Format(_("%.*f MB"), precision, bytesize/MEGABYTESIZE); 2402 if (bytesize < TERABYTESIZE) 2403 return wxString::Format(_("%.*f GB"), precision, bytesize/GIGABYTESIZE); 2404 2405 return wxString::Format(_("%.*f TB"), precision, bytesize/TERABYTESIZE); 2406} 2407 2408wxULongLong wxFileName::GetSize() const 2409{ 2410 return GetSize(GetFullPath()); 2411} 2412 2413wxString wxFileName::GetHumanReadableSize(const wxString &failmsg, int precision) const 2414{ 2415 return GetHumanReadableSize(GetSize(), failmsg, precision); 2416} 2417 2418 2419// ---------------------------------------------------------------------------- 2420// Mac-specific functions 2421// ---------------------------------------------------------------------------- 2422 2423#ifdef __WXMAC__ 2424 2425const short kMacExtensionMaxLength = 16 ; 2426class MacDefaultExtensionRecord 2427{ 2428public : 2429 MacDefaultExtensionRecord() 2430 { 2431 m_ext[0] = 0 ; 2432 m_type = m_creator = 0 ; 2433 } 2434 MacDefaultExtensionRecord( const MacDefaultExtensionRecord& from ) 2435 { 2436 wxStrcpy( m_ext , from.m_ext ) ; 2437 m_type = from.m_type ; 2438 m_creator = from.m_creator ; 2439 } 2440 MacDefaultExtensionRecord( const wxChar * extension , OSType type , OSType creator ) 2441 { 2442 wxStrncpy( m_ext , extension , kMacExtensionMaxLength ) ; 2443 m_ext[kMacExtensionMaxLength] = 0 ; 2444 m_type = type ; 2445 m_creator = creator ; 2446 } 2447 wxChar m_ext[kMacExtensionMaxLength] ; 2448 OSType m_type ; 2449 OSType m_creator ; 2450} ; 2451 2452WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord, MacDefaultExtensionArray) ; 2453 2454bool gMacDefaultExtensionsInited = false ; 2455 2456#include "wx/arrimpl.cpp" 2457 2458WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray) ; 2459 2460MacDefaultExtensionArray gMacDefaultExtensions ; 2461 2462// load the default extensions 2463MacDefaultExtensionRecord gDefaults[] = 2464{ 2465 MacDefaultExtensionRecord( wxT("txt") , 'TEXT' , 'ttxt' ) , 2466 MacDefaultExtensionRecord( wxT("tif") , 'TIFF' , '****' ) , 2467 MacDefaultExtensionRecord( wxT("jpg") , 'JPEG' , '****' ) , 2468} ; 2469 2470static void MacEnsureDefaultExtensionsLoaded() 2471{ 2472 if ( !gMacDefaultExtensionsInited ) 2473 { 2474 // we could load the pc exchange prefs here too 2475 for ( size_t i = 0 ; i < WXSIZEOF( gDefaults ) ; ++i ) 2476 { 2477 gMacDefaultExtensions.Add( gDefaults[i] ) ; 2478 } 2479 gMacDefaultExtensionsInited = true ; 2480 } 2481} 2482 2483bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator ) 2484{ 2485 FSRef fsRef ; 2486 FSCatalogInfo catInfo; 2487 FileInfo *finfo ; 2488 2489 if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr ) 2490 { 2491 if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr ) 2492 { 2493 finfo = (FileInfo*)&catInfo.finderInfo; 2494 finfo->fileType = type ; 2495 finfo->fileCreator = creator ; 2496 FSSetCatalogInfo( &fsRef, kFSCatInfoFinderInfo, &catInfo ) ; 2497 return true ; 2498 } 2499 } 2500 return false ; 2501} 2502 2503bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator ) 2504{ 2505 FSRef fsRef ; 2506 FSCatalogInfo catInfo; 2507 FileInfo *finfo ; 2508 2509 if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr ) 2510 { 2511 if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr ) 2512 { 2513 finfo = (FileInfo*)&catInfo.finderInfo; 2514 *type = finfo->fileType ; 2515 *creator = finfo->fileCreator ; 2516 return true ; 2517 } 2518 } 2519 return false ; 2520} 2521 2522bool wxFileName::MacSetDefaultTypeAndCreator() 2523{ 2524 wxUint32 type , creator ; 2525 if ( wxFileName::MacFindDefaultTypeAndCreator(GetExt() , &type , 2526 &creator ) ) 2527 { 2528 return MacSetTypeAndCreator( type , creator ) ; 2529 } 2530 return false; 2531} 2532 2533bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator ) 2534{ 2535 MacEnsureDefaultExtensionsLoaded() ; 2536 wxString extl = ext.Lower() ; 2537 for( int i = gMacDefaultExtensions.Count() - 1 ; i >= 0 ; --i ) 2538 { 2539 if ( gMacDefaultExtensions.Item(i).m_ext == extl ) 2540 { 2541 *type = gMacDefaultExtensions.Item(i).m_type ; 2542 *creator = gMacDefaultExtensions.Item(i).m_creator ; 2543 return true ; 2544 } 2545 } 2546 return false ; 2547} 2548 2549void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator ) 2550{ 2551 MacEnsureDefaultExtensionsLoaded() ; 2552 MacDefaultExtensionRecord rec ; 2553 rec.m_type = type ; 2554 rec.m_creator = creator ; 2555 wxStrncpy( rec.m_ext , ext.Lower().c_str() , kMacExtensionMaxLength ) ; 2556 gMacDefaultExtensions.Add( rec ) ; 2557} 2558#endif 2559