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