leptris 1.9.331.0 → 1.9.333.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fc1b0aba84d11a2bd956b152537aed516fc163092d95a1fe92775353205516d
4
- data.tar.gz: 7456448f5ebbe5a52fa55984f2456a4ccb1c7982255676bd60910abb4f1e795d
3
+ metadata.gz: b49c473e39e2876b99fafc9c9bf9c6025fe52640699d0eac17a7054a699fdb5d
4
+ data.tar.gz: 42f69045bf2379d40d51b8503bec81e7e7bbf5dbc5e89eaef959e7b6a15d5db6
5
5
  SHA512:
6
- metadata.gz: b6562c013f17c610ed0388ad0ad479a99d255856250211027e57aa1738b9f0b62380e05b3ddfd4bd5bda7e5049c6c98add28a85599e825ae12c7f7f58e147ded
7
- data.tar.gz: 5dfd4067930a755d13e1c7149d2c5de004bc2aa5e7205347c1d60701c32d6887f532d2fb2de161011d246c674b3028c0a1ebdbed6b8b941f7017cb301d926363
6
+ metadata.gz: fd6fcd375d425f8dcaf8069c83c9d169acc45dd60fff80e7ff3661b6d119b06f61d3285f79b788fe693ba061c884d06469639976d9b9ac807012a475d70a5217
7
+ data.tar.gz: f8145e11531fd836f617a233634ec23fefc5faca631d28fd475f281951cbeaf838f522fbc338157490932e80ec04fba2fa9f096fdecd87ce5439e5762470e3ed
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.331"
11
+ LIBLEPTRIS_VERSION = "1.9.333"
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.331.0"
4
+ VERSION = "1.9.333.0"
5
5
  end
@@ -31,4 +31,8 @@ class Leptris::XML::CDATA < Leptris::XML::Text
31
31
  Leptris::XML::FFI.leptris_cdata_node_set_content(c_ptr, new_content.to_s))
32
32
  new_content
33
33
  end
34
+
35
+ def to_s
36
+ "<![CDATA[#{content}]]>"
37
+ end
34
38
  end
@@ -31,4 +31,8 @@ class Leptris::XML::Comment < Leptris::XML::Node
31
31
  Leptris::XML::FFI.leptris_comment_node_set_content(c_ptr, new_content.to_s))
32
32
  new_content
33
33
  end
34
+
35
+ def to_s
36
+ "<!--#{content}-->"
37
+ end
34
38
  end
@@ -10,4 +10,8 @@ class Leptris::XML::EntityReference < Leptris::XML::Node
10
10
  Leptris::XML::FFI.leptris_entity_ref_node_name(c_ptr)
11
11
  end
12
12
  alias_method :node_name, :name
13
+
14
+ def to_s
15
+ "&#{name};"
16
+ end
13
17
  end
@@ -8,6 +8,7 @@ class Leptris::XML::Node
8
8
  @c_ptr ||= ::FFI::Pointer.new(@c_address)
9
9
  end
10
10
 
11
+
11
12
  # The Integer twin of #c_ptr — reading it never materializes a
12
13
  # Pointer (the native faces take addresses).
13
14
  def c_address
@@ -50,6 +50,10 @@ class Leptris::XML::ProcessingInstruction < Leptris::XML::Node
50
50
  new_data
51
51
  end
52
52
 
53
+ def to_s
54
+ content.to_s.empty? ? "<?#{name}?>" : "<?#{name} #{content}?>"
55
+ end
56
+
53
57
  # Document-level PIs have no tree parent for leptris_node_unlink
54
58
  # — route through the document-level removal (libleptris 1.9.9,
55
59
  # #612), identity-matched by index so the right same-target PI
@@ -36,4 +36,8 @@ class Leptris::XML::Text < Leptris::XML::Node
36
36
  Leptris::XML::FFI.leptris_text_node_set_content(c_ptr, new_content.to_s))
37
37
  new_content
38
38
  end
