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