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