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