]> git.saurik.com Git - wxWidgets.git/blob - src/common/filename.cpp
Minor code tweeks to stop BCC compiler warnings
[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 short avoidCompilerWarning = 0;
611 if ( avoidCompilerWarning ) // dllKernel )
612 {
613 // may succeed or fail depending on the Windows version
614 #ifdef _UNICODE
615 s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameW"));
616 #else
617 s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameA"));
618 #endif
619
620 wxDllLoader::UnloadLibrary(dllKernel);
621
622 if ( s_pfnGetLongPathName )
623 {
624 DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0);
625 bool ok = dwSize > 0;
626
627 if ( ok )
628 {
629 DWORD sz = (*s_pfnGetLongPathName)(path, NULL, 0);
630 ok = sz != 0;
631 if ( ok )
632 {
633 ok = (*s_pfnGetLongPathName)
634 (
635 path,
636 pathOut.GetWriteBuf(sz),
637 sz
638 ) != 0;
639 pathOut.UngetWriteBuf();
640
641 success = TRUE;
642 }
643 }
644 }
645 }
646 }
647 if (success)
648 return pathOut;
649 #endif
650 // wxUSE_DYNLIB_CLASS
651
652 if (!success)
653 {
654 // The OS didn't support GetLongPathName, or some other error.
655 // We need to call FindFirstFile on each component in turn.
656
657 WIN32_FIND_DATA findFileData;
658 HANDLE hFind;
659 pathOut = wxEmptyString;
660
661 wxArrayString dirs = GetDirs();
662 dirs.Add(GetName());
663
664 size_t count = dirs.GetCount();
665 size_t i;
666 wxString tmpPath;
667
668 for ( i = 0; i < count; i++ )
669 {
670 // We're using pathOut to collect the long-name path,
671 // but using a temporary for appending the last path component which may be short-name
672 tmpPath = pathOut + dirs[i];
673
674 if (tmpPath.Last() == wxT(':'))
675 {
676 // Can't pass a drive and root dir to FindFirstFile,
677 // so continue to next dir
678 tmpPath += wxFILE_SEP_PATH;
679 pathOut = tmpPath;
680 continue;
681 }
682
683 hFind = ::FindFirstFile(tmpPath, &findFileData);
684 if (hFind == INVALID_HANDLE_VALUE)
685 {
686 // Error: return immediately with the original path
687 return path;
688 }
689 else
690 {
691 pathOut += findFileData.cFileName;
692 if ( (i < (count-1)) )
693 pathOut += wxFILE_SEP_PATH;
694
695 ::FindClose(hFind);
696 }
697 }
698 }
699 return pathOut;
700 #else
701 return GetFullPath();
702 #endif
703 }
704
705 wxPathFormat wxFileName::GetFormat( wxPathFormat format )
706 {
707 if (format == wxPATH_NATIVE)
708 {
709 #if defined(__WXMSW__) || defined(__WXPM__)
710 format = wxPATH_DOS;
711 #elif defined(__WXMAC__)
712 format = wxPATH_UNIX; // that's the way the rest of wx' code works right now
713 #else
714 format = wxPATH_UNIX;
715 #endif
716 }
717 return format;
718 }
719
720 // ----------------------------------------------------------------------------
721 // path splitting function
722 // ----------------------------------------------------------------------------
723
724 void wxFileName::SplitPath(const wxString& fullpath,
725 wxString *pstrPath,
726 wxString *pstrName,
727 wxString *pstrExt,
728 wxPathFormat format)
729 {
730 format = GetFormat(format);
731
732 // find the positions of the last dot and last path separator in the path
733 size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
734 size_t posLastSlash = fullpath.find_last_of(GetPathSeparators(format));
735
736 if ( (posLastDot != wxString::npos) && (format == wxPATH_UNIX) )
737 {
738 if ( (posLastDot == 0) ||
739 (fullpath[posLastDot - 1] == wxFILE_SEP_PATH_UNIX) )
740 {
741 // under Unix, dot may be (and commonly is) the first character of
742 // the filename, don't treat the entire filename as extension in
743 // this case
744 posLastDot = wxString::npos;
745 }
746 }
747
748 // if we do have a dot and a slash, check that the dot is in the name part
749 if ( (posLastDot != wxString::npos) &&
750 (posLastSlash != wxString::npos) &&
751 (posLastDot < posLastSlash) )
752 {
753 // the dot is part of the path, not the start of the extension
754 posLastDot = wxString::npos;
755 }
756
757 // now fill in the variables provided by user
758 if ( pstrPath )
759 {
760 if ( posLastSlash == wxString::npos )
761 {
762 // no path at all
763 pstrPath->Empty();
764 }
765 else
766 {
767 // take all until the separator
768 *pstrPath = fullpath.Left(posLastSlash);
769 }
770 }
771
772 if ( pstrName )
773 {
774 // take all characters starting from the one after the last slash and
775 // up to, but excluding, the last dot
776 size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
777 size_t count;
778 if ( posLastDot == wxString::npos )
779 {
780 // take all until the end
781 count = wxString::npos;
782 }
783 else if ( posLastSlash == wxString::npos )
784 {
785 count = posLastDot;
786 }
787 else // have both dot and slash
788 {
789 count = posLastDot - posLastSlash - 1;
790 }
791
792 *pstrName = fullpath.Mid(nStart, count);
793 }
794
795 if ( pstrExt )
796 {
797 if ( posLastDot == wxString::npos )
798 {
799 // no extension
800 pstrExt->Empty();
801 }
802 else
803 {
804 // take everything after the dot
805 *pstrExt = fullpath.Mid(posLastDot + 1);
806 }
807 }
808 }