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