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