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