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