]> git.saurik.com Git - wxWidgets.git/blob - src/common/filename.cpp
fixed bug in AssignDir() which didn't always consider the argument as directory
[wxWidgets.git] / src / common / filename.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/filename.cpp
3 // Purpose: wxFileName - encapsulates a file path
4 // Author: Robert Roebling, Vadim Zeitlin
5 // Modified by:
6 // Created: 28.12.2000
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 Robert Roebling
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 /*
13 Here are brief descriptions of the filename formats supported by this class:
14
15 wxPATH_UNIX: standard Unix format, absolute file 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
25
26 wxPATH_MAC: Mac OS 8/9 format, absolute file names have the form
27 volume:dir1:...:dirN:filename
28 and the relative file names are either
29 :dir1:...:dirN:filename
30 or just
31 filename
32 (although :filename works as well).
33
34 wxPATH_VMS: VMS native format, absolute file names have the form
35 <device>:[dir1.dir2.dir3]file.txt
36 or
37 <device>:[000000.dir1.dir2.dir3]file.txt
38
39 the <device> is the physical device (i.e. disk). 000000 is the
40 root directory on the device which can be omitted.
41
42 Note that VMS uses different separators unlike Unix:
43 : always after the device. If the path does not contain : than
44 the default (the device of the current directory) is assumed.
45 [ start of directory specyfication
46 . separator between directory and subdirectory
47 ] between directory and file
48 */
49
50 // ============================================================================
51 // declarations
52 // ============================================================================
53
54 // ----------------------------------------------------------------------------
55 // headers
56 // ----------------------------------------------------------------------------
57
58 #ifdef __GNUG__
59 #pragma implementation "filename.h"
60 #endif
61
62 // For compilers that support precompilation, includes "wx.h".
63 #include "wx/wxprec.h"
64
65 #ifdef __BORLANDC__
66 #pragma hdrstop
67 #endif
68
69 #ifndef WX_PRECOMP
70 #include "wx/intl.h"
71 #include "wx/log.h"
72 #endif
73
74 #include "wx/filename.h"
75 #include "wx/tokenzr.h"
76 #include "wx/config.h" // for wxExpandEnvVars
77 #include "wx/utils.h"
78
79 #if wxUSE_DYNLIB_CLASS
80 #include "wx/dynlib.h"
81 #endif
82
83 // For GetShort/LongPathName
84 #ifdef __WIN32__
85 #include <windows.h>
86
87 #include "wx/msw/winundef.h"
88 #endif
89
90 // utime() is POSIX so should normally be available on all Unices
91 #ifdef __UNIX_LIKE__
92 #include <sys/types.h>
93 #include <utime.h>
94 #include <sys/stat.h>
95 #include <unistd.h>
96 #endif
97
98 #ifdef __MWERKS__
99 #include <stat.h>
100 #include <unistd.h>
101 #include <unix.h>
102 #endif
103
104 // ----------------------------------------------------------------------------
105 // private classes
106 // ----------------------------------------------------------------------------
107
108 // small helper class which opens and closes the file - we use it just to get
109 // a file handle for the given file name to pass it to some Win32 API function
110 #if defined(__WIN32__) && !defined(__WXMICROWIN__)
111
112 class wxFileHandle
113 {
114 public:
115 wxFileHandle(const wxString& filename)
116 {
117 m_hFile = ::CreateFile
118 (
119 filename, // name
120 GENERIC_READ, // access mask
121 0, // no sharing
122 NULL, // no secutity attr
123 OPEN_EXISTING, // creation disposition
124 0, // no flags
125 NULL // no template file
126 );
127
128 if ( m_hFile == INVALID_HANDLE_VALUE )
129 {
130 wxLogSysError(_("Failed to open '%s' for reading"),
131 filename.c_str());
132 }
133 }
134
135 ~wxFileHandle()
136 {
137 if ( m_hFile != INVALID_HANDLE_VALUE )
138 {
139 if ( !::CloseHandle(m_hFile) )
140 {
141 wxLogSysError(_("Failed to close file handle"));
142 }
143 }
144 }
145
146 // return TRUE only if the file could be opened successfully
147 bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; }
148
149 // get the handle
150 operator HANDLE() const { return m_hFile; }
151
152 private:
153 HANDLE m_hFile;
154 };
155
156 #endif // __WIN32__
157
158 // ----------------------------------------------------------------------------
159 // private functions
160 // ----------------------------------------------------------------------------
161
162 #if defined(__WIN32__) && !defined(__WXMICROWIN__)
163
164 // convert between wxDateTime and FILETIME which is a 64-bit value representing
165 // the number of 100-nanosecond intervals since January 1, 1601.
166
167 // the number of milliseconds between the Unix Epoch (January 1, 1970) and the
168 // FILETIME reference point (January 1, 1601)
169 static const wxLongLong FILETIME_EPOCH_OFFSET = wxLongLong(0xa97, 0x30b66800);
170
171 static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft)
172 {
173 wxLongLong ll(ft.dwHighDateTime, ft.dwLowDateTime);
174
175 // convert 100ns to ms
176 ll /= 10000;
177
178 // move it to our Epoch
179 ll -= FILETIME_EPOCH_OFFSET;
180
181 *dt = wxDateTime(ll);
182 }
183
184 static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt)
185 {
186 // do the reverse of ConvertFileTimeToWx()
187 wxLongLong ll = dt.GetValue();
188 ll *= 10000;
189 ll += FILETIME_EPOCH_OFFSET;
190
191 ft->dwHighDateTime = ll.GetHi();
192 ft->dwLowDateTime = ll.GetLo();
193 }
194
195 #endif // __WIN32__
196
197 // ============================================================================
198 // implementation
199 // ============================================================================
200
201 // ----------------------------------------------------------------------------
202 // wxFileName construction
203 // ----------------------------------------------------------------------------
204
205 void wxFileName::Assign( const wxFileName &filepath )
206 {
207 m_volume = filepath.GetVolume();
208 m_dirs = filepath.GetDirs();
209 m_name = filepath.GetName();
210 m_ext = filepath.GetExt();
211 }
212
213 void wxFileName::Assign(const wxString& volume,
214 const wxString& path,
215 const wxString& name,
216 const wxString& ext,
217 wxPathFormat format )
218 {
219 wxStringTokenizer tn(path, GetPathSeparators(format));
220
221 m_dirs.Clear();
222 while ( tn.HasMoreTokens() )
223 {
224 wxString token = tn.GetNextToken();
225
226 // if the path starts with a slash, we do need the first empty dir
227 // entry to be able to tell later that it was an absolute path, but
228 // otherwise ignore the double slashes
229 if ( m_dirs.IsEmpty() || !token.IsEmpty() )
230 m_dirs.Add( token );
231 }
232
233 m_volume = volume;
234 m_ext = ext;
235 m_name = name;
236 }
237
238 void wxFileName::Assign(const wxString& fullpath,
239 wxPathFormat format)
240 {
241 wxString volume, path, name, ext;
242 SplitPath(fullpath, &volume, &path, &name, &ext, format);
243
244 Assign(volume, path, name, ext, format);
245 }
246
247 void wxFileName::Assign(const wxString& fullpath,
248 const wxString& fullname,
249 wxPathFormat format)
250 {
251 wxString volume, path, name, ext;
252 SplitPath(fullname, NULL /* no path */, &name, &ext, format);
253 SplitPath(fullpath, &volume, &path, NULL, NULL, format);
254
255 Assign(volume, path, name, ext, format);
256 }
257
258 void wxFileName::AssignDir(const wxString& dir, wxPathFormat format)
259 {
260 // always recognize dir as directory, even if it doesn't end with a slash
261 wxString dirname = dir;
262 if ( !wxEndsWithPathSeparator(dirname) )
263 {
264 dirname += GetPathSeparators(format)[0u];
265 }
266
267 Assign(dirname, _T(""), format);
268 }
269
270 void wxFileName::Clear()
271 {
272 m_dirs.Clear();
273
274 m_volume =
275 m_name =
276 m_ext = wxEmptyString;
277 }
278
279 /* static */
280 wxFileName wxFileName::FileName(const wxString& file)
281 {
282 return wxFileName(file);
283 }
284
285 /* static */
286 wxFileName wxFileName::DirName(const wxString& dir)
287 {
288 wxFileName fn;
289 fn.AssignDir(dir);
290 return fn;
291 }
292
293 // ----------------------------------------------------------------------------
294 // existence tests
295 // ----------------------------------------------------------------------------
296
297 bool wxFileName::FileExists()
298 {
299 return wxFileName::FileExists( GetFullPath() );
300 }
301
302 bool wxFileName::FileExists( const wxString &file )
303 {
304 return ::wxFileExists( file );
305 }
306
307 bool wxFileName::DirExists()
308 {
309 return wxFileName::DirExists( GetFullPath() );
310 }
311
312 bool wxFileName::DirExists( const wxString &dir )
313 {
314 return ::wxDirExists( dir );
315 }
316
317 // ----------------------------------------------------------------------------
318 // CWD and HOME stuff
319 // ----------------------------------------------------------------------------
320
321 void wxFileName::AssignCwd(const wxString& volume)
322 {
323 AssignDir(wxFileName::GetCwd(volume));
324 }
325
326 /* static */
327 wxString wxFileName::GetCwd(const wxString& volume)
328 {
329 // if we have the volume, we must get the current directory on this drive
330 // and to do this we have to chdir to this volume - at least under Windows,
331 // I don't know how to get the current drive on another volume elsewhere
332 // (TODO)
333 wxString cwdOld;
334 if ( !volume.empty() )
335 {
336 cwdOld = wxGetCwd();
337 SetCwd(volume + GetVolumeSeparator());
338 }
339
340 wxString cwd = ::wxGetCwd();
341
342 if ( !volume.empty() )
343 {
344 SetCwd(cwdOld);
345 }
346
347 return cwd;
348 }
349
350 bool wxFileName::SetCwd()
351 {
352 return wxFileName::SetCwd( GetFullPath() );
353 }
354
355 bool wxFileName::SetCwd( const wxString &cwd )
356 {
357 return ::wxSetWorkingDirectory( cwd );
358 }
359
360 void wxFileName::AssignHomeDir()
361 {
362 AssignDir(wxFileName::GetHomeDir());
363 }
364
365 wxString wxFileName::GetHomeDir()
366 {
367 return ::wxGetHomeDir();
368 }
369
370 void wxFileName::AssignTempFileName( const wxString& prefix )
371 {
372 wxString tempname = CreateTempFileName(prefix);
373 if ( tempname.empty() )
374 {
375 // error, failed to get temp file name
376 Clear();
377 }
378 else // ok
379 {
380 Assign(tempname);
381 }
382 }
383
384 /* static */
385 wxString wxFileName::CreateTempFileName(const wxString& prefix)
386 {
387 wxString path, dir, name;
388
389 // use the directory specified by the prefix
390 SplitPath(prefix, &dir, &name, NULL /* extension */);
391
392 #if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
393
394 #ifdef __WIN32__
395 if ( dir.empty() )
396 {
397 if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
398 {
399 wxLogLastError(_T("GetTempPath"));
400 }
401
402 if ( dir.empty() )
403 {
404 // GetTempFileName() fails if we pass it an empty string
405 dir = _T('.');
406 }
407 }
408
409 if ( !::GetTempFileName(dir, name, 0, wxStringBuffer(path, MAX_PATH + 1)) )
410 {
411 wxLogLastError(_T("GetTempFileName"));
412
413 path.clear();
414 }
415 #else // Win16
416 if ( !::GetTempFileName(NULL, prefix, 0, wxStringBuffer(path, 1025)) )
417 {
418 path.clear();
419 }
420 #endif // Win32/16
421
422 #elif defined(__WXPM__)
423 // for now just create a file
424 //
425 // future enhancements can be to set some extended attributes for file
426 // systems OS/2 supports that have them (HPFS, FAT32) and security
427 // (HPFS386)
428 static const wxChar *szMktempSuffix = wxT("XXX");
429 path << dir << _T('/') << name << szMktempSuffix;
430
431 // Temporarily remove - MN
432 #ifndef __WATCOMC__
433 ::DosCreateDir(wxStringBuffer(MAX_PATH), NULL);
434 #endif
435
436 #else // !Windows, !OS/2
437 if ( dir.empty() )
438 {
439 dir = wxGetenv(_T("TMP"));
440 if ( path.empty() )
441 {
442 dir = wxGetenv(_T("TEMP"));
443 }
444
445 if ( dir.empty() )
446 {
447 // default
448 dir = _T("/tmp");
449 }
450 }
451
452 path = dir;
453
454 if ( !wxEndsWithPathSeparator(dir) &&
455 (name.empty() || !wxIsPathSeparator(name[0u])) )
456 {
457 path += _T('/');
458 }
459
460 path += name;
461
462 #ifdef HAVE_MKSTEMP
463 // scratch space for mkstemp()
464 path += _T("XXXXXX");
465
466 // can use the cast here because the length doesn't change and the string
467 // is not shared
468 if ( mkstemp((char *)path.mb_str()) == -1 )
469 {
470 // this might be not necessary as mkstemp() on most systems should have
471 // already done it but it doesn't hurt neither...
472 path.clear();
473 }
474 //else: file already created
475 #else // !HAVE_MKSTEMP
476
477 #ifdef HAVE_MKTEMP
478 // same as above
479 path += _T("XXXXXX");
480
481 if ( !mktemp((char *)path.mb_str()) )
482 {
483 path.clear();
484 }
485 #else // !HAVE_MKTEMP
486 // generate the unique file name ourselves
487 path << (unsigned int)getpid();
488
489 wxString pathTry;
490
491 static const size_t numTries = 1000;
492 for ( size_t n = 0; n < numTries; n++ )
493 {
494 // 3 hex digits is enough for numTries == 1000 < 4096
495 pathTry = path + wxString::Format(_T("%.03x"), n);
496 if ( !wxFile::Exists(pathTry) )
497 {
498 break;
499 }
500
501 pathTry.clear();
502 }
503
504 path = pathTry;
505 #endif // HAVE_MKTEMP/!HAVE_MKTEMP
506
507 if ( !path.empty() )
508 {
509 // create the file - of course, there is a race condition here, this is
510 // why we always prefer using mkstemp()...
511 wxFile file;
512 if ( !file.Open(path, wxFile::write_excl, wxS_IRUSR | wxS_IWUSR) )
513 {
514 // FIXME: If !ok here should we loop and try again with another
515 // file name? That is the standard recourse if open(O_EXCL)
516 // fails, though of course it should be protected against
517 // possible infinite looping too.
518
519 wxLogError(_("Failed to open temporary file."));
520
521 path.clear();
522 }
523 }
524 #endif // HAVE_MKSTEMP/!HAVE_MKSTEMP
525
526 #endif // Windows/!Windows
527
528 if ( path.empty() )
529 {
530 wxLogSysError(_("Failed to create a temporary file name"));
531 }
532
533 return path;
534 }
535
536 // ----------------------------------------------------------------------------
537 // directory operations
538 // ----------------------------------------------------------------------------
539
540 bool wxFileName::Mkdir( int perm, bool full )
541 {
542 return wxFileName::Mkdir( GetFullPath(), perm, full );
543 }
544
545 bool wxFileName::Mkdir( const wxString &dir, int perm, bool full )
546 {
547 if (full)
548 {
549 wxFileName filename(dir);
550 wxArrayString dirs = filename.GetDirs();
551 dirs.Add(filename.GetName());
552
553 size_t count = dirs.GetCount();
554 size_t i;
555 wxString currPath;
556 int noErrors = 0;
557 for ( i = 0; i < count; i++ )
558 {
559 currPath += dirs[i];
560
561 if (currPath.Last() == wxT(':'))
562 {
563 // Can't create a root directory so continue to next dir
564 currPath += wxFILE_SEP_PATH;
565 continue;
566 }
567
568 if (!DirExists(currPath))
569 if (!wxMkdir(currPath, perm))
570 noErrors ++;
571
572 if ( (i < (count-1)) )
573 currPath += wxFILE_SEP_PATH;
574 }
575
576 return (noErrors == 0);
577
578 }
579 else
580 return ::wxMkdir( dir, perm );
581 }
582
583 bool wxFileName::Rmdir()
584 {
585 return wxFileName::Rmdir( GetFullPath() );
586 }
587
588 bool wxFileName::Rmdir( const wxString &dir )
589 {
590 return ::wxRmdir( dir );
591 }
592
593 // ----------------------------------------------------------------------------
594 // path normalization
595 // ----------------------------------------------------------------------------
596
597 bool wxFileName::Normalize(wxPathNormalize flags,
598 const wxString& cwd,
599 wxPathFormat format)
600 {
601 // the existing path components
602 wxArrayString dirs = GetDirs();
603
604 // the path to prepend in front to make the path absolute
605 wxFileName curDir;
606
607 format = GetFormat(format);
608
609 // make the path absolute
610 if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute() )
611 {
612 if ( cwd.empty() )
613 {
614 curDir.AssignCwd(GetVolume());
615 }
616 else // cwd provided
617 {
618 curDir.AssignDir(cwd);
619 }
620
621 // the path may be not absolute because it doesn't have the volume name
622 // but in this case we shouldn't modify the directory components of it
623 // but just set the current volume
624 if ( !HasVolume() && curDir.HasVolume() )
625 {
626 SetVolume(curDir.GetVolume());
627
628 if ( IsAbsolute() )
629 {
630 // yes, it was the case - we don't need curDir then
631 curDir.Clear();
632 }
633 }
634 }
635
636 // handle ~ stuff under Unix only
637 if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) )
638 {
639 if ( !dirs.IsEmpty() )
640 {
641 wxString dir = dirs[0u];
642 if ( !dir.empty() && dir[0u] == _T('~') )
643 {
644 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
645
646 dirs.RemoveAt(0u);
647 }
648 }
649 }
650
651 // transform relative path into abs one
652 if ( curDir.IsOk() )
653 {
654 wxArrayString dirsNew = curDir.GetDirs();
655 size_t count = dirs.GetCount();
656 for ( size_t n = 0; n < count; n++ )
657 {
658 dirsNew.Add(dirs[n]);
659 }
660
661 dirs = dirsNew;
662 }
663
664 // now deal with ".", ".." and the rest
665 m_dirs.Empty();
666 size_t count = dirs.GetCount();
667 for ( size_t n = 0; n < count; n++ )
668 {
669 wxString dir = dirs[n];
670
671 if ( flags && wxPATH_NORM_DOTS )
672 {
673 if ( dir == wxT(".") )
674 {
675 // just ignore
676 continue;
677 }
678
679 if ( dir == wxT("..") )
680 {
681 if ( m_dirs.IsEmpty() )
682 {
683 wxLogError(_("The path '%s' contains too many \"..\"!"),
684 GetFullPath().c_str());
685 return FALSE;
686 }
687
688 m_dirs.Remove(m_dirs.GetCount() - 1);
689 continue;
690 }
691 }
692
693 if ( flags & wxPATH_NORM_ENV_VARS )
694 {
695 dir = wxExpandEnvVars(dir);
696 }
697
698 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
699 {
700 dir.MakeLower();
701 }
702
703 m_dirs.Add(dir);
704 }
705
706 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
707 {
708 // VZ: expand env vars here too?
709
710 m_name.MakeLower();
711 m_ext.MakeLower();
712 }
713
714 #if defined(__WIN32__)
715 if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
716 {
717 Assign(GetLongPath());
718 }
719 #endif // Win32
720
721 return TRUE;
722 }
723
724 // ----------------------------------------------------------------------------
725 // filename kind tests
726 // ----------------------------------------------------------------------------
727
728 bool wxFileName::SameAs( const wxFileName &filepath, wxPathFormat format)
729 {
730 wxFileName fn1 = *this,
731 fn2 = filepath;
732
733 // get cwd only once - small time saving
734 wxString cwd = wxGetCwd();
735 fn1.Normalize(wxPATH_NORM_ALL, cwd, format);
736 fn2.Normalize(wxPATH_NORM_ALL, cwd, format);
737
738 if ( fn1.GetFullPath() == fn2.GetFullPath() )
739 return TRUE;
740
741 // TODO: compare inodes for Unix, this works even when filenames are
742 // different but files are the same (symlinks) (VZ)
743
744 return FALSE;
745 }
746
747 /* static */
748 bool wxFileName::IsCaseSensitive( wxPathFormat format )
749 {
750 // only Unix filenames are truely case-sensitive
751 return GetFormat(format) == wxPATH_UNIX;
752 }
753
754 bool wxFileName::IsAbsolute( wxPathFormat format )
755 {
756 // if we have no path, we can't be an abs filename
757 if ( m_dirs.IsEmpty() )
758 {
759 return FALSE;
760 }
761
762 format = GetFormat(format);
763
764 if ( format == wxPATH_UNIX )
765 {
766 const wxString& str = m_dirs[0u];
767 if ( str.empty() )
768 {
769 // the path started with '/', it's an absolute one
770 return TRUE;
771 }
772
773 // the path is absolute if it starts with a path separator or
774 // with "~" or "~user"
775 wxChar ch = str[0u];
776
777 return IsPathSeparator(ch, format) || ch == _T('~');
778 }
779 else // !Unix
780 {
781 // must have the drive
782 if ( m_volume.empty() )
783 return FALSE;
784
785 switch ( format )
786 {
787 default:
788 wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
789 // fall through
790
791 case wxPATH_DOS:
792 return m_dirs[0u].empty();
793
794 case wxPATH_VMS:
795 // TODO: what is the relative path format here?
796 return TRUE;
797
798 case wxPATH_MAC:
799 return !m_dirs[0u].empty();
800 }
801 }
802 }
803
804 /* static */
805 wxString wxFileName::GetVolumeSeparator(wxPathFormat format)
806 {
807 wxString sepVol;
808
809 if ( GetFormat(format) != wxPATH_UNIX )
810 {
811 // so far it is the same for all systems which have it
812 sepVol = wxFILE_SEP_DSK;
813 }
814 //else: leave empty, no volume separators under Unix
815
816 return sepVol;
817 }
818
819 /* static */
820 wxString wxFileName::GetPathSeparators(wxPathFormat format)
821 {
822 wxString seps;
823 switch ( GetFormat(format) )
824 {
825 case wxPATH_DOS:
826 // accept both as native APIs do but put the native one first as
827 // this is the one we use in GetFullPath()
828 seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX;
829 break;
830
831 default:
832 wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
833 // fall through
834
835 case wxPATH_UNIX:
836 seps = wxFILE_SEP_PATH_UNIX;
837 break;
838
839 case wxPATH_MAC:
840 seps = wxFILE_SEP_PATH_MAC;
841 break;
842
843 case wxPATH_VMS:
844 seps = wxFILE_SEP_PATH_VMS;
845 break;
846 }
847
848 return seps;
849 }
850
851 /* static */
852 bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
853 {
854 // wxString::Find() doesn't work as expected with NUL - it will always find
855 // it, so it is almost surely a bug if this function is called with NUL arg
856 wxASSERT_MSG( ch != _T('\0'), _T("shouldn't be called with NUL") );
857
858 return GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
859 }
860
861 bool wxFileName::IsWild( wxPathFormat format )
862 {
863 // FIXME: this is probably false for Mac and this is surely wrong for most
864 // of Unix shells (think about "[...]")
865 (void)format;
866 return m_name.find_first_of(_T("*?")) != wxString::npos;
867 }
868
869 // ----------------------------------------------------------------------------
870 // path components manipulation
871 // ----------------------------------------------------------------------------
872
873 void wxFileName::AppendDir( const wxString &dir )
874 {
875 m_dirs.Add( dir );
876 }
877
878 void wxFileName::PrependDir( const wxString &dir )
879 {
880 m_dirs.Insert( dir, 0 );
881 }
882
883 void wxFileName::InsertDir( int before, const wxString &dir )
884 {
885 m_dirs.Insert( dir, before );
886 }
887
888 void wxFileName::RemoveDir( int pos )
889 {
890 m_dirs.Remove( (size_t)pos );
891 }
892
893 // ----------------------------------------------------------------------------
894 // accessors
895 // ----------------------------------------------------------------------------
896
897 void wxFileName::SetFullName(const wxString& fullname)
898 {
899 SplitPath(fullname, NULL /* no path */, &m_name, &m_ext);
900 }
901
902 wxString wxFileName::GetFullName() const
903 {
904 wxString fullname = m_name;
905 if ( !m_ext.empty() )
906 {
907 fullname << wxFILE_SEP_EXT << m_ext;
908 }
909
910 return fullname;
911 }
912
913 wxString wxFileName::GetPath( bool add_separator, wxPathFormat format ) const
914 {
915 format = GetFormat( format );
916
917 wxString ret;
918 size_t count = m_dirs.GetCount();
919 for ( size_t i = 0; i < count; i++ )
920 {
921 ret += m_dirs[i];
922 if ( add_separator || (i < count) )
923 ret += wxFILE_SEP_PATH;
924 }
925
926 return ret;
927 }
928
929 wxString wxFileName::GetFullPath( wxPathFormat format ) const
930 {
931 format = GetFormat(format);
932
933 wxString fullpath;
934
935 // first put the volume
936 if ( !m_volume.empty() )
937 {
938 // special Windows UNC paths hack, part 2: undo what we did in
939 // SplitPath() and make an UNC path if we have a drive which is not a
940 // single letter (hopefully the network shares can't be one letter only
941 // although I didn't find any authoritative docs on this)
942 if ( format == wxPATH_DOS && m_volume.length() > 1 )
943 {
944 fullpath << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << m_volume;
945 }
946 else // !UNC
947 {
948 fullpath << m_volume << GetVolumeSeparator(format);
949 }
950 }
951
952 // then concatenate all the path components using the path separator
953 size_t dirCount = m_dirs.GetCount();
954 if ( dirCount )
955 {
956 // under Mac, we must have a path separator in the beginning of the
957 // relative path - otherwise it would be parsed as an absolute one
958 if ( format == wxPATH_MAC && m_volume.empty() && !m_dirs[0].empty() )
959 {
960 fullpath += wxFILE_SEP_PATH_MAC;
961 }
962
963 wxChar chPathSep = GetPathSeparators(format)[0u];
964 if ( format == wxPATH_VMS )
965 {
966 fullpath += _T('[');
967 }
968
969 for ( size_t i = 0; i < dirCount; i++ )
970 {
971 // under VMS, we shouldn't have a leading dot
972 if ( i && (format != wxPATH_VMS || !m_dirs[i - 1].empty()) )
973 fullpath += chPathSep;
974
975 fullpath += m_dirs[i];
976 }
977
978 if ( format == wxPATH_VMS )
979 {
980 fullpath += _T(']');
981 }
982 else // !VMS
983 {
984 // separate the file name from the last directory, notice that we
985 // intentionally do it even if the name and extension are empty as
986 // this allows us to distinguish the directories from the file
987 // names (the directories have the trailing slash)
988 fullpath += chPathSep;
989 }
990 }
991
992 // finally add the file name and extension
993 fullpath += GetFullName();
994
995 return fullpath;
996 }
997
998 // Return the short form of the path (returns identity on non-Windows platforms)
999 wxString wxFileName::GetShortPath() const
1000 {
1001 #if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__)
1002 wxString path(GetFullPath());
1003 wxString pathOut;
1004 DWORD sz = ::GetShortPathName(path, NULL, 0);
1005 bool ok = sz != 0;
1006 if ( ok )
1007 {
1008 ok = ::GetShortPathName
1009 (
1010 path,
1011 pathOut.GetWriteBuf(sz),
1012 sz
1013 ) != 0;
1014 pathOut.UngetWriteBuf();
1015 }
1016 if (ok)
1017 return pathOut;
1018
1019 return path;
1020 #else
1021 return GetFullPath();
1022 #endif
1023 }
1024
1025 // Return the long form of the path (returns identity on non-Windows platforms)
1026 wxString wxFileName::GetLongPath() const
1027 {
1028 wxString pathOut,
1029 path = GetFullPath();
1030
1031 #if defined(__WIN32__) && !defined(__WXMICROWIN__)
1032 bool success = FALSE;
1033
1034 // VZ: this code was disabled, why?
1035 #if 0 // wxUSE_DYNLIB_CLASS
1036 typedef DWORD (*GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
1037
1038 static bool s_triedToLoad = FALSE;
1039
1040 if ( !s_triedToLoad )
1041 {
1042 s_triedToLoad = TRUE;
1043 wxDllType dllKernel = wxDllLoader::LoadLibrary(_T("kernel32"));
1044 if ( dllKernel )
1045 {
1046 // may succeed or fail depending on the Windows version
1047 static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
1048 #ifdef _UNICODE
1049 s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameW"));
1050 #else
1051 s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameA"));
1052 #endif
1053
1054 wxDllLoader::UnloadLibrary(dllKernel);
1055
1056 if ( s_pfnGetLongPathName )
1057 {
1058 DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0);
1059 bool ok = dwSize > 0;
1060
1061 if ( ok )
1062 {
1063 DWORD sz = (*s_pfnGetLongPathName)(path, NULL, 0);
1064 ok = sz != 0;
1065 if ( ok )
1066 {
1067 ok = (*s_pfnGetLongPathName)
1068 (
1069 path,
1070 pathOut.GetWriteBuf(sz),
1071 sz
1072 ) != 0;
1073 pathOut.UngetWriteBuf();
1074
1075 success = TRUE;
1076 }
1077 }
1078 }
1079 }
1080 }
1081 if (success)
1082 return pathOut;
1083 #endif // wxUSE_DYNLIB_CLASS
1084
1085 if (!success)
1086 {
1087 // The OS didn't support GetLongPathName, or some other error.
1088 // We need to call FindFirstFile on each component in turn.
1089
1090 WIN32_FIND_DATA findFileData;
1091 HANDLE hFind;
1092 pathOut = wxEmptyString;
1093
1094 wxArrayString dirs = GetDirs();
1095 dirs.Add(GetFullName());
1096
1097 wxString tmpPath;
1098
1099 size_t count = dirs.GetCount();
1100 for ( size_t i = 0; i < count; i++ )
1101 {
1102 // We're using pathOut to collect the long-name path, but using a
1103 // temporary for appending the last path component which may be
1104 // short-name
1105 tmpPath = pathOut + dirs[i];
1106
1107 if ( tmpPath.empty() )
1108 continue;
1109
1110 if ( tmpPath.Last() == wxT(':') )
1111 {
1112 // Can't pass a drive and root dir to FindFirstFile,
1113 // so continue to next dir
1114 tmpPath += wxFILE_SEP_PATH;
1115 pathOut = tmpPath;
1116 continue;
1117 }
1118
1119 hFind = ::FindFirstFile(tmpPath, &findFileData);
1120 if (hFind == INVALID_HANDLE_VALUE)
1121 {
1122 // Error: return immediately with the original path
1123 return path;
1124 }
1125
1126 pathOut += findFileData.cFileName;
1127 if ( (i < (count-1)) )
1128 pathOut += wxFILE_SEP_PATH;
1129
1130 ::FindClose(hFind);
1131 }
1132 }
1133 #else // !Win32
1134 pathOut = path;
1135 #endif // Win32/!Win32
1136
1137 return pathOut;
1138 }
1139
1140 wxPathFormat wxFileName::GetFormat( wxPathFormat format )
1141 {
1142 if (format == wxPATH_NATIVE)
1143 {
1144 #if defined(__WXMSW__) || defined(__WXPM__)
1145 format = wxPATH_DOS;
1146 #elif defined(__WXMAC__) && !defined(__DARWIN__)
1147 format = wxPATH_MAC;
1148 #elif defined(__VMS)
1149 format = wxPATH_VMS;
1150 #else
1151 format = wxPATH_UNIX;
1152 #endif
1153 }
1154 return format;
1155 }
1156
1157 // ----------------------------------------------------------------------------
1158 // path splitting function
1159 // ----------------------------------------------------------------------------
1160
1161 /* static */
1162 void wxFileName::SplitPath(const wxString& fullpathWithVolume,
1163 wxString *pstrVolume,
1164 wxString *pstrPath,
1165 wxString *pstrName,
1166 wxString *pstrExt,
1167 wxPathFormat format)
1168 {
1169 format = GetFormat(format);
1170
1171 wxString fullpath = fullpathWithVolume;
1172
1173 // under VMS the end of the path is ']', not the path separator used to
1174 // separate the components
1175 wxString sepPath = format == wxPATH_VMS ? wxString(_T(']'))
1176 : GetPathSeparators(format);
1177
1178 // special Windows UNC paths hack: transform \\share\path into share:path
1179 if ( format == wxPATH_DOS )
1180 {
1181 if ( fullpath.length() >= 4 &&
1182 fullpath[0u] == wxFILE_SEP_PATH_DOS &&
1183 fullpath[1u] == wxFILE_SEP_PATH_DOS )
1184 {
1185 fullpath.erase(0, 2);
1186
1187 size_t posFirstSlash = fullpath.find_first_of(sepPath);
1188 if ( posFirstSlash != wxString::npos )
1189 {
1190 fullpath[posFirstSlash] = wxFILE_SEP_DSK;
1191
1192 // UNC paths are always absolute, right? (FIXME)
1193 fullpath.insert(posFirstSlash + 1, wxFILE_SEP_PATH_DOS);
1194 }
1195 }
1196 }
1197
1198 // do we have the volume name in the beginning?
1199 wxString sepVol = GetVolumeSeparator(format);
1200 if ( !sepVol.empty() )
1201 {
1202 size_t posFirstColon = fullpath.find_first_of(sepVol);
1203 if ( posFirstColon != wxString::npos )
1204 {
1205 if ( pstrVolume )
1206 {
1207 *pstrVolume = fullpath.Left(posFirstColon);
1208 }
1209
1210 // remove the volume name and the separator from the full path
1211 fullpath.erase(0, posFirstColon + sepVol.length());
1212 }
1213 }
1214
1215 // find the positions of the last dot and last path separator in the path
1216 size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
1217 size_t posLastSlash = fullpath.find_last_of(sepPath);
1218
1219 if ( (posLastDot != wxString::npos) &&
1220 ((format == wxPATH_UNIX) || (format == wxPATH_VMS)) )
1221 {
1222 if ( (posLastDot == 0) ||
1223 (fullpath[posLastDot - 1] == sepPath[0u] ) )
1224 {
1225 // under Unix and VMS, dot may be (and commonly is) the first
1226 // character of the filename, don't treat the entire filename as
1227 // extension in this case
1228 posLastDot = wxString::npos;
1229 }
1230 }
1231
1232 // if we do have a dot and a slash, check that the dot is in the name part
1233 if ( (posLastDot != wxString::npos) &&
1234 (posLastSlash != wxString::npos) &&
1235 (posLastDot < posLastSlash) )
1236 {
1237 // the dot is part of the path, not the start of the extension
1238 posLastDot = wxString::npos;
1239 }
1240
1241 // now fill in the variables provided by user
1242 if ( pstrPath )
1243 {
1244 if ( posLastSlash == wxString::npos )
1245 {
1246 // no path at all
1247 pstrPath->Empty();
1248 }
1249 else
1250 {
1251 // take everything up to the path separator but take care to make
1252 // tha path equal to something like '/', not empty, for the files
1253 // immediately under root directory
1254 size_t len = posLastSlash;
1255 if ( !len )
1256 len++;
1257
1258 *pstrPath = fullpath.Left(len);
1259
1260 // special VMS hack: remove the initial bracket
1261 if ( format == wxPATH_VMS )
1262 {
1263 if ( (*pstrPath)[0u] == _T('[') )
1264 pstrPath->erase(0, 1);
1265 }
1266 }
1267 }
1268
1269 if ( pstrName )
1270 {
1271 // take all characters starting from the one after the last slash and
1272 // up to, but excluding, the last dot
1273 size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
1274 size_t count;
1275 if ( posLastDot == wxString::npos )
1276 {
1277 // take all until the end
1278 count = wxString::npos;
1279 }
1280 else if ( posLastSlash == wxString::npos )
1281 {
1282 count = posLastDot;
1283 }
1284 else // have both dot and slash
1285 {
1286 count = posLastDot - posLastSlash - 1;
1287 }
1288
1289 *pstrName = fullpath.Mid(nStart, count);
1290 }
1291
1292 if ( pstrExt )
1293 {
1294 if ( posLastDot == wxString::npos )
1295 {
1296 // no extension
1297 pstrExt->Empty();
1298 }
1299 else
1300 {
1301 // take everything after the dot
1302 *pstrExt = fullpath.Mid(posLastDot + 1);
1303 }
1304 }
1305 }
1306
1307 /* static */
1308 void wxFileName::SplitPath(const wxString& fullpath,
1309 wxString *path,
1310 wxString *name,
1311 wxString *ext,
1312 wxPathFormat format)
1313 {
1314 wxString volume;
1315 SplitPath(fullpath, &volume, path, name, ext, format);
1316
1317 if ( path && !volume.empty() )
1318 {
1319 path->Prepend(volume + GetVolumeSeparator(format));
1320 }
1321 }
1322
1323 // ----------------------------------------------------------------------------
1324 // time functions
1325 // ----------------------------------------------------------------------------
1326
1327 bool wxFileName::SetTimes(const wxDateTime *dtCreate,
1328 const wxDateTime *dtAccess,
1329 const wxDateTime *dtMod)
1330 {
1331 #if defined(__UNIX_LIKE__)
1332 if ( !dtAccess && !dtMod )
1333 {
1334 // can't modify the creation time anyhow, don't try
1335 return TRUE;
1336 }
1337
1338 // if dtAccess or dtMod is not specified, use the other one (which must be
1339 // non NULL because of the test above) for both times
1340 utimbuf utm;
1341 utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
1342 utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
1343 if ( utime(GetFullPath(), &utm) == 0 )
1344 {
1345 return TRUE;
1346 }
1347 #elif defined(__WIN32__)
1348 wxFileHandle fh(GetFullPath());
1349 if ( fh.IsOk() )
1350 {
1351 FILETIME ftAccess, ftCreate, ftWrite;
1352
1353 if ( dtCreate )
1354 ConvertWxToFileTime(&ftCreate, *dtCreate);
1355 if ( dtAccess )
1356 ConvertWxToFileTime(&ftAccess, *dtAccess);
1357 if ( dtMod )
1358 ConvertWxToFileTime(&ftWrite, *dtMod);
1359
1360 if ( ::SetFileTime(fh,
1361 dtCreate ? &ftCreate : NULL,
1362 dtAccess ? &ftAccess : NULL,
1363 dtMod ? &ftWrite : NULL) )
1364 {
1365 return TRUE;
1366 }
1367 }
1368 #else // other platform
1369 #endif // platforms
1370
1371 wxLogSysError(_("Failed to modify file times for '%s'"),
1372 GetFullPath().c_str());
1373
1374 return FALSE;
1375 }
1376
1377 bool wxFileName::Touch()
1378 {
1379 #if defined(__UNIX_LIKE__)
1380 // under Unix touching file is simple: just pass NULL to utime()
1381 if ( utime(GetFullPath(), NULL) == 0 )
1382 {
1383 return TRUE;
1384 }
1385
1386 wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str());
1387
1388 return FALSE;
1389 #else // other platform
1390 wxDateTime dtNow = wxDateTime::Now();
1391
1392 return SetTimes(NULL /* don't change create time */, &dtNow, &dtNow);
1393 #endif // platforms
1394 }
1395
1396 bool wxFileName::GetTimes(wxDateTime *dtAccess,
1397 wxDateTime *dtMod,
1398 wxDateTime *dtChange) const
1399 {
1400 #if defined(__UNIX_LIKE__)
1401 wxStructStat stBuf;
1402 if ( wxStat(GetFullPath(), &stBuf) == 0 )
1403 {
1404 if ( dtAccess )
1405 dtAccess->Set(stBuf.st_atime);
1406 if ( dtMod )
1407 dtMod->Set(stBuf.st_mtime);
1408 if ( dtChange )
1409 dtChange->Set(stBuf.st_ctime);
1410
1411 return TRUE;
1412 }
1413 #elif defined(__WXMAC__)
1414 wxStructStat stBuf;
1415 if ( wxStat(GetFullPath(), &stBuf) == 0 )
1416 {
1417 if ( dtAccess )
1418 dtAccess->Set(stBuf.st_atime);
1419 if ( dtMod )
1420 dtMod->Set(stBuf.st_mtime);
1421 if ( dtChange )
1422 dtChange->Set(stBuf.st_ctime);
1423
1424 return TRUE;
1425 }
1426 #elif defined(__WIN32__)
1427 wxFileHandle fh(GetFullPath());
1428 if ( fh.IsOk() )
1429 {
1430 FILETIME ftAccess, ftCreate, ftWrite;
1431
1432 if ( ::GetFileTime(fh,
1433 dtMod ? &ftCreate : NULL,
1434 dtAccess ? &ftAccess : NULL,
1435 dtChange ? &ftWrite : NULL) )
1436 {
1437 if ( dtMod )
1438 ConvertFileTimeToWx(dtMod, ftCreate);
1439 if ( dtAccess )
1440 ConvertFileTimeToWx(dtAccess, ftAccess);
1441 if ( dtChange )
1442 ConvertFileTimeToWx(dtChange, ftWrite);
1443
1444 return TRUE;
1445 }
1446 }
1447 #else // other platform
1448 #endif // platforms
1449
1450 wxLogSysError(_("Failed to retrieve file times for '%s'"),
1451 GetFullPath().c_str());
1452
1453 return FALSE;
1454 }
1455