chm.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 909 行 · 第 1/2 页

CPP
909
字号

wxChmInputStream::~wxChmInputStream()
{
    delete m_chm;

    delete m_contentStream;

    if (m_content)
    {
        free (m_content);
        m_content=NULL;
    }
}

bool wxChmInputStream::Eof() const
{
    return (m_content==NULL ||
            m_contentStream==NULL ||
            m_contentStream->Eof() ||
            m_pos>m_size);
}



size_t wxChmInputStream::OnSysRead(void *buffer, size_t bufsize)
{
    if ( m_pos >= m_size )
    {
        m_lasterror = wxSTREAM_EOF;
        return 0;
    }
    m_lasterror = wxSTREAM_NO_ERROR;

    // If the rest to read from the stream is less
    // than the buffer size, than only read the rest
    if ( m_pos + bufsize > m_size )
        bufsize = m_size - m_pos;

    m_contentStream->SeekI(m_pos);
    m_contentStream->Read(buffer, bufsize);
    m_pos +=bufsize;
    m_contentStream->SeekI(m_pos);
    return bufsize;
}




wxFileOffset wxChmInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
{
    wxString mode_str = wxEmptyString;

    if ( !m_contentStream || m_contentStream->Eof() )
    {
        m_lasterror = wxSTREAM_EOF;
        return 0;
    }
    m_lasterror = wxSTREAM_NO_ERROR;

    wxFileOffset nextpos;

    switch ( mode )
    {
        case wxFromCurrent:
            nextpos = seek + m_pos;
            break;
        case wxFromStart:
            nextpos = seek;
            break;
        case wxFromEnd:
            nextpos = m_size - 1 + seek;
            break;
        default:
            nextpos = m_pos;
            break; /* just to fool compiler, never happens */
    }
    m_pos=nextpos;

    // Set current position on stream
    m_contentStream->SeekI(m_pos);
    return m_pos;
}



/**
 * Help Browser tries to read the contents of the
 * file by interpreting a .hhp file in the Archiv.
 * For .chm doesnt include such a file, we need
 * to rebuild the information based on stored
 * system-files.
 */
