1#![allow(non_snake_case)]
18#![allow(unused_variables)]
19#![allow(clippy::missing_safety_doc)]
20#![allow(clippy::not_unsafe_ptr_arg_deref)]
21
22use core::ptr;
23use std::collections::HashMap;
24use std::ffi::CStr;
25use std::os::raw::{c_char, c_int, c_void};
26
27use parking_lot::RwLock;
28
29use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl};
30use crate::abi::structs::*;
31use crate::abi::types::*;
32
33pub type xsltExtInitFunction =
37 unsafe extern "C" fn(ctxt: *mut _xsltTransformContext, URI: *const xmlChar) -> *mut c_void;
38pub type xsltExtShutdownFunction =
40 unsafe extern "C" fn(ctxt: *mut _xsltTransformContext, URI: *const xmlChar, data: *mut c_void);
41pub type xsltStyleExtInitFunction =
43 unsafe extern "C" fn(style: *mut _xsltStylesheet, URI: *const xmlChar) -> *mut c_void;
44pub type xsltStyleExtShutdownFunction =
46 unsafe extern "C" fn(style: *mut _xsltStylesheet, URI: *const xmlChar, data: *mut c_void);
47pub type xsltTopLevelFunction =
49 unsafe extern "C" fn(style: *mut _xsltStylesheet, node: *mut _xmlNode, data: *mut c_void);
50
51#[derive(Clone, Copy)]
52struct ExtModule {
53 init_func: Option<xsltExtInitFunction>,
54 shutdown_func: Option<xsltExtShutdownFunction>,
55 style_init_func: Option<xsltStyleExtInitFunction>,
56 style_shutdown_func: Option<xsltStyleExtShutdownFunction>,
57}
58
59#[derive(Clone, Copy)]
64struct ExtElement {
65 precomp: usize, transform: usize, }
68
69fn ext_key(name: *const xmlChar, uri: *const xmlChar) -> Option<Vec<u8>> {
71 if name.is_null() || uri.is_null() {
72 return None;
73 }
74 let n = unsafe { CStr::from_ptr(name as *const c_char).to_bytes() };
75 let u = unsafe { CStr::from_ptr(uri as *const c_char).to_bytes() };
76 let mut k = Vec::with_capacity(n.len() + 1 + u.len());
77 k.extend_from_slice(n);
78 k.push(0);
79 k.extend_from_slice(u);
80 Some(k)
81}
82
83static EXT_MODULES: once_cell::sync::Lazy<RwLock<HashMap<Vec<u8>, ExtModule>>> =
85 once_cell::sync::Lazy::new(|| RwLock::new(HashMap::new()));
86static EXT_ELEMENTS: once_cell::sync::Lazy<RwLock<HashMap<Vec<u8>, ExtElement>>> =
89 once_cell::sync::Lazy::new(|| RwLock::new(HashMap::new()));
90static EXT_FUNCTIONS: once_cell::sync::Lazy<RwLock<HashMap<Vec<u8>, usize>>> =
92 once_cell::sync::Lazy::new(|| RwLock::new(HashMap::new()));
93static EXT_TOPLEVELS: once_cell::sync::Lazy<RwLock<HashMap<Vec<u8>, usize>>> =
95 once_cell::sync::Lazy::new(|| RwLock::new(HashMap::new()));
96
97#[no_mangle]
109pub unsafe extern "C" fn xsltRegisterExtModule(
110 URI: *const xmlChar,
111 initFunc: Option<xsltExtInitFunction>,
112 shutdownFunc: Option<xsltExtShutdownFunction>,
113) -> c_int {
114 if URI.is_null() || initFunc.is_none() {
116 return -1;
117 }
118 let key = unsafe { CStr::from_ptr(URI as *const c_char).to_bytes().to_vec() };
119 EXT_MODULES.write().insert(
120 key,
121 ExtModule {
122 init_func: initFunc,
123 shutdown_func: shutdownFunc,
124 style_init_func: None,
125 style_shutdown_func: None,
126 },
127 );
128 0
129}
130
131#[no_mangle]
144pub unsafe extern "C" fn xsltRegisterExtModuleFull(
145 URI: *const xmlChar,
146 initFunc: Option<xsltExtInitFunction>,
147 shutdownFunc: Option<xsltExtShutdownFunction>,
148 styleInitFunc: Option<xsltStyleExtInitFunction>,
149 styleShutdownFunc: Option<xsltStyleExtShutdownFunction>,
150) -> c_int {
151 if URI.is_null() || initFunc.is_none() {
152 return -1;
153 }
154 let key = unsafe { CStr::from_ptr(URI as *const c_char).to_bytes().to_vec() };
155 EXT_MODULES.write().insert(
156 key,
157 ExtModule {
158 init_func: initFunc,
159 shutdown_func: shutdownFunc,
160 style_init_func: styleInitFunc,
161 style_shutdown_func: styleShutdownFunc,
162 },
163 );
164 0
165}
166
167#[no_mangle]
178pub unsafe extern "C" fn xsltRegisterExtModuleElement(
179 name: *const xmlChar,
180 URI: *const xmlChar,
181 precomp: *mut c_void,
182 transform: *mut c_void,
183) -> c_int {
184 let Some(key) = ext_key(name, URI) else {
185 return -1;
186 };
187 EXT_ELEMENTS.write().insert(
188 key,
189 ExtElement {
190 precomp: precomp as usize,
191 transform: transform as usize,
192 },
193 );
194 0
195}
196
197#[no_mangle]
207pub unsafe extern "C" fn xsltRegisterExtModuleFunction(
208 name: *const xmlChar,
209 URI: *const xmlChar,
210 function: *mut c_void,
211) -> c_int {
212 let Some(key) = ext_key(name, URI) else {
213 return -1;
214 };
215 EXT_FUNCTIONS.write().insert(key, function as usize);
216 0
217}
218
219#[no_mangle]
229pub unsafe extern "C" fn xsltRegisterExtModuleTopLevel(
230 name: *const xmlChar,
231 URI: *const xmlChar,
232 function: *mut c_void,
233) -> c_int {
234 let Some(key) = ext_key(name, URI) else {
235 return -1;
236 };
237 EXT_TOPLEVELS.write().insert(key, function as usize);
238 0
239}
240
241#[no_mangle]
250pub unsafe extern "C" fn xsltUnregisterExtModule(URI: *const xmlChar) -> c_int {
251 if URI.is_null() {
252 return -1;
253 }
254 let uri_bytes = unsafe { CStr::from_ptr(URI as *const c_char).to_bytes().to_vec() };
255 let mut mods = EXT_MODULES.write();
256 if mods.remove(&uri_bytes).is_none() {
257 return -1;
258 }
259 drop(mods);
260 let mut elems = EXT_ELEMENTS.write();
262 let mut funcs = EXT_FUNCTIONS.write();
263 let mut tops = EXT_TOPLEVELS.write();
264 let suffix: Vec<u8> = {
265 let mut s = vec![0];
266 s.extend_from_slice(&uri_bytes);
267 s
268 };
269 elems.retain(|k, _| !k.ends_with(&suffix));
270 funcs.retain(|k, _| !k.ends_with(&suffix));
271 tops.retain(|k, _| !k.ends_with(&suffix));
272 0
273}
274
275#[no_mangle]
283pub unsafe extern "C" fn xsltUnregisterExtModuleElement(
284 name: *const xmlChar,
285 URI: *const xmlChar,
286) -> c_int {
287 let Some(key) = ext_key(name, URI) else {
288 return -1;
289 };
290 if EXT_ELEMENTS.write().remove(&key).is_some() {
291 0
292 } else {
293 -1
294 }
295}
296
297#[no_mangle]
305pub unsafe extern "C" fn xsltUnregisterExtModuleFunction(
306 name: *const xmlChar,
307 URI: *const xmlChar,
308) -> c_int {
309 let Some(key) = ext_key(name, URI) else {
310 return -1;
311 };
312 if EXT_FUNCTIONS.write().remove(&key).is_some() {
313 0
314 } else {
315 -1
316 }
317}
318
319#[no_mangle]
327pub unsafe extern "C" fn xsltUnregisterExtModuleTopLevel(
328 name: *const xmlChar,
329 URI: *const xmlChar,
330) -> c_int {
331 let Some(key) = ext_key(name, URI) else {
332 return -1;
333 };
334 if EXT_TOPLEVELS.write().remove(&key).is_some() {
335 0
336 } else {
337 -1
338 }
339}
340
341#[no_mangle]
351pub unsafe extern "C" fn xsltRegisterExtPrefix(
352 style: *mut _xsltStylesheet,
353 prefix: *const xmlChar,
354 URI: *const xmlChar,
355) -> c_int {
356 if style.is_null() || prefix.is_null() || URI.is_null() {
357 return -1;
358 }
359 let mut cur = (*style).extInfos as *mut ExtPrefixEntry;
362 while !cur.is_null() {
363 if !(*cur).prefix.is_null()
364 && libc::strcmp((*cur).prefix as *const c_char, prefix as *const c_char) == 0
365 {
366 let new_uri =
368 crate::abi::allocator::xmlMemStrdupImpl(URI as *const c_char) as *mut c_char;
369 if new_uri.is_null() {
370 return -1;
371 }
372 xmlFreeImpl((*cur).uri as *mut c_void);
373 (*cur).uri = new_uri;
374 return 0;
375 }
376 cur = (*cur).next;
377 }
378 let entry = xmlMallocImpl(size_of::<ExtPrefixEntry>()) as *mut ExtPrefixEntry;
379 if entry.is_null() {
380 return -1;
381 }
382 let p = crate::abi::allocator::xmlMemStrdupImpl(prefix as *const c_char) as *mut c_char;
383 let u = crate::abi::allocator::xmlMemStrdupImpl(URI as *const c_char) as *mut c_char;
384 if p.is_null() || u.is_null() {
385 if !p.is_null() {
386 xmlFreeImpl(p as *mut c_void);
387 }
388 if !u.is_null() {
389 xmlFreeImpl(u as *mut c_void);
390 }
391 xmlFreeImpl(entry as *mut c_void);
392 return -1;
393 }
394 ptr::write(
395 entry,
396 ExtPrefixEntry {
397 next: (*style).extInfos as *mut ExtPrefixEntry,
398 prefix: p,
399 uri: u,
400 },
401 );
402 (*style).extInfos = entry as *mut c_void;
403 0
404}
405
406#[no_mangle]
416pub unsafe extern "C" fn xsltCheckExtPrefix(
417 style: *mut _xsltStylesheet,
418 prefix: *const xmlChar,
419) -> c_int {
420 if style.is_null() || prefix.is_null() {
421 return 0;
422 }
423 let mut cur = (*style).extInfos as *mut ExtPrefixEntry;
424 while !cur.is_null() {
425 if !(*cur).prefix.is_null()
426 && libc::strcmp((*cur).prefix as *const c_char, prefix as *const c_char) == 0
427 {
428 return 1;
429 }
430 cur = (*cur).next;
431 }
432 0
433}
434
435#[no_mangle]
444pub unsafe extern "C" fn xsltCheckExtURI(
445 style: *mut _xsltStylesheet,
446 URI: *const xmlChar,
447) -> c_int {
448 if style.is_null() || URI.is_null() {
449 return 0;
450 }
451 let mut cur = (*style).extInfos as *mut ExtPrefixEntry;
452 while !cur.is_null() {
453 if !(*cur).uri.is_null()
454 && libc::strcmp((*cur).uri as *const c_char, URI as *const c_char) == 0
455 {
456 return 1;
457 }
458 cur = (*cur).next;
459 }
460 0
461}
462
463#[no_mangle]
475pub unsafe extern "C" fn xsltExtElementLookup(
476 ctxt: *mut _xsltTransformContext,
477 name: *const xmlChar,
478 URI: *const xmlChar,
479) -> *mut c_void {
480 if ctxt.is_null() || name.is_null() || URI.is_null() {
481 return ptr::null_mut();
482 }
483 let found = crate::xslt::extensions::xsltFindExtElement(ctxt, name, URI);
485 if !found.is_null() {
486 return found;
487 }
488 let Some(key) = ext_key(name, URI) else {
489 return ptr::null_mut();
490 };
491 EXT_ELEMENTS
492 .read()
493 .get(&key)
494 .map(|e| e.transform as *mut c_void)
495 .unwrap_or(ptr::null_mut())
496}
497
498#[no_mangle]
507pub unsafe extern "C" fn xsltExtModuleElementLookup(
508 name: *const xmlChar,
509 URI: *const xmlChar,
510) -> *mut c_void {
511 let Some(key) = ext_key(name, URI) else {
512 return ptr::null_mut();
513 };
514 EXT_ELEMENTS
515 .read()
516 .get(&key)
517 .map(|e| e.transform as *mut c_void)
518 .unwrap_or(ptr::null_mut())
519}
520
521#[no_mangle]
530pub unsafe extern "C" fn xsltExtModuleFunctionLookup(
531 name: *const xmlChar,
532 URI: *const xmlChar,
533) -> *mut c_void {
534 let Some(key) = ext_key(name, URI) else {
535 return ptr::null_mut();
536 };
537 EXT_FUNCTIONS
538 .read()
539 .get(&key)
540 .copied()
541 .map(|p| p as *mut c_void)
542 .unwrap_or(ptr::null_mut())
543}
544
545#[no_mangle]
554pub unsafe extern "C" fn xsltExtModuleElementPreComputeLookup(
555 name: *const xmlChar,
556 URI: *const xmlChar,
557) -> *mut c_void {
558 let Some(key) = ext_key(name, URI) else {
559 return ptr::null_mut();
560 };
561 EXT_ELEMENTS
562 .read()
563 .get(&key)
564 .map(|e| e.precomp as *mut c_void)
565 .unwrap_or(ptr::null_mut())
566}
567
568#[no_mangle]
577pub unsafe extern "C" fn xsltExtModuleTopLevelLookup(
578 name: *const xmlChar,
579 URI: *const xmlChar,
580) -> *mut c_void {
581 let Some(key) = ext_key(name, URI) else {
582 return ptr::null_mut();
583 };
584 EXT_TOPLEVELS
585 .read()
586 .get(&key)
587 .copied()
588 .map(|p| p as *mut c_void)
589 .unwrap_or(ptr::null_mut())
590}
591
592#[no_mangle]
603pub unsafe extern "C" fn xsltInitCtxtExts(ctxt: *mut _xsltTransformContext) -> c_int {
604 if ctxt.is_null() || (*ctxt).style.is_null() {
605 return 0;
606 }
607 let style = (*ctxt).style;
608 let mut cur = (*style).extInfos as *mut ExtPrefixEntry;
609 while !cur.is_null() {
610 if !(*cur).uri.is_null() {
611 let key = CStr::from_ptr((*cur).uri as *const c_char)
612 .to_bytes()
613 .to_vec();
614 if let Some(module) = EXT_MODULES.read().get(&key).copied() {
615 if let Some(init) = module.init_func {
616 let data = init(ctxt, (*cur).uri as *const xmlChar);
617 if data.is_null() {
618 return -1;
619 }
620 let entry = xmlMallocImpl(size_of::<ExtDataEntry>()) as *mut ExtDataEntry;
622 if entry.is_null() {
623 return -1;
624 }
625 let u = crate::abi::allocator::xmlMemStrdupImpl((*cur).uri as *const c_char)
626 as *mut c_char;
627 if u.is_null() {
628 xmlFreeImpl(entry as *mut c_void);
629 return -1;
630 }
631 ptr::write(
632 entry,
633 ExtDataEntry {
634 next: (*ctxt).extInfos as *mut ExtDataEntry,
635 uri: u,
636 data,
637 },
638 );
639 (*ctxt).extInfos = entry as *mut c_void;
640 }
641 }
642 }
643 cur = (*cur).next;
644 }
645 0
646}
647
648#[no_mangle]
657pub unsafe extern "C" fn xsltShutdownCtxtExts(ctxt: *mut _xsltTransformContext) {
658 if ctxt.is_null() {
659 return;
660 }
661 let mut cur = (*ctxt).extInfos as *mut ExtDataEntry;
662 while !cur.is_null() {
663 if !(*cur).uri.is_null() {
664 let key = CStr::from_ptr((*cur).uri as *const c_char)
665 .to_bytes()
666 .to_vec();
667 if let Some(module) = EXT_MODULES.read().get(&key).copied() {
668 if let Some(shutdown) = module.shutdown_func {
669 shutdown(ctxt, (*cur).uri as *const xmlChar, (*cur).data);
670 }
671 }
672 }
673 cur = (*cur).next;
674 }
675}
676
677#[no_mangle]
685pub unsafe extern "C" fn xsltFreeCtxtExts(ctxt: *mut _xsltTransformContext) {
686 if ctxt.is_null() {
687 return;
688 }
689 let mut cur = (*ctxt).extInfos as *mut ExtDataEntry;
690 (*ctxt).extInfos = ptr::null_mut();
691 while !cur.is_null() {
692 let next = (*cur).next;
693 if !(*cur).uri.is_null() {
694 xmlFreeImpl((*cur).uri as *mut c_void);
695 }
696 xmlFreeImpl(cur as *mut c_void);
697 cur = next;
698 }
699}
700
701#[no_mangle]
709pub unsafe extern "C" fn xsltGetExtData(
710 ctxt: *mut _xsltTransformContext,
711 URI: *const xmlChar,
712) -> *mut c_void {
713 if ctxt.is_null() || URI.is_null() {
714 return ptr::null_mut();
715 }
716 let mut cur = (*ctxt).extInfos as *mut ExtDataEntry;
717 while !cur.is_null() {
718 if !(*cur).uri.is_null()
719 && libc::strcmp((*cur).uri as *const c_char, URI as *const c_char) == 0
720 {
721 return (*cur).data;
722 }
723 cur = (*cur).next;
724 }
725 ptr::null_mut()
726}
727
728#[no_mangle]
737pub unsafe extern "C" fn xsltStyleGetExtData(
738 style: *mut _xsltStylesheet,
739 URI: *const xmlChar,
740) -> *mut c_void {
741 if style.is_null() || URI.is_null() {
742 return ptr::null_mut();
743 }
744 let mut cur = (*style).extInfos as *mut ExtDataEntry;
745 while !cur.is_null() {
746 if !(*cur).uri.is_null()
747 && libc::strcmp((*cur).uri as *const c_char, URI as *const c_char) == 0
748 {
749 return (*cur).data;
750 }
751 cur = (*cur).next;
752 }
753 let key = CStr::from_ptr(URI as *const c_char).to_bytes().to_vec();
754 let module = EXT_MODULES.read().get(&key).copied();
755 let data = match module {
756 Some(m) => match m.style_init_func {
757 Some(init) => init(style, URI),
758 None => ptr::null_mut(),
759 },
760 None => ptr::null_mut(),
761 };
762 let entry = xmlMallocImpl(size_of::<ExtDataEntry>()) as *mut ExtDataEntry;
763 if entry.is_null() {
764 return ptr::null_mut();
765 }
766 let u = crate::abi::allocator::xmlMemStrdupImpl(URI as *const c_char) as *mut c_char;
767 if u.is_null() {
768 xmlFreeImpl(entry as *mut c_void);
769 return ptr::null_mut();
770 }
771 ptr::write(
772 entry,
773 ExtDataEntry {
774 next: (*style).extInfos as *mut ExtDataEntry,
775 uri: u,
776 data,
777 },
778 );
779 (*style).extInfos = entry as *mut c_void;
780 data
781}
782
783#[no_mangle]
795pub unsafe extern "C" fn xsltStyleStylesheetLevelGetExtData(
796 style: *mut _xsltStylesheet,
797 URI: *const xmlChar,
798) -> *mut c_void {
799 unsafe { xsltStyleGetExtData(style, URI) }
800}
801
802#[no_mangle]
811pub unsafe extern "C" fn xsltGetExtInfo(
812 style: *mut _xsltStylesheet,
813 _URI: *const xmlChar,
814) -> *mut c_void {
815 if style.is_null() {
816 return ptr::null_mut();
817 }
818 (*style).extInfos
819}
820
821#[no_mangle]
830pub unsafe extern "C" fn xsltRegisterAllExtras() {
831 xsltRegisterExtModule(
835 b"http://exslt.org/common\0".as_ptr() as *const xmlChar,
836 None,
837 None,
838 );
839}
840
841#[no_mangle]
850pub unsafe extern "C" fn xsltRegisterExtras(ctxt: *mut _xsltTransformContext) {
851 if ctxt.is_null() || (*ctxt).xpathCtxt.is_null() {
852 return;
853 }
854 crate::abi::exports_xslt_functions::xsltRegisterAllFunctions((*ctxt).xpathCtxt);
855}
856
857#[no_mangle]
866pub unsafe extern "C" fn xsltRegisterAllElement(ctxt: *mut _xsltTransformContext) {
867 if ctxt.is_null() {
868 return;
869 }
870 }
874
875#[no_mangle]
884pub unsafe extern "C" fn xsltRegisterTestModule() {
885 xsltRegisterExtModule(
886 b"http://xmlsoft.org/XSLT/\0".as_ptr() as *const xmlChar,
887 None,
888 None,
889 );
890}
891
892#[repr(C)]
897pub struct ExtPrefixEntry {
898 pub next: *mut ExtPrefixEntry,
899 pub prefix: *mut c_char,
900 pub uri: *mut c_char,
901}
902
903#[repr(C)]
905pub struct ExtDataEntry {
906 pub next: *mut ExtDataEntry,
907 pub uri: *mut c_char,
908 pub data: *mut c_void,
909}