Skip to main content

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/// `const xmlChar xsltExtMarker[]` — empty string used to mark extension
653/// nodes (upstream transform.c).
654#[no_mangle]
655pub static xsltExtMarker: [xmlChar; 1] = [0];
656
657/// `xsltDocLoaderFunc xsltDocDefaultLoader` — the document loader callback.
658/// Upstream defaults to `xsltDocDefaultLoaderFunc`; the candidate defaults
659/// to NULL and its internal loader path is used — documented safe
660/// divergence (residual R-000135).
661#[no_mangle]
662pub static mut xsltDocDefaultLoader: Option<
663    unsafe extern "C" fn(
664        *const xmlChar,
665        *mut c_void,
666        c_int,
667        *mut crate::abi::structs::_xsltStylesheet,
668        *mut crate::abi::structs::_xsltTransformContext,
669    ) -> *mut crate::abi::structs::_xmlDoc,
670> = None;
671
672// ═══════════════════════════════════════════════════════════════════════════════
673// I/O filename callback globals (upstream xmlIO.h)
674// ═══════════════════════════════════════════════════════════════════════════════
675
676/// `xmlParserInputBufferCreateFilenameFunc xmlParserInputBufferCreateFilenameValue`
677#[no_mangle]
678pub static mut xmlParserInputBufferCreateFilenameValue: Option<
679    unsafe extern "C" fn(*const c_char, c_int) -> *mut crate::abi::structs::_xmlParserInputBuffer,
680> = None;
681
682/// `xmlOutputBufferCreateFilenameFunc xmlOutputBufferCreateFilenameValue`
683#[no_mangle]
684pub static mut xmlOutputBufferCreateFilenameValue: Option<
685    unsafe extern "C" fn(
686        *const c_char,
687        crate::abi::structs::xmlCharEncodingHandlerPtr,
688        c_int,
689    ) -> *mut crate::abi::structs::_xmlOutputBuffer,
690> = None;
691
692// ═══════════════════════════════════════════════════════════════════════════════
693// Default SAX v1 handler structs + locator (upstream globals.c 2.15.3)
694// ═══════════════════════════════════════════════════════════════════════════════
695//
696// `const xmlSAXHandlerV1 xmlDefaultSAXHandler` and `htmlDefaultSAXHandler`
697// (parser.h / HTMLparser.h), plus `const xmlSAXLocator xmlDefaultSAXLocator`.
698// The handler instances reproduce the upstream initializer lists exactly
699// (globals.c); every referenced xmlSAX2* entry point is a real candidate
700// export.
701
702/// `const xmlSAXHandlerV1 xmlDefaultSAXHandler` (globals.c 2.15.3).
703#[no_mangle]
704pub static xmlDefaultSAXHandler: crate::abi::structs::_xmlSAXHandlerV1 =
705    crate::abi::structs::_xmlSAXHandlerV1 {
706        internalSubset: Some(crate::abi::exports_xml2::xmlSAX2InternalSubset),
707        isStandalone: Some(crate::abi::exports_xml2::xmlSAX2IsStandalone),
708        hasInternalSubset: Some(crate::abi::exports_xml2::xmlSAX2HasInternalSubset),
709        hasExternalSubset: Some(crate::abi::exports_xml2::xmlSAX2HasExternalSubset),
710        resolveEntity: Some(crate::abi::exports_xml2::xmlSAX2ResolveEntity),
711        getEntity: Some(crate::abi::exports_xml2::xmlSAX2GetEntity),
712        entityDecl: Some(crate::abi::exports_xml2::xmlSAX2EntityDecl),
713        notationDecl: Some(crate::abi::exports_xml2::xmlSAX2NotationDecl),
714        attributeDecl: Some(crate::abi::exports_xml2::xmlSAX2AttributeDecl),
715        elementDecl: Some(crate::abi::exports_xml2::xmlSAX2ElementDecl),
716        unparsedEntityDecl: Some(crate::abi::exports_xml2::xmlSAX2UnparsedEntityDecl),
717        setDocumentLocator: Some(crate::abi::exports_xml2::xmlSAX2SetDocumentLocator),
718        startDocument: Some(crate::abi::exports_xml2::xmlSAX2StartDocument),
719        endDocument: Some(crate::abi::exports_xml2::xmlSAX2EndDocument),
720        startElement: Some(crate::abi::exports_xml2::xmlSAX2StartElement),
721        endElement: Some(crate::abi::exports_xml2::xmlSAX2EndElement),
722        reference: Some(crate::abi::exports_xml2::xmlSAX2Reference),
723        characters: Some(crate::abi::exports_xml2::xmlSAX2Characters),
724        ignorableWhitespace: Some(crate::abi::exports_xml2::xmlSAX2IgnorableWhitespace),
725        processingInstruction: Some(crate::abi::exports_xml2::xmlSAX2ProcessingInstruction),
726        comment: Some(crate::abi::exports_xml2::xmlSAX2Comment),
727        warning: Some(crate::xml::errors::xmlParserWarning),
728        error: Some(crate::xml::errors::xmlParserError),
729        fatalError: Some(crate::xml::errors::xmlParserError),
730        getParameterEntity: Some(crate::abi::exports_xml2::xmlSAX2GetParameterEntity),
731        cdataBlock: Some(crate::abi::exports_xml2::xmlSAX2CDataBlock),
732        externalSubset: Some(crate::abi::exports_xml2::xmlSAX2ExternalSubset),
733        initialized: 1,
734    };
735
736/// `const xmlSAXHandlerV1 htmlDefaultSAXHandler` (globals.c 2.15.3).
737#[no_mangle]
738pub static htmlDefaultSAXHandler: crate::abi::structs::_xmlSAXHandlerV1 =
739    crate::abi::structs::_xmlSAXHandlerV1 {
740        internalSubset: Some(crate::abi::exports_xml2::xmlSAX2InternalSubset),
741        isStandalone: None,
742        hasInternalSubset: None,
743        hasExternalSubset: None,
744        resolveEntity: None,
745        getEntity: Some(crate::abi::exports_xml2::xmlSAX2GetEntity),
746        entityDecl: None,
747        notationDecl: None,
748        attributeDecl: None,
749        elementDecl: None,
750        unparsedEntityDecl: None,
751        setDocumentLocator: Some(crate::abi::exports_xml2::xmlSAX2SetDocumentLocator),
752        startDocument: Some(crate::abi::exports_xml2::xmlSAX2StartDocument),
753        endDocument: Some(crate::abi::exports_xml2::xmlSAX2EndDocument),
754        startElement: Some(crate::abi::exports_xml2::xmlSAX2StartElement),
755        endElement: Some(crate::abi::exports_xml2::xmlSAX2EndElement),
756        reference: None,
757        characters: Some(crate::abi::exports_xml2::xmlSAX2Characters),
758        ignorableWhitespace: Some(crate::abi::exports_xml2::xmlSAX2IgnorableWhitespace),
759        processingInstruction: Some(crate::abi::exports_xml2::xmlSAX2ProcessingInstruction),
760        comment: Some(crate::abi::exports_xml2::xmlSAX2Comment),
761        warning: Some(crate::xml::errors::xmlParserWarning),
762        error: Some(crate::xml::errors::xmlParserError),
763        fatalError: Some(crate::xml::errors::xmlParserError),
764        getParameterEntity: None,
765        cdataBlock: Some(crate::abi::exports_xml2::xmlSAX2CDataBlock),
766        externalSubset: None,
767        initialized: 1,
768    };
769
770/// `const xmlSAXLocator xmlDefaultSAXLocator` (globals.c 2.15.3).
771#[no_mangle]
772pub static xmlDefaultSAXLocator: crate::abi::callbacks::_xmlSAXLocator =
773    crate::abi::callbacks::_xmlSAXLocator {
774        getPublicId: Some(crate::abi::exports_xml2::xmlSAX2GetPublicId),
775        getSystemId: Some(crate::abi::exports_xml2::xmlSAX2GetSystemId),
776        getLineNumber: Some(crate::abi::exports_xml2::xmlSAX2GetLineNumber),
777        getColumnNumber: Some(crate::abi::exports_xml2::xmlSAX2GetColumnNumber),
778    };
779
780// ═══════════════════════════════════════════════════════════════════════════════
781// xmlLastError — the exported C global mirror of the thread-local error state
782// ═══════════════════════════════════════════════════════════════════════════════
783//
784// Upstream `XMLPUBVAR xmlError xmlLastError` (xmlerror.h). The candidate's
785// internal error state is thread-local (safe divergence, more correct than
786// upstream's racy global); this mirror is deep-copied on every error raise
787// and freed on reset, so C consumers observe upstream-equivalent lifetime
788// semantics. Residual R-000135.
789
790/// `xmlError xmlLastError` — most recent error (mirror).
791#[no_mangle]
792pub static mut xmlLastError: crate::abi::structs::_xmlError = crate::abi::structs::_xmlError {
793    domain: 0,
794    code: 0,
795    message: core::ptr::null_mut(),
796    level: 0,
797    file: core::ptr::null_mut(),
798    line: 0,
799    str1: core::ptr::null_mut(),
800    str2: core::ptr::null_mut(),
801    str3: core::ptr::null_mut(),
802    int1: 0,
803    int2: 0,
804    ctxt: core::ptr::null_mut(),
805    node: core::ptr::null_mut(),
806};
807
808/// Deep-copy `err` into the exported `xmlLastError` global.
809///
810/// The string fields are copied with `libc::malloc`/`memcpy` so the mirror
811/// owns them (previous mirror strings are freed first — upstream
812/// xmlResetError semantics).
813///
814/// # SAFETY
815///
816/// - `err` must point to a valid `_xmlError` whose string fields are
817///   NUL-terminated or NULL.
818pub unsafe fn sync_xml_last_error(err: *const crate::abi::structs::_xmlError) {
819    if err.is_null() {
820        return;
821    }
822    let _guard = LAST_ERROR_MIRROR_LOCK.lock();
823    unsafe { sync_xml_last_error_locked(err) };
824}
825
826/// Mirror write helper; caller must hold `LAST_ERROR_MIRROR_LOCK`.
827unsafe fn sync_xml_last_error_locked(err: *const crate::abi::structs::_xmlError) {
828    unsafe {
829        reset_xml_last_error_locked();
830        let src = &*err;
831        let dst = core::ptr::addr_of_mut!(xmlLastError);
832        (*dst).domain = src.domain;
833        (*dst).code = src.code;
834        (*dst).level = src.level;
835        (*dst).line = src.line;
836        (*dst).int1 = src.int1;
837        (*dst).int2 = src.int2;
838        (*dst).ctxt = src.ctxt;
839        (*dst).node = src.node;
840        (*dst).message = dup_cstr(src.message as *const u8);
841        (*dst).file = dup_cstr(src.file as *const u8);
842        (*dst).str1 = dup_cstr(src.str1 as *const u8);
843        (*dst).str2 = dup_cstr(src.str2 as *const u8);
844        (*dst).str3 = dup_cstr(src.str3 as *const u8);
845    }
846}
847
848/// Reset the exported `xmlLastError` global, freeing owned strings
849/// (upstream xmlResetError).
850///
851/// # SAFETY
852///
853/// Only call while no other thread is reading the global (upstream has the
854/// same race; documented).
855pub unsafe fn reset_xml_last_error() {
856    let _guard = LAST_ERROR_MIRROR_LOCK.lock();
857    unsafe { reset_xml_last_error_locked() };
858}
859
860/// Mirror reset helper; caller must hold `LAST_ERROR_MIRROR_LOCK`.
861unsafe fn reset_xml_last_error_locked() {
862    unsafe {
863        let dst = core::ptr::addr_of_mut!(xmlLastError);
864        if !(*dst).message.is_null() {
865            libc::free((*dst).message as *mut libc::c_void);
866        }
867        if !(*dst).file.is_null() {
868            libc::free((*dst).file as *mut libc::c_void);
869        }
870        if !(*dst).str1.is_null() {
871            libc::free((*dst).str1 as *mut libc::c_void);
872        }
873        if !(*dst).str2.is_null() {
874            libc::free((*dst).str2 as *mut libc::c_void);
875        }
876        if !(*dst).str3.is_null() {
877            libc::free((*dst).str3 as *mut libc::c_void);
878        }
879        *dst = crate::abi::structs::_xmlError {
880            domain: 0,
881            code: 0,
882            message: core::ptr::null_mut(),
883            level: 0,
884            file: core::ptr::null_mut(),
885            line: 0,
886            str1: core::ptr::null_mut(),
887            str2: core::ptr::null_mut(),
888            str3: core::ptr::null_mut(),
889            int1: 0,
890            int2: 0,
891            ctxt: core::ptr::null_mut(),
892            node: core::ptr::null_mut(),
893        };
894    }
895}
896
897/// Heap-copy a NUL-terminated string (NULL-safe).
898unsafe fn dup_cstr(s: *const u8) -> *mut c_char {
899    if s.is_null() {
900        return core::ptr::null_mut();
901    }
902    unsafe {
903        let len = libc::strlen(s as *const libc::c_char) as usize;
904        let p = libc::malloc(len + 1) as *mut u8;
905        if p.is_null() {
906            return core::ptr::null_mut();
907        }
908        libc::memcpy(p as *mut libc::c_void, s as *const libc::c_void, len + 1);
909        p as *mut c_char
910    }
911}
912
913// ═══════════════════════════════════════════════════════════════════════════════
914// Default accessor functions (upstream parser.h / tree.h / xmlsave.h)
915// ═══════════════════════════════════════════════════════════════════════════════
916//
917// The deprecated `xmlXxxDefault(v)` accessors set the corresponding global
918// when `v != 0` and return the (new) value — upstream semantics (they
919// predate the plain globals; the modern behavior is conditional-set-and-
920// return, see upstream globals.c / parser.c).
921
922/// Upstream `xmlKeepBlanksDefault(int v)`.
923///
924/// # SAFETY
925///
926/// The function touches crate-global state only; it is safe
927/// as long as the caller respects the library's global
928/// initialization/cleanup ordering (xmlInitParser before use,
929/// xmlCleanupParser only after all users are done).
930///
931/// Violating the global lifecycle ordering, or calling this after
932/// teardown or from a signal handler, is undefined behavior.
933#[no_mangle]
934pub unsafe extern "C" fn xmlKeepBlanksDefault(v: c_int) -> c_int {
935    unsafe {
936        if v != 0 {
937            xmlKeepBlanksDefaultValue = v;
938        }
939        xmlKeepBlanksDefaultValue
940    }
941}
942
943/// Upstream `xmlLineNumbersDefault(int v)`.
944///
945/// # SAFETY
946///
947/// The function touches crate-global state only; it is safe
948/// as long as the caller respects the library's global
949/// initialization/cleanup ordering (xmlInitParser before use,
950/// xmlCleanupParser only after all users are done).
951///
952/// Violating the global lifecycle ordering, or calling this after
953/// teardown or from a signal handler, is undefined behavior.
954#[no_mangle]
955pub unsafe extern "C" fn xmlLineNumbersDefault(v: c_int) -> c_int {
956    unsafe {
957        if v != 0 {
958            xmlLineNumbersDefaultValue = v;
959        }
960        xmlLineNumbersDefaultValue
961    }
962}
963
964/// Upstream `xmlSubstituteEntitiesDefault(int v)`.
965///
966/// # SAFETY
967///
968/// The function touches crate-global state only; it is safe
969/// as long as the caller respects the library's global
970/// initialization/cleanup ordering (xmlInitParser before use,
971/// xmlCleanupParser only after all users are done).
972///
973/// Violating the global lifecycle ordering, or calling this after
974/// teardown or from a signal handler, is undefined behavior.
975#[no_mangle]
976pub unsafe extern "C" fn xmlSubstituteEntitiesDefault(v: c_int) -> c_int {
977    unsafe {
978        if v != 0 {
979            xmlSubstituteEntitiesDefaultValue = v;
980        }
981        xmlSubstituteEntitiesDefaultValue
982    }
983}
984
985/// Upstream `xmlPedanticParserDefault(int v)`.
986///
987/// # SAFETY
988///
989/// The function touches crate-global state only; it is safe
990/// as long as the caller respects the library's global
991/// initialization/cleanup ordering (xmlInitParser before use,
992/// xmlCleanupParser only after all users are done).
993///
994/// Violating the global lifecycle ordering, or calling this after
995/// teardown or from a signal handler, is undefined behavior.
996#[no_mangle]
997pub unsafe extern "C" fn xmlPedanticParserDefault(v: c_int) -> c_int {
998    unsafe {
999        if v != 0 {
1000            xmlPedanticParserDefaultValue = v;
1001        }
1002        xmlPedanticParserDefaultValue
1003    }
1004}
1005
1006/// Upstream `xmlDoValidityCheckingDefaultValue` accessor is the global
1007/// itself; `xmlGetWarningsDefaultValue` likewise (no accessor functions
1008/// exist for those in upstream 2.15).
1009/// Upstream `xmlRegisterNodeDefault(xmlRegisterNodeFunc func)`.
1010///
1011/// # SAFETY
1012///
1013///
1014/// - `func` must be a valid callback (or None);
1015///   the callback is invoked with the documented context pointer and
1016///   must itself uphold the same pointer invariants.
1017///
1018/// The caller must not race this call with concurrent mutation of the
1019/// same objects from other threads (per-object state is not internally
1020/// synchronized). Violating any of the above is undefined behavior.
1021///
1022/// Exercised by the C-API differential courts
1023/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1024/// courts; those pass byte-for-byte against the upstream oracle.
1025#[no_mangle]
1026pub unsafe extern "C" fn xmlRegisterNodeDefault(
1027    func: Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)>,
1028) -> Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1029    unsafe {
1030        // UPSTREAM-PARITY (tree.c): registering any callback arms the
1031        // xmlRegisterCallbacks gate.
1032        XML_REGISTER_CALLBACKS.store(true, core::sync::atomic::Ordering::Relaxed);
1033        if func.is_some() {
1034            xmlRegisterNodeDefaultValue = func;
1035        }
1036        xmlRegisterNodeDefaultValue
1037    }
1038}
1039
1040/// Upstream `xmlDeregisterNodeDefault(xmlDeregisterNodeFunc func)`.
1041///
1042/// # SAFETY
1043///
1044///
1045/// - `func` must be a valid callback (or None);
1046///   the callback is invoked with the documented context pointer and
1047///   must itself uphold the same pointer invariants.
1048///
1049/// The caller must not race this call with concurrent mutation of the
1050/// same objects from other threads (per-object state is not internally
1051/// synchronized). Violating any of the above is undefined behavior.
1052///
1053/// Exercised by the C-API differential courts
1054/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1055/// courts; those pass byte-for-byte against the upstream oracle.
1056#[no_mangle]
1057pub unsafe extern "C" fn xmlDeregisterNodeDefault(
1058    func: Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)>,
1059) -> Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1060    unsafe {
1061        // UPSTREAM-PARITY (tree.c): registering any callback arms the
1062        // xmlRegisterCallbacks gate.
1063        XML_REGISTER_CALLBACKS.store(true, core::sync::atomic::Ordering::Relaxed);
1064        if func.is_some() {
1065            xmlDeregisterNodeDefaultValue = func;
1066        }
1067        xmlDeregisterNodeDefaultValue
1068    }
1069}
1070
1071/// Upstream `__xmlIndentTreeOutput(void)` (parser.h) — returns a pointer to
1072/// the `xmlIndentTreeOutput` global.
1073///
1074/// # SAFETY
1075///
1076/// The function touches crate-global state only; it is safe
1077/// as long as the caller respects the library's global
1078/// initialization/cleanup ordering (xmlInitParser before use,
1079/// xmlCleanupParser only after all users are done).
1080///
1081/// Violating the global lifecycle ordering, or calling this after
1082/// teardown or from a signal handler, is undefined behavior.
1083#[no_mangle]
1084pub unsafe extern "C" fn __xmlIndentTreeOutput() -> *mut c_int {
1085    core::ptr::addr_of_mut!(xmlIndentTreeOutput)
1086}
1087
1088/// Upstream `__xmlSaveNoEmptyTags(void)` (parser.h) — returns a pointer to
1089/// the `xmlSaveNoEmptyTags` global.
1090///
1091/// # SAFETY
1092///
1093/// The function touches crate-global state only; it is safe
1094/// as long as the caller respects the library's global
1095/// initialization/cleanup ordering (xmlInitParser before use,
1096/// xmlCleanupParser only after all users are done).
1097///
1098/// Violating the global lifecycle ordering, or calling this after
1099/// teardown or from a signal handler, is undefined behavior.
1100#[no_mangle]
1101pub unsafe extern "C" fn __xmlSaveNoEmptyTags() -> *mut c_int {
1102    core::ptr::addr_of_mut!(xmlSaveNoEmptyTags)
1103}
1104
1105/// Upstream `__xmlTreeIndentString(void)` (parser.h) — returns a pointer to
1106/// the `xmlTreeIndentString` global.
1107///
1108/// # SAFETY
1109///
1110/// The function touches crate-global state only; it is safe
1111/// as long as the caller respects the library's global
1112/// initialization/cleanup ordering (xmlInitParser before use,
1113/// xmlCleanupParser only after all users are done).
1114///
1115/// Violating the global lifecycle ordering, or calling this after
1116/// teardown or from a signal handler, is undefined behavior.
1117#[no_mangle]
1118pub unsafe extern "C" fn __xmlTreeIndentString() -> *mut *const xmlChar {
1119    core::ptr::addr_of_mut!(xmlTreeIndentString)
1120}
1121
1122// ═══════════════════════════════════════════════════════════════════════════════
1123// xmlThrDef* accessors (upstream threads.c / globals.c)
1124// ═══════════════════════════════════════════════════════════════════════════════
1125//
1126// The deprecated `xmlThrDef*` family reads/writes the public globals with
1127// the upstream semantics: when `v != 0` the global is set, and the (new)
1128// value is returned. In upstream these were thread-local definitions before
1129// the globals became plain variables; the modern behavior is exactly this
1130// conditional-set-and-return on the global (see upstream globals.c).
1131
1132/// Upstream `xmlThrDefDoValidityCheckingDefaultValue(int v)`.
1133///
1134/// # SAFETY
1135///
1136/// The function touches crate-global state only; it is safe
1137/// as long as the caller respects the library's global
1138/// initialization/cleanup ordering (xmlInitParser before use,
1139/// xmlCleanupParser only after all users are done).
1140///
1141/// Violating the global lifecycle ordering, or calling this after
1142/// teardown or from a signal handler, is undefined behavior.
1143#[no_mangle]
1144pub unsafe extern "C" fn xmlThrDefDoValidityCheckingDefaultValue(v: c_int) -> c_int {
1145    unsafe {
1146        if v != 0 {
1147            xmlDoValidityCheckingDefaultValue = v;
1148        }
1149        xmlDoValidityCheckingDefaultValue
1150    }
1151}
1152
1153/// Upstream `xmlThrDefGetWarningsDefaultValue(int v)`.
1154///
1155/// # SAFETY
1156///
1157/// The function touches crate-global state only; it is safe
1158/// as long as the caller respects the library's global
1159/// initialization/cleanup ordering (xmlInitParser before use,
1160/// xmlCleanupParser only after all users are done).
1161///
1162/// Violating the global lifecycle ordering, or calling this after
1163/// teardown or from a signal handler, is undefined behavior.
1164#[no_mangle]
1165pub unsafe extern "C" fn xmlThrDefGetWarningsDefaultValue(v: c_int) -> c_int {
1166    unsafe {
1167        if v != 0 {
1168            xmlGetWarningsDefaultValue = v;
1169        }
1170        xmlGetWarningsDefaultValue
1171    }
1172}
1173
1174/// Upstream `xmlThrDefLoadExtDtdDefaultValue(int v)`.
1175///
1176/// # SAFETY
1177///
1178/// The function touches crate-global state only; it is safe
1179/// as long as the caller respects the library's global
1180/// initialization/cleanup ordering (xmlInitParser before use,
1181/// xmlCleanupParser only after all users are done).
1182///
1183/// Violating the global lifecycle ordering, or calling this after
1184/// teardown or from a signal handler, is undefined behavior.
1185#[no_mangle]
1186pub unsafe extern "C" fn xmlThrDefLoadExtDtdDefaultValue(v: c_int) -> c_int {
1187    unsafe {
1188        if v != 0 {
1189            xmlLoadExtDtdDefaultValue = v;
1190        }
1191        xmlLoadExtDtdDefaultValue
1192    }
1193}
1194
1195/// Upstream `xmlThrDefPedanticParserDefaultValue(int v)`.
1196///
1197/// # SAFETY
1198///
1199/// The function touches crate-global state only; it is safe
1200/// as long as the caller respects the library's global
1201/// initialization/cleanup ordering (xmlInitParser before use,
1202/// xmlCleanupParser only after all users are done).
1203///
1204/// Violating the global lifecycle ordering, or calling this after
1205/// teardown or from a signal handler, is undefined behavior.
1206#[no_mangle]
1207pub unsafe extern "C" fn xmlThrDefPedanticParserDefaultValue(v: c_int) -> c_int {
1208    unsafe {
1209        if v != 0 {
1210            xmlPedanticParserDefaultValue = v;
1211        }
1212        xmlPedanticParserDefaultValue
1213    }
1214}
1215
1216/// Upstream `xmlThrDefLineNumbersDefaultValue(int v)`.
1217///
1218/// # SAFETY
1219///
1220/// The function touches crate-global state only; it is safe
1221/// as long as the caller respects the library's global
1222/// initialization/cleanup ordering (xmlInitParser before use,
1223/// xmlCleanupParser only after all users are done).
1224///
1225/// Violating the global lifecycle ordering, or calling this after
1226/// teardown or from a signal handler, is undefined behavior.
1227#[no_mangle]
1228pub unsafe extern "C" fn xmlThrDefLineNumbersDefaultValue(v: c_int) -> c_int {
1229    unsafe {
1230        if v != 0 {
1231            xmlLineNumbersDefaultValue = v;
1232        }
1233        xmlLineNumbersDefaultValue
1234    }
1235}
1236
1237/// Upstream `xmlThrDefKeepBlanksDefaultValue(int v)`.
1238///
1239/// # SAFETY
1240///
1241/// The function touches crate-global state only; it is safe
1242/// as long as the caller respects the library's global
1243/// initialization/cleanup ordering (xmlInitParser before use,
1244/// xmlCleanupParser only after all users are done).
1245///
1246/// Violating the global lifecycle ordering, or calling this after
1247/// teardown or from a signal handler, is undefined behavior.
1248#[no_mangle]
1249pub unsafe extern "C" fn xmlThrDefKeepBlanksDefaultValue(v: c_int) -> c_int {
1250    unsafe {
1251        if v != 0 {
1252            xmlKeepBlanksDefaultValue = v;
1253        }
1254        xmlKeepBlanksDefaultValue
1255    }
1256}
1257
1258/// Upstream `xmlThrDefSubstituteEntitiesDefaultValue(int v)`.
1259///
1260/// # SAFETY
1261///
1262/// The function touches crate-global state only; it is safe
1263/// as long as the caller respects the library's global
1264/// initialization/cleanup ordering (xmlInitParser before use,
1265/// xmlCleanupParser only after all users are done).
1266///
1267/// Violating the global lifecycle ordering, or calling this after
1268/// teardown or from a signal handler, is undefined behavior.
1269#[no_mangle]
1270pub unsafe extern "C" fn xmlThrDefSubstituteEntitiesDefaultValue(v: c_int) -> c_int {
1271    unsafe {
1272        if v != 0 {
1273            xmlSubstituteEntitiesDefaultValue = v;
1274        }
1275        xmlSubstituteEntitiesDefaultValue
1276    }
1277}
1278
1279/// Upstream `xmlThrDefParserDebugEntities(int v)`.
1280///
1281/// # SAFETY
1282///
1283/// The function touches crate-global state only; it is safe
1284/// as long as the caller respects the library's global
1285/// initialization/cleanup ordering (xmlInitParser before use,
1286/// xmlCleanupParser only after all users are done).
1287///
1288/// Violating the global lifecycle ordering, or calling this after
1289/// teardown or from a signal handler, is undefined behavior.
1290#[no_mangle]
1291pub unsafe extern "C" fn xmlThrDefParserDebugEntities(v: c_int) -> c_int {
1292    unsafe {
1293        if v != 0 {
1294            xmlParserDebugEntities = v;
1295        }
1296        xmlParserDebugEntities
1297    }
1298}
1299
1300/// Upstream `xmlThrDefIndentTreeOutput(int v)`.
1301///
1302/// # SAFETY
1303///
1304/// The function touches crate-global state only; it is safe
1305/// as long as the caller respects the library's global
1306/// initialization/cleanup ordering (xmlInitParser before use,
1307/// xmlCleanupParser only after all users are done).
1308///
1309/// Violating the global lifecycle ordering, or calling this after
1310/// teardown or from a signal handler, is undefined behavior.
1311#[no_mangle]
1312pub unsafe extern "C" fn xmlThrDefIndentTreeOutput(v: c_int) -> c_int {
1313    unsafe {
1314        if v != 0 {
1315            xmlIndentTreeOutput = v;
1316        }
1317        xmlIndentTreeOutput
1318    }
1319}
1320
1321/// Upstream `xmlThrDefTreeIndentString(const char *v)` — sets the indent
1322/// string when non-NULL and returns the current pointer.
1323///
1324/// # SAFETY
1325///
1326///
1327/// - `v` must point to valid NUL-terminated
1328///   strings (or NULL where the C contract allows) for the lifetime
1329///   of the call.
1330///
1331/// The caller must not race this call with concurrent mutation of the
1332/// same objects from other threads (per-object state is not internally
1333/// synchronized). Violating any of the above is undefined behavior.
1334///
1335/// Exercised by the C-API differential courts
1336/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1337/// courts; those pass byte-for-byte against the upstream oracle.
1338#[no_mangle]
1339pub unsafe extern "C" fn xmlThrDefTreeIndentString(v: *const c_char) -> *const c_char {
1340    unsafe {
1341        if !v.is_null() {
1342            xmlTreeIndentString = v as *const xmlChar;
1343        }
1344        xmlTreeIndentString as *const c_char
1345    }
1346}
1347
1348/// Upstream `xmlThrDefSaveNoEmptyTags(int v)`.
1349///
1350/// # SAFETY
1351///
1352/// The function touches crate-global state only; it is safe
1353/// as long as the caller respects the library's global
1354/// initialization/cleanup ordering (xmlInitParser before use,
1355/// xmlCleanupParser only after all users are done).
1356///
1357/// Violating the global lifecycle ordering, or calling this after
1358/// teardown or from a signal handler, is undefined behavior.
1359#[no_mangle]
1360pub unsafe extern "C" fn xmlThrDefSaveNoEmptyTags(v: c_int) -> c_int {
1361    unsafe {
1362        if v != 0 {
1363            xmlSaveNoEmptyTags = v;
1364        }
1365        xmlSaveNoEmptyTags
1366    }
1367}
1368
1369/// Upstream `xmlThrDefRegisterNodeDefault(xmlRegisterNodeFunc func)`.
1370///
1371/// # SAFETY
1372///
1373///
1374/// - `func` must be a valid callback (or None);
1375///   the callback is invoked with the documented context pointer and
1376///   must itself uphold the same pointer invariants.
1377///
1378/// The caller must not race this call with concurrent mutation of the
1379/// same objects from other threads (per-object state is not internally
1380/// synchronized). Violating any of the above is undefined behavior.
1381///
1382/// Exercised by the C-API differential courts
1383/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1384/// courts; those pass byte-for-byte against the upstream oracle.
1385#[no_mangle]
1386pub unsafe extern "C" fn xmlThrDefRegisterNodeDefault(
1387    func: Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)>,
1388) -> Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1389    unsafe {
1390        if func.is_some() {
1391            xmlRegisterNodeDefaultValue = func;
1392        }
1393        xmlRegisterNodeDefaultValue
1394    }
1395}
1396
1397/// Upstream `xmlThrDefDeregisterNodeDefault(xmlDeregisterNodeFunc func)`.
1398///
1399/// # SAFETY
1400///
1401///
1402/// - `func` must be a valid callback (or None);
1403///   the callback is invoked with the documented context pointer and
1404///   must itself uphold the same pointer invariants.
1405///
1406/// The caller must not race this call with concurrent mutation of the
1407/// same objects from other threads (per-object state is not internally
1408/// synchronized). Violating any of the above is undefined behavior.
1409///
1410/// Exercised by the C-API differential courts
1411/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1412/// courts; those pass byte-for-byte against the upstream oracle.
1413#[no_mangle]
1414pub unsafe extern "C" fn xmlThrDefDeregisterNodeDefault(
1415    func: Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)>,
1416) -> Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1417    unsafe {
1418        if func.is_some() {
1419            xmlDeregisterNodeDefaultValue = func;
1420        }
1421        xmlDeregisterNodeDefaultValue
1422    }
1423}
1424
1425/// Upstream `xmlThrDefSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc func)`.
1426///
1427/// # SAFETY
1428///
1429/// - `ctx` must be valid pointers (or NULL
1430///   where the upstream C contract allows), obtained from the
1431///   matching constructor/owner and not yet freed; the callee may
1432///   take or keep ownership exactly as the C API specifies.
1433///
1434/// - `func` must be a valid callback (or None);
1435///   the callback is invoked with the documented context pointer and
1436///   must itself uphold the same pointer invariants.
1437///
1438/// The caller must not race this call with concurrent mutation of the
1439/// same objects from other threads (per-object state is not internally
1440/// synchronized). Violating any of the above is undefined behavior.
1441///
1442/// Exercised by the C-API differential courts
1443/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1444/// courts; those pass byte-for-byte against the upstream oracle.
1445#[no_mangle]
1446pub unsafe extern "C" fn xmlThrDefSetGenericErrorFunc(
1447    ctx: *mut c_void,
1448    func: Option<xmlGenericErrorFunc>,
1449) {
1450    unsafe {
1451        xmlGenericErrorContext = ctx;
1452        xmlGenericError = func;
1453    }
1454}
1455
1456/// Upstream `xmlThrDefSetStructuredErrorFunc(void *ctx, xmlStructuredErrorFunc func)`.
1457///
1458/// # SAFETY
1459///
1460/// - `ctx` must be valid pointers (or NULL
1461///   where the upstream C contract allows), obtained from the
1462///   matching constructor/owner and not yet freed; the callee may
1463///   take or keep ownership exactly as the C API specifies.
1464///
1465/// - `func` must be a valid callback (or None);
1466///   the callback is invoked with the documented context pointer and
1467///   must itself uphold the same pointer invariants.
1468///
1469/// The caller must not race this call with concurrent mutation of the
1470/// same objects from other threads (per-object state is not internally
1471/// synchronized). Violating any of the above is undefined behavior.
1472///
1473/// Exercised by the C-API differential courts
1474/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1475/// courts; those pass byte-for-byte against the upstream oracle.
1476#[no_mangle]
1477pub unsafe extern "C" fn xmlThrDefSetStructuredErrorFunc(
1478    ctx: *mut c_void,
1479    func: Option<xmlStructuredErrorFunc>,
1480) {
1481    unsafe {
1482        xmlStructuredErrorContext = ctx;
1483        xmlStructuredError = func;
1484    }
1485}
1486
1487/// Upstream `xmlThrDefDefaultBufferSize(int v)`.
1488///
1489/// # SAFETY
1490///
1491/// The function touches crate-global state only; it is safe
1492/// as long as the caller respects the library's global
1493/// initialization/cleanup ordering (xmlInitParser before use,
1494/// xmlCleanupParser only after all users are done).
1495///
1496/// Violating the global lifecycle ordering, or calling this after
1497/// teardown or from a signal handler, is undefined behavior.
1498#[no_mangle]
1499pub unsafe extern "C" fn xmlThrDefDefaultBufferSize(v: c_int) -> c_int {
1500    unsafe {
1501        if v != 0 {
1502            xmlDefaultBufferSize = v;
1503        }
1504        xmlDefaultBufferSize
1505    }
1506}
1507
1508/// Upstream `xmlThrDefBufferAllocScheme(xmlBufferAllocationScheme v)`.
1509///
1510/// # SAFETY
1511///
1512/// The function touches crate-global state only; it is safe
1513/// as long as the caller respects the library's global
1514/// initialization/cleanup ordering (xmlInitParser before use,
1515/// xmlCleanupParser only after all users are done).
1516///
1517/// Violating the global lifecycle ordering, or calling this after
1518/// teardown or from a signal handler, is undefined behavior.
1519#[no_mangle]
1520pub unsafe extern "C" fn xmlThrDefBufferAllocScheme(v: c_int) -> c_int {
1521    unsafe {
1522        if v != 0 {
1523            xmlBufferAllocScheme = v;
1524        }
1525        xmlBufferAllocScheme
1526    }
1527}
1528
1529/// Upstream `xmlThrDefParserInputBufferCreateFilenameDefault(...)`.
1530///
1531/// # SAFETY
1532///
1533///
1534/// - `func` must be a valid callback (or None);
1535///   the callback is invoked with the documented context pointer and
1536///   must itself uphold the same pointer invariants.
1537///
1538/// The caller must not race this call with concurrent mutation of the
1539/// same objects from other threads (per-object state is not internally
1540/// synchronized). Violating any of the above is undefined behavior.
1541///
1542/// Exercised by the C-API differential courts
1543/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1544/// courts; those pass byte-for-byte against the upstream oracle.
1545#[no_mangle]
1546pub unsafe extern "C" fn xmlThrDefParserInputBufferCreateFilenameDefault(
1547    func: Option<
1548        unsafe extern "C" fn(
1549            *const c_char,
1550            c_int,
1551        ) -> *mut crate::abi::structs::_xmlParserInputBuffer,
1552    >,
1553) -> Option<
1554    unsafe extern "C" fn(*const c_char, c_int) -> *mut crate::abi::structs::_xmlParserInputBuffer,
1555> {
1556    unsafe {
1557        if func.is_some() {
1558            xmlParserInputBufferCreateFilenameValue = func;
1559        }
1560        xmlParserInputBufferCreateFilenameValue
1561    }
1562}
1563
1564/// Upstream `xmlThrDefOutputBufferCreateFilenameDefault(...)`.
1565///
1566/// # SAFETY
1567///
1568///
1569/// - `func` must be a valid callback (or None);
1570///   the callback is invoked with the documented context pointer and
1571///   must itself uphold the same pointer invariants.
1572///
1573/// The caller must not race this call with concurrent mutation of the
1574/// same objects from other threads (per-object state is not internally
1575/// synchronized). Violating any of the above is undefined behavior.
1576///
1577/// Exercised by the C-API differential courts
1578/// (courts/suites/data-abi/*-family-probe.c) and the CLI differential
1579/// courts; those pass byte-for-byte against the upstream oracle.
1580#[no_mangle]
1581pub unsafe extern "C" fn xmlThrDefOutputBufferCreateFilenameDefault(
1582    func: Option<
1583        unsafe extern "C" fn(
1584            *const c_char,
1585            crate::abi::structs::xmlCharEncodingHandlerPtr,
1586            c_int,
1587        ) -> *mut crate::abi::structs::_xmlOutputBuffer,
1588    >,
1589) -> Option<
1590    unsafe extern "C" fn(
1591        *const c_char,
1592        crate::abi::structs::xmlCharEncodingHandlerPtr,
1593        c_int,
1594    ) -> *mut crate::abi::structs::_xmlOutputBuffer,
1595> {
1596    unsafe {
1597        if func.is_some() {
1598            xmlOutputBufferCreateFilenameValue = func;
1599        }
1600        xmlOutputBufferCreateFilenameValue
1601    }
1602}
1603
1604// ═══════════════════════════════════════════════════════════════════════════════
1605// __xmlXxx() pointer accessors (upstream threads.c / globals.c)
1606// ═══════════════════════════════════════════════════════════════════════════════
1607// The deprecated thread-local API exports one `__xmlXxx(void)` accessor per
1608// global; each returns a pointer to the global so callers can read/write it.
1609
1610/// Upstream `__xmlBufferAllocScheme(void)` — returns a pointer to `xmlBufferAllocScheme`.
1611///
1612/// # SAFETY
1613///
1614/// The function touches crate-global state only; it is safe
1615/// as long as the caller respects the library's global
1616/// initialization/cleanup ordering (xmlInitParser before use,
1617/// xmlCleanupParser only after all users are done).
1618///
1619/// Violating the global lifecycle ordering, or calling this after
1620/// teardown or from a signal handler, is undefined behavior.
1621#[no_mangle]
1622pub unsafe extern "C" fn __xmlBufferAllocScheme() -> *mut c_int {
1623    // SAFETY: returning a pointer to an exported static; the caller may
1624    // read/write it exactly as with upstream's deprecated accessor.
1625    core::ptr::addr_of_mut!(xmlBufferAllocScheme)
1626}
1627
1628/// Upstream `__xmlDefaultBufferSize(void)` — returns a pointer to `xmlDefaultBufferSize`.
1629///
1630/// # SAFETY
1631///
1632/// The function touches crate-global state only; it is safe
1633/// as long as the caller respects the library's global
1634/// initialization/cleanup ordering (xmlInitParser before use,
1635/// xmlCleanupParser only after all users are done).
1636///
1637/// Violating the global lifecycle ordering, or calling this after
1638/// teardown or from a signal handler, is undefined behavior.
1639#[no_mangle]
1640pub unsafe extern "C" fn __xmlDefaultBufferSize() -> *mut c_int {
1641    // SAFETY: returning a pointer to an exported static; the caller may
1642    // read/write it exactly as with upstream's deprecated accessor.
1643    core::ptr::addr_of_mut!(xmlDefaultBufferSize)
1644}
1645
1646/// Upstream `__xmlDeregisterNodeDefaultValue(void)` — returns a pointer to `xmlDeregisterNodeDefaultValue`.
1647///
1648/// # SAFETY
1649///
1650/// The function touches crate-global state only; it is safe
1651/// as long as the caller respects the library's global
1652/// initialization/cleanup ordering (xmlInitParser before use,
1653/// xmlCleanupParser only after all users are done).
1654///
1655/// Violating the global lifecycle ordering, or calling this after
1656/// teardown or from a signal handler, is undefined behavior.
1657#[no_mangle]
1658pub unsafe extern "C" fn __xmlDeregisterNodeDefaultValue(
1659) -> *mut Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1660    // SAFETY: returning a pointer to an exported static; the caller may
1661    // read/write it exactly as with upstream's deprecated accessor.
1662    core::ptr::addr_of_mut!(xmlDeregisterNodeDefaultValue)
1663}
1664
1665/// Upstream `__xmlDoValidityCheckingDefaultValue(void)` — returns a pointer to `xmlDoValidityCheckingDefaultValue`.
1666///
1667/// # SAFETY
1668///
1669/// The function touches crate-global state only; it is safe
1670/// as long as the caller respects the library's global
1671/// initialization/cleanup ordering (xmlInitParser before use,
1672/// xmlCleanupParser only after all users are done).
1673///
1674/// Violating the global lifecycle ordering, or calling this after
1675/// teardown or from a signal handler, is undefined behavior.
1676#[no_mangle]
1677pub unsafe extern "C" fn __xmlDoValidityCheckingDefaultValue() -> *mut c_int {
1678    // SAFETY: returning a pointer to an exported static; the caller may
1679    // read/write it exactly as with upstream's deprecated accessor.
1680    core::ptr::addr_of_mut!(xmlDoValidityCheckingDefaultValue)
1681}
1682
1683/// Upstream `__xmlGenericError(void)` — returns a pointer to `xmlGenericError`.
1684///
1685/// # SAFETY
1686///
1687/// The function touches crate-global state only; it is safe
1688/// as long as the caller respects the library's global
1689/// initialization/cleanup ordering (xmlInitParser before use,
1690/// xmlCleanupParser only after all users are done).
1691///
1692/// Violating the global lifecycle ordering, or calling this after
1693/// teardown or from a signal handler, is undefined behavior.
1694#[no_mangle]
1695pub unsafe extern "C" fn __xmlGenericError() -> *mut Option<xmlGenericErrorFunc> {
1696    // SAFETY: returning a pointer to an exported static; the caller may
1697    // read/write it exactly as with upstream's deprecated accessor.
1698    core::ptr::addr_of_mut!(xmlGenericError)
1699}
1700
1701/// Upstream `__xmlGenericErrorContext(void)` — returns a pointer to `xmlGenericErrorContext`.
1702///
1703/// # SAFETY
1704///
1705/// The function touches crate-global state only; it is safe
1706/// as long as the caller respects the library's global
1707/// initialization/cleanup ordering (xmlInitParser before use,
1708/// xmlCleanupParser only after all users are done).
1709///
1710/// Violating the global lifecycle ordering, or calling this after
1711/// teardown or from a signal handler, is undefined behavior.
1712#[no_mangle]
1713pub unsafe extern "C" fn __xmlGenericErrorContext() -> *mut *mut c_void {
1714    // SAFETY: returning a pointer to an exported static; the caller may
1715    // read/write it exactly as with upstream's deprecated accessor.
1716    core::ptr::addr_of_mut!(xmlGenericErrorContext)
1717}
1718
1719/// Upstream `__xmlGetWarningsDefaultValue(void)` — returns a pointer to `xmlGetWarningsDefaultValue`.
1720///
1721/// # SAFETY
1722///
1723/// The function touches crate-global state only; it is safe
1724/// as long as the caller respects the library's global
1725/// initialization/cleanup ordering (xmlInitParser before use,
1726/// xmlCleanupParser only after all users are done).
1727///
1728/// Violating the global lifecycle ordering, or calling this after
1729/// teardown or from a signal handler, is undefined behavior.
1730#[no_mangle]
1731pub unsafe extern "C" fn __xmlGetWarningsDefaultValue() -> *mut c_int {
1732    // SAFETY: returning a pointer to an exported static; the caller may
1733    // read/write it exactly as with upstream's deprecated accessor.
1734    core::ptr::addr_of_mut!(xmlGetWarningsDefaultValue)
1735}
1736
1737/// Upstream `__xmlKeepBlanksDefaultValue(void)` — returns a pointer to `xmlKeepBlanksDefaultValue`.
1738///
1739/// # SAFETY
1740///
1741/// The function touches crate-global state only; it is safe
1742/// as long as the caller respects the library's global
1743/// initialization/cleanup ordering (xmlInitParser before use,
1744/// xmlCleanupParser only after all users are done).
1745///
1746/// Violating the global lifecycle ordering, or calling this after
1747/// teardown or from a signal handler, is undefined behavior.
1748#[no_mangle]
1749pub unsafe extern "C" fn __xmlKeepBlanksDefaultValue() -> *mut c_int {
1750    // SAFETY: returning a pointer to an exported static; the caller may
1751    // read/write it exactly as with upstream's deprecated accessor.
1752    core::ptr::addr_of_mut!(xmlKeepBlanksDefaultValue)
1753}
1754
1755/// Upstream `__xmlLineNumbersDefaultValue(void)` — returns a pointer to `xmlLineNumbersDefaultValue`.
1756///
1757/// # SAFETY
1758///
1759/// The function touches crate-global state only; it is safe
1760/// as long as the caller respects the library's global
1761/// initialization/cleanup ordering (xmlInitParser before use,
1762/// xmlCleanupParser only after all users are done).
1763///
1764/// Violating the global lifecycle ordering, or calling this after
1765/// teardown or from a signal handler, is undefined behavior.
1766#[no_mangle]
1767pub unsafe extern "C" fn __xmlLineNumbersDefaultValue() -> *mut c_int {
1768    // SAFETY: returning a pointer to an exported static; the caller may
1769    // read/write it exactly as with upstream's deprecated accessor.
1770    core::ptr::addr_of_mut!(xmlLineNumbersDefaultValue)
1771}
1772
1773/// Upstream `__xmlLoadExtDtdDefaultValue(void)` — returns a pointer to `xmlLoadExtDtdDefaultValue`.
1774///
1775/// # SAFETY
1776///
1777/// The function touches crate-global state only; it is safe
1778/// as long as the caller respects the library's global
1779/// initialization/cleanup ordering (xmlInitParser before use,
1780/// xmlCleanupParser only after all users are done).
1781///
1782/// Violating the global lifecycle ordering, or calling this after
1783/// teardown or from a signal handler, is undefined behavior.
1784#[no_mangle]
1785pub unsafe extern "C" fn __xmlLoadExtDtdDefaultValue() -> *mut c_int {
1786    // SAFETY: returning a pointer to an exported static; the caller may
1787    // read/write it exactly as with upstream's deprecated accessor.
1788    core::ptr::addr_of_mut!(xmlLoadExtDtdDefaultValue)
1789}
1790
1791/// Upstream `__xmlOutputBufferCreateFilenameValue(void)` — returns a pointer to `xmlOutputBufferCreateFilenameValue`.
1792///
1793/// # SAFETY
1794///
1795/// The function touches crate-global state only; it is safe
1796/// as long as the caller respects the library's global
1797/// initialization/cleanup ordering (xmlInitParser before use,
1798/// xmlCleanupParser only after all users are done).
1799///
1800/// Violating the global lifecycle ordering, or calling this after
1801/// teardown or from a signal handler, is undefined behavior.
1802#[no_mangle]
1803pub unsafe extern "C" fn __xmlOutputBufferCreateFilenameValue() -> *mut Option<
1804    unsafe extern "C" fn(
1805        *const c_char,
1806        crate::abi::structs::xmlCharEncodingHandlerPtr,
1807        c_int,
1808    ) -> *mut crate::abi::structs::_xmlOutputBuffer,
1809> {
1810    // SAFETY: returning a pointer to an exported static; the caller may
1811    // read/write it exactly as with upstream's deprecated accessor.
1812    core::ptr::addr_of_mut!(xmlOutputBufferCreateFilenameValue)
1813}
1814
1815/// Upstream `__xmlParserDebugEntities(void)` — returns a pointer to `xmlParserDebugEntities`.
1816///
1817/// # SAFETY
1818///
1819/// The function touches crate-global state only; it is safe
1820/// as long as the caller respects the library's global
1821/// initialization/cleanup ordering (xmlInitParser before use,
1822/// xmlCleanupParser only after all users are done).
1823///
1824/// Violating the global lifecycle ordering, or calling this after
1825/// teardown or from a signal handler, is undefined behavior.
1826#[no_mangle]
1827pub unsafe extern "C" fn __xmlParserDebugEntities() -> *mut c_int {
1828    // SAFETY: returning a pointer to an exported static; the caller may
1829    // read/write it exactly as with upstream's deprecated accessor.
1830    core::ptr::addr_of_mut!(xmlParserDebugEntities)
1831}
1832
1833/// Upstream `__xmlParserInputBufferCreateFilenameValue(void)` — returns a pointer to `xmlParserInputBufferCreateFilenameValue`.
1834///
1835/// # SAFETY
1836///
1837/// The function touches crate-global state only; it is safe
1838/// as long as the caller respects the library's global
1839/// initialization/cleanup ordering (xmlInitParser before use,
1840/// xmlCleanupParser only after all users are done).
1841///
1842/// Violating the global lifecycle ordering, or calling this after
1843/// teardown or from a signal handler, is undefined behavior.
1844#[no_mangle]
1845pub unsafe extern "C" fn __xmlParserInputBufferCreateFilenameValue() -> *mut Option<
1846    unsafe extern "C" fn(*const c_char, c_int) -> *mut crate::abi::structs::_xmlParserInputBuffer,
1847> {
1848    // SAFETY: returning a pointer to an exported static; the caller may
1849    // read/write it exactly as with upstream's deprecated accessor.
1850    core::ptr::addr_of_mut!(xmlParserInputBufferCreateFilenameValue)
1851}
1852
1853/// Upstream `__xmlParserVersion(void)` — returns a pointer to `xmlParserVersion`.
1854///
1855/// # SAFETY
1856///
1857/// The function touches crate-global state only; it is safe
1858/// as long as the caller respects the library's global
1859/// initialization/cleanup ordering (xmlInitParser before use,
1860/// xmlCleanupParser only after all users are done).
1861///
1862/// Violating the global lifecycle ordering, or calling this after
1863/// teardown or from a signal handler, is undefined behavior.
1864#[no_mangle]
1865pub unsafe extern "C" fn __xmlParserVersion() -> *mut *const c_char {
1866    // SAFETY: returning a pointer to an exported static; the caller may
1867    // read/write it exactly as with upstream's deprecated accessor.
1868    core::ptr::addr_of_mut!(xmlParserVersion)
1869}
1870
1871/// Upstream `__xmlPedanticParserDefaultValue(void)` — returns a pointer to `xmlPedanticParserDefaultValue`.
1872///
1873/// # SAFETY
1874///
1875/// The function touches crate-global state only; it is safe
1876/// as long as the caller respects the library's global
1877/// initialization/cleanup ordering (xmlInitParser before use,
1878/// xmlCleanupParser only after all users are done).
1879///
1880/// Violating the global lifecycle ordering, or calling this after
1881/// teardown or from a signal handler, is undefined behavior.
1882#[no_mangle]
1883pub unsafe extern "C" fn __xmlPedanticParserDefaultValue() -> *mut c_int {
1884    // SAFETY: returning a pointer to an exported static; the caller may
1885    // read/write it exactly as with upstream's deprecated accessor.
1886    core::ptr::addr_of_mut!(xmlPedanticParserDefaultValue)
1887}
1888
1889/// Upstream `__xmlRegisterNodeDefaultValue(void)` — returns a pointer to `xmlRegisterNodeDefaultValue`.
1890///
1891/// # SAFETY
1892///
1893/// The function touches crate-global state only; it is safe
1894/// as long as the caller respects the library's global
1895/// initialization/cleanup ordering (xmlInitParser before use,
1896/// xmlCleanupParser only after all users are done).
1897///
1898/// Violating the global lifecycle ordering, or calling this after
1899/// teardown or from a signal handler, is undefined behavior.
1900#[no_mangle]
1901pub unsafe extern "C" fn __xmlRegisterNodeDefaultValue(
1902) -> *mut Option<unsafe extern "C" fn(*mut crate::abi::structs::_xmlNode)> {
1903    // SAFETY: returning a pointer to an exported static; the caller may
1904    // read/write it exactly as with upstream's deprecated accessor.
1905    core::ptr::addr_of_mut!(xmlRegisterNodeDefaultValue)
1906}
1907
1908/// Upstream `__xmlStructuredError(void)` — returns a pointer to `xmlStructuredError`.
1909///
1910/// # SAFETY
1911///
1912/// The function touches crate-global state only; it is safe
1913/// as long as the caller respects the library's global
1914/// initialization/cleanup ordering (xmlInitParser before use,
1915/// xmlCleanupParser only after all users are done).
1916///
1917/// Violating the global lifecycle ordering, or calling this after
1918/// teardown or from a signal handler, is undefined behavior.
1919#[no_mangle]
1920pub unsafe extern "C" fn __xmlStructuredError() -> *mut Option<xmlStructuredErrorFunc> {
1921    // SAFETY: returning a pointer to an exported static; the caller may
1922    // read/write it exactly as with upstream's deprecated accessor.
1923    core::ptr::addr_of_mut!(xmlStructuredError)
1924}
1925
1926/// Upstream `__xmlStructuredErrorContext(void)` — returns a pointer to `xmlStructuredErrorContext`.
1927///
1928/// # SAFETY
1929///
1930/// The function touches crate-global state only; it is safe
1931/// as long as the caller respects the library's global
1932/// initialization/cleanup ordering (xmlInitParser before use,
1933/// xmlCleanupParser only after all users are done).
1934///
1935/// Violating the global lifecycle ordering, or calling this after
1936/// teardown or from a signal handler, is undefined behavior.
1937#[no_mangle]
1938pub unsafe extern "C" fn __xmlStructuredErrorContext() -> *mut *mut c_void {
1939    // SAFETY: returning a pointer to an exported static; the caller may
1940    // read/write it exactly as with upstream's deprecated accessor.
1941    core::ptr::addr_of_mut!(xmlStructuredErrorContext)
1942}
1943
1944/// Upstream `__xmlSubstituteEntitiesDefaultValue(void)` — returns a pointer to `xmlSubstituteEntitiesDefaultValue`.
1945///
1946/// # SAFETY
1947///
1948/// The function touches crate-global state only; it is safe
1949/// as long as the caller respects the library's global
1950/// initialization/cleanup ordering (xmlInitParser before use,
1951/// xmlCleanupParser only after all users are done).
1952///
1953/// Violating the global lifecycle ordering, or calling this after
1954/// teardown or from a signal handler, is undefined behavior.
1955#[no_mangle]
1956pub unsafe extern "C" fn __xmlSubstituteEntitiesDefaultValue() -> *mut c_int {
1957    // SAFETY: returning a pointer to an exported static; the caller may
1958    // read/write it exactly as with upstream's deprecated accessor.
1959    core::ptr::addr_of_mut!(xmlSubstituteEntitiesDefaultValue)
1960}
1961
1962// ═══════════════════════════════════════════════════════════════════════════════
1963// Regression court — xmlLastError mirror concurrency (11.1-X)
1964// ═══════════════════════════════════════════════════════════════════════════════
1965//
1966// R-000135 discovery during 11.1-X: the exported `xmlLastError` mirror was
1967// deep-copied and freed without synchronization, so concurrent error raises
1968// on different threads double-freed the mirror strings. The parallel lib
1969// test suite observed this as `double free or corruption (!prev)` aborts
1970// (xml::errors tests racing with any other raising thread). The writers are
1971// serialized via LAST_ERROR_MIRROR_LOCK; these courts hammer the exact
1972// interleavings and must complete without crashing.
1973
1974#[cfg(test)]
1975mod tests {
1976    use super::*;
1977    use crate::abi::allocator::xmlMallocImpl;
1978    use crate::abi::structs::_xmlError;
1979    use crate::xml::globals;
1980    use core::ptr;
1981
1982    /// Allocate a NUL-terminated C string owned by xmlMallocImpl (the same
1983    /// allocator the thread-local error slot uses).
1984    unsafe fn alloc_cstr(s: &str) -> *mut c_char {
1985        let bytes = s.as_bytes();
1986        let p = unsafe { xmlMallocImpl(bytes.len() + 1) as *mut c_char };
1987        assert!(!p.is_null(), "alloc_cstr: xmlMallocImpl failed");
1988        unsafe {
1989            ptr::copy_nonoverlapping(bytes.as_ptr(), p as *mut u8, bytes.len());
1990            *((p as *mut u8).add(bytes.len())) = 0;
1991        }
1992        p
1993    }
1994
1995    /// Build an owned `_xmlError` with distinct string fields.
1996    unsafe fn build_error(tag: &str) -> _xmlError {
1997        _xmlError {
1998            domain: 1,
1999            code: 2,
2000            message: unsafe { alloc_cstr(&format!("msg {tag}")) },
2001            level: 3,
2002            file: unsafe { alloc_cstr(&format!("file {tag}")) },
2003            line: 4,
2004            str1: unsafe { alloc_cstr(&format!("str1 {tag}")) },
2005            str2: ptr::null_mut(),
2006            str3: ptr::null_mut(),
2007            int1: 0,
2008            int2: 0,
2009            ctxt: ptr::null_mut(),
2010            node: ptr::null_mut(),
2011        }
2012    }
2013
2014    /// Concurrent sync/reset hammer: one thread raises errors while another
2015    /// resets. Before the mirror lock this double-freed the shared strings;
2016    /// the test crashes (SIGABRT) under the old code and passes now.
2017    #[test]
2018    fn test_last_error_mirror_concurrent_sync_reset() {
2019        let sync = std::thread::spawn(|| {
2020            for i in 0..400 {
2021                unsafe { globals::set_last_error(build_error(&format!("sync {i}"))) };
2022            }
2023        });
2024        let reset = std::thread::spawn(|| {
2025            for _ in 0..400 {
2026                globals::reset_last_error();
2027            }
2028        });
2029        sync.join().unwrap();
2030        reset.join().unwrap();
2031        // Leave the mirror in a clean state for later tests. (No thread-local
2032        // assertion: the harness reuses OS threads across tests, so a prior
2033        // test's error may legitimately live in this thread's slot.)
2034        globals::reset_last_error();
2035    }
2036
2037    /// Many threads raising concurrently (the full parallel-suite shape that
2038    /// originally aborted in `test_encode_entities_reentrant_*` victims).
2039    #[test]
2040    fn test_last_error_mirror_many_threads() {
2041        let mut handles = Vec::new();
2042        for t in 0..8 {
2043            handles.push(std::thread::spawn(move || {
2044                for i in 0..150 {
2045                    unsafe { globals::set_last_error(build_error(&format!("t{t} i{i}"))) };
2046                    if i % 7 == 0 {
2047                        globals::reset_last_error();
2048                    }
2049                }
2050            }));
2051        }
2052        for h in handles {
2053            h.join().unwrap();
2054        }
2055        globals::reset_last_error();
2056    }
2057}