Skip to main content

libxml_rs/abi/
exports_xml2.rs

1//! C ABI exports for libxml2.so.2 — no_mangle extern "C" functions (§1, §16).
2//!
3//! This module contains all `#[no_mangle] pub extern "C"` function definitions
4//! that form the public ABI of libxml2.so.2. Every function here corresponds to
5//! a function in the upstream libxml2 headers.
6//!
7//! # Phase 1 status
8//!
9//! Complete — all major ABI entry points are implemented. Functions that require
10//! modules not yet implemented (tree, parser, etc.) call into those modules,
11//! which will be filled in as Phase 1 continues.
12//!
13//! # Organization
14//!
15//! Exports are grouped by subsystem in the order they appear in upstream headers:
16//!
17//! 1. Initialization / Cleanup
18//! 2. Version
19//! 3. Memory / Allocator
20//! 4. Error handling
21//! 5. String utilities
22//! 6. Tree (document, node, attribute, namespace, DTD, entity)
23//! 7. Parser (SAX, DOM, push, reader)
24//! 8. I/O
25//! 9. Dictionary
26//! 10. Hash table
27//! 11. List
28//! 12. Buffer
29//! 13. Encoding
30//! 14. XPath
31//! 15. XInclude
32//! 16. Catalog
33//! 17. HTML
34//! 18. Debug/misc
35
36#![allow(non_snake_case)]
37#![allow(unused_variables)]
38#![allow(clippy::missing_safety_doc)]
39#![allow(clippy::not_unsafe_ptr_arg_deref)]
40
41use core::ffi::c_void;
42use core::ptr;
43use once_cell::sync::Lazy;
44use parking_lot::Mutex;
45use std::collections::HashMap;
46use std::ffi::{CStr, CString};
47use std::mem::size_of;
48use std::os::raw::{c_char, c_int, c_long, c_uint, c_ulong};
49
50use crate::xml::xinclude;
51use crate::xml::xpath::ast::CompiledExpr;
52use crate::xml::xpath::context::{BoxedXPathFunction, XPathContext};
53use crate::xml::xpath::types::{NodeSet, XPathValue};
54use crate::xml::xpointer;
55
56use crate::abi::allocator::*;
57use crate::abi::callbacks::*;
58use crate::abi::structs::*;
59use crate::abi::types::*;
60
61// ═══════════════════════════════════════════════════════════════════════════════
62// 1. Initialization / Cleanup
63// ═══════════════════════════════════════════════════════════════════════════════
64
65/// Initialize the parser library.
66///
67/// Must be called before any other libxml2 functions.
68/// Safe to call multiple times (reference-counted in modern libxml2).
69///
70/// # UPSTREAM-PARITY
71///
72/// ```c
73/// void xmlInitParser(void);
74/// ```
75#[no_mangle]
76pub unsafe extern "C" fn xmlInitParser() {
77    crate::internal::globals::init_parser();
78}
79
80/// Initialize the global variables module (upstream globals.h).
81///
82/// # UPSTREAM-PARITY
83///
84/// ```c
85/// void xmlInitGlobals(void);
86/// ```
87///
88/// Upstream `xmlInitGlobals` (globals.c) is called once to initialize the
89/// global variable defaults. The candidate's globals are initialized
90/// statically/on first use, so this is a no-op that exists for ABI
91/// compatibility.
92#[no_mangle]
93pub unsafe extern "C" fn xmlInitGlobals() {
94    // Globals are statically initialized in the candidate.
95}
96
97/// Upstream `xmlInitializeGlobalState` (globals.c) — initializes a
98/// `xmlGlobalState` struct; the candidate keeps no global-state struct, so
99/// this is a no-op for ABI compatibility.
100///
101/// # UPSTREAM-PARITY
102///
103/// ```c
104/// void xmlInitializeGlobalState(xmlGlobalStatePtr gs);
105/// ```
106#[no_mangle]
107pub unsafe extern "C" fn xmlInitializeGlobalState(_gs: *mut c_void) {
108    // No-op: the candidate's globals are statically initialized.
109}
110
111/// Upstream `xmlInitializeDict` (dict.c) — ensures the dictionary
112/// subsystem is initialized; no-op in the candidate (lazy init).
113///
114/// # UPSTREAM-PARITY
115///
116/// ```c
117/// int xmlInitializeDict(void);
118/// ```
119#[no_mangle]
120pub extern "C" fn xmlInitializeDict() -> c_int {
121    0
122}
123
124/// Upstream `xmlInitializePredefinedEntities` (entities.c) — the
125/// predefined entities (& < > " ') are built lazily by
126/// the candidate; no-op.
127///
128/// # UPSTREAM-PARITY
129///
130/// ```c
131/// void xmlInitializePredefinedEntities(void);
132/// ```
133#[no_mangle]
134pub extern "C" fn xmlInitializePredefinedEntities() {
135    // No-op: predefined entities are resolved on demand.
136}
137
138/// Upstream `xmlCleanupPredefinedEntities` (entities.c) — no-op in the
139/// candidate (no global entity table to release).
140///
141/// # UPSTREAM-PARITY
142///
143/// ```c
144/// void xmlCleanupPredefinedEntities(void);
145/// ```
146#[no_mangle]
147pub extern "C" fn xmlCleanupPredefinedEntities() {
148    // No-op.
149}
150
151/// Upstream `xmlDefaultSAXHandlerInit` (SAX2.c) — fills the
152/// `xmlDefaultSAXHandler` global. The candidate's default handler is built
153/// on demand; this initializes the exported default-handler global when it
154/// is added (currently tracked in R-000135). No-op for now.
155///
156/// # UPSTREAM-PARITY
157///
158/// ```c
159/// void xmlDefaultSAXHandlerInit(void);
160/// ```
161#[no_mangle]
162pub extern "C" fn xmlDefaultSAXHandlerInit() {
163    // The candidate builds default handlers on demand; the exported
164    // xmlDefaultSAXHandler global is part of the R-000135 data closure.
165}
166
167/// Set the default SAX version (upstream SAX2.c `xmlSAXDefaultVersion`):
168/// returns the previous default; -1 when the version is not 1 or 2.
169///
170/// # UPSTREAM-PARITY
171///
172/// ```c
173/// int xmlSAXDefaultVersion(int version);
174/// ```
175static SAX2_DEFAULT_VERSION: core::sync::atomic::AtomicI32 = core::sync::atomic::AtomicI32::new(2);
176#[no_mangle]
177pub extern "C" fn xmlSAXDefaultVersion(version: c_int) -> c_int {
178    use core::sync::atomic::Ordering;
179    let ret = SAX2_DEFAULT_VERSION.load(Ordering::Relaxed);
180    if version != 1 && version != 2 {
181        return -1;
182    }
183    SAX2_DEFAULT_VERSION.store(version, Ordering::Relaxed);
184    ret
185}
186
187/// Initialize a SAX handler for a given SAX version (upstream SAX2.c
188/// `xmlSAXVersion`): fills the handler with the default callbacks and sets
189/// `initialized` (XML_SAX2_MAGIC for version 2, 1 for version 1).
190///
191/// # UPSTREAM-PARITY
192///
193/// ```c
194/// int xmlSAXVersion(xmlSAXHandler *hdlr, int version);
195/// ```
196#[no_mangle]
197pub unsafe extern "C" fn xmlSAXVersion(
198    hdlr: *mut crate::abi::structs::_xmlSAXHandler,
199    version: c_int,
200) -> c_int {
201    if hdlr.is_null() {
202        return -1;
203    }
204    if version != 1 && version != 2 {
205        return -1;
206    }
207    // SAFETY: hdlr is non-NULL and writable.
208    unsafe {
209        crate::xml::sax::dispatch::xmlSAX2InitDefaultSAXHandler(hdlr);
210        let h = &mut *hdlr;
211        if version == 2 {
212            h.initialized = crate::abi::constants::XML_SAX2_MAGIC as c_uint;
213        } else {
214            h.initialized = 1;
215        }
216    }
217    0
218}
219
220/// Upstream `xmlHasFeature` (parser.c): returns 1 when the library was
221/// compiled with the requested feature. The candidate enables the full
222/// feature set (see include/libxml/xmlversion.h), so every known feature
223/// reports 1; unknown features report 0.
224///
225/// # UPSTREAM-PARITY
226///
227/// ```c
228/// int xmlHasFeature(xmlFeature feature);
229/// ```
230#[no_mangle]
231pub extern "C" fn xmlHasFeature(feature: c_int) -> c_int {
232    // xmlFeature enum values (upstream xmlversion.h): XML_WITH_* run 1..24
233    // (XML_WITH_THREAD=1 ... XML_WITH_MODULES=24).
234    if (1..=24).contains(&feature) {
235        1
236    } else {
237        0
238    }
239}
240
241/// Clean up the global variables module (upstream globals.h).
242///
243/// # UPSTREAM-PARITY
244///
245/// ```c
246/// void xmlCleanupGlobals(void);
247/// ```
248///
249/// Upstream `xmlCleanupGlobals` frees the global defaults. The candidate
250/// keeps globals alive for the process lifetime (repeated init/cleanup is
251/// reference-counted); no-op for ABI compatibility.
252#[no_mangle]
253pub unsafe extern "C" fn xmlCleanupGlobals() {
254    // The candidate's globals are process-lifetime statics.
255}
256
257/// Clean up the parser library.
258///
259/// Should be called when the library is no longer needed.
260///
261/// # UPSTREAM-PARITY
262///
263/// ```c
264/// void xmlCleanupParser(void);
265/// ```
266#[no_mangle]
267pub unsafe extern "C" fn xmlCleanupParser() {
268    crate::internal::globals::cleanup_parser();
269}
270
271/// Create a simple mutex (upstream threads.h).
272///
273/// # UPSTREAM-PARITY
274///
275/// ```c
276/// xmlMutexPtr xmlNewMutex(void);
277/// ```
278#[no_mangle]
279pub extern "C" fn xmlNewMutex() -> *mut c_void {
280    crate::xml::threads::new_mutex()
281}
282
283/// Free a simple mutex (upstream threads.h).
284///
285/// # UPSTREAM-PARITY
286///
287/// ```c
288/// void xmlFreeMutex(xmlMutexPtr tok);
289/// ```
290#[no_mangle]
291pub unsafe extern "C" fn xmlFreeMutex(tok: *mut c_void) {
292    crate::xml::threads::free_mutex(tok);
293}
294
295/// Lock a simple mutex (upstream threads.h).
296///
297/// # UPSTREAM-PARITY
298///
299/// ```c
300/// void xmlMutexLock(xmlMutexPtr tok);
301/// ```
302#[no_mangle]
303pub unsafe extern "C" fn xmlMutexLock(tok: *mut c_void) {
304    crate::xml::threads::mutex_lock(tok);
305}
306
307/// Unlock a simple mutex (upstream threads.h).
308///
309/// # UPSTREAM-PARITY
310///
311/// ```c
312/// void xmlMutexUnlock(xmlMutexPtr tok);
313/// ```
314#[no_mangle]
315pub unsafe extern "C" fn xmlMutexUnlock(tok: *mut c_void) {
316    crate::xml::threads::mutex_unlock(tok);
317}
318
319/// Create a recursive mutex (upstream threads.h).
320///
321/// # UPSTREAM-PARITY
322///
323/// ```c
324/// xmlRMutexPtr xmlNewRMutex(void);
325/// ```
326#[no_mangle]
327pub extern "C" fn xmlNewRMutex() -> *mut c_void {
328    crate::xml::threads::new_rmutex()
329}
330
331/// Free a recursive mutex (upstream threads.h).
332///
333/// # UPSTREAM-PARITY
334///
335/// ```c
336/// void xmlFreeRMutex(xmlRMutexPtr tok);
337/// ```
338#[no_mangle]
339pub unsafe extern "C" fn xmlFreeRMutex(tok: *mut c_void) {
340    crate::xml::threads::free_rmutex(tok);
341}
342
343/// Lock a recursive mutex (upstream threads.h).
344///
345/// # UPSTREAM-PARITY
346///
347/// ```c
348/// void xmlRMutexLock(xmlRMutexPtr tok);
349/// ```
350#[no_mangle]
351pub unsafe extern "C" fn xmlRMutexLock(tok: *mut c_void) {
352    crate::xml::threads::rmutex_lock(tok);
353}
354
355/// Unlock a recursive mutex (upstream threads.h).
356///
357/// # UPSTREAM-PARITY
358///
359/// ```c
360/// void xmlRMutexUnlock(xmlRMutexPtr tok);
361/// ```
362#[no_mangle]
363pub unsafe extern "C" fn xmlRMutexUnlock(tok: *mut c_void) {
364    crate::xml::threads::rmutex_unlock(tok);
365}
366
367/// Check the thread-local storage (upstream threads.h `xmlCheckThreadLocalStorage`):
368/// returns 0 when TLS is functional, -1 otherwise. The candidate uses Rust
369/// thread-locals which are always functional.
370///
371/// # UPSTREAM-PARITY
372///
373/// ```c
374/// int xmlCheckThreadLocalStorage(void);
375/// ```
376#[no_mangle]
377pub extern "C" fn xmlCheckThreadLocalStorage() -> c_int {
378    0
379}
380
381/// Initialize threading support.
382///
383/// # UPSTREAM-PARITY
384///
385/// ```c
386/// int xmlInitThreads(void);
387/// ```
388///
389/// Returns 0 on success.
390#[no_mangle]
391pub unsafe extern "C" fn xmlInitThreads() -> c_int {
392    crate::internal::globals::init_threads()
393}
394
395/// Clean up threading support.
396///
397/// # UPSTREAM-PARITY
398///
399/// ```c
400/// void xmlCleanupThreads(void);
401/// ```
402#[no_mangle]
403pub unsafe extern "C" fn xmlCleanupThreads() {
404    crate::xml::threads::cleanup_threads();
405}
406
407/// Check whether the library has been initialized.
408///
409/// # UPSTREAM-PARITY
410///
411/// ```c
412/// int xmlIsInitialized(void);
413/// ```
414#[no_mangle]
415pub extern "C" fn xmlIsInitialized() -> c_int {
416    if crate::abi::versioning::is_initialized() {
417        1
418    } else {
419        0
420    }
421}
422
423/// Initialize a set of threads (libxml2 compat).
424///
425/// # UPSTREAM-PARITY
426///
427/// ```c
428/// int xmlInitThreads(void);
429/// ```
430/// This is an alias.
431#[no_mangle]
432pub unsafe extern "C" fn xmlLockLibrary() {
433    crate::xml::threads::lock_library();
434}
435
436/// Unlock the library (libxml2 compat).
437///
438/// # UPSTREAM-PARITY
439///
440/// ```c
441/// void xmlUnlockLibrary(void);
442/// ```
443#[no_mangle]
444pub unsafe extern "C" fn xmlUnlockLibrary() {
445    crate::xml::threads::unlock_library();
446}
447
448// ═══════════════════════════════════════════════════════════════════════════════
449// 4. Error Handling
450// ═══════════════════════════════════════════════════════════════════════════════
451
452/// Set the generic error handler.
453///
454/// # UPSTREAM-PARITY
455///
456/// ```c
457/// void xmlSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler);
458/// ```
459///
460/// # SAFETY
461///
462/// - `handler` must be a valid function pointer or NULL (to reset to default).
463/// - If non-NULL, the handler may be called at any time with `ctx`.
464#[no_mangle]
465pub unsafe extern "C" fn xmlSetGenericErrorFunc(
466    ctx: *mut c_void,
467    handler: Option<xmlGenericErrorFunc>,
468) {
469    // SAFETY: Delegates to xml::errors with same safety contract.
470    unsafe { crate::xml::errors::set_generic_error_func(ctx, handler) };
471}
472
473/// Set the structured error handler.
474///
475/// # UPSTREAM-PARITY
476///
477/// ```c
478/// void xmlSetStructuredErrorFunc(void *ctx, xmlStructuredErrorFunc handler);
479/// ```
480///
481/// # SAFETY
482///
483/// - `handler` must be a valid function pointer or NULL.
484#[no_mangle]
485pub unsafe extern "C" fn xmlSetStructuredErrorFunc(
486    ctx: *mut c_void,
487    handler: Option<xmlStructuredErrorFunc>,
488) {
489    // SAFETY: Delegates to xml::errors with same safety contract.
490    unsafe { crate::xml::errors::set_structured_error_func(ctx, handler) };
491}
492
493/// Get the last error for the current thread.
494///
495/// # UPSTREAM-PARITY
496///
497/// ```c
498/// xmlErrorPtr xmlGetLastError(void);
499/// ```
500///
501/// Returns a pointer to the last error, or NULL if no error occurred.
502/// The returned pointer is valid until the next libxml2 call in this thread.
503#[no_mangle]
504pub extern "C" fn xmlGetLastError() -> *mut _xmlError {
505    crate::xml::errors::get_last_error()
506}
507
508/// Get a copy of the last error for the current thread.
509///
510/// # UPSTREAM-PARITY
511///
512/// ```c
513/// xmlErrorPtr xmlCopyError(xmlErrorPtr from, xmlErrorPtr to);
514/// ```
515///
516/// Copies `from` into `to`. Returns 0 on success, -1 on error.
517///
518/// # SAFETY
519///
520/// - `from` and `to` must be valid pointers to `_xmlError` structs, or NULL.
521#[no_mangle]
522pub unsafe extern "C" fn xmlCopyError(from: *const _xmlError, to: *mut _xmlError) -> c_int {
523    // SAFETY: Delegates to xml::errors with same safety contract.
524    unsafe { crate::xml::errors::copy_error(from, to) }
525}
526
527/// Reset an error structure.
528///
529/// # UPSTREAM-PARITY
530///
531/// ```c
532/// void xmlResetError(xmlErrorPtr err);
533/// ```
534///
535/// # SAFETY
536///
537/// - `err` must be a valid pointer to `_xmlError`, or NULL.
538#[no_mangle]
539pub unsafe extern "C" fn xmlResetError(err: *mut _xmlError) {
540    // SAFETY: Delegates to xml::errors with same safety contract.
541    unsafe { crate::xml::errors::reset_error(err) };
542}
543
544/// Raise a structured error.
545///
546/// This is called internally when an error occurs. It updates the last error
547/// and invokes the structured error handler if one is set.
548///
549/// # SAFETY
550///
551/// - `ctxt` may be NULL (context of the error).
552/// - `domain`, `code`, `level`: valid error codes.
553/// - `msg` must be a valid C string or NULL.
554/// - `file` must be a valid C string or NULL.
555/// - `str1`, `str2`, `str3`: error-related strings (may be NULL).
556#[no_mangle]
557pub unsafe extern "C" fn xmlRaiseError(
558    ctxt: *mut c_void,
559    ctxt2: *mut c_void,
560    ctxt3: *mut c_void,
561    ctxt4: *mut c_void,
562    ctxt5: *mut c_void,
563    domain: c_int,
564    code: c_int,
565    level: c_int,
566    file: *const c_char,
567    line: c_int,
568    str1: *const c_char,
569    str2: *const c_char,
570    str3: *const c_char,
571    int1: c_int,
572    int2: c_int,
573    msg: *const c_char,
574) {
575    // SAFETY: Delegates to xml::errors with same safety contract.
576    unsafe {
577        crate::xml::errors::raise_error(
578            ctxt, ctxt2, ctxt3, ctxt4, ctxt5, domain, code, level, file, line, str1, str2, str3,
579            int1, int2, msg,
580        );
581    }
582}
583
584/// Remove any error from the last error stack.
585///
586/// # UPSTREAM-PARITY
587///
588/// ```c
589/// void xmlResetLastError(void);
590/// ```
591#[no_mangle]
592pub extern "C" fn xmlResetLastError() {
593    crate::xml::errors::reset_last_error();
594}
595
596// ═══════════════════════════════════════════════════════════════════════════════
597// 5. String Utilities
598// ═══════════════════════════════════════════════════════════════════════════════
599
600/// Duplicate a string using xmlChar.
601///
602/// # UPSTREAM-PARITY
603///
604/// ```c
605/// xmlChar *xmlStrdup(const xmlChar *cur);
606/// ```
607///
608/// # SAFETY
609///
610/// - `cur` must be a valid null-terminated xmlChar string or NULL.
611#[no_mangle]
612pub unsafe extern "C" fn xmlStrdup(cur: *const xmlChar) -> *mut xmlChar {
613    if cur.is_null() {
614        return ptr::null_mut();
615    }
616    let len = unsafe { xmlStrlen(cur) };
617    let size = len + 1;
618    let new_ptr = unsafe { xmlMallocImpl(size as usize) };
619    if new_ptr.is_null() {
620        return ptr::null_mut();
621    }
622    unsafe {
623        ptr::copy_nonoverlapping(cur as *const u8, new_ptr as *mut u8, size as usize);
624    }
625    new_ptr as *mut xmlChar
626}
627
628/// Duplicate a substring.
629///
630/// # UPSTREAM-PARITY
631///
632/// ```c
633/// xmlChar *xmlStrndup(const xmlChar *cur, int len);
634/// ```
635///
636/// # SAFETY
637///
638/// - `cur` must be a valid pointer or NULL.
639#[no_mangle]
640pub unsafe extern "C" fn xmlStrndup(cur: *const xmlChar, len: c_int) -> *mut xmlChar {
641    if cur.is_null() || len <= 0 {
642        return ptr::null_mut();
643    }
644    let size = len as usize + 1;
645    let new_ptr = unsafe { xmlMallocImpl(size) };
646    if new_ptr.is_null() {
647        return ptr::null_mut();
648    }
649    unsafe {
650        ptr::copy_nonoverlapping(cur as *const u8, new_ptr as *mut u8, len as usize);
651        *(new_ptr.add(len as usize) as *mut u8) = 0;
652    }
653    new_ptr as *mut xmlChar
654}
655
656/// Get the length of an xmlChar string.
657///
658/// # UPSTREAM-PARITY
659///
660/// ```c
661/// int xmlStrlen(const xmlChar *str);
662/// ```
663///
664/// # SAFETY
665///
666/// - `str` must be a valid null-terminated string or NULL (returns 0).
667#[no_mangle]
668pub unsafe extern "C" fn xmlStrlen(str: *const xmlChar) -> c_int {
669    if str.is_null() {
670        return 0;
671    }
672    unsafe { libc::strlen(str as *const c_char) as c_int }
673}
674
675/// Find the first occurrence of a character in a string (upstream tree.c
676/// `xmlStrchr`): returns a pointer to the first occurrence or NULL.
677///
678/// # UPSTREAM-PARITY
679///
680/// ```c
681/// const xmlChar *xmlStrchr(const xmlChar *str, xmlChar val);
682/// ```
683///
684/// # SAFETY
685///
686/// - `str` must be a valid null-terminated string or NULL.
687#[no_mangle]
688pub unsafe extern "C" fn xmlStrchr(str: *const xmlChar, val: xmlChar) -> *const xmlChar {
689    if str.is_null() {
690        return ptr::null();
691    }
692    unsafe {
693        let mut cur = str;
694        while *cur != 0 {
695            if *cur == val {
696                return cur;
697            }
698            cur = cur.add(1);
699        }
700        ptr::null()
701    }
702}
703
704/// Compare two xmlChar strings.
705///
706/// # UPSTREAM-PARITY
707///
708/// ```c
709/// int xmlStrcmp(const xmlChar *str1, const xmlChar *str2);
710/// ```
711///
712/// Returns 0 if equal, <0 if str1 < str2, >0 if str1 > str2.
713/// NULL-safe: NULL sorts before any non-NULL string.
714#[no_mangle]
715pub unsafe extern "C" fn xmlStrcmp(str1: *const xmlChar, str2: *const xmlChar) -> c_int {
716    if str1.is_null() && str2.is_null() {
717        return 0;
718    }
719    if str1.is_null() {
720        return -1;
721    }
722    if str2.is_null() {
723        return 1;
724    }
725    unsafe { libc::strcmp(str1 as *const c_char, str2 as *const c_char) as c_int }
726}
727
728/// Compare two xmlChar strings up to a given length.
729///
730/// # UPSTREAM-PARITY
731///
732/// ```c
733/// int xmlStrncmp(const xmlChar *str1, const xmlChar *str2, int len);
734/// ```
735#[no_mangle]
736pub unsafe extern "C" fn xmlStrncmp(
737    str1: *const xmlChar,
738    str2: *const xmlChar,
739    len: c_int,
740) -> c_int {
741    if len <= 0 {
742        return 0;
743    }
744    if str1.is_null() && str2.is_null() {
745        return 0;
746    }
747    if str1.is_null() {
748        return -1;
749    }
750    if str2.is_null() {
751        return 1;
752    }
753    unsafe { libc::strncmp(str1 as *const c_char, str2 as *const c_char, len as usize) as c_int }
754}
755
756/// Case-insensitive comparison of two xmlChar strings.
757///
758/// # UPSTREAM-PARITY
759///
760/// ```c
761/// int xmlStrcasecmp(const xmlChar *str1, const xmlChar *str2);
762/// ```
763#[no_mangle]
764pub unsafe extern "C" fn xmlStrcasecmp(str1: *const xmlChar, str2: *const xmlChar) -> c_int {
765    if str1.is_null() && str2.is_null() {
766        return 0;
767    }
768    if str1.is_null() {
769        return -1;
770    }
771    if str2.is_null() {
772        return 1;
773    }
774    unsafe { libc::strcasecmp(str1 as *const c_char, str2 as *const c_char) as c_int }
775}
776
777/// Case-insensitive comparison with length limit.
778///
779/// # UPSTREAM-PARITY
780///
781/// ```c
782/// int xmlStrncasecmp(const xmlChar *str1, const xmlChar *str2, int len);
783/// ```
784#[no_mangle]
785pub unsafe extern "C" fn xmlStrncasecmp(
786    str1: *const xmlChar,
787    str2: *const xmlChar,
788    len: c_int,
789) -> c_int {
790    if len <= 0 {
791        return 0;
792    }
793    if str1.is_null() && str2.is_null() {
794        return 0;
795    }
796    if str1.is_null() {
797        return -1;
798    }
799    if str2.is_null() {
800        return 1;
801    }
802    unsafe {
803        libc::strncasecmp(str1 as *const c_char, str2 as *const c_char, len as usize) as c_int
804    }
805}
806
807/// Check if two xmlChar strings are equal.
808///
809/// # UPSTREAM-PARITY
810///
811/// ```c
812/// int xmlStrEqual(const xmlChar *str1, const xmlChar *str2);
813/// ```
814///
815/// Returns 1 if equal, 0 if not. NULL-safe.
816#[no_mangle]
817pub unsafe extern "C" fn xmlStrEqual(str1: *const xmlChar, str2: *const xmlChar) -> c_int {
818    if str1.is_null() && str2.is_null() {
819        return 1;
820    }
821    if str1.is_null() || str2.is_null() {
822        return 0;
823    }
824    unsafe { (libc::strcmp(str1 as *const c_char, str2 as *const c_char) == 0) as c_int }
825}
826
827/// Build a QName (upstream tree.h).
828///
829/// # UPSTREAM-PARITY
830///
831/// ```c
832/// xmlChar *xmlBuildQName(const xmlChar *ncname, const xmlChar *prefix,
833///                        xmlChar *memory, int len);
834/// ```
835#[no_mangle]
836pub unsafe extern "C" fn xmlBuildQName(
837    ncname: *const xmlChar,
838    prefix: *const xmlChar,
839    memory: *mut xmlChar,
840    len: c_int,
841) -> *mut xmlChar {
842    crate::xml::string::build_qname(ncname, prefix, memory, len)
843}
844
845/// Split a QName into prefix + local part (upstream tree.h).
846///
847/// # UPSTREAM-PARITY
848///
849/// ```c
850/// xmlChar *xmlSplitQName2(const xmlChar *name, xmlChar **prefix);
851/// ```
852#[no_mangle]
853pub unsafe extern "C" fn xmlSplitQName2(
854    name: *const xmlChar,
855    prefix: *mut *mut xmlChar,
856) -> *mut xmlChar {
857    crate::xml::string::split_qname2(name, prefix)
858}
859
860/// Return the prefix length of a QName (upstream tree.h).
861///
862/// # UPSTREAM-PARITY
863///
864/// ```c
865/// int xmlSplitQName3(const xmlChar *name, int *len);
866/// ```
867#[no_mangle]
868pub unsafe extern "C" fn xmlSplitQName3(name: *const xmlChar, len: *mut c_int) -> c_int {
869    crate::xml::string::split_qname3(name, len)
870}
871
872/// Deprecated alias of `xmlSplitQName2` (upstream tree.h `xmlSplitQName`).
873#[no_mangle]
874pub unsafe extern "C" fn xmlSplitQName(
875    name: *const xmlChar,
876    prefix: *mut *mut xmlChar,
877) -> *mut xmlChar {
878    crate::xml::string::split_qname2(name, prefix)
879}
880
881/// Count UTF-8 characters (upstream xmlstring.h).
882///
883/// # UPSTREAM-PARITY
884///
885/// ```c
886/// int xmlUTF8Strlen(const xmlChar *utf);
887/// ```
888#[no_mangle]
889pub unsafe extern "C" fn xmlUTF8Strlen(utf: *const xmlChar) -> c_int {
890    crate::xml::string::utf8_strlen(utf)
891}
892
893/// Size in bytes of a UTF-8 sequence (upstream xmlstring.h).
894///
895/// # UPSTREAM-PARITY
896///
897/// ```c
898/// int xmlUTF8Size(const xmlChar *utf);
899/// ```
900#[no_mangle]
901pub unsafe extern "C" fn xmlUTF8Size(utf: *const xmlChar) -> c_int {
902    crate::xml::string::utf8_size(utf)
903}
904
905/// Check UTF-8 validity (upstream xmlstring.h).
906///
907/// # UPSTREAM-PARITY
908///
909/// ```c
910/// int xmlCheckUTF8(const unsigned char *utf);
911/// ```
912#[no_mangle]
913pub unsafe extern "C" fn xmlCheckUTF8(utf: *const xmlChar) -> c_int {
914    crate::xml::string::check_utf8(utf)
915}
916
917/// Check if an xmlChar string equals a qualified name.
918///
919/// # UPSTREAM-PARITY
920///
921/// ```c
922/// int xmlStrQEqual(const xmlChar *pref, const xmlChar *name, const xmlChar *str);
923/// ```
924///
925/// Returns 1 if `pref:name` equals `str`, 0 otherwise.
926/// `pref` may be NULL (compares only name).
927#[no_mangle]
928pub unsafe extern "C" fn xmlStrQEqual(
929    pref: *const xmlChar,
930    name: *const xmlChar,
931    str: *const xmlChar,
932) -> c_int {
933    if name.is_null() || str.is_null() {
934        return 0;
935    }
936    if pref.is_null() {
937        return unsafe { xmlStrEqual(name, str) };
938    }
939    // Compare "pref:name" with str
940    let pref_len = unsafe { xmlStrlen(pref) };
941    let name_len = unsafe { xmlStrlen(name) };
942    let total_len = pref_len + 1 + name_len;
943    let str_len = unsafe { xmlStrlen(str) };
944    if total_len != str_len {
945        return 0;
946    }
947    // Compare prefix part
948    if unsafe {
949        libc::strncmp(
950            pref as *const c_char,
951            str as *const c_char,
952            pref_len as usize,
953        )
954    } != 0
955    {
956        return 0;
957    }
958    // Check colon
959    if unsafe { *str.add(pref_len as usize) } != b':' as xmlChar {
960        return 0;
961    }
962    // Compare name part
963    (unsafe {
964        libc::strncmp(
965            name as *const c_char,
966            str.add((pref_len + 1) as usize) as *const c_char,
967            name_len as usize,
968        ) == 0
969    }) as c_int
970}
971
972/// Concatenate two strings.
973///
974/// # UPSTREAM-PARITY
975///
976/// ```c
977/// xmlChar *xmlStrcat(xmlChar *cur, const xmlChar *add);
978/// ```
979///
980/// # SAFETY
981///
982/// - `cur` must be a valid xmlMalloc'd string or NULL.
983/// - `add` must be a valid string or NULL.
984/// - If `cur` is NULL, behaves like xmlStrdup(add).
985#[no_mangle]
986pub unsafe extern "C" fn xmlStrcat(cur: *mut xmlChar, add: *const xmlChar) -> *mut xmlChar {
987    if add.is_null() {
988        return cur;
989    }
990    if cur.is_null() {
991        return unsafe { xmlStrdup(add) };
992    }
993    let cur_len = unsafe { xmlStrlen(cur) } as usize;
994    let add_len = unsafe { xmlStrlen(add) } as usize;
995    let new_size = cur_len + add_len + 1;
996    let new_ptr = unsafe { xmlReallocImpl(cur as *mut c_void, new_size) };
997    if new_ptr.is_null() {
998        return ptr::null_mut();
999    }
1000    unsafe {
1001        ptr::copy_nonoverlapping(add as *const u8, (new_ptr as *mut u8).add(cur_len), add_len);
1002        *((new_ptr as *mut u8).add(cur_len + add_len)) = 0;
1003    }
1004    new_ptr as *mut xmlChar
1005}
1006
1007/// Concatenate up to `len` characters.
1008///
1009/// # UPSTREAM-PARITY
1010///
1011/// ```c
1012/// xmlChar *xmlStrncat(xmlChar *cur, const xmlChar *add, int len);
1013/// ```
1014///
1015/// # SAFETY
1016///
1017/// Same as xmlStrcat, but only copies up to `len` characters from `add`.
1018#[no_mangle]
1019pub unsafe extern "C" fn xmlStrncat(
1020    cur: *mut xmlChar,
1021    add: *const xmlChar,
1022    len: c_int,
1023) -> *mut xmlChar {
1024    if add.is_null() || len <= 0 {
1025        return cur;
1026    }
1027    let len = len as usize;
1028    if cur.is_null() {
1029        return unsafe { xmlStrndup(add, len as c_int) };
1030    }
1031    let cur_len = unsafe { xmlStrlen(cur) } as usize;
1032    let new_size = cur_len + len + 1;
1033    let new_ptr = unsafe { xmlReallocImpl(cur as *mut c_void, new_size) };
1034    if new_ptr.is_null() {
1035        return ptr::null_mut();
1036    }
1037    unsafe {
1038        ptr::copy_nonoverlapping(add as *const u8, (new_ptr as *mut u8).add(cur_len), len);
1039        *((new_ptr as *mut u8).add(cur_len + len)) = 0;
1040    }
1041    new_ptr as *mut xmlChar
1042}
1043
1044/// Create a new string by concatenating up to `len` characters.
1045///
1046/// # UPSTREAM-PARITY
1047///
1048/// ```c
1049/// xmlChar *xmlStrncatNew(const xmlChar *str1, const xmlChar *str2, int len);
1050/// ```
1051#[no_mangle]
1052pub unsafe extern "C" fn xmlStrncatNew(
1053    str1: *const xmlChar,
1054    str2: *const xmlChar,
1055    len: c_int,
1056) -> *mut xmlChar {
1057    let mut result: *mut xmlChar = ptr::null_mut();
1058    if !str1.is_null() {
1059        result = unsafe { xmlStrdup(str1) };
1060    }
1061    if !str2.is_null() && len > 0 {
1062        result = unsafe { xmlStrncat(result, str2, len) };
1063    }
1064    result
1065}
1066
1067/// Copy a string.
1068///
1069/// # UPSTREAM-PARITY
1070///
1071/// ```c
1072/// xmlChar *xmlStrcpy(xmlChar *dst, const xmlChar *src);
1073/// ```
1074///
1075/// # SAFETY
1076///
1077/// - `dst` must be a valid xmlMalloc'd buffer large enough to hold `src`.
1078/// - `src` must be a valid string.
1079#[no_mangle]
1080pub unsafe extern "C" fn xmlStrcpy(dst: *mut xmlChar, src: *const xmlChar) -> *mut xmlChar {
1081    if dst.is_null() || src.is_null() {
1082        return dst;
1083    }
1084    let len = unsafe { xmlStrlen(src) } as usize + 1;
1085    unsafe {
1086        ptr::copy_nonoverlapping(src as *const u8, dst as *mut u8, len);
1087    }
1088    dst
1089}
1090
1091/// Copy up to `len` characters.
1092///
1093/// # UPSTREAM-PARITY
1094///
1095/// ```c
1096/// xmlChar *xmlStrncpy(xmlChar *dst, const xmlChar *src, int len);
1097/// ```
1098#[no_mangle]
1099pub unsafe extern "C" fn xmlStrncpy(
1100    dst: *mut xmlChar,
1101    src: *const xmlChar,
1102    len: c_int,
1103) -> *mut xmlChar {
1104    if dst.is_null() || src.is_null() || len <= 0 {
1105        return dst;
1106    }
1107    let len = len as usize;
1108    let src_len = unsafe { xmlStrlen(src) } as usize;
1109    let copy_len = if src_len < len { src_len } else { len - 1 };
1110    unsafe {
1111        ptr::copy_nonoverlapping(src as *const u8, dst as *mut u8, copy_len);
1112        *dst.add(copy_len) = 0;
1113    }
1114    dst
1115}
1116
1117/// Extract a substring.
1118///
1119/// # UPSTREAM-PARITY
1120///
1121/// ```c
1122/// xmlChar *xmlStrsub(const xmlChar *str, int start, int len);
1123/// ```
1124///
1125/// Returns a newly allocated substring, or NULL on error.
1126#[no_mangle]
1127pub unsafe extern "C" fn xmlStrsub(str: *const xmlChar, start: c_int, len: c_int) -> *mut xmlChar {
1128    if str.is_null() || start < 0 || len < 0 {
1129        return ptr::null_mut();
1130    }
1131    let str_len = unsafe { xmlStrlen(str) };
1132    if start >= str_len {
1133        return unsafe { xmlStrdup(b"\0" as *const u8 as *const xmlChar) };
1134    }
1135    let actual_len = if start + len > str_len {
1136        str_len - start
1137    } else {
1138        len
1139    };
1140    unsafe { xmlStrndup(str.add(start as usize), actual_len) }
1141}
1142
1143// ═══════════════════════════════════════════════════════════════════════════════
1144// 6. Tree — Document, Node, Attribute, Namespace, DTD, Entity
1145// ═══════════════════════════════════════════════════════════════════════════════
1146
1147/// Create a new document.
1148///
1149/// # UPSTREAM-PARITY
1150///
1151/// ```c
1152/// xmlDocPtr xmlNewDoc(const xmlChar *version);
1153/// ```
1154///
1155/// # SAFETY
1156///
1157/// - `version` must be a valid string or NULL (defaults to "1.0").
1158/// - Returns a newly allocated document. Caller must free with `xmlFreeDoc`.
1159#[no_mangle]
1160pub unsafe extern "C" fn xmlNewDoc(version: *const xmlChar) -> *mut _xmlDoc {
1161    crate::xml::tree::new_doc(version)
1162}
1163
1164/// Free a document.
1165///
1166/// # UPSTREAM-PARITY
1167///
1168/// ```c
1169/// void xmlFreeDoc(xmlDocPtr doc);
1170/// ```
1171///
1172/// # SAFETY
1173///
1174/// - `doc` must be a valid document pointer or NULL.
1175#[no_mangle]
1176pub unsafe extern "C" fn xmlFreeDoc(doc: *mut _xmlDoc) {
1177    crate::xml::tree::free_doc(doc);
1178}
1179
1180/// Get the compression mode of a document (upstream tree.h).
1181///
1182/// # UPSTREAM-PARITY
1183///
1184/// ```c
1185/// int xmlGetDocCompressMode(const xmlDoc *doc);
1186/// ```
1187///
1188/// Returns the compression level (0-9) or -1 if `doc` is NULL.
1189#[no_mangle]
1190pub unsafe extern "C" fn xmlGetDocCompressMode(doc: *mut _xmlDoc) -> c_int {
1191    crate::xml::tree::xmlGetDocCompressMode(doc)
1192}
1193
1194/// Set the compression mode of a document (upstream tree.h).
1195///
1196/// # UPSTREAM-PARITY
1197///
1198/// ```c
1199/// void xmlSetDocCompressMode(xmlDocPtr doc, int mode);
1200/// ```
1201#[no_mangle]
1202pub unsafe extern "C" fn xmlSetDocCompressMode(doc: *mut _xmlDoc, mode: c_int) {
1203    crate::xml::tree::xmlSetDocCompressMode(doc, mode);
1204}
1205
1206/// Create a new node.
1207///
1208/// # UPSTREAM-PARITY
1209///
1210/// ```c
1211/// xmlNodePtr xmlNewNode(xmlNsPtr ns, const xmlChar *name);
1212/// ```
1213///
1214/// # SAFETY
1215///
1216/// - `ns` may be NULL.
1217/// - `name` must be a valid string.
1218/// - Returns a newly allocated node. Caller must free with `xmlFreeNode`.
1219#[no_mangle]
1220pub unsafe extern "C" fn xmlNewNode(ns: *mut _xmlNs, name: *const xmlChar) -> *mut _xmlNode {
1221    crate::xml::tree::new_node(ns, name)
1222}
1223
1224/// Free a node.
1225///
1226/// # UPSTREAM-PARITY
1227///
1228/// ```c
1229/// void xmlFreeNode(xmlNodePtr node);
1230/// ```
1231///
1232/// # SAFETY
1233///
1234/// - `node` must be a valid node pointer or NULL.
1235/// - The node must NOT be part of a document tree (must be unlinked first).
1236#[no_mangle]
1237pub unsafe extern "C" fn xmlFreeNode(node: *mut _xmlNode) {
1238    crate::xml::tree::free_node(node);
1239}
1240
1241/// Free a linked list of nodes (upstream tree.h).
1242///
1243/// # UPSTREAM-PARITY
1244///
1245/// ```c
1246/// void xmlFreeNodeList(xmlNodePtr node);
1247/// ```
1248///
1249/// Frees the node and all its siblings (following `next` pointers),
1250/// recursively freeing children. Matches upstream `xmlFreeNodeList`
1251/// (tree.c): a NULL argument is a no-op.
1252///
1253/// # SAFETY
1254///
1255/// - `node` must be a valid node pointer or NULL.
1256/// - The list must NOT be part of a document tree (must be unlinked first).
1257#[no_mangle]
1258pub unsafe extern "C" fn xmlFreeNodeList(node: *mut _xmlNode) {
1259    crate::xml::tree::free_node_list(node);
1260}
1261
1262/// Unlink a node from its tree.
1263///
1264/// # UPSTREAM-PARITY
1265///
1266/// ```c
1267/// void xmlUnlinkNode(xmlNodePtr node);
1268/// ```
1269///
1270/// # SAFETY
1271///
1272/// - `node` must be a valid node pointer or NULL.
1273#[no_mangle]
1274pub unsafe extern "C" fn xmlUnlinkNode(node: *mut _xmlNode) {
1275    crate::xml::tree::unlink_node(node);
1276}
1277
1278/// Initialize a SAX handler with the default SAX2 callbacks (upstream SAX2.h).
1279///
1280/// # UPSTREAM-PARITY
1281///
1282/// ```c
1283/// void xmlSAX2InitDefaultSAXHandler(xmlSAXHandler *hdlr, int warning);
1284/// ```
1285///
1286/// The `warning` parameter controls whether the warning callback is set in
1287/// upstream; the candidate always sets it (the parser dispatches warnings
1288/// identically) — a documented safe divergence tracked in the parity ledger.
1289///
1290/// # SAFETY
1291///
1292/// - `handler` must be a valid writable `_xmlSAXHandler` or NULL.
1293#[no_mangle]
1294pub unsafe extern "C" fn xmlSAX2InitDefaultSAXHandler(
1295    handler: *mut crate::abi::structs::_xmlSAXHandler,
1296    _warning: c_int,
1297) {
1298    crate::xml::sax::dispatch::xmlSAX2InitDefaultSAXHandler(handler);
1299}
1300
1301/// Initialize a SAX handler with the default HTML callbacks (upstream SAX2.h).
1302///
1303/// # UPSTREAM-PARITY
1304///
1305/// ```c
1306/// void xmlSAX2InitHtmlDefaultSAXHandler(xmlSAXHandler *hdlr);
1307/// ```
1308///
1309/// Upstream (SAX2.c `xmlSAX2InitHtmlDefaultSAXHandler`) fills the handler
1310/// with the SAX2 defaults minus the DTD-declaration callbacks
1311/// (resolveEntity/getParameterEntity/entityDecl/attributeDecl/elementDecl/
1312/// notationDecl/unparsedEntityDecl/reference/externalSubset are NULL) and
1313/// sets `initialized = 1` (not XML_SAX2_MAGIC). The candidate mirrors that
1314/// exactly.
1315///
1316/// # SAFETY
1317///
1318/// - `handler` must be a valid writable `_xmlSAXHandler` or NULL.
1319#[no_mangle]
1320pub unsafe extern "C" fn xmlSAX2InitHtmlDefaultSAXHandler(
1321    handler: *mut crate::abi::structs::_xmlSAXHandler,
1322) {
1323    use crate::abi::callbacks::*;
1324    use crate::abi::structs::_xmlSAXHandler;
1325    if handler.is_null() {
1326        return;
1327    }
1328    // SAFETY: handler is non-NULL and writable.
1329    unsafe {
1330        // The DTD-ish callbacks are not part of the HTML default set.
1331        if (*handler).initialized != 0 {
1332            return;
1333        }
1334        crate::xml::sax::dispatch::xmlSAX2InitDefaultSAXHandler(handler);
1335        let h = &mut *handler;
1336        h.resolveEntity = None;
1337        h.getParameterEntity = None;
1338        h.entityDecl = None;
1339        h.attributeDecl = None;
1340        h.elementDecl = None;
1341        h.notationDecl = None;
1342        h.unparsedEntityDecl = None;
1343        h.reference = None;
1344        h.externalSubset = None;
1345        h.initialized = 1;
1346    }
1347}
1348
1349/// Add a child node.
1350///
1351/// # UPSTREAM-PARITY
1352///
1353/// ```c
1354/// xmlNodePtr xmlAddChild(xmlNodePtr parent, xmlNodePtr cur);
1355/// ```
1356///
1357/// # SAFETY
1358///
1359/// - `parent` must be a valid node.
1360/// - `cur` must be a valid node (ownership transfers to parent).
1361/// - Returns pointer to the added child (borrowed).
1362#[no_mangle]
1363pub unsafe extern "C" fn xmlAddChild(parent: *mut _xmlNode, cur: *mut _xmlNode) -> *mut _xmlNode {
1364    crate::xml::tree::add_child(parent, cur)
1365}
1366
1367/// Add a sibling node.
1368///
1369/// # UPSTREAM-PARITY
1370///
1371/// ```c
1372/// xmlNodePtr xmlAddSibling(xmlNodePtr cur, xmlNodePtr sibling);
1373/// ```
1374///
1375/// # SAFETY
1376///
1377/// Same as xmlAddChild, but adds after `cur` instead of as a child.
1378#[no_mangle]
1379pub unsafe extern "C" fn xmlAddSibling(
1380    cur: *mut _xmlNode,
1381    sibling: *mut _xmlNode,
1382) -> *mut _xmlNode {
1383    crate::xml::tree::add_sibling(cur, sibling)
1384}
1385
1386/// Create a new child element.
1387///
1388/// # UPSTREAM-PARITY
1389///
1390/// ```c
1391/// xmlNodePtr xmlNewChild(xmlNodePtr parent, xmlNsPtr ns,
1392///                        const xmlChar *name, const xmlChar *content);
1393/// ```
1394///
1395/// Creates a new element node, adds it as a child of `parent`, and
1396/// sets its content if `content` is non-NULL.
1397///
1398/// # SAFETY
1399///
1400/// - `parent` must be a valid node (may be NULL).
1401/// - `ns` may be NULL.
1402/// - `name` must be a valid string.
1403/// - Returns a newly allocated node (owned by parent).
1404#[no_mangle]
1405pub unsafe extern "C" fn xmlNewChild(
1406    parent: *mut _xmlNode,
1407    ns: *mut _xmlNs,
1408    name: *const xmlChar,
1409    content: *const xmlChar,
1410) -> *mut _xmlNode {
1411    crate::xml::tree::new_child(parent, ns, name)
1412}
1413
1414/// Set the root element of a document.
1415///
1416/// # UPSTREAM-PARITY
1417///
1418/// ```c
1419/// xmlNodePtr xmlDocSetRootElement(xmlDocPtr doc, xmlNodePtr root);
1420/// ```
1421///
1422/// Returns the old root element (if any), which the caller must free.
1423///
1424/// # SAFETY
1425///
1426/// - `doc` must be a valid document.
1427/// - `root` must be a valid node (ownership transfers to doc).
1428#[no_mangle]
1429pub unsafe extern "C" fn xmlDocSetRootElement(
1430    doc: *mut _xmlDoc,
1431    root: *mut _xmlNode,
1432) -> *mut _xmlNode {
1433    crate::xml::tree::doc_set_root_element(doc, root)
1434}
1435
1436/// Get the root element of a document.
1437///
1438/// # UPSTREAM-PARITY
1439///
1440/// ```c
1441/// xmlNodePtr xmlDocGetRootElement(const xmlDoc *doc);
1442/// ```
1443///
1444/// Returns a borrowed pointer (do not free).
1445#[no_mangle]
1446pub extern "C" fn xmlDocGetRootElement(doc: *const _xmlDoc) -> *mut _xmlNode {
1447    crate::xml::tree::doc_get_root_element(doc as *mut _xmlDoc)
1448}
1449
1450/// Copy a node.
1451///
1452/// # UPSTREAM-PARITY
1453///
1454/// ```c
1455/// xmlNodePtr xmlCopyNode(const xmlNodePtr node, int extended);
1456/// ```
1457///
1458/// If `extended` is 1, copies recursively (deep copy).
1459/// If `extended` is 0, copies only the node itself (shallow copy).
1460///
1461/// Returns a newly allocated copy. Caller must free with `xmlFreeNode`.
1462#[no_mangle]
1463pub unsafe extern "C" fn xmlCopyNode(node: *const _xmlNode, extended: c_int) -> *mut _xmlNode {
1464    crate::xml::tree::copy_node(node, extended)
1465}
1466
1467/// Copy a document.
1468///
1469/// # UPSTREAM-PARITY
1470///
1471/// ```c
1472/// xmlDocPtr xmlCopyDoc(const xmlDocPtr doc, int recursive);
1473/// ```
1474///
1475/// Returns a newly allocated copy. Caller must free with `xmlFreeDoc`.
1476#[no_mangle]
1477pub unsafe extern "C" fn xmlCopyDoc(doc: *const _xmlDoc, recursive: c_int) -> *mut _xmlDoc {
1478    crate::xml::tree::copy_doc(doc, recursive)
1479}
1480
1481/// Create a text node.
1482///
1483/// # UPSTREAM-PARITY
1484///
1485/// ```c
1486/// xmlNodePtr xmlNewText(const xmlChar *content);
1487/// ```
1488///
1489/// Creates a new text node with the given content.
1490/// If `content` is NULL, creates an empty text node.
1491#[no_mangle]
1492pub unsafe extern "C" fn xmlNewText(content: *const xmlChar) -> *mut _xmlNode {
1493    crate::xml::tree::new_text(content)
1494}
1495
1496/// Create a new comment node.
1497///
1498/// # UPSTREAM-PARITY
1499///
1500/// ```c
1501/// xmlNodePtr xmlNewComment(const xmlChar *content);
1502/// ```
1503#[no_mangle]
1504pub unsafe extern "C" fn xmlNewComment(content: *const xmlChar) -> *mut _xmlNode {
1505    crate::xml::tree::new_comment(content)
1506}
1507
1508/// Create a new PI node.
1509///
1510/// # UPSTREAM-PARITY
1511///
1512/// ```c
1513/// xmlNodePtr xmlNewPI(const xmlChar *name, const xmlChar *content);
1514/// ```
1515#[no_mangle]
1516pub unsafe extern "C" fn xmlNewPI(name: *const xmlChar, content: *const xmlChar) -> *mut _xmlNode {
1517    crate::xml::tree::new_pi(name, content)
1518}
1519
1520/// Create a new CDATA node.
1521///
1522/// # UPSTREAM-PARITY
1523///
1524/// ```c
1525/// xmlNodePtr xmlNewCDataBlock(xmlDocPtr doc, const xmlChar *content, int len);
1526/// ```
1527#[no_mangle]
1528pub unsafe extern "C" fn xmlNewCDataBlock(
1529    doc: *mut _xmlDoc,
1530    content: *const xmlChar,
1531    len: c_int,
1532) -> *mut _xmlNode {
1533    crate::xml::tree::new_cdata_block(doc, content, len)
1534}
1535
1536/// Create a new namespace definition.
1537///
1538/// # UPSTREAM-PARITY
1539///
1540/// ```c
1541/// xmlNsPtr xmlNewNs(xmlNodePtr node, const xmlChar *href, const xmlChar *prefix);
1542/// ```
1543///
1544/// # SAFETY
1545///
1546/// - `node` may be NULL.
1547/// - `href` and `prefix` are copied.
1548/// - Returns a borrowed pointer (namespace is owned by the node).
1549#[no_mangle]
1550pub unsafe extern "C" fn xmlNewNs(
1551    node: *mut _xmlNode,
1552    href: *const xmlChar,
1553    prefix: *const xmlChar,
1554) -> *mut _xmlNs {
1555    crate::xml::tree::new_ns(node, href, prefix)
1556}
1557
1558/// Set the namespace of a node.
1559///
1560/// # UPSTREAM-PARITY
1561///
1562/// ```c
1563/// void xmlSetNs(xmlNodePtr node, xmlNsPtr ns);
1564/// ```
1565#[no_mangle]
1566pub unsafe extern "C" fn xmlSetNs(node: *mut _xmlNode, ns: *mut _xmlNs) {
1567    crate::xml::tree::set_ns(node, ns);
1568}
1569
1570/// Get the namespace of a node.
1571///
1572/// # UPSTREAM-PARITY
1573///
1574/// ```c
1575/// xmlNsPtr xmlGetNsList(xmlDocPtr doc, const xmlNode *node);
1576/// ```
1577#[no_mangle]
1578pub unsafe extern "C" fn xmlGetNsList(
1579    doc: *mut _xmlDoc,
1580    node: *const _xmlNode,
1581) -> *mut *mut _xmlNs {
1582    crate::xml::tree::get_ns_list(doc, node as *mut _xmlNode)
1583}
1584
1585/// Search for a namespace by href.
1586///
1587/// # UPSTREAM-PARITY
1588///
1589/// ```c
1590/// xmlNsPtr xmlSearchNs(xmlDocPtr doc, xmlNodePtr node, const xmlChar *nameSpace);
1591/// ```
1592#[no_mangle]
1593pub unsafe extern "C" fn xmlSearchNs(
1594    doc: *mut _xmlDoc,
1595    node: *mut _xmlNode,
1596    nameSpace: *const xmlChar,
1597) -> *mut _xmlNs {
1598    crate::xml::tree::search_ns(doc, node, nameSpace)
1599}
1600
1601/// Search for a namespace by href, using the full in-scope chain.
1602///
1603/// # UPSTREAM-PARITY
1604///
1605/// ```c
1606/// xmlNsPtr xmlSearchNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar *href);
1607/// ```
1608#[no_mangle]
1609pub unsafe extern "C" fn xmlSearchNsByHref(
1610    doc: *mut _xmlDoc,
1611    node: *mut _xmlNode,
1612    href: *const xmlChar,
1613) -> *mut _xmlNs {
1614    crate::xml::tree::search_ns_by_href(doc, node, href)
1615}
1616
1617/// Set a property (attribute) on a node.
1618///
1619/// # UPSTREAM-PARITY
1620///
1621/// ```c
1622/// xmlAttrPtr xmlSetProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value);
1623/// ```
1624///
1625/// If the attribute already exists, its value is updated.
1626/// Returns a borrowed pointer to the attribute.
1627///
1628/// # SAFETY
1629///
1630/// - `node` must be a valid element node.
1631/// - `name` must be a valid string.
1632/// - `value` may be NULL.
1633#[no_mangle]
1634pub unsafe extern "C" fn xmlSetProp(
1635    node: *mut _xmlNode,
1636    name: *const xmlChar,
1637    value: *const xmlChar,
1638) -> *mut _xmlAttr {
1639    crate::xml::tree::set_prop(node, name, value)
1640}
1641
1642/// Get a property value by name.
1643///
1644/// # UPSTREAM-PARITY
1645///
1646/// ```c
1647/// xmlChar *xmlGetProp(const xmlNode *node, const xmlChar *name);
1648/// ```
1649///
1650/// Returns a newly allocated string. Caller must free with `xmlFree`.
1651#[no_mangle]
1652pub unsafe extern "C" fn xmlGetProp(node: *const _xmlNode, name: *const xmlChar) -> *mut xmlChar {
1653    crate::xml::tree::get_prop(node as *mut _xmlNode, name)
1654}
1655
1656/// Get a namespaced property value.
1657///
1658/// # UPSTREAM-PARITY
1659///
1660/// ```c
1661/// xmlChar *xmlGetNsProp(const xmlNode *node, const xmlChar *name, const xmlChar *nameSpace);
1662/// ```
1663#[no_mangle]
1664pub unsafe extern "C" fn xmlGetNsProp(
1665    node: *const _xmlNode,
1666    name: *const xmlChar,
1667    nameSpace: *const xmlChar,
1668) -> *mut xmlChar {
1669    crate::xml::tree::get_ns_prop(node as *mut _xmlNode, name, nameSpace)
1670}
1671
1672/// Set a namespaced property.
1673///
1674/// # UPSTREAM-PARITY
1675///
1676/// ```c
1677/// xmlAttrPtr xmlSetNsProp(xmlNodePtr node, xmlNsPtr ns,
1678///                         const xmlChar *name, const xmlChar *value);
1679/// ```
1680#[no_mangle]
1681pub unsafe extern "C" fn xmlSetNsProp(
1682    node: *mut _xmlNode,
1683    ns: *mut _xmlNs,
1684    name: *const xmlChar,
1685    value: *const xmlChar,
1686) -> *mut _xmlAttr {
1687    crate::xml::tree::set_ns_prop(node, ns, name, value)
1688}
1689
1690/// Remove a property by name.
1691///
1692/// # UPSTREAM-PARITY
1693///
1694/// ```c
1695/// int xmlRemoveProp(xmlAttrPtr attr);
1696/// ```
1697///
1698/// Returns 0 on success, -1 on error.
1699#[no_mangle]
1700pub unsafe extern "C" fn xmlRemoveProp(attr: *mut _xmlAttr) -> c_int {
1701    crate::xml::tree::remove_prop(attr)
1702}
1703
1704/// Check whether a node has a property (upstream tree.h).
1705///
1706/// # UPSTREAM-PARITY
1707///
1708/// ```c
1709/// xmlAttrPtr xmlHasProp(const xmlNode *node, const xmlChar *name);
1710/// ```
1711///
1712/// Returns the attribute pointer or NULL.
1713#[no_mangle]
1714pub unsafe extern "C" fn xmlHasProp(node: *const _xmlNode, name: *const xmlChar) -> *mut _xmlAttr {
1715    crate::xml::tree::has_prop(node as *mut _xmlNode, name)
1716}
1717
1718/// Check whether a node has a namespaced property (upstream tree.h).
1719///
1720/// # UPSTREAM-PARITY
1721///
1722/// ```c
1723/// xmlAttrPtr xmlHasNsProp(const xmlNode *node, const xmlChar *name,
1724///                         const xmlChar *nameSpace);
1725/// ```
1726#[no_mangle]
1727pub unsafe extern "C" fn xmlHasNsProp(
1728    node: *const _xmlNode,
1729    name: *const xmlChar,
1730    nameSpace: *const xmlChar,
1731) -> *mut _xmlAttr {
1732    crate::xml::tree::has_ns_prop(node as *mut _xmlNode, name, nameSpace)
1733}
1734
1735/// Remove a property by name (upstream tree.h).
1736///
1737/// # UPSTREAM-PARITY
1738///
1739/// ```c
1740/// int xmlUnsetProp(xmlNodePtr node, const xmlChar *name);
1741/// ```
1742///
1743/// Returns 0 on success, -1 if not found.
1744#[no_mangle]
1745pub unsafe extern "C" fn xmlUnsetProp(node: *mut _xmlNode, name: *const xmlChar) -> c_int {
1746    crate::xml::tree::unset_prop(node, name)
1747}
1748
1749/// Remove a namespaced property by name (upstream tree.h).
1750///
1751/// # UPSTREAM-PARITY
1752///
1753/// ```c
1754/// int xmlUnsetNsProp(xmlNodePtr node, const xmlChar *name,
1755///                    const xmlChar *nameSpace);
1756/// ```
1757#[no_mangle]
1758pub unsafe extern "C" fn xmlUnsetNsProp(
1759    node: *mut _xmlNode,
1760    name: *const xmlChar,
1761    nameSpace: *const xmlChar,
1762) -> c_int {
1763    crate::xml::tree::unset_ns_prop(node, name, nameSpace)
1764}
1765
1766/// Return the first child element (upstream tree.h).
1767///
1768/// # UPSTREAM-PARITY
1769///
1770/// ```c
1771/// xmlNodePtr xmlFirstElementChild(xmlNodePtr parent);
1772/// ```
1773#[no_mangle]
1774pub unsafe extern "C" fn xmlFirstElementChild(parent: *mut _xmlNode) -> *mut _xmlNode {
1775    crate::xml::tree::first_element_child(parent)
1776}
1777
1778/// Return the last child element (upstream tree.h).
1779#[no_mangle]
1780pub unsafe extern "C" fn xmlLastElementChild(parent: *mut _xmlNode) -> *mut _xmlNode {
1781    crate::xml::tree::last_element_child(parent)
1782}
1783
1784/// Return the next element sibling (upstream tree.h).
1785#[no_mangle]
1786pub unsafe extern "C" fn xmlNextElementSibling(node: *mut _xmlNode) -> *mut _xmlNode {
1787    crate::xml::tree::next_element_sibling(node)
1788}
1789
1790/// Return the previous element sibling (upstream tree.h).
1791#[no_mangle]
1792pub unsafe extern "C" fn xmlPreviousElementSibling(node: *mut _xmlNode) -> *mut _xmlNode {
1793    crate::xml::tree::previous_element_sibling(node)
1794}
1795
1796/// Count the child elements (upstream tree.h).
1797///
1798/// # UPSTREAM-PARITY
1799///
1800/// ```c
1801/// unsigned long xmlChildElementCount(xmlNodePtr parent);
1802/// ```
1803#[no_mangle]
1804pub unsafe extern "C" fn xmlChildElementCount(parent: *mut _xmlNode) -> c_ulong {
1805    crate::xml::tree::child_element_count(parent)
1806}
1807
1808/// Concatenate text to a node (upstream tree.h).
1809///
1810/// # UPSTREAM-PARITY
1811///
1812/// ```c
1813/// int xmlTextConcat(xmlNodePtr node, const xmlChar *content, int len);
1814/// ```
1815#[no_mangle]
1816pub unsafe extern "C" fn xmlTextConcat(
1817    node: *mut _xmlNode,
1818    content: *const xmlChar,
1819    len: c_int,
1820) -> c_int {
1821    crate::xml::tree::text_concat(node, content, len)
1822}
1823
1824/// Merge two text nodes (upstream tree.h).
1825///
1826/// # UPSTREAM-PARITY
1827///
1828/// ```c
1829/// xmlNodePtr xmlTextMerge(xmlNodePtr first, xmlNodePtr second);
1830/// ```
1831#[no_mangle]
1832pub unsafe extern "C" fn xmlTextMerge(
1833    first: *mut _xmlNode,
1834    second: *mut _xmlNode,
1835) -> *mut _xmlNode {
1836    crate::xml::tree::text_merge(first, second)
1837}
1838
1839/// Get a DTD from a document, creating one if needed.
1840///
1841/// # UPSTREAM-PARITY
1842///
1843/// ```c
1844/// xmlDtdPtr xmlGetIntSubset(const xmlDoc *doc);
1845/// ```
1846#[no_mangle]
1847pub extern "C" fn xmlGetIntSubset(doc: *const _xmlDoc) -> *mut _xmlDtd {
1848    crate::xml::tree::get_int_subset(doc)
1849}
1850
1851/// Create a new DTD.
1852///
1853/// # UPSTREAM-PARITY
1854///
1855/// ```c
1856/// xmlDtdPtr xmlNewDtd(xmlDocPtr doc, const xmlChar *name,
1857///                     const xmlChar *ExternalID, const xmlChar *SystemID);
1858/// ```
1859#[no_mangle]
1860pub unsafe extern "C" fn xmlNewDtd(
1861    doc: *mut _xmlDoc,
1862    name: *const xmlChar,
1863    ExternalID: *const xmlChar,
1864    SystemID: *const xmlChar,
1865) -> *mut _xmlDtd {
1866    crate::xml::tree::new_dtd(doc, name, ExternalID, SystemID)
1867}
1868
1869/// Create a new entity.
1870///
1871/// # UPSTREAM-PARITY
1872///
1873/// ```c
1874/// xmlEntityPtr xmlNewEntity(xmlDocPtr doc, const xmlChar *name, int type,
1875///                           const xmlChar *ExternalID, const xmlChar *SystemID,
1876///                           const xmlChar *content);
1877/// ```
1878#[no_mangle]
1879pub unsafe extern "C" fn xmlNewEntity(
1880    doc: *mut _xmlDoc,
1881    name: *const xmlChar,
1882    type_: c_int,
1883    ExternalID: *const xmlChar,
1884    SystemID: *const xmlChar,
1885    content: *const xmlChar,
1886) -> *mut _xmlEntity {
1887    crate::xml::tree::new_entity(doc, name, type_, ExternalID, SystemID, content)
1888}
1889
1890/// Get an entity by name.
1891///
1892/// # UPSTREAM-PARITY
1893///
1894/// ```c
1895/// xmlEntityPtr xmlGetDocEntity(const xmlDoc *doc, const xmlChar *name);
1896/// ```
1897#[no_mangle]
1898pub unsafe extern "C" fn xmlGetDocEntity(
1899    doc: *const _xmlDoc,
1900    name: *const xmlChar,
1901) -> *mut _xmlEntity {
1902    crate::xml::tree::get_doc_entity(doc, name)
1903}
1904
1905/// Get a parameter entity by name.
1906///
1907/// # UPSTREAM-PARITY
1908///
1909/// ```c
1910/// xmlEntityPtr xmlGetParameterEntity(const xmlDoc *doc, const xmlChar *name);
1911/// ```
1912#[no_mangle]
1913pub unsafe extern "C" fn xmlGetParameterEntity(
1914    doc: *const _xmlDoc,
1915    name: *const xmlChar,
1916) -> *mut _xmlEntity {
1917    crate::xml::tree::get_parameter_entity(doc, name)
1918}
1919
1920// ── DTD Declaration Exports ────────────────────────────────────────────
1921
1922/// Create an internal subset (DTD).
1923///
1924/// # UPSTREAM-PARITY
1925///
1926/// ```c
1927/// xmlDtdPtr xmlCreateIntSubset(xmlDocPtr doc, const xmlChar *name,
1928///                              const xmlChar *ExternalID, const xmlChar *SystemID);
1929/// ```
1930#[no_mangle]
1931pub unsafe extern "C" fn xmlCreateIntSubset(
1932    doc: *mut _xmlDoc,
1933    name: *const xmlChar,
1934    ExternalID: *const xmlChar,
1935    SystemID: *const xmlChar,
1936) -> *mut _xmlDtd {
1937    crate::xml::dtd::create_int_subset(doc, name, ExternalID, SystemID)
1938}
1939
1940/// Free a DTD.
1941///
1942/// # UPSTREAM-PARITY
1943///
1944/// ```c
1945/// void xmlFreeDtd(xmlDtdPtr dtd);
1946/// ```
1947#[no_mangle]
1948pub unsafe extern "C" fn xmlFreeDtd(dtd: *mut _xmlDtd) {
1949    crate::xml::dtd::free_dtd(dtd);
1950}
1951
1952/// Add a notation declaration.
1953///
1954/// # UPSTREAM-PARITY
1955///
1956/// ```c
1957/// xmlNotationPtr xmlAddNotationDecl(xmlDtdPtr dtd, const xmlChar *name,
1958///                                   const xmlChar *PublicID,
1959///                                   const xmlChar *SystemID);
1960/// ```
1961#[no_mangle]
1962pub unsafe extern "C" fn xmlAddNotationDecl(
1963    dtd: *mut _xmlDtd,
1964    name: *const xmlChar,
1965    PublicID: *const xmlChar,
1966    SystemID: *const xmlChar,
1967) -> *mut _xmlNotation {
1968    crate::xml::dtd::add_notation_decl(dtd, name, PublicID, SystemID)
1969}
1970
1971/// Look up a notation declaration.
1972///
1973/// # UPSTREAM-PARITY
1974///
1975/// ```c
1976/// xmlNotationPtr xmlGetNotationDecl(xmlDtdPtr dtd, const xmlChar *name);
1977/// ```
1978#[no_mangle]
1979pub unsafe extern "C" fn xmlGetNotationDecl(
1980    dtd: *mut _xmlDtd,
1981    name: *const xmlChar,
1982) -> *mut _xmlNotation {
1983    crate::xml::dtd::get_notation_decl(dtd, name)
1984}
1985
1986/// Copy a notation declaration.
1987///
1988/// # UPSTREAM-PARITY
1989///
1990/// ```c
1991/// xmlNotationPtr xmlCopyNotation(xmlNotationPtr notation);
1992/// ```
1993#[no_mangle]
1994pub unsafe extern "C" fn xmlCopyNotation(notation: *mut _xmlNotation) -> *mut _xmlNotation {
1995    crate::xml::dtd::copy_notation(notation)
1996}
1997
1998/// Free a notation declaration.
1999///
2000/// # UPSTREAM-PARITY
2001///
2002/// ```c
2003/// void xmlFreeNotation(xmlNotationPtr notation);
2004/// ```
2005#[no_mangle]
2006pub unsafe extern "C" fn xmlFreeNotation(notation: *mut _xmlNotation) {
2007    crate::xml::dtd::free_notation(notation);
2008}
2009
2010/// Add an element declaration.
2011///
2012/// # UPSTREAM-PARITY
2013///
2014/// ```c
2015/// xmlElementPtr xmlAddElementDecl(xmlDtdPtr dtd, const xmlChar *name, int type,
2016///                                 xmlElementContentPtr content);
2017/// ```
2018#[no_mangle]
2019pub unsafe extern "C" fn xmlAddElementDecl(
2020    dtd: *mut _xmlDtd,
2021    name: *const xmlChar,
2022    type_: c_int,
2023    content: *mut _xmlElementContent,
2024) -> *mut _xmlElement {
2025    crate::xml::dtd::add_element_decl(dtd, name, type_, content)
2026}
2027
2028/// Look up an element declaration.
2029///
2030/// # UPSTREAM-PARITY
2031///
2032/// ```c
2033/// xmlElementPtr xmlGetElementDecl(xmlDtdPtr dtd, const xmlChar *name);
2034/// ```
2035#[no_mangle]
2036pub unsafe extern "C" fn xmlGetElementDecl(
2037    dtd: *mut _xmlDtd,
2038    name: *const xmlChar,
2039) -> *mut _xmlElement {
2040    crate::xml::dtd::get_element_decl(dtd, name)
2041}
2042
2043/// Copy an element declaration.
2044///
2045/// # UPSTREAM-PARITY
2046///
2047/// ```c
2048/// xmlElementPtr xmlCopyElement(xmlElementPtr elem);
2049/// ```
2050#[no_mangle]
2051pub unsafe extern "C" fn xmlCopyElement(elem: *mut _xmlElement) -> *mut _xmlElement {
2052    crate::xml::dtd::copy_element(elem)
2053}
2054
2055/// Free an element declaration.
2056///
2057/// # UPSTREAM-PARITY
2058///
2059/// ```c
2060/// void xmlFreeElement(xmlElementPtr elem);
2061/// ```
2062#[no_mangle]
2063pub unsafe extern "C" fn xmlFreeElement(elem: *mut _xmlElement) {
2064    crate::xml::dtd::free_element(elem);
2065}
2066
2067/// Add an attribute declaration.
2068///
2069/// # UPSTREAM-PARITY
2070///
2071/// ```c
2072/// xmlAttributePtr xmlAddAttributeDecl(xmlDtdPtr dtd, xmlElementPtr elem,
2073///                                     const xmlChar *name, int type, int def,
2074///                                     const xmlChar *defaultValue,
2075///                                     xmlEnumerationPtr tree);
2076/// ```
2077#[no_mangle]
2078pub unsafe extern "C" fn xmlAddAttributeDecl(
2079    dtd: *mut _xmlDtd,
2080    elem: *mut _xmlElement,
2081    name: *const xmlChar,
2082    type_: c_int,
2083    def: c_int,
2084    defaultValue: *const xmlChar,
2085    tree: *mut _xmlEnumeration,
2086) -> *mut _xmlAttribute {
2087    crate::xml::dtd::add_attribute_decl(dtd, elem, name, type_, def, defaultValue, tree)
2088}
2089
2090/// Look up an attribute declaration.
2091///
2092/// # UPSTREAM-PARITY
2093///
2094/// ```c
2095/// xmlAttributePtr xmlGetAttributeDecl(xmlDtdPtr dtd, xmlElementPtr elem,
2096///                                     const xmlChar *name, int namePrefix);
2097/// ```
2098#[no_mangle]
2099pub unsafe extern "C" fn xmlGetAttributeDecl(
2100    dtd: *mut _xmlDtd,
2101    elem: *mut _xmlElement,
2102    name: *const xmlChar,
2103    namePrefix: c_int,
2104) -> *mut _xmlAttribute {
2105    crate::xml::dtd::get_attribute_decl(dtd, elem, name, namePrefix)
2106}
2107
2108/// Copy an attribute declaration.
2109///
2110/// # UPSTREAM-PARITY
2111///
2112/// ```c
2113/// xmlAttributePtr xmlCopyAttribute(xmlAttributePtr attr);
2114/// ```
2115#[no_mangle]
2116pub unsafe extern "C" fn xmlCopyAttribute(attr: *mut _xmlAttribute) -> *mut _xmlAttribute {
2117    crate::xml::dtd::copy_attribute_decl(attr)
2118}
2119
2120/// Free an attribute declaration.
2121///
2122/// # UPSTREAM-PARITY
2123///
2124/// ```c
2125/// void xmlFreeAttribute(xmlAttributePtr attr);
2126/// ```
2127#[no_mangle]
2128pub unsafe extern "C" fn xmlFreeAttribute(attr: *mut _xmlAttribute) {
2129    crate::xml::dtd::free_attribute(attr);
2130}
2131
2132/// Create a new element content model.
2133///
2134/// # UPSTREAM-PARITY
2135///
2136/// ```c
2137/// xmlElementContentPtr xmlNewElementContent(const xmlChar *name, int type);
2138/// ```
2139#[no_mangle]
2140pub unsafe extern "C" fn xmlNewElementContent(
2141    name: *const xmlChar,
2142    type_: c_int,
2143) -> *mut _xmlElementContent {
2144    crate::xml::dtd::create_content_model(name, type_)
2145}
2146
2147/// Copy an element content model.
2148///
2149/// # UPSTREAM-PARITY
2150///
2151/// ```c
2152/// xmlElementContentPtr xmlCopyElementContent(xmlElementContentPtr content);
2153/// ```
2154#[no_mangle]
2155pub unsafe extern "C" fn xmlCopyElementContent(
2156    content: *mut _xmlElementContent,
2157) -> *mut _xmlElementContent {
2158    crate::xml::dtd::copy_content_model(content)
2159}
2160
2161/// Free an element content model.
2162///
2163/// # UPSTREAM-PARITY
2164///
2165/// ```c
2166/// void xmlFreeElementContent(xmlElementContentPtr cur);
2167/// ```
2168#[no_mangle]
2169pub unsafe extern "C" fn xmlFreeElementContent(cur: *mut _xmlElementContent) {
2170    crate::xml::dtd::free_content_model(cur);
2171}
2172
2173// ── Entity Exports ─────────────────────────────────────────────────────
2174
2175/// Add an entity declaration.
2176///
2177/// # UPSTREAM-PARITY
2178///
2179/// ```c
2180/// xmlEntityPtr xmlAddEntity(xmlDtdPtr dtd, const xmlChar *name, int type,
2181///                           const xmlChar *ExternalID, const xmlChar *SystemID,
2182///                           const xmlChar *content);
2183/// ```
2184#[no_mangle]
2185pub unsafe extern "C" fn xmlAddEntity(
2186    dtd: *mut _xmlDtd,
2187    name: *const xmlChar,
2188    type_: c_int,
2189    ExternalID: *const xmlChar,
2190    SystemID: *const xmlChar,
2191    content: *const xmlChar,
2192) -> *mut _xmlEntity {
2193    crate::xml::entities::add_entity(dtd, name, type_, ExternalID, SystemID, content)
2194}
2195
2196/// Add an entity declaration to a document's internal subset (upstream
2197/// entities.c `xmlAddDocEntity`): if the document has no internal subset
2198/// one is created.
2199///
2200/// # UPSTREAM-PARITY
2201///
2202/// ```c
2203/// xmlEntityPtr xmlAddDocEntity(xmlDocPtr doc, const xmlChar *name, int type,
2204///                              const xmlChar *ExternalID, const xmlChar *SystemID,
2205///                              const xmlChar *content);
2206/// ```
2207#[no_mangle]
2208pub unsafe extern "C" fn xmlAddDocEntity(
2209    doc: *mut _xmlDoc,
2210    name: *const xmlChar,
2211    type_: c_int,
2212    ExternalID: *const xmlChar,
2213    SystemID: *const xmlChar,
2214    content: *const xmlChar,
2215) -> *mut _xmlEntity {
2216    crate::xml::tree::add_doc_entity(doc, name, type_, ExternalID, SystemID, content)
2217}
2218
2219/// Add an entity declaration to a document's external subset (upstream
2220/// entities.c `xmlAddDtdEntity`).
2221///
2222/// # UPSTREAM-PARITY
2223///
2224/// ```c
2225/// xmlEntityPtr xmlAddDtdEntity(xmlDocPtr doc, const xmlChar *name, int type,
2226///                              const xmlChar *ExternalID, const xmlChar *SystemID,
2227///                              const xmlChar *content);
2228/// ```
2229#[no_mangle]
2230pub unsafe extern "C" fn xmlAddDtdEntity(
2231    doc: *mut _xmlDoc,
2232    name: *const xmlChar,
2233    type_: c_int,
2234    ExternalID: *const xmlChar,
2235    SystemID: *const xmlChar,
2236    content: *const xmlChar,
2237) -> *mut _xmlEntity {
2238    crate::xml::tree::add_dtd_entity(doc, name, type_, ExternalID, SystemID, content)
2239}
2240
2241/// Get an entity declaration from a DTD (upstream entities.c
2242/// `xmlGetDtdEntity`): searches the internal then external subset.
2243///
2244/// # UPSTREAM-PARITY
2245///
2246/// ```c
2247/// xmlEntityPtr xmlGetDtdEntity(xmlDocPtr doc, const xmlChar *name);
2248/// ```
2249#[no_mangle]
2250pub unsafe extern "C" fn xmlGetDtdEntity(
2251    doc: *mut _xmlDoc,
2252    name: *const xmlChar,
2253) -> *mut _xmlEntity {
2254    crate::xml::tree::get_dtd_entity(doc, name)
2255}
2256
2257/// Get an entity by name.
2258///
2259/// # UPSTREAM-PARITY
2260///
2261/// ```c
2262/// xmlEntityPtr xmlGetEntity(xmlDocPtr doc, const xmlChar *name);
2263/// ```
2264#[no_mangle]
2265pub unsafe extern "C" fn xmlGetEntity(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlEntity {
2266    crate::xml::entities::get_entity(doc, name)
2267}
2268
2269/// Copy an entity.
2270///
2271/// # UPSTREAM-PARITY
2272///
2273/// ```c
2274/// xmlEntityPtr xmlCopyEntity(xmlEntityPtr entity);
2275/// ```
2276#[no_mangle]
2277pub unsafe extern "C" fn xmlCopyEntity(entity: *mut _xmlEntity) -> *mut _xmlEntity {
2278    crate::xml::entities::copy_entity(entity)
2279}
2280
2281/// Free an entity.
2282///
2283/// # UPSTREAM-PARITY
2284///
2285/// ```c
2286/// void xmlFreeEntity(xmlEntityPtr entity);
2287/// ```
2288#[no_mangle]
2289pub unsafe extern "C" fn xmlFreeEntity(entity: *mut _xmlEntity) {
2290    crate::xml::entities::free_entity(entity);
2291}
2292
2293/// Encode entities for reentrant output.
2294///
2295/// # UPSTREAM-PARITY
2296///
2297/// ```c
2298/// xmlChar *xmlEncodeEntitiesReentrant(xmlDocPtr doc, const xmlChar *input);
2299/// ```
2300#[no_mangle]
2301pub unsafe extern "C" fn xmlEncodeEntitiesReentrant(
2302    doc: *mut _xmlDoc,
2303    input: *const xmlChar,
2304) -> *mut xmlChar {
2305    crate::xml::entities::encode_entities_reentrant(doc, input)
2306}
2307
2308/// Encode special characters in a string (upstream entities.c
2309/// `xmlEncodeSpecialChars`): escapes `<`, `>`, `&`, `"` and `\r`.
2310///
2311/// # UPSTREAM-PARITY
2312///
2313/// ```c
2314/// xmlChar *xmlEncodeSpecialChars(const xmlDoc *doc, const xmlChar *input);
2315/// ```
2316///
2317/// Returns a newly allocated string (free with `xmlFree`) or NULL.
2318#[no_mangle]
2319pub unsafe extern "C" fn xmlEncodeSpecialChars(
2320    _doc: *const _xmlDoc,
2321    input: *const xmlChar,
2322) -> *mut xmlChar {
2323    if input.is_null() {
2324        return ptr::null_mut();
2325    }
2326    unsafe {
2327        let len = crate::xml::string::xml_strlen(input);
2328        // Worst case: every byte becomes a 6-byte entity (&#13; is 5; &quot;
2329        // is 6).
2330        let cap = len * 6 + 1;
2331        let out = crate::abi::allocator::xmlMallocImpl(cap) as *mut xmlChar;
2332        if out.is_null() {
2333            return ptr::null_mut();
2334        }
2335        let mut o = 0usize;
2336        let mut i = 0usize;
2337        while i < len {
2338            let c = *input.add(i);
2339            let rep: &[u8] = match c {
2340                b'<' => b"&lt;",
2341                b'>' => b"&gt;",
2342                b'&' => b"&amp;",
2343                b'"' => b"&quot;",
2344                b'\r' => b"&#13;",
2345                _ => {
2346                    *out.add(o) = c;
2347                    o += 1;
2348                    i += 1;
2349                    continue;
2350                }
2351            };
2352            core::ptr::copy_nonoverlapping(rep.as_ptr(), out.add(o), rep.len());
2353            o += rep.len();
2354            i += 1;
2355        }
2356        *out.add(o) = 0;
2357        out
2358    }
2359}
2360
2361/// Deprecated entity encoder (upstream 2.15 `xmlEncodeEntities`): the
2362/// symbol still exists for ABI compatibility but emits a one-time
2363/// deprecation warning and returns NULL (verified against the oracle DSO
2364/// disassembly — the 2.15 implementation returns NULL after warning).
2365///
2366/// # UPSTREAM-PARITY
2367///
2368/// ```c
2369/// xmlChar *xmlEncodeEntities(xmlDocPtr doc, const xmlChar *input);
2370/// ```
2371#[no_mangle]
2372pub unsafe extern "C" fn xmlEncodeEntities(
2373    _doc: *mut _xmlDoc,
2374    _input: *const xmlChar,
2375) -> *mut xmlChar {
2376    use core::sync::atomic::{AtomicBool, Ordering};
2377    static WARNED: AtomicBool = AtomicBool::new(false);
2378    if !WARNED.swap(true, Ordering::Relaxed) {
2379        // Match the oracle: one-time "deprecated" diagnostic on stderr.
2380        let msg = b"xmlEncodeEntities is deprecated, use xmlEncodeSpecialChars or xmlEncodeEntitiesReentrant\n";
2381        unsafe {
2382            libc::fwrite(
2383                msg.as_ptr() as *const c_void,
2384                1,
2385                msg.len(),
2386                libc::fdopen(2, b"w\0" as *const u8 as *const c_char) as *mut libc::FILE,
2387            );
2388        }
2389    }
2390    ptr::null_mut()
2391}
2392
2393/// Get the line number of a node.
2394///
2395/// # UPSTREAM-PARITY
2396///
2397/// ```c
2398/// long xmlGetLineNo(const xmlNode *node);
2399/// ```
2400#[no_mangle]
2401pub extern "C" fn xmlGetLineNo(node: *const _xmlNode) -> c_long {
2402    crate::xml::tree::get_line_no(node)
2403}
2404
2405// ═══════════════════════════════════════════════════════════════════════════════
2406// Serialization — xmlNodeDump, xmlDocDump, xmlSaveFile, etc.
2407// ═══════════════════════════════════════════════════════════════════════════════
2408
2409/// Dump a node to a buffer.
2410///
2411/// # UPSTREAM-PARITY
2412///
2413/// ```c
2414/// int xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level, int format);
2415/// ```
2416#[no_mangle]
2417pub unsafe extern "C" fn xmlNodeDump(
2418    buf: *mut _xmlBuffer,
2419    doc: *mut _xmlDoc,
2420    cur: *mut _xmlNode,
2421    level: c_int,
2422    format: c_int,
2423) -> c_int {
2424    if buf.is_null() || cur.is_null() {
2425        return -1;
2426    }
2427    crate::xml::tree::xmlNodeDump(buf, doc, cur, level, format)
2428}
2429
2430/// Dump a document to a file pointer.
2431///
2432/// # UPSTREAM-PARITY
2433///
2434/// ```c
2435/// int xmlDocDump(FILE *f, xmlDocPtr doc);
2436/// ```
2437#[no_mangle]
2438pub unsafe extern "C" fn xmlDocDump(fp: *mut c_void, doc: *mut _xmlDoc) -> c_int {
2439    if fp.is_null() || doc.is_null() {
2440        return -1;
2441    }
2442    crate::xml::tree::xmlDocDump(fp, doc)
2443}
2444
2445/// Dump a document to memory with format.
2446///
2447/// # UPSTREAM-PARITY
2448///
2449/// ```c
2450/// void xmlDocDumpFormatMemory(xmlDocPtr doc, xmlChar **mem, int *size, int format);
2451/// ```
2452#[no_mangle]
2453pub unsafe extern "C" fn xmlDocDumpFormatMemory(
2454    doc: *mut _xmlDoc,
2455    mem: *mut *mut xmlChar,
2456    size: *mut c_int,
2457    format: c_int,
2458) {
2459    if doc.is_null() || mem.is_null() || size.is_null() {
2460        return;
2461    }
2462    crate::xml::tree::xmlDocDumpFormatMemory(doc, mem, size, format)
2463}
2464
2465/// Dump a document to memory (unformatted).
2466///
2467/// # UPSTREAM-PARITY
2468///
2469/// ```c
2470/// void xmlDocDumpMemory(xmlDocPtr doc, xmlChar **mem, int *size);
2471/// ```
2472#[no_mangle]
2473pub unsafe extern "C" fn xmlDocDumpMemory(
2474    doc: *mut _xmlDoc,
2475    mem: *mut *mut xmlChar,
2476    size: *mut c_int,
2477) {
2478    if doc.is_null() || mem.is_null() || size.is_null() {
2479        return;
2480    }
2481    crate::xml::tree::xmlDocDumpMemory(doc, mem, size)
2482}
2483
2484/// Save a document to a file.
2485///
2486/// # UPSTREAM-PARITY
2487///
2488/// ```c
2489/// int xmlSaveFile(const char *filename, xmlDocPtr cur);
2490/// ```
2491#[no_mangle]
2492pub unsafe extern "C" fn xmlSaveFile(filename: *const c_char, cur: *mut _xmlDoc) -> c_int {
2493    if filename.is_null() || cur.is_null() {
2494        return -1;
2495    }
2496    crate::xml::tree::xmlSaveFile(filename, cur)
2497}
2498
2499/// Save a document to a file with encoding.
2500///
2501/// # UPSTREAM-PARITY
2502///
2503/// ```c
2504/// int xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding);
2505/// ```
2506#[no_mangle]
2507pub unsafe extern "C" fn xmlSaveFileEnc(
2508    filename: *const c_char,
2509    cur: *mut _xmlDoc,
2510    encoding: *const c_char,
2511) -> c_int {
2512    if filename.is_null() || cur.is_null() {
2513        return -1;
2514    }
2515    crate::xml::tree::xmlSaveFileEnc(filename, cur, encoding)
2516}
2517
2518/// Save a document to a file with format.
2519///
2520/// # UPSTREAM-PARITY
2521///
2522/// ```c
2523/// int xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format);
2524/// ```
2525#[no_mangle]
2526pub unsafe extern "C" fn xmlSaveFormatFile(
2527    filename: *const c_char,
2528    cur: *mut _xmlDoc,
2529    format: c_int,
2530) -> c_int {
2531    if filename.is_null() || cur.is_null() {
2532        return -1;
2533    }
2534    crate::xml::tree::xmlSaveFormatFile(filename, cur, format)
2535}
2536
2537/// Save a document to a file with encoding and format.
2538///
2539/// # UPSTREAM-PARITY
2540///
2541/// ```c
2542/// int xmlSaveFormatFileEnc(const char *filename, xmlDocPtr cur, const char *encoding, int format);
2543/// ```
2544#[no_mangle]
2545pub unsafe extern "C" fn xmlSaveFormatFileEnc(
2546    filename: *const c_char,
2547    cur: *mut _xmlDoc,
2548    encoding: *const c_char,
2549    format: c_int,
2550) -> c_int {
2551    if filename.is_null() || cur.is_null() {
2552        return -1;
2553    }
2554    crate::xml::tree::xmlSaveFormatFileEnc(filename, cur, encoding, format)
2555}
2556
2557// ═══════════════════════════════════════════════════════════════════════════════
2558// 7. Parser — SAX, DOM, Push, Reader
2559// ═══════════════════════════════════════════════════════════════════════════════
2560
2561/// Read an XML document from a string.
2562///
2563/// # UPSTREAM-PARITY
2564///
2565/// ```c
2566/// xmlDocPtr xmlReadDoc(const xmlChar *cur, const char *URL,
2567///                      const char *encoding, int options);
2568/// ```
2569///
2570/// Returns a parsed document. Caller must free with `xmlFreeDoc`.
2571#[no_mangle]
2572pub unsafe extern "C" fn xmlReadDoc(
2573    cur: *const xmlChar,
2574    URL: *const c_char,
2575    encoding: *const c_char,
2576    options: c_int,
2577) -> *mut _xmlDoc {
2578    // SAFETY: cur must be a valid null-terminated xmlChar string if non-null.
2579    if cur.is_null() {
2580        return ptr::null_mut();
2581    }
2582    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2583    if ctxt.is_null() {
2584        return ptr::null_mut();
2585    }
2586    let len = crate::xml::string::xml_strlen(cur);
2587    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
2588    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2589    (*ctxt).options = options;
2590    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
2591        let doc = (*ctxt).myDoc;
2592        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2593        return doc;
2594    }
2595    let doc = (*ctxt).myDoc;
2596    if !doc.is_null() {
2597        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
2598    }
2599    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2600    doc
2601}
2602
2603/// Read an XML document from a file.
2604///
2605/// # UPSTREAM-PARITY
2606///
2607/// ```c
2608/// xmlDocPtr xmlReadFile(const char *URL, const char *encoding, int options);
2609/// ```
2610#[no_mangle]
2611pub unsafe extern "C" fn xmlReadFile(
2612    URL: *const c_char,
2613    encoding: *const c_char,
2614    options: c_int,
2615) -> *mut _xmlDoc {
2616    // SAFETY: URL must be a valid C string or NULL.
2617    if URL.is_null() {
2618        return ptr::null_mut();
2619    }
2620    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2621    if ctxt.is_null() {
2622        return ptr::null_mut();
2623    }
2624    let input = match crate::xml::parser::helpers::input_from_file(URL) {
2625        Ok(input) => input,
2626        Err(_) => {
2627            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2628            return ptr::null_mut();
2629        }
2630    };
2631    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2632    (*ctxt).options = options;
2633    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
2634        let doc = (*ctxt).myDoc;
2635        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2636        // UPSTREAM-PARITY: on a hard (non-recoverable) parse error the
2637        // partially built document is discarded and NULL is returned; with
2638        // XML_PARSE_RECOVER the partial tree is kept.
2639        if options & 1 << 0 != 0 {
2640            return doc;
2641        }
2642        if !doc.is_null() {
2643            crate::xml::tree::free_doc(doc);
2644        }
2645        return ptr::null_mut();
2646    }
2647    let doc = (*ctxt).myDoc;
2648    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2649    doc
2650}
2651
2652/// Recover-parse a document from a string (upstream parser.h): same as
2653/// `xmlReadDoc` with XML_PARSE_RECOVER forced.
2654///
2655/// # UPSTREAM-PARITY
2656///
2657/// ```c
2658/// xmlDocPtr xmlRecoverDoc(const xmlChar *cur);
2659/// ```
2660#[no_mangle]
2661pub unsafe extern "C" fn xmlRecoverDoc(cur: *const xmlChar) -> *mut _xmlDoc {
2662    unsafe { xmlReadDoc(cur, ptr::null(), ptr::null(), 1 << 0) }
2663}
2664
2665/// Recover-parse a document from a file (upstream parser.h).
2666///
2667/// # UPSTREAM-PARITY
2668///
2669/// ```c
2670/// xmlDocPtr xmlRecoverFile(const char *filename);
2671/// ```
2672#[no_mangle]
2673pub unsafe extern "C" fn xmlRecoverFile(filename: *const c_char) -> *mut _xmlDoc {
2674    unsafe { xmlReadFile(filename, ptr::null(), 1 << 0) }
2675}
2676
2677/// Recover-parse a document from memory (upstream parser.h).
2678///
2679/// # UPSTREAM-PARITY
2680///
2681/// ```c
2682/// xmlDocPtr xmlRecoverMemory(const char *buffer, int size);
2683/// ```
2684#[no_mangle]
2685pub unsafe extern "C" fn xmlRecoverMemory(buffer: *const c_char, size: c_int) -> *mut _xmlDoc {
2686    unsafe { xmlReadMemory(buffer, size, ptr::null(), ptr::null(), 1 << 0) }
2687}
2688
2689/// Read an XML document from a file (upstream parser.h).
2690///
2691/// # UPSTREAM-PARITY
2692///
2693/// ```c
2694/// xmlDocPtr xmlReadMemory(const char *buffer, int size,
2695///                         const char *URL, const char *encoding, int options);
2696/// ```
2697#[no_mangle]
2698pub unsafe extern "C" fn xmlReadMemory(
2699    buffer: *const c_char,
2700    size: c_int,
2701    URL: *const c_char,
2702    encoding: *const c_char,
2703    options: c_int,
2704) -> *mut _xmlDoc {
2705    // SAFETY: buffer must be a valid pointer with at least `size` readable
2706    // bytes. An empty input (size 0) is still parsed — upstream reports
2707    // "Document is empty".
2708    if buffer.is_null() || size < 0 {
2709        return ptr::null_mut();
2710    }
2711    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2712    if ctxt.is_null() {
2713        return ptr::null_mut();
2714    }
2715    // UPSTREAM-PARITY: the URL becomes the input's filename (feeds the
2716    // `file:line:` error prefix and doc->URL).
2717    let input = crate::xml::parser::helpers::input_from_memory_named(buffer, size, URL);
2718    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2719    // UPSTREAM-PARITY (xmlReadMemory -> xmlCtxtReadMemory): the parse
2720    // options are mirrored into the context members (dictNames, keepBlanks,
2721    // recovery, ...) before parsing starts.
2722    crate::abi::exports_parser::apply_options(ctxt, options);
2723    let parsed = crate::xml::parser::helpers::parse_document(ctxt);
2724    let doc = (*ctxt).myDoc;
2725    // UPSTREAM-PARITY: the URL is attached to the document on success AND on
2726    // the recovery path (the partial tree keeps the document identity).
2727    if !doc.is_null() && !URL.is_null() && (*doc).URL.is_null() {
2728        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
2729    }
2730    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2731    if parsed != 0 {
2732        // UPSTREAM-PARITY: on a hard (non-recoverable) parse error the
2733        // partially built document is discarded and NULL is returned; with
2734        // XML_PARSE_RECOVER the partial tree is kept.
2735        if options & 1 << 0 != 0 {
2736            return doc;
2737        }
2738        if !doc.is_null() {
2739            crate::xml::tree::free_doc(doc);
2740        }
2741        return ptr::null_mut();
2742    }
2743    doc
2744}
2745
2746/// Load a list of catalogs (upstream `xmlLoadCatalogs`).
2747///
2748/// # SAFETY
2749///
2750/// - `catalogs` must be a valid NUL-terminated string or NULL.
2751#[no_mangle]
2752pub unsafe extern "C" fn xmlLoadCatalogs(catalogs: *const c_char) {
2753    if !catalogs.is_null() {
2754        crate::xml::catalog::load_catalog(catalogs);
2755    }
2756}
2757
2758/// Load a single catalog (upstream `xmlLoadCatalog`).
2759///
2760/// # SAFETY
2761///
2762/// - `catalogs` must be a valid NUL-terminated string or NULL.
2763#[no_mangle]
2764pub unsafe extern "C" fn xmlLoadCatalog(catalogs: *const c_char) -> c_int {
2765    // UPSTREAM-PARITY (catalog.c xmlLoadCatalog): returns 0 on success,
2766    // 1 on error (unlike xmlCatalogLoad which returns the catalog handle).
2767    let handle = crate::xml::catalog::load_catalog(catalogs);
2768    if handle.is_null() {
2769        1
2770    } else {
2771        0
2772    }
2773}
2774
2775/// Read an XML document from a file descriptor.
2776///
2777/// # UPSTREAM-PARITY
2778///
2779/// ```c
2780/// xmlDocPtr xmlReadFd(int fd, const char *URL, const char *encoding, int options);
2781/// ```
2782#[no_mangle]
2783pub unsafe extern "C" fn xmlReadFd(
2784    fd: c_int,
2785    URL: *const c_char,
2786    encoding: *const c_char,
2787    options: c_int,
2788) -> *mut _xmlDoc {
2789    // SAFETY: fd must be a valid open file descriptor.
2790    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2791    if ctxt.is_null() {
2792        return ptr::null_mut();
2793    }
2794    // Read all data from the fd
2795    let mut buf = Vec::new();
2796    let mut tmp = [0u8; 4096];
2797    loop {
2798        let n = libc::read(fd, tmp.as_mut_ptr() as *mut c_void, tmp.len());
2799        if n <= 0 {
2800            break;
2801        }
2802        buf.extend_from_slice(&tmp[..n as usize]);
2803    }
2804    let input = crate::xml::parser::helpers::input_from_memory(
2805        buf.as_ptr() as *const c_char,
2806        buf.len() as c_int,
2807    );
2808    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2809    (*ctxt).options = options;
2810    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
2811        let doc = (*ctxt).myDoc;
2812        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2813        return doc;
2814    }
2815    let doc = (*ctxt).myDoc;
2816    if !doc.is_null() && !URL.is_null() {
2817        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
2818    }
2819    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2820    doc
2821}
2822
2823/// Read an XML document from I/O callbacks.
2824///
2825/// # UPSTREAM-PARITY
2826///
2827/// ```c
2828/// xmlDocPtr xmlReadIO(xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
2829///                     void *ioctx, const char *URL, const char *encoding, int options);
2830/// ```
2831#[no_mangle]
2832pub unsafe extern "C" fn xmlReadIO(
2833    ioread: Option<xmlInputReadCallback>,
2834    ioclose: Option<xmlInputCloseCallback>,
2835    ioctx: *mut c_void,
2836    URL: *const c_char,
2837    encoding: *const c_char,
2838    options: c_int,
2839) -> *mut _xmlDoc {
2840    // SAFETY: callbacks must be valid function pointers if non-NULL.
2841    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2842    if ctxt.is_null() {
2843        return ptr::null_mut();
2844    }
2845    let input = crate::xml::parser::helpers::input_from_io(ioread, ioclose, ioctx);
2846    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2847    (*ctxt).options = options;
2848    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
2849        let doc = (*ctxt).myDoc;
2850        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2851        return doc;
2852    }
2853    let doc = (*ctxt).myDoc;
2854    if !doc.is_null() && !URL.is_null() {
2855        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
2856    }
2857    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2858    doc
2859}
2860
2861/// Parse an XML document (SAX1).
2862///
2863/// # UPSTREAM-PARITY
2864///
2865/// ```c
2866/// xmlDocPtr xmlSAXParseDoc(xmlSAXHandlerPtr sax, const xmlChar *cur, int recovery);
2867/// ```
2868#[no_mangle]
2869pub unsafe extern "C" fn xmlSAXParseDoc(
2870    sax: *mut _xmlSAXHandler,
2871    cur: *const xmlChar,
2872    recovery: c_int,
2873) -> *mut _xmlDoc {
2874    // SAFETY: cur must be a valid null-terminated xmlChar string.
2875    if cur.is_null() {
2876        return ptr::null_mut();
2877    }
2878    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2879    if ctxt.is_null() {
2880        return ptr::null_mut();
2881    }
2882    if !sax.is_null() {
2883        (*ctxt).sax = sax;
2884        (*ctxt).userData = (*ctxt).sax as *mut c_void;
2885    }
2886    if recovery != 0 {
2887        (*ctxt).recovery = 1;
2888        (*ctxt).options |= 1; // XML_PARSE_RECOVER
2889    }
2890    let len = crate::xml::string::xml_strlen(cur);
2891    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
2892    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2893    crate::xml::parser::helpers::parse_document(ctxt);
2894    let doc = (*ctxt).myDoc;
2895    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2896    doc
2897}
2898
2899/// Parse an XML document (SAX1) with user data (upstream parser.h
2900/// `xmlSAXParseDocWithData`): `user_data` is passed to the SAX callbacks.
2901///
2902/// # UPSTREAM-PARITY
2903///
2904/// ```c
2905/// xmlDocPtr xmlSAXParseDocWithData(xmlSAXHandlerPtr sax, const xmlChar *cur,
2906///                                  int recovery, void *data);
2907/// ```
2908#[no_mangle]
2909pub unsafe extern "C" fn xmlSAXParseDocWithData(
2910    sax: *mut _xmlSAXHandler,
2911    cur: *const xmlChar,
2912    recovery: c_int,
2913    data: *mut c_void,
2914) -> *mut _xmlDoc {
2915    // SAFETY: cur must be a valid null-terminated xmlChar string.
2916    if cur.is_null() {
2917        return ptr::null_mut();
2918    }
2919    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2920    if ctxt.is_null() {
2921        return ptr::null_mut();
2922    }
2923    if !sax.is_null() {
2924        (*ctxt).sax = sax;
2925    }
2926    (*ctxt).userData = data;
2927    if recovery != 0 {
2928        (*ctxt).recovery = 1;
2929        (*ctxt).options |= 1; // XML_PARSE_RECOVER
2930    }
2931    let len = crate::xml::string::xml_strlen(cur);
2932    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
2933    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2934    crate::xml::parser::helpers::parse_document(ctxt);
2935    let doc = (*ctxt).myDoc;
2936    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2937    doc
2938}
2939
2940/// Parse an XML file (SAX1) with user data (upstream parser.h
2941/// `xmlSAXParseFileWithData`).
2942///
2943/// # UPSTREAM-PARITY
2944///
2945/// ```c
2946/// xmlDocPtr xmlSAXParseFileWithData(xmlSAXHandlerPtr sax, const char *filename,
2947///                                   int recovery, void *data);
2948/// ```
2949#[no_mangle]
2950pub unsafe extern "C" fn xmlSAXParseFileWithData(
2951    sax: *mut _xmlSAXHandler,
2952    filename: *const c_char,
2953    recovery: c_int,
2954    data: *mut c_void,
2955) -> *mut _xmlDoc {
2956    // SAFETY: filename must be a valid C string or NULL.
2957    if filename.is_null() {
2958        return ptr::null_mut();
2959    }
2960    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2961    if ctxt.is_null() {
2962        return ptr::null_mut();
2963    }
2964    if !sax.is_null() {
2965        (*ctxt).sax = sax;
2966    }
2967    (*ctxt).userData = data;
2968    if recovery != 0 {
2969        (*ctxt).recovery = 1;
2970        (*ctxt).options |= 1; // XML_PARSE_RECOVER
2971    }
2972    let input = match crate::xml::parser::helpers::input_from_file(filename) {
2973        Ok(input) => input,
2974        Err(_) => {
2975            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2976            return ptr::null_mut();
2977        }
2978    };
2979    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2980    crate::xml::parser::helpers::parse_document(ctxt);
2981    let doc = (*ctxt).myDoc;
2982    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2983    doc
2984}
2985
2986/// Parse an XML document (SAX1) with user data from memory (upstream
2987/// parser.h `xmlSAXParseMemoryWithData`).
2988///
2989/// # UPSTREAM-PARITY
2990///
2991/// ```c
2992/// xmlDocPtr xmlSAXParseMemoryWithData(xmlSAXHandlerPtr sax, const char *buffer,
2993///                                     int size, int recovery, void *data);
2994/// ```
2995#[no_mangle]
2996pub unsafe extern "C" fn xmlSAXParseMemoryWithData(
2997    sax: *mut _xmlSAXHandler,
2998    buffer: *const c_char,
2999    size: c_int,
3000    recovery: c_int,
3001    data: *mut c_void,
3002) -> *mut _xmlDoc {
3003    // SAFETY: buffer must be a valid pointer with `size` readable bytes.
3004    if buffer.is_null() || size <= 0 {
3005        return ptr::null_mut();
3006    }
3007    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3008    if ctxt.is_null() {
3009        return ptr::null_mut();
3010    }
3011    if !sax.is_null() {
3012        (*ctxt).sax = sax;
3013    }
3014    (*ctxt).userData = data;
3015    if recovery != 0 {
3016        (*ctxt).recovery = 1;
3017        (*ctxt).options |= 1; // XML_PARSE_RECOVER
3018    }
3019    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
3020    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3021    crate::xml::parser::helpers::parse_document(ctxt);
3022    let doc = (*ctxt).myDoc;
3023    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3024    doc
3025}
3026
3027/// Parse an XML file (SAX1).
3028///
3029/// # UPSTREAM-PARITY
3030///
3031/// ```c
3032/// xmlDocPtr xmlSAXParseFile(xmlSAXHandlerPtr sax, const char *filename, int recovery);
3033/// ```
3034#[no_mangle]
3035pub unsafe extern "C" fn xmlSAXParseFile(
3036    sax: *mut _xmlSAXHandler,
3037    filename: *const c_char,
3038    recovery: c_int,
3039) -> *mut _xmlDoc {
3040    // SAFETY: filename must be a valid C string.
3041    if filename.is_null() {
3042        return ptr::null_mut();
3043    }
3044    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3045    if ctxt.is_null() {
3046        return ptr::null_mut();
3047    }
3048    if !sax.is_null() {
3049        (*ctxt).sax = sax;
3050        (*ctxt).userData = (*ctxt).sax as *mut c_void;
3051    }
3052    if recovery != 0 {
3053        (*ctxt).recovery = 1;
3054        (*ctxt).options |= 1;
3055    }
3056    let input = match crate::xml::parser::helpers::input_from_file(filename) {
3057        Ok(input) => input,
3058        Err(_) => {
3059            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3060            return ptr::null_mut();
3061        }
3062    };
3063    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3064    crate::xml::parser::helpers::parse_document(ctxt);
3065    let doc = (*ctxt).myDoc;
3066    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3067    doc
3068}
3069
3070/// Parse an XML document from memory (SAX1).
3071///
3072/// # UPSTREAM-PARITY
3073///
3074/// ```c
3075/// xmlDocPtr xmlSAXParseMemory(xmlSAXHandlerPtr sax,
3076///                             const char *buffer, int size, int recovery);
3077/// ```
3078#[no_mangle]
3079pub unsafe extern "C" fn xmlSAXParseMemory(
3080    sax: *mut _xmlSAXHandler,
3081    buffer: *const c_char,
3082    size: c_int,
3083    recovery: c_int,
3084) -> *mut _xmlDoc {
3085    // SAFETY: buffer must be valid with at least `size` bytes.
3086    if buffer.is_null() || size <= 0 {
3087        return ptr::null_mut();
3088    }
3089    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3090    if ctxt.is_null() {
3091        return ptr::null_mut();
3092    }
3093    if !sax.is_null() {
3094        (*ctxt).sax = sax;
3095        (*ctxt).userData = (*ctxt).sax as *mut c_void;
3096    }
3097    if recovery != 0 {
3098        (*ctxt).recovery = 1;
3099        (*ctxt).options |= 1;
3100    }
3101    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
3102    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3103    crate::xml::parser::helpers::parse_document(ctxt);
3104    let doc = (*ctxt).myDoc;
3105    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3106    doc
3107}
3108
3109/// SAX user parse file.
3110///
3111/// # UPSTREAM-PARITY
3112///
3113/// ```c
3114/// int xmlSAXUserParseFile(xmlSAXHandlerPtr sax, void *user_data,
3115///                         const char *filename);
3116/// ```
3117#[no_mangle]
3118pub unsafe extern "C" fn xmlSAXUserParseFile(
3119    sax: *mut _xmlSAXHandler,
3120    user_data: *mut c_void,
3121    filename: *const c_char,
3122) -> c_int {
3123    // SAFETY: filename must be a valid C string. sax and user_data may be NULL.
3124    if filename.is_null() {
3125        return -1;
3126    }
3127    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3128    if ctxt.is_null() {
3129        return -1;
3130    }
3131    if !sax.is_null() {
3132        (*ctxt).sax = sax;
3133    }
3134    (*ctxt).userData = if !user_data.is_null() {
3135        user_data
3136    } else {
3137        ctxt as *mut c_void
3138    };
3139    let input = match crate::xml::parser::helpers::input_from_file(filename) {
3140        Ok(input) => input,
3141        Err(_) => {
3142            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3143            return -1;
3144        }
3145    };
3146    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3147    let ret = crate::xml::parser::helpers::parse_document(ctxt);
3148    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3149    ret
3150}
3151
3152/// SAX user parse memory.
3153///
3154/// # UPSTREAM-PARITY
3155///
3156/// ```c
3157/// int xmlSAXUserParseMemory(xmlSAXHandlerPtr sax, void *user_data,
3158///                           const char *buffer, int size);
3159/// ```
3160#[no_mangle]
3161pub unsafe extern "C" fn xmlSAXUserParseMemory(
3162    sax: *mut _xmlSAXHandler,
3163    user_data: *mut c_void,
3164    buffer: *const c_char,
3165    size: c_int,
3166) -> c_int {
3167    // SAFETY: buffer must be valid with at least `size` bytes.
3168    if buffer.is_null() || size <= 0 {
3169        return -1;
3170    }
3171    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3172    if ctxt.is_null() {
3173        return -1;
3174    }
3175    if !sax.is_null() {
3176        (*ctxt).sax = sax;
3177    }
3178    (*ctxt).userData = if !user_data.is_null() {
3179        user_data
3180    } else {
3181        ctxt as *mut c_void
3182    };
3183    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
3184    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3185    let ret = crate::xml::parser::helpers::parse_document(ctxt);
3186    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3187    ret
3188}
3189
3190/// Parse an XML document from a string (DOM).
3191///
3192/// # UPSTREAM-PARITY
3193///
3194/// ```c
3195/// xmlDocPtr xmlParseDoc(const xmlChar *cur);
3196/// ```
3197#[no_mangle]
3198pub unsafe extern "C" fn xmlParseDoc(cur: *const xmlChar) -> *mut _xmlDoc {
3199    // SAFETY: cur must be a valid null-terminated xmlChar string.
3200    if cur.is_null() {
3201        return ptr::null_mut();
3202    }
3203    xmlReadDoc(cur, ptr::null(), ptr::null(), 0)
3204}
3205
3206/// Parse an XML file (DOM).
3207///
3208/// # UPSTREAM-PARITY
3209///
3210/// ```c
3211/// xmlDocPtr xmlParseFile(const char *filename);
3212/// ```
3213#[no_mangle]
3214pub unsafe extern "C" fn xmlParseFile(filename: *const c_char) -> *mut _xmlDoc {
3215    // SAFETY: filename must be a valid C string.
3216    if filename.is_null() {
3217        return ptr::null_mut();
3218    }
3219    xmlReadFile(filename, ptr::null(), 0)
3220}
3221
3222/// Parse an XML document from memory (DOM).
3223///
3224/// # UPSTREAM-PARITY
3225///
3226/// ```c
3227/// xmlDocPtr xmlParseMemory(const char *buffer, int size);
3228/// ```
3229#[no_mangle]
3230pub unsafe extern "C" fn xmlParseMemory(buffer: *const c_char, size: c_int) -> *mut _xmlDoc {
3231    // SAFETY: buffer must be valid with at least `size` bytes.
3232    if buffer.is_null() || size <= 0 {
3233        return ptr::null_mut();
3234    }
3235    xmlReadMemory(buffer, size, ptr::null(), ptr::null(), 0)
3236}
3237
3238/// Create a file parser context.
3239///
3240/// # UPSTREAM-PARITY
3241///
3242/// ```c
3243/// xmlParserCtxtPtr xmlCreateFileParserCtxt(const char *filename);
3244/// ```
3245#[no_mangle]
3246pub unsafe extern "C" fn xmlCreateFileParserCtxt(filename: *const c_char) -> *mut _xmlParserCtxt {
3247    // SAFETY: filename must be a valid C string.
3248    if filename.is_null() {
3249        return ptr::null_mut();
3250    }
3251    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3252    if ctxt.is_null() {
3253        return ptr::null_mut();
3254    }
3255    let input = match crate::xml::parser::helpers::input_from_file(filename) {
3256        Ok(input) => input,
3257        Err(_) => {
3258            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3259            return ptr::null_mut();
3260        }
3261    };
3262    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3263    ctxt
3264}
3265
3266/// Create a document parser context.
3267///
3268/// # UPSTREAM-PARITY
3269///
3270/// ```c
3271/// xmlParserCtxtPtr xmlCreateDocParserCtxt(const xmlChar *cur);
3272/// ```
3273#[no_mangle]
3274pub unsafe extern "C" fn xmlCreateDocParserCtxt(cur: *const xmlChar) -> *mut _xmlParserCtxt {
3275    // SAFETY: cur must be a valid null-terminated xmlChar string.
3276    if cur.is_null() {
3277        return ptr::null_mut();
3278    }
3279    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3280    if ctxt.is_null() {
3281        return ptr::null_mut();
3282    }
3283    let len = crate::xml::string::xml_strlen(cur);
3284    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
3285    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3286    ctxt
3287}
3288
3289/// Parse a document using an existing parser context.
3290///
3291/// # UPSTREAM-PARITY
3292///
3293/// ```c
3294/// int xmlParseDocument(xmlParserCtxtPtr ctxt);
3295/// ```
3296#[no_mangle]
3297pub unsafe extern "C" fn xmlParseDocument(ctxt: *mut _xmlParserCtxt) -> c_int {
3298    // SAFETY: ctxt must be a valid parser context.
3299    if ctxt.is_null() {
3300        return -1;
3301    }
3302    crate::xml::parser::helpers::parse_document(ctxt)
3303}
3304
3305/// Free a parser context.
3306///
3307/// # UPSTREAM-PARITY
3308///
3309/// ```c
3310/// void xmlFreeParserCtxt(xmlParserCtxtPtr ctxt);
3311/// ```
3312#[no_mangle]
3313pub unsafe extern "C" fn xmlFreeParserCtxt(ctxt: *mut _xmlParserCtxt) {
3314    if ctxt.is_null() {
3315        return;
3316    }
3317    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3318}
3319
3320/// Set parser options.
3321///
3322/// # UPSTREAM-PARITY
3323///
3324/// ```c
3325/// int xmlCtxtUseOptions(xmlParserCtxtPtr ctxt, int options);
3326/// ```
3327#[no_mangle]
3328pub unsafe extern "C" fn xmlCtxtUseOptions(ctxt: *mut _xmlParserCtxt, options: c_int) -> c_int {
3329    if ctxt.is_null() {
3330        return -1;
3331    }
3332    // Phase 1: STUB
3333    unsafe {
3334        (*ctxt).options = options;
3335    }
3336    0
3337}
3338
3339/// Parse a well-balanced chunk (for push parsing).
3340///
3341/// # UPSTREAM-PARITY
3342///
3343/// ```c
3344/// xmlParserErrors xmlParseChunk(xmlParserCtxtPtr ctxt,
3345///                               const char *chunk, int size, int terminate);
3346/// ```
3347#[no_mangle]
3348pub unsafe extern "C" fn xmlParseChunk(
3349    ctxt: *mut _xmlParserCtxt,
3350    chunk: *const c_char,
3351    size: c_int,
3352    terminate: c_int,
3353) -> c_int {
3354    // SAFETY: ctxt must be a valid parser context.
3355    // chunk may be NULL if terminate is set (finalize without data).
3356    if ctxt.is_null() {
3357        return -1;
3358    }
3359    crate::xml::parser::helpers::parse_chunk(ctxt, chunk, size, terminate)
3360}
3361
3362/// Create a memory parser input buffer.
3363///
3364/// # UPSTREAM-PARITY
3365///
3366/// ```c
3367/// xmlParserInputBufferPtr xmlParserInputBufferCreateMem(const char *buffer, int size, int enc);
3368/// ```
3369#[no_mangle]
3370pub unsafe extern "C" fn xmlParserInputBufferCreateMem(
3371    buffer: *const c_char,
3372    size: c_int,
3373    enc: c_int,
3374) -> *mut _xmlParserInputBuffer {
3375    // SAFETY: buffer must be valid with at least `size` bytes.
3376    if buffer.is_null() || size <= 0 {
3377        return ptr::null_mut();
3378    }
3379    crate::xml::parser::helpers::alloc_parser_input_buffer()
3380}
3381
3382/// Create a file parser input buffer.
3383///
3384/// # UPSTREAM-PARITY
3385///
3386/// ```c
3387/// xmlParserInputBufferPtr xmlParserInputBufferCreateFilename(const char *URI, int enc);
3388/// ```
3389#[no_mangle]
3390pub unsafe extern "C" fn xmlParserInputBufferCreateFilename(
3391    URI: *const c_char,
3392    enc: c_int,
3393) -> *mut _xmlParserInputBuffer {
3394    // SAFETY: URI must be a valid C string or NULL.
3395    if URI.is_null() {
3396        return ptr::null_mut();
3397    }
3398    crate::xml::parser::helpers::alloc_parser_input_buffer()
3399}
3400
3401/// Create an I/O parser input buffer.
3402///
3403/// # UPSTREAM-PARITY
3404///
3405/// ```c
3406/// xmlParserInputBufferPtr xmlParserInputBufferCreateIO(
3407///     xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
3408///     void *ioctx, int enc);
3409/// ```
3410#[no_mangle]
3411pub unsafe extern "C" fn xmlParserInputBufferCreateIO(
3412    ioread: Option<xmlInputReadCallback>,
3413    ioclose: Option<xmlInputCloseCallback>,
3414    ioctx: *mut c_void,
3415    enc: c_int,
3416) -> *mut _xmlParserInputBuffer {
3417    // SAFETY: ioread must be a valid callback if Some. ioctx may be NULL.
3418    let buf = crate::xml::parser::helpers::alloc_parser_input_buffer();
3419    if !buf.is_null() {
3420        (*buf).readcallback = ioread;
3421        (*buf).closecallback = ioclose;
3422        (*buf).context = ioctx;
3423    }
3424    buf
3425}
3426
3427/// Free a parser input buffer.
3428///
3429/// # UPSTREAM-PARITY
3430///
3431/// ```c
3432/// void xmlFreeParserInputBuffer(xmlParserInputBufferPtr buf);
3433/// ```
3434#[no_mangle]
3435pub unsafe extern "C" fn xmlFreeParserInputBuffer(buf: *mut _xmlParserInputBuffer) {
3436    if buf.is_null() {
3437        return;
3438    }
3439    crate::xml::parser::helpers::free_parser_input_buffer(buf);
3440}
3441
3442/// Create a new parser input.
3443///
3444/// # UPSTREAM-PARITY
3445///
3446/// ```c
3447/// xmlParserInputPtr xmlNewInputFromFile(xmlParserCtxtPtr ctxt, const char *filename);
3448/// ```
3449#[no_mangle]
3450pub unsafe extern "C" fn xmlNewInputFromFile(
3451    ctxt: *mut _xmlParserCtxt,
3452    filename: *const c_char,
3453) -> *mut _xmlParserInput {
3454    // SAFETY: filename must be a valid C string. ctxt may be NULL.
3455    // This function allocates a _xmlParserInput. The caller owns it.
3456    // Note: The InputBuffer backing data is NOT leaked here (no ctxt._private
3457    // to store it). Use xmlCreateFileParserCtxt + xmlParseDocument instead.
3458    if filename.is_null() {
3459        return ptr::null_mut();
3460    }
3461    crate::xml::parser::helpers::alloc_parser_input_buffer() as *mut _xmlParserInput
3462}
3463
3464/// Free a parser input.
3465///
3466/// # UPSTREAM-PARITY
3467///
3468/// ```c
3469/// void xmlFreeInputStream(xmlParserInputPtr input);
3470/// ```
3471#[no_mangle]
3472pub unsafe extern "C" fn xmlFreeInputStream(input: *mut _xmlParserInput) {
3473    if input.is_null() {
3474        return;
3475    }
3476    crate::xml::parser::helpers::free_parser_input(input);
3477}
3478
3479// ═══════════════════════════════════════════════════════════════════════════════
3480// 8. I/O
3481// ═══════════════════════════════════════════════════════════════════════════════
3482
3483/// Create an output buffer for a file.
3484///
3485/// # UPSTREAM-PARITY
3486///
3487/// ```c
3488/// xmlOutputBufferPtr xmlOutputBufferCreateFilename(const char *URI,
3489///                                                  xmlCharEncodingHandlerPtr encoder,
3490///                                                  int compression);
3491/// ```
3492#[no_mangle]
3493pub unsafe extern "C" fn xmlOutputBufferCreateFilename(
3494    URI: *const c_char,
3495    encoder: *mut c_void,
3496    compression: c_int,
3497) -> *mut _xmlOutputBuffer {
3498    let _ = compression;
3499    if URI.is_null() {
3500        return ptr::null_mut();
3501    }
3502    crate::xml::io::output_buffer_create_filename(
3503        URI,
3504        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3505        0,
3506    )
3507}
3508
3509/// Create an output buffer for a file descriptor.
3510///
3511/// # UPSTREAM-PARITY
3512///
3513/// ```c
3514/// xmlOutputBufferPtr xmlOutputBufferCreateFd(int fd,
3515///                                            xmlCharEncodingHandlerPtr encoder);
3516/// ```
3517#[no_mangle]
3518pub unsafe extern "C" fn xmlOutputBufferCreateFd(
3519    fd: c_int,
3520    encoder: *mut c_void,
3521) -> *mut _xmlOutputBuffer {
3522    if fd < 0 {
3523        return ptr::null_mut();
3524    }
3525    crate::xml::io::output_buffer_create_fd(
3526        fd,
3527        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3528    )
3529}
3530
3531/// Create an output buffer from I/O callbacks.
3532///
3533/// # UPSTREAM-PARITY
3534///
3535/// ```c
3536/// xmlOutputBufferPtr xmlOutputBufferCreateIO(
3537///     xmlOutputWriteCallback iowrite, xmlOutputCloseCallback ioclose,
3538///     void *ioctx, xmlCharEncodingHandlerPtr encoder);
3539/// ```
3540#[no_mangle]
3541pub unsafe extern "C" fn xmlOutputBufferCreateIO(
3542    iowrite: Option<xmlOutputWriteCallback>,
3543    ioclose: Option<xmlOutputCloseCallback>,
3544    ioctx: *mut c_void,
3545    encoder: *mut c_void,
3546) -> *mut _xmlOutputBuffer {
3547    crate::xml::io::output_buffer_create_io(
3548        iowrite,
3549        ioclose,
3550        ioctx,
3551        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3552    )
3553}
3554
3555/// Free an output buffer.
3556///
3557/// # UPSTREAM-PARITY
3558///
3559/// ```c
3560/// void xmlOutputBufferClose(xmlOutputBufferPtr out);
3561/// ```
3562#[no_mangle]
3563pub unsafe extern "C" fn xmlOutputBufferClose(out: *mut _xmlOutputBuffer) -> c_int {
3564    if out.is_null() {
3565        return -1;
3566    }
3567    crate::xml::io::output_buffer_close(out)
3568}
3569
3570/// Flush an output buffer.
3571///
3572/// # UPSTREAM-PARITY
3573///
3574/// ```c
3575/// int xmlOutputBufferFlush(xmlOutputBufferPtr out);
3576/// ```
3577#[no_mangle]
3578pub unsafe extern "C" fn xmlOutputBufferFlush(out: *mut _xmlOutputBuffer) -> c_int {
3579    if out.is_null() {
3580        return -1;
3581    }
3582    crate::xml::io::output_buffer_flush(out)
3583}
3584
3585/// Write to an output buffer.
3586///
3587/// # UPSTREAM-PARITY
3588///
3589/// ```c
3590/// int xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *data);
3591/// ```
3592#[no_mangle]
3593pub unsafe extern "C" fn xmlOutputBufferWrite(
3594    out: *mut _xmlOutputBuffer,
3595    len: c_int,
3596    data: *const c_char,
3597) -> c_int {
3598    if out.is_null() || data.is_null() || len <= 0 {
3599        return -1;
3600    }
3601    crate::xml::io::output_buffer_write(out, len, data)
3602}
3603
3604/// Write a string to an output buffer.
3605///
3606/// # UPSTREAM-PARITY
3607///
3608/// ```c
3609/// int xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str);
3610/// ```
3611#[no_mangle]
3612pub unsafe extern "C" fn xmlOutputBufferWriteString(
3613    out: *mut _xmlOutputBuffer,
3614    str: *const c_char,
3615) -> c_int {
3616    if str.is_null() {
3617        return 0;
3618    }
3619    unsafe { xmlOutputBufferWrite(out, xmlStrlen(str as *const xmlChar), str) }
3620}
3621
3622/// Allocate an output buffer with no I/O target (upstream xmlAllocOutputBuffer).
3623///
3624/// # UPSTREAM-PARITY
3625///
3626/// ```c
3627/// xmlOutputBufferPtr xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder);
3628/// ```
3629#[no_mangle]
3630pub unsafe extern "C" fn xmlAllocOutputBuffer(encoder: *mut c_void) -> *mut _xmlOutputBuffer {
3631    crate::xml::io::output_buffer_create(
3632        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3633    )
3634}
3635
3636/// Create an output buffer that writes into a `_xmlBuffer` (upstream
3637/// xmlOutputBufferCreateBuffer).
3638///
3639/// # UPSTREAM-PARITY
3640///
3641/// ```c
3642/// xmlOutputBufferPtr xmlOutputBufferCreateBuffer(xmlBufferPtr buffer,
3643///                                                xmlCharEncodingHandlerPtr encoder);
3644/// ```
3645///
3646/// # SAFETY
3647///
3648/// - `buffer` must be a valid `_xmlBuffer`.
3649#[no_mangle]
3650pub unsafe extern "C" fn xmlOutputBufferCreateBuffer(
3651    buffer: *mut crate::abi::structs::_xmlBuffer,
3652    encoder: *mut c_void,
3653) -> *mut _xmlOutputBuffer {
3654    crate::xml::io::output_buffer_create_buffer(
3655        buffer,
3656        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3657    )
3658}
3659
3660/// Create an output buffer writing to a `FILE *` (upstream
3661/// xmlOutputBufferCreateFile): the FILE is the I/O context with a
3662/// write callback wrapping `fwrite` and a close callback wrapping `fflush`.
3663///
3664/// # SAFETY
3665///
3666/// - `file` must be a valid `FILE *` or NULL.
3667#[no_mangle]
3668pub unsafe extern "C" fn xmlOutputBufferCreateFile(
3669    file: *mut libc::FILE,
3670    encoder: *mut c_void,
3671) -> *mut _xmlOutputBuffer {
3672    crate::xml::io::output_buffer_create_file(
3673        file,
3674        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3675    )
3676}
3677
3678/// Get the current content of an output buffer (upstream xmlOutputBufferGetContent).
3679///
3680/// # SAFETY
3681///
3682/// - `out` must be a valid output buffer.
3683#[no_mangle]
3684pub unsafe extern "C" fn xmlOutputBufferGetContent(out: *mut _xmlOutputBuffer) -> *const c_char {
3685    crate::xml::io::output_buffer_get_content(out) as *const c_char
3686}
3687
3688/// Get the number of bytes currently in the output buffer (upstream
3689/// xmlOutputBufferGetSize).
3690///
3691/// # SAFETY
3692///
3693/// - `out` must be a valid output buffer.
3694#[no_mangle]
3695pub unsafe extern "C" fn xmlOutputBufferGetSize(out: *mut _xmlOutputBuffer) -> c_int {
3696    crate::xml::io::output_buffer_get_size(out)
3697}
3698
3699/// Write to an output buffer, escaping special characters with the given
3700/// escape function (upstream xmlOutputBufferWriteEscape).
3701///
3702/// # SAFETY
3703///
3704/// - `out` must be a valid output buffer; `str` a NUL-terminated string;
3705///   `escaping` a valid escape callback or NULL.
3706#[no_mangle]
3707pub unsafe extern "C" fn xmlOutputBufferWriteEscape(
3708    out: *mut _xmlOutputBuffer,
3709    str: *const xmlChar,
3710    escaping: Option<xmlCharEncodingOutputFunc>,
3711) -> c_int {
3712    if out.is_null() || str.is_null() {
3713        return -1;
3714    }
3715    crate::xml::io::output_buffer_write_escape(out, str, escaping)
3716}
3717
3718/// Global default `xmlOutputBufferCreateFilename` callback
3719/// (upstream xmlOutputBufferCreateFilenameDefault).
3720static mut OUTPUT_CREATE_FILENAME_DEFAULT: Option<
3721    unsafe extern "C" fn(
3722        *const c_char,
3723        *mut crate::abi::structs::_xmlCharEncodingHandler,
3724        c_int,
3725    ) -> *mut _xmlOutputBuffer,
3726> = None;
3727
3728/// Set/query the default output-buffer filename callback
3729/// (upstream xmlOutputBufferCreateFilenameDefault).
3730///
3731/// # SAFETY
3732///
3733/// - `func` must be a valid function pointer or NULL.
3734#[no_mangle]
3735pub unsafe extern "C" fn xmlOutputBufferCreateFilenameDefault(
3736    func: Option<
3737        unsafe extern "C" fn(
3738            *const c_char,
3739            *mut crate::abi::structs::_xmlCharEncodingHandler,
3740            c_int,
3741        ) -> *mut _xmlOutputBuffer,
3742    >,
3743) -> Option<
3744    unsafe extern "C" fn(
3745        *const c_char,
3746        *mut crate::abi::structs::_xmlCharEncodingHandler,
3747        c_int,
3748    ) -> *mut _xmlOutputBuffer,
3749> {
3750    let old = unsafe { OUTPUT_CREATE_FILENAME_DEFAULT };
3751    if func.is_some() {
3752        unsafe { OUTPUT_CREATE_FILENAME_DEFAULT = func };
3753    }
3754    old
3755}
3756
3757/// `__xmlOutputBufferCreateFilename` — accessor returning a pointer to the
3758/// default callback (upstream xmlIO.c).
3759#[no_mangle]
3760pub unsafe extern "C" fn __xmlOutputBufferCreateFilename() -> *mut Option<
3761    unsafe extern "C" fn(
3762        *const c_char,
3763        *mut crate::abi::structs::_xmlCharEncodingHandler,
3764        c_int,
3765    ) -> *mut _xmlOutputBuffer,
3766> {
3767    unsafe { core::ptr::addr_of_mut!(OUTPUT_CREATE_FILENAME_DEFAULT) }
3768}
3769
3770// ═══════════════════════════════════════════════════════════════════════════════
3771// 9. Dictionary
3772// ═══════════════════════════════════════════════════════════════════════════════
3773
3774/// Create a new dictionary.
3775///
3776/// # UPSTREAM-PARITY
3777///
3778/// ```c
3779/// xmlDictPtr xmlDictCreate(void);
3780/// ```
3781#[no_mangle]
3782pub extern "C" fn xmlDictCreate() -> *mut c_void {
3783    let d = unsafe { crate::xml::dictionary::dict_create() as *mut c_void };
3784    if !d.is_null() {
3785        // UPSTREAM-PARITY: the creator holds the base reference (count 1);
3786        // xmlDictReference adds to it and xmlDictFree decrements, freeing
3787        // the dictionary when it reaches zero.
3788        *crate::abi::exports_hash::DICT_REFS
3789            .lock()
3790            .entry(d as usize)
3791            .or_insert(0) = 1;
3792    }
3793    d
3794}
3795
3796/// Create a sub-dictionary.
3797///
3798/// # UPSTREAM-PARITY
3799///
3800/// ```c
3801/// xmlDictPtr xmlDictCreateSub(xmlDictPtr sub);
3802/// ```
3803#[no_mangle]
3804pub extern "C" fn xmlDictCreateSub(sub: *mut c_void) -> *mut c_void {
3805    let d = unsafe {
3806        crate::xml::dictionary::dict_create_sub(sub as *mut crate::xml::dictionary::Dict)
3807            as *mut c_void
3808    };
3809    if !d.is_null() {
3810        *crate::abi::exports_hash::DICT_REFS
3811            .lock()
3812            .entry(d as usize)
3813            .or_insert(0) = 1;
3814    }
3815    d
3816}
3817
3818/// Look up a string in the dictionary.
3819///
3820/// # UPSTREAM-PARITY
3821///
3822/// ```c
3823/// const xmlChar *xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len);
3824/// ```
3825///
3826/// Returns an interned string pointer (valid as long as the dictionary exists).
3827/// - If `len` < 0, `name` must be null-terminated.
3828/// - If `len` >= 0, exactly `len` bytes are used.
3829#[no_mangle]
3830pub unsafe extern "C" fn xmlDictLookup(
3831    dict: *mut c_void,
3832    name: *const xmlChar,
3833    len: c_int,
3834) -> *const xmlChar {
3835    unsafe {
3836        crate::xml::dictionary::dict_lookup(dict as *mut crate::xml::dictionary::Dict, name, len)
3837    }
3838}
3839
3840/// Check if a string exists in the dictionary.
3841///
3842/// # UPSTREAM-PARITY
3843///
3844/// ```c
3845/// const xmlChar *xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len);
3846/// ```
3847#[no_mangle]
3848pub unsafe extern "C" fn xmlDictExists(
3849    dict: *mut c_void,
3850    name: *const xmlChar,
3851    len: c_int,
3852) -> *const xmlChar {
3853    unsafe {
3854        crate::xml::dictionary::dict_exists(dict as *mut crate::xml::dictionary::Dict, name, len)
3855    }
3856}
3857
3858/// Query dictionary size.
3859///
3860/// # UPSTREAM-PARITY
3861///
3862/// ```c
3863/// unsigned int xmlDictSize(const xmlDictPtr dict);
3864/// ```
3865#[no_mangle]
3866pub extern "C" fn xmlDictSize(dict: *const c_void) -> c_uint {
3867    unsafe {
3868        crate::xml::dictionary::dict_size(dict as *const crate::xml::dictionary::Dict) as c_uint
3869    }
3870}
3871
3872/// Free a dictionary.
3873///
3874/// # UPSTREAM-PARITY
3875///
3876/// ```c
3877/// void xmlDictFree(xmlDictPtr dict);
3878/// ```
3879///
3880/// The reference counter added by `xmlDictReference` is honored: the
3881/// underlying dictionary is destroyed only when the last reference is
3882/// released (the base owner counts as one implicit reference).
3883#[no_mangle]
3884pub extern "C" fn xmlDictFree(dict: *mut c_void) {
3885    if dict.is_null() {
3886        return;
3887    }
3888    let mut remaining = 0u32;
3889    {
3890        let mut refs = crate::abi::exports_hash::DICT_REFS.lock();
3891        if let Some(r) = refs.get_mut(&(dict as usize)) {
3892            if *r > 0 {
3893                *r -= 1;
3894            }
3895            remaining = *r;
3896            if remaining == 0 {
3897                refs.remove(&(dict as usize));
3898            }
3899        }
3900    }
3901    if remaining == 0 {
3902        unsafe { crate::xml::dictionary::dict_free(dict as *mut crate::xml::dictionary::Dict) };
3903    }
3904}
3905
3906/// Set the dictionary size limit.
3907///
3908/// # UPSTREAM-PARITY
3909///
3910/// ```c
3911/// unsigned int xmlDictSetLimit(xmlDictPtr dict, unsigned int limit);
3912/// ```
3913#[no_mangle]
3914pub extern "C" fn xmlDictSetLimit(dict: *mut c_void, limit: c_uint) -> c_uint {
3915    unsafe {
3916        crate::xml::dictionary::dict_set_limit(
3917            dict as *mut crate::xml::dictionary::Dict,
3918            limit as usize,
3919        ) as c_uint
3920    }
3921}
3922
3923/// Get current dictionary usage.
3924///
3925/// # UPSTREAM-PARITY
3926///
3927/// ```c
3928/// unsigned int xmlDictGetUsage(const xmlDictPtr dict);
3929/// ```
3930#[no_mangle]
3931pub extern "C" fn xmlDictGetUsage(dict: *const c_void) -> c_uint {
3932    unsafe {
3933        crate::xml::dictionary::dict_get_usage(dict as *mut crate::xml::dictionary::Dict) as c_uint
3934    }
3935}
3936
3937// ═══════════════════════════════════════════════════════════════════════════════
3938// 10. Hash Table
3939// ═══════════════════════════════════════════════════════════════════════════════
3940
3941/// Create a new hash table.
3942///
3943/// # UPSTREAM-PARITY
3944///
3945/// ```c
3946/// xmlHashTablePtr xmlHashCreate(int size);
3947/// ```
3948#[no_mangle]
3949pub extern "C" fn xmlHashCreate(size: c_int) -> *mut c_void {
3950    unsafe { crate::xml::hash::hash_create(size) as *mut c_void }
3951}
3952
3953/// Create a new hash table with a dictionary.
3954///
3955/// # UPSTREAM-PARITY
3956///
3957/// ```c
3958/// xmlHashTablePtr xmlHashCreateDict(int size, xmlDictPtr dict);
3959/// ```
3960#[no_mangle]
3961pub extern "C" fn xmlHashCreateDict(size: c_int, dict: *mut c_void) -> *mut c_void {
3962    unsafe { crate::xml::hash::hash_create_dict(size, dict) as *mut c_void }
3963}
3964
3965/// Free a hash table.
3966///
3967/// # UPSTREAM-PARITY
3968///
3969/// ```c
3970/// void xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f);
3971/// ```
3972#[no_mangle]
3973pub extern "C" fn xmlHashFree(
3974    table: *mut c_void,
3975    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
3976) {
3977    unsafe { crate::xml::hash::hash_free(table as *mut crate::xml::hash::HashTable, f) }
3978}
3979
3980/// Add an entry to a hash table.
3981///
3982/// # UPSTREAM-PARITY
3983///
3984/// ```c
3985/// int xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata);
3986/// ```
3987#[no_mangle]
3988pub unsafe extern "C" fn xmlHashAddEntry(
3989    table: *mut c_void,
3990    name: *const xmlChar,
3991    userdata: *mut c_void,
3992) -> c_int {
3993    unsafe {
3994        crate::xml::hash::hash_add_entry(table as *mut crate::xml::hash::HashTable, name, userdata)
3995    }
3996}
3997
3998/// Add a 2-key entry.
3999///
4000/// # UPSTREAM-PARITY
4001///
4002/// ```c
4003/// int xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
4004///                      const xmlChar *name2, void *userdata);
4005/// ```
4006#[no_mangle]
4007pub unsafe extern "C" fn xmlHashAddEntry2(
4008    table: *mut c_void,
4009    name: *const xmlChar,
4010    name2: *const xmlChar,
4011    userdata: *mut c_void,
4012) -> c_int {
4013    unsafe {
4014        crate::xml::hash::hash_add_entry2(
4015            table as *mut crate::xml::hash::HashTable,
4016            name,
4017            name2,
4018            userdata,
4019        )
4020    }
4021}
4022
4023/// Add a 3-key entry.
4024///
4025/// # UPSTREAM-PARITY
4026///
4027/// ```c
4028/// int xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
4029///                      const xmlChar *name2, const xmlChar *name3, void *userdata);
4030/// ```
4031#[no_mangle]
4032pub unsafe extern "C" fn xmlHashAddEntry3(
4033    table: *mut c_void,
4034    name: *const xmlChar,
4035    name2: *const xmlChar,
4036    name3: *const xmlChar,
4037    userdata: *mut c_void,
4038) -> c_int {
4039    unsafe {
4040        crate::xml::hash::hash_add_entry3(
4041            table as *mut crate::xml::hash::HashTable,
4042            name,
4043            name2,
4044            name3,
4045            userdata,
4046        )
4047    }
4048}
4049
4050/// Update or add an entry.
4051///
4052/// # UPSTREAM-PARITY
4053///
4054/// ```c
4055/// int xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
4056///                        void *userdata, xmlHashDeallocator f);
4057/// ```
4058#[no_mangle]
4059pub unsafe extern "C" fn xmlHashUpdateEntry(
4060    table: *mut c_void,
4061    name: *const xmlChar,
4062    userdata: *mut c_void,
4063    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4064) -> c_int {
4065    unsafe {
4066        crate::xml::hash::hash_update_entry(
4067            table as *mut crate::xml::hash::HashTable,
4068            name,
4069            userdata,
4070            f,
4071        )
4072    }
4073}
4074
4075/// Update or add a 2-key entry.
4076#[no_mangle]
4077pub unsafe extern "C" fn xmlHashUpdateEntry2(
4078    table: *mut c_void,
4079    name: *const xmlChar,
4080    name2: *const xmlChar,
4081    userdata: *mut c_void,
4082    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4083) -> c_int {
4084    unsafe {
4085        crate::xml::hash::hash_update_entry2(
4086            table as *mut crate::xml::hash::HashTable,
4087            name,
4088            name2,
4089            userdata,
4090            f,
4091        )
4092    }
4093}
4094
4095/// Update or add a 3-key entry.
4096#[no_mangle]
4097pub unsafe extern "C" fn xmlHashUpdateEntry3(
4098    table: *mut c_void,
4099    name: *const xmlChar,
4100    name2: *const xmlChar,
4101    name3: *const xmlChar,
4102    userdata: *mut c_void,
4103    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4104) -> c_int {
4105    unsafe {
4106        crate::xml::hash::hash_update_entry3(
4107            table as *mut crate::xml::hash::HashTable,
4108            name,
4109            name2,
4110            name3,
4111            userdata,
4112            f,
4113        )
4114    }
4115}
4116
4117/// Look up an entry.
4118///
4119/// # UPSTREAM-PARITY
4120///
4121/// ```c
4122/// void *xmlHashLookup(xmlHashTablePtr table, const xmlChar *name);
4123/// ```
4124#[no_mangle]
4125pub unsafe extern "C" fn xmlHashLookup(table: *mut c_void, name: *const xmlChar) -> *mut c_void {
4126    unsafe { crate::xml::hash::hash_lookup(table as *mut crate::xml::hash::HashTable, name) }
4127}
4128
4129/// Look up a 2-key entry.
4130#[no_mangle]
4131pub unsafe extern "C" fn xmlHashLookup2(
4132    table: *mut c_void,
4133    name: *const xmlChar,
4134    name2: *const xmlChar,
4135) -> *mut c_void {
4136    unsafe {
4137        crate::xml::hash::hash_lookup2(table as *mut crate::xml::hash::HashTable, name, name2)
4138    }
4139}
4140
4141/// Look up a 3-key entry.
4142#[no_mangle]
4143pub unsafe extern "C" fn xmlHashLookup3(
4144    table: *mut c_void,
4145    name: *const xmlChar,
4146    name2: *const xmlChar,
4147    name3: *const xmlChar,
4148) -> *mut c_void {
4149    unsafe {
4150        crate::xml::hash::hash_lookup3(
4151            table as *mut crate::xml::hash::HashTable,
4152            name,
4153            name2,
4154            name3,
4155        )
4156    }
4157}
4158
4159/// Get the size of a hash table.
4160///
4161/// # UPSTREAM-PARITY
4162///
4163/// ```c
4164/// int xmlHashSize(xmlHashTablePtr table);
4165/// ```
4166#[no_mangle]
4167pub extern "C" fn xmlHashSize(table: *mut c_void) -> c_int {
4168    unsafe { crate::xml::hash::hash_size(table as *mut crate::xml::hash::HashTable) }
4169}
4170
4171/// Remove an entry.
4172///
4173/// # UPSTREAM-PARITY
4174///
4175/// ```c
4176/// int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
4177///                        xmlHashDeallocator f);
4178/// ```
4179#[no_mangle]
4180pub unsafe extern "C" fn xmlHashRemoveEntry(
4181    table: *mut c_void,
4182    name: *const xmlChar,
4183    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4184) -> c_int {
4185    unsafe {
4186        crate::xml::hash::hash_remove_entry(table as *mut crate::xml::hash::HashTable, name, f)
4187    }
4188}
4189
4190/// Remove a 2-key entry.
4191#[no_mangle]
4192pub unsafe extern "C" fn xmlHashRemoveEntry2(
4193    table: *mut c_void,
4194    name: *const xmlChar,
4195    name2: *const xmlChar,
4196    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4197) -> c_int {
4198    unsafe {
4199        crate::xml::hash::hash_remove_entry2(
4200            table as *mut crate::xml::hash::HashTable,
4201            name,
4202            name2,
4203            f,
4204        )
4205    }
4206}
4207
4208/// Remove a 3-key entry.
4209#[no_mangle]
4210pub unsafe extern "C" fn xmlHashRemoveEntry3(
4211    table: *mut c_void,
4212    name: *const xmlChar,
4213    name2: *const xmlChar,
4214    name3: *const xmlChar,
4215    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4216) -> c_int {
4217    unsafe {
4218        crate::xml::hash::hash_remove_entry3(
4219            table as *mut crate::xml::hash::HashTable,
4220            name,
4221            name2,
4222            name3,
4223            f,
4224        )
4225    }
4226}
4227
4228/// Scan a hash table with a scanner function.
4229///
4230/// # UPSTREAM-PARITY
4231///
4232/// ```c
4233/// void xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data);
4234/// ```
4235#[no_mangle]
4236pub extern "C" fn xmlHashScan(table: *mut c_void, f: Option<xmlHashScanner>, data: *mut c_void) {
4237    unsafe { crate::xml::hash::hash_scan(table as *mut crate::xml::hash::HashTable, f, data) }
4238}
4239
4240/// Scan a hash table with a full scanner function.
4241#[no_mangle]
4242pub extern "C" fn xmlHashScanFull(
4243    table: *mut c_void,
4244    f: Option<xmlHashScannerFull>,
4245    data: *mut c_void,
4246) {
4247    unsafe { crate::xml::hash::hash_scan_full(table as *mut crate::xml::hash::HashTable, f, data) }
4248}
4249
4250/// Copy a hash table.
4251///
4252/// # UPSTREAM-PARITY
4253///
4254/// ```c
4255/// xmlHashTablePtr xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f);
4256/// ```
4257#[no_mangle]
4258pub extern "C" fn xmlHashCopy(
4259    table: *mut c_void,
4260    f: Option<unsafe extern "C" fn(*mut c_void, *const xmlChar) -> *mut c_void>,
4261) -> *mut c_void {
4262    unsafe {
4263        crate::xml::hash::hash_copy(table as *mut crate::xml::hash::HashTable, f) as *mut c_void
4264    }
4265}
4266
4267// ═══════════════════════════════════════════════════════════════════════════════
4268// 11. List
4269// ═══════════════════════════════════════════════════════════════════════════════
4270
4271/// Create a new list.
4272///
4273/// # UPSTREAM-PARITY
4274///
4275/// ```c
4276/// xmlListPtr xmlListCreate(xmlListDeallocator deallocator,
4277///                          xmlListDataCompare compare);
4278/// ```
4279#[no_mangle]
4280pub extern "C" fn xmlListCreate(
4281    deallocator: Option<unsafe extern "C" fn(*mut c_void)>,
4282    compare: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
4283) -> *mut c_void {
4284    unsafe { crate::xml::list::list_create(deallocator, compare) as *mut c_void }
4285}
4286
4287/// Delete a list.
4288///
4289/// # UPSTREAM-PARITY
4290///
4291/// ```c
4292/// void xmlListDelete(xmlListPtr list);
4293/// ```
4294#[no_mangle]
4295pub extern "C" fn xmlListDelete(list: *mut c_void) {
4296    unsafe { crate::xml::list::list_delete(list as *mut crate::xml::list::List) }
4297}
4298
4299/// Search a list.
4300///
4301/// # UPSTREAM-PARITY
4302///
4303/// ```c
4304/// void *xmlListSearch(xmlListPtr list, void *data);
4305/// ```
4306#[no_mangle]
4307pub extern "C" fn xmlListSearch(list: *mut c_void, data: *mut c_void) -> *mut c_void {
4308    unsafe { crate::xml::list::list_search(list as *mut crate::xml::list::List, data) }
4309}
4310
4311/// Walk a list.
4312///
4313/// # UPSTREAM-PARITY
4314///
4315/// ```c
4316/// void xmlListWalk(xmlListPtr list, xmlListWalker walker, void *data);
4317/// ```
4318#[no_mangle]
4319pub extern "C" fn xmlListWalk(
4320    list: *mut c_void,
4321    walker: Option<unsafe extern "C" fn(*mut c_void, *mut c_void) -> c_int>,
4322    data: *mut c_void,
4323) {
4324    unsafe { crate::xml::list::list_walk(list as *mut crate::xml::list::List, walker, data) }
4325}
4326
4327/// Push to back.
4328///
4329/// # UPSTREAM-PARITY
4330///
4331/// ```c
4332/// int xmlListPushBack(xmlListPtr list, void *data);
4333/// ```
4334#[no_mangle]
4335pub extern "C" fn xmlListPushBack(list: *mut c_void, data: *mut c_void) -> c_int {
4336    unsafe { crate::xml::list::list_push_back(list as *mut crate::xml::list::List, data) }
4337}
4338
4339/// Push to front.
4340///
4341/// # UPSTREAM-PARITY
4342///
4343/// ```c
4344/// int xmlListPushFront(xmlListPtr list, void *data);
4345/// ```
4346#[no_mangle]
4347pub extern "C" fn xmlListPushFront(list: *mut c_void, data: *mut c_void) -> c_int {
4348    unsafe { crate::xml::list::list_push_front(list as *mut crate::xml::list::List, data) }
4349}
4350
4351/// Pop from back.
4352#[no_mangle]
4353pub extern "C" fn xmlListPopBack(list: *mut c_void) {
4354    unsafe { crate::xml::list::list_pop_back(list as *mut crate::xml::list::List) }
4355}
4356
4357/// Pop from front.
4358#[no_mangle]
4359pub extern "C" fn xmlListPopFront(list: *mut c_void) {
4360    unsafe { crate::xml::list::list_pop_front(list as *mut crate::xml::list::List) }
4361}
4362
4363/// Insert into sorted list.
4364///
4365/// # UPSTREAM-PARITY
4366///
4367/// ```c
4368/// int xmlListInsert(xmlListPtr list, void *data);
4369/// ```
4370#[no_mangle]
4371pub extern "C" fn xmlListInsert(list: *mut c_void, data: *mut c_void) -> c_int {
4372    unsafe { crate::xml::list::list_insert(list as *mut crate::xml::list::List, data) }
4373}
4374
4375/// Append to list.
4376#[no_mangle]
4377pub extern "C" fn xmlListAppend(list: *mut c_void, data: *mut c_void) -> c_int {
4378    unsafe { crate::xml::list::list_append(list as *mut crate::xml::list::List, data) }
4379}
4380
4381/// Remove first matching element.
4382#[no_mangle]
4383pub extern "C" fn xmlListRemoveFirst(list: *mut c_void, data: *mut c_void) -> c_int {
4384    unsafe { crate::xml::list::list_remove_first(list as *mut crate::xml::list::List, data) }
4385}
4386
4387/// Remove last matching element.
4388#[no_mangle]
4389pub extern "C" fn xmlListRemoveLast(list: *mut c_void, data: *mut c_void) -> c_int {
4390    unsafe { crate::xml::list::list_remove_last(list as *mut crate::xml::list::List, data) }
4391}
4392
4393/// Remove all matching elements.
4394#[no_mangle]
4395pub extern "C" fn xmlListRemoveAll(list: *mut c_void, data: *mut c_void) -> c_int {
4396    unsafe { crate::xml::list::list_remove_all(list as *mut crate::xml::list::List, data) }
4397}
4398
4399/// Clear a list.
4400#[no_mangle]
4401pub extern "C" fn xmlListClear(list: *mut c_void) {
4402    unsafe { crate::xml::list::list_clear(list as *mut crate::xml::list::List) }
4403}
4404
4405/// Check if list is empty.
4406///
4407/// # UPSTREAM-PARITY
4408///
4409/// ```c
4410/// int xmlListEmpty(xmlListPtr list);
4411/// ```
4412#[no_mangle]
4413pub extern "C" fn xmlListEmpty(list: *mut c_void) -> c_int {
4414    unsafe { crate::xml::list::list_empty(list as *mut crate::xml::list::List) }
4415}
4416
4417/// Get front element.
4418///
4419/// # UPSTREAM-PARITY
4420///
4421/// ```c
4422/// void *xmlListFront(xmlListPtr list);
4423/// ```
4424#[no_mangle]
4425pub extern "C" fn xmlListFront(list: *mut c_void) -> *mut c_void {
4426    unsafe { crate::xml::list::list_front(list as *mut crate::xml::list::List) }
4427}
4428
4429/// Get back element.
4430///
4431/// # UPSTREAM-PARITY
4432///
4433/// ```c
4434/// void *xmlListBack(xmlListPtr list);
4435/// ```
4436#[no_mangle]
4437pub extern "C" fn xmlListBack(list: *mut c_void) -> *mut c_void {
4438    unsafe { crate::xml::list::list_back(list as *mut crate::xml::list::List) }
4439}
4440
4441/// Get list size.
4442///
4443/// # UPSTREAM-PARITY
4444///
4445/// ```c
4446/// int xmlListSize(xmlListPtr list);
4447/// ```
4448#[no_mangle]
4449pub extern "C" fn xmlListSize(list: *mut c_void) -> c_int {
4450    unsafe { crate::xml::list::list_size(list as *mut crate::xml::list::List) }
4451}
4452
4453/// Sort a list.
4454#[no_mangle]
4455pub extern "C" fn xmlListSort(list: *mut c_void) {
4456    unsafe { crate::xml::list::list_sort(list as *mut crate::xml::list::List) }
4457}
4458
4459/// Reverse a list.
4460#[no_mangle]
4461pub extern "C" fn xmlListReverse(list: *mut c_void) {
4462    unsafe { crate::xml::list::list_reverse(list as *mut crate::xml::list::List) }
4463}
4464
4465/// Reverse a list in-place.
4466#[no_mangle]
4467pub extern "C" fn xmlListReverseSplice(list: *mut c_void, list2: *mut c_void) {
4468    unsafe {
4469        crate::xml::list::list_reverse_splice(
4470            list as *mut crate::xml::list::List,
4471            list2 as *mut crate::xml::list::List,
4472        )
4473    }
4474}
4475
4476/// Merge two sorted lists.
4477#[no_mangle]
4478pub extern "C" fn xmlListMerge(list: *mut c_void, list2: *mut c_void) {
4479    unsafe {
4480        crate::xml::list::list_merge(
4481            list as *mut crate::xml::list::List,
4482            list2 as *mut crate::xml::list::List,
4483        )
4484    }
4485}
4486/// Return the last element of a list (upstream list.h).
4487///
4488/// # UPSTREAM-PARITY
4489///
4490/// ```c
4491/// void *xmlListEnd(xmlListPtr l);
4492/// ```
4493#[no_mangle]
4494pub unsafe extern "C" fn xmlListEnd(l: *mut c_void) -> *mut c_void {
4495    crate::xml::list::list_end(l as *mut crate::xml::list::List)
4496}
4497
4498/// Reverse-search a list (upstream list.h).
4499///
4500/// # UPSTREAM-PARITY
4501///
4502/// ```c
4503/// void *xmlListReverseSearch(xmlListPtr l, void *data);
4504/// ```
4505#[no_mangle]
4506pub unsafe extern "C" fn xmlListReverseSearch(l: *mut c_void, data: *mut c_void) -> *mut c_void {
4507    crate::xml::list::list_reverse_search(l as *mut crate::xml::list::List, data)
4508}
4509
4510/// Walk a list in reverse (upstream list.h).
4511///
4512/// # UPSTREAM-PARITY
4513///
4514/// ```c
4515/// void xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, void *data);
4516/// ```
4517#[no_mangle]
4518pub unsafe extern "C" fn xmlListReverseWalk(
4519    l: *mut c_void,
4520    walker: Option<unsafe extern "C" fn(*mut c_void, *mut c_void) -> c_int>,
4521    data: *mut c_void,
4522) {
4523    crate::xml::list::list_reverse_walk(l as *mut crate::xml::list::List, walker, data)
4524}
4525
4526/// Duplicate a list (upstream list.h).
4527///
4528/// # UPSTREAM-PARITY
4529///
4530/// ```c
4531/// xmlListPtr xmlListDup(xmlListPtr l);
4532/// ```
4533#[no_mangle]
4534pub unsafe extern "C" fn xmlListDup(l: *mut c_void) -> *mut c_void {
4535    crate::xml::list::list_dup(l as *mut crate::xml::list::List) as *mut c_void
4536}
4537
4538/// Copy a list with a copier (upstream list.h).
4539///
4540/// # UPSTREAM-PARITY
4541///
4542/// ```c
4543/// int xmlListCopy(xmlListPtr l, xmlListCopier copier);
4544/// ```
4545#[no_mangle]
4546pub unsafe extern "C" fn xmlListCopy(
4547    l: *mut c_void,
4548    copier: Option<unsafe extern "C" fn(*mut c_void) -> *mut c_void>,
4549) -> c_int {
4550    crate::xml::list::list_copy(l as *mut crate::xml::list::List, copier)
4551}
4552
4553/// Return the data of a link (upstream list.h).
4554///
4555/// # UPSTREAM-PARITY
4556///
4557/// ```c
4558/// void *xmlLinkGetData(xmlLinkPtr lk);
4559/// ```
4560#[no_mangle]
4561pub unsafe extern "C" fn xmlLinkGetData(lk: *mut c_void) -> *mut c_void {
4562    crate::xml::list::link_get_data(lk)
4563}
4564
4565// ═══════════════════════════════════════════════════════════════════════════════
4566// 12. Buffer
4567// ═══════════════════════════════════════════════════════════════════════════════
4568
4569/// Create a new buffer.
4570///
4571/// # UPSTREAM-PARITY
4572///
4573/// ```c
4574/// xmlBufferPtr xmlBufferCreate(void);
4575/// ```
4576#[no_mangle]
4577pub extern "C" fn xmlBufferCreate() -> *mut _xmlBuffer {
4578    crate::xml::io::buf_create(-1)
4579}
4580
4581/// Create a new buffer of a given size.
4582///
4583/// # UPSTREAM-PARITY
4584///
4585/// ```c
4586/// xmlBufferPtr xmlBufferCreateSize(size_t size);
4587/// ```
4588#[no_mangle]
4589pub extern "C" fn xmlBufferCreateSize(size: usize) -> *mut _xmlBuffer {
4590    crate::xml::io::buf_create(size as c_int)
4591}
4592
4593/// Create a buffer from a static string.
4594///
4595/// # UPSTREAM-PARITY
4596///
4597/// ```c
4598/// xmlBufferPtr xmlBufferCreateStatic(void *mem, size_t size);
4599/// ```
4600#[no_mangle]
4601pub extern "C" fn xmlBufferCreateStatic(mem: *mut c_void, size: usize) -> *mut _xmlBuffer {
4602    if mem.is_null() || size == 0 {
4603        return ptr::null_mut();
4604    }
4605    crate::xml::io::buf_create_static(mem as *const xmlChar, size as c_int)
4606}
4607
4608/// Free a buffer.
4609///
4610/// # UPSTREAM-PARITY
4611///
4612/// ```c
4613/// void xmlBufferFree(xmlBufferPtr buf);
4614/// ```
4615#[no_mangle]
4616pub extern "C" fn xmlBufferFree(buf: *mut _xmlBuffer) {
4617    crate::xml::io::buf_free(buf)
4618}
4619
4620/// Empty a buffer.
4621///
4622/// # UPSTREAM-PARITY
4623///
4624/// ```c
4625/// void xmlBufferEmpty(xmlBufferPtr buf);
4626/// ```
4627#[no_mangle]
4628pub extern "C" fn xmlBufferEmpty(buf: *mut _xmlBuffer) {
4629    if buf.is_null() {
4630        return;
4631    }
4632    unsafe {
4633        if !(*buf).content.is_null() {
4634            *(*buf).content = 0;
4635        }
4636        (*buf).use_ = 0;
4637    }
4638}
4639
4640/// Get buffer content.
4641///
4642/// # UPSTREAM-PARITY
4643///
4644/// ```c
4645/// xmlChar *xmlBufferContent(const xmlBuffer *buf);
4646/// ```
4647#[no_mangle]
4648pub extern "C" fn xmlBufferContent(buf: *const _xmlBuffer) -> *mut xmlChar {
4649    crate::xml::io::buf_content(buf as *mut _xmlBuffer)
4650}
4651
4652/// Get buffer length.
4653///
4654/// # UPSTREAM-PARITY
4655///
4656/// ```c
4657/// int xmlBufferLength(const xmlBuffer *buf);
4658/// ```
4659#[no_mangle]
4660pub extern "C" fn xmlBufferLength(buf: *const _xmlBuffer) -> c_int {
4661    crate::xml::io::buf_length(buf as *mut _xmlBuffer)
4662}
4663
4664/// Write to a buffer.
4665///
4666/// # UPSTREAM-PARITY
4667///
4668/// ```c
4669/// int xmlBufferAdd(xmlBufferPtr buf, const xmlChar *str, int len);
4670/// ```
4671#[no_mangle]
4672pub unsafe extern "C" fn xmlBufferAdd(
4673    buf: *mut _xmlBuffer,
4674    str: *const xmlChar,
4675    len: c_int,
4676) -> c_int {
4677    crate::xml::io::buf_add(buf, str, len)
4678}
4679
4680/// Write to a buffer at a position.
4681///
4682/// # UPSTREAM-PARITY
4683///
4684/// ```c
4685/// int xmlBufferAddHead(xmlBufferPtr buf, const xmlChar *str, int len);
4686/// ```
4687#[no_mangle]
4688pub unsafe extern "C" fn xmlBufferAddHead(
4689    buf: *mut _xmlBuffer,
4690    str: *const xmlChar,
4691    len: c_int,
4692) -> c_int {
4693    crate::xml::io::buf_add_head(buf, str, len)
4694}
4695
4696/// Write a C string to a buffer.
4697///
4698/// # UPSTREAM-PARITY
4699///
4700/// ```c
4701/// int xmlBufferCat(xmlBufferPtr buf, const xmlChar *str);
4702/// ```
4703#[no_mangle]
4704pub unsafe extern "C" fn xmlBufferCat(buf: *mut _xmlBuffer, str: *const xmlChar) -> c_int {
4705    if str.is_null() {
4706        return -1;
4707    }
4708    let len = crate::xml::string::xml_strlen(str) as c_int;
4709    crate::xml::io::buf_add(buf, str, len)
4710}
4711
4712/// Set buffer allocation scheme.
4713///
4714/// # UPSTREAM-PARITY
4715///
4716/// ```c
4717/// void xmlBufferSetAllocationScheme(xmlBufferPtr buf,
4718///                                    xmlBufferAllocationScheme scheme);
4719/// ```
4720#[no_mangle]
4721pub extern "C" fn xmlBufferSetAllocationScheme(buf: *mut _xmlBuffer, scheme: c_int) {
4722    if buf.is_null() {
4723        return;
4724    }
4725    unsafe {
4726        (*buf).alloc = scheme;
4727    }
4728}
4729
4730/// Shrink buffer.
4731///
4732/// # UPSTREAM-PARITY
4733///
4734/// ```c
4735/// int xmlBufferShrink(xmlBufferPtr buf, int len);
4736/// ```
4737#[no_mangle]
4738pub extern "C" fn xmlBufferShrink(buf: *mut _xmlBuffer, len: c_int) -> c_int {
4739    if buf.is_null() || len <= 0 {
4740        return 0;
4741    }
4742    unsafe {
4743        let b = &mut *buf;
4744        let shrink_len = (len as c_uint).min(b.use_);
4745        if shrink_len > 0 {
4746            let remaining = b.use_ - shrink_len;
4747            if remaining > 0 {
4748                core::ptr::copy(
4749                    b.content.add(shrink_len as usize),
4750                    b.content,
4751                    remaining as usize,
4752                );
4753            }
4754            *b.content.add(remaining as usize) = 0;
4755            b.use_ = remaining;
4756        }
4757    }
4758    len
4759}
4760
4761/// Grow buffer.
4762///
4763/// # UPSTREAM-PARITY
4764///
4765/// ```c
4766/// int xmlBufferGrow(xmlBufferPtr buf, int len);
4767/// ```
4768#[no_mangle]
4769pub extern "C" fn xmlBufferGrow(buf: *mut _xmlBuffer, len: c_int) -> c_int {
4770    if buf.is_null() || len <= 0 {
4771        return 0;
4772    }
4773    let cur_use = unsafe { (*buf).use_ };
4774    let new_size = cur_use + len as c_uint + 1;
4775    crate::xml::io::buf_grow(buf, new_size)
4776}
4777
4778/// Reserve buffer space.
4779///
4780/// # UPSTREAM-PARITY
4781///
4782/// ```c
4783/// int xmlBufferReserve(xmlBufferPtr buf, int len);
4784/// ```
4785#[no_mangle]
4786pub extern "C" fn xmlBufferReserve(buf: *mut _xmlBuffer, len: c_int) -> c_int {
4787    xmlBufferGrow(buf, len)
4788}
4789
4790/// Detach buffer content.
4791///
4792/// # UPSTREAM-PARITY
4793///
4794/// ```c
4795/// xmlChar *xmlBufferDetach(xmlBufferPtr buf);
4796/// ```
4797#[no_mangle]
4798pub extern "C" fn xmlBufferDetach(buf: *mut _xmlBuffer) -> *mut xmlChar {
4799    if buf.is_null() {
4800        return ptr::null_mut();
4801    }
4802    unsafe {
4803        let content = (*buf).content;
4804        (*buf).content = ptr::null_mut();
4805        (*buf).use_ = 0;
4806        (*buf).size = 0;
4807        content
4808    }
4809}
4810
4811// ═══════════════════════════════════════════════════════════════════════════════
4812// 13. Encoding
4813// ═══════════════════════════════════════════════════════════════════════════════
4814
4815/// Get encoding from a name string.
4816///
4817/// # UPSTREAM-PARITY
4818///
4819/// ```c
4820/// xmlCharEncoding xmlGetCharEncoding(const char *name);
4821/// ```
4822#[no_mangle]
4823pub extern "C" fn xmlGetCharEncoding(name: *const c_char) -> c_int {
4824    if name.is_null() {
4825        return 0; // XML_CHAR_ENCODING_NONE
4826    }
4827    let name_bytes = unsafe {
4828        let len = libc::strlen(name);
4829        core::slice::from_raw_parts(name as *const u8, len)
4830    };
4831    crate::xml::encoding::encoding_from_name(name_bytes) as c_int
4832}
4833
4834/// Find an encoding handler.
4835///
4836/// # UPSTREAM-PARITY
4837///
4838/// ```c
4839/// xmlCharEncodingHandlerPtr xmlFindCharEncodingHandler(const char *name);
4840/// ```
4841#[no_mangle]
4842pub extern "C" fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut c_void {
4843    if name.is_null() {
4844        return ptr::null_mut();
4845    }
4846    crate::xml::encoding::find_encoding_handler(name as *const xmlChar) as *mut c_void
4847}
4848
4849/// Close an encoding handler.
4850///
4851/// # UPSTREAM-PARITY
4852///
4853/// ```c
4854/// int xmlCharEncCloseFunc(xmlCharEncodingHandlerPtr handler);
4855/// ```
4856#[no_mangle]
4857pub extern "C" fn xmlCharEncCloseFunc(handler: *mut c_void) -> c_int {
4858    if handler.is_null() {
4859        return -1;
4860    }
4861    // Free the encoding handler
4862    unsafe {
4863        let h = handler as *mut crate::abi::structs::_xmlCharEncodingHandler;
4864        if !(*h).name.is_null() {
4865            crate::abi::allocator::xmlFreeImpl((*h).name as *mut c_void);
4866        }
4867        crate::abi::allocator::xmlFreeImpl(handler);
4868    }
4869    0
4870}
4871
4872/// Convert a block of ISO-8859-1 bytes to UTF-8 (upstream encoding.c
4873/// `xmlIsolat1ToUTF8`; R-000165 closure).
4874///
4875/// `*outlen`/`*inlen` are updated with the bytes produced/consumed; returns
4876/// the number of bytes written or an xmlCharEncError code.
4877///
4878/// # SAFETY
4879///
4880/// `out`/`in` must be valid buffers for `*outlen`/`*inlen` bytes.
4881#[no_mangle]
4882pub unsafe extern "C" fn xmlIsolat1ToUTF8(
4883    out: *mut u8,
4884    outlen: *mut c_int,
4885    input: *const u8,
4886    inlen: *mut c_int,
4887) -> c_int {
4888    // xmlCharEncError (encoding.h): SUCCESS 0, INTERNAL -1, SPACE -2.
4889    const XML_ENC_ERR_SPACE: c_int = -2;
4890    const XML_ENC_ERR_INTERNAL: c_int = -1;
4891    unsafe {
4892        if out.is_null() || input.is_null() || outlen.is_null() || inlen.is_null() {
4893            return XML_ENC_ERR_INTERNAL;
4894        }
4895        let outstart = out;
4896        let instart = input;
4897        let outend = out.add(*outlen as usize);
4898        let inend = input.add(*inlen as usize);
4899        let mut cur = input;
4900        let mut o = out;
4901        while cur < inend {
4902            let c = *cur;
4903            if c < 0x80 {
4904                if o >= outend {
4905                    break;
4906                }
4907                *o = c;
4908                o = o.add(1);
4909            } else {
4910                if (outend as usize) - (o as usize) < 2 {
4911                    break;
4912                }
4913                *o = (c >> 6) | 0xC0;
4914                *o.add(1) = (c & 0x3F) | 0x80;
4915                o = o.add(2);
4916            }
4917            cur = cur.add(1);
4918        }
4919        let mut ret = XML_ENC_ERR_SPACE;
4920        if cur == inend {
4921            ret = (o as usize - outstart as usize) as c_int;
4922        }
4923        *outlen = (o as usize - outstart as usize) as c_int;
4924        *inlen = (cur as usize - instart as usize) as c_int;
4925        ret
4926    }
4927}
4928
4929/// Convert a block of UTF-8 to ISO-8859-1 (upstream encoding.c
4930/// `xmlUTF8ToIsolat1`; R-000165 closure).
4931///
4932/// # SAFETY
4933///
4934/// `out`/`in` must be valid buffers for `*outlen`/`*inlen` bytes.
4935#[no_mangle]
4936pub unsafe extern "C" fn xmlUTF8ToIsolat1(
4937    out: *mut u8,
4938    outlen: *mut c_int,
4939    input: *const u8,
4940    inlen: *mut c_int,
4941) -> c_int {
4942    const XML_ENC_ERR_SPACE: c_int = -2;
4943    const XML_ENC_ERR_INTERNAL: c_int = -1;
4944    const XML_ENC_ERR_INPUT: c_int = -3;
4945    const XML_ENC_ERR_SUCCESS: c_int = 0;
4946    unsafe {
4947        if out.is_null() || outlen.is_null() || inlen.is_null() {
4948            return XML_ENC_ERR_INTERNAL;
4949        }
4950        if input.is_null() {
4951            *inlen = 0;
4952            *outlen = 0;
4953            return XML_ENC_ERR_SUCCESS;
4954        }
4955        let outstart = out;
4956        let instart = input;
4957        let outend = out.add(*outlen as usize);
4958        let inend = input.add(*inlen as usize);
4959        let mut cur = input;
4960        let mut o = out;
4961        let mut ret = XML_ENC_ERR_SPACE;
4962        while cur < inend {
4963            if o >= outend {
4964                break;
4965            }
4966            let c = *cur;
4967            if c < 0x80 {
4968                *o = c;
4969                o = o.add(1);
4970            } else if c >= 0xC2 && c <= 0xC3 {
4971                if (inend as usize) - (cur as usize) < 2 {
4972                    break;
4973                }
4974                cur = cur.add(1);
4975                *o = (c << 6) | (*cur & 0x3F);
4976                o = o.add(1);
4977            } else {
4978                ret = XML_ENC_ERR_INPUT;
4979                break;
4980            }
4981            cur = cur.add(1);
4982        }
4983        if ret != XML_ENC_ERR_INPUT {
4984            ret = (o as usize - outstart as usize) as c_int;
4985        }
4986        *outlen = (o as usize - outstart as usize) as c_int;
4987        *inlen = (cur as usize - instart as usize) as c_int;
4988        ret
4989    }
4990}
4991
4992/// Return the name of a character encoding (upstream encoding.h).
4993///
4994/// # UPSTREAM-PARITY
4995///
4996/// ```c
4997/// const char *xmlGetCharEncodingName(xmlCharEncoding enc);
4998/// ```
4999#[no_mangle]
5000pub extern "C" fn xmlGetCharEncodingName(enc: c_int) -> *const c_char {
5001    /* Values outside the local enum resolve against the upstream
5002     * defaultHandlers table (XML_CHAR_ENCODING_UTF16=23, HTML=24,
5003     * WINDOWS_1252=31); anything else is unknown (NULL). */
5004    if enc < -1 || enc > 22 {
5005        return match enc {
5006            23 => c"UTF-16".as_ptr(),
5007            24 => c"HTML".as_ptr(),
5008            31 => c"windows-1252".as_ptr(),
5009            _ => ptr::null(),
5010        };
5011    }
5012    let e: crate::abi::types::xmlCharEncoding = unsafe { core::mem::transmute(enc) };
5013    crate::xml::encoding::xmlGetCharEncodingName(e)
5014}
5015
5016/// Parse an encoding name into an xmlCharEncoding value (upstream encoding.h).
5017///
5018/// # UPSTREAM-PARITY
5019///
5020/// ```c
5021/// xmlCharEncoding xmlParseCharEncoding(const char *name);
5022/// ```
5023///
5024/// Returns the encoding value or XML_CHAR_ENCODING_ERROR (-1).
5025#[no_mangle]
5026pub extern "C" fn xmlParseCharEncoding(name: *const c_char) -> c_int {
5027    crate::xml::encoding::xmlParseCharEncoding(name)
5028}
5029
5030/// Add an encoding alias (upstream encoding.h).
5031///
5032/// # UPSTREAM-PARITY
5033///
5034/// ```c
5035/// int xmlAddEncodingAlias(const char *name, const char *alias);
5036/// ```
5037#[no_mangle]
5038pub extern "C" fn xmlAddEncodingAlias(name: *const c_char, alias: *const c_char) -> c_int {
5039    crate::xml::encoding::add_encoding_alias(name, alias)
5040}
5041
5042/// Delete an encoding alias (upstream encoding.h).
5043///
5044/// # UPSTREAM-PARITY
5045///
5046/// ```c
5047/// int xmlDelEncodingAlias(const char *alias);
5048/// ```
5049#[no_mangle]
5050pub extern "C" fn xmlDelEncodingAlias(alias: *const c_char) -> c_int {
5051    crate::xml::encoding::del_encoding_alias(alias)
5052}
5053
5054/// Look up an encoding alias (upstream encoding.h).
5055///
5056/// # UPSTREAM-PARITY
5057///
5058/// ```c
5059/// const char *xmlGetEncodingAlias(const char *alias);
5060/// ```
5061#[no_mangle]
5062pub extern "C" fn xmlGetEncodingAlias(alias: *const c_char) -> *const c_char {
5063    crate::xml::encoding::get_encoding_alias(alias)
5064}
5065
5066/// Clean up the encoding alias table (upstream encoding.h).
5067///
5068/// # UPSTREAM-PARITY
5069///
5070/// ```c
5071/// void xmlCleanupEncodingAliases(void);
5072/// ```
5073#[no_mangle]
5074pub extern "C" fn xmlCleanupEncodingAliases() {
5075    crate::xml::encoding::cleanup_encoding_aliases();
5076}
5077
5078/// Convert the input buffer using an encoding handler (upstream encoding.h).
5079///
5080/// # UPSTREAM-PARITY
5081///
5082/// ```c
5083/// int xmlCharEncInFunc(xmlCharEncodingHandler *handler,
5084///                      xmlBufferPtr out, xmlBufferPtr in);
5085/// ```
5086#[no_mangle]
5087pub extern "C" fn xmlCharEncInFunc(
5088    handler: *mut c_void,
5089    out: *mut c_void,
5090    in_: *mut c_void,
5091) -> c_int {
5092    crate::xml::encoding::xmlCharEncInFunc(
5093        handler as *mut crate::abi::structs::_xmlCharEncodingHandler,
5094        out as *mut crate::abi::structs::_xmlBuffer,
5095        in_ as *mut crate::abi::structs::_xmlBuffer,
5096    )
5097}
5098
5099/// Convert the output buffer using an encoding handler (upstream encoding.h).
5100///
5101/// # UPSTREAM-PARITY
5102///
5103/// ```c
5104/// int xmlCharEncOutFunc(xmlCharEncodingHandler *handler,
5105///                       xmlBufferPtr out, xmlBufferPtr in);
5106/// ```
5107#[no_mangle]
5108pub extern "C" fn xmlCharEncOutFunc(
5109    handler: *mut c_void,
5110    out: *mut c_void,
5111    in_: *mut c_void,
5112) -> c_int {
5113    crate::xml::encoding::xmlCharEncOutFunc(
5114        handler as *mut crate::abi::structs::_xmlCharEncodingHandler,
5115        out as *mut crate::abi::structs::_xmlBuffer,
5116        in_ as *mut crate::abi::structs::_xmlBuffer,
5117    )
5118}
5119
5120/// Create a new encoding handler (upstream encoding.h).
5121///
5122/// # UPSTREAM-PARITY
5123///
5124/// ```c
5125/// xmlCharEncodingHandlerPtr xmlNewCharEncodingHandler(
5126///     const char *name, xmlCharEncodingInputFunc input,
5127///     xmlCharEncodingOutputFunc output);
5128/// ```
5129#[no_mangle]
5130pub extern "C" fn xmlNewCharEncodingHandler(
5131    name: *const c_char,
5132    input: crate::abi::callbacks::xmlCharEncodingInputFunc,
5133    output: crate::abi::callbacks::xmlCharEncodingOutputFunc,
5134) -> *mut c_void {
5135    crate::xml::encoding::xmlNewCharEncodingHandler(name, input, output) as *mut c_void
5136}
5137
5138/// Initialize the built-in encoding handlers (upstream encoding.h).
5139///
5140/// # UPSTREAM-PARITY
5141///
5142/// ```c
5143/// void xmlInitCharEncodingHandlers(void);
5144/// ```
5145#[no_mangle]
5146pub extern "C" fn xmlInitCharEncodingHandlers() {
5147    crate::xml::encoding::xmlInitCharEncodingHandlers();
5148}
5149
5150/// Clean up the encoding handlers (upstream encoding.h).
5151///
5152/// # UPSTREAM-PARITY
5153///
5154/// ```c
5155/// void xmlCleanupCharEncodingHandlers(void);
5156/// ```
5157#[no_mangle]
5158pub extern "C" fn xmlCleanupCharEncodingHandlers() {
5159    crate::xml::encoding::xmlCleanupCharEncodingHandlers();
5160}
5161
5162/// Look up a built-in encoding handler by `xmlCharEncoding` value.
5163///
5164/// Returns an `xmlParserErrors` code; on success `*out` receives the static
5165/// handler (NULL for UTF-8, which needs no conversion).
5166///
5167/// # UPSTREAM-PARITY
5168///
5169/// ```c
5170/// xmlParserErrors xmlLookupCharEncodingHandler(xmlCharEncoding enc,
5171///                                              xmlCharEncodingHandler **out);
5172/// ```
5173#[no_mangle]
5174pub extern "C" fn xmlLookupCharEncodingHandler(enc: c_int, out: *mut *mut c_void) -> c_int {
5175    crate::xml::encoding::xmlLookupCharEncodingHandler(enc, out)
5176}
5177
5178/// Get the encoding handler for an `xmlCharEncoding` value (deprecated).
5179///
5180/// # UPSTREAM-PARITY
5181///
5182/// ```c
5183/// xmlCharEncodingHandler *xmlGetCharEncodingHandler(xmlCharEncoding enc);
5184/// ```
5185#[no_mangle]
5186pub extern "C" fn xmlGetCharEncodingHandler(enc: c_int) -> *mut c_void {
5187    crate::xml::encoding::xmlGetCharEncodingHandler(enc)
5188}
5189
5190/// Find or create an encoding handler by name for one conversion direction.
5191///
5192/// # UPSTREAM-PARITY
5193///
5194/// ```c
5195/// xmlParserErrors xmlOpenCharEncodingHandler(const char *name, int output,
5196///                                            xmlCharEncodingHandler **out);
5197/// ```
5198#[no_mangle]
5199pub extern "C" fn xmlOpenCharEncodingHandler(
5200    name: *const c_char,
5201    output: c_int,
5202    out: *mut *mut c_void,
5203) -> c_int {
5204    crate::xml::encoding::xmlOpenCharEncodingHandler(name, output, out)
5205}
5206
5207/// Find or create an encoding handler by name with flags and an optional
5208/// custom conversion implementation.
5209///
5210/// # UPSTREAM-PARITY
5211///
5212/// ```c
5213/// xmlParserErrors xmlCreateCharEncodingHandler(
5214///     const char *name, xmlCharEncFlags flags, xmlCharEncConvImpl impl,
5215///     void *implCtxt, xmlCharEncodingHandler **out);
5216/// ```
5217#[no_mangle]
5218pub extern "C" fn xmlCreateCharEncodingHandler(
5219    name: *const c_char,
5220    flags: c_int,
5221    impl_: Option<crate::abi::callbacks::xmlCharEncConvImpl>,
5222    implCtxt: *mut c_void,
5223    out: *mut *mut c_void,
5224) -> c_int {
5225    crate::xml::encoding::xmlCreateCharEncodingHandler(name, flags, impl_, implCtxt, out)
5226}
5227
5228/// Create an encoding handler backed by modern conversion callbacks.
5229///
5230/// # UPSTREAM-PARITY
5231///
5232/// ```c
5233/// xmlParserErrors xmlCharEncNewCustomHandler(
5234///     const char *name, xmlCharEncConvFunc input, xmlCharEncConvFunc output,
5235///     xmlCharEncConvCtxtDtor ctxtDtor, void *inputCtxt, void *outputCtxt,
5236///     xmlCharEncodingHandler **out);
5237/// ```
5238#[no_mangle]
5239pub extern "C" fn xmlCharEncNewCustomHandler(
5240    name: *const c_char,
5241    input: crate::abi::callbacks::xmlCharEncConvFunc,
5242    output: crate::abi::callbacks::xmlCharEncConvFunc,
5243    ctxtDtor: Option<crate::abi::callbacks::xmlCharEncConvCtxtDtor>,
5244    inputCtxt: *mut c_void,
5245    outputCtxt: *mut c_void,
5246    out: *mut *mut c_void,
5247) -> c_int {
5248    crate::xml::encoding::xmlCharEncNewCustomHandler(
5249        name, input, output, ctxtDtor, inputCtxt, outputCtxt, out,
5250    )
5251}
5252
5253/// Convert an input buffer's encoding.
5254///
5255/// # UPSTREAM-PARITY
5256///
5257/// ```c
5258/// int xmlCharEncInput(xmlParserInputBufferPtr input, int to);
5259/// ```
5260#[no_mangle]
5261pub unsafe extern "C" fn xmlCharEncInput(input: *mut _xmlParserInputBuffer, _to: c_int) -> c_int {
5262    if input.is_null() {
5263        return -1;
5264    }
5265    let handler = (*input).encoder as *mut crate::abi::structs::_xmlCharEncodingHandler;
5266    if handler.is_null() {
5267        return -1;
5268    }
5269    let raw = (*input).raw as *mut crate::abi::structs::_xmlBuffer;
5270    let buf = (*input).buffer as *mut crate::abi::structs::_xmlBuffer;
5271    if raw.is_null() || buf.is_null() {
5272        return -1;
5273    }
5274    crate::xml::encoding::char_enc_in(handler, buf, raw)
5275}
5276
5277/// Convert an output buffer's encoding.
5278///
5279/// # UPSTREAM-PARITY
5280///
5281/// ```c
5282/// int xmlCharEncOutput(xmlOutputBufferPtr output, int to);
5283/// ```
5284#[no_mangle]
5285pub unsafe extern "C" fn xmlCharEncOutput(output: *mut _xmlOutputBuffer, _to: c_int) -> c_int {
5286    if output.is_null() {
5287        return -1;
5288    }
5289    let handler = (*output).encoder as *mut crate::abi::structs::_xmlCharEncodingHandler;
5290    if handler.is_null() {
5291        return -1;
5292    }
5293    let buf = (*output).buffer as *mut crate::abi::structs::_xmlBuffer;
5294    let conv = (*output).conv as *mut crate::abi::structs::_xmlBuffer;
5295    if buf.is_null() || conv.is_null() {
5296        return -1;
5297    }
5298    crate::xml::encoding::char_enc_out(handler, conv, buf)
5299}
5300
5301// ═══════════════════════════════════════════════════════════════════════════════
5302// URI
5303// ═══════════════════════════════════════════════════════════════════════════════
5304
5305/// Parse a URI string.
5306///
5307/// # UPSTREAM-PARITY
5308///
5309/// ```c
5310/// xmlURIPtr xmlParseURI(const char *str);
5311/// ```
5312#[no_mangle]
5313pub unsafe extern "C" fn xmlParseURI(str: *const c_char) -> *mut c_void {
5314    crate::xml::uri::xmlParseURI(str)
5315}
5316
5317/// Parse a URI string (raw version).
5318///
5319/// # UPSTREAM-PARITY
5320///
5321/// ```c
5322/// xmlURIPtr xmlParseURIRaw(const char *str, int raw);
5323/// ```
5324#[no_mangle]
5325pub unsafe extern "C" fn xmlParseURIRaw(str: *const c_char, raw: c_int) -> *mut c_void {
5326    let _ = raw;
5327    crate::xml::uri::xmlParseURI(str)
5328}
5329
5330/// Free a URI structure.
5331///
5332/// # UPSTREAM-PARITY
5333///
5334/// ```c
5335/// void xmlFreeURI(xmlURIPtr uri);
5336/// ```
5337#[no_mangle]
5338pub unsafe extern "C" fn xmlFreeURI(uri: *mut c_void) {
5339    crate::xml::uri::xmlFreeURI(uri)
5340}
5341
5342/// Create an empty URI.
5343///
5344/// # UPSTREAM-PARITY
5345///
5346/// ```c
5347/// xmlURIPtr xmlCreateURI(void);
5348/// ```
5349#[no_mangle]
5350pub extern "C" fn xmlCreateURI() -> *mut c_void {
5351    crate::xml::uri::xmlCreateURI()
5352}
5353
5354/// Save a URI structure to a string.
5355///
5356/// # UPSTREAM-PARITY
5357///
5358/// ```c
5359/// xmlChar *xmlSaveUri(xmlURIPtr uri);
5360/// ```
5361#[no_mangle]
5362pub unsafe extern "C" fn xmlSaveUri(uri: *mut c_void) -> *mut xmlChar {
5363    crate::xml::uri::xmlSaveUri(uri)
5364}
5365
5366/// Parse a URI string into an existing URI structure (upstream uri.h).
5367///
5368/// # UPSTREAM-PARITY
5369///
5370/// ```c
5371/// int xmlParseURIReference(xmlURIPtr uri, const char *str);
5372/// ```
5373///
5374/// Returns 0 on success, -1 on failure (the URI structure is left
5375/// untouched on failure).
5376///
5377/// # Safety
5378///
5379/// - `uri` must be a valid pointer from `xmlParseURI`/`xmlCreateURI`.
5380/// - `str` must be a valid null-terminated C string.
5381#[no_mangle]
5382pub unsafe extern "C" fn xmlParseURIReference(uri: *mut c_void, str: *const c_char) -> c_int {
5383    crate::xml::uri::xmlParseURIReference(uri, str)
5384}
5385
5386/// Normalize a URI path in place (upstream uri.h).
5387///
5388/// # UPSTREAM-PARITY
5389///
5390/// ```c
5391/// int xmlNormalizeURIPath(char *path);
5392/// ```
5393///
5394/// Returns 0 on success, -1 if the path is NULL, not absolute, or contains
5395/// `..` segments that climb above the root.
5396///
5397/// # Safety
5398///
5399/// `path` must be a valid writable null-terminated C string buffer.
5400#[no_mangle]
5401pub unsafe extern "C" fn xmlNormalizeURIPath(path: *mut c_char) -> c_int {
5402    crate::xml::uri::xmlNormalizeURIPath(path)
5403}
5404
5405/// Escape a URI string.
5406///
5407/// # UPSTREAM-PARITY
5408///
5409/// ```c
5410/// xmlChar *xmlURIEscapeStr(const xmlChar *str, const xmlChar *list);
5411/// ```
5412#[no_mangle]
5413pub unsafe extern "C" fn xmlURIEscapeStr(
5414    str: *const xmlChar,
5415    list: *const xmlChar,
5416) -> *mut xmlChar {
5417    crate::xml::uri::xmlURIEscapeStr(str, list)
5418}
5419
5420/// Unescape a URI string.
5421///
5422/// # UPSTREAM-PARITY
5423///
5424/// ```c
5425/// char *xmlURIUnescapeString(const char *str, int len, char *target);
5426/// ```
5427#[no_mangle]
5428pub unsafe extern "C" fn xmlURIUnescapeString(
5429    str: *const c_char,
5430    len: c_int,
5431    target: *mut c_char,
5432) -> *mut c_char {
5433    crate::xml::uri::xmlURIUnescapeString(str, len, target)
5434}
5435
5436// ═══════════════════════════════════════════════════════════════════════════════
5437// 14. XPath
5438// ═══════════════════════════════════════════════════════════════════════════════
5439
5440// ── Helper functions ────────────────────────────────────────────────────
5441
5442/// Convert an internal `XPathValue` to a C ABI `_xmlXPathObject`.
5443///
5444/// The returned pointer is heap-allocated via `xmlMallocZero` and must be
5445/// freed with `xmlXPathFreeObject`.
5446///
5447/// # Safety
5448///
5449/// Must be called from a context where `xmlMalloc` is safe to call.
5450unsafe fn xpath_to_object(val: XPathValue) -> *mut _xmlXPathObject {
5451    let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
5452    if obj.is_null() {
5453        return ptr::null_mut();
5454    }
5455    match val {
5456        XPathValue::NodeSet(ns) => {
5457            (*obj).type_ = xmlXPathObjectType::XPATH_NODESET as c_int;
5458            (*obj).nodesetval = ns.to_raw() as *mut c_void;
5459        }
5460        XPathValue::Boolean(b) => {
5461            (*obj).type_ = xmlXPathObjectType::XPATH_BOOLEAN as c_int;
5462            (*obj).boolval = if b { 1 } else { 0 };
5463        }
5464        XPathValue::Number(n) => {
5465            (*obj).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
5466            (*obj).floatval = n;
5467        }
5468        XPathValue::String(s) => {
5469            (*obj).type_ = xmlXPathObjectType::XPATH_STRING as c_int;
5470            let bytes = s.as_bytes();
5471            let len = bytes.len();
5472            let buf = xmlMallocImpl(len + 1) as *mut xmlChar;
5473            if !buf.is_null() {
5474                ptr::copy_nonoverlapping(bytes.as_ptr(), buf, len);
5475                *buf.add(len) = 0; // null terminator
5476            }
5477            (*obj).stringval = buf;
5478        }
5479    }
5480    obj
5481}
5482
5483/// Extract an internal `XPathValue` from a C ABI `_xmlXPathObject`.
5484///
5485/// # Safety
5486///
5487/// `obj` must be a valid, non-null pointer to a properly initialised
5488/// `_xmlXPathObject`.
5489unsafe fn object_to_xpathvalue(obj: *mut _xmlXPathObject) -> XPathValue {
5490    let typ = (*obj).type_;
5491    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
5492        let ns_ptr = (*obj).nodesetval as *mut _xmlNodeSet;
5493        if ns_ptr.is_null() {
5494            return XPathValue::NodeSet(NodeSet::new());
5495        }
5496        let node_nr = (*ns_ptr).nodeNr;
5497        let node_tab = (*ns_ptr).nodeTab;
5498        let mut ns = NodeSet::new();
5499        if !node_tab.is_null() {
5500            for i in 0..node_nr as isize {
5501                let node = *node_tab.add(i as usize);
5502                ns.push(node);
5503            }
5504        }
5505        XPathValue::NodeSet(ns)
5506    } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
5507        XPathValue::Boolean((*obj).boolval != 0)
5508    } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
5509        XPathValue::Number((*obj).floatval)
5510    } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
5511        let s_ptr = (*obj).stringval;
5512        if s_ptr.is_null() {
5513            XPathValue::String(String::new())
5514        } else {
5515            let s = CStr::from_ptr(s_ptr as *const c_char)
5516                .to_string_lossy()
5517                .into_owned();
5518            XPathValue::String(s)
5519        }
5520    } else if typ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
5521        // A result tree fragment: node-set containing the fragment's
5522        // document node (matching how global RTF variables are bound), so
5523        // local RTF variables stringify to their text and remain navigable
5524        // via exsl:node-set.
5525        let frag_doc = (*obj).nodesetval as *mut _xmlDoc;
5526        if frag_doc.is_null() {
5527            XPathValue::NodeSet(NodeSet::new())
5528        } else {
5529            let mut ns = NodeSet::new();
5530            ns.push(frag_doc as *mut _xmlNode);
5531            XPathValue::NodeSet(ns)
5532        }
5533    } else {
5534        // Undefined / unknown type — return boolean false as a safe default.
5535        XPathValue::Boolean(false)
5536    }
5537}
5538
5539/// Public wrapper for `xpath_to_object` (used by the XPath export bridge).
5540///
5541/// # Safety
5542///
5543/// - `val` is consumed and converted into a heap-allocated `_xmlXPathObject`.
5544pub unsafe fn xpath_to_object_pub(val: XPathValue) -> *mut _xmlXPathObject {
5545    xpath_to_object(val)
5546}
5547
5548/// Public wrapper for `object_to_xpathvalue` (used by the XSLT engine).
5549///
5550/// # Safety
5551///
5552/// `obj` must be a valid, non-null pointer to a properly initialised
5553/// `_xmlXPathObject`.
5554pub unsafe fn object_to_xpathvalue_pub(obj: *mut _xmlXPathObject) -> XPathValue {
5555    object_to_xpathvalue(obj)
5556}
5557
5558// ── Compiled expression registry ────────────────────────────────────────
5559//
5560// Compiled XPath expressions are opaque pointers returned by xmlXPathCompile.
5561// We store them in a global registry keyed by a monotonically increasing ID.
5562
5563static COMPILED_EXPRS: Lazy<Mutex<HashMap<u64, Box<CompiledExpr>>>> =
5564    Lazy::new(|| Mutex::new(HashMap::new()));
5565static NEXT_COMPILED_KEY: Lazy<Mutex<u64>> = Lazy::new(|| Mutex::new(1));
5566
5567/// Accessor for the compiled-expression registry (used by the XPath export
5568/// bridge for `xmlXPathCompiledEval` / `xmlXPathCompiledEvalToBoolean`).
5569pub(crate) fn xpath_compiled_registry() -> &'static Mutex<HashMap<u64, Box<CompiledExpr>>> {
5570    &COMPILED_EXPRS
5571}
5572
5573// ── C extension-function registry ──────────────────────────────────────
5574//
5575// C extension functions registered via xmlXPathRegisterFunc / RegisterFuncNS
5576// are stored here because the Rust XPathFunction signature is incompatible
5577// with the C xmlXPathFunction calling convention (the C function expects a
5578// parser context, not pre-evaluated argument slices). The registration is
5579// stored faithfully; invoking registered C functions from within the Rust
5580// evaluator requires a bridge that is not yet implemented.
5581
5582type CXPathFunc = unsafe extern "C" fn(*mut c_void, c_int);
5583
5584/// Wrapper around `*mut c_void` that implements `Send` + `Sync` so it can
5585/// be used as a key in a `Mutex`-protected global `HashMap`.
5586#[derive(Clone, Copy, PartialEq, Eq, Hash)]
5587struct SendSyncPtr(*mut c_void);
5588unsafe impl Send for SendSyncPtr {}
5589unsafe impl Sync for SendSyncPtr {}
5590
5591static C_FUNCTIONS: Lazy<Mutex<HashMap<(SendSyncPtr, String), CXPathFunc>>> =
5592    Lazy::new(|| Mutex::new(HashMap::new()));
5593
5594/// Look up a C-registered extension function for the context identified by
5595/// `extra` (the internal XPathContext pointer). Used by
5596/// `xmlXPathFunctionLookupNS`.
5597pub(crate) fn xpath_cfunc_lookup(extra: *mut c_void, qualified: &str) -> Option<CXPathFunc> {
5598    C_FUNCTIONS
5599        .lock()
5600        .get(&(SendSyncPtr(extra), qualified.to_string()))
5601        .copied()
5602}
5603
5604/// Drop every C extension-function registration belonging to the context
5605/// identified by `extra` (upstream `xmlXPathRegisteredFuncsCleanup`).
5606pub(crate) fn xpath_cfunc_cleanup(extra: *mut c_void) {
5607    C_FUNCTIONS.lock().retain(|(k, _), _| k.0 != extra);
5608}
5609
5610/// Build the Rust-side closure that bridges a C-registered XPath function
5611/// into the Rust evaluator (see `c_func_call_bridge`). Returns a
5612/// `BoxedXPathFunction` so the closure is coerced with the higher-ranked
5613/// signature the evaluator requires.
5614fn c_func_bridge_closure(c_ctxt: SendSyncPtr, qualified: String) -> BoxedXPathFunction {
5615    Box::new(move |_ctx: &mut XPathContext, args: &[XPathValue]| {
5616        let cc = c_ctxt;
5617        unsafe { c_func_call_bridge(cc.0 as *mut _xmlXPathContext, &qualified, args) }
5618    })
5619}
5620
5621/// Call a C-ABI `xmlXPathFunction` through a synthesized
5622/// `xmlXPathParserContext`: push the evaluated arguments as XPath objects,
5623/// invoke the function, pop and convert the result — the upstream
5624/// `xmlXPathCompOpEval` function-call sequence (xpath.c).
5625///
5626/// # SAFETY
5627///
5628/// - `fnptr` must be a valid C callback (or None).
5629/// - `c_ctxt` must be the live C XPath context the callback belongs to.
5630pub(crate) unsafe fn call_c_xpath_function(
5631    fnptr: Option<unsafe extern "C" fn(*mut c_void, c_int)>,
5632    c_ctxt: *mut _xmlXPathContext,
5633    args: &[XPathValue],
5634) -> Result<XPathValue, String> {
5635    let func = match fnptr {
5636        Some(f) => f,
5637        None => return Err("XPath: missing C function pointer".to_string()),
5638    };
5639    let pc = crate::xml::xpath::parser_context::new_parser_context(ptr::null(), c_ctxt);
5640    if pc.is_null() {
5641        return Err("XPath: parser-context allocation failure".to_string());
5642    }
5643    let mut push_ok = true;
5644    for v in args {
5645        let obj = xpath_to_object(v.clone());
5646        if obj.is_null() || crate::xml::xpath::parser_context::value_push(pc, obj).is_null() {
5647            push_ok = false;
5648            break;
5649        }
5650    }
5651    let result = if push_ok {
5652        // SAFETY: `func` is a valid C callback; the arguments are on the
5653        // parser-context value stack exactly as upstream would leave them.
5654        unsafe { func(pc as *mut c_void, args.len() as c_int) };
5655        let ret = crate::xml::xpath::parser_context::value_pop(pc);
5656        if ret.is_null() {
5657            Err("XPath: C function returned no value".to_string())
5658        } else {
5659            let v = object_to_xpathvalue(ret);
5660            // The popped object is heap-allocated; free it after converting.
5661            unsafe { xmlXPathFreeObject(ret) };
5662            Ok(v)
5663        }
5664    } else {
5665        Err("XPath: failed to push arguments to C function".to_string())
5666    };
5667    // Free any objects the C function left on the stack, then the context.
5668    unsafe {
5669        loop {
5670            let leftover = crate::xml::xpath::parser_context::value_pop(pc);
5671            if leftover.is_null() {
5672                break;
5673            }
5674            xmlXPathFreeObject(leftover);
5675        }
5676        crate::xml::xpath::parser_context::free_parser_context(pc);
5677    }
5678    result
5679}
5680
5681/// Rust-side wrapper registered in the internal XPathContext when a C
5682/// extension function is registered (`xmlXPathRegisterFunc[NS]`). This is the
5683/// parser-context bridge: it synthesises the upstream `xmlXPathParserContext`
5684/// (value stack + context pointer), pushes the evaluated arguments as XPath
5685/// objects, invokes the C function, then pops and converts the result — the
5686/// upstream `xmlXPathCompOpEval` function-call sequence (xpath.c).
5687unsafe fn c_func_call_bridge(
5688    c_ctxt: *mut _xmlXPathContext,
5689    qualified: &str,
5690    args: &[XPathValue],
5691) -> Result<XPathValue, String> {
5692    if c_ctxt.is_null() {
5693        return Err("XPath: null context in C function bridge".to_string());
5694    }
5695    let func = xpath_cfunc_lookup((*c_ctxt).extra, qualified);
5696    if func.is_none() {
5697        return Err(format!("XPath: unknown C function '{}'", qualified));
5698    }
5699    unsafe { call_c_xpath_function(func, c_ctxt, args) }
5700}
5701
5702// ── Public API ─────────────────────────────────────────────────────────
5703
5704/// Create a new XPath context.
5705///
5706/// Allocates a `_xmlXPathContext` and an internal `XPathContext`, storing
5707/// the latter's pointer in the `extra` field.
5708///
5709/// # UPSTREAM-PARITY
5710///
5711/// ```c
5712/// xmlXPathContextPtr xmlXPathNewContext(xmlDocPtr doc);
5713/// ```
5714#[no_mangle]
5715pub unsafe extern "C" fn xmlXPathNewContext(doc: *mut _xmlDoc) -> *mut _xmlXPathContext {
5716    let ctxt = xmlMallocZero(size_of::<_xmlXPathContext>()) as *mut _xmlXPathContext;
5717    if ctxt.is_null() {
5718        return ptr::null_mut();
5719    }
5720
5721    // Initialise the C ABI context fields.
5722    (*ctxt).doc = doc;
5723    (*ctxt).node = ptr::null_mut();
5724    (*ctxt).contextSize = 1;
5725    (*ctxt).proximityPosition = 1;
5726
5727    // Create the internal XPathContext and store it in `extra`.
5728    let mut internal = Box::new(XPathContext::new(doc));
5729    // UPSTREAM-PARITY: the standard function library is implicitly available
5730    // in every context (upstream compiles it in; xmlXPathRegisterAllFunctions
5731    // is a no-op since 2.14.0). Without this, core-function calls such as
5732    // count() would fail as unknown functions.
5733    for (name, func) in crate::xml::xpath::functions::core_functions() {
5734        internal.register_function(&name, func);
5735    }
5736    (*ctxt).extra = Box::into_raw(internal) as *mut c_void;
5737
5738    ctxt
5739}
5740
5741/// Free an XPath context.
5742///
5743/// # UPSTREAM-PARITY
5744///
5745/// ```c
5746/// void xmlXPathFreeContext(xmlXPathContextPtr ctxt);
5747/// ```
5748#[no_mangle]
5749pub unsafe extern "C" fn xmlXPathFreeContext(ctxt: *mut _xmlXPathContext) {
5750    if ctxt.is_null() {
5751        return;
5752    }
5753    // Drop the internal XPathContext.
5754    if !(*ctxt).extra.is_null() {
5755        let _ = Box::from_raw((*ctxt).extra as *mut XPathContext);
5756        (*ctxt).extra = ptr::null_mut();
5757    }
5758    // Drop the registered-namespace C-string hash (xmlXPathNsLookup pointers).
5759    if !(*ctxt).nsHash.is_null() {
5760        drop(Box::from_raw(
5761            (*ctxt).nsHash as *mut HashMap<String, CString>,
5762        ));
5763        (*ctxt).nsHash = ptr::null_mut();
5764    }
5765    // Free the C ABI context struct.
5766    xmlFreeImpl(ctxt as *mut c_void);
5767}
5768
5769/// Evaluate an XPath expression.
5770///
5771/// # UPSTREAM-PARITY
5772///
5773/// ```c
5774/// xmlXPathObjectPtr xmlXPathEvalExpression(const xmlChar *str,
5775///                                          xmlXPathContextPtr ctxt);
5776/// ```
5777#[no_mangle]
5778pub unsafe extern "C" fn xmlXPathEvalExpression(
5779    str_: *const xmlChar,
5780    ctxt: *mut _xmlXPathContext,
5781) -> *mut _xmlXPathObject {
5782    if str_.is_null() || ctxt.is_null() {
5783        return ptr::null_mut();
5784    }
5785    let expr_str = match CStr::from_ptr(str_ as *const c_char).to_str() {
5786        Ok(s) => s,
5787        Err(_) => return ptr::null_mut(),
5788    };
5789    let internal = (*ctxt).extra as *mut XPathContext;
5790    if internal.is_null() {
5791        return ptr::null_mut();
5792    }
5793    let internal = &mut *internal;
5794    // Clear any stale error so a fresh evaluation either succeeds or records
5795    // its own failure message (the XSLT layer surfaces it verbatim).
5796    internal.clear_error();
5797
5798    match crate::xml::xpath::evaluate_str(expr_str, internal) {
5799        Some(val) => xpath_to_object(val),
5800        None => {
5801            // UPSTREAM-PARITY: libxml2 reports a failed compile/eval with
5802            // "XPath error : Invalid expression" (xmlXPathErr,
5803            // XPATH_EXPR_ERROR). The precise per-expression diagnostics are
5804            // tracked as RESIDUAL R-XPATH-ERRMSG.
5805            if internal.error.is_none() {
5806                internal.set_error("Invalid expression");
5807            }
5808            ptr::null_mut()
5809        }
5810    }
5811}
5812
5813/// Evaluate an XPath expression (simplified alias).
5814///
5815/// # UPSTREAM-PARITY
5816///
5817/// ```c
5818/// xmlXPathObjectPtr xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctxt);
5819/// ```
5820#[no_mangle]
5821pub unsafe extern "C" fn xmlXPathEval(
5822    str_: *const xmlChar,
5823    ctxt: *mut _xmlXPathContext,
5824) -> *mut _xmlXPathObject {
5825    xmlXPathEvalExpression(str_, ctxt)
5826}
5827
5828/// Free an XPath object.
5829///
5830/// Releases the internal members (string buffer or node-set) and then frees
5831/// the object struct itself.
5832///
5833/// # UPSTREAM-PARITY
5834///
5835/// ```c
5836/// void xmlXPathFreeObject(xmlXPathObjectPtr obj);
5837/// ```
5838#[no_mangle]
5839pub unsafe extern "C" fn xmlXPathFreeObject(obj: *mut _xmlXPathObject) {
5840    if obj.is_null() {
5841        return;
5842    }
5843    let typ = (*obj).type_;
5844    // Free string storage.
5845    if typ == xmlXPathObjectType::XPATH_STRING as c_int {
5846        if !(*obj).stringval.is_null() {
5847            xmlFreeImpl((*obj).stringval as *mut c_void);
5848            (*obj).stringval = ptr::null_mut();
5849        }
5850    }
5851    // Free node-set storage.
5852    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
5853        let ns = (*obj).nodesetval as *mut _xmlNodeSet;
5854        if !ns.is_null() {
5855            if !(*ns).nodeTab.is_null() {
5856                xmlFreeImpl((*ns).nodeTab as *mut c_void);
5857            }
5858            xmlFreeImpl(ns as *mut c_void);
5859        }
5860        (*obj).nodesetval = ptr::null_mut();
5861    }
5862    xmlFreeImpl(obj as *mut c_void);
5863}
5864
5865/// Copy an XPath object (deep copy).
5866///
5867/// # UPSTREAM-PARITY
5868///
5869/// ```c
5870/// xmlXPathObjectPtr xmlXPathObjectCopy(xmlXPathObjectPtr val);
5871/// ```
5872///
5873/// Oracle behavior: returns a newly allocated object with the same type
5874/// and value. Node-sets are copied element-by-element; strings are
5875/// duplicated; numbers and booleans are copied by value.
5876#[no_mangle]
5877pub unsafe extern "C" fn xmlXPathObjectCopy(val: *mut _xmlXPathObject) -> *mut _xmlXPathObject {
5878    if val.is_null() {
5879        return ptr::null_mut();
5880    }
5881    let typ = (*val).type_;
5882    let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
5883    if obj.is_null() {
5884        return ptr::null_mut();
5885    }
5886    (*obj).type_ = typ;
5887    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
5888        let src_ns = (*val).nodesetval as *mut _xmlNodeSet;
5889        if !src_ns.is_null() {
5890            let nr = (*src_ns).nodeNr;
5891            let ns = xmlMallocZero(size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
5892            if ns.is_null() {
5893                xmlFreeImpl(obj as *mut c_void);
5894                return ptr::null_mut();
5895            }
5896            (*ns).nodeNr = nr;
5897            (*ns).nodeMax = nr;
5898            if nr > 0 && !(*src_ns).nodeTab.is_null() {
5899                let tab = xmlMallocImpl((nr as usize) * core::mem::size_of::<*mut _xmlNode>())
5900                    as *mut *mut _xmlNode;
5901                if tab.is_null() {
5902                    xmlFreeImpl(ns as *mut c_void);
5903                    xmlFreeImpl(obj as *mut c_void);
5904                    return ptr::null_mut();
5905                }
5906                ptr::copy_nonoverlapping((*src_ns).nodeTab, tab, nr as usize);
5907                (*ns).nodeTab = tab;
5908            } else {
5909                (*ns).nodeTab = ptr::null_mut();
5910            }
5911            (*obj).nodesetval = ns as *mut c_void;
5912        }
5913    } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
5914        (*obj).boolval = (*val).boolval;
5915    } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
5916        (*obj).floatval = (*val).floatval;
5917    } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
5918        let src = (*val).stringval;
5919        if !src.is_null() {
5920            let len = libc::strlen(src as *const libc::c_char);
5921            let buf = xmlMallocImpl(len + 1) as *mut xmlChar;
5922            if !buf.is_null() {
5923                ptr::copy_nonoverlapping(src, buf, len);
5924                *buf.add(len) = 0;
5925            }
5926            (*obj).stringval = buf;
5927        }
5928    }
5929    obj
5930}
5931
5932/// Cast an XPath object to its string value.
5933///
5934/// Returns a newly allocated string (caller frees with `xmlFree`).
5935///
5936/// # UPSTREAM-PARITY
5937///
5938/// ```c
5939/// xmlChar *xmlXPathCastToString(xmlXPathObjectPtr val);
5940/// ```
5941#[no_mangle]
5942pub unsafe extern "C" fn xmlXPathCastToString(val: *mut _xmlXPathObject) -> *mut xmlChar {
5943    if val.is_null() {
5944        return ptr::null_mut();
5945    }
5946    let typ = (*val).type_;
5947    let mut result: Vec<u8> = Vec::new();
5948    if typ == xmlXPathObjectType::XPATH_STRING as c_int {
5949        if !(*val).stringval.is_null() {
5950            let len = libc::strlen((*val).stringval as *const libc::c_char);
5951            result.extend_from_slice(core::slice::from_raw_parts((*val).stringval, len));
5952        }
5953    } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
5954        // Number → string conversion per XPath 1.0 §4.2:
5955        // - NaN → "NaN"
5956        // - +0/-0 → "0"
5957        // - infinity → "Infinity" / "-Infinity"
5958        // - integer → decimal representation without exponent
5959        let n = (*val).floatval;
5960        result.extend_from_slice(xml_number_to_string(n).as_bytes());
5961    } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
5962        result.extend_from_slice(if (*val).boolval != 0 {
5963            b"true"
5964        } else {
5965            b"false"
5966        });
5967    } else if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
5968        // String value of a node-set is the string value of the first node
5969        // in document order (or empty if empty).
5970        let ns = (*val).nodesetval as *mut _xmlNodeSet;
5971        if !ns.is_null() && (*ns).nodeNr > 0 && !(*ns).nodeTab.is_null() {
5972            let node = *(*ns).nodeTab;
5973            if !node.is_null() {
5974                let content = crate::xml::tree::node_get_content(node);
5975                if !content.is_null() {
5976                    let len = libc::strlen(content as *const libc::c_char);
5977                    result.extend_from_slice(core::slice::from_raw_parts(content, len));
5978                    xmlFreeImpl(content as *mut c_void);
5979                }
5980            }
5981        }
5982    }
5983    // Allocate the C string.
5984    let buf = xmlMallocImpl(result.len() + 1) as *mut xmlChar;
5985    if buf.is_null() {
5986        return ptr::null_mut();
5987    }
5988    if !result.is_empty() {
5989        ptr::copy_nonoverlapping(result.as_ptr(), buf, result.len());
5990    }
5991    *buf.add(result.len()) = 0;
5992    buf
5993}
5994
5995/// Convert an XPath number to its string representation (XPath 1.0 §4.2).
5996///
5997/// Canonical implementation lives in `crate::xml::xpath::types::number_to_string`
5998/// (a port of upstream `xmlXPathCastNumberToString` / `xmlXPathFormatNumber`,
5999/// R-000166); this ABI helper delegates so every number→string conversion
6000/// shares exactly one oracle-verified code path.
6001pub fn xml_number_to_string(n: f64) -> String {
6002    crate::xml::xpath::types::number_to_string(n)
6003}
6004
6005/// Port of upstream xpath.c `xmlXPathStringEvalNumber` (R-000166): see
6006/// `crate::xml::xpath::types::string_bytes_to_number` — the oracle
6007/// accumulates digits directly, caps the fraction at MAX_FRAC=20 digits
6008/// after any leading zeros, applies the exponent with `pow(10.0, exp)`
6009/// (underflowing to 0 below the smallest subnormal), accepts XML whitespace
6010/// around the number, and returns NaN for anything else — including a
6011/// leading '+'.
6012fn xpath_string_eval_number(bytes: &[u8]) -> f64 {
6013    crate::xml::xpath::types::string_bytes_to_number(bytes)
6014}
6015
6016/// Cast a C string to a number per XPath 1.0 §4.2 conversion rules.
6017///
6018/// # UPSTREAM-PARITY
6019///
6020/// ```c
6021/// double xmlXPathCastStringToNumber(const xmlChar *val);
6022/// ```
6023#[no_mangle]
6024pub unsafe extern "C" fn xmlXPathCastStringToNumber(val: *const xmlChar) -> f64 {
6025    if val.is_null() {
6026        return f64::NAN;
6027    }
6028    let len = libc::strlen(val as *const libc::c_char);
6029    let bytes = core::slice::from_raw_parts(val, len);
6030    xpath_string_eval_number(bytes)
6031}
6032
6033/// Compare two nodes in document order.
6034///
6035/// UPSTREAM-PARITY (xpath.c `xmlXPathCmpNodes`): returns **1** when
6036/// `node1` precedes `node2` in document order, **-1** when `node1` follows
6037/// `node2`, 0 for the same node, and -2 for NULL or cross-document
6038/// comparisons. The sign convention was verified against the system oracle
6039/// (libxml2 2.15.3): `xmlXPathCmpNodes(book1, book2)` returns 1.
6040///
6041/// # UPSTREAM-PARITY
6042///
6043/// ```c
6044/// int xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2);
6045/// ```
6046#[no_mangle]
6047pub unsafe extern "C" fn xmlXPathCmpNodes(node1: *mut _xmlNode, node2: *mut _xmlNode) -> c_int {
6048    if node1.is_null() || node2.is_null() {
6049        return -2;
6050    }
6051    if node1 == node2 {
6052        return 0;
6053    }
6054    // Build ancestor chains.
6055    let mut chain1: Vec<*mut _xmlNode> = Vec::new();
6056    let mut chain2: Vec<*mut _xmlNode> = Vec::new();
6057    let mut n = node1;
6058    while !n.is_null() {
6059        chain1.push(n);
6060        n = (*n).parent as *mut _xmlNode;
6061    }
6062    let mut n = node2;
6063    while !n.is_null() {
6064        chain2.push(n);
6065        n = (*n).parent as *mut _xmlNode;
6066    }
6067    // Distinct documents (or entities) case.
6068    if chain1[chain1.len() - 1] != chain2[chain2.len() - 1] {
6069        return -2;
6070    }
6071    // Find the nearest common ancestor.
6072    let mut i = chain1.len();
6073    let mut j = chain2.len();
6074    while i > 0 && j > 0 && chain1[i - 1] == chain2[j - 1] {
6075        i -= 1;
6076        j -= 1;
6077    }
6078    // node1 is an ancestor of node2 -> node1 precedes it -> 1.
6079    if i == 0 {
6080        return 1;
6081    }
6082    // node2 is an ancestor of node1 -> node1 follows it -> -1.
6083    if j == 0 {
6084        return -1;
6085    }
6086    // Compare sibling order at the divergence point.
6087    let mut a = chain1[i - 1];
6088    let mut b = chain2[j - 1];
6089    // Climb to the same level.
6090    while !a.is_null() && !b.is_null() {
6091        let pa = (*a).parent as *mut _xmlNode;
6092        let pb = (*b).parent as *mut _xmlNode;
6093        if pa == pb {
6094            break;
6095        }
6096        a = pa;
6097        b = pb;
6098    }
6099    // Walk forward from the first child of the common parent.
6100    let parent = (*a).parent as *mut _xmlNode;
6101    let mut child = if parent.is_null() {
6102        ptr::null_mut()
6103    } else {
6104        (*parent).children
6105    };
6106    while !child.is_null() {
6107        if child == a {
6108            return 1; // a precedes b
6109        }
6110        if child == b {
6111            return -1; // b precedes a
6112        }
6113        child = (*child).next;
6114    }
6115    0
6116}
6117
6118/// Create a node-set from a range of an existing node-set.
6119///
6120/// # UPSTREAM-PARITY
6121///
6122/// ```c
6123/// xmlNodeSetPtr xmlXPathNodeSetCreate(xmlNodePtr val);
6124/// ```
6125///
6126/// With a null `val`, creates an empty node-set.
6127#[no_mangle]
6128pub unsafe extern "C" fn xmlXPathNodeSetCreate(val: *mut _xmlNode) -> *mut _xmlNodeSet {
6129    let ns = xmlMallocZero(size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
6130    if ns.is_null() {
6131        return ptr::null_mut();
6132    }
6133    if val.is_null() {
6134        return ns;
6135    }
6136    let tab = xmlMallocImpl(core::mem::size_of::<*mut _xmlNode>()) as *mut *mut _xmlNode;
6137    if tab.is_null() {
6138        xmlFreeImpl(ns as *mut c_void);
6139        return ptr::null_mut();
6140    }
6141    *tab = val;
6142    (*ns).nodeTab = tab;
6143    (*ns).nodeNr = 1;
6144    (*ns).nodeMax = 1;
6145    ns
6146}
6147
6148/// Free a node-set allocated by `xmlXPathNodeSetCreate` or a node-set
6149/// builder in this library.
6150///
6151/// Frees the node-set structure and its node table; the nodes themselves
6152/// are owned by their document and are not freed.
6153///
6154/// # UPSTREAM-PARITY
6155///
6156/// ```c
6157/// void xmlXPathFreeNodeSet(xmlNodeSetPtr ns);
6158/// ```
6159#[no_mangle]
6160pub unsafe extern "C" fn xmlXPathFreeNodeSet(ns: *mut _xmlNodeSet) {
6161    if ns.is_null() {
6162        return;
6163    }
6164    if !(*ns).nodeTab.is_null() {
6165        xmlFreeImpl((*ns).nodeTab as *mut c_void);
6166        (*ns).nodeTab = ptr::null_mut();
6167    }
6168    (*ns).nodeNr = 0;
6169    (*ns).nodeMax = 0;
6170    xmlFreeImpl(ns as *mut c_void);
6171}
6172
6173/// Compile an XPath expression.
6174///
6175/// Returns an opaque pointer that can be passed to `xmlXPathEvalExpression`
6176/// (via the compiled-expr infrastructure) or freed with `xmlXPathFreeCompExpr`.
6177///
6178/// # UPSTREAM-PARITY
6179///
6180/// ```c
6181/// xmlXPathCompExprPtr xmlXPathCompile(const xmlChar *str);
6182/// ```
6183#[no_mangle]
6184pub unsafe extern "C" fn xmlXPathCompile(str_: *const xmlChar) -> *mut c_void {
6185    if str_.is_null() {
6186        return ptr::null_mut();
6187    }
6188    let expr_str = match CStr::from_ptr(str_ as *const c_char).to_str() {
6189        Ok(s) => s,
6190        Err(_) => return ptr::null_mut(),
6191    };
6192
6193    match crate::xml::xpath::compile(expr_str) {
6194        Some(compiled) => {
6195            let mut map = COMPILED_EXPRS.lock();
6196            let mut counter = NEXT_COMPILED_KEY.lock();
6197            let key = *counter;
6198            *counter += 1;
6199            map.insert(key, Box::new(compiled));
6200            key as *mut c_void
6201        }
6202        None => ptr::null_mut(),
6203    }
6204}
6205
6206/// Free a compiled XPath expression.
6207///
6208/// # UPSTREAM-PARITY
6209///
6210/// ```c
6211/// void xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp);
6212/// ```
6213#[no_mangle]
6214pub unsafe extern "C" fn xmlXPathFreeCompExpr(comp: *mut c_void) {
6215    if comp.is_null() {
6216        return;
6217    }
6218    let mut map = COMPILED_EXPRS.lock();
6219    map.remove(&(comp as u64));
6220}
6221
6222/// Register an XPath namespace.
6223///
6224/// # UPSTREAM-PARITY
6225///
6226/// ```c
6227/// int xmlXPathRegisterNs(xmlXPathContextPtr ctxt,
6228///                        const xmlChar *prefix, const xmlChar *ns_uri);
6229/// ```
6230#[no_mangle]
6231pub unsafe extern "C" fn xmlXPathRegisterNs(
6232    ctxt: *mut _xmlXPathContext,
6233    prefix: *const xmlChar,
6234    ns_uri: *const xmlChar,
6235) -> c_int {
6236    if ctxt.is_null() || prefix.is_null() || ns_uri.is_null() {
6237        return -1;
6238    }
6239    let internal = (*ctxt).extra as *mut XPathContext;
6240    if internal.is_null() {
6241        return -1;
6242    }
6243    let internal = &mut *internal;
6244
6245    let prefix_str = match CStr::from_ptr(prefix as *const c_char).to_str() {
6246        Ok(s) => s,
6247        Err(_) => return -1,
6248    };
6249    let uri_str = match CStr::from_ptr(ns_uri as *const c_char).to_str() {
6250        Ok(s) => s,
6251        Err(_) => return -1,
6252    };
6253
6254    internal.register_namespace(prefix_str, uri_str);
6255
6256    // Mirror the registration into the C context's nsHash (Box<HashMap<
6257    // String, CString>>): xmlXPathNsLookup hands out pointers into these
6258    // owned C strings, matching upstream ownership (strdup'd in nsHash,
6259    // freed by xmlXPathRegisteredNsCleanup / xmlXPathFreeContext).
6260    let map: &mut HashMap<String, CString> = if (*ctxt).nsHash.is_null() {
6261        let b: Box<HashMap<String, CString>> = Box::new(HashMap::new());
6262        (*ctxt).nsHash = Box::into_raw(b) as *mut c_void;
6263        &mut *((*ctxt).nsHash as *mut HashMap<String, CString>)
6264    } else {
6265        &mut *((*ctxt).nsHash as *mut HashMap<String, CString>)
6266    };
6267    map.insert(
6268        prefix_str.to_string(),
6269        CString::new(uri_str.as_bytes()).unwrap_or_default(),
6270    );
6271    0
6272}
6273
6274/// Register an XPath function.
6275///
6276/// The C function pointer is stored in a side table keyed by the context.
6277/// A Rust-side stub is registered in the internal context so that the Rust
6278/// evaluator is aware of the function; however, calling the C function
6279/// directly from the Rust evaluator is not yet supported.
6280///
6281/// # UPSTREAM-PARITY
6282///
6283/// ```c
6284/// int xmlXPathRegisterFunc(xmlXPathContextPtr ctxt,
6285///                          const xmlChar *name, xmlXPathFunction f);
6286/// ```
6287#[no_mangle]
6288pub unsafe extern "C" fn xmlXPathRegisterFunc(
6289    ctxt: *mut _xmlXPathContext,
6290    name: *const xmlChar,
6291    f: Option<unsafe extern "C" fn(*mut c_void, c_int)>,
6292) -> c_int {
6293    if ctxt.is_null() || name.is_null() {
6294        return -1;
6295    }
6296    let internal = (*ctxt).extra as *mut XPathContext;
6297    if internal.is_null() {
6298        return -1;
6299    }
6300    let internal = &mut *internal;
6301
6302    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
6303        Ok(s) => s,
6304        Err(_) => return -1,
6305    };
6306
6307    if let Some(func) = f {
6308        // Store the C function pointer in the side table.
6309        let key = (SendSyncPtr((*ctxt).extra), name_str.to_string());
6310        C_FUNCTIONS.lock().insert(key, func);
6311        // Register a Rust closure that bridges to the C function through a
6312        // synthesized xmlXPathParserContext (upstream function-call ABI).
6313        let c_ctxt = SendSyncPtr(ctxt as *mut c_void);
6314        let name_owned = name_str.to_string();
6315        internal.register_function(name_str, c_func_bridge_closure(c_ctxt, name_owned));
6316    }
6317    0
6318}
6319
6320/// Register an XPath function with namespace.
6321///
6322/// # UPSTREAM-PARITY
6323///
6324/// ```c
6325/// int xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt,
6326///                            const xmlChar *name, const xmlChar *ns_uri,
6327///                            xmlXPathFunction f);
6328/// ```
6329#[no_mangle]
6330pub unsafe extern "C" fn xmlXPathRegisterFuncNS(
6331    ctxt: *mut _xmlXPathContext,
6332    name: *const xmlChar,
6333    ns_uri: *const xmlChar,
6334    f: Option<unsafe extern "C" fn(*mut c_void, c_int)>,
6335) -> c_int {
6336    if ctxt.is_null() || name.is_null() {
6337        return -1;
6338    }
6339    let internal = (*ctxt).extra as *mut XPathContext;
6340    if internal.is_null() {
6341        return -1;
6342    }
6343    let internal = &mut *internal;
6344
6345    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
6346        Ok(s) => s,
6347        Err(_) => return -1,
6348    };
6349    let ns_str = if ns_uri.is_null() {
6350        String::new()
6351    } else {
6352        match CStr::from_ptr(ns_uri as *const c_char).to_str() {
6353            Ok(s) => s.to_string(),
6354            Err(_) => return -1,
6355        }
6356    };
6357
6358    // Use "{ns}:" prefix as part of the key to keep functions unique.
6359    let qualified = if ns_str.is_empty() {
6360        name_str.to_string()
6361    } else {
6362        format!("{{{}}}{}", ns_str, name_str)
6363    };
6364
6365    if let Some(func) = f {
6366        let key = (SendSyncPtr((*ctxt).extra), qualified.clone());
6367        C_FUNCTIONS.lock().insert(key, func);
6368        let c_ctxt = SendSyncPtr(ctxt as *mut c_void);
6369        let qualified_owned = qualified.clone();
6370        internal.register_function(&qualified, c_func_bridge_closure(c_ctxt, qualified_owned));
6371    }
6372    0
6373}
6374
6375/// Register an XPath variable.
6376///
6377/// # UPSTREAM-PARITY
6378///
6379/// ```c
6380/// int xmlXPathRegisterVariable(xmlXPathContextPtr ctxt,
6381///                              const xmlChar *name, xmlXPathObjectPtr value);
6382/// ```
6383#[no_mangle]
6384pub unsafe extern "C" fn xmlXPathRegisterVariable(
6385    ctxt: *mut _xmlXPathContext,
6386    name: *const xmlChar,
6387    value: *mut _xmlXPathObject,
6388) -> c_int {
6389    if ctxt.is_null() || name.is_null() || value.is_null() {
6390        return -1;
6391    }
6392    let internal = (*ctxt).extra as *mut XPathContext;
6393    if internal.is_null() {
6394        return -1;
6395    }
6396    let internal = &mut *internal;
6397
6398    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
6399        Ok(s) => s,
6400        Err(_) => return -1,
6401    };
6402
6403    let xpath_val = object_to_xpathvalue(value);
6404    internal.register_variable(name_str, xpath_val);
6405    0
6406}
6407
6408/// Create an XPath object wrapping a single node in a node-set.
6409///
6410/// # UPSTREAM-PARITY
6411///
6412/// ```c
6413/// xmlXPathObjectPtr xmlXPathNewNodeSet(xmlNodePtr val);
6414/// ```
6415#[no_mangle]
6416pub unsafe extern "C" fn xmlXPathNewNodeSet(val: *mut _xmlNode) -> *mut _xmlXPathObject {
6417    let ns = if val.is_null() {
6418        NodeSet::new()
6419    } else {
6420        NodeSet::singleton(val)
6421    };
6422    xpath_to_object(XPathValue::NodeSet(ns))
6423}
6424
6425/// Create an XPath object from a C string value.
6426///
6427/// # UPSTREAM-PARITY
6428///
6429/// ```c
6430/// xmlXPathObjectPtr xmlXPathNewCString(const xmlChar *val);
6431/// ```
6432#[no_mangle]
6433pub unsafe extern "C" fn xmlXPathNewCString(val: *const xmlChar) -> *mut _xmlXPathObject {
6434    if val.is_null() {
6435        return xpath_to_object(XPathValue::String(String::new()));
6436    }
6437    let s = match CStr::from_ptr(val as *const c_char).to_str() {
6438        Ok(s) => s.to_string(),
6439        Err(_) => return ptr::null_mut(),
6440    };
6441    xpath_to_object(XPathValue::String(s))
6442}
6443
6444/// Create an XPath number object.
6445///
6446/// # UPSTREAM-PARITY
6447///
6448/// ```c
6449/// xmlXPathObjectPtr xmlXPathNewFloat(double val);
6450/// ```
6451#[no_mangle]
6452pub extern "C" fn xmlXPathNewFloat(val: f64) -> *mut _xmlXPathObject {
6453    unsafe { xpath_to_object(XPathValue::Number(val)) }
6454}
6455
6456/// Create an XPath boolean object.
6457///
6458/// # UPSTREAM-PARITY
6459///
6460/// ```c
6461/// xmlXPathObjectPtr xmlXPathNewBoolean(int val);
6462/// ```
6463#[no_mangle]
6464pub extern "C" fn xmlXPathNewBoolean(val: c_int) -> *mut _xmlXPathObject {
6465    unsafe { xpath_to_object(XPathValue::Boolean(val != 0)) }
6466}
6467
6468// ═══════════════════════════════════════════════════════════════════════════════
6469// 14.5. XPointer
6470// ═══════════════════════════════════════════════════════════════════════════════
6471
6472/// Evaluate an XPointer expression.
6473///
6474/// Delegates to the xpointer module.
6475///
6476/// # UPSTREAM-PARITY
6477///
6478/// ```c
6479/// xmlNodePtr xmlXPtrEval(const xmlChar *expr, xmlDocPtr doc);
6480/// ```
6481#[no_mangle]
6482pub unsafe extern "C" fn xmlXPtrEval(expr: *const c_char, doc: *mut _xmlDoc) -> *mut _xmlNode {
6483    crate::xml::xpointer::xmlXPtrEval(expr, doc)
6484}
6485
6486// ═══════════════════════════════════════════════════════════════════════════════
6487// 15. XInclude
6488// ═══════════════════════════════════════════════════════════════════════════════
6489
6490/// Process XInclude nodes in a document.
6491///
6492/// # UPSTREAM-PARITY
6493///
6494/// ```c
6495/// int xmlXIncludeProcess(xmlDocPtr doc);
6496/// ```
6497#[no_mangle]
6498pub unsafe extern "C" fn xmlXIncludeProcess(doc: *mut _xmlDoc) -> c_int {
6499    crate::xml::xinclude::xinclude_process(doc)
6500}
6501
6502/// Process XInclude nodes with flags.
6503///
6504/// # UPSTREAM-PARITY
6505///
6506/// ```c
6507/// int xmlXIncludeProcessFlags(xmlDocPtr doc, int flags);
6508/// ```
6509#[no_mangle]
6510pub unsafe extern "C" fn xmlXIncludeProcessFlags(doc: *mut _xmlDoc, flags: c_int) -> c_int {
6511    crate::xml::xinclude::xinclude_process_flags(doc, flags)
6512}
6513
6514// ═══════════════════════════════════════════════════════════════════════════════
6515// 16. Catalog
6516// ═══════════════════════════════════════════════════════════════════════════════
6517
6518/// Load a catalog.
6519///
6520/// # UPSTREAM-PARITY
6521///
6522/// ```c
6523/// xmlCatalogPtr xmlCatalogLoad(const char *catalogs);
6524/// ```
6525#[no_mangle]
6526pub extern "C" fn xmlCatalogLoad(catalogs: *const c_char) -> *mut c_void {
6527    if catalogs.is_null() {
6528        return ptr::null_mut();
6529    }
6530    crate::xml::catalog::load_catalog(catalogs)
6531}
6532
6533/// Resolve a public ID.
6534///
6535/// # UPSTREAM-PARITY
6536///
6537/// ```c
6538/// xmlCharPtr xmlCatalogResolvePublic(const xmlChar *pubID);
6539/// ```
6540#[no_mangle]
6541pub unsafe extern "C" fn xmlCatalogResolvePublic(pubID: *const xmlChar) -> *mut xmlChar {
6542    if pubID.is_null() {
6543        return ptr::null_mut();
6544    }
6545    crate::xml::catalog::resolve_public(pubID)
6546}
6547
6548/// Resolve a system ID.
6549///
6550/// # UPSTREAM-PARITY
6551///
6552/// ```c
6553/// xmlCharPtr xmlCatalogResolveSystem(const xmlChar *sysID);
6554/// ```
6555#[no_mangle]
6556pub unsafe extern "C" fn xmlCatalogResolveSystem(sysID: *const xmlChar) -> *mut xmlChar {
6557    if sysID.is_null() {
6558        return ptr::null_mut();
6559    }
6560    crate::xml::catalog::resolve_system(sysID)
6561}
6562
6563/// Resolve a URI.
6564///
6565/// # UPSTREAM-PARITY
6566///
6567/// ```c
6568/// xmlCharPtr xmlCatalogResolveURI(const xmlChar *URI);
6569/// ```
6570#[no_mangle]
6571pub unsafe extern "C" fn xmlCatalogResolveURI(URI: *const xmlChar) -> *mut xmlChar {
6572    if URI.is_null() {
6573        return ptr::null_mut();
6574    }
6575    crate::xml::catalog::resolve_uri(URI)
6576}
6577
6578/// Set catalog defaults.
6579///
6580/// # UPSTREAM-PARITY
6581///
6582/// ```c
6583/// void xmlCatalogSetDefaults(xmlCatalogAllowValue allow);
6584/// ```
6585#[no_mangle]
6586pub extern "C" fn xmlCatalogSetDefaults(allow: c_int) {
6587    crate::xml::catalog::set_defaults(allow)
6588}
6589
6590/// Get catalog defaults.
6591///
6592/// # UPSTREAM-PARITY
6593///
6594/// ```c
6595/// xmlCatalogAllowValue xmlCatalogGetDefaults(void);
6596/// ```
6597#[no_mangle]
6598pub extern "C" fn xmlCatalogGetDefaults() -> c_int {
6599    crate::xml::catalog::get_defaults()
6600}
6601
6602/// Add a catalog.
6603///
6604/// # UPSTREAM-PARITY
6605///
6606/// ```c
6607/// int xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace);
6608/// ```
6609#[no_mangle]
6610pub unsafe extern "C" fn xmlCatalogAdd(
6611    type_: *const xmlChar,
6612    orig: *const xmlChar,
6613    replace: *const xmlChar,
6614) -> c_int {
6615    if type_.is_null() || orig.is_null() || replace.is_null() {
6616        return -1;
6617    }
6618    crate::xml::catalog::add(type_, orig, replace)
6619}
6620
6621/// Remove a catalog entry.
6622///
6623/// # UPSTREAM-PARITY
6624///
6625/// ```c
6626/// int xmlCatalogRemove(const xmlChar *value);
6627/// ```
6628#[no_mangle]
6629pub unsafe extern "C" fn xmlCatalogRemove(value: *const xmlChar) -> c_int {
6630    if value.is_null() {
6631        return 0;
6632    }
6633    crate::xml::catalog::remove(value)
6634}
6635
6636/// Dump the catalog in XML format to a FILE* (upstream `xmlCatalogDump`).
6637///
6638/// # UPSTREAM-PARITY
6639///
6640/// ```c
6641/// void xmlCatalogDump(FILE *out, xmlCatalogPtr catal);
6642/// ```
6643#[no_mangle]
6644pub unsafe extern "C" fn xmlCatalogDump(output: *mut c_void, _catal: *mut c_void) {
6645    if output.is_null() {
6646        return;
6647    }
6648    let doc = crate::xml::catalog::dump_doc();
6649    if doc.is_null() {
6650        return;
6651    }
6652    let mut mem: *mut xmlChar = ptr::null_mut();
6653    let mut size: c_int = 0;
6654    crate::xml::tree::xmlDocDumpFormatMemory(doc, &mut mem, &mut size, 1);
6655    if !mem.is_null() {
6656        libc::fwrite(
6657            mem as *const c_void,
6658            1,
6659            size as usize,
6660            output as *mut libc::FILE,
6661        );
6662        xmlFreeImpl(mem as *mut c_void);
6663    }
6664    crate::xml::tree::free_doc(doc);
6665}
6666
6667/// Save the catalog to a file (upstream `xmlCatalogSave`).
6668///
6669/// Returns 0 on success, -1 on failure.
6670///
6671/// # UPSTREAM-PARITY
6672///
6673/// ```c
6674/// int xmlCatalogSave(const char *filename);
6675/// ```
6676#[no_mangle]
6677pub unsafe extern "C" fn xmlCatalogSave(filename: *const c_char) -> c_int {
6678    if filename.is_null() {
6679        return -1;
6680    }
6681    let doc = crate::xml::catalog::dump_doc();
6682    if doc.is_null() {
6683        return -1;
6684    }
6685    let mut mem: *mut xmlChar = ptr::null_mut();
6686    let mut size: c_int = 0;
6687    crate::xml::tree::xmlDocDumpFormatMemory(doc, &mut mem, &mut size, 1);
6688    let mut ret: c_int = -1;
6689    if !mem.is_null() {
6690        let fp = libc::fopen(filename, b"w\0".as_ptr() as *const c_char);
6691        if !fp.is_null() {
6692            let written = libc::fwrite(mem as *const c_void, 1, size as usize, fp);
6693            ret = if written == size as usize { 0 } else { -1 };
6694            libc::fclose(fp);
6695        }
6696        xmlFreeImpl(mem as *mut c_void);
6697    }
6698    crate::xml::tree::free_doc(doc);
6699    ret
6700}
6701
6702/// Clean up the catalog subsystem.
6703///
6704/// # UPSTREAM-PARITY
6705///
6706/// ```c
6707/// void xmlCatalogCleanup(void);
6708/// ```
6709#[no_mangle]
6710pub extern "C" fn xmlCatalogCleanup() {
6711    crate::xml::catalog::cleanup();
6712}
6713
6714/// Convert an SGML catalog to XML.
6715///
6716/// # UPSTREAM-PARITY
6717///
6718/// ```c
6719/// xmlDocPtr xmlCatalogConvert(void);
6720/// ```
6721#[no_mangle]
6722pub extern "C" fn xmlCatalogConvert() -> *mut _xmlDoc {
6723    // SAFETY: catalog::convert() allocates and builds an XML document tree.
6724    unsafe { crate::xml::catalog::convert() }
6725}
6726
6727// ═══════════════════════════════════════════════════════════════════════════════
6728// 17. HTML
6729// ═══════════════════════════════════════════════════════════════════════════════
6730
6731/// Parse an HTML document from a file.
6732///
6733/// # UPSTREAM-PARITY
6734///
6735/// ```c
6736/// htmlDocPtr htmlParseFile(const char *filename, const char *encoding);
6737/// ```
6738#[no_mangle]
6739pub unsafe extern "C" fn htmlParseFile(
6740    _filename: *const c_char,
6741    _encoding: *const c_char,
6742) -> *mut _xmlDoc {
6743    // Phase 1: STUB
6744    ptr::null_mut()
6745}
6746
6747/// Parse an HTML document from memory.
6748///
6749/// # UPSTREAM-PARITY
6750///
6751/// ```c
6752/// htmlDocPtr htmlParseMemory(const char *buffer, int size);
6753/// ```
6754#[no_mangle]
6755pub unsafe extern "C" fn htmlParseMemory(_buffer: *const c_char, _size: c_int) -> *mut _xmlDoc {
6756    // Phase 1: STUB
6757    ptr::null_mut()
6758}
6759
6760/// Parse an HTML document from a document string.
6761///
6762/// # UPSTREAM-PARITY
6763///
6764/// ```c
6765/// htmlDocPtr htmlParseDoc(const xmlChar *cur, const char *encoding);
6766/// ```
6767#[no_mangle]
6768pub unsafe extern "C" fn htmlParseDoc(
6769    _cur: *const xmlChar,
6770    _encoding: *const c_char,
6771) -> *mut _xmlDoc {
6772    // Phase 1: STUB
6773    ptr::null_mut()
6774}
6775
6776/// Create an HTML parser context.
6777///
6778/// # UPSTREAM-PARITY
6779///
6780/// ```c
6781/// htmlParserCtxtPtr htmlCreateFileParserCtxt(const char *filename,
6782///                                            const char *encoding);
6783/// ```
6784#[no_mangle]
6785pub unsafe extern "C" fn htmlCreateFileParserCtxt(
6786    _filename: *const c_char,
6787    _encoding: *const c_char,
6788) -> *mut c_void {
6789    // Phase 1: STUB
6790    ptr::null_mut()
6791}
6792
6793/// Free an HTML parser context.
6794///
6795/// # UPSTREAM-PARITY
6796///
6797/// ```c
6798/// void htmlFreeParserCtxt(htmlParserCtxtPtr ctxt);
6799/// ```
6800#[no_mangle]
6801pub extern "C" fn htmlFreeParserCtxt(ctxt: *mut c_void) {
6802    unsafe { crate::xml::html::free_parser_ctxt(ctxt) }
6803}
6804
6805/// Initialize the HTML parser.
6806///
6807/// # UPSTREAM-PARITY
6808///
6809/// ```c
6810/// void htmlInitParser(void);
6811/// ```
6812#[no_mangle]
6813pub extern "C" fn htmlInitParser() {
6814    // Phase 1: STUB
6815}
6816
6817/// Clean up the HTML parser.
6818///
6819/// # UPSTREAM-PARITY
6820///
6821/// ```c
6822/// void htmlCleanupParser(void);
6823/// ```
6824#[no_mangle]
6825pub extern "C" fn htmlCleanupParser() {
6826    // Phase 1: STUB
6827}
6828
6829// ═══════════════════════════════════════════════════════════════════════════════
6830// 17.5. Validation (DTD)
6831// ═══════════════════════════════════════════════════════════════════════════════
6832
6833/// Create a new validation context.
6834///
6835/// # UPSTREAM-PARITY
6836///
6837/// ```c
6838/// xmlValidCtxtPtr xmlNewValidCtxt(void);
6839/// ```
6840#[no_mangle]
6841pub unsafe extern "C" fn xmlNewValidCtxt() -> *mut _xmlValidCtxt {
6842    crate::xml::validation::new_valid_ctxt()
6843}
6844
6845/// Free a validation context.
6846///
6847/// # UPSTREAM-PARITY
6848///
6849/// ```c
6850/// void xmlFreeValidCtxt(xmlValidCtxtPtr ctxt);
6851/// ```
6852#[no_mangle]
6853pub unsafe extern "C" fn xmlFreeValidCtxt(ctxt: *mut _xmlValidCtxt) {
6854    crate::xml::validation::free_valid_ctxt(ctxt);
6855}
6856
6857/// Set error and warning callbacks on a validation context.
6858///
6859/// # UPSTREAM-PARITY
6860///
6861/// ```c
6862/// void xmlSetValidErrors(xmlValidCtxtPtr ctxt,
6863///                        xmlGenericErrorFunc err,
6864///                        xmlGenericErrorFunc warn,
6865///                        void *data);
6866/// ```
6867#[no_mangle]
6868pub unsafe extern "C" fn xmlSetValidErrors(
6869    ctxt: *mut _xmlValidCtxt,
6870    err: Option<xmlGenericErrorFunc>,
6871    warn: Option<xmlGenericErrorFunc>,
6872    data: *mut c_void,
6873) {
6874    crate::xml::validation::set_valid_errors(ctxt, err, warn, data);
6875}
6876
6877/// Validate a document against its DTD.
6878///
6879/// # UPSTREAM-PARITY
6880///
6881/// ```c
6882/// int xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
6883/// ```
6884#[no_mangle]
6885pub unsafe extern "C" fn xmlValidateDocument(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
6886    crate::xml::validation::validate_document(ctxt, doc)
6887}
6888
6889/// Final validation pass (check ID/IDREF consistency).
6890///
6891/// # UPSTREAM-PARITY
6892///
6893/// ```c
6894/// int xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
6895/// ```
6896#[no_mangle]
6897pub unsafe extern "C" fn xmlValidateDocumentFinal(
6898    ctxt: *mut _xmlValidCtxt,
6899    doc: *mut _xmlDoc,
6900) -> c_int {
6901    crate::xml::validation::validate_document_final(ctxt, doc)
6902}
6903
6904/// Validate an element node against its DTD declarations.
6905///
6906/// # UPSTREAM-PARITY
6907///
6908/// ```c
6909/// int xmlValidateElement(xmlValidCtxtPtr ctxt,
6910///                        xmlDocPtr doc,
6911///                        xmlNodePtr elem);
6912/// ```
6913#[no_mangle]
6914pub unsafe extern "C" fn xmlValidateElement(
6915    ctxt: *mut _xmlValidCtxt,
6916    doc: *mut _xmlDoc,
6917    elem: *mut _xmlNode,
6918) -> c_int {
6919    crate::xml::validation::validate_element(ctxt, doc, elem)
6920}
6921
6922/// Validate an attribute declaration.
6923///
6924/// # UPSTREAM-PARITY
6925///
6926/// ```c
6927/// int xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt,
6928///                              xmlDocPtr doc,
6929///                              xmlNodePtr elem,
6930///                              xmlAttributePtr attr);
6931/// ```
6932#[no_mangle]
6933pub unsafe extern "C" fn xmlValidateAttributeDecl(
6934    ctxt: *mut _xmlValidCtxt,
6935    doc: *mut _xmlDoc,
6936    elem: *mut _xmlNode,
6937    attr: *mut _xmlAttribute,
6938) -> c_int {
6939    crate::xml::validation::validate_attribute_decl(ctxt, doc, elem, attr)
6940}
6941
6942/// Validate an attribute value against its declared type.
6943///
6944/// # UPSTREAM-PARITY
6945///
6946/// ```c
6947/// int xmlValidateAttributeValue(int type, const xmlChar *value);
6948/// ```
6949#[no_mangle]
6950pub unsafe extern "C" fn xmlValidateAttributeValue(atype: c_int, value: *const xmlChar) -> c_int {
6951    crate::xml::validation::validate_attribute_value(atype, value)
6952}
6953
6954/// Validate a NOTATION reference.
6955///
6956/// # UPSTREAM-PARITY
6957///
6958/// ```c
6959/// int xmlValidateNotationUse(xmlValidCtxtPtr ctxt,
6960///                            xmlDocPtr doc,
6961///                            const xmlChar *notationName);
6962/// ```
6963#[no_mangle]
6964pub unsafe extern "C" fn xmlValidateNotationUse(
6965    ctxt: *mut _xmlValidCtxt,
6966    doc: *mut _xmlDoc,
6967    notation_name: *const xmlChar,
6968) -> c_int {
6969    crate::xml::validation::validate_notation_use(ctxt, doc, notation_name)
6970}
6971
6972/// Validate an ID value (check uniqueness).
6973///
6974/// # UPSTREAM-PARITY
6975///
6976/// ```c
6977/// int xmlValidateID(xmlValidCtxtPtr ctxt,
6978///                   xmlDocPtr doc,
6979///                   xmlNodePtr node,
6980///                   const xmlChar *value);
6981/// ```
6982#[no_mangle]
6983pub unsafe extern "C" fn xmlValidateID(
6984    ctxt: *mut _xmlValidCtxt,
6985    doc: *mut _xmlDoc,
6986    node: *mut _xmlNode,
6987    value: *const xmlChar,
6988) -> c_int {
6989    crate::xml::validation::validate_id(ctxt, doc, node, value)
6990}
6991
6992/// Validate an IDREF value (check it references a known ID).
6993///
6994/// # UPSTREAM-PARITY
6995///
6996/// ```c
6997/// int xmlValidateIDRef(xmlValidCtxtPtr ctxt,
6998///                      xmlDocPtr doc,
6999///                      xmlNodePtr node,
7000///                      const xmlChar *value);
7001/// ```
7002#[no_mangle]
7003pub unsafe extern "C" fn xmlValidateIDRef(
7004    ctxt: *mut _xmlValidCtxt,
7005    doc: *mut _xmlDoc,
7006    node: *mut _xmlNode,
7007    value: *const xmlChar,
7008) -> c_int {
7009    crate::xml::validation::validate_id_ref(ctxt, doc, node, value)
7010}
7011
7012/// Validate IDREFS (whitespace-separated list of IDREFs).
7013///
7014/// # UPSTREAM-PARITY
7015///
7016/// ```c
7017/// int xmlValidateIDRefs(xmlValidCtxtPtr ctxt,
7018///                       xmlDocPtr doc,
7019///                       xmlNodePtr node,
7020///                       const xmlChar *value);
7021/// ```
7022#[no_mangle]
7023pub unsafe extern "C" fn xmlValidateIDRefs(
7024    ctxt: *mut _xmlValidCtxt,
7025    doc: *mut _xmlDoc,
7026    node: *mut _xmlNode,
7027    value: *const xmlChar,
7028) -> c_int {
7029    crate::xml::validation::validate_id_refs(ctxt, doc, node, value)
7030}
7031
7032/// Validate an NCName value (modern 2-arg form, upstream tree.c).
7033///
7034/// # UPSTREAM-PARITY
7035///
7036/// ```c
7037/// int xmlValidateNCName(const xmlChar *value, int space);
7038/// ```
7039///
7040/// Returns -1 on NULL, 0 if valid, 1 if invalid.
7041#[no_mangle]
7042pub unsafe extern "C" fn xmlValidateNCName(value: *const xmlChar, space: c_int) -> c_int {
7043    crate::xml::validation::validate_ncname(value, space)
7044}
7045
7046/// Validate a QName value (modern 2-arg form, upstream tree.c).
7047///
7048/// # UPSTREAM-PARITY
7049///
7050/// ```c
7051/// int xmlValidateQName(const xmlChar *value, int space);
7052/// ```
7053#[no_mangle]
7054pub unsafe extern "C" fn xmlValidateQName(value: *const xmlChar, space: c_int) -> c_int {
7055    crate::xml::validation::validate_qname(value, space)
7056}
7057
7058/// Validate an XML Name value (modern 2-arg form, upstream tree.c).
7059///
7060/// # UPSTREAM-PARITY / HISTORICAL
7061///
7062/// Since libxml2 2.12 the DSO symbol carries a second `int space` parameter
7063/// with inverted return semantics (0 valid / 1 invalid / -1 NULL); the
7064/// pre-2.12 1-arg form no longer exists in the DSO. The candidate matches
7065/// the current oracle. (The 1-arg semantics live on as xmlValidateNameValue.)
7066///
7067/// ```c
7068/// int xmlValidateName(const xmlChar *value, int space);
7069/// ```
7070#[no_mangle]
7071pub unsafe extern "C" fn xmlValidateName(value: *const xmlChar, space: c_int) -> c_int {
7072    crate::xml::validation::validate_name_space(value, space)
7073}
7074
7075/// Validate an NMToken value (modern 2-arg form, upstream tree.c).
7076///
7077/// # UPSTREAM-PARITY
7078///
7079/// ```c
7080/// int xmlValidateNMToken(const xmlChar *value, int space);
7081/// ```
7082#[no_mangle]
7083pub unsafe extern "C" fn xmlValidateNMToken(value: *const xmlChar, space: c_int) -> c_int {
7084    crate::xml::validation::validate_nmtoken_space(value, space)
7085}
7086
7087/// Validate a Name value (1-arg form, upstream valid.c).
7088///
7089/// # UPSTREAM-PARITY
7090///
7091/// ```c
7092/// int xmlValidateNameValue(const xmlChar *value);
7093/// ```
7094///
7095/// Returns 1 if valid, 0 if not (NULL included).
7096#[no_mangle]
7097pub unsafe extern "C" fn xmlValidateNameValue(value: *const xmlChar) -> c_int {
7098    crate::xml::validation::validate_name_value(value)
7099}
7100
7101/// Validate a whitespace-separated list of Names (separator is exactly
7102/// 0x20, upstream erratum E20).
7103///
7104/// # UPSTREAM-PARITY
7105///
7106/// ```c
7107/// int xmlValidateNamesValue(const xmlChar *value);
7108/// ```
7109#[no_mangle]
7110pub unsafe extern "C" fn xmlValidateNamesValue(value: *const xmlChar) -> c_int {
7111    crate::xml::validation::validate_names_value(value)
7112}
7113
7114/// Validate an Nmtoken value (1-arg form, upstream valid.c).
7115///
7116/// # UPSTREAM-PARITY
7117///
7118/// ```c
7119/// int xmlValidateNmtokenValue(const xmlChar *value);
7120/// ```
7121#[no_mangle]
7122pub unsafe extern "C" fn xmlValidateNmtokenValue(value: *const xmlChar) -> c_int {
7123    crate::xml::validation::validate_nmtoken_value(value)
7124}
7125
7126/// Validate a whitespace-separated list of Nmtokens.
7127///
7128/// # UPSTREAM-PARITY
7129///
7130/// ```c
7131/// int xmlValidateNmtokensValue(const xmlChar *value);
7132/// ```
7133#[no_mangle]
7134pub unsafe extern "C" fn xmlValidateNmtokensValue(value: *const xmlChar) -> c_int {
7135    crate::xml::validation::validate_nmtokens_value(value)
7136}
7137
7138/// Validate a single element declaration (VC: Unique Element Type
7139/// Declaration, VC: No Duplicate Types).
7140///
7141/// # UPSTREAM-PARITY
7142///
7143/// ```c
7144/// int xmlValidateElementDecl(xmlValidCtxtPtr ctxt,
7145///                            xmlDocPtr doc,
7146///                            xmlElementPtr elem);
7147/// ```
7148#[no_mangle]
7149pub unsafe extern "C" fn xmlValidateElementDecl(
7150    ctxt: *mut _xmlValidCtxt,
7151    doc: *mut _xmlDoc,
7152    elem: *mut _xmlElement,
7153) -> c_int {
7154    crate::xml::validation::validate_element_decl(ctxt, doc, elem)
7155}
7156
7157/// Validate a notation declaration.
7158///
7159/// # UPSTREAM-PARITY
7160///
7161/// Modern libxml2 has no validity constraint on notation declarations; the
7162/// oracle returns 1 unconditionally (verified by DSO disassembly).
7163///
7164/// ```c
7165/// int xmlValidateNotationDecl(xmlValidCtxtPtr ctxt,
7166///                             xmlDocPtr doc,
7167///                             xmlNotationPtr nota);
7168/// ```
7169#[no_mangle]
7170pub unsafe extern "C" fn xmlValidateNotationDecl(
7171    ctxt: *mut _xmlValidCtxt,
7172    doc: *mut _xmlDoc,
7173    nota: *mut _xmlNotation,
7174) -> c_int {
7175    crate::xml::validation::validate_notation_decl(ctxt, doc, nota)
7176}
7177
7178/// Validate a single attribute against its declaration.
7179///
7180/// # UPSTREAM-PARITY
7181///
7182/// ```c
7183/// int xmlValidateOneAttribute(xmlValidCtxtPtr ctxt,
7184///                             xmlDocPtr doc,
7185///                             xmlNodePtr elem,
7186///                             xmlAttrPtr attr,
7187///                             const xmlChar *value);
7188/// ```
7189#[no_mangle]
7190pub unsafe extern "C" fn xmlValidateOneAttribute(
7191    ctxt: *mut _xmlValidCtxt,
7192    doc: *mut _xmlDoc,
7193    elem: *mut _xmlNode,
7194    attr: *mut _xmlAttr,
7195    value: *const xmlChar,
7196) -> c_int {
7197    crate::xml::validation::validate_one_attribute(ctxt, doc, elem, attr, value)
7198}
7199
7200/// Validate a single element against its declaration (without recursing).
7201///
7202/// # UPSTREAM-PARITY
7203///
7204/// ```c
7205/// int xmlValidateOneElement(xmlValidCtxtPtr ctxt,
7206///                           xmlDocPtr doc,
7207///                           xmlNodePtr elem);
7208/// ```
7209#[no_mangle]
7210pub unsafe extern "C" fn xmlValidateOneElement(
7211    ctxt: *mut _xmlValidCtxt,
7212    doc: *mut _xmlDoc,
7213    elem: *mut _xmlNode,
7214) -> c_int {
7215    crate::xml::validation::validate_one_element(ctxt, doc, elem)
7216}
7217
7218/// Validate a namespace declaration attribute.
7219///
7220/// # UPSTREAM-PARITY
7221///
7222/// ```c
7223/// int xmlValidateOneNamespace(xmlValidCtxtPtr ctxt,
7224///                             xmlDocPtr doc,
7225///                             xmlNodePtr elem,
7226///                             const xmlChar *prefix,
7227///                             xmlNsPtr ns,
7228///                             const xmlChar *value);
7229/// ```
7230#[no_mangle]
7231pub unsafe extern "C" fn xmlValidateOneNamespace(
7232    ctxt: *mut _xmlValidCtxt,
7233    doc: *mut _xmlDoc,
7234    elem: *mut _xmlNode,
7235    prefix: *const xmlChar,
7236    ns: *mut _xmlNs,
7237    value: *const xmlChar,
7238) -> c_int {
7239    crate::xml::validation::validate_one_namespace(ctxt, doc, elem, prefix, ns, value)
7240}
7241
7242/// Push a new element start onto the validation stack (streaming DTD
7243/// validation).
7244///
7245/// # UPSTREAM-PARITY
7246///
7247/// ```c
7248/// int xmlValidatePushElement(xmlValidCtxtPtr ctxt,
7249///                            xmlDocPtr doc,
7250///                            xmlNodePtr elem,
7251///                            const xmlChar *qname);
7252/// ```
7253#[no_mangle]
7254pub unsafe extern "C" fn xmlValidatePushElement(
7255    ctxt: *mut _xmlValidCtxt,
7256    doc: *mut _xmlDoc,
7257    elem: *mut _xmlNode,
7258    qname: *const xmlChar,
7259) -> c_int {
7260    crate::xml::validation::validate_push_element(ctxt, doc, elem, qname)
7261}
7262
7263/// Push character data onto the validation stack.
7264///
7265/// # UPSTREAM-PARITY
7266///
7267/// ```c
7268/// int xmlValidatePushCData(xmlValidCtxtPtr ctxt,
7269///                          const xmlChar *data,
7270///                          int len);
7271/// ```
7272#[no_mangle]
7273pub unsafe extern "C" fn xmlValidatePushCData(
7274    ctxt: *mut _xmlValidCtxt,
7275    data: *const xmlChar,
7276    len: c_int,
7277) -> c_int {
7278    crate::xml::validation::validate_push_cdata(ctxt, data, len)
7279}
7280
7281/// Pop an element end from the validation stack.
7282///
7283/// # UPSTREAM-PARITY
7284///
7285/// ```c
7286/// int xmlValidatePopElement(xmlValidCtxtPtr ctxt,
7287///                           xmlDocPtr doc,
7288///                           xmlNodePtr elem,
7289///                           const xmlChar *qname);
7290/// ```
7291#[no_mangle]
7292pub unsafe extern "C" fn xmlValidatePopElement(
7293    ctxt: *mut _xmlValidCtxt,
7294    doc: *mut _xmlDoc,
7295    elem: *mut _xmlNode,
7296    qname: *const xmlChar,
7297) -> c_int {
7298    crate::xml::validation::validate_pop_element(ctxt, doc, elem, qname)
7299}
7300
7301/// Build the content-model automaton for an element declaration.
7302///
7303/// # UPSTREAM-PARITY
7304///
7305/// ```c
7306/// int xmlValidBuildContentModel(xmlValidCtxtPtr ctxt,
7307///                               xmlElementPtr elem);
7308/// ```
7309#[no_mangle]
7310pub unsafe extern "C" fn xmlValidBuildContentModel(
7311    ctxt: *mut _xmlValidCtxt,
7312    elem: *mut _xmlElement,
7313) -> c_int {
7314    crate::xml::validation::validate_build_content_model(ctxt, elem)
7315}
7316
7317/// Add an attribute to the document's ID table.
7318///
7319/// # UPSTREAM-PARITY
7320///
7321/// ```c
7322/// xmlIDPtr xmlAddID(xmlValidCtxtPtr ctxt,
7323///                   xmlDocPtr doc,
7324///                   const xmlChar *value,
7325///                   xmlAttrPtr attr);
7326/// ```
7327#[no_mangle]
7328pub unsafe extern "C" fn xmlAddID(
7329    ctxt: *mut _xmlValidCtxt,
7330    doc: *mut _xmlDoc,
7331    value: *const xmlChar,
7332    attr: *mut _xmlAttr,
7333) -> *mut _xmlID {
7334    crate::xml::validation::add_id(ctxt, doc, value, attr)
7335}
7336
7337/// Remove an attribute from the document's ID table.
7338///
7339/// # UPSTREAM-PARITY
7340///
7341/// ```c
7342/// int xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr);
7343/// ```
7344#[no_mangle]
7345pub unsafe extern "C" fn xmlRemoveID(doc: *mut _xmlDoc, attr: *mut _xmlAttr) -> c_int {
7346    crate::xml::validation::remove_id(doc, attr)
7347}
7348
7349/// Register an IDREF in the document's ref table.
7350///
7351/// # UPSTREAM-PARITY
7352///
7353/// ```c
7354/// xmlRefPtr xmlAddRef(xmlValidCtxtPtr ctxt,
7355///                     xmlDocPtr doc,
7356///                     const xmlChar *value,
7357///                     xmlAttrPtr attr);
7358/// ```
7359#[no_mangle]
7360pub unsafe extern "C" fn xmlAddRef(
7361    ctxt: *mut _xmlValidCtxt,
7362    doc: *mut _xmlDoc,
7363    value: *const xmlChar,
7364    attr: *mut _xmlAttr,
7365) -> *mut _xmlRef {
7366    crate::xml::validation::add_ref(ctxt, doc, value, attr)
7367}
7368
7369/// Remove an attribute's IDREF entries.
7370///
7371/// # UPSTREAM-PARITY
7372///
7373/// ```c
7374/// int xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr);
7375/// ```
7376#[no_mangle]
7377pub unsafe extern "C" fn xmlRemoveRef(doc: *mut _xmlDoc, attr: *mut _xmlAttr) -> c_int {
7378    crate::xml::validation::remove_ref(doc, attr)
7379}
7380
7381/// Add an ID without a validation context (2.13+).
7382///
7383/// # UPSTREAM-PARITY
7384///
7385/// ```c
7386/// int xmlAddIDSafe(xmlAttrPtr attr, const xmlChar *value);
7387/// ```
7388#[no_mangle]
7389pub unsafe extern "C" fn xmlAddIDSafe(attr: *mut _xmlAttr, value: *const xmlChar) -> c_int {
7390    crate::xml::validation::add_id_safe(attr, value)
7391}
7392
7393/// Free an ID hash table.
7394///
7395/// # UPSTREAM-PARITY
7396///
7397/// ```c
7398/// void xmlFreeIDTable(xmlIDTablePtr table);
7399/// ```
7400#[no_mangle]
7401pub unsafe extern "C" fn xmlFreeIDTable(table: *mut c_void) {
7402    crate::xml::validation::free_id_table(table as *mut crate::xml::hash::HashTable);
7403}
7404
7405/// Free an IDREF hash table.
7406///
7407/// # UPSTREAM-PARITY
7408///
7409/// ```c
7410/// void xmlFreeRefTable(xmlRefTablePtr table);
7411/// ```
7412#[no_mangle]
7413pub unsafe extern "C" fn xmlFreeRefTable(table: *mut c_void) {
7414    crate::xml::validation::free_ref_table(table as *mut crate::xml::hash::HashTable);
7415}
7416
7417/// Look up the attribute holding an ID.
7418///
7419/// # UPSTREAM-PARITY
7420///
7421/// ```c
7422/// xmlAttrPtr xmlGetID(xmlDocPtr doc, const xmlChar *ID);
7423/// ```
7424#[no_mangle]
7425pub unsafe extern "C" fn xmlGetID(doc: *mut _xmlDoc, id: *const xmlChar) -> *mut _xmlAttr {
7426    crate::xml::validation::get_id(doc, id)
7427}
7428
7429/// Look up the list of references for an ID.
7430///
7431/// # UPSTREAM-PARITY
7432///
7433/// ```c
7434/// xmlListPtr xmlGetRefs(xmlDocPtr doc, const xmlChar *ID);
7435/// ```
7436#[no_mangle]
7437pub unsafe extern "C" fn xmlGetRefs(doc: *mut _xmlDoc, id: *const xmlChar) -> *mut c_void {
7438    crate::xml::validation::get_refs(doc, id) as *mut c_void
7439}
7440
7441/// Is this attribute an ID?
7442///
7443/// # UPSTREAM-PARITY
7444///
7445/// ```c
7446/// int xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr);
7447/// ```
7448#[no_mangle]
7449pub unsafe extern "C" fn xmlIsID(
7450    doc: *mut _xmlDoc,
7451    elem: *mut _xmlNode,
7452    attr: *mut _xmlAttr,
7453) -> c_int {
7454    crate::xml::validation::is_id(doc, elem, attr)
7455}
7456
7457/// Is this attribute an IDREF?
7458///
7459/// # UPSTREAM-PARITY
7460///
7461/// ```c
7462/// int xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr);
7463/// ```
7464#[no_mangle]
7465pub unsafe extern "C" fn xmlIsRef(
7466    doc: *mut _xmlDoc,
7467    elem: *mut _xmlNode,
7468    attr: *mut _xmlAttr,
7469) -> c_int {
7470    crate::xml::validation::is_ref(doc, elem, attr)
7471}
7472
7473/// Search a DTD for an element declaration (with QName splitting).
7474///
7475/// # UPSTREAM-PARITY
7476///
7477/// ```c
7478/// xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
7479/// ```
7480#[no_mangle]
7481pub unsafe extern "C" fn xmlGetDtdElementDesc(
7482    dtd: *mut _xmlDtd,
7483    name: *const xmlChar,
7484) -> *mut _xmlElement {
7485    crate::xml::validation::get_dtd_element_desc(dtd, name)
7486}
7487
7488/// Search a DTD for an attribute declaration (with QName splitting).
7489///
7490/// # UPSTREAM-PARITY
7491///
7492/// ```c
7493/// xmlAttributePtr xmlGetDtdAttrDesc(xmlDtdPtr dtd,
7494///                                   const xmlChar *elem,
7495///                                   const xmlChar *name);
7496/// ```
7497#[no_mangle]
7498pub unsafe extern "C" fn xmlGetDtdAttrDesc(
7499    dtd: *mut _xmlDtd,
7500    elem: *const xmlChar,
7501    name: *const xmlChar,
7502) -> *mut _xmlAttribute {
7503    crate::xml::validation::get_dtd_attr_desc(dtd, elem, name)
7504}
7505
7506/// Search a DTD for a qualified element declaration.
7507///
7508/// # UPSTREAM-PARITY
7509///
7510/// ```c
7511/// xmlElementPtr xmlGetDtdQElementDesc(xmlDtdPtr dtd,
7512///                                     const xmlChar *name,
7513///                                     const xmlChar *prefix);
7514/// ```
7515#[no_mangle]
7516pub unsafe extern "C" fn xmlGetDtdQElementDesc(
7517    dtd: *mut _xmlDtd,
7518    name: *const xmlChar,
7519    prefix: *const xmlChar,
7520) -> *mut _xmlElement {
7521    crate::xml::validation::get_dtd_qelement_desc(dtd, name, prefix)
7522}
7523
7524/// Search a DTD for a qualified attribute declaration.
7525///
7526/// # UPSTREAM-PARITY
7527///
7528/// ```c
7529/// xmlAttributePtr xmlGetDtdQAttrDesc(xmlDtdPtr dtd,
7530///                                    const xmlChar *elem,
7531///                                    const xmlChar *name,
7532///                                    const xmlChar *prefix);
7533/// ```
7534#[no_mangle]
7535pub unsafe extern "C" fn xmlGetDtdQAttrDesc(
7536    dtd: *mut _xmlDtd,
7537    elem: *const xmlChar,
7538    name: *const xmlChar,
7539    prefix: *const xmlChar,
7540) -> *mut _xmlAttribute {
7541    crate::xml::validation::get_dtd_qattr_desc(dtd, elem, name, prefix)
7542}
7543
7544/// Search a DTD for a notation declaration.
7545///
7546/// # UPSTREAM-PARITY
7547///
7548/// ```c
7549/// xmlNotationPtr xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name);
7550/// ```
7551#[no_mangle]
7552pub unsafe extern "C" fn xmlGetDtdNotationDesc(
7553    dtd: *mut _xmlDtd,
7554    name: *const xmlChar,
7555) -> *mut _xmlNotation {
7556    crate::xml::validation::get_dtd_notation_desc(dtd, name)
7557}
7558
7559/// Validate the root element of a document.
7560///
7561/// # UPSTREAM-PARITY
7562///
7563/// ```c
7564/// int xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
7565/// ```
7566#[no_mangle]
7567pub unsafe extern "C" fn xmlValidateRoot(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
7568    crate::xml::validation::validate_root(ctxt, doc)
7569}
7570
7571/// Validate element content against its content model.
7572///
7573/// # UPSTREAM-PARITY
7574///
7575/// ```c
7576/// int xmlValidateContent(xmlValidCtxtPtr ctxt,
7577///                        xmlNodePtr node,
7578///                        xmlDocPtr doc);
7579/// ```
7580#[no_mangle]
7581pub unsafe extern "C" fn xmlValidateContent(
7582    ctxt: *mut _xmlValidCtxt,
7583    node: *mut _xmlNode,
7584    doc: *mut _xmlDoc,
7585) -> c_int {
7586    crate::xml::validation::validate_content(ctxt, node, doc)
7587}
7588
7589/// Check if an element is declared as mixed content.
7590///
7591/// # UPSTREAM-PARITY
7592///
7593/// ```c
7594/// int xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name);
7595/// ```
7596#[no_mangle]
7597pub unsafe extern "C" fn xmlIsMixedElement(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
7598    crate::xml::validation::is_mixed_element(doc, name)
7599}
7600
7601/// Check if an element is declared as EMPTY.
7602///
7603/// # UPSTREAM-PARITY
7604///
7605/// ```c
7606/// int xmlIsEmptyElement(xmlDocPtr doc, const xmlChar *name);
7607/// ```
7608#[no_mangle]
7609pub unsafe extern "C" fn xmlIsEmptyElement(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
7610    crate::xml::validation::is_empty_element(doc, name)
7611}
7612
7613/// Validate a DTD's declarations.
7614///
7615/// # UPSTREAM-PARITY
7616///
7617/// ```c
7618/// int xmlValidateDtd(xmlValidCtxtPtr ctxt,
7619///                    xmlDocPtr doc,
7620///                    xmlDtdPtr dtd);
7621/// ```
7622#[no_mangle]
7623pub unsafe extern "C" fn xmlValidateDtd(
7624    ctxt: *mut _xmlValidCtxt,
7625    doc: *mut _xmlDoc,
7626    dtd: *mut _xmlDtd,
7627) -> c_int {
7628    crate::xml::validation::validate_dtd(ctxt, doc, dtd)
7629}
7630
7631/// Final DTD validation (ID/IDREF consistency).
7632///
7633/// # UPSTREAM-PARITY
7634///
7635/// ```c
7636/// int xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
7637/// ```
7638#[no_mangle]
7639pub unsafe extern "C" fn xmlValidateDtdFinal(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
7640    crate::xml::validation::validate_dtd_final(ctxt, doc)
7641}
7642
7643/// Validate that a value is in an enumeration.
7644///
7645/// # UPSTREAM-PARITY
7646///
7647/// ```c
7648/// int xmlValidateEnumeration(xmlValidCtxtPtr ctxt,
7649///                            const xmlChar *value,
7650///                            xmlEnumerationPtr tree);
7651/// ```
7652#[no_mangle]
7653pub unsafe extern "C" fn xmlValidateEnumeration(
7654    ctxt: *mut _xmlValidCtxt,
7655    value: *const xmlChar,
7656    tree: *mut _xmlEnumeration,
7657) -> c_int {
7658    crate::xml::validation::validate_enumeration(ctxt, value, tree)
7659}
7660
7661// ═══════════════════════════════════════════════════════════════════════════════
7662// 18. Debug / Miscellaneous
7663// ═══════════════════════════════════════════════════════════════════════════════
7664
7665/// Dump a document to a file for debugging.
7666
7667/// Get the path to the current executable.
7668///
7669/// # UPSTREAM-PARITY
7670///
7671/// ```c
7672/// char *xmlGetBinaryPath(void);
7673/// ```
7674#[no_mangle]
7675pub extern "C" fn xmlGetBinaryPath() -> *mut c_char {
7676    // Phase 1: STUB
7677    ptr::null_mut()
7678}
7679
7680/// Get the path to the current executable's home directory.
7681///
7682/// # UPSTREAM-PARITY
7683///
7684/// ```c
7685/// char *xmlGetHomeOfBinary(void);
7686/// ```
7687#[no_mangle]
7688pub extern "C" fn xmlGetHomeOfBinary() -> *mut c_char {
7689    // Phase 1: STUB
7690    ptr::null_mut()
7691}
7692
7693// ═══════════════════════════════════════════════════════════════════════════════
7694// SAX2 default callback entry points (upstream SAX2.c)
7695// ═══════════════════════════════════════════════════════════════════════════════
7696//
7697// These are the public `xmlSAX2*` callback functions that downstream code
7698// installs into `xmlSAXHandler` structures. They are the same implementations
7699// the candidate's default SAX handler uses; exporting them under the
7700// upstream names is required for ABI parity (R-000136 closure).
7701
7702/// Upstream SAX2.c `xmlSAX2StartDocument` — public entry point of the default handler.
7703#[no_mangle]
7704pub unsafe extern "C" fn xmlSAX2StartDocument(ctx: *mut c_void) {
7705    crate::xml::sax::default::default_sax_handler::startDocument(ctx)
7706}
7707
7708/// Upstream SAX2.c `xmlSAX2EndDocument` — public entry point of the default handler.
7709#[no_mangle]
7710pub unsafe extern "C" fn xmlSAX2EndDocument(ctx: *mut c_void) {
7711    crate::xml::sax::default::default_sax_handler::endDocument(ctx)
7712}
7713
7714/// Upstream SAX2.c `xmlSAX2StartElementNs` — public entry point of the default handler.
7715#[no_mangle]
7716pub unsafe extern "C" fn xmlSAX2StartElementNs(
7717    ctx: *mut c_void,
7718    localname: *const xmlChar,
7719    prefix: *const xmlChar,
7720    URI: *const xmlChar,
7721    nb_namespaces: c_int,
7722    namespaces: *mut *const xmlChar,
7723    nb_attributes: c_int,
7724    nb_defaulted: c_int,
7725    attributes: *mut *const xmlChar,
7726) {
7727    crate::xml::sax::default::default_sax_handler::startElementNs(
7728        ctx,
7729        localname,
7730        prefix,
7731        URI,
7732        nb_namespaces,
7733        namespaces,
7734        nb_attributes,
7735        nb_defaulted,
7736        attributes,
7737    )
7738}
7739
7740/// Upstream SAX2.c `xmlSAX2EndElementNs` — public entry point of the default handler.
7741#[no_mangle]
7742pub unsafe extern "C" fn xmlSAX2EndElementNs(
7743    ctx: *mut c_void,
7744    localname: *const xmlChar,
7745    prefix: *const xmlChar,
7746    URI: *const xmlChar,
7747) {
7748    crate::xml::sax::default::default_sax_handler::endElementNs(ctx, localname, prefix, URI)
7749}
7750
7751/// Upstream SAX2.c `xmlSAX2Characters` — public entry point of the default handler.
7752#[no_mangle]
7753pub unsafe extern "C" fn xmlSAX2Characters(ctx: *mut c_void, ch: *const xmlChar, len: c_int) {
7754    crate::xml::sax::default::default_sax_handler::characters(ctx, ch, len)
7755}
7756
7757/// Upstream SAX2.c `xmlSAX2IgnorableWhitespace` — public entry point of the default handler.
7758#[no_mangle]
7759pub unsafe extern "C" fn xmlSAX2IgnorableWhitespace(
7760    ctx: *mut c_void,
7761    ch: *const xmlChar,
7762    len: c_int,
7763) {
7764    crate::xml::sax::default::default_sax_handler::ignorableWhitespace(ctx, ch, len)
7765}
7766
7767/// Upstream SAX2.c `xmlSAX2Comment` — public entry point of the default handler.
7768#[no_mangle]
7769pub unsafe extern "C" fn xmlSAX2Comment(ctx: *mut c_void, value: *const xmlChar) {
7770    crate::xml::sax::default::default_sax_handler::comment(ctx, value)
7771}
7772
7773/// Upstream SAX2.c `xmlSAX2ProcessingInstruction` — public entry point of the default handler.
7774#[no_mangle]
7775pub unsafe extern "C" fn xmlSAX2ProcessingInstruction(
7776    ctx: *mut c_void,
7777    target: *const xmlChar,
7778    data: *const xmlChar,
7779) {
7780    crate::xml::sax::default::default_sax_handler::processingInstruction(ctx, target, data)
7781}
7782
7783/// Upstream SAX2.c `xmlSAX2CDataBlock` — public entry point of the default handler.
7784#[no_mangle]
7785pub unsafe extern "C" fn xmlSAX2CDataBlock(ctx: *mut c_void, value: *const xmlChar, len: c_int) {
7786    crate::xml::sax::default::default_sax_handler::cdataBlock(ctx, value, len)
7787}
7788
7789/// Upstream SAX2.c `xmlSAX2InternalSubset` — public entry point of the default handler.
7790#[no_mangle]
7791pub unsafe extern "C" fn xmlSAX2InternalSubset(
7792    ctx: *mut c_void,
7793    name: *const xmlChar,
7794    ExternalID: *const xmlChar,
7795    SystemID: *const xmlChar,
7796) {
7797    crate::xml::sax::default::default_sax_handler::internalSubset(ctx, name, ExternalID, SystemID)
7798}
7799
7800/// Upstream SAX2.c `xmlSAX2ExternalSubset` — public entry point of the default handler.
7801#[no_mangle]
7802pub unsafe extern "C" fn xmlSAX2ExternalSubset(
7803    ctx: *mut c_void,
7804    name: *const xmlChar,
7805    ExternalID: *const xmlChar,
7806    SystemID: *const xmlChar,
7807) {
7808    crate::xml::sax::default::default_sax_handler::externalSubset(ctx, name, ExternalID, SystemID)
7809}
7810
7811/// Upstream SAX2.c `xmlSAX2EntityDecl` — public entry point of the default handler.
7812#[no_mangle]
7813pub unsafe extern "C" fn xmlSAX2EntityDecl(
7814    ctx: *mut c_void,
7815    name: *const xmlChar,
7816    type_: c_int,
7817    publicId: *const xmlChar,
7818    systemId: *const xmlChar,
7819    content: *mut xmlChar,
7820) {
7821    crate::xml::sax::default::default_sax_handler::entityDecl(
7822        ctx, name, type_, publicId, systemId, content,
7823    )
7824}
7825
7826/// Upstream SAX2.c `xmlSAX2AttributeDecl` — public entry point of the default handler.
7827#[no_mangle]
7828pub unsafe extern "C" fn xmlSAX2AttributeDecl(
7829    ctx: *mut c_void,
7830    elem: *const xmlChar,
7831    fullname: *const xmlChar,
7832    type_: c_int,
7833    def: c_int,
7834    defaultValue: *const xmlChar,
7835    tree: *mut crate::abi::structs::_xmlEnumeration,
7836) {
7837    crate::xml::sax::default::default_sax_handler::attributeDecl(
7838        ctx,
7839        elem,
7840        fullname,
7841        type_,
7842        def,
7843        defaultValue,
7844        tree,
7845    )
7846}
7847
7848/// Upstream SAX2.c `xmlSAX2ElementDecl` — public entry point of the default handler.
7849#[no_mangle]
7850pub unsafe extern "C" fn xmlSAX2ElementDecl(
7851    ctx: *mut c_void,
7852    name: *const xmlChar,
7853    type_: c_int,
7854    content: *mut crate::abi::structs::_xmlElementContent,
7855) {
7856    crate::xml::sax::default::default_sax_handler::elementDecl(ctx, name, type_, content)
7857}
7858
7859/// Upstream SAX2.c `xmlSAX2NotationDecl` — public entry point of the default handler.
7860#[no_mangle]
7861pub unsafe extern "C" fn xmlSAX2NotationDecl(
7862    ctx: *mut c_void,
7863    name: *const xmlChar,
7864    publicId: *const xmlChar,
7865    systemId: *const xmlChar,
7866) {
7867    crate::xml::sax::default::default_sax_handler::notationDecl(ctx, name, publicId, systemId)
7868}
7869
7870/// Upstream SAX2.c `xmlSAX2UnparsedEntityDecl` — public entry point of the default handler.
7871#[no_mangle]
7872pub unsafe extern "C" fn xmlSAX2UnparsedEntityDecl(
7873    ctx: *mut c_void,
7874    name: *const xmlChar,
7875    publicId: *const xmlChar,
7876    systemId: *const xmlChar,
7877    notationName: *const xmlChar,
7878) {
7879    crate::xml::sax::default::default_sax_handler::unparsedEntityDecl(
7880        ctx,
7881        name,
7882        publicId,
7883        systemId,
7884        notationName,
7885    )
7886}
7887
7888/// Upstream SAX2.c `xmlSAX2ResolveEntity` — public entry point of the default handler.
7889#[no_mangle]
7890pub unsafe extern "C" fn xmlSAX2ResolveEntity(
7891    ctx: *mut c_void,
7892    publicId: *const xmlChar,
7893    systemId: *const xmlChar,
7894) -> *mut crate::abi::structs::_xmlParserInput {
7895    crate::xml::sax::default::default_sax_handler::resolveEntity(ctx, publicId, systemId)
7896}
7897
7898/// Upstream SAX2.c `xmlSAX2IsStandalone` — public entry point of the default handler.
7899#[no_mangle]
7900pub unsafe extern "C" fn xmlSAX2IsStandalone(ctx: *mut c_void) -> c_int {
7901    crate::xml::sax::default::default_sax_handler::isStandalone(ctx)
7902}
7903
7904/// Upstream SAX2.c `xmlSAX2HasInternalSubset` — public entry point of the default handler.
7905#[no_mangle]
7906pub unsafe extern "C" fn xmlSAX2HasInternalSubset(ctx: *mut c_void) -> c_int {
7907    crate::xml::sax::default::default_sax_handler::hasInternalSubset(ctx)
7908}
7909
7910/// Upstream SAX2.c `xmlSAX2HasExternalSubset` — public entry point of the default handler.
7911#[no_mangle]
7912pub unsafe extern "C" fn xmlSAX2HasExternalSubset(ctx: *mut c_void) -> c_int {
7913    crate::xml::sax::default::default_sax_handler::hasExternalSubset(ctx)
7914}
7915
7916/// Upstream SAX2.c `xmlSAX2GetEntity` — public entry point of the default handler.
7917#[no_mangle]
7918pub unsafe extern "C" fn xmlSAX2GetEntity(
7919    ctx: *mut c_void,
7920    name: *const xmlChar,
7921) -> *mut crate::abi::structs::_xmlEntity {
7922    crate::xml::sax::default::default_sax_handler::getEntity(ctx, name)
7923}
7924
7925/// Upstream SAX2.c `xmlSAX2GetParameterEntity` — public entry point of the default handler.
7926#[no_mangle]
7927pub unsafe extern "C" fn xmlSAX2GetParameterEntity(
7928    ctx: *mut c_void,
7929    name: *const xmlChar,
7930) -> *mut crate::abi::structs::_xmlEntity {
7931    crate::xml::sax::default::default_sax_handler::getParameterEntity(ctx, name)
7932}
7933
7934/// Upstream SAX2.c `xmlSAX2GetLineNumber` — public entry point of the
7935/// default handler (SAX locator callback).
7936#[no_mangle]
7937pub unsafe extern "C" fn xmlSAX2GetLineNumber(ctx: *mut c_void) -> c_int {
7938    crate::xml::sax::default::default_sax_handler::getLineNumber(ctx)
7939}
7940
7941/// Upstream SAX2.c `xmlSAX2GetColumnNumber`.
7942#[no_mangle]
7943pub unsafe extern "C" fn xmlSAX2GetColumnNumber(ctx: *mut c_void) -> c_int {
7944    crate::xml::sax::default::default_sax_handler::getColumnNumber(ctx)
7945}
7946
7947/// Upstream SAX2.c `xmlSAX2GetPublicId`.
7948#[no_mangle]
7949pub unsafe extern "C" fn xmlSAX2GetPublicId(ctx: *mut c_void) -> *const xmlChar {
7950    crate::xml::sax::default::default_sax_handler::getPublicId(ctx)
7951}
7952
7953/// Upstream SAX2.c `xmlSAX2GetSystemId`.
7954#[no_mangle]
7955pub unsafe extern "C" fn xmlSAX2GetSystemId(ctx: *mut c_void) -> *const xmlChar {
7956    crate::xml::sax::default::default_sax_handler::getSystemId(ctx)
7957}
7958
7959/// Upstream SAX2.c `xmlSAX2StartElement` — SAX1 start-element entry point.
7960/// The candidate parser dispatches through the SAX2 (namespaced) callbacks;
7961/// this wrapper maps to the SAX1 handler when installed.
7962#[no_mangle]
7963pub unsafe extern "C" fn xmlSAX2StartElement(
7964    ctx: *mut c_void,
7965    name: *const xmlChar,
7966    atts: *mut *const xmlChar,
7967) {
7968    // The parser core invokes startElementNs; the SAX1 shim is provided by
7969    // the dispatch layer. When this entry point is installed directly on a
7970    // handler, route through the internal SAX1 path.
7971    crate::xml::sax::dispatch::SaxDispatcher::sax1_start_element(ctx, name, atts);
7972}
7973
7974/// Upstream SAX2.c `xmlSAX2EndElement` — SAX1 end-element entry point.
7975#[no_mangle]
7976pub unsafe extern "C" fn xmlSAX2EndElement(ctx: *mut c_void, name: *const xmlChar) {
7977    crate::xml::sax::dispatch::SaxDispatcher::sax1_end_element(ctx, name);
7978}
7979
7980/// Upstream SAX2.c `xmlSAX2SetDocumentLocator` — public entry point of the default handler.
7981#[no_mangle]
7982pub unsafe extern "C" fn xmlSAX2SetDocumentLocator(
7983    ctx: *mut c_void,
7984    loc: *mut crate::abi::callbacks::_xmlSAXLocator,
7985) {
7986    crate::xml::sax::default::default_sax_handler::setDocumentLocator(ctx, loc)
7987}
7988
7989/// Upstream SAX2.c `xmlSAX2Reference` — public entry point of the default handler.
7990#[no_mangle]
7991pub unsafe extern "C" fn xmlSAX2Reference(ctx: *mut c_void, name: *const xmlChar) {
7992    crate::xml::sax::default::default_sax_handler::reference(ctx, name)
7993}
7994
7995#[cfg(test)]
7996mod tests {
7997    use super::xml_number_to_string;
7998
7999    /// R-000166: number-to-string follows upstream xmlXPathFormatNumber —
8000    /// verified byte-identical against the oracle (xsltproc) on the t4/n3
8001    /// differential corpora. Cases here are exact doubles or
8002    /// rounding-robust formats (parser-dependent literals are covered by the
8003    /// differential corpora, not unit tests).
8004    #[test]
8005    fn test_xml_number_to_string_parity_cases() {
8006        let cases: &[(f64, &str)] = &[
8007            (1234567.891, "1234567.891"),
8008            (0.1 + 0.2, "0.3"),
8009            (1.0 / 3.0, "0.333333333333333"),
8010            (1e20, "1e+20"),
8011            (1e-5, "0.00001"),
8012            (123456789012345678901234567890.0, "1.23456789012346e+29"),
8013            (1e100, "1e+100"),
8014            (-1e100, "-1e+100"),
8015            (1.5e-100, "1.5e-100"),
8016            (1e9, "1000000000"),
8017            (0.00001, "0.00001"),
8018            (9.99e-6, "9.99e-06"),
8019            (2147483646.0, "2147483646"),
8020            (2147483648.0, "2.147483648e+09"),
8021            (-2147483647.0, "-2147483647"),
8022            (-2147483649.0, "-2.147483649e+09"),
8023            (0.5, "0.5"),
8024            (1.0 / 7.0, "0.142857142857143"),
8025            (2.675, "2.675"),
8026            (3.141592653589793, "3.141592653589793"),
8027            (-0.0, "0"),
8028            (0.0, "0"),
8029            (f64::INFINITY, "Infinity"),
8030            (f64::NEG_INFINITY, "-Infinity"),
8031            (f64::NAN, "NaN"),
8032            (0.30000000000000004, "0.3"),
8033            (2.2250738585072014e-308, "2.2250738585072e-308"),
8034            (5e-324, "4.94065645841247e-324"),
8035        ];
8036        for (n, expected) in cases {
8037            assert_eq!(&xml_number_to_string(*n), expected, "value: {}", n);
8038        }
8039    }
8040}