1
/* Copyright (C) 2000-2004 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3
* This program is free software; you can redistribute it and/or modify
4
* it under the terms of the GNU General Public License as published by
5
* the Free Software Foundation; either version 2 of the License, or
6
* (at your option) any later version.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program; if not, write to the Free Software
15
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
* $Id: xslt.c,v 1.1.1.1 2006/03/27 12:40:19 exodusd Exp $
22
#include <libxml/xmlmemory.h>
23
#include <libxml/debugXML.h>
24
#include <libxml/HTMLtree.h>
25
#include <libxml/xmlIO.h>
26
#include <libxml/DOCBparser.h>
27
#include <libxml/xinclude.h>
28
#include <libxml/catalog.h>
29
#include <libxml/xmlversion.h>
30
#include <libxslt/xslt.h>
31
#include <libxslt/xsltInternals.h>
32
#include <libxslt/transform.h>
33
#include <libxslt/xsltutils.h>
34
#include <libxslt/xsltconfig.h>
38
#include "interpret.h"
40
#include "pike_macros.h"
41
#include "module_support.h"
51
//#define XSLT_DEBUG 1
54
#define DEBUG_XSLT(d) fprintf(stderr, d)
59
extern int xmlLoadExtDtdDefaultValue;
60
xmlExternalEntityLoader defaultLoader = NULL;
63
struct program *xslt_program=NULL;
64
struct program *stylesheet_program=NULL;
66
//! Free the allocated xslt storage
69
static void free_xslt_storage(struct object *o)
71
if(THIS->base_uri != NULL) free_string(THIS->base_uri);
72
if(THIS->variables != NULL) free_mapping(THIS->variables);
73
if(THIS->xml != NULL) free_string(THIS->xml);
74
if(THIS->language != NULL) free_string(THIS->language);
75
if(THIS->charset) free(THIS->charset);
76
if(THIS->content_type) free(THIS->content_type);
77
if(THIS->stylesheet != NULL ) xsltFreeStylesheet(THIS->stylesheet);
78
if(THIS->match_include != NULL ) free_svalue(THIS->match_include);
79
if(THIS->open_include != NULL ) free_svalue(THIS->open_include);
80
if(THIS->read_include != NULL ) free_svalue(THIS->read_include);
81
if(THIS->close_include != NULL ) free_svalue(THIS->close_include);
82
if(THIS->file != NULL ) free_object(THIS->file);
84
MEMSET(THIS, 0, sizeof(xslt_storage));
88
//! Free the stylesheet storage when the object is destructed
90
static void free_stylesheet_storage(struct object *o)
92
stylesheet_storage* store = (stylesheet_storage*)o->storage;
94
MEMSET(store, 0, sizeof(stylesheet_storage));
98
//! Initialization routine for the storage
100
static void init_xslt_storage(struct object *o)
102
MEMSET(o->storage, 0, sizeof(xslt_storage));
106
//! Initialization for the stylesheet storage.
108
static void init_stylesheet_storage(struct object *o)
110
MEMSET(o->storage, 0, sizeof(stylesheet_storage));
114
//! Creation function for the Parser object.
116
void f_create(INT32 args)
122
//! Set the xml Data for the transformation.
124
void f_set_xml_data(INT32 args)
126
struct pike_string* xml_data;
129
Pike_error("XSLT.Parser()->set_xml_data: Expected one argument.\n");
131
if( THIS->xml != NULL )
132
free_string(THIS->xml);
133
if ( THIS->err_str != NULL ) {
134
free_string(THIS->err_str);
135
THIS->err_str = NULL;
140
switch ( ARG(1).type ) {
142
xmlSetGenericErrorFunc(THIS, xml_error);
143
xml_data = Pike_sp[-args].u.string;
144
//add_ref(THIS->xml);
145
THIS->doc = xmlParseMemory(xml_data->str, xml_data->len);
146
if ( THIS->err_str != NULL ) {
147
// only free doc at this point
148
xmlFreeDoc(THIS->doc);
149
Pike_error(THIS->err_str->str);
153
CHECK_DOM(Pike_sp[0-1].u.object);
154
add_ref(Pike_sp[0-1].u.object);
155
THIS->doc = OBJ2_DOM(Pike_sp[0-1].u.object)->domDoc;
158
Pike_error("XSLT.Parser()->set_xml_data: Expected string|object.\n");
168
//! Set the variables used for transformation.
170
void f_set_variables(INT32 args)
173
Pike_error("XSLT.Parser()->set_variables: Expected one argument.\n");
174
if ( Pike_sp[-args].type != T_MAPPING )
175
Pike_error("XSLT.Parser()->set_variables: Expected Mapping\n");
176
if ( THIS->variables != NULL )
177
free_mapping(THIS->variables);
178
THIS->variables = Pike_sp[-args].u.mapping;
179
add_ref(THIS->variables);
186
void xsl_error(void* ctx, const char* msg, ...) {
189
char out[2048] = { 0 };
190
xslt_storage* store = (xslt_storage*) ctx;
193
DEBUG_XSLT("xsl_error()\n");
195
fprintf(stderr, "No error context, error: %s\n", msg);
199
fprintf(stderr, "xsl_error() with no messages !");
204
vfprintf(stderr, msg, args);
211
vsnprintf(buf, 1023, msg, args);
215
if ( store->err_str != NULL ) {
216
if ( strlen(buf) + strlen(store->err_str->str) < 2048 )
217
strcat(out, store->err_str->str);
218
free_string(store->err_str);
222
store->err_str = make_shared_string(&out[0]);
223
add_ref(store->err_str);
224
DEBUG_XSLT("leaving xsl_error() with:\n");
226
DEBUG_XSLT("----\n");
232
void xml_error(void* ctx, const char* msg, ...) {
235
char out[2048] = { 0 };
236
xslt_storage* store = (xslt_storage*) ctx;
238
DEBUG_XSLT("xml_error()\n");
241
fprintf(stderr, "No error context, error: %s\n", msg);
248
vsnprintf(buf, 1023, msg, args);
252
if ( store->err_str != NULL ) {
253
if ( strlen(buf) + strlen(store->err_str->str) < 2048 )
254
strcat(out, store->err_str->str);
255
free_string(store->err_str);
258
DEBUG_XSLT("leaving xml_error():\n");
260
store->err_str = make_shared_string(&out[0]);
261
add_ref(store->err_str);
264
#define MAX_PARAMS 100
269
static void f_run( INT32 args )
271
xsltStylesheetPtr cur = NULL;
273
char *xmlstr, *xslstr;
274
xmlOutputBufferPtr xmlBuf;
282
char **vars = NULL; // variables
283
xmlChar* params[MAX_PARAMS+1];
285
if ( args != 1 || Pike_sp[-args].type != T_OBJECT )
286
Pike_error("XSLT.Parser->run(): requires XSL Stylesheet parameter.\n");
288
if ( THIS->doc == NULL ) {
289
Pike_error("XML input not set correctly.\n");
291
if ( THIS->err_str != NULL ) {
292
free_string(THIS->err_str);
293
THIS->err_str = NULL;
298
DEBUG_XSLT("Running XSL transformation\n");
301
xmlstr = THIS->xml->str;
302
if ( THIS->xml->len == 0 ) {
303
Pike_error("No XML code given - cannot transform.\n");
310
Pike_error("Unable to parse xml source.\n");
312
xmlSetGenericErrorFunc(NULL, NULL);
314
xsltSetGenericErrorFunc(THIS, xsl_error);
316
cur = ((xslt_storage*)Pike_sp[-args].u.object->storage)->stylesheet;
318
if ( THIS->variables != NULL )
320
struct svalue sind, sval;
324
vars = malloc( sizeof(char *) *
325
( 1 + ((m_sizeof(THIS->variables)) * 2 )));
326
MY_MAPPING_LOOP(THIS->variables, count, k) {
329
if(!(sind.type == T_STRING && sval.type == T_STRING)) {
334
str = (const xmlChar *) sval.u.string->str;
335
if ( xmlStrstr(str, (xmlChar*) "\n") == NULL )
337
if ( xmlStrchr(str, '"') ) {
338
if (xmlStrchr(str, '\'')) {
340
Pike_error("Param contains quote and double-quotes.");
343
value = xmlStrdup((const xmlChar *)"'");
344
value = xmlStrcat(value, str);
345
value = xmlStrcat(value, (const xmlChar *)"'");
347
value = xmlStrdup((const xmlChar *)"\"");
348
value = xmlStrcat(value, str);
349
value = xmlStrcat(value, (const xmlChar *)"\"");
354
// param contains newlines
355
value = xmlStrdup((const xmlChar *)"\"");
356
value = xmlStrcat(value, (const xmlChar *)"\"");
359
str = (const xmlChar*) sind.u.string->str;
360
// namespaces are bad
361
if ( xmlStrchr(str, ':') ) {
362
Pike_error("Invalid Parameter %s in namespace format !",
366
vars[tmpint++] = sind.u.string->str;
368
vars[tmpint++] = value;
369
params[varcount++] = value;
370
if ( varcount > MAX_PARAMS )
371
Pike_error("Too many params !");
376
vars = malloc(sizeof(char *));
380
DEBUG_XSLT("Applying stylesheet!\n");
381
res = xsltApplyStylesheet(cur, doc, vars);
382
if ( THIS->err_str != NULL ) {
383
// only free doc at this point
385
Pike_error(THIS->err_str->str);
387
DEBUG_XSLT("Preparing output buffer !\n");
389
// find out the encoding and use EncodingHandler
390
if ( cur->encoding != NULL && strcmp(cur->encoding, "iso-8859-1") == 0 )
392
xmlBuf = xmlAllocOutputBuffer(
393
xmlGetCharEncodingHandler((xmlCharEncoding)10));
397
// utf-8 does not required a handler
398
xmlBuf = xmlAllocOutputBuffer(NULL);
401
xsltSaveResultTo( xmlBuf, res, cur );
404
if ( THIS->err_str != NULL ) {
405
Pike_error(THIS->err_str->str);
409
if ( cur->encoding != NULL && strcmp(cur->encoding,"iso-8859-1") == 0 )
411
resultBuffer = malloc(strlen(xmlBuf->conv->content)+1);
412
strcpy(resultBuffer, xmlBuf->conv->content);
413
push_text(resultBuffer);
417
push_text(xmlBuf->buffer->content);
419
xmlOutputBufferClose(xmlBuf);
424
for ( i = 0; i < varcount; i++ )
427
if ( THIS->variables != NULL ) {
428
free_mapping(THIS->variables);
429
THIS->variables = NULL;
431
xsltCleanupGlobals();
434
DEBUG_XSLT("done...\n");
438
//! Get the libxml2 Version-
440
void f_get_version(INT32 args)
442
char *result = malloc(200);
444
sprintf(result, "libxml %s, libxslt %s",
445
LIBXML_DOTTED_VERSION, LIBXSLT_DOTTED_VERSION);
454
//! Create a new Stylesheet object and set the initial error function.
456
void f_create_stylesheet(INT32 args)
458
if ( THIS->err_str != NULL ) {
459
free_string(THIS->err_str);
460
THIS->err_str = NULL;
465
int xmlInputRead(void* context, char* buffer, int len)
472
//! Set the function to be called when parsing xsl:include tags.
474
void f_set_include_callbacks(INT32 args)
478
Pike_error("XSLT.Parser()->set_include_callbacks(): Expected four arguments (functions: match, open, read, close).\n");
479
for ( i = 0; i < 4; i++ )
480
if ( Pike_sp[-args+i].type != T_FUNCTION )
481
Pike_error("Arguments must be function pointers !\n");
484
if ( THIS->match_include != NULL )
485
free_svalue(THIS->match_include);
486
if ( THIS->open_include != NULL )
487
free_svalue(THIS->open_include);
488
if ( THIS->read_include != NULL )
489
free_svalue(THIS->read_include);
490
if ( THIS->close_include != NULL )
491
free_svalue(THIS->close_include);
493
THIS->match_include = malloc(sizeof(struct svalue));
494
THIS->open_include = malloc(sizeof(struct svalue));
495
THIS->read_include = malloc(sizeof(struct svalue));
496
THIS->close_include = malloc(sizeof(struct svalue));
498
assign_svalue_no_free(THIS->match_include, &Pike_sp[-4]);
499
assign_svalue_no_free(THIS->open_include, &Pike_sp[-3]);
500
assign_svalue_no_free(THIS->read_include, &Pike_sp[-2]);
501
assign_svalue_no_free(THIS->close_include, &Pike_sp[-1]);
506
//! Set a language for the Stylesheet.
508
void f_set_language(INT32 args)
510
struct pike_string* str;
513
Pike_error("XSLT.Stylesheeet->f_set_language(): Expected string.\n");
514
if ( Pike_sp[-args].type != T_STRING )
515
Pike_error("Argument must be the language string !\n");
516
str = (struct pike_string*)Pike_sp[-args].u.string;
517
THAT->language = str;
518
add_ref(THAT->language);
520
DEBUG_XSLT("Language set to ");
521
DEBUG_XSLT(str->str);
527
//! xsl:include callback registered to xslt. Only matches steam://
529
int _include_match(const char* filename)
533
if ( THIS->match_include == NULL ) {
534
DEBUG_XSLT("No include resolve callback !");
538
apply_svalue(THIS->match_include, 1);
540
if ( Pike_sp[-1].type != T_INT ) {
544
match = Pike_sp[-1].u.integer == 1;
550
//! Include open callback function.
552
void * _include_open(const char* filename)
556
if ( THIS->open_include == NULL )
560
apply_svalue(THIS->open_include, 1);
562
if ( Pike_sp[-1].type == T_INT ) {
566
obj = Pike_sp[-1].u.object;
568
if ( THIS->file != NULL )
569
free_object(THIS->file);
580
//! Read from the opened include file.
582
int _include_read(void* context, char* buffer, int len)
585
THREAD_SAFE_RUN(result=f_include_read(context, buffer, len));
593
int f_include_read(void* context, char* buffer, int len)
595
struct pike_string* str;
598
if ( THIS->read_include == NULL )
601
add_ref(THIS->file); // somehow the function call makes it loose refs
602
push_object(THIS->file);
603
if ( THAT->language == NULL )
604
push_text("english");
606
push_string(THAT->language);
607
add_ref(THAT->language);
610
push_int(THIS->iPosition);
611
apply_svalue(THIS->read_include, 3);
613
if ( Pike_sp[-1].type == T_INT ) {
618
str = Pike_sp[-1].u.string;
619
if ( str->len == 0 ) {
623
if ( str->len > len+THIS->iPosition ) {
624
strncpy(buffer, &str->str[THIS->iPosition], len);
625
THIS->iPosition += len;
627
else if ( str->len - THIS->iPosition >= 0 ) {
629
&str->str[THIS->iPosition],
630
str->len-THIS->iPosition);
631
buffer[str->len-THIS->iPosition] = '\0';
632
len = str->len+1-THIS->iPosition;
636
"Fatal error while reading include file-length mismatch!\n");
645
int _include_close(void* context)
647
struct pike_string* str;
649
if ( THIS->close_include == NULL )
652
push_object(THIS->file);
653
apply_svalue(THIS->close_include, 1);
658
steamExternalEntityLoader(const char *URL, const char *ID,
659
xmlParserCtxtPtr ctxt)
661
xmlParserInputPtr ret;
662
struct pike_string* str;
667
if ( THAT->language == NULL )
668
push_text("english");
670
push_string(THAT->language);
671
add_ref(THAT->language);
675
apply_svalue(THIS->read_include, 3);
677
if ( Pike_sp[-1].type == T_INT ) {
678
fprintf(stderr, "Fatal error - empty string: include not found");
683
str = Pike_sp[-1].u.string;
684
if ( str->len == 0 ) {
685
fprintf(stderr, "Fatal error - empty string: include not found");
690
buf = xmlCharStrdup(str->str);
691
ret = xmlNewStringInputStream(ctxt, buf);
692
ret->length = str->len;
698
void f_get_method(INT32 args)
700
if ( THAT->stylesheet == NULL )
701
Pike_error("XSLT.Stylesheet(): no stylesheet!");
702
if ( THAT->stylesheet->method == NULL )
703
Pike_error("XSLT.Stylesheet does not define a method!");
705
push_text((char*)THAT->stylesheet->method);
708
void f_get_encoding(INT32 args)
710
if ( THAT->stylesheet == NULL )
711
Pike_error("XSLT.Stylesheet(): no stylesheet!");
712
if ( THAT->stylesheet->encoding == NULL )
713
Pike_error("XSLT.Stylesheet does not define a method!");
715
push_text((char*)THAT->stylesheet->encoding);
719
//! Set the content of the Stylesheet which will create the parse
720
//! xsltStylesheet structure and store it in the objects storage.
722
void f_set_content(INT32 args)
724
struct pike_string* str;
728
Pike_error("XSLT.Stylesheet(): Expected content string.\n");
729
if(Pike_sp[-args].type != T_STRING)
730
Pike_error("XSLT.Stylesheet(): need xsl data for creation.\n");
732
if ( THAT->open_include == NULL || THAT->match_include == NULL ||
733
THAT->read_include == NULL || THAT->close_include == NULL )
734
Pike_error("XSLT.Stylesheet(): No callback functions defined.\n");
735
if ( THAT->stylesheet != NULL )
736
Pike_error("XSLT.Stylesheet(): stylesheet is not initialized correctly!\n");
741
str = (struct pike_string*)Pike_sp[-args].u.string;
743
Pike_error("XSLT.Stylesheet(): need content for stylesheet !\n");
749
xmlSetGenericErrorFunc(THAT, (xml_error));
751
xsl = xmlParseMemory(str->str, str->len);
753
if ( THAT->err_str != NULL ) {
754
Pike_error(THAT->err_str->str);
757
xmlSetGenericErrorFunc(NULL, NULL);
759
xsltSetGenericErrorFunc(THAT,(xsl_error));
761
THAT->stylesheet = xsltParseStylesheetDoc(xsl);
762
if ( THAT->err_str != NULL ) {
763
Pike_error(THAT->err_str->str);
766
xsltSetGenericErrorFunc(NULL, NULL);
770
//! Pike module initialization code. Offer two classes.
772
void pike_module_init( void )
774
xmlLoadExtDtdDefaultValue = 1;
775
DEBUG_XSLT("RegisterInputCallbacks()");
776
// old libxml (pre 2.6)
778
#if LIBXML_VERSION < 20600
779
if ( xmlRegisterInputCallbacks(_include_match, _include_open, _include_read, _include_close) == -1 )
780
fprintf(stderr, "Error registering input callbacks !");
782
defaultLoader = xmlGetExternalEntityLoader();
783
xmlSetExternalEntityLoader(steamExternalEntityLoader);
784
if ( xmlGetExternalEntityLoader() != steamExternalEntityLoader )
785
fprintf(stderr, "XSLT: Failed to set external entity loader !\n");
791
ADD_STORAGE(xslt_storage);
792
set_init_callback(init_xslt_storage);
793
set_exit_callback(free_xslt_storage);
794
ADD_FUNCTION2("create", f_create, tFunc(tNone,tVoid), 0,
795
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
796
ADD_FUNCTION2("set_xml_data", f_set_xml_data, tFunc(tOr(tStr, tObj), tVoid),0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
797
ADD_FUNCTION2("set_variables", f_set_variables,
798
tFunc(tMapping,tVoid), 0,
799
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
800
ADD_FUNCTION2("run", f_run, tFunc(tObj,tStr), 0,
801
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
802
ADD_FUNCTION2("get_version", f_get_version, tFunc(tVoid,tStr), 0,
803
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
805
end_class("Parser", 0);
808
ADD_STORAGE(xslt_storage);
809
set_init_callback(init_xslt_storage);
810
set_exit_callback(free_xslt_storage);
812
ADD_FUNCTION2("create", f_create_stylesheet, tFunc(tNone,tVoid), 0,
813
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
814
ADD_FUNCTION2("set_include_callbacks", f_set_include_callbacks,
815
tFunc(tFunc(tStr,tInt) tFunc(tStr,tObj) tFunc(tObj tStr tInt,tStr) tFunc(tObj,tVoid), tVoid), 0,
816
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
817
ADD_FUNCTION2("set_content", f_set_content, tFunc(tStr,tVoid), 0,
818
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
819
ADD_FUNCTION2("set_language", f_set_language, tFunc(tStr,tVoid), 0,
820
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
821
ADD_FUNCTION2("get_method", f_get_method, tFunc(tVoid,tStr), 0,
822
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
823
ADD_FUNCTION2("get_encoding", f_get_encoding, tFunc(tVoid,tStr), 0,
824
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
825
end_class("Stylesheet", 0);
828
/* Restore and exit module */
829
void pike_module_exit( void )
832
free_program(xslt_program);
833
if ( stylesheet_program )
834
free_program(stylesheet_program);