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