00001 #include <libxslt/xsltconfig.h>
00002 #include <libxslt/xsltInternals.h>
00003 #include <libxslt/transform.h>
00004 #include <libxslt/xsltutils.h>
00005 #include <libxml/xmlIO.h>
00006 #include <libxml/parserInternals.h>
00007 #include <libxml/catalog.h>
00008 #include <kdebug.h>
00009 #include <kstandarddirs.h>
00010 #include <qdir.h>
00011 #include <qregexp.h>
00012 #include <xslt.h>
00013 #include <kinstance.h>
00014 #include "kio_help.h"
00015 #include <klocale.h>
00016 #include <assert.h>
00017 #include <kfilterbase.h>
00018 #include <kfilterdev.h>
00019 #include <qtextcodec.h>
00020 #include <stdlib.h>
00021 #include <config.h>
00022 #include <stdarg.h>
00023 #include <klibloader.h>
00024 #include <kcharsets.h>
00025 #include <gzip/kgzipfilter.h>
00026 #include <bzip2/kbzip2filter.h>
00027 #include <klibloader.h>
00028 #include <qvaluevector.h>
00029
00030 #if !defined( SIMPLE_XSLT )
00031 extern HelpProtocol *slave;
00032 #define INFO( x ) if (slave) slave->infoMessage(x);
00033 #else
00034 #define INFO( x )
00035 #endif
00036
00037 int writeToQString(void * context, const char * buffer, int len)
00038 {
00039 QString *t = (QString*)context;
00040 *t += QString::fromUtf8(buffer, len);
00041 return len;
00042 }
00043
00044 int closeQString(void * context) {
00045 QString *t = (QString*)context;
00046 *t += '\n';
00047 return 0;
00048 }
00049
00050 QString transform( const QString &pat, const QString& tss,
00051 const QValueVector<const char *> ¶ms )
00052 {
00053 QString parsed;
00054
00055 INFO(i18n("Parsing stylesheet"));
00056
00057 xsltStylesheetPtr style_sheet =
00058 xsltParseStylesheetFile((const xmlChar *)tss.latin1());
00059
00060 if ( !style_sheet ) {
00061 return parsed;
00062 }
00063
00064 if (style_sheet->indent == 1)
00065 xmlIndentTreeOutput = 1;
00066 else
00067 xmlIndentTreeOutput = 0;
00068
00069 INFO(i18n("Parsing document"));
00070
00071 xmlDocPtr doc = xmlParseFile( pat.latin1() );
00072 xsltTransformContextPtr ctxt;
00073
00074 ctxt = xsltNewTransformContext(style_sheet, doc);
00075 if (ctxt == NULL)
00076 return parsed;
00077
00078 INFO(i18n("Applying stylesheet"));
00079 QValueVector<const char *> p = params;
00080 p.append( NULL );
00081 xmlDocPtr res = xsltApplyStylesheet(style_sheet, doc, const_cast<const char **>(&p[0]));
00082 xmlFreeDoc(doc);
00083 if (res != NULL) {
00084 xmlOutputBufferPtr outp = xmlOutputBufferCreateIO(writeToQString, (xmlOutputCloseCallback)closeQString, &parsed, 0);
00085 outp->written = 0;
00086 INFO(i18n("Writing document"));
00087 xsltSaveResultTo ( outp, res, style_sheet );
00088 xmlOutputBufferFlush(outp);
00089 xmlFreeDoc(res);
00090 }
00091 xsltFreeStylesheet(style_sheet);
00092
00093 if (parsed.isEmpty())
00094 parsed = " ";
00095 return parsed;
00096 }
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133 QString splitOut(const QString &parsed, int index)
00134 {
00135 int start_index = index + 1;
00136 while (parsed.at(start_index - 1) != '>') start_index++;
00137
00138 int inside = 0;
00139
00140 QString filedata;
00141
00142 while (true) {
00143 int endindex = parsed.find("</FILENAME>", index);
00144 int startindex = parsed.find("<FILENAME ", index) + 1;
00145
00146
00147
00148 if (startindex > 0) {
00149 if (startindex < endindex) {
00150
00151 index = startindex + 8;
00152 inside++;
00153 } else {
00154 index = endindex + 8;
00155 inside--;
00156 }
00157 } else {
00158 inside--;
00159 index = endindex + 1;
00160 }
00161
00162 if (inside == 0) {
00163 filedata = parsed.mid(start_index, endindex - start_index);
00164 break;
00165 }
00166
00167 }
00168
00169 index = filedata.find("<FILENAME ");
00170
00171 if (index > 0) {
00172 int endindex = filedata.findRev("</FILENAME>");
00173 while (filedata.at(endindex) != '>') endindex++;
00174 endindex++;
00175 filedata = filedata.left(index) + filedata.mid(endindex);
00176 }
00177
00178
00179 return filedata;
00180 }
00181
00182 void fillInstance(KInstance &ins, const QString &srcdir) {
00183 QString catalogs;
00184
00185 if ( srcdir.isEmpty() ) {
00186 catalogs += ins.dirs()->findResource("data", "ksgmltools2/customization/catalog");
00187 catalogs += ':';
00188 catalogs += ins.dirs()->findResource("data", "ksgmltools2/docbook/xml-dtd-4.2/docbook.cat");
00189 ins.dirs()->addResourceType("dtd", KStandardDirs::kde_default("data") + "ksgmltools2");
00190 } else {
00191 catalogs += srcdir +"/customization/catalog:" + srcdir + "/docbook/xml-dtd-4.2/docbook.cat";
00192 ins.dirs()->addResourceDir("dtd", srcdir);
00193 }
00194
00195 xmlLoadCatalogs(catalogs.latin1());
00196 }
00197
00198 extern "C" void *init_kbzip2filter();
00199
00200 static QIODevice *getBZip2device(const QString &fileName )
00201 {
00202 QFile * f = new QFile( fileName );
00203 KLibFactory * factory = static_cast<KLibFactory*>(init_kbzip2filter());
00204 KFilterBase * base = static_cast<KFilterBase*>( factory->create(0, "bzip2" ) );
00205
00206 if ( base )
00207 {
00208 base->setDevice(f, true);
00209 return new KFilterDev(base, true);
00210 }
00211 return 0;
00212 }
00213
00214 bool saveToCache( const QString &contents, const QString &filename )
00215 {
00216 QIODevice *fd = ::getBZip2device(filename);
00217 if ( !fd )
00218 return false;
00219
00220 if (!fd->open(IO_WriteOnly))
00221 {
00222 delete fd;
00223 return false;
00224 }
00225
00226 fd->writeBlock( contents.utf8() );
00227 fd->close();
00228 delete fd;
00229 return true;
00230 }
00231
00232 static bool readCache( const QString &filename,
00233 const QString &cache, QString &output)
00234 {
00235 kdDebug( 7119 ) << "verifyCache " << filename << " " << cache << endl;
00236 if ( !compareTimeStamps( filename, cache ) )
00237 return false;
00238 if ( !compareTimeStamps( locate( "dtd", "customization/kde-chunk.xsl"), cache ) )
00239 return false;
00240
00241 kdDebug( 7119 ) << "create filter" << endl;
00242 QIODevice *fd = ::getBZip2device(cache);
00243 if ( !fd )
00244 return false;
00245
00246 if (!fd->open(IO_ReadOnly))
00247 {
00248 delete fd;
00249 QFile::remove(cache);
00250 return false;
00251 }
00252
00253 kdDebug( 7119 ) << "reading" << endl;
00254
00255 char buffer[32000];
00256 int n;
00257 QCString text;
00258
00259 while ( ( n = fd->readBlock(buffer, 31900) ) > 0)
00260 {
00261 buffer[n] = 0;
00262 text += buffer;
00263 }
00264 kdDebug( 7119 ) << "read " << text.length() << endl;
00265 fd->close();
00266
00267 output = QString::fromUtf8( text );
00268 delete fd;
00269
00270 if (n == -1)
00271 return false;
00272
00273 kdDebug( 7119 ) << "finished " << endl;
00274
00275 return true;
00276 }
00277
00278 QString lookForCache( const QString &filename )
00279 {
00280 kdDebug() << "lookForCache " << filename << endl;
00281 assert( filename.endsWith( ".docbook" ) || filename.endsWith( ".xml" ) );
00282 assert( filename.at( 0 ) == '/' );
00283
00284 QString cache;
00285
00286 if (filename.endsWith( ".xml" ))
00287 cache = filename.left( filename.length() - strlen ("xml") );
00288 else
00289 cache = filename.left( filename.length() - strlen ("docbook") );
00290
00291 QString output;
00292 if ( readCache( filename, cache + "cache.bz2", output) )
00293 return output;
00294 if ( readCache( filename,
00295 locateLocal( "cache",
00296 "kio_help" + cache +
00297 "cache.bz2" ), output ) )
00298 return output;
00299
00300 return QString::null;
00301 }
00302
00303 bool compareTimeStamps( const QString &older, const QString &newer )
00304 {
00305 QFileInfo _older( older );
00306 QFileInfo _newer( newer );
00307 assert( _older.exists() );
00308 if ( !_newer.exists() )
00309 return false;
00310 return ( _newer.lastModified() > _older.lastModified() );
00311 }
00312
00313 QCString fromUnicode( const QString &data )
00314 {
00315 QTextCodec *locale = QTextCodec::codecForLocale();
00316 QCString result;
00317 char buffer[30000];
00318 uint buffer_len = 0;
00319 uint len = 0;
00320 uint offset = 0;
00321 const int part_len = 5000;
00322
00323 QString part;
00324
00325 while ( offset < data.length() )
00326 {
00327 part = data.mid( offset, part_len );
00328 QCString test = locale->fromUnicode( part );
00329 if ( locale->toUnicode( test ) == part ) {
00330 result += test;
00331 offset += part_len;
00332 continue;
00333 }
00334 len = part.length();
00335 buffer_len = 0;
00336 for ( uint i = 0; i < len; i++ ) {
00337 QCString test = locale->fromUnicode( part.mid( i, 1 ) );
00338 if ( locale->toUnicode( test ) == part.mid( i, 1 ) ) {
00339 if (buffer_len + test.length() + 1 > sizeof(buffer))
00340 break;
00341 strcpy( buffer + buffer_len, test.data() );
00342 buffer_len += test.length();
00343 } else {
00344 QString res;
00345 res.sprintf( "&#%d;", part.at( i ).unicode() );
00346 test = locale->fromUnicode( res );
00347 if (buffer_len + test.length() + 1 > sizeof(buffer))
00348 break;
00349 strcpy( buffer + buffer_len, test.data() );
00350 buffer_len += test.length();
00351 }
00352 }
00353 result += QCString( buffer, buffer_len + 1);
00354 offset += part_len;
00355 }
00356 return result;
00357 }
00358
00359 void replaceCharsetHeader( QString &output )
00360 {
00361 QString name = QTextCodec::codecForLocale()->mimeName();
00362 output.replace( QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ),
00363 QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\">" ).arg( name ) );
00364 }