leptris 1.9.364.1-aarch64-linux → 1.9.365.1-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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be9630b0b1d264a6ee32c6ab3babcfb54657ed868a483b70dc88c576831c5e64
4
- data.tar.gz: 454d31c45ee453cbaf61a2e24473f37256219781e00471a3cd4d18409d6b41a5
3
+ metadata.gz: '09191b1d6e47cb07f20ee17bca8e1dc83969f96f19b92ea218baa1e2957c619d'
4
+ data.tar.gz: 293dddc9db111199bb43df0d66243bc059a83ac5c8f0f21c9a8eca819352fcb1
5
5
  SHA512:
6
- metadata.gz: eb24c7797b4710ece57f2274ecd04a51fd6e14829a43f04240a4d3b30d9744ce2bb61a353546c2ee4cd314a66562093b4cd11a9e5bcb1234ac9931a3e0726559
7
- data.tar.gz: a2b8d0174b7101a2f35104f09d2a2928a3ee59a10449905033cfcc7ebf5453c67c43d598b9152ab4de1da20face8c1e4356a1021a303242d514ceeeb47dd8e54
6
+ metadata.gz: 69c108f41af0596b6988d121afeaa92c867d9c6c5e8a3905f2b18bacb6a0866e83bd8b9e48950b822d0a9da64d624927653f6ad1616393c387e9a59f3df74c10
7
+ data.tar.gz: bdfb75ee4ec215661ebda9e71afeee737bb6fe2216fc9495469f2baf70d41bf4eec77eacb5f32ffa8c84ecfb76ca34ba15445b0b862d4fce1b806a206923edb6
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.363"
11
+ LIBLEPTRIS_VERSION = "1.9.365"
12
12
  # Vendored alongside libleptris for fn:normalize-unicode (TODO
13
13
  # .restructure/20): built per platform with a RELOCATABLE @rpath
14
14
  # install name, loaded by ffi.rb before libleptris so the
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.364.1"
4
+ VERSION = "1.9.365.1"
5
5
  end
@@ -864,6 +864,16 @@ class Leptris::XML::Document
864
864
  node
865
865
  end
866
866
 
867
+ # #1749: engine diag kind -> SyntaxError severity. FATAL = the
868
+ # classes the strict lane raises on (element still open at EOF;
869
+ # unescaped '<' in text, engine >= 1.9.364); the recover-class
870
+ # records (stray close dropped/implicit close, duplicate attribute,
871
+ # ...) and any future kind read :recoverable.
872
+ DIAG_SEVERITY = {
873
+ Leptris::XML::FFI::DIAG_KIND_UNCLOSED_ELEMENT => :fatal,
874
+ Leptris::XML::FFI::DIAG_KIND_UNESCAPED_LT => :fatal
875
+ }.freeze
876
+
867
877
  # Recovered-parse diagnostics (leptris-ruby#465): nokogiri parity
868
878
  # for the recover lane — the engine's parse diagnostics as
869
879
  # SyntaxError entries, empty for a clean parse. The engine
@@ -878,7 +888,9 @@ class Leptris::XML::Document
878
888
  ok = Leptris::XML::FFI.leptris_document_parse_diag(
879
889
  c_ptr, i, kind_buf, msg_buf, 512)
880
890
  next nil if ok.zero?
881
- Leptris::XML::SyntaxError.new(msg_buf.read_string.force_encoding(Encoding::UTF_8))
891
+ Leptris::XML::SyntaxError.new(
892
+ msg_buf.read_string.force_encoding(Encoding::UTF_8),
893
+ severity: DIAG_SEVERITY.fetch(kind_buf.read_int, :recoverable))
882
894
  end.compact
883
895
  end
884
896
 
@@ -1520,6 +1520,13 @@ attach_function :leptris_parse_string,
1520
1520
  [:leptris_document], :leptris_status
1521
1521
  attach_function :leptris_document_remove_doctype,
1522
1522
  [:leptris_document], :leptris_status
