//! exports_misc — family closure (11.1-I).
//!
//! C ABI exports for the "miscellaneous" families:
//!
//! 1. getset — xmlGet*/xmlSet* legacy accessors (features, threads,
//! globals, tree navigation, entities, buffers)
//! 2. module — xmlModule* dynamic-loading API (dlopen/dlsym/dlclose)
//! 3. ucs — legacy `xmlUCSIsBlock`/`xmlUCSIsCat` name-table lookups
//! plus the `xmlUCSIsCatCc` control-character test
//! 4. valid — validation helpers (attribute-value normalization,
//! potential-children / valid-elements enumeration)
//! 5. misc2 — `__xml*` aliases, parser-context error helpers,
//! xmlFormatError, tree node constructors, content-model
//! serialization, deprecated stubs
//!
//! Every function here mirrors an exported symbol of the oracle DSO
//! (`nm -D /usr/lib/libxml2.so.2`); signatures follow the installed
//! headers (`/usr/include/libxml2/libxml/*.h`) and the archaeology tree
//! (`archaeology/libxml2-git/*.c`).
//!
//! # Upstream contract
//!
//! Parity target is the oracle DSO (`nm -D /usr/lib/libxml2.so.2`): every
//! symbol here mirrors an exported libxml2 2.15.3 symbol from `parser.c`,
//! `tree.c`, `uri.c`, `xmlstring.c` and the module loader (`xmlmodule.c`),
//! with signatures per the installed headers. R-000165 (11.1-O) closed the
//! misc-family export gaps.
//!
//! # Conceptual behavior
//!
//! This module implements the miscellaneous families: the `xmlGet*/xmlSet*`
//! legacy accessors (features, threads, globals, tree navigation, buffer
//! scheme), the `xmlModule*` dynamic-loading API, the legacy `xmlUCSIsBlock`/
//! `xmlUCSIsCat` name-table lookups, validation helpers, `__xml*` aliases,
//! `xmlFormatError` and the deprecated stubs.
//!
//! # Ownership & safety invariants
//!
//! Ownership follows the per-family contract: module handles are caller-owned
//! (`xmlModuleClose`), strings from feature/name lookups are borrowed static
//! tables, and the getset accessors transfer no ownership. The `__xml*`
//! aliases must keep the same free-with contract as their primary names (xml
//! allocator results freed with `xmlFree`).
//!
//! # Historical quirks & epochs
//!
//! The legacy accessors and `__xml*` aliases are remnants of the 2.0-2.4
//! `legacy_parser` epoch (HISTORY.md) that the ABI still exports;
//! `xmlGetFeaturesList`/`xmlGetFeature` are deprecated since the 2.9 era.
//! R-000165 (11.1-O) added the missing misc symbols.
//!
//! # Deliberate oddities
//!
//! The deprecated stubs are deliberate: several misc2 entry points are kept
//! with their upstream empty/trivial bodies (R-000138 documented no-op set)
//! rather than being removed, because removal would break linking for
//! downstream code.
//!
//! # Proving courts
//!
//! The OWNERSHIP, PARSER and TREE-STRUCTURE court families plus the
//! DSO-LOADER and HEADER-COMPILE courts exercise this
//! module; the data-ABI probes require byte-identical output on the exercised
//! paths.
//!
//! # Tempting simplifications that would break parity
//!
//! A tempting simplification is to delete the deprecated accessors and
//! `__xml*` aliases as dead code — they are the observable ABI surface for
//! legacy consumers and the DSO-LOADER court resolves them, so removing them
//! would fail symbol resolution. Another shortcut, reimplementing
//! `xmlGetFeature` as a no-op, would silently change behavior for consumers
//! probing parser capabilities.
#![allow(missing_docs)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(unused_variables)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
// SAFETY-SCOPE: EXPORT-MISC-MECHANICAL-001
// (11.1-Z.3 proof scope, classified-generated) — this module is the
// mechanical extern-"C" export surface: every `unsafe` block in it is
// the documented indirection/registry-access pattern whose validity
// rests on the upstream C contract, and the exported signatures are
// machine-measured by the ABI-FUNCTION-SIGNATURE and DSO-LOADER
// courts and the C-API differential probes. The safety contract of
// each export is stated in its own doc comment; this scope covers the
// mechanical wrappers' unsafe blocks.
use core::ffi::{c_char, c_void};
use core::mem::{size_of, zeroed};
use core::ptr;
use core::sync::atomic::{AtomicI32, AtomicPtr, Ordering};
use std::os::raw::c_int;
use crate::abi::allocator::*;
use crate::abi::callbacks::*;
use crate::abi::structs::*;
use crate::abi::types::xmlAttributeType::*;
use crate::abi::types::xmlBufferAllocationScheme::*;
use crate::abi::types::xmlElementContentOccur::*;
use crate::abi::types::xmlElementContentType::*;
use crate::abi::types::xmlElementType::*;
use crate::abi::types::xmlEntityType::*;
use crate::abi::types::xmlErrorLevel::*;
use crate::abi::types::*;
// ═══════════════════════════════════════════════════════════════════════════════
// Small C-string / xmlChar helpers (local, so this module is self-contained)
// ═══════════════════════════════════════════════════════════════════════════════
/// strlen for a NUL-terminated byte string.
const unsafe fn cstr_len(s: *const c_char) -> usize {
if s.is_null() {
return 0;
}
let mut n = 0usize;
while unsafe { *((s as *const u8).add(n)) } != 0 {
n += 1;
}
n
}
/// Compare a C string against a byte literal (with implicit NUL).
const unsafe fn cstr_eq(s: *const c_char, lit: &[u8]) -> bool {
if s.is_null() {
return false;
}
let b = s as *const u8;
let mut i = 0usize;
while i < lit.len() {
if unsafe { *b.add(i) } != lit[i] {
return false;
}
i += 1;
}
unsafe { *b.add(i) == 0 }
}
/// strcmp ordering between a C string and a byte literal.
const unsafe fn cstr_cmp(s: *const c_char, lit: &[u8]) -> core::cmp::Ordering {
let b = s as *const u8;
let mut i = 0usize;
loop {
let c = if i < lit.len() { lit[i] } else { 0 };
let sc = unsafe { *b.add(i) };
if sc < c {
return core::cmp::Ordering::Less;
}
if sc > c {
return core::cmp::Ordering::Greater;
}
if sc == 0 {
return core::cmp::Ordering::Equal;
}
i += 1;
}
}
/// Append a NUL-terminated C string's bytes to `v`.
unsafe fn append_cstr(v: &mut Vec<u8>, s: *const c_char) {
if s.is_null() {
return;
}
let b = s as *const u8;
let mut i = 0usize;
loop {
let c = unsafe { *b.add(i) };
if c == 0 {
break;
}
v.push(c);
i += 1;
}
}
/// Append an xmlChar (byte) string to `v`.
unsafe fn append_xmlstr(v: &mut Vec<u8>, s: *const xmlChar) {
if s.is_null() {
return;
}
let b = s;
let mut i = 0usize;
loop {
let c = unsafe { *b.add(i) };
if c == 0 {
break;
}
v.push(c);
i += 1;
}
}
/// strcat a byte literal onto a C string buffer.
unsafe fn cstr_cat_lit(buf: *mut c_char, lit: &[u8]) {
if buf.is_null() {
return;
}
let mut i = cstr_len(buf);
let b = buf as *mut u8;
for &c in lit {
unsafe {
*b.add(i) = c;
}
i += 1;
}
unsafe {
*b.add(i) = 0;
}
}
/// strcat an xmlChar string onto a C string buffer.
unsafe fn cstr_cat_xmlstr(buf: *mut c_char, s: *const xmlChar) {
if s.is_null() {
return;
}
let mut i = cstr_len(buf);
let mut j = 0usize;
let b = buf as *mut u8;
let sb = s;
loop {
let c = unsafe { *sb.add(j) };
if c == 0 {
break;
}
unsafe {
*b.add(i) = c;
}
i += 1;
j += 1;
}
unsafe {
*b.add(i) = 0;
}
}
/// Membership test over a merged short+long range list (upstream
/// `xmlCharInRange` semantics; short and long ranges are disjoint so a
/// linear scan over the merged list is equivalent).
fn ucs_in_ranges(code: c_int, ranges: &[(u32, u32)]) -> c_int {
let v = code as u32;
for &(lo, hi) in ranges {
if v >= lo && v <= hi {
return 1;
}
}
0
}
/// Append an integer in decimal to `v`.
fn append_int(v: &mut Vec<u8>, n: i32) {
let mut x = n as i64;
if x < 0 {
v.push(b'-');
x = -x;
}
let mut buf = [0u8; 20];
let mut i = 0usize;
loop {
buf[i] = b'0' + (x % 10) as u8;
i += 1;
x /= 10;
if x == 0 {
break;
}
}
while i > 0 {
i -= 1;
v.push(buf[i]);
}
}
/// Upper-case hex digit for a nibble.
const fn hex_digit(v: u8) -> u8 {
if v < 10 {
b'0' + v
} else {
b'A' + v - 10
}
}
/// Emit `text` (without NUL) through the generic error channel.
unsafe fn chan_emit(channel: xmlGenericErrorFunc, data: *mut c_void, text: &[u8]) {
let mut b = text.to_vec();
b.push(0);
channel(data, b.as_ptr() as *const c_char);
}
// ═══════════════════════════════════════════════════════════════════════════════
// 1. getset family
// ═══════════════════════════════════════════════════════════════════════════════
/// The deprecated global buffer allocation scheme.
///
/// Upstream 2.13+ removed per-buffer allocation schemes; the getter always
/// returns `XML_BUFFER_ALLOC_EXACT` and the setter is a no-op. The candidate
/// keeps a process-global slot (defaulting to `XML_BUFFER_ALLOC_EXACT` = 1)
/// so the get/set pair round-trips while matching the upstream default.
static XML_BUFFER_ALLOC_SCHEME: AtomicI32 = AtomicI32::new(XML_BUFFER_ALLOC_EXACT as c_int);
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlBufferAllocationScheme xmlGetBufferAllocationScheme(void);
/// ```
#[no_mangle]
pub extern "C" fn xmlGetBufferAllocationScheme() -> xmlBufferAllocationScheme {
match XML_BUFFER_ALLOC_SCHEME.load(Ordering::Relaxed) {
v if v == XML_BUFFER_ALLOC_DOUBLEIT as c_int => {
xmlBufferAllocationScheme::XML_BUFFER_ALLOC_DOUBLEIT
}
v if v == XML_BUFFER_ALLOC_EXACT as c_int => {
xmlBufferAllocationScheme::XML_BUFFER_ALLOC_EXACT
}
v if v == XML_BUFFER_ALLOC_IMMUTABLE as c_int => {
xmlBufferAllocationScheme::XML_BUFFER_ALLOC_IMMUTABLE
}
v if v == XML_BUFFER_ALLOC_IO as c_int => xmlBufferAllocationScheme::XML_BUFFER_ALLOC_IO,
v if v == XML_BUFFER_ALLOC_HYBRID as c_int => {
xmlBufferAllocationScheme::XML_BUFFER_ALLOC_HYBRID
}
_ => xmlBufferAllocationScheme::XML_BUFFER_ALLOC_BOUNDED,
}
}
/// # UPSTREAM-PARITY
///
/// ```c
/// void xmlSetBufferAllocationScheme(xmlBufferAllocationScheme scheme);
/// ```
#[no_mangle]
pub extern "C" fn xmlSetBufferAllocationScheme(scheme: xmlBufferAllocationScheme) {
XML_BUFFER_ALLOC_SCHEME.store(scheme as c_int, Ordering::Relaxed);
}
/// The legacy feature-name list (upstream features.c `xmlFeaturesList[]`,
/// 42 entries, as exported by the oracle DSO).
static XML_FEATURES: [&[u8]; 42] = [
b"validate\0",
b"load subset\0",
b"keep blanks\0",
b"disable SAX\0",
b"fetch external entities\0",
b"substitute entities\0",
b"gather line info\0",
b"user data\0",
b"is html\0",
b"is standalone\0",
b"stop parser\0",
b"document\0",
b"is well formed\0",
b"is valid\0",
b"SAX block\0",
b"SAX function internalSubset\0",
b"SAX function isStandalone\0",
b"SAX function hasInternalSubset\0",
b"SAX function hasExternalSubset\0",
b"SAX function resolveEntity\0",
b"SAX function getEntity\0",
b"SAX function entityDecl\0",
b"SAX function notationDecl\0",
b"SAX function attributeDecl\0",
b"SAX function elementDecl\0",
b"SAX function unparsedEntityDecl\0",
b"SAX function setDocumentLocator\0",
b"SAX function startDocument\0",
b"SAX function endDocument\0",
b"SAX function startElement\0",
b"SAX function endElement\0",
b"SAX function reference\0",
b"SAX function characters\0",
b"SAX function ignorableWhitespace\0",
b"SAX function processingInstruction\0",
b"SAX function comment\0",
b"SAX function warning\0",
b"SAX function error\0",
b"SAX function fatalError\0",
b"SAX function getParameterEntity\0",
b"SAX function cdataBlock\0",
b"SAX function externalSubset\0",
];
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlGetFeaturesList(int *len, const char **result);
/// ```
///
/// Legacy API (features.c, removed from source but still exported). Copies
/// up to `*len` feature names into `result` and returns the total number of
/// features (42). Returns -1 when `*len` is larger than 999.
#[no_mangle]
pub unsafe extern "C" fn xmlGetFeaturesList(len: *mut c_int, result: *mut *const c_char) -> c_int {
let n = XML_FEATURES.len() as c_int;
if len.is_null() || result.is_null() {
return n;
}
let cur = unsafe { *len };
if cur > 999 {
return -1;
}
let mut cnt = cur;
if cnt > n {
cnt = n;
unsafe {
*len = n;
}
}
if cnt > 0 {
for (i, f) in XML_FEATURES.iter().take(cnt as usize).enumerate() {
unsafe {
*result.add(i) = f.as_ptr() as *const c_char;
}
}
}
n
}
/// Byte offset of each SAX function slot inside `_xmlSAXHandler`
/// (upstream features.c reads `ctxt->sax-><slot>`; the candidate struct
/// is `#[repr(C)]` with the same field order, so the offsets match).
const SAX_FN_OFFSETS: [(&[u8], usize); 27] = [
(b"SAX function internalSubset", 0x00),
(b"SAX function isStandalone", 0x08),
(b"SAX function hasInternalSubset", 0x10),
(b"SAX function hasExternalSubset", 0x18),
(b"SAX function resolveEntity", 0x20),
(b"SAX function getEntity", 0x28),
(b"SAX function entityDecl", 0x30),
(b"SAX function notationDecl", 0x38),
(b"SAX function attributeDecl", 0x40),
(b"SAX function elementDecl", 0x48),
(b"SAX function unparsedEntityDecl", 0x50),
(b"SAX function setDocumentLocator", 0x58),
(b"SAX function startDocument", 0x60),
(b"SAX function endDocument", 0x68),
(b"SAX function startElement", 0x70),
(b"SAX function endElement", 0x78),
(b"SAX function reference", 0x80),
(b"SAX function characters", 0x88),
(b"SAX function ignorableWhitespace", 0x90),
(b"SAX function processingInstruction", 0x98),
(b"SAX function comment", 0xa0),
(b"SAX function warning", 0xa8),
(b"SAX function error", 0xb0),
(b"SAX function fatalError", 0xb8),
(b"SAX function getParameterEntity", 0xc0),
(b"SAX function cdataBlock", 0xc8),
(b"SAX function externalSubset", 0xd0),
];
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlGetFeature(xmlParserCtxtPtr ctxt, const char *name, void *result);
/// ```
///
/// Legacy feature getter (features.c). Returns 0 and stores the feature
/// value at `result`, or -1 on error / unknown feature.
#[no_mangle]
pub unsafe extern "C" fn xmlGetFeature(
ctxt: *mut _xmlParserCtxt,
name: *const c_char,
result: *mut c_void,
) -> c_int {
if name.is_null() || result.is_null() || ctxt.is_null() {
return -1;
}
let c = unsafe { &*ctxt };
if unsafe { cstr_eq(name, b"validate") } {
unsafe { *(result as *mut c_int) = c.validate };
return 0;
}
if unsafe { cstr_eq(name, b"keep blanks") } {
unsafe { *(result as *mut c_int) = c.keepBlanks };
return 0;
}
if unsafe { cstr_eq(name, b"disable SAX") } {
unsafe { *(result as *mut c_int) = c.disableSAX };
return 0;
}
if unsafe { cstr_eq(name, b"fetch external entities") } {
unsafe { *(result as *mut c_int) = c.loadsubset };
return 0;
}
if unsafe { cstr_eq(name, b"substitute entities") } {
unsafe { *(result as *mut c_int) = c.replaceEntities };
return 0;
}
if unsafe { cstr_eq(name, b"gather line info") } {
unsafe { *(result as *mut c_int) = c.record_info };
return 0;
}
if unsafe { cstr_eq(name, b"user data") } {
unsafe { *(result as *mut *mut c_void) = c.userData };
return 0;
}
if unsafe { cstr_eq(name, b"is html") } {
unsafe { *(result as *mut c_int) = c.html };
return 0;
}
if unsafe { cstr_eq(name, b"is standalone") } {
unsafe { *(result as *mut c_int) = c.standalone };
return 0;
}
if unsafe { cstr_eq(name, b"document") } {
unsafe { *(result as *mut *mut _xmlDoc) = c.myDoc };
return 0;
}
if unsafe { cstr_eq(name, b"is well formed") } {
unsafe { *(result as *mut c_int) = c.wellFormed };
return 0;
}
if unsafe { cstr_eq(name, b"is valid") } {
unsafe { *(result as *mut c_int) = c.valid };
return 0;
}
if unsafe { cstr_eq(name, b"SAX block") } {
unsafe { *(result as *mut *mut _xmlSAXHandler) = c.sax };
return 0;
}
for &(feat, off) in &SAX_FN_OFFSETS {
if unsafe { cstr_eq(name, feat) } {
if c.sax.is_null() {
return -1;
}
let slot = (c.sax as *mut u8).add(off) as *mut *mut c_void;
unsafe { *(result as *mut *mut c_void) = *slot };
return 0;
}
}
-1
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlSetFeature(xmlParserCtxtPtr ctxt, const char *name, void *value);
/// ```
///
/// Legacy feature setter (features.c). Returns 0 on success, -1 on error /
/// unknown feature. Enabling "validate" also wires up the default validity
/// error/warning handlers on the context's validation context.
#[no_mangle]
pub unsafe extern "C" fn xmlSetFeature(
ctxt: *mut _xmlParserCtxt,
name: *const c_char,
value: *mut c_void,
) -> c_int {
if name.is_null() || value.is_null() || ctxt.is_null() {
return -1;
}
let c = unsafe { &mut *ctxt };
if unsafe { cstr_eq(name, b"validate") } {
let val = unsafe { *(value as *const c_int) };
if c.validate == 0 && val != 0 {
if c.vctxt.warning.is_none() {
c.vctxt.warning = Some(crate::xml::errors::XML_PARSER_VALIDITY_WARNING_SAX1);
}
if c.vctxt.error.is_none() {
c.vctxt.error = Some(crate::xml::errors::XML_PARSER_VALIDITY_ERROR_SAX1);
}
c.vctxt.valid = 0;
}
c.validate = val;
return 0;
}
if unsafe { cstr_eq(name, b"keep blanks") } {
c.keepBlanks = unsafe { *(value as *const c_int) };
return 0;
}
if unsafe { cstr_eq(name, b"disable SAX") } {
c.disableSAX = unsafe { *(value as *const c_int) };
return 0;
}
if unsafe { cstr_eq(name, b"fetch external entities") } {
c.loadsubset = unsafe { *(value as *const c_int) };
return 0;
}
if unsafe { cstr_eq(name, b"substitute entities") } {
c.replaceEntities = unsafe { *(value as *const c_int) };
return 0;
}
if unsafe { cstr_eq(name, b"gather line info") } {
c.record_info = unsafe { *(value as *const c_int) };
return 0;
}
if unsafe { cstr_eq(name, b"user data") } {
c.userData = unsafe { *(value as *const *mut c_void) };
return 0;
}
if unsafe { cstr_eq(name, b"is html") } {
c.html = unsafe { *(value as *const c_int) };
return 0;
}
if unsafe { cstr_eq(name, b"is standalone") } {
c.standalone = unsafe { *(value as *const c_int) };
return 0;
}
if unsafe { cstr_eq(name, b"document") } {
c.myDoc = unsafe { *(value as *const *mut _xmlDoc) };
return 0;
}
if unsafe { cstr_eq(name, b"is well formed") } {
c.wellFormed = unsafe { *(value as *const c_int) };
return 0;
}
if unsafe { cstr_eq(name, b"is valid") } {
c.valid = unsafe { *(value as *const c_int) };
return 0;
}
if unsafe { cstr_eq(name, b"SAX block") } {
c.sax = unsafe { *(value as *const *mut _xmlSAXHandler) };
return 0;
}
for &(feat, off) in &SAX_FN_OFFSETS {
if unsafe { cstr_eq(name, feat) } {
if c.sax.is_null() {
return -1;
}
let slot = (c.sax as *mut u8).add(off) as *mut *mut c_void;
unsafe { *slot = *(value as *const *mut c_void) };
return 0;
}
}
-1
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlGlobalStatePtr xmlGetGlobalState(void);
/// ```
///
/// Deprecated (globals.c): returns the global state, which the candidate
/// does not keep — the oracle DSO itself returns NULL.
#[no_mangle]
pub const unsafe extern "C" fn xmlGetGlobalState() -> *mut c_void {
ptr::null_mut()
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlNodePtr xmlGetLastChild(const xmlNode *parent);
/// ```
#[no_mangle]
pub const unsafe extern "C" fn xmlGetLastChild(parent: *const _xmlNode) -> *mut _xmlNode {
if parent.is_null() || unsafe { (*parent).type_ == XML_NAMESPACE_DECL as c_int } {
return ptr::null_mut();
}
unsafe { (*parent).last }
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlChar *xmlGetNoNsProp(const xmlNode *node, const xmlChar *name);
/// ```
///
/// Value of the no-namespace attribute `name` (with the DTD default/fixed
/// declaration fallback), or NULL.
#[no_mangle]
pub unsafe extern "C" fn xmlGetNoNsProp(
node: *const _xmlNode,
name: *const xmlChar,
) -> *mut xmlChar {
if node.is_null() || unsafe { (*node).type_ != XML_ELEMENT_NODE as c_int } || name.is_null() {
return ptr::null_mut();
}
let mut prop = unsafe { (*node).properties };
while !prop.is_null() {
if unsafe { (*prop).ns.is_null() }
&& unsafe { crate::abi::exports_xml2::xmlStrEqual((*prop).name, name) != 0 }
{
return get_prop_value(prop);
}
prop = unsafe { (*prop).next };
}
dtd_default_attr(node, name, ptr::null())
}
/// The DTD default/fixed attribute declaration fallback of upstream
/// `xmlGetPropNodeInternal` (useDTD == 1).
unsafe fn dtd_default_attr(
node: *const _xmlNode,
name: *const xmlChar,
ns_uri: *const xmlChar,
) -> *mut xmlChar {
let doc = unsafe { (*node).doc };
if doc.is_null() || unsafe { (*doc).intSubset.is_null() } {
return ptr::null_mut();
}
// Build the element QName for the DTD lookup.
let mut tmp: *mut xmlChar = ptr::null_mut();
let ns = unsafe { (*node).ns };
let elem_qname: *const xmlChar = if !ns.is_null() && !unsafe { (*ns).prefix.is_null() } {
tmp = unsafe { crate::abi::exports_xml2::xmlStrdup((*ns).prefix) };
if !tmp.is_null() {
let colon = b":\0";
tmp = unsafe {
crate::abi::exports_xml2::xmlStrcat(tmp, colon.as_ptr() as *const xmlChar)
};
}
if !tmp.is_null() {
tmp = unsafe { crate::abi::exports_xml2::xmlStrcat(tmp, (*node).name) };
}
if tmp.is_null() {
return ptr::null_mut();
}
tmp
} else {
unsafe { (*node).name }
};
let mut attr_decl: *mut _xmlAttribute = ptr::null_mut();
let doc = unsafe { &*doc };
let xml_ns = b"http://www.w3.org/XML/1998/namespace\0";
if ns_uri.is_null() {
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.intSubset,
elem_qname,
name,
ptr::null(),
);
if attr_decl.is_null() && !doc.extSubset.is_null() {
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.extSubset,
elem_qname,
name,
ptr::null(),
);
}
} else if unsafe {
crate::abi::exports_xml2::xmlStrEqual(ns_uri, xml_ns.as_ptr() as *const xmlChar) != 0
} {
let xml_prefix = b"xml\0";
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.intSubset,
elem_qname,
name,
xml_prefix.as_ptr() as *const xmlChar,
);
if attr_decl.is_null() && !doc.extSubset.is_null() {
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.extSubset,
elem_qname,
name,
xml_prefix.as_ptr() as *const xmlChar,
);
}
} else {
// The ugly case: search using the prefixes of in-scope ns-decls
// corresponding to ns_uri.
let ns_list = crate::xml::tree::get_ns_list((*node).doc, node as *mut _xmlNode);
if ns_list.is_null() {
if !tmp.is_null() {
unsafe { xmlFreeImpl(tmp as *mut c_void) };
}
return ptr::null_mut();
}
let mut cur = ns_list;
while !unsafe { *cur }.is_null() {
let n = unsafe { *cur };
if !unsafe { (*n).href }.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual((*n).href, ns_uri) != 0 }
{
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.intSubset,
elem_qname,
name,
(*n).prefix,
);
if attr_decl.is_null() && !doc.extSubset.is_null() {
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.extSubset,
elem_qname,
name,
(*n).prefix,
);
}
if !attr_decl.is_null() {
break;
}
}
cur = cur.add(1);
}
unsafe { xmlFreeImpl(ns_list as *mut c_void) };
}
if !tmp.is_null() {
unsafe { xmlFreeImpl(tmp as *mut c_void) };
}
if !attr_decl.is_null() && !unsafe { (*attr_decl).defaultValue.is_null() } {
return unsafe { crate::abi::exports_xml2::xmlStrdup((*attr_decl).defaultValue) };
}
ptr::null_mut()
}
/// Value of an attribute node (upstream `xmlGetPropNodeValueInternal`):
/// content of the attribute's children for attribute nodes, the default
/// value for attribute declarations.
unsafe fn get_prop_value(prop: *mut _xmlAttr) -> *mut xmlChar {
if prop.is_null() {
return ptr::null_mut();
}
if unsafe { (*prop).type_ == XML_ATTRIBUTE_NODE as c_int } {
unsafe { crate::xml::tree::node_get_content(prop as *mut _xmlNode) }
} else if unsafe { (*prop).type_ == XML_ATTRIBUTE_DECL as c_int } {
let a = prop as *mut _xmlAttribute;
unsafe { crate::abi::exports_xml2::xmlStrdup((*a).defaultValue) }
} else {
ptr::null_mut()
}
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlChar *xmlGetNodePath(const xmlNode *node);
/// ```
///
/// Build an XPath-like path for `node` (tree.c `xmlGetNodePath`), or NULL
/// on error. The result is allocated with xmlMalloc.
#[no_mangle]
pub unsafe extern "C" fn xmlGetNodePath(node: *const _xmlNode) -> *mut xmlChar {
if node.is_null() || unsafe { (*node).type_ == XML_NAMESPACE_DECL as c_int } {
return ptr::null_mut();
}
unsafe {
// Collect the ancestor chain (node, parent, ..., root).
let mut num_nodes = 0usize;
let mut cur: *const _xmlNode = node;
while !cur.is_null() {
num_nodes += 1;
cur = (*cur).parent;
}
let mut nodes: Vec<*const _xmlNode> = Vec::with_capacity(num_nodes);
cur = node;
while !cur.is_null() && nodes.len() < num_nodes {
nodes.push(cur);
cur = (*cur).parent;
}
let mut out: Vec<u8> = Vec::new();
let mut i = nodes.len();
while i > 0 {
let mut occur: i32 = 0;
i -= 1;
let cur = nodes[i];
let t = (*cur).type_;
if t == XML_DOCUMENT_NODE as c_int || t == XML_HTML_DOCUMENT_NODE as c_int {
if i == 0 {
out.push(b'/');
}
} else if t == XML_ELEMENT_NODE as c_int {
let mut generic = 0;
out.push(b'/');
let ns = (*cur).ns;
if !ns.is_null() {
if !(*ns).prefix.is_null() {
append_xmlstr(&mut out, (*ns).prefix);
out.push(b':');
append_xmlstr(&mut out, (*cur).name);
} else {
// Cannot express named elements in the default
// namespace, so use "*".
generic = 1;
out.push(b'*');
}
} else {
append_xmlstr(&mut out, (*cur).name);
}
// Thumbler index computation.
let mut tmp = (*cur).prev;
while !tmp.is_null() {
if (*tmp).type_ == XML_ELEMENT_NODE as c_int
&& (generic != 0
|| (crate::abi::exports_xml2::xmlStrEqual((*cur).name, (*tmp).name)
!= 0
&& ((*tmp).ns == ns
|| (!(*tmp).ns.is_null()
&& !ns.is_null()
&& crate::abi::exports_xml2::xmlStrEqual(
(*ns).prefix,
(*(*tmp).ns).prefix,
) != 0))))
{
occur += 1;
}
tmp = (*tmp).prev;
}
if occur == 0 {
tmp = (*cur).next;
while !tmp.is_null() && occur == 0 {
if (*tmp).type_ == XML_ELEMENT_NODE as c_int
&& (generic != 0
|| (crate::abi::exports_xml2::xmlStrEqual(
(*cur).name,
(*tmp).name,
) != 0
&& ((*tmp).ns == ns
|| (!(*tmp).ns.is_null()
&& !ns.is_null()
&& crate::abi::exports_xml2::xmlStrEqual(
(*ns).prefix,
(*(*tmp).ns).prefix,
) != 0))))
{
occur += 1;
}
tmp = (*tmp).next;
}
if occur != 0 {
occur = 1;
}
} else {
occur += 1;
}
} else if t == XML_COMMENT_NODE as c_int {
out.extend_from_slice(b"/comment()");
let mut tmp = (*cur).prev;
while !tmp.is_null() {
if (*tmp).type_ == XML_COMMENT_NODE as c_int {
occur += 1;
}
tmp = (*tmp).prev;
}
if occur == 0 {
tmp = (*cur).next;
while !tmp.is_null() && occur == 0 {
if (*tmp).type_ == XML_COMMENT_NODE as c_int {
occur += 1;
}
tmp = (*tmp).next;
}
if occur != 0 {
occur = 1;
}
} else {
occur += 1;
}
} else if t == XML_TEXT_NODE as c_int || t == XML_CDATA_SECTION_NODE as c_int {
out.extend_from_slice(b"/text()");
let mut tmp = (*cur).prev;
while !tmp.is_null() {
if (*tmp).type_ == XML_TEXT_NODE as c_int
|| (*tmp).type_ == XML_CDATA_SECTION_NODE as c_int
{
occur += 1;
}
tmp = (*tmp).prev;
}
if occur == 0 {
tmp = (*cur).next;
while !tmp.is_null() {
if (*tmp).type_ == XML_TEXT_NODE as c_int
|| (*tmp).type_ == XML_CDATA_SECTION_NODE as c_int
{
occur = 1;
break;
}
tmp = (*tmp).next;
}
} else {
occur += 1;
}
} else if t == XML_PI_NODE as c_int {
out.extend_from_slice(b"/processing-instruction('");
append_xmlstr(&mut out, (*cur).name);
out.extend_from_slice(b"')");
let mut tmp = (*cur).prev;
while !tmp.is_null() {
if (*tmp).type_ == XML_PI_NODE as c_int
&& crate::abi::exports_xml2::xmlStrEqual((*cur).name, (*tmp).name) != 0
{
occur += 1;
}
tmp = (*tmp).prev;
}
if occur == 0 {
tmp = (*cur).next;
while !tmp.is_null() && occur == 0 {
if (*tmp).type_ == XML_PI_NODE as c_int
&& crate::abi::exports_xml2::xmlStrEqual((*cur).name, (*tmp).name) != 0
{
occur += 1;
}
tmp = (*tmp).next;
}
if occur != 0 {
occur = 1;
}
} else {
occur += 1;
}
} else if t == XML_ATTRIBUTE_NODE as c_int {
out.extend_from_slice(b"/@");
let ns = (*cur).ns;
if !ns.is_null() && !(*ns).prefix.is_null() {
append_xmlstr(&mut out, (*ns).prefix);
out.push(b':');
}
append_xmlstr(&mut out, (*cur).name);
} else {
return ptr::null_mut();
}
if occur > 0 {
out.push(b'[');
append_int(&mut out, occur);
out.push(b']');
}
}
out.push(0);
let ret = xmlMallocImpl(out.len());
if ret.is_null() {
return ptr::null_mut();
}
ptr::copy_nonoverlapping(out.as_ptr(), ret as *mut u8, out.len());
ret as *mut xmlChar
}
}
/// The five XML predefined entities, mirroring upstream entities.c static
/// `xmlEntityLt`/`xmlEntityGt`/`xmlEntityAmp`/`xmlEntityQuot`/`xmlEntityApos`.
///
/// These are WRITABLE (upstream declares them as non-const globals): PHP's
/// libxml wrapper stores its `php_libxml_node_ptr` in `node->_private` when a
/// DOM wrapper is created around an entity declaration (a doc-less
/// `new DOMEntityReference("amp")` binds the predefined declaration as its
/// child, and property reads wrap it). A plain `static` here would land in
/// `.data.rel.ro`, which full RELRO mprotects read-only — PHP's `_private`
/// store then faults. `static mut` keeps them in the writable `.data`.
static mut PREDEFINED_LT_DATA: _xmlEntity = _xmlEntity {
_private: ptr::null_mut(),
type_: XML_ENTITY_DECL as c_int,
name: b"lt\0" as *const u8 as *const xmlChar,
children: ptr::null_mut(),
last: ptr::null_mut(),
parent: ptr::null_mut(),
next: ptr::null_mut(),
prev: ptr::null_mut(),
doc: ptr::null_mut(),
orig: ptr::null_mut(),
content: b"<\0" as *const u8 as *mut xmlChar,
length: 1,
etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
ExternalID: ptr::null(),
SystemID: ptr::null(),
nexte: ptr::null_mut(),
URI: ptr::null(),
owner: 0,
flags: 0,
expandedSize: 0,
};
static mut PREDEFINED_GT_DATA: _xmlEntity = _xmlEntity {
_private: ptr::null_mut(),
type_: XML_ENTITY_DECL as c_int,
name: b"gt\0" as *const u8 as *const xmlChar,
children: ptr::null_mut(),
last: ptr::null_mut(),
parent: ptr::null_mut(),
next: ptr::null_mut(),
prev: ptr::null_mut(),
doc: ptr::null_mut(),
orig: ptr::null_mut(),
content: b">\0" as *const u8 as *mut xmlChar,
length: 1,
etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
ExternalID: ptr::null(),
SystemID: ptr::null(),
nexte: ptr::null_mut(),
URI: ptr::null(),
owner: 0,
flags: 0,
expandedSize: 0,
};
static mut PREDEFINED_AMP_DATA: _xmlEntity = _xmlEntity {
_private: ptr::null_mut(),
type_: XML_ENTITY_DECL as c_int,
name: b"amp\0" as *const u8 as *const xmlChar,
children: ptr::null_mut(),
last: ptr::null_mut(),
parent: ptr::null_mut(),
next: ptr::null_mut(),
prev: ptr::null_mut(),
doc: ptr::null_mut(),
orig: ptr::null_mut(),
content: b"&\0" as *const u8 as *mut xmlChar,
length: 1,
etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
ExternalID: ptr::null(),
SystemID: ptr::null(),
nexte: ptr::null_mut(),
URI: ptr::null(),
owner: 0,
flags: 0,
expandedSize: 0,
};
static mut PREDEFINED_QUOT_DATA: _xmlEntity = _xmlEntity {
_private: ptr::null_mut(),
type_: XML_ENTITY_DECL as c_int,
name: b"quot\0" as *const u8 as *const xmlChar,
children: ptr::null_mut(),
last: ptr::null_mut(),
parent: ptr::null_mut(),
next: ptr::null_mut(),
prev: ptr::null_mut(),
doc: ptr::null_mut(),
orig: ptr::null_mut(),
content: b"\"\0" as *const u8 as *mut xmlChar,
length: 1,
etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
ExternalID: ptr::null(),
SystemID: ptr::null(),
nexte: ptr::null_mut(),
URI: ptr::null(),
owner: 0,
flags: 0,
expandedSize: 0,
};
static mut PREDEFINED_APOS_DATA: _xmlEntity = _xmlEntity {
_private: ptr::null_mut(),
type_: XML_ENTITY_DECL as c_int,
name: b"apos\0" as *const u8 as *const xmlChar,
children: ptr::null_mut(),
last: ptr::null_mut(),
parent: ptr::null_mut(),
next: ptr::null_mut(),
prev: ptr::null_mut(),
doc: ptr::null_mut(),
orig: ptr::null_mut(),
content: b"'\0" as *const u8 as *mut xmlChar,
length: 1,
etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
ExternalID: ptr::null(),
SystemID: ptr::null(),
nexte: ptr::null_mut(),
URI: ptr::null(),
owner: 0,
flags: 0,
expandedSize: 0,
};
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlEntityPtr xmlGetPredefinedEntity(const xmlChar *name);
/// ```
///
/// Returns a pointer to the static predefined entity, or NULL.
#[no_mangle]
pub unsafe extern "C" fn xmlGetPredefinedEntity(name: *const xmlChar) -> *mut _xmlEntity {
if name.is_null() {
return ptr::null_mut();
}
unsafe {
if crate::abi::exports_xml2::xmlStrEqual(name, b"lt\0" as *const u8 as *const xmlChar) != 0
{
return std::ptr::addr_of_mut!(PREDEFINED_LT_DATA);
}
if crate::abi::exports_xml2::xmlStrEqual(name, b"gt\0" as *const u8 as *const xmlChar) != 0
{
return std::ptr::addr_of_mut!(PREDEFINED_GT_DATA);
}
if crate::abi::exports_xml2::xmlStrEqual(name, b"amp\0" as *const u8 as *const xmlChar) != 0
{
return std::ptr::addr_of_mut!(PREDEFINED_AMP_DATA);
}
if crate::abi::exports_xml2::xmlStrEqual(name, b"quot\0" as *const u8 as *const xmlChar)
!= 0
{
return std::ptr::addr_of_mut!(PREDEFINED_QUOT_DATA);
}
if crate::abi::exports_xml2::xmlStrEqual(name, b"apos\0" as *const u8 as *const xmlChar)
!= 0
{
return std::ptr::addr_of_mut!(PREDEFINED_APOS_DATA);
}
}
ptr::null_mut()
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlGetThreadId(void);
/// ```
///
/// Upstream returns `pthread_self()` (or 0 when single-threaded); the
/// candidate is single-threaded, so 0 matches the oracle's common path.
#[no_mangle]
pub const extern "C" fn xmlGetThreadId() -> c_int {
0
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlGetUTF8Char(const unsigned char *utf, int *len);
/// ```
///
/// Decode the UTF-8 character at `utf`, set `*len` to its byte length and
/// return the code point; -1 (and `*len = 0`) on error.
#[no_mangle]
pub unsafe extern "C" fn xmlGetUTF8Char(utf: *const u8, len: *mut c_int) -> c_int {
unsafe {
if utf.is_null() || len.is_null() {
if !len.is_null() {
*len = 0;
}
return -1;
}
let mut c: u32 = *utf as u32;
if c < 0x80 {
if *len < 1 {
*len = 0;
return -1;
}
*len = 1;
} else {
if *len < 2 || (*utf.add(1) & 0xc0) != 0x80 {
*len = 0;
return -1;
}
if c < 0xe0 {
if c < 0xc2 {
*len = 0;
return -1;
}
*len = 2;
c = (c & 0x1f) << 6;
c |= (*utf.add(1) & 0x3f) as u32;
} else {
if *len < 3 || (*utf.add(2) & 0xc0) != 0x80 {
*len = 0;
return -1;
}
if c < 0xf0 {
*len = 3;
c = (c & 0x0f) << 12;
c |= ((*utf.add(1) & 0x3f) as u32) << 6;
c |= (*utf.add(2) & 0x3f) as u32;
if c < 0x800 || (0xd800..0xe000).contains(&c) {
*len = 0;
return -1;
}
} else {
if *len < 4 || (*utf.add(3) & 0xc0) != 0x80 {
*len = 0;
return -1;
}
*len = 4;
c = (c & 0x07) << 18;
c |= ((*utf.add(1) & 0x3f) as u32) << 12;
c |= ((*utf.add(2) & 0x3f) as u32) << 6;
c |= (*utf.add(3) & 0x3f) as u32;
if !(0x10000..0x110000).contains(&c) {
*len = 0;
return -1;
}
}
}
}
c as c_int
}
}
/// Type of the entity-reference callback (upstream entities.h
/// `xmlEntityReferenceFunc`).
pub type xmlEntityReferenceFunc = unsafe extern "C" fn(
entity: *mut _xmlEntity,
firstChild: *mut _xmlNode,
lastChild: *mut _xmlNode,
);
/// The global entity-reference callback (legacy entities.c global; the
/// candidate's parser does not invoke it, mirroring modern upstream where
/// `xmlSetEntityReferenceFunc` is a no-op).
static ENTITY_REFERENCE_FUNC: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
/// # UPSTREAM-PARITY
///
/// ```c
/// void xmlSetEntityReferenceFunc(xmlEntityReferenceFunc func);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlSetEntityReferenceFunc(func: Option<xmlEntityReferenceFunc>) {
ENTITY_REFERENCE_FUNC.store(
func.map_or(ptr::null_mut(), |f| f as *const c_void as *mut c_void),
Ordering::Relaxed,
);
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlSetListDoc(xmlNode *list, xmlDoc *doc);
/// ```
///
/// Set `doc` on every node of the sibling list (tree.c). Returns 0, or -1
/// if a subtree assignment failed.
#[no_mangle]
pub unsafe extern "C" fn xmlSetListDoc(list: *mut _xmlNode, doc: *mut _xmlDoc) -> c_int {
if list.is_null() || unsafe { (*list).type_ == XML_NAMESPACE_DECL as c_int } {
return 0;
}
let mut ret = 0;
let mut cur = list;
while !cur.is_null() {
if unsafe { (*cur).doc != doc }
&& unsafe { crate::abi::exports_tree::xmlSetTreeDoc(cur, doc) } < 0
{
ret = -1;
}
cur = unsafe { (*cur).next };
}
ret
}
// ═══════════════════════════════════════════════════════════════════════════════
// 2. module family — xmlModule* (xmlmodule.c, dlopen/dlsym/dlclose)
// ═══════════════════════════════════════════════════════════════════════════════
/// Module handle (upstream `struct _xmlModule`, xmlmodule.c).
#[derive(Debug)]
#[repr(C)]
pub struct _xmlModule {
pub name: *mut xmlChar,
pub handle: *mut c_void,
}
extern "C" {
fn dlopen(filename: *const c_char, flags: c_int) -> *mut c_void;
fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
fn dlclose(handle: *mut c_void) -> c_int;
fn dlerror() -> *mut c_char;
}
// RTLD_NOW | RTLD_GLOBAL (glibc; matches upstream xmlModulePlatformOpen).
const RTLD_NOW: c_int = 2;
const RTLD_GLOBAL: c_int = 0x100;
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlModulePtr xmlModuleOpen(const char *filename, int options);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlModuleOpen(
filename: *const c_char,
_options: c_int,
) -> *mut _xmlModule {
let module = unsafe { xmlMallocZero(size_of::<_xmlModule>()) } as *mut _xmlModule;
if module.is_null() {
return ptr::null_mut();
}
let handle = unsafe { dlopen(filename, RTLD_GLOBAL | RTLD_NOW) };
if handle.is_null() {
unsafe { xmlFreeImpl(module as *mut c_void) };
return ptr::null_mut();
}
unsafe {
(*module).handle = handle;
if !filename.is_null() {
(*module).name = crate::abi::exports_xml2::xmlStrdup(filename as *const xmlChar);
}
}
module
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlModuleSymbol(xmlModule *module, const char *name, void **symbol);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlModuleSymbol(
module: *mut _xmlModule,
name: *const c_char,
symbol: *mut *mut c_void,
) -> c_int {
if module.is_null() || symbol.is_null() || name.is_null() {
return -1;
}
unsafe {
*symbol = dlsym((*module).handle, name);
if !dlerror().is_null() {
return -1;
}
}
0
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlModuleClose(xmlModule *module);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlModuleClose(module: *mut _xmlModule) -> c_int {
if module.is_null() {
return -1;
}
let rc = unsafe { dlclose((*module).handle) };
if rc != 0 {
return -2;
}
unsafe { xmlModuleFree(module) }
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlModuleFree(xmlModule *module);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlModuleFree(module: *mut _xmlModule) -> c_int {
if module.is_null() {
return -1;
}
unsafe {
if !(*module).name.is_null() {
xmlFreeImpl((*module).name as *mut c_void);
}
xmlFreeImpl(module as *mut c_void);
}
0
}
// ═══════════════════════════════════════════════════════════════════════════════
// 3. ucs family — xmlUCSIsBlock / xmlUCSIsCat / xmlUCSIsCatCc
// ═══════════════════════════════════════════════════════════════════════════════
// Generated: UCS block/category name tables (archaeology libxml2-git
// codegen/unicode.inc). Each entry: (name, &[(lo, hi)]) sorted by name
// (upstream xmlUnicodeBlocks / xmlUnicodeCats).
#[rustfmt::skip]
static XML_UCS_BLOCKS: &[(&str, &[(u32, u32)])] = &[
("AegeanNumbers", &[(65792,65855)]),
("AlphabeticPresentationForms", &[(64256,64335)]),
("Arabic", &[(1536,1791)]),
("ArabicPresentationForms-A", &[(64336,65023)]),
("ArabicPresentationForms-B", &[(65136,65279)]),
("Armenian", &[(1328,1423)]),
("Arrows", &[(8592,8703)]),
("BasicLatin", &[(0,127)]),
("Bengali", &[(2432,2559)]),
("BlockElements", &[(9600,9631)]),
("Bopomofo", &[(12544,12591)]),
("BopomofoExtended", &[(12704,12735)]),
("BoxDrawing", &[(9472,9599)]),
("BraillePatterns", &[(10240,10495)]),
("Buhid", &[(5952,5983)]),
("ByzantineMusicalSymbols", &[(118784,119039)]),
("CJKCompatibility", &[(13056,13311)]),
("CJKCompatibilityForms", &[(65072,65103)]),
("CJKCompatibilityIdeographs", &[(63744,64255)]),
("CJKCompatibilityIdeographsSupplement", &[(194560,195103)]),
("CJKRadicalsSupplement", &[(11904,12031)]),
("CJKSymbolsandPunctuation", &[(12288,12351)]),
("CJKUnifiedIdeographs", &[(19968,40959)]),
("CJKUnifiedIdeographsExtensionA", &[(13312,19903)]),
("CJKUnifiedIdeographsExtensionB", &[(131072,173791)]),
("Cherokee", &[(5024,5119)]),
("CombiningDiacriticalMarks", &[(768,879)]),
("CombiningDiacriticalMarksforSymbols", &[(8400,8447)]),
("CombiningHalfMarks", &[(65056,65071)]),
("CombiningMarksforSymbols", &[(8400,8447)]),
("ControlPictures", &[(9216,9279)]),
("CurrencySymbols", &[(8352,8399)]),
("CypriotSyllabary", &[(67584,67647)]),
("Cyrillic", &[(1024,1279)]),
("CyrillicSupplement", &[(1280,1327)]),
("Deseret", &[(66560,66639)]),
("Devanagari", &[(2304,2431)]),
("Dingbats", &[(9984,10175)]),
("EnclosedAlphanumerics", &[(9312,9471)]),
("EnclosedCJKLettersandMonths", &[(12800,13055)]),
("Ethiopic", &[(4608,4991)]),
("GeneralPunctuation", &[(8192,8303)]),
("GeometricShapes", &[(9632,9727)]),
("Georgian", &[(4256,4351)]),
("Gothic", &[(66352,66383)]),
("Greek", &[(880,1023)]),
("GreekExtended", &[(7936,8191)]),
("GreekandCoptic", &[(880,1023)]),
("Gujarati", &[(2688,2815)]),
("Gurmukhi", &[(2560,2687)]),
("HalfwidthandFullwidthForms", &[(65280,65519)]),
("HangulCompatibilityJamo", &[(12592,12687)]),
("HangulJamo", &[(4352,4607)]),
("HangulSyllables", &[(44032,55215)]),
("Hanunoo", &[(5920,5951)]),
("Hebrew", &[(1424,1535)]),
("HighPrivateUseSurrogates", &[(56192,56319)]),
("HighSurrogates", &[(55296,56191)]),
("Hiragana", &[(12352,12447)]),
("IPAExtensions", &[(592,687)]),
("IdeographicDescriptionCharacters", &[(12272,12287)]),
("Kanbun", &[(12688,12703)]),
("KangxiRadicals", &[(12032,12255)]),
("Kannada", &[(3200,3327)]),
("Katakana", &[(12448,12543)]),
("KatakanaPhoneticExtensions", &[(12784,12799)]),
("Khmer", &[(6016,6143)]),
("KhmerSymbols", &[(6624,6655)]),
("Lao", &[(3712,3839)]),
("Latin-1Supplement", &[(128,255)]),
("LatinExtended-A", &[(256,383)]),
("LatinExtended-B", &[(384,591)]),
("LatinExtendedAdditional", &[(7680,7935)]),
("LetterlikeSymbols", &[(8448,8527)]),
("Limbu", &[(6400,6479)]),
("LinearBIdeograms", &[(65664,65791)]),
("LinearBSyllabary", &[(65536,65663)]),
("LowSurrogates", &[(56320,57343)]),
("Malayalam", &[(3328,3455)]),
("MathematicalAlphanumericSymbols", &[(119808,120831)]),
("MathematicalOperators", &[(8704,8959)]),
("MiscellaneousMathematicalSymbols-A", &[(10176,10223)]),
("MiscellaneousMathematicalSymbols-B", &[(10624,10751)]),
("MiscellaneousSymbols", &[(9728,9983)]),
("MiscellaneousSymbolsandArrows", &[(11008,11263)]),
("MiscellaneousTechnical", &[(8960,9215)]),
("Mongolian", &[(6144,6319)]),
("MusicalSymbols", &[(119040,119295)]),
("Myanmar", &[(4096,4255)]),
("NumberForms", &[(8528,8591)]),
("Ogham", &[(5760,5791)]),
("OldItalic", &[(66304,66351)]),
("OpticalCharacterRecognition", &[(9280,9311)]),
("Oriya", &[(2816,2943)]),
("Osmanya", &[(66688,66735)]),
("PhoneticExtensions", &[(7424,7551)]),
("PrivateUse", &[(57344,63743),(983040,1048575),(1048576,1114111)]),
("PrivateUseArea", &[(57344,63743)]),
("Runic", &[(5792,5887)]),
("Shavian", &[(66640,66687)]),
("Sinhala", &[(3456,3583)]),
("SmallFormVariants", &[(65104,65135)]),
("SpacingModifierLetters", &[(688,767)]),
("Specials", &[(65520,65535)]),
("SuperscriptsandSubscripts", &[(8304,8351)]),
("SupplementalArrows-A", &[(10224,10239)]),
("SupplementalArrows-B", &[(10496,10623)]),
("SupplementalMathematicalOperators", &[(10752,11007)]),
("SupplementaryPrivateUseArea-A", &[(983040,1048575)]),
("SupplementaryPrivateUseArea-B", &[(1048576,1114111)]),
("Syriac", &[(1792,1871)]),
("Tagalog", &[(5888,5919)]),
("Tagbanwa", &[(5984,6015)]),
("Tags", &[(917504,917631)]),
("TaiLe", &[(6480,6527)]),
("TaiXuanJingSymbols", &[(119552,119647)]),
("Tamil", &[(2944,3071)]),
("Telugu", &[(3072,3199)]),
("Thaana", &[(1920,1983)]),
("Thai", &[(3584,3711)]),
("Tibetan", &[(3840,4095)]),
("Ugaritic", &[(66432,66463)]),
("UnifiedCanadianAboriginalSyllabics", &[(5120,5759)]),
("VariationSelectors", &[(65024,65039)]),
("VariationSelectorsSupplement", &[(917760,917999)]),
("YiRadicals", &[(42128,42191)]),
("YiSyllables", &[(40960,42127)]),
("YijingHexagramSymbols", &[(19904,19967)]),
];
#[rustfmt::skip]
static XML_UCS_CATS: &[(&str, &[(u32, u32)])] = &[
("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)]),
("Cc", &[(0,31),(127,159)]),
("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)]),
("Co", &[(57344,57344),(63743,63743),(983040,983040),(1048573,1048573),(1048576,1048576),(1114109,1114109)]),
("Cs", &[(55296,57343)]),
("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)]),
("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)]),
("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)]),
("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)]),
("Lt", &[(453,453),(456,456),(459,459),(498,498),(8072,8079),(8088,8095),(8104,8111),(8124,8124),(8140,8140),(8188,8188)]),
("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)]),
("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)]),
("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)]),
("Me", &[(1160,1161),(1758,1758),(8413,8416),(8418,8420)]),
("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)]),
("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)]),
("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)]),
("Nl", &[(5870,5872),(8544,8579),(12295,12295),(12321,12329),(12344,12346),(66378,66378)]),
("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)]),
("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)]),
("Pc", &[(95,95),(8255,8256),(8276,8276),(12539,12539),(65075,65076),(65101,65103),(65343,65343),(65381,65381)]),
("Pd", &[(45,45),(1418,1418),(6150,6150),(8208,8213),(12316,12316),(12336,12336),(12448,12448),(65073,65074),(65112,65112),(65123,65123),(65293,65293)]),
("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)]),
("Pf", &[(187,187),(8217,8217),(8221,8221),(8250,8250)]),
("Pi", &[(171,171),(8216,8216),(8219,8220),(8223,8223),(8249,8249)]),
("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)]),
("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)]),
("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)]),
("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)]),
("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)]),
("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)]),
("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)]),
("Z", &[(32,32),(160,160),(5760,5760),(6158,6158),(8192,8202),(8232,8233),(8239,8239),(8287,8287),(12288,12288)]),
("Zl", &[(8232,8232)]),
("Zp", &[(8233,8233)]),
("Zs", &[(32,32),(160,160),(5760,5760),(6158,6158),(8192,8202),(8239,8239),(8287,8287),(12288,12288)]),
];
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlUCSIsBlock(int code, const char *block);
/// ```
///
/// Legacy (xmlregexp.c): 1 if `code` is in the named Unicode block, 0 if
/// not, -1 for an unknown block name.
#[no_mangle]
pub unsafe extern "C" fn xmlUCSIsBlock(code: c_int, block: *const c_char) -> c_int {
if block.is_null() || XML_UCS_BLOCKS.is_empty() {
return -1;
}
let mut low = 0usize;
let mut high = XML_UCS_BLOCKS.len() - 1;
while low <= high {
let mid = (low + high) / 2;
let (name, ranges) = XML_UCS_BLOCKS[mid];
match unsafe { cstr_cmp(block, name.as_bytes()) } {
core::cmp::Ordering::Equal => return ucs_in_ranges(code, ranges),
core::cmp::Ordering::Less => {
if mid == 0 {
break;
}
high = mid - 1;
}
core::cmp::Ordering::Greater => low = mid + 1,
}
}
-1
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlUCSIsCat(int code, const char *cat);
/// ```
///
/// Legacy: 1 if `code` belongs to the named Unicode general category, 0 if
/// not, -1 for an unknown category name.
#[no_mangle]
pub unsafe extern "C" fn xmlUCSIsCat(code: c_int, cat: *const c_char) -> c_int {
if cat.is_null() || XML_UCS_CATS.is_empty() {
return -1;
}
let mut low = 0usize;
let mut high = XML_UCS_CATS.len() - 1;
while low <= high {
let mid = (low + high) / 2;
let (name, ranges) = XML_UCS_CATS[mid];
match unsafe { cstr_cmp(cat, name.as_bytes()) } {
core::cmp::Ordering::Equal => return ucs_in_ranges(code, ranges),
core::cmp::Ordering::Less => {
if mid == 0 {
break;
}
high = mid - 1;
}
core::cmp::Ordering::Greater => low = mid + 1,
}
}
-1
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlUCSIsCatCc(int code);
/// ```
///
/// Control characters (C0 + DEL + C1).
#[no_mangle]
pub const extern "C" fn xmlUCSIsCatCc(code: c_int) -> c_int {
if (code >= 0x0 && code <= 0x1f) || (code >= 0x7f && code <= 0x9f) {
1
} else {
0
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// 4. valid family
// ═══════════════════════════════════════════════════════════════════════════════
/// Normalize an attribute value in place (upstream valid.c
/// `xmlValidNormalizeString`): trim leading/trailing spaces and collapse
/// runs of spaces to a single space.
unsafe fn normalize_string(str_: *mut xmlChar) {
if str_.is_null() {
return;
}
let mut src = str_;
let mut dst = str_;
while unsafe { *src == 0x20 } {
src = src.add(1);
}
loop {
let c = unsafe { *src };
if c == 0 {
break;
}
if c == 0x20 {
while unsafe { *src == 0x20 } {
src = src.add(1);
}
if unsafe { *src != 0 } {
unsafe {
*dst = 0x20;
}
dst = dst.add(1);
}
} else {
unsafe {
*dst = *src;
}
dst = dst.add(1);
src = src.add(1);
}
}
unsafe {
*dst = 0;
}
}
/// Report a validation memory error (upstream `xmlVErrMemory`).
unsafe fn v_err_memory(ctxt: *mut _xmlValidCtxt) {
if !ctxt.is_null() {
unsafe {
(*ctxt).valid = 0;
}
}
unsafe {
crate::xml::errors::raise_error(
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
XML_FROM_VALID,
XML_ERR_NO_MEMORY,
XML_ERR_FATAL as c_int,
ptr::null(),
0,
ptr::null(),
ptr::null(),
ptr::null(),
0,
0,
c"Memory allocation failed : \n".as_ptr() as *const c_char,
);
}
}
// XML_DTD_NOT_STANDALONE (xmlerror.h, value 530) — not re-exported by the
// candidate's constants module.
const XML_DTD_NOT_STANDALONE: c_int = 530;
/// Report a validation error with node context (upstream `xmlErrValidNode`),
/// and clear `ctxt->valid`.
unsafe fn v_err_valid_node(
ctxt: *mut _xmlValidCtxt,
_node: *mut _xmlNode,
code: c_int,
msg: *const c_char,
str1: *const xmlChar,
str2: *const xmlChar,
) {
if !ctxt.is_null() {
unsafe {
(*ctxt).valid = 0;
}
}
let mut buf = Vec::new();
let mut arg_idx = 0;
if !msg.is_null() {
let mut i = 0usize;
loop {
let c = unsafe { *((msg as *const u8).add(i)) };
if c == 0 {
break;
}
if c == b'%' {
let c2 = unsafe { *((msg as *const u8).add(i + 1)) };
if c2 == b's' {
if arg_idx == 0 {
append_xmlstr(&mut buf, str1);
} else {
append_xmlstr(&mut buf, str2);
}
arg_idx += 1;
i += 2;
continue;
}
}
buf.push(c);
i += 1;
}
}
buf.push(0);
unsafe {
crate::xml::errors::raise_error(
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
XML_FROM_VALID,
code,
XML_ERR_ERROR as c_int,
ptr::null(),
0,
ptr::null(),
ptr::null(),
ptr::null(),
0,
0,
buf.as_ptr() as *const c_char,
);
}
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlChar *xmlValidNormalizeAttributeValue(xmlDoc *doc, xmlNode *elem,
/// const xmlChar *name,
/// const xmlChar *value);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlValidNormalizeAttributeValue(
doc: *mut _xmlDoc,
elem: *mut _xmlNode,
name: *const xmlChar,
value: *const xmlChar,
) -> *mut xmlChar {
if doc.is_null() || elem.is_null() || name.is_null() || value.is_null() {
return ptr::null_mut();
}
unsafe {
let mut attr_decl =
crate::xml::validation::get_dtd_attr_desc((*doc).intSubset, (*elem).name, name);
if attr_decl.is_null() && !(*doc).extSubset.is_null() {
attr_decl =
crate::xml::validation::get_dtd_attr_desc((*doc).extSubset, (*elem).name, name);
}
if attr_decl.is_null() || (*attr_decl).atype == XML_ATTRIBUTE_CDATA as c_int {
return ptr::null_mut();
}
let ret = crate::abi::exports_xml2::xmlStrdup(value);
if ret.is_null() {
return ptr::null_mut();
}
normalize_string(ret);
ret
}
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlChar *xmlValidCtxtNormalizeAttributeValue(xmlValidCtxt *ctxt,
/// xmlDoc *doc, xmlNode *elem,
/// const xmlChar *name,
/// const xmlChar *value);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlValidCtxtNormalizeAttributeValue(
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
elem: *mut _xmlNode,
name: *const xmlChar,
value: *const xmlChar,
) -> *mut xmlChar {
if doc.is_null() || elem.is_null() || name.is_null() || value.is_null() {
return ptr::null_mut();
}
unsafe {
let mut prefix: *mut xmlChar = ptr::null_mut();
let local_name = crate::xml::string::split_qname2(name, &mut prefix);
if local_name.is_null() {
if !prefix.is_null() {
xmlFreeImpl(prefix as *mut c_void);
}
v_err_memory(ctxt);
return ptr::null_mut();
}
let mut attr_decl: *mut _xmlAttribute = ptr::null_mut();
let mut extsubset = 0;
let ns = (*elem).ns;
if !ns.is_null() && !(*ns).prefix.is_null() {
let mut buf = [0u8; 50];
let elemname = crate::xml::string::build_qname(
(*elem).name,
(*ns).prefix,
buf.as_mut_ptr() as *mut xmlChar,
50,
);
if elemname.is_null() {
if !prefix.is_null() {
xmlFreeImpl(prefix as *mut c_void);
}
v_err_memory(ctxt);
return ptr::null_mut();
}
if !(*doc).intSubset.is_null() {
attr_decl = crate::xml::hash::hash_lookup3(
(*(*doc).intSubset).attributes as *mut crate::xml::hash::HashTable,
local_name,
prefix,
elemname,
) as *mut _xmlAttribute;
}
if attr_decl.is_null() && !(*doc).extSubset.is_null() {
attr_decl = crate::xml::hash::hash_lookup3(
(*(*doc).extSubset).attributes as *mut crate::xml::hash::HashTable,
local_name,
prefix,
elemname,
) as *mut _xmlAttribute;
if !attr_decl.is_null() {
extsubset = 1;
}
}
if !std::ptr::eq(elemname, (*elem).name) {
xmlFreeImpl(elemname as *mut c_void);
}
}
if attr_decl.is_null() && !(*doc).intSubset.is_null() {
attr_decl = crate::xml::hash::hash_lookup3(
(*(*doc).intSubset).attributes as *mut crate::xml::hash::HashTable,
local_name,
prefix,
(*elem).name,
) as *mut _xmlAttribute;
}
if attr_decl.is_null() && !(*doc).extSubset.is_null() {
attr_decl = crate::xml::hash::hash_lookup3(
(*(*doc).extSubset).attributes as *mut crate::xml::hash::HashTable,
local_name,
prefix,
(*elem).name,
) as *mut _xmlAttribute;
if !attr_decl.is_null() {
extsubset = 1;
}
}
if attr_decl.is_null() || (*attr_decl).atype == XML_ATTRIBUTE_CDATA as c_int {
if !prefix.is_null() {
xmlFreeImpl(prefix as *mut c_void);
}
return ptr::null_mut();
}
let ret = crate::abi::exports_xml2::xmlStrdup(value);
if ret.is_null() {
if !prefix.is_null() {
xmlFreeImpl(prefix as *mut c_void);
}
v_err_memory(ctxt);
return ptr::null_mut();
}
normalize_string(ret);
if (*doc).standalone != 0
&& extsubset == 1
&& crate::abi::exports_xml2::xmlStrEqual(value, ret) == 0
{
v_err_valid_node(
ctxt,
elem,
XML_DTD_NOT_STANDALONE,
c"standalone: %s on %s value had to be normalized based on external subset declaration\n"
.as_ptr() as *const c_char,
name,
(*elem).name,
);
}
if !prefix.is_null() {
xmlFreeImpl(prefix as *mut c_void);
}
ret
}
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlValidGetPotentialChildren(xmlElementContent *ctree,
/// const xmlChar **names,
/// int *len, int max);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlValidGetPotentialChildren(
ctree: *mut _xmlElementContent,
names: *mut *const xmlChar,
len: *mut c_int,
max: c_int,
) -> c_int {
if ctree.is_null() || names.is_null() || len.is_null() {
return -1;
}
unsafe {
if *len >= max {
return *len;
}
let pcdata = b"#PCDATA\0";
match (*ctree).type_ as u32 {
t if t == XML_ELEMENT_CONTENT_PCDATA as u32 => {
for i in 0..(*len as usize) {
if crate::abi::exports_xml2::xmlStrEqual(
pcdata.as_ptr() as *const xmlChar,
*names.add(i),
) != 0
{
return *len;
}
}
*names.add(*len as usize) = pcdata.as_ptr() as *const xmlChar;
*len += 1;
}
t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
for i in 0..(*len as usize) {
if crate::abi::exports_xml2::xmlStrEqual((*ctree).name, *names.add(i)) != 0 {
return *len;
}
}
*names.add(*len as usize) = (*ctree).name;
*len += 1;
}
t if t == XML_ELEMENT_CONTENT_SEQ as u32 || t == XML_ELEMENT_CONTENT_OR as u32 => {
xmlValidGetPotentialChildren((*ctree).c1, names, len, max);
xmlValidGetPotentialChildren((*ctree).c2, names, len, max);
}
_ => {}
}
*len
}
}
/// Dummy validity error handler that suppresses messages (upstream
/// `xmlNoValidityErr`).
const unsafe extern "C" fn xml_no_validity_err(_ctx: *mut c_void, _msg: *const c_char) {}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlValidGetValidElements(xmlNode *prev, xmlNode *next,
/// const xmlChar **names, int max);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlValidGetValidElements(
prev: *mut _xmlNode,
next: *mut _xmlNode,
names: *mut *const xmlChar,
max: c_int,
) -> c_int {
if prev.is_null() && next.is_null() {
return -1;
}
if names.is_null() || max <= 0 {
return -1;
}
unsafe {
// Local validation context with errors suppressed, exactly like the
// upstream xmlValidCtxt stack instance in valid.c.
let mut vctxt: _xmlValidCtxt = zeroed();
vctxt.error = Some(xml_no_validity_err);
let mut nb_valid_elements = 0;
let ref_node = if prev.is_null() { next } else { prev };
let parent = (*ref_node).parent;
/*
* Retrieves the parent element declaration.
*/
let mut element_desc = if (*parent).doc.is_null() {
ptr::null_mut()
} else {
crate::xml::validation::get_dtd_element_desc((*(*parent).doc).intSubset, (*parent).name)
};
if element_desc.is_null()
&& !(*parent).doc.is_null()
&& !(*(*parent).doc).extSubset.is_null()
{
element_desc = crate::xml::validation::get_dtd_element_desc(
(*(*parent).doc).extSubset,
(*parent).name,
);
}
if element_desc.is_null() {
return -1;
}
/*
* Do a backup of the current tree structure.
*/
let prev_next = if prev.is_null() {
ptr::null_mut()
} else {
(*prev).next
};
let next_prev = if next.is_null() {
ptr::null_mut()
} else {
(*next).prev
};
let parent_childs = (*parent).children;
let parent_last = (*parent).last;
/*
* Create a dummy node and insert it into the tree.
*/
let dummy_name = b"<!dummy?>\0";
let test_node = crate::abi::exports_tree::xmlNewDocNode(
(*ref_node).doc,
ptr::null_mut(),
dummy_name.as_ptr() as *const xmlChar,
ptr::null(),
);
if test_node.is_null() {
return -1;
}
(*test_node).parent = parent;
(*test_node).prev = prev;
(*test_node).next = next;
let name = (*test_node).name;
if prev.is_null() {
(*parent).children = test_node;
} else {
(*prev).next = test_node;
}
if next.is_null() {
(*parent).last = test_node;
} else {
(*next).prev = test_node;
}
/*
* Insert each potential child node and check if the parent is
* still valid.
*/
let mut elements: [*const xmlChar; 256] = [ptr::null(); 256];
let mut nb_elements = 0;
nb_elements = xmlValidGetPotentialChildren(
(*element_desc).content,
elements.as_mut_ptr(),
&mut nb_elements,
256,
);
let mut i = 0;
while i < nb_elements {
(*test_node).name = elements[i as usize];
if crate::xml::validation::validate_one_element(&mut vctxt, (*parent).doc, parent) != 0
{
let mut j = 0;
while j < nb_valid_elements {
if crate::abi::exports_xml2::xmlStrEqual(
elements[i as usize],
*names.add(j as usize),
) != 0
{
break;
}
j += 1;
}
if j >= nb_valid_elements {
*names.add(nb_valid_elements as usize) = elements[i as usize];
nb_valid_elements += 1;
if nb_valid_elements >= max {
break;
}
}
}
i += 1;
}
/*
* Restore the tree structure.
*/
if prev.is_null() {
(*parent).children = parent_childs;
} else {
(*prev).next = prev_next;
}
if next.is_null() {
(*parent).last = parent_last;
} else {
(*next).prev = next_prev;
}
/*
* Free the dummy node.
*/
(*test_node).name = name;
crate::abi::exports_xml2::xmlFreeNode(test_node);
nb_valid_elements
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// 5. misc2 family
// ═══════════════════════════════════════════════════════════════════════════════
// ── __xml* aliases (globals.c / xmlIO.c) ───────────────────────────────────────
/// Upstream `__xmlDefaultSAXHandler(void)` — pointer to `xmlDefaultSAXHandler`.
#[no_mangle]
pub unsafe extern "C" fn __xmlDefaultSAXHandler() -> *mut _xmlSAXHandlerV1 {
// SAFETY: returning a pointer to an exported static; the caller may
// read/write it exactly as with upstream's deprecated accessor.
core::ptr::addr_of!(crate::abi::data_globals::xmlDefaultSAXHandler) as *mut _xmlSAXHandlerV1
}
/// Upstream `__xmlDefaultSAXLocator(void)` — pointer to `xmlDefaultSAXLocator`.
#[no_mangle]
pub unsafe extern "C" fn __xmlDefaultSAXLocator() -> *mut _xmlSAXLocator {
// SAFETY: returning a pointer to an exported static; the caller may
// read/write it exactly as with upstream's deprecated accessor.
core::ptr::addr_of!(crate::abi::data_globals::xmlDefaultSAXLocator) as *mut _xmlSAXLocator
}
/// Upstream `__htmlDefaultSAXHandler(void)` — pointer to `htmlDefaultSAXHandler`.
#[no_mangle]
pub unsafe extern "C" fn __htmlDefaultSAXHandler() -> *mut _xmlSAXHandlerV1 {
core::ptr::addr_of!(crate::abi::data_globals::htmlDefaultSAXHandler) as *mut _xmlSAXHandlerV1
}
/// Upstream `__docbDefaultSAXHandler(void)` — pointer to `docbDefaultSAXHandler`.
#[no_mangle]
pub unsafe extern "C" fn __docbDefaultSAXHandler() -> *mut _xmlSAXHandlerV1 {
core::ptr::addr_of!(crate::abi::data_globals::docbDefaultSAXHandler) as *mut _xmlSAXHandlerV1
}
/// Upstream `__oldXMLWDcompatibility(void)` — pointer to `oldXMLWDcompatibility`.
#[no_mangle]
pub unsafe extern "C" fn __oldXMLWDcompatibility() -> *mut c_int {
core::ptr::addr_of_mut!(crate::abi::data_globals::oldXMLWDcompatibility)
}
// ── Legacy SAX1 default handler functions (deprecated, exported for the ────
// distro ABI). These are the OLD SAX1 handler entry points (`startElement`,
// `characters`, …) that upstream 2.9-era DSOs export as standalone symbols
// for backward compatibility. They are the DEFAULT handlers and are no-ops
// (or return the documented sentinel), matching the deprecated upstream bodies.
macro_rules! sax1_void_1 {
($name:ident, $($arg:ident: $ty:ty),+) => {
#[no_mangle]
pub unsafe extern "C" fn $name($(_: $ty),*) {}
};
}
#[no_mangle]
pub unsafe extern "C" fn attribute(_: *mut c_void, _: *const xmlChar, _: *const xmlChar) {}
sax1_void_1!(internalSubset, a: *mut c_void, b: *const xmlChar, c: *const xmlChar, d: *const xmlChar);
#[no_mangle]
pub unsafe extern "C" fn isStandalone(_: *mut c_void) -> c_int {
-1
}
#[no_mangle]
pub unsafe extern "C" fn hasInternalSubset(_: *mut c_void) -> c_int {
0
}
#[no_mangle]
pub unsafe extern "C" fn hasExternalSubset(_: *mut c_void) -> c_int {
0
}
#[no_mangle]
pub unsafe extern "C" fn resolveEntity(
_: *mut c_void,
_: *const xmlChar,
_: *const xmlChar,
) -> *mut _xmlParserInput {
ptr::null_mut()
}
#[no_mangle]
pub unsafe extern "C" fn getEntity(_: *mut c_void, _: *const xmlChar) -> *mut _xmlEntity {
ptr::null_mut()
}
sax1_void_1!(entityDecl, a: *mut c_void, b: *const xmlChar, c: c_int, d: *const xmlChar, e: *const xmlChar, f: *mut xmlChar);
sax1_void_1!(notationDecl, a: *mut c_void, b: *const xmlChar, c: *const xmlChar, d: *const xmlChar);
sax1_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);
sax1_void_1!(elementDecl, a: *mut c_void, b: *const xmlChar, c: c_int, d: *mut _xmlElementContent);
sax1_void_1!(unparsedEntityDecl, a: *mut c_void, b: *const xmlChar, c: *const xmlChar, d: *const xmlChar, e: *const xmlChar);
#[no_mangle]
pub unsafe extern "C" fn setDocumentLocator(_: *mut c_void, _: *mut _xmlSAXLocator) {}
#[no_mangle]
pub unsafe extern "C" fn startDocument(_: *mut c_void) {}
#[no_mangle]
pub unsafe extern "C" fn endDocument(_: *mut c_void) {}
#[no_mangle]
pub unsafe extern "C" fn startElement(_: *mut c_void, _: *const xmlChar, _: *mut *const xmlChar) {}
#[no_mangle]
pub unsafe extern "C" fn endElement(_: *mut c_void, _: *const xmlChar) {}
#[no_mangle]
pub unsafe extern "C" fn reference(_: *mut c_void, _: *const xmlChar) {}
#[no_mangle]
pub unsafe extern "C" fn characters(_: *mut c_void, _: *const xmlChar, _: c_int) {}
#[no_mangle]
pub unsafe extern "C" fn ignorableWhitespace(_: *mut c_void, _: *const xmlChar, _: c_int) {}
#[no_mangle]
pub unsafe extern "C" fn processingInstruction(
_: *mut c_void,
_: *const xmlChar,
_: *const xmlChar,
) {
}
#[no_mangle]
pub unsafe extern "C" fn comment(_: *mut c_void, _: *const xmlChar) {}
#[no_mangle]
pub unsafe extern "C" fn getParameterEntity(_: *mut c_void, _: *const xmlChar) -> *mut _xmlEntity {
ptr::null_mut()
}
#[no_mangle]
pub unsafe extern "C" fn cdataBlock(_: *mut c_void, _: *const xmlChar, _: c_int) {}
sax1_void_1!(externalSubset, a: *mut c_void, b: *const xmlChar, c: *const xmlChar, d: *const xmlChar);
/// Upstream `__xmlLastError(void)` — pointer to the exported `xmlLastError`
/// mirror (kept in sync with the thread-local error state on every raise).
#[no_mangle]
pub unsafe extern "C" fn __xmlLastError() -> *mut _xmlError {
// SAFETY: returning a pointer to an exported static; the caller may
// read/write it exactly as with upstream's deprecated accessor.
core::ptr::addr_of!(crate::abi::data_globals::xmlLastError) as *mut _xmlError
}
/// Upstream `__xmlParserInputBufferCreateFilename(const char *URI,
/// xmlCharEncoding enc)` — the non-reentrant internal variant; forwards to
/// the regular `xmlParserInputBufferCreateFilename`.
#[no_mangle]
pub unsafe extern "C" fn __xmlParserInputBufferCreateFilename(
URI: *const c_char,
enc: c_int,
) -> *mut _xmlParserInputBuffer {
crate::abi::exports_xml2::xmlParserInputBufferCreateFilename(URI, enc)
}
// ── xmlFormatError (error.c) ───────────────────────────────────────────────────
/// Compute the input window around `input->cur` (upstream parserInternals.c
/// `xmlParserInputGetWindow`).
unsafe fn parser_input_get_window(
input: *mut _xmlParserInput,
start_out: *mut *const xmlChar,
size_in_out: *mut c_int,
offset_out: *mut c_int,
) {
unsafe {
let mut cur = (*input).cur;
let base = (*input).base;
let size = *size_in_out;
// Skip backwards over any end-of-lines.
while cur > base && (*cur == b'\n' || *cur == b'\r') {
cur = cur.sub(1);
}
let mut n: usize = 0;
// Search backwards for beginning-of-line (to max buff size).
while n < size as usize && cur > base && *cur != b'\n' && *cur != b'\r' {
cur = cur.sub(1);
n += 1;
}
if n > 0 && (*cur == b'\n' || *cur == b'\r') {
cur = cur.add(1);
} else {
// Skip over continuation bytes.
while cur < (*input).cur && (*cur & 0xC0) == 0x80 {
cur = cur.add(1);
}
}
// Calculate the error position in terms of the current position.
let mut col = (*input).cur as usize - cur as usize;
// Search forward for end-of-line (to max buff size).
let mut nfwd: usize = 0;
let start = cur;
while *cur != 0 && *cur != b'\n' && *cur != b'\r' {
let avail = (*input).end as usize - cur as usize;
let mut clen: c_int = avail as c_int;
let c = xmlGetUTF8Char(cur, &mut clen);
if c < 0 || nfwd + clen as usize > size as usize {
break;
}
cur = cur.add(clen as usize);
nfwd += clen as usize;
}
if col >= nfwd {
col = if nfwd < size as usize {
nfwd
} else {
size as usize - 1
};
}
*start_out = start;
*size_in_out = nfwd as c_int;
*offset_out = col as c_int;
}
}
/// `pub(crate)` wrapper of [`parser_input_get_window`] for sibling export
/// modules (xmlCtxtGetInputWindow, 11.1-X R-000165 closure).
pub(crate) unsafe fn parser_input_get_window_pub(
input: *mut _xmlParserInput,
start_out: *mut *const xmlChar,
size_in_out: *mut c_int,
offset_out: *mut c_int,
) {
unsafe { parser_input_get_window(input, start_out, size_in_out, offset_out) };
}
/// Print the source context around an input position (upstream error.c
/// `xmlParserPrintFileContextInternal`).
unsafe fn print_file_context(
input: *mut _xmlParserInput,
channel: xmlGenericErrorFunc,
data: *mut c_void,
) {
if input.is_null() || unsafe { (*input).cur.is_null() } {
return;
}
let mut n: c_int = 80;
let mut start: *const xmlChar = ptr::null();
let mut col: c_int = 0;
unsafe { parser_input_get_window(input, &mut start, &mut n, &mut col) };
let mut content = [0u8; 81];
if n > 0 && !start.is_null() {
unsafe {
ptr::copy_nonoverlapping(start, content.as_mut_ptr(), n as usize);
}
}
content[n as usize] = 0;
unsafe { chan_emit(channel, data, &content[..n as usize]) };
// Create blank line with problem pointer.
let mut i = 0usize;
while i < col as usize {
if content[i] != b'\t' {
content[i] = b' ';
}
i += 1;
}
content[i] = b'^';
i += 1;
content[i] = 0;
unsafe { chan_emit(channel, data, &content[..i]) };
}
/// # UPSTREAM-PARITY
///
/// ```c
/// void xmlFormatError(const xmlError *err, xmlGenericErrorFunc channel,
/// void *data);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlFormatError(
err: *const _xmlError,
channel: Option<xmlGenericErrorFunc>,
data: *mut c_void,
) {
if err.is_null() || channel.is_none() {
return;
}
let channel = channel.unwrap();
unsafe {
let e = &*err;
let message = e.message;
let file = e.file;
let line = e.line;
let code = e.code;
let domain = e.domain;
let level = e.level;
let node = e.node as *mut _xmlNode;
if code == XML_ERR_OK {
return;
}
let mut ctxt: *mut _xmlParserCtxt = ptr::null_mut();
if domain == XML_FROM_PARSER
|| domain == XML_FROM_HTML
|| domain == XML_FROM_DTD
|| domain == XML_FROM_NAMESPACE
|| domain == XML_FROM_IO
|| domain == XML_FROM_VALID
{
ctxt = e.ctxt as *mut _xmlParserCtxt;
}
let mut name: *const xmlChar = ptr::null();
if !node.is_null()
&& (*node).type_ == XML_ELEMENT_NODE as c_int
&& domain != XML_FROM_SCHEMASV
{
name = (*node).name;
}
let mut input: *mut _xmlParserInput = ptr::null_mut();
let mut cur: *mut _xmlParserInput = ptr::null_mut();
if !ctxt.is_null() && !(*ctxt).input.is_null() {
input = (*ctxt).input;
if (*input).filename.is_null() && (*ctxt).inputNr > 1 {
cur = input;
input = *(*ctxt).inputTab.add((*ctxt).inputNr as usize - 2);
}
if !(*input).filename.is_null() {
let mut buf = Vec::new();
append_cstr(&mut buf, (*input).filename);
buf.push(b':');
append_int(&mut buf, (*input).line);
buf.extend_from_slice(b": ");
chan_emit(channel, data, &buf);
} else if line != 0 && domain == XML_FROM_PARSER {
let mut buf = b"Entity: line ".to_vec();
append_int(&mut buf, (*input).line);
buf.extend_from_slice(b": ");
chan_emit(channel, data, &buf);
}
} else {
if !file.is_null() {
let mut buf = Vec::new();
append_cstr(&mut buf, file);
buf.push(b':');
append_int(&mut buf, line);
buf.extend_from_slice(b": ");
chan_emit(channel, data, &buf);
} else if line != 0
&& (domain == XML_FROM_PARSER
|| domain == XML_FROM_SCHEMASV
|| domain == XML_FROM_SCHEMASP
|| domain == XML_FROM_DTD
|| domain == XML_FROM_RELAXNGP
|| domain == XML_FROM_RELAXNGV)
{
let mut buf = b"Entity: line ".to_vec();
append_int(&mut buf, line);
buf.extend_from_slice(b": ");
chan_emit(channel, data, &buf);
}
}
if !name.is_null() {
let mut buf = b"element ".to_vec();
append_xmlstr(&mut buf, name);
buf.extend_from_slice(b": ");
chan_emit(channel, data, &buf);
}
let domain_prefix: &[u8] = match domain {
XML_FROM_PARSER => b"parser ",
XML_FROM_NAMESPACE => b"namespace ",
XML_FROM_DTD | XML_FROM_VALID => b"validity ",
XML_FROM_HTML => b"HTML parser ",
XML_FROM_MEMORY => b"memory ",
XML_FROM_OUTPUT => b"output ",
XML_FROM_IO => b"I/O ",
XML_FROM_XINCLUDE => b"XInclude ",
XML_FROM_XPATH => b"XPath ",
XML_FROM_XPOINTER => b"parser ",
XML_FROM_REGEXP => b"regexp ",
XML_FROM_MODULE => b"module ",
XML_FROM_SCHEMASV => b"Schemas validity ",
XML_FROM_SCHEMASP => b"Schemas parser ",
XML_FROM_RELAXNGP => b"Relax-NG parser ",
XML_FROM_RELAXNGV => b"Relax-NG validity ",
XML_FROM_CATALOG => b"Catalog ",
XML_FROM_C14N => b"C14N ",
XML_FROM_XSLT => b"XSLT ",
XML_FROM_I18N => b"encoding ",
XML_FROM_SCHEMATRONV => b"schematron ",
XML_FROM_BUFFER => b"internal buffer ",
XML_FROM_URI => b"URI ",
_ => b"",
};
chan_emit(channel, data, domain_prefix);
let level_prefix: &[u8] = if level == XML_ERR_NONE as c_int {
b": "
} else if level == XML_ERR_WARNING as c_int {
b"warning : "
} else if level == XML_ERR_ERROR as c_int || level == XML_ERR_FATAL as c_int {
b"error : "
} else {
b": "
};
chan_emit(channel, data, level_prefix);
if !message.is_null() {
let len = cstr_len(message as *const c_char);
let msg_bytes = core::slice::from_raw_parts(message as *const u8, len);
let mut buf = msg_bytes.to_vec();
if len > 0 && *message.add(len - 1) as u8 != b'\n' {
buf.push(b'\n');
}
chan_emit(channel, data, &buf);
} else {
chan_emit(channel, data, b"No error message provided\n");
}
if !ctxt.is_null() {
if !input.is_null()
&& ((*input).buf.is_null() || (*(*input).buf).encoder.is_null())
&& code == XML_ERR_INVALID_ENCODING
&& (*input).cur < (*input).end
{
let mut buf = b"Bytes:".to_vec();
for i in 0..4 {
if (*input).cur.add(i) >= (*input).end {
break;
}
let b = *(*input).cur.add(i);
buf.extend_from_slice(b" 0x");
buf.push(hex_digit(b >> 4));
buf.push(hex_digit(b & 0xf));
}
buf.push(b'\n');
chan_emit(channel, data, &buf);
}
print_file_context(input, channel, data);
if !cur.is_null() {
if !(*cur).filename.is_null() {
let mut buf = Vec::new();
append_cstr(&mut buf, (*cur).filename);
buf.extend_from_slice(b": ");
append_int(&mut buf, (*cur).line);
buf.extend_from_slice(b": \n");
chan_emit(channel, data, &buf);
} else if line != 0
&& (domain == XML_FROM_PARSER
|| domain == XML_FROM_SCHEMASV
|| domain == XML_FROM_SCHEMASP
|| domain == XML_FROM_DTD
|| domain == XML_FROM_RELAXNGP
|| domain == XML_FROM_RELAXNGV)
{
let mut buf = b"Entity: line ".to_vec();
append_int(&mut buf, (*cur).line);
buf.extend_from_slice(b": \n");
chan_emit(channel, data, &buf);
}
print_file_context(cur, channel, data);
}
}
if domain == XML_FROM_XPATH
&& !e.str1.is_null()
&& e.int1 < 100
&& e.int1 < crate::abi::exports_xml2::xmlStrlen(e.str1 as *const xmlChar)
{
let mut buf = Vec::new();
append_cstr(&mut buf, e.str1);
buf.push(b'\n');
chan_emit(channel, data, &buf);
let mut marker = vec![b' '; e.int1 as usize];
marker.push(b'^');
marker.push(b'\n');
chan_emit(channel, data, &marker);
}
}
}
// ── tree node constructors (tree.c) ───────────────────────────────────────────
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlNode *xmlNewCharRef(xmlDoc *doc, const xmlChar *name);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlNewCharRef(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlNode {
if name.is_null() {
return ptr::null_mut();
}
crate::abi::exports_tree::xmlNewReference(doc, name)
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlNode *xmlNewDocText(const xmlDoc *doc, const xmlChar *content);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlNewDocText(
doc: *const _xmlDoc,
content: *const xmlChar,
) -> *mut _xmlNode {
let cur = unsafe { crate::abi::exports_xml2::xmlNewText(content) };
if !cur.is_null() {
unsafe { (*cur).doc = doc as *mut _xmlDoc };
}
cur
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlNode *xmlNewDocTextLen(xmlDoc *doc, const xmlChar *content, int len);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlNewDocTextLen(
doc: *mut _xmlDoc,
content: *const xmlChar,
len: c_int,
) -> *mut _xmlNode {
let cur = unsafe { crate::abi::exports_tree::xmlNewTextLen(content, len) };
if !cur.is_null() {
unsafe { (*cur).doc = doc };
}
cur
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlNode *xmlNewDocComment(xmlDoc *doc, const xmlChar *content);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlNewDocComment(
doc: *mut _xmlDoc,
content: *const xmlChar,
) -> *mut _xmlNode {
let cur = unsafe { crate::abi::exports_xml2::xmlNewComment(content) };
if !cur.is_null() {
unsafe { (*cur).doc = doc };
}
cur
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlNode *xmlNewDocPI(xmlDoc *doc, const xmlChar *name,
/// const xmlChar *content);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlNewDocPI(
doc: *mut _xmlDoc,
name: *const xmlChar,
content: *const xmlChar,
) -> *mut _xmlNode {
let cur = unsafe { crate::abi::exports_xml2::xmlNewPI(name, content) };
if !cur.is_null() {
unsafe { (*cur).doc = doc };
}
cur
}
/// Split a QName, returning a pointer to the local part and storing the
/// prefix length (upstream tree.c `xmlSplitQName3`).
unsafe fn qname_split3(name: *const xmlChar, len: *mut c_int) -> *const xmlChar {
if name.is_null() || len.is_null() {
return ptr::null();
}
unsafe {
if *name == b':' as xmlChar {
return ptr::null();
}
let mut l = 0usize;
while *name.add(l) != 0 && *name.add(l) != b':' as xmlChar {
l += 1;
}
if *name.add(l) == 0 {
return ptr::null();
}
*len = l as c_int;
name.add(l + 1)
}
}
/// # UPSTREAM-PARITY
///
/// ```c
/// xmlElementContent *xmlNewDocElementContent(xmlDoc *doc,
/// const xmlChar *name,
/// xmlElementContentType type);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlNewDocElementContent(
doc: *mut _xmlDoc,
name: *const xmlChar,
type_: xmlElementContentType,
) -> *mut _xmlElementContent {
let _ = doc;
unsafe {
let ret = xmlMallocZero(size_of::<_xmlElementContent>()) as *mut _xmlElementContent;
if ret.is_null() {
return ptr::null_mut();
}
(*ret).type_ = type_ as c_int;
(*ret).ocur = XML_ELEMENT_CONTENT_ONCE as c_int;
if !name.is_null() {
let mut l: c_int = 0;
let tmp = qname_split3(name, &mut l);
if tmp.is_null() {
(*ret).name = crate::abi::exports_xml2::xmlStrdup(name);
} else {
(*ret).prefix = crate::abi::exports_xml2::xmlStrndup(name, l);
(*ret).name = crate::abi::exports_xml2::xmlStrdup(tmp);
if (*ret).prefix.is_null() {
crate::xml::dtd::free_content_model(ret);
return ptr::null_mut();
}
}
if (*ret).name.is_null() {
crate::xml::dtd::free_content_model(ret);
return ptr::null_mut();
}
}
ret
}
}
// ── node content / attribute value (tree.c) ───────────────────────────────────
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlNodeBufGetContent(xmlBuffer *buffer, const xmlNode *cur);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlNodeBufGetContent(
buffer: *mut _xmlBuffer,
cur: *const _xmlNode,
) -> c_int {
if cur.is_null() || buffer.is_null() {
return -1;
}
let content = unsafe { crate::xml::tree::node_get_content(cur as *mut _xmlNode) };
if content.is_null() {
return 0;
}
let len = unsafe { crate::abi::exports_xml2::xmlStrlen(content) };
let ret = crate::xml::io::buf_add(buffer, content, len);
unsafe { xmlFreeImpl(content as *mut c_void) };
if ret < 0 {
-1
} else {
0
}
}
/// Find an attribute node by name and namespace (upstream tree.c
/// `xmlGetPropNodeInternal` with useDTD == 0).
unsafe fn find_prop_ns(
node: *const _xmlNode,
name: *const xmlChar,
ns_uri: *const xmlChar,
) -> *mut _xmlAttr {
if node.is_null() || unsafe { (*node).type_ != XML_ELEMENT_NODE as c_int } || name.is_null() {
return ptr::null_mut();
}
let mut prop = unsafe { (*node).properties };
while !prop.is_null() {
let ns = unsafe { (*prop).ns };
let ns_match = if ns_uri.is_null() {
ns.is_null()
} else {
!ns.is_null()
&& !unsafe { (*ns).href }.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual((*ns).href, ns_uri) != 0 }
};
if ns_match && unsafe { crate::abi::exports_xml2::xmlStrEqual((*prop).name, name) != 0 } {
return prop;
}
prop = unsafe { (*prop).next };
}
ptr::null_mut()
}
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlNodeGetAttrValue(const xmlNode *node, const xmlChar *name,
/// const xmlChar *nsUri, xmlChar **out);
/// ```
///
/// Returns 0 with `*out` set to the attribute value, 1 when the attribute
/// is missing (or arguments are invalid), -1 on allocation failure.
#[no_mangle]
pub unsafe extern "C" fn xmlNodeGetAttrValue(
node: *const _xmlNode,
name: *const xmlChar,
ns_uri: *const xmlChar,
out: *mut *mut xmlChar,
) -> c_int {
if out.is_null() {
return 1;
}
unsafe { *out = ptr::null_mut() };
let prop = unsafe { find_prop_ns(node, name, ns_uri) };
if prop.is_null() {
return 1;
}
let value = unsafe { crate::xml::tree::node_get_content(prop as *mut _xmlNode) };
if value.is_null() {
return -1;
}
unsafe { *out = value };
0
}
// ── element content serialization (valid.c) ───────────────────────────────────
/// # UPSTREAM-PARITY
///
/// ```c
/// void xmlSprintfElementContent(char *buf, xmlElementContent *content,
/// int englob);
/// ```
///
/// Deprecated; upstream (2.13+) ships this as an empty stub.
#[no_mangle]
pub const unsafe extern "C" fn xmlSprintfElementContent(
_buf: *mut c_char,
_content: *mut _xmlElementContent,
_englob: c_int,
) {
}
/// # UPSTREAM-PARITY
///
/// ```c
/// void xmlSnprintfElementContent(char *buf, int size,
/// xmlElementContent *content, int englob);
/// ```
#[no_mangle]
pub unsafe extern "C" fn xmlSnprintfElementContent(
buf: *mut c_char,
size: c_int,
content: *mut _xmlElementContent,
englob: c_int,
) {
if content.is_null() || buf.is_null() {
return;
}
unsafe {
let mut len = cstr_len(buf) as i32;
if size - len < 50 {
if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
cstr_cat_lit(buf, b" ...");
}
return;
}
if englob != 0 {
cstr_cat_lit(buf, b"(");
}
match (*content).type_ as u32 {
t if t == XML_ELEMENT_CONTENT_PCDATA as u32 => {
cstr_cat_lit(buf, b"#PCDATA");
}
t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
let mut qname_len = crate::abi::exports_xml2::xmlStrlen((*content).name);
if !(*content).prefix.is_null() {
qname_len += crate::abi::exports_xml2::xmlStrlen((*content).prefix) + 1;
}
if size - len < qname_len + 10 {
cstr_cat_lit(buf, b" ...");
return;
}
if !(*content).prefix.is_null() {
cstr_cat_xmlstr(buf, (*content).prefix);
cstr_cat_lit(buf, b":");
}
if !(*content).name.is_null() {
cstr_cat_xmlstr(buf, (*content).name);
}
}
t if t == XML_ELEMENT_CONTENT_SEQ as u32 => {
if (*(*content).c1).type_ == XML_ELEMENT_CONTENT_OR as c_int
|| (*(*content).c1).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
{
xmlSnprintfElementContent(buf, size, (*content).c1, 1);
} else {
xmlSnprintfElementContent(buf, size, (*content).c1, 0);
}
len = cstr_len(buf) as i32;
if size - len < 50 {
if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
cstr_cat_lit(buf, b" ...");
}
return;
}
cstr_cat_lit(buf, b" , ");
if ((*(*content).c2).type_ == XML_ELEMENT_CONTENT_OR as c_int
|| (*(*content).c2).ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
&& (*(*content).c2).type_ != XML_ELEMENT_CONTENT_ELEMENT as c_int
{
xmlSnprintfElementContent(buf, size, (*content).c2, 1);
} else {
xmlSnprintfElementContent(buf, size, (*content).c2, 0);
}
}
t if t == XML_ELEMENT_CONTENT_OR as u32 => {
if (*(*content).c1).type_ == XML_ELEMENT_CONTENT_OR as c_int
|| (*(*content).c1).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
{
xmlSnprintfElementContent(buf, size, (*content).c1, 1);
} else {
xmlSnprintfElementContent(buf, size, (*content).c1, 0);
}
len = cstr_len(buf) as i32;
if size - len < 50 {
if len > 0 && size - len > 4 && *buf.add(len as usize - 1) != b'.' as c_char {
cstr_cat_lit(buf, b" ...");
}
return;
}
cstr_cat_lit(buf, b" | ");
if ((*(*content).c2).type_ == XML_ELEMENT_CONTENT_SEQ as c_int
|| (*(*content).c2).ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
&& (*(*content).c2).type_ != XML_ELEMENT_CONTENT_ELEMENT as c_int
{
xmlSnprintfElementContent(buf, size, (*content).c2, 1);
} else {
xmlSnprintfElementContent(buf, size, (*content).c2, 0);
}
}
_ => {}
}
if size - cstr_len(buf) as i32 <= 2 {
return;
}
if englob != 0 {
cstr_cat_lit(buf, b")");
}
match (*content).ocur as u32 {
t if t == XML_ELEMENT_CONTENT_OPT as u32 => cstr_cat_lit(buf, b"?"),
t if t == XML_ELEMENT_CONTENT_MULT as u32 => cstr_cat_lit(buf, b"*"),
t if t == XML_ELEMENT_CONTENT_PLUS as u32 => cstr_cat_lit(buf, b"+"),
_ => {}
}
}
}
// ── deprecated stubs ───────────────────────────────────────────────────────────
/// # UPSTREAM-PARITY
///
/// ```c
/// int xmlUpgradeOldNs(xmlDocPtr doc);
/// ```
///
/// Deprecated (legacy.c): modern documents no longer carry old-style
/// namespace declarations, so this is a no-op returning 0.
#[no_mangle]
pub const unsafe extern "C" fn xmlUpgradeOldNs(_doc: *mut _xmlDoc) -> c_int {
0
}