leptris 1.9.319.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 +4 -4
- data/CHANGELOG.md +35 -0
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/ffi.rb +15 -0
- data/lib/leptris/xml/xsd.rb +58 -6
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +45 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/CMakeLists.txt +2 -0
- data/vendor-src/libleptris/src/include/leptris.h +22 -0
- data/vendor-src/libleptris/src/leptris/descriptor/plan.c +35 -3
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +309 -1
- data/vendor-src/libleptris/src/leptris/xsd/content_model.c +232 -0
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +287 -0
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +48 -0
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +168 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +253 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +3 -1
|
@@ -13,13 +13,122 @@
|
|
|
13
13
|
#define XSD_NS "http://www.w3.org/2001/XMLSchema"
|
|
14
14
|
|
|
15
15
|
extern int xsd_simple_valid(const XsdSimple* simple, const char* v);
|
|
16
|
+
extern struct xsd_validator* xsd_validator_new(
|
|
17
|
+
struct leptris_xsd_schema* s);
|
|
18
|
+
extern void xsd_validator_free(struct xsd_validator* v);
|
|
19
|
+
extern int xsd_validator_run(struct xsd_validator* v, LeptrisDocument doc);
|
|
20
|
+
extern size_t xsd_validator_error_count(struct xsd_validator* v);
|
|
21
|
+
extern const char* xsd_validator_error_at(struct xsd_validator* v,
|
|
22
|
+
size_t i);
|
|
23
|
+
|
|
24
|
+
extern int xsd_content_valid(struct leptris_xsd_schema* s, XsdCm* model,
|
|
25
|
+
const char* target_ns,
|
|
26
|
+
const char* const* names,
|
|
27
|
+
const char* const* ns_uris, size_t count);
|
|
16
28
|
|
|
17
29
|
struct leptris_xsd_schema {
|
|
18
30
|
size_t declaration_count;
|
|
19
31
|
XsdSimple* simple_types;
|
|
32
|
+
XsdElementDecl* elements;
|
|
33
|
+
XsdCm* complex_types; /* name-carrying model roots */
|
|
34
|
+
XsdTypeAttrs* type_attrs;
|
|
35
|
+
char* target_ns;
|
|
36
|
+
void* validator; /* slice-4 parked error list */
|
|
20
37
|
char* error;
|
|
21
38
|
};
|
|
22
39
|
|
|
40
|
+
/* ---- slice 3: content-model capture ------------------------------ */
|
|
41
|
+
|
|
42
|
+
static char* xsd_strdup(const char* s);
|
|
43
|
+
|
|
44
|
+
static XsdCm* xsd_cm_new(XsdCmKind kind) {
|
|
45
|
+
XsdCm* cm = (XsdCm*)calloc(1, sizeof(*cm));
|
|
46
|
+
if (!cm) return NULL;
|
|
47
|
+
cm->kind = kind;
|
|
48
|
+
cm->min = 1;
|
|
49
|
+
cm->max = 1;
|
|
50
|
+
return cm;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static void xsd_cm_free(XsdCm* cm) {
|
|
54
|
+
while (cm) {
|
|
55
|
+
XsdCm* next = cm->next;
|
|
56
|
+
xsd_cm_free(cm->first_child);
|
|
57
|
+
free(cm->name);
|
|
58
|
+
free(cm->type);
|
|
59
|
+
free(cm->ns);
|
|
60
|
+
free(cm->any_ns);
|
|
61
|
+
free(cm);
|
|
62
|
+
cm = next;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static void xsd_cm_occurrence(XsdCm* cm, LeptrisElement e) {
|
|
67
|
+
const char* mn = leptris_element_attribute(e, "minOccurs");
|
|
68
|
+
if (mn) cm->min = (strcmp(mn, "unbounded") == 0) ? 0 : atoi(mn);
|
|
69
|
+
const char* mx = leptris_element_attribute(e, "maxOccurs");
|
|
70
|
+
if (mx) cm->max = (strcmp(mx, "unbounded") == 0) ? -1 : atoi(mx);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Recursive model build over sequence | choice | all | element |
|
|
74
|
+
* any children. Returns the child list head; *tail_out receives
|
|
75
|
+
* the list tail for sibling chaining. */
|
|
76
|
+
static XsdCm* xsd_cm_parse(LeptrisElement group, XsdCm** tail_out) {
|
|
77
|
+
XsdCm* head = NULL;
|
|
78
|
+
XsdCm* tail = NULL;
|
|
79
|
+
for (LeptrisNodeRef n =
|
|
80
|
+
leptris_node_first_child(leptris_element_as_node(group));
|
|
81
|
+
n; n = leptris_node_next_sibling(n)) {
|
|
82
|
+
if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
83
|
+
continue;
|
|
84
|
+
LeptrisElement e = (LeptrisElement)n;
|
|
85
|
+
const char* local = NULL, *prefix = NULL, *uri = NULL;
|
|
86
|
+
leptris_element_expanded_name(e, &local, &prefix, &uri);
|
|
87
|
+
if (!local || (uri && strcmp(uri, XSD_NS) != 0)) continue;
|
|
88
|
+
|
|
89
|
+
XsdCm* cm = NULL;
|
|
90
|
+
if (strcmp(local, "sequence") == 0 || strcmp(local, "choice") == 0 ||
|
|
91
|
+
strcmp(local, "all") == 0) {
|
|
92
|
+
cm = xsd_cm_new(strcmp(local, "sequence") == 0 ? XSD_CM_SEQ
|
|
93
|
+
: strcmp(local, "choice") == 0 ? XSD_CM_CHOICE
|
|
94
|
+
: XSD_CM_ALL);
|
|
95
|
+
if (cm) cm->first_child = xsd_cm_parse(e, NULL);
|
|
96
|
+
} else if (strcmp(local, "element") == 0) {
|
|
97
|
+
cm = xsd_cm_new(XSD_CM_ELEMENT);
|
|
98
|
+
if (cm) {
|
|
99
|
+
const char* nm =
|
|
100
|
+
leptris_element_attribute(e, "name");
|
|
101
|
+
if (!nm) nm = leptris_element_attribute(e, "ref");
|
|
102
|
+
cm->name = xsd_strdup(nm);
|
|
103
|
+
cm->type = xsd_strdup(
|
|
104
|
+
leptris_element_attribute(e, "type"));
|
|
105
|
+
}
|
|
106
|
+
} else if (strcmp(local, "any") == 0) {
|
|
107
|
+
cm = xsd_cm_new(XSD_CM_ANY);
|
|
108
|
+
if (cm) {
|
|
109
|
+
cm->any_ns = xsd_strdup(
|
|
110
|
+
leptris_element_attribute(e, "namespace"));
|
|
111
|
+
const char* pc = leptris_element_attribute(
|
|
112
|
+
e, "processContents");
|
|
113
|
+
cm->process_skip =
|
|
114
|
+
(pc && (strcmp(pc, "skip") == 0 ||
|
|
115
|
+
strcmp(pc, "lax") == 0)) ? 1 : 0;
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
continue; /* annotation & friends */
|
|
119
|
+
}
|
|
120
|
+
if (!cm) continue;
|
|
121
|
+
xsd_cm_occurrence(cm, e);
|
|
122
|
+
if (tail)
|
|
123
|
+
tail->next = cm;
|
|
124
|
+
else
|
|
125
|
+
head = cm;
|
|
126
|
+
tail = cm;
|
|
127
|
+
}
|
|
128
|
+
if (tail_out) *tail_out = tail;
|
|
129
|
+
return head;
|
|
130
|
+
}
|
|
131
|
+
|
|
23
132
|
static char* xsd_strdup(const char* s) {
|
|
24
133
|
if (!s) return NULL;
|
|
25
134
|
size_t n = strlen(s) + 1;
|
|
@@ -161,6 +270,9 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
|
161
270
|
return NULL;
|
|
162
271
|
}
|
|
163
272
|
|
|
273
|
+
/* target namespace (xs:any ##other/##targetNamespace filters) */
|
|
274
|
+
s->target_ns = NULL;
|
|
275
|
+
|
|
164
276
|
LeptrisDocument doc = leptris_parse_string(xsd_text, len, status);
|
|
165
277
|
if (!doc) {
|
|
166
278
|
xsd_set_error(s, "schema document is not well-formed XML");
|
|
@@ -172,6 +284,8 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
|
172
284
|
xsd_set_error(s, "schema document has no root element");
|
|
173
285
|
goto fail;
|
|
174
286
|
}
|
|
287
|
+
s->target_ns = xsd_strdup(
|
|
288
|
+
leptris_element_attribute(root, "targetNamespace"));
|
|
175
289
|
|
|
176
290
|
const char* local = NULL;
|
|
177
291
|
const char* prefix = NULL;
|
|
@@ -195,7 +309,77 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
|
195
309
|
&uri);
|
|
196
310
|
if (!xsd_is_declaration(local, uri)) continue;
|
|
197
311
|
s->declaration_count++;
|
|
198
|
-
if (strcmp(local, "
|
|
312
|
+
if (strcmp(local, "element") == 0 && uri && !strcmp(uri, XSD_NS)) {
|
|
313
|
+
const char* nm = leptris_element_attribute((LeptrisElement)n,
|
|
314
|
+
"name");
|
|
315
|
+
const char* ty = leptris_element_attribute((LeptrisElement)n,
|
|
316
|
+
"type");
|
|
317
|
+
if (nm && ty) {
|
|
318
|
+
XsdElementDecl* d =
|
|
319
|
+
(XsdElementDecl*)calloc(1, sizeof(*d));
|
|
320
|
+
if (d) {
|
|
321
|
+
d->name = xsd_strdup(nm);
|
|
322
|
+
d->type = xsd_strdup(ty);
|
|
323
|
+
d->next = s->elements;
|
|
324
|
+
s->elements = d;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} else if (strcmp(local, "complexType") == 0 && uri &&
|
|
328
|
+
!strcmp(uri, XSD_NS)) {
|
|
329
|
+
const char* nm =
|
|
330
|
+
leptris_element_attribute((LeptrisElement)n, "name");
|
|
331
|
+
if (nm) {
|
|
332
|
+
XsdCm* root =
|
|
333
|
+
xsd_cm_new(XSD_CM_SEQ); /* wrapper keeps the name */
|
|
334
|
+
if (root) {
|
|
335
|
+
root->name = xsd_strdup(nm);
|
|
336
|
+
root->first_child =
|
|
337
|
+
xsd_cm_parse((LeptrisElement)n, NULL);
|
|
338
|
+
root->next = s->complex_types;
|
|
339
|
+
s->complex_types = root;
|
|
340
|
+
}
|
|
341
|
+
/* slice 4: attribute declarations of the type */
|
|
342
|
+
XsdAttrDecl* head = NULL;
|
|
343
|
+
XsdAttrDecl* tail = NULL;
|
|
344
|
+
for (LeptrisNodeRef ac = leptris_node_first_child(
|
|
345
|
+
leptris_element_as_node((LeptrisElement)n));
|
|
346
|
+
ac; ac = leptris_node_next_sibling(ac)) {
|
|
347
|
+
if (leptris_node_get_type(ac) !=
|
|
348
|
+
LEPTRIS_NODE_TYPE_ELEMENT)
|
|
349
|
+
continue;
|
|
350
|
+
LeptrisElement ae = (LeptrisElement)ac;
|
|
351
|
+
const char* al = NULL, *ap = NULL, *au = NULL;
|
|
352
|
+
leptris_element_expanded_name(ae, &al, &ap, &au);
|
|
353
|
+
if (!al || (au && strcmp(au, XSD_NS) != 0)) continue;
|
|
354
|
+
if (strcmp(al, "attribute") != 0) continue;
|
|
355
|
+
const char* an = leptris_element_attribute(ae, "name");
|
|
356
|
+
if (!an) continue; /* ref forms: slice 5 */
|
|
357
|
+
XsdAttrDecl* d = (XsdAttrDecl*)calloc(1, sizeof(*d));
|
|
358
|
+
if (!d) continue;
|
|
359
|
+
d->name = xsd_strdup(an);
|
|
360
|
+
d->type = xsd_strdup(
|
|
361
|
+
leptris_element_attribute(ae, "type"));
|
|
362
|
+
const char* use =
|
|
363
|
+
leptris_element_attribute(ae, "use");
|
|
364
|
+
d->required = (use && strcmp(use, "required") == 0);
|
|
365
|
+
if (tail)
|
|
366
|
+
tail->next = d;
|
|
367
|
+
else
|
|
368
|
+
head = d;
|
|
369
|
+
tail = d;
|
|
370
|
+
}
|
|
371
|
+
if (head) {
|
|
372
|
+
XsdTypeAttrs* ta =
|
|
373
|
+
(XsdTypeAttrs*)calloc(1, sizeof(*ta));
|
|
374
|
+
if (ta) {
|
|
375
|
+
ta->type_name = xsd_strdup(nm);
|
|
376
|
+
ta->attrs = head;
|
|
377
|
+
ta->next = s->type_attrs;
|
|
378
|
+
s->type_attrs = ta;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
} else if (strcmp(local, "simpleType") == 0 && uri && !strcmp(uri, XSD_NS)) {
|
|
199
383
|
XsdSimple* captured = xsd_capture_simple((LeptrisElement)n);
|
|
200
384
|
if (captured) {
|
|
201
385
|
captured->next = s->simple_types;
|
|
@@ -218,6 +402,33 @@ fail:
|
|
|
218
402
|
LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
|
|
219
403
|
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
220
404
|
if (!s) return;
|
|
405
|
+
xsd_cm_free(s->complex_types);
|
|
406
|
+
XsdTypeAttrs* ta = s->type_attrs;
|
|
407
|
+
while (ta) {
|
|
408
|
+
XsdTypeAttrs* tn = ta->next;
|
|
409
|
+
XsdAttrDecl* a = ta->attrs;
|
|
410
|
+
while (a) {
|
|
411
|
+
XsdAttrDecl* an = a->next;
|
|
412
|
+
free(a->name);
|
|
413
|
+
free(a->type);
|
|
414
|
+
free(a);
|
|
415
|
+
a = an;
|
|
416
|
+
}
|
|
417
|
+
free(ta->type_name);
|
|
418
|
+
free(ta);
|
|
419
|
+
ta = tn;
|
|
420
|
+
}
|
|
421
|
+
XsdElementDecl* d = s->elements;
|
|
422
|
+
while (d) {
|
|
423
|
+
XsdElementDecl* dn = d->next;
|
|
424
|
+
free(d->name);
|
|
425
|
+
free(d->type);
|
|
426
|
+
free(d);
|
|
427
|
+
d = dn;
|
|
428
|
+
}
|
|
429
|
+
free(s->target_ns);
|
|
430
|
+
if (s->validator)
|
|
431
|
+
xsd_validator_free((struct xsd_validator*)s->validator);
|
|
221
432
|
XsdSimple* t = s->simple_types;
|
|
222
433
|
while (t) {
|
|
223
434
|
XsdSimple* next = t->next;
|
|
@@ -261,6 +472,103 @@ LEPTRIS_API int leptris_xsd_simple_valid(LeptrisXsdSchema schema,
|
|
|
261
472
|
return xsd_valid_chain(s, t, lexical, 0);
|
|
262
473
|
}
|
|
263
474
|
|
|
475
|
+
/* ---- validator accessors (validate.c) --------------------------- */
|
|
476
|
+
|
|
477
|
+
const XsdElementDecl* xsd_find_element(struct leptris_xsd_schema* s,
|
|
478
|
+
const char* name) {
|
|
479
|
+
if (!s || !name) return NULL;
|
|
480
|
+
for (XsdElementDecl* d = s->elements; d; d = d->next)
|
|
481
|
+
if (strcmp(d->name, name) == 0) return d;
|
|
482
|
+
return NULL;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
XsdCm* xsd_find_complex(struct leptris_xsd_schema* s,
|
|
486
|
+
const char* type_name) {
|
|
487
|
+
if (!s || !type_name) return NULL;
|
|
488
|
+
for (XsdCm* c = s->complex_types; c; c = c->next)
|
|
489
|
+
if (strcmp(c->name, type_name) == 0) return c;
|
|
490
|
+
return NULL;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const XsdSimple* xsd_find_simple_pub(struct leptris_xsd_schema* s,
|
|
494
|
+
const char* name) {
|
|
495
|
+
return xsd_find_simple(s, name);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const char* xsd_target_ns(struct leptris_xsd_schema* s) {
|
|
499
|
+
return s ? s->target_ns : NULL;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const XsdTypeAttrs* xsd_find_type_attrs(struct leptris_xsd_schema* s,
|
|
503
|
+
const char* type_name) {
|
|
504
|
+
if (!s || !type_name) return NULL;
|
|
505
|
+
for (XsdTypeAttrs* t = s->type_attrs; t; t = t->next)
|
|
506
|
+
if (strcmp(t->type_name, type_name) == 0) return t;
|
|
507
|
+
return NULL;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* ---- slice 4: instance validation (validate.c engine) ------------ */
|
|
511
|
+
|
|
512
|
+
LEPTRIS_API int leptris_xsd_validate(LeptrisXsdSchema schema,
|
|
513
|
+
LeptrisDocument doc) {
|
|
514
|
+
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
515
|
+
if (!s || !doc) return -1;
|
|
516
|
+
struct xsd_validator* v = xsd_validator_new(s);
|
|
517
|
+
if (!v) return -1;
|
|
518
|
+
int r = xsd_validator_run(v, doc);
|
|
519
|
+
/* park the run's errors on the schema for the accessors; a new
|
|
520
|
+
* validate run replaces them. */
|
|
521
|
+
struct xsd_validator* prev = (struct xsd_validator*)s->validator;
|
|
522
|
+
if (prev) xsd_validator_free(prev);
|
|
523
|
+
s->validator = v;
|
|
524
|
+
return r;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
LEPTRIS_API size_t leptris_xsd_error_count(LeptrisXsdSchema schema) {
|
|
528
|
+
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
529
|
+
if (!s) return 0;
|
|
530
|
+
return xsd_validator_error_count(
|
|
531
|
+
(struct xsd_validator*)s->validator);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
LEPTRIS_API const char* leptris_xsd_error_at(LeptrisXsdSchema schema,
|
|
535
|
+
size_t i) {
|
|
536
|
+
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
537
|
+
if (!s) return NULL;
|
|
538
|
+
return xsd_validator_error_at((struct xsd_validator*)s->validator,
|
|
539
|
+
i);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
LEPTRIS_API int leptris_xsd_content_valid(
|
|
543
|
+
LeptrisXsdSchema schema, const char* element_name,
|
|
544
|
+
const char* const* child_names, const char* const* child_ns,
|
|
545
|
+
size_t child_count) {
|
|
546
|
+
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
547
|
+
if (!s || !element_name) return -1;
|
|
548
|
+
|
|
549
|
+
const XsdElementDecl* d = s->elements;
|
|
550
|
+
while (d && strcmp(d->name, element_name) != 0) d = d->next;
|
|
551
|
+
if (!d) return -1; /* unknown element */
|
|
552
|
+
|
|
553
|
+
if (!d->type) return 1; /* no type attr: anyType accepts all */
|
|
554
|
+
|
|
555
|
+
/* built-in or simpleType ref: element content is text-only —
|
|
556
|
+
* any element child fails */
|
|
557
|
+
if (strncmp(d->type, "xs:", 3) == 0 ||
|
|
558
|
+
xsd_find_simple(s, d->type))
|
|
559
|
+
return child_count == 0;
|
|
560
|
+
|
|
561
|
+
XsdCm* ct = s->complex_types;
|
|
562
|
+
while (ct && strcmp(ct->name, d->type) != 0) ct = ct->next;
|
|
563
|
+
if (!ct) return child_count == 0; /* unknown type: accept-empty */
|
|
564
|
+
|
|
565
|
+
XsdCm* model = ct->first_child;
|
|
566
|
+
if (!model) return child_count == 0;
|
|
567
|
+
|
|
568
|
+
return xsd_content_valid(s, model, s->target_ns, child_names,
|
|
569
|
+
child_ns, child_count);
|
|
570
|
+
}
|
|
571
|
+
|
|
264
572
|
LEPTRIS_API size_t leptris_xsd_declaration_count(LeptrisXsdSchema schema) {
|
|
265
573
|
if (!schema) return 0;
|
|
266
574
|
return ((struct leptris_xsd_schema*)schema)->declaration_count;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/* xsd/content_model.c — #1075 slice 3: content-model evaluation.
|
|
2
|
+
*
|
|
3
|
+
* The compiled model tree lowers into a Thompson NFA (epsilon +
|
|
4
|
+
* symbol transitions, occurrence bounds via counting copies and a
|
|
5
|
+
* loop for unbounded) and the child sequence is consumed by
|
|
6
|
+
* simulating the live state set — linear in children x states, no
|
|
7
|
+
* backtracking blowup on nested choices.
|
|
8
|
+
*
|
|
9
|
+
* Namespace filters (xs:any/@namespace): "##any" (default),
|
|
10
|
+
* "##other" (not the target namespace, not unqualified),
|
|
11
|
+
* "##targetNamespace", "##local", or a space-separated URI list.
|
|
12
|
+
*/
|
|
13
|
+
#include <stdlib.h>
|
|
14
|
+
#include <string.h>
|
|
15
|
+
|
|
16
|
+
#include "xsd_internal.h"
|
|
17
|
+
|
|
18
|
+
typedef struct {
|
|
19
|
+
int from;
|
|
20
|
+
int symbol; /* -1 = epsilon; else a particle transition */
|
|
21
|
+
XsdCm* particle; /* symbol owner (element ref or wildcard) */
|
|
22
|
+
int to;
|
|
23
|
+
} CmTrans;
|
|
24
|
+
|
|
25
|
+
typedef struct {
|
|
26
|
+
CmTrans* trans;
|
|
27
|
+
size_t count, cap;
|
|
28
|
+
int state_count; /* explicit: state ids are NOT transition ids */
|
|
29
|
+
int start, accept;
|
|
30
|
+
} CmNfa;
|
|
31
|
+
|
|
32
|
+
static int nfa_state(CmNfa* n) { return n->state_count++; }
|
|
33
|
+
|
|
34
|
+
static void nfa_trans(CmNfa* n, int from, int symbol, XsdCm* particle,
|
|
35
|
+
int to) {
|
|
36
|
+
if (n->count == n->cap) {
|
|
37
|
+
size_t cap = n->cap ? n->cap * 2 : 16;
|
|
38
|
+
CmTrans* grown = (CmTrans*)realloc(n->trans, cap * sizeof(*grown));
|
|
39
|
+
if (!grown) return; /* OOM: walk treats the NFA as non-matching */
|
|
40
|
+
n->trans = grown;
|
|
41
|
+
n->cap = cap;
|
|
42
|
+
}
|
|
43
|
+
n->trans[n->count].from = from;
|
|
44
|
+
n->trans[n->count].symbol = symbol;
|
|
45
|
+
n->trans[n->count].particle = particle;
|
|
46
|
+
n->trans[n->count].to = to;
|
|
47
|
+
n->count++;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static void cm_build(CmNfa* n, XsdCm* node, int from, int to);
|
|
51
|
+
static void cm_build_occurrence(CmNfa* n, XsdCm* node,
|
|
52
|
+
int from, int to);
|
|
53
|
+
|
|
54
|
+
/* chain the group children: first reads `from`, last writes `to`.
|
|
55
|
+
* xs:all is evaluated in sequence order for now (its order-free
|
|
56
|
+
* semantics arrive with slice 4's all-semantics pass). */
|
|
57
|
+
static void cm_build_children(CmNfa* n, XsdCm* node, int from, int to) {
|
|
58
|
+
XsdCm* c = node->first_child;
|
|
59
|
+
if (!c) {
|
|
60
|
+
nfa_trans(n, from, -1, NULL, to);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
int cursor = from;
|
|
64
|
+
while (c->next) {
|
|
65
|
+
int mid = nfa_state(n);
|
|
66
|
+
cm_build_occurrence(n, c, cursor, mid);
|
|
67
|
+
cursor = mid;
|
|
68
|
+
c = c->next;
|
|
69
|
+
}
|
|
70
|
+
cm_build_occurrence(n, c, cursor, to);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static void cm_build(CmNfa* n, XsdCm* node, int from, int to) {
|
|
74
|
+
switch (node->kind) {
|
|
75
|
+
case XSD_CM_ELEMENT:
|
|
76
|
+
case XSD_CM_ANY:
|
|
77
|
+
nfa_trans(n, from, 0, node, to);
|
|
78
|
+
return;
|
|
79
|
+
case XSD_CM_SEQ:
|
|
80
|
+
case XSD_CM_ALL:
|
|
81
|
+
cm_build_children(n, node, from, to);
|
|
82
|
+
return;
|
|
83
|
+
case XSD_CM_CHOICE: {
|
|
84
|
+
XsdCm* c = node->first_child;
|
|
85
|
+
if (!c) {
|
|
86
|
+
nfa_trans(n, from, -1, NULL, to);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
for (; c; c = c->next) cm_build_occurrence(n, c, from, to);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* occurrence bounds: min copies, then (max-min) optionals, or an
|
|
96
|
+
* epsilon self-loop region for unbounded. */
|
|
97
|
+
static void cm_build_occurrence(CmNfa* n, XsdCm* node, int from, int to) {
|
|
98
|
+
for (int i = 0; i < node->min; i++) {
|
|
99
|
+
int mid = nfa_state(n);
|
|
100
|
+
cm_build(n, node, from, mid);
|
|
101
|
+
from = mid;
|
|
102
|
+
}
|
|
103
|
+
if (node->max < 0) {
|
|
104
|
+
int loop = nfa_state(n);
|
|
105
|
+
nfa_trans(n, from, -1, NULL, loop);
|
|
106
|
+
cm_build(n, node, loop, loop);
|
|
107
|
+
nfa_trans(n, loop, -1, NULL, to);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
for (int i = node->min; i < node->max; i++) {
|
|
111
|
+
nfa_trans(n, from, -1, NULL, to);
|
|
112
|
+
int mid = nfa_state(n);
|
|
113
|
+
cm_build(n, node, from, mid);
|
|
114
|
+
from = mid;
|
|
115
|
+
}
|
|
116
|
+
nfa_trans(n, from, -1, NULL, to);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ---- namespace filter (xs:any/@namespace) ------------------------ */
|
|
120
|
+
|
|
121
|
+
static int any_ns_matches(XsdCm* any, const char* uri,
|
|
122
|
+
const char* target_ns) {
|
|
123
|
+
const char* f = any->any_ns ? any->any_ns : "##any";
|
|
124
|
+
if (strcmp(f, "##any") == 0) return 1;
|
|
125
|
+
if (strcmp(f, "##local") == 0) return uri == NULL;
|
|
126
|
+
if (strcmp(f, "##targetNamespace") == 0)
|
|
127
|
+
return target_ns && uri && strcmp(uri, target_ns) == 0;
|
|
128
|
+
if (strcmp(f, "##other") == 0)
|
|
129
|
+
return uri != NULL &&
|
|
130
|
+
!(target_ns && strcmp(uri, target_ns) == 0);
|
|
131
|
+
/* space-separated URI list (+##local) */
|
|
132
|
+
const char* p = f;
|
|
133
|
+
while (*p) {
|
|
134
|
+
while (*p == ' ') p++;
|
|
135
|
+
const char* start = p;
|
|
136
|
+
while (*p && *p != ' ') p++;
|
|
137
|
+
size_t len = (size_t)(p - start);
|
|
138
|
+
if (len == 0) break;
|
|
139
|
+
if (strncmp(start, "##local", len) == 0 && len == 7) {
|
|
140
|
+
if (uri == NULL) return 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (uri && strlen(uri) == len && strncmp(start, uri, len) == 0)
|
|
144
|
+
return 1;
|
|
145
|
+
}
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* ---- simulation --------------------------------------------------- */
|
|
150
|
+
|
|
151
|
+
typedef struct {
|
|
152
|
+
int* states;
|
|
153
|
+
size_t count, cap;
|
|
154
|
+
} StateSet;
|
|
155
|
+
|
|
156
|
+
static void ss_add(StateSet* ss, int st) {
|
|
157
|
+
for (size_t i = 0; i < ss->count; i++)
|
|
158
|
+
if (ss->states[i] == st) return;
|
|
159
|
+
if (ss->count == ss->cap) {
|
|
160
|
+
size_t cap = ss->cap ? ss->cap * 2 : 16;
|
|
161
|
+
int* grown = (int*)realloc(ss->states, cap * sizeof(int));
|
|
162
|
+
if (!grown) return;
|
|
163
|
+
ss->states = grown;
|
|
164
|
+
ss->cap = cap;
|
|
165
|
+
}
|
|
166
|
+
ss->states[ss->count++] = st;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static void ss_close(StateSet* ss, CmNfa* n) {
|
|
170
|
+
/* fixpoint over epsilon edges */
|
|
171
|
+
size_t i = 0;
|
|
172
|
+
while (i < ss->count) {
|
|
173
|
+
int st = ss->states[i];
|
|
174
|
+
for (size_t t = 0; t < n->count; t++)
|
|
175
|
+
if (n->trans[t].from == st && n->trans[t].symbol == -1)
|
|
176
|
+
ss_add(ss, n->trans[t].to);
|
|
177
|
+
i++;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
int xsd_content_valid(struct leptris_xsd_schema* s, XsdCm* model,
|
|
182
|
+
const char* target_ns, const char* const* names,
|
|
183
|
+
const char* const* ns_uris, size_t count) {
|
|
184
|
+
(void)s;
|
|
185
|
+
if (!model) return -1;
|
|
186
|
+
CmNfa n;
|
|
187
|
+
memset(&n, 0, sizeof(n));
|
|
188
|
+
n.start = nfa_state(&n);
|
|
189
|
+
n.accept = nfa_state(&n);
|
|
190
|
+
cm_build_occurrence(&n, model, n.start, n.accept);
|
|
191
|
+
|
|
192
|
+
StateSet live;
|
|
193
|
+
memset(&live, 0, sizeof(live));
|
|
194
|
+
ss_add(&live, n.start);
|
|
195
|
+
ss_close(&live, &n);
|
|
196
|
+
|
|
197
|
+
for (size_t c = 0; c < count; c++) {
|
|
198
|
+
StateSet next;
|
|
199
|
+
memset(&next, 0, sizeof(next));
|
|
200
|
+
const char* uri = ns_uris ? ns_uris[c] : NULL;
|
|
201
|
+
for (size_t i = 0; i < live.count; i++) {
|
|
202
|
+
for (size_t t = 0; t < n.count; t++) {
|
|
203
|
+
CmTrans* tr = &n.trans[t];
|
|
204
|
+
if (tr->from != live.states[i] || tr->symbol == -1)
|
|
205
|
+
continue;
|
|
206
|
+
XsdCm* p = tr->particle;
|
|
207
|
+
if (p->kind == XSD_CM_ELEMENT) {
|
|
208
|
+
if (strcmp(p->name, names[c]) == 0 &&
|
|
209
|
+
(!p->ns || (uri && strcmp(uri, p->ns) == 0)))
|
|
210
|
+
ss_add(&next, tr->to);
|
|
211
|
+
} else if (p->kind == XSD_CM_ANY) {
|
|
212
|
+
if (any_ns_matches(p, uri, target_ns))
|
|
213
|
+
ss_add(&next, tr->to);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
free(live.states);
|
|
218
|
+
live = next;
|
|
219
|
+
ss_close(&live, &n);
|
|
220
|
+
if (live.count == 0) {
|
|
221
|
+
free(n.trans);
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
int accept = 0;
|
|
227
|
+
for (size_t i = 0; i < live.count; i++)
|
|
228
|
+
if (live.states[i] == n.accept) accept = 1;
|
|
229
|
+
free(live.states);
|
|
230
|
+
free(n.trans);
|
|
231
|
+
return accept;
|
|
232
|
+
}
|