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