leptris 1.9.318.0 → 1.9.320.0
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 +48 -0
- data/Rakefile +1 -1
- data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/ppc64le-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/s390x-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/ffi.rb +9 -0
- data/lib/leptris/xml/xsd.rb +32 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +57 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/CMakeLists.txt +1 -0
- data/vendor-src/libleptris/src/include/leptris.h +11 -0
- data/vendor-src/libleptris/src/leptris/descriptor/plan.c +35 -3
- data/vendor-src/libleptris/src/leptris/rng/rng_internal.h +8 -0
- data/vendor-src/libleptris/src/leptris/rng/rng_regex.c +4 -0
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +153 -2
- data/vendor-src/libleptris/src/leptris/xsd/datatypes.c +445 -0
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +43 -0
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +168 -0
- data/vendor-src/libleptris/test/rng/test_rng.cpp +15 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +112 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7e309fd6444a641db0b0f24f92aef96097860678c006a217a783724c499b7f50
|
|
4
|
+
data.tar.gz: d041122c9d4d1b366549b4dd2d81c8e6c8ee226cc8ddee62d30a6f9a7a582458
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae5b672a25949b41161419054c7ba635e2d234a5ea635a195ad5002d2aeaff4a4c18c3b0e98af5c23cb09c1fc7cec6967e4e866ea4b96c44031e0422fe1545fd
|
|
7
|
+
data.tar.gz: 6f454b0ad8bc92f31196fca9eb28369118cfe9ed81a7ccff3563a9dd903345ff145cdcb9549bcb6efc7d087718f143e1338704f4d6fdd63face059b74369f97f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
## [1.9.320.0] - 2026-10-08
|
|
2
|
+
|
|
3
|
+
### Fixed — engine sync (libleptris 1.9.320)
|
|
4
|
+
|
|
5
|
+
- **Exact-URI child rows survive build-anchor GC (#1585)**: the
|
|
6
|
+
engine's plan build let child-row `ns_uri` ride a pointer into
|
|
7
|
+
the caller's build buffers instead of deep-copying — bindings
|
|
8
|
+
GC their anchors after build, so exact-URI child rows
|
|
9
|
+
deterministically stopped matching (uniword's
|
|
10
|
+
dc:title/dc:creator/dc:description hydrated nil). The engine
|
|
11
|
+
retains the URI; pinned with build + GC + walk.
|
|
12
|
+
- **Plain attribute rows match any qualification (#1586)**:
|
|
13
|
+
ns-unset attr rows try the exact wire spelling first, then any
|
|
14
|
+
qualification by local name (#758 parity) — plain rows bind
|
|
15
|
+
`w:name`/`w:val` exactly as the interpretive path does, top
|
|
16
|
+
level and nested; exact-URI rows keep their #1486 precedence.
|
|
17
|
+
Pinned at both depths.
|
|
18
|
+
|
|
19
|
+
## [1.9.319.0] - 2026-10-08
|
|
20
|
+
|
|
21
|
+
### Added — engine sync (libleptris 1.9.319)
|
|
22
|
+
|
|
23
|
+
- **XSD lexical validation, slice 2 (#1075)**:
|
|
24
|
+
`XSD.builtin_valid?` covers the tier-1 built-in table (string
|
|
25
|
+
family, token, boolean, decimal/integer ranges, float/double,
|
|
26
|
+
date/time with bounds, duration, hex/base64Binary) by the
|
|
27
|
+
"xs:NAME" reference spelling; `Schema#simple_valid?` resolves
|
|
28
|
+
user simpleTypes through cycle-guarded restriction chains
|
|
29
|
+
(pattern / enumeration / min-max / length / digits facets).
|
|
30
|
+
The C face's -1 (not-in-table / unknown type) raises — a silent
|
|
31
|
+
false would let misspelled types validate nothing forever.
|
|
32
|
+
(Slice 1's compile surface rode in the parallel 1.9.318.0.)
|
|
33
|
+
|
|
34
|
+
### Fixed — engine sync (1.9.319)
|
|
35
|
+
|
|
36
|
+
- **RELAX NG exact `{n}` quantifiers (PR #1583)**: the regex
|
|
37
|
+
engine's no-comma branch never set the hi bound — `[A-Z]{3}`
|
|
38
|
+
matched `ABCD`, so any schema using exact bounds silently
|
|
39
|
+
over-accepted. Pinned through the binding's datatype path
|
|
40
|
+
(accepts ABC, rejects ABCD).
|
|
41
|
+
|
|
42
|
+
### Performance — engine sync (1.9.318)
|
|
43
|
+
|
|
44
|
+
- Per-thread small-span arena recycle (#1436): the tiny-doc
|
|
45
|
+
create/destroy row drops ~15% (183→159 ns/doc quiet-hardware) —
|
|
46
|
+
engine-internal; the binding inherits it on every
|
|
47
|
+
Document.create cycle.
|
|
48
|
+
|
|
1
49
|
## [1.9.317.1] - 2026-10-08
|
|
2
50
|
|
|
3
51
|
### Fixed
|
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.320"
|
|
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -1295,6 +1295,15 @@ attach_function :leptris_parse_string,
|
|
|
1295
1295
|
[:leptris_xsd_schema], :size_t
|
|
1296
1296
|
attach_function :leptris_xsd_error,
|
|
1297
1297
|
[:leptris_xsd_schema], UTF8_STRING
|
|
1298
|
+
# Slice 2 (libleptris 1.9.319, #1075): lexical validation.
|
|
1299
|
+
# Built-ins take the "xs:NAME" reference spelling; -1 = not
|
|
1300
|
+
# in the tier-1 table. simple_valid resolves a user
|
|
1301
|
+
# simpleType by local name through cycle-guarded restriction
|
|
1302
|
+
# chains; -1 = unknown type.
|
|
1303
|
+
attach_function :leptris_xsd_builtin_valid,
|
|
1304
|
+
[:string, :string], :int
|
|
1305
|
+
attach_function :leptris_xsd_simple_valid,
|
|
1306
|
+
[:leptris_xsd_schema, :string, :string], :int
|
|
1298
1307
|
|
|
1299
1308
|
attach_function :leptris_rng_parse,
|
|
1300
1309
|
[:string, :size_t, :pointer], :leptris_relaxng
|
data/lib/leptris/xml/xsd.rb
CHANGED
|
@@ -9,6 +9,22 @@ module Leptris::XML::XSD
|
|
|
9
9
|
Schema.compile(schema_xml)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
# Slice 2 (libleptris 1.9.319, #1075): built-in lexical table.
|
|
13
|
+
# +builtin+ takes the "xs:NAME" reference spelling. Returns
|
|
14
|
+
# true/false; an unknown table name raises — a silent false
|
|
15
|
+
# would let misspelled types validate nothing forever.
|
|
16
|
+
def builtin_valid?(builtin, lexical)
|
|
17
|
+
r = Leptris::XML::FFI.leptris_xsd_builtin_valid(
|
|
18
|
+
builtin.to_s, lexical.to_s)
|
|
19
|
+
case r
|
|
20
|
+
when 1 then true
|
|
21
|
+
when 0 then false
|
|
22
|
+
else
|
|
23
|
+
raise ArgumentError,
|
|
24
|
+
"#{builtin.inspect} is not in the tier-1 built-in table"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
12
28
|
# A compiled XSD schema (libleptris >= 1.9.318, #1075 tier-1):
|
|
13
29
|
# the compilation surface — a declaration census and schema-level
|
|
14
30
|
# error reporting. Validation is a later tier and not exposed yet.
|
|
@@ -57,5 +73,21 @@ module Leptris::XML::XSD
|
|
|
57
73
|
msg = Leptris::XML::FFI.leptris_xsd_error(@handle)
|
|
58
74
|
msg.nil? || msg.empty? ? nil : msg
|
|
59
75
|
end
|
|
76
|
+
|
|
77
|
+
# Slice 2 (libleptris 1.9.319, #1075): lexical validation of
|
|
78
|
+
# +lexical+ against the user simpleType +type_name+ (local
|
|
79
|
+
# name) — restriction chains validate every hop's facets,
|
|
80
|
+
# cycle-guarded. Unknown type names raise (the -1 contract).
|
|
81
|
+
def simple_valid?(type_name, lexical)
|
|
82
|
+
r = Leptris::XML::FFI.leptris_xsd_simple_valid(
|
|
83
|
+
@handle, type_name.to_s, lexical.to_s)
|
|
84
|
+
case r
|
|
85
|
+
when 1 then true
|
|
86
|
+
when 0 then false
|
|
87
|
+
else
|
|
88
|
+
raise ArgumentError,
|
|
89
|
+
"#{type_name.inspect} is not a simpleType in this schema"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
60
92
|
end
|
|
61
93
|
end
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.320] - 2026-10-08
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Descriptor: child-row exact-URI `ns_uri` retained at build
|
|
8
|
+
(#1585, PR #1587).** The build deep-copied child `ns_prefix` but
|
|
9
|
+
let `ns_uri` ride the struct copy — a pointer into the caller's
|
|
10
|
+
build buffers. Bindings GC their anchors (a warm walk plus one GC
|
|
11
|
+
makes it deterministic), after which exact-URI child rows
|
|
12
|
+
silently stopped matching and hydration answered nil (uniword's
|
|
13
|
+
`dc:title`/`dc:creator`/`dc:description`). The spec trashes the
|
|
14
|
+
caller's buffer after build and asserts the row still binds.
|
|
15
|
+
- **Descriptor: plain attribute rows match any qualification
|
|
16
|
+
(#1586, PR #1587).** `dp_attr_value`'s ns-unset path now mirrors
|
|
17
|
+
#758 — the exact wire spelling first, then any qualification by
|
|
18
|
+
local name — so plain rows bind `w:name`/`w:val` exactly as the
|
|
19
|
+
interpretive path does, top level and nested. Exact-URI rows keep
|
|
20
|
+
their #1486 precedence. A NESTED row's binding also consults the
|
|
21
|
+
TARGET plan's `ns_lenient` now (the #1115-consistent authority)
|
|
22
|
+
instead of the outer plan's.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## [1.9.319] - 2026-10-08
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **XSD validation, tier 1, slices 1–2 (#1075, PRs #1581/#1583).**
|
|
31
|
+
Slice 1: the `leptris_xsd_compile` surface (xs:schema identity
|
|
32
|
+
under any namespace spelling, top-level declaration enumeration,
|
|
33
|
+
error-carrying failure handles) plus the corpus gate — libxml2's
|
|
34
|
+
own `test/schemas` set (350 files, MIT) vendored at
|
|
35
|
+
`test/xsd/libxml2-cases`, **163/163 fixtures compiling** with a
|
|
36
|
+
pinned monotonic floor. Slice 2: `leptris_xsd_builtin_valid` —
|
|
37
|
+
the tier-1 built-in lexical table (string/Name family, token,
|
|
38
|
+
language, anyURI, QName, boolean, decimal, the integer-derivation
|
|
39
|
+
ranges, float/double, the date/time family with month/day bounds,
|
|
40
|
+
duration, hex/base64Binary) — and `leptris_xsd_simple_valid`:
|
|
41
|
+
named simpleTypes captured at compile (base + pattern /
|
|
42
|
+
enumeration / min-max inclusive-exclusive / length family /
|
|
43
|
+
total-fractionDigits facets), validated through cycle-guarded
|
|
44
|
+
restriction chains where every hop's facets bind.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- **Regex engine: `{n}` EXACT quantifiers parsed open-ended
|
|
49
|
+
(PR #1583).** The no-comma branch never set the hi bound, so
|
|
50
|
+
`[A-Z]{3}` matched `ABCD` — any RELAX NG schema using exact
|
|
51
|
+
quantifier bounds silently accepted longer runs. Root fix in the
|
|
52
|
+
quantifier parser plus a never-again spec on the direct engine
|
|
53
|
+
call; `rng_internal.h` gained C++ guards for test-side calls.
|
|
54
|
+
- Amalgamated single-TU build: the XSD datatype statics now carry
|
|
55
|
+
the `xsd_` prefix (collision with the xpath lexer's same-named
|
|
56
|
+
statics — the #1217 class).
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
3
60
|
## [1.9.318] - 2026-10-08
|
|
4
61
|
|
|
5
62
|
### Performance
|
|
@@ -909,6 +909,17 @@ LEPTRIS_API size_t leptris_xsd_declaration_count(LeptrisXsdSchema schema);
|
|
|
909
909
|
/* Last compile error detail (NULL when the handle is valid).
|
|
910
910
|
* Schema-owned; lives until leptris_xsd_free. */
|
|
911
911
|
LEPTRIS_API const char* leptris_xsd_error(LeptrisXsdSchema schema);
|
|
912
|
+
|
|
913
|
+
/* Slice 2 — lexical validation. Built-ins take the "xs:NAME"
|
|
914
|
+
* reference spelling; -1 = not in the tier-1 table. */
|
|
915
|
+
LEPTRIS_API int leptris_xsd_builtin_valid(const char* builtin,
|
|
916
|
+
const char* lexical);
|
|
917
|
+
/* A user simpleType compiled into the schema, by local name:
|
|
918
|
+
* 1 valid, 0 invalid, -1 unknown type. Restriction chains (local
|
|
919
|
+
* bases) validate every hop's facets, cycle-guarded. */
|
|
920
|
+
LEPTRIS_API int leptris_xsd_simple_valid(LeptrisXsdSchema schema,
|
|
921
|
+
const char* type_name,
|
|
922
|
+
const char* lexical);
|
|
912
923
|
/* Validate an instance document against the parsed schema
|
|
913
924
|
* (phase-2 core subset: element/attribute/text/data/value/
|
|
914
925
|
* choice/group/interleave/repeats/ref). Returns 1 valid; on 0,
|
|
@@ -285,6 +285,7 @@ static void dp_plan_free(LeptrisPlan p) {
|
|
|
285
285
|
for (uint32_t c = 0; c < d->child_count; c++) {
|
|
286
286
|
free((char *)d->child_plans[c].wire_name);
|
|
287
287
|
free((char *)d->child_plans[c].ns_prefix);
|
|
288
|
+
free((char *)d->child_plans[c].ns_uri);
|
|
288
289
|
for (uint16_t pi = 0;
|
|
289
290
|
pi < d->child_plans[c].predicate_count; pi++) {
|
|
290
291
|
free((char*)d->child_plans[c]
|
|
@@ -481,6 +482,15 @@ LEPTRIS_API LeptrisPlan leptris_plan_build(const leptris_plan_spec* spec,
|
|
|
481
482
|
sc->ns_prefix ? dp_strdup(sc->ns_prefix) : NULL;
|
|
482
483
|
if (sc->ns_prefix && !d->child_plans[c].ns_prefix)
|
|
483
484
|
goto oom;
|
|
485
|
+
/* #1585: ns_uri rides the struct copy otherwise —
|
|
486
|
+
* a dangling pointer into the caller's build
|
|
487
|
+
* buffers once they are reclaimed (bindings GC
|
|
488
|
+
* their anchors; the descriptor must outlive
|
|
489
|
+
* them). Same retention as attr rows (#1486). */
|
|
490
|
+
d->child_plans[c].ns_uri =
|
|
491
|
+
sc->ns_uri ? dp_strdup(sc->ns_uri) : NULL;
|
|
492
|
+
if (sc->ns_uri && !d->child_plans[c].ns_uri)
|
|
493
|
+
goto oom;
|
|
484
494
|
uint16_t pc = sc->predicate_count;
|
|
485
495
|
if (pc) {
|
|
486
496
|
if (!sc->predicates) goto oom;
|
|
@@ -598,8 +608,25 @@ static int dp_walk_children(LeptrisElement elem, const dp_plan* plan,
|
|
|
598
608
|
* namespace (XML namespaces §5.2) — EXACT never binds them. */
|
|
599
609
|
static const char* dp_attr_value(LeptrisElement elem,
|
|
600
610
|
const leptris_attr_plan* ap) {
|
|
601
|
-
if (!ap->ns_form)
|
|
602
|
-
|
|
611
|
+
if (!ap->ns_form) {
|
|
612
|
+
/* #1586, the plan-path mirror of #758: the exact wire
|
|
613
|
+
* spelling first, then any qualification by local name —
|
|
614
|
+
* plain rows are lenient exactly like element rows under
|
|
615
|
+
* ns_lenient. Exact-URI rows keep their #1486 semantics. */
|
|
616
|
+
const char* exact =
|
|
617
|
+
leptris_element_attribute(elem, ap->wire_name);
|
|
618
|
+
if (exact) return exact;
|
|
619
|
+
for (LeptrisAttribute at = leptris_element_first_attribute(elem);
|
|
620
|
+
at; at = leptris_attribute_next(at)) {
|
|
621
|
+
const char* qn = leptris_attribute_get_name(at);
|
|
622
|
+
if (!qn) continue;
|
|
623
|
+
const char* colon = strchr(qn, ':');
|
|
624
|
+
const char* local = colon ? colon + 1 : qn;
|
|
625
|
+
if (strcmp(local, ap->wire_name) == 0)
|
|
626
|
+
return leptris_attribute_get_value(elem, at);
|
|
627
|
+
}
|
|
628
|
+
return NULL;
|
|
629
|
+
}
|
|
603
630
|
for (LeptrisAttribute at = leptris_element_first_attribute(elem);
|
|
604
631
|
at; at = leptris_attribute_next(at)) {
|
|
605
632
|
const char* qn = leptris_attribute_get_name(at);
|
|
@@ -832,8 +859,13 @@ static int dp_walk_children(LeptrisElement elem, const dp_plan* plan,
|
|
|
832
859
|
LeptrisElement child = (LeptrisElement)n;
|
|
833
860
|
if (!dp_wire_name_matches(child, row->wire_name))
|
|
834
861
|
continue;
|
|
862
|
+
/* The TARGET plan's form governs (#1115) — so its
|
|
863
|
+
* lenient flag governs the leniency, not the outer
|
|
864
|
+
* plan's (#1586: a namespaced child under a plan
|
|
865
|
+
* that declares ns_lenient binds by local name). */
|
|
835
866
|
if (!dp_ns_binds(child, target->ns_form, target->ns_uri,
|
|
836
|
-
|
|
867
|
+
(target->flags &
|
|
868
|
+
LEPTRIS_PLAN_FLAG_NS_LENIENT) != 0))
|
|
837
869
|
continue;
|
|
838
870
|
/* Claim the element against later same-wire rows
|
|
839
871
|
* (predicate partitioning). */
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
#ifndef LEPTRIS_RNG_INTERNAL_H
|
|
9
9
|
#define LEPTRIS_RNG_INTERNAL_H
|
|
10
10
|
|
|
11
|
+
#ifdef __cplusplus
|
|
12
|
+
extern "C" {
|
|
13
|
+
#endif
|
|
14
|
+
|
|
11
15
|
#include "../include/leptris.h"
|
|
12
16
|
#include "../dom/diag.h"
|
|
13
17
|
#include <stddef.h>
|
|
@@ -109,4 +113,8 @@ int rng_regex_matches(const char* pat, const char* text);
|
|
|
109
113
|
void rng_pattern_free(RngPattern* p);
|
|
110
114
|
void rng_grammar_free(RngGrammar* g);
|
|
111
115
|
|
|
116
|
+
#ifdef __cplusplus
|
|
117
|
+
}
|
|
118
|
+
#endif
|
|
119
|
+
|
|
112
120
|
#endif
|
|
@@ -289,6 +289,10 @@ static RreNode* parse_piece(RreParser* ps) {
|
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
|
+
/* {n} with no comma is EXACT — h must be l, not the
|
|
293
|
+
* -1 "unbounded" sentinel. The old code left h = -1,
|
|
294
|
+
* so [A-Z]{3} matched ABCD (open-ended {3,}). */
|
|
295
|
+
if (*q != ',' && h == -1) h = l;
|
|
292
296
|
if (*q == '}' && l >= 0 && (h == -1 || h >= l)) {
|
|
293
297
|
lo = l;
|
|
294
298
|
hi = h;
|
|
@@ -8,14 +8,119 @@
|
|
|
8
8
|
#include <string.h>
|
|
9
9
|
|
|
10
10
|
#include "../../include/leptris.h"
|
|
11
|
+
#include "xsd_internal.h"
|
|
11
12
|
|
|
12
13
|
#define XSD_NS "http://www.w3.org/2001/XMLSchema"
|
|
13
14
|
|
|
15
|
+
extern int xsd_simple_valid(const XsdSimple* simple, const char* v);
|
|
16
|
+
|
|
14
17
|
struct leptris_xsd_schema {
|
|
15
18
|
size_t declaration_count;
|
|
19
|
+
XsdSimple* simple_types;
|
|
16
20
|
char* error;
|
|
17
21
|
};
|
|
18
22
|
|
|
23
|
+
static char* xsd_strdup(const char* s) {
|
|
24
|
+
if (!s) return NULL;
|
|
25
|
+
size_t n = strlen(s) + 1;
|
|
26
|
+
char* out = (char*)malloc(n);
|
|
27
|
+
if (out) memcpy(out, s, n);
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Capture one xs:simpleType's restriction model (slice 2). */
|
|
32
|
+
static XsdSimple* xsd_capture_simple(LeptrisElement st) {
|
|
33
|
+
const char* name = leptris_element_attribute(st, "name");
|
|
34
|
+
if (!name) return NULL; /* anonymous types ride their element */
|
|
35
|
+
|
|
36
|
+
XsdSimple* simple = (XsdSimple*)calloc(1, sizeof(*simple));
|
|
37
|
+
if (!simple) return NULL;
|
|
38
|
+
simple->name = xsd_strdup(name);
|
|
39
|
+
simple->length = simple->min_length = simple->max_length = -1;
|
|
40
|
+
simple->total_digits = simple->fraction_digits = -1;
|
|
41
|
+
|
|
42
|
+
for (LeptrisNodeRef n =
|
|
43
|
+
leptris_node_first_child(leptris_element_as_node(st));
|
|
44
|
+
n; n = leptris_node_next_sibling(n)) {
|
|
45
|
+
if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
46
|
+
continue;
|
|
47
|
+
LeptrisElement e = (LeptrisElement)n;
|
|
48
|
+
const char* local = NULL, *prefix = NULL, *uri = NULL;
|
|
49
|
+
leptris_element_expanded_name(e, &local, &prefix, &uri);
|
|
50
|
+
if (uri && strcmp(uri, XSD_NS) != 0) continue;
|
|
51
|
+
if (!local) continue;
|
|
52
|
+
if (strcmp(local, "restriction") == 0) {
|
|
53
|
+
simple->base =
|
|
54
|
+
xsd_strdup(leptris_element_attribute(e, "base"));
|
|
55
|
+
for (LeptrisNodeRef f = leptris_node_first_child(
|
|
56
|
+
leptris_element_as_node(e));
|
|
57
|
+
f; f = leptris_node_next_sibling(f)) {
|
|
58
|
+
if (leptris_node_get_type(f) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
59
|
+
continue;
|
|
60
|
+
LeptrisElement fe = (LeptrisElement)f;
|
|
61
|
+
const char* fl = NULL, *fp = NULL, *fu = NULL;
|
|
62
|
+
leptris_element_expanded_name(fe, &fl, &fp, &fu);
|
|
63
|
+
if (!fl || (fu && strcmp(fu, XSD_NS) != 0)) continue;
|
|
64
|
+
const char* val =
|
|
65
|
+
leptris_element_attribute(fe, "value");
|
|
66
|
+
if (strcmp(fl, "pattern") == 0)
|
|
67
|
+
simple->pattern = xsd_strdup(val);
|
|
68
|
+
else if (strcmp(fl, "enumeration") == 0) {
|
|
69
|
+
XsdFacetValue* fv =
|
|
70
|
+
(XsdFacetValue*)calloc(1, sizeof(*fv));
|
|
71
|
+
if (fv) {
|
|
72
|
+
fv->text = xsd_strdup(val);
|
|
73
|
+
fv->next = simple->enum_values;
|
|
74
|
+
simple->enum_values = fv;
|
|
75
|
+
simple->enum_count++;
|
|
76
|
+
}
|
|
77
|
+
} else if (strcmp(fl, "minInclusive") == 0)
|
|
78
|
+
simple->min_inclusive = xsd_strdup(val);
|
|
79
|
+
else if (strcmp(fl, "minExclusive") == 0)
|
|
80
|
+
simple->min_exclusive = xsd_strdup(val);
|
|
81
|
+
else if (strcmp(fl, "maxInclusive") == 0)
|
|
82
|
+
simple->max_inclusive = xsd_strdup(val);
|
|
83
|
+
else if (strcmp(fl, "maxExclusive") == 0)
|
|
84
|
+
simple->max_exclusive = xsd_strdup(val);
|
|
85
|
+
else if (strcmp(fl, "length") == 0)
|
|
86
|
+
simple->length = val ? strtol(val, NULL, 10) : -1;
|
|
87
|
+
else if (strcmp(fl, "minLength") == 0)
|
|
88
|
+
simple->min_length = val ? strtol(val, NULL, 10) : -1;
|
|
89
|
+
else if (strcmp(fl, "maxLength") == 0)
|
|
90
|
+
simple->max_length = val ? strtol(val, NULL, 10) : -1;
|
|
91
|
+
else if (strcmp(fl, "totalDigits") == 0)
|
|
92
|
+
simple->total_digits = val ? strtol(val, NULL, 10) : -1;
|
|
93
|
+
else if (strcmp(fl, "fractionDigits") == 0)
|
|
94
|
+
simple->fraction_digits = val ? strtol(val, NULL, 10) : -1;
|
|
95
|
+
else if (strcmp(fl, "whiteSpace") == 0)
|
|
96
|
+
simple->whitespace = xsd_strdup(val);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return simple;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Depth-capped local-base resolution with a cycle guard: the
|
|
105
|
+
* restriction chain walks to its built-in, facets of EVERY hop
|
|
106
|
+
* apply (XSD semantics). */
|
|
107
|
+
static const XsdSimple* xsd_find_simple(struct leptris_xsd_schema* s,
|
|
108
|
+
const char* name) {
|
|
109
|
+
for (XsdSimple* t = s->simple_types; t; t = t->next)
|
|
110
|
+
if (strcmp(t->name, name) == 0) return t;
|
|
111
|
+
return NULL;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static int xsd_valid_chain(struct leptris_xsd_schema* s,
|
|
115
|
+
const XsdSimple* t, const char* v, int depth) {
|
|
116
|
+
if (!t || depth > 32) return 0;
|
|
117
|
+
if (t->base && strncmp(t->base, "xs:", 3) != 0) {
|
|
118
|
+
const XsdSimple* next = xsd_find_simple(s, t->base);
|
|
119
|
+
if (next && !xsd_valid_chain(s, next, v, depth + 1)) return 0;
|
|
120
|
+
}
|
|
121
|
+
return xsd_simple_valid(t, v);
|
|
122
|
+
}
|
|
123
|
+
|
|
19
124
|
static void xsd_set_error(struct leptris_xsd_schema* s, const char* msg) {
|
|
20
125
|
free(s->error);
|
|
21
126
|
s->error = NULL;
|
|
@@ -88,8 +193,15 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
|
88
193
|
continue;
|
|
89
194
|
leptris_element_expanded_name((LeptrisElement)n, &local, &prefix,
|
|
90
195
|
&uri);
|
|
91
|
-
if (xsd_is_declaration(local, uri))
|
|
92
|
-
|
|
196
|
+
if (!xsd_is_declaration(local, uri)) continue;
|
|
197
|
+
s->declaration_count++;
|
|
198
|
+
if (strcmp(local, "simpleType") == 0 && uri && !strcmp(uri, XSD_NS)) {
|
|
199
|
+
XsdSimple* captured = xsd_capture_simple((LeptrisElement)n);
|
|
200
|
+
if (captured) {
|
|
201
|
+
captured->next = s->simple_types;
|
|
202
|
+
s->simple_types = captured;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
93
205
|
}
|
|
94
206
|
|
|
95
207
|
leptris_document_free(doc);
|
|
@@ -106,10 +218,49 @@ fail:
|
|
|
106
218
|
LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
|
|
107
219
|
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
108
220
|
if (!s) return;
|
|
221
|
+
XsdSimple* t = s->simple_types;
|
|
222
|
+
while (t) {
|
|
223
|
+
XsdSimple* next = t->next;
|
|
224
|
+
free(t->name);
|
|
225
|
+
free(t->base);
|
|
226
|
+
free(t->pattern);
|
|
227
|
+
XsdFacetValue* fv = t->enum_values;
|
|
228
|
+
while (fv) {
|
|
229
|
+
XsdFacetValue* fn = fv->next;
|
|
230
|
+
free(fv->text);
|
|
231
|
+
free(fv);
|
|
232
|
+
fv = fn;
|
|
233
|
+
}
|
|
234
|
+
free(t->min_inclusive);
|
|
235
|
+
free(t->min_exclusive);
|
|
236
|
+
free(t->max_inclusive);
|
|
237
|
+
free(t->max_exclusive);
|
|
238
|
+
free(t->whitespace);
|
|
239
|
+
free(t);
|
|
240
|
+
t = next;
|
|
241
|
+
}
|
|
109
242
|
free(s->error);
|
|
110
243
|
free(s);
|
|
111
244
|
}
|
|
112
245
|
|
|
246
|
+
/* ---- slice 2: lexical validation --------------------------------- */
|
|
247
|
+
|
|
248
|
+
LEPTRIS_API int leptris_xsd_builtin_valid(const char* builtin,
|
|
249
|
+
const char* lexical) {
|
|
250
|
+
extern int xsd_builtin_valid(const char* type, const char* v);
|
|
251
|
+
return xsd_builtin_valid(builtin, lexical);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
LEPTRIS_API int leptris_xsd_simple_valid(LeptrisXsdSchema schema,
|
|
255
|
+
const char* type_name,
|
|
256
|
+
const char* lexical) {
|
|
257
|
+
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
258
|
+
if (!s || !type_name || !lexical) return 0;
|
|
259
|
+
const XsdSimple* t = xsd_find_simple(s, type_name);
|
|
260
|
+
if (!t) return -1; /* unknown type */
|
|
261
|
+
return xsd_valid_chain(s, t, lexical, 0);
|
|
262
|
+
}
|
|
263
|
+
|
|
113
264
|
LEPTRIS_API size_t leptris_xsd_declaration_count(LeptrisXsdSchema schema) {
|
|
114
265
|
if (!schema) return 0;
|
|
115
266
|
return ((struct leptris_xsd_schema*)schema)->declaration_count;
|