void
wxChmInputStream::CreateHHPStream()
{
    wxFileName file;
    bool topic = false;
    bool hhc = false;
    bool hhk = false;
    wxInputStream *i;
    wxMemoryOutputStream *out;
    const char *tmp;

    // Try to open the #SYSTEM-File and create the HHP File out of it
    // see http://bonedaddy.net/pabs3/chmspec/0.1.2/Internal.html#SYSTEM
    if ( ! m_chm->Contains(_T("/#SYSTEM")) )
    {
#ifdef DEBUG
        wxLogDebug(_("Archive doesnt contain #SYSTEM file"));
#endif
        return;
    }
    else
    {
        file = wxFileName(_T("/#SYSTEM"));
    }

    if ( CreateFileStream(_T("/#SYSTEM")) )
    {
        // New stream for writing a memory area to simulate the
        // .hhp-file
        out = new wxMemoryOutputStream();

        tmp = "[OPTIONS]\r\n";
        out->Write((const void *) tmp, strlen(tmp));

        wxUint16 code;
        wxUint16 len;
        void *buf;

        // use the actual stream for reading
        i = m_contentStream;

        /* Now read the contents, and try to get the needed information */

        // First 4 Bytes are Version information, skip
        i->SeekI(4);

        while (!i->Eof())
        {
            // Read #SYSTEM-Code and length
            i->Read(&code, 2);
            code = wxUINT16_SWAP_ON_BE( code ) ;
            i->Read(&len, 2);
            len = wxUINT16_SWAP_ON_BE( len ) ;
            // data
            buf = malloc(len);
            i->Read(buf, len);

            switch (code)
            {
                case 0: // CONTENTS_FILE
                    tmp = "Contents file=";
                    hhc=true;
                    break;
                case 1: // INDEX_FILE
                    tmp = "Index file=";
                    hhk = true;
                    break;
                case 2: // DEFAULT_TOPIC
                    tmp = "Default Topic=";
                    topic = true;
                    break;
                case 3: // TITLE
                    tmp = "Title=";
                    break;
                //       case 6: // COMPILED_FILE
                //         tmp = "Compiled File=";
                //         break;
                case 7: // COMPILED_FILE
                    tmp = "Binary Index=YES\r\n";
                    out->Write( (const void *) tmp, strlen(tmp));
                    tmp = NULL;
                    break;
                case 4: // STRUCT SYSTEM INFO
                    tmp = NULL ;
                    if ( len >= 28 )
                    {
                        char *structptr = (char*) buf ;
                        // LCID at position 0
                        wxUint32 dummy = *((wxUint32 *)(structptr+0)) ;
                        wxUint32 lcid = wxUINT32_SWAP_ON_BE( dummy ) ;
                        wxString msg ;
                        msg.Printf(_T("Language=0x%X\r\n"),lcid) ;
                        out->Write(msg.c_str() , msg.Length() ) ;
                    }
                    break ;
                default:
                    tmp=NULL;
            }

            if (tmp)
            {
                out->Write((const void *) tmp, strlen(tmp));
                out->Write(buf, strlen((char*)buf));
                out->Write("\r\n", 2);
            }

            free(buf);
            buf=NULL;
        }


        // Free the old data which wont be used any more
        delete m_contentStream;
        if (m_content)
            free (m_content);

        // Now add entries which are missing
        if ( !hhc && m_chm->Contains(_T("*.hhc")) )
        {
            tmp = "Contents File=*.hhc\r\n";
            out->Write((const void *) tmp, strlen(tmp));
        }

        if ( !hhk && m_chm->Contains(_T("*.hhk")) )
        {
            tmp = "Index File=*.hhk\r\n";
            out->Write((const void *) tmp, strlen(tmp));
        }
        
        // Now copy the Data from the memory
        out->SeekO(0, wxFromEnd);
        m_size = out->TellO();
        out->SeekO(0, wxFromStart);
        m_content = (char *) malloc (m_size+1);
        out->CopyTo(m_content, m_size);
        m_content[m_size]='\0';
        m_size++;
        m_contentStream = new wxMemoryInputStream(m_content, m_size);

        delete out;
    }
}


/**
 * Creates a Stream pointing to a virtual file in
 * the current archive
 */
bool wxChmInputStream::CreateFileStream(const wxString& pattern)
{
    wxFileInputStream * fin;
    wxString tmpfile = wxFileName::CreateTempFileName(_T("chmstrm"), NULL);

    if ( tmpfile.empty() )
    {
        wxLogError(_("Could not create temporary file '%s'"), tmpfile.c_str());
        return false;
    }

    // try to extract the file
    if ( m_chm->Extract(pattern, tmpfile) <= 0 )
    {
        wxLogError(_("Extraction of '%s' into '%s' failed."),
                   pattern.c_str(), tmpfile.c_str());
        if ( wxFileExists(tmpfile) )
            wxRemoveFile(tmpfile);
        return false;
    }
    else
    {
        // Open a filestream to extracted file
        fin = new wxFileInputStream(tmpfile);
        m_size = fin->GetSize();
        m_content = (char *) malloc(m_size+1);
        fin->Read(m_content, m_size);
        m_content[m_size]='\0';

        wxRemoveFile(tmpfile);

        delete fin;

        m_contentStream = new wxMemoryInputStream (m_content, m_size);

        return m_contentStream->IsOk();
    }
}



// ----------------------------------------------------------------------------
// wxChmFSHandler
// ----------------------------------------------------------------------------

class wxChmFSHandler : public wxFileSystemHandler
{
public:
    /// Constructor and Destructor
    wxChmFSHandler();
    ~wxChmFSHandler();

    /// Is able to open location?
    virtual bool CanOpen(const wxString& location);
    /// Open a file
    virtual wxFSFile* OpenFile(wxFileSystem& fs, const wxString& location);
    /// Find first occurrence of spec
    virtual wxString FindFirst(const wxString& spec, int flags = 0);
    /// Find next occurrence of spec
    virtual wxString FindNext();

private:
    int m_lasterror;
    wxString m_pattern;
    wxString m_found;
    wxChmTools * m_chm;
};

