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