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