PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/ExtLibs/wxWidgets/src/html/chm.cpp

https://bitbucket.org/lennonchan/cafu
C++ | 929 lines | 602 code | 162 blank | 165 comment | 71 complexity | b3b77d0dd1535657b70e94baef6fbb51 MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/html/chm.cpp
  3. // Purpose: CHM (Help) support for wxHTML
  4. // Author: Markus Sinner
  5. // Copyright: (c) 2003 Herd Software Development
  6. // CVS-ID: $Id$
  7. // Licence: wxWindows licence
  8. /////////////////////////////////////////////////////////////////////////////
  9. #include "wx/wxprec.h"
  10. #ifdef __BORLANDC__
  11. #pragma hdrstop
  12. #endif
  13. #if wxUSE_LIBMSPACK
  14. #include <mspack.h>
  15. #ifndef WX_PRECOMP
  16. #include "wx/intl.h"
  17. #include "wx/log.h"
  18. #include "wx/module.h"
  19. #endif
  20. #include "wx/filesys.h"
  21. #include "wx/mstream.h"
  22. #include "wx/wfstream.h"
  23. #include "wx/html/forcelnk.h"
  24. FORCE_LINK_ME(wxhtml_chm_support)
  25. // ----------------------------------------------------------------------------
  26. /// wxChmTools
  27. /// <p>
  28. /// this class is used to abstract access to CHM-Archives
  29. /// with library mspack written by Stuart Caie
  30. /// http://www.kyz.uklinux.net/libmspack/
  31. // ----------------------------------------------------------------------------
  32. class wxChmTools
  33. {
  34. public:
  35. /// constructor
  36. wxChmTools(const wxFileName &archive);
  37. /// destructor
  38. ~wxChmTools();
  39. /// Generate error-string for error-code
  40. static const wxString ChmErrorMsg(int error);
  41. /// get an array of archive-member-filenames
  42. const wxArrayString *GetFileNames()
  43. {
  44. return m_fileNames;
  45. };
  46. /// get the name of the archive representated by this class
  47. const wxString GetArchiveName()
  48. {
  49. return m_chmFileName;
  50. };
  51. /// Find a file in the archive
  52. const wxString Find(const wxString& pattern,
  53. const wxString& startfrom = wxEmptyString);
  54. /// Extract a file in the archive into a file
  55. size_t Extract(const wxString& pattern, const wxString& filename);
  56. /// check archive for a file
  57. bool Contains(const wxString& pattern);
  58. /// get a string for the last error which occurred
  59. const wxString GetLastErrorMessage();
  60. /// Last Error
  61. int m_lasterror;
  62. private:
  63. // these vars are used by FindFirst/Next:
  64. wxString m_chmFileName;
  65. char *m_chmFileNameANSI;
  66. /// mspack-pointer to mschmd_header
  67. struct mschmd_header *m_archive;
  68. /// mspack-pointer to mschm_decompressor
  69. struct mschm_decompressor *m_decompressor;
  70. /// Array of filenames in archive
  71. wxArrayString * m_fileNames;
  72. /// Internal function to get filepointer
  73. struct mschmd_file *GetMschmdFile(const wxString& pattern);
  74. };
  75. /***
  76. * constructor
  77. *
  78. * @param archive The filename of the archive to open
  79. */
  80. wxChmTools::wxChmTools(const wxFileName &archive)
  81. {
  82. m_chmFileName = archive.GetFullPath();
  83. wxASSERT_MSG( !m_chmFileName.empty(), wxT("empty archive name") );
  84. m_archive = NULL;
  85. m_decompressor = NULL;
  86. m_fileNames = NULL;
  87. m_lasterror = 0;
  88. struct mschmd_header *chmh;
  89. struct mschm_decompressor *chmd;
  90. struct mschmd_file *file;
  91. // Create decompressor
  92. chmd = mspack_create_chm_decompressor(NULL);
  93. m_decompressor = (struct mschm_decompressor *) chmd;
  94. // NB: we must make a copy of the string because chmd->open won't call
  95. // strdup() [libmspack-20030726], which would cause crashes in
  96. // Unicode build when mb_str() returns temporary buffer
  97. m_chmFileNameANSI = strdup((const char*)m_chmFileName.mb_str(wxConvFile));
  98. // Open the archive and store it in class:
  99. if ( (chmh = chmd->open(chmd, (char*)m_chmFileNameANSI)) )
  100. {
  101. m_archive = chmh;
  102. // Create Filenamearray
  103. m_fileNames = new wxArrayString;
  104. // Store Filenames in array
  105. for (file = chmh->files; file; file = file->next)
  106. {
  107. m_fileNames->Add(wxString::FromAscii(file->filename));
  108. }
  109. }
  110. else
  111. {
  112. wxLogError(_("Failed to open CHM archive '%s'."),
  113. archive.GetFullPath().c_str());
  114. m_lasterror = (chmd->last_error(chmd));
  115. return;
  116. }
  117. }
  118. /***
  119. * Destructor
  120. */
  121. wxChmTools::~wxChmTools()
  122. {
  123. struct mschm_decompressor *chmd = m_decompressor;
  124. struct mschmd_header *chmh = m_archive;
  125. delete m_fileNames;
  126. // Close Archive
  127. if (chmh && chmd)
  128. chmd->close(chmd, chmh);
  129. free(m_chmFileNameANSI);
  130. // Destroy Decompressor
  131. if (chmd)
  132. mspack_destroy_chm_decompressor(chmd);
  133. }
  134. /**
  135. * Checks if the given pattern matches to any
  136. * filename stored in archive
  137. *
  138. * @param pattern The filename pattern, may include '*' and/or '?'
  139. * @return true, if any file matching pattern has been found,
  140. * false if not
  141. */
  142. bool wxChmTools::Contains(const wxString& pattern)
  143. {
  144. int count;
  145. wxString pattern_tmp = wxString(pattern).MakeLower();
  146. // loop through filearay
  147. if ( m_fileNames && (count = m_fileNames->GetCount()) > 0 )
  148. {
  149. for (int i = 0; i < count; i++)
  150. {
  151. wxString tmp = m_fileNames->Item(i).MakeLower();
  152. if ( tmp.Matches(pattern_tmp) || tmp.Mid(1).Matches(pattern_tmp))
  153. return true;
  154. }
  155. }
  156. return false;
  157. }
  158. /**
  159. * Find()
  160. *
  161. * Finds the next file descibed by a pattern in the archive, starting
  162. * the file given by second parameter
  163. *
  164. * @param pattern The file-pattern to search for. May contain '*' and/or '?'
  165. * @param startfrom The filename which the search should start after
  166. * @returns The full pathname of the found file
  167. */
  168. const wxString wxChmTools::Find(const wxString& pattern,
  169. const wxString& startfrom)
  170. {
  171. int count;
  172. wxString tmp;
  173. wxString pattern_tmp(pattern);
  174. wxString startfrom_tmp(startfrom);
  175. pattern_tmp.MakeLower();
  176. startfrom_tmp.MakeLower();
  177. if ( m_fileNames && (count = m_fileNames->GetCount()) > 0 )
  178. {
  179. for (int i = 0; i < count; i++)
  180. {
  181. tmp = m_fileNames->Item(i).MakeLower();
  182. // if we find the string where the search should began
  183. if ( tmp.Matches(startfrom_tmp) ||
  184. tmp.Mid(1).Matches(startfrom_tmp) )
  185. continue;
  186. if ( tmp.Matches(pattern_tmp) ||
  187. tmp.Mid(1).Matches(pattern_tmp) )
  188. {
  189. return tmp;
  190. }
  191. }
  192. }
  193. return wxEmptyString;
  194. }
  195. /**
  196. * Extract ()
  197. *
  198. * extracts the first hit of pattern to the given position
  199. *
  200. * @param pattern A filename pattern (may contain * and ? chars)
  201. * @param filename The FileName where to temporary extract the file to
  202. * @return 0 at no file extracted<br>
  203. * number of bytes extracted else
  204. */
  205. size_t wxChmTools::Extract(const wxString& pattern, const wxString& filename)
  206. {
  207. struct mschm_decompressor *d = m_decompressor;
  208. struct mschmd_header *h = m_archive;
  209. struct mschmd_file *f;
  210. wxString tmp;
  211. wxString pattern_tmp = (wxString(pattern)).MakeLower();
  212. for (f = h->files; f; f = f->next)
  213. {
  214. tmp = wxString::FromAscii(f->filename).MakeLower();
  215. if ( tmp.Matches(pattern_tmp) ||
  216. tmp.Mid(1).Matches(pattern_tmp) )
  217. {
  218. // ignore leading '/'
  219. if (d->extract(d, f,
  220. (char*)(const char*)filename.mb_str(wxConvFile)))
  221. {
  222. // Error
  223. m_lasterror = d->last_error(d);
  224. wxLogError(_("Could not extract %s into %s: %s"),
  225. wxString::FromAscii(f->filename).c_str(),
  226. filename.c_str(),
  227. ChmErrorMsg(m_lasterror).c_str());
  228. return 0;
  229. }
  230. else
  231. {
  232. return (size_t) f->length;
  233. }
  234. }
  235. }
  236. return 0;
  237. }
  238. /**
  239. * Find a file by pattern
  240. *
  241. * @param pattern A filename pattern (may contain * and ? chars)
  242. * @return A pointer to the file (mschmd_file*)
  243. */
  244. struct mschmd_file *wxChmTools::GetMschmdFile(const wxString& pattern_orig)
  245. {
  246. struct mschmd_file *f;
  247. struct mschmd_header *h = (struct mschmd_header *) m_archive;
  248. wxString tmp;
  249. wxString pattern = wxString(pattern_orig).MakeLower();
  250. for (f = h->files; f; f = f->next)
  251. {
  252. tmp = wxString::FromAscii(f->filename).MakeLower();
  253. if ( tmp.Matches(pattern) || tmp.Mid(1).Matches(pattern) )
  254. {
  255. // ignore leading '/'
  256. return f;
  257. }
  258. }
  259. return NULL;
  260. }
  261. const wxString wxChmTools::GetLastErrorMessage()
  262. {
  263. return ChmErrorMsg(m_lasterror);
  264. }
  265. const wxString wxChmTools::ChmErrorMsg(int error)
  266. {
  267. switch (error)
  268. {
  269. case MSPACK_ERR_OK:
  270. return _("no error");
  271. case MSPACK_ERR_ARGS:
  272. return _("bad arguments to library function");
  273. case MSPACK_ERR_OPEN:
  274. return _("error opening file");
  275. case MSPACK_ERR_READ:
  276. return _("read error");
  277. case MSPACK_ERR_WRITE:
  278. return _("write error");
  279. case MSPACK_ERR_SEEK:
  280. return _("seek error");
  281. case MSPACK_ERR_NOMEMORY:
  282. return _("out of memory");
  283. case MSPACK_ERR_SIGNATURE:
  284. return _("bad signature");
  285. case MSPACK_ERR_DATAFORMAT:
  286. return _("error in data format");
  287. case MSPACK_ERR_CHECKSUM:
  288. return _("checksum error");
  289. case MSPACK_ERR_CRUNCH:
  290. return _("compression error");
  291. case MSPACK_ERR_DECRUNCH:
  292. return _("decompression error");
  293. }
  294. return _("unknown error");
  295. }
  296. // ---------------------------------------------------------------------------
  297. /// wxChmInputStream
  298. // ---------------------------------------------------------------------------
  299. class wxChmInputStream : public wxInputStream
  300. {
  301. public:
  302. /// Constructor
  303. wxChmInputStream(const wxString& archive,
  304. const wxString& file, bool simulate = false);
  305. /// Destructor
  306. virtual ~wxChmInputStream();
  307. /// Return the size of the accessed file in archive
  308. virtual size_t GetSize() const { return m_size; }
  309. /// End of Stream?
  310. virtual bool Eof() const;
  311. /// Set simulation-mode of HHP-File (if non is found)
  312. void SimulateHHP(bool sim) { m_simulateHHP = sim; }
  313. protected:
  314. /// See wxInputStream
  315. virtual size_t OnSysRead(void *buffer, size_t bufsize);
  316. /// See wxInputStream
  317. virtual wxFileOffset OnSysSeek(wxFileOffset seek, wxSeekMode mode);
  318. /// See wxInputStream
  319. virtual wxFileOffset OnSysTell() const { return m_pos; }
  320. private:
  321. size_t m_size;
  322. wxFileOffset m_pos;
  323. bool m_simulateHHP;
  324. char * m_content;
  325. wxInputStream * m_contentStream;
  326. void CreateHHPStream();
  327. bool CreateFileStream(const wxString& pattern);
  328. // this void* is handle of archive . I'm sorry it is void and not proper
  329. // type but I don't want to make unzip.h header public.
  330. // locates the file and returns a mspack_file *
  331. mspack_file *LocateFile(wxString filename);
  332. // should store pointer to current file
  333. mspack_file *m_file;
  334. // The Chm-Class for extracting the data
  335. wxChmTools *m_chm;
  336. wxString m_fileName;
  337. };
  338. /**
  339. * Constructor
  340. * @param archive The name of the .chm archive. Remember that archive must
  341. * be local file accesible via fopen, fread functions!
  342. * @param filename The Name of the file to be extracted from archive
  343. * @param simulate if true than class should simulate .HHP-File based on #SYSTEM
  344. * if false than class does nothing if it doesn't find .hhp
  345. */
  346. wxChmInputStream::wxChmInputStream(const wxString& archive,
  347. const wxString& filename, bool simulate)
  348. : wxInputStream()
  349. {
  350. m_pos = 0;
  351. m_size = 0;
  352. m_content = NULL;
  353. m_contentStream = NULL;
  354. m_lasterror = wxSTREAM_NO_ERROR;
  355. m_chm = new wxChmTools (wxFileName(archive));
  356. m_file = NULL;
  357. m_fileName = wxString(filename).MakeLower();
  358. m_simulateHHP = simulate;
  359. if ( !m_chm->Contains(m_fileName) )
  360. {
  361. // if the file could not be located, but was *.hhp, than we create
  362. // the content of the hhp-file on the fly and store it for reading
  363. // by the application
  364. if ( m_fileName.Find(wxT(".hhp")) != wxNOT_FOUND && m_simulateHHP )
  365. {
  366. // now we open an hhp-file
  367. CreateHHPStream();
  368. }
  369. else
  370. {
  371. wxLogError(_("Could not locate file '%s'."), filename.c_str());
  372. m_lasterror = wxSTREAM_READ_ERROR;
  373. return;
  374. }
  375. }
  376. else
  377. { // file found
  378. CreateFileStream(m_fileName);
  379. }
  380. }
  381. wxChmInputStream::~wxChmInputStream()
  382. {
  383. delete m_chm;
  384. delete m_contentStream;
  385. if (m_content)
  386. {
  387. free (m_content);
  388. m_content=NULL;
  389. }
  390. }
  391. bool wxChmInputStream::Eof() const
  392. {
  393. return (m_content==NULL ||
  394. m_contentStream==NULL ||
  395. m_contentStream->Eof() ||
  396. m_pos>m_size);
  397. }
  398. size_t wxChmInputStream::OnSysRead(void *buffer, size_t bufsize)
  399. {
  400. if ( m_pos >= m_size )
  401. {
  402. m_lasterror = wxSTREAM_EOF;
  403. return 0;
  404. }
  405. m_lasterror = wxSTREAM_NO_ERROR;
  406. // If the rest to read from the stream is less
  407. // than the buffer size, then only read the rest
  408. if ( m_pos + bufsize > m_size )
  409. bufsize = m_size - m_pos;
  410. if (m_contentStream->SeekI(m_pos) == wxInvalidOffset)
  411. {
  412. m_lasterror = wxSTREAM_EOF;
  413. return 0;
  414. }
  415. size_t read = m_contentStream->Read(buffer, bufsize).LastRead();
  416. m_pos += read;
  417. if (m_contentStream->SeekI(m_pos) == wxInvalidOffset)
  418. {
  419. m_lasterror = wxSTREAM_READ_ERROR;
  420. return 0;
  421. }
  422. if (read != bufsize)
  423. m_lasterror = m_contentStream->GetLastError();
  424. return read;
  425. }
  426. wxFileOffset wxChmInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
  427. {
  428. wxString mode_str = wxEmptyString;
  429. if ( !m_contentStream || m_contentStream->Eof() )
  430. {
  431. m_lasterror = wxSTREAM_EOF;
  432. return 0;
  433. }
  434. m_lasterror = wxSTREAM_NO_ERROR;
  435. wxFileOffset nextpos;
  436. switch ( mode )
  437. {
  438. case wxFromCurrent:
  439. nextpos = seek + m_pos;
  440. break;
  441. case wxFromStart:
  442. nextpos = seek;
  443. break;
  444. case wxFromEnd:
  445. nextpos = m_size - 1 + seek;
  446. break;
  447. default:
  448. nextpos = m_pos;
  449. break; /* just to fool compiler, never happens */
  450. }
  451. m_pos=nextpos;
  452. // Set current position on stream
  453. m_contentStream->SeekI(m_pos);
  454. return m_pos;
  455. }
  456. /**
  457. * Help Browser tries to read the contents of the
  458. * file by interpreting a .hhp file in the Archiv.
  459. * For .chm doesn't include such a file, we need
  460. * to rebuild the information based on stored
  461. * system-files.
  462. */
  463. void
  464. wxChmInputStream::CreateHHPStream()
  465. {
  466. wxFileName file;
  467. bool topic = false;
  468. bool hhc = false;
  469. bool hhk = false;
  470. wxInputStream *i;
  471. wxMemoryOutputStream *out;
  472. const char *tmp;
  473. // Try to open the #SYSTEM-File and create the HHP File out of it
  474. // see http://bonedaddy.net/pabs3/chmspec/0.1.2/Internal.html#SYSTEM
  475. if ( ! m_chm->Contains(wxT("/#SYSTEM")) )
  476. {
  477. #ifdef DEBUG
  478. wxLogDebug("Archive doesn't contain #SYSTEM file");
  479. #endif
  480. return;
  481. }
  482. else
  483. {
  484. file = wxFileName(wxT("/#SYSTEM"));
  485. }
  486. if ( CreateFileStream(wxT("/#SYSTEM")) )
  487. {
  488. // New stream for writing a memory area to simulate the
  489. // .hhp-file
  490. out = new wxMemoryOutputStream();
  491. tmp = "[OPTIONS]\r\n";
  492. out->Write((const void *) tmp, strlen(tmp));
  493. wxUint16 code;
  494. wxUint16 len;
  495. void *buf;
  496. // use the actual stream for reading
  497. i = m_contentStream;
  498. /* Now read the contents, and try to get the needed information */
  499. // First 4 Bytes are Version information, skip
  500. i->SeekI(4);
  501. while (!i->Eof())
  502. {
  503. // Read #SYSTEM-Code and length
  504. i->Read(&code, 2);
  505. code = wxUINT16_SWAP_ON_BE( code ) ;
  506. i->Read(&len, 2);
  507. len = wxUINT16_SWAP_ON_BE( len ) ;
  508. // data
  509. buf = malloc(len);
  510. i->Read(buf, len);
  511. switch (code)
  512. {
  513. case 0: // CONTENTS_FILE
  514. if (len)
  515. {
  516. tmp = "Contents file=";
  517. hhc=true;
  518. }
  519. break;
  520. case 1: // INDEX_FILE
  521. tmp = "Index file=";
  522. hhk = true;
  523. break;
  524. case 2: // DEFAULT_TOPIC
  525. tmp = "Default Topic=";
  526. topic = true;
  527. break;
  528. case 3: // TITLE
  529. tmp = "Title=";
  530. break;
  531. // case 6: // COMPILED_FILE
  532. // tmp = "Compiled File=";
  533. // break;
  534. case 7: // COMPILED_FILE
  535. tmp = "Binary Index=YES\r\n";
  536. out->Write( (const void *) tmp, strlen(tmp));
  537. tmp = NULL;
  538. break;
  539. case 4: // STRUCT SYSTEM INFO
  540. tmp = NULL ;
  541. if ( len >= 28 )
  542. {
  543. char *structptr = (char*) buf ;
  544. // LCID at position 0
  545. wxUint32 dummy = *((wxUint32 *)(structptr+0)) ;
  546. wxUint32 lcid = wxUINT32_SWAP_ON_BE( dummy ) ;
  547. char msg[64];
  548. int len = sprintf(msg, "Language=0x%X\r\n", lcid) ;
  549. if (len > 0)
  550. out->Write(msg, len) ;
  551. }
  552. break ;
  553. default:
  554. tmp=NULL;
  555. }
  556. if (tmp)
  557. {
  558. out->Write((const void *) tmp, strlen(tmp));
  559. out->Write(buf, strlen((char*)buf));
  560. out->Write("\r\n", 2);
  561. }
  562. free(buf);
  563. buf=NULL;
  564. }
  565. // Free the old data which wont be used any more
  566. delete m_contentStream;
  567. if (m_content)
  568. free (m_content);
  569. // Now add entries which are missing
  570. if ( !hhc && m_chm->Contains(wxT("*.hhc")) )
  571. {
  572. tmp = "Contents File=*.hhc\r\n";
  573. out->Write((const void *) tmp, strlen(tmp));
  574. }
  575. if ( !hhk && m_chm->Contains(wxT("*.hhk")) )
  576. {
  577. tmp = "Index File=*.hhk\r\n";
  578. out->Write((const void *) tmp, strlen(tmp));
  579. }
  580. // Now copy the Data from the memory
  581. out->SeekO(0, wxFromEnd);
  582. m_size = out->TellO();
  583. out->SeekO(0, wxFromStart);
  584. m_content = (char *) malloc (m_size+1);
  585. out->CopyTo(m_content, m_size);
  586. m_content[m_size]='\0';
  587. m_size++;
  588. m_contentStream = new wxMemoryInputStream(m_content, m_size);
  589. delete out;
  590. }
  591. }
  592. /**
  593. * Creates a Stream pointing to a virtual file in
  594. * the current archive
  595. */
  596. bool wxChmInputStream::CreateFileStream(const wxString& pattern)
  597. {
  598. wxFileInputStream * fin;
  599. wxString tmpfile = wxFileName::CreateTempFileName(wxT("chmstrm"));
  600. if ( tmpfile.empty() )
  601. {
  602. wxLogError(_("Could not create temporary file '%s'"), tmpfile.c_str());
  603. return false;
  604. }
  605. // try to extract the file
  606. if ( m_chm->Extract(pattern, tmpfile) <= 0 )
  607. {
  608. wxLogError(_("Extraction of '%s' into '%s' failed."),
  609. pattern.c_str(), tmpfile.c_str());
  610. if ( wxFileExists(tmpfile) )
  611. wxRemoveFile(tmpfile);
  612. return false;
  613. }
  614. else
  615. {
  616. // Open a filestream to extracted file
  617. fin = new wxFileInputStream(tmpfile);
  618. if (!fin->IsOk())
  619. return false;
  620. m_size = fin->GetSize();
  621. m_content = (char *) malloc(m_size+1);
  622. fin->Read(m_content, m_size);
  623. m_content[m_size]='\0';
  624. wxRemoveFile(tmpfile);
  625. delete fin;
  626. m_contentStream = new wxMemoryInputStream (m_content, m_size);
  627. return m_contentStream->IsOk();
  628. }
  629. }
  630. // ----------------------------------------------------------------------------
  631. // wxChmFSHandler
  632. // ----------------------------------------------------------------------------
  633. class wxChmFSHandler : public wxFileSystemHandler
  634. {
  635. public:
  636. /// Constructor and Destructor
  637. wxChmFSHandler();
  638. virtual ~wxChmFSHandler();
  639. /// Is able to open location?
  640. virtual bool CanOpen(const wxString& location);
  641. /// Open a file
  642. virtual wxFSFile* OpenFile(wxFileSystem& fs, const wxString& location);
  643. /// Find first occurrence of spec
  644. virtual wxString FindFirst(const wxString& spec, int flags = 0);
  645. /// Find next occurrence of spec
  646. virtual wxString FindNext();
  647. private:
  648. int m_lasterror;
  649. wxString m_pattern;
  650. wxString m_found;
  651. wxChmTools * m_chm;
  652. };
  653. wxChmFSHandler::wxChmFSHandler() : wxFileSystemHandler()
  654. {
  655. m_lasterror=0;
  656. m_pattern=wxEmptyString;
  657. m_found=wxEmptyString;
  658. m_chm=NULL;
  659. }
  660. wxChmFSHandler::~wxChmFSHandler()
  661. {
  662. if (m_chm)
  663. delete m_chm;
  664. }
  665. bool wxChmFSHandler::CanOpen(const wxString& location)
  666. {
  667. wxString p = GetProtocol(location);
  668. return (p == wxT("chm")) &&
  669. (GetProtocol(GetLeftLocation(location)) == wxT("file"));
  670. }
  671. wxFSFile* wxChmFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs),
  672. const wxString& location)
  673. {
  674. wxString right = GetRightLocation(location);
  675. wxString left = GetLeftLocation(location);
  676. wxInputStream *s;
  677. int index;
  678. if ( GetProtocol(left) != wxT("file") )
  679. {
  680. wxLogError(_("CHM handler currently supports only local files!"));
  681. return NULL;
  682. }
  683. // Work around javascript
  684. wxString tmp = wxString(right);
  685. if ( tmp.MakeLower().Contains(wxT("javascipt")) && tmp.Contains(wxT("\'")) )
  686. {
  687. right = right.AfterFirst(wxT('\'')).BeforeLast(wxT('\''));
  688. }
  689. // now work on the right location
  690. if (right.Contains(wxT("..")))
  691. {
  692. wxFileName abs(right);
  693. abs.MakeAbsolute(wxT("/"));
  694. right = abs.GetFullPath();
  695. }
  696. // a workaround for absolute links to root
  697. if ( (index=right.Index(wxT("//"))) != wxNOT_FOUND )
  698. {
  699. right=wxString(right.Mid(index+1));
  700. wxLogWarning(_("Link contained '//', converted to absolute link."));
  701. }
  702. wxFileName leftFilename = wxFileSystem::URLToFileName(left);
  703. if (!leftFilename.FileExists())
  704. return NULL;
  705. // Open a stream to read the content of the chm-file
  706. s = new wxChmInputStream(leftFilename.GetFullPath(), right, true);
  707. if ( s )
  708. {
  709. return new wxFSFile(s,
  710. left + wxT("#chm:") + right,
  711. wxEmptyString,
  712. GetAnchor(location),
  713. wxDateTime(leftFilename.GetModificationTime()));
  714. }
  715. delete s;
  716. return NULL;
  717. }
  718. /**
  719. * Doku see wxFileSystemHandler
  720. */
  721. wxString wxChmFSHandler::FindFirst(const wxString& spec, int flags)
  722. {
  723. wxString right = GetRightLocation(spec);
  724. wxString left = GetLeftLocation(spec);
  725. wxString nativename = wxFileSystem::URLToFileName(left).GetFullPath();
  726. if ( GetProtocol(left) != wxT("file") )
  727. {
  728. wxLogError(_("CHM handler currently supports only local files!"));
  729. return wxEmptyString;
  730. }
  731. m_chm = new wxChmTools(wxFileName(nativename));
  732. m_pattern = right.AfterLast(wxT('/'));
  733. wxString m_found = m_chm->Find(m_pattern);
  734. // now fake around hhp-files which are not existing in projects...
  735. if (m_found.empty() &&
  736. m_pattern.Contains(wxT(".hhp")) &&
  737. !m_pattern.Contains(wxT(".hhp.cached")))
  738. {
  739. m_found.Printf(wxT("%s#chm:%s.hhp"),
  740. left.c_str(), m_pattern.BeforeLast(wxT('.')).c_str());
  741. }
  742. return m_found;
  743. }
  744. wxString wxChmFSHandler::FindNext()
  745. {
  746. if (m_pattern.empty())
  747. return wxEmptyString;
  748. else
  749. return m_chm->Find(m_pattern, m_found);
  750. }
  751. // ---------------------------------------------------------------------------
  752. // wxModule to register CHM handler
  753. // ---------------------------------------------------------------------------
  754. class wxChmSupportModule : public wxModule
  755. {
  756. DECLARE_DYNAMIC_CLASS(wxChmSupportModule)
  757. public:
  758. virtual bool OnInit()
  759. {
  760. wxFileSystem::AddHandler(new wxChmFSHandler);
  761. return true;
  762. }
  763. virtual void OnExit() {}
  764. }
  765. ;
  766. IMPLEMENT_DYNAMIC_CLASS(wxChmSupportModule, wxModule)
  767. #endif // wxUSE_LIBMSPACK