wxChmFSHandler::wxChmFSHandler() : wxFileSystemHandler()
{
    m_lasterror=0;
    m_pattern=wxEmptyString;
    m_found=wxEmptyString;
    m_chm=NULL;
}

wxChmFSHandler::~wxChmFSHandler()
{
    if (m_chm)
        delete m_chm;
}

bool wxChmFSHandler::CanOpen(const wxString& location)
{
    wxString p = GetProtocol(location);
    return (p == _T("chm")) &&
           (GetProtocol(GetLeftLocation(location)) == _T("file"));
}

wxFSFile* wxChmFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs),
                                   const wxString& location)
{
    wxString right = GetRightLocation(location);
    wxString left = GetLeftLocation(location);

    wxInputStream *s;

    int index;

    if ( GetProtocol(left) != _T("file") )
    {
        wxLogError(_("CHM handler currently supports only local files!"));
        return NULL;
    }

    // Work around javascript
    wxString tmp = wxString(right);
    if ( tmp.MakeLower().Contains(_T("javascipt")) && tmp.Contains(_T("\'")) )
    {
        right = right.AfterFirst(_T('\'')).BeforeLast(_T('\''));
    }

    // now work on the right location
    if (right.Contains(_T("..")))
    {
        wxFileName abs(right);
        abs.MakeAbsolute(_T("/"));
        right = abs.GetFullPath();
    }

    // a workaround for absolute links to root
    if ( (index=right.Index(_T("//"))) != wxNOT_FOUND )
    {
        right=wxString(right.Mid(index+1));
        wxLogWarning(_("Link contained '//', converted to absolute link."));
    }

    wxFileName leftFilename = wxFileSystem::URLToFileName(left);

    // Open a stream to read the content of the chm-file
    s = new wxChmInputStream(leftFilename.GetFullPath(), right, true);

    wxString mime = GetMimeTypeFromExt(location);

    if ( s )
    {
        return new wxFSFile(s,
                            left + _T("#chm:") + right,
                            mime,
                            GetAnchor(location),
                            wxDateTime(wxFileModificationTime(left)));
    }

    delete s;
    return NULL;
}



/**
 * Doku see wxFileSystemHandler
 */
wxString wxChmFSHandler::FindFirst(const wxString& spec, int flags)
{
    wxString right = GetRightLocation(spec);
    wxString left = GetLeftLocation(spec);
    wxString nativename = wxFileSystem::URLToFileName(left).GetFullPath();

    if ( GetProtocol(left) != _T("file") )
    {
        wxLogError(_("CHM handler currently supports only local files!"));
        return wxEmptyString;
    }

    m_chm = new wxChmTools(wxFileName(nativename));
    m_pattern = right.AfterLast(_T('/'));

    wxString m_found = m_chm->Find(m_pattern);

    // now fake around hhp-files which are not existing in projects...
    if (m_found.empty() &&
        m_pattern.Contains(_T(".hhp")) &&
        !m_pattern.Contains(_T(".hhp.cached")))
    {
        m_found.Printf(_T("%s#chm:%s.hhp"),
                       left.c_str(), m_pattern.BeforeLast(_T('.')).c_str());
    }

    return m_found;

}



wxString wxChmFSHandler::FindNext()
{
    if (m_pattern.empty())
        return wxEmptyString;
    else
        return m_chm->Find(m_pattern, m_found);
}

// ---------------------------------------------------------------------------
// wxModule to register CHM handler
// ---------------------------------------------------------------------------

class wxChmSupportModule : public wxModule
{
    DECLARE_DYNAMIC_CLASS(wxChmSupportModule)

public:
    virtual bool OnInit()
    {
        wxFileSystem::AddHandler(new wxChmFSHandler);
        return true;
    }
    virtual void OnExit() {}
}
;

IMPLEMENT_DYNAMIC_CLASS(wxChmSupportModule, wxModule)

#endif // wxUSE_LIBMSPACK

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?