libxml_rs/abi/data_globals.rs
1//! Exported C data globals — the classic libxml2/libxslt public variables
2//! (11.1-G data-ABI closure, residual R-000135).
3//!
4//! Downstream C code reads and writes these symbols directly
5//! (e.g. `xmlDoValidityCheckingDefaultValue = 1;`), so they must exist with
6//! upstream names, types and defaults. The parser-default accessors in
7//! `src/xml/globals/mod.rs` read and write the SAME statics (single source
8//! of truth): a C write is immediately observable by the candidate parser.
9//!
10//! Defaults match upstream `globals.c` (libxml2 2.15.3) and `xslt.c`
11//! (libxslt 1.1.45).
12//!
13//! # SAFETY
14//!
15//! `static mut` globals are unsafe to touch from Rust; every access goes
16//! through the accessor functions in `crate::xml::globals` (or directly
17//! here with an explicit safety note). C accesses are inherently racy in
18//! upstream too — upstream documents these globals as deprecated and
19//! not thread-safe.
20//!
21//! # Upstream contract
22//!
23//! The parity target is upstream `globals.c` (libxml2 2.15.3), `SAX2.c` and
24//! `chvalid.c`, plus the libxslt `xslt.c` globals. Every symbol here is
25//! exported `#[no_mangle]` as DATA with the upstream name, type and default so
26//! downstream C code that reads or writes the classic global variables links
27//! and observes the same values (R-000135 data-globals closure).
28//!
29//! # Conceptual behavior
30//!
31//! This module implements the exported C data-global surface: parser defaults
32//! (`xmlDoValidityCheckingDefaultValue`, `xmlKeepBlanksDefaultValue`, ...),
33//! the error handler/context slots (`xmlGenericError`, `xmlStructuredError`,
34//! `xmlLastError`), the default SAX handler structs and locator, the version
35//! data symbols and the allocator function pointers. The parser-default
36//! accessors in `src/xml/globals/mod.rs` read and write the SAME statics
37//! (single source of truth), so a C write is immediately observable by the
38//! candidate parser.
39//!
40//! # Ownership & safety invariants
41//!
42//! `static mut` globals are inherently racy — upstream documents them as
43//! deprecated and not thread-safe, and the candidate preserves that C-visible
44//! contract. Internal writers are serialized where corruption is possible: the
45//! `xmlLastError` mirror deep-copy/free is guarded by `LAST_ERROR_MIRROR_LOCK`
46//! (R-000170) and the handler-slot pairs are read and written atomically
47//! (R-000171), while C readers keep upstreams documented racy semantics.
48//!
49//! # Historical quirks & epochs
50//!
51//! R-000135 (11.1-I): 11 exported data symbols were missing entirely and are
52//! now byte-identical with the oracle DSO. R-000161 (11.1-K): `xmlGenericError`
53//! / `xsltGenericError` default to the variadic stderr printers
54//! (`xmlGenericErrorDefaultFunc` / `xsltGenericErrorDefaultFunc`) via the asm
55//! va_list shims, and five default values were wrong before that fix
56//! (e.g. `xmlLineNumbersDefaultValue` 0 to 1, `xmlTreeIndentString` NULL to a
57//! two-space string). R-000167 (11.1-S): `xsltLibxsltVersion` and friends are
58//! exported as data (R) not functions (T). R-000162 (11.1-L): the allocator
59//! entry points are DATA function pointers. R-000170/R-000171 (11.1-X): the
60//! parallel-suite corruption races.
61//!
62//! # Deliberate oddities
63//!
64//! The candidates internal writers are locked while the exported symbols stay
65//! racy — a deliberate safe divergence that keeps the C-visible contract
66//! identical to upstream while making the Rust internals safe.
67//! `xsltDocDefaultLoader` defaults to NULL instead of upstreams variadic
68//! default function (stable Rust cannot define variadic extern fns) —
69//! documented safe divergence from R-000135.
70//!
71//! # Proving courts
72//!
73//! The DATA-GLOBALS-001 differential court (tools/abi/data_globals_probe.py
74//! and courts/suites/data-abi/data-globals-probe.c) compiles one probe
75//! against the oracle DSO and the candidate and requires byte-identical
76//! output; the ABI-DATA, ALLOCATOR, GLOBAL-STATE and THREADING families
77//! plus DSO-LOADER (symbol-type parity) and HEADER-COMPILE also cover this
78//! module.
79//!
80//! # Tempting simplifications that would break parity
81//!
82//! A tempting simplification is to keep parser defaults only in Rust atomics
83//! and skip the exported C globals — that was the pre-R-000135 state and it
84//! breaks every downstream consumer that reads or writes
85//! `xmlDoValidityCheckingDefaultValue` directly (the link would fail).
86//! Another tempting shortcut is dropping the mirror locks: R-000170 observed
87//! glibc `double free or corruption` aborts in about 12% of parallel runs, so
88//! the locks must not be removed even though upstream is racy.
89
90use core::ffi::c_void;
91use std::os::raw::{c_char, c_int, c_uint};
92
93use crate::abi::callbacks::{xmlGenericErrorFunc, xmlStructuredErrorFunc};
94use crate::abi::types::xmlChar;
95
96/// Serializes writes to the exported `xmlLastError` mirror.
97///
98/// Upstream's `xmlLastError` is a bare racy global (deprecated, documented
99/// not thread-safe), and the candidate preserves the C-visible semantics:
100/// downstream C consumers read the symbol directly without a lock. The
101/// candidate's internal deep-copy/free writers are serialized here so that
102/// concurrent error raises on different threads can never free the same
103/// mirror string twice or write while another thread is freeing — observed
104/// as heap corruption (`double free or corruption (!prev)`) in the parallel
105/// lib test suite (xml::errors tests racing with any other raising thread).
106static LAST_ERROR_MIRROR_LOCK: parking_lot::Mutex<()> = parking_lot::Mutex::new(());
107
108// ═══════════════════════════════════════════════════════════════════════════════
109// Parser defaults (upstream globals.c)
110// ═══════════════════════════════════════════════════════════════════════════════
111
112/// `int xmlDoValidityCheckingDefaultValue` (default 0)
113#[no_mangle]
114pub static mut xmlDoValidityCheckingDefaultValue: c_int = 0;
115
116/// `int xmlGetWarningsDefaultValue` (default 1)
117#[no_mangle]
118pub static mut xmlGetWarningsDefaultValue: c_int = 1;
119
120/// `int xmlLoadExtDtdDefaultValue` (default 0)
121#[no_mangle]
122pub static mut xmlLoadExtDtdDefaultValue: c_int = 0;
123
124/// `int xmlPedanticParserDefaultValue` (default 0)
125#[no_mangle]
126pub static mut xmlPedanticParserDefaultValue: c_int = 0;
127
128/// `int xmlLineNumbersDefaultValue` (default 1 — upstream globals.c
129/// `xmlLineNumbersDefaultValueThrDef = 1`)
130#[no_mangle]
131pub static mut xmlLineNumbersDefaultValue: c_int = 1;
132
133/// `int xmlKeepBlanksDefaultValue` (default 1)
134#[no_mangle]
135pub static mut xmlKeepBlanksDefaultValue: c_int = 1;
136
137/// `int xmlSubstituteEntitiesDefaultValue` (default 0)
138#[no_mangle]
139pub static mut xmlSubstituteEntitiesDefaultValue: c_int = 0;
140
141/// `int xmlParserDebugEntities` (default 0)
142#[no_mangle]
143pub static mut xmlParserDebugEntities: c_int = 0;
144
145/// `int xmlIndentTreeOutput` (default 1 — upstream globals.c
146/// `xmlIndentTreeOutputThrDef = 1`)
147#[no_mangle]
148pub static mut xmlIndentTreeOutput: c_int = 1;
149
150/// `const xmlChar *xmlTreeIndentString` (default " " — upstream globals.c
151/// `xmlTreeIndentStringThrDef = " "`)
152#[no_mangle]
153pub static mut xmlTreeIndentString: *const xmlChar = {
154 static S: [u8; 3] = *b" \0";
155 S.as_ptr()
156};
157
158/// `int xmlSaveNoEmptyTags` (default 0)
159#[no_mangle]
160pub static mut xmlSaveNoEmptyTags: c_int = 0;
161
162/// Upstream `xmlRegisterNodeFunc xmlRegisterNodeDefaultValue` (default NULL)
163#[no_mangle]
164pub static mut xmlRegisterNodeDefaultValue: Option<
165 unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode),
166> = None;
167
168/// Upstream static `xmlRegisterCallbacks` (tree.c): the gate that arms the
169/// node register/deregister hooks once a callback has been registered.
170pub static XML_REGISTER_CALLBACKS: core::sync::atomic::AtomicBool =
171 core::sync::atomic::AtomicBool::new(false);
172
173/// Upstream `xmlRegisterNodeCallback(node)` — invoke the registered node
174/// hook, gated by `xmlRegisterCallbacks` (tree.c).
175///
176/// # SAFETY
177///
178/// - `node` must be a valid, fully-initialised node or NULL.
179pub fn register_node_hook(node: *mut crate::abi::structs::_xmlNode) {
180 if !XML_REGISTER_CALLBACKS.load(core::sync::atomic::Ordering::Relaxed) {
181 return;
182 }
183 let hook = unsafe { xmlRegisterNodeDefaultValue };
184 if let Some(h) = hook {
185 if !node.is_null() {
186 // SAFETY: the hook is a valid C callback registered by the user.
187 unsafe { h(node) };
188 }
189 }
190}
191
192/// Upstream `xmlDeregisterNodeCallback(node)` — invoke the registered node
193/// deregister hook, gated by `xmlRegisterCallbacks` (tree.c).
194///
195/// # SAFETY
196///
197/// - `node` must be a valid node about to be freed, or NULL.
198pub fn deregister_node_hook(node: *mut crate::abi::structs::_xmlNode) {
199 if !XML_REGISTER_CALLBACKS.load(core::sync::atomic::Ordering::Relaxed) {
200 return;
201 }
202 let hook = unsafe { xmlDeregisterNodeDefaultValue };
203 if let Some(h) = hook {
204 if !node.is_null() {
205 // SAFETY: the hook is a valid C callback registered by the user.
206 unsafe { h(node) };
207 }
208 }
209}
210
211/// `xmlDeregisterNodeFunc xmlDeregisterNodeDefaultValue` (default NULL)
212#[no_mangle]
213pub static mut xmlDeregisterNodeDefaultValue: Option<
214 unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode),
215> = None;
216
217/// `const char *xmlParserVersion` — matched to the system oracle build
218/// (libxml2 2.15.3 GIT build: LIBXML_VERSION_STRING "21503" plus the
219/// upstream version extra "-GITv2.15.3").
220///
221/// SAFETY: the pointed-to string is a static, immutable, null-terminated
222/// literal; `static mut` is used because C raw pointers are not `Sync`.
223/// Reads/writes of the pointer itself are racy only if C code mutates it
224/// (upstream treats it as a constant).
225#[no_mangle]
226pub static mut xmlParserVersion: *const c_char = {
227 static V: [u8; 17] = *b"21503-GITv2.15.3\0";
228 V.as_ptr() as *const c_char
229};
230
231/// `int xmlParserMaxDepth` (default 256)
232#[no_mangle]
233pub static mut xmlParserMaxDepth: c_int = 256;
234
235// ═══════════════════════════════════════════════════════════════════════════════
236// Buffer globals (upstream tree.h / xmlIO.c)
237// ═══════════════════════════════════════════════════════════════════════════════
238
239/// `int xmlDefaultBufferSize` (default 4096)
240#[no_mangle]
241pub static mut xmlDefaultBufferSize: c_int = 4096;
242
243/// `xmlBufferAllocationScheme xmlBufferAllocScheme` (default XML_BUFFER_ALLOC_EXACT = 1;
244/// upstream globals.c `xmlBufferAllocSchemeThrDef = XML_BUFFER_ALLOC_EXACT`)
245#[no_mangle]
246pub static mut xmlBufferAllocScheme: c_int = 1;
247
248// ═══════════════════════════════════════════════════════════════════════════════
249// Error callback globals (upstream xmlerror.h)
250// ═══════════════════════════════════════════════════════════════════════════════
251
252/// `xmlGenericErrorFunc xmlGenericError` — the generic error callback.
253///
254/// Upstream defaults to `xmlGenericErrorDefaultFunc` (a variadic stderr
255/// printer, error.c); the candidate's shim below reproduces it, so a
256/// freshly-initialized library routes errors to stderr exactly like upstream.
257#[cfg(target_arch = "x86_64")]
258#[no_mangle]
259pub static mut xmlGenericError: Option<xmlGenericErrorFunc> = Some(XML_GENERIC_ERROR_DEFAULT);
260
261#[cfg(not(target_arch = "x86_64"))]
262#[no_mangle]
263pub static mut xmlGenericError: Option<xmlGenericErrorFunc> = None;
264
265/// `void *xmlGenericErrorContext` — context for the generic error callback.
266#[no_mangle]
267pub static mut xmlGenericErrorContext: *mut c_void = core::ptr::null_mut();
268
269/// `xmlStructuredErrorFunc xmlStructuredError` — the structured error callback.
270#[no_mangle]
271pub static mut xmlStructuredError: Option<xmlStructuredErrorFunc> = None;
272
273/// `void *xmlStructuredErrorContext` — context for the structured callback.
274#[no_mangle]
275pub static mut xmlStructuredErrorContext: *mut c_void = core::ptr::null_mut();
276
277// ═══════════════════════════════════════════════════════════════════════════════
278// Variadic default error handlers (upstream error.c / xsltutils.c)
279// ═══════════════════════════════════════════════════════════════════════════════
280//
281// Upstream `xmlGenericErrorDefaultFunc(void *ctx, const char *msg, ...)`
282// prints the formatted message to `xmlGenericErrorContext` (stderr when
283// NULL). Stable Rust cannot define a variadic extern fn body, so the ABI
284// entry is an x86_64 SysV inline-asm shim that materialises the caller's
285// register/stack arguments into a `va_list` and forwards to a non-variadic
286// receiver — the same pattern as `xsltTransformError`
287// (exports_xslt_util.rs) and the writer's `vfmt_shim!`. Neither default
288// function is a dynamic export upstream (both are internal); the exported
289// data globals merely point at them.
290
291/// System V AMD64 `__va_list_tag` (24 bytes) — same layout as the writer's
292/// shims and `exports_xslt_util.rs`.
293#[cfg(target_arch = "x86_64")]
294#[repr(C)]
295#[derive(Clone, Copy, Debug)]
296pub struct VaListTag {
297 gp_offset: c_uint,
298 fp_offset: c_uint,
299 overflow_arg_area: *mut c_void,
300 reg_save_area: *mut c_void,
301}
302
303#[cfg(target_arch = "x86_64")]
304unsafe extern "C" {
305 fn vfprintf(stream: *mut c_void, format: *const c_char, ap: *mut VaListTag) -> c_int;
306}
307
308/// The `stderr` FILE* — glibc exports the `stderr` data object, an
309/// 8-byte pointer variable whose value is `&_IO_2_1_stderr_`. Upstream
310/// `xmlGenericErrorDefaultFunc` defaults the error context to `stderr`;
311/// using the real stdio object (unbuffered, fd-2 relative) keeps writes
312/// byte-exact and honors fd-2 redirection, unlike a private `fdopen(2)`
313/// FILE* which is fully buffered and lands at exit on whatever fd 2 then
314/// points to.
315#[cfg(target_arch = "x86_64")]
316unsafe fn stderr_file() -> *mut c_void {
317 extern "C" {
318 static stderr: *mut c_void;
319 }
320 unsafe { stderr }
321}
322
323/// Variadic receiver for the `xmlGenericErrorDefaultFunc` shim (upstream
324/// error.c semantics: default the context to stderr, then `vfprintf`).
325///
326/// # SAFETY
327///
328/// - `_ctx`, `ap` must be valid pointers (or NULL
329/// where the upstream C contract allows), obtained from the
330/// matching constructor/owner and not yet freed; the callee may
331/// take or keep ownership exactly as the C API specifies.
332///
333/// - `msg` must point to valid NUL-terminated
334/// strings (or NULL where the C contract allows) for the lifetime
335/// of the call.
336///
337/// The caller must not race this call with concurrent mutation of the
338/// same objects from other threads (per-object state is not internally
339/// synchronized). Violating any of the above is undefined behavior.
340///
341/// Exercised by the C-API differential courts
342/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
343/// courts; those pass byte-for-byte against the upstream oracle.
344#[cfg(target_arch = "x86_64")]
345#[no_mangle]
346pub unsafe extern "C" fn xmlGenericErrorDefaultFuncV(
347 _ctx: *mut c_void,
348 msg: *const c_char,
349 ap: *mut VaListTag,
350) -> c_int {
351 unsafe {
352 if crate::abi::data_globals::xmlGenericErrorContext.is_null() {
353 crate::abi::data_globals::xmlGenericErrorContext = stderr_file();
354 }
355 let stream = crate::abi::data_globals::xmlGenericErrorContext;
356 if msg.is_null() || stream.is_null() {
357 return 0;
358 }
359 vfprintf(stream, msg, ap)
360 }
361}
362
363/// `xmlGenericErrorDefaultFunc(void *ctx, const char *msg, ...)` — the
364/// upstream default generic error handler. Not exported dynamically (matches
365/// upstream, where the symbol is internal).
366///
367/// 2 fixed args (ctx=rdi, msg=rsi) → `gp_offset` 16; the va_list pointer is
368/// passed as the 3rd arg (rdx) of the receiver.
369///
370/// # SAFETY
371///
372/// The function touches crate-global state only; it is safe
373/// as long as the caller respects the library's global
374/// initialization/cleanup ordering (xmlInitParser before use,
375/// xmlCleanupParser only after all users are done).
376///
377/// Violating the global lifecycle ordering, or calling this after
378/// teardown or from a signal handler, is undefined behavior.
379#[cfg(target_arch = "x86_64")]
380pub unsafe extern "C" fn xmlGenericErrorDefaultFunc() -> c_int {
381 unsafe {
382 core::arch::asm!(
383 "sub rsp, 240",
384 "mov [rsp+0], rdi",
385 "mov [rsp+8], rsi",
386 "mov [rsp+16], rdx",
387 "mov [rsp+24], rcx",
388 "mov [rsp+32], r8",
389 "mov [rsp+40], r9",
390 "movaps [rsp+48], xmm0",
391 "movaps [rsp+64], xmm1",
392 "movaps [rsp+80], xmm2",
393 "movaps [rsp+96], xmm3",
394 "movaps [rsp+112], xmm4",
395 "movaps [rsp+128], xmm5",
396 "movaps [rsp+144], xmm6",
397 "movaps [rsp+160], xmm7",
398 "mov dword ptr [rsp+176], 16",
399 "mov dword ptr [rsp+180], 48",
400 "lea rax, [rsp+256]",
401 "mov [rsp+184], rax",
402 "lea rax, [rsp]",
403 "mov [rsp+192], rax",
404 "lea rdx, [rsp+176]",
405 "call xmlGenericErrorDefaultFuncV",
406 "add rsp, 240",
407 "add rsp, 8",
408 "ret",
409 options(noreturn),
410 );
411 }
412}
413
414/// Default value of the exported `xmlGenericError` data global.
415#[cfg(target_arch = "x86_64")]
416const XML_GENERIC_ERROR_DEFAULT: xmlGenericErrorFunc = unsafe {
417 // SAFETY: the shim and the function-pointer type have identical ABI
418 // (a code pointer); the declared arity is a Rust-side fiction required
419 // to store a variadic entry in the non-variadic pointer type.
420 core::mem::transmute::<
421 unsafe extern "C" fn() -> c_int,
422 unsafe extern "C" fn(*mut c_void, *const c_char),
423 >(xmlGenericErrorDefaultFunc)
424};
425
426/// Variadic receiver for the `xsltGenericErrorDefaultFunc` shim (upstream
427/// xsltutils.c semantics: default the context to stderr, then `vfprintf`).
428///
429/// # SAFETY
430///
431/// - `_ctx`, `ap` must be valid pointers (or NULL
432/// where the upstream C contract allows), obtained from the
433/// matching constructor/owner and not yet freed; the callee may
434/// take or keep ownership exactly as the C API specifies.
435///
436/// - `msg` must point to valid NUL-terminated
437/// strings (or NULL where the C contract allows) for the lifetime
438/// of the call.
439///
440/// The caller must not race this call with concurrent mutation of the
441/// same objects from other threads (per-object state is not internally
442/// synchronized). Violating any of the above is undefined behavior.
443///
444/// Exercised by the C-API differential courts
445/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
446/// courts; those pass byte-for-byte against the upstream oracle.
447#[cfg(target_arch = "x86_64")]
448#[no_mangle]
449pub unsafe extern "C" fn xsltGenericErrorDefaultFuncV(
450 _ctx: *mut c_void,
451 msg: *const c_char,
452 ap: *mut VaListTag,
453) -> c_int {
454 unsafe {
455 if crate::abi::data_globals::xsltGenericErrorContext.is_null() {
456 crate::abi::data_globals::xsltGenericErrorContext = stderr_file();
457 }
458 let stream = crate::abi::data_globals::xsltGenericErrorContext;
459 if msg.is_null() || stream.is_null() {
460 return 0;
461 }
462 vfprintf(stream, msg, ap)
463 }
464}
465
466/// `xsltGenericErrorDefaultFunc(void *ctx, const char *msg, ...)` — the
467/// upstream default XSLT error handler (xsltutils.c).
468///
469/// # SAFETY
470///
471/// The function touches crate-global state only; it is safe
472/// as long as the caller respects the library's global
473/// initialization/cleanup ordering (xmlInitParser before use,
474/// xmlCleanupParser only after all users are done).
475///
476/// Violating the global lifecycle ordering, or calling this after
477/// teardown or from a signal handler, is undefined behavior.
478#[cfg(target_arch = "x86_64")]
479pub unsafe extern "C" fn xsltGenericErrorDefaultFunc() -> c_int {
480 unsafe {
481 core::arch::asm!(
482 "sub rsp, 240",
483 "mov [rsp+0], rdi",
484 "mov [rsp+8], rsi",
485 "mov [rsp+16], rdx",
486 "mov [rsp+24], rcx",
487 "mov [rsp+32], r8",
488 "mov [rsp+40], r9",
489 "movaps [rsp+48], xmm0",
490 "movaps [rsp+64], xmm1",
491 "movaps [rsp+80], xmm2",
492 "movaps [rsp+96], xmm3",
493 "movaps [rsp+112], xmm4",
494 "movaps [rsp+128], xmm5",
495 "movaps [rsp+144], xmm6",
496 "movaps [rsp+160], xmm7",
497 "mov dword ptr [rsp+176], 16",
498 "mov dword ptr [rsp+180], 48",
499 "lea rax, [rsp+256]",
500 "mov [rsp+184], rax",
501 "lea rax, [rsp]",
502 "mov [rsp+192], rax",
503 "lea rdx, [rsp+176]",
504 "call xsltGenericErrorDefaultFuncV",
505 "add rsp, 240",
506 "add rsp, 8",
507 "ret",
508 options(noreturn),
509 );
510 }
511}
512
513/// Default value of the exported `xsltGenericError` data global.
514#[cfg(target_arch = "x86_64")]
515const XSLT_GENERIC_ERROR_DEFAULT: xmlGenericErrorFunc = unsafe {
516 // SAFETY: as above — ABI-identical code pointer.
517 core::mem::transmute::<
518 unsafe extern "C" fn() -> c_int,
519 unsafe extern "C" fn(*mut c_void, *const c_char),
520 >(xsltGenericErrorDefaultFunc)
521};
522
523/// The built-in default generic error handler (upstream
524/// `xmlGenericErrorDefaultFunc`), for use when a caller resets the handler
525/// with NULL. Only available on x86_64 (the variadic shim is SysV-specific);
526/// on other targets there is no default (resets leave the handler unset).
527pub fn default_generic_error_func() -> Option<xmlGenericErrorFunc> {
528 #[cfg(target_arch = "x86_64")]
529 {
530 Some(XML_GENERIC_ERROR_DEFAULT)
531 }
532 #[cfg(not(target_arch = "x86_64"))]
533 {
534 None
535 }
536}
537
538// ═══════════════════════════════════════════════════════════════════════════════
539// Static strings (upstream xmlstring.h / tree.c)
540// ═══════════════════════════════════════════════════════════════════════════════
541
542/// `const xmlChar xmlStringText[]` — "text"
543#[no_mangle]
544pub static xmlStringText: [xmlChar; 5] = [b't', b'e', b'x', b't', 0];
545
546/// `const xmlChar xmlStringTextNoenc[]` — "textnoenc"
547#[no_mangle]
548pub static xmlStringTextNoenc: [xmlChar; 9] = *b"textnoenc";
549
550/// `const xmlChar xmlStringComment[]` — "comment"
551#[no_mangle]
552pub static xmlStringComment: [xmlChar; 8] = [b'c', b'o', b'm', b'm', b'e', b'n', b't', 0];
553
554// ═══════════════════════════════════════════════════════════════════════════════
555// XPath numeric constants (upstream xpath.c)
556// ═══════════════════════════════════════════════════════════════════════════════
557
558/// `double xmlXPathNAN` — NaN
559#[no_mangle]
560pub static xmlXPathNAN: f64 = f64::NAN;
561
562/// `double xmlXPathPINF` — +infinity
563#[no_mangle]
564pub static xmlXPathPINF: f64 = f64::INFINITY;
565
566/// `double xmlXPathNINF` — -infinity
567#[no_mangle]
568pub static xmlXPathNINF: f64 = f64::NEG_INFINITY;
569
570// ═══════════════════════════════════════════════════════════════════════════════
571// libxslt globals (upstream xslt.c / xsltutils.c / documents.c / xslt.h)
572// ═══════════════════════════════════════════════════════════════════════════════
573// xsltMaxDepth / xsltMaxVars are exported from src/xslt/transform/mod.rs
574// (they are read by the transform engine).
575
576/// `const int xsltLibxmlVersion` = 21501 — the libxml2 version the system
577/// libxslt 1.1.45 was compiled against (upstream xslt.c `LIBXML_VERSION`;
578/// the system libxslt was built against libxml2 2.15.1). Byte-parity with
579/// the oracle DSO, read directly by `xsltproc -V`.
580#[no_mangle]
581pub static xsltLibxmlVersion: c_int = 21501;
582
583/// `const int xsltLibxsltVersion` = 10145 — the libxslt version
584/// (upstream xslt.c `XSLTPUBVAR const int xsltLibxsltVersion = LIBXSLT_VERSION`;
585/// oracle DSO symbol type R). Was previously exported as a function (T)
586/// — R-000167.
587#[no_mangle]
588pub static xsltLibxsltVersion: c_int = 10145;
589
590/// `const char *xsltEngineVersion` = "10145-GITv1.1.45" — the libxslt engine
591/// version string (upstream xslt.c `XSLTPUBVAR const char *xsltEngineVersion`;
592/// oracle DSO symbol type D). Was previously exported as a function (T)
593/// — R-000167. `static mut` follows the xmlParserVersion pattern (raw
594/// pointers are not Sync).
595#[no_mangle]
596pub static mut xsltEngineVersion: *const c_char = {
597 static S: [u8; 17] = *b"10145-GITv1.1.45\0";
598 S.as_ptr() as *const c_char
599};
600
601/// `const char *exsltLibraryVersion` = "825-GITv1.1.45" — the libexslt
602/// library version string (upstream exslt.c `EXSLTPUBVAR const char *`;
603/// oracle DSO symbol type D). Read by `xsltproc -V`.
604#[no_mangle]
605pub static mut exsltLibraryVersion: *const c_char = {
606 static S: [u8; 15] = *b"825-GITv1.1.45\0";
607 S.as_ptr() as *const c_char
608};
609
610/// `const int exsltLibexsltVersion` = 825 — the libexslt version
611/// (upstream exslt.h `EXSLTPUBVAR const int`; oracle DSO symbol type R).
612#[no_mangle]
613pub static exsltLibexsltVersion: c_int = 825;
614
615/// `const int exsltLibxmlVersion` = 21501 — the libxml2 version the system
616/// libexslt 0.8.25 was compiled against (oracle DSO symbol type R).
617#[no_mangle]
618pub static exsltLibxmlVersion: c_int = 21501;
619
620/// `const int exsltLibxsltVersion` = 10145 — the libxslt version the system
621/// libexslt 0.8.25 was compiled against (oracle DSO symbol type R).
622#[no_mangle]
623pub static exsltLibxsltVersion: c_int = 10145;
624
625/// `int xslDebugStatus` — the libxslt debugger status (upstream xsltutils.c
626/// `XSLTPUBVAR int xslDebugStatus;`, default XSLT_DEBUG_NONE = 0; oracle DSO
627/// symbol type B). Written by `xsltSetDebuggerStatus`; read by the transform
628/// engine's profiling gates (R-000165 DATA_MISSING closure).
629#[no_mangle]
630pub static mut xslDebugStatus: c_int = 0;
631
632/// `xmlGenericErrorFunc xsltGenericError` — the libxslt error callback.
633/// Upstream defaults to `xsltGenericErrorDefaultFunc` (xsltutils.c, a variadic
634/// stderr printer); the candidate's shim below reproduces it (R-000135
635/// divergence now closed).
636#[cfg(target_arch = "x86_64")]
637#[no_mangle]
638pub static mut xsltGenericError: Option<xmlGenericErrorFunc> = Some(XSLT_GENERIC_ERROR_DEFAULT);
639
640#[cfg(not(target_arch = "x86_64"))]
641#[no_mangle]
642pub static mut xsltGenericError: Option<xmlGenericErrorFunc> = None;
643
644/// `void *xsltGenericErrorContext` (default NULL)
645#[no_mangle]
646pub static mut xsltGenericErrorContext: *mut c_void = core::ptr::null_mut();
647
648/// `void *xsltGenericDebugContext` (default NULL)
649#[no_mangle]
650pub static mut xsltGenericDebugContext: *mut c_void = core::ptr::null_mut();
651
652/// `xmlGenericErrorFunc xsltGenericDebug` — the libxslt debug callback.
653/// Upstream defaults to `xsltGenericDebugDefaultFunc` (xsltutils.c:632), a
654/// handler that writes the message to `xsltGenericDebugContext` (a FILE*)
655/// when that context is non-NULL. The candidate reproduces the default via
656/// `xsltGenericDebugDefaultFunc` below. Was previously exported as a
657/// FUNCTION (T) — an R-000167-class ABI type divergence closed in 11.1-Z.1
658/// (oracle DSO symbol type D, `nm -D /usr/lib/libxslt.so.1`).
659#[no_mangle]
660pub static mut xsltGenericDebug: Option<xmlGenericErrorFunc> = Some(XSLT_GENERIC_DEBUG_DEFAULT);
661
662/// Default debug handler reproducing upstream `xsltGenericDebugDefaultFunc`
663/// (xsltutils.c:621): no-op unless `xsltGenericDebugContext` is non-NULL,
664/// then the (pre-formatted) message is written to that context FILE*.
665/// Upstream's handler is variadic; the candidate's callers format the
666/// message before dispatch (same documented divergence as xsltGenericError).
667///
668/// # Safety
669///
670/// - `msg` must be NULL or a valid NUL-terminated string valid for the
671/// call; when `xsltGenericDebugContext` is non-NULL it must be a valid
672/// `FILE*` writable via `fwrite` for the message length.
673unsafe extern "C" fn xsltGenericDebugDefaultFunc(_ctx: *mut c_void, msg: *const c_char) {
674 if msg.is_null() {
675 return;
676 }
677 let dctx = unsafe { xsltGenericDebugContext };
678 if dctx.is_null() {
679 return;
680 }
681 let len = unsafe { libc::strlen(msg) };
682 unsafe {
683 libc::fwrite(msg as *const libc::c_void, 1, len, dctx as *mut libc::FILE);
684 }
685}
686
687/// Default value of the exported `xsltGenericDebug` data global.
688const XSLT_GENERIC_DEBUG_DEFAULT: xmlGenericErrorFunc = xsltGenericDebugDefaultFunc;
689
690/// `const xmlChar xsltExtMarker[]` — empty string used to mark extension
691/// nodes (upstream transform.c).
692#[no_mangle]
693pub static xsltExtMarker: [xmlChar; 1] = [0];
694
695/// `xsltDocLoaderFunc xsltDocDefaultLoader` — the document loader callback.
696/// Upstream defaults to `xsltDocDefaultLoaderFunc`; the candidate defaults
697/// to NULL and its internal loader path is used — documented safe
698/// divergence (residual R-000135).
699#[no_mangle]
700pub static mut xsltDocDefaultLoader: Option<
701 unsafe extern "C" fn(
702 *const xmlChar,
703 *mut c_void,
704 c_int,
705 *mut crate::abi::structs::_xsltStylesheet,
706 *mut crate::abi::structs::_xsltTransformContext,
707 ) -> *mut crate::abi::structs::_xmlDoc,
708> = None;
709
710// ═══════════════════════════════════════════════════════════════════════════════
711// I/O filename callback globals (upstream xmlIO.h)
712// ═══════════════════════════════════════════════════════════════════════════════
713
714/// `xmlParserInputBufferCreateFilenameFunc xmlParserInputBufferCreateFilenameValue`
715#[no_mangle]
716pub static mut xmlParserInputBufferCreateFilenameValue: Option<
717 unsafe extern "C" fn(*const c_char, c_int) -> *mut crate::abi::structs::_xmlParserInputBuffer,
718> = None;
719
720/// `xmlOutputBufferCreateFilenameFunc xmlOutputBufferCreateFilenameValue`
721#[no_mangle]
722pub static mut xmlOutputBufferCreateFilenameValue: Option<
723 unsafe extern "C" fn(
724 *const c_char,
725 crate::abi::structs::xmlCharEncodingHandlerPtr,
726 c_int,
727 ) -> *mut crate::abi::structs::_xmlOutputBuffer,
728> = None;
729
730// ═══════════════════════════════════════════════════════════════════════════════
731// Default SAX v1 handler structs + locator (upstream globals.c 2.15.3)
732// ═══════════════════════════════════════════════════════════════════════════════
733//
734// `const xmlSAXHandlerV1 xmlDefaultSAXHandler` and `htmlDefaultSAXHandler`
735// (parser.h / HTMLparser.h), plus `const xmlSAXLocator xmlDefaultSAXLocator`.
736// The handler instances reproduce the upstream initializer lists exactly
737// (globals.c); every referenced xmlSAX2* entry point is a real candidate
738// export.
739
740/// `const xmlSAXHandlerV1 xmlDefaultSAXHandler` (globals.c 2.15.3).
741#[no_mangle]
742pub static xmlDefaultSAXHandler: crate::abi::structs::_xmlSAXHandlerV1 =
743 crate::abi::structs::_xmlSAXHandlerV1 {
744 internalSubset: Some(crate::abi::exports_xml2::xmlSAX2InternalSubset),
745 isStandalone: Some(crate::abi::exports_xml2::xmlSAX2IsStandalone),
746 hasInternalSubset: Some(crate::abi::exports_xml2::xmlSAX2HasInternalSubset),
747 hasExternalSubset: Some(crate::abi::exports_xml2::xmlSAX2HasExternalSubset),
748 resolveEntity: Some(crate::abi::exports_xml2::xmlSAX2ResolveEntity),
749 getEntity: Some(crate::abi::exports_xml2::xmlSAX2GetEntity),
750 entityDecl: Some(crate::abi::exports_xml2::xmlSAX2EntityDecl),
751 notationDecl: Some(crate::abi::exports_xml2::xmlSAX2NotationDecl),
752 attributeDecl: Some(crate::abi::exports_xml2::xmlSAX2AttributeDecl),
753 elementDecl: Some(crate::abi::exports_xml2::xmlSAX2ElementDecl),
754 unparsedEntityDecl: Some(crate::abi::exports_xml2::xmlSAX2UnparsedEntityDecl),
755 setDocumentLocator: Some(crate::abi::exports_xml2::xmlSAX2SetDocumentLocator),
756 startDocument: Some(crate::abi::exports_xml2::xmlSAX2StartDocument),
757 endDocument: Some(crate::abi::exports_xml2::xmlSAX2EndDocument),
758 startElement: Some(crate::abi::exports_xml2::xmlSAX2StartElement),
759 endElement: Some(crate::abi::exports_xml2::xmlSAX2EndElement),
760 reference: Some(crate::abi::exports_xml2::xmlSAX2Reference),
761 characters: Some(crate::abi::exports_xml2::xmlSAX2Characters),
762 ignorableWhitespace: Some(crate::abi::exports_xml2::xmlSAX2IgnorableWhitespace),
763 processingInstruction: Some(crate::abi::exports_xml2::xmlSAX2ProcessingInstruction),
764 comment: Some(crate::abi::exports_xml2::xmlSAX2Comment),
765 warning: Some(crate::xml::errors::XML_PARSER_WARNING_SAX1),
766 error: Some(crate::xml::errors::XML_PARSER_ERROR_SAX1),
767 fatalError: Some(crate::xml::errors::XML_PARSER_ERROR_SAX1),
768 getParameterEntity: Some(crate::abi::exports_xml2::xmlSAX2GetParameterEntity),
769 cdataBlock: Some(crate::abi::exports_xml2::xmlSAX2CDataBlock),
770 externalSubset: Some(crate::abi::exports_xml2::xmlSAX2ExternalSubset),
771 initialized: 1,
772 };
773
774/// `const xmlSAXHandlerV1 htmlDefaultSAXHandler` (globals.c 2.15.3).
775#[no_mangle]
776pub static htmlDefaultSAXHandler: crate::abi::structs::_xmlSAXHandlerV1 =
777 crate::abi::structs::_xmlSAXHandlerV1 {
778 internalSubset: Some(crate::abi::exports_xml2::xmlSAX2InternalSubset),
779 isStandalone: None,
780 hasInternalSubset: None,
781 hasExternalSubset: None,
782 resolveEntity: None,
783 getEntity: Some(crate::abi::exports_xml2::xmlSAX2GetEntity),
784 entityDecl: None,
785 notationDecl: None,
786 attributeDecl: None,
787 elementDecl: None,
788 unparsedEntityDecl: None,
789 setDocumentLocator: Some(crate::abi::exports_xml2::xmlSAX2SetDocumentLocator),
790 startDocument: Some(crate::abi::exports_xml2::xmlSAX2StartDocument),
791 endDocument: Some(crate::abi::exports_xml2::xmlSAX2EndDocument),
792 startElement: Some(crate::abi::exports_xml2::xmlSAX2StartElement),
793 endElement: Some(crate::abi::exports_xml2::xmlSAX2EndElement),
794 reference: None,
795 characters: Some(crate::abi::exports_xml2::xmlSAX2Characters),
796 ignorableWhitespace: Some(crate::abi::exports_xml2::xmlSAX2IgnorableWhitespace),
797 processingInstruction: Some(crate::abi::exports_xml2::xmlSAX2ProcessingInstruction),
798 comment: Some(crate::abi::exports_xml2::xmlSAX2Comment),
799 warning: Some(crate::xml::errors::XML_PARSER_WARNING_SAX1),
800 error: Some(crate::xml::errors::XML_PARSER_ERROR_SAX1),
801 fatalError: Some(crate::xml::errors::XML_PARSER_ERROR_SAX1),
802 getParameterEntity: None,
803 cdataBlock: Some(crate::abi::exports_xml2::xmlSAX2CDataBlock),
804 externalSubset: None,
805 initialized: 1,
806 };
807
808/// `const xmlSAXLocator xmlDefaultSAXLocator` (globals.c 2.15.3).
809#[no_mangle]
810pub static xmlDefaultSAXLocator: crate::abi::callbacks::_xmlSAXLocator =
811 crate::abi::callbacks::_xmlSAXLocator {
812 getPublicId: Some(crate::abi::exports_xml2::xmlSAX2GetPublicId),
813 getSystemId: Some(crate::abi::exports_xml2::xmlSAX2GetSystemId),
814 getLineNumber: Some(crate::abi::exports_xml2::xmlSAX2GetLineNumber),
815 getColumnNumber: Some(crate::abi::exports_xml2::xmlSAX2GetColumnNumber),
816 };
817
818// ═══════════════════════════════════════════════════════════════════════════════
819// xmlLastError — the exported C global mirror of the thread-local error state
820// ═══════════════════════════════════════════════════════════════════════════════
821//
822// Upstream `XMLPUBVAR xmlError xmlLastError` (xmlerror.h). The candidate's
823// internal error state is thread-local (safe divergence, more correct than
824// upstream's racy global); this mirror is deep-copied on every error raise
825// and freed on reset, so C consumers observe upstream-equivalent lifetime
826// semantics. Residual R-000135.
827
828/// `xmlError xmlLastError` — most recent error (mirror).
829#[no_mangle]
830pub static mut xmlLastError: crate::abi::structs::_xmlError = crate::abi::structs::_xmlError {
831 domain: 0,
832 code: 0,
833 message: core::ptr::null_mut(),
834 level: 0,
835 file: core::ptr::null_mut(),
836 line: 0,
837 str1: core::ptr::null_mut(),
838 str2: core::ptr::null_mut(),
839 str3: core::ptr::null_mut(),
840 int1: 0,
841 int2: 0,
842 ctxt: core::ptr::null_mut(),
843 node: core::ptr::null_mut(),
844};
845
846/// Deep-copy `err` into the exported `xmlLastError` global.
847///
848/// The string fields are copied with `libc::malloc`/`memcpy` so the mirror
849/// owns them (previous mirror strings are freed first — upstream
850/// xmlResetError semantics).
851///
852/// # SAFETY
853///
854/// - `err` must point to a valid `_xmlError` whose string fields are
855/// NUL-terminated or NULL.
856pub unsafe fn sync_xml_last_error(err: *const crate::abi::structs::_xmlError) {
857 if err.is_null() {
858 return;
859 }
860 let _guard = LAST_ERROR_MIRROR_LOCK.lock();
861 unsafe { sync_xml_last_error_locked(err) };
862}
863
864/// Mirror write helper; caller must hold `LAST_ERROR_MIRROR_LOCK`.
865///
866/// # Safety
867///
868/// - `err` must be non-NULL and point to a valid `_xmlError` whose string
869/// fields are NULL or NUL-terminated; the caller must hold
870/// `LAST_ERROR_MIRROR_LOCK` so the `xmlLastError` global is not read or
871/// written concurrently; each field is deep-copied with `dup_cstr`.
872unsafe fn sync_xml_last_error_locked(err: *const crate::abi::structs::_xmlError) {
873 unsafe {
874 reset_xml_last_error_locked();
875 let src = &*err;
876 let dst = core::ptr::addr_of_mut!(xmlLastError);
877 (*dst).domain = src.domain;
878 (*dst).code = src.code;
879 (*dst).level = src.level;
880 (*dst).line = src.line;
881 (*dst).int1 = src.int1;
882 (*dst).int2 = src.int2;
883 (*dst).ctxt = src.ctxt;
884 (*dst).node = src.node;
885 (*dst).message = dup_cstr(src.message as *const u8);
886 (*dst).file = dup_cstr(src.file as *const u8);
887 (*dst).str1 = dup_cstr(src.str1 as *const u8);
888 (*dst).str2 = dup_cstr(src.str2 as *const u8);
889 (*dst).str3 = dup_cstr(src.str3 as *const u8);
890 }
891}
892
893/// Reset the exported `xmlLastError` global, freeing owned strings
894/// (upstream xmlResetError).
895///
896/// # SAFETY
897///
898/// Only call while no other thread is reading the global (upstream has the
899/// same race; documented).
900pub unsafe fn reset_xml_last_error() {
901 let _guard = LAST_ERROR_MIRROR_LOCK.lock();
902 unsafe { reset_xml_last_error_locked() };
903}
904
905/// Mirror reset helper; caller must hold `LAST_ERROR_MIRROR_LOCK`.
906unsafe fn reset_xml_last_error_locked() {
907 unsafe {
908 let dst = core::ptr::addr_of_mut!(xmlLastError);
909 if !(*dst).message.is_null() {
910 libc::free((*dst).message as *mut libc::c_void);
911 }
912 if !(*dst).file.is_null() {
913 libc::free((*dst).file as *mut libc::c_void);
914 }
915 if !(*dst).str1.is_null() {
916 libc::free((*dst).str1 as *mut libc::c_void);
917 }
918 if !(*dst).str2.is_null() {
919 libc::free((*dst).str2 as *mut libc::c_void);
920 }
921 if !(*dst).str3.is_null() {
922 libc::free((*dst).str3 as *mut libc::c_void);
923 }
924 *dst = crate::abi::structs::_xmlError {
925 domain: 0,
926 code: 0,
927 message: core::ptr::null_mut(),
928 level: 0,
929 file: core::ptr::null_mut(),
930 line: 0,
931 str1: core::ptr::null_mut(),
932 str2: core::ptr::null_mut(),
933 str3: core::ptr::null_mut(),
934 int1: 0,
935 int2: 0,
936 ctxt: core::ptr::null_mut(),
937 node: core::ptr::null_mut(),
938 };
939 }
940}
941
942/// Heap-copy a NUL-terminated string (NULL-safe).
943///
944/// # Safety
945///
946/// - `s` must be NULL or a valid NUL-terminated string readable via
947/// `strlen` for the duration of the copy; the returned pointer is
948/// `libc::malloc`-owned and must be freed with `libc::free`, or is NULL
949/// when `s` is NULL or the allocation fails.
950unsafe fn dup_cstr(s: *const u8) -> *mut c_char {
951 if s.is_null() {
952 return core::ptr::null_mut();
953 }
954 unsafe {
955 let len = libc::strlen(s as *const libc::c_char) as usize;
956 let p = libc::malloc(len + 1) as *mut u8;
957 if p.is_null() {
958 return core::ptr::null_mut();
959 }
960 libc::memcpy(p as *mut libc::c_void, s as *const libc::c_void, len + 1);
961 p as *mut c_char
962 }
963}
964
965// ═══════════════════════════════════════════════════════════════════════════════
966// Default accessor functions (upstream parser.h / tree.h / xmlsave.h)
967// ═══════════════════════════════════════════════════════════════════════════════
968//
969// The deprecated `xmlXxxDefault(v)` accessors set the corresponding global
970// when `v != 0` and return the (new) value — upstream semantics (they
971// predate the plain globals; the modern behavior is conditional-set-and-
972// return, see upstream globals.c / parser.c).
973
974/// Upstream `xmlKeepBlanksDefault(int v)`.
975///
976/// # SAFETY
977///
978/// The function touches crate-global state only; it is safe
979/// as long as the caller respects the library's global
980/// initialization/cleanup ordering (xmlInitParser before use,
981/// xmlCleanupParser only after all users are done).
982///
983/// Violating the global lifecycle ordering, or calling this after
984/// teardown or from a signal handler, is undefined behavior.
985#[no_mangle]
986pub unsafe extern "C" fn xmlKeepBlanksDefault(v: c_int) -> c_int {
987 unsafe {
988 if v != 0 {
989 xmlKeepBlanksDefaultValue = v;
990 }
991 xmlKeepBlanksDefaultValue
992 }
993}
994
995/// Upstream `xmlLineNumbersDefault(int v)`.
996///
997/// # SAFETY
998///
999/// The function touches crate-global state only; it is safe
1000/// as long as the caller respects the library's global
1001/// initialization/cleanup ordering (xmlInitParser before use,
1002/// xmlCleanupParser only after all users are done).
1003///
1004/// Violating the global lifecycle ordering, or calling this after
1005/// teardown or from a signal handler, is undefined behavior.
1006#[no_mangle]
1007pub unsafe extern "C" fn xmlLineNumbersDefault(v: c_int) -> c_int {
1008 unsafe {
1009 if v != 0 {
1010 xmlLineNumbersDefaultValue = v;
1011 }
1012 xmlLineNumbersDefaultValue
1013 }
1014}
1015
1016/// Upstream `xmlSubstituteEntitiesDefault(int v)`.
1017///
1018/// # SAFETY
1019///
1020/// The function touches crate-global state only; it is safe
1021/// as long as the caller respects the library's global
1022/// initialization/cleanup ordering (xmlInitParser before use,
1023/// xmlCleanupParser only after all users are done).
1024///
1025/// Violating the global lifecycle ordering, or calling this after
1026/// teardown or from a signal handler, is undefined behavior.
1027#[no_mangle]
1028pub unsafe extern "C" fn xmlSubstituteEntitiesDefault(v: c_int) -> c_int {
1029 unsafe {
1030 if v != 0 {
1031 xmlSubstituteEntitiesDefaultValue = v;
1032 }
1033 xmlSubstituteEntitiesDefaultValue
1034 }
1035}
1036
1037/// Upstream `xmlPedanticParserDefault(int v)`.
1038///
1039/// # SAFETY
1040///
1041/// The function touches crate-global state only; it is safe
1042/// as long as the caller respects the library's global
1043/// initialization/cleanup ordering (xmlInitParser before use,
1044/// xmlCleanupParser only after all users are done).
1045///
1046/// Violating the global lifecycle ordering, or calling this after
1047/// teardown or from a signal handler, is undefined behavior.
1048#[no_mangle]
1049pub unsafe extern "C" fn xmlPedanticParserDefault(v: c_int) -> c_int {
1050 unsafe {
1051 if v != 0 {
1052 xmlPedanticParserDefaultValue = v;
1053 }
1054 xmlPedanticParserDefaultValue
1055 }
1056}
1057
1058/// Upstream `xmlDoValidityCheckingDefaultValue` accessor is the global
1059/// itself; `xmlGetWarningsDefaultValue` likewise (no accessor functions
1060/// exist for those in upstream 2.15).
1061/// Upstream `xmlRegisterNodeDefault(xmlRegisterNodeFunc func)`.
1062///
1063/// # SAFETY
1064///
1065///
1066/// - `func` must be a valid callback (or None);
1067/// the callback is invoked with the documented context pointer and
1068/// must itself uphold the same pointer invariants.
1069///
1070/// The caller must not race this call with concurrent mutation of the
1071/// same objects from other threads (per-object state is not internally
1072/// synchronized). Violating any of the above is undefined behavior.
1073///
1074/// Exercised by the C-API differential courts
1075/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1076/// courts; those pass byte-for-byte against the upstream oracle.
1077#[no_mangle]
1078pub unsafe extern "C" fn xmlRegisterNodeDefault(
1079 func: Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)>,
1080) -> Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1081 unsafe {
1082 // UPSTREAM-PARITY (tree.c): registering any callback arms the
1083 // xmlRegisterCallbacks gate.
1084 XML_REGISTER_CALLBACKS.store(true, core::sync::atomic::Ordering::Relaxed);
1085 if func.is_some() {
1086 xmlRegisterNodeDefaultValue = func;
1087 }
1088 xmlRegisterNodeDefaultValue
1089 }
1090}
1091
1092/// Upstream `xmlDeregisterNodeDefault(xmlDeregisterNodeFunc func)`.
1093///
1094/// # SAFETY
1095///
1096///
1097/// - `func` must be a valid callback (or None);
1098/// the callback is invoked with the documented context pointer and
1099/// must itself uphold the same pointer invariants.
1100///
1101/// The caller must not race this call with concurrent mutation of the
1102/// same objects from other threads (per-object state is not internally
1103/// synchronized). Violating any of the above is undefined behavior.
1104///
1105/// Exercised by the C-API differential courts
1106/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1107/// courts; those pass byte-for-byte against the upstream oracle.
1108#[no_mangle]
1109pub unsafe extern "C" fn xmlDeregisterNodeDefault(
1110 func: Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)>,
1111) -> Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1112 unsafe {
1113 // UPSTREAM-PARITY (tree.c): registering any callback arms the
1114 // xmlRegisterCallbacks gate.
1115 XML_REGISTER_CALLBACKS.store(true, core::sync::atomic::Ordering::Relaxed);
1116 if func.is_some() {
1117 xmlDeregisterNodeDefaultValue = func;
1118 }
1119 xmlDeregisterNodeDefaultValue
1120 }
1121}
1122
1123/// Upstream `__xmlIndentTreeOutput(void)` (parser.h) — returns a pointer to
1124/// the `xmlIndentTreeOutput` global.
1125///
1126/// # SAFETY
1127///
1128/// The function touches crate-global state only; it is safe
1129/// as long as the caller respects the library's global
1130/// initialization/cleanup ordering (xmlInitParser before use,
1131/// xmlCleanupParser only after all users are done).
1132///
1133/// Violating the global lifecycle ordering, or calling this after
1134/// teardown or from a signal handler, is undefined behavior.
1135#[no_mangle]
1136pub unsafe extern "C" fn __xmlIndentTreeOutput() -> *mut c_int {
1137 core::ptr::addr_of_mut!(xmlIndentTreeOutput)
1138}
1139
1140/// Upstream `__xmlSaveNoEmptyTags(void)` (parser.h) — returns a pointer to
1141/// the `xmlSaveNoEmptyTags` global.
1142///
1143/// # SAFETY
1144///
1145/// The function touches crate-global state only; it is safe
1146/// as long as the caller respects the library's global
1147/// initialization/cleanup ordering (xmlInitParser before use,
1148/// xmlCleanupParser only after all users are done).
1149///
1150/// Violating the global lifecycle ordering, or calling this after
1151/// teardown or from a signal handler, is undefined behavior.
1152#[no_mangle]
1153pub unsafe extern "C" fn __xmlSaveNoEmptyTags() -> *mut c_int {
1154 core::ptr::addr_of_mut!(xmlSaveNoEmptyTags)
1155}
1156
1157/// Upstream `__xmlTreeIndentString(void)` (parser.h) — returns a pointer to
1158/// the `xmlTreeIndentString` global.
1159///
1160/// # SAFETY
1161///
1162/// The function touches crate-global state only; it is safe
1163/// as long as the caller respects the library's global
1164/// initialization/cleanup ordering (xmlInitParser before use,
1165/// xmlCleanupParser only after all users are done).
1166///
1167/// Violating the global lifecycle ordering, or calling this after
1168/// teardown or from a signal handler, is undefined behavior.
1169#[no_mangle]
1170pub unsafe extern "C" fn __xmlTreeIndentString() -> *mut *const xmlChar {
1171 core::ptr::addr_of_mut!(xmlTreeIndentString)
1172}
1173
1174// ═══════════════════════════════════════════════════════════════════════════════
1175// xmlThrDef* accessors (upstream threads.c / globals.c)
1176// ═══════════════════════════════════════════════════════════════════════════════
1177//
1178// The deprecated `xmlThrDef*` family reads/writes the public globals with
1179// the upstream semantics: when `v != 0` the global is set, and the (new)
1180// value is returned. In upstream these were thread-local definitions before
1181// the globals became plain variables; the modern behavior is exactly this
1182// conditional-set-and-return on the global (see upstream globals.c).
1183
1184/// Upstream `xmlThrDefDoValidityCheckingDefaultValue(int v)`.
1185///
1186/// # SAFETY
1187///
1188/// The function touches crate-global state only; it is safe
1189/// as long as the caller respects the library's global
1190/// initialization/cleanup ordering (xmlInitParser before use,
1191/// xmlCleanupParser only after all users are done).
1192///
1193/// Violating the global lifecycle ordering, or calling this after
1194/// teardown or from a signal handler, is undefined behavior.
1195#[no_mangle]
1196pub unsafe extern "C" fn xmlThrDefDoValidityCheckingDefaultValue(v: c_int) -> c_int {
1197 unsafe {
1198 if v != 0 {
1199 xmlDoValidityCheckingDefaultValue = v;
1200 }
1201 xmlDoValidityCheckingDefaultValue
1202 }
1203}
1204
1205/// Upstream `xmlThrDefGetWarningsDefaultValue(int v)`.
1206///
1207/// # SAFETY
1208///
1209/// The function touches crate-global state only; it is safe
1210/// as long as the caller respects the library's global
1211/// initialization/cleanup ordering (xmlInitParser before use,
1212/// xmlCleanupParser only after all users are done).
1213///
1214/// Violating the global lifecycle ordering, or calling this after
1215/// teardown or from a signal handler, is undefined behavior.
1216#[no_mangle]
1217pub unsafe extern "C" fn xmlThrDefGetWarningsDefaultValue(v: c_int) -> c_int {
1218 unsafe {
1219 if v != 0 {
1220 xmlGetWarningsDefaultValue = v;
1221 }
1222 xmlGetWarningsDefaultValue
1223 }
1224}
1225
1226/// Upstream `xmlThrDefLoadExtDtdDefaultValue(int v)`.
1227///
1228/// # SAFETY
1229///
1230/// The function touches crate-global state only; it is safe
1231/// as long as the caller respects the library's global
1232/// initialization/cleanup ordering (xmlInitParser before use,
1233/// xmlCleanupParser only after all users are done).
1234///
1235/// Violating the global lifecycle ordering, or calling this after
1236/// teardown or from a signal handler, is undefined behavior.
1237#[no_mangle]
1238pub unsafe extern "C" fn xmlThrDefLoadExtDtdDefaultValue(v: c_int) -> c_int {
1239 unsafe {
1240 if v != 0 {
1241 xmlLoadExtDtdDefaultValue = v;
1242 }
1243 xmlLoadExtDtdDefaultValue
1244 }
1245}
1246
1247/// Upstream `xmlThrDefPedanticParserDefaultValue(int v)`.
1248///
1249/// # SAFETY
1250///
1251/// The function touches crate-global state only; it is safe
1252/// as long as the caller respects the library's global
1253/// initialization/cleanup ordering (xmlInitParser before use,
1254/// xmlCleanupParser only after all users are done).
1255///
1256/// Violating the global lifecycle ordering, or calling this after
1257/// teardown or from a signal handler, is undefined behavior.
1258#[no_mangle]
1259pub unsafe extern "C" fn xmlThrDefPedanticParserDefaultValue(v: c_int) -> c_int {
1260 unsafe {
1261 if v != 0 {
1262 xmlPedanticParserDefaultValue = v;
1263 }
1264 xmlPedanticParserDefaultValue
1265 }
1266}
1267
1268/// Upstream `xmlThrDefLineNumbersDefaultValue(int v)`.
1269///
1270/// # SAFETY
1271///
1272/// The function touches crate-global state only; it is safe
1273/// as long as the caller respects the library's global
1274/// initialization/cleanup ordering (xmlInitParser before use,
1275/// xmlCleanupParser only after all users are done).
1276///
1277/// Violating the global lifecycle ordering, or calling this after
1278/// teardown or from a signal handler, is undefined behavior.
1279#[no_mangle]
1280pub unsafe extern "C" fn xmlThrDefLineNumbersDefaultValue(v: c_int) -> c_int {
1281 unsafe {
1282 if v != 0 {
1283 xmlLineNumbersDefaultValue = v;
1284 }
1285 xmlLineNumbersDefaultValue
1286 }
1287}
1288
1289/// Upstream `xmlThrDefKeepBlanksDefaultValue(int v)`.
1290///
1291/// # SAFETY
1292///
1293/// The function touches crate-global state only; it is safe
1294/// as long as the caller respects the library's global
1295/// initialization/cleanup ordering (xmlInitParser before use,
1296/// xmlCleanupParser only after all users are done).
1297///
1298/// Violating the global lifecycle ordering, or calling this after
1299/// teardown or from a signal handler, is undefined behavior.
1300#[no_mangle]
1301pub unsafe extern "C" fn xmlThrDefKeepBlanksDefaultValue(v: c_int) -> c_int {
1302 unsafe {
1303 if v != 0 {
1304 xmlKeepBlanksDefaultValue = v;
1305 }
1306 xmlKeepBlanksDefaultValue
1307 }
1308}
1309
1310/// Upstream `xmlThrDefSubstituteEntitiesDefaultValue(int v)`.
1311///
1312/// # SAFETY
1313///
1314/// The function touches crate-global state only; it is safe
1315/// as long as the caller respects the library's global
1316/// initialization/cleanup ordering (xmlInitParser before use,
1317/// xmlCleanupParser only after all users are done).
1318///
1319/// Violating the global lifecycle ordering, or calling this after
1320/// teardown or from a signal handler, is undefined behavior.
1321#[no_mangle]
1322pub unsafe extern "C" fn xmlThrDefSubstituteEntitiesDefaultValue(v: c_int) -> c_int {
1323 unsafe {
1324 if v != 0 {
1325 xmlSubstituteEntitiesDefaultValue = v;
1326 }
1327 xmlSubstituteEntitiesDefaultValue
1328 }
1329}
1330
1331/// Upstream `xmlThrDefParserDebugEntities(int v)`.
1332///
1333/// # SAFETY
1334///
1335/// The function touches crate-global state only; it is safe
1336/// as long as the caller respects the library's global
1337/// initialization/cleanup ordering (xmlInitParser before use,
1338/// xmlCleanupParser only after all users are done).
1339///
1340/// Violating the global lifecycle ordering, or calling this after
1341/// teardown or from a signal handler, is undefined behavior.
1342#[no_mangle]
1343pub unsafe extern "C" fn xmlThrDefParserDebugEntities(v: c_int) -> c_int {
1344 unsafe {
1345 if v != 0 {
1346 xmlParserDebugEntities = v;
1347 }
1348 xmlParserDebugEntities
1349 }
1350}
1351
1352/// Upstream `xmlThrDefIndentTreeOutput(int v)`.
1353///
1354/// # SAFETY
1355///
1356/// The function touches crate-global state only; it is safe
1357/// as long as the caller respects the library's global
1358/// initialization/cleanup ordering (xmlInitParser before use,
1359/// xmlCleanupParser only after all users are done).
1360///
1361/// Violating the global lifecycle ordering, or calling this after
1362/// teardown or from a signal handler, is undefined behavior.
1363#[no_mangle]
1364pub unsafe extern "C" fn xmlThrDefIndentTreeOutput(v: c_int) -> c_int {
1365 unsafe {
1366 if v != 0 {
1367 xmlIndentTreeOutput = v;
1368 }
1369 xmlIndentTreeOutput
1370 }
1371}
1372
1373/// Upstream `xmlThrDefTreeIndentString(const char *v)` — sets the indent
1374/// string when non-NULL and returns the current pointer.
1375///
1376/// # SAFETY
1377///
1378///
1379/// - `v` must point to valid NUL-terminated
1380/// strings (or NULL where the C contract allows) for the lifetime
1381/// of the call.
1382///
1383/// The caller must not race this call with concurrent mutation of the
1384/// same objects from other threads (per-object state is not internally
1385/// synchronized). Violating any of the above is undefined behavior.
1386///
1387/// Exercised by the C-API differential courts
1388/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1389/// courts; those pass byte-for-byte against the upstream oracle.
1390#[no_mangle]
1391pub unsafe extern "C" fn xmlThrDefTreeIndentString(v: *const c_char) -> *const c_char {
1392 unsafe {
1393 if !v.is_null() {
1394 xmlTreeIndentString = v as *const xmlChar;
1395 }
1396 xmlTreeIndentString as *const c_char
1397 }
1398}
1399
1400/// Upstream `xmlThrDefSaveNoEmptyTags(int v)`.
1401///
1402/// # SAFETY
1403///
1404/// The function touches crate-global state only; it is safe
1405/// as long as the caller respects the library's global
1406/// initialization/cleanup ordering (xmlInitParser before use,
1407/// xmlCleanupParser only after all users are done).
1408///
1409/// Violating the global lifecycle ordering, or calling this after
1410/// teardown or from a signal handler, is undefined behavior.
1411#[no_mangle]
1412pub unsafe extern "C" fn xmlThrDefSaveNoEmptyTags(v: c_int) -> c_int {
1413 unsafe {
1414 if v != 0 {
1415 xmlSaveNoEmptyTags = v;
1416 }
1417 xmlSaveNoEmptyTags
1418 }
1419}
1420
1421/// Upstream `xmlThrDefRegisterNodeDefault(xmlRegisterNodeFunc func)`.
1422///
1423/// # SAFETY
1424///
1425///
1426/// - `func` must be a valid callback (or None);
1427/// the callback is invoked with the documented context pointer and
1428/// must itself uphold the same pointer invariants.
1429///
1430/// The caller must not race this call with concurrent mutation of the
1431/// same objects from other threads (per-object state is not internally
1432/// synchronized). Violating any of the above is undefined behavior.
1433///
1434/// Exercised by the C-API differential courts
1435/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1436/// courts; those pass byte-for-byte against the upstream oracle.
1437#[no_mangle]
1438pub unsafe extern "C" fn xmlThrDefRegisterNodeDefault(
1439 func: Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)>,
1440) -> Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1441 unsafe {
1442 if func.is_some() {
1443 xmlRegisterNodeDefaultValue = func;
1444 }
1445 xmlRegisterNodeDefaultValue
1446 }
1447}
1448
1449/// Upstream `xmlThrDefDeregisterNodeDefault(xmlDeregisterNodeFunc func)`.
1450///
1451/// # SAFETY
1452///
1453///
1454/// - `func` must be a valid callback (or None);
1455/// the callback is invoked with the documented context pointer and
1456/// must itself uphold the same pointer invariants.
1457///
1458/// The caller must not race this call with concurrent mutation of the
1459/// same objects from other threads (per-object state is not internally
1460/// synchronized). Violating any of the above is undefined behavior.
1461///
1462/// Exercised by the C-API differential courts
1463/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1464/// courts; those pass byte-for-byte against the upstream oracle.
1465#[no_mangle]
1466pub unsafe extern "C" fn xmlThrDefDeregisterNodeDefault(
1467 func: Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)>,
1468) -> Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1469 unsafe {
1470 if func.is_some() {
1471 xmlDeregisterNodeDefaultValue = func;
1472 }
1473 xmlDeregisterNodeDefaultValue
1474 }
1475}
1476
1477/// Upstream `xmlThrDefSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc func)`.
1478///
1479/// # SAFETY
1480///
1481/// - `ctx` must be valid pointers (or NULL
1482/// where the upstream C contract allows), obtained from the
1483/// matching constructor/owner and not yet freed; the callee may
1484/// take or keep ownership exactly as the C API specifies.
1485///
1486/// - `func` must be a valid callback (or None);
1487/// the callback is invoked with the documented context pointer and
1488/// must itself uphold the same pointer invariants.
1489///
1490/// The caller must not race this call with concurrent mutation of the
1491/// same objects from other threads (per-object state is not internally
1492/// synchronized). Violating any of the above is undefined behavior.
1493///
1494/// Exercised by the C-API differential courts
1495/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1496/// courts; those pass byte-for-byte against the upstream oracle.
1497#[no_mangle]
1498pub unsafe extern "C" fn xmlThrDefSetGenericErrorFunc(
1499 ctx: *mut c_void,
1500 func: Option<xmlGenericErrorFunc>,
1501) {
1502 unsafe {
1503 xmlGenericErrorContext = ctx;
1504 xmlGenericError = func;
1505 }
1506}
1507
1508/// Upstream `xmlThrDefSetStructuredErrorFunc(void *ctx, xmlStructuredErrorFunc func)`.
1509///
1510/// # SAFETY
1511///
1512/// - `ctx` must be valid pointers (or NULL
1513/// where the upstream C contract allows), obtained from the
1514/// matching constructor/owner and not yet freed; the callee may
1515/// take or keep ownership exactly as the C API specifies.
1516///
1517/// - `func` must be a valid callback (or None);
1518/// the callback is invoked with the documented context pointer and
1519/// must itself uphold the same pointer invariants.
1520///
1521/// The caller must not race this call with concurrent mutation of the
1522/// same objects from other threads (per-object state is not internally
1523/// synchronized). Violating any of the above is undefined behavior.
1524///
1525/// Exercised by the C-API differential courts
1526/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1527/// courts; those pass byte-for-byte against the upstream oracle.
1528#[no_mangle]
1529pub unsafe extern "C" fn xmlThrDefSetStructuredErrorFunc(
1530 ctx: *mut c_void,
1531 func: Option<xmlStructuredErrorFunc>,
1532) {
1533 unsafe {
1534 xmlStructuredErrorContext = ctx;
1535 xmlStructuredError = func;
1536 }
1537}
1538
1539/// Upstream `xmlThrDefDefaultBufferSize(int v)`.
1540///
1541/// # SAFETY
1542///
1543/// The function touches crate-global state only; it is safe
1544/// as long as the caller respects the library's global
1545/// initialization/cleanup ordering (xmlInitParser before use,
1546/// xmlCleanupParser only after all users are done).
1547///
1548/// Violating the global lifecycle ordering, or calling this after
1549/// teardown or from a signal handler, is undefined behavior.
1550#[no_mangle]
1551pub unsafe extern "C" fn xmlThrDefDefaultBufferSize(v: c_int) -> c_int {
1552 unsafe {
1553 if v != 0 {
1554 xmlDefaultBufferSize = v;
1555 }
1556 xmlDefaultBufferSize
1557 }
1558}
1559
1560/// Upstream `xmlThrDefBufferAllocScheme(xmlBufferAllocationScheme v)`.
1561///
1562/// # SAFETY
1563///
1564/// The function touches crate-global state only; it is safe
1565/// as long as the caller respects the library's global
1566/// initialization/cleanup ordering (xmlInitParser before use,
1567/// xmlCleanupParser only after all users are done).
1568///
1569/// Violating the global lifecycle ordering, or calling this after
1570/// teardown or from a signal handler, is undefined behavior.
1571#[no_mangle]
1572pub unsafe extern "C" fn xmlThrDefBufferAllocScheme(v: c_int) -> c_int {
1573 unsafe {
1574 if v != 0 {
1575 xmlBufferAllocScheme = v;
1576 }
1577 xmlBufferAllocScheme
1578 }
1579}
1580
1581/// Upstream `xmlThrDefParserInputBufferCreateFilenameDefault(...)`.
1582///
1583/// # SAFETY
1584///
1585///
1586/// - `func` must be a valid callback (or None);
1587/// the callback is invoked with the documented context pointer and
1588/// must itself uphold the same pointer invariants.
1589///
1590/// The caller must not race this call with concurrent mutation of the
1591/// same objects from other threads (per-object state is not internally
1592/// synchronized). Violating any of the above is undefined behavior.
1593///
1594/// Exercised by the C-API differential courts
1595/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1596/// courts; those pass byte-for-byte against the upstream oracle.
1597#[no_mangle]
1598pub unsafe extern "C" fn xmlThrDefParserInputBufferCreateFilenameDefault(
1599 func: Option<
1600 unsafe extern "C" fn(
1601 *const c_char,
1602 c_int,
1603 ) -> *mut crate::abi::structs::_xmlParserInputBuffer,
1604 >,
1605) -> Option<
1606 unsafe extern "C" fn(*const c_char, c_int) -> *mut crate::abi::structs::_xmlParserInputBuffer,
1607> {
1608 unsafe {
1609 if func.is_some() {
1610 xmlParserInputBufferCreateFilenameValue = func;
1611 }
1612 xmlParserInputBufferCreateFilenameValue
1613 }
1614}
1615
1616/// Upstream `xmlThrDefOutputBufferCreateFilenameDefault(...)`.
1617///
1618/// # SAFETY
1619///
1620///
1621/// - `func` must be a valid callback (or None);
1622/// the callback is invoked with the documented context pointer and
1623/// must itself uphold the same pointer invariants.
1624///
1625/// The caller must not race this call with concurrent mutation of the
1626/// same objects from other threads (per-object state is not internally
1627/// synchronized). Violating any of the above is undefined behavior.
1628///
1629/// Exercised by the C-API differential courts
1630/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1631/// courts; those pass byte-for-byte against the upstream oracle.
1632#[no_mangle]
1633pub unsafe extern "C" fn xmlThrDefOutputBufferCreateFilenameDefault(
1634 func: Option<
1635 unsafe extern "C" fn(
1636 *const c_char,
1637 crate::abi::structs::xmlCharEncodingHandlerPtr,
1638 c_int,
1639 ) -> *mut crate::abi::structs::_xmlOutputBuffer,
1640 >,
1641) -> Option<
1642 unsafe extern "C" fn(
1643 *const c_char,
1644 crate::abi::structs::xmlCharEncodingHandlerPtr,
1645 c_int,
1646 ) -> *mut crate::abi::structs::_xmlOutputBuffer,
1647> {
1648 unsafe {
1649 if func.is_some() {
1650 xmlOutputBufferCreateFilenameValue = func;
1651 }
1652 xmlOutputBufferCreateFilenameValue
1653 }
1654}
1655
1656// ═══════════════════════════════════════════════════════════════════════════════
1657// __xmlXxx() pointer accessors (upstream threads.c / globals.c)
1658// ═══════════════════════════════════════════════════════════════════════════════
1659// The deprecated thread-local API exports one `__xmlXxx(void)` accessor per
1660// global; each returns a pointer to the global so callers can read/write it.
1661
1662/// Upstream `__xmlBufferAllocScheme(void)` — returns a pointer to `xmlBufferAllocScheme`.
1663///
1664/// # SAFETY
1665///
1666/// The function touches crate-global state only; it is safe
1667/// as long as the caller respects the library's global
1668/// initialization/cleanup ordering (xmlInitParser before use,
1669/// xmlCleanupParser only after all users are done).
1670///
1671/// Violating the global lifecycle ordering, or calling this after
1672/// teardown or from a signal handler, is undefined behavior.
1673#[no_mangle]
1674pub unsafe extern "C" fn __xmlBufferAllocScheme() -> *mut c_int {
1675 // SAFETY: returning a pointer to an exported static; the caller may
1676 // read/write it exactly as with upstream's deprecated accessor.
1677 core::ptr::addr_of_mut!(xmlBufferAllocScheme)
1678}
1679
1680/// Upstream `__xmlDefaultBufferSize(void)` — returns a pointer to `xmlDefaultBufferSize`.
1681///
1682/// # SAFETY
1683///
1684/// The function touches crate-global state only; it is safe
1685/// as long as the caller respects the library's global
1686/// initialization/cleanup ordering (xmlInitParser before use,
1687/// xmlCleanupParser only after all users are done).
1688///
1689/// Violating the global lifecycle ordering, or calling this after
1690/// teardown or from a signal handler, is undefined behavior.
1691#[no_mangle]
1692pub unsafe extern "C" fn __xmlDefaultBufferSize() -> *mut c_int {
1693 // SAFETY: returning a pointer to an exported static; the caller may
1694 // read/write it exactly as with upstream's deprecated accessor.
1695 core::ptr::addr_of_mut!(xmlDefaultBufferSize)
1696}
1697
1698/// Upstream `__xmlDeregisterNodeDefaultValue(void)` — returns a pointer to `xmlDeregisterNodeDefaultValue`.
1699///
1700/// # SAFETY
1701///
1702/// The function touches crate-global state only; it is safe
1703/// as long as the caller respects the library's global
1704/// initialization/cleanup ordering (xmlInitParser before use,
1705/// xmlCleanupParser only after all users are done).
1706///
1707/// Violating the global lifecycle ordering, or calling this after
1708/// teardown or from a signal handler, is undefined behavior.
1709#[no_mangle]
1710pub unsafe extern "C" fn __xmlDeregisterNodeDefaultValue(
1711) -> *mut Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1712 // SAFETY: returning a pointer to an exported static; the caller may
1713 // read/write it exactly as with upstream's deprecated accessor.
1714 core::ptr::addr_of_mut!(xmlDeregisterNodeDefaultValue)
1715}
1716
1717/// Upstream `__xmlDoValidityCheckingDefaultValue(void)` — returns a pointer to `xmlDoValidityCheckingDefaultValue`.
1718///
1719/// # SAFETY
1720///
1721/// The function touches crate-global state only; it is safe
1722/// as long as the caller respects the library's global
1723/// initialization/cleanup ordering (xmlInitParser before use,
1724/// xmlCleanupParser only after all users are done).
1725///
1726/// Violating the global lifecycle ordering, or calling this after
1727/// teardown or from a signal handler, is undefined behavior.
1728#[no_mangle]
1729pub unsafe extern "C" fn __xmlDoValidityCheckingDefaultValue() -> *mut c_int {
1730 // SAFETY: returning a pointer to an exported static; the caller may
1731 // read/write it exactly as with upstream's deprecated accessor.
1732 core::ptr::addr_of_mut!(xmlDoValidityCheckingDefaultValue)
1733}
1734
1735/// Upstream `__xmlGenericError(void)` — returns a pointer to `xmlGenericError`.
1736///
1737/// # SAFETY
1738///
1739/// The function touches crate-global state only; it is safe
1740/// as long as the caller respects the library's global
1741/// initialization/cleanup ordering (xmlInitParser before use,
1742/// xmlCleanupParser only after all users are done).
1743///
1744/// Violating the global lifecycle ordering, or calling this after
1745/// teardown or from a signal handler, is undefined behavior.
1746#[no_mangle]
1747pub unsafe extern "C" fn __xmlGenericError() -> *mut Option<xmlGenericErrorFunc> {
1748 // SAFETY: returning a pointer to an exported static; the caller may
1749 // read/write it exactly as with upstream's deprecated accessor.
1750 core::ptr::addr_of_mut!(xmlGenericError)
1751}
1752
1753/// Upstream `__xmlGenericErrorContext(void)` — returns a pointer to `xmlGenericErrorContext`.
1754///
1755/// # SAFETY
1756///
1757/// The function touches crate-global state only; it is safe
1758/// as long as the caller respects the library's global
1759/// initialization/cleanup ordering (xmlInitParser before use,
1760/// xmlCleanupParser only after all users are done).
1761///
1762/// Violating the global lifecycle ordering, or calling this after
1763/// teardown or from a signal handler, is undefined behavior.
1764#[no_mangle]
1765pub unsafe extern "C" fn __xmlGenericErrorContext() -> *mut *mut c_void {
1766 // SAFETY: returning a pointer to an exported static; the caller may
1767 // read/write it exactly as with upstream's deprecated accessor.
1768 core::ptr::addr_of_mut!(xmlGenericErrorContext)
1769}
1770
1771/// Upstream `__xmlGetWarningsDefaultValue(void)` — returns a pointer to `xmlGetWarningsDefaultValue`.
1772///
1773/// # SAFETY
1774///
1775/// The function touches crate-global state only; it is safe
1776/// as long as the caller respects the library's global
1777/// initialization/cleanup ordering (xmlInitParser before use,
1778/// xmlCleanupParser only after all users are done).
1779///
1780/// Violating the global lifecycle ordering, or calling this after
1781/// teardown or from a signal handler, is undefined behavior.
1782#[no_mangle]
1783pub unsafe extern "C" fn __xmlGetWarningsDefaultValue() -> *mut c_int {
1784 // SAFETY: returning a pointer to an exported static; the caller may
1785 // read/write it exactly as with upstream's deprecated accessor.
1786 core::ptr::addr_of_mut!(xmlGetWarningsDefaultValue)
1787}
1788
1789/// Upstream `__xmlKeepBlanksDefaultValue(void)` — returns a pointer to `xmlKeepBlanksDefaultValue`.
1790///
1791/// # SAFETY
1792///
1793/// The function touches crate-global state only; it is safe
1794/// as long as the caller respects the library's global
1795/// initialization/cleanup ordering (xmlInitParser before use,
1796/// xmlCleanupParser only after all users are done).
1797///
1798/// Violating the global lifecycle ordering, or calling this after
1799/// teardown or from a signal handler, is undefined behavior.
1800#[no_mangle]
1801pub unsafe extern "C" fn __xmlKeepBlanksDefaultValue() -> *mut c_int {
1802 // SAFETY: returning a pointer to an exported static; the caller may
1803 // read/write it exactly as with upstream's deprecated accessor.
1804 core::ptr::addr_of_mut!(xmlKeepBlanksDefaultValue)
1805}
1806
1807/// Upstream `__xmlLineNumbersDefaultValue(void)` — returns a pointer to `xmlLineNumbersDefaultValue`.
1808///
1809/// # SAFETY
1810///
1811/// The function touches crate-global state only; it is safe
1812/// as long as the caller respects the library's global
1813/// initialization/cleanup ordering (xmlInitParser before use,
1814/// xmlCleanupParser only after all users are done).
1815///
1816/// Violating the global lifecycle ordering, or calling this after
1817/// teardown or from a signal handler, is undefined behavior.
1818#[no_mangle]
1819pub unsafe extern "C" fn __xmlLineNumbersDefaultValue() -> *mut c_int {
1820 // SAFETY: returning a pointer to an exported static; the caller may
1821 // read/write it exactly as with upstream's deprecated accessor.
1822 core::ptr::addr_of_mut!(xmlLineNumbersDefaultValue)
1823}
1824
1825/// Upstream `__xmlLoadExtDtdDefaultValue(void)` — returns a pointer to `xmlLoadExtDtdDefaultValue`.
1826///
1827/// # SAFETY
1828///
1829/// The function touches crate-global state only; it is safe
1830/// as long as the caller respects the library's global
1831/// initialization/cleanup ordering (xmlInitParser before use,
1832/// xmlCleanupParser only after all users are done).
1833///
1834/// Violating the global lifecycle ordering, or calling this after
1835/// teardown or from a signal handler, is undefined behavior.
1836#[no_mangle]
1837pub unsafe extern "C" fn __xmlLoadExtDtdDefaultValue() -> *mut c_int {
1838 // SAFETY: returning a pointer to an exported static; the caller may
1839 // read/write it exactly as with upstream's deprecated accessor.
1840 core::ptr::addr_of_mut!(xmlLoadExtDtdDefaultValue)
1841}
1842
1843/// Upstream `__xmlOutputBufferCreateFilenameValue(void)` — returns a pointer to `xmlOutputBufferCreateFilenameValue`.
1844///
1845/// # SAFETY
1846///
1847/// The function touches crate-global state only; it is safe
1848/// as long as the caller respects the library's global
1849/// initialization/cleanup ordering (xmlInitParser before use,
1850/// xmlCleanupParser only after all users are done).
1851///
1852/// Violating the global lifecycle ordering, or calling this after
1853/// teardown or from a signal handler, is undefined behavior.
1854#[no_mangle]
1855pub unsafe extern "C" fn __xmlOutputBufferCreateFilenameValue() -> *mut Option<
1856 unsafe extern "C" fn(
1857 *const c_char,
1858 crate::abi::structs::xmlCharEncodingHandlerPtr,
1859 c_int,
1860 ) -> *mut crate::abi::structs::_xmlOutputBuffer,
1861> {
1862 // SAFETY: returning a pointer to an exported static; the caller may
1863 // read/write it exactly as with upstream's deprecated accessor.
1864 core::ptr::addr_of_mut!(xmlOutputBufferCreateFilenameValue)
1865}
1866
1867/// Upstream `__xmlParserDebugEntities(void)` — returns a pointer to `xmlParserDebugEntities`.
1868///
1869/// # SAFETY
1870///
1871/// The function touches crate-global state only; it is safe
1872/// as long as the caller respects the library's global
1873/// initialization/cleanup ordering (xmlInitParser before use,
1874/// xmlCleanupParser only after all users are done).
1875///
1876/// Violating the global lifecycle ordering, or calling this after
1877/// teardown or from a signal handler, is undefined behavior.
1878#[no_mangle]
1879pub unsafe extern "C" fn __xmlParserDebugEntities() -> *mut c_int {
1880 // SAFETY: returning a pointer to an exported static; the caller may
1881 // read/write it exactly as with upstream's deprecated accessor.
1882 core::ptr::addr_of_mut!(xmlParserDebugEntities)
1883}
1884
1885/// Upstream `__xmlParserInputBufferCreateFilenameValue(void)` — returns a pointer to `xmlParserInputBufferCreateFilenameValue`.
1886///
1887/// # SAFETY
1888///
1889/// The function touches crate-global state only; it is safe
1890/// as long as the caller respects the library's global
1891/// initialization/cleanup ordering (xmlInitParser before use,
1892/// xmlCleanupParser only after all users are done).
1893///
1894/// Violating the global lifecycle ordering, or calling this after
1895/// teardown or from a signal handler, is undefined behavior.
1896#[no_mangle]
1897pub unsafe extern "C" fn __xmlParserInputBufferCreateFilenameValue() -> *mut Option<
1898 unsafe extern "C" fn(*const c_char, c_int) -> *mut crate::abi::structs::_xmlParserInputBuffer,
1899> {
1900 // SAFETY: returning a pointer to an exported static; the caller may
1901 // read/write it exactly as with upstream's deprecated accessor.
1902 core::ptr::addr_of_mut!(xmlParserInputBufferCreateFilenameValue)
1903}
1904
1905/// Upstream `__xmlParserVersion(void)` — returns a pointer to `xmlParserVersion`.
1906///
1907/// # SAFETY
1908///
1909/// The function touches crate-global state only; it is safe
1910/// as long as the caller respects the library's global
1911/// initialization/cleanup ordering (xmlInitParser before use,
1912/// xmlCleanupParser only after all users are done).
1913///
1914/// Violating the global lifecycle ordering, or calling this after
1915/// teardown or from a signal handler, is undefined behavior.
1916#[no_mangle]
1917pub unsafe extern "C" fn __xmlParserVersion() -> *mut *const c_char {
1918 // SAFETY: returning a pointer to an exported static; the caller may
1919 // read/write it exactly as with upstream's deprecated accessor.
1920 core::ptr::addr_of_mut!(xmlParserVersion)
1921}
1922
1923/// Upstream `__xmlPedanticParserDefaultValue(void)` — returns a pointer to `xmlPedanticParserDefaultValue`.
1924///
1925/// # SAFETY
1926///
1927/// The function touches crate-global state only; it is safe
1928/// as long as the caller respects the library's global
1929/// initialization/cleanup ordering (xmlInitParser before use,
1930/// xmlCleanupParser only after all users are done).
1931///
1932/// Violating the global lifecycle ordering, or calling this after
1933/// teardown or from a signal handler, is undefined behavior.
1934#[no_mangle]
1935pub unsafe extern "C" fn __xmlPedanticParserDefaultValue() -> *mut c_int {
1936 // SAFETY: returning a pointer to an exported static; the caller may
1937 // read/write it exactly as with upstream's deprecated accessor.
1938 core::ptr::addr_of_mut!(xmlPedanticParserDefaultValue)
1939}
1940
1941/// Upstream `__xmlRegisterNodeDefaultValue(void)` — returns a pointer to `xmlRegisterNodeDefaultValue`.
1942///
1943/// # SAFETY
1944///
1945/// The function touches crate-global state only; it is safe
1946/// as long as the caller respects the library's global
1947/// initialization/cleanup ordering (xmlInitParser before use,
1948/// xmlCleanupParser only after all users are done).
1949///
1950/// Violating the global lifecycle ordering, or calling this after
1951/// teardown or from a signal handler, is undefined behavior.
1952#[no_mangle]
1953pub unsafe extern "C" fn __xmlRegisterNodeDefaultValue(
1954) -> *mut Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1955 // SAFETY: returning a pointer to an exported static; the caller may
1956 // read/write it exactly as with upstream's deprecated accessor.
1957 core::ptr::addr_of_mut!(xmlRegisterNodeDefaultValue)
1958}
1959
1960/// Upstream `__xmlStructuredError(void)` — returns a pointer to `xmlStructuredError`.
1961///
1962/// # SAFETY
1963///
1964/// The function touches crate-global state only; it is safe
1965/// as long as the caller respects the library's global
1966/// initialization/cleanup ordering (xmlInitParser before use,
1967/// xmlCleanupParser only after all users are done).
1968///
1969/// Violating the global lifecycle ordering, or calling this after
1970/// teardown or from a signal handler, is undefined behavior.
1971#[no_mangle]
1972pub unsafe extern "C" fn __xmlStructuredError() -> *mut Option<xmlStructuredErrorFunc> {
1973 // SAFETY: returning a pointer to an exported static; the caller may
1974 // read/write it exactly as with upstream's deprecated accessor.
1975 core::ptr::addr_of_mut!(xmlStructuredError)
1976}
1977
1978/// Upstream `__xmlStructuredErrorContext(void)` — returns a pointer to `xmlStructuredErrorContext`.
1979///
1980/// # SAFETY
1981///
1982/// The function touches crate-global state only; it is safe
1983/// as long as the caller respects the library's global
1984/// initialization/cleanup ordering (xmlInitParser before use,
1985/// xmlCleanupParser only after all users are done).
1986///
1987/// Violating the global lifecycle ordering, or calling this after
1988/// teardown or from a signal handler, is undefined behavior.
1989#[no_mangle]
1990pub unsafe extern "C" fn __xmlStructuredErrorContext() -> *mut *mut c_void {
1991 // SAFETY: returning a pointer to an exported static; the caller may
1992 // read/write it exactly as with upstream's deprecated accessor.
1993 core::ptr::addr_of_mut!(xmlStructuredErrorContext)
1994}
1995
1996/// Upstream `__xmlSubstituteEntitiesDefaultValue(void)` — returns a pointer to `xmlSubstituteEntitiesDefaultValue`.
1997///
1998/// # SAFETY
1999///
2000/// The function touches crate-global state only; it is safe
2001/// as long as the caller respects the library's global
2002/// initialization/cleanup ordering (xmlInitParser before use,
2003/// xmlCleanupParser only after all users are done).
2004///
2005/// Violating the global lifecycle ordering, or calling this after
2006/// teardown or from a signal handler, is undefined behavior.
2007#[no_mangle]
2008pub unsafe extern "C" fn __xmlSubstituteEntitiesDefaultValue() -> *mut c_int {
2009 // SAFETY: returning a pointer to an exported static; the caller may
2010 // read/write it exactly as with upstream's deprecated accessor.
2011 core::ptr::addr_of_mut!(xmlSubstituteEntitiesDefaultValue)
2012}
2013
2014// ═══════════════════════════════════════════════════════════════════════════════
2015// Regression court — xmlLastError mirror concurrency (11.1-X)
2016// ═══════════════════════════════════════════════════════════════════════════════
2017//
2018// R-000135 discovery during 11.1-X: the exported `xmlLastError` mirror was
2019// deep-copied and freed without synchronization, so concurrent error raises
2020// on different threads double-freed the mirror strings. The parallel lib
2021// test suite observed this as `double free or corruption (!prev)` aborts
2022// (xml::errors tests racing with any other raising thread). The writers are
2023// serialized via LAST_ERROR_MIRROR_LOCK; these courts hammer the exact
2024// interleavings and must complete without crashing.
2025
2026#[cfg(test)]
2027mod tests {
2028 use super::*;
2029 use crate::abi::allocator::xmlMallocImpl;
2030 use crate::abi::structs::_xmlError;
2031 use crate::xml::globals;
2032 use core::ptr;
2033
2034 /// Allocate a NUL-terminated C string owned by xmlMallocImpl (the same
2035 /// allocator the thread-local error slot uses).
2036 ///
2037 /// # Safety
2038 ///
2039 /// - `s` must be a valid string; the returned pointer is
2040 /// allocator-owned, valid for `bytes.len() + 1` bytes, and must be
2041 /// freed with `xmlFreeImpl` exactly once.
2042 unsafe fn alloc_cstr(s: &str) -> *mut c_char {
2043 let bytes = s.as_bytes();
2044 let p = unsafe { xmlMallocImpl(bytes.len() + 1) as *mut c_char };
2045 assert!(!p.is_null(), "alloc_cstr: xmlMallocImpl failed");
2046 unsafe {
2047 ptr::copy_nonoverlapping(bytes.as_ptr(), p as *mut u8, bytes.len());
2048 *((p as *mut u8).add(bytes.len())) = 0;
2049 }
2050 p
2051 }
2052
2053 /// Build an owned `_xmlError` with distinct string fields.
2054 ///
2055 /// # Safety
2056 ///
2057 /// - Every `message`/`file`/`str1` field is an `alloc_cstr`
2058 /// allocation that the returned struct owns; the caller must free
2059 /// them with `xmlFreeImpl` exactly once (e.g. via
2060 /// `reset_last_error`), or the fields leak.
2061 unsafe fn build_error(tag: &str) -> _xmlError {
2062 _xmlError {
2063 domain: 1,
2064 code: 2,
2065 message: unsafe { alloc_cstr(&format!("msg {tag}")) },
2066 level: 3,
2067 file: unsafe { alloc_cstr(&format!("file {tag}")) },
2068 line: 4,
2069 str1: unsafe { alloc_cstr(&format!("str1 {tag}")) },
2070 str2: ptr::null_mut(),
2071 str3: ptr::null_mut(),
2072 int1: 0,
2073 int2: 0,
2074 ctxt: ptr::null_mut(),
2075 node: ptr::null_mut(),
2076 }
2077 }
2078
2079 /// Concurrent sync/reset hammer: one thread raises errors while another
2080 /// resets. Before the mirror lock this double-freed the shared strings;
2081 /// the test crashes (SIGABRT) under the old code and passes now.
2082 ///
2083 /// # Safety
2084 ///
2085 /// - `build_error` allocates owned string fields with `xmlFreeImpl`;
2086 /// `set_last_error` deep-copies them into the mirror and the
2087 /// thread-local slot, and `reset_last_error` frees each copy exactly
2088 /// once; the mirror is internally serialized by `LAST_ERROR_MIRROR_LOCK`.
2089 #[test]
2090 fn test_last_error_mirror_concurrent_sync_reset() {
2091 let sync = std::thread::spawn(|| {
2092 for i in 0..400 {
2093 unsafe { globals::set_last_error(build_error(&format!("sync {i}"))) };
2094 }
2095 });
2096 let reset = std::thread::spawn(|| {
2097 for _ in 0..400 {
2098 globals::reset_last_error();
2099 }
2100 });
2101 sync.join().unwrap();
2102 reset.join().unwrap();
2103 // Leave the mirror in a clean state for later tests. (No thread-local
2104 // assertion: the harness reuses OS threads across tests, so a prior
2105 // test's error may legitimately live in this thread's slot.)
2106 globals::reset_last_error();
2107 }
2108
2109 /// Many threads raising concurrently (the full parallel-suite shape that
2110 /// originally aborted in `test_encode_entities_reentrant_*` victims).
2111 ///
2112 /// # Safety
2113 ///
2114 /// - Each thread's `build_error` allocations are owned by the
2115 /// thread-local last-error slot and the locked mirror; every
2116 /// `set_last_error`/`reset_last_error` pair frees owned strings
2117 /// exactly once, and the final `reset_last_error` leaves the mirror
2118 /// clean.
2119 #[test]
2120 fn test_last_error_mirror_many_threads() {
2121 let mut handles = Vec::new();
2122 for t in 0..8 {
2123 handles.push(std::thread::spawn(move || {
2124 for i in 0..150 {
2125 unsafe { globals::set_last_error(build_error(&format!("t{t} i{i}"))) };
2126 if i % 7 == 0 {
2127 globals::reset_last_error();
2128 }
2129 }
2130 }));
2131 }
2132 for h in handles {
2133 h.join().unwrap();
2134 }
2135 globals::reset_last_error();
2136 }
2137}