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 + -
显示快捷键?