xslt.cpp

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 *> &params )
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 = " "; // avoid error message
00095     return parsed;
00096 }
00097 
00098 /*
00099 xmlParserInputPtr meinExternalEntityLoader(const char *URL, const char *ID,
00100                        xmlParserCtxtPtr ctxt) {
00101     xmlParserInputPtr ret = NULL;
00102 
00103     // fprintf(stderr, "loading %s %s %s\n", URL, ID, ctxt->directory);
00104 
00105     if (URL == NULL) {
00106         if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
00107             ctxt->sax->warning(ctxt,
00108                     "failed to load external entity \"%s\"\n", ID);
00109         return(NULL);
00110     }
00111     if (!qstrcmp(ID, "-//OASIS//DTD DocBook XML V4.1.2//EN"))
00112         URL = "docbook/xml-dtd-4.1.2/docbookx.dtd";
00113     if (!qstrcmp(ID, "-//OASIS//DTD XML DocBook V4.1.2//EN"))
00114     URL = "docbook/xml-dtd-4.1.2/docbookx.dtd";
00115 
00116     QString file;
00117     if (KStandardDirs::exists( QDir::currentDirPath() + "/" + URL ) )
00118         file = QDir::currentDirPath() + "/" + URL;
00119     else
00120         file = locate("dtd", URL);
00121 
00122     ret = xmlNewInputFromFile(ctxt, file.latin1());
00123     if (ret == NULL) {
00124         if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
00125             ctxt->sax->warning(ctxt,
00126 
00127                 "failed to load external entity \"%s\"\n", URL);
00128     }
00129     return(ret);
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 //        kdDebug() << "FILENAME " << startindex << " " << endindex << " " << inside << " " << parsed.mid(startindex + 18, 15)<< " " << parsed.length() << endl;
00147 
00148         if (startindex > 0) {
00149             if (startindex < endindex) {
00150                 //              kdDebug() << "finding another" << endl;
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     // filedata.replace(QRegExp(">"), "\n>");
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     // Also end loop in case of error, when -1 is returned
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 }
KDE Home | KDE Accessibility Home | Description of Access Keys