chm.cpp
来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 909 行 · 第 1/2 页
CPP
909 行
/////////////////////////////////////////////////////////////////////////////
// Name: chm.cpp
// Purpose: CHM (Help) support for wxHTML
// Author: Markus Sinner
// Copyright: (c) 2003 Herd Software Development
// CVS-ID: $Id: chm.cpp,v 1.9 2005/05/31 09:20:12 JS Exp $
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_LIBMSPACK
#include <mspack.h>
#ifndef WXPRECOMP
#include "wx/intl.h"
#include "wx/log.h"
#endif
#include "wx/module.h"
#include "wx/filesys.h"
#include "wx/mstream.h"
#include "wx/wfstream.h"
#include "wx/html/forcelnk.h"
FORCE_LINK_ME(wxhtml_chm_support)
// ----------------------------------------------------------------------------
/// wxChmTools
/// <p>
/// this class is used to abstract access to CHM-Archives
/// with library mspack written by Stuart Caie
/// http://www.kyz.uklinux.net/libmspack/
// ----------------------------------------------------------------------------
class wxChmTools
{
public:
/// constructor
wxChmTools(const wxFileName &archive);
/// destructor
~wxChmTools();
/// Generate error-string for error-code
static const wxString ChmErrorMsg(int error);
/// get an array of archive-member-filenames
const wxArrayString *GetFileNames()
{
return m_fileNames;
};
/// get the name of the archive representated by this class
const wxString GetArchiveName()
{
return m_chmFileName;
};
/// Find a file in the archive
const wxString Find(const wxString& pattern,
const wxString& startfrom = wxEmptyString);
/// Extract a file in the archive into a file
size_t Extract(const wxString& pattern, const wxString& filename);
/// check archive for a file
bool Contains(const wxString& pattern);
/// get a string for the last error which occurred
const wxString GetLastErrorMessage();
/// Last Error
int m_lasterror;
private:
// these vars are used by FindFirst/Next:
wxString m_chmFileName;
char *m_chmFileNameANSI;
/// mspack-pointer to mschmd_header
struct mschmd_header *m_archive;
/// mspack-pointer to mschm_decompressor
struct mschm_decompressor *m_decompressor;
/// Array of filenames in archive
wxArrayString * m_fileNames;
/// Internal function to get filepointer
struct mschmd_file *GetMschmdFile(const wxString& pattern);
};
/***
* constructor
*
* @param archive The filename of the archive to open
*/
wxChmTools::wxChmTools(const wxFileName &archive)
{
m_chmFileName = archive.GetFullPath();
wxASSERT_MSG( !m_chmFileName.empty(), _T("empty archive name") );
m_archive = NULL;
m_decompressor = NULL;
m_fileNames = NULL;
m_lasterror = 0;
struct mschmd_header *chmh;
struct mschm_decompressor *chmd;
struct mschmd_file *file;
// Create decompressor
chmd = mspack_create_chm_decompressor(NULL);
m_decompressor = (struct mschm_decompressor *) chmd;
// NB: we must make a copy of the string because chmd->open won't call
// strdup() [libmspack-20030726], which would cause crashes in
// Unicode build when mb_str() returns temporary buffer
m_chmFileNameANSI = strdup((const char*)m_chmFileName.mb_str(wxConvFile));
// Open the archive and store it in class:
if ( (chmh = chmd->open(chmd, (char*)m_chmFileNameANSI)) )
{
m_archive = chmh;
// Create Filenamearray
m_fileNames = new wxArrayString;
// Store Filenames in array
for (file = chmh->files; file; file = file->next)
{
m_fileNames->Add(wxString::FromAscii(file->filename));
}
}
else
{
wxLogError(_("Failed to open CHM archive '%s'."),
archive.GetFullPath().c_str());
m_lasterror = (chmd->last_error(chmd));
return;
}
}
/***
* Destructor
*/
wxChmTools::~wxChmTools()
{
struct mschm_decompressor *chmd = m_decompressor;
struct mschmd_header *chmh = m_archive;
delete m_fileNames;
// Close Archive
if (chmh && chmd)
chmd->close(chmd, chmh);
free(m_chmFileNameANSI);
// Destroy Decompressor
if (chmd)
mspack_destroy_chm_decompressor(chmd);
}
/**
* Checks if the given pattern matches to any
* filename stored in archive
*
* @param pattern The filename pattern, may include '*' and/or '?'
* @return true, if any file matching pattern has been found,
* false if not
*/
bool wxChmTools::Contains(const wxString& pattern)
{
int count;
wxString pattern_tmp = wxString(pattern).MakeLower();
// loop through filearay
if ( m_fileNames && (count = m_fileNames->GetCount()) > 0 )
{
for (int i = 0; i < count; i++)
{
wxString tmp = m_fileNames->Item(i).MakeLower();
if ( tmp.Matches(pattern_tmp) || tmp.Mid(1).Matches(pattern_tmp))
return true;
}
}
return false;
}
/**
* Find()
*
* Finds the next file descibed by a pattern in the archive, starting
* the file given by second parameter
*
* @param pattern The file-pattern to search for. May contain '*' and/or '?'
* @param startfrom The filename which the search should start after
* @returns The full pathname of the found file
*/
const wxString wxChmTools::Find(const wxString& pattern,
const wxString& startfrom)
{
int count;
wxString tmp;
wxString pattern_tmp(pattern);
wxString startfrom_tmp(startfrom);
pattern_tmp.MakeLower();
startfrom_tmp.MakeLower();
if ( m_fileNames && (count = m_fileNames->GetCount()) > 0 )
{
for (int i = 0; i < count; i++)
{
tmp = m_fileNames->Item(i).MakeLower();
// if we find the string where the search should began
if ( tmp.Matches(startfrom_tmp) ||
tmp.Mid(1).Matches(startfrom_tmp) )
continue;
if ( tmp.Matches(pattern_tmp) ||
tmp.Mid(1).Matches(pattern_tmp) )
{
return tmp;
}
}
}
return wxEmptyString;
}
/**
* Extract ()
*
* extracts the first hit of pattern to the given position
*
* @param pattern A filename pattern (may contain * and ? chars)
* @param filename The FileName where to temporary extract the file to
* @return 0 at no file extracted<br>
* number of bytes extracted else
*/
size_t wxChmTools::Extract(const wxString& pattern, const wxString& filename)
{
struct mschm_decompressor *d = m_decompressor;
struct mschmd_header *h = m_archive;
struct mschmd_file *f;
wxString tmp;
wxString pattern_tmp = (wxString(pattern)).MakeLower();
for (f = h->files; f; f = f->next)
{
tmp = wxString::FromAscii(f->filename).MakeLower();
if ( tmp.Matches(pattern_tmp) ||
tmp.Mid(1).Matches(pattern_tmp) )
{
// ignore leading '/'
if (d->extract(d, f,
(char*)(const char*)filename.mb_str(wxConvFile)))
{
// Error
m_lasterror = d->last_error(d);
wxLogError(_("Could not extract %s into %s: %s"),
wxString::FromAscii(f->filename).c_str(),
filename.c_str(),
ChmErrorMsg(m_lasterror).c_str());
return 0;
}
else
{
return (size_t) f->length;
}
}
}
return 0;
}
/**
* Find a file by pattern
*
* @param pattern A filename pattern (may contain * and ? chars)
* @return A pointer to the file (mschmd_file*)
*/
struct mschmd_file *wxChmTools::GetMschmdFile(const wxString& pattern_orig)
{
struct mschmd_file *f;
struct mschmd_header *h = (struct mschmd_header *) m_archive;
wxString tmp;
wxString pattern = wxString(pattern_orig).MakeLower();
for (f = h->files; f; f = f->next)
{
tmp = wxString::FromAscii(f->filename).MakeLower();
if ( tmp.Matches(pattern) || tmp.Mid(1).Matches(pattern) )
{
// ignore leading '/'
return f;
}
}
return NULL;
}
const wxString wxChmTools::GetLastErrorMessage()
{
return ChmErrorMsg(m_lasterror);
}
const wxString wxChmTools::ChmErrorMsg(int error)
{
switch (error)
{
case MSPACK_ERR_OK:
return _("no error");
case MSPACK_ERR_ARGS:
return _("bad arguments to library function");
case MSPACK_ERR_OPEN:
return _("error opening file");
case MSPACK_ERR_READ:
return _("read error");
case MSPACK_ERR_WRITE:
return _("write error");
case MSPACK_ERR_SEEK:
return _("seek error");
case MSPACK_ERR_NOMEMORY:
return _("out of memory");
case MSPACK_ERR_SIGNATURE:
return _("bad signature");
case MSPACK_ERR_DATAFORMAT:
return _("error in data format");
case MSPACK_ERR_CHECKSUM:
return _("checksum error");
case MSPACK_ERR_CRUNCH:
return _("compression error");
case MSPACK_ERR_DECRUNCH:
return _("decompression error");
}
return _("unknown error");
}
// ---------------------------------------------------------------------------
/// wxChmInputStream
// ---------------------------------------------------------------------------
class wxChmInputStream : public wxInputStream
{
public:
/// Constructor
wxChmInputStream(const wxString& archive,
const wxString& file, bool simulate = false);
/// Destructor
~wxChmInputStream();
/// Return the size of the accessed file in archive
virtual size_t GetSize() const { return m_size; }
/// End of Stream?
virtual bool Eof() const;
/// Set simulation-mode of HHP-File (if non is found)
void SimulateHHP(bool sim) { m_simulateHHP = sim; }
protected:
/// See wxInputStream
virtual size_t OnSysRead(void *buffer, size_t bufsize);
/// See wxInputStream
virtual wxFileOffset OnSysSeek(wxFileOffset seek, wxSeekMode mode);
/// See wxInputStream
virtual wxFileOffset OnSysTell() const { return m_pos; }
private:
size_t m_size;
wxFileOffset m_pos;
bool m_simulateHHP;
char * m_content;
wxInputStream * m_contentStream;
void CreateHHPStream();
bool CreateFileStream(const wxString& pattern);
// this void* is handle of archive . I'm sorry it is void and not proper
// type but I don't want to make unzip.h header public.
// locates the file and returns a mspack_file *
mspack_file *LocateFile(wxString filename);
// should store pointer to current file
mspack_file *m_file;
// The Chm-Class for extracting the data
wxChmTools *m_chm;
wxString m_fileName;
};
/**
* Constructor
* @param archive The name of the .chm archive. Remember that archive must
* be local file accesible via fopen, fread functions!
* @param filename The Name of the file to be extracted from archive
* @param simulate if true than class should simulate .HHP-File based on #SYSTEM
* if false than class does nothing if it doesnt find .hhp
*/
wxChmInputStream::wxChmInputStream(const wxString& archive,
const wxString& filename, bool simulate)
: wxInputStream()
{
m_pos = 0;
m_size = 0;
m_content = NULL;
m_contentStream = NULL;
m_lasterror = wxSTREAM_NO_ERROR;
m_chm = new wxChmTools (wxFileName(archive));
m_file = NULL;
m_fileName = wxString(filename).MakeLower();
m_simulateHHP = simulate;
if ( !m_chm->Contains(m_fileName) )
{
// if the file could not be located, but was *.hhp, than we create
// the content of the hhp-file on the fly and store it for reading
// by the application
if ( m_fileName.Find(_T(".hhp")) != wxNOT_FOUND && m_simulateHHP )
{
// now we open an hhp-file
CreateHHPStream();
}
else
{
wxLogError(_("Could not locate file '%s'."), filename.c_str());
m_lasterror = wxSTREAM_READ_ERROR;
return;
}
}
else
{ // file found
CreateFileStream(m_fileName);
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?