]> git.saurik.com Git - wxWidgets.git/blob - src/common/filename.cpp
wxString::FormatV() fix (bug 416853)
[wxWidgets.git] / src / common / filename.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/filename.cpp
3 // Purpose: wxFileName - encapsulates a file path
4 // Author: Robert Roebling, Vadim Zeitlin
5 // Modified by:
6 // Created: 28.12.2000
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 Robert Roebling
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
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"
38 #include "wx/config.h" // for wxExpandEnvVars
39 #include "wx/utils.h"
40
41 #if wxUSE_DYNLIB_CLASS
42 #include "wx/dynlib.h"
43 #endif
44
45 // For GetShort/LongPathName
46 #ifdef __WIN32__
47 #include <windows.h>
48 #include "wx/msw/winundef.h"
49 #endif
50
51 // at least some of these are required for file mod time
52 #ifdef __WXGTK__
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <dirent.h>
56 #include <pwd.h>
57 #ifndef __VMS
58 # include <grp.h>
59 #endif
60 # include <time.h>
61 #include <unistd.h>
62 #endif
63
64 // ============================================================================
65 // implementation
66 // ============================================================================
67
68 // ----------------------------------------------------------------------------
69 // wxFileName construction
70 // ----------------------------------------------------------------------------
71
72 void wxFileName::Assign( const wxFileName &filepath )
73 {
74 m_ext = filepath.GetExt();
75 m_name = filepath.GetName();
76 m_dirs = filepath.GetDirs();
77 }
78
79 void wxFileName::Assign( const wxString& path,
80 const wxString& name,
81 const wxString& ext,
82 wxPathFormat format )
83 {
84 wxStringTokenizer tn(path, GetPathSeparators(format),
85 wxTOKEN_RET_EMPTY_ALL);
86 bool first = TRUE;
87 m_dirs.Clear();
88 while ( tn.HasMoreTokens() )
89 {
90 wxString token = tn.GetNextToken();
91
92 // If the path starts with a slash, we need the first
93 // dir entry to be an empty for later reassembly.
94 if (first || !token.IsEmpty())
95 m_dirs.Add( token );
96
97 first = FALSE;
98 }
99
100 m_ext = ext;
101 m_name = name;
102 }
103
104 void wxFileName::Assign(const wxString& fullpath,
105 wxPathFormat format)
106 {
107 wxString path, name, ext;
108 SplitPath(fullpath, &path, &name, &ext, format);
109
110 Assign(path, name, ext, format);
111 }
112
113 void wxFileName::Assign(const wxString& path,
114 const wxString& fullname,
115 wxPathFormat format)
116 {
117 wxString name, ext;
118 SplitPath(fullname, NULL /* no path */, &name, &ext, format);
119
120 Assign(path, name, ext, format);
121 }
122
123 void wxFileName::Clear()
124 {
125 m_dirs.Clear();
126 m_name =
127 m_ext = wxEmptyString;
128 }
129
130 /* static */
131 wxFileName wxFileName::FileName(const wxString& file)
132 {
133 return wxFileName(file);
134 }
135
136 /* static */
137 wxFileName wxFileName::DirName(const wxString& dir)
138 {
139 wxFileName fn;
140 fn.AssignDir(dir);
141 return fn;
142 }
143
144 // ----------------------------------------------------------------------------
145 // existence tests
146 // ----------------------------------------------------------------------------
147
148 bool wxFileName::FileExists()
149 {
150 return wxFileName::FileExists( GetFullPath() );
151 }
152
153 bool wxFileName::FileExists( const wxString &file )
154 {
155 return ::wxFileExists( file );
156 }
157
158 bool wxFileName::DirExists()
159 {
160 return wxFileName::DirExists( GetFullPath() );
161 }
162
163 bool wxFileName::DirExists( const wxString &dir )
164 {
165 return ::wxDirExists( dir );
166 }
167
168 wxDateTime wxFileName::GetModificationTime()
169 {
170 #ifdef __WXGTK__
171 struct stat buff;
172 stat( GetFullName().fn_str(), &buff );
173
174 #if !defined( __EMX__ ) && !defined(__VMS)
175 struct stat lbuff;
176 lstat( GetFullName().fn_str(), &lbuff );
177 struct tm *t = localtime( &lbuff.st_mtime );
178 #else
179 struct tm *t = localtime( &buff.st_mtime );
180 #endif
181
182 wxDateTime ret( t->tm_mday, (wxDateTime::Month)t->tm_mon, t->tm_year+1900, t->tm_hour, t->tm_min, t->tm_sec );
183 #else
184
185 wxDateTime ret = wxDateTime::Now();
186
187 #endif
188 return ret;
189 }
190
191 // ----------------------------------------------------------------------------
192 // CWD and HOME stuff
193 // ----------------------------------------------------------------------------
194
195 void wxFileName::AssignCwd()
196 {
197 AssignDir(wxFileName::GetCwd());
198 }
199
200 /* static */
201 wxString wxFileName::GetCwd()
202 {
203 return ::wxGetCwd();
204 }
205
206 bool wxFileName::SetCwd()
207 {
208 return wxFileName::SetCwd( GetFullPath() );
209 }
210
211 bool wxFileName::SetCwd( const wxString &cwd )
212 {
213 return ::wxSetWorkingDirectory( cwd );
214 }
215
216 void wxFileName::AssignHomeDir()
217 {
218 AssignDir(wxFileName::GetHomeDir());
219 }
220
221 wxString wxFileName::GetHomeDir()
222 {
223 return ::wxGetHomeDir();
224 }
225
226 void wxFileName::AssignTempFileName( const wxString &prefix )
227 {
228 wxString fullname;
229 if ( wxGetTempFileName(prefix, fullname) )
230 {
231 Assign(fullname);
232 }
233 else // error
234 {
235 Clear();
236 }
237 }
238
239 // ----------------------------------------------------------------------------
240 // directory operations
241 // ----------------------------------------------------------------------------
242
243 bool wxFileName::Mkdir( int perm, bool full )
244 {
245 return wxFileName::Mkdir( GetFullPath(), perm, full );
246 }
247
248 bool wxFileName::Mkdir( const wxString &dir, int perm, bool full )
249 {
250 if (full)
251 {
252 wxFileName filename(dir);
253 wxArrayString dirs = filename.GetDirs();
254 dirs.Add(filename.GetName());
255
256 size_t count = dirs.GetCount();
257 size_t i;
258 wxString currPath;
259 int noErrors = 0;
260 for ( i = 0; i < count; i++ )
261 {
262 currPath += dirs[i];
263
264 if (currPath.Last() == wxT(':'))
265 {
266 // Can't create a root directory so continue to next dir
267 currPath += wxFILE_SEP_PATH;
268 continue;
269 }
270
271 if (!DirExists(currPath))
272 if (!wxMkdir(currPath, perm))
273 noErrors ++;
274
275 if ( (i < (count-1)) )
276 currPath += wxFILE_SEP_PATH;
277 }
278
279 return (noErrors == 0);
280
281 }
282 else
283 return ::wxMkdir( dir, perm );
284 }
285
286 bool wxFileName::Rmdir()
287 {
288 return wxFileName::Rmdir( GetFullPath() );
289 }
290
291 bool wxFileName::Rmdir( const wxString &dir )
292 {
293 return ::wxRmdir( dir );
294 }
295
296 // ----------------------------------------------------------------------------
297 // path normalization
298 // ----------------------------------------------------------------------------
299
300 bool wxFileName::Normalize(wxPathNormalize flags,
301 const wxString& cwd,
302 wxPathFormat format)
303 {
304 // the existing path components
305 wxArrayString dirs = GetDirs();
306
307 // the path to prepend in front to make the path absolute
308 wxFileName curDir;
309
310 format = GetFormat(format);
311
312 // make the path absolute
313 if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute() )
314 {
315 if ( cwd.empty() )
316 curDir.AssignCwd();
317 else
318 curDir.AssignDir(cwd);
319 }
320
321 // handle ~ stuff under Unix only
322 if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) )
323 {
324 if ( !dirs.IsEmpty() )
325 {
326 wxString dir = dirs[0u];
327 if ( !dir.empty() && dir[0u] == _T('~') )
328 {
329 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
330
331 dirs.RemoveAt(0u);
332 }
333 }
334 }
335
336 if ( curDir.IsOk() )
337 {
338 wxArrayString dirsNew = curDir.GetDirs();
339 size_t count = dirs.GetCount();
340 for ( size_t n = 0; n < count; n++ )
341 {
342 dirsNew.Add(dirs[n]);
343 }
344
345 dirs = dirsNew;
346 }
347
348 // now deal with ".", ".." and the rest
349 m_dirs.Empty();
350 size_t count = dirs.GetCount();
351 for ( size_t n = 0; n < count; n++ )
352 {
353 wxString dir = dirs[n];
354
355 if ( flags && wxPATH_NORM_DOTS )
356 {
357 if ( dir == wxT(".") )
358 {
359 // just ignore
360 continue;
361 }
362
363 if ( dir == wxT("..") )
364 {
365 if ( m_dirs.IsEmpty() )
366 {
367 wxLogError(_("The path '%s' contains too many \"..\"!"),
368 GetFullPath().c_str());
369 return FALSE;
370 }
371
372 m_dirs.Remove(m_dirs.GetCount() - 1);
373 continue;
374 }
375 }
376
377 if ( flags & wxPATH_NORM_ENV_VARS )
378 {
379 dir = wxExpandEnvVars(dir);
380 }
381
382 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
383 {
384 dir.MakeLower();
385 }
386
387 m_dirs.Add(dir);
388 }
389
390 if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
391 {
392 // VZ: expand env vars here too?
393
394 m_name.MakeLower();
395 m_ext.MakeLower();
396 }
397
398 #if defined(__WXMSW__) && defined(__WIN32__)
399 if (flags & wxPATH_NORM_LONG)
400 {
401 Assign(GetLongPath());
402 }
403 #endif
404
405 return TRUE;
406 }
407
408 // ----------------------------------------------------------------------------
409 // filename kind tests
410 // ----------------------------------------------------------------------------
411
412 bool wxFileName::SameAs( const wxFileName &filepath, wxPathFormat format)
413 {
414 wxFileName fn1 = *this,
415 fn2 = filepath;
416
417 // get cwd only once - small time saving
418 wxString cwd = wxGetCwd();
419 fn1.Normalize(wxPATH_NORM_ALL, cwd, format);
420 fn2.Normalize(wxPATH_NORM_ALL, cwd, format);
421
422 if ( fn1.GetFullPath() == fn2.GetFullPath() )
423 return TRUE;
424
425 // TODO: compare inodes for Unix, this works even when filenames are
426 // different but files are the same (symlinks) (VZ)
427
428 return FALSE;
429 }
430
431 /* static */
432 bool wxFileName::IsCaseSensitive( wxPathFormat format )
433 {
434 // only DOS filenames are case-sensitive
435 return GetFormat(format) != wxPATH_DOS;
436 }
437
438 bool wxFileName::IsRelative( wxPathFormat format )
439 {
440 return !IsAbsolute(format);
441 }
442
443 bool wxFileName::IsAbsolute( wxPathFormat format )
444 {
445 wxChar ch = m_dirs.IsEmpty() ? _T('\0') : m_dirs[0u][0u];
446
447 // Hack to cope with e.g. c:\thing - need something better
448 wxChar driveSep = _T('\0');
449 if (!m_dirs.IsEmpty() && m_dirs[0].Length() > 1)
450 driveSep = m_dirs[0u][1u];
451
452 // the path is absolute if it starts with a path separator or, only for
453 // Unix filenames, with "~" or "~user"
454 return IsPathSeparator(ch, format) ||
455 driveSep == _T(':') ||
456 (GetFormat(format) == wxPATH_UNIX && ch == _T('~') );
457 }
458
459 /* static */
460 wxString wxFileName::GetPathSeparators(wxPathFormat format)
461 {
462 wxString seps;
463 switch ( GetFormat(format) )
464 {
465 case wxPATH_DOS:
466 // accept both as native APIs do
467 seps << wxFILE_SEP_PATH_UNIX << wxFILE_SEP_PATH_DOS;
468 break;
469
470 default:
471 wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
472 // fall through
473
474 case wxPATH_UNIX:
475 seps = wxFILE_SEP_PATH_UNIX;
476 break;
477
478 case wxPATH_MAC:
479 seps = wxFILE_SEP_PATH_MAC;
480 break;
481 }
482
483 return seps;
484 }
485
486 /* static */
487 bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
488 {
489 return GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
490 }
491
492 bool wxFileName::IsWild( wxPathFormat format )
493 {
494 // FIXME: this is probably false for Mac and this is surely wrong for most
495 // of Unix shells (think about "[...]")
496 return m_name.find_first_of(_T("*?")) != wxString::npos;
497 }
498
499 // ----------------------------------------------------------------------------
500 // path components manipulation
501 // ----------------------------------------------------------------------------
502
503 void wxFileName::AppendDir( const wxString &dir )
504 {
505 m_dirs.Add( dir );
506 }
507
508 void wxFileName::PrependDir( const wxString &dir )
509 {
510 m_dirs.Insert( dir, 0 );
511 }
512
513 void wxFileName::InsertDir( int before, const wxString &dir )
514 {
515 m_dirs.Insert( dir, before );
516 }
517
518 void wxFileName::RemoveDir( int pos )
519 {
520 m_dirs.Remove( (size_t)pos );
521 }
522
523 // ----------------------------------------------------------------------------
524 // accessors
525 // ----------------------------------------------------------------------------
526
527 void wxFileName::SetFullName(const wxString& fullname)
528 {
529 SplitPath(fullname, NULL /* no path */, &m_name, &m_ext);
530 }
531
532 wxString wxFileName::GetFullName() const
533 {
534 wxString fullname = m_name;
535 if ( !m_ext.empty() )
536 {
537 fullname << wxFILE_SEP_EXT << m_ext;
538 }
539
540 return fullname;
541 }
542
543 wxString wxFileName::GetPath( bool add_separator, wxPathFormat format ) const
544 {
545 format = GetFormat( format );
546
547 wxString ret;
548 size_t count = m_dirs.GetCount();
549 for ( size_t i = 0; i < count; i++ )
550 {
551 ret += m_dirs[i];
552 if ( add_separator || (i < count) )
553 ret += wxFILE_SEP_PATH;
554 }
555
556 return ret;
557 }
558
559 wxString wxFileName::GetFullPath( wxPathFormat format ) const
560 {
561 return GetPathWithSep() + GetFullName();
562 }
563
564 // Return the short form of the path (returns identity on non-Windows platforms)
565 wxString wxFileName::GetShortPath() const
566 {
567 #if defined(__WXMSW__) && defined(__WIN32__)
568 wxString path(GetFullPath());
569 wxString pathOut;
570 DWORD sz = ::GetShortPathName(path, NULL, 0);
571 bool ok = sz != 0;
572 if ( ok )
573 {
574 ok = ::GetShortPathName
575 (
576 path,
577 pathOut.GetWriteBuf(sz),
578 sz
579 ) != 0;
580 pathOut.UngetWriteBuf();
581 }
582 if (ok)
583 return pathOut;
584 else
585 return path;
586 #else
587 return GetFullPath();
588 #endif
589 }
590
591 // Return the long form of the path (returns identity on non-Windows platforms)
592 wxString wxFileName::GetLongPath() const
593 {
594 #if defined(__WXMSW__) && defined(__WIN32__)
595 wxString path(GetFullPath());
596 wxString pathOut;
597 bool success = FALSE;
598
599 #if wxUSE_DYNLIB_CLASS
600 typedef DWORD (*GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
601
602 static bool s_triedToLoad = FALSE;
603 static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
604
605 if ( !s_triedToLoad )
606 {
607 s_triedToLoad = TRUE;
608
609 wxDllType dllKernel = wxDllLoader::LoadLibrary(_T("kernel32"));
610 if ( 0 ) // dllKernel )
611 {
612 // may succeed or fail depending on the Windows version
613 #ifdef _UNICODE
614 s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameW"));
615 #else
616 s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameA"));
617 #endif
618
619 wxDllLoader::UnloadLibrary(dllKernel);
620
621 if ( s_pfnGetLongPathName )
622 {
623 DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0);
624 bool ok = dwSize > 0;
625
626 if ( ok )
627 {
628 DWORD sz = (*s_pfnGetLongPathName)(path, NULL, 0);
629 ok = sz != 0;
630 if ( ok )
631 {
632 ok = (*s_pfnGetLongPathName)
633 (
634 path,
635 pathOut.GetWriteBuf(sz),
636 sz
637 ) != 0;
638 pathOut.UngetWriteBuf();
639
640 success = TRUE;
641 }
642 }
643 }
644 }
645 }
646 if (success)
647 return pathOut;
648 #endif
649 // wxUSE_DYNLIB_CLASS
650
651 if (!success)
652 {
653 // The OS didn't support GetLongPathName, or some other error.
654 // We need to call FindFirstFile on each component in turn.
655
656 WIN32_FIND_DATA findFileData;
657 HANDLE hFind;
658 pathOut = wxEmptyString;
659
660 wxArrayString dirs = GetDirs();
661 dirs.Add(GetName());
662
663 size_t count = dirs.GetCount();
664 size_t i;
665 wxString tmpPath;
666
667 for ( i = 0; i < count; i++ )
668 {
669 // We're using pathOut to collect the long-name path,
670 // but using a temporary for appending the last path component which may be short-name
671 tmpPath = pathOut + dirs[i];
672
673 if (tmpPath.Last() == wxT(':'))
674 {
675 // Can't pass a drive and root dir to FindFirstFile,
676 // so continue to next dir
677 tmpPath += wxFILE_SEP_PATH;
678 pathOut = tmpPath;
679 continue;
680 }
681
682 hFind = ::FindFirstFile(tmpPath, &findFileData);
683 if (hFind == INVALID_HANDLE_VALUE)
684 {
685 // Error: return immediately with the original path
686 return path;
687 }
688 else
689 {
690 pathOut += findFileData.cFileName;
691 if ( (i < (count-1)) )
692 pathOut += wxFILE_SEP_PATH;
693
694 ::FindClose(hFind);
695 }
696 }
697 }
698 return pathOut;
699 #else
700 return GetFullPath();
701 #endif
702 }
703
704 wxPathFormat wxFileName::GetFormat( wxPathFormat format )
705 {
706 if (format == wxPATH_NATIVE)
707 {
708 #if defined(__WXMSW__) || defined(__WXPM__)
709 format = wxPATH_DOS;
710 #elif defined(__WXMAC__)
711 format = wxPATH_UNIX; // that's the way the rest of wx' code works right now
712 #else
713 format = wxPATH_UNIX;
714 #endif
715 }
716 return format;
717 }
718
719 // ----------------------------------------------------------------------------
720 // path splitting function
721 // ----------------------------------------------------------------------------
722
723 void wxFileName::SplitPath(const wxString& fullpath,
724 wxString *pstrPath,
725 wxString *pstrName,
726 wxString *pstrExt,
727 wxPathFormat format)
728 {
729 format = GetFormat(format);
730
731 // find the positions of the last dot and last path separator in the path
732 size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
733 size_t posLastSlash = fullpath.find_last_of(GetPathSeparators(format));
734
735 if ( (posLastDot != wxString::npos) && (format == wxPATH_UNIX) )
736 {
737 if ( (posLastDot == 0) ||
738 (fullpath[posLastDot - 1] == wxFILE_SEP_PATH_UNIX) )
739 {
740 // under Unix, dot may be (and commonly is) the first character of
741 // the filename, don't treat the entire filename as extension in
742 // this case
743 posLastDot = wxString::npos;
744 }
745 }
746
747 // if we do have a dot and a slash, check that the dot is in the name part
748 if ( (posLastDot != wxString::npos) &&
749 (posLastSlash != wxString::npos) &&
750 (posLastDot < posLastSlash) )
751 {
752 // the dot is part of the path, not the start of the extension
753 posLastDot = wxString::npos;
754 }
755
756 // now fill in the variables provided by user
757 if ( pstrPath )
758 {
759 if ( posLastSlash == wxString::npos )
760 {
761 // no path at all
762 pstrPath->Empty();
763 }
764 else
765 {
766 // take all until the separator
767 *pstrPath = fullpath.Left(posLastSlash);
768 }
769 }
770
771 if ( pstrName )
772 {
773 // take all characters starting from the one after the last slash and
774 // up to, but excluding, the last dot
775 size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
776 size_t count;
777 if ( posLastDot == wxString::npos )
778 {
779 // take all until the end
780 count = wxString::npos;
781 }
782 else if ( posLastSlash == wxString::npos )
783 {
784 count = posLastDot;
785 }
786 else // have both dot and slash
787 {
788 count = posLastDot - posLastSlash - 1;
789 }
790
791 *pstrName = fullpath.Mid(nStart, count);
792 }
793
794 if ( pstrExt )
795 {
796 if ( posLastDot == wxString::npos )
797 {
798 // no extension
799 pstrExt->Empty();
800 }
801 else
802 {
803 // take everything after the dot
804 *pstrExt = fullpath.Mid(posLastDot + 1);
805 }
806 }
807 }