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