1///////////////////////////////////////////////////////////////////////////// 2// Name: src/common/filesys.cpp 3// Purpose: wxFileSystem class - interface for opening files 4// Author: Vaclav Slavik 5// Copyright: (c) 1999 Vaclav Slavik 6// CVS-ID: $Id: filesys.cpp 55271 2008-08-26 00:03:04Z VZ $ 7// Licence: wxWindows licence 8///////////////////////////////////////////////////////////////////////////// 9 10#include "wx/wxprec.h" 11 12#ifdef __BORLANDC__ 13 #pragma hdrstop 14#endif 15 16 17#if wxUSE_FILESYSTEM 18 19#include "wx/filesys.h" 20 21#ifndef WX_PRECOMP 22 #include "wx/log.h" 23 #include "wx/module.h" 24#endif 25 26#include "wx/sysopt.h" 27#include "wx/wfstream.h" 28#include "wx/mimetype.h" 29#include "wx/filename.h" 30#include "wx/tokenzr.h" 31#include "wx/uri.h" 32#include "wx/private/fileback.h" 33 34 35//-------------------------------------------------------------------------------- 36// wxFileSystemHandler 37//-------------------------------------------------------------------------------- 38 39IMPLEMENT_ABSTRACT_CLASS(wxFileSystemHandler, wxObject) 40 41 42wxString wxFileSystemHandler::GetMimeTypeFromExt(const wxString& location) 43{ 44 wxString ext, mime; 45 wxString loc = GetRightLocation(location); 46 wxChar c; 47 int l = loc.length(), l2; 48 49 l2 = l; 50 for (int i = l-1; i >= 0; i--) 51 { 52 c = loc[(unsigned int) i]; 53 if ( c == wxT('#') ) 54 l2 = i + 1; 55 if ( c == wxT('.') ) 56 { 57 ext = loc.Right(l2-i-1); 58 break; 59 } 60 if ( (c == wxT('/')) || (c == wxT('\\')) || (c == wxT(':')) ) 61 return wxEmptyString; 62 } 63 64#if wxUSE_MIMETYPE 65 static bool s_MinimalMimeEnsured = false; 66 67 // Don't use mime types manager if the application doesn't need it and it would be 68 // cause an unacceptable delay, especially on startup. 69 bool useMimeTypesManager = true; 70#if wxUSE_SYSTEM_OPTIONS 71 useMimeTypesManager = (wxSystemOptions::GetOptionInt(wxT("filesys.no-mimetypesmanager")) == 0); 72#endif 73 74 if (useMimeTypesManager) 75 { 76 if (!s_MinimalMimeEnsured) 77 { 78 static const wxFileTypeInfo fallbacks[] = 79 { 80 wxFileTypeInfo(_T("image/jpeg"), 81 wxEmptyString, 82 wxEmptyString, 83 _T("JPEG image (from fallback)"), 84 _T("jpg"), _T("jpeg"), _T("JPG"), _T("JPEG"), NULL), 85 wxFileTypeInfo(_T("image/gif"), 86 wxEmptyString, 87 wxEmptyString, 88 _T("GIF image (from fallback)"), 89 _T("gif"), _T("GIF"), NULL), 90 wxFileTypeInfo(_T("image/png"), 91 wxEmptyString, 92 wxEmptyString, 93 _T("PNG image (from fallback)"), 94 _T("png"), _T("PNG"), NULL), 95 wxFileTypeInfo(_T("image/bmp"), 96 wxEmptyString, 97 wxEmptyString, 98 _T("windows bitmap image (from fallback)"), 99 _T("bmp"), _T("BMP"), NULL), 100 wxFileTypeInfo(_T("text/html"), 101 wxEmptyString, 102 wxEmptyString, 103 _T("HTML document (from fallback)"), 104 _T("htm"), _T("html"), _T("HTM"), _T("HTML"), NULL), 105 // must terminate the table with this! 106 wxFileTypeInfo() 107 }; 108 wxTheMimeTypesManager->AddFallbacks(fallbacks); 109 s_MinimalMimeEnsured = true; 110 } 111 112 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext); 113 if ( !ft || !ft -> GetMimeType(&mime) ) 114 { 115 mime = wxEmptyString; 116 } 117 118 delete ft; 119 120 return mime; 121 } 122 else 123#endif 124 { 125 if ( ext.IsSameAs(wxT("htm"), false) || ext.IsSameAs(_T("html"), false) ) 126 return wxT("text/html"); 127 if ( ext.IsSameAs(wxT("jpg"), false) || ext.IsSameAs(_T("jpeg"), false) ) 128 return wxT("image/jpeg"); 129 if ( ext.IsSameAs(wxT("gif"), false) ) 130 return wxT("image/gif"); 131 if ( ext.IsSameAs(wxT("png"), false) ) 132 return wxT("image/png"); 133 if ( ext.IsSameAs(wxT("bmp"), false) ) 134 return wxT("image/bmp"); 135 return wxEmptyString; 136 } 137} 138 139 140 141wxString wxFileSystemHandler::GetProtocol(const wxString& location) const 142{ 143 wxString s = wxEmptyString; 144 int i, l = location.length(); 145 bool fnd = false; 146 147 for (i = l-1; (i >= 0) && ((location[i] != wxT('#')) || (!fnd)); i--) { 148 if ((location[i] == wxT(':')) && (i != 1 /*win: C:\path*/)) fnd = true; 149 } 150 if (!fnd) return wxT("file"); 151 for (++i; (i < l) && (location[i] != wxT(':')); i++) s << location[i]; 152 return s; 153} 154 155 156wxString wxFileSystemHandler::GetLeftLocation(const wxString& location) const 157{ 158 int i; 159 bool fnd = false; 160 161 for (i = location.length()-1; i >= 0; i--) { 162 if ((location[i] == wxT(':')) && (i != 1 /*win: C:\path*/)) fnd = true; 163 else if (fnd && (location[i] == wxT('#'))) return location.Left(i); 164 } 165 return wxEmptyString; 166} 167 168wxString wxFileSystemHandler::GetRightLocation(const wxString& location) const 169{ 170 int i, l = location.length(); 171 int l2 = l + 1; 172 173 for (i = l-1; 174 (i >= 0) && 175 ((location[i] != wxT(':')) || (i == 1) || (location[i-2] == wxT(':'))); 176 i--) 177 { 178 if (location[i] == wxT('#')) l2 = i + 1; 179 } 180 if (i == 0) return wxEmptyString; 181 else return location.Mid(i + 1, l2 - i - 2); 182} 183 184wxString wxFileSystemHandler::GetAnchor(const wxString& location) const 185{ 186 wxChar c; 187 int l = location.length(); 188 189 for (int i = l-1; i >= 0; i--) { 190 c = location[i]; 191 if (c == wxT('#')) 192 return location.Right(l-i-1); 193 else if ((c == wxT('/')) || (c == wxT('\\')) || (c == wxT(':'))) 194 return wxEmptyString; 195 } 196 return wxEmptyString; 197} 198 199 200wxString wxFileSystemHandler::FindFirst(const wxString& WXUNUSED(spec), 201 int WXUNUSED(flags)) 202{ 203 return wxEmptyString; 204} 205 206wxString wxFileSystemHandler::FindNext() 207{ 208 return wxEmptyString; 209} 210 211//-------------------------------------------------------------------------------- 212// wxLocalFSHandler 213//-------------------------------------------------------------------------------- 214 215 216wxString wxLocalFSHandler::ms_root; 217 218bool wxLocalFSHandler::CanOpen(const wxString& location) 219{ 220 return GetProtocol(location) == wxT("file"); 221} 222 223wxFSFile* wxLocalFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs), const wxString& location) 224{ 225 // location has Unix path separators 226 wxString right = GetRightLocation(location); 227 wxFileName fn = wxFileSystem::URLToFileName(right); 228 wxString fullpath = ms_root + fn.GetFullPath(); 229 230 if (!wxFileExists(fullpath)) 231 return (wxFSFile*) NULL; 232 233 // we need to check whether we can really read from this file, otherwise 234 // wxFSFile is not going to work 235#if wxUSE_FFILE 236 wxFFileInputStream *is = new wxFFileInputStream(fullpath); 237#elif wxUSE_FILE 238 wxFileInputStream *is = new wxFileInputStream(fullpath); 239#else 240#error One of wxUSE_FILE or wxUSE_FFILE must be set to 1 for wxFSHandler to work 241#endif 242 if ( !is->Ok() ) 243 { 244 delete is; 245 return (wxFSFile*) NULL; 246 } 247 248 return new wxFSFile(is, 249 right, 250 GetMimeTypeFromExt(location), 251 GetAnchor(location) 252#if wxUSE_DATETIME 253 ,wxDateTime(wxFileModificationTime(fullpath)) 254#endif // wxUSE_DATETIME 255 ); 256} 257 258wxString wxLocalFSHandler::FindFirst(const wxString& spec, int flags) 259{ 260 wxFileName fn = wxFileSystem::URLToFileName(GetRightLocation(spec)); 261 return wxFindFirstFile(ms_root + fn.GetFullPath(), flags); 262} 263 264wxString wxLocalFSHandler::FindNext() 265{ 266 return wxFindNextFile(); 267} 268 269 270 271//----------------------------------------------------------------------------- 272// wxFileSystem 273//----------------------------------------------------------------------------- 274 275IMPLEMENT_DYNAMIC_CLASS(wxFileSystem, wxObject) 276IMPLEMENT_ABSTRACT_CLASS(wxFSFile, wxObject) 277 278 279wxList wxFileSystem::m_Handlers; 280 281 282wxFileSystem::~wxFileSystem() 283{ 284 WX_CLEAR_HASH_MAP(wxFSHandlerHash, m_LocalHandlers) 285} 286 287 288static wxString MakeCorrectPath(const wxString& path) 289{ 290 wxString p(path); 291 wxString r; 292 int i, j, cnt; 293 294 cnt = p.length(); 295 for (i = 0; i < cnt; i++) 296 if (p.GetChar(i) == wxT('\\')) p.GetWritableChar(i) = wxT('/'); // Want to be windows-safe 297 298 if (p.Left(2) == wxT("./")) { p = p.Mid(2); cnt -= 2; } 299 300 if (cnt < 3) return p; 301 302 r << p.GetChar(0) << p.GetChar(1); 303 304 // skip trailing ../.., if any 305 for (i = 2; i < cnt && (p.GetChar(i) == wxT('/') || p.GetChar(i) == wxT('.')); i++) r << p.GetChar(i); 306 307 // remove back references: translate dir1/../dir2 to dir2 308 for (; i < cnt; i++) 309 { 310 r << p.GetChar(i); 311 if (p.GetChar(i) == wxT('/') && p.GetChar(i-1) == wxT('.') && p.GetChar(i-2) == wxT('.')) 312 { 313 for (j = r.length() - 2; j >= 0 && r.GetChar(j) != wxT('/') && r.GetChar(j) != wxT(':'); j--) {} 314 if (j >= 0 && r.GetChar(j) != wxT(':')) 315 { 316 for (j = j - 1; j >= 0 && r.GetChar(j) != wxT('/') && r.GetChar(j) != wxT(':'); j--) {} 317 r.Remove(j + 1); 318 } 319 } 320 } 321 322 for (; i < cnt; i++) r << p.GetChar(i); 323 324 return r; 325} 326 327 328void wxFileSystem::ChangePathTo(const wxString& location, bool is_dir) 329{ 330 int i, pathpos = -1; 331 332 m_Path = MakeCorrectPath(location); 333 334 if (is_dir) 335 { 336 if (m_Path.length() > 0 && m_Path.Last() != wxT('/') && m_Path.Last() != wxT(':')) 337 m_Path << wxT('/'); 338 } 339 340 else 341 { 342 for (i = m_Path.length()-1; i >= 0; i--) 343 { 344 if (m_Path[(unsigned int) i] == wxT('/')) 345 { 346 if ((i > 1) && (m_Path[(unsigned int) (i-1)] == wxT('/')) && (m_Path[(unsigned int) (i-2)] == wxT(':'))) 347 { 348 i -= 2; 349 continue; 350 } 351 else 352 { 353 pathpos = i; 354 break; 355 } 356 } 357 else if (m_Path[(unsigned int) i] == wxT(':')) { 358 pathpos = i; 359 break; 360 } 361 } 362 if (pathpos == -1) 363 { 364 for (i = 0; i < (int) m_Path.length(); i++) 365 { 366 if (m_Path[(unsigned int) i] == wxT(':')) 367 { 368 m_Path.Remove(i+1); 369 break; 370 } 371 } 372 if (i == (int) m_Path.length()) 373 m_Path = wxEmptyString; 374 } 375 else 376 { 377 m_Path.Remove(pathpos+1); 378 } 379 } 380} 381 382 383 384wxFileSystemHandler *wxFileSystem::MakeLocal(wxFileSystemHandler *h) 385{ 386 wxClassInfo *classinfo = h->GetClassInfo(); 387 388 if (classinfo->IsDynamic()) 389 { 390 wxFileSystemHandler*& local = m_LocalHandlers[classinfo]; 391 if (!local) 392 local = (wxFileSystemHandler*)classinfo->CreateObject(); 393 return local; 394 } 395 else 396 { 397 return h; 398 } 399} 400 401 402 403wxFSFile* wxFileSystem::OpenFile(const wxString& location, int flags) 404{ 405 if ((flags & wxFS_READ) == 0) 406 return NULL; 407 408 wxString loc = MakeCorrectPath(location); 409 unsigned i, ln; 410 wxChar meta; 411 wxFSFile *s = NULL; 412 wxList::compatibility_iterator node; 413 414 ln = loc.length(); 415 meta = 0; 416 for (i = 0; i < ln; i++) 417 { 418 switch (loc[i]) 419 { 420 case wxT('/') : case wxT(':') : case wxT('#') : 421 meta = loc[i]; 422 break; 423 } 424 if (meta != 0) break; 425 } 426 m_LastName = wxEmptyString; 427 428 // try relative paths first : 429 if (meta != wxT(':')) 430 { 431 node = m_Handlers.GetFirst(); 432 while (node) 433 { 434 wxFileSystemHandler *h = (wxFileSystemHandler*) node -> GetData(); 435 if (h->CanOpen(m_Path + loc)) 436 { 437 s = MakeLocal(h)->OpenFile(*this, m_Path + loc); 438 if (s) { m_LastName = m_Path + loc; break; } 439 } 440 node = node->GetNext(); 441 } 442 } 443 444 // if failed, try absolute paths : 445 if (s == NULL) 446 { 447 node = m_Handlers.GetFirst(); 448 while (node) 449 { 450 wxFileSystemHandler *h = (wxFileSystemHandler*) node->GetData(); 451 if (h->CanOpen(loc)) 452 { 453 s = MakeLocal(h)->OpenFile(*this, loc); 454 if (s) { m_LastName = loc; break; } 455 } 456 node = node->GetNext(); 457 } 458 } 459 460 if (s && (flags & wxFS_SEEKABLE) != 0 && !s->GetStream()->IsSeekable()) 461 { 462 wxBackedInputStream *stream; 463 stream = new wxBackedInputStream(s->DetachStream()); 464 stream->FindLength(); 465 s->SetStream(stream); 466 } 467 468 return (s); 469} 470 471 472 473wxString wxFileSystem::FindFirst(const wxString& spec, int flags) 474{ 475 wxList::compatibility_iterator node; 476 wxString spec2(spec); 477 478 m_FindFileHandler = NULL; 479 480 for (int i = spec2.length()-1; i >= 0; i--) 481 if (spec2[(unsigned int) i] == wxT('\\')) spec2.GetWritableChar(i) = wxT('/'); // Want to be windows-safe 482 483 node = m_Handlers.GetFirst(); 484 while (node) 485 { 486 wxFileSystemHandler *h = (wxFileSystemHandler*) node -> GetData(); 487 if (h -> CanOpen(m_Path + spec2)) 488 { 489 m_FindFileHandler = MakeLocal(h); 490 return m_FindFileHandler -> FindFirst(m_Path + spec2, flags); 491 } 492 node = node->GetNext(); 493 } 494 495 node = m_Handlers.GetFirst(); 496 while (node) 497 { 498 wxFileSystemHandler *h = (wxFileSystemHandler*) node -> GetData(); 499 if (h -> CanOpen(spec2)) 500 { 501 m_FindFileHandler = MakeLocal(h); 502 return m_FindFileHandler -> FindFirst(spec2, flags); 503 } 504 node = node->GetNext(); 505 } 506 507 return wxEmptyString; 508} 509 510 511 512wxString wxFileSystem::FindNext() 513{ 514 if (m_FindFileHandler == NULL) return wxEmptyString; 515 else return m_FindFileHandler -> FindNext(); 516} 517 518bool wxFileSystem::FindFileInPath(wxString *pStr, 519 const wxChar *path, 520 const wxChar *basename) 521{ 522 // we assume that it's not empty 523 wxCHECK_MSG( !wxIsEmpty(basename), false, 524 _T("empty file name in wxFileSystem::FindFileInPath")); 525 526 // skip path separator in the beginning of the file name if present 527 if ( wxIsPathSeparator(*basename) ) 528 basename++; 529 530 wxStringTokenizer tokenizer(path, wxPATH_SEP); 531 while ( tokenizer.HasMoreTokens() ) 532 { 533 wxString strFile = tokenizer.GetNextToken(); 534 if ( !wxEndsWithPathSeparator(strFile) ) 535 strFile += wxFILE_SEP_PATH; 536 strFile += basename; 537 538 wxFSFile *file = OpenFile(strFile); 539 if ( file ) 540 { 541 delete file; 542 *pStr = strFile; 543 return true; 544 } 545 } 546 547 return false; 548} 549 550void wxFileSystem::AddHandler(wxFileSystemHandler *handler) 551{ 552 // prepend the handler to the beginning of the list because handlers added 553 // last should have the highest priority to allow overriding them 554 m_Handlers.Insert((size_t)0, handler); 555} 556 557wxFileSystemHandler* wxFileSystem::RemoveHandler(wxFileSystemHandler *handler) 558{ 559 // if handler has already been removed (or deleted) 560 // we return NULL. This is by design in case 561 // CleanUpHandlers() is called before RemoveHandler 562 // is called, as we cannot control the order 563 // which modules are unloaded 564 if (!m_Handlers.DeleteObject(handler)) 565 return NULL; 566 567 return handler; 568} 569 570 571bool wxFileSystem::HasHandlerForPath(const wxString &location) 572{ 573 for ( wxList::compatibility_iterator node = m_Handlers.GetFirst(); 574 node; node = node->GetNext() ) 575 { 576 wxFileSystemHandler *h = (wxFileSystemHandler*) node->GetData(); 577 if (h->CanOpen(location)) 578 return true; 579 } 580 581 return false; 582} 583 584void wxFileSystem::CleanUpHandlers() 585{ 586 WX_CLEAR_LIST(wxList, m_Handlers); 587} 588 589static const wxString g_unixPathString(wxT("/")); 590static const wxString g_nativePathString(wxFILE_SEP_PATH); 591 592// Returns the native path for a file URL 593wxFileName wxFileSystem::URLToFileName(const wxString& url) 594{ 595 wxString path = url; 596 597 if ( path.Find(wxT("file://")) == 0 ) 598 { 599 path = path.Mid(7); 600 } 601 else if ( path.Find(wxT("file:")) == 0 ) 602 { 603 path = path.Mid(5); 604 } 605 // Remove preceding double slash on Mac Classic 606#if defined(__WXMAC__) && !defined(__UNIX__) 607 else if ( path.Find(wxT("//")) == 0 ) 608 path = path.Mid(2); 609#endif 610 611 path = wxURI::Unescape(path); 612 613#ifdef __WXMSW__ 614 // file urls either start with a forward slash (local harddisk), 615 // otherwise they have a servername/sharename notation, 616 // which only exists on msw and corresponds to a unc 617 if ( path[0u] == wxT('/') && path [1u] != wxT('/')) 618 { 619 path = path.Mid(1); 620 } 621 else if ( (url.Find(wxT("file://")) == 0) && 622 (path.Find(wxT('/')) != wxNOT_FOUND) && 623 (path.length() > 1) && (path[1u] != wxT(':')) ) 624 { 625 path = wxT("//") + path; 626 } 627#endif 628 629 path.Replace(g_unixPathString, g_nativePathString); 630 631 return wxFileName(path, wxPATH_NATIVE); 632} 633 634// Returns the file URL for a native path 635wxString wxFileSystem::FileNameToURL(const wxFileName& filename) 636{ 637 wxFileName fn = filename; 638 fn.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | wxPATH_NORM_ABSOLUTE); 639 wxString url = fn.GetFullPath(wxPATH_NATIVE); 640 641#ifndef __UNIX__ 642 // unc notation, wxMSW 643 if ( url.Find(wxT("\\\\")) == 0 ) 644 { 645 url = wxT("//") + url.Mid(2); 646 } 647 else 648 { 649 url = wxT("/") + url; 650#ifdef __WXMAC__ 651 url = wxT("/") + url; 652#endif 653 654 } 655#endif 656 657 url.Replace(g_nativePathString, g_unixPathString); 658 url.Replace(wxT("%"), wxT("%25")); // '%'s must be replaced first! 659 url.Replace(wxT("#"), wxT("%23")); 660 url.Replace(wxT(":"), wxT("%3A")); 661 url = wxT("file:") + url; 662 return url; 663} 664 665 666///// Module: 667 668class wxFileSystemModule : public wxModule 669{ 670 DECLARE_DYNAMIC_CLASS(wxFileSystemModule) 671 672 public: 673 wxFileSystemModule() : 674 wxModule(), 675 m_handler(NULL) 676 { 677 } 678 679 virtual bool OnInit() 680 { 681 m_handler = new wxLocalFSHandler; 682 wxFileSystem::AddHandler(m_handler); 683 return true; 684 } 685 virtual void OnExit() 686 { 687 delete wxFileSystem::RemoveHandler(m_handler); 688 689 wxFileSystem::CleanUpHandlers(); 690 } 691 692 private: 693 wxFileSystemHandler* m_handler; 694 695}; 696 697IMPLEMENT_DYNAMIC_CLASS(wxFileSystemModule, wxModule) 698 699#endif 700 // wxUSE_FILESYSTEM 701