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 "kio_help.h"
00017 #include <klocale.h>
00018 #include <assert.h>
00019 #include <kfilterbase.h>
00020 #include <kfilterdev.h>
00021 #include <QtCore/QTextCodec>
00022 #include <stdlib.h>
00023 #include <config.h>
00024 #include <stdarg.h>
00025 #include <klibloader.h>
00026 #include <kcharsets.h>
00027 #include <kurl.h>
00028
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 QVector<const char *> ¶ms )
00052 {
00053 QString parsed;
00054
00055 INFO(i18n("Parsing stylesheet"));
00056
00057 xsltStylesheetPtr style_sheet =
00058 xsltParseStylesheetFile((const xmlChar *)tss.toLatin1().data());
00059
00060 if ( !style_sheet ) {
00061 return parsed;
00062 }
00063 if (style_sheet->indent == 1)
00064 xmlIndentTreeOutput = 1;
00065 else
00066 xmlIndentTreeOutput = 0;
00067
00068 INFO(i18n("Parsing document"));
00069
00070 xmlDocPtr doc = xmlParseFile( pat.toLatin1() );
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 QTextCodec *locale = QTextCodec::codecForLocale();
00310 QByteArray result;
00311 char buffer[30000];
00312 uint buffer_len = 0;
00313 uint len = 0;
00314 int 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 QByteArray 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 QByteArray 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 += QByteArray( 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 }