]> git.saurik.com Git - wxWidgets.git/blame - src/common/filename.cpp
wxDateTime::IsValid() now returns m_time != (wxLongLong)-1
[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
097ead30
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
df5ddbca
RR
20#ifdef __GNUG__
21 #pragma implementation "filename.h"
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
28 #pragma hdrstop
29#endif
30
31#ifndef WX_PRECOMP
32 #include "wx/intl.h"
33 #include "wx/log.h"
34#endif
35
36#include "wx/filename.h"
37#include "wx/tokenzr.h"
844f90fb 38#include "wx/config.h" // for wxExpandEnvVars
a35b27b1 39#include "wx/utils.h"
df5ddbca 40
097ead30
VZ
41// ============================================================================
42// implementation
43// ============================================================================
44
45// ----------------------------------------------------------------------------
844f90fb 46// wxFileName construction
097ead30 47// ----------------------------------------------------------------------------
df5ddbca 48
a35b27b1
RR
49void wxFileName::Assign( const wxFileName &filepath )
50{
a35b27b1
RR
51 m_ext = filepath.GetExt();
52 m_name = filepath.GetName();
844f90fb 53 m_dirs = filepath.GetDirs();
df5ddbca
RR
54}
55
844f90fb
VZ
56void wxFileName::Assign( const wxString& path,
57 const wxString& name,
58 const wxString& ext,
59 wxPathFormat format )
df5ddbca 60{
844f90fb
VZ
61 wxStringTokenizer tn(path, GetPathSeparators(format),
62 wxTOKEN_RET_EMPTY_ALL);
4fdfb558 63 bool first = TRUE;
844f90fb
VZ
64 m_dirs.Clear();
65 while ( tn.HasMoreTokens() )
df5ddbca 66 {
844f90fb
VZ
67 wxString token = tn.GetNextToken();
68
4fdfb558
RR
69 // If the path starts with a slash, we need the first
70 // dir entry to be an empty for later reassembly.
4fdfb558 71 if (first || !token.IsEmpty())
df5ddbca 72 m_dirs.Add( token );
844f90fb 73
4fdfb558 74 first = FALSE;
df5ddbca 75 }
844f90fb
VZ
76
77 m_ext = ext;
78 m_name = name;
79}
80
81void wxFileName::Assign(const wxString& fullpath,
82 wxPathFormat format)
83{
84 wxString path, name, ext;
9e8d8607 85 SplitPath(fullpath, &path, &name, &ext, format);
844f90fb
VZ
86
87 Assign(path, name, ext, format);
88}
89
90void wxFileName::Assign(const wxString& path,
91 const wxString& fullname,
92 wxPathFormat format)
93{
94 wxString name, ext;
9e8d8607 95 SplitPath(fullname, NULL /* no path */, &name, &ext, format);
844f90fb
VZ
96
97 Assign(path, name, ext, format);
98}
99
100void wxFileName::Clear()
101{
102 m_dirs.Clear();
103 m_name =
104 m_ext = wxEmptyString;
105}
106
107/* static */
108wxFileName wxFileName::FileName(const wxString& file)
109{
110 return wxFileName(file);
111}
112
113/* static */
114wxFileName wxFileName::DirName(const wxString& dir)
115{
116 wxFileName fn;
117 fn.AssignDir(dir);
118 return fn;
df5ddbca
RR
119}
120
844f90fb
VZ
121// ----------------------------------------------------------------------------
122// existence tests
123// ----------------------------------------------------------------------------
124
df5ddbca
RR
125bool wxFileName::FileExists()
126{
a35b27b1
RR
127 return wxFileName::FileExists( GetFullPath() );
128}
129
130bool wxFileName::FileExists( const wxString &file )
131{
132 return ::wxFileExists( file );
df5ddbca
RR
133}
134
135bool wxFileName::DirExists()
136{
a35b27b1
RR
137 return wxFileName::DirExists( GetFullPath() );
138}
139
140bool wxFileName::DirExists( const wxString &dir )
141{
142 return ::wxDirExists( dir );
df5ddbca
RR
143}
144
844f90fb
VZ
145// ----------------------------------------------------------------------------
146// CWD and HOME stuff
147// ----------------------------------------------------------------------------
148
df5ddbca
RR
149void wxFileName::AssignCwd()
150{
844f90fb 151 AssignDir(wxFileName::GetCwd());
a35b27b1
RR
152}
153
844f90fb 154/* static */
a35b27b1
RR
155wxString wxFileName::GetCwd()
156{
157 return ::wxGetCwd();
158}
159
160bool wxFileName::SetCwd()
161{
162 return wxFileName::SetCwd( GetFullPath() );
df5ddbca
RR
163}
164
a35b27b1 165bool wxFileName::SetCwd( const wxString &cwd )
df5ddbca 166{
a35b27b1 167 return ::wxSetWorkingDirectory( cwd );
df5ddbca
RR
168}
169
a35b27b1
RR
170void wxFileName::AssignHomeDir()
171{
844f90fb 172 AssignDir(wxFileName::GetHomeDir());
a35b27b1 173}
844f90fb 174
a35b27b1
RR
175wxString wxFileName::GetHomeDir()
176{
177 return ::wxGetHomeDir();
178}
844f90fb 179
df5ddbca
RR
180void wxFileName::AssignTempFileName( const wxString &prefix )
181{
844f90fb
VZ
182 wxString fullname;
183 if ( wxGetTempFileName(prefix, fullname) )
184 {
185 Assign(fullname);
186 }
187 else // error
188 {
189 Clear();
190 }
df5ddbca
RR
191}
192
844f90fb
VZ
193// ----------------------------------------------------------------------------
194// directory operations
195// ----------------------------------------------------------------------------
196
a35b27b1
RR
197bool wxFileName::Mkdir( int perm )
198{
199 return wxFileName::Mkdir( GetFullPath(), perm );
200}
201
202bool wxFileName::Mkdir( const wxString &dir, int perm )
df5ddbca 203{
a35b27b1 204 return ::wxMkdir( dir, perm );
df5ddbca
RR
205}
206
a35b27b1 207bool wxFileName::Rmdir()
df5ddbca 208{
a35b27b1 209 return wxFileName::Rmdir( GetFullPath() );
df5ddbca
RR
210}
211
a35b27b1 212bool wxFileName::Rmdir( const wxString &dir )
df5ddbca 213{
a35b27b1 214 return ::wxRmdir( dir );
df5ddbca
RR
215}
216
844f90fb
VZ
217// ----------------------------------------------------------------------------
218// path normalization
219// ----------------------------------------------------------------------------
220
221bool wxFileName::Normalize(wxPathNormalize flags,
222 const wxString& cwd,
223 wxPathFormat format)
a35b27b1 224{
844f90fb
VZ
225 // the existing path components
226 wxArrayString dirs = GetDirs();
227
228 // the path to prepend in front to make the path absolute
229 wxFileName curDir;
230
231 format = GetFormat(format);
232
233 // make the path absolute
234 if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute() )
a35b27b1 235 {
844f90fb
VZ
236 if ( cwd.empty() )
237 curDir.AssignCwd();
a35b27b1 238 else
844f90fb 239 curDir.AssignDir(cwd);
a35b27b1 240 }
844f90fb
VZ
241
242 // handle ~ stuff under Unix only
243 if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) )
a35b27b1 244 {
844f90fb
VZ
245 if ( !dirs.IsEmpty() )
246 {
247 wxString dir = dirs[0u];
248 if ( !dir.empty() && dir[0u] == _T('~') )
249 {
250 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
251
da2fd5ac 252 dirs.RemoveAt(0u);
844f90fb
VZ
253 }
254 }
a35b27b1 255 }
844f90fb
VZ
256
257 if ( curDir.IsOk() )
a35b27b1 258 {
844f90fb
VZ
259 wxArrayString dirsNew = curDir.GetDirs();
260 size_t count = dirs.GetCount();
261 for ( size_t n = 0; n < count; n++ )
262 {
263 dirsNew.Add(dirs[n]);
264 }
265
266 dirs = dirsNew;
a35b27b1 267 }
844f90fb
VZ
268
269 // now deal with ".", ".." and the rest
270 m_dirs.Empty();
271 size_t count = dirs.GetCount();
272 for ( size_t n = 0; n < count; n++ )
a35b27b1 273 {
844f90fb
VZ
274 wxString dir = dirs[n];
275
276 if ( flags && wxPATH_NORM_DOTS )
277 {
278 if ( dir == wxT(".") )
279 {
280 // just ignore
281 continue;
282 }
283
284 if ( dir == wxT("..") )
285 {
286 if ( m_dirs.IsEmpty() )
287 {
288 wxLogError(_("The path '%s' contains too many \"..\"!"),
289 GetFullPath().c_str());
290 return FALSE;
291 }
292
293 m_dirs.Remove(m_dirs.GetCount() - 1);
294 continue;
295 }
296 }
297
298 if ( flags & wxPATH_NORM_ENV_VARS )
a35b27b1 299 {
844f90fb 300 dir = wxExpandEnvVars(dir);
a35b27b1 301 }
844f90fb
VZ
302
303 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
304 {
305 dir.MakeLower();
306 }
307
308 m_dirs.Add(dir);
a35b27b1 309 }
844f90fb
VZ
310
311 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
312 {
313 // VZ: expand env vars here too?
314
315 m_name.MakeLower();
316 m_ext.MakeLower();
317 }
318
a35b27b1
RR
319 return TRUE;
320}
321
844f90fb
VZ
322// ----------------------------------------------------------------------------
323// filename kind tests
324// ----------------------------------------------------------------------------
325
326bool wxFileName::SameAs( const wxFileName &filepath, wxPathFormat format)
df5ddbca 327{
844f90fb
VZ
328 wxFileName fn1 = *this,
329 fn2 = filepath;
330
331 // get cwd only once - small time saving
332 wxString cwd = wxGetCwd();
333 fn1.Normalize(wxPATH_NORM_ALL, cwd, format);
334 fn2.Normalize(wxPATH_NORM_ALL, cwd, format);
335
336 if ( fn1.GetFullPath() == fn2.GetFullPath() )
337 return TRUE;
338
339 // TODO: compare inodes for Unix, this works even when filenames are
340 // different but files are the same (symlinks) (VZ)
341
342 return FALSE;
df5ddbca
RR
343}
344
844f90fb 345/* static */
df5ddbca
RR
346bool wxFileName::IsCaseSensitive( wxPathFormat format )
347{
844f90fb
VZ
348 // only DOS filenames are case-sensitive
349 return GetFormat(format) != wxPATH_DOS;
df5ddbca
RR
350}
351
352bool wxFileName::IsRelative( wxPathFormat format )
353{
844f90fb
VZ
354 return !IsAbsolute(format);
355}
356
357bool wxFileName::IsAbsolute( wxPathFormat format )
358{
359 wxChar ch = m_dirs.IsEmpty() ? _T('\0') : m_dirs[0u][0u];
360
361 // the path is absolute if it starts with a path separator or, only for
362 // Unix filenames, with "~" or "~user"
363 return IsPathSeparator(ch, format) ||
364 (GetFormat(format) == wxPATH_UNIX && ch == _T('~') );
365}
366
367/* static */
368wxString wxFileName::GetPathSeparators(wxPathFormat format)
369{
370 wxString seps;
371 switch ( GetFormat(format) )
df5ddbca 372 {
844f90fb
VZ
373 case wxPATH_DOS:
374 // accept both as native APIs do
9e8d8607 375 seps << wxFILE_SEP_PATH_UNIX << wxFILE_SEP_PATH_DOS;
844f90fb
VZ
376 break;
377
378 default:
379 wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
380 // fall through
381
382 case wxPATH_UNIX:
9e8d8607 383 seps = wxFILE_SEP_PATH_UNIX;
844f90fb
VZ
384 break;
385
386 case wxPATH_MAC:
9e8d8607 387 seps = wxFILE_SEP_PATH_MAC;
844f90fb 388 break;
df5ddbca
RR
389 }
390
844f90fb 391 return seps;
df5ddbca
RR
392}
393
844f90fb
VZ
394/* static */
395bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
df5ddbca 396{
844f90fb 397 return GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
df5ddbca
RR
398}
399
400bool wxFileName::IsWild( wxPathFormat format )
401{
844f90fb
VZ
402 // FIXME: this is probably false for Mac and this is surely wrong for most
403 // of Unix shells (think about "[...]")
404 return m_name.find_first_of(_T("*?")) != wxString::npos;
df5ddbca
RR
405}
406
844f90fb
VZ
407// ----------------------------------------------------------------------------
408// path components manipulation
409// ----------------------------------------------------------------------------
410
df5ddbca
RR
411void wxFileName::AppendDir( const wxString &dir )
412{
413 m_dirs.Add( dir );
414}
415
416void wxFileName::PrependDir( const wxString &dir )
417{
418 m_dirs.Insert( dir, 0 );
419}
420
421void wxFileName::InsertDir( int before, const wxString &dir )
422{
423 m_dirs.Insert( dir, before );
424}
425
426void wxFileName::RemoveDir( int pos )
427{
428 m_dirs.Remove( (size_t)pos );
429}
430
844f90fb
VZ
431// ----------------------------------------------------------------------------
432// accessors
433// ----------------------------------------------------------------------------
434
7124df9b
VZ
435void wxFileName::SetFullName(const wxString& fullname)
436{
437 SplitPath(fullname, NULL /* no path */, &m_name, &m_ext);
438}
439
844f90fb 440wxString wxFileName::GetFullName() const
a35b27b1 441{
844f90fb
VZ
442 wxString fullname = m_name;
443 if ( !m_ext.empty() )
a35b27b1 444 {
9e8d8607 445 fullname << wxFILE_SEP_EXT << m_ext;
a35b27b1 446 }
a35b27b1 447
844f90fb 448 return fullname;
a35b27b1
RR
449}
450
451wxString wxFileName::GetPath( bool add_separator, wxPathFormat format ) const
df5ddbca
RR
452{
453 format = GetFormat( format );
844f90fb 454
df5ddbca 455 wxString ret;
844f90fb
VZ
456 size_t count = m_dirs.GetCount();
457 for ( size_t i = 0; i < count; i++ )
df5ddbca 458 {
844f90fb
VZ
459 ret += m_dirs[i];
460 if ( add_separator || (i < count) )
461 ret += wxFILE_SEP_PATH;
df5ddbca 462 }
844f90fb 463
df5ddbca
RR
464 return ret;
465}
466
467wxString wxFileName::GetFullPath( wxPathFormat format ) const
468{
844f90fb 469 return GetPathWithSep() + GetFullName();
df5ddbca
RR
470}
471
472wxPathFormat wxFileName::GetFormat( wxPathFormat format )
473{
474 if (format == wxPATH_NATIVE)
475 {
476#if defined(__WXMSW__) || defined(__WXPM__)
477 format = wxPATH_DOS;
844f90fb 478#elif defined(__WXMAC__)
df5ddbca 479 format = wxPATH_MAC;
844f90fb 480#else
df5ddbca
RR
481 format = wxPATH_UNIX;
482#endif
483 }
484 return format;
485}
a35b27b1 486
9e8d8607
VZ
487// ----------------------------------------------------------------------------
488// path splitting function
489// ----------------------------------------------------------------------------
490
491void wxFileName::SplitPath(const wxString& fullpath,
492 wxString *pstrPath,
493 wxString *pstrName,
494 wxString *pstrExt,
495 wxPathFormat format)
496{
497 format = GetFormat(format);
498
499 // find the positions of the last dot and last path separator in the path
500 size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
501 size_t posLastSlash = fullpath.find_last_of(GetPathSeparators(format));
502
503 if ( (posLastDot != wxString::npos) && (format == wxPATH_UNIX) )
504 {
505 if ( (posLastDot == 0) ||
506 (fullpath[posLastDot - 1] == wxFILE_SEP_PATH_UNIX) )
507 {
508 // under Unix, dot may be (and commonly is) the first character of
509 // the filename, don't treat the entire filename as extension in
510 // this case
511 posLastDot = wxString::npos;
512 }
513 }
514
515 if ( (posLastDot != wxString::npos) && (posLastDot < posLastSlash) )
516 {
517 // the dot is part of the path, not the start of the extension
518 posLastDot = wxString::npos;
519 }
520
521 // now fill in the variables provided by user
522 if ( pstrPath )
523 {
524 if ( posLastSlash == wxString::npos )
525 {
526 // no path at all
527 pstrPath->Empty();
528 }
529 else
530 {
531 // take all until the separator
532 *pstrPath = fullpath.Left(posLastSlash);
533 }
534 }
535
536 if ( pstrName )
537 {
538 size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
539 size_t count = posLastDot == wxString::npos ? wxString::npos
540 : posLastDot - posLastSlash;
541
542 *pstrName = fullpath.Mid(nStart, count);
543 }
544
545 if ( pstrExt )
546 {
547 if ( posLastDot == wxString::npos )
548 {
549 // no extension
550 pstrExt->Empty();
551 }
552 else
553 {
554 // take everything after the dot
555 *pstrExt = fullpath.Mid(posLastDot + 1);
556 }
557 }
558}