Skip to main content

libxml_rs/abi/
exports_misc.rs

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