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 // Copyright:   (c) 2000 Robert Roebling
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 /*
12    Here are brief descriptions of the filename formats supported by this class:
13 
14    wxPATH_UNIX: standard Unix format, used under Darwin as well, absolute file
15                 names have the form:
16                 /dir1/dir2/.../dirN/filename, "." and ".." stand for the
17                 current and parent directory respectively, "~" is parsed as the
18                 user HOME and "~username" as the HOME of that user
19 
20    wxPATH_DOS:  DOS/Windows format, absolute file names have the form:
21                 drive:\dir1\dir2\...\dirN\filename.ext where drive is a single
22                 letter. "." and ".." as for Unix but no "~".
23 
24                 There are also UNC names of the form \\share\fullpath and
25                 MSW unique volume names of the form \\?\Volume{GUID}\fullpath.
26 
27                 The latter provide a uniform way to access a volume regardless of
28                 its current mount point, i.e. you can change a volume's mount
29                 point from D: to E:, or even remove it, and still be able to
30                 access it through its unique volume name. More on the subject can
31                 be found in MSDN's article "Naming a Volume" that is currently at
32                 http://msdn.microsoft.com/en-us/library/aa365248(VS.85).aspx.
33 
34 
35    wxPATH_MAC:  Mac OS 8/9 only, not used any longer, absolute file
36                 names have the form
37                     volume:dir1:...:dirN:filename
38                 and the relative file names are either
39                     :dir1:...:dirN:filename
40                 or just
41                     filename
42                 (although :filename works as well).
43                 Since the volume is just part of the file path, it is not
44                 treated like a separate entity as it is done under DOS and
45                 VMS, it is just treated as another dir.
46 
47    wxPATH_VMS:  VMS native format, absolute file names have the form
48                     <device>:[dir1.dir2.dir3]file.txt
49                 or
50                     <device>:[000000.dir1.dir2.dir3]file.txt
51 
52                 the <device> is the physical device (i.e. disk). 000000 is the
53                 root directory on the device which can be omitted.
54 
55                 Note that VMS uses different separators unlike Unix:
56                  : always after the device. If the path does not contain : than
57                    the default (the device of the current directory) is assumed.
58                  [ start of directory specification
59                  . separator between directory and subdirectory
60                  ] between directory and file
61  */
62 
63 // ============================================================================
64 // declarations
65 // ============================================================================
66 
67 // ----------------------------------------------------------------------------
68 // headers
69 // ----------------------------------------------------------------------------
70 
71 // For compilers that support precompilation, includes "wx.h".
72 #include "wx/wxprec.h"
73 
74 #ifdef __BORLANDC__
75 #pragma hdrstop
76 #endif
77 
78 #ifndef WX_PRECOMP
79     #ifdef __WINDOWS__
80         #include "wx/msw/wrapwin.h" // For GetShort/LongPathName
81     #endif
82     #include "wx/dynarray.h"
83     #include "wx/intl.h"
84     #include "wx/log.h"
85     #include "wx/utils.h"
86     #include "wx/crt.h"
87 #endif
88 
89 #include "wx/filename.h"
90 #include "wx/private/filename.h"
91 #include "wx/tokenzr.h"
92 #include "wx/config.h"          // for wxExpandEnvVars
93 #include "wx/dynlib.h"
94 #include "wx/dir.h"
95 #include "wx/longlong.h"
96 
97 #if defined(__WIN32__) && defined(__MINGW32__)
98     #include "wx/msw/gccpriv.h"
99 #endif
100 
101 #ifdef __WINDOWS__
102     #include "wx/msw/private.h"
103     #include "wx/msw/wrapshl.h"         // for CLSID_ShellLink
104     #include "wx/msw/missing.h"
105     #include "wx/msw/ole/oleutils.h"
106 #endif
107 
108 #if defined(__WXMAC__)
109   #include  "wx/osx/private.h"  // includes mac headers
110 #endif
111 
112 // utime() is POSIX so should normally be available on all Unices
113 #ifdef __UNIX_LIKE__
114 #include <sys/types.h>
115 #include <utime.h>
116 #include <sys/stat.h>
117 #include <unistd.h>
118 #endif
119 
120 #ifdef __DJGPP__
121 #include <unistd.h>
122 #endif
123 
124 #ifdef __WATCOMC__
125 #include <io.h>
126 #include <sys/utime.h>
127 #include <sys/stat.h>
128 #endif
129 
130 #ifdef __VISAGECPP__
131 #ifndef MAX_PATH
132 #define MAX_PATH 256
133 #endif
134 #endif
135 
136 #ifdef __EMX__
137 #include <os2.h>
138 #define MAX_PATH _MAX_PATH
139 #endif
140 
141 #ifndef S_ISREG
142     #define S_ISREG(mode) ((mode) & S_IFREG)
143 #endif
144 #ifndef S_ISDIR
145     #define S_ISDIR(mode) ((mode) & S_IFDIR)
146 #endif
147 
148 #if wxUSE_LONGLONG
149 extern const wxULongLong wxInvalidSize = (unsigned)-1;
150 #endif // wxUSE_LONGLONG
151 
152 namespace
153 {
154 
155 // ----------------------------------------------------------------------------
156 // private classes
157 // ----------------------------------------------------------------------------
158 
159 // small helper class which opens and closes the file - we use it just to get
160 // a file handle for the given file name to pass it to some Win32 API function
161 #if defined(__WIN32__) && !defined(__WXMICROWIN__)
162 
163 class wxFileHandle
164 {
165 public:
166     enum OpenMode
167     {
168         ReadAttr,
169         WriteAttr
170     };
171 
wxFileHandle(const wxString & filename,OpenMode mode,int flags=0)172     wxFileHandle(const wxString& filename, OpenMode mode, int flags = 0)
173     {
174         // be careful and use FILE_{READ,WRITE}_ATTRIBUTES here instead of the
175         // usual GENERIC_{READ,WRITE} as we don't want the file access time to
176         // be changed when we open it because this class is used for setting
177         // access time (see #10567)
178         m_hFile = ::CreateFile
179                     (
180                      filename.t_str(),             // name
181                      mode == ReadAttr ? FILE_READ_ATTRIBUTES    // access mask
182                                       : FILE_WRITE_ATTRIBUTES,
183                      FILE_SHARE_READ |              // sharing mode
184                      FILE_SHARE_WRITE,              // (allow everything)
185                      NULL,                          // no secutity attr
186                      OPEN_EXISTING,                 // creation disposition
187                      flags,                         // flags
188                      NULL                           // no template file
189                     );
190 
191         if ( m_hFile == INVALID_HANDLE_VALUE )
192         {
193             if ( mode == ReadAttr )
194             {
195                 wxLogSysError(_("Failed to open '%s' for reading"),
196                               filename.c_str());
197             }
198             else
199             {
200                 wxLogSysError(_("Failed to open '%s' for writing"),
201                               filename.c_str());
202             }
203         }
204     }
205 
~wxFileHandle()206     ~wxFileHandle()
207     {
208         if ( m_hFile != INVALID_HANDLE_VALUE )
209         {
210             if ( !::CloseHandle(m_hFile) )
211             {
212                 wxLogSysError(_("Failed to close file handle"));
213             }
214         }
215     }
216 
217     // return true only if the file could be opened successfully
IsOk() const218     bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; }
219 
220     // get the handle
operator HANDLE() const221     operator HANDLE() const { return m_hFile; }
222 
223 private:
224     HANDLE m_hFile;
225 };
226 
227 #endif // __WIN32__
228 
229 // ----------------------------------------------------------------------------
230 // private functions
231 // ----------------------------------------------------------------------------
232 
233 #if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__)
234 
235 // Convert between wxDateTime and FILETIME which is a 64-bit value representing
236 // the number of 100-nanosecond intervals since January 1, 1601 UTC.
237 //
238 // This is the offset between FILETIME epoch and the Unix/wxDateTime Epoch.
239 static wxInt64 EPOCH_OFFSET_IN_MSEC = wxLL(11644473600000);
240 
ConvertFileTimeToWx(wxDateTime * dt,const FILETIME & ft)241 static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft)
242 {
243     wxLongLong t(ft.dwHighDateTime, ft.dwLowDateTime);
244     t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
245     t -= EPOCH_OFFSET_IN_MSEC;
246 
247     *dt = wxDateTime(t);
248 }
249 
ConvertWxToFileTime(FILETIME * ft,const wxDateTime & dt)250 static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt)
251 {
252     // Undo the conversions above.
253     wxLongLong t(dt.GetValue());
254     t += EPOCH_OFFSET_IN_MSEC;
255     t *= 10000;
256 
257     ft->dwHighDateTime = t.GetHi();
258     ft->dwLowDateTime = t.GetLo();
259 }
260 
261 #endif // wxUSE_DATETIME && __WIN32__
262 
263 // return a string with the volume par
wxGetVolumeString(const wxString & volume,wxPathFormat format)264 static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format)
265 {
266     wxString path;
267 
268     if ( !volume.empty() )
269     {
270         format = wxFileName::GetFormat(format);
271 
272         // Special Windows UNC paths hack, part 2: undo what we did in
273         // SplitPath() and make an UNC path if we have a drive which is not a
274         // single letter (hopefully the network shares can't be one letter only
275         // although I didn't find any authoritative docs on this)
276         if ( format == wxPATH_DOS && volume.length() > 1 )
277         {
278             // We also have to check for Windows unique volume names here and
279             // return it with '\\?\' prepended to it
280             if ( wxFileName::IsMSWUniqueVolumeNamePath("\\\\?\\" + volume + "\\",
281                                                        format) )
282             {
283                 path << "\\\\?\\" << volume;
284             }
285             else
286             {
287                 // it must be a UNC path
288                 path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
289             }
290         }
291         else if  ( format == wxPATH_DOS || format == wxPATH_VMS )
292         {
293             path << volume << wxFileName::GetVolumeSeparator(format);
294         }
295         // else ignore
296     }
297 
298     return path;
299 }
300 
301 // return true if the character is a DOS path separator i.e. either a slash or
302 // a backslash
IsDOSPathSep(wxUniChar ch)303 inline bool IsDOSPathSep(wxUniChar ch)
304 {
305     return ch == wxFILE_SEP_PATH_DOS || ch == wxFILE_SEP_PATH_UNIX;
306 }
307 
308 // return true if the format used is the DOS/Windows one and the string looks
309 // like a UNC path
IsUNCPath(const wxString & path,wxPathFormat format)310 static bool IsUNCPath(const wxString& path, wxPathFormat format)
311 {
312     return format == wxPATH_DOS &&
313                 path.length() >= 4 && // "\\a" can't be a UNC path
314                     IsDOSPathSep(path[0u]) &&
315                         IsDOSPathSep(path[1u]) &&
316                             !IsDOSPathSep(path[2u]);
317 }
318 
319 // Under Unix-ish systems (basically everything except Windows but we can't
320 // just test for non-__WIN32__ because Cygwin defines it, yet we want to use
321 // lstat() under it, so test for all the rest explicitly) we may work either
322 // with the file itself or its target if it's a symbolic link and we should
323 // dereference it, as determined by wxFileName::ShouldFollowLink() and the
324 // absence of the wxFILE_EXISTS_NO_FOLLOW flag. StatAny() can be used to stat
325 // the appropriate file with an extra twist that it also works when there is no
326 // wxFileName object at all, as is the case in static methods.
327 
328 #if defined(__UNIX_LIKE__) || defined(__WXMAC__) || defined(__OS2__) || (defined(__DOS__) && defined(__WATCOMC__))
329     #define wxHAVE_LSTAT
330 #endif
331 
332 #ifdef wxHAVE_LSTAT
333 
334 // Private implementation, don't call directly, use one of the overloads below.
DoStatAny(wxStructStat & st,wxString path,bool dereference)335 bool DoStatAny(wxStructStat& st, wxString path, bool dereference)
336 {
337     // We need to remove any trailing slashes from the path because they could
338     // interfere with the symlink following decision: even if we use lstat(),
339     // it would still follow the symlink if we pass it a path with a slash at
340     // the end because the symlink resolution would happen while following the
341     // path and not for the last path element itself.
342 
343     while ( wxEndsWithPathSeparator(path) )
344     {
345         const size_t posLast = path.length() - 1;
346         if ( !posLast )
347         {
348             // Don't turn "/" into empty string.
349             break;
350         }
351 
352         path.erase(posLast);
353     }
354 
355     int ret = dereference ? wxStat(path, &st) : wxLstat(path, &st);
356     return ret == 0;
357 }
358 
359 // Overloads to use for a case when we don't have wxFileName object and when we
360 // do have one.
361 inline
StatAny(wxStructStat & st,const wxString & path,int flags)362 bool StatAny(wxStructStat& st, const wxString& path, int flags)
363 {
364     return DoStatAny(st, path, !(flags & wxFILE_EXISTS_NO_FOLLOW));
365 }
366 
367 inline
StatAny(wxStructStat & st,const wxFileName & fn)368 bool StatAny(wxStructStat& st, const wxFileName& fn)
369 {
370     return DoStatAny(st, fn.GetFullPath(), fn.ShouldFollowLink());
371 }
372 
373 #endif // wxHAVE_LSTAT
374 
375 // ----------------------------------------------------------------------------
376 // private constants
377 // ----------------------------------------------------------------------------
378 
379 // length of \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\ string
380 static const size_t wxMSWUniqueVolumePrefixLength = 49;
381 
382 } // anonymous namespace
383 
384 // ============================================================================
385 // implementation
386 // ============================================================================
387 
388 // ----------------------------------------------------------------------------
389 // wxFileName construction
390 // ----------------------------------------------------------------------------
391 
Assign(const wxFileName & filepath)392 void wxFileName::Assign( const wxFileName &filepath )
393 {
394     m_volume = filepath.GetVolume();
395     m_dirs = filepath.GetDirs();
396     m_name = filepath.GetName();
397     m_ext = filepath.GetExt();
398     m_relative = filepath.m_relative;
399     m_hasExt = filepath.m_hasExt;
400     m_dontFollowLinks = filepath.m_dontFollowLinks;
401 }
402 
Assign(const wxString & volume,const wxString & path,const wxString & name,const wxString & ext,bool hasExt,wxPathFormat format)403 void wxFileName::Assign(const wxString& volume,
404                         const wxString& path,
405                         const wxString& name,
406                         const wxString& ext,
407                         bool hasExt,
408                         wxPathFormat format)
409 {
410     // we should ignore paths which look like UNC shares because we already
411     // have the volume here and the UNC notation (\\server\path) is only valid
412     // for paths which don't start with a volume, so prevent SetPath() from
413     // recognizing "\\foo\bar" in "c:\\foo\bar" as an UNC path
414     //
415     // note also that this is a rather ugly way to do what we want (passing
416     // some kind of flag telling to ignore UNC paths to SetPath() would be
417     // better) but this is the safest thing to do to avoid breaking backwards
418     // compatibility in 2.8
419     if ( IsUNCPath(path, format) )
420     {
421         // remove one of the 2 leading backslashes to ensure that it's not
422         // recognized as an UNC path by SetPath()
423         wxString pathNonUNC(path, 1, wxString::npos);
424         SetPath(pathNonUNC, format);
425     }
426     else // no UNC complications
427     {
428         SetPath(path, format);
429     }
430 
431     m_volume = volume;
432     m_ext = ext;
433     m_name = name;
434 
435     m_hasExt = hasExt;
436 }
437 
SetPath(const wxString & pathOrig,wxPathFormat format)438 void wxFileName::SetPath( const wxString& pathOrig, wxPathFormat format )
439 {
440     m_dirs.Clear();
441 
442     if ( pathOrig.empty() )
443     {
444         // no path at all
445         m_relative = true;
446 
447         return;
448     }
449 
450     format = GetFormat( format );
451 
452     // 0) deal with possible volume part first
453     wxString volume,
454              path;
455     SplitVolume(pathOrig, &volume, &path, format);
456     if ( !volume.empty() )
457     {
458         m_relative = false;
459 
460         SetVolume(volume);
461     }
462 
463     // 1) Determine if the path is relative or absolute.
464 
465     if ( path.empty() )
466     {
467         // we had only the volume
468         return;
469     }
470 
471     wxChar leadingChar = path[0u];
472 
473     switch (format)
474     {
475         case wxPATH_MAC:
476             m_relative = leadingChar == wxT(':');
477 
478             // We then remove a leading ":". The reason is in our
479             // storage form for relative paths:
480             // ":dir:file.txt" actually means "./dir/file.txt" in
481             // DOS notation and should get stored as
482             // (relative) (dir) (file.txt)
483             // "::dir:file.txt" actually means "../dir/file.txt"
484             // stored as (relative) (..) (dir) (file.txt)
485             // This is important only for the Mac as an empty dir
486             // actually means <UP>, whereas under DOS, double
487             // slashes can be ignored: "\\\\" is the same as "\\".
488             if (m_relative)
489                 path.erase( 0, 1 );
490             break;
491 
492         case wxPATH_VMS:
493             // TODO: what is the relative path format here?
494             m_relative = false;
495             break;
496 
497         default:
498             wxFAIL_MSG( wxT("Unknown path format") );
499             // !! Fall through !!
500 
501         case wxPATH_UNIX:
502             m_relative = leadingChar != wxT('/');
503             break;
504 
505         case wxPATH_DOS:
506             m_relative = !IsPathSeparator(leadingChar, format);
507             break;
508 
509     }
510 
511     // 2) Break up the path into its members. If the original path
512     //    was just "/" or "\\", m_dirs will be empty. We know from
513     //    the m_relative field, if this means "nothing" or "root dir".
514 
515     wxStringTokenizer tn( path, GetPathSeparators(format) );
516 
517     while ( tn.HasMoreTokens() )
518     {
519         wxString token = tn.GetNextToken();
520 
521         // Remove empty token under DOS and Unix, interpret them
522         // as .. under Mac.
523         if (token.empty())
524         {
525             if (format == wxPATH_MAC)
526                 m_dirs.Add( wxT("..") );
527             // else ignore
528         }
529         else
530         {
531            m_dirs.Add( token );
532         }
533     }
534 }
535 
Assign(const wxString & fullpath,wxPathFormat format)536 void wxFileName::Assign(const wxString& fullpath,
537                         wxPathFormat format)
538 {
539     wxString volume, path, name, ext;
540     bool hasExt;
541     SplitPath(fullpath, &volume, &path, &name, &ext, &hasExt, format);
542 
543     Assign(volume, path, name, ext, hasExt, format);
544 }
545 
Assign(const wxString & fullpathOrig,const wxString & fullname,wxPathFormat format)546 void wxFileName::Assign(const wxString& fullpathOrig,
547                         const wxString& fullname,
548                         wxPathFormat format)
549 {
550     // always recognize fullpath as directory, even if it doesn't end with a
551     // slash
552     wxString fullpath = fullpathOrig;
553     if ( !fullpath.empty() && !wxEndsWithPathSeparator(fullpath) )
554     {
555         fullpath += GetPathSeparator(format);
556     }
557 
558     wxString volume, path, name, ext;
559     bool hasExt;
560 
561     // do some consistency checks: the name should be really just the filename
562     // and the path should be really just a path
563     wxString volDummy, pathDummy, nameDummy, extDummy;
564 
565     SplitPath(fullname, &volDummy, &pathDummy, &name, &ext, &hasExt, format);
566 
567     wxASSERT_MSG( volDummy.empty() && pathDummy.empty(),
568                   wxT("the file name shouldn't contain the path") );
569 
570     SplitPath(fullpath, &volume, &path, &nameDummy, &extDummy, format);
571 
572 #ifndef __VMS
573    // This test makes no sense on an OpenVMS system.
574    wxASSERT_MSG( nameDummy.empty() && extDummy.empty(),
575                   wxT("the path shouldn't contain file name nor extension") );
576 #endif
577     Assign(volume, path, name, ext, hasExt, format);
578 }
579 
Assign(const wxString & pathOrig,const wxString & name,const wxString & ext,wxPathFormat format)580 void wxFileName::Assign(const wxString& pathOrig,
581                         const wxString& name,
582                         const wxString& ext,
583                         wxPathFormat format)
584 {
585     wxString volume,
586              path;
587     SplitVolume(pathOrig, &volume, &path, format);
588 
589     Assign(volume, path, name, ext, format);
590 }
591 
AssignDir(const wxString & dir,wxPathFormat format)592 void wxFileName::AssignDir(const wxString& dir, wxPathFormat format)
593 {
594     Assign(dir, wxEmptyString, format);
595 }
596 
Clear()597 void wxFileName::Clear()
598 {
599     m_dirs.clear();
600     m_volume.clear();
601     m_name.clear();
602     m_ext.clear();
603 
604     // we don't have any absolute path for now
605     m_relative = true;
606 
607     // nor any extension
608     m_hasExt = false;
609 
610     // follow symlinks by default
611     m_dontFollowLinks = false;
612 }
613 
614 /* static */
FileName(const wxString & file,wxPathFormat format)615 wxFileName wxFileName::FileName(const wxString& file, wxPathFormat format)
616 {
617     return wxFileName(file, format);
618 }
619 
620 /* static */
DirName(const wxString & dir,wxPathFormat format)621 wxFileName wxFileName::DirName(const wxString& dir, wxPathFormat format)
622 {
623     wxFileName fn;
624     fn.AssignDir(dir, format);
625     return fn;
626 }
627 
628 // ----------------------------------------------------------------------------
629 // existence tests
630 // ----------------------------------------------------------------------------
631 
632 namespace
633 {
634 
635 #if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
636 
RemoveTrailingSeparatorsFromPath(wxString & strPath)637 void RemoveTrailingSeparatorsFromPath(wxString& strPath)
638 {
639     // Windows fails to find directory named "c:\dir\" even if "c:\dir" exists,
640     // so remove all trailing backslashes from the path - but don't do this for
641     // the paths "d:\" (which are different from "d:"), for just "\" or for
642     // windows unique volume names ("\\?\Volume{GUID}\")
643     while ( wxEndsWithPathSeparator( strPath ) )
644     {
645         size_t len = strPath.length();
646         if ( len == 1 || (len == 3 && strPath[len - 2] == wxT(':')) ||
647                 (len == wxMSWUniqueVolumePrefixLength &&
648                  wxFileName::IsMSWUniqueVolumeNamePath(strPath)))
649         {
650             break;
651         }
652 
653         strPath.Truncate(len - 1);
654     }
655 }
656 
657 #endif // __WINDOWS__ || __OS2__
658 
659 bool
wxFileSystemObjectExists(const wxString & path,int flags)660 wxFileSystemObjectExists(const wxString& path, int flags)
661 {
662 
663     // Should the existence of file/directory with this name be accepted, i.e.
664     // result in the true return value from this function?
665     const bool acceptFile = (flags & wxFILE_EXISTS_REGULAR) != 0;
666     const bool acceptDir  = (flags & wxFILE_EXISTS_DIR)  != 0;
667 
668     wxString strPath(path);
669 
670 #if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
671     if ( acceptDir )
672     {
673         // Ensure that the path doesn't have any trailing separators when
674         // checking for directories.
675         RemoveTrailingSeparatorsFromPath(strPath);
676     }
677 
678     // we must use GetFileAttributes() instead of the ANSI C functions because
679     // it can cope with network (UNC) paths unlike them
680     DWORD ret = ::GetFileAttributes(strPath.t_str());
681 
682     if ( ret == INVALID_FILE_ATTRIBUTES )
683         return false;
684 
685     if ( ret & FILE_ATTRIBUTE_DIRECTORY )
686         return acceptDir;
687 
688     // Anything else must be a file (perhaps we should check for
689     // FILE_ATTRIBUTE_REPARSE_POINT?)
690     return acceptFile;
691 #elif defined(__OS2__)
692     if ( acceptDir )
693     {
694         // OS/2 can't handle "d:", it wants either "d:\" or "d:."
695         if (strPath.length() == 2 && strPath[1u] == wxT(':'))
696             strPath << wxT('.');
697     }
698 
699     FILESTATUS3 Info = {{0}};
700     APIRET rc = ::DosQueryPathInfo((PSZ)(WXSTRINGCAST strPath), FIL_STANDARD,
701             (void*) &Info, sizeof(FILESTATUS3));
702 
703     if ( rc == NO_ERROR )
704     {
705         if ( Info.attrFile & FILE_DIRECTORY )
706             return acceptDir;
707         else
708             return acceptFile;
709     }
710 
711     // We consider that the path must exist if we get a sharing violation for
712     // it but we don't know what is it in this case.
713     if ( rc == ERROR_SHARING_VIOLATION )
714         return flags & wxFILE_EXISTS_ANY;
715 
716     // Any other error (usually ERROR_PATH_NOT_FOUND), means there is nothing
717     // there.
718     return false;
719 #else // Non-MSW, non-OS/2
720     wxStructStat st;
721     if ( !StatAny(st, strPath, flags) )
722         return false;
723 
724     if ( S_ISREG(st.st_mode) )
725         return acceptFile;
726     if ( S_ISDIR(st.st_mode) )
727         return acceptDir;
728     if ( S_ISLNK(st.st_mode) )
729     {
730         // Take care to not test for "!= 0" here as this would erroneously
731         // return true if only wxFILE_EXISTS_NO_FOLLOW, which is part of
732         // wxFILE_EXISTS_SYMLINK, is set too.
733         return (flags & wxFILE_EXISTS_SYMLINK) == wxFILE_EXISTS_SYMLINK;
734     }
735     if ( S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) )
736         return (flags & wxFILE_EXISTS_DEVICE) != 0;
737     if ( S_ISFIFO(st.st_mode) )
738         return (flags & wxFILE_EXISTS_FIFO) != 0;
739     if ( S_ISSOCK(st.st_mode) )
740         return (flags & wxFILE_EXISTS_SOCKET) != 0;
741 
742     return flags & wxFILE_EXISTS_ANY;
743 #endif // Platforms
744 }
745 
746 } // anonymous namespace
747 
FileExists() const748 bool wxFileName::FileExists() const
749 {
750     int flags = wxFILE_EXISTS_REGULAR;
751     if ( !ShouldFollowLink() )
752         flags |= wxFILE_EXISTS_NO_FOLLOW;
753 
754     return wxFileSystemObjectExists(GetFullPath(), flags);
755 }
756 
757 /* static */
FileExists(const wxString & filePath)758 bool wxFileName::FileExists( const wxString &filePath )
759 {
760     return wxFileSystemObjectExists(filePath, wxFILE_EXISTS_REGULAR);
761 }
762 
DirExists() const763 bool wxFileName::DirExists() const
764 {
765     int flags = wxFILE_EXISTS_DIR;
766     if ( !ShouldFollowLink() )
767         flags |= wxFILE_EXISTS_NO_FOLLOW;
768 
769     return Exists(GetPath(), flags);
770 }
771 
772 /* static */
DirExists(const wxString & dirPath)773 bool wxFileName::DirExists( const wxString &dirPath )
774 {
775     return wxFileSystemObjectExists(dirPath, wxFILE_EXISTS_DIR);
776 }
777 
Exists(int flags) const778 bool wxFileName::Exists(int flags) const
779 {
780     // Notice that wxFILE_EXISTS_NO_FOLLOW may be specified in the flags even
781     // if our DontFollowLink() hadn't been called and we do honour it then. But
782     // if the user took the care of calling DontFollowLink(), it is always
783     // taken into account.
784     if ( !ShouldFollowLink() )
785         flags |= wxFILE_EXISTS_NO_FOLLOW;
786 
787     return wxFileSystemObjectExists(GetFullPath(), flags);
788 }
789 
790 /* static */
Exists(const wxString & path,int flags)791 bool wxFileName::Exists(const wxString& path, int flags)
792 {
793     return wxFileSystemObjectExists(path, flags);
794 }
795 
796 // ----------------------------------------------------------------------------
797 // CWD and HOME stuff
798 // ----------------------------------------------------------------------------
799 
AssignCwd(const wxString & volume)800 void wxFileName::AssignCwd(const wxString& volume)
801 {
802     AssignDir(wxFileName::GetCwd(volume));
803 }
804 
805 /* static */
GetCwd(const wxString & volume)806 wxString wxFileName::GetCwd(const wxString& volume)
807 {
808     // if we have the volume, we must get the current directory on this drive
809     // and to do this we have to chdir to this volume - at least under Windows,
810     // I don't know how to get the current drive on another volume elsewhere
811     // (TODO)
812     wxString cwdOld;
813     if ( !volume.empty() )
814     {
815         cwdOld = wxGetCwd();
816         SetCwd(volume + GetVolumeSeparator());
817     }
818 
819     wxString cwd = ::wxGetCwd();
820 
821     if ( !volume.empty() )
822     {
823         SetCwd(cwdOld);
824     }
825 
826     return cwd;
827 }
828 
SetCwd() const829 bool wxFileName::SetCwd() const
830 {
831     return wxFileName::SetCwd( GetPath() );
832 }
833 
SetCwd(const wxString & cwd)834 bool wxFileName::SetCwd( const wxString &cwd )
835 {
836     return ::wxSetWorkingDirectory( cwd );
837 }
838 
AssignHomeDir()839 void wxFileName::AssignHomeDir()
840 {
841     AssignDir(wxFileName::GetHomeDir());
842 }
843 
GetHomeDir()844 wxString wxFileName::GetHomeDir()
845 {
846     return ::wxGetHomeDir();
847 }
848 
849 
850 // ----------------------------------------------------------------------------
851 // CreateTempFileName
852 // ----------------------------------------------------------------------------
853 
854 #if wxUSE_FILE || wxUSE_FFILE
855 
856 
857 #if !defined wx_fdopen && defined HAVE_FDOPEN
858     #define wx_fdopen fdopen
859 #endif
860 
861 // NB: GetTempFileName() under Windows creates the file, so using
862 //     O_EXCL there would fail
863 #ifdef __WINDOWS__
864     #define wxOPEN_EXCL 0
865 #else
866     #define wxOPEN_EXCL O_EXCL
867 #endif
868 
869 
870 #ifdef wxOpenOSFHandle
871 #define WX_HAVE_DELETE_ON_CLOSE
872 // On Windows create a file with the FILE_FLAGS_DELETE_ON_CLOSE flags.
873 //
wxOpenWithDeleteOnClose(const wxString & filename)874 static int wxOpenWithDeleteOnClose(const wxString& filename)
875 {
876     DWORD access = GENERIC_READ | GENERIC_WRITE;
877 
878     DWORD disposition = OPEN_ALWAYS;
879 
880     DWORD attributes = FILE_ATTRIBUTE_TEMPORARY |
881                        FILE_FLAG_DELETE_ON_CLOSE;
882 
883     HANDLE h = ::CreateFile(filename.t_str(), access, 0, NULL,
884                             disposition, attributes, NULL);
885 
886     return wxOpenOSFHandle(h, wxO_BINARY);
887 }
888 #endif // wxOpenOSFHandle
889 
890 
891 // Helper to open the file
892 //
wxTempOpen(const wxString & path,bool * deleteOnClose)893 static int wxTempOpen(const wxString& path, bool *deleteOnClose)
894 {
895 #ifdef WX_HAVE_DELETE_ON_CLOSE
896     if (*deleteOnClose)
897         return wxOpenWithDeleteOnClose(path);
898 #endif
899 
900     *deleteOnClose = false;
901 
902     return wxOpen(path, wxO_BINARY | O_RDWR | O_CREAT | wxOPEN_EXCL, 0600);
903 }
904 
905 
906 #if wxUSE_FFILE
907 // Helper to open the file and attach it to the wxFFile
908 //
wxTempOpen(wxFFile * file,const wxString & path,bool * deleteOnClose)909 static bool wxTempOpen(wxFFile *file, const wxString& path, bool *deleteOnClose)
910 {
911 #ifndef wx_fdopen
912     *deleteOnClose = false;
913     return file->Open(path, wxT("w+b"));
914 #else // wx_fdopen
915     int fd = wxTempOpen(path, deleteOnClose);
916     if (fd == -1)
917         return false;
918     file->Attach(wx_fdopen(fd, "w+b"), path);
919     return file->IsOpened();
920 #endif // wx_fdopen
921 }
922 #endif // wxUSE_FFILE
923 
924 
925 #if !wxUSE_FILE
926     #define WXFILEARGS(x, y) y
927 #elif !wxUSE_FFILE
928     #define WXFILEARGS(x, y) x
929 #else
930     #define WXFILEARGS(x, y) x, y
931 #endif
932 
933 
934 // Implementation of wxFileName::CreateTempFileName().
935 //
wxCreateTempImpl(const wxString & prefix,WXFILEARGS (wxFile * fileTemp,wxFFile * ffileTemp),bool * deleteOnClose=NULL)936 static wxString wxCreateTempImpl(
937         const wxString& prefix,
938         WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
939         bool *deleteOnClose = NULL)
940 {
941 #if wxUSE_FILE && wxUSE_FFILE
942     wxASSERT(fileTemp == NULL || ffileTemp == NULL);
943 #endif
944     wxString path, dir, name;
945     bool wantDeleteOnClose = false;
946 
947     if (deleteOnClose)
948     {
949         // set the result to false initially
950         wantDeleteOnClose = *deleteOnClose;
951         *deleteOnClose = false;
952     }
953     else
954     {
955         // easier if it alwasys points to something
956         deleteOnClose = &wantDeleteOnClose;
957     }
958 
959     // use the directory specified by the prefix
960     wxFileName::SplitPath(prefix, &dir, &name, NULL /* extension */);
961 
962     if (dir.empty())
963     {
964         dir = wxFileName::GetTempDir();
965     }
966 
967 #if defined(__WXWINCE__)
968     path = dir + wxT("\\") + name;
969     int i = 1;
970     while (wxFileName::FileExists(path))
971     {
972         path = dir + wxT("\\") + name ;
973         path << i;
974         i ++;
975     }
976 
977 #elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
978     if (!::GetTempFileName(dir.t_str(), name.t_str(), 0,
979                            wxStringBuffer(path, MAX_PATH + 1)))
980     {
981         wxLogLastError(wxT("GetTempFileName"));
982 
983         path.clear();
984     }
985 
986 #else // !Windows
987     path = dir;
988 
989     if ( !wxEndsWithPathSeparator(dir) &&
990             (name.empty() || !wxIsPathSeparator(name[0u])) )
991     {
992         path += wxFILE_SEP_PATH;
993     }
994 
995     path += name;
996 
997 #if defined(HAVE_MKSTEMP)
998     // scratch space for mkstemp()
999     path += wxT("XXXXXX");
1000 
1001     // we need to copy the path to the buffer in which mkstemp() can modify it
1002     wxCharBuffer buf(path.fn_str());
1003 
1004     // cast is safe because the string length doesn't change
1005     int fdTemp = mkstemp( (char*)(const char*) buf );
1006     if ( fdTemp == -1 )
1007     {
1008         // this might be not necessary as mkstemp() on most systems should have
1009         // already done it but it doesn't hurt neither...
1010         path.clear();
1011     }
1012     else // mkstemp() succeeded
1013     {
1014         path = wxConvFile.cMB2WX( (const char*) buf );
1015 
1016     #if wxUSE_FILE
1017         // avoid leaking the fd
1018         if ( fileTemp )
1019         {
1020             fileTemp->Attach(fdTemp);
1021         }
1022         else
1023     #endif
1024 
1025     #if wxUSE_FFILE
1026         if ( ffileTemp )
1027         {
1028         #ifdef wx_fdopen
1029             ffileTemp->Attach(wx_fdopen(fdTemp, "r+b"), path);
1030         #else
1031             ffileTemp->Open(path, wxT("r+b"));
1032             close(fdTemp);
1033         #endif
1034         }
1035         else
1036     #endif
1037 
1038         {
1039             close(fdTemp);
1040         }
1041     }
1042 #else // !HAVE_MKSTEMP
1043 
1044 #ifdef HAVE_MKTEMP
1045     // same as above
1046     path += wxT("XXXXXX");
1047 
1048     wxCharBuffer buf = wxConvFile.cWX2MB( path );
1049     if ( !mktemp( (char*)(const char*) buf ) )
1050     {
1051         path.clear();
1052     }
1053     else
1054     {
1055         path = wxConvFile.cMB2WX( (const char*) buf );
1056     }
1057 #else // !HAVE_MKTEMP (includes __DOS__)
1058     // generate the unique file name ourselves
1059     #if !defined(__DOS__)
1060     path << (unsigned int)getpid();
1061     #endif
1062 
1063     wxString pathTry;
1064 
1065     static const size_t numTries = 1000;
1066     for ( size_t n = 0; n < numTries; n++ )
1067     {
1068         // 3 hex digits is enough for numTries == 1000 < 4096
1069         pathTry = path + wxString::Format(wxT("%.03x"), (unsigned int) n);
1070         if ( !wxFileName::FileExists(pathTry) )
1071         {
1072             break;
1073         }
1074 
1075         pathTry.clear();
1076     }
1077 
1078     path = pathTry;
1079 #endif // HAVE_MKTEMP/!HAVE_MKTEMP
1080 
1081 #endif // HAVE_MKSTEMP/!HAVE_MKSTEMP
1082 
1083 #endif // Windows/!Windows
1084 
1085     if ( path.empty() )
1086     {
1087         wxLogSysError(_("Failed to create a temporary file name"));
1088     }
1089     else
1090     {
1091         bool ok = true;
1092 
1093         // open the file - of course, there is a race condition here, this is
1094         // why we always prefer using mkstemp()...
1095     #if wxUSE_FILE
1096         if ( fileTemp && !fileTemp->IsOpened() )
1097         {
1098             *deleteOnClose = wantDeleteOnClose;
1099             int fd = wxTempOpen(path, deleteOnClose);
1100             if (fd != -1)
1101                 fileTemp->Attach(fd);
1102             else
1103                 ok = false;
1104         }
1105     #endif
1106 
1107     #if wxUSE_FFILE
1108         if ( ffileTemp && !ffileTemp->IsOpened() )
1109         {
1110             *deleteOnClose = wantDeleteOnClose;
1111             ok = wxTempOpen(ffileTemp, path, deleteOnClose);
1112         }
1113     #endif
1114 
1115         if ( !ok )
1116         {
1117             // FIXME: If !ok here should we loop and try again with another
1118             //        file name?  That is the standard recourse if open(O_EXCL)
1119             //        fails, though of course it should be protected against
1120             //        possible infinite looping too.
1121 
1122             wxLogError(_("Failed to open temporary file."));
1123 
1124             path.clear();
1125         }
1126     }
1127 
1128     return path;
1129 }
1130 
1131 
wxCreateTempImpl(const wxString & prefix,WXFILEARGS (wxFile * fileTemp,wxFFile * ffileTemp),wxString * name)1132 static bool wxCreateTempImpl(
1133         const wxString& prefix,
1134         WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
1135         wxString *name)
1136 {
1137     bool deleteOnClose = true;
1138 
1139     *name = wxCreateTempImpl(prefix,
1140                              WXFILEARGS(fileTemp, ffileTemp),
1141                              &deleteOnClose);
1142 
1143     bool ok = !name->empty();
1144 
1145     if (deleteOnClose)
1146         name->clear();
1147 #ifdef __UNIX__
1148     else if (ok && wxRemoveFile(*name))
1149         name->clear();
1150 #endif
1151 
1152     return ok;
1153 }
1154 
1155 
wxAssignTempImpl(wxFileName * fn,const wxString & prefix,WXFILEARGS (wxFile * fileTemp,wxFFile * ffileTemp))1156 static void wxAssignTempImpl(
1157         wxFileName *fn,
1158         const wxString& prefix,
1159         WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp))
1160 {
1161     wxString tempname;
1162     tempname = wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, ffileTemp));
1163 
1164     if ( tempname.empty() )
1165     {
1166         // error, failed to get temp file name
1167         fn->Clear();
1168     }
1169     else // ok
1170     {
1171         fn->Assign(tempname);
1172     }
1173 }
1174 
1175 
AssignTempFileName(const wxString & prefix)1176 void wxFileName::AssignTempFileName(const wxString& prefix)
1177 {
1178     wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, NULL));
1179 }
1180 
1181 /* static */
CreateTempFileName(const wxString & prefix)1182 wxString wxFileName::CreateTempFileName(const wxString& prefix)
1183 {
1184     return wxCreateTempImpl(prefix, WXFILEARGS(NULL, NULL));
1185 }
1186 
1187 #endif // wxUSE_FILE || wxUSE_FFILE
1188 
1189 
1190 #if wxUSE_FILE
1191 
wxCreateTempFileName(const wxString & prefix,wxFile * fileTemp,bool * deleteOnClose)1192 wxString wxCreateTempFileName(const wxString& prefix,
1193                               wxFile *fileTemp,
1194                               bool *deleteOnClose)
1195 {
1196     return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), deleteOnClose);
1197 }
1198 
wxCreateTempFile(const wxString & prefix,wxFile * fileTemp,wxString * name)1199 bool wxCreateTempFile(const wxString& prefix,
1200                       wxFile *fileTemp,
1201                       wxString *name)
1202 {
1203     return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), name);
1204 }
1205 
AssignTempFileName(const wxString & prefix,wxFile * fileTemp)1206 void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp)
1207 {
1208     wxAssignTempImpl(this, prefix, WXFILEARGS(fileTemp, NULL));
1209 }
1210 
1211 /* static */
1212 wxString
CreateTempFileName(const wxString & prefix,wxFile * fileTemp)1213 wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp)
1214 {
1215     return wxCreateTempFileName(prefix, fileTemp);
1216 }
1217 
1218 #endif // wxUSE_FILE
1219 
1220 
1221 #if wxUSE_FFILE
1222 
wxCreateTempFileName(const wxString & prefix,wxFFile * fileTemp,bool * deleteOnClose)1223 wxString wxCreateTempFileName(const wxString& prefix,
1224                               wxFFile *fileTemp,
1225                               bool *deleteOnClose)
1226 {
1227     return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), deleteOnClose);
1228 }
1229 
wxCreateTempFile(const wxString & prefix,wxFFile * fileTemp,wxString * name)1230 bool wxCreateTempFile(const wxString& prefix,
1231                       wxFFile *fileTemp,
1232                       wxString *name)
1233 {
1234     return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), name);
1235 
1236 }
1237 
AssignTempFileName(const wxString & prefix,wxFFile * fileTemp)1238 void wxFileName::AssignTempFileName(const wxString& prefix, wxFFile *fileTemp)
1239 {
1240     wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, fileTemp));
1241 }
1242 
1243 /* static */
1244 wxString
CreateTempFileName(const wxString & prefix,wxFFile * fileTemp)1245 wxFileName::CreateTempFileName(const wxString& prefix, wxFFile *fileTemp)
1246 {
1247     return wxCreateTempFileName(prefix, fileTemp);
1248 }
1249 
1250 #endif // wxUSE_FFILE
1251 
1252 
1253 // ----------------------------------------------------------------------------
1254 // directory operations
1255 // ----------------------------------------------------------------------------
1256 
1257 // helper of GetTempDir(): check if the given directory exists and return it if
1258 // it does or an empty string otherwise
1259 namespace
1260 {
1261 
CheckIfDirExists(const wxString & dir)1262 wxString CheckIfDirExists(const wxString& dir)
1263 {
1264     return wxFileName::DirExists(dir) ? dir : wxString();
1265 }
1266 
1267 } // anonymous namespace
1268 
GetTempDir()1269 wxString wxFileName::GetTempDir()
1270 {
1271     // first try getting it from environment: this allows overriding the values
1272     // used by default if the user wants to create temporary files in another
1273     // directory
1274     wxString dir = CheckIfDirExists(wxGetenv("TMPDIR"));
1275     if ( dir.empty() )
1276     {
1277         dir = CheckIfDirExists(wxGetenv("TMP"));
1278         if ( dir.empty() )
1279             dir = CheckIfDirExists(wxGetenv("TEMP"));
1280     }
1281 
1282     // if no environment variables are set, use the system default
1283     if ( dir.empty() )
1284     {
1285 #if defined(__WXWINCE__)
1286         dir = CheckIfDirExists(wxT("\\temp"));
1287 #elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
1288         if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
1289         {
1290             wxLogLastError(wxT("GetTempPath"));
1291         }
1292 #elif defined(__WXMAC__) && wxOSX_USE_CARBON
1293         dir = wxMacFindFolderNoSeparator(short(kOnSystemDisk), kTemporaryFolderType, kCreateFolder);
1294 #endif // systems with native way
1295     }
1296     else // we got directory from an environment variable
1297     {
1298         // remove any trailing path separators, we don't want to ever return
1299         // them from this function for consistency
1300         const size_t lastNonSep = dir.find_last_not_of(GetPathSeparators());
1301         if ( lastNonSep == wxString::npos )
1302         {
1303             // the string consists entirely of separators, leave only one
1304             dir = GetPathSeparator();
1305         }
1306         else
1307         {
1308             dir.erase(lastNonSep + 1);
1309         }
1310     }
1311 
1312     // fall back to hard coded value
1313     if ( dir.empty() )
1314     {
1315 #ifdef __UNIX_LIKE__
1316         dir = CheckIfDirExists("/tmp");
1317         if ( dir.empty() )
1318 #endif // __UNIX_LIKE__
1319             dir = ".";
1320     }
1321 
1322     return dir;
1323 }
1324 
Mkdir(int perm,int flags) const1325 bool wxFileName::Mkdir( int perm, int flags ) const
1326 {
1327     return wxFileName::Mkdir(GetPath(), perm, flags);
1328 }
1329 
Mkdir(const wxString & dir,int perm,int flags)1330 bool wxFileName::Mkdir( const wxString& dir, int perm, int flags )
1331 {
1332     if ( flags & wxPATH_MKDIR_FULL )
1333     {
1334         // split the path in components
1335         wxFileName filename;
1336         filename.AssignDir(dir);
1337 
1338         wxString currPath;
1339         if ( filename.HasVolume())
1340         {
1341             currPath << wxGetVolumeString(filename.GetVolume(), wxPATH_NATIVE);
1342         }
1343 
1344         wxArrayString dirs = filename.GetDirs();
1345         size_t count = dirs.GetCount();
1346         for ( size_t i = 0; i < count; i++ )
1347         {
1348             if ( i > 0 || filename.IsAbsolute() )
1349                 currPath += wxFILE_SEP_PATH;
1350             currPath += dirs[i];
1351 
1352             if (!DirExists(currPath))
1353             {
1354                 if (!wxMkdir(currPath, perm))
1355                 {
1356                     // no need to try creating further directories
1357                     return false;
1358                 }
1359             }
1360         }
1361 
1362         return true;
1363 
1364     }
1365 
1366     return ::wxMkdir( dir, perm );
1367 }
1368 
Rmdir(int flags) const1369 bool wxFileName::Rmdir(int flags) const
1370 {
1371     return wxFileName::Rmdir( GetPath(), flags );
1372 }
1373 
Rmdir(const wxString & dir,int flags)1374 bool wxFileName::Rmdir(const wxString& dir, int flags)
1375 {
1376 #ifdef __WINDOWS__
1377     if ( flags & wxPATH_RMDIR_RECURSIVE )
1378     {
1379         // SHFileOperation needs double null termination string
1380         // but without separator at the end of the path
1381         wxString path(dir);
1382         if ( path.Last() == wxFILE_SEP_PATH )
1383             path.RemoveLast();
1384         path += wxT('\0');
1385 
1386         SHFILEOPSTRUCT fileop;
1387         wxZeroMemory(fileop);
1388         fileop.wFunc = FO_DELETE;
1389         fileop.pFrom = path.t_str();
1390         fileop.fFlags = FOF_SILENT | FOF_NOCONFIRMATION;
1391     #ifndef __WXWINCE__
1392         // FOF_NOERRORUI is not defined in WinCE
1393         fileop.fFlags |= FOF_NOERRORUI;
1394     #endif
1395 
1396         int ret = SHFileOperation(&fileop);
1397         if ( ret != 0 )
1398         {
1399             // SHFileOperation may return non-Win32 error codes, so the error
1400             // message can be incorrect
1401             wxLogApiError(wxT("SHFileOperation"), ret);
1402             return false;
1403         }
1404 
1405         return true;
1406     }
1407     else if ( flags & wxPATH_RMDIR_FULL )
1408 #else // !__WINDOWS__
1409     if ( flags != 0 )   // wxPATH_RMDIR_FULL or wxPATH_RMDIR_RECURSIVE
1410 #endif // !__WINDOWS__
1411     {
1412 #ifndef __WINDOWS__
1413         if ( flags & wxPATH_RMDIR_RECURSIVE )
1414         {
1415             // When deleting the tree recursively, we are supposed to delete
1416             // this directory itself even when it is a symlink -- but without
1417             // following it. Do it here as wxRmdir() would simply follow if
1418             // called for a symlink.
1419             if ( wxFileName::Exists(dir, wxFILE_EXISTS_SYMLINK) )
1420             {
1421                 return wxRemoveFile(dir);
1422             }
1423         }
1424 #endif // !__WINDOWS__
1425 
1426         wxString path(dir);
1427         if ( path.Last() != wxFILE_SEP_PATH )
1428             path += wxFILE_SEP_PATH;
1429 
1430         wxDir d(path);
1431 
1432         if ( !d.IsOpened() )
1433             return false;
1434 
1435         wxString filename;
1436 
1437         // First delete all subdirectories: notice that we don't follow
1438         // symbolic links, potentially leading outside this directory, to avoid
1439         // unpleasant surprises.
1440         bool cont = d.GetFirst(&filename, wxString(),
1441                                wxDIR_DIRS | wxDIR_HIDDEN | wxDIR_NO_FOLLOW);
1442         while ( cont )
1443         {
1444             wxFileName::Rmdir(path + filename, flags);
1445             cont = d.GetNext(&filename);
1446         }
1447 
1448 #ifndef __WINDOWS__
1449         if ( flags & wxPATH_RMDIR_RECURSIVE )
1450         {
1451             // Delete all files too and, for the same reasons as above, don't
1452             // follow symlinks which could refer to the files outside of this
1453             // directory and just delete the symlinks themselves.
1454             cont = d.GetFirst(&filename, wxString(),
1455                               wxDIR_FILES | wxDIR_HIDDEN | wxDIR_NO_FOLLOW);
1456             while ( cont )
1457             {
1458                 ::wxRemoveFile(path + filename);
1459                 cont = d.GetNext(&filename);
1460             }
1461         }
1462 #endif // !__WINDOWS__
1463     }
1464 
1465     return ::wxRmdir(dir);
1466 }
1467 
1468 // ----------------------------------------------------------------------------
1469 // path normalization
1470 // ----------------------------------------------------------------------------
1471 
Normalize(int flags,const wxString & cwd,wxPathFormat format)1472 bool wxFileName::Normalize(int flags,
1473                            const wxString& cwd,
1474                            wxPathFormat format)
1475 {
1476     // deal with env vars renaming first as this may seriously change the path
1477     if ( flags & wxPATH_NORM_ENV_VARS )
1478     {
1479         wxString pathOrig = GetFullPath(format);
1480         wxString path = wxExpandEnvVars(pathOrig);
1481         if ( path != pathOrig )
1482         {
1483             Assign(path);
1484         }
1485     }
1486 
1487     // the existing path components
1488     wxArrayString dirs = GetDirs();
1489 
1490     // the path to prepend in front to make the path absolute
1491     wxFileName curDir;
1492 
1493     format = GetFormat(format);
1494 
1495     // set up the directory to use for making the path absolute later
1496     if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute(format) )
1497     {
1498         if ( cwd.empty() )
1499         {
1500             curDir.AssignCwd(GetVolume());
1501         }
1502         else // cwd provided
1503         {
1504             curDir.AssignDir(cwd);
1505         }
1506     }
1507 
1508     // handle ~ stuff under Unix only
1509     if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) && m_relative )
1510     {
1511         if ( !dirs.IsEmpty() )
1512         {
1513             wxString dir = dirs[0u];
1514             if ( !dir.empty() && dir[0u] == wxT('~') )
1515             {
1516                 // to make the path absolute use the home directory
1517                 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
1518                 dirs.RemoveAt(0u);
1519             }
1520         }
1521     }
1522 
1523     // transform relative path into abs one
1524     if ( curDir.IsOk() )
1525     {
1526         // this path may be relative because it doesn't have the volume name
1527         // and still have m_relative=true; in this case we shouldn't modify
1528         // our directory components but just set the current volume
1529         if ( !HasVolume() && curDir.HasVolume() )
1530         {
1531             SetVolume(curDir.GetVolume());
1532 
1533             if ( !m_relative )
1534         {
1535                 // yes, it was the case - we don't need curDir then
1536                 curDir.Clear();
1537             }
1538         }
1539 
1540         // finally, prepend curDir to the dirs array
1541         wxArrayString dirsNew = curDir.GetDirs();
1542         WX_PREPEND_ARRAY(dirs, dirsNew);
1543 
1544         // if we used e.g. tilde expansion previously and wxGetUserHome didn't
1545         // return for some reason an absolute path, then curDir maybe not be absolute!
1546         if ( !curDir.m_relative )
1547         {
1548             // we have prepended an absolute path and thus we are now an absolute
1549             // file name too
1550             m_relative = false;
1551         }
1552         // else if (flags & wxPATH_NORM_ABSOLUTE):
1553         //   should we warn the user that we didn't manage to make the path absolute?
1554     }
1555 
1556     // now deal with ".", ".." and the rest
1557     m_dirs.Empty();
1558     size_t count = dirs.GetCount();
1559     for ( size_t n = 0; n < count; n++ )
1560     {
1561         wxString dir = dirs[n];
1562 
1563         if ( flags & wxPATH_NORM_DOTS )
1564         {
1565             if ( dir == wxT(".") )
1566             {
1567                 // just ignore
1568                 continue;
1569             }
1570 
1571             if ( dir == wxT("..") )
1572             {
1573                 if ( m_dirs.empty() )
1574                 {
1575                     // We have more ".." than directory components so far.
1576                     // Don't treat this as an error as the path could have been
1577                     // entered by user so try to handle it reasonably: if the
1578                     // path is absolute, just ignore the extra ".." because
1579                     // "/.." is the same as "/". Otherwise, i.e. for relative
1580                     // paths, keep ".." unchanged because removing it would
1581                     // modify the file a relative path refers to.
1582                     if ( !m_relative )
1583                         continue;
1584 
1585                 }
1586                 else // Normal case, go one step up.
1587                 {
1588                     m_dirs.pop_back();
1589                     continue;
1590                 }
1591             }
1592         }
1593 
1594         m_dirs.Add(dir);
1595     }
1596 
1597 #if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
1598     if ( (flags & wxPATH_NORM_SHORTCUT) )
1599     {
1600         wxString filename;
1601         if (GetShortcutTarget(GetFullPath(format), filename))
1602         {
1603             m_relative = false;
1604             Assign(filename);
1605         }
1606     }
1607 #endif
1608 
1609 #if defined(__WIN32__)
1610     if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
1611     {
1612         Assign(GetLongPath());
1613     }
1614 #endif // Win32
1615 
1616     // Change case  (this should be kept at the end of the function, to ensure
1617     // that the path doesn't change any more after we normalize its case)
1618     if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
1619     {
1620         m_volume.MakeLower();
1621         m_name.MakeLower();
1622         m_ext.MakeLower();
1623 
1624         // directory entries must be made lower case as well
1625         count = m_dirs.GetCount();
1626         for ( size_t i = 0; i < count; i++ )
1627         {
1628             m_dirs[i].MakeLower();
1629         }
1630     }
1631 
1632     return true;
1633 }
1634 
1635 #ifndef __WXWINCE__
ReplaceEnvVariable(const wxString & envname,const wxString & replacementFmtString,wxPathFormat format)1636 bool wxFileName::ReplaceEnvVariable(const wxString& envname,
1637                                     const wxString& replacementFmtString,
1638                                     wxPathFormat format)
1639 {
1640     // look into stringForm for the contents of the given environment variable
1641     wxString val;
1642     if (envname.empty() ||
1643         !wxGetEnv(envname, &val))
1644         return false;
1645     if (val.empty())
1646         return false;
1647 
1648     wxString stringForm = GetPath(wxPATH_GET_VOLUME, format);
1649         // do not touch the file name and the extension
1650 
1651     wxString replacement = wxString::Format(replacementFmtString, envname);
1652     stringForm.Replace(val, replacement);
1653 
1654     // Now assign ourselves the modified path:
1655     Assign(stringForm, GetFullName(), format);
1656 
1657     return true;
1658 }
1659 #endif
1660 
ReplaceHomeDir(wxPathFormat format)1661 bool wxFileName::ReplaceHomeDir(wxPathFormat format)
1662 {
1663     wxString homedir = wxGetHomeDir();
1664     if (homedir.empty())
1665         return false;
1666 
1667     wxString stringForm = GetPath(wxPATH_GET_VOLUME, format);
1668         // do not touch the file name and the extension
1669 
1670     stringForm.Replace(homedir, "~");
1671 
1672     // Now assign ourselves the modified path:
1673     Assign(stringForm, GetFullName(), format);
1674 
1675     return true;
1676 }
1677 
1678 // ----------------------------------------------------------------------------
1679 // get the shortcut target
1680 // ----------------------------------------------------------------------------
1681 
1682 // WinCE (3) doesn't have CLSID_ShellLink, IID_IShellLink definitions.
1683 // The .lnk file is a plain text file so it should be easy to
1684 // make it work. Hint from Google Groups:
1685 // "If you open up a lnk file, you'll see a
1686 // number, followed by a pound sign (#), followed by more text. The
1687 // number is the number of characters that follows the pound sign. The
1688 // characters after the pound sign are the command line (which _can_
1689 // include arguments) to be executed. Any path (e.g. \windows\program
1690 // files\myapp.exe) that includes spaces needs to be enclosed in
1691 // quotation marks."
1692 
1693 #if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
1694 
GetShortcutTarget(const wxString & shortcutPath,wxString & targetFilename,wxString * arguments) const1695 bool wxFileName::GetShortcutTarget(const wxString& shortcutPath,
1696                                    wxString& targetFilename,
1697                                    wxString* arguments) const
1698 {
1699     wxString path, file, ext;
1700     wxFileName::SplitPath(shortcutPath, & path, & file, & ext);
1701 
1702     HRESULT hres;
1703     IShellLink* psl;
1704     bool success = false;
1705 
1706     // Assume it's not a shortcut if it doesn't end with lnk
1707     if (ext.CmpNoCase(wxT("lnk"))!=0)
1708         return false;
1709 
1710     // Ensure OLE is initialized.
1711     wxOleInitializer oleInit;
1712 
1713     // create a ShellLink object
1714     hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1715                             IID_IShellLink, (LPVOID*) &psl);
1716 
1717     if (SUCCEEDED(hres))
1718     {
1719         IPersistFile* ppf;
1720         hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf);
1721         if (SUCCEEDED(hres))
1722         {
1723             WCHAR wsz[MAX_PATH];
1724 
1725             MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, shortcutPath.mb_str(), -1, wsz,
1726                                 MAX_PATH);
1727 
1728             hres = ppf->Load(wsz, 0);
1729             ppf->Release();
1730 
1731             if (SUCCEEDED(hres))
1732             {
1733                 wxChar buf[2048];
1734                 // Wrong prototype in early versions
1735 #if defined(__MINGW32__) && !wxCHECK_W32API_VERSION(2, 2)
1736                 psl->GetPath((CHAR*) buf, 2048, NULL, SLGP_UNCPRIORITY);
1737 #else
1738                 psl->GetPath(buf, 2048, NULL, SLGP_UNCPRIORITY);
1739 #endif
1740                 targetFilename = wxString(buf);
1741                 success = (shortcutPath != targetFilename);
1742 
1743                 psl->GetArguments(buf, 2048);
1744                 wxString args(buf);
1745                 if (!args.empty() && arguments)
1746                 {
1747                     *arguments = args;
1748                 }
1749             }
1750         }
1751 
1752         psl->Release();
1753     }
1754     return success;
1755 }
1756 
1757 #endif // __WIN32__ && !__WXWINCE__
1758 
1759 
1760 // ----------------------------------------------------------------------------
1761 // absolute/relative paths
1762 // ----------------------------------------------------------------------------
1763 
IsAbsolute(wxPathFormat format) const1764 bool wxFileName::IsAbsolute(wxPathFormat format) const
1765 {
1766     // unix paths beginning with ~ are reported as being absolute
1767     if ( format == wxPATH_UNIX )
1768     {
1769         if ( !m_dirs.IsEmpty() )
1770         {
1771             wxString dir = m_dirs[0u];
1772 
1773             if (!dir.empty() && dir[0u] == wxT('~'))
1774                 return true;
1775         }
1776     }
1777 
1778     // if our path doesn't start with a path separator, it's not an absolute
1779     // path
1780     if ( m_relative )
1781         return false;
1782 
1783     if ( !GetVolumeSeparator(format).empty() )
1784     {
1785         // this format has volumes and an absolute path must have one, it's not
1786         // enough to have the full path to be an absolute file under Windows
1787         if ( GetVolume().empty() )
1788             return false;
1789     }
1790 
1791     return true;
1792 }
1793 
MakeRelativeTo(const wxString & pathBase,wxPathFormat format)1794 bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
1795 {
1796     wxFileName fnBase = wxFileName::DirName(pathBase, format);
1797 
1798     // get cwd only once - small time saving
1799     wxString cwd = wxGetCwd();
1800 
1801     // Normalize the paths but avoid changing the case or turning a shortcut
1802     // into a file that it points to.
1803     const int normFlags = wxPATH_NORM_ALL &
1804                             ~(wxPATH_NORM_CASE | wxPATH_NORM_SHORTCUT);
1805     Normalize(normFlags, cwd, format);
1806     fnBase.Normalize(normFlags, cwd, format);
1807 
1808     bool withCase = IsCaseSensitive(format);
1809 
1810     // we can't do anything if the files live on different volumes
1811     if ( !GetVolume().IsSameAs(fnBase.GetVolume(), withCase) )
1812     {
1813         // nothing done
1814         return false;
1815     }
1816 
1817     // same drive, so we don't need our volume
1818     m_volume.clear();
1819 
1820     // remove common directories starting at the top
1821     while ( !m_dirs.IsEmpty() && !fnBase.m_dirs.IsEmpty() &&
1822                 m_dirs[0u].IsSameAs(fnBase.m_dirs[0u], withCase) )
1823     {
1824         m_dirs.RemoveAt(0);
1825         fnBase.m_dirs.RemoveAt(0);
1826     }
1827 
1828     // add as many ".." as needed
1829     size_t count = fnBase.m_dirs.GetCount();
1830     for ( size_t i = 0; i < count; i++ )
1831     {
1832         m_dirs.Insert(wxT(".."), 0u);
1833     }
1834 
1835     switch ( GetFormat(format) )
1836     {
1837         case wxPATH_NATIVE:
1838         case wxPATH_MAX:
1839             wxFAIL_MSG( wxS("unreachable") );
1840             // fall through
1841 
1842         case wxPATH_UNIX:
1843         case wxPATH_DOS:
1844             // a directory made relative with respect to itself is '.' under
1845             // Unix and DOS, by definition (but we don't have to insert "./"
1846             // for the files)
1847             if ( m_dirs.IsEmpty() && IsDir() )
1848             {
1849                 m_dirs.Add(wxT('.'));
1850             }
1851             break;
1852 
1853         case wxPATH_MAC:
1854         case wxPATH_VMS:
1855             break;
1856     }
1857 
1858     m_relative = true;
1859 
1860     // we were modified
1861     return true;
1862 }
1863 
1864 // ----------------------------------------------------------------------------
1865 // filename kind tests
1866 // ----------------------------------------------------------------------------
1867 
SameAs(const wxFileName & filepath,wxPathFormat format) const1868 bool wxFileName::SameAs(const wxFileName& filepath, wxPathFormat format) const
1869 {
1870     wxFileName fn1 = *this,
1871                fn2 = filepath;
1872 
1873     // get cwd only once - small time saving
1874     wxString cwd = wxGetCwd();
1875     fn1.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
1876     fn2.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
1877 
1878     if ( fn1.GetFullPath() == fn2.GetFullPath() )
1879         return true;
1880 
1881 #ifdef wxHAVE_LSTAT
1882     wxStructStat st1, st2;
1883     if ( StatAny(st1, fn1) && StatAny(st2, fn2) )
1884     {
1885         if ( st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev )
1886             return true;
1887     }
1888     //else: It's not an error if one or both files don't exist.
1889 #endif // wxHAVE_LSTAT
1890 
1891     return false;
1892 }
1893 
1894 /* static */
IsCaseSensitive(wxPathFormat format)1895 bool wxFileName::IsCaseSensitive( wxPathFormat format )
1896 {
1897     // only Unix filenames are truly case-sensitive
1898     return GetFormat(format) == wxPATH_UNIX;
1899 }
1900 
1901 /* static */
GetForbiddenChars(wxPathFormat format)1902 wxString wxFileName::GetForbiddenChars(wxPathFormat format)
1903 {
1904     // Inits to forbidden characters that are common to (almost) all platforms.
1905     wxString strForbiddenChars = wxT("*?");
1906 
1907     // If asserts, wxPathFormat has been changed. In case of a new path format
1908     // addition, the following code might have to be updated.
1909     wxCOMPILE_TIME_ASSERT(wxPATH_MAX == 5, wxPathFormatChanged);
1910     switch ( GetFormat(format) )
1911     {
1912         default :
1913             wxFAIL_MSG( wxT("Unknown path format") );
1914             // !! Fall through !!
1915 
1916         case wxPATH_UNIX:
1917             break;
1918 
1919         case wxPATH_MAC:
1920             // On a Mac even names with * and ? are allowed (Tested with OS
1921             // 9.2.1 and OS X 10.2.5)
1922             strForbiddenChars.clear();
1923             break;
1924 
1925         case wxPATH_DOS:
1926             strForbiddenChars += wxT("\\/:\"<>|");
1927             break;
1928 
1929         case wxPATH_VMS:
1930             break;
1931     }
1932 
1933     return strForbiddenChars;
1934 }
1935 
1936 /* static */
GetVolumeSeparator(wxPathFormat WXUNUSED_IN_WINCE (format))1937 wxString wxFileName::GetVolumeSeparator(wxPathFormat WXUNUSED_IN_WINCE(format))
1938 {
1939 #ifdef __WXWINCE__
1940     return wxEmptyString;
1941 #else
1942     wxString sepVol;
1943 
1944     if ( (GetFormat(format) == wxPATH_DOS) ||
1945          (GetFormat(format) == wxPATH_VMS) )
1946     {
1947         sepVol = wxFILE_SEP_DSK;
1948     }
1949     //else: leave empty
1950 
1951     return sepVol;
1952 #endif
1953 }
1954 
1955 /* static */
GetPathSeparators(wxPathFormat format)1956 wxString wxFileName::GetPathSeparators(wxPathFormat format)
1957 {
1958     wxString seps;
1959     switch ( GetFormat(format) )
1960     {
1961         case wxPATH_DOS:
1962             // accept both as native APIs do but put the native one first as
1963             // this is the one we use in GetFullPath()
1964             seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX;
1965             break;
1966 
1967         default:
1968             wxFAIL_MSG( wxT("Unknown wxPATH_XXX style") );
1969             // fall through
1970 
1971         case wxPATH_UNIX:
1972             seps = wxFILE_SEP_PATH_UNIX;
1973             break;
1974 
1975         case wxPATH_MAC:
1976             seps = wxFILE_SEP_PATH_MAC;
1977             break;
1978 
1979         case wxPATH_VMS:
1980             seps = wxFILE_SEP_PATH_VMS;
1981             break;
1982     }
1983 
1984     return seps;
1985 }
1986 
1987 /* static */
GetPathTerminators(wxPathFormat format)1988 wxString wxFileName::GetPathTerminators(wxPathFormat format)
1989 {
1990     format = GetFormat(format);
1991 
1992     // under VMS the end of the path is ']', not the path separator used to
1993     // separate the components
1994     return format == wxPATH_VMS ? wxString(wxT(']')) : GetPathSeparators(format);
1995 }
1996 
1997 /* static */
IsPathSeparator(wxChar ch,wxPathFormat format)1998 bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
1999 {
2000     // wxString::Find() doesn't work as expected with NUL - it will always find
2001     // it, so test for it separately
2002     return ch != wxT('\0') && GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
2003 }
2004 
2005 /* static */
2006 bool
IsMSWUniqueVolumeNamePath(const wxString & path,wxPathFormat format)2007 wxFileName::IsMSWUniqueVolumeNamePath(const wxString& path, wxPathFormat format)
2008 {
2009     // return true if the format used is the DOS/Windows one and the string begins
2010     // with a Windows unique volume name ("\\?\Volume{guid}\")
2011     return format == wxPATH_DOS &&
2012             path.length() >= wxMSWUniqueVolumePrefixLength &&
2013              path.StartsWith(wxS("\\\\?\\Volume{")) &&
2014               path[wxMSWUniqueVolumePrefixLength - 1] == wxFILE_SEP_PATH_DOS;
2015 }
2016 
2017 // ----------------------------------------------------------------------------
2018 // path components manipulation
2019 // ----------------------------------------------------------------------------
2020 
IsValidDirComponent(const wxString & dir)2021 /* static */ bool wxFileName::IsValidDirComponent(const wxString& dir)
2022 {
2023     if ( dir.empty() )
2024     {
2025         wxFAIL_MSG( wxT("empty directory passed to wxFileName::InsertDir()") );
2026 
2027         return false;
2028     }
2029 
2030     const size_t len = dir.length();
2031     for ( size_t n = 0; n < len; n++ )
2032     {
2033         if ( dir[n] == GetVolumeSeparator() || IsPathSeparator(dir[n]) )
2034         {
2035             wxFAIL_MSG( wxT("invalid directory component in wxFileName") );
2036 
2037             return false;
2038         }
2039     }
2040 
2041     return true;
2042 }
2043 
AppendDir(const wxString & dir)2044 bool wxFileName::AppendDir( const wxString& dir )
2045 {
2046     if (!IsValidDirComponent(dir))
2047         return false;
2048     m_dirs.Add(dir);
2049     return true;
2050 }
2051 
PrependDir(const wxString & dir)2052 void wxFileName::PrependDir( const wxString& dir )
2053 {
2054     InsertDir(0, dir);
2055 }
2056 
InsertDir(size_t before,const wxString & dir)2057 bool wxFileName::InsertDir(size_t before, const wxString& dir)
2058 {
2059     if (!IsValidDirComponent(dir))
2060         return false;
2061     m_dirs.Insert(dir, before);
2062     return true;
2063 }
2064 
RemoveDir(size_t pos)2065 void wxFileName::RemoveDir(size_t pos)
2066 {
2067     m_dirs.RemoveAt(pos);
2068 }
2069 
2070 // ----------------------------------------------------------------------------
2071 // accessors
2072 // ----------------------------------------------------------------------------
2073 
SetFullName(const wxString & fullname)2074 void wxFileName::SetFullName(const wxString& fullname)
2075 {
2076     SplitPath(fullname, NULL /* no volume */, NULL /* no path */,
2077                         &m_name, &m_ext, &m_hasExt);
2078 }
2079 
GetFullName() const2080 wxString wxFileName::GetFullName() const
2081 {
2082     wxString fullname = m_name;
2083     if ( m_hasExt )
2084     {
2085         fullname << wxFILE_SEP_EXT << m_ext;
2086     }
2087 
2088     return fullname;
2089 }
2090 
GetPath(int flags,wxPathFormat format) const2091 wxString wxFileName::GetPath( int flags, wxPathFormat format ) const
2092 {
2093     format = GetFormat( format );
2094 
2095     wxString fullpath;
2096 
2097     // return the volume with the path as well if requested
2098     if ( flags & wxPATH_GET_VOLUME )
2099     {
2100         fullpath += wxGetVolumeString(GetVolume(), format);
2101     }
2102 
2103     // the leading character
2104     switch ( format )
2105     {
2106         case wxPATH_MAC:
2107             if ( m_relative )
2108                 fullpath += wxFILE_SEP_PATH_MAC;
2109             break;
2110 
2111         case wxPATH_DOS:
2112             if ( !m_relative )
2113                 fullpath += wxFILE_SEP_PATH_DOS;
2114             break;
2115 
2116         default:
2117             wxFAIL_MSG( wxT("Unknown path format") );
2118             // fall through
2119 
2120         case wxPATH_UNIX:
2121             if ( !m_relative )
2122             {
2123                 fullpath += wxFILE_SEP_PATH_UNIX;
2124             }
2125             break;
2126 
2127         case wxPATH_VMS:
2128             // no leading character here but use this place to unset
2129             // wxPATH_GET_SEPARATOR flag: under VMS it doesn't make sense
2130             // as, if I understand correctly, there should never be a dot
2131             // before the closing bracket
2132             flags &= ~wxPATH_GET_SEPARATOR;
2133     }
2134 
2135     if ( m_dirs.empty() )
2136     {
2137         // there is nothing more
2138         return fullpath;
2139     }
2140 
2141     // then concatenate all the path components using the path separator
2142     if ( format == wxPATH_VMS )
2143     {
2144         fullpath += wxT('[');
2145     }
2146 
2147     const size_t dirCount = m_dirs.GetCount();
2148     for ( size_t i = 0; i < dirCount; i++ )
2149     {
2150         switch (format)
2151         {
2152             case wxPATH_MAC:
2153                 if ( m_dirs[i] == wxT(".") )
2154                 {
2155                     // skip appending ':', this shouldn't be done in this
2156                     // case as "::" is interpreted as ".." under Unix
2157                     continue;
2158                 }
2159 
2160                 // convert back from ".." to nothing
2161                 if ( !m_dirs[i].IsSameAs(wxT("..")) )
2162                      fullpath += m_dirs[i];
2163                 break;
2164 
2165             default:
2166                 wxFAIL_MSG( wxT("Unexpected path format") );
2167                 // still fall through
2168 
2169             case wxPATH_DOS:
2170             case wxPATH_UNIX:
2171                 fullpath += m_dirs[i];
2172                 break;
2173 
2174             case wxPATH_VMS:
2175                 // TODO: What to do with ".." under VMS
2176 
2177                 // convert back from ".." to nothing
2178                 if ( !m_dirs[i].IsSameAs(wxT("..")) )
2179                     fullpath += m_dirs[i];
2180                 break;
2181         }
2182 
2183         if ( (flags & wxPATH_GET_SEPARATOR) || (i != dirCount - 1) )
2184             fullpath += GetPathSeparator(format);
2185     }
2186 
2187     if ( format == wxPATH_VMS )
2188     {
2189         fullpath += wxT(']');
2190     }
2191 
2192     return fullpath;
2193 }
2194 
GetFullPath(wxPathFormat format) const2195 wxString wxFileName::GetFullPath( wxPathFormat format ) const
2196 {
2197     // we already have a function to get the path
2198     wxString fullpath = GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR,
2199                                 format);
2200 
2201     // now just add the file name and extension to it
2202     fullpath += GetFullName();
2203 
2204     return fullpath;
2205 }
2206 
2207 // Return the short form of the path (returns identity on non-Windows platforms)
GetShortPath() const2208 wxString wxFileName::GetShortPath() const
2209 {
2210     wxString path(GetFullPath());
2211 
2212 #if defined(__WINDOWS__) && defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
2213     DWORD sz = ::GetShortPathName(path.t_str(), NULL, 0);
2214     if ( sz != 0 )
2215     {
2216         wxString pathOut;
2217         if ( ::GetShortPathName
2218                (
2219                 path.t_str(),
2220                 wxStringBuffer(pathOut, sz),
2221                 sz
2222                ) != 0 )
2223         {
2224             return pathOut;
2225         }
2226     }
2227 #endif // Windows
2228 
2229     return path;
2230 }
2231 
2232 // Return the long form of the path (returns identity on non-Windows platforms)
GetLongPath() const2233 wxString wxFileName::GetLongPath() const
2234 {
2235     wxString pathOut,
2236              path = GetFullPath();
2237 
2238 #if defined(__WIN32__) && !defined(__WXWINCE__) && !defined(__WXMICROWIN__)
2239 
2240 #if wxUSE_DYNLIB_CLASS
2241     typedef DWORD (WINAPI *GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
2242 
2243     // this is MT-safe as in the worst case we're going to resolve the function
2244     // twice -- but as the result is the same in both threads, it's ok
2245     static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
2246     if ( !s_pfnGetLongPathName )
2247     {
2248         static bool s_triedToLoad = false;
2249 
2250         if ( !s_triedToLoad )
2251         {
2252             s_triedToLoad = true;
2253 
2254             wxDynamicLibrary dllKernel(wxT("kernel32"));
2255 
2256             const wxChar* GetLongPathName = wxT("GetLongPathName")
2257 #if wxUSE_UNICODE
2258                               wxT("W");
2259 #else // ANSI
2260                               wxT("A");
2261 #endif // Unicode/ANSI
2262 
2263             if ( dllKernel.HasSymbol(GetLongPathName) )
2264             {
2265                 s_pfnGetLongPathName = (GET_LONG_PATH_NAME)
2266                     dllKernel.GetSymbol(GetLongPathName);
2267             }
2268 
2269             // note that kernel32.dll can be unloaded, it stays in memory
2270             // anyhow as all Win32 programs link to it and so it's safe to call
2271             // GetLongPathName() even after unloading it
2272         }
2273     }
2274 
2275     if ( s_pfnGetLongPathName )
2276     {
2277         DWORD dwSize = (*s_pfnGetLongPathName)(path.t_str(), NULL, 0);
2278         if ( dwSize > 0 )
2279         {
2280             if ( (*s_pfnGetLongPathName)
2281                  (
2282                   path.t_str(),
2283                   wxStringBuffer(pathOut, dwSize),
2284                   dwSize
2285                  ) != 0 )
2286             {
2287                 return pathOut;
2288             }
2289         }
2290     }
2291 #endif // wxUSE_DYNLIB_CLASS
2292 
2293     // The OS didn't support GetLongPathName, or some other error.
2294     // We need to call FindFirstFile on each component in turn.
2295 
2296     WIN32_FIND_DATA findFileData;
2297     HANDLE hFind;
2298 
2299     if ( HasVolume() )
2300         pathOut = GetVolume() +
2301                   GetVolumeSeparator(wxPATH_DOS) +
2302                   GetPathSeparator(wxPATH_DOS);
2303     else
2304         pathOut.clear();
2305 
2306     wxArrayString dirs = GetDirs();
2307     dirs.Add(GetFullName());
2308 
2309     wxString tmpPath;
2310 
2311     size_t count = dirs.GetCount();
2312     for ( size_t i = 0; i < count; i++ )
2313     {
2314         const wxString& dir = dirs[i];
2315 
2316         // We're using pathOut to collect the long-name path, but using a
2317         // temporary for appending the last path component which may be
2318         // short-name
2319         tmpPath = pathOut + dir;
2320 
2321         // We must not process "." or ".." here as they would be (unexpectedly)
2322         // replaced by the corresponding directory names so just leave them
2323         // alone
2324         //
2325         // And we can't pass a drive and root dir to FindFirstFile (VZ: why?)
2326         if ( tmpPath.empty() || dir == '.' || dir == ".." ||
2327                 tmpPath.Last() == GetVolumeSeparator(wxPATH_DOS) )
2328         {
2329             tmpPath += wxFILE_SEP_PATH;
2330             pathOut = tmpPath;
2331             continue;
2332         }
2333 
2334         hFind = ::FindFirstFile(tmpPath.t_str(), &findFileData);
2335         if (hFind == INVALID_HANDLE_VALUE)
2336         {
2337             // Error: most likely reason is that path doesn't exist, so
2338             // append any unprocessed parts and return
2339             for ( i += 1; i < count; i++ )
2340                 tmpPath += wxFILE_SEP_PATH + dirs[i];
2341 
2342             return tmpPath;
2343         }
2344 
2345         pathOut += findFileData.cFileName;
2346         if ( (i < (count-1)) )
2347             pathOut += wxFILE_SEP_PATH;
2348 
2349         ::FindClose(hFind);
2350     }
2351 #else // !Win32
2352     pathOut = path;
2353 #endif // Win32/!Win32
2354 
2355     return pathOut;
2356 }
2357 
GetFormat(wxPathFormat format)2358 wxPathFormat wxFileName::GetFormat( wxPathFormat format )
2359 {
2360     if (format == wxPATH_NATIVE)
2361     {
2362 #if defined(__WINDOWS__) || defined(__OS2__) || defined(__DOS__)
2363         format = wxPATH_DOS;
2364 #elif defined(__VMS)
2365         format = wxPATH_VMS;
2366 #else
2367         format = wxPATH_UNIX;
2368 #endif
2369     }
2370     return format;
2371 }
2372 
2373 #ifdef wxHAS_FILESYSTEM_VOLUMES
2374 
2375 /* static */
GetVolumeString(char drive,int flags)2376 wxString wxFileName::GetVolumeString(char drive, int flags)
2377 {
2378     wxASSERT_MSG( !(flags & ~wxPATH_GET_SEPARATOR), "invalid flag specified" );
2379 
2380     wxString vol(drive);
2381     vol += wxFILE_SEP_DSK;
2382     if ( flags & wxPATH_GET_SEPARATOR )
2383         vol += wxFILE_SEP_PATH;
2384 
2385     return vol;
2386 }
2387 
2388 #endif // wxHAS_FILESYSTEM_VOLUMES
2389 
2390 // ----------------------------------------------------------------------------
2391 // path splitting function
2392 // ----------------------------------------------------------------------------
2393 
2394 /* static */
2395 void
SplitVolume(const wxString & fullpathWithVolume,wxString * pstrVolume,wxString * pstrPath,wxPathFormat format)2396 wxFileName::SplitVolume(const wxString& fullpathWithVolume,
2397                         wxString *pstrVolume,
2398                         wxString *pstrPath,
2399                         wxPathFormat format)
2400 {
2401     format = GetFormat(format);
2402 
2403     wxString fullpath = fullpathWithVolume;
2404 
2405     if ( IsMSWUniqueVolumeNamePath(fullpath, format) )
2406     {
2407         // special Windows unique volume names hack: transform
2408         // \\?\Volume{guid}\path into Volume{guid}:path
2409         // note: this check must be done before the check for UNC path
2410 
2411         // we know the last backslash from the unique volume name is located
2412         // there from IsMSWUniqueVolumeNamePath
2413         fullpath[wxMSWUniqueVolumePrefixLength - 1] = wxFILE_SEP_DSK;
2414 
2415         // paths starting with a unique volume name should always be absolute
2416         fullpath.insert(wxMSWUniqueVolumePrefixLength, 1, wxFILE_SEP_PATH_DOS);
2417 
2418         // remove the leading "\\?\" part
2419         fullpath.erase(0, 4);
2420     }
2421     else if ( IsUNCPath(fullpath, format) )
2422     {
2423         // special Windows UNC paths hack: transform \\share\path into share:path
2424 
2425         fullpath.erase(0, 2);
2426 
2427         size_t posFirstSlash =
2428             fullpath.find_first_of(GetPathTerminators(format));
2429         if ( posFirstSlash != wxString::npos )
2430         {
2431             fullpath[posFirstSlash] = wxFILE_SEP_DSK;
2432 
2433             // UNC paths are always absolute, right? (FIXME)
2434             fullpath.insert(posFirstSlash + 1, 1, wxFILE_SEP_PATH_DOS);
2435         }
2436     }
2437 
2438     // We separate the volume here
2439     if ( format == wxPATH_DOS || format == wxPATH_VMS )
2440     {
2441         wxString sepVol = GetVolumeSeparator(format);
2442 
2443         // we have to exclude the case of a colon in the very beginning of the
2444         // string as it can't be a volume separator (nor can this be a valid
2445         // DOS file name at all but we'll leave dealing with this to our caller)
2446         size_t posFirstColon = fullpath.find_first_of(sepVol);
2447         if ( posFirstColon && posFirstColon != wxString::npos )
2448         {
2449             if ( pstrVolume )
2450             {
2451                 *pstrVolume = fullpath.Left(posFirstColon);
2452             }
2453 
2454             // remove the volume name and the separator from the full path
2455             fullpath.erase(0, posFirstColon + sepVol.length());
2456         }
2457     }
2458 
2459     if ( pstrPath )
2460         *pstrPath = fullpath;
2461 }
2462 
2463 /* static */
SplitPath(const wxString & fullpathWithVolume,wxString * pstrVolume,wxString * pstrPath,wxString * pstrName,wxString * pstrExt,bool * hasExt,wxPathFormat format)2464 void wxFileName::SplitPath(const wxString& fullpathWithVolume,
2465                            wxString *pstrVolume,
2466                            wxString *pstrPath,
2467                            wxString *pstrName,
2468                            wxString *pstrExt,
2469                            bool *hasExt,
2470                            wxPathFormat format)
2471 {
2472     format = GetFormat(format);
2473 
2474     wxString fullpath;
2475     SplitVolume(fullpathWithVolume, pstrVolume, &fullpath, format);
2476 
2477     // find the positions of the last dot and last path separator in the path
2478     size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
2479     size_t posLastSlash = fullpath.find_last_of(GetPathTerminators(format));
2480 
2481     // check whether this dot occurs at the very beginning of a path component
2482     if ( (posLastDot != wxString::npos) &&
2483          (posLastDot == 0 ||
2484             IsPathSeparator(fullpath[posLastDot - 1]) ||
2485             (format == wxPATH_VMS && fullpath[posLastDot - 1] == wxT(']'))) )
2486     {
2487         // dot may be (and commonly -- at least under Unix -- is) the first
2488         // character of the filename, don't treat the entire filename as
2489         // extension in this case
2490         posLastDot = wxString::npos;
2491     }
2492 
2493     // if we do have a dot and a slash, check that the dot is in the name part
2494     if ( (posLastDot != wxString::npos) &&
2495          (posLastSlash != wxString::npos) &&
2496          (posLastDot < posLastSlash) )
2497     {
2498         // the dot is part of the path, not the start of the extension
2499         posLastDot = wxString::npos;
2500     }
2501 
2502     // now fill in the variables provided by user
2503     if ( pstrPath )
2504     {
2505         if ( posLastSlash == wxString::npos )
2506         {
2507             // no path at all
2508             pstrPath->Empty();
2509         }
2510         else
2511         {
2512             // take everything up to the path separator but take care to make
2513             // the path equal to something like '/', not empty, for the files
2514             // immediately under root directory
2515             size_t len = posLastSlash;
2516 
2517             // this rule does not apply to mac since we do not start with colons (sep)
2518             // except for relative paths
2519             if ( !len && format != wxPATH_MAC)
2520                 len++;
2521 
2522             *pstrPath = fullpath.Left(len);
2523 
2524             // special VMS hack: remove the initial bracket
2525             if ( format == wxPATH_VMS )
2526             {
2527                 if ( (*pstrPath)[0u] == wxT('[') )
2528                     pstrPath->erase(0, 1);
2529             }
2530         }
2531     }
2532 
2533     if ( pstrName )
2534     {
2535         // take all characters starting from the one after the last slash and
2536         // up to, but excluding, the last dot
2537         size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
2538         size_t count;
2539         if ( posLastDot == wxString::npos )
2540         {
2541             // take all until the end
2542             count = wxString::npos;
2543         }
2544         else if ( posLastSlash == wxString::npos )
2545         {
2546             count = posLastDot;
2547         }
2548         else // have both dot and slash
2549         {
2550             count = posLastDot - posLastSlash - 1;
2551         }
2552 
2553         *pstrName = fullpath.Mid(nStart, count);
2554     }
2555 
2556     // finally deal with the extension here: we have an added complication that
2557     // extension may be empty (but present) as in "foo." where trailing dot
2558     // indicates the empty extension at the end -- and hence we must remember
2559     // that we have it independently of pstrExt
2560     if ( posLastDot == wxString::npos )
2561     {
2562         // no extension
2563         if ( pstrExt )
2564             pstrExt->clear();
2565         if ( hasExt )
2566             *hasExt = false;
2567     }
2568     else
2569     {
2570         // take everything after the dot
2571         if ( pstrExt )
2572             *pstrExt = fullpath.Mid(posLastDot + 1);
2573         if ( hasExt )
2574             *hasExt = true;
2575     }
2576 }
2577 
2578 /* static */
SplitPath(const wxString & fullpath,wxString * path,wxString * name,wxString * ext,wxPathFormat format)2579 void wxFileName::SplitPath(const wxString& fullpath,
2580                            wxString *path,
2581                            wxString *name,
2582                            wxString *ext,
2583                            wxPathFormat format)
2584 {
2585     wxString volume;
2586     SplitPath(fullpath, &volume, path, name, ext, format);
2587 
2588     if ( path )
2589     {
2590         path->Prepend(wxGetVolumeString(volume, format));
2591     }
2592 }
2593 
2594 /* static */
StripExtension(const wxString & fullpath)2595 wxString wxFileName::StripExtension(const wxString& fullpath)
2596 {
2597     wxFileName fn(fullpath);
2598     fn.SetExt("");
2599     return fn.GetFullPath();
2600 }
2601 
2602 // ----------------------------------------------------------------------------
2603 // file permissions functions
2604 // ----------------------------------------------------------------------------
2605 
SetPermissions(int permissions)2606 bool wxFileName::SetPermissions(int permissions)
2607 {
2608     // Don't do anything for a symlink but first make sure it is one.
2609     if ( m_dontFollowLinks &&
2610             Exists(GetFullPath(), wxFILE_EXISTS_SYMLINK|wxFILE_EXISTS_NO_FOLLOW) )
2611     {
2612         // Looks like changing permissions for a symlinc is only supported
2613         // on BSD where lchmod is present and correctly implemented.
2614         // http://lists.gnu.org/archive/html/bug-coreutils/2009-09/msg00268.html
2615         return false;
2616     }
2617 
2618 #ifdef __WINDOWS__
2619     int accMode = 0;
2620 
2621     if ( permissions & (wxS_IRUSR|wxS_IRGRP|wxS_IROTH) )
2622         accMode = _S_IREAD;
2623 
2624     if ( permissions & (wxS_IWUSR|wxS_IWGRP|wxS_IWOTH) )
2625         accMode |= _S_IWRITE;
2626 
2627     permissions = accMode;
2628 #endif // __WINDOWS__
2629 
2630     return wxChmod(GetFullPath(), permissions) == 0;
2631 }
2632 
2633 // ----------------------------------------------------------------------------
2634 // time functions
2635 // ----------------------------------------------------------------------------
2636 
2637 #if wxUSE_DATETIME
2638 
SetTimes(const wxDateTime * dtAccess,const wxDateTime * dtMod,const wxDateTime * dtCreate) const2639 bool wxFileName::SetTimes(const wxDateTime *dtAccess,
2640                           const wxDateTime *dtMod,
2641                           const wxDateTime *dtCreate) const
2642 {
2643 #if defined(__WIN32__)
2644     FILETIME ftAccess, ftCreate, ftWrite;
2645 
2646     if ( dtCreate )
2647         ConvertWxToFileTime(&ftCreate, *dtCreate);
2648     if ( dtAccess )
2649         ConvertWxToFileTime(&ftAccess, *dtAccess);
2650     if ( dtMod )
2651         ConvertWxToFileTime(&ftWrite, *dtMod);
2652 
2653     wxString path;
2654     int flags;
2655     if ( IsDir() )
2656     {
2657         if ( wxGetOsVersion() == wxOS_WINDOWS_9X )
2658         {
2659             wxLogError(_("Setting directory access times is not supported "
2660                          "under this OS version"));
2661             return false;
2662         }
2663 
2664         path = GetPath();
2665         flags = FILE_FLAG_BACKUP_SEMANTICS;
2666     }
2667     else // file
2668     {
2669         path = GetFullPath();
2670         flags = 0;
2671     }
2672 
2673     wxFileHandle fh(path, wxFileHandle::WriteAttr, flags);
2674     if ( fh.IsOk() )
2675     {
2676         if ( ::SetFileTime(fh,
2677                            dtCreate ? &ftCreate : NULL,
2678                            dtAccess ? &ftAccess : NULL,
2679                            dtMod ? &ftWrite : NULL) )
2680         {
2681             return true;
2682         }
2683     }
2684 #elif defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__))
2685     wxUnusedVar(dtCreate);
2686 
2687     if ( !dtAccess && !dtMod )
2688     {
2689         // can't modify the creation time anyhow, don't try
2690         return true;
2691     }
2692 
2693     // if dtAccess or dtMod is not specified, use the other one (which must be
2694     // non NULL because of the test above) for both times
2695     utimbuf utm;
2696     utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
2697     utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
2698     if ( utime(GetFullPath().fn_str(), &utm) == 0 )
2699     {
2700         return true;
2701     }
2702 #else // other platform
2703     wxUnusedVar(dtAccess);
2704     wxUnusedVar(dtMod);
2705     wxUnusedVar(dtCreate);
2706 #endif // platforms
2707 
2708     wxLogSysError(_("Failed to modify file times for '%s'"),
2709                   GetFullPath().c_str());
2710 
2711     return false;
2712 }
2713 
Touch() const2714 bool wxFileName::Touch() const
2715 {
2716 #if defined(__UNIX_LIKE__)
2717     // under Unix touching file is simple: just pass NULL to utime()
2718     if ( utime(GetFullPath().fn_str(), NULL) == 0 )
2719     {
2720         return true;
2721     }
2722 
2723     wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str());
2724 
2725     return false;
2726 #else // other platform
2727     wxDateTime dtNow = wxDateTime::Now();
2728 
2729     return SetTimes(&dtNow, &dtNow, NULL /* don't change create time */);
2730 #endif // platforms
2731 }
2732 
GetTimes(wxDateTime * dtAccess,wxDateTime * dtMod,wxDateTime * dtCreate) const2733 bool wxFileName::GetTimes(wxDateTime *dtAccess,
2734                           wxDateTime *dtMod,
2735                           wxDateTime *dtCreate) const
2736 {
2737 #if defined(__WIN32__)
2738     // we must use different methods for the files and directories under
2739     // Windows as CreateFile(GENERIC_READ) doesn't work for the directories and
2740     // CreateFile(FILE_FLAG_BACKUP_SEMANTICS) works -- but only under NT and
2741     // not 9x
2742     bool ok;
2743     FILETIME ftAccess, ftCreate, ftWrite;
2744     if ( IsDir() )
2745     {
2746         // implemented in msw/dir.cpp
2747         extern bool wxGetDirectoryTimes(const wxString& dirname,
2748                                         FILETIME *, FILETIME *, FILETIME *);
2749 
2750         // we should pass the path without the trailing separator to
2751         // wxGetDirectoryTimes()
2752         ok = wxGetDirectoryTimes(GetPath(wxPATH_GET_VOLUME),
2753                                  &ftAccess, &ftCreate, &ftWrite);
2754     }
2755     else // file
2756     {
2757         wxFileHandle fh(GetFullPath(), wxFileHandle::ReadAttr);
2758         if ( fh.IsOk() )
2759         {
2760             ok = ::GetFileTime(fh,
2761                                dtCreate ? &ftCreate : NULL,
2762                                dtAccess ? &ftAccess : NULL,
2763                                dtMod ? &ftWrite : NULL) != 0;
2764         }
2765         else
2766         {
2767             ok = false;
2768         }
2769     }
2770 
2771     if ( ok )
2772     {
2773         if ( dtCreate )
2774             ConvertFileTimeToWx(dtCreate, ftCreate);
2775         if ( dtAccess )
2776             ConvertFileTimeToWx(dtAccess, ftAccess);
2777         if ( dtMod )
2778             ConvertFileTimeToWx(dtMod, ftWrite);
2779 
2780         return true;
2781     }
2782 #elif defined(wxHAVE_LSTAT)
2783     // no need to test for IsDir() here
2784     wxStructStat stBuf;
2785     if ( StatAny(stBuf, *this) )
2786     {
2787         // Android defines st_*time fields as unsigned long, but time_t as long,
2788         // hence the static_casts.
2789         if ( dtAccess )
2790             dtAccess->Set(static_cast<time_t>(stBuf.st_atime));
2791         if ( dtMod )
2792             dtMod->Set(static_cast<time_t>(stBuf.st_mtime));
2793         if ( dtCreate )
2794             dtCreate->Set(static_cast<time_t>(stBuf.st_ctime));
2795 
2796         return true;
2797     }
2798 #else // other platform
2799     wxUnusedVar(dtAccess);
2800     wxUnusedVar(dtMod);
2801     wxUnusedVar(dtCreate);
2802 #endif // platforms
2803 
2804     wxLogSysError(_("Failed to retrieve file times for '%s'"),
2805                   GetFullPath().c_str());
2806 
2807     return false;
2808 }
2809 
2810 #endif // wxUSE_DATETIME
2811 
2812 
2813 // ----------------------------------------------------------------------------
2814 // file size functions
2815 // ----------------------------------------------------------------------------
2816 
2817 #if wxUSE_LONGLONG
2818 
2819 /* static */
GetSize(const wxString & filename)2820 wxULongLong wxFileName::GetSize(const wxString &filename)
2821 {
2822     if (!wxFileExists(filename))
2823         return wxInvalidSize;
2824 
2825 #if defined(__WIN32__)
2826     wxFileHandle f(filename, wxFileHandle::ReadAttr);
2827     if (!f.IsOk())
2828         return wxInvalidSize;
2829 
2830     DWORD lpFileSizeHigh;
2831     DWORD ret = GetFileSize(f, &lpFileSizeHigh);
2832     if ( ret == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR )
2833         return wxInvalidSize;
2834 
2835     return wxULongLong(lpFileSizeHigh, ret);
2836 #else // ! __WIN32__
2837     wxStructStat st;
2838     if (wxStat( filename, &st) != 0)
2839         return wxInvalidSize;
2840     return wxULongLong(st.st_size);
2841 #endif
2842 }
2843 
2844 /* static */
GetHumanReadableSize(const wxULongLong & bs,const wxString & nullsize,int precision,wxSizeConvention conv)2845 wxString wxFileName::GetHumanReadableSize(const wxULongLong &bs,
2846                                           const wxString &nullsize,
2847                                           int precision,
2848                                           wxSizeConvention conv)
2849 {
2850     // deal with trivial case first
2851     if ( bs == 0 || bs == wxInvalidSize )
2852         return nullsize;
2853 
2854     // depending on the convention used the multiplier may be either 1000 or
2855     // 1024 and the binary infix may be empty (for "KB") or "i" (for "KiB")
2856     double multiplier = 1024.;
2857     wxString biInfix;
2858 
2859     switch ( conv )
2860     {
2861         case wxSIZE_CONV_TRADITIONAL:
2862             // nothing to do, this corresponds to the default values of both
2863             // the multiplier and infix string
2864             break;
2865 
2866         case wxSIZE_CONV_IEC:
2867             biInfix = "i";
2868             break;
2869 
2870         case wxSIZE_CONV_SI:
2871             multiplier = 1000;
2872             break;
2873     }
2874 
2875     const double kiloByteSize = multiplier;
2876     const double megaByteSize = multiplier * kiloByteSize;
2877     const double gigaByteSize = multiplier * megaByteSize;
2878     const double teraByteSize = multiplier * gigaByteSize;
2879 
2880     const double bytesize = bs.ToDouble();
2881 
2882     wxString result;
2883     if ( bytesize < kiloByteSize )
2884         result.Printf("%s B", bs.ToString());
2885     else if ( bytesize < megaByteSize )
2886         result.Printf("%.*f K%sB", precision, bytesize/kiloByteSize, biInfix);
2887     else if (bytesize < gigaByteSize)
2888         result.Printf("%.*f M%sB", precision, bytesize/megaByteSize, biInfix);
2889     else if (bytesize < teraByteSize)
2890         result.Printf("%.*f G%sB", precision, bytesize/gigaByteSize, biInfix);
2891     else
2892         result.Printf("%.*f T%sB", precision, bytesize/teraByteSize, biInfix);
2893 
2894     return result;
2895 }
2896 
GetSize() const2897 wxULongLong wxFileName::GetSize() const
2898 {
2899     return GetSize(GetFullPath());
2900 }
2901 
GetHumanReadableSize(const wxString & failmsg,int precision,wxSizeConvention conv) const2902 wxString wxFileName::GetHumanReadableSize(const wxString& failmsg,
2903                                           int precision,
2904                                           wxSizeConvention conv) const
2905 {
2906     return GetHumanReadableSize(GetSize(), failmsg, precision, conv);
2907 }
2908 
2909 #endif // wxUSE_LONGLONG
2910 
2911 // ----------------------------------------------------------------------------
2912 // Mac-specific functions
2913 // ----------------------------------------------------------------------------
2914 
2915 #if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
2916 
2917 namespace
2918 {
2919 
2920 class MacDefaultExtensionRecord
2921 {
2922 public:
MacDefaultExtensionRecord()2923     MacDefaultExtensionRecord()
2924     {
2925         m_type =
2926         m_creator = 0 ;
2927     }
2928 
2929     // default copy ctor, assignment operator and dtor are ok
2930 
MacDefaultExtensionRecord(const wxString & ext,OSType type,OSType creator)2931     MacDefaultExtensionRecord(const wxString& ext, OSType type, OSType creator)
2932         : m_ext(ext)
2933     {
2934         m_type = type;
2935         m_creator = creator;
2936     }
2937 
2938     wxString m_ext;
2939     OSType m_type;
2940     OSType m_creator;
2941 };
2942 
2943 WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord, MacDefaultExtensionArray);
2944 
2945 bool gMacDefaultExtensionsInited = false;
2946 
2947 #include "wx/arrimpl.cpp"
2948 
2949 WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray);
2950 
2951 MacDefaultExtensionArray gMacDefaultExtensions;
2952 
2953 // load the default extensions
2954 const MacDefaultExtensionRecord gDefaults[] =
2955 {
2956     MacDefaultExtensionRecord( "txt", 'TEXT', 'ttxt' ),
2957     MacDefaultExtensionRecord( "tif", 'TIFF', '****' ),
2958     MacDefaultExtensionRecord( "jpg", 'JPEG', '****' ),
2959 };
2960 
MacEnsureDefaultExtensionsLoaded()2961 void MacEnsureDefaultExtensionsLoaded()
2962 {
2963     if ( !gMacDefaultExtensionsInited )
2964     {
2965         // we could load the pc exchange prefs here too
2966         for ( size_t i = 0 ; i < WXSIZEOF( gDefaults ) ; ++i )
2967         {
2968             gMacDefaultExtensions.Add( gDefaults[i] ) ;
2969         }
2970         gMacDefaultExtensionsInited = true;
2971     }
2972 }
2973 
2974 } // anonymous namespace
2975 
MacSetTypeAndCreator(wxUint32 type,wxUint32 creator)2976 bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
2977 {
2978     FSRef fsRef ;
2979     FSCatalogInfo catInfo;
2980     FileInfo *finfo ;
2981 
2982     if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
2983     {
2984         if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
2985         {
2986             finfo = (FileInfo*)&catInfo.finderInfo;
2987             finfo->fileType = type ;
2988             finfo->fileCreator = creator ;
2989             FSSetCatalogInfo( &fsRef, kFSCatInfoFinderInfo, &catInfo ) ;
2990             return true ;
2991         }
2992     }
2993     return false ;
2994 }
2995 
MacGetTypeAndCreator(wxUint32 * type,wxUint32 * creator) const2996 bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator ) const
2997 {
2998     FSRef fsRef ;
2999     FSCatalogInfo catInfo;
3000     FileInfo *finfo ;
3001 
3002     if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
3003     {
3004         if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
3005         {
3006             finfo = (FileInfo*)&catInfo.finderInfo;
3007             *type = finfo->fileType ;
3008             *creator = finfo->fileCreator ;
3009             return true ;
3010         }
3011     }
3012     return false ;
3013 }
3014 
MacSetDefaultTypeAndCreator()3015 bool wxFileName::MacSetDefaultTypeAndCreator()
3016 {
3017     wxUint32 type , creator ;
3018     if ( wxFileName::MacFindDefaultTypeAndCreator(GetExt() , &type ,
3019       &creator ) )
3020     {
3021         return MacSetTypeAndCreator( type , creator ) ;
3022     }
3023     return false;
3024 }
3025 
MacFindDefaultTypeAndCreator(const wxString & ext,wxUint32 * type,wxUint32 * creator)3026 bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
3027 {
3028   MacEnsureDefaultExtensionsLoaded() ;
3029   wxString extl = ext.Lower() ;
3030   for( int i = gMacDefaultExtensions.Count() - 1 ; i >= 0 ; --i )
3031   {
3032     if ( gMacDefaultExtensions.Item(i).m_ext == extl )
3033     {
3034       *type = gMacDefaultExtensions.Item(i).m_type ;
3035       *creator = gMacDefaultExtensions.Item(i).m_creator ;
3036       return true ;
3037     }
3038   }
3039   return false ;
3040 }
3041 
MacRegisterDefaultTypeAndCreator(const wxString & ext,wxUint32 type,wxUint32 creator)3042 void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )
3043 {
3044   MacEnsureDefaultExtensionsLoaded();
3045   MacDefaultExtensionRecord rec(ext.Lower(), type, creator);
3046   gMacDefaultExtensions.Add( rec );
3047 }
3048 
3049 #endif // defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
3050