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