leptris 1.9.384.0-arm-linux → 1.9.386.0-arm-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/document.rb +11 -1
- data/lib/leptris/xml/element.rb +15 -10
- data/lib/leptris/xml/ffi.rb +42 -0
- data/lib/leptris/xml/xsd.rb +35 -0
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +135 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/include/leptris/descriptor.h +16 -0
- data/vendor-src/libleptris/src/include/leptris.h +77 -0
- data/vendor-src/libleptris/src/leptris/descriptor/plan.c +82 -0
- data/vendor-src/libleptris/src/leptris/dom/element_modify.c +23 -2
- data/vendor-src/libleptris/src/leptris/dom/element_query.c +201 -0
- data/vendor-src/libleptris/src/leptris/memory/pool.c +27 -0
- data/vendor-src/libleptris/src/leptris/memory/pool.h +5 -0
- data/vendor-src/libleptris/src/leptris/xpath/evaluator_axes.c +15 -0
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +232 -15
- data/vendor-src/libleptris/src/leptris/xsd/datatypes.c +180 -46
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +69 -22
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +10 -0
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +201 -0
- data/vendor-src/libleptris/test/dom/test_document_children.cpp +256 -0
- data/vendor-src/libleptris/test/dom/test_dom.cpp +263 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/schema_set/f.xsd +13 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/schema_set/g.xsd +9 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +293 -1
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +4 -2
|
@@ -1616,6 +1616,165 @@ LEPTRIS_API const char* leptris_element_namespace_decl_uri(
|
|
|
1616
1616
|
return uri;
|
|
1617
1617
|
}
|
|
1618
1618
|
|
|
1619
|
+
/* #1874: pre-order DFS over the subtree, one row per (element with
|
|
1620
|
+
* declarations, declaration). The explicit stack (elements + the
|
|
1621
|
+
* borrowed path-name chain) keeps arbitrarily deep trees off the C
|
|
1622
|
+
* stack. First pass counts rows and blob bytes; the second fills —
|
|
1623
|
+
* the two passes walk identically, so sizing and filling agree. */
|
|
1624
|
+
LEPTRIS_API size_t leptris_element_namespace_declarations_snapshot(
|
|
1625
|
+
LeptrisElement elem,
|
|
1626
|
+
leptris_ns_decl_row* rows, size_t rows_cap, size_t* out_row_count,
|
|
1627
|
+
char* blob, size_t blob_cap, size_t* blob_used_out) {
|
|
1628
|
+
size_t row_count = 0;
|
|
1629
|
+
size_t blob_used = 0;
|
|
1630
|
+
|
|
1631
|
+
/* Dynamic DFS stack: element + depth. The path-name chain rides
|
|
1632
|
+
* a parallel array of borrowed name pointers indexed by depth. */
|
|
1633
|
+
struct dfs_entry {
|
|
1634
|
+
LeptrisElement e;
|
|
1635
|
+
uint32_t depth;
|
|
1636
|
+
};
|
|
1637
|
+
size_t stack_cap = 64, names_cap = 64;
|
|
1638
|
+
struct dfs_entry* stack = (struct dfs_entry*)malloc(
|
|
1639
|
+
stack_cap * sizeof(*stack));
|
|
1640
|
+
const char** names = (const char**)malloc(names_cap * sizeof(*names));
|
|
1641
|
+
if (!stack || !names) {
|
|
1642
|
+
free(stack);
|
|
1643
|
+
free(names);
|
|
1644
|
+
if (out_row_count) *out_row_count = 0;
|
|
1645
|
+
if (blob_used_out) *blob_used_out = 0;
|
|
1646
|
+
return (size_t)-1;
|
|
1647
|
+
}
|
|
1648
|
+
size_t sp = 0;
|
|
1649
|
+
|
|
1650
|
+
if (elem) {
|
|
1651
|
+
stack[sp].e = elem;
|
|
1652
|
+
stack[sp].depth = 0;
|
|
1653
|
+
sp++;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
while (sp > 0) {
|
|
1657
|
+
sp--;
|
|
1658
|
+
LeptrisElement e = stack[sp].e;
|
|
1659
|
+
uint32_t depth = stack[sp].depth;
|
|
1660
|
+
|
|
1661
|
+
/* Path name for this element (depth 0 writes nothing); DFS
|
|
1662
|
+
* visits the parent first, so ancestors are recorded. */
|
|
1663
|
+
if (depth > 0) {
|
|
1664
|
+
if (depth >= names_cap) {
|
|
1665
|
+
size_t nc = names_cap * 2;
|
|
1666
|
+
while (nc <= depth) nc *= 2;
|
|
1667
|
+
const char** grown = (const char**)realloc(
|
|
1668
|
+
names, nc * sizeof(*names));
|
|
1669
|
+
if (!grown) goto oom;
|
|
1670
|
+
names = grown;
|
|
1671
|
+
names_cap = nc;
|
|
1672
|
+
}
|
|
1673
|
+
names[depth] = leptris_element_name(e);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
/* Count (or emit) this element's declarations. */
|
|
1677
|
+
size_t decls = leptris_element_namespace_count(e);
|
|
1678
|
+
for (size_t i = 0; i < decls; i++) {
|
|
1679
|
+
const char* prefix = NULL;
|
|
1680
|
+
const char* uri = NULL;
|
|
1681
|
+
element_namespace_decl_at(e, i, &prefix, &uri);
|
|
1682
|
+
size_t prefix_len = prefix ? strlen(prefix) : 0;
|
|
1683
|
+
size_t uri_len = uri ? strlen(uri) : 0;
|
|
1684
|
+
/* Row bytes: the path names + prefix + href, the
|
|
1685
|
+
* prefix/href NUL-terminated like the attributes_snapshot
|
|
1686
|
+
* lane blobs (bindings read C strings straight out). */
|
|
1687
|
+
size_t row_bytes = (prefix ? prefix_len + 1 : 0) +
|
|
1688
|
+
uri_len + 1;
|
|
1689
|
+
for (uint32_t d = 1; d <= depth; d++) {
|
|
1690
|
+
const char* nm = names[d] ? names[d] : "";
|
|
1691
|
+
row_bytes += strlen(nm) + 1;
|
|
1692
|
+
}
|
|
1693
|
+
if (rows) {
|
|
1694
|
+
if (row_count >= rows_cap ||
|
|
1695
|
+
blob_used + row_bytes > blob_cap) {
|
|
1696
|
+
free(stack);
|
|
1697
|
+
free(names);
|
|
1698
|
+
return (size_t)-1;
|
|
1699
|
+
}
|
|
1700
|
+
leptris_ns_decl_row* r = &rows[row_count];
|
|
1701
|
+
r->depth = depth;
|
|
1702
|
+
r->name_count = depth;
|
|
1703
|
+
/* Path names: depth components, ancestor-first,
|
|
1704
|
+
* each NUL-terminated. */
|
|
1705
|
+
r->names_off = (uint32_t)blob_used;
|
|
1706
|
+
for (uint32_t d = 1; d <= depth; d++) {
|
|
1707
|
+
const char* nm = names[d] ? names[d] : "";
|
|
1708
|
+
size_t nl = strlen(nm) + 1;
|
|
1709
|
+
memcpy(blob + blob_used, nm, nl);
|
|
1710
|
+
blob_used += nl;
|
|
1711
|
+
}
|
|
1712
|
+
if (prefix) {
|
|
1713
|
+
r->prefix_off = (uint32_t)blob_used;
|
|
1714
|
+
r->prefix_len = (uint32_t)prefix_len;
|
|
1715
|
+
memcpy(blob + blob_used, prefix, prefix_len);
|
|
1716
|
+
blob_used += prefix_len;
|
|
1717
|
+
blob[blob_used++] = '\0';
|
|
1718
|
+
} else {
|
|
1719
|
+
r->prefix_off = 0xFFFFFFFFu;
|
|
1720
|
+
r->prefix_len = 0;
|
|
1721
|
+
}
|
|
1722
|
+
r->href_off = (uint32_t)blob_used;
|
|
1723
|
+
r->href_len = (uint32_t)uri_len;
|
|
1724
|
+
if (uri_len) {
|
|
1725
|
+
memcpy(blob + blob_used, uri, uri_len);
|
|
1726
|
+
blob_used += uri_len;
|
|
1727
|
+
}
|
|
1728
|
+
blob[blob_used++] = '\0';
|
|
1729
|
+
} else {
|
|
1730
|
+
/* Sizing pass: measure the same bytes the fill pass
|
|
1731
|
+
* writes (those advance blob_used inline). */
|
|
1732
|
+
blob_used += row_bytes;
|
|
1733
|
+
}
|
|
1734
|
+
row_count++;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
/* Push children in REVERSE so the pop order is document
|
|
1738
|
+
* order. */
|
|
1739
|
+
size_t nkids = 0;
|
|
1740
|
+
for (LeptrisElement c = leptris_element_first_child_any(e); c;
|
|
1741
|
+
c = leptris_element_next_sibling_any(c)) {
|
|
1742
|
+
nkids++;
|
|
1743
|
+
}
|
|
1744
|
+
if (sp + nkids > stack_cap) {
|
|
1745
|
+
size_t nc = stack_cap * 2;
|
|
1746
|
+
while (nc < sp + nkids) nc *= 2;
|
|
1747
|
+
struct dfs_entry* grown = (struct dfs_entry*)realloc(
|
|
1748
|
+
stack, nc * sizeof(*stack));
|
|
1749
|
+
if (!grown) goto oom;
|
|
1750
|
+
stack = grown;
|
|
1751
|
+
stack_cap = nc;
|
|
1752
|
+
}
|
|
1753
|
+
/* Collect children into the stack tail backwards. */
|
|
1754
|
+
size_t w = sp + nkids;
|
|
1755
|
+
for (LeptrisElement c = leptris_element_first_child_any(e); c;
|
|
1756
|
+
c = leptris_element_next_sibling_any(c)) {
|
|
1757
|
+
w--;
|
|
1758
|
+
stack[w].e = c;
|
|
1759
|
+
stack[w].depth = depth + 1;
|
|
1760
|
+
}
|
|
1761
|
+
sp += nkids;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
free(stack);
|
|
1765
|
+
free(names);
|
|
1766
|
+
if (out_row_count) *out_row_count = row_count;
|
|
1767
|
+
if (blob_used_out) *blob_used_out = blob_used;
|
|
1768
|
+
return 0;
|
|
1769
|
+
|
|
1770
|
+
oom:
|
|
1771
|
+
free(stack);
|
|
1772
|
+
free(names);
|
|
1773
|
+
if (out_row_count) *out_row_count = 0;
|
|
1774
|
+
if (blob_used_out) *blob_used_out = 0;
|
|
1775
|
+
return (size_t)-1;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1619
1778
|
/* ============================================================================
|
|
1620
1779
|
* Namespace mutation (issue #186).
|
|
1621
1780
|
*
|
|
@@ -1790,3 +1949,45 @@ LEPTRIS_API size_t leptris_element_attributes_raw(
|
|
|
1790
1949
|
}
|
|
1791
1950
|
return written;
|
|
1792
1951
|
}
|
|
1952
|
+
|
|
1953
|
+
/* ---- #1855: address-Integer batch faces ------------------------------ */
|
|
1954
|
+
|
|
1955
|
+
LEPTRIS_API size_t leptris_element_children_batch(
|
|
1956
|
+
uintptr_t elem_addr,
|
|
1957
|
+
uintptr_t* child_addrs, uint8_t* child_kinds, size_t cap) {
|
|
1958
|
+
if (!elem_addr) return 0;
|
|
1959
|
+
LeptrisElement e = (LeptrisElement)elem_addr;
|
|
1960
|
+
size_t count = 0;
|
|
1961
|
+
/* NODE-level walk: the batch carries every child kind (text,
|
|
1962
|
+
* comment, PI, CDATA included) — first_child_any is
|
|
1963
|
+
* element-only. */
|
|
1964
|
+
for (LeptrisNode* c = leptris_node_first_child((LeptrisNode*)e); c;
|
|
1965
|
+
c = leptris_node_next_sibling(c)) {
|
|
1966
|
+
if (child_addrs && child_kinds && count < cap) {
|
|
1967
|
+
child_addrs[count] = (uintptr_t)c;
|
|
1968
|
+
child_kinds[count] = (uint8_t)c->type;
|
|
1969
|
+
}
|
|
1970
|
+
count++;
|
|
1971
|
+
}
|
|
1972
|
+
/* Undersized cap: write nothing and hand back the count so the
|
|
1973
|
+
* caller sizes and retries (the snapshot-face discipline). */
|
|
1974
|
+
if (count > cap) return count;
|
|
1975
|
+
return count;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
LEPTRIS_API int leptris_node_name_compare(uintptr_t node_addr,
|
|
1979
|
+
const char* name, size_t len) {
|
|
1980
|
+
if (!node_addr) return -2;
|
|
1981
|
+
const char* node_name = "";
|
|
1982
|
+
if (leptris_node_get_type((LeptrisNodeRef)node_addr) ==
|
|
1983
|
+
LEPTRIS_NODE_TYPE_ELEMENT) {
|
|
1984
|
+
node_name = leptris_element_get_name((LeptrisElement)node_addr);
|
|
1985
|
+
if (!node_name) node_name = "";
|
|
1986
|
+
}
|
|
1987
|
+
size_t node_len = strlen(node_name);
|
|
1988
|
+
size_t min = node_len < len ? node_len : len;
|
|
1989
|
+
int rc = strncmp(node_name, name ? name : "", min);
|
|
1990
|
+
if (rc != 0) return rc < 0 ? -1 : 1;
|
|
1991
|
+
if (node_len == len) return 0;
|
|
1992
|
+
return node_len < len ? -1 : 1;
|
|
1993
|
+
}
|
|
@@ -340,6 +340,33 @@ LeptrisMemoryPool* leptris_pool_create_with_page_size(size_t page_size) {
|
|
|
340
340
|
return pool;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
int leptris_pool_contains(const LeptrisMemoryPool* pool,
|
|
344
|
+
const void* addr) {
|
|
345
|
+
if (!pool || !addr) return 0;
|
|
346
|
+
for (const MemoryPage* p = pool->first_page; p; p = p->next) {
|
|
347
|
+
if (p->busy_size <= sizeof(struct memory_page)) continue;
|
|
348
|
+
const char* lo = p->data;
|
|
349
|
+
const char* hi =
|
|
350
|
+
p->data + (p->busy_size - sizeof(struct memory_page));
|
|
351
|
+
if ((const char*)addr >= lo && (const char*)addr < hi) return 1;
|
|
352
|
+
}
|
|
353
|
+
/* Post-parse growth (arena extensions, oversized requests) lives
|
|
354
|
+
* in tracked malloc blocks outside the span — still pool-owned,
|
|
355
|
+
* still freed by leptris_pool_destroy. Callers use this predicate
|
|
356
|
+
* to answer "did this pointer die with the document?"
|
|
357
|
+
* (leptris#1866). */
|
|
358
|
+
for (const LeptrisBigAlloc* b = pool->first_big_alloc; b; b = b->next) {
|
|
359
|
+
if ((const char*)addr >= (const char*)b->ptr &&
|
|
360
|
+
(const char*)addr < (const char*)b->ptr + b->size) return 1;
|
|
361
|
+
}
|
|
362
|
+
if (pool->arena && pool->arena->base) {
|
|
363
|
+
if ((const char*)addr >= pool->arena->base &&
|
|
364
|
+
(const char*)addr < pool->arena->base + pool->arena->used)
|
|
365
|
+
return 1;
|
|
366
|
+
}
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
343
370
|
void leptris_pool_destroy(LeptrisMemoryPool* pool) {
|
|
344
371
|
if (!pool) return;
|
|
345
372
|
|
|
@@ -172,6 +172,11 @@ int leptris_pool_is_arena_backed(const LeptrisMemoryPool* pool);
|
|
|
172
172
|
* @param pool Pool to destroy (NULL is safe)
|
|
173
173
|
*/
|
|
174
174
|
void leptris_pool_destroy(LeptrisMemoryPool* pool);
|
|
175
|
+
/* leptris#1866: nonzero when addr lies within the pool's handed-out
|
|
176
|
+
* memory — any page's live span, the arena bump region, or a tracked
|
|
177
|
+
* extension block (freed with the pool like the rest). */
|
|
178
|
+
int leptris_pool_contains(const LeptrisMemoryPool* pool,
|
|
179
|
+
const void* addr);
|
|
175
180
|
|
|
176
181
|
/* Release this thread's recycler pages back to the global pool
|
|
177
182
|
* (leptris_thread_cleanup). */
|
|
@@ -755,6 +755,21 @@ XPathNodeSet* apply_axis(XPathContext* ctx, LeptrisNode* node,
|
|
|
755
755
|
return xpath_nodeset_new();
|
|
756
756
|
}
|
|
757
757
|
|
|
758
|
+
/* Attribute inputs: the parent axis resolves through the OWNER
|
|
759
|
+
* element (XPath 1.0 §2.2 — `./@id/..` names the attribute's
|
|
760
|
+
* element). The element-typed dispatch below would read the
|
|
761
|
+
* parent field in element form off the attribute struct and
|
|
762
|
+
* return empty (leptris#1866, the standoc svg-uniqueids face). */
|
|
763
|
+
if (axis_id == XPATH_AXIS_PARENT && node && node->type == LEPTRIS_NODE_ATTRIBUTE) {
|
|
764
|
+
LeptrisElement owner = ((LeptrisAttributeNode*)node)->owner;
|
|
765
|
+
XPathNodeSet* result = xpath_nodeset_new();
|
|
766
|
+
if (!result) return result;
|
|
767
|
+
if (owner && matches_node_test(ctx, (LeptrisNode*)owner, test)) {
|
|
768
|
+
xpath_nodeset_add(result, owner);
|
|
769
|
+
}
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
772
|
+
|
|
758
773
|
LeptrisElement elem = (LeptrisElement)node;
|
|
759
774
|
switch (axis_id) {
|
|
760
775
|
case XPATH_AXIS_DESCENDANT:
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
void xsd_capture_ics(struct leptris_xsd_schema* s,
|
|
21
21
|
LeptrisElement elem, const char* element_name);
|
|
22
22
|
extern int xsd_simple_valid(const XsdSimple* simple, const char* v,
|
|
23
|
-
const char* terminal_base);
|
|
23
|
+
const char* terminal_base, int chain_is_list);
|
|
24
24
|
extern struct xsd_validator* xsd_validator_new(
|
|
25
25
|
struct leptris_xsd_schema* s);
|
|
26
26
|
extern void xsd_validator_free(struct xsd_validator* v);
|
|
@@ -179,6 +179,7 @@ static void xsd_cm_free(XsdCm* cm) {
|
|
|
179
179
|
free(cm->type);
|
|
180
180
|
free(cm->ns);
|
|
181
181
|
free(cm->ext_base);
|
|
182
|
+
free(cm->ext_base_ns);
|
|
182
183
|
free(cm->any_ns);
|
|
183
184
|
free(cm->not_qname);
|
|
184
185
|
free(cm->any_unit_tns);
|
|
@@ -292,6 +293,9 @@ static XsdCm* xsd_cm_parse(struct leptris_xsd_schema* s,
|
|
|
292
293
|
cm->process_skip =
|
|
293
294
|
(pc && (strcmp(pc, "skip") == 0 ||
|
|
294
295
|
strcmp(pc, "lax") == 0)) ? 1 : 0;
|
|
296
|
+
cm->pc_level =
|
|
297
|
+
(pc && strcmp(pc, "skip") == 0) ? 2
|
|
298
|
+
: (pc && strcmp(pc, "lax") == 0) ? 1 : 0;
|
|
295
299
|
}
|
|
296
300
|
} else {
|
|
297
301
|
continue; /* annotation & friends */
|
|
@@ -565,6 +569,25 @@ static int xsd_item_valid(struct leptris_xsd_schema* s,
|
|
|
565
569
|
return 1;
|
|
566
570
|
}
|
|
567
571
|
|
|
572
|
+
/* #1882: does the base chain resolve to a LIST type? A restriction
|
|
573
|
+
* OF a list keeps deriv RESTRICT, but its length facets still count
|
|
574
|
+
* items — the value space is the list's. */
|
|
575
|
+
static int xsd_chain_is_list(struct leptris_xsd_schema* s,
|
|
576
|
+
const XsdSimple* t) {
|
|
577
|
+
int depth = 0;
|
|
578
|
+
while (t && depth < 32) {
|
|
579
|
+
if (t->deriv == XSD_SIMPLE_LIST) return 1;
|
|
580
|
+
if (!t->base) return 0;
|
|
581
|
+
if (strncmp(t->base, "xs:", 3) == 0)
|
|
582
|
+
return strcmp(t->base, "xs:IDREFS") == 0 ||
|
|
583
|
+
strcmp(t->base, "xs:NMTOKENS") == 0 ||
|
|
584
|
+
strcmp(t->base, "xs:ENTITIES") == 0;
|
|
585
|
+
t = xsd_find_simple(s, t->base);
|
|
586
|
+
depth++;
|
|
587
|
+
}
|
|
588
|
+
return 0;
|
|
589
|
+
}
|
|
590
|
+
|
|
568
591
|
/* The chain's terminal builtin (NULL = none resolved): the length
|
|
569
592
|
* family needs it to count octets for the binary bases
|
|
570
593
|
* (leptris-ruby#434). */
|
|
@@ -621,7 +644,7 @@ static int xsd_deriv_valid(struct leptris_xsd_schema* s,
|
|
|
621
644
|
return 1;
|
|
622
645
|
return 0;
|
|
623
646
|
}
|
|
624
|
-
return xsd_simple_valid(t, v, xsd_chain_terminal(s, t));
|
|
647
|
+
return xsd_simple_valid(t, v, xsd_chain_terminal(s, t), 0);
|
|
625
648
|
}
|
|
626
649
|
|
|
627
650
|
static int xsd_valid_chain(struct leptris_xsd_schema* s,
|
|
@@ -633,7 +656,8 @@ static int xsd_valid_chain(struct leptris_xsd_schema* s,
|
|
|
633
656
|
}
|
|
634
657
|
if (t->deriv != XSD_SIMPLE_RESTRICT)
|
|
635
658
|
return xsd_deriv_valid(s, t, v, depth);
|
|
636
|
-
return xsd_simple_valid(t, v, xsd_chain_terminal(s, t)
|
|
659
|
+
return xsd_simple_valid(t, v, xsd_chain_terminal(s, t),
|
|
660
|
+
xsd_chain_is_list(s, t));
|
|
637
661
|
}
|
|
638
662
|
|
|
639
663
|
static void xsd_set_error(struct leptris_xsd_schema* s, const char* msg) {
|
|
@@ -2030,7 +2054,13 @@ static void xsd_resolve_extension_bases(struct leptris_xsd_schema* s) {
|
|
|
2030
2054
|
progressed = 0;
|
|
2031
2055
|
for (XsdCm* c = s->complex_types; c; c = c->next) {
|
|
2032
2056
|
if (!c->ext_base) continue;
|
|
2033
|
-
|
|
2057
|
+
/* #1881: the fold resolves in the BASE's namespace —
|
|
2058
|
+
* recorded at capture from the base QName's prefix —
|
|
2059
|
+
* falling back to the deriver's ns (same-unit refs,
|
|
2060
|
+
* where the old behavior lives). */
|
|
2061
|
+
const char* bns = c->ext_base_ns ? c->ext_base_ns : c->ns;
|
|
2062
|
+
XsdCm* base = xsd_find_complex_ns(s, c->ext_base, bns);
|
|
2063
|
+
if (!base) base = xsd_find_complex_ns(s, c->ext_base, c->ns);
|
|
2034
2064
|
if (!base || base->ext_base) continue;
|
|
2035
2065
|
XsdCm* copied = xsd_cm_copy_list(base->first_child);
|
|
2036
2066
|
if (copied) {
|
|
@@ -2050,7 +2080,9 @@ static void xsd_resolve_extension_bases(struct leptris_xsd_schema* s) {
|
|
|
2050
2080
|
}
|
|
2051
2081
|
}
|
|
2052
2082
|
const XsdTypeAttrs* bt =
|
|
2053
|
-
xsd_find_type_attrs_ns(s, c->ext_base,
|
|
2083
|
+
xsd_find_type_attrs_ns(s, c->ext_base, bns);
|
|
2084
|
+
if (!bt)
|
|
2085
|
+
bt = xsd_find_type_attrs_ns(s, c->ext_base, c->ns);
|
|
2054
2086
|
if (bt) {
|
|
2055
2087
|
XsdTypeAttrs* mine = NULL;
|
|
2056
2088
|
for (XsdTypeAttrs* t = s->type_attrs; t; t = t->next)
|
|
@@ -2094,6 +2126,8 @@ static void xsd_resolve_extension_bases(struct leptris_xsd_schema* s) {
|
|
|
2094
2126
|
}
|
|
2095
2127
|
free(c->ext_base);
|
|
2096
2128
|
c->ext_base = NULL;
|
|
2129
|
+
free(c->ext_base_ns);
|
|
2130
|
+
c->ext_base_ns = NULL;
|
|
2097
2131
|
progressed = 1;
|
|
2098
2132
|
}
|
|
2099
2133
|
}
|
|
@@ -2159,8 +2193,43 @@ static void xsd_visited_add(struct leptris_xsd_schema* s,
|
|
|
2159
2193
|
s->visited[s->visited_len++] = dup;
|
|
2160
2194
|
}
|
|
2161
2195
|
|
|
2196
|
+
static XsdAttrDecl* xsd_capture_attr_rows(
|
|
2197
|
+
struct leptris_xsd_schema* s, LeptrisElement container, int depth);
|
|
2198
|
+
/* #1880: top-level attributeGroup definitions are captured in a
|
|
2199
|
+
* PRE-PASS — the symbol space is order-free, but the ref expansion
|
|
2200
|
+
* in xsd_capture_attr_rows looks up s->attr_groups at capture time,
|
|
2201
|
+
* so a group declared AFTER the referencing type used to vanish. */
|
|
2202
|
+
static void xsd_capture_attr_group_def(struct leptris_xsd_schema* s,
|
|
2203
|
+
LeptrisElement n,
|
|
2204
|
+
const char* nm) {
|
|
2205
|
+
XsdAttrGroupDef* g = (XsdAttrGroupDef*)calloc(1, sizeof(*g));
|
|
2206
|
+
if (!g) return;
|
|
2207
|
+
g->name = xsd_strdup(nm);
|
|
2208
|
+
g->attrs = xsd_capture_attr_rows(s, n, 0);
|
|
2209
|
+
g->next = s->attr_groups;
|
|
2210
|
+
s->attr_groups = g;
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2162
2213
|
static void xsd_capture_root(struct leptris_xsd_schema* s,
|
|
2163
2214
|
LeptrisElement root) {
|
|
2215
|
+
/* pre-pass: every top-level attributeGroup def, document order */
|
|
2216
|
+
for (LeptrisNodeRef n =
|
|
2217
|
+
leptris_node_first_child(leptris_element_as_node(root));
|
|
2218
|
+
n; n = leptris_node_next_sibling(n)) {
|
|
2219
|
+
if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
2220
|
+
continue;
|
|
2221
|
+
const char* local = NULL;
|
|
2222
|
+
const char* uri = NULL;
|
|
2223
|
+
leptris_element_expanded_name((LeptrisElement)n, &local,
|
|
2224
|
+
NULL, &uri);
|
|
2225
|
+
if (!local || strcmp(local, "attributeGroup") != 0 ||
|
|
2226
|
+
!uri || strcmp(uri, XSD_NS) != 0)
|
|
2227
|
+
continue;
|
|
2228
|
+
const char* nm = leptris_element_attribute((LeptrisElement)n,
|
|
2229
|
+
"name");
|
|
2230
|
+
if (!nm) continue;
|
|
2231
|
+
xsd_capture_attr_group_def(s, (LeptrisElement)n, nm);
|
|
2232
|
+
}
|
|
2164
2233
|
for (LeptrisNodeRef n =
|
|
2165
2234
|
leptris_node_first_child(leptris_element_as_node(root));
|
|
2166
2235
|
n; n = leptris_node_next_sibling(n)) {
|
|
@@ -2371,16 +2440,13 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
|
|
|
2371
2440
|
} else if (strcmp(local, "attributeGroup") == 0 && uri && !strcmp(uri, XSD_NS)) {
|
|
2372
2441
|
const char* nm =
|
|
2373
2442
|
leptris_element_attribute((LeptrisElement)n, "name");
|
|
2443
|
+
/* the #1880 pre-pass already captured it — a second
|
|
2444
|
+
* registration would shadow the first */
|
|
2374
2445
|
if (nm) {
|
|
2375
|
-
XsdAttrGroupDef*
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
g->attrs = xsd_capture_attr_rows(
|
|
2380
|
-
s, (LeptrisElement)n, 0);
|
|
2381
|
-
g->next = s->attr_groups;
|
|
2382
|
-
s->attr_groups = g;
|
|
2383
|
-
}
|
|
2446
|
+
XsdAttrGroupDef* seen = s->attr_groups;
|
|
2447
|
+
for (; seen; seen = seen->next)
|
|
2448
|
+
if (strcmp(seen->name, nm) == 0) break;
|
|
2449
|
+
if (!seen) xsd_capture_attr_group_def(s, (LeptrisElement)n, nm);
|
|
2384
2450
|
}
|
|
2385
2451
|
} else if (strcmp(local, "group") == 0 && uri && !strcmp(uri, XSD_NS)) {
|
|
2386
2452
|
const char* nm =
|
|
@@ -2569,6 +2635,7 @@ static XsdCm* xsd_cm_copy_list(const XsdCm* head) {
|
|
|
2569
2635
|
d->any_ns = xsd_strdup(src->any_ns);
|
|
2570
2636
|
d->any_unit_tns = xsd_strdup(src->any_unit_tns);
|
|
2571
2637
|
d->process_skip = src->process_skip;
|
|
2638
|
+
d->pc_level = src->pc_level;
|
|
2572
2639
|
d->min = src->min;
|
|
2573
2640
|
d->max = src->max;
|
|
2574
2641
|
d->first_child = xsd_cm_copy_list(src->first_child);
|
|
@@ -2662,6 +2729,8 @@ static XsdCm* xsd_capture_any(LeptrisElement any_child) {
|
|
|
2662
2729
|
(pc && (strcmp(pc, "skip") == 0 || strcmp(pc, "lax") == 0))
|
|
2663
2730
|
? 1
|
|
2664
2731
|
: 0;
|
|
2732
|
+
w->pc_level = (pc && strcmp(pc, "skip") == 0) ? 2
|
|
2733
|
+
: (pc && strcmp(pc, "lax") == 0) ? 1 : 0;
|
|
2665
2734
|
w->min = 0;
|
|
2666
2735
|
w->max = -1;
|
|
2667
2736
|
return w;
|
|
@@ -2746,7 +2815,21 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
2746
2815
|
continue;
|
|
2747
2816
|
const char* base_raw =
|
|
2748
2817
|
leptris_element_attribute(dq, "base");
|
|
2749
|
-
|
|
2818
|
+
/* #1881: resolve the base QName against the
|
|
2819
|
+
* extension element's in-scope declarations — the
|
|
2820
|
+
* resolved namespace rides the deferral so the
|
|
2821
|
+
* post-capture fold can cross units (g:foo1 extends
|
|
2822
|
+
* f:foo). */
|
|
2823
|
+
char buri[256], blocal[256];
|
|
2824
|
+
buri[0] = 0;
|
|
2825
|
+
blocal[0] = 0;
|
|
2826
|
+
if (base_raw)
|
|
2827
|
+
xsd_resolve_qname(dq, base_raw, buri, sizeof(buri),
|
|
2828
|
+
blocal, sizeof(blocal));
|
|
2829
|
+
const char* base =
|
|
2830
|
+
base_raw ? (blocal[0] ? blocal
|
|
2831
|
+
: xsd_local_name(base_raw))
|
|
2832
|
+
: NULL;
|
|
2750
2833
|
XsdCm* base_root =
|
|
2751
2834
|
(base && strncmp(base, "xs:", 3) != 0)
|
|
2752
2835
|
? xsd_find_complex_ns(s, base, s->target_ns)
|
|
@@ -2771,6 +2854,9 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
2771
2854
|
strcmp(dl, "extension") == 0) {
|
|
2772
2855
|
free(root->ext_base);
|
|
2773
2856
|
root->ext_base = xsd_strdup(base);
|
|
2857
|
+
free(root->ext_base_ns);
|
|
2858
|
+
root->ext_base_ns =
|
|
2859
|
+
buri[0] ? xsd_strdup(buri) : NULL;
|
|
2774
2860
|
}
|
|
2775
2861
|
if (strcmp(dl, "extension") == 0) {
|
|
2776
2862
|
/* base content first, derived content after */
|
|
@@ -3578,3 +3664,134 @@ LEPTRIS_API const char* leptris_xsd_error(LeptrisXsdSchema schema) {
|
|
|
3578
3664
|
if (!schema) return NULL;
|
|
3579
3665
|
return ((struct leptris_xsd_schema*)schema)->error;
|
|
3580
3666
|
}
|
|
3667
|
+
|
|
3668
|
+
/* leptris#1881: schema-set compilation — every listed document's
|
|
3669
|
+
* declarations merge into ONE schema. The per-document pipeline is
|
|
3670
|
+
* the single-compile one (checks, per-unit tns, capture); the
|
|
3671
|
+
* visited-set is seeded with every member path first so an import
|
|
3672
|
+
* chain back to a set member does not recapture it, and extension
|
|
3673
|
+
* bases resolve ONCE after all units are in (type references
|
|
3674
|
+
* resolve against the whole schema, the leptris-ruby#440 law). */
|
|
3675
|
+
LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile_set(
|
|
3676
|
+
const char* const* paths, size_t count, LeptrisStatus* status) {
|
|
3677
|
+
if (status) *status = LEPTRIS_OK;
|
|
3678
|
+
if (!paths || count == 0) {
|
|
3679
|
+
if (status) *status = LEPTRIS_ERROR_PARSE;
|
|
3680
|
+
return NULL;
|
|
3681
|
+
}
|
|
3682
|
+
struct leptris_xsd_schema* s =
|
|
3683
|
+
(struct leptris_xsd_schema*)calloc(1, sizeof(*s));
|
|
3684
|
+
if (!s) {
|
|
3685
|
+
if (status) *status = LEPTRIS_ERROR_MEMORY;
|
|
3686
|
+
return NULL;
|
|
3687
|
+
}
|
|
3688
|
+
const char* main_tns = NULL;
|
|
3689
|
+
for (size_t i = 0; i < count; i++) {
|
|
3690
|
+
const char* path = paths[i];
|
|
3691
|
+
if (!path || !*path) {
|
|
3692
|
+
xsd_set_error(s, "schema set member path is empty");
|
|
3693
|
+
if (status) *status = LEPTRIS_ERROR_PARSE;
|
|
3694
|
+
return s;
|
|
3695
|
+
}
|
|
3696
|
+
size_t len = 0;
|
|
3697
|
+
char* text = xsd_read_file(path, &len);
|
|
3698
|
+
if (!text) {
|
|
3699
|
+
xsd_set_error(s, "schema set member cannot be read");
|
|
3700
|
+
if (status) *status = LEPTRIS_ERROR_PARSE;
|
|
3701
|
+
return s;
|
|
3702
|
+
}
|
|
3703
|
+
const char* slash = strrchr(path, '/');
|
|
3704
|
+
const char* bslash = strrchr(path, '\\');
|
|
3705
|
+
if (bslash && (!slash || bslash > slash)) slash = bslash;
|
|
3706
|
+
char* base_dir = NULL;
|
|
3707
|
+
if (slash) {
|
|
3708
|
+
size_t n = (size_t)(slash - path);
|
|
3709
|
+
if (n == 0) n = 1;
|
|
3710
|
+
base_dir = (char*)malloc(n + 1);
|
|
3711
|
+
if (base_dir) {
|
|
3712
|
+
memcpy(base_dir, path, n);
|
|
3713
|
+
base_dir[n] = 0;
|
|
3714
|
+
}
|
|
3715
|
+
} else {
|
|
3716
|
+
base_dir = xsd_strdup(".");
|
|
3717
|
+
}
|
|
3718
|
+
|
|
3719
|
+
LeptrisDocument doc =
|
|
3720
|
+
leptris_parse_string(text, len, status);
|
|
3721
|
+
free(text);
|
|
3722
|
+
if (!doc) {
|
|
3723
|
+
xsd_set_error(s,
|
|
3724
|
+
"schema set member is not well-formed XML");
|
|
3725
|
+
free(base_dir);
|
|
3726
|
+
if (status) *status = LEPTRIS_ERROR_PARSE;
|
|
3727
|
+
return s;
|
|
3728
|
+
}
|
|
3729
|
+
LeptrisElement root = leptris_document_root(doc);
|
|
3730
|
+
if (!root) {
|
|
3731
|
+
xsd_set_error(s, "schema set member has no root element");
|
|
3732
|
+
leptris_document_free(doc);
|
|
3733
|
+
free(base_dir);
|
|
3734
|
+
if (status) *status = LEPTRIS_ERROR_PARSE;
|
|
3735
|
+
return s;
|
|
3736
|
+
}
|
|
3737
|
+
const char* local = NULL;
|
|
3738
|
+
const char* uri = NULL;
|
|
3739
|
+
leptris_element_expanded_name(root, &local, NULL, &uri);
|
|
3740
|
+
if (!local || strcmp(local, "schema") != 0 ||
|
|
3741
|
+
(uri && strcmp(uri, XSD_NS) != 0)) {
|
|
3742
|
+
xsd_set_error(s, "schema set member root is not xs:schema");
|
|
3743
|
+
leptris_document_free(doc);
|
|
3744
|
+
free(base_dir);
|
|
3745
|
+
if (status) *status = LEPTRIS_ERROR_PARSE;
|
|
3746
|
+
return s;
|
|
3747
|
+
}
|
|
3748
|
+
const char* doc_tns =
|
|
3749
|
+
leptris_element_attribute(root, "targetNamespace");
|
|
3750
|
+
char* saved = s->base_dir;
|
|
3751
|
+
s->base_dir = base_dir;
|
|
3752
|
+
if (i == 0) {
|
|
3753
|
+
main_tns = doc_tns;
|
|
3754
|
+
s->target_ns = xsd_strdup(doc_tns);
|
|
3755
|
+
}
|
|
3756
|
+
/* Per-unit tns during checks + capture; the visited-set
|
|
3757
|
+
* seeds each member so import chains close (the import in
|
|
3758
|
+
* g.xsd would otherwise re-capture f.xsd mid-set). */
|
|
3759
|
+
{
|
|
3760
|
+
char* saved_ns = s->target_ns;
|
|
3761
|
+
s->target_ns = xsd_strdup(doc_tns);
|
|
3762
|
+
char canon[4096];
|
|
3763
|
+
xsd_path_normalize(path, canon, sizeof(canon));
|
|
3764
|
+
/* An earlier member's import may have captured this
|
|
3765
|
+
* document already (order [importer, imported]) — the
|
|
3766
|
+
* visited check keeps each unit to one capture. */
|
|
3767
|
+
if (xsd_visited_has(s, canon)) {
|
|
3768
|
+
free(s->target_ns);
|
|
3769
|
+
s->target_ns = saved_ns;
|
|
3770
|
+
leptris_document_free(doc);
|
|
3771
|
+
free(base_dir);
|
|
3772
|
+
s->base_dir = saved;
|
|
3773
|
+
continue;
|
|
3774
|
+
}
|
|
3775
|
+
xsd_visited_add(s, canon);
|
|
3776
|
+
int bad = xsd_check_structure(s, root, 0) != 0 ||
|
|
3777
|
+
xsd_check_derivation(s, root) != 0 ||
|
|
3778
|
+
xsd_check_refs(s, root) != 0 ||
|
|
3779
|
+
xsd_check_upa(s, root) != 0 ||
|
|
3780
|
+
xsd_check_include_tns(s, root) != 0;
|
|
3781
|
+
if (!bad) xsd_capture_root(s, root);
|
|
3782
|
+
free(s->target_ns);
|
|
3783
|
+
s->target_ns = saved_ns;
|
|
3784
|
+
if (bad) {
|
|
3785
|
+
leptris_document_free(doc);
|
|
3786
|
+
free(base_dir);
|
|
3787
|
+
if (status) *status = LEPTRIS_ERROR_PARSE;
|
|
3788
|
+
return s;
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
leptris_document_free(doc);
|
|
3792
|
+
free(base_dir);
|
|
3793
|
+
s->base_dir = saved;
|
|
3794
|
+
}
|
|
3795
|
+
xsd_resolve_extension_bases(s);
|
|
3796
|
+
return s;
|
|
3797
|
+
}
|