1#include "xslt.h"
2
3#include <libxslt/xsltconfig.h>
4#include <libxslt/xsltInternals.h>
5#include <libxslt/transform.h>
6#include <libxslt/xsltutils.h>
7#include <libxml/xmlIO.h>
8#include <libxml/parserInternals.h>
9#include <libxml/catalog.h>
10#include <QtCore/QDate>
11#include <QtCore/QDir>
12#include <QtCore/QRegExp>
13#include <assert.h>
14#include <QtCore/QTextCodec>
15#include <stdlib.h>
16#include <stdarg.h>
17
18#ifdef Q_OS_WIN
19#include <config-kdoctools.h>
20#include <QtCore/QCoreApplication>
21#include <QtCore/QDebug>
22#include <QtCore/QHash>
23#endif
24
25#if !defined( SIMPLE_XSLT )
26extern HelpProtocol *slave;
27#define INFO( x ) if (slave) slave->infoMessage(x);
28#else
29#define INFO( x )
30#endif
31
32int writeToQString(void * context, const char * buffer, int len)
33{
34 QString *t = (QString*)context;
35 *t += QString::fromUtf8(buffer, len);
36 return len;
37}
38
39int closeQString(void * context) {
40 QString *t = (QString*)context;
41 *t += '\n';
42 return 0;
43}
44
45#if defined (SIMPLE_XSLT) && defined(Q_WS_WIN)
46
47#define MAX_PATHS 64
48xmlExternalEntityLoader defaultEntityLoader = NULL;
49static xmlChar *paths[MAX_PATHS + 1];
50static int nbpaths = 0;
51static QHash<QString,QString> replaceURLList;
52
53/*
54* Entity loading control and customization.
55* taken from xsltproc.c
56*/
57static xmlParserInputPtr xsltprocExternalEntityLoader(const char *_URL, const char *ID,xmlParserCtxtPtr ctxt)
58{
59 xmlParserInputPtr ret;
60 warningSAXFunc warning = NULL;
61
62 // use local available dtd versions instead of fetching it everytime from the internet
63 QString url = QLatin1String(_URL);
64 QHash<QString, QString>::const_iterator i;
65 for(i = replaceURLList.constBegin(); i != replaceURLList.constEnd(); i++)
66 {
67 if (url.startsWith(i.key()))
68 {
69 url.replace(i.key(),i.value());
70 qDebug() << "converted" << _URL << "to" << url;
71 }
72 }
73 char URL[1024];
74 strcpy(URL,url.toLatin1().constData());
75
76 const char *lastsegment = URL;
77 const char *iter = URL;
78
79 if (nbpaths > 0) {
80 while (*iter != 0) {
81 if (*iter == '/')
82 lastsegment = iter + 1;
83 iter++;
84 }
85 }
86
87 if ((ctxt != NULL) && (ctxt->sax != NULL)) {
88 warning = ctxt->sax->warning;
89 ctxt->sax->warning = NULL;
90 }
91
92 if (defaultEntityLoader != NULL) {
93 ret = defaultEntityLoader(URL, ID, ctxt);
94 if (ret != NULL) {
95 if (warning != NULL)
96 ctxt->sax->warning = warning;
97 qDebug() << "Loaded URL=\"" << URL << "\" ID=\"" << ID << "\"";
98 return(ret);
99 }
100 }
101 for (int i = 0;i < nbpaths;i++) {
102 xmlChar *newURL;
103
104 newURL = xmlStrdup((const xmlChar *) paths[i]);
105 newURL = xmlStrcat(newURL, (const xmlChar *) "/");
106 newURL = xmlStrcat(newURL, (const xmlChar *) lastsegment);
107 if (newURL != NULL) {
108 ret = defaultEntityLoader((const char *)newURL, ID, ctxt);
109 if (ret != NULL) {
110 if (warning != NULL)
111 ctxt->sax->warning = warning;
112 qDebug() << "Loaded URL=\"" << newURL << "\" ID=\"" << ID << "\"";
113 xmlFree(newURL);
114 return(ret);
115 }
116 xmlFree(newURL);
117 }
118 }
119 if (warning != NULL) {
120 ctxt->sax->warning = warning;
121 if (URL != NULL)
122 warning(ctxt, "failed to load external entity \"%s\"\n", URL);
123 else if (ID != NULL)
124 warning(ctxt, "failed to load external entity \"%s\"\n", ID);
125 }
126 return(NULL);
127}
128#endif
129
130QString transform( const QString &pat, const QString& tss,
131 const QVector<const char *> &params )
132{
133 QString parsed;
134
135 INFO(i18n("Parsing stylesheet"));
136#if defined (SIMPLE_XSLT) && defined(Q_WS_WIN)
137 // prepare use of local available dtd versions instead of fetching everytime from the internet
138 // this approach is url based
139 if (!defaultEntityLoader) {
140 defaultEntityLoader = xmlGetExternalEntityLoader();
141 xmlSetExternalEntityLoader(xsltprocExternalEntityLoader);
142
143 replaceURLList[QLatin1String("http://www.oasis-open.org/docbook/xml/4.2")] = QString("file:///%1").arg(DOCBOOK_XML_CURRDTD);
144 }
145#endif
146
147 xsltStylesheetPtr style_sheet =
148 xsltParseStylesheetFile((const xmlChar *)QFile::encodeName(tss).constData());
149
150 if ( !style_sheet ) {
151 return parsed;
152 }
153 if (style_sheet->indent == 1)
154 xmlIndentTreeOutput = 1;
155 else
156 xmlIndentTreeOutput = 0;
157
158 INFO(i18n("Parsing document"));
159
160 xmlParserCtxtPtr pctxt;
161
162 pctxt = xmlNewParserCtxt();
163 if ( pctxt == NULL ) {
164 return parsed;
165 }
166
167 xmlDocPtr doc = xmlCtxtReadFile(pctxt, QFile::encodeName(pat), NULL,
168 XML_PARSE_NOENT|XML_PARSE_DTDLOAD|XML_PARSE_NONET);
169 /* Check both the returned doc (for parsing errors) and the context
170 (for validation errors) */
171 if (doc == NULL) {
172 return parsed;
173 } else {
174 if (pctxt->valid == 0) {
175 xmlFreeDoc(doc);
176 return parsed;
177 }
178 }
179
180 xsltTransformContextPtr ctxt;
181
182 ctxt = xsltNewTransformContext(style_sheet, doc);
183 if (ctxt == NULL)
184 return parsed;
185
186 INFO(i18n("Applying stylesheet"));
187 QVector<const char *> p = params;
188 p.append( NULL );
189 xmlDocPtr res = xsltApplyStylesheet(style_sheet, doc, const_cast<const char **>(&p[0]));
190 xmlFreeDoc(doc);
191 if (res != NULL) {
192 xmlOutputBufferPtr outp = xmlOutputBufferCreateIO(writeToQString, (xmlOutputCloseCallback)closeQString, &parsed, 0);
193 outp->written = 0;
194 INFO(i18n("Writing document"));
195 xsltSaveResultTo ( outp, res, style_sheet );
196 xmlOutputBufferFlush(outp);
197 xmlFreeDoc(res);
198 }
199 xsltFreeStylesheet(style_sheet);
200
201 if (parsed.isEmpty())
202 parsed = ' '; // avoid error message
203 return parsed;
204}
205
206/*
207xmlParserInputPtr meinExternalEntityLoader(const char *URL, const char *ID,
208 xmlParserCtxtPtr ctxt) {
209 xmlParserInputPtr ret = NULL;
210
211 // fprintf(stderr, "loading %s %s %s\n", URL, ID, ctxt->directory);
212
213 if (URL == NULL) {
214 if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
215 ctxt->sax->warning(ctxt,
216 "failed to load external entity \"%s\"\n", ID);
217 return(NULL);
218 }
219 if (!qstrcmp(ID, "-//OASIS//DTD DocBook XML V4.1.2//EN"))
220 URL = "docbook/xml-dtd-4.1.2/docbookx.dtd";
221 if (!qstrcmp(ID, "-//OASIS//DTD XML DocBook V4.1.2//EN"))
222 URL = "docbook/xml-dtd-4.1.2/docbookx.dtd";
223
224 QString file;
225 if (KStandardDirs::exists( QDir::currentPath() + "/" + URL ) )
226 file = QDir::currentPath() + "/" + URL;
227 else
228 file = locate("dtd", URL);
229
230 ret = xmlNewInputFromFile(ctxt, file.toLatin1().constData());
231 if (ret == NULL) {
232 if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
233 ctxt->sax->warning(ctxt,
234
235 "failed to load external entity \"%s\"\n", URL);
236 }
237 return(ret);
238}
239*/
240
241QString splitOut(const QString &parsed, int index)
242{
243 int start_index = index + 1;
244 while (parsed.at(start_index - 1) != '>') start_index++;
245
246 int inside = 0;
247
248 QString filedata;
249
250 while (true) {
251 int endindex = parsed.indexOf("</FILENAME>", index);
252 int startindex = parsed.indexOf("<FILENAME ", index) + 1;
253
254// kDebug() << "FILENAME " << startindex << " " << endindex << " " << inside << " " << parsed.mid(startindex + 18, 15)<< " " << parsed.length();
255
256 if (startindex > 0) {
257 if (startindex < endindex) {
258 // kDebug() << "finding another";
259 index = startindex + 8;
260 inside++;
261 } else {
262 index = endindex + 8;
263 inside--;
264 }
265 } else {
266 inside--;
267 index = endindex + 1;
268 }
269
270 if (inside == 0) {
271 filedata = parsed.mid(start_index, endindex - start_index);
272 break;
273 }
274
275 }
276
277 index = filedata.indexOf("<FILENAME ");
278
279 if (index > 0) {
280 int endindex = filedata.lastIndexOf("</FILENAME>");
281 while (filedata.at(endindex) != '>') endindex++;
282 endindex++;
283 filedata = filedata.left(index) + filedata.mid(endindex);
284 }
285
286 // filedata.replace(QRegExp(">"), "\n>");
287 return filedata;
288}
289
290QByteArray fromUnicode( const QString &data )
291{
292#ifdef Q_WS_WIN
293 return data.toUtf8();
294#else
295 QTextCodec *locale = QTextCodec::codecForLocale();
296 QByteArray result;
297 char buffer[30000];
298 uint buffer_len = 0;
299 uint len = 0;
300 int offset = 0;
301 const int part_len = 5000;
302
303 QString part;
304
305 while ( offset < data.length() )
306 {
307 part = data.mid( offset, part_len );
308 QByteArray test = locale->fromUnicode( part );
309 if ( locale->toUnicode( test ) == part ) {
310 result += test;
311 offset += part_len;
312 continue;
313 }
314 len = part.length();
315 buffer_len = 0;
316 for ( uint i = 0; i < len; i++ ) {
317 QByteArray test = locale->fromUnicode( part.mid( i, 1 ) );
318 if ( locale->toUnicode( test ) == part.mid( i, 1 ) ) {
319 if (buffer_len + test.length() + 1 > sizeof(buffer))
320 break;
321 strcpy( buffer + buffer_len, test.data() );
322 buffer_len += test.length();
323 } else {
324 QString res;
325 res.sprintf( "&#%d;", part.at( i ).unicode() );
326 test = locale->fromUnicode( res );
327 if (buffer_len + test.length() + 1 > sizeof(buffer))
328 break;
329 strcpy( buffer + buffer_len, test.data() );
330 buffer_len += test.length();
331 }
332 }
333 result += QByteArray( buffer, buffer_len + 1);
334 offset += part_len;
335 }
336 return result;
337#endif
338}
339
340void replaceCharsetHeader( QString &output )
341{
342 QString name;
343#ifdef Q_WS_WIN
344 name = "utf-8";
345 // may be required for all xml output
346 if (output.contains("<table-of-contents>"))
347 output.replace( QString( "<?xml version=\"1.0\"?>" ),
348 QString( "<?xml version=\"1.0\" encoding=\"%1\"?>").arg( name ) );
349#else
350 name = QTextCodec::codecForLocale()->name();
351 name.replace( QString( "ISO " ), "iso-" );
352 output.replace( QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ),
353 QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\">" ).arg( name ) );
354#endif
355}
356