1523
+ # LeptrisDiagKind (engine types.h, append-only): the values the
1524
+ # DOM parse channel records. Document#errors maps these to
1525
+ # SyntaxError severity (#1749) — FATAL kinds are the classes the
1526
+ # strict lane raises on; everything else reads :recoverable.
1527
+ DIAG_KIND_UNCLOSED_ELEMENT = 11
1528
+ DIAG_KIND_UNESCAPED_LT = 12 # engine >= 1.9.364 (#1748/#1749)
1529
+
1523
1530
  attach_function :leptris_document_parse_diag_count,
1524
1531
  [:leptris_document], :size_t
1525
1532
  attach_function :leptris_document_parse_diag,
data/lib/leptris/xml.rb CHANGED
@@ -111,14 +111,22 @@ module Leptris
111
111
  # SyntaxError shape — a message plus optional position — served
112
112
  # by Document#errors.
113
113
  class SyntaxError < Error
114
- attr_reader :message, :line, :column
114
+ attr_reader :message, :line, :column, :severity
115
115
 
116
- def initialize(message, line: nil, column: nil)
116
+ # severity: :fatal (the strict lane raises on this class) or
117
+ # :recoverable (leptris-ruby#1749) — raise-on-fatal /
118
+ # recover-on-recoverable without string matching.
119
+ def initialize(message, line: nil, column: nil, severity: :recoverable)
117
120
  @message = message
118
121
  @line = line
119
122
  @column = column
123
+ @severity = severity
120
124
  super(message)
121
125
  end
126
+
127
+ def fatal?
128
+ @severity == :fatal
129
+ end
122
130
  end
123
131
  class ReadOnlyError < Error; end
124
132
  class XPathError < Error; end
data/lib/libleptris.so CHANGED
Binary file
data/vendor-src/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Sources vendored in this gem (recompile rights; the ruby
2
2
  variant builds them at install via extconf.rb):
3
3
 
