Skip to main content

libxml_rs/abi/
exports_misc.rs

1//! exports_misc — family closure (11.1-I).
2//!
3//! C ABI exports for the "miscellaneous" families:
4//!
5//! 1. getset — xmlGet*/xmlSet* legacy accessors (features, threads,
6//!    globals, tree navigation, entities, buffers)
7//! 2. module — xmlModule* dynamic-loading API (dlopen/dlsym/dlclose)
8//! 3. ucs — legacy `xmlUCSIsBlock`/`xmlUCSIsCat` name-table lookups
9//!    plus the `xmlUCSIsCatCc` control-character test
10//! 4. valid — validation helpers (attribute-value normalization,
11//!    potential-children / valid-elements enumeration)
12//! 5. misc2 — `__xml*` aliases, parser-context error helpers,
13//!    xmlFormatError, tree node constructors, content-model
14//!    serialization, deprecated stubs
15//!
16//! Every function here mirrors an exported symbol of the oracle DSO
17//! (`nm -D /usr/lib/libxml2.so.2`); signatures follow the installed
18//! headers (`/usr/include/libxml2/libxml/*.h`) and the archaeology tree
19//! (`archaeology/libxml2-git/*.c`).
20//!
21//! # Upstream contract
22//!
23//! Parity target is the oracle DSO (`nm -D /usr/lib/libxml2.so.2`): every
24//! symbol here mirrors an exported libxml2 2.15.3 symbol from `parser.c`,
25//! `tree.c`, `uri.c`, `xmlstring.c` and the module loader (`xmlmodule.c`),
26//! with signatures per the installed headers. R-000165 (11.1-O) closed the
27//! misc-family export gaps.
28//!
29//! # Conceptual behavior
30//!
31//! This module implements the miscellaneous families: the `xmlGet*/xmlSet*`
32//! legacy accessors (features, threads, globals, tree navigation, buffer
33//! scheme), the `xmlModule*` dynamic-loading API, the legacy `xmlUCSIsBlock`/
34//! `xmlUCSIsCat` name-table lookups, validation helpers, `__xml*` aliases,
35//! `xmlFormatError` and the deprecated stubs.
36//!
37//! # Ownership & safety invariants
38//!
39//! Ownership follows the per-family contract: module handles are caller-owned
40//! (`xmlModuleClose`), strings from feature/name lookups are borrowed static
41//! tables, and the getset accessors transfer no ownership. The `__xml*`
42//! aliases must keep the same free-with contract as their primary names (xml
43//! allocator results freed with `xmlFree`).
44//!
45//! # Historical quirks & epochs
46//!
47//! The legacy accessors and `__xml*` aliases are remnants of the 2.0-2.4
48//! `legacy_parser` epoch (HISTORY.md) that the ABI still exports;
49//! `xmlGetFeaturesList`/`xmlGetFeature` are deprecated since the 2.9 era.
50//! R-000165 (11.1-O) added the missing misc symbols.
51//!
52//! # Deliberate oddities
53//!
54//! The deprecated stubs are deliberate: several misc2 entry points are kept
55//! with their upstream empty/trivial bodies (R-000138 documented no-op set)
56//! rather than being removed, because removal would break linking for
57//! downstream code.
58//!
59//! # Proving courts
60//!
61//! The OWNERSHIP, PARSER and TREE-STRUCTURE court families plus the
62//! DSO-LOADER and HEADER-COMPILE courts exercise this
63//! module; the data-ABI probes require byte-identical output on the exercised
64//! paths.
65//!
66//! # Tempting simplifications that would break parity
67//!
68//! A tempting simplification is to delete the deprecated accessors and
69//! `__xml*` aliases as dead code — they are the observable ABI surface for
70//! legacy consumers and the DSO-LOADER court resolves them, so removing them
71//! would fail symbol resolution. Another shortcut, reimplementing
72//! `xmlGetFeature` as a no-op, would silently change behavior for consumers
73//! probing parser capabilities.
74
75#![allow(missing_docs)]
76#![allow(non_snake_case)]
77#![allow(non_camel_case_types)]
78#![allow(non_upper_case_globals)]
79#![allow(unused_variables)]
80#![allow(clippy::missing_safety_doc)]
81#![allow(clippy::not_unsafe_ptr_arg_deref)]
82
83// SAFETY-SCOPE: EXPORT-MISC-MECHANICAL-001
84// (11.1-Z.3 proof scope, classified-generated) — this module is the
85// mechanical extern-"C" export surface: every `unsafe` block in it is
86// the documented indirection/registry-access pattern whose validity
87// rests on the upstream C contract, and the exported signatures are
88// machine-measured by the ABI-FUNCTION-SIGNATURE and DSO-LOADER
89// courts and the C-API differential probes. The safety contract of
90// each export is stated in its own doc comment; this scope covers the
91// mechanical wrappers' unsafe blocks.
92
93use core::ffi::{c_char, c_void};
94use core::mem::{size_of, zeroed};
95use core::ptr;
96use core::sync::atomic::{AtomicI32, AtomicPtr, Ordering};
97use std::os::raw::c_int;
98
99use crate::abi::allocator::*;
100use crate::abi::callbacks::*;
101use crate::abi::structs::*;
102use crate::abi::types::xmlAttributeType::*;
103use crate::abi::types::xmlBufferAllocationScheme::*;
104use crate::abi::types::xmlElementContentOccur::*;
105use crate::abi::types::xmlElementContentType::*;
106use crate::abi::types::xmlElementType::*;
107use crate::abi::types::xmlEntityType::*;
108use crate::abi::types::xmlErrorLevel::*;
109use crate::abi::types::*;
110
111// ═══════════════════════════════════════════════════════════════════════════════
112// Small C-string / xmlChar helpers (local, so this module is self-contained)
113// ═══════════════════════════════════════════════════════════════════════════════
114
115/// strlen for a NUL-terminated byte string.
116const unsafe fn cstr_len(s: *const c_char) -> usize {
117    if s.is_null() {
118        return 0;
119    }
120    let mut n = 0usize;
121    while unsafe { *((s as *const u8).add(n)) } != 0 {
122        n += 1;
123    }
124    n
125}
126
127/// Compare a C string against a byte literal (with implicit NUL).
128const unsafe fn cstr_eq(s: *const c_char, lit: &[u8]) -> bool {
129    if s.is_null() {
130        return false;
131    }
132    let b = s as *const u8;
133    let mut i = 0usize;
134    while i < lit.len() {
135        if unsafe { *b.add(i) } != lit[i] {
136            return false;
137        }
138        i += 1;
139    }
140    unsafe { *b.add(i) == 0 }
141}
142
143/// strcmp ordering between a C string and a byte literal.
144const unsafe fn cstr_cmp(s: *const c_char, lit: &[u8]) -> core::cmp::Ordering {
145    let b = s as *const u8;
146    let mut i = 0usize;
147    loop {
148        let c = if i < lit.len() { lit[i] } else { 0 };
149        let sc = unsafe { *b.add(i) };
150        if sc < c {
151            return core::cmp::Ordering::Less;
152        }
153        if sc > c {
154            return core::cmp::Ordering::Greater;
155        }
156        if sc == 0 {
157            return core::cmp::Ordering::Equal;
158        }
159        i += 1;
160    }
161}
162
163/// Append a NUL-terminated C string's bytes to `v`.
164unsafe fn append_cstr(v: &mut Vec<u8>, s: *const c_char) {
165    if s.is_null() {
166        return;
167    }
168    let b = s as *const u8;
169    let mut i = 0usize;
170    loop {
171        let c = unsafe { *b.add(i) };
172        if c == 0 {
173            break;
174        }
175        v.push(c);
176        i += 1;
177    }
178}
179
180/// Append an xmlChar (byte) string to `v`.
181unsafe fn append_xmlstr(v: &mut Vec<u8>, s: *const xmlChar) {
182    if s.is_null() {
183        return;
184    }
185    let b = s;
186    let mut i = 0usize;
187    loop {
188        let c = unsafe { *b.add(i) };
189        if c == 0 {
190            break;
191        }
192        v.push(c);
193        i += 1;
194    }
195}
196
197/// strcat a byte literal onto a C string buffer.
198unsafe fn cstr_cat_lit(buf: *mut c_char, lit: &[u8]) {
199    if buf.is_null() {
200        return;
201    }
202    let mut i = cstr_len(buf);
203    let b = buf as *mut u8;
204    for &c in lit {
205        unsafe {
206            *b.add(i) = c;
207        }
208        i += 1;
209    }
210    unsafe {
211        *b.add(i) = 0;
212    }
213}
214
215/// strcat an xmlChar string onto a C string buffer.
216unsafe fn cstr_cat_xmlstr(buf: *mut c_char, s: *const xmlChar) {
217    if s.is_null() {
218        return;
219    }
220    let mut i = cstr_len(buf);
221    let mut j = 0usize;
222    let b = buf as *mut u8;
223    let sb = s;
224    loop {
225        let c = unsafe { *sb.add(j) };
226        if c == 0 {
227            break;
228        }
229        unsafe {
230            *b.add(i) = c;
231        }
232        i += 1;
233        j += 1;
234    }
235    unsafe {
236        *b.add(i) = 0;
237    }
238}
239
240/// Membership test over a merged short+long range list (upstream
241/// `xmlCharInRange` semantics; short and long ranges are disjoint so a
242/// linear scan over the merged list is equivalent).
243fn ucs_in_ranges(code: c_int, ranges: &[(u32, u32)]) -> c_int {
244    let v = code as u32;
245    for &(lo, hi) in ranges {
246        if v >= lo && v <= hi {
247            return 1;
248        }
249    }
250    0
251}
252
253/// Append an integer in decimal to `v`.
254fn append_int(v: &mut Vec<u8>, n: i32) {
255    let mut x = n as i64;
256    if x < 0 {
257        v.push(b'-');
258        x = -x;
259    }
260    let mut buf = [0u8; 20];
261    let mut i = 0usize;
262    loop {
263        buf[i] = b'0' + (x % 10) as u8;
264        i += 1;
265        x /= 10;
266        if x == 0 {
267            break;
268        }
269    }
270    while i > 0 {
271        i -= 1;
272        v.push(buf[i]);
273    }
274}
275
276/// Upper-case hex digit for a nibble.
277const fn hex_digit(v: u8) -> u8 {
278    if v < 10 {
279        b'0' + v
280    } else {
281        b'A' + v - 10
282    }
283}
284
285/// Emit `text` (without NUL) through the generic error channel.
286unsafe fn chan_emit(channel: xmlGenericErrorFunc, data: *mut c_void, text: &[u8]) {
287    let mut b = text.to_vec();
288    b.push(0);
289    channel(data, b.as_ptr() as *const c_char);
290}
291
292// ═══════════════════════════════════════════════════════════════════════════════
293// 1. getset family
294// ═══════════════════════════════════════════════════════════════════════════════
295
296/// The deprecated global buffer allocation scheme.
297///
298/// Upstream 2.13+ removed per-buffer allocation schemes; the getter always
299/// returns `XML_BUFFER_ALLOC_EXACT` and the setter is a no-op. The candidate
300/// keeps a process-global slot (defaulting to `XML_BUFFER_ALLOC_EXACT` = 1)
301/// so the get/set pair round-trips while matching the upstream default.
302static XML_BUFFER_ALLOC_SCHEME: AtomicI32 = AtomicI32::new(XML_BUFFER_ALLOC_EXACT as c_int);
303
304/// # UPSTREAM-PARITY
305///
306/// ```c
307/// xmlBufferAllocationScheme xmlGetBufferAllocationScheme(void);
308/// ```
309#[no_mangle]
310pub extern "C" fn xmlGetBufferAllocationScheme() -> xmlBufferAllocationScheme {
311    match XML_BUFFER_ALLOC_SCHEME.load(Ordering::Relaxed) {
312        v if v == XML_BUFFER_ALLOC_DOUBLEIT as c_int => {
313            xmlBufferAllocationScheme::XML_BUFFER_ALLOC_DOUBLEIT
314        }
315        v if v == XML_BUFFER_ALLOC_EXACT as c_int => {
316            xmlBufferAllocationScheme::XML_BUFFER_ALLOC_EXACT
317        }
318        v if v == XML_BUFFER_ALLOC_IMMUTABLE as c_int => {
319            xmlBufferAllocationScheme::XML_BUFFER_ALLOC_IMMUTABLE
320        }
321        v if v == XML_BUFFER_ALLOC_IO as c_int => xmlBufferAllocationScheme::XML_BUFFER_ALLOC_IO,
322        v if v == XML_BUFFER_ALLOC_HYBRID as c_int => {
323            xmlBufferAllocationScheme::XML_BUFFER_ALLOC_HYBRID
324        }
325        _ => xmlBufferAllocationScheme::XML_BUFFER_ALLOC_BOUNDED,
326    }
327}
328
329/// # UPSTREAM-PARITY
330///
331/// ```c
332/// void xmlSetBufferAllocationScheme(xmlBufferAllocationScheme scheme);
333/// ```
334#[no_mangle]
335pub extern "C" fn xmlSetBufferAllocationScheme(scheme: xmlBufferAllocationScheme) {
336    XML_BUFFER_ALLOC_SCHEME.store(scheme as c_int, Ordering::Relaxed);
337}
338
339/// The legacy feature-name list (upstream features.c `xmlFeaturesList[]`,
340/// 42 entries, as exported by the oracle DSO).
341static XML_FEATURES: [&[u8]; 42] = [
342    b"validate\0",
343    b"load subset\0",
344    b"keep blanks\0",
345    b"disable SAX\0",
346    b"fetch external entities\0",
347    b"substitute entities\0",
348    b"gather line info\0",
349    b"user data\0",
350    b"is html\0",
351    b"is standalone\0",
352    b"stop parser\0",
353    b"document\0",
354    b"is well formed\0",
355    b"is valid\0",
356    b"SAX block\0",
357    b"SAX function internalSubset\0",
358    b"SAX function isStandalone\0",
359    b"SAX function hasInternalSubset\0",
360    b"SAX function hasExternalSubset\0",
361    b"SAX function resolveEntity\0",
362    b"SAX function getEntity\0",
363    b"SAX function entityDecl\0",
364    b"SAX function notationDecl\0",
365    b"SAX function attributeDecl\0",
366    b"SAX function elementDecl\0",
367    b"SAX function unparsedEntityDecl\0",
368    b"SAX function setDocumentLocator\0",
369    b"SAX function startDocument\0",
370    b"SAX function endDocument\0",
371    b"SAX function startElement\0",
372    b"SAX function endElement\0",
373    b"SAX function reference\0",
374    b"SAX function characters\0",
375    b"SAX function ignorableWhitespace\0",
376    b"SAX function processingInstruction\0",
377    b"SAX function comment\0",
378    b"SAX function warning\0",
379    b"SAX function error\0",
380    b"SAX function fatalError\0",
381    b"SAX function getParameterEntity\0",
382    b"SAX function cdataBlock\0",
383    b"SAX function externalSubset\0",
384];
385
386/// # UPSTREAM-PARITY
387///
388/// ```c
389/// int xmlGetFeaturesList(int *len, const char **result);
390/// ```
391///
392/// Legacy API (features.c, removed from source but still exported). Copies
393/// up to `*len` feature names into `result` and returns the total number of
394/// features (42). Returns -1 when `*len` is larger than 999.
395#[no_mangle]
396pub unsafe extern "C" fn xmlGetFeaturesList(len: *mut c_int, result: *mut *const c_char) -> c_int {
397    let n = XML_FEATURES.len() as c_int;
398    if len.is_null() || result.is_null() {
399        return n;
400    }
401    let cur = unsafe { *len };
402    if cur > 999 {
403        return -1;
404    }
405    let mut cnt = cur;
406    if cnt > n {
407        cnt = n;
408        unsafe {
409            *len = n;
410        }
411    }
412    if cnt > 0 {
413        for (i, f) in XML_FEATURES.iter().take(cnt as usize).enumerate() {
414            unsafe {
415                *result.add(i) = f.as_ptr() as *const c_char;
416            }
417        }
418    }
419    n
420}
421
422/// Byte offset of each SAX function slot inside `_xmlSAXHandler`
423/// (upstream features.c reads `ctxt->sax-><slot>`; the candidate struct
424/// is `#[repr(C)]` with the same field order, so the offsets match).
425const SAX_FN_OFFSETS: [(&[u8], usize); 27] = [
426    (b"SAX function internalSubset", 0x00),
427    (b"SAX function isStandalone", 0x08),
428    (b"SAX function hasInternalSubset", 0x10),
429    (b"SAX function hasExternalSubset", 0x18),
430    (b"SAX function resolveEntity", 0x20),
431    (b"SAX function getEntity", 0x28),
432    (b"SAX function entityDecl", 0x30),
433    (b"SAX function notationDecl", 0x38),
434    (b"SAX function attributeDecl", 0x40),
435    (b"SAX function elementDecl", 0x48),
436    (b"SAX function unparsedEntityDecl", 0x50),
437    (b"SAX function setDocumentLocator", 0x58),
438    (b"SAX function startDocument", 0x60),
439    (b"SAX function endDocument", 0x68),
440    (b"SAX function startElement", 0x70),
441    (b"SAX function endElement", 0x78),
442    (b"SAX function reference", 0x80),
443    (b"SAX function characters", 0x88),
444    (b"SAX function ignorableWhitespace", 0x90),
445    (b"SAX function processingInstruction", 0x98),
446    (b"SAX function comment", 0xa0),
447    (b"SAX function warning", 0xa8),
448    (b"SAX function error", 0xb0),
449    (b"SAX function fatalError", 0xb8),
450    (b"SAX function getParameterEntity", 0xc0),
451    (b"SAX function cdataBlock", 0xc8),
452    (b"SAX function externalSubset", 0xd0),
453];
454
455/// # UPSTREAM-PARITY
456///
457/// ```c
458/// int xmlGetFeature(xmlParserCtxtPtr ctxt, const char *name, void *result);
459/// ```
460///
461/// Legacy feature getter (features.c). Returns 0 and stores the feature
462/// value at `result`, or -1 on error / unknown feature.
463#[no_mangle]
464pub unsafe extern "C" fn xmlGetFeature(
465    ctxt: *mut _xmlParserCtxt,
466    name: *const c_char,
467    result: *mut c_void,
468) -> c_int {
469    if name.is_null() || result.is_null() || ctxt.is_null() {
470        return -1;
471    }
472    let c = unsafe { &*ctxt };
473    if unsafe { cstr_eq(name, b"validate") } {
474        unsafe { *(result as *mut c_int) = c.validate };
475        return 0;
476    }
477    if unsafe { cstr_eq(name, b"keep blanks") } {
478        unsafe { *(result as *mut c_int) = c.keepBlanks };
479        return 0;
480    }
481    if unsafe { cstr_eq(name, b"disable SAX") } {
482        unsafe { *(result as *mut c_int) = c.disableSAX };
483        return 0;
484    }
485    if unsafe { cstr_eq(name, b"fetch external entities") } {
486        unsafe { *(result as *mut c_int) = c.loadsubset };
487        return 0;
488    }
489    if unsafe { cstr_eq(name, b"substitute entities") } {
490        unsafe { *(result as *mut c_int) = c.replaceEntities };
491        return 0;
492    }
493    if unsafe { cstr_eq(name, b"gather line info") } {
494        unsafe { *(result as *mut c_int) = c.record_info };
495        return 0;
496    }
497    if unsafe { cstr_eq(name, b"user data") } {
498        unsafe { *(result as *mut *mut c_void) = c.userData };
499        return 0;
500    }
501    if unsafe { cstr_eq(name, b"is html") } {
502        unsafe { *(result as *mut c_int) = c.html };
503        return 0;
504    }
505    if unsafe { cstr_eq(name, b"is standalone") } {
506        unsafe { *(result as *mut c_int) = c.standalone };
507        return 0;
508    }
509    if unsafe { cstr_eq(name, b"document") } {
510        unsafe { *(result as *mut *mut _xmlDoc) = c.myDoc };
511        return 0;
512    }
513    if unsafe { cstr_eq(name, b"is well formed") } {
514        unsafe { *(result as *mut c_int) = c.wellFormed };
515        return 0;
516    }
517    if unsafe { cstr_eq(name, b"is valid") } {
518        unsafe { *(result as *mut c_int) = c.valid };
519        return 0;
520    }
521    if unsafe { cstr_eq(name, b"SAX block") } {
522        unsafe { *(result as *mut *mut _xmlSAXHandler) = c.sax };
523        return 0;
524    }
525    for &(feat, off) in &SAX_FN_OFFSETS {
526        if unsafe { cstr_eq(name, feat) } {
527            if c.sax.is_null() {
528                return -1;
529            }
530            let slot = (c.sax as *mut u8).add(off) as *mut *mut c_void;
531            unsafe { *(result as *mut *mut c_void) = *slot };
532            return 0;
533        }
534    }
535    -1
536}
537
538/// # UPSTREAM-PARITY
539///
540/// ```c
541/// int xmlSetFeature(xmlParserCtxtPtr ctxt, const char *name, void *value);
542/// ```
543///
544/// Legacy feature setter (features.c). Returns 0 on success, -1 on error /
545/// unknown feature. Enabling "validate" also wires up the default validity
546/// error/warning handlers on the context's validation context.
547#[no_mangle]
548pub unsafe extern "C" fn xmlSetFeature(
549    ctxt: *mut _xmlParserCtxt,
550    name: *const c_char,
551    value: *mut c_void,
552) -> c_int {
553    if name.is_null() || value.is_null() || ctxt.is_null() {
554        return -1;
555    }
556    let c = unsafe { &mut *ctxt };
557    if unsafe { cstr_eq(name, b"validate") } {
558        let val = unsafe { *(value as *const c_int) };
559        if c.validate == 0 && val != 0 {
560            if c.vctxt.warning.is_none() {
561                c.vctxt.warning = Some(crate::xml::errors::XML_PARSER_VALIDITY_WARNING_SAX1);
562            }
563            if c.vctxt.error.is_none() {
564                c.vctxt.error = Some(crate::xml::errors::XML_PARSER_VALIDITY_ERROR_SAX1);
565            }
566            c.vctxt.valid = 0;
567        }
568        c.validate = val;
569        return 0;
570    }
571    if unsafe { cstr_eq(name, b"keep blanks") } {
572        c.keepBlanks = unsafe { *(value as *const c_int) };
573        return 0;
574    }
575    if unsafe { cstr_eq(name, b"disable SAX") } {
576        c.disableSAX = unsafe { *(value as *const c_int) };
577        return 0;
578    }
579    if unsafe { cstr_eq(name, b"fetch external entities") } {
580        c.loadsubset = unsafe { *(value as *const c_int) };
581        return 0;
582    }
583    if unsafe { cstr_eq(name, b"substitute entities") } {
584        c.replaceEntities = unsafe { *(value as *const c_int) };
585        return 0;
586    }
587    if unsafe { cstr_eq(name, b"gather line info") } {
588        c.record_info = unsafe { *(value as *const c_int) };
589        return 0;
590    }
591    if unsafe { cstr_eq(name, b"user data") } {
592        c.userData = unsafe { *(value as *const *mut c_void) };
593        return 0;
594    }
595    if unsafe { cstr_eq(name, b"is html") } {
596        c.html = unsafe { *(value as *const c_int) };
597        return 0;
598    }
599    if unsafe { cstr_eq(name, b"is standalone") } {
600        c.standalone = unsafe { *(value as *const c_int) };
601        return 0;
602    }
603    if unsafe { cstr_eq(name, b"document") } {
604        c.myDoc = unsafe { *(value as *const *mut _xmlDoc) };
605        return 0;
606    }
607    if unsafe { cstr_eq(name, b"is well formed") } {
608        c.wellFormed = unsafe { *(value as *const c_int) };
609        return 0;
610    }
611    if unsafe { cstr_eq(name, b"is valid") } {
612        c.valid = unsafe { *(value as *const c_int) };
613        return 0;
614    }
615    if unsafe { cstr_eq(name, b"SAX block") } {
616        c.sax = unsafe { *(value as *const *mut _xmlSAXHandler) };
617        return 0;
618    }
619    for &(feat, off) in &SAX_FN_OFFSETS {
620        if unsafe { cstr_eq(name, feat) } {
621            if c.sax.is_null() {
622                return -1;
623            }
624            let slot = (c.sax as *mut u8).add(off) as *mut *mut c_void;
625            unsafe { *slot = *(value as *const *mut c_void) };
626            return 0;
627        }
628    }
629    -1
630}
631
632/// # UPSTREAM-PARITY
633///
634/// ```c
635/// xmlGlobalStatePtr xmlGetGlobalState(void);
636/// ```
637///
638/// Deprecated (globals.c): returns the global state, which the candidate
639/// does not keep — the oracle DSO itself returns NULL.
640#[no_mangle]
641pub const unsafe extern "C" fn xmlGetGlobalState() -> *mut c_void {
642    ptr::null_mut()
643}
644
645/// # UPSTREAM-PARITY
646///
647/// ```c
648/// xmlNodePtr xmlGetLastChild(const xmlNode *parent);
649/// ```
650#[no_mangle]
651pub const unsafe extern "C" fn xmlGetLastChild(parent: *const _xmlNode) -> *mut _xmlNode {
652    if parent.is_null() || unsafe { (*parent).type_ == XML_NAMESPACE_DECL as c_int } {
653        return ptr::null_mut();
654    }
655    unsafe { (*parent).last }
656}
657
658/// # UPSTREAM-PARITY
659///
660/// ```c
661/// xmlChar *xmlGetNoNsProp(const xmlNode *node, const xmlChar *name);
662/// ```
663///
664/// Value of the no-namespace attribute `name` (with the DTD default/fixed
665/// declaration fallback), or NULL.
666#[no_mangle]
667pub unsafe extern "C" fn xmlGetNoNsProp(
668    node: *const _xmlNode,
669    name: *const xmlChar,
670) -> *mut xmlChar {
671    if node.is_null() || unsafe { (*node).type_ != XML_ELEMENT_NODE as c_int } || name.is_null() {
672        return ptr::null_mut();
673    }
674    let mut prop = unsafe { (*node).properties };
675    while !prop.is_null() {
676        if unsafe { (*prop).ns.is_null() }
677            && unsafe { crate::abi::exports_xml2::xmlStrEqual((*prop).name, name) != 0 }
678        {
679            return get_prop_value(prop);
680        }
681        prop = unsafe { (*prop).next };
682    }
683    dtd_default_attr(node, name, ptr::null())
684}
685
686/// The DTD default/fixed attribute declaration fallback of upstream
687/// `xmlGetPropNodeInternal` (useDTD == 1).
688unsafe fn dtd_default_attr(
689    node: *const _xmlNode,
690    name: *const xmlChar,
691    ns_uri: *const xmlChar,
692) -> *mut xmlChar {
693    let doc = unsafe { (*node).doc };
694    if doc.is_null() || unsafe { (*doc).intSubset.is_null() } {
695        return ptr::null_mut();
696    }
697    // Build the element QName for the DTD lookup.
698    let mut tmp: *mut xmlChar = ptr::null_mut();
699
700    let ns = unsafe { (*node).ns };
701    let elem_qname: *const xmlChar = if !ns.is_null() && !unsafe { (*ns).prefix.is_null() } {
702        tmp = unsafe { crate::abi::exports_xml2::xmlStrdup((*ns).prefix) };
703        if !tmp.is_null() {
704            let colon = b":\0";
705            tmp = unsafe {
706                crate::abi::exports_xml2::xmlStrcat(tmp, colon.as_ptr() as *const xmlChar)
707            };
708        }
709        if !tmp.is_null() {
710            tmp = unsafe { crate::abi::exports_xml2::xmlStrcat(tmp, (*node).name) };
711        }
712        if tmp.is_null() {
713            return ptr::null_mut();
714        }
715        tmp
716    } else {
717        unsafe { (*node).name }
718    };
719
720    let mut attr_decl: *mut _xmlAttribute = ptr::null_mut();
721    let doc = unsafe { &*doc };
722    let xml_ns = b"http://www.w3.org/XML/1998/namespace\0";
723    if ns_uri.is_null() {
724        attr_decl = crate::xml::validation::get_dtd_qattr_desc(
725            doc.intSubset,
726            elem_qname,
727            name,
728            ptr::null(),
729        );
730        if attr_decl.is_null() && !doc.extSubset.is_null() {
731            attr_decl = crate::xml::validation::get_dtd_qattr_desc(
732                doc.extSubset,
733                elem_qname,
734                name,
735                ptr::null(),
736            );
737        }
738    } else if unsafe {
739        crate::abi::exports_xml2::xmlStrEqual(ns_uri, xml_ns.as_ptr() as *const xmlChar) != 0
740    } {
741        let xml_prefix = b"xml\0";
742        attr_decl = crate::xml::validation::get_dtd_qattr_desc(
743            doc.intSubset,
744            elem_qname,
745            name,
746            xml_prefix.as_ptr() as *const xmlChar,
747        );
748        if attr_decl.is_null() && !doc.extSubset.is_null() {
749            attr_decl = crate::xml::validation::get_dtd_qattr_desc(
750                doc.extSubset,
751                elem_qname,
752                name,
753                xml_prefix.as_ptr() as *const xmlChar,
754            );
755        }
756    } else {
757        // The ugly case: search using the prefixes of in-scope ns-decls
758        // corresponding to ns_uri.
759        let ns_list = crate::xml::tree::get_ns_list((*node).doc, node as *mut _xmlNode);
760        if ns_list.is_null() {
761            if !tmp.is_null() {
762                unsafe { xmlFreeImpl(tmp as *mut c_void) };
763            }
764            return ptr::null_mut();
765        }
766        let mut cur = ns_list;
767        while !unsafe { *cur }.is_null() {
768            let n = unsafe { *cur };
769            if !unsafe { (*n).href }.is_null()
770                && unsafe { crate::abi::exports_xml2::xmlStrEqual((*n).href, ns_uri) != 0 }
771            {
772                attr_decl = crate::xml::validation::get_dtd_qattr_desc(
773                    doc.intSubset,
774                    elem_qname,
775                    name,
776                    (*n).prefix,
777                );
778                if attr_decl.is_null() && !doc.extSubset.is_null() {
779                    attr_decl = crate::xml::validation::get_dtd_qattr_desc(
780                        doc.extSubset,
781                        elem_qname,
782                        name,
783                        (*n).prefix,
784                    );
785                }
786                if !attr_decl.is_null() {
787                    break;
788                }
789            }
790            cur = cur.add(1);
791        }
792        unsafe { xmlFreeImpl(ns_list as *mut c_void) };
793    }
794    if !tmp.is_null() {
795        unsafe { xmlFreeImpl(tmp as *mut c_void) };
796    }
797
798    if !attr_decl.is_null() && !unsafe { (*attr_decl).defaultValue.is_null() } {
799        return unsafe { crate::abi::exports_xml2::xmlStrdup((*attr_decl).defaultValue) };
800    }
801    ptr::null_mut()
802}
803
804/// Value of an attribute node (upstream `xmlGetPropNodeValueInternal`):
805/// content of the attribute's children for attribute nodes, the default
806/// value for attribute declarations.
807unsafe fn get_prop_value(prop: *mut _xmlAttr) -> *mut xmlChar {
808    if prop.is_null() {
809        return ptr::null_mut();
810    }
811    if unsafe { (*prop).type_ == XML_ATTRIBUTE_NODE as c_int } {
812        unsafe { crate::xml::tree::node_get_content(prop as *mut _xmlNode) }
813    } else if unsafe { (*prop).type_ == XML_ATTRIBUTE_DECL as c_int } {
814        let a = prop as *mut _xmlAttribute;
815        unsafe { crate::abi::exports_xml2::xmlStrdup((*a).defaultValue) }
816    } else {
817        ptr::null_mut()
818    }
819}
820
821/// # UPSTREAM-PARITY
822///
823/// ```c
824/// xmlChar *xmlGetNodePath(const xmlNode *node);
825/// ```
826///
827/// Build an XPath-like path for `node` (tree.c `xmlGetNodePath`), or NULL
828/// on error. The result is allocated with xmlMalloc.
829#[no_mangle]
830pub unsafe extern "C" fn xmlGetNodePath(node: *const _xmlNode) -> *mut xmlChar {
831    if node.is_null() || unsafe { (*node).type_ == XML_NAMESPACE_DECL as c_int } {
832        return ptr::null_mut();
833    }
834    unsafe {
835        // Collect the ancestor chain (node, parent, ..., root).
836        let mut num_nodes = 0usize;
837        let mut cur: *const _xmlNode = node;
838        while !cur.is_null() {
839            num_nodes += 1;
840            cur = (*cur).parent;
841        }
842        let mut nodes: Vec<*const _xmlNode> = Vec::with_capacity(num_nodes);
843        cur = node;
844        while !cur.is_null() && nodes.len() < num_nodes {
845            nodes.push(cur);
846            cur = (*cur).parent;
847        }
848
849        let mut out: Vec<u8> = Vec::new();
850        let mut i = nodes.len();
851        while i > 0 {
852            let mut occur: i32 = 0;
853            i -= 1;
854            let cur = nodes[i];
855            let t = (*cur).type_;
856
857            if t == XML_DOCUMENT_NODE as c_int || t == XML_HTML_DOCUMENT_NODE as c_int {
858                if i == 0 {
859                    out.push(b'/');
860                }
861            } else if t == XML_ELEMENT_NODE as c_int {
862                let mut generic = 0;
863                out.push(b'/');
864                let ns = (*cur).ns;
865                if !ns.is_null() {
866                    if !(*ns).prefix.is_null() {
867                        append_xmlstr(&mut out, (*ns).prefix);
868                        out.push(b':');
869                        append_xmlstr(&mut out, (*cur).name);
870                    } else {
871                        // Cannot express named elements in the default
872                        // namespace, so use "*".
873                        generic = 1;
874                        out.push(b'*');
875                    }
876                } else {
877                    append_xmlstr(&mut out, (*cur).name);
878                }
879                // Thumbler index computation.
880                let mut tmp = (*cur).prev;
881                while !tmp.is_null() {
882                    if (*tmp).type_ == XML_ELEMENT_NODE as c_int
883                        && (generic != 0
884                            || (crate::abi::exports_xml2::xmlStrEqual((*cur).name, (*tmp).name)
885                                != 0
886                                && ((*tmp).ns == ns
887                                    || (!(*tmp).ns.is_null()
888                                        && !ns.is_null()
889                                        && crate::abi::exports_xml2::xmlStrEqual(
890                                            (*ns).prefix,
891                                            (*(*tmp).ns).prefix,
892                                        ) != 0))))
893                    {
894                        occur += 1;
895                    }
896                    tmp = (*tmp).prev;
897                }
898                if occur == 0 {
899                    tmp = (*cur).next;
900                    while !tmp.is_null() && occur == 0 {
901                        if (*tmp).type_ == XML_ELEMENT_NODE as c_int
902                            && (generic != 0
903                                || (crate::abi::exports_xml2::xmlStrEqual(
904                                    (*cur).name,
905                                    (*tmp).name,
906                                ) != 0
907                                    && ((*tmp).ns == ns
908                                        || (!(*tmp).ns.is_null()
909                                            && !ns.is_null()
910                                            && crate::abi::exports_xml2::xmlStrEqual(
911                                                (*ns).prefix,
912                                                (*(*tmp).ns).prefix,
913                                            ) != 0))))
914                        {
915                            occur += 1;
916                        }
917                        tmp = (*tmp).next;
918                    }
919                    if occur != 0 {
920                        occur = 1;
921                    }
922                } else {
923                    occur += 1;
924                }
925            } else if t == XML_COMMENT_NODE as c_int {
926                out.extend_from_slice(b"/comment()");
927                let mut tmp = (*cur).prev;
928                while !tmp.is_null() {
929                    if (*tmp).type_ == XML_COMMENT_NODE as c_int {
930                        occur += 1;
931                    }
932                    tmp = (*tmp).prev;
933                }
934                if occur == 0 {
935                    tmp = (*cur).next;
936                    while !tmp.is_null() && occur == 0 {
937                        if (*tmp).type_ == XML_COMMENT_NODE as c_int {
938                            occur += 1;
939                        }
940                        tmp = (*tmp).next;
941                    }
942                    if occur != 0 {
943                        occur = 1;
944                    }
945                } else {
946                    occur += 1;
947                }
948            } else if t == XML_TEXT_NODE as c_int || t == XML_CDATA_SECTION_NODE as c_int {
949                out.extend_from_slice(b"/text()");
950                let mut tmp = (*cur).prev;
951                while !tmp.is_null() {
952                    if (*tmp).type_ == XML_TEXT_NODE as c_int
953                        || (*tmp).type_ == XML_CDATA_SECTION_NODE as c_int
954                    {
955                        occur += 1;
956                    }
957                    tmp = (*tmp).prev;
958                }
959                if occur == 0 {
960                    tmp = (*cur).next;
961                    while !tmp.is_null() {
962                        if (*tmp).type_ == XML_TEXT_NODE as c_int
963                            || (*tmp).type_ == XML_CDATA_SECTION_NODE as c_int
964                        {
965                            occur = 1;
966                            break;
967                        }
968                        tmp = (*tmp).next;
969                    }
970                } else {
971                    occur += 1;
972                }
973            } else if t == XML_PI_NODE as c_int {
974                out.extend_from_slice(b"/processing-instruction('");
975                append_xmlstr(&mut out, (*cur).name);
976                out.extend_from_slice(b"')");
977                let mut tmp = (*cur).prev;
978                while !tmp.is_null() {
979                    if (*tmp).type_ == XML_PI_NODE as c_int
980                        && crate::abi::exports_xml2::xmlStrEqual((*cur).name, (*tmp).name) != 0
981                    {
982                        occur += 1;
983                    }
984                    tmp = (*tmp).prev;
985                }
986                if occur == 0 {
987                    tmp = (*cur).next;
988                    while !tmp.is_null() && occur == 0 {
989                        if (*tmp).type_ == XML_PI_NODE as c_int
990                            && crate::abi::exports_xml2::xmlStrEqual((*cur).name, (*tmp).name) != 0
991                        {
992                            occur += 1;
993                        }
994                        tmp = (*tmp).next;
995                    }
996                    if occur != 0 {
997                        occur = 1;
998                    }
999                } else {
1000                    occur += 1;
1001                }
1002            } else if t == XML_ATTRIBUTE_NODE as c_int {
1003                out.extend_from_slice(b"/@");
1004                let ns = (*cur).ns;
1005                if !ns.is_null() && !(*ns).prefix.is_null() {
1006                    append_xmlstr(&mut out, (*ns).prefix);
1007                    out.push(b':');
1008                }
1009                append_xmlstr(&mut out, (*cur).name);
1010            } else {
1011                return ptr::null_mut();
1012            }
1013
1014            if occur > 0 {
1015                out.push(b'[');
1016                append_int(&mut out, occur);
1017                out.push(b']');
1018            }
1019        }
1020
1021        out.push(0);
1022        let ret = xmlMallocImpl(out.len());
1023        if ret.is_null() {
1024            return ptr::null_mut();
1025        }
1026        ptr::copy_nonoverlapping(out.as_ptr(), ret as *mut u8, out.len());
1027        ret as *mut xmlChar
1028    }
1029}
1030
1031/// The five XML predefined entities, mirroring upstream entities.c static
1032/// `xmlEntityLt`/`xmlEntityGt`/`xmlEntityAmp`/`xmlEntityQuot`/`xmlEntityApos`.
1033///
1034/// These are WRITABLE (upstream declares them as non-const globals): PHP's
1035/// libxml wrapper stores its `php_libxml_node_ptr` in `node->_private` when a
1036/// DOM wrapper is created around an entity declaration (a doc-less
1037/// `new DOMEntityReference("amp")` binds the predefined declaration as its
1038/// child, and property reads wrap it). A plain `static` here would land in
1039/// `.data.rel.ro`, which full RELRO mprotects read-only — PHP's `_private`
1040/// store then faults. `static mut` keeps them in the writable `.data`.
1041static mut PREDEFINED_LT_DATA: _xmlEntity = _xmlEntity {
1042    _private: ptr::null_mut(),
1043    type_: XML_ENTITY_DECL as c_int,
1044    name: b"lt\0" as *const u8 as *const xmlChar,
1045    children: ptr::null_mut(),
1046    last: ptr::null_mut(),
1047    parent: ptr::null_mut(),
1048    next: ptr::null_mut(),
1049    prev: ptr::null_mut(),
1050    doc: ptr::null_mut(),
1051    orig: ptr::null_mut(),
1052    content: b"<\0" as *const u8 as *mut xmlChar,
1053    length: 1,
1054    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
1055    ExternalID: ptr::null(),
1056    SystemID: ptr::null(),
1057    nexte: ptr::null_mut(),
1058    URI: ptr::null(),
1059    owner: 0,
1060    flags: 0,
1061    expandedSize: 0,
1062};
1063static mut PREDEFINED_GT_DATA: _xmlEntity = _xmlEntity {
1064    _private: ptr::null_mut(),
1065    type_: XML_ENTITY_DECL as c_int,
1066    name: b"gt\0" as *const u8 as *const xmlChar,
1067    children: ptr::null_mut(),
1068    last: ptr::null_mut(),
1069    parent: ptr::null_mut(),
1070    next: ptr::null_mut(),
1071    prev: ptr::null_mut(),
1072    doc: ptr::null_mut(),
1073    orig: ptr::null_mut(),
1074    content: b">\0" as *const u8 as *mut xmlChar,
1075    length: 1,
1076    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
1077    ExternalID: ptr::null(),
1078    SystemID: ptr::null(),
1079    nexte: ptr::null_mut(),
1080    URI: ptr::null(),
1081    owner: 0,
1082    flags: 0,
1083    expandedSize: 0,
1084};
1085static mut PREDEFINED_AMP_DATA: _xmlEntity = _xmlEntity {
1086    _private: ptr::null_mut(),
1087    type_: XML_ENTITY_DECL as c_int,
1088    name: b"amp\0" as *const u8 as *const xmlChar,
1089    children: ptr::null_mut(),
1090    last: ptr::null_mut(),
1091    parent: ptr::null_mut(),
1092    next: ptr::null_mut(),
1093    prev: ptr::null_mut(),
1094    doc: ptr::null_mut(),
1095    orig: ptr::null_mut(),
1096    content: b"&\0" as *const u8 as *mut xmlChar,
1097    length: 1,
1098    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
1099    ExternalID: ptr::null(),
1100    SystemID: ptr::null(),
1101    nexte: ptr::null_mut(),
1102    URI: ptr::null(),
1103    owner: 0,
1104    flags: 0,
1105    expandedSize: 0,
1106};
1107static mut PREDEFINED_QUOT_DATA: _xmlEntity = _xmlEntity {
1108    _private: ptr::null_mut(),
1109    type_: XML_ENTITY_DECL as c_int,
1110    name: b"quot\0" as *const u8 as *const xmlChar,
1111    children: ptr::null_mut(),
1112    last: ptr::null_mut(),
1113    parent: ptr::null_mut(),
1114    next: ptr::null_mut(),
1115    prev: ptr::null_mut(),
1116    doc: ptr::null_mut(),
1117    orig: ptr::null_mut(),
1118    content: b"\"\0" as *const u8 as *mut xmlChar,
1119    length: 1,
1120    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
1121    ExternalID: ptr::null(),
1122    SystemID: ptr::null(),
1123    nexte: ptr::null_mut(),
1124    URI: ptr::null(),
1125    owner: 0,
1126    flags: 0,
1127    expandedSize: 0,
1128};
1129static mut PREDEFINED_APOS_DATA: _xmlEntity = _xmlEntity {
1130    _private: ptr::null_mut(),
1131    type_: XML_ENTITY_DECL as c_int,
1132    name: b"apos\0" as *const u8 as *const xmlChar,
1133    children: ptr::null_mut(),
1134    last: ptr::null_mut(),
1135    parent: ptr::null_mut(),
1136    next: ptr::null_mut(),
1137    prev: ptr::null_mut(),
1138    doc: ptr::null_mut(),
1139    orig: ptr::null_mut(),
1140    content: b"'\0" as *const u8 as *mut xmlChar,
1141    length: 1,
1142    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
1143    ExternalID: ptr::null(),
1144    SystemID: ptr::null(),
1145    nexte: ptr::null_mut(),
1146    URI: ptr::null(),
1147    owner: 0,
1148    flags: 0,
1149    expandedSize: 0,
1150};
1151
1152/// # UPSTREAM-PARITY
1153///
1154/// ```c
1155/// xmlEntityPtr xmlGetPredefinedEntity(const xmlChar *name);
1156/// ```
1157///
1158/// Returns a pointer to the static predefined entity, or NULL.
1159#[no_mangle]
1160pub unsafe extern "C" fn xmlGetPredefinedEntity(name: *const xmlChar) -> *mut _xmlEntity {
1161    if name.is_null() {
1162        return ptr::null_mut();
1163    }
1164    unsafe {
1165        if crate::abi::exports_xml2::xmlStrEqual(name, b"lt\0" as *const u8 as *const xmlChar) != 0
1166        {
1167            return std::ptr::addr_of_mut!(PREDEFINED_LT_DATA);
1168        }
1169        if crate::abi::exports_xml2::xmlStrEqual(name, b"gt\0" as *const u8 as *const xmlChar) != 0
1170        {
1171            return std::ptr::addr_of_mut!(PREDEFINED_GT_DATA);
1172        }
1173        if crate::abi::exports_xml2::xmlStrEqual(name, b"amp\0" as *const u8 as *const xmlChar) != 0
1174        {
1175            return std::ptr::addr_of_mut!(PREDEFINED_AMP_DATA);
1176        }
1177        if crate::abi::exports_xml2::xmlStrEqual(name, b"quot\0" as *const u8 as *const xmlChar)
1178            != 0
1179        {
1180            return std::ptr::addr_of_mut!(PREDEFINED_QUOT_DATA);
1181        }
1182        if crate::abi::exports_xml2::xmlStrEqual(name, b"apos\0" as *const u8 as *const xmlChar)
1183            != 0
1184        {
1185            return std::ptr::addr_of_mut!(PREDEFINED_APOS_DATA);
1186        }
1187    }
1188    ptr::null_mut()
1189}
1190
1191/// # UPSTREAM-PARITY
1192///
1193/// ```c
1194/// int xmlGetThreadId(void);
1195/// ```
1196///
1197/// Upstream returns `pthread_self()` (or 0 when single-threaded); the
1198/// candidate is single-threaded, so 0 matches the oracle's common path.
1199#[no_mangle]
1200pub const extern "C" fn xmlGetThreadId() -> c_int {
1201    0
1202}
1203
1204/// # UPSTREAM-PARITY
1205///
1206/// ```c
1207/// int xmlGetUTF8Char(const unsigned char *utf, int *len);
1208/// ```
1209///
1210/// Decode the UTF-8 character at `utf`, set `*len` to its byte length and
1211/// return the code point; -1 (and `*len = 0`) on error.
1212#[no_mangle]
1213pub unsafe extern "C" fn xmlGetUTF8Char(utf: *const u8, len: *mut c_int) -> c_int {
1214    unsafe {
1215        if utf.is_null() || len.is_null() {
1216            if !len.is_null() {
1217                *len = 0;
1218            }
1219            return -1;
1220        }
1221        let mut c: u32 = *utf as u32;
1222        if c < 0x80 {
1223            if *len < 1 {
1224                *len = 0;
1225                return -1;
1226            }
1227            *len = 1;
1228        } else {
1229            if *len < 2 || (*utf.add(1) & 0xc0) != 0x80 {
1230                *len = 0;
1231                return -1;
1232            }
1233            if c < 0xe0 {
1234                if c < 0xc2 {
1235                    *len = 0;
1236                    return -1;
1237                }
1238                *len = 2;
1239                c = (c & 0x1f) << 6;
1240                c |= (*utf.add(1) & 0x3f) as u32;
1241            } else {
1242                if *len < 3 || (*utf.add(2) & 0xc0) != 0x80 {
1243                    *len = 0;
1244                    return -1;
1245                }
1246                if c < 0xf0 {
1247                    *len = 3;
1248                    c = (c & 0x0f) << 12;
1249                    c |= ((*utf.add(1) & 0x3f) as u32) << 6;
1250                    c |= (*utf.add(2) & 0x3f) as u32;
1251                    if c < 0x800 || (0xd800..0xe000).contains(&c) {
1252                        *len = 0;
1253                        return -1;
1254                    }
1255                } else {
1256                    if *len < 4 || (*utf.add(3) & 0xc0) != 0x80 {
1257                        *len = 0;
1258                        return -1;
1259                    }
1260                    *len = 4;
1261                    c = (c & 0x07) << 18;
1262                    c |= ((*utf.add(1) & 0x3f) as u32) << 12;
1263                    c |= ((*utf.add(2) & 0x3f) as u32) << 6;
1264                    c |= (*utf.add(3) & 0x3f) as u32;
1265                    if !(0x10000..0x110000).contains(&c) {
1266                        *len = 0;
1267                        return -1;
1268                    }
1269                }
1270            }
1271        }
1272        c as c_int
1273    }
1274}
1275
1276/// Type of the entity-reference callback (upstream entities.h
1277/// `xmlEntityReferenceFunc`).
1278pub type xmlEntityReferenceFunc = unsafe extern "C" fn(
1279    entity: *mut _xmlEntity,
1280    firstChild: *mut _xmlNode,
1281    lastChild: *mut _xmlNode,
1282);
1283
1284/// The global entity-reference callback (legacy entities.c global; the
1285/// candidate's parser does not invoke it, mirroring modern upstream where
1286/// `xmlSetEntityReferenceFunc` is a no-op).
1287static ENTITY_REFERENCE_FUNC: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
1288
1289/// # UPSTREAM-PARITY
1290///
1291/// ```c
1292/// void xmlSetEntityReferenceFunc(xmlEntityReferenceFunc func);
1293/// ```
1294#[no_mangle]
1295pub unsafe extern "C" fn xmlSetEntityReferenceFunc(func: Option<xmlEntityReferenceFunc>) {
1296    ENTITY_REFERENCE_FUNC.store(
1297        func.map_or(ptr::null_mut(), |f| f as *const c_void as *mut c_void),
1298        Ordering::Relaxed,
1299    );
1300}
1301
1302/// # UPSTREAM-PARITY
1303///
1304/// ```c
1305/// int xmlSetListDoc(xmlNode *list, xmlDoc *doc);
1306/// ```
1307///
1308/// Set `doc` on every node of the sibling list (tree.c). Returns 0, or -1
1309/// if a subtree assignment failed.
1310#[no_mangle]
1311pub unsafe extern "C" fn xmlSetListDoc(list: *mut _xmlNode, doc: *mut _xmlDoc) -> c_int {
1312    if list.is_null() || unsafe { (*list).type_ == XML_NAMESPACE_DECL as c_int } {
1313        return 0;
1314    }
1315    let mut ret = 0;
1316    let mut cur = list;
1317    while !cur.is_null() {
1318        if unsafe { (*cur).doc != doc }
1319            && unsafe { crate::abi::exports_tree::xmlSetTreeDoc(cur, doc) } < 0
1320        {
1321            ret = -1;
1322        }
1323        cur = unsafe { (*cur).next };
1324    }
1325    ret
1326}
1327
1328// ═══════════════════════════════════════════════════════════════════════════════
1329// 2. module family — xmlModule* (xmlmodule.c, dlopen/dlsym/dlclose)
1330// ═══════════════════════════════════════════════════════════════════════════════
1331
1332/// Module handle (upstream `struct _xmlModule`, xmlmodule.c).
1333#[derive(Debug)]
1334#[repr(C)]
1335pub struct _xmlModule {
1336    pub name: *mut xmlChar,
1337    pub handle: *mut c_void,
1338}
1339
1340extern "C" {
1341    fn dlopen(filename: *const c_char, flags: c_int) -> *mut c_void;
1342    fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
1343    fn dlclose(handle: *mut c_void) -> c_int;
1344    fn dlerror() -> *mut c_char;
1345}
1346
1347// RTLD_NOW | RTLD_GLOBAL (glibc; matches upstream xmlModulePlatformOpen).
1348const RTLD_NOW: c_int = 2;
1349const RTLD_GLOBAL: c_int = 0x100;
1350
1351/// # UPSTREAM-PARITY
1352///
1353/// ```c
1354/// xmlModulePtr xmlModuleOpen(const char *filename, int options);
1355/// ```
1356#[no_mangle]
1357pub unsafe extern "C" fn xmlModuleOpen(
1358    filename: *const c_char,
1359    _options: c_int,
1360) -> *mut _xmlModule {
1361    let module = unsafe { xmlMallocZero(size_of::<_xmlModule>()) } as *mut _xmlModule;
1362    if module.is_null() {
1363        return ptr::null_mut();
1364    }
1365    let handle = unsafe { dlopen(filename, RTLD_GLOBAL | RTLD_NOW) };
1366    if handle.is_null() {
1367        unsafe { xmlFreeImpl(module as *mut c_void) };
1368        return ptr::null_mut();
1369    }
1370    unsafe {
1371        (*module).handle = handle;
1372        if !filename.is_null() {
1373            (*module).name = crate::abi::exports_xml2::xmlStrdup(filename as *const xmlChar);
1374        }
1375    }
1376    module
1377}
1378
1379/// # UPSTREAM-PARITY
1380///
1381/// ```c
1382/// int xmlModuleSymbol(xmlModule *module, const char *name, void **symbol);
1383/// ```
1384#[no_mangle]
1385pub unsafe extern "C" fn xmlModuleSymbol(
1386    module: *mut _xmlModule,
1387    name: *const c_char,
1388    symbol: *mut *mut c_void,
1389) -> c_int {
1390    if module.is_null() || symbol.is_null() || name.is_null() {
1391        return -1;
1392    }
1393    unsafe {
1394        *symbol = dlsym((*module).handle, name);
1395        if !dlerror().is_null() {
1396            return -1;
1397        }
1398    }
1399    0
1400}
1401
1402/// # UPSTREAM-PARITY
1403///
1404/// ```c
1405/// int xmlModuleClose(xmlModule *module);
1406/// ```
1407#[no_mangle]
1408pub unsafe extern "C" fn xmlModuleClose(module: *mut _xmlModule) -> c_int {
1409    if module.is_null() {
1410        return -1;
1411    }
1412    let rc = unsafe { dlclose((*module).handle) };
1413    if rc != 0 {
1414        return -2;
1415    }
1416    unsafe { xmlModuleFree(module) }
1417}
1418
1419/// # UPSTREAM-PARITY
1420///
1421/// ```c
1422/// int xmlModuleFree(xmlModule *module);
1423/// ```
1424#[no_mangle]
1425pub unsafe extern "C" fn xmlModuleFree(module: *mut _xmlModule) -> c_int {
1426    if module.is_null() {
1427        return -1;
1428    }
1429    unsafe {
1430        if !(*module).name.is_null() {
1431            xmlFreeImpl((*module).name as *mut c_void);
1432        }
1433        xmlFreeImpl(module as *mut c_void);
1434    }
1435    0
1436}
1437
1438// ═══════════════════════════════════════════════════════════════════════════════
1439// 3. ucs family — xmlUCSIsBlock / xmlUCSIsCat / xmlUCSIsCatCc
1440// ═══════════════════════════════════════════════════════════════════════════════
1441
1442// Generated: UCS block/category name tables (archaeology libxml2-git
1443// codegen/unicode.inc). Each entry: (name, &[(lo, hi)]) sorted by name
1444// (upstream xmlUnicodeBlocks / xmlUnicodeCats).
1445
1446#[rustfmt::skip]
1447static XML_UCS_BLOCKS: &[(&str, &[(u32, u32)])] = &[
1448    ("AegeanNumbers", &[(65792,65855)]),
1449    ("AlphabeticPresentationForms", &[(64256,64335)]),
1450    ("Arabic", &[(1536,1791)]),
1451    ("ArabicPresentationForms-A", &[(64336,65023)]),
1452    ("ArabicPresentationForms-B", &[(65136,65279)]),
1453    ("Armenian", &[(1328,1423)]),
1454    ("Arrows", &[(8592,8703)]),
1455    ("BasicLatin", &[(0,127)]),
1456    ("Bengali", &[(2432,2559)]),
1457    ("BlockElements", &[(9600,9631)]),
1458    ("Bopomofo", &[(12544,12591)]),
1459    ("BopomofoExtended", &[(12704,12735)]),
1460    ("BoxDrawing", &[(9472,9599)]),
1461    ("BraillePatterns", &[(10240,10495)]),
1462    ("Buhid", &[(5952,5983)]),
1463    ("ByzantineMusicalSymbols", &[(118784,119039)]),
1464    ("CJKCompatibility", &[(13056,13311)]),
1465    ("CJKCompatibilityForms", &[(65072,65103)]),
1466    ("CJKCompatibilityIdeographs", &[(63744,64255)]),
1467    ("CJKCompatibilityIdeographsSupplement", &[(194560,195103)]),
1468    ("CJKRadicalsSupplement", &[(11904,12031)]),
1469    ("CJKSymbolsandPunctuation", &[(12288,12351)]),
1470    ("CJKUnifiedIdeographs", &[(19968,40959)]),
1471    ("CJKUnifiedIdeographsExtensionA", &[(13312,19903)]),
1472    ("CJKUnifiedIdeographsExtensionB", &[(131072,173791)]),
1473    ("Cherokee", &[(5024,5119)]),
1474    ("CombiningDiacriticalMarks", &[(768,879)]),
1475    ("CombiningDiacriticalMarksforSymbols", &[(8400,8447)]),
1476    ("CombiningHalfMarks", &[(65056,65071)]),
1477    ("CombiningMarksforSymbols", &[(8400,8447)]),
1478    ("ControlPictures", &[(9216,9279)]),
1479    ("CurrencySymbols", &[(8352,8399)]),
1480    ("CypriotSyllabary", &[(67584,67647)]),
1481    ("Cyrillic", &[(1024,1279)]),
1482    ("CyrillicSupplement", &[(1280,1327)]),
1483    ("Deseret", &[(66560,66639)]),
1484    ("Devanagari", &[(2304,2431)]),
1485    ("Dingbats", &[(9984,10175)]),
1486    ("EnclosedAlphanumerics", &[(9312,9471)]),
1487    ("EnclosedCJKLettersandMonths", &[(12800,13055)]),
1488    ("Ethiopic", &[(4608,4991)]),
1489    ("GeneralPunctuation", &[(8192,8303)]),
1490    ("GeometricShapes", &[(9632,9727)]),
1491    ("Georgian", &[(4256,4351)]),
1492    ("Gothic", &[(66352,66383)]),
1493    ("Greek", &[(880,1023)]),
1494    ("GreekExtended", &[(7936,8191)]),
1495    ("GreekandCoptic", &[(880,1023)]),
1496    ("Gujarati", &[(2688,2815)]),
1497    ("Gurmukhi", &[(2560,2687)]),
1498    ("HalfwidthandFullwidthForms", &[(65280,65519)]),
1499    ("HangulCompatibilityJamo", &[(12592,12687)]),
1500    ("HangulJamo", &[(4352,4607)]),
1501    ("HangulSyllables", &[(44032,55215)]),
1502    ("Hanunoo", &[(5920,5951)]),
1503    ("Hebrew", &[(1424,1535)]),
1504    ("HighPrivateUseSurrogates", &[(56192,56319)]),
1505    ("HighSurrogates", &[(55296,56191)]),
1506    ("Hiragana", &[(12352,12447)]),
1507    ("IPAExtensions", &[(592,687)]),
1508    ("IdeographicDescriptionCharacters", &[(12272,12287)]),
1509    ("Kanbun", &[(12688,12703)]),
1510    ("KangxiRadicals", &[(12032,12255)]),
1511    ("Kannada", &[(3200,3327)]),
1512    ("Katakana", &[(12448,12543)]),
1513    ("KatakanaPhoneticExtensions", &[(12784,12799)]),
1514    ("Khmer", &[(6016,6143)]),
1515    ("KhmerSymbols", &[(6624,6655)]),
1516    ("Lao", &[(3712,3839)]),
1517    ("Latin-1Supplement", &[(128,255)]),
1518    ("LatinExtended-A", &[(256,383)]),
1519    ("LatinExtended-B", &[(384,591)]),
1520    ("LatinExtendedAdditional", &[(7680,7935)]),
1521    ("LetterlikeSymbols", &[(8448,8527)]),
1522    ("Limbu", &[(6400,6479)]),
1523    ("LinearBIdeograms", &[(65664,65791)]),
1524    ("LinearBSyllabary", &[(65536,65663)]),
1525    ("LowSurrogates", &[(56320,57343)]),
1526    ("Malayalam", &[(3328,3455)]),
1527    ("MathematicalAlphanumericSymbols", &[(119808,120831)]),
1528    ("MathematicalOperators", &[(8704,8959)]),
1529    ("MiscellaneousMathematicalSymbols-A", &[(10176,10223)]),
1530    ("MiscellaneousMathematicalSymbols-B", &[(10624,10751)]),
1531    ("MiscellaneousSymbols", &[(9728,9983)]),
1532    ("MiscellaneousSymbolsandArrows", &[(11008,11263)]),
1533    ("MiscellaneousTechnical", &[(8960,9215)]),
1534    ("Mongolian", &[(6144,6319)]),
1535    ("MusicalSymbols", &[(119040,119295)]),
1536    ("Myanmar", &[(4096,4255)]),
1537    ("NumberForms", &[(8528,8591)]),
1538    ("Ogham", &[(5760,5791)]),
1539    ("OldItalic", &[(66304,66351)]),
1540    ("OpticalCharacterRecognition", &[(9280,9311)]),
1541    ("Oriya", &[(2816,2943)]),
1542    ("Osmanya", &[(66688,66735)]),
1543    ("PhoneticExtensions", &[(7424,7551)]),
1544    ("PrivateUse", &[(57344,63743),(983040,1048575),(1048576,1114111)]),
1545    ("PrivateUseArea", &[(57344,63743)]),
1546    ("Runic", &[(5792,5887)]),
1547    ("Shavian", &[(66640,66687)]),
1548    ("Sinhala", &[(3456,3583)]),
1549    ("SmallFormVariants", &[(65104,65135)]),
1550    ("SpacingModifierLetters", &[(688,767)]),
1551    ("Specials", &[(65520,65535)]),
1552    ("SuperscriptsandSubscripts", &[(8304,8351)]),
1553    ("SupplementalArrows-A", &[(10224,10239)]),
1554    ("SupplementalArrows-B", &[(10496,10623)]),
1555    ("SupplementalMathematicalOperators", &[(10752,11007)]),
1556    ("SupplementaryPrivateUseArea-A", &[(983040,1048575)]),
1557    ("SupplementaryPrivateUseArea-B", &[(1048576,1114111)]),
1558    ("Syriac", &[(1792,1871)]),
1559    ("Tagalog", &[(5888,5919)]),
1560    ("Tagbanwa", &[(5984,6015)]),
1561    ("Tags", &[(917504,917631)]),
1562    ("TaiLe", &[(6480,6527)]),
1563    ("TaiXuanJingSymbols", &[(119552,119647)]),
1564    ("Tamil", &[(2944,3071)]),
1565    ("Telugu", &[(3072,3199)]),
1566    ("Thaana", &[(1920,1983)]),
1567    ("Thai", &[(3584,3711)]),
1568    ("Tibetan", &[(3840,4095)]),
1569    ("Ugaritic", &[(66432,66463)]),
1570    ("UnifiedCanadianAboriginalSyllabics", &[(5120,5759)]),
1571    ("VariationSelectors", &[(65024,65039)]),
1572    ("VariationSelectorsSupplement", &[(917760,917999)]),
1573    ("YiRadicals", &[(42128,42191)]),
1574    ("YiSyllables", &[(40960,42127)]),
1575    ("YijingHexagramSymbols", &[(19904,19967)]),
1576];
1577
1578#[rustfmt::skip]
1579static XML_UCS_CATS: &[(&str, &[(u32, u32)])] = &[
1580    ("C", &[(0,31),(127,159),(173,173),(1536,1539),(1757,1757),(1807,1807),(6068,6069),(8203,8207),(8234,8238),(8288,8291),(8298,8303),(55296,55296),(56191,56192),(56319,56320),(57343,57344),(63743,63743),(65279,65279),(65529,65531),(119155,119162),(917505,917505),(917536,917631),(983040,983040),(1048573,1048573),(1048576,1048576),(1114109,1114109)]),
1581    ("Cc", &[(0,31),(127,159)]),
1582    ("Cf", &[(173,173),(1536,1539),(1757,1757),(1807,1807),(6068,6069),(8203,8207),(8234,8238),(8288,8291),(8298,8303),(65279,65279),(65529,65531),(119155,119162),(917505,917505),(917536,917631)]),
1583    ("Co", &[(57344,57344),(63743,63743),(983040,983040),(1048573,1048573),(1048576,1048576),(1114109,1114109)]),
1584    ("Cs", &[(55296,57343)]),
1585    ("L", &[(65,90),(97,122),(170,170),(181,181),(186,186),(192,214),(216,246),(248,566),(592,705),(710,721),(736,740),(750,750),(890,890),(902,902),(904,906),(908,908),(910,929),(931,974),(976,1013),(1015,1019),(1024,1153),(1162,1230),(1232,1269),(1272,1273),(1280,1295),(1329,1366),(1369,1369),(1377,1415),(1488,1514),(1520,1522),(1569,1594),(1600,1610),(1646,1647),(1649,1747),(1749,1749),(1765,1766),(1774,1775),(1786,1788),(1791,1791),(1808,1808),(1810,1839),(1869,1871),(1920,1957),(1969,1969),(2308,2361),(2365,2365),(2384,2384),(2392,2401),(2437,2444),(2447,2448),(2451,2472),(2474,2480),(2482,2482),(2486,2489),(2493,2493),(2524,2525),(2527,2529),(2544,2545),(2565,2570),(2575,2576),(2579,2600),(2602,2608),(2610,2611),(2613,2614),(2616,2617),(2649,2652),(2654,2654),(2674,2676),(2693,2701),(2703,2705),(2707,2728),(2730,2736),(2738,2739),(2741,2745),(2749,2749),(2768,2768),(2784,2785),(2821,2828),(2831,2832),(2835,2856),(2858,2864),(2866,2867),(2869,2873),(2877,2877),(2908,2909),(2911,2913),(2929,2929),(2947,2947),(2949,2954),(2958,2960),(2962,2965),(2969,2970),(2972,2972),(2974,2975),(2979,2980),(2984,2986),(2990,2997),(2999,3001),(3077,3084),(3086,3088),(3090,3112),(3114,3123),(3125,3129),(3168,3169),(3205,3212),(3214,3216),(3218,3240),(3242,3251),(3253,3257),(3261,3261),(3294,3294),(3296,3297),(3333,3340),(3342,3344),(3346,3368),(3370,3385),(3424,3425),(3461,3478),(3482,3505),(3507,3515),(3517,3517),(3520,3526),(3585,3632),(3634,3635),(3648,3654),(3713,3714),(3716,3716),(3719,3720),(3722,3722),(3725,3725),(3732,3735),(3737,3743),(3745,3747),(3749,3749),(3751,3751),(3754,3755),(3757,3760),(3762,3763),(3773,3773),(3776,3780),(3782,3782),(3804,3805),(3840,3840),(3904,3911),(3913,3946),(3976,3979),(4096,4129),(4131,4135),(4137,4138),(4176,4181),(4256,4293),(4304,4344),(4352,4441),(4447,4514),(4520,4601),(4608,4614),(4616,4678),(4680,4680),(4682,4685),(4688,4694),(4696,4696),(4698,4701),(4704,4742),(4744,4744),(4746,4749),(4752,4782),(4784,4784),(4786,4789),(4792,4798),(4800,4800),(4802,4805),(4808,4814),(4816,4822),(4824,4846),(4848,4878),(4880,4880),(4882,4885),(4888,4894),(4896,4934),(4936,4954),(5024,5108),(5121,5740),(5743,5750),(5761,5786),(5792,5866),(5888,5900),(5902,5905),(5920,5937),(5952,5969),(5984,5996),(5998,6000),(6016,6067),(6103,6103),(6108,6108),(6176,6263),(6272,6312),(6400,6428),(6480,6509),(6512,6516),(7424,7531),(7680,7835),(7840,7929),(7936,7957),(7960,7965),(7968,8005),(8008,8013),(8016,8023),(8025,8025),(8027,8027),(8029,8029),(8031,8061),(8064,8116),(8118,8124),(8126,8126),(8130,8132),(8134,8140),(8144,8147),(8150,8155),(8160,8172),(8178,8180),(8182,8188),(8305,8305),(8319,8319),(8450,8450),(8455,8455),(8458,8467),(8469,8469),(8473,8477),(8484,8484),(8486,8486),(8488,8488),(8490,8493),(8495,8497),(8499,8505),(8509,8511),(8517,8521),(12293,12294),(12337,12341),(12347,12348),(12353,12438),(12445,12447),(12449,12538),(12540,12543),(12549,12588),(12593,12686),(12704,12727),(12784,12799),(13312,13312),(19893,19893),(19968,19968),(40869,40869),(40960,42124),(44032,44032),(55203,55203),(63744,64045),(64048,64106),(64256,64262),(64275,64279),(64285,64285),(64287,64296),(64298,64310),(64312,64316),(64318,64318),(64320,64321),(64323,64324),(64326,64433),(64467,64829),(64848,64911),(64914,64967),(65008,65019),(65136,65140),(65142,65276),(65313,65338),(65345,65370),(65382,65470),(65474,65479),(65482,65487),(65490,65495),(65498,65500),(65536,65547),(65549,65574),(65576,65594),(65596,65597),(65599,65613),(65616,65629),(65664,65786),(66304,66334),(66352,66377),(66432,66461),(66560,66717),(67584,67589),(67592,67592),(67594,67637),(67639,67640),(67644,67644),(67647,67647),(119808,119892),(119894,119964),(119966,119967),(119970,119970),(119973,119974),(119977,119980),(119982,119993),(119995,119995),(119997,120003),(120005,120069),(120071,120074),(120077,120084),(120086,120092),(120094,120121),(120123,120126),(120128,120132),(120134,120134),(120138,120144),(120146,120483),(120488,120512),(120514,120538),(120540,120570),(120572,120596),(120598,120628),(120630,120654),(120656,120686),(120688,120712),(120714,120744),(120746,120770),(120772,120777),(131072,131072),(173782,173782),(194560,195101)]),
1586    ("Ll", &[(97,122),(170,170),(181,181),(186,186),(223,246),(248,255),(257,257),(259,259),(261,261),(263,263),(265,265),(267,267),(269,269),(271,271),(273,273),(275,275),(277,277),(279,279),(281,281),(283,283),(285,285),(287,287),(289,289),(291,291),(293,293),(295,295),(297,297),(299,299),(301,301),(303,303),(305,305),(307,307),(309,309),(311,312),(314,314),(316,316),(318,318),(320,320),(322,322),(324,324),(326,326),(328,329),(331,331),(333,333),(335,335),(337,337),(339,339),(341,341),(343,343),(345,345),(347,347),(349,349),(351,351),(353,353),(355,355),(357,357),(359,359),(361,361),(363,363),(365,365),(367,367),(369,369),(371,371),(373,373),(375,375),(378,378),(380,380),(382,384),(387,387),(389,389),(392,392),(396,397),(402,402),(405,405),(409,411),(414,414),(417,417),(419,419),(421,421),(424,424),(426,427),(429,429),(432,432),(436,436),(438,438),(441,442),(445,447),(454,454),(457,457),(460,460),(462,462),(464,464),(466,466),(468,468),(470,470),(472,472),(474,474),(476,477),(479,479),(481,481),(483,483),(485,485),(487,487),(489,489),(491,491),(493,493),(495,496),(499,499),(501,501),(505,505),(507,507),(509,509),(511,511),(513,513),(515,515),(517,517),(519,519),(521,521),(523,523),(525,525),(527,527),(529,529),(531,531),(533,533),(535,535),(537,537),(539,539),(541,541),(543,543),(545,545),(547,547),(549,549),(551,551),(553,553),(555,555),(557,557),(559,559),(561,561),(563,566),(592,687),(912,912),(940,974),(976,977),(981,983),(985,985),(987,987),(989,989),(991,991),(993,993),(995,995),(997,997),(999,999),(1001,1001),(1003,1003),(1005,1005),(1007,1011),(1013,1013),(1016,1016),(1019,1019),(1072,1119),(1121,1121),(1123,1123),(1125,1125),(1127,1127),(1129,1129),(1131,1131),(1133,1133),(1135,1135),(1137,1137),(1139,1139),(1141,1141),(1143,1143),(1145,1145),(1147,1147),(1149,1149),(1151,1151),(1153,1153),(1163,1163),(1165,1165),(1167,1167),(1169,1169),(1171,1171),(1173,1173),(1175,1175),(1177,1177),(1179,1179),(1181,1181),(1183,1183),(1185,1185),(1187,1187),(1189,1189),(1191,1191),(1193,1193),(1195,1195),(1197,1197),(1199,1199),(1201,1201),(1203,1203),(1205,1205),(1207,1207),(1209,1209),(1211,1211),(1213,1213),(1215,1215),(1218,1218),(1220,1220),(1222,1222),(1224,1224),(1226,1226),(1228,1228),(1230,1230),(1233,1233),(1235,1235),(1237,1237),(1239,1239),(1241,1241),(1243,1243),(1245,1245),(1247,1247),(1249,1249),(1251,1251),(1253,1253),(1255,1255),(1257,1257),(1259,1259),(1261,1261),(1263,1263),(1265,1265),(1267,1267),(1269,1269),(1273,1273),(1281,1281),(1283,1283),(1285,1285),(1287,1287),(1289,1289),(1291,1291),(1293,1293),(1295,1295),(1377,1415),(7424,7467),(7522,7531),(7681,7681),(7683,7683),(7685,7685),(7687,7687),(7689,7689),(7691,7691),(7693,7693),(7695,7695),(7697,7697),(7699,7699),(7701,7701),(7703,7703),(7705,7705),(7707,7707),(7709,7709),(7711,7711),(7713,7713),(7715,7715),(7717,7717),(7719,7719),(7721,7721),(7723,7723),(7725,7725),(7727,7727),(7729,7729),(7731,7731),(7733,7733),(7735,7735),(7737,7737),(7739,7739),(7741,7741),(7743,7743),(7745,7745),(7747,7747),(7749,7749),(7751,7751),(7753,7753),(7755,7755),(7757,7757),(7759,7759),(7761,7761),(7763,7763),(7765,7765),(7767,7767),(7769,7769),(7771,7771),(7773,7773),(7775,7775),(7777,7777),(7779,7779),(7781,7781),(7783,7783),(7785,7785),(7787,7787),(7789,7789),(7791,7791),(7793,7793),(7795,7795),(7797,7797),(7799,7799),(7801,7801),(7803,7803),(7805,7805),(7807,7807),(7809,7809),(7811,7811),(7813,7813),(7815,7815),(7817,7817),(7819,7819),(7821,7821),(7823,7823),(7825,7825),(7827,7827),(7829,7835),(7841,7841),(7843,7843),(7845,7845),(7847,7847),(7849,7849),(7851,7851),(7853,7853),(7855,7855),(7857,7857),(7859,7859),(7861,7861),(7863,7863),(7865,7865),(7867,7867),(7869,7869),(7871,7871),(7873,7873),(7875,7875),(7877,7877),(7879,7879),(7881,7881),(7883,7883),(7885,7885),(7887,7887),(7889,7889),(7891,7891),(7893,7893),(7895,7895),(7897,7897),(7899,7899),(7901,7901),(7903,7903),(7905,7905),(7907,7907),(7909,7909),(7911,7911),(7913,7913),(7915,7915),(7917,7917),(7919,7919),(7921,7921),(7923,7923),(7925,7925),(7927,7927),(7929,7929),(7936,7943),(7952,7957),(7968,7975),(7984,7991),(8000,8005),(8016,8023),(8032,8039),(8048,8061),(8064,8071),(8080,8087),(8096,8103),(8112,8116),(8118,8119),(8126,8126),(8130,8132),(8134,8135),(8144,8147),(8150,8151),(8160,8167),(8178,8180),(8182,8183),(8305,8305),(8319,8319),(8458,8458),(8462,8463),(8467,8467),(8495,8495),(8500,8500),(8505,8505),(8509,8509),(8518,8521),(64256,64262),(64275,64279),(65345,65370),(66600,66639),(119834,119859),(119886,119892),(119894,119911),(119938,119963),(119990,119993),(119995,119995),(119997,120003),(120005,120015),(120042,120067),(120094,120119),(120146,120171),(120198,120223),(120250,120275),(120302,120327),(120354,120379),(120406,120431),(120458,120483),(120514,120538),(120540,120545),(120572,120596),(120598,120603),(120630,120654),(120656,120661),(120688,120712),(120714,120719),(120746,120770),(120772,120777)]),
1587    ("Lm", &[(688,705),(710,721),(736,740),(750,750),(890,890),(1369,1369),(1600,1600),(1765,1766),(3654,3654),(3782,3782),(6103,6103),(6211,6211),(7468,7521),(12293,12293),(12337,12341),(12347,12347),(12445,12446),(12540,12542),(65392,65392),(65438,65439)]),
1588    ("Lo", &[(443,443),(448,451),(1488,1514),(1520,1522),(1569,1594),(1601,1610),(1646,1647),(1649,1747),(1749,1749),(1774,1775),(1786,1788),(1791,1791),(1808,1808),(1810,1839),(1869,1871),(1920,1957),(1969,1969),(2308,2361),(2365,2365),(2384,2384),(2392,2401),(2437,2444),(2447,2448),(2451,2472),(2474,2480),(2482,2482),(2486,2489),(2493,2493),(2524,2525),(2527,2529),(2544,2545),(2565,2570),(2575,2576),(2579,2600),(2602,2608),(2610,2611),(2613,2614),(2616,2617),(2649,2652),(2654,2654),(2674,2676),(2693,2701),(2703,2705),(2707,2728),(2730,2736),(2738,2739),(2741,2745),(2749,2749),(2768,2768),(2784,2785),(2821,2828),(2831,2832),(2835,2856),(2858,2864),(2866,2867),(2869,2873),(2877,2877),(2908,2909),(2911,2913),(2929,2929),(2947,2947),(2949,2954),(2958,2960),(2962,2965),(2969,2970),(2972,2972),(2974,2975),(2979,2980),(2984,2986),(2990,2997),(2999,3001),(3077,3084),(3086,3088),(3090,3112),(3114,3123),(3125,3129),(3168,3169),(3205,3212),(3214,3216),(3218,3240),(3242,3251),(3253,3257),(3261,3261),(3294,3294),(3296,3297),(3333,3340),(3342,3344),(3346,3368),(3370,3385),(3424,3425),(3461,3478),(3482,3505),(3507,3515),(3517,3517),(3520,3526),(3585,3632),(3634,3635),(3648,3653),(3713,3714),(3716,3716),(3719,3720),(3722,3722),(3725,3725),(3732,3735),(3737,3743),(3745,3747),(3749,3749),(3751,3751),(3754,3755),(3757,3760),(3762,3763),(3773,3773),(3776,3780),(3804,3805),(3840,3840),(3904,3911),(3913,3946),(3976,3979),(4096,4129),(4131,4135),(4137,4138),(4176,4181),(4304,4344),(4352,4441),(4447,4514),(4520,4601),(4608,4614),(4616,4678),(4680,4680),(4682,4685),(4688,4694),(4696,4696),(4698,4701),(4704,4742),(4744,4744),(4746,4749),(4752,4782),(4784,4784),(4786,4789),(4792,4798),(4800,4800),(4802,4805),(4808,4814),(4816,4822),(4824,4846),(4848,4878),(4880,4880),(4882,4885),(4888,4894),(4896,4934),(4936,4954),(5024,5108),(5121,5740),(5743,5750),(5761,5786),(5792,5866),(5888,5900),(5902,5905),(5920,5937),(5952,5969),(5984,5996),(5998,6000),(6016,6067),(6108,6108),(6176,6210),(6212,6263),(6272,6312),(6400,6428),(6480,6509),(6512,6516),(8501,8504),(12294,12294),(12348,12348),(12353,12438),(12447,12447),(12449,12538),(12543,12543),(12549,12588),(12593,12686),(12704,12727),(12784,12799),(13312,13312),(19893,19893),(19968,19968),(40869,40869),(40960,42124),(44032,44032),(55203,55203),(63744,64045),(64048,64106),(64285,64285),(64287,64296),(64298,64310),(64312,64316),(64318,64318),(64320,64321),(64323,64324),(64326,64433),(64467,64829),(64848,64911),(64914,64967),(65008,65019),(65136,65140),(65142,65276),(65382,65391),(65393,65437),(65440,65470),(65474,65479),(65482,65487),(65490,65495),(65498,65500),(65536,65547),(65549,65574),(65576,65594),(65596,65597),(65599,65613),(65616,65629),(65664,65786),(66304,66334),(66352,66377),(66432,66461),(66640,66717),(67584,67589),(67592,67592),(67594,67637),(67639,67640),(67644,67644),(67647,67647),(131072,131072),(173782,173782),(194560,195101)]),
1589    ("Lt", &[(453,453),(456,456),(459,459),(498,498),(8072,8079),(8088,8095),(8104,8111),(8124,8124),(8140,8140),(8188,8188)]),
1590    ("Lu", &[(65,90),(192,214),(216,222),(256,256),(258,258),(260,260),(262,262),(264,264),(266,266),(268,268),(270,270),(272,272),(274,274),(276,276),(278,278),(280,280),(282,282),(284,284),(286,286),(288,288),(290,290),(292,292),(294,294),(296,296),(298,298),(300,300),(302,302),(304,304),(306,306),(308,308),(310,310),(313,313),(315,315),(317,317),(319,319),(321,321),(323,323),(325,325),(327,327),(330,330),(332,332),(334,334),(336,336),(338,338),(340,340),(342,342),(344,344),(346,346),(348,348),(350,350),(352,352),(354,354),(356,356),(358,358),(360,360),(362,362),(364,364),(366,366),(368,368),(370,370),(372,372),(374,374),(376,377),(379,379),(381,381),(385,386),(388,388),(390,391),(393,395),(398,401),(403,404),(406,408),(412,413),(415,416),(418,418),(420,420),(422,423),(425,425),(428,428),(430,431),(433,435),(437,437),(439,440),(444,444),(452,452),(455,455),(458,458),(461,461),(463,463),(465,465),(467,467),(469,469),(471,471),(473,473),(475,475),(478,478),(480,480),(482,482),(484,484),(486,486),(488,488),(490,490),(492,492),(494,494),(497,497),(500,500),(502,504),(506,506),(508,508),(510,510),(512,512),(514,514),(516,516),(518,518),(520,520),(522,522),(524,524),(526,526),(528,528),(530,530),(532,532),(534,534),(536,536),(538,538),(540,540),(542,542),(544,544),(546,546),(548,548),(550,550),(552,552),(554,554),(556,556),(558,558),(560,560),(562,562),(902,902),(904,906),(908,908),(910,911),(913,929),(931,939),(978,980),(984,984),(986,986),(988,988),(990,990),(992,992),(994,994),(996,996),(998,998),(1000,1000),(1002,1002),(1004,1004),(1006,1006),(1012,1012),(1015,1015),(1017,1018),(1024,1071),(1120,1120),(1122,1122),(1124,1124),(1126,1126),(1128,1128),(1130,1130),(1132,1132),(1134,1134),(1136,1136),(1138,1138),(1140,1140),(1142,1142),(1144,1144),(1146,1146),(1148,1148),(1150,1150),(1152,1152),(1162,1162),(1164,1164),(1166,1166),(1168,1168),(1170,1170),(1172,1172),(1174,1174),(1176,1176),(1178,1178),(1180,1180),(1182,1182),(1184,1184),(1186,1186),(1188,1188),(1190,1190),(1192,1192),(1194,1194),(1196,1196),(1198,1198),(1200,1200),(1202,1202),(1204,1204),(1206,1206),(1208,1208),(1210,1210),(1212,1212),(1214,1214),(1216,1217),(1219,1219),(1221,1221),(1223,1223),(1225,1225),(1227,1227),(1229,1229),(1232,1232),(1234,1234),(1236,1236),(1238,1238),(1240,1240),(1242,1242),(1244,1244),(1246,1246),(1248,1248),(1250,1250),(1252,1252),(1254,1254),(1256,1256),(1258,1258),(1260,1260),(1262,1262),(1264,1264),(1266,1266),(1268,1268),(1272,1272),(1280,1280),(1282,1282),(1284,1284),(1286,1286),(1288,1288),(1290,1290),(1292,1292),(1294,1294),(1329,1366),(4256,4293),(7680,7680),(7682,7682),(7684,7684),(7686,7686),(7688,7688),(7690,7690),(7692,7692),(7694,7694),(7696,7696),(7698,7698),(7700,7700),(7702,7702),(7704,7704),(7706,7706),(7708,7708),(7710,7710),(7712,7712),(7714,7714),(7716,7716),(7718,7718),(7720,7720),(7722,7722),(7724,7724),(7726,7726),(7728,7728),(7730,7730),(7732,7732),(7734,7734),(7736,7736),(7738,7738),(7740,7740),(7742,7742),(7744,7744),(7746,7746),(7748,7748),(7750,7750),(7752,7752),(7754,7754),(7756,7756),(7758,7758),(7760,7760),(7762,7762),(7764,7764),(7766,7766),(7768,7768),(7770,7770),(7772,7772),(7774,7774),(7776,7776),(7778,7778),(7780,7780),(7782,7782),(7784,7784),(7786,7786),(7788,7788),(7790,7790),(7792,7792),(7794,7794),(7796,7796),(7798,7798),(7800,7800),(7802,7802),(7804,7804),(7806,7806),(7808,7808),(7810,7810),(7812,7812),(7814,7814),(7816,7816),(7818,7818),(7820,7820),(7822,7822),(7824,7824),(7826,7826),(7828,7828),(7840,7840),(7842,7842),(7844,7844),(7846,7846),(7848,7848),(7850,7850),(7852,7852),(7854,7854),(7856,7856),(7858,7858),(7860,7860),(7862,7862),(7864,7864),(7866,7866),(7868,7868),(7870,7870),(7872,7872),(7874,7874),(7876,7876),(7878,7878),(7880,7880),(7882,7882),(7884,7884),(7886,7886),(7888,7888),(7890,7890),(7892,7892),(7894,7894),(7896,7896),(7898,7898),(7900,7900),(7902,7902),(7904,7904),(7906,7906),(7908,7908),(7910,7910),(7912,7912),(7914,7914),(7916,7916),(7918,7918),(7920,7920),(7922,7922),(7924,7924),(7926,7926),(7928,7928),(7944,7951),(7960,7965),(7976,7983),(7992,7999),(8008,8013),(8025,8025),(8027,8027),(8029,8029),(8031,8031),(8040,8047),(8120,8123),(8136,8139),(8152,8155),(8168,8172),(8184,8187),(8450,8450),(8455,8455),(8459,8461),(8464,8466),(8469,8469),(8473,8477),(8484,8484),(8486,8486),(8488,8488),(8490,8493),(8496,8497),(8499,8499),(8510,8511),(8517,8517),(65313,65338),(66560,66599),(119808,119833),(119860,119885),(119912,119937),(119964,119964),(119966,119967),(119970,119970),(119973,119974),(119977,119980),(119982,119989),(120016,120041),(120068,120069),(120071,120074),(120077,120084),(120086,120092),(120120,120121),(120123,120126),(120128,120132),(120134,120134),(120138,120144),(120172,120197),(120224,120249),(120276,120301),(120328,120353),(120380,120405),(120432,120457),(120488,120512),(120546,120570),(120604,120628),(120662,120686),(120720,120744)]),
1591    ("M", &[(768,855),(861,879),(1155,1158),(1160,1161),(1425,1441),(1443,1465),(1467,1469),(1471,1471),(1473,1474),(1476,1476),(1552,1557),(1611,1624),(1648,1648),(1750,1756),(1758,1764),(1767,1768),(1770,1773),(1809,1809),(1840,1866),(1958,1968),(2305,2307),(2364,2364),(2366,2381),(2385,2388),(2402,2403),(2433,2435),(2492,2492),(2494,2500),(2503,2504),(2507,2509),(2519,2519),(2530,2531),(2561,2563),(2620,2620),(2622,2626),(2631,2632),(2635,2637),(2672,2673),(2689,2691),(2748,2748),(2750,2757),(2759,2761),(2763,2765),(2786,2787),(2817,2819),(2876,2876),(2878,2883),(2887,2888),(2891,2893),(2902,2903),(2946,2946),(3006,3010),(3014,3016),(3018,3021),(3031,3031),(3073,3075),(3134,3140),(3142,3144),(3146,3149),(3157,3158),(3202,3203),(3260,3260),(3262,3268),(3270,3272),(3274,3277),(3285,3286),(3330,3331),(3390,3395),(3398,3400),(3402,3405),(3415,3415),(3458,3459),(3530,3530),(3535,3540),(3542,3542),(3544,3551),(3570,3571),(3633,3633),(3636,3642),(3655,3662),(3761,3761),(3764,3769),(3771,3772),(3784,3789),(3864,3865),(3893,3893),(3895,3895),(3897,3897),(3902,3903),(3953,3972),(3974,3975),(3984,3991),(3993,4028),(4038,4038),(4140,4146),(4150,4153),(4182,4185),(5906,5908),(5938,5940),(5970,5971),(6002,6003),(6070,6099),(6109,6109),(6155,6157),(6313,6313),(6432,6443),(6448,6459),(8400,8426),(12330,12335),(12441,12442),(64286,64286),(65024,65039),(65056,65059),(119141,119145),(119149,119154),(119163,119170),(119173,119179),(119210,119213),(917760,917999)]),
1592    ("Mc", &[(2307,2307),(2366,2368),(2377,2380),(2434,2435),(2494,2496),(2503,2504),(2507,2508),(2519,2519),(2563,2563),(2622,2624),(2691,2691),(2750,2752),(2761,2761),(2763,2764),(2818,2819),(2878,2878),(2880,2880),(2887,2888),(2891,2892),(2903,2903),(3006,3007),(3009,3010),(3014,3016),(3018,3020),(3031,3031),(3073,3075),(3137,3140),(3202,3203),(3262,3262),(3264,3268),(3271,3272),(3274,3275),(3285,3286),(3330,3331),(3390,3392),(3398,3400),(3402,3404),(3415,3415),(3458,3459),(3535,3537),(3544,3551),(3570,3571),(3902,3903),(3967,3967),(4140,4140),(4145,4145),(4152,4152),(4182,4183),(6070,6070),(6078,6085),(6087,6088),(6435,6438),(6441,6443),(6448,6449),(6451,6456),(119141,119142),(119149,119154)]),
1593    ("Me", &[(1160,1161),(1758,1758),(8413,8416),(8418,8420)]),
1594    ("Mn", &[(768,855),(861,879),(1155,1158),(1425,1441),(1443,1465),(1467,1469),(1471,1471),(1473,1474),(1476,1476),(1552,1557),(1611,1624),(1648,1648),(1750,1756),(1759,1764),(1767,1768),(1770,1773),(1809,1809),(1840,1866),(1958,1968),(2305,2306),(2364,2364),(2369,2376),(2381,2381),(2385,2388),(2402,2403),(2433,2433),(2492,2492),(2497,2500),(2509,2509),(2530,2531),(2561,2562),(2620,2620),(2625,2626),(2631,2632),(2635,2637),(2672,2673),(2689,2690),(2748,2748),(2753,2757),(2759,2760),(2765,2765),(2786,2787),(2817,2817),(2876,2876),(2879,2879),(2881,2883),(2893,2893),(2902,2902),(2946,2946),(3008,3008),(3021,3021),(3134,3136),(3142,3144),(3146,3149),(3157,3158),(3260,3260),(3263,3263),(3270,3270),(3276,3277),(3393,3395),(3405,3405),(3530,3530),(3538,3540),(3542,3542),(3633,3633),(3636,3642),(3655,3662),(3761,3761),(3764,3769),(3771,3772),(3784,3789),(3864,3865),(3893,3893),(3895,3895),(3897,3897),(3953,3966),(3968,3972),(3974,3975),(3984,3991),(3993,4028),(4038,4038),(4141,4144),(4146,4146),(4150,4151),(4153,4153),(4184,4185),(5906,5908),(5938,5940),(5970,5971),(6002,6003),(6071,6077),(6086,6086),(6089,6099),(6109,6109),(6155,6157),(6313,6313),(6432,6434),(6439,6440),(6450,6450),(6457,6459),(8400,8412),(8417,8417),(8421,8426),(12330,12335),(12441,12442),(64286,64286),(65024,65039),(65056,65059),(119143,119145),(119163,119170),(119173,119179),(119210,119213),(917760,917999)]),
1595    ("N", &[(48,57),(178,179),(185,185),(188,190),(1632,1641),(1776,1785),(2406,2415),(2534,2543),(2548,2553),(2662,2671),(2790,2799),(2918,2927),(3047,3058),(3174,3183),(3302,3311),(3430,3439),(3664,3673),(3792,3801),(3872,3891),(4160,4169),(4969,4988),(5870,5872),(6112,6121),(6128,6137),(6160,6169),(6470,6479),(8304,8304),(8308,8313),(8320,8329),(8531,8579),(9312,9371),(9450,9471),(10102,10131),(12295,12295),(12321,12329),(12344,12346),(12690,12693),(12832,12841),(12881,12895),(12928,12937),(12977,12991),(65296,65305),(65799,65843),(66336,66339),(66378,66378),(66720,66729),(120782,120831)]),
1596    ("Nd", &[(48,57),(1632,1641),(1776,1785),(2406,2415),(2534,2543),(2662,2671),(2790,2799),(2918,2927),(3047,3055),(3174,3183),(3302,3311),(3430,3439),(3664,3673),(3792,3801),(3872,3881),(4160,4169),(4969,4977),(6112,6121),(6160,6169),(6470,6479),(65296,65305),(66720,66729),(120782,120831)]),
1597    ("Nl", &[(5870,5872),(8544,8579),(12295,12295),(12321,12329),(12344,12346),(66378,66378)]),
1598    ("No", &[(178,179),(185,185),(188,190),(2548,2553),(3056,3058),(3882,3891),(4978,4988),(6128,6137),(8304,8304),(8308,8313),(8320,8329),(8531,8543),(9312,9371),(9450,9471),(10102,10131),(12690,12693),(12832,12841),(12881,12895),(12928,12937),(12977,12991),(65799,65843),(66336,66339)]),
1599    ("P", &[(33,35),(37,42),(44,47),(58,59),(63,64),(91,93),(95,95),(123,123),(125,125),(161,161),(171,171),(183,183),(187,187),(191,191),(894,894),(903,903),(1370,1375),(1417,1418),(1470,1470),(1472,1472),(1475,1475),(1523,1524),(1548,1549),(1563,1563),(1567,1567),(1642,1645),(1748,1748),(1792,1805),(2404,2405),(2416,2416),(3572,3572),(3663,3663),(3674,3675),(3844,3858),(3898,3901),(3973,3973),(4170,4175),(4347,4347),(4961,4968),(5741,5742),(5787,5788),(5867,5869),(5941,5942),(6100,6102),(6104,6106),(6144,6154),(6468,6469),(8208,8231),(8240,8259),(8261,8273),(8275,8276),(8279,8279),(8317,8318),(8333,8334),(9001,9002),(9140,9142),(10088,10101),(10214,10219),(10627,10648),(10712,10715),(10748,10749),(12289,12291),(12296,12305),(12308,12319),(12336,12336),(12349,12349),(12448,12448),(12539,12539),(64830,64831),(65072,65106),(65108,65121),(65123,65123),(65128,65128),(65130,65131),(65281,65283),(65285,65290),(65292,65295),(65306,65307),(65311,65312),(65339,65341),(65343,65343),(65371,65371),(65373,65373),(65375,65381),(65792,65793),(66463,66463)]),
1600    ("Pc", &[(95,95),(8255,8256),(8276,8276),(12539,12539),(65075,65076),(65101,65103),(65343,65343),(65381,65381)]),
1601    ("Pd", &[(45,45),(1418,1418),(6150,6150),(8208,8213),(12316,12316),(12336,12336),(12448,12448),(65073,65074),(65112,65112),(65123,65123),(65293,65293)]),
1602    ("Pe", &[(41,41),(93,93),(125,125),(3899,3899),(3901,3901),(5788,5788),(8262,8262),(8318,8318),(8334,8334),(9002,9002),(9141,9141),(10089,10089),(10091,10091),(10093,10093),(10095,10095),(10097,10097),(10099,10099),(10101,10101),(10215,10215),(10217,10217),(10219,10219),(10628,10628),(10630,10630),(10632,10632),(10634,10634),(10636,10636),(10638,10638),(10640,10640),(10642,10642),(10644,10644),(10646,10646),(10648,10648),(10713,10713),(10715,10715),(10749,10749),(12297,12297),(12299,12299),(12301,12301),(12303,12303),(12305,12305),(12309,12309),(12311,12311),(12313,12313),(12315,12315),(12318,12319),(64831,64831),(65078,65078),(65080,65080),(65082,65082),(65084,65084),(65086,65086),(65088,65088),(65090,65090),(65092,65092),(65096,65096),(65114,65114),(65116,65116),(65118,65118),(65289,65289),(65341,65341),(65373,65373),(65376,65376),(65379,65379)]),
1603    ("Pf", &[(187,187),(8217,8217),(8221,8221),(8250,8250)]),
1604    ("Pi", &[(171,171),(8216,8216),(8219,8220),(8223,8223),(8249,8249)]),
1605    ("Po", &[(33,35),(37,39),(42,42),(44,44),(46,47),(58,59),(63,64),(92,92),(161,161),(183,183),(191,191),(894,894),(903,903),(1370,1375),(1417,1417),(1470,1470),(1472,1472),(1475,1475),(1523,1524),(1548,1549),(1563,1563),(1567,1567),(1642,1645),(1748,1748),(1792,1805),(2404,2405),(2416,2416),(3572,3572),(3663,3663),(3674,3675),(3844,3858),(3973,3973),(4170,4175),(4347,4347),(4961,4968),(5741,5742),(5867,5869),(5941,5942),(6100,6102),(6104,6106),(6144,6149),(6151,6154),(6468,6469),(8214,8215),(8224,8231),(8240,8248),(8251,8254),(8257,8259),(8263,8273),(8275,8275),(8279,8279),(9142,9142),(12289,12291),(12349,12349),(65072,65072),(65093,65094),(65097,65100),(65104,65106),(65108,65111),(65119,65121),(65128,65128),(65130,65131),(65281,65283),(65285,65287),(65290,65290),(65292,65292),(65294,65295),(65306,65307),(65311,65312),(65340,65340),(65377,65377),(65380,65380),(65792,65793),(66463,66463)]),
1606    ("Ps", &[(40,40),(91,91),(123,123),(3898,3898),(3900,3900),(5787,5787),(8218,8218),(8222,8222),(8261,8261),(8317,8317),(8333,8333),(9001,9001),(9140,9140),(10088,10088),(10090,10090),(10092,10092),(10094,10094),(10096,10096),(10098,10098),(10100,10100),(10214,10214),(10216,10216),(10218,10218),(10627,10627),(10629,10629),(10631,10631),(10633,10633),(10635,10635),(10637,10637),(10639,10639),(10641,10641),(10643,10643),(10645,10645),(10647,10647),(10712,10712),(10714,10714),(10748,10748),(12296,12296),(12298,12298),(12300,12300),(12302,12302),(12304,12304),(12308,12308),(12310,12310),(12312,12312),(12314,12314),(12317,12317),(64830,64830),(65077,65077),(65079,65079),(65081,65081),(65083,65083),(65085,65085),(65087,65087),(65089,65089),(65091,65091),(65095,65095),(65113,65113),(65115,65115),(65117,65117),(65288,65288),(65339,65339),(65371,65371),(65375,65375),(65378,65378)]),
1607    ("S", &[(36,36),(43,43),(60,62),(94,94),(96,96),(124,124),(126,126),(162,169),(172,172),(174,177),(180,180),(182,182),(184,184),(215,215),(247,247),(706,709),(722,735),(741,749),(751,767),(884,885),(900,901),(1014,1014),(1154,1154),(1550,1551),(1769,1769),(1789,1790),(2546,2547),(2554,2554),(2801,2801),(2928,2928),(3059,3066),(3647,3647),(3841,3843),(3859,3863),(3866,3871),(3892,3892),(3894,3894),(3896,3896),(4030,4037),(4039,4044),(4047,4047),(6107,6107),(6464,6464),(6624,6655),(8125,8125),(8127,8129),(8141,8143),(8157,8159),(8173,8175),(8189,8190),(8260,8260),(8274,8274),(8314,8316),(8330,8332),(8352,8369),(8448,8449),(8451,8454),(8456,8457),(8468,8468),(8470,8472),(8478,8483),(8485,8485),(8487,8487),(8489,8489),(8494,8494),(8498,8498),(8506,8507),(8512,8516),(8522,8523),(8592,9000),(9003,9139),(9143,9168),(9216,9254),(9280,9290),(9372,9449),(9472,9751),(9753,9853),(9856,9873),(9888,9889),(9985,9988),(9990,9993),(9996,10023),(10025,10059),(10061,10061),(10063,10066),(10070,10070),(10072,10078),(10081,10087),(10132,10132),(10136,10159),(10161,10174),(10192,10213),(10224,10626),(10649,10711),(10716,10747),(10750,11021),(11904,11929),(11931,12019),(12032,12245),(12272,12283),(12292,12292),(12306,12307),(12320,12320),(12342,12343),(12350,12351),(12443,12444),(12688,12689),(12694,12703),(12800,12830),(12842,12867),(12880,12880),(12896,12925),(12927,12927),(12938,12976),(12992,13054),(13056,13311),(19904,19967),(42128,42182),(64297,64297),(65020,65021),(65122,65122),(65124,65126),(65129,65129),(65284,65284),(65291,65291),(65308,65310),(65342,65342),(65344,65344),(65372,65372),(65374,65374),(65504,65510),(65512,65518),(65532,65533),(65794,65794),(65847,65855),(118784,119029),(119040,119078),(119082,119140),(119146,119148),(119171,119172),(119180,119209),(119214,119261),(119552,119638),(120513,120513),(120539,120539),(120571,120571),(120597,120597),(120629,120629),(120655,120655),(120687,120687),(120713,120713),(120745,120745),(120771,120771)]),
1608    ("Sc", &[(36,36),(162,165),(2546,2547),(2801,2801),(3065,3065),(3647,3647),(6107,6107),(8352,8369),(65020,65020),(65129,65129),(65284,65284),(65504,65505),(65509,65510)]),
1609    ("Sk", &[(94,94),(96,96),(168,168),(175,175),(180,180),(184,184),(706,709),(722,735),(741,749),(751,767),(884,885),(900,901),(8125,8125),(8127,8129),(8141,8143),(8157,8159),(8173,8175),(8189,8190),(12443,12444),(65342,65342),(65344,65344),(65507,65507)]),
1610    ("Sm", &[(43,43),(60,62),(124,124),(126,126),(172,172),(177,177),(215,215),(247,247),(1014,1014),(8260,8260),(8274,8274),(8314,8316),(8330,8332),(8512,8516),(8523,8523),(8592,8596),(8602,8603),(8608,8608),(8611,8611),(8614,8614),(8622,8622),(8654,8655),(8658,8658),(8660,8660),(8692,8959),(8968,8971),(8992,8993),(9084,9084),(9115,9139),(9655,9655),(9665,9665),(9720,9727),(9839,9839),(10192,10213),(10224,10239),(10496,10626),(10649,10711),(10716,10747),(10750,11007),(64297,64297),(65122,65122),(65124,65126),(65291,65291),(65308,65310),(65372,65372),(65374,65374),(65506,65506),(65513,65516),(120513,120513),(120539,120539),(120571,120571),(120597,120597),(120629,120629),(120655,120655),(120687,120687),(120713,120713),(120745,120745),(120771,120771)]),
1611    ("So", &[(166,167),(169,169),(174,174),(176,176),(182,182),(1154,1154),(1550,1551),(1769,1769),(1789,1790),(2554,2554),(2928,2928),(3059,3064),(3066,3066),(3841,3843),(3859,3863),(3866,3871),(3892,3892),(3894,3894),(3896,3896),(4030,4037),(4039,4044),(4047,4047),(6464,6464),(6624,6655),(8448,8449),(8451,8454),(8456,8457),(8468,8468),(8470,8472),(8478,8483),(8485,8485),(8487,8487),(8489,8489),(8494,8494),(8498,8498),(8506,8507),(8522,8522),(8597,8601),(8604,8607),(8609,8610),(8612,8613),(8615,8621),(8623,8653),(8656,8657),(8659,8659),(8661,8691),(8960,8967),(8972,8991),(8994,9000),(9003,9083),(9085,9114),(9143,9168),(9216,9254),(9280,9290),(9372,9449),(9472,9654),(9656,9664),(9666,9719),(9728,9751),(9753,9838),(9840,9853),(9856,9873),(9888,9889),(9985,9988),(9990,9993),(9996,10023),(10025,10059),(10061,10061),(10063,10066),(10070,10070),(10072,10078),(10081,10087),(10132,10132),(10136,10159),(10161,10174),(10240,10495),(11008,11021),(11904,11929),(11931,12019),(12032,12245),(12272,12283),(12292,12292),(12306,12307),(12320,12320),(12342,12343),(12350,12351),(12688,12689),(12694,12703),(12800,12830),(12842,12867),(12880,12880),(12896,12925),(12927,12927),(12938,12976),(12992,13054),(13056,13311),(19904,19967),(42128,42182),(65021,65021),(65508,65508),(65512,65512),(65517,65518),(65532,65533),(65794,65794),(65847,65855),(118784,119029),(119040,119078),(119082,119140),(119146,119148),(119171,119172),(119180,119209),(119214,119261),(119552,119638)]),
1612    ("Z", &[(32,32),(160,160),(5760,5760),(6158,6158),(8192,8202),(8232,8233),(8239,8239),(8287,8287),(12288,12288)]),
1613    ("Zl", &[(8232,8232)]),
1614    ("Zp", &[(8233,8233)]),
1615    ("Zs", &[(32,32),(160,160),(5760,5760),(6158,6158),(8192,8202),(8239,8239),(8287,8287),(12288,12288)]),
1616];
1617
1618/// # UPSTREAM-PARITY
1619///
1620/// ```c
1621/// int xmlUCSIsBlock(int code, const char *block);
1622/// ```
1623///
1624/// Legacy (xmlregexp.c): 1 if `code` is in the named Unicode block, 0 if
1625/// not, -1 for an unknown block name.
1626#[no_mangle]
1627pub unsafe extern "C" fn xmlUCSIsBlock(code: c_int, block: *const c_char) -> c_int {
1628    if block.is_null() || XML_UCS_BLOCKS.is_empty() {
1629        return -1;
1630    }
1631    let mut low = 0usize;
1632    let mut high = XML_UCS_BLOCKS.len() - 1;
1633    while low <= high {
1634        let mid = (low + high) / 2;
1635        let (name, ranges) = XML_UCS_BLOCKS[mid];
1636        match unsafe { cstr_cmp(block, name.as_bytes()) } {
1637            core::cmp::Ordering::Equal => return ucs_in_ranges(code, ranges),
1638            core::cmp::Ordering::Less => {
1639                if mid == 0 {
1640                    break;
1641                }
1642                high = mid - 1;
1643            }
1644            core::cmp::Ordering::Greater => low = mid + 1,
1645        }
1646    }
1647    -1
1648}
1649
1650/// # UPSTREAM-PARITY
1651///
1652/// ```c
1653/// int xmlUCSIsCat(int code, const char *cat);
1654/// ```
1655///
1656/// Legacy: 1 if `code` belongs to the named Unicode general category, 0 if
1657/// not, -1 for an unknown category name.
1658#[no_mangle]
1659pub unsafe extern "C" fn xmlUCSIsCat(code: c_int, cat: *const c_char) -> c_int {
1660    if cat.is_null() || XML_UCS_CATS.is_empty() {
1661        return -1;
1662    }
1663    let mut low = 0usize;
1664    let mut high = XML_UCS_CATS.len() - 1;
1665    while low <= high {
1666        let mid = (low + high) / 2;
1667        let (name, ranges) = XML_UCS_CATS[mid];
1668        match unsafe { cstr_cmp(cat, name.as_bytes()) } {
1669            core::cmp::Ordering::Equal => return ucs_in_ranges(code, ranges),
1670            core::cmp::Ordering::Less => {
1671                if mid == 0 {
1672                    break;
1673                }
1674                high = mid - 1;
1675            }
1676            core::cmp::Ordering::Greater => low = mid + 1,
1677        }
1678    }
1679    -1
1680}
1681
1682/// # UPSTREAM-PARITY
1683///
1684/// ```c
1685/// int xmlUCSIsCatCc(int code);
1686/// ```
1687///
1688/// Control characters (C0 + DEL + C1).
1689#[no_mangle]
1690pub const extern "C" fn xmlUCSIsCatCc(code: c_int) -> c_int {
1691    if (code >= 0x0 && code <= 0x1f) || (code >= 0x7f && code <= 0x9f) {
1692        1
1693    } else {
1694        0
1695    }
1696}
1697
1698// ═══════════════════════════════════════════════════════════════════════════════
1699// 4. valid family
1700// ═══════════════════════════════════════════════════════════════════════════════
1701
1702/// Normalize an attribute value in place (upstream valid.c
1703/// `xmlValidNormalizeString`): trim leading/trailing spaces and collapse
1704/// runs of spaces to a single space.
1705unsafe fn normalize_string(str_: *mut xmlChar) {
1706    if str_.is_null() {
1707        return;
1708    }
1709    let mut src = str_;
1710    let mut dst = str_;
1711    while unsafe { *src == 0x20 } {
1712        src = src.add(1);
1713    }
1714    loop {
1715        let c = unsafe { *src };
1716        if c == 0 {
1717            break;
1718        }
1719        if c == 0x20 {
1720            while unsafe { *src == 0x20 } {
1721                src = src.add(1);
1722            }
1723            if unsafe { *src != 0 } {
1724                unsafe {
1725                    *dst = 0x20;
1726                }
1727                dst = dst.add(1);
1728            }
1729        } else {
1730            unsafe {
1731                *dst = *src;
1732            }
1733            dst = dst.add(1);
1734            src = src.add(1);
1735        }
1736    }
1737    unsafe {
1738        *dst = 0;
1739    }
1740}
1741
1742/// Report a validation memory error (upstream `xmlVErrMemory`).
1743unsafe fn v_err_memory(ctxt: *mut _xmlValidCtxt) {
1744    if !ctxt.is_null() {
1745        unsafe {
1746            (*ctxt).valid = 0;
1747        }
1748    }
1749    unsafe {
1750        crate::xml::errors::raise_error(
1751            ptr::null_mut(),
1752            ptr::null_mut(),
1753            ptr::null_mut(),
1754            ptr::null_mut(),
1755            ptr::null_mut(),
1756            XML_FROM_VALID,
1757            XML_ERR_NO_MEMORY,
1758            XML_ERR_FATAL as c_int,
1759            ptr::null(),
1760            0,
1761            ptr::null(),
1762            ptr::null(),
1763            ptr::null(),
1764            0,
1765            0,
1766            c"Memory allocation failed : \n".as_ptr() as *const c_char,
1767        );
1768    }
1769}
1770
1771// XML_DTD_NOT_STANDALONE (xmlerror.h, value 530) — not re-exported by the
1772// candidate's constants module.
1773const XML_DTD_NOT_STANDALONE: c_int = 530;
1774
1775/// Report a validation error with node context (upstream `xmlErrValidNode`),
1776/// and clear `ctxt->valid`.
1777unsafe fn v_err_valid_node(
1778    ctxt: *mut _xmlValidCtxt,
1779    _node: *mut _xmlNode,
1780    code: c_int,
1781    msg: *const c_char,
1782    str1: *const xmlChar,
1783    str2: *const xmlChar,
1784) {
1785    if !ctxt.is_null() {
1786        unsafe {
1787            (*ctxt).valid = 0;
1788        }
1789    }
1790    let mut buf = Vec::new();
1791    let mut arg_idx = 0;
1792    if !msg.is_null() {
1793        let mut i = 0usize;
1794        loop {
1795            let c = unsafe { *((msg as *const u8).add(i)) };
1796            if c == 0 {
1797                break;
1798            }
1799            if c == b'%' {
1800                let c2 = unsafe { *((msg as *const u8).add(i + 1)) };
1801                if c2 == b's' {
1802                    if arg_idx == 0 {
1803                        append_xmlstr(&mut buf, str1);
1804                    } else {
1805                        append_xmlstr(&mut buf, str2);
1806                    }
1807                    arg_idx += 1;
1808                    i += 2;
1809                    continue;
1810                }
1811            }
1812            buf.push(c);
1813            i += 1;
1814        }
1815    }
1816    buf.push(0);
1817    unsafe {
1818        crate::xml::errors::raise_error(
1819            ptr::null_mut(),
1820            ptr::null_mut(),
1821            ptr::null_mut(),
1822            ptr::null_mut(),
1823            ptr::null_mut(),
1824            XML_FROM_VALID,
1825            code,
1826            XML_ERR_ERROR as c_int,
1827            ptr::null(),
1828            0,
1829            ptr::null(),
1830            ptr::null(),
1831            ptr::null(),
1832            0,
1833            0,
1834            buf.as_ptr() as *const c_char,
1835        );
1836    }
1837}
1838
1839/// # UPSTREAM-PARITY
1840///
1841/// ```c
1842/// xmlChar *xmlValidNormalizeAttributeValue(xmlDoc *doc, xmlNode *elem,
1843///                                          const xmlChar *name,
1844///                                          const xmlChar *value);
1845/// ```
1846#[no_mangle]
1847pub unsafe extern "C" fn xmlValidNormalizeAttributeValue(
1848    doc: *mut _xmlDoc,
1849    elem: *mut _xmlNode,
1850    name: *const xmlChar,
1851    value: *const xmlChar,
1852) -> *mut xmlChar {
1853    if doc.is_null() || elem.is_null() || name.is_null() || value.is_null() {
1854        return ptr::null_mut();
1855    }
1856    unsafe {
1857        let mut attr_decl =
1858            crate::xml::validation::get_dtd_attr_desc((*doc).intSubset, (*elem).name, name);
1859        if attr_decl.is_null() && !(*doc).extSubset.is_null() {
1860            attr_decl =
1861                crate::xml::validation::get_dtd_attr_desc((*doc).extSubset, (*elem).name, name);
1862        }
1863        if attr_decl.is_null() || (*attr_decl).atype == XML_ATTRIBUTE_CDATA as c_int {
1864            return ptr::null_mut();
1865        }
1866        let ret = crate::abi::exports_xml2::xmlStrdup(value);
1867        if ret.is_null() {
1868            return ptr::null_mut();
1869        }
1870        normalize_string(ret);
1871        ret
1872    }
1873}
1874
1875/// # UPSTREAM-PARITY
1876///
1877/// ```c
1878/// xmlChar *xmlValidCtxtNormalizeAttributeValue(xmlValidCtxt *ctxt,
1879///                                              xmlDoc *doc, xmlNode *elem,
1880///                                              const xmlChar *name,
1881///                                              const xmlChar *value);
1882/// ```
1883#[no_mangle]
1884pub unsafe extern "C" fn xmlValidCtxtNormalizeAttributeValue(
1885    ctxt: *mut _xmlValidCtxt,
1886    doc: *mut _xmlDoc,
1887    elem: *mut _xmlNode,
1888    name: *const xmlChar,
1889    value: *const xmlChar,
1890) -> *mut xmlChar {
1891    if doc.is_null() || elem.is_null() || name.is_null() || value.is_null() {
1892        return ptr::null_mut();
1893    }
1894    unsafe {
1895        let mut prefix: *mut xmlChar = ptr::null_mut();
1896        let local_name = crate::xml::string::split_qname2(name, &mut prefix);
1897        if local_name.is_null() {
1898            if !prefix.is_null() {
1899                xmlFreeImpl(prefix as *mut c_void);
1900            }
1901            v_err_memory(ctxt);
1902            return ptr::null_mut();
1903        }
1904
1905        let mut attr_decl: *mut _xmlAttribute = ptr::null_mut();
1906        let mut extsubset = 0;
1907
1908        let ns = (*elem).ns;
1909        if !ns.is_null() && !(*ns).prefix.is_null() {
1910            let mut buf = [0u8; 50];
1911            let elemname = crate::xml::string::build_qname(
1912                (*elem).name,
1913                (*ns).prefix,
1914                buf.as_mut_ptr() as *mut xmlChar,
1915                50,
1916            );
1917            if elemname.is_null() {
1918                if !prefix.is_null() {
1919                    xmlFreeImpl(prefix as *mut c_void);
1920                }
1921                v_err_memory(ctxt);
1922                return ptr::null_mut();
1923            }
1924            if !(*doc).intSubset.is_null() {
1925                attr_decl = crate::xml::hash::hash_lookup3(
1926                    (*(*doc).intSubset).attributes as *mut crate::xml::hash::HashTable,
1927                    local_name,
1928                    prefix,
1929                    elemname,
1930                ) as *mut _xmlAttribute;
1931            }
1932            if attr_decl.is_null() && !(*doc).extSubset.is_null() {
1933                attr_decl = crate::xml::hash::hash_lookup3(
1934                    (*(*doc).extSubset).attributes as *mut crate::xml::hash::HashTable,
1935                    local_name,
1936                    prefix,
1937                    elemname,
1938                ) as *mut _xmlAttribute;
1939                if !attr_decl.is_null() {
1940                    extsubset = 1;
1941                }
1942            }
1943            if !std::ptr::eq(elemname, (*elem).name) {
1944                xmlFreeImpl(elemname as *mut c_void);
1945            }
1946        }
1947        if attr_decl.is_null() && !(*doc).intSubset.is_null() {
1948            attr_decl = crate::xml::hash::hash_lookup3(
1949                (*(*doc).intSubset).attributes as *mut crate::xml::hash::HashTable,
1950                local_name,
1951                prefix,
1952                (*elem).name,
1953            ) as *mut _xmlAttribute;
1954        }
1955        if attr_decl.is_null() && !(*doc).extSubset.is_null() {
1956            attr_decl = crate::xml::hash::hash_lookup3(
1957                (*(*doc).extSubset).attributes as *mut crate::xml::hash::HashTable,
1958                local_name,
1959                prefix,
1960                (*elem).name,
1961            ) as *mut _xmlAttribute;
1962            if !attr_decl.is_null() {
1963                extsubset = 1;
1964            }
1965        }
1966
1967        if attr_decl.is_null() || (*attr_decl).atype == XML_ATTRIBUTE_CDATA as c_int {
1968            if !prefix.is_null() {
1969                xmlFreeImpl(prefix as *mut c_void);
1970            }
1971            return ptr::null_mut();
1972        }
1973        let ret = crate::abi::exports_xml2::xmlStrdup(value);
1974        if ret.is_null() {
1975            if !prefix.is_null() {
1976                xmlFreeImpl(prefix as *mut c_void);
1977            }
1978            v_err_memory(ctxt);
1979            return ptr::null_mut();
1980        }
1981        normalize_string(ret);
1982        if (*doc).standalone != 0
1983            && extsubset == 1
1984            && crate::abi::exports_xml2::xmlStrEqual(value, ret) == 0
1985        {
1986            v_err_valid_node(
1987                ctxt,
1988                elem,
1989                XML_DTD_NOT_STANDALONE,
1990                c"standalone: %s on %s value had to be normalized based on external subset declaration\n"
1991                    .as_ptr() as *const c_char,
1992                name,
1993                (*elem).name,
1994            );
1995        }
1996        if !prefix.is_null() {
1997            xmlFreeImpl(prefix as *mut c_void);
1998        }
1999        ret
2000    }
2001}
2002
2003/// # UPSTREAM-PARITY
2004///
2005/// ```c
2006/// int xmlValidGetPotentialChildren(xmlElementContent *ctree,
2007///                                  const xmlChar **names,
2008///                                  int *len, int max);
2009/// ```
2010#[no_mangle]
2011pub unsafe extern "C" fn xmlValidGetPotentialChildren(
2012    ctree: *mut _xmlElementContent,
2013    names: *mut *const xmlChar,
2014    len: *mut c_int,
2015    max: c_int,
2016) -> c_int {
2017    if ctree.is_null() || names.is_null() || len.is_null() {
2018        return -1;
2019    }
2020    unsafe {
2021        if *len >= max {
2022            return *len;
2023        }
2024        let pcdata = b"#PCDATA\0";
2025        match (*ctree).type_ as u32 {
2026            t if t == XML_ELEMENT_CONTENT_PCDATA as u32 => {
2027                for i in 0..(*len as usize) {
2028                    if crate::abi::exports_xml2::xmlStrEqual(
2029                        pcdata.as_ptr() as *const xmlChar,
2030                        *names.add(i),
2031                    ) != 0
2032                    {
2033                        return *len;
2034                    }
2035                }
2036                *names.add(*len as usize) = pcdata.as_ptr() as *const xmlChar;
2037                *len += 1;
2038            }
2039            t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
2040                for i in 0..(*len as usize) {
2041                    if crate::abi::exports_xml2::xmlStrEqual((*ctree).name, *names.add(i)) != 0 {
2042                        return *len;
2043                    }
2044                }
2045                *names.add(*len as usize) = (*ctree).name;
2046                *len += 1;
2047            }
2048            t if t == XML_ELEMENT_CONTENT_SEQ as u32 || t == XML_ELEMENT_CONTENT_OR as u32 => {
2049                xmlValidGetPotentialChildren((*ctree).c1, names, len, max);
2050                xmlValidGetPotentialChildren((*ctree).c2, names, len, max);
2051            }
2052            _ => {}
2053        }
2054        *len
2055    }
2056}
2057
2058/// Dummy validity error handler that suppresses messages (upstream
2059/// `xmlNoValidityErr`).
2060const unsafe extern "C" fn xml_no_validity_err(_ctx: *mut c_void, _msg: *const c_char) {}
2061
2062/// # UPSTREAM-PARITY
2063///
2064/// ```c
2065/// int xmlValidGetValidElements(xmlNode *prev, xmlNode *next,
2066///                              const xmlChar **names, int max);
2067/// ```
2068#[no_mangle]
2069pub unsafe extern "C" fn xmlValidGetValidElements(
2070    prev: *mut _xmlNode,
2071    next: *mut _xmlNode,
2072    names: *mut *const xmlChar,
2073    max: c_int,
2074) -> c_int {
2075    if prev.is_null() && next.is_null() {
2076        return -1;
2077    }
2078    if names.is_null() || max <= 0 {
2079        return -1;
2080    }
2081    unsafe {
2082        // Local validation context with errors suppressed, exactly like the
2083        // upstream xmlValidCtxt stack instance in valid.c.
2084        let mut vctxt: _xmlValidCtxt = zeroed();
2085        vctxt.error = Some(xml_no_validity_err);
2086
2087        let mut nb_valid_elements = 0;
2088        let ref_node = if prev.is_null() { next } else { prev };
2089        let parent = (*ref_node).parent;
2090
2091        /*
2092         * Retrieves the parent element declaration.
2093         */
2094        let mut element_desc = if (*parent).doc.is_null() {
2095            ptr::null_mut()
2096        } else {
2097            crate::xml::validation::get_dtd_element_desc((*(*parent).doc).intSubset, (*parent).name)
2098        };
2099        if element_desc.is_null()
2100            && !(*parent).doc.is_null()
2101            && !(*(*parent).doc).extSubset.is_null()
2102        {
2103            element_desc = crate::xml::validation::get_dtd_element_desc(
2104                (*(*parent).doc).extSubset,
2105                (*parent).name,
2106            );
2107        }
2108        if element_desc.is_null() {
2109            return -1;
2110        }
2111
2112        /*
2113         * Do a backup of the current tree structure.
2114         */
2115        let prev_next = if prev.is_null() {
2116            ptr::null_mut()
2117        } else {
2118            (*prev).next
2119        };
2120        let next_prev = if next.is_null() {
2121            ptr::null_mut()
2122        } else {
2123            (*next).prev
2124        };
2125        let parent_childs = (*parent).children;
2126        let parent_last = (*parent).last;
2127
2128        /*
2129         * Create a dummy node and insert it into the tree.
2130         */
2131        let dummy_name = b"<!dummy?>\0";
2132        let test_node = crate::abi::exports_tree::xmlNewDocNode(
2133            (*ref_node).doc,
2134            ptr::null_mut(),
2135            dummy_name.as_ptr() as *const xmlChar,
2136            ptr::null(),
2137        );
2138        if test_node.is_null() {
2139            return -1;
2140        }
2141
2142        (*test_node).parent = parent;
2143        (*test_node).prev = prev;
2144        (*test_node).next = next;
2145        let name = (*test_node).name;
2146
2147        if prev.is_null() {
2148            (*parent).children = test_node;
2149        } else {
2150            (*prev).next = test_node;
2151        }
2152        if next.is_null() {
2153            (*parent).last = test_node;
2154        } else {
2155            (*next).prev = test_node;
2156        }
2157
2158        /*
2159         * Insert each potential child node and check if the parent is
2160         * still valid.
2161         */
2162        let mut elements: [*const xmlChar; 256] = [ptr::null(); 256];
2163        let mut nb_elements = 0;
2164        nb_elements = xmlValidGetPotentialChildren(
2165            (*element_desc).content,
2166            elements.as_mut_ptr(),
2167            &mut nb_elements,
2168            256,
2169        );
2170
2171        let mut i = 0;
2172        while i < nb_elements {
2173            (*test_node).name = elements[i as usize];
2174            if crate::xml::validation::validate_one_element(&mut vctxt, (*parent).doc, parent) != 0
2175            {
2176                let mut j = 0;
2177                while j < nb_valid_elements {
2178                    if crate::abi::exports_xml2::xmlStrEqual(
2179                        elements[i as usize],
2180                        *names.add(j as usize),
2181                    ) != 0
2182                    {
2183                        break;
2184                    }
2185                    j += 1;
2186                }
2187                if j >= nb_valid_elements {
2188                    *names.add(nb_valid_elements as usize) = elements[i as usize];
2189                    nb_valid_elements += 1;
2190                    if nb_valid_elements >= max {
2191                        break;
2192                    }
2193                }
2194            }
2195            i += 1;
2196        }
2197
2198        /*
2199         * Restore the tree structure.
2200         */
2201        if prev.is_null() {
2202            (*parent).children = parent_childs;
2203        } else {
2204            (*prev).next = prev_next;
2205        }
2206        if next.is_null() {
2207            (*parent).last = parent_last;
2208        } else {
2209            (*next).prev = next_prev;
2210        }
2211
2212        /*
2213         * Free the dummy node.
2214         */
2215        (*test_node).name = name;
2216        crate::abi::exports_xml2::xmlFreeNode(test_node);
2217
2218        nb_valid_elements
2219    }
2220}
2221
2222// ═══════════════════════════════════════════════════════════════════════════════
2223// 5. misc2 family
2224// ═══════════════════════════════════════════════════════════════════════════════
2225
2226// ── __xml* aliases (globals.c / xmlIO.c) ───────────────────────────────────────
2227
2228/// Upstream `__xmlDefaultSAXHandler(void)` — pointer to `xmlDefaultSAXHandler`.
2229#[no_mangle]
2230pub unsafe extern "C" fn __xmlDefaultSAXHandler() -> *mut _xmlSAXHandlerV1 {
2231    // SAFETY: returning a pointer to an exported static; the caller may
2232    // read/write it exactly as with upstream's deprecated accessor.
2233    core::ptr::addr_of!(crate::abi::data_globals::xmlDefaultSAXHandler) as *mut _xmlSAXHandlerV1
2234}
2235
2236/// Upstream `__xmlDefaultSAXLocator(void)` — pointer to `xmlDefaultSAXLocator`.
2237#[no_mangle]
2238pub unsafe extern "C" fn __xmlDefaultSAXLocator() -> *mut _xmlSAXLocator {
2239    // SAFETY: returning a pointer to an exported static; the caller may
2240    // read/write it exactly as with upstream's deprecated accessor.
2241    core::ptr::addr_of!(crate::abi::data_globals::xmlDefaultSAXLocator) as *mut _xmlSAXLocator
2242}
2243
2244/// Upstream `__xmlLastError(void)` — pointer to the exported `xmlLastError`
2245/// mirror (kept in sync with the thread-local error state on every raise).
2246#[no_mangle]
2247pub unsafe extern "C" fn __xmlLastError() -> *mut _xmlError {
2248    // SAFETY: returning a pointer to an exported static; the caller may
2249    // read/write it exactly as with upstream's deprecated accessor.
2250    core::ptr::addr_of!(crate::abi::data_globals::xmlLastError) as *mut _xmlError
2251}
2252
2253/// Upstream `__xmlParserInputBufferCreateFilename(const char *URI,
2254/// xmlCharEncoding enc)` — the non-reentrant internal variant; forwards to
2255/// the regular `xmlParserInputBufferCreateFilename`.
2256#[no_mangle]
2257pub unsafe extern "C" fn __xmlParserInputBufferCreateFilename(
2258    URI: *const c_char,
2259    enc: c_int,
2260) -> *mut _xmlParserInputBuffer {
2261    crate::abi::exports_xml2::xmlParserInputBufferCreateFilename(URI, enc)
2262}
2263
2264// ── xmlFormatError (error.c) ───────────────────────────────────────────────────
2265
2266/// Compute the input window around `input->cur` (upstream parserInternals.c
2267/// `xmlParserInputGetWindow`).
2268unsafe fn parser_input_get_window(
2269    input: *mut _xmlParserInput,
2270    start_out: *mut *const xmlChar,
2271    size_in_out: *mut c_int,
2272    offset_out: *mut c_int,
2273) {
2274    unsafe {
2275        let mut cur = (*input).cur;
2276        let base = (*input).base;
2277        let size = *size_in_out;
2278        // Skip backwards over any end-of-lines.
2279        while cur > base && (*cur == b'\n' || *cur == b'\r') {
2280            cur = cur.sub(1);
2281        }
2282        let mut n: usize = 0;
2283        // Search backwards for beginning-of-line (to max buff size).
2284        while n < size as usize && cur > base && *cur != b'\n' && *cur != b'\r' {
2285            cur = cur.sub(1);
2286            n += 1;
2287        }
2288        if n > 0 && (*cur == b'\n' || *cur == b'\r') {
2289            cur = cur.add(1);
2290        } else {
2291            // Skip over continuation bytes.
2292            while cur < (*input).cur && (*cur & 0xC0) == 0x80 {
2293                cur = cur.add(1);
2294            }
2295        }
2296        // Calculate the error position in terms of the current position.
2297        let mut col = (*input).cur as usize - cur as usize;
2298        // Search forward for end-of-line (to max buff size).
2299        let mut nfwd: usize = 0;
2300        let start = cur;
2301        while *cur != 0 && *cur != b'\n' && *cur != b'\r' {
2302            let avail = (*input).end as usize - cur as usize;
2303            let mut clen: c_int = avail as c_int;
2304            let c = xmlGetUTF8Char(cur, &mut clen);
2305            if c < 0 || nfwd + clen as usize > size as usize {
2306                break;
2307            }
2308            cur = cur.add(clen as usize);
2309            nfwd += clen as usize;
2310        }
2311        if col >= nfwd {
2312            col = if nfwd < size as usize {
2313                nfwd
2314            } else {
2315                size as usize - 1
2316            };
2317        }
2318        *start_out = start;
2319        *size_in_out = nfwd as c_int;
2320        *offset_out = col as c_int;
2321    }
2322}
2323
2324/// `pub(crate)` wrapper of [`parser_input_get_window`] for sibling export
2325/// modules (xmlCtxtGetInputWindow, 11.1-X R-000165 closure).
2326pub(crate) unsafe fn parser_input_get_window_pub(
2327    input: *mut _xmlParserInput,
2328    start_out: *mut *const xmlChar,
2329    size_in_out: *mut c_int,
2330    offset_out: *mut c_int,
2331) {
2332    unsafe { parser_input_get_window(input, start_out, size_in_out, offset_out) };
2333}
2334
2335/// Print the source context around an input position (upstream error.c
2336/// `xmlParserPrintFileContextInternal`).
2337unsafe fn print_file_context(
2338    input: *mut _xmlParserInput,
2339    channel: xmlGenericErrorFunc,
2340    data: *mut c_void,
2341) {
2342    if input.is_null() || unsafe { (*input).cur.is_null() } {
2343        return;
2344    }
2345    let mut n: c_int = 80;
2346    let mut start: *const xmlChar = ptr::null();
2347    let mut col: c_int = 0;
2348    unsafe { parser_input_get_window(input, &mut start, &mut n, &mut col) };
2349    let mut content = [0u8; 81];
2350    if n > 0 && !start.is_null() {
2351        unsafe {
2352            ptr::copy_nonoverlapping(start, content.as_mut_ptr(), n as usize);
2353        }
2354    }
2355    content[n as usize] = 0;
2356    unsafe { chan_emit(channel, data, &content[..n as usize]) };
2357    // Create blank line with problem pointer.
2358    let mut i = 0usize;
2359    while i < col as usize {
2360        if content[i] != b'\t' {
2361            content[i] = b' ';
2362        }
2363        i += 1;
2364    }
2365    content[i] = b'^';
2366    i += 1;
2367    content[i] = 0;
2368    unsafe { chan_emit(channel, data, &content[..i]) };
2369}
2370
2371/// # UPSTREAM-PARITY
2372///
2373/// ```c
2374/// void xmlFormatError(const xmlError *err, xmlGenericErrorFunc channel,
2375///                     void *data);
2376/// ```
2377#[no_mangle]
2378pub unsafe extern "C" fn xmlFormatError(
2379    err: *const _xmlError,
2380    channel: Option<xmlGenericErrorFunc>,
2381    data: *mut c_void,
2382) {
2383    if err.is_null() || channel.is_none() {
2384        return;
2385    }
2386    let channel = channel.unwrap();
2387    unsafe {
2388        let e = &*err;
2389        let message = e.message;
2390        let file = e.file;
2391        let line = e.line;
2392        let code = e.code;
2393        let domain = e.domain;
2394        let level = e.level;
2395        let node = e.node as *mut _xmlNode;
2396
2397        if code == XML_ERR_OK {
2398            return;
2399        }
2400
2401        let mut ctxt: *mut _xmlParserCtxt = ptr::null_mut();
2402        if domain == XML_FROM_PARSER
2403            || domain == XML_FROM_HTML
2404            || domain == XML_FROM_DTD
2405            || domain == XML_FROM_NAMESPACE
2406            || domain == XML_FROM_IO
2407            || domain == XML_FROM_VALID
2408        {
2409            ctxt = e.ctxt as *mut _xmlParserCtxt;
2410        }
2411
2412        let mut name: *const xmlChar = ptr::null();
2413        if !node.is_null()
2414            && (*node).type_ == XML_ELEMENT_NODE as c_int
2415            && domain != XML_FROM_SCHEMASV
2416        {
2417            name = (*node).name;
2418        }
2419
2420        let mut input: *mut _xmlParserInput = ptr::null_mut();
2421        let mut cur: *mut _xmlParserInput = ptr::null_mut();
2422        if !ctxt.is_null() && !(*ctxt).input.is_null() {
2423            input = (*ctxt).input;
2424            if (*input).filename.is_null() && (*ctxt).inputNr > 1 {
2425                cur = input;
2426                input = *(*ctxt).inputTab.add((*ctxt).inputNr as usize - 2);
2427            }
2428            if !(*input).filename.is_null() {
2429                let mut buf = Vec::new();
2430                append_cstr(&mut buf, (*input).filename);
2431                buf.push(b':');
2432                append_int(&mut buf, (*input).line);
2433                buf.extend_from_slice(b": ");
2434                chan_emit(channel, data, &buf);
2435            } else if line != 0 && domain == XML_FROM_PARSER {
2436                let mut buf = b"Entity: line ".to_vec();
2437                append_int(&mut buf, (*input).line);
2438                buf.extend_from_slice(b": ");
2439                chan_emit(channel, data, &buf);
2440            }
2441        } else {
2442            if !file.is_null() {
2443                let mut buf = Vec::new();
2444                append_cstr(&mut buf, file);
2445                buf.push(b':');
2446                append_int(&mut buf, line);
2447                buf.extend_from_slice(b": ");
2448                chan_emit(channel, data, &buf);
2449            } else if line != 0
2450                && (domain == XML_FROM_PARSER
2451                    || domain == XML_FROM_SCHEMASV
2452                    || domain == XML_FROM_SCHEMASP
2453                    || domain == XML_FROM_DTD
2454                    || domain == XML_FROM_RELAXNGP
2455                    || domain == XML_FROM_RELAXNGV)
2456            {
2457                let mut buf = b"Entity: line ".to_vec();
2458                append_int(&mut buf, line);
2459                buf.extend_from_slice(b": ");
2460                chan_emit(channel, data, &buf);
2461            }
2462        }
2463        if !name.is_null() {
2464            let mut buf = b"element ".to_vec();
2465            append_xmlstr(&mut buf, name);
2466            buf.extend_from_slice(b": ");
2467            chan_emit(channel, data, &buf);
2468        }
2469        let domain_prefix: &[u8] = match domain {
2470            XML_FROM_PARSER => b"parser ",
2471            XML_FROM_NAMESPACE => b"namespace ",
2472            XML_FROM_DTD | XML_FROM_VALID => b"validity ",
2473            XML_FROM_HTML => b"HTML parser ",
2474            XML_FROM_MEMORY => b"memory ",
2475            XML_FROM_OUTPUT => b"output ",
2476            XML_FROM_IO => b"I/O ",
2477            XML_FROM_XINCLUDE => b"XInclude ",
2478            XML_FROM_XPATH => b"XPath ",
2479            XML_FROM_XPOINTER => b"parser ",
2480            XML_FROM_REGEXP => b"regexp ",
2481            XML_FROM_MODULE => b"module ",
2482            XML_FROM_SCHEMASV => b"Schemas validity ",
2483            XML_FROM_SCHEMASP => b"Schemas parser ",
2484            XML_FROM_RELAXNGP => b"Relax-NG parser ",
2485            XML_FROM_RELAXNGV => b"Relax-NG validity ",
2486            XML_FROM_CATALOG => b"Catalog ",
2487            XML_FROM_C14N => b"C14N ",
2488            XML_FROM_XSLT => b"XSLT ",
2489            XML_FROM_I18N => b"encoding ",
2490            XML_FROM_SCHEMATRONV => b"schematron ",
2491            XML_FROM_BUFFER => b"internal buffer ",
2492            XML_FROM_URI => b"URI ",
2493            _ => b"",
2494        };
2495        chan_emit(channel, data, domain_prefix);
2496        let level_prefix: &[u8] = if level == XML_ERR_NONE as c_int {
2497            b": "
2498        } else if level == XML_ERR_WARNING as c_int {
2499            b"warning : "
2500        } else if level == XML_ERR_ERROR as c_int || level == XML_ERR_FATAL as c_int {
2501            b"error : "
2502        } else {
2503            b": "
2504        };
2505        chan_emit(channel, data, level_prefix);
2506        if !message.is_null() {
2507            let len = cstr_len(message as *const c_char);
2508            let msg_bytes = core::slice::from_raw_parts(message as *const u8, len);
2509            let mut buf = msg_bytes.to_vec();
2510            if len > 0 && *message.add(len - 1) as u8 != b'\n' {
2511                buf.push(b'\n');
2512            }
2513            chan_emit(channel, data, &buf);
2514        } else {
2515            chan_emit(channel, data, b"No error message provided\n");
2516        }
2517
2518        if !ctxt.is_null() {
2519            if !input.is_null()
2520                && ((*input).buf.is_null() || (*(*input).buf).encoder.is_null())
2521                && code == XML_ERR_INVALID_ENCODING
2522                && (*input).cur < (*input).end
2523            {
2524                let mut buf = b"Bytes:".to_vec();
2525                for i in 0..4 {
2526                    if (*input).cur.add(i) >= (*input).end {
2527                        break;
2528                    }
2529                    let b = *(*input).cur.add(i);
2530                    buf.extend_from_slice(b" 0x");
2531                    buf.push(hex_digit(b >> 4));
2532                    buf.push(hex_digit(b & 0xf));
2533                }
2534                buf.push(b'\n');
2535                chan_emit(channel, data, &buf);
2536            }
2537            print_file_context(input, channel, data);
2538            if !cur.is_null() {
2539                if !(*cur).filename.is_null() {
2540                    let mut buf = Vec::new();
2541                    append_cstr(&mut buf, (*cur).filename);
2542                    buf.extend_from_slice(b": ");
2543                    append_int(&mut buf, (*cur).line);
2544                    buf.extend_from_slice(b": \n");
2545                    chan_emit(channel, data, &buf);
2546                } else if line != 0
2547                    && (domain == XML_FROM_PARSER
2548                        || domain == XML_FROM_SCHEMASV
2549                        || domain == XML_FROM_SCHEMASP
2550                        || domain == XML_FROM_DTD
2551                        || domain == XML_FROM_RELAXNGP
2552                        || domain == XML_FROM_RELAXNGV)
2553                {
2554                    let mut buf = b"Entity: line ".to_vec();
2555                    append_int(&mut buf, (*cur).line);
2556                    buf.extend_from_slice(b": \n");
2557                    chan_emit(channel, data, &buf);
2558                }
2559                print_file_context(cur, channel, data);
2560            }
2561        }
2562        if domain == XML_FROM_XPATH
2563            && !e.str1.is_null()
2564            && e.int1 < 100
2565            && e.int1 < crate::abi::exports_xml2::xmlStrlen(e.str1 as *const xmlChar)
2566        {
2567            let mut buf = Vec::new();
2568            append_cstr(&mut buf, e.str1);
2569            buf.push(b'\n');
2570            chan_emit(channel, data, &buf);
2571            let mut marker = vec![b' '; e.int1 as usize];
2572            marker.push(b'^');
2573            marker.push(b'\n');
2574            chan_emit(channel, data, &marker);
2575        }
2576    }
2577}
2578
2579// ── tree node constructors (tree.c) ───────────────────────────────────────────
2580
2581/// # UPSTREAM-PARITY
2582///
2583/// ```c
2584/// xmlNode *xmlNewCharRef(xmlDoc *doc, const xmlChar *name);
2585/// ```
2586#[no_mangle]
2587pub unsafe extern "C" fn xmlNewCharRef(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlNode {
2588    if name.is_null() {
2589        return ptr::null_mut();
2590    }
2591    crate::abi::exports_tree::xmlNewReference(doc, name)
2592}
2593
2594/// # UPSTREAM-PARITY
2595///
2596/// ```c
2597/// xmlNode *xmlNewDocText(const xmlDoc *doc, const xmlChar *content);
2598/// ```
2599#[no_mangle]
2600pub unsafe extern "C" fn xmlNewDocText(
2601    doc: *const _xmlDoc,
2602    content: *const xmlChar,
2603) -> *mut _xmlNode {
2604    let cur = unsafe { crate::abi::exports_xml2::xmlNewText(content) };
2605    if !cur.is_null() {
2606        unsafe { (*cur).doc = doc as *mut _xmlDoc };
2607    }
2608    cur
2609}
2610
2611/// # UPSTREAM-PARITY
2612///
2613/// ```c
2614/// xmlNode *xmlNewDocTextLen(xmlDoc *doc, const xmlChar *content, int len);
2615/// ```
2616#[no_mangle]
2617pub unsafe extern "C" fn xmlNewDocTextLen(
2618    doc: *mut _xmlDoc,
2619    content: *const xmlChar,
2620    len: c_int,
2621) -> *mut _xmlNode {
2622    let cur = unsafe { crate::abi::exports_tree::xmlNewTextLen(content, len) };
2623    if !cur.is_null() {
2624        unsafe { (*cur).doc = doc };
2625    }
2626    cur
2627}
2628
2629/// # UPSTREAM-PARITY
2630///
2631/// ```c
2632/// xmlNode *xmlNewDocComment(xmlDoc *doc, const xmlChar *content);
2633/// ```
2634#[no_mangle]
2635pub unsafe extern "C" fn xmlNewDocComment(
2636    doc: *mut _xmlDoc,
2637    content: *const xmlChar,
2638) -> *mut _xmlNode {
2639    let cur = unsafe { crate::abi::exports_xml2::xmlNewComment(content) };
2640    if !cur.is_null() {
2641        unsafe { (*cur).doc = doc };
2642    }
2643    cur
2644}
2645
2646/// # UPSTREAM-PARITY
2647///
2648/// ```c
2649/// xmlNode *xmlNewDocPI(xmlDoc *doc, const xmlChar *name,
2650///                      const xmlChar *content);
2651/// ```
2652#[no_mangle]
2653pub unsafe extern "C" fn xmlNewDocPI(
2654    doc: *mut _xmlDoc,
2655    name: *const xmlChar,
2656    content: *const xmlChar,
2657) -> *mut _xmlNode {
2658    let cur = unsafe { crate::abi::exports_xml2::xmlNewPI(name, content) };
2659    if !cur.is_null() {
2660        unsafe { (*cur).doc = doc };
2661    }
2662    cur
2663}
2664
2665/// Split a QName, returning a pointer to the local part and storing the
2666/// prefix length (upstream tree.c `xmlSplitQName3`).
2667unsafe fn qname_split3(name: *const xmlChar, len: *mut c_int) -> *const xmlChar {
2668    if name.is_null() || len.is_null() {
2669        return ptr::null();
2670    }
2671    unsafe {
2672        if *name == b':' as xmlChar {
2673            return ptr::null();
2674        }
2675        let mut l = 0usize;
2676        while *name.add(l) != 0 && *name.add(l) != b':' as xmlChar {
2677            l += 1;
2678        }
2679        if *name.add(l) == 0 {
2680            return ptr::null();
2681        }
2682        *len = l as c_int;
2683        name.add(l + 1)
2684    }
2685}
2686
2687/// # UPSTREAM-PARITY
2688///
2689/// ```c
2690/// xmlElementContent *xmlNewDocElementContent(xmlDoc *doc,
2691///                                            const xmlChar *name,
2692///                                            xmlElementContentType type);
2693/// ```
2694#[no_mangle]
2695pub unsafe extern "C" fn xmlNewDocElementContent(
2696    doc: *mut _xmlDoc,
2697    name: *const xmlChar,
2698    type_: xmlElementContentType,
2699) -> *mut _xmlElementContent {
2700    let _ = doc;
2701    unsafe {
2702        let ret = xmlMallocZero(size_of::<_xmlElementContent>()) as *mut _xmlElementContent;
2703        if ret.is_null() {
2704            return ptr::null_mut();
2705        }
2706        (*ret).type_ = type_ as c_int;
2707        (*ret).ocur = XML_ELEMENT_CONTENT_ONCE as c_int;
2708        if !name.is_null() {
2709            let mut l: c_int = 0;
2710            let tmp = qname_split3(name, &mut l);
2711            if tmp.is_null() {
2712                (*ret).name = crate::abi::exports_xml2::xmlStrdup(name);
2713            } else {
2714                (*ret).prefix = crate::abi::exports_xml2::xmlStrndup(name, l);
2715                (*ret).name = crate::abi::exports_xml2::xmlStrdup(tmp);
2716                if (*ret).prefix.is_null() {
2717                    crate::xml::dtd::free_content_model(ret);
2718                    return ptr::null_mut();
2719                }
2720            }
2721            if (*ret).name.is_null() {
2722                crate::xml::dtd::free_content_model(ret);
2723                return ptr::null_mut();
2724            }
2725        }
2726        ret
2727    }
2728}
2729
2730// ── node content / attribute value (tree.c) ───────────────────────────────────
2731
2732/// # UPSTREAM-PARITY
2733///
2734/// ```c
2735/// int xmlNodeBufGetContent(xmlBuffer *buffer, const xmlNode *cur);
2736/// ```
2737#[no_mangle]
2738pub unsafe extern "C" fn xmlNodeBufGetContent(
2739    buffer: *mut _xmlBuffer,
2740    cur: *const _xmlNode,
2741) -> c_int {
2742    if cur.is_null() || buffer.is_null() {
2743        return -1;
2744    }
2745    let content = unsafe { crate::xml::tree::node_get_content(cur as *mut _xmlNode) };
2746    if content.is_null() {
2747        return 0;
2748    }
2749    let len = unsafe { crate::abi::exports_xml2::xmlStrlen(content) };
2750    let ret = crate::xml::io::buf_add(buffer, content, len);
2751    unsafe { xmlFreeImpl(content as *mut c_void) };
2752    if ret < 0 {
2753        -1
2754    } else {
2755        0
2756    }
2757}
2758
2759/// Find an attribute node by name and namespace (upstream tree.c
2760/// `xmlGetPropNodeInternal` with useDTD == 0).
2761unsafe fn find_prop_ns(
2762    node: *const _xmlNode,
2763    name: *const xmlChar,
2764    ns_uri: *const xmlChar,
2765) -> *mut _xmlAttr {
2766    if node.is_null() || unsafe { (*node).type_ != XML_ELEMENT_NODE as c_int } || name.is_null() {
2767        return ptr::null_mut();
2768    }
2769    let mut prop = unsafe { (*node).properties };
2770    while !prop.is_null() {
2771        let ns = unsafe { (*prop).ns };
2772        let ns_match = if ns_uri.is_null() {
2773            ns.is_null()
2774        } else {
2775            !ns.is_null()
2776                && !unsafe { (*ns).href }.is_null()
2777                && unsafe { crate::abi::exports_xml2::xmlStrEqual((*ns).href, ns_uri) != 0 }
2778        };
2779        if ns_match && unsafe { crate::abi::exports_xml2::xmlStrEqual((*prop).name, name) != 0 } {
2780            return prop;
2781        }
2782        prop = unsafe { (*prop).next };
2783    }
2784    ptr::null_mut()
2785}
2786
2787/// # UPSTREAM-PARITY
2788///
2789/// ```c
2790/// int xmlNodeGetAttrValue(const xmlNode *node, const xmlChar *name,
2791///                         const xmlChar *nsUri, xmlChar **out);
2792/// ```
2793///
2794/// Returns 0 with `*out` set to the attribute value, 1 when the attribute
2795/// is missing (or arguments are invalid), -1 on allocation failure.
2796#[no_mangle]
2797pub unsafe extern "C" fn xmlNodeGetAttrValue(
2798    node: *const _xmlNode,
2799    name: *const xmlChar,
2800    ns_uri: *const xmlChar,
2801    out: *mut *mut xmlChar,
2802) -> c_int {
2803    if out.is_null() {
2804        return 1;
2805    }
2806    unsafe { *out = ptr::null_mut() };
2807    let prop = unsafe { find_prop_ns(node, name, ns_uri) };
2808    if prop.is_null() {
2809        return 1;
2810    }
2811    let value = unsafe { crate::xml::tree::node_get_content(prop as *mut _xmlNode) };
2812    if value.is_null() {
2813        return -1;
2814    }
2815    unsafe { *out = value };
2816    0
2817}
2818
2819// ── element content serialization (valid.c) ───────────────────────────────────
2820
2821/// # UPSTREAM-PARITY
2822///
2823/// ```c
2824/// void xmlSprintfElementContent(char *buf, xmlElementContent *content,
2825///                               int englob);
2826/// ```
2827///
2828/// Deprecated; upstream (2.13+) ships this as an empty stub.
2829#[no_mangle]
2830pub const unsafe extern "C" fn xmlSprintfElementContent(
2831    _buf: *mut c_char,
2832    _content: *mut _xmlElementContent,
2833    _englob: c_int,
2834) {
2835}
2836
2837/// # UPSTREAM-PARITY
2838///
2839/// ```c
2840/// void xmlSnprintfElementContent(char *buf, int size,
2841///                                xmlElementContent *content, int englob);
2842/// ```
2843#[no_mangle]
2844pub unsafe extern "C" fn xmlSnprintfElementContent(
2845    buf: *mut c_char,
2846    size: c_int,
2847    content: *mut _xmlElementContent,
2848    englob: c_int,
2849) {
2850    if content.is_null() || buf.is_null() {
2851        return;
2852    }
2853    unsafe {
2854        let mut len = cstr_len(buf) as i32;
2855        if size - len < 50 {
2856            if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
2857                cstr_cat_lit(buf, b" ...");
2858            }
2859            return;
2860        }
2861        if englob != 0 {
2862            cstr_cat_lit(buf, b"(");
2863        }
2864        match (*content).type_ as u32 {
2865            t if t == XML_ELEMENT_CONTENT_PCDATA as u32 => {
2866                cstr_cat_lit(buf, b"#PCDATA");
2867            }
2868            t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
2869                let mut qname_len = crate::abi::exports_xml2::xmlStrlen((*content).name);
2870                if !(*content).prefix.is_null() {
2871                    qname_len += crate::abi::exports_xml2::xmlStrlen((*content).prefix) + 1;
2872                }
2873                if size - len < qname_len + 10 {
2874                    cstr_cat_lit(buf, b" ...");
2875                    return;
2876                }
2877                if !(*content).prefix.is_null() {
2878                    cstr_cat_xmlstr(buf, (*content).prefix);
2879                    cstr_cat_lit(buf, b":");
2880                }
2881                if !(*content).name.is_null() {
2882                    cstr_cat_xmlstr(buf, (*content).name);
2883                }
2884            }
2885            t if t == XML_ELEMENT_CONTENT_SEQ as u32 => {
2886                if (*(*content).c1).type_ == XML_ELEMENT_CONTENT_OR as c_int
2887                    || (*(*content).c1).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
2888                {
2889                    xmlSnprintfElementContent(buf, size, (*content).c1, 1);
2890                } else {
2891                    xmlSnprintfElementContent(buf, size, (*content).c1, 0);
2892                }
2893                len = cstr_len(buf) as i32;
2894                if size - len < 50 {
2895                    if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
2896                        cstr_cat_lit(buf, b" ...");
2897                    }
2898                    return;
2899                }
2900                cstr_cat_lit(buf, b" , ");
2901                if ((*(*content).c2).type_ == XML_ELEMENT_CONTENT_OR as c_int
2902                    || (*(*content).c2).ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
2903                    && (*(*content).c2).type_ != XML_ELEMENT_CONTENT_ELEMENT as c_int
2904                {
2905                    xmlSnprintfElementContent(buf, size, (*content).c2, 1);
2906                } else {
2907                    xmlSnprintfElementContent(buf, size, (*content).c2, 0);
2908                }
2909            }
2910            t if t == XML_ELEMENT_CONTENT_OR as u32 => {
2911                if (*(*content).c1).type_ == XML_ELEMENT_CONTENT_OR as c_int
2912                    || (*(*content).c1).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
2913                {
2914                    xmlSnprintfElementContent(buf, size, (*content).c1, 1);
2915                } else {
2916                    xmlSnprintfElementContent(buf, size, (*content).c1, 0);
2917                }
2918                len = cstr_len(buf) as i32;
2919                if size - len < 50 {
2920                    if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
2921                        cstr_cat_lit(buf, b" ...");
2922                    }
2923                    return;
2924                }
2925                cstr_cat_lit(buf, b" | ");
2926                if ((*(*content).c2).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
2927                    || (*(*content).c2).ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
2928                    && (*(*content).c2).type_ != XML_ELEMENT_CONTENT_ELEMENT as c_int
2929                {
2930                    xmlSnprintfElementContent(buf, size, (*content).c2, 1);
2931                } else {
2932                    xmlSnprintfElementContent(buf, size, (*content).c2, 0);
2933                }
2934            }
2935            _ => {}
2936        }
2937        if size - cstr_len(buf) as i32 <= 2 {
2938            return;
2939        }
2940        if englob != 0 {
2941            cstr_cat_lit(buf, b")");
2942        }
2943        match (*content).ocur as u32 {
2944            t if t == XML_ELEMENT_CONTENT_OPT as u32 => cstr_cat_lit(buf, b"?"),
2945            t if t == XML_ELEMENT_CONTENT_MULT as u32 => cstr_cat_lit(buf, b"*"),
2946            t if t == XML_ELEMENT_CONTENT_PLUS as u32 => cstr_cat_lit(buf, b"+"),
2947            _ => {}
2948        }
2949    }
2950}
2951
2952// ── deprecated stubs ───────────────────────────────────────────────────────────
2953
2954/// # UPSTREAM-PARITY
2955///
2956/// ```c
2957/// int xmlUpgradeOldNs(xmlDocPtr doc);
2958/// ```
2959///
2960/// Deprecated (legacy.c): modern documents no longer carry old-style
2961/// namespace declarations, so this is a no-op returning 0.
2962#[no_mangle]
2963pub const unsafe extern "C" fn xmlUpgradeOldNs(_doc: *mut _xmlDoc) -> c_int {
2964    0
2965}