Skip to main content

libxml_rs/abi/
exports_misc.rs

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