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 { xmlFreeImpl(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 { xmlFreeImpl(ns_list as *mut c_void) };
729    }
730    if !tmp.is_null() {
731        unsafe { xmlFreeImpl(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 = xmlMallocImpl(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 { xmlFreeImpl(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            xmlFreeImpl((*module).name as *mut c_void);
1368        }
1369        xmlFreeImpl(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                xmlFreeImpl(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                    xmlFreeImpl(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                xmlFreeImpl(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                xmlFreeImpl(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                xmlFreeImpl(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            xmlFreeImpl(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/// `pub(crate)` wrapper of [`parser_input_get_window`] for sibling export
2262/// modules (xmlCtxtGetInputWindow, 11.1-X R-000165 closure).
2263pub(crate) unsafe fn parser_input_get_window_pub(
2264    input: *mut _xmlParserInput,
2265    start_out: *mut *const xmlChar,
2266    size_in_out: *mut c_int,
2267    offset_out: *mut c_int,
2268) {
2269    unsafe { parser_input_get_window(input, start_out, size_in_out, offset_out) };
2270}
2271
2272/// Print the source context around an input position (upstream error.c
2273/// `xmlParserPrintFileContextInternal`).
2274unsafe fn print_file_context(
2275    input: *mut _xmlParserInput,
2276    channel: xmlGenericErrorFunc,
2277    data: *mut c_void,
2278) {
2279    if input.is_null() || unsafe { (*input).cur.is_null() } {
2280        return;
2281    }
2282    let mut n: c_int = 80;
2283    let mut start: *const xmlChar = ptr::null();
2284    let mut col: c_int = 0;
2285    unsafe { parser_input_get_window(input, &mut start, &mut n, &mut col) };
2286    let mut content = [0u8; 81];
2287    if n > 0 && !start.is_null() {
2288        unsafe {
2289            ptr::copy_nonoverlapping(start, content.as_mut_ptr(), n as usize);
2290        }
2291    }
2292    content[n as usize] = 0;
2293    unsafe { chan_emit(channel, data, &content[..n as usize]) };
2294    // Create blank line with problem pointer.
2295    let mut i = 0usize;
2296    while i < col as usize {
2297        if content[i] != b'\t' {
2298            content[i] = b' ';
2299        }
2300        i += 1;
2301    }
2302    content[i] = b'^';
2303    i += 1;
2304    content[i] = 0;
2305    unsafe { chan_emit(channel, data, &content[..i]) };
2306}
2307
2308/// # UPSTREAM-PARITY
2309///
2310/// ```c
2311/// void xmlFormatError(const xmlError *err, xmlGenericErrorFunc channel,
2312///                     void *data);
2313/// ```
2314#[no_mangle]
2315pub unsafe extern "C" fn xmlFormatError(
2316    err: *const _xmlError,
2317    channel: Option<xmlGenericErrorFunc>,
2318    data: *mut c_void,
2319) {
2320    if err.is_null() || channel.is_none() {
2321        return;
2322    }
2323    let channel = channel.unwrap();
2324    unsafe {
2325        let e = &*err;
2326        let message = e.message;
2327        let file = e.file;
2328        let line = e.line;
2329        let code = e.code;
2330        let domain = e.domain;
2331        let level = e.level;
2332        let node = e.node as *mut _xmlNode;
2333
2334        if code == XML_ERR_OK {
2335            return;
2336        }
2337
2338        let mut ctxt: *mut _xmlParserCtxt = ptr::null_mut();
2339        if domain == XML_FROM_PARSER
2340            || domain == XML_FROM_HTML
2341            || domain == XML_FROM_DTD
2342            || domain == XML_FROM_NAMESPACE
2343            || domain == XML_FROM_IO
2344            || domain == XML_FROM_VALID
2345        {
2346            ctxt = e.ctxt as *mut _xmlParserCtxt;
2347        }
2348
2349        let mut name: *const xmlChar = ptr::null();
2350        if !node.is_null()
2351            && (*node).type_ == XML_ELEMENT_NODE as c_int
2352            && domain != XML_FROM_SCHEMASV
2353        {
2354            name = (*node).name;
2355        }
2356
2357        let mut input: *mut _xmlParserInput = ptr::null_mut();
2358        let mut cur: *mut _xmlParserInput = ptr::null_mut();
2359        if !ctxt.is_null() && !(*ctxt).input.is_null() {
2360            input = (*ctxt).input;
2361            if (*input).filename.is_null() && (*ctxt).inputNr > 1 {
2362                cur = input;
2363                input = *(*ctxt).inputTab.add((*ctxt).inputNr as usize - 2);
2364            }
2365            if !(*input).filename.is_null() {
2366                let mut buf = Vec::new();
2367                append_cstr(&mut buf, (*input).filename);
2368                buf.push(b':');
2369                append_int(&mut buf, (*input).line);
2370                buf.extend_from_slice(b": ");
2371                chan_emit(channel, data, &buf);
2372            } else if line != 0 && domain == XML_FROM_PARSER {
2373                let mut buf = b"Entity: line ".to_vec();
2374                append_int(&mut buf, (*input).line);
2375                buf.extend_from_slice(b": ");
2376                chan_emit(channel, data, &buf);
2377            }
2378        } else {
2379            if !file.is_null() {
2380                let mut buf = Vec::new();
2381                append_cstr(&mut buf, file);
2382                buf.push(b':');
2383                append_int(&mut buf, line);
2384                buf.extend_from_slice(b": ");
2385                chan_emit(channel, data, &buf);
2386            } else if line != 0
2387                && (domain == XML_FROM_PARSER
2388                    || domain == XML_FROM_SCHEMASV
2389                    || domain == XML_FROM_SCHEMASP
2390                    || domain == XML_FROM_DTD
2391                    || domain == XML_FROM_RELAXNGP
2392                    || domain == XML_FROM_RELAXNGV)
2393            {
2394                let mut buf = b"Entity: line ".to_vec();
2395                append_int(&mut buf, line);
2396                buf.extend_from_slice(b": ");
2397                chan_emit(channel, data, &buf);
2398            }
2399        }
2400        if !name.is_null() {
2401            let mut buf = b"element ".to_vec();
2402            append_xmlstr(&mut buf, name);
2403            buf.extend_from_slice(b": ");
2404            chan_emit(channel, data, &buf);
2405        }
2406        let domain_prefix: &[u8] = match domain {
2407            XML_FROM_PARSER => b"parser ",
2408            XML_FROM_NAMESPACE => b"namespace ",
2409            XML_FROM_DTD | XML_FROM_VALID => b"validity ",
2410            XML_FROM_HTML => b"HTML parser ",
2411            XML_FROM_MEMORY => b"memory ",
2412            XML_FROM_OUTPUT => b"output ",
2413            XML_FROM_IO => b"I/O ",
2414            XML_FROM_XINCLUDE => b"XInclude ",
2415            XML_FROM_XPATH => b"XPath ",
2416            XML_FROM_XPOINTER => b"parser ",
2417            XML_FROM_REGEXP => b"regexp ",
2418            XML_FROM_MODULE => b"module ",
2419            XML_FROM_SCHEMASV => b"Schemas validity ",
2420            XML_FROM_SCHEMASP => b"Schemas parser ",
2421            XML_FROM_RELAXNGP => b"Relax-NG parser ",
2422            XML_FROM_RELAXNGV => b"Relax-NG validity ",
2423            XML_FROM_CATALOG => b"Catalog ",
2424            XML_FROM_C14N => b"C14N ",
2425            XML_FROM_XSLT => b"XSLT ",
2426            XML_FROM_I18N => b"encoding ",
2427            XML_FROM_SCHEMATRONV => b"schematron ",
2428            XML_FROM_BUFFER => b"internal buffer ",
2429            XML_FROM_URI => b"URI ",
2430            _ => b"",
2431        };
2432        chan_emit(channel, data, domain_prefix);
2433        let level_prefix: &[u8] = if level == XML_ERR_NONE as c_int {
2434            b": "
2435        } else if level == XML_ERR_WARNING as c_int {
2436            b"warning : "
2437        } else if level == XML_ERR_ERROR as c_int || level == XML_ERR_FATAL as c_int {
2438            b"error : "
2439        } else {
2440            b": "
2441        };
2442        chan_emit(channel, data, level_prefix);
2443        if !message.is_null() {
2444            let len = cstr_len(message as *const c_char);
2445            let msg_bytes = core::slice::from_raw_parts(message as *const u8, len);
2446            let mut buf = msg_bytes.to_vec();
2447            if len > 0 && *message.add(len - 1) as u8 != b'\n' {
2448                buf.push(b'\n');
2449            }
2450            chan_emit(channel, data, &buf);
2451        } else {
2452            chan_emit(channel, data, b"No error message provided\n");
2453        }
2454
2455        if !ctxt.is_null() {
2456            if !input.is_null()
2457                && ((*input).buf.is_null() || (*(*input).buf).encoder.is_null())
2458                && code == XML_ERR_INVALID_ENCODING
2459                && (*input).cur < (*input).end
2460            {
2461                let mut buf = b"Bytes:".to_vec();
2462                for i in 0..4 {
2463                    if (*input).cur.add(i) >= (*input).end {
2464                        break;
2465                    }
2466                    let b = *(*input).cur.add(i);
2467                    buf.extend_from_slice(b" 0x");
2468                    buf.push(hex_digit(b >> 4));
2469                    buf.push(hex_digit(b & 0xf));
2470                }
2471                buf.push(b'\n');
2472                chan_emit(channel, data, &buf);
2473            }
2474            print_file_context(input, channel, data);
2475            if !cur.is_null() {
2476                if !(*cur).filename.is_null() {
2477                    let mut buf = Vec::new();
2478                    append_cstr(&mut buf, (*cur).filename);
2479                    buf.extend_from_slice(b": ");
2480                    append_int(&mut buf, (*cur).line);
2481                    buf.extend_from_slice(b": \n");
2482                    chan_emit(channel, data, &buf);
2483                } else if line != 0
2484                    && (domain == XML_FROM_PARSER
2485                        || domain == XML_FROM_SCHEMASV
2486                        || domain == XML_FROM_SCHEMASP
2487                        || domain == XML_FROM_DTD
2488                        || domain == XML_FROM_RELAXNGP
2489                        || domain == XML_FROM_RELAXNGV)
2490                {
2491                    let mut buf = b"Entity: line ".to_vec();
2492                    append_int(&mut buf, (*cur).line);
2493                    buf.extend_from_slice(b": \n");
2494                    chan_emit(channel, data, &buf);
2495                }
2496                print_file_context(cur, channel, data);
2497            }
2498        }
2499        if domain == XML_FROM_XPATH
2500            && !e.str1.is_null()
2501            && e.int1 < 100
2502            && e.int1 < crate::abi::exports_xml2::xmlStrlen(e.str1 as *const xmlChar)
2503        {
2504            let mut buf = Vec::new();
2505            append_cstr(&mut buf, e.str1);
2506            buf.push(b'\n');
2507            chan_emit(channel, data, &buf);
2508            let mut marker = vec![b' '; e.int1 as usize];
2509            marker.push(b'^');
2510            marker.push(b'\n');
2511            chan_emit(channel, data, &marker);
2512        }
2513    }
2514}
2515
2516// ── tree node constructors (tree.c) ───────────────────────────────────────────
2517
2518/// # UPSTREAM-PARITY
2519///
2520/// ```c
2521/// xmlNode *xmlNewCharRef(xmlDoc *doc, const xmlChar *name);
2522/// ```
2523#[no_mangle]
2524pub unsafe extern "C" fn xmlNewCharRef(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlNode {
2525    if name.is_null() {
2526        return ptr::null_mut();
2527    }
2528    crate::abi::exports_tree::xmlNewReference(doc, name)
2529}
2530
2531/// # UPSTREAM-PARITY
2532///
2533/// ```c
2534/// xmlNode *xmlNewDocText(const xmlDoc *doc, const xmlChar *content);
2535/// ```
2536#[no_mangle]
2537pub unsafe extern "C" fn xmlNewDocText(
2538    doc: *const _xmlDoc,
2539    content: *const xmlChar,
2540) -> *mut _xmlNode {
2541    let cur = unsafe { crate::abi::exports_xml2::xmlNewText(content) };
2542    if !cur.is_null() {
2543        unsafe { (*cur).doc = doc as *mut _xmlDoc };
2544    }
2545    cur
2546}
2547
2548/// # UPSTREAM-PARITY
2549///
2550/// ```c
2551/// xmlNode *xmlNewDocTextLen(xmlDoc *doc, const xmlChar *content, int len);
2552/// ```
2553#[no_mangle]
2554pub unsafe extern "C" fn xmlNewDocTextLen(
2555    doc: *mut _xmlDoc,
2556    content: *const xmlChar,
2557    len: c_int,
2558) -> *mut _xmlNode {
2559    let cur = unsafe { crate::abi::exports_tree::xmlNewTextLen(content, len) };
2560    if !cur.is_null() {
2561        unsafe { (*cur).doc = doc };
2562    }
2563    cur
2564}
2565
2566/// # UPSTREAM-PARITY
2567///
2568/// ```c
2569/// xmlNode *xmlNewDocComment(xmlDoc *doc, const xmlChar *content);
2570/// ```
2571#[no_mangle]
2572pub unsafe extern "C" fn xmlNewDocComment(
2573    doc: *mut _xmlDoc,
2574    content: *const xmlChar,
2575) -> *mut _xmlNode {
2576    let cur = unsafe { crate::abi::exports_xml2::xmlNewComment(content) };
2577    if !cur.is_null() {
2578        unsafe { (*cur).doc = doc };
2579    }
2580    cur
2581}
2582
2583/// # UPSTREAM-PARITY
2584///
2585/// ```c
2586/// xmlNode *xmlNewDocPI(xmlDoc *doc, const xmlChar *name,
2587///                      const xmlChar *content);
2588/// ```
2589#[no_mangle]
2590pub unsafe extern "C" fn xmlNewDocPI(
2591    doc: *mut _xmlDoc,
2592    name: *const xmlChar,
2593    content: *const xmlChar,
2594) -> *mut _xmlNode {
2595    let cur = unsafe { crate::abi::exports_xml2::xmlNewPI(name, content) };
2596    if !cur.is_null() {
2597        unsafe { (*cur).doc = doc };
2598    }
2599    cur
2600}
2601
2602/// Split a QName, returning a pointer to the local part and storing the
2603/// prefix length (upstream tree.c `xmlSplitQName3`).
2604unsafe fn qname_split3(name: *const xmlChar, len: *mut c_int) -> *const xmlChar {
2605    if name.is_null() || len.is_null() {
2606        return ptr::null();
2607    }
2608    unsafe {
2609        if *name == b':' as xmlChar {
2610            return ptr::null();
2611        }
2612        let mut l = 0usize;
2613        while *name.add(l) != 0 && *name.add(l) != b':' as xmlChar {
2614            l += 1;
2615        }
2616        if *name.add(l) == 0 {
2617            return ptr::null();
2618        }
2619        *len = l as c_int;
2620        name.add(l + 1)
2621    }
2622}
2623
2624/// # UPSTREAM-PARITY
2625///
2626/// ```c
2627/// xmlElementContent *xmlNewDocElementContent(xmlDoc *doc,
2628///                                            const xmlChar *name,
2629///                                            xmlElementContentType type);
2630/// ```
2631#[no_mangle]
2632pub unsafe extern "C" fn xmlNewDocElementContent(
2633    doc: *mut _xmlDoc,
2634    name: *const xmlChar,
2635    type_: xmlElementContentType,
2636) -> *mut _xmlElementContent {
2637    let _ = doc;
2638    unsafe {
2639        let ret = xmlMallocZero(size_of::<_xmlElementContent>()) as *mut _xmlElementContent;
2640        if ret.is_null() {
2641            return ptr::null_mut();
2642        }
2643        (*ret).type_ = type_ as c_int;
2644        (*ret).ocur = XML_ELEMENT_CONTENT_ONCE as c_int;
2645        if !name.is_null() {
2646            let mut l: c_int = 0;
2647            let tmp = qname_split3(name, &mut l);
2648            if tmp.is_null() {
2649                (*ret).name = crate::abi::exports_xml2::xmlStrdup(name);
2650            } else {
2651                (*ret).prefix = crate::abi::exports_xml2::xmlStrndup(name, l);
2652                (*ret).name = crate::abi::exports_xml2::xmlStrdup(tmp);
2653                if (*ret).prefix.is_null() {
2654                    crate::xml::dtd::free_content_model(ret);
2655                    return ptr::null_mut();
2656                }
2657            }
2658            if (*ret).name.is_null() {
2659                crate::xml::dtd::free_content_model(ret);
2660                return ptr::null_mut();
2661            }
2662        }
2663        ret
2664    }
2665}
2666
2667// ── node content / attribute value (tree.c) ───────────────────────────────────
2668
2669/// # UPSTREAM-PARITY
2670///
2671/// ```c
2672/// int xmlNodeBufGetContent(xmlBuffer *buffer, const xmlNode *cur);
2673/// ```
2674#[no_mangle]
2675pub unsafe extern "C" fn xmlNodeBufGetContent(
2676    buffer: *mut _xmlBuffer,
2677    cur: *const _xmlNode,
2678) -> c_int {
2679    if cur.is_null() || buffer.is_null() {
2680        return -1;
2681    }
2682    let content = unsafe { crate::xml::tree::node_get_content(cur as *mut _xmlNode) };
2683    if content.is_null() {
2684        return 0;
2685    }
2686    let len = unsafe { crate::abi::exports_xml2::xmlStrlen(content) };
2687    let ret = crate::xml::io::buf_add(buffer, content, len);
2688    unsafe { xmlFreeImpl(content as *mut c_void) };
2689    if ret < 0 {
2690        -1
2691    } else {
2692        0
2693    }
2694}
2695
2696/// Find an attribute node by name and namespace (upstream tree.c
2697/// `xmlGetPropNodeInternal` with useDTD == 0).
2698unsafe fn find_prop_ns(
2699    node: *const _xmlNode,
2700    name: *const xmlChar,
2701    ns_uri: *const xmlChar,
2702) -> *mut _xmlAttr {
2703    if node.is_null() || unsafe { (*node).type_ != XML_ELEMENT_NODE as c_int } || name.is_null() {
2704        return ptr::null_mut();
2705    }
2706    let mut prop = unsafe { (*node).properties };
2707    while !prop.is_null() {
2708        let ns = unsafe { (*prop).ns };
2709        let ns_match = if ns_uri.is_null() {
2710            ns.is_null()
2711        } else {
2712            !ns.is_null()
2713                && !unsafe { (*ns).href }.is_null()
2714                && unsafe { crate::abi::exports_xml2::xmlStrEqual((*ns).href, ns_uri) != 0 }
2715        };
2716        if ns_match && unsafe { crate::abi::exports_xml2::xmlStrEqual((*prop).name, name) != 0 } {
2717            return prop;
2718        }
2719        prop = unsafe { (*prop).next };
2720    }
2721    ptr::null_mut()
2722}
2723
2724/// # UPSTREAM-PARITY
2725///
2726/// ```c
2727/// int xmlNodeGetAttrValue(const xmlNode *node, const xmlChar *name,
2728///                         const xmlChar *nsUri, xmlChar **out);
2729/// ```
2730///
2731/// Returns 0 with `*out` set to the attribute value, 1 when the attribute
2732/// is missing (or arguments are invalid), -1 on allocation failure.
2733#[no_mangle]
2734pub unsafe extern "C" fn xmlNodeGetAttrValue(
2735    node: *const _xmlNode,
2736    name: *const xmlChar,
2737    ns_uri: *const xmlChar,
2738    out: *mut *mut xmlChar,
2739) -> c_int {
2740    if out.is_null() {
2741        return 1;
2742    }
2743    unsafe { *out = ptr::null_mut() };
2744    let prop = unsafe { find_prop_ns(node, name, ns_uri) };
2745    if prop.is_null() {
2746        return 1;
2747    }
2748    let value = unsafe { crate::xml::tree::node_get_content(prop as *mut _xmlNode) };
2749    if value.is_null() {
2750        return -1;
2751    }
2752    unsafe { *out = value };
2753    0
2754}
2755
2756// ── element content serialization (valid.c) ───────────────────────────────────
2757
2758/// # UPSTREAM-PARITY
2759///
2760/// ```c
2761/// void xmlSprintfElementContent(char *buf, xmlElementContent *content,
2762///                               int englob);
2763/// ```
2764///
2765/// Deprecated; upstream (2.13+) ships this as an empty stub.
2766#[no_mangle]
2767pub unsafe extern "C" fn xmlSprintfElementContent(
2768    _buf: *mut c_char,
2769    _content: *mut _xmlElementContent,
2770    _englob: c_int,
2771) {
2772}
2773
2774/// # UPSTREAM-PARITY
2775///
2776/// ```c
2777/// void xmlSnprintfElementContent(char *buf, int size,
2778///                                xmlElementContent *content, int englob);
2779/// ```
2780#[no_mangle]
2781pub unsafe extern "C" fn xmlSnprintfElementContent(
2782    buf: *mut c_char,
2783    size: c_int,
2784    content: *mut _xmlElementContent,
2785    englob: c_int,
2786) {
2787    if content.is_null() || buf.is_null() {
2788        return;
2789    }
2790    unsafe {
2791        let mut len = cstr_len(buf) as i32;
2792        if size - len < 50 {
2793            if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
2794                cstr_cat_lit(buf, b" ...");
2795            }
2796            return;
2797        }
2798        if englob != 0 {
2799            cstr_cat_lit(buf, b"(");
2800        }
2801        match (*content).type_ as u32 {
2802            t if t == XML_ELEMENT_CONTENT_PCDATA as u32 => {
2803                cstr_cat_lit(buf, b"#PCDATA");
2804            }
2805            t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
2806                let mut qname_len = crate::abi::exports_xml2::xmlStrlen((*content).name);
2807                if !(*content).prefix.is_null() {
2808                    qname_len += crate::abi::exports_xml2::xmlStrlen((*content).prefix) + 1;
2809                }
2810                if size - len < qname_len + 10 {
2811                    cstr_cat_lit(buf, b" ...");
2812                    return;
2813                }
2814                if !(*content).prefix.is_null() {
2815                    cstr_cat_xmlstr(buf, (*content).prefix);
2816                    cstr_cat_lit(buf, b":");
2817                }
2818                if !(*content).name.is_null() {
2819                    cstr_cat_xmlstr(buf, (*content).name);
2820                }
2821            }
2822            t if t == XML_ELEMENT_CONTENT_SEQ as u32 => {
2823                if (*(*content).c1).type_ == XML_ELEMENT_CONTENT_OR as c_int
2824                    || (*(*content).c1).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
2825                {
2826                    xmlSnprintfElementContent(buf, size, (*content).c1, 1);
2827                } else {
2828                    xmlSnprintfElementContent(buf, size, (*content).c1, 0);
2829                }
2830                len = cstr_len(buf) as i32;
2831                if size - len < 50 {
2832                    if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
2833                        cstr_cat_lit(buf, b" ...");
2834                    }
2835                    return;
2836                }
2837                cstr_cat_lit(buf, b" , ");
2838                if ((*(*content).c2).type_ == XML_ELEMENT_CONTENT_OR as c_int
2839                    || (*(*content).c2).ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
2840                    && (*(*content).c2).type_ != XML_ELEMENT_CONTENT_ELEMENT as c_int
2841                {
2842                    xmlSnprintfElementContent(buf, size, (*content).c2, 1);
2843                } else {
2844                    xmlSnprintfElementContent(buf, size, (*content).c2, 0);
2845                }
2846            }
2847            t if t == XML_ELEMENT_CONTENT_OR as u32 => {
2848                if (*(*content).c1).type_ == XML_ELEMENT_CONTENT_OR as c_int
2849                    || (*(*content).c1).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
2850                {
2851                    xmlSnprintfElementContent(buf, size, (*content).c1, 1);
2852                } else {
2853                    xmlSnprintfElementContent(buf, size, (*content).c1, 0);
2854                }
2855                len = cstr_len(buf) as i32;
2856                if size - len < 50 {
2857                    if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
2858                        cstr_cat_lit(buf, b" ...");
2859                    }
2860                    return;
2861                }
2862                cstr_cat_lit(buf, b" | ");
2863                if ((*(*content).c2).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
2864                    || (*(*content).c2).ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
2865                    && (*(*content).c2).type_ != XML_ELEMENT_CONTENT_ELEMENT as c_int
2866                {
2867                    xmlSnprintfElementContent(buf, size, (*content).c2, 1);
2868                } else {
2869                    xmlSnprintfElementContent(buf, size, (*content).c2, 0);
2870                }
2871            }
2872            _ => {}
2873        }
2874        if size - cstr_len(buf) as i32 <= 2 {
2875            return;
2876        }
2877        if englob != 0 {
2878            cstr_cat_lit(buf, b")");
2879        }
2880        match (*content).ocur as u32 {
2881            t if t == XML_ELEMENT_CONTENT_OPT as u32 => cstr_cat_lit(buf, b"?"),
2882            t if t == XML_ELEMENT_CONTENT_MULT as u32 => cstr_cat_lit(buf, b"*"),
2883            t if t == XML_ELEMENT_CONTENT_PLUS as u32 => cstr_cat_lit(buf, b"+"),
2884            _ => {}
2885        }
2886    }
2887}
2888
2889// ── deprecated stubs ───────────────────────────────────────────────────────────
2890
2891/// # UPSTREAM-PARITY
2892///
2893/// ```c
2894/// int xmlUpgradeOldNs(xmlDocPtr doc);
2895/// ```
2896///
2897/// Deprecated (legacy.c): modern documents no longer carry old-style
2898/// namespace declarations, so this is a no-op returning 0.
2899#[no_mangle]
2900pub unsafe extern "C" fn xmlUpgradeOldNs(_doc: *mut _xmlDoc) -> c_int {
2901    0
2902}