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 = crate::abi::allocator::xmlMemStrdupImpl(URI as *const c_char) as *mut c_char;
368 if new_uri.is_null() {
369 return -1;
370 }
371 xmlFreeImpl((*cur).uri as *mut c_void);
372 (*cur).uri = new_uri;
373 return 0;
374 }
375 cur = (*cur).next;
376 }
377 let entry = xmlMallocImpl(size_of::<ExtPrefixEntry>()) as *mut ExtPrefixEntry;
378 if entry.is_null() {
379 return -1;
380 }
381 let p = crate::abi::allocator::xmlMemStrdupImpl(prefix as *const c_char) as *mut c_char;
382 let u = crate::abi::allocator::xmlMemStrdupImpl(URI as *const c_char) as *mut c_char;
383 if p.is_null() || u.is_null() {
384 if !p.is_null() {
385 xmlFreeImpl(p as *mut c_void);
386 }
387 if !u.is_null() {
388 xmlFreeImpl(u as *mut c_void);
389 }
390 xmlFreeImpl(entry as *mut c_void);
391 return -1;
392 }
393 ptr::write(
394 entry,
395 ExtPrefixEntry {
396 next: (*style).extInfos as *mut ExtPrefixEntry,
397 prefix: p,
398 uri: u,
399 },
400 );
401 (*style).extInfos = entry as *mut c_void;
402 0
403}
404
405#[no_mangle]
415pub unsafe extern "C" fn xsltCheckExtPrefix(
416 style: *mut _xsltStylesheet,
417 prefix: *const xmlChar,
418) -> c_int {
419 if style.is_null() || prefix.is_null() {
420 return 0;
421 }
422 let mut cur = (*style).extInfos as *mut ExtPrefixEntry;
423 while !cur.is_null() {
424 if !(*cur).prefix.is_null()
425 && libc::strcmp((*cur).prefix as *const c_char, prefix as *const c_char) == 0
426 {
427 return 1;
428 }
429 cur = (*cur).next;
430 }
431 0
432}
433
434#[no_mangle]
443pub unsafe extern "C" fn xsltCheckExtURI(
444 style: *mut _xsltStylesheet,
445 URI: *const xmlChar,
446) -> c_int {
447 if style.is_null() || URI.is_null() {
448 return 0;
449 }
450 let mut cur = (*style).extInfos as *mut ExtPrefixEntry;
451 while !cur.is_null() {
452 if !(*cur).uri.is_null()
453 && libc::strcmp((*cur).uri as *const c_char, URI as *const c_char) == 0
454 {
455 return 1;
456 }
457 cur = (*cur).next;
458 }
459 0
460}
461
462#[no_mangle]
474pub unsafe extern "C" fn xsltExtElementLookup(
475 ctxt: *mut _xsltTransformContext,
476 name: *const xmlChar,
477 URI: *const xmlChar,
478) -> *mut c_void {
479 if ctxt.is_null() || name.is_null() || URI.is_null() {
480 return ptr::null_mut();
481 }
482 let found = crate::xslt::extensions::xsltFindExtElement(ctxt, name, URI);
484 if !found.is_null() {
485 return found;
486 }
487 let Some(key) = ext_key(name, URI) else {
488 return ptr::null_mut();
489 };
490 EXT_ELEMENTS
491 .read()
492 .get(&key)
493 .map(|e| e.transform as *mut c_void)
494 .unwrap_or(ptr::null_mut())
495}
496
497#[no_mangle]
506pub unsafe extern "C" fn xsltExtModuleElementLookup(
507 name: *const xmlChar,
508 URI: *const xmlChar,
509) -> *mut c_void {
510 let Some(key) = ext_key(name, URI) else {
511 return ptr::null_mut();
512 };
513 EXT_ELEMENTS
514 .read()
515 .get(&key)
516 .map(|e| e.transform as *mut c_void)
517 .unwrap_or(ptr::null_mut())
518}
519
520#[no_mangle]
529pub unsafe extern "C" fn xsltExtModuleFunctionLookup(
530 name: *const xmlChar,
531 URI: *const xmlChar,
532) -> *mut c_void {
533 let Some(key) = ext_key(name, URI) else {
534 return ptr::null_mut();
535 };
536 EXT_FUNCTIONS
537 .read()
538 .get(&key)
539 .copied()
540 .map(|p| p as *mut c_void)
541 .unwrap_or(ptr::null_mut())
542}
543
544#[no_mangle]
553pub unsafe extern "C" fn xsltExtModuleElementPreComputeLookup(
554 name: *const xmlChar,
555 URI: *const xmlChar,
556) -> *mut c_void {
557 let Some(key) = ext_key(name, URI) else {
558 return ptr::null_mut();
559 };
560 EXT_ELEMENTS
561 .read()
562 .get(&key)
563 .map(|e| e.precomp as *mut c_void)
564 .unwrap_or(ptr::null_mut())
565}
566
567#[no_mangle]
576pub unsafe extern "C" fn xsltExtModuleTopLevelLookup(
577 name: *const xmlChar,
578 URI: *const xmlChar,
579) -> *mut c_void {
580 let Some(key) = ext_key(name, URI) else {
581 return ptr::null_mut();
582 };
583 EXT_TOPLEVELS
584 .read()
585 .get(&key)
586 .copied()
587 .map(|p| p as *mut c_void)
588 .unwrap_or(ptr::null_mut())
589}
590
591#[no_mangle]
602pub unsafe extern "C" fn xsltInitCtxtExts(ctxt: *mut _xsltTransformContext) -> c_int {
603 if ctxt.is_null() || (*ctxt).style.is_null() {
604 return 0;
605 }
606 let style = (*ctxt).style;
607 let mut cur = (*style).extInfos as *mut ExtPrefixEntry;
608 while !cur.is_null() {
609 if !(*cur).uri.is_null() {
610 let key = CStr::from_ptr((*cur).uri as *const c_char)
611 .to_bytes()
612 .to_vec();
613 if let Some(module) = EXT_MODULES.read().get(&key).copied() {
614 if let Some(init) = module.init_func {
615 let data = init(ctxt, (*cur).uri as *const xmlChar);
616 if data.is_null() {
617 return -1;
618 }
619 let entry = xmlMallocImpl(size_of::<ExtDataEntry>()) as *mut ExtDataEntry;
621 if entry.is_null() {
622 return -1;
623 }
624 let u = crate::abi::allocator::xmlMemStrdupImpl((*cur).uri as *const c_char)
625 as *mut c_char;
626 if u.is_null() {
627 xmlFreeImpl(entry as *mut c_void);
628 return -1;
629 }
630 ptr::write(
631 entry,
632 ExtDataEntry {
633 next: (*ctxt).extInfos as *mut ExtDataEntry,
634 uri: u,
635 data,
636 },
637 );
638 (*ctxt).extInfos = entry as *mut c_void;
639 }
640 }
641 }
642 cur = (*cur).next;
643 }
644 0
645}
646
647#[no_mangle]
656pub unsafe extern "C" fn xsltShutdownCtxtExts(ctxt: *mut _xsltTransformContext) {
657 if ctxt.is_null() {
658 return;
659 }
660 let mut cur = (*ctxt).extInfos as *mut ExtDataEntry;
661 while !cur.is_null() {
662 if !(*cur).uri.is_null() {
663 let key = CStr::from_ptr((*cur).uri as *const c_char)
664 .to_bytes()
665 .to_vec();
666 if let Some(module) = EXT_MODULES.read().get(&key).copied() {
667 if let Some(shutdown) = module.shutdown_func {
668 shutdown(ctxt, (*cur).uri as *const xmlChar, (*cur).data);
669 }
670 }
671 }
672 cur = (*cur).next;
673 }
674}
675
676#[no_mangle]
684pub unsafe extern "C" fn xsltFreeCtxtExts(ctxt: *mut _xsltTransformContext) {
685 if ctxt.is_null() {
686 return;
687 }
688 let mut cur = (*ctxt).extInfos as *mut ExtDataEntry;
689 (*ctxt).extInfos = ptr::null_mut();
690 while !cur.is_null() {
691 let next = (*cur).next;
692 if !(*cur).uri.is_null() {
693 xmlFreeImpl((*cur).uri as *mut c_void);
694 }
695 xmlFreeImpl(cur as *mut c_void);
696 cur = next;
697 }
698}
699
700#[no_mangle]
708pub unsafe extern "C" fn xsltGetExtData(
709 ctxt: *mut _xsltTransformContext,
710 URI: *const xmlChar,
711) -> *mut c_void {
712 if ctxt.is_null() || URI.is_null() {
713 return ptr::null_mut();
714 }
715 let mut cur = (*ctxt).extInfos as *mut ExtDataEntry;
716 while !cur.is_null() {
717 if !(*cur).uri.is_null()
718 && libc::strcmp((*cur).uri as *const c_char, URI as *const c_char) == 0
719 {
720 return (*cur).data;
721 }
722 cur = (*cur).next;
723 }
724 ptr::null_mut()
725}
726
727#[no_mangle]
736pub unsafe extern "C" fn xsltStyleGetExtData(
737 style: *mut _xsltStylesheet,
738 URI: *const xmlChar,
739) -> *mut c_void {
740 if style.is_null() || URI.is_null() {
741 return ptr::null_mut();
742 }
743 let mut cur = (*style).extInfos as *mut ExtDataEntry;
744 while !cur.is_null() {
745 if !(*cur).uri.is_null()
746 && libc::strcmp((*cur).uri as *const c_char, URI as *const c_char) == 0
747 {
748 return (*cur).data;
749 }
750 cur = (*cur).next;
751 }
752 let key = CStr::from_ptr(URI as *const c_char).to_bytes().to_vec();
753 let module = EXT_MODULES.read().get(&key).copied();
754 let data = match module {
755 Some(m) => match m.style_init_func {
756 Some(init) => init(style, URI),
757 None => ptr::null_mut(),
758 },
759 None => ptr::null_mut(),
760 };
761 let entry = xmlMallocImpl(size_of::<ExtDataEntry>()) as *mut ExtDataEntry;
762 if entry.is_null() {
763 return ptr::null_mut();
764 }
765 let u = crate::abi::allocator::xmlMemStrdupImpl(URI as *const c_char) as *mut c_char;
766 if u.is_null() {
767 xmlFreeImpl(entry as *mut c_void);
768 return ptr::null_mut();
769 }
770 ptr::write(
771 entry,
772 ExtDataEntry {
773 next: (*style).extInfos as *mut ExtDataEntry,
774 uri: u,
775 data,
776 },
777 );
778 (*style).extInfos = entry as *mut c_void;
779 data
780}
781
782#[no_mangle]
791pub unsafe extern "C" fn xsltGetExtInfo(
792 style: *mut _xsltStylesheet,
793 _URI: *const xmlChar,
794) -> *mut c_void {
795 if style.is_null() {
796 return ptr::null_mut();
797 }
798 (*style).extInfos
799}
800
801#[no_mangle]
810pub unsafe extern "C" fn xsltRegisterAllExtras() {
811 xsltRegisterExtModule(
815 b"http://exslt.org/common\0".as_ptr() as *const xmlChar,
816 None,
817 None,
818 );
819}
820
821#[no_mangle]
830pub unsafe extern "C" fn xsltRegisterExtras(ctxt: *mut _xsltTransformContext) {
831 if ctxt.is_null() || (*ctxt).xpathCtxt.is_null() {
832 return;
833 }
834 crate::abi::exports_xslt_functions::xsltRegisterAllFunctions((*ctxt).xpathCtxt);
835}
836
837#[no_mangle]
846pub unsafe extern "C" fn xsltRegisterAllElement(ctxt: *mut _xsltTransformContext) {
847 if ctxt.is_null() {
848 return;
849 }
850 }
854
855#[no_mangle]
864pub unsafe extern "C" fn xsltRegisterTestModule() {
865 xsltRegisterExtModule(
866 b"http://xmlsoft.org/XSLT/\0".as_ptr() as *const xmlChar,
867 None,
868 None,
869 );
870}
871
872#[repr(C)]
877pub struct ExtPrefixEntry {
878 pub next: *mut ExtPrefixEntry,
879 pub prefix: *mut c_char,
880 pub uri: *mut c_char,
881}
882
883#[repr(C)]
885pub struct ExtDataEntry {
886 pub next: *mut ExtDataEntry,
887 pub uri: *mut c_char,
888 pub data: *mut c_void,
889}