Skip to main content

libxml_rs/abi/
exports_misc.rs

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