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
00218 if (!fd->open(IO_WriteOnly))
00219 {
00220 delete fd;
00221 return false;
00222 }
00223
00224 fd->writeBlock( contents.utf8() );
00225 fd->close();
00226 delete fd;
00227 return true;
00228 }
00229
00230 static bool readCache( const QString &filename,
00231 const QString &cache, QString &output)
00232 {
00233 kdDebug( 7119 ) << "verifyCache " << filename << " " << cache << endl;
00234 if ( !compareTimeStamps( filename, cache ) )
00235 return false;
00236 if ( !compareTimeStamps( locate( "dtd", "customization/kde-chunk.xsl"), cache ) )
00237 return false;
00238
00239 kdDebug( 7119 ) << "create filter" << endl;
00240 QIODevice *fd = ::getBZip2device(cache);
00241
00242 if (!fd->open(IO_ReadOnly))
00243 {
00244 delete fd;
00245 QFile::remove(cache);
00246 return false;
00247 }
00248
00249 kdDebug( 7119 ) << "reading" << endl;
00250
00251 char buffer[32000];
00252 int n;
00253 QCString text;
00254
00255 while ( ( n = fd->readBlock(buffer, 31900) ) > 0)
00256 {
00257 buffer[n] = 0;
00258 text += buffer;
00259 }
00260 kdDebug( 7119 ) << "read " << text.length() << endl;
00261 fd->close();
00262
00263 output = QString::fromUtf8( text );
00264 delete fd;
00265
00266 if (n == -1)
00267 return false;
00268
00269 kdDebug( 7119 ) << "finished " << endl;
00270
00271 return true;
00272 }
00273
00274 QString lookForCache( const QString &filename )
00275 {
00276 kdDebug() << "lookForCache " << filename << endl;
00277 assert( filename.endsWith( ".docbook" ) );
00278 assert( filename.at( 0 ) == '/' );
00279
00280 QString cache = filename.left( filename.length() - 7 );
00281 QString output;
00282 if ( readCache( filename, cache + "cache.bz2", output) )
00283 return output;
00284 if ( readCache( filename,
00285 locateLocal( "cache",
00286 "kio_help" + cache +
00287 "cache.bz2" ), output ) )
00288 return output;
00289
00290 return QString::null;
00291 }
00292
00293 bool compareTimeStamps( const QString &older, const QString &newer )
00294 {
00295 QFileInfo _older( older );
00296 QFileInfo _newer( newer );
00297 assert( _older.exists() );
00298 if ( !_newer.exists() )
00299 return false;
00300 return ( _newer.lastModified() > _older.lastModified() );
00301 }
00302
00303 QCString fromUnicode( const QString &data )
00304 {
00305 QTextCodec *locale = QTextCodec::codecForLocale();
00306 QCString result;
00307 char buffer[30000];
00308 uint buffer_len = 0;
00309 uint len = 0;
00310 uint offset = 0;
00311 const int part_len = 5000;
00312
00313 QString part;
00314
00315 while ( offset < data.length() )
00316 {
00317 part = data.mid( offset, part_len );
00318 QCString test = locale->fromUnicode( part );
00319 if ( locale->toUnicode( test ) == part ) {
00320 result += test;
00321 offset += part_len;
00322 continue;
00323 }
00324 len = part.length();
00325 buffer_len = 0;
00326 for ( uint i = 0; i < len; i++ ) {
00327 QCString test = locale->fromUnicode( part.mid( i, 1 ) );
00328 if ( locale->toUnicode( test ) == part.mid( i, 1 ) ) {
00329 if (buffer_len + test.length() + 1 > sizeof(buffer))
00330 break;
00331 strcpy( buffer + buffer_len, test.data() );
00332 buffer_len += test.length();
00333 } else {
00334 QString res;
00335 res.sprintf( "&#%d;", part.at( i ).unicode() );
00336 test = locale->fromUnicode( res );
00337 if (buffer_len + test.length() + 1 > sizeof(buffer))
00338 break;
00339 strcpy( buffer + buffer_len, test.data() );
00340 buffer_len += test.length();
00341 }
00342 }
00343 result += QCString( buffer, buffer_len + 1);
00344 offset += part_len;
00345 }
00346 return result;
00347 }
00348
00349 void replaceCharsetHeader( QString &output )
00350 {
00351 QString name = QTextCodec::codecForLocale()->name();
00352 name.replace( QString( "ISO " ), "iso-" );
00353 output.replace( QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ),
00354 QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\">" ).arg( name ) );
00355 }