39
+
40
+ def to_s
41
+ Leptris::XML::Serialization.escape_text(content)
42
+ end
39
43
  end
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.331
4
+ - libleptris v1.9.333
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,41 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.333] - 2026-10-10
4
+
5
+ ### Added
6
+
7
+ - **Regression pin: union `memberTypes` + inline anonymous members
8
+ (#1615, PR #1618).** The reported over-acceptance was not
9
+ reproducible on any engine artifact (main, the v1.9.331 tag,
10
+ every published gem binary — including through the full Ruby
11
+ binding path). The exact schema now asserts in CI through both
12
+ `leptris_xsd_simple_valid` and instance validation, so the
13
+ mixed-member semantics are verifiable binary-independent of any
14
+ binding environment.
15
+
16
+
17
+ ## [1.9.332] - 2026-10-09
18
+
19
+ ### Added
20
+
21
+ - **XSD 1.1 — tier 2 (#1075, PR #1616).** Gated on
22
+ `xs:schema @version="1.1"`: `xs:assert` on complexTypes (direct
23
+ and derivation-nested; compiled once, evaluated per instance
24
+ through the engine's XPath 2 lane; evaluation errors are
25
+ failures); `xs:assertion` on simpleTypes (`$value` binds the
26
+ lexical, element context); `xs:openContent` interleave/suffix
27
+ (wildcard self-loops at the Thompson NFA's junction states;
28
+ `xs:defaultOpenContent` schema-wide with `appliesToEmpty`) and
29
+ `xs:alternative` conditional type assignment (first passing
30
+ test types the instance, declared type as fallback). Ten
31
+ red-first specs; ctest 1951/1951; 1.0 schemas unaffected.
32
+
33
+ ### Fixed
34
+
35
+ - free the defaultOpenContent wildcard with the schema (xsd)
36
+
37
+
38
+
3
39
  ## [1.9.331] - 2026-10-09
4
40
 
5
41
  ### Fixed
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.331
6
+ VERSION 1.9.333
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -36,11 +36,14 @@ struct leptris_xsd_schema {
36
36
  XsdElementDecl* elements;
37
37
  XsdCm* complex_types; /* name-carrying model roots */
38
38
  XsdGroupDef* groups; /* top-level xs:group definitions */
39
+ XsdCm* default_open; /* xs:defaultOpenContent wildcard */
40
+ int default_oc_empty; /* appliesToEmpty="true" */
39
41
  XsdAttrGroupDef* attr_groups;
40
42
  XsdAttrDecl* top_attrs; /* top-level xs:attribute declarations */
41
43
  XsdTypeAttrs* type_attrs;
42
44
  XsdElementIcs* element_ics;
43
45
  char* target_ns;
46
+ int version_11; /* xs:schema @version = "1.1" */
44
47
  char* base_dir; /* schemaLocation resolution (file compile) */
45
48
  int include_depth; /* include/import recursion guard */
46
49
  void* validator; /* slice-4 parked error list */
@@ -80,6 +83,12 @@ static void xsd_register_complex(struct leptris_xsd_schema* s,
80
83
  static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
81
84
  LeptrisElement ct,
82
85
  const char* name);
86
+ static XsdAssert* xsd_capture_assert(struct leptris_xsd_schema* s,
87
+ LeptrisElement e);
88
+ static XsdCm* xsd_capture_any(LeptrisElement any_child);
89
+ static void xsd_capture_alternatives(struct leptris_xsd_schema* s,
90
+ LeptrisElement decl_elem,
91
+ XsdElementDecl* d);
83
92
  static XsdSimple* xsd_capture_simple(LeptrisElement st);
84
93
 
85
94
 
@@ -95,6 +104,15 @@ static XsdCm* xsd_cm_new(XsdCmKind kind) {
95
104
  static void xsd_cm_free(XsdCm* cm) {
96
105
  while (cm) {
97
106
  XsdCm* next = cm->next;
107
+ XsdAssert* a = cm->asserts;
108
+ while (a) {
109
+ XsdAssert* an = a->next;
110
+ if (a->compiled) leptris_xquery_free(a->compiled);
111
+ free(a->test);
112
+ free(a);
113
+ a = an;
114
+ }
115
+ if (cm->oc_owned && cm->open_any) xsd_cm_free(cm->open_any);
98
116
  xsd_cm_free(cm->first_child);
99
117
  free(cm->name);
100
118
  free(cm->type);
@@ -370,6 +388,9 @@ static XsdSimple* xsd_capture_simple(LeptrisElement st) {
370
388
  simple->fraction_digits = val ? strtol(val, NULL, 10) : -1;
371
389
  else if (strcmp(fl, "whiteSpace") == 0)
372
390
  simple->whitespace = xsd_strdup(val);
391
+ else if (strcmp(fl, "assertion") == 0)
392
+ simple->assertion = xsd_strdup(
393
+ leptris_element_attribute(fe, "test"));
373
394
  }
374
395
  }
375
396
  }
@@ -481,6 +502,7 @@ static int xsd_is_declaration(const char* local, const char* uri) {
481
502
  static const char* names[] = {
482
503
  "element", "attribute", "simpleType", "complexType",
483
504
  "group", "attributeGroup", "notation", "include",
505
+ "defaultOpenContent",
484
506
  "import", "redefine", NULL
485
507
  };
486
508
  for (int i = 0; names[i]; i++)
@@ -594,6 +616,10 @@ static LeptrisXsdSchema xsd_compile_impl(const char* xsd_text,
594
616
  }
595
617
  s->target_ns = xsd_strdup(
596
618
  leptris_element_attribute(root, "targetNamespace"));
619
+ {
620
+ const char* ver = leptris_element_attribute(root, "version");
621
+ s->version_11 = (ver && strcmp(ver, "1.1") == 0);
622
+ }
597
623
 
598
624
  const char* local = NULL;
599
625
  const char* prefix = NULL;
@@ -692,6 +718,7 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
692
718
  if (d) {
693
719
  d->name = xsd_strdup(nm);
694
720
  d->type = xsd_strdup(ty);
721
+ xsd_capture_alternatives(s, (LeptrisElement)n, d);
695
722
  d->fixed = xsd_strdup(
696
723
  leptris_element_attribute((LeptrisElement)n,
697
724
  "fixed"));
@@ -730,6 +757,29 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
730
757
  leptris_element_attribute((LeptrisElement)n, "name");
731
758
  if (nm)
732
759
  xsd_capture_complex_model(s, (LeptrisElement)n, nm);
760
+ } else if (strcmp(local, "defaultOpenContent") == 0 && uri &&
761
+ !strcmp(uri, XSD_NS)) {
762
+ if (s->version_11) {
763
+ const char* ape = leptris_element_attribute(
764
+ (LeptrisElement)n, "appliesToEmpty");
765
+ for (LeptrisNodeRef q = leptris_node_first_child(
766
+ leptris_element_as_node((LeptrisElement)n));
767
+ q && !s->default_open;
768
+ q = leptris_node_next_sibling(q)) {
769
+ if (leptris_node_get_type(q) !=
770
+ LEPTRIS_NODE_TYPE_ELEMENT)
771
+ continue;
772
+ LeptrisElement qe = (LeptrisElement)q;
773
+ const char* ql = NULL;
774
+ leptris_element_expanded_name(qe, &ql, NULL, NULL);
775
+ if (ql && strcmp(ql, "any") == 0)
776
+ s->default_open = xsd_capture_any(qe);
777
+ }
778
+ if (s->default_open)
779
+ s->default_oc_empty =
780
+ (ape && strcmp(ape, "true") == 0);
781
+ }
782
+ continue;
733
783
  } else if (strcmp(local, "attribute") == 0 && uri && !strcmp(uri, XSD_NS)) {
734
784
  const char* nm =
735
785
  leptris_element_attribute((LeptrisElement)n, "name");
@@ -803,6 +853,8 @@ static void xsd_free_simple_one(XsdSimple* t) {
803
853
  free(t->max_inclusive);
804
854
  free(t->max_exclusive);
805
855
  free(t->whitespace);
856
+ if (t->assertion_q) leptris_xquery_free(t->assertion_q);
857
+ free(t->assertion);
806
858
  free(t->item_type);
807
859
  xsd_free_simple_one(t->item_def);
808
860
  for (size_t i = 0; i < t->member_count; i++) free(t->members[i]);
@@ -854,6 +906,15 @@ LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
854
906
  XsdElementDecl* d = s->elements;
855
907
  while (d) {
856
908
  XsdElementDecl* dn = d->next;
909
+ XsdAlternative* a = d->alternatives;
910
+ while (a) {
911
+ XsdAlternative* an = a->next;
912
+ if (a->compiled) leptris_xquery_free(a->compiled);
913
+ free(a->test);
914
+ free(a->type);
915
+ free(a);
916
+ a = an;
917
+ }
857
918
  free(d->name);
858
919
  free(d->type);
859
920
  free(d->fixed);
@@ -894,6 +955,7 @@ LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
894
955
  free(g);
895
956
  g = gn;
896
957
  }
958
+ if (s->default_open) xsd_cm_free(s->default_open);
897
959
  free(s->target_ns);
898
960
  free(s->base_dir);
899
961
  if (s->validator)
@@ -952,6 +1014,90 @@ static void xsd_register_complex(struct leptris_xsd_schema* s,
952
1014
  }
953
1015
  }
954
1016
 
1017
+ /* XSD 1.1: xs:alternative children of an element declaration. */
1018
+ static void xsd_capture_alternatives(struct leptris_xsd_schema* s,
1019
+ LeptrisElement decl_elem,
1020
+ XsdElementDecl* d) {
1021
+ if (!s->version_11) return;
1022
+ XsdAlternative* tail = NULL;
1023
+ for (LeptrisNodeRef n =
1024
+ leptris_node_first_child(leptris_element_as_node(decl_elem));
1025
+ n; n = leptris_node_next_sibling(n)) {
1026
+ if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
1027
+ continue;
1028
+ LeptrisElement e = (LeptrisElement)n;
1029
+ const char* local = NULL;
1030
+ leptris_element_expanded_name(e, &local, NULL, NULL);
1031
+ if (!local || strcmp(local, "alternative") != 0) continue;
1032
+ const char* test = leptris_element_attribute(e, "test");
1033
+ const char* type = leptris_element_attribute(e, "type");
1034
+ if (!test || !type) continue;
1035
+ XsdAlternative* a = (XsdAlternative*)calloc(1, sizeof(*a));
1036
+ if (!a) continue;
1037
+ a->test = xsd_strdup(test);
1038
+ a->type = xsd_strdup(type);
1039
+ a->compiled = leptris_xquery_parse(test, strlen(test));
1040
+ if (tail)
1041
+ tail->next = a;
1042
+ else
1043
+ d->alternatives = a;
1044
+ tail = a;
1045
+ }
1046
+ }
1047
+
1048
+ static XsdCm* xsd_capture_any(LeptrisElement any_child) {
1049
+ XsdCm* w = xsd_cm_new(XSD_CM_ANY);
1050
+ if (!w) return NULL;
1051
+ w->any_ns = xsd_strdup(
1052
+ leptris_element_attribute(any_child, "namespace"));
1053
+ const char* pc = leptris_element_attribute(any_child,
1054
+ "processContents");
1055
+ w->process_skip =
1056
+ (pc && (strcmp(pc, "skip") == 0 || strcmp(pc, "lax") == 0))
1057
+ ? 1
1058
+ : 0;
1059
+ w->min = 0;
1060
+ w->max = -1;
1061
+ return w;
1062
+ }
1063
+
1064
+ static XsdAssert* xsd_capture_assert(struct leptris_xsd_schema* s,
1065
+ LeptrisElement e) {
1066
+ const char* test = leptris_element_attribute(e, "test");
1067
+ if (!test || !*test) return NULL;
1068
+ XsdAssert* a = (XsdAssert*)calloc(1, sizeof(*a));
1069
+ if (!a) return NULL;
1070
+ a->test = xsd_strdup(test);
1071
+ if (s->version_11) {
1072
+ a->compiled = leptris_xquery_parse(test, strlen(test));
1073
+ /* NULL = syntax error: evaluation reports the assertion
1074
+ * as failed (XSD 1.1: an error is a failure) */
1075
+ }
1076
+ return a;
1077
+ }
1078
+
1079
+ /* Append every xs:assert child under `container` to the type. */
1080
+ static void xsd_capture_assert_children(
1081
+ struct leptris_xsd_schema* s, LeptrisElement container,
1082
+ XsdCm* root) {
1083
+ XsdAssert* tail = root->asserts;
1084
+ while (tail && tail->next) tail = tail->next;
1085
+ for (LeptrisNodeRef n =
1086
+ leptris_node_first_child(leptris_element_as_node(container));
1087
+ n; n = leptris_node_next_sibling(n)) {
1088
+ if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
1089
+ continue;
1090
+ LeptrisElement e = (LeptrisElement)n;
1091
+ const char* local = NULL;
1092
+ leptris_element_expanded_name(e, &local, NULL, NULL);
1093
+ if (!local || strcmp(local, "assert") != 0) continue;
1094
+ XsdAssert* a = xsd_capture_assert(s, e);
1095
+ if (!a) continue;
1096
+ if (!root->asserts) root->asserts = a;
1097
+ tail = a;
1098
+ }
1099
+ }
1100
+
955
1101
  /* One complexType capture, shared by top-level declarations and
956
1102
  * inline anonymous types: content model + derivation
957
1103
  * (complexContent/simpleContent extension|restriction) + the
@@ -1026,6 +1172,9 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
1026
1172
  root->first_child = xsd_cm_parse(s, dq, NULL);
1027
1173
  attrs = xsd_capture_attr_rows(s, dq, 0);
1028
1174
  }
1175
+ /* XSD 1.1: asserts nested under the derivation */
1176
+ if (s->version_11)
1177
+ xsd_capture_assert_children(s, dq, root);
1029
1178
  }
1030
1179
  } else if (strcmp(local, "simpleContent") == 0) {
1031
1180
  for (LeptrisNodeRef q = leptris_node_first_child(
@@ -1093,6 +1242,50 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
1093
1242
  root->first_child = xsd_cm_parse(s, ct, NULL);
1094
1243
  if (!attrs) attrs = xsd_capture_attr_rows(s, ct, 0);
1095
1244
  }
1245
+ /* XSD 1.1: openContent on the type, else the schema default */
1246
+ if (s->version_11) {
1247
+ for (LeptrisNodeRef n =
1248
+ leptris_node_first_child(leptris_element_as_node(ct));
1249
+ n; n = leptris_node_next_sibling(n)) {
1250
+ if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
1251
+ continue;
1252
+ LeptrisElement e = (LeptrisElement)n;
1253
+ const char* ocl = NULL;
1254
+ leptris_element_expanded_name(e, &ocl, NULL, NULL);
1255
+ if (!ocl || strcmp(ocl, "openContent") != 0) continue;
1256
+ const char* mode = leptris_element_attribute(e, "mode");
1257
+ for (LeptrisNodeRef q = leptris_node_first_child(
1258
+ leptris_element_as_node(e));
1259
+ q && !root->open_any;
1260
+ q = leptris_node_next_sibling(q)) {
1261
+ if (leptris_node_get_type(q) !=
1262
+ LEPTRIS_NODE_TYPE_ELEMENT)
1263
+ continue;
1264
+ LeptrisElement qe = (LeptrisElement)q;
1265
+ const char* ql = NULL;
1266
+ leptris_element_expanded_name(qe, &ql, NULL, NULL);
1267
+ if (ql && strcmp(ql, "any") == 0) {
1268
+ root->open_any = xsd_capture_any(qe);
1269
+ if (root->open_any) root->oc_owned = 1;
1270
+ }
1271
+ }
1272
+ if (mode && strcmp(mode, "suffix") == 0)
1273
+ root->oc_mode = 1;
1274
+ else if (mode && strcmp(mode, "interleave") == 0)
1275
+ root->oc_mode = 2;
1276
+ else
1277
+ root->oc_mode = 0;
1278
+ }
1279
+ }
1280
+ if (!root->oc_mode && s->default_open && root->first_child)
1281
+ root->oc_mode = 2;
1282
+ if (!root->oc_mode && s->default_open && !root->first_child &&
1283
+ s->default_oc_empty)
1284
+ root->oc_mode = 2;
1285
+ if (root->oc_mode == 2 && !root->open_any && s->default_open)
1286
+ root->open_any = s->default_open;
1287
+ /* XSD 1.1: direct xs:assert children of the complexType */
1288
+ if (s->version_11) xsd_capture_assert_children(s, ct, root);
1096
1289
  /* the NFA and model walkers consume ONE root particle —
1097
1290
  * a multi-particle model (derivation splices) normalizes
1098
1291
  * under a sequence */
@@ -1103,6 +1296,18 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
1103
1296
  root->first_child = seq;
1104
1297
  }
1105
1298
  }
1299
+ /* open-content semantics ride the model root (the NFA builds
1300
+ * from ct->first_child, never the name wrapper) */
1301
+ if (root->first_child) {
1302
+ root->first_child->open_any = root->open_any;
1303
+ root->first_child->oc_mode = root->oc_mode;
1304
+ root->first_child->oc_owned = root->oc_owned;
1305
+ root->open_any = NULL;
1306
+ root->oc_owned = 0;
1307
+ } else if (root->open_any && root->oc_owned) {
1308
+ xsd_cm_free(root->open_any);
1309
+ root->open_any = NULL;
1310
+ }
1106
1311
  xsd_register_complex(s, root, attrs, name);
1107
1312
  return root;
1108
1313
  }
@@ -1124,6 +1329,10 @@ int xsd_is_substitute(struct leptris_xsd_schema* s,
1124
1329
  return 0;
1125
1330
  }
1126
1331
 
1332
+ int xsd_schema_version_11(struct leptris_xsd_schema* s) {
1333
+ return s ? s->version_11 : 0;
1334
+ }
1335
+
1127
1336
  XsdGroupDef* xsd_find_group(struct leptris_xsd_schema* s,
1128
1337
  const char* name) {
1129
1338
  if (!s || !name) return NULL;
@@ -56,20 +56,35 @@ static void cm_build_occurrence(CmNfa* n, XsdCm* node,
56
56
  /* chain the group children: first reads `from`, last writes `to`.
57
57
  * xs:all is evaluated in sequence order for now (its order-free
58
58
  * semantics arrive with slice 4's all-semantics pass). */
59
+ /* XSD 1.1 open content: a wildcard self-loop at a junction lets
60
+ * any-namespace elements (per the wildcard's grammar) consume a
61
+ * child without advancing the model. */
62
+ static void oc_loop(CmNfa* n, XsdCm* node, int state) {
63
+ if (!node->open_any || !node->oc_mode) return;
64
+ nfa_trans(n, state, 0, node->open_any, state);
65
+ }
66
+
59
67
  static void cm_build_children(CmNfa* n, XsdCm* node, int from, int to) {
60
68
  XsdCm* c = node->first_child;
61
69
  if (!c) {
62
70
  nfa_trans(n, from, -1, NULL, to);
63
71
  return;
64
72
  }
73
+ int interleave =
74
+ (node->open_any && node->oc_mode == 2) ? 1 : 0;
65
75
  int cursor = from;
76
+ if (interleave) oc_loop(n, node, cursor);
66
77
  while (c->next) {
67
78
  int mid = nfa_state(n);
68
79
  cm_build_occurrence(n, c, cursor, mid);
69
80
  cursor = mid;
81
+ if (interleave) oc_loop(n, node, cursor);
70
82
  c = c->next;
71
83
  }
72
84
  cm_build_occurrence(n, c, cursor, to);
85
+ /* suffix (and interleave) wildcards may follow the sequence */
86
+ if (node->open_any && node->oc_mode)
87
+ oc_loop(n, node, to);
73
88
  }
74
89
 
75
90
  static void cm_build(CmNfa* n, XsdCm* node, int from, int to) {
@@ -8,6 +8,7 @@
8
8
  * accumulate in the schema-owned error list — the RNG accessor
9
9
  * pattern: validate returns 1/0, errors enumerate every miss.
10
10
  */
11
+ #include <ctype.h>
11
12
  #include <stdio.h>
12
13
  #include <stdlib.h>
13
14
  #include <string.h>
@@ -50,6 +51,7 @@ static void verr(struct leptris_xsd_schema* s, const char* fmt,
50
51
  const char* a, const char* b);
51
52
  struct xsd_validator {
52
53
  struct leptris_xsd_schema* s;
54
+ LeptrisDocument doc; /* the instance (assertion evaluation) */
53
55
  struct xsd_error* errors;
54
56
  struct xsd_error* tail;
55
57
  size_t count;
@@ -89,13 +91,14 @@ static void verrf(struct xsd_validator* v, const char* what,
89
91
  static int text_valid(struct xsd_validator* v,
90
92
  struct leptris_xsd_schema* s, const char* type,
91
93
  const char* lexical, const char* what,
92
- const char* name) {
94
+ const char* name, LeptrisElement elem) {
93
95
  if (!type) return 1;
94
96
  int r;
97
+ const XsdSimple* st = NULL;
95
98
  if (strncmp(type, "xs:", 3) == 0)
96
99
  r = xsd_builtin_valid(type, lexical);
97
100
  else {
98
- const XsdSimple* st = xsd_find_simple_pub(s, type);
101
+ st = xsd_find_simple_pub(s, type);
99
102
  if (!st) return 1; /* unresolved: accept */
100
103
  r = xsd_simple_valid_chain(s, st, lexical);
101
104
  }
@@ -103,6 +106,61 @@ static int text_valid(struct xsd_validator* v,
103
106
  verrf(v, what, name, "lexical value does not match its type");
104
107
  return 0;
105
108
  }
109
+ /* XSD 1.1 xs:assertion facet (document validation only):
110
+ * $value binds the lexical — numeric lexicals bind bare,
111
+ * everything else as a string — and the context is the
112
+ * owning element. */
113
+ if (st && st->assertion && elem && v->doc) {
114
+ XsdSimple* mut = (XsdSimple*)st;
115
+ if (!mut->assertion_tried) {
116
+ mut->assertion_tried = 1;
117
+ if (xsd_schema_version_11(s)) {
118
+ /* eval_params binds DECLARED external variables —
119
+ * the test compiles under a $value declaration */
120
+ char wrapped[1024];
121
+ int w = snprintf(wrapped, sizeof(wrapped),
122
+ "declare variable $value external; %s",
123
+ mut->assertion);
124
+ if (w > 0 && (size_t)w < sizeof(wrapped))
125
+ mut->assertion_q =
126
+ leptris_xquery_parse(wrapped, (size_t)w);
127
+ }
128
+ }
129
+ if (mut->assertion_q) {
130
+ char sel[256];
131
+ int numeric = 1;
132
+ for (const char* p = lexical; *p; p++)
133
+ if (!isdigit((unsigned char)*p) && *p != '.' &&
134
+ *p != '-' && *p != '+' && *p != 'e' && *p != 'E')
135
+ numeric = 0;
136
+ if (numeric && *lexical)
137
+ snprintf(sel, sizeof(sel), "%s", lexical);
138
+ else {
139
+ size_t w = 0;
140
+ sel[w++] = '\'';
141
+ for (const char* p = lexical; *p && w < sizeof(sel) - 3;
142
+ p++) {
143
+ if (*p == '\'') sel[w++] = '\'';
144
+ sel[w++] = *p;
145
+ }
146
+ sel[w++] = '\'';
147
+ sel[w] = 0;
148
+ }
149
+ const char* names[1] = {"value"};
150
+ const char* sels[1] = {sel};
151
+ LeptrisXPathResult r2 = leptris_xquery_eval_params(
152
+ mut->assertion_q, v->doc, elem, names, sels, 1);
153
+ int ebv = 0;
154
+ if (r2) {
155
+ ebv = leptris_xpath_result_boolean(r2);
156
+ leptris_xpath_result_free(r2);
157
+ }
158
+ if (!ebv) {
159
+ verrf(v, what, name, "assertion failed");
160
+ return 0;
161
+ }
162
+ }
163
+ }
106
164
  return 1;
107
165
  }
108
166
 
@@ -231,7 +289,7 @@ static void validate_attributes(struct xsd_validator* v,
231
289
  verrf(v, "attribute", a->name, "required but absent");
232
290
  continue;
233
291
  }
234
- text_valid(v, s, a->type, val, "attribute", a->name);
292
+ text_valid(v, s, a->type, val, "attribute", a->name, elem);
235
293
  if (a->fixed && strcmp(val, a->fixed) != 0)
236
294
  verrf(v, "attribute", a->name,
237
295
  "value does not match the fixed value");
@@ -248,14 +306,32 @@ static void validate_element(struct xsd_validator* v,
248
306
  const XsdElementDecl* d = xsd_find_element(s, local);
249
307
  if (!d) return; /* undeclared: lax at this depth (strict later) */
250
308
 
251
- if (!d->type) return; /* anyType */
309
+ /* XSD 1.1: the first alternative whose test passes types the
310
+ * element for this instance (context = the element). */
311
+ const char* eff_type = d->type;
312
+ for (XsdAlternative* alt = d->alternatives; alt; alt = alt->next) {
313
+ int ebv = 0;
314
+ if (alt->compiled && v->doc) {
315
+ LeptrisXPathResult r =
316
+ leptris_xquery_eval(alt->compiled, v->doc, elem);
317
+ if (r) {
318
+ ebv = leptris_xpath_result_boolean(r);
319
+ leptris_xpath_result_free(r);
320
+ }
321
+ }
322
+ if (ebv) {
323
+ eff_type = alt->type;
324
+ break;
325
+ }
326
+ }
327
+ if (!eff_type) return; /* anyType */
252
328
  if (d->fixed) {
253
329
  const char* text = leptris_element_text(elem);
254
330
  if (text && *text && strcmp(text, d->fixed) != 0)
255
331
  verrf(v, "element", local,
256
332
  "content does not match the fixed value");
257
333
  }
258
- validate_local(v, s, elem, local, d->type);
334
+ validate_local(v, s, elem, local, eff_type);
259
335
  }
260
336
 
261
337
  /* Validate one element against a type name that comes from a
@@ -270,11 +346,27 @@ static void validate_local(struct xsd_validator* v,
270
346
  if (ct) {
271
347
  validate_attributes(v, s, elem, xsd_find_type_attrs(s, type));
272
348
  validate_children(v, s, elem, ct);
349
+ /* XSD 1.1: the type's xs:assert list — context is the
350
+ * element; evaluation errors are failures */
351
+ for (XsdAssert* a = ct->asserts; a; a = a->next) {
352
+ int ebv = 0;
353
+ if (a->compiled && v->doc) {
354
+ LeptrisXPathResult r =
355
+ leptris_xquery_eval(a->compiled, v->doc, elem);
356
+ if (r) {
357
+ ebv = leptris_xpath_result_boolean(r);
358
+ leptris_xpath_result_free(r);
359
+ }
360
+ }
361
+ if (!ebv)
362
+ verrf(v, "assertion", local, a->test ? a->test
363
+ : "(invalid)");
364
+ }
273
365
  if (ct->text_type) {
274
366
  const char* text = leptris_element_text(elem);
275
367
  if (text && *text)
276
368
  text_valid(v, s, ct->text_type, text, "element",
277
- local);
369
+ local, elem);
278
370
  }
279
371
  return;
280
372
  }
@@ -284,7 +376,7 @@ static void validate_local(struct xsd_validator* v,
284
376
  xsd_find_simple_pub(s, type)) {
285
377
  const char* text = leptris_element_text(elem);
286
378
  if (text && *text)
287
- text_valid(v, s, type, text, "element", local);
379
+ text_valid(v, s, type, text, "element", local, elem);
288
380
  }
289
381
  }
290
382
 
@@ -509,6 +601,7 @@ void xsd_validator_free(struct xsd_validator* v) {
509
601
 
510
602
  int xsd_validator_run(struct xsd_validator* v, LeptrisDocument doc) {
511
603
  if (!v || !doc) return -1;
604
+ v->doc = doc; /* XSD 1.1: assertion evaluation context */
512
605
  LeptrisElement root = leptris_document_root(doc);
513
606
  if (!root) return -1;
514
607
  validate_element(v, v->s, root, 0);
@@ -5,6 +5,8 @@
5
5
 
6
6
  #include <stddef.h>
7
7
 
8
+ #include "../../include/leptris/xquery/xquery.h"
9
+
8
10
  #ifdef __cplusplus
9
11
  extern "C" {
10
12
  #endif
@@ -57,6 +59,9 @@ typedef struct xsd_simple {
57
59
  long total_digits;
58
60
  long fraction_digits;
59
61
  char* whitespace; /* "preserve" | "replace" | "collapse" */
62
+ char* assertion; /* XSD 1.1: the xs:assertion test text */
63
+ LeptrisXQuery assertion_q; /* compiled lazily at first use */
64
+ int assertion_tried; /* lazy-compile guard */
60
65
  struct xsd_simple* next;
61
66
  } XsdSimple;
62
67
 
@@ -65,6 +70,15 @@ typedef struct xsd_simple {
65
70
  int xsd_simple_valid_chain(struct leptris_xsd_schema* s,
66
71
  const XsdSimple* t, const char* v);
67
72
 
73
+ /* XSD 1.1 (#1075 tier 2): one xs:assert bound to a complexType —
74
+ * the compiled XPath 2 test plus its source text. Evaluation
75
+ * errors count as assertion failure. */
76
+ typedef struct xsd_assert {
77
+ char* test; /* the wire expression, for reporting */
78
+ LeptrisXQuery compiled; /* NULL when compilation failed */
79
+ struct xsd_assert* next;
80
+ } XsdAssert;
81
+
68
82
  /* Content-model node: a particle (element ref / wildcard) or a
69
83
  * group (sequence | choice | all). Owned by the schema; freed
70
84
  * with the schema. */
@@ -88,8 +102,14 @@ typedef struct xsd_cm {
88
102
  struct xsd_cm* first_child; /* group children list */
89
103
  struct xsd_cm* next; /* sibling link within the parent group */
90
104
  char* text_type; /* simpleContent wrapper: the text's type ref */
105
+ XsdAssert* asserts; /* XSD 1.1: the type's xs:assert list */
106
+ struct xsd_cm* open_any; /* XSD 1.1: xs:openContent wildcard */
107
+ int oc_mode; /* 0 none/unset, 1 suffix, 2 interleave */
108
+ int oc_owned; /* the wildcard node is this model's own */
91
109
  } XsdCm;
92
110
 
111
+
112
+
93
113
  /* A top-level xs:group definition: name + the model its refs
94
114
  * splice in (the definition's single sequence/choice/all). */
95
115
  typedef struct xsd_group_def {
@@ -98,15 +118,28 @@ typedef struct xsd_group_def {
98
118
  struct xsd_group_def* next;
99
119
  } XsdGroupDef;
100
120
 
121
+ /* XSD 1.1 (xs:schema @version="1.1") gates 1.1 enforcement. */
122
+ int xsd_schema_version_11(struct leptris_xsd_schema* s);
123
+
101
124
  /* Group refs resolve through the schema at NFA-build time —
102
125
  * forward references need no capture order. */
103
126
  XsdGroupDef* xsd_find_group(struct leptris_xsd_schema* s,
104
127
  const char* name);
105
128
 
129
+ /* XSD 1.1 (#1075 tier 2): one xs:alternative on an element
130
+ * declaration — the test plus the type it selects. */
131
+ typedef struct xsd_alternative {
132
+ char* test; /* wire text, for reporting */
133
+ LeptrisXQuery compiled; /* NULL when compilation failed */
134
+ char* type; /* the selected type name */
135
+ struct xsd_alternative* next;
136
+ } XsdAlternative;
137
+
106
138
  /* A top-level xs:element declaration (slice 3: name + type ref). */
107
139
  typedef struct xsd_element_decl {
108
140
  char* name;
109
141
  char* type; /* "xs:string"-style builtin or a local type name */
142
+ XsdAlternative* alternatives; /* XSD 1.1 conditional types */
110
143
  char* fixed; /* fixed element content: text must string-equal it */
111
144
  char* sub_head; /* substitutionGroup head (local name after ':') */
112
145
  struct xsd_element_decl* next;
@@ -1119,3 +1119,280 @@ TEST(XsdCompileFile, MissingFileIsNullWithStatus) {
1119
1119
  }
1120
1120
 
1121
1121
  } // namespace
1122
+
1123
+ // ---- tier 2 (#1075): xs:assert --------------------------------------
1124
+ namespace {
1125
+
1126
+ const char* k_assert_schema =
1127
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
1128
+ " version=\"1.1\">"
1129
+ "<xs:complexType name=\"boxT\">"
1130
+ "<xs:sequence>"
1131
+ "<xs:element name=\"item\" type=\"xs:token\" minOccurs=\"0\""
1132
+ " maxOccurs=\"unbounded\"/>"
1133
+ "</xs:sequence>"
1134
+ "<xs:attribute name=\"qty\" type=\"xs:integer\"/>"
1135
+ "<xs:assert test=\"count(item) &lt;= 2\"/>"
1136
+ "</xs:complexType>"
1137
+ "<xs:element name=\"box\" type=\"boxT\"/>"
1138
+ "</xs:schema>";
1139
+
1140
+ TEST(XsdAssert11, ComplexTypeAssertionEnforces) {
1141
+ EXPECT_EQ(validate_doc(k_assert_schema,
1142
+ "<box qty=\"1\"><item>a</item>"
1143
+ "</box>"), 1);
1144
+ EXPECT_EQ(validate_doc(k_assert_schema,
1145
+ "<box qty=\"1\"><item>a</item><item>b</item>"
1146
+ "</box>"), 1);
1147
+ /* the assertion fails at three items */
1148
+ EXPECT_EQ(validate_doc(k_assert_schema,
1149
+ "<box qty=\"1\"><item>a</item><item>b</item>"
1150
+ "<item>c</item></box>"), 0);
1151
+ /* attributes ride the assertion context too */
1152
+ EXPECT_EQ(validate_doc(k_assert_schema,
1153
+ "<box qty=\"9\"><item>a</item>"
1154
+ "<item>b</item><item>c</item></box>"), 0);
1155
+ }
1156
+
1157
+ const char* k_assert_attr_schema =
1158
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
1159
+ " version=\"1.1\">"
1160
+ "<xs:complexType name=\"pairT\">"
1161
+ "<xs:attribute name=\"lo\" type=\"xs:integer\"/>"
1162
+ "<xs:attribute name=\"hi\" type=\"xs:integer\"/>"
1163
+ "<xs:assert test=\"number(@lo) &lt;= number(@hi)\"/>"
1164
+ "</xs:complexType>"
1165
+ "<xs:element name=\"p\" type=\"pairT\"/>"
1166
+ "</xs:schema>";
1167
+
1168
+ TEST(XsdAssert11, AttributeComparisonAssertion) {
1169
+ EXPECT_EQ(validate_doc(k_assert_attr_schema,
1170
+ "<p lo=\"1\" hi=\"3\"/>"), 1);
1171
+ EXPECT_EQ(validate_doc(k_assert_attr_schema,
1172
+ "<p lo=\"4\" hi=\"3\"/>"), 0);
1173
+ }
1174
+
1175
+ /* assertions nested under complexContent/extension also bind */
1176
+ TEST(XsdAssert11, ExtensionAssertionEnforces) {
1177
+ const char* x =
1178
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
1179
+ " version=\"1.1\">"
1180
+ "<xs:complexType name=\"baseT\">"
1181
+ "<xs:sequence><xs:element name=\"a\" type=\"xs:token\"/>"
1182
+ "</xs:sequence>"
1183
+ "</xs:complexType>"
1184
+ "<xs:complexType name=\"extT\">"
1185
+ "<xs:complexContent>"
1186
+ "<xs:extension base=\"baseT\">"
1187
+ "<xs:sequence><xs:element name=\"b\" type=\"xs:token\"/>"
1188
+ "</xs:sequence>"
1189
+ "<xs:assert test=\"a = b\"/>"
1190
+ "</xs:extension>"
1191
+ "</xs:complexContent>"
1192
+ "</xs:complexType>"
1193
+ "<xs:element name=\"e\" type=\"extT\"/>"
1194
+ "</xs:schema>";
1195
+ EXPECT_EQ(validate_doc(x, "<e><a>t</a><b>t</b></e>"), 1);
1196
+ EXPECT_EQ(validate_doc(x, "<e><a>t</a><b>u</b></e>"), 0);
1197
+ }
1198
+
1199
+ /* 1.0-labelled schemas ignore the construct (XSD 1.0 validators
1200
+ * see unknown elements) — no assertion enforcement */
1201
+ TEST(XsdAssert11, Version10IgnoresAssertions) {
1202
+ const char* x =
1203
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
1204
+ " version=\"1.0\">"
1205
+ "<xs:complexType name=\"boxT\">"
1206
+ "<xs:sequence>"
1207
+ "<xs:element name=\"item\" type=\"xs:token\" minOccurs=\"0\""
1208
+ " maxOccurs=\"unbounded\"/>"
1209
+ "</xs:sequence>"
1210
+ "<xs:assert test=\"count(item) &lt;= 1\"/>"
1211
+ "</xs:complexType>"
1212
+ "<xs:element name=\"box\" type=\"boxT\"/>"
1213
+ "</xs:schema>";
1214
+ EXPECT_EQ(validate_doc(x,
1215
+ "<box><item>a</item><item>b</item></box>"), 1);
1216
+ }
1217
+
1218
+ } // namespace
1219
+
1220
+ // ---- tier 2 (#1075): xs:assertion on simpleTypes -------------------
1221
+ namespace {
1222
+
1223
+ const char* k_sa_schema =
1224
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
1225
+ " version=\"1.1\">"
1226
+ "<xs:simpleType name=\"small\">"
1227
+ "<xs:restriction base=\"xs:integer\">"
1228
+ "<xs:assertion test=\"$value &lt; 10\"/>"
1229
+ "</xs:restriction>"
1230
+ "</xs:simpleType>"
1231
+ "<xs:simpleType name=\"word\">"
1232
+ "<xs:restriction base=\"xs:string\">"
1233
+ "<xs:assertion test=\"string-length($value) &lt;= 3\"/>"
1234
+ "</xs:restriction>"
1235
+ "</xs:simpleType>"
1236
+ "<xs:element name=\"n\" type=\"small\"/>"
1237
+ "<xs:element name=\"w\" type=\"word\"/>"
1238
+ "</xs:schema>";
1239
+
1240
+ TEST(XsdAssertion11, SimpleTypeValueAssertions) {
1241
+ EXPECT_EQ(validate_doc(k_sa_schema, "<n>5</n>"), 1);
1242
+ EXPECT_EQ(validate_doc(k_sa_schema, "<n>50</n>"), 0);
1243
+ EXPECT_EQ(validate_doc(k_sa_schema, "<w>abc</w>"), 1);
1244
+ EXPECT_EQ(validate_doc(k_sa_schema, "<w>abcd</w>"), 0);
1245
+ }
1246
+
1247
+ } // namespace
1248
+
1249
+ // ---- tier 2 (#1075): open content ----------------------------------
1250
+ namespace {
1251
+
1252
+ const char* k_oc_interleave =
1253
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
1254
+ " version=\"1.1\">"
1255
+ "<xs:complexType name=\"tT\">"
1256
+ "<xs:sequence>"
1257
+ "<xs:element name=\"a\" type=\"xs:token\"/>"
1258
+ "<xs:element name=\"b\" type=\"xs:token\"/>"
1259
+ "</xs:sequence>"
1260
+ "<xs:openContent mode=\"interleave\">"
1261
+ "<xs:any namespace=\"##any\" processContents=\"lax\"/>"
1262
+ "</xs:openContent>"
1263
+ "</xs:complexType>"
1264
+ "<xs:element name=\"e\" type=\"tT\"/>"
1265
+ "</xs:schema>";
1266
+
1267
+ TEST(XsdOpenContent11, InterleavedWildcardsBindAnywhere) {
1268
+ /* the plain sequence */
1269
+ EXPECT_EQ(validate_doc(k_oc_interleave,
1270
+ "<e><a>1</a><b>2</b></e>"), 1);
1271
+ /* foreign element before, between, after */
1272
+ EXPECT_EQ(validate_doc(k_oc_interleave,
1273
+ "<e><q/><a>1</a><b>2</b></e>"), 1);
1274
+ EXPECT_EQ(validate_doc(k_oc_interleave,
1275
+ "<e><a>1</a><q/><b>2</b></e>"), 1);
1276
+ EXPECT_EQ(validate_doc(k_oc_interleave,
1277
+ "<e><a>1</a><b>2</b><q/></e>"), 1);
1278
+ /* the sequence itself still enforces order */
1279
+ EXPECT_EQ(validate_doc(k_oc_interleave,
1280
+ "<e><b>2</b><a>1</a></e>"), 0);
1281
+ }
1282
+
1283
+ const char* k_oc_suffix =
1284
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
1285
+ " version=\"1.1\">"
1286
+ "<xs:complexType name=\"tT\">"
1287
+ "<xs:sequence>"
1288
+ "<xs:element name=\"a\" type=\"xs:token\"/>"
1289
+ "</xs:sequence>"
1290
+ "<xs:openContent mode=\"suffix\">"
1291
+ "<xs:any namespace=\"##any\" processContents=\"lax\"/>"
1292
+ "</xs:openContent>"
1293
+ "</xs:complexType>"
1294
+ "<xs:element name=\"e\" type=\"tT\"/>"
1295
+ "</xs:schema>";
1296
+
1297
+ TEST(XsdOpenContent11, SuffixWildcardsBindOnlyAtTheEnd) {
1298
+ EXPECT_EQ(validate_doc(k_oc_suffix, "<e><a>1</a><q/></e>"), 1);
1299
+ EXPECT_EQ(validate_doc(k_oc_suffix, "<e><a>1</a><q/><r/></e>"), 1);
1300
+ EXPECT_EQ(validate_doc(k_oc_suffix, "<e><q/><a>1</a></e>"), 0);
1301
+ }
1302
+
1303
+ const char* k_oc_default =
1304
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
1305
+ " version=\"1.1\">"
1306
+ "<xs:defaultOpenContent mode=\"interleave\">"
1307
+ "<xs:any namespace=\"##any\" processContents=\"lax\"/>"
1308
+ "</xs:defaultOpenContent>"
1309
+ "<xs:complexType name=\"tT\">"
1310
+ "<xs:sequence>"
1311
+ "<xs:element name=\"a\" type=\"xs:token\"/>"
1312
+ "</xs:sequence>"
1313
+ "</xs:complexType>"
1314
+ "<xs:element name=\"e\" type=\"tT\"/>"
1315
+ "</xs:schema>";
1316
+
1317
+ TEST(XsdOpenContent11, DefaultOpenContentApplies) {
1318
+ EXPECT_EQ(validate_doc(k_oc_default, "<e><a>1</a></e>"), 1);
1319
+ EXPECT_EQ(validate_doc(k_oc_default, "<e><a>1</a><q/></e>"), 1);
1320
+ EXPECT_EQ(validate_doc(k_oc_default, "<e><q/><a>1</a></e>"), 1);
1321
+ }
1322
+
1323
+ } // namespace
1324
+
1325
+ // ---- tier 2 (#1075): conditional type assignment --------------
1326
+ namespace {
1327
+
1328
+ const char* k_alt_schema =
1329
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
1330
+ " version=\"1.1\">"
1331
+ "<xs:complexType name=\"circleT\">"
1332
+ "<xs:sequence><xs:element name=\"r\" type=\"xs:integer\"/>"
1333
+ "</xs:sequence>"
1334
+ "</xs:complexType>"
1335
+ "<xs:complexType name=\"squareT\">"
1336
+ "<xs:sequence><xs:element name=\"s\" type=\"xs:string\"/>"
1337
+ "</xs:sequence>"
1338
+ "</xs:complexType>"
1339
+ "<xs:complexType name=\"fallbackT\">"
1340
+ "<xs:sequence><xs:element name=\"u\" type=\"xs:token\"/>"
1341
+ "</xs:sequence>"
1342
+ "</xs:complexType>"
1343
+ "<xs:element name=\"shape\" type=\"fallbackT\">"
1344
+ "<xs:alternative test=\"@kind = 'circle'\" type=\"circleT\"/>"
1345
+ "<xs:alternative test=\"@kind = 'square'\" type=\"squareT\"/>"
1346
+ "</xs:element>"
1347
+ "</xs:schema>";
1348
+
1349
+ TEST(XsdAlternative11, FirstMatchingAlternativeTypes) {
1350
+ /* circle alternative */
1351
+ EXPECT_EQ(validate_doc(k_alt_schema,
1352
+ "<shape kind=\"circle\"><r>2</r></shape>"), 1);
1353
+ EXPECT_EQ(validate_doc(k_alt_schema,
1354
+ "<shape kind=\"circle\"><r>x</r></shape>"), 0);
1355
+ /* square alternative */
1356
+ EXPECT_EQ(validate_doc(k_alt_schema,
1357
+ "<shape kind=\"square\"><s>hi</s></shape>"), 1);
1358
+ EXPECT_EQ(validate_doc(k_alt_schema,
1359
+ "<shape kind=\"square\"><r>2</r></shape>"), 0);
1360
+ /* no alternative matches: the declared fallback type applies */
1361
+ EXPECT_EQ(validate_doc(k_alt_schema,
1362
+ "<shape kind=\"blob\"><u>t</u></shape>"), 1);
1363
+ EXPECT_EQ(validate_doc(k_alt_schema,
1364
+ "<shape kind=\"blob\"><r>2</r></shape>"), 0);
1365
+ }
1366
+
1367
+ } // namespace
1368
+
1369
+ /* #1615 regression pin: a union combining memberTypes with inline
1370
+ * anonymous members consults BOTH member sets (the issue reported
1371
+ * over-acceptance through a stale binding binary; the engine was
1372
+ * verified correct on main, the tag, and every published gem —
1373
+ * this pins the behavior in CI, binary-independent). */
1374
+ TEST(XsdDatatypes, UnionMixesNamedAndInlineMembers) {
1375
+ const char* x =
1376
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
1377
+ "<xs:simpleType name=\"intOrAuto\">"
1378
+ "<xs:union memberTypes=\"xs:integer\">"
1379
+ "<xs:simpleType>"
1380
+ "<xs:restriction base=\"xs:token\">"
1381
+ "<xs:enumeration value=\"auto\"/>"
1382
+ "</xs:restriction>"
1383
+ "</xs:simpleType>"
1384
+ "</xs:union>"
1385
+ "</xs:simpleType>"
1386
+ "<xs:element name=\"v\" type=\"intOrAuto\"/>"
1387
+ "</xs:schema>";
1388
+ LeptrisStatus st = LEPTRIS_OK;
1389
+ LeptrisXsdSchema s = leptris_xsd_compile(x, strlen(x), &st);
1390
+ ASSERT_NE(s, (LeptrisXsdSchema)0);
1391
+ EXPECT_EQ(leptris_xsd_simple_valid(s, "intOrAuto", "42"), 1);
1392
+ EXPECT_EQ(leptris_xsd_simple_valid(s, "intOrAuto", "auto"), 1);
1393
+ EXPECT_EQ(leptris_xsd_simple_valid(s, "intOrAuto", "zz"), 0);
1394
+ leptris_xsd_free(s);
1395
+ EXPECT_EQ(validate_doc(x, "<v>zz</v>"), 0);
1396
+ EXPECT_EQ(validate_doc(x, "<v>42</v>"), 1);
1397
+ EXPECT_EQ(validate_doc(x, "<v>auto</v>"), 1);
1398
+ }
@@ -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.331",
4
+ "version": "1.9.333",
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",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.331.0
4
+ version: 1.9.333.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-09 00:00:00.000000000 Z
11
+ date: 2026-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi