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 <tqdir.h> 00011 #include <tqregexp.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 <tqtextcodec.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 <tqvaluevector.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 TQString *t = (TQString*)context; 00040 *t += TQString::fromUtf8(buffer, len); 00041 return len; 00042 } 00043 00044 int closeQString(void * context) { 00045 TQString *t = (TQString*)context; 00046 *t += '\n'; 00047 return 0; 00048 } 00049 00050 TQString transform( const TQString &pat, const TQString& tss, 00051 const TQValueVector<const char *> ¶ms ) 00052 { 00053 TQString 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 TQValueVector<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 TQString file; 00117 if (KStandardDirs::exists( TQDir::currentDirPath() + "/" + URL ) ) 00118 file = TQDir::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 TQString splitOut(const TQString &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 TQString 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(TQRegExp(">"), "\n>"); 00179 return filedata; 00180 } 00181 00182 void fillInstance(KInstance &ins, const TQString &srcdir) { 00183 TQString catalogs; 00184 00185 if ( srcdir.isEmpty() ) { 00186 catalogs += ins.dirs()->findResource("data", "ksgmltools2/customization/catalog.xml"); 00187 catalogs += ':'; 00188 catalogs += ins.dirs()->findResource("data", "ksgmltools2/docbook/xml-dtd-4.2/catalog.xml"); 00189 ins.dirs()->addResourceType("dtd", KStandardDirs::kde_default("data") + "ksgmltools2"); 00190 } else { 00191 catalogs += srcdir +"/customization/catalog.xml:" + srcdir + "/docbook/xml-dtd-4.2/catalog.xml"; 00192 ins.dirs()->addResourceDir("dtd", srcdir); 00193 } 00194 00195 xmlLoadCatalogs(catalogs.latin1()); 00196 } 00197 00198 extern "C" void *init_kbzip2filter(); 00199 00200 static TQIODevice *getBZip2device(const TQString &fileName ) 00201 { 00202 TQFile * f = new TQFile( 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(TQT_TQIODEVICE(f), true); 00209 return new KFilterDev(base, true); 00210 } 00211 return 0; 00212 } 00213 00214 bool saveToCache( const TQString &contents, const TQString &filename ) 00215 { 00216 TQIODevice *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 TQString &filename, 00233 const TQString &cache, TQString &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 TQIODevice *fd = ::getBZip2device(cache); 00243 if ( !fd ) 00244 return false; 00245 00246 if (!fd->open(IO_ReadOnly)) 00247 { 00248 delete fd; 00249 TQFile::remove(cache); 00250 return false; 00251 } 00252 00253 kdDebug( 7119 ) << "reading" << endl; 00254 00255 char buffer[32000]; 00256 int n; 00257 TQCString 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 = TQString::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 TQString lookForCache( const TQString &filename ) 00279 { 00280 kdDebug() << "lookForCache " << filename << endl; 00281 assert( filename.endsWith( ".docbook" ) ); 00282 assert( filename.at( 0 ) == '/' ); 00283 00284 TQString cache = filename.left( filename.length() - 7 ); 00285 TQString output; 00286 if ( readCache( filename, cache + "cache.bz2", output) ) 00287 return output; 00288 if ( readCache( filename, 00289 locateLocal( "cache", 00290 "kio_help" + cache + 00291 "cache.bz2" ), output ) ) 00292 return output; 00293 00294 return TQString::null; 00295 } 00296 00297 bool compareTimeStamps( const TQString &older, const TQString &newer ) 00298 { 00299 TQFileInfo _older( older ); 00300 TQFileInfo _newer( newer ); 00301 assert( _older.exists() ); 00302 if ( !_newer.exists() ) 00303 return false; 00304 return ( _newer.lastModified() > _older.lastModified() ); 00305 } 00306 00307 TQCString fromUnicode( const TQString &data ) 00308 { 00309 TQTextCodec *locale = TQTextCodec::codecForLocale(); 00310 TQCString result; 00311 char buffer[30000]; 00312 uint buffer_len = 0; 00313 uint len = 0; 00314 uint offset = 0; 00315 const int part_len = 5000; 00316 00317 TQString part; 00318 00319 while ( offset < data.length() ) 00320 { 00321 part = data.mid( offset, part_len ); 00322 TQCString 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 TQCString 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 TQString res; 00339 res.sprintf( "&#%d;", TQChar(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 += TQCString( buffer, buffer_len + 1); 00348 offset += part_len; 00349 } 00350 return result; 00351 } 00352 00353 void replaceCharsetHeader( TQString &output ) 00354 { 00355 TQString name = TQTextCodec::codecForLocale()->name(); 00356 name.replace( TQString( "ISO " ), "iso-" ); 00357 output.replace( TQString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ), 00358 TQString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\">" ).arg( name ) ); 00359 }