]> git.saurik.com Git - wxWidgets.git/blame - src/common/filename.cpp
Implement and document wxDataViewTreeCtrl::IsContainer(), use it in the sample to...
[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
65571936 9// Licence: wxWindows licence
df5ddbca
RR
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.
ba623ba2 50 [ start of directory specification
04c943b1
VZ
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// For compilers that support precompilation, includes "wx.h".
64#include "wx/wxprec.h"
65
66#ifdef __BORLANDC__
0b9ab0bd 67#pragma hdrstop
df5ddbca
RR
68#endif
69
70#ifndef WX_PRECOMP
57bd4c60
WS
71 #ifdef __WXMSW__
72 #include "wx/msw/wrapwin.h" // For GetShort/LongPathName
73 #endif
ad9835c9
WS
74 #include "wx/dynarray.h"
75 #include "wx/intl.h"
76 #include "wx/log.h"
de6185e2 77 #include "wx/utils.h"
0bf751e7 78 #include "wx/crt.h"
df5ddbca
RR
79#endif
80
81#include "wx/filename.h"
b70a2866 82#include "wx/private/filename.h"
df5ddbca 83#include "wx/tokenzr.h"
844f90fb 84#include "wx/config.h" // for wxExpandEnvVars
35a6691a 85#include "wx/dynlib.h"
110c5094 86#include "wx/dir.h"
df5ddbca 87
57bd4c60
WS
88#if defined(__WIN32__) && defined(__MINGW32__)
89 #include "wx/msw/gccpriv.h"
951cd180
VZ
90#endif
91
1c193821
JS
92#ifdef __WXWINCE__
93#include "wx/msw/private.h"
94#endif
95
76a5e5d2 96#if defined(__WXMAC__)
c933e267 97 #include "wx/osx/private.h" // includes mac headers
76a5e5d2
SC
98#endif
99
951cd180
VZ
100// utime() is POSIX so should normally be available on all Unices
101#ifdef __UNIX_LIKE__
0b9ab0bd
RL
102#include <sys/types.h>
103#include <utime.h>
104#include <sys/stat.h>
105#include <unistd.h>
9e9b65c1
JS
106#endif
107
444d61ba
VS
108#ifdef __DJGPP__
109#include <unistd.h>
110#endif
111
01d981ec 112#ifdef __MWERKS__
31907d03
SC
113#ifdef __MACH__
114#include <sys/types.h>
115#include <utime.h>
116#include <sys/stat.h>
117#include <unistd.h>
118#else
0b9ab0bd
RL
119#include <stat.h>
120#include <unistd.h>
121#include <unix.h>
01d981ec 122#endif
31907d03 123#endif
01d981ec 124
d9f54bb0 125#ifdef __WATCOMC__
0b9ab0bd
RL
126#include <io.h>
127#include <sys/utime.h>
128#include <sys/stat.h>
d9f54bb0
VS
129#endif
130
24eb81cb
DW
131#ifdef __VISAGECPP__
132#ifndef MAX_PATH
133#define MAX_PATH 256
134#endif
135#endif
136
01c9a906 137#ifdef __EMX__
5d66debf 138#include <os2.h>
01c9a906
SN
139#define MAX_PATH _MAX_PATH
140#endif
141
23b8a262 142
bd08f2f7 143#if wxUSE_LONGLONG
060668a1 144extern const wxULongLong wxInvalidSize = (unsigned)-1;
bd08f2f7 145#endif // wxUSE_LONGLONG
23b8a262 146
7b611a3a
VZ
147namespace
148{
23b8a262 149
951cd180
VZ
150// ----------------------------------------------------------------------------
151// private classes
152// ----------------------------------------------------------------------------
153
154// small helper class which opens and closes the file - we use it just to get
155// a file handle for the given file name to pass it to some Win32 API function
c67d6888 156#if defined(__WIN32__) && !defined(__WXMICROWIN__)
951cd180
VZ
157
158class wxFileHandle
159{
160public:
6dbb903b
VZ
161 enum OpenMode
162 {
163 Read,
164 Write
165 };
166
6bc176b4 167 wxFileHandle(const wxString& filename, OpenMode mode, int flags = 0)
951cd180
VZ
168 {
169 m_hFile = ::CreateFile
170 (
e0a050e3 171 filename.fn_str(), // name
0ea621cc 172 mode == Read ? GENERIC_READ // access mask
6dbb903b 173 : GENERIC_WRITE,
54bcff35
VZ
174 FILE_SHARE_READ | // sharing mode
175 FILE_SHARE_WRITE, // (allow everything)
6dbb903b
VZ
176 NULL, // no secutity attr
177 OPEN_EXISTING, // creation disposition
6bc176b4 178 flags, // flags
6dbb903b 179 NULL // no template file
951cd180
VZ
180 );
181
182 if ( m_hFile == INVALID_HANDLE_VALUE )
183 {
49a70977 184 if ( mode == Read )
43b2d5e7 185 {
49a70977
VS
186 wxLogSysError(_("Failed to open '%s' for reading"),
187 filename.c_str());
43b2d5e7 188 }
49a70977 189 else
43b2d5e7 190 {
49a70977
VS
191 wxLogSysError(_("Failed to open '%s' for writing"),
192 filename.c_str());
43b2d5e7 193 }
951cd180
VZ
194 }
195 }
196
197 ~wxFileHandle()
198 {
199 if ( m_hFile != INVALID_HANDLE_VALUE )
200 {
201 if ( !::CloseHandle(m_hFile) )
202 {
203 wxLogSysError(_("Failed to close file handle"));
204 }
205 }
206 }
207
f363e05c 208 // return true only if the file could be opened successfully
951cd180
VZ
209 bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; }
210
211 // get the handle
212 operator HANDLE() const { return m_hFile; }
213
214private:
215 HANDLE m_hFile;
216};
217
218#endif // __WIN32__
219
220// ----------------------------------------------------------------------------
221// private functions
222// ----------------------------------------------------------------------------
223
e2b87f38 224#if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__)
951cd180
VZ
225
226// convert between wxDateTime and FILETIME which is a 64-bit value representing
227// the number of 100-nanosecond intervals since January 1, 1601.
228
d56e2b97 229static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft)
951cd180 230{
752f10a8 231 FILETIME ftcopy = ft;
6dbb903b 232 FILETIME ftLocal;
752f10a8 233 if ( !::FileTimeToLocalFileTime(&ftcopy, &ftLocal) )
6dbb903b 234 {
9a83f860 235 wxLogLastError(wxT("FileTimeToLocalFileTime"));
6dbb903b 236 }
d56e2b97 237
6dbb903b
VZ
238 SYSTEMTIME st;
239 if ( !::FileTimeToSystemTime(&ftLocal, &st) )
240 {
9a83f860 241 wxLogLastError(wxT("FileTimeToSystemTime"));
6dbb903b 242 }
d56e2b97 243
6dbb903b
VZ
244 dt->Set(st.wDay, wxDateTime::Month(st.wMonth - 1), st.wYear,
245 st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
951cd180
VZ
246}
247
d56e2b97 248static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt)
951cd180 249{
6dbb903b
VZ
250 SYSTEMTIME st;
251 st.wDay = dt.GetDay();
ad816476
WS
252 st.wMonth = (WORD)(dt.GetMonth() + 1);
253 st.wYear = (WORD)dt.GetYear();
6dbb903b
VZ
254 st.wHour = dt.GetHour();
255 st.wMinute = dt.GetMinute();
256 st.wSecond = dt.GetSecond();
257 st.wMilliseconds = dt.GetMillisecond();
258
259 FILETIME ftLocal;
260 if ( !::SystemTimeToFileTime(&st, &ftLocal) )
261 {
9a83f860 262 wxLogLastError(wxT("SystemTimeToFileTime"));
6dbb903b 263 }
d56e2b97 264
6dbb903b
VZ
265 if ( !::LocalFileTimeToFileTime(&ftLocal, ft) )
266 {
9a83f860 267 wxLogLastError(wxT("LocalFileTimeToFileTime"));
6dbb903b 268 }
951cd180
VZ
269}
270
e2b87f38 271#endif // wxUSE_DATETIME && __WIN32__
951cd180 272
67c34f64
VZ
273// return a string with the volume par
274static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format)
275{
276 wxString path;
277
278 if ( !volume.empty() )
279 {
6ce27aa2
VZ
280 format = wxFileName::GetFormat(format);
281
67c34f64
VZ
282 // Special Windows UNC paths hack, part 2: undo what we did in
283 // SplitPath() and make an UNC path if we have a drive which is not a
284 // single letter (hopefully the network shares can't be one letter only
285 // although I didn't find any authoritative docs on this)
286 if ( format == wxPATH_DOS && volume.length() > 1 )
287 {
288 path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
289 }
290 else if ( format == wxPATH_DOS || format == wxPATH_VMS )
291 {
292 path << volume << wxFileName::GetVolumeSeparator(format);
293 }
294 // else ignore
295 }
296
297 return path;
298}
299
7b611a3a
VZ
300// return true if the character is a DOS path separator i.e. either a slash or
301// a backslash
302inline bool IsDOSPathSep(wxUniChar ch)
303{
304 return ch == wxFILE_SEP_PATH_DOS || ch == wxFILE_SEP_PATH_UNIX;
305}
306
9e1c7236
VZ
307// return true if the format used is the DOS/Windows one and the string looks
308// like a UNC path
309static bool IsUNCPath(const wxString& path, wxPathFormat format)
310{
311 return format == wxPATH_DOS &&
312 path.length() >= 4 && // "\\a" can't be a UNC path
7b611a3a
VZ
313 IsDOSPathSep(path[0u]) &&
314 IsDOSPathSep(path[1u]) &&
315 !IsDOSPathSep(path[2u]);
9e1c7236
VZ
316}
317
7b611a3a
VZ
318} // anonymous namespace
319
097ead30
VZ
320// ============================================================================
321// implementation
322// ============================================================================
323
324// ----------------------------------------------------------------------------
844f90fb 325// wxFileName construction
097ead30 326// ----------------------------------------------------------------------------
df5ddbca 327
a35b27b1
RR
328void wxFileName::Assign( const wxFileName &filepath )
329{
04c943b1 330 m_volume = filepath.GetVolume();
844f90fb 331 m_dirs = filepath.GetDirs();
04c943b1
VZ
332 m_name = filepath.GetName();
333 m_ext = filepath.GetExt();
a2fa5040 334 m_relative = filepath.m_relative;
dfecbee5 335 m_hasExt = filepath.m_hasExt;
df5ddbca
RR
336}
337
04c943b1
VZ
338void wxFileName::Assign(const wxString& volume,
339 const wxString& path,
340 const wxString& name,
341 const wxString& ext,
dfecbee5 342 bool hasExt,
9e1c7236 343 wxPathFormat format)
a7b51bc8 344{
9e1c7236
VZ
345 // we should ignore paths which look like UNC shares because we already
346 // have the volume here and the UNC notation (\\server\path) is only valid
347 // for paths which don't start with a volume, so prevent SetPath() from
348 // recognizing "\\foo\bar" in "c:\\foo\bar" as an UNC path
349 //
350 // note also that this is a rather ugly way to do what we want (passing
351 // some kind of flag telling to ignore UNC paths to SetPath() would be
352 // better) but this is the safest thing to do to avoid breaking backwards
353 // compatibility in 2.8
354 if ( IsUNCPath(path, format) )
355 {
356 // remove one of the 2 leading backslashes to ensure that it's not
357 // recognized as an UNC path by SetPath()
358 wxString pathNonUNC(path, 1, wxString::npos);
359 SetPath(pathNonUNC, format);
360 }
361 else // no UNC complications
362 {
363 SetPath(path, format);
364 }
a7b51bc8
RR
365
366 m_volume = volume;
367 m_ext = ext;
368 m_name = name;
dfecbee5
VZ
369
370 m_hasExt = hasExt;
a7b51bc8
RR
371}
372
f1e77933 373void wxFileName::SetPath( const wxString& pathOrig, wxPathFormat format )
df5ddbca 374{
844f90fb 375 m_dirs.Clear();
90a68369 376
f1e77933 377 if ( pathOrig.empty() )
df5ddbca 378 {
f1e77933
VZ
379 // no path at all
380 m_relative = true;
a2fa5040 381
f1e77933
VZ
382 return;
383 }
90a68369 384
f1e77933 385 format = GetFormat( format );
a2fa5040 386
f1e77933
VZ
387 // 0) deal with possible volume part first
388 wxString volume,
389 path;
390 SplitVolume(pathOrig, &volume, &path, format);
391 if ( !volume.empty() )
392 {
393 m_relative = false;
a2fa5040 394
f1e77933
VZ
395 SetVolume(volume);
396 }
f363e05c 397
f1e77933
VZ
398 // 1) Determine if the path is relative or absolute.
399 wxChar leadingChar = path[0u];
a2fa5040 400
f1e77933
VZ
401 switch (format)
402 {
403 case wxPATH_MAC:
404 m_relative = leadingChar == wxT(':');
405
406 // We then remove a leading ":". The reason is in our
407 // storage form for relative paths:
408 // ":dir:file.txt" actually means "./dir/file.txt" in
409 // DOS notation and should get stored as
410 // (relative) (dir) (file.txt)
411 // "::dir:file.txt" actually means "../dir/file.txt"
412 // stored as (relative) (..) (dir) (file.txt)
413 // This is important only for the Mac as an empty dir
414 // actually means <UP>, whereas under DOS, double
415 // slashes can be ignored: "\\\\" is the same as "\\".
416 if (m_relative)
417 path.erase( 0, 1 );
418 break;
a2fa5040 419
f1e77933
VZ
420 case wxPATH_VMS:
421 // TODO: what is the relative path format here?
422 m_relative = false;
423 break;
90a68369 424
f1e77933 425 default:
9a83f860 426 wxFAIL_MSG( wxT("Unknown path format") );
f1e77933 427 // !! Fall through !!
90a68369 428
f1e77933 429 case wxPATH_UNIX:
be5be16a 430 m_relative = leadingChar != wxT('/');
f1e77933 431 break;
353f41cb 432
f1e77933
VZ
433 case wxPATH_DOS:
434 m_relative = !IsPathSeparator(leadingChar, format);
435 break;
844f90fb 436
df5ddbca 437 }
f1e77933
VZ
438
439 // 2) Break up the path into its members. If the original path
440 // was just "/" or "\\", m_dirs will be empty. We know from
441 // the m_relative field, if this means "nothing" or "root dir".
442
443 wxStringTokenizer tn( path, GetPathSeparators(format) );
444
445 while ( tn.HasMoreTokens() )
a7b51bc8 446 {
f1e77933
VZ
447 wxString token = tn.GetNextToken();
448
449 // Remove empty token under DOS and Unix, interpret them
450 // as .. under Mac.
451 if (token.empty())
452 {
453 if (format == wxPATH_MAC)
454 m_dirs.Add( wxT("..") );
455 // else ignore
456 }
457 else
458 {
459 m_dirs.Add( token );
460 }
a7b51bc8 461 }
844f90fb
VZ
462}
463
464void wxFileName::Assign(const wxString& fullpath,
465 wxPathFormat format)
466{
04c943b1 467 wxString volume, path, name, ext;
dfecbee5
VZ
468 bool hasExt;
469 SplitPath(fullpath, &volume, &path, &name, &ext, &hasExt, format);
844f90fb 470
dfecbee5 471 Assign(volume, path, name, ext, hasExt, format);
844f90fb
VZ
472}
473
81f25632 474void wxFileName::Assign(const wxString& fullpathOrig,
844f90fb
VZ
475 const wxString& fullname,
476 wxPathFormat format)
477{
81f25632
VZ
478 // always recognize fullpath as directory, even if it doesn't end with a
479 // slash
480 wxString fullpath = fullpathOrig;
69858116 481 if ( !fullpath.empty() && !wxEndsWithPathSeparator(fullpath) )
81f25632 482 {
33b97389 483 fullpath += GetPathSeparator(format);
81f25632
VZ
484 }
485
52dbd056 486 wxString volume, path, name, ext;
dfecbee5 487 bool hasExt;
81f25632 488
4b6a582b
VZ
489 // do some consistency checks: the name should be really just the filename
490 // and the path should be really just a path
dfecbee5 491 wxString volDummy, pathDummy, nameDummy, extDummy;
81f25632 492
dfecbee5 493 SplitPath(fullname, &volDummy, &pathDummy, &name, &ext, &hasExt, format);
81f25632 494
dfecbee5 495 wxASSERT_MSG( volDummy.empty() && pathDummy.empty(),
9a83f860 496 wxT("the file name shouldn't contain the path") );
81f25632
VZ
497
498 SplitPath(fullpath, &volume, &path, &nameDummy, &extDummy, format);
499
4c914788
JJ
500#ifndef __VMS
501 // This test makes no sense on an OpenVMS system.
502 wxASSERT_MSG( nameDummy.empty() && extDummy.empty(),
9a83f860 503 wxT("the path shouldn't contain file name nor extension") );
4c914788 504#endif
dfecbee5 505 Assign(volume, path, name, ext, hasExt, format);
52dbd056
VZ
506}
507
4c2deb19
VZ
508void wxFileName::Assign(const wxString& pathOrig,
509 const wxString& name,
510 const wxString& ext,
511 wxPathFormat format)
512{
513 wxString volume,
514 path;
515 SplitVolume(pathOrig, &volume, &path, format);
516
517 Assign(volume, path, name, ext, format);
518}
519
52dbd056
VZ
520void wxFileName::AssignDir(const wxString& dir, wxPathFormat format)
521{
b494c48b 522 Assign(dir, wxEmptyString, format);
844f90fb
VZ
523}
524
525void wxFileName::Clear()
526{
527 m_dirs.Clear();
04c943b1
VZ
528
529 m_volume =
844f90fb
VZ
530 m_name =
531 m_ext = wxEmptyString;
fb969475
VZ
532
533 // we don't have any absolute path for now
f363e05c 534 m_relative = true;
dfecbee5
VZ
535
536 // nor any extension
537 m_hasExt = false;
844f90fb
VZ
538}
539
540/* static */
520200fd 541wxFileName wxFileName::FileName(const wxString& file, wxPathFormat format)
844f90fb 542{
520200fd 543 return wxFileName(file, format);
844f90fb
VZ
544}
545
546/* static */
520200fd 547wxFileName wxFileName::DirName(const wxString& dir, wxPathFormat format)
844f90fb
VZ
548{
549 wxFileName fn;
520200fd 550 fn.AssignDir(dir, format);
844f90fb 551 return fn;
df5ddbca
RR
552}
553
844f90fb
VZ
554// ----------------------------------------------------------------------------
555// existence tests
556// ----------------------------------------------------------------------------
557
8e41796c 558bool wxFileName::FileExists() const
df5ddbca 559{
a35b27b1
RR
560 return wxFileName::FileExists( GetFullPath() );
561}
562
563bool wxFileName::FileExists( const wxString &file )
564{
565 return ::wxFileExists( file );
df5ddbca
RR
566}
567
8e41796c 568bool wxFileName::DirExists() const
df5ddbca 569{
db759dde 570 return wxFileName::DirExists( GetPath() );
a35b27b1
RR
571}
572
573bool wxFileName::DirExists( const wxString &dir )
574{
da865fdd 575 return ::wxDirExists( dir );
df5ddbca
RR
576}
577
844f90fb
VZ
578// ----------------------------------------------------------------------------
579// CWD and HOME stuff
580// ----------------------------------------------------------------------------
581
6f91bc33 582void wxFileName::AssignCwd(const wxString& volume)
df5ddbca 583{
6f91bc33 584 AssignDir(wxFileName::GetCwd(volume));
a35b27b1
RR
585}
586
844f90fb 587/* static */
6f91bc33 588wxString wxFileName::GetCwd(const wxString& volume)
a35b27b1 589{
6f91bc33
VZ
590 // if we have the volume, we must get the current directory on this drive
591 // and to do this we have to chdir to this volume - at least under Windows,
592 // I don't know how to get the current drive on another volume elsewhere
593 // (TODO)
594 wxString cwdOld;
595 if ( !volume.empty() )
596 {
597 cwdOld = wxGetCwd();
598 SetCwd(volume + GetVolumeSeparator());
599 }
600
601 wxString cwd = ::wxGetCwd();
602
603 if ( !volume.empty() )
604 {
605 SetCwd(cwdOld);
606 }
ae4d7004 607
6f91bc33 608 return cwd;
a35b27b1
RR
609}
610
d9e80dce 611bool wxFileName::SetCwd() const
a35b27b1 612{
db759dde 613 return wxFileName::SetCwd( GetPath() );
df5ddbca
RR
614}
615
a35b27b1 616bool wxFileName::SetCwd( const wxString &cwd )
df5ddbca 617{
a35b27b1 618 return ::wxSetWorkingDirectory( cwd );
df5ddbca
RR
619}
620
a35b27b1
RR
621void wxFileName::AssignHomeDir()
622{
844f90fb 623 AssignDir(wxFileName::GetHomeDir());
a35b27b1 624}
844f90fb 625
a35b27b1
RR
626wxString wxFileName::GetHomeDir()
627{
628 return ::wxGetHomeDir();
629}
844f90fb 630
68351053 631
b70a2866
MW
632// ----------------------------------------------------------------------------
633// CreateTempFileName
634// ----------------------------------------------------------------------------
635
636#if wxUSE_FILE || wxUSE_FFILE
637
638
639#if !defined wx_fdopen && defined HAVE_FDOPEN
640 #define wx_fdopen fdopen
641#endif
642
643// NB: GetTempFileName() under Windows creates the file, so using
644// O_EXCL there would fail
645#ifdef __WINDOWS__
646 #define wxOPEN_EXCL 0
647#else
648 #define wxOPEN_EXCL O_EXCL
649#endif
650
651
652#ifdef wxOpenOSFHandle
653#define WX_HAVE_DELETE_ON_CLOSE
654// On Windows create a file with the FILE_FLAGS_DELETE_ON_CLOSE flags.
655//
656static int wxOpenWithDeleteOnClose(const wxString& filename)
df5ddbca 657{
b70a2866
MW
658 DWORD access = GENERIC_READ | GENERIC_WRITE;
659
660 DWORD disposition = OPEN_ALWAYS;
661
662 DWORD attributes = FILE_ATTRIBUTE_TEMPORARY |
663 FILE_FLAG_DELETE_ON_CLOSE;
664
e0a050e3 665 HANDLE h = ::CreateFile(filename.fn_str(), access, 0, NULL,
b70a2866
MW
666 disposition, attributes, NULL);
667
693bfcaf 668 return wxOpenOSFHandle(h, wxO_BINARY);
ade35f11 669}
b70a2866 670#endif // wxOpenOSFHandle
ade35f11 671
b70a2866
MW
672
673// Helper to open the file
674//
675static int wxTempOpen(const wxString& path, bool *deleteOnClose)
676{
677#ifdef WX_HAVE_DELETE_ON_CLOSE
678 if (*deleteOnClose)
679 return wxOpenWithDeleteOnClose(path);
680#endif
681
682 *deleteOnClose = false;
683
684 return wxOpen(path, wxO_BINARY | O_RDWR | O_CREAT | wxOPEN_EXCL, 0600);
685}
686
687
688#if wxUSE_FFILE
689// Helper to open the file and attach it to the wxFFile
690//
691static bool wxTempOpen(wxFFile *file, const wxString& path, bool *deleteOnClose)
ade35f11 692{
b70a2866
MW
693#ifndef wx_fdopen
694 *deleteOnClose = false;
9a83f860 695 return file->Open(path, wxT("w+b"));
b70a2866
MW
696#else // wx_fdopen
697 int fd = wxTempOpen(path, deleteOnClose);
693bfcaf 698 if (fd == -1)
b70a2866
MW
699 return false;
700 file->Attach(wx_fdopen(fd, "w+b"));
701 return file->IsOpened();
702#endif // wx_fdopen
703}
704#endif // wxUSE_FFILE
705
706
707#if !wxUSE_FILE
708 #define WXFILEARGS(x, y) y
709#elif !wxUSE_FFILE
710 #define WXFILEARGS(x, y) x
711#else
712 #define WXFILEARGS(x, y) x, y
713#endif
714
715
716// Implementation of wxFileName::CreateTempFileName().
717//
718static wxString wxCreateTempImpl(
719 const wxString& prefix,
720 WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
721 bool *deleteOnClose = NULL)
722{
723#if wxUSE_FILE && wxUSE_FFILE
724 wxASSERT(fileTemp == NULL || ffileTemp == NULL);
725#endif
ade35f11 726 wxString path, dir, name;
b70a2866
MW
727 bool wantDeleteOnClose = false;
728
729 if (deleteOnClose)
730 {
731 // set the result to false initially
732 wantDeleteOnClose = *deleteOnClose;
733 *deleteOnClose = false;
734 }
735 else
736 {
737 // easier if it alwasys points to something
738 deleteOnClose = &wantDeleteOnClose;
739 }
ade35f11
VZ
740
741 // use the directory specified by the prefix
b70a2866 742 wxFileName::SplitPath(prefix, &dir, &name, NULL /* extension */);
ade35f11 743
6091caf0
JS
744 if (dir.empty())
745 {
8d7d6dea 746 dir = wxFileName::GetTempDir();
6091caf0
JS
747 }
748
1c193821 749#if defined(__WXWINCE__)
f05ba7ff 750 path = dir + wxT("\\") + name;
1c193821 751 int i = 1;
b70a2866 752 while (wxFileName::FileExists(path))
1c193821 753 {
f05ba7ff 754 path = dir + wxT("\\") + name ;
1c193821
JS
755 path << i;
756 i ++;
757 }
ade35f11 758
1c193821 759#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
e0a050e3
VS
760 if ( !::GetTempFileName(dir.fn_str(), name.fn_str(), 0,
761 wxStringBuffer(path, MAX_PATH + 1)) )
ade35f11 762 {
9a83f860 763 wxLogLastError(wxT("GetTempFileName"));
ade35f11
VZ
764
765 path.clear();
766 }
ade35f11 767
5a3912f2 768#else // !Windows
ade35f11
VZ
769 path = dir;
770
771 if ( !wxEndsWithPathSeparator(dir) &&
772 (name.empty() || !wxIsPathSeparator(name[0u])) )
773 {
d9f54bb0 774 path += wxFILE_SEP_PATH;
ade35f11
VZ
775 }
776
777 path += name;
778
7070f55b 779#if defined(HAVE_MKSTEMP)
ade35f11 780 // scratch space for mkstemp()
9a83f860 781 path += wxT("XXXXXX");
ade35f11 782
74cf9763 783 // we need to copy the path to the buffer in which mkstemp() can modify it
86501081 784 wxCharBuffer buf(path.fn_str());
74cf9763
VZ
785
786 // cast is safe because the string length doesn't change
ca11abde 787 int fdTemp = mkstemp( (char*)(const char*) buf );
df22f860 788 if ( fdTemp == -1 )
ade35f11
VZ
789 {
790 // this might be not necessary as mkstemp() on most systems should have
791 // already done it but it doesn't hurt neither...
792 path.clear();
793 }
df22f860
VZ
794 else // mkstemp() succeeded
795 {
ca11abde 796 path = wxConvFile.cMB2WX( (const char*) buf );
a62848fd 797
b70a2866 798 #if wxUSE_FILE
df22f860
VZ
799 // avoid leaking the fd
800 if ( fileTemp )
801 {
802 fileTemp->Attach(fdTemp);
803 }
804 else
b70a2866
MW
805 #endif
806
807 #if wxUSE_FFILE
808 if ( ffileTemp )
809 {
810 #ifdef wx_fdopen
811 ffileTemp->Attach(wx_fdopen(fdTemp, "r+b"));
812 #else
9a83f860 813 ffileTemp->Open(path, wxT("r+b"));
b70a2866
MW
814 close(fdTemp);
815 #endif
816 }
817 else
818 #endif
819
df22f860
VZ
820 {
821 close(fdTemp);
822 }
823 }
ade35f11
VZ
824#else // !HAVE_MKSTEMP
825
826#ifdef HAVE_MKTEMP
827 // same as above
9a83f860 828 path += wxT("XXXXXX");
ade35f11 829
ca11abde 830 wxCharBuffer buf = wxConvFile.cWX2MB( path );
b70a2866 831 if ( !mktemp( (char*)(const char*) buf ) )
ade35f11
VZ
832 {
833 path.clear();
834 }
aed08d79
RR
835 else
836 {
ca11abde 837 path = wxConvFile.cMB2WX( (const char*) buf );
aed08d79 838 }
7070f55b 839#else // !HAVE_MKTEMP (includes __DOS__)
ade35f11 840 // generate the unique file name ourselves
68351053 841 #if !defined(__DOS__) && !defined(__PALMOS__) && (!defined(__MWERKS__) || defined(__DARWIN__) )
ade35f11 842 path << (unsigned int)getpid();
7070f55b 843 #endif
ade35f11
VZ
844
845 wxString pathTry;
846
847 static const size_t numTries = 1000;
848 for ( size_t n = 0; n < numTries; n++ )
849 {
850 // 3 hex digits is enough for numTries == 1000 < 4096
9a83f860 851 pathTry = path + wxString::Format(wxT("%.03x"), (unsigned int) n);
b70a2866 852 if ( !wxFileName::FileExists(pathTry) )
ade35f11
VZ
853 {
854 break;
855 }
856
857 pathTry.clear();
858 }
859
860 path = pathTry;
861#endif // HAVE_MKTEMP/!HAVE_MKTEMP
862
df22f860
VZ
863#endif // HAVE_MKSTEMP/!HAVE_MKSTEMP
864
865#endif // Windows/!Windows
866
867 if ( path.empty() )
868 {
869 wxLogSysError(_("Failed to create a temporary file name"));
870 }
b70a2866 871 else
df22f860 872 {
b70a2866
MW
873 bool ok = true;
874
df22f860 875 // open the file - of course, there is a race condition here, this is
ade35f11 876 // why we always prefer using mkstemp()...
b70a2866
MW
877 #if wxUSE_FILE
878 if ( fileTemp && !fileTemp->IsOpened() )
879 {
880 *deleteOnClose = wantDeleteOnClose;
881 int fd = wxTempOpen(path, deleteOnClose);
882 if (fd != -1)
883 fileTemp->Attach(fd);
884 else
885 ok = false;
886 }
887 #endif
888
889 #if wxUSE_FFILE
890 if ( ffileTemp && !ffileTemp->IsOpened() )
891 {
892 *deleteOnClose = wantDeleteOnClose;
893 ok = wxTempOpen(ffileTemp, path, deleteOnClose);
894 }
895 #endif
896
897 if ( !ok )
ade35f11
VZ
898 {
899 // FIXME: If !ok here should we loop and try again with another
900 // file name? That is the standard recourse if open(O_EXCL)
901 // fails, though of course it should be protected against
902 // possible infinite looping too.
903
904 wxLogError(_("Failed to open temporary file."));
905
906 path.clear();
907 }
908 }
ade35f11
VZ
909
910 return path;
df5ddbca
RR
911}
912
b70a2866
MW
913
914static bool wxCreateTempImpl(
915 const wxString& prefix,
916 WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
917 wxString *name)
918{
919 bool deleteOnClose = true;
920
921 *name = wxCreateTempImpl(prefix,
922 WXFILEARGS(fileTemp, ffileTemp),
923 &deleteOnClose);
924
925 bool ok = !name->empty();
926
927 if (deleteOnClose)
928 name->clear();
929#ifdef __UNIX__
930 else if (ok && wxRemoveFile(*name))
931 name->clear();
932#endif
933
934 return ok;
935}
936
937
938static void wxAssignTempImpl(
939 wxFileName *fn,
940 const wxString& prefix,
941 WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp))
942{
943 wxString tempname;
944 tempname = wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, ffileTemp));
945
946 if ( tempname.empty() )
947 {
948 // error, failed to get temp file name
949 fn->Clear();
950 }
951 else // ok
952 {
953 fn->Assign(tempname);
954 }
955}
956
957
958void wxFileName::AssignTempFileName(const wxString& prefix)
959{
960 wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, NULL));
961}
962
963/* static */
964wxString wxFileName::CreateTempFileName(const wxString& prefix)
965{
966 return wxCreateTempImpl(prefix, WXFILEARGS(NULL, NULL));
967}
968
969#endif // wxUSE_FILE || wxUSE_FFILE
970
971
972#if wxUSE_FILE
973
974wxString wxCreateTempFileName(const wxString& prefix,
975 wxFile *fileTemp,
976 bool *deleteOnClose)
977{
978 return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), deleteOnClose);
979}
980
981bool wxCreateTempFile(const wxString& prefix,
982 wxFile *fileTemp,
983 wxString *name)
984{
985 return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), name);
986}
987
988void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp)
989{
990 wxAssignTempImpl(this, prefix, WXFILEARGS(fileTemp, NULL));
991}
992
993/* static */
994wxString
995wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp)
996{
997 return wxCreateTempFileName(prefix, fileTemp);
998}
999
68351053
WS
1000#endif // wxUSE_FILE
1001
b70a2866
MW
1002
1003#if wxUSE_FFILE
1004
1005wxString wxCreateTempFileName(const wxString& prefix,
1006 wxFFile *fileTemp,
1007 bool *deleteOnClose)
1008{
1009 return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), deleteOnClose);
1010}
1011
1012bool wxCreateTempFile(const wxString& prefix,
1013 wxFFile *fileTemp,
1014 wxString *name)
1015{
1016 return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), name);
1017
1018}
1019
1020void wxFileName::AssignTempFileName(const wxString& prefix, wxFFile *fileTemp)
1021{
1022 wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, fileTemp));
1023}
1024
1025/* static */
1026wxString
1027wxFileName::CreateTempFileName(const wxString& prefix, wxFFile *fileTemp)
1028{
1029 return wxCreateTempFileName(prefix, fileTemp);
1030}
1031
1032#endif // wxUSE_FFILE
1033
1034
844f90fb
VZ
1035// ----------------------------------------------------------------------------
1036// directory operations
1037// ----------------------------------------------------------------------------
1038
8758875e
VZ
1039// helper of GetTempDir(): check if the given directory exists and return it if
1040// it does or an empty string otherwise
1041namespace
8d7d6dea 1042{
8d7d6dea 1043
8758875e
VZ
1044wxString CheckIfDirExists(const wxString& dir)
1045{
1046 return wxFileName::DirExists(dir) ? dir : wxString();
1047}
1048
1049} // anonymous namespace
1050
1051wxString wxFileName::GetTempDir()
1052{
1053 // first try getting it from environment: this allows overriding the values
1054 // used by default if the user wants to create temporary files in another
1055 // directory
1056 wxString dir = CheckIfDirExists(wxGetenv("TMPDIR"));
1057 if ( dir.empty() )
8d7d6dea 1058 {
8758875e
VZ
1059 dir = CheckIfDirExists(wxGetenv("TMP"));
1060 if ( dir.empty() )
1061 dir = CheckIfDirExists(wxGetenv("TEMP"));
8d7d6dea 1062 }
8d7d6dea 1063
8758875e 1064 // if no environment variables are set, use the system default
8d7d6dea
JS
1065 if ( dir.empty() )
1066 {
8758875e
VZ
1067#if defined(__WXWINCE__)
1068 dir = CheckIfDirExists(wxT("\\temp"));
1069#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
8d7d6dea
JS
1070 if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
1071 {
9a83f860 1072 wxLogLastError(wxT("GetTempPath"));
8d7d6dea 1073 }
8758875e
VZ
1074#elif defined(__WXMAC__) && wxOSX_USE_CARBON
1075 dir = wxMacFindFolder(short(kOnSystemDisk), kTemporaryFolderType, kCreateFolder);
1076#endif // systems with native way
8d7d6dea 1077 }
8d7d6dea 1078
8758875e 1079 // fall back to hard coded value
8d7d6dea
JS
1080 if ( dir.empty() )
1081 {
8758875e
VZ
1082#ifdef __UNIX_LIKE__
1083 dir = CheckIfDirExists("/tmp");
1084 if ( dir.empty() )
1085#endif // __UNIX_LIKE__
1086 dir = ".";
8d7d6dea 1087 }
8d7d6dea
JS
1088
1089 return dir;
1090}
1091
89391a4e 1092bool wxFileName::Mkdir( int perm, int flags ) const
a35b27b1 1093{
db759dde 1094 return wxFileName::Mkdir(GetPath(), perm, flags);
a35b27b1
RR
1095}
1096
1527281e 1097bool wxFileName::Mkdir( const wxString& dir, int perm, int flags )
df5ddbca 1098{
1527281e 1099 if ( flags & wxPATH_MKDIR_FULL )
f0ce3409 1100 {
1527281e
VZ
1101 // split the path in components
1102 wxFileName filename;
1103 filename.AssignDir(dir);
f0ce3409 1104
f0ce3409 1105 wxString currPath;
0ea621cc
VZ
1106 if ( filename.HasVolume())
1107 {
1527281e 1108 currPath << wxGetVolumeString(filename.GetVolume(), wxPATH_NATIVE);
0ea621cc 1109 }
f0ce3409 1110
1527281e
VZ
1111 wxArrayString dirs = filename.GetDirs();
1112 size_t count = dirs.GetCount();
1113 for ( size_t i = 0; i < count; i++ )
1114 {
e0d81eac 1115 if ( i > 0 || filename.IsAbsolute() )
f0ce3409 1116 currPath += wxFILE_SEP_PATH;
1527281e 1117 currPath += dirs[i];
f0ce3409
JS
1118
1119 if (!DirExists(currPath))
1527281e 1120 {
f0ce3409 1121 if (!wxMkdir(currPath, perm))
1527281e
VZ
1122 {
1123 // no need to try creating further directories
f363e05c 1124 return false;
1527281e
VZ
1125 }
1126 }
f0ce3409
JS
1127 }
1128
f363e05c 1129 return true;
f0ce3409
JS
1130
1131 }
1527281e
VZ
1132
1133 return ::wxMkdir( dir, perm );
df5ddbca
RR
1134}
1135
89391a4e 1136bool wxFileName::Rmdir(int flags) const
df5ddbca 1137{
110c5094 1138 return wxFileName::Rmdir( GetPath(), flags );
df5ddbca
RR
1139}
1140
110c5094 1141bool wxFileName::Rmdir(const wxString& dir, int flags)
df5ddbca 1142{
110c5094
VZ
1143#ifdef __WXMSW__
1144 if ( flags & wxPATH_RMDIR_RECURSIVE )
1145 {
1146 // SHFileOperation needs double null termination string
1147 // but without separator at the end of the path
1148 wxString path(dir);
1149 if ( path.Last() == wxFILE_SEP_PATH )
1150 path.RemoveLast();
9a83f860 1151 path += wxT('\0');
110c5094
VZ
1152
1153 SHFILEOPSTRUCT fileop;
1154 wxZeroMemory(fileop);
1155 fileop.wFunc = FO_DELETE;
1156 fileop.pFrom = path.fn_str();
1157 fileop.fFlags = FOF_SILENT | FOF_NOCONFIRMATION;
1158 #ifndef __WXWINCE__
1159 // FOF_NOERRORUI is not defined in WinCE
1160 fileop.fFlags |= FOF_NOERRORUI;
1161 #endif
1162
1163 int ret = SHFileOperation(&fileop);
1164 if ( ret != 0 )
1165 {
1166 // SHFileOperation may return non-Win32 error codes, so the error
1167 // message can be incorrect
9a83f860 1168 wxLogApiError(wxT("SHFileOperation"), ret);
110c5094
VZ
1169 return false;
1170 }
1171
1172 return true;
1173 }
1174 else if ( flags & wxPATH_RMDIR_FULL )
1175#else // !__WXMSW__
1176 if ( flags != 0 ) // wxPATH_RMDIR_FULL or wxPATH_RMDIR_RECURSIVE
1177#endif // !__WXMSW__
1178 {
1179 wxString path(dir);
1180 if ( path.Last() != wxFILE_SEP_PATH )
1181 path += wxFILE_SEP_PATH;
1182
1183 wxDir d(path);
1184
1185 if ( !d.IsOpened() )
1186 return false;
1187
1188 wxString filename;
1189
1190 // first delete all subdirectories
1191 bool cont = d.GetFirst(&filename, "", wxDIR_DIRS | wxDIR_HIDDEN);
1192 while ( cont )
1193 {
1194 wxFileName::Rmdir(path + filename, flags);
1195 cont = d.GetNext(&filename);
1196 }
1197
1198#ifndef __WXMSW__
1199 if ( flags & wxPATH_RMDIR_RECURSIVE )
1200 {
1201 // delete all files too
1202 cont = d.GetFirst(&filename, "", wxDIR_FILES | wxDIR_HIDDEN);
1203 while ( cont )
1204 {
1205 ::wxRemoveFile(path + filename);
1206 cont = d.GetNext(&filename);
1207 }
1208 }
1209#endif // !__WXMSW__
1210 }
1211
1212 return ::wxRmdir(dir);
df5ddbca
RR
1213}
1214
844f90fb
VZ
1215// ----------------------------------------------------------------------------
1216// path normalization
1217// ----------------------------------------------------------------------------
1218
32a0d013 1219bool wxFileName::Normalize(int flags,
844f90fb
VZ
1220 const wxString& cwd,
1221 wxPathFormat format)
a35b27b1 1222{
05e1201c
VZ
1223 // deal with env vars renaming first as this may seriously change the path
1224 if ( flags & wxPATH_NORM_ENV_VARS )
1225 {
1226 wxString pathOrig = GetFullPath(format);
1227 wxString path = wxExpandEnvVars(pathOrig);
1228 if ( path != pathOrig )
1229 {
1230 Assign(path);
1231 }
1232 }
1233
844f90fb
VZ
1234 // the existing path components
1235 wxArrayString dirs = GetDirs();
1236
1237 // the path to prepend in front to make the path absolute
1238 wxFileName curDir;
1239
1240 format = GetFormat(format);
ae4d7004 1241
bf7f7793 1242 // set up the directory to use for making the path absolute later
a2fa5040 1243 if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute(format) )
a35b27b1 1244 {
844f90fb 1245 if ( cwd.empty() )
6f91bc33
VZ
1246 {
1247 curDir.AssignCwd(GetVolume());
1248 }
1249 else // cwd provided
1250 {
844f90fb 1251 curDir.AssignDir(cwd);
6f91bc33 1252 }
a35b27b1 1253 }
ae4d7004 1254
844f90fb 1255 // handle ~ stuff under Unix only
be5be16a 1256 if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) && m_relative )
a35b27b1 1257 {
844f90fb
VZ
1258 if ( !dirs.IsEmpty() )
1259 {
1260 wxString dir = dirs[0u];
9a83f860 1261 if ( !dir.empty() && dir[0u] == wxT('~') )
844f90fb 1262 {
bf7f7793 1263 // to make the path absolute use the home directory
844f90fb 1264 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
da2fd5ac 1265 dirs.RemoveAt(0u);
844f90fb
VZ
1266 }
1267 }
a35b27b1 1268 }
844f90fb 1269
52dbd056 1270 // transform relative path into abs one
844f90fb 1271 if ( curDir.IsOk() )
a35b27b1 1272 {
bf7f7793
RR
1273 // this path may be relative because it doesn't have the volume name
1274 // and still have m_relative=true; in this case we shouldn't modify
1275 // our directory components but just set the current volume
1276 if ( !HasVolume() && curDir.HasVolume() )
844f90fb 1277 {
bf7f7793
RR
1278 SetVolume(curDir.GetVolume());
1279
1280 if ( !m_relative )
1281 {
1282 // yes, it was the case - we don't need curDir then
1283 curDir.Clear();
1284 }
844f90fb
VZ
1285 }
1286
bf7f7793
RR
1287 // finally, prepend curDir to the dirs array
1288 wxArrayString dirsNew = curDir.GetDirs();
1289 WX_PREPEND_ARRAY(dirs, dirsNew);
1290
1291 // if we used e.g. tilde expansion previously and wxGetUserHome didn't
1292 // return for some reason an absolute path, then curDir maybe not be absolute!
be5be16a 1293 if ( !curDir.m_relative )
bf7f7793
RR
1294 {
1295 // we have prepended an absolute path and thus we are now an absolute
1296 // file name too
1297 m_relative = false;
1298 }
1299 // else if (flags & wxPATH_NORM_ABSOLUTE):
1300 // should we warn the user that we didn't manage to make the path absolute?
a35b27b1 1301 }
844f90fb
VZ
1302
1303 // now deal with ".", ".." and the rest
1304 m_dirs.Empty();
1305 size_t count = dirs.GetCount();
1306 for ( size_t n = 0; n < count; n++ )
a35b27b1 1307 {
844f90fb
VZ
1308 wxString dir = dirs[n];
1309
2bc60417 1310 if ( flags & wxPATH_NORM_DOTS )
844f90fb
VZ
1311 {
1312 if ( dir == wxT(".") )
1313 {
1314 // just ignore
1315 continue;
1316 }
1317
1318 if ( dir == wxT("..") )
1319 {
1320 if ( m_dirs.IsEmpty() )
1321 {
1322 wxLogError(_("The path '%s' contains too many \"..\"!"),
1323 GetFullPath().c_str());
f363e05c 1324 return false;
844f90fb
VZ
1325 }
1326
ae4d7004 1327 m_dirs.RemoveAt(m_dirs.GetCount() - 1);
844f90fb
VZ
1328 continue;
1329 }
1330 }
1331
844f90fb 1332 m_dirs.Add(dir);
a35b27b1 1333 }
a62848fd 1334
6bda7391 1335#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
21f60945
JS
1336 if ( (flags & wxPATH_NORM_SHORTCUT) )
1337 {
1338 wxString filename;
1339 if (GetShortcutTarget(GetFullPath(format), filename))
1340 {
21f60945
JS
1341 m_relative = false;
1342 Assign(filename);
1343 }
1344 }
1345#endif
844f90fb 1346
9a0c5c01
VZ
1347#if defined(__WIN32__)
1348 if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
844f90fb 1349 {
9a0c5c01
VZ
1350 Assign(GetLongPath());
1351 }
1352#endif // Win32
844f90fb 1353
9a0c5c01
VZ
1354 // Change case (this should be kept at the end of the function, to ensure
1355 // that the path doesn't change any more after we normalize its case)
1356 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
1357 {
55268a3a 1358 m_volume.MakeLower();
844f90fb
VZ
1359 m_name.MakeLower();
1360 m_ext.MakeLower();
844f90fb 1361
9a0c5c01
VZ
1362 // directory entries must be made lower case as well
1363 count = m_dirs.GetCount();
1364 for ( size_t i = 0; i < count; i++ )
1365 {
1366 m_dirs[i].MakeLower();
1367 }
9e9b65c1 1368 }
9e9b65c1 1369
f363e05c 1370 return true;
a2fa5040
VZ
1371}
1372
395f3aa8
FM
1373#ifndef __WXWINCE__
1374bool wxFileName::ReplaceEnvVariable(const wxString& envname,
1375 const wxString& replacementFmtString,
1376 wxPathFormat format)
1377{
1378 // look into stringForm for the contents of the given environment variable
1379 wxString val;
1380 if (envname.empty() ||
1381 !wxGetEnv(envname, &val))
1382 return false;
1383 if (val.empty())
1384 return false;
1385
1386 wxString stringForm = GetPath(wxPATH_GET_VOLUME, format);
1387 // do not touch the file name and the extension
1388
1389 wxString replacement = wxString::Format(replacementFmtString, envname);
1390 stringForm.Replace(val, replacement);
1391
1392 // Now assign ourselves the modified path:
1393 Assign(stringForm, GetFullName(), format);
1394
1395 return true;
1396}
1397#endif
1398
1399bool wxFileName::ReplaceHomeDir(wxPathFormat format)
1400{
1401 wxString homedir = wxGetHomeDir();
1402 if (homedir.empty())
1403 return false;
1404
1405 wxString stringForm = GetPath(wxPATH_GET_VOLUME, format);
1406 // do not touch the file name and the extension
1407
1408 stringForm.Replace(homedir, "~");
1409
1410 // Now assign ourselves the modified path:
1411 Assign(stringForm, GetFullName(), format);
1412
1413 return true;
1414}
1415
21f60945
JS
1416// ----------------------------------------------------------------------------
1417// get the shortcut target
1418// ----------------------------------------------------------------------------
1419
5671b3b6
JS
1420// WinCE (3) doesn't have CLSID_ShellLink, IID_IShellLink definitions.
1421// The .lnk file is a plain text file so it should be easy to
1422// make it work. Hint from Google Groups:
1423// "If you open up a lnk file, you'll see a
1424// number, followed by a pound sign (#), followed by more text. The
1425// number is the number of characters that follows the pound sign. The
1426// characters after the pound sign are the command line (which _can_
1427// include arguments) to be executed. Any path (e.g. \windows\program
1428// files\myapp.exe) that includes spaces needs to be enclosed in
1429// quotation marks."
1430
6bda7391 1431#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
5671b3b6
JS
1432// The following lines are necessary under WinCE
1433// #include "wx/msw/private.h"
1434// #include <ole2.h>
21f60945 1435#include <shlobj.h>
5671b3b6
JS
1436#if defined(__WXWINCE__)
1437#include <shlguid.h>
1438#endif
21f60945 1439
cea0869c
VZ
1440bool wxFileName::GetShortcutTarget(const wxString& shortcutPath,
1441 wxString& targetFilename,
d9e80dce 1442 wxString* arguments) const
21f60945 1443{
21f60945 1444 wxString path, file, ext;
bd365871 1445 wxFileName::SplitPath(shortcutPath, & path, & file, & ext);
a62848fd
WS
1446
1447 HRESULT hres;
1448 IShellLink* psl;
1449 bool success = false;
21f60945
JS
1450
1451 // Assume it's not a shortcut if it doesn't end with lnk
489f6cf7 1452 if (ext.CmpNoCase(wxT("lnk"))!=0)
a62848fd
WS
1453 return false;
1454
1455 // create a ShellLink object
1456 hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1457 IID_IShellLink, (LPVOID*) &psl);
1458
1459 if (SUCCEEDED(hres))
1460 {
1461 IPersistFile* ppf;
1462 hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf);
1463 if (SUCCEEDED(hres))
1464 {
1465 WCHAR wsz[MAX_PATH];
1466
1467 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, shortcutPath.mb_str(), -1, wsz,
1468 MAX_PATH);
1469
1470 hres = ppf->Load(wsz, 0);
cea0869c
VZ
1471 ppf->Release();
1472
a62848fd
WS
1473 if (SUCCEEDED(hres))
1474 {
21f60945 1475 wxChar buf[2048];
59ba321b
JS
1476 // Wrong prototype in early versions
1477#if defined(__MINGW32__) && !wxCHECK_W32API_VERSION(2, 2)
1478 psl->GetPath((CHAR*) buf, 2048, NULL, SLGP_UNCPRIORITY);
1479#else
a62848fd 1480 psl->GetPath(buf, 2048, NULL, SLGP_UNCPRIORITY);
59ba321b 1481#endif
a62848fd 1482 targetFilename = wxString(buf);
21f60945
JS
1483 success = (shortcutPath != targetFilename);
1484
a62848fd 1485 psl->GetArguments(buf, 2048);
21f60945 1486 wxString args(buf);
b494c48b 1487 if (!args.empty() && arguments)
21f60945
JS
1488 {
1489 *arguments = args;
a62848fd
WS
1490 }
1491 }
1492 }
cea0869c
VZ
1493
1494 psl->Release();
a62848fd 1495 }
a62848fd 1496 return success;
21f60945 1497}
cea0869c
VZ
1498
1499#endif // __WIN32__ && !__WXWINCE__
21f60945
JS
1500
1501
a2fa5040
VZ
1502// ----------------------------------------------------------------------------
1503// absolute/relative paths
1504// ----------------------------------------------------------------------------
1505
1506bool wxFileName::IsAbsolute(wxPathFormat format) const
1507{
be5be16a
VZ
1508 // unix paths beginning with ~ are reported as being absolute
1509 if ( format == wxPATH_UNIX )
1510 {
1511 if ( !m_dirs.IsEmpty() )
1512 {
1513 wxString dir = m_dirs[0u];
1514
9a83f860 1515 if (!dir.empty() && dir[0u] == wxT('~'))
be5be16a
VZ
1516 return true;
1517 }
1518 }
1519
a2fa5040
VZ
1520 // if our path doesn't start with a path separator, it's not an absolute
1521 // path
1522 if ( m_relative )
f363e05c 1523 return false;
a2fa5040
VZ
1524
1525 if ( !GetVolumeSeparator(format).empty() )
1526 {
1527 // this format has volumes and an absolute path must have one, it's not
be5be16a 1528 // enough to have the full path to be an absolute file under Windows
a2fa5040 1529 if ( GetVolume().empty() )
f363e05c 1530 return false;
a2fa5040
VZ
1531 }
1532
f363e05c 1533 return true;
a35b27b1
RR
1534}
1535
f7d886af
VZ
1536bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
1537{
cef9731a 1538 wxFileName fnBase = wxFileName::DirName(pathBase, format);
f7d886af
VZ
1539
1540 // get cwd only once - small time saving
1541 wxString cwd = wxGetCwd();
b5b62eea
JS
1542 Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
1543 fnBase.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
f7d886af
VZ
1544
1545 bool withCase = IsCaseSensitive(format);
1546
1547 // we can't do anything if the files live on different volumes
1548 if ( !GetVolume().IsSameAs(fnBase.GetVolume(), withCase) )
1549 {
1550 // nothing done
f363e05c 1551 return false;
f7d886af
VZ
1552 }
1553
1554 // same drive, so we don't need our volume
1555 m_volume.clear();
1556
1557 // remove common directories starting at the top
1558 while ( !m_dirs.IsEmpty() && !fnBase.m_dirs.IsEmpty() &&
1559 m_dirs[0u].IsSameAs(fnBase.m_dirs[0u], withCase) )
1560 {
ae4d7004
VZ
1561 m_dirs.RemoveAt(0);
1562 fnBase.m_dirs.RemoveAt(0);
f7d886af
VZ
1563 }
1564
1565 // add as many ".." as needed
1566 size_t count = fnBase.m_dirs.GetCount();
1567 for ( size_t i = 0; i < count; i++ )
1568 {
1569 m_dirs.Insert(wxT(".."), 0u);
1570 }
90a68369 1571
2db991f4
VZ
1572 if ( format == wxPATH_UNIX || format == wxPATH_DOS )
1573 {
1574 // a directory made relative with respect to itself is '.' under Unix
1575 // and DOS, by definition (but we don't have to insert "./" for the
1576 // files)
1577 if ( m_dirs.IsEmpty() && IsDir() )
1578 {
9a83f860 1579 m_dirs.Add(wxT('.'));
0ea621cc 1580 }
2db991f4
VZ
1581 }
1582
f363e05c 1583 m_relative = true;
f7d886af
VZ
1584
1585 // we were modified
f363e05c 1586 return true;
f7d886af
VZ
1587}
1588
844f90fb
VZ
1589// ----------------------------------------------------------------------------
1590// filename kind tests
1591// ----------------------------------------------------------------------------
1592
2b5f62a0 1593bool wxFileName::SameAs(const wxFileName& filepath, wxPathFormat format) const
df5ddbca 1594{
844f90fb
VZ
1595 wxFileName fn1 = *this,
1596 fn2 = filepath;
1597
1598 // get cwd only once - small time saving
1599 wxString cwd = wxGetCwd();
55268a3a
VZ
1600 fn1.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
1601 fn2.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
844f90fb
VZ
1602
1603 if ( fn1.GetFullPath() == fn2.GetFullPath() )
f363e05c 1604 return true;
844f90fb
VZ
1605
1606 // TODO: compare inodes for Unix, this works even when filenames are
1607 // different but files are the same (symlinks) (VZ)
1608
f363e05c 1609 return false;
df5ddbca
RR
1610}
1611
844f90fb 1612/* static */
df5ddbca
RR
1613bool wxFileName::IsCaseSensitive( wxPathFormat format )
1614{
04c943b1
VZ
1615 // only Unix filenames are truely case-sensitive
1616 return GetFormat(format) == wxPATH_UNIX;
df5ddbca
RR
1617}
1618
f363e05c
VZ
1619/* static */
1620wxString wxFileName::GetForbiddenChars(wxPathFormat format)
1621{
1622 // Inits to forbidden characters that are common to (almost) all platforms.
1623 wxString strForbiddenChars = wxT("*?");
1624
1c6f2414 1625 // If asserts, wxPathFormat has been changed. In case of a new path format
f363e05c 1626 // addition, the following code might have to be updated.
1c6f2414 1627 wxCOMPILE_TIME_ASSERT(wxPATH_MAX == 5, wxPathFormatChanged);
f363e05c
VZ
1628 switch ( GetFormat(format) )
1629 {
1630 default :
1631 wxFAIL_MSG( wxT("Unknown path format") );
1632 // !! Fall through !!
1633
1634 case wxPATH_UNIX:
1635 break;
1636
1637 case wxPATH_MAC:
1638 // On a Mac even names with * and ? are allowed (Tested with OS
1639 // 9.2.1 and OS X 10.2.5)
1640 strForbiddenChars = wxEmptyString;
1641 break;
1642
1643 case wxPATH_DOS:
1644 strForbiddenChars += wxT("\\/:\"<>|");
1645 break;
1646
1647 case wxPATH_VMS:
1648 break;
1649 }
1650
1651 return strForbiddenChars;
1652}
1653
04c943b1 1654/* static */
bcfa2b31 1655wxString wxFileName::GetVolumeSeparator(wxPathFormat WXUNUSED_IN_WINCE(format))
844f90fb 1656{
bcfa2b31
WS
1657#ifdef __WXWINCE__
1658 return wxEmptyString;
1659#else
04c943b1
VZ
1660 wxString sepVol;
1661
a385b5df
RR
1662 if ( (GetFormat(format) == wxPATH_DOS) ||
1663 (GetFormat(format) == wxPATH_VMS) )
04c943b1 1664 {
04c943b1
VZ
1665 sepVol = wxFILE_SEP_DSK;
1666 }
a385b5df 1667 //else: leave empty
04c943b1
VZ
1668
1669 return sepVol;
bcfa2b31 1670#endif
844f90fb
VZ
1671}
1672
1673/* static */
1674wxString wxFileName::GetPathSeparators(wxPathFormat format)
1675{
1676 wxString seps;
1677 switch ( GetFormat(format) )
df5ddbca 1678 {
844f90fb 1679 case wxPATH_DOS:
04c943b1
VZ
1680 // accept both as native APIs do but put the native one first as
1681 // this is the one we use in GetFullPath()
1682 seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX;
844f90fb
VZ
1683 break;
1684
1685 default:
9a83f860 1686 wxFAIL_MSG( wxT("Unknown wxPATH_XXX style") );
844f90fb
VZ
1687 // fall through
1688
1689 case wxPATH_UNIX:
9e8d8607 1690 seps = wxFILE_SEP_PATH_UNIX;
844f90fb
VZ
1691 break;
1692
1693 case wxPATH_MAC:
9e8d8607 1694 seps = wxFILE_SEP_PATH_MAC;
844f90fb 1695 break;
04c943b1 1696
3c621059
JJ
1697 case wxPATH_VMS:
1698 seps = wxFILE_SEP_PATH_VMS;
1699 break;
df5ddbca
RR
1700 }
1701
844f90fb 1702 return seps;
df5ddbca
RR
1703}
1704
f1e77933
VZ
1705/* static */
1706wxString wxFileName::GetPathTerminators(wxPathFormat format)
1707{
1708 format = GetFormat(format);
1709
1710 // under VMS the end of the path is ']', not the path separator used to
1711 // separate the components
9a83f860 1712 return format == wxPATH_VMS ? wxString(wxT(']')) : GetPathSeparators(format);
f1e77933
VZ
1713}
1714
844f90fb
VZ
1715/* static */
1716bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
df5ddbca 1717{
04c943b1 1718 // wxString::Find() doesn't work as expected with NUL - it will always find
2fb5a4ce 1719 // it, so test for it separately
9a83f860 1720 return ch != wxT('\0') && GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
df5ddbca
RR
1721}
1722
844f90fb
VZ
1723// ----------------------------------------------------------------------------
1724// path components manipulation
1725// ----------------------------------------------------------------------------
1726
5bb9aeb2
VZ
1727/* static */ bool wxFileName::IsValidDirComponent(const wxString& dir)
1728{
1729 if ( dir.empty() )
1730 {
9a83f860 1731 wxFAIL_MSG( wxT("empty directory passed to wxFileName::InsertDir()") );
5bb9aeb2
VZ
1732
1733 return false;
1734 }
1735
1736 const size_t len = dir.length();
1737 for ( size_t n = 0; n < len; n++ )
1738 {
1739 if ( dir[n] == GetVolumeSeparator() || IsPathSeparator(dir[n]) )
1740 {
9a83f860 1741 wxFAIL_MSG( wxT("invalid directory component in wxFileName") );
5bb9aeb2
VZ
1742
1743 return false;
1744 }
1745 }
1746
1747 return true;
1748}
1749
2458d90b 1750void wxFileName::AppendDir( const wxString& dir )
df5ddbca 1751{
5bb9aeb2
VZ
1752 if ( IsValidDirComponent(dir) )
1753 m_dirs.Add( dir );
df5ddbca
RR
1754}
1755
2458d90b 1756void wxFileName::PrependDir( const wxString& dir )
df5ddbca 1757{
5bb9aeb2 1758 InsertDir(0, dir);
df5ddbca
RR
1759}
1760
2458d90b 1761void wxFileName::InsertDir(size_t before, const wxString& dir)
df5ddbca 1762{
5bb9aeb2 1763 if ( IsValidDirComponent(dir) )
2458d90b 1764 m_dirs.Insert(dir, before);
df5ddbca
RR
1765}
1766
2458d90b 1767void wxFileName::RemoveDir(size_t pos)
df5ddbca 1768{
2458d90b 1769 m_dirs.RemoveAt(pos);
df5ddbca
RR
1770}
1771
844f90fb
VZ
1772// ----------------------------------------------------------------------------
1773// accessors
1774// ----------------------------------------------------------------------------
1775
7124df9b
VZ
1776void wxFileName::SetFullName(const wxString& fullname)
1777{
dfecbee5
VZ
1778 SplitPath(fullname, NULL /* no volume */, NULL /* no path */,
1779 &m_name, &m_ext, &m_hasExt);
7124df9b
VZ
1780}
1781
844f90fb 1782wxString wxFileName::GetFullName() const
a35b27b1 1783{
844f90fb 1784 wxString fullname = m_name;
dfecbee5 1785 if ( m_hasExt )
a35b27b1 1786 {
9e8d8607 1787 fullname << wxFILE_SEP_EXT << m_ext;
a35b27b1 1788 }
a35b27b1 1789
844f90fb 1790 return fullname;
a35b27b1
RR
1791}
1792
33b97389 1793wxString wxFileName::GetPath( int flags, wxPathFormat format ) const
df5ddbca
RR
1794{
1795 format = GetFormat( format );
844f90fb 1796
353f41cb
RR
1797 wxString fullpath;
1798
33b97389
VZ
1799 // return the volume with the path as well if requested
1800 if ( flags & wxPATH_GET_VOLUME )
1801 {
1802 fullpath += wxGetVolumeString(GetVolume(), format);
1803 }
1804
034742fc
VZ
1805 // the leading character
1806 switch ( format )
1807 {
1808 case wxPATH_MAC:
1809 if ( m_relative )
1810 fullpath += wxFILE_SEP_PATH_MAC;
1811 break;
1812
1813 case wxPATH_DOS:
1814 if ( !m_relative )
1815 fullpath += wxFILE_SEP_PATH_DOS;
1816 break;
1817
1818 default:
1819 wxFAIL_MSG( wxT("Unknown path format") );
1820 // fall through
1821
1822 case wxPATH_UNIX:
1823 if ( !m_relative )
1824 {
be5be16a 1825 fullpath += wxFILE_SEP_PATH_UNIX;
034742fc
VZ
1826 }
1827 break;
1828
1829 case wxPATH_VMS:
1830 // no leading character here but use this place to unset
1831 // wxPATH_GET_SEPARATOR flag: under VMS it doesn't make sense
1832 // as, if I understand correctly, there should never be a dot
1833 // before the closing bracket
1834 flags &= ~wxPATH_GET_SEPARATOR;
1835 }
1836
1837 if ( m_dirs.empty() )
1838 {
1839 // there is nothing more
1840 return fullpath;
1841 }
1842
1843 // then concatenate all the path components using the path separator
1844 if ( format == wxPATH_VMS )
1845 {
1846 fullpath += wxT('[');
1847 }
1848
5bb9aeb2 1849 const size_t dirCount = m_dirs.GetCount();
034742fc 1850 for ( size_t i = 0; i < dirCount; i++ )
353f41cb 1851 {
034742fc 1852 switch (format)
5bb9aeb2
VZ
1853 {
1854 case wxPATH_MAC:
034742fc
VZ
1855 if ( m_dirs[i] == wxT(".") )
1856 {
1857 // skip appending ':', this shouldn't be done in this
1858 // case as "::" is interpreted as ".." under Unix
1859 continue;
1860 }
2361ce82 1861
034742fc 1862 // convert back from ".." to nothing
489f6cf7 1863 if ( !m_dirs[i].IsSameAs(wxT("..")) )
034742fc 1864 fullpath += m_dirs[i];
5bb9aeb2 1865 break;
2361ce82 1866
5bb9aeb2 1867 default:
034742fc
VZ
1868 wxFAIL_MSG( wxT("Unexpected path format") );
1869 // still fall through
2361ce82 1870
034742fc 1871 case wxPATH_DOS:
5bb9aeb2 1872 case wxPATH_UNIX:
034742fc 1873 fullpath += m_dirs[i];
5bb9aeb2 1874 break;
2361ce82 1875
5bb9aeb2 1876 case wxPATH_VMS:
034742fc 1877 // TODO: What to do with ".." under VMS
353f41cb 1878
034742fc 1879 // convert back from ".." to nothing
489f6cf7 1880 if ( !m_dirs[i].IsSameAs(wxT("..")) )
353f41cb 1881 fullpath += m_dirs[i];
034742fc 1882 break;
4175794e
VZ
1883 }
1884
034742fc
VZ
1885 if ( (flags & wxPATH_GET_SEPARATOR) || (i != dirCount - 1) )
1886 fullpath += GetPathSeparator(format);
df5ddbca 1887 }
034742fc
VZ
1888
1889 if ( format == wxPATH_VMS )
5bb9aeb2 1890 {
034742fc 1891 fullpath += wxT(']');
5bb9aeb2 1892 }
844f90fb 1893
353f41cb 1894 return fullpath;
df5ddbca
RR
1895}
1896
1897wxString wxFileName::GetFullPath( wxPathFormat format ) const
1898{
4175794e
VZ
1899 // we already have a function to get the path
1900 wxString fullpath = GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR,
1901 format);
04c943b1 1902
4175794e 1903 // now just add the file name and extension to it
04c943b1
VZ
1904 fullpath += GetFullName();
1905
1906 return fullpath;
df5ddbca
RR
1907}
1908
9e9b65c1
JS
1909// Return the short form of the path (returns identity on non-Windows platforms)
1910wxString wxFileName::GetShortPath() const
1911{
9e9b65c1 1912 wxString path(GetFullPath());
2f3d9587
VZ
1913
1914#if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
e0a050e3 1915 DWORD sz = ::GetShortPathName(path.fn_str(), NULL, 0);
2f3d9587 1916 if ( sz != 0 )
9e9b65c1 1917 {
2f3d9587
VZ
1918 wxString pathOut;
1919 if ( ::GetShortPathName
75ef5722 1920 (
e0a050e3 1921 path.fn_str(),
de564874 1922 wxStringBuffer(pathOut, sz),
75ef5722 1923 sz
2f3d9587
VZ
1924 ) != 0 )
1925 {
1926 return pathOut;
1927 }
9e9b65c1 1928 }
2f3d9587 1929#endif // Windows
5716a1ab
VZ
1930
1931 return path;
9e9b65c1
JS
1932}
1933
1934// Return the long form of the path (returns identity on non-Windows platforms)
1935wxString wxFileName::GetLongPath() const
1936{
52dbd056
VZ
1937 wxString pathOut,
1938 path = GetFullPath();
1939
b517351b 1940#if defined(__WIN32__) && !defined(__WXWINCE__) && !defined(__WXMICROWIN__)
05e7001c 1941
0e207280 1942#if wxUSE_DYNLIB_CLASS
62dcaed6 1943 typedef DWORD (WINAPI *GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
05e7001c 1944
2f3d9587
VZ
1945 // this is MT-safe as in the worst case we're going to resolve the function
1946 // twice -- but as the result is the same in both threads, it's ok
1947 static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
1948 if ( !s_pfnGetLongPathName )
9e9b65c1 1949 {
2f3d9587 1950 static bool s_triedToLoad = false;
2361ce82 1951
2f3d9587 1952 if ( !s_triedToLoad )
05e7001c 1953 {
2f3d9587
VZ
1954 s_triedToLoad = true;
1955
9a83f860 1956 wxDynamicLibrary dllKernel(wxT("kernel32"));
2f3d9587 1957
9a83f860 1958 const wxChar* GetLongPathName = wxT("GetLongPathName")
4377d3ab 1959#if wxUSE_UNICODE
9a83f860 1960 wxT("W");
4377d3ab 1961#else // ANSI
9a83f860 1962 wxT("A");
4377d3ab
WS
1963#endif // Unicode/ANSI
1964
1965 if ( dllKernel.HasSymbol(GetLongPathName) )
05e7001c 1966 {
2f3d9587 1967 s_pfnGetLongPathName = (GET_LONG_PATH_NAME)
4377d3ab 1968 dllKernel.GetSymbol(GetLongPathName);
05e7001c 1969 }
2f3d9587
VZ
1970
1971 // note that kernel32.dll can be unloaded, it stays in memory
1972 // anyhow as all Win32 programs link to it and so it's safe to call
1973 // GetLongPathName() even after unloading it
05e7001c 1974 }
9e9b65c1 1975 }
2361ce82 1976
2f3d9587
VZ
1977 if ( s_pfnGetLongPathName )
1978 {
e0a050e3 1979 DWORD dwSize = (*s_pfnGetLongPathName)(path.fn_str(), NULL, 0);
2f3d9587
VZ
1980 if ( dwSize > 0 )
1981 {
1982 if ( (*s_pfnGetLongPathName)
1983 (
e0a050e3 1984 path.fn_str(),
2f3d9587
VZ
1985 wxStringBuffer(pathOut, dwSize),
1986 dwSize
1987 ) != 0 )
1988 {
1989 return pathOut;
1990 }
1991 }
1992 }
0e207280 1993#endif // wxUSE_DYNLIB_CLASS
05e7001c 1994
2f3d9587
VZ
1995 // The OS didn't support GetLongPathName, or some other error.
1996 // We need to call FindFirstFile on each component in turn.
05e7001c 1997
2f3d9587
VZ
1998 WIN32_FIND_DATA findFileData;
1999 HANDLE hFind;
b5b62eea 2000
2f3d9587
VZ
2001 if ( HasVolume() )
2002 pathOut = GetVolume() +
2003 GetVolumeSeparator(wxPATH_DOS) +
2004 GetPathSeparator(wxPATH_DOS);
2005 else
2006 pathOut = wxEmptyString;
05e7001c 2007
2f3d9587
VZ
2008 wxArrayString dirs = GetDirs();
2009 dirs.Add(GetFullName());
77fe02a8 2010
2f3d9587 2011 wxString tmpPath;
5d978d07 2012
2f3d9587
VZ
2013 size_t count = dirs.GetCount();
2014 for ( size_t i = 0; i < count; i++ )
2015 {
ea6319cb
VZ
2016 const wxString& dir = dirs[i];
2017
2f3d9587
VZ
2018 // We're using pathOut to collect the long-name path, but using a
2019 // temporary for appending the last path component which may be
2020 // short-name
ea6319cb
VZ
2021 tmpPath = pathOut + dir;
2022
2023 // We must not process "." or ".." here as they would be (unexpectedly)
2024 // replaced by the corresponding directory names so just leave them
2025 // alone
2026 //
2027 // And we can't pass a drive and root dir to FindFirstFile (VZ: why?)
2028 if ( tmpPath.empty() || dir == '.' || dir == ".." ||
2029 tmpPath.Last() == GetVolumeSeparator(wxPATH_DOS) )
2f3d9587 2030 {
2f3d9587
VZ
2031 tmpPath += wxFILE_SEP_PATH;
2032 pathOut = tmpPath;
2033 continue;
2034 }
05e7001c 2035
e0a050e3 2036 hFind = ::FindFirstFile(tmpPath.fn_str(), &findFileData);
2f3d9587
VZ
2037 if (hFind == INVALID_HANDLE_VALUE)
2038 {
2039 // Error: most likely reason is that path doesn't exist, so
2040 // append any unprocessed parts and return
2041 for ( i += 1; i < count; i++ )
2042 tmpPath += wxFILE_SEP_PATH + dirs[i];
b5b62eea 2043
2f3d9587
VZ
2044 return tmpPath;
2045 }
05e7001c 2046
2f3d9587
VZ
2047 pathOut += findFileData.cFileName;
2048 if ( (i < (count-1)) )
2049 pathOut += wxFILE_SEP_PATH;
52dbd056 2050
2f3d9587 2051 ::FindClose(hFind);
05e7001c 2052 }
52dbd056
VZ
2053#else // !Win32
2054 pathOut = path;
2055#endif // Win32/!Win32
5716a1ab 2056
05e7001c 2057 return pathOut;
9e9b65c1
JS
2058}
2059
df5ddbca
RR
2060wxPathFormat wxFileName::GetFormat( wxPathFormat format )
2061{
2062 if (format == wxPATH_NATIVE)
2063 {
5a3912f2 2064#if defined(__WXMSW__) || defined(__OS2__) || defined(__DOS__)
df5ddbca 2065 format = wxPATH_DOS;
3c621059 2066#elif defined(__VMS)
04c943b1 2067 format = wxPATH_VMS;
844f90fb 2068#else
df5ddbca
RR
2069 format = wxPATH_UNIX;
2070#endif
2071 }
2072 return format;
2073}
a35b27b1 2074
35c2aa4f
VZ
2075#ifdef wxHAS_FILESYSTEM_VOLUMES
2076
2077/* static */
2078wxString wxFileName::GetVolumeString(char drive, int flags)
2079{
2080 wxASSERT_MSG( !(flags & ~wxPATH_GET_SEPARATOR), "invalid flag specified" );
2081
2082 wxString vol(drive);
2083 vol += wxFILE_SEP_DSK;
2084 if ( flags & wxPATH_GET_SEPARATOR )
2085 vol += wxFILE_SEP_PATH;
2086
2087 return vol;
2088}
2089
2090#endif // wxHAS_FILESYSTEM_VOLUMES
2091
9e8d8607
VZ
2092// ----------------------------------------------------------------------------
2093// path splitting function
2094// ----------------------------------------------------------------------------
2095
6f91bc33 2096/* static */
f1e77933
VZ
2097void
2098wxFileName::SplitVolume(const wxString& fullpathWithVolume,
2099 wxString *pstrVolume,
2100 wxString *pstrPath,
2101 wxPathFormat format)
9e8d8607
VZ
2102{
2103 format = GetFormat(format);
2104
04c943b1
VZ
2105 wxString fullpath = fullpathWithVolume;
2106
04c943b1 2107 // special Windows UNC paths hack: transform \\share\path into share:path
9e1c7236 2108 if ( IsUNCPath(fullpath, format) )
9e8d8607 2109 {
9e1c7236 2110 fullpath.erase(0, 2);
04c943b1 2111
9e1c7236
VZ
2112 size_t posFirstSlash =
2113 fullpath.find_first_of(GetPathTerminators(format));
2114 if ( posFirstSlash != wxString::npos )
2115 {
2116 fullpath[posFirstSlash] = wxFILE_SEP_DSK;
04c943b1 2117
9e1c7236
VZ
2118 // UNC paths are always absolute, right? (FIXME)
2119 fullpath.insert(posFirstSlash + 1, 1, wxFILE_SEP_PATH_DOS);
3c621059
JJ
2120 }
2121 }
04c943b1 2122
a385b5df
RR
2123 // We separate the volume here
2124 if ( format == wxPATH_DOS || format == wxPATH_VMS )
04c943b1 2125 {
a385b5df 2126 wxString sepVol = GetVolumeSeparator(format);
24eb81cb 2127
04c943b1
VZ
2128 size_t posFirstColon = fullpath.find_first_of(sepVol);
2129 if ( posFirstColon != wxString::npos )
2130 {
2131 if ( pstrVolume )
2132 {
2133 *pstrVolume = fullpath.Left(posFirstColon);
2134 }
2135
2136 // remove the volume name and the separator from the full path
2137 fullpath.erase(0, posFirstColon + sepVol.length());
2138 }
2139 }
2140
f1e77933
VZ
2141 if ( pstrPath )
2142 *pstrPath = fullpath;
2143}
2144
2145/* static */
2146void wxFileName::SplitPath(const wxString& fullpathWithVolume,
2147 wxString *pstrVolume,
2148 wxString *pstrPath,
2149 wxString *pstrName,
2150 wxString *pstrExt,
dfecbee5 2151 bool *hasExt,
f1e77933
VZ
2152 wxPathFormat format)
2153{
2154 format = GetFormat(format);
2155
2156 wxString fullpath;
2157 SplitVolume(fullpathWithVolume, pstrVolume, &fullpath, format);
2158
04c943b1
VZ
2159 // find the positions of the last dot and last path separator in the path
2160 size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
f1e77933 2161 size_t posLastSlash = fullpath.find_last_of(GetPathTerminators(format));
04c943b1 2162
e5a573a2 2163 // check whether this dot occurs at the very beginning of a path component
04c943b1 2164 if ( (posLastDot != wxString::npos) &&
e5a573a2
VZ
2165 (posLastDot == 0 ||
2166 IsPathSeparator(fullpath[posLastDot - 1]) ||
9a83f860 2167 (format == wxPATH_VMS && fullpath[posLastDot - 1] == wxT(']'))) )
f1e77933
VZ
2168 {
2169 // dot may be (and commonly -- at least under Unix -- is) the first
2170 // character of the filename, don't treat the entire filename as
2171 // extension in this case
2172 posLastDot = wxString::npos;
2173 }
9e8d8607 2174
8e7dda21
VZ
2175 // if we do have a dot and a slash, check that the dot is in the name part
2176 if ( (posLastDot != wxString::npos) &&
2177 (posLastSlash != wxString::npos) &&
2178 (posLastDot < posLastSlash) )
9e8d8607
VZ
2179 {
2180 // the dot is part of the path, not the start of the extension
2181 posLastDot = wxString::npos;
2182 }
2183
2184 // now fill in the variables provided by user
2185 if ( pstrPath )
2186 {
2187 if ( posLastSlash == wxString::npos )
2188 {
2189 // no path at all
2190 pstrPath->Empty();
2191 }
2192 else
2193 {
04c943b1 2194 // take everything up to the path separator but take care to make
353f41cb 2195 // the path equal to something like '/', not empty, for the files
04c943b1
VZ
2196 // immediately under root directory
2197 size_t len = posLastSlash;
91b4bd63
SC
2198
2199 // this rule does not apply to mac since we do not start with colons (sep)
2200 // except for relative paths
2201 if ( !len && format != wxPATH_MAC)
04c943b1
VZ
2202 len++;
2203
2204 *pstrPath = fullpath.Left(len);
2205
2206 // special VMS hack: remove the initial bracket
2207 if ( format == wxPATH_VMS )
2208 {
9a83f860 2209 if ( (*pstrPath)[0u] == wxT('[') )
04c943b1
VZ
2210 pstrPath->erase(0, 1);
2211 }
9e8d8607
VZ
2212 }
2213 }
2214
2215 if ( pstrName )
2216 {
42b1f941
VZ
2217 // take all characters starting from the one after the last slash and
2218 // up to, but excluding, the last dot
9e8d8607 2219 size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
8e7dda21
VZ
2220 size_t count;
2221 if ( posLastDot == wxString::npos )
2222 {
2223 // take all until the end
2224 count = wxString::npos;
2225 }
2226 else if ( posLastSlash == wxString::npos )
2227 {
2228 count = posLastDot;
2229 }
2230 else // have both dot and slash
2231 {
2232 count = posLastDot - posLastSlash - 1;
2233 }
9e8d8607
VZ
2234
2235 *pstrName = fullpath.Mid(nStart, count);
2236 }
2237
dfecbee5
VZ
2238 // finally deal with the extension here: we have an added complication that
2239 // extension may be empty (but present) as in "foo." where trailing dot
2240 // indicates the empty extension at the end -- and hence we must remember
2241 // that we have it independently of pstrExt
2242 if ( posLastDot == wxString::npos )
9e8d8607 2243 {
dfecbee5
VZ
2244 // no extension
2245 if ( pstrExt )
2246 pstrExt->clear();
2247 if ( hasExt )
2248 *hasExt = false;
2249 }
2250 else
2251 {
2252 // take everything after the dot
2253 if ( pstrExt )
9e8d8607 2254 *pstrExt = fullpath.Mid(posLastDot + 1);
dfecbee5
VZ
2255 if ( hasExt )
2256 *hasExt = true;
9e8d8607
VZ
2257 }
2258}
951cd180 2259
6f91bc33
VZ
2260/* static */
2261void wxFileName::SplitPath(const wxString& fullpath,
2262 wxString *path,
2263 wxString *name,
2264 wxString *ext,
2265 wxPathFormat format)
2266{
2267 wxString volume;
2268 SplitPath(fullpath, &volume, path, name, ext, format);
2269
67c34f64 2270 if ( path )
6f91bc33 2271 {
67c34f64 2272 path->Prepend(wxGetVolumeString(volume, format));
6f91bc33
VZ
2273 }
2274}
2275
181dd701
VZ
2276/* static */
2277wxString wxFileName::StripExtension(const wxString& fullpath)
2278{
2279 wxFileName fn(fullpath);
2280 fn.SetExt("");
2281 return fn.GetFullPath();
2282}
2283
951cd180
VZ
2284// ----------------------------------------------------------------------------
2285// time functions
2286// ----------------------------------------------------------------------------
2287
e2b87f38
VZ
2288#if wxUSE_DATETIME
2289
6dbb903b
VZ
2290bool wxFileName::SetTimes(const wxDateTime *dtAccess,
2291 const wxDateTime *dtMod,
d9e80dce 2292 const wxDateTime *dtCreate) const
951cd180 2293{
2b5f62a0 2294#if defined(__WIN32__)
6bc176b4
VZ
2295 FILETIME ftAccess, ftCreate, ftWrite;
2296
2297 if ( dtCreate )
2298 ConvertWxToFileTime(&ftCreate, *dtCreate);
2299 if ( dtAccess )
2300 ConvertWxToFileTime(&ftAccess, *dtAccess);
2301 if ( dtMod )
2302 ConvertWxToFileTime(&ftWrite, *dtMod);
2303
2304 wxString path;
2305 int flags;
2b5f62a0
VZ
2306 if ( IsDir() )
2307 {
6bc176b4
VZ
2308 if ( wxGetOsVersion() == wxOS_WINDOWS_9X )
2309 {
2310 wxLogError(_("Setting directory access times is not supported "
2311 "under this OS version"));
2312 return false;
2313 }
2314
2315 path = GetPath();
2316 flags = FILE_FLAG_BACKUP_SEMANTICS;
2b5f62a0
VZ
2317 }
2318 else // file
2319 {
6bc176b4
VZ
2320 path = GetFullPath();
2321 flags = 0;
2322 }
2323
2324 wxFileHandle fh(path, wxFileHandle::Write, flags);
2325 if ( fh.IsOk() )
2326 {
2327 if ( ::SetFileTime(fh,
2328 dtCreate ? &ftCreate : NULL,
2329 dtAccess ? &ftAccess : NULL,
2330 dtMod ? &ftWrite : NULL) )
2b5f62a0 2331 {
6bc176b4 2332 return true;
2b5f62a0
VZ
2333 }
2334 }
2335#elif defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__))
17a1ebd1
VZ
2336 wxUnusedVar(dtCreate);
2337
246c704f
VZ
2338 if ( !dtAccess && !dtMod )
2339 {
2340 // can't modify the creation time anyhow, don't try
f363e05c 2341 return true;
246c704f
VZ
2342 }
2343
2344 // if dtAccess or dtMod is not specified, use the other one (which must be
2345 // non NULL because of the test above) for both times
951cd180 2346 utimbuf utm;
246c704f
VZ
2347 utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
2348 utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
401eb3de 2349 if ( utime(GetFullPath().fn_str(), &utm) == 0 )
951cd180 2350 {
f363e05c 2351 return true;
951cd180 2352 }
951cd180 2353#else // other platform
7a893a31
WS
2354 wxUnusedVar(dtAccess);
2355 wxUnusedVar(dtMod);
2356 wxUnusedVar(dtCreate);
951cd180
VZ
2357#endif // platforms
2358
2359 wxLogSysError(_("Failed to modify file times for '%s'"),
2360 GetFullPath().c_str());
2361
f363e05c 2362 return false;
951cd180
VZ
2363}
2364
d9e80dce 2365bool wxFileName::Touch() const
951cd180
VZ
2366{
2367#if defined(__UNIX_LIKE__)
2368 // under Unix touching file is simple: just pass NULL to utime()
401eb3de 2369 if ( utime(GetFullPath().fn_str(), NULL) == 0 )
951cd180 2370 {
f363e05c 2371 return true;
951cd180
VZ
2372 }
2373
2374 wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str());
2375
f363e05c 2376 return false;
951cd180
VZ
2377#else // other platform
2378 wxDateTime dtNow = wxDateTime::Now();
2379
6dbb903b 2380 return SetTimes(&dtNow, &dtNow, NULL /* don't change create time */);
951cd180
VZ
2381#endif // platforms
2382}
2383
2384bool wxFileName::GetTimes(wxDateTime *dtAccess,
2385 wxDateTime *dtMod,
6dbb903b 2386 wxDateTime *dtCreate) const
951cd180 2387{
2b5f62a0
VZ
2388#if defined(__WIN32__)
2389 // we must use different methods for the files and directories under
2390 // Windows as CreateFile(GENERIC_READ) doesn't work for the directories and
2391 // CreateFile(FILE_FLAG_BACKUP_SEMANTICS) works -- but only under NT and
2392 // not 9x
2393 bool ok;
2394 FILETIME ftAccess, ftCreate, ftWrite;
9a0c5c01 2395 if ( IsDir() )
2b5f62a0
VZ
2396 {
2397 // implemented in msw/dir.cpp
2398 extern bool wxGetDirectoryTimes(const wxString& dirname,
2399 FILETIME *, FILETIME *, FILETIME *);
2400
2401 // we should pass the path without the trailing separator to
2402 // wxGetDirectoryTimes()
2403 ok = wxGetDirectoryTimes(GetPath(wxPATH_GET_VOLUME),
2404 &ftAccess, &ftCreate, &ftWrite);
2405 }
2406 else // file
2407 {
2408 wxFileHandle fh(GetFullPath(), wxFileHandle::Read);
2409 if ( fh.IsOk() )
2410 {
2411 ok = ::GetFileTime(fh,
2412 dtCreate ? &ftCreate : NULL,
2413 dtAccess ? &ftAccess : NULL,
2414 dtMod ? &ftWrite : NULL) != 0;
2415 }
2416 else
2417 {
f363e05c 2418 ok = false;
2b5f62a0
VZ
2419 }
2420 }
2421
2422 if ( ok )
2423 {
2424 if ( dtCreate )
2425 ConvertFileTimeToWx(dtCreate, ftCreate);
2426 if ( dtAccess )
2427 ConvertFileTimeToWx(dtAccess, ftAccess);
2428 if ( dtMod )
2429 ConvertFileTimeToWx(dtMod, ftWrite);
2430
f363e05c 2431 return true;
2b5f62a0 2432 }
bf58daba 2433#elif defined(__UNIX_LIKE__) || defined(__WXMAC__) || defined(__OS2__) || (defined(__DOS__) && defined(__WATCOMC__))
51cb1a65 2434 // no need to test for IsDir() here
951cd180 2435 wxStructStat stBuf;
92980e90 2436 if ( wxStat( GetFullPath().c_str(), &stBuf) == 0 )
951cd180
VZ
2437 {
2438 if ( dtAccess )
2439 dtAccess->Set(stBuf.st_atime);
2440 if ( dtMod )
2441 dtMod->Set(stBuf.st_mtime);
6dbb903b
VZ
2442 if ( dtCreate )
2443 dtCreate->Set(stBuf.st_ctime);
951cd180 2444
f363e05c 2445 return true;
951cd180 2446 }
951cd180 2447#else // other platform
7a893a31
WS
2448 wxUnusedVar(dtAccess);
2449 wxUnusedVar(dtMod);
2450 wxUnusedVar(dtCreate);
951cd180
VZ
2451#endif // platforms
2452
2453 wxLogSysError(_("Failed to retrieve file times for '%s'"),
2454 GetFullPath().c_str());
2455
f363e05c 2456 return false;
951cd180
VZ
2457}
2458
e2b87f38
VZ
2459#endif // wxUSE_DATETIME
2460
23b8a262
JS
2461
2462// ----------------------------------------------------------------------------
2463// file size functions
2464// ----------------------------------------------------------------------------
2465
bd08f2f7
VZ
2466#if wxUSE_LONGLONG
2467
23b8a262
JS
2468/* static */
2469wxULongLong wxFileName::GetSize(const wxString &filename)
2470{
2471 if (!wxFileExists(filename))
2472 return wxInvalidSize;
2473
cdbce971
WS
2474#if defined(__WXPALMOS__)
2475 // TODO
2476 return wxInvalidSize;
2477#elif defined(__WIN32__)
23b8a262
JS
2478 wxFileHandle f(filename, wxFileHandle::Read);
2479 if (!f.IsOk())
2480 return wxInvalidSize;
2481
2482 DWORD lpFileSizeHigh;
2483 DWORD ret = GetFileSize(f, &lpFileSizeHigh);
5ab9534b 2484 if ( ret == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR )
23b8a262
JS
2485 return wxInvalidSize;
2486
5ab9534b
VZ
2487 return wxULongLong(lpFileSizeHigh, ret);
2488#else // ! __WIN32__
23b8a262
JS
2489 wxStructStat st;
2490#ifndef wxNEED_WX_UNISTD_H
2491 if (wxStat( filename.fn_str() , &st) != 0)
2492#else
2493 if (wxStat( filename, &st) != 0)
2494#endif
2495 return wxInvalidSize;
2496 return wxULongLong(st.st_size);
2497#endif
2498}
2499
2500/* static */
2501wxString wxFileName::GetHumanReadableSize(const wxULongLong &bs,
2502 const wxString &nullsize,
2503 int precision)
2504{
2505 static const double KILOBYTESIZE = 1024.0;
2506 static const double MEGABYTESIZE = 1024.0*KILOBYTESIZE;
2507 static const double GIGABYTESIZE = 1024.0*MEGABYTESIZE;
2508 static const double TERABYTESIZE = 1024.0*GIGABYTESIZE;
2509
2510 if (bs == 0 || bs == wxInvalidSize)
2511 return nullsize;
2512
2513 double bytesize = bs.ToDouble();
2514 if (bytesize < KILOBYTESIZE)
2515 return wxString::Format(_("%s B"), bs.ToString().c_str());
2516 if (bytesize < MEGABYTESIZE)
2517 return wxString::Format(_("%.*f kB"), precision, bytesize/KILOBYTESIZE);
2518 if (bytesize < GIGABYTESIZE)
2519 return wxString::Format(_("%.*f MB"), precision, bytesize/MEGABYTESIZE);
2520 if (bytesize < TERABYTESIZE)
2521 return wxString::Format(_("%.*f GB"), precision, bytesize/GIGABYTESIZE);
2522
2523 return wxString::Format(_("%.*f TB"), precision, bytesize/TERABYTESIZE);
2524}
2525
2526wxULongLong wxFileName::GetSize() const
2527{
2528 return GetSize(GetFullPath());
2529}
2530
2531wxString wxFileName::GetHumanReadableSize(const wxString &failmsg, int precision) const
2532{
2533 return GetHumanReadableSize(GetSize(), failmsg, precision);
2534}
2535
bd08f2f7 2536#endif // wxUSE_LONGLONG
23b8a262
JS
2537
2538// ----------------------------------------------------------------------------
2539// Mac-specific functions
2540// ----------------------------------------------------------------------------
2541
26eef304 2542#if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
4dfbcdd5 2543
56ce942b
VZ
2544namespace
2545{
2546
0ca4ae93 2547class MacDefaultExtensionRecord
4dfbcdd5 2548{
56ce942b
VZ
2549public:
2550 MacDefaultExtensionRecord()
2551 {
2552 m_type =
2553 m_creator = 0 ;
2554 }
4dfbcdd5 2555
56ce942b 2556 // default copy ctor, assignment operator and dtor are ok
0ca4ae93 2557
56ce942b
VZ
2558 MacDefaultExtensionRecord(const wxString& ext, OSType type, OSType creator)
2559 : m_ext(ext)
2560 {
2561 m_type = type;
2562 m_creator = creator;
2563 }
2564
2565 wxString m_ext;
2566 OSType m_type;
2567 OSType m_creator;
2568};
2569
2570WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord, MacDefaultExtensionArray);
2571
2572bool gMacDefaultExtensionsInited = false;
0ca4ae93 2573
4dfbcdd5 2574#include "wx/arrimpl.cpp"
0ca4ae93 2575
56ce942b 2576WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray);
4dfbcdd5 2577
56ce942b 2578MacDefaultExtensionArray gMacDefaultExtensions;
4dfbcdd5 2579
5974c3cf 2580// load the default extensions
56ce942b 2581const MacDefaultExtensionRecord gDefaults[] =
4dfbcdd5 2582{
56ce942b
VZ
2583 MacDefaultExtensionRecord( "txt", 'TEXT', 'ttxt' ),
2584 MacDefaultExtensionRecord( "tif", 'TIFF', '****' ),
2585 MacDefaultExtensionRecord( "jpg", 'JPEG', '****' ),
2586};
0ea621cc 2587
56ce942b 2588void MacEnsureDefaultExtensionsLoaded()
5974c3cf
SC
2589{
2590 if ( !gMacDefaultExtensionsInited )
4dfbcdd5 2591 {
5974c3cf
SC
2592 // we could load the pc exchange prefs here too
2593 for ( size_t i = 0 ; i < WXSIZEOF( gDefaults ) ; ++i )
2594 {
2595 gMacDefaultExtensions.Add( gDefaults[i] ) ;
2596 }
56ce942b 2597 gMacDefaultExtensionsInited = true;
0ea621cc 2598 }
4dfbcdd5 2599}
a2b77260 2600
56ce942b
VZ
2601} // anonymous namespace
2602
0ea621cc 2603bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
4dfbcdd5 2604{
a2b77260
SC
2605 FSRef fsRef ;
2606 FSCatalogInfo catInfo;
2607 FileInfo *finfo ;
4dfbcdd5 2608
a2b77260
SC
2609 if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
2610 {
a62848fd
WS
2611 if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
2612 {
2613 finfo = (FileInfo*)&catInfo.finderInfo;
2614 finfo->fileType = type ;
2615 finfo->fileCreator = creator ;
2616 FSSetCatalogInfo( &fsRef, kFSCatInfoFinderInfo, &catInfo ) ;
a2b77260 2617 return true ;
a62848fd 2618 }
a2b77260
SC
2619 }
2620 return false ;
4dfbcdd5
SC
2621}
2622
d9e80dce 2623bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator ) const
4dfbcdd5 2624{
a2b77260
SC
2625 FSRef fsRef ;
2626 FSCatalogInfo catInfo;
2627 FileInfo *finfo ;
4dfbcdd5 2628
a2b77260
SC
2629 if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
2630 {
a62848fd
WS
2631 if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
2632 {
2633 finfo = (FileInfo*)&catInfo.finderInfo;
2634 *type = finfo->fileType ;
2635 *creator = finfo->fileCreator ;
a2b77260 2636 return true ;
a62848fd 2637 }
a2b77260
SC
2638 }
2639 return false ;
4dfbcdd5
SC
2640}
2641
2642bool wxFileName::MacSetDefaultTypeAndCreator()
2643{
2644 wxUint32 type , creator ;
2645 if ( wxFileName::MacFindDefaultTypeAndCreator(GetExt() , &type ,
2646 &creator ) )
2647 {
f63fb56d
GD
2648 return MacSetTypeAndCreator( type , creator ) ;
2649 }
2650 return false;
4dfbcdd5
SC
2651}
2652
0ea621cc 2653bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
4dfbcdd5
SC
2654{
2655 MacEnsureDefaultExtensionsLoaded() ;
2656 wxString extl = ext.Lower() ;
2657 for( int i = gMacDefaultExtensions.Count() - 1 ; i >= 0 ; --i )
2658 {
2659 if ( gMacDefaultExtensions.Item(i).m_ext == extl )
2660 {
2661 *type = gMacDefaultExtensions.Item(i).m_type ;
2662 *creator = gMacDefaultExtensions.Item(i).m_creator ;
2663 return true ;
2664 }
2665 }
2666 return false ;
2667}
2668
2669void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )
2670{
56ce942b
VZ
2671 MacEnsureDefaultExtensionsLoaded();
2672 MacDefaultExtensionRecord rec(ext.Lower(), type, creator);
2673 gMacDefaultExtensions.Add( rec );
4dfbcdd5 2674}
56ce942b
VZ
2675
2676#endif // defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON