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