leptris 1.9.334.1-ppc64le-linux → 1.9.336.0-ppc64le-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/CHANGELOG.md +14 -0
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +46 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/leptris/descriptor/plan.c +15 -5
- data/vendor-src/libleptris/src/leptris/flat/direct_parse.c +116 -5
- data/vendor-src/libleptris/src/leptris/flat/il_scan.h +10 -2
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +63 -21
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +48 -4
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +9 -3
- data/vendor-src/libleptris/test/parser/test_parser.cpp +30 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +125 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 67a961b3def69a2e6249c91dfecbb9d501456233269b090d6069ae1630ced027
|
|
4
|
+
data.tar.gz: 6fb3c1105222a431ad17b63beb80a2bdbe014775f279abbd7d842ee85ac06136
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4ec23a47d3dbc3860229d16225135569a7219d3b5b6338665fe260a793b6c94a7e906d87e3fceb481d961a04d880139a202af3373777ebaa6055e1b50b828f7
|
|
7
|
+
data.tar.gz: 94c877a350d646348dfcef4cd1159dda8b237b8e4d197380f3746509af70ba55a824c0259eaeb190485f0fbd56adaaf11a47f9eed07a8fd350714e93ed6ed46b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [1.9.335.0] - 2026-10-11
|
|
2
|
+
|
|
3
|
+
### Fixed — engine sync (libleptris 1.9.335)
|
|
4
|
+
|
|
5
|
+
- **The #414 wave (#1626, engine PR #1627)**: cross-namespace
|
|
6
|
+
`xs:import` declarations merge under a compiling schema's own
|
|
7
|
+
targetNamespace (the wml-2010 chain shape — previously invisible),
|
|
8
|
+
prefixed QName type references (`type="w:CT_Border"`) resolve
|
|
9
|
+
with `xs:`/`xsd:` builtin spellings canonized (closing a silent
|
|
10
|
+
gap where `xsd:`-spelled types were untyped), and content-model
|
|
11
|
+
failures NAME the offending child once (`(at 'right')`) — a
|
|
12
|
+
misordered child no longer cascades anonymously down the
|
|
13
|
+
ancestors. All three pinned with minimal cases.
|
|
14
|
+
|
|
1
15
|
## [1.9.334.1] - 2026-10-10
|
|
2
16
|
|
|
3
17
|
### Performance
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
8
8
|
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
9
|
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
10
|
# CHANGELOG when libleptris releases.
|
|
11
|
-
LIBLEPTRIS_VERSION = "1.9.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.9.336"
|
|
12
12
|
# Vendored alongside libleptris for fn:normalize-unicode (TODO
|
|
13
13
|
# .restructure/20): built per platform with a RELOCATABLE @rpath
|
|
14
14
|
# install name, loaded by ffi.rb before libleptris so the
|
data/lib/leptris/version.rb
CHANGED
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.336] - 2026-10-10
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **plan_result_build members emit in op order on every platform
|
|
8
|
+
(leptris#1625, PR #1629).** The serializer orders children by
|
|
9
|
+
byte position with an unstable qsort; builder-created values
|
|
10
|
+
carry no position, so the tie fell to unspecified libc behavior
|
|
11
|
+
and collection members reversed on Windows (both x64 and
|
|
12
|
+
arm64). Builder values now take a dense insertion rank before
|
|
13
|
+
push — fixing an off-by-one in the element branch besides — and
|
|
14
|
+
the comparator breaks position ties on it, making the order
|
|
15
|
+
total and platform-independent. Walk output is undisturbed (the
|
|
16
|
+
walk's own ranks are dense already). The binding's round-trip
|
|
17
|
+
spec can flip to strict member order once a release carries
|
|
18
|
+
this.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## [1.9.335] - 2026-10-10
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **XSD: cross-namespace imports, prefixed type refs, named
|
|
27
|
+
content errors (#1626, PR #1627).** `xs:import` with a foreign
|
|
28
|
+
targetNamespace merges its declarations — the previous gate
|
|
29
|
+
skipped the normal multi-namespace shape (wml-2010 imports were
|
|
30
|
+
invisible when the compiling schema carried its own
|
|
31
|
+
targetNamespace). Prefixed QName references
|
|
32
|
+
(`type="w:CT_Border"`) resolve by local name at every capture
|
|
33
|
+
site, and the `xs:`/`xsd:` builtin spellings canonize —
|
|
34
|
+
closing a pre-existing gap where `xsd:`-spelled types were
|
|
35
|
+
silently untyped. Content-model failures name the offending
|
|
36
|
+
child once (`children do not match the content model
|
|
37
|
+
(at 'insideH')`) via a greedy expected-next match with
|
|
38
|
+
`minOccurs="0"` optionals skippable — one misordered child no
|
|
39
|
+
longer cascades anonymously down the ancestors (the reported
|
|
40
|
+
~175x amplification is a bounded single report).
|
|
41
|
+
- **interleaved lane: compute record depths before sizing the
|
|
42
|
+
replay stacks (leptris#1577, PR #1624).** The sizing scan read
|
|
43
|
+
the still-zero-filled depth array, bound the replay to the
|
|
44
|
+
in-struct 256-slot buffers, and every document deeper than
|
|
45
|
+
DP_MAX_DEPTH overflowed them under a raised cap.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
3
49
|
## [1.9.334] - 2026-10-10
|
|
4
50
|
|
|
5
51
|
### Added
|
|
@@ -1344,6 +1344,11 @@ static int dp_kid_pos_cmp(const void* a, const void* b) {
|
|
|
1344
1344
|
(const struct leptris_plan_result* const*)b;
|
|
1345
1345
|
if ((*ka)->position < (*kb)->position) return -1;
|
|
1346
1346
|
if ((*ka)->position > (*kb)->position) return 1;
|
|
1347
|
+
/* #1625: position ties (builder nodes, offset-less rows) fall
|
|
1348
|
+
* to the dense insertion rank - qsort is not stable, and an
|
|
1349
|
+
* unspecified tie reordered members per platform libc. */
|
|
1350
|
+
if ((*ka)->order_index < (*kb)->order_index) return -1;
|
|
1351
|
+
if ((*ka)->order_index > (*kb)->order_index) return 1;
|
|
1347
1352
|
return 0;
|
|
1348
1353
|
}
|
|
1349
1354
|
|
|
@@ -1586,12 +1591,16 @@ LEPTRIS_API LeptrisPlanResult leptris_plan_result_build(
|
|
|
1586
1591
|
return NULL;
|
|
1587
1592
|
}
|
|
1588
1593
|
root = v;
|
|
1589
|
-
} else
|
|
1590
|
-
|
|
1591
|
-
|
|
1594
|
+
} else {
|
|
1595
|
+
/* rank BEFORE push (kid_count is post-increment) -
|
|
1596
|
+
* every value needs one: the serializer's qsort
|
|
1597
|
+
* breaks position ties on it (#1625) */
|
|
1598
|
+
v->order_index = (uint32_t)stack[sp - 1]->kid_count;
|
|
1599
|
+
if (!dp_value_push(stack[sp - 1], v)) {
|
|
1600
|
+
dp_result_free_rec(v);
|
|
1601
|
+
goto oom;
|
|
1602
|
+
}
|
|
1592
1603
|
}
|
|
1593
|
-
v->order_index = (uint32_t)
|
|
1594
|
-
(sp == 0 ? 0 : stack[sp - 1]->kid_count);
|
|
1595
1604
|
stack[sp++] = v;
|
|
1596
1605
|
} else if (op->kind == LEPTRIS_PLAN_OP_SCALAR) {
|
|
1597
1606
|
if (sp == 0) {
|
|
@@ -1609,6 +1618,7 @@ LEPTRIS_API LeptrisPlanResult leptris_plan_result_build(
|
|
|
1609
1618
|
if (!dp_value_set_str(v, op->value ? op->value : "",
|
|
1610
1619
|
op->value ? op->value_len : 0))
|
|
1611
1620
|
dp_value_set_str(v, "", 0);
|
|
1621
|
+
v->order_index = (uint32_t)stack[sp - 1]->kid_count;
|
|
1612
1622
|
if (!dp_value_push(stack[sp - 1], v)) {
|
|
1613
1623
|
dp_result_free_rec(v);
|
|
1614
1624
|
goto oom;
|
|
@@ -64,15 +64,26 @@ typedef struct {
|
|
|
64
64
|
char* end;
|
|
65
65
|
LeptrisMemoryPool* pool;
|
|
66
66
|
struct leptris_document* doc;
|
|
67
|
-
LeptrisElement
|
|
67
|
+
LeptrisElement open_stack_buf[DP_MAX_DEPTH];
|
|
68
68
|
/* Round 10: journal slot of each open element's elem_pos entry —
|
|
69
69
|
* the close path updates by slot (O(1)) instead of hashing. */
|
|
70
|
-
int
|
|
70
|
+
int pos_slot_stack_buf[DP_MAX_DEPTH];
|
|
71
71
|
/* TODO 155 Phase C: per-depth last-child cache. Replaces the
|
|
72
72
|
* last_child_off field on element. last_child_stack[i] holds
|
|
73
73
|
* the most recently wired child of open_stack[i], or NULL when
|
|
74
74
|
* no child has been wired yet at that depth. */
|
|
75
|
-
LeptrisNode*
|
|
75
|
+
LeptrisNode* last_child_stack_buf[DP_MAX_DEPTH];
|
|
76
|
+
/* Depth-stack indirection (leptris#1577): the in-struct buffers
|
|
77
|
+
* serve the default DP_MAX_DEPTH cap; a configured max depth
|
|
78
|
+
* above that (public leptris_set_max_depth / parse options)
|
|
79
|
+
* switches the pointers to one heap block sized for the cap.
|
|
80
|
+
* The open-tag guard honors the configured depth — with the
|
|
81
|
+
* arrays fixed at 256, any deeper document wrote past them. */
|
|
82
|
+
LeptrisElement* open_stack;
|
|
83
|
+
int* pos_slot_stack;
|
|
84
|
+
LeptrisNode** last_child_stack;
|
|
85
|
+
size_t stack_cap;
|
|
86
|
+
void* stack_heap;
|
|
76
87
|
int depth;
|
|
77
88
|
LeptrisElement root;
|
|
78
89
|
/* Document children (issue #580): [prolog..., root, epilog...]
|
|
@@ -195,6 +206,36 @@ typedef struct {
|
|
|
195
206
|
LeptrisDTD* dtd;
|
|
196
207
|
} DParser;
|
|
197
208
|
|
|
209
|
+
/* Depth stacks (leptris#1577): the default cap stays on the in-struct
|
|
210
|
+
* buffers (zero allocation); a larger configured cap gets one
|
|
211
|
+
* aligned heap block. Returns 0 on OOM (pointers left NULL). */
|
|
212
|
+
static int dp_stacks_init(DParser* p, size_t cap) {
|
|
213
|
+
if (cap <= DP_MAX_DEPTH) {
|
|
214
|
+
p->open_stack = p->open_stack_buf;
|
|
215
|
+
p->pos_slot_stack = p->pos_slot_stack_buf;
|
|
216
|
+
p->last_child_stack = p->last_child_stack_buf;
|
|
217
|
+
p->stack_cap = DP_MAX_DEPTH;
|
|
218
|
+
p->stack_heap = NULL;
|
|
219
|
+
return 1;
|
|
220
|
+
}
|
|
221
|
+
size_t a = (cap * sizeof(LeptrisElement) + 15u) & ~(size_t)15u;
|
|
222
|
+
size_t b = (cap * sizeof(int) + 15u) & ~(size_t)15u;
|
|
223
|
+
size_t c = cap * sizeof(LeptrisNode*);
|
|
224
|
+
unsigned char* blk = (unsigned char*)malloc(a + b + c);
|
|
225
|
+
if (!blk) return 0;
|
|
226
|
+
p->open_stack = (LeptrisElement*)blk;
|
|
227
|
+
p->pos_slot_stack = (int*)(blk + a);
|
|
228
|
+
p->last_child_stack = (LeptrisNode**)(blk + a + b);
|
|
229
|
+
p->stack_cap = cap;
|
|
230
|
+
p->stack_heap = blk;
|
|
231
|
+
return 1;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static void dp_stacks_release(DParser* p) {
|
|
235
|
+
free(p->stack_heap);
|
|
236
|
+
p->stack_heap = NULL;
|
|
237
|
+
}
|
|
238
|
+
|
|
198
239
|
/* ---- #1125: immutable-buffer logs --------------------------------
|
|
199
240
|
*
|
|
200
241
|
* The zero-copy parse NUL-terminates names/values in place. To keep
|
|
@@ -1551,6 +1592,12 @@ static struct leptris_document* direct_parse_internal(char* buf, size_t len,
|
|
|
1551
1592
|
p.end = buf + len;
|
|
1552
1593
|
p.pool = pool;
|
|
1553
1594
|
p.doc = doc;
|
|
1595
|
+
{
|
|
1596
|
+
extern LEPTRIS_THREAD_LOCAL int g_leptris_max_depth;
|
|
1597
|
+
size_t dcap = g_leptris_max_depth > 0
|
|
1598
|
+
? (size_t)g_leptris_max_depth : (size_t)DP_MAX_DEPTH;
|
|
1599
|
+
if (!dp_stacks_init(&p, dcap)) goto fail;
|
|
1600
|
+
}
|
|
1554
1601
|
p.skip_flags = skip_flags;
|
|
1555
1602
|
p.depth = 0;
|
|
1556
1603
|
p.root = NULL;
|
|
@@ -2311,6 +2358,7 @@ static struct leptris_document* direct_parse_internal(char* buf, size_t len,
|
|
|
2311
2358
|
* ends the parse byte-identical to the input. (Runs after the
|
|
2312
2359
|
* encoding/version strdups above, which read the buffer.) */
|
|
2313
2360
|
|
|
2361
|
+
dp_stacks_release(&p);
|
|
2314
2362
|
return doc;
|
|
2315
2363
|
|
|
2316
2364
|
fail:
|
|
@@ -2372,6 +2420,7 @@ fail:
|
|
|
2372
2420
|
* pool_destroy above. Don't LEPTRIS_FREE(doc) (TODO 154). */
|
|
2373
2421
|
/* Round 8: scratch/pristine are arena-carved — pool_destroy
|
|
2374
2422
|
* above already reclaimed them. */
|
|
2423
|
+
dp_stacks_release(&p);
|
|
2375
2424
|
return NULL;
|
|
2376
2425
|
}
|
|
2377
2426
|
|
|
@@ -2446,7 +2495,35 @@ static LEPTRIS_ALWAYS_INLINE size_t il_name_end(const char* s,
|
|
|
2446
2495
|
return (size_t)(p - s);
|
|
2447
2496
|
}
|
|
2448
2497
|
|
|
2498
|
+
/* leptris#1577: the IL scan's depth stacks sized to the configured
|
|
2499
|
+
* max depth (fixed 256 before — same overflow class as the classic
|
|
2500
|
+
* lane). The wrapper owns the block so every body exit is covered. */
|
|
2501
|
+
static int il_stacks_init(IlCtx* c, size_t cap) {
|
|
2502
|
+
if (cap <= LEPTRIS_IL_MAX_DEPTH) {
|
|
2503
|
+
c->open = c->open_buf;
|
|
2504
|
+
c->last_child = c->last_child_buf;
|
|
2505
|
+
c->stack_heap = NULL;
|
|
2506
|
+
return 1;
|
|
2507
|
+
}
|
|
2508
|
+
unsigned char* blk = (unsigned char*)malloc(
|
|
2509
|
+
2u * cap * sizeof(uint32_t));
|
|
2510
|
+
if (!blk) return 0;
|
|
2511
|
+
c->open = (uint32_t*)blk;
|
|
2512
|
+
c->last_child = (uint32_t*)(blk + cap * sizeof(uint32_t));
|
|
2513
|
+
c->stack_heap = blk;
|
|
2514
|
+
return 1;
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
static void il_stacks_release(IlCtx* c) {
|
|
2518
|
+
free(c->stack_heap);
|
|
2519
|
+
c->stack_heap = NULL;
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2449
2522
|
int leptris_il_scan(char* s, size_t len, int drop_ws, IlCtx* c,
|
|
2523
|
+
uint32_t* root_out, size_t* trail_off,
|
|
2524
|
+
size_t* trail_len);
|
|
2525
|
+
|
|
2526
|
+
static int il_scan_body(char* s, size_t len, int drop_ws, IlCtx* c,
|
|
2450
2527
|
uint32_t* root_out, size_t* trail_off,
|
|
2451
2528
|
size_t* trail_len) {
|
|
2452
2529
|
const char* send = s + len;
|
|
@@ -2682,6 +2759,19 @@ int leptris_il_scan(char* s, size_t len, int drop_ws, IlCtx* c,
|
|
|
2682
2759
|
}
|
|
2683
2760
|
}
|
|
2684
2761
|
|
|
2762
|
+
int leptris_il_scan(char* s, size_t len, int drop_ws, IlCtx* c,
|
|
2763
|
+
uint32_t* root_out, size_t* trail_off,
|
|
2764
|
+
size_t* trail_len) {
|
|
2765
|
+
extern LEPTRIS_THREAD_LOCAL int g_leptris_max_depth;
|
|
2766
|
+
size_t dcap = g_leptris_max_depth > 0
|
|
2767
|
+
? (size_t)g_leptris_max_depth : (size_t)LEPTRIS_IL_MAX_DEPTH;
|
|
2768
|
+
if (!il_stacks_init(c, dcap)) return 0;
|
|
2769
|
+
int ok = il_scan_body(s, len, drop_ws, c, root_out, trail_off,
|
|
2770
|
+
trail_len);
|
|
2771
|
+
il_stacks_release(c);
|
|
2772
|
+
return ok;
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2685
2775
|
|
|
2686
2776
|
/* Post-pass: build the public tree from the streams, replaying the
|
|
2687
2777
|
* classic lane's exact stores. Returns NULL on OOM (caller frees). */
|
|
@@ -2791,11 +2881,28 @@ static struct leptris_document* dp_il_build(
|
|
|
2791
2881
|
if (!smap || !rdepth || !lc) {
|
|
2792
2882
|
free(smap); free(rdepth); free(lc); goto oom_pool;
|
|
2793
2883
|
}
|
|
2884
|
+
/* Depth-stack indirection (leptris#1577): record depths are not
|
|
2885
|
+
* bounded by DP_MAX_DEPTH here — the replay indexes
|
|
2886
|
+
* last_child_stack by them. Size the stacks to the document. */
|
|
2887
|
+
/* Depth computation FIRST: the sizing scan below reads
|
|
2888
|
+
* rdepth — computed here, before the max is taken (the sizing
|
|
2889
|
+
* pass originally ran on the zero-filled array and sized the
|
|
2890
|
+
* stacks to 1, so every deep replay overflowed the in-struct
|
|
2891
|
+
* buffers — the exact class this PR fixes). */
|
|
2892
|
+
for (size_t i = 0; i < c->nrec; i++) {
|
|
2893
|
+
rdepth[i] = (c->recs[i].parent == IL_NONE)
|
|
2894
|
+
? 0 : rdepth[c->recs[i].parent] + 1;
|
|
2895
|
+
}
|
|
2896
|
+
uint32_t il_max_d = 0;
|
|
2897
|
+
for (size_t i = 0; i < c->nrec; i++) {
|
|
2898
|
+
if (rdepth[i] > il_max_d) il_max_d = rdepth[i];
|
|
2899
|
+
}
|
|
2900
|
+
if (!dp_stacks_init(&pp, (size_t)il_max_d + 1)) {
|
|
2901
|
+
free(smap); free(rdepth); free(lc); goto oom_pool;
|
|
2902
|
+
}
|
|
2794
2903
|
|
|
2795
2904
|
for (size_t i = 0; i < c->nrec; i++) {
|
|
2796
2905
|
IlRec* r = &c->recs[i];
|
|
2797
|
-
rdepth[i] = (r->parent == IL_NONE)
|
|
2798
|
-
? 0 : rdepth[r->parent] + 1;
|
|
2799
2906
|
if (r->kind == 0) {
|
|
2800
2907
|
LeptrisElement e = &eblk[nelem++];
|
|
2801
2908
|
e->base.type = LEPTRIS_NODE_TYPE_ELEMENT;
|
|
@@ -3015,10 +3122,14 @@ static struct leptris_document* dp_il_build(
|
|
|
3015
3122
|
}
|
|
3016
3123
|
|
|
3017
3124
|
leptris_root_doc_register(doc->root, doc);
|
|
3125
|
+
dp_stacks_release(&pp);
|
|
3018
3126
|
free(smap); free(rdepth); free(lc);
|
|
3019
3127
|
return doc;
|
|
3020
3128
|
|
|
3021
3129
|
oom_pool:
|
|
3130
|
+
/* Reached only at/after the replay's DParser init — the init
|
|
3131
|
+
* failure path leaves stack_heap NULL, so the release is safe. */
|
|
3132
|
+
dp_stacks_release(&pp);
|
|
3022
3133
|
leptris_pool_destroy(pool);
|
|
3023
3134
|
free(scratch);
|
|
3024
3135
|
free(pristine);
|
|
@@ -43,8 +43,16 @@ typedef struct IlAttr {
|
|
|
43
43
|
typedef struct IlCtx {
|
|
44
44
|
IlRec* recs; size_t nrec, crec;
|
|
45
45
|
IlAttr* attrs; size_t nattr, cattr;
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
/* Depth stacks (leptris#1577): the fixed buffers serve the
|
|
47
|
+
* default 256 cap; a configured max depth above that switches
|
|
48
|
+
* the pointers to one heap block. Owned by leptris_il_scan —
|
|
49
|
+
* initialized at entry, released at every exit; callers never
|
|
50
|
+
* touch these directly. */
|
|
51
|
+
uint32_t open_buf[LEPTRIS_IL_MAX_DEPTH];
|
|
52
|
+
uint32_t last_child_buf[LEPTRIS_IL_MAX_DEPTH];
|
|
53
|
+
uint32_t* open;
|
|
54
|
+
uint32_t* last_child;
|
|
55
|
+
void* stack_heap;
|
|
48
56
|
int depth;
|
|
49
57
|
} IlCtx;
|
|
50
58
|
|
|
@@ -88,6 +88,37 @@ static XsdAssert* xsd_capture_assert(struct leptris_xsd_schema* s,
|
|
|
88
88
|
LeptrisElement e);
|
|
89
89
|
static XsdCm* xsd_capture_any(LeptrisElement any_child);
|
|
90
90
|
static char* xsd_capture_any_attr_ns(LeptrisElement container);
|
|
91
|
+
|
|
92
|
+
/* #1626: prefixed QName refs (type="w:CT_Border") resolve by local
|
|
93
|
+
* name — the engine's tables key on local names; the prefix maps
|
|
94
|
+
* to a namespace the name-based resolution folds away. Returns the
|
|
95
|
+
* local part after the colon when present, else the argument. */
|
|
96
|
+
static char xsd_qname_buf[8][512];
|
|
97
|
+
static int xsd_qname_slot;
|
|
98
|
+
|
|
99
|
+
static const char* xsd_local_name(const char* qname) {
|
|
100
|
+
if (!qname) return NULL;
|
|
101
|
+
const char* colon = strchr(qname, ':');
|
|
102
|
+
if (!colon) return qname;
|
|
103
|
+
/* xs:/xsd: are builtin references — canonize to "xs:local" so
|
|
104
|
+
* text_valid's dispatch matches both spellings (xsd:-spelled
|
|
105
|
+
* types were silently untyped before #1626). */
|
|
106
|
+
size_t pl = (size_t)(colon - qname);
|
|
107
|
+
if ((pl == 2 && strncmp(qname, "xs", 2) == 0) ||
|
|
108
|
+
(pl == 3 && strncmp(qname, "xsd", 3) == 0)) {
|
|
109
|
+
if (pl == 2) return qname;
|
|
110
|
+
char* dst = xsd_qname_buf[xsd_qname_slot++ & 7];
|
|
111
|
+
dst[0] = 'x';
|
|
112
|
+
dst[1] = 's';
|
|
113
|
+
dst[2] = ':';
|
|
114
|
+
snprintf(dst + 3, sizeof(xsd_qname_buf[0]) - 3, "%s",
|
|
115
|
+
colon + 1);
|
|
116
|
+
return dst;
|
|
117
|
+
}
|
|
118
|
+
/* a foreign prefix maps to a namespace; the name-based tables
|
|
119
|
+
* resolve by local name */
|
|
120
|
+
return colon + 1;
|
|
121
|
+
}
|
|
91
122
|
static void xsd_capture_alternatives(struct leptris_xsd_schema* s,
|
|
92
123
|
LeptrisElement decl_elem,
|
|
93
124
|
XsdElementDecl* d);
|
|
@@ -193,12 +224,14 @@ static XsdCm* xsd_cm_parse(struct leptris_xsd_schema* s,
|
|
|
193
224
|
if (cm) {
|
|
194
225
|
const char* nm =
|
|
195
226
|
leptris_element_attribute(e, "name");
|
|
196
|
-
if (!nm)
|
|
227
|
+
if (!nm)
|
|
228
|
+
nm = xsd_local_name(
|
|
229
|
+
leptris_element_attribute(e, "ref"));
|
|
197
230
|
cm->name = xsd_strdup(nm);
|
|
198
231
|
const char* pty =
|
|
199
232
|
leptris_element_attribute(e, "type");
|
|
200
233
|
if (pty) {
|
|
201
|
-
cm->type = xsd_strdup(pty);
|
|
234
|
+
cm->type = xsd_strdup(xsd_local_name(pty));
|
|
202
235
|
} else if (s && nm) {
|
|
203
236
|
/* #1592: inline anonymous type on the particle */
|
|
204
237
|
char synth[512];
|
|
@@ -212,8 +245,8 @@ static XsdCm* xsd_cm_parse(struct leptris_xsd_schema* s,
|
|
|
212
245
|
* forward references need no capture order */
|
|
213
246
|
cm = xsd_cm_new(XSD_CM_GROUP_REF);
|
|
214
247
|
if (cm)
|
|
215
|
-
cm->name = xsd_strdup(
|
|
216
|
-
leptris_element_attribute(e, "ref"));
|
|
248
|
+
cm->name = xsd_strdup(xsd_local_name(
|
|
249
|
+
leptris_element_attribute(e, "ref")));
|
|
217
250
|
} else if (strcmp(local, "any") == 0) {
|
|
218
251
|
cm = xsd_cm_new(XSD_CM_ANY);
|
|
219
252
|
if (cm) {
|
|
@@ -273,7 +306,8 @@ static XsdSimple* xsd_capture_simple(LeptrisElement st) {
|
|
|
273
306
|
if (!local) continue;
|
|
274
307
|
if (strcmp(local, "list") == 0) {
|
|
275
308
|
simple->deriv = XSD_SIMPLE_LIST;
|
|
276
|
-
const char* it =
|
|
309
|
+
const char* it = xsd_local_name(
|
|
310
|
+
leptris_element_attribute(e, "itemType"));
|
|
277
311
|
if (it && *it) {
|
|
278
312
|
simple->item_type = xsd_strdup(it);
|
|
279
313
|
} else {
|
|
@@ -346,8 +380,8 @@ static XsdSimple* xsd_capture_simple(LeptrisElement st) {
|
|
|
346
380
|
simple->member_defs[simple->member_def_count++] = def;
|
|
347
381
|
}
|
|
348
382
|
} else if (strcmp(local, "restriction") == 0) {
|
|
349
|
-
simple->base =
|
|
350
|
-
|
|
383
|
+
simple->base = xsd_strdup(xsd_local_name(
|
|
384
|
+
leptris_element_attribute(e, "base")));
|
|
351
385
|
for (LeptrisNodeRef f = leptris_node_first_child(
|
|
352
386
|
leptris_element_as_node(e));
|
|
353
387
|
f; f = leptris_node_next_sibling(f)) {
|
|
@@ -694,9 +728,16 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
|
|
|
694
728
|
/* same-namespace merges and no-namespace
|
|
695
729
|
* chameleon includes; foreign-namespace
|
|
696
730
|
* imports stay lax at this tier */
|
|
731
|
+
/* xs:include: same-namespace (or
|
|
732
|
+
* chameleon — the no-tns form adopts ours).
|
|
733
|
+
* xs:import: foreign namespaces are the
|
|
734
|
+
* point — merge regardless (#1626: wml-2010
|
|
735
|
+
* shape imports were skipped when the
|
|
736
|
+
* compiler had its own targetNamespace). */
|
|
737
|
+
int same_ns = subroot && (!tns || !s->target_ns ||
|
|
738
|
+
strcmp(tns, s->target_ns) == 0);
|
|
697
739
|
if (subroot &&
|
|
698
|
-
(
|
|
699
|
-
strcmp(tns, s->target_ns) == 0)) {
|
|
740
|
+
(strcmp(local, "import") == 0 || same_ns)) {
|
|
700
741
|
s->include_depth++;
|
|
701
742
|
xsd_capture_root(s, subroot);
|
|
702
743
|
s->include_depth--;
|
|
@@ -719,7 +760,7 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
|
|
|
719
760
|
(XsdElementDecl*)calloc(1, sizeof(*d));
|
|
720
761
|
if (d) {
|
|
721
762
|
d->name = xsd_strdup(nm);
|
|
722
|
-
d->type = xsd_strdup(ty);
|
|
763
|
+
d->type = xsd_strdup(xsd_local_name(ty));
|
|
723
764
|
xsd_capture_alternatives(s, (LeptrisElement)n, d);
|
|
724
765
|
d->fixed = xsd_strdup(
|
|
725
766
|
leptris_element_attribute((LeptrisElement)n,
|
|
@@ -789,9 +830,9 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
|
|
|
789
830
|
XsdAttrDecl* d = (XsdAttrDecl*)calloc(1, sizeof(*d));
|
|
790
831
|
if (d) {
|
|
791
832
|
d->name = xsd_strdup(nm);
|
|
792
|
-
d->type = xsd_strdup(
|
|
833
|
+
d->type = xsd_strdup(xsd_local_name(
|
|
793
834
|
leptris_element_attribute((LeptrisElement)n,
|
|
794
|
-
"type"));
|
|
835
|
+
"type")));
|
|
795
836
|
d->fixed = xsd_strdup(
|
|
796
837
|
leptris_element_attribute((LeptrisElement)n,
|
|
797
838
|
"fixed"));
|
|
@@ -1141,8 +1182,9 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
1141
1182
|
if (!dl || (strcmp(dl, "extension") != 0 &&
|
|
1142
1183
|
strcmp(dl, "restriction") != 0))
|
|
1143
1184
|
continue;
|
|
1144
|
-
const char*
|
|
1185
|
+
const char* base_raw =
|
|
1145
1186
|
leptris_element_attribute(dq, "base");
|
|
1187
|
+
const char* base = xsd_local_name(base_raw);
|
|
1146
1188
|
XsdCm* base_root =
|
|
1147
1189
|
(base && strncmp(base, "xs:", 3) != 0)
|
|
1148
1190
|
? xsd_find_complex(s, base)
|
|
@@ -1199,8 +1241,8 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
1199
1241
|
if (!dl || (strcmp(dl, "extension") != 0 &&
|
|
1200
1242
|
strcmp(dl, "restriction") != 0))
|
|
1201
1243
|
continue;
|
|
1202
|
-
const char* base =
|
|
1203
|
-
leptris_element_attribute(dq, "base");
|
|
1244
|
+
const char* base = xsd_local_name(
|
|
1245
|
+
leptris_element_attribute(dq, "base"));
|
|
1204
1246
|
if (base) {
|
|
1205
1247
|
if (strncmp(base, "xs:", 3) == 0) {
|
|
1206
1248
|
root->text_type = xsd_strdup(base);
|
|
@@ -1434,8 +1476,8 @@ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
|
|
|
1434
1476
|
if (!d) continue;
|
|
1435
1477
|
if (an) {
|
|
1436
1478
|
d->name = xsd_strdup(an);
|
|
1437
|
-
d->type = xsd_strdup(
|
|
1438
|
-
leptris_element_attribute(ae, "type"));
|
|
1479
|
+
d->type = xsd_strdup(xsd_local_name(
|
|
1480
|
+
leptris_element_attribute(ae, "type")));
|
|
1439
1481
|
if (!d->type)
|
|
1440
1482
|
d->type = xsd_strdup(
|
|
1441
1483
|
xsd_inline_attr_type(s, ae, an));
|
|
@@ -1444,8 +1486,8 @@ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
|
|
|
1444
1486
|
d->required = (use && strcmp(use, "required") == 0);
|
|
1445
1487
|
} else {
|
|
1446
1488
|
/* ref to a top-level attribute declaration */
|
|
1447
|
-
const char* ref =
|
|
1448
|
-
leptris_element_attribute(ae, "ref");
|
|
1489
|
+
const char* ref = xsd_local_name(
|
|
1490
|
+
leptris_element_attribute(ae, "ref"));
|
|
1449
1491
|
const XsdAttrDecl* src =
|
|
1450
1492
|
ref ? xsd_find_top_attr(s, ref) : NULL;
|
|
1451
1493
|
if (!src) {
|
|
@@ -1465,8 +1507,8 @@ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
|
|
|
1465
1507
|
head = d;
|
|
1466
1508
|
tail = d;
|
|
1467
1509
|
} else if (strcmp(al, "attributeGroup") == 0) {
|
|
1468
|
-
const char* ref =
|
|
1469
|
-
leptris_element_attribute(ae, "ref");
|
|
1510
|
+
const char* ref = xsd_local_name(
|
|
1511
|
+
leptris_element_attribute(ae, "ref"));
|
|
1470
1512
|
if (!ref) continue;
|
|
1471
1513
|
XsdAttrGroupDef* g = s->attr_groups;
|
|
1472
1514
|
for (; g; g = g->next)
|
|
@@ -208,12 +208,56 @@ static void validate_children(struct xsd_validator* v,
|
|
|
208
208
|
}
|
|
209
209
|
int ok = xsd_content_valid(s, model, xsd_target_ns(s), names, uris,
|
|
210
210
|
n);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
if (ok) {
|
|
212
|
+
free(names);
|
|
213
|
+
free(uris);
|
|
214
|
+
} else {
|
|
215
|
+
/* Name the culprit: libxml2 names the offending child once
|
|
216
|
+
* and recovers; the unnamed form cascaded ambiguously
|
|
217
|
+
* (#1626 — 353 errors for one misordered pair). Greedy
|
|
218
|
+
* linear match against the sequence's particle list;
|
|
219
|
+
* non-sequence models keep the generic message. */
|
|
220
|
+
const char* culprit = NULL;
|
|
221
|
+
if (model->kind == XSD_CM_SEQ) {
|
|
222
|
+
XsdCm* parts[256];
|
|
223
|
+
size_t j = 0;
|
|
224
|
+
for (XsdCm* q = model->first_child; q && j < 256;
|
|
225
|
+
q = q->next)
|
|
226
|
+
parts[j++] = q;
|
|
227
|
+
size_t pj = 0;
|
|
228
|
+
for (size_t k2 = 0; k2 < n && !culprit; k2++) {
|
|
229
|
+
const char* uri = uris ? uris[k2] : NULL;
|
|
230
|
+
int matched = 0;
|
|
231
|
+
while (pj < j) {
|
|
232
|
+
XsdCm* q = parts[pj];
|
|
233
|
+
if (q->name && strcmp(q->name, names[k2]) == 0 &&
|
|
234
|
+
(!q->ns ||
|
|
235
|
+
(uri && strcmp(uri, q->ns) == 0))) {
|
|
236
|
+
matched = 1;
|
|
237
|
+
pj++;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
if (q->min == 0) {
|
|
241
|
+
pj++; /* skippable optional: not expected here */
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
culprit = names[k2]; /* expected q, found this */
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
if (!matched && !culprit) culprit = names[k2];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
214
250
|
const char* local = NULL;
|
|
215
251
|
leptris_element_expanded_name(elem, &local, NULL, NULL);
|
|
216
|
-
|
|
252
|
+
char msg[256];
|
|
253
|
+
snprintf(msg, sizeof(msg),
|
|
254
|
+
culprit
|
|
255
|
+
? "children do not match the content model (at '%s')"
|
|
256
|
+
: "children do not match the content model",
|
|
257
|
+
culprit ? culprit : "");
|
|
258
|
+
verrf(v, "element", local, msg);
|
|
259
|
+
free(names);
|
|
260
|
+
free(uris);
|
|
217
261
|
}
|
|
218
262
|
/* recurse: a global declaration governs the child when one
|
|
219
263
|
* exists; otherwise the parent's particle declaration does
|
|
@@ -2163,15 +2163,21 @@ TEST(Plan408, ResultBuildFeedsSerialize) {
|
|
|
2163
2163
|
leptris_plan_row_op ops[] = {
|
|
2164
2164
|
{LEPTRIS_PLAN_OP_ELEMENT, 0, UINT32_MAX, NULL, NULL, 0},
|
|
2165
2165
|
{LEPTRIS_PLAN_OP_ATTR, 0, UINT32_MAX, "id", "7", 1},
|
|
2166
|
-
{LEPTRIS_PLAN_OP_SCALAR, 0, 0, NULL, "
|
|
2166
|
+
{LEPTRIS_PLAN_OP_SCALAR, 0, 0, NULL, "alpha", 5},
|
|
2167
|
+
{LEPTRIS_PLAN_OP_SCALAR, 0, 0, NULL, "beta", 4},
|
|
2168
|
+
{LEPTRIS_PLAN_OP_SCALAR, 0, 0, NULL, "gamma", 5},
|
|
2167
2169
|
{LEPTRIS_PLAN_OP_END, 0, UINT32_MAX, NULL, NULL, 0},
|
|
2168
2170
|
};
|
|
2169
2171
|
LeptrisPlanResult r =
|
|
2170
|
-
leptris_plan_result_build(plan, ops,
|
|
2172
|
+
leptris_plan_result_build(plan, ops, 6, &st);
|
|
2171
2173
|
ASSERT_NE(r, nullptr);
|
|
2172
2174
|
char* out = leptris_plan_serialize(plan, r, &st);
|
|
2173
2175
|
ASSERT_NE(out, nullptr);
|
|
2174
|
-
|
|
2176
|
+
/* #1625: multi-member order is the OP order on every platform
|
|
2177
|
+
* (position ties fell to unstable qsort and reversed members
|
|
2178
|
+
* on Windows) */
|
|
2179
|
+
EXPECT_STREQ(out,
|
|
2180
|
+
"<b id=\"7\"><p>alpha</p><p>beta</p><p>gamma</p></b>");
|
|
2175
2181
|
leptris_free_string(out);
|
|
2176
2182
|
leptris_plan_result_free(r);
|
|
2177
2183
|
leptris_plan_free(plan);
|
|
@@ -233,6 +233,36 @@ TEST(ParserConfigurableDepth, LowerCapRejectsShallowerNesting) {
|
|
|
233
233
|
leptris_set_max_depth(0);
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/* leptris#1577: the open-tag guard honors the configured max depth
|
|
237
|
+
* while the parser's depth stacks were fixed at DP_MAX_DEPTH (256) —
|
|
238
|
+
* any document deeper than 256 under a raised cap wrote past the
|
|
239
|
+
* stacks (layout-dependent state corruption). The stacks now size to
|
|
240
|
+
* the cap; a 600-deep document must parse and walk cleanly. */
|
|
241
|
+
TEST(ParserConfigurableDepth, DeepDocumentUnderRaisedCap) {
|
|
242
|
+
leptris_set_max_depth(1024);
|
|
243
|
+
std::string xml;
|
|
244
|
+
for (int i = 0; i < 600; i++) xml += "<d>";
|
|
245
|
+
xml += 'x';
|
|
246
|
+
for (int i = 0; i < 600; i++) xml += "</d>";
|
|
247
|
+
|
|
248
|
+
LeptrisStatus st;
|
|
249
|
+
LeptrisDocument doc = leptris_parse_string(xml.data(), xml.size(), &st);
|
|
250
|
+
ASSERT_NE(doc, nullptr);
|
|
251
|
+
LeptrisElement root = leptris_document_root(doc);
|
|
252
|
+
ASSERT_NE(root, nullptr);
|
|
253
|
+
|
|
254
|
+
/* Walk to the spine bottom: 600 opens must survive intact. */
|
|
255
|
+
int depth_seen = 0;
|
|
256
|
+
LeptrisElement e = root;
|
|
257
|
+
while (e) {
|
|
258
|
+
depth_seen++;
|
|
259
|
+
e = leptris_element_first_child_any(e);
|
|
260
|
+
}
|
|
261
|
+
EXPECT_EQ(depth_seen, 600); /* the spine: 600 nested <d> */
|
|
262
|
+
leptris_document_free(doc);
|
|
263
|
+
leptris_set_max_depth(0);
|
|
264
|
+
}
|
|
265
|
+
|
|
236
266
|
TEST(ParserConfigurableDepth, GetReturnsEffectiveValue) {
|
|
237
267
|
leptris_set_max_depth(0);
|
|
238
268
|
EXPECT_EQ(leptris_get_max_depth(), 256);
|
|
@@ -1499,3 +1499,128 @@ TEST(XsdValidate, UndeclaredAttributeIsStrictUnlessAnyAttribute) {
|
|
|
1499
1499
|
}
|
|
1500
1500
|
|
|
1501
1501
|
} // namespace
|
|
1502
|
+
|
|
1503
|
+
// ---- #1626: cross-ns imports, prefixed refs, error cascade -----
|
|
1504
|
+
namespace {
|
|
1505
|
+
|
|
1506
|
+
/* 1. cross-namespace xs:import registers its declarations when the
|
|
1507
|
+
* compiling schema carries its own targetNamespace (the wml-2010
|
|
1508
|
+
* shape). Chameleon/same-ns worked; foreign imports were skipped. */
|
|
1509
|
+
TEST(XsdImport1626, CrossNamespaceImportRegisters) {
|
|
1510
|
+
std::string dir = ::testing::TempDir();
|
|
1511
|
+
FILE* f1 = fopen((dir + "x6shared.xsd").c_str(), "wb");
|
|
1512
|
+
ASSERT_NE(f1, nullptr);
|
|
1513
|
+
const char* shared =
|
|
1514
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
|
|
1515
|
+
" xmlns:w=\"urn:shared\""
|
|
1516
|
+
" targetNamespace=\"urn:shared\">"
|
|
1517
|
+
"<xs:complexType name=\"CT_Border\">"
|
|
1518
|
+
"<xs:attribute name=\"val\" type=\"xs:token\"/>"
|
|
1519
|
+
"</xs:complexType>"
|
|
1520
|
+
"</xs:schema>";
|
|
1521
|
+
fwrite(shared, 1, strlen(shared), f1);
|
|
1522
|
+
fclose(f1);
|
|
1523
|
+
FILE* f2 = fopen((dir + "x6main.xsd").c_str(), "wb");
|
|
1524
|
+
ASSERT_NE(f2, nullptr);
|
|
1525
|
+
/* the importing schema has ITS OWN target namespace and
|
|
1526
|
+
* references the imported type through the shared prefix */
|
|
1527
|
+
const char* mainx =
|
|
1528
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
|
|
1529
|
+
" xmlns:w=\"urn:shared\""
|
|
1530
|
+
" targetNamespace=\"urn:main\">"
|
|
1531
|
+
"<xs:import namespace=\"urn:shared\""
|
|
1532
|
+
" schemaLocation=\"x6shared.xsd\"/>"
|
|
1533
|
+
"<xs:element name=\"b\">"
|
|
1534
|
+
"<xs:complexType>"
|
|
1535
|
+
"<xs:attribute name=\"id\" type=\"xs:integer\"/>"
|
|
1536
|
+
"<xs:attribute name=\"bd\" type=\"w:CT_Border\"/>"
|
|
1537
|
+
"</xs:complexType>"
|
|
1538
|
+
"</xs:element>"
|
|
1539
|
+
"</xs:schema>";
|
|
1540
|
+
fwrite(mainx, 1, strlen(mainx), f2);
|
|
1541
|
+
fclose(f2);
|
|
1542
|
+
|
|
1543
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
1544
|
+
LeptrisXsdSchema s =
|
|
1545
|
+
leptris_xsd_compile_file((dir + "x6main.xsd").c_str(), &st);
|
|
1546
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0);
|
|
1547
|
+
/* the imported type resolves: the attribute types */
|
|
1548
|
+
LeptrisDocument d = leptris_parse_string(
|
|
1549
|
+
"<b id=\"1\" bd=\"x\"/>", strlen("<b id=\"1\" bd=\"x\"/>"), &st);
|
|
1550
|
+
ASSERT_NE(d, nullptr);
|
|
1551
|
+
EXPECT_EQ(leptris_xsd_validate(s, d), 1);
|
|
1552
|
+
leptris_document_free(d);
|
|
1553
|
+
leptris_xsd_free(s);
|
|
1554
|
+
remove((dir + "x6shared.xsd").c_str());
|
|
1555
|
+
remove((dir + "x6main.xsd").c_str());
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
/* 3. prefixed type refs resolve by local name (uniword's
|
|
1559
|
+
* default-xmlns spelling always worked; the w: spelling did not) */
|
|
1560
|
+
TEST(XsdImport1626, PrefixedTypeRefsResolve) {
|
|
1561
|
+
const char* x =
|
|
1562
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
|
|
1563
|
+
" xmlns:w=\"urn:x\" targetNamespace=\"urn:x\">"
|
|
1564
|
+
"<xs:complexType name=\"CT_Border\">"
|
|
1565
|
+
"<xs:attribute name=\"v\" type=\"xs:integer\"/>"
|
|
1566
|
+
"</xs:complexType>"
|
|
1567
|
+
"<xs:complexType name=\"CT_Wrap\">"
|
|
1568
|
+
"<xs:sequence>"
|
|
1569
|
+
"<xs:element name=\"b\" type=\"w:CT_Border\"/>"
|
|
1570
|
+
"</xs:sequence>"
|
|
1571
|
+
"</xs:complexType>"
|
|
1572
|
+
"<xs:element name=\"r\" type=\"w:CT_Wrap\"/>"
|
|
1573
|
+
"</xs:schema>";
|
|
1574
|
+
EXPECT_EQ(validate_doc(x, "<r><b v=\"1\"/></r>"), 1);
|
|
1575
|
+
EXPECT_EQ(validate_doc(x, "<r><b v=\"z\"/></r>"), 0);
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
/* 2. error amplification: one misordered child reports ONCE, with
|
|
1579
|
+
* the offending child named — not once per ancestor (~175x) */
|
|
1580
|
+
TEST(XsdImport1626, MisorderReportsOnceNamed) {
|
|
1581
|
+
const char* x =
|
|
1582
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
1583
|
+
"<xs:complexType name=\"CT_TblBorders\">"
|
|
1584
|
+
"<xs:sequence>"
|
|
1585
|
+
"<xs:element name=\"top\" type=\"xs:token\"/>"
|
|
1586
|
+
"<xs:element name=\"right\" type=\"xs:token\"/>"
|
|
1587
|
+
"<xs:element name=\"insideH\" type=\"xs:token\"/>"
|
|
1588
|
+
"</xs:sequence>"
|
|
1589
|
+
"</xs:complexType>"
|
|
1590
|
+
"<xs:complexType name=\"CT_TblPr\">"
|
|
1591
|
+
"<xs:sequence>"
|
|
1592
|
+
"<xs:element name=\"borders\" type=\"CT_TblBorders\"/>"
|
|
1593
|
+
"<xs:element name=\"width\" type=\"xs:integer\"/>"
|
|
1594
|
+
"</xs:sequence>"
|
|
1595
|
+
"</xs:complexType>"
|
|
1596
|
+
"<xs:complexType name=\"CT_Tbl\">"
|
|
1597
|
+
"<xs:sequence>"
|
|
1598
|
+
"<xs:element name=\"tblPr\" type=\"CT_TblPr\"/>"
|
|
1599
|
+
"<xs:element name=\"row\" type=\"xs:token\"/>"
|
|
1600
|
+
"</xs:sequence>"
|
|
1601
|
+
"</xs:complexType>"
|
|
1602
|
+
"<xs:element name=\"tbl\" type=\"CT_Tbl\"/>"
|
|
1603
|
+
"</xs:schema>";
|
|
1604
|
+
/* insideH before right: the classic wml minimal case */
|
|
1605
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
1606
|
+
LeptrisXsdSchema s = leptris_xsd_compile(x, strlen(x), &st);
|
|
1607
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0);
|
|
1608
|
+
const char* bad =
|
|
1609
|
+
"<tbl><tblPr><borders><top/><insideH/><right/>"
|
|
1610
|
+
"</borders><width>5</width></tblPr><row/></tbl>";
|
|
1611
|
+
LeptrisDocument d = leptris_parse_string(bad, strlen(bad), &st);
|
|
1612
|
+
ASSERT_NE(d, nullptr);
|
|
1613
|
+
EXPECT_EQ(leptris_xsd_validate(s, d), 0);
|
|
1614
|
+
size_t n = leptris_xsd_error_count(s);
|
|
1615
|
+
EXPECT_LE(n, 3u) << "amplified: " << n << " errors";
|
|
1616
|
+
if (n > 0) {
|
|
1617
|
+
const char* e = leptris_xsd_error_at(s, 0);
|
|
1618
|
+
EXPECT_NE(e, nullptr);
|
|
1619
|
+
EXPECT_NE(strstr(e, "insideH"), nullptr)
|
|
1620
|
+
<< "first error names the offending child: " << e;
|
|
1621
|
+
}
|
|
1622
|
+
leptris_document_free(d);
|
|
1623
|
+
leptris_xsd_free(s);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
} // namespace
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
|
|
3
3
|
"name": "leptris",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.336",
|
|
5
5
|
"description": "High-performance XML parser and XPath 1.0 engine in C (libleptris)",
|
|
6
6
|
"homepage": "https://github.com/leptris/leptris",
|
|
7
7
|
"license": "MIT",
|