leptris 1.9.324.0-aarch64-linux → 1.9.331.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.
@@ -4,6 +4,7 @@
4
4
  * grow the model (datatypes, facets, content models, identity
5
5
  * constraints) — this slice's contract is the API shape, the
6
6
  * xs:schema identity check, and the declaration enumeration. */
7
+ #include <ctype.h>
7
8
  #include <stdio.h>
8
9
  #include <stdlib.h>
9
10
  #include <string.h>
@@ -34,9 +35,14 @@ struct leptris_xsd_schema {
34
35
  XsdSimple* simple_types;
35
36
  XsdElementDecl* elements;
36
37
  XsdCm* complex_types; /* name-carrying model roots */
38
+ XsdGroupDef* groups; /* top-level xs:group definitions */
39
+ XsdAttrGroupDef* attr_groups;
40
+ XsdAttrDecl* top_attrs; /* top-level xs:attribute declarations */
37
41
  XsdTypeAttrs* type_attrs;
38
42
  XsdElementIcs* element_ics;
39
43
  char* target_ns;
44
+ char* base_dir; /* schemaLocation resolution (file compile) */
45
+ int include_depth; /* include/import recursion guard */
40
46
  void* validator; /* slice-4 parked error list */
41
47
  char* error;
42
48
  };
@@ -56,6 +62,24 @@ static XsdCm* xsd_cm_new(XsdCmKind kind);
56
62
  static void xsd_cm_occurrence(XsdCm* cm, LeptrisElement e);
57
63
  static XsdCm* xsd_cm_parse(struct leptris_xsd_schema* s,
58
64
  LeptrisElement group, XsdCm** tail_out);
65
+ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
66
+ LeptrisElement container,
67
+ int depth);
68
+ extern XsdCm* xsd_find_complex(struct leptris_xsd_schema* s,
69
+ const char* type_name);
70
+ extern const XsdTypeAttrs* xsd_find_type_attrs(
71
+ struct leptris_xsd_schema* s, const char* type_name);
72
+ static XsdCm* xsd_cm_copy_list(const XsdCm* head);
73
+ static void xsd_capture_root(struct leptris_xsd_schema* s,
74
+ LeptrisElement root);
75
+ static char* xsd_read_file(const char* path, size_t* out_len);
76
+ static XsdAttrDecl* xsd_copy_attr_rows(const XsdAttrDecl* src);
77
+ static void xsd_register_complex(struct leptris_xsd_schema* s,
78
+ XsdCm* root, XsdAttrDecl* attrs,
79
+ const char* name);
80
+ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
81
+ LeptrisElement ct,
82
+ const char* name);
59
83
  static XsdSimple* xsd_capture_simple(LeptrisElement st);
60
84
 
61
85
 
@@ -76,6 +100,7 @@ static void xsd_cm_free(XsdCm* cm) {
76
100
  free(cm->type);
77
101
  free(cm->ns);
78
102
  free(cm->any_ns);
103
+ free(cm->text_type);
79
104
  free(cm);
80
105
  cm = next;
81
106
  }
