leptris 1.9.320.0-arm-linux → 1.9.321.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c9a0179ae0c076cfc42de6b88569425e0f424a43feb4507bd3f07855fed3ca7
4
- data.tar.gz: c80d44187d6da051998ea400689ceded6e05f58948de49520f1e0f663f418801
3
+ metadata.gz: 302f2471b6341b7c0fb55a4e38abf332033d17a1f9050494a0b4f1131c3953cc
4
+ data.tar.gz: 7c84976af95b097ade4dc4229426b41b7d94660ae184796fbef70e606a5dcd20
5
5
  SHA512:
6
- metadata.gz: 85e0932ed8e0b6a3e82631df9eb718efd1af051bc8c440e313ff116064c4dacb6bde417ae148a0768734f84dabe3523cc6e6e5e67d5bf776be3ed8af96ae2ddd
7
- data.tar.gz: bf3fc27bce9fbbe7934e380ec4f80d60159fd5e84243b77173b608ac31245245c1eec83ecb0086d37f2bedb4c7a69fcf1a057844fa5677dbe8f8fb0db832c779
6
+ metadata.gz: abe70ccb890c3fbb4fd9fe262837b70615628043730987477c87c91a9da24ec9a80b47bd823abae831645d8163164d1ec798b9b7ece33850fe7d3a61ecaad593
7
+ data.tar.gz: b8ca3b96073cf922093da36e10f62af0e62814f4ff8b89b5cbb1a0a09b3c151471c5a4d0fed2dcc9d40982230516d7a8ab642a11184b7e00d4f1de07b49ebed7
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.320"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.320.0"
4
+ VERSION = "1.9.321.0"
5
5
  end
@@ -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
@@ -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
- r = Leptris::XML::FFI.leptris_xsd_simple_valid(
83
- @handle, type_name.to_s, lexical.to_s)
84
- case r
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,7 +1,7 @@
1
1
  Sources vendored in this gem (recompile rights; the ruby
2
2
  variant builds them at install via extconf.rb):
3
3
 
4
- - libleptris v1.9.320
4
+ - libleptris v1.9.321
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -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
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.320
6
+ VERSION 1.9.321
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -29,6 +29,8 @@ set(LEPTRIS_SOURCES
29
29
  leptris/rng/parse.c
30
30
  leptris/xsd/compile.c
31
31
  leptris/xsd/datatypes.c
32
+ leptris/xsd/content_model.c
33
+ leptris/xsd/validate.c
32
34
  leptris/rng/rng_public.c
33
35
  leptris/rng/rng_regex.c
34
36
  leptris/rng/validate.c
@@ -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, "simpleType") == 0 && uri && !strcmp(uri, XSD_NS)) {
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;