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