1use crate::abi::allocator::xmlFreeImpl;
28use crate::abi::exports_xml2::*;
29use crate::abi::structs::*;
30use crate::abi::types::xmlElementType::*;
31use crate::abi::types::xmlXPathObjectType;
32use crate::abi::types::*;
33use crate::xml::tree::*;
34use std::ffi::c_void;
35use std::os::raw::{c_char, c_int};
36use std::ptr;
37
38use super::compiler::{
39 get_element_name, get_element_ns, is_xslt_element, is_xslt_namespace, XSLT_NAMESPACE,
40};
41
42pub const XSLT_MAX_DEPTH: c_int = 3000;
44
45pub const XSLT_MAX_INSERT_DEPTH: c_int = 50;
47
48pub const XSLT_STATE_OK: c_int = 0;
50pub const XSLT_STATE_ERROR: c_int = 1;
51
52pub const XSLT_STATE_STOPPED: c_int = 2;
54
55#[no_mangle]
58pub static mut xsltMaxDepth: c_int = 30000;
59
60#[no_mangle]
62pub static mut xsltMaxVars: c_int = 15000;
63
64static mut XSLT_XINCLUDE_DEFAULT: c_int = 0;
67
68#[no_mangle]
75pub unsafe extern "C" fn xsltSetXIncludeDefault(xinclude: c_int) {
76 unsafe { XSLT_XINCLUDE_DEFAULT = if xinclude != 0 { 1 } else { 0 } };
77}
78
79#[no_mangle]
81pub unsafe extern "C" fn xsltGetXIncludeDefault() -> c_int {
82 unsafe { XSLT_XINCLUDE_DEFAULT }
83}
84
85#[no_mangle]
92pub unsafe extern "C" fn xsltNewTransformContext(
93 style: *mut _xsltStylesheet,
94 doc: *mut _xmlDoc,
95) -> *mut _xsltTransformContext {
96 if style.is_null() {
97 return ptr::null_mut();
98 }
99 let ctxt = libc::calloc(1, core::mem::size_of::<_xsltTransformContext>())
100 as *mut _xsltTransformContext;
101 if ctxt.is_null() {
102 return ptr::null_mut();
103 }
104 (*ctxt).style = style;
105 (*ctxt).state = XSLT_STATE_OK;
106 (*ctxt).parserOptions = 2 | 4 | 8 | 16384;
110 (*ctxt).maxTemplateDepth = unsafe { xsltMaxDepth };
114 (*ctxt).maxTemplateVars = unsafe { xsltMaxVars };
115
116 let xpath_ctxt = xmlXPathNewContext(doc);
118 if !xpath_ctxt.is_null() {
119 (*ctxt).xpathCtxt = xpath_ctxt;
120 let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
123 if !internal.is_null() {
124 (*internal).func_lookup_data = ctxt as *mut c_void;
125 }
126 register_xslt_functions(ctxt);
129 }
130
131 (*ctxt).sec = crate::xslt::security::xsltGetDefaultSecurityPrefs();
135
136 if !doc.is_null() {
140 let docu = libc::calloc(1, core::mem::size_of::<_xsltDocument>()) as *mut _xsltDocument;
141 if !docu.is_null() {
142 (*docu).main = 1;
143 (*docu).doc = doc;
144 (*ctxt).document = docu;
145 (*ctxt).docList = docu;
146 }
147 (*ctxt).initialContextDoc = doc;
151 (*ctxt).initialContextNode = doc as *mut _xmlNode;
152 }
153 ctxt
154}
155
156#[no_mangle]
163pub unsafe extern "C" fn xsltFreeTransformContext(ctxt: *mut _xsltTransformContext) {
164 if ctxt.is_null() {
165 return;
166 }
167 if !(*ctxt).xpathCtxt.is_null() {
169 xmlXPathFreeContext((*ctxt).xpathCtxt);
170 }
171 crate::xslt::variables::xsltFreeGlobalVariables(ctxt);
177 crate::xslt::keys::xsltFreeKeyTables(ctxt);
179 crate::xslt::documents::xsltFreeDocCache(ctxt);
181 crate::xslt::extensions::xsltFreeExts(ctxt);
183 if !(*ctxt).varsTab.is_null() {
185 libc::free((*ctxt).varsTab as *mut libc::c_void);
186 }
187 if !(*ctxt).templTab.is_null() {
188 libc::free((*ctxt).templTab as *mut libc::c_void);
189 }
190 if !(*ctxt).document.is_null() {
193 let docu = (*ctxt).document;
194 (*docu).doc = ptr::null_mut();
195 libc::free(docu as *mut libc::c_void);
196 (*ctxt).document = ptr::null_mut();
197 }
198 libc::free(ctxt as *mut libc::c_void);
199}
200
201#[no_mangle]
211pub unsafe extern "C" fn xsltApplyStylesheet(
212 style: *mut _xsltStylesheet,
213 doc: *mut _xmlDoc,
214 params: *mut *const c_char,
215) -> *mut _xmlDoc {
216 xsltApplyStylesheetUser(
217 style,
218 doc,
219 params,
220 ptr::null(),
221 ptr::null_mut(),
222 ptr::null_mut(),
223 )
224}
225
226#[no_mangle]
232pub unsafe extern "C" fn xsltApplyStylesheetUser(
233 style: *mut _xsltStylesheet,
234 doc: *mut _xmlDoc,
235 params: *mut *const c_char,
236 _output: *const c_char,
237 _profile: *mut c_void,
238 userCtxt: *mut _xsltTransformContext,
239) -> *mut _xmlDoc {
240 if style.is_null() || doc.is_null() {
241 return ptr::null_mut();
242 }
243
244 let mut ctxt = userCtxt;
246 let mut own_ctxt = false;
247 if ctxt.is_null() {
248 ctxt = xsltNewTransformContext(style, doc);
249 if ctxt.is_null() {
250 return ptr::null_mut();
251 }
252 own_ctxt = true;
253 }
254
255 if !params.is_null() {
257 crate::xslt::parameters::xsltParseStylesheetParams(style, params);
258 }
259
260 crate::xslt::variables::xsltInitGlobalVariables(ctxt);
262
263 crate::xslt::keys::xsltInitKeys(ctxt, style);
265
266 crate::xslt::whitespace::xsltApplyStripSpaces(style, doc);
268
269 let result = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
271 if result.is_null() {
272 if own_ctxt {
273 xsltFreeTransformContext(ctxt);
274 }
275 return ptr::null_mut();
276 }
277 (*result).type_ = XML_DOCUMENT_NODE as c_int;
278 (*result).version = crate::xml::string::xml_strdup(b"1.0\0".as_ptr() as *const xmlChar);
279 (*result).doc = result;
280 if !(*style).encoding.is_null() {
284 (*result).encoding = crate::xml::string::xml_strdup((*style).encoding);
285 }
286 if !(*style).version.is_null() {
287 let v = crate::xml::string::xml_strdup((*style).version);
288 if !v.is_null() {
289 if !(*result).version.is_null() {
290 libc::free((*result).version as *mut libc::c_void);
291 }
292 (*result).version = v;
293 }
294 }
295
296 (*ctxt).initialContextDoc = doc;
299 (*ctxt).initialContextNode = doc as *mut _xmlNode;
300
301 (*ctxt).output = result;
302 (*ctxt).insert = result as *mut _xmlNode;
303
304 (*ctxt).node = doc as *mut _xmlNode;
308 if !(*ctxt).xpathCtxt.is_null() {
309 (*(*ctxt).xpathCtxt).node = doc as *mut _xmlNode;
310 (*(*ctxt).xpathCtxt).doc = doc;
311 (*(*ctxt).xpathCtxt).contextSize = 1;
312 (*(*ctxt).xpathCtxt).proximityPosition = 1;
313 }
314 let result_code = apply_templates_to_node(ctxt, doc as *mut _xmlNode, ptr::null());
315 let _ = result_code;
316
317 let final_result = if (*ctxt).state == XSLT_STATE_OK {
321 result
322 } else {
323 free_doc(result);
324 ptr::null_mut()
325 };
326
327 if own_ctxt {
328 (*ctxt).output = ptr::null_mut();
330 xsltFreeTransformContext(ctxt);
331 }
332 final_result
333}
334
335#[no_mangle]
341pub unsafe extern "C" fn xsltApplyStylesheetStacked(
342 style: *mut _xsltStylesheet,
343 doc: *mut _xmlDoc,
344 params: *mut *const c_char,
345 _stack: *mut c_void,
346) -> *mut _xmlDoc {
347 xsltApplyStylesheet(style, doc, params)
348}
349
350#[no_mangle]
356pub unsafe extern "C" fn xsltFreeTransformResult(result: *mut _xmlDoc) {
357 if !result.is_null() {
358 free_doc(result);
359 }
360}
361
362#[no_mangle]
374pub unsafe extern "C" fn xsltRunStylesheetUser(
375 style: *mut _xsltStylesheet,
376 doc: *mut _xmlDoc,
377 params: *mut *const c_char,
378 output: *const c_char,
379 SAX: *mut crate::abi::structs::_xmlSAXHandler,
380 IObuf: *mut crate::abi::structs::_xmlOutputBuffer,
381 profile: *mut c_void,
382 userCtxt: *mut _xsltTransformContext,
383) -> c_int {
384 if output.is_null() && SAX.is_null() && IObuf.is_null() {
385 return -1;
386 }
387 if !SAX.is_null() && !IObuf.is_null() {
388 return -1;
389 }
390 if !SAX.is_null() {
392 return -1;
393 }
394 let tmp = xsltApplyStylesheetUser(style, doc, params, output, profile, userCtxt);
395 if tmp.is_null() {
396 eprintln!("xsltRunStylesheet : run failed");
397 return -1;
398 }
399 let ret = if !IObuf.is_null() {
400 let mut txt: *mut xmlChar = ptr::null_mut();
401 let mut len: c_int = 0;
402 let r = crate::xslt::serialization::xsltSaveResultToString(&mut txt, &mut len, tmp, style);
403 if r != 0 || txt.is_null() {
404 -1
405 } else {
406 let written = crate::xml::io::output_buffer_write(IObuf, len, txt as *const c_char);
407 crate::abi::allocator::xmlFreeImpl(txt as *mut c_void);
408 written
409 }
410 } else {
411 crate::xslt::serialization::xsltSaveResultToFilename(output, tmp, style, 0)
412 };
413 free_doc(tmp);
414 ret
415}
416
417#[no_mangle]
423pub unsafe extern "C" fn xsltRunStylesheet(
424 style: *mut _xsltStylesheet,
425 doc: *mut _xmlDoc,
426 params: *mut *const c_char,
427 output: *const c_char,
428 SAX: *mut crate::abi::structs::_xmlSAXHandler,
429 IObuf: *mut crate::abi::structs::_xmlOutputBuffer,
430) -> c_int {
431 xsltRunStylesheetUser(
432 style,
433 doc,
434 params,
435 output,
436 SAX,
437 IObuf,
438 ptr::null_mut(),
439 ptr::null_mut(),
440 )
441}
442
443pub(crate) unsafe fn apply_root_template(
449 ctxt: *mut _xsltTransformContext,
450 doc: *mut _xmlDoc,
451) -> c_int {
452 let style = (*ctxt).style;
454 if style.is_null() {
455 return -1;
456 }
457 let doc_node = doc as *mut _xmlNode;
459 (*ctxt).node = doc_node;
460 if !(*ctxt).xpathCtxt.is_null() {
461 (*(*ctxt).xpathCtxt).contextSize = 1;
462 (*(*ctxt).xpathCtxt).proximityPosition = 1;
463 }
464 let templ = crate::xslt::templates::xsltFindTemplate(style, doc_node, ptr::null());
465 if templ.is_null() {
466 return 0;
468 }
469 let mut vars_base = (*ctxt).varsNr;
471 let _ = &mut vars_base;
472 (*ctxt).templ = templ;
473 execute_content(ctxt, (*templ).content);
474 (*ctxt).templ = ptr::null_mut();
475 0
476}
477
478pub(crate) unsafe fn apply_templates_to_node(
484 ctxt: *mut _xsltTransformContext,
485 node: *mut _xmlNode,
486 mode: *const xmlChar,
487) -> c_int {
488 let style = (*ctxt).style;
489 if style.is_null() {
490 return -1;
491 }
492 let templ = crate::xslt::templates::xsltFindTemplate(style, node, mode);
493 if templ.is_null() {
494 let typ = (*node).type_;
500 if typ == XML_TEXT_NODE as c_int
501 || typ == XML_CDATA_SECTION_NODE as c_int
502 || typ == XML_ATTRIBUTE_NODE as c_int
503 {
504 let content = node_get_content(node);
505 if !content.is_null() {
506 append_text_node(ctxt, content);
507 libc::free(content as *mut libc::c_void);
508 }
509 } else if typ == XML_ELEMENT_NODE as c_int
510 || typ == XML_DOCUMENT_NODE as c_int
511 || typ == XML_HTML_DOCUMENT_NODE as c_int
512 {
513 apply_templates_to_children(ctxt, node, mode);
515 }
516 return 0;
517 }
518 if (*ctxt).depth >= (*ctxt).maxTemplateDepth {
520 return -1;
521 }
522 (*ctxt).depth += 1;
523 (*ctxt).templ = templ;
524 (*ctxt).node = node;
525 if !(*ctxt).xpathCtxt.is_null() {
526 (*(*ctxt).xpathCtxt).contextSize = 1;
527 (*(*ctxt).xpathCtxt).proximityPosition = 1;
528 }
529 execute_content(ctxt, (*templ).content);
530 (*ctxt).depth -= 1;
531 (*ctxt).templ = ptr::null_mut();
532 0
533}
534
535pub(crate) unsafe fn apply_templates_to_children(
541 ctxt: *mut _xsltTransformContext,
542 node: *mut _xmlNode,
543 mode: *const xmlChar,
544) -> c_int {
545 let mut children: Vec<*mut _xmlNode> = Vec::new();
547 let mut child = (*node).children;
548 while !child.is_null() {
549 children.push(child);
550 child = (*child).next;
551 }
552 let size = children.len();
555 if !(*ctxt).xpathCtxt.is_null() {
556 (*(*ctxt).xpathCtxt).contextSize = size as c_int;
557 }
558 for (i, node) in children.iter().enumerate() {
559 (*ctxt).node = *node;
560 if !(*ctxt).xpathCtxt.is_null() {
561 (*(*ctxt).xpathCtxt).proximityPosition = (i + 1) as c_int;
562 }
563 apply_templates_to_node(ctxt, *node, mode);
564 }
565 0
566}
567
568pub unsafe fn execute_content(ctxt: *mut _xsltTransformContext, content: *mut _xmlNode) -> c_int {
574 let mut cur = content;
575 while !cur.is_null() {
576 let next = (*cur).next;
577 xsltProcessInstruction(ctxt, cur);
578 if (*ctxt).state == XSLT_STATE_ERROR {
579 return -1;
580 }
581 cur = next;
582 }
583 0
584}
585
586pub unsafe fn xsltProcessInstruction(
593 ctxt: *mut _xsltTransformContext,
594 inst: *mut _xmlNode,
595) -> c_int {
596 if ctxt.is_null() || inst.is_null() {
597 return -1;
598 }
599 let typ = (*inst).type_;
600 match typ {
601 t if t == XML_TEXT_NODE as c_int || t == XML_CDATA_SECTION_NODE as c_int => {
602 if !(*inst).content.is_null() {
604 append_text_node(ctxt, (*inst).content);
605 }
606 0
607 }
608 t if t == XML_COMMENT_NODE as c_int => {
609 if !(*inst).content.is_null() {
611 append_comment_node(ctxt, (*inst).content);
612 }
613 0
614 }
615 t if t == XML_PI_NODE as c_int => {
616 if !(*inst).name.is_null() {
618 let content = if (*inst).content.is_null() {
619 ptr::null()
620 } else {
621 (*inst).content
622 };
623 append_pi_node(ctxt, (*inst).name, content);
624 }
625 0
626 }
627 t if t == XML_ELEMENT_NODE as c_int => {
628 if is_xslt_namespace(inst) {
629 process_xslt_instruction(ctxt, inst);
630 } else {
631 process_literal_element(ctxt, inst);
634 }
635 0
636 }
637 _ => 0,
638 }
639}
640
641pub(crate) unsafe fn process_xslt_instruction(
647 ctxt: *mut _xsltTransformContext,
648 inst: *mut _xmlNode,
649) -> c_int {
650 let name = get_element_name(inst);
651 match name.as_deref() {
652 Some("apply-templates") => {
653 process_apply_templates(ctxt, inst);
654 }
655 Some("call-template") => {
656 process_call_template(ctxt, inst);
657 }
658 Some("apply-imports") => {
659 process_apply_imports(ctxt, inst);
660 }
661 Some("for-each") => {
662 process_for_each(ctxt, inst);
663 }
664 Some("value-of") => {
665 process_value_of(ctxt, inst);
666 }
667 Some("copy-of") => {
668 process_copy_of(ctxt, inst);
669 }
670 Some("copy") => {
671 process_copy(ctxt, inst);
672 }
673 Some("element") => {
674 process_element(ctxt, inst);
675 }
676 Some("attribute") => {
677 process_attribute(ctxt, inst);
678 }
679 Some("text") => {
680 process_text(ctxt, inst);
681 }
682 Some("comment") => {
683 process_comment(ctxt, inst);
684 }
685 Some("processing-instruction") => {
686 process_pi(ctxt, inst);
687 }
688 Some("number") => {
689 process_number(ctxt, inst);
690 }
691 Some("choose") => {
692 process_choose(ctxt, inst);
693 }
694 Some("when") | Some("otherwise") => {
695 }
697 Some("if") => {
698 process_if(ctxt, inst);
699 }
700 Some("variable") => {
701 process_variable(ctxt, inst);
702 }
703 Some("param") => {
704 process_param(ctxt, inst);
705 }
706 Some("with-param") => {
707 }
709 Some("sort") => {
710 }
712 Some("message") => {
713 process_message(ctxt, inst);
714 }
715 Some("fallback") => {
716 }
718 Some("output")
719 | Some("decimal-format")
720 | Some("namespace-alias")
721 | Some("attribute-set")
722 | Some("key")
723 | Some("strip-space")
724 | Some("preserve-space")
725 | Some("import")
726 | Some("include")
727 | Some("stylesheet")
728 | Some("transform") => {
729 }
731 _ => {
732 let ns = get_element_ns(inst);
735 if let Some(ns_uri) = ns {
736 if ns_uri == crate::exslt::EXSLT_NS_COMMON
738 && get_element_name(inst).as_deref() == Some("document")
739 {
740 process_exsl_document(ctxt, inst);
741 return 0;
742 }
743 let name_ptr = (*inst).name;
745 let ns_cstr = str_to_cstr(&ns_uri);
746 let found = crate::xslt::extensions::xsltFindExtElement(
747 ctxt,
748 name_ptr,
749 ns_cstr.as_ptr() as *const xmlChar,
750 );
751 if !found.is_null() {
752 return 0;
754 }
755 }
756 }
758 }
759 0
760}
761
762fn str_to_cstr(s: &str) -> Vec<u8> {
764 let mut v = s.as_bytes().to_vec();
765 v.push(0);
766 v
767}
768
769unsafe fn process_exsl_document(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
784 let href = get_prop(inst, b"href\0".as_ptr() as *const xmlChar);
785 if href.is_null() {
786 crate::xslt::errors::xsltTransformError(
787 ctxt,
788 (*ctxt).style,
789 inst,
790 b"exsl:document: missing href attribute\0".as_ptr() as *const c_char,
791 );
792 return;
793 }
794 let frag = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
796 if frag.is_null() {
797 libc::free(href as *mut libc::c_void);
798 return;
799 }
800 (*frag).type_ = XML_DOCUMENT_NODE as c_int;
801 (*frag).doc = frag;
802 let saved_insert = (*ctxt).insert;
803 let saved_output = (*ctxt).output;
804 (*ctxt).insert = frag as *mut _xmlNode;
805 (*ctxt).output = frag;
806 execute_content(ctxt, (*inst).children);
807 (*ctxt).insert = saved_insert;
808 (*ctxt).output = saved_output;
809
810 let fname = crate::abi::versioning::c_str_to_bytes(href as *const c_char);
812 if let Some(name) = fname {
813 let path = String::from_utf8_lossy(name);
814 let cpath = str_to_cstr(&path);
815 let out = libc::fopen(
816 cpath.as_ptr() as *const c_char,
817 b"wb\0".as_ptr() as *const c_char,
818 );
819 if !out.is_null() {
820 let buf = crate::xml::io::buf_create(-1);
821 if !buf.is_null() {
822 crate::xml::tree::doc_dump(buf, frag);
823 let content = crate::xml::io::buf_content(buf);
824 let len = crate::xml::io::buf_length(buf);
825 if !content.is_null() && len > 0 {
826 libc::fwrite(content as *const libc::c_void, 1, len as usize, out);
827 }
828 crate::xml::io::buf_free(buf);
829 }
830 libc::fclose(out);
831 }
832 }
833 libc::free(href as *mut libc::c_void);
834 free_doc(frag);
835}
836
837pub(crate) unsafe fn eval_xpath(
844 ctxt: *mut _xsltTransformContext,
845 expr: *const xmlChar,
846) -> *mut _xmlXPathObject {
847 if ctxt.is_null() || expr.is_null() {
848 return ptr::null_mut();
849 }
850 let xpath_ctxt = (*ctxt).xpathCtxt;
851 if xpath_ctxt.is_null() {
852 return ptr::null_mut();
853 }
854 (*xpath_ctxt).node = (*ctxt).node;
860 if !(*ctxt).document.is_null() {
861 (*xpath_ctxt).doc = (*(*ctxt).document).doc;
862 }
863 let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
864 if !internal.is_null() {
865 (*internal).context_node = (*ctxt).node;
866 if !(*ctxt).document.is_null() {
867 (*internal).document = (*(*ctxt).document).doc;
868 }
869 (*internal).context_size = (*xpath_ctxt).contextSize;
870 (*internal).context_position = (*xpath_ctxt).proximityPosition;
871 (*internal).proximity_position = (*xpath_ctxt).proximityPosition;
872 }
873 xmlXPathEvalExpression(expr, xpath_ctxt)
874}
875
876pub(crate) unsafe fn process_apply_templates(
882 ctxt: *mut _xsltTransformContext,
883 inst: *mut _xmlNode,
884) {
885 let mode = get_prop(inst, b"mode\0".as_ptr() as *const xmlChar);
887 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
889 let params = collect_with_params(ctxt, inst);
891
892 let obj = if !select.is_null() {
893 eval_xpath(ctxt, select)
894 } else {
895 build_child_node_set(ctxt)
898 };
899 if !select.is_null() {
900 libc::free(select as *mut libc::c_void);
901 }
902 if obj.is_null() {
903 if !mode.is_null() {
904 libc::free(mode as *mut libc::c_void);
905 }
906 return;
907 }
908
909 let nodes = if (*obj).type_ == xmlXPathObjectType::XPATH_NODESET as c_int {
911 (*obj).nodesetval as *mut _xmlNodeSet
912 } else {
913 ptr::null_mut()
914 };
915
916 if !nodes.is_null() && (*nodes).nodeNr > 0 {
917 let sort = find_sort_children(ctxt, inst);
919 let mut node_ptrs: Vec<*mut _xmlNode> = Vec::new();
920 let mut i = 0;
921 while i < (*nodes).nodeNr {
922 let n = *(*nodes).nodeTab.offset(i as isize);
923 if !n.is_null() {
924 node_ptrs.push(n);
925 }
926 i += 1;
927 }
928 if !sort.is_null() {
930 let mut sorted =
931 libc::calloc(1, core::mem::size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
932 if !sorted.is_null() {
933 (*sorted).nodeNr = node_ptrs.len() as c_int;
934 (*sorted).nodeMax = node_ptrs.len() as c_int;
935 let tab = libc::malloc(node_ptrs.len() * core::mem::size_of::<*mut _xmlNode>())
936 as *mut *mut _xmlNode;
937 (*sorted).nodeTab = tab;
938 for (idx, n) in node_ptrs.iter().enumerate() {
939 if !tab.is_null() {
940 *tab.offset(idx as isize) = *n;
941 }
942 }
943 crate::xslt::sorting::xsltSortNodeSet(ctxt, sorted, sort);
944 let mut k = 0;
946 while k < (*sorted).nodeNr {
947 let n = *(*sorted).nodeTab.offset(k as isize);
948 if !n.is_null() {
949 (*ctxt).node = n;
950 if !(*ctxt).xpathCtxt.is_null() {
951 (*(*ctxt).xpathCtxt).contextSize = (*sorted).nodeNr;
952 (*(*ctxt).xpathCtxt).proximityPosition = k + 1;
953 }
954 apply_templates_with_params(ctxt, n, mode, params);
955 }
956 k += 1;
957 }
958 libc::free((*sorted).nodeTab as *mut libc::c_void);
959 libc::free(sorted as *mut libc::c_void);
960 }
961 } else {
962 if !(*ctxt).xpathCtxt.is_null() {
963 (*(*ctxt).xpathCtxt).contextSize = node_ptrs.len() as c_int;
964 }
965 for (i, n) in node_ptrs.iter().enumerate() {
966 if !n.is_null() {
967 (*ctxt).node = *n;
968 if !(*ctxt).xpathCtxt.is_null() {
969 (*(*ctxt).xpathCtxt).proximityPosition = (i + 1) as c_int;
970 }
971 apply_templates_with_params(ctxt, *n, mode, params);
972 }
973 }
974 }
975 }
976
977 if !mode.is_null() {
978 libc::free(mode as *mut libc::c_void);
979 }
980 xmlXPathFreeObject(obj);
981}
982
983pub(crate) unsafe fn apply_templates_with_params(
989 ctxt: *mut _xsltTransformContext,
990 node: *mut _xmlNode,
991 mode: *const xmlChar,
992 params: *mut _xsltStackElem,
993) {
994 let mut p = params;
996 while !p.is_null() {
997 crate::xslt::parameters::xsltPushParam(ctxt, p);
998 p = (*p).next;
999 }
1000 apply_templates_to_node(ctxt, node, mode);
1001 let mut p = params;
1003 while !p.is_null() {
1004 crate::xslt::parameters::xsltPopParam(ctxt);
1005 p = (*p).next;
1006 }
1007}
1008
1009pub(crate) unsafe fn build_child_node_set(
1015 ctxt: *mut _xsltTransformContext,
1016) -> *mut _xmlXPathObject {
1017 let ns = xmlXPathNodeSetCreate(ptr::null_mut());
1018 if ns.is_null() {
1019 return ptr::null_mut();
1020 }
1021 let obj = xmlMalloc_zero_obj();
1022 if obj.is_null() {
1023 libc::free(ns as *mut libc::c_void);
1024 return ptr::null_mut();
1025 }
1026 (*obj).type_ = xmlXPathObjectType::XPATH_NODESET as c_int;
1027 (*obj).nodesetval = ns as *mut c_void;
1028 let node = (*ctxt).node;
1029 if !node.is_null() {
1030 let mut child = (*node).children;
1031 while !child.is_null() {
1032 append_to_node_set(ns, child);
1033 child = (*child).next;
1034 }
1035 }
1036 obj
1037}
1038
1039unsafe fn xmlMalloc_zero_obj() -> *mut _xmlXPathObject {
1041 libc::calloc(1, core::mem::size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject
1042}
1043
1044unsafe fn append_to_node_set(ns: *mut _xmlNodeSet, node: *mut _xmlNode) {
1050 if ns.is_null() || node.is_null() {
1051 return;
1052 }
1053 let mut i = 0;
1055 while i < (*ns).nodeNr {
1056 if *(*ns).nodeTab.offset(i as isize) == node {
1057 return;
1058 }
1059 i += 1;
1060 }
1061 if (*ns).nodeNr >= (*ns).nodeMax {
1062 let new_max = if (*ns).nodeMax == 0 {
1063 8
1064 } else {
1065 (*ns).nodeMax * 2
1066 };
1067 let new_tab = libc::realloc(
1068 (*ns).nodeTab as *mut libc::c_void,
1069 (new_max as usize) * core::mem::size_of::<*mut _xmlNode>(),
1070 ) as *mut *mut _xmlNode;
1071 if new_tab.is_null() {
1072 return;
1073 }
1074 (*ns).nodeTab = new_tab;
1075 (*ns).nodeMax = new_max;
1076 }
1077 *(*ns).nodeTab.offset((*ns).nodeNr as isize) = node;
1078 (*ns).nodeNr += 1;
1079}
1080
1081pub(crate) unsafe fn collect_with_params(
1089 ctxt: *mut _xsltTransformContext,
1090 inst: *mut _xmlNode,
1091) -> *mut _xsltStackElem {
1092 let mut head: *mut _xsltStackElem = ptr::null_mut();
1093 let mut tail: *mut _xsltStackElem = ptr::null_mut();
1094 let mut child = (*inst).children;
1095 while !child.is_null() {
1096 let next = (*child).next;
1097 if is_xslt_element(child, "with-param") {
1098 let param = evaluate_with_param(ctxt, child);
1099 if !param.is_null() {
1100 (*param).next = ptr::null_mut();
1101 if tail.is_null() {
1102 head = param;
1103 tail = param;
1104 } else {
1105 (*tail).next = param;
1106 tail = param;
1107 }
1108 }
1109 }
1110 child = next;
1111 }
1112 head
1113}
1114
1115pub(crate) unsafe fn evaluate_with_param(
1121 ctxt: *mut _xsltTransformContext,
1122 inst: *mut _xmlNode,
1123) -> *mut _xsltStackElem {
1124 let name = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1125 if name.is_null() {
1126 return ptr::null_mut();
1127 }
1128 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
1129 let param = libc::calloc(1, core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
1130 if param.is_null() {
1131 libc::free(name as *mut libc::c_void);
1132 if !select.is_null() {
1133 libc::free(select as *mut libc::c_void);
1134 }
1135 return ptr::null_mut();
1136 }
1137 (*param).name = name;
1138 (*param).flags = 2 | 4; if !select.is_null() {
1140 let obj = eval_xpath(ctxt, select);
1141 if !obj.is_null() {
1142 (*param).value = obj;
1143 }
1144 libc::free(select as *mut libc::c_void);
1145 } else {
1146 let value = eval_content_fragment(ctxt, (*inst).children);
1148 if !value.is_null() {
1149 (*param).value = value;
1150 }
1151 }
1152 param
1153}
1154
1155pub(crate) unsafe fn eval_content_fragment(
1161 ctxt: *mut _xsltTransformContext,
1162 content: *mut _xmlNode,
1163) -> *mut _xmlXPathObject {
1164 let frag = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
1165 if frag.is_null() {
1166 return ptr::null_mut();
1167 }
1168 (*frag).type_ = XML_DOCUMENT_NODE as c_int;
1169 (*frag).doc = frag;
1170 let saved_insert = (*ctxt).insert;
1172 let saved_output = (*ctxt).output;
1173 (*ctxt).insert = frag as *mut _xmlNode;
1174 (*ctxt).output = frag;
1175 execute_content(ctxt, content);
1176 (*ctxt).insert = saved_insert;
1177 (*ctxt).output = saved_output;
1178
1179 let obj = xmlMalloc_zero_obj();
1180 if obj.is_null() {
1181 free_doc(frag);
1182 return ptr::null_mut();
1183 }
1184 (*obj).type_ = xmlXPathObjectType::XPATH_XSLT_TREE as c_int;
1185 (*obj).nodesetval = frag as *mut c_void;
1186 obj
1187}
1188
1189pub(crate) unsafe fn process_call_template(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1195 let name = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1196 if name.is_null() {
1197 return;
1198 }
1199 let style = (*ctxt).style;
1200 let templ = crate::xslt::templates::xsltLookupTemplate(style, name);
1201 libc::free(name as *mut libc::c_void);
1202 if templ.is_null() {
1203 return;
1204 }
1205 if (*ctxt).depth >= (*ctxt).maxTemplateDepth {
1206 return;
1207 }
1208 (*ctxt).depth += 1;
1209 let params = collect_with_params(ctxt, inst);
1211 let mut p = params;
1212 while !p.is_null() {
1213 crate::xslt::parameters::xsltPushParam(ctxt, p);
1214 p = (*p).next;
1215 }
1216 let saved_templ = (*ctxt).templ;
1217 (*ctxt).templ = templ;
1218 execute_content(ctxt, (*templ).content);
1219 (*ctxt).templ = saved_templ;
1220 let mut p = params;
1221 while !p.is_null() {
1222 crate::xslt::parameters::xsltPopParam(ctxt);
1223 p = (*p).next;
1224 }
1225 (*ctxt).depth -= 1;
1226}
1227
1228pub(crate) unsafe fn process_apply_imports(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1238 let _ = inst;
1239 let style = (*ctxt).style;
1240 let node = (*ctxt).node;
1241 let current_templ = (*ctxt).templ;
1242 if style.is_null() || node.is_null() || current_templ.is_null() {
1243 return;
1244 }
1245 let current_depth = (*current_templ).position;
1251 let mode = (*current_templ).mode;
1252
1253 let mut best: *mut _xsltTemplate = ptr::null_mut();
1254 let mut best_priority: f32 = f32::NEG_INFINITY;
1255 let mut best_depth: c_int = -1;
1256
1257 let mut templ = (*style).templates;
1258 while !templ.is_null() {
1259 if (*templ).position <= current_depth {
1261 templ = (*templ).next;
1262 continue;
1263 }
1264 if !(*templ).mode.is_null() {
1266 if mode.is_null()
1267 || libc::strcmp(
1268 (*templ).mode as *const libc::c_char,
1269 mode as *const libc::c_char,
1270 ) != 0
1271 {
1272 templ = (*templ).next;
1273 continue;
1274 }
1275 } else if !mode.is_null() {
1276 templ = (*templ).next;
1277 continue;
1278 }
1279 let pattern_ptr = (*templ).params as *mut crate::xslt::patterns::_xsltPattern;
1282 if pattern_ptr.is_null() {
1283 templ = (*templ).next;
1284 continue;
1285 }
1286 if crate::xslt::patterns::xsltTestPattern(ctxt, pattern_ptr, node) == 0 {
1287 templ = (*templ).next;
1288 continue;
1289 }
1290 let priority = (*templ).priority;
1292 if priority > best_priority || (priority == best_priority && (*templ).position > best_depth)
1293 {
1294 best = templ;
1295 best_priority = priority;
1296 best_depth = (*templ).position;
1297 }
1298 templ = (*templ).next;
1299 }
1300
1301 if !best.is_null() {
1302 if (*ctxt).depth >= (*ctxt).maxTemplateDepth {
1303 return;
1304 }
1305 (*ctxt).depth += 1;
1306 let saved_templ = (*ctxt).templ;
1307 (*ctxt).templ = best;
1308 execute_content(ctxt, (*best).content);
1309 (*ctxt).templ = saved_templ;
1310 (*ctxt).depth -= 1;
1311 }
1312}
1313
1314pub(crate) unsafe fn process_for_each(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1320 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
1321 if select.is_null() {
1322 return;
1323 }
1324 let obj = eval_xpath(ctxt, select);
1325 libc::free(select as *mut libc::c_void);
1326 if obj.is_null() {
1327 return;
1328 }
1329 if (*obj).type_ != xmlXPathObjectType::XPATH_NODESET as c_int {
1330 xmlXPathFreeObject(obj);
1331 return;
1332 }
1333 let nodes = (*obj).nodesetval as *mut _xmlNodeSet;
1334 if nodes.is_null() || (*nodes).nodeNr == 0 {
1335 xmlXPathFreeObject(obj);
1336 return;
1337 }
1338 let sort = find_sort_children(ctxt, inst);
1340 let saved_node = (*ctxt).node;
1343 let xpath_ctxt = (*ctxt).xpathCtxt;
1344 let (saved_size, saved_pos) = if xpath_ctxt.is_null() {
1345 (0, 0)
1346 } else {
1347 ((*xpath_ctxt).contextSize, (*xpath_ctxt).proximityPosition)
1348 };
1349
1350 let mut node_ptrs: Vec<*mut _xmlNode> = Vec::new();
1351 let mut i = 0;
1352 while i < (*nodes).nodeNr {
1353 let n = *(*nodes).nodeTab.offset(i as isize);
1354 if !n.is_null() {
1355 node_ptrs.push(n);
1356 }
1357 i += 1;
1358 }
1359
1360 if !sort.is_null() {
1361 let mut sorted = libc::calloc(1, core::mem::size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
1363 if !sorted.is_null() {
1364 (*sorted).nodeNr = node_ptrs.len() as c_int;
1365 (*sorted).nodeMax = node_ptrs.len() as c_int;
1366 let tab = libc::malloc(node_ptrs.len() * core::mem::size_of::<*mut _xmlNode>())
1367 as *mut *mut _xmlNode;
1368 (*sorted).nodeTab = tab;
1369 for (idx, n) in node_ptrs.iter().enumerate() {
1370 if !tab.is_null() {
1371 *tab.offset(idx as isize) = *n;
1372 }
1373 }
1374 crate::xslt::sorting::xsltSortNodeSet(ctxt, sorted, sort);
1375 if !xpath_ctxt.is_null() {
1376 (*xpath_ctxt).contextSize = (*sorted).nodeNr;
1377 }
1378 let mut k = 0;
1379 while k < (*sorted).nodeNr {
1380 let n = *(*sorted).nodeTab.offset(k as isize);
1381 if !n.is_null() {
1382 (*ctxt).node = n;
1383 if !xpath_ctxt.is_null() {
1384 (*xpath_ctxt).proximityPosition = k + 1;
1385 }
1386 execute_content(ctxt, (*inst).children);
1387 }
1388 k += 1;
1389 }
1390 libc::free((*sorted).nodeTab as *mut libc::c_void);
1391 libc::free(sorted as *mut libc::c_void);
1392 }
1393 } else {
1394 if !xpath_ctxt.is_null() {
1395 (*xpath_ctxt).contextSize = node_ptrs.len() as c_int;
1396 }
1397 for (i, n) in node_ptrs.iter().enumerate() {
1398 if !n.is_null() {
1399 (*ctxt).node = *n;
1400 if !xpath_ctxt.is_null() {
1401 (*xpath_ctxt).proximityPosition = (i + 1) as c_int;
1402 }
1403 execute_content(ctxt, (*inst).children);
1404 }
1405 }
1406 }
1407
1408 (*ctxt).node = saved_node;
1410 if !xpath_ctxt.is_null() {
1411 (*xpath_ctxt).contextSize = saved_size;
1412 (*xpath_ctxt).proximityPosition = saved_pos;
1413 }
1414 xmlXPathFreeObject(obj);
1415}
1416
1417unsafe fn find_sort_children(
1424 ctxt: *mut _xsltTransformContext,
1425 inst: *mut _xmlNode,
1426) -> *mut _xsltSort {
1427 if ctxt.is_null() || inst.is_null() {
1428 return ptr::null_mut();
1429 }
1430 let mut child = (*inst).children;
1431 while !child.is_null() {
1432 if is_xslt_element(child, "sort") {
1433 let style = (*ctxt).style;
1436 let sort = crate::xslt::sorting::xsltCompileSort(style, child);
1437 return sort;
1438 }
1439 child = (*child).next;
1440 }
1441 ptr::null_mut()
1442}
1443
1444pub(crate) unsafe fn process_value_of(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1450 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
1451 if select.is_null() {
1452 return;
1453 }
1454 let obj = eval_xpath(ctxt, select);
1455 libc::free(select as *mut libc::c_void);
1456 if obj.is_null() {
1457 return;
1458 }
1459 let strv = xmlXPathCastToString(obj);
1460 xmlXPathFreeObject(obj);
1461 if !strv.is_null() {
1462 append_text_node(ctxt, strv);
1463 libc::free(strv as *mut libc::c_void);
1464 }
1465}
1466
1467pub(crate) unsafe fn process_copy_of(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1473 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
1474 if select.is_null() {
1475 return;
1476 }
1477 let obj = eval_xpath(ctxt, select);
1478 libc::free(select as *mut libc::c_void);
1479 if obj.is_null() {
1480 return;
1481 }
1482 if (*obj).type_ == xmlXPathObjectType::XPATH_NODESET as c_int {
1483 let nodes = (*obj).nodesetval as *mut _xmlNodeSet;
1484 if !nodes.is_null() {
1485 let mut i = 0;
1486 while i < (*nodes).nodeNr {
1487 let n = *(*nodes).nodeTab.offset(i as isize);
1488 if !n.is_null() {
1489 copy_node_deep(ctxt, n);
1490 }
1491 i += 1;
1492 }
1493 }
1494 } else if (*obj).type_ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
1495 let frag = (*obj).nodesetval as *mut _xmlDoc;
1497 if !frag.is_null() {
1498 let mut child = (*frag).children;
1499 while !child.is_null() {
1500 let next = (*child).next;
1501 copy_node_deep(ctxt, child);
1502 child = next;
1503 }
1504 }
1505 } else {
1506 let strv = xmlXPathCastToString(obj);
1508 if !strv.is_null() {
1509 append_text_node(ctxt, strv);
1510 libc::free(strv as *mut libc::c_void);
1511 }
1512 }
1513 xmlXPathFreeObject(obj);
1514}
1515
1516pub(crate) unsafe fn copy_node_deep(ctxt: *mut _xsltTransformContext, node: *mut _xmlNode) {
1522 if node.is_null() {
1523 return;
1524 }
1525 let typ = (*node).type_;
1526 if typ == XML_TEXT_NODE as c_int || typ == XML_CDATA_SECTION_NODE as c_int {
1527 if !(*node).content.is_null() {
1528 append_text_node(ctxt, (*node).content);
1529 }
1530 } else if typ == XML_COMMENT_NODE as c_int {
1531 if !(*node).content.is_null() {
1532 append_comment_node(ctxt, (*node).content);
1533 }
1534 } else if typ == XML_PI_NODE as c_int {
1535 if !(*node).name.is_null() {
1536 append_pi_node(ctxt, (*node).name, (*node).content);
1537 }
1538 } else if typ == XML_ELEMENT_NODE as c_int {
1539 let name = (*node).name;
1541 let new_elem = new_element_node(ctxt, name, (*node).ns);
1542 if new_elem.is_null() {
1543 return;
1544 }
1545 let mut prop = (*node).properties;
1547 while !prop.is_null() {
1548 let attr_name = (*prop).name;
1549 let attr_val = node_get_content((*prop).children);
1550 if !attr_name.is_null() && !attr_val.is_null() {
1551 set_prop(new_elem, attr_name, attr_val);
1552 libc::free(attr_val as *mut libc::c_void);
1553 }
1554 prop = (*prop).next;
1555 }
1556 let saved_insert = (*ctxt).insert;
1558 (*ctxt).insert = new_elem;
1559 let mut child = (*node).children;
1560 while !child.is_null() {
1561 let next = (*child).next;
1562 copy_node_deep(ctxt, child);
1563 child = next;
1564 }
1565 (*ctxt).insert = saved_insert;
1566 }
1567}
1568
1569unsafe fn new_element_node(
1575 ctxt: *mut _xsltTransformContext,
1576 name: *const xmlChar,
1577 ns: *mut _xmlNs,
1578) -> *mut _xmlNode {
1579 let elem = new_node(ns, name);
1580 if elem.is_null() {
1581 return ptr::null_mut();
1582 }
1583 append_to_result(ctxt, elem);
1584 elem
1585}
1586
1587pub(crate) unsafe fn append_to_result(ctxt: *mut _xsltTransformContext, node: *mut _xmlNode) {
1593 let insert = (*ctxt).insert;
1594 if insert.is_null() {
1595 return;
1596 }
1597 add_child(insert, node);
1599 let doc = if (*insert).type_ == XML_DOCUMENT_NODE as c_int {
1601 insert as *mut _xmlDoc
1602 } else {
1603 (*insert).doc
1604 };
1605 if !doc.is_null() {
1606 set_node_doc(node, doc);
1607 }
1608}
1609
1610unsafe fn set_node_doc(node: *mut _xmlNode, doc: *mut _xmlDoc) {
1616 if node.is_null() {
1617 return;
1618 }
1619 (*node).doc = doc;
1620 let mut prop = (*node).properties;
1621 while !prop.is_null() {
1622 (*prop).doc = doc;
1623 prop = (*prop).next;
1624 }
1625 let mut child = (*node).children;
1626 while !child.is_null() {
1627 set_node_doc(child, doc);
1628 child = (*child).next;
1629 }
1630}
1631
1632pub(crate) unsafe fn append_text_node(ctxt: *mut _xsltTransformContext, content: *const xmlChar) {
1638 let insert = (*ctxt).insert;
1639 if insert.is_null() || content.is_null() {
1640 return;
1641 }
1642 let text = new_text(content);
1643 if text.is_null() {
1644 return;
1645 }
1646 append_to_result(ctxt, text);
1647}
1648
1649pub(crate) unsafe fn append_comment_node(
1655 ctxt: *mut _xsltTransformContext,
1656 content: *const xmlChar,
1657) {
1658 let insert = (*ctxt).insert;
1659 if insert.is_null() || content.is_null() {
1660 return;
1661 }
1662 let comment = new_comment(content);
1663 if comment.is_null() {
1664 return;
1665 }
1666 append_to_result(ctxt, comment);
1667}
1668
1669pub(crate) unsafe fn append_pi_node(
1675 ctxt: *mut _xsltTransformContext,
1676 name: *const xmlChar,
1677 content: *const xmlChar,
1678) {
1679 let insert = (*ctxt).insert;
1680 if insert.is_null() || name.is_null() {
1681 return;
1682 }
1683 let pi = new_pi(name, content);
1684 if pi.is_null() {
1685 return;
1686 }
1687 append_to_result(ctxt, pi);
1688}
1689
1690pub(crate) unsafe fn process_copy(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1696 let node = (*ctxt).node;
1697 if node.is_null() {
1698 return;
1699 }
1700 let typ = (*node).type_;
1701 let saved_insert = (*ctxt).insert;
1702 if typ == XML_ELEMENT_NODE as c_int {
1703 let new_elem = new_element_node(ctxt, (*node).name, (*node).ns);
1704 if new_elem.is_null() {
1705 return;
1706 }
1707 (*ctxt).insert = new_elem;
1708 } else if typ == XML_TEXT_NODE as c_int || typ == XML_CDATA_SECTION_NODE as c_int {
1709 if !(*node).content.is_null() {
1710 append_text_node(ctxt, (*node).content);
1711 }
1712 } else if typ == XML_COMMENT_NODE as c_int {
1713 if !(*node).content.is_null() {
1714 append_comment_node(ctxt, (*node).content);
1715 }
1716 } else if typ == XML_PI_NODE as c_int {
1717 if !(*node).name.is_null() {
1718 append_pi_node(ctxt, (*node).name, (*node).content);
1719 }
1720 } else if typ == XML_ATTRIBUTE_NODE as c_int {
1721 if !(*node).children.is_null() {
1722 let val = node_get_content((*node).children);
1723 if !val.is_null() {
1724 append_text_node(ctxt, val);
1725 libc::free(val as *mut libc::c_void);
1726 }
1727 }
1728 }
1729 execute_content(ctxt, (*inst).children);
1731 (*ctxt).insert = saved_insert;
1732}
1733
1734pub(crate) unsafe fn process_element(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1740 let name_attr = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1741 if name_attr.is_null() {
1742 return;
1743 }
1744 let name_str = eval_avt(ctxt, name_attr);
1746 libc::free(name_attr as *mut libc::c_void);
1747 if name_str.is_null() {
1748 return;
1749 }
1750 let ns_attr = get_prop(inst, b"namespace\0".as_ptr() as *const xmlChar);
1752 let ns_str = if !ns_attr.is_null() {
1753 let v = eval_avt(ctxt, ns_attr);
1754 libc::free(ns_attr as *mut libc::c_void);
1755 v
1756 } else {
1757 ptr::null_mut()
1758 };
1759 let ns = if !ns_str.is_null() && *ns_str != 0 {
1761 let n = new_ns(ptr::null_mut(), ns_str, ptr::null());
1762 libc::free(ns_str as *mut libc::c_void);
1763 n
1764 } else {
1765 if !ns_str.is_null() {
1766 libc::free(ns_str as *mut libc::c_void);
1767 }
1768 ptr::null_mut()
1769 };
1770 let elem = new_node(ns, name_str);
1771 libc::free(name_str as *mut libc::c_void);
1772 if elem.is_null() {
1773 return;
1774 }
1775 append_to_result(ctxt, elem);
1776 let saved_insert = (*ctxt).insert;
1777 (*ctxt).insert = elem;
1778 execute_content(ctxt, (*inst).children);
1779 (*ctxt).insert = saved_insert;
1780}
1781
1782pub(crate) unsafe fn process_attribute(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1788 let name_attr = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1789 if name_attr.is_null() {
1790 return;
1791 }
1792 let name_str = eval_avt(ctxt, name_attr);
1794 libc::free(name_attr as *mut libc::c_void);
1795 if name_str.is_null() {
1796 return;
1797 }
1798 let insert = (*ctxt).insert;
1799 if insert.is_null() {
1800 xmlFreeImpl(name_str as *mut c_void);
1801 return;
1802 }
1803 let saved_insert = (*ctxt).insert;
1805 let buf = libc::calloc(1, core::mem::size_of::<_xmlBuffer>()) as *mut _xmlBuffer;
1806 if buf.is_null() {
1807 xmlFreeImpl(name_str as *mut c_void);
1808 return;
1809 }
1810 (*buf).content = libc::calloc(1, 64) as *mut xmlChar;
1811 (*buf).size = 64;
1812 (*buf).use_ = 0;
1813 let frag_doc = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
1814 if frag_doc.is_null() {
1815 libc::free(buf as *mut libc::c_void);
1816 xmlFreeImpl(name_str as *mut c_void);
1817 return;
1818 }
1819 (*frag_doc).type_ = XML_DOCUMENT_NODE as c_int;
1820 (*frag_doc).doc = frag_doc;
1821 (*ctxt).insert = frag_doc as *mut _xmlNode;
1822 execute_content(ctxt, (*inst).children);
1823 let mut value: Vec<u8> = Vec::new();
1825 let mut child = (*frag_doc).children;
1826 while !child.is_null() {
1827 if (*child).type_ == XML_TEXT_NODE as c_int {
1828 if !(*child).content.is_null() {
1829 let len = libc::strlen((*child).content as *const libc::c_char) as usize;
1830 value.extend_from_slice(core::slice::from_raw_parts((*child).content, len));
1831 }
1832 }
1833 child = (*child).next;
1834 }
1835 free_doc(frag_doc);
1837 (*ctxt).insert = saved_insert;
1838
1839 let mut cvalue = value.clone();
1841 cvalue.push(0);
1842 set_prop(insert, name_str, cvalue.as_ptr() as *const xmlChar);
1843 xmlFreeImpl(name_str as *mut c_void);
1844}
1845
1846pub(crate) unsafe fn process_text(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1852 let doe = get_prop(
1854 inst,
1855 b"disable-output-escaping\0".as_ptr() as *const xmlChar,
1856 );
1857 if !doe.is_null() {
1858 libc::free(doe as *mut libc::c_void);
1859 }
1860 let mut child = (*inst).children;
1862 while !child.is_null() {
1863 if (*child).type_ == XML_TEXT_NODE as c_int && !(*child).content.is_null() {
1864 append_text_node(ctxt, (*child).content);
1865 }
1866 child = (*child).next;
1867 }
1868}
1869
1870pub(crate) unsafe fn process_comment(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1876 let content = node_get_content((*inst).children);
1877 if !content.is_null() {
1878 append_comment_node(ctxt, content);
1879 libc::free(content as *mut libc::c_void);
1880 }
1881}
1882
1883pub(crate) unsafe fn process_pi(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1889 let name_attr = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1890 if name_attr.is_null() {
1891 return;
1892 }
1893 let name_str = eval_avt(ctxt, name_attr);
1895 libc::free(name_attr as *mut libc::c_void);
1896 if name_str.is_null() {
1897 return;
1898 }
1899 let content = node_get_content((*inst).children);
1900 append_pi_node(ctxt, name_str, content);
1901 if !content.is_null() {
1902 libc::free(content as *mut libc::c_void);
1903 }
1904 xmlFreeImpl(name_str as *mut c_void);
1905}
1906
1907pub(crate) unsafe fn process_number(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1913 let value_attr = get_prop(inst, b"value\0".as_ptr() as *const xmlChar);
1914 let mut number: f64 = f64::NAN;
1915 if !value_attr.is_null() {
1916 let obj = eval_xpath(ctxt, value_attr);
1917 libc::free(value_attr as *mut libc::c_void);
1918 if !obj.is_null() {
1919 number = (*obj).floatval;
1920 xmlXPathFreeObject(obj);
1921 }
1922 } else {
1923 number = 1.0;
1927 let node = (*ctxt).node;
1928 if !node.is_null() {
1929 let mut sib = (*node).prev;
1930 while !sib.is_null() {
1931 if (*sib).type_ == XML_ELEMENT_NODE as c_int {
1932 number += 1.0;
1933 }
1934 sib = (*sib).prev;
1935 }
1936 }
1937 }
1938 let format = get_prop(inst, b"format\0".as_ptr() as *const xmlChar);
1940 let formatted = crate::xslt::numbering::xsltFormatNumber(number, format);
1941 if !format.is_null() {
1942 libc::free(format as *mut libc::c_void);
1943 }
1944 if !formatted.is_null() {
1945 append_text_node(ctxt, formatted);
1946 libc::free(formatted as *mut libc::c_void);
1947 }
1948}
1949
1950unsafe fn xpath_obj_boolean(obj: *mut _xmlXPathObject) -> bool {
1957 if obj.is_null() {
1958 return false;
1959 }
1960 let typ = (*obj).type_;
1961 if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
1962 return (*obj).boolval != 0;
1963 }
1964 if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
1965 let n = (*obj).floatval;
1966 return n != 0.0 && !n.is_nan();
1967 }
1968 if typ == xmlXPathObjectType::XPATH_STRING as c_int {
1969 return !(*obj).stringval.is_null() && *(*obj).stringval != 0;
1970 }
1971 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1972 let ns = (*obj).nodesetval as *mut _xmlNodeSet;
1973 return !ns.is_null() && (*ns).nodeNr > 0;
1974 }
1975 false
1976}
1977
1978pub(crate) unsafe fn process_choose(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1984 let mut child = (*inst).children;
1985 let mut executed = false;
1986 while !child.is_null() {
1987 let next = (*child).next;
1988 if is_xslt_element(child, "when") {
1989 let test = get_prop(child, b"test\0".as_ptr() as *const xmlChar);
1990 if !test.is_null() {
1991 let obj = eval_xpath(ctxt, test);
1992 libc::free(test as *mut libc::c_void);
1993 let truthy = !obj.is_null() && xpath_obj_boolean(obj);
1994 if !obj.is_null() {
1995 xmlXPathFreeObject(obj);
1996 }
1997 if truthy {
1998 execute_content(ctxt, (*child).children);
1999 executed = true;
2000 break;
2001 }
2002 }
2003 } else if is_xslt_element(child, "otherwise") {
2004 if !executed {
2005 execute_content(ctxt, (*child).children);
2006 executed = true;
2007 }
2008 }
2009 child = next;
2010 }
2011}
2012
2013pub(crate) unsafe fn process_if(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
2019 let test = get_prop(inst, b"test\0".as_ptr() as *const xmlChar);
2020 if test.is_null() {
2021 return;
2022 }
2023 let obj = eval_xpath(ctxt, test);
2024 libc::free(test as *mut libc::c_void);
2025 if obj.is_null() {
2026 return;
2027 }
2028 let truthy = xpath_obj_boolean(obj);
2032 xmlXPathFreeObject(obj);
2033 if truthy {
2034 execute_content(ctxt, (*inst).children);
2035 }
2036}
2037
2038pub(crate) unsafe fn process_variable(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
2044 let name = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
2045 if name.is_null() {
2046 return;
2047 }
2048 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
2049 let var = libc::calloc(1, core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
2050 if var.is_null() {
2051 libc::free(name as *mut libc::c_void);
2052 if !select.is_null() {
2053 libc::free(select as *mut libc::c_void);
2054 }
2055 return;
2056 }
2057 (*var).name = name;
2058 (*var).flags = 4; if !select.is_null() {
2060 let obj = eval_xpath(ctxt, select);
2061 if !obj.is_null() {
2062 (*var).value = obj;
2063 }
2064 libc::free(select as *mut libc::c_void);
2065 } else {
2066 let value = eval_content_fragment(ctxt, (*inst).children);
2067 if !value.is_null() {
2068 (*var).value = value;
2069 }
2070 }
2071 crate::xslt::variables::xsltPushVariable(ctxt, var);
2072}
2073
2074pub(crate) unsafe fn process_param(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
2080 let name = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
2081 if name.is_null() {
2082 return;
2083 }
2084 let already_bound = {
2088 let xpath_ctxt = (*ctxt).xpathCtxt;
2089 if xpath_ctxt.is_null() {
2090 false
2091 } else {
2092 let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
2093 if internal.is_null() {
2094 false
2095 } else {
2096 let name_len = libc::strlen(name as *const libc::c_char);
2097 let name_bytes = core::slice::from_raw_parts(name, name_len);
2098 let name_owned = String::from_utf8_lossy(name_bytes).into_owned();
2099 (*internal).variables.contains_key(&name_owned)
2100 }
2101 }
2102 };
2103 if already_bound {
2104 libc::free(name as *mut libc::c_void);
2106 return;
2107 }
2108 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
2109 let var = libc::calloc(1, core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
2110 if var.is_null() {
2111 libc::free(name as *mut libc::c_void);
2112 if !select.is_null() {
2113 libc::free(select as *mut libc::c_void);
2114 }
2115 return;
2116 }
2117 (*var).name = name;
2118 (*var).flags = 2 | 4; if !select.is_null() {
2120 let obj = eval_xpath(ctxt, select);
2121 if !obj.is_null() {
2122 (*var).value = obj;
2123 }
2124 libc::free(select as *mut libc::c_void);
2125 } else {
2126 let value = eval_content_fragment(ctxt, (*inst).children);
2127 if !value.is_null() {
2128 (*var).value = value;
2129 }
2130 }
2131 crate::xslt::variables::xsltPushVariable(ctxt, var);
2132}
2133
2134pub(crate) unsafe fn process_message(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
2140 let content = node_get_content((*inst).children);
2141 if !content.is_null() {
2142 let len = libc::strlen(content as *const libc::c_char) as usize;
2144 let _ = libc::write(2, content as *const libc::c_void, len);
2145 let terminate = get_prop(inst, b"terminate\0".as_ptr() as *const xmlChar);
2147 if !terminate.is_null() {
2148 if libc::strcmp(
2149 terminate as *const libc::c_char,
2150 b"yes\0".as_ptr() as *const libc::c_char,
2151 ) == 0
2152 {
2153 (*ctxt).state = XSLT_STATE_ERROR;
2154 }
2155 libc::free(terminate as *mut libc::c_void);
2156 }
2157 libc::free(content as *mut libc::c_void);
2158 }
2159}
2160
2161pub(crate) unsafe fn eval_avt(
2177 ctxt: *mut _xsltTransformContext,
2178 value: *const xmlChar,
2179) -> *mut xmlChar {
2180 if value.is_null() {
2181 return ptr::null_mut();
2182 }
2183 let len = libc::strlen(value as *const libc::c_char);
2184 let bytes = core::slice::from_raw_parts(value, len);
2185 let mut out: Vec<u8> = Vec::new();
2186 let mut i = 0;
2187 while i < bytes.len() {
2188 let b = bytes[i];
2189 if b == b'{' {
2190 if i + 1 < bytes.len() && bytes[i + 1] == b'{' {
2191 out.push(b'{');
2192 i += 2;
2193 continue;
2194 }
2195 if let Some(rel) = bytes[i + 1..].iter().position(|c| *c == b'}') {
2197 let close = i + 1 + rel;
2198 let expr_bytes = &bytes[i + 1..close];
2199 let expr_c = crate::xml::string::bytes_to_xmlstr(expr_bytes);
2200 if !expr_c.is_null() {
2201 let obj = eval_xpath(ctxt, expr_c);
2202 xmlFreeImpl(expr_c as *mut c_void);
2203 if !obj.is_null() {
2204 let strv = xmlXPathCastToString(obj);
2205 xmlXPathFreeObject(obj);
2206 if !strv.is_null() {
2207 let slen = libc::strlen(strv as *const libc::c_char);
2208 out.extend_from_slice(core::slice::from_raw_parts(strv, slen));
2209 xmlFreeImpl(strv as *mut c_void);
2210 }
2211 }
2212 }
2213 i = close + 1;
2214 continue;
2215 }
2216 out.push(b'{');
2218 i += 1;
2219 continue;
2220 }
2221 if b == b'}' {
2222 if i + 1 < bytes.len() && bytes[i + 1] == b'}' {
2223 out.push(b'}');
2224 i += 2;
2225 continue;
2226 }
2227 out.push(b'}');
2228 i += 1;
2229 continue;
2230 }
2231 out.push(b);
2232 i += 1;
2233 }
2234 crate::xml::string::bytes_to_xmlstr(&out)
2235}
2236
2237pub(crate) unsafe fn process_literal_element(
2243 ctxt: *mut _xsltTransformContext,
2244 inst: *mut _xmlNode,
2245) {
2246 let elem = new_node((*inst).ns, (*inst).name);
2248 if elem.is_null() {
2249 return;
2250 }
2251 append_to_result(ctxt, elem);
2252 let mut prop = (*inst).properties;
2254 while !prop.is_null() {
2255 let attr_name = (*prop).name;
2256 if !attr_name.is_null() {
2258 let name_bytes = core::slice::from_raw_parts(
2259 attr_name,
2260 libc::strlen(attr_name as *const libc::c_char) as usize,
2261 );
2262 if name_bytes != b"xmlns" {
2263 let attr_val = node_get_content((*prop).children);
2264 if !attr_val.is_null() {
2265 let avt_val = eval_avt(ctxt, attr_val);
2267 libc::free(attr_val as *mut libc::c_void);
2268 if !avt_val.is_null() {
2269 set_prop(elem, attr_name, avt_val);
2270 xmlFreeImpl(avt_val as *mut c_void);
2271 }
2272 }
2273 }
2274 }
2275 prop = (*prop).next;
2276 }
2277 let saved_insert = (*ctxt).insert;
2279 (*ctxt).insert = elem;
2280 execute_content(ctxt, (*inst).children);
2281 (*ctxt).insert = saved_insert;
2282}
2283
2284pub(crate) unsafe fn register_xslt_functions(ctxt: *mut _xsltTransformContext) {
2292 let xpath_ctxt = (*ctxt).xpathCtxt;
2293 if xpath_ctxt.is_null() {
2294 return;
2295 }
2296 let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
2297 if internal.is_null() {
2298 return;
2299 }
2300 let internal = &mut *internal;
2301
2302 use crate::xml::xpath::context::XPathContext;
2303 use crate::xml::xpath::types::{NodeSet, XPathValue};
2304
2305 let core_funcs = crate::xml::xpath::functions::core_functions();
2310 for (name, func) in core_funcs {
2311 internal.register_function(&name, func);
2312 }
2313
2314 internal.register_function("document", |ctx, args| {
2317 let value = match args.first() {
2318 Some(v) => v.as_string(),
2319 None => return Err("document() requires an argument".to_string()),
2320 };
2321 let uri = value;
2323 let _ = ctx;
2328 let _ = uri;
2329 Ok(XPathValue::NodeSet(NodeSet::new()))
2330 });
2331
2332 internal.register_function("key", |ctx, args| {
2337 let tctxt = ctx.func_lookup_data as *mut _xsltTransformContext;
2338 if tctxt.is_null() {
2339 return Ok(XPathValue::NodeSet(NodeSet::new()));
2340 }
2341 let name_str = match args.first() {
2342 Some(v) => v.as_string(),
2343 None => return Err("key() requires a name argument".to_string()),
2344 };
2345 let value_str = match args.get(1) {
2348 Some(XPathValue::NodeSet(ns)) => match ns.first() {
2349 Some(n) => crate::xml::xpath::types::node_string_value(n),
2350 None => return Ok(XPathValue::NodeSet(NodeSet::new())),
2351 },
2352 Some(v) => v.as_string(),
2353 None => return Err("key() requires a value argument".to_string()),
2354 };
2355 let name_c = crate::xml::string::bytes_to_xmlstr(name_str.as_bytes());
2356 let value_c = crate::xml::string::bytes_to_xmlstr(value_str.as_bytes());
2357 if name_c.is_null() || value_c.is_null() {
2358 if !name_c.is_null() {
2359 crate::abi::allocator::xmlFreeImpl(name_c as *mut c_void);
2360 }
2361 if !value_c.is_null() {
2362 crate::abi::allocator::xmlFreeImpl(value_c as *mut c_void);
2363 }
2364 return Ok(XPathValue::NodeSet(NodeSet::new()));
2365 }
2366 let ns = unsafe { crate::xslt::keys::xsltEvalKeyFunction(tctxt, name_c, value_c) };
2367 crate::abi::allocator::xmlFreeImpl(name_c as *mut c_void);
2368 crate::abi::allocator::xmlFreeImpl(value_c as *mut c_void);
2369 if ns.is_null() {
2370 return Ok(XPathValue::NodeSet(NodeSet::new()));
2371 }
2372 let mut out = NodeSet::new();
2373 unsafe {
2374 let node_nr = (*ns).nodeNr;
2375 let node_tab = (*ns).nodeTab;
2376 if !node_tab.is_null() {
2377 for i in 0..node_nr as isize {
2378 let n = *node_tab.add(i as usize);
2379 if !n.is_null() {
2380 out.push(n);
2381 }
2382 }
2383 }
2384 crate::abi::exports_xml2::xmlXPathFreeNodeSet(ns);
2385 }
2386 Ok(XPathValue::NodeSet(out))
2387 });
2388
2389 internal.register_function("generate-id", |ctx, args| {
2392 let node = match args.first() {
2393 Some(XPathValue::NodeSet(ns)) => ns.first().unwrap_or(ctx.context_node),
2394 _ => ctx.context_node,
2395 };
2396 if node.is_null() {
2397 return Ok(XPathValue::String(String::new()));
2398 }
2399 let id = unsafe { format!("id{:p}", node) };
2401 Ok(XPathValue::String(id))
2402 });
2403
2404 internal.register_function("system-property", |_ctx, args| {
2407 let name = match args.first() {
2408 Some(v) => v.as_string(),
2409 None => return Err("system-property() requires an argument".to_string()),
2410 };
2411 let value = match name.as_str() {
2412 "xsl:version" => "1.0",
2413 "xsl:vendor" => "libxslt",
2414 "xsl:vendor-url" => "http://xmlsoft.org/XSLT/",
2415 _ => "",
2416 };
2417 Ok(XPathValue::String(value.to_string()))
2418 });
2419
2420 internal.register_function("element-available", |_ctx, args| {
2423 let name = match args.first() {
2424 Some(v) => v.as_string(),
2425 None => return Err("element-available() requires an argument".to_string()),
2426 };
2427 let exslt_elements = [
2429 "exsl:document",
2430 "exsl:node-set",
2431 "exsl:object-type",
2432 "func:function",
2433 "func:result",
2434 "func:script",
2435 "dyn:element",
2436 "dyn:attribute",
2437 "dyn:call",
2438 "dyn:evaluate",
2439 ];
2440 let available = name.starts_with("xsl:")
2441 || exslt_elements.contains(&name.as_str())
2442 || matches!(
2443 name.as_str(),
2444 "apply-templates"
2445 | "call-template"
2446 | "apply-imports"
2447 | "for-each"
2448 | "value-of"
2449 | "copy-of"
2450 | "copy"
2451 | "element"
2452 | "attribute"
2453 | "text"
2454 | "comment"
2455 | "processing-instruction"
2456 | "number"
2457 | "choose"
2458 | "if"
2459 | "variable"
2460 | "param"
2461 | "sort"
2462 | "message"
2463 | "fallback"
2464 | "output"
2465 | "decimal-format"
2466 | "namespace-alias"
2467 | "attribute-set"
2468 | "key"
2469 | "strip-space"
2470 | "preserve-space"
2471 | "import"
2472 | "include"
2473 | "stylesheet"
2474 | "transform"
2475 );
2476 Ok(XPathValue::Boolean(available))
2477 });
2478
2479 internal.register_function("function-available", |_ctx, args| {
2480 let name = match args.first() {
2481 Some(v) => v.as_string(),
2482 None => return Err("function-available() requires an argument".to_string()),
2483 };
2484 let core = [
2486 "last",
2487 "position",
2488 "count",
2489 "id",
2490 "local-name",
2491 "namespace-uri",
2492 "name",
2493 "string",
2494 "concat",
2495 "starts-with",
2496 "contains",
2497 "substring-before",
2498 "substring-after",
2499 "substring",
2500 "string-length",
2501 "normalize-space",
2502 "translate",
2503 "boolean",
2504 "not",
2505 "true",
2506 "false",
2507 "lang",
2508 "number",
2509 "sum",
2510 "floor",
2511 "ceiling",
2512 "round",
2513 ];
2514 let xslt_fn = [
2515 "document",
2516 "key",
2517 "generate-id",
2518 "system-property",
2519 "element-available",
2520 "function-available",
2521 "current",
2522 "unparsed-entity-uri",
2523 ];
2524 let exslt_available = crate::exslt::lookup(&name).is_some();
2527 let local = name.rsplit(':').next().unwrap_or(&name);
2528 Ok(XPathValue::Boolean(
2529 core.contains(&local) || xslt_fn.contains(&local) || exslt_available,
2530 ))
2531 });
2532
2533 internal.register_function("current", |ctx, _args| {
2535 let node = ctx.context_node;
2536 if node.is_null() {
2537 return Ok(XPathValue::NodeSet(NodeSet::new()));
2538 }
2539 let mut ns = NodeSet::new();
2540 ns.push(node);
2541 Ok(XPathValue::NodeSet(ns))
2542 });
2543
2544 for (name, f) in crate::exslt::iter_functions() {
2550 internal.register_function(&name, f);
2551 }
2552 crate::exslt::functions::register_stylesheet_functions(
2554 internal,
2555 (*ctxt)
2556 .style
2557 .as_ref()
2558 .map_or(std::ptr::null_mut(), |s| s.doc),
2559 );
2560
2561 internal.function_lookup = Some(Box::new(|ctx: &XPathContext, name: &str| {
2568 let Some((prefix, local)) = name.split_once(':') else {
2569 return None;
2570 };
2571 let tctxt = ctx.func_lookup_data as *mut _xsltTransformContext;
2572 if tctxt.is_null() {
2573 return None;
2574 }
2575 let style = unsafe { (*tctxt).style.as_ref() }?;
2577 let style_doc = style.doc;
2578 let prefix_c = crate::xml::string::bytes_to_xmlstr(prefix.as_bytes());
2579 let root = unsafe { (*style_doc).children };
2580 let ns = unsafe { crate::xml::tree::search_ns(style_doc, root, prefix_c) };
2581 if !prefix_c.is_null() {
2582 crate::abi::allocator::xmlFreeImpl(prefix_c as *mut c_void);
2583 }
2584 if ns.is_null() {
2585 return None;
2586 }
2587 let href = unsafe { (*ns).href };
2588 if href.is_null() {
2589 return None;
2590 }
2591 let href_str = unsafe {
2592 std::ffi::CStr::from_ptr(href as *const std::os::raw::c_char)
2593 .to_string_lossy()
2594 .into_owned()
2595 };
2596 let local_c = crate::xml::string::bytes_to_xmlstr(local.as_bytes());
2597 let href_c = crate::xml::string::bytes_to_xmlstr(href_str.as_bytes());
2598 let fnptr = unsafe { crate::xslt::extensions::xsltFindExtFunction(tctxt, local_c, href_c) };
2599 if !local_c.is_null() {
2600 crate::abi::allocator::xmlFreeImpl(local_c as *mut c_void);
2601 }
2602 if !href_c.is_null() {
2603 crate::abi::allocator::xmlFreeImpl(href_c as *mut c_void);
2604 }
2605 if fnptr.is_null() {
2606 return None;
2607 }
2608 let f: Option<unsafe extern "C" fn(*mut c_void, c_int)> =
2611 unsafe { std::mem::transmute(fnptr) };
2612 let tctxt_addr = tctxt as usize;
2613 Some(Box::new(
2614 move |_ctx: &mut XPathContext, args: &[XPathValue]| {
2615 let t = tctxt_addr as *mut _xsltTransformContext;
2616 let xpath_ctxt = unsafe { (*t).xpathCtxt };
2617 if xpath_ctxt.is_null() {
2618 return Err("XSLT: null XPath context".to_string());
2619 }
2620 unsafe { crate::abi::exports_xml2::call_c_xpath_function(f, xpath_ctxt, args) }
2621 },
2622 ))
2623 }));
2624}
2625
2626pub fn exslt_element_names() -> &'static [&'static str] {
2628 &[
2629 "exsl:document",
2630 "exsl:node-set",
2631 "exsl:object-type",
2632 "func:function",
2633 "func:result",
2634 "func:script",
2635 "dyn:element",
2636 "dyn:attribute",
2637 "dyn:call",
2638 "dyn:evaluate",
2639 ]
2640}
2641
2642pub fn exslt_function_names() -> Vec<String> {
2644 crate::exslt::iter_functions()
2645 .into_iter()
2646 .map(|(n, _)| n)
2647 .collect()
2648}
2649
2650#[cfg(test)]
2651mod tests {
2652 use super::*;
2653 use core::ptr;
2654
2655 #[test]
2656 fn test_new_context_null_style() {
2657 unsafe {
2658 assert!(xsltNewTransformContext(ptr::null_mut(), ptr::null_mut()).is_null());
2659 }
2660 }
2661
2662 #[test]
2663 fn test_free_null() {
2664 unsafe {
2665 xsltFreeTransformContext(ptr::null_mut());
2666 xsltFreeTransformResult(ptr::null_mut());
2667 }
2668 }
2669
2670 #[test]
2671 fn test_apply_stylesheet_null() {
2672 unsafe {
2673 assert!(
2674 xsltApplyStylesheet(ptr::null_mut(), ptr::null_mut(), ptr::null_mut()).is_null()
2675 );
2676 }
2677 }
2678
2679 #[test]
2680 fn test_end_to_end_simplified_stylesheet() {
2681 unsafe {
2682 let xsl = b"<?xml version=\"1.0\"?><html xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"><body><p>Hello</p></body></html>\0";
2685 let style = crate::xslt::stylesheet::xsltParseStylesheetMemory(
2686 xsl.as_ptr() as *const c_char,
2687 (xsl.len() - 1) as c_int,
2688 ptr::null(),
2689 );
2690 assert!(!style.is_null(), "stylesheett parse failed");
2691
2692 let src = b"<?xml version=\"1.0\"?><root><item>world</item></root>\0";
2694 let doc = crate::abi::exports_xml2::xmlReadMemory(
2695 src.as_ptr() as *const c_char,
2696 (src.len() - 1) as c_int,
2697 ptr::null(),
2698 ptr::null(),
2699 0,
2700 );
2701 assert!(!doc.is_null());
2702
2703 let result = xsltApplyStylesheet(style, doc, ptr::null_mut());
2704 assert!(!result.is_null(), "apply failed");
2705
2706 let mut txt: *mut xmlChar = ptr::null_mut();
2708 let mut len: c_int = 0;
2709 let ret = crate::xslt::serialization::xsltSaveResultToString(
2710 &mut txt, &mut len, result, style,
2711 );
2712 assert_eq!(ret, 0);
2713 assert!(!txt.is_null());
2714 let out = String::from_utf8_lossy(core::slice::from_raw_parts(txt, len as usize));
2715 assert!(
2716 out.contains("Hello"),
2717 "result should contain the literal text, got: {}",
2718 out
2719 );
2720
2721 libc::free(txt as *mut libc::c_void);
2722 crate::xml::tree::free_doc(result);
2723 crate::xml::tree::free_doc(doc);
2724 crate::xslt::stylesheet::xsltFreeStylesheet(style);
2725 }
2726 }
2727
2728 #[test]
2729 fn test_end_to_end_template_transform() {
2730 unsafe {
2731 let xsl = b"<?xml version=\"1.0\"?>\n\
2734 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\
2735 <xsl:template match=\"/\">\n\
2736 <out><xsl:value-of select=\"/root/item\"/></out>\n\
2737 </xsl:template>\n\
2738 </xsl:stylesheet>\0";
2739 let style = crate::xslt::stylesheet::xsltParseStylesheetMemory(
2740 xsl.as_ptr() as *const c_char,
2741 (xsl.len() - 1) as c_int,
2742 ptr::null(),
2743 );
2744 assert!(!style.is_null(), "stylesheet parse failed");
2745
2746 let src = b"<?xml version=\"1.0\"?><root><item>world</item></root>\0";
2747 let doc = crate::abi::exports_xml2::xmlReadMemory(
2748 src.as_ptr() as *const c_char,
2749 (src.len() - 1) as c_int,
2750 ptr::null(),
2751 ptr::null(),
2752 0,
2753 );
2754 assert!(!doc.is_null());
2755
2756 let result = xsltApplyStylesheet(style, doc, ptr::null_mut());
2757 assert!(!result.is_null(), "apply failed");
2758
2759 let mut txt: *mut xmlChar = ptr::null_mut();
2760 let mut len: c_int = 0;
2761 let ret = crate::xslt::serialization::xsltSaveResultToString(
2762 &mut txt, &mut len, result, style,
2763 );
2764 assert_eq!(ret, 0);
2765 let out = String::from_utf8_lossy(core::slice::from_raw_parts(txt, len as usize));
2766 assert!(
2767 out.contains("world"),
2768 "result should contain the selected value, got: {}",
2769 out
2770 );
2771
2772 libc::free(txt as *mut libc::c_void);
2773 crate::xml::tree::free_doc(result);
2774 crate::xml::tree::free_doc(doc);
2775 crate::xslt::stylesheet::xsltFreeStylesheet(style);
2776 }
2777 }
2778
2779 unsafe fn run_transform(xsl: &[u8], src: &[u8]) -> String {
2782 let style = crate::xslt::stylesheet::xsltParseStylesheetMemory(
2783 xsl.as_ptr() as *const c_char,
2784 (xsl.len() - 1) as c_int,
2785 ptr::null(),
2786 );
2787 assert!(!style.is_null(), "stylesheet parse failed");
2788 let doc = crate::abi::exports_xml2::xmlReadMemory(
2789 src.as_ptr() as *const c_char,
2790 (src.len() - 1) as c_int,
2791 ptr::null(),
2792 ptr::null(),
2793 0,
2794 );
2795 assert!(!doc.is_null());
2796 let result = xsltApplyStylesheet(style, doc, ptr::null_mut());
2797 assert!(!result.is_null(), "apply failed");
2798 let mut txt: *mut xmlChar = ptr::null_mut();
2799 let mut len: c_int = 0;
2800 let ret =
2801 crate::xslt::serialization::xsltSaveResultToString(&mut txt, &mut len, result, style);
2802 assert_eq!(ret, 0);
2803 let out =
2804 String::from_utf8_lossy(core::slice::from_raw_parts(txt, len as usize)).into_owned();
2805 libc::free(txt as *mut libc::c_void);
2806 crate::xml::tree::free_doc(result);
2807 crate::xml::tree::free_doc(doc);
2808 crate::xslt::stylesheet::xsltFreeStylesheet(style);
2809 out
2810 }
2811
2812 #[test]
2813 fn test_xslt_for_each() {
2814 unsafe {
2815 let xsl = b"<?xml version=\"1.0\"?>\
2816 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2817 <xsl:template match=\"/\">\
2818 <list><xsl:for-each select=\"/root/item\"><i><xsl:value-of select=\".\"/></i></xsl:for-each></list>\
2819 </xsl:template>\
2820 </xsl:stylesheet>\0";
2821 let src =
2822 b"<?xml version=\"1.0\"?><root><item>a</item><item>b</item><item>c</item></root>\0";
2823 let out = run_transform(xsl, src);
2824 assert!(out.contains("<i>a</i>"), "got: {}", out);
2825 assert!(out.contains("<i>b</i>"), "got: {}", out);
2826 assert!(out.contains("<i>c</i>"), "got: {}", out);
2827 }
2828 }
2829
2830 #[test]
2831 fn test_xslt_core_functions_in_value_of() {
2832 unsafe {
2837 let xsl = b"<?xml version=\"1.0\"?>\
2838 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2839 <xsl:template match=\"/\">\
2840 <out>\
2841 <cnt><xsl:value-of select=\"count(library/book)\"/></cnt>\
2842 <sub><xsl:value-of select=\"substring('hello',1,2)\"/></sub>\
2843 <str><xsl:value-of select=\"string(library/book[1]/title)\"/></str>\
2844 </out>\
2845 </xsl:template>\
2846 </xsl:stylesheet>\0";
2847 let src = b"<?xml version=\"1.0\"?>\
2848 <library>\
2849 <book><title>Rust</title></book>\
2850 <book><title>XML</title></book>\
2851 </library>\0";
2852 let out = run_transform(xsl, src);
2853 assert!(out.contains("<cnt>2</cnt>"), "count() wrong: {}", out);
2854 assert!(out.contains("<sub>he</sub>"), "substring() wrong: {}", out);
2855 assert!(out.contains("<str>Rust</str>"), "string() wrong: {}", out);
2856 }
2857 }
2858
2859 #[test]
2860 fn test_xslt_avt_in_literal_attribute() {
2861 unsafe {
2865 let xsl = b"<?xml version=\"1.0\"?>\
2866 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2867 <xsl:template match=\"/\">\
2868 <out>\
2869 <xsl:for-each select=\"library/book\">\
2870 <book id=\"{@id}\" label=\"{{literal}}\"/>\
2871 </xsl:for-each>\
2872 </out>\
2873 </xsl:template>\
2874 </xsl:stylesheet>\0";
2875 let src = b"<?xml version=\"1.0\"?>\
2876 <library>\
2877 <book id=\"b1\"/>\
2878 <book id=\"b2\"/>\
2879 </library>\0";
2880 let out = run_transform(xsl, src);
2881 assert!(
2882 out.contains("<book id=\"b1\" label=\"{literal}\""),
2883 "AVT not evaluated: {}",
2884 out
2885 );
2886 assert!(
2887 out.contains("<book id=\"b2\" label=\"{literal}\""),
2888 "AVT not evaluated: {}",
2889 out
2890 );
2891 }
2892 }
2893
2894 #[test]
2895 fn test_xslt_avt_in_xsl_element_name() {
2896 unsafe {
2898 let xsl = b"<?xml version=\"1.0\"?>\
2899 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2900 <xsl:template match=\"/\">\
2901 <out>\
2902 <xsl:element name=\"el-{library/book/@id}\">text</xsl:element>\
2903 </out>\
2904 </xsl:template>\
2905 </xsl:stylesheet>\0";
2906 let src = b"<?xml version=\"1.0\"?>\
2907 <library><book id=\"b1\"/></library>\0";
2908 let out = run_transform(xsl, src);
2909 assert!(
2910 out.contains("<el-b1>"),
2911 "xsl:element AVT not evaluated: {}",
2912 out
2913 );
2914 }
2915 }
2916
2917 #[test]
2918 fn test_xslt_variable_inline_content_rtf() {
2919 unsafe {
2925 let xsl = b"<?xml version=\"1.0\"?>\
2926 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2927 <xsl:variable name=\"rtf\"><nums><n>3</n><n>7</n></nums></xsl:variable>\
2928 <xsl:template match=\"/\">\
2929 <out><v><xsl:value-of select=\"$rtf\"/></v></out>\
2930 </xsl:template>\
2931 </xsl:stylesheet>\0";
2932 let src = b"<?xml version=\"1.0\"?><root/>\0";
2933 let out = run_transform(xsl, src);
2935 assert!(out.contains("<v>37</v>"), "RTF string-value wrong: {}", out);
2936 }
2937 }
2938
2939 #[test]
2940 fn test_xslt_exsl_node_set_on_rtf() {
2941 unsafe {
2945 crate::exslt::register_all();
2946 let xsl = b"<?xml version=\"1.0\"?>\
2947 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:exsl=\"http://exslt.org/common\" xmlns:math=\"http://exslt.org/math\" extension-element-prefixes=\"exsl math\">\
2948 <xsl:variable name=\"rtf\"><nums><n>3</n><n>7</n><n>1</n><n>9</n></nums></xsl:variable>\
2949 <xsl:template match=\"/\">\
2950 <out>\
2951 <max><xsl:value-of select=\"math:max(exsl:node-set($rtf)/nums/n)\"/></max>\
2952 <cnt><xsl:value-of select=\"count(exsl:node-set($rtf)/nums/n)\"/></cnt>\
2953 </out>\
2954 </xsl:template>\
2955 </xsl:stylesheet>\0";
2956 let src = b"<?xml version=\"1.0\"?><root/>\0";
2957 let out = run_transform(xsl, src);
2958 assert!(out.contains("<max>9</max>"), "math:max wrong: {}", out);
2959 assert!(out.contains("<cnt>4</cnt>"), "count wrong: {}", out);
2960 }
2961 }
2962
2963 #[test]
2964 fn test_xslt_if_node_set_test() {
2965 unsafe {
2970 let xsl = b"<?xml version=\"1.0\"?>\
2971 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2972 <xsl:template match=\"/\">\
2973 <out>\
2974 <xsl:for-each select=\"library/book\">\
2975 <b><xsl:if test=\"author\">A</xsl:if><xsl:if test=\"missing\">M</xsl:if></b>\
2976 </xsl:for-each>\
2977 </out>\
2978 </xsl:template>\
2979 </xsl:stylesheet>\0";
2980 let src = b"<?xml version=\"1.0\"?>\
2981 <library><book><author>x</author></book><book/></library>\0";
2982 let out = run_transform(xsl, src);
2983 assert!(out.contains("<b>A</b>"), "node-set test false: {}", out);
2984 assert!(out.contains("<b/>"), "missing-node test true: {}", out);
2985 }
2986 }
2987
2988 #[test]
2989 fn test_xslt_attribute_string_value() {
2990 unsafe {
2995 let xsl = b"<?xml version=\"1.0\"?>\
2996 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2997 <xsl:template match=\"/\">\
2998 <out>\
2999 <x><xsl:value-of select=\"string(library/book[1]/@id)\"/></x>\
3000 <y><xsl:value-of select=\"count(library/book[@id='b2'])\"/></y>\
3001 </out>\
3002 </xsl:template>\
3003 </xsl:stylesheet>\0";
3004 let src = b"<?xml version=\"1.0\"?>\
3005 <library><book id=\"b1\"/><book id=\"b2\"/></library>\0";
3006 let out = run_transform(xsl, src);
3007 assert!(
3008 out.contains("<x>b1</x>"),
3009 "attr string-value wrong: {}",
3010 out
3011 );
3012 assert!(out.contains("<y>1</y>"), "attr predicate wrong: {}", out);
3013 }
3014 }
3015
3016 #[test]
3017 fn test_xslt_sort_descending() {
3018 unsafe {
3022 let xsl = b"<?xml version=\"1.0\"?>\
3023 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3024 <xsl:template match=\"/\">\
3025 <out>\
3026 <xsl:for-each select=\"library/book\">\
3027 <xsl:sort select=\"title\" order=\"descending\"/>\
3028 <i><xsl:value-of select=\"title\"/></i>\
3029 </xsl:for-each>\
3030 </out>\
3031 </xsl:template>\
3032 </xsl:stylesheet>\0";
3033 let src = b"<?xml version=\"1.0\"?>\
3034 <library><book><title>Alpha</title></book><book><title>Gamma</title></book><book><title>Beta</title></book></library>\0";
3035 let out = run_transform(xsl, src);
3036 let gamma = out.find("<i>Gamma</i>").unwrap();
3037 let beta = out.find("<i>Beta</i>").unwrap();
3038 let alpha = out.find("<i>Alpha</i>").unwrap();
3039 assert!(gamma < beta && beta < alpha, "not descending: {}", out);
3040 }
3041 }
3042
3043 #[test]
3044 fn test_xslt_key_function() {
3045 unsafe {
3048 let xsl = b"<?xml version=\"1.0\"?>\
3049 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3050 <xsl:key name=\"byAuthor\" match=\"book\" use=\"author\"/>\
3051 <xsl:template match=\"/\">\
3052 <out><k><xsl:value-of select=\"key('byAuthor', 'Smith')/title\"/></k></out>\
3053 </xsl:template>\
3054 </xsl:stylesheet>\0";
3055 let src = b"<?xml version=\"1.0\"?>\
3056 <library><book><title>A</title><author>Smith</author></book><book><title>B</title><author>Jones</author></book></library>\0";
3057 let out = run_transform(xsl, src);
3058 assert!(out.contains("<k>A</k>"), "key() wrong: {}", out);
3059 }
3060 }
3061
3062 #[test]
3063 fn test_xslt_call_template_with_params() {
3064 unsafe {
3069 let xsl = b"<?xml version=\"1.0\"?>\
3070 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3071 <xsl:template match=\"/\">\
3072 <out>\
3073 <xsl:call-template name=\"greet\">\
3074 <xsl:with-param name=\"who\" select=\"'World'\"/>\
3075 </xsl:call-template>\
3076 </out>\
3077 </xsl:template>\
3078 <xsl:template name=\"greet\">\
3079 <xsl:param name=\"who\" select=\"'nobody'\"/>\
3080 <g>Hello <xsl:value-of select=\"$who\"/>!</g>\
3081 </xsl:template>\
3082 </xsl:stylesheet>\0";
3083 let src = b"<?xml version=\"1.0\"?><root/>\0";
3084 let out = run_transform(xsl, src);
3085 assert!(out.contains("Hello World"), "with-param lost: {}", out);
3086 }
3087 }
3088
3089 #[test]
3090 fn test_xslt_html_method_meta_charset() {
3091 unsafe {
3094 let xsl = b"<?xml version=\"1.0\"?>\
3095 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3096 <xsl:output method=\"html\" indent=\"yes\"/>\
3097 <xsl:template match=\"/\">\
3098 <html><head><title>T</title></head><body><p>x</p></body></html>\
3099 </xsl:template>\
3100 </xsl:stylesheet>\0";
3101 let src = b"<?xml version=\"1.0\"?><root/>\0";
3102 let out = run_transform(xsl, src);
3103 assert!(
3104 out.contains("<meta charset=\"UTF-8\">"),
3105 "meta charset missing: {}",
3106 out
3107 );
3108 assert!(!out.contains(" <head>"), "unexpected indent: {}", out);
3109 }
3110 }
3111
3112 #[test]
3113 fn test_xslt_if() {
3114 unsafe {
3115 let xsl = b"<?xml version=\"1.0\"?>\
3116 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3117 <xsl:template match=\"/\">\
3118 <xsl:if test=\"/root/item = 'yes'\"><yes/></xsl:if>\
3119 <xsl:if test=\"/root/item = 'no'\"><no/></xsl:if>\
3120 </xsl:template>\
3121 </xsl:stylesheet>\0";
3122 let src = b"<?xml version=\"1.0\"?><root><item>yes</item></root>\0";
3123 let out = run_transform(xsl, src);
3124 assert!(out.contains("<yes/>"), "got: {}", out);
3125 assert!(!out.contains("<no/>"), "got: {}", out);
3126 }
3127 }
3128
3129 #[test]
3130 fn test_xslt_choose() {
3131 unsafe {
3132 let xsl = b"<?xml version=\"1.0\"?>\
3133 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3134 <xsl:template match=\"/\">\
3135 <xsl:choose>\
3136 <xsl:when test=\"/root/item = 'a'\"><chosen>a</chosen></xsl:when>\
3137 <xsl:when test=\"/root/item = 'b'\"><chosen>b</chosen></xsl:when>\
3138 <xsl:otherwise><chosen>other</chosen></xsl:otherwise>\
3139 </xsl:choose>\
3140 </xsl:template>\
3141 </xsl:stylesheet>\0";
3142 let src = b"<?xml version=\"1.0\"?><root><item>b</item></root>\0";
3143 let out = run_transform(xsl, src);
3144 assert!(out.contains("<chosen>b</chosen>"), "got: {}", out);
3145 }
3146 }
3147
3148 #[test]
3149 fn test_xslt_variable_and_call_template() {
3150 unsafe {
3151 let xsl = b"<?xml version=\"1.0\"?>\
3152 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3153 <xsl:variable name=\"greeting\" select=\"'Hello'\"/>\
3154 <xsl:template match=\"/\">\
3155 <xsl:call-template name=\"say\"/>\
3156 </xsl:template>\
3157 <xsl:template name=\"say\">\
3158 <msg><xsl:value-of select=\"$greeting\"/> <xsl:value-of select=\"/root/name\"/></msg>\
3159 </xsl:template>\
3160 </xsl:stylesheet>\0";
3161 let src = b"<?xml version=\"1.0\"?><root><name>World</name></root>\0";
3162 let out = run_transform(xsl, src);
3163 assert!(out.contains("HelloWorld"), "got: {}", out);
3168 }
3169 }
3170
3171 #[test]
3172 fn test_xslt_text_preserves_whitespace() {
3173 unsafe {
3174 let xsl = b"<?xml version=\"1.0\"?>\
3175 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3176 <xsl:template match=\"/\">\
3177 <msg><xsl:value-of select=\"'Hello'\"/><xsl:text> </xsl:text><xsl:value-of select=\"/root/name\"/></msg>\
3178 </xsl:template>\
3179 </xsl:stylesheet>\0";
3180 let src = b"<?xml version=\"1.0\"?><root><name>World</name></root>\0";
3181 let out = run_transform(xsl, src);
3182 assert!(out.contains("Hello World"), "got: {}", out);
3184 }
3185 }
3186
3187 #[test]
3188 fn test_xslt_element_and_attribute() {
3189 unsafe {
3190 let xsl = b"<?xml version=\"1.0\"?>\
3191 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3192 <xsl:template match=\"/\">\
3193 <xsl:element name=\"custom\">\
3194 <xsl:attribute name=\"attr\">value</xsl:attribute>\
3195 <xsl:value-of select=\"/root/item\"/>\
3196 </xsl:element>\
3197 </xsl:template>\
3198 </xsl:stylesheet>\0";
3199 let src = b"<?xml version=\"1.0\"?><root><item>data</item></root>\0";
3200 let out = run_transform(xsl, src);
3201 assert!(out.contains("custom"), "got: {}", out);
3202 assert!(out.contains("attr=\"value\""), "got: {}", out);
3203 assert!(out.contains("data"), "got: {}", out);
3204 }
3205 }
3206
3207 #[test]
3208 fn test_xslt_apply_templates_with_select() {
3209 unsafe {
3210 let xsl = b"<?xml version=\"1.0\"?>\
3211 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3212 <xsl:template match=\"/\">\
3213 <out><xsl:apply-templates select=\"/root/item\"/></out>\
3214 </xsl:template>\
3215 <xsl:template match=\"item\"><item><xsl:value-of select=\".\"/></item></xsl:template>\
3216 </xsl:stylesheet>\0";
3217 let src = b"<?xml version=\"1.0\"?><root><item>alpha</item><item>beta</item></root>\0";
3218 let out = run_transform(xsl, src);
3219 assert!(out.contains("<item>alpha</item>"), "got: {}", out);
3220 assert!(out.contains("<item>beta</item>"), "got: {}", out);
3221 }
3222 }
3223
3224 #[test]
3225 fn test_xslt_text_and_comment_and_pi() {
3226 unsafe {
3227 let xsl = b"<?xml version=\"1.0\"?>\
3228 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3229 <xsl:template match=\"/\">\
3230 <xsl:text>plain</xsl:text>\
3231 <xsl:comment>a comment</xsl:comment>\
3232 <xsl:processing-instruction name=\"target\">pi-data</xsl:processing-instruction>\
3233 </xsl:template>\
3234 </xsl:stylesheet>\0";
3235 let src = b"<?xml version=\"1.0\"?><root/>\0";
3236 let out = run_transform(xsl, src);
3237 assert!(out.contains("plain"), "got: {}", out);
3238 assert!(out.contains("<!--a comment-->"), "got: {}", out);
3239 assert!(out.contains("<?target pi-data?>"), "got: {}", out);
3240 }
3241 }
3242
3243 #[test]
3244 fn test_xslt_copy_and_copy_of() {
3245 unsafe {
3246 let xsl = b"<?xml version=\"1.0\"?>\
3247 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3248 <xsl:template match=\"/\">\
3249 <xsl:copy-of select=\"/root/item\"/>\
3250 <xsl:copy-of select=\"'literal'\"/>\
3251 </xsl:template>\
3252 </xsl:stylesheet>\0";
3253 let src = b"<?xml version=\"1.0\"?><root><item>copied</item></root>\0";
3254 let out = run_transform(xsl, src);
3255 assert!(out.contains("<item>copied</item>"), "got: {}", out);
3256 assert!(out.contains("literal"), "got: {}", out);
3257 }
3258 }
3259
3260 #[test]
3261 fn test_xslt_number() {
3262 unsafe {
3263 let xsl = b"<?xml version=\"1.0\"?>\
3264 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3265 <xsl:template match=\"/\">\
3266 <n><xsl:number value=\"42\"/></n>\
3267 <r><xsl:number value=\"9\" format=\"I\"/></r>\
3268 </xsl:template>\
3269 </xsl:stylesheet>\0";
3270 let src = b"<?xml version=\"1.0\"?><root/>\0";
3271 let out = run_transform(xsl, src);
3272 assert!(out.contains("<n>42</n>"), "got: {}", out);
3273 assert!(out.contains("<r>IX</r>"), "got: {}", out);
3274 }
3275 }
3276}