@@ -107,13 +132,7 @@ static int xsd_capture_inline(struct leptris_xsd_schema* s,
107
132
  leptris_element_expanded_name(e, &local, &prefix, &uri);
108
133
  if (!local || !uri || strcmp(uri, XSD_NS) != 0) continue;
109
134
  if (strcmp(local, "complexType") == 0) {
110
- XsdCm* root = xsd_cm_new(XSD_CM_SEQ);
111
- if (!root) return 0;
112
- root->name = xsd_strdup(synth);
113
- root->first_child = xsd_cm_parse(s, e, NULL);
114
- root->next = s->complex_types;
115
- s->complex_types = root;
116
- return 1;
135
+ return xsd_capture_complex_model(s, e, synth) ? 1 : 0;
117
136
  }
118
137
  if (strcmp(local, "simpleType") == 0) {
119
138
  XsdSimple* captured = xsd_capture_simple(e);
@@ -168,6 +187,13 @@ static XsdCm* xsd_cm_parse(struct leptris_xsd_schema* s,
168
187
  cm->type = xsd_strdup(synth);
169
188
  }
170
189
  }
190
+ } else if (strcmp(local, "group") == 0) {
191
+ /* <xs:group ref="g"/> — expanded at NFA build so
192
+ * forward references need no capture order */
193
+ cm = xsd_cm_new(XSD_CM_GROUP_REF);
194
+ if (cm)
195
+ cm->name = xsd_strdup(
196
+ leptris_element_attribute(e, "ref"));
171
197
  } else if (strcmp(local, "any") == 0) {
172
198
  cm = xsd_cm_new(XSD_CM_ANY);
173
199
  if (cm) {
@@ -225,7 +251,81 @@ static XsdSimple* xsd_capture_simple(LeptrisElement st) {
225
251
  leptris_element_expanded_name(e, &local, &prefix, &uri);
226
252
  if (uri && strcmp(uri, XSD_NS) != 0) continue;
227
253
  if (!local) continue;
228
- if (strcmp(local, "restriction") == 0) {
254
+ if (strcmp(local, "list") == 0) {
255
+ simple->deriv = XSD_SIMPLE_LIST;
256
+ const char* it = leptris_element_attribute(e, "itemType");
257
+ if (it && *it) {
258
+ simple->item_type = xsd_strdup(it);
259
+ } else {
260
+ /* inline anonymous item type rides this capture */
261
+ for (LeptrisNodeRef q = leptris_node_first_child(
262
+ leptris_element_as_node(e));
263
+ q && !simple->item_def;
264
+ q = leptris_node_next_sibling(q)) {
265
+ if (leptris_node_get_type(q) !=
266
+ LEPTRIS_NODE_TYPE_ELEMENT)
267
+ continue;
268
+ LeptrisElement qe = (LeptrisElement)q;
269
+ const char* ql = NULL;
270
+ leptris_element_expanded_name(qe, &ql, NULL, NULL);
271
+ if (ql && strcmp(ql, "simpleType") == 0)
272
+ simple->item_def = xsd_capture_simple(qe);
273
+ }
274
+ }
275
+ } else if (strcmp(local, "union") == 0) {
276
+ simple->deriv = XSD_SIMPLE_UNION;
277
+ const char* mt =
278
+ leptris_element_attribute(e, "memberTypes");
279
+ if (mt && *mt) {
280
+ size_t n = 0;
281
+ for (const char* p = mt; *p;) {
282
+ while (*p && isspace((unsigned char)*p)) p++;
283
+ if (!*p) break;
284
+ while (*p && !isspace((unsigned char)*p)) p++;
285
+ n++;
286
+ }
287
+ if (n) {
288
+ simple->members = (char**)calloc(n, sizeof(char*));
289
+ if (simple->members) {
290
+ size_t i = 0;
291
+ const char* p = mt;
292
+ while (*p && i < n) {
293
+ while (*p && isspace((unsigned char)*p)) p++;
294
+ if (!*p) break;
295
+ const char* start = p;
296
+ while (*p && !isspace((unsigned char)*p)) p++;
297
+ size_t len = (size_t)(p - start);
298
+ char* tok = (char*)malloc(len + 1);
299
+ if (!tok) break;
300
+ memcpy(tok, start, len);
301
+ tok[len] = 0;
302
+ simple->members[i++] = tok;
303
+ }
304
+ simple->member_count = i;
305
+ }
306
+ }
307
+ }
308
+ /* inline anonymous members ride this capture */
309
+ for (LeptrisNodeRef q = leptris_node_first_child(
310
+ leptris_element_as_node(e));
311
+ q; q = leptris_node_next_sibling(q)) {
312
+ if (leptris_node_get_type(q) != LEPTRIS_NODE_TYPE_ELEMENT)
313
+ continue;
314
+ LeptrisElement qe = (LeptrisElement)q;
315
+ const char* ql = NULL;
316
+ leptris_element_expanded_name(qe, &ql, NULL, NULL);
317
+ if (!ql || strcmp(ql, "simpleType") != 0) continue;
318
+ XsdSimple* def = xsd_capture_simple(qe);
319
+ if (!def) continue;
320
+ XsdSimple** grown = (XsdSimple**)realloc(
321
+ simple->member_defs,
322
+ (simple->member_def_count + 1) *
323
+ sizeof(XsdSimple*));
324
+ if (!grown) break;
325
+ simple->member_defs = grown;
326
+ simple->member_defs[simple->member_def_count++] = def;
327
+ }
328
+ } else if (strcmp(local, "restriction") == 0) {
229
329
  simple->base =
230
330
  xsd_strdup(leptris_element_attribute(e, "base"));
231
331
  for (LeptrisNodeRef f = leptris_node_first_child(
@@ -287,6 +387,71 @@ static const XsdSimple* xsd_find_simple(struct leptris_xsd_schema* s,
287
387
  return NULL;
288
388
  }
289
389
 
390
+ extern int xsd_builtin_valid(const char* type, const char* v);
391
+ extern int rng_regex_matches(const char* pat, const char* text);
392
+ static int xsd_valid_chain(struct leptris_xsd_schema* s,
393
+ const XsdSimple* t, const char* v, int depth);
394
+
395
+ /* One list item or union member: a builtin reference, a local
396
+ * schema type (chain-walked), or an inline capture. Unresolved
397
+ * names accept (the compile-time leniency the corpus gate pins). */
398
+ static int xsd_item_valid(struct leptris_xsd_schema* s,
399
+ const char* name, const XsdSimple* def,
400
+ const char* v, int depth) {
401
+ if (name && strncmp(name, "xs:", 3) == 0)
402
+ return xsd_builtin_valid(name, v);
403
+ if (def) return xsd_valid_chain(s, def, v, depth + 1);
404
+ if (name) {
405
+ const XsdSimple* next = xsd_find_simple(s, name);
406
+ if (next) return xsd_valid_chain(s, next, v, depth + 1);
407
+ }
408
+ return 1;
409
+ }
410
+
411
+ static int xsd_deriv_valid(struct leptris_xsd_schema* s,
412
+ const XsdSimple* t, const char* v,
413
+ int depth) {
414
+ if (t->deriv == XSD_SIMPLE_LIST) {
415
+ char* dup = xsd_strdup(v);
416
+ if (!dup) return 0;
417
+ size_t n = 0;
418
+ int ok = 1;
419
+ for (char* p = dup; *p;) {
420
+ while (*p && isspace((unsigned char)*p)) p++;
421
+ if (!*p) break;
422
+ char* end = p;
423
+ while (*end && !isspace((unsigned char)*end)) end++;
424
+ char was = *end;
425
+ *end = 0;
426
+ if (ok && !xsd_item_valid(s, t->item_type, t->item_def, p,
427
+ depth))
428
+ ok = 0;
429
+ *end = was;
430
+ p = end;
431
+ n++;
432
+ }
433
+ free(dup);
434
+ /* length family counts ITEMS for lists (XSD semantics) */
435
+ if (ok && t->length >= 0 && (long)n != t->length) ok = 0;
436
+ if (ok && t->min_length >= 0 && (long)n < t->min_length) ok = 0;
437
+ if (ok && t->max_length >= 0 && (long)n > t->max_length) ok = 0;
438
+ /* pattern applies to the whole lexical */
439
+ if (ok && t->pattern && !rng_regex_matches(t->pattern, v))
440
+ ok = 0;
441
+ return ok;
442
+ }
443
+ if (t->deriv == XSD_SIMPLE_UNION) {
444
+ for (size_t i = 0; i < t->member_count; i++)
445
+ if (xsd_item_valid(s, t->members[i], NULL, v, depth))
446
+ return 1;
447
+ for (size_t i = 0; i < t->member_def_count; i++)
448
+ if (xsd_item_valid(s, NULL, t->member_defs[i], v, depth))
449
+ return 1;
450
+ return 0;
451
+ }
452
+ return xsd_simple_valid(t, v);
453
+ }
454
+
290
455
  static int xsd_valid_chain(struct leptris_xsd_schema* s,
291
456
  const XsdSimple* t, const char* v, int depth) {
292
457
  if (!t || depth > 32) return 0;
@@ -294,6 +459,8 @@ static int xsd_valid_chain(struct leptris_xsd_schema* s,
294
459
  const XsdSimple* next = xsd_find_simple(s, t->base);
295
460
  if (next && !xsd_valid_chain(s, next, v, depth + 1)) return 0;
296
461
  }
462
+ if (t->deriv != XSD_SIMPLE_RESTRICT)
463
+ return xsd_deriv_valid(s, t, v, depth);
297
464
  return xsd_simple_valid(t, v);
298
465
  }
299
466
 
@@ -321,9 +488,82 @@ static int xsd_is_declaration(const char* local, const char* uri) {
321
488
  return 0;
322
489
  }
323
490
 
491
+ static char* xsd_read_file(const char* path, size_t* out_len) {
492
+ *out_len = 0;
493
+ FILE* f = fopen(path, "rb");
494
+ if (!f) return NULL;
495
+ if (fseek(f, 0, SEEK_END) != 0) {
496
+ fclose(f);
497
+ return NULL;
498
+ }
499
+ long size = ftell(f);
500
+ if (size < 0 || fseek(f, 0, SEEK_SET) != 0) {
501
+ fclose(f);
502
+ return NULL;
503
+ }
504
+ char* buf = (char*)malloc((size_t)size + 1);
505
+ if (!buf) {
506
+ fclose(f);
507
+ return NULL;
508
+ }
509
+ size_t got = fread(buf, 1, (size_t)size, f);
510
+ fclose(f);
511
+ buf[got] = 0;
512
+ *out_len = got;
513
+ return buf;
514
+ }
515
+
516
+ static LeptrisXsdSchema xsd_compile_impl(const char* xsd_text,
517
+ size_t len,
518
+ const char* base_dir,
519
+ LeptrisStatus* status);
520
+
324
521
  LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
325
522
  size_t len,
326
523
  LeptrisStatus* status) {
524
+ return xsd_compile_impl(xsd_text, len, NULL, status);
525
+ }
526
+
527
+ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile_file(
528
+ const char* path, LeptrisStatus* status) {
529
+ if (status) *status = LEPTRIS_OK;
530
+ if (!path || !*path) {
531
+ if (status) *status = LEPTRIS_ERROR_PARSE;
532
+ return NULL;
533
+ }
534
+ size_t len = 0;
535
+ char* text = xsd_read_file(path, &len);
536
+ if (!text) {
537
+ if (status) *status = LEPTRIS_ERROR_PARSE;
538
+ return NULL;
539
+ }
540
+ /* relative schemaLocations resolve against the schema's own
541
+ * directory (both separators: Windows paths carry '\') */
542
+ const char* slash = strrchr(path, '/');
543
+ const char* bslash = strrchr(path, '\\');
544
+ if (bslash && (!slash || bslash > slash)) slash = bslash;
545
+ char* base_dir = NULL;
546
+ if (slash) {
547
+ size_t n = (size_t)(slash - path);
548
+ if (n == 0) n = 1; /* "/f.xsd" roots at "/" */
549
+ base_dir = (char*)malloc(n + 1);
550
+ if (base_dir) {
551
+ memcpy(base_dir, path, n);
552
+ base_dir[n] = 0;
553
+ }
554
+ } else {
555
+ base_dir = xsd_strdup(".");
556
+ }
557
+ LeptrisXsdSchema s = xsd_compile_impl(text, len, base_dir, status);
558
+ free(base_dir);
559
+ free(text);
560
+ return s;
561
+ }
562
+
563
+ static LeptrisXsdSchema xsd_compile_impl(const char* xsd_text,
564
+ size_t len,
565
+ const char* base_dir,
566
+ LeptrisStatus* status) {
327
567
  if (status) *status = LEPTRIS_OK;
328
568
  if (!xsd_text || len == 0) {
329
569
  if (status) *status = LEPTRIS_ERROR_PARSE;
@@ -336,6 +576,7 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
336
576
  if (status) *status = LEPTRIS_ERROR_MEMORY;
337
577
  return NULL;
338
578
  }
579
+ s->base_dir = xsd_strdup(base_dir);
339
580
 
340
581
  /* target namespace (xs:any ##other/##targetNamespace filters) */
341
582
  s->target_ns = NULL;
@@ -367,15 +608,79 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
367
608
  goto fail;
368
609
  }
369
610
 
611
+ xsd_capture_root(s, root);
612
+
613
+ leptris_document_free(doc);
614
+ return s;
615
+
616
+ fail:
617
+ if (doc) leptris_document_free(doc);
618
+ if (status) *status = LEPTRIS_ERROR_PARSE;
619
+ /* Failure returns an error-carrying handle (the documented
620
+ * contract): leptris_xsd_error says why, free it the same way. */
621
+ return (LeptrisXsdSchema)s;
622
+ }
623
+
624
+ /* Every top-level declaration under an xs:schema root. xs:include
625
+ * / xs:import / xs:redefine resolve their schemaLocation against
626
+ * the owning schema's base_dir (string-compiled schemas have none
627
+ * and skip them); the referenced root's declarations merge into
628
+ * the same tables. */
629
+ static void xsd_capture_root(struct leptris_xsd_schema* s,
630
+ LeptrisElement root) {
370
631
  for (LeptrisNodeRef n =
371
632
  leptris_node_first_child(leptris_element_as_node(root));
372
633
  n; n = leptris_node_next_sibling(n)) {
373
634
  if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
374
635
  continue;
636
+ const char* local = NULL;
637
+ const char* prefix = NULL;
638
+ const char* uri = NULL;
375
639
  leptris_element_expanded_name((LeptrisElement)n, &local, &prefix,
376
640
  &uri);
377
641
  if (!xsd_is_declaration(local, uri)) continue;
378
642
  s->declaration_count++;
643
+
644
+ if (strcmp(local, "include") == 0 ||
645
+ strcmp(local, "import") == 0 ||
646
+ strcmp(local, "redefine") == 0) {
647
+ const char* loc = leptris_element_attribute(
648
+ (LeptrisElement)n, "schemaLocation");
649
+ if (loc && s->base_dir && s->include_depth < 16) {
650
+ char path[4096];
651
+ snprintf(path, sizeof(path), "%s/%s", s->base_dir,
652
+ loc);
653
+ size_t flen = 0;
654
+ char* text = xsd_read_file(path, &flen);
655
+ if (text) {
656
+ LeptrisDocument sub =
657
+ leptris_parse_string(text, flen, NULL);
658
+ if (sub) {
659
+ LeptrisElement subroot =
660
+ leptris_document_root(sub);
661
+ const char* tns =
662
+ subroot
663
+ ? leptris_element_attribute(
664
+ subroot, "targetNamespace")
665
+ : NULL;
666
+ /* same-namespace merges and no-namespace
667
+ * chameleon includes; foreign-namespace
668
+ * imports stay lax at this tier */
669
+ if (subroot &&
670
+ (!tns || !s->target_ns ||
671
+ strcmp(tns, s->target_ns) == 0)) {
672
+ s->include_depth++;
673
+ xsd_capture_root(s, subroot);
674
+ s->include_depth--;
675
+ }
676
+ leptris_document_free(sub);
677
+ }
678
+ free(text);
679
+ }
680
+ }
681
+ continue;
682
+ }
683
+
379
684
  if (strcmp(local, "element") == 0 && uri && !strcmp(uri, XSD_NS)) {
380
685
  const char* nm = leptris_element_attribute((LeptrisElement)n,
381
686
  "name");
@@ -387,6 +692,16 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
387
692
  if (d) {
388
693
  d->name = xsd_strdup(nm);
389
694
  d->type = xsd_strdup(ty);
695
+ d->fixed = xsd_strdup(
696
+ leptris_element_attribute((LeptrisElement)n,
697
+ "fixed"));
698
+ const char* sg = leptris_element_attribute(
699
+ (LeptrisElement)n, "substitutionGroup");
700
+ if (sg) {
701
+ const char* colon = strchr(sg, ':');
702
+ d->sub_head = xsd_strdup(
703
+ colon ? colon + 1 : sg);
704
+ }
390
705
  d->next = s->elements;
391
706
  s->elements = d;
392
707
  }
@@ -411,57 +726,54 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
411
726
  }
412
727
  } else if (strcmp(local, "complexType") == 0 && uri &&
413
728
  !strcmp(uri, XSD_NS)) {
729
+ const char* nm =
730
+ leptris_element_attribute((LeptrisElement)n, "name");
731
+ if (nm)
732
+ xsd_capture_complex_model(s, (LeptrisElement)n, nm);
733
+ } else if (strcmp(local, "attribute") == 0 && uri && !strcmp(uri, XSD_NS)) {
414
734
  const char* nm =
415
735
  leptris_element_attribute((LeptrisElement)n, "name");
416
736
  if (nm) {
417
- XsdCm* root =
418
- xsd_cm_new(XSD_CM_SEQ); /* wrapper keeps the name */
419
- if (root) {
420
- root->name = xsd_strdup(nm);
421
- root->first_child =
422
- xsd_cm_parse(s, (LeptrisElement)n, NULL);
423
- root->next = s->complex_types;
424
- s->complex_types = root;
425
- }
426
- /* slice 4: attribute declarations of the type */
427
- XsdAttrDecl* head = NULL;
428
- XsdAttrDecl* tail = NULL;
429
- for (LeptrisNodeRef ac = leptris_node_first_child(
430
- leptris_element_as_node((LeptrisElement)n));
431
- ac; ac = leptris_node_next_sibling(ac)) {
432
- if (leptris_node_get_type(ac) !=
433
- LEPTRIS_NODE_TYPE_ELEMENT)
434
- continue;
435
- LeptrisElement ae = (LeptrisElement)ac;
436
- const char* al = NULL, *ap = NULL, *au = NULL;
437
- leptris_element_expanded_name(ae, &al, &ap, &au);
438
- if (!al || (au && strcmp(au, XSD_NS) != 0)) continue;
439
- if (strcmp(al, "attribute") != 0) continue;
440
- const char* an = leptris_element_attribute(ae, "name");
441
- if (!an) continue; /* ref forms: slice 5 */
442
- XsdAttrDecl* d = (XsdAttrDecl*)calloc(1, sizeof(*d));
443
- if (!d) continue;
444
- d->name = xsd_strdup(an);
737
+ XsdAttrDecl* d = (XsdAttrDecl*)calloc(1, sizeof(*d));
738
+ if (d) {
739
+ d->name = xsd_strdup(nm);
445
740
  d->type = xsd_strdup(
446
- leptris_element_attribute(ae, "type"));
447
- const char* use =
448
- leptris_element_attribute(ae, "use");
741
+ leptris_element_attribute((LeptrisElement)n,
742
+ "type"));
743
+ d->fixed = xsd_strdup(
744
+ leptris_element_attribute((LeptrisElement)n,
745
+ "fixed"));
746
+ const char* use = leptris_element_attribute(
747
+ (LeptrisElement)n, "use");
449
748
  d->required = (use && strcmp(use, "required") == 0);
450
- if (tail)
451
- tail->next = d;
452
- else
453
- head = d;
454
- tail = d;
749
+ d->next = s->top_attrs;
750
+ s->top_attrs = d;
455
751
  }
456
- if (head) {
457
- XsdTypeAttrs* ta =
458
- (XsdTypeAttrs*)calloc(1, sizeof(*ta));
459
- if (ta) {
460
- ta->type_name = xsd_strdup(nm);
461
- ta->attrs = head;
462
- ta->next = s->type_attrs;
463
- s->type_attrs = ta;
464
- }
752
+ }
753
+ } else if (strcmp(local, "attributeGroup") == 0 && uri && !strcmp(uri, XSD_NS)) {
754
+ const char* nm =
755
+ leptris_element_attribute((LeptrisElement)n, "name");
756
+ if (nm) {
757
+ XsdAttrGroupDef* g =
758
+ (XsdAttrGroupDef*)calloc(1, sizeof(*g));
759
+ if (g) {
760
+ g->name = xsd_strdup(nm);
761
+ g->attrs = xsd_capture_attr_rows(
762
+ s, (LeptrisElement)n, 0);
763
+ g->next = s->attr_groups;
764
+ s->attr_groups = g;
765
+ }
766
+ }
767
+ } else if (strcmp(local, "group") == 0 && uri && !strcmp(uri, XSD_NS)) {
768
+ const char* nm =
769
+ leptris_element_attribute((LeptrisElement)n, "name");
770
+ if (nm) {
771
+ XsdGroupDef* g = (XsdGroupDef*)calloc(1, sizeof(*g));
772
+ if (g) {
773
+ g->name = xsd_strdup(nm);
774
+ g->model = xsd_cm_parse(s, (LeptrisElement)n, NULL);
775
+ g->next = s->groups;
776
+ s->groups = g;
465
777
  }
466
778
  }
467
779
  } else if (strcmp(local, "simpleType") == 0 && uri && !strcmp(uri, XSD_NS)) {
@@ -472,16 +784,33 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
472
784
  }
473
785
  }
474
786
  }
787
+ }
475
788
 
476
- leptris_document_free(doc);
477
- return s;
478
-
479
- fail:
480
- if (doc) leptris_document_free(doc);
481
- if (status) *status = LEPTRIS_ERROR_PARSE;
482
- /* Failure returns an error-carrying handle (the documented
483
- * contract): leptris_xsd_error says why, free it the same way. */
484
- return (LeptrisXsdSchema)s;
789
+ static void xsd_free_simple_one(XsdSimple* t) {
790
+ if (!t) return;
791
+ free(t->name);
792
+ free(t->base);
793
+ free(t->pattern);
794
+ XsdFacetValue* fv = t->enum_values;
795
+ while (fv) {
796
+ XsdFacetValue* fn = fv->next;
797
+ free(fv->text);
798
+ free(fv);
799
+ fv = fn;
800
+ }
801
+ free(t->min_inclusive);
802
+ free(t->min_exclusive);
803
+ free(t->max_inclusive);
804
+ free(t->max_exclusive);
805
+ free(t->whitespace);
806
+ free(t->item_type);
807
+ xsd_free_simple_one(t->item_def);
808
+ for (size_t i = 0; i < t->member_count; i++) free(t->members[i]);
809
+ free(t->members);
810
+ for (size_t i = 0; i < t->member_def_count; i++)
811
+ xsd_free_simple_one(t->member_defs[i]);
812
+ free(t->member_defs);
813
+ free(t);
485
814
  }
486
815
 
487
816
  LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
@@ -514,6 +843,7 @@ LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
514
843
  XsdAttrDecl* an = a->next;
515
844
  free(a->name);
516
845
  free(a->type);
846
+ free(a->fixed);
517
847
  free(a);
518
848
  a = an;
519
849
  }
@@ -526,31 +856,52 @@ LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
526
856
  XsdElementDecl* dn = d->next;
527
857
  free(d->name);
528
858
  free(d->type);
859
+ free(d->fixed);
860
+ free(d->sub_head);
529
861
  free(d);
530
862
  d = dn;
531
863
  }
864
+ XsdAttrGroupDef* ag = s->attr_groups;
865
+ while (ag) {
866
+ XsdAttrGroupDef* agn = ag->next;
867
+ free(ag->name);
868
+ XsdAttrDecl* a = ag->attrs;
869
+ while (a) {
870
+ XsdAttrDecl* an2 = a->next;
871
+ free(a->name);
872
+ free(a->type);
873
+ free(a->fixed);
874
+ free(a);
875
+ a = an2;
876
+ }
877
+ free(ag);
878
+ ag = agn;
879
+ }
880
+ XsdAttrDecl* ta_row = s->top_attrs;
881
+ while (ta_row) {
882
+ XsdAttrDecl* ta_next = ta_row->next;
883
+ free(ta_row->name);
884
+ free(ta_row->type);
885
+ free(ta_row->fixed);
886
+ free(ta_row);
887
+ ta_row = ta_next;
888
+ }
889
+ XsdGroupDef* g = s->groups;
890
+ while (g) {
891
+ XsdGroupDef* gn = g->next;
892
+ free(g->name);
893
+ xsd_cm_free(g->model);
894
+ free(g);
895
+ g = gn;
896
+ }
532
897
  free(s->target_ns);
898
+ free(s->base_dir);
533
899
  if (s->validator)
534
900
  xsd_validator_free((struct xsd_validator*)s->validator);
535
901
  XsdSimple* t = s->simple_types;
536
902
  while (t) {
537
903
  XsdSimple* next = t->next;
538
- free(t->name);
539
- free(t->base);
540
- free(t->pattern);
541
- XsdFacetValue* fv = t->enum_values;
542
- while (fv) {
543
- XsdFacetValue* fn = fv->next;
544
- free(fv->text);
545
- free(fv);
546
- fv = fn;
547
- }
548
- free(t->min_inclusive);
549
- free(t->min_exclusive);
550
- free(t->max_inclusive);
551
- free(t->max_exclusive);
552
- free(t->whitespace);
553
- free(t);
904
+ xsd_free_simple_one(t);
554
905
  t = next;
555
906
  }
556
907
  free(s->error);
@@ -559,12 +910,376 @@ LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
559
910
 
560
911
  /* ---- slice 2: lexical validation --------------------------------- */
561
912
 
913
+ static XsdCm* xsd_cm_copy_list(const XsdCm* head) {
914
+ XsdCm* out = NULL;
915
+ XsdCm* out_tail = NULL;
916
+ for (const XsdCm* src = head; src; src = src->next) {
917
+ XsdCm* d = (XsdCm*)calloc(1, sizeof(*d));
918
+ if (!d) break;
919
+ d->kind = src->kind;
920
+ d->name = xsd_strdup(src->name);
921
+ d->type = xsd_strdup(src->type);
922
+ d->ns = xsd_strdup(src->ns);
923
+ d->any_ns = xsd_strdup(src->any_ns);
924
+ d->process_skip = src->process_skip;
925
+ d->min = src->min;
926
+ d->max = src->max;
927
+ d->first_child = xsd_cm_copy_list(src->first_child);
928
+ if (out_tail)
929
+ out_tail->next = d;
930
+ else
931
+ out = d;
932
+ out_tail = d;
933
+ }
934
+ return out;
935
+ }
936
+
937
+ static void xsd_register_complex(struct leptris_xsd_schema* s,
938
+ XsdCm* root, XsdAttrDecl* attrs,
939
+ const char* name) {
940
+ if (!root) return;
941
+ root->name = xsd_strdup(name);
942
+ root->next = s->complex_types;
943
+ s->complex_types = root;
944
+ if (attrs) {
945
+ XsdTypeAttrs* ta = (XsdTypeAttrs*)calloc(1, sizeof(*ta));
946
+ if (ta) {
947
+ ta->type_name = xsd_strdup(name);
948
+ ta->attrs = attrs;
949
+ ta->next = s->type_attrs;
950
+ s->type_attrs = ta;
951
+ }
952
+ }
953
+ }
954
+
955
+ /* One complexType capture, shared by top-level declarations and
956
+ * inline anonymous types: content model + derivation
957
+ * (complexContent/simpleContent extension|restriction) + the
958
+ * attribute rows the derivation carries. */
959
+ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
960
+ LeptrisElement ct,
961
+ const char* name) {
962
+ XsdCm* root = xsd_cm_new(XSD_CM_SEQ); /* wrapper keeps the name */
963
+ if (!root) return NULL;
964
+ XsdAttrDecl* attrs = NULL;
965
+
966
+ for (LeptrisNodeRef n =
967
+ leptris_node_first_child(leptris_element_as_node(ct));
968
+ n; n = leptris_node_next_sibling(n)) {
969
+ if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
970
+ continue;
971
+ LeptrisElement e = (LeptrisElement)n;
972
+ const char* local = NULL, *prefix = NULL, *uri = NULL;
973
+ leptris_element_expanded_name(e, &local, &prefix, &uri);
974
+ if (!local || !uri || strcmp(uri, XSD_NS) != 0) continue;
975
+
976
+ if (strcmp(local, "complexContent") == 0) {
977
+ /* extension|restriction child carries base + model */
978
+ for (LeptrisNodeRef q = leptris_node_first_child(
979
+ leptris_element_as_node(e));
980
+ q; q = leptris_node_next_sibling(q)) {
981
+ if (leptris_node_get_type(q) !=
982
+ LEPTRIS_NODE_TYPE_ELEMENT)
983
+ continue;
984
+ LeptrisElement dq = (LeptrisElement)q;
985
+ const char* dl = NULL;
986
+ leptris_element_expanded_name(dq, &dl, NULL, NULL);
987
+ if (!dl || (strcmp(dl, "extension") != 0 &&
988
+ strcmp(dl, "restriction") != 0))
989
+ continue;
990
+ const char* base =
991
+ leptris_element_attribute(dq, "base");
992
+ XsdCm* base_root =
993
+ (base && strncmp(base, "xs:", 3) != 0)
994
+ ? xsd_find_complex(s, base)
995
+ : NULL;
996
+ if (strcmp(dl, "extension") == 0) {
997
+ /* base content first, derived content after */
998
+ root->first_child = xsd_cm_copy_list(
999
+ base_root ? base_root->first_child : NULL);
1000
+ XsdCm* own_tail = root->first_child;
1001
+ while (own_tail && own_tail->next)
1002
+ own_tail = own_tail->next;
1003
+ XsdCm* own = xsd_cm_parse(s, dq, NULL);
1004
+ if (own_tail)
1005
+ own_tail->next = own;
1006
+ else if (!root->first_child)
1007
+ root->first_child = own;
1008
+ if (base_root) {
1009
+ const XsdTypeAttrs* bt = xsd_find_type_attrs(
1010
+ s, base);
1011
+ attrs = xsd_copy_attr_rows(bt ? bt->attrs
1012
+ : NULL);
1013
+ }
1014
+ XsdAttrDecl* own_attrs =
1015
+ xsd_capture_attr_rows(s, dq, 0);
1016
+ if (own_attrs) {
1017
+ XsdAttrDecl* at = attrs;
1018
+ while (at && at->next) at = at->next;
1019
+ if (at)
1020
+ at->next = own_attrs;
1021
+ else
1022
+ attrs = own_attrs;
1023
+ }
1024
+ } else {
1025
+ /* restriction: the derived model REPLACES */
1026
+ root->first_child = xsd_cm_parse(s, dq, NULL);
1027
+ attrs = xsd_capture_attr_rows(s, dq, 0);
1028
+ }
1029
+ }
1030
+ } else if (strcmp(local, "simpleContent") == 0) {
1031
+ for (LeptrisNodeRef q = leptris_node_first_child(
1032
+ leptris_element_as_node(e));
1033
+ q; q = leptris_node_next_sibling(q)) {
1034
+ if (leptris_node_get_type(q) !=
1035
+ LEPTRIS_NODE_TYPE_ELEMENT)
1036
+ continue;
1037
+ LeptrisElement dq = (LeptrisElement)q;
1038
+ const char* dl = NULL;
1039
+ leptris_element_expanded_name(dq, &dl, NULL, NULL);
1040
+ if (!dl || (strcmp(dl, "extension") != 0 &&
1041
+ strcmp(dl, "restriction") != 0))
1042
+ continue;
1043
+ const char* base =
1044
+ leptris_element_attribute(dq, "base");
1045
+ if (base) {
1046
+ if (strncmp(base, "xs:", 3) == 0) {
1047
+ root->text_type = xsd_strdup(base);
1048
+ } else {
1049
+ /* restriction facets under a local base
1050
+ * ride a synthesized "complex:NAME" slot */
1051
+ int has_facets = 0;
1052
+ for (LeptrisNodeRef f =
1053
+ leptris_node_first_child(
1054
+ leptris_element_as_node(dq));
1055
+ f; f = leptris_node_next_sibling(f)) {
1056
+ if (leptris_node_get_type(f) !=
1057
+ LEPTRIS_NODE_TYPE_ELEMENT)
1058
+ continue;
1059
+ const char* fl = NULL;
1060
+ leptris_element_expanded_name(
1061
+ (LeptrisElement)f, &fl, NULL, NULL);
1062
+ if (fl && strcmp(fl, "facet") != 0 &&
1063
+ strcmp(fl, "simpleType") != 0)
1064
+ has_facets = 1;
1065
+ }
1066
+ XsdSimple* captured =
1067
+ has_facets
1068
+ ? xsd_capture_simple(dq)
1069
+ : NULL;
1070
+ if (captured) {
1071
+ char synth[512];
1072
+ snprintf(synth, sizeof(synth),
1073
+ "complex:%s", name);
1074
+ free(captured->name);
1075
+ captured->name = xsd_strdup(synth);
1076
+ captured->next = s->simple_types;
1077
+ s->simple_types = captured;
1078
+ root->text_type = xsd_strdup(synth);
1079
+ } else {
1080
+ root->text_type = xsd_strdup(base);
1081
+ }
1082
+ }
1083
+ }
1084
+ attrs = xsd_capture_attr_rows(s, dq, 0);
1085
+ }
1086
+ }
1087
+ }
1088
+ if (!root->first_child && !root->text_type) {
1089
+ /* no derivation: the direct-content spelling. A derivation
1090
+ * that captured attributes but produced no element model
1091
+ * (e.g. an attributes-only restriction) already set them —
1092
+ * re-capturing would orphan those rows. */
1093
+ root->first_child = xsd_cm_parse(s, ct, NULL);
1094
+ if (!attrs) attrs = xsd_capture_attr_rows(s, ct, 0);
1095
+ }
1096
+ /* the NFA and model walkers consume ONE root particle —
1097
+ * a multi-particle model (derivation splices) normalizes
1098
+ * under a sequence */
1099
+ if (root->first_child && root->first_child->next) {
1100
+ XsdCm* seq = xsd_cm_new(XSD_CM_SEQ);
1101
+ if (seq) {
1102
+ seq->first_child = root->first_child;
1103
+ root->first_child = seq;
1104
+ }
1105
+ }
1106
+ xsd_register_complex(s, root, attrs, name);
1107
+ return root;
1108
+ }
1109
+
1110
+ static const XsdElementDecl* xsd_find_element_decl(
1111
+ struct leptris_xsd_schema* s, const char* name) {
1112
+ for (XsdElementDecl* d = s->elements; d; d = d->next)
1113
+ if (strcmp(d->name, name) == 0) return d;
1114
+ return NULL;
1115
+ }
1116
+
1117
+ int xsd_is_substitute(struct leptris_xsd_schema* s,
1118
+ const char* name, const char* head) {
1119
+ const XsdElementDecl* d = xsd_find_element_decl(s, name);
1120
+ for (int hops = 0; d && d->sub_head && hops < 16; hops++) {
1121
+ if (strcmp(d->sub_head, head) == 0) return 1;
1122
+ d = xsd_find_element_decl(s, d->sub_head);
1123
+ }
1124
+ return 0;
1125
+ }
1126
+
1127
+ XsdGroupDef* xsd_find_group(struct leptris_xsd_schema* s,
1128
+ const char* name) {
1129
+ if (!s || !name) return NULL;
1130
+ for (XsdGroupDef* g = s->groups; g; g = g->next)
1131
+ if (strcmp(g->name, name) == 0) return g;
1132
+ return NULL;
1133
+ }
1134
+
1135
+ static const XsdAttrDecl* xsd_find_top_attr(
1136
+ struct leptris_xsd_schema* s, const char* name) {
1137
+ if (!s || !name) return NULL;
1138
+ for (XsdAttrDecl* a = s->top_attrs; a; a = a->next)
1139
+ if (strcmp(a->name, name) == 0) return a;
1140
+ return NULL;
1141
+ }
1142
+
1143
+ /* Deep-copy a captured row chain (attributeGroup refs splice
1144
+ * copies — every referencing type owns its rows). */
1145
+ static XsdAttrDecl* xsd_copy_attr_rows(const XsdAttrDecl* src) {
1146
+ XsdAttrDecl* head = NULL;
1147
+ XsdAttrDecl* tail = NULL;
1148
+ for (; src; src = src->next) {
1149
+ XsdAttrDecl* d = (XsdAttrDecl*)calloc(1, sizeof(*d));
1150
+ if (!d) break;
1151
+ d->name = xsd_strdup(src->name);
1152
+ d->type = xsd_strdup(src->type);
1153
+ d->fixed = xsd_strdup(src->fixed);
1154
+ d->required = src->required;
1155
+ if (tail)
1156
+ tail->next = d;
1157
+ else
1158
+ head = d;
1159
+ tail = d;
1160
+ }
1161
+ return head;
1162
+ }
1163
+
1164
+ /* Capture the attribute rows of a complexType or attributeGroup:
1165
+ * named rows, inline anonymous simpleTypes (synthesized slots),
1166
+ * refs to top-level attributes, and attributeGroup refs. */
1167
+ /* Named row with an inline anonymous simpleType rides a
1168
+ * synthesized "attribute:NAME" slot, the element-slot pattern. */
1169
+ static const char* xsd_inline_attr_type(
1170
+ struct leptris_xsd_schema* s, LeptrisElement ae,
1171
+ const char* attr_name) {
1172
+ for (LeptrisNodeRef q = leptris_node_first_child(
1173
+ leptris_element_as_node(ae));
1174
+ q; q = leptris_node_next_sibling(q)) {
1175
+ if (leptris_node_get_type(q) != LEPTRIS_NODE_TYPE_ELEMENT)
1176
+ continue;
1177
+ LeptrisElement qe = (LeptrisElement)q;
1178
+ const char* ql = NULL;
1179
+ leptris_element_expanded_name(qe, &ql, NULL, NULL);
1180
+ if (!ql || strcmp(ql, "simpleType") != 0) continue;
1181
+ char synth[512];
1182
+ snprintf(synth, sizeof(synth), "attribute:%s", attr_name);
1183
+ XsdSimple* captured = xsd_capture_simple(qe);
1184
+ if (!captured) return NULL;
1185
+ free(captured->name);
1186
+ captured->name = xsd_strdup(synth);
1187
+ captured->next = s->simple_types;
1188
+ s->simple_types = captured;
1189
+ return captured->name;
1190
+ }
1191
+ return NULL;
1192
+ }
1193
+
1194
+ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
1195
+ LeptrisElement container,
1196
+ int depth) {
1197
+ XsdAttrDecl* head = NULL;
1198
+ XsdAttrDecl* tail = NULL;
1199
+ if (depth > 8) return NULL;
1200
+ for (LeptrisNodeRef ac = leptris_node_first_child(
1201
+ leptris_element_as_node(container));
1202
+ ac; ac = leptris_node_next_sibling(ac)) {
1203
+ if (leptris_node_get_type(ac) != LEPTRIS_NODE_TYPE_ELEMENT)
1204
+ continue;
1205
+ LeptrisElement ae = (LeptrisElement)ac;
1206
+ const char* al = NULL, *ap = NULL, *au = NULL;
1207
+ leptris_element_expanded_name(ae, &al, &ap, &au);
1208
+ if (!al || (au && strcmp(au, XSD_NS) != 0)) continue;
1209
+
1210
+ if (strcmp(al, "attribute") == 0) {
1211
+ const char* an = leptris_element_attribute(ae, "name");
1212
+ const char* use = leptris_element_attribute(ae, "use");
1213
+ XsdAttrDecl* d = (XsdAttrDecl*)calloc(1, sizeof(*d));
1214
+ if (!d) continue;
1215
+ if (an) {
1216
+ d->name = xsd_strdup(an);
1217
+ d->type = xsd_strdup(
1218
+ leptris_element_attribute(ae, "type"));
1219
+ if (!d->type)
1220
+ d->type = xsd_strdup(
1221
+ xsd_inline_attr_type(s, ae, an));
1222
+ d->fixed = xsd_strdup(
1223
+ leptris_element_attribute(ae, "fixed"));
1224
+ d->required = (use && strcmp(use, "required") == 0);
1225
+ } else {
1226
+ /* ref to a top-level attribute declaration */
1227
+ const char* ref =
1228
+ leptris_element_attribute(ae, "ref");
1229
+ const XsdAttrDecl* src =
1230
+ ref ? xsd_find_top_attr(s, ref) : NULL;
1231
+ if (!src) {
1232
+ free(d);
1233
+ continue;
1234
+ }
1235
+ d->name = xsd_strdup(src->name);
1236
+ d->type = xsd_strdup(src->type);
1237
+ d->fixed = xsd_strdup(src->fixed);
1238
+ d->required = use
1239
+ ? (strcmp(use, "required") == 0)
1240
+ : src->required;
1241
+ }
1242
+ if (tail)
1243
+ tail->next = d;
1244
+ else
1245
+ head = d;
1246
+ tail = d;
1247
+ } else if (strcmp(al, "attributeGroup") == 0) {
1248
+ const char* ref =
1249
+ leptris_element_attribute(ae, "ref");
1250
+ if (!ref) continue;
1251
+ XsdAttrGroupDef* g = s->attr_groups;
1252
+ for (; g; g = g->next)
1253
+ if (strcmp(g->name, ref) == 0) break;
1254
+ if (!g || !g->attrs) continue;
1255
+ XsdAttrDecl* copied = xsd_copy_attr_rows(g->attrs);
1256
+ if (!copied) continue;
1257
+ if (!head)
1258
+ head = copied;
1259
+ else
1260
+ tail->next = copied;
1261
+ tail = copied;
1262
+ while (tail->next) tail = tail->next;
1263
+ }
1264
+ }
1265
+ return head;
1266
+ }
1267
+
562
1268
  LEPTRIS_API int leptris_xsd_builtin_valid(const char* builtin,
563
1269
  const char* lexical) {
564
1270
  extern int xsd_builtin_valid(const char* type, const char* v);
565
1271
  return xsd_builtin_valid(builtin, lexical);
566
1272
  }
567
1273
 
1274
+ /* The instance validator routes every user simpleType through the
1275
+ * chain walker so list/union derivations dispatch with the schema
1276
+ * in hand (the bare xsd_simple_valid cannot resolve item/member
1277
+ * names). */
1278
+ int xsd_simple_valid_chain(struct leptris_xsd_schema* s,
1279
+ const XsdSimple* t, const char* v) {
1280
+ return xsd_valid_chain(s, t, v, 0);
1281
+ }
1282
+
568
1283
  LEPTRIS_API int leptris_xsd_simple_valid(LeptrisXsdSchema schema,
569
1284
  const char* type_name,
570
1285
  const char* lexical) {