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