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/// Get the line number of a node.
1262///
1263/// # UPSTREAM-PARITY
1264///
1265/// ```c
1266/// long xmlGetLineNo(const xmlNode *node);
1267/// ```
1268#[no_mangle]
1269pub extern "C" fn xmlGetLineNo(node: *const _xmlNode) -> c_int {
1270    crate::xml::tree::get_line_no(node)
1271}
1272
1273// ═══════════════════════════════════════════════════════════════════════════════
1274// Serialization — xmlNodeDump, xmlDocDump, xmlSaveFile, etc.
1275// ═══════════════════════════════════════════════════════════════════════════════
1276
1277/// Dump a node to a buffer.
1278///
1279/// # UPSTREAM-PARITY
1280///
1281/// ```c
1282/// int xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level, int format);
1283/// ```
1284#[no_mangle]
1285pub unsafe extern "C" fn xmlNodeDump(
1286    buf: *mut _xmlBuffer,
1287    doc: *mut _xmlDoc,
1288    cur: *mut _xmlNode,
1289    level: c_int,
1290    format: c_int,
1291) -> c_int {
1292    if buf.is_null() || cur.is_null() {
1293        return -1;
1294    }
1295    crate::xml::tree::xmlNodeDump(buf, doc, cur, level, format)
1296}
1297
1298/// Dump a document to a file pointer.
1299///
1300/// # UPSTREAM-PARITY
1301///
1302/// ```c
1303/// int xmlDocDump(FILE *f, xmlDocPtr doc);
1304/// ```
1305#[no_mangle]
1306pub unsafe extern "C" fn xmlDocDump(fp: *mut c_void, doc: *mut _xmlDoc) -> c_int {
1307    if fp.is_null() || doc.is_null() {
1308        return -1;
1309    }
1310    crate::xml::tree::xmlDocDump(fp, doc)
1311}
1312
1313/// Dump a document to memory with format.
1314///
1315/// # UPSTREAM-PARITY
1316///
1317/// ```c
1318/// void xmlDocDumpFormatMemory(xmlDocPtr doc, xmlChar **mem, int *size, int format);
1319/// ```
1320#[no_mangle]
1321pub unsafe extern "C" fn xmlDocDumpFormatMemory(
1322    doc: *mut _xmlDoc,
1323    mem: *mut *mut xmlChar,
1324    size: *mut c_int,
1325    format: c_int,
1326) {
1327    if doc.is_null() || mem.is_null() || size.is_null() {
1328        return;
1329    }
1330    crate::xml::tree::xmlDocDumpFormatMemory(doc, mem, size, format)
1331}
1332
1333/// Dump a document to memory (unformatted).
1334///
1335/// # UPSTREAM-PARITY
1336///
1337/// ```c
1338/// void xmlDocDumpMemory(xmlDocPtr doc, xmlChar **mem, int *size);
1339/// ```
1340#[no_mangle]
1341pub unsafe extern "C" fn xmlDocDumpMemory(
1342    doc: *mut _xmlDoc,
1343    mem: *mut *mut xmlChar,
1344    size: *mut c_int,
1345) {
1346    if doc.is_null() || mem.is_null() || size.is_null() {
1347        return;
1348    }
1349    crate::xml::tree::xmlDocDumpMemory(doc, mem, size)
1350}
1351
1352/// Save a document to a file.
1353///
1354/// # UPSTREAM-PARITY
1355///
1356/// ```c
1357/// int xmlSaveFile(const char *filename, xmlDocPtr cur);
1358/// ```
1359#[no_mangle]
1360pub unsafe extern "C" fn xmlSaveFile(filename: *const c_char, cur: *mut _xmlDoc) -> c_int {
1361    if filename.is_null() || cur.is_null() {
1362        return -1;
1363    }
1364    crate::xml::tree::xmlSaveFile(filename, cur)
1365}
1366
1367/// Save a document to a file with encoding.
1368///
1369/// # UPSTREAM-PARITY
1370///
1371/// ```c
1372/// int xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding);
1373/// ```
1374#[no_mangle]
1375pub unsafe extern "C" fn xmlSaveFileEnc(
1376    filename: *const c_char,
1377    cur: *mut _xmlDoc,
1378    encoding: *const c_char,
1379) -> c_int {
1380    if filename.is_null() || cur.is_null() {
1381        return -1;
1382    }
1383    crate::xml::tree::xmlSaveFileEnc(filename, cur, encoding)
1384}
1385
1386/// Save a document to a file with format.
1387///
1388/// # UPSTREAM-PARITY
1389///
1390/// ```c
1391/// int xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format);
1392/// ```
1393#[no_mangle]
1394pub unsafe extern "C" fn xmlSaveFormatFile(
1395    filename: *const c_char,
1396    cur: *mut _xmlDoc,
1397    format: c_int,
1398) -> c_int {
1399    if filename.is_null() || cur.is_null() {
1400        return -1;
1401    }
1402    crate::xml::tree::xmlSaveFormatFile(filename, cur, format)
1403}
1404
1405/// Save a document to a file with encoding and format.
1406///
1407/// # UPSTREAM-PARITY
1408///
1409/// ```c
1410/// int xmlSaveFormatFileEnc(const char *filename, xmlDocPtr cur, const char *encoding, int format);
1411/// ```
1412#[no_mangle]
1413pub unsafe extern "C" fn xmlSaveFormatFileEnc(
1414    filename: *const c_char,
1415    cur: *mut _xmlDoc,
1416    encoding: *const c_char,
1417    format: c_int,
1418) -> c_int {
1419    if filename.is_null() || cur.is_null() {
1420        return -1;
1421    }
1422    crate::xml::tree::xmlSaveFormatFileEnc(filename, cur, encoding, format)
1423}
1424
1425// ═══════════════════════════════════════════════════════════════════════════════
1426// 7. Parser — SAX, DOM, Push, Reader
1427// ═══════════════════════════════════════════════════════════════════════════════
1428
1429/// Read an XML document from a string.
1430///
1431/// # UPSTREAM-PARITY
1432///
1433/// ```c
1434/// xmlDocPtr xmlReadDoc(const xmlChar *cur, const char *URL,
1435///                      const char *encoding, int options);
1436/// ```
1437///
1438/// Returns a parsed document. Caller must free with `xmlFreeDoc`.
1439#[no_mangle]
1440pub unsafe extern "C" fn xmlReadDoc(
1441    cur: *const xmlChar,
1442    URL: *const c_char,
1443    encoding: *const c_char,
1444    options: c_int,
1445) -> *mut _xmlDoc {
1446    // SAFETY: cur must be a valid null-terminated xmlChar string if non-null.
1447    if cur.is_null() {
1448        return ptr::null_mut();
1449    }
1450    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1451    if ctxt.is_null() {
1452        return ptr::null_mut();
1453    }
1454    let len = crate::xml::string::xml_strlen(cur);
1455    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
1456    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1457    (*ctxt).options = options;
1458    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1459        let doc = (*ctxt).myDoc;
1460        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1461        return doc;
1462    }
1463    let doc = (*ctxt).myDoc;
1464    if !doc.is_null() {
1465        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
1466    }
1467    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1468    doc
1469}
1470
1471/// Read an XML document from a file.
1472///
1473/// # UPSTREAM-PARITY
1474///
1475/// ```c
1476/// xmlDocPtr xmlReadFile(const char *URL, const char *encoding, int options);
1477/// ```
1478#[no_mangle]
1479pub unsafe extern "C" fn xmlReadFile(
1480    URL: *const c_char,
1481    encoding: *const c_char,
1482    options: c_int,
1483) -> *mut _xmlDoc {
1484    // SAFETY: URL must be a valid C string or NULL.
1485    if URL.is_null() {
1486        return ptr::null_mut();
1487    }
1488    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1489    if ctxt.is_null() {
1490        return ptr::null_mut();
1491    }
1492    let input = match crate::xml::parser::helpers::input_from_file(URL) {
1493        Ok(input) => input,
1494        Err(_) => {
1495            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1496            return ptr::null_mut();
1497        }
1498    };
1499    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1500    (*ctxt).options = options;
1501    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1502        let doc = (*ctxt).myDoc;
1503        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1504        return doc;
1505    }
1506    let doc = (*ctxt).myDoc;
1507    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1508    doc
1509}
1510
1511/// Read an XML document from memory.
1512///
1513/// # UPSTREAM-PARITY
1514///
1515/// ```c
1516/// xmlDocPtr xmlReadMemory(const char *buffer, int size,
1517///                         const char *URL, const char *encoding, int options);
1518/// ```
1519#[no_mangle]
1520pub unsafe extern "C" fn xmlReadMemory(
1521    buffer: *const c_char,
1522    size: c_int,
1523    URL: *const c_char,
1524    encoding: *const c_char,
1525    options: c_int,
1526) -> *mut _xmlDoc {
1527    // SAFETY: buffer must be a valid pointer with at least `size` readable bytes.
1528    if buffer.is_null() || size <= 0 {
1529        return ptr::null_mut();
1530    }
1531    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1532    if ctxt.is_null() {
1533        return ptr::null_mut();
1534    }
1535    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
1536    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1537    (*ctxt).options = options;
1538    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1539        let doc = (*ctxt).myDoc;
1540        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1541        return doc;
1542    }
1543    let doc = (*ctxt).myDoc;
1544    if !doc.is_null() && !URL.is_null() {
1545        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
1546    }
1547    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1548    doc
1549}
1550
1551/// Read an XML document from a file descriptor.
1552///
1553/// # UPSTREAM-PARITY
1554///
1555/// ```c
1556/// xmlDocPtr xmlReadFd(int fd, const char *URL, const char *encoding, int options);
1557/// ```
1558#[no_mangle]
1559pub unsafe extern "C" fn xmlReadFd(
1560    fd: c_int,
1561    URL: *const c_char,
1562    encoding: *const c_char,
1563    options: c_int,
1564) -> *mut _xmlDoc {
1565    // SAFETY: fd must be a valid open file descriptor.
1566    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1567    if ctxt.is_null() {
1568        return ptr::null_mut();
1569    }
1570    // Read all data from the fd
1571    let mut buf = Vec::new();
1572    let mut tmp = [0u8; 4096];
1573    loop {
1574        let n = libc::read(fd, tmp.as_mut_ptr() as *mut c_void, tmp.len());
1575        if n <= 0 {
1576            break;
1577        }
1578        buf.extend_from_slice(&tmp[..n as usize]);
1579    }
1580    let input = crate::xml::parser::helpers::input_from_memory(
1581        buf.as_ptr() as *const c_char,
1582        buf.len() as c_int,
1583    );
1584    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1585    (*ctxt).options = options;
1586    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1587        let doc = (*ctxt).myDoc;
1588        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1589        return doc;
1590    }
1591    let doc = (*ctxt).myDoc;
1592    if !doc.is_null() && !URL.is_null() {
1593        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
1594    }
1595    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1596    doc
1597}
1598
1599/// Read an XML document from I/O callbacks.
1600///
1601/// # UPSTREAM-PARITY
1602///
1603/// ```c
1604/// xmlDocPtr xmlReadIO(xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
1605///                     void *ioctx, const char *URL, const char *encoding, int options);
1606/// ```
1607#[no_mangle]
1608pub unsafe extern "C" fn xmlReadIO(
1609    ioread: Option<xmlInputReadCallback>,
1610    ioclose: Option<xmlInputCloseCallback>,
1611    ioctx: *mut c_void,
1612    URL: *const c_char,
1613    encoding: *const c_char,
1614    options: c_int,
1615) -> *mut _xmlDoc {
1616    // SAFETY: callbacks must be valid function pointers if non-NULL.
1617    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1618    if ctxt.is_null() {
1619        return ptr::null_mut();
1620    }
1621    let input = crate::xml::parser::helpers::input_from_io(ioread, ioclose, ioctx);
1622    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1623    (*ctxt).options = options;
1624    if crate::xml::parser::helpers::parse_document(ctxt) != 0 {
1625        let doc = (*ctxt).myDoc;
1626        crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1627        return doc;
1628    }
1629    let doc = (*ctxt).myDoc;
1630    if !doc.is_null() && !URL.is_null() {
1631        (*doc).URL = crate::xml::string::xml_strdup(URL as *const xmlChar);
1632    }
1633    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1634    doc
1635}
1636
1637/// Parse an XML document (SAX1).
1638///
1639/// # UPSTREAM-PARITY
1640///
1641/// ```c
1642/// xmlDocPtr xmlSAXParseDoc(xmlSAXHandlerPtr sax, const xmlChar *cur, int recovery);
1643/// ```
1644#[no_mangle]
1645pub unsafe extern "C" fn xmlSAXParseDoc(
1646    sax: *mut _xmlSAXHandler,
1647    cur: *const xmlChar,
1648    recovery: c_int,
1649) -> *mut _xmlDoc {
1650    // SAFETY: cur must be a valid null-terminated xmlChar string.
1651    if cur.is_null() {
1652        return ptr::null_mut();
1653    }
1654    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1655    if ctxt.is_null() {
1656        return ptr::null_mut();
1657    }
1658    if !sax.is_null() {
1659        (*ctxt).sax = sax;
1660        (*ctxt).userData = (*ctxt).sax as *mut c_void;
1661    }
1662    if recovery != 0 {
1663        (*ctxt).recovery = 1;
1664        (*ctxt).options |= 1; // XML_PARSE_RECOVER
1665    }
1666    let len = crate::xml::string::xml_strlen(cur);
1667    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
1668    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1669    crate::xml::parser::helpers::parse_document(ctxt);
1670    let doc = (*ctxt).myDoc;
1671    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1672    doc
1673}
1674
1675/// Parse an XML file (SAX1).
1676///
1677/// # UPSTREAM-PARITY
1678///
1679/// ```c
1680/// xmlDocPtr xmlSAXParseFile(xmlSAXHandlerPtr sax, const char *filename, int recovery);
1681/// ```
1682#[no_mangle]
1683pub unsafe extern "C" fn xmlSAXParseFile(
1684    sax: *mut _xmlSAXHandler,
1685    filename: *const c_char,
1686    recovery: c_int,
1687) -> *mut _xmlDoc {
1688    // SAFETY: filename must be a valid C string.
1689    if filename.is_null() {
1690        return ptr::null_mut();
1691    }
1692    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1693    if ctxt.is_null() {
1694        return ptr::null_mut();
1695    }
1696    if !sax.is_null() {
1697        (*ctxt).sax = sax;
1698        (*ctxt).userData = (*ctxt).sax as *mut c_void;
1699    }
1700    if recovery != 0 {
1701        (*ctxt).recovery = 1;
1702        (*ctxt).options |= 1;
1703    }
1704    let input = match crate::xml::parser::helpers::input_from_file(filename) {
1705        Ok(input) => input,
1706        Err(_) => {
1707            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1708            return ptr::null_mut();
1709        }
1710    };
1711    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1712    crate::xml::parser::helpers::parse_document(ctxt);
1713    let doc = (*ctxt).myDoc;
1714    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1715    doc
1716}
1717
1718/// Parse an XML document from memory (SAX1).
1719///
1720/// # UPSTREAM-PARITY
1721///
1722/// ```c
1723/// xmlDocPtr xmlSAXParseMemory(xmlSAXHandlerPtr sax,
1724///                             const char *buffer, int size, int recovery);
1725/// ```
1726#[no_mangle]
1727pub unsafe extern "C" fn xmlSAXParseMemory(
1728    sax: *mut _xmlSAXHandler,
1729    buffer: *const c_char,
1730    size: c_int,
1731    recovery: c_int,
1732) -> *mut _xmlDoc {
1733    // SAFETY: buffer must be valid with at least `size` bytes.
1734    if buffer.is_null() || size <= 0 {
1735        return ptr::null_mut();
1736    }
1737    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1738    if ctxt.is_null() {
1739        return ptr::null_mut();
1740    }
1741    if !sax.is_null() {
1742        (*ctxt).sax = sax;
1743        (*ctxt).userData = (*ctxt).sax as *mut c_void;
1744    }
1745    if recovery != 0 {
1746        (*ctxt).recovery = 1;
1747        (*ctxt).options |= 1;
1748    }
1749    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
1750    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1751    crate::xml::parser::helpers::parse_document(ctxt);
1752    let doc = (*ctxt).myDoc;
1753    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1754    doc
1755}
1756
1757/// SAX user parse file.
1758///
1759/// # UPSTREAM-PARITY
1760///
1761/// ```c
1762/// int xmlSAXUserParseFile(xmlSAXHandlerPtr sax, void *user_data,
1763///                         const char *filename);
1764/// ```
1765#[no_mangle]
1766pub unsafe extern "C" fn xmlSAXUserParseFile(
1767    sax: *mut _xmlSAXHandler,
1768    user_data: *mut c_void,
1769    filename: *const c_char,
1770) -> c_int {
1771    // SAFETY: filename must be a valid C string. sax and user_data may be NULL.
1772    if filename.is_null() {
1773        return -1;
1774    }
1775    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1776    if ctxt.is_null() {
1777        return -1;
1778    }
1779    if !sax.is_null() {
1780        (*ctxt).sax = sax;
1781    }
1782    (*ctxt).userData = if !user_data.is_null() {
1783        user_data
1784    } else {
1785        ctxt as *mut c_void
1786    };
1787    let input = match crate::xml::parser::helpers::input_from_file(filename) {
1788        Ok(input) => input,
1789        Err(_) => {
1790            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1791            return -1;
1792        }
1793    };
1794    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1795    let ret = crate::xml::parser::helpers::parse_document(ctxt);
1796    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1797    ret
1798}
1799
1800/// SAX user parse memory.
1801///
1802/// # UPSTREAM-PARITY
1803///
1804/// ```c
1805/// int xmlSAXUserParseMemory(xmlSAXHandlerPtr sax, void *user_data,
1806///                           const char *buffer, int size);
1807/// ```
1808#[no_mangle]
1809pub unsafe extern "C" fn xmlSAXUserParseMemory(
1810    sax: *mut _xmlSAXHandler,
1811    user_data: *mut c_void,
1812    buffer: *const c_char,
1813    size: c_int,
1814) -> c_int {
1815    // SAFETY: buffer must be valid with at least `size` bytes.
1816    if buffer.is_null() || size <= 0 {
1817        return -1;
1818    }
1819    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1820    if ctxt.is_null() {
1821        return -1;
1822    }
1823    if !sax.is_null() {
1824        (*ctxt).sax = sax;
1825    }
1826    (*ctxt).userData = if !user_data.is_null() {
1827        user_data
1828    } else {
1829        ctxt as *mut c_void
1830    };
1831    let input = crate::xml::parser::helpers::input_from_memory(buffer, size);
1832    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1833    let ret = crate::xml::parser::helpers::parse_document(ctxt);
1834    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1835    ret
1836}
1837
1838/// Parse an XML document from a string (DOM).
1839///
1840/// # UPSTREAM-PARITY
1841///
1842/// ```c
1843/// xmlDocPtr xmlParseDoc(const xmlChar *cur);
1844/// ```
1845#[no_mangle]
1846pub unsafe extern "C" fn xmlParseDoc(cur: *const xmlChar) -> *mut _xmlDoc {
1847    // SAFETY: cur must be a valid null-terminated xmlChar string.
1848    if cur.is_null() {
1849        return ptr::null_mut();
1850    }
1851    xmlReadDoc(cur, ptr::null(), ptr::null(), 0)
1852}
1853
1854/// Parse an XML file (DOM).
1855///
1856/// # UPSTREAM-PARITY
1857///
1858/// ```c
1859/// xmlDocPtr xmlParseFile(const char *filename);
1860/// ```
1861#[no_mangle]
1862pub unsafe extern "C" fn xmlParseFile(filename: *const c_char) -> *mut _xmlDoc {
1863    // SAFETY: filename must be a valid C string.
1864    if filename.is_null() {
1865        return ptr::null_mut();
1866    }
1867    xmlReadFile(filename, ptr::null(), 0)
1868}
1869
1870/// Parse an XML document from memory (DOM).
1871///
1872/// # UPSTREAM-PARITY
1873///
1874/// ```c
1875/// xmlDocPtr xmlParseMemory(const char *buffer, int size);
1876/// ```
1877#[no_mangle]
1878pub unsafe extern "C" fn xmlParseMemory(buffer: *const c_char, size: c_int) -> *mut _xmlDoc {
1879    // SAFETY: buffer must be valid with at least `size` bytes.
1880    if buffer.is_null() || size <= 0 {
1881        return ptr::null_mut();
1882    }
1883    xmlReadMemory(buffer, size, ptr::null(), ptr::null(), 0)
1884}
1885
1886/// Create a file parser context.
1887///
1888/// # UPSTREAM-PARITY
1889///
1890/// ```c
1891/// xmlParserCtxtPtr xmlCreateFileParserCtxt(const char *filename);
1892/// ```
1893#[no_mangle]
1894pub unsafe extern "C" fn xmlCreateFileParserCtxt(filename: *const c_char) -> *mut _xmlParserCtxt {
1895    // SAFETY: filename must be a valid C string.
1896    if filename.is_null() {
1897        return ptr::null_mut();
1898    }
1899    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1900    if ctxt.is_null() {
1901        return ptr::null_mut();
1902    }
1903    let input = match crate::xml::parser::helpers::input_from_file(filename) {
1904        Ok(input) => input,
1905        Err(_) => {
1906            crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1907            return ptr::null_mut();
1908        }
1909    };
1910    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1911    ctxt
1912}
1913
1914/// Create a document parser context.
1915///
1916/// # UPSTREAM-PARITY
1917///
1918/// ```c
1919/// xmlParserCtxtPtr xmlCreateDocParserCtxt(const xmlChar *cur);
1920/// ```
1921#[no_mangle]
1922pub unsafe extern "C" fn xmlCreateDocParserCtxt(cur: *const xmlChar) -> *mut _xmlParserCtxt {
1923    // SAFETY: cur must be a valid null-terminated xmlChar string.
1924    if cur.is_null() {
1925        return ptr::null_mut();
1926    }
1927    let ctxt = crate::xml::parser::helpers::create_parser_ctxt();
1928    if ctxt.is_null() {
1929        return ptr::null_mut();
1930    }
1931    let len = crate::xml::string::xml_strlen(cur);
1932    let input = crate::xml::parser::helpers::input_from_memory(cur as *const c_char, len as c_int);
1933    crate::xml::parser::helpers::setup_parser_input(ctxt, input);
1934    ctxt
1935}
1936
1937/// Parse a document using an existing parser context.
1938///
1939/// # UPSTREAM-PARITY
1940///
1941/// ```c
1942/// int xmlParseDocument(xmlParserCtxtPtr ctxt);
1943/// ```
1944#[no_mangle]
1945pub unsafe extern "C" fn xmlParseDocument(ctxt: *mut _xmlParserCtxt) -> c_int {
1946    // SAFETY: ctxt must be a valid parser context.
1947    if ctxt.is_null() {
1948        return -1;
1949    }
1950    crate::xml::parser::helpers::parse_document(ctxt)
1951}
1952
1953/// Free a parser context.
1954///
1955/// # UPSTREAM-PARITY
1956///
1957/// ```c
1958/// void xmlFreeParserCtxt(xmlParserCtxtPtr ctxt);
1959/// ```
1960#[no_mangle]
1961pub unsafe extern "C" fn xmlFreeParserCtxt(ctxt: *mut _xmlParserCtxt) {
1962    if ctxt.is_null() {
1963        return;
1964    }
1965    crate::xml::parser::helpers::free_parser_ctxt(ctxt);
1966}
1967
1968/// Set parser options.
1969///
1970/// # UPSTREAM-PARITY
1971///
1972/// ```c
1973/// int xmlCtxtUseOptions(xmlParserCtxtPtr ctxt, int options);
1974/// ```
1975#[no_mangle]
1976pub unsafe extern "C" fn xmlCtxtUseOptions(ctxt: *mut _xmlParserCtxt, options: c_int) -> c_int {
1977    if ctxt.is_null() {
1978        return -1;
1979    }
1980    // Phase 1: STUB
1981    unsafe {
1982        (*ctxt).options = options;
1983    }
1984    0
1985}
1986
1987/// Parse a well-balanced chunk (for push parsing).
1988///
1989/// # UPSTREAM-PARITY
1990///
1991/// ```c
1992/// xmlParserErrors xmlParseChunk(xmlParserCtxtPtr ctxt,
1993///                               const char *chunk, int size, int terminate);
1994/// ```
1995#[no_mangle]
1996pub unsafe extern "C" fn xmlParseChunk(
1997    ctxt: *mut _xmlParserCtxt,
1998    chunk: *const c_char,
1999    size: c_int,
2000    terminate: c_int,
2001) -> c_int {
2002    // SAFETY: ctxt must be a valid parser context.
2003    // chunk may be NULL if terminate is set (finalize without data).
2004    if ctxt.is_null() {
2005        return -1;
2006    }
2007    crate::xml::parser::helpers::parse_chunk(ctxt, chunk, size, terminate)
2008}
2009
2010/// Create a memory parser input buffer.
2011///
2012/// # UPSTREAM-PARITY
2013///
2014/// ```c
2015/// xmlParserInputBufferPtr xmlParserInputBufferCreateMem(const char *buffer, int size, int enc);
2016/// ```
2017#[no_mangle]
2018pub unsafe extern "C" fn xmlParserInputBufferCreateMem(
2019    buffer: *const c_char,
2020    size: c_int,
2021    enc: c_int,
2022) -> *mut _xmlParserInputBuffer {
2023    // SAFETY: buffer must be valid with at least `size` bytes.
2024    if buffer.is_null() || size <= 0 {
2025        return ptr::null_mut();
2026    }
2027    crate::xml::parser::helpers::alloc_parser_input_buffer()
2028}
2029
2030/// Create a file parser input buffer.
2031///
2032/// # UPSTREAM-PARITY
2033///
2034/// ```c
2035/// xmlParserInputBufferPtr xmlParserInputBufferCreateFilename(const char *URI, int enc);
2036/// ```
2037#[no_mangle]
2038pub unsafe extern "C" fn xmlParserInputBufferCreateFilename(
2039    URI: *const c_char,
2040    enc: c_int,
2041) -> *mut _xmlParserInputBuffer {
2042    // SAFETY: URI must be a valid C string or NULL.
2043    if URI.is_null() {
2044        return ptr::null_mut();
2045    }
2046    crate::xml::parser::helpers::alloc_parser_input_buffer()
2047}
2048
2049/// Create an I/O parser input buffer.
2050///
2051/// # UPSTREAM-PARITY
2052///
2053/// ```c
2054/// xmlParserInputBufferPtr xmlParserInputBufferCreateIO(
2055///     xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
2056///     void *ioctx, int enc);
2057/// ```
2058#[no_mangle]
2059pub unsafe extern "C" fn xmlParserInputBufferCreateIO(
2060    ioread: Option<xmlInputReadCallback>,
2061    ioclose: Option<xmlInputCloseCallback>,
2062    ioctx: *mut c_void,
2063    enc: c_int,
2064) -> *mut _xmlParserInputBuffer {
2065    // SAFETY: ioread must be a valid callback if Some. ioctx may be NULL.
2066    let buf = crate::xml::parser::helpers::alloc_parser_input_buffer();
2067    if !buf.is_null() {
2068        (*buf).readcallback = ioread;
2069        (*buf).closecallback = ioclose;
2070        (*buf).context = ioctx;
2071    }
2072    buf
2073}
2074
2075/// Free a parser input buffer.
2076///
2077/// # UPSTREAM-PARITY
2078///
2079/// ```c
2080/// void xmlFreeParserInputBuffer(xmlParserInputBufferPtr buf);
2081/// ```
2082#[no_mangle]
2083pub unsafe extern "C" fn xmlFreeParserInputBuffer(buf: *mut _xmlParserInputBuffer) {
2084    if buf.is_null() {
2085        return;
2086    }
2087    crate::xml::parser::helpers::free_parser_input_buffer(buf);
2088}
2089
2090/// Create a new parser input.
2091///
2092/// # UPSTREAM-PARITY
2093///
2094/// ```c
2095/// xmlParserInputPtr xmlNewInputFromFile(xmlParserCtxtPtr ctxt, const char *filename);
2096/// ```
2097#[no_mangle]
2098pub unsafe extern "C" fn xmlNewInputFromFile(
2099    ctxt: *mut _xmlParserCtxt,
2100    filename: *const c_char,
2101) -> *mut _xmlParserInput {
2102    // SAFETY: filename must be a valid C string. ctxt may be NULL.
2103    // This function allocates a _xmlParserInput. The caller owns it.
2104    // Note: The InputBuffer backing data is NOT leaked here (no ctxt._private
2105    // to store it). Use xmlCreateFileParserCtxt + xmlParseDocument instead.
2106    if filename.is_null() {
2107        return ptr::null_mut();
2108    }
2109    crate::xml::parser::helpers::alloc_parser_input_buffer() as *mut _xmlParserInput
2110}
2111
2112/// Free a parser input.
2113///
2114/// # UPSTREAM-PARITY
2115///
2116/// ```c
2117/// void xmlFreeInputStream(xmlParserInputPtr input);
2118/// ```
2119#[no_mangle]
2120pub unsafe extern "C" fn xmlFreeInputStream(input: *mut _xmlParserInput) {
2121    if input.is_null() {
2122        return;
2123    }
2124    crate::xml::parser::helpers::free_parser_input(input);
2125}
2126
2127// ═══════════════════════════════════════════════════════════════════════════════
2128// 8. I/O
2129// ═══════════════════════════════════════════════════════════════════════════════
2130
2131/// Create an output buffer for a file.
2132///
2133/// # UPSTREAM-PARITY
2134///
2135/// ```c
2136/// xmlOutputBufferPtr xmlOutputBufferCreateFilename(const char *URI,
2137///                                                  xmlCharEncodingHandlerPtr encoder,
2138///                                                  int compression);
2139/// ```
2140#[no_mangle]
2141pub unsafe extern "C" fn xmlOutputBufferCreateFilename(
2142    URI: *const c_char,
2143    encoder: *mut c_void,
2144    compression: c_int,
2145) -> *mut _xmlOutputBuffer {
2146    let _ = compression;
2147    if URI.is_null() {
2148        return ptr::null_mut();
2149    }
2150    crate::xml::io::output_buffer_create_filename(
2151        URI,
2152        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
2153        0,
2154    )
2155}
2156
2157/// Create an output buffer for a file descriptor.
2158///
2159/// # UPSTREAM-PARITY
2160///
2161/// ```c
2162/// xmlOutputBufferPtr xmlOutputBufferCreateFd(int fd,
2163///                                            xmlCharEncodingHandlerPtr encoder);
2164/// ```
2165#[no_mangle]
2166pub unsafe extern "C" fn xmlOutputBufferCreateFd(
2167    fd: c_int,
2168    encoder: *mut c_void,
2169) -> *mut _xmlOutputBuffer {
2170    if fd < 0 {
2171        return ptr::null_mut();
2172    }
2173    crate::xml::io::output_buffer_create_fd(
2174        fd,
2175        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
2176    )
2177}
2178
2179/// Create an output buffer from I/O callbacks.
2180///
2181/// # UPSTREAM-PARITY
2182///
2183/// ```c
2184/// xmlOutputBufferPtr xmlOutputBufferCreateIO(
2185///     xmlOutputWriteCallback iowrite, xmlOutputCloseCallback ioclose,
2186///     void *ioctx, xmlCharEncodingHandlerPtr encoder);
2187/// ```
2188#[no_mangle]
2189pub unsafe extern "C" fn xmlOutputBufferCreateIO(
2190    iowrite: Option<xmlOutputWriteCallback>,
2191    ioclose: Option<xmlOutputCloseCallback>,
2192    ioctx: *mut c_void,
2193    encoder: *mut c_void,
2194) -> *mut _xmlOutputBuffer {
2195    crate::xml::io::output_buffer_create_io(
2196        iowrite,
2197        ioclose,
2198        ioctx,
2199        encoder as *mut crate::abi::structs::_xmlCharEncodingHandler,
2200    )
2201}
2202
2203/// Free an output buffer.
2204///
2205/// # UPSTREAM-PARITY
2206///
2207/// ```c
2208/// void xmlOutputBufferClose(xmlOutputBufferPtr out);
2209/// ```
2210#[no_mangle]
2211pub unsafe extern "C" fn xmlOutputBufferClose(out: *mut _xmlOutputBuffer) -> c_int {
2212    if out.is_null() {
2213        return -1;
2214    }
2215    crate::xml::io::output_buffer_close(out)
2216}
2217
2218/// Flush an output buffer.
2219///
2220/// # UPSTREAM-PARITY
2221///
2222/// ```c
2223/// int xmlOutputBufferFlush(xmlOutputBufferPtr out);
2224/// ```
2225#[no_mangle]
2226pub unsafe extern "C" fn xmlOutputBufferFlush(out: *mut _xmlOutputBuffer) -> c_int {
2227    if out.is_null() {
2228        return -1;
2229    }
2230    crate::xml::io::output_buffer_flush(out)
2231}
2232
2233/// Write to an output buffer.
2234///
2235/// # UPSTREAM-PARITY
2236///
2237/// ```c
2238/// int xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *data);
2239/// ```
2240#[no_mangle]
2241pub unsafe extern "C" fn xmlOutputBufferWrite(
2242    out: *mut _xmlOutputBuffer,
2243    len: c_int,
2244    data: *const c_char,
2245) -> c_int {
2246    if out.is_null() || data.is_null() || len <= 0 {
2247        return -1;
2248    }
2249    crate::xml::io::output_buffer_write(out, len, data)
2250}
2251
2252/// Write a string to an output buffer.
2253///
2254/// # UPSTREAM-PARITY
2255///
2256/// ```c
2257/// int xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str);
2258/// ```
2259#[no_mangle]
2260pub unsafe extern "C" fn xmlOutputBufferWriteString(
2261    out: *mut _xmlOutputBuffer,
2262    str: *const c_char,
2263) -> c_int {
2264    if str.is_null() {
2265        return 0;
2266    }
2267    unsafe { xmlOutputBufferWrite(out, xmlStrlen(str as *const xmlChar), str) }
2268}
2269
2270// ═══════════════════════════════════════════════════════════════════════════════
2271// 9. Dictionary
2272// ═══════════════════════════════════════════════════════════════════════════════
2273
2274/// Create a new dictionary.
2275///
2276/// # UPSTREAM-PARITY
2277///
2278/// ```c
2279/// xmlDictPtr xmlDictCreate(void);
2280/// ```
2281#[no_mangle]
2282pub extern "C" fn xmlDictCreate() -> *mut c_void {
2283    // Phase 1: STUB — will be implemented in xml/dictionary module.
2284    ptr::null_mut()
2285}
2286
2287/// Create a sub-dictionary.
2288///
2289/// # UPSTREAM-PARITY
2290///
2291/// ```c
2292/// xmlDictPtr xmlDictCreateSub(xmlDictPtr sub);
2293/// ```
2294#[no_mangle]
2295pub extern "C" fn xmlDictCreateSub(_sub: *mut c_void) -> *mut c_void {
2296    // Phase 1: STUB
2297    ptr::null_mut()
2298}
2299
2300/// Look up a string in the dictionary.
2301///
2302/// # UPSTREAM-PARITY
2303///
2304/// ```c
2305/// const xmlChar *xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len);
2306/// ```
2307///
2308/// Returns an interned string pointer (valid as long as the dictionary exists).
2309/// - If `len` < 0, `name` must be null-terminated.
2310/// - If `len` >= 0, exactly `len` bytes are used.
2311#[no_mangle]
2312pub unsafe extern "C" fn xmlDictLookup(
2313    dict: *mut c_void,
2314    name: *const xmlChar,
2315    len: c_int,
2316) -> *const xmlChar {
2317    // Phase 1: STUB
2318    name
2319}
2320
2321/// Check if a string exists in the dictionary.
2322///
2323/// # UPSTREAM-PARITY
2324///
2325/// ```c
2326/// const xmlChar *xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len);
2327/// ```
2328#[no_mangle]
2329pub unsafe extern "C" fn xmlDictExists(
2330    dict: *mut c_void,
2331    name: *const xmlChar,
2332    len: c_int,
2333) -> *const xmlChar {
2334    // Phase 1: STUB
2335    ptr::null()
2336}
2337
2338/// Query dictionary size.
2339///
2340/// # UPSTREAM-PARITY
2341///
2342/// ```c
2343/// unsigned int xmlDictSize(const xmlDictPtr dict);
2344/// ```
2345#[no_mangle]
2346pub extern "C" fn xmlDictSize(dict: *const c_void) -> c_uint {
2347    // Phase 1: STUB
2348    0
2349}
2350
2351/// Free a dictionary.
2352///
2353/// # UPSTREAM-PARITY
2354///
2355/// ```c
2356/// void xmlDictFree(xmlDictPtr dict);
2357/// ```
2358#[no_mangle]
2359pub extern "C" fn xmlDictFree(_dict: *mut c_void) {
2360    // Phase 1: STUB
2361}
2362
2363/// Set the dictionary size limit.
2364///
2365/// # UPSTREAM-PARITY
2366///
2367/// ```c
2368/// unsigned int xmlDictSetLimit(xmlDictPtr dict, unsigned int limit);
2369/// ```
2370#[no_mangle]
2371pub extern "C" fn xmlDictSetLimit(_dict: *mut c_void, _limit: c_uint) -> c_uint {
2372    // Phase 1: STUB
2373    0
2374}
2375
2376/// Get current dictionary usage.
2377///
2378/// # UPSTREAM-PARITY
2379///
2380/// ```c
2381/// unsigned int xmlDictGetUsage(const xmlDictPtr dict);
2382/// ```
2383#[no_mangle]
2384pub extern "C" fn xmlDictGetUsage(_dict: *const c_void) -> c_uint {
2385    // Phase 1: STUB
2386    0
2387}
2388
2389// ═══════════════════════════════════════════════════════════════════════════════
2390// 10. Hash Table
2391// ═══════════════════════════════════════════════════════════════════════════════
2392
2393/// Create a new hash table.
2394///
2395/// # UPSTREAM-PARITY
2396///
2397/// ```c
2398/// xmlHashTablePtr xmlHashCreate(int size);
2399/// ```
2400#[no_mangle]
2401pub extern "C" fn xmlHashCreate(_size: c_int) -> *mut c_void {
2402    // Phase 1: STUB
2403    ptr::null_mut()
2404}
2405
2406/// Create a new hash table with a dictionary.
2407///
2408/// # UPSTREAM-PARITY
2409///
2410/// ```c
2411/// xmlHashTablePtr xmlHashCreateDict(int size, xmlDictPtr dict);
2412/// ```
2413#[no_mangle]
2414pub extern "C" fn xmlHashCreateDict(_size: c_int, _dict: *mut c_void) -> *mut c_void {
2415    // Phase 1: STUB
2416    ptr::null_mut()
2417}
2418
2419/// Free a hash table.
2420///
2421/// # UPSTREAM-PARITY
2422///
2423/// ```c
2424/// void xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f);
2425/// ```
2426#[no_mangle]
2427pub extern "C" fn xmlHashFree(
2428    _table: *mut c_void,
2429    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2430) {
2431    // Phase 1: STUB
2432}
2433
2434/// Add an entry to a hash table.
2435///
2436/// # UPSTREAM-PARITY
2437///
2438/// ```c
2439/// int xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata);
2440/// ```
2441#[no_mangle]
2442pub unsafe extern "C" fn xmlHashAddEntry(
2443    _table: *mut c_void,
2444    _name: *const xmlChar,
2445    _userdata: *mut c_void,
2446) -> c_int {
2447    // Phase 1: STUB
2448    0
2449}
2450
2451/// Add a 2-key entry.
2452///
2453/// # UPSTREAM-PARITY
2454///
2455/// ```c
2456/// int xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
2457///                      const xmlChar *name2, void *userdata);
2458/// ```
2459#[no_mangle]
2460pub unsafe extern "C" fn xmlHashAddEntry2(
2461    _table: *mut c_void,
2462    _name: *const xmlChar,
2463    _name2: *const xmlChar,
2464    _userdata: *mut c_void,
2465) -> c_int {
2466    // Phase 1: STUB
2467    0
2468}
2469
2470/// Add a 3-key entry.
2471///
2472/// # UPSTREAM-PARITY
2473///
2474/// ```c
2475/// int xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
2476///                      const xmlChar *name2, const xmlChar *name3, void *userdata);
2477/// ```
2478#[no_mangle]
2479pub unsafe extern "C" fn xmlHashAddEntry3(
2480    _table: *mut c_void,
2481    _name: *const xmlChar,
2482    _name2: *const xmlChar,
2483    _name3: *const xmlChar,
2484    _userdata: *mut c_void,
2485) -> c_int {
2486    // Phase 1: STUB
2487    0
2488}
2489
2490/// Update or add an entry.
2491///
2492/// # UPSTREAM-PARITY
2493///
2494/// ```c
2495/// int xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
2496///                        void *userdata, xmlHashDeallocator f);
2497/// ```
2498#[no_mangle]
2499pub unsafe extern "C" fn xmlHashUpdateEntry(
2500    _table: *mut c_void,
2501    _name: *const xmlChar,
2502    _userdata: *mut c_void,
2503    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2504) -> c_int {
2505    // Phase 1: STUB
2506    0
2507}
2508
2509/// Update or add a 2-key entry.
2510#[no_mangle]
2511pub unsafe extern "C" fn xmlHashUpdateEntry2(
2512    _table: *mut c_void,
2513    _name: *const xmlChar,
2514    _name2: *const xmlChar,
2515    _userdata: *mut c_void,
2516    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2517) -> c_int {
2518    // Phase 1: STUB
2519    0
2520}
2521
2522/// Update or add a 3-key entry.
2523#[no_mangle]
2524pub unsafe extern "C" fn xmlHashUpdateEntry3(
2525    _table: *mut c_void,
2526    _name: *const xmlChar,
2527    _name2: *const xmlChar,
2528    _name3: *const xmlChar,
2529    _userdata: *mut c_void,
2530    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2531) -> c_int {
2532    // Phase 1: STUB
2533    0
2534}
2535
2536/// Look up an entry.
2537///
2538/// # UPSTREAM-PARITY
2539///
2540/// ```c
2541/// void *xmlHashLookup(xmlHashTablePtr table, const xmlChar *name);
2542/// ```
2543#[no_mangle]
2544pub unsafe extern "C" fn xmlHashLookup(_table: *mut c_void, _name: *const xmlChar) -> *mut c_void {
2545    // Phase 1: STUB
2546    ptr::null_mut()
2547}
2548
2549/// Look up a 2-key entry.
2550#[no_mangle]
2551pub unsafe extern "C" fn xmlHashLookup2(
2552    _table: *mut c_void,
2553    _name: *const xmlChar,
2554    _name2: *const xmlChar,
2555) -> *mut c_void {
2556    // Phase 1: STUB
2557    ptr::null_mut()
2558}
2559
2560/// Look up a 3-key entry.
2561#[no_mangle]
2562pub unsafe extern "C" fn xmlHashLookup3(
2563    _table: *mut c_void,
2564    _name: *const xmlChar,
2565    _name2: *const xmlChar,
2566    _name3: *const xmlChar,
2567) -> *mut c_void {
2568    // Phase 1: STUB
2569    ptr::null_mut()
2570}
2571
2572/// Get the size of a hash table.
2573///
2574/// # UPSTREAM-PARITY
2575///
2576/// ```c
2577/// int xmlHashSize(xmlHashTablePtr table);
2578/// ```
2579#[no_mangle]
2580pub extern "C" fn xmlHashSize(_table: *mut c_void) -> c_int {
2581    // Phase 1: STUB
2582    0
2583}
2584
2585/// Remove an entry.
2586///
2587/// # UPSTREAM-PARITY
2588///
2589/// ```c
2590/// int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
2591///                        xmlHashDeallocator f);
2592/// ```
2593#[no_mangle]
2594pub unsafe extern "C" fn xmlHashRemoveEntry(
2595    _table: *mut c_void,
2596    _name: *const xmlChar,
2597    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2598) -> c_int {
2599    // Phase 1: STUB
2600    0
2601}
2602
2603/// Remove a 2-key entry.
2604#[no_mangle]
2605pub unsafe extern "C" fn xmlHashRemoveEntry2(
2606    _table: *mut c_void,
2607    _name: *const xmlChar,
2608    _name2: *const xmlChar,
2609    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2610) -> c_int {
2611    // Phase 1: STUB
2612    0
2613}
2614
2615/// Remove a 3-key entry.
2616#[no_mangle]
2617pub unsafe extern "C" fn xmlHashRemoveEntry3(
2618    _table: *mut c_void,
2619    _name: *const xmlChar,
2620    _name2: *const xmlChar,
2621    _name3: *const xmlChar,
2622    _f: Option<unsafe extern "C" fn(*mut c_void, *mut xmlChar)>,
2623) -> c_int {
2624    // Phase 1: STUB
2625    0
2626}
2627
2628/// Scan a hash table with a scanner function.
2629///
2630/// # UPSTREAM-PARITY
2631///
2632/// ```c
2633/// void xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data);
2634/// ```
2635#[no_mangle]
2636pub extern "C" fn xmlHashScan(
2637    _table: *mut c_void,
2638    _f: Option<unsafe extern "C" fn(*mut c_void, *const xmlChar, *mut c_void)>,
2639    _data: *mut c_void,
2640) {
2641    // Phase 1: STUB
2642}
2643
2644/// Scan a hash table with a full scanner function.
2645#[no_mangle]
2646pub extern "C" fn xmlHashScanFull(
2647    _table: *mut c_void,
2648    _f: Option<unsafe extern "C" fn(*mut c_void, *const xmlChar, *mut c_void, *mut c_void)>,
2649    _data: *mut c_void,
2650) {
2651    // Phase 1: STUB
2652}
2653
2654/// Copy a hash table.
2655///
2656/// # UPSTREAM-PARITY
2657///
2658/// ```c
2659/// xmlHashTablePtr xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f);
2660/// ```
2661#[no_mangle]
2662pub extern "C" fn xmlHashCopy(
2663    _table: *mut c_void,
2664    _f: Option<unsafe extern "C" fn(*mut c_void, *const xmlChar) -> *mut c_void>,
2665) -> *mut c_void {
2666    // Phase 1: STUB
2667    ptr::null_mut()
2668}
2669
2670// ═══════════════════════════════════════════════════════════════════════════════
2671// 11. List
2672// ═══════════════════════════════════════════════════════════════════════════════
2673
2674/// Create a new list.
2675///
2676/// # UPSTREAM-PARITY
2677///
2678/// ```c
2679/// xmlListPtr xmlListCreate(xmlListDeallocator deallocator,
2680///                          xmlListDataCompare compare);
2681/// ```
2682#[no_mangle]
2683pub extern "C" fn xmlListCreate(
2684    _deallocator: Option<unsafe extern "C" fn(*mut c_void)>,
2685    _compare: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
2686) -> *mut c_void {
2687    // Phase 1: STUB
2688    ptr::null_mut()
2689}
2690
2691/// Delete a list.
2692///
2693/// # UPSTREAM-PARITY
2694///
2695/// ```c
2696/// void xmlListDelete(xmlListPtr list);
2697/// ```
2698#[no_mangle]
2699pub extern "C" fn xmlListDelete(_list: *mut c_void) {
2700    // Phase 1: STUB
2701}
2702
2703/// Search a list.
2704///
2705/// # UPSTREAM-PARITY
2706///
2707/// ```c
2708/// void *xmlListSearch(xmlListPtr list, void *data);
2709/// ```
2710#[no_mangle]
2711pub extern "C" fn xmlListSearch(_list: *mut c_void, _data: *mut c_void) -> *mut c_void {
2712    // Phase 1: STUB
2713    ptr::null_mut()
2714}
2715
2716/// Walk a list.
2717///
2718/// # UPSTREAM-PARITY
2719///
2720/// ```c
2721/// void xmlListWalk(xmlListPtr list, xmlListWalker walker, void *data);
2722/// ```
2723#[no_mangle]
2724pub extern "C" fn xmlListWalk(
2725    _list: *mut c_void,
2726    _walker: Option<unsafe extern "C" fn(*mut c_void, *mut c_void) -> c_int>,
2727    _data: *mut c_void,
2728) {
2729    // Phase 1: STUB
2730}
2731
2732/// Push to back.
2733///
2734/// # UPSTREAM-PARITY
2735///
2736/// ```c
2737/// int xmlListPushBack(xmlListPtr list, void *data);
2738/// ```
2739#[no_mangle]
2740pub extern "C" fn xmlListPushBack(_list: *mut c_void, _data: *mut c_void) -> c_int {
2741    // Phase 1: STUB
2742    0
2743}
2744
2745/// Push to front.
2746///
2747/// # UPSTREAM-PARITY
2748///
2749/// ```c
2750/// int xmlListPushFront(xmlListPtr list, void *data);
2751/// ```
2752#[no_mangle]
2753pub extern "C" fn xmlListPushFront(_list: *mut c_void, _data: *mut c_void) -> c_int {
2754    // Phase 1: STUB
2755    0
2756}
2757
2758/// Pop from back.
2759#[no_mangle]
2760pub extern "C" fn xmlListPopBack(_list: *mut c_void) {
2761    // Phase 1: STUB
2762}
2763
2764/// Pop from front.
2765#[no_mangle]
2766pub extern "C" fn xmlListPopFront(_list: *mut c_void) {
2767    // Phase 1: STUB
2768}
2769
2770/// Insert into sorted list.
2771///
2772/// # UPSTREAM-PARITY
2773///
2774/// ```c
2775/// int xmlListInsert(xmlListPtr list, void *data);
2776/// ```
2777#[no_mangle]
2778pub extern "C" fn xmlListInsert(_list: *mut c_void, _data: *mut c_void) -> c_int {
2779    // Phase 1: STUB
2780    0
2781}
2782
2783/// Append to list.
2784#[no_mangle]
2785pub extern "C" fn xmlListAppend(_list: *mut c_void, _data: *mut c_void) -> c_int {
2786    // Phase 1: STUB
2787    0
2788}
2789
2790/// Remove first matching element.
2791#[no_mangle]
2792pub extern "C" fn xmlListRemoveFirst(_list: *mut c_void, _data: *mut c_void) -> c_int {
2793    // Phase 1: STUB
2794    0
2795}
2796
2797/// Remove last matching element.
2798#[no_mangle]
2799pub extern "C" fn xmlListRemoveLast(_list: *mut c_void, _data: *mut c_void) -> c_int {
2800    // Phase 1: STUB
2801    0
2802}
2803
2804/// Remove all matching elements.
2805#[no_mangle]
2806pub extern "C" fn xmlListRemoveAll(_list: *mut c_void, _data: *mut c_void) -> c_int {
2807    // Phase 1: STUB
2808    0
2809}
2810
2811/// Clear a list.
2812#[no_mangle]
2813pub extern "C" fn xmlListClear(_list: *mut c_void) {
2814    // Phase 1: STUB
2815}
2816
2817/// Check if list is empty.
2818///
2819/// # UPSTREAM-PARITY
2820///
2821/// ```c
2822/// int xmlListEmpty(xmlListPtr list);
2823/// ```
2824#[no_mangle]
2825pub extern "C" fn xmlListEmpty(_list: *mut c_void) -> c_int {
2826    // Phase 1: STUB
2827    1
2828}
2829
2830/// Get front element.
2831///
2832/// # UPSTREAM-PARITY
2833///
2834/// ```c
2835/// void *xmlListFront(xmlListPtr list);
2836/// ```
2837#[no_mangle]
2838pub extern "C" fn xmlListFront(_list: *mut c_void) -> *mut c_void {
2839    // Phase 1: STUB
2840    ptr::null_mut()
2841}
2842
2843/// Get back element.
2844///
2845/// # UPSTREAM-PARITY
2846///
2847/// ```c
2848/// void *xmlListBack(xmlListPtr list);
2849/// ```
2850#[no_mangle]
2851pub extern "C" fn xmlListBack(_list: *mut c_void) -> *mut c_void {
2852    // Phase 1: STUB
2853    ptr::null_mut()
2854}
2855
2856/// Get list size.
2857///
2858/// # UPSTREAM-PARITY
2859///
2860/// ```c
2861/// int xmlListSize(xmlListPtr list);
2862/// ```
2863#[no_mangle]
2864pub extern "C" fn xmlListSize(_list: *mut c_void) -> c_int {
2865    // Phase 1: STUB
2866    0
2867}
2868
2869/// Sort a list.
2870#[no_mangle]
2871pub extern "C" fn xmlListSort(_list: *mut c_void) {
2872    // Phase 1: STUB
2873}
2874
2875/// Reverse a list.
2876#[no_mangle]
2877pub extern "C" fn xmlListReverse(_list: *mut c_void) {
2878    // Phase 1: STUB
2879}
2880
2881/// Reverse a list in-place.
2882#[no_mangle]
2883pub extern "C" fn xmlListReverseSplice(_list: *mut c_void, _list2: *mut c_void) {
2884    // Phase 1: STUB
2885}
2886
2887/// Merge two sorted lists.
2888#[no_mangle]
2889pub extern "C" fn xmlListMerge(_list: *mut c_void, _list2: *mut c_void) {
2890    // Phase 1: STUB
2891}
2892
2893// ═══════════════════════════════════════════════════════════════════════════════
2894// 12. Buffer
2895// ═══════════════════════════════════════════════════════════════════════════════
2896
2897/// Create a new buffer.
2898///
2899/// # UPSTREAM-PARITY
2900///
2901/// ```c
2902/// xmlBufferPtr xmlBufferCreate(void);
2903/// ```
2904#[no_mangle]
2905pub extern "C" fn xmlBufferCreate() -> *mut _xmlBuffer {
2906    crate::xml::io::buf_create(-1)
2907}
2908
2909/// Create a new buffer of a given size.
2910///
2911/// # UPSTREAM-PARITY
2912///
2913/// ```c
2914/// xmlBufferPtr xmlBufferCreateSize(size_t size);
2915/// ```
2916#[no_mangle]
2917pub extern "C" fn xmlBufferCreateSize(size: usize) -> *mut _xmlBuffer {
2918    crate::xml::io::buf_create(size as c_int)
2919}
2920
2921/// Create a buffer from a static string.
2922///
2923/// # UPSTREAM-PARITY
2924///
2925/// ```c
2926/// xmlBufferPtr xmlBufferCreateStatic(void *mem, size_t size);
2927/// ```
2928#[no_mangle]
2929pub extern "C" fn xmlBufferCreateStatic(mem: *mut c_void, size: usize) -> *mut _xmlBuffer {
2930    if mem.is_null() || size == 0 {
2931        return ptr::null_mut();
2932    }
2933    crate::xml::io::buf_create_static(mem as *const xmlChar, size as c_int)
2934}
2935
2936/// Free a buffer.
2937///
2938/// # UPSTREAM-PARITY
2939///
2940/// ```c
2941/// void xmlBufferFree(xmlBufferPtr buf);
2942/// ```
2943#[no_mangle]
2944pub extern "C" fn xmlBufferFree(buf: *mut _xmlBuffer) {
2945    crate::xml::io::buf_free(buf)
2946}
2947
2948/// Empty a buffer.
2949///
2950/// # UPSTREAM-PARITY
2951///
2952/// ```c
2953/// void xmlBufferEmpty(xmlBufferPtr buf);
2954/// ```
2955#[no_mangle]
2956pub extern "C" fn xmlBufferEmpty(buf: *mut _xmlBuffer) {
2957    if buf.is_null() {
2958        return;
2959    }
2960    unsafe {
2961        if !(*buf).content.is_null() {
2962            *(*buf).content = 0;
2963        }
2964        (*buf).use_ = 0;
2965    }
2966}
2967
2968/// Get buffer content.
2969///
2970/// # UPSTREAM-PARITY
2971///
2972/// ```c
2973/// xmlChar *xmlBufferContent(const xmlBuffer *buf);
2974/// ```
2975#[no_mangle]
2976pub extern "C" fn xmlBufferContent(buf: *const _xmlBuffer) -> *mut xmlChar {
2977    crate::xml::io::buf_content(buf as *mut _xmlBuffer)
2978}
2979
2980/// Get buffer length.
2981///
2982/// # UPSTREAM-PARITY
2983///
2984/// ```c
2985/// int xmlBufferLength(const xmlBuffer *buf);
2986/// ```
2987#[no_mangle]
2988pub extern "C" fn xmlBufferLength(buf: *const _xmlBuffer) -> c_int {
2989    crate::xml::io::buf_length(buf as *mut _xmlBuffer)
2990}
2991
2992/// Write to a buffer.
2993///
2994/// # UPSTREAM-PARITY
2995///
2996/// ```c
2997/// int xmlBufferAdd(xmlBufferPtr buf, const xmlChar *str, int len);
2998/// ```
2999#[no_mangle]
3000pub unsafe extern "C" fn xmlBufferAdd(
3001    buf: *mut _xmlBuffer,
3002    str: *const xmlChar,
3003    len: c_int,
3004) -> c_int {
3005    crate::xml::io::buf_add(buf, str, len)
3006}
3007
3008/// Write to a buffer at a position.
3009///
3010/// # UPSTREAM-PARITY
3011///
3012/// ```c
3013/// int xmlBufferAddHead(xmlBufferPtr buf, const xmlChar *str, int len);
3014/// ```
3015#[no_mangle]
3016pub unsafe extern "C" fn xmlBufferAddHead(
3017    buf: *mut _xmlBuffer,
3018    str: *const xmlChar,
3019    len: c_int,
3020) -> c_int {
3021    crate::xml::io::buf_add_head(buf, str, len)
3022}
3023
3024/// Write a C string to a buffer.
3025///
3026/// # UPSTREAM-PARITY
3027///
3028/// ```c
3029/// int xmlBufferCat(xmlBufferPtr buf, const xmlChar *str);
3030/// ```
3031#[no_mangle]
3032pub unsafe extern "C" fn xmlBufferCat(buf: *mut _xmlBuffer, str: *const xmlChar) -> c_int {
3033    if str.is_null() {
3034        return -1;
3035    }
3036    let len = crate::xml::string::xml_strlen(str) as c_int;
3037    crate::xml::io::buf_add(buf, str, len)
3038}
3039
3040/// Set buffer allocation scheme.
3041///
3042/// # UPSTREAM-PARITY
3043///
3044/// ```c
3045/// void xmlBufferSetAllocationScheme(xmlBufferPtr buf,
3046///                                    xmlBufferAllocationScheme scheme);
3047/// ```
3048#[no_mangle]
3049pub extern "C" fn xmlBufferSetAllocationScheme(buf: *mut _xmlBuffer, scheme: c_int) {
3050    if buf.is_null() {
3051        return;
3052    }
3053    unsafe {
3054        (*buf).alloc = scheme;
3055    }
3056}
3057
3058/// Shrink buffer.
3059///
3060/// # UPSTREAM-PARITY
3061///
3062/// ```c
3063/// int xmlBufferShrink(xmlBufferPtr buf, int len);
3064/// ```
3065#[no_mangle]
3066pub extern "C" fn xmlBufferShrink(buf: *mut _xmlBuffer, len: c_int) -> c_int {
3067    if buf.is_null() || len <= 0 {
3068        return 0;
3069    }
3070    unsafe {
3071        let b = &mut *buf;
3072        let shrink_len = (len as c_uint).min(b.use_);
3073        if shrink_len > 0 {
3074            let remaining = b.use_ - shrink_len;
3075            if remaining > 0 {
3076                core::ptr::copy(
3077                    b.content.add(shrink_len as usize),
3078                    b.content,
3079                    remaining as usize,
3080                );
3081            }
3082            *b.content.add(remaining as usize) = 0;
3083            b.use_ = remaining;
3084        }
3085    }
3086    len
3087}
3088
3089/// Grow buffer.
3090///
3091/// # UPSTREAM-PARITY
3092///
3093/// ```c
3094/// int xmlBufferGrow(xmlBufferPtr buf, int len);
3095/// ```
3096#[no_mangle]
3097pub extern "C" fn xmlBufferGrow(buf: *mut _xmlBuffer, len: c_int) -> c_int {
3098    if buf.is_null() || len <= 0 {
3099        return 0;
3100    }
3101    let cur_use = unsafe { (*buf).use_ };
3102    let new_size = cur_use + len as c_uint + 1;
3103    crate::xml::io::buf_grow(buf, new_size)
3104}
3105
3106/// Reserve buffer space.
3107///
3108/// # UPSTREAM-PARITY
3109///
3110/// ```c
3111/// int xmlBufferReserve(xmlBufferPtr buf, int len);
3112/// ```
3113#[no_mangle]
3114pub extern "C" fn xmlBufferReserve(buf: *mut _xmlBuffer, len: c_int) -> c_int {
3115    xmlBufferGrow(buf, len)
3116}
3117
3118/// Detach buffer content.
3119///
3120/// # UPSTREAM-PARITY
3121///
3122/// ```c
3123/// xmlChar *xmlBufferDetach(xmlBufferPtr buf);
3124/// ```
3125#[no_mangle]
3126pub extern "C" fn xmlBufferDetach(buf: *mut _xmlBuffer) -> *mut xmlChar {
3127    if buf.is_null() {
3128        return ptr::null_mut();
3129    }
3130    unsafe {
3131        let content = (*buf).content;
3132        (*buf).content = ptr::null_mut();
3133        (*buf).use_ = 0;
3134        (*buf).size = 0;
3135        content
3136    }
3137}
3138
3139// ═══════════════════════════════════════════════════════════════════════════════
3140// 13. Encoding
3141// ═══════════════════════════════════════════════════════════════════════════════
3142
3143/// Get encoding from a name string.
3144///
3145/// # UPSTREAM-PARITY
3146///
3147/// ```c
3148/// xmlCharEncoding xmlGetCharEncoding(const char *name);
3149/// ```
3150#[no_mangle]
3151pub extern "C" fn xmlGetCharEncoding(name: *const c_char) -> c_int {
3152    if name.is_null() {
3153        return 0; // XML_CHAR_ENCODING_NONE
3154    }
3155    let name_bytes = unsafe {
3156        let len = libc::strlen(name);
3157        core::slice::from_raw_parts(name as *const u8, len)
3158    };
3159    crate::xml::encoding::encoding_from_name(name_bytes) as c_int
3160}
3161
3162/// Find an encoding handler.
3163///
3164/// # UPSTREAM-PARITY
3165///
3166/// ```c
3167/// xmlCharEncodingHandlerPtr xmlFindCharEncodingHandler(const char *name);
3168/// ```
3169#[no_mangle]
3170pub extern "C" fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut c_void {
3171    if name.is_null() {
3172        return ptr::null_mut();
3173    }
3174    crate::xml::encoding::find_encoding_handler(name as *const xmlChar) as *mut c_void
3175}
3176
3177/// Close an encoding handler.
3178///
3179/// # UPSTREAM-PARITY
3180///
3181/// ```c
3182/// int xmlCharEncCloseFunc(xmlCharEncodingHandlerPtr handler);
3183/// ```
3184#[no_mangle]
3185pub extern "C" fn xmlCharEncCloseFunc(handler: *mut c_void) -> c_int {
3186    if handler.is_null() {
3187        return -1;
3188    }
3189    // Free the encoding handler
3190    unsafe {
3191        let h = handler as *mut crate::abi::structs::_xmlCharEncodingHandler;
3192        if !(*h).name.is_null() {
3193            crate::abi::allocator::xmlFree((*h).name as *mut c_void);
3194        }
3195        crate::abi::allocator::xmlFree(handler);
3196    }
3197    0
3198}
3199
3200/// Convert an input buffer's encoding.
3201///
3202/// # UPSTREAM-PARITY
3203///
3204/// ```c
3205/// int xmlCharEncInput(xmlParserInputBufferPtr input, int to);
3206/// ```
3207#[no_mangle]
3208pub unsafe extern "C" fn xmlCharEncInput(input: *mut _xmlParserInputBuffer, _to: c_int) -> c_int {
3209    if input.is_null() {
3210        return -1;
3211    }
3212    let handler = (*input).encoder as *mut crate::abi::structs::_xmlCharEncodingHandler;
3213    if handler.is_null() {
3214        return -1;
3215    }
3216    let raw = (*input).raw as *mut crate::abi::structs::_xmlBuffer;
3217    let buf = (*input).buffer as *mut crate::abi::structs::_xmlBuffer;
3218    if raw.is_null() || buf.is_null() {
3219        return -1;
3220    }
3221    crate::xml::encoding::char_enc_in(handler, buf, raw)
3222}
3223
3224/// Convert an output buffer's encoding.
3225///
3226/// # UPSTREAM-PARITY
3227///
3228/// ```c
3229/// int xmlCharEncOutput(xmlOutputBufferPtr output, int to);
3230/// ```
3231#[no_mangle]
3232pub unsafe extern "C" fn xmlCharEncOutput(output: *mut _xmlOutputBuffer, _to: c_int) -> c_int {
3233    if output.is_null() {
3234        return -1;
3235    }
3236    let handler = (*output).encoder as *mut crate::abi::structs::_xmlCharEncodingHandler;
3237    if handler.is_null() {
3238        return -1;
3239    }
3240    let buf = (*output).buffer as *mut crate::abi::structs::_xmlBuffer;
3241    let conv = (*output).conv as *mut crate::abi::structs::_xmlBuffer;
3242    if buf.is_null() || conv.is_null() {
3243        return -1;
3244    }
3245    crate::xml::encoding::char_enc_out(handler, conv, buf)
3246}
3247
3248// ═══════════════════════════════════════════════════════════════════════════════
3249// URI
3250// ═══════════════════════════════════════════════════════════════════════════════
3251
3252/// Parse a URI string.
3253///
3254/// # UPSTREAM-PARITY
3255///
3256/// ```c
3257/// xmlURIPtr xmlParseURI(const char *str);
3258/// ```
3259#[no_mangle]
3260pub unsafe extern "C" fn xmlParseURI(str: *const c_char) -> *mut c_void {
3261    crate::xml::uri::xmlParseURI(str)
3262}
3263
3264/// Parse a URI string (raw version).
3265///
3266/// # UPSTREAM-PARITY
3267///
3268/// ```c
3269/// xmlURIPtr xmlParseURIRaw(const char *str, int raw);
3270/// ```
3271#[no_mangle]
3272pub unsafe extern "C" fn xmlParseURIRaw(str: *const c_char, raw: c_int) -> *mut c_void {
3273    let _ = raw;
3274    crate::xml::uri::xmlParseURI(str)
3275}
3276
3277/// Free a URI structure.
3278///
3279/// # UPSTREAM-PARITY
3280///
3281/// ```c
3282/// void xmlFreeURI(xmlURIPtr uri);
3283/// ```
3284#[no_mangle]
3285pub unsafe extern "C" fn xmlFreeURI(uri: *mut c_void) {
3286    crate::xml::uri::xmlFreeURI(uri)
3287}
3288
3289/// Create an empty URI.
3290///
3291/// # UPSTREAM-PARITY
3292///
3293/// ```c
3294/// xmlURIPtr xmlCreateURI(void);
3295/// ```
3296#[no_mangle]
3297pub extern "C" fn xmlCreateURI() -> *mut c_void {
3298    crate::xml::uri::xmlCreateURI()
3299}
3300
3301/// Save a URI structure to a string.
3302///
3303/// # UPSTREAM-PARITY
3304///
3305/// ```c
3306/// xmlChar *xmlSaveUri(xmlURIPtr uri);
3307/// ```
3308#[no_mangle]
3309pub unsafe extern "C" fn xmlSaveUri(uri: *mut c_void) -> *mut xmlChar {
3310    crate::xml::uri::xmlSaveUri(uri)
3311}
3312
3313/// Escape a URI string.
3314///
3315/// # UPSTREAM-PARITY
3316///
3317/// ```c
3318/// xmlChar *xmlURIEscapeStr(const xmlChar *str, const xmlChar *list);
3319/// ```
3320#[no_mangle]
3321pub unsafe extern "C" fn xmlURIEscapeStr(
3322    str: *const xmlChar,
3323    list: *const xmlChar,
3324) -> *mut xmlChar {
3325    crate::xml::uri::xmlURIEscapeStr(str, list)
3326}
3327
3328/// Unescape a URI string.
3329///
3330/// # UPSTREAM-PARITY
3331///
3332/// ```c
3333/// char *xmlURIUnescapeString(const char *str, int len, char *target);
3334/// ```
3335#[no_mangle]
3336pub unsafe extern "C" fn xmlURIUnescapeString(
3337    str: *const c_char,
3338    len: c_int,
3339    target: *mut c_char,
3340) -> *mut c_char {
3341    crate::xml::uri::xmlURIUnescapeString(str, len, target)
3342}
3343
3344// ═══════════════════════════════════════════════════════════════════════════════
3345// 14. XPath
3346// ═══════════════════════════════════════════════════════════════════════════════
3347
3348// ── Helper functions ────────────────────────────────────────────────────
3349
3350/// Convert an internal `XPathValue` to a C ABI `_xmlXPathObject`.
3351///
3352/// The returned pointer is heap-allocated via `xmlMallocZero` and must be
3353/// freed with `xmlXPathFreeObject`.
3354///
3355/// # Safety
3356///
3357/// Must be called from a context where `xmlMalloc` is safe to call.
3358unsafe fn xpath_to_object(val: XPathValue) -> *mut _xmlXPathObject {
3359    let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
3360    if obj.is_null() {
3361        return ptr::null_mut();
3362    }
3363    match val {
3364        XPathValue::NodeSet(ns) => {
3365            (*obj).type_ = xmlXPathObjectType::XPATH_NODESET as c_int;
3366            (*obj).nodesetval = ns.to_raw() as *mut c_void;
3367        }
3368        XPathValue::Boolean(b) => {
3369            (*obj).type_ = xmlXPathObjectType::XPATH_BOOLEAN as c_int;
3370            (*obj).boolval = if b { 1 } else { 0 };
3371        }
3372        XPathValue::Number(n) => {
3373            (*obj).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
3374            (*obj).floatval = n;
3375        }
3376        XPathValue::String(s) => {
3377            (*obj).type_ = xmlXPathObjectType::XPATH_STRING as c_int;
3378            let bytes = s.as_bytes();
3379            let len = bytes.len();
3380            let buf = xmlMalloc(len + 1) as *mut xmlChar;
3381            if !buf.is_null() {
3382                ptr::copy_nonoverlapping(bytes.as_ptr(), buf, len);
3383                *buf.add(len) = 0; // null terminator
3384            }
3385            (*obj).stringval = buf;
3386        }
3387    }
3388    obj
3389}
3390
3391/// Extract an internal `XPathValue` from a C ABI `_xmlXPathObject`.
3392///
3393/// # Safety
3394///
3395/// `obj` must be a valid, non-null pointer to a properly initialised
3396/// `_xmlXPathObject`.
3397unsafe fn object_to_xpathvalue(obj: *mut _xmlXPathObject) -> XPathValue {
3398    let typ = (*obj).type_;
3399    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
3400        let ns_ptr = (*obj).nodesetval as *mut _xmlNodeSet;
3401        if ns_ptr.is_null() {
3402            return XPathValue::NodeSet(NodeSet::new());
3403        }
3404        let node_nr = (*ns_ptr).nodeNr;
3405        let node_tab = (*ns_ptr).nodeTab;
3406        let mut ns = NodeSet::new();
3407        if !node_tab.is_null() {
3408            for i in 0..node_nr as isize {
3409                let node = *node_tab.add(i as usize);
3410                ns.push(node);
3411            }
3412        }
3413        XPathValue::NodeSet(ns)
3414    } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
3415        XPathValue::Boolean((*obj).boolval != 0)
3416    } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
3417        XPathValue::Number((*obj).floatval)
3418    } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
3419        let s_ptr = (*obj).stringval;
3420        if s_ptr.is_null() {
3421            XPathValue::String(String::new())
3422        } else {
3423            let s = CStr::from_ptr(s_ptr as *const c_char)
3424                .to_string_lossy()
3425                .into_owned();
3426            XPathValue::String(s)
3427        }
3428    } else {
3429        // Undefined / unknown type — return boolean false as a safe default.
3430        XPathValue::Boolean(false)
3431    }
3432}
3433
3434// ── Compiled expression registry ────────────────────────────────────────
3435//
3436// Compiled XPath expressions are opaque pointers returned by xmlXPathCompile.
3437// We store them in a global registry keyed by a monotonically increasing ID.
3438
3439static COMPILED_EXPRS: Lazy<Mutex<HashMap<u64, Box<CompiledExpr>>>> =
3440    Lazy::new(|| Mutex::new(HashMap::new()));
3441static NEXT_COMPILED_KEY: Lazy<Mutex<u64>> = Lazy::new(|| Mutex::new(1));
3442
3443// ── C extension-function registry ──────────────────────────────────────
3444//
3445// C extension functions registered via xmlXPathRegisterFunc / RegisterFuncNS
3446// are stored here because the Rust XPathFunction signature is incompatible
3447// with the C xmlXPathFunction calling convention (the C function expects a
3448// parser context, not pre-evaluated argument slices). The registration is
3449// stored faithfully; invoking registered C functions from within the Rust
3450// evaluator requires a bridge that is not yet implemented.
3451
3452type CXPathFunc = unsafe extern "C" fn(*mut c_void, c_int);
3453
3454/// Wrapper around `*mut c_void` that implements `Send` + `Sync` so it can
3455/// be used as a key in a `Mutex`-protected global `HashMap`.
3456#[derive(Clone, Copy, PartialEq, Eq, Hash)]
3457struct SendSyncPtr(*mut c_void);
3458unsafe impl Send for SendSyncPtr {}
3459unsafe impl Sync for SendSyncPtr {}
3460
3461static C_FUNCTIONS: Lazy<Mutex<HashMap<(SendSyncPtr, String), CXPathFunc>>> =
3462    Lazy::new(|| Mutex::new(HashMap::new()));
3463
3464/// Rust-side wrapper that is registered in the internal XPathContext when a
3465/// C extension function is registered. It looks up the C function pointer and
3466/// attempts to call it, but the calling-convention mismatch means this is a
3467/// stub that returns an error for now.
3468fn c_func_stub(_ctx: &mut XPathContext, _args: &[XPathValue]) -> Result<XPathValue, String> {
3469    Err(
3470        "C extension function cannot be called from Rust evaluator without a parser-context bridge"
3471            .to_string(),
3472    )
3473}
3474
3475// ── Public API ─────────────────────────────────────────────────────────
3476
3477/// Create a new XPath context.
3478///
3479/// Allocates a `_xmlXPathContext` and an internal `XPathContext`, storing
3480/// the latter's pointer in the `extra` field.
3481///
3482/// # UPSTREAM-PARITY
3483///
3484/// ```c
3485/// xmlXPathContextPtr xmlXPathNewContext(xmlDocPtr doc);
3486/// ```
3487#[no_mangle]
3488pub unsafe extern "C" fn xmlXPathNewContext(doc: *mut _xmlDoc) -> *mut _xmlXPathContext {
3489    let ctxt = xmlMallocZero(size_of::<_xmlXPathContext>()) as *mut _xmlXPathContext;
3490    if ctxt.is_null() {
3491        return ptr::null_mut();
3492    }
3493
3494    // Initialise the C ABI context fields.
3495    (*ctxt).doc = doc;
3496    (*ctxt).node = ptr::null_mut();
3497    (*ctxt).contextSize = 1;
3498    (*ctxt).proximityPosition = 1;
3499
3500    // Create the internal XPathContext and store it in `extra`.
3501    let internal = Box::new(XPathContext::new(doc));
3502    (*ctxt).extra = Box::into_raw(internal) as *mut c_void;
3503
3504    ctxt
3505}
3506
3507/// Free an XPath context.
3508///
3509/// # UPSTREAM-PARITY
3510///
3511/// ```c
3512/// void xmlXPathFreeContext(xmlXPathContextPtr ctxt);
3513/// ```
3514#[no_mangle]
3515pub unsafe extern "C" fn xmlXPathFreeContext(ctxt: *mut _xmlXPathContext) {
3516    if ctxt.is_null() {
3517        return;
3518    }
3519    // Drop the internal XPathContext.
3520    if !(*ctxt).extra.is_null() {
3521        let _ = Box::from_raw((*ctxt).extra as *mut XPathContext);
3522        (*ctxt).extra = ptr::null_mut();
3523    }
3524    // Free the C ABI context struct.
3525    xmlFree(ctxt as *mut c_void);
3526}
3527
3528/// Evaluate an XPath expression.
3529///
3530/// # UPSTREAM-PARITY
3531///
3532/// ```c
3533/// xmlXPathObjectPtr xmlXPathEvalExpression(const xmlChar *str,
3534///                                          xmlXPathContextPtr ctxt);
3535/// ```
3536#[no_mangle]
3537pub unsafe extern "C" fn xmlXPathEvalExpression(
3538    str_: *const xmlChar,
3539    ctxt: *mut _xmlXPathContext,
3540) -> *mut _xmlXPathObject {
3541    if str_.is_null() || ctxt.is_null() {
3542        return ptr::null_mut();
3543    }
3544    let expr_str = match CStr::from_ptr(str_ as *const c_char).to_str() {
3545        Ok(s) => s,
3546        Err(_) => return ptr::null_mut(),
3547    };
3548    let internal = (*ctxt).extra as *mut XPathContext;
3549    if internal.is_null() {
3550        return ptr::null_mut();
3551    }
3552    let internal = &mut *internal;
3553
3554    match crate::xml::xpath::evaluate_str(expr_str, internal) {
3555        Some(val) => xpath_to_object(val),
3556        None => ptr::null_mut(),
3557    }
3558}
3559
3560/// Evaluate an XPath expression (simplified alias).
3561///
3562/// # UPSTREAM-PARITY
3563///
3564/// ```c
3565/// xmlXPathObjectPtr xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctxt);
3566/// ```
3567#[no_mangle]
3568pub unsafe extern "C" fn xmlXPathEval(
3569    str_: *const xmlChar,
3570    ctxt: *mut _xmlXPathContext,
3571) -> *mut _xmlXPathObject {
3572    xmlXPathEvalExpression(str_, ctxt)
3573}
3574
3575/// Free an XPath object.
3576///
3577/// Releases the internal members (string buffer or node-set) and then frees
3578/// the object struct itself.
3579///
3580/// # UPSTREAM-PARITY
3581///
3582/// ```c
3583/// void xmlXPathFreeObject(xmlXPathObjectPtr obj);
3584/// ```
3585#[no_mangle]
3586pub unsafe extern "C" fn xmlXPathFreeObject(obj: *mut _xmlXPathObject) {
3587    if obj.is_null() {
3588        return;
3589    }
3590    let typ = (*obj).type_;
3591    // Free string storage.
3592    if typ == xmlXPathObjectType::XPATH_STRING as c_int {
3593        if !(*obj).stringval.is_null() {
3594            xmlFree((*obj).stringval as *mut c_void);
3595            (*obj).stringval = ptr::null_mut();
3596        }
3597    }
3598    // Free node-set storage.
3599    if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
3600        let ns = (*obj).nodesetval as *mut _xmlNodeSet;
3601        if !ns.is_null() {
3602            if !(*ns).nodeTab.is_null() {
3603                xmlFree((*ns).nodeTab as *mut c_void);
3604            }
3605            xmlFree(ns as *mut c_void);
3606        }
3607        (*obj).nodesetval = ptr::null_mut();
3608    }
3609    xmlFree(obj as *mut c_void);
3610}
3611
3612/// Compile an XPath expression.
3613///
3614/// Returns an opaque pointer that can be passed to `xmlXPathEvalExpression`
3615/// (via the compiled-expr infrastructure) or freed with `xmlXPathFreeCompExpr`.
3616///
3617/// # UPSTREAM-PARITY
3618///
3619/// ```c
3620/// xmlXPathCompExprPtr xmlXPathCompile(const xmlChar *str);
3621/// ```
3622#[no_mangle]
3623pub unsafe extern "C" fn xmlXPathCompile(str_: *const xmlChar) -> *mut c_void {
3624    if str_.is_null() {
3625        return ptr::null_mut();
3626    }
3627    let expr_str = match CStr::from_ptr(str_ as *const c_char).to_str() {
3628        Ok(s) => s,
3629        Err(_) => return ptr::null_mut(),
3630    };
3631
3632    match crate::xml::xpath::compile(expr_str) {
3633        Some(compiled) => {
3634            let mut map = COMPILED_EXPRS.lock();
3635            let mut counter = NEXT_COMPILED_KEY.lock();
3636            let key = *counter;
3637            *counter += 1;
3638            map.insert(key, Box::new(compiled));
3639            key as *mut c_void
3640        }
3641        None => ptr::null_mut(),
3642    }
3643}
3644
3645/// Free a compiled XPath expression.
3646///
3647/// # UPSTREAM-PARITY
3648///
3649/// ```c
3650/// void xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp);
3651/// ```
3652#[no_mangle]
3653pub unsafe extern "C" fn xmlXPathFreeCompExpr(comp: *mut c_void) {
3654    if comp.is_null() {
3655        return;
3656    }
3657    let mut map = COMPILED_EXPRS.lock();
3658    map.remove(&(comp as u64));
3659}
3660
3661/// Register an XPath namespace.
3662///
3663/// # UPSTREAM-PARITY
3664///
3665/// ```c
3666/// int xmlXPathRegisterNs(xmlXPathContextPtr ctxt,
3667///                        const xmlChar *prefix, const xmlChar *ns_uri);
3668/// ```
3669#[no_mangle]
3670pub unsafe extern "C" fn xmlXPathRegisterNs(
3671    ctxt: *mut _xmlXPathContext,
3672    prefix: *const xmlChar,
3673    ns_uri: *const xmlChar,
3674) -> c_int {
3675    if ctxt.is_null() || prefix.is_null() || ns_uri.is_null() {
3676        return -1;
3677    }
3678    let internal = (*ctxt).extra as *mut XPathContext;
3679    if internal.is_null() {
3680        return -1;
3681    }
3682    let internal = &mut *internal;
3683
3684    let prefix_str = match CStr::from_ptr(prefix as *const c_char).to_str() {
3685        Ok(s) => s,
3686        Err(_) => return -1,
3687    };
3688    let uri_str = match CStr::from_ptr(ns_uri as *const c_char).to_str() {
3689        Ok(s) => s,
3690        Err(_) => return -1,
3691    };
3692
3693    internal.register_namespace(prefix_str, uri_str);
3694    0
3695}
3696
3697/// Register an XPath function.
3698///
3699/// The C function pointer is stored in a side table keyed by the context.
3700/// A Rust-side stub is registered in the internal context so that the Rust
3701/// evaluator is aware of the function; however, calling the C function
3702/// directly from the Rust evaluator is not yet supported.
3703///
3704/// # UPSTREAM-PARITY
3705///
3706/// ```c
3707/// int xmlXPathRegisterFunc(xmlXPathContextPtr ctxt,
3708///                          const xmlChar *name, xmlXPathFunction f);
3709/// ```
3710#[no_mangle]
3711pub unsafe extern "C" fn xmlXPathRegisterFunc(
3712    ctxt: *mut _xmlXPathContext,
3713    name: *const xmlChar,
3714    f: Option<unsafe extern "C" fn(*mut c_void, c_int)>,
3715) -> c_int {
3716    if ctxt.is_null() || name.is_null() {
3717        return -1;
3718    }
3719    let internal = (*ctxt).extra as *mut XPathContext;
3720    if internal.is_null() {
3721        return -1;
3722    }
3723    let internal = &mut *internal;
3724
3725    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
3726        Ok(s) => s,
3727        Err(_) => return -1,
3728    };
3729
3730    if let Some(func) = f {
3731        // Store the C function pointer in the side table.
3732        let key = (SendSyncPtr((*ctxt).extra), name_str.to_string());
3733        C_FUNCTIONS.lock().insert(key, func);
3734        // Register a Rust stub so the evaluator knows the function exists.
3735        internal.register_function(name_str, c_func_stub);
3736    }
3737    0
3738}
3739
3740/// Register an XPath function with namespace.
3741///
3742/// # UPSTREAM-PARITY
3743///
3744/// ```c
3745/// int xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt,
3746///                            const xmlChar *name, const xmlChar *ns_uri,
3747///                            xmlXPathFunction f);
3748/// ```
3749#[no_mangle]
3750pub unsafe extern "C" fn xmlXPathRegisterFuncNS(
3751    ctxt: *mut _xmlXPathContext,
3752    name: *const xmlChar,
3753    ns_uri: *const xmlChar,
3754    f: Option<unsafe extern "C" fn(*mut c_void, c_int)>,
3755) -> c_int {
3756    if ctxt.is_null() || name.is_null() {
3757        return -1;
3758    }
3759    let internal = (*ctxt).extra as *mut XPathContext;
3760    if internal.is_null() {
3761        return -1;
3762    }
3763    let internal = &mut *internal;
3764
3765    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
3766        Ok(s) => s,
3767        Err(_) => return -1,
3768    };
3769    let ns_str = if ns_uri.is_null() {
3770        String::new()
3771    } else {
3772        match CStr::from_ptr(ns_uri as *const c_char).to_str() {
3773            Ok(s) => s.to_string(),
3774            Err(_) => return -1,
3775        }
3776    };
3777
3778    // Use "{ns}:" prefix as part of the key to keep functions unique.
3779    let qualified = if ns_str.is_empty() {
3780        name_str.to_string()
3781    } else {
3782        format!("{{{}}}{}", ns_str, name_str)
3783    };
3784
3785    if let Some(func) = f {
3786        let key = (SendSyncPtr((*ctxt).extra), qualified.clone());
3787        C_FUNCTIONS.lock().insert(key, func);
3788        internal.register_function(&qualified, c_func_stub);
3789    }
3790    0
3791}
3792
3793/// Register an XPath variable.
3794///
3795/// # UPSTREAM-PARITY
3796///
3797/// ```c
3798/// int xmlXPathRegisterVariable(xmlXPathContextPtr ctxt,
3799///                              const xmlChar *name, xmlXPathObjectPtr value);
3800/// ```
3801#[no_mangle]
3802pub unsafe extern "C" fn xmlXPathRegisterVariable(
3803    ctxt: *mut _xmlXPathContext,
3804    name: *const xmlChar,
3805    value: *mut _xmlXPathObject,
3806) -> c_int {
3807    if ctxt.is_null() || name.is_null() || value.is_null() {
3808        return -1;
3809    }
3810    let internal = (*ctxt).extra as *mut XPathContext;
3811    if internal.is_null() {
3812        return -1;
3813    }
3814    let internal = &mut *internal;
3815
3816    let name_str = match CStr::from_ptr(name as *const c_char).to_str() {
3817        Ok(s) => s,
3818        Err(_) => return -1,
3819    };
3820
3821    let xpath_val = object_to_xpathvalue(value);
3822    internal.register_variable(name_str, xpath_val);
3823    0
3824}
3825
3826/// Create an XPath object wrapping a single node in a node-set.
3827///
3828/// # UPSTREAM-PARITY
3829///
3830/// ```c
3831/// xmlXPathObjectPtr xmlXPathNewNodeSet(xmlNodePtr val);
3832/// ```
3833#[no_mangle]
3834pub unsafe extern "C" fn xmlXPathNewNodeSet(val: *mut _xmlNode) -> *mut _xmlXPathObject {
3835    let ns = if val.is_null() {
3836        NodeSet::new()
3837    } else {
3838        NodeSet::singleton(val)
3839    };
3840    xpath_to_object(XPathValue::NodeSet(ns))
3841}
3842
3843/// Create an XPath object from a C string value.
3844///
3845/// # UPSTREAM-PARITY
3846///
3847/// ```c
3848/// xmlXPathObjectPtr xmlXPathNewCString(const xmlChar *val);
3849/// ```
3850#[no_mangle]
3851pub unsafe extern "C" fn xmlXPathNewCString(val: *const xmlChar) -> *mut _xmlXPathObject {
3852    if val.is_null() {
3853        return xpath_to_object(XPathValue::String(String::new()));
3854    }
3855    let s = match CStr::from_ptr(val as *const c_char).to_str() {
3856        Ok(s) => s.to_string(),
3857        Err(_) => return ptr::null_mut(),
3858    };
3859    xpath_to_object(XPathValue::String(s))
3860}
3861
3862/// Create an XPath number object.
3863///
3864/// # UPSTREAM-PARITY
3865///
3866/// ```c
3867/// xmlXPathObjectPtr xmlXPathNewFloat(double val);
3868/// ```
3869#[no_mangle]
3870pub extern "C" fn xmlXPathNewFloat(val: f64) -> *mut _xmlXPathObject {
3871    unsafe { xpath_to_object(XPathValue::Number(val)) }
3872}
3873
3874/// Create an XPath boolean object.
3875///
3876/// # UPSTREAM-PARITY
3877///
3878/// ```c
3879/// xmlXPathObjectPtr xmlXPathNewBoolean(int val);
3880/// ```
3881#[no_mangle]
3882pub extern "C" fn xmlXPathNewBoolean(val: c_int) -> *mut _xmlXPathObject {
3883    unsafe { xpath_to_object(XPathValue::Boolean(val != 0)) }
3884}
3885
3886// ═══════════════════════════════════════════════════════════════════════════════
3887// 14.5. XPointer
3888// ═══════════════════════════════════════════════════════════════════════════════
3889
3890/// Evaluate an XPointer expression.
3891///
3892/// Delegates to the xpointer module.
3893///
3894/// # UPSTREAM-PARITY
3895///
3896/// ```c
3897/// xmlNodePtr xmlXPtrEval(const xmlChar *expr, xmlDocPtr doc);
3898/// ```
3899#[no_mangle]
3900pub unsafe extern "C" fn xmlXPtrEval(expr: *const c_char, doc: *mut _xmlDoc) -> *mut _xmlNode {
3901    crate::xml::xpointer::xmlXPtrEval(expr, doc)
3902}
3903
3904// ═══════════════════════════════════════════════════════════════════════════════
3905// 15. XInclude
3906// ═══════════════════════════════════════════════════════════════════════════════
3907
3908/// Process XInclude nodes in a document.
3909///
3910/// # UPSTREAM-PARITY
3911///
3912/// ```c
3913/// int xmlXIncludeProcess(xmlDocPtr doc);
3914/// ```
3915#[no_mangle]
3916pub unsafe extern "C" fn xmlXIncludeProcess(doc: *mut _xmlDoc) -> c_int {
3917    crate::xml::xinclude::xinclude_process(doc)
3918}
3919
3920/// Process XInclude nodes with flags.
3921///
3922/// # UPSTREAM-PARITY
3923///
3924/// ```c
3925/// int xmlXIncludeProcessFlags(xmlDocPtr doc, int flags);
3926/// ```
3927#[no_mangle]
3928pub unsafe extern "C" fn xmlXIncludeProcessFlags(doc: *mut _xmlDoc, flags: c_int) -> c_int {
3929    crate::xml::xinclude::xinclude_process_flags(doc, flags)
3930}
3931
3932// ═══════════════════════════════════════════════════════════════════════════════
3933// 16. Catalog
3934// ═══════════════════════════════════════════════════════════════════════════════
3935
3936/// Load a catalog.
3937///
3938/// # UPSTREAM-PARITY
3939///
3940/// ```c
3941/// xmlCatalogPtr xmlCatalogLoad(const char *catalogs);
3942/// ```
3943#[no_mangle]
3944pub extern "C" fn xmlCatalogLoad(catalogs: *const c_char) -> *mut c_void {
3945    if catalogs.is_null() {
3946        return ptr::null_mut();
3947    }
3948    crate::xml::catalog::load_catalog(catalogs)
3949}
3950
3951/// Resolve a public ID.
3952///
3953/// # UPSTREAM-PARITY
3954///
3955/// ```c
3956/// xmlCharPtr xmlCatalogResolvePublic(const xmlChar *pubID);
3957/// ```
3958#[no_mangle]
3959pub unsafe extern "C" fn xmlCatalogResolvePublic(pubID: *const xmlChar) -> *mut xmlChar {
3960    if pubID.is_null() {
3961        return ptr::null_mut();
3962    }
3963    crate::xml::catalog::resolve_public(pubID)
3964}
3965
3966/// Resolve a system ID.
3967///
3968/// # UPSTREAM-PARITY
3969///
3970/// ```c
3971/// xmlCharPtr xmlCatalogResolveSystem(const xmlChar *sysID);
3972/// ```
3973#[no_mangle]
3974pub unsafe extern "C" fn xmlCatalogResolveSystem(sysID: *const xmlChar) -> *mut xmlChar {
3975    if sysID.is_null() {
3976        return ptr::null_mut();
3977    }
3978    crate::xml::catalog::resolve_system(sysID)
3979}
3980
3981/// Resolve a URI.
3982///
3983/// # UPSTREAM-PARITY
3984///
3985/// ```c
3986/// xmlCharPtr xmlCatalogResolveURI(const xmlChar *URI);
3987/// ```
3988#[no_mangle]
3989pub unsafe extern "C" fn xmlCatalogResolveURI(URI: *const xmlChar) -> *mut xmlChar {
3990    if URI.is_null() {
3991        return ptr::null_mut();
3992    }
3993    crate::xml::catalog::resolve_uri(URI)
3994}
3995
3996/// Set catalog defaults.
3997///
3998/// # UPSTREAM-PARITY
3999///
4000/// ```c
4001/// void xmlCatalogSetDefaults(xmlCatalogAllowValue allow);
4002/// ```
4003#[no_mangle]
4004pub extern "C" fn xmlCatalogSetDefaults(allow: c_int) {
4005    crate::xml::catalog::set_defaults(allow)
4006}
4007
4008/// Get catalog defaults.
4009///
4010/// # UPSTREAM-PARITY
4011///
4012/// ```c
4013/// xmlCatalogAllowValue xmlCatalogGetDefaults(void);
4014/// ```
4015#[no_mangle]
4016pub extern "C" fn xmlCatalogGetDefaults() -> c_int {
4017    crate::xml::catalog::get_defaults()
4018}
4019
4020/// Add a catalog.
4021///
4022/// # UPSTREAM-PARITY
4023///
4024/// ```c
4025/// int xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace);
4026/// ```
4027#[no_mangle]
4028pub unsafe extern "C" fn xmlCatalogAdd(
4029    type_: *const xmlChar,
4030    orig: *const xmlChar,
4031    replace: *const xmlChar,
4032) -> c_int {
4033    if type_.is_null() || orig.is_null() || replace.is_null() {
4034        return -1;
4035    }
4036    crate::xml::catalog::add(type_, orig, replace)
4037}
4038
4039/// Remove a catalog entry.
4040///
4041/// # UPSTREAM-PARITY
4042///
4043/// ```c
4044/// int xmlCatalogRemove(const xmlChar *value);
4045/// ```
4046#[no_mangle]
4047pub unsafe extern "C" fn xmlCatalogRemove(value: *const xmlChar) -> c_int {
4048    if value.is_null() {
4049        return 0;
4050    }
4051    crate::xml::catalog::remove(value)
4052}
4053
4054/// Clean up the catalog subsystem.
4055///
4056/// # UPSTREAM-PARITY
4057///
4058/// ```c
4059/// void xmlCatalogCleanup(void);
4060/// ```
4061#[no_mangle]
4062pub extern "C" fn xmlCatalogCleanup() {
4063    crate::xml::catalog::cleanup();
4064}
4065
4066/// Convert an SGML catalog to XML.
4067///
4068/// # UPSTREAM-PARITY
4069///
4070/// ```c
4071/// xmlDocPtr xmlCatalogConvert(void);
4072/// ```
4073#[no_mangle]
4074pub extern "C" fn xmlCatalogConvert() -> *mut _xmlDoc {
4075    // SAFETY: catalog::convert() allocates and builds an XML document tree.
4076    unsafe { crate::xml::catalog::convert() }
4077}
4078
4079// ═══════════════════════════════════════════════════════════════════════════════
4080// 17. HTML
4081// ═══════════════════════════════════════════════════════════════════════════════
4082
4083/// Parse an HTML document from a file.
4084///
4085/// # UPSTREAM-PARITY
4086///
4087/// ```c
4088/// htmlDocPtr htmlParseFile(const char *filename, const char *encoding);
4089/// ```
4090#[no_mangle]
4091pub unsafe extern "C" fn htmlParseFile(
4092    _filename: *const c_char,
4093    _encoding: *const c_char,
4094) -> *mut _xmlDoc {
4095    // Phase 1: STUB
4096    ptr::null_mut()
4097}
4098
4099/// Parse an HTML document from memory.
4100///
4101/// # UPSTREAM-PARITY
4102///
4103/// ```c
4104/// htmlDocPtr htmlParseMemory(const char *buffer, int size);
4105/// ```
4106#[no_mangle]
4107pub unsafe extern "C" fn htmlParseMemory(_buffer: *const c_char, _size: c_int) -> *mut _xmlDoc {
4108    // Phase 1: STUB
4109    ptr::null_mut()
4110}
4111
4112/// Parse an HTML document from a document string.
4113///
4114/// # UPSTREAM-PARITY
4115///
4116/// ```c
4117/// htmlDocPtr htmlParseDoc(const xmlChar *cur, const char *encoding);
4118/// ```
4119#[no_mangle]
4120pub unsafe extern "C" fn htmlParseDoc(
4121    _cur: *const xmlChar,
4122    _encoding: *const c_char,
4123) -> *mut _xmlDoc {
4124    // Phase 1: STUB
4125    ptr::null_mut()
4126}
4127
4128/// Create an HTML parser context.
4129///
4130/// # UPSTREAM-PARITY
4131///
4132/// ```c
4133/// htmlParserCtxtPtr htmlCreateFileParserCtxt(const char *filename,
4134///                                            const char *encoding);
4135/// ```
4136#[no_mangle]
4137pub unsafe extern "C" fn htmlCreateFileParserCtxt(
4138    _filename: *const c_char,
4139    _encoding: *const c_char,
4140) -> *mut c_void {
4141    // Phase 1: STUB
4142    ptr::null_mut()
4143}
4144
4145/// Free an HTML parser context.
4146///
4147/// # UPSTREAM-PARITY
4148///
4149/// ```c
4150/// void htmlFreeParserCtxt(htmlParserCtxtPtr ctxt);
4151/// ```
4152#[no_mangle]
4153pub extern "C" fn htmlFreeParserCtxt(_ctxt: *mut c_void) {
4154    // Phase 1: STUB
4155}
4156
4157/// Initialize the HTML parser.
4158///
4159/// # UPSTREAM-PARITY
4160///
4161/// ```c
4162/// void htmlInitParser(void);
4163/// ```
4164#[no_mangle]
4165pub extern "C" fn htmlInitParser() {
4166    // Phase 1: STUB
4167}
4168
4169/// Clean up the HTML parser.
4170///
4171/// # UPSTREAM-PARITY
4172///
4173/// ```c
4174/// void htmlCleanupParser(void);
4175/// ```
4176#[no_mangle]
4177pub extern "C" fn htmlCleanupParser() {
4178    // Phase 1: STUB
4179}
4180
4181// ═══════════════════════════════════════════════════════════════════════════════
4182// 18. Debug / Miscellaneous
4183// ═══════════════════════════════════════════════════════════════════════════════
4184
4185/// Dump a document to a file for debugging.
4186///
4187/// # UPSTREAM-PARITY
4188///
4189/// ```c
4190/// void xmlDebugDumpDocument(FILE *output, xmlDocPtr doc);
4191/// ```
4192#[no_mangle]
4193pub unsafe extern "C" fn xmlDebugDumpDocument(_output: *mut c_void, _doc: *mut _xmlDoc) {
4194    // Phase 1: STUB
4195}
4196
4197/// Dump a node for debugging.
4198///
4199/// # UPSTREAM-PARITY
4200///
4201/// ```c
4202/// void xmlDebugDumpNode(FILE *output, xmlNodePtr node);
4203/// ```
4204#[no_mangle]
4205pub unsafe extern "C" fn xmlDebugDumpNode(_output: *mut c_void, _node: *mut _xmlNode) {
4206    // Phase 1: STUB
4207}
4208
4209/// Dump a node for debugging (recursive).
4210///
4211/// # UPSTREAM-PARITY
4212///
4213/// ```c
4214/// void xmlDebugDumpNodeList(FILE *output, xmlNodePtr node);
4215/// ```
4216#[no_mangle]
4217pub unsafe extern "C" fn xmlDebugDumpNodeList(_output: *mut c_void, _node: *mut _xmlNode) {
4218    // Phase 1: STUB
4219}
4220
4221/// Get the path to the current executable.
4222///
4223/// # UPSTREAM-PARITY
4224///
4225/// ```c
4226/// char *xmlGetBinaryPath(void);
4227/// ```
4228#[no_mangle]
4229pub extern "C" fn xmlGetBinaryPath() -> *mut c_char {
4230    // Phase 1: STUB
4231    ptr::null_mut()
4232}
4233
4234/// Get the path to the current executable's home directory.
4235///
4236/// # UPSTREAM-PARITY
4237///
4238/// ```c
4239/// char *xmlGetHomeOfBinary(void);
4240/// ```
4241#[no_mangle]
4242pub extern "C" fn xmlGetHomeOfBinary() -> *mut c_char {
4243    // Phase 1: STUB
4244    ptr::null_mut()
4245}