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_uint, c_ulong};
49
50use crate::xml::xinclude;
51use crate::xml::xpath::ast::CompiledExpr;
52use crate::xml::xpath::context::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 { xmlMalloc(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 { xmlMalloc(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 { xmlRealloc(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 { xmlRealloc(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::xmlMalloc(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_int {
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 bytes.
2706    if buffer.is_null() || size <= 0 {
2707        return ptr::null_mut();
2708    }
2709    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2710    if ctxt.is_null() {
2711        return ptr::null_mut();
2712    }
2713    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
2714    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2715    (*ctxt).options = options;
2716    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
2717        let doc = (*ctxt).myDoc;
2718        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2719        // UPSTREAM-PARITY: on a hard (non-recoverable) parse error the
2720        // partially built document is discarded and NULL is returned; with
2721        // XML_PARSE_RECOVER the partial tree is kept.
2722        if options & 1 << 0 != 0 {
2723            return doc;
2724        }
2725        if !doc.is_null() {
2726            crate::xml::tree::free_doc(doc);
2727        }
2728        return ptr::null_mut();
2729    }
2730    let doc = (*ctxt).myDoc;
2731    if !doc.is_null() && !URL.is_null() {
2732        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
2733    }
2734    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2735    doc
2736}
2737
2738/// Load a list of catalogs (upstream `xmlLoadCatalogs`).
2739///
2740/// # SAFETY
2741///
2742/// - `catalogs` must be a valid NUL-terminated string or NULL.
2743#[no_mangle]
2744pub unsafe extern "C" fn xmlLoadCatalogs(catalogs: *const c_char) {
2745    if !catalogs.is_null() {
2746        crate::xml::catalog::load_catalog(catalogs);
2747    }
2748}
2749
2750/// Load a single catalog (upstream `xmlLoadCatalog`).
2751///
2752/// # SAFETY
2753///
2754/// - `catalogs` must be a valid NUL-terminated string or NULL.
2755#[no_mangle]
2756pub unsafe extern "C" fn xmlLoadCatalog(catalogs: *const c_char) -> *mut c_void {
2757    crate::xml::catalog::load_catalog(catalogs)
2758}
2759
2760/// Read an XML document from a file descriptor.
2761///
2762/// # UPSTREAM-PARITY
2763///
2764/// ```c
2765/// xmlDocPtr xmlReadFd(int fd, const char *URL, const char *encoding, int options);
2766/// ```
2767#[no_mangle]
2768pub unsafe extern "C" fn xmlReadFd(
2769    fd: c_int,
2770    URL: *const c_char,
2771    encoding: *const c_char,
2772    options: c_int,
2773) -> *mut _xmlDoc {
2774    // SAFETY: fd must be a valid open file descriptor.
2775    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2776    if ctxt.is_null() {
2777        return ptr::null_mut();
2778    }
2779    // Read all data from the fd
2780    let mut buf = Vec::new();
2781    let mut tmp = [0u8; 4096];
2782    loop {
2783        let n = libc::read(fd, tmp.as_mut_ptr() as *mut c_void, tmp.len());
2784        if n <= 0 {
2785            break;
2786        }
2787        buf.extend_from_slice(&tmp[..n as usize]);
2788    }
2789    let input = crate::xml::parser::helpers::input_from_memory(
2790        buf.as_ptr() as *const c_char,
2791        buf.len() as c_int,
2792    );
2793    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2794    (*ctxt).options = options;
2795    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
2796        let doc = (*ctxt).myDoc;
2797        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2798        return doc;
2799    }
2800    let doc = (*ctxt).myDoc;
2801    if !doc.is_null() && !URL.is_null() {
2802        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
2803    }
2804    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2805    doc
2806}
2807
2808/// Read an XML document from I/O callbacks.
2809///
2810/// # UPSTREAM-PARITY
2811///
2812/// ```c
2813/// xmlDocPtr xmlReadIO(xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
2814///                     void *ioctx, const char *URL, const char *encoding, int options);
2815/// ```
2816#[no_mangle]
2817pub unsafe extern "C" fn xmlReadIO(
2818    ioread: Option<xmlInputReadCallback>,
2819    ioclose: Option<xmlInputCloseCallback>,
2820    ioctx: *mut c_void,
2821    URL: *const c_char,
2822    encoding: *const c_char,
2823    options: c_int,
2824) -> *mut _xmlDoc {
2825    // SAFETY: callbacks must be valid function pointers if non-NULL.
2826    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2827    if ctxt.is_null() {
2828        return ptr::null_mut();
2829    }
2830    let input = crate::xml::parser::helpers::input_from_io(ioread, ioclose, ioctx);
2831    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2832    (*ctxt).options = options;
2833    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
2834        let doc = (*ctxt).myDoc;
2835        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2836        return doc;
2837    }
2838    let doc = (*ctxt).myDoc;
2839    if !doc.is_null() && !URL.is_null() {
2840        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
2841    }
2842    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2843    doc
2844}
2845
2846/// Parse an XML document (SAX1).
2847///
2848/// # UPSTREAM-PARITY
2849///
2850/// ```c
2851/// xmlDocPtr xmlSAXParseDoc(xmlSAXHandlerPtr sax, const xmlChar *cur, int recovery);
2852/// ```
2853#[no_mangle]
2854pub unsafe extern "C" fn xmlSAXParseDoc(
2855    sax: *mut _xmlSAXHandler,
2856    cur: *const xmlChar,
2857    recovery: c_int,
2858) -> *mut _xmlDoc {
2859    // SAFETY: cur must be a valid null-terminated xmlChar string.
2860    if cur.is_null() {
2861        return ptr::null_mut();
2862    }
2863    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2864    if ctxt.is_null() {
2865        return ptr::null_mut();
2866    }
2867    if !sax.is_null() {
2868        (*ctxt).sax = sax;
2869        (*ctxt).userData = (*ctxt).sax as *mut c_void;
2870    }
2871    if recovery != 0 {
2872        (*ctxt).recovery = 1;
2873        (*ctxt).options |= 1; // XML_PARSE_RECOVER
2874    }
2875    let len = crate::xml::string::xml_strlen(cur);
2876    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
2877    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2878    crate::xml::parser::helpers::parse_document(ctxt);
2879    let doc = (*ctxt).myDoc;
2880    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2881    doc
2882}
2883
2884/// Parse an XML document (SAX1) with user data (upstream parser.h
2885/// `xmlSAXParseDocWithData`): `user_data` is passed to the SAX callbacks.
2886///
2887/// # UPSTREAM-PARITY
2888///
2889/// ```c
2890/// xmlDocPtr xmlSAXParseDocWithData(xmlSAXHandlerPtr sax, const xmlChar *cur,
2891///                                  int recovery, void *data);
2892/// ```
2893#[no_mangle]
2894pub unsafe extern "C" fn xmlSAXParseDocWithData(
2895    sax: *mut _xmlSAXHandler,
2896    cur: *const xmlChar,
2897    recovery: c_int,
2898    data: *mut c_void,
2899) -> *mut _xmlDoc {
2900    // SAFETY: cur must be a valid null-terminated xmlChar string.
2901    if cur.is_null() {
2902        return ptr::null_mut();
2903    }
2904    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2905    if ctxt.is_null() {
2906        return ptr::null_mut();
2907    }
2908    if !sax.is_null() {
2909        (*ctxt).sax = sax;
2910    }
2911    (*ctxt).userData = data;
2912    if recovery != 0 {
2913        (*ctxt).recovery = 1;
2914        (*ctxt).options |= 1; // XML_PARSE_RECOVER
2915    }
2916    let len = crate::xml::string::xml_strlen(cur);
2917    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
2918    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2919    crate::xml::parser::helpers::parse_document(ctxt);
2920    let doc = (*ctxt).myDoc;
2921    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2922    doc
2923}
2924
2925/// Parse an XML file (SAX1) with user data (upstream parser.h
2926/// `xmlSAXParseFileWithData`).
2927///
2928/// # UPSTREAM-PARITY
2929///
2930/// ```c
2931/// xmlDocPtr xmlSAXParseFileWithData(xmlSAXHandlerPtr sax, const char *filename,
2932///                                   int recovery, void *data);
2933/// ```
2934#[no_mangle]
2935pub unsafe extern "C" fn xmlSAXParseFileWithData(
2936    sax: *mut _xmlSAXHandler,
2937    filename: *const c_char,
2938    recovery: c_int,
2939    data: *mut c_void,
2940) -> *mut _xmlDoc {
2941    // SAFETY: filename must be a valid C string or NULL.
2942    if filename.is_null() {
2943        return ptr::null_mut();
2944    }
2945    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2946    if ctxt.is_null() {
2947        return ptr::null_mut();
2948    }
2949    if !sax.is_null() {
2950        (*ctxt).sax = sax;
2951    }
2952    (*ctxt).userData = data;
2953    if recovery != 0 {
2954        (*ctxt).recovery = 1;
2955        (*ctxt).options |= 1; // XML_PARSE_RECOVER
2956    }
2957    let input = match crate::xml::parser::helpers::input_from_file(filename) {
2958        Ok(input) => input,
2959        Err(_) => {
2960            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2961            return ptr::null_mut();
2962        }
2963    };
2964    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2965    crate::xml::parser::helpers::parse_document(ctxt);
2966    let doc = (*ctxt).myDoc;
2967    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2968    doc
2969}
2970
2971/// Parse an XML document (SAX1) with user data from memory (upstream
2972/// parser.h `xmlSAXParseMemoryWithData`).
2973///
2974/// # UPSTREAM-PARITY
2975///
2976/// ```c
2977/// xmlDocPtr xmlSAXParseMemoryWithData(xmlSAXHandlerPtr sax, const char *buffer,
2978///                                     int size, int recovery, void *data);
2979/// ```
2980#[no_mangle]
2981pub unsafe extern "C" fn xmlSAXParseMemoryWithData(
2982    sax: *mut _xmlSAXHandler,
2983    buffer: *const c_char,
2984    size: c_int,
2985    recovery: c_int,
2986    data: *mut c_void,
2987) -> *mut _xmlDoc {
2988    // SAFETY: buffer must be a valid pointer with `size` readable bytes.
2989    if buffer.is_null() || size <= 0 {
2990        return ptr::null_mut();
2991    }
2992    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2993    if ctxt.is_null() {
2994        return ptr::null_mut();
2995    }
2996    if !sax.is_null() {
2997        (*ctxt).sax = sax;
2998    }
2999    (*ctxt).userData = data;
3000    if recovery != 0 {
3001        (*ctxt).recovery = 1;
3002        (*ctxt).options |= 1; // XML_PARSE_RECOVER
3003    }
3004    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
3005    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3006    crate::xml::parser::helpers::parse_document(ctxt);
3007    let doc = (*ctxt).myDoc;
3008    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3009    doc
3010}
3011
3012/// Parse an XML file (SAX1).
3013///
3014/// # UPSTREAM-PARITY
3015///
3016/// ```c
3017/// xmlDocPtr xmlSAXParseFile(xmlSAXHandlerPtr sax, const char *filename, int recovery);
3018/// ```
3019#[no_mangle]
3020pub unsafe extern "C" fn xmlSAXParseFile(
3021    sax: *mut _xmlSAXHandler,
3022    filename: *const c_char,
3023    recovery: c_int,
3024) -> *mut _xmlDoc {
3025    // SAFETY: filename must be a valid C string.
3026    if filename.is_null() {
3027        return ptr::null_mut();
3028    }
3029    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3030    if ctxt.is_null() {
3031        return ptr::null_mut();
3032    }
3033    if !sax.is_null() {
3034        (*ctxt).sax = sax;
3035        (*ctxt).userData = (*ctxt).sax as *mut c_void;
3036    }
3037    if recovery != 0 {
3038        (*ctxt).recovery = 1;
3039        (*ctxt).options |= 1;
3040    }
3041    let input = match crate::xml::parser::helpers::input_from_file(filename) {
3042        Ok(input) => input,
3043        Err(_) => {
3044            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3045            return ptr::null_mut();
3046        }
3047    };
3048    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3049    crate::xml::parser::helpers::parse_document(ctxt);
3050    let doc = (*ctxt).myDoc;
3051    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3052    doc
3053}
3054
3055/// Parse an XML document from memory (SAX1).
3056///
3057/// # UPSTREAM-PARITY
3058///
3059/// ```c
3060/// xmlDocPtr xmlSAXParseMemory(xmlSAXHandlerPtr sax,
3061///                             const char *buffer, int size, int recovery);
3062/// ```
3063#[no_mangle]
3064pub unsafe extern "C" fn xmlSAXParseMemory(
3065    sax: *mut _xmlSAXHandler,
3066    buffer: *const c_char,
3067    size: c_int,
3068    recovery: c_int,
3069) -> *mut _xmlDoc {
3070    // SAFETY: buffer must be valid with at least `size` bytes.
3071    if buffer.is_null() || size <= 0 {
3072        return ptr::null_mut();
3073    }
3074    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3075    if ctxt.is_null() {
3076        return ptr::null_mut();
3077    }
3078    if !sax.is_null() {
3079        (*ctxt).sax = sax;
3080        (*ctxt).userData = (*ctxt).sax as *mut c_void;
3081    }
3082    if recovery != 0 {
3083        (*ctxt).recovery = 1;
3084        (*ctxt).options |= 1;
3085    }
3086    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
3087    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3088    crate::xml::parser::helpers::parse_document(ctxt);
3089    let doc = (*ctxt).myDoc;
3090    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3091    doc
3092}
3093
3094/// SAX user parse file.
3095///
3096/// # UPSTREAM-PARITY
3097///
3098/// ```c
3099/// int xmlSAXUserParseFile(xmlSAXHandlerPtr sax, void *user_data,
3100///                         const char *filename);
3101/// ```
3102#[no_mangle]
3103pub unsafe extern "C" fn xmlSAXUserParseFile(
3104    sax: *mut _xmlSAXHandler,
3105    user_data: *mut c_void,
3106    filename: *const c_char,
3107) -> c_int {
3108    // SAFETY: filename must be a valid C string. sax and user_data may be NULL.
3109    if filename.is_null() {
3110        return -1;
3111    }
3112    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3113    if ctxt.is_null() {
3114        return -1;
3115    }
3116    if !sax.is_null() {
3117        (*ctxt).sax = sax;
3118    }
3119    (*ctxt).userData = if !user_data.is_null() {
3120        user_data
3121    } else {
3122        ctxt as *mut c_void
3123    };
3124    let input = match crate::xml::parser::helpers::input_from_file(filename) {
3125        Ok(input) => input,
3126        Err(_) => {
3127            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3128            return -1;
3129        }
3130    };
3131    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3132    let ret = crate::xml::parser::helpers::parse_document(ctxt);
3133    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3134    ret
3135}
3136
3137/// SAX user parse memory.
3138///
3139/// # UPSTREAM-PARITY
3140///
3141/// ```c
3142/// int xmlSAXUserParseMemory(xmlSAXHandlerPtr sax, void *user_data,
3143///                           const char *buffer, int size);
3144/// ```
3145#[no_mangle]
3146pub unsafe extern "C" fn xmlSAXUserParseMemory(
3147    sax: *mut _xmlSAXHandler,
3148    user_data: *mut c_void,
3149    buffer: *const c_char,
3150    size: c_int,
3151) -> c_int {
3152    // SAFETY: buffer must be valid with at least `size` bytes.
3153    if buffer.is_null() || size <= 0 {
3154        return -1;
3155    }
3156    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3157    if ctxt.is_null() {
3158        return -1;
3159    }
3160    if !sax.is_null() {
3161        (*ctxt).sax = sax;
3162    }
3163    (*ctxt).userData = if !user_data.is_null() {
3164        user_data
3165    } else {
3166        ctxt as *mut c_void
3167    };
3168    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
3169    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3170    let ret = crate::xml::parser::helpers::parse_document(ctxt);
3171    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3172    ret
3173}
3174
3175/// Parse an XML document from a string (DOM).
3176///
3177/// # UPSTREAM-PARITY
3178///
3179/// ```c
3180/// xmlDocPtr xmlParseDoc(const xmlChar *cur);
3181/// ```
3182#[no_mangle]
3183pub unsafe extern "C" fn xmlParseDoc(cur: *const xmlChar) -> *mut _xmlDoc {
3184    // SAFETY: cur must be a valid null-terminated xmlChar string.
3185    if cur.is_null() {
3186        return ptr::null_mut();
3187    }
3188    xmlReadDoc(cur, ptr::null(), ptr::null(), 0)
3189}
3190
3191/// Parse an XML file (DOM).
3192///
3193/// # UPSTREAM-PARITY
3194///
3195/// ```c
3196/// xmlDocPtr xmlParseFile(const char *filename);
3197/// ```
3198#[no_mangle]
3199pub unsafe extern "C" fn xmlParseFile(filename: *const c_char) -> *mut _xmlDoc {
3200    // SAFETY: filename must be a valid C string.
3201    if filename.is_null() {
3202        return ptr::null_mut();
3203    }
3204    xmlReadFile(filename, ptr::null(), 0)
3205}
3206
3207/// Parse an XML document from memory (DOM).
3208///
3209/// # UPSTREAM-PARITY
3210///
3211/// ```c
3212/// xmlDocPtr xmlParseMemory(const char *buffer, int size);
3213/// ```
3214#[no_mangle]
3215pub unsafe extern "C" fn xmlParseMemory(buffer: *const c_char, size: c_int) -> *mut _xmlDoc {
3216    // SAFETY: buffer must be valid with at least `size` bytes.
3217    if buffer.is_null() || size <= 0 {
3218        return ptr::null_mut();
3219    }
3220    xmlReadMemory(buffer, size, ptr::null(), ptr::null(), 0)
3221}
3222
3223/// Create a file parser context.
3224///
3225/// # UPSTREAM-PARITY
3226///
3227/// ```c
3228/// xmlParserCtxtPtr xmlCreateFileParserCtxt(const char *filename);
3229/// ```
3230#[no_mangle]
3231pub unsafe extern "C" fn xmlCreateFileParserCtxt(filename: *const c_char) -> *mut _xmlParserCtxt {
3232    // SAFETY: filename must be a valid C string.
3233    if filename.is_null() {
3234        return ptr::null_mut();
3235    }
3236    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3237    if ctxt.is_null() {
3238        return ptr::null_mut();
3239    }
3240    let input = match crate::xml::parser::helpers::input_from_file(filename) {
3241        Ok(input) => input,
3242        Err(_) => {
3243            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3244            return ptr::null_mut();
3245        }
3246    };
3247    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3248    ctxt
3249}
3250
3251/// Create a document parser context.
3252///
3253/// # UPSTREAM-PARITY
3254///
3255/// ```c
3256/// xmlParserCtxtPtr xmlCreateDocParserCtxt(const xmlChar *cur);
3257/// ```
3258#[no_mangle]
3259pub unsafe extern "C" fn xmlCreateDocParserCtxt(cur: *const xmlChar) -> *mut _xmlParserCtxt {
3260    // SAFETY: cur must be a valid null-terminated xmlChar string.
3261    if cur.is_null() {
3262        return ptr::null_mut();
3263    }
3264    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
3265    if ctxt.is_null() {
3266        return ptr::null_mut();
3267    }
3268    let len = crate::xml::string::xml_strlen(cur);
3269    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
3270    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
3271    ctxt
3272}
3273
3274/// Parse a document using an existing parser context.
3275///
3276/// # UPSTREAM-PARITY
3277///
3278/// ```c
3279/// int xmlParseDocument(xmlParserCtxtPtr ctxt);
3280/// ```
3281#[no_mangle]
3282pub unsafe extern "C" fn xmlParseDocument(ctxt: *mut _xmlParserCtxt) -> c_int {
3283    // SAFETY: ctxt must be a valid parser context.
3284    if ctxt.is_null() {
3285        return -1;
3286    }
3287    crate::xml::parser::helpers::parse_document(ctxt)
3288}
3289
3290/// Free a parser context.
3291///
3292/// # UPSTREAM-PARITY
3293///
3294/// ```c
3295/// void xmlFreeParserCtxt(xmlParserCtxtPtr ctxt);
3296/// ```
3297#[no_mangle]
3298pub unsafe extern "C" fn xmlFreeParserCtxt(ctxt: *mut _xmlParserCtxt) {
3299    if ctxt.is_null() {
3300        return;
3301    }
3302    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
3303}
3304
3305/// Set parser options.
3306///
3307/// # UPSTREAM-PARITY
3308///
3309/// ```c
3310/// int xmlCtxtUseOptions(xmlParserCtxtPtr ctxt, int options);
3311/// ```
3312#[no_mangle]
3313pub unsafe extern "C" fn xmlCtxtUseOptions(ctxt: *mut _xmlParserCtxt, options: c_int) -> c_int {
3314    if ctxt.is_null() {
3315        return -1;
3316    }
3317    // Phase 1: STUB
3318    unsafe {
3319        (*ctxt).options = options;
3320    }
3321    0
3322}
3323
3324/// Parse a well-balanced chunk (for push parsing).
3325///
3326/// # UPSTREAM-PARITY
3327///
3328/// ```c
3329/// xmlParserErrors xmlParseChunk(xmlParserCtxtPtr ctxt,
3330///                               const char *chunk, int size, int terminate);
3331/// ```
3332#[no_mangle]
3333pub unsafe extern "C" fn xmlParseChunk(
3334    ctxt: *mut _xmlParserCtxt,
3335    chunk: *const c_char,
3336    size: c_int,
3337    terminate: c_int,
3338) -> c_int {
3339    // SAFETY: ctxt must be a valid parser context.
3340    // chunk may be NULL if terminate is set (finalize without data).
3341    if ctxt.is_null() {
3342        return -1;
3343    }
3344    crate::xml::parser::helpers::parse_chunk(ctxt, chunk, size, terminate)
3345}
3346
3347/// Create a memory parser input buffer.
3348///
3349/// # UPSTREAM-PARITY
3350///
3351/// ```c
3352/// xmlParserInputBufferPtr xmlParserInputBufferCreateMem(const char *buffer, int size, int enc);
3353/// ```
3354#[no_mangle]
3355pub unsafe extern "C" fn xmlParserInputBufferCreateMem(
3356    buffer: *const c_char,
3357    size: c_int,
3358    enc: c_int,
3359) -> *mut _xmlParserInputBuffer {
3360    // SAFETY: buffer must be valid with at least `size` bytes.
3361    if buffer.is_null() || size <= 0 {
3362        return ptr::null_mut();
3363    }
3364    crate::xml::parser::helpers::alloc_parser_input_buffer()
3365}
3366
3367/// Create a file parser input buffer.
3368///
3369/// # UPSTREAM-PARITY
3370///
3371/// ```c
3372/// xmlParserInputBufferPtr xmlParserInputBufferCreateFilename(const char *URI, int enc);
3373/// ```
3374#[no_mangle]
3375pub unsafe extern "C" fn xmlParserInputBufferCreateFilename(
3376    URI: *const c_char,
3377    enc: c_int,
3378) -> *mut _xmlParserInputBuffer {
3379    // SAFETY: URI must be a valid C string or NULL.
3380    if URI.is_null() {
3381        return ptr::null_mut();
3382    }
3383    crate::xml::parser::helpers::alloc_parser_input_buffer()
3384}
3385
3386/// Create an I/O parser input buffer.
3387///
3388/// # UPSTREAM-PARITY
3389///
3390/// ```c
3391/// xmlParserInputBufferPtr xmlParserInputBufferCreateIO(
3392///     xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
3393///     void *ioctx, int enc);
3394/// ```
3395#[no_mangle]
3396pub unsafe extern "C" fn xmlParserInputBufferCreateIO(
3397    ioread: Option<xmlInputReadCallback>,
3398    ioclose: Option<xmlInputCloseCallback>,
3399    ioctx: *mut c_void,
3400    enc: c_int,
3401) -> *mut _xmlParserInputBuffer {
3402    // SAFETY: ioread must be a valid callback if Some. ioctx may be NULL.
3403    let buf = crate::xml::parser::helpers::alloc_parser_input_buffer();
3404    if !buf.is_null() {
3405        (*buf).readcallback = ioread;
3406        (*buf).closecallback = ioclose;
3407        (*buf).context = ioctx;
3408    }
3409    buf
3410}
3411
3412/// Free a parser input buffer.
3413///
3414/// # UPSTREAM-PARITY
3415///
3416/// ```c
3417/// void xmlFreeParserInputBuffer(xmlParserInputBufferPtr buf);
3418/// ```
3419#[no_mangle]
3420pub unsafe extern "C" fn xmlFreeParserInputBuffer(buf: *mut _xmlParserInputBuffer) {
3421    if buf.is_null() {
3422        return;
3423    }
3424    crate::xml::parser::helpers::free_parser_input_buffer(buf);
3425}
3426
3427/// Create a new parser input.
3428///
3429/// # UPSTREAM-PARITY
3430///
3431/// ```c
3432/// xmlParserInputPtr xmlNewInputFromFile(xmlParserCtxtPtr ctxt, const char *filename);
3433/// ```
3434#[no_mangle]
3435pub unsafe extern "C" fn xmlNewInputFromFile(
3436    ctxt: *mut _xmlParserCtxt,
3437    filename: *const c_char,
3438) -> *mut _xmlParserInput {
3439    // SAFETY: filename must be a valid C string. ctxt may be NULL.
3440    // This function allocates a _xmlParserInput. The caller owns it.
3441    // Note: The InputBuffer backing data is NOT leaked here (no ctxt._private
3442    // to store it). Use xmlCreateFileParserCtxt + xmlParseDocument instead.
3443    if filename.is_null() {
3444        return ptr::null_mut();
3445    }
3446    crate::xml::parser::helpers::alloc_parser_input_buffer() as *mut _xmlParserInput
3447}
3448
3449/// Free a parser input.
3450///
3451/// # UPSTREAM-PARITY
3452///
3453/// ```c
3454/// void xmlFreeInputStream(xmlParserInputPtr input);
3455/// ```
3456#[no_mangle]
3457pub unsafe extern "C" fn xmlFreeInputStream(input: *mut _xmlParserInput) {
3458    if input.is_null() {
3459        return;
3460    }
3461    crate::xml::parser::helpers::free_parser_input(input);
3462}
3463
3464// ═══════════════════════════════════════════════════════════════════════════════
3465// 8. I/O
3466// ═══════════════════════════════════════════════════════════════════════════════
3467
3468/// Create an output buffer for a file.
3469///
3470/// # UPSTREAM-PARITY
3471///
3472/// ```c
3473/// xmlOutputBufferPtr xmlOutputBufferCreateFilename(const char *URI,
3474///                                                  xmlCharEncodingHandlerPtr encoder,
3475///                                                  int compression);
3476/// ```
3477#[no_mangle]
3478pub unsafe extern "C" fn xmlOutputBufferCreateFilename(
3479    URI: *const c_char,
3480    encoder: *mut c_void,
3481    compression: c_int,
3482) -> *mut _xmlOutputBuffer {
3483    let _ = compression;
3484    if URI.is_null() {
3485        return ptr::null_mut();
3486    }
3487    crate::xml::io::output_buffer_create_filename(
3488        URI,
3489        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3490        0,
3491    )
3492}
3493
3494/// Create an output buffer for a file descriptor.
3495///
3496/// # UPSTREAM-PARITY
3497///
3498/// ```c
3499/// xmlOutputBufferPtr xmlOutputBufferCreateFd(int fd,
3500///                                            xmlCharEncodingHandlerPtr encoder);
3501/// ```
3502#[no_mangle]
3503pub unsafe extern "C" fn xmlOutputBufferCreateFd(
3504    fd: c_int,
3505    encoder: *mut c_void,
3506) -> *mut _xmlOutputBuffer {
3507    if fd < 0 {
3508        return ptr::null_mut();
3509    }
3510    crate::xml::io::output_buffer_create_fd(
3511        fd,
3512        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3513    )
3514}
3515
3516/// Create an output buffer from I/O callbacks.
3517///
3518/// # UPSTREAM-PARITY
3519///
3520/// ```c
3521/// xmlOutputBufferPtr xmlOutputBufferCreateIO(
3522///     xmlOutputWriteCallback iowrite, xmlOutputCloseCallback ioclose,
3523///     void *ioctx, xmlCharEncodingHandlerPtr encoder);
3524/// ```
3525#[no_mangle]
3526pub unsafe extern "C" fn xmlOutputBufferCreateIO(
3527    iowrite: Option<xmlOutputWriteCallback>,
3528    ioclose: Option<xmlOutputCloseCallback>,
3529    ioctx: *mut c_void,
3530    encoder: *mut c_void,
3531) -> *mut _xmlOutputBuffer {
3532    crate::xml::io::output_buffer_create_io(
3533        iowrite,
3534        ioclose,
3535        ioctx,
3536        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3537    )
3538}
3539
3540/// Free an output buffer.
3541///
3542/// # UPSTREAM-PARITY
3543///
3544/// ```c
3545/// void xmlOutputBufferClose(xmlOutputBufferPtr out);
3546/// ```
3547#[no_mangle]
3548pub unsafe extern "C" fn xmlOutputBufferClose(out: *mut _xmlOutputBuffer) -> c_int {
3549    if out.is_null() {
3550        return -1;
3551    }
3552    crate::xml::io::output_buffer_close(out)
3553}
3554
3555/// Flush an output buffer.
3556///
3557/// # UPSTREAM-PARITY
3558///
3559/// ```c
3560/// int xmlOutputBufferFlush(xmlOutputBufferPtr out);
3561/// ```
3562#[no_mangle]
3563pub unsafe extern "C" fn xmlOutputBufferFlush(out: *mut _xmlOutputBuffer) -> c_int {
3564    if out.is_null() {
3565        return -1;
3566    }
3567    crate::xml::io::output_buffer_flush(out)
3568}
3569
3570/// Write to an output buffer.
3571///
3572/// # UPSTREAM-PARITY
3573///
3574/// ```c
3575/// int xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *data);
3576/// ```
3577#[no_mangle]
3578pub unsafe extern "C" fn xmlOutputBufferWrite(
3579    out: *mut _xmlOutputBuffer,
3580    len: c_int,
3581    data: *const c_char,
3582) -> c_int {
3583    if out.is_null() || data.is_null() || len <= 0 {
3584        return -1;
3585    }
3586    crate::xml::io::output_buffer_write(out, len, data)
3587}
3588
3589/// Write a string to an output buffer.
3590///
3591/// # UPSTREAM-PARITY
3592///
3593/// ```c
3594/// int xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str);
3595/// ```
3596#[no_mangle]
3597pub unsafe extern "C" fn xmlOutputBufferWriteString(
3598    out: *mut _xmlOutputBuffer,
3599    str: *const c_char,
3600) -> c_int {
3601    if str.is_null() {
3602        return 0;
3603    }
3604    unsafe { xmlOutputBufferWrite(out, xmlStrlen(str as *const xmlChar), str) }
3605}
3606
3607/// Allocate an output buffer with no I/O target (upstream xmlAllocOutputBuffer).
3608///
3609/// # UPSTREAM-PARITY
3610///
3611/// ```c
3612/// xmlOutputBufferPtr xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder);
3613/// ```
3614#[no_mangle]
3615pub unsafe extern "C" fn xmlAllocOutputBuffer(encoder: *mut c_void) -> *mut _xmlOutputBuffer {
3616    crate::xml::io::output_buffer_create(
3617        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3618    )
3619}
3620
3621/// Create an output buffer that writes into a `_xmlBuffer` (upstream
3622/// xmlOutputBufferCreateBuffer).
3623///
3624/// # UPSTREAM-PARITY
3625///
3626/// ```c
3627/// xmlOutputBufferPtr xmlOutputBufferCreateBuffer(xmlBufferPtr buffer,
3628///                                                xmlCharEncodingHandlerPtr encoder);
3629/// ```
3630///
3631/// # SAFETY
3632///
3633/// - `buffer` must be a valid `_xmlBuffer`.
3634#[no_mangle]
3635pub unsafe extern "C" fn xmlOutputBufferCreateBuffer(
3636    buffer: *mut crate::abi::structs::_xmlBuffer,
3637    encoder: *mut c_void,
3638) -> *mut _xmlOutputBuffer {
3639    crate::xml::io::output_buffer_create_buffer(
3640        buffer,
3641        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3642    )
3643}
3644
3645/// Create an output buffer writing to a `FILE *` (upstream
3646/// xmlOutputBufferCreateFile): the FILE is the I/O context with a
3647/// write callback wrapping `fwrite` and a close callback wrapping `fflush`.
3648///
3649/// # SAFETY
3650///
3651/// - `file` must be a valid `FILE *` or NULL.
3652#[no_mangle]
3653pub unsafe extern "C" fn xmlOutputBufferCreateFile(
3654    file: *mut libc::FILE,
3655    encoder: *mut c_void,
3656) -> *mut _xmlOutputBuffer {
3657    crate::xml::io::output_buffer_create_file(
3658        file,
3659        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
3660    )
3661}
3662
3663/// Get the current content of an output buffer (upstream xmlOutputBufferGetContent).
3664///
3665/// # SAFETY
3666///
3667/// - `out` must be a valid output buffer.
3668#[no_mangle]
3669pub unsafe extern "C" fn xmlOutputBufferGetContent(out: *mut _xmlOutputBuffer) -> *const c_char {
3670    crate::xml::io::output_buffer_get_content(out) as *const c_char
3671}
3672
3673/// Get the number of bytes currently in the output buffer (upstream
3674/// xmlOutputBufferGetSize).
3675///
3676/// # SAFETY
3677///
3678/// - `out` must be a valid output buffer.
3679#[no_mangle]
3680pub unsafe extern "C" fn xmlOutputBufferGetSize(out: *mut _xmlOutputBuffer) -> c_int {
3681    crate::xml::io::output_buffer_get_size(out)
3682}
3683
3684/// Write to an output buffer, escaping special characters with the given
3685/// escape function (upstream xmlOutputBufferWriteEscape).
3686///
3687/// # SAFETY
3688///
3689/// - `out` must be a valid output buffer; `str` a NUL-terminated string;
3690///   `escaping` a valid escape callback or NULL.
3691#[no_mangle]
3692pub unsafe extern "C" fn xmlOutputBufferWriteEscape(
3693    out: *mut _xmlOutputBuffer,
3694    str: *const xmlChar,
3695    escaping: Option<xmlCharEncodingOutputFunc>,
3696) -> c_int {
3697    if out.is_null() || str.is_null() {
3698        return -1;
3699    }
3700    crate::xml::io::output_buffer_write_escape(out, str, escaping)
3701}
3702
3703/// Global default `xmlOutputBufferCreateFilename` callback
3704/// (upstream xmlOutputBufferCreateFilenameDefault).
3705static mut OUTPUT_CREATE_FILENAME_DEFAULT: Option<
3706    unsafe extern "C" fn(
3707        *const c_char,
3708        *mut crate::abi::structs::_xmlCharEncodingHandler,
3709        c_int,
3710    ) -> *mut _xmlOutputBuffer,
3711> = None;
3712
3713/// Set/query the default output-buffer filename callback
3714/// (upstream xmlOutputBufferCreateFilenameDefault).
3715///
3716/// # SAFETY
3717///
3718/// - `func` must be a valid function pointer or NULL.
3719#[no_mangle]
3720pub unsafe extern "C" fn xmlOutputBufferCreateFilenameDefault(
3721    func: Option<
3722        unsafe extern "C" fn(
3723            *const c_char,
3724            *mut crate::abi::structs::_xmlCharEncodingHandler,
3725            c_int,
3726        ) -> *mut _xmlOutputBuffer,
3727    >,
3728) -> Option<
3729    unsafe extern "C" fn(
3730        *const c_char,
3731        *mut crate::abi::structs::_xmlCharEncodingHandler,
3732        c_int,
3733    ) -> *mut _xmlOutputBuffer,
3734> {
3735    let old = unsafe { OUTPUT_CREATE_FILENAME_DEFAULT };
3736    if func.is_some() {
3737        unsafe { OUTPUT_CREATE_FILENAME_DEFAULT = func };
3738    }
3739    old
3740}
3741
3742/// `__xmlOutputBufferCreateFilename` — accessor returning a pointer to the
3743/// default callback (upstream xmlIO.c).
3744#[no_mangle]
3745pub unsafe extern "C" fn __xmlOutputBufferCreateFilename() -> *mut Option<
3746    unsafe extern "C" fn(
3747        *const c_char,
3748        *mut crate::abi::structs::_xmlCharEncodingHandler,
3749        c_int,
3750    ) -> *mut _xmlOutputBuffer,
3751> {
3752    unsafe { core::ptr::addr_of_mut!(OUTPUT_CREATE_FILENAME_DEFAULT) }
3753}
3754
3755// ═══════════════════════════════════════════════════════════════════════════════
3756// 9. Dictionary
3757// ═══════════════════════════════════════════════════════════════════════════════
3758
3759/// Create a new dictionary.
3760///
3761/// # UPSTREAM-PARITY
3762///
3763/// ```c
3764/// xmlDictPtr xmlDictCreate(void);
3765/// ```
3766#[no_mangle]
3767pub extern "C" fn xmlDictCreate() -> *mut c_void {
3768    unsafe { crate::xml::dictionary::dict_create() as *mut c_void }
3769}
3770
3771/// Create a sub-dictionary.
3772///
3773/// # UPSTREAM-PARITY
3774///
3775/// ```c
3776/// xmlDictPtr xmlDictCreateSub(xmlDictPtr sub);
3777/// ```
3778#[no_mangle]
3779pub extern "C" fn xmlDictCreateSub(sub: *mut c_void) -> *mut c_void {
3780    unsafe {
3781        crate::xml::dictionary::dict_create_sub(sub as *mut crate::xml::dictionary::Dict)
3782            as *mut c_void
3783    }
3784}
3785
3786/// Look up a string in the dictionary.
3787///
3788/// # UPSTREAM-PARITY
3789///
3790/// ```c
3791/// const xmlChar *xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len);
3792/// ```
3793///
3794/// Returns an interned string pointer (valid as long as the dictionary exists).
3795/// - If `len` < 0, `name` must be null-terminated.
3796/// - If `len` >= 0, exactly `len` bytes are used.
3797#[no_mangle]
3798pub unsafe extern "C" fn xmlDictLookup(
3799    dict: *mut c_void,
3800    name: *const xmlChar,
3801    len: c_int,
3802) -> *const xmlChar {
3803    unsafe {
3804        crate::xml::dictionary::dict_lookup(dict as *mut crate::xml::dictionary::Dict, name, len)
3805    }
3806}
3807
3808/// Check if a string exists in the dictionary.
3809///
3810/// # UPSTREAM-PARITY
3811///
3812/// ```c
3813/// const xmlChar *xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len);
3814/// ```
3815#[no_mangle]
3816pub unsafe extern "C" fn xmlDictExists(
3817    dict: *mut c_void,
3818    name: *const xmlChar,
3819    len: c_int,
3820) -> *const xmlChar {
3821    unsafe {
3822        crate::xml::dictionary::dict_exists(dict as *mut crate::xml::dictionary::Dict, name, len)
3823    }
3824}
3825
3826/// Query dictionary size.
3827///
3828/// # UPSTREAM-PARITY
3829///
3830/// ```c
3831/// unsigned int xmlDictSize(const xmlDictPtr dict);
3832/// ```
3833#[no_mangle]
3834pub extern "C" fn xmlDictSize(dict: *const c_void) -> c_uint {
3835    unsafe {
3836        crate::xml::dictionary::dict_size(dict as *const crate::xml::dictionary::Dict) as c_uint
3837    }
3838}
3839
3840/// Free a dictionary.
3841///
3842/// # UPSTREAM-PARITY
3843///
3844/// ```c
3845/// void xmlDictFree(xmlDictPtr dict);
3846/// ```
3847#[no_mangle]
3848pub extern "C" fn xmlDictFree(dict: *mut c_void) {
3849    unsafe { crate::xml::dictionary::dict_free(dict as *mut crate::xml::dictionary::Dict) }
3850}
3851
3852/// Set the dictionary size limit.
3853///
3854/// # UPSTREAM-PARITY
3855///
3856/// ```c
3857/// unsigned int xmlDictSetLimit(xmlDictPtr dict, unsigned int limit);
3858/// ```
3859#[no_mangle]
3860pub extern "C" fn xmlDictSetLimit(dict: *mut c_void, limit: c_uint) -> c_uint {
3861    unsafe {
3862        crate::xml::dictionary::dict_set_limit(
3863            dict as *mut crate::xml::dictionary::Dict,
3864            limit as usize,
3865        ) as c_uint
3866    }
3867}
3868
3869/// Get current dictionary usage.
3870///
3871/// # UPSTREAM-PARITY
3872///
3873/// ```c
3874/// unsigned int xmlDictGetUsage(const xmlDictPtr dict);
3875/// ```
3876#[no_mangle]
3877pub extern "C" fn xmlDictGetUsage(dict: *const c_void) -> c_uint {
3878    unsafe {
3879        crate::xml::dictionary::dict_get_usage(dict as *mut crate::xml::dictionary::Dict) as c_uint
3880    }
3881}
3882
3883// ═══════════════════════════════════════════════════════════════════════════════
3884// 10. Hash Table
3885// ═══════════════════════════════════════════════════════════════════════════════
3886
3887/// Create a new hash table.
3888///
3889/// # UPSTREAM-PARITY
3890///
3891/// ```c
3892/// xmlHashTablePtr xmlHashCreate(int size);
3893/// ```
3894#[no_mangle]
3895pub extern "C" fn xmlHashCreate(size: c_int) -> *mut c_void {
3896    unsafe { crate::xml::hash::hash_create(size) as *mut c_void }
3897}
3898
3899/// Create a new hash table with a dictionary.
3900///
3901/// # UPSTREAM-PARITY
3902///
3903/// ```c
3904/// xmlHashTablePtr xmlHashCreateDict(int size, xmlDictPtr dict);
3905/// ```
3906#[no_mangle]
3907pub extern "C" fn xmlHashCreateDict(size: c_int, dict: *mut c_void) -> *mut c_void {
3908    unsafe { crate::xml::hash::hash_create_dict(size, dict) as *mut c_void }
3909}
3910
3911/// Free a hash table.
3912///
3913/// # UPSTREAM-PARITY
3914///
3915/// ```c
3916/// void xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f);
3917/// ```
3918#[no_mangle]
3919pub extern "C" fn xmlHashFree(
3920    table: *mut c_void,
3921    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
3922) {
3923    unsafe { crate::xml::hash::hash_free(table as *mut crate::xml::hash::HashTable, f) }
3924}
3925
3926/// Add an entry to a hash table.
3927///
3928/// # UPSTREAM-PARITY
3929///
3930/// ```c
3931/// int xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata);
3932/// ```
3933#[no_mangle]
3934pub unsafe extern "C" fn xmlHashAddEntry(
3935    table: *mut c_void,
3936    name: *const xmlChar,
3937    userdata: *mut c_void,
3938) -> c_int {
3939    unsafe {
3940        crate::xml::hash::hash_add_entry(table as *mut crate::xml::hash::HashTable, name, userdata)
3941    }
3942}
3943
3944/// Add a 2-key entry.
3945///
3946/// # UPSTREAM-PARITY
3947///
3948/// ```c
3949/// int xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
3950///                      const xmlChar *name2, void *userdata);
3951/// ```
3952#[no_mangle]
3953pub unsafe extern "C" fn xmlHashAddEntry2(
3954    table: *mut c_void,
3955    name: *const xmlChar,
3956    name2: *const xmlChar,
3957    userdata: *mut c_void,
3958) -> c_int {
3959    unsafe {
3960        crate::xml::hash::hash_add_entry2(
3961            table as *mut crate::xml::hash::HashTable,
3962            name,
3963            name2,
3964            userdata,
3965        )
3966    }
3967}
3968
3969/// Add a 3-key entry.
3970///
3971/// # UPSTREAM-PARITY
3972///
3973/// ```c
3974/// int xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
3975///                      const xmlChar *name2, const xmlChar *name3, void *userdata);
3976/// ```
3977#[no_mangle]
3978pub unsafe extern "C" fn xmlHashAddEntry3(
3979    table: *mut c_void,
3980    name: *const xmlChar,
3981    name2: *const xmlChar,
3982    name3: *const xmlChar,
3983    userdata: *mut c_void,
3984) -> c_int {
3985    unsafe {
3986        crate::xml::hash::hash_add_entry3(
3987            table as *mut crate::xml::hash::HashTable,
3988            name,
3989            name2,
3990            name3,
3991            userdata,
3992        )
3993    }
3994}
3995
3996/// Update or add an entry.
3997///
3998/// # UPSTREAM-PARITY
3999///
4000/// ```c
4001/// int xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
4002///                        void *userdata, xmlHashDeallocator f);
4003/// ```
4004#[no_mangle]
4005pub unsafe extern "C" fn xmlHashUpdateEntry(
4006    table: *mut c_void,
4007    name: *const xmlChar,
4008    userdata: *mut c_void,
4009    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4010) -> c_int {
4011    unsafe {
4012        crate::xml::hash::hash_update_entry(
4013            table as *mut crate::xml::hash::HashTable,
4014            name,
4015            userdata,
4016            f,
4017        )
4018    }
4019}
4020
4021/// Update or add a 2-key entry.
4022#[no_mangle]
4023pub unsafe extern "C" fn xmlHashUpdateEntry2(
4024    table: *mut c_void,
4025    name: *const xmlChar,
4026    name2: *const xmlChar,
4027    userdata: *mut c_void,
4028    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4029) -> c_int {
4030    unsafe {
4031        crate::xml::hash::hash_update_entry2(
4032            table as *mut crate::xml::hash::HashTable,
4033            name,
4034            name2,
4035            userdata,
4036            f,
4037        )
4038    }
4039}
4040
4041/// Update or add a 3-key entry.
4042#[no_mangle]
4043pub unsafe extern "C" fn xmlHashUpdateEntry3(
4044    table: *mut c_void,
4045    name: *const xmlChar,
4046    name2: *const xmlChar,
4047    name3: *const xmlChar,
4048    userdata: *mut c_void,
4049    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4050) -> c_int {
4051    unsafe {
4052        crate::xml::hash::hash_update_entry3(
4053            table as *mut crate::xml::hash::HashTable,
4054            name,
4055            name2,
4056            name3,
4057            userdata,
4058            f,
4059        )
4060    }
4061}
4062
4063/// Look up an entry.
4064///
4065/// # UPSTREAM-PARITY
4066///
4067/// ```c
4068/// void *xmlHashLookup(xmlHashTablePtr table, const xmlChar *name);
4069/// ```
4070#[no_mangle]
4071pub unsafe extern "C" fn xmlHashLookup(table: *mut c_void, name: *const xmlChar) -> *mut c_void {
4072    unsafe { crate::xml::hash::hash_lookup(table as *mut crate::xml::hash::HashTable, name) }
4073}
4074
4075/// Look up a 2-key entry.
4076#[no_mangle]
4077pub unsafe extern "C" fn xmlHashLookup2(
4078    table: *mut c_void,
4079    name: *const xmlChar,
4080    name2: *const xmlChar,
4081) -> *mut c_void {
4082    unsafe {
4083        crate::xml::hash::hash_lookup2(table as *mut crate::xml::hash::HashTable, name, name2)
4084    }
4085}
4086
4087/// Look up a 3-key entry.
4088#[no_mangle]
4089pub unsafe extern "C" fn xmlHashLookup3(
4090    table: *mut c_void,
4091    name: *const xmlChar,
4092    name2: *const xmlChar,
4093    name3: *const xmlChar,
4094) -> *mut c_void {
4095    unsafe {
4096        crate::xml::hash::hash_lookup3(
4097            table as *mut crate::xml::hash::HashTable,
4098            name,
4099            name2,
4100            name3,
4101        )
4102    }
4103}
4104
4105/// Get the size of a hash table.
4106///
4107/// # UPSTREAM-PARITY
4108///
4109/// ```c
4110/// int xmlHashSize(xmlHashTablePtr table);
4111/// ```
4112#[no_mangle]
4113pub extern "C" fn xmlHashSize(table: *mut c_void) -> c_int {
4114    unsafe { crate::xml::hash::hash_size(table as *mut crate::xml::hash::HashTable) }
4115}
4116
4117/// Remove an entry.
4118///
4119/// # UPSTREAM-PARITY
4120///
4121/// ```c
4122/// int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
4123///                        xmlHashDeallocator f);
4124/// ```
4125#[no_mangle]
4126pub unsafe extern "C" fn xmlHashRemoveEntry(
4127    table: *mut c_void,
4128    name: *const xmlChar,
4129    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4130) -> c_int {
4131    unsafe {
4132        crate::xml::hash::hash_remove_entry(table as *mut crate::xml::hash::HashTable, name, f)
4133    }
4134}
4135
4136/// Remove a 2-key entry.
4137#[no_mangle]
4138pub unsafe extern "C" fn xmlHashRemoveEntry2(
4139    table: *mut c_void,
4140    name: *const xmlChar,
4141    name2: *const xmlChar,
4142    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4143) -> c_int {
4144    unsafe {
4145        crate::xml::hash::hash_remove_entry2(
4146            table as *mut crate::xml::hash::HashTable,
4147            name,
4148            name2,
4149            f,
4150        )
4151    }
4152}
4153
4154/// Remove a 3-key entry.
4155#[no_mangle]
4156pub unsafe extern "C" fn xmlHashRemoveEntry3(
4157    table: *mut c_void,
4158    name: *const xmlChar,
4159    name2: *const xmlChar,
4160    name3: *const xmlChar,
4161    f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
4162) -> c_int {
4163    unsafe {
4164        crate::xml::hash::hash_remove_entry3(
4165            table as *mut crate::xml::hash::HashTable,
4166            name,
4167            name2,
4168            name3,
4169            f,
4170        )
4171    }
4172}
4173
4174/// Scan a hash table with a scanner function.
4175///
4176/// # UPSTREAM-PARITY
4177///
4178/// ```c
4179/// void xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data);
4180/// ```
4181#[no_mangle]
4182pub extern "C" fn xmlHashScan(table: *mut c_void, f: Option<xmlHashScanner>, data: *mut c_void) {
4183    unsafe { crate::xml::hash::hash_scan(table as *mut crate::xml::hash::HashTable, f, data) }
4184}
4185
4186/// Scan a hash table with a full scanner function.
4187#[no_mangle]
4188pub extern "C" fn xmlHashScanFull(
4189    table: *mut c_void,
4190    f: Option<xmlHashScannerFull>,
4191    data: *mut c_void,
4192) {
4193    unsafe { crate::xml::hash::hash_scan_full(table as *mut crate::xml::hash::HashTable, f, data) }
4194}
4195
4196/// Copy a hash table.
4197///
4198/// # UPSTREAM-PARITY
4199///
4200/// ```c
4201/// xmlHashTablePtr xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f);
4202/// ```
4203#[no_mangle]
4204pub extern "C" fn xmlHashCopy(
4205    table: *mut c_void,
4206    f: Option<unsafe extern "C" fn(*mut c_void, *const xmlChar) -> *mut c_void>,
4207) -> *mut c_void {
4208    unsafe {
4209        crate::xml::hash::hash_copy(table as *mut crate::xml::hash::HashTable, f) as *mut c_void
4210    }
4211}
4212
4213// ═══════════════════════════════════════════════════════════════════════════════
4214// 11. List
4215// ═══════════════════════════════════════════════════════════════════════════════
4216
4217/// Create a new list.
4218///
4219/// # UPSTREAM-PARITY
4220///
4221/// ```c
4222/// xmlListPtr xmlListCreate(xmlListDeallocator deallocator,
4223///                          xmlListDataCompare compare);
4224/// ```
4225#[no_mangle]
4226pub extern "C" fn xmlListCreate(
4227    deallocator: Option<unsafe extern "C" fn(*mut c_void)>,
4228    compare: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
4229) -> *mut c_void {
4230    unsafe { crate::xml::list::list_create(deallocator, compare) as *mut c_void }
4231}
4232
4233/// Delete a list.
4234///
4235/// # UPSTREAM-PARITY
4236///
4237/// ```c
4238/// void xmlListDelete(xmlListPtr list);
4239/// ```
4240#[no_mangle]
4241pub extern "C" fn xmlListDelete(list: *mut c_void) {
4242    unsafe { crate::xml::list::list_delete(list as *mut crate::xml::list::List) }
4243}
4244
4245/// Search a list.
4246///
4247/// # UPSTREAM-PARITY
4248///
4249/// ```c
4250/// void *xmlListSearch(xmlListPtr list, void *data);
4251/// ```
4252#[no_mangle]
4253pub extern "C" fn xmlListSearch(list: *mut c_void, data: *mut c_void) -> *mut c_void {
4254    unsafe { crate::xml::list::list_search(list as *mut crate::xml::list::List, data) }
4255}
4256
4257/// Walk a list.
4258///
4259/// # UPSTREAM-PARITY
4260///
4261/// ```c
4262/// void xmlListWalk(xmlListPtr list, xmlListWalker walker, void *data);
4263/// ```
4264#[no_mangle]
4265pub extern "C" fn xmlListWalk(
4266    list: *mut c_void,
4267    walker: Option<unsafe extern "C" fn(*mut c_void, *mut c_void) -> c_int>,
4268    data: *mut c_void,
4269) {
4270    unsafe { crate::xml::list::list_walk(list as *mut crate::xml::list::List, walker, data) }
4271}
4272
4273/// Push to back.
4274///
4275/// # UPSTREAM-PARITY
4276///
4277/// ```c
4278/// int xmlListPushBack(xmlListPtr list, void *data);
4279/// ```
4280#[no_mangle]
4281pub extern "C" fn xmlListPushBack(list: *mut c_void, data: *mut c_void) -> c_int {
4282    unsafe { crate::xml::list::list_push_back(list as *mut crate::xml::list::List, data) }
4283}
4284
4285/// Push to front.
4286///
4287/// # UPSTREAM-PARITY
4288///
4289/// ```c
4290/// int xmlListPushFront(xmlListPtr list, void *data);
4291/// ```
4292#[no_mangle]
4293pub extern "C" fn xmlListPushFront(list: *mut c_void, data: *mut c_void) -> c_int {
4294    unsafe { crate::xml::list::list_push_front(list as *mut crate::xml::list::List, data) }
4295}
4296
4297/// Pop from back.
4298#[no_mangle]
4299pub extern "C" fn xmlListPopBack(list: *mut c_void) {
4300    unsafe { crate::xml::list::list_pop_back(list as *mut crate::xml::list::List) }
4301}
4302
4303/// Pop from front.
4304#[no_mangle]
4305pub extern "C" fn xmlListPopFront(list: *mut c_void) {
4306    unsafe { crate::xml::list::list_pop_front(list as *mut crate::xml::list::List) }
4307}
4308
4309/// Insert into sorted list.
4310///
4311/// # UPSTREAM-PARITY
4312///
4313/// ```c
4314/// int xmlListInsert(xmlListPtr list, void *data);
4315/// ```
4316#[no_mangle]
4317pub extern "C" fn xmlListInsert(list: *mut c_void, data: *mut c_void) -> c_int {
4318    unsafe { crate::xml::list::list_insert(list as *mut crate::xml::list::List, data) }
4319}
4320
4321/// Append to list.
4322#[no_mangle]
4323pub extern "C" fn xmlListAppend(list: *mut c_void, data: *mut c_void) -> c_int {
4324    unsafe { crate::xml::list::list_append(list as *mut crate::xml::list::List, data) }
4325}
4326
4327/// Remove first matching element.
4328#[no_mangle]
4329pub extern "C" fn xmlListRemoveFirst(list: *mut c_void, data: *mut c_void) -> c_int {
4330    unsafe { crate::xml::list::list_remove_first(list as *mut crate::xml::list::List, data) }
4331}
4332
4333/// Remove last matching element.
4334#[no_mangle]
4335pub extern "C" fn xmlListRemoveLast(list: *mut c_void, data: *mut c_void) -> c_int {
4336    unsafe { crate::xml::list::list_remove_last(list as *mut crate::xml::list::List, data) }
4337}
4338
4339/// Remove all matching elements.
4340#[no_mangle]
4341pub extern "C" fn xmlListRemoveAll(list: *mut c_void, data: *mut c_void) -> c_int {
4342    unsafe { crate::xml::list::list_remove_all(list as *mut crate::xml::list::List, data) }
4343}
4344
4345/// Clear a list.
4346#[no_mangle]
4347pub extern "C" fn xmlListClear(list: *mut c_void) {
4348    unsafe { crate::xml::list::list_clear(list as *mut crate::xml::list::List) }
4349}
4350
4351/// Check if list is empty.
4352///
4353/// # UPSTREAM-PARITY
4354///
4355/// ```c
4356/// int xmlListEmpty(xmlListPtr list);
4357/// ```
4358#[no_mangle]
4359pub extern "C" fn xmlListEmpty(list: *mut c_void) -> c_int {
4360    unsafe { crate::xml::list::list_empty(list as *mut crate::xml::list::List) }
4361}
4362
4363/// Get front element.
4364///
4365/// # UPSTREAM-PARITY
4366///
4367/// ```c
4368/// void *xmlListFront(xmlListPtr list);
4369/// ```
4370#[no_mangle]
4371pub extern "C" fn xmlListFront(list: *mut c_void) -> *mut c_void {
4372    unsafe { crate::xml::list::list_front(list as *mut crate::xml::list::List) }
4373}
4374
4375/// Get back element.
4376///
4377/// # UPSTREAM-PARITY
4378///
4379/// ```c
4380/// void *xmlListBack(xmlListPtr list);
4381/// ```
4382#[no_mangle]
4383pub extern "C" fn xmlListBack(list: *mut c_void) -> *mut c_void {
4384    unsafe { crate::xml::list::list_back(list as *mut crate::xml::list::List) }
4385}
4386
4387/// Get list size.
4388///
4389/// # UPSTREAM-PARITY
4390///
4391/// ```c
4392/// int xmlListSize(xmlListPtr list);
4393/// ```
4394#[no_mangle]
4395pub extern "C" fn xmlListSize(list: *mut c_void) -> c_int {
4396    unsafe { crate::xml::list::list_size(list as *mut crate::xml::list::List) }
4397}
4398
4399/// Sort a list.
4400#[no_mangle]
4401pub extern "C" fn xmlListSort(list: *mut c_void) {
4402    unsafe { crate::xml::list::list_sort(list as *mut crate::xml::list::List) }
4403}
4404
4405/// Reverse a list.
4406#[no_mangle]
4407pub extern "C" fn xmlListReverse(list: *mut c_void) {
4408    unsafe { crate::xml::list::list_reverse(list as *mut crate::xml::list::List) }
4409}
4410
4411/// Reverse a list in-place.
4412#[no_mangle]
4413pub extern "C" fn xmlListReverseSplice(list: *mut c_void, list2: *mut c_void) {
4414    unsafe {
4415        crate::xml::list::list_reverse_splice(
4416            list as *mut crate::xml::list::List,
4417            list2 as *mut crate::xml::list::List,
4418        )
4419    }
4420}
4421
4422/// Merge two sorted lists.
4423#[no_mangle]
4424pub extern "C" fn xmlListMerge(list: *mut c_void, list2: *mut c_void) {
4425    unsafe {
4426        crate::xml::list::list_merge(
4427            list as *mut crate::xml::list::List,
4428            list2 as *mut crate::xml::list::List,
4429        )
4430    }
4431}
4432/// Return the last element of a list (upstream list.h).
4433///
4434/// # UPSTREAM-PARITY
4435///
4436/// ```c
4437/// void *xmlListEnd(xmlListPtr l);
4438/// ```
4439#[no_mangle]
4440pub unsafe extern "C" fn xmlListEnd(l: *mut c_void) -> *mut c_void {
4441    crate::xml::list::list_end(l as *mut crate::xml::list::List)
4442}
4443
4444/// Reverse-search a list (upstream list.h).
4445///
4446/// # UPSTREAM-PARITY
4447///
4448/// ```c
4449/// void *xmlListReverseSearch(xmlListPtr l, void *data);
4450/// ```
4451#[no_mangle]
4452pub unsafe extern "C" fn xmlListReverseSearch(l: *mut c_void, data: *mut c_void) -> *mut c_void {
4453    crate::xml::list::list_reverse_search(l as *mut crate::xml::list::List, data)
4454}
4455
4456/// Walk a list in reverse (upstream list.h).
4457///
4458/// # UPSTREAM-PARITY
4459///
4460/// ```c
4461/// void xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, void *data);
4462/// ```
4463#[no_mangle]
4464pub unsafe extern "C" fn xmlListReverseWalk(
4465    l: *mut c_void,
4466    walker: Option<unsafe extern "C" fn(*mut c_void, *mut c_void) -> c_int>,
4467    data: *mut c_void,
4468) {
4469    crate::xml::list::list_reverse_walk(l as *mut crate::xml::list::List, walker, data)
4470}
4471
4472/// Duplicate a list (upstream list.h).
4473///
4474/// # UPSTREAM-PARITY
4475///
4476/// ```c
4477/// xmlListPtr xmlListDup(xmlListPtr l);
4478/// ```
4479#[no_mangle]
4480pub unsafe extern "C" fn xmlListDup(l: *mut c_void) -> *mut c_void {
4481    crate::xml::list::list_dup(l as *mut crate::xml::list::List) as *mut c_void
4482}
4483
4484/// Copy a list with a copier (upstream list.h).
4485///
4486/// # UPSTREAM-PARITY
4487///
4488/// ```c
4489/// int xmlListCopy(xmlListPtr l, xmlListCopier copier);
4490/// ```
4491#[no_mangle]
4492pub unsafe extern "C" fn xmlListCopy(
4493    l: *mut c_void,
4494    copier: Option<unsafe extern "C" fn(*mut c_void) -> *mut c_void>,
4495) -> c_int {
4496    crate::xml::list::list_copy(l as *mut crate::xml::list::List, copier)
4497}
4498
4499/// Return the data of a link (upstream list.h).
4500///
4501/// # UPSTREAM-PARITY
4502///
4503/// ```c
4504/// void *xmlLinkGetData(xmlLinkPtr lk);
4505/// ```
4506#[no_mangle]
4507pub unsafe extern "C" fn xmlLinkGetData(lk: *mut c_void) -> *mut c_void {
4508    crate::xml::list::link_get_data(lk)
4509}
4510
4511// ═══════════════════════════════════════════════════════════════════════════════
4512// 12. Buffer
4513// ═══════════════════════════════════════════════════════════════════════════════
4514
4515/// Create a new buffer.
4516///
4517/// # UPSTREAM-PARITY
4518///
4519/// ```c
4520/// xmlBufferPtr xmlBufferCreate(void);
4521/// ```
4522#[no_mangle]
4523pub extern "C" fn xmlBufferCreate() -> *mut _xmlBuffer {
4524    crate::xml::io::buf_create(-1)
4525}
4526
4527/// Create a new buffer of a given size.
4528///
4529/// # UPSTREAM-PARITY
4530///
4531/// ```c
4532/// xmlBufferPtr xmlBufferCreateSize(size_t size);
4533/// ```
4534#[no_mangle]
4535pub extern "C" fn xmlBufferCreateSize(size: usize) -> *mut _xmlBuffer {
4536    crate::xml::io::buf_create(size as c_int)
4537}
4538
4539/// Create a buffer from a static string.
4540///
4541/// # UPSTREAM-PARITY
4542///
4543/// ```c
4544/// xmlBufferPtr xmlBufferCreateStatic(void *mem, size_t size);
4545/// ```
4546#[no_mangle]
4547pub extern "C" fn xmlBufferCreateStatic(mem: *mut c_void, size: usize) -> *mut _xmlBuffer {
4548    if mem.is_null() || size == 0 {
4549        return ptr::null_mut();
4550    }
4551    crate::xml::io::buf_create_static(mem as *const xmlChar, size as c_int)
4552}
4553
4554/// Free a buffer.
4555///
4556/// # UPSTREAM-PARITY
4557///
4558/// ```c
4559/// void xmlBufferFree(xmlBufferPtr buf);
4560/// ```
4561#[no_mangle]
4562pub extern "C" fn xmlBufferFree(buf: *mut _xmlBuffer) {
4563    crate::xml::io::buf_free(buf)
4564}
4565
4566/// Empty a buffer.
4567///
4568/// # UPSTREAM-PARITY
4569///
4570/// ```c
4571/// void xmlBufferEmpty(xmlBufferPtr buf);
4572/// ```
4573#[no_mangle]
4574pub extern "C" fn xmlBufferEmpty(buf: *mut _xmlBuffer) {
4575    if buf.is_null() {
4576        return;
4577    }
4578    unsafe {
4579        if !(*buf).content.is_null() {
4580            *(*buf).content = 0;
4581        }
4582        (*buf).use_ = 0;
4583    }
4584}
4585
4586/// Get buffer content.
4587///
4588/// # UPSTREAM-PARITY
4589///
4590/// ```c
4591/// xmlChar *xmlBufferContent(const xmlBuffer *buf);
4592/// ```
4593#[no_mangle]
4594pub extern "C" fn xmlBufferContent(buf: *const _xmlBuffer) -> *mut xmlChar {
4595    crate::xml::io::buf_content(buf as *mut _xmlBuffer)
4596}
4597
4598/// Get buffer length.
4599///
4600/// # UPSTREAM-PARITY
4601///
4602/// ```c
4603/// int xmlBufferLength(const xmlBuffer *buf);
4604/// ```
4605#[no_mangle]
4606pub extern "C" fn xmlBufferLength(buf: *const _xmlBuffer) -> c_int {
4607    crate::xml::io::buf_length(buf as *mut _xmlBuffer)
4608}
4609
4610/// Write to a buffer.
4611///
4612/// # UPSTREAM-PARITY
4613///
4614/// ```c
4615/// int xmlBufferAdd(xmlBufferPtr buf, const xmlChar *str, int len);
4616/// ```
4617#[no_mangle]
4618pub unsafe extern "C" fn xmlBufferAdd(
4619    buf: *mut _xmlBuffer,
4620    str: *const xmlChar,
4621    len: c_int,
4622) -> c_int {
4623    crate::xml::io::buf_add(buf, str, len)
4624}
4625
4626/// Write to a buffer at a position.
4627///
4628/// # UPSTREAM-PARITY
4629///
4630/// ```c
4631/// int xmlBufferAddHead(xmlBufferPtr buf, const xmlChar *str, int len);
4632/// ```
4633#[no_mangle]
4634pub unsafe extern "C" fn xmlBufferAddHead(
4635    buf: *mut _xmlBuffer,
4636    str: *const xmlChar,
4637    len: c_int,
4638) -> c_int {
4639    crate::xml::io::buf_add_head(buf, str, len)
4640}
4641
4642/// Write a C string to a buffer.
4643///
4644/// # UPSTREAM-PARITY
4645///
4646/// ```c
4647/// int xmlBufferCat(xmlBufferPtr buf, const xmlChar *str);
4648/// ```
4649#[no_mangle]
4650pub unsafe extern "C" fn xmlBufferCat(buf: *mut _xmlBuffer, str: *const xmlChar) -> c_int {
4651    if str.is_null() {
4652        return -1;
4653    }
4654    let len = crate::xml::string::xml_strlen(str) as c_int;
4655    crate::xml::io::buf_add(buf, str, len)
4656}
4657
4658/// Set buffer allocation scheme.
4659///
4660/// # UPSTREAM-PARITY
4661///
4662/// ```c
4663/// void xmlBufferSetAllocationScheme(xmlBufferPtr buf,
4664///                                    xmlBufferAllocationScheme scheme);
4665/// ```
4666#[no_mangle]
4667pub extern "C" fn xmlBufferSetAllocationScheme(buf: *mut _xmlBuffer, scheme: c_int) {
4668    if buf.is_null() {
4669        return;
4670    }
4671    unsafe {
4672        (*buf).alloc = scheme;
4673    }
4674}
4675
4676/// Shrink buffer.
4677///
4678/// # UPSTREAM-PARITY
4679///
4680/// ```c
4681/// int xmlBufferShrink(xmlBufferPtr buf, int len);
4682/// ```
4683#[no_mangle]
4684pub extern "C" fn xmlBufferShrink(buf: *mut _xmlBuffer, len: c_int) -> c_int {
4685    if buf.is_null() || len <= 0 {
4686        return 0;
4687    }
4688    unsafe {
4689        let b = &mut *buf;
4690        let shrink_len = (len as c_uint).min(b.use_);
4691        if shrink_len > 0 {
4692            let remaining = b.use_ - shrink_len;
4693            if remaining > 0 {
4694                core::ptr::copy(
4695                    b.content.add(shrink_len as usize),
4696                    b.content,
4697                    remaining as usize,
4698                );
4699            }
4700            *b.content.add(remaining as usize) = 0;
4701            b.use_ = remaining;
4702        }
4703    }
4704    len
4705}
4706
4707/// Grow buffer.
4708///
4709/// # UPSTREAM-PARITY
4710///
4711/// ```c
4712/// int xmlBufferGrow(xmlBufferPtr buf, int len);
4713/// ```
4714#[no_mangle]
4715pub extern "C" fn xmlBufferGrow(buf: *mut _xmlBuffer, len: c_int) -> c_int {
4716    if buf.is_null() || len <= 0 {
4717        return 0;
4718    }
4719    let cur_use = unsafe { (*buf).use_ };
4720    let new_size = cur_use + len as c_uint + 1;
4721    crate::xml::io::buf_grow(buf, new_size)
4722}
4723
4724/// Reserve buffer space.
4725///
4726/// # UPSTREAM-PARITY
4727///
4728/// ```c
4729/// int xmlBufferReserve(xmlBufferPtr buf, int len);
4730/// ```
4731#[no_mangle]
4732pub extern "C" fn xmlBufferReserve(buf: *mut _xmlBuffer, len: c_int) -> c_int {
4733    xmlBufferGrow(buf, len)
4734}
4735
4736/// Detach buffer content.
4737///
4738/// # UPSTREAM-PARITY
4739///
4740/// ```c
4741/// xmlChar *xmlBufferDetach(xmlBufferPtr buf);
4742/// ```
4743#[no_mangle]
4744pub extern "C" fn xmlBufferDetach(buf: *mut _xmlBuffer) -> *mut xmlChar {
4745    if buf.is_null() {
4746        return ptr::null_mut();
4747    }
4748    unsafe {
4749        let content = (*buf).content;
4750        (*buf).content = ptr::null_mut();
4751        (*buf).use_ = 0;
4752        (*buf).size = 0;
4753        content
4754    }
4755}
4756
4757// ═══════════════════════════════════════════════════════════════════════════════
4758// 13. Encoding
4759// ═══════════════════════════════════════════════════════════════════════════════
4760
4761/// Get encoding from a name string.
4762///
4763/// # UPSTREAM-PARITY
4764///
4765/// ```c
4766/// xmlCharEncoding xmlGetCharEncoding(const char *name);
4767/// ```
4768#[no_mangle]
4769pub extern "C" fn xmlGetCharEncoding(name: *const c_char) -> c_int {
4770    if name.is_null() {
4771        return 0; // XML_CHAR_ENCODING_NONE
4772    }
4773    let name_bytes = unsafe {
4774        let len = libc::strlen(name);
4775        core::slice::from_raw_parts(name as *const u8, len)
4776    };
4777    crate::xml::encoding::encoding_from_name(name_bytes) as c_int
4778}
4779
4780/// Find an encoding handler.
4781///
4782/// # UPSTREAM-PARITY
4783///
4784/// ```c
4785/// xmlCharEncodingHandlerPtr xmlFindCharEncodingHandler(const char *name);
4786/// ```
4787#[no_mangle]
4788pub extern "C" fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut c_void {
4789    if name.is_null() {
4790        return ptr::null_mut();
4791    }
4792    crate::xml::encoding::find_encoding_handler(name as *const xmlChar) as *mut c_void
4793}
4794
4795/// Close an encoding handler.
4796///
4797/// # UPSTREAM-PARITY
4798///
4799/// ```c
4800/// int xmlCharEncCloseFunc(xmlCharEncodingHandlerPtr handler);
4801/// ```
4802#[no_mangle]
4803pub extern "C" fn xmlCharEncCloseFunc(handler: *mut c_void) -> c_int {
4804    if handler.is_null() {
4805        return -1;
4806    }
4807    // Free the encoding handler
4808    unsafe {
4809        let h = handler as *mut crate::abi::structs::_xmlCharEncodingHandler;
4810        if !(*h).name.is_null() {
4811            crate::abi::allocator::xmlFree((*h).name as *mut c_void);
4812        }
4813        crate::abi::allocator::xmlFree(handler);
4814    }
4815    0
4816}
4817
4818/// Return the name of a character encoding (upstream encoding.h).
4819///
4820/// # UPSTREAM-PARITY
4821///
4822/// ```c
4823/// const char *xmlGetCharEncodingName(xmlCharEncoding enc);
4824/// ```
4825#[no_mangle]
4826pub extern "C" fn xmlGetCharEncodingName(enc: c_int) -> *const c_char {
4827    let e: crate::abi::types::xmlCharEncoding = unsafe { core::mem::transmute(enc) };
4828    crate::xml::encoding::xmlGetCharEncodingName(e)
4829}
4830
4831/// Parse an encoding name into an xmlCharEncoding value (upstream encoding.h).
4832///
4833/// # UPSTREAM-PARITY
4834///
4835/// ```c
4836/// xmlCharEncoding xmlParseCharEncoding(const char *name);
4837/// ```
4838///
4839/// Returns the encoding value or XML_CHAR_ENCODING_ERROR (-1).
4840#[no_mangle]
4841pub extern "C" fn xmlParseCharEncoding(name: *const c_char) -> c_int {
4842    crate::xml::encoding::xmlParseCharEncoding(name)
4843}
4844
4845/// Add an encoding alias (upstream encoding.h).
4846///
4847/// # UPSTREAM-PARITY
4848///
4849/// ```c
4850/// int xmlAddEncodingAlias(const char *name, const char *alias);
4851/// ```
4852#[no_mangle]
4853pub extern "C" fn xmlAddEncodingAlias(name: *const c_char, alias: *const c_char) -> c_int {
4854    crate::xml::encoding::add_encoding_alias(name, alias)
4855}
4856
4857/// Delete an encoding alias (upstream encoding.h).
4858///
4859/// # UPSTREAM-PARITY
4860///
4861/// ```c
4862/// int xmlDelEncodingAlias(const char *alias);
4863/// ```
4864#[no_mangle]
4865pub extern "C" fn xmlDelEncodingAlias(alias: *const c_char) -> c_int {
4866    crate::xml::encoding::del_encoding_alias(alias)
4867}
4868
4869/// Look up an encoding alias (upstream encoding.h).
4870///
4871/// # UPSTREAM-PARITY
4872///
4873/// ```c
4874/// const char *xmlGetEncodingAlias(const char *alias);
4875/// ```
4876#[no_mangle]
4877pub extern "C" fn xmlGetEncodingAlias(alias: *const c_char) -> *const c_char {
4878    crate::xml::encoding::get_encoding_alias(alias)
4879}
4880
4881/// Clean up the encoding alias table (upstream encoding.h).
4882///
4883/// # UPSTREAM-PARITY
4884///
4885/// ```c
4886/// void xmlCleanupEncodingAliases(void);
4887/// ```
4888#[no_mangle]
4889pub extern "C" fn xmlCleanupEncodingAliases() {
4890    crate::xml::encoding::cleanup_encoding_aliases();
4891}
4892
4893/// Convert the input buffer using an encoding handler (upstream encoding.h).
4894///
4895/// # UPSTREAM-PARITY
4896///
4897/// ```c
4898/// int xmlCharEncInFunc(xmlCharEncodingHandler *handler,
4899///                      xmlBufferPtr out, xmlBufferPtr in);
4900/// ```
4901#[no_mangle]
4902pub extern "C" fn xmlCharEncInFunc(
4903    handler: *mut c_void,
4904    out: *mut c_void,
4905    in_: *mut c_void,
4906) -> c_int {
4907    crate::xml::encoding::xmlCharEncInFunc(
4908        handler as *mut crate::abi::structs::_xmlCharEncodingHandler,
4909        out as *mut crate::abi::structs::_xmlBuffer,
4910        in_ as *mut crate::abi::structs::_xmlBuffer,
4911    )
4912}
4913
4914/// Convert the output buffer using an encoding handler (upstream encoding.h).
4915///
4916/// # UPSTREAM-PARITY
4917///
4918/// ```c
4919/// int xmlCharEncOutFunc(xmlCharEncodingHandler *handler,
4920///                       xmlBufferPtr out, xmlBufferPtr in);
4921/// ```
4922#[no_mangle]
4923pub extern "C" fn xmlCharEncOutFunc(
4924    handler: *mut c_void,
4925    out: *mut c_void,
4926    in_: *mut c_void,
4927) -> c_int {
4928    crate::xml::encoding::xmlCharEncOutFunc(
4929        handler as *mut crate::abi::structs::_xmlCharEncodingHandler,
4930        out as *mut crate::abi::structs::_xmlBuffer,
4931        in_ as *mut crate::abi::structs::_xmlBuffer,
4932    )
4933}
4934
4935/// Create a new encoding handler (upstream encoding.h).
4936///
4937/// # UPSTREAM-PARITY
4938///
4939/// ```c
4940/// xmlCharEncodingHandlerPtr xmlNewCharEncodingHandler(
4941///     const char *name, xmlCharEncodingInputFunc input,
4942///     xmlCharEncodingOutputFunc output);
4943/// ```
4944#[no_mangle]
4945pub extern "C" fn xmlNewCharEncodingHandler(
4946    name: *const c_char,
4947    input: crate::abi::callbacks::xmlCharEncodingInputFunc,
4948    output: crate::abi::callbacks::xmlCharEncodingOutputFunc,
4949) -> *mut c_void {
4950    crate::xml::encoding::xmlNewCharEncodingHandler(name, input, output) as *mut c_void
4951}
4952
4953/// Initialize the built-in encoding handlers (upstream encoding.h).
4954///
4955/// # UPSTREAM-PARITY
4956///
4957/// ```c
4958/// void xmlInitCharEncodingHandlers(void);
4959/// ```
4960#[no_mangle]
4961pub extern "C" fn xmlInitCharEncodingHandlers() {
4962    crate::xml::encoding::xmlInitCharEncodingHandlers();
4963}
4964
4965/// Clean up the encoding handlers (upstream encoding.h).
4966///
4967/// # UPSTREAM-PARITY
4968///
4969/// ```c
4970/// void xmlCleanupCharEncodingHandlers(void);
4971/// ```
4972#[no_mangle]
4973pub extern "C" fn xmlCleanupCharEncodingHandlers() {
4974    crate::xml::encoding::xmlCleanupCharEncodingHandlers();
4975}
4976
4977/// Convert an input buffer's encoding.
4978///
4979/// # UPSTREAM-PARITY
4980///
4981/// ```c
4982/// int xmlCharEncInput(xmlParserInputBufferPtr input, int to);
4983/// ```
4984#[no_mangle]
4985pub unsafe extern "C" fn xmlCharEncInput(input: *mut _xmlParserInputBuffer, _to: c_int) -> c_int {
4986    if input.is_null() {
4987        return -1;
4988    }
4989    let handler = (*input).encoder as *mut crate::abi::structs::_xmlCharEncodingHandler;
4990    if handler.is_null() {
4991        return -1;
4992    }
4993    let raw = (*input).raw as *mut crate::abi::structs::_xmlBuffer;
4994    let buf = (*input).buffer as *mut crate::abi::structs::_xmlBuffer;
4995    if raw.is_null() || buf.is_null() {
4996        return -1;
4997    }
4998    crate::xml::encoding::char_enc_in(handler, buf, raw)
4999}
5000
5001/// Convert an output buffer's encoding.
5002///
5003/// # UPSTREAM-PARITY
5004///
5005/// ```c
5006/// int xmlCharEncOutput(xmlOutputBufferPtr output, int to);
5007/// ```
5008#[no_mangle]
5009pub unsafe extern "C" fn xmlCharEncOutput(output: *mut _xmlOutputBuffer, _to: c_int) -> c_int {
5010    if output.is_null() {
5011        return -1;
5012    }
5013    let handler = (*output).encoder as *mut crate::abi::structs::_xmlCharEncodingHandler;
5014    if handler.is_null() {
5015        return -1;
5016    }
5017    let buf = (*output).buffer as *mut crate::abi::structs::_xmlBuffer;
5018    let conv = (*output).conv as *mut crate::abi::structs::_xmlBuffer;
5019    if buf.is_null() || conv.is_null() {
5020        return -1;
5021    }
5022    crate::xml::encoding::char_enc_out(handler, conv, buf)
5023}
5024
5025// ═══════════════════════════════════════════════════════════════════════════════
5026// URI
5027// ═══════════════════════════════════════════════════════════════════════════════
5028
5029/// Parse a URI string.
5030///
5031/// # UPSTREAM-PARITY
5032///
5033/// ```c
5034/// xmlURIPtr xmlParseURI(const char *str);
5035/// ```
5036#[no_mangle]
5037pub unsafe extern "C" fn xmlParseURI(str: *const c_char) -> *mut c_void {
5038    crate::xml::uri::xmlParseURI(str)
5039}
5040
5041/// Parse a URI string (raw version).
5042///
5043/// # UPSTREAM-PARITY
5044///
5045/// ```c
5046/// xmlURIPtr xmlParseURIRaw(const char *str, int raw);
5047/// ```
5048#[no_mangle]
5049pub unsafe extern "C" fn xmlParseURIRaw(str: *const c_char, raw: c_int) -> *mut c_void {
5050    let _ = raw;
5051    crate::xml::uri::xmlParseURI(str)
5052}
5053
5054/// Free a URI structure.
5055///
5056/// # UPSTREAM-PARITY
5057///
5058/// ```c
5059/// void xmlFreeURI(xmlURIPtr uri);
5060/// ```
5061#[no_mangle]
5062pub unsafe extern "C" fn xmlFreeURI(uri: *mut c_void) {
5063    crate::xml::uri::xmlFreeURI(uri)
5064}
5065
5066/// Create an empty URI.
5067///
5068/// # UPSTREAM-PARITY
5069///
5070/// ```c
5071/// xmlURIPtr xmlCreateURI(void);
5072/// ```
5073#[no_mangle]
5074pub extern "C" fn xmlCreateURI() -> *mut c_void {
5075    crate::xml::uri::xmlCreateURI()
5076}
5077
5078/// Save a URI structure to a string.
5079///
5080/// # UPSTREAM-PARITY
5081///
5082/// ```c
5083/// xmlChar *xmlSaveUri(xmlURIPtr uri);
5084/// ```
5085#[no_mangle]
5086pub unsafe extern "C" fn xmlSaveUri(uri: *mut c_void) -> *mut xmlChar {
5087    crate::xml::uri::xmlSaveUri(uri)
5088}
5089
5090/// Parse a URI string into an existing URI structure (upstream uri.h).
5091///
5092/// # UPSTREAM-PARITY
5093///
5094/// ```c
5095/// int xmlParseURIReference(xmlURIPtr uri, const char *str);
5096/// ```
5097///
5098/// Returns 0 on success, -1 on failure (the URI structure is left
5099/// untouched on failure).
5100///
5101/// # Safety
5102///
5103/// - `uri` must be a valid pointer from `xmlParseURI`/`xmlCreateURI`.
5104/// - `str` must be a valid null-terminated C string.
5105#[no_mangle]
5106pub unsafe extern "C" fn xmlParseURIReference(uri: *mut c_void, str: *const c_char) -> c_int {
5107    crate::xml::uri::xmlParseURIReference(uri, str)
5108}
5109
5110/// Normalize a URI path in place (upstream uri.h).
5111///
5112/// # UPSTREAM-PARITY
5113///
5114/// ```c
5115/// int xmlNormalizeURIPath(char *path);
5116/// ```
5117///
5118/// Returns 0 on success, -1 if the path is NULL, not absolute, or contains
5119/// `..` segments that climb above the root.
5120///
5121/// # Safety
5122///
5123/// `path` must be a valid writable null-terminated C string buffer.
5124#[no_mangle]
5125pub unsafe extern "C" fn xmlNormalizeURIPath(path: *mut c_char) -> c_int {
5126    crate::xml::uri::xmlNormalizeURIPath(path)
5127}
5128
5129/// Escape a URI string.
5130///
5131/// # UPSTREAM-PARITY
5132///
5133/// ```c
5134/// xmlChar *xmlURIEscapeStr(const xmlChar *str, const xmlChar *list);
5135/// ```
5136#[no_mangle]
5137pub unsafe extern "C" fn xmlURIEscapeStr(
5138    str: *const xmlChar,
5139    list: *const xmlChar,
5140) -> *mut xmlChar {
5141    crate::xml::uri::xmlURIEscapeStr(str, list)
5142}
5143
5144/// Unescape a URI string.
5145///
5146/// # UPSTREAM-PARITY
5147///
5148/// ```c
5149/// char *xmlURIUnescapeString(const char *str, int len, char *target);
5150/// ```
5151#[no_mangle]
5152pub unsafe extern "C" fn xmlURIUnescapeString(
5153    str: *const c_char,
5154    len: c_int,
5155    target: *mut c_char,
5156) -> *mut c_char {
5157    crate::xml::uri::xmlURIUnescapeString(str, len, target)
5158}
5159
5160// ═══════════════════════════════════════════════════════════════════════════════
5161// 14. XPath
5162// ═══════════════════════════════════════════════════════════════════════════════
5163
5164// ── Helper functions ────────────────────────────────────────────────────
5165
5166/// Convert an internal `XPathValue` to a C ABI `_xmlXPathObject`.
5167///
5168/// The returned pointer is heap-allocated via `xmlMallocZero` and must be
5169/// freed with `xmlXPathFreeObject`.
5170///
5171/// # Safety
5172///
5173/// Must be called from a context where `xmlMalloc` is safe to call.
5174unsafe fn xpath_to_object(val: XPathValue) -> *mut _xmlXPathObject {
5175    let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
5176    if obj.is_null() {
5177        return ptr::null_mut();
5178    }
5179    match val {
5180        XPathValue::NodeSet(ns) => {
5181            (*obj).type_ = xmlXPathObjectType::XPATH_NODESET as c_int;
5182            (*obj).nodesetval = ns.to_raw() as *mut c_void;
5183        }
5184        XPathValue::Boolean(b) => {
5185            (*obj).type_ = xmlXPathObjectType::XPATH_BOOLEAN as c_int;
5186            (*obj).boolval = if b { 1 } else { 0 };
5187        }
5188        XPathValue::Number(n) => {
5189            (*obj).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
5190            (*obj).floatval = n;
5191        }
5192        XPathValue::String(s) => {
5193            (*obj).type_ = xmlXPathObjectType::XPATH_STRING as c_int;
5194            let bytes = s.as_bytes();
5195            let len = bytes.len();
5196            let buf = xmlMalloc(len + 1) as *mut xmlChar;
5197            if !buf.is_null() {
5198                ptr::copy_nonoverlapping(bytes.as_ptr(), buf, len);
5199                *buf.add(len) = 0; // null terminator
5200            }
5201            (*obj).stringval = buf;
5202        }
5203    }
5204    obj
5205}
5206
5207/// Extract an internal `XPathValue` from a C ABI `_xmlXPathObject`.
5208///
5209/// # Safety
5210///
5211/// `obj` must be a valid, non-null pointer to a properly initialised
5212/// `_xmlXPathObject`.
5213unsafe fn object_to_xpathvalue(obj: *mut _xmlXPathObject) -> XPathValue {
5214    let typ = (*obj).type_;
5215    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
5216        let ns_ptr = (*obj).nodesetval as *mut _xmlNodeSet;
5217        if ns_ptr.is_null() {
5218            return XPathValue::NodeSet(NodeSet::new());
5219        }
5220        let node_nr = (*ns_ptr).nodeNr;
5221        let node_tab = (*ns_ptr).nodeTab;
5222        let mut ns = NodeSet::new();
5223        if !node_tab.is_null() {
5224            for i in 0..node_nr as isize {
5225                let node = *node_tab.add(i as usize);
5226                ns.push(node);
5227            }
5228        }
5229        XPathValue::NodeSet(ns)
5230    } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
5231        XPathValue::Boolean((*obj).boolval != 0)
5232    } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
5233        XPathValue::Number((*obj).floatval)
5234    } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
5235        let s_ptr = (*obj).stringval;
5236        if s_ptr.is_null() {
5237            XPathValue::String(String::new())
5238        } else {
5239            let s = CStr::from_ptr(s_ptr as *const c_char)
5240                .to_string_lossy()
5241                .into_owned();
5242            XPathValue::String(s)
5243        }
5244    } else if typ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
5245        // A result tree fragment: node-set containing the fragment's
5246        // document node (matching how global RTF variables are bound), so
5247        // local RTF variables stringify to their text and remain navigable
5248        // via exsl:node-set.
5249        let frag_doc = (*obj).nodesetval as *mut _xmlDoc;
5250        if frag_doc.is_null() {
5251            XPathValue::NodeSet(NodeSet::new())
5252        } else {
5253            let mut ns = NodeSet::new();
5254            ns.push(frag_doc as *mut _xmlNode);
5255            XPathValue::NodeSet(ns)
5256        }
5257    } else {
5258        // Undefined / unknown type — return boolean false as a safe default.
5259        XPathValue::Boolean(false)
5260    }
5261}
5262
5263/// Public wrapper for `xpath_to_object` (used by the XPath export bridge).
5264///
5265/// # Safety
5266///
5267/// - `val` is consumed and converted into a heap-allocated `_xmlXPathObject`.
5268pub unsafe fn xpath_to_object_pub(val: XPathValue) -> *mut _xmlXPathObject {
5269    xpath_to_object(val)
5270}
5271
5272/// Public wrapper for `object_to_xpathvalue` (used by the XSLT engine).
5273///
5274/// # Safety
5275///
5276/// `obj` must be a valid, non-null pointer to a properly initialised
5277/// `_xmlXPathObject`.
5278pub unsafe fn object_to_xpathvalue_pub(obj: *mut _xmlXPathObject) -> XPathValue {
5279    object_to_xpathvalue(obj)
5280}
5281
5282// ── Compiled expression registry ────────────────────────────────────────
5283//
5284// Compiled XPath expressions are opaque pointers returned by xmlXPathCompile.
5285// We store them in a global registry keyed by a monotonically increasing ID.
5286
5287static COMPILED_EXPRS: Lazy<Mutex<HashMap<u64, Box<CompiledExpr>>>> =
5288    Lazy::new(|| Mutex::new(HashMap::new()));
5289static NEXT_COMPILED_KEY: Lazy<Mutex<u64>> = Lazy::new(|| Mutex::new(1));
5290
5291/// Accessor for the compiled-expression registry (used by the XPath export
5292/// bridge for `xmlXPathCompiledEval` / `xmlXPathCompiledEvalToBoolean`).
5293pub(crate) fn xpath_compiled_registry() -> &'static Mutex<HashMap<u64, Box<CompiledExpr>>> {
5294    &COMPILED_EXPRS
5295}
5296
5297// ── C extension-function registry ──────────────────────────────────────
5298//
5299// C extension functions registered via xmlXPathRegisterFunc / RegisterFuncNS
5300// are stored here because the Rust XPathFunction signature is incompatible
5301// with the C xmlXPathFunction calling convention (the C function expects a
5302// parser context, not pre-evaluated argument slices). The registration is
5303// stored faithfully; invoking registered C functions from within the Rust
5304// evaluator requires a bridge that is not yet implemented.
5305
5306type CXPathFunc = unsafe extern "C" fn(*mut c_void, c_int);
5307
5308/// Wrapper around `*mut c_void` that implements `Send` + `Sync` so it can
5309/// be used as a key in a `Mutex`-protected global `HashMap`.
5310#[derive(Clone, Copy, PartialEq, Eq, Hash)]
5311struct SendSyncPtr(*mut c_void);
5312unsafe impl Send for SendSyncPtr {}
5313unsafe impl Sync for SendSyncPtr {}
5314
5315static C_FUNCTIONS: Lazy<Mutex<HashMap<(SendSyncPtr, String), CXPathFunc>>> =
5316    Lazy::new(|| Mutex::new(HashMap::new()));
5317
5318/// Look up a C-registered extension function for the context identified by
5319/// `extra` (the internal XPathContext pointer). Used by
5320/// `xmlXPathFunctionLookupNS`.
5321pub(crate) fn xpath_cfunc_lookup(extra: *mut c_void, qualified: &str) -> Option<CXPathFunc> {
5322    C_FUNCTIONS
5323        .lock()
5324        .get(&(SendSyncPtr(extra), qualified.to_string()))
5325        .copied()
5326}
5327
5328/// Drop every C extension-function registration belonging to the context
5329/// identified by `extra` (upstream `xmlXPathRegisteredFuncsCleanup`).
5330pub(crate) fn xpath_cfunc_cleanup(extra: *mut c_void) {
5331    C_FUNCTIONS.lock().retain(|(k, _), _| k.0 != extra);
5332}
5333
5334/// Rust-side wrapper that is registered in the internal XPathContext when a
5335/// C extension function is registered. It looks up the C function pointer and
5336/// attempts to call it, but the calling-convention mismatch means this is a
5337/// stub that returns an error for now.
5338fn c_func_stub(_ctx: &mut XPathContext, _args: &[XPathValue]) -> Result<XPathValue, String> {
5339    Err(
5340        "C extension function cannot be called from Rust evaluator without a parser-context bridge"
5341            .to_string(),
5342    )
5343}
5344
5345// ── Public API ─────────────────────────────────────────────────────────
5346
5347/// Create a new XPath context.
5348///
5349/// Allocates a `_xmlXPathContext` and an internal `XPathContext`, storing
5350/// the latter's pointer in the `extra` field.
5351///
5352/// # UPSTREAM-PARITY
5353///
5354/// ```c
5355/// xmlXPathContextPtr xmlXPathNewContext(xmlDocPtr doc);
5356/// ```
5357#[no_mangle]
5358pub unsafe extern "C" fn xmlXPathNewContext(doc: *mut _xmlDoc) -> *mut _xmlXPathContext {
5359    let ctxt = xmlMallocZero(size_of::<_xmlXPathContext>()) as *mut _xmlXPathContext;
5360    if ctxt.is_null() {
5361        return ptr::null_mut();
5362    }
5363
5364    // Initialise the C ABI context fields.
5365    (*ctxt).doc = doc;
5366    (*ctxt).node = ptr::null_mut();
5367    (*ctxt).contextSize = 1;
5368    (*ctxt).proximityPosition = 1;
5369
5370    // Create the internal XPathContext and store it in `extra`.
5371    let mut internal = Box::new(XPathContext::new(doc));
5372    // UPSTREAM-PARITY: the standard function library is implicitly available
5373    // in every context (upstream compiles it in; xmlXPathRegisterAllFunctions
5374    // is a no-op since 2.14.0). Without this, core-function calls such as
5375    // count() would fail as unknown functions.
5376    for (name, func) in crate::xml::xpath::functions::core_functions() {
5377        internal.register_function(&name, func);
5378    }
5379    (*ctxt).extra = Box::into_raw(internal) as *mut c_void;
5380
5381    ctxt
5382}
5383
5384/// Free an XPath context.
5385///
5386/// # UPSTREAM-PARITY
5387///
5388/// ```c
5389/// void xmlXPathFreeContext(xmlXPathContextPtr ctxt);
5390/// ```
5391#[no_mangle]
5392pub unsafe extern "C" fn xmlXPathFreeContext(ctxt: *mut _xmlXPathContext) {
5393    if ctxt.is_null() {
5394        return;
5395    }
5396    // Drop the internal XPathContext.
5397    if !(*ctxt).extra.is_null() {
5398        let _ = Box::from_raw((*ctxt).extra as *mut XPathContext);
5399        (*ctxt).extra = ptr::null_mut();
5400    }
5401    // Drop the registered-namespace C-string hash (xmlXPathNsLookup pointers).
5402    if !(*ctxt).nsHash.is_null() {
5403        drop(Box::from_raw(
5404            (*ctxt).nsHash as *mut HashMap<String, CString>,
5405        ));
5406        (*ctxt).nsHash = ptr::null_mut();
5407    }
5408    // Free the C ABI context struct.
5409    xmlFree(ctxt as *mut c_void);
5410}
5411
5412/// Evaluate an XPath expression.
5413///
5414/// # UPSTREAM-PARITY
5415///
5416/// ```c
5417/// xmlXPathObjectPtr xmlXPathEvalExpression(const xmlChar *str,
5418///                                          xmlXPathContextPtr ctxt);
5419/// ```
5420#[no_mangle]
5421pub unsafe extern "C" fn xmlXPathEvalExpression(
5422    str_: *const xmlChar,
5423    ctxt: *mut _xmlXPathContext,
5424) -> *mut _xmlXPathObject {
5425    if str_.is_null() || ctxt.is_null() {
5426        return ptr::null_mut();
5427    }
5428    let expr_str = match CStr::from_ptr(str_ as *const c_char).to_str() {
5429        Ok(s) => s,
5430        Err(_) => return ptr::null_mut(),
5431    };
5432    let internal = (*ctxt).extra as *mut XPathContext;
5433    if internal.is_null() {
5434        return ptr::null_mut();
5435    }
5436    let internal = &mut *internal;
5437
5438    match crate::xml::xpath::evaluate_str(expr_str, internal) {
5439        Some(val) => xpath_to_object(val),
5440        None => {
5441            // UPSTREAM-PARITY: libxml2 reports a failed compile/eval with
5442            // "XPath error : Invalid expression" (xmlXPathErr,
5443            // XPATH_EXPR_ERROR). The precise per-expression diagnostics are
5444            // tracked as RESIDUAL R-XPATH-ERRMSG.
5445            if internal.error.is_none() {
5446                internal.set_error("Invalid expression");
5447            }
5448            ptr::null_mut()
5449        }
5450    }
5451}
5452
5453/// Evaluate an XPath expression (simplified alias).
5454///
5455/// # UPSTREAM-PARITY
5456///
5457/// ```c
5458/// xmlXPathObjectPtr xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctxt);
5459/// ```
5460#[no_mangle]
5461pub unsafe extern "C" fn xmlXPathEval(
5462    str_: *const xmlChar,
5463    ctxt: *mut _xmlXPathContext,
5464) -> *mut _xmlXPathObject {
5465    xmlXPathEvalExpression(str_, ctxt)
5466}
5467
5468/// Free an XPath object.
5469///
5470/// Releases the internal members (string buffer or node-set) and then frees
5471/// the object struct itself.
5472///
5473/// # UPSTREAM-PARITY
5474///
5475/// ```c
5476/// void xmlXPathFreeObject(xmlXPathObjectPtr obj);
5477/// ```
5478#[no_mangle]
5479pub unsafe extern "C" fn xmlXPathFreeObject(obj: *mut _xmlXPathObject) {
5480    if obj.is_null() {
5481        return;
5482    }
5483    let typ = (*obj).type_;
5484    // Free string storage.
5485    if typ == xmlXPathObjectType::XPATH_STRING as c_int {
5486        if !(*obj).stringval.is_null() {
5487            xmlFree((*obj).stringval as *mut c_void);
5488            (*obj).stringval = ptr::null_mut();
5489        }
5490    }
5491    // Free node-set storage.
5492    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
5493        let ns = (*obj).nodesetval as *mut _xmlNodeSet;
5494        if !ns.is_null() {
5495            if !(*ns).nodeTab.is_null() {
5496                xmlFree((*ns).nodeTab as *mut c_void);
5497            }
5498            xmlFree(ns as *mut c_void);
5499        }
5500        (*obj).nodesetval = ptr::null_mut();
5501    }
5502    xmlFree(obj as *mut c_void);
5503}
5504
5505/// Copy an XPath object (deep copy).
5506///
5507/// # UPSTREAM-PARITY
5508///
5509/// ```c
5510/// xmlXPathObjectPtr xmlXPathObjectCopy(xmlXPathObjectPtr val);
5511/// ```
5512///
5513/// Oracle behavior: returns a newly allocated object with the same type
5514/// and value. Node-sets are copied element-by-element; strings are
5515/// duplicated; numbers and booleans are copied by value.
5516#[no_mangle]
5517pub unsafe extern "C" fn xmlXPathObjectCopy(val: *mut _xmlXPathObject) -> *mut _xmlXPathObject {
5518    if val.is_null() {
5519        return ptr::null_mut();
5520    }
5521    let typ = (*val).type_;
5522    let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
5523    if obj.is_null() {
5524        return ptr::null_mut();
5525    }
5526    (*obj).type_ = typ;
5527    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
5528        let src_ns = (*val).nodesetval as *mut _xmlNodeSet;
5529        if !src_ns.is_null() {
5530            let nr = (*src_ns).nodeNr;
5531            let ns = xmlMallocZero(size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
5532            if ns.is_null() {
5533                xmlFree(obj as *mut c_void);
5534                return ptr::null_mut();
5535            }
5536            (*ns).nodeNr = nr;
5537            (*ns).nodeMax = nr;
5538            if nr > 0 && !(*src_ns).nodeTab.is_null() {
5539                let tab = xmlMalloc((nr as usize) * core::mem::size_of::<*mut _xmlNode>())
5540                    as *mut *mut _xmlNode;
5541                if tab.is_null() {
5542                    xmlFree(ns as *mut c_void);
5543                    xmlFree(obj as *mut c_void);
5544                    return ptr::null_mut();
5545                }
5546                ptr::copy_nonoverlapping((*src_ns).nodeTab, tab, nr as usize);
5547                (*ns).nodeTab = tab;
5548            } else {
5549                (*ns).nodeTab = ptr::null_mut();
5550            }
5551            (*obj).nodesetval = ns as *mut c_void;
5552        }
5553    } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
5554        (*obj).boolval = (*val).boolval;
5555    } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
5556        (*obj).floatval = (*val).floatval;
5557    } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
5558        let src = (*val).stringval;
5559        if !src.is_null() {
5560            let len = libc::strlen(src as *const libc::c_char);
5561            let buf = xmlMalloc(len + 1) as *mut xmlChar;
5562            if !buf.is_null() {
5563                ptr::copy_nonoverlapping(src, buf, len);
5564                *buf.add(len) = 0;
5565            }
5566            (*obj).stringval = buf;
5567        }
5568    }
5569    obj
5570}
5571
5572/// Cast an XPath object to its string value.
5573///
5574/// Returns a newly allocated string (caller frees with `xmlFree`).
5575///
5576/// # UPSTREAM-PARITY
5577///
5578/// ```c
5579/// xmlChar *xmlXPathCastToString(xmlXPathObjectPtr val);
5580/// ```
5581#[no_mangle]
5582pub unsafe extern "C" fn xmlXPathCastToString(val: *mut _xmlXPathObject) -> *mut xmlChar {
5583    if val.is_null() {
5584        return ptr::null_mut();
5585    }
5586    let typ = (*val).type_;
5587    let mut result: Vec<u8> = Vec::new();
5588    if typ == xmlXPathObjectType::XPATH_STRING as c_int {
5589        if !(*val).stringval.is_null() {
5590            let len = libc::strlen((*val).stringval as *const libc::c_char);
5591            result.extend_from_slice(core::slice::from_raw_parts((*val).stringval, len));
5592        }
5593    } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
5594        // Number → string conversion per XPath 1.0 §4.2:
5595        // - NaN → "NaN"
5596        // - +0/-0 → "0"
5597        // - infinity → "Infinity" / "-Infinity"
5598        // - integer → decimal representation without exponent
5599        let n = (*val).floatval;
5600        result.extend_from_slice(xml_number_to_string(n).as_bytes());
5601    } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
5602        result.extend_from_slice(if (*val).boolval != 0 {
5603            b"true"
5604        } else {
5605            b"false"
5606        });
5607    } else if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
5608        // String value of a node-set is the string value of the first node
5609        // in document order (or empty if empty).
5610        let ns = (*val).nodesetval as *mut _xmlNodeSet;
5611        if !ns.is_null() && (*ns).nodeNr > 0 && !(*ns).nodeTab.is_null() {
5612            let node = *(*ns).nodeTab;
5613            if !node.is_null() {
5614                let content = crate::xml::tree::node_get_content(node);
5615                if !content.is_null() {
5616                    let len = libc::strlen(content as *const libc::c_char);
5617                    result.extend_from_slice(core::slice::from_raw_parts(content, len));
5618                    xmlFree(content as *mut c_void);
5619                }
5620            }
5621        }
5622    }
5623    // Allocate the C string.
5624    let buf = xmlMalloc(result.len() + 1) as *mut xmlChar;
5625    if buf.is_null() {
5626        return ptr::null_mut();
5627    }
5628    if !result.is_empty() {
5629        ptr::copy_nonoverlapping(result.as_ptr(), buf, result.len());
5630    }
5631    *buf.add(result.len()) = 0;
5632    buf
5633}
5634
5635/// Convert an XPath number to its string representation (XPath 1.0 §4.2).
5636///
5637/// Exposed as a helper; matches upstream `xmlXPathCastNumberToString`.
5638pub fn xml_number_to_string(n: f64) -> String {
5639    if n.is_nan() {
5640        return "NaN".to_string();
5641    }
5642    if n.is_infinite() {
5643        return if n > 0.0 {
5644            "Infinity".to_string()
5645        } else {
5646            "-Infinity".to_string()
5647        };
5648    }
5649    if n == 0.0 {
5650        // Both +0 and -0 serialize as "0" per XPath 1.0.
5651        return "0".to_string();
5652    }
5653    // Integer values serialize without a decimal point or exponent.
5654    if n.fract() == 0.0 && n.abs() < 1e15 {
5655        return format!("{:.0}", n);
5656    }
5657    // For other values, upstream uses %.15g-ish formatting with adjustments.
5658    // Try to find the shortest representation that round-trips (%.17g then
5659    // trim), which matches upstream's effective behavior for most inputs.
5660    let mut s = format!("{:.15}", n);
5661    // Trim trailing zeros and possible trailing dot.
5662    if s.contains('.') {
5663        while s.ends_with('0') {
5664            s.pop();
5665        }
5666        if s.ends_with('.') {
5667            s.pop();
5668        }
5669    }
5670    if s == "-0" {
5671        return "0".to_string();
5672    }
5673    s
5674}
5675
5676/// Cast a C string to a number per XPath 1.0 §4.2 conversion rules.
5677///
5678/// # UPSTREAM-PARITY
5679///
5680/// ```c
5681/// double xmlXPathCastStringToNumber(const xmlChar *val);
5682/// ```
5683#[no_mangle]
5684pub unsafe extern "C" fn xmlXPathCastStringToNumber(val: *const xmlChar) -> f64 {
5685    if val.is_null() {
5686        return f64::NAN;
5687    }
5688    let len = libc::strlen(val as *const libc::c_char);
5689    let bytes = core::slice::from_raw_parts(val, len);
5690    // Skip leading whitespace (XML whitespace per XPath 1.0).
5691    let mut i = 0;
5692    while i < bytes.len() && matches!(bytes[i], b' ' | b'\t' | b'\n' | b'\r') {
5693        i += 1;
5694    }
5695    let s = &bytes[i..];
5696    if s.is_empty() {
5697        return f64::NAN;
5698    }
5699    // Parse an optional sign.
5700    let (sign, rest) = match s[0] {
5701        b'+' => (1.0f64, &s[1..]),
5702        b'-' => (-1.0f64, &s[1..]),
5703        _ => (1.0f64, s),
5704    };
5705    if rest.is_empty() {
5706        return f64::NAN;
5707    }
5708    // Try full parse; if it fails (trailing junk), the value is NaN per spec.
5709    // XPath 1.0: a string that does not conform to the Number production is NaN.
5710    let num_str = core::str::from_utf8(rest);
5711    match num_str {
5712        Ok(s) => {
5713            // Accept only valid Number productions: digits with optional
5714            // fraction/exponent.
5715            let valid = is_xpath_number(s);
5716            if !valid {
5717                f64::NAN
5718            } else {
5719                s.trim()
5720                    .parse::<f64>()
5721                    .map(|v| v * sign)
5722                    .unwrap_or(f64::NAN)
5723            }
5724        }
5725        Err(_) => f64::NAN,
5726    }
5727}
5728
5729/// Check whether a string conforms to the XPath 1.0 Number production.
5730fn is_xpath_number(s: &str) -> bool {
5731    let b = s.as_bytes();
5732    if b.is_empty() {
5733        return false;
5734    }
5735    let mut i = 0;
5736    let mut saw_digit = false;
5737    while i < b.len() && b[i].is_ascii_digit() {
5738        saw_digit = true;
5739        i += 1;
5740    }
5741    if i < b.len() && b[i] == b'.' {
5742        i += 1;
5743        while i < b.len() && b[i].is_ascii_digit() {
5744            saw_digit = true;
5745            i += 1;
5746        }
5747    }
5748    if !saw_digit {
5749        return false;
5750    }
5751    if i < b.len() && (b[i] == b'e' || b[i] == b'E') {
5752        i += 1;
5753        if i < b.len() && (b[i] == b'+' || b[i] == b'-') {
5754            i += 1;
5755        }
5756        let mut saw_exp = false;
5757        while i < b.len() && b[i].is_ascii_digit() {
5758            saw_exp = true;
5759            i += 1;
5760        }
5761        if !saw_exp {
5762            return false;
5763        }
5764    }
5765    i == b.len()
5766}
5767
5768/// Compare two nodes in document order.
5769///
5770/// Returns negative if `node1` precedes `node2`, positive if it follows,
5771/// 0 if they are the same node.
5772///
5773/// # UPSTREAM-PARITY
5774///
5775/// ```c
5776/// int xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2);
5777/// ```
5778///
5779/// Oracle behavior: uses ancestor comparison — if one node is an ancestor
5780/// of the other, the ancestor comes first; otherwise the nearest common
5781/// ancestor's child order determines the result.
5782#[no_mangle]
5783pub unsafe extern "C" fn xmlXPathCmpNodes(node1: *mut _xmlNode, node2: *mut _xmlNode) -> c_int {
5784    if node1.is_null() || node2.is_null() {
5785        return 0;
5786    }
5787    if node1 == node2 {
5788        return 0;
5789    }
5790    // Build ancestor chains.
5791    let mut chain1: Vec<*mut _xmlNode> = Vec::new();
5792    let mut chain2: Vec<*mut _xmlNode> = Vec::new();
5793    let mut n = node1;
5794    while !n.is_null() {
5795        chain1.push(n);
5796        n = (*n).parent as *mut _xmlNode;
5797    }
5798    let mut n = node2;
5799    while !n.is_null() {
5800        chain2.push(n);
5801        n = (*n).parent as *mut _xmlNode;
5802    }
5803    // Find the nearest common ancestor.
5804    let mut i = chain1.len();
5805    let mut j = chain2.len();
5806    while i > 0 && j > 0 && chain1[i - 1] == chain2[j - 1] {
5807        i -= 1;
5808        j -= 1;
5809    }
5810    if i == 0 && j == 0 {
5811        return 0; // Same node (already handled) or disjoint trees treated as equal
5812    }
5813    if i == 0 {
5814        return -1; // node1 is an ancestor of node2
5815    }
5816    if j == 0 {
5817        return 1; // node2 is an ancestor of node1
5818    }
5819    // Compare sibling order at the divergence point.
5820    let mut a = chain1[i - 1];
5821    let mut b = chain2[j - 1];
5822    // Climb to the same level.
5823    while !a.is_null() && !b.is_null() {
5824        let pa = (*a).parent as *mut _xmlNode;
5825        let pb = (*b).parent as *mut _xmlNode;
5826        if pa == pb {
5827            break;
5828        }
5829        a = pa;
5830        b = pb;
5831    }
5832    // Walk forward from the first child of the common parent.
5833    let parent = (*a).parent as *mut _xmlNode;
5834    let mut child = if parent.is_null() {
5835        ptr::null_mut()
5836    } else {
5837        (*parent).children
5838    };
5839    while !child.is_null() {
5840        if child == a {
5841            return -1;
5842        }
5843        if child == b {
5844            return 1;
5845        }
5846        child = (*child).next;
5847    }
5848    0
5849}
5850
5851/// Create a node-set from a range of an existing node-set.
5852///
5853/// # UPSTREAM-PARITY
5854///
5855/// ```c
5856/// xmlNodeSetPtr xmlXPathNodeSetCreate(xmlNodePtr val);
5857/// ```
5858///
5859/// With a null `val`, creates an empty node-set.
5860#[no_mangle]
5861pub unsafe extern "C" fn xmlXPathNodeSetCreate(val: *mut _xmlNode) -> *mut _xmlNodeSet {
5862    let ns = xmlMallocZero(size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
5863    if ns.is_null() {
5864        return ptr::null_mut();
5865    }
5866    if val.is_null() {
5867        return ns;
5868    }
5869    let tab = xmlMalloc(core::mem::size_of::<*mut _xmlNode>()) as *mut *mut _xmlNode;
5870    if tab.is_null() {
5871        xmlFree(ns as *mut c_void);
5872        return ptr::null_mut();
5873    }
5874    *tab = val;
5875    (*ns).nodeTab = tab;
5876    (*ns).nodeNr = 1;
5877    (*ns).nodeMax = 1;
5878    ns
5879}
5880
5881/// Free a node-set allocated by `xmlXPathNodeSetCreate` or a node-set
5882/// builder in this library.
5883///
5884/// Frees the node-set structure and its node table; the nodes themselves
5885/// are owned by their document and are not freed.
5886///
5887/// # UPSTREAM-PARITY
5888///
5889/// ```c
5890/// void xmlXPathFreeNodeSet(xmlNodeSetPtr ns);
5891/// ```
5892#[no_mangle]
5893pub unsafe extern "C" fn xmlXPathFreeNodeSet(ns: *mut _xmlNodeSet) {
5894    if ns.is_null() {
5895        return;
5896    }
5897    if !(*ns).nodeTab.is_null() {
5898        xmlFree((*ns).nodeTab as *mut c_void);
5899        (*ns).nodeTab = ptr::null_mut();
5900    }
5901    (*ns).nodeNr = 0;
5902    (*ns).nodeMax = 0;
5903    xmlFree(ns as *mut c_void);
5904}
5905
5906/// Compile an XPath expression.
5907///
5908/// Returns an opaque pointer that can be passed to `xmlXPathEvalExpression`
5909/// (via the compiled-expr infrastructure) or freed with `xmlXPathFreeCompExpr`.
5910///
5911/// # UPSTREAM-PARITY
5912///
5913/// ```c
5914/// xmlXPathCompExprPtr xmlXPathCompile(const xmlChar *str);
5915/// ```
5916#[no_mangle]
5917pub unsafe extern "C" fn xmlXPathCompile(str_: *const xmlChar) -> *mut c_void {
5918    if str_.is_null() {
5919        return ptr::null_mut();
5920    }
5921    let expr_str = match CStr::from_ptr(str_ as *const c_char).to_str() {
5922        Ok(s) => s,
5923        Err(_) => return ptr::null_mut(),
5924    };
5925
5926    match crate::xml::xpath::compile(expr_str) {
5927        Some(compiled) => {
5928            let mut map = COMPILED_EXPRS.lock();
5929            let mut counter = NEXT_COMPILED_KEY.lock();
5930            let key = *counter;
5931            *counter += 1;
5932            map.insert(key, Box::new(compiled));
5933            key as *mut c_void
5934        }
5935        None => ptr::null_mut(),
5936    }
5937}
5938
5939/// Free a compiled XPath expression.
5940///
5941/// # UPSTREAM-PARITY
5942///
5943/// ```c
5944/// void xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp);
5945/// ```
5946#[no_mangle]
5947pub unsafe extern "C" fn xmlXPathFreeCompExpr(comp: *mut c_void) {
5948    if comp.is_null() {
5949        return;
5950    }
5951    let mut map = COMPILED_EXPRS.lock();
5952    map.remove(&(comp as u64));
5953}
5954
5955/// Register an XPath namespace.
5956///
5957/// # UPSTREAM-PARITY
5958///
5959/// ```c
5960/// int xmlXPathRegisterNs(xmlXPathContextPtr ctxt,
5961///                        const xmlChar *prefix, const xmlChar *ns_uri);
5962/// ```
5963#[no_mangle]
5964pub unsafe extern "C" fn xmlXPathRegisterNs(
5965    ctxt: *mut _xmlXPathContext,
5966    prefix: *const xmlChar,
5967    ns_uri: *const xmlChar,
5968) -> c_int {
5969    if ctxt.is_null() || prefix.is_null() || ns_uri.is_null() {
5970        return -1;
5971    }
5972    let internal = (*ctxt).extra as *mut XPathContext;
5973    if internal.is_null() {
5974        return -1;
5975    }
5976    let internal = &mut *internal;
5977
5978    let prefix_str = match CStr::from_ptr(prefix as *const c_char).to_str() {
5979        Ok(s) => s,
5980        Err(_) => return -1,
5981    };
5982    let uri_str = match CStr::from_ptr(ns_uri as *const c_char).to_str() {
5983        Ok(s) => s,
5984        Err(_) => return -1,
5985    };
5986
5987    internal.register_namespace(prefix_str, uri_str);
5988
5989    // Mirror the registration into the C context's nsHash (Box<HashMap<
5990    // String, CString>>): xmlXPathNsLookup hands out pointers into these
5991    // owned C strings, matching upstream ownership (strdup'd in nsHash,
5992    // freed by xmlXPathRegisteredNsCleanup / xmlXPathFreeContext).
5993    let map: &mut HashMap<String, CString> = if (*ctxt).nsHash.is_null() {
5994        let b: Box<HashMap<String, CString>> = Box::new(HashMap::new());
5995        (*ctxt).nsHash = Box::into_raw(b) as *mut c_void;
5996        &mut *((*ctxt).nsHash as *mut HashMap<String, CString>)
5997    } else {
5998        &mut *((*ctxt).nsHash as *mut HashMap<String, CString>)
5999    };
6000    map.insert(
6001        prefix_str.to_string(),
6002        CString::new(uri_str.as_bytes()).unwrap_or_default(),
6003    );
6004    0
6005}
6006
6007/// Register an XPath function.
6008///
6009/// The C function pointer is stored in a side table keyed by the context.
6010/// A Rust-side stub is registered in the internal context so that the Rust
6011/// evaluator is aware of the function; however, calling the C function
6012/// directly from the Rust evaluator is not yet supported.
6013///
6014/// # UPSTREAM-PARITY
6015///
6016/// ```c
6017/// int xmlXPathRegisterFunc(xmlXPathContextPtr ctxt,
6018///                          const xmlChar *name, xmlXPathFunction f);
6019/// ```
6020#[no_mangle]
6021pub unsafe extern "C" fn xmlXPathRegisterFunc(
6022    ctxt: *mut _xmlXPathContext,
6023    name: *const xmlChar,
6024    f: Option<unsafe extern "C" fn(*mut c_void, c_int)>,
6025) -> c_int {
6026    if ctxt.is_null() || name.is_null() {
6027        return -1;
6028    }
6029    let internal = (*ctxt).extra as *mut XPathContext;
6030    if internal.is_null() {
6031        return -1;
6032    }
6033    let internal = &mut *internal;
6034
6035    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
6036        Ok(s) => s,
6037        Err(_) => return -1,
6038    };
6039
6040    if let Some(func) = f {
6041        // Store the C function pointer in the side table.
6042        let key = (SendSyncPtr((*ctxt).extra), name_str.to_string());
6043        C_FUNCTIONS.lock().insert(key, func);
6044        // Register a Rust stub so the evaluator knows the function exists.
6045        internal.register_function(name_str, c_func_stub);
6046    }
6047    0
6048}
6049
6050/// Register an XPath function with namespace.
6051///
6052/// # UPSTREAM-PARITY
6053///
6054/// ```c
6055/// int xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt,
6056///                            const xmlChar *name, const xmlChar *ns_uri,
6057///                            xmlXPathFunction f);
6058/// ```
6059#[no_mangle]
6060pub unsafe extern "C" fn xmlXPathRegisterFuncNS(
6061    ctxt: *mut _xmlXPathContext,
6062    name: *const xmlChar,
6063    ns_uri: *const xmlChar,
6064    f: Option<unsafe extern "C" fn(*mut c_void, c_int)>,
6065) -> c_int {
6066    if ctxt.is_null() || name.is_null() {
6067        return -1;
6068    }
6069    let internal = (*ctxt).extra as *mut XPathContext;
6070    if internal.is_null() {
6071        return -1;
6072    }
6073    let internal = &mut *internal;
6074
6075    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
6076        Ok(s) => s,
6077        Err(_) => return -1,
6078    };
6079    let ns_str = if ns_uri.is_null() {
6080        String::new()
6081    } else {
6082        match CStr::from_ptr(ns_uri as *const c_char).to_str() {
6083            Ok(s) => s.to_string(),
6084            Err(_) => return -1,
6085        }
6086    };
6087
6088    // Use "{ns}:" prefix as part of the key to keep functions unique.
6089    let qualified = if ns_str.is_empty() {
6090        name_str.to_string()
6091    } else {
6092        format!("{{{}}}{}", ns_str, name_str)
6093    };
6094
6095    if let Some(func) = f {
6096        let key = (SendSyncPtr((*ctxt).extra), qualified.clone());
6097        C_FUNCTIONS.lock().insert(key, func);
6098        internal.register_function(&qualified, c_func_stub);
6099    }
6100    0
6101}
6102
6103/// Register an XPath variable.
6104///
6105/// # UPSTREAM-PARITY
6106///
6107/// ```c
6108/// int xmlXPathRegisterVariable(xmlXPathContextPtr ctxt,
6109///                              const xmlChar *name, xmlXPathObjectPtr value);
6110/// ```
6111#[no_mangle]
6112pub unsafe extern "C" fn xmlXPathRegisterVariable(
6113    ctxt: *mut _xmlXPathContext,
6114    name: *const xmlChar,
6115    value: *mut _xmlXPathObject,
6116) -> c_int {
6117    if ctxt.is_null() || name.is_null() || value.is_null() {
6118        return -1;
6119    }
6120    let internal = (*ctxt).extra as *mut XPathContext;
6121    if internal.is_null() {
6122        return -1;
6123    }
6124    let internal = &mut *internal;
6125
6126    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
6127        Ok(s) => s,
6128        Err(_) => return -1,
6129    };
6130
6131    let xpath_val = object_to_xpathvalue(value);
6132    internal.register_variable(name_str, xpath_val);
6133    0
6134}
6135
6136/// Create an XPath object wrapping a single node in a node-set.
6137///
6138/// # UPSTREAM-PARITY
6139///
6140/// ```c
6141/// xmlXPathObjectPtr xmlXPathNewNodeSet(xmlNodePtr val);
6142/// ```
6143#[no_mangle]
6144pub unsafe extern "C" fn xmlXPathNewNodeSet(val: *mut _xmlNode) -> *mut _xmlXPathObject {
6145    let ns = if val.is_null() {
6146        NodeSet::new()
6147    } else {
6148        NodeSet::singleton(val)
6149    };
6150    xpath_to_object(XPathValue::NodeSet(ns))
6151}
6152
6153/// Create an XPath object from a C string value.
6154///
6155/// # UPSTREAM-PARITY
6156///
6157/// ```c
6158/// xmlXPathObjectPtr xmlXPathNewCString(const xmlChar *val);
6159/// ```
6160#[no_mangle]
6161pub unsafe extern "C" fn xmlXPathNewCString(val: *const xmlChar) -> *mut _xmlXPathObject {
6162    if val.is_null() {
6163        return xpath_to_object(XPathValue::String(String::new()));
6164    }
6165    let s = match CStr::from_ptr(val as *const c_char).to_str() {
6166        Ok(s) => s.to_string(),
6167        Err(_) => return ptr::null_mut(),
6168    };
6169    xpath_to_object(XPathValue::String(s))
6170}
6171
6172/// Create an XPath number object.
6173///
6174/// # UPSTREAM-PARITY
6175///
6176/// ```c
6177/// xmlXPathObjectPtr xmlXPathNewFloat(double val);
6178/// ```
6179#[no_mangle]
6180pub extern "C" fn xmlXPathNewFloat(val: f64) -> *mut _xmlXPathObject {
6181    unsafe { xpath_to_object(XPathValue::Number(val)) }
6182}
6183
6184/// Create an XPath boolean object.
6185///
6186/// # UPSTREAM-PARITY
6187///
6188/// ```c
6189/// xmlXPathObjectPtr xmlXPathNewBoolean(int val);
6190/// ```
6191#[no_mangle]
6192pub extern "C" fn xmlXPathNewBoolean(val: c_int) -> *mut _xmlXPathObject {
6193    unsafe { xpath_to_object(XPathValue::Boolean(val != 0)) }
6194}
6195
6196// ═══════════════════════════════════════════════════════════════════════════════
6197// 14.5. XPointer
6198// ═══════════════════════════════════════════════════════════════════════════════
6199
6200/// Evaluate an XPointer expression.
6201///
6202/// Delegates to the xpointer module.
6203///
6204/// # UPSTREAM-PARITY
6205///
6206/// ```c
6207/// xmlNodePtr xmlXPtrEval(const xmlChar *expr, xmlDocPtr doc);
6208/// ```
6209#[no_mangle]
6210pub unsafe extern "C" fn xmlXPtrEval(expr: *const c_char, doc: *mut _xmlDoc) -> *mut _xmlNode {
6211    crate::xml::xpointer::xmlXPtrEval(expr, doc)
6212}
6213
6214// ═══════════════════════════════════════════════════════════════════════════════
6215// 15. XInclude
6216// ═══════════════════════════════════════════════════════════════════════════════
6217
6218/// Process XInclude nodes in a document.
6219///
6220/// # UPSTREAM-PARITY
6221///
6222/// ```c
6223/// int xmlXIncludeProcess(xmlDocPtr doc);
6224/// ```
6225#[no_mangle]
6226pub unsafe extern "C" fn xmlXIncludeProcess(doc: *mut _xmlDoc) -> c_int {
6227    crate::xml::xinclude::xinclude_process(doc)
6228}
6229
6230/// Process XInclude nodes with flags.
6231///
6232/// # UPSTREAM-PARITY
6233///
6234/// ```c
6235/// int xmlXIncludeProcessFlags(xmlDocPtr doc, int flags);
6236/// ```
6237#[no_mangle]
6238pub unsafe extern "C" fn xmlXIncludeProcessFlags(doc: *mut _xmlDoc, flags: c_int) -> c_int {
6239    crate::xml::xinclude::xinclude_process_flags(doc, flags)
6240}
6241
6242// ═══════════════════════════════════════════════════════════════════════════════
6243// 16. Catalog
6244// ═══════════════════════════════════════════════════════════════════════════════
6245
6246/// Load a catalog.
6247///
6248/// # UPSTREAM-PARITY
6249///
6250/// ```c
6251/// xmlCatalogPtr xmlCatalogLoad(const char *catalogs);
6252/// ```
6253#[no_mangle]
6254pub extern "C" fn xmlCatalogLoad(catalogs: *const c_char) -> *mut c_void {
6255    if catalogs.is_null() {
6256        return ptr::null_mut();
6257    }
6258    crate::xml::catalog::load_catalog(catalogs)
6259}
6260
6261/// Resolve a public ID.
6262///
6263/// # UPSTREAM-PARITY
6264///
6265/// ```c
6266/// xmlCharPtr xmlCatalogResolvePublic(const xmlChar *pubID);
6267/// ```
6268#[no_mangle]
6269pub unsafe extern "C" fn xmlCatalogResolvePublic(pubID: *const xmlChar) -> *mut xmlChar {
6270    if pubID.is_null() {
6271        return ptr::null_mut();
6272    }
6273    crate::xml::catalog::resolve_public(pubID)
6274}
6275
6276/// Resolve a system ID.
6277///
6278/// # UPSTREAM-PARITY
6279///
6280/// ```c
6281/// xmlCharPtr xmlCatalogResolveSystem(const xmlChar *sysID);
6282/// ```
6283#[no_mangle]
6284pub unsafe extern "C" fn xmlCatalogResolveSystem(sysID: *const xmlChar) -> *mut xmlChar {
6285    if sysID.is_null() {
6286        return ptr::null_mut();
6287    }
6288    crate::xml::catalog::resolve_system(sysID)
6289}
6290
6291/// Resolve a URI.
6292///
6293/// # UPSTREAM-PARITY
6294///
6295/// ```c
6296/// xmlCharPtr xmlCatalogResolveURI(const xmlChar *URI);
6297/// ```
6298#[no_mangle]
6299pub unsafe extern "C" fn xmlCatalogResolveURI(URI: *const xmlChar) -> *mut xmlChar {
6300    if URI.is_null() {
6301        return ptr::null_mut();
6302    }
6303    crate::xml::catalog::resolve_uri(URI)
6304}
6305
6306/// Set catalog defaults.
6307///
6308/// # UPSTREAM-PARITY
6309///
6310/// ```c
6311/// void xmlCatalogSetDefaults(xmlCatalogAllowValue allow);
6312/// ```
6313#[no_mangle]
6314pub extern "C" fn xmlCatalogSetDefaults(allow: c_int) {
6315    crate::xml::catalog::set_defaults(allow)
6316}
6317
6318/// Get catalog defaults.
6319///
6320/// # UPSTREAM-PARITY
6321///
6322/// ```c
6323/// xmlCatalogAllowValue xmlCatalogGetDefaults(void);
6324/// ```
6325#[no_mangle]
6326pub extern "C" fn xmlCatalogGetDefaults() -> c_int {
6327    crate::xml::catalog::get_defaults()
6328}
6329
6330/// Add a catalog.
6331///
6332/// # UPSTREAM-PARITY
6333///
6334/// ```c
6335/// int xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace);
6336/// ```
6337#[no_mangle]
6338pub unsafe extern "C" fn xmlCatalogAdd(
6339    type_: *const xmlChar,
6340    orig: *const xmlChar,
6341    replace: *const xmlChar,
6342) -> c_int {
6343    if type_.is_null() || orig.is_null() || replace.is_null() {
6344        return -1;
6345    }
6346    crate::xml::catalog::add(type_, orig, replace)
6347}
6348
6349/// Remove a catalog entry.
6350///
6351/// # UPSTREAM-PARITY
6352///
6353/// ```c
6354/// int xmlCatalogRemove(const xmlChar *value);
6355/// ```
6356#[no_mangle]
6357pub unsafe extern "C" fn xmlCatalogRemove(value: *const xmlChar) -> c_int {
6358    if value.is_null() {
6359        return 0;
6360    }
6361    crate::xml::catalog::remove(value)
6362}
6363
6364/// Dump the catalog in XML format to a FILE* (upstream `xmlCatalogDump`).
6365///
6366/// # UPSTREAM-PARITY
6367///
6368/// ```c
6369/// void xmlCatalogDump(FILE *out, xmlCatalogPtr catal);
6370/// ```
6371#[no_mangle]
6372pub unsafe extern "C" fn xmlCatalogDump(output: *mut c_void, _catal: *mut c_void) {
6373    if output.is_null() {
6374        return;
6375    }
6376    let doc = crate::xml::catalog::dump_doc();
6377    if doc.is_null() {
6378        return;
6379    }
6380    let mut mem: *mut xmlChar = ptr::null_mut();
6381    let mut size: c_int = 0;
6382    crate::xml::tree::xmlDocDumpFormatMemory(doc, &mut mem, &mut size, 1);
6383    if !mem.is_null() {
6384        libc::fwrite(
6385            mem as *const c_void,
6386            1,
6387            size as usize,
6388            output as *mut libc::FILE,
6389        );
6390        xmlFree(mem as *mut c_void);
6391    }
6392    crate::xml::tree::free_doc(doc);
6393}
6394
6395/// Save the catalog to a file (upstream `xmlCatalogSave`).
6396///
6397/// Returns 0 on success, -1 on failure.
6398///
6399/// # UPSTREAM-PARITY
6400///
6401/// ```c
6402/// int xmlCatalogSave(const char *filename);
6403/// ```
6404#[no_mangle]
6405pub unsafe extern "C" fn xmlCatalogSave(filename: *const c_char) -> c_int {
6406    if filename.is_null() {
6407        return -1;
6408    }
6409    let doc = crate::xml::catalog::dump_doc();
6410    if doc.is_null() {
6411        return -1;
6412    }
6413    let mut mem: *mut xmlChar = ptr::null_mut();
6414    let mut size: c_int = 0;
6415    crate::xml::tree::xmlDocDumpFormatMemory(doc, &mut mem, &mut size, 1);
6416    let mut ret: c_int = -1;
6417    if !mem.is_null() {
6418        let fp = libc::fopen(filename, b"w\0".as_ptr() as *const c_char);
6419        if !fp.is_null() {
6420            let written = libc::fwrite(mem as *const c_void, 1, size as usize, fp);
6421            ret = if written == size as usize { 0 } else { -1 };
6422            libc::fclose(fp);
6423        }
6424        xmlFree(mem as *mut c_void);
6425    }
6426    crate::xml::tree::free_doc(doc);
6427    ret
6428}
6429
6430/// Clean up the catalog subsystem.
6431///
6432/// # UPSTREAM-PARITY
6433///
6434/// ```c
6435/// void xmlCatalogCleanup(void);
6436/// ```
6437#[no_mangle]
6438pub extern "C" fn xmlCatalogCleanup() {
6439    crate::xml::catalog::cleanup();
6440}
6441
6442/// Convert an SGML catalog to XML.
6443///
6444/// # UPSTREAM-PARITY
6445///
6446/// ```c
6447/// xmlDocPtr xmlCatalogConvert(void);
6448/// ```
6449#[no_mangle]
6450pub extern "C" fn xmlCatalogConvert() -> *mut _xmlDoc {
6451    // SAFETY: catalog::convert() allocates and builds an XML document tree.
6452    unsafe { crate::xml::catalog::convert() }
6453}
6454
6455// ═══════════════════════════════════════════════════════════════════════════════
6456// 17. HTML
6457// ═══════════════════════════════════════════════════════════════════════════════
6458
6459/// Parse an HTML document from a file.
6460///
6461/// # UPSTREAM-PARITY
6462///
6463/// ```c
6464/// htmlDocPtr htmlParseFile(const char *filename, const char *encoding);
6465/// ```
6466#[no_mangle]
6467pub unsafe extern "C" fn htmlParseFile(
6468    _filename: *const c_char,
6469    _encoding: *const c_char,
6470) -> *mut _xmlDoc {
6471    // Phase 1: STUB
6472    ptr::null_mut()
6473}
6474
6475/// Parse an HTML document from memory.
6476///
6477/// # UPSTREAM-PARITY
6478///
6479/// ```c
6480/// htmlDocPtr htmlParseMemory(const char *buffer, int size);
6481/// ```
6482#[no_mangle]
6483pub unsafe extern "C" fn htmlParseMemory(_buffer: *const c_char, _size: c_int) -> *mut _xmlDoc {
6484    // Phase 1: STUB
6485    ptr::null_mut()
6486}
6487
6488/// Parse an HTML document from a document string.
6489///
6490/// # UPSTREAM-PARITY
6491///
6492/// ```c
6493/// htmlDocPtr htmlParseDoc(const xmlChar *cur, const char *encoding);
6494/// ```
6495#[no_mangle]
6496pub unsafe extern "C" fn htmlParseDoc(
6497    _cur: *const xmlChar,
6498    _encoding: *const c_char,
6499) -> *mut _xmlDoc {
6500    // Phase 1: STUB
6501    ptr::null_mut()
6502}
6503
6504/// Create an HTML parser context.
6505///
6506/// # UPSTREAM-PARITY
6507///
6508/// ```c
6509/// htmlParserCtxtPtr htmlCreateFileParserCtxt(const char *filename,
6510///                                            const char *encoding);
6511/// ```
6512#[no_mangle]
6513pub unsafe extern "C" fn htmlCreateFileParserCtxt(
6514    _filename: *const c_char,
6515    _encoding: *const c_char,
6516) -> *mut c_void {
6517    // Phase 1: STUB
6518    ptr::null_mut()
6519}
6520
6521/// Free an HTML parser context.
6522///
6523/// # UPSTREAM-PARITY
6524///
6525/// ```c
6526/// void htmlFreeParserCtxt(htmlParserCtxtPtr ctxt);
6527/// ```
6528#[no_mangle]
6529pub extern "C" fn htmlFreeParserCtxt(ctxt: *mut c_void) {
6530    unsafe { crate::xml::html::free_parser_ctxt(ctxt) }
6531}
6532
6533/// Initialize the HTML parser.
6534///
6535/// # UPSTREAM-PARITY
6536///
6537/// ```c
6538/// void htmlInitParser(void);
6539/// ```
6540#[no_mangle]
6541pub extern "C" fn htmlInitParser() {
6542    // Phase 1: STUB
6543}
6544
6545/// Clean up the HTML parser.
6546///
6547/// # UPSTREAM-PARITY
6548///
6549/// ```c
6550/// void htmlCleanupParser(void);
6551/// ```
6552#[no_mangle]
6553pub extern "C" fn htmlCleanupParser() {
6554    // Phase 1: STUB
6555}
6556
6557// ═══════════════════════════════════════════════════════════════════════════════
6558// 17.5. Validation (DTD)
6559// ═══════════════════════════════════════════════════════════════════════════════
6560
6561/// Create a new validation context.
6562///
6563/// # UPSTREAM-PARITY
6564///
6565/// ```c
6566/// xmlValidCtxtPtr xmlNewValidCtxt(void);
6567/// ```
6568#[no_mangle]
6569pub unsafe extern "C" fn xmlNewValidCtxt() -> *mut _xmlValidCtxt {
6570    crate::xml::validation::new_valid_ctxt()
6571}
6572
6573/// Free a validation context.
6574///
6575/// # UPSTREAM-PARITY
6576///
6577/// ```c
6578/// void xmlFreeValidCtxt(xmlValidCtxtPtr ctxt);
6579/// ```
6580#[no_mangle]
6581pub unsafe extern "C" fn xmlFreeValidCtxt(ctxt: *mut _xmlValidCtxt) {
6582    crate::xml::validation::free_valid_ctxt(ctxt);
6583}
6584
6585/// Set error and warning callbacks on a validation context.
6586///
6587/// # UPSTREAM-PARITY
6588///
6589/// ```c
6590/// void xmlSetValidErrors(xmlValidCtxtPtr ctxt,
6591///                        xmlGenericErrorFunc err,
6592///                        xmlGenericErrorFunc warn,
6593///                        void *data);
6594/// ```
6595#[no_mangle]
6596pub unsafe extern "C" fn xmlSetValidErrors(
6597    ctxt: *mut _xmlValidCtxt,
6598    err: Option<xmlGenericErrorFunc>,
6599    warn: Option<xmlGenericErrorFunc>,
6600    data: *mut c_void,
6601) {
6602    crate::xml::validation::set_valid_errors(ctxt, err, warn, data);
6603}
6604
6605/// Validate a document against its DTD.
6606///
6607/// # UPSTREAM-PARITY
6608///
6609/// ```c
6610/// int xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
6611/// ```
6612#[no_mangle]
6613pub unsafe extern "C" fn xmlValidateDocument(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
6614    crate::xml::validation::validate_document(ctxt, doc)
6615}
6616
6617/// Final validation pass (check ID/IDREF consistency).
6618///
6619/// # UPSTREAM-PARITY
6620///
6621/// ```c
6622/// int xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
6623/// ```
6624#[no_mangle]
6625pub unsafe extern "C" fn xmlValidateDocumentFinal(
6626    ctxt: *mut _xmlValidCtxt,
6627    doc: *mut _xmlDoc,
6628) -> c_int {
6629    crate::xml::validation::validate_document_final(ctxt, doc)
6630}
6631
6632/// Validate an element node against its DTD declarations.
6633///
6634/// # UPSTREAM-PARITY
6635///
6636/// ```c
6637/// int xmlValidateElement(xmlValidCtxtPtr ctxt,
6638///                        xmlDocPtr doc,
6639///                        xmlNodePtr elem);
6640/// ```
6641#[no_mangle]
6642pub unsafe extern "C" fn xmlValidateElement(
6643    ctxt: *mut _xmlValidCtxt,
6644    doc: *mut _xmlDoc,
6645    elem: *mut _xmlNode,
6646) -> c_int {
6647    crate::xml::validation::validate_element(ctxt, doc, elem)
6648}
6649
6650/// Validate an attribute declaration.
6651///
6652/// # UPSTREAM-PARITY
6653///
6654/// ```c
6655/// int xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt,
6656///                              xmlDocPtr doc,
6657///                              xmlNodePtr elem,
6658///                              xmlAttributePtr attr);
6659/// ```
6660#[no_mangle]
6661pub unsafe extern "C" fn xmlValidateAttributeDecl(
6662    ctxt: *mut _xmlValidCtxt,
6663    doc: *mut _xmlDoc,
6664    elem: *mut _xmlNode,
6665    attr: *mut _xmlAttribute,
6666) -> c_int {
6667    crate::xml::validation::validate_attribute_decl(ctxt, doc, elem, attr)
6668}
6669
6670/// Validate an attribute value against its declared type.
6671///
6672/// # UPSTREAM-PARITY
6673///
6674/// ```c
6675/// int xmlValidateAttributeValue(int type, const xmlChar *value);
6676/// ```
6677#[no_mangle]
6678pub unsafe extern "C" fn xmlValidateAttributeValue(atype: c_int, value: *const xmlChar) -> c_int {
6679    crate::xml::validation::validate_attribute_value(atype, value)
6680}
6681
6682/// Validate a NOTATION reference.
6683///
6684/// # UPSTREAM-PARITY
6685///
6686/// ```c
6687/// int xmlValidateNotationUse(xmlValidCtxtPtr ctxt,
6688///                            xmlDocPtr doc,
6689///                            const xmlChar *notationName);
6690/// ```
6691#[no_mangle]
6692pub unsafe extern "C" fn xmlValidateNotationUse(
6693    ctxt: *mut _xmlValidCtxt,
6694    doc: *mut _xmlDoc,
6695    notation_name: *const xmlChar,
6696) -> c_int {
6697    crate::xml::validation::validate_notation_use(ctxt, doc, notation_name)
6698}
6699
6700/// Validate an ID value (check uniqueness).
6701///
6702/// # UPSTREAM-PARITY
6703///
6704/// ```c
6705/// int xmlValidateID(xmlValidCtxtPtr ctxt,
6706///                   xmlDocPtr doc,
6707///                   xmlNodePtr node,
6708///                   const xmlChar *value);
6709/// ```
6710#[no_mangle]
6711pub unsafe extern "C" fn xmlValidateID(
6712    ctxt: *mut _xmlValidCtxt,
6713    doc: *mut _xmlDoc,
6714    node: *mut _xmlNode,
6715    value: *const xmlChar,
6716) -> c_int {
6717    crate::xml::validation::validate_id(ctxt, doc, node, value)
6718}
6719
6720/// Validate an IDREF value (check it references a known ID).
6721///
6722/// # UPSTREAM-PARITY
6723///
6724/// ```c
6725/// int xmlValidateIDRef(xmlValidCtxtPtr ctxt,
6726///                      xmlDocPtr doc,
6727///                      xmlNodePtr node,
6728///                      const xmlChar *value);
6729/// ```
6730#[no_mangle]
6731pub unsafe extern "C" fn xmlValidateIDRef(
6732    ctxt: *mut _xmlValidCtxt,
6733    doc: *mut _xmlDoc,
6734    node: *mut _xmlNode,
6735    value: *const xmlChar,
6736) -> c_int {
6737    crate::xml::validation::validate_id_ref(ctxt, doc, node, value)
6738}
6739
6740/// Validate IDREFS (whitespace-separated list of IDREFs).
6741///
6742/// # UPSTREAM-PARITY
6743///
6744/// ```c
6745/// int xmlValidateIDRefs(xmlValidCtxtPtr ctxt,
6746///                       xmlDocPtr doc,
6747///                       xmlNodePtr node,
6748///                       const xmlChar *value);
6749/// ```
6750#[no_mangle]
6751pub unsafe extern "C" fn xmlValidateIDRefs(
6752    ctxt: *mut _xmlValidCtxt,
6753    doc: *mut _xmlDoc,
6754    node: *mut _xmlNode,
6755    value: *const xmlChar,
6756) -> c_int {
6757    crate::xml::validation::validate_id_refs(ctxt, doc, node, value)
6758}
6759
6760/// Validate an NCName value (modern 2-arg form, upstream tree.c).
6761///
6762/// # UPSTREAM-PARITY
6763///
6764/// ```c
6765/// int xmlValidateNCName(const xmlChar *value, int space);
6766/// ```
6767///
6768/// Returns -1 on NULL, 0 if valid, 1 if invalid.
6769#[no_mangle]
6770pub unsafe extern "C" fn xmlValidateNCName(value: *const xmlChar, space: c_int) -> c_int {
6771    crate::xml::validation::validate_ncname(value, space)
6772}
6773
6774/// Validate a QName value (modern 2-arg form, upstream tree.c).
6775///
6776/// # UPSTREAM-PARITY
6777///
6778/// ```c
6779/// int xmlValidateQName(const xmlChar *value, int space);
6780/// ```
6781#[no_mangle]
6782pub unsafe extern "C" fn xmlValidateQName(value: *const xmlChar, space: c_int) -> c_int {
6783    crate::xml::validation::validate_qname(value, space)
6784}
6785
6786/// Validate an XML Name value (modern 2-arg form, upstream tree.c).
6787///
6788/// # UPSTREAM-PARITY / HISTORICAL
6789///
6790/// Since libxml2 2.12 the DSO symbol carries a second `int space` parameter
6791/// with inverted return semantics (0 valid / 1 invalid / -1 NULL); the
6792/// pre-2.12 1-arg form no longer exists in the DSO. The candidate matches
6793/// the current oracle. (The 1-arg semantics live on as xmlValidateNameValue.)
6794///
6795/// ```c
6796/// int xmlValidateName(const xmlChar *value, int space);
6797/// ```
6798#[no_mangle]
6799pub unsafe extern "C" fn xmlValidateName(value: *const xmlChar, space: c_int) -> c_int {
6800    crate::xml::validation::validate_name_space(value, space)
6801}
6802
6803/// Validate an NMToken value (modern 2-arg form, upstream tree.c).
6804///
6805/// # UPSTREAM-PARITY
6806///
6807/// ```c
6808/// int xmlValidateNMToken(const xmlChar *value, int space);
6809/// ```
6810#[no_mangle]
6811pub unsafe extern "C" fn xmlValidateNMToken(value: *const xmlChar, space: c_int) -> c_int {
6812    crate::xml::validation::validate_nmtoken_space(value, space)
6813}
6814
6815/// Validate a Name value (1-arg form, upstream valid.c).
6816///
6817/// # UPSTREAM-PARITY
6818///
6819/// ```c
6820/// int xmlValidateNameValue(const xmlChar *value);
6821/// ```
6822///
6823/// Returns 1 if valid, 0 if not (NULL included).
6824#[no_mangle]
6825pub unsafe extern "C" fn xmlValidateNameValue(value: *const xmlChar) -> c_int {
6826    crate::xml::validation::validate_name_value(value)
6827}
6828
6829/// Validate a whitespace-separated list of Names (separator is exactly
6830/// 0x20, upstream erratum E20).
6831///
6832/// # UPSTREAM-PARITY
6833///
6834/// ```c
6835/// int xmlValidateNamesValue(const xmlChar *value);
6836/// ```
6837#[no_mangle]
6838pub unsafe extern "C" fn xmlValidateNamesValue(value: *const xmlChar) -> c_int {
6839    crate::xml::validation::validate_names_value(value)
6840}
6841
6842/// Validate an Nmtoken value (1-arg form, upstream valid.c).
6843///
6844/// # UPSTREAM-PARITY
6845///
6846/// ```c
6847/// int xmlValidateNmtokenValue(const xmlChar *value);
6848/// ```
6849#[no_mangle]
6850pub unsafe extern "C" fn xmlValidateNmtokenValue(value: *const xmlChar) -> c_int {
6851    crate::xml::validation::validate_nmtoken_value(value)
6852}
6853
6854/// Validate a whitespace-separated list of Nmtokens.
6855///
6856/// # UPSTREAM-PARITY
6857///
6858/// ```c
6859/// int xmlValidateNmtokensValue(const xmlChar *value);
6860/// ```
6861#[no_mangle]
6862pub unsafe extern "C" fn xmlValidateNmtokensValue(value: *const xmlChar) -> c_int {
6863    crate::xml::validation::validate_nmtokens_value(value)
6864}
6865
6866/// Validate a single element declaration (VC: Unique Element Type
6867/// Declaration, VC: No Duplicate Types).
6868///
6869/// # UPSTREAM-PARITY
6870///
6871/// ```c
6872/// int xmlValidateElementDecl(xmlValidCtxtPtr ctxt,
6873///                            xmlDocPtr doc,
6874///                            xmlElementPtr elem);
6875/// ```
6876#[no_mangle]
6877pub unsafe extern "C" fn xmlValidateElementDecl(
6878    ctxt: *mut _xmlValidCtxt,
6879    doc: *mut _xmlDoc,
6880    elem: *mut _xmlElement,
6881) -> c_int {
6882    crate::xml::validation::validate_element_decl(ctxt, doc, elem)
6883}
6884
6885/// Validate a notation declaration.
6886///
6887/// # UPSTREAM-PARITY
6888///
6889/// Modern libxml2 has no validity constraint on notation declarations; the
6890/// oracle returns 1 unconditionally (verified by DSO disassembly).
6891///
6892/// ```c
6893/// int xmlValidateNotationDecl(xmlValidCtxtPtr ctxt,
6894///                             xmlDocPtr doc,
6895///                             xmlNotationPtr nota);
6896/// ```
6897#[no_mangle]
6898pub unsafe extern "C" fn xmlValidateNotationDecl(
6899    ctxt: *mut _xmlValidCtxt,
6900    doc: *mut _xmlDoc,
6901    nota: *mut _xmlNotation,
6902) -> c_int {
6903    crate::xml::validation::validate_notation_decl(ctxt, doc, nota)
6904}
6905
6906/// Validate a single attribute against its declaration.
6907///
6908/// # UPSTREAM-PARITY
6909///
6910/// ```c
6911/// int xmlValidateOneAttribute(xmlValidCtxtPtr ctxt,
6912///                             xmlDocPtr doc,
6913///                             xmlNodePtr elem,
6914///                             xmlAttrPtr attr,
6915///                             const xmlChar *value);
6916/// ```
6917#[no_mangle]
6918pub unsafe extern "C" fn xmlValidateOneAttribute(
6919    ctxt: *mut _xmlValidCtxt,
6920    doc: *mut _xmlDoc,
6921    elem: *mut _xmlNode,
6922    attr: *mut _xmlAttr,
6923    value: *const xmlChar,
6924) -> c_int {
6925    crate::xml::validation::validate_one_attribute(ctxt, doc, elem, attr, value)
6926}
6927
6928/// Validate a single element against its declaration (without recursing).
6929///
6930/// # UPSTREAM-PARITY
6931///
6932/// ```c
6933/// int xmlValidateOneElement(xmlValidCtxtPtr ctxt,
6934///                           xmlDocPtr doc,
6935///                           xmlNodePtr elem);
6936/// ```
6937#[no_mangle]
6938pub unsafe extern "C" fn xmlValidateOneElement(
6939    ctxt: *mut _xmlValidCtxt,
6940    doc: *mut _xmlDoc,
6941    elem: *mut _xmlNode,
6942) -> c_int {
6943    crate::xml::validation::validate_one_element(ctxt, doc, elem)
6944}
6945
6946/// Validate a namespace declaration attribute.
6947///
6948/// # UPSTREAM-PARITY
6949///
6950/// ```c
6951/// int xmlValidateOneNamespace(xmlValidCtxtPtr ctxt,
6952///                             xmlDocPtr doc,
6953///                             xmlNodePtr elem,
6954///                             const xmlChar *prefix,
6955///                             xmlNsPtr ns,
6956///                             const xmlChar *value);
6957/// ```
6958#[no_mangle]
6959pub unsafe extern "C" fn xmlValidateOneNamespace(
6960    ctxt: *mut _xmlValidCtxt,
6961    doc: *mut _xmlDoc,
6962    elem: *mut _xmlNode,
6963    prefix: *const xmlChar,
6964    ns: *mut _xmlNs,
6965    value: *const xmlChar,
6966) -> c_int {
6967    crate::xml::validation::validate_one_namespace(ctxt, doc, elem, prefix, ns, value)
6968}
6969
6970/// Push a new element start onto the validation stack (streaming DTD
6971/// validation).
6972///
6973/// # UPSTREAM-PARITY
6974///
6975/// ```c
6976/// int xmlValidatePushElement(xmlValidCtxtPtr ctxt,
6977///                            xmlDocPtr doc,
6978///                            xmlNodePtr elem,
6979///                            const xmlChar *qname);
6980/// ```
6981#[no_mangle]
6982pub unsafe extern "C" fn xmlValidatePushElement(
6983    ctxt: *mut _xmlValidCtxt,
6984    doc: *mut _xmlDoc,
6985    elem: *mut _xmlNode,
6986    qname: *const xmlChar,
6987) -> c_int {
6988    crate::xml::validation::validate_push_element(ctxt, doc, elem, qname)
6989}
6990
6991/// Push character data onto the validation stack.
6992///
6993/// # UPSTREAM-PARITY
6994///
6995/// ```c
6996/// int xmlValidatePushCData(xmlValidCtxtPtr ctxt,
6997///                          const xmlChar *data,
6998///                          int len);
6999/// ```
7000#[no_mangle]
7001pub unsafe extern "C" fn xmlValidatePushCData(
7002    ctxt: *mut _xmlValidCtxt,
7003    data: *const xmlChar,
7004    len: c_int,
7005) -> c_int {
7006    crate::xml::validation::validate_push_cdata(ctxt, data, len)
7007}
7008
7009/// Pop an element end from the validation stack.
7010///
7011/// # UPSTREAM-PARITY
7012///
7013/// ```c
7014/// int xmlValidatePopElement(xmlValidCtxtPtr ctxt,
7015///                           xmlDocPtr doc,
7016///                           xmlNodePtr elem,
7017///                           const xmlChar *qname);
7018/// ```
7019#[no_mangle]
7020pub unsafe extern "C" fn xmlValidatePopElement(
7021    ctxt: *mut _xmlValidCtxt,
7022    doc: *mut _xmlDoc,
7023    elem: *mut _xmlNode,
7024    qname: *const xmlChar,
7025) -> c_int {
7026    crate::xml::validation::validate_pop_element(ctxt, doc, elem, qname)
7027}
7028
7029/// Build the content-model automaton for an element declaration.
7030///
7031/// # UPSTREAM-PARITY
7032///
7033/// ```c
7034/// int xmlValidBuildContentModel(xmlValidCtxtPtr ctxt,
7035///                               xmlElementPtr elem);
7036/// ```
7037#[no_mangle]
7038pub unsafe extern "C" fn xmlValidBuildContentModel(
7039    ctxt: *mut _xmlValidCtxt,
7040    elem: *mut _xmlElement,
7041) -> c_int {
7042    crate::xml::validation::validate_build_content_model(ctxt, elem)
7043}
7044
7045/// Add an attribute to the document's ID table.
7046///
7047/// # UPSTREAM-PARITY
7048///
7049/// ```c
7050/// xmlIDPtr xmlAddID(xmlValidCtxtPtr ctxt,
7051///                   xmlDocPtr doc,
7052///                   const xmlChar *value,
7053///                   xmlAttrPtr attr);
7054/// ```
7055#[no_mangle]
7056pub unsafe extern "C" fn xmlAddID(
7057    ctxt: *mut _xmlValidCtxt,
7058    doc: *mut _xmlDoc,
7059    value: *const xmlChar,
7060    attr: *mut _xmlAttr,
7061) -> *mut _xmlID {
7062    crate::xml::validation::add_id(ctxt, doc, value, attr)
7063}
7064
7065/// Remove an attribute from the document's ID table.
7066///
7067/// # UPSTREAM-PARITY
7068///
7069/// ```c
7070/// int xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr);
7071/// ```
7072#[no_mangle]
7073pub unsafe extern "C" fn xmlRemoveID(doc: *mut _xmlDoc, attr: *mut _xmlAttr) -> c_int {
7074    crate::xml::validation::remove_id(doc, attr)
7075}
7076
7077/// Register an IDREF in the document's ref table.
7078///
7079/// # UPSTREAM-PARITY
7080///
7081/// ```c
7082/// xmlRefPtr xmlAddRef(xmlValidCtxtPtr ctxt,
7083///                     xmlDocPtr doc,
7084///                     const xmlChar *value,
7085///                     xmlAttrPtr attr);
7086/// ```
7087#[no_mangle]
7088pub unsafe extern "C" fn xmlAddRef(
7089    ctxt: *mut _xmlValidCtxt,
7090    doc: *mut _xmlDoc,
7091    value: *const xmlChar,
7092    attr: *mut _xmlAttr,
7093) -> *mut _xmlRef {
7094    crate::xml::validation::add_ref(ctxt, doc, value, attr)
7095}
7096
7097/// Remove an attribute's IDREF entries.
7098///
7099/// # UPSTREAM-PARITY
7100///
7101/// ```c
7102/// int xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr);
7103/// ```
7104#[no_mangle]
7105pub unsafe extern "C" fn xmlRemoveRef(doc: *mut _xmlDoc, attr: *mut _xmlAttr) -> c_int {
7106    crate::xml::validation::remove_ref(doc, attr)
7107}
7108
7109/// Add an ID without a validation context (2.13+).
7110///
7111/// # UPSTREAM-PARITY
7112///
7113/// ```c
7114/// int xmlAddIDSafe(xmlAttrPtr attr, const xmlChar *value);
7115/// ```
7116#[no_mangle]
7117pub unsafe extern "C" fn xmlAddIDSafe(attr: *mut _xmlAttr, value: *const xmlChar) -> c_int {
7118    crate::xml::validation::add_id_safe(attr, value)
7119}
7120
7121/// Free an ID hash table.
7122///
7123/// # UPSTREAM-PARITY
7124///
7125/// ```c
7126/// void xmlFreeIDTable(xmlIDTablePtr table);
7127/// ```
7128#[no_mangle]
7129pub unsafe extern "C" fn xmlFreeIDTable(table: *mut c_void) {
7130    crate::xml::validation::free_id_table(table as *mut crate::xml::hash::HashTable);
7131}
7132
7133/// Free an IDREF hash table.
7134///
7135/// # UPSTREAM-PARITY
7136///
7137/// ```c
7138/// void xmlFreeRefTable(xmlRefTablePtr table);
7139/// ```
7140#[no_mangle]
7141pub unsafe extern "C" fn xmlFreeRefTable(table: *mut c_void) {
7142    crate::xml::validation::free_ref_table(table as *mut crate::xml::hash::HashTable);
7143}
7144
7145/// Look up the attribute holding an ID.
7146///
7147/// # UPSTREAM-PARITY
7148///
7149/// ```c
7150/// xmlAttrPtr xmlGetID(xmlDocPtr doc, const xmlChar *ID);
7151/// ```
7152#[no_mangle]
7153pub unsafe extern "C" fn xmlGetID(doc: *mut _xmlDoc, id: *const xmlChar) -> *mut _xmlAttr {
7154    crate::xml::validation::get_id(doc, id)
7155}
7156
7157/// Look up the list of references for an ID.
7158///
7159/// # UPSTREAM-PARITY
7160///
7161/// ```c
7162/// xmlListPtr xmlGetRefs(xmlDocPtr doc, const xmlChar *ID);
7163/// ```
7164#[no_mangle]
7165pub unsafe extern "C" fn xmlGetRefs(doc: *mut _xmlDoc, id: *const xmlChar) -> *mut c_void {
7166    crate::xml::validation::get_refs(doc, id) as *mut c_void
7167}
7168
7169/// Is this attribute an ID?
7170///
7171/// # UPSTREAM-PARITY
7172///
7173/// ```c
7174/// int xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr);
7175/// ```
7176#[no_mangle]
7177pub unsafe extern "C" fn xmlIsID(
7178    doc: *mut _xmlDoc,
7179    elem: *mut _xmlNode,
7180    attr: *mut _xmlAttr,
7181) -> c_int {
7182    crate::xml::validation::is_id(doc, elem, attr)
7183}
7184
7185/// Is this attribute an IDREF?
7186///
7187/// # UPSTREAM-PARITY
7188///
7189/// ```c
7190/// int xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr);
7191/// ```
7192#[no_mangle]
7193pub unsafe extern "C" fn xmlIsRef(
7194    doc: *mut _xmlDoc,
7195    elem: *mut _xmlNode,
7196    attr: *mut _xmlAttr,
7197) -> c_int {
7198    crate::xml::validation::is_ref(doc, elem, attr)
7199}
7200
7201/// Search a DTD for an element declaration (with QName splitting).
7202///
7203/// # UPSTREAM-PARITY
7204///
7205/// ```c
7206/// xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
7207/// ```
7208#[no_mangle]
7209pub unsafe extern "C" fn xmlGetDtdElementDesc(
7210    dtd: *mut _xmlDtd,
7211    name: *const xmlChar,
7212) -> *mut _xmlElement {
7213    crate::xml::validation::get_dtd_element_desc(dtd, name)
7214}
7215
7216/// Search a DTD for an attribute declaration (with QName splitting).
7217///
7218/// # UPSTREAM-PARITY
7219///
7220/// ```c
7221/// xmlAttributePtr xmlGetDtdAttrDesc(xmlDtdPtr dtd,
7222///                                   const xmlChar *elem,
7223///                                   const xmlChar *name);
7224/// ```
7225#[no_mangle]
7226pub unsafe extern "C" fn xmlGetDtdAttrDesc(
7227    dtd: *mut _xmlDtd,
7228    elem: *const xmlChar,
7229    name: *const xmlChar,
7230) -> *mut _xmlAttribute {
7231    crate::xml::validation::get_dtd_attr_desc(dtd, elem, name)
7232}
7233
7234/// Search a DTD for a qualified element declaration.
7235///
7236/// # UPSTREAM-PARITY
7237///
7238/// ```c
7239/// xmlElementPtr xmlGetDtdQElementDesc(xmlDtdPtr dtd,
7240///                                     const xmlChar *name,
7241///                                     const xmlChar *prefix);
7242/// ```
7243#[no_mangle]
7244pub unsafe extern "C" fn xmlGetDtdQElementDesc(
7245    dtd: *mut _xmlDtd,
7246    name: *const xmlChar,
7247    prefix: *const xmlChar,
7248) -> *mut _xmlElement {
7249    crate::xml::validation::get_dtd_qelement_desc(dtd, name, prefix)
7250}
7251
7252/// Search a DTD for a qualified attribute declaration.
7253///
7254/// # UPSTREAM-PARITY
7255///
7256/// ```c
7257/// xmlAttributePtr xmlGetDtdQAttrDesc(xmlDtdPtr dtd,
7258///                                    const xmlChar *elem,
7259///                                    const xmlChar *name,
7260///                                    const xmlChar *prefix);
7261/// ```
7262#[no_mangle]
7263pub unsafe extern "C" fn xmlGetDtdQAttrDesc(
7264    dtd: *mut _xmlDtd,
7265    elem: *const xmlChar,
7266    name: *const xmlChar,
7267    prefix: *const xmlChar,
7268) -> *mut _xmlAttribute {
7269    crate::xml::validation::get_dtd_qattr_desc(dtd, elem, name, prefix)
7270}
7271
7272/// Search a DTD for a notation declaration.
7273///
7274/// # UPSTREAM-PARITY
7275///
7276/// ```c
7277/// xmlNotationPtr xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name);
7278/// ```
7279#[no_mangle]
7280pub unsafe extern "C" fn xmlGetDtdNotationDesc(
7281    dtd: *mut _xmlDtd,
7282    name: *const xmlChar,
7283) -> *mut _xmlNotation {
7284    crate::xml::validation::get_dtd_notation_desc(dtd, name)
7285}
7286
7287/// Validate the root element of a document.
7288///
7289/// # UPSTREAM-PARITY
7290///
7291/// ```c
7292/// int xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
7293/// ```
7294#[no_mangle]
7295pub unsafe extern "C" fn xmlValidateRoot(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
7296    crate::xml::validation::validate_root(ctxt, doc)
7297}
7298
7299/// Validate element content against its content model.
7300///
7301/// # UPSTREAM-PARITY
7302///
7303/// ```c
7304/// int xmlValidateContent(xmlValidCtxtPtr ctxt,
7305///                        xmlNodePtr node,
7306///                        xmlDocPtr doc);
7307/// ```
7308#[no_mangle]
7309pub unsafe extern "C" fn xmlValidateContent(
7310    ctxt: *mut _xmlValidCtxt,
7311    node: *mut _xmlNode,
7312    doc: *mut _xmlDoc,
7313) -> c_int {
7314    crate::xml::validation::validate_content(ctxt, node, doc)
7315}
7316
7317/// Check if an element is declared as mixed content.
7318///
7319/// # UPSTREAM-PARITY
7320///
7321/// ```c
7322/// int xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name);
7323/// ```
7324#[no_mangle]
7325pub unsafe extern "C" fn xmlIsMixedElement(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
7326    crate::xml::validation::is_mixed_element(doc, name)
7327}
7328
7329/// Check if an element is declared as EMPTY.
7330///
7331/// # UPSTREAM-PARITY
7332///
7333/// ```c
7334/// int xmlIsEmptyElement(xmlDocPtr doc, const xmlChar *name);
7335/// ```
7336#[no_mangle]
7337pub unsafe extern "C" fn xmlIsEmptyElement(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
7338    crate::xml::validation::is_empty_element(doc, name)
7339}
7340
7341/// Validate a DTD's declarations.
7342///
7343/// # UPSTREAM-PARITY
7344///
7345/// ```c
7346/// int xmlValidateDtd(xmlValidCtxtPtr ctxt,
7347///                    xmlDocPtr doc,
7348///                    xmlDtdPtr dtd);
7349/// ```
7350#[no_mangle]
7351pub unsafe extern "C" fn xmlValidateDtd(
7352    ctxt: *mut _xmlValidCtxt,
7353    doc: *mut _xmlDoc,
7354    dtd: *mut _xmlDtd,
7355) -> c_int {
7356    crate::xml::validation::validate_dtd(ctxt, doc, dtd)
7357}
7358
7359/// Final DTD validation (ID/IDREF consistency).
7360///
7361/// # UPSTREAM-PARITY
7362///
7363/// ```c
7364/// int xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
7365/// ```
7366#[no_mangle]
7367pub unsafe extern "C" fn xmlValidateDtdFinal(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
7368    crate::xml::validation::validate_dtd_final(ctxt, doc)
7369}
7370
7371/// Validate that a value is in an enumeration.
7372///
7373/// # UPSTREAM-PARITY
7374///
7375/// ```c
7376/// int xmlValidateEnumeration(xmlValidCtxtPtr ctxt,
7377///                            const xmlChar *value,
7378///                            xmlEnumerationPtr tree);
7379/// ```
7380#[no_mangle]
7381pub unsafe extern "C" fn xmlValidateEnumeration(
7382    ctxt: *mut _xmlValidCtxt,
7383    value: *const xmlChar,
7384    tree: *mut _xmlEnumeration,
7385) -> c_int {
7386    crate::xml::validation::validate_enumeration(ctxt, value, tree)
7387}
7388
7389// ═══════════════════════════════════════════════════════════════════════════════
7390// 18. Debug / Miscellaneous
7391// ═══════════════════════════════════════════════════════════════════════════════
7392
7393/// Dump a document to a file for debugging.
7394
7395/// Get the path to the current executable.
7396///
7397/// # UPSTREAM-PARITY
7398///
7399/// ```c
7400/// char *xmlGetBinaryPath(void);
7401/// ```
7402#[no_mangle]
7403pub extern "C" fn xmlGetBinaryPath() -> *mut c_char {
7404    // Phase 1: STUB
7405    ptr::null_mut()
7406}
7407
7408/// Get the path to the current executable's home directory.
7409///
7410/// # UPSTREAM-PARITY
7411///
7412/// ```c
7413/// char *xmlGetHomeOfBinary(void);
7414/// ```
7415#[no_mangle]
7416pub extern "C" fn xmlGetHomeOfBinary() -> *mut c_char {
7417    // Phase 1: STUB
7418    ptr::null_mut()
7419}
7420
7421// ═══════════════════════════════════════════════════════════════════════════════
7422// SAX2 default callback entry points (upstream SAX2.c)
7423// ═══════════════════════════════════════════════════════════════════════════════
7424//
7425// These are the public `xmlSAX2*` callback functions that downstream code
7426// installs into `xmlSAXHandler` structures. They are the same implementations
7427// the candidate's default SAX handler uses; exporting them under the
7428// upstream names is required for ABI parity (R-000136 closure).
7429
7430/// Upstream SAX2.c `xmlSAX2StartDocument` — public entry point of the default handler.
7431#[no_mangle]
7432pub unsafe extern "C" fn xmlSAX2StartDocument(ctx: *mut c_void) {
7433    crate::xml::sax::default::default_sax_handler::startDocument(ctx)
7434}
7435
7436/// Upstream SAX2.c `xmlSAX2EndDocument` — public entry point of the default handler.
7437#[no_mangle]
7438pub unsafe extern "C" fn xmlSAX2EndDocument(ctx: *mut c_void) {
7439    crate::xml::sax::default::default_sax_handler::endDocument(ctx)
7440}
7441
7442/// Upstream SAX2.c `xmlSAX2StartElementNs` — public entry point of the default handler.
7443#[no_mangle]
7444pub unsafe extern "C" fn xmlSAX2StartElementNs(
7445    ctx: *mut c_void,
7446    localname: *const xmlChar,
7447    prefix: *const xmlChar,
7448    URI: *const xmlChar,
7449    nb_namespaces: c_int,
7450    namespaces: *mut *const xmlChar,
7451    nb_attributes: c_int,
7452    nb_defaulted: c_int,
7453    attributes: *mut *const xmlChar,
7454) {
7455    crate::xml::sax::default::default_sax_handler::startElementNs(
7456        ctx,
7457        localname,
7458        prefix,
7459        URI,
7460        nb_namespaces,
7461        namespaces,
7462        nb_attributes,
7463        nb_defaulted,
7464        attributes,
7465    )
7466}
7467
7468/// Upstream SAX2.c `xmlSAX2EndElementNs` — public entry point of the default handler.
7469#[no_mangle]
7470pub unsafe extern "C" fn xmlSAX2EndElementNs(
7471    ctx: *mut c_void,
7472    localname: *const xmlChar,
7473    prefix: *const xmlChar,
7474    URI: *const xmlChar,
7475) {
7476    crate::xml::sax::default::default_sax_handler::endElementNs(ctx, localname, prefix, URI)
7477}
7478
7479/// Upstream SAX2.c `xmlSAX2Characters` — public entry point of the default handler.
7480#[no_mangle]
7481pub unsafe extern "C" fn xmlSAX2Characters(ctx: *mut c_void, ch: *const xmlChar, len: c_int) {
7482    crate::xml::sax::default::default_sax_handler::characters(ctx, ch, len)
7483}
7484
7485/// Upstream SAX2.c `xmlSAX2IgnorableWhitespace` — public entry point of the default handler.
7486#[no_mangle]
7487pub unsafe extern "C" fn xmlSAX2IgnorableWhitespace(
7488    ctx: *mut c_void,
7489    ch: *const xmlChar,
7490    len: c_int,
7491) {
7492    crate::xml::sax::default::default_sax_handler::ignorableWhitespace(ctx, ch, len)
7493}
7494
7495/// Upstream SAX2.c `xmlSAX2Comment` — public entry point of the default handler.
7496#[no_mangle]
7497pub unsafe extern "C" fn xmlSAX2Comment(ctx: *mut c_void, value: *const xmlChar) {
7498    crate::xml::sax::default::default_sax_handler::comment(ctx, value)
7499}
7500
7501/// Upstream SAX2.c `xmlSAX2ProcessingInstruction` — public entry point of the default handler.
7502#[no_mangle]
7503pub unsafe extern "C" fn xmlSAX2ProcessingInstruction(
7504    ctx: *mut c_void,
7505    target: *const xmlChar,
7506    data: *const xmlChar,
7507) {
7508    crate::xml::sax::default::default_sax_handler::processingInstruction(ctx, target, data)
7509}
7510
7511/// Upstream SAX2.c `xmlSAX2CDataBlock` — public entry point of the default handler.
7512#[no_mangle]
7513pub unsafe extern "C" fn xmlSAX2CDataBlock(ctx: *mut c_void, value: *const xmlChar, len: c_int) {
7514    crate::xml::sax::default::default_sax_handler::cdataBlock(ctx, value, len)
7515}
7516
7517/// Upstream SAX2.c `xmlSAX2InternalSubset` — public entry point of the default handler.
7518#[no_mangle]
7519pub unsafe extern "C" fn xmlSAX2InternalSubset(
7520    ctx: *mut c_void,
7521    name: *const xmlChar,
7522    ExternalID: *const xmlChar,
7523    SystemID: *const xmlChar,
7524) {
7525    crate::xml::sax::default::default_sax_handler::internalSubset(ctx, name, ExternalID, SystemID)
7526}
7527
7528/// Upstream SAX2.c `xmlSAX2ExternalSubset` — public entry point of the default handler.
7529#[no_mangle]
7530pub unsafe extern "C" fn xmlSAX2ExternalSubset(
7531    ctx: *mut c_void,
7532    name: *const xmlChar,
7533    ExternalID: *const xmlChar,
7534    SystemID: *const xmlChar,
7535) {
7536    crate::xml::sax::default::default_sax_handler::externalSubset(ctx, name, ExternalID, SystemID)
7537}
7538
7539/// Upstream SAX2.c `xmlSAX2EntityDecl` — public entry point of the default handler.
7540#[no_mangle]
7541pub unsafe extern "C" fn xmlSAX2EntityDecl(
7542    ctx: *mut c_void,
7543    name: *const xmlChar,
7544    type_: c_int,
7545    publicId: *const xmlChar,
7546    systemId: *const xmlChar,
7547    content: *mut xmlChar,
7548) {
7549    crate::xml::sax::default::default_sax_handler::entityDecl(
7550        ctx, name, type_, publicId, systemId, content,
7551    )
7552}
7553
7554/// Upstream SAX2.c `xmlSAX2AttributeDecl` — public entry point of the default handler.
7555#[no_mangle]
7556pub unsafe extern "C" fn xmlSAX2AttributeDecl(
7557    ctx: *mut c_void,
7558    elem: *const xmlChar,
7559    fullname: *const xmlChar,
7560    type_: c_int,
7561    def: c_int,
7562    defaultValue: *const xmlChar,
7563    tree: *mut crate::abi::structs::_xmlEnumeration,
7564) {
7565    crate::xml::sax::default::default_sax_handler::attributeDecl(
7566        ctx,
7567        elem,
7568        fullname,
7569        type_,
7570        def,
7571        defaultValue,
7572        tree,
7573    )
7574}
7575
7576/// Upstream SAX2.c `xmlSAX2ElementDecl` — public entry point of the default handler.
7577#[no_mangle]
7578pub unsafe extern "C" fn xmlSAX2ElementDecl(
7579    ctx: *mut c_void,
7580    name: *const xmlChar,
7581    type_: c_int,
7582    content: *mut crate::abi::structs::_xmlElementContent,
7583) {
7584    crate::xml::sax::default::default_sax_handler::elementDecl(ctx, name, type_, content)
7585}
7586
7587/// Upstream SAX2.c `xmlSAX2NotationDecl` — public entry point of the default handler.
7588#[no_mangle]
7589pub unsafe extern "C" fn xmlSAX2NotationDecl(
7590    ctx: *mut c_void,
7591    name: *const xmlChar,
7592    publicId: *const xmlChar,
7593    systemId: *const xmlChar,
7594) {
7595    crate::xml::sax::default::default_sax_handler::notationDecl(ctx, name, publicId, systemId)
7596}
7597
7598/// Upstream SAX2.c `xmlSAX2UnparsedEntityDecl` — public entry point of the default handler.
7599#[no_mangle]
7600pub unsafe extern "C" fn xmlSAX2UnparsedEntityDecl(
7601    ctx: *mut c_void,
7602    name: *const xmlChar,
7603    publicId: *const xmlChar,
7604    systemId: *const xmlChar,
7605    notationName: *const xmlChar,
7606) {
7607    crate::xml::sax::default::default_sax_handler::unparsedEntityDecl(
7608        ctx,
7609        name,
7610        publicId,
7611        systemId,
7612        notationName,
7613    )
7614}
7615
7616/// Upstream SAX2.c `xmlSAX2ResolveEntity` — public entry point of the default handler.
7617#[no_mangle]
7618pub unsafe extern "C" fn xmlSAX2ResolveEntity(
7619    ctx: *mut c_void,
7620    publicId: *const xmlChar,
7621    systemId: *const xmlChar,
7622) -> *mut crate::abi::structs::_xmlParserInput {
7623    crate::xml::sax::default::default_sax_handler::resolveEntity(ctx, publicId, systemId)
7624}
7625
7626/// Upstream SAX2.c `xmlSAX2IsStandalone` — public entry point of the default handler.
7627#[no_mangle]
7628pub unsafe extern "C" fn xmlSAX2IsStandalone(ctx: *mut c_void) -> c_int {
7629    crate::xml::sax::default::default_sax_handler::isStandalone(ctx)
7630}
7631
7632/// Upstream SAX2.c `xmlSAX2HasInternalSubset` — public entry point of the default handler.
7633#[no_mangle]
7634pub unsafe extern "C" fn xmlSAX2HasInternalSubset(ctx: *mut c_void) -> c_int {
7635    crate::xml::sax::default::default_sax_handler::hasInternalSubset(ctx)
7636}
7637
7638/// Upstream SAX2.c `xmlSAX2HasExternalSubset` — public entry point of the default handler.
7639#[no_mangle]
7640pub unsafe extern "C" fn xmlSAX2HasExternalSubset(ctx: *mut c_void) -> c_int {
7641    crate::xml::sax::default::default_sax_handler::hasExternalSubset(ctx)
7642}
7643
7644/// Upstream SAX2.c `xmlSAX2GetEntity` — public entry point of the default handler.
7645#[no_mangle]
7646pub unsafe extern "C" fn xmlSAX2GetEntity(
7647    ctx: *mut c_void,
7648    name: *const xmlChar,
7649) -> *mut crate::abi::structs::_xmlEntity {
7650    crate::xml::sax::default::default_sax_handler::getEntity(ctx, name)
7651}
7652
7653/// Upstream SAX2.c `xmlSAX2GetParameterEntity` — public entry point of the default handler.
7654#[no_mangle]
7655pub unsafe extern "C" fn xmlSAX2GetParameterEntity(
7656    ctx: *mut c_void,
7657    name: *const xmlChar,
7658) -> *mut crate::abi::structs::_xmlEntity {
7659    crate::xml::sax::default::default_sax_handler::getParameterEntity(ctx, name)
7660}
7661
7662/// Upstream SAX2.c `xmlSAX2GetLineNumber` — public entry point of the
7663/// default handler (SAX locator callback).
7664#[no_mangle]
7665pub unsafe extern "C" fn xmlSAX2GetLineNumber(ctx: *mut c_void) -> c_int {
7666    crate::xml::sax::default::default_sax_handler::getLineNumber(ctx)
7667}
7668
7669/// Upstream SAX2.c `xmlSAX2GetColumnNumber`.
7670#[no_mangle]
7671pub unsafe extern "C" fn xmlSAX2GetColumnNumber(ctx: *mut c_void) -> c_int {
7672    crate::xml::sax::default::default_sax_handler::getColumnNumber(ctx)
7673}
7674
7675/// Upstream SAX2.c `xmlSAX2GetPublicId`.
7676#[no_mangle]
7677pub unsafe extern "C" fn xmlSAX2GetPublicId(ctx: *mut c_void) -> *const xmlChar {
7678    crate::xml::sax::default::default_sax_handler::getPublicId(ctx)
7679}
7680
7681/// Upstream SAX2.c `xmlSAX2GetSystemId`.
7682#[no_mangle]
7683pub unsafe extern "C" fn xmlSAX2GetSystemId(ctx: *mut c_void) -> *const xmlChar {
7684    crate::xml::sax::default::default_sax_handler::getSystemId(ctx)
7685}
7686
7687/// Upstream SAX2.c `xmlSAX2StartElement` — SAX1 start-element entry point.
7688/// The candidate parser dispatches through the SAX2 (namespaced) callbacks;
7689/// this wrapper maps to the SAX1 handler when installed.
7690#[no_mangle]
7691pub unsafe extern "C" fn xmlSAX2StartElement(
7692    ctx: *mut c_void,
7693    name: *const xmlChar,
7694    atts: *mut *const xmlChar,
7695) {
7696    // The parser core invokes startElementNs; the SAX1 shim is provided by
7697    // the dispatch layer. When this entry point is installed directly on a
7698    // handler, route through the internal SAX1 path.
7699    crate::xml::sax::dispatch::SaxDispatcher::sax1_start_element(ctx, name, atts);
7700}
7701
7702/// Upstream SAX2.c `xmlSAX2EndElement` — SAX1 end-element entry point.
7703#[no_mangle]
7704pub unsafe extern "C" fn xmlSAX2EndElement(ctx: *mut c_void, name: *const xmlChar) {
7705    crate::xml::sax::dispatch::SaxDispatcher::sax1_end_element(ctx, name);
7706}
7707
7708/// Upstream SAX2.c `xmlSAX2SetDocumentLocator` — public entry point of the default handler.
7709#[no_mangle]
7710pub unsafe extern "C" fn xmlSAX2SetDocumentLocator(
7711    ctx: *mut c_void,
7712    loc: *mut crate::abi::callbacks::_xmlSAXLocator,
7713) {
7714    crate::xml::sax::default::default_sax_handler::setDocumentLocator(ctx, loc)
7715}
7716
7717/// Upstream SAX2.c `xmlSAX2Reference` — public entry point of the default handler.
7718#[no_mangle]
7719pub unsafe extern "C" fn xmlSAX2Reference(ctx: *mut c_void, name: *const xmlChar) {
7720    crate::xml::sax::default::default_sax_handler::reference(ctx, name)
7721}