1use crate::abi::allocator::xmlFree;
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]
57pub static mut xsltMaxDepth: c_int = 3000;
58
59#[no_mangle]
61pub static mut xsltMaxVars: c_int = 15000;
62
63static mut XSLT_XINCLUDE_DEFAULT: c_int = 0;
66
67#[no_mangle]
74pub unsafe extern "C" fn xsltSetXIncludeDefault(xinclude: c_int) {
75 unsafe { XSLT_XINCLUDE_DEFAULT = if xinclude != 0 { 1 } else { 0 } };
76}
77
78#[no_mangle]
80pub unsafe extern "C" fn xsltGetXIncludeDefault() -> c_int {
81 unsafe { XSLT_XINCLUDE_DEFAULT }
82}
83
84#[no_mangle]
91pub unsafe extern "C" fn xsltNewTransformContext(
92 style: *mut _xsltStylesheet,
93 doc: *mut _xmlDoc,
94) -> *mut _xsltTransformContext {
95 if style.is_null() {
96 return ptr::null_mut();
97 }
98 let ctxt = libc::calloc(1, core::mem::size_of::<_xsltTransformContext>())
99 as *mut _xsltTransformContext;
100 if ctxt.is_null() {
101 return ptr::null_mut();
102 }
103 (*ctxt).style = style;
104 (*ctxt).document = doc;
105 (*ctxt).state = XSLT_STATE_OK;
106 (*ctxt).maxDepth = unsafe { xsltMaxDepth };
110 (*ctxt).maxInsertDepth = XSLT_MAX_INSERT_DEPTH;
111 (*ctxt).profile = ptr::null_mut();
112
113 let xpath_ctxt = xmlXPathNewContext(doc);
115 if !xpath_ctxt.is_null() {
116 (*ctxt).xpathCtxt = xpath_ctxt;
117 let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
120 if !internal.is_null() {
121 (*internal).func_lookup_data = ctxt as *mut c_void;
122 }
123 register_xslt_functions(ctxt);
126 }
127
128 if (*style).secPrefs.is_null() {
130 (*ctxt).secPrefs = crate::xslt::security::xsltGetDefaultSecurityPrefs();
131 } else {
132 (*ctxt).secPrefs = (*style).secPrefs;
133 }
134 ctxt
135}
136
137#[no_mangle]
144pub unsafe extern "C" fn xsltFreeTransformContext(ctxt: *mut _xsltTransformContext) {
145 if ctxt.is_null() {
146 return;
147 }
148 if !(*ctxt).xpathCtxt.is_null() {
150 xmlXPathFreeContext((*ctxt).xpathCtxt);
151 }
152 crate::xslt::variables::xsltFreeGlobalVariables(ctxt);
158 crate::xslt::keys::xsltFreeKeyTables(ctxt);
160 crate::xslt::documents::xsltFreeDocCache(ctxt);
162 crate::xslt::extensions::xsltFreeExts(ctxt);
164 if !(*ctxt).varsTab.is_null() {
166 libc::free((*ctxt).varsTab as *mut libc::c_void);
167 }
168 if !(*ctxt).paramsTab.is_null() {
169 libc::free((*ctxt).paramsTab as *mut libc::c_void);
170 }
171 if !(*ctxt).templTab.is_null() {
172 libc::free((*ctxt).templTab as *mut libc::c_void);
173 }
174 libc::free(ctxt as *mut libc::c_void);
175}
176
177#[no_mangle]
187pub unsafe extern "C" fn xsltApplyStylesheet(
188 style: *mut _xsltStylesheet,
189 doc: *mut _xmlDoc,
190 params: *mut *const c_char,
191) -> *mut _xmlDoc {
192 xsltApplyStylesheetUser(
193 style,
194 doc,
195 params,
196 ptr::null(),
197 ptr::null_mut(),
198 ptr::null_mut(),
199 )
200}
201
202#[no_mangle]
208pub unsafe extern "C" fn xsltApplyStylesheetUser(
209 style: *mut _xsltStylesheet,
210 doc: *mut _xmlDoc,
211 params: *mut *const c_char,
212 _output: *const c_char,
213 _profile: *mut c_void,
214 userCtxt: *mut _xsltTransformContext,
215) -> *mut _xmlDoc {
216 if style.is_null() || doc.is_null() {
217 return ptr::null_mut();
218 }
219
220 let mut ctxt = userCtxt;
222 let mut own_ctxt = false;
223 if ctxt.is_null() {
224 ctxt = xsltNewTransformContext(style, doc);
225 if ctxt.is_null() {
226 return ptr::null_mut();
227 }
228 own_ctxt = true;
229 }
230
231 if !params.is_null() {
233 crate::xslt::parameters::xsltParseStylesheetParams(style, params);
234 }
235
236 crate::xslt::variables::xsltInitGlobalVariables(ctxt);
238
239 crate::xslt::keys::xsltInitKeys(ctxt, style);
241
242 crate::xslt::whitespace::xsltApplyStripSpaces(style, doc);
244
245 let result = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
247 if result.is_null() {
248 if own_ctxt {
249 xsltFreeTransformContext(ctxt);
250 }
251 return ptr::null_mut();
252 }
253 (*result).type_ = XML_DOCUMENT_NODE as c_int;
254 (*result).version = crate::xml::string::xml_strdup(b"1.0\0".as_ptr() as *const xmlChar);
255 (*result).doc = result;
256 if !(*style).encoding.is_null() {
260 (*result).encoding = crate::xml::string::xml_strdup((*style).encoding);
261 }
262 if !(*style).version.is_null() {
263 let v = crate::xml::string::xml_strdup((*style).version);
264 if !v.is_null() {
265 if !(*result).version.is_null() {
266 libc::free((*result).version as *mut libc::c_void);
267 }
268 (*result).version = v;
269 }
270 }
271
272 (*ctxt).resultDoc = result;
273 (*ctxt).insert = result as *mut _xmlNode;
274
275 (*ctxt).node = doc as *mut _xmlNode;
279 (*ctxt).document = doc;
280 (*ctxt).contextSize = 1;
281 (*ctxt).proximityPosition = 1;
282 let result_code = apply_templates_to_node(ctxt, doc as *mut _xmlNode, ptr::null());
283 let _ = result_code;
284
285 let final_result = if (*ctxt).state == XSLT_STATE_OK {
289 result
290 } else {
291 free_doc(result);
292 ptr::null_mut()
293 };
294
295 if own_ctxt {
296 (*ctxt).resultDoc = ptr::null_mut();
298 xsltFreeTransformContext(ctxt);
299 }
300 final_result
301}
302
303#[no_mangle]
309pub unsafe extern "C" fn xsltApplyStylesheetStacked(
310 style: *mut _xsltStylesheet,
311 doc: *mut _xmlDoc,
312 params: *mut *const c_char,
313 _stack: *mut c_void,
314) -> *mut _xmlDoc {
315 xsltApplyStylesheet(style, doc, params)
316}
317
318#[no_mangle]
324pub unsafe extern "C" fn xsltFreeTransformResult(result: *mut _xmlDoc) {
325 if !result.is_null() {
326 free_doc(result);
327 }
328}
329
330#[no_mangle]
342pub unsafe extern "C" fn xsltRunStylesheetUser(
343 style: *mut _xsltStylesheet,
344 doc: *mut _xmlDoc,
345 params: *mut *const c_char,
346 output: *const c_char,
347 SAX: *mut crate::abi::structs::_xmlSAXHandler,
348 IObuf: *mut crate::abi::structs::_xmlOutputBuffer,
349 profile: *mut c_void,
350 userCtxt: *mut _xsltTransformContext,
351) -> c_int {
352 if output.is_null() && SAX.is_null() && IObuf.is_null() {
353 return -1;
354 }
355 if !SAX.is_null() && !IObuf.is_null() {
356 return -1;
357 }
358 if !SAX.is_null() {
360 return -1;
361 }
362 let tmp = xsltApplyStylesheetUser(style, doc, params, output, profile, userCtxt);
363 if tmp.is_null() {
364 eprintln!("xsltRunStylesheet : run failed");
365 return -1;
366 }
367 let ret = if !IObuf.is_null() {
368 let mut txt: *mut xmlChar = ptr::null_mut();
369 let mut len: c_int = 0;
370 let r = crate::xslt::serialization::xsltSaveResultToString(&mut txt, &mut len, tmp, style);
371 if r != 0 || txt.is_null() {
372 -1
373 } else {
374 let written = crate::xml::io::output_buffer_write(IObuf, len, txt as *const c_char);
375 crate::abi::allocator::xmlFree(txt as *mut c_void);
376 written
377 }
378 } else {
379 crate::xslt::serialization::xsltSaveResultToFilename(output, tmp, style, 0)
380 };
381 free_doc(tmp);
382 ret
383}
384
385#[no_mangle]
391pub unsafe extern "C" fn xsltRunStylesheet(
392 style: *mut _xsltStylesheet,
393 doc: *mut _xmlDoc,
394 params: *mut *const c_char,
395 output: *const c_char,
396 SAX: *mut crate::abi::structs::_xmlSAXHandler,
397 IObuf: *mut crate::abi::structs::_xmlOutputBuffer,
398) -> c_int {
399 xsltRunStylesheetUser(
400 style,
401 doc,
402 params,
403 output,
404 SAX,
405 IObuf,
406 ptr::null_mut(),
407 ptr::null_mut(),
408 )
409}
410
411unsafe fn apply_root_template(ctxt: *mut _xsltTransformContext, doc: *mut _xmlDoc) -> c_int {
417 let style = (*ctxt).style;
419 if style.is_null() {
420 return -1;
421 }
422 let doc_node = doc as *mut _xmlNode;
424 (*ctxt).node = doc_node;
425 (*ctxt).contextSize = 1;
426 (*ctxt).proximityPosition = 1;
427 let templ = crate::xslt::templates::xsltFindTemplate(style, doc_node, ptr::null());
428 if templ.is_null() {
429 return 0;
431 }
432 let mut vars_base = (*ctxt).varsNr;
434 let _ = &mut vars_base;
435 (*ctxt).templ = templ;
436 execute_content(ctxt, (*templ).content);
437 (*ctxt).templ = ptr::null_mut();
438 0
439}
440
441unsafe fn apply_templates_to_node(
447 ctxt: *mut _xsltTransformContext,
448 node: *mut _xmlNode,
449 mode: *const xmlChar,
450) -> c_int {
451 let style = (*ctxt).style;
452 if style.is_null() {
453 return -1;
454 }
455 let templ = crate::xslt::templates::xsltFindTemplate(style, node, mode);
456 if templ.is_null() {
457 let typ = (*node).type_;
463 if typ == XML_TEXT_NODE as c_int
464 || typ == XML_CDATA_SECTION_NODE as c_int
465 || typ == XML_ATTRIBUTE_NODE as c_int
466 {
467 let content = node_get_content(node);
468 if !content.is_null() {
469 append_text_node(ctxt, content);
470 libc::free(content as *mut libc::c_void);
471 }
472 } else if typ == XML_ELEMENT_NODE as c_int
473 || typ == XML_DOCUMENT_NODE as c_int
474 || typ == XML_HTML_DOCUMENT_NODE as c_int
475 {
476 apply_templates_to_children(ctxt, node, mode);
478 }
479 return 0;
480 }
481 if (*ctxt).depth >= (*ctxt).maxDepth {
483 return -1;
484 }
485 (*ctxt).depth += 1;
486 (*ctxt).templ = templ;
487 (*ctxt).node = node;
488 (*ctxt).contextSize = 1;
489 (*ctxt).proximityPosition = 1;
490 execute_content(ctxt, (*templ).content);
491 (*ctxt).depth -= 1;
492 (*ctxt).templ = ptr::null_mut();
493 0
494}
495
496unsafe fn apply_templates_to_children(
502 ctxt: *mut _xsltTransformContext,
503 node: *mut _xmlNode,
504 mode: *const xmlChar,
505) -> c_int {
506 let mut children: Vec<*mut _xmlNode> = Vec::new();
508 let mut child = (*node).children;
509 while !child.is_null() {
510 children.push(child);
511 child = (*child).next;
512 }
513 let size = children.len();
516 (*ctxt).contextSize = size as c_int;
517 for (i, node) in children.iter().enumerate() {
518 (*ctxt).node = *node;
519 (*ctxt).proximityPosition = (i + 1) as c_int;
520 apply_templates_to_node(ctxt, *node, mode);
521 }
522 0
523}
524
525pub unsafe fn execute_content(ctxt: *mut _xsltTransformContext, content: *mut _xmlNode) -> c_int {
531 let mut cur = content;
532 while !cur.is_null() {
533 let next = (*cur).next;
534 xsltProcessInstruction(ctxt, cur);
535 if (*ctxt).state == XSLT_STATE_ERROR {
536 return -1;
537 }
538 cur = next;
539 }
540 0
541}
542
543pub unsafe fn xsltProcessInstruction(
550 ctxt: *mut _xsltTransformContext,
551 inst: *mut _xmlNode,
552) -> c_int {
553 if ctxt.is_null() || inst.is_null() {
554 return -1;
555 }
556 let typ = (*inst).type_;
557 match typ {
558 t if t == XML_TEXT_NODE as c_int || t == XML_CDATA_SECTION_NODE as c_int => {
559 if !(*inst).content.is_null() {
561 append_text_node(ctxt, (*inst).content);
562 }
563 0
564 }
565 t if t == XML_COMMENT_NODE as c_int => {
566 if !(*inst).content.is_null() {
568 append_comment_node(ctxt, (*inst).content);
569 }
570 0
571 }
572 t if t == XML_PI_NODE as c_int => {
573 if !(*inst).name.is_null() {
575 let content = if (*inst).content.is_null() {
576 ptr::null()
577 } else {
578 (*inst).content
579 };
580 append_pi_node(ctxt, (*inst).name, content);
581 }
582 0
583 }
584 t if t == XML_ELEMENT_NODE as c_int => {
585 if is_xslt_namespace(inst) {
586 process_xslt_instruction(ctxt, inst);
587 } else {
588 process_literal_element(ctxt, inst);
591 }
592 0
593 }
594 _ => 0,
595 }
596}
597
598unsafe fn process_xslt_instruction(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) -> c_int {
604 let name = get_element_name(inst);
605 match name.as_deref() {
606 Some("apply-templates") => {
607 process_apply_templates(ctxt, inst);
608 }
609 Some("call-template") => {
610 process_call_template(ctxt, inst);
611 }
612 Some("apply-imports") => {
613 process_apply_imports(ctxt, inst);
614 }
615 Some("for-each") => {
616 process_for_each(ctxt, inst);
617 }
618 Some("value-of") => {
619 process_value_of(ctxt, inst);
620 }
621 Some("copy-of") => {
622 process_copy_of(ctxt, inst);
623 }
624 Some("copy") => {
625 process_copy(ctxt, inst);
626 }
627 Some("element") => {
628 process_element(ctxt, inst);
629 }
630 Some("attribute") => {
631 process_attribute(ctxt, inst);
632 }
633 Some("text") => {
634 process_text(ctxt, inst);
635 }
636 Some("comment") => {
637 process_comment(ctxt, inst);
638 }
639 Some("processing-instruction") => {
640 process_pi(ctxt, inst);
641 }
642 Some("number") => {
643 process_number(ctxt, inst);
644 }
645 Some("choose") => {
646 process_choose(ctxt, inst);
647 }
648 Some("when") | Some("otherwise") => {
649 }
651 Some("if") => {
652 process_if(ctxt, inst);
653 }
654 Some("variable") => {
655 process_variable(ctxt, inst);
656 }
657 Some("param") => {
658 process_param(ctxt, inst);
659 }
660 Some("with-param") => {
661 }
663 Some("sort") => {
664 }
666 Some("message") => {
667 process_message(ctxt, inst);
668 }
669 Some("fallback") => {
670 }
672 Some("output")
673 | Some("decimal-format")
674 | Some("namespace-alias")
675 | Some("attribute-set")
676 | Some("key")
677 | Some("strip-space")
678 | Some("preserve-space")
679 | Some("import")
680 | Some("include")
681 | Some("stylesheet")
682 | Some("transform") => {
683 }
685 _ => {
686 let ns = get_element_ns(inst);
689 if let Some(ns_uri) = ns {
690 if ns_uri == crate::exslt::EXSLT_NS_COMMON
692 && get_element_name(inst).as_deref() == Some("document")
693 {
694 process_exsl_document(ctxt, inst);
695 return 0;
696 }
697 let name_ptr = (*inst).name;
699 let ns_cstr = str_to_cstr(&ns_uri);
700 let found = crate::xslt::extensions::xsltFindExtElement(
701 ctxt,
702 name_ptr,
703 ns_cstr.as_ptr() as *const xmlChar,
704 );
705 if !found.is_null() {
706 return 0;
708 }
709 }
710 }
712 }
713 0
714}
715
716fn str_to_cstr(s: &str) -> Vec<u8> {
718 let mut v = s.as_bytes().to_vec();
719 v.push(0);
720 v
721}
722
723unsafe fn process_exsl_document(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
738 let href = get_prop(inst, b"href\0".as_ptr() as *const xmlChar);
739 if href.is_null() {
740 crate::xslt::errors::xsltTransformError(
741 ctxt,
742 (*ctxt).style,
743 inst,
744 b"exsl:document: missing href attribute\0".as_ptr() as *const c_char,
745 );
746 return;
747 }
748 let frag = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
750 if frag.is_null() {
751 libc::free(href as *mut libc::c_void);
752 return;
753 }
754 (*frag).type_ = XML_DOCUMENT_NODE as c_int;
755 (*frag).doc = frag;
756 let saved_insert = (*ctxt).insert;
757 let saved_result = (*ctxt).resultDoc;
758 (*ctxt).insert = frag as *mut _xmlNode;
759 (*ctxt).resultDoc = frag;
760 execute_content(ctxt, (*inst).children);
761 (*ctxt).insert = saved_insert;
762 (*ctxt).resultDoc = saved_result;
763
764 let fname = crate::abi::versioning::c_str_to_bytes(href as *const c_char);
766 if let Some(name) = fname {
767 let path = String::from_utf8_lossy(name);
768 let cpath = str_to_cstr(&path);
769 let out = libc::fopen(
770 cpath.as_ptr() as *const c_char,
771 b"wb\0".as_ptr() as *const c_char,
772 );
773 if !out.is_null() {
774 let buf = crate::xml::io::buf_create(-1);
775 if !buf.is_null() {
776 crate::xml::tree::doc_dump(buf, frag);
777 let content = crate::xml::io::buf_content(buf);
778 let len = crate::xml::io::buf_length(buf);
779 if !content.is_null() && len > 0 {
780 libc::fwrite(content as *const libc::c_void, 1, len as usize, out);
781 }
782 crate::xml::io::buf_free(buf);
783 }
784 libc::fclose(out);
785 }
786 }
787 libc::free(href as *mut libc::c_void);
788 free_doc(frag);
789}
790
791unsafe fn eval_xpath(
798 ctxt: *mut _xsltTransformContext,
799 expr: *const xmlChar,
800) -> *mut _xmlXPathObject {
801 if ctxt.is_null() || expr.is_null() {
802 return ptr::null_mut();
803 }
804 let xpath_ctxt = (*ctxt).xpathCtxt;
805 if xpath_ctxt.is_null() {
806 return ptr::null_mut();
807 }
808 (*xpath_ctxt).node = (*ctxt).node;
812 (*xpath_ctxt).doc = (*ctxt).document;
813 (*xpath_ctxt).contextSize = (*ctxt).contextSize;
814 (*xpath_ctxt).proximityPosition = (*ctxt).proximityPosition;
815 let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
816 if !internal.is_null() {
817 (*internal).context_node = (*ctxt).node;
818 (*internal).document = (*ctxt).document;
819 (*internal).context_size = (*ctxt).contextSize;
820 (*internal).context_position = (*ctxt).proximityPosition;
821 (*internal).proximity_position = (*ctxt).proximityPosition;
822 }
823 xmlXPathEvalExpression(expr, xpath_ctxt)
824}
825
826unsafe fn process_apply_templates(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
832 let mode = get_prop(inst, b"mode\0".as_ptr() as *const xmlChar);
834 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
836 let params = collect_with_params(ctxt, inst);
838
839 let obj = if !select.is_null() {
840 eval_xpath(ctxt, select)
841 } else {
842 build_child_node_set(ctxt)
845 };
846 if !select.is_null() {
847 libc::free(select as *mut libc::c_void);
848 }
849 if obj.is_null() {
850 if !mode.is_null() {
851 libc::free(mode as *mut libc::c_void);
852 }
853 return;
854 }
855
856 let nodes = if (*obj).type_ == xmlXPathObjectType::XPATH_NODESET as c_int {
858 (*obj).nodesetval as *mut _xmlNodeSet
859 } else {
860 ptr::null_mut()
861 };
862
863 if !nodes.is_null() && (*nodes).nodeNr > 0 {
864 let sort = find_sort_children(ctxt, inst);
866 let mut node_ptrs: Vec<*mut _xmlNode> = Vec::new();
867 let mut i = 0;
868 while i < (*nodes).nodeNr {
869 let n = *(*nodes).nodeTab.offset(i as isize);
870 if !n.is_null() {
871 node_ptrs.push(n);
872 }
873 i += 1;
874 }
875 if !sort.is_null() {
877 let mut sorted =
878 libc::calloc(1, core::mem::size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
879 if !sorted.is_null() {
880 (*sorted).nodeNr = node_ptrs.len() as c_int;
881 (*sorted).nodeMax = node_ptrs.len() as c_int;
882 let tab = libc::malloc(node_ptrs.len() * core::mem::size_of::<*mut _xmlNode>())
883 as *mut *mut _xmlNode;
884 (*sorted).nodeTab = tab;
885 for (idx, n) in node_ptrs.iter().enumerate() {
886 if !tab.is_null() {
887 *tab.offset(idx as isize) = *n;
888 }
889 }
890 crate::xslt::sorting::xsltSortNodeSet(ctxt, sorted, sort);
891 let mut k = 0;
893 while k < (*sorted).nodeNr {
894 let n = *(*sorted).nodeTab.offset(k as isize);
895 if !n.is_null() {
896 (*ctxt).node = n;
897 (*ctxt).contextSize = (*sorted).nodeNr;
898 (*ctxt).proximityPosition = k + 1;
899 apply_templates_with_params(ctxt, n, mode, params);
900 }
901 k += 1;
902 }
903 libc::free((*sorted).nodeTab as *mut libc::c_void);
904 libc::free(sorted as *mut libc::c_void);
905 }
906 } else {
907 (*ctxt).contextSize = node_ptrs.len() as c_int;
908 for (i, n) in node_ptrs.iter().enumerate() {
909 if !n.is_null() {
910 (*ctxt).node = *n;
911 (*ctxt).proximityPosition = (i + 1) as c_int;
912 apply_templates_with_params(ctxt, *n, mode, params);
913 }
914 }
915 }
916 }
917
918 if !mode.is_null() {
919 libc::free(mode as *mut libc::c_void);
920 }
921 xmlXPathFreeObject(obj);
922}
923
924unsafe fn apply_templates_with_params(
930 ctxt: *mut _xsltTransformContext,
931 node: *mut _xmlNode,
932 mode: *const xmlChar,
933 params: *mut _xsltStackElem,
934) {
935 let mut p = params;
937 while !p.is_null() {
938 crate::xslt::parameters::xsltPushParam(ctxt, p);
939 p = (*p).next;
940 }
941 apply_templates_to_node(ctxt, node, mode);
942 let mut p = params;
944 while !p.is_null() {
945 crate::xslt::parameters::xsltPopParam(ctxt);
946 p = (*p).next;
947 }
948}
949
950unsafe fn build_child_node_set(ctxt: *mut _xsltTransformContext) -> *mut _xmlXPathObject {
956 let ns = xmlXPathNodeSetCreate(ptr::null_mut());
957 if ns.is_null() {
958 return ptr::null_mut();
959 }
960 let obj = xmlMalloc_zero_obj();
961 if obj.is_null() {
962 libc::free(ns as *mut libc::c_void);
963 return ptr::null_mut();
964 }
965 (*obj).type_ = xmlXPathObjectType::XPATH_NODESET as c_int;
966 (*obj).nodesetval = ns as *mut c_void;
967 let node = (*ctxt).node;
968 if !node.is_null() {
969 let mut child = (*node).children;
970 while !child.is_null() {
971 append_to_node_set(ns, child);
972 child = (*child).next;
973 }
974 }
975 obj
976}
977
978unsafe fn xmlMalloc_zero_obj() -> *mut _xmlXPathObject {
980 libc::calloc(1, core::mem::size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject
981}
982
983unsafe fn append_to_node_set(ns: *mut _xmlNodeSet, node: *mut _xmlNode) {
989 if ns.is_null() || node.is_null() {
990 return;
991 }
992 let mut i = 0;
994 while i < (*ns).nodeNr {
995 if *(*ns).nodeTab.offset(i as isize) == node {
996 return;
997 }
998 i += 1;
999 }
1000 if (*ns).nodeNr >= (*ns).nodeMax {
1001 let new_max = if (*ns).nodeMax == 0 {
1002 8
1003 } else {
1004 (*ns).nodeMax * 2
1005 };
1006 let new_tab = libc::realloc(
1007 (*ns).nodeTab as *mut libc::c_void,
1008 (new_max as usize) * core::mem::size_of::<*mut _xmlNode>(),
1009 ) as *mut *mut _xmlNode;
1010 if new_tab.is_null() {
1011 return;
1012 }
1013 (*ns).nodeTab = new_tab;
1014 (*ns).nodeMax = new_max;
1015 }
1016 *(*ns).nodeTab.offset((*ns).nodeNr as isize) = node;
1017 (*ns).nodeNr += 1;
1018}
1019
1020unsafe fn collect_with_params(
1028 ctxt: *mut _xsltTransformContext,
1029 inst: *mut _xmlNode,
1030) -> *mut _xsltStackElem {
1031 let mut head: *mut _xsltStackElem = ptr::null_mut();
1032 let mut tail: *mut _xsltStackElem = ptr::null_mut();
1033 let mut child = (*inst).children;
1034 while !child.is_null() {
1035 let next = (*child).next;
1036 if is_xslt_element(child, "with-param") {
1037 let param = evaluate_with_param(ctxt, child);
1038 if !param.is_null() {
1039 (*param).next = ptr::null_mut();
1040 if tail.is_null() {
1041 head = param;
1042 tail = param;
1043 } else {
1044 (*tail).next = param;
1045 tail = param;
1046 }
1047 }
1048 }
1049 child = next;
1050 }
1051 head
1052}
1053
1054unsafe fn evaluate_with_param(
1060 ctxt: *mut _xsltTransformContext,
1061 inst: *mut _xmlNode,
1062) -> *mut _xsltStackElem {
1063 let name = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1064 if name.is_null() {
1065 return ptr::null_mut();
1066 }
1067 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
1068 let param = libc::calloc(1, core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
1069 if param.is_null() {
1070 libc::free(name as *mut libc::c_void);
1071 if !select.is_null() {
1072 libc::free(select as *mut libc::c_void);
1073 }
1074 return ptr::null_mut();
1075 }
1076 (*param).name = name;
1077 (*param).flags = 2 | 4; if !select.is_null() {
1079 let obj = eval_xpath(ctxt, select);
1080 if !obj.is_null() {
1081 (*param).value = obj;
1082 }
1083 libc::free(select as *mut libc::c_void);
1084 } else {
1085 let value = eval_content_fragment(ctxt, (*inst).children);
1087 if !value.is_null() {
1088 (*param).value = value;
1089 }
1090 }
1091 param
1092}
1093
1094unsafe fn eval_content_fragment(
1100 ctxt: *mut _xsltTransformContext,
1101 content: *mut _xmlNode,
1102) -> *mut _xmlXPathObject {
1103 let frag = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
1104 if frag.is_null() {
1105 return ptr::null_mut();
1106 }
1107 (*frag).type_ = XML_DOCUMENT_NODE as c_int;
1108 (*frag).doc = frag;
1109 let saved_insert = (*ctxt).insert;
1111 let saved_result = (*ctxt).resultDoc;
1112 (*ctxt).insert = frag as *mut _xmlNode;
1113 (*ctxt).resultDoc = frag;
1114 execute_content(ctxt, content);
1115 (*ctxt).insert = saved_insert;
1116 (*ctxt).resultDoc = saved_result;
1117
1118 let obj = xmlMalloc_zero_obj();
1119 if obj.is_null() {
1120 free_doc(frag);
1121 return ptr::null_mut();
1122 }
1123 (*obj).type_ = xmlXPathObjectType::XPATH_XSLT_TREE as c_int;
1124 (*obj).nodesetval = frag as *mut c_void;
1125 obj
1126}
1127
1128unsafe fn process_call_template(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1134 let name = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1135 if name.is_null() {
1136 return;
1137 }
1138 let style = (*ctxt).style;
1139 let templ = crate::xslt::templates::xsltLookupTemplate(style, name);
1140 libc::free(name as *mut libc::c_void);
1141 if templ.is_null() {
1142 return;
1143 }
1144 if (*ctxt).depth >= (*ctxt).maxDepth {
1145 return;
1146 }
1147 (*ctxt).depth += 1;
1148 let params = collect_with_params(ctxt, inst);
1150 let mut p = params;
1151 while !p.is_null() {
1152 crate::xslt::parameters::xsltPushParam(ctxt, p);
1153 p = (*p).next;
1154 }
1155 let saved_templ = (*ctxt).templ;
1156 (*ctxt).templ = templ;
1157 execute_content(ctxt, (*templ).content);
1158 (*ctxt).templ = saved_templ;
1159 let mut p = params;
1160 while !p.is_null() {
1161 crate::xslt::parameters::xsltPopParam(ctxt);
1162 p = (*p).next;
1163 }
1164 (*ctxt).depth -= 1;
1165}
1166
1167unsafe fn process_apply_imports(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1177 let _ = inst;
1178 let style = (*ctxt).style;
1179 let node = (*ctxt).node;
1180 let current_templ = (*ctxt).templ;
1181 if style.is_null() || node.is_null() || current_templ.is_null() {
1182 return;
1183 }
1184 let current_depth = (*current_templ).depth;
1188 let mode = (*current_templ).mode;
1189
1190 let mut best: *mut _xsltTemplate = ptr::null_mut();
1191 let mut best_priority: f64 = f64::NEG_INFINITY;
1192 let mut best_depth: c_int = -1;
1193
1194 let mut templ = (*style).templates;
1195 while !templ.is_null() {
1196 if (*templ).depth <= current_depth {
1198 templ = (*templ).next;
1199 continue;
1200 }
1201 if !(*templ).mode.is_null() {
1203 if mode.is_null()
1204 || libc::strcmp(
1205 (*templ).mode as *const libc::c_char,
1206 mode as *const libc::c_char,
1207 ) != 0
1208 {
1209 templ = (*templ).next;
1210 continue;
1211 }
1212 } else if !mode.is_null() {
1213 templ = (*templ).next;
1214 continue;
1215 }
1216 let pattern_ptr = (*templ).r#match as *mut crate::xslt::patterns::_xsltPattern;
1218 if pattern_ptr.is_null() {
1219 templ = (*templ).next;
1220 continue;
1221 }
1222 if crate::xslt::patterns::xsltTestPattern(ctxt, pattern_ptr, node) == 0 {
1223 templ = (*templ).next;
1224 continue;
1225 }
1226 let priority = (*templ).priority;
1228 if priority > best_priority || (priority == best_priority && (*templ).depth > best_depth) {
1229 best = templ;
1230 best_priority = priority;
1231 best_depth = (*templ).depth;
1232 }
1233 templ = (*templ).next;
1234 }
1235
1236 if !best.is_null() {
1237 if (*ctxt).depth >= (*ctxt).maxDepth {
1238 return;
1239 }
1240 (*ctxt).depth += 1;
1241 let saved_templ = (*ctxt).templ;
1242 (*ctxt).templ = best;
1243 execute_content(ctxt, (*best).content);
1244 (*ctxt).templ = saved_templ;
1245 (*ctxt).depth -= 1;
1246 }
1247}
1248
1249unsafe fn process_for_each(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1255 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
1256 if select.is_null() {
1257 return;
1258 }
1259 let obj = eval_xpath(ctxt, select);
1260 libc::free(select as *mut libc::c_void);
1261 if obj.is_null() {
1262 return;
1263 }
1264 if (*obj).type_ != xmlXPathObjectType::XPATH_NODESET as c_int {
1265 xmlXPathFreeObject(obj);
1266 return;
1267 }
1268 let nodes = (*obj).nodesetval as *mut _xmlNodeSet;
1269 if nodes.is_null() || (*nodes).nodeNr == 0 {
1270 xmlXPathFreeObject(obj);
1271 return;
1272 }
1273 let sort = find_sort_children(ctxt, inst);
1275 let saved_node = (*ctxt).node;
1277 let saved_size = (*ctxt).contextSize;
1278 let saved_pos = (*ctxt).proximityPosition;
1279
1280 let mut node_ptrs: Vec<*mut _xmlNode> = Vec::new();
1281 let mut i = 0;
1282 while i < (*nodes).nodeNr {
1283 let n = *(*nodes).nodeTab.offset(i as isize);
1284 if !n.is_null() {
1285 node_ptrs.push(n);
1286 }
1287 i += 1;
1288 }
1289
1290 if !sort.is_null() {
1291 let mut sorted = libc::calloc(1, core::mem::size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
1293 if !sorted.is_null() {
1294 (*sorted).nodeNr = node_ptrs.len() as c_int;
1295 (*sorted).nodeMax = node_ptrs.len() as c_int;
1296 let tab = libc::malloc(node_ptrs.len() * core::mem::size_of::<*mut _xmlNode>())
1297 as *mut *mut _xmlNode;
1298 (*sorted).nodeTab = tab;
1299 for (idx, n) in node_ptrs.iter().enumerate() {
1300 if !tab.is_null() {
1301 *tab.offset(idx as isize) = *n;
1302 }
1303 }
1304 crate::xslt::sorting::xsltSortNodeSet(ctxt, sorted, sort);
1305 (*ctxt).contextSize = (*sorted).nodeNr;
1306 let mut k = 0;
1307 while k < (*sorted).nodeNr {
1308 let n = *(*sorted).nodeTab.offset(k as isize);
1309 if !n.is_null() {
1310 (*ctxt).node = n;
1311 (*ctxt).proximityPosition = k + 1;
1312 execute_content(ctxt, (*inst).children);
1313 }
1314 k += 1;
1315 }
1316 libc::free((*sorted).nodeTab as *mut libc::c_void);
1317 libc::free(sorted as *mut libc::c_void);
1318 }
1319 } else {
1320 (*ctxt).contextSize = node_ptrs.len() as c_int;
1321 for (i, n) in node_ptrs.iter().enumerate() {
1322 if !n.is_null() {
1323 (*ctxt).node = *n;
1324 (*ctxt).proximityPosition = (i + 1) as c_int;
1325 execute_content(ctxt, (*inst).children);
1326 }
1327 }
1328 }
1329
1330 (*ctxt).node = saved_node;
1332 (*ctxt).contextSize = saved_size;
1333 (*ctxt).proximityPosition = saved_pos;
1334 xmlXPathFreeObject(obj);
1335}
1336
1337unsafe fn find_sort_children(
1344 ctxt: *mut _xsltTransformContext,
1345 inst: *mut _xmlNode,
1346) -> *mut _xsltSort {
1347 if ctxt.is_null() || inst.is_null() {
1348 return ptr::null_mut();
1349 }
1350 let mut child = (*inst).children;
1351 while !child.is_null() {
1352 if is_xslt_element(child, "sort") {
1353 let style = (*ctxt).style;
1356 let sort = crate::xslt::sorting::xsltCompileSort(style, child);
1357 return sort;
1358 }
1359 child = (*child).next;
1360 }
1361 ptr::null_mut()
1362}
1363
1364unsafe fn process_value_of(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1370 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
1371 if select.is_null() {
1372 return;
1373 }
1374 let obj = eval_xpath(ctxt, select);
1375 libc::free(select as *mut libc::c_void);
1376 if obj.is_null() {
1377 return;
1378 }
1379 let strv = xmlXPathCastToString(obj);
1380 xmlXPathFreeObject(obj);
1381 if !strv.is_null() {
1382 append_text_node(ctxt, strv);
1383 libc::free(strv as *mut libc::c_void);
1384 }
1385}
1386
1387unsafe fn process_copy_of(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1393 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
1394 if select.is_null() {
1395 return;
1396 }
1397 let obj = eval_xpath(ctxt, select);
1398 libc::free(select as *mut libc::c_void);
1399 if obj.is_null() {
1400 return;
1401 }
1402 if (*obj).type_ == xmlXPathObjectType::XPATH_NODESET as c_int {
1403 let nodes = (*obj).nodesetval as *mut _xmlNodeSet;
1404 if !nodes.is_null() {
1405 let mut i = 0;
1406 while i < (*nodes).nodeNr {
1407 let n = *(*nodes).nodeTab.offset(i as isize);
1408 if !n.is_null() {
1409 copy_node_deep(ctxt, n);
1410 }
1411 i += 1;
1412 }
1413 }
1414 } else if (*obj).type_ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
1415 let frag = (*obj).nodesetval as *mut _xmlDoc;
1417 if !frag.is_null() {
1418 let mut child = (*frag).children;
1419 while !child.is_null() {
1420 let next = (*child).next;
1421 copy_node_deep(ctxt, child);
1422 child = next;
1423 }
1424 }
1425 } else {
1426 let strv = xmlXPathCastToString(obj);
1428 if !strv.is_null() {
1429 append_text_node(ctxt, strv);
1430 libc::free(strv as *mut libc::c_void);
1431 }
1432 }
1433 xmlXPathFreeObject(obj);
1434}
1435
1436unsafe fn copy_node_deep(ctxt: *mut _xsltTransformContext, node: *mut _xmlNode) {
1442 if node.is_null() {
1443 return;
1444 }
1445 let typ = (*node).type_;
1446 if typ == XML_TEXT_NODE as c_int || typ == XML_CDATA_SECTION_NODE as c_int {
1447 if !(*node).content.is_null() {
1448 append_text_node(ctxt, (*node).content);
1449 }
1450 } else if typ == XML_COMMENT_NODE as c_int {
1451 if !(*node).content.is_null() {
1452 append_comment_node(ctxt, (*node).content);
1453 }
1454 } else if typ == XML_PI_NODE as c_int {
1455 if !(*node).name.is_null() {
1456 append_pi_node(ctxt, (*node).name, (*node).content);
1457 }
1458 } else if typ == XML_ELEMENT_NODE as c_int {
1459 let name = (*node).name;
1461 let new_elem = new_element_node(ctxt, name, (*node).ns);
1462 if new_elem.is_null() {
1463 return;
1464 }
1465 let mut prop = (*node).properties;
1467 while !prop.is_null() {
1468 let attr_name = (*prop).name;
1469 let attr_val = node_get_content((*prop).children);
1470 if !attr_name.is_null() && !attr_val.is_null() {
1471 set_prop(new_elem, attr_name, attr_val);
1472 libc::free(attr_val as *mut libc::c_void);
1473 }
1474 prop = (*prop).next;
1475 }
1476 let saved_insert = (*ctxt).insert;
1478 (*ctxt).insert = new_elem;
1479 let mut child = (*node).children;
1480 while !child.is_null() {
1481 let next = (*child).next;
1482 copy_node_deep(ctxt, child);
1483 child = next;
1484 }
1485 (*ctxt).insert = saved_insert;
1486 }
1487}
1488
1489unsafe fn new_element_node(
1495 ctxt: *mut _xsltTransformContext,
1496 name: *const xmlChar,
1497 ns: *mut _xmlNs,
1498) -> *mut _xmlNode {
1499 let elem = new_node(ns, name);
1500 if elem.is_null() {
1501 return ptr::null_mut();
1502 }
1503 append_to_result(ctxt, elem);
1504 elem
1505}
1506
1507unsafe fn append_to_result(ctxt: *mut _xsltTransformContext, node: *mut _xmlNode) {
1513 let insert = (*ctxt).insert;
1514 if insert.is_null() {
1515 return;
1516 }
1517 add_child(insert, node);
1519 let doc = if (*insert).type_ == XML_DOCUMENT_NODE as c_int {
1521 insert as *mut _xmlDoc
1522 } else {
1523 (*insert).doc
1524 };
1525 if !doc.is_null() {
1526 set_node_doc(node, doc);
1527 }
1528}
1529
1530unsafe fn set_node_doc(node: *mut _xmlNode, doc: *mut _xmlDoc) {
1536 if node.is_null() {
1537 return;
1538 }
1539 (*node).doc = doc;
1540 let mut prop = (*node).properties;
1541 while !prop.is_null() {
1542 (*prop).doc = doc;
1543 prop = (*prop).next;
1544 }
1545 let mut child = (*node).children;
1546 while !child.is_null() {
1547 set_node_doc(child, doc);
1548 child = (*child).next;
1549 }
1550}
1551
1552unsafe fn append_text_node(ctxt: *mut _xsltTransformContext, content: *const xmlChar) {
1558 let insert = (*ctxt).insert;
1559 if insert.is_null() || content.is_null() {
1560 return;
1561 }
1562 let text = new_text(content);
1563 if text.is_null() {
1564 return;
1565 }
1566 append_to_result(ctxt, text);
1567}
1568
1569unsafe fn append_comment_node(ctxt: *mut _xsltTransformContext, content: *const xmlChar) {
1575 let insert = (*ctxt).insert;
1576 if insert.is_null() || content.is_null() {
1577 return;
1578 }
1579 let comment = new_comment(content);
1580 if comment.is_null() {
1581 return;
1582 }
1583 append_to_result(ctxt, comment);
1584}
1585
1586unsafe fn append_pi_node(
1592 ctxt: *mut _xsltTransformContext,
1593 name: *const xmlChar,
1594 content: *const xmlChar,
1595) {
1596 let insert = (*ctxt).insert;
1597 if insert.is_null() || name.is_null() {
1598 return;
1599 }
1600 let pi = new_pi(name, content);
1601 if pi.is_null() {
1602 return;
1603 }
1604 append_to_result(ctxt, pi);
1605}
1606
1607unsafe fn process_copy(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1613 let node = (*ctxt).node;
1614 if node.is_null() {
1615 return;
1616 }
1617 let typ = (*node).type_;
1618 let saved_insert = (*ctxt).insert;
1619 if typ == XML_ELEMENT_NODE as c_int {
1620 let new_elem = new_element_node(ctxt, (*node).name, (*node).ns);
1621 if new_elem.is_null() {
1622 return;
1623 }
1624 (*ctxt).insert = new_elem;
1625 } else if typ == XML_TEXT_NODE as c_int || typ == XML_CDATA_SECTION_NODE as c_int {
1626 if !(*node).content.is_null() {
1627 append_text_node(ctxt, (*node).content);
1628 }
1629 } else if typ == XML_COMMENT_NODE as c_int {
1630 if !(*node).content.is_null() {
1631 append_comment_node(ctxt, (*node).content);
1632 }
1633 } else if typ == XML_PI_NODE as c_int {
1634 if !(*node).name.is_null() {
1635 append_pi_node(ctxt, (*node).name, (*node).content);
1636 }
1637 } else if typ == XML_ATTRIBUTE_NODE as c_int {
1638 if !(*node).children.is_null() {
1639 let val = node_get_content((*node).children);
1640 if !val.is_null() {
1641 append_text_node(ctxt, val);
1642 libc::free(val as *mut libc::c_void);
1643 }
1644 }
1645 }
1646 execute_content(ctxt, (*inst).children);
1648 (*ctxt).insert = saved_insert;
1649}
1650
1651unsafe fn process_element(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1657 let name_attr = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1658 if name_attr.is_null() {
1659 return;
1660 }
1661 let name_str = eval_avt(ctxt, name_attr);
1663 libc::free(name_attr as *mut libc::c_void);
1664 if name_str.is_null() {
1665 return;
1666 }
1667 let ns_attr = get_prop(inst, b"namespace\0".as_ptr() as *const xmlChar);
1669 let ns_str = if !ns_attr.is_null() {
1670 let v = eval_avt(ctxt, ns_attr);
1671 libc::free(ns_attr as *mut libc::c_void);
1672 v
1673 } else {
1674 ptr::null_mut()
1675 };
1676 let ns = if !ns_str.is_null() && *ns_str != 0 {
1678 let n = new_ns(ptr::null_mut(), ns_str, ptr::null());
1679 libc::free(ns_str as *mut libc::c_void);
1680 n
1681 } else {
1682 if !ns_str.is_null() {
1683 libc::free(ns_str as *mut libc::c_void);
1684 }
1685 ptr::null_mut()
1686 };
1687 let elem = new_node(ns, name_str);
1688 libc::free(name_str as *mut libc::c_void);
1689 if elem.is_null() {
1690 return;
1691 }
1692 append_to_result(ctxt, elem);
1693 let saved_insert = (*ctxt).insert;
1694 (*ctxt).insert = elem;
1695 execute_content(ctxt, (*inst).children);
1696 (*ctxt).insert = saved_insert;
1697}
1698
1699unsafe fn process_attribute(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1705 let name_attr = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1706 if name_attr.is_null() {
1707 return;
1708 }
1709 let name_str = eval_avt(ctxt, name_attr);
1711 libc::free(name_attr as *mut libc::c_void);
1712 if name_str.is_null() {
1713 return;
1714 }
1715 let insert = (*ctxt).insert;
1716 if insert.is_null() {
1717 xmlFree(name_str as *mut c_void);
1718 return;
1719 }
1720 let saved_insert = (*ctxt).insert;
1722 let buf = libc::calloc(1, core::mem::size_of::<_xmlBuffer>()) as *mut _xmlBuffer;
1723 if buf.is_null() {
1724 xmlFree(name_str as *mut c_void);
1725 return;
1726 }
1727 (*buf).content = libc::calloc(1, 64) as *mut xmlChar;
1728 (*buf).size = 64;
1729 (*buf).use_ = 0;
1730 let frag_doc = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
1731 if frag_doc.is_null() {
1732 libc::free(buf as *mut libc::c_void);
1733 xmlFree(name_str as *mut c_void);
1734 return;
1735 }
1736 (*frag_doc).type_ = XML_DOCUMENT_NODE as c_int;
1737 (*frag_doc).doc = frag_doc;
1738 (*ctxt).insert = frag_doc as *mut _xmlNode;
1739 execute_content(ctxt, (*inst).children);
1740 let mut value: Vec<u8> = Vec::new();
1742 let mut child = (*frag_doc).children;
1743 while !child.is_null() {
1744 if (*child).type_ == XML_TEXT_NODE as c_int {
1745 if !(*child).content.is_null() {
1746 let len = libc::strlen((*child).content as *const libc::c_char) as usize;
1747 value.extend_from_slice(core::slice::from_raw_parts((*child).content, len));
1748 }
1749 }
1750 child = (*child).next;
1751 }
1752 free_doc(frag_doc);
1754 (*ctxt).insert = saved_insert;
1755
1756 let mut cvalue = value.clone();
1758 cvalue.push(0);
1759 set_prop(insert, name_str, cvalue.as_ptr() as *const xmlChar);
1760 xmlFree(name_str as *mut c_void);
1761}
1762
1763unsafe fn process_text(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1769 let doe = get_prop(
1771 inst,
1772 b"disable-output-escaping\0".as_ptr() as *const xmlChar,
1773 );
1774 if !doe.is_null() {
1775 libc::free(doe as *mut libc::c_void);
1776 }
1777 let mut child = (*inst).children;
1779 while !child.is_null() {
1780 if (*child).type_ == XML_TEXT_NODE as c_int && !(*child).content.is_null() {
1781 append_text_node(ctxt, (*child).content);
1782 }
1783 child = (*child).next;
1784 }
1785}
1786
1787unsafe fn process_comment(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1793 let content = node_get_content((*inst).children);
1794 if !content.is_null() {
1795 append_comment_node(ctxt, content);
1796 libc::free(content as *mut libc::c_void);
1797 }
1798}
1799
1800unsafe fn process_pi(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1806 let name_attr = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1807 if name_attr.is_null() {
1808 return;
1809 }
1810 let name_str = eval_avt(ctxt, name_attr);
1812 libc::free(name_attr as *mut libc::c_void);
1813 if name_str.is_null() {
1814 return;
1815 }
1816 let content = node_get_content((*inst).children);
1817 append_pi_node(ctxt, name_str, content);
1818 if !content.is_null() {
1819 libc::free(content as *mut libc::c_void);
1820 }
1821 xmlFree(name_str as *mut c_void);
1822}
1823
1824unsafe fn process_number(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1830 let value_attr = get_prop(inst, b"value\0".as_ptr() as *const xmlChar);
1831 let mut number: f64 = f64::NAN;
1832 if !value_attr.is_null() {
1833 let obj = eval_xpath(ctxt, value_attr);
1834 libc::free(value_attr as *mut libc::c_void);
1835 if !obj.is_null() {
1836 number = (*obj).floatval;
1837 xmlXPathFreeObject(obj);
1838 }
1839 } else {
1840 number = 1.0;
1844 let node = (*ctxt).node;
1845 if !node.is_null() {
1846 let mut sib = (*node).prev;
1847 while !sib.is_null() {
1848 if (*sib).type_ == XML_ELEMENT_NODE as c_int {
1849 number += 1.0;
1850 }
1851 sib = (*sib).prev;
1852 }
1853 }
1854 }
1855 let format = get_prop(inst, b"format\0".as_ptr() as *const xmlChar);
1857 let formatted = crate::xslt::numbering::xsltFormatNumber(number, format);
1858 if !format.is_null() {
1859 libc::free(format as *mut libc::c_void);
1860 }
1861 if !formatted.is_null() {
1862 append_text_node(ctxt, formatted);
1863 libc::free(formatted as *mut libc::c_void);
1864 }
1865}
1866
1867unsafe fn xpath_obj_boolean(obj: *mut _xmlXPathObject) -> bool {
1874 if obj.is_null() {
1875 return false;
1876 }
1877 let typ = (*obj).type_;
1878 if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
1879 return (*obj).boolval != 0;
1880 }
1881 if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
1882 let n = (*obj).floatval;
1883 return n != 0.0 && !n.is_nan();
1884 }
1885 if typ == xmlXPathObjectType::XPATH_STRING as c_int {
1886 return !(*obj).stringval.is_null() && *(*obj).stringval != 0;
1887 }
1888 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1889 let ns = (*obj).nodesetval as *mut _xmlNodeSet;
1890 return !ns.is_null() && (*ns).nodeNr > 0;
1891 }
1892 false
1893}
1894
1895unsafe fn process_choose(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1901 let mut child = (*inst).children;
1902 let mut executed = false;
1903 while !child.is_null() {
1904 let next = (*child).next;
1905 if is_xslt_element(child, "when") {
1906 let test = get_prop(child, b"test\0".as_ptr() as *const xmlChar);
1907 if !test.is_null() {
1908 let obj = eval_xpath(ctxt, test);
1909 libc::free(test as *mut libc::c_void);
1910 let truthy = !obj.is_null() && xpath_obj_boolean(obj);
1911 if !obj.is_null() {
1912 xmlXPathFreeObject(obj);
1913 }
1914 if truthy {
1915 execute_content(ctxt, (*child).children);
1916 executed = true;
1917 break;
1918 }
1919 }
1920 } else if is_xslt_element(child, "otherwise") {
1921 if !executed {
1922 execute_content(ctxt, (*child).children);
1923 executed = true;
1924 }
1925 }
1926 child = next;
1927 }
1928}
1929
1930unsafe fn process_if(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1936 let test = get_prop(inst, b"test\0".as_ptr() as *const xmlChar);
1937 if test.is_null() {
1938 return;
1939 }
1940 let obj = eval_xpath(ctxt, test);
1941 libc::free(test as *mut libc::c_void);
1942 if obj.is_null() {
1943 return;
1944 }
1945 let truthy = xpath_obj_boolean(obj);
1949 xmlXPathFreeObject(obj);
1950 if truthy {
1951 execute_content(ctxt, (*inst).children);
1952 }
1953}
1954
1955unsafe fn process_variable(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1961 let name = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1962 if name.is_null() {
1963 return;
1964 }
1965 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
1966 let var = libc::calloc(1, core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
1967 if var.is_null() {
1968 libc::free(name as *mut libc::c_void);
1969 if !select.is_null() {
1970 libc::free(select as *mut libc::c_void);
1971 }
1972 return;
1973 }
1974 (*var).name = name;
1975 (*var).flags = 4; if !select.is_null() {
1977 let obj = eval_xpath(ctxt, select);
1978 if !obj.is_null() {
1979 (*var).value = obj;
1980 }
1981 libc::free(select as *mut libc::c_void);
1982 } else {
1983 let value = eval_content_fragment(ctxt, (*inst).children);
1984 if !value.is_null() {
1985 (*var).value = value;
1986 }
1987 }
1988 crate::xslt::variables::xsltPushVariable(ctxt, var);
1989}
1990
1991unsafe fn process_param(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
1997 let name = get_prop(inst, b"name\0".as_ptr() as *const xmlChar);
1998 if name.is_null() {
1999 return;
2000 }
2001 let already_bound = {
2005 let xpath_ctxt = (*ctxt).xpathCtxt;
2006 if xpath_ctxt.is_null() {
2007 false
2008 } else {
2009 let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
2010 if internal.is_null() {
2011 false
2012 } else {
2013 let name_len = libc::strlen(name as *const libc::c_char);
2014 let name_bytes = core::slice::from_raw_parts(name, name_len);
2015 let name_owned = String::from_utf8_lossy(name_bytes).into_owned();
2016 (*internal).variables.contains_key(&name_owned)
2017 }
2018 }
2019 };
2020 if already_bound {
2021 libc::free(name as *mut libc::c_void);
2023 return;
2024 }
2025 let select = get_prop(inst, b"select\0".as_ptr() as *const xmlChar);
2026 let var = libc::calloc(1, core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
2027 if var.is_null() {
2028 libc::free(name as *mut libc::c_void);
2029 if !select.is_null() {
2030 libc::free(select as *mut libc::c_void);
2031 }
2032 return;
2033 }
2034 (*var).name = name;
2035 (*var).flags = 2 | 4; if !select.is_null() {
2037 let obj = eval_xpath(ctxt, select);
2038 if !obj.is_null() {
2039 (*var).value = obj;
2040 }
2041 libc::free(select as *mut libc::c_void);
2042 } else {
2043 let value = eval_content_fragment(ctxt, (*inst).children);
2044 if !value.is_null() {
2045 (*var).value = value;
2046 }
2047 }
2048 crate::xslt::variables::xsltPushVariable(ctxt, var);
2049}
2050
2051unsafe fn process_message(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
2057 let content = node_get_content((*inst).children);
2058 if !content.is_null() {
2059 let len = libc::strlen(content as *const libc::c_char) as usize;
2061 let _ = libc::write(2, content as *const libc::c_void, len);
2062 let terminate = get_prop(inst, b"terminate\0".as_ptr() as *const xmlChar);
2064 if !terminate.is_null() {
2065 if libc::strcmp(
2066 terminate as *const libc::c_char,
2067 b"yes\0".as_ptr() as *const libc::c_char,
2068 ) == 0
2069 {
2070 (*ctxt).state = XSLT_STATE_ERROR;
2071 }
2072 libc::free(terminate as *mut libc::c_void);
2073 }
2074 libc::free(content as *mut libc::c_void);
2075 }
2076}
2077
2078unsafe fn eval_avt(ctxt: *mut _xsltTransformContext, value: *const xmlChar) -> *mut xmlChar {
2094 if value.is_null() {
2095 return ptr::null_mut();
2096 }
2097 let len = libc::strlen(value as *const libc::c_char);
2098 let bytes = core::slice::from_raw_parts(value, len);
2099 let mut out: Vec<u8> = Vec::new();
2100 let mut i = 0;
2101 while i < bytes.len() {
2102 let b = bytes[i];
2103 if b == b'{' {
2104 if i + 1 < bytes.len() && bytes[i + 1] == b'{' {
2105 out.push(b'{');
2106 i += 2;
2107 continue;
2108 }
2109 if let Some(rel) = bytes[i + 1..].iter().position(|c| *c == b'}') {
2111 let close = i + 1 + rel;
2112 let expr_bytes = &bytes[i + 1..close];
2113 let expr_c = crate::xml::string::bytes_to_xmlstr(expr_bytes);
2114 if !expr_c.is_null() {
2115 let obj = eval_xpath(ctxt, expr_c);
2116 xmlFree(expr_c as *mut c_void);
2117 if !obj.is_null() {
2118 let strv = xmlXPathCastToString(obj);
2119 xmlXPathFreeObject(obj);
2120 if !strv.is_null() {
2121 let slen = libc::strlen(strv as *const libc::c_char);
2122 out.extend_from_slice(core::slice::from_raw_parts(strv, slen));
2123 xmlFree(strv as *mut c_void);
2124 }
2125 }
2126 }
2127 i = close + 1;
2128 continue;
2129 }
2130 out.push(b'{');
2132 i += 1;
2133 continue;
2134 }
2135 if b == b'}' {
2136 if i + 1 < bytes.len() && bytes[i + 1] == b'}' {
2137 out.push(b'}');
2138 i += 2;
2139 continue;
2140 }
2141 out.push(b'}');
2142 i += 1;
2143 continue;
2144 }
2145 out.push(b);
2146 i += 1;
2147 }
2148 crate::xml::string::bytes_to_xmlstr(&out)
2149}
2150
2151unsafe fn process_literal_element(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
2157 let elem = new_node((*inst).ns, (*inst).name);
2159 if elem.is_null() {
2160 return;
2161 }
2162 append_to_result(ctxt, elem);
2163 let mut prop = (*inst).properties;
2165 while !prop.is_null() {
2166 let attr_name = (*prop).name;
2167 if !attr_name.is_null() {
2169 let name_bytes = core::slice::from_raw_parts(
2170 attr_name,
2171 libc::strlen(attr_name as *const libc::c_char) as usize,
2172 );
2173 if name_bytes != b"xmlns" {
2174 let attr_val = node_get_content((*prop).children);
2175 if !attr_val.is_null() {
2176 let avt_val = eval_avt(ctxt, attr_val);
2178 libc::free(attr_val as *mut libc::c_void);
2179 if !avt_val.is_null() {
2180 set_prop(elem, attr_name, avt_val);
2181 xmlFree(avt_val as *mut c_void);
2182 }
2183 }
2184 }
2185 }
2186 prop = (*prop).next;
2187 }
2188 let saved_insert = (*ctxt).insert;
2190 (*ctxt).insert = elem;
2191 execute_content(ctxt, (*inst).children);
2192 (*ctxt).insert = saved_insert;
2193}
2194
2195unsafe fn register_xslt_functions(ctxt: *mut _xsltTransformContext) {
2203 let xpath_ctxt = (*ctxt).xpathCtxt;
2204 if xpath_ctxt.is_null() {
2205 return;
2206 }
2207 let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
2208 if internal.is_null() {
2209 return;
2210 }
2211 let internal = &mut *internal;
2212
2213 use crate::xml::xpath::context::XPathContext;
2214 use crate::xml::xpath::types::{NodeSet, XPathValue};
2215
2216 let core_funcs = crate::xml::xpath::functions::core_functions();
2221 for (name, func) in core_funcs {
2222 internal.register_function(&name, func);
2223 }
2224
2225 internal.register_function("document", |ctx, args| {
2228 let value = match args.first() {
2229 Some(v) => v.as_string(),
2230 None => return Err("document() requires an argument".to_string()),
2231 };
2232 let uri = value;
2234 let _ = ctx;
2239 let _ = uri;
2240 Ok(XPathValue::NodeSet(NodeSet::new()))
2241 });
2242
2243 internal.register_function("key", |ctx, args| {
2248 let tctxt = ctx.func_lookup_data as *mut _xsltTransformContext;
2249 if tctxt.is_null() {
2250 return Ok(XPathValue::NodeSet(NodeSet::new()));
2251 }
2252 let name_str = match args.first() {
2253 Some(v) => v.as_string(),
2254 None => return Err("key() requires a name argument".to_string()),
2255 };
2256 let value_str = match args.get(1) {
2259 Some(XPathValue::NodeSet(ns)) => match ns.first() {
2260 Some(n) => crate::xml::xpath::types::node_string_value(n),
2261 None => return Ok(XPathValue::NodeSet(NodeSet::new())),
2262 },
2263 Some(v) => v.as_string(),
2264 None => return Err("key() requires a value argument".to_string()),
2265 };
2266 let name_c = crate::xml::string::bytes_to_xmlstr(name_str.as_bytes());
2267 let value_c = crate::xml::string::bytes_to_xmlstr(value_str.as_bytes());
2268 if name_c.is_null() || value_c.is_null() {
2269 if !name_c.is_null() {
2270 crate::abi::allocator::xmlFree(name_c as *mut c_void);
2271 }
2272 if !value_c.is_null() {
2273 crate::abi::allocator::xmlFree(value_c as *mut c_void);
2274 }
2275 return Ok(XPathValue::NodeSet(NodeSet::new()));
2276 }
2277 let ns = unsafe { crate::xslt::keys::xsltEvalKeyFunction(tctxt, name_c, value_c) };
2278 crate::abi::allocator::xmlFree(name_c as *mut c_void);
2279 crate::abi::allocator::xmlFree(value_c as *mut c_void);
2280 if ns.is_null() {
2281 return Ok(XPathValue::NodeSet(NodeSet::new()));
2282 }
2283 let mut out = NodeSet::new();
2284 unsafe {
2285 let node_nr = (*ns).nodeNr;
2286 let node_tab = (*ns).nodeTab;
2287 if !node_tab.is_null() {
2288 for i in 0..node_nr as isize {
2289 let n = *node_tab.add(i as usize);
2290 if !n.is_null() {
2291 out.push(n);
2292 }
2293 }
2294 }
2295 crate::abi::exports_xml2::xmlXPathFreeNodeSet(ns);
2296 }
2297 Ok(XPathValue::NodeSet(out))
2298 });
2299
2300 internal.register_function("generate-id", |ctx, args| {
2303 let node = match args.first() {
2304 Some(XPathValue::NodeSet(ns)) => ns.first().unwrap_or(ctx.context_node),
2305 _ => ctx.context_node,
2306 };
2307 if node.is_null() {
2308 return Ok(XPathValue::String(String::new()));
2309 }
2310 let id = unsafe { format!("id{:p}", node) };
2312 Ok(XPathValue::String(id))
2313 });
2314
2315 internal.register_function("system-property", |_ctx, args| {
2318 let name = match args.first() {
2319 Some(v) => v.as_string(),
2320 None => return Err("system-property() requires an argument".to_string()),
2321 };
2322 let value = match name.as_str() {
2323 "xsl:version" => "1.0",
2324 "xsl:vendor" => "libxslt",
2325 "xsl:vendor-url" => "http://xmlsoft.org/XSLT/",
2326 _ => "",
2327 };
2328 Ok(XPathValue::String(value.to_string()))
2329 });
2330
2331 internal.register_function("element-available", |_ctx, args| {
2334 let name = match args.first() {
2335 Some(v) => v.as_string(),
2336 None => return Err("element-available() requires an argument".to_string()),
2337 };
2338 let exslt_elements = [
2340 "exsl:document",
2341 "exsl:node-set",
2342 "exsl:object-type",
2343 "func:function",
2344 "func:result",
2345 "func:script",
2346 "dyn:element",
2347 "dyn:attribute",
2348 "dyn:call",
2349 "dyn:evaluate",
2350 ];
2351 let available = name.starts_with("xsl:")
2352 || exslt_elements.contains(&name.as_str())
2353 || matches!(
2354 name.as_str(),
2355 "apply-templates"
2356 | "call-template"
2357 | "apply-imports"
2358 | "for-each"
2359 | "value-of"
2360 | "copy-of"
2361 | "copy"
2362 | "element"
2363 | "attribute"
2364 | "text"
2365 | "comment"
2366 | "processing-instruction"
2367 | "number"
2368 | "choose"
2369 | "if"
2370 | "variable"
2371 | "param"
2372 | "sort"
2373 | "message"
2374 | "fallback"
2375 | "output"
2376 | "decimal-format"
2377 | "namespace-alias"
2378 | "attribute-set"
2379 | "key"
2380 | "strip-space"
2381 | "preserve-space"
2382 | "import"
2383 | "include"
2384 | "stylesheet"
2385 | "transform"
2386 );
2387 Ok(XPathValue::Boolean(available))
2388 });
2389
2390 internal.register_function("function-available", |_ctx, args| {
2391 let name = match args.first() {
2392 Some(v) => v.as_string(),
2393 None => return Err("function-available() requires an argument".to_string()),
2394 };
2395 let core = [
2397 "last",
2398 "position",
2399 "count",
2400 "id",
2401 "local-name",
2402 "namespace-uri",
2403 "name",
2404 "string",
2405 "concat",
2406 "starts-with",
2407 "contains",
2408 "substring-before",
2409 "substring-after",
2410 "substring",
2411 "string-length",
2412 "normalize-space",
2413 "translate",
2414 "boolean",
2415 "not",
2416 "true",
2417 "false",
2418 "lang",
2419 "number",
2420 "sum",
2421 "floor",
2422 "ceiling",
2423 "round",
2424 ];
2425 let xslt_fn = [
2426 "document",
2427 "key",
2428 "generate-id",
2429 "system-property",
2430 "element-available",
2431 "function-available",
2432 "current",
2433 "unparsed-entity-uri",
2434 ];
2435 let exslt_available = crate::exslt::lookup(&name).is_some();
2438 let local = name.rsplit(':').next().unwrap_or(&name);
2439 Ok(XPathValue::Boolean(
2440 core.contains(&local) || xslt_fn.contains(&local) || exslt_available,
2441 ))
2442 });
2443
2444 internal.register_function("current", |ctx, _args| {
2446 let node = ctx.context_node;
2447 if node.is_null() {
2448 return Ok(XPathValue::NodeSet(NodeSet::new()));
2449 }
2450 let mut ns = NodeSet::new();
2451 ns.push(node);
2452 Ok(XPathValue::NodeSet(ns))
2453 });
2454
2455 for (name, f) in crate::exslt::iter_functions() {
2461 internal.register_function(&name, f);
2462 }
2463 crate::exslt::functions::register_stylesheet_functions(
2465 internal,
2466 (*ctxt)
2467 .style
2468 .as_ref()
2469 .map_or(std::ptr::null_mut(), |s| s.doc),
2470 );
2471}
2472
2473pub fn exslt_element_names() -> &'static [&'static str] {
2475 &[
2476 "exsl:document",
2477 "exsl:node-set",
2478 "exsl:object-type",
2479 "func:function",
2480 "func:result",
2481 "func:script",
2482 "dyn:element",
2483 "dyn:attribute",
2484 "dyn:call",
2485 "dyn:evaluate",
2486 ]
2487}
2488
2489pub fn exslt_function_names() -> Vec<String> {
2491 crate::exslt::iter_functions()
2492 .into_iter()
2493 .map(|(n, _)| n)
2494 .collect()
2495}
2496
2497#[cfg(test)]
2498mod tests {
2499 use super::*;
2500 use core::ptr;
2501
2502 #[test]
2503 fn test_new_context_null_style() {
2504 unsafe {
2505 assert!(xsltNewTransformContext(ptr::null_mut(), ptr::null_mut()).is_null());
2506 }
2507 }
2508
2509 #[test]
2510 fn test_free_null() {
2511 unsafe {
2512 xsltFreeTransformContext(ptr::null_mut());
2513 xsltFreeTransformResult(ptr::null_mut());
2514 }
2515 }
2516
2517 #[test]
2518 fn test_apply_stylesheet_null() {
2519 unsafe {
2520 assert!(
2521 xsltApplyStylesheet(ptr::null_mut(), ptr::null_mut(), ptr::null_mut()).is_null()
2522 );
2523 }
2524 }
2525
2526 #[test]
2527 fn test_end_to_end_simplified_stylesheet() {
2528 unsafe {
2529 let xsl = b"<?xml version=\"1.0\"?><html xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"><body><p>Hello</p></body></html>\0";
2532 let style = crate::xslt::stylesheet::xsltParseStylesheetMemory(
2533 xsl.as_ptr() as *const c_char,
2534 (xsl.len() - 1) as c_int,
2535 ptr::null(),
2536 );
2537 assert!(!style.is_null(), "stylesheett parse failed");
2538
2539 let src = b"<?xml version=\"1.0\"?><root><item>world</item></root>\0";
2541 let doc = crate::abi::exports_xml2::xmlReadMemory(
2542 src.as_ptr() as *const c_char,
2543 (src.len() - 1) as c_int,
2544 ptr::null(),
2545 ptr::null(),
2546 0,
2547 );
2548 assert!(!doc.is_null());
2549
2550 let result = xsltApplyStylesheet(style, doc, ptr::null_mut());
2551 assert!(!result.is_null(), "apply failed");
2552
2553 let mut txt: *mut xmlChar = ptr::null_mut();
2555 let mut len: c_int = 0;
2556 let ret = crate::xslt::serialization::xsltSaveResultToString(
2557 &mut txt, &mut len, result, style,
2558 );
2559 assert_eq!(ret, 0);
2560 assert!(!txt.is_null());
2561 let out = String::from_utf8_lossy(core::slice::from_raw_parts(txt, len as usize));
2562 assert!(
2563 out.contains("Hello"),
2564 "result should contain the literal text, got: {}",
2565 out
2566 );
2567
2568 libc::free(txt as *mut libc::c_void);
2569 crate::xml::tree::free_doc(result);
2570 crate::xml::tree::free_doc(doc);
2571 crate::xslt::stylesheet::xsltFreeStylesheet(style);
2572 }
2573 }
2574
2575 #[test]
2576 fn test_end_to_end_template_transform() {
2577 unsafe {
2578 let xsl = b"<?xml version=\"1.0\"?>\n\
2581 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\
2582 <xsl:template match=\"/\">\n\
2583 <out><xsl:value-of select=\"/root/item\"/></out>\n\
2584 </xsl:template>\n\
2585 </xsl:stylesheet>\0";
2586 let style = crate::xslt::stylesheet::xsltParseStylesheetMemory(
2587 xsl.as_ptr() as *const c_char,
2588 (xsl.len() - 1) as c_int,
2589 ptr::null(),
2590 );
2591 assert!(!style.is_null(), "stylesheet parse failed");
2592
2593 let src = b"<?xml version=\"1.0\"?><root><item>world</item></root>\0";
2594 let doc = crate::abi::exports_xml2::xmlReadMemory(
2595 src.as_ptr() as *const c_char,
2596 (src.len() - 1) as c_int,
2597 ptr::null(),
2598 ptr::null(),
2599 0,
2600 );
2601 assert!(!doc.is_null());
2602
2603 let result = xsltApplyStylesheet(style, doc, ptr::null_mut());
2604 assert!(!result.is_null(), "apply failed");
2605
2606 let mut txt: *mut xmlChar = ptr::null_mut();
2607 let mut len: c_int = 0;
2608 let ret = crate::xslt::serialization::xsltSaveResultToString(
2609 &mut txt, &mut len, result, style,
2610 );
2611 assert_eq!(ret, 0);
2612 let out = String::from_utf8_lossy(core::slice::from_raw_parts(txt, len as usize));
2613 assert!(
2614 out.contains("world"),
2615 "result should contain the selected value, got: {}",
2616 out
2617 );
2618
2619 libc::free(txt as *mut libc::c_void);
2620 crate::xml::tree::free_doc(result);
2621 crate::xml::tree::free_doc(doc);
2622 crate::xslt::stylesheet::xsltFreeStylesheet(style);
2623 }
2624 }
2625
2626 unsafe fn run_transform(xsl: &[u8], src: &[u8]) -> String {
2629 let style = crate::xslt::stylesheet::xsltParseStylesheetMemory(
2630 xsl.as_ptr() as *const c_char,
2631 (xsl.len() - 1) as c_int,
2632 ptr::null(),
2633 );
2634 assert!(!style.is_null(), "stylesheet parse failed");
2635 let doc = crate::abi::exports_xml2::xmlReadMemory(
2636 src.as_ptr() as *const c_char,
2637 (src.len() - 1) as c_int,
2638 ptr::null(),
2639 ptr::null(),
2640 0,
2641 );
2642 assert!(!doc.is_null());
2643 let result = xsltApplyStylesheet(style, doc, ptr::null_mut());
2644 assert!(!result.is_null(), "apply failed");
2645 let mut txt: *mut xmlChar = ptr::null_mut();
2646 let mut len: c_int = 0;
2647 let ret =
2648 crate::xslt::serialization::xsltSaveResultToString(&mut txt, &mut len, result, style);
2649 assert_eq!(ret, 0);
2650 let out =
2651 String::from_utf8_lossy(core::slice::from_raw_parts(txt, len as usize)).into_owned();
2652 libc::free(txt as *mut libc::c_void);
2653 crate::xml::tree::free_doc(result);
2654 crate::xml::tree::free_doc(doc);
2655 crate::xslt::stylesheet::xsltFreeStylesheet(style);
2656 out
2657 }
2658
2659 #[test]
2660 fn test_xslt_for_each() {
2661 unsafe {
2662 let xsl = b"<?xml version=\"1.0\"?>\
2663 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2664 <xsl:template match=\"/\">\
2665 <list><xsl:for-each select=\"/root/item\"><i><xsl:value-of select=\".\"/></i></xsl:for-each></list>\
2666 </xsl:template>\
2667 </xsl:stylesheet>\0";
2668 let src =
2669 b"<?xml version=\"1.0\"?><root><item>a</item><item>b</item><item>c</item></root>\0";
2670 let out = run_transform(xsl, src);
2671 assert!(out.contains("<i>a</i>"), "got: {}", out);
2672 assert!(out.contains("<i>b</i>"), "got: {}", out);
2673 assert!(out.contains("<i>c</i>"), "got: {}", out);
2674 }
2675 }
2676
2677 #[test]
2678 fn test_xslt_core_functions_in_value_of() {
2679 unsafe {
2684 let xsl = b"<?xml version=\"1.0\"?>\
2685 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2686 <xsl:template match=\"/\">\
2687 <out>\
2688 <cnt><xsl:value-of select=\"count(library/book)\"/></cnt>\
2689 <sub><xsl:value-of select=\"substring('hello',1,2)\"/></sub>\
2690 <str><xsl:value-of select=\"string(library/book[1]/title)\"/></str>\
2691 </out>\
2692 </xsl:template>\
2693 </xsl:stylesheet>\0";
2694 let src = b"<?xml version=\"1.0\"?>\
2695 <library>\
2696 <book><title>Rust</title></book>\
2697 <book><title>XML</title></book>\
2698 </library>\0";
2699 let out = run_transform(xsl, src);
2700 assert!(out.contains("<cnt>2</cnt>"), "count() wrong: {}", out);
2701 assert!(out.contains("<sub>he</sub>"), "substring() wrong: {}", out);
2702 assert!(out.contains("<str>Rust</str>"), "string() wrong: {}", out);
2703 }
2704 }
2705
2706 #[test]
2707 fn test_xslt_avt_in_literal_attribute() {
2708 unsafe {
2712 let xsl = b"<?xml version=\"1.0\"?>\
2713 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2714 <xsl:template match=\"/\">\
2715 <out>\
2716 <xsl:for-each select=\"library/book\">\
2717 <book id=\"{@id}\" label=\"{{literal}}\"/>\
2718 </xsl:for-each>\
2719 </out>\
2720 </xsl:template>\
2721 </xsl:stylesheet>\0";
2722 let src = b"<?xml version=\"1.0\"?>\
2723 <library>\
2724 <book id=\"b1\"/>\
2725 <book id=\"b2\"/>\
2726 </library>\0";
2727 let out = run_transform(xsl, src);
2728 assert!(
2729 out.contains("<book id=\"b1\" label=\"{literal}\""),
2730 "AVT not evaluated: {}",
2731 out
2732 );
2733 assert!(
2734 out.contains("<book id=\"b2\" label=\"{literal}\""),
2735 "AVT not evaluated: {}",
2736 out
2737 );
2738 }
2739 }
2740
2741 #[test]
2742 fn test_xslt_avt_in_xsl_element_name() {
2743 unsafe {
2745 let xsl = b"<?xml version=\"1.0\"?>\
2746 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2747 <xsl:template match=\"/\">\
2748 <out>\
2749 <xsl:element name=\"el-{library/book/@id}\">text</xsl:element>\
2750 </out>\
2751 </xsl:template>\
2752 </xsl:stylesheet>\0";
2753 let src = b"<?xml version=\"1.0\"?>\
2754 <library><book id=\"b1\"/></library>\0";
2755 let out = run_transform(xsl, src);
2756 assert!(
2757 out.contains("<el-b1>"),
2758 "xsl:element AVT not evaluated: {}",
2759 out
2760 );
2761 }
2762 }
2763
2764 #[test]
2765 fn test_xslt_variable_inline_content_rtf() {
2766 unsafe {
2772 let xsl = b"<?xml version=\"1.0\"?>\
2773 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2774 <xsl:variable name=\"rtf\"><nums><n>3</n><n>7</n></nums></xsl:variable>\
2775 <xsl:template match=\"/\">\
2776 <out><v><xsl:value-of select=\"$rtf\"/></v></out>\
2777 </xsl:template>\
2778 </xsl:stylesheet>\0";
2779 let src = b"<?xml version=\"1.0\"?><root/>\0";
2780 let out = run_transform(xsl, src);
2782 assert!(out.contains("<v>37</v>"), "RTF string-value wrong: {}", out);
2783 }
2784 }
2785
2786 #[test]
2787 fn test_xslt_exsl_node_set_on_rtf() {
2788 unsafe {
2792 crate::exslt::register_all();
2793 let xsl = b"<?xml version=\"1.0\"?>\
2794 <xsl:stylesheet version=\"1.0\"\
2795 xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\
2796 xmlns:exsl=\"http://exslt.org/common\"\
2797 xmlns:math=\"http://exslt.org/math\"\
2798 extension-element-prefixes=\"exsl math\">\
2799 <xsl:variable name=\"rtf\"><nums><n>3</n><n>7</n><n>1</n><n>9</n></nums></xsl:variable>\
2800 <xsl:template match=\"/\">\
2801 <out>\
2802 <max><xsl:value-of select=\"math:max(exsl:node-set($rtf)/nums/n)\"/></max>\
2803 <cnt><xsl:value-of select=\"count(exsl:node-set($rtf)/nums/n)\"/></cnt>\
2804 </out>\
2805 </xsl:template>\
2806 </xsl:stylesheet>\0";
2807 let src = b"<?xml version=\"1.0\"?><root/>\0";
2808 let out = run_transform(xsl, src);
2809 assert!(out.contains("<max>9</max>"), "math:max wrong: {}", out);
2810 assert!(out.contains("<cnt>4</cnt>"), "count wrong: {}", out);
2811 }
2812 }
2813
2814 #[test]
2815 fn test_xslt_if_node_set_test() {
2816 unsafe {
2821 let xsl = b"<?xml version=\"1.0\"?>\
2822 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2823 <xsl:template match=\"/\">\
2824 <out>\
2825 <xsl:for-each select=\"library/book\">\
2826 <b><xsl:if test=\"author\">A</xsl:if><xsl:if test=\"missing\">M</xsl:if></b>\
2827 </xsl:for-each>\
2828 </out>\
2829 </xsl:template>\
2830 </xsl:stylesheet>\0";
2831 let src = b"<?xml version=\"1.0\"?>\
2832 <library><book><author>x</author></book><book/></library>\0";
2833 let out = run_transform(xsl, src);
2834 assert!(out.contains("<b>A</b>"), "node-set test false: {}", out);
2835 assert!(out.contains("<b/>"), "missing-node test true: {}", out);
2836 }
2837 }
2838
2839 #[test]
2840 fn test_xslt_attribute_string_value() {
2841 unsafe {
2846 let xsl = b"<?xml version=\"1.0\"?>\
2847 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2848 <xsl:template match=\"/\">\
2849 <out>\
2850 <x><xsl:value-of select=\"string(library/book[1]/@id)\"/></x>\
2851 <y><xsl:value-of select=\"count(library/book[@id='b2'])\"/></y>\
2852 </out>\
2853 </xsl:template>\
2854 </xsl:stylesheet>\0";
2855 let src = b"<?xml version=\"1.0\"?>\
2856 <library><book id=\"b1\"/><book id=\"b2\"/></library>\0";
2857 let out = run_transform(xsl, src);
2858 assert!(
2859 out.contains("<x>b1</x>"),
2860 "attr string-value wrong: {}",
2861 out
2862 );
2863 assert!(out.contains("<y>1</y>"), "attr predicate wrong: {}", out);
2864 }
2865 }
2866
2867 #[test]
2868 fn test_xslt_sort_descending() {
2869 unsafe {
2873 let xsl = b"<?xml version=\"1.0\"?>\
2874 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2875 <xsl:template match=\"/\">\
2876 <out>\
2877 <xsl:for-each select=\"library/book\">\
2878 <xsl:sort select=\"title\" order=\"descending\"/>\
2879 <i><xsl:value-of select=\"title\"/></i>\
2880 </xsl:for-each>\
2881 </out>\
2882 </xsl:template>\
2883 </xsl:stylesheet>\0";
2884 let src = b"<?xml version=\"1.0\"?>\
2885 <library><book><title>Alpha</title></book><book><title>Gamma</title></book><book><title>Beta</title></book></library>\0";
2886 let out = run_transform(xsl, src);
2887 let gamma = out.find("<i>Gamma</i>").unwrap();
2888 let beta = out.find("<i>Beta</i>").unwrap();
2889 let alpha = out.find("<i>Alpha</i>").unwrap();
2890 assert!(gamma < beta && beta < alpha, "not descending: {}", out);
2891 }
2892 }
2893
2894 #[test]
2895 fn test_xslt_key_function() {
2896 unsafe {
2899 let xsl = b"<?xml version=\"1.0\"?>\
2900 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2901 <xsl:key name=\"byAuthor\" match=\"book\" use=\"author\"/>\
2902 <xsl:template match=\"/\">\
2903 <out><k><xsl:value-of select=\"key('byAuthor', 'Smith')/title\"/></k></out>\
2904 </xsl:template>\
2905 </xsl:stylesheet>\0";
2906 let src = b"<?xml version=\"1.0\"?>\
2907 <library><book><title>A</title><author>Smith</author></book><book><title>B</title><author>Jones</author></book></library>\0";
2908 let out = run_transform(xsl, src);
2909 assert!(out.contains("<k>A</k>"), "key() wrong: {}", out);
2910 }
2911 }
2912
2913 #[test]
2914 fn test_xslt_call_template_with_params() {
2915 unsafe {
2920 let xsl = b"<?xml version=\"1.0\"?>\
2921 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2922 <xsl:template match=\"/\">\
2923 <out>\
2924 <xsl:call-template name=\"greet\">\
2925 <xsl:with-param name=\"who\" select=\"'World'\"/>\
2926 </xsl:call-template>\
2927 </out>\
2928 </xsl:template>\
2929 <xsl:template name=\"greet\">\
2930 <xsl:param name=\"who\" select=\"'nobody'\"/>\
2931 <g>Hello <xsl:value-of select=\"$who\"/>!</g>\
2932 </xsl:template>\
2933 </xsl:stylesheet>\0";
2934 let src = b"<?xml version=\"1.0\"?><root/>\0";
2935 let out = run_transform(xsl, src);
2936 assert!(out.contains("Hello World"), "with-param lost: {}", out);
2937 }
2938 }
2939
2940 #[test]
2941 fn test_xslt_html_method_meta_charset() {
2942 unsafe {
2945 let xsl = b"<?xml version=\"1.0\"?>\
2946 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2947 <xsl:output method=\"html\" indent=\"yes\"/>\
2948 <xsl:template match=\"/\">\
2949 <html><head><title>T</title></head><body><p>x</p></body></html>\
2950 </xsl:template>\
2951 </xsl:stylesheet>\0";
2952 let src = b"<?xml version=\"1.0\"?><root/>\0";
2953 let out = run_transform(xsl, src);
2954 assert!(
2955 out.contains("<meta charset=\"UTF-8\">"),
2956 "meta charset missing: {}",
2957 out
2958 );
2959 assert!(!out.contains(" <head>"), "unexpected indent: {}", out);
2960 }
2961 }
2962
2963 #[test]
2964 fn test_xslt_if() {
2965 unsafe {
2966 let xsl = b"<?xml version=\"1.0\"?>\
2967 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2968 <xsl:template match=\"/\">\
2969 <xsl:if test=\"/root/item = 'yes'\"><yes/></xsl:if>\
2970 <xsl:if test=\"/root/item = 'no'\"><no/></xsl:if>\
2971 </xsl:template>\
2972 </xsl:stylesheet>\0";
2973 let src = b"<?xml version=\"1.0\"?><root><item>yes</item></root>\0";
2974 let out = run_transform(xsl, src);
2975 assert!(out.contains("<yes/>"), "got: {}", out);
2976 assert!(!out.contains("<no/>"), "got: {}", out);
2977 }
2978 }
2979
2980 #[test]
2981 fn test_xslt_choose() {
2982 unsafe {
2983 let xsl = b"<?xml version=\"1.0\"?>\
2984 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
2985 <xsl:template match=\"/\">\
2986 <xsl:choose>\
2987 <xsl:when test=\"/root/item = 'a'\"><chosen>a</chosen></xsl:when>\
2988 <xsl:when test=\"/root/item = 'b'\"><chosen>b</chosen></xsl:when>\
2989 <xsl:otherwise><chosen>other</chosen></xsl:otherwise>\
2990 </xsl:choose>\
2991 </xsl:template>\
2992 </xsl:stylesheet>\0";
2993 let src = b"<?xml version=\"1.0\"?><root><item>b</item></root>\0";
2994 let out = run_transform(xsl, src);
2995 assert!(out.contains("<chosen>b</chosen>"), "got: {}", out);
2996 }
2997 }
2998
2999 #[test]
3000 fn test_xslt_variable_and_call_template() {
3001 unsafe {
3002 let xsl = b"<?xml version=\"1.0\"?>\
3003 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3004 <xsl:variable name=\"greeting\" select=\"'Hello'\"/>\
3005 <xsl:template match=\"/\">\
3006 <xsl:call-template name=\"say\"/>\
3007 </xsl:template>\
3008 <xsl:template name=\"say\">\
3009 <msg><xsl:value-of select=\"$greeting\"/> <xsl:value-of select=\"/root/name\"/></msg>\
3010 </xsl:template>\
3011 </xsl:stylesheet>\0";
3012 let src = b"<?xml version=\"1.0\"?><root><name>World</name></root>\0";
3013 let out = run_transform(xsl, src);
3014 assert!(out.contains("HelloWorld"), "got: {}", out);
3019 }
3020 }
3021
3022 #[test]
3023 fn test_xslt_text_preserves_whitespace() {
3024 unsafe {
3025 let xsl = b"<?xml version=\"1.0\"?>\
3026 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3027 <xsl:template match=\"/\">\
3028 <msg><xsl:value-of select=\"'Hello'\"/><xsl:text> </xsl:text><xsl:value-of select=\"/root/name\"/></msg>\
3029 </xsl:template>\
3030 </xsl:stylesheet>\0";
3031 let src = b"<?xml version=\"1.0\"?><root><name>World</name></root>\0";
3032 let out = run_transform(xsl, src);
3033 assert!(out.contains("Hello World"), "got: {}", out);
3035 }
3036 }
3037
3038 #[test]
3039 fn test_xslt_element_and_attribute() {
3040 unsafe {
3041 let xsl = b"<?xml version=\"1.0\"?>\
3042 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3043 <xsl:template match=\"/\">\
3044 <xsl:element name=\"custom\">\
3045 <xsl:attribute name=\"attr\">value</xsl:attribute>\
3046 <xsl:value-of select=\"/root/item\"/>\
3047 </xsl:element>\
3048 </xsl:template>\
3049 </xsl:stylesheet>\0";
3050 let src = b"<?xml version=\"1.0\"?><root><item>data</item></root>\0";
3051 let out = run_transform(xsl, src);
3052 assert!(out.contains("custom"), "got: {}", out);
3053 assert!(out.contains("attr=\"value\""), "got: {}", out);
3054 assert!(out.contains("data"), "got: {}", out);
3055 }
3056 }
3057
3058 #[test]
3059 fn test_xslt_apply_templates_with_select() {
3060 unsafe {
3061 let xsl = b"<?xml version=\"1.0\"?>\
3062 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3063 <xsl:template match=\"/\">\
3064 <out><xsl:apply-templates select=\"/root/item\"/></out>\
3065 </xsl:template>\
3066 <xsl:template match=\"item\"><item><xsl:value-of select=\".\"/></item></xsl:template>\
3067 </xsl:stylesheet>\0";
3068 let src = b"<?xml version=\"1.0\"?><root><item>alpha</item><item>beta</item></root>\0";
3069 let out = run_transform(xsl, src);
3070 assert!(out.contains("<item>alpha</item>"), "got: {}", out);
3071 assert!(out.contains("<item>beta</item>"), "got: {}", out);
3072 }
3073 }
3074
3075 #[test]
3076 fn test_xslt_text_and_comment_and_pi() {
3077 unsafe {
3078 let xsl = b"<?xml version=\"1.0\"?>\
3079 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3080 <xsl:template match=\"/\">\
3081 <xsl:text>plain</xsl:text>\
3082 <xsl:comment>a comment</xsl:comment>\
3083 <xsl:processing-instruction name=\"target\">pi-data</xsl:processing-instruction>\
3084 </xsl:template>\
3085 </xsl:stylesheet>\0";
3086 let src = b"<?xml version=\"1.0\"?><root/>\0";
3087 let out = run_transform(xsl, src);
3088 assert!(out.contains("plain"), "got: {}", out);
3089 assert!(out.contains("<!--a comment-->"), "got: {}", out);
3090 assert!(out.contains("<?target pi-data?>"), "got: {}", out);
3091 }
3092 }
3093
3094 #[test]
3095 fn test_xslt_copy_and_copy_of() {
3096 unsafe {
3097 let xsl = b"<?xml version=\"1.0\"?>\
3098 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3099 <xsl:template match=\"/\">\
3100 <xsl:copy-of select=\"/root/item\"/>\
3101 <xsl:copy-of select=\"'literal'\"/>\
3102 </xsl:template>\
3103 </xsl:stylesheet>\0";
3104 let src = b"<?xml version=\"1.0\"?><root><item>copied</item></root>\0";
3105 let out = run_transform(xsl, src);
3106 assert!(out.contains("<item>copied</item>"), "got: {}", out);
3107 assert!(out.contains("literal"), "got: {}", out);
3108 }
3109 }
3110
3111 #[test]
3112 fn test_xslt_number() {
3113 unsafe {
3114 let xsl = b"<?xml version=\"1.0\"?>\
3115 <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
3116 <xsl:template match=\"/\">\
3117 <n><xsl:number value=\"42\"/></n>\
3118 <r><xsl:number value=\"9\" format=\"I\"/></r>\
3119 </xsl:template>\
3120 </xsl:stylesheet>\0";
3121 let src = b"<?xml version=\"1.0\"?><root/>\0";
3122 let out = run_transform(xsl, src);
3123 assert!(out.contains("<n>42</n>"), "got: {}", out);
3124 assert!(out.contains("<r>IX</r>"), "got: {}", out);
3125 }
3126 }
3127}