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#![allow(missing_docs)]
22#![allow(non_snake_case)]
23#![allow(non_camel_case_types)]
24#![allow(non_upper_case_globals)]
25#![allow(unused_variables)]
26#![allow(clippy::missing_safety_doc)]
27#![allow(clippy::not_unsafe_ptr_arg_deref)]
28
29use core::ffi::{c_char, c_void};
30use core::mem::{size_of, zeroed};
31use core::ptr;
32use core::sync::atomic::{AtomicI32, AtomicPtr, Ordering};
33use std::os::raw::c_int;
34
35use crate::abi::allocator::*;
36use crate::abi::callbacks::*;
37use crate::abi::structs::*;
38use crate::abi::types::xmlAttributeType::*;
39use crate::abi::types::xmlBufferAllocationScheme::*;
40use crate::abi::types::xmlElementContentOccur::*;
41use crate::abi::types::xmlElementContentType::*;
42use crate::abi::types::xmlElementType::*;
43use crate::abi::types::xmlEntityType::*;
44use crate::abi::types::xmlErrorLevel::*;
45use crate::abi::types::*;
46
47// ═══════════════════════════════════════════════════════════════════════════════
48// Small C-string / xmlChar helpers (local, so this module is self-contained)
49// ═══════════════════════════════════════════════════════════════════════════════
50
51/// strlen for a NUL-terminated byte string.
52unsafe fn cstr_len(s: *const c_char) -> usize {
53    if s.is_null() {
54        return 0;
55    }
56    let mut n = 0usize;
57    while unsafe { *((s as *const u8).add(n)) } != 0 {
58        n += 1;
59    }
60    n
61}
62
63/// Compare a C string against a byte literal (with implicit NUL).
64unsafe fn cstr_eq(s: *const c_char, lit: &[u8]) -> bool {
65    if s.is_null() {
66        return false;
67    }
68    let b = s as *const u8;
69    let mut i = 0usize;
70    while i < lit.len() {
71        if unsafe { *b.add(i) } != lit[i] {
72            return false;
73        }
74        i += 1;
75    }
76    unsafe { *b.add(i) == 0 }
77}
78
79/// strcmp ordering between a C string and a byte literal.
80unsafe fn cstr_cmp(s: *const c_char, lit: &[u8]) -> core::cmp::Ordering {
81    let b = s as *const u8;
82    let mut i = 0usize;
83    loop {
84        let c = if i < lit.len() { lit[i] } else { 0 };
85        let sc = unsafe { *b.add(i) };
86        if sc < c {
87            return core::cmp::Ordering::Less;
88        }
89        if sc > c {
90            return core::cmp::Ordering::Greater;
91        }
92        if sc == 0 {
93            return core::cmp::Ordering::Equal;
94        }
95        i += 1;
96    }
97}
98
99/// Append a NUL-terminated C string's bytes to `v`.
100unsafe fn append_cstr(v: &mut Vec<u8>, s: *const c_char) {
101    if s.is_null() {
102        return;
103    }
104    let b = s as *const u8;
105    let mut i = 0usize;
106    loop {
107        let c = unsafe { *b.add(i) };
108        if c == 0 {
109            break;
110        }
111        v.push(c);
112        i += 1;
113    }
114}
115
116/// Append an xmlChar (byte) string to `v`.
117unsafe fn append_xmlstr(v: &mut Vec<u8>, s: *const xmlChar) {
118    if s.is_null() {
119        return;
120    }
121    let b = s as *const u8;
122    let mut i = 0usize;
123    loop {
124        let c = unsafe { *b.add(i) };
125        if c == 0 {
126            break;
127        }
128        v.push(c);
129        i += 1;
130    }
131}
132
133/// strcat a byte literal onto a C string buffer.
134unsafe fn cstr_cat_lit(buf: *mut c_char, lit: &[u8]) {
135    if buf.is_null() {
136        return;
137    }
138    let mut i = cstr_len(buf);
139    let b = buf as *mut u8;
140    for &c in lit {
141        unsafe {
142            *b.add(i) = c;
143        }
144        i += 1;
145    }
146    unsafe {
147        *b.add(i) = 0;
148    }
149}
150
151/// strcat an xmlChar string onto a C string buffer.
152unsafe fn cstr_cat_xmlstr(buf: *mut c_char, s: *const xmlChar) {
153    if s.is_null() {
154        return;
155    }
156    let mut i = cstr_len(buf);
157    let mut j = 0usize;
158    let b = buf as *mut u8;
159    let sb = s as *const u8;
160    loop {
161        let c = unsafe { *sb.add(j) };
162        if c == 0 {
163            break;
164        }
165        unsafe {
166            *b.add(i) = c;
167        }
168        i += 1;
169        j += 1;
170    }
171    unsafe {
172        *b.add(i) = 0;
173    }
174}
175
176/// Membership test over a merged short+long range list (upstream
177/// `xmlCharInRange` semantics; short and long ranges are disjoint so a
178/// linear scan over the merged list is equivalent).
179fn ucs_in_ranges(code: c_int, ranges: &[(u32, u32)]) -> c_int {
180    let v = code as u32;
181    for &(lo, hi) in ranges {
182        if v >= lo && v <= hi {
183            return 1;
184        }
185    }
186    0
187}
188
189/// Append an integer in decimal to `v`.
190fn append_int(v: &mut Vec<u8>, n: i32) {
191    let mut x = n as i64;
192    if x < 0 {
193        v.push(b'-');
194        x = -x;
195    }
196    let mut buf = [0u8; 20];
197    let mut i = 0usize;
198    loop {
199        buf[i] = b'0' + (x % 10) as u8;
200        i += 1;
201        x /= 10;
202        if x == 0 {
203            break;
204        }
205    }
206    while i > 0 {
207        i -= 1;
208        v.push(buf[i]);
209    }
210}
211
212/// Upper-case hex digit for a nibble.
213fn hex_digit(v: u8) -> u8 {
214    if v < 10 {
215        b'0' + v
216    } else {
217        b'A' + v - 10
218    }
219}
220
221/// Emit `text` (without NUL) through the generic error channel.
222unsafe fn chan_emit(channel: xmlGenericErrorFunc, data: *mut c_void, text: &[u8]) {
223    let mut b = text.to_vec();
224    b.push(0);
225    channel(data, b.as_ptr() as *const c_char);
226}
227
228// ═══════════════════════════════════════════════════════════════════════════════
229// 1. getset family
230// ═══════════════════════════════════════════════════════════════════════════════
231
232/// The deprecated global buffer allocation scheme.
233///
234/// Upstream 2.13+ removed per-buffer allocation schemes; the getter always
235/// returns `XML_BUFFER_ALLOC_EXACT` and the setter is a no-op. The candidate
236/// keeps a process-global slot (defaulting to `XML_BUFFER_ALLOC_EXACT` = 1)
237/// so the get/set pair round-trips while matching the upstream default.
238static XML_BUFFER_ALLOC_SCHEME: AtomicI32 = AtomicI32::new(XML_BUFFER_ALLOC_EXACT as c_int);
239
240/// # UPSTREAM-PARITY
241///
242/// ```c
243/// xmlBufferAllocationScheme xmlGetBufferAllocationScheme(void);
244/// ```
245#[no_mangle]
246pub extern "C" fn xmlGetBufferAllocationScheme() -> xmlBufferAllocationScheme {
247    match XML_BUFFER_ALLOC_SCHEME.load(Ordering::Relaxed) {
248        v if v == XML_BUFFER_ALLOC_DOUBLEIT as c_int => {
249            xmlBufferAllocationScheme::XML_BUFFER_ALLOC_DOUBLEIT
250        }
251        v if v == XML_BUFFER_ALLOC_EXACT as c_int => {
252            xmlBufferAllocationScheme::XML_BUFFER_ALLOC_EXACT
253        }
254        v if v == XML_BUFFER_ALLOC_IMMUTABLE as c_int => {
255            xmlBufferAllocationScheme::XML_BUFFER_ALLOC_IMMUTABLE
256        }
257        v if v == XML_BUFFER_ALLOC_IO as c_int => xmlBufferAllocationScheme::XML_BUFFER_ALLOC_IO,
258        v if v == XML_BUFFER_ALLOC_HYBRID as c_int => {
259            xmlBufferAllocationScheme::XML_BUFFER_ALLOC_HYBRID
260        }
261        _ => xmlBufferAllocationScheme::XML_BUFFER_ALLOC_BOUNDED,
262    }
263}
264
265/// # UPSTREAM-PARITY
266///
267/// ```c
268/// void xmlSetBufferAllocationScheme(xmlBufferAllocationScheme scheme);
269/// ```
270#[no_mangle]
271pub extern "C" fn xmlSetBufferAllocationScheme(scheme: xmlBufferAllocationScheme) {
272    XML_BUFFER_ALLOC_SCHEME.store(scheme as c_int, Ordering::Relaxed);
273}
274
275/// The legacy feature-name list (upstream features.c `xmlFeaturesList[]`,
276/// 42 entries, as exported by the oracle DSO).
277static XML_FEATURES: [&[u8]; 42] = [
278    b"validate\0",
279    b"load subset\0",
280    b"keep blanks\0",
281    b"disable SAX\0",
282    b"fetch external entities\0",
283    b"substitute entities\0",
284    b"gather line info\0",
285    b"user data\0",
286    b"is html\0",
287    b"is standalone\0",
288    b"stop parser\0",
289    b"document\0",
290    b"is well formed\0",
291    b"is valid\0",
292    b"SAX block\0",
293    b"SAX function internalSubset\0",
294    b"SAX function isStandalone\0",
295    b"SAX function hasInternalSubset\0",
296    b"SAX function hasExternalSubset\0",
297    b"SAX function resolveEntity\0",
298    b"SAX function getEntity\0",
299    b"SAX function entityDecl\0",
300    b"SAX function notationDecl\0",
301    b"SAX function attributeDecl\0",
302    b"SAX function elementDecl\0",
303    b"SAX function unparsedEntityDecl\0",
304    b"SAX function setDocumentLocator\0",
305    b"SAX function startDocument\0",
306    b"SAX function endDocument\0",
307    b"SAX function startElement\0",
308    b"SAX function endElement\0",
309    b"SAX function reference\0",
310    b"SAX function characters\0",
311    b"SAX function ignorableWhitespace\0",
312    b"SAX function processingInstruction\0",
313    b"SAX function comment\0",
314    b"SAX function warning\0",
315    b"SAX function error\0",
316    b"SAX function fatalError\0",
317    b"SAX function getParameterEntity\0",
318    b"SAX function cdataBlock\0",
319    b"SAX function externalSubset\0",
320];
321
322/// # UPSTREAM-PARITY
323///
324/// ```c
325/// int xmlGetFeaturesList(int *len, const char **result);
326/// ```
327///
328/// Legacy API (features.c, removed from source but still exported). Copies
329/// up to `*len` feature names into `result` and returns the total number of
330/// features (42). Returns -1 when `*len` is larger than 999.
331#[no_mangle]
332pub unsafe extern "C" fn xmlGetFeaturesList(len: *mut c_int, result: *mut *const c_char) -> c_int {
333    let n = XML_FEATURES.len() as c_int;
334    if len.is_null() || result.is_null() {
335        return n;
336    }
337    let cur = unsafe { *len };
338    if cur > 999 {
339        return -1;
340    }
341    let mut cnt = cur;
342    if cnt > n {
343        cnt = n;
344        unsafe {
345            *len = n;
346        }
347    }
348    if cnt > 0 {
349        for i in 0..cnt as usize {
350            unsafe {
351                *result.add(i) = XML_FEATURES[i].as_ptr() as *const c_char;
352            }
353        }
354    }
355    n
356}
357
358/// Byte offset of each SAX function slot inside `_xmlSAXHandler`
359/// (upstream features.c reads `ctxt->sax-><slot>`; the candidate struct
360/// is `#[repr(C)]` with the same field order, so the offsets match).
361const SAX_FN_OFFSETS: [(&[u8], usize); 27] = [
362    (b"SAX function internalSubset", 0x00),
363    (b"SAX function isStandalone", 0x08),
364    (b"SAX function hasInternalSubset", 0x10),
365    (b"SAX function hasExternalSubset", 0x18),
366    (b"SAX function resolveEntity", 0x20),
367    (b"SAX function getEntity", 0x28),
368    (b"SAX function entityDecl", 0x30),
369    (b"SAX function notationDecl", 0x38),
370    (b"SAX function attributeDecl", 0x40),
371    (b"SAX function elementDecl", 0x48),
372    (b"SAX function unparsedEntityDecl", 0x50),
373    (b"SAX function setDocumentLocator", 0x58),
374    (b"SAX function startDocument", 0x60),
375    (b"SAX function endDocument", 0x68),
376    (b"SAX function startElement", 0x70),
377    (b"SAX function endElement", 0x78),
378    (b"SAX function reference", 0x80),
379    (b"SAX function characters", 0x88),
380    (b"SAX function ignorableWhitespace", 0x90),
381    (b"SAX function processingInstruction", 0x98),
382    (b"SAX function comment", 0xa0),
383    (b"SAX function warning", 0xa8),
384    (b"SAX function error", 0xb0),
385    (b"SAX function fatalError", 0xb8),
386    (b"SAX function getParameterEntity", 0xc0),
387    (b"SAX function cdataBlock", 0xc8),
388    (b"SAX function externalSubset", 0xd0),
389];
390
391/// # UPSTREAM-PARITY
392///
393/// ```c
394/// int xmlGetFeature(xmlParserCtxtPtr ctxt, const char *name, void *result);
395/// ```
396///
397/// Legacy feature getter (features.c). Returns 0 and stores the feature
398/// value at `result`, or -1 on error / unknown feature.
399#[no_mangle]
400pub unsafe extern "C" fn xmlGetFeature(
401    ctxt: *mut _xmlParserCtxt,
402    name: *const c_char,
403    result: *mut c_void,
404) -> c_int {
405    if name.is_null() || result.is_null() || ctxt.is_null() {
406        return -1;
407    }
408    let c = unsafe { &*ctxt };
409    if unsafe { cstr_eq(name, b"validate") } {
410        unsafe { *(result as *mut c_int) = c.validate };
411        return 0;
412    }
413    if unsafe { cstr_eq(name, b"keep blanks") } {
414        unsafe { *(result as *mut c_int) = c.keepBlanks };
415        return 0;
416    }
417    if unsafe { cstr_eq(name, b"disable SAX") } {
418        unsafe { *(result as *mut c_int) = c.disableSAX };
419        return 0;
420    }
421    if unsafe { cstr_eq(name, b"fetch external entities") } {
422        unsafe { *(result as *mut c_int) = c.loadsubset };
423        return 0;
424    }
425    if unsafe { cstr_eq(name, b"substitute entities") } {
426        unsafe { *(result as *mut c_int) = c.replaceEntities };
427        return 0;
428    }
429    if unsafe { cstr_eq(name, b"gather line info") } {
430        unsafe { *(result as *mut c_int) = c.record_info };
431        return 0;
432    }
433    if unsafe { cstr_eq(name, b"user data") } {
434        unsafe { *(result as *mut *mut c_void) = c.userData };
435        return 0;
436    }
437    if unsafe { cstr_eq(name, b"is html") } {
438        unsafe { *(result as *mut c_int) = c.html };
439        return 0;
440    }
441    if unsafe { cstr_eq(name, b"is standalone") } {
442        unsafe { *(result as *mut c_int) = c.standalone };
443        return 0;
444    }
445    if unsafe { cstr_eq(name, b"document") } {
446        unsafe { *(result as *mut *mut _xmlDoc) = c.myDoc };
447        return 0;
448    }
449    if unsafe { cstr_eq(name, b"is well formed") } {
450        unsafe { *(result as *mut c_int) = c.wellFormed };
451        return 0;
452    }
453    if unsafe { cstr_eq(name, b"is valid") } {
454        unsafe { *(result as *mut c_int) = c.valid };
455        return 0;
456    }
457    if unsafe { cstr_eq(name, b"SAX block") } {
458        unsafe { *(result as *mut *mut _xmlSAXHandler) = c.sax };
459        return 0;
460    }
461    for &(feat, off) in &SAX_FN_OFFSETS {
462        if unsafe { cstr_eq(name, feat) } {
463            if c.sax.is_null() {
464                return -1;
465            }
466            let slot = (c.sax as *mut u8).add(off) as *mut *mut c_void;
467            unsafe { *(result as *mut *mut c_void) = *slot };
468            return 0;
469        }
470    }
471    -1
472}
473
474/// # UPSTREAM-PARITY
475///
476/// ```c
477/// int xmlSetFeature(xmlParserCtxtPtr ctxt, const char *name, void *value);
478/// ```
479///
480/// Legacy feature setter (features.c). Returns 0 on success, -1 on error /
481/// unknown feature. Enabling "validate" also wires up the default validity
482/// error/warning handlers on the context's validation context.
483#[no_mangle]
484pub unsafe extern "C" fn xmlSetFeature(
485    ctxt: *mut _xmlParserCtxt,
486    name: *const c_char,
487    value: *mut c_void,
488) -> c_int {
489    if name.is_null() || value.is_null() || ctxt.is_null() {
490        return -1;
491    }
492    let c = unsafe { &mut *ctxt };
493    if unsafe { cstr_eq(name, b"validate") } {
494        let val = unsafe { *(value as *const c_int) };
495        if c.validate == 0 && val != 0 {
496            if c.vctxt.warning.is_none() {
497                c.vctxt.warning = Some(crate::xml::errors::xmlParserValidityWarning);
498            }
499            if c.vctxt.error.is_none() {
500                c.vctxt.error = Some(crate::xml::errors::xmlParserValidityError);
501            }
502            c.vctxt.valid = 0;
503        }
504        c.validate = val;
505        return 0;
506    }
507    if unsafe { cstr_eq(name, b"keep blanks") } {
508        c.keepBlanks = unsafe { *(value as *const c_int) };
509        return 0;
510    }
511    if unsafe { cstr_eq(name, b"disable SAX") } {
512        c.disableSAX = unsafe { *(value as *const c_int) };
513        return 0;
514    }
515    if unsafe { cstr_eq(name, b"fetch external entities") } {
516        c.loadsubset = unsafe { *(value as *const c_int) };
517        return 0;
518    }
519    if unsafe { cstr_eq(name, b"substitute entities") } {
520        c.replaceEntities = unsafe { *(value as *const c_int) };
521        return 0;
522    }
523    if unsafe { cstr_eq(name, b"gather line info") } {
524        c.record_info = unsafe { *(value as *const c_int) };
525        return 0;
526    }
527    if unsafe { cstr_eq(name, b"user data") } {
528        c.userData = unsafe { *(value as *const *mut c_void) };
529        return 0;
530    }
531    if unsafe { cstr_eq(name, b"is html") } {
532        c.html = unsafe { *(value as *const c_int) };
533        return 0;
534    }
535    if unsafe { cstr_eq(name, b"is standalone") } {
536        c.standalone = unsafe { *(value as *const c_int) };
537        return 0;
538    }
539    if unsafe { cstr_eq(name, b"document") } {
540        c.myDoc = unsafe { *(value as *const *mut _xmlDoc) };
541        return 0;
542    }
543    if unsafe { cstr_eq(name, b"is well formed") } {
544        c.wellFormed = unsafe { *(value as *const c_int) };
545        return 0;
546    }
547    if unsafe { cstr_eq(name, b"is valid") } {
548        c.valid = unsafe { *(value as *const c_int) };
549        return 0;
550    }
551    if unsafe { cstr_eq(name, b"SAX block") } {
552        c.sax = unsafe { *(value as *const *mut _xmlSAXHandler) };
553        return 0;
554    }
555    for &(feat, off) in &SAX_FN_OFFSETS {
556        if unsafe { cstr_eq(name, feat) } {
557            if c.sax.is_null() {
558                return -1;
559            }
560            let slot = (c.sax as *mut u8).add(off) as *mut *mut c_void;
561            unsafe { *slot = *(value as *const *mut c_void) };
562            return 0;
563        }
564    }
565    -1
566}
567
568/// # UPSTREAM-PARITY
569///
570/// ```c
571/// xmlGlobalStatePtr xmlGetGlobalState(void);
572/// ```
573///
574/// Deprecated (globals.c): returns the global state, which the candidate
575/// does not keep — the oracle DSO itself returns NULL.
576#[no_mangle]
577pub unsafe extern "C" fn xmlGetGlobalState() -> *mut c_void {
578    ptr::null_mut()
579}
580
581/// # UPSTREAM-PARITY
582///
583/// ```c
584/// xmlNodePtr xmlGetLastChild(const xmlNode *parent);
585/// ```
586#[no_mangle]
587pub unsafe extern "C" fn xmlGetLastChild(parent: *const _xmlNode) -> *mut _xmlNode {
588    if parent.is_null() || unsafe { (*parent).type_ == XML_NAMESPACE_DECL as c_int } {
589        return ptr::null_mut();
590    }
591    unsafe { (*parent).last }
592}
593
594/// # UPSTREAM-PARITY
595///
596/// ```c
597/// xmlChar *xmlGetNoNsProp(const xmlNode *node, const xmlChar *name);
598/// ```
599///
600/// Value of the no-namespace attribute `name` (with the DTD default/fixed
601/// declaration fallback), or NULL.
602#[no_mangle]
603pub unsafe extern "C" fn xmlGetNoNsProp(
604    node: *const _xmlNode,
605    name: *const xmlChar,
606) -> *mut xmlChar {
607    if node.is_null() || unsafe { (*node).type_ != XML_ELEMENT_NODE as c_int } || name.is_null() {
608        return ptr::null_mut();
609    }
610    let mut prop = unsafe { (*node).properties };
611    while !prop.is_null() {
612        if unsafe { (*prop).ns.is_null() }
613            && unsafe { crate::abi::exports_xml2::xmlStrEqual((*prop).name, name) != 0 }
614        {
615            return get_prop_value(prop);
616        }
617        prop = unsafe { (*prop).next };
618    }
619    dtd_default_attr(node, name, ptr::null())
620}
621
622/// The DTD default/fixed attribute declaration fallback of upstream
623/// `xmlGetPropNodeInternal` (useDTD == 1).
624unsafe fn dtd_default_attr(
625    node: *const _xmlNode,
626    name: *const xmlChar,
627    ns_uri: *const xmlChar,
628) -> *mut xmlChar {
629    let doc = unsafe { (*node).doc };
630    if doc.is_null() || unsafe { (*doc).intSubset.is_null() } {
631        return ptr::null_mut();
632    }
633    // Build the element QName for the DTD lookup.
634    let mut tmp: *mut xmlChar = ptr::null_mut();
635    let elem_qname: *const xmlChar;
636    let ns = unsafe { (*node).ns };
637    if !ns.is_null() && !unsafe { (*ns).prefix.is_null() } {
638        tmp = unsafe { crate::abi::exports_xml2::xmlStrdup((*ns).prefix) };
639        if !tmp.is_null() {
640            let colon = b":\0";
641            tmp = unsafe {
642                crate::abi::exports_xml2::xmlStrcat(tmp, colon.as_ptr() as *const xmlChar)
643            };
644        }
645        if !tmp.is_null() {
646            tmp = unsafe { crate::abi::exports_xml2::xmlStrcat(tmp, (*node).name) };
647        }
648        if tmp.is_null() {
649            return ptr::null_mut();
650        }
651        elem_qname = tmp;
652    } else {
653        elem_qname = unsafe { (*node).name };
654    }
655
656    let mut attr_decl: *mut _xmlAttribute = ptr::null_mut();
657    let doc = unsafe { &*doc };
658    let xml_ns = b"http://www.w3.org/XML/1998/namespace\0";
659    if ns_uri.is_null() {
660        attr_decl = crate::xml::validation::get_dtd_qattr_desc(
661            doc.intSubset,
662            elem_qname,
663            name,
664            ptr::null(),
665        );
666        if attr_decl.is_null() && !doc.extSubset.is_null() {
667            attr_decl = crate::xml::validation::get_dtd_qattr_desc(
668                doc.extSubset,
669                elem_qname,
670                name,
671                ptr::null(),
672            );
673        }
674    } else if unsafe {
675        crate::abi::exports_xml2::xmlStrEqual(ns_uri, xml_ns.as_ptr() as *const xmlChar) != 0
676    } {
677        let xml_prefix = b"xml\0";
678        attr_decl = crate::xml::validation::get_dtd_qattr_desc(
679            doc.intSubset,
680            elem_qname,
681            name,
682            xml_prefix.as_ptr() as *const xmlChar,
683        );
684        if attr_decl.is_null() && !doc.extSubset.is_null() {
685            attr_decl = crate::xml::validation::get_dtd_qattr_desc(
686                doc.extSubset,
687                elem_qname,
688                name,
689                xml_prefix.as_ptr() as *const xmlChar,
690            );
691        }
692    } else {
693        // The ugly case: search using the prefixes of in-scope ns-decls
694        // corresponding to ns_uri.
695        let ns_list = crate::xml::tree::get_ns_list((*node).doc, node as *mut _xmlNode);
696        if ns_list.is_null() {
697            if !tmp.is_null() {
698                unsafe { xmlFree(tmp as *mut c_void) };
699            }
700            return ptr::null_mut();
701        }
702        let mut cur = ns_list;
703        while !unsafe { *cur }.is_null() {
704            let n = unsafe { *cur };
705            if !unsafe { (*n).href }.is_null()
706                && unsafe { crate::abi::exports_xml2::xmlStrEqual((*n).href, ns_uri) != 0 }
707            {
708                attr_decl = crate::xml::validation::get_dtd_qattr_desc(
709                    doc.intSubset,
710                    elem_qname,
711                    name,
712                    (*n).prefix,
713                );
714                if attr_decl.is_null() && !doc.extSubset.is_null() {
715                    attr_decl = crate::xml::validation::get_dtd_qattr_desc(
716                        doc.extSubset,
717                        elem_qname,
718                        name,
719                        (*n).prefix,
720                    );
721                }
722                if !attr_decl.is_null() {
723                    break;
724                }
725            }
726            cur = cur.add(1);
727        }
728        unsafe { xmlFree(ns_list as *mut c_void) };
729    }
730    if !tmp.is_null() {
731        unsafe { xmlFree(tmp as *mut c_void) };
732    }
733
734    if !attr_decl.is_null() && !unsafe { (*attr_decl).defaultValue.is_null() } {
735        return unsafe { crate::abi::exports_xml2::xmlStrdup((*attr_decl).defaultValue) };
736    }
737    ptr::null_mut()
738}
739
740/// Value of an attribute node (upstream `xmlGetPropNodeValueInternal`):
741/// content of the attribute's children for attribute nodes, the default
742/// value for attribute declarations.
743unsafe fn get_prop_value(prop: *mut _xmlAttr) -> *mut xmlChar {
744    if prop.is_null() {
745        return ptr::null_mut();
746    }
747    if unsafe { (*prop).type_ == XML_ATTRIBUTE_NODE as c_int } {
748        unsafe { crate::xml::tree::node_get_content(prop as *mut _xmlNode) }
749    } else if unsafe { (*prop).type_ == XML_ATTRIBUTE_DECL as c_int } {
750        let a = prop as *mut _xmlAttribute;
751        unsafe { crate::abi::exports_xml2::xmlStrdup((*a).defaultValue) }
752    } else {
753        ptr::null_mut()
754    }
755}
756
757/// # UPSTREAM-PARITY
758///
759/// ```c
760/// xmlChar *xmlGetNodePath(const xmlNode *node);
761/// ```
762///
763/// Build an XPath-like path for `node` (tree.c `xmlGetNodePath`), or NULL
764/// on error. The result is allocated with xmlMalloc.
765#[no_mangle]
766pub unsafe extern "C" fn xmlGetNodePath(node: *const _xmlNode) -> *mut xmlChar {
767    if node.is_null() || unsafe { (*node).type_ == XML_NAMESPACE_DECL as c_int } {
768        return ptr::null_mut();
769    }
770    unsafe {
771        // Collect the ancestor chain (node, parent, ..., root).
772        let mut num_nodes = 0usize;
773        let mut cur: *const _xmlNode = node;
774        while !cur.is_null() {
775            num_nodes += 1;
776            cur = (*cur).parent;
777        }
778        let mut nodes: Vec<*const _xmlNode> = Vec::with_capacity(num_nodes);
779        cur = node;
780        while !cur.is_null() && nodes.len() < num_nodes {
781            nodes.push(cur);
782            cur = (*cur).parent;
783        }
784
785        let mut out: Vec<u8> = Vec::new();
786        let mut i = nodes.len();
787        while i > 0 {
788            let mut occur: i32 = 0;
789            i -= 1;
790            let cur = nodes[i];
791            let t = (*cur).type_;
792
793            if t == XML_DOCUMENT_NODE as c_int || t == XML_HTML_DOCUMENT_NODE as c_int {
794                if i == 0 {
795                    out.push(b'/');
796                }
797            } else if t == XML_ELEMENT_NODE as c_int {
798                let mut generic = 0;
799                out.push(b'/');
800                let ns = (*cur).ns;
801                if !ns.is_null() {
802                    if !(*ns).prefix.is_null() {
803                        append_xmlstr(&mut out, (*ns).prefix);
804                        out.push(b':');
805                        append_xmlstr(&mut out, (*cur).name);
806                    } else {
807                        // Cannot express named elements in the default
808                        // namespace, so use "*".
809                        generic = 1;
810                        out.push(b'*');
811                    }
812                } else {
813                    append_xmlstr(&mut out, (*cur).name);
814                }
815                // Thumbler index computation.
816                let mut tmp = (*cur).prev;
817                while !tmp.is_null() {
818                    if (*tmp).type_ == XML_ELEMENT_NODE as c_int
819                        && (generic != 0
820                            || (crate::abi::exports_xml2::xmlStrEqual((*cur).name, (*tmp).name)
821                                != 0
822                                && ((*tmp).ns == ns
823                                    || (!(*tmp).ns.is_null()
824                                        && !ns.is_null()
825                                        && crate::abi::exports_xml2::xmlStrEqual(
826                                            (*ns).prefix,
827                                            (*(*tmp).ns).prefix,
828                                        ) != 0))))
829                    {
830                        occur += 1;
831                    }
832                    tmp = (*tmp).prev;
833                }
834                if occur == 0 {
835                    tmp = (*cur).next;
836                    while !tmp.is_null() && occur == 0 {
837                        if (*tmp).type_ == XML_ELEMENT_NODE as c_int
838                            && (generic != 0
839                                || (crate::abi::exports_xml2::xmlStrEqual(
840                                    (*cur).name,
841                                    (*tmp).name,
842                                ) != 0
843                                    && ((*tmp).ns == ns
844                                        || (!(*tmp).ns.is_null()
845                                            && !ns.is_null()
846                                            && crate::abi::exports_xml2::xmlStrEqual(
847                                                (*ns).prefix,
848                                                (*(*tmp).ns).prefix,
849                                            ) != 0))))
850                        {
851                            occur += 1;
852                        }
853                        tmp = (*tmp).next;
854                    }
855                    if occur != 0 {
856                        occur = 1;
857                    }
858                } else {
859                    occur += 1;
860                }
861            } else if t == XML_COMMENT_NODE as c_int {
862                out.extend_from_slice(b"/comment()");
863                let mut tmp = (*cur).prev;
864                while !tmp.is_null() {
865                    if (*tmp).type_ == XML_COMMENT_NODE as c_int {
866                        occur += 1;
867                    }
868                    tmp = (*tmp).prev;
869                }
870                if occur == 0 {
871                    tmp = (*cur).next;
872                    while !tmp.is_null() && occur == 0 {
873                        if (*tmp).type_ == XML_COMMENT_NODE as c_int {
874                            occur += 1;
875                        }
876                        tmp = (*tmp).next;
877                    }
878                    if occur != 0 {
879                        occur = 1;
880                    }
881                } else {
882                    occur += 1;
883                }
884            } else if t == XML_TEXT_NODE as c_int || t == XML_CDATA_SECTION_NODE as c_int {
885                out.extend_from_slice(b"/text()");
886                let mut tmp = (*cur).prev;
887                while !tmp.is_null() {
888                    if (*tmp).type_ == XML_TEXT_NODE as c_int
889                        || (*tmp).type_ == XML_CDATA_SECTION_NODE as c_int
890                    {
891                        occur += 1;
892                    }
893                    tmp = (*tmp).prev;
894                }
895                if occur == 0 {
896                    tmp = (*cur).next;
897                    while !tmp.is_null() {
898                        if (*tmp).type_ == XML_TEXT_NODE as c_int
899                            || (*tmp).type_ == XML_CDATA_SECTION_NODE as c_int
900                        {
901                            occur = 1;
902                            break;
903                        }
904                        tmp = (*tmp).next;
905                    }
906                } else {
907                    occur += 1;
908                }
909            } else if t == XML_PI_NODE as c_int {
910                out.extend_from_slice(b"/processing-instruction('");
911                append_xmlstr(&mut out, (*cur).name);
912                out.extend_from_slice(b"')");
913                let mut tmp = (*cur).prev;
914                while !tmp.is_null() {
915                    if (*tmp).type_ == XML_PI_NODE as c_int
916                        && crate::abi::exports_xml2::xmlStrEqual((*cur).name, (*tmp).name) != 0
917                    {
918                        occur += 1;
919                    }
920                    tmp = (*tmp).prev;
921                }
922                if occur == 0 {
923                    tmp = (*cur).next;
924                    while !tmp.is_null() && occur == 0 {
925                        if (*tmp).type_ == XML_PI_NODE as c_int
926                            && crate::abi::exports_xml2::xmlStrEqual((*cur).name, (*tmp).name) != 0
927                        {
928                            occur += 1;
929                        }
930                        tmp = (*tmp).next;
931                    }
932                    if occur != 0 {
933                        occur = 1;
934                    }
935                } else {
936                    occur += 1;
937                }
938            } else if t == XML_ATTRIBUTE_NODE as c_int {
939                out.extend_from_slice(b"/@");
940                let ns = (*cur).ns;
941                if !ns.is_null() && !(*ns).prefix.is_null() {
942                    append_xmlstr(&mut out, (*ns).prefix);
943                    out.push(b':');
944                }
945                append_xmlstr(&mut out, (*cur).name);
946            } else {
947                return ptr::null_mut();
948            }
949
950            if occur > 0 {
951                out.push(b'[');
952                append_int(&mut out, occur);
953                out.push(b']');
954            }
955        }
956
957        out.push(0);
958        let ret = xmlMalloc(out.len());
959        if ret.is_null() {
960            return ptr::null_mut();
961        }
962        ptr::copy_nonoverlapping(out.as_ptr(), ret as *mut u8, out.len());
963        ret as *mut xmlChar
964    }
965}
966
967/// The five XML predefined entities, mirroring upstream entities.c static
968/// `xmlEntityLt`/`xmlEntityGt`/`xmlEntityAmp`/`xmlEntityQuot`/`xmlEntityApos`.
969struct SyncEntity(*const _xmlEntity);
970unsafe impl Sync for SyncEntity {}
971
972static PREDEFINED_LT_DATA: _xmlEntity = _xmlEntity {
973    _private: ptr::null_mut(),
974    type_: XML_ENTITY_DECL as c_int,
975    name: b"lt\0" as *const u8 as *const xmlChar,
976    children: ptr::null_mut(),
977    last: ptr::null_mut(),
978    parent: ptr::null_mut(),
979    next: ptr::null_mut(),
980    prev: ptr::null_mut(),
981    doc: ptr::null_mut(),
982    orig: ptr::null_mut(),
983    content: b"<\0" as *const u8 as *mut xmlChar,
984    length: 1,
985    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
986    ExternalID: ptr::null(),
987    SystemID: ptr::null(),
988    nexte: ptr::null_mut(),
989    URI: ptr::null(),
990    owner: 0,
991    flags: 0,
992    expandedSize: 0,
993};
994static PREDEFINED_GT_DATA: _xmlEntity = _xmlEntity {
995    _private: ptr::null_mut(),
996    type_: XML_ENTITY_DECL as c_int,
997    name: b"gt\0" as *const u8 as *const xmlChar,
998    children: ptr::null_mut(),
999    last: ptr::null_mut(),
1000    parent: ptr::null_mut(),
1001    next: ptr::null_mut(),
1002    prev: ptr::null_mut(),
1003    doc: ptr::null_mut(),
1004    orig: ptr::null_mut(),
1005    content: b">\0" as *const u8 as *mut xmlChar,
1006    length: 1,
1007    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
1008    ExternalID: ptr::null(),
1009    SystemID: ptr::null(),
1010    nexte: ptr::null_mut(),
1011    URI: ptr::null(),
1012    owner: 0,
1013    flags: 0,
1014    expandedSize: 0,
1015};
1016static PREDEFINED_AMP_DATA: _xmlEntity = _xmlEntity {
1017    _private: ptr::null_mut(),
1018    type_: XML_ENTITY_DECL as c_int,
1019    name: b"amp\0" as *const u8 as *const xmlChar,
1020    children: ptr::null_mut(),
1021    last: ptr::null_mut(),
1022    parent: ptr::null_mut(),
1023    next: ptr::null_mut(),
1024    prev: ptr::null_mut(),
1025    doc: ptr::null_mut(),
1026    orig: ptr::null_mut(),
1027    content: b"&\0" as *const u8 as *mut xmlChar,
1028    length: 1,
1029    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
1030    ExternalID: ptr::null(),
1031    SystemID: ptr::null(),
1032    nexte: ptr::null_mut(),
1033    URI: ptr::null(),
1034    owner: 0,
1035    flags: 0,
1036    expandedSize: 0,
1037};
1038static PREDEFINED_QUOT_DATA: _xmlEntity = _xmlEntity {
1039    _private: ptr::null_mut(),
1040    type_: XML_ENTITY_DECL as c_int,
1041    name: b"quot\0" as *const u8 as *const xmlChar,
1042    children: ptr::null_mut(),
1043    last: ptr::null_mut(),
1044    parent: ptr::null_mut(),
1045    next: ptr::null_mut(),
1046    prev: ptr::null_mut(),
1047    doc: ptr::null_mut(),
1048    orig: ptr::null_mut(),
1049    content: b"\"\0" as *const u8 as *mut xmlChar,
1050    length: 1,
1051    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
1052    ExternalID: ptr::null(),
1053    SystemID: ptr::null(),
1054    nexte: ptr::null_mut(),
1055    URI: ptr::null(),
1056    owner: 0,
1057    flags: 0,
1058    expandedSize: 0,
1059};
1060static PREDEFINED_APOS_DATA: _xmlEntity = _xmlEntity {
1061    _private: ptr::null_mut(),
1062    type_: XML_ENTITY_DECL as c_int,
1063    name: b"apos\0" as *const u8 as *const xmlChar,
1064    children: ptr::null_mut(),
1065    last: ptr::null_mut(),
1066    parent: ptr::null_mut(),
1067    next: ptr::null_mut(),
1068    prev: ptr::null_mut(),
1069    doc: ptr::null_mut(),
1070    orig: ptr::null_mut(),
1071    content: b"'\0" as *const u8 as *mut xmlChar,
1072    length: 1,
1073    etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
1074    ExternalID: ptr::null(),
1075    SystemID: ptr::null(),
1076    nexte: ptr::null_mut(),
1077    URI: ptr::null(),
1078    owner: 0,
1079    flags: 0,
1080    expandedSize: 0,
1081};
1082
1083static PREDEFINED_LT: SyncEntity = SyncEntity(&PREDEFINED_LT_DATA as *const _xmlEntity);
1084static PREDEFINED_GT: SyncEntity = SyncEntity(&PREDEFINED_GT_DATA as *const _xmlEntity);
1085static PREDEFINED_AMP: SyncEntity = SyncEntity(&PREDEFINED_AMP_DATA as *const _xmlEntity);
1086static PREDEFINED_QUOT: SyncEntity = SyncEntity(&PREDEFINED_QUOT_DATA as *const _xmlEntity);
1087static PREDEFINED_APOS: SyncEntity = SyncEntity(&PREDEFINED_APOS_DATA as *const _xmlEntity);
1088
1089/// # UPSTREAM-PARITY
1090///
1091/// ```c
1092/// xmlEntityPtr xmlGetPredefinedEntity(const xmlChar *name);
1093/// ```
1094///
1095/// Returns a pointer to the static predefined entity, or NULL.
1096#[no_mangle]
1097pub unsafe extern "C" fn xmlGetPredefinedEntity(name: *const xmlChar) -> *mut _xmlEntity {
1098    if name.is_null() {
1099        return ptr::null_mut();
1100    }
1101    unsafe {
1102        if crate::abi::exports_xml2::xmlStrEqual(name, b"lt\0" as *const u8 as *const xmlChar) != 0
1103        {
1104            return PREDEFINED_LT.0 as *mut _xmlEntity;
1105        }
1106        if crate::abi::exports_xml2::xmlStrEqual(name, b"gt\0" as *const u8 as *const xmlChar) != 0
1107        {
1108            return PREDEFINED_GT.0 as *mut _xmlEntity;
1109        }
1110        if crate::abi::exports_xml2::xmlStrEqual(name, b"amp\0" as *const u8 as *const xmlChar) != 0
1111        {
1112            return PREDEFINED_AMP.0 as *mut _xmlEntity;
1113        }
1114        if crate::abi::exports_xml2::xmlStrEqual(name, b"quot\0" as *const u8 as *const xmlChar)
1115            != 0
1116        {
1117            return PREDEFINED_QUOT.0 as *mut _xmlEntity;
1118        }
1119        if crate::abi::exports_xml2::xmlStrEqual(name, b"apos\0" as *const u8 as *const xmlChar)
1120            != 0
1121        {
1122            return PREDEFINED_APOS.0 as *mut _xmlEntity;
1123        }
1124    }
1125    ptr::null_mut()
1126}
1127
1128/// # UPSTREAM-PARITY
1129///
1130/// ```c
1131/// int xmlGetThreadId(void);
1132/// ```
1133///
1134/// Upstream returns `pthread_self()` (or 0 when single-threaded); the
1135/// candidate is single-threaded, so 0 matches the oracle's common path.
1136#[no_mangle]
1137pub extern "C" fn xmlGetThreadId() -> c_int {
1138    0
1139}
1140
1141/// # UPSTREAM-PARITY
1142///
1143/// ```c
1144/// int xmlGetUTF8Char(const unsigned char *utf, int *len);
1145/// ```
1146///
1147/// Decode the UTF-8 character at `utf`, set `*len` to its byte length and
1148/// return the code point; -1 (and `*len = 0`) on error.
1149#[no_mangle]
1150pub unsafe extern "C" fn xmlGetUTF8Char(utf: *const u8, len: *mut c_int) -> c_int {
1151    unsafe {
1152        if utf.is_null() || len.is_null() {
1153            if !len.is_null() {
1154                *len = 0;
1155            }
1156            return -1;
1157        }
1158        let mut c: u32 = *utf as u32;
1159        if c < 0x80 {
1160            if *len < 1 {
1161                *len = 0;
1162                return -1;
1163            }
1164            *len = 1;
1165        } else {
1166            if *len < 2 || (*utf.add(1) & 0xc0) != 0x80 {
1167                *len = 0;
1168                return -1;
1169            }
1170            if c < 0xe0 {
1171                if c < 0xc2 {
1172                    *len = 0;
1173                    return -1;
1174                }
1175                *len = 2;
1176                c = (c & 0x1f) << 6;
1177                c |= (*utf.add(1) & 0x3f) as u32;
1178            } else {
1179                if *len < 3 || (*utf.add(2) & 0xc0) != 0x80 {
1180                    *len = 0;
1181                    return -1;
1182                }
1183                if c < 0xf0 {
1184                    *len = 3;
1185                    c = (c & 0x0f) << 12;
1186                    c |= ((*utf.add(1) & 0x3f) as u32) << 6;
1187                    c |= (*utf.add(2) & 0x3f) as u32;
1188                    if c < 0x800 || (c >= 0xd800 && c < 0xe000) {
1189                        *len = 0;
1190                        return -1;
1191                    }
1192                } else {
1193                    if *len < 4 || (*utf.add(3) & 0xc0) != 0x80 {
1194                        *len = 0;
1195                        return -1;
1196                    }
1197                    *len = 4;
1198                    c = (c & 0x07) << 18;
1199                    c |= ((*utf.add(1) & 0x3f) as u32) << 12;
1200                    c |= ((*utf.add(2) & 0x3f) as u32) << 6;
1201                    c |= (*utf.add(3) & 0x3f) as u32;
1202                    if c < 0x10000 || c >= 0x110000 {
1203                        *len = 0;
1204                        return -1;
1205                    }
1206                }
1207            }
1208        }
1209        c as c_int
1210    }
1211}
1212
1213/// Type of the entity-reference callback (upstream entities.h
1214/// `xmlEntityReferenceFunc`).
1215pub type xmlEntityReferenceFunc = unsafe extern "C" fn(
1216    entity: *mut _xmlEntity,
1217    firstChild: *mut _xmlNode,
1218    lastChild: *mut _xmlNode,
1219);
1220
1221/// The global entity-reference callback (legacy entities.c global; the
1222/// candidate's parser does not invoke it, mirroring modern upstream where
1223/// `xmlSetEntityReferenceFunc` is a no-op).
1224static ENTITY_REFERENCE_FUNC: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
1225
1226/// # UPSTREAM-PARITY
1227///
1228/// ```c
1229/// void xmlSetEntityReferenceFunc(xmlEntityReferenceFunc func);
1230/// ```
1231#[no_mangle]
1232pub unsafe extern "C" fn xmlSetEntityReferenceFunc(func: Option<xmlEntityReferenceFunc>) {
1233    ENTITY_REFERENCE_FUNC.store(
1234        func.map_or(ptr::null_mut(), |f| f as *const c_void as *mut c_void),
1235        Ordering::Relaxed,
1236    );
1237}
1238
1239/// # UPSTREAM-PARITY
1240///
1241/// ```c
1242/// int xmlSetListDoc(xmlNode *list, xmlDoc *doc);
1243/// ```
1244///
1245/// Set `doc` on every node of the sibling list (tree.c). Returns 0, or -1
1246/// if a subtree assignment failed.
1247#[no_mangle]
1248pub unsafe extern "C" fn xmlSetListDoc(list: *mut _xmlNode, doc: *mut _xmlDoc) -> c_int {
1249    if list.is_null() || unsafe { (*list).type_ == XML_NAMESPACE_DECL as c_int } {
1250        return 0;
1251    }
1252    let mut ret = 0;
1253    let mut cur = list;
1254    while !cur.is_null() {
1255        if unsafe { (*cur).doc != doc } {
1256            if unsafe { crate::abi::exports_tree::xmlSetTreeDoc(cur, doc) } < 0 {
1257                ret = -1;
1258            }
1259        }
1260        cur = unsafe { (*cur).next };
1261    }
1262    ret
1263}
1264
1265// ═══════════════════════════════════════════════════════════════════════════════
1266// 2. module family — xmlModule* (xmlmodule.c, dlopen/dlsym/dlclose)
1267// ═══════════════════════════════════════════════════════════════════════════════
1268
1269/// Module handle (upstream `struct _xmlModule`, xmlmodule.c).
1270#[repr(C)]
1271pub struct _xmlModule {
1272    pub name: *mut xmlChar,
1273    pub handle: *mut c_void,
1274}
1275
1276extern "C" {
1277    fn dlopen(filename: *const c_char, flags: c_int) -> *mut c_void;
1278    fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
1279    fn dlclose(handle: *mut c_void) -> c_int;
1280    fn dlerror() -> *mut c_char;
1281}
1282
1283// RTLD_NOW | RTLD_GLOBAL (glibc; matches upstream xmlModulePlatformOpen).
1284const RTLD_NOW: c_int = 2;
1285const RTLD_GLOBAL: c_int = 0x100;
1286
1287/// # UPSTREAM-PARITY
1288///
1289/// ```c
1290/// xmlModulePtr xmlModuleOpen(const char *filename, int options);
1291/// ```
1292#[no_mangle]
1293pub unsafe extern "C" fn xmlModuleOpen(
1294    filename: *const c_char,
1295    _options: c_int,
1296) -> *mut _xmlModule {
1297    let module = unsafe { xmlMallocZero(size_of::<_xmlModule>()) } as *mut _xmlModule;
1298    if module.is_null() {
1299        return ptr::null_mut();
1300    }
1301    let handle = unsafe { dlopen(filename, RTLD_GLOBAL | RTLD_NOW) };
1302    if handle.is_null() {
1303        unsafe { xmlFree(module as *mut c_void) };
1304        return ptr::null_mut();
1305    }
1306    unsafe {
1307        (*module).handle = handle;
1308        if !filename.is_null() {
1309            (*module).name = crate::abi::exports_xml2::xmlStrdup(filename as *const xmlChar);
1310        }
1311    }
1312    module
1313}
1314
1315/// # UPSTREAM-PARITY
1316///
1317/// ```c
1318/// int xmlModuleSymbol(xmlModule *module, const char *name, void **symbol);
1319/// ```
1320#[no_mangle]
1321pub unsafe extern "C" fn xmlModuleSymbol(
1322    module: *mut _xmlModule,
1323    name: *const c_char,
1324    symbol: *mut *mut c_void,
1325) -> c_int {
1326    if module.is_null() || symbol.is_null() || name.is_null() {
1327        return -1;
1328    }
1329    unsafe {
1330        *symbol = dlsym((*module).handle, name);
1331        if !dlerror().is_null() {
1332            return -1;
1333        }
1334    }
1335    0
1336}
1337
1338/// # UPSTREAM-PARITY
1339///
1340/// ```c
1341/// int xmlModuleClose(xmlModule *module);
1342/// ```
1343#[no_mangle]
1344pub unsafe extern "C" fn xmlModuleClose(module: *mut _xmlModule) -> c_int {
1345    if module.is_null() {
1346        return -1;
1347    }
1348    let rc = unsafe { dlclose((*module).handle) };
1349    if rc != 0 {
1350        return -2;
1351    }
1352    unsafe { xmlModuleFree(module) }
1353}
1354
1355/// # UPSTREAM-PARITY
1356///
1357/// ```c
1358/// int xmlModuleFree(xmlModule *module);
1359/// ```
1360#[no_mangle]
1361pub unsafe extern "C" fn xmlModuleFree(module: *mut _xmlModule) -> c_int {
1362    if module.is_null() {
1363        return -1;
1364    }
1365    unsafe {
1366        if !(*module).name.is_null() {
1367            xmlFree((*module).name as *mut c_void);
1368        }
1369        xmlFree(module as *mut c_void);
1370    }
1371    0
1372}
1373
1374// ═══════════════════════════════════════════════════════════════════════════════
1375// 3. ucs family — xmlUCSIsBlock / xmlUCSIsCat / xmlUCSIsCatCc
1376// ═══════════════════════════════════════════════════════════════════════════════
1377
1378// Generated: UCS block/category name tables (archaeology libxml2-git
1379// codegen/unicode.inc). Each entry: (name, &[(lo, hi)]) sorted by name
1380// (upstream xmlUnicodeBlocks / xmlUnicodeCats).
1381
1382#[rustfmt::skip]
1383static XML_UCS_BLOCKS: &[(&str, &[(u32, u32)])] = &[
1384    ("AegeanNumbers", &[(65792,65855)]),
1385    ("AlphabeticPresentationForms", &[(64256,64335)]),
1386    ("Arabic", &[(1536,1791)]),
1387    ("ArabicPresentationForms-A", &[(64336,65023)]),
1388    ("ArabicPresentationForms-B", &[(65136,65279)]),
1389    ("Armenian", &[(1328,1423)]),
1390    ("Arrows", &[(8592,8703)]),
1391    ("BasicLatin", &[(0,127)]),
1392    ("Bengali", &[(2432,2559)]),
1393    ("BlockElements", &[(9600,9631)]),
1394    ("Bopomofo", &[(12544,12591)]),
1395    ("BopomofoExtended", &[(12704,12735)]),
1396    ("BoxDrawing", &[(9472,9599)]),
1397    ("BraillePatterns", &[(10240,10495)]),
1398    ("Buhid", &[(5952,5983)]),
1399    ("ByzantineMusicalSymbols", &[(118784,119039)]),
1400    ("CJKCompatibility", &[(13056,13311)]),
1401    ("CJKCompatibilityForms", &[(65072,65103)]),
1402    ("CJKCompatibilityIdeographs", &[(63744,64255)]),
1403    ("CJKCompatibilityIdeographsSupplement", &[(194560,195103)]),
1404    ("CJKRadicalsSupplement", &[(11904,12031)]),
1405    ("CJKSymbolsandPunctuation", &[(12288,12351)]),
1406    ("CJKUnifiedIdeographs", &[(19968,40959)]),
1407    ("CJKUnifiedIdeographsExtensionA", &[(13312,19903)]),
1408    ("CJKUnifiedIdeographsExtensionB", &[(131072,173791)]),
1409    ("Cherokee", &[(5024,5119)]),
1410    ("CombiningDiacriticalMarks", &[(768,879)]),
1411    ("CombiningDiacriticalMarksforSymbols", &[(8400,8447)]),
1412    ("CombiningHalfMarks", &[(65056,65071)]),
1413    ("CombiningMarksforSymbols", &[(8400,8447)]),
1414    ("ControlPictures", &[(9216,9279)]),
1415    ("CurrencySymbols", &[(8352,8399)]),
1416    ("CypriotSyllabary", &[(67584,67647)]),
1417    ("Cyrillic", &[(1024,1279)]),
1418    ("CyrillicSupplement", &[(1280,1327)]),
1419    ("Deseret", &[(66560,66639)]),
1420    ("Devanagari", &[(2304,2431)]),
1421    ("Dingbats", &[(9984,10175)]),
1422    ("EnclosedAlphanumerics", &[(9312,9471)]),
1423    ("EnclosedCJKLettersandMonths", &[(12800,13055)]),
1424    ("Ethiopic", &[(4608,4991)]),
1425    ("GeneralPunctuation", &[(8192,8303)]),
1426    ("GeometricShapes", &[(9632,9727)]),
1427    ("Georgian", &[(4256,4351)]),
1428    ("Gothic", &[(66352,66383)]),
1429    ("Greek", &[(880,1023)]),
1430    ("GreekExtended", &[(7936,8191)]),
1431    ("GreekandCoptic", &[(880,1023)]),
1432    ("Gujarati", &[(2688,2815)]),
1433    ("Gurmukhi", &[(2560,2687)]),
1434    ("HalfwidthandFullwidthForms", &[(65280,65519)]),
1435    ("HangulCompatibilityJamo", &[(12592,12687)]),
1436    ("HangulJamo", &[(4352,4607)]),
1437    ("HangulSyllables", &[(44032,55215)]),
1438    ("Hanunoo", &[(5920,5951)]),
1439    ("Hebrew", &[(1424,1535)]),
1440    ("HighPrivateUseSurrogates", &[(56192,56319)]),
1441    ("HighSurrogates", &[(55296,56191)]),
1442    ("Hiragana", &[(12352,12447)]),
1443    ("IPAExtensions", &[(592,687)]),
1444    ("IdeographicDescriptionCharacters", &[(12272,12287)]),
1445    ("Kanbun", &[(12688,12703)]),
1446    ("KangxiRadicals", &[(12032,12255)]),
1447    ("Kannada", &[(3200,3327)]),
1448    ("Katakana", &[(12448,12543)]),
1449    ("KatakanaPhoneticExtensions", &[(12784,12799)]),
1450    ("Khmer", &[(6016,6143)]),
1451    ("KhmerSymbols", &[(6624,6655)]),
1452    ("Lao", &[(3712,3839)]),
1453    ("Latin-1Supplement", &[(128,255)]),
1454    ("LatinExtended-A", &[(256,383)]),
1455    ("LatinExtended-B", &[(384,591)]),
1456    ("LatinExtendedAdditional", &[(7680,7935)]),
1457    ("LetterlikeSymbols", &[(8448,8527)]),
1458    ("Limbu", &[(6400,6479)]),
1459    ("LinearBIdeograms", &[(65664,65791)]),
1460    ("LinearBSyllabary", &[(65536,65663)]),
1461    ("LowSurrogates", &[(56320,57343)]),
1462    ("Malayalam", &[(3328,3455)]),
1463    ("MathematicalAlphanumericSymbols", &[(119808,120831)]),
1464    ("MathematicalOperators", &[(8704,8959)]),
1465    ("MiscellaneousMathematicalSymbols-A", &[(10176,10223)]),
1466    ("MiscellaneousMathematicalSymbols-B", &[(10624,10751)]),
1467    ("MiscellaneousSymbols", &[(9728,9983)]),
1468    ("MiscellaneousSymbolsandArrows", &[(11008,11263)]),
1469    ("MiscellaneousTechnical", &[(8960,9215)]),
1470    ("Mongolian", &[(6144,6319)]),
1471    ("MusicalSymbols", &[(119040,119295)]),
1472    ("Myanmar", &[(4096,4255)]),
1473    ("NumberForms", &[(8528,8591)]),
1474    ("Ogham", &[(5760,5791)]),
1475    ("OldItalic", &[(66304,66351)]),
1476    ("OpticalCharacterRecognition", &[(9280,9311)]),
1477    ("Oriya", &[(2816,2943)]),
1478    ("Osmanya", &[(66688,66735)]),
1479    ("PhoneticExtensions", &[(7424,7551)]),
1480    ("PrivateUse", &[(57344,63743),(983040,1048575),(1048576,1114111)]),
1481    ("PrivateUseArea", &[(57344,63743)]),
1482    ("Runic", &[(5792,5887)]),
1483    ("Shavian", &[(66640,66687)]),
1484    ("Sinhala", &[(3456,3583)]),
1485    ("SmallFormVariants", &[(65104,65135)]),
1486    ("SpacingModifierLetters", &[(688,767)]),
1487    ("Specials", &[(65520,65535)]),
1488    ("SuperscriptsandSubscripts", &[(8304,8351)]),
1489    ("SupplementalArrows-A", &[(10224,10239)]),
1490    ("SupplementalArrows-B", &[(10496,10623)]),
1491    ("SupplementalMathematicalOperators", &[(10752,11007)]),
1492    ("SupplementaryPrivateUseArea-A", &[(983040,1048575)]),
1493    ("SupplementaryPrivateUseArea-B", &[(1048576,1114111)]),
1494    ("Syriac", &[(1792,1871)]),
1495    ("Tagalog", &[(5888,5919)]),
1496    ("Tagbanwa", &[(5984,6015)]),
1497    ("Tags", &[(917504,917631)]),
1498    ("TaiLe", &[(6480,6527)]),
1499    ("TaiXuanJingSymbols", &[(119552,119647)]),
1500    ("Tamil", &[(2944,3071)]),
1501    ("Telugu", &[(3072,3199)]),
1502    ("Thaana", &[(1920,1983)]),
1503    ("Thai", &[(3584,3711)]),
1504    ("Tibetan", &[(3840,4095)]),
1505    ("Ugaritic", &[(66432,66463)]),
1506    ("UnifiedCanadianAboriginalSyllabics", &[(5120,5759)]),
1507    ("VariationSelectors", &[(65024,65039)]),
1508    ("VariationSelectorsSupplement", &[(917760,917999)]),
1509    ("YiRadicals", &[(42128,42191)]),
1510    ("YiSyllables", &[(40960,42127)]),
1511    ("YijingHexagramSymbols", &[(19904,19967)]),
1512];
1513
1514#[rustfmt::skip]
1515
1516static XML_UCS_CATS: &[(&str, &[(u32, u32)])] = &[
1517    ("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)]),
1518    ("Cc", &[(0,31),(127,159)]),
1519    ("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)]),
1520    ("Co", &[(57344,57344),(63743,63743),(983040,983040),(1048573,1048573),(1048576,1048576),(1114109,1114109)]),
1521    ("Cs", &[(55296,57343)]),
1522    ("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)]),
1523    ("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)]),
1524    ("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)]),
1525    ("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)]),
1526    ("Lt", &[(453,453),(456,456),(459,459),(498,498),(8072,8079),(8088,8095),(8104,8111),(8124,8124),(8140,8140),(8188,8188)]),
1527    ("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)]),
1528    ("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)]),
1529    ("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)]),
1530    ("Me", &[(1160,1161),(1758,1758),(8413,8416),(8418,8420)]),
1531    ("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)]),
1532    ("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)]),
1533    ("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)]),
1534    ("Nl", &[(5870,5872),(8544,8579),(12295,12295),(12321,12329),(12344,12346),(66378,66378)]),
1535    ("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)]),
1536    ("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)]),
1537    ("Pc", &[(95,95),(8255,8256),(8276,8276),(12539,12539),(65075,65076),(65101,65103),(65343,65343),(65381,65381)]),
1538    ("Pd", &[(45,45),(1418,1418),(6150,6150),(8208,8213),(12316,12316),(12336,12336),(12448,12448),(65073,65074),(65112,65112),(65123,65123),(65293,65293)]),
1539    ("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)]),
1540    ("Pf", &[(187,187),(8217,8217),(8221,8221),(8250,8250)]),
1541    ("Pi", &[(171,171),(8216,8216),(8219,8220),(8223,8223),(8249,8249)]),
1542    ("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)]),
1543    ("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)]),
1544    ("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)]),
1545    ("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)]),
1546    ("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)]),
1547    ("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)]),
1548    ("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)]),
1549    ("Z", &[(32,32),(160,160),(5760,5760),(6158,6158),(8192,8202),(8232,8233),(8239,8239),(8287,8287),(12288,12288)]),
1550    ("Zl", &[(8232,8232)]),
1551    ("Zp", &[(8233,8233)]),
1552    ("Zs", &[(32,32),(160,160),(5760,5760),(6158,6158),(8192,8202),(8239,8239),(8287,8287),(12288,12288)]),
1553];
1554
1555/// # UPSTREAM-PARITY
1556///
1557/// ```c
1558/// int xmlUCSIsBlock(int code, const char *block);
1559/// ```
1560///
1561/// Legacy (xmlregexp.c): 1 if `code` is in the named Unicode block, 0 if
1562/// not, -1 for an unknown block name.
1563#[no_mangle]
1564pub unsafe extern "C" fn xmlUCSIsBlock(code: c_int, block: *const c_char) -> c_int {
1565    if block.is_null() || XML_UCS_BLOCKS.is_empty() {
1566        return -1;
1567    }
1568    let mut low = 0usize;
1569    let mut high = XML_UCS_BLOCKS.len() - 1;
1570    while low <= high {
1571        let mid = (low + high) / 2;
1572        let (name, ranges) = XML_UCS_BLOCKS[mid];
1573        match unsafe { cstr_cmp(block, name.as_bytes()) } {
1574            core::cmp::Ordering::Equal => return ucs_in_ranges(code, ranges),
1575            core::cmp::Ordering::Less => {
1576                if mid == 0 {
1577                    break;
1578                }
1579                high = mid - 1;
1580            }
1581            core::cmp::Ordering::Greater => low = mid + 1,
1582        }
1583    }
1584    -1
1585}
1586
1587/// # UPSTREAM-PARITY
1588///
1589/// ```c
1590/// int xmlUCSIsCat(int code, const char *cat);
1591/// ```
1592///
1593/// Legacy: 1 if `code` belongs to the named Unicode general category, 0 if
1594/// not, -1 for an unknown category name.
1595#[no_mangle]
1596pub unsafe extern "C" fn xmlUCSIsCat(code: c_int, cat: *const c_char) -> c_int {
1597    if cat.is_null() || XML_UCS_CATS.is_empty() {
1598        return -1;
1599    }
1600    let mut low = 0usize;
1601    let mut high = XML_UCS_CATS.len() - 1;
1602    while low <= high {
1603        let mid = (low + high) / 2;
1604        let (name, ranges) = XML_UCS_CATS[mid];
1605        match unsafe { cstr_cmp(cat, name.as_bytes()) } {
1606            core::cmp::Ordering::Equal => return ucs_in_ranges(code, ranges),
1607            core::cmp::Ordering::Less => {
1608                if mid == 0 {
1609                    break;
1610                }
1611                high = mid - 1;
1612            }
1613            core::cmp::Ordering::Greater => low = mid + 1,
1614        }
1615    }
1616    -1
1617}
1618
1619/// # UPSTREAM-PARITY
1620///
1621/// ```c
1622/// int xmlUCSIsCatCc(int code);
1623/// ```
1624///
1625/// Control characters (C0 + DEL + C1).
1626#[no_mangle]
1627pub extern "C" fn xmlUCSIsCatCc(code: c_int) -> c_int {
1628    if (code >= 0x0 && code <= 0x1f) || (code >= 0x7f && code <= 0x9f) {
1629        1
1630    } else {
1631        0
1632    }
1633}
1634
1635// ═══════════════════════════════════════════════════════════════════════════════
1636// 4. valid family
1637// ═══════════════════════════════════════════════════════════════════════════════
1638
1639/// Normalize an attribute value in place (upstream valid.c
1640/// `xmlValidNormalizeString`): trim leading/trailing spaces and collapse
1641/// runs of spaces to a single space.
1642unsafe fn normalize_string(str_: *mut xmlChar) {
1643    if str_.is_null() {
1644        return;
1645    }
1646    let mut src = str_;
1647    let mut dst = str_;
1648    while unsafe { *src == 0x20 } {
1649        src = src.add(1);
1650    }
1651    loop {
1652        let c = unsafe { *src };
1653        if c == 0 {
1654            break;
1655        }
1656        if c == 0x20 {
1657            while unsafe { *src == 0x20 } {
1658                src = src.add(1);
1659            }
1660            if unsafe { *src != 0 } {
1661                unsafe {
1662                    *dst = 0x20;
1663                }
1664                dst = dst.add(1);
1665            }
1666        } else {
1667            unsafe {
1668                *dst = *src;
1669            }
1670            dst = dst.add(1);
1671            src = src.add(1);
1672        }
1673    }
1674    unsafe {
1675        *dst = 0;
1676    }
1677}
1678
1679/// Report a validation memory error (upstream `xmlVErrMemory`).
1680unsafe fn v_err_memory(ctxt: *mut _xmlValidCtxt) {
1681    if !ctxt.is_null() {
1682        unsafe {
1683            (*ctxt).valid = 0;
1684        }
1685    }
1686    unsafe {
1687        crate::xml::errors::raise_error(
1688            ptr::null_mut(),
1689            ptr::null_mut(),
1690            ptr::null_mut(),
1691            ptr::null_mut(),
1692            ptr::null_mut(),
1693            XML_FROM_VALID,
1694            XML_ERR_NO_MEMORY,
1695            XML_ERR_FATAL as c_int,
1696            ptr::null(),
1697            0,
1698            ptr::null(),
1699            ptr::null(),
1700            ptr::null(),
1701            0,
1702            0,
1703            b"Memory allocation failed : \n\0".as_ptr() as *const c_char,
1704        );
1705    }
1706}
1707
1708// XML_DTD_NOT_STANDALONE (xmlerror.h, value 530) — not re-exported by the
1709// candidate's constants module.
1710const XML_DTD_NOT_STANDALONE: c_int = 530;
1711
1712/// Report a validation error with node context (upstream `xmlErrValidNode`),
1713/// and clear `ctxt->valid`.
1714unsafe fn v_err_valid_node(
1715    ctxt: *mut _xmlValidCtxt,
1716    _node: *mut _xmlNode,
1717    code: c_int,
1718    msg: *const c_char,
1719    str1: *const xmlChar,
1720    str2: *const xmlChar,
1721) {
1722    if !ctxt.is_null() {
1723        unsafe {
1724            (*ctxt).valid = 0;
1725        }
1726    }
1727    let mut buf = Vec::new();
1728    let mut arg_idx = 0;
1729    if !msg.is_null() {
1730        let mut i = 0usize;
1731        loop {
1732            let c = unsafe { *((msg as *const u8).add(i)) };
1733            if c == 0 {
1734                break;
1735            }
1736            if c == b'%' {
1737                let c2 = unsafe { *((msg as *const u8).add(i + 1)) };
1738                if c2 == b's' {
1739                    if arg_idx == 0 {
1740                        append_xmlstr(&mut buf, str1);
1741                    } else {
1742                        append_xmlstr(&mut buf, str2);
1743                    }
1744                    arg_idx += 1;
1745                    i += 2;
1746                    continue;
1747                }
1748            }
1749            buf.push(c);
1750            i += 1;
1751        }
1752    }
1753    buf.push(0);
1754    unsafe {
1755        crate::xml::errors::raise_error(
1756            ptr::null_mut(),
1757            ptr::null_mut(),
1758            ptr::null_mut(),
1759            ptr::null_mut(),
1760            ptr::null_mut(),
1761            XML_FROM_VALID,
1762            code,
1763            XML_ERR_ERROR as c_int,
1764            ptr::null(),
1765            0,
1766            ptr::null(),
1767            ptr::null(),
1768            ptr::null(),
1769            0,
1770            0,
1771            buf.as_ptr() as *const c_char,
1772        );
1773    }
1774}
1775
1776/// # UPSTREAM-PARITY
1777///
1778/// ```c
1779/// xmlChar *xmlValidNormalizeAttributeValue(xmlDoc *doc, xmlNode *elem,
1780///                                          const xmlChar *name,
1781///                                          const xmlChar *value);
1782/// ```
1783#[no_mangle]
1784pub unsafe extern "C" fn xmlValidNormalizeAttributeValue(
1785    doc: *mut _xmlDoc,
1786    elem: *mut _xmlNode,
1787    name: *const xmlChar,
1788    value: *const xmlChar,
1789) -> *mut xmlChar {
1790    if doc.is_null() || elem.is_null() || name.is_null() || value.is_null() {
1791        return ptr::null_mut();
1792    }
1793    unsafe {
1794        let mut attr_decl =
1795            crate::xml::validation::get_dtd_attr_desc((*doc).intSubset, (*elem).name, name);
1796        if attr_decl.is_null() && !(*doc).extSubset.is_null() {
1797            attr_decl =
1798                crate::xml::validation::get_dtd_attr_desc((*doc).extSubset, (*elem).name, name);
1799        }
1800        if attr_decl.is_null() || (*attr_decl).atype == XML_ATTRIBUTE_CDATA as c_int {
1801            return ptr::null_mut();
1802        }
1803        let ret = crate::abi::exports_xml2::xmlStrdup(value);
1804        if ret.is_null() {
1805            return ptr::null_mut();
1806        }
1807        normalize_string(ret);
1808        ret
1809    }
1810}
1811
1812/// # UPSTREAM-PARITY
1813///
1814/// ```c
1815/// xmlChar *xmlValidCtxtNormalizeAttributeValue(xmlValidCtxt *ctxt,
1816///                                              xmlDoc *doc, xmlNode *elem,
1817///                                              const xmlChar *name,
1818///                                              const xmlChar *value);
1819/// ```
1820#[no_mangle]
1821pub unsafe extern "C" fn xmlValidCtxtNormalizeAttributeValue(
1822    ctxt: *mut _xmlValidCtxt,
1823    doc: *mut _xmlDoc,
1824    elem: *mut _xmlNode,
1825    name: *const xmlChar,
1826    value: *const xmlChar,
1827) -> *mut xmlChar {
1828    if doc.is_null() || elem.is_null() || name.is_null() || value.is_null() {
1829        return ptr::null_mut();
1830    }
1831    unsafe {
1832        let mut prefix: *mut xmlChar = ptr::null_mut();
1833        let local_name = crate::xml::string::split_qname2(name, &mut prefix);
1834        if local_name.is_null() {
1835            if !prefix.is_null() {
1836                xmlFree(prefix as *mut c_void);
1837            }
1838            v_err_memory(ctxt);
1839            return ptr::null_mut();
1840        }
1841
1842        let mut attr_decl: *mut _xmlAttribute = ptr::null_mut();
1843        let mut extsubset = 0;
1844
1845        let ns = (*elem).ns;
1846        if !ns.is_null() && !(*ns).prefix.is_null() {
1847            let mut buf = [0u8; 50];
1848            let elemname = crate::xml::string::build_qname(
1849                (*elem).name,
1850                (*ns).prefix,
1851                buf.as_mut_ptr() as *mut xmlChar,
1852                50,
1853            );
1854            if elemname.is_null() {
1855                if !prefix.is_null() {
1856                    xmlFree(prefix as *mut c_void);
1857                }
1858                v_err_memory(ctxt);
1859                return ptr::null_mut();
1860            }
1861            if !(*doc).intSubset.is_null() {
1862                attr_decl = crate::xml::hash::hash_lookup3(
1863                    (*(*doc).intSubset).attributes as *mut crate::xml::hash::HashTable,
1864                    local_name,
1865                    prefix,
1866                    elemname,
1867                ) as *mut _xmlAttribute;
1868            }
1869            if attr_decl.is_null() && !(*doc).extSubset.is_null() {
1870                attr_decl = crate::xml::hash::hash_lookup3(
1871                    (*(*doc).extSubset).attributes as *mut crate::xml::hash::HashTable,
1872                    local_name,
1873                    prefix,
1874                    elemname,
1875                ) as *mut _xmlAttribute;
1876                if !attr_decl.is_null() {
1877                    extsubset = 1;
1878                }
1879            }
1880            if elemname as *const xmlChar != (*elem).name {
1881                xmlFree(elemname as *mut c_void);
1882            }
1883        }
1884        if attr_decl.is_null() && !(*doc).intSubset.is_null() {
1885            attr_decl = crate::xml::hash::hash_lookup3(
1886                (*(*doc).intSubset).attributes as *mut crate::xml::hash::HashTable,
1887                local_name,
1888                prefix,
1889                (*elem).name,
1890            ) as *mut _xmlAttribute;
1891        }
1892        if attr_decl.is_null() && !(*doc).extSubset.is_null() {
1893            attr_decl = crate::xml::hash::hash_lookup3(
1894                (*(*doc).extSubset).attributes as *mut crate::xml::hash::HashTable,
1895                local_name,
1896                prefix,
1897                (*elem).name,
1898            ) as *mut _xmlAttribute;
1899            if !attr_decl.is_null() {
1900                extsubset = 1;
1901            }
1902        }
1903
1904        if attr_decl.is_null() || (*attr_decl).atype == XML_ATTRIBUTE_CDATA as c_int {
1905            if !prefix.is_null() {
1906                xmlFree(prefix as *mut c_void);
1907            }
1908            return ptr::null_mut();
1909        }
1910        let ret = crate::abi::exports_xml2::xmlStrdup(value);
1911        if ret.is_null() {
1912            if !prefix.is_null() {
1913                xmlFree(prefix as *mut c_void);
1914            }
1915            v_err_memory(ctxt);
1916            return ptr::null_mut();
1917        }
1918        normalize_string(ret);
1919        if (*doc).standalone != 0
1920            && extsubset == 1
1921            && crate::abi::exports_xml2::xmlStrEqual(value, ret) == 0
1922        {
1923            v_err_valid_node(
1924                ctxt,
1925                elem,
1926                XML_DTD_NOT_STANDALONE,
1927                b"standalone: %s on %s value had to be normalized based on external subset declaration\n\0"
1928                    .as_ptr() as *const c_char,
1929                name,
1930                (*elem).name,
1931            );
1932        }
1933        if !prefix.is_null() {
1934            xmlFree(prefix as *mut c_void);
1935        }
1936        ret
1937    }
1938}
1939
1940/// # UPSTREAM-PARITY
1941///
1942/// ```c
1943/// int xmlValidGetPotentialChildren(xmlElementContent *ctree,
1944///                                  const xmlChar **names,
1945///                                  int *len, int max);
1946/// ```
1947#[no_mangle]
1948pub unsafe extern "C" fn xmlValidGetPotentialChildren(
1949    ctree: *mut _xmlElementContent,
1950    names: *mut *const xmlChar,
1951    len: *mut c_int,
1952    max: c_int,
1953) -> c_int {
1954    if ctree.is_null() || names.is_null() || len.is_null() {
1955        return -1;
1956    }
1957    unsafe {
1958        if *len >= max {
1959            return *len;
1960        }
1961        let pcdata = b"#PCDATA\0";
1962        match (*ctree).type_ as u32 {
1963            t if t == XML_ELEMENT_CONTENT_PCDATA as u32 => {
1964                for i in 0..(*len as usize) {
1965                    if crate::abi::exports_xml2::xmlStrEqual(
1966                        pcdata.as_ptr() as *const xmlChar,
1967                        *names.add(i),
1968                    ) != 0
1969                    {
1970                        return *len;
1971                    }
1972                }
1973                *names.add(*len as usize) = pcdata.as_ptr() as *const xmlChar;
1974                *len += 1;
1975            }
1976            t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
1977                for i in 0..(*len as usize) {
1978                    if crate::abi::exports_xml2::xmlStrEqual((*ctree).name, *names.add(i)) != 0 {
1979                        return *len;
1980                    }
1981                }
1982                *names.add(*len as usize) = (*ctree).name;
1983                *len += 1;
1984            }
1985            t if t == XML_ELEMENT_CONTENT_SEQ as u32 || t == XML_ELEMENT_CONTENT_OR as u32 => {
1986                xmlValidGetPotentialChildren((*ctree).c1, names, len, max);
1987                xmlValidGetPotentialChildren((*ctree).c2, names, len, max);
1988            }
1989            _ => {}
1990        }
1991        *len
1992    }
1993}
1994
1995/// Dummy validity error handler that suppresses messages (upstream
1996/// `xmlNoValidityErr`).
1997unsafe extern "C" fn xml_no_validity_err(_ctx: *mut c_void, _msg: *const c_char) {}
1998
1999/// # UPSTREAM-PARITY
2000///
2001/// ```c
2002/// int xmlValidGetValidElements(xmlNode *prev, xmlNode *next,
2003///                              const xmlChar **names, int max);
2004/// ```
2005#[no_mangle]
2006pub unsafe extern "C" fn xmlValidGetValidElements(
2007    prev: *mut _xmlNode,
2008    next: *mut _xmlNode,
2009    names: *mut *const xmlChar,
2010    max: c_int,
2011) -> c_int {
2012    if prev.is_null() && next.is_null() {
2013        return -1;
2014    }
2015    if names.is_null() || max <= 0 {
2016        return -1;
2017    }
2018    unsafe {
2019        // Local validation context with errors suppressed, exactly like the
2020        // upstream xmlValidCtxt stack instance in valid.c.
2021        let mut vctxt: _xmlValidCtxt = zeroed();
2022        vctxt.error = Some(xml_no_validity_err);
2023
2024        let mut nb_valid_elements = 0;
2025        let ref_node = if prev.is_null() { next } else { prev };
2026        let parent = (*ref_node).parent;
2027
2028        /*
2029         * Retrieves the parent element declaration.
2030         */
2031        let mut element_desc = if (*parent).doc.is_null() {
2032            ptr::null_mut()
2033        } else {
2034            crate::xml::validation::get_dtd_element_desc((*(*parent).doc).intSubset, (*parent).name)
2035        };
2036        if element_desc.is_null()
2037            && !(*parent).doc.is_null()
2038            && !(*(*parent).doc).extSubset.is_null()
2039        {
2040            element_desc = crate::xml::validation::get_dtd_element_desc(
2041                (*(*parent).doc).extSubset,
2042                (*parent).name,
2043            );
2044        }
2045        if element_desc.is_null() {
2046            return -1;
2047        }
2048
2049        /*
2050         * Do a backup of the current tree structure.
2051         */
2052        let prev_next = if prev.is_null() {
2053            ptr::null_mut()
2054        } else {
2055            (*prev).next
2056        };
2057        let next_prev = if next.is_null() {
2058            ptr::null_mut()
2059        } else {
2060            (*next).prev
2061        };
2062        let parent_childs = (*parent).children;
2063        let parent_last = (*parent).last;
2064
2065        /*
2066         * Create a dummy node and insert it into the tree.
2067         */
2068        let dummy_name = b"<!dummy?>\0";
2069        let test_node = crate::abi::exports_tree::xmlNewDocNode(
2070            (*ref_node).doc,
2071            ptr::null_mut(),
2072            dummy_name.as_ptr() as *const xmlChar,
2073            ptr::null(),
2074        );
2075        if test_node.is_null() {
2076            return -1;
2077        }
2078
2079        (*test_node).parent = parent;
2080        (*test_node).prev = prev;
2081        (*test_node).next = next;
2082        let name = (*test_node).name;
2083
2084        if prev.is_null() {
2085            (*parent).children = test_node;
2086        } else {
2087            (*prev).next = test_node;
2088        }
2089        if next.is_null() {
2090            (*parent).last = test_node;
2091        } else {
2092            (*next).prev = test_node;
2093        }
2094
2095        /*
2096         * Insert each potential child node and check if the parent is
2097         * still valid.
2098         */
2099        let mut elements: [*const xmlChar; 256] = [ptr::null(); 256];
2100        let mut nb_elements = 0;
2101        nb_elements = xmlValidGetPotentialChildren(
2102            (*element_desc).content,
2103            elements.as_mut_ptr(),
2104            &mut nb_elements,
2105            256,
2106        );
2107
2108        let mut i = 0;
2109        while i < nb_elements {
2110            (*test_node).name = elements[i as usize];
2111            if crate::xml::validation::validate_one_element(&mut vctxt, (*parent).doc, parent) != 0
2112            {
2113                let mut j = 0;
2114                while j < nb_valid_elements {
2115                    if crate::abi::exports_xml2::xmlStrEqual(
2116                        elements[i as usize],
2117                        *names.add(j as usize),
2118                    ) != 0
2119                    {
2120                        break;
2121                    }
2122                    j += 1;
2123                }
2124                if j >= nb_valid_elements {
2125                    *names.add(nb_valid_elements as usize) = elements[i as usize];
2126                    nb_valid_elements += 1;
2127                    if nb_valid_elements >= max {
2128                        break;
2129                    }
2130                }
2131            }
2132            i += 1;
2133        }
2134
2135        /*
2136         * Restore the tree structure.
2137         */
2138        if prev.is_null() {
2139            (*parent).children = parent_childs;
2140        } else {
2141            (*prev).next = prev_next;
2142        }
2143        if next.is_null() {
2144            (*parent).last = parent_last;
2145        } else {
2146            (*next).prev = next_prev;
2147        }
2148
2149        /*
2150         * Free the dummy node.
2151         */
2152        (*test_node).name = name;
2153        crate::abi::exports_xml2::xmlFreeNode(test_node);
2154
2155        nb_valid_elements
2156    }
2157}
2158
2159// ═══════════════════════════════════════════════════════════════════════════════
2160// 5. misc2 family
2161// ═══════════════════════════════════════════════════════════════════════════════
2162
2163// ── __xml* aliases (globals.c / xmlIO.c) ───────────────────────────────────────
2164
2165/// Upstream `__xmlDefaultSAXHandler(void)` — pointer to `xmlDefaultSAXHandler`.
2166#[no_mangle]
2167pub unsafe extern "C" fn __xmlDefaultSAXHandler() -> *mut _xmlSAXHandlerV1 {
2168    // SAFETY: returning a pointer to an exported static; the caller may
2169    // read/write it exactly as with upstream's deprecated accessor.
2170    core::ptr::addr_of!(crate::abi::data_globals::xmlDefaultSAXHandler) as *mut _xmlSAXHandlerV1
2171}
2172
2173/// Upstream `__xmlDefaultSAXLocator(void)` — pointer to `xmlDefaultSAXLocator`.
2174#[no_mangle]
2175pub unsafe extern "C" fn __xmlDefaultSAXLocator() -> *mut _xmlSAXLocator {
2176    // SAFETY: returning a pointer to an exported static; the caller may
2177    // read/write it exactly as with upstream's deprecated accessor.
2178    core::ptr::addr_of!(crate::abi::data_globals::xmlDefaultSAXLocator) as *mut _xmlSAXLocator
2179}
2180
2181/// Upstream `__xmlLastError(void)` — pointer to the exported `xmlLastError`
2182/// mirror (kept in sync with the thread-local error state on every raise).
2183#[no_mangle]
2184pub unsafe extern "C" fn __xmlLastError() -> *mut _xmlError {
2185    // SAFETY: returning a pointer to an exported static; the caller may
2186    // read/write it exactly as with upstream's deprecated accessor.
2187    core::ptr::addr_of!(crate::abi::data_globals::xmlLastError) as *mut _xmlError
2188}
2189
2190/// Upstream `__xmlParserInputBufferCreateFilename(const char *URI,
2191/// xmlCharEncoding enc)` — the non-reentrant internal variant; forwards to
2192/// the regular `xmlParserInputBufferCreateFilename`.
2193#[no_mangle]
2194pub unsafe extern "C" fn __xmlParserInputBufferCreateFilename(
2195    URI: *const c_char,
2196    enc: c_int,
2197) -> *mut _xmlParserInputBuffer {
2198    crate::abi::exports_xml2::xmlParserInputBufferCreateFilename(URI, enc)
2199}
2200
2201// ── xmlFormatError (error.c) ───────────────────────────────────────────────────
2202
2203/// Compute the input window around `input->cur` (upstream parserInternals.c
2204/// `xmlParserInputGetWindow`).
2205unsafe fn parser_input_get_window(
2206    input: *mut _xmlParserInput,
2207    start_out: *mut *const xmlChar,
2208    size_in_out: *mut c_int,
2209    offset_out: *mut c_int,
2210) {
2211    unsafe {
2212        let mut cur = (*input).cur;
2213        let base = (*input).base;
2214        let size = *size_in_out;
2215        // Skip backwards over any end-of-lines.
2216        while cur > base && (*cur == b'\n' || *cur == b'\r') {
2217            cur = cur.sub(1);
2218        }
2219        let mut n: usize = 0;
2220        // Search backwards for beginning-of-line (to max buff size).
2221        while n < size as usize && cur > base && *cur != b'\n' && *cur != b'\r' {
2222            cur = cur.sub(1);
2223            n += 1;
2224        }
2225        if n > 0 && (*cur == b'\n' || *cur == b'\r') {
2226            cur = cur.add(1);
2227        } else {
2228            // Skip over continuation bytes.
2229            while cur < (*input).cur && (*cur & 0xC0) == 0x80 {
2230                cur = cur.add(1);
2231            }
2232        }
2233        // Calculate the error position in terms of the current position.
2234        let mut col = (*input).cur as usize - cur as usize;
2235        // Search forward for end-of-line (to max buff size).
2236        let mut nfwd: usize = 0;
2237        let start = cur;
2238        while *cur != 0 && *cur != b'\n' && *cur != b'\r' {
2239            let avail = (*input).end as usize - cur as usize;
2240            let mut clen: c_int = avail as c_int;
2241            let c = xmlGetUTF8Char(cur, &mut clen);
2242            if c < 0 || nfwd + clen as usize > size as usize {
2243                break;
2244            }
2245            cur = cur.add(clen as usize);
2246            nfwd += clen as usize;
2247        }
2248        if col >= nfwd {
2249            col = if nfwd < size as usize {
2250                nfwd
2251            } else {
2252                size as usize - 1
2253            };
2254        }
2255        *start_out = start;
2256        *size_in_out = nfwd as c_int;
2257        *offset_out = col as c_int;
2258    }
2259}
2260
2261/// Print the source context around an input position (upstream error.c
2262/// `xmlParserPrintFileContextInternal`).
2263unsafe fn print_file_context(
2264    input: *mut _xmlParserInput,
2265    channel: xmlGenericErrorFunc,
2266    data: *mut c_void,
2267) {
2268    if input.is_null() || unsafe { (*input).cur.is_null() } {
2269        return;
2270    }
2271    let mut n: c_int = 80;
2272    let mut start: *const xmlChar = ptr::null();
2273    let mut col: c_int = 0;
2274    unsafe { parser_input_get_window(input, &mut start, &mut n, &mut col) };
2275    let mut content = [0u8; 81];
2276    if n > 0 && !start.is_null() {
2277        unsafe {
2278            ptr::copy_nonoverlapping(start, content.as_mut_ptr(), n as usize);
2279        }
2280    }
2281    content[n as usize] = 0;
2282    unsafe { chan_emit(channel, data, &content[..n as usize]) };
2283    // Create blank line with problem pointer.
2284    let mut i = 0usize;
2285    while i < col as usize {
2286        if content[i] != b'\t' {
2287            content[i] = b' ';
2288        }
2289        i += 1;
2290    }
2291    content[i] = b'^';
2292    i += 1;
2293    content[i] = 0;
2294    unsafe { chan_emit(channel, data, &content[..i]) };
2295}
2296
2297/// # UPSTREAM-PARITY
2298///
2299/// ```c
2300/// void xmlFormatError(const xmlError *err, xmlGenericErrorFunc channel,
2301///                     void *data);
2302/// ```
2303#[no_mangle]
2304pub unsafe extern "C" fn xmlFormatError(
2305    err: *const _xmlError,
2306    channel: Option<xmlGenericErrorFunc>,
2307    data: *mut c_void,
2308) {
2309    if err.is_null() || channel.is_none() {
2310        return;
2311    }
2312    let channel = channel.unwrap();
2313    unsafe {
2314        let e = &*err;
2315        let message = e.message;
2316        let file = e.file;
2317        let line = e.line;
2318        let code = e.code;
2319        let domain = e.domain;
2320        let level = e.level;
2321        let node = e.node as *mut _xmlNode;
2322
2323        if code == XML_ERR_OK {
2324            return;
2325        }
2326
2327        let mut ctxt: *mut _xmlParserCtxt = ptr::null_mut();
2328        if domain == XML_FROM_PARSER
2329            || domain == XML_FROM_HTML
2330            || domain == XML_FROM_DTD
2331            || domain == XML_FROM_NAMESPACE
2332            || domain == XML_FROM_IO
2333            || domain == XML_FROM_VALID
2334        {
2335            ctxt = e.ctxt as *mut _xmlParserCtxt;
2336        }
2337
2338        let mut name: *const xmlChar = ptr::null();
2339        if !node.is_null()
2340            && (*node).type_ == XML_ELEMENT_NODE as c_int
2341            && domain != XML_FROM_SCHEMASV
2342        {
2343            name = (*node).name;
2344        }
2345
2346        let mut input: *mut _xmlParserInput = ptr::null_mut();
2347        let mut cur: *mut _xmlParserInput = ptr::null_mut();
2348        if !ctxt.is_null() && !(*ctxt).input.is_null() {
2349            input = (*ctxt).input;
2350            if (*input).filename.is_null() && (*ctxt).inputNr > 1 {
2351                cur = input;
2352                input = *(*ctxt).inputTab.add((*ctxt).inputNr as usize - 2);
2353            }
2354            if !(*input).filename.is_null() {
2355                let mut buf = Vec::new();
2356                append_cstr(&mut buf, (*input).filename);
2357                buf.push(b':');
2358                append_int(&mut buf, (*input).line);
2359                buf.extend_from_slice(b": ");
2360                chan_emit(channel, data, &buf);
2361            } else if line != 0 && domain == XML_FROM_PARSER {
2362                let mut buf = b"Entity: line ".to_vec();
2363                append_int(&mut buf, (*input).line);
2364                buf.extend_from_slice(b": ");
2365                chan_emit(channel, data, &buf);
2366            }
2367        } else {
2368            if !file.is_null() {
2369                let mut buf = Vec::new();
2370                append_cstr(&mut buf, file);
2371                buf.push(b':');
2372                append_int(&mut buf, line);
2373                buf.extend_from_slice(b": ");
2374                chan_emit(channel, data, &buf);
2375            } else if line != 0
2376                && (domain == XML_FROM_PARSER
2377                    || domain == XML_FROM_SCHEMASV
2378                    || domain == XML_FROM_SCHEMASP
2379                    || domain == XML_FROM_DTD
2380                    || domain == XML_FROM_RELAXNGP
2381                    || domain == XML_FROM_RELAXNGV)
2382            {
2383                let mut buf = b"Entity: line ".to_vec();
2384                append_int(&mut buf, line);
2385                buf.extend_from_slice(b": ");
2386                chan_emit(channel, data, &buf);
2387            }
2388        }
2389        if !name.is_null() {
2390            let mut buf = b"element ".to_vec();
2391            append_xmlstr(&mut buf, name);
2392            buf.extend_from_slice(b": ");
2393            chan_emit(channel, data, &buf);
2394        }
2395        let domain_prefix: &[u8] = match domain {
2396            XML_FROM_PARSER => b"parser ",
2397            XML_FROM_NAMESPACE => b"namespace ",
2398            XML_FROM_DTD | XML_FROM_VALID => b"validity ",
2399            XML_FROM_HTML => b"HTML parser ",
2400            XML_FROM_MEMORY => b"memory ",
2401            XML_FROM_OUTPUT => b"output ",
2402            XML_FROM_IO => b"I/O ",
2403            XML_FROM_XINCLUDE => b"XInclude ",
2404            XML_FROM_XPATH => b"XPath ",
2405            XML_FROM_XPOINTER => b"parser ",
2406            XML_FROM_REGEXP => b"regexp ",
2407            XML_FROM_MODULE => b"module ",
2408            XML_FROM_SCHEMASV => b"Schemas validity ",
2409            XML_FROM_SCHEMASP => b"Schemas parser ",
2410            XML_FROM_RELAXNGP => b"Relax-NG parser ",
2411            XML_FROM_RELAXNGV => b"Relax-NG validity ",
2412            XML_FROM_CATALOG => b"Catalog ",
2413            XML_FROM_C14N => b"C14N ",
2414            XML_FROM_XSLT => b"XSLT ",
2415            XML_FROM_I18N => b"encoding ",
2416            XML_FROM_SCHEMATRONV => b"schematron ",
2417            XML_FROM_BUFFER => b"internal buffer ",
2418            XML_FROM_URI => b"URI ",
2419            _ => b"",
2420        };
2421        chan_emit(channel, data, domain_prefix);
2422        let level_prefix: &[u8] = if level == XML_ERR_NONE as c_int {
2423            b": "
2424        } else if level == XML_ERR_WARNING as c_int {
2425            b"warning : "
2426        } else if level == XML_ERR_ERROR as c_int || level == XML_ERR_FATAL as c_int {
2427            b"error : "
2428        } else {
2429            b": "
2430        };
2431        chan_emit(channel, data, level_prefix);
2432        if !message.is_null() {
2433            let len = cstr_len(message as *const c_char);
2434            let msg_bytes = core::slice::from_raw_parts(message as *const u8, len);
2435            let mut buf = msg_bytes.to_vec();
2436            if len > 0 && *message.add(len - 1) as u8 != b'\n' {
2437                buf.push(b'\n');
2438            }
2439            chan_emit(channel, data, &buf);
2440        } else {
2441            chan_emit(channel, data, b"No error message provided\n");
2442        }
2443
2444        if !ctxt.is_null() {
2445            if !input.is_null()
2446                && ((*input).buf.is_null() || (*(*input).buf).encoder.is_null())
2447                && code == XML_ERR_INVALID_ENCODING
2448                && (*input).cur < (*input).end
2449            {
2450                let mut buf = b"Bytes:".to_vec();
2451                for i in 0..4 {
2452                    if (*input).cur.add(i) >= (*input).end {
2453                        break;
2454                    }
2455                    let b = *(*input).cur.add(i);
2456                    buf.extend_from_slice(b" 0x");
2457                    buf.push(hex_digit(b >> 4));
2458                    buf.push(hex_digit(b & 0xf));
2459                }
2460                buf.push(b'\n');
2461                chan_emit(channel, data, &buf);
2462            }
2463            print_file_context(input, channel, data);
2464            if !cur.is_null() {
2465                if !(*cur).filename.is_null() {
2466                    let mut buf = Vec::new();
2467                    append_cstr(&mut buf, (*cur).filename);
2468                    buf.extend_from_slice(b": ");
2469                    append_int(&mut buf, (*cur).line);
2470                    buf.extend_from_slice(b": \n");
2471                    chan_emit(channel, data, &buf);
2472                } else if line != 0
2473                    && (domain == XML_FROM_PARSER
2474                        || domain == XML_FROM_SCHEMASV
2475                        || domain == XML_FROM_SCHEMASP
2476                        || domain == XML_FROM_DTD
2477                        || domain == XML_FROM_RELAXNGP
2478                        || domain == XML_FROM_RELAXNGV)
2479                {
2480                    let mut buf = b"Entity: line ".to_vec();
2481                    append_int(&mut buf, (*cur).line);
2482                    buf.extend_from_slice(b": \n");
2483                    chan_emit(channel, data, &buf);
2484                }
2485                print_file_context(cur, channel, data);
2486            }
2487        }
2488        if domain == XML_FROM_XPATH
2489            && !e.str1.is_null()
2490            && e.int1 < 100
2491            && e.int1 < crate::abi::exports_xml2::xmlStrlen(e.str1 as *const xmlChar)
2492        {
2493            let mut buf = Vec::new();
2494            append_cstr(&mut buf, e.str1);
2495            buf.push(b'\n');
2496            chan_emit(channel, data, &buf);
2497            let mut marker = vec![b' '; e.int1 as usize];
2498            marker.push(b'^');
2499            marker.push(b'\n');
2500            chan_emit(channel, data, &marker);
2501        }
2502    }
2503}
2504
2505// ── tree node constructors (tree.c) ───────────────────────────────────────────
2506
2507/// # UPSTREAM-PARITY
2508///
2509/// ```c
2510/// xmlNode *xmlNewCharRef(xmlDoc *doc, const xmlChar *name);
2511/// ```
2512#[no_mangle]
2513pub unsafe extern "C" fn xmlNewCharRef(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlNode {
2514    if name.is_null() {
2515        return ptr::null_mut();
2516    }
2517    crate::abi::exports_tree::xmlNewReference(doc, name)
2518}
2519
2520/// # UPSTREAM-PARITY
2521///
2522/// ```c
2523/// xmlNode *xmlNewDocText(const xmlDoc *doc, const xmlChar *content);
2524/// ```
2525#[no_mangle]
2526pub unsafe extern "C" fn xmlNewDocText(
2527    doc: *const _xmlDoc,
2528    content: *const xmlChar,
2529) -> *mut _xmlNode {
2530    let cur = unsafe { crate::abi::exports_xml2::xmlNewText(content) };
2531    if !cur.is_null() {
2532        unsafe { (*cur).doc = doc as *mut _xmlDoc };
2533    }
2534    cur
2535}
2536
2537/// # UPSTREAM-PARITY
2538///
2539/// ```c
2540/// xmlNode *xmlNewDocTextLen(xmlDoc *doc, const xmlChar *content, int len);
2541/// ```
2542#[no_mangle]
2543pub unsafe extern "C" fn xmlNewDocTextLen(
2544    doc: *mut _xmlDoc,
2545    content: *const xmlChar,
2546    len: c_int,
2547) -> *mut _xmlNode {
2548    let cur = unsafe { crate::abi::exports_tree::xmlNewTextLen(content, len) };
2549    if !cur.is_null() {
2550        unsafe { (*cur).doc = doc };
2551    }
2552    cur
2553}
2554
2555/// # UPSTREAM-PARITY
2556///
2557/// ```c
2558/// xmlNode *xmlNewDocComment(xmlDoc *doc, const xmlChar *content);
2559/// ```
2560#[no_mangle]
2561pub unsafe extern "C" fn xmlNewDocComment(
2562    doc: *mut _xmlDoc,
2563    content: *const xmlChar,
2564) -> *mut _xmlNode {
2565    let cur = unsafe { crate::abi::exports_xml2::xmlNewComment(content) };
2566    if !cur.is_null() {
2567        unsafe { (*cur).doc = doc };
2568    }
2569    cur
2570}
2571
2572/// # UPSTREAM-PARITY
2573///
2574/// ```c
2575/// xmlNode *xmlNewDocPI(xmlDoc *doc, const xmlChar *name,
2576///                      const xmlChar *content);
2577/// ```
2578#[no_mangle]
2579pub unsafe extern "C" fn xmlNewDocPI(
2580    doc: *mut _xmlDoc,
2581    name: *const xmlChar,
2582    content: *const xmlChar,
2583) -> *mut _xmlNode {
2584    let cur = unsafe { crate::abi::exports_xml2::xmlNewPI(name, content) };
2585    if !cur.is_null() {
2586        unsafe { (*cur).doc = doc };
2587    }
2588    cur
2589}
2590
2591/// Split a QName, returning a pointer to the local part and storing the
2592/// prefix length (upstream tree.c `xmlSplitQName3`).
2593unsafe fn qname_split3(name: *const xmlChar, len: *mut c_int) -> *const xmlChar {
2594    if name.is_null() || len.is_null() {
2595        return ptr::null();
2596    }
2597    unsafe {
2598        if *name == b':' as xmlChar {
2599            return ptr::null();
2600        }
2601        let mut l = 0usize;
2602        while *name.add(l) != 0 && *name.add(l) != b':' as xmlChar {
2603            l += 1;
2604        }
2605        if *name.add(l) == 0 {
2606            return ptr::null();
2607        }
2608        *len = l as c_int;
2609        name.add(l + 1)
2610    }
2611}
2612
2613/// # UPSTREAM-PARITY
2614///
2615/// ```c
2616/// xmlElementContent *xmlNewDocElementContent(xmlDoc *doc,
2617///                                            const xmlChar *name,
2618///                                            xmlElementContentType type);
2619/// ```
2620#[no_mangle]
2621pub unsafe extern "C" fn xmlNewDocElementContent(
2622    doc: *mut _xmlDoc,
2623    name: *const xmlChar,
2624    type_: xmlElementContentType,
2625) -> *mut _xmlElementContent {
2626    let _ = doc;
2627    unsafe {
2628        let ret = xmlMallocZero(size_of::<_xmlElementContent>()) as *mut _xmlElementContent;
2629        if ret.is_null() {
2630            return ptr::null_mut();
2631        }
2632        (*ret).type_ = type_ as c_int;
2633        (*ret).ocur = XML_ELEMENT_CONTENT_ONCE as c_int;
2634        if !name.is_null() {
2635            let mut l: c_int = 0;
2636            let tmp = qname_split3(name, &mut l);
2637            if tmp.is_null() {
2638                (*ret).name = crate::abi::exports_xml2::xmlStrdup(name);
2639            } else {
2640                (*ret).prefix = crate::abi::exports_xml2::xmlStrndup(name, l);
2641                (*ret).name = crate::abi::exports_xml2::xmlStrdup(tmp);
2642                if (*ret).prefix.is_null() {
2643                    crate::xml::dtd::free_content_model(ret);
2644                    return ptr::null_mut();
2645                }
2646            }
2647            if (*ret).name.is_null() {
2648                crate::xml::dtd::free_content_model(ret);
2649                return ptr::null_mut();
2650            }
2651        }
2652        ret
2653    }
2654}
2655
2656// ── node content / attribute value (tree.c) ───────────────────────────────────
2657
2658/// # UPSTREAM-PARITY
2659///
2660/// ```c
2661/// int xmlNodeBufGetContent(xmlBuffer *buffer, const xmlNode *cur);
2662/// ```
2663#[no_mangle]
2664pub unsafe extern "C" fn xmlNodeBufGetContent(
2665    buffer: *mut _xmlBuffer,
2666    cur: *const _xmlNode,
2667) -> c_int {
2668    if cur.is_null() || buffer.is_null() {
2669        return -1;
2670    }
2671    let content = unsafe { crate::xml::tree::node_get_content(cur as *mut _xmlNode) };
2672    if content.is_null() {
2673        return 0;
2674    }
2675    let len = unsafe { crate::abi::exports_xml2::xmlStrlen(content) };
2676    let ret = crate::xml::io::buf_add(buffer, content, len);
2677    unsafe { xmlFree(content as *mut c_void) };
2678    if ret < 0 {
2679        -1
2680    } else {
2681        0
2682    }
2683}
2684
2685/// Find an attribute node by name and namespace (upstream tree.c
2686/// `xmlGetPropNodeInternal` with useDTD == 0).
2687unsafe fn find_prop_ns(
2688    node: *const _xmlNode,
2689    name: *const xmlChar,
2690    ns_uri: *const xmlChar,
2691) -> *mut _xmlAttr {
2692    if node.is_null() || unsafe { (*node).type_ != XML_ELEMENT_NODE as c_int } || name.is_null() {
2693        return ptr::null_mut();
2694    }
2695    let mut prop = unsafe { (*node).properties };
2696    while !prop.is_null() {
2697        let ns = unsafe { (*prop).ns };
2698        let ns_match = if ns_uri.is_null() {
2699            ns.is_null()
2700        } else {
2701            !ns.is_null()
2702                && !unsafe { (*ns).href }.is_null()
2703                && unsafe { crate::abi::exports_xml2::xmlStrEqual((*ns).href, ns_uri) != 0 }
2704        };
2705        if ns_match && unsafe { crate::abi::exports_xml2::xmlStrEqual((*prop).name, name) != 0 } {
2706            return prop;
2707        }
2708        prop = unsafe { (*prop).next };
2709    }
2710    ptr::null_mut()
2711}
2712
2713/// # UPSTREAM-PARITY
2714///
2715/// ```c
2716/// int xmlNodeGetAttrValue(const xmlNode *node, const xmlChar *name,
2717///                         const xmlChar *nsUri, xmlChar **out);
2718/// ```
2719///
2720/// Returns 0 with `*out` set to the attribute value, 1 when the attribute
2721/// is missing (or arguments are invalid), -1 on allocation failure.
2722#[no_mangle]
2723pub unsafe extern "C" fn xmlNodeGetAttrValue(
2724    node: *const _xmlNode,
2725    name: *const xmlChar,
2726    ns_uri: *const xmlChar,
2727    out: *mut *mut xmlChar,
2728) -> c_int {
2729    if out.is_null() {
2730        return 1;
2731    }
2732    unsafe { *out = ptr::null_mut() };
2733    let prop = unsafe { find_prop_ns(node, name, ns_uri) };
2734    if prop.is_null() {
2735        return 1;
2736    }
2737    let value = unsafe { crate::xml::tree::node_get_content(prop as *mut _xmlNode) };
2738    if value.is_null() {
2739        return -1;
2740    }
2741    unsafe { *out = value };
2742    0
2743}
2744
2745// ── element content serialization (valid.c) ───────────────────────────────────
2746
2747/// # UPSTREAM-PARITY
2748///
2749/// ```c
2750/// void xmlSprintfElementContent(char *buf, xmlElementContent *content,
2751///                               int englob);
2752/// ```
2753///
2754/// Deprecated; upstream (2.13+) ships this as an empty stub.
2755#[no_mangle]
2756pub unsafe extern "C" fn xmlSprintfElementContent(
2757    _buf: *mut c_char,
2758    _content: *mut _xmlElementContent,
2759    _englob: c_int,
2760) {
2761}
2762
2763/// # UPSTREAM-PARITY
2764///
2765/// ```c
2766/// void xmlSnprintfElementContent(char *buf, int size,
2767///                                xmlElementContent *content, int englob);
2768/// ```
2769#[no_mangle]
2770pub unsafe extern "C" fn xmlSnprintfElementContent(
2771    buf: *mut c_char,
2772    size: c_int,
2773    content: *mut _xmlElementContent,
2774    englob: c_int,
2775) {
2776    if content.is_null() || buf.is_null() {
2777        return;
2778    }
2779    unsafe {
2780        let mut len = cstr_len(buf) as i32;
2781        if size - len < 50 {
2782            if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
2783                cstr_cat_lit(buf, b" ...");
2784            }
2785            return;
2786        }
2787        if englob != 0 {
2788            cstr_cat_lit(buf, b"(");
2789        }
2790        match (*content).type_ as u32 {
2791            t if t == XML_ELEMENT_CONTENT_PCDATA as u32 => {
2792                cstr_cat_lit(buf, b"#PCDATA");
2793            }
2794            t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
2795                let mut qname_len = crate::abi::exports_xml2::xmlStrlen((*content).name);
2796                if !(*content).prefix.is_null() {
2797                    qname_len += crate::abi::exports_xml2::xmlStrlen((*content).prefix) + 1;
2798                }
2799                if size - len < qname_len + 10 {
2800                    cstr_cat_lit(buf, b" ...");
2801                    return;
2802                }
2803                if !(*content).prefix.is_null() {
2804                    cstr_cat_xmlstr(buf, (*content).prefix);
2805                    cstr_cat_lit(buf, b":");
2806                }
2807                if !(*content).name.is_null() {
2808                    cstr_cat_xmlstr(buf, (*content).name);
2809                }
2810            }
2811            t if t == XML_ELEMENT_CONTENT_SEQ as u32 => {
2812                if (*(*content).c1).type_ == XML_ELEMENT_CONTENT_OR as c_int
2813                    || (*(*content).c1).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
2814                {
2815                    xmlSnprintfElementContent(buf, size, (*content).c1, 1);
2816                } else {
2817                    xmlSnprintfElementContent(buf, size, (*content).c1, 0);
2818                }
2819                len = cstr_len(buf) as i32;
2820                if size - len < 50 {
2821                    if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
2822                        cstr_cat_lit(buf, b" ...");
2823                    }
2824                    return;
2825                }
2826                cstr_cat_lit(buf, b" , ");
2827                if ((*(*content).c2).type_ == XML_ELEMENT_CONTENT_OR as c_int
2828                    || (*(*content).c2).ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
2829                    && (*(*content).c2).type_ != XML_ELEMENT_CONTENT_ELEMENT as c_int
2830                {
2831                    xmlSnprintfElementContent(buf, size, (*content).c2, 1);
2832                } else {
2833                    xmlSnprintfElementContent(buf, size, (*content).c2, 0);
2834                }
2835            }
2836            t if t == XML_ELEMENT_CONTENT_OR as u32 => {
2837                if (*(*content).c1).type_ == XML_ELEMENT_CONTENT_OR as c_int
2838                    || (*(*content).c1).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
2839                {
2840                    xmlSnprintfElementContent(buf, size, (*content).c1, 1);
2841                } else {
2842                    xmlSnprintfElementContent(buf, size, (*content).c1, 0);
2843                }
2844                len = cstr_len(buf) as i32;
2845                if size - len < 50 {
2846                    if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
2847                        cstr_cat_lit(buf, b" ...");
2848                    }
2849                    return;
2850                }
2851                cstr_cat_lit(buf, b" | ");
2852                if ((*(*content).c2).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
2853                    || (*(*content).c2).ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
2854                    && (*(*content).c2).type_ != XML_ELEMENT_CONTENT_ELEMENT as c_int
2855                {
2856                    xmlSnprintfElementContent(buf, size, (*content).c2, 1);
2857                } else {
2858                    xmlSnprintfElementContent(buf, size, (*content).c2, 0);
2859                }
2860            }
2861            _ => {}
2862        }
2863        if size - cstr_len(buf) as i32 <= 2 {
2864            return;
2865        }
2866        if englob != 0 {
2867            cstr_cat_lit(buf, b")");
2868        }
2869        match (*content).ocur as u32 {
2870            t if t == XML_ELEMENT_CONTENT_OPT as u32 => cstr_cat_lit(buf, b"?"),
2871            t if t == XML_ELEMENT_CONTENT_MULT as u32 => cstr_cat_lit(buf, b"*"),
2872            t if t == XML_ELEMENT_CONTENT_PLUS as u32 => cstr_cat_lit(buf, b"+"),
2873            _ => {}
2874        }
2875    }
2876}
2877
2878// ── deprecated stubs ───────────────────────────────────────────────────────────
2879
2880/// # UPSTREAM-PARITY
2881///
2882/// ```c
2883/// int xmlUpgradeOldNs(xmlDocPtr doc);
2884/// ```
2885///
2886/// Deprecated (legacy.c): modern documents no longer carry old-style
2887/// namespace declarations, so this is a no-op returning 0.
2888#[no_mangle]
2889pub unsafe extern "C" fn xmlUpgradeOldNs(_doc: *mut _xmlDoc) -> c_int {
2890    0
2891}