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