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