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 = xmlParseFile( pat.toLatin1() );
00070 xsltTransformContextPtr ctxt;
00071
00072 ctxt = xsltNewTransformContext(style_sheet, doc);
00073 if (ctxt == NULL)
00074 return parsed;
00075
00076 INFO(i18n("Applying stylesheet"));
00077 QVector<const char *> p = params;
00078 p.append( NULL );
00079 xmlDocPtr res = xsltApplyStylesheet(style_sheet, doc, const_cast<const char **>(&p[0]));
00080 xmlFreeDoc(doc);
00081 if (res != NULL) {
00082 xmlOutputBufferPtr outp = xmlOutputBufferCreateIO(writeToQString, (xmlOutputCloseCallback)closeQString, &parsed, 0);
00083 outp->written = 0;
00084 INFO(i18n("Writing document"));
00085 xsltSaveResultTo ( outp, res, style_sheet );
00086 xmlOutputBufferFlush(outp);
00087 xmlFreeDoc(res);
00088 }
00089 xsltFreeStylesheet(style_sheet);
00090
00091 if (parsed.isEmpty())
00092 parsed = " ";
00093 return parsed;
00094 }
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 QString splitOut(const QString &parsed, int index)
00132 {
00133 int start_index = index + 1;
00134 while (parsed.at(start_index - 1) != '>') start_index++;
00135
00136 int inside = 0;
00137
00138 QString filedata;
00139
00140 while (true) {
00141 int endindex = parsed.indexOf("</FILENAME>", index);
00142 int startindex = parsed.indexOf("<FILENAME ", index) + 1;
00143
00144
00145
00146 if (startindex > 0) {
00147 if (startindex < endindex) {
00148
00149 index = startindex + 8;
00150 inside++;
00151 } else {
00152 index = endindex + 8;
00153 inside--;
00154 }
00155 } else {
00156 inside--;
00157 index = endindex + 1;
00158 }
00159
00160 if (inside == 0) {
00161 filedata = parsed.mid(start_index, endindex - start_index);
00162 break;
00163 }
00164
00165 }
00166
00167 index = filedata.indexOf("<FILENAME ");
00168
00169 if (index > 0) {
00170 int endindex = filedata.lastIndexOf("</FILENAME>");
00171 while (filedata.at(endindex) != '>') endindex++;
00172 endindex++;
00173 filedata = filedata.left(index) + filedata.mid(endindex);
00174 }
00175
00176
00177 return filedata;
00178 }
00179
00180 void fillInstance(KComponentData &ins, const QString &srcdir)
00181 {
00182 QByteArray catalogs;
00183
00184 if ( srcdir.isEmpty() ) {
00185 catalogs += KUrl::fromLocalFile( ins.dirs()->findResource("data", "ksgmltools2/customization/catalog.xml") ).toEncoded();
00186 catalogs += ' ';
00187 catalogs += KUrl::fromLocalFile( ins.dirs()->findResource("data", "ksgmltools2/docbook/xml-dtd-4.2/catalog.xml") ).toEncoded();
00188 ins.dirs()->addResourceType("dtd", "data", "ksgmltools2/");
00189 } else {
00190 catalogs += KUrl::fromLocalFile( srcdir +"/customization/catalog.xml" ).toEncoded();
00191 catalogs += ' ';
00192 catalogs += KUrl::fromLocalFile( srcdir + "/docbook/xml-dtd-4.2/catalog.xml" ).toEncoded();
00193 ins.dirs()->addResourceDir("dtd", srcdir);
00194 }
00195
00196 setenv( "XML_CATALOG_FILES", catalogs.constData(), 1 );
00197 xmlInitializeCatalog();
00198 }
00199
00200 static QIODevice *getBZip2device(const QString &fileName )
00201 {
00202 return KFilterDev::deviceForFile(fileName);
00203 }
00204
00205 bool saveToCache( const QString &contents, const QString &filename )
00206 {
00207 QIODevice *fd = ::getBZip2device(filename);
00208 if ( !fd )
00209 return false;
00210
00211 if (!fd->open(QIODevice::WriteOnly))
00212 {
00213 delete fd;
00214 return false;
00215 }
00216
00217 fd->write( contents.toUtf8() );
00218 fd->close();
00219 delete fd;
00220 return true;
00221 }
00222
00223 static bool readCache( const QString &filename,
00224 const QString &cache, QString &output)
00225 {
00226 kDebug( 7119 ) << filename << " " << cache;
00227 KGlobal::dirs()->addResourceType("dtd", "data", "ksgmltools2/");
00228 if ( !compareTimeStamps( filename, cache ) )
00229 return false;
00230 if ( !compareTimeStamps( KStandardDirs::locate( "dtd", "customization/kde-chunk.xsl"), cache ) )
00231 return false;
00232
00233 kDebug( 7119 ) << "create filter";
00234 QIODevice *fd = ::getBZip2device(cache);
00235 if ( !fd )
00236 return false;
00237
00238 if (!fd->open(QIODevice::ReadOnly))
00239 {
00240 delete fd;
00241 QFile::remove(cache);
00242 return false;
00243 }
00244
00245 kDebug( 7119 ) << "reading";
00246
00247 char buffer[32000];
00248 int n;
00249 QByteArray text;
00250
00251 while ( ( n = fd->read(buffer, 31900) ) > 0)
00252 {
00253 buffer[n] = 0;
00254 text += buffer;
00255 }
00256 kDebug( 7119 ) << "read " << text.length();
00257 fd->close();
00258
00259 output = QString::fromUtf8( text );
00260 delete fd;
00261
00262 if (n == -1)
00263 return false;
00264
00265 kDebug( 7119 ) << "finished ";
00266
00267 return true;
00268 }
00269
00270 QString lookForCache( const QString &filename )
00271 {
00272 kDebug() << "lookForCache " << filename;
00273 assert( filename.endsWith( ".docbook" ) );
00274 assert( QDir::isAbsolutePath(filename));
00275 QString cache = filename.left( filename.length() - 7 );
00276 QString output;
00277 if ( readCache( filename, cache + "cache.bz2", output) )
00278 return output;
00279 #ifdef Q_WS_WIN
00280 QFileInfo fi(filename);
00281
00282
00283
00284
00285 cache = '/' + fi.absolutePath().remove(KStandardDirs::installPath("html"),Qt::CaseInsensitive).replace('/','_') + '_' + fi.baseName() + '.';
00286 #endif
00287 if ( readCache( filename,
00288 KStandardDirs::locateLocal( "cache",
00289 "kio_help" + cache +
00290 "cache.bz2" ), output ) )
00291 return output;
00292
00293 return QString();
00294 }
00295
00296 bool compareTimeStamps( const QString &older, const QString &newer )
00297 {
00298 QFileInfo _older( older );
00299 QFileInfo _newer( newer );
00300 assert( _older.exists() );
00301 if ( !_newer.exists() )
00302 return false;
00303 return ( _newer.lastModified() > _older.lastModified() );
00304 }
00305
00306 QByteArray fromUnicode( const QString &data )
00307 {
00308 #ifdef Q_WS_WIN
00309 return data.toUtf8();
00310 #else
00311 QTextCodec *locale = QTextCodec::codecForLocale();
00312 QByteArray result;
00313 char buffer[30000];
00314 uint buffer_len = 0;
00315 uint len = 0;
00316 int offset = 0;
00317 const int part_len = 5000;
00318
00319 QString part;
00320
00321 while ( offset < data.length() )
00322 {
00323 part = data.mid( offset, part_len );
00324 QByteArray test = locale->fromUnicode( part );
00325 if ( locale->toUnicode( test ) == part ) {
00326 result += test;
00327 offset += part_len;
00328 continue;
00329 }
00330 len = part.length();
00331 buffer_len = 0;
00332 for ( uint i = 0; i < len; i++ ) {
00333 QByteArray test = locale->fromUnicode( part.mid( i, 1 ) );
00334 if ( locale->toUnicode( test ) == part.mid( i, 1 ) ) {
00335 if (buffer_len + test.length() + 1 > sizeof(buffer))
00336 break;
00337 strcpy( buffer + buffer_len, test.data() );
00338 buffer_len += test.length();
00339 } else {
00340 QString res;
00341 res.sprintf( "&#%d;", part.at( i ).unicode() );
00342 test = locale->fromUnicode( res );
00343 if (buffer_len + test.length() + 1 > sizeof(buffer))
00344 break;
00345 strcpy( buffer + buffer_len, test.data() );
00346 buffer_len += test.length();
00347 }
00348 }
00349 result += QByteArray( buffer, buffer_len + 1);
00350 offset += part_len;
00351 }
00352 return result;
00353 #endif
00354 }
00355
00356 void replaceCharsetHeader( QString &output )
00357 {
00358 QString name;
00359 #ifdef Q_WS_WIN
00360 name = "utf-8";
00361
00362 if (output.contains("<table-of-contents>"))
00363 output.replace( QString( "<?xml version=\"1.0\"?>" ),
00364 QString( "<?xml version=\"1.0\" encoding=\"%1\"?>").arg( name ) );
00365 #else
00366 name = QTextCodec::codecForLocale()->name();
00367 name.replace( QString( "ISO " ), "iso-" );
00368 output.replace( QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ),
00369 QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\">" ).arg( name ) );
00370 #endif
00371 }