leptris 1.9.320.0-aarch64-linux → 1.9.321.0-aarch64-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 +17 -0
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/ffi.rb +15 -0
- data/lib/leptris/xml/xsd.rb +58 -6
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +22 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/CMakeLists.txt +2 -0
- data/vendor-src/libleptris/src/include/leptris.h +22 -0
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +309 -1
- data/vendor-src/libleptris/src/leptris/xsd/content_model.c +232 -0
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +287 -0
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +48 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +253 -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: db9055121618343dff8b5e698e7c92d47dbd434db73fe6d8339dc46a0be73c82
|
|
4
|
+
data.tar.gz: 3e7cf86ffa532fff238034f21f55dbc7117a85398fd1aed4bf3ade5d0c7c3b4e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3a625fa0c0ef2ac0e64244b09872d64e6f2f6c9a0610c8b65a1c931b69006755e7ce6afe380a0a0a4c8980916e2aeb57dfebbf9b0fde1baf7a5d298dfb866c36
|
|
7
|
+
data.tar.gz: 4607ae9d6d1952a36f2d9b100417e97c77bca875aeee0dafa5736168d3b6a2966f6ead5a59d1384d8f8ec3adebd542ff05e41ce2fc632767fa466fcf539538cb
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## [1.9.321.0] - 2026-10-08
|
|
2
|
+
|
|
3
|
+
### Added — engine sync (libleptris 1.9.321)
|
|
4
|
+
|
|
5
|
+
- **XSD instance validation, slices 3-4 (#1075)**:
|
|
6
|
+
`Schema#valid?` walks the whole document — element declarations,
|
|
7
|
+
attribute rows (required + typed + lenient qualified spellings),
|
|
8
|
+
text lexical checks, child content models through a Thompson NFA
|
|
9
|
+
(linear in children x states, no backtracking blowup);
|
|
10
|
+
`Schema#validate_errors` enumerates every failure (the engine
|
|
11
|
+
replaces the list per run); `Schema#content_valid?` evaluates a
|
|
12
|
+
child sequence directly against a top-level declaration's model.
|
|
13
|
+
The -1 contract raises (unknown declaration never masquerades as
|
|
14
|
+
invalid input). Pins use the named-ref shape — the inline-
|
|
15
|
+
anonymous complexType capture gap is filed upstream
|
|
16
|
+
(leptris#1592).
|
|
17
|
+
|
|
1
18
|
## [1.9.320.0] - 2026-10-08
|
|
2
19
|
|
|
3
20
|
### Fixed — engine sync (libleptris 1.9.320)
|
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.321"
|
|
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/leptris/xml/ffi.rb
CHANGED
|
@@ -1304,6 +1304,21 @@ attach_function :leptris_parse_string,
|
|
|
1304
1304
|
[:string, :string], :int
|
|
1305
1305
|
attach_function :leptris_xsd_simple_valid,
|
|
1306
1306
|
[:leptris_xsd_schema, :string, :string], :int
|
|
1307
|
+
# Slices 3-4 (libleptris 1.9.321, #1075): content models via
|
|
1308
|
+
# Thompson NFA (linear in children x states) and whole-
|
|
1309
|
+
# instance validation — element declarations, attribute rows
|
|
1310
|
+
# (required + typed + lenient qualified spellings), text
|
|
1311
|
+
# lexical checks, child content models. Failures enumerate
|
|
1312
|
+
# behind error_count/_at (schema-owned; the next validate
|
|
1313
|
+
# run replaces them).
|
|
1314
|
+
attach_function :leptris_xsd_content_valid,
|
|
1315
|
+
[:leptris_xsd_schema, :string, :pointer, :pointer, :size_t], :int
|
|
1316
|
+
attach_function :leptris_xsd_validate,
|
|
1317
|
+
[:leptris_xsd_schema, :leptris_document], :int
|
|
1318
|
+
attach_function :leptris_xsd_error_count,
|
|
1319
|
+
[:leptris_xsd_schema], :size_t
|
|
1320
|
+
attach_function :leptris_xsd_error_at,
|
|
1321
|
+
[:leptris_xsd_schema, :size_t], UTF8_STRING
|
|
1307
1322
|
|
|
1308
1323
|
attach_function :leptris_rng_parse,
|
|
1309
1324
|
[:string, :size_t, :pointer], :leptris_relaxng
|
data/lib/leptris/xml/xsd.rb
CHANGED
|
@@ -79,14 +79,66 @@ module Leptris::XML::XSD
|
|
|
79
79
|
# name) — restriction chains validate every hop's facets,
|
|
80
80
|
# cycle-guarded. Unknown type names raise (the -1 contract).
|
|
81
81
|
def simple_valid?(type_name, lexical)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
tri_state(
|
|
83
|
+
Leptris::XML::FFI.leptris_xsd_simple_valid(
|
|
84
|
+
@handle, type_name.to_s, lexical.to_s),
|
|
85
|
+
"#{type_name.inspect} is not a simpleType in this schema")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Slices 3-4 (libleptris 1.9.321, #1075): whole-instance
|
|
89
|
+
# validation — element declarations, attribute rows (required
|
|
90
|
+
# + typed + lenient qualified spellings), text lexical checks,
|
|
91
|
+
# and child content models through the Thompson NFA. Returns
|
|
92
|
+
# true/false.
|
|
93
|
+
def valid?(document)
|
|
94
|
+
tri_state(
|
|
95
|
+
Leptris::XML::FFI.leptris_xsd_validate(
|
|
96
|
+
@handle, document.c_ptr), "invalid document handle")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The enumerated failure list of the last #validate run
|
|
100
|
+
# (empty when valid; the NEXT validate replaces the list).
|
|
101
|
+
def validate_errors(document)
|
|
102
|
+
valid?(document)
|
|
103
|
+
Leptris::XML::FFI.leptris_xsd_error_count(@handle).times.map do |i|
|
|
104
|
+
Leptris::XML::FFI.leptris_xsd_error_at(@handle, i)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Slice 3 direct face: a child SEQUENCE against a top-level
|
|
109
|
+
# element declaration's compiled content model (occurrence
|
|
110
|
+
# bounds, xs:any wildcards). +children+ are child Nodes (name
|
|
111
|
+
# + effective namespace extracted per child). Unknown element
|
|
112
|
+
# declarations raise (the -1 contract).
|
|
113
|
+
def content_valid?(element_name, children)
|
|
114
|
+
kids = children.to_a
|
|
115
|
+
nb, na = Leptris::XML::CStringArray.to_c(
|
|
116
|
+
kids.map { |c| c.name.to_s })
|
|
117
|
+
ub, ua = Leptris::XML::CStringArray.to_c(
|
|
118
|
+
kids.map { |c| child_ns_uri(c) })
|
|
119
|
+
tri_state(
|
|
120
|
+
Leptris::XML::FFI.leptris_xsd_content_valid(
|
|
121
|
+
@handle, element_name.to_s, nb, ub, kids.size),
|
|
122
|
+
"#{element_name.inspect} is not a top-level element declaration")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
# A child's effective namespace URI (nil = none) — read
|
|
128
|
+
# through its own c_ptr, so docless wrappers work.
|
|
129
|
+
def child_ns_uri(node)
|
|
130
|
+
uri = Leptris::XML::FFI.leptris_element_namespace(node.c_ptr)
|
|
131
|
+
uri.nil? || uri.empty? ? "" : uri
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# The C faces' 1/0/-1 contract: true/false, with -1 raising —
|
|
135
|
+
# a silent false would mean "no such declaration/type", which
|
|
136
|
+
# callers must never confuse with "invalid input".
|
|
137
|
+
def tri_state(code, unknown_message)
|
|
138
|
+
case code
|
|
85
139
|
when 1 then true
|
|
86
140
|
when 0 then false
|
|
87
|
-
else
|
|
88
|
-
raise ArgumentError,
|
|
89
|
-
"#{type_name.inspect} is not a simpleType in this schema"
|
|
141
|
+
else raise ArgumentError, unknown_message
|
|
90
142
|
end
|
|
91
143
|
end
|
|
92
144
|
end
|
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.321] - 2026-10-08
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **XSD validation, tier 1, slices 3–4 (#1075, PRs #1589/#1590).**
|
|
8
|
+
Slice 3 — content models: the compiler captures top-level
|
|
9
|
+
`xs:element` declarations and named `xs:complexType` models
|
|
10
|
+
(sequence/choice/all, occurrence bounds incl. unbounded, element
|
|
11
|
+
name/ref particles, `xs:any` wildcards with the full
|
|
12
|
+
namespace-filter grammar); `leptris_xsd_content_valid` evaluates
|
|
13
|
+
a child sequence through a Thompson NFA — linear in children x
|
|
14
|
+
states, no backtracking blowup on nested choices. Slice 4 —
|
|
15
|
+
instance validation: `leptris_xsd_validate(schema, document)`
|
|
16
|
+
walks element declarations, complexType attribute rows
|
|
17
|
+
(required + typed + lenient qualified spellings), child content
|
|
18
|
+
models, and lexical text checks for simple-typed elements
|
|
19
|
+
(particles carry type refs); failures enumerate behind
|
|
20
|
+
`leptris_xsd_error_count/_at`. The libxml2 corpus gate holds at
|
|
21
|
+
163/163; full suite 1911/1911.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
3
25
|
## [1.9.320] - 2026-10-08
|
|
4
26
|
|
|
5
27
|
### Fixed
|
|
@@ -920,6 +920,28 @@ LEPTRIS_API int leptris_xsd_builtin_valid(const char* builtin,
|
|
|
920
920
|
LEPTRIS_API int leptris_xsd_simple_valid(LeptrisXsdSchema schema,
|
|
921
921
|
const char* type_name,
|
|
922
922
|
const char* lexical);
|
|
923
|
+
/* Slice 3 — content models. Validates a child sequence against the
|
|
924
|
+
* compiled content model of a top-level element declaration (its
|
|
925
|
+
* type's sequence/choice/all with occurrence bounds and xs:any
|
|
926
|
+
* wildcards). 1 valid, 0 invalid, -1 unknown element. child_ns may
|
|
927
|
+
* be NULL (all children unqualified). */
|
|
928
|
+
LEPTRIS_API int leptris_xsd_content_valid(
|
|
929
|
+
LeptrisXsdSchema schema, const char* element_name,
|
|
930
|
+
const char* const* child_names, const char* const* child_ns,
|
|
931
|
+
size_t child_count);
|
|
932
|
+
/* Slice 4 — instance validation. Walks the document against the
|
|
933
|
+
* compiled schema: element declarations by name, attribute rows
|
|
934
|
+
* (required use + typed values, lenient qualified spellings),
|
|
935
|
+
* text lexical checks for simple-typed elements, child content
|
|
936
|
+
* models (slice 3). Returns 1 valid, 0 invalid, -1 bad args.
|
|
937
|
+
* Errors accumulate — leptris_xsd_error_count/_at enumerate them
|
|
938
|
+
* (schema-owned; the next validate run replaces them; freed with
|
|
939
|
+
* the schema). */
|
|
940
|
+
LEPTRIS_API int leptris_xsd_validate(LeptrisXsdSchema schema,
|
|
941
|
+
LeptrisDocument doc);
|
|
942
|
+
LEPTRIS_API size_t leptris_xsd_error_count(LeptrisXsdSchema schema);
|
|
943
|
+
LEPTRIS_API const char* leptris_xsd_error_at(LeptrisXsdSchema schema,
|
|
944
|
+
size_t i);
|
|
923
945
|
/* Validate an instance document against the parsed schema
|
|
924
946
|
* (phase-2 core subset: element/attribute/text/data/value/
|
|
925
947
|
* choice/group/interleave/repeats/ref). Returns 1 valid; on 0,
|
|
@@ -13,13 +13,122 @@
|
|
|
13
13
|
#define XSD_NS "http://www.w3.org/2001/XMLSchema"
|
|
14
14
|
|
|
15
15
|
extern int xsd_simple_valid(const XsdSimple* simple, const char* v);
|
|
16
|
+
extern struct xsd_validator* xsd_validator_new(
|
|
17
|
+
struct leptris_xsd_schema* s);
|
|
18
|
+
extern void xsd_validator_free(struct xsd_validator* v);
|
|
19
|
+
extern int xsd_validator_run(struct xsd_validator* v, LeptrisDocument doc);
|
|
20
|
+
extern size_t xsd_validator_error_count(struct xsd_validator* v);
|
|
21
|
+
extern const char* xsd_validator_error_at(struct xsd_validator* v,
|
|
22
|
+
size_t i);
|
|
23
|
+
|
|
24
|
+
extern int xsd_content_valid(struct leptris_xsd_schema* s, XsdCm* model,
|
|
25
|
+
const char* target_ns,
|
|
26
|
+
const char* const* names,
|
|
27
|
+
const char* const* ns_uris, size_t count);
|
|
16
28
|
|
|
17
29
|
struct leptris_xsd_schema {
|
|
18
30
|
size_t declaration_count;
|
|
19
31
|
XsdSimple* simple_types;
|
|
32
|
+
XsdElementDecl* elements;
|
|
33
|
+
XsdCm* complex_types; /* name-carrying model roots */
|
|
34
|
+
XsdTypeAttrs* type_attrs;
|
|
35
|
+
char* target_ns;
|
|
36
|
+
void* validator; /* slice-4 parked error list */
|
|
20
37
|
char* error;
|
|
21
38
|
};
|
|
22
39
|
|
|
40
|
+
/* ---- slice 3: content-model capture ------------------------------ */
|
|
41
|
+
|
|
42
|
+
static char* xsd_strdup(const char* s);
|
|
43
|
+
|
|
44
|
+
static XsdCm* xsd_cm_new(XsdCmKind kind) {
|
|
45
|
+
XsdCm* cm = (XsdCm*)calloc(1, sizeof(*cm));
|
|
46
|
+
if (!cm) return NULL;
|
|
47
|
+
cm->kind = kind;
|
|
48
|
+
cm->min = 1;
|
|
49
|
+
cm->max = 1;
|
|
50
|
+
return cm;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static void xsd_cm_free(XsdCm* cm) {
|
|
54
|
+
while (cm) {
|
|
55
|
+
XsdCm* next = cm->next;
|
|
56
|
+
xsd_cm_free(cm->first_child);
|
|
57
|
+
free(cm->name);
|
|
58
|
+
free(cm->type);
|
|
59
|
+
free(cm->ns);
|
|
60
|
+
free(cm->any_ns);
|
|
61
|
+
free(cm);
|
|
62
|
+
cm = next;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static void xsd_cm_occurrence(XsdCm* cm, LeptrisElement e) {
|
|
67
|
+
const char* mn = leptris_element_attribute(e, "minOccurs");
|
|
68
|
+
if (mn) cm->min = (strcmp(mn, "unbounded") == 0) ? 0 : atoi(mn);
|
|
69
|
+
const char* mx = leptris_element_attribute(e, "maxOccurs");
|
|
70
|
+
if (mx) cm->max = (strcmp(mx, "unbounded") == 0) ? -1 : atoi(mx);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Recursive model build over sequence | choice | all | element |
|
|
74
|
+
* any children. Returns the child list head; *tail_out receives
|
|
75
|
+
* the list tail for sibling chaining. */
|
|
76
|
+
static XsdCm* xsd_cm_parse(LeptrisElement group, XsdCm** tail_out) {
|
|
77
|
+
XsdCm* head = NULL;
|
|
78
|
+
XsdCm* tail = NULL;
|
|
79
|
+
for (LeptrisNodeRef n =
|
|
80
|
+
leptris_node_first_child(leptris_element_as_node(group));
|
|
81
|
+
n; n = leptris_node_next_sibling(n)) {
|
|
82
|
+
if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
83
|
+
continue;
|
|
84
|
+
LeptrisElement e = (LeptrisElement)n;
|
|
85
|
+
const char* local = NULL, *prefix = NULL, *uri = NULL;
|
|
86
|
+
leptris_element_expanded_name(e, &local, &prefix, &uri);
|
|
87
|
+
if (!local || (uri && strcmp(uri, XSD_NS) != 0)) continue;
|
|
88
|
+
|
|
89
|
+
XsdCm* cm = NULL;
|
|
90
|
+
if (strcmp(local, "sequence") == 0 || strcmp(local, "choice") == 0 ||
|
|
91
|
+
strcmp(local, "all") == 0) {
|
|
92
|
+
cm = xsd_cm_new(strcmp(local, "sequence") == 0 ? XSD_CM_SEQ
|
|
93
|
+
: strcmp(local, "choice") == 0 ? XSD_CM_CHOICE
|
|
94
|
+
: XSD_CM_ALL);
|
|
95
|
+
if (cm) cm->first_child = xsd_cm_parse(e, NULL);
|
|
96
|
+
} else if (strcmp(local, "element") == 0) {
|
|
97
|
+
cm = xsd_cm_new(XSD_CM_ELEMENT);
|
|
98
|
+
if (cm) {
|
|
99
|
+
const char* nm =
|
|
100
|
+
leptris_element_attribute(e, "name");
|
|
101
|
+
if (!nm) nm = leptris_element_attribute(e, "ref");
|
|
102
|
+
cm->name = xsd_strdup(nm);
|
|
103
|
+
cm->type = xsd_strdup(
|
|
104
|
+
leptris_element_attribute(e, "type"));
|
|
105
|
+
}
|
|
106
|
+
} else if (strcmp(local, "any") == 0) {
|
|
107
|
+
cm = xsd_cm_new(XSD_CM_ANY);
|
|
108
|
+
if (cm) {
|
|
109
|
+
cm->any_ns = xsd_strdup(
|
|
110
|
+
leptris_element_attribute(e, "namespace"));
|
|
111
|
+
const char* pc = leptris_element_attribute(
|
|
112
|
+
e, "processContents");
|
|
113
|
+
cm->process_skip =
|
|
114
|
+
(pc && (strcmp(pc, "skip") == 0 ||
|
|
115
|
+
strcmp(pc, "lax") == 0)) ? 1 : 0;
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
continue; /* annotation & friends */
|
|
119
|
+
}
|
|
120
|
+
if (!cm) continue;
|
|
121
|
+
xsd_cm_occurrence(cm, e);
|
|
122
|
+
if (tail)
|
|
123
|
+
tail->next = cm;
|
|
124
|
+
else
|
|
125
|
+
head = cm;
|
|
126
|
+
tail = cm;
|
|
127
|
+
}
|
|
128
|
+
if (tail_out) *tail_out = tail;
|
|
129
|
+
return head;
|
|
130
|
+
}
|
|
131
|
+
|
|
23
132
|
static char* xsd_strdup(const char* s) {
|
|
24
133
|
if (!s) return NULL;
|
|
25
134
|
size_t n = strlen(s) + 1;
|
|
@@ -161,6 +270,9 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
|
161
270
|
return NULL;
|
|
162
271
|
}
|
|
163
272
|
|
|
273
|
+
/* target namespace (xs:any ##other/##targetNamespace filters) */
|
|
274
|
+
s->target_ns = NULL;
|
|
275
|
+
|
|
164
276
|
LeptrisDocument doc = leptris_parse_string(xsd_text, len, status);
|
|
165
277
|
if (!doc) {
|
|
166
278
|
xsd_set_error(s, "schema document is not well-formed XML");
|
|
@@ -172,6 +284,8 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
|
172
284
|
xsd_set_error(s, "schema document has no root element");
|
|
173
285
|
goto fail;
|
|
174
286
|
}
|
|
287
|
+
s->target_ns = xsd_strdup(
|
|
288
|
+
leptris_element_attribute(root, "targetNamespace"));
|
|
175
289
|
|
|
176
290
|
const char* local = NULL;
|
|
177
291
|
const char* prefix = NULL;
|
|
@@ -195,7 +309,77 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
|
195
309
|
&uri);
|
|
196
310
|
if (!xsd_is_declaration(local, uri)) continue;
|
|
197
311
|
s->declaration_count++;
|
|
198
|
-
if (strcmp(local, "
|
|
312
|
+
if (strcmp(local, "element") == 0 && uri && !strcmp(uri, XSD_NS)) {
|
|
313
|
+
const char* nm = leptris_element_attribute((LeptrisElement)n,
|
|
314
|
+
"name");
|
|
315
|
+
const char* ty = leptris_element_attribute((LeptrisElement)n,
|
|
316
|
+
"type");
|
|
317
|
+
if (nm && ty) {
|
|
318
|
+
XsdElementDecl* d =
|
|
319
|
+
(XsdElementDecl*)calloc(1, sizeof(*d));
|
|
320
|
+
if (d) {
|
|
321
|
+
d->name = xsd_strdup(nm);
|
|
322
|
+
d->type = xsd_strdup(ty);
|
|
323
|
+
d->next = s->elements;
|
|
324
|
+
s->elements = d;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} else if (strcmp(local, "complexType") == 0 && uri &&
|
|
328
|
+
!strcmp(uri, XSD_NS)) {
|
|
329
|
+
const char* nm =
|
|
330
|
+
leptris_element_attribute((LeptrisElement)n, "name");
|
|
331
|
+
if (nm) {
|
|
332
|
+
XsdCm* root =
|
|
333
|
+
xsd_cm_new(XSD_CM_SEQ); /* wrapper keeps the name */
|
|
334
|
+
if (root) {
|
|
335
|
+
root->name = xsd_strdup(nm);
|
|
336
|
+
root->first_child =
|
|
337
|
+
xsd_cm_parse((LeptrisElement)n, NULL);
|
|
338
|
+
root->next = s->complex_types;
|
|
339
|
+
s->complex_types = root;
|
|
340
|
+
}
|
|
341
|
+
/* slice 4: attribute declarations of the type */
|
|
342
|
+
XsdAttrDecl* head = NULL;
|
|
343
|
+
XsdAttrDecl* tail = NULL;
|
|
344
|
+
for (LeptrisNodeRef ac = leptris_node_first_child(
|
|
345
|
+
leptris_element_as_node((LeptrisElement)n));
|
|
346
|
+
ac; ac = leptris_node_next_sibling(ac)) {
|
|
347
|
+
if (leptris_node_get_type(ac) !=
|
|
348
|
+
LEPTRIS_NODE_TYPE_ELEMENT)
|
|
349
|
+
continue;
|
|
350
|
+
LeptrisElement ae = (LeptrisElement)ac;
|
|
351
|
+
const char* al = NULL, *ap = NULL, *au = NULL;
|
|
352
|
+
leptris_element_expanded_name(ae, &al, &ap, &au);
|
|
353
|
+
if (!al || (au && strcmp(au, XSD_NS) != 0)) continue;
|
|
354
|
+
if (strcmp(al, "attribute") != 0) continue;
|
|
355
|
+
const char* an = leptris_element_attribute(ae, "name");
|
|
356
|
+
if (!an) continue; /* ref forms: slice 5 */
|
|
357
|
+
XsdAttrDecl* d = (XsdAttrDecl*)calloc(1, sizeof(*d));
|
|
358
|
+
if (!d) continue;
|
|
359
|
+
d->name = xsd_strdup(an);
|
|
360
|
+
d->type = xsd_strdup(
|
|
361
|
+
leptris_element_attribute(ae, "type"));
|
|
362
|
+
const char* use =
|
|
363
|
+
leptris_element_attribute(ae, "use");
|
|
364
|
+
d->required = (use && strcmp(use, "required") == 0);
|
|
365
|
+
if (tail)
|
|
366
|
+
tail->next = d;
|
|
367
|
+
else
|
|
368
|
+
head = d;
|
|
369
|
+
tail = d;
|
|
370
|
+
}
|
|
371
|
+
if (head) {
|
|
372
|
+
XsdTypeAttrs* ta =
|
|
373
|
+
(XsdTypeAttrs*)calloc(1, sizeof(*ta));
|
|
374
|
+
if (ta) {
|
|
375
|
+
ta->type_name = xsd_strdup(nm);
|
|
376
|
+
ta->attrs = head;
|
|
377
|
+
ta->next = s->type_attrs;
|
|
378
|
+
s->type_attrs = ta;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
} else if (strcmp(local, "simpleType") == 0 && uri && !strcmp(uri, XSD_NS)) {
|
|
199
383
|
XsdSimple* captured = xsd_capture_simple((LeptrisElement)n);
|
|
200
384
|
if (captured) {
|
|
201
385
|
captured->next = s->simple_types;
|
|
@@ -218,6 +402,33 @@ fail:
|
|
|
218
402
|
LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
|
|
219
403
|
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
220
404
|
if (!s) return;
|
|
405
|
+
xsd_cm_free(s->complex_types);
|
|
406
|
+
XsdTypeAttrs* ta = s->type_attrs;
|
|
407
|
+
while (ta) {
|
|
408
|
+
XsdTypeAttrs* tn = ta->next;
|
|
409
|
+
XsdAttrDecl* a = ta->attrs;
|
|
410
|
+
while (a) {
|
|
411
|
+
XsdAttrDecl* an = a->next;
|
|
412
|
+
free(a->name);
|
|
413
|
+
free(a->type);
|
|
414
|
+
free(a);
|
|
415
|
+
a = an;
|
|
416
|
+
}
|
|
417
|
+
free(ta->type_name);
|
|
418
|
+
free(ta);
|
|
419
|
+
ta = tn;
|
|
420
|
+
}
|
|
421
|
+
XsdElementDecl* d = s->elements;
|
|
422
|
+
while (d) {
|
|
423
|
+
XsdElementDecl* dn = d->next;
|
|
424
|
+
free(d->name);
|
|
425
|
+
free(d->type);
|
|
426
|
+
free(d);
|
|
427
|
+
d = dn;
|
|
428
|
+
}
|
|
429
|
+
free(s->target_ns);
|
|
430
|
+
if (s->validator)
|
|
431
|
+
xsd_validator_free((struct xsd_validator*)s->validator);
|
|
221
432
|
XsdSimple* t = s->simple_types;
|
|
222
433
|
while (t) {
|
|
223
434
|
XsdSimple* next = t->next;
|
|
@@ -261,6 +472,103 @@ LEPTRIS_API int leptris_xsd_simple_valid(LeptrisXsdSchema schema,
|
|
|
261
472
|
return xsd_valid_chain(s, t, lexical, 0);
|
|
262
473
|
}
|
|
263
474
|
|
|
475
|
+
/* ---- validator accessors (validate.c) --------------------------- */
|
|
476
|
+
|
|
477
|
+
const XsdElementDecl* xsd_find_element(struct leptris_xsd_schema* s,
|
|
478
|
+
const char* name) {
|
|
479
|
+
if (!s || !name) return NULL;
|
|
480
|
+
for (XsdElementDecl* d = s->elements; d; d = d->next)
|
|
481
|
+
if (strcmp(d->name, name) == 0) return d;
|
|
482
|
+
return NULL;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
XsdCm* xsd_find_complex(struct leptris_xsd_schema* s,
|
|
486
|
+
const char* type_name) {
|
|
487
|
+
if (!s || !type_name) return NULL;
|
|
488
|
+
for (XsdCm* c = s->complex_types; c; c = c->next)
|
|
489
|
+
if (strcmp(c->name, type_name) == 0) return c;
|
|
490
|
+
return NULL;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const XsdSimple* xsd_find_simple_pub(struct leptris_xsd_schema* s,
|
|
494
|
+
const char* name) {
|
|
495
|
+
return xsd_find_simple(s, name);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const char* xsd_target_ns(struct leptris_xsd_schema* s) {
|
|
499
|
+
return s ? s->target_ns : NULL;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const XsdTypeAttrs* xsd_find_type_attrs(struct leptris_xsd_schema* s,
|
|
503
|
+
const char* type_name) {
|
|
504
|
+
if (!s || !type_name) return NULL;
|
|
505
|
+
for (XsdTypeAttrs* t = s->type_attrs; t; t = t->next)
|
|
506
|
+
if (strcmp(t->type_name, type_name) == 0) return t;
|
|
507
|
+
return NULL;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* ---- slice 4: instance validation (validate.c engine) ------------ */
|
|
511
|
+
|
|
512
|
+
LEPTRIS_API int leptris_xsd_validate(LeptrisXsdSchema schema,
|
|
513
|
+
LeptrisDocument doc) {
|
|
514
|
+
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
515
|
+
if (!s || !doc) return -1;
|
|
516
|
+
struct xsd_validator* v = xsd_validator_new(s);
|
|
517
|
+
if (!v) return -1;
|
|
518
|
+
int r = xsd_validator_run(v, doc);
|
|
519
|
+
/* park the run's errors on the schema for the accessors; a new
|
|
520
|
+
* validate run replaces them. */
|
|
521
|
+
struct xsd_validator* prev = (struct xsd_validator*)s->validator;
|
|
522
|
+
if (prev) xsd_validator_free(prev);
|
|
523
|
+
s->validator = v;
|
|
524
|
+
return r;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
LEPTRIS_API size_t leptris_xsd_error_count(LeptrisXsdSchema schema) {
|
|
528
|
+
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
529
|
+
if (!s) return 0;
|
|
530
|
+
return xsd_validator_error_count(
|
|
531
|
+
(struct xsd_validator*)s->validator);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
LEPTRIS_API const char* leptris_xsd_error_at(LeptrisXsdSchema schema,
|
|
535
|
+
size_t i) {
|
|
536
|
+
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
537
|
+
if (!s) return NULL;
|
|
538
|
+
return xsd_validator_error_at((struct xsd_validator*)s->validator,
|
|
539
|
+
i);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
LEPTRIS_API int leptris_xsd_content_valid(
|
|
543
|
+
LeptrisXsdSchema schema, const char* element_name,
|
|
544
|
+
const char* const* child_names, const char* const* child_ns,
|
|
545
|
+
size_t child_count) {
|
|
546
|
+
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
547
|
+
if (!s || !element_name) return -1;
|
|
548
|
+
|
|
549
|
+
const XsdElementDecl* d = s->elements;
|
|
550
|
+
while (d && strcmp(d->name, element_name) != 0) d = d->next;
|
|
551
|
+
if (!d) return -1; /* unknown element */
|
|
552
|
+
|
|
553
|
+
if (!d->type) return 1; /* no type attr: anyType accepts all */
|
|
554
|
+
|
|
555
|
+
/* built-in or simpleType ref: element content is text-only —
|
|
556
|
+
* any element child fails */
|
|
557
|
+
if (strncmp(d->type, "xs:", 3) == 0 ||
|
|
558
|
+
xsd_find_simple(s, d->type))
|
|
559
|
+
return child_count == 0;
|
|
560
|
+
|
|
561
|
+
XsdCm* ct = s->complex_types;
|
|
562
|
+
while (ct && strcmp(ct->name, d->type) != 0) ct = ct->next;
|
|
563
|
+
if (!ct) return child_count == 0; /* unknown type: accept-empty */
|
|
564
|
+
|
|
565
|
+
XsdCm* model = ct->first_child;
|
|
566
|
+
if (!model) return child_count == 0;
|
|
567
|
+
|
|
568
|
+
return xsd_content_valid(s, model, s->target_ns, child_names,
|
|
569
|
+
child_ns, child_count);
|
|
570
|
+
}
|
|
571
|
+
|
|
264
572
|
LEPTRIS_API size_t leptris_xsd_declaration_count(LeptrisXsdSchema schema) {
|
|
265
573
|
if (!schema) return 0;
|
|
266
574
|
return ((struct leptris_xsd_schema*)schema)->declaration_count;
|