home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / src / common / filename.cpp < prev    next >
C/C++ Source or Header  |  2002-08-31  |  54KB  |  1,855 lines

  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,v 1.100 2002/08/31 11:42:12 JS Exp $
  8. // Copyright:   (c) 2000 Robert Roebling
  9. // Licence:     wxWindows license
  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 specyfication
  51.                  . separator between directory and subdirectory
  52.                  ] between directory and file
  53.  */
  54.  
  55. // ============================================================================
  56. // declarations
  57. // ============================================================================
  58.  
  59. // ----------------------------------------------------------------------------
  60. // headers
  61. // ----------------------------------------------------------------------------
  62.  
  63. #ifdef __GNUG__
  64. #pragma implementation "filename.h"
  65. #endif
  66.  
  67. // For compilers that support precompilation, includes "wx.h".
  68. #include "wx/wxprec.h"
  69.  
  70. #ifdef __BORLANDC__
  71. #pragma hdrstop
  72. #endif
  73.  
  74. #ifndef WX_PRECOMP
  75. #include "wx/intl.h"
  76. #include "wx/log.h"
  77. #include "wx/file.h"
  78. #endif
  79.  
  80. #include "wx/filename.h"
  81. #include "wx/tokenzr.h"
  82. #include "wx/config.h"          // for wxExpandEnvVars
  83. #include "wx/utils.h"
  84. #include "wx/file.h"
  85. #include "wx/dynlib.h"
  86.  
  87. // For GetShort/LongPathName
  88. #ifdef __WIN32__
  89. #include <windows.h>
  90. #include "wx/msw/winundef.h"
  91. #endif
  92.  
  93. #if defined(__WXMAC__)
  94.   #include  "wx/mac/private.h"  // includes mac headers
  95. #endif
  96.  
  97. // utime() is POSIX so should normally be available on all Unices
  98. #ifdef __UNIX_LIKE__
  99. #include <sys/types.h>
  100. #include <utime.h>
  101. #include <sys/stat.h>
  102. #include <unistd.h>
  103. #endif
  104.  
  105. #ifdef __DJGPP__
  106. #include <unistd.h>
  107. #endif
  108.  
  109. #ifdef __MWERKS__
  110. #include <stat.h>
  111. #include <unistd.h>
  112. #include <unix.h>
  113. #endif
  114.  
  115. #ifdef __WATCOMC__
  116. #include <io.h>
  117. #include <sys/utime.h>
  118. #include <sys/stat.h>
  119. #endif
  120.  
  121. #ifdef __VISAGECPP__
  122. #ifndef MAX_PATH
  123. #define MAX_PATH 256
  124. #endif
  125. #endif
  126.  
  127. #ifdef __EMX__
  128. #define MAX_PATH _MAX_PATH
  129. #endif
  130.  
  131. // ----------------------------------------------------------------------------
  132. // private classes
  133. // ----------------------------------------------------------------------------
  134.  
  135. // small helper class which opens and closes the file - we use it just to get
  136. // a file handle for the given file name to pass it to some Win32 API function
  137. #if defined(__WIN32__) && !defined(__WXMICROWIN__)
  138.  
  139. class wxFileHandle
  140. {
  141. public:
  142.     enum OpenMode
  143.     {
  144.         Read,
  145.         Write
  146.     };
  147.  
  148.     wxFileHandle(const wxString& filename, OpenMode mode)
  149.     {
  150.         m_hFile = ::CreateFile
  151.                     (
  152.                      filename,                      // name
  153.                      mode == Read ? GENERIC_READ    // access mask
  154.                                   : GENERIC_WRITE,
  155.                      0,                             // no sharing
  156.                      NULL,                          // no secutity attr
  157.                      OPEN_EXISTING,                 // creation disposition
  158.                      0,                             // no flags
  159.                      NULL                           // no template file
  160.                     );
  161.  
  162.         if ( m_hFile == INVALID_HANDLE_VALUE )
  163.         {
  164.             wxLogSysError(_("Failed to open '%s' for %s"),
  165.                           filename.c_str(),
  166.                           mode == Read ? _("reading") : _("writing"));
  167.         }
  168.     }
  169.  
  170.     ~wxFileHandle()
  171.     {
  172.         if ( m_hFile != INVALID_HANDLE_VALUE )
  173.         {
  174.             if ( !::CloseHandle(m_hFile) )
  175.             {
  176.                 wxLogSysError(_("Failed to close file handle"));
  177.             }
  178.         }
  179.     }
  180.  
  181.     // return TRUE only if the file could be opened successfully
  182.     bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; }
  183.  
  184.     // get the handle
  185.     operator HANDLE() const { return m_hFile; }
  186.  
  187. private:
  188.     HANDLE m_hFile;
  189. };
  190.  
  191. #endif // __WIN32__
  192.  
  193. // ----------------------------------------------------------------------------
  194. // private functions
  195. // ----------------------------------------------------------------------------
  196.  
  197. #if defined(__WIN32__) && !defined(__WXMICROWIN__)
  198.  
  199. // convert between wxDateTime and FILETIME which is a 64-bit value representing
  200. // the number of 100-nanosecond intervals since January 1, 1601.
  201.  
  202. static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft)
  203. {
  204.     FILETIME ftcopy = ft;
  205.     FILETIME ftLocal;
  206.     if ( !::FileTimeToLocalFileTime(&ftcopy, &ftLocal) )
  207.     {
  208.         wxLogLastError(_T("FileTimeToLocalFileTime"));
  209.     }
  210.  
  211.     SYSTEMTIME st;
  212.     if ( !::FileTimeToSystemTime(&ftLocal, &st) )
  213.     {
  214.         wxLogLastError(_T("FileTimeToSystemTime"));
  215.     }
  216.  
  217.     dt->Set(st.wDay, wxDateTime::Month(st.wMonth - 1), st.wYear,
  218.             st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
  219. }
  220.  
  221. static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt)
  222. {
  223.     SYSTEMTIME st;
  224.     st.wDay = dt.GetDay();
  225.     st.wMonth = dt.GetMonth() + 1;
  226.     st.wYear = dt.GetYear();
  227.     st.wHour = dt.GetHour();
  228.     st.wMinute = dt.GetMinute();
  229.     st.wSecond = dt.GetSecond();
  230.     st.wMilliseconds = dt.GetMillisecond();
  231.  
  232.     FILETIME ftLocal;
  233.     if ( !::SystemTimeToFileTime(&st, &ftLocal) )
  234.     {
  235.         wxLogLastError(_T("SystemTimeToFileTime"));
  236.     }
  237.  
  238.     if ( !::LocalFileTimeToFileTime(&ftLocal, ft) )
  239.     {
  240.         wxLogLastError(_T("LocalFileTimeToFileTime"));
  241.     }
  242. }
  243.  
  244. #endif // __WIN32__
  245.  
  246. // return a string with the volume par
  247. static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format)
  248. {
  249.     wxString path;
  250.  
  251.     if ( !volume.empty() )
  252.     {
  253.         format = wxFileName::GetFormat(format);
  254.  
  255.         // Special Windows UNC paths hack, part 2: undo what we did in
  256.         // SplitPath() and make an UNC path if we have a drive which is not a
  257.         // single letter (hopefully the network shares can't be one letter only
  258.         // although I didn't find any authoritative docs on this)
  259.         if ( format == wxPATH_DOS && volume.length() > 1 )
  260.         {
  261.             path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
  262.         }
  263.         else if  ( format == wxPATH_DOS || format == wxPATH_VMS )
  264.         {
  265.             path << volume << wxFileName::GetVolumeSeparator(format);
  266.         }
  267.         // else ignore
  268.     }
  269.  
  270.     return path;
  271. }
  272.  
  273. // ============================================================================
  274. // implementation
  275. // ============================================================================
  276.  
  277. // ----------------------------------------------------------------------------
  278. // wxFileName construction
  279. // ----------------------------------------------------------------------------
  280.  
  281. void wxFileName::Assign( const wxFileName &filepath )
  282. {
  283.     m_volume = filepath.GetVolume();
  284.     m_dirs = filepath.GetDirs();
  285.     m_name = filepath.GetName();
  286.     m_ext = filepath.GetExt();
  287.     m_relative = filepath.m_relative;
  288. }
  289.  
  290. void wxFileName::Assign(const wxString& volume,
  291.                         const wxString& path,
  292.                         const wxString& name,
  293.                         const wxString& ext,
  294.                         wxPathFormat format )
  295. {
  296.     SetPath( path, format );
  297.  
  298.     m_volume = volume;
  299.     m_ext = ext;
  300.     m_name = name;
  301. }
  302.  
  303. void wxFileName::SetPath( const wxString &path, wxPathFormat format )
  304. {
  305.     m_dirs.Clear();
  306.  
  307.     if ( !path.empty() )
  308.     {
  309.         wxPathFormat my_format = GetFormat( format );
  310.         wxString my_path = path;
  311.  
  312.         // 1) Determine if the path is relative or absolute.
  313.         wxChar leadingChar = my_path[0u];
  314.  
  315.         switch (my_format)
  316.         {
  317.             case wxPATH_MAC:
  318.                 m_relative = leadingChar == wxT(':');
  319.  
  320.                 // We then remove a leading ":". The reason is in our
  321.                 // storage form for relative paths:
  322.                 // ":dir:file.txt" actually means "./dir/file.txt" in
  323.                 // DOS notation and should get stored as
  324.                 // (relative) (dir) (file.txt)
  325.                 // "::dir:file.txt" actually means "../dir/file.txt"
  326.                 // stored as (relative) (..) (dir) (file.txt)
  327.                 // This is important only for the Mac as an empty dir
  328.                 // actually means <UP>, whereas under DOS, double
  329.                 // slashes can be ignored: "\\\\" is the same as "\\".
  330.                 if (m_relative)
  331.                     my_path.erase( 0, 1 );
  332.                 break;
  333.  
  334.             case wxPATH_VMS:
  335.                 // TODO: what is the relative path format here?
  336.                 m_relative = FALSE;
  337.                 break;
  338.  
  339.             case wxPATH_UNIX:
  340.                 // the paths of the form "~" or "~username" are absolute
  341.                 m_relative = leadingChar != wxT('/') && leadingChar != _T('~');
  342.                 break;
  343.  
  344.             case wxPATH_DOS:
  345.                 m_relative = !IsPathSeparator(leadingChar, my_format);
  346.                 break;
  347.  
  348.             default:
  349.                 wxFAIL_MSG( wxT("error") );
  350.                 break;
  351.         }
  352.  
  353.         // 2) Break up the path into its members. If the original path
  354.         //    was just "/" or "\\", m_dirs will be empty. We know from
  355.         //    the m_relative field, if this means "nothing" or "root dir".
  356.  
  357.         wxStringTokenizer tn( my_path, GetPathSeparators(my_format) );
  358.  
  359.         while ( tn.HasMoreTokens() )
  360.         {
  361.             wxString token = tn.GetNextToken();
  362.  
  363.             // Remove empty token under DOS and Unix, interpret them
  364.             // as .. under Mac.
  365.             if (token.empty())
  366.             {
  367.                 if (my_format == wxPATH_MAC)
  368.                     m_dirs.Add( wxT("..") );
  369.                 // else ignore
  370.             }
  371.             else
  372.             {
  373.                m_dirs.Add( token );
  374.             }
  375.         }
  376.     }
  377.     else // no path at all
  378.     {
  379.         m_relative = TRUE;
  380.     }
  381. }
  382.  
  383. void wxFileName::Assign(const wxString& fullpath,
  384.                         wxPathFormat format)
  385. {
  386.     wxString volume, path, name, ext;
  387.     SplitPath(fullpath, &volume, &path, &name, &ext, format);
  388.  
  389.     Assign(volume, path, name, ext, format);
  390. }
  391.  
  392. void wxFileName::Assign(const wxString& fullpathOrig,
  393.                         const wxString& fullname,
  394.                         wxPathFormat format)
  395. {
  396.     // always recognize fullpath as directory, even if it doesn't end with a
  397.     // slash
  398.     wxString fullpath = fullpathOrig;
  399.     if ( !wxEndsWithPathSeparator(fullpath) )
  400.     {
  401.         fullpath += GetPathSeparator(format);
  402.     }
  403.  
  404.     wxString volume, path, name, ext;
  405.  
  406.     // do some consistency checks in debug mode: the name should be really just
  407.     // the filename and the path should be really just a path
  408. #ifdef __WXDEBUG__
  409.     wxString pathDummy, nameDummy, extDummy;
  410.  
  411.     SplitPath(fullname, &pathDummy, &name, &ext, format);
  412.  
  413.     wxASSERT_MSG( pathDummy.empty(),
  414.                   _T("the file name shouldn't contain the path") );
  415.  
  416.     SplitPath(fullpath, &volume, &path, &nameDummy, &extDummy, format);
  417.  
  418.     wxASSERT_MSG( nameDummy.empty() && extDummy.empty(),
  419.                   _T("the path shouldn't contain file name nor extension") );
  420.  
  421. #else // !__WXDEBUG__
  422.     SplitPath(fullname, NULL /* no path */, &name, &ext, format);
  423.     SplitPath(fullpath, &volume, &path, NULL, NULL, format);
  424. #endif // __WXDEBUG__/!__WXDEBUG__
  425.  
  426.     Assign(volume, path, name, ext, format);
  427. }
  428.  
  429. void wxFileName::AssignDir(const wxString& dir, wxPathFormat format)
  430. {
  431.     Assign(dir, _T(""), format);
  432. }
  433.  
  434. void wxFileName::Clear()
  435. {
  436.     m_dirs.Clear();
  437.  
  438.     m_volume =
  439.     m_name =
  440.     m_ext = wxEmptyString;
  441.  
  442.     // we don't have any absolute path for now
  443.     m_relative = TRUE;
  444. }
  445.  
  446. /* static */
  447. wxFileName wxFileName::FileName(const wxString& file)
  448. {
  449.     return wxFileName(file);
  450. }
  451.  
  452. /* static */
  453. wxFileName wxFileName::DirName(const wxString& dir)
  454. {
  455.     wxFileName fn;
  456.     fn.AssignDir(dir);
  457.     return fn;
  458. }
  459.  
  460. // ----------------------------------------------------------------------------
  461. // existence tests
  462. // ----------------------------------------------------------------------------
  463.  
  464. bool wxFileName::FileExists()
  465. {
  466.     return wxFileName::FileExists( GetFullPath() );
  467. }
  468.  
  469. bool wxFileName::FileExists( const wxString &file )
  470. {
  471.     return ::wxFileExists( file );
  472. }
  473.  
  474. bool wxFileName::DirExists()
  475. {
  476.     return wxFileName::DirExists( GetFullPath() );
  477. }
  478.  
  479. bool wxFileName::DirExists( const wxString &dir )
  480. {
  481.     return ::wxDirExists( dir );
  482. }
  483.  
  484. // ----------------------------------------------------------------------------
  485. // CWD and HOME stuff
  486. // ----------------------------------------------------------------------------
  487.  
  488. void wxFileName::AssignCwd(const wxString& volume)
  489. {
  490.     AssignDir(wxFileName::GetCwd(volume));
  491. }
  492.  
  493. /* static */
  494. wxString wxFileName::GetCwd(const wxString& volume)
  495. {
  496.     // if we have the volume, we must get the current directory on this drive
  497.     // and to do this we have to chdir to this volume - at least under Windows,
  498.     // I don't know how to get the current drive on another volume elsewhere
  499.     // (TODO)
  500.     wxString cwdOld;
  501.     if ( !volume.empty() )
  502.     {
  503.         cwdOld = wxGetCwd();
  504.         SetCwd(volume + GetVolumeSeparator());
  505.     }
  506.  
  507.     wxString cwd = ::wxGetCwd();
  508.  
  509.     if ( !volume.empty() )
  510.     {
  511.         SetCwd(cwdOld);
  512.     }
  513.  
  514.     return cwd;
  515. }
  516.  
  517. bool wxFileName::SetCwd()
  518. {
  519.     return wxFileName::SetCwd( GetFullPath() );
  520. }
  521.  
  522. bool wxFileName::SetCwd( const wxString &cwd )
  523. {
  524.     return ::wxSetWorkingDirectory( cwd );
  525. }
  526.  
  527. void wxFileName::AssignHomeDir()
  528. {
  529.     AssignDir(wxFileName::GetHomeDir());
  530. }
  531.  
  532. wxString wxFileName::GetHomeDir()
  533. {
  534.     return ::wxGetHomeDir();
  535. }
  536.  
  537. void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp)
  538. {
  539.     wxString tempname = CreateTempFileName(prefix, fileTemp);
  540.     if ( tempname.empty() )
  541.     {
  542.         // error, failed to get temp file name
  543.         Clear();
  544.     }
  545.     else // ok
  546.     {
  547.         Assign(tempname);
  548.     }
  549. }
  550.  
  551. /* static */
  552. wxString
  553. wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp)
  554. {
  555.     wxString path, dir, name;
  556.  
  557.     // use the directory specified by the prefix
  558.     SplitPath(prefix, &dir, &name, NULL /* extension */);
  559.  
  560. #if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
  561.  
  562. #ifdef __WIN32__
  563.     if ( dir.empty() )
  564.     {
  565.         if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
  566.         {
  567.             wxLogLastError(_T("GetTempPath"));
  568.         }
  569.  
  570.         if ( dir.empty() )
  571.         {
  572.             // GetTempFileName() fails if we pass it an empty string
  573.             dir = _T('.');
  574.         }
  575.     }
  576.     else // we have a dir to create the file in
  577.     {
  578.         // ensure we use only the back slashes as GetTempFileName(), unlike all
  579.         // the other APIs, is picky and doesn't accept the forward ones
  580.         dir.Replace(_T("/"), _T("\\"));
  581.     }
  582.  
  583.     if ( !::GetTempFileName(dir, name, 0, wxStringBuffer(path, MAX_PATH + 1)) )
  584.     {
  585.         wxLogLastError(_T("GetTempFileName"));
  586.  
  587.         path.clear();
  588.     }
  589. #else // Win16
  590.     if ( !::GetTempFileName(NULL, prefix, 0, wxStringBuffer(path, 1025)) )
  591.     {
  592.         path.clear();
  593.     }
  594. #endif // Win32/16
  595.  
  596. #elif defined(__WXPM__)
  597.     // for now just create a file
  598.     //
  599.     // future enhancements can be to set some extended attributes for file
  600.     // systems OS/2 supports that have them (HPFS, FAT32) and security
  601.     // (HPFS386)
  602.     static const wxChar *szMktempSuffix = wxT("XXX");
  603.     path << dir << _T('/') << name << szMktempSuffix;
  604.  
  605.     // Temporarily remove - MN
  606.     #ifndef __WATCOMC__
  607.         ::DosCreateDir(wxStringBuffer(path, MAX_PATH), NULL);
  608.     #endif
  609.  
  610. #else // !Windows, !OS/2
  611.     if ( dir.empty() )
  612.     {
  613. #if defined(__WXMAC__) && !defined(__DARWIN__)
  614.         dir = wxMacFindFolder(  (short) kOnSystemDisk, kTemporaryFolderType, kCreateFolder ) ;
  615. #else // !Mac
  616.         dir = wxGetenv(_T("TMP"));
  617.         if ( dir.empty() )
  618.         {
  619.             dir = wxGetenv(_T("TEMP"));
  620.         }
  621.  
  622.         if ( dir.empty() )
  623.         {
  624.             // default
  625.             #ifdef __DOS__
  626.                 dir = _T(".");
  627.             #else
  628.                 dir = _T("/tmp");
  629.             #endif
  630.         }
  631. #endif // Mac/!Mac
  632.     }
  633.  
  634.     path = dir;
  635.  
  636.     if ( !wxEndsWithPathSeparator(dir) &&
  637.             (name.empty() || !wxIsPathSeparator(name[0u])) )
  638.     {
  639.         path += wxFILE_SEP_PATH;
  640.     }
  641.  
  642.     path += name;
  643.  
  644. #if defined(HAVE_MKSTEMP)
  645.     // scratch space for mkstemp()
  646.     path += _T("XXXXXX");
  647.  
  648.     // we need to copy the path to the buffer in which mkstemp() can modify it
  649.     wxCharBuffer buf = wxConvFile.cWX2MB( path );
  650.  
  651.     // cast is safe because the string length doesn't change
  652.     int fdTemp = mkstemp( (char*)(const char*) buf );
  653.     if ( fdTemp == -1 )
  654.     {
  655.         // this might be not necessary as mkstemp() on most systems should have
  656.         // already done it but it doesn't hurt neither...
  657.         path.clear();
  658.     }
  659.     else // mkstemp() succeeded
  660.     {
  661.         path = wxConvFile.cMB2WX( (const char*) buf );
  662.         
  663.         // avoid leaking the fd
  664.         if ( fileTemp )
  665.         {
  666.             fileTemp->Attach(fdTemp);
  667.         }
  668.         else
  669.         {
  670.             close(fdTemp);
  671.         }
  672.     }
  673. #else // !HAVE_MKSTEMP
  674.  
  675. #ifdef HAVE_MKTEMP
  676.     // same as above
  677.     path += _T("XXXXXX");
  678.  
  679.     wxCharBuffer buf = wxConvFile.cWX2MB( path );
  680.     if ( !mktemp( (const char*) buf ) )
  681.     {
  682.         path.clear();
  683.     }
  684.     else
  685.     {
  686.         path = wxConvFile.cMB2WX( (const char*) buf );
  687.     }
  688. #else // !HAVE_MKTEMP (includes __DOS__)
  689.     // generate the unique file name ourselves
  690.     #ifndef __DOS__
  691.     path << (unsigned int)getpid();
  692.     #endif
  693.  
  694.     wxString pathTry;
  695.  
  696.     static const size_t numTries = 1000;
  697.     for ( size_t n = 0; n < numTries; n++ )
  698.     {
  699.         // 3 hex digits is enough for numTries == 1000 < 4096
  700.         pathTry = path + wxString::Format(_T("%.03x"), n);
  701.         if ( !wxFile::Exists(pathTry) )
  702.         {
  703.             break;
  704.         }
  705.  
  706.         pathTry.clear();
  707.     }
  708.  
  709.     path = pathTry;
  710. #endif // HAVE_MKTEMP/!HAVE_MKTEMP
  711.  
  712.     if ( !path.empty() )
  713.     {
  714.     }
  715. #endif // HAVE_MKSTEMP/!HAVE_MKSTEMP
  716.  
  717. #endif // Windows/!Windows
  718.  
  719.     if ( path.empty() )
  720.     {
  721.         wxLogSysError(_("Failed to create a temporary file name"));
  722.     }
  723.     else if ( fileTemp && !fileTemp->IsOpened() )
  724.     {
  725.         // open the file - of course, there is a race condition here, this is
  726.         // why we always prefer using mkstemp()...
  727.         //
  728.         // NB: GetTempFileName() under Windows creates the file, so using
  729.         //     write_excl there would fail
  730.         if ( !fileTemp->Open(path,
  731. #if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
  732.                              wxFile::write,
  733. #else
  734.                              wxFile::write_excl,
  735. #endif
  736.                              wxS_IRUSR | wxS_IWUSR) )
  737.         {
  738.             // FIXME: If !ok here should we loop and try again with another
  739.             //        file name?  That is the standard recourse if open(O_EXCL)
  740.             //        fails, though of course it should be protected against
  741.             //        possible infinite looping too.
  742.  
  743.             wxLogError(_("Failed to open temporary file."));
  744.  
  745.             path.clear();
  746.         }
  747.     }
  748.  
  749.     return path;
  750. }
  751.  
  752. // ----------------------------------------------------------------------------
  753. // directory operations
  754. // ----------------------------------------------------------------------------
  755.  
  756. bool wxFileName::Mkdir( int perm, int flags )
  757. {
  758.     return wxFileName::Mkdir( GetFullPath(), perm, flags );
  759. }
  760.  
  761. bool wxFileName::Mkdir( const wxString& dir, int perm, int flags )
  762. {
  763.     if ( flags & wxPATH_MKDIR_FULL )
  764.     {
  765.         // split the path in components
  766.         wxFileName filename;
  767.         filename.AssignDir(dir);
  768.  
  769.         wxString currPath;
  770.         if ( filename.HasVolume())
  771.         {
  772.             currPath << wxGetVolumeString(filename.GetVolume(), wxPATH_NATIVE);
  773.         }
  774.  
  775.         wxArrayString dirs = filename.GetDirs();
  776.         size_t count = dirs.GetCount();
  777.         for ( size_t i = 0; i < count; i++ )
  778.         {
  779.             if ( i > 0 || filename.IsAbsolute() )
  780.                 currPath += wxFILE_SEP_PATH;
  781.             currPath += dirs[i];
  782.  
  783.             if (!DirExists(currPath))
  784.             {
  785.                 if (!wxMkdir(currPath, perm))
  786.                 {
  787.                     // no need to try creating further directories
  788.                     return FALSE;
  789.                 }
  790.             }
  791.         }
  792.  
  793.         return TRUE;
  794.  
  795.     }
  796.  
  797.     return ::wxMkdir( dir, perm );
  798. }
  799.  
  800. bool wxFileName::Rmdir()
  801. {
  802.     return wxFileName::Rmdir( GetFullPath() );
  803. }
  804.  
  805. bool wxFileName::Rmdir( const wxString &dir )
  806. {
  807.     return ::wxRmdir( dir );
  808. }
  809.  
  810. // ----------------------------------------------------------------------------
  811. // path normalization
  812. // ----------------------------------------------------------------------------
  813.  
  814. bool wxFileName::Normalize(int flags,
  815.                            const wxString& cwd,
  816.                            wxPathFormat format)
  817. {
  818.     // the existing path components
  819.     wxArrayString dirs = GetDirs();
  820.  
  821.     // the path to prepend in front to make the path absolute
  822.     wxFileName curDir;
  823.  
  824.     format = GetFormat(format);
  825.  
  826.     // make the path absolute
  827.     if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute(format) )
  828.     {
  829.         if ( cwd.empty() )
  830.         {
  831.             curDir.AssignCwd(GetVolume());
  832.         }
  833.         else // cwd provided
  834.         {
  835.             curDir.AssignDir(cwd);
  836.         }
  837.  
  838.         // the path may be not absolute because it doesn't have the volume name
  839.         // but in this case we shouldn't modify the directory components of it
  840.         // but just set the current volume
  841.         if ( !HasVolume() && curDir.HasVolume() )
  842.         {
  843.             SetVolume(curDir.GetVolume());
  844.  
  845.             if ( !m_relative )
  846.             {
  847.                 // yes, it was the case - we don't need curDir then
  848.                 curDir.Clear();
  849.             }
  850.         }
  851.     }
  852.  
  853.     // handle ~ stuff under Unix only
  854.     if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) )
  855.     {
  856.         if ( !dirs.IsEmpty() )
  857.         {
  858.             wxString dir = dirs[0u];
  859.             if ( !dir.empty() && dir[0u] == _T('~') )
  860.             {
  861.                 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
  862.  
  863.                 dirs.RemoveAt(0u);
  864.             }
  865.         }
  866.     }
  867.  
  868.     // transform relative path into abs one
  869.     if ( curDir.IsOk() )
  870.     {
  871.         wxArrayString dirsNew = curDir.GetDirs();
  872.         size_t count = dirs.GetCount();
  873.         for ( size_t n = 0; n < count; n++ )
  874.         {
  875.             dirsNew.Add(dirs[n]);
  876.         }
  877.  
  878.         dirs = dirsNew;
  879.     }
  880.  
  881.     // now deal with ".", ".." and the rest
  882.     m_dirs.Empty();
  883.     size_t count = dirs.GetCount();
  884.     for ( size_t n = 0; n < count; n++ )
  885.     {
  886.         wxString dir = dirs[n];
  887.  
  888.         if ( flags & wxPATH_NORM_DOTS )
  889.         {
  890.             if ( dir == wxT(".") )
  891.             {
  892.                 // just ignore
  893.                 continue;
  894.             }
  895.  
  896.             if ( dir == wxT("..") )
  897.             {
  898.                 if ( m_dirs.IsEmpty() )
  899.                 {
  900.                     wxLogError(_("The path '%s' contains too many \"..\"!"),
  901.                                GetFullPath().c_str());
  902.                     return FALSE;
  903.                 }
  904.  
  905.                 m_dirs.RemoveAt(m_dirs.GetCount() - 1);
  906.                 continue;
  907.             }
  908.         }
  909.  
  910.         if ( flags & wxPATH_NORM_ENV_VARS )
  911.         {
  912.             dir = wxExpandEnvVars(dir);
  913.         }
  914.  
  915.         if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
  916.         {
  917.             dir.MakeLower();
  918.         }
  919.  
  920.         m_dirs.Add(dir);
  921.     }
  922.  
  923.     if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
  924.     {
  925.         // VZ: expand env vars here too?
  926.  
  927.         m_name.MakeLower();
  928.         m_ext.MakeLower();
  929.     }
  930.  
  931.     // we do have the path now
  932.     //
  933.     // NB: need to do this before (maybe) calling Assign() below
  934.     m_relative = FALSE;
  935.  
  936. #if defined(__WIN32__)
  937.     if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
  938.     {
  939.         Assign(GetLongPath());
  940.     }
  941. #endif // Win32
  942.  
  943.     return TRUE;
  944. }
  945.  
  946. // ----------------------------------------------------------------------------
  947. // absolute/relative paths
  948. // ----------------------------------------------------------------------------
  949.  
  950. bool wxFileName::IsAbsolute(wxPathFormat format) const
  951. {
  952.     // if our path doesn't start with a path separator, it's not an absolute
  953.     // path
  954.     if ( m_relative )
  955.         return FALSE;
  956.  
  957.     if ( !GetVolumeSeparator(format).empty() )
  958.     {
  959.         // this format has volumes and an absolute path must have one, it's not
  960.         // enough to have the full path to bean absolute file under Windows
  961.         if ( GetVolume().empty() )
  962.             return FALSE;
  963.     }
  964.  
  965.     return TRUE;
  966. }
  967.  
  968. bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
  969. {
  970.     wxFileName fnBase(pathBase, format);
  971.  
  972.     // get cwd only once - small time saving
  973.     wxString cwd = wxGetCwd();
  974.     Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
  975.     fnBase.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
  976.  
  977.     bool withCase = IsCaseSensitive(format);
  978.  
  979.     // we can't do anything if the files live on different volumes
  980.     if ( !GetVolume().IsSameAs(fnBase.GetVolume(), withCase) )
  981.     {
  982.         // nothing done
  983.         return FALSE;
  984.     }
  985.  
  986.     // same drive, so we don't need our volume
  987.     m_volume.clear();
  988.  
  989.     // remove common directories starting at the top
  990.     while ( !m_dirs.IsEmpty() && !fnBase.m_dirs.IsEmpty() &&
  991.                 m_dirs[0u].IsSameAs(fnBase.m_dirs[0u], withCase) )
  992.     {
  993.         m_dirs.RemoveAt(0);
  994.         fnBase.m_dirs.RemoveAt(0);
  995.     }
  996.  
  997.     // add as many ".." as needed
  998.     size_t count = fnBase.m_dirs.GetCount();
  999.     for ( size_t i = 0; i < count; i++ )
  1000.     {
  1001.         m_dirs.Insert(wxT(".."), 0u);
  1002.     }
  1003.  
  1004.     if ( format == wxPATH_UNIX || format == wxPATH_DOS )
  1005.     {
  1006.         // a directory made relative with respect to itself is '.' under Unix
  1007.         // and DOS, by definition (but we don't have to insert "./" for the
  1008.         // files)
  1009.         if ( m_dirs.IsEmpty() && IsDir() )
  1010.         {
  1011.             m_dirs.Add(_T('.'));
  1012.         }
  1013.     }
  1014.  
  1015.     m_relative = TRUE;
  1016.  
  1017.     // we were modified
  1018.     return TRUE;
  1019. }
  1020.  
  1021. // ----------------------------------------------------------------------------
  1022. // filename kind tests
  1023. // ----------------------------------------------------------------------------
  1024.  
  1025. bool wxFileName::SameAs(const wxFileName &filepath, wxPathFormat format)
  1026. {
  1027.     wxFileName fn1 = *this,
  1028.                fn2 = filepath;
  1029.  
  1030.     // get cwd only once - small time saving
  1031.     wxString cwd = wxGetCwd();
  1032.     fn1.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
  1033.     fn2.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
  1034.  
  1035.     if ( fn1.GetFullPath() == fn2.GetFullPath() )
  1036.         return TRUE;
  1037.  
  1038.     // TODO: compare inodes for Unix, this works even when filenames are
  1039.     //       different but files are the same (symlinks) (VZ)
  1040.  
  1041.     return FALSE;
  1042. }
  1043.  
  1044. /* static */
  1045. bool wxFileName::IsCaseSensitive( wxPathFormat format )
  1046. {
  1047.     // only Unix filenames are truely case-sensitive
  1048.     return GetFormat(format) == wxPATH_UNIX;
  1049. }
  1050.  
  1051. /* static */
  1052. wxString wxFileName::GetVolumeSeparator(wxPathFormat format)
  1053. {
  1054.     wxString sepVol;
  1055.  
  1056.     if ( (GetFormat(format) == wxPATH_DOS) ||
  1057.          (GetFormat(format) == wxPATH_VMS) )
  1058.     {
  1059.         sepVol = wxFILE_SEP_DSK;
  1060.     }
  1061.     //else: leave empty
  1062.  
  1063.     return sepVol;
  1064. }
  1065.  
  1066. /* static */
  1067. wxString wxFileName::GetPathSeparators(wxPathFormat format)
  1068. {
  1069.     wxString seps;
  1070.     switch ( GetFormat(format) )
  1071.     {
  1072.         case wxPATH_DOS:
  1073.             // accept both as native APIs do but put the native one first as
  1074.             // this is the one we use in GetFullPath()
  1075.             seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX;
  1076.             break;
  1077.  
  1078.         default:
  1079.             wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
  1080.             // fall through
  1081.  
  1082.         case wxPATH_UNIX:
  1083.             seps = wxFILE_SEP_PATH_UNIX;
  1084.             break;
  1085.  
  1086.         case wxPATH_MAC:
  1087.             seps = wxFILE_SEP_PATH_MAC;
  1088.             break;
  1089.  
  1090.         case wxPATH_VMS:
  1091.             seps = wxFILE_SEP_PATH_VMS;
  1092.             break;
  1093.     }
  1094.  
  1095.     return seps;
  1096. }
  1097.  
  1098. /* static */
  1099. bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
  1100. {
  1101.     // wxString::Find() doesn't work as expected with NUL - it will always find
  1102.     // it, so it is almost surely a bug if this function is called with NUL arg
  1103.     wxASSERT_MSG( ch != _T('\0'), _T("shouldn't be called with NUL") );
  1104.  
  1105.     return GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
  1106. }
  1107.  
  1108. // ----------------------------------------------------------------------------
  1109. // path components manipulation
  1110. // ----------------------------------------------------------------------------
  1111.  
  1112. void wxFileName::AppendDir( const wxString &dir )
  1113. {
  1114.     m_dirs.Add( dir );
  1115. }
  1116.  
  1117. void wxFileName::PrependDir( const wxString &dir )
  1118. {
  1119.     m_dirs.Insert( dir, 0 );
  1120. }
  1121.  
  1122. void wxFileName::InsertDir( int before, const wxString &dir )
  1123. {
  1124.     m_dirs.Insert( dir, before );
  1125. }
  1126.  
  1127. void wxFileName::RemoveDir( int pos )
  1128. {
  1129.     m_dirs.Remove( (size_t)pos );
  1130. }
  1131.  
  1132. // ----------------------------------------------------------------------------
  1133. // accessors
  1134. // ----------------------------------------------------------------------------
  1135.  
  1136. void wxFileName::SetFullName(const wxString& fullname)
  1137. {
  1138.     SplitPath(fullname, NULL /* no path */, &m_name, &m_ext);
  1139. }
  1140.  
  1141. wxString wxFileName::GetFullName() const
  1142. {
  1143.     wxString fullname = m_name;
  1144.     if ( !m_ext.empty() )
  1145.     {
  1146.         fullname << wxFILE_SEP_EXT << m_ext;
  1147.     }
  1148.  
  1149.     return fullname;
  1150. }
  1151.  
  1152. wxString wxFileName::GetPath( int flags, wxPathFormat format ) const
  1153. {
  1154.     format = GetFormat( format );
  1155.  
  1156.     wxString fullpath;
  1157.  
  1158.     // return the volume with the path as well if requested
  1159.     if ( flags & wxPATH_GET_VOLUME )
  1160.     {
  1161.         fullpath += wxGetVolumeString(GetVolume(), format);
  1162.     }
  1163.  
  1164.     // the leading character
  1165.     switch ( format )
  1166.     {
  1167.         case wxPATH_MAC:
  1168.             if ( m_relative )
  1169.                 fullpath += wxFILE_SEP_PATH_MAC;
  1170.             break;
  1171.  
  1172.         case wxPATH_DOS:
  1173.             if (!m_relative)
  1174.                 fullpath += wxFILE_SEP_PATH_DOS;
  1175.             break;
  1176.  
  1177.         default:
  1178.             wxFAIL_MSG( _T("unknown path format") );
  1179.             // fall through
  1180.  
  1181.         case wxPATH_UNIX:
  1182.             if ( !m_relative )
  1183.             {
  1184.                 // normally the absolute file names starts with a slash with
  1185.                 // one exception: file names like "~/foo.bar" don't have it
  1186.                 if ( m_dirs.IsEmpty() || m_dirs[0u] != _T('~') )
  1187.                 {
  1188.                     fullpath += wxFILE_SEP_PATH_UNIX;
  1189.                 }
  1190.             }
  1191.             break;
  1192.  
  1193.         case wxPATH_VMS:
  1194.             // no leading character here but use this place to unset
  1195.             // wxPATH_GET_SEPARATOR flag: under VMS it doesn't make sense as,
  1196.             // if I understand correctly, there should never be a dot before
  1197.             // the closing bracket
  1198.             flags &= ~wxPATH_GET_SEPARATOR;
  1199.     }
  1200.  
  1201.     // then concatenate all the path components using the path separator
  1202.     size_t dirCount = m_dirs.GetCount();
  1203.     if ( dirCount )
  1204.     {
  1205.         if ( format == wxPATH_VMS )
  1206.         {
  1207.             fullpath += wxT('[');
  1208.         }
  1209.  
  1210.         for ( size_t i = 0; i < dirCount; i++ )
  1211.         {
  1212.             switch (format)
  1213.             {
  1214.                 case wxPATH_MAC:
  1215.                     if ( m_dirs[i] == wxT(".") )
  1216.                     {
  1217.                         // skip appending ':', this shouldn't be done in this
  1218.                         // case as "::" is interpreted as ".." under Unix
  1219.                         continue;
  1220.                     }
  1221.  
  1222.                     // convert back from ".." to nothing
  1223.                     if ( m_dirs[i] != wxT("..") )
  1224.                          fullpath += m_dirs[i];
  1225.                     break;
  1226.  
  1227.                 default:
  1228.                     wxFAIL_MSG( wxT("unexpected path format") );
  1229.                     // still fall through
  1230.  
  1231.                 case wxPATH_DOS:
  1232.                 case wxPATH_UNIX:
  1233.                     fullpath += m_dirs[i];
  1234.                     break;
  1235.  
  1236.                 case wxPATH_VMS:
  1237.                     // TODO: What to do with ".." under VMS
  1238.  
  1239.                     // convert back from ".." to nothing
  1240.                     if ( m_dirs[i] != wxT("..") )
  1241.                         fullpath += m_dirs[i];
  1242.                     break;
  1243.             }
  1244.  
  1245.             if ( (flags & wxPATH_GET_SEPARATOR) || (i != dirCount - 1) )
  1246.                 fullpath += GetPathSeparator(format);
  1247.         }
  1248.  
  1249.         if ( format == wxPATH_VMS )
  1250.         {
  1251.             fullpath += wxT(']');
  1252.         }
  1253.     }
  1254.  
  1255.     return fullpath;
  1256. }
  1257.  
  1258. wxString wxFileName::GetFullPath( wxPathFormat format ) const
  1259. {
  1260.     // we already have a function to get the path
  1261.     wxString fullpath = GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR,
  1262.                                 format);
  1263.  
  1264.     // now just add the file name and extension to it
  1265.     fullpath += GetFullName();
  1266.  
  1267.     return fullpath;
  1268. }
  1269.  
  1270. // Return the short form of the path (returns identity on non-Windows platforms)
  1271. wxString wxFileName::GetShortPath() const
  1272. {
  1273. #if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__)
  1274.     wxString path(GetFullPath());
  1275.     wxString pathOut;
  1276.     DWORD sz = ::GetShortPathName(path, NULL, 0);
  1277.     bool ok = sz != 0;
  1278.     if ( ok )
  1279.     {
  1280.         ok = ::GetShortPathName
  1281.                (
  1282.                 path,
  1283.                 pathOut.GetWriteBuf(sz),
  1284.                 sz
  1285.                ) != 0;
  1286.         pathOut.UngetWriteBuf();
  1287.     }
  1288.     if (ok)
  1289.         return pathOut;
  1290.  
  1291.     return path;
  1292. #else
  1293.     return GetFullPath();
  1294. #endif
  1295. }
  1296.  
  1297. // Return the long form of the path (returns identity on non-Windows platforms)
  1298. wxString wxFileName::GetLongPath() const
  1299. {
  1300.     wxString pathOut,
  1301.              path = GetFullPath();
  1302.  
  1303. #if defined(__WIN32__) && !defined(__WXMICROWIN__)
  1304.     bool success = FALSE;
  1305.  
  1306. #if wxUSE_DYNAMIC_LOADER
  1307.     typedef DWORD (WINAPI *GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
  1308.  
  1309.     static bool s_triedToLoad = FALSE;
  1310.  
  1311.     if ( !s_triedToLoad )
  1312.     {
  1313.         // suppress the errors about missing GetLongPathName[AW]
  1314.         wxLogNull noLog;
  1315.  
  1316.         s_triedToLoad = TRUE;
  1317.         wxDynamicLibrary dllKernel(_T("kernel32"));
  1318.         if ( dllKernel.IsLoaded() )
  1319.         {
  1320.             // may succeed or fail depending on the Windows version
  1321.             static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
  1322. #ifdef _UNICODE
  1323.             s_pfnGetLongPathName = (GET_LONG_PATH_NAME) dllKernel.GetSymbol(_T("GetLongPathNameW"));
  1324. #else
  1325.             s_pfnGetLongPathName = (GET_LONG_PATH_NAME) dllKernel.GetSymbol(_T("GetLongPathNameA"));
  1326. #endif
  1327.  
  1328.             if ( s_pfnGetLongPathName )
  1329.             {
  1330.                 DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0);
  1331.                 bool ok = dwSize > 0;
  1332.  
  1333.                 if ( ok )
  1334.                 {
  1335.                     DWORD sz = (*s_pfnGetLongPathName)(path, NULL, 0);
  1336.                     ok = sz != 0;
  1337.                     if ( ok )
  1338.                     {
  1339.                         ok = (*s_pfnGetLongPathName)
  1340.                                 (
  1341.                                 path,
  1342.                                 pathOut.GetWriteBuf(sz),
  1343.                                 sz
  1344.                                 ) != 0;
  1345.                         pathOut.UngetWriteBuf();
  1346.  
  1347.                         success = TRUE;
  1348.                     }
  1349.                 }
  1350.             }
  1351.         }
  1352.     }
  1353.  
  1354.     if (success)
  1355.         return pathOut;
  1356. #endif // wxUSE_DYNAMIC_LOADER
  1357.  
  1358.     if (!success)
  1359.     {
  1360.         // The OS didn't support GetLongPathName, or some other error.
  1361.         // We need to call FindFirstFile on each component in turn.
  1362.  
  1363.         WIN32_FIND_DATA findFileData;
  1364.         HANDLE hFind;
  1365.  
  1366.         if ( HasVolume() )
  1367.             pathOut = GetVolume() +
  1368.                       GetVolumeSeparator(wxPATH_DOS) +
  1369.                       GetPathSeparator(wxPATH_DOS);
  1370.         else
  1371.             pathOut = wxEmptyString;
  1372.  
  1373.         wxArrayString dirs = GetDirs();
  1374.         dirs.Add(GetFullName());
  1375.  
  1376.         wxString tmpPath;
  1377.  
  1378.         size_t count = dirs.GetCount();
  1379.         for ( size_t i = 0; i < count; i++ )
  1380.         {
  1381.             // We're using pathOut to collect the long-name path, but using a
  1382.             // temporary for appending the last path component which may be
  1383.             // short-name
  1384.             tmpPath = pathOut + dirs[i];
  1385.  
  1386.             if ( tmpPath.empty() )
  1387.                 continue;
  1388.  
  1389.             // can't see this being necessary? MF
  1390.             if ( tmpPath.Last() == GetVolumeSeparator(wxPATH_DOS) )
  1391.             {
  1392.                 // Can't pass a drive and root dir to FindFirstFile,
  1393.                 // so continue to next dir
  1394.                 tmpPath += wxFILE_SEP_PATH;
  1395.                 pathOut = tmpPath;
  1396.                 continue;
  1397.             }
  1398.  
  1399.             hFind = ::FindFirstFile(tmpPath, &findFileData);
  1400.             if (hFind == INVALID_HANDLE_VALUE)
  1401.             {
  1402.                 // Error: most likely reason is that path doesn't exist, so
  1403.                 // append any unprocessed parts and return
  1404.                 for ( i += 1; i < count; i++ )
  1405.                     tmpPath += wxFILE_SEP_PATH + dirs[i];
  1406.  
  1407.                 return tmpPath;
  1408.             }
  1409.  
  1410.             pathOut += findFileData.cFileName;
  1411.             if ( (i < (count-1)) )
  1412.                 pathOut += wxFILE_SEP_PATH;
  1413.  
  1414.             ::FindClose(hFind);
  1415.         }
  1416.     }
  1417. #else // !Win32
  1418.     pathOut = path;
  1419. #endif // Win32/!Win32
  1420.  
  1421.     return pathOut;
  1422. }
  1423.  
  1424. wxPathFormat wxFileName::GetFormat( wxPathFormat format )
  1425. {
  1426.     if (format == wxPATH_NATIVE)
  1427.     {
  1428. #if defined(__WXMSW__) || defined(__WXPM__) || defined(__DOS__)
  1429.         format = wxPATH_DOS;
  1430. #elif defined(__WXMAC__) && !defined(__DARWIN__)
  1431.         format = wxPATH_MAC;
  1432. #elif defined(__VMS)
  1433.         format = wxPATH_VMS;
  1434. #else
  1435.         format = wxPATH_UNIX;
  1436. #endif
  1437.     }
  1438.     return format;
  1439. }
  1440.  
  1441. // ----------------------------------------------------------------------------
  1442. // path splitting function
  1443. // ----------------------------------------------------------------------------
  1444.  
  1445. /* static */
  1446. void wxFileName::SplitPath(const wxString& fullpathWithVolume,
  1447.                            wxString *pstrVolume,
  1448.                            wxString *pstrPath,
  1449.                            wxString *pstrName,
  1450.                            wxString *pstrExt,
  1451.                            wxPathFormat format)
  1452. {
  1453.     format = GetFormat(format);
  1454.  
  1455.     wxString fullpath = fullpathWithVolume;
  1456.  
  1457.     // under VMS the end of the path is ']', not the path separator used to
  1458.     // separate the components
  1459.     wxString sepPath = format == wxPATH_VMS ? wxString(_T(']'))
  1460.                                             : GetPathSeparators(format);
  1461.  
  1462.     // special Windows UNC paths hack: transform \\share\path into share:path
  1463.     if ( format == wxPATH_DOS )
  1464.     {
  1465.         if ( fullpath.length() >= 4 &&
  1466.                 fullpath[0u] == wxFILE_SEP_PATH_DOS &&
  1467.                     fullpath[1u] == wxFILE_SEP_PATH_DOS )
  1468.         {
  1469.             fullpath.erase(0, 2);
  1470.  
  1471.             size_t posFirstSlash = fullpath.find_first_of(sepPath);
  1472.             if ( posFirstSlash != wxString::npos )
  1473.             {
  1474.                 fullpath[posFirstSlash] = wxFILE_SEP_DSK;
  1475.  
  1476.                 // UNC paths are always absolute, right? (FIXME)
  1477.                 fullpath.insert(posFirstSlash + 1, wxFILE_SEP_PATH_DOS);
  1478.             }
  1479.         }
  1480.     }
  1481.  
  1482.     // We separate the volume here
  1483.     if ( format == wxPATH_DOS || format == wxPATH_VMS )
  1484.     {
  1485.         wxString sepVol = GetVolumeSeparator(format);
  1486.  
  1487.         size_t posFirstColon = fullpath.find_first_of(sepVol);
  1488.         if ( posFirstColon != wxString::npos )
  1489.         {
  1490.             if ( pstrVolume )
  1491.             {
  1492.                 *pstrVolume = fullpath.Left(posFirstColon);
  1493.             }
  1494.  
  1495.             // remove the volume name and the separator from the full path
  1496.             fullpath.erase(0, posFirstColon + sepVol.length());
  1497.         }
  1498.     }
  1499.  
  1500.     // find the positions of the last dot and last path separator in the path
  1501.     size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
  1502.     size_t posLastSlash = fullpath.find_last_of(sepPath);
  1503.  
  1504.     if ( (posLastDot != wxString::npos) &&
  1505.             ((format == wxPATH_UNIX) || (format == wxPATH_VMS)) )
  1506.     {
  1507.         if ( (posLastDot == 0) ||
  1508.              (fullpath[posLastDot - 1] == sepPath[0u] ) )
  1509.         {
  1510.             // under Unix and VMS, dot may be (and commonly is) the first
  1511.             // character of the filename, don't treat the entire filename as
  1512.             // extension in this case
  1513.             posLastDot = wxString::npos;
  1514.         }
  1515.     }
  1516.  
  1517.     // if we do have a dot and a slash, check that the dot is in the name part
  1518.     if ( (posLastDot != wxString::npos) &&
  1519.          (posLastSlash != wxString::npos) &&
  1520.          (posLastDot < posLastSlash) )
  1521.     {
  1522.         // the dot is part of the path, not the start of the extension
  1523.         posLastDot = wxString::npos;
  1524.     }
  1525.  
  1526.     // now fill in the variables provided by user
  1527.     if ( pstrPath )
  1528.     {
  1529.         if ( posLastSlash == wxString::npos )
  1530.         {
  1531.             // no path at all
  1532.             pstrPath->Empty();
  1533.         }
  1534.         else
  1535.         {
  1536.             // take everything up to the path separator but take care to make
  1537.             // the path equal to something like '/', not empty, for the files
  1538.             // immediately under root directory
  1539.             size_t len = posLastSlash;
  1540.  
  1541.             // this rule does not apply to mac since we do not start with colons (sep)
  1542.             // except for relative paths
  1543.             if ( !len && format != wxPATH_MAC)
  1544.                 len++;
  1545.  
  1546.             *pstrPath = fullpath.Left(len);
  1547.  
  1548.             // special VMS hack: remove the initial bracket
  1549.             if ( format == wxPATH_VMS )
  1550.             {
  1551.                 if ( (*pstrPath)[0u] == _T('[') )
  1552.                     pstrPath->erase(0, 1);
  1553.             }
  1554.         }
  1555.     }
  1556.  
  1557.     if ( pstrName )
  1558.     {
  1559.         // take all characters starting from the one after the last slash and
  1560.         // up to, but excluding, the last dot
  1561.         size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
  1562.         size_t count;
  1563.         if ( posLastDot == wxString::npos )
  1564.         {
  1565.             // take all until the end
  1566.             count = wxString::npos;
  1567.         }
  1568.         else if ( posLastSlash == wxString::npos )
  1569.         {
  1570.             count = posLastDot;
  1571.         }
  1572.         else // have both dot and slash
  1573.         {
  1574.             count = posLastDot - posLastSlash - 1;
  1575.         }
  1576.  
  1577.         *pstrName = fullpath.Mid(nStart, count);
  1578.     }
  1579.  
  1580.     if ( pstrExt )
  1581.     {
  1582.         if ( posLastDot == wxString::npos )
  1583.         {
  1584.             // no extension
  1585.             pstrExt->Empty();
  1586.         }
  1587.         else
  1588.         {
  1589.             // take everything after the dot
  1590.             *pstrExt = fullpath.Mid(posLastDot + 1);
  1591.         }
  1592.     }
  1593. }
  1594.  
  1595. /* static */
  1596. void wxFileName::SplitPath(const wxString& fullpath,
  1597.                            wxString *path,
  1598.                            wxString *name,
  1599.                            wxString *ext,
  1600.                            wxPathFormat format)
  1601. {
  1602.     wxString volume;
  1603.     SplitPath(fullpath, &volume, path, name, ext, format);
  1604.  
  1605.     if ( path )
  1606.     {
  1607.         path->Prepend(wxGetVolumeString(volume, format));
  1608.     }
  1609. }
  1610.  
  1611. // ----------------------------------------------------------------------------
  1612. // time functions
  1613. // ----------------------------------------------------------------------------
  1614.  
  1615. bool wxFileName::SetTimes(const wxDateTime *dtAccess,
  1616.                           const wxDateTime *dtMod,
  1617.                           const wxDateTime *dtCreate)
  1618. {
  1619. #if defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__))
  1620.     if ( !dtAccess && !dtMod )
  1621.     {
  1622.         // can't modify the creation time anyhow, don't try
  1623.         return TRUE;
  1624.     }
  1625.  
  1626.     // if dtAccess or dtMod is not specified, use the other one (which must be
  1627.     // non NULL because of the test above) for both times
  1628.     utimbuf utm;
  1629.     utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
  1630.     utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
  1631.     if ( utime(GetFullPath().fn_str(), &utm) == 0 )
  1632.     {
  1633.         return TRUE;
  1634.     }
  1635. #elif defined(__WIN32__)
  1636.     wxFileHandle fh(GetFullPath(), wxFileHandle::Write);
  1637.     if ( fh.IsOk() )
  1638.     {
  1639.         FILETIME ftAccess, ftCreate, ftWrite;
  1640.  
  1641.         if ( dtCreate )
  1642.             ConvertWxToFileTime(&ftCreate, *dtCreate);
  1643.         if ( dtAccess )
  1644.             ConvertWxToFileTime(&ftAccess, *dtAccess);
  1645.         if ( dtMod )
  1646.             ConvertWxToFileTime(&ftWrite, *dtMod);
  1647.  
  1648.         if ( ::SetFileTime(fh,
  1649.                            dtCreate ? &ftCreate : NULL,
  1650.                            dtAccess ? &ftAccess : NULL,
  1651.                            dtMod ? &ftWrite : NULL) )
  1652.         {
  1653.             return TRUE;
  1654.         }
  1655.     }
  1656. #else // other platform
  1657. #endif // platforms
  1658.  
  1659.     wxLogSysError(_("Failed to modify file times for '%s'"),
  1660.                   GetFullPath().c_str());
  1661.  
  1662.     return FALSE;
  1663. }
  1664.  
  1665. bool wxFileName::Touch()
  1666. {
  1667. #if defined(__UNIX_LIKE__)
  1668.     // under Unix touching file is simple: just pass NULL to utime()
  1669.     if ( utime(GetFullPath().fn_str(), NULL) == 0 )
  1670.     {
  1671.         return TRUE;
  1672.     }
  1673.  
  1674.     wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str());
  1675.  
  1676.     return FALSE;
  1677. #else // other platform
  1678.     wxDateTime dtNow = wxDateTime::Now();
  1679.  
  1680.     return SetTimes(&dtNow, &dtNow, NULL /* don't change create time */);
  1681. #endif // platforms
  1682. }
  1683.  
  1684. bool wxFileName::GetTimes(wxDateTime *dtAccess,
  1685.                           wxDateTime *dtMod,
  1686.                           wxDateTime *dtCreate) const
  1687. {
  1688. #if defined(__UNIX_LIKE__) || defined(__WXMAC__) || (defined(__DOS__) && defined(__WATCOMC__))
  1689.     wxStructStat stBuf;
  1690.     if ( wxStat( GetFullPath().c_str(), &stBuf) == 0 )
  1691.     {
  1692.         if ( dtAccess )
  1693.             dtAccess->Set(stBuf.st_atime);
  1694.         if ( dtMod )
  1695.             dtMod->Set(stBuf.st_mtime);
  1696.         if ( dtCreate )
  1697.             dtCreate->Set(stBuf.st_ctime);
  1698.  
  1699.         return TRUE;
  1700.     }
  1701. #elif defined(__WIN32__)
  1702.     wxFileHandle fh(GetFullPath(), wxFileHandle::Read);
  1703.     if ( fh.IsOk() )
  1704.     {
  1705.         FILETIME ftAccess, ftCreate, ftWrite;
  1706.  
  1707.         if ( ::GetFileTime(fh,
  1708.                            dtCreate ? &ftCreate : NULL,
  1709.                            dtAccess ? &ftAccess : NULL,
  1710.                            dtMod ? &ftWrite : NULL) )
  1711.         {
  1712.             if ( dtCreate )
  1713.                 ConvertFileTimeToWx(dtCreate, ftCreate);
  1714.             if ( dtAccess )
  1715.                 ConvertFileTimeToWx(dtAccess, ftAccess);
  1716.             if ( dtMod )
  1717.                 ConvertFileTimeToWx(dtMod, ftWrite);
  1718.  
  1719.             return TRUE;
  1720.         }
  1721.     }
  1722. #else // other platform
  1723. #endif // platforms
  1724.  
  1725.     wxLogSysError(_("Failed to retrieve file times for '%s'"),
  1726.                   GetFullPath().c_str());
  1727.  
  1728.     return FALSE;
  1729. }
  1730.  
  1731. #ifdef __WXMAC__
  1732.  
  1733. const short kMacExtensionMaxLength = 16 ;
  1734. class MacDefaultExtensionRecord
  1735. {
  1736. public :
  1737.   MacDefaultExtensionRecord()
  1738.   {
  1739.     m_ext[0] = 0 ;
  1740.     m_type = m_creator = NULL ;
  1741.   }
  1742.   MacDefaultExtensionRecord( const MacDefaultExtensionRecord& from )
  1743.   {
  1744.     strcpy( m_ext , from.m_ext ) ;
  1745.     m_type = from.m_type ;
  1746.     m_creator = from.m_creator ;
  1747.   }
  1748.   MacDefaultExtensionRecord( const char * extension , OSType type , OSType creator )
  1749.   {
  1750.     strncpy( m_ext , extension , kMacExtensionMaxLength ) ;
  1751.     m_ext[kMacExtensionMaxLength] = 0 ;
  1752.     m_type = type ;
  1753.     m_creator = creator ;
  1754.   }
  1755.   char m_ext[kMacExtensionMaxLength] ;
  1756.   OSType m_type ;
  1757.   OSType m_creator ;
  1758. }  ;
  1759.  
  1760. #include "wx/dynarray.h"
  1761. WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord, MacDefaultExtensionArray) ;
  1762.  
  1763. bool gMacDefaultExtensionsInited = false ;
  1764.  
  1765. #include "wx/arrimpl.cpp"
  1766.  
  1767. WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray) ;
  1768.  
  1769. MacDefaultExtensionArray gMacDefaultExtensions ;
  1770.  
  1771. static void MacEnsureDefaultExtensionsLoaded()
  1772. {
  1773.   if ( !gMacDefaultExtensionsInited )
  1774.   {
  1775.  
  1776.     // load the default extensions
  1777.     MacDefaultExtensionRecord defaults[1] =
  1778.     {
  1779.       MacDefaultExtensionRecord( "txt" , 'TEXT' , 'ttxt' ) ,
  1780.  
  1781.     } ;
  1782.     // we could load the pc exchange prefs here too
  1783.  
  1784.     for ( size_t i = 0 ; i < WXSIZEOF( defaults ) ; ++i )
  1785.     {
  1786.       gMacDefaultExtensions.Add( defaults[i] ) ;
  1787.     }
  1788.     gMacDefaultExtensionsInited = true ;
  1789.   }
  1790. }
  1791. bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
  1792. {
  1793.   FInfo fndrInfo ;
  1794.   FSSpec spec ;
  1795.   wxMacFilename2FSSpec(GetFullPath(),&spec) ;
  1796.   OSErr err = FSpGetFInfo( &spec , &fndrInfo ) ;
  1797.   wxCHECK( err == noErr , false ) ;
  1798.  
  1799.   fndrInfo.fdType = type ;
  1800.   fndrInfo.fdCreator = creator ;
  1801.   FSpSetFInfo( &spec , &fndrInfo ) ;
  1802.   return true ;
  1803. }
  1804.  
  1805. bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator )
  1806. {
  1807.   FInfo fndrInfo ;
  1808.   FSSpec spec ;
  1809.   wxMacFilename2FSSpec(GetFullPath(),&spec) ;
  1810.   OSErr err = FSpGetFInfo( &spec , &fndrInfo ) ;
  1811.   wxCHECK( err == noErr , false ) ;
  1812.  
  1813.   *type = fndrInfo.fdType ;
  1814.   *creator = fndrInfo.fdCreator ;
  1815.   return true ;
  1816. }
  1817.  
  1818. bool wxFileName::MacSetDefaultTypeAndCreator()
  1819. {
  1820.     wxUint32 type , creator ;
  1821.     if ( wxFileName::MacFindDefaultTypeAndCreator(GetExt() , &type ,
  1822.       &creator ) )
  1823.     {
  1824.         return MacSetTypeAndCreator( type , creator ) ;
  1825.     }
  1826.     return false;
  1827. }
  1828.  
  1829. bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
  1830. {
  1831.   MacEnsureDefaultExtensionsLoaded() ;
  1832.   wxString extl = ext.Lower() ;
  1833.   for( int i = gMacDefaultExtensions.Count() - 1 ; i >= 0 ; --i )
  1834.   {
  1835.     if ( gMacDefaultExtensions.Item(i).m_ext == extl )
  1836.     {
  1837.       *type = gMacDefaultExtensions.Item(i).m_type ;
  1838.       *creator = gMacDefaultExtensions.Item(i).m_creator ;
  1839.       return true ;
  1840.     }
  1841.   }
  1842.   return false ;
  1843. }
  1844.  
  1845. void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )
  1846. {
  1847.   MacEnsureDefaultExtensionsLoaded() ;
  1848.   MacDefaultExtensionRecord rec ;
  1849.   rec.m_type = type ;
  1850.   rec.m_creator = creator ;
  1851.   strncpy( rec.m_ext , ext.Lower().c_str() , kMacExtensionMaxLength ) ;
  1852.   gMacDefaultExtensions.Add( rec ) ;
  1853. }
  1854. #endif
  1855.