leptris 1.9.333.0-arm-linux → 1.9.334.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 +31 -0
- data/Rakefile +19 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/descriptor.rb +55 -0
- data/lib/leptris/xml/ffi.rb +25 -0
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +29 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/include/leptris/descriptor.h +30 -0
- data/vendor-src/libleptris/src/include/leptris.h +5 -0
- data/vendor-src/libleptris/src/leptris/descriptor/plan.c +144 -0
- data/vendor-src/libleptris/src/leptris/dom/element_modify.c +7 -0
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +32 -4
- data/vendor-src/libleptris/src/leptris/xsd/content_model.c +35 -0
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +96 -7
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +1 -0
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +95 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +103 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd2cbe214147c78280cf50d20d970f9e66ab0d4348651327e75e3dbd8cfac964
|
|
4
|
+
data.tar.gz: 0ccd0d2b41ed6cec2f5af1bacdc944de14bca5a5a4b4dcdbd8729ae610d291ac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41ee708352f94d4795bf2066961e335eb16e8d537b0afd5af1cc63785ae5d5c9735600677fee0fd609986d4c9f591c711233de345396999771fc59c27f90ab59
|
|
7
|
+
data.tar.gz: 82c5ddfa78c6051e4431a59f84f613d3320ec7bd30dfc3b168d8135dda25e58ca9ee107f8c9a0b03fb55df1808054080740c5990ae0a235ec3f8cc444550e489
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
## [1.9.334.0] - 2026-10-10
|
|
2
|
+
|
|
3
|
+
### Added — engine sync (libleptris 1.9.332-1.9.334)
|
|
4
|
+
|
|
5
|
+
- **Descriptor#build_result — serialize-side construction
|
|
6
|
+
(#408's face)**: `leptris_plan_result_build` builds a result
|
|
7
|
+
tree from row ops (:element structural opens, :scalar nesting
|
|
8
|
+
inside its producing row's element implicitly, :attr by name,
|
|
9
|
+
:end closes) that `#serialize` renders — the reverse of #walk,
|
|
10
|
+
byte-identical round-trip pinned. `leptris_element_add_child_addr`
|
|
11
|
+
attached (the #408 address-based attach; sugar is #408's design
|
|
12
|
+
work). Audit 384/383.
|
|
13
|
+
- **XSD 1.1 tier 2 (1.9.332)**: `xs:assert` on complexTypes
|
|
14
|
+
(XPath 2 lane, per-instance), `xs:assertion` on simpleTypes,
|
|
15
|
+
`xs:openContent` — `xs:assert` pinned.
|
|
16
|
+
- **Completion wave (1.9.334)**: `xs:all` order-free with foreign
|
|
17
|
+
children rejected; strict validation of undeclared elements
|
|
18
|
+
and attributes — both pinned.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **#1615 resolved as a malformed repro**: the well-formed mixed
|
|
23
|
+
union (memberTypes + inline member) validates correctly on
|
|
24
|
+
both paths — my filed schema was missing a `</xs:restriction>`
|
|
25
|
+
close. On 1.9.334 the malformed schema compiles to an
|
|
26
|
+
error-carrying handle ("schema document is not well-formed
|
|
27
|
+
XML") and fails validation CLOSED (it over-accepted silently
|
|
28
|
+
on 1.9.331 — the real defect, fixed engine-side by their
|
|
29
|
+
regression pin). Both behaviors spec-pinned; leptris#1615
|
|
30
|
+
closed with the correction.
|
|
31
|
+
|
|
1
32
|
## [1.9.331.0] - 2026-10-10
|
|
2
33
|
|
|
3
34
|
### Added — engine sync (libleptris 1.9.325-1.9.331)
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
8
8
|
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
9
|
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
10
|
# CHANGELOG when libleptris releases.
|
|
11
|
-
LIBLEPTRIS_VERSION = "1.9.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.9.334"
|
|
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
|
|
@@ -31,6 +31,22 @@ CMAKE_FLAGS = %w[
|
|
|
31
31
|
require "tmpdir"
|
|
32
32
|
|
|
33
33
|
desc "Build libleptris #{LIBLEPTRIS_VERSION} (+ utf8proc) from release tarballs into lib/"
|
|
34
|
+
# leptris#1623: macOS 14's dyld intermittently SIGKILLs
|
|
35
|
+
# linker-signed adhoc images at load ("CODESIGNING Invalid Page" —
|
|
36
|
+
# the same kill the atomic-replace dance below already guards in
|
|
37
|
+
# dev). The linker's implicit signature is the bare minimum; an
|
|
38
|
+
# explicit codesign at package time writes a complete, deterministic
|
|
39
|
+
# CodeDirectory that survives the gem pipeline (cp, packaging,
|
|
40
|
+
# install). Re-sign every shipped darwin binary and verify it.
|
|
41
|
+
def sign_darwin_binaries(*bins)
|
|
42
|
+
return unless RUBY_PLATFORM =~ /darwin/
|
|
43
|
+
|
|
44
|
+
bins.flatten.compact.each do |bin|
|
|
45
|
+
sh "codesign --force --sign - #{bin}"
|
|
46
|
+
sh "codesign --verify #{bin}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
34
50
|
task :compile do
|
|
35
51
|
# GitHub tarball downloads intermittently return a non-gzip body
|
|
36
52
|
# (rate-limit/redirect HTML) — the piped curl|tar form then fails
|
|
@@ -256,6 +272,7 @@ task :compile do
|
|
|
256
272
|
Dir.glob("#{u8_prefix}/{lib,bin}/utf8proc.dll").first
|
|
257
273
|
raise "utf8proc shared library not found after build" unless u8_lib
|
|
258
274
|
cp(u8_lib, "lib/#{File.basename(u8_lib)}")
|
|
275
|
+
sign_darwin_binaries("lib/libleptris#{ext}", "lib/#{File.basename(u8_lib)}")
|
|
259
276
|
puts "Vendored #{File.basename(lib)} + #{File.basename(u8_lib)} into lib/"
|
|
260
277
|
|
|
261
278
|
# Opt-in native read layer ext (#185): built and vendored beside
|
|
@@ -319,6 +336,7 @@ task :compile do
|
|
|
319
336
|
dest = "lib/leptris/xml/#{File.basename(bundle)}"
|
|
320
337
|
cp(bundle, "#{dest}.new")
|
|
321
338
|
mv("#{dest}.new", dest)
|
|
339
|
+
sign_darwin_binaries(dest)
|
|
322
340
|
puts "Vendored native layer (#{File.basename(bundle)}) into lib/leptris/xml/"
|
|
323
341
|
end
|
|
324
342
|
end
|
data/lib/leptris/version.rb
CHANGED
|
@@ -347,6 +347,61 @@ class Leptris::XML::Descriptor
|
|
|
347
347
|
owner: true, plans: @plans, plan: @plans[0])
|
|
348
348
|
end
|
|
349
349
|
|
|
350
|
+
# Serialize-side construction (leptris_plan_result_build): a
|
|
351
|
+
# result tree from +ops+ — hashes of {kind: :element / :scalar /
|
|
352
|
+
# :attr / :end, plan_index:, row_index:, name:, value:} — that
|
|
353
|
+
# #serialize renders through this plan. Encoding: an :element op
|
|
354
|
+
# opens a structural element (row_index UINT32_MAX = the root);
|
|
355
|
+
# a :scalar op carries its PRODUCING row and nests inside that
|
|
356
|
+
# row's element implicitly; :attr attaches by name; :end closes.
|
|
357
|
+
# Returns a root-owned PlanValue.
|
|
358
|
+
ROW_OP_KINDS = {
|
|
359
|
+
scalar: Leptris::XML::FFI::PLAN_OP_SCALAR,
|
|
360
|
+
element: Leptris::XML::FFI::PLAN_OP_ELEMENT,
|
|
361
|
+
attr: Leptris::XML::FFI::PLAN_OP_ATTR,
|
|
362
|
+
end: Leptris::XML::FFI::PLAN_OP_END,
|
|
363
|
+
}.freeze
|
|
364
|
+
private_constant :ROW_OP_KINDS
|
|
365
|
+
|
|
366
|
+
def build_result(ops)
|
|
367
|
+
memory = ::FFI::MemoryPointer.new(Leptris::XML::FFI::PlanRowOp, ops.size)
|
|
368
|
+
anchors = [memory]
|
|
369
|
+
ops.each_with_index do |op, i|
|
|
370
|
+
row = Leptris::XML::FFI::PlanRowOp.new(memory[i])
|
|
371
|
+
kind = ROW_OP_KINDS.fetch(op.fetch(:kind)) do
|
|
372
|
+
raise ArgumentError,
|
|
373
|
+
"op kind must be one of #{ROW_OP_KINDS.keys.inspect}"
|
|
374
|
+
end
|
|
375
|
+
value = op[:value]
|
|
376
|
+
row[:kind] = kind
|
|
377
|
+
row[:plan_index] = op.fetch(:plan_index, 0)
|
|
378
|
+
row[:row_index] = op.fetch(:row_index, 0xFFFF_FFFF)
|
|
379
|
+
row[:name] =
|
|
380
|
+
if (n = op[:name])
|
|
381
|
+
anchors << (ptr = ::FFI::MemoryPointer.from_string(n.to_s))
|
|
382
|
+
ptr
|
|
383
|
+
end
|
|
384
|
+
if value.nil?
|
|
385
|
+
row[:value] = nil
|
|
386
|
+
row[:value_len] = 0
|
|
387
|
+
else
|
|
388
|
+
s = value.to_s
|
|
389
|
+
anchors << (ptr = ::FFI::MemoryPointer.from_string(s))
|
|
390
|
+
row[:value] = ptr
|
|
391
|
+
row[:value_len] = s.bytesize
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
status = ::FFI::MemoryPointer.new(:int)
|
|
395
|
+
raw = Leptris::XML::FFI.leptris_plan_result_build(
|
|
396
|
+
@handle, memory, ops.size, status)
|
|
397
|
+
if raw.null?
|
|
398
|
+
raise Leptris::XML::Error,
|
|
399
|
+
"plan result build failed (status=#{status.read_int})"
|
|
400
|
+
end
|
|
401
|
+
Leptris::XML::PlanValue.new(ResultHandle.new(raw),
|
|
402
|
+
owner: true, plans: @plans, plan: @plans[0])
|
|
403
|
+
end
|
|
404
|
+
|
|
350
405
|
# libleptris 1.9.312 (#1551): serialize a walk result back to
|
|
351
406
|
# XML guided by this plan — the plan supplies element wrappers
|
|
352
407
|
# (row wire_name + ns_prefix), the result the content; children
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -418,6 +418,26 @@ module Leptris
|
|
|
418
418
|
:leptris_plan_result
|
|
419
419
|
attach_function :leptris_plan_result_free,
|
|
420
420
|
[:leptris_plan_result], :void
|
|
421
|
+
# libleptris 1.9.324+: construct a result tree from row ops
|
|
422
|
+
# (the serialize-side fast path — the reverse of the walk):
|
|
423
|
+
# ELEMENT opens under the current parent, SCALAR/ATTR carry
|
|
424
|
+
# values, END closes. Strings are copied at build; free with
|
|
425
|
+
# leptris_plan_result_free (the ResultHandle does).
|
|
426
|
+
class PlanRowOp < ::FFI::Struct
|
|
427
|
+
layout :kind, :int,
|
|
428
|
+
:plan_index, :uint32,
|
|
429
|
+
:row_index, :uint32,
|
|
430
|
+
:name, :pointer,
|
|
431
|
+
:value, :pointer,
|
|
432
|
+
:value_len, :size_t
|
|
433
|
+
end
|
|
434
|
+
PLAN_OP_SCALAR = 0
|
|
435
|
+
PLAN_OP_ELEMENT = 1
|
|
436
|
+
PLAN_OP_ATTR = 2
|
|
437
|
+
PLAN_OP_END = 3
|
|
438
|
+
attach_function :leptris_plan_result_build,
|
|
439
|
+
[:leptris_plan, :pointer, :size_t, :pointer], :leptris_plan_result
|
|
440
|
+
|
|
421
441
|
# libleptris 1.9.312 (#1551): serialize a walk result back
|
|
422
442
|
# to XML guided by its plan — the plan supplies element
|
|
423
443
|
# wrappers (row wire_name + ns_prefix), the result the
|
|
@@ -858,6 +878,11 @@ attach_function :leptris_parse_string,
|
|
|
858
878
|
[:leptris_element, :string], :leptris_status
|
|
859
879
|
attach_function :leptris_element_append_child,
|
|
860
880
|
[:leptris_element, :leptris_element], :leptris_status
|
|
881
|
+
# #408: address-based attach — bindings pass raw addresses
|
|
882
|
+
# and skip the per-node FFI pointer-wrapper mint. Same
|
|
883
|
+
# semantics and status contract as the pointer face.
|
|
884
|
+
attach_function :leptris_element_add_child_addr,
|
|
885
|
+
[:ulong_long, :ulong_long], :leptris_status
|
|
861
886
|
# Fused create+append (libleptris 1.9.153): one document
|
|
862
887
|
# resolution, identical tree semantics to the two-call pair.
|
|
863
888
|
attach_function :leptris_element_create_child,
|
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.334] - 2026-10-10
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **XSD completion wave (#1075, PR #1620).** `xs:all` at the model
|
|
8
|
+
root is order-free now (counting check: each member at most
|
|
9
|
+
maxOccurs, minOccurs floors, foreign children rejected) — it
|
|
10
|
+
previously enforced declaration order. Strict validation:
|
|
11
|
+
undeclared elements (including the document root) and undeclared
|
|
12
|
+
attributes fail with `not declared (strict)`, unless the type
|
|
13
|
+
captures `xs:anyAttribute`, whose namespace grammar
|
|
14
|
+
(`##any`/`##other`/`##targetNamespace`/`##local`/URI list) is
|
|
15
|
+
enforced. Unresolved named types are loud — `type not found in
|
|
16
|
+
the schema` — closing the silent-accept class behind #1615.
|
|
17
|
+
`$value` binds typed: the simple type's numeric chain decides
|
|
18
|
+
number-vs-string, not the lexical's shape. Eight red-first
|
|
19
|
+
specs; zero fallout in existing pins.
|
|
20
|
+
- **Native plan-result builder + address-based attach (leptris-
|
|
21
|
+
ruby#408 asks, PR #1621).** `leptris_plan_result_build` builds
|
|
22
|
+
the standalone result tree from a flat op program —
|
|
23
|
+
ELEMENT/SCALAR/ATTR/END, stamped exactly as a walk stamps it —
|
|
24
|
+
so `leptris_plan_serialize` serves standalone serialization with
|
|
25
|
+
zero DOM mint on the host (the to_xml → to_json-floor path).
|
|
26
|
+
`leptris_element_add_child_addr` attaches by raw addresses,
|
|
27
|
+
dropping the per-node FFI pointer-wrapper mint (~2 allocs/row).
|
|
28
|
+
The Ruby-side wiring rides the binding repo.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
3
32
|
## [1.9.333] - 2026-10-10
|
|
4
33
|
|
|
5
34
|
### Added
|
|
@@ -259,6 +259,36 @@ LEPTRIS_API LeptrisPlanResult leptris_plan_walk(LeptrisDocument doc,
|
|
|
259
259
|
LeptrisStatus* status);
|
|
260
260
|
LEPTRIS_API void leptris_plan_result_free(LeptrisPlanResult result);
|
|
261
261
|
|
|
262
|
+
/* #408 ask 2: build a standalone result natively from a flat op
|
|
263
|
+
* program — the same tree a walk produces (wrappers, ns bindings,
|
|
264
|
+
* kid order), so leptris_plan_serialize serves standalone
|
|
265
|
+
* serialization with no DOM mint on the host side. ELEMENT ops
|
|
266
|
+
* reference the plan row that captured the member (plan_index +
|
|
267
|
+
* row_index; the walk root uses row_index = UINT32_MAX); SCALAR
|
|
268
|
+
* and ATTR carry the value; END pops the open element. Programs
|
|
269
|
+
* must balance (every ELEMENT closed; no END without an open
|
|
270
|
+
* element) and open with the root element op. Strings are copied.
|
|
271
|
+
* Memory: free with leptris_plan_result_free. */
|
|
272
|
+
typedef enum {
|
|
273
|
+
LEPTRIS_PLAN_OP_SCALAR = 0,
|
|
274
|
+
LEPTRIS_PLAN_OP_ELEMENT = 1,
|
|
275
|
+
LEPTRIS_PLAN_OP_ATTR = 2,
|
|
276
|
+
LEPTRIS_PLAN_OP_END = 3
|
|
277
|
+
} LeptrisPlanRowOpKind;
|
|
278
|
+
|
|
279
|
+
typedef struct {
|
|
280
|
+
LeptrisPlanRowOpKind kind;
|
|
281
|
+
uint32_t plan_index; /* ELEMENT: the plan whose row captured it */
|
|
282
|
+
uint32_t row_index; /* producing row (UINT32_MAX = root) */
|
|
283
|
+
const char* name; /* ATTR: attribute name */
|
|
284
|
+
const char* value; /* SCALAR/ATTR: value (NULL = empty) */
|
|
285
|
+
size_t value_len;
|
|
286
|
+
} leptris_plan_row_op;
|
|
287
|
+
|
|
288
|
+
LEPTRIS_API LeptrisPlanResult leptris_plan_result_build(
|
|
289
|
+
LeptrisPlan plan, const leptris_plan_row_op* ops, size_t count,
|
|
290
|
+
LeptrisStatus* status);
|
|
291
|
+
|
|
262
292
|
/* #1551: serialize a walk result back to XML, guided by its plan.
|
|
263
293
|
* The plan supplies element wrappers (row wire_name + ns_prefix)
|
|
264
294
|
* and the result supplies content; children emit in document order
|
|
@@ -2153,6 +2153,11 @@ LEPTRIS_API LeptrisStatus leptris_element_set_name(LeptrisElement elem, const ch
|
|
|
2153
2153
|
* @param child Child element to append
|
|
2154
2154
|
* @return LEPTRIS_OK on success, error code otherwise
|
|
2155
2155
|
*/
|
|
2156
|
+
/* #408: address-based attach — bindings pass raw addresses and
|
|
2157
|
+
* skip the per-node FFI pointer-wrapper mint. Same semantics and
|
|
2158
|
+
* status contract as leptris_element_append_child. */
|
|
2159
|
+
LEPTRIS_API LeptrisStatus leptris_element_add_child_addr(uintptr_t parent_addr,
|
|
2160
|
+
uintptr_t child_addr);
|
|
2156
2161
|
LEPTRIS_API LeptrisStatus leptris_element_append_child(LeptrisElement parent, LeptrisElement child);
|
|
2157
2162
|
|
|
2158
2163
|
/**
|
|
@@ -1530,6 +1530,150 @@ LEPTRIS_API char* leptris_plan_serialize(LeptrisPlan plan,
|
|
|
1530
1530
|
return b.s;
|
|
1531
1531
|
}
|
|
1532
1532
|
|
|
1533
|
+
LEPTRIS_API LeptrisPlanResult leptris_plan_result_build(
|
|
1534
|
+
LeptrisPlan plan, const leptris_plan_row_op* ops, size_t count,
|
|
1535
|
+
LeptrisStatus* status) {
|
|
1536
|
+
if (status) *status = LEPTRIS_OK;
|
|
1537
|
+
if (!plan || !ops || count == 0) {
|
|
1538
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
1539
|
+
return NULL;
|
|
1540
|
+
}
|
|
1541
|
+
const struct leptris_plan* pool =
|
|
1542
|
+
(const struct leptris_plan*)plan;
|
|
1543
|
+
/* op stack of open elements; depth is program-bounded */
|
|
1544
|
+
struct leptris_plan_result** stack =
|
|
1545
|
+
(struct leptris_plan_result**)calloc(count,
|
|
1546
|
+
sizeof(*stack));
|
|
1547
|
+
if (!stack) {
|
|
1548
|
+
if (status) *status = LEPTRIS_ERROR_MEMORY;
|
|
1549
|
+
return NULL;
|
|
1550
|
+
}
|
|
1551
|
+
int sp = 0;
|
|
1552
|
+
struct leptris_plan_result* root = NULL;
|
|
1553
|
+
for (size_t i = 0; i < count; i++) {
|
|
1554
|
+
const leptris_plan_row_op* op = &ops[i];
|
|
1555
|
+
if (op->kind == LEPTRIS_PLAN_OP_ELEMENT) {
|
|
1556
|
+
if (op->plan_index >= pool->plan_count ||
|
|
1557
|
+
(op->row_index != UINT32_MAX &&
|
|
1558
|
+
op->row_index >= pool->plans[op->plan_index]
|
|
1559
|
+
.child_count)) {
|
|
1560
|
+
free(stack);
|
|
1561
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
1562
|
+
return NULL;
|
|
1563
|
+
}
|
|
1564
|
+
struct leptris_plan_result* v = dp_value_new(
|
|
1565
|
+
LEPTRIS_PLAN_VALUE_ELEMENT);
|
|
1566
|
+
if (!v) goto oom;
|
|
1567
|
+
const dp_plan* cp = &pool->plans[op->plan_index];
|
|
1568
|
+
const char* wire =
|
|
1569
|
+
op->row_index == UINT32_MAX
|
|
1570
|
+
? cp->element_name
|
|
1571
|
+
: cp->child_plans[op->row_index].wire_name;
|
|
1572
|
+
v->name = dp_strdup(wire);
|
|
1573
|
+
v->container_plan = op->plan_index;
|
|
1574
|
+
v->row_index = op->row_index;
|
|
1575
|
+
if (op->row_index != UINT32_MAX) {
|
|
1576
|
+
const leptris_child_plan* cr =
|
|
1577
|
+
&cp->child_plans[op->row_index];
|
|
1578
|
+
v->ns_prefix = dp_strdup(cr->ns_prefix);
|
|
1579
|
+
v->ns_uri = dp_strdup(cr->ns_uri);
|
|
1580
|
+
}
|
|
1581
|
+
if (sp == 0) {
|
|
1582
|
+
if (root) { /* multiple roots */
|
|
1583
|
+
dp_result_free_rec(v);
|
|
1584
|
+
free(stack);
|
|
1585
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
1586
|
+
return NULL;
|
|
1587
|
+
}
|
|
1588
|
+
root = v;
|
|
1589
|
+
} else if (!dp_value_push(stack[sp - 1], v)) {
|
|
1590
|
+
dp_result_free_rec(v);
|
|
1591
|
+
goto oom;
|
|
1592
|
+
}
|
|
1593
|
+
v->order_index = (uint32_t)
|
|
1594
|
+
(sp == 0 ? 0 : stack[sp - 1]->kid_count);
|
|
1595
|
+
stack[sp++] = v;
|
|
1596
|
+
} else if (op->kind == LEPTRIS_PLAN_OP_SCALAR) {
|
|
1597
|
+
if (sp == 0) {
|
|
1598
|
+
free(stack);
|
|
1599
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
1600
|
+
return NULL;
|
|
1601
|
+
}
|
|
1602
|
+
struct leptris_plan_result* v =
|
|
1603
|
+
dp_value_new(LEPTRIS_PLAN_VALUE_SCALAR);
|
|
1604
|
+
if (!v) goto oom;
|
|
1605
|
+
const dp_plan* cp = &pool->plans[op->plan_index];
|
|
1606
|
+
if (op->row_index < cp->child_count)
|
|
1607
|
+
v->name = dp_strdup(
|
|
1608
|
+
cp->child_plans[op->row_index].wire_name);
|
|
1609
|
+
if (!dp_value_set_str(v, op->value ? op->value : "",
|
|
1610
|
+
op->value ? op->value_len : 0))
|
|
1611
|
+
dp_value_set_str(v, "", 0);
|
|
1612
|
+
if (!dp_value_push(stack[sp - 1], v)) {
|
|
1613
|
+
dp_result_free_rec(v);
|
|
1614
|
+
goto oom;
|
|
1615
|
+
}
|
|
1616
|
+
} else if (op->kind == LEPTRIS_PLAN_OP_ATTR) {
|
|
1617
|
+
if (sp == 0 || !op->name) {
|
|
1618
|
+
free(stack);
|
|
1619
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
1620
|
+
return NULL;
|
|
1621
|
+
}
|
|
1622
|
+
struct leptris_plan_result* el = stack[sp - 1];
|
|
1623
|
+
char** gn = (char**)realloc(
|
|
1624
|
+
el->attr_names,
|
|
1625
|
+
(el->attr_count + 1) * sizeof(char*));
|
|
1626
|
+
char** gv = (char**)realloc(
|
|
1627
|
+
el->attr_values,
|
|
1628
|
+
(el->attr_count + 1) * sizeof(char*));
|
|
1629
|
+
char** gp = (char**)realloc(
|
|
1630
|
+
el->attr_ns_prefixes,
|
|
1631
|
+
(el->attr_count + 1) * sizeof(char*));
|
|
1632
|
+
char** gu = (char**)realloc(
|
|
1633
|
+
el->attr_ns_uris,
|
|
1634
|
+
(el->attr_count + 1) * sizeof(char*));
|
|
1635
|
+
if (!gn || !gv || !gp || !gu) {
|
|
1636
|
+
free(gn); free(gv); free(gp); free(gu);
|
|
1637
|
+
goto oom;
|
|
1638
|
+
}
|
|
1639
|
+
el->attr_names = gn;
|
|
1640
|
+
el->attr_values = gv;
|
|
1641
|
+
el->attr_ns_prefixes = gp;
|
|
1642
|
+
el->attr_ns_uris = gu;
|
|
1643
|
+
gn[el->attr_count] = dp_strdup(op->name);
|
|
1644
|
+
gv[el->attr_count] =
|
|
1645
|
+
dp_strdup_n(op->value ? op->value : "",
|
|
1646
|
+
op->value ? op->value_len : 0);
|
|
1647
|
+
gp[el->attr_count] = NULL;
|
|
1648
|
+
gu[el->attr_count] = NULL;
|
|
1649
|
+
if (!gn[el->attr_count] || !gv[el->attr_count])
|
|
1650
|
+
goto oom;
|
|
1651
|
+
el->attr_count++;
|
|
1652
|
+
} else if (op->kind == LEPTRIS_PLAN_OP_END) {
|
|
1653
|
+
if (sp == 0) {
|
|
1654
|
+
free(stack);
|
|
1655
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
1656
|
+
return NULL;
|
|
1657
|
+
}
|
|
1658
|
+
sp--;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
int balanced = (sp == 0 && root != NULL);
|
|
1662
|
+
free(stack);
|
|
1663
|
+
if (!balanced) {
|
|
1664
|
+
dp_result_free_rec(root);
|
|
1665
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
1666
|
+
return NULL;
|
|
1667
|
+
}
|
|
1668
|
+
return (LeptrisPlanResult)root;
|
|
1669
|
+
|
|
1670
|
+
oom:
|
|
1671
|
+
if (root) dp_result_free_rec(root);
|
|
1672
|
+
free(stack);
|
|
1673
|
+
if (status) *status = LEPTRIS_ERROR_MEMORY;
|
|
1674
|
+
return NULL;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1533
1677
|
LEPTRIS_API void leptris_plan_result_free(LeptrisPlanResult result) {
|
|
1534
1678
|
dp_result_free_rec((struct leptris_plan_result*)result);
|
|
1535
1679
|
}
|
|
@@ -493,6 +493,13 @@ static LeptrisNode* adopt_for_splice(LeptrisNode* node,
|
|
|
493
493
|
return copy ? (LeptrisNode*)copy : NULL;
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
+
LeptrisStatus leptris_element_add_child_addr(uintptr_t parent_addr,
|
|
497
|
+
uintptr_t child_addr) {
|
|
498
|
+
if (!parent_addr || !child_addr) return LEPTRIS_ERROR_NULL_ARG;
|
|
499
|
+
return leptris_element_append_child((LeptrisElement)parent_addr,
|
|
500
|
+
(LeptrisElement)child_addr);
|
|
501
|
+
}
|
|
502
|
+
|
|
496
503
|
LeptrisStatus leptris_element_append_child(LeptrisElement parent, LeptrisElement child) {
|
|
497
504
|
if (!parent || !child) return LEPTRIS_ERROR_NULL_ARG;
|
|
498
505
|
|
|
@@ -79,13 +79,15 @@ static char* xsd_read_file(const char* path, size_t* out_len);
|
|
|
79
79
|
static XsdAttrDecl* xsd_copy_attr_rows(const XsdAttrDecl* src);
|
|
80
80
|
static void xsd_register_complex(struct leptris_xsd_schema* s,
|
|
81
81
|
XsdCm* root, XsdAttrDecl* attrs,
|
|
82
|
-
const char* name
|
|
82
|
+
const char* name,
|
|
83
|
+
char* any_attr_ns);
|
|
83
84
|
static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
84
85
|
LeptrisElement ct,
|
|
85
86
|
const char* name);
|
|
86
87
|
static XsdAssert* xsd_capture_assert(struct leptris_xsd_schema* s,
|
|
87
88
|
LeptrisElement e);
|
|
88
89
|
static XsdCm* xsd_capture_any(LeptrisElement any_child);
|
|
90
|
+
static char* xsd_capture_any_attr_ns(LeptrisElement container);
|
|
89
91
|
static void xsd_capture_alternatives(struct leptris_xsd_schema* s,
|
|
90
92
|
LeptrisElement decl_elem,
|
|
91
93
|
XsdElementDecl* d);
|
|
@@ -900,6 +902,7 @@ LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
|
|
|
900
902
|
a = an;
|
|
901
903
|
}
|
|
902
904
|
free(ta->type_name);
|
|
905
|
+
free(ta->any_attr_ns);
|
|
903
906
|
free(ta);
|
|
904
907
|
ta = tn;
|
|
905
908
|
}
|
|
@@ -998,19 +1001,23 @@ static XsdCm* xsd_cm_copy_list(const XsdCm* head) {
|
|
|
998
1001
|
|
|
999
1002
|
static void xsd_register_complex(struct leptris_xsd_schema* s,
|
|
1000
1003
|
XsdCm* root, XsdAttrDecl* attrs,
|
|
1001
|
-
const char* name
|
|
1004
|
+
const char* name,
|
|
1005
|
+
char* any_attr_ns) {
|
|
1002
1006
|
if (!root) return;
|
|
1003
1007
|
root->name = xsd_strdup(name);
|
|
1004
1008
|
root->next = s->complex_types;
|
|
1005
1009
|
s->complex_types = root;
|
|
1006
|
-
if (attrs) {
|
|
1010
|
+
if (attrs || any_attr_ns) {
|
|
1007
1011
|
XsdTypeAttrs* ta = (XsdTypeAttrs*)calloc(1, sizeof(*ta));
|
|
1008
1012
|
if (ta) {
|
|
1009
1013
|
ta->type_name = xsd_strdup(name);
|
|
1010
1014
|
ta->attrs = attrs;
|
|
1015
|
+
ta->any_attr_ns = any_attr_ns;
|
|
1011
1016
|
ta->next = s->type_attrs;
|
|
1012
1017
|
s->type_attrs = ta;
|
|
1013
1018
|
}
|
|
1019
|
+
} else if (any_attr_ns) {
|
|
1020
|
+
free(any_attr_ns);
|
|
1014
1021
|
}
|
|
1015
1022
|
}
|
|
1016
1023
|
|
|
@@ -1108,6 +1115,7 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
1108
1115
|
XsdCm* root = xsd_cm_new(XSD_CM_SEQ); /* wrapper keeps the name */
|
|
1109
1116
|
if (!root) return NULL;
|
|
1110
1117
|
XsdAttrDecl* attrs = NULL;
|
|
1118
|
+
char* any_attr_ns = NULL;
|
|
1111
1119
|
|
|
1112
1120
|
for (LeptrisNodeRef n =
|
|
1113
1121
|
leptris_node_first_child(leptris_element_as_node(ct));
|
|
@@ -1175,6 +1183,8 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
1175
1183
|
/* XSD 1.1: asserts nested under the derivation */
|
|
1176
1184
|
if (s->version_11)
|
|
1177
1185
|
xsd_capture_assert_children(s, dq, root);
|
|
1186
|
+
if (!any_attr_ns)
|
|
1187
|
+
any_attr_ns = xsd_capture_any_attr_ns(dq);
|
|
1178
1188
|
}
|
|
1179
1189
|
} else if (strcmp(local, "simpleContent") == 0) {
|
|
1180
1190
|
for (LeptrisNodeRef q = leptris_node_first_child(
|
|
@@ -1241,6 +1251,7 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
1241
1251
|
* re-capturing would orphan those rows. */
|
|
1242
1252
|
root->first_child = xsd_cm_parse(s, ct, NULL);
|
|
1243
1253
|
if (!attrs) attrs = xsd_capture_attr_rows(s, ct, 0);
|
|
1254
|
+
if (!any_attr_ns) any_attr_ns = xsd_capture_any_attr_ns(ct);
|
|
1244
1255
|
}
|
|
1245
1256
|
/* XSD 1.1: openContent on the type, else the schema default */
|
|
1246
1257
|
if (s->version_11) {
|
|
@@ -1308,7 +1319,7 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
1308
1319
|
xsd_cm_free(root->open_any);
|
|
1309
1320
|
root->open_any = NULL;
|
|
1310
1321
|
}
|
|
1311
|
-
xsd_register_complex(s, root, attrs, name);
|
|
1322
|
+
xsd_register_complex(s, root, attrs, name, any_attr_ns);
|
|
1312
1323
|
return root;
|
|
1313
1324
|
}
|
|
1314
1325
|
|
|
@@ -1474,6 +1485,23 @@ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
|
|
|
1474
1485
|
return head;
|
|
1475
1486
|
}
|
|
1476
1487
|
|
|
1488
|
+
/* One container's xs:anyAttribute grammar (NULL when absent). */
|
|
1489
|
+
static char* xsd_capture_any_attr_ns(LeptrisElement container) {
|
|
1490
|
+
for (LeptrisNodeRef ac = leptris_node_first_child(
|
|
1491
|
+
leptris_element_as_node(container));
|
|
1492
|
+
ac; ac = leptris_node_next_sibling(ac)) {
|
|
1493
|
+
if (leptris_node_get_type(ac) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
1494
|
+
continue;
|
|
1495
|
+
LeptrisElement ae = (LeptrisElement)ac;
|
|
1496
|
+
const char* al = NULL;
|
|
1497
|
+
leptris_element_expanded_name(ae, &al, NULL, NULL);
|
|
1498
|
+
if (al && strcmp(al, "anyAttribute") == 0)
|
|
1499
|
+
return xsd_strdup(
|
|
1500
|
+
leptris_element_attribute(ae, "namespace"));
|
|
1501
|
+
}
|
|
1502
|
+
return NULL;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1477
1505
|
LEPTRIS_API int leptris_xsd_builtin_valid(const char* builtin,
|
|
1478
1506
|
const char* lexical) {
|
|
1479
1507
|
extern int xsd_builtin_valid(const char* type, const char* v);
|
|
@@ -208,10 +208,45 @@ static void ss_close(StateSet* ss, CmNfa* n) {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
/* XSD xs:all at the model root: order-free, each particle at
|
|
212
|
+
* most maxOccurs times, minOccurs floors — a counting check over
|
|
213
|
+
* the particle list (XSD 1.0 restricts xs:all to the model root,
|
|
214
|
+
* each child a particle, maxOccurs <= 1). */
|
|
215
|
+
static int all_root_valid(XsdCm* all, const char* target_ns,
|
|
216
|
+
const char* const* names,
|
|
217
|
+
const char* const* ns_uris, size_t count) {
|
|
218
|
+
for (XsdCm* p = all->first_child; p; p = p->next) {
|
|
219
|
+
size_t hits = 0;
|
|
220
|
+
for (size_t i = 0; i < count; i++) {
|
|
221
|
+
const char* uri = ns_uris ? ns_uris[i] : NULL;
|
|
222
|
+
if (p->name && strcmp(p->name, names[i]) == 0 &&
|
|
223
|
+
(!p->ns || (uri && strcmp(uri, p->ns) == 0)))
|
|
224
|
+
hits++;
|
|
225
|
+
}
|
|
226
|
+
if ((long)hits < p->min) return 0;
|
|
227
|
+
if (p->max >= 0 && (long)hits > p->max) return 0;
|
|
228
|
+
}
|
|
229
|
+
/* every instance child must belong to a particle */
|
|
230
|
+
for (size_t i = 0; i < count; i++) {
|
|
231
|
+
int matched = 0;
|
|
232
|
+
const char* uri = ns_uris ? ns_uris[i] : NULL;
|
|
233
|
+
for (XsdCm* p = all->first_child; p && !matched; p = p->next) {
|
|
234
|
+
if (p->name && strcmp(p->name, names[i]) == 0 &&
|
|
235
|
+
(!p->ns || (uri && strcmp(uri, p->ns) == 0)))
|
|
236
|
+
matched = 1;
|
|
237
|
+
}
|
|
238
|
+
if (!matched) return 0;
|
|
239
|
+
}
|
|
240
|
+
(void)target_ns;
|
|
241
|
+
return 1;
|
|
242
|
+
}
|
|
243
|
+
|
|
211
244
|
int xsd_content_valid(struct leptris_xsd_schema* s, XsdCm* model,
|
|
212
245
|
const char* target_ns, const char* const* names,
|
|
213
246
|
const char* const* ns_uris, size_t count) {
|
|
214
247
|
if (!model) return -1;
|
|
248
|
+
if (model->kind == XSD_CM_ALL && model->first_child)
|
|
249
|
+
return all_root_valid(model, target_ns, names, ns_uris, count);
|
|
215
250
|
CmNfa n;
|
|
216
251
|
memset(&n, 0, sizeof(n));
|
|
217
252
|
n.s = s;
|
|
@@ -88,6 +88,9 @@ static void verrf(struct xsd_validator* v, const char* what,
|
|
|
88
88
|
|
|
89
89
|
/* ---- lexical helpers ---------------------------------------------- */
|
|
90
90
|
|
|
91
|
+
static int simple_is_numeric(struct leptris_xsd_schema* s,
|
|
92
|
+
const XsdSimple* st);
|
|
93
|
+
|
|
91
94
|
static int text_valid(struct xsd_validator* v,
|
|
92
95
|
struct leptris_xsd_schema* s, const char* type,
|
|
93
96
|
const char* lexical, const char* what,
|
|
@@ -128,12 +131,10 @@ static int text_valid(struct xsd_validator* v,
|
|
|
128
131
|
}
|
|
129
132
|
if (mut->assertion_q) {
|
|
130
133
|
char sel[256];
|
|
131
|
-
int numeric =
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
numeric = 0;
|
|
136
|
-
if (numeric && *lexical)
|
|
134
|
+
int numeric =
|
|
135
|
+
simple_is_numeric(s, st) && *lexical &&
|
|
136
|
+
strspn(lexical, "0123456789.+-eE") == strlen(lexical);
|
|
137
|
+
if (numeric)
|
|
137
138
|
snprintf(sel, sizeof(sel), "%s", lexical);
|
|
138
139
|
else {
|
|
139
140
|
size_t w = 0;
|
|
@@ -259,6 +260,59 @@ static void validate_children(struct xsd_validator* v,
|
|
|
259
260
|
}
|
|
260
261
|
}
|
|
261
262
|
|
|
263
|
+
/* $value binds typed: a numeric-derived simple type binds the
|
|
264
|
+
* lexical as a number literal, everything else as a string — the
|
|
265
|
+
* TYPE decides, not the lexical's shape ("5" under xs:string is
|
|
266
|
+
* the string '5'). */
|
|
267
|
+
static int numeric_builtin(const char* b) {
|
|
268
|
+
static const char* nums[] = {
|
|
269
|
+
"xs:integer", "xs:decimal", "xs:double", "xs:float",
|
|
270
|
+
"xs:int", "xs:long", "xs:short", "xs:byte",
|
|
271
|
+
"xs:nonNegativeInteger", "xs:positiveInteger",
|
|
272
|
+
"xs:negativeInteger", "xs:nonPositiveInteger",
|
|
273
|
+
"xs:unsignedLong", "xs:unsignedInt",
|
|
274
|
+
"xs:unsignedShort", "xs:unsignedByte", NULL};
|
|
275
|
+
for (int i = 0; nums[i]; i++)
|
|
276
|
+
if (strcmp(b, nums[i]) == 0) return 1;
|
|
277
|
+
return 0;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
static int simple_is_numeric(struct leptris_xsd_schema* s,
|
|
281
|
+
const XsdSimple* st) {
|
|
282
|
+
for (int d = 0; st && d < 32; d++) {
|
|
283
|
+
if (!st->base) return 0;
|
|
284
|
+
if (strncmp(st->base, "xs:", 3) == 0)
|
|
285
|
+
return numeric_builtin(st->base);
|
|
286
|
+
st = xsd_find_simple_pub(s, st->base);
|
|
287
|
+
}
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/* xs:anyAttribute grammar: ##any (or an absent attribute),
|
|
292
|
+
* ##other, ##targetNamespace, ##local, or a space-separated URI
|
|
293
|
+
* list. */
|
|
294
|
+
static int any_attr_matches(const char* grammar, const char* uri,
|
|
295
|
+
const char* target_ns) {
|
|
296
|
+
if (!grammar || strcmp(grammar, "##any") == 0) return 1;
|
|
297
|
+
if (strcmp(grammar, "##other") == 0)
|
|
298
|
+
return uri && target_ns && strcmp(uri, target_ns) != 0;
|
|
299
|
+
if (strcmp(grammar, "##targetNamespace") == 0)
|
|
300
|
+
return uri && target_ns && strcmp(uri, target_ns) == 0;
|
|
301
|
+
if (strcmp(grammar, "##local") == 0) return uri == NULL;
|
|
302
|
+
/* URI list */
|
|
303
|
+
const char* p = grammar;
|
|
304
|
+
while (*p) {
|
|
305
|
+
while (*p == ' ' || *p == '\t') p++;
|
|
306
|
+
if (!*p) break;
|
|
307
|
+
const char* start = p;
|
|
308
|
+
while (*p && *p != ' ' && *p != '\t') p++;
|
|
309
|
+
size_t len = (size_t)(p - start);
|
|
310
|
+
if (uri && strlen(uri) == len && strncmp(uri, start, len) == 0)
|
|
311
|
+
return 1;
|
|
312
|
+
}
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
|
|
262
316
|
static void validate_attributes(struct xsd_validator* v,
|
|
263
317
|
struct leptris_xsd_schema* s,
|
|
264
318
|
LeptrisElement elem,
|
|
@@ -294,6 +348,32 @@ static void validate_attributes(struct xsd_validator* v,
|
|
|
294
348
|
verrf(v, "attribute", a->name,
|
|
295
349
|
"value does not match the fixed value");
|
|
296
350
|
}
|
|
351
|
+
/* strict: every instance attribute must be declared (or pass
|
|
352
|
+
* the type's xs:anyAttribute grammar). Namespace declarations
|
|
353
|
+
* are not attributes. */
|
|
354
|
+
for (LeptrisAttribute at = leptris_element_first_attribute(elem);
|
|
355
|
+
at; at = leptris_attribute_next(at)) {
|
|
356
|
+
const char* qn = leptris_attribute_get_name(at);
|
|
357
|
+
if (!qn || strncmp(qn, "xmlns", 5) == 0) continue;
|
|
358
|
+
const char* colon = strchr(qn, ':');
|
|
359
|
+
const char* local = colon ? colon + 1 : qn;
|
|
360
|
+
int declared = 0;
|
|
361
|
+
for (XsdAttrDecl* a = ta->attrs; a && !declared; a = a->next)
|
|
362
|
+
if (strcmp(a->name, local) == 0 ||
|
|
363
|
+
strcmp(a->name, qn) == 0)
|
|
364
|
+
declared = 1;
|
|
365
|
+
if (declared) continue;
|
|
366
|
+
if (ta->any_attr_ns) {
|
|
367
|
+
/* namespace of the attribute: engine-side attributes
|
|
368
|
+
* carry their uri through the expanded form */
|
|
369
|
+
const char* auri =
|
|
370
|
+
leptris_attribute_namespace_uri(at);
|
|
371
|
+
if (any_attr_matches(ta->any_attr_ns, auri,
|
|
372
|
+
xsd_target_ns(s)))
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
verrf(v, "attribute", local, "not declared (strict)");
|
|
376
|
+
}
|
|
297
377
|
}
|
|
298
378
|
|
|
299
379
|
static void validate_element(struct xsd_validator* v,
|
|
@@ -304,7 +384,12 @@ static void validate_element(struct xsd_validator* v,
|
|
|
304
384
|
leptris_element_expanded_name(elem, &local, &prefix, &uri);
|
|
305
385
|
if (!local) return;
|
|
306
386
|
const XsdElementDecl* d = xsd_find_element(s, local);
|
|
307
|
-
if (!d)
|
|
387
|
+
if (!d) {
|
|
388
|
+
/* strict: the instance uses an element the schema never
|
|
389
|
+
* declared (a local particle would have typed it) */
|
|
390
|
+
verrf(v, "element", local, "not declared (strict)");
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
308
393
|
|
|
309
394
|
/* XSD 1.1: the first alternative whose test passes types the
|
|
310
395
|
* element for this instance (context = the element). */
|
|
@@ -377,7 +462,11 @@ static void validate_local(struct xsd_validator* v,
|
|
|
377
462
|
const char* text = leptris_element_text(elem);
|
|
378
463
|
if (text && *text)
|
|
379
464
|
text_valid(v, s, type, text, "element", local, elem);
|
|
465
|
+
return;
|
|
380
466
|
}
|
|
467
|
+
/* neither complex nor simple nor builtin: the schema is
|
|
468
|
+
* incomplete — loud, never a silent accept (#1615 class) */
|
|
469
|
+
verrf(v, "element", local, "type not found in the schema");
|
|
381
470
|
}
|
|
382
471
|
|
|
383
472
|
/* ---- slice 6: identity constraints -------------------------------- */
|
|
@@ -2131,3 +2131,98 @@ TEST(Plan1586, ExactUriRowStillWinsAndUnqualifiedStillBinds) {
|
|
|
2131
2131
|
leptris_document_free(doc);
|
|
2132
2132
|
leptris_plan_free(plan);
|
|
2133
2133
|
}
|
|
2134
|
+
|
|
2135
|
+
// ---- #408: native result builder + address attach -------------------
|
|
2136
|
+
namespace {
|
|
2137
|
+
|
|
2138
|
+
/* leptris_plan_result_build: a flat op program produces the same
|
|
2139
|
+
* standalone result tree a walk would — wrappers, ns bindings, kid
|
|
2140
|
+
* order — so leptris_plan_serialize serves standalone
|
|
2141
|
+
* serialization with no DOM mint on the host side. */
|
|
2142
|
+
TEST(Plan408, ResultBuildFeedsSerialize) {
|
|
2143
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
2144
|
+
leptris_child_plan kids[1] = {};
|
|
2145
|
+
kids[0].wire_name = "p";
|
|
2146
|
+
kids[0].kind = LEPTRIS_PLAN_KIND_SCALAR;
|
|
2147
|
+
kids[0].type_tag = 1;
|
|
2148
|
+
kids[0].child_plan_index = -1;
|
|
2149
|
+
leptris_element_plan plans[1] = {};
|
|
2150
|
+
plans[0].element_name = "b";
|
|
2151
|
+
plans[0].ns_form = LEPTRIS_PLAN_NS_ANY;
|
|
2152
|
+
plans[0].child_count = 1;
|
|
2153
|
+
plans[0].child_plans = kids;
|
|
2154
|
+
leptris_plan_spec spec = {};
|
|
2155
|
+
spec.abi_version = leptris_plan_abi_version();
|
|
2156
|
+
spec.plan_count = 1;
|
|
2157
|
+
spec.plans = plans;
|
|
2158
|
+
LeptrisPlan plan = leptris_plan_build(&spec, &st);
|
|
2159
|
+
ASSERT_NE(plan, nullptr);
|
|
2160
|
+
|
|
2161
|
+
/* a SCALAR op IS the row's value (name and binding come from
|
|
2162
|
+
* the plan row) — no ELEMENT wrapper needed for scalar rows */
|
|
2163
|
+
leptris_plan_row_op ops[] = {
|
|
2164
|
+
{LEPTRIS_PLAN_OP_ELEMENT, 0, UINT32_MAX, NULL, NULL, 0},
|
|
2165
|
+
{LEPTRIS_PLAN_OP_ATTR, 0, UINT32_MAX, "id", "7", 1},
|
|
2166
|
+
{LEPTRIS_PLAN_OP_SCALAR, 0, 0, NULL, "first", 5},
|
|
2167
|
+
{LEPTRIS_PLAN_OP_END, 0, UINT32_MAX, NULL, NULL, 0},
|
|
2168
|
+
};
|
|
2169
|
+
LeptrisPlanResult r =
|
|
2170
|
+
leptris_plan_result_build(plan, ops, 4, &st);
|
|
2171
|
+
ASSERT_NE(r, nullptr);
|
|
2172
|
+
char* out = leptris_plan_serialize(plan, r, &st);
|
|
2173
|
+
ASSERT_NE(out, nullptr);
|
|
2174
|
+
EXPECT_STREQ(out, "<b id=\"7\"><p>first</p></b>");
|
|
2175
|
+
leptris_free_string(out);
|
|
2176
|
+
leptris_plan_result_free(r);
|
|
2177
|
+
leptris_plan_free(plan);
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
TEST(Plan408, ResultBuildRejectsBadPrograms) {
|
|
2181
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
2182
|
+
leptris_element_plan plans[1] = {};
|
|
2183
|
+
plans[0].element_name = "b";
|
|
2184
|
+
plans[0].ns_form = LEPTRIS_PLAN_NS_ANY;
|
|
2185
|
+
leptris_plan_spec spec = {};
|
|
2186
|
+
spec.abi_version = leptris_plan_abi_version();
|
|
2187
|
+
spec.plan_count = 1;
|
|
2188
|
+
spec.plans = plans;
|
|
2189
|
+
LeptrisPlan plan = leptris_plan_build(&spec, &st);
|
|
2190
|
+
ASSERT_NE(plan, nullptr);
|
|
2191
|
+
|
|
2192
|
+
/* END without an open element */
|
|
2193
|
+
leptris_plan_row_op bad1[] = {
|
|
2194
|
+
{LEPTRIS_PLAN_OP_END, 0, UINT32_MAX, NULL, NULL, 0}};
|
|
2195
|
+
EXPECT_EQ(leptris_plan_result_build(plan, bad1, 1, &st), nullptr);
|
|
2196
|
+
/* unclosed element */
|
|
2197
|
+
leptris_plan_row_op bad2[] = {
|
|
2198
|
+
{LEPTRIS_PLAN_OP_ELEMENT, 0, UINT32_MAX, NULL, NULL, 0}};
|
|
2199
|
+
EXPECT_EQ(leptris_plan_result_build(plan, bad2, 1, &st), nullptr);
|
|
2200
|
+
/* NULL/empty args */
|
|
2201
|
+
EXPECT_EQ(leptris_plan_result_build(NULL, bad2, 1, &st), nullptr);
|
|
2202
|
+
EXPECT_EQ(leptris_plan_result_build(plan, NULL, 0, &st), nullptr);
|
|
2203
|
+
leptris_plan_free(plan);
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
/* leptris_element_add_child_addr: attach by raw addresses — the
|
|
2207
|
+
* binding's per-node FFI::Pointer mint (#408 ask 1) disappears. */
|
|
2208
|
+
TEST(Plan408, AddChildByAddress) {
|
|
2209
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
2210
|
+
LeptrisDocument doc = leptris_parse_string("<r/>", 4, &st);
|
|
2211
|
+
ASSERT_NE(doc, nullptr);
|
|
2212
|
+
LeptrisElement parent = leptris_document_root(doc);
|
|
2213
|
+
LeptrisElement child = leptris_element_create(doc, "c");
|
|
2214
|
+
ASSERT_NE(child, nullptr);
|
|
2215
|
+
EXPECT_EQ(leptris_element_add_child_addr(
|
|
2216
|
+
(uintptr_t)parent, (uintptr_t)child),
|
|
2217
|
+
LEPTRIS_OK);
|
|
2218
|
+
EXPECT_EQ(leptris_element_child_count(parent), 1u);
|
|
2219
|
+
LeptrisElement got = leptris_element_first_child_any(parent);
|
|
2220
|
+
ASSERT_NE(got, nullptr);
|
|
2221
|
+
EXPECT_STREQ(leptris_element_name(got), "c");
|
|
2222
|
+
/* NULL-shaped addresses are clean errors */
|
|
2223
|
+
EXPECT_NE(leptris_element_add_child_addr(0, (uintptr_t)child),
|
|
2224
|
+
LEPTRIS_OK);
|
|
2225
|
+
leptris_document_free(doc);
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
} // namespace
|
|
@@ -1396,3 +1396,106 @@ TEST(XsdDatatypes, UnionMixesNamedAndInlineMembers) {
|
|
|
1396
1396
|
EXPECT_EQ(validate_doc(x, "<v>42</v>"), 1);
|
|
1397
1397
|
EXPECT_EQ(validate_doc(x, "<v>auto</v>"), 1);
|
|
1398
1398
|
}
|
|
1399
|
+
|
|
1400
|
+
// ---- completion wave: xs:all order-free semantics ------------------
|
|
1401
|
+
namespace {
|
|
1402
|
+
|
|
1403
|
+
const char* k_all_schema =
|
|
1404
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
1405
|
+
"<xs:complexType name=\"aT\">"
|
|
1406
|
+
"<xs:all>"
|
|
1407
|
+
"<xs:element name=\"x\" type=\"xs:token\"/>"
|
|
1408
|
+
"<xs:element name=\"y\" type=\"xs:token\" minOccurs=\"0\"/>"
|
|
1409
|
+
"</xs:all>"
|
|
1410
|
+
"</xs:complexType>"
|
|
1411
|
+
"<xs:element name=\"r\" type=\"aT\"/>"
|
|
1412
|
+
"</xs:schema>";
|
|
1413
|
+
|
|
1414
|
+
TEST(XsdContent, AllGroupIsOrderFree) {
|
|
1415
|
+
/* declaration order */
|
|
1416
|
+
EXPECT_EQ(validate_doc(k_all_schema, "<r><x>1</x><y>2</y></r>"), 1);
|
|
1417
|
+
/* reversed order: xs:all is order-free */
|
|
1418
|
+
EXPECT_EQ(validate_doc(k_all_schema, "<r><y>2</y><x>1</x></r>"), 1);
|
|
1419
|
+
/* optional member absent */
|
|
1420
|
+
EXPECT_EQ(validate_doc(k_all_schema, "<r><x>1</x></r>"), 1);
|
|
1421
|
+
/* a member appears twice: at most once in xs:all */
|
|
1422
|
+
EXPECT_EQ(validate_doc(k_all_schema, "<r><x>1</x><x>1</x></r>"), 0);
|
|
1423
|
+
/* required member missing */
|
|
1424
|
+
EXPECT_EQ(validate_doc(k_all_schema, "<r><y>2</y></r>"), 0);
|
|
1425
|
+
/* undeclared child inside xs:all */
|
|
1426
|
+
EXPECT_EQ(validate_doc(k_all_schema, "<r><x>1</x><z/></r>"), 0);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
} // namespace
|
|
1430
|
+
|
|
1431
|
+
// ---- completion wave: loud unresolved types ------------------------
|
|
1432
|
+
namespace {
|
|
1433
|
+
|
|
1434
|
+
TEST(XsdValidate, UnresolvedTypeIsALoudFailure) {
|
|
1435
|
+
/* the schema references a type that does not exist — the
|
|
1436
|
+
* instance must not silently validate (the #1615 symptom
|
|
1437
|
+
* class: stale engines accepted unresolved types) */
|
|
1438
|
+
const char* x =
|
|
1439
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
1440
|
+
"<xs:element name=\"v\" type=\"noSuchType\"/>"
|
|
1441
|
+
"</xs:schema>";
|
|
1442
|
+
EXPECT_EQ(validate_doc(x, "<v>anything</v>"), 0);
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
} // namespace
|
|
1446
|
+
|
|
1447
|
+
// ---- completion wave: strict undeclared elements/attributes -------
|
|
1448
|
+
namespace {
|
|
1449
|
+
|
|
1450
|
+
TEST(XsdValidate, UndeclaredElementIsStrict) {
|
|
1451
|
+
const char* x =
|
|
1452
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
1453
|
+
"<xs:element name=\"r\">"
|
|
1454
|
+
"<xs:complexType><xs:sequence>"
|
|
1455
|
+
"<xs:element name=\"a\" type=\"xs:token\"/>"
|
|
1456
|
+
"</xs:sequence></xs:complexType>"
|
|
1457
|
+
"</xs:element>"
|
|
1458
|
+
"</xs:schema>";
|
|
1459
|
+
EXPECT_EQ(validate_doc(x, "<r><a>1</a></r>"), 1);
|
|
1460
|
+
/* a child the schema never declared */
|
|
1461
|
+
EXPECT_EQ(validate_doc(x, "<r><a>1</a><ghost/></r>"), 0);
|
|
1462
|
+
/* an element outside the sequence */
|
|
1463
|
+
EXPECT_EQ(validate_doc(x, "<r><ghost/><a>1</a></r>"), 0);
|
|
1464
|
+
/* an undeclared ROOT element */
|
|
1465
|
+
EXPECT_EQ(validate_doc(x, "<ghost/>"), 0);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
TEST(XsdValidate, UndeclaredAttributeIsStrictUnlessAnyAttribute) {
|
|
1469
|
+
const char* strict =
|
|
1470
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
1471
|
+
"<xs:element name=\"r\">"
|
|
1472
|
+
"<xs:complexType><xs:sequence>"
|
|
1473
|
+
"<xs:element name=\"a\" type=\"xs:token\"/>"
|
|
1474
|
+
"</xs:sequence>"
|
|
1475
|
+
"<xs:attribute name=\"id\" type=\"xs:integer\"/>"
|
|
1476
|
+
"</xs:complexType>"
|
|
1477
|
+
"</xs:element>"
|
|
1478
|
+
"</xs:schema>";
|
|
1479
|
+
EXPECT_EQ(validate_doc(strict, "<r id=\"1\"><a>x</a></r>"), 1);
|
|
1480
|
+
/* an attribute the schema never declared */
|
|
1481
|
+
EXPECT_EQ(validate_doc(strict, "<r id=\"1\" note=\"hi\"><a>x</a></r>"),
|
|
1482
|
+
0);
|
|
1483
|
+
|
|
1484
|
+
/* xs:anyAttribute opens the wildcard: undeclared attrs pass */
|
|
1485
|
+
const char* open_attrs =
|
|
1486
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
1487
|
+
"<xs:element name=\"r\">"
|
|
1488
|
+
"<xs:complexType><xs:sequence>"
|
|
1489
|
+
"<xs:element name=\"a\" type=\"xs:token\"/>"
|
|
1490
|
+
"</xs:sequence>"
|
|
1491
|
+
"<xs:attribute name=\"id\" type=\"xs:integer\"/>"
|
|
1492
|
+
"<xs:anyAttribute namespace=\"##any\" processContents=\"lax\"/>"
|
|
1493
|
+
"</xs:complexType>"
|
|
1494
|
+
"</xs:element>"
|
|
1495
|
+
"</xs:schema>";
|
|
1496
|
+
EXPECT_EQ(validate_doc(open_attrs,
|
|
1497
|
+
"<r id=\"1\" note=\"hi\" tag=\"x\"><a>x</a></r>"),
|
|
1498
|
+
1);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
} // namespace
|
|
@@ -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.
|
|
4
|
+
"version": "1.9.334",
|
|
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",
|