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;
47use std::mem::size_of;
48use std::os::raw::{c_char, c_int, c_uint};
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/// Clean up the parser library.
81///
82/// Should be called when the library is no longer needed.
83///
84/// # UPSTREAM-PARITY
85///
86/// ```c
87/// void xmlCleanupParser(void);
88/// ```
89#[no_mangle]
90pub unsafe extern "C" fn xmlCleanupParser() {
91    crate::internal::globals::cleanup_parser();
92}
93
94/// Initialize threading support.
95///
96/// # UPSTREAM-PARITY
97///
98/// ```c
99/// int xmlInitThreads(void);
100/// ```
101///
102/// Returns 0 on success.
103#[no_mangle]
104pub unsafe extern "C" fn xmlInitThreads() -> c_int {
105    crate::internal::globals::init_threads()
106}
107
108/// Clean up threading support.
109///
110/// # UPSTREAM-PARITY
111///
112/// ```c
113/// void xmlCleanupThreads(void);
114/// ```
115#[no_mangle]
116pub unsafe extern "C" fn xmlCleanupThreads() {
117    crate::xml::threads::cleanup_threads();
118}
119
120/// Check whether the library has been initialized.
121///
122/// # UPSTREAM-PARITY
123///
124/// ```c
125/// int xmlIsInitialized(void);
126/// ```
127#[no_mangle]
128pub extern "C" fn xmlIsInitialized() -> c_int {
129    if crate::abi::versioning::is_initialized() {
130        1
131    } else {
132        0
133    }
134}
135
136/// Initialize a set of threads (libxml2 compat).
137///
138/// # UPSTREAM-PARITY
139///
140/// ```c
141/// int xmlInitThreads(void);
142/// ```
143/// This is an alias.
144#[no_mangle]
145pub unsafe extern "C" fn xmlLockLibrary() {
146    crate::xml::threads::lock_library();
147}
148
149/// Unlock the library (libxml2 compat).
150///
151/// # UPSTREAM-PARITY
152///
153/// ```c
154/// void xmlUnlockLibrary(void);
155/// ```
156#[no_mangle]
157pub unsafe extern "C" fn xmlUnlockLibrary() {
158    crate::xml::threads::unlock_library();
159}
160
161// ═══════════════════════════════════════════════════════════════════════════════
162// 4. Error Handling
163// ═══════════════════════════════════════════════════════════════════════════════
164
165/// Set the generic error handler.
166///
167/// # UPSTREAM-PARITY
168///
169/// ```c
170/// void xmlSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler);
171/// ```
172///
173/// # SAFETY
174///
175/// - `handler` must be a valid function pointer or NULL (to reset to default).
176/// - If non-NULL, the handler may be called at any time with `ctx`.
177#[no_mangle]
178pub unsafe extern "C" fn xmlSetGenericErrorFunc(
179    ctx: *mut c_void,
180    handler: Option<xmlGenericErrorFunc>,
181) {
182    // SAFETY: Delegates to xml::errors with same safety contract.
183    unsafe { crate::xml::errors::set_generic_error_func(ctx, handler) };
184}
185
186/// Set the structured error handler.
187///
188/// # UPSTREAM-PARITY
189///
190/// ```c
191/// void xmlSetStructuredErrorFunc(void *ctx, xmlStructuredErrorFunc handler);
192/// ```
193///
194/// # SAFETY
195///
196/// - `handler` must be a valid function pointer or NULL.
197#[no_mangle]
198pub unsafe extern "C" fn xmlSetStructuredErrorFunc(
199    ctx: *mut c_void,
200    handler: Option<xmlStructuredErrorFunc>,
201) {
202    // SAFETY: Delegates to xml::errors with same safety contract.
203    unsafe { crate::xml::errors::set_structured_error_func(ctx, handler) };
204}
205
206/// Get the last error for the current thread.
207///
208/// # UPSTREAM-PARITY
209///
210/// ```c
211/// xmlErrorPtr xmlGetLastError(void);
212/// ```
213///
214/// Returns a pointer to the last error, or NULL if no error occurred.
215/// The returned pointer is valid until the next libxml2 call in this thread.
216#[no_mangle]
217pub extern "C" fn xmlGetLastError() -> *mut _xmlError {
218    crate::xml::errors::get_last_error()
219}
220
221/// Get a copy of the last error for the current thread.
222///
223/// # UPSTREAM-PARITY
224///
225/// ```c
226/// xmlErrorPtr xmlCopyError(xmlErrorPtr from, xmlErrorPtr to);
227/// ```
228///
229/// Copies `from` into `to`. Returns 0 on success, -1 on error.
230///
231/// # SAFETY
232///
233/// - `from` and `to` must be valid pointers to `_xmlError` structs, or NULL.
234#[no_mangle]
235pub unsafe extern "C" fn xmlCopyError(from: *const _xmlError, to: *mut _xmlError) -> c_int {
236    // SAFETY: Delegates to xml::errors with same safety contract.
237    unsafe { crate::xml::errors::copy_error(from, to) }
238}
239
240/// Reset an error structure.
241///
242/// # UPSTREAM-PARITY
243///
244/// ```c
245/// void xmlResetError(xmlErrorPtr err);
246/// ```
247///
248/// # SAFETY
249///
250/// - `err` must be a valid pointer to `_xmlError`, or NULL.
251#[no_mangle]
252pub unsafe extern "C" fn xmlResetError(err: *mut _xmlError) {
253    // SAFETY: Delegates to xml::errors with same safety contract.
254    unsafe { crate::xml::errors::reset_error(err) };
255}
256
257/// Raise a structured error.
258///
259/// This is called internally when an error occurs. It updates the last error
260/// and invokes the structured error handler if one is set.
261///
262/// # SAFETY
263///
264/// - `ctxt` may be NULL (context of the error).
265/// - `domain`, `code`, `level`: valid error codes.
266/// - `msg` must be a valid C string or NULL.
267/// - `file` must be a valid C string or NULL.
268/// - `str1`, `str2`, `str3`: error-related strings (may be NULL).
269#[no_mangle]
270pub unsafe extern "C" fn xmlRaiseError(
271    ctxt: *mut c_void,
272    ctxt2: *mut c_void,
273    ctxt3: *mut c_void,
274    ctxt4: *mut c_void,
275    ctxt5: *mut c_void,
276    domain: c_int,
277    code: c_int,
278    level: c_int,
279    file: *const c_char,
280    line: c_int,
281    str1: *const c_char,
282    str2: *const c_char,
283    str3: *const c_char,
284    int1: c_int,
285    int2: c_int,
286    msg: *const c_char,
287) {
288    // SAFETY: Delegates to xml::errors with same safety contract.
289    unsafe {
290        crate::xml::errors::raise_error(
291            ctxt, ctxt2, ctxt3, ctxt4, ctxt5, domain, code, level, file, line, str1, str2, str3,
292            int1, int2, msg,
293        );
294    }
295}
296
297/// Remove any error from the last error stack.
298///
299/// # UPSTREAM-PARITY
300///
301/// ```c
302/// void xmlResetLastError(void);
303/// ```
304#[no_mangle]
305pub extern "C" fn xmlResetLastError() {
306    crate::xml::errors::reset_last_error();
307}
308
309// ═══════════════════════════════════════════════════════════════════════════════
310// 5. String Utilities
311// ═══════════════════════════════════════════════════════════════════════════════
312
313/// Duplicate a string using xmlChar.
314///
315/// # UPSTREAM-PARITY
316///
317/// ```c
318/// xmlChar *xmlStrdup(const xmlChar *cur);
319/// ```
320///
321/// # SAFETY
322///
323/// - `cur` must be a valid null-terminated xmlChar string or NULL.
324#[no_mangle]
325pub unsafe extern "C" fn xmlStrdup(cur: *const xmlChar) -> *mut xmlChar {
326    if cur.is_null() {
327        return ptr::null_mut();
328    }
329    let len = unsafe { xmlStrlen(cur) };
330    let size = len + 1;
331    let new_ptr = unsafe { xmlMalloc(size as usize) };
332    if new_ptr.is_null() {
333        return ptr::null_mut();
334    }
335    unsafe {
336        ptr::copy_nonoverlapping(cur as *const u8, new_ptr as *mut u8, size as usize);
337    }
338    new_ptr as *mut xmlChar
339}
340
341/// Duplicate a substring.
342///
343/// # UPSTREAM-PARITY
344///
345/// ```c
346/// xmlChar *xmlStrndup(const xmlChar *cur, int len);
347/// ```
348///
349/// # SAFETY
350///
351/// - `cur` must be a valid pointer or NULL.
352#[no_mangle]
353pub unsafe extern "C" fn xmlStrndup(cur: *const xmlChar, len: c_int) -> *mut xmlChar {
354    if cur.is_null() || len <= 0 {
355        return ptr::null_mut();
356    }
357    let size = len as usize + 1;
358    let new_ptr = unsafe { xmlMalloc(size) };
359    if new_ptr.is_null() {
360        return ptr::null_mut();
361    }
362    unsafe {
363        ptr::copy_nonoverlapping(cur as *const u8, new_ptr as *mut u8, len as usize);
364        *(new_ptr.add(len as usize) as *mut u8) = 0;
365    }
366    new_ptr as *mut xmlChar
367}
368
369/// Get the length of an xmlChar string.
370///
371/// # UPSTREAM-PARITY
372///
373/// ```c
374/// int xmlStrlen(const xmlChar *str);
375/// ```
376///
377/// # SAFETY
378///
379/// - `str` must be a valid null-terminated string or NULL (returns 0).
380#[no_mangle]
381pub unsafe extern "C" fn xmlStrlen(str: *const xmlChar) -> c_int {
382    if str.is_null() {
383        return 0;
384    }
385    unsafe { libc::strlen(str as *const c_char) as c_int }
386}
387
388/// Compare two xmlChar strings.
389///
390/// # UPSTREAM-PARITY
391///
392/// ```c
393/// int xmlStrcmp(const xmlChar *str1, const xmlChar *str2);
394/// ```
395///
396/// Returns 0 if equal, <0 if str1 < str2, >0 if str1 > str2.
397/// NULL-safe: NULL sorts before any non-NULL string.
398#[no_mangle]
399pub unsafe extern "C" fn xmlStrcmp(str1: *const xmlChar, str2: *const xmlChar) -> c_int {
400    if str1.is_null() && str2.is_null() {
401        return 0;
402    }
403    if str1.is_null() {
404        return -1;
405    }
406    if str2.is_null() {
407        return 1;
408    }
409    unsafe { libc::strcmp(str1 as *const c_char, str2 as *const c_char) as c_int }
410}
411
412/// Compare two xmlChar strings up to a given length.
413///
414/// # UPSTREAM-PARITY
415///
416/// ```c
417/// int xmlStrncmp(const xmlChar *str1, const xmlChar *str2, int len);
418/// ```
419#[no_mangle]
420pub unsafe extern "C" fn xmlStrncmp(
421    str1: *const xmlChar,
422    str2: *const xmlChar,
423    len: c_int,
424) -> c_int {
425    if len <= 0 {
426        return 0;
427    }
428    if str1.is_null() && str2.is_null() {
429        return 0;
430    }
431    if str1.is_null() {
432        return -1;
433    }
434    if str2.is_null() {
435        return 1;
436    }
437    unsafe { libc::strncmp(str1 as *const c_char, str2 as *const c_char, len as usize) as c_int }
438}
439
440/// Case-insensitive comparison of two xmlChar strings.
441///
442/// # UPSTREAM-PARITY
443///
444/// ```c
445/// int xmlStrcasecmp(const xmlChar *str1, const xmlChar *str2);
446/// ```
447#[no_mangle]
448pub unsafe extern "C" fn xmlStrcasecmp(str1: *const xmlChar, str2: *const xmlChar) -> c_int {
449    if str1.is_null() && str2.is_null() {
450        return 0;
451    }
452    if str1.is_null() {
453        return -1;
454    }
455    if str2.is_null() {
456        return 1;
457    }
458    unsafe { libc::strcasecmp(str1 as *const c_char, str2 as *const c_char) as c_int }
459}
460
461/// Case-insensitive comparison with length limit.
462///
463/// # UPSTREAM-PARITY
464///
465/// ```c
466/// int xmlStrncasecmp(const xmlChar *str1, const xmlChar *str2, int len);
467/// ```
468#[no_mangle]
469pub unsafe extern "C" fn xmlStrncasecmp(
470    str1: *const xmlChar,
471    str2: *const xmlChar,
472    len: c_int,
473) -> c_int {
474    if len <= 0 {
475        return 0;
476    }
477    if str1.is_null() && str2.is_null() {
478        return 0;
479    }
480    if str1.is_null() {
481        return -1;
482    }
483    if str2.is_null() {
484        return 1;
485    }
486    unsafe {
487        libc::strncasecmp(str1 as *const c_char, str2 as *const c_char, len as usize) as c_int
488    }
489}
490
491/// Check if two xmlChar strings are equal.
492///
493/// # UPSTREAM-PARITY
494///
495/// ```c
496/// int xmlStrEqual(const xmlChar *str1, const xmlChar *str2);
497/// ```
498///
499/// Returns 1 if equal, 0 if not. NULL-safe.
500#[no_mangle]
501pub unsafe extern "C" fn xmlStrEqual(str1: *const xmlChar, str2: *const xmlChar) -> c_int {
502    if str1.is_null() && str2.is_null() {
503        return 1;
504    }
505    if str1.is_null() || str2.is_null() {
506        return 0;
507    }
508    unsafe { (libc::strcmp(str1 as *const c_char, str2 as *const c_char) == 0) as c_int }
509}
510
511/// Check if an xmlChar string equals a qualified name.
512///
513/// # UPSTREAM-PARITY
514///
515/// ```c
516/// int xmlStrQEqual(const xmlChar *pref, const xmlChar *name, const xmlChar *str);
517/// ```
518///
519/// Returns 1 if `pref:name` equals `str`, 0 otherwise.
520/// `pref` may be NULL (compares only name).
521#[no_mangle]
522pub unsafe extern "C" fn xmlStrQEqual(
523    pref: *const xmlChar,
524    name: *const xmlChar,
525    str: *const xmlChar,
526) -> c_int {
527    if name.is_null() || str.is_null() {
528        return 0;
529    }
530    if pref.is_null() {
531        return unsafe { xmlStrEqual(name, str) };
532    }
533    // Compare "pref:name" with str
534    let pref_len = unsafe { xmlStrlen(pref) };
535    let name_len = unsafe { xmlStrlen(name) };
536    let total_len = pref_len + 1 + name_len;
537    let str_len = unsafe { xmlStrlen(str) };
538    if total_len != str_len {
539        return 0;
540    }
541    // Compare prefix part
542    if unsafe {
543        libc::strncmp(
544            pref as *const c_char,
545            str as *const c_char,
546            pref_len as usize,
547        )
548    } != 0
549    {
550        return 0;
551    }
552    // Check colon
553    if unsafe { *str.add(pref_len as usize) } != b':' as xmlChar {
554        return 0;
555    }
556    // Compare name part
557    (unsafe {
558        libc::strncmp(
559            name as *const c_char,
560            str.add((pref_len + 1) as usize) as *const c_char,
561            name_len as usize,
562        ) == 0
563    }) as c_int
564}
565
566/// Concatenate two strings.
567///
568/// # UPSTREAM-PARITY
569///
570/// ```c
571/// xmlChar *xmlStrcat(xmlChar *cur, const xmlChar *add);
572/// ```
573///
574/// # SAFETY
575///
576/// - `cur` must be a valid xmlMalloc'd string or NULL.
577/// - `add` must be a valid string or NULL.
578/// - If `cur` is NULL, behaves like xmlStrdup(add).
579#[no_mangle]
580pub unsafe extern "C" fn xmlStrcat(cur: *mut xmlChar, add: *const xmlChar) -> *mut xmlChar {
581    if add.is_null() {
582        return cur;
583    }
584    if cur.is_null() {
585        return unsafe { xmlStrdup(add) };
586    }
587    let cur_len = unsafe { xmlStrlen(cur) } as usize;
588    let add_len = unsafe { xmlStrlen(add) } as usize;
589    let new_size = cur_len + add_len + 1;
590    let new_ptr = unsafe { xmlRealloc(cur as *mut c_void, new_size) };
591    if new_ptr.is_null() {
592        return ptr::null_mut();
593    }
594    unsafe {
595        ptr::copy_nonoverlapping(add as *const u8, (new_ptr as *mut u8).add(cur_len), add_len);
596        *((new_ptr as *mut u8).add(cur_len + add_len)) = 0;
597    }
598    new_ptr as *mut xmlChar
599}
600
601/// Concatenate up to `len` characters.
602///
603/// # UPSTREAM-PARITY
604///
605/// ```c
606/// xmlChar *xmlStrncat(xmlChar *cur, const xmlChar *add, int len);
607/// ```
608///
609/// # SAFETY
610///
611/// Same as xmlStrcat, but only copies up to `len` characters from `add`.
612#[no_mangle]
613pub unsafe extern "C" fn xmlStrncat(
614    cur: *mut xmlChar,
615    add: *const xmlChar,
616    len: c_int,
617) -> *mut xmlChar {
618    if add.is_null() || len <= 0 {
619        return cur;
620    }
621    let len = len as usize;
622    if cur.is_null() {
623        return unsafe { xmlStrndup(add, len as c_int) };
624    }
625    let cur_len = unsafe { xmlStrlen(cur) } as usize;
626    let new_size = cur_len + len + 1;
627    let new_ptr = unsafe { xmlRealloc(cur as *mut c_void, new_size) };
628    if new_ptr.is_null() {
629        return ptr::null_mut();
630    }
631    unsafe {
632        ptr::copy_nonoverlapping(add as *const u8, (new_ptr as *mut u8).add(cur_len), len);
633        *((new_ptr as *mut u8).add(cur_len + len)) = 0;
634    }
635    new_ptr as *mut xmlChar
636}
637
638/// Create a new string by concatenating up to `len` characters.
639///
640/// # UPSTREAM-PARITY
641///
642/// ```c
643/// xmlChar *xmlStrncatNew(const xmlChar *str1, const xmlChar *str2, int len);
644/// ```
645#[no_mangle]
646pub unsafe extern "C" fn xmlStrncatNew(
647    str1: *const xmlChar,
648    str2: *const xmlChar,
649    len: c_int,
650) -> *mut xmlChar {
651    let mut result: *mut xmlChar = ptr::null_mut();
652    if !str1.is_null() {
653        result = unsafe { xmlStrdup(str1) };
654    }
655    if !str2.is_null() && len > 0 {
656        result = unsafe { xmlStrncat(result, str2, len) };
657    }
658    result
659}
660
661/// Copy a string.
662///
663/// # UPSTREAM-PARITY
664///
665/// ```c
666/// xmlChar *xmlStrcpy(xmlChar *dst, const xmlChar *src);
667/// ```
668///
669/// # SAFETY
670///
671/// - `dst` must be a valid xmlMalloc'd buffer large enough to hold `src`.
672/// - `src` must be a valid string.
673#[no_mangle]
674pub unsafe extern "C" fn xmlStrcpy(dst: *mut xmlChar, src: *const xmlChar) -> *mut xmlChar {
675    if dst.is_null() || src.is_null() {
676        return dst;
677    }
678    let len = unsafe { xmlStrlen(src) } as usize + 1;
679    unsafe {
680        ptr::copy_nonoverlapping(src as *const u8, dst as *mut u8, len);
681    }
682    dst
683}
684
685/// Copy up to `len` characters.
686///
687/// # UPSTREAM-PARITY
688///
689/// ```c
690/// xmlChar *xmlStrncpy(xmlChar *dst, const xmlChar *src, int len);
691/// ```
692#[no_mangle]
693pub unsafe extern "C" fn xmlStrncpy(
694    dst: *mut xmlChar,
695    src: *const xmlChar,
696    len: c_int,
697) -> *mut xmlChar {
698    if dst.is_null() || src.is_null() || len <= 0 {
699        return dst;
700    }
701    let len = len as usize;
702    let src_len = unsafe { xmlStrlen(src) } as usize;
703    let copy_len = if src_len < len { src_len } else { len - 1 };
704    unsafe {
705        ptr::copy_nonoverlapping(src as *const u8, dst as *mut u8, copy_len);
706        *dst.add(copy_len) = 0;
707    }
708    dst
709}
710
711/// Extract a substring.
712///
713/// # UPSTREAM-PARITY
714///
715/// ```c
716/// xmlChar *xmlStrsub(const xmlChar *str, int start, int len);
717/// ```
718///
719/// Returns a newly allocated substring, or NULL on error.
720#[no_mangle]
721pub unsafe extern "C" fn xmlStrsub(str: *const xmlChar, start: c_int, len: c_int) -> *mut xmlChar {
722    if str.is_null() || start < 0 || len < 0 {
723        return ptr::null_mut();
724    }
725    let str_len = unsafe { xmlStrlen(str) };
726    if start >= str_len {
727        return unsafe { xmlStrdup(b"\0" as *const u8 as *const xmlChar) };
728    }
729    let actual_len = if start + len > str_len {
730        str_len - start
731    } else {
732        len
733    };
734    unsafe { xmlStrndup(str.add(start as usize), actual_len) }
735}
736
737// ═══════════════════════════════════════════════════════════════════════════════
738// 6. Tree — Document, Node, Attribute, Namespace, DTD, Entity
739// ═══════════════════════════════════════════════════════════════════════════════
740
741/// Create a new document.
742///
743/// # UPSTREAM-PARITY
744///
745/// ```c
746/// xmlDocPtr xmlNewDoc(const xmlChar *version);
747/// ```
748///
749/// # SAFETY
750///
751/// - `version` must be a valid string or NULL (defaults to "1.0").
752/// - Returns a newly allocated document. Caller must free with `xmlFreeDoc`.
753#[no_mangle]
754pub unsafe extern "C" fn xmlNewDoc(version: *const xmlChar) -> *mut _xmlDoc {
755    crate::xml::tree::new_doc(version)
756}
757
758/// Free a document.
759///
760/// # UPSTREAM-PARITY
761///
762/// ```c
763/// void xmlFreeDoc(xmlDocPtr doc);
764/// ```
765///
766/// # SAFETY
767///
768/// - `doc` must be a valid document pointer or NULL.
769#[no_mangle]
770pub unsafe extern "C" fn xmlFreeDoc(doc: *mut _xmlDoc) {
771    crate::xml::tree::free_doc(doc);
772}
773
774/// Create a new node.
775///
776/// # UPSTREAM-PARITY
777///
778/// ```c
779/// xmlNodePtr xmlNewNode(xmlNsPtr ns, const xmlChar *name);
780/// ```
781///
782/// # SAFETY
783///
784/// - `ns` may be NULL.
785/// - `name` must be a valid string.
786/// - Returns a newly allocated node. Caller must free with `xmlFreeNode`.
787#[no_mangle]
788pub unsafe extern "C" fn xmlNewNode(ns: *mut _xmlNs, name: *const xmlChar) -> *mut _xmlNode {
789    crate::xml::tree::new_node(ns, name)
790}
791
792/// Free a node.
793///
794/// # UPSTREAM-PARITY
795///
796/// ```c
797/// void xmlFreeNode(xmlNodePtr node);
798/// ```
799///
800/// # SAFETY
801///
802/// - `node` must be a valid node pointer or NULL.
803/// - The node must NOT be part of a document tree (must be unlinked first).
804#[no_mangle]
805pub unsafe extern "C" fn xmlFreeNode(node: *mut _xmlNode) {
806    crate::xml::tree::free_node(node);
807}
808
809/// Unlink a node from its tree.
810///
811/// # UPSTREAM-PARITY
812///
813/// ```c
814/// void xmlUnlinkNode(xmlNodePtr node);
815/// ```
816///
817/// # SAFETY
818///
819/// - `node` must be a valid node pointer or NULL.
820#[no_mangle]
821pub unsafe extern "C" fn xmlUnlinkNode(node: *mut _xmlNode) {
822    crate::xml::tree::unlink_node(node);
823}
824
825/// Add a child node.
826///
827/// # UPSTREAM-PARITY
828///
829/// ```c
830/// xmlNodePtr xmlAddChild(xmlNodePtr parent, xmlNodePtr cur);
831/// ```
832///
833/// # SAFETY
834///
835/// - `parent` must be a valid node.
836/// - `cur` must be a valid node (ownership transfers to parent).
837/// - Returns pointer to the added child (borrowed).
838#[no_mangle]
839pub unsafe extern "C" fn xmlAddChild(parent: *mut _xmlNode, cur: *mut _xmlNode) -> *mut _xmlNode {
840    crate::xml::tree::add_child(parent, cur)
841}
842
843/// Add a sibling node.
844///
845/// # UPSTREAM-PARITY
846///
847/// ```c
848/// xmlNodePtr xmlAddSibling(xmlNodePtr cur, xmlNodePtr sibling);
849/// ```
850///
851/// # SAFETY
852///
853/// Same as xmlAddChild, but adds after `cur` instead of as a child.
854#[no_mangle]
855pub unsafe extern "C" fn xmlAddSibling(
856    cur: *mut _xmlNode,
857    sibling: *mut _xmlNode,
858) -> *mut _xmlNode {
859    crate::xml::tree::add_sibling(cur, sibling)
860}
861
862/// Create a new child element.
863///
864/// # UPSTREAM-PARITY
865///
866/// ```c
867/// xmlNodePtr xmlNewChild(xmlNodePtr parent, xmlNsPtr ns,
868///                        const xmlChar *name, const xmlChar *content);
869/// ```
870///
871/// Creates a new element node, adds it as a child of `parent`, and
872/// sets its content if `content` is non-NULL.
873///
874/// # SAFETY
875///
876/// - `parent` must be a valid node (may be NULL).
877/// - `ns` may be NULL.
878/// - `name` must be a valid string.
879/// - Returns a newly allocated node (owned by parent).
880#[no_mangle]
881pub unsafe extern "C" fn xmlNewChild(
882    parent: *mut _xmlNode,
883    ns: *mut _xmlNs,
884    name: *const xmlChar,
885    content: *const xmlChar,
886) -> *mut _xmlNode {
887    crate::xml::tree::new_child(parent, ns, name)
888}
889
890/// Set the root element of a document.
891///
892/// # UPSTREAM-PARITY
893///
894/// ```c
895/// xmlNodePtr xmlDocSetRootElement(xmlDocPtr doc, xmlNodePtr root);
896/// ```
897///
898/// Returns the old root element (if any), which the caller must free.
899///
900/// # SAFETY
901///
902/// - `doc` must be a valid document.
903/// - `root` must be a valid node (ownership transfers to doc).
904#[no_mangle]
905pub unsafe extern "C" fn xmlDocSetRootElement(
906    doc: *mut _xmlDoc,
907    root: *mut _xmlNode,
908) -> *mut _xmlNode {
909    crate::xml::tree::doc_set_root_element(doc, root)
910}
911
912/// Get the root element of a document.
913///
914/// # UPSTREAM-PARITY
915///
916/// ```c
917/// xmlNodePtr xmlDocGetRootElement(const xmlDoc *doc);
918/// ```
919///
920/// Returns a borrowed pointer (do not free).
921#[no_mangle]
922pub extern "C" fn xmlDocGetRootElement(doc: *const _xmlDoc) -> *mut _xmlNode {
923    crate::xml::tree::doc_get_root_element(doc as *mut _xmlDoc)
924}
925
926/// Copy a node.
927///
928/// # UPSTREAM-PARITY
929///
930/// ```c
931/// xmlNodePtr xmlCopyNode(const xmlNodePtr node, int extended);
932/// ```
933///
934/// If `extended` is 1, copies recursively (deep copy).
935/// If `extended` is 0, copies only the node itself (shallow copy).
936///
937/// Returns a newly allocated copy. Caller must free with `xmlFreeNode`.
938#[no_mangle]
939pub unsafe extern "C" fn xmlCopyNode(node: *const _xmlNode, extended: c_int) -> *mut _xmlNode {
940    crate::xml::tree::copy_node(node, extended)
941}
942
943/// Copy a document.
944///
945/// # UPSTREAM-PARITY
946///
947/// ```c
948/// xmlDocPtr xmlCopyDoc(const xmlDocPtr doc, int recursive);
949/// ```
950///
951/// Returns a newly allocated copy. Caller must free with `xmlFreeDoc`.
952#[no_mangle]
953pub unsafe extern "C" fn xmlCopyDoc(doc: *const _xmlDoc, recursive: c_int) -> *mut _xmlDoc {
954    crate::xml::tree::copy_doc(doc, recursive)
955}
956
957/// Create a text node.
958///
959/// # UPSTREAM-PARITY
960///
961/// ```c
962/// xmlNodePtr xmlNewText(const xmlChar *content);
963/// ```
964///
965/// Creates a new text node with the given content.
966/// If `content` is NULL, creates an empty text node.
967#[no_mangle]
968pub unsafe extern "C" fn xmlNewText(content: *const xmlChar) -> *mut _xmlNode {
969    crate::xml::tree::new_text(content)
970}
971
972/// Create a new comment node.
973///
974/// # UPSTREAM-PARITY
975///
976/// ```c
977/// xmlNodePtr xmlNewComment(const xmlChar *content);
978/// ```
979#[no_mangle]
980pub unsafe extern "C" fn xmlNewComment(content: *const xmlChar) -> *mut _xmlNode {
981    crate::xml::tree::new_comment(content)
982}
983
984/// Create a new PI node.
985///
986/// # UPSTREAM-PARITY
987///
988/// ```c
989/// xmlNodePtr xmlNewPI(const xmlChar *name, const xmlChar *content);
990/// ```
991#[no_mangle]
992pub unsafe extern "C" fn xmlNewPI(name: *const xmlChar, content: *const xmlChar) -> *mut _xmlNode {
993    crate::xml::tree::new_pi(name, content)
994}
995
996/// Create a new CDATA node.
997///
998/// # UPSTREAM-PARITY
999///
1000/// ```c
1001/// xmlNodePtr xmlNewCDataBlock(xmlDocPtr doc, const xmlChar *content, int len);
1002/// ```
1003#[no_mangle]
1004pub unsafe extern "C" fn xmlNewCDataBlock(
1005    doc: *mut _xmlDoc,
1006    content: *const xmlChar,
1007    len: c_int,
1008) -> *mut _xmlNode {
1009    crate::xml::tree::new_cdata_block(doc, content, len)
1010}
1011
1012/// Create a new namespace definition.
1013///
1014/// # UPSTREAM-PARITY
1015///
1016/// ```c
1017/// xmlNsPtr xmlNewNs(xmlNodePtr node, const xmlChar *href, const xmlChar *prefix);
1018/// ```
1019///
1020/// # SAFETY
1021///
1022/// - `node` may be NULL.
1023/// - `href` and `prefix` are copied.
1024/// - Returns a borrowed pointer (namespace is owned by the node).
1025#[no_mangle]
1026pub unsafe extern "C" fn xmlNewNs(
1027    node: *mut _xmlNode,
1028    href: *const xmlChar,
1029    prefix: *const xmlChar,
1030) -> *mut _xmlNs {
1031    crate::xml::tree::new_ns(node, href, prefix)
1032}
1033
1034/// Set the namespace of a node.
1035///
1036/// # UPSTREAM-PARITY
1037///
1038/// ```c
1039/// void xmlSetNs(xmlNodePtr node, xmlNsPtr ns);
1040/// ```
1041#[no_mangle]
1042pub unsafe extern "C" fn xmlSetNs(node: *mut _xmlNode, ns: *mut _xmlNs) {
1043    crate::xml::tree::set_ns(node, ns);
1044}
1045
1046/// Get the namespace of a node.
1047///
1048/// # UPSTREAM-PARITY
1049///
1050/// ```c
1051/// xmlNsPtr xmlGetNsList(xmlDocPtr doc, const xmlNode *node);
1052/// ```
1053#[no_mangle]
1054pub unsafe extern "C" fn xmlGetNsList(
1055    doc: *mut _xmlDoc,
1056    node: *const _xmlNode,
1057) -> *mut *mut _xmlNs {
1058    crate::xml::tree::get_ns_list(doc, node as *mut _xmlNode)
1059}
1060
1061/// Search for a namespace by href.
1062///
1063/// # UPSTREAM-PARITY
1064///
1065/// ```c
1066/// xmlNsPtr xmlSearchNs(xmlDocPtr doc, xmlNodePtr node, const xmlChar *nameSpace);
1067/// ```
1068#[no_mangle]
1069pub unsafe extern "C" fn xmlSearchNs(
1070    doc: *mut _xmlDoc,
1071    node: *mut _xmlNode,
1072    nameSpace: *const xmlChar,
1073) -> *mut _xmlNs {
1074    crate::xml::tree::search_ns(doc, node, nameSpace)
1075}
1076
1077/// Search for a namespace by href, using the full in-scope chain.
1078///
1079/// # UPSTREAM-PARITY
1080///
1081/// ```c
1082/// xmlNsPtr xmlSearchNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar *href);
1083/// ```
1084#[no_mangle]
1085pub unsafe extern "C" fn xmlSearchNsByHref(
1086    doc: *mut _xmlDoc,
1087    node: *mut _xmlNode,
1088    href: *const xmlChar,
1089) -> *mut _xmlNs {
1090    crate::xml::tree::search_ns_by_href(doc, node, href)
1091}
1092
1093/// Set a property (attribute) on a node.
1094///
1095/// # UPSTREAM-PARITY
1096///
1097/// ```c
1098/// xmlAttrPtr xmlSetProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value);
1099/// ```
1100///
1101/// If the attribute already exists, its value is updated.
1102/// Returns a borrowed pointer to the attribute.
1103///
1104/// # SAFETY
1105///
1106/// - `node` must be a valid element node.
1107/// - `name` must be a valid string.
1108/// - `value` may be NULL.
1109#[no_mangle]
1110pub unsafe extern "C" fn xmlSetProp(
1111    node: *mut _xmlNode,
1112    name: *const xmlChar,
1113    value: *const xmlChar,
1114) -> *mut _xmlAttr {
1115    crate::xml::tree::set_prop(node, name, value)
1116}
1117
1118/// Get a property value by name.
1119///
1120/// # UPSTREAM-PARITY
1121///
1122/// ```c
1123/// xmlChar *xmlGetProp(const xmlNode *node, const xmlChar *name);
1124/// ```
1125///
1126/// Returns a newly allocated string. Caller must free with `xmlFree`.
1127#[no_mangle]
1128pub unsafe extern "C" fn xmlGetProp(node: *const _xmlNode, name: *const xmlChar) -> *mut xmlChar {
1129    crate::xml::tree::get_prop(node as *mut _xmlNode, name)
1130}
1131
1132/// Get a namespaced property value.
1133///
1134/// # UPSTREAM-PARITY
1135///
1136/// ```c
1137/// xmlChar *xmlGetNsProp(const xmlNode *node, const xmlChar *name, const xmlChar *nameSpace);
1138/// ```
1139#[no_mangle]
1140pub unsafe extern "C" fn xmlGetNsProp(
1141    node: *const _xmlNode,
1142    name: *const xmlChar,
1143    nameSpace: *const xmlChar,
1144) -> *mut xmlChar {
1145    crate::xml::tree::get_ns_prop(node as *mut _xmlNode, name, nameSpace)
1146}
1147
1148/// Set a namespaced property.
1149///
1150/// # UPSTREAM-PARITY
1151///
1152/// ```c
1153/// xmlAttrPtr xmlSetNsProp(xmlNodePtr node, xmlNsPtr ns,
1154///                         const xmlChar *name, const xmlChar *value);
1155/// ```
1156#[no_mangle]
1157pub unsafe extern "C" fn xmlSetNsProp(
1158    node: *mut _xmlNode,
1159    ns: *mut _xmlNs,
1160    name: *const xmlChar,
1161    value: *const xmlChar,
1162) -> *mut _xmlAttr {
1163    crate::xml::tree::set_ns_prop(node, ns, name, value)
1164}
1165
1166/// Remove a property by name.
1167///
1168/// # UPSTREAM-PARITY
1169///
1170/// ```c
1171/// int xmlRemoveProp(xmlAttrPtr attr);
1172/// ```
1173///
1174/// Returns 0 on success, -1 on error.
1175#[no_mangle]
1176pub unsafe extern "C" fn xmlRemoveProp(attr: *mut _xmlAttr) -> c_int {
1177    crate::xml::tree::remove_prop(attr)
1178}
1179
1180/// Get a DTD from a document, creating one if needed.
1181///
1182/// # UPSTREAM-PARITY
1183///
1184/// ```c
1185/// xmlDtdPtr xmlGetIntSubset(const xmlDoc *doc);
1186/// ```
1187#[no_mangle]
1188pub extern "C" fn xmlGetIntSubset(doc: *const _xmlDoc) -> *mut _xmlDtd {
1189    crate::xml::tree::get_int_subset(doc)
1190}
1191
1192/// Create a new DTD.
1193///
1194/// # UPSTREAM-PARITY
1195///
1196/// ```c
1197/// xmlDtdPtr xmlNewDtd(xmlDocPtr doc, const xmlChar *name,
1198///                     const xmlChar *ExternalID, const xmlChar *SystemID);
1199/// ```
1200#[no_mangle]
1201pub unsafe extern "C" fn xmlNewDtd(
1202    doc: *mut _xmlDoc,
1203    name: *const xmlChar,
1204    ExternalID: *const xmlChar,
1205    SystemID: *const xmlChar,
1206) -> *mut _xmlDtd {
1207    crate::xml::tree::new_dtd(doc, name, ExternalID, SystemID)
1208}
1209
1210/// Create a new entity.
1211///
1212/// # UPSTREAM-PARITY
1213///
1214/// ```c
1215/// xmlEntityPtr xmlNewEntity(xmlDocPtr doc, const xmlChar *name, int type,
1216///                           const xmlChar *ExternalID, const xmlChar *SystemID,
1217///                           const xmlChar *content);
1218/// ```
1219#[no_mangle]
1220pub unsafe extern "C" fn xmlNewEntity(
1221    doc: *mut _xmlDoc,
1222    name: *const xmlChar,
1223    type_: c_int,
1224    ExternalID: *const xmlChar,
1225    SystemID: *const xmlChar,
1226    content: *const xmlChar,
1227) -> *mut _xmlEntity {
1228    crate::xml::tree::new_entity(doc, name, type_, ExternalID, SystemID, content)
1229}
1230
1231/// Get an entity by name.
1232///
1233/// # UPSTREAM-PARITY
1234///
1235/// ```c
1236/// xmlEntityPtr xmlGetDocEntity(const xmlDoc *doc, const xmlChar *name);
1237/// ```
1238#[no_mangle]
1239pub unsafe extern "C" fn xmlGetDocEntity(
1240    doc: *const _xmlDoc,
1241    name: *const xmlChar,
1242) -> *mut _xmlEntity {
1243    crate::xml::tree::get_doc_entity(doc, name)
1244}
1245
1246/// Get a parameter entity by name.
1247///
1248/// # UPSTREAM-PARITY
1249///
1250/// ```c
1251/// xmlEntityPtr xmlGetParameterEntity(const xmlDoc *doc, const xmlChar *name);
1252/// ```
1253#[no_mangle]
1254pub unsafe extern "C" fn xmlGetParameterEntity(
1255    doc: *const _xmlDoc,
1256    name: *const xmlChar,
1257) -> *mut _xmlEntity {
1258    crate::xml::tree::get_parameter_entity(doc, name)
1259}
1260
1261// ── DTD Declaration Exports ────────────────────────────────────────────
1262
1263/// Create an internal subset (DTD).
1264///
1265/// # UPSTREAM-PARITY
1266///
1267/// ```c
1268/// xmlDtdPtr xmlCreateIntSubset(xmlDocPtr doc, const xmlChar *name,
1269///                              const xmlChar *ExternalID, const xmlChar *SystemID);
1270/// ```
1271#[no_mangle]
1272pub unsafe extern "C" fn xmlCreateIntSubset(
1273    doc: *mut _xmlDoc,
1274    name: *const xmlChar,
1275    ExternalID: *const xmlChar,
1276    SystemID: *const xmlChar,
1277) -> *mut _xmlDtd {
1278    crate::xml::dtd::create_int_subset(doc, name, ExternalID, SystemID)
1279}
1280
1281/// Free a DTD.
1282///
1283/// # UPSTREAM-PARITY
1284///
1285/// ```c
1286/// void xmlFreeDtd(xmlDtdPtr dtd);
1287/// ```
1288#[no_mangle]
1289pub unsafe extern "C" fn xmlFreeDtd(dtd: *mut _xmlDtd) {
1290    crate::xml::dtd::free_dtd(dtd);
1291}
1292
1293/// Add a notation declaration.
1294///
1295/// # UPSTREAM-PARITY
1296///
1297/// ```c
1298/// xmlNotationPtr xmlAddNotationDecl(xmlDtdPtr dtd, const xmlChar *name,
1299///                                   const xmlChar *PublicID,
1300///                                   const xmlChar *SystemID);
1301/// ```
1302#[no_mangle]
1303pub unsafe extern "C" fn xmlAddNotationDecl(
1304    dtd: *mut _xmlDtd,
1305    name: *const xmlChar,
1306    PublicID: *const xmlChar,
1307    SystemID: *const xmlChar,
1308) -> *mut _xmlNotation {
1309    crate::xml::dtd::add_notation_decl(dtd, name, PublicID, SystemID)
1310}
1311
1312/// Look up a notation declaration.
1313///
1314/// # UPSTREAM-PARITY
1315///
1316/// ```c
1317/// xmlNotationPtr xmlGetNotationDecl(xmlDtdPtr dtd, const xmlChar *name);
1318/// ```
1319#[no_mangle]
1320pub unsafe extern "C" fn xmlGetNotationDecl(
1321    dtd: *mut _xmlDtd,
1322    name: *const xmlChar,
1323) -> *mut _xmlNotation {
1324    crate::xml::dtd::get_notation_decl(dtd, name)
1325}
1326
1327/// Copy a notation declaration.
1328///
1329/// # UPSTREAM-PARITY
1330///
1331/// ```c
1332/// xmlNotationPtr xmlCopyNotation(xmlNotationPtr notation);
1333/// ```
1334#[no_mangle]
1335pub unsafe extern "C" fn xmlCopyNotation(notation: *mut _xmlNotation) -> *mut _xmlNotation {
1336    crate::xml::dtd::copy_notation(notation)
1337}
1338
1339/// Free a notation declaration.
1340///
1341/// # UPSTREAM-PARITY
1342///
1343/// ```c
1344/// void xmlFreeNotation(xmlNotationPtr notation);
1345/// ```
1346#[no_mangle]
1347pub unsafe extern "C" fn xmlFreeNotation(notation: *mut _xmlNotation) {
1348    crate::xml::dtd::free_notation(notation);
1349}
1350
1351/// Add an element declaration.
1352///
1353/// # UPSTREAM-PARITY
1354///
1355/// ```c
1356/// xmlElementPtr xmlAddElementDecl(xmlDtdPtr dtd, const xmlChar *name, int type,
1357///                                 xmlElementContentPtr content);
1358/// ```
1359#[no_mangle]
1360pub unsafe extern "C" fn xmlAddElementDecl(
1361    dtd: *mut _xmlDtd,
1362    name: *const xmlChar,
1363    type_: c_int,
1364    content: *mut _xmlElementContent,
1365) -> *mut _xmlElement {
1366    crate::xml::dtd::add_element_decl(dtd, name, type_, content)
1367}
1368
1369/// Look up an element declaration.
1370///
1371/// # UPSTREAM-PARITY
1372///
1373/// ```c
1374/// xmlElementPtr xmlGetElementDecl(xmlDtdPtr dtd, const xmlChar *name);
1375/// ```
1376#[no_mangle]
1377pub unsafe extern "C" fn xmlGetElementDecl(
1378    dtd: *mut _xmlDtd,
1379    name: *const xmlChar,
1380) -> *mut _xmlElement {
1381    crate::xml::dtd::get_element_decl(dtd, name)
1382}
1383
1384/// Copy an element declaration.
1385///
1386/// # UPSTREAM-PARITY
1387///
1388/// ```c
1389/// xmlElementPtr xmlCopyElement(xmlElementPtr elem);
1390/// ```
1391#[no_mangle]
1392pub unsafe extern "C" fn xmlCopyElement(elem: *mut _xmlElement) -> *mut _xmlElement {
1393    crate::xml::dtd::copy_element(elem)
1394}
1395
1396/// Free an element declaration.
1397///
1398/// # UPSTREAM-PARITY
1399///
1400/// ```c
1401/// void xmlFreeElement(xmlElementPtr elem);
1402/// ```
1403#[no_mangle]
1404pub unsafe extern "C" fn xmlFreeElement(elem: *mut _xmlElement) {
1405    crate::xml::dtd::free_element(elem);
1406}
1407
1408/// Add an attribute declaration.
1409///
1410/// # UPSTREAM-PARITY
1411///
1412/// ```c
1413/// xmlAttributePtr xmlAddAttributeDecl(xmlDtdPtr dtd, xmlElementPtr elem,
1414///                                     const xmlChar *name, int type, int def,
1415///                                     const xmlChar *defaultValue,
1416///                                     xmlEnumerationPtr tree);
1417/// ```
1418#[no_mangle]
1419pub unsafe extern "C" fn xmlAddAttributeDecl(
1420    dtd: *mut _xmlDtd,
1421    elem: *mut _xmlElement,
1422    name: *const xmlChar,
1423    type_: c_int,
1424    def: c_int,
1425    defaultValue: *const xmlChar,
1426    tree: *mut _xmlEnumeration,
1427) -> *mut _xmlAttribute {
1428    crate::xml::dtd::add_attribute_decl(dtd, elem, name, type_, def, defaultValue, tree)
1429}
1430
1431/// Look up an attribute declaration.
1432///
1433/// # UPSTREAM-PARITY
1434///
1435/// ```c
1436/// xmlAttributePtr xmlGetAttributeDecl(xmlDtdPtr dtd, xmlElementPtr elem,
1437///                                     const xmlChar *name, int namePrefix);
1438/// ```
1439#[no_mangle]
1440pub unsafe extern "C" fn xmlGetAttributeDecl(
1441    dtd: *mut _xmlDtd,
1442    elem: *mut _xmlElement,
1443    name: *const xmlChar,
1444    namePrefix: c_int,
1445) -> *mut _xmlAttribute {
1446    crate::xml::dtd::get_attribute_decl(dtd, elem, name, namePrefix)
1447}
1448
1449/// Copy an attribute declaration.
1450///
1451/// # UPSTREAM-PARITY
1452///
1453/// ```c
1454/// xmlAttributePtr xmlCopyAttribute(xmlAttributePtr attr);
1455/// ```
1456#[no_mangle]
1457pub unsafe extern "C" fn xmlCopyAttribute(attr: *mut _xmlAttribute) -> *mut _xmlAttribute {
1458    crate::xml::dtd::copy_attribute_decl(attr)
1459}
1460
1461/// Free an attribute declaration.
1462///
1463/// # UPSTREAM-PARITY
1464///
1465/// ```c
1466/// void xmlFreeAttribute(xmlAttributePtr attr);
1467/// ```
1468#[no_mangle]
1469pub unsafe extern "C" fn xmlFreeAttribute(attr: *mut _xmlAttribute) {
1470    crate::xml::dtd::free_attribute(attr);
1471}
1472
1473/// Create a new element content model.
1474///
1475/// # UPSTREAM-PARITY
1476///
1477/// ```c
1478/// xmlElementContentPtr xmlNewElementContent(const xmlChar *name, int type);
1479/// ```
1480#[no_mangle]
1481pub unsafe extern "C" fn xmlNewElementContent(
1482    name: *const xmlChar,
1483    type_: c_int,
1484) -> *mut _xmlElementContent {
1485    crate::xml::dtd::create_content_model(name, type_)
1486}
1487
1488/// Copy an element content model.
1489///
1490/// # UPSTREAM-PARITY
1491///
1492/// ```c
1493/// xmlElementContentPtr xmlCopyElementContent(xmlElementContentPtr content);
1494/// ```
1495#[no_mangle]
1496pub unsafe extern "C" fn xmlCopyElementContent(
1497    content: *mut _xmlElementContent,
1498) -> *mut _xmlElementContent {
1499    crate::xml::dtd::copy_content_model(content)
1500}
1501
1502/// Free an element content model.
1503///
1504/// # UPSTREAM-PARITY
1505///
1506/// ```c
1507/// void xmlFreeElementContent(xmlElementContentPtr cur);
1508/// ```
1509#[no_mangle]
1510pub unsafe extern "C" fn xmlFreeElementContent(cur: *mut _xmlElementContent) {
1511    crate::xml::dtd::free_content_model(cur);
1512}
1513
1514// ── Entity Exports ─────────────────────────────────────────────────────
1515
1516/// Add an entity declaration.
1517///
1518/// # UPSTREAM-PARITY
1519///
1520/// ```c
1521/// xmlEntityPtr xmlAddEntity(xmlDtdPtr dtd, const xmlChar *name, int type,
1522///                           const xmlChar *ExternalID, const xmlChar *SystemID,
1523///                           const xmlChar *content);
1524/// ```
1525#[no_mangle]
1526pub unsafe extern "C" fn xmlAddEntity(
1527    dtd: *mut _xmlDtd,
1528    name: *const xmlChar,
1529    type_: c_int,
1530    ExternalID: *const xmlChar,
1531    SystemID: *const xmlChar,
1532    content: *const xmlChar,
1533) -> *mut _xmlEntity {
1534    crate::xml::entities::add_entity(dtd, name, type_, ExternalID, SystemID, content)
1535}
1536
1537/// Get an entity by name.
1538///
1539/// # UPSTREAM-PARITY
1540///
1541/// ```c
1542/// xmlEntityPtr xmlGetEntity(xmlDocPtr doc, const xmlChar *name);
1543/// ```
1544#[no_mangle]
1545pub unsafe extern "C" fn xmlGetEntity(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlEntity {
1546    crate::xml::entities::get_entity(doc, name)
1547}
1548
1549/// Copy an entity.
1550///
1551/// # UPSTREAM-PARITY
1552///
1553/// ```c
1554/// xmlEntityPtr xmlCopyEntity(xmlEntityPtr entity);
1555/// ```
1556#[no_mangle]
1557pub unsafe extern "C" fn xmlCopyEntity(entity: *mut _xmlEntity) -> *mut _xmlEntity {
1558    crate::xml::entities::copy_entity(entity)
1559}
1560
1561/// Free an entity.
1562///
1563/// # UPSTREAM-PARITY
1564///
1565/// ```c
1566/// void xmlFreeEntity(xmlEntityPtr entity);
1567/// ```
1568#[no_mangle]
1569pub unsafe extern "C" fn xmlFreeEntity(entity: *mut _xmlEntity) {
1570    crate::xml::entities::free_entity(entity);
1571}
1572
1573/// Encode entities for reentrant output.
1574///
1575/// # UPSTREAM-PARITY
1576///
1577/// ```c
1578/// xmlChar *xmlEncodeEntitiesReentrant(xmlDocPtr doc, const xmlChar *input);
1579/// ```
1580#[no_mangle]
1581pub unsafe extern "C" fn xmlEncodeEntitiesReentrant(
1582    doc: *mut _xmlDoc,
1583    input: *const xmlChar,
1584) -> *mut xmlChar {
1585    crate::xml::entities::encode_entities_reentrant(doc, input)
1586}
1587
1588/// Get the line number of a node.
1589///
1590/// # UPSTREAM-PARITY
1591///
1592/// ```c
1593/// long xmlGetLineNo(const xmlNode *node);
1594/// ```
1595#[no_mangle]
1596pub extern "C" fn xmlGetLineNo(node: *const _xmlNode) -> c_int {
1597    crate::xml::tree::get_line_no(node)
1598}
1599
1600// ═══════════════════════════════════════════════════════════════════════════════
1601// Serialization — xmlNodeDump, xmlDocDump, xmlSaveFile, etc.
1602// ═══════════════════════════════════════════════════════════════════════════════
1603
1604/// Dump a node to a buffer.
1605///
1606/// # UPSTREAM-PARITY
1607///
1608/// ```c
1609/// int xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level, int format);
1610/// ```
1611#[no_mangle]
1612pub unsafe extern "C" fn xmlNodeDump(
1613    buf: *mut _xmlBuffer,
1614    doc: *mut _xmlDoc,
1615    cur: *mut _xmlNode,
1616    level: c_int,
1617    format: c_int,
1618) -> c_int {
1619    if buf.is_null() || cur.is_null() {
1620        return -1;
1621    }
1622    crate::xml::tree::xmlNodeDump(buf, doc, cur, level, format)
1623}
1624
1625/// Dump a document to a file pointer.
1626///
1627/// # UPSTREAM-PARITY
1628///
1629/// ```c
1630/// int xmlDocDump(FILE *f, xmlDocPtr doc);
1631/// ```
1632#[no_mangle]
1633pub unsafe extern "C" fn xmlDocDump(fp: *mut c_void, doc: *mut _xmlDoc) -> c_int {
1634    if fp.is_null() || doc.is_null() {
1635        return -1;
1636    }
1637    crate::xml::tree::xmlDocDump(fp, doc)
1638}
1639
1640/// Dump a document to memory with format.
1641///
1642/// # UPSTREAM-PARITY
1643///
1644/// ```c
1645/// void xmlDocDumpFormatMemory(xmlDocPtr doc, xmlChar **mem, int *size, int format);
1646/// ```
1647#[no_mangle]
1648pub unsafe extern "C" fn xmlDocDumpFormatMemory(
1649    doc: *mut _xmlDoc,
1650    mem: *mut *mut xmlChar,
1651    size: *mut c_int,
1652    format: c_int,
1653) {
1654    if doc.is_null() || mem.is_null() || size.is_null() {
1655        return;
1656    }
1657    crate::xml::tree::xmlDocDumpFormatMemory(doc, mem, size, format)
1658}
1659
1660/// Dump a document to memory (unformatted).
1661///
1662/// # UPSTREAM-PARITY
1663///
1664/// ```c
1665/// void xmlDocDumpMemory(xmlDocPtr doc, xmlChar **mem, int *size);
1666/// ```
1667#[no_mangle]
1668pub unsafe extern "C" fn xmlDocDumpMemory(
1669    doc: *mut _xmlDoc,
1670    mem: *mut *mut xmlChar,
1671    size: *mut c_int,
1672) {
1673    if doc.is_null() || mem.is_null() || size.is_null() {
1674        return;
1675    }
1676    crate::xml::tree::xmlDocDumpMemory(doc, mem, size)
1677}
1678
1679/// Save a document to a file.
1680///
1681/// # UPSTREAM-PARITY
1682///
1683/// ```c
1684/// int xmlSaveFile(const char *filename, xmlDocPtr cur);
1685/// ```
1686#[no_mangle]
1687pub unsafe extern "C" fn xmlSaveFile(filename: *const c_char, cur: *mut _xmlDoc) -> c_int {
1688    if filename.is_null() || cur.is_null() {
1689        return -1;
1690    }
1691    crate::xml::tree::xmlSaveFile(filename, cur)
1692}
1693
1694/// Save a document to a file with encoding.
1695///
1696/// # UPSTREAM-PARITY
1697///
1698/// ```c
1699/// int xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding);
1700/// ```
1701#[no_mangle]
1702pub unsafe extern "C" fn xmlSaveFileEnc(
1703    filename: *const c_char,
1704    cur: *mut _xmlDoc,
1705    encoding: *const c_char,
1706) -> c_int {
1707    if filename.is_null() || cur.is_null() {
1708        return -1;
1709    }
1710    crate::xml::tree::xmlSaveFileEnc(filename, cur, encoding)
1711}
1712
1713/// Save a document to a file with format.
1714///
1715/// # UPSTREAM-PARITY
1716///
1717/// ```c
1718/// int xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format);
1719/// ```
1720#[no_mangle]
1721pub unsafe extern "C" fn xmlSaveFormatFile(
1722    filename: *const c_char,
1723    cur: *mut _xmlDoc,
1724    format: c_int,
1725) -> c_int {
1726    if filename.is_null() || cur.is_null() {
1727        return -1;
1728    }
1729    crate::xml::tree::xmlSaveFormatFile(filename, cur, format)
1730}
1731
1732/// Save a document to a file with encoding and format.
1733///
1734/// # UPSTREAM-PARITY
1735///
1736/// ```c
1737/// int xmlSaveFormatFileEnc(const char *filename, xmlDocPtr cur, const char *encoding, int format);
1738/// ```
1739#[no_mangle]
1740pub unsafe extern "C" fn xmlSaveFormatFileEnc(
1741    filename: *const c_char,
1742    cur: *mut _xmlDoc,
1743    encoding: *const c_char,
1744    format: c_int,
1745) -> c_int {
1746    if filename.is_null() || cur.is_null() {
1747        return -1;
1748    }
1749    crate::xml::tree::xmlSaveFormatFileEnc(filename, cur, encoding, format)
1750}
1751
1752// ═══════════════════════════════════════════════════════════════════════════════
1753// 7. Parser — SAX, DOM, Push, Reader
1754// ═══════════════════════════════════════════════════════════════════════════════
1755
1756/// Read an XML document from a string.
1757///
1758/// # UPSTREAM-PARITY
1759///
1760/// ```c
1761/// xmlDocPtr xmlReadDoc(const xmlChar *cur, const char *URL,
1762///                      const char *encoding, int options);
1763/// ```
1764///
1765/// Returns a parsed document. Caller must free with `xmlFreeDoc`.
1766#[no_mangle]
1767pub unsafe extern "C" fn xmlReadDoc(
1768    cur: *const xmlChar,
1769    URL: *const c_char,
1770    encoding: *const c_char,
1771    options: c_int,
1772) -> *mut _xmlDoc {
1773    // SAFETY: cur must be a valid null-terminated xmlChar string if non-null.
1774    if cur.is_null() {
1775        return ptr::null_mut();
1776    }
1777    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1778    if ctxt.is_null() {
1779        return ptr::null_mut();
1780    }
1781    let len = crate::xml::string::xml_strlen(cur);
1782    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
1783    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1784    (*ctxt).options = options;
1785    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1786        let doc = (*ctxt).myDoc;
1787        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1788        return doc;
1789    }
1790    let doc = (*ctxt).myDoc;
1791    if !doc.is_null() {
1792        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
1793    }
1794    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1795    doc
1796}
1797
1798/// Read an XML document from a file.
1799///
1800/// # UPSTREAM-PARITY
1801///
1802/// ```c
1803/// xmlDocPtr xmlReadFile(const char *URL, const char *encoding, int options);
1804/// ```
1805#[no_mangle]
1806pub unsafe extern "C" fn xmlReadFile(
1807    URL: *const c_char,
1808    encoding: *const c_char,
1809    options: c_int,
1810) -> *mut _xmlDoc {
1811    // SAFETY: URL must be a valid C string or NULL.
1812    if URL.is_null() {
1813        return ptr::null_mut();
1814    }
1815    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1816    if ctxt.is_null() {
1817        return ptr::null_mut();
1818    }
1819    let input = match crate::xml::parser::helpers::input_from_file(URL) {
1820        Ok(input) => input,
1821        Err(_) => {
1822            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1823            return ptr::null_mut();
1824        }
1825    };
1826    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1827    (*ctxt).options = options;
1828    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1829        let doc = (*ctxt).myDoc;
1830        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1831        return doc;
1832    }
1833    let doc = (*ctxt).myDoc;
1834    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1835    doc
1836}
1837
1838/// Read an XML document from memory.
1839///
1840/// # UPSTREAM-PARITY
1841///
1842/// ```c
1843/// xmlDocPtr xmlReadMemory(const char *buffer, int size,
1844///                         const char *URL, const char *encoding, int options);
1845/// ```
1846#[no_mangle]
1847pub unsafe extern "C" fn xmlReadMemory(
1848    buffer: *const c_char,
1849    size: c_int,
1850    URL: *const c_char,
1851    encoding: *const c_char,
1852    options: c_int,
1853) -> *mut _xmlDoc {
1854    // SAFETY: buffer must be a valid pointer with at least `size` readable bytes.
1855    if buffer.is_null() || size <= 0 {
1856        return ptr::null_mut();
1857    }
1858    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1859    if ctxt.is_null() {
1860        return ptr::null_mut();
1861    }
1862    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
1863    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1864    (*ctxt).options = options;
1865    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1866        let doc = (*ctxt).myDoc;
1867        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1868        return doc;
1869    }
1870    let doc = (*ctxt).myDoc;
1871    if !doc.is_null() && !URL.is_null() {
1872        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
1873    }
1874    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1875    doc
1876}
1877
1878/// Read an XML document from a file descriptor.
1879///
1880/// # UPSTREAM-PARITY
1881///
1882/// ```c
1883/// xmlDocPtr xmlReadFd(int fd, const char *URL, const char *encoding, int options);
1884/// ```
1885#[no_mangle]
1886pub unsafe extern "C" fn xmlReadFd(
1887    fd: c_int,
1888    URL: *const c_char,
1889    encoding: *const c_char,
1890    options: c_int,
1891) -> *mut _xmlDoc {
1892    // SAFETY: fd must be a valid open file descriptor.
1893    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1894    if ctxt.is_null() {
1895        return ptr::null_mut();
1896    }
1897    // Read all data from the fd
1898    let mut buf = Vec::new();
1899    let mut tmp = [0u8; 4096];
1900    loop {
1901        let n = libc::read(fd, tmp.as_mut_ptr() as *mut c_void, tmp.len());
1902        if n <= 0 {
1903            break;
1904        }
1905        buf.extend_from_slice(&tmp[..n as usize]);
1906    }
1907    let input = crate::xml::parser::helpers::input_from_memory(
1908        buf.as_ptr() as *const c_char,
1909        buf.len() as c_int,
1910    );
1911    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1912    (*ctxt).options = options;
1913    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1914        let doc = (*ctxt).myDoc;
1915        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1916        return doc;
1917    }
1918    let doc = (*ctxt).myDoc;
1919    if !doc.is_null() && !URL.is_null() {
1920        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
1921    }
1922    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1923    doc
1924}
1925
1926/// Read an XML document from I/O callbacks.
1927///
1928/// # UPSTREAM-PARITY
1929///
1930/// ```c
1931/// xmlDocPtr xmlReadIO(xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
1932///                     void *ioctx, const char *URL, const char *encoding, int options);
1933/// ```
1934#[no_mangle]
1935pub unsafe extern "C" fn xmlReadIO(
1936    ioread: Option<xmlInputReadCallback>,
1937    ioclose: Option<xmlInputCloseCallback>,
1938    ioctx: *mut c_void,
1939    URL: *const c_char,
1940    encoding: *const c_char,
1941    options: c_int,
1942) -> *mut _xmlDoc {
1943    // SAFETY: callbacks must be valid function pointers if non-NULL.
1944    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1945    if ctxt.is_null() {
1946        return ptr::null_mut();
1947    }
1948    let input = crate::xml::parser::helpers::input_from_io(ioread, ioclose, ioctx);
1949    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1950    (*ctxt).options = options;
1951    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1952        let doc = (*ctxt).myDoc;
1953        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1954        return doc;
1955    }
1956    let doc = (*ctxt).myDoc;
1957    if !doc.is_null() && !URL.is_null() {
1958        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
1959    }
1960    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1961    doc
1962}
1963
1964/// Parse an XML document (SAX1).
1965///
1966/// # UPSTREAM-PARITY
1967///
1968/// ```c
1969/// xmlDocPtr xmlSAXParseDoc(xmlSAXHandlerPtr sax, const xmlChar *cur, int recovery);
1970/// ```
1971#[no_mangle]
1972pub unsafe extern "C" fn xmlSAXParseDoc(
1973    sax: *mut _xmlSAXHandler,
1974    cur: *const xmlChar,
1975    recovery: c_int,
1976) -> *mut _xmlDoc {
1977    // SAFETY: cur must be a valid null-terminated xmlChar string.
1978    if cur.is_null() {
1979        return ptr::null_mut();
1980    }
1981    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1982    if ctxt.is_null() {
1983        return ptr::null_mut();
1984    }
1985    if !sax.is_null() {
1986        (*ctxt).sax = sax;
1987        (*ctxt).userData = (*ctxt).sax as *mut c_void;
1988    }
1989    if recovery != 0 {
1990        (*ctxt).recovery = 1;
1991        (*ctxt).options |= 1; // XML_PARSE_RECOVER
1992    }
1993    let len = crate::xml::string::xml_strlen(cur);
1994    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
1995    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1996    crate::xml::parser::helpers::parse_document(ctxt);
1997    let doc = (*ctxt).myDoc;
1998    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1999    doc
2000}
2001
2002/// Parse an XML file (SAX1).
2003///
2004/// # UPSTREAM-PARITY
2005///
2006/// ```c
2007/// xmlDocPtr xmlSAXParseFile(xmlSAXHandlerPtr sax, const char *filename, int recovery);
2008/// ```
2009#[no_mangle]
2010pub unsafe extern "C" fn xmlSAXParseFile(
2011    sax: *mut _xmlSAXHandler,
2012    filename: *const c_char,
2013    recovery: c_int,
2014) -> *mut _xmlDoc {
2015    // SAFETY: filename must be a valid C string.
2016    if filename.is_null() {
2017        return ptr::null_mut();
2018    }
2019    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2020    if ctxt.is_null() {
2021        return ptr::null_mut();
2022    }
2023    if !sax.is_null() {
2024        (*ctxt).sax = sax;
2025        (*ctxt).userData = (*ctxt).sax as *mut c_void;
2026    }
2027    if recovery != 0 {
2028        (*ctxt).recovery = 1;
2029        (*ctxt).options |= 1;
2030    }
2031    let input = match crate::xml::parser::helpers::input_from_file(filename) {
2032        Ok(input) => input,
2033        Err(_) => {
2034            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2035            return ptr::null_mut();
2036        }
2037    };
2038    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2039    crate::xml::parser::helpers::parse_document(ctxt);
2040    let doc = (*ctxt).myDoc;
2041    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2042    doc
2043}
2044
2045/// Parse an XML document from memory (SAX1).
2046///
2047/// # UPSTREAM-PARITY
2048///
2049/// ```c
2050/// xmlDocPtr xmlSAXParseMemory(xmlSAXHandlerPtr sax,
2051///                             const char *buffer, int size, int recovery);
2052/// ```
2053#[no_mangle]
2054pub unsafe extern "C" fn xmlSAXParseMemory(
2055    sax: *mut _xmlSAXHandler,
2056    buffer: *const c_char,
2057    size: c_int,
2058    recovery: c_int,
2059) -> *mut _xmlDoc {
2060    // SAFETY: buffer must be valid with at least `size` bytes.
2061    if buffer.is_null() || size <= 0 {
2062        return ptr::null_mut();
2063    }
2064    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2065    if ctxt.is_null() {
2066        return ptr::null_mut();
2067    }
2068    if !sax.is_null() {
2069        (*ctxt).sax = sax;
2070        (*ctxt).userData = (*ctxt).sax as *mut c_void;
2071    }
2072    if recovery != 0 {
2073        (*ctxt).recovery = 1;
2074        (*ctxt).options |= 1;
2075    }
2076    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
2077    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2078    crate::xml::parser::helpers::parse_document(ctxt);
2079    let doc = (*ctxt).myDoc;
2080    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2081    doc
2082}
2083
2084/// SAX user parse file.
2085///
2086/// # UPSTREAM-PARITY
2087///
2088/// ```c
2089/// int xmlSAXUserParseFile(xmlSAXHandlerPtr sax, void *user_data,
2090///                         const char *filename);
2091/// ```
2092#[no_mangle]
2093pub unsafe extern "C" fn xmlSAXUserParseFile(
2094    sax: *mut _xmlSAXHandler,
2095    user_data: *mut c_void,
2096    filename: *const c_char,
2097) -> c_int {
2098    // SAFETY: filename must be a valid C string. sax and user_data may be NULL.
2099    if filename.is_null() {
2100        return -1;
2101    }
2102    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2103    if ctxt.is_null() {
2104        return -1;
2105    }
2106    if !sax.is_null() {
2107        (*ctxt).sax = sax;
2108    }
2109    (*ctxt).userData = if !user_data.is_null() {
2110        user_data
2111    } else {
2112        ctxt as *mut c_void
2113    };
2114    let input = match crate::xml::parser::helpers::input_from_file(filename) {
2115        Ok(input) => input,
2116        Err(_) => {
2117            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2118            return -1;
2119        }
2120    };
2121    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2122    let ret = crate::xml::parser::helpers::parse_document(ctxt);
2123    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2124    ret
2125}
2126
2127/// SAX user parse memory.
2128///
2129/// # UPSTREAM-PARITY
2130///
2131/// ```c
2132/// int xmlSAXUserParseMemory(xmlSAXHandlerPtr sax, void *user_data,
2133///                           const char *buffer, int size);
2134/// ```
2135#[no_mangle]
2136pub unsafe extern "C" fn xmlSAXUserParseMemory(
2137    sax: *mut _xmlSAXHandler,
2138    user_data: *mut c_void,
2139    buffer: *const c_char,
2140    size: c_int,
2141) -> c_int {
2142    // SAFETY: buffer must be valid with at least `size` bytes.
2143    if buffer.is_null() || size <= 0 {
2144        return -1;
2145    }
2146    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2147    if ctxt.is_null() {
2148        return -1;
2149    }
2150    if !sax.is_null() {
2151        (*ctxt).sax = sax;
2152    }
2153    (*ctxt).userData = if !user_data.is_null() {
2154        user_data
2155    } else {
2156        ctxt as *mut c_void
2157    };
2158    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
2159    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2160    let ret = crate::xml::parser::helpers::parse_document(ctxt);
2161    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2162    ret
2163}
2164
2165/// Parse an XML document from a string (DOM).
2166///
2167/// # UPSTREAM-PARITY
2168///
2169/// ```c
2170/// xmlDocPtr xmlParseDoc(const xmlChar *cur);
2171/// ```
2172#[no_mangle]
2173pub unsafe extern "C" fn xmlParseDoc(cur: *const xmlChar) -> *mut _xmlDoc {
2174    // SAFETY: cur must be a valid null-terminated xmlChar string.
2175    if cur.is_null() {
2176        return ptr::null_mut();
2177    }
2178    xmlReadDoc(cur, ptr::null(), ptr::null(), 0)
2179}
2180
2181/// Parse an XML file (DOM).
2182///
2183/// # UPSTREAM-PARITY
2184///
2185/// ```c
2186/// xmlDocPtr xmlParseFile(const char *filename);
2187/// ```
2188#[no_mangle]
2189pub unsafe extern "C" fn xmlParseFile(filename: *const c_char) -> *mut _xmlDoc {
2190    // SAFETY: filename must be a valid C string.
2191    if filename.is_null() {
2192        return ptr::null_mut();
2193    }
2194    xmlReadFile(filename, ptr::null(), 0)
2195}
2196
2197/// Parse an XML document from memory (DOM).
2198///
2199/// # UPSTREAM-PARITY
2200///
2201/// ```c
2202/// xmlDocPtr xmlParseMemory(const char *buffer, int size);
2203/// ```
2204#[no_mangle]
2205pub unsafe extern "C" fn xmlParseMemory(buffer: *const c_char, size: c_int) -> *mut _xmlDoc {
2206    // SAFETY: buffer must be valid with at least `size` bytes.
2207    if buffer.is_null() || size <= 0 {
2208        return ptr::null_mut();
2209    }
2210    xmlReadMemory(buffer, size, ptr::null(), ptr::null(), 0)
2211}
2212
2213/// Create a file parser context.
2214///
2215/// # UPSTREAM-PARITY
2216///
2217/// ```c
2218/// xmlParserCtxtPtr xmlCreateFileParserCtxt(const char *filename);
2219/// ```
2220#[no_mangle]
2221pub unsafe extern "C" fn xmlCreateFileParserCtxt(filename: *const c_char) -> *mut _xmlParserCtxt {
2222    // SAFETY: filename must be a valid C string.
2223    if filename.is_null() {
2224        return ptr::null_mut();
2225    }
2226    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2227    if ctxt.is_null() {
2228        return ptr::null_mut();
2229    }
2230    let input = match crate::xml::parser::helpers::input_from_file(filename) {
2231        Ok(input) => input,
2232        Err(_) => {
2233            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2234            return ptr::null_mut();
2235        }
2236    };
2237    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2238    ctxt
2239}
2240
2241/// Create a document parser context.
2242///
2243/// # UPSTREAM-PARITY
2244///
2245/// ```c
2246/// xmlParserCtxtPtr xmlCreateDocParserCtxt(const xmlChar *cur);
2247/// ```
2248#[no_mangle]
2249pub unsafe extern "C" fn xmlCreateDocParserCtxt(cur: *const xmlChar) -> *mut _xmlParserCtxt {
2250    // SAFETY: cur must be a valid null-terminated xmlChar string.
2251    if cur.is_null() {
2252        return ptr::null_mut();
2253    }
2254    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
2255    if ctxt.is_null() {
2256        return ptr::null_mut();
2257    }
2258    let len = crate::xml::string::xml_strlen(cur);
2259    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
2260    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
2261    ctxt
2262}
2263
2264/// Parse a document using an existing parser context.
2265///
2266/// # UPSTREAM-PARITY
2267///
2268/// ```c
2269/// int xmlParseDocument(xmlParserCtxtPtr ctxt);
2270/// ```
2271#[no_mangle]
2272pub unsafe extern "C" fn xmlParseDocument(ctxt: *mut _xmlParserCtxt) -> c_int {
2273    // SAFETY: ctxt must be a valid parser context.
2274    if ctxt.is_null() {
2275        return -1;
2276    }
2277    crate::xml::parser::helpers::parse_document(ctxt)
2278}
2279
2280/// Free a parser context.
2281///
2282/// # UPSTREAM-PARITY
2283///
2284/// ```c
2285/// void xmlFreeParserCtxt(xmlParserCtxtPtr ctxt);
2286/// ```
2287#[no_mangle]
2288pub unsafe extern "C" fn xmlFreeParserCtxt(ctxt: *mut _xmlParserCtxt) {
2289    if ctxt.is_null() {
2290        return;
2291    }
2292    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
2293}
2294
2295/// Set parser options.
2296///
2297/// # UPSTREAM-PARITY
2298///
2299/// ```c
2300/// int xmlCtxtUseOptions(xmlParserCtxtPtr ctxt, int options);
2301/// ```
2302#[no_mangle]
2303pub unsafe extern "C" fn xmlCtxtUseOptions(ctxt: *mut _xmlParserCtxt, options: c_int) -> c_int {
2304    if ctxt.is_null() {
2305        return -1;
2306    }
2307    // Phase 1: STUB
2308    unsafe {
2309        (*ctxt).options = options;
2310    }
2311    0
2312}
2313
2314/// Parse a well-balanced chunk (for push parsing).
2315///
2316/// # UPSTREAM-PARITY
2317///
2318/// ```c
2319/// xmlParserErrors xmlParseChunk(xmlParserCtxtPtr ctxt,
2320///                               const char *chunk, int size, int terminate);
2321/// ```
2322#[no_mangle]
2323pub unsafe extern "C" fn xmlParseChunk(
2324    ctxt: *mut _xmlParserCtxt,
2325    chunk: *const c_char,
2326    size: c_int,
2327    terminate: c_int,
2328) -> c_int {
2329    // SAFETY: ctxt must be a valid parser context.
2330    // chunk may be NULL if terminate is set (finalize without data).
2331    if ctxt.is_null() {
2332        return -1;
2333    }
2334    crate::xml::parser::helpers::parse_chunk(ctxt, chunk, size, terminate)
2335}
2336
2337/// Create a memory parser input buffer.
2338///
2339/// # UPSTREAM-PARITY
2340///
2341/// ```c
2342/// xmlParserInputBufferPtr xmlParserInputBufferCreateMem(const char *buffer, int size, int enc);
2343/// ```
2344#[no_mangle]
2345pub unsafe extern "C" fn xmlParserInputBufferCreateMem(
2346    buffer: *const c_char,
2347    size: c_int,
2348    enc: c_int,
2349) -> *mut _xmlParserInputBuffer {
2350    // SAFETY: buffer must be valid with at least `size` bytes.
2351    if buffer.is_null() || size <= 0 {
2352        return ptr::null_mut();
2353    }
2354    crate::xml::parser::helpers::alloc_parser_input_buffer()
2355}
2356
2357/// Create a file parser input buffer.
2358///
2359/// # UPSTREAM-PARITY
2360///
2361/// ```c
2362/// xmlParserInputBufferPtr xmlParserInputBufferCreateFilename(const char *URI, int enc);
2363/// ```
2364#[no_mangle]
2365pub unsafe extern "C" fn xmlParserInputBufferCreateFilename(
2366    URI: *const c_char,
2367    enc: c_int,
2368) -> *mut _xmlParserInputBuffer {
2369    // SAFETY: URI must be a valid C string or NULL.
2370    if URI.is_null() {
2371        return ptr::null_mut();
2372    }
2373    crate::xml::parser::helpers::alloc_parser_input_buffer()
2374}
2375
2376/// Create an I/O parser input buffer.
2377///
2378/// # UPSTREAM-PARITY
2379///
2380/// ```c
2381/// xmlParserInputBufferPtr xmlParserInputBufferCreateIO(
2382///     xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
2383///     void *ioctx, int enc);
2384/// ```
2385#[no_mangle]
2386pub unsafe extern "C" fn xmlParserInputBufferCreateIO(
2387    ioread: Option<xmlInputReadCallback>,
2388    ioclose: Option<xmlInputCloseCallback>,
2389    ioctx: *mut c_void,
2390    enc: c_int,
2391) -> *mut _xmlParserInputBuffer {
2392    // SAFETY: ioread must be a valid callback if Some. ioctx may be NULL.
2393    let buf = crate::xml::parser::helpers::alloc_parser_input_buffer();
2394    if !buf.is_null() {
2395        (*buf).readcallback = ioread;
2396        (*buf).closecallback = ioclose;
2397        (*buf).context = ioctx;
2398    }
2399    buf
2400}
2401
2402/// Free a parser input buffer.
2403///
2404/// # UPSTREAM-PARITY
2405///
2406/// ```c
2407/// void xmlFreeParserInputBuffer(xmlParserInputBufferPtr buf);
2408/// ```
2409#[no_mangle]
2410pub unsafe extern "C" fn xmlFreeParserInputBuffer(buf: *mut _xmlParserInputBuffer) {
2411    if buf.is_null() {
2412        return;
2413    }
2414    crate::xml::parser::helpers::free_parser_input_buffer(buf);
2415}
2416
2417/// Create a new parser input.
2418///
2419/// # UPSTREAM-PARITY
2420///
2421/// ```c
2422/// xmlParserInputPtr xmlNewInputFromFile(xmlParserCtxtPtr ctxt, const char *filename);
2423/// ```
2424#[no_mangle]
2425pub unsafe extern "C" fn xmlNewInputFromFile(
2426    ctxt: *mut _xmlParserCtxt,
2427    filename: *const c_char,
2428) -> *mut _xmlParserInput {
2429    // SAFETY: filename must be a valid C string. ctxt may be NULL.
2430    // This function allocates a _xmlParserInput. The caller owns it.
2431    // Note: The InputBuffer backing data is NOT leaked here (no ctxt._private
2432    // to store it). Use xmlCreateFileParserCtxt + xmlParseDocument instead.
2433    if filename.is_null() {
2434        return ptr::null_mut();
2435    }
2436    crate::xml::parser::helpers::alloc_parser_input_buffer() as *mut _xmlParserInput
2437}
2438
2439/// Free a parser input.
2440///
2441/// # UPSTREAM-PARITY
2442///
2443/// ```c
2444/// void xmlFreeInputStream(xmlParserInputPtr input);
2445/// ```
2446#[no_mangle]
2447pub unsafe extern "C" fn xmlFreeInputStream(input: *mut _xmlParserInput) {
2448    if input.is_null() {
2449        return;
2450    }
2451    crate::xml::parser::helpers::free_parser_input(input);
2452}
2453
2454// ═══════════════════════════════════════════════════════════════════════════════
2455// 8. I/O
2456// ═══════════════════════════════════════════════════════════════════════════════
2457
2458/// Create an output buffer for a file.
2459///
2460/// # UPSTREAM-PARITY
2461///
2462/// ```c
2463/// xmlOutputBufferPtr xmlOutputBufferCreateFilename(const char *URI,
2464///                                                  xmlCharEncodingHandlerPtr encoder,
2465///                                                  int compression);
2466/// ```
2467#[no_mangle]
2468pub unsafe extern "C" fn xmlOutputBufferCreateFilename(
2469    URI: *const c_char,
2470    encoder: *mut c_void,
2471    compression: c_int,
2472) -> *mut _xmlOutputBuffer {
2473    let _ = compression;
2474    if URI.is_null() {
2475        return ptr::null_mut();
2476    }
2477    crate::xml::io::output_buffer_create_filename(
2478        URI,
2479        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
2480        0,
2481    )
2482}
2483
2484/// Create an output buffer for a file descriptor.
2485///
2486/// # UPSTREAM-PARITY
2487///
2488/// ```c
2489/// xmlOutputBufferPtr xmlOutputBufferCreateFd(int fd,
2490///                                            xmlCharEncodingHandlerPtr encoder);
2491/// ```
2492#[no_mangle]
2493pub unsafe extern "C" fn xmlOutputBufferCreateFd(
2494    fd: c_int,
2495    encoder: *mut c_void,
2496) -> *mut _xmlOutputBuffer {
2497    if fd < 0 {
2498        return ptr::null_mut();
2499    }
2500    crate::xml::io::output_buffer_create_fd(
2501        fd,
2502        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
2503    )
2504}
2505
2506/// Create an output buffer from I/O callbacks.
2507///
2508/// # UPSTREAM-PARITY
2509///
2510/// ```c
2511/// xmlOutputBufferPtr xmlOutputBufferCreateIO(
2512///     xmlOutputWriteCallback iowrite, xmlOutputCloseCallback ioclose,
2513///     void *ioctx, xmlCharEncodingHandlerPtr encoder);
2514/// ```
2515#[no_mangle]
2516pub unsafe extern "C" fn xmlOutputBufferCreateIO(
2517    iowrite: Option<xmlOutputWriteCallback>,
2518    ioclose: Option<xmlOutputCloseCallback>,
2519    ioctx: *mut c_void,
2520    encoder: *mut c_void,
2521) -> *mut _xmlOutputBuffer {
2522    crate::xml::io::output_buffer_create_io(
2523        iowrite,
2524        ioclose,
2525        ioctx,
2526        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
2527    )
2528}
2529
2530/// Free an output buffer.
2531///
2532/// # UPSTREAM-PARITY
2533///
2534/// ```c
2535/// void xmlOutputBufferClose(xmlOutputBufferPtr out);
2536/// ```
2537#[no_mangle]
2538pub unsafe extern "C" fn xmlOutputBufferClose(out: *mut _xmlOutputBuffer) -> c_int {
2539    if out.is_null() {
2540        return -1;
2541    }
2542    crate::xml::io::output_buffer_close(out)
2543}
2544
2545/// Flush an output buffer.
2546///
2547/// # UPSTREAM-PARITY
2548///
2549/// ```c
2550/// int xmlOutputBufferFlush(xmlOutputBufferPtr out);
2551/// ```
2552#[no_mangle]
2553pub unsafe extern "C" fn xmlOutputBufferFlush(out: *mut _xmlOutputBuffer) -> c_int {
2554    if out.is_null() {
2555        return -1;
2556    }
2557    crate::xml::io::output_buffer_flush(out)
2558}
2559
2560/// Write to an output buffer.
2561///
2562/// # UPSTREAM-PARITY
2563///
2564/// ```c
2565/// int xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *data);
2566/// ```
2567#[no_mangle]
2568pub unsafe extern "C" fn xmlOutputBufferWrite(
2569    out: *mut _xmlOutputBuffer,
2570    len: c_int,
2571    data: *const c_char,
2572) -> c_int {
2573    if out.is_null() || data.is_null() || len <= 0 {
2574        return -1;
2575    }
2576    crate::xml::io::output_buffer_write(out, len, data)
2577}
2578
2579/// Write a string to an output buffer.
2580///
2581/// # UPSTREAM-PARITY
2582///
2583/// ```c
2584/// int xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str);
2585/// ```
2586#[no_mangle]
2587pub unsafe extern "C" fn xmlOutputBufferWriteString(
2588    out: *mut _xmlOutputBuffer,
2589    str: *const c_char,
2590) -> c_int {
2591    if str.is_null() {
2592        return 0;
2593    }
2594    unsafe { xmlOutputBufferWrite(out, xmlStrlen(str as *const xmlChar), str) }
2595}
2596
2597// ═══════════════════════════════════════════════════════════════════════════════
2598// 9. Dictionary
2599// ═══════════════════════════════════════════════════════════════════════════════
2600
2601/// Create a new dictionary.
2602///
2603/// # UPSTREAM-PARITY
2604///
2605/// ```c
2606/// xmlDictPtr xmlDictCreate(void);
2607/// ```
2608#[no_mangle]
2609pub extern "C" fn xmlDictCreate() -> *mut c_void {
2610    // Phase 1: STUB — will be implemented in xml/dictionary module.
2611    ptr::null_mut()
2612}
2613
2614/// Create a sub-dictionary.
2615///
2616/// # UPSTREAM-PARITY
2617///
2618/// ```c
2619/// xmlDictPtr xmlDictCreateSub(xmlDictPtr sub);
2620/// ```
2621#[no_mangle]
2622pub extern "C" fn xmlDictCreateSub(_sub: *mut c_void) -> *mut c_void {
2623    // Phase 1: STUB
2624    ptr::null_mut()
2625}
2626
2627/// Look up a string in the dictionary.
2628///
2629/// # UPSTREAM-PARITY
2630///
2631/// ```c
2632/// const xmlChar *xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len);
2633/// ```
2634///
2635/// Returns an interned string pointer (valid as long as the dictionary exists).
2636/// - If `len` < 0, `name` must be null-terminated.
2637/// - If `len` >= 0, exactly `len` bytes are used.
2638#[no_mangle]
2639pub unsafe extern "C" fn xmlDictLookup(
2640    dict: *mut c_void,
2641    name: *const xmlChar,
2642    len: c_int,
2643) -> *const xmlChar {
2644    // Phase 1: STUB
2645    name
2646}
2647
2648/// Check if a string exists in the dictionary.
2649///
2650/// # UPSTREAM-PARITY
2651///
2652/// ```c
2653/// const xmlChar *xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len);
2654/// ```
2655#[no_mangle]
2656pub unsafe extern "C" fn xmlDictExists(
2657    dict: *mut c_void,
2658    name: *const xmlChar,
2659    len: c_int,
2660) -> *const xmlChar {
2661    // Phase 1: STUB
2662    ptr::null()
2663}
2664
2665/// Query dictionary size.
2666///
2667/// # UPSTREAM-PARITY
2668///
2669/// ```c
2670/// unsigned int xmlDictSize(const xmlDictPtr dict);
2671/// ```
2672#[no_mangle]
2673pub extern "C" fn xmlDictSize(dict: *const c_void) -> c_uint {
2674    // Phase 1: STUB
2675    0
2676}
2677
2678/// Free a dictionary.
2679///
2680/// # UPSTREAM-PARITY
2681///
2682/// ```c
2683/// void xmlDictFree(xmlDictPtr dict);
2684/// ```
2685#[no_mangle]
2686pub extern "C" fn xmlDictFree(_dict: *mut c_void) {
2687    // Phase 1: STUB
2688}
2689
2690/// Set the dictionary size limit.
2691///
2692/// # UPSTREAM-PARITY
2693///
2694/// ```c
2695/// unsigned int xmlDictSetLimit(xmlDictPtr dict, unsigned int limit);
2696/// ```
2697#[no_mangle]
2698pub extern "C" fn xmlDictSetLimit(_dict: *mut c_void, _limit: c_uint) -> c_uint {
2699    // Phase 1: STUB
2700    0
2701}
2702
2703/// Get current dictionary usage.
2704///
2705/// # UPSTREAM-PARITY
2706///
2707/// ```c
2708/// unsigned int xmlDictGetUsage(const xmlDictPtr dict);
2709/// ```
2710#[no_mangle]
2711pub extern "C" fn xmlDictGetUsage(_dict: *const c_void) -> c_uint {
2712    // Phase 1: STUB
2713    0
2714}
2715
2716// ═══════════════════════════════════════════════════════════════════════════════
2717// 10. Hash Table
2718// ═══════════════════════════════════════════════════════════════════════════════
2719
2720/// Create a new hash table.
2721///
2722/// # UPSTREAM-PARITY
2723///
2724/// ```c
2725/// xmlHashTablePtr xmlHashCreate(int size);
2726/// ```
2727#[no_mangle]
2728pub extern "C" fn xmlHashCreate(_size: c_int) -> *mut c_void {
2729    // Phase 1: STUB
2730    ptr::null_mut()
2731}
2732
2733/// Create a new hash table with a dictionary.
2734///
2735/// # UPSTREAM-PARITY
2736///
2737/// ```c
2738/// xmlHashTablePtr xmlHashCreateDict(int size, xmlDictPtr dict);
2739/// ```
2740#[no_mangle]
2741pub extern "C" fn xmlHashCreateDict(_size: c_int, _dict: *mut c_void) -> *mut c_void {
2742    // Phase 1: STUB
2743    ptr::null_mut()
2744}
2745
2746/// Free a hash table.
2747///
2748/// # UPSTREAM-PARITY
2749///
2750/// ```c
2751/// void xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f);
2752/// ```
2753#[no_mangle]
2754pub extern "C" fn xmlHashFree(
2755    _table: *mut c_void,
2756    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2757) {
2758    // Phase 1: STUB
2759}
2760
2761/// Add an entry to a hash table.
2762///
2763/// # UPSTREAM-PARITY
2764///
2765/// ```c
2766/// int xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata);
2767/// ```
2768#[no_mangle]
2769pub unsafe extern "C" fn xmlHashAddEntry(
2770    _table: *mut c_void,
2771    _name: *const xmlChar,
2772    _userdata: *mut c_void,
2773) -> c_int {
2774    // Phase 1: STUB
2775    0
2776}
2777
2778/// Add a 2-key entry.
2779///
2780/// # UPSTREAM-PARITY
2781///
2782/// ```c
2783/// int xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
2784///                      const xmlChar *name2, void *userdata);
2785/// ```
2786#[no_mangle]
2787pub unsafe extern "C" fn xmlHashAddEntry2(
2788    _table: *mut c_void,
2789    _name: *const xmlChar,
2790    _name2: *const xmlChar,
2791    _userdata: *mut c_void,
2792) -> c_int {
2793    // Phase 1: STUB
2794    0
2795}
2796
2797/// Add a 3-key entry.
2798///
2799/// # UPSTREAM-PARITY
2800///
2801/// ```c
2802/// int xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
2803///                      const xmlChar *name2, const xmlChar *name3, void *userdata);
2804/// ```
2805#[no_mangle]
2806pub unsafe extern "C" fn xmlHashAddEntry3(
2807    _table: *mut c_void,
2808    _name: *const xmlChar,
2809    _name2: *const xmlChar,
2810    _name3: *const xmlChar,
2811    _userdata: *mut c_void,
2812) -> c_int {
2813    // Phase 1: STUB
2814    0
2815}
2816
2817/// Update or add an entry.
2818///
2819/// # UPSTREAM-PARITY
2820///
2821/// ```c
2822/// int xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
2823///                        void *userdata, xmlHashDeallocator f);
2824/// ```
2825#[no_mangle]
2826pub unsafe extern "C" fn xmlHashUpdateEntry(
2827    _table: *mut c_void,
2828    _name: *const xmlChar,
2829    _userdata: *mut c_void,
2830    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2831) -> c_int {
2832    // Phase 1: STUB
2833    0
2834}
2835
2836/// Update or add a 2-key entry.
2837#[no_mangle]
2838pub unsafe extern "C" fn xmlHashUpdateEntry2(
2839    _table: *mut c_void,
2840    _name: *const xmlChar,
2841    _name2: *const xmlChar,
2842    _userdata: *mut c_void,
2843    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2844) -> c_int {
2845    // Phase 1: STUB
2846    0
2847}
2848
2849/// Update or add a 3-key entry.
2850#[no_mangle]
2851pub unsafe extern "C" fn xmlHashUpdateEntry3(
2852    _table: *mut c_void,
2853    _name: *const xmlChar,
2854    _name2: *const xmlChar,
2855    _name3: *const xmlChar,
2856    _userdata: *mut c_void,
2857    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2858) -> c_int {
2859    // Phase 1: STUB
2860    0
2861}
2862
2863/// Look up an entry.
2864///
2865/// # UPSTREAM-PARITY
2866///
2867/// ```c
2868/// void *xmlHashLookup(xmlHashTablePtr table, const xmlChar *name);
2869/// ```
2870#[no_mangle]
2871pub unsafe extern "C" fn xmlHashLookup(_table: *mut c_void, _name: *const xmlChar) -> *mut c_void {
2872    // Phase 1: STUB
2873    ptr::null_mut()
2874}
2875
2876/// Look up a 2-key entry.
2877#[no_mangle]
2878pub unsafe extern "C" fn xmlHashLookup2(
2879    _table: *mut c_void,
2880    _name: *const xmlChar,
2881    _name2: *const xmlChar,
2882) -> *mut c_void {
2883    // Phase 1: STUB
2884    ptr::null_mut()
2885}
2886
2887/// Look up a 3-key entry.
2888#[no_mangle]
2889pub unsafe extern "C" fn xmlHashLookup3(
2890    _table: *mut c_void,
2891    _name: *const xmlChar,
2892    _name2: *const xmlChar,
2893    _name3: *const xmlChar,
2894) -> *mut c_void {
2895    // Phase 1: STUB
2896    ptr::null_mut()
2897}
2898
2899/// Get the size of a hash table.
2900///
2901/// # UPSTREAM-PARITY
2902///
2903/// ```c
2904/// int xmlHashSize(xmlHashTablePtr table);
2905/// ```
2906#[no_mangle]
2907pub extern "C" fn xmlHashSize(_table: *mut c_void) -> c_int {
2908    // Phase 1: STUB
2909    0
2910}
2911
2912/// Remove an entry.
2913///
2914/// # UPSTREAM-PARITY
2915///
2916/// ```c
2917/// int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
2918///                        xmlHashDeallocator f);
2919/// ```
2920#[no_mangle]
2921pub unsafe extern "C" fn xmlHashRemoveEntry(
2922    _table: *mut c_void,
2923    _name: *const xmlChar,
2924    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2925) -> c_int {
2926    // Phase 1: STUB
2927    0
2928}
2929
2930/// Remove a 2-key entry.
2931#[no_mangle]
2932pub unsafe extern "C" fn xmlHashRemoveEntry2(
2933    _table: *mut c_void,
2934    _name: *const xmlChar,
2935    _name2: *const xmlChar,
2936    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2937) -> c_int {
2938    // Phase 1: STUB
2939    0
2940}
2941
2942/// Remove a 3-key entry.
2943#[no_mangle]
2944pub unsafe extern "C" fn xmlHashRemoveEntry3(
2945    _table: *mut c_void,
2946    _name: *const xmlChar,
2947    _name2: *const xmlChar,
2948    _name3: *const xmlChar,
2949    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2950) -> c_int {
2951    // Phase 1: STUB
2952    0
2953}
2954
2955/// Scan a hash table with a scanner function.
2956///
2957/// # UPSTREAM-PARITY
2958///
2959/// ```c
2960/// void xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data);
2961/// ```
2962#[no_mangle]
2963pub extern "C" fn xmlHashScan(
2964    _table: *mut c_void,
2965    _f: Option<unsafe extern "C" fn(*mut c_void, *const xmlChar, *mut c_void)>,
2966    _data: *mut c_void,
2967) {
2968    // Phase 1: STUB
2969}
2970
2971/// Scan a hash table with a full scanner function.
2972#[no_mangle]
2973pub extern "C" fn xmlHashScanFull(
2974    _table: *mut c_void,
2975    _f: Option<unsafe extern "C" fn(*mut c_void, *const xmlChar, *mut c_void, *mut c_void)>,
2976    _data: *mut c_void,
2977) {
2978    // Phase 1: STUB
2979}
2980
2981/// Copy a hash table.
2982///
2983/// # UPSTREAM-PARITY
2984///
2985/// ```c
2986/// xmlHashTablePtr xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f);
2987/// ```
2988#[no_mangle]
2989pub extern "C" fn xmlHashCopy(
2990    _table: *mut c_void,
2991    _f: Option<unsafe extern "C" fn(*mut c_void, *const xmlChar) -> *mut c_void>,
2992) -> *mut c_void {
2993    // Phase 1: STUB
2994    ptr::null_mut()
2995}
2996
2997// ═══════════════════════════════════════════════════════════════════════════════
2998// 11. List
2999// ═══════════════════════════════════════════════════════════════════════════════
3000
3001/// Create a new list.
3002///
3003/// # UPSTREAM-PARITY
3004///
3005/// ```c
3006/// xmlListPtr xmlListCreate(xmlListDeallocator deallocator,
3007///                          xmlListDataCompare compare);
3008/// ```
3009#[no_mangle]
3010pub extern "C" fn xmlListCreate(
3011    _deallocator: Option<unsafe extern "C" fn(*mut c_void)>,
3012    _compare: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
3013) -> *mut c_void {
3014    // Phase 1: STUB
3015    ptr::null_mut()
3016}
3017
3018/// Delete a list.
3019///
3020/// # UPSTREAM-PARITY
3021///
3022/// ```c
3023/// void xmlListDelete(xmlListPtr list);
3024/// ```
3025#[no_mangle]
3026pub extern "C" fn xmlListDelete(_list: *mut c_void) {
3027    // Phase 1: STUB
3028}
3029
3030/// Search a list.
3031///
3032/// # UPSTREAM-PARITY
3033///
3034/// ```c
3035/// void *xmlListSearch(xmlListPtr list, void *data);
3036/// ```
3037#[no_mangle]
3038pub extern "C" fn xmlListSearch(_list: *mut c_void, _data: *mut c_void) -> *mut c_void {
3039    // Phase 1: STUB
3040    ptr::null_mut()
3041}
3042
3043/// Walk a list.
3044///
3045/// # UPSTREAM-PARITY
3046///
3047/// ```c
3048/// void xmlListWalk(xmlListPtr list, xmlListWalker walker, void *data);
3049/// ```
3050#[no_mangle]
3051pub extern "C" fn xmlListWalk(
3052    _list: *mut c_void,
3053    _walker: Option<unsafe extern "C" fn(*mut c_void, *mut c_void) -> c_int>,
3054    _data: *mut c_void,
3055) {
3056    // Phase 1: STUB
3057}
3058
3059/// Push to back.
3060///
3061/// # UPSTREAM-PARITY
3062///
3063/// ```c
3064/// int xmlListPushBack(xmlListPtr list, void *data);
3065/// ```
3066#[no_mangle]
3067pub extern "C" fn xmlListPushBack(_list: *mut c_void, _data: *mut c_void) -> c_int {
3068    // Phase 1: STUB
3069    0
3070}
3071
3072/// Push to front.
3073///
3074/// # UPSTREAM-PARITY
3075///
3076/// ```c
3077/// int xmlListPushFront(xmlListPtr list, void *data);
3078/// ```
3079#[no_mangle]
3080pub extern "C" fn xmlListPushFront(_list: *mut c_void, _data: *mut c_void) -> c_int {
3081    // Phase 1: STUB
3082    0
3083}
3084
3085/// Pop from back.
3086#[no_mangle]
3087pub extern "C" fn xmlListPopBack(_list: *mut c_void) {
3088    // Phase 1: STUB
3089}
3090
3091/// Pop from front.
3092#[no_mangle]
3093pub extern "C" fn xmlListPopFront(_list: *mut c_void) {
3094    // Phase 1: STUB
3095}
3096
3097/// Insert into sorted list.
3098///
3099/// # UPSTREAM-PARITY
3100///
3101/// ```c
3102/// int xmlListInsert(xmlListPtr list, void *data);
3103/// ```
3104#[no_mangle]
3105pub extern "C" fn xmlListInsert(_list: *mut c_void, _data: *mut c_void) -> c_int {
3106    // Phase 1: STUB
3107    0
3108}
3109
3110/// Append to list.
3111#[no_mangle]
3112pub extern "C" fn xmlListAppend(_list: *mut c_void, _data: *mut c_void) -> c_int {
3113    // Phase 1: STUB
3114    0
3115}
3116
3117/// Remove first matching element.
3118#[no_mangle]
3119pub extern "C" fn xmlListRemoveFirst(_list: *mut c_void, _data: *mut c_void) -> c_int {
3120    // Phase 1: STUB
3121    0
3122}
3123
3124/// Remove last matching element.
3125#[no_mangle]
3126pub extern "C" fn xmlListRemoveLast(_list: *mut c_void, _data: *mut c_void) -> c_int {
3127    // Phase 1: STUB
3128    0
3129}
3130
3131/// Remove all matching elements.
3132#[no_mangle]
3133pub extern "C" fn xmlListRemoveAll(_list: *mut c_void, _data: *mut c_void) -> c_int {
3134    // Phase 1: STUB
3135    0
3136}
3137
3138/// Clear a list.
3139#[no_mangle]
3140pub extern "C" fn xmlListClear(_list: *mut c_void) {
3141    // Phase 1: STUB
3142}
3143
3144/// Check if list is empty.
3145///
3146/// # UPSTREAM-PARITY
3147///
3148/// ```c
3149/// int xmlListEmpty(xmlListPtr list);
3150/// ```
3151#[no_mangle]
3152pub extern "C" fn xmlListEmpty(_list: *mut c_void) -> c_int {
3153    // Phase 1: STUB
3154    1
3155}
3156
3157/// Get front element.
3158///
3159/// # UPSTREAM-PARITY
3160///
3161/// ```c
3162/// void *xmlListFront(xmlListPtr list);
3163/// ```
3164#[no_mangle]
3165pub extern "C" fn xmlListFront(_list: *mut c_void) -> *mut c_void {
3166    // Phase 1: STUB
3167    ptr::null_mut()
3168}
3169
3170/// Get back element.
3171///
3172/// # UPSTREAM-PARITY
3173///
3174/// ```c
3175/// void *xmlListBack(xmlListPtr list);
3176/// ```
3177#[no_mangle]
3178pub extern "C" fn xmlListBack(_list: *mut c_void) -> *mut c_void {
3179    // Phase 1: STUB
3180    ptr::null_mut()
3181}
3182
3183/// Get list size.
3184///
3185/// # UPSTREAM-PARITY
3186///
3187/// ```c
3188/// int xmlListSize(xmlListPtr list);
3189/// ```
3190#[no_mangle]
3191pub extern "C" fn xmlListSize(_list: *mut c_void) -> c_int {
3192    // Phase 1: STUB
3193    0
3194}
3195
3196/// Sort a list.
3197#[no_mangle]
3198pub extern "C" fn xmlListSort(_list: *mut c_void) {
3199    // Phase 1: STUB
3200}
3201
3202/// Reverse a list.
3203#[no_mangle]
3204pub extern "C" fn xmlListReverse(_list: *mut c_void) {
3205    // Phase 1: STUB
3206}
3207
3208/// Reverse a list in-place.
3209#[no_mangle]
3210pub extern "C" fn xmlListReverseSplice(_list: *mut c_void, _list2: *mut c_void) {
3211    // Phase 1: STUB
3212}
3213
3214/// Merge two sorted lists.
3215#[no_mangle]
3216pub extern "C" fn xmlListMerge(_list: *mut c_void, _list2: *mut c_void) {
3217    // Phase 1: STUB
3218}
3219
3220// ═══════════════════════════════════════════════════════════════════════════════
3221// 12. Buffer
3222// ═══════════════════════════════════════════════════════════════════════════════
3223
3224/// Create a new buffer.
3225///
3226/// # UPSTREAM-PARITY
3227///
3228/// ```c
3229/// xmlBufferPtr xmlBufferCreate(void);
3230/// ```
3231#[no_mangle]
3232pub extern "C" fn xmlBufferCreate() -> *mut _xmlBuffer {
3233    crate::xml::io::buf_create(-1)
3234}
3235
3236/// Create a new buffer of a given size.
3237///
3238/// # UPSTREAM-PARITY
3239///
3240/// ```c
3241/// xmlBufferPtr xmlBufferCreateSize(size_t size);
3242/// ```
3243#[no_mangle]
3244pub extern "C" fn xmlBufferCreateSize(size: usize) -> *mut _xmlBuffer {
3245    crate::xml::io::buf_create(size as c_int)
3246}
3247
3248/// Create a buffer from a static string.
3249///
3250/// # UPSTREAM-PARITY
3251///
3252/// ```c
3253/// xmlBufferPtr xmlBufferCreateStatic(void *mem, size_t size);
3254/// ```
3255#[no_mangle]
3256pub extern "C" fn xmlBufferCreateStatic(mem: *mut c_void, size: usize) -> *mut _xmlBuffer {
3257    if mem.is_null() || size == 0 {
3258        return ptr::null_mut();
3259    }
3260    crate::xml::io::buf_create_static(mem as *const xmlChar, size as c_int)
3261}
3262
3263/// Free a buffer.
3264///
3265/// # UPSTREAM-PARITY
3266///
3267/// ```c
3268/// void xmlBufferFree(xmlBufferPtr buf);
3269/// ```
3270#[no_mangle]
3271pub extern "C" fn xmlBufferFree(buf: *mut _xmlBuffer) {
3272    crate::xml::io::buf_free(buf)
3273}
3274
3275/// Empty a buffer.
3276///
3277/// # UPSTREAM-PARITY
3278///
3279/// ```c
3280/// void xmlBufferEmpty(xmlBufferPtr buf);
3281/// ```
3282#[no_mangle]
3283pub extern "C" fn xmlBufferEmpty(buf: *mut _xmlBuffer) {
3284    if buf.is_null() {
3285        return;
3286    }
3287    unsafe {
3288        if !(*buf).content.is_null() {
3289            *(*buf).content = 0;
3290        }
3291        (*buf).use_ = 0;
3292    }
3293}
3294
3295/// Get buffer content.
3296///
3297/// # UPSTREAM-PARITY
3298///
3299/// ```c
3300/// xmlChar *xmlBufferContent(const xmlBuffer *buf);
3301/// ```
3302#[no_mangle]
3303pub extern "C" fn xmlBufferContent(buf: *const _xmlBuffer) -> *mut xmlChar {
3304    crate::xml::io::buf_content(buf as *mut _xmlBuffer)
3305}
3306
3307/// Get buffer length.
3308///
3309/// # UPSTREAM-PARITY
3310///
3311/// ```c
3312/// int xmlBufferLength(const xmlBuffer *buf);
3313/// ```
3314#[no_mangle]
3315pub extern "C" fn xmlBufferLength(buf: *const _xmlBuffer) -> c_int {
3316    crate::xml::io::buf_length(buf as *mut _xmlBuffer)
3317}
3318
3319/// Write to a buffer.
3320///
3321/// # UPSTREAM-PARITY
3322///
3323/// ```c
3324/// int xmlBufferAdd(xmlBufferPtr buf, const xmlChar *str, int len);
3325/// ```
3326#[no_mangle]
3327pub unsafe extern "C" fn xmlBufferAdd(
3328    buf: *mut _xmlBuffer,
3329    str: *const xmlChar,
3330    len: c_int,
3331) -> c_int {
3332    crate::xml::io::buf_add(buf, str, len)
3333}
3334
3335/// Write to a buffer at a position.
3336///
3337/// # UPSTREAM-PARITY
3338///
3339/// ```c
3340/// int xmlBufferAddHead(xmlBufferPtr buf, const xmlChar *str, int len);
3341/// ```
3342#[no_mangle]
3343pub unsafe extern "C" fn xmlBufferAddHead(
3344    buf: *mut _xmlBuffer,
3345    str: *const xmlChar,
3346    len: c_int,
3347) -> c_int {
3348    crate::xml::io::buf_add_head(buf, str, len)
3349}
3350
3351/// Write a C string to a buffer.
3352///
3353/// # UPSTREAM-PARITY
3354///
3355/// ```c
3356/// int xmlBufferCat(xmlBufferPtr buf, const xmlChar *str);
3357/// ```
3358#[no_mangle]
3359pub unsafe extern "C" fn xmlBufferCat(buf: *mut _xmlBuffer, str: *const xmlChar) -> c_int {
3360    if str.is_null() {
3361        return -1;
3362    }
3363    let len = crate::xml::string::xml_strlen(str) as c_int;
3364    crate::xml::io::buf_add(buf, str, len)
3365}
3366
3367/// Set buffer allocation scheme.
3368///
3369/// # UPSTREAM-PARITY
3370///
3371/// ```c
3372/// void xmlBufferSetAllocationScheme(xmlBufferPtr buf,
3373///                                    xmlBufferAllocationScheme scheme);
3374/// ```
3375#[no_mangle]
3376pub extern "C" fn xmlBufferSetAllocationScheme(buf: *mut _xmlBuffer, scheme: c_int) {
3377    if buf.is_null() {
3378        return;
3379    }
3380    unsafe {
3381        (*buf).alloc = scheme;
3382    }
3383}
3384
3385/// Shrink buffer.
3386///
3387/// # UPSTREAM-PARITY
3388///
3389/// ```c
3390/// int xmlBufferShrink(xmlBufferPtr buf, int len);
3391/// ```
3392#[no_mangle]
3393pub extern "C" fn xmlBufferShrink(buf: *mut _xmlBuffer, len: c_int) -> c_int {
3394    if buf.is_null() || len <= 0 {
3395        return 0;
3396    }
3397    unsafe {
3398        let b = &mut *buf;
3399        let shrink_len = (len as c_uint).min(b.use_);
3400        if shrink_len > 0 {
3401            let remaining = b.use_ - shrink_len;
3402            if remaining > 0 {
3403                core::ptr::copy(
3404                    b.content.add(shrink_len as usize),
3405                    b.content,
3406                    remaining as usize,
3407                );
3408            }
3409            *b.content.add(remaining as usize) = 0;
3410            b.use_ = remaining;
3411        }
3412    }
3413    len
3414}
3415
3416/// Grow buffer.
3417///
3418/// # UPSTREAM-PARITY
3419///
3420/// ```c
3421/// int xmlBufferGrow(xmlBufferPtr buf, int len);
3422/// ```
3423#[no_mangle]
3424pub extern "C" fn xmlBufferGrow(buf: *mut _xmlBuffer, len: c_int) -> c_int {
3425    if buf.is_null() || len <= 0 {
3426        return 0;
3427    }
3428    let cur_use = unsafe { (*buf).use_ };
3429    let new_size = cur_use + len as c_uint + 1;
3430    crate::xml::io::buf_grow(buf, new_size)
3431}
3432
3433/// Reserve buffer space.
3434///
3435/// # UPSTREAM-PARITY
3436///
3437/// ```c
3438/// int xmlBufferReserve(xmlBufferPtr buf, int len);
3439/// ```
3440#[no_mangle]
3441pub extern "C" fn xmlBufferReserve(buf: *mut _xmlBuffer, len: c_int) -> c_int {
3442    xmlBufferGrow(buf, len)
3443}
3444
3445/// Detach buffer content.
3446///
3447/// # UPSTREAM-PARITY
3448///
3449/// ```c
3450/// xmlChar *xmlBufferDetach(xmlBufferPtr buf);
3451/// ```
3452#[no_mangle]
3453pub extern "C" fn xmlBufferDetach(buf: *mut _xmlBuffer) -> *mut xmlChar {
3454    if buf.is_null() {
3455        return ptr::null_mut();
3456    }
3457    unsafe {
3458        let content = (*buf).content;
3459        (*buf).content = ptr::null_mut();
3460        (*buf).use_ = 0;
3461        (*buf).size = 0;
3462        content
3463    }
3464}
3465
3466// ═══════════════════════════════════════════════════════════════════════════════
3467// 13. Encoding
3468// ═══════════════════════════════════════════════════════════════════════════════
3469
3470/// Get encoding from a name string.
3471///
3472/// # UPSTREAM-PARITY
3473///
3474/// ```c
3475/// xmlCharEncoding xmlGetCharEncoding(const char *name);
3476/// ```
3477#[no_mangle]
3478pub extern "C" fn xmlGetCharEncoding(name: *const c_char) -> c_int {
3479    if name.is_null() {
3480        return 0; // XML_CHAR_ENCODING_NONE
3481    }
3482    let name_bytes = unsafe {
3483        let len = libc::strlen(name);
3484        core::slice::from_raw_parts(name as *const u8, len)
3485    };
3486    crate::xml::encoding::encoding_from_name(name_bytes) as c_int
3487}
3488
3489/// Find an encoding handler.
3490///
3491/// # UPSTREAM-PARITY
3492///
3493/// ```c
3494/// xmlCharEncodingHandlerPtr xmlFindCharEncodingHandler(const char *name);
3495/// ```
3496#[no_mangle]
3497pub extern "C" fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut c_void {
3498    if name.is_null() {
3499        return ptr::null_mut();
3500    }
3501    crate::xml::encoding::find_encoding_handler(name as *const xmlChar) as *mut c_void
3502}
3503
3504/// Close an encoding handler.
3505///
3506/// # UPSTREAM-PARITY
3507///
3508/// ```c
3509/// int xmlCharEncCloseFunc(xmlCharEncodingHandlerPtr handler);
3510/// ```
3511#[no_mangle]
3512pub extern "C" fn xmlCharEncCloseFunc(handler: *mut c_void) -> c_int {
3513    if handler.is_null() {
3514        return -1;
3515    }
3516    // Free the encoding handler
3517    unsafe {
3518        let h = handler as *mut crate::abi::structs::_xmlCharEncodingHandler;
3519        if !(*h).name.is_null() {
3520            crate::abi::allocator::xmlFree((*h).name as *mut c_void);
3521        }
3522        crate::abi::allocator::xmlFree(handler);
3523    }
3524    0
3525}
3526
3527/// Convert an input buffer's encoding.
3528///
3529/// # UPSTREAM-PARITY
3530///
3531/// ```c
3532/// int xmlCharEncInput(xmlParserInputBufferPtr input, int to);
3533/// ```
3534#[no_mangle]
3535pub unsafe extern "C" fn xmlCharEncInput(input: *mut _xmlParserInputBuffer, _to: c_int) -> c_int {
3536    if input.is_null() {
3537        return -1;
3538    }
3539    let handler = (*input).encoder as *mut crate::abi::structs::_xmlCharEncodingHandler;
3540    if handler.is_null() {
3541        return -1;
3542    }
3543    let raw = (*input).raw as *mut crate::abi::structs::_xmlBuffer;
3544    let buf = (*input).buffer as *mut crate::abi::structs::_xmlBuffer;
3545    if raw.is_null() || buf.is_null() {
3546        return -1;
3547    }
3548    crate::xml::encoding::char_enc_in(handler, buf, raw)
3549}
3550
3551/// Convert an output buffer's encoding.
3552///
3553/// # UPSTREAM-PARITY
3554///
3555/// ```c
3556/// int xmlCharEncOutput(xmlOutputBufferPtr output, int to);
3557/// ```
3558#[no_mangle]
3559pub unsafe extern "C" fn xmlCharEncOutput(output: *mut _xmlOutputBuffer, _to: c_int) -> c_int {
3560    if output.is_null() {
3561        return -1;
3562    }
3563    let handler = (*output).encoder as *mut crate::abi::structs::_xmlCharEncodingHandler;
3564    if handler.is_null() {
3565        return -1;
3566    }
3567    let buf = (*output).buffer as *mut crate::abi::structs::_xmlBuffer;
3568    let conv = (*output).conv as *mut crate::abi::structs::_xmlBuffer;
3569    if buf.is_null() || conv.is_null() {
3570        return -1;
3571    }
3572    crate::xml::encoding::char_enc_out(handler, conv, buf)
3573}
3574
3575// ═══════════════════════════════════════════════════════════════════════════════
3576// URI
3577// ═══════════════════════════════════════════════════════════════════════════════
3578
3579/// Parse a URI string.
3580///
3581/// # UPSTREAM-PARITY
3582///
3583/// ```c
3584/// xmlURIPtr xmlParseURI(const char *str);
3585/// ```
3586#[no_mangle]
3587pub unsafe extern "C" fn xmlParseURI(str: *const c_char) -> *mut c_void {
3588    crate::xml::uri::xmlParseURI(str)
3589}
3590
3591/// Parse a URI string (raw version).
3592///
3593/// # UPSTREAM-PARITY
3594///
3595/// ```c
3596/// xmlURIPtr xmlParseURIRaw(const char *str, int raw);
3597/// ```
3598#[no_mangle]
3599pub unsafe extern "C" fn xmlParseURIRaw(str: *const c_char, raw: c_int) -> *mut c_void {
3600    let _ = raw;
3601    crate::xml::uri::xmlParseURI(str)
3602}
3603
3604/// Free a URI structure.
3605///
3606/// # UPSTREAM-PARITY
3607///
3608/// ```c
3609/// void xmlFreeURI(xmlURIPtr uri);
3610/// ```
3611#[no_mangle]
3612pub unsafe extern "C" fn xmlFreeURI(uri: *mut c_void) {
3613    crate::xml::uri::xmlFreeURI(uri)
3614}
3615
3616/// Create an empty URI.
3617///
3618/// # UPSTREAM-PARITY
3619///
3620/// ```c
3621/// xmlURIPtr xmlCreateURI(void);
3622/// ```
3623#[no_mangle]
3624pub extern "C" fn xmlCreateURI() -> *mut c_void {
3625    crate::xml::uri::xmlCreateURI()
3626}
3627
3628/// Save a URI structure to a string.
3629///
3630/// # UPSTREAM-PARITY
3631///
3632/// ```c
3633/// xmlChar *xmlSaveUri(xmlURIPtr uri);
3634/// ```
3635#[no_mangle]
3636pub unsafe extern "C" fn xmlSaveUri(uri: *mut c_void) -> *mut xmlChar {
3637    crate::xml::uri::xmlSaveUri(uri)
3638}
3639
3640/// Escape a URI string.
3641///
3642/// # UPSTREAM-PARITY
3643///
3644/// ```c
3645/// xmlChar *xmlURIEscapeStr(const xmlChar *str, const xmlChar *list);
3646/// ```
3647#[no_mangle]
3648pub unsafe extern "C" fn xmlURIEscapeStr(
3649    str: *const xmlChar,
3650    list: *const xmlChar,
3651) -> *mut xmlChar {
3652    crate::xml::uri::xmlURIEscapeStr(str, list)
3653}
3654
3655/// Unescape a URI string.
3656///
3657/// # UPSTREAM-PARITY
3658///
3659/// ```c
3660/// char *xmlURIUnescapeString(const char *str, int len, char *target);
3661/// ```
3662#[no_mangle]
3663pub unsafe extern "C" fn xmlURIUnescapeString(
3664    str: *const c_char,
3665    len: c_int,
3666    target: *mut c_char,
3667) -> *mut c_char {
3668    crate::xml::uri::xmlURIUnescapeString(str, len, target)
3669}
3670
3671// ═══════════════════════════════════════════════════════════════════════════════
3672// 14. XPath
3673// ═══════════════════════════════════════════════════════════════════════════════
3674
3675// ── Helper functions ────────────────────────────────────────────────────
3676
3677/// Convert an internal `XPathValue` to a C ABI `_xmlXPathObject`.
3678///
3679/// The returned pointer is heap-allocated via `xmlMallocZero` and must be
3680/// freed with `xmlXPathFreeObject`.
3681///
3682/// # Safety
3683///
3684/// Must be called from a context where `xmlMalloc` is safe to call.
3685unsafe fn xpath_to_object(val: XPathValue) -> *mut _xmlXPathObject {
3686    let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
3687    if obj.is_null() {
3688        return ptr::null_mut();
3689    }
3690    match val {
3691        XPathValue::NodeSet(ns) => {
3692            (*obj).type_ = xmlXPathObjectType::XPATH_NODESET as c_int;
3693            (*obj).nodesetval = ns.to_raw() as *mut c_void;
3694        }
3695        XPathValue::Boolean(b) => {
3696            (*obj).type_ = xmlXPathObjectType::XPATH_BOOLEAN as c_int;
3697            (*obj).boolval = if b { 1 } else { 0 };
3698        }
3699        XPathValue::Number(n) => {
3700            (*obj).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
3701            (*obj).floatval = n;
3702        }
3703        XPathValue::String(s) => {
3704            (*obj).type_ = xmlXPathObjectType::XPATH_STRING as c_int;
3705            let bytes = s.as_bytes();
3706            let len = bytes.len();
3707            let buf = xmlMalloc(len + 1) as *mut xmlChar;
3708            if !buf.is_null() {
3709                ptr::copy_nonoverlapping(bytes.as_ptr(), buf, len);
3710                *buf.add(len) = 0; // null terminator
3711            }
3712            (*obj).stringval = buf;
3713        }
3714    }
3715    obj
3716}
3717
3718/// Extract an internal `XPathValue` from a C ABI `_xmlXPathObject`.
3719///
3720/// # Safety
3721///
3722/// `obj` must be a valid, non-null pointer to a properly initialised
3723/// `_xmlXPathObject`.
3724unsafe fn object_to_xpathvalue(obj: *mut _xmlXPathObject) -> XPathValue {
3725    let typ = (*obj).type_;
3726    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
3727        let ns_ptr = (*obj).nodesetval as *mut _xmlNodeSet;
3728        if ns_ptr.is_null() {
3729            return XPathValue::NodeSet(NodeSet::new());
3730        }
3731        let node_nr = (*ns_ptr).nodeNr;
3732        let node_tab = (*ns_ptr).nodeTab;
3733        let mut ns = NodeSet::new();
3734        if !node_tab.is_null() {
3735            for i in 0..node_nr as isize {
3736                let node = *node_tab.add(i as usize);
3737                ns.push(node);
3738            }
3739        }
3740        XPathValue::NodeSet(ns)
3741    } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
3742        XPathValue::Boolean((*obj).boolval != 0)
3743    } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
3744        XPathValue::Number((*obj).floatval)
3745    } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
3746        let s_ptr = (*obj).stringval;
3747        if s_ptr.is_null() {
3748            XPathValue::String(String::new())
3749        } else {
3750            let s = CStr::from_ptr(s_ptr as *const c_char)
3751                .to_string_lossy()
3752                .into_owned();
3753            XPathValue::String(s)
3754        }
3755    } else {
3756        // Undefined / unknown type — return boolean false as a safe default.
3757        XPathValue::Boolean(false)
3758    }
3759}
3760
3761// ── Compiled expression registry ────────────────────────────────────────
3762//
3763// Compiled XPath expressions are opaque pointers returned by xmlXPathCompile.
3764// We store them in a global registry keyed by a monotonically increasing ID.
3765
3766static COMPILED_EXPRS: Lazy<Mutex<HashMap<u64, Box<CompiledExpr>>>> =
3767    Lazy::new(|| Mutex::new(HashMap::new()));
3768static NEXT_COMPILED_KEY: Lazy<Mutex<u64>> = Lazy::new(|| Mutex::new(1));
3769
3770// ── C extension-function registry ──────────────────────────────────────
3771//
3772// C extension functions registered via xmlXPathRegisterFunc / RegisterFuncNS
3773// are stored here because the Rust XPathFunction signature is incompatible
3774// with the C xmlXPathFunction calling convention (the C function expects a
3775// parser context, not pre-evaluated argument slices). The registration is
3776// stored faithfully; invoking registered C functions from within the Rust
3777// evaluator requires a bridge that is not yet implemented.
3778
3779type CXPathFunc = unsafe extern "C" fn(*mut c_void, c_int);
3780
3781/// Wrapper around `*mut c_void` that implements `Send` + `Sync` so it can
3782/// be used as a key in a `Mutex`-protected global `HashMap`.
3783#[derive(Clone, Copy, PartialEq, Eq, Hash)]
3784struct SendSyncPtr(*mut c_void);
3785unsafe impl Send for SendSyncPtr {}
3786unsafe impl Sync for SendSyncPtr {}
3787
3788static C_FUNCTIONS: Lazy<Mutex<HashMap<(SendSyncPtr, String), CXPathFunc>>> =
3789    Lazy::new(|| Mutex::new(HashMap::new()));
3790
3791/// Rust-side wrapper that is registered in the internal XPathContext when a
3792/// C extension function is registered. It looks up the C function pointer and
3793/// attempts to call it, but the calling-convention mismatch means this is a
3794/// stub that returns an error for now.
3795fn c_func_stub(_ctx: &mut XPathContext, _args: &[XPathValue]) -> Result<XPathValue, String> {
3796    Err(
3797        "C extension function cannot be called from Rust evaluator without a parser-context bridge"
3798            .to_string(),
3799    )
3800}
3801
3802// ── Public API ─────────────────────────────────────────────────────────
3803
3804/// Create a new XPath context.
3805///
3806/// Allocates a `_xmlXPathContext` and an internal `XPathContext`, storing
3807/// the latter's pointer in the `extra` field.
3808///
3809/// # UPSTREAM-PARITY
3810///
3811/// ```c
3812/// xmlXPathContextPtr xmlXPathNewContext(xmlDocPtr doc);
3813/// ```
3814#[no_mangle]
3815pub unsafe extern "C" fn xmlXPathNewContext(doc: *mut _xmlDoc) -> *mut _xmlXPathContext {
3816    let ctxt = xmlMallocZero(size_of::<_xmlXPathContext>()) as *mut _xmlXPathContext;
3817    if ctxt.is_null() {
3818        return ptr::null_mut();
3819    }
3820
3821    // Initialise the C ABI context fields.
3822    (*ctxt).doc = doc;
3823    (*ctxt).node = ptr::null_mut();
3824    (*ctxt).contextSize = 1;
3825    (*ctxt).proximityPosition = 1;
3826
3827    // Create the internal XPathContext and store it in `extra`.
3828    let internal = Box::new(XPathContext::new(doc));
3829    (*ctxt).extra = Box::into_raw(internal) as *mut c_void;
3830
3831    ctxt
3832}
3833
3834/// Free an XPath context.
3835///
3836/// # UPSTREAM-PARITY
3837///
3838/// ```c
3839/// void xmlXPathFreeContext(xmlXPathContextPtr ctxt);
3840/// ```
3841#[no_mangle]
3842pub unsafe extern "C" fn xmlXPathFreeContext(ctxt: *mut _xmlXPathContext) {
3843    if ctxt.is_null() {
3844        return;
3845    }
3846    // Drop the internal XPathContext.
3847    if !(*ctxt).extra.is_null() {
3848        let _ = Box::from_raw((*ctxt).extra as *mut XPathContext);
3849        (*ctxt).extra = ptr::null_mut();
3850    }
3851    // Free the C ABI context struct.
3852    xmlFree(ctxt as *mut c_void);
3853}
3854
3855/// Evaluate an XPath expression.
3856///
3857/// # UPSTREAM-PARITY
3858///
3859/// ```c
3860/// xmlXPathObjectPtr xmlXPathEvalExpression(const xmlChar *str,
3861///                                          xmlXPathContextPtr ctxt);
3862/// ```
3863#[no_mangle]
3864pub unsafe extern "C" fn xmlXPathEvalExpression(
3865    str_: *const xmlChar,
3866    ctxt: *mut _xmlXPathContext,
3867) -> *mut _xmlXPathObject {
3868    if str_.is_null() || ctxt.is_null() {
3869        return ptr::null_mut();
3870    }
3871    let expr_str = match CStr::from_ptr(str_ as *const c_char).to_str() {
3872        Ok(s) => s,
3873        Err(_) => return ptr::null_mut(),
3874    };
3875    let internal = (*ctxt).extra as *mut XPathContext;
3876    if internal.is_null() {
3877        return ptr::null_mut();
3878    }
3879    let internal = &mut *internal;
3880
3881    match crate::xml::xpath::evaluate_str(expr_str, internal) {
3882        Some(val) => xpath_to_object(val),
3883        None => ptr::null_mut(),
3884    }
3885}
3886
3887/// Evaluate an XPath expression (simplified alias).
3888///
3889/// # UPSTREAM-PARITY
3890///
3891/// ```c
3892/// xmlXPathObjectPtr xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctxt);
3893/// ```
3894#[no_mangle]
3895pub unsafe extern "C" fn xmlXPathEval(
3896    str_: *const xmlChar,
3897    ctxt: *mut _xmlXPathContext,
3898) -> *mut _xmlXPathObject {
3899    xmlXPathEvalExpression(str_, ctxt)
3900}
3901
3902/// Free an XPath object.
3903///
3904/// Releases the internal members (string buffer or node-set) and then frees
3905/// the object struct itself.
3906///
3907/// # UPSTREAM-PARITY
3908///
3909/// ```c
3910/// void xmlXPathFreeObject(xmlXPathObjectPtr obj);
3911/// ```
3912#[no_mangle]
3913pub unsafe extern "C" fn xmlXPathFreeObject(obj: *mut _xmlXPathObject) {
3914    if obj.is_null() {
3915        return;
3916    }
3917    let typ = (*obj).type_;
3918    // Free string storage.
3919    if typ == xmlXPathObjectType::XPATH_STRING as c_int {
3920        if !(*obj).stringval.is_null() {
3921            xmlFree((*obj).stringval as *mut c_void);
3922            (*obj).stringval = ptr::null_mut();
3923        }
3924    }
3925    // Free node-set storage.
3926    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
3927        let ns = (*obj).nodesetval as *mut _xmlNodeSet;
3928        if !ns.is_null() {
3929            if !(*ns).nodeTab.is_null() {
3930                xmlFree((*ns).nodeTab as *mut c_void);
3931            }
3932            xmlFree(ns as *mut c_void);
3933        }
3934        (*obj).nodesetval = ptr::null_mut();
3935    }
3936    xmlFree(obj as *mut c_void);
3937}
3938
3939/// Compile an XPath expression.
3940///
3941/// Returns an opaque pointer that can be passed to `xmlXPathEvalExpression`
3942/// (via the compiled-expr infrastructure) or freed with `xmlXPathFreeCompExpr`.
3943///
3944/// # UPSTREAM-PARITY
3945///
3946/// ```c
3947/// xmlXPathCompExprPtr xmlXPathCompile(const xmlChar *str);
3948/// ```
3949#[no_mangle]
3950pub unsafe extern "C" fn xmlXPathCompile(str_: *const xmlChar) -> *mut c_void {
3951    if str_.is_null() {
3952        return ptr::null_mut();
3953    }
3954    let expr_str = match CStr::from_ptr(str_ as *const c_char).to_str() {
3955        Ok(s) => s,
3956        Err(_) => return ptr::null_mut(),
3957    };
3958
3959    match crate::xml::xpath::compile(expr_str) {
3960        Some(compiled) => {
3961            let mut map = COMPILED_EXPRS.lock();
3962            let mut counter = NEXT_COMPILED_KEY.lock();
3963            let key = *counter;
3964            *counter += 1;
3965            map.insert(key, Box::new(compiled));
3966            key as *mut c_void
3967        }
3968        None => ptr::null_mut(),
3969    }
3970}
3971
3972/// Free a compiled XPath expression.
3973///
3974/// # UPSTREAM-PARITY
3975///
3976/// ```c
3977/// void xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp);
3978/// ```
3979#[no_mangle]
3980pub unsafe extern "C" fn xmlXPathFreeCompExpr(comp: *mut c_void) {
3981    if comp.is_null() {
3982        return;
3983    }
3984    let mut map = COMPILED_EXPRS.lock();
3985    map.remove(&(comp as u64));
3986}
3987
3988/// Register an XPath namespace.
3989///
3990/// # UPSTREAM-PARITY
3991///
3992/// ```c
3993/// int xmlXPathRegisterNs(xmlXPathContextPtr ctxt,
3994///                        const xmlChar *prefix, const xmlChar *ns_uri);
3995/// ```
3996#[no_mangle]
3997pub unsafe extern "C" fn xmlXPathRegisterNs(
3998    ctxt: *mut _xmlXPathContext,
3999    prefix: *const xmlChar,
4000    ns_uri: *const xmlChar,
4001) -> c_int {
4002    if ctxt.is_null() || prefix.is_null() || ns_uri.is_null() {
4003        return -1;
4004    }
4005    let internal = (*ctxt).extra as *mut XPathContext;
4006    if internal.is_null() {
4007        return -1;
4008    }
4009    let internal = &mut *internal;
4010
4011    let prefix_str = match CStr::from_ptr(prefix as *const c_char).to_str() {
4012        Ok(s) => s,
4013        Err(_) => return -1,
4014    };
4015    let uri_str = match CStr::from_ptr(ns_uri as *const c_char).to_str() {
4016        Ok(s) => s,
4017        Err(_) => return -1,
4018    };
4019
4020    internal.register_namespace(prefix_str, uri_str);
4021    0
4022}
4023
4024/// Register an XPath function.
4025///
4026/// The C function pointer is stored in a side table keyed by the context.
4027/// A Rust-side stub is registered in the internal context so that the Rust
4028/// evaluator is aware of the function; however, calling the C function
4029/// directly from the Rust evaluator is not yet supported.
4030///
4031/// # UPSTREAM-PARITY
4032///
4033/// ```c
4034/// int xmlXPathRegisterFunc(xmlXPathContextPtr ctxt,
4035///                          const xmlChar *name, xmlXPathFunction f);
4036/// ```
4037#[no_mangle]
4038pub unsafe extern "C" fn xmlXPathRegisterFunc(
4039    ctxt: *mut _xmlXPathContext,
4040    name: *const xmlChar,
4041    f: Option<unsafe extern "C" fn(*mut c_void, c_int)>,
4042) -> c_int {
4043    if ctxt.is_null() || name.is_null() {
4044        return -1;
4045    }
4046    let internal = (*ctxt).extra as *mut XPathContext;
4047    if internal.is_null() {
4048        return -1;
4049    }
4050    let internal = &mut *internal;
4051
4052    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
4053        Ok(s) => s,
4054        Err(_) => return -1,
4055    };
4056
4057    if let Some(func) = f {
4058        // Store the C function pointer in the side table.
4059        let key = (SendSyncPtr((*ctxt).extra), name_str.to_string());
4060        C_FUNCTIONS.lock().insert(key, func);
4061        // Register a Rust stub so the evaluator knows the function exists.
4062        internal.register_function(name_str, c_func_stub);
4063    }
4064    0
4065}
4066
4067/// Register an XPath function with namespace.
4068///
4069/// # UPSTREAM-PARITY
4070///
4071/// ```c
4072/// int xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt,
4073///                            const xmlChar *name, const xmlChar *ns_uri,
4074///                            xmlXPathFunction f);
4075/// ```
4076#[no_mangle]
4077pub unsafe extern "C" fn xmlXPathRegisterFuncNS(
4078    ctxt: *mut _xmlXPathContext,
4079    name: *const xmlChar,
4080    ns_uri: *const xmlChar,
4081    f: Option<unsafe extern "C" fn(*mut c_void, c_int)>,
4082) -> c_int {
4083    if ctxt.is_null() || name.is_null() {
4084        return -1;
4085    }
4086    let internal = (*ctxt).extra as *mut XPathContext;
4087    if internal.is_null() {
4088        return -1;
4089    }
4090    let internal = &mut *internal;
4091
4092    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
4093        Ok(s) => s,
4094        Err(_) => return -1,
4095    };
4096    let ns_str = if ns_uri.is_null() {
4097        String::new()
4098    } else {
4099        match CStr::from_ptr(ns_uri as *const c_char).to_str() {
4100            Ok(s) => s.to_string(),
4101            Err(_) => return -1,
4102        }
4103    };
4104
4105    // Use "{ns}:" prefix as part of the key to keep functions unique.
4106    let qualified = if ns_str.is_empty() {
4107        name_str.to_string()
4108    } else {
4109        format!("{{{}}}{}", ns_str, name_str)
4110    };
4111
4112    if let Some(func) = f {
4113        let key = (SendSyncPtr((*ctxt).extra), qualified.clone());
4114        C_FUNCTIONS.lock().insert(key, func);
4115        internal.register_function(&qualified, c_func_stub);
4116    }
4117    0
4118}
4119
4120/// Register an XPath variable.
4121///
4122/// # UPSTREAM-PARITY
4123///
4124/// ```c
4125/// int xmlXPathRegisterVariable(xmlXPathContextPtr ctxt,
4126///                              const xmlChar *name, xmlXPathObjectPtr value);
4127/// ```
4128#[no_mangle]
4129pub unsafe extern "C" fn xmlXPathRegisterVariable(
4130    ctxt: *mut _xmlXPathContext,
4131    name: *const xmlChar,
4132    value: *mut _xmlXPathObject,
4133) -> c_int {
4134    if ctxt.is_null() || name.is_null() || value.is_null() {
4135        return -1;
4136    }
4137    let internal = (*ctxt).extra as *mut XPathContext;
4138    if internal.is_null() {
4139        return -1;
4140    }
4141    let internal = &mut *internal;
4142
4143    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
4144        Ok(s) => s,
4145        Err(_) => return -1,
4146    };
4147
4148    let xpath_val = object_to_xpathvalue(value);
4149    internal.register_variable(name_str, xpath_val);
4150    0
4151}
4152
4153/// Create an XPath object wrapping a single node in a node-set.
4154///
4155/// # UPSTREAM-PARITY
4156///
4157/// ```c
4158/// xmlXPathObjectPtr xmlXPathNewNodeSet(xmlNodePtr val);
4159/// ```
4160#[no_mangle]
4161pub unsafe extern "C" fn xmlXPathNewNodeSet(val: *mut _xmlNode) -> *mut _xmlXPathObject {
4162    let ns = if val.is_null() {
4163        NodeSet::new()
4164    } else {
4165        NodeSet::singleton(val)
4166    };
4167    xpath_to_object(XPathValue::NodeSet(ns))
4168}
4169
4170/// Create an XPath object from a C string value.
4171///
4172/// # UPSTREAM-PARITY
4173///
4174/// ```c
4175/// xmlXPathObjectPtr xmlXPathNewCString(const xmlChar *val);
4176/// ```
4177#[no_mangle]
4178pub unsafe extern "C" fn xmlXPathNewCString(val: *const xmlChar) -> *mut _xmlXPathObject {
4179    if val.is_null() {
4180        return xpath_to_object(XPathValue::String(String::new()));
4181    }
4182    let s = match CStr::from_ptr(val as *const c_char).to_str() {
4183        Ok(s) => s.to_string(),
4184        Err(_) => return ptr::null_mut(),
4185    };
4186    xpath_to_object(XPathValue::String(s))
4187}
4188
4189/// Create an XPath number object.
4190///
4191/// # UPSTREAM-PARITY
4192///
4193/// ```c
4194/// xmlXPathObjectPtr xmlXPathNewFloat(double val);
4195/// ```
4196#[no_mangle]
4197pub extern "C" fn xmlXPathNewFloat(val: f64) -> *mut _xmlXPathObject {
4198    unsafe { xpath_to_object(XPathValue::Number(val)) }
4199}
4200
4201/// Create an XPath boolean object.
4202///
4203/// # UPSTREAM-PARITY
4204///
4205/// ```c
4206/// xmlXPathObjectPtr xmlXPathNewBoolean(int val);
4207/// ```
4208#[no_mangle]
4209pub extern "C" fn xmlXPathNewBoolean(val: c_int) -> *mut _xmlXPathObject {
4210    unsafe { xpath_to_object(XPathValue::Boolean(val != 0)) }
4211}
4212
4213// ═══════════════════════════════════════════════════════════════════════════════
4214// 14.5. XPointer
4215// ═══════════════════════════════════════════════════════════════════════════════
4216
4217/// Evaluate an XPointer expression.
4218///
4219/// Delegates to the xpointer module.
4220///
4221/// # UPSTREAM-PARITY
4222///
4223/// ```c
4224/// xmlNodePtr xmlXPtrEval(const xmlChar *expr, xmlDocPtr doc);
4225/// ```
4226#[no_mangle]
4227pub unsafe extern "C" fn xmlXPtrEval(expr: *const c_char, doc: *mut _xmlDoc) -> *mut _xmlNode {
4228    crate::xml::xpointer::xmlXPtrEval(expr, doc)
4229}
4230
4231// ═══════════════════════════════════════════════════════════════════════════════
4232// 15. XInclude
4233// ═══════════════════════════════════════════════════════════════════════════════
4234
4235/// Process XInclude nodes in a document.
4236///
4237/// # UPSTREAM-PARITY
4238///
4239/// ```c
4240/// int xmlXIncludeProcess(xmlDocPtr doc);
4241/// ```
4242#[no_mangle]
4243pub unsafe extern "C" fn xmlXIncludeProcess(doc: *mut _xmlDoc) -> c_int {
4244    crate::xml::xinclude::xinclude_process(doc)
4245}
4246
4247/// Process XInclude nodes with flags.
4248///
4249/// # UPSTREAM-PARITY
4250///
4251/// ```c
4252/// int xmlXIncludeProcessFlags(xmlDocPtr doc, int flags);
4253/// ```
4254#[no_mangle]
4255pub unsafe extern "C" fn xmlXIncludeProcessFlags(doc: *mut _xmlDoc, flags: c_int) -> c_int {
4256    crate::xml::xinclude::xinclude_process_flags(doc, flags)
4257}
4258
4259// ═══════════════════════════════════════════════════════════════════════════════
4260// 16. Catalog
4261// ═══════════════════════════════════════════════════════════════════════════════
4262
4263/// Load a catalog.
4264///
4265/// # UPSTREAM-PARITY
4266///
4267/// ```c
4268/// xmlCatalogPtr xmlCatalogLoad(const char *catalogs);
4269/// ```
4270#[no_mangle]
4271pub extern "C" fn xmlCatalogLoad(catalogs: *const c_char) -> *mut c_void {
4272    if catalogs.is_null() {
4273        return ptr::null_mut();
4274    }
4275    crate::xml::catalog::load_catalog(catalogs)
4276}
4277
4278/// Resolve a public ID.
4279///
4280/// # UPSTREAM-PARITY
4281///
4282/// ```c
4283/// xmlCharPtr xmlCatalogResolvePublic(const xmlChar *pubID);
4284/// ```
4285#[no_mangle]
4286pub unsafe extern "C" fn xmlCatalogResolvePublic(pubID: *const xmlChar) -> *mut xmlChar {
4287    if pubID.is_null() {
4288        return ptr::null_mut();
4289    }
4290    crate::xml::catalog::resolve_public(pubID)
4291}
4292
4293/// Resolve a system ID.
4294///
4295/// # UPSTREAM-PARITY
4296///
4297/// ```c
4298/// xmlCharPtr xmlCatalogResolveSystem(const xmlChar *sysID);
4299/// ```
4300#[no_mangle]
4301pub unsafe extern "C" fn xmlCatalogResolveSystem(sysID: *const xmlChar) -> *mut xmlChar {
4302    if sysID.is_null() {
4303        return ptr::null_mut();
4304    }
4305    crate::xml::catalog::resolve_system(sysID)
4306}
4307
4308/// Resolve a URI.
4309///
4310/// # UPSTREAM-PARITY
4311///
4312/// ```c
4313/// xmlCharPtr xmlCatalogResolveURI(const xmlChar *URI);
4314/// ```
4315#[no_mangle]
4316pub unsafe extern "C" fn xmlCatalogResolveURI(URI: *const xmlChar) -> *mut xmlChar {
4317    if URI.is_null() {
4318        return ptr::null_mut();
4319    }
4320    crate::xml::catalog::resolve_uri(URI)
4321}
4322
4323/// Set catalog defaults.
4324///
4325/// # UPSTREAM-PARITY
4326///
4327/// ```c
4328/// void xmlCatalogSetDefaults(xmlCatalogAllowValue allow);
4329/// ```
4330#[no_mangle]
4331pub extern "C" fn xmlCatalogSetDefaults(allow: c_int) {
4332    crate::xml::catalog::set_defaults(allow)
4333}
4334
4335/// Get catalog defaults.
4336///
4337/// # UPSTREAM-PARITY
4338///
4339/// ```c
4340/// xmlCatalogAllowValue xmlCatalogGetDefaults(void);
4341/// ```
4342#[no_mangle]
4343pub extern "C" fn xmlCatalogGetDefaults() -> c_int {
4344    crate::xml::catalog::get_defaults()
4345}
4346
4347/// Add a catalog.
4348///
4349/// # UPSTREAM-PARITY
4350///
4351/// ```c
4352/// int xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace);
4353/// ```
4354#[no_mangle]
4355pub unsafe extern "C" fn xmlCatalogAdd(
4356    type_: *const xmlChar,
4357    orig: *const xmlChar,
4358    replace: *const xmlChar,
4359) -> c_int {
4360    if type_.is_null() || orig.is_null() || replace.is_null() {
4361        return -1;
4362    }
4363    crate::xml::catalog::add(type_, orig, replace)
4364}
4365
4366/// Remove a catalog entry.
4367///
4368/// # UPSTREAM-PARITY
4369///
4370/// ```c
4371/// int xmlCatalogRemove(const xmlChar *value);
4372/// ```
4373#[no_mangle]
4374pub unsafe extern "C" fn xmlCatalogRemove(value: *const xmlChar) -> c_int {
4375    if value.is_null() {
4376        return 0;
4377    }
4378    crate::xml::catalog::remove(value)
4379}
4380
4381/// Clean up the catalog subsystem.
4382///
4383/// # UPSTREAM-PARITY
4384///
4385/// ```c
4386/// void xmlCatalogCleanup(void);
4387/// ```
4388#[no_mangle]
4389pub extern "C" fn xmlCatalogCleanup() {
4390    crate::xml::catalog::cleanup();
4391}
4392
4393/// Convert an SGML catalog to XML.
4394///
4395/// # UPSTREAM-PARITY
4396///
4397/// ```c
4398/// xmlDocPtr xmlCatalogConvert(void);
4399/// ```
4400#[no_mangle]
4401pub extern "C" fn xmlCatalogConvert() -> *mut _xmlDoc {
4402    // SAFETY: catalog::convert() allocates and builds an XML document tree.
4403    unsafe { crate::xml::catalog::convert() }
4404}
4405
4406// ═══════════════════════════════════════════════════════════════════════════════
4407// 17. HTML
4408// ═══════════════════════════════════════════════════════════════════════════════
4409
4410/// Parse an HTML document from a file.
4411///
4412/// # UPSTREAM-PARITY
4413///
4414/// ```c
4415/// htmlDocPtr htmlParseFile(const char *filename, const char *encoding);
4416/// ```
4417#[no_mangle]
4418pub unsafe extern "C" fn htmlParseFile(
4419    _filename: *const c_char,
4420    _encoding: *const c_char,
4421) -> *mut _xmlDoc {
4422    // Phase 1: STUB
4423    ptr::null_mut()
4424}
4425
4426/// Parse an HTML document from memory.
4427///
4428/// # UPSTREAM-PARITY
4429///
4430/// ```c
4431/// htmlDocPtr htmlParseMemory(const char *buffer, int size);
4432/// ```
4433#[no_mangle]
4434pub unsafe extern "C" fn htmlParseMemory(_buffer: *const c_char, _size: c_int) -> *mut _xmlDoc {
4435    // Phase 1: STUB
4436    ptr::null_mut()
4437}
4438
4439/// Parse an HTML document from a document string.
4440///
4441/// # UPSTREAM-PARITY
4442///
4443/// ```c
4444/// htmlDocPtr htmlParseDoc(const xmlChar *cur, const char *encoding);
4445/// ```
4446#[no_mangle]
4447pub unsafe extern "C" fn htmlParseDoc(
4448    _cur: *const xmlChar,
4449    _encoding: *const c_char,
4450) -> *mut _xmlDoc {
4451    // Phase 1: STUB
4452    ptr::null_mut()
4453}
4454
4455/// Create an HTML parser context.
4456///
4457/// # UPSTREAM-PARITY
4458///
4459/// ```c
4460/// htmlParserCtxtPtr htmlCreateFileParserCtxt(const char *filename,
4461///                                            const char *encoding);
4462/// ```
4463#[no_mangle]
4464pub unsafe extern "C" fn htmlCreateFileParserCtxt(
4465    _filename: *const c_char,
4466    _encoding: *const c_char,
4467) -> *mut c_void {
4468    // Phase 1: STUB
4469    ptr::null_mut()
4470}
4471
4472/// Free an HTML parser context.
4473///
4474/// # UPSTREAM-PARITY
4475///
4476/// ```c
4477/// void htmlFreeParserCtxt(htmlParserCtxtPtr ctxt);
4478/// ```
4479#[no_mangle]
4480pub extern "C" fn htmlFreeParserCtxt(_ctxt: *mut c_void) {
4481    // Phase 1: STUB
4482}
4483
4484/// Initialize the HTML parser.
4485///
4486/// # UPSTREAM-PARITY
4487///
4488/// ```c
4489/// void htmlInitParser(void);
4490/// ```
4491#[no_mangle]
4492pub extern "C" fn htmlInitParser() {
4493    // Phase 1: STUB
4494}
4495
4496/// Clean up the HTML parser.
4497///
4498/// # UPSTREAM-PARITY
4499///
4500/// ```c
4501/// void htmlCleanupParser(void);
4502/// ```
4503#[no_mangle]
4504pub extern "C" fn htmlCleanupParser() {
4505    // Phase 1: STUB
4506}
4507
4508// ═══════════════════════════════════════════════════════════════════════════════
4509// 17.5. Validation (DTD)
4510// ═══════════════════════════════════════════════════════════════════════════════
4511
4512/// Create a new validation context.
4513///
4514/// # UPSTREAM-PARITY
4515///
4516/// ```c
4517/// xmlValidCtxtPtr xmlNewValidCtxt(void);
4518/// ```
4519#[no_mangle]
4520pub unsafe extern "C" fn xmlNewValidCtxt() -> *mut _xmlValidCtxt {
4521    crate::xml::validation::new_valid_ctxt()
4522}
4523
4524/// Free a validation context.
4525///
4526/// # UPSTREAM-PARITY
4527///
4528/// ```c
4529/// void xmlFreeValidCtxt(xmlValidCtxtPtr ctxt);
4530/// ```
4531#[no_mangle]
4532pub unsafe extern "C" fn xmlFreeValidCtxt(ctxt: *mut _xmlValidCtxt) {
4533    crate::xml::validation::free_valid_ctxt(ctxt);
4534}
4535
4536/// Set error and warning callbacks on a validation context.
4537///
4538/// # UPSTREAM-PARITY
4539///
4540/// ```c
4541/// void xmlSetValidErrors(xmlValidCtxtPtr ctxt,
4542///                        xmlGenericErrorFunc err,
4543///                        xmlGenericErrorFunc warn,
4544///                        void *data);
4545/// ```
4546#[no_mangle]
4547pub unsafe extern "C" fn xmlSetValidErrors(
4548    ctxt: *mut _xmlValidCtxt,
4549    err: Option<xmlGenericErrorFunc>,
4550    warn: Option<xmlGenericErrorFunc>,
4551    data: *mut c_void,
4552) {
4553    crate::xml::validation::set_valid_errors(ctxt, err, warn, data);
4554}
4555
4556/// Validate a document against its DTD.
4557///
4558/// # UPSTREAM-PARITY
4559///
4560/// ```c
4561/// int xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
4562/// ```
4563#[no_mangle]
4564pub unsafe extern "C" fn xmlValidateDocument(
4565    ctxt: *mut _xmlValidCtxt,
4566    doc: *mut _xmlDoc,
4567) -> c_int {
4568    crate::xml::validation::validate_document(ctxt, doc)
4569}
4570
4571/// Final validation pass (check ID/IDREF consistency).
4572///
4573/// # UPSTREAM-PARITY
4574///
4575/// ```c
4576/// int xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
4577/// ```
4578#[no_mangle]
4579pub unsafe extern "C" fn xmlValidateDocumentFinal(
4580    ctxt: *mut _xmlValidCtxt,
4581    doc: *mut _xmlDoc,
4582) -> c_int {
4583    crate::xml::validation::validate_document_final(ctxt, doc)
4584}
4585
4586/// Validate an element node against its DTD declarations.
4587///
4588/// # UPSTREAM-PARITY
4589///
4590/// ```c
4591/// int xmlValidateElement(xmlValidCtxtPtr ctxt,
4592///                        xmlDocPtr doc,
4593///                        xmlNodePtr elem);
4594/// ```
4595#[no_mangle]
4596pub unsafe extern "C" fn xmlValidateElement(
4597    ctxt: *mut _xmlValidCtxt,
4598    doc: *mut _xmlDoc,
4599    elem: *mut _xmlNode,
4600) -> c_int {
4601    crate::xml::validation::validate_element(ctxt, doc, elem)
4602}
4603
4604/// Validate an attribute declaration.
4605///
4606/// # UPSTREAM-PARITY
4607///
4608/// ```c
4609/// int xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt,
4610///                              xmlDocPtr doc,
4611///                              xmlNodePtr elem,
4612///                              xmlAttributePtr attr);
4613/// ```
4614#[no_mangle]
4615pub unsafe extern "C" fn xmlValidateAttributeDecl(
4616    ctxt: *mut _xmlValidCtxt,
4617    doc: *mut _xmlDoc,
4618    elem: *mut _xmlNode,
4619    attr: *mut _xmlAttribute,
4620) -> c_int {
4621    crate::xml::validation::validate_attribute_decl(ctxt, doc, elem, attr)
4622}
4623
4624/// Validate an attribute value against its declared type.
4625///
4626/// # UPSTREAM-PARITY
4627///
4628/// ```c
4629/// int xmlValidateAttributeValue(int type, const xmlChar *value);
4630/// ```
4631#[no_mangle]
4632pub unsafe extern "C" fn xmlValidateAttributeValue(
4633    atype: c_int,
4634    value: *const xmlChar,
4635) -> c_int {
4636    crate::xml::validation::validate_attribute_value(atype, value)
4637}
4638
4639/// Validate a NOTATION reference.
4640///
4641/// # UPSTREAM-PARITY
4642///
4643/// ```c
4644/// int xmlValidateNotationUse(xmlValidCtxtPtr ctxt,
4645///                            xmlDocPtr doc,
4646///                            const xmlChar *notationName);
4647/// ```
4648#[no_mangle]
4649pub unsafe extern "C" fn xmlValidateNotationUse(
4650    ctxt: *mut _xmlValidCtxt,
4651    doc: *mut _xmlDoc,
4652    notation_name: *const xmlChar,
4653) -> c_int {
4654    crate::xml::validation::validate_notation_use(ctxt, doc, notation_name)
4655}
4656
4657/// Validate an ID value (check uniqueness).
4658///
4659/// # UPSTREAM-PARITY
4660///
4661/// ```c
4662/// int xmlValidateID(xmlValidCtxtPtr ctxt,
4663///                   xmlDocPtr doc,
4664///                   xmlNodePtr node,
4665///                   const xmlChar *value);
4666/// ```
4667#[no_mangle]
4668pub unsafe extern "C" fn xmlValidateID(
4669    ctxt: *mut _xmlValidCtxt,
4670    doc: *mut _xmlDoc,
4671    node: *mut _xmlNode,
4672    value: *const xmlChar,
4673) -> c_int {
4674    crate::xml::validation::validate_id(ctxt, doc, node, value)
4675}
4676
4677/// Validate an IDREF value (check it references a known ID).
4678///
4679/// # UPSTREAM-PARITY
4680///
4681/// ```c
4682/// int xmlValidateIDRef(xmlValidCtxtPtr ctxt,
4683///                      xmlDocPtr doc,
4684///                      xmlNodePtr node,
4685///                      const xmlChar *value);
4686/// ```
4687#[no_mangle]
4688pub unsafe extern "C" fn xmlValidateIDRef(
4689    ctxt: *mut _xmlValidCtxt,
4690    doc: *mut _xmlDoc,
4691    node: *mut _xmlNode,
4692    value: *const xmlChar,
4693) -> c_int {
4694    crate::xml::validation::validate_id_ref(ctxt, doc, node, value)
4695}
4696
4697/// Validate IDREFS (whitespace-separated list of IDREFs).
4698///
4699/// # UPSTREAM-PARITY
4700///
4701/// ```c
4702/// int xmlValidateIDRefs(xmlValidCtxtPtr ctxt,
4703///                       xmlDocPtr doc,
4704///                       xmlNodePtr node,
4705///                       const xmlChar *value);
4706/// ```
4707#[no_mangle]
4708pub unsafe extern "C" fn xmlValidateIDRefs(
4709    ctxt: *mut _xmlValidCtxt,
4710    doc: *mut _xmlDoc,
4711    node: *mut _xmlNode,
4712    value: *const xmlChar,
4713) -> c_int {
4714    crate::xml::validation::validate_id_refs(ctxt, doc, node, value)
4715}
4716
4717/// Validate an NMTOKEN value.
4718///
4719/// # UPSTREAM-PARITY
4720///
4721/// ```c
4722/// int xmlValidateNmtoken(const xmlChar *value);
4723/// ```
4724#[no_mangle]
4725pub unsafe extern "C" fn xmlValidateNmtoken(value: *const xmlChar) -> c_int {
4726    crate::xml::validation::validate_nmtoken(value)
4727}
4728
4729/// Validate a whitespace-separated list of NMTOKENs.
4730///
4731/// # UPSTREAM-PARITY
4732///
4733/// ```c
4734/// int xmlValidateNmtokens(const xmlChar *value);
4735/// ```
4736#[no_mangle]
4737pub unsafe extern "C" fn xmlValidateNmtokens(value: *const xmlChar) -> c_int {
4738    crate::xml::validation::validate_nmtokens(value)
4739}
4740
4741/// Validate an XML Name value.
4742///
4743/// # UPSTREAM-PARITY
4744///
4745/// ```c
4746/// int xmlValidateName(const xmlChar *value);
4747/// ```
4748#[no_mangle]
4749pub unsafe extern "C" fn xmlValidateName(value: *const xmlChar) -> c_int {
4750    crate::xml::validation::validate_name(value)
4751}
4752
4753/// Validate a whitespace-separated list of XML Names.
4754///
4755/// # UPSTREAM-PARITY
4756///
4757/// ```c
4758/// int xmlValidateNames(const xmlChar *value);
4759/// ```
4760#[no_mangle]
4761pub unsafe extern "C" fn xmlValidateNames(value: *const xmlChar) -> c_int {
4762    crate::xml::validation::validate_names(value)
4763}
4764
4765/// Validate the root element of a document.
4766///
4767/// # UPSTREAM-PARITY
4768///
4769/// ```c
4770/// int xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
4771/// ```
4772#[no_mangle]
4773pub unsafe extern "C" fn xmlValidateRoot(
4774    ctxt: *mut _xmlValidCtxt,
4775    doc: *mut _xmlDoc,
4776) -> c_int {
4777    crate::xml::validation::validate_root(ctxt, doc)
4778}
4779
4780/// Validate element content against its content model.
4781///
4782/// # UPSTREAM-PARITY
4783///
4784/// ```c
4785/// int xmlValidateContent(xmlValidCtxtPtr ctxt,
4786///                        xmlNodePtr node,
4787///                        xmlDocPtr doc);
4788/// ```
4789#[no_mangle]
4790pub unsafe extern "C" fn xmlValidateContent(
4791    ctxt: *mut _xmlValidCtxt,
4792    node: *mut _xmlNode,
4793    doc: *mut _xmlDoc,
4794) -> c_int {
4795    crate::xml::validation::validate_content(ctxt, node, doc)
4796}
4797
4798/// Check if an element is declared as mixed content.
4799///
4800/// # UPSTREAM-PARITY
4801///
4802/// ```c
4803/// int xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name);
4804/// ```
4805#[no_mangle]
4806pub unsafe extern "C" fn xmlIsMixedElement(
4807    doc: *mut _xmlDoc,
4808    name: *const xmlChar,
4809) -> c_int {
4810    crate::xml::validation::is_mixed_element(doc, name)
4811}
4812
4813/// Check if an element is declared as EMPTY.
4814///
4815/// # UPSTREAM-PARITY
4816///
4817/// ```c
4818/// int xmlIsEmptyElement(xmlDocPtr doc, const xmlChar *name);
4819/// ```
4820#[no_mangle]
4821pub unsafe extern "C" fn xmlIsEmptyElement(
4822    doc: *mut _xmlDoc,
4823    name: *const xmlChar,
4824) -> c_int {
4825    crate::xml::validation::is_empty_element(doc, name)
4826}
4827
4828/// Validate a DTD's declarations.
4829///
4830/// # UPSTREAM-PARITY
4831///
4832/// ```c
4833/// int xmlValidateDtd(xmlValidCtxtPtr ctxt,
4834///                    xmlDocPtr doc,
4835///                    xmlDtdPtr dtd);
4836/// ```
4837#[no_mangle]
4838pub unsafe extern "C" fn xmlValidateDtd(
4839    ctxt: *mut _xmlValidCtxt,
4840    doc: *mut _xmlDoc,
4841    dtd: *mut _xmlDtd,
4842) -> c_int {
4843    crate::xml::validation::validate_dtd(ctxt, doc, dtd)
4844}
4845
4846/// Final DTD validation (ID/IDREF consistency).
4847///
4848/// # UPSTREAM-PARITY
4849///
4850/// ```c
4851/// int xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc);
4852/// ```
4853#[no_mangle]
4854pub unsafe extern "C" fn xmlValidateDtdFinal(
4855    ctxt: *mut _xmlValidCtxt,
4856    doc: *mut _xmlDoc,
4857) -> c_int {
4858    crate::xml::validation::validate_dtd_final(ctxt, doc)
4859}
4860
4861/// Validate that a value is in an enumeration.
4862///
4863/// # UPSTREAM-PARITY
4864///
4865/// ```c
4866/// int xmlValidateEnumeration(xmlValidCtxtPtr ctxt,
4867///                            const xmlChar *value,
4868///                            xmlEnumerationPtr tree);
4869/// ```
4870#[no_mangle]
4871pub unsafe extern "C" fn xmlValidateEnumeration(
4872    ctxt: *mut _xmlValidCtxt,
4873    value: *const xmlChar,
4874    tree: *mut _xmlEnumeration,
4875) -> c_int {
4876    crate::xml::validation::validate_enumeration(ctxt, value, tree)
4877}
4878
4879// ═══════════════════════════════════════════════════════════════════════════════
4880// 18. Debug / Miscellaneous
4881// ═══════════════════════════════════════════════════════════════════════════════
4882
4883/// Dump a document to a file for debugging.
4884///
4885/// # UPSTREAM-PARITY
4886///
4887/// ```c
4888/// void xmlDebugDumpDocument(FILE *output, xmlDocPtr doc);
4889/// ```
4890#[no_mangle]
4891pub unsafe extern "C" fn xmlDebugDumpDocument(_output: *mut c_void, _doc: *mut _xmlDoc) {
4892    // Phase 1: STUB
4893}
4894
4895/// Dump a node for debugging.
4896///
4897/// # UPSTREAM-PARITY
4898///
4899/// ```c
4900/// void xmlDebugDumpNode(FILE *output, xmlNodePtr node);
4901/// ```
4902#[no_mangle]
4903pub unsafe extern "C" fn xmlDebugDumpNode(_output: *mut c_void, _node: *mut _xmlNode) {
4904    // Phase 1: STUB
4905}
4906
4907/// Dump a node for debugging (recursive).
4908///
4909/// # UPSTREAM-PARITY
4910///
4911/// ```c
4912/// void xmlDebugDumpNodeList(FILE *output, xmlNodePtr node);
4913/// ```
4914#[no_mangle]
4915pub unsafe extern "C" fn xmlDebugDumpNodeList(_output: *mut c_void, _node: *mut _xmlNode) {
4916    // Phase 1: STUB
4917}
4918
4919/// Get the path to the current executable.
4920///
4921/// # UPSTREAM-PARITY
4922///
4923/// ```c
4924/// char *xmlGetBinaryPath(void);
4925/// ```
4926#[no_mangle]
4927pub extern "C" fn xmlGetBinaryPath() -> *mut c_char {
4928    // Phase 1: STUB
4929    ptr::null_mut()
4930}
4931
4932/// Get the path to the current executable's home directory.
4933///
4934/// # UPSTREAM-PARITY
4935///
4936/// ```c
4937/// char *xmlGetHomeOfBinary(void);
4938/// ```
4939#[no_mangle]
4940pub extern "C" fn xmlGetHomeOfBinary() -> *mut c_char {
4941    // Phase 1: STUB
4942    ptr::null_mut()
4943}