00001 #include "xslt.h"
00002
00003 #include <libxslt/xsltconfig.h>
00004 #include <libxslt/xsltInternals.h>
00005 #include <libxslt/transform.h>
00006 #include <libxslt/xsltutils.h>
00007 #include <libxml/xmlIO.h>
00008 #include <libxml/parserInternals.h>
00009 #include <libxml/catalog.h>
00010 #include <kdebug.h>
00011 #include <kstandarddirs.h>
00012 #include <QtCore/QDate>
00013 #include <QtCore/QDir>
00014 #include <QtCore/QRegExp>
00015 #include <kcomponentdata.h>
00016 #include <klocale.h>
00017 #include <assert.h>
00018 #include <kfilterbase.h>
00019 #include <kfilterdev.h>
00020 #include <QtCore/QTextCodec>
00021 #include <stdlib.h>
00022 #include <config.h>
00023 #include <stdarg.h>
00024 #include <klibloader.h>
00025 #include <kcharsets.h>
00026 #include <kurl.h>
00027
00028
00029 #if !defined( SIMPLE_XSLT )
00030 extern HelpProtocol *slave;
00031 #define INFO( x ) if (slave) slave->infoMessage(x);
00032 #else
00033 #define INFO( x )
00034 #endif
00035
00036 int writeToQString(void * context, const char * buffer, int len)
00037 {
00038 QString *t = (QString*)context;
00039 *t += QString::fromUtf8(buffer, len);
00040 return len;
00041 }
00042
00043 int closeQString(void * context) {
00044 QString *t = (QString*)context;
00045 *t += '\n';
00046 return 0;
00047 }
00048
00049 QString transform( const QString &pat, const QString& tss,
00050 const QVector<const char *> ¶ms )
00051 {
00052 QString parsed;
00053
00054 INFO(i18n("Parsing stylesheet"));
00055
00056 xsltStylesheetPtr style_sheet =
00057 xsltParseStylesheetFile((const xmlChar *)tss.toLatin1().data());
00058
00059 if ( !style_sheet ) {
00060 return parsed;
00061 }
00062 if (style_sheet->indent == 1)
00063 xmlIndentTreeOutput = 1;
00064 else
00065 xmlIndentTreeOutput = 0;
00066
00067 INFO(i18n("Parsing document"));
00068
00069 xmlDocPtr doc = xmlReadFile( pat.toLatin1(), NULL,
00070 XML_PARSE_NOENT|XML_PARSE_DTDLOAD|XML_PARSE_NONET);
00071 xsltTransformContextPtr ctxt;
00072
00073 ctxt = xsltNewTransformContext(style_sheet, doc);
00074 if (ctxt == NULL)
00075 return parsed;
00076
00077 INFO(i18n("Applying stylesheet"));
00078 QVector<const char *> p = params;
00079 p.append( NULL );
00080 xmlDocPtr res = xsltApplyStylesheet(style_sheet, doc, const_cast<const char **>(&p[0]));
00081 xmlFreeDoc(doc);
00082 if (res != NULL) {
00083 xmlOutputBufferPtr outp = xmlOutputBufferCreateIO(writeToQString, (xmlOutputCloseCallback)closeQString, &parsed, 0);
00084 outp->written = 0;
00085 INFO(i18n("Writing document"));
00086 xsltSaveResultTo ( outp, res, style_sheet );
00087 xmlOutputBufferFlush(outp);
00088 xmlFreeDoc(res);
00089 }
00090 xsltFreeStylesheet(style_sheet);
00091
00092 if (parsed.isEmpty())
00093 parsed = " ";
00094 return parsed;
00095 }
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 QString splitOut(const QString &parsed, int index)
00133 {
00134 int start_index = index + 1;
00135 while (parsed.at(start_index - 1) != '>') start_index++;
00136
00137 int inside = 0;
00138
00139 QString filedata;
00140
00141 while (true) {
00142 int endindex = parsed.indexOf("</FILENAME>", index);
00143 int startindex = parsed.indexOf("<FILENAME ", index) + 1;
00144
00145
00146
00147 if (startindex > 0) {
00148 if (startindex < endindex) {
00149
00150 index = startindex + 8;
00151 inside++;
00152 } else {
00153 index = endindex + 8;
00154 inside--;
00155 }
00156 } else {
00157 inside--;
00158 index = endindex + 1;
00159 }
00160
00161 if (inside == 0) {
00162 filedata = parsed.mid(start_index, endindex - start_index);
00163 break;
00164 }
00165
00166 }
00167
00168 index = filedata.indexOf("<FILENAME ");
00169
00170 if (index > 0) {
00171 int endindex = filedata.lastIndexOf("</FILENAME>");
00172 while (filedata.at(endindex) != '>') endindex++;
00173 endindex++;
00174 filedata = filedata.left(index) + filedata.mid(endindex);
00175 }
00176
00177
00178 return filedata;
00179 }
00180
00181 void fillInstance(KComponentData &ins, const QString &srcdir)
00182 {
00183 QByteArray catalogs;
00184
00185 if ( srcdir.isEmpty() ) {
00186 catalogs += KUrl::fromLocalFile( ins.dirs()->findResource("data", "ksgmltools2/customization/catalog.xml") ).toEncoded();
00187 catalogs += ' ';
00188 catalogs += KUrl::fromLocalFile( ins.dirs()->findResource("data", "ksgmltools2/docbook/xml-dtd-4.2/catalog.xml") ).toEncoded();
00189 ins.dirs()->addResourceType("dtd", "data", "ksgmltools2/");
00190 } else {
00191 catalogs += KUrl::fromLocalFile( srcdir +"/customization/catalog.xml" ).toEncoded();
00192 catalogs += ' ';
00193 catalogs += KUrl::fromLocalFile( srcdir + "/docbook/xml-dtd-4.2/catalog.xml" ).toEncoded();
00194 ins.dirs()->addResourceDir("dtd", srcdir);
00195 }
00196
00197 setenv( "XML_CATALOG_FILES", catalogs.constData(), 1 );
00198 xmlInitializeCatalog();
00199 }
00200
00201 static QIODevice *getBZip2device(const QString &fileName )
00202 {
00203 return KFilterDev::deviceForFile(fileName);
00204 }
00205
00206 bool saveToCache( const QString &contents, const QString &filename )
00207 {
00208 QIODevice *fd = ::getBZip2device(filename);
00209 if ( !fd )
00210 return false;
00211
00212 if (!fd->open(QIODevice::WriteOnly))
00213 {
00214 delete fd;
00215 return false;
00216 }
00217
00218 fd->write( contents.toUtf8() );
00219 fd->close();
00220 delete fd;
00221 return true;
00222 }
00223
00224 static bool readCache( const QString &filename,
00225 const QString &cache, QString &output)
00226 {
00227 kDebug( 7119 ) << filename << " " << cache;
00228 KGlobal::dirs()->addResourceType("dtd", "data", "ksgmltools2/");
00229 if ( !compareTimeStamps( filename, cache ) )
00230 return false;
00231 if ( !compareTimeStamps( KStandardDirs::locate( "dtd", "customization/kde-chunk.xsl"), cache ) )
00232 return false;
00233
00234 kDebug( 7119 ) << "create filter";
00235 QIODevice *fd = ::getBZip2device(cache);
00236 if ( !fd )
00237 return false;
00238
00239 if (!fd->open(QIODevice::ReadOnly))
00240 {
00241 delete fd;
00242 QFile::remove(cache);
00243 return false;
00244 }
00245
00246 kDebug( 7119 ) << "reading";
00247
00248 char buffer[32000];
00249 int n;
00250 QByteArray text;
00251
00252 while ( ( n = fd->read(buffer, 31900) ) > 0)
00253 {
00254 buffer[n] = 0;
00255 text += buffer;
00256 }
00257 kDebug( 7119 ) << "read " << text.length();
00258 fd->close();
00259
00260 output = QString::fromUtf8( text );
00261 delete fd;
00262
00263 if (n == -1)
00264 return false;
00265
00266 kDebug( 7119 ) << "finished ";
00267
00268 return true;
00269 }
00270
00271 QString lookForCache( const QString &filename )
00272 {
00273 kDebug() << "lookForCache " << filename;
00274 assert( filename.endsWith( ".docbook" ) );
00275 assert( QDir::isAbsolutePath(filename));
00276 QString cache = filename.left( filename.length() - 7 );
00277 QString output;
00278 if ( readCache( filename, cache + "cache.bz2", output) )
00279 return output;
00280 #ifdef Q_WS_WIN
00281 QFileInfo fi(filename);
00282
00283
00284
00285
00286 cache = '/' + fi.absolutePath().remove(KStandardDirs::installPath("html"),Qt::CaseInsensitive).replace('/','_') + '_' + fi.baseName() + '.';
00287 #endif
00288 if ( readCache( filename,
00289 KStandardDirs::locateLocal( "cache",
00290 "kio_help" + cache +
00291 "cache.bz2" ), output ) )
00292 return output;
00293
00294 return QString();
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 QByteArray fromUnicode( const QString &data )
00308 {
00309 #ifdef Q_WS_WIN
00310 return data.toUtf8();
00311 #else
00312 QTextCodec *locale = QTextCodec::codecForLocale();
00313 QByteArray result;
00314 char buffer[30000];
00315 uint buffer_len = 0;
00316 uint len = 0;
00317 int offset = 0;
00318 const int part_len = 5000;
00319
00320 QString part;
00321
00322 while ( offset < data.length() )
00323 {
00324 part = data.mid( offset, part_len );
00325 QByteArray test = locale->fromUnicode( part );
00326 if ( locale->toUnicode( test ) == part ) {
00327 result += test;
00328 offset += part_len;
00329 continue;
00330 }
00331 len = part.length();
00332 buffer_len = 0;
00333 for ( uint i = 0; i < len; i++ ) {
00334 QByteArray test = locale->fromUnicode( part.mid( i, 1 ) );
00335 if ( locale->toUnicode( test ) == part.mid( i, 1 ) ) {
00336 if (buffer_len + test.length() + 1 > sizeof(buffer))
00337 break;
00338 strcpy( buffer + buffer_len, test.data() );
00339 buffer_len += test.length();
00340 } else {
00341 QString res;
00342 res.sprintf( "&#%d;", part.at( i ).unicode() );
00343 test = locale->fromUnicode( res );
00344 if (buffer_len + test.length() + 1 > sizeof(buffer))
00345 break;
00346 strcpy( buffer + buffer_len, test.data() );
00347 buffer_len += test.length();
00348 }
00349 }
00350 result += QByteArray( buffer, buffer_len + 1);
00351 offset += part_len;
00352 }
00353 return result;
00354 #endif
00355 }
00356
00357 void replaceCharsetHeader( QString &output )
00358 {
00359 QString name;
00360 #ifdef Q_WS_WIN
00361 name = "utf-8";
00362
00363 if (output.contains("<table-of-contents>"))
00364 output.replace( QString( "<?xml version=\"1.0\"?>" ),
00365 QString( "<?xml version=\"1.0\" encoding=\"%1\"?>").arg( name ) );
00366 #else
00367 name = QTextCodec::codecForLocale()->name();
00368 name.replace( QString( "ISO " ), "iso-" );
00369 output.replace( QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ),
00370 QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\">" ).arg( name ) );
00371 #endif
00372 }