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