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" ) );
00282 assert( filename.at( 0 ) == '/' );
00283
00284 QString cache = filename.left( filename.length() - 7 );
00285 QString output;
00286 if ( readCache( filename, cache + "cache.bz2", output) )
00287 return output;
00288 if ( readCache( filename,
00289 locateLocal( "cache",
00290 "kio_help" + cache +
00291 "cache.bz2" ), output ) )
00292 return output;
00293
00294 return QString::null;
00295 }
00296
00297 bool compareTimeStamps( const QString &older, const QString &newer )
00298 {
00299 QFileInfo _older( older );
00300 QFileInfo _newer( newer );
00301 assert( _older.exists() );
00302 if ( !_newer.exists() )
00303 return false;
00304 return ( _newer.lastModified() > _older.lastModified() );
00305 }
00306
00307 QCString fromUnicode( const QString &data )
00308 {
00309 QTextCodec *locale = QTextCodec::codecForLocale();
00310 QCString result;
00311 char buffer[30000];
00312 uint buffer_len = 0;
00313 uint len = 0;
00314 uint offset = 0;
00315 const int part_len = 5000;
00316
00317 QString part;
00318
00319 while ( offset < data.length() )
00320 {
00321 part = data.mid( offset, part_len );
00322 QCString test = locale->fromUnicode( part );
00323 if ( locale->toUnicode( test ) == part ) {
00324 result += test;
00325 offset += part_len;
00326 continue;
00327 }
00328 len = part.length();
00329 buffer_len = 0;
00330 for ( uint i = 0; i < len; i++ ) {
00331 QCString test = locale->fromUnicode( part.mid( i, 1 ) );
00332 if ( locale->toUnicode( test ) == part.mid( i, 1 ) ) {
00333 if (buffer_len + test.length() + 1 > sizeof(buffer))
00334 break;
00335 strcpy( buffer + buffer_len, test.data() );
00336 buffer_len += test.length();
00337 } else {
00338 QString res;
00339 res.sprintf( "&#%d;", part.at( i ).unicode() );
00340 test = locale->fromUnicode( res );
00341 if (buffer_len + test.length() + 1 > sizeof(buffer))
00342 break;
00343 strcpy( buffer + buffer_len, test.data() );
00344 buffer_len += test.length();
00345 }
00346 }
00347 result += QCString( buffer, buffer_len + 1);
00348 offset += part_len;
00349 }
00350 return result;
00351 }
00352
00353 void replaceCharsetHeader( QString &output )
00354 {
00355 QString name = QTextCodec::codecForLocale()->name();
00356 name.replace( QString( "ISO " ), "iso-" );
00357 output.replace( QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ),
00358 QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\">" ).arg( name ) );
00359 }