4
- - libleptris v1.9.363
4
+ - libleptris v1.9.365
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,39 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.365] - 2026-10-16
4
+
5
+ ### Fixed
6
+
7
+ - **html4: skip the XML-declaration construct in HTML input (#1748, PR #1754).**
8
+ The html4 lane parsed `<?xml ...?>` as a PI node (target `xml`) whose data
9
+ carried the terminator `?`, so serialization emitted
10
+ `<?xml version="1.0" encoding="UTF-8"??>` — and with the serializer's
11
+ `xml_declaration` riding along (the default `to_xml`), the output carried
12
+ two declarations, which canon's XHTML pretty-printer degenerated to
13
+ declaration-only output downstream. libxml2's HTML parser skips the
14
+ XML-declaration construct entirely; the html4 lane now does the same (the
15
+ WHATWG lane keeps its specified bogus-comment shape). Red-first gtest pins
16
+ the contract: `xml_declaration=0` emits no declaration, `1` emits exactly
17
+ the serializer's own — never the input's remnant, never `??`. Engine suite
18
+ 2047/2047; canon's battery 9/9.
19
+
20
+
21
+ ## [1.9.364] - 2026-10-16
22
+
23
+ ### Added
24
+
25
+ - **parse diagnostics carry severity classification (#1749, PR #1752).**
26
+ Two append-only `LeptrisDiagKind` refinements so embedders can implement
27
+ raise-on-fatal / recover-on-recoverable without string matching:
28
+ `LEPTRIS_DIAG_UNESCAPED_LT` — the #1748 unescaped-`<` recovery is a
29
+ strict-fatal class and now records its own kind instead of the generic
30
+ RECOVER bucket; and the ancestor-implicit-close recovery records
31
+ `LEPTRIS_DIAG_MISMATCHED_TAG` (libxml2 reports the same shape as a tag
32
+ mismatch), reserving `LEPTRIS_DIAG_UNCLOSED_ELEMENT` for the
33
+ still-open-at-EOF fatal class. Record messages unchanged; enum is
34
+ append-only.
35
+
36
+
3
37
  ## [1.9.363] - 2026-10-16
4
38
 
5
39
  ### Fixed
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.363
6
+ VERSION 1.9.365
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -290,6 +290,10 @@ typedef enum {
290
290
  * implicitly), and an element still open at EOF. Append-only. */
291
291
  LEPTRIS_DIAG_MISMATCHED_TAG,
292
292
  LEPTRIS_DIAG_UNCLOSED_ELEMENT,
293
+ /* leptris#1748: an unescaped '<' in text folded into the run —
294
+ * RECOVERABLE-lane record of a STRICT-FATAL class (the strict
295
+ * path fails the parse; severity mapping: fatal). Append-only. */
296
+ LEPTRIS_DIAG_UNESCAPED_LT,
293
297
  LEPTRIS_DIAG_COUNT_
294
298
  } LeptrisDiagKind;
295
299
 
@@ -1755,7 +1755,7 @@ static struct leptris_document* direct_parse_internal(char* buf, size_t len,
1755
1755
  leptris_diag_emit(
1756
1756
  &p.doc->parse_diags, &p.doc->parse_diag_count,
1757
1757
  &p.doc->parse_diag_cap,
1758
- LEPTRIS_DIAG_RECOVER, NULL,
1758
+ LEPTRIS_DIAG_UNESCAPED_LT, NULL,
1759
1759
  "Unescaped < not allowed in a text node");
1760
1760
  const char* nq = lt + 1;
1761
1761
  const char* nlt =
@@ -2142,11 +2142,17 @@ static struct leptris_document* direct_parse_internal(char* buf, size_t len,
2142
2142
  while ((int)p.depth - 1 > anc) {
2143
2143
  LeptrisElement ce =
2144
2144
  p.open_stack[p.depth - 1];
2145
+ /* #1749 severity: the implicit close
2146
+ * is the MISMATCH recovery (libxml2
2147
+ * reports a tag mismatch; recoverable
2148
+ * class) — UNCLOSED_ELEMENT stays
2149
+ * reserved for the still-open-at-EOF
2150
+ * fatal class. */
2145
2151
  leptris_diag_emit(
2146
2152
  &p.doc->parse_diags,
2147
2153
  &p.doc->parse_diag_count,
2148
2154
  &p.doc->parse_diag_cap,
2149
- LEPTRIS_DIAG_UNCLOSED_ELEMENT, NULL,
2155
+ LEPTRIS_DIAG_MISMATCHED_TAG, NULL,
2150
2156
  "Element %s closed implicitly by </%.*s>",
2151
2157
  elem_name(ce),
2152
2158
  (int)close_local_len, close_local);
@@ -6685,6 +6685,19 @@ static LeptrisDocument html_parse_shared(
6685
6685
  size_t dlen = (size_t)(de > ds ? de - ds : 0);
6686
6686
  char* tgt = (char*)leptris_pool_alloc(b.pool, tlen + 1);
6687
6687
  char* dat = (char*)leptris_pool_alloc(b.pool, dlen + 1);
6688
+ /* libxml2 HTML parity (#1748): the XML-declaration
6689
+ * construct ('<?xml ...?>') is an XML-ism — the HTML
6690
+ * lane skips it entirely. Retaining it as a PI made the
6691
+ * data carry the terminator '?' (serialized
6692
+ * '<?xml ...??>'), and the declaration metadata + PI
6693
+ * doubled when the serializer's xml_declaration rode
6694
+ * along. */
6695
+ if (tlen == 3 && (ts[0] | 0x20) == 'x' &&
6696
+ (ts[1] | 0x20) == 'm' && (ts[2] | 0x20) == 'l') {
6697
+ p = (de < end) ? de + 1 : end;
6698
+ text = p;
6699
+ continue;
6700
+ }
6688
6701
  if (tgt && dat) {
6689
6702
  memcpy(tgt, ts, tlen);
6690
6703
  tgt[tlen] = 0;
@@ -188,3 +188,43 @@ TEST(HtmlBuilder, RawTextElementNotEscaped) {
188
188
  EXPECT_EQ(out.find("&lt;"), std::string::npos) << out;
189
189
  leptris_document_free(d);
190
190
  }
191
+
192
+ /* leptris#1748: the XML-declaration construct ('<?xml ...?>') in
193
+ * HTML input is an XML-ism — libxml2's HTML parser skips it, and
194
+ * retaining it as a PI (data carrying the terminator '?') made
195
+ * serialization emit '<?xml ...??>' and double the declaration when
196
+ * the serializer's own xml_declaration rode along. */
197
+ TEST(HtmlBuilder, Html4DropsXmlDeclarationProlog) {
198
+ const char* html =
199
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
200
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
201
+ "<head><title>t</title></head><body><p>x</p></body></html>";
202
+ LeptrisStatus st = LEPTRIS_OK;
203
+ LeptrisDocument d =
204
+ leptris_parse_html4_string(html, strlen(html), &st);
205
+ ASSERT_EQ(st, LEPTRIS_OK);
206
+ ASSERT_NE(d, nullptr);
207
+ for (int decl = 0; decl < 2; decl++) {
208
+ LeptrisSerializeOptions opts;
209
+ memset(&opts, 0, sizeof(opts));
210
+ opts.xml_declaration = decl;
211
+ opts.encoding = "UTF-8";
212
+ char* s = leptris_document_serialize(d, &opts);
213
+ ASSERT_NE(s, nullptr);
214
+ std::string out(s);
215
+ leptris_free_string(s);
216
+ EXPECT_EQ(out.find("??"), std::string::npos)
217
+ << "decl=" << decl << " got: " << out.substr(0, 90);
218
+ /* decl=0: the prolog never leaks. decl=1: exactly the
219
+ * serializer's own declaration — never two, never the
220
+ * input's remnant. */
221
+ size_t decls = 0;
222
+ for (size_t at = out.find("<?xml"); at != std::string::npos;
223
+ at = out.find("<?xml", at + 1))
224
+ decls++;
225
+ EXPECT_EQ(decls, (size_t)decl)
226
+ << "decl=" << decl << " got: " << out.substr(0, 90);
227
+ EXPECT_NE(out.find("<html"), std::string::npos) << out;
228
+ }
229
+ leptris_document_free(d);
230
+ }
@@ -799,8 +799,17 @@ TEST(ParseRecover, MismatchedCloseKeepsTreeAndDropsStray) {
799
799
  EXPECT_STREQ(leptris_element_text(a), "x");
800
800
  /* Two recoveries: the stray </b> drops, and <a> closes
801
801
  * implicitly when </root> names the ancestor (libxml2 reports
802
- * both as well). */
802
+ * both as well). Both are MISMATCHED_TAG — the #1749 severity
803
+ * contract keeps UNCLOSED_ELEMENT for the EOF class only. */
803
804
  EXPECT_EQ(leptris_document_parse_diag_count(doc), 2u);
805
+ LeptrisDiagKind kind = (LeptrisDiagKind)-1;
806
+ char msg[128] = {0};
807
+ ASSERT_EQ(leptris_document_parse_diag(doc, 0, &kind, msg,
808
+ sizeof(msg)), 1);
809
+ EXPECT_EQ(kind, LEPTRIS_DIAG_MISMATCHED_TAG);
810
+ ASSERT_EQ(leptris_document_parse_diag(doc, 1, &kind, msg,
811
+ sizeof(msg)), 1);
812
+ EXPECT_EQ(kind, LEPTRIS_DIAG_MISMATCHED_TAG);
804
813
  leptris_document_free(doc);
805
814
  }
806
815
 
@@ -867,7 +876,10 @@ TEST(ParseRecover, BareLtInTextKeepsTreeAndRecords) {
867
876
  char msg[128] = {0};
868
877
  ASSERT_EQ(leptris_document_parse_diag(doc, 0, &kind, msg,
869
878
  sizeof(msg)), 1);
870
- EXPECT_EQ(kind, LEPTRIS_DIAG_RECOVER);
879
+ /* #1749: the kind carries the severity — unescaped '<' is a
880
+ * STRICT-FATAL class recovered on the lenient lane, its own
881
+ * kind, not the generic RECOVER bucket. */
882
+ EXPECT_EQ(kind, LEPTRIS_DIAG_UNESCAPED_LT);
871
883
  leptris_document_free(doc);
872
884
  }
873
885
 
@@ -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.363",
4
+ "version": "1.9.365",
5
5
  "description": "High-performance XML parser and XPath 1.0 engine in C (libleptris)",
6
6
  "homepage": "https://github.com/leptris/leptris",
7
7
  "license": "MIT",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.364.1
4
+ version: 1.9.365.1
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.