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}