You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdevelop/parts/documentation/protocols/chm/chm.cpp

363 lines
12 KiB

/* This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <qcstring.h>
#include <qbitarray.h>
#include <qfile.h>
#include <qregexp.h>
#include <stack>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kinstance.h>
#include <kglobal.h>
#include <kurl.h>
#include <kmimemagic.h>
#include <ktempfile.h>
#include "chm.h"
using namespace KIO;
extern "C"
{
int kdemain( int argc, char **argv )
{
KInstance instance( "kio_chm" );
kdDebug() << "*** Starting kio_chm " << endl;
if (argc != 4) {
kdDebug() << "Usage: kio_chm protocol domain-socket1 domain-socket2" << endl;
exit(-1);
}
ChmProtocol slave(argv[2], argv[3]);
slave.dispatchLoop();
kdDebug() << "*** kio_chm Done" << endl;
return 0;
}
}
ChmProtocol::ChmProtocol(const QCString &pool_socket, const QCString &app_socket)
: SlaveBase("kio_chm", pool_socket, app_socket)
{
kdDebug() << "ChmProtocol::ChmProtocol()" << endl;
}
/* ---------------------------------------------------------------------------------- */
ChmProtocol::~ChmProtocol()
{
kdDebug() << "ChmProtocol::~ChmProtocol()" << endl;
}
/* ---------------------------------------------------------------------------------- */
void ChmProtocol::get( const KURL& url )
{
/** When :catalog is appended to the end, a plain-text representation of the catalog
* is given out where each entry consists of four lines, an integer representing the parent
* of the node, an integer representing a node's ID, the Title of the Node, and it's hyperlink.
* When :contents is appended, all contained htm- and html-files will be printed, each in a line.
*/
kdDebug() << "kio_chm::get(const KURL& url) " << url.path() << endl;
bool catalog = false;
bool contents = false;
QString bigpath = url.path();
if(bigpath.endsWith(":catalog")) {
catalog = true;
int len = QString(":catalog").length();
bigpath.remove(bigpath.length() - len, len); ///strip :catalog from the end
}
if(bigpath.endsWith(":contents")) {
contents = true;
int len = QString(":contents").length();
bigpath.remove(bigpath.length() - len, len); ///strip :catalog from the end
}
QString path;
if ( !checkNewFile( bigpath, path ) ) {
error( KIO::ERR_DOES_NOT_EXIST, url.prettyURL() );
return;
}
if (m_dirMap.find(path) == m_dirMap.end()) {
error( KIO::ERR_DOES_NOT_EXIST, url.prettyURL() );
return;
}
QByteArray theData;
//init..
//added by lucida lucida@users.sf.net
QString fname = QString();
QString chmpath = QString();
KTempFile f("",".html");
fname = f.name();
QTextStream *t = f.textStream();
QString firstPage = QString("");
QString m_strIndex = QString("");
QString tmpstr = QString("");
bool m_bIndex = 0;
if(contents) {
QString output;
KURL u = url;
ChmDirectoryMap::Iterator it;
for ( it = m_dirMap.begin(); it != m_dirMap.end(); ++it) {
u.setPath(bigpath);
u.addPath(it.key());
output += u.prettyURL() + "\n";
}
data(output.local8Bit());
processedSize(output.length());
finished();
return;
}
//try get some page to display, if the chm missing index
ChmDirectoryMap::Iterator it;
for ( it = m_dirMap.begin(); it != m_dirMap.end(); ++it) {
tmpstr.sprintf("%s", it.key().latin1());
if ((m_strIndex == "") &&
(tmpstr.endsWith(".htm") || tmpstr.endsWith(".html")))
m_strIndex = tmpstr;
if ((tmpstr == "/index.htm") || (tmpstr == "/index.html")) {
m_strIndex = tmpstr;
break;
}
}
m_strIndex.remove(0,1);
if (path == "/" || catalog) {
bool htmlOutput = !catalog;
int offset = m_dirMap["/@contents"].offset;
int length = m_dirMap["/@contents"].length;
theData.setRawData(&m_contents[offset], length);
QString s(theData);
QString output;
QRegExp object("<OBJECT type=\"text/sitemap\">(.*)</OBJECT>", false);
object.setMinimal(true);
QRegExp nameParam("<param name=\"Name\" value=\"(.*)\">", false);
nameParam.setMinimal(true);
QRegExp localParam("<param name=\"Local\" value=\"(.*)\">", false);
localParam.setMinimal(true);
QRegExp mergeParam("<param name=\"Merge\" value=\"(.*)\">", false);
mergeParam.setMinimal(true);
std::stack<int> parents;
int counter = 1;
int current = 0;
int old = 0, pos = 0;
parents.push(0);
while ((pos = s.find(object, pos)) != -1) {
if(htmlOutput) output += s.mid(old, pos - old);
if(catalog) {
QRegExp ex("<UL>|</UL>", false); ex.setMinimal(true);
QString ms = s.mid(old, pos - old);
int pos = 0;
while( (pos = ms.find(ex, pos)) != -1) {
if(ms.mid(pos, 4) == "<UL>") {
parents.push(current);
} else{
if(parents.empty()){
}else{
current = parents.top();
parents.pop();
}
}
pos++;
}
}
pos += object.matchedLength();
old = pos;
QString obj = object.cap(1);
QString name, local;
if (obj.find(nameParam) != -1) {
name = nameParam.cap(1);
if (obj.find(localParam) != -1) {
local = localParam.cap(1);
//output += "<a href=\"" + local + "\">" + name + "</a>";
//added by lucida lucida@users.sf.net
if (local != "" && local != "/") {
if(!catalog) {
output += "<a target=\"browse\" href=\"" + url.url() + local + "\">" + name + "</a>";
}else{
current = counter;
++counter;
KURL u = url;
u.setPath(bigpath + local);
QString str;
output += str.sprintf("%i\n%i\n", parents.top(), current);
output += name + "\n" + u.prettyURL() + "\n";
}
m_bIndex = 1;
if (firstPage == "") firstPage = url.url()+QString::fromLocal8Bit(local.latin1());
}
else
if(htmlOutput) output += name;
} else {
if(htmlOutput) output += name;
}
}
if (obj.find(mergeParam) != -1 && htmlOutput) {
QString link = mergeParam.cap(1);
QString href = link.left(link.find("::"));
QString path = m_chmFile.left(m_chmFile.findRev("/") + 1);
//output += " (<a href=\"" + path + href + "\">link</a>)";
m_bIndex = 1;
output += " (<a target=\"browse\" href=\"" + url.url() + path + href + "\">link</a>)";
if (firstPage == "") firstPage = url.url()+QString::fromLocal8Bit(local.latin1());
}
}
if(htmlOutput) output += s.mid(old);
//set left pane
//added by lucida, lucida@users.sf.net
QString lframe = QString("</HEAD><FRAMESET COLS=\"25%,*\">\n");
lframe += "<FRAME NAME=\"index\" src=\"file:"+ fname+"\"" + " marginwidth=\"0\"></FRAME>\n";
if (!m_bIndex) {
lframe = "</HEAD><FRAMESET>";
firstPage = url.url() + QString::fromLocal8Bit(m_strIndex.latin1());
}
theData.resetRawData(&m_contents[offset], length);
//KMimeMagicResult * result = KMimeMagic::self()->findBufferFileType( output, path );
//kdDebug() << "Emitting mimetype " << result->mimeType() << endl;
//mimeType( result->mimeType() );
/* QCString output1 = (QCString)(output.latin1());
data(output1);
processedSize(output1.length());*/
//construct the frame
//added by lucida lucida@users.sf.net
QString framestr = QString("<HTML><HEAD>\n");
framestr += lframe;
framestr += "<FRAME NAME=\"browse\" src=\"" + firstPage + "\">\n";
framestr += "</FRAME>\n";
framestr += "</FRAMESET></HTML>";
//write index file
//added by lucida lucida@users.sf.net
*t << QString::fromLocal8Bit(output.latin1()) << endl;
if(catalog) {
data(output.local8Bit());
processedSize(output.length());
}else{
data(framestr.local8Bit());
processedSize(framestr.length());
}
} else {
int offset = m_dirMap[path].offset;
int length = m_dirMap[path].length;
totalSize(length);
theData.setRawData(&m_contents[offset], length);
KMimeMagicResult * result = KMimeMagic::self()->findBufferFileType( theData, path );
kdDebug() << "Emitting mimetype " << result->mimeType() << endl;
mimeType( result->mimeType() );
data(theData);
theData.resetRawData(&m_contents[offset], length);
processedSize(length);
}
finished();
}
/* --------------------------------------------------------------------------- */
bool ChmProtocol::checkNewFile( QString fullPath, QString& path )
{
//kdDebug() << "ChmProtocol::checkNewFile " << fullPath << endl;
fullPath = fullPath.replace(QRegExp("::"), "");
// Are we already looking at that file ?
if ( !m_chmFile.isEmpty() && fullPath.startsWith(m_chmFile) )
{
path = fullPath.mid(m_chmFile.length()).lower();
return true;
}
kdDebug() << "Need to open a new file" << endl;
m_chmFile = "";
// Find where the chm file is in the full path
int pos = 0;
QString chmFile;
path = "";
int len = fullPath.length();
if ( len != 0 && fullPath[ len - 1 ] != '/' )
fullPath += '/';
//kdDebug() << "the full path is " << fullPath << endl;
while ( (pos=fullPath.find( '/', pos+1 )) != -1 )
{
QString tryPath = fullPath.left( pos );
//kdDebug() << fullPath << " trying " << tryPath << endl;
struct stat statbuf;
if ( ::stat( QFile::encodeName(tryPath), &statbuf ) == 0 && !S_ISDIR(statbuf.st_mode) )
{
chmFile = tryPath;
path = fullPath.mid( pos ).lower();
kdDebug() << "fullPath=" << fullPath << " path=" << path << endl;
len = path.length();
if ( len > 2 )
{
if ( path[ len - 1 ] == '/' )
path.truncate( len - 1 );
}
else
{
path = QString::fromLatin1("/");
}
kdDebug() << "Found. chmFile=" << chmFile << " path=" << path << endl;
break;
}
}
if ( chmFile.isEmpty() )
{
kdDebug() << "ChmProtocol::checkNewFile: not found" << endl;
return false;
}
m_chmFile = chmFile;
// Open new file
//kdDebug() << "Opening Chm file on " << chmFile << endl;
return m_chm.read(chmFile, m_dirMap, m_contents);
}