]> git.saurik.com Git - wxWidgets.git/blob - src/common/filename.cpp
1. GetCwd() now has the volume argument, Normalize() works correctly for the
[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 Assign(dir, _T(""), format);
261 }
262
263 void wxFileName::Clear()
264 {
265 m_dirs.Clear();
266
267 m_volume =
268 m_name =
269 m_ext = wxEmptyString;
270 }
271
272 /* static */
273 wxFileName wxFileName::FileName(const wxString& file)
274 {
275 return wxFileName(file);
276 }
277
278 /* static */
279 wxFileName wxFileName::DirName(const wxString& dir)
280 {
281 wxFileName fn;
282 fn.AssignDir(dir);
283 return fn;
284 }
285
286 // ----------------------------------------------------------------------------
287 // existence tests
288 // ----------------------------------------------------------------------------
289
290 bool wxFileName::FileExists()
291 {
292 return wxFileName::FileExists( GetFullPath() );
293 }
294
295 bool wxFileName::FileExists( const wxString &file )
296 {
297 return ::wxFileExists( file );
298 }
299
300 bool wxFileName::DirExists()
301 {
302 return wxFileName::DirExists( GetFullPath() );
303 }
304
305 bool wxFileName::DirExists( const wxString &dir )
306 {
307 return ::wxDirExists( dir );
308 }
309
310 // ----------------------------------------------------------------------------
311 // CWD and HOME stuff
312 // ----------------------------------------------------------------------------
313
314 void wxFileName::AssignCwd(const wxString& volume)
315 {
316 AssignDir(wxFileName::GetCwd(volume));
317 }
318
319 /* static */
320 wxString wxFileName::GetCwd(const wxString& volume)
321 {
322 // if we have the volume, we must get the current directory on this drive
323 // and to do this we have to chdir to this volume - at least under Windows,
324 // I don't know how to get the current drive on another volume elsewhere
325 // (TODO)
326 wxString cwdOld;
327 if ( !volume.empty() )
328 {
329 cwdOld = wxGetCwd();
330 SetCwd(volume + GetVolumeSeparator());
331 }
332
333 wxString cwd = ::wxGetCwd();
334
335 if ( !volume.empty() )
336 {
337 SetCwd(cwdOld);
338 }
339
340 return cwd;
341 }
342
343 bool wxFileName::SetCwd()
344 {
345 return wxFileName::SetCwd( GetFullPath() );
346 }
347
348 bool wxFileName::SetCwd( const wxString &cwd )
349 {
350 return ::wxSetWorkingDirectory( cwd );
351 }
352
353 void wxFileName::AssignHomeDir()
354 {
355 AssignDir(wxFileName::GetHomeDir());
356 }
357
358 wxString wxFileName::GetHomeDir()
359 {
360 return ::wxGetHomeDir();
361 }
362
363 void wxFileName::AssignTempFileName( const wxString &prefix )
364 {
365 wxString fullname;
366 if ( wxGetTempFileName(prefix, fullname) )
367 {
368 Assign(fullname);
369 }
370 else // error
371 {
372 Clear();
373 }
374 }
375
376 // ----------------------------------------------------------------------------
377 // directory operations
378 // ----------------------------------------------------------------------------
379
380 bool wxFileName::Mkdir( int perm, bool full )
381 {
382 return wxFileName::Mkdir( GetFullPath(), perm, full );
383 }
384
385 bool wxFileName::Mkdir( const wxString &dir, int perm, bool full )
386 {
387 if (full)
388 {
389 wxFileName filename(dir);
390 wxArrayString dirs = filename.GetDirs();
391 dirs.Add(filename.GetName());
392
393 size_t count = dirs.GetCount();
394 size_t i;
395 wxString currPath;
396 int noErrors = 0;
397 for ( i = 0; i < count; i++ )
398 {
399 currPath += dirs[i];
400
401 if (currPath.Last() == wxT(':'))
402 {
403 // Can't create a root directory so continue to next dir
404 currPath += wxFILE_SEP_PATH;
405 continue;
406 }
407
408 if (!DirExists(currPath))
409 if (!wxMkdir(currPath, perm))
410 noErrors ++;
411
412 if ( (i < (count-1)) )
413 currPath += wxFILE_SEP_PATH;
414 }
415
416 return (noErrors == 0);
417
418 }
419 else
420 return ::wxMkdir( dir, perm );
421 }
422
423 bool wxFileName::Rmdir()
424 {
425 return wxFileName::Rmdir( GetFullPath() );
426 }
427
428 bool wxFileName::Rmdir( const wxString &dir )
429 {
430 return ::wxRmdir( dir );
431 }
432
433 // ----------------------------------------------------------------------------
434 // path normalization
435 // ----------------------------------------------------------------------------
436
437 bool wxFileName::Normalize(wxPathNormalize flags,
438 const wxString& cwd,
439 wxPathFormat format)
440 {
441 // the existing path components
442 wxArrayString dirs = GetDirs();
443
444 // the path to prepend in front to make the path absolute
445 wxFileName curDir;
446
447 format = GetFormat(format);
448
449 // make the path absolute
450 if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute() )
451 {
452 if ( cwd.empty() )
453 {
454 curDir.AssignCwd(GetVolume());
455 }
456 else // cwd provided
457 {
458 curDir.AssignDir(cwd);
459 }
460
461 // the path may be not absolute because it doesn't have the volume name
462 // but in this case we shouldn't modify the directory components of it
463 // but just set the current volume
464 if ( !HasVolume() && curDir.HasVolume() )
465 {
466 SetVolume(curDir.GetVolume());
467
468 if ( IsAbsolute() )
469 {
470 // yes, it was the case - we don't need curDir then
471 curDir.Clear();
472 }
473 }
474 }
475
476 // handle ~ stuff under Unix only
477 if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) )
478 {
479 if ( !dirs.IsEmpty() )
480 {
481 wxString dir = dirs[0u];
482 if ( !dir.empty() && dir[0u] == _T('~') )
483 {
484 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
485
486 dirs.RemoveAt(0u);
487 }
488 }
489 }
490
491 // transform relative path into abs one
492 if ( curDir.IsOk() )
493 {
494 wxArrayString dirsNew = curDir.GetDirs();
495 size_t count = dirs.GetCount();
496 for ( size_t n = 0; n < count; n++ )
497 {
498 dirsNew.Add(dirs[n]);
499 }
500
501 dirs = dirsNew;
502 }
503
504 // now deal with ".", ".." and the rest
505 m_dirs.Empty();
506 size_t count = dirs.GetCount();
507 for ( size_t n = 0; n < count; n++ )
508 {
509 wxString dir = dirs[n];
510
511 if ( flags && wxPATH_NORM_DOTS )
512 {
513 if ( dir == wxT(".") )
514 {
515 // just ignore
516 continue;
517 }
518
519 if ( dir == wxT("..") )
520 {
521 if ( m_dirs.IsEmpty() )
522 {
523 wxLogError(_("The path '%s' contains too many \"..\"!"),
524 GetFullPath().c_str());
525 return FALSE;
526 }
527
528 m_dirs.Remove(m_dirs.GetCount() - 1);
529 continue;
530 }
531 }
532
533 if ( flags & wxPATH_NORM_ENV_VARS )
534 {
535 dir = wxExpandEnvVars(dir);
536 }
537
538 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
539 {
540 dir.MakeLower();
541 }
542
543 m_dirs.Add(dir);
544 }
545
546 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
547 {
548 // VZ: expand env vars here too?
549
550 m_name.MakeLower();
551 m_ext.MakeLower();
552 }
553
554 #if defined(__WIN32__)
555 if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
556 {
557 Assign(GetLongPath());
558 }
559 #endif // Win32
560
561 return TRUE;
562 }
563
564 // ----------------------------------------------------------------------------
565 // filename kind tests
566 // ----------------------------------------------------------------------------
567
568 bool wxFileName::SameAs( const wxFileName &filepath, wxPathFormat format)
569 {
570 wxFileName fn1 = *this,
571 fn2 = filepath;
572
573 // get cwd only once - small time saving
574 wxString cwd = wxGetCwd();
575 fn1.Normalize(wxPATH_NORM_ALL, cwd, format);
576 fn2.Normalize(wxPATH_NORM_ALL, cwd, format);
577
578 if ( fn1.GetFullPath() == fn2.GetFullPath() )
579 return TRUE;
580
581 // TODO: compare inodes for Unix, this works even when filenames are
582 // different but files are the same (symlinks) (VZ)
583
584 return FALSE;
585 }
586
587 /* static */
588 bool wxFileName::IsCaseSensitive( wxPathFormat format )
589 {
590 // only Unix filenames are truely case-sensitive
591 return GetFormat(format) == wxPATH_UNIX;
592 }
593
594 bool wxFileName::IsAbsolute( wxPathFormat format )
595 {
596 // if we have no path, we can't be an abs filename
597 if ( m_dirs.IsEmpty() )
598 {
599 return FALSE;
600 }
601
602 format = GetFormat(format);
603
604 if ( format == wxPATH_UNIX )
605 {
606 const wxString& str = m_dirs[0u];
607 if ( str.empty() )
608 {
609 // the path started with '/', it's an absolute one
610 return TRUE;
611 }
612
613 // the path is absolute if it starts with a path separator or
614 // with "~" or "~user"
615 wxChar ch = str[0u];
616
617 return IsPathSeparator(ch, format) || ch == _T('~');
618 }
619 else // !Unix
620 {
621 // must have the drive
622 if ( m_volume.empty() )
623 return FALSE;
624
625 switch ( format )
626 {
627 default:
628 wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
629 // fall through
630
631 case wxPATH_DOS:
632 return m_dirs[0u].empty();
633
634 case wxPATH_VMS:
635 // TODO: what is the relative path format here?
636 return TRUE;
637
638 case wxPATH_MAC:
639 return !m_dirs[0u].empty();
640 }
641 }
642 }
643
644 /* static */
645 wxString wxFileName::GetVolumeSeparator(wxPathFormat format)
646 {
647 wxString sepVol;
648
649 if ( GetFormat(format) != wxPATH_UNIX )
650 {
651 // so far it is the same for all systems which have it
652 sepVol = wxFILE_SEP_DSK;
653 }
654 //else: leave empty, no volume separators under Unix
655
656 return sepVol;
657 }
658
659 /* static */
660 wxString wxFileName::GetPathSeparators(wxPathFormat format)
661 {
662 wxString seps;
663 switch ( GetFormat(format) )
664 {
665 case wxPATH_DOS:
666 // accept both as native APIs do but put the native one first as
667 // this is the one we use in GetFullPath()
668 seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX;
669 break;
670
671 default:
672 wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
673 // fall through
674
675 case wxPATH_UNIX:
676 seps = wxFILE_SEP_PATH_UNIX;
677 break;
678
679 case wxPATH_MAC:
680 seps = wxFILE_SEP_PATH_MAC;
681 break;
682
683 case wxPATH_VMS:
684 seps = wxFILE_SEP_PATH_VMS;
685 break;
686 }
687
688 return seps;
689 }
690
691 /* static */
692 bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
693 {
694 // wxString::Find() doesn't work as expected with NUL - it will always find
695 // it, so it is almost surely a bug if this function is called with NUL arg
696 wxASSERT_MSG( ch != _T('\0'), _T("shouldn't be called with NUL") );
697
698 return GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
699 }
700
701 bool wxFileName::IsWild( wxPathFormat format )
702 {
703 // FIXME: this is probably false for Mac and this is surely wrong for most
704 // of Unix shells (think about "[...]")
705 (void)format;
706 return m_name.find_first_of(_T("*?")) != wxString::npos;
707 }
708
709 // ----------------------------------------------------------------------------
710 // path components manipulation
711 // ----------------------------------------------------------------------------
712
713 void wxFileName::AppendDir( const wxString &dir )
714 {
715 m_dirs.Add( dir );
716 }
717
718 void wxFileName::PrependDir( const wxString &dir )
719 {
720 m_dirs.Insert( dir, 0 );
721 }
722
723 void wxFileName::InsertDir( int before, const wxString &dir )
724 {
725 m_dirs.Insert( dir, before );
726 }
727
728 void wxFileName::RemoveDir( int pos )
729 {
730 m_dirs.Remove( (size_t)pos );
731 }
732
733 // ----------------------------------------------------------------------------
734 // accessors
735 // ----------------------------------------------------------------------------
736
737 void wxFileName::SetFullName(const wxString& fullname)
738 {
739 SplitPath(fullname, NULL /* no path */, &m_name, &m_ext);
740 }
741
742 wxString wxFileName::GetFullName() const
743 {
744 wxString fullname = m_name;
745 if ( !m_ext.empty() )
746 {
747 fullname << wxFILE_SEP_EXT << m_ext;
748 }
749
750 return fullname;
751 }
752
753 wxString wxFileName::GetPath( bool add_separator, wxPathFormat format ) const
754 {
755 format = GetFormat( format );
756
757 wxString ret;
758 size_t count = m_dirs.GetCount();
759 for ( size_t i = 0; i < count; i++ )
760 {
761 ret += m_dirs[i];
762 if ( add_separator || (i < count) )
763 ret += wxFILE_SEP_PATH;
764 }
765
766 return ret;
767 }
768
769 wxString wxFileName::GetFullPath( wxPathFormat format ) const
770 {
771 format = GetFormat(format);
772
773 wxString fullpath;
774
775 // first put the volume
776 if ( !m_volume.empty() )
777 {
778 // special Windows UNC paths hack, part 2: undo what we did in
779 // SplitPath() and make an UNC path if we have a drive which is not a
780 // single letter (hopefully the network shares can't be one letter only
781 // although I didn't find any authoritative docs on this)
782 if ( format == wxPATH_DOS && m_volume.length() > 1 )
783 {
784 fullpath << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << m_volume;
785 }
786 else // !UNC
787 {
788 fullpath << m_volume << GetVolumeSeparator(format);
789 }
790 }
791
792 // then concatenate all the path components using the path separator
793 size_t dirCount = m_dirs.GetCount();
794 if ( dirCount )
795 {
796 // under Mac, we must have a path separator in the beginning of the
797 // relative path - otherwise it would be parsed as an absolute one
798 if ( format == wxPATH_MAC && m_volume.empty() && !m_dirs[0].empty() )
799 {
800 fullpath += wxFILE_SEP_PATH_MAC;
801 }
802
803 wxChar chPathSep = GetPathSeparators(format)[0u];
804 if ( format == wxPATH_VMS )
805 {
806 fullpath += _T('[');
807 }
808
809 for ( size_t i = 0; i < dirCount; i++ )
810 {
811 // under VMS, we shouldn't have a leading dot
812 if ( i && (format != wxPATH_VMS || !m_dirs[i - 1].empty()) )
813 fullpath += chPathSep;
814
815 fullpath += m_dirs[i];
816 }
817
818 if ( format == wxPATH_VMS )
819 {
820 fullpath += _T(']');
821 }
822 else // !VMS
823 {
824 // separate the file name from the last directory, notice that we
825 // intentionally do it even if the name and extension are empty as
826 // this allows us to distinguish the directories from the file
827 // names (the directories have the trailing slash)
828 fullpath += chPathSep;
829 }
830 }
831
832 // finally add the file name and extension
833 fullpath += GetFullName();
834
835 return fullpath;
836 }
837
838 // Return the short form of the path (returns identity on non-Windows platforms)
839 wxString wxFileName::GetShortPath() const
840 {
841 #if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__)
842 wxString path(GetFullPath());
843 wxString pathOut;
844 DWORD sz = ::GetShortPathName(path, NULL, 0);
845 bool ok = sz != 0;
846 if ( ok )
847 {
848 ok = ::GetShortPathName
849 (
850 path,
851 pathOut.GetWriteBuf(sz),
852 sz
853 ) != 0;
854 pathOut.UngetWriteBuf();
855 }
856 if (ok)
857 return pathOut;
858
859 return path;
860 #else
861 return GetFullPath();
862 #endif
863 }
864
865 // Return the long form of the path (returns identity on non-Windows platforms)
866 wxString wxFileName::GetLongPath() const
867 {
868 wxString pathOut,
869 path = GetFullPath();
870
871 #if defined(__WIN32__) && !defined(__WXMICROWIN__)
872 bool success = FALSE;
873
874 // VZ: this code was disabled, why?
875 #if 0 // wxUSE_DYNLIB_CLASS
876 typedef DWORD (*GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
877
878 static bool s_triedToLoad = FALSE;
879
880 if ( !s_triedToLoad )
881 {
882 s_triedToLoad = TRUE;
883 wxDllType dllKernel = wxDllLoader::LoadLibrary(_T("kernel32"));
884 if ( dllKernel )
885 {
886 // may succeed or fail depending on the Windows version
887 static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
888 #ifdef _UNICODE
889 s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameW"));
890 #else
891 s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameA"));
892 #endif
893
894 wxDllLoader::UnloadLibrary(dllKernel);
895
896 if ( s_pfnGetLongPathName )
897 {
898 DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0);
899 bool ok = dwSize > 0;
900
901 if ( ok )
902 {
903 DWORD sz = (*s_pfnGetLongPathName)(path, NULL, 0);
904 ok = sz != 0;
905 if ( ok )
906 {
907 ok = (*s_pfnGetLongPathName)
908 (
909 path,
910 pathOut.GetWriteBuf(sz),
911 sz
912 ) != 0;
913 pathOut.UngetWriteBuf();
914
915 success = TRUE;
916 }
917 }
918 }
919 }
920 }
921 if (success)
922 return pathOut;
923 #endif // wxUSE_DYNLIB_CLASS
924
925 if (!success)
926 {
927 // The OS didn't support GetLongPathName, or some other error.
928 // We need to call FindFirstFile on each component in turn.
929
930 WIN32_FIND_DATA findFileData;
931 HANDLE hFind;
932 pathOut = wxEmptyString;
933
934 wxArrayString dirs = GetDirs();
935 dirs.Add(GetFullName());
936
937 wxString tmpPath;
938
939 size_t count = dirs.GetCount();
940 for ( size_t i = 0; i < count; i++ )
941 {
942 // We're using pathOut to collect the long-name path, but using a
943 // temporary for appending the last path component which may be
944 // short-name
945 tmpPath = pathOut + dirs[i];
946
947 if ( tmpPath.empty() )
948 continue;
949
950 if ( tmpPath.Last() == wxT(':') )
951 {
952 // Can't pass a drive and root dir to FindFirstFile,
953 // so continue to next dir
954 tmpPath += wxFILE_SEP_PATH;
955 pathOut = tmpPath;
956 continue;
957 }
958
959 hFind = ::FindFirstFile(tmpPath, &findFileData);
960 if (hFind == INVALID_HANDLE_VALUE)
961 {
962 // Error: return immediately with the original path
963 return path;
964 }
965
966 pathOut += findFileData.cFileName;
967 if ( (i < (count-1)) )
968 pathOut += wxFILE_SEP_PATH;
969
970 ::FindClose(hFind);
971 }
972 }
973 #else // !Win32
974 pathOut = path;
975 #endif // Win32/!Win32
976
977 return pathOut;
978 }
979
980 wxPathFormat wxFileName::GetFormat( wxPathFormat format )
981 {
982 if (format == wxPATH_NATIVE)
983 {
984 #if defined(__WXMSW__) || defined(__WXPM__)
985 format = wxPATH_DOS;
986 #elif defined(__WXMAC__) && !defined(__DARWIN__)
987 format = wxPATH_MAC;
988 #elif defined(__VMS)
989 format = wxPATH_VMS;
990 #else
991 format = wxPATH_UNIX;
992 #endif
993 }
994 return format;
995 }
996
997 // ----------------------------------------------------------------------------
998 // path splitting function
999 // ----------------------------------------------------------------------------
1000
1001 /* static */
1002 void wxFileName::SplitPath(const wxString& fullpathWithVolume,
1003 wxString *pstrVolume,
1004 wxString *pstrPath,
1005 wxString *pstrName,
1006 wxString *pstrExt,
1007 wxPathFormat format)
1008 {
1009 format = GetFormat(format);
1010
1011 wxString fullpath = fullpathWithVolume;
1012
1013 // under VMS the end of the path is ']', not the path separator used to
1014 // separate the components
1015 wxString sepPath = format == wxPATH_VMS ? _T(']')
1016 : GetPathSeparators(format);
1017
1018 // special Windows UNC paths hack: transform \\share\path into share:path
1019 if ( format == wxPATH_DOS )
1020 {
1021 if ( fullpath.length() >= 4 &&
1022 fullpath[0u] == wxFILE_SEP_PATH_DOS &&
1023 fullpath[1u] == wxFILE_SEP_PATH_DOS )
1024 {
1025 fullpath.erase(0, 2);
1026
1027 size_t posFirstSlash = fullpath.find_first_of(sepPath);
1028 if ( posFirstSlash != wxString::npos )
1029 {
1030 fullpath[posFirstSlash] = wxFILE_SEP_DSK;
1031
1032 // UNC paths are always absolute, right? (FIXME)
1033 fullpath.insert(posFirstSlash + 1, wxFILE_SEP_PATH_DOS);
1034 }
1035 }
1036 }
1037
1038 // do we have the volume name in the beginning?
1039 wxString sepVol = GetVolumeSeparator(format);
1040 if ( !sepVol.empty() )
1041 {
1042 size_t posFirstColon = fullpath.find_first_of(sepVol);
1043 if ( posFirstColon != wxString::npos )
1044 {
1045 if ( pstrVolume )
1046 {
1047 *pstrVolume = fullpath.Left(posFirstColon);
1048 }
1049
1050 // remove the volume name and the separator from the full path
1051 fullpath.erase(0, posFirstColon + sepVol.length());
1052 }
1053 }
1054
1055 // find the positions of the last dot and last path separator in the path
1056 size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
1057 size_t posLastSlash = fullpath.find_last_of(sepPath);
1058
1059 if ( (posLastDot != wxString::npos) &&
1060 ((format == wxPATH_UNIX) || (format == wxPATH_VMS)) )
1061 {
1062 if ( (posLastDot == 0) ||
1063 (fullpath[posLastDot - 1] == sepPath[0u] ) )
1064 {
1065 // under Unix and VMS, dot may be (and commonly is) the first
1066 // character of the filename, don't treat the entire filename as
1067 // extension in this case
1068 posLastDot = wxString::npos;
1069 }
1070 }
1071
1072 // if we do have a dot and a slash, check that the dot is in the name part
1073 if ( (posLastDot != wxString::npos) &&
1074 (posLastSlash != wxString::npos) &&
1075 (posLastDot < posLastSlash) )
1076 {
1077 // the dot is part of the path, not the start of the extension
1078 posLastDot = wxString::npos;
1079 }
1080
1081 // now fill in the variables provided by user
1082 if ( pstrPath )
1083 {
1084 if ( posLastSlash == wxString::npos )
1085 {
1086 // no path at all
1087 pstrPath->Empty();
1088 }
1089 else
1090 {
1091 // take everything up to the path separator but take care to make
1092 // tha path equal to something like '/', not empty, for the files
1093 // immediately under root directory
1094 size_t len = posLastSlash;
1095 if ( !len )
1096 len++;
1097
1098 *pstrPath = fullpath.Left(len);
1099
1100 // special VMS hack: remove the initial bracket
1101 if ( format == wxPATH_VMS )
1102 {
1103 if ( (*pstrPath)[0u] == _T('[') )
1104 pstrPath->erase(0, 1);
1105 }
1106 }
1107 }
1108
1109 if ( pstrName )
1110 {
1111 // take all characters starting from the one after the last slash and
1112 // up to, but excluding, the last dot
1113 size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
1114 size_t count;
1115 if ( posLastDot == wxString::npos )
1116 {
1117 // take all until the end
1118 count = wxString::npos;
1119 }
1120 else if ( posLastSlash == wxString::npos )
1121 {
1122 count = posLastDot;
1123 }
1124 else // have both dot and slash
1125 {
1126 count = posLastDot - posLastSlash - 1;
1127 }
1128
1129 *pstrName = fullpath.Mid(nStart, count);
1130 }
1131
1132 if ( pstrExt )
1133 {
1134 if ( posLastDot == wxString::npos )
1135 {
1136 // no extension
1137 pstrExt->Empty();
1138 }
1139 else
1140 {
1141 // take everything after the dot
1142 *pstrExt = fullpath.Mid(posLastDot + 1);
1143 }
1144 }
1145 }
1146
1147 /* static */
1148 void wxFileName::SplitPath(const wxString& fullpath,
1149 wxString *path,
1150 wxString *name,
1151 wxString *ext,
1152 wxPathFormat format)
1153 {
1154 wxString volume;
1155 SplitPath(fullpath, &volume, path, name, ext, format);
1156
1157 if ( path && !volume.empty() )
1158 {
1159 path->Prepend(volume + GetVolumeSeparator(format));
1160 }
1161 }
1162
1163 // ----------------------------------------------------------------------------
1164 // time functions
1165 // ----------------------------------------------------------------------------
1166
1167 bool wxFileName::SetTimes(const wxDateTime *dtCreate,
1168 const wxDateTime *dtAccess,
1169 const wxDateTime *dtMod)
1170 {
1171 #if defined(__UNIX_LIKE__)
1172 if ( !dtAccess && !dtMod )
1173 {
1174 // can't modify the creation time anyhow, don't try
1175 return TRUE;
1176 }
1177
1178 // if dtAccess or dtMod is not specified, use the other one (which must be
1179 // non NULL because of the test above) for both times
1180 utimbuf utm;
1181 utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
1182 utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
1183 if ( utime(GetFullPath(), &utm) == 0 )
1184 {
1185 return TRUE;
1186 }
1187 #elif defined(__WIN32__)
1188 wxFileHandle fh(GetFullPath());
1189 if ( fh.IsOk() )
1190 {
1191 FILETIME ftAccess, ftCreate, ftWrite;
1192
1193 if ( dtCreate )
1194 ConvertWxToFileTime(&ftCreate, *dtCreate);
1195 if ( dtAccess )
1196 ConvertWxToFileTime(&ftAccess, *dtAccess);
1197 if ( dtMod )
1198 ConvertWxToFileTime(&ftWrite, *dtMod);
1199
1200 if ( ::SetFileTime(fh,
1201 dtCreate ? &ftCreate : NULL,
1202 dtAccess ? &ftAccess : NULL,
1203 dtMod ? &ftWrite : NULL) )
1204 {
1205 return TRUE;
1206 }
1207 }
1208 #else // other platform
1209 #endif // platforms
1210
1211 wxLogSysError(_("Failed to modify file times for '%s'"),
1212 GetFullPath().c_str());
1213
1214 return FALSE;
1215 }
1216
1217 bool wxFileName::Touch()
1218 {
1219 #if defined(__UNIX_LIKE__)
1220 // under Unix touching file is simple: just pass NULL to utime()
1221 if ( utime(GetFullPath(), NULL) == 0 )
1222 {
1223 return TRUE;
1224 }
1225
1226 wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str());
1227
1228 return FALSE;
1229 #else // other platform
1230 wxDateTime dtNow = wxDateTime::Now();
1231
1232 return SetTimes(NULL /* don't change create time */, &dtNow, &dtNow);
1233 #endif // platforms
1234 }
1235
1236 bool wxFileName::GetTimes(wxDateTime *dtAccess,
1237 wxDateTime *dtMod,
1238 wxDateTime *dtChange) const
1239 {
1240 #if defined(__UNIX_LIKE__)
1241 wxStructStat stBuf;
1242 if ( wxStat(GetFullPath(), &stBuf) == 0 )
1243 {
1244 if ( dtAccess )
1245 dtAccess->Set(stBuf.st_atime);
1246 if ( dtMod )
1247 dtMod->Set(stBuf.st_mtime);
1248 if ( dtChange )
1249 dtChange->Set(stBuf.st_ctime);
1250
1251 return TRUE;
1252 }
1253 #elif defined(__WXMAC__)
1254 wxStructStat stBuf;
1255 if ( wxStat(GetFullPath(), &stBuf) == 0 )
1256 {
1257 if ( dtAccess )
1258 dtAccess->Set(stBuf.st_atime);
1259 if ( dtMod )
1260 dtMod->Set(stBuf.st_mtime);
1261 if ( dtChange )
1262 dtChange->Set(stBuf.st_ctime);
1263
1264 return TRUE;
1265 }
1266 #elif defined(__WIN32__)
1267 wxFileHandle fh(GetFullPath());
1268 if ( fh.IsOk() )
1269 {
1270 FILETIME ftAccess, ftCreate, ftWrite;
1271
1272 if ( ::GetFileTime(fh,
1273 dtMod ? &ftCreate : NULL,
1274 dtAccess ? &ftAccess : NULL,
1275 dtChange ? &ftWrite : NULL) )
1276 {
1277 if ( dtMod )
1278 ConvertFileTimeToWx(dtMod, ftCreate);
1279 if ( dtAccess )
1280 ConvertFileTimeToWx(dtAccess, ftAccess);
1281 if ( dtChange )
1282 ConvertFileTimeToWx(dtChange, ftWrite);
1283
1284 return TRUE;
1285 }
1286 }
1287 #else // other platform
1288 #endif // platforms
1289
1290 wxLogSysError(_("Failed to retrieve file times for '%s'"),
1291 GetFullPath().c_str());
1292
1293 return FALSE;
1294 }
1295