aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format30
-rw-r--r--.git-blame-ignore-revs5
-rw-r--r--.gitignore16
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--.reuse/dep511
-rw-r--r--.suppress.cppcheck8
-rw-r--r--COPYING2
-rw-r--r--LICENSES/0BSD.txt2
-rw-r--r--[l---------]LICENSES/ISC.txt14
-rw-r--r--NEWS21
-rw-r--r--doc/conf.py.in4
-rw-r--r--doc/man/meson.build1
-rw-r--r--doc/man/serdi.18
-rw-r--r--include/serd/serd.h17
-rw-r--r--meson.build97
-rw-r--r--meson/suppressions/meson.build106
-rw-r--r--meson_options.txt12
-rwxr-xr-xscripts/check_formatting.py2
-rwxr-xr-xscripts/serd_bench.py9
-rw-r--r--src/.clang-tidy6
-rw-r--r--src/base64.c8
-rw-r--r--src/base64.h2
-rw-r--r--src/byte_sink.h10
-rw-r--r--src/byte_source.c17
-rw-r--r--src/byte_source.h13
-rw-r--r--src/env.c2
-rw-r--r--src/n3.c239
-rw-r--r--src/node.c19
-rw-r--r--src/node.h36
-rw-r--r--src/reader.c37
-rw-r--r--src/reader.h39
-rw-r--r--src/serd_config.h2
-rw-r--r--src/serd_internal.h6
-rw-r--r--src/serdi.c30
-rw-r--r--src/stack.h21
-rw-r--r--src/string.c2
-rw-r--r--src/string_utils.h16
-rw-r--r--src/system.c1
-rw-r--r--src/try.h2
-rw-r--r--src/uri.c18
-rw-r--r--src/uri_utils.h16
-rw-r--r--src/writer.c267
-rw-r--r--subprojects/sphinxygen.wrap12
-rw-r--r--test/.clang-tidy3
-rw-r--r--test/extra/bad/bad-lang-start-delete.nt1
-rw-r--r--test/extra/bad/bad-lang-start-space.nt1
-rw-r--r--test/extra/bad/bad-lang-start-tab.nt1
-rw-r--r--test/extra/bad/bad-lang-start-wide.nt1
-rw-r--r--test/extra/bad/bad-lang.ttl2
-rw-r--r--test/extra/bad/bad-predicate-in-blank.ttl1
-rw-r--r--test/extra/bad/bad-uri-scheme-start-apostrophe.nt1
-rw-r--r--test/extra/bad/bad-uri-scheme-start-delete.nt1
-rw-r--r--test/extra/bad/bad-uri-scheme-start-space.nt1
-rw-r--r--test/extra/bad/bad-uri-scheme-start-tab.nt1
-rw-r--r--test/extra/bad/bad-uri-scheme-start-wide.nt1
-rw-r--r--test/extra/bad/bad-uri-scheme-start.nt1
-rw-r--r--test/extra/bad/manifest.ttl277
-rw-r--r--test/extra/eof/README.md5
-rw-r--r--test/extra/eof/bad-nt-eof-after-blank.nt (renamed from test/extra/bad/bad-nt-eof-after-blank.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-after-lang-hyphen.nt (renamed from test/extra/bad/bad-nt-eof-after-lang-hyphen.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-after-lang-subtag.nt (renamed from test/extra/bad/bad-nt-eof-after-lang-subtag.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-after-lang.nt (renamed from test/extra/bad/bad-nt-eof-after-lang.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-after-object.nt (renamed from test/extra/bad/bad-nt-eof-after-object.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-after-predicate.nt (renamed from test/extra/bad/bad-nt-eof-after-predicate.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-after-string-escape.nt (renamed from test/extra/bad/bad-nt-eof-after-string-escape.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-after-string.nt (renamed from test/extra/bad/bad-nt-eof-after-string.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-after-subject.nt (renamed from test/extra/bad/bad-nt-eof-after-subject.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-after-underscore.nt (renamed from test/extra/bad/bad-nt-eof-after-underscore.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-before-blank.nt (renamed from test/extra/bad/bad-nt-eof-before-blank.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-before-iri.nt (renamed from test/extra/bad/bad-nt-eof-before-iri.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-before-lang.nt (renamed from test/extra/bad/bad-nt-eof-before-lang.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-before-string-escape.nt (renamed from test/extra/bad/bad-nt-eof-before-string-escape.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-before-string.nt (renamed from test/extra/bad/bad-nt-eof-before-string.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-in-iri-path.nt (renamed from test/extra/bad/bad-nt-eof-in-iri-path.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-in-iri-scheme.nt (renamed from test/extra/bad/bad-nt-eof-in-iri-scheme.nt)0
-rw-r--r--test/extra/eof/bad-nt-eof-in-string.nt (renamed from test/extra/bad/bad-nt-eof-in-string.nt)0
-rw-r--r--test/extra/eof/bad-ttl-eof-after-quotes.ttl (renamed from test/extra/bad/bad-eof-after-quotes.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-at-string-start.ttl (renamed from test/extra/bad/bad-eof-at-string-start.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-blank.ttl (renamed from test/extra/bad/bad-eof-in-blank.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-escape.ttl (renamed from test/extra/bad/bad-eof-in-escape.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-lang-suffix.ttl (renamed from test/extra/bad/bad-eof-in-lang-suffix.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-lang.ttl (renamed from test/extra/bad/bad-eof-in-lang.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-list.ttl (renamed from test/extra/bad/bad-eof-in-list.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-long-string.ttl (renamed from test/extra/bad/bad-eof-in-long-string.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-object-list.ttl (renamed from test/extra/bad/bad-eof-in-object-list.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-object-list2.ttl (renamed from test/extra/bad/bad-eof-in-object-list2.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-predicate-list.ttl (renamed from test/extra/bad/bad-eof-in-predicate-list.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-string.ttl (renamed from test/extra/bad/bad-eof-in-string.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-text-character.ttl (renamed from test/extra/bad/bad-eof-in-text-character.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-triple-quote.ttl (renamed from test/extra/bad/bad-eof-in-triple-quote.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-uri-character.ttl (renamed from test/extra/bad/bad-eof-in-uri-character.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-uri-scheme.ttl (renamed from test/extra/bad/bad-eof-in-uri-scheme.nt)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-uri.ttl (renamed from test/extra/bad/bad-eof-in-uri.ttl)0
-rw-r--r--test/extra/eof/bad-ttl-eof-in-utf8-character.ttl (renamed from test/extra/bad/bad-eof-in-utf8-character.ttl)0
-rw-r--r--test/extra/eof/manifest.ttl225
-rw-r--r--test/extra/good/manifest.ttl48
-rw-r--r--test/extra/good/test-bom-only.nt0
-rw-r--r--test/extra/good/test-bom-only.ttl1
-rw-r--r--test/extra/good/test-boolish-prefix.nt2
-rw-r--r--test/extra/good/test-boolish-prefix.ttl5
-rw-r--r--test/extra/good/test-decimal-ends-with-dot.nt1
-rw-r--r--test/extra/good/test-decimal-ends-with-dot.ttl4
-rw-r--r--test/extra/good/test-double-ends-with-dot.nt1
-rw-r--r--test/extra/good/test-double-ends-with-dot.ttl4
-rw-r--r--test/extra/good/test-false-ends-with-dot.nt1
-rw-r--r--test/extra/good/test-false-ends-with-dot.ttl4
-rw-r--r--test/extra/good/test-integer-ends-with-dot.nt1
-rw-r--r--test/extra/good/test-integer-ends-with-dot.ttl4
-rw-r--r--test/extra/good/test-nq-syntax-all-rules.nq2
-rw-r--r--test/extra/good/test-nt-syntax-all-rules.nt2
-rw-r--r--test/extra/good/test-trig-syntax-all-rules.trig4
-rw-r--r--test/extra/good/test-true-ends-with-dot.nt1
-rw-r--r--test/extra/good/test-true-ends-with-dot.ttl4
-rw-r--r--test/extra/good/test-ttl-syntax-all-rules.ttl6
-rw-r--r--test/extra/pretty/manifest.ttl7
-rw-r--r--test/extra/pretty/repeated-directives.ttl17
-rw-r--r--test/headers/meson.build1
-rw-r--r--test/headers/test_headers.c5
-rw-r--r--test/lint/meson.build133
-rw-r--r--test/meson.build137
-rwxr-xr-xtest/run_suite.py19
-rw-r--r--test/serd_test_util/__init__.py41
-rw-r--r--test/test_env.c6
-rw-r--r--test/test_free_null.c2
-rw-r--r--test/test_node.c13
-rwxr-xr-xtest/test_quiet.py20
-rw-r--r--test/test_reader.c126
-rw-r--r--test/test_reader_writer.c171
-rwxr-xr-xtest/test_stdin.py37
-rw-r--r--test/test_string.c5
-rw-r--r--test/test_uri.c162
-rwxr-xr-xtest/test_write_error.py19
-rw-r--r--test/test_writer.c181
133 files changed, 1630 insertions, 1280 deletions
diff --git a/.clang-format b/.clang-format
index 26b10123..1ec52448 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,17 +1,29 @@
-# Copyright 2020-2023 David Robillard <d@drobilla.net>
+# Copyright 2020-2024 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC
---
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
-AlignEscapedNewlinesLeft: true
+AlignEscapedNewlines: Left
+AttributeMacros:
+ - SERD_ALLOCATED
+ - SERD_API
+ - SERD_CONST_API
+ - SERD_CONST_FUNC
+ - SERD_FALLTHROUGH
+ - SERD_MALLOC_FUNC
+ - SERD_NODISCARD
+ - SERD_NONNULL
+ - SERD_NULLABLE
+ - SERD_PURE_API
+ - SERD_PURE_FUNC
BasedOnStyle: Mozilla
BraceWrapping:
- AfterNamespace: false
AfterClass: true
AfterEnum: false
AfterExternBlock: false
AfterFunction: true
+ AfterNamespace: false
AfterStruct: false
SplitEmptyFunction: false
SplitEmptyRecord: false
@@ -21,20 +33,10 @@ IndentCaseLabels: false
IndentPPDirectives: AfterHash
KeepEmptyLinesAtTheStartOfBlocks: false
SpacesInContainerLiterals: false
-AttributeMacros:
- - SERD_ALLOCATED
- - SERD_API
- - SERD_CONST_FINC
- - SERD_FALLTHROUGH
- - SERD_MALLOC_FUNC
- - SERD_NODISCARD
- - SERD_NONNULL
- - SERD_NULLABLE
- - SERD_PURE_FUNC
StatementMacros:
- SERD_DEPRECATED_BY
- - SERD_LOG_FUNC
- SERD_DISABLE_NULL_WARNINGS
+ - SERD_LOG_FUNC
- SERD_RESTORE_WARNINGS
- _Pragma
...
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 00000000..a922b478
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,5 @@
+# Copyright 2025 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+# Format all code with clang-format
+d101d926946a5e8067a90d157b6553aae7bddc19
diff --git a/.gitignore b/.gitignore
index eeeeac8c..e73e7aaf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,11 @@
-# Copyright 2017-2023 David Robillard <d@drobilla.net>
+# Copyright 2017-2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC
-.meson-subproject-wrap-hash.txt
-__pycache__
-build/
-subprojects/packagecache/
-subprojects/sphinxygen-1.0.4/
-subprojects/sphinxygen/
+/.meson-subproject-wrap-hash.txt
+/build/
+/subprojects/packagecache/
+/subprojects/sphinxygen-1.0.10/
+/subprojects/sphinxygen/
+
+*.pyc
+__pycache__/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ec20e2f7..41904c85 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -41,7 +41,7 @@ sanitize:
stage: build
image: lv2plugin/debian-x64-clang
script:
- - meson setup build -Db_lundef=false -Dbuildtype=plain -Dc_std=c11 -Ddocs=disabled -Dwarning_level=3 -Dwerror=true
+ - meson setup build -Db_lundef=false -Dbuildtype=plain -Dc_std=c11 -Ddocs=disabled -Dlint=true -Dwarning_level=3 -Dwerror=true
- ninja -C build test
variables:
CC: "clang"
@@ -158,4 +158,4 @@ pages:
paths:
- public
only:
- - master
+ - main
diff --git a/.reuse/dep5 b/.reuse/dep5
index 3173226a..f21e0ee1 100644
--- a/.reuse/dep5
+++ b/.reuse/dep5
@@ -3,23 +3,28 @@ Upstream-Name: serd
Upstream-Contact: David Robillard <d@drobilla.net>
Source: https://gitlab.com/drobilla/serd
+Files: .suppress.cppcheck
+Copyright: 2024 David Robillard <d@drobilla.net>
+Comment: Contributed to the Commons as a tool configuration
+License: 0BSD OR ISC
+
Files: test/w3c/*
Copyright: 2010 World Wide Web Consortium, (MIT, ERCIM, Keio, Beihang) and others.
Comment: Standard test suites from the W3C
License: BSD-3-Clause
Files: test/extra/*
-Copyright: 2011-2023 David Robillard <d@drobilla.net>
+Copyright: 2011-2025 David Robillard <d@drobilla.net>
Comment: Extra test suites for serd
License: BSD-3-Clause OR ISC
Files: AUTHORS NEWS serd.ttl
-Copyright: 2011-2023 David Robillard <d@drobilla.net>
+Copyright: 2011-2025 David Robillard <d@drobilla.net>
Comment: Contributed to the Commons as a representation of simple facts
License: 0BSD OR ISC
Files: doc/.stylelintrc.json
-Copyright: 2022 David Robillard <d@drobilla.net>
+Copyright: 2022-2024 David Robillard <d@drobilla.net>
Comment: Contributed to the Commons as a tool configuration
License: 0BSD OR ISC
diff --git a/.suppress.cppcheck b/.suppress.cppcheck
new file mode 100644
index 00000000..c6e4fdc0
--- /dev/null
+++ b/.suppress.cppcheck
@@ -0,0 +1,8 @@
+assertWithSideEffect
+assignmentInAssert
+normalCheckLevelMaxBranches
+nullPointerArithmeticOutOfMemory
+nullPointerOutOfMemory
+redundantInitialization
+unreadVariable
+unusedStructMember
diff --git a/COPYING b/COPYING
index 78df83ba..d16d277c 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-Copyright 2011-2023 David Robillard <d@drobilla.net>
+Copyright 2011-2025 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/LICENSES/0BSD.txt b/LICENSES/0BSD.txt
index c338c71a..f33ca7b4 100644
--- a/LICENSES/0BSD.txt
+++ b/LICENSES/0BSD.txt
@@ -1,4 +1,4 @@
-Copyright 2011-2023 David Robillard <d@drobilla.net>
+Copyright 2011-2025 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
diff --git a/LICENSES/ISC.txt b/LICENSES/ISC.txt
index 012065c8..d16d277c 120000..100644
--- a/LICENSES/ISC.txt
+++ b/LICENSES/ISC.txt
@@ -1 +1,13 @@
-../COPYING \ No newline at end of file
+Copyright 2011-2025 David Robillard <d@drobilla.net>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/NEWS b/NEWS
index 3e9c71d3..8a14d2ed 100644
--- a/NEWS
+++ b/NEWS
@@ -1,9 +1,24 @@
-serd (0.32.3) unstable; urgency=medium
+serd (0.32.5) unstable; urgency=medium
+
+ * Drop graphs when writing Turtle output
+ * Fix handling of bad predicates in anonymous blank nodes
+ * Fix handling of some invalid EOF cases in lax mode
+ * Fix invalid characters in error messages
+ * Fix reading numbers with no space before the final dot
+ * Fix reading prefix names that start with "true." or "false."
+ * Refuse to write incoherent statements
+ * Remove project and version number from man page OS field
+ * Write a blank line between statements and Turtle/TriG directives
+
+ -- David Robillard <d@drobilla.net> Thu, 26 Jun 2025 03:48:18 +0000
+
+serd (0.32.4) stable; urgency=medium
* Clean up enum declarations
* Fix library current_version on MacOS
* Fix overly permissive parsing of syntax names on the command line
* Fix parsing NQuads lines with no space before the final dot
+ * Fix reading chunks from files without trailing newlines
* Fix rewriting special literals when datatype URIs are prefixed names
* Gracefully handle errors while writing the end of anonymous nodes
* Improve test suite coverage
@@ -12,7 +27,7 @@ serd (0.32.3) unstable; urgency=medium
* Treat out of range unicode characters as errors
* Write blank lines between graphs and statements in TriG
- -- David Robillard <d@drobilla.net> Tue, 25 Jun 2024 22:38:16 +0000
+ -- David Robillard <d@drobilla.net> Sun, 19 Jan 2025 00:17:58 +0000
serd (0.32.2) stable; urgency=medium
@@ -164,7 +179,7 @@ serd (0.28.0) stable; urgency=medium
* Add support for reading from a user provided callback
* Fix hangs when reading corrupt UTF-8
* Fix parsing of hex escapes in file URIs (thanks Johannes Mueller)
- * Fix strict parsing of abolute URI schemes
+ * Fix strict parsing of absolute URI schemes
* Gracefully handle applications that write corrupt UTF-8
-- David Robillard <d@drobilla.net> Fri, 21 Jul 2017 06:28:47 +0000
diff --git a/doc/conf.py.in b/doc/conf.py.in
index 5037d217..77a7752b 100644
--- a/doc/conf.py.in
+++ b/doc/conf.py.in
@@ -1,10 +1,10 @@
-# Copyright 2020-2022 David Robillard <d@drobilla.net>
+# Copyright 2020-2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: ISC
# Project information
project = "@SERD_TITLE@"
-copyright = "2022-2023, David Robillard"
+copyright = "2022-2025, David Robillard"
author = "David Robillard"
release = "@SERD_VERSION@"
version = "@SERD_VERSION@"
diff --git a/doc/man/meson.build b/doc/man/meson.build
index 0ce65d01..f714b6be 100644
--- a/doc/man/meson.build
+++ b/doc/man/meson.build
@@ -49,6 +49,7 @@ if not get_option('tools').disabled()
mandoc,
'-Kutf-8',
'-Ostyle=mandoc.css,man=%N.html',
+ '-Ios=Serd @0@'.format(meson.project_version()),
'-Thtml',
'-Wwarning,stop',
'@INPUT@',
diff --git a/doc/man/serdi.1 b/doc/man/serdi.1
index a30c83a2..d928ad39 100644
--- a/doc/man/serdi.1
+++ b/doc/man/serdi.1
@@ -1,8 +1,8 @@
-.\" Copyright 2011-2024 David Robillard <d@drobilla.net>
+.\" Copyright 2011-2025 David Robillard <d@drobilla.net>
.\" SPDX-License-Identifier: ISC
-.Dd April 30, 2023
+.Dd January 30, 2025
.Dt SERDI 1
-.Os Serd 0.32.2
+.Os
.Sh NAME
.Nm serdi
.Nd read and write RDF syntax
@@ -191,4 +191,4 @@ exits with a status of 0, or non-zero if an error occurred.
.Nm
is a part of serd, by
.An David Robillard
-.Mt d@drobilla.net .
+.Aq Mt d@drobilla.net .
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 1f84950b..416da630 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -127,8 +127,7 @@ typedef enum {
} SerdStatus;
/// Return a string describing a status code
-SERD_CONST_API
-const uint8_t* SERD_NONNULL
+SERD_CONST_API const uint8_t* SERD_NONNULL
serd_strerror(SerdStatus status);
/**
@@ -268,8 +267,7 @@ serd_file_uri_parse(const uint8_t* SERD_NONNULL uri,
uint8_t* SERD_UNSPECIFIED* SERD_NULLABLE hostname);
/// Return true iff `utf8` starts with a valid URI scheme
-SERD_PURE_API
-bool
+SERD_PURE_API bool
serd_uri_string_has_scheme(const uint8_t* SERD_NULLABLE utf8);
/// Parse `utf8`, writing result to `out`
@@ -501,8 +499,7 @@ SERD_API SerdNode
serd_node_copy(const SerdNode* SERD_NULLABLE node);
/// Return true iff `a` is equal to `b`
-SERD_PURE_API
-bool
+SERD_PURE_API bool
serd_node_equals(const SerdNode* SERD_NONNULL a,
const SerdNode* SERD_NONNULL b);
@@ -717,8 +714,7 @@ serd_reader_set_error_sink(SerdReader* SERD_NONNULL reader,
void* SERD_UNSPECIFIED error_handle);
/// Return the `handle` passed to serd_reader_new()
-SERD_PURE_API
-void* SERD_UNSPECIFIED
+SERD_PURE_API void* SERD_UNSPECIFIED
serd_reader_get_handle(const SerdReader* SERD_NONNULL reader);
/**
@@ -816,7 +812,7 @@ serd_reader_read_string(SerdReader* SERD_NONNULL reader,
/**
Skip over bytes in the input until a specific byte is encountered.
- Typically used for recording from errors in a line-based syntax by skipping
+ Typically used for recovering from errors in a line-based syntax by skipping
ahead to the next newline.
@return #SERD_SUCCESS if the given byte was reached, or #SERD_FAILURE if the
@@ -868,8 +864,7 @@ SERD_API void
serd_writer_free(SerdWriter* SERD_NULLABLE writer);
/// Return the env used by `writer`
-SERD_PURE_API
-SerdEnv* SERD_NONNULL
+SERD_PURE_API SerdEnv* SERD_NONNULL
serd_writer_get_env(SerdWriter* SERD_NONNULL writer);
/**
diff --git a/meson.build b/meson.build
index 4b01ff9c..255c1870 100644
--- a/meson.build
+++ b/meson.build
@@ -12,10 +12,11 @@ project(
],
license: 'ISC',
meson_version: '>= 0.56.0',
- version: '0.32.3',
+ version: '0.32.5',
)
serd_src_root = meson.current_source_dir()
+serd_build_root = meson.current_build_dir()
major_version = meson.project_version().split('.')[0]
version_suffix = '-@0@'.format(major_version)
versioned_name = 'serd' + version_suffix
@@ -28,8 +29,94 @@ versioned_name = 'serd' + version_suffix
pkg = import('pkgconfig')
cc = meson.get_compiler('c')
-# Set global warning flags
-subdir('meson/suppressions')
+# Set global warning suppressions
+warning_level = get_option('warning_level')
+c_suppressions = []
+if cc.get_id() in ['clang', 'emscripten']
+ if warning_level == 'everything'
+ c_suppressions += [
+ '-Wno-cast-align',
+ '-Wno-cast-function-type-strict',
+ '-Wno-cast-qual',
+ '-Wno-declaration-after-statement',
+ '-Wno-format-nonliteral',
+ '-Wno-padded',
+ '-Wno-switch-default',
+ '-Wno-unsafe-buffer-usage',
+ ]
+
+ if host_machine.system() == 'windows'
+ c_suppressions += [
+ '-Wno-deprecated-declarations',
+ '-Wno-nonportable-system-include-path',
+ '-Wno-unused-macros',
+ ]
+ endif
+ endif
+
+ if warning_level in ['everything', '3']
+ c_suppressions += ['-Wno-nullability-extension']
+
+ if host_machine.system() == 'freebsd'
+ c_suppressions += ['-Wno-c11-extensions']
+ endif
+ endif
+
+ if not meson.is_cross_build()
+ c_suppressions += ['-Wno-poison-system-directories']
+ endif
+
+elif cc.get_id() == 'gcc'
+ if warning_level == 'everything'
+ c_suppressions += [
+ '-Wno-cast-align',
+ '-Wno-cast-qual',
+ '-Wno-format-nonliteral',
+ '-Wno-inline',
+ '-Wno-padded',
+ '-Wno-switch-default',
+ '-Wno-unsuffixed-float-constants',
+ '-Wno-unused-const-variable',
+ ]
+
+ if host_machine.system() == 'windows'
+ c_suppressions += ['-Wno-float-conversion']
+ endif
+ endif
+
+elif cc.get_id() == 'msvc'
+ c_suppressions += [
+ '/experimental:external',
+ '/external:W0',
+ '/external:anglebrackets',
+ ]
+
+ if warning_level == 'everything'
+ c_suppressions += [
+ '/wd4061', # enumerator in switch is not explicitly handled
+ '/wd4514', # unreferenced inline function has been removed
+ '/wd4710', # function not inlined
+ '/wd4711', # function selected for automatic inline expansion
+ '/wd4800', # implicit conversion from int to bool
+ '/wd4820', # padding added after construct
+ '/wd5045', # will insert Spectre mitigation for memory load
+ ]
+ endif
+
+ if warning_level in ['everything', '3']
+ c_suppressions += [
+ '/wd4706', # assignment within conditional expression
+ ]
+ endif
+
+ if warning_level in ['everything', '3', '2']
+ c_suppressions += [
+ '/wd4996', # POSIX name for this item is deprecated
+ ]
+ endif
+endif
+
+c_suppressions = cc.get_supported_arguments(c_suppressions)
################
# Dependencies #
@@ -135,6 +222,7 @@ libserd = library(
darwin_versions: [major_version + '.0.0', meson.project_version()],
dependencies: m_dep,
gnu_symbol_visibility: 'hidden',
+ implicit_include_directories: false,
include_directories: include_dirs,
install: true,
soversion: soversion,
@@ -148,7 +236,7 @@ serd_dep = declare_dependency(
link_with: libserd,
)
-# Generage pkg-config file for external dependants
+# Generate pkg-config file for external dependants
pkg.generate(
libserd,
description: 'Lightweight C library for working with RDF data',
@@ -181,6 +269,7 @@ if not get_option('tools').disabled()
files('src/serdi.c'),
c_args: c_suppressions + platform_c_args,
dependencies: serd_dep,
+ implicit_include_directories: false,
install: true,
link_args: tool_link_args,
)
diff --git a/meson/suppressions/meson.build b/meson/suppressions/meson.build
deleted file mode 100644
index 94ca305d..00000000
--- a/meson/suppressions/meson.build
+++ /dev/null
@@ -1,106 +0,0 @@
-# Copyright 2020-2024 David Robillard <d@drobilla.net>
-# SPDX-License-Identifier: 0BSD OR ISC
-
-# Project-specific warning suppressions
-
-warning_level = get_option('warning_level')
-
-#####
-# C #
-#####
-
-if is_variable('cc')
- c_suppressions = []
-
- if cc.get_id() in ['clang', 'emscripten']
- if warning_level == 'everything'
- c_suppressions += [
- '-Wno-cast-align',
- '-Wno-cast-function-type-strict',
- '-Wno-cast-qual',
- '-Wno-declaration-after-statement',
- '-Wno-double-promotion',
- '-Wno-format-nonliteral',
- '-Wno-padded',
- '-Wno-unsafe-buffer-usage',
- ]
-
- if host_machine.system() == 'windows'
- c_suppressions += [
- '-Wno-deprecated-declarations',
- '-Wno-nonportable-system-include-path',
- '-Wno-unused-macros',
- ]
- endif
- endif
-
- if warning_level in ['everything', '3']
- c_suppressions += [
- '-Wno-nullability-extension',
- ]
-
- if host_machine.system() == 'freebsd'
- c_suppressions += [
- '-Wno-c11-extensions',
- ]
- endif
- endif
-
- if not meson.is_cross_build()
- c_suppressions += ['-Wno-poison-system-directories']
- endif
-
- elif cc.get_id() == 'gcc'
- if warning_level == 'everything'
- c_suppressions += [
- '-Wno-cast-align',
- '-Wno-cast-qual',
- '-Wno-format-nonliteral',
- '-Wno-inline',
- '-Wno-padded',
- '-Wno-switch-default',
- '-Wno-unsuffixed-float-constants',
- '-Wno-unused-const-variable',
- ]
-
- if host_machine.system() == 'windows'
- c_suppressions += [
- '-Wno-float-conversion',
- ]
- endif
- endif
-
- elif cc.get_id() == 'msvc'
- c_suppressions += [
- '/experimental:external',
- '/external:W0',
- '/external:anglebrackets',
- ]
-
- if warning_level == 'everything'
- c_suppressions += [
- '/wd4061', # enumerator in switch is not explicitly handled
- '/wd4514', # unreferenced inline function has been removed
- '/wd4710', # function not inlined
- '/wd4711', # function selected for automatic inline expansion
- '/wd4800', # implicit conversion from int to bool
- '/wd4820', # padding added after construct
- '/wd5045', # will insert Spectre mitigation for memory load
- ]
- endif
-
- if warning_level in ['everything', '3']
- c_suppressions += [
- '/wd4706', # assignment within conditional expression
- ]
- endif
-
- if warning_level in ['everything', '3', '2']
- c_suppressions += [
- '/wd4996', # POSIX name for this item is deprecated
- ]
- endif
- endif
-
- c_suppressions = cc.get_supported_arguments(c_suppressions)
-endif
diff --git a/meson_options.txt b/meson_options.txt
index 09fc2334..30473e89 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -4,10 +4,10 @@
option('checks', type: 'feature', value: 'enabled', yield: true,
description: 'Check for platform-specific features')
-option('docs', type: 'feature', value: 'auto', yield: true,
+option('docs', type: 'feature', yield: true,
description: 'Build documentation')
-option('html', type: 'feature', value: 'auto', yield: true,
+option('html', type: 'feature', yield: true,
description: 'Build paginated HTML documentation')
option('lint', type: 'boolean', value: false, yield: true,
@@ -16,20 +16,20 @@ option('lint', type: 'boolean', value: false, yield: true,
option('man', type: 'feature', value: 'enabled', yield: true,
description: 'Install man pages')
-option('man_html', type: 'feature', value: 'auto', yield: true,
+option('man_html', type: 'feature', yield: true,
description: 'Build HTML man pages')
-option('singlehtml', type: 'feature', value: 'auto', yield: true,
+option('singlehtml', type: 'feature', yield: true,
description: 'Build single-page HTML documentation')
option('static', type: 'boolean', value: false, yield: true,
description: 'Statically link executables')
-option('tests', type: 'feature', value: 'auto', yield: true,
+option('tests', type: 'feature', yield: true,
description: 'Build tests')
option('title', type: 'string', value: 'Serd',
description: 'Project title')
-option('tools', type: 'feature', value: 'auto', yield: true,
+option('tools', type: 'feature', yield: true,
description: 'Build command line utilities')
diff --git a/scripts/check_formatting.py b/scripts/check_formatting.py
index e3342603..27daebe5 100755
--- a/scripts/check_formatting.py
+++ b/scripts/check_formatting.py
@@ -20,7 +20,7 @@ def run_formatter(command, good_path):
"""Run the formatter and compare the output."""
with subprocess.Popen(
- command, stderr=DEVNULL, stdout=PIPE, encoding="utf-8"
+ command, encoding="utf-8", stderr=DEVNULL, stdout=PIPE
) as proc:
out = list(proc.stdout)
diff --git a/scripts/serd_bench.py b/scripts/serd_bench.py
index e342976b..8e176322 100755
--- a/scripts/serd_bench.py
+++ b/scripts/serd_bench.py
@@ -3,7 +3,7 @@
# Copyright 2018-2023 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: ISC
-""""Benchmark RDF reading and writing commands."""
+"""Benchmark RDF reading and writing commands."""
# pylint: disable=consider-using-f-string
# pylint: disable=consider-using-with
@@ -192,10 +192,13 @@ def run(progs, n_min, n_max, step):
with open(filename(n) + ".out", "w") as out:
sys.stderr.write(cmd + "\n")
proc = subprocess.Popen(
- cmd.split(), stdout=out, stderr=subprocess.PIPE
+ cmd.split(),
+ encoding="utf-8",
+ stdout=out,
+ stderr=subprocess.PIPE,
)
- time, memory = parse_time(proc.communicate()[1].decode())
+ time, memory = parse_time(proc.communicate()[1])
rows["time"] += ["%.07f" % time]
rows["throughput"] += ["%d" % (n / time)]
rows["memory"] += [str(memory)]
diff --git a/src/.clang-tidy b/src/.clang-tidy
index 52862fe4..18cba92e 100644
--- a/src/.clang-tidy
+++ b/src/.clang-tidy
@@ -2,9 +2,13 @@
# SPDX-License-Identifier: 0BSD OR ISC
Checks: >
+ -*-macro-to-enum,
-*-magic-numbers,
-bugprone-easily-swappable-parameters,
+ -bugprone-inc-dec-in-conditions,
+ -bugprone-multi-level-implicit-pointer-conversion,
-cert-err33-c,
+ -clang-analyzer-optin.core.EnumCastOutOfRange,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
-concurrency-mt-unsafe,
-google-readability-todo,
@@ -12,7 +16,7 @@ Checks: >
-hicpp-signed-bitwise,
-llvm-header-guard,
-misc-no-recursion,
- -modernize-macro-to-enum,
+ -readability-avoid-nested-conditional-operator,
CheckOptions:
- key: readability-function-cognitive-complexity.IgnoreMacros
value: 'true'
diff --git a/src/base64.c b/src/base64.c
index 42d296b3..07fdcbfd 100644
--- a/src/base64.c
+++ b/src/base64.c
@@ -6,7 +6,7 @@
#include "serd_internal.h"
#include "string_utils.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdbool.h>
@@ -51,7 +51,7 @@ encode_chunk(uint8_t out[4], const uint8_t in[3], const size_t n_in)
size_t
serd_base64_get_length(const size_t size, const bool wrap_lines)
{
- return (size + 2) / 3 * 4 + (wrap_lines * ((size - 1) / 57));
+ return ((size + 2) / 3 * 4) + (wrap_lines * ((size - 1) / 57));
}
bool
@@ -91,7 +91,7 @@ decode_chunk(const uint8_t in[4], uint8_t out[3])
out[0] = (uint8_t)(((unmap(in[0]) << 2)) | unmap(in[1]) >> 4);
out[1] = (uint8_t)(((unmap(in[1]) << 4) & 0xF0) | unmap(in[2]) >> 2);
out[2] = (uint8_t)(((unmap(in[2]) << 6) & 0xC0) | unmap(in[3]));
- return 1U + (in[2] != '=') + ((in[2] != '=') && (in[3] != '='));
+ return (size_t)1U + (in[2] != '=') + ((in[2] != '=') && (in[3] != '='));
}
void*
@@ -102,7 +102,7 @@ serd_base64_decode(const uint8_t* const str,
assert(str);
assert(size);
- void* buf = malloc((len * 3) / 4 + 2);
+ void* buf = malloc(((len * 3) / 4) + 2);
*size = 0;
for (size_t i = 0, j = 0; i < len; j += 3) {
diff --git a/src/base64.h b/src/base64.h
index 21a6878c..4a4675e7 100644
--- a/src/base64.h
+++ b/src/base64.h
@@ -4,7 +4,7 @@
#ifndef SERD_SRC_BASE64_H
#define SERD_SRC_BASE64_H
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <stdbool.h>
#include <stddef.h>
diff --git a/src/byte_sink.h b/src/byte_sink.h
index 65b5eb12..327c1b82 100644
--- a/src/byte_sink.h
+++ b/src/byte_sink.h
@@ -7,7 +7,7 @@
#include "serd_internal.h"
#include "system.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <stddef.h>
#include <stdint.h>
@@ -22,7 +22,7 @@ typedef struct SerdByteSinkImpl {
} SerdByteSink;
static inline SerdByteSink
-serd_byte_sink_new(SerdSink sink, void* stream, size_t block_size)
+serd_byte_sink_new(SerdSink sink, void* const stream, const size_t block_size)
{
SerdByteSink bsink = {sink, stream, NULL, 0, block_size};
@@ -34,7 +34,7 @@ serd_byte_sink_new(SerdSink sink, void* stream, size_t block_size)
}
static inline SerdStatus
-serd_byte_sink_flush(SerdByteSink* bsink)
+serd_byte_sink_flush(SerdByteSink* const bsink)
{
if (bsink->block_size > 1 && bsink->size > 0) {
const size_t size = bsink->size;
@@ -48,7 +48,7 @@ serd_byte_sink_flush(SerdByteSink* bsink)
}
static inline void
-serd_byte_sink_free(SerdByteSink* bsink)
+serd_byte_sink_free(SerdByteSink* const bsink)
{
serd_byte_sink_flush(bsink);
serd_free_aligned(bsink->buf);
@@ -56,7 +56,7 @@ serd_byte_sink_free(SerdByteSink* bsink)
}
static inline size_t
-serd_byte_sink_write(const void* buf, size_t len, SerdByteSink* bsink)
+serd_byte_sink_write(const void* buf, size_t len, SerdByteSink* const bsink)
{
if (len == 0) {
return 0;
diff --git a/src/byte_source.c b/src/byte_source.c
index 5443f4dd..74827741 100644
--- a/src/byte_source.c
+++ b/src/byte_source.c
@@ -5,7 +5,7 @@
#include "system.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdbool.h>
@@ -19,18 +19,17 @@ serd_byte_source_page(SerdByteSource* const source)
const size_t n_read =
source->read_func(source->file_buf, 1, source->page_size, source->stream);
+ source->buf_size = n_read;
+ if (n_read < source->page_size) {
+ source->file_buf[n_read] = '\0';
+ }
+
if (n_read == 0) {
- source->file_buf[0] = '\0';
- source->eof = true;
+ source->eof = true;
return (source->error_func(source->stream) ? SERD_ERR_UNKNOWN
: SERD_FAILURE);
}
- if (n_read < source->page_size) {
- source->file_buf[n_read] = '\0';
- source->buf_size = n_read;
- }
-
return SERD_SUCCESS;
}
@@ -52,7 +51,7 @@ serd_byte_source_open_source(SerdByteSource* const source,
source->stream = stream;
source->from_stream = true;
source->page_size = page_size;
- source->buf_size = page_size;
+ source->buf_size = 0U;
source->cur = cur;
source->error_func = error_func;
source->read_func = read_func;
diff --git a/src/byte_source.h b/src/byte_source.h
index 02aab663..ac0bc306 100644
--- a/src/byte_source.h
+++ b/src/byte_source.h
@@ -4,7 +4,7 @@
#ifndef SERD_SRC_BYTE_SOURCE_H
#define SERD_SRC_BYTE_SOURCE_H
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdbool.h>
@@ -58,21 +58,22 @@ SerdStatus
serd_byte_source_page(SerdByteSource* source);
static inline SERD_PURE_FUNC uint8_t
-serd_byte_source_peek(SerdByteSource* source)
+serd_byte_source_peek(SerdByteSource* const source)
{
assert(source->prepared);
return source->read_buf[source->read_head];
}
static inline SerdStatus
-serd_byte_source_advance(SerdByteSource* source)
+serd_byte_source_advance(SerdByteSource* const source)
{
SerdStatus st = SERD_SUCCESS;
- if (serd_byte_source_peek(source) == '\n') {
+ const uint8_t c = serd_byte_source_peek(source);
+ if (c == '\n') {
++source->cur.line;
source->cur.col = 0;
- } else {
+ } else if (c) {
++source->cur.col;
}
@@ -82,7 +83,7 @@ serd_byte_source_advance(SerdByteSource* source)
if (source->page_size > 1) {
if (++source->read_head == source->page_size) {
st = serd_byte_source_page(source);
- } else if (source->read_head == source->buf_size) {
+ } else if (source->read_head >= source->buf_size) {
source->eof = true;
}
} else {
diff --git a/src/env.c b/src/env.c
index 75ae6485..2185d022 100644
--- a/src/env.c
+++ b/src/env.c
@@ -1,7 +1,7 @@
// Copyright 2011-2023 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdbool.h>
diff --git a/src/n3.c b/src/n3.c
index e5a06c77..b0bd4476 100644
--- a/src/n3.c
+++ b/src/n3.c
@@ -1,14 +1,13 @@
-// Copyright 2011-2023 David Robillard <d@drobilla.net>
+// Copyright 2011-2025 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#include "reader.h"
#include "serd_internal.h"
-#include "stack.h"
#include "string_utils.h"
#include "try.h"
#include "uri_utils.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdbool.h>
@@ -46,7 +45,7 @@ read_HEX(SerdReader* const reader)
return (uint8_t)eat_byte_safe(reader, c);
}
- r_err(reader, SERD_ERR_BAD_SYNTAX, "invalid hexadecimal digit '%c'\n", c);
+ r_err_char(reader, "hexadecimal", c);
return 0;
}
@@ -172,7 +171,7 @@ bad_char(SerdReader* const reader, const char* const fmt, const uint8_t c)
static SerdStatus
read_utf8_bytes(SerdReader* const reader,
uint8_t bytes[4],
- uint32_t* const size,
+ uint8_t* const size,
const uint8_t c)
{
*size = utf8_num_bytes(c);
@@ -181,9 +180,9 @@ read_utf8_bytes(SerdReader* const reader,
}
bytes[0] = c;
- for (unsigned i = 1; i < *size; ++i) {
+ for (uint8_t i = 1U; i < *size; ++i) {
const int b = peek_byte(reader);
- if (b == EOF || ((uint8_t)b & 0x80) == 0) {
+ if (b == EOF || ((uint8_t)b & 0x80U) == 0U) {
return bad_char(reader, "invalid UTF-8 continuation 0x%X\n", (uint8_t)b);
}
@@ -196,7 +195,7 @@ read_utf8_bytes(SerdReader* const reader,
static SerdStatus
read_utf8_character(SerdReader* const reader, const Ref dest, const uint8_t c)
{
- uint32_t size = 0;
+ uint8_t size = 0U;
uint8_t bytes[4] = {0, 0, 0, 0};
SerdStatus st = read_utf8_bytes(reader, bytes, &size, c);
if (st) {
@@ -214,7 +213,7 @@ read_utf8_code(SerdReader* const reader,
uint32_t* const code,
const uint8_t c)
{
- uint32_t size = 0;
+ uint8_t size = 0U;
uint8_t bytes[4] = {0, 0, 0, 0};
SerdStatus st = read_utf8_bytes(reader, bytes, &size, c);
if (st) {
@@ -354,11 +353,11 @@ read_STRING_LITERAL_LONG(SerdReader* const reader,
push_byte(reader, ref, c);
st = read_character(reader, ref, flags, (uint8_t)q2);
}
- } else if (c == EOF) {
- st = r_err(reader, SERD_ERR_BAD_SYNTAX, "end of file in long string\n");
- } else {
+ } else if (c > 0) {
st =
read_character(reader, ref, flags, (uint8_t)eat_byte_safe(reader, c));
+ } else {
+ return r_err(reader, SERD_ERR_BAD_SYNTAX, "end of file in long string\n");
}
}
@@ -457,7 +456,7 @@ read_PN_CHARS_BASE(SerdReader* const reader, const Ref dest)
return push_byte(reader, dest, eat_byte_safe(reader, c));
}
- if (c == EOF || !(c & 0x80)) {
+ if (c < 0x80) {
return SERD_FAILURE;
}
@@ -465,11 +464,7 @@ read_PN_CHARS_BASE(SerdReader* const reader, const Ref dest)
read_utf8_code(reader, dest, &code, (uint8_t)c);
if (!is_PN_CHARS_BASE(code)) {
- r_err(
- reader, SERD_ERR_BAD_SYNTAX, "invalid character U+%04X in name\n", code);
- if (reader->strict) {
- return SERD_ERR_BAD_SYNTAX;
- }
+ st = r_err_char(reader, "name", (int)code);
}
return st;
@@ -493,7 +488,7 @@ read_PN_CHARS(SerdReader* const reader, const Ref dest)
return push_byte(reader, dest, eat_byte_safe(reader, c));
}
- if (c == EOF || !(c & 0x80)) {
+ if (c < 0x80) {
return SERD_FAILURE;
}
@@ -501,8 +496,7 @@ read_PN_CHARS(SerdReader* const reader, const Ref dest)
TRY(st, read_utf8_code(reader, dest, &code, (uint8_t)c));
if (!is_PN_CHARS(code)) {
- return r_err(
- reader, SERD_ERR_BAD_SYNTAX, "invalid character U+%04X in name\n", code);
+ st = r_err_char(reader, "name", (int)code);
}
return st;
@@ -531,7 +525,7 @@ read_PN_LOCAL_ESC(SerdReader* const reader, const Ref dest)
return ((c == '!') || in_range(c, '#', '/') || (c == ';') || (c == '=') ||
(c == '?') || (c == '@') || (c == '_') || (c == '~'))
? push_byte(reader, dest, eat_byte_safe(reader, c))
- : r_err(reader, SERD_ERR_BAD_SYNTAX, "invalid escape\n");
+ : r_err(reader, SERD_ERR_BAD_SYNTAX, "bad escape\n");
}
static SerdStatus
@@ -589,9 +583,7 @@ read_PN_LOCAL(SerdReader* const reader, const Ref dest, bool* const ate_dot)
SerdNode* const n = deref(reader, dest);
if (trailing_unescaped_dot) {
// Ate trailing dot, pop it from stack/node and inform caller
- --n->n_bytes;
- serd_stack_pop(&reader->stack, 1);
- *ate_dot = true;
+ *ate_dot = pop_last_node_char(reader, n);
}
return (st > SERD_FAILURE) ? st : SERD_SUCCESS;
@@ -599,31 +591,37 @@ read_PN_LOCAL(SerdReader* const reader, const Ref dest, bool* const ate_dot)
// Read the remainder of a PN_PREFIX after some initial characters
static SerdStatus
-read_PN_PREFIX_tail(SerdReader* const reader, const Ref dest)
+read_PN_PREFIX_tail(SerdReader* const reader,
+ const Ref dest,
+ bool* const ate_dot)
{
- int c = 0;
- while ((c = peek_byte(reader)) > 0) { // Middle: (PN_CHARS | '.')*
+ SerdStatus st = SERD_SUCCESS;
+ bool trailing_unescaped_dot = false;
+
+ while (!st) { // Middle: (PN_CHARS | '.')*
+ const int c = peek_byte(reader);
if (c == '.') {
push_byte(reader, dest, eat_byte_safe(reader, c));
- } else if (read_PN_CHARS(reader, dest)) {
- break;
+ trailing_unescaped_dot = true;
+ } else if (!(st = read_PN_CHARS(reader, dest))) {
+ trailing_unescaped_dot = false;
}
}
- const SerdNode* const n = deref(reader, dest);
- if (n->buf[n->n_bytes - 1] == '.' && read_PN_CHARS(reader, dest)) {
- return r_err(reader, SERD_ERR_BAD_SYNTAX, "prefix ends with '.'\n");
+ if (trailing_unescaped_dot) {
+ SerdNode* const n = deref(reader, dest);
+ *ate_dot = pop_last_node_char(reader, n);
}
- return SERD_SUCCESS;
+ return st;
}
static SerdStatus
-read_PN_PREFIX(SerdReader* const reader, const Ref dest)
+read_PN_PREFIX(SerdReader* const reader, const Ref dest, bool* const ate_dot)
{
const SerdStatus st = read_PN_CHARS_BASE(reader, dest);
- return st ? st : read_PN_PREFIX_tail(reader, dest);
+ return st ? st : read_PN_PREFIX_tail(reader, dest, ate_dot);
}
static SerdStatus
@@ -631,7 +629,7 @@ read_LANGTAG(SerdReader* const reader, Ref* const dest)
{
int c = peek_byte(reader);
if (!is_alpha(c)) {
- return r_err(reader, SERD_ERR_BAD_SYNTAX, "unexpected '%c'\n", c);
+ return r_err_char(reader, "language", c);
}
*dest = push_node(reader, SERD_LITERAL, "", 0);
@@ -657,7 +655,7 @@ read_IRIREF_scheme(SerdReader* const reader, const Ref dest)
{
int c = peek_byte(reader);
if (!is_alpha(c)) {
- return r_err(reader, SERD_ERR_BAD_SYNTAX, "bad IRI scheme start '%c'\n", c);
+ return r_err_char(reader, "IRI scheme start", c);
}
while ((c = peek_byte(reader)) > 0) {
@@ -666,11 +664,7 @@ read_IRIREF_scheme(SerdReader* const reader, const Ref dest)
}
if (!is_uri_scheme_char(c)) {
- return r_err(reader,
- SERD_ERR_BAD_SYNTAX,
- "bad IRI scheme char U+%04X (%c)\n",
- (unsigned)c,
- (char)c);
+ return r_err_char(reader, "IRI scheme", c);
}
push_byte(reader, dest, eat_byte_safe(reader, c));
@@ -704,8 +698,7 @@ read_IRIREF(SerdReader* const reader, Ref* const dest)
case '"':
case '<':
*dest = pop_node(reader, *dest);
- return r_err(
- reader, SERD_ERR_BAD_SYNTAX, "invalid IRI character '%c'\n", c);
+ return r_err_char(reader, "IRI", c);
case '>':
return SERD_SUCCESS;
@@ -713,7 +706,7 @@ read_IRIREF(SerdReader* const reader, Ref* const dest)
case '\\':
if (read_UCHAR(reader, *dest, &code)) {
*dest = pop_node(reader, *dest);
- return r_err(reader, SERD_ERR_BAD_SYNTAX, "invalid IRI escape\n");
+ return r_err_char(reader, "IRI escape", c);
}
if (code == ' ' || code == '<' || code == '>') {
@@ -731,21 +724,20 @@ read_IRIREF(SerdReader* const reader, Ref* const dest)
case '|':
case '}':
*dest = pop_node(reader, *dest);
- return r_err(
- reader, SERD_ERR_BAD_SYNTAX, "invalid IRI character '%c'\n", c);
+ return r_err_char(reader, "IRI", c);
default:
- if (c <= 0x20) {
+ if (c <= 0) {
+ st = r_err(reader, SERD_ERR_BAD_SYNTAX, "unexpected end of file\n");
+ } else if (c <= 0x20) {
st = r_err(reader,
SERD_ERR_BAD_SYNTAX,
"invalid IRI character (escape %%%02X)\n",
(unsigned)c);
- if (reader->strict) {
- break;
+ if (!reader->strict) {
+ st = SERD_FAILURE;
+ push_byte(reader, *dest, c);
}
-
- st = SERD_FAILURE;
- push_byte(reader, *dest, c);
} else if (!(c & 0x80)) {
push_byte(reader, *dest, c);
} else if (read_utf8_character(reader, *dest, (uint8_t)c)) {
@@ -762,15 +754,10 @@ read_IRIREF(SerdReader* const reader, Ref* const dest)
}
static SerdStatus
-read_PrefixedName(SerdReader* const reader,
- const Ref dest,
- const bool read_prefix,
- bool* const ate_dot)
+read_PrefixedName(SerdReader* const reader, const Ref dest, bool* const ate_dot)
{
SerdStatus st = SERD_SUCCESS;
- if (read_prefix) {
- TRY_FAILING(st, read_PN_PREFIX(reader, dest));
- }
+ TRY_FAILING(st, read_PN_PREFIX(reader, dest, ate_dot));
if (peek_byte(reader) != ':') {
return SERD_FAILURE;
@@ -825,18 +812,16 @@ read_number(SerdReader* const reader,
// all other cases ::= ( '-' | '+' ) [0-9]+ ( . )? ( [0-9]+ )? ...
TRY(st, read_0_9(reader, *dest, true));
if ((c = peek_byte(reader)) == '.') {
- has_decimal = true;
-
// Annoyingly, dot can be end of statement, so tentatively eat
skip_byte(reader, c);
c = peek_byte(reader);
if (!is_digit(c) && c != 'e' && c != 'E') {
- *ate_dot = true; // Force caller to deal with stupid grammar
- return SERD_SUCCESS; // Next byte is not a number character
+ *ate_dot = true; // Force caller to deal with silly grammar
+ } else {
+ has_decimal = true;
+ push_byte(reader, *dest, '.');
+ read_0_9(reader, *dest, false);
}
-
- push_byte(reader, *dest, '.');
- read_0_9(reader, *dest, false);
}
}
c = peek_byte(reader);
@@ -868,7 +853,7 @@ read_iri(SerdReader* const reader, Ref* const dest, bool* const ate_dot)
}
*dest = push_node(reader, SERD_CURIE, "", 0);
- return read_PrefixedName(reader, *dest, true, ate_dot);
+ return read_PrefixedName(reader, *dest, ate_dot);
}
static SerdStatus
@@ -920,29 +905,31 @@ read_verb(SerdReader* const reader, Ref* const dest)
return read_IRIREF(reader, dest);
}
- /* Either a qname, or "a". Read the prefix first, and if it is in fact
- "a", produce that instead.
- */
- *dest = push_node(reader, SERD_CURIE, "", 0);
+ Ref p = push_node(reader, SERD_CURIE, "", 0);
- SerdStatus st = read_PN_PREFIX(reader, *dest);
+ // Try to read as a prefixed name
bool ate_dot = false;
- SerdNode* node = deref(reader, *dest);
- const int next = peek_byte(reader);
- if (!st && node->n_bytes == 1 && node->buf[0] == 'a' && next != ':' &&
- !is_PN_CHARS_BASE((uint32_t)next)) {
- pop_node(reader, *dest);
- *dest = push_node(reader, SERD_URI, NS_RDF "type", 47);
- return SERD_SUCCESS;
+ SerdStatus st = read_PrefixedName(reader, p, &ate_dot);
+
+ if (st == SERD_FAILURE) {
+ // Check if this is actually the "a" shorthand
+ const SerdNode* const node = deref(reader, p);
+ if (node->n_bytes == 1 && node->buf[0] == 'a') {
+ pop_node(reader, p);
+ p = push_node(reader, SERD_URI, NS_RDF "type", 47);
+ st = SERD_SUCCESS;
+ } else {
+ st = SERD_ERR_BAD_SYNTAX;
+ }
}
- if (st > SERD_FAILURE ||
- (st = read_PrefixedName(reader, *dest, false, &ate_dot)) || ate_dot) {
- *dest = pop_node(reader, *dest);
- st = st > SERD_FAILURE ? st : SERD_ERR_BAD_SYNTAX;
+ if (st) {
+ pop_node(reader, p);
+ *dest = 0;
return r_err(reader, st, "bad verb\n");
}
+ *dest = p;
return SERD_SUCCESS;
}
@@ -981,9 +968,7 @@ read_BLANK_NODE_LABEL(SerdReader* const reader,
SerdNode* n = deref(reader, ref);
if (n->buf[n->n_bytes - 1] == '.' && read_PN_CHARS(reader, ref)) {
// Ate trailing dot, pop it from stack/node and inform caller
- --n->n_bytes;
- serd_stack_pop(&reader->stack, 1);
- *ate_dot = true;
+ *ate_dot = pop_last_node_char(reader, n);
}
if (fancy_syntax(reader)) {
@@ -1078,6 +1063,40 @@ read_anon(SerdReader* const reader,
: SERD_ERR_BAD_SYNTAX;
}
+// Read a "named" object: a boolean literal or a prefixed name
+static SerdStatus
+read_named_object(SerdReader* const reader,
+ Ref* const dest,
+ Ref* const datatype,
+ bool* const ate_dot)
+{
+ static const char* const XSD_BOOLEAN = NS_XSD "boolean";
+ static const size_t XSD_BOOLEAN_LEN = 40;
+
+ // Try to read as a prefixed name
+ const Ref o = push_node(reader, SERD_CURIE, "", 0);
+ SerdStatus st = read_PrefixedName(reader, o, ate_dot);
+
+ if (st == SERD_FAILURE) {
+ // Check if this is actually a boolean literal
+ SerdNode* const node = deref(reader, o);
+ if ((node->n_bytes == 4 && !memcmp(node->buf, "true", 4)) ||
+ (node->n_bytes == 5 && !memcmp(node->buf, "false", 5))) {
+ node->type = SERD_LITERAL;
+ *datatype = push_node(reader, SERD_URI, XSD_BOOLEAN, XSD_BOOLEAN_LEN);
+ st = SERD_SUCCESS;
+ }
+ }
+
+ if (st) {
+ pop_node(reader, o);
+ return r_err(reader, SERD_ERR_BAD_SYNTAX, "expected prefixed name\n");
+ }
+
+ *dest = o;
+ return SERD_SUCCESS;
+}
+
/* If emit is true: recurses, calling statement_sink for every statement
encountered, and leaves stack in original calling state (i.e. pops
everything it pushes). */
@@ -1087,9 +1106,6 @@ read_object(SerdReader* const reader,
const bool emit,
bool* const ate_dot)
{
- static const char* const XSD_BOOLEAN = NS_XSD "boolean";
- static const size_t XSD_BOOLEAN_LEN = 40;
-
#ifndef NDEBUG
const size_t orig_stack_size = reader->stack.size;
#endif
@@ -1097,7 +1113,6 @@ read_object(SerdReader* const reader,
SerdStatus st = SERD_FAILURE;
bool simple = (ctx->subject != 0);
- SerdNode* node = NULL;
Ref o = 0;
Ref datatype = 0;
Ref lang = 0;
@@ -1147,27 +1162,8 @@ read_object(SerdReader* const reader,
st = read_literal(reader, &o, &datatype, &lang, &flags, ate_dot);
break;
default:
- /* Either a boolean literal, or a qname. Read the prefix first, and if
- it is in fact a "true" or "false" literal, produce that instead.
- */
- o = push_node(reader, SERD_CURIE, "", 0);
- while (!read_PN_CHARS_BASE(reader, o)) {
- }
- node = deref(reader, o);
- if ((node->n_bytes == 4 && !memcmp(node->buf, "true", 4)) ||
- (node->n_bytes == 5 && !memcmp(node->buf, "false", 5))) {
- node->type = SERD_LITERAL;
- datatype = push_node(reader, SERD_URI, XSD_BOOLEAN, XSD_BOOLEAN_LEN);
- st = SERD_SUCCESS;
- } else if (read_PN_PREFIX_tail(reader, o) > SERD_FAILURE) {
- st = SERD_ERR_BAD_SYNTAX;
- } else {
- if ((st = read_PrefixedName(reader, o, false, ate_dot))) {
- st = st > SERD_FAILURE ? st : SERD_ERR_BAD_SYNTAX;
- pop_node(reader, o);
- return r_err(reader, st, "expected prefixed name\n");
- }
- }
+ // Either a boolean literal or a prefixed name
+ st = read_named_object(reader, &o, &datatype, ate_dot);
}
if (!st && simple && o) {
@@ -1319,7 +1315,10 @@ read_collection(SerdReader* const reader, ReadContext ctx, Ref* const dest)
// _:node rdf:rest _:rest
*ctx.flags |= SERD_LIST_CONT;
ctx.predicate = reader->rdf_rest;
- TRY(st, emit_statement(reader, ctx, (end ? reader->rdf_nil : rest), 0, 0));
+ st = emit_statement(reader, ctx, (end ? reader->rdf_nil : rest), 0, 0);
+ if (st) {
+ break;
+ }
ctx.subject = rest; // _:node = _:rest
rest = node; // _:rest = (old)_:node
@@ -1444,12 +1443,12 @@ read_prefixID(SerdReader* const reader, const bool sparql, const bool token)
}
read_ws_star(reader);
- Ref name = push_node(reader, SERD_LITERAL, "", 0);
- TRY_FAILING(st, read_PN_PREFIX(reader, name));
-
- if (eat_byte_check(reader, ':') != ':') {
+ Ref name = push_node(reader, SERD_LITERAL, "", 0);
+ bool ate_dot = false;
+ TRY_FAILING(st, read_PN_PREFIX(reader, name, &ate_dot));
+ if (ate_dot || eat_byte_check(reader, ':') != ':') {
pop_node(reader, name);
- return SERD_ERR_BAD_SYNTAX;
+ return r_err(reader, SERD_ERR_BAD_SYNTAX, "expected a prefix name\n");
}
read_ws_star(reader);
@@ -1540,7 +1539,7 @@ token_equals(SerdReader* const reader,
const char* const tok,
const size_t n)
{
- SerdNode* const node = deref(reader, ref);
+ const SerdNode* const node = deref(reader, ref);
if (!node || node->n_bytes != n) {
return false;
}
diff --git a/src/node.c b/src/node.c
index e435093e..b7bbfd1c 100644
--- a/src/node.c
+++ b/src/node.c
@@ -1,12 +1,10 @@
// Copyright 2011-2023 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
-#include "node.h"
-
#include "base64.h"
#include "string_utils.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <float.h>
@@ -17,14 +15,11 @@
#include <stdlib.h>
#include <string.h>
-#ifdef _WIN32
-# ifndef isnan
-# define isnan(x) _isnan(x)
-# endif
-# ifndef isinf
-# define isinf(x) (!_finite(x))
-# endif
-#endif
+struct SerdNodeImpl {
+ size_t n_bytes; /**< Size in bytes (not including null) */
+ SerdNodeFlags flags; /**< Node flags (e.g. string properties) */
+ SerdType type; /**< Node type */
+};
static size_t
serd_uri_string_length(const SerdURI* const uri)
@@ -297,7 +292,7 @@ serd_node_new_decimal(const double d, const unsigned frac_digits)
char* t = s - 1;
uint64_t dec = (uint64_t)int_part;
do {
- *t-- = (char)('0' + dec % 10);
+ *t-- = (char)('0' + (dec % 10));
} while ((dec /= 10) > 0);
*s++ = '.';
diff --git a/src/node.h b/src/node.h
deleted file mode 100644
index a4d5dcd5..00000000
--- a/src/node.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2011-2023 David Robillard <d@drobilla.net>
-// SPDX-License-Identifier: ISC
-
-#ifndef SERD_SRC_NODE_H
-#define SERD_SRC_NODE_H
-
-#include "serd/serd.h"
-
-#include <stddef.h>
-
-struct SerdNodeImpl {
- size_t n_bytes; /**< Size in bytes (not including null) */
- SerdNodeFlags flags; /**< Node flags (e.g. string properties) */
- SerdType type; /**< Node type */
-};
-
-static inline char* SERD_NONNULL
-serd_node_buffer(SerdNode* SERD_NONNULL node)
-{
- return (char*)(node + 1);
-}
-
-static inline const char* SERD_NONNULL
-serd_node_buffer_c(const SerdNode* SERD_NONNULL node)
-{
- return (const char*)(node + 1);
-}
-
-SerdNode* SERD_ALLOCATED
-serd_node_malloc(size_t n_bytes, SerdNodeFlags flags, SerdType type);
-
-void
-serd_node_set(SerdNode* SERD_NULLABLE* SERD_NONNULL dst,
- const SerdNode* SERD_NULLABLE src);
-
-#endif // SERD_SRC_NODE_H
diff --git a/src/reader.c b/src/reader.c
index aa24a9ca..98500fcd 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -4,10 +4,11 @@
#include "reader.h"
#include "byte_source.h"
+#include "serd_internal.h"
#include "stack.h"
#include "system.h"
-#include "serd_internal.h"
+#include <serd/serd.h>
#include <assert.h>
#include <errno.h>
@@ -29,6 +30,17 @@ r_err(SerdReader* const reader, const SerdStatus st, const char* const fmt, ...)
return st;
}
+SerdStatus
+r_err_char(SerdReader* const reader, const char* const kind, const int c)
+{
+ const SerdStatus st = SERD_ERR_BAD_SYNTAX;
+
+ return (c < 0x20 || c == 0x7F) ? r_err(reader, st, "bad %s character\n", kind)
+ : (c == '\'' || c >= 0x80)
+ ? r_err(reader, st, "bad %s character U+%04X\n", kind, (uint32_t)c)
+ : r_err(reader, st, "bad %s character '%c'\n", kind, c);
+}
+
void
set_blank_id(SerdReader* const reader, const Ref ref, const size_t buf_size)
{
@@ -90,12 +102,16 @@ push_node_padded(SerdReader* const reader,
uint8_t* buf = (uint8_t*)(node + 1);
memcpy(buf, str, n_bytes + 1);
+ const Ref ref = (Ref)((uint8_t*)node - reader->stack.buf);
+
#ifdef SERD_STACK_CHECK
- reader->allocs = (Ref*)realloc(reader->allocs,
+ reader->allocs = (Ref*)realloc(reader->allocs,
sizeof(reader->allocs) * (++reader->n_allocs));
- reader->allocs[reader->n_allocs - 1] = ((uint8_t*)mem - reader->stack.buf);
+
+ reader->allocs[reader->n_allocs - 1] = ref;
#endif
- return (Ref)((uint8_t*)node - reader->stack.buf);
+
+ return ref;
}
Ref
@@ -118,6 +134,14 @@ deref(SerdReader* const reader, const Ref ref)
return NULL;
}
+bool
+pop_last_node_char(SerdReader* const reader, SerdNode* const node)
+{
+ --node->n_bytes;
+ serd_stack_pop(&reader->stack, 1);
+ return true;
+}
+
Ref
pop_node(SerdReader* const reader, const Ref ref)
{
@@ -127,8 +151,9 @@ pop_node(SerdReader* const reader, const Ref ref)
SERD_STACK_ASSERT_TOP(reader, ref);
--reader->n_allocs;
#endif
- SerdNode* const node = deref(reader, ref);
- uint8_t* const top = reader->stack.buf + reader->stack.size;
+ SerdNode* const node = deref(reader, ref);
+ const uint8_t* const top = reader->stack.buf + reader->stack.size;
+ assert(top > (uint8_t*)node);
serd_stack_pop_aligned(&reader->stack, (size_t)(top - (uint8_t*)node));
}
return 0;
diff --git a/src/reader.h b/src/reader.h
index 9b558d1f..74255862 100644
--- a/src/reader.h
+++ b/src/reader.h
@@ -8,16 +8,16 @@
#include "byte_source.h"
#include "stack.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
-#include <stdio.h>
#ifdef SERD_STACK_CHECK
# define SERD_STACK_ASSERT_TOP(reader, ref) \
- assert(ref == reader->allocs[reader->n_allocs - 1]);
+ assert(ref == reader->allocs[reader->n_allocs - 1])
#else
# define SERD_STACK_ASSERT_TOP(reader, ref)
#endif
@@ -69,6 +69,9 @@ SERD_LOG_FUNC(3, 4)
SerdStatus
r_err(SerdReader* reader, SerdStatus st, const char* fmt, ...);
+SerdStatus
+r_err_char(SerdReader* reader, const char* kind, int c);
+
Ref
push_node_padded(SerdReader* reader,
size_t maxlen,
@@ -91,6 +94,9 @@ set_blank_id(SerdReader* reader, Ref ref, size_t buf_size);
SerdNode*
deref(SerdReader* reader, Ref ref);
+bool
+pop_last_node_char(SerdReader* reader, SerdNode* node);
+
Ref
pop_node(SerdReader* reader, Ref ref);
@@ -110,15 +116,15 @@ SerdStatus
read_turtleTrigDoc(SerdReader* reader);
static inline int
-peek_byte(SerdReader* reader)
+peek_byte(SerdReader* const reader)
{
SerdByteSource* source = &reader->source;
- return source->eof ? EOF : (int)source->read_buf[source->read_head];
+ return source->eof ? -1 : (int)source->read_buf[source->read_head];
}
static inline SerdStatus
-skip_byte(SerdReader* reader, const int byte)
+skip_byte(SerdReader* const reader, const int byte)
{
(void)byte;
@@ -127,8 +133,8 @@ skip_byte(SerdReader* reader, const int byte)
return serd_byte_source_advance(&reader->source);
}
-static inline int SERD_NODISCARD
-eat_byte_safe(SerdReader* reader, const int byte)
+SERD_NODISCARD static inline int
+eat_byte_safe(SerdReader* const reader, const int byte)
{
(void)byte;
@@ -138,19 +144,19 @@ eat_byte_safe(SerdReader* reader, const int byte)
return byte;
}
-static inline int SERD_NODISCARD
-eat_byte_check(SerdReader* reader, const int byte)
+SERD_NODISCARD static inline int
+eat_byte_check(SerdReader* const reader, const int byte)
{
const int c = peek_byte(reader);
if (c != byte) {
- r_err(reader, SERD_ERR_BAD_SYNTAX, "expected '%c', not '%c'\n", byte, c);
+ r_err(reader, SERD_ERR_BAD_SYNTAX, "expected '%c'\n", byte);
return 0;
}
return eat_byte_safe(reader, byte);
}
static inline SerdStatus
-eat_string(SerdReader* reader, const char* str, unsigned n)
+eat_string(SerdReader* const reader, const char* const str, const unsigned n)
{
for (unsigned i = 0; i < n; ++i) {
if (!eat_byte_check(reader, ((const uint8_t*)str)[i])) {
@@ -161,9 +167,9 @@ eat_string(SerdReader* reader, const char* str, unsigned n)
}
static inline SerdStatus
-push_byte(SerdReader* reader, Ref ref, const int c)
+push_byte(SerdReader* const reader, const Ref ref, const int c)
{
- assert(c != EOF);
+ assert(c >= 0);
SERD_STACK_ASSERT_TOP(reader, ref);
uint8_t* const s = (uint8_t*)serd_stack_push(&reader->stack, 1);
@@ -180,7 +186,10 @@ push_byte(SerdReader* reader, Ref ref, const int c)
}
static inline void
-push_bytes(SerdReader* reader, Ref ref, const uint8_t* bytes, unsigned len)
+push_bytes(SerdReader* const reader,
+ const Ref ref,
+ const uint8_t* const bytes,
+ const unsigned len)
{
for (unsigned i = 0; i < len; ++i) {
push_byte(reader, ref, bytes[i]);
diff --git a/src/serd_config.h b/src/serd_config.h
index 150ffb81..d4165a20 100644
--- a/src/serd_config.h
+++ b/src/serd_config.h
@@ -36,7 +36,7 @@
#define SERD_SRC_SERD_CONFIG_H
// Define version unconditionally so a warning will catch a mismatch
-#define SERD_VERSION "0.32.3"
+#define SERD_VERSION "0.32.5"
#if !defined(SERD_NO_DEFAULT_CONFIG)
diff --git a/src/serd_internal.h b/src/serd_internal.h
index 388c12ec..ead6d796 100644
--- a/src/serd_internal.h
+++ b/src/serd_internal.h
@@ -4,7 +4,7 @@
#ifndef SERD_SRC_SERD_INTERNAL_H
#define SERD_SRC_SERD_INTERNAL_H
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <stdio.h>
@@ -20,7 +20,9 @@
/* Error reporting */
static inline void
-serd_error(SerdErrorSink error_sink, void* handle, const SerdError* e)
+serd_error(const SerdErrorSink error_sink,
+ void* const handle,
+ const SerdError* const e)
{
if (error_sink) {
error_sink(handle, e);
diff --git a/src/serdi.c b/src/serdi.c
index 9d0a8f44..c19bd212 100644
--- a/src/serdi.c
+++ b/src/serdi.c
@@ -4,7 +4,7 @@
#include "serd_config.h"
#include "string_utils.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#ifdef _WIN32
# ifdef _MSC_VER
@@ -85,20 +85,20 @@ print_usage(const char* const name, const bool error)
static const char* const description =
"Read and write RDF syntax.\n"
"Use - for INPUT to read from standard input.\n\n"
- " -a Write ASCII output.\n"
- " -b Write output in blocks for performance.\n"
- " -c PREFIX Chop PREFIX from matching blank node IDs.\n"
- " -e Eat input one character at a time.\n"
- " -f Fast and loose URI pass-through.\n"
- " -h Display this help and exit.\n"
- " -i SYNTAX Input syntax: turtle/ntriples/trig/nquads.\n"
- " -l Lax (non-strict) parsing.\n"
- " -o SYNTAX Output syntax: turtle/ntriples/nquads.\n"
- " -p PREFIX Add PREFIX to blank node IDs.\n"
- " -q Suppress all output except data.\n"
- " -r ROOT_URI Keep relative URIs within ROOT_URI.\n"
- " -s INPUT Parse INPUT as string (terminates options).\n"
- " -v Display version information and exit.\n";
+ " -a Write ASCII output\n"
+ " -b Write output in blocks for performance\n"
+ " -c PREFIX Chop PREFIX from matching blank node IDs\n"
+ " -e Eat input one character at a time\n"
+ " -f Fast and loose URI pass-through\n"
+ " -h Display this help and exit\n"
+ " -i SYNTAX Input syntax: turtle/ntriples/trig/nquads\n"
+ " -l Lax (non-strict) parsing\n"
+ " -o SYNTAX Output syntax: turtle/ntriples/nquads\n"
+ " -p PREFIX Add PREFIX to blank node IDs\n"
+ " -q Suppress all output except data\n"
+ " -r ROOT_URI Keep relative URIs within ROOT_URI\n"
+ " -s INPUT Parse INPUT as string (terminates options)\n"
+ " -v Display version information and exit\n";
FILE* const os = error ? stderr : stdout;
fprintf(os, "%s", error ? "\n" : "");
diff --git a/src/stack.h b/src/stack.h
index 388dd054..63c6ba01 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -20,11 +20,8 @@ typedef struct {
size_t size; ///< Conceptual size of stack in buf
} SerdStack;
-/** An offset to start the stack at. Note 0 is reserved for NULL. */
-#define SERD_STACK_BOTTOM sizeof(void*)
-
static inline SerdStack
-serd_stack_new(size_t size)
+serd_stack_new(const size_t size)
{
SerdStack stack;
stack.buf = (uint8_t*)calloc(size, 1);
@@ -34,13 +31,13 @@ serd_stack_new(size_t size)
}
static inline bool
-serd_stack_is_empty(const SerdStack* stack)
+serd_stack_is_empty(const SerdStack* const stack)
{
return stack->size <= SERD_STACK_BOTTOM;
}
static inline void
-serd_stack_free(SerdStack* stack)
+serd_stack_free(SerdStack* const stack)
{
free(stack->buf);
stack->buf = NULL;
@@ -49,7 +46,7 @@ serd_stack_free(SerdStack* stack)
}
static inline void*
-serd_stack_push(SerdStack* stack, size_t n_bytes)
+serd_stack_push(SerdStack* const stack, const size_t n_bytes)
{
const size_t new_size = stack->size + n_bytes;
if (stack->buf_size < new_size) {
@@ -64,20 +61,22 @@ serd_stack_push(SerdStack* stack, size_t n_bytes)
}
static inline void
-serd_stack_pop(SerdStack* stack, size_t n_bytes)
+serd_stack_pop(SerdStack* const stack, const size_t n_bytes)
{
assert(stack->size >= n_bytes);
stack->size -= n_bytes;
}
static inline void*
-serd_stack_push_aligned(SerdStack* stack, size_t n_bytes, size_t align)
+serd_stack_push_aligned(SerdStack* const stack,
+ const size_t n_bytes,
+ const size_t align)
{
// Push one byte to ensure space for a pad count
serd_stack_push(stack, 1);
// Push padding if necessary
- const size_t pad = align - stack->size % align;
+ const size_t pad = align - (stack->size % align);
serd_stack_push(stack, pad);
// Set top of stack to pad count so we can properly pop later
@@ -89,7 +88,7 @@ serd_stack_push_aligned(SerdStack* stack, size_t n_bytes, size_t align)
}
static inline void
-serd_stack_pop_aligned(SerdStack* stack, size_t n_bytes)
+serd_stack_pop_aligned(SerdStack* const stack, const size_t n_bytes)
{
// Pop requested space down to aligned location
serd_stack_pop(stack, n_bytes);
diff --git a/src/string.c b/src/string.c
index 936989c2..1a9d62d4 100644
--- a/src/string.c
+++ b/src/string.c
@@ -3,7 +3,7 @@
#include "string_utils.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <math.h>
diff --git a/src/string_utils.h b/src/string_utils.h
index 2ce90ac9..f4b27bcc 100644
--- a/src/string_utils.h
+++ b/src/string_utils.h
@@ -4,7 +4,7 @@
#ifndef SERD_SRC_STRING_UTILS_H
#define SERD_SRC_STRING_UTILS_H
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <stdbool.h>
#include <stddef.h>
@@ -51,13 +51,13 @@ is_xdigit(const int c)
static inline bool
is_space(const char c)
{
- return c == ' ' || (c >= '\t' && c <= '\r');
+ return c == ' ' || in_range(c, '\t', '\r');
}
static inline bool
is_print(const int c)
{
- return c >= 0x20 && c <= 0x7E;
+ return in_range(c, 0x20, 0x7E);
}
static inline bool
@@ -88,7 +88,7 @@ hex_digit_value(const uint8_t c)
static inline char
serd_to_upper(const char c)
{
- return (char)((c >= 'a' && c <= 'z') ? c - 32 : c);
+ return (char)(in_range(c, 'a', 'z') ? (c - 32) : c);
}
SERD_PURE_FUNC static inline int
@@ -107,7 +107,7 @@ serd_strcasecmp(const char* s1, const char* s2)
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : +1;
}
-static inline uint32_t
+static inline uint8_t
utf8_num_bytes(const uint8_t leading)
{
return ((leading & 0x80U) == 0x00U) ? 1U // Starts with `0'
@@ -119,18 +119,18 @@ utf8_num_bytes(const uint8_t leading)
/// Return the code point of a UTF-8 character with known length
static inline uint32_t
-parse_counted_utf8_char(const uint8_t* utf8, size_t size)
+parse_counted_utf8_char(const uint8_t* const utf8, const uint8_t size)
{
uint32_t c = utf8[0] & ((1U << (8U - size)) - 1U);
for (size_t i = 1; i < size; ++i) {
- c = (c << 6) | (utf8[i] & 0x3FU);
+ c = (c << 6U) | (utf8[i] & 0x3FU);
}
return c;
}
/// Parse a UTF-8 character, set *size to the length, and return the code point
static inline uint32_t
-parse_utf8_char(const uint8_t* utf8, size_t* size)
+parse_utf8_char(const uint8_t* const utf8, uint8_t* const size)
{
switch (*size = utf8_num_bytes(utf8[0])) {
case 1:
diff --git a/src/system.c b/src/system.c
index 072d2ed5..84916060 100644
--- a/src/system.c
+++ b/src/system.c
@@ -15,7 +15,6 @@
#endif
#include <errno.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/src/try.h b/src/try.h
index f49ae05f..bc1b8ec3 100644
--- a/src/try.h
+++ b/src/try.h
@@ -4,6 +4,8 @@
#ifndef SERD_SRC_TRY_H
#define SERD_SRC_TRY_H
+#include <serd/serd.h>
+
#define TRY(st, exp) \
do { \
if (((st) = (exp))) { \
diff --git a/src/uri.c b/src/uri.c
index 9856ce45..e9be9c3c 100644
--- a/src/uri.c
+++ b/src/uri.c
@@ -5,7 +5,7 @@
#include "uri_utils.h"
#include "warnings.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdbool.h>
@@ -15,7 +15,7 @@
#include <string.h>
const uint8_t*
-serd_uri_to_path(const uint8_t* uri)
+serd_uri_to_path(const uint8_t* const uri)
{
assert(uri);
@@ -344,7 +344,7 @@ serd_uri_resolve(const SerdURI* const r,
/** Write the path of `uri` starting at index `i` */
static size_t
-write_path_tail(SerdSink sink,
+write_path_tail(const SerdSink sink,
void* const stream,
const SerdURI* const uri,
const size_t i)
@@ -372,7 +372,7 @@ write_path_tail(SerdSink sink,
/** Write the path of `uri` relative to the path of `base`. */
static size_t
-write_rel_path(SerdSink sink,
+write_rel_path(const SerdSink sink,
void* const stream,
const SerdURI* const uri,
const SerdURI* const base)
@@ -413,7 +413,7 @@ write_rel_path(SerdSink sink,
}
static uint8_t
-serd_uri_path_starts_without_slash(const SerdURI* uri)
+serd_uri_path_starts_without_slash(const SerdURI* const uri)
{
return ((uri->path_base.len || uri->path.len) &&
((!uri->path_base.len || uri->path_base.buf[0] != '/') &&
@@ -425,7 +425,7 @@ size_t
serd_uri_serialise_relative(const SerdURI* const uri,
const SerdURI* const base,
const SerdURI* const root,
- SerdSink sink,
+ const SerdSink sink,
void* const stream)
{
assert(uri);
@@ -441,7 +441,7 @@ serd_uri_serialise_relative(const SerdURI* const uri,
SERD_DISABLE_NULL_WARNINGS
- if (!relative || (!len && base->query.buf)) {
+ if (!relative || (!len && base && base->query.buf)) {
if (uri->scheme.buf) {
len += sink(uri->scheme.buf, uri->scheme.len, stream);
len += sink(":", 1, stream);
@@ -481,7 +481,9 @@ serd_uri_serialise_relative(const SerdURI* const uri,
/// See http://tools.ietf.org/html/rfc3986#section-5.3
size_t
-serd_uri_serialise(const SerdURI* const uri, SerdSink sink, void* const stream)
+serd_uri_serialise(const SerdURI* const uri,
+ const SerdSink sink,
+ void* const stream)
{
assert(uri);
assert(sink);
diff --git a/src/uri_utils.h b/src/uri_utils.h
index 0d3bd74e..1e7adb82 100644
--- a/src/uri_utils.h
+++ b/src/uri_utils.h
@@ -4,10 +4,10 @@
#ifndef SERD_SRC_URI_UTILS_H
#define SERD_SRC_URI_UTILS_H
-#include "serd/serd.h"
-
#include "string_utils.h"
+#include <serd/serd.h>
+
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
@@ -18,20 +18,20 @@ typedef struct {
} SlashIndexes;
static inline bool
-chunk_equals(const SerdChunk* a, const SerdChunk* b)
+chunk_equals(const SerdChunk* const a, const SerdChunk* const b)
{
return a->len == b->len &&
!strncmp((const char*)a->buf, (const char*)b->buf, a->len);
}
static inline size_t
-uri_path_len(const SerdURI* uri)
+uri_path_len(const SerdURI* const uri)
{
return uri->path_base.len + uri->path.len;
}
static inline uint8_t
-uri_path_at(const SerdURI* uri, size_t i)
+uri_path_at(const SerdURI* const uri, const size_t i)
{
return (i < uri->path_base.len) ? uri->path_base.buf[i]
: uri->path.buf[i - uri->path_base.len];
@@ -46,7 +46,7 @@ uri_path_at(const SerdURI* uri, size_t i)
otherwise it may merely share some leading path components).
*/
static inline SERD_PURE_FUNC SlashIndexes
-uri_rooted_index(const SerdURI* uri, const SerdURI* root)
+uri_rooted_index(const SerdURI* const uri, const SerdURI* const root)
{
SlashIndexes indexes = {SIZE_MAX, SIZE_MAX};
@@ -84,14 +84,14 @@ uri_rooted_index(const SerdURI* uri, const SerdURI* root)
/** Return true iff `uri` shares path components with `root` */
static inline SERD_PURE_FUNC bool
-uri_is_related(const SerdURI* uri, const SerdURI* root)
+uri_is_related(const SerdURI* const uri, const SerdURI* const root)
{
return uri_rooted_index(uri, root).shared != SIZE_MAX;
}
/** Return true iff `uri` is within the base of `root` */
static inline SERD_PURE_FUNC bool
-uri_is_under(const SerdURI* uri, const SerdURI* root)
+uri_is_under(const SerdURI* const uri, const SerdURI* const root)
{
const SlashIndexes indexes = uri_rooted_index(uri, root);
return indexes.shared && indexes.shared != SIZE_MAX &&
diff --git a/src/writer.c b/src/writer.c
index e4ef5651..aa7fd980 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -1,4 +1,4 @@
-// Copyright 2011-2023 David Robillard <d@drobilla.net>
+// Copyright 2011-2025 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#include "attributes.h"
@@ -10,7 +10,7 @@
#include "uri_utils.h"
#include "warnings.h"
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <errno.h>
@@ -37,20 +37,12 @@ typedef enum {
typedef struct {
ContextType type;
+ bool comma_indented;
SerdNode graph;
SerdNode subject;
SerdNode predicate;
- bool predicates;
- bool comma_indented;
} WriteContext;
-static const WriteContext WRITE_CONTEXT_NULL = {CTX_NAMED,
- {0, 0, 0, 0, SERD_NOTHING},
- {0, 0, 0, 0, SERD_NOTHING},
- {0, 0, 0, 0, SERD_NOTHING},
- 0U,
- 0U};
-
typedef enum {
SEP_NONE, ///< Sentinel after "nothing"
SEP_NEWLINE, ///< Sentinel after a line end
@@ -143,7 +135,7 @@ write_node(SerdWriter* writer,
SerdStatementFlags flags);
SERD_NODISCARD static bool
-supports_abbrev(const SerdWriter* writer)
+supports_abbrev(const SerdWriter* const writer)
{
return writer->syntax == SERD_TURTLE || writer->syntax == SERD_TRIG;
}
@@ -162,7 +154,7 @@ free_context(WriteContext* const ctx)
SERD_LOG_FUNC(3, 4)
static SerdStatus
-w_err(SerdWriter* writer, SerdStatus st, const char* fmt, ...)
+w_err(SerdWriter* const writer, const SerdStatus st, const char* const fmt, ...)
{
/* TODO: This results in errors with no file information, which is not
helpful when re-serializing a file (particularly for "undefined
@@ -179,7 +171,7 @@ w_err(SerdWriter* writer, SerdStatus st, const char* fmt, ...)
}
static void
-copy_node(SerdNode* dst, const SerdNode* src)
+copy_node(SerdNode* const dst, const SerdNode* const src)
{
const size_t new_size = src->n_bytes + 1U;
uint8_t* const new_buf = (uint8_t*)realloc((char*)dst->buf, new_size);
@@ -205,12 +197,12 @@ push_context(SerdWriter* const writer,
*(WriteContext*)top = writer->context;
// Update the current context
- const WriteContext current = {type, graph, subject, predicate, 0U, 0U};
+ const WriteContext current = {type, false, graph, subject, predicate};
writer->context = current;
}
static void
-pop_context(SerdWriter* writer)
+pop_context(SerdWriter* const writer)
{
// Replace the current context with the top of the stack
free_context(&writer->context);
@@ -223,7 +215,7 @@ pop_context(SerdWriter* writer)
}
SERD_NODISCARD static size_t
-sink(const void* buf, size_t len, SerdWriter* writer)
+sink(const void* const buf, const size_t len, SerdWriter* const writer)
{
const size_t written = serd_byte_sink_write(buf, len, &writer->byte_sink);
if (written != len) {
@@ -238,8 +230,8 @@ sink(const void* buf, size_t len, SerdWriter* writer)
return written;
}
-SERD_NODISCARD static inline SerdStatus
-esink(const void* buf, size_t len, SerdWriter* writer)
+SERD_NODISCARD static SerdStatus
+esink(const void* const buf, const size_t len, SerdWriter* const writer)
{
return sink(buf, len, writer) == len ? SERD_SUCCESS : SERD_ERR_BAD_WRITE;
}
@@ -247,10 +239,10 @@ esink(const void* buf, size_t len, SerdWriter* writer)
// Write a single character, as an escape for single byte characters
// (Caller prints any single byte characters that don't need escaping)
static size_t
-write_character(SerdWriter* writer,
- const uint8_t* utf8,
- size_t* size,
- SerdStatus* st)
+write_character(SerdWriter* const writer,
+ const uint8_t* const utf8,
+ uint8_t* const size,
+ SerdStatus* const st)
{
char escape[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const uint32_t c = parse_utf8_char(utf8, size);
@@ -288,10 +280,10 @@ uri_must_escape(const uint8_t c)
}
static size_t
-write_uri(SerdWriter* writer,
- const uint8_t* utf8,
- size_t n_bytes,
- SerdStatus* st)
+write_uri(SerdWriter* const writer,
+ const uint8_t* const utf8,
+ const size_t n_bytes,
+ SerdStatus* const st)
{
size_t len = 0;
for (size_t i = 0; i < n_bytes;) {
@@ -315,14 +307,14 @@ write_uri(SerdWriter* writer,
}
// Write UTF-8 character
- size_t size = 0;
+ uint8_t size = 0U;
len += write_character(writer, utf8 + i, &size, st);
i += size;
if (*st && (writer->style & SERD_STYLE_STRICT)) {
break;
}
- if (size == 0) {
+ if (!size) {
// Corrupt input, write percent-encoded bytes and scan to next start
char escape[4] = {0, 0, 0, 0};
for (; i < n_bytes && (utf8[i] & 0x80); ++i) {
@@ -336,7 +328,9 @@ write_uri(SerdWriter* writer,
}
SERD_NODISCARD static SerdStatus
-ewrite_uri(SerdWriter* writer, const uint8_t* utf8, size_t n_bytes)
+ewrite_uri(SerdWriter* const writer,
+ const uint8_t* const utf8,
+ const size_t n_bytes)
{
SerdStatus st = SERD_SUCCESS;
write_uri(writer, utf8, n_bytes, &st);
@@ -347,7 +341,7 @@ ewrite_uri(SerdWriter* writer, const uint8_t* utf8, size_t n_bytes)
}
SERD_NODISCARD static SerdStatus
-write_uri_from_node(SerdWriter* writer, const SerdNode* node)
+write_uri_from_node(SerdWriter* const writer, const SerdNode* const node)
{
return ewrite_uri(writer, node->buf, node->n_bytes);
}
@@ -369,7 +363,9 @@ lname_must_escape(const uint8_t c)
}
SERD_NODISCARD static SerdStatus
-write_lname(SerdWriter* writer, const uint8_t* utf8, size_t n_bytes)
+write_lname(SerdWriter* const writer,
+ const uint8_t* const utf8,
+ const size_t n_bytes)
{
SerdStatus st = SERD_SUCCESS;
for (size_t i = 0; i < n_bytes; ++i) {
@@ -395,10 +391,10 @@ write_lname(SerdWriter* writer, const uint8_t* utf8, size_t n_bytes)
}
SERD_NODISCARD static SerdStatus
-write_text(SerdWriter* writer,
- TextContext ctx,
- const uint8_t* utf8,
- size_t n_bytes)
+write_text(SerdWriter* const writer,
+ const TextContext ctx,
+ const uint8_t* const utf8,
+ const size_t n_bytes)
{
size_t n_consecutive_quotes = 0;
SerdStatus st = SERD_SUCCESS;
@@ -484,19 +480,19 @@ write_text(SerdWriter* writer,
}
// Write UTF-8 character
- size_t size = 0;
+ uint8_t size = 0U;
write_character(writer, utf8 + i - 1, &size, &st);
if (st && (writer->style & SERD_STYLE_STRICT)) {
return st;
}
- if (size == 0) {
+ if (size > 0U) {
+ i += size - 1U;
+ } else {
// Corrupt input, write replacement character and scan to the next start
st = esink(replacement_char, sizeof(replacement_char), writer);
- for (; i < n_bytes && (utf8[i] & 0x80); ++i) {
+ for (; i < n_bytes && (utf8[i] & 0x80U); ++i) {
}
- } else {
- i += size - 1;
}
}
@@ -509,7 +505,7 @@ typedef struct {
} UriSinkContext;
SERD_NODISCARD static size_t
-uri_sink(const void* buf, size_t len, void* stream)
+uri_sink(const void* const buf, const size_t len, void* const stream)
{
UriSinkContext* const context = (UriSinkContext*)stream;
SerdWriter* const writer = context->writer;
@@ -518,7 +514,7 @@ uri_sink(const void* buf, size_t len, void* stream)
}
SERD_NODISCARD static SerdStatus
-write_newline(SerdWriter* writer)
+write_newline(SerdWriter* const writer)
{
SerdStatus st = SERD_SUCCESS;
@@ -531,7 +527,7 @@ write_newline(SerdWriter* writer)
}
SERD_NODISCARD static SerdStatus
-write_sep(SerdWriter* writer, const Sep sep)
+write_sep(SerdWriter* const writer, const Sep sep)
{
SerdStatus st = SERD_SUCCESS;
const SepRule* const rule = &rules[sep];
@@ -546,10 +542,13 @@ write_sep(SerdWriter* writer, const Sep sep)
: 0);
}
- // If this is the first comma, bump the increment for the following object
+ // Adjust indentation for object comma if necessary
if (sep == SEP_END_O && !writer->context.comma_indented) {
++writer->indent;
writer->context.comma_indented = true;
+ } else if (sep == SEP_END_P && writer->context.comma_indented) {
+ --writer->indent;
+ writer->context.comma_indented = false;
}
// Write newline or space before separator if necessary
@@ -575,7 +574,6 @@ write_sep(SerdWriter* writer, const Sep sep)
// Reset context and write a blank line after ends of subjects
if (sep == SEP_END_S) {
writer->indent = writer->context.graph.type ? 1 : 0;
- writer->context.predicates = false;
writer->context.comma_indented = false;
TRY(st, esink("\n", 1, writer));
}
@@ -585,7 +583,7 @@ write_sep(SerdWriter* writer, const Sep sep)
}
static void
-free_anon_stack(SerdWriter* writer)
+free_anon_stack(SerdWriter* const writer)
{
while (!serd_stack_is_empty(&writer->anon_stack)) {
pop_context(writer);
@@ -593,7 +591,7 @@ free_anon_stack(SerdWriter* writer)
}
static SerdStatus
-reset_context(SerdWriter* writer, const unsigned flags)
+reset_context(SerdWriter* const writer, const unsigned flags)
{
free_anon_stack(writer);
@@ -608,7 +606,6 @@ reset_context(SerdWriter* writer, const unsigned flags)
writer->context.type = CTX_NAMED;
writer->context.subject.type = SERD_NOTHING;
writer->context.predicate.type = SERD_NOTHING;
- writer->context.predicates = false;
writer->context.comma_indented = false;
return SERD_SUCCESS;
}
@@ -639,11 +636,11 @@ get_xsd_name(const SerdEnv* const env, const SerdNode* const datatype)
}
SERD_NODISCARD static SerdStatus
-write_literal(SerdWriter* writer,
- const SerdNode* node,
- const SerdNode* datatype,
- const SerdNode* lang,
- SerdStatementFlags flags)
+write_literal(SerdWriter* const writer,
+ const SerdNode* const node,
+ const SerdNode* const datatype,
+ const SerdNode* const lang,
+ const SerdStatementFlags flags)
{
SerdStatus st = SERD_SUCCESS;
@@ -679,7 +676,7 @@ write_literal(SerdWriter* writer,
// Return true iff `buf` is a valid prefixed name prefix or suffix
static bool
-is_name(const uint8_t* buf, const size_t len)
+is_name(const uint8_t* const buf, const size_t len)
{
// TODO: This is more strict than it should be
for (size_t i = 0; i < len; ++i) {
@@ -692,9 +689,9 @@ is_name(const uint8_t* buf, const size_t len)
}
SERD_NODISCARD static SerdStatus
-write_uri_node(SerdWriter* const writer,
- const SerdNode* node,
- const Field field)
+write_uri_node(SerdWriter* const writer,
+ const SerdNode* const node,
+ const Field field)
{
SerdStatus st = SERD_SUCCESS;
SerdNode prefix = SERD_NODE_NULL;
@@ -741,8 +738,8 @@ write_uri_node(SerdWriter* const writer,
serd_uri_parse(node->buf, &uri);
SERD_RESTORE_WARNINGS
serd_uri_resolve(&uri, &in_base_uri, &abs_uri);
- bool rooted = uri_is_under(&writer->base_uri, &writer->root_uri);
- SerdURI* root = rooted ? &writer->root_uri : &writer->base_uri;
+ const bool rooted = uri_is_under(&writer->base_uri, &writer->root_uri);
+ const SerdURI* root = rooted ? &writer->root_uri : &writer->base_uri;
UriSinkContext ctx = {writer, SERD_SUCCESS};
if (!uri_is_under(&abs_uri, root) || writer->syntax == SERD_NTRIPLES ||
writer->syntax == SERD_NQUADS) {
@@ -789,7 +786,7 @@ write_curie(SerdWriter* const writer, const SerdNode* const node)
SERD_NODISCARD static SerdStatus
write_blank(SerdWriter* const writer,
- const SerdNode* node,
+ const SerdNode* const node,
const Field field,
const SerdStatementFlags flags)
{
@@ -828,12 +825,12 @@ write_blank(SerdWriter* const writer,
}
SERD_NODISCARD static SerdStatus
-write_node(SerdWriter* writer,
- const SerdNode* node,
- const SerdNode* datatype,
- const SerdNode* lang,
- Field field,
- SerdStatementFlags flags)
+write_node(SerdWriter* const writer,
+ const SerdNode* const node,
+ const SerdNode* const datatype,
+ const SerdNode* const lang,
+ const Field field,
+ const SerdStatementFlags flags)
{
return (node->type == SERD_LITERAL)
? write_literal(writer, node, datatype, lang, flags)
@@ -844,13 +841,15 @@ write_node(SerdWriter* writer,
}
static bool
-is_resource(const SerdNode* node)
+is_resource(const SerdNode* const node)
{
return node->buf && node->type > SERD_LITERAL;
}
SERD_NODISCARD static SerdStatus
-write_pred(SerdWriter* writer, SerdStatementFlags flags, const SerdNode* pred)
+write_pred(SerdWriter* const writer,
+ const SerdStatementFlags flags,
+ const SerdNode* const pred)
{
SerdStatus st = SERD_SUCCESS;
@@ -858,18 +857,17 @@ write_pred(SerdWriter* writer, SerdStatementFlags flags, const SerdNode* pred)
TRY(st, write_sep(writer, SEP_P_O));
copy_node(&writer->context.predicate, pred);
- writer->context.predicates = true;
writer->context.comma_indented = false;
return st;
}
SERD_NODISCARD static SerdStatus
-write_list_next(SerdWriter* writer,
- SerdStatementFlags flags,
- const SerdNode* predicate,
- const SerdNode* object,
- const SerdNode* datatype,
- const SerdNode* lang)
+write_list_next(SerdWriter* const writer,
+ const SerdStatementFlags flags,
+ const SerdNode* const predicate,
+ const SerdNode* const object,
+ const SerdNode* const datatype,
+ const SerdNode* const lang)
{
SerdStatus st = SERD_SUCCESS;
@@ -888,7 +886,7 @@ write_list_next(SerdWriter* writer,
}
SERD_NODISCARD static SerdStatus
-terminate_context(SerdWriter* writer)
+terminate_context(SerdWriter* const writer)
{
SerdStatus st = SERD_SUCCESS;
@@ -904,14 +902,14 @@ terminate_context(SerdWriter* writer)
}
SerdStatus
-serd_writer_write_statement(SerdWriter* writer,
- SerdStatementFlags flags,
- const SerdNode* graph,
- const SerdNode* subject,
- const SerdNode* predicate,
- const SerdNode* object,
- const SerdNode* datatype,
- const SerdNode* lang)
+serd_writer_write_statement(SerdWriter* const writer,
+ SerdStatementFlags flags,
+ const SerdNode* const graph,
+ const SerdNode* const subject,
+ const SerdNode* const predicate,
+ const SerdNode* const object,
+ const SerdNode* const datatype,
+ const SerdNode* const lang)
{
assert(writer);
assert(subject);
@@ -920,10 +918,6 @@ serd_writer_write_statement(SerdWriter* writer,
SerdStatus st = SERD_SUCCESS;
- if (!is_resource(subject) || !is_resource(predicate) || !object->buf) {
- return SERD_ERR_BAD_ARG;
- }
-
if ((flags & SERD_LIST_O_BEGIN) &&
!strcmp((const char*)object->buf, NS_RDF "nil")) {
/* Tolerate LIST_O_BEGIN for "()" objects, even though it doesn't make
@@ -932,6 +926,17 @@ serd_writer_write_statement(SerdWriter* writer,
flags &= (SerdStatementFlags)~SERD_LIST_O_BEGIN;
}
+ // Refuse to write incoherent statements
+ if (!is_resource(subject) || !is_resource(predicate) ||
+ object->type == SERD_NOTHING || !object->buf ||
+ (datatype && datatype->buf && lang && lang->buf) ||
+ ((flags & SERD_ANON_S_BEGIN) && (flags & SERD_LIST_S_BEGIN)) ||
+ ((flags & SERD_EMPTY_S) && (flags & SERD_LIST_S_BEGIN)) ||
+ ((flags & SERD_ANON_O_BEGIN) && (flags & SERD_LIST_O_BEGIN)) ||
+ ((flags & SERD_EMPTY_O) && (flags & SERD_LIST_O_BEGIN))) {
+ return SERD_ERR_BAD_ARG;
+ }
+
// Simple case: write a line of NTriples or NQuads
if (writer->syntax == SERD_NTRIPLES || writer->syntax == SERD_NQUADS) {
TRY(st, write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags));
@@ -947,23 +952,21 @@ serd_writer_write_statement(SerdWriter* writer,
return SERD_SUCCESS;
}
- SERD_DISABLE_NULL_WARNINGS
-
// Separate graphs if necessary
- if ((graph && !serd_node_equals(graph, &writer->context.graph)) ||
- (!graph && writer->context.graph.type)) {
+ const SerdNode* const out_graph = writer->syntax == SERD_TRIG ? graph : NULL;
+ if ((out_graph && !serd_node_equals(out_graph, &writer->context.graph)) ||
+ (!out_graph && writer->context.graph.type)) {
TRY(st, terminate_context(writer));
reset_context(writer, RESET_GRAPH | RESET_INDENT);
TRY(st, write_newline(writer));
- if (graph) {
- TRY(st, write_node(writer, graph, datatype, lang, FIELD_GRAPH, flags));
+ if (out_graph) {
+ TRY(st,
+ write_node(writer, out_graph, datatype, lang, FIELD_GRAPH, flags));
TRY(st, write_sep(writer, SEP_GRAPH_BEGIN));
- copy_node(&writer->context.graph, graph);
+ copy_node(&writer->context.graph, out_graph);
}
}
- SERD_RESTORE_WARNINGS
-
if ((flags & SERD_LIST_CONT)) {
// Continue a list
if (!strcmp((const char*)predicate->buf, NS_RDF "first") &&
@@ -997,11 +1000,6 @@ serd_writer_write_statement(SerdWriter* writer,
} else {
// Elide S (write P and O)
- if (writer->context.comma_indented) {
- --writer->indent;
- writer->context.comma_indented = false;
- }
-
const bool first = !writer->context.predicate.type;
TRY(st, write_sep(writer, first ? SEP_S_P : SEP_END_P));
TRY(st, write_pred(writer, flags, predicate));
@@ -1046,7 +1044,7 @@ serd_writer_write_statement(SerdWriter* writer,
const bool is_list = (flags & SERD_LIST_S_BEGIN);
push_context(writer,
is_list ? CTX_LIST : CTX_BLANK,
- serd_node_copy(graph),
+ serd_node_copy(out_graph),
serd_node_copy(subject),
is_list ? SERD_NODE_NULL : serd_node_copy(predicate));
}
@@ -1055,7 +1053,7 @@ serd_writer_write_statement(SerdWriter* writer,
// Push context for anonymous or list object if necessary
push_context(writer,
(flags & SERD_LIST_O_BEGIN) ? CTX_LIST : CTX_BLANK,
- serd_node_copy(graph),
+ serd_node_copy(out_graph),
serd_node_copy(object),
SERD_NODE_NULL);
}
@@ -1064,7 +1062,7 @@ serd_writer_write_statement(SerdWriter* writer,
}
SerdStatus
-serd_writer_end_anon(SerdWriter* writer, const SerdNode* node)
+serd_writer_end_anon(SerdWriter* const writer, const SerdNode* const node)
{
assert(writer);
@@ -1083,19 +1081,16 @@ serd_writer_end_anon(SerdWriter* writer, const SerdNode* node)
TRY(st, write_sep(writer, SEP_ANON_END));
pop_context(writer);
- SERD_DISABLE_NULL_WARNINGS
-
if (node && serd_node_equals(node, &writer->context.subject)) {
// Now-finished anonymous node is the new subject with no other context
writer->context.predicate.type = SERD_NOTHING;
}
- SERD_RESTORE_WARNINGS
return st;
}
SerdStatus
-serd_writer_finish(SerdWriter* writer)
+serd_writer_finish(SerdWriter* const writer)
{
assert(writer);
@@ -1107,18 +1102,17 @@ serd_writer_finish(SerdWriter* writer)
}
SerdWriter*
-serd_writer_new(SerdSyntax syntax,
- SerdStyle style,
- SerdEnv* env,
- const SerdURI* base_uri,
- SerdSink ssink,
- void* stream)
+serd_writer_new(const SerdSyntax syntax,
+ const SerdStyle style,
+ SerdEnv* const env,
+ const SerdURI* const base_uri,
+ SerdSink ssink,
+ void* const stream)
{
assert(env);
assert(ssink);
- const WriteContext context = WRITE_CONTEXT_NULL;
- SerdWriter* writer = (SerdWriter*)calloc(1, sizeof(SerdWriter));
+ SerdWriter* writer = (SerdWriter*)calloc(1, sizeof(SerdWriter));
writer->syntax = syntax;
writer->style = style;
@@ -1127,7 +1121,6 @@ serd_writer_new(SerdSyntax syntax,
writer->root_uri = SERD_URI_NULL;
writer->base_uri = base_uri ? *base_uri : SERD_URI_NULL;
writer->anon_stack = serd_stack_new(SERD_PAGE_SIZE);
- writer->context = context;
writer->byte_sink = serd_byte_sink_new(
ssink, stream, (style & SERD_STYLE_BULK) ? SERD_PAGE_SIZE : 1);
@@ -1135,9 +1128,9 @@ serd_writer_new(SerdSyntax syntax,
}
void
-serd_writer_set_error_sink(SerdWriter* writer,
- SerdErrorSink error_sink,
- void* error_handle)
+serd_writer_set_error_sink(SerdWriter* const writer,
+ const SerdErrorSink error_sink,
+ void* const error_handle)
{
assert(writer);
assert(error_sink);
@@ -1146,7 +1139,8 @@ serd_writer_set_error_sink(SerdWriter* writer,
}
void
-serd_writer_chop_blank_prefix(SerdWriter* writer, const uint8_t* prefix)
+serd_writer_chop_blank_prefix(SerdWriter* const writer,
+ const uint8_t* const prefix)
{
assert(writer);
@@ -1163,7 +1157,7 @@ serd_writer_chop_blank_prefix(SerdWriter* writer, const uint8_t* prefix)
}
SerdStatus
-serd_writer_set_base_uri(SerdWriter* writer, const SerdNode* uri)
+serd_writer_set_base_uri(SerdWriter* const writer, const SerdNode* const uri)
{
assert(writer);
@@ -1185,7 +1179,7 @@ serd_writer_set_base_uri(SerdWriter* writer, const SerdNode* uri)
}
SerdStatus
-serd_writer_set_root_uri(SerdWriter* writer, const SerdNode* uri)
+serd_writer_set_root_uri(SerdWriter* const writer, const SerdNode* const uri)
{
assert(writer);
@@ -1205,9 +1199,9 @@ serd_writer_set_root_uri(SerdWriter* writer, const SerdNode* uri)
}
SerdStatus
-serd_writer_set_prefix(SerdWriter* writer,
- const SerdNode* name,
- const SerdNode* uri)
+serd_writer_set_prefix(SerdWriter* const writer,
+ const SerdNode* const name,
+ const SerdNode* const uri)
{
assert(writer);
assert(name);
@@ -1218,7 +1212,12 @@ serd_writer_set_prefix(SerdWriter* writer,
TRY(st, serd_env_set_prefix(writer->env, name, uri));
if (writer->syntax == SERD_TURTLE || writer->syntax == SERD_TRIG) {
+ const bool had_subject = writer->context.subject.type;
TRY(st, terminate_context(writer));
+ if (had_subject) {
+ TRY(st, esink("\n", 1, writer));
+ }
+
TRY(st, esink("@prefix ", 8, writer));
TRY(st, esink(name->buf, name->n_bytes, writer));
TRY(st, esink(": <", 3, writer));
@@ -1231,15 +1230,13 @@ serd_writer_set_prefix(SerdWriter* writer,
}
void
-serd_writer_free(SerdWriter* writer)
+serd_writer_free(SerdWriter* const writer)
{
if (!writer) {
return;
}
- SERD_DISABLE_NULL_WARNINGS
serd_writer_finish(writer);
- SERD_RESTORE_WARNINGS
free_context(&writer->context);
free_anon_stack(writer);
serd_stack_free(&writer->anon_stack);
@@ -1250,14 +1247,14 @@ serd_writer_free(SerdWriter* writer)
}
SerdEnv*
-serd_writer_get_env(SerdWriter* writer)
+serd_writer_get_env(SerdWriter* const writer)
{
assert(writer);
return writer->env;
}
size_t
-serd_file_sink(const void* buf, size_t len, void* stream)
+serd_file_sink(const void* const buf, const size_t len, void* const stream)
{
assert(buf);
assert(stream);
@@ -1265,7 +1262,7 @@ serd_file_sink(const void* buf, size_t len, void* stream)
}
size_t
-serd_chunk_sink(const void* buf, size_t len, void* stream)
+serd_chunk_sink(const void* const buf, const size_t len, void* const stream)
{
assert(buf);
assert(stream);
@@ -1281,7 +1278,7 @@ serd_chunk_sink(const void* buf, size_t len, void* stream)
}
uint8_t*
-serd_chunk_sink_finish(SerdChunk* stream)
+serd_chunk_sink_finish(SerdChunk* const stream)
{
assert(stream);
serd_chunk_sink("", 1, stream);
diff --git a/subprojects/sphinxygen.wrap b/subprojects/sphinxygen.wrap
index 9707d1a6..898ee526 100644
--- a/subprojects/sphinxygen.wrap
+++ b/subprojects/sphinxygen.wrap
@@ -1,14 +1,14 @@
-# Copyright 2022-2023 David Robillard <d@drobilla.net>
+# Copyright 2022-2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC
[wrap-file]
-directory = sphinxygen-1.0.4
-source_url = https://download.drobilla.net/sphinxygen-1.0.4.tar.gz
-source_filename = sphinxygen-1.0.4.tar.gz
-source_hash = 12fa9f18ed9fca608f272520072257ba61fd9eff25613f86d83d4fce14fc01f5
+directory = sphinxygen-1.0.10
+source_url = https://download.drobilla.net/sphinxygen-1.0.10.tar.gz
+source_filename = sphinxygen-1.0.10.tar.gz
+source_hash = 4b5eeb1ff47d43ee1ddae9327c2b49bab1e49363538c569bc544705284f3695d
# [wrap-git]
# url = https://gitlab.com/drobilla/sphinxygen.git
# push-url = ssh://git@gitlab.com:drobilla/sphinxygen.git
-# revision = v1.0.4
+# revision = v1.0.10
# depth = 1
diff --git a/test/.clang-tidy b/test/.clang-tidy
index 457abcaf..80737308 100644
--- a/test/.clang-tidy
+++ b/test/.clang-tidy
@@ -7,10 +7,11 @@ Checks: >
-bugprone-assert-side-effect,
-bugprone-easily-swappable-parameters,
-cert-err33-c,
- -clang-analyzer-nullability.NullableDereferenced,
+ -clang-analyzer-optin.core.EnumCastOutOfRange,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
-concurrency-mt-unsafe,
-hicpp-signed-bitwise,
+ -readability-redundant-casting,
CheckOptions:
- key: readability-function-cognitive-complexity.IgnoreMacros
value: 'true'
diff --git a/test/extra/bad/bad-lang-start-delete.nt b/test/extra/bad/bad-lang-start-delete.nt
new file mode 100644
index 00000000..122625e0
--- /dev/null
+++ b/test/extra/bad/bad-lang-start-delete.nt
@@ -0,0 +1 @@
+<http://example.org/s> <http://example.org/p> "hello"@bad .
diff --git a/test/extra/bad/bad-lang-start-space.nt b/test/extra/bad/bad-lang-start-space.nt
new file mode 100644
index 00000000..ff5c12ab
--- /dev/null
+++ b/test/extra/bad/bad-lang-start-space.nt
@@ -0,0 +1 @@
+<http://example.org/s> <http://example.org/p> "hello"@ bad .
diff --git a/test/extra/bad/bad-lang-start-tab.nt b/test/extra/bad/bad-lang-start-tab.nt
new file mode 100644
index 00000000..ad005c6d
--- /dev/null
+++ b/test/extra/bad/bad-lang-start-tab.nt
@@ -0,0 +1 @@
+<http://example.org/s> <http://example.org/p> "hello"@ bad .
diff --git a/test/extra/bad/bad-lang-start-wide.nt b/test/extra/bad/bad-lang-start-wide.nt
new file mode 100644
index 00000000..04ca4899
--- /dev/null
+++ b/test/extra/bad/bad-lang-start-wide.nt
@@ -0,0 +1 @@
+<http://example.org/s> <http://example.org/p> "hello"@βad .
diff --git a/test/extra/bad/bad-lang.ttl b/test/extra/bad/bad-lang.ttl
index 01e04328..b51df89c 100644
--- a/test/extra/bad/bad-lang.ttl
+++ b/test/extra/bad/bad-lang.ttl
@@ -1 +1 @@
-<> <http://example.org/pred> "hello"@\bad . \ No newline at end of file
+<> <http://example.org/pred> "hello"@b\ad .
diff --git a/test/extra/bad/bad-predicate-in-blank.ttl b/test/extra/bad/bad-predicate-in-blank.ttl
new file mode 100644
index 00000000..e4200f15
--- /dev/null
+++ b/test/extra/bad/bad-predicate-in-blank.ttl
@@ -0,0 +1 @@
+<a> <b> [ ERRORHERE <c> ]
diff --git a/test/extra/bad/bad-uri-scheme-start-apostrophe.nt b/test/extra/bad/bad-uri-scheme-start-apostrophe.nt
new file mode 100644
index 00000000..265b46d1
--- /dev/null
+++ b/test/extra/bad/bad-uri-scheme-start-apostrophe.nt
@@ -0,0 +1 @@
+<http://example.org/s> <http://example.org/p> <'http://example.org/o> .
diff --git a/test/extra/bad/bad-uri-scheme-start-delete.nt b/test/extra/bad/bad-uri-scheme-start-delete.nt
new file mode 100644
index 00000000..70b4962a
--- /dev/null
+++ b/test/extra/bad/bad-uri-scheme-start-delete.nt
@@ -0,0 +1 @@
+<http://example.org/s> <http://example.org/p> <http://example.org/o> .
diff --git a/test/extra/bad/bad-uri-scheme-start-space.nt b/test/extra/bad/bad-uri-scheme-start-space.nt
new file mode 100644
index 00000000..d396d6dd
--- /dev/null
+++ b/test/extra/bad/bad-uri-scheme-start-space.nt
@@ -0,0 +1 @@
+<http://example.org/s> <http://example.org/p> < http://example.org/o> .
diff --git a/test/extra/bad/bad-uri-scheme-start-tab.nt b/test/extra/bad/bad-uri-scheme-start-tab.nt
new file mode 100644
index 00000000..458a5743
--- /dev/null
+++ b/test/extra/bad/bad-uri-scheme-start-tab.nt
@@ -0,0 +1 @@
+<http://example.org/s> <http://example.org/p> < http://example.org/o> .
diff --git a/test/extra/bad/bad-uri-scheme-start-wide.nt b/test/extra/bad/bad-uri-scheme-start-wide.nt
new file mode 100644
index 00000000..7ddfff60
--- /dev/null
+++ b/test/extra/bad/bad-uri-scheme-start-wide.nt
@@ -0,0 +1 @@
+<http://example.org/s> <http://example.org/p> <σhttp://example.org/o> .
diff --git a/test/extra/bad/bad-uri-scheme-start.nt b/test/extra/bad/bad-uri-scheme-start.nt
deleted file mode 100644
index cd3fd70f..00000000
--- a/test/extra/bad/bad-uri-scheme-start.nt
+++ /dev/null
@@ -1 +0,0 @@
-<2http://example.org/s> <http://example.org/p> <http://example.org/o> .
diff --git a/test/extra/bad/manifest.ttl b/test/extra/bad/manifest.ttl
index 64dbf05f..acadde28 100644
--- a/test/extra/bad/manifest.ttl
+++ b/test/extra/bad/manifest.ttl
@@ -28,24 +28,6 @@
<#bad-dot-after-subject>
<#bad-dot-in-collection>
<#bad-empty-blank-predicate>
- <#bad-nt-eof-after-blank>
- <#bad-nt-eof-after-lang>
- <#bad-nt-eof-after-lang-hyphen>
- <#bad-nt-eof-after-lang-subtag>
- <#bad-nt-eof-after-object>
- <#bad-nt-eof-after-predicate>
- <#bad-nt-eof-after-string>
- <#bad-nt-eof-after-string-escape>
- <#bad-nt-eof-after-subject>
- <#bad-nt-eof-after-underscore>
- <#bad-nt-eof-before-blank>
- <#bad-nt-eof-before-iri>
- <#bad-nt-eof-before-lang>
- <#bad-nt-eof-before-string>
- <#bad-nt-eof-before-string-escape>
- <#bad-nt-eof-in-iri-path>
- <#bad-nt-eof-in-iri-scheme>
- <#bad-nt-eof-in-string>
<#bad-nt-syntax-blank-u00F7.nt>
<#bad-nt-syntax-blank-u037E.nt>
<#bad-nt-syntax-blank-u200B.nt>
@@ -65,24 +47,6 @@
<#bad-nt-syntax-uri-grave>
<#bad-nt-syntax-uri-less-than>
<#bad-nt-syntax-uri-opening-brace>
- <#bad-eof-after-quotes>
- <#bad-eof-at-string-start>
- <#bad-eof-in-blank>
- <#bad-eof-in-escape>
- <#bad-eof-in-lang-suffix>
- <#bad-eof-in-lang>
- <#bad-eof-in-list>
- <#bad-eof-in-long-string>
- <#bad-eof-in-object-list>
- <#bad-eof-in-object-list2>
- <#bad-eof-in-predicate-list>
- <#bad-eof-in-string>
- <#bad-eof-in-text-character>
- <#bad-eof-in-triple-quote>
- <#bad-eof-in-uri>
- <#bad-eof-in-uri-character>
- <#bad-eof-in-uri-scheme>
- <#bad-eof-in-utf8-character>
<#bad-equivalence>
<#bad-escape>
<#bad-ext-namedblank-op>
@@ -99,6 +63,10 @@
<#bad-is-of-keywords>
<#bad-keywords>
<#bad-lang>
+ <#bad-lang-start-delete>
+ <#bad-lang-start-space>
+ <#bad-lang-start-tab>
+ <#bad-lang-start-wide>
<#bad-list>
<#bad-list-close-object>
<#bad-list2>
@@ -116,6 +84,7 @@
<#bad-object2>
<#bad-paths>
<#bad-pn-escape>
+ <#bad-predicate-in-blank>
<#bad-prefix>
<#bad-prefix-dot>
<#bad-prefix-missing-colon>
@@ -128,7 +97,10 @@
<#bad-true-subject>
<#bad-uri-escape>
<#bad-uri-scheme>
- <#bad-uri-scheme-start>
+ <#bad-uri-scheme-start-delete>
+ <#bad-uri-scheme-start-space>
+ <#bad-uri-scheme-start-tab>
+ <#bad-uri-scheme-start-wide>
<#bad-uri-truncated>
<#bad-verb>
) .
@@ -243,96 +215,6 @@
mf:action <bad-empty-blank-predicate.ttl> ;
mf:name "bad-empty-blank-predicate" .
-<#bad-nt-eof-after-blank>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-blank.nt> ;
- mf:name "bad-nt-eof-after-blank" .
-
-<#bad-nt-eof-after-lang>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-lang.nt> ;
- mf:name "bad-nt-eof-after-lang" .
-
-<#bad-nt-eof-after-lang-hyphen>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-lang-hyphen.nt> ;
- mf:name "bad-nt-eof-after-lang-hyphen" .
-
-<#bad-nt-eof-after-lang-subtag>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-lang-subtag.nt> ;
- mf:name "bad-nt-eof-after-lang-subtag" .
-
-<#bad-nt-eof-after-object>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-object.nt> ;
- mf:name "bad-nt-eof-after-object" .
-
-<#bad-nt-eof-after-predicate>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-predicate.nt> ;
- mf:name "bad-nt-eof-after-predicate" .
-
-<#bad-nt-eof-after-string>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-string.nt> ;
- mf:name "bad-nt-eof-after-string" .
-
-<#bad-nt-eof-after-string-escape>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-string-escape.nt> ;
- mf:name "bad-nt-eof-after-string-escape" .
-
-<#bad-nt-eof-after-subject>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-subject.nt> ;
- mf:name "bad-nt-eof-after-subject" .
-
-<#bad-nt-eof-after-underscore>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-after-underscore.nt> ;
- mf:name "bad-nt-eof-after-underscore" .
-
-<#bad-nt-eof-before-blank>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-before-blank.nt> ;
- mf:name "bad-nt-eof-before-blank" .
-
-<#bad-nt-eof-before-iri>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-before-iri.nt> ;
- mf:name "bad-nt-eof-before-iri" .
-
-<#bad-nt-eof-before-lang>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-before-lang.nt> ;
- mf:name "bad-nt-eof-before-lang" .
-
-<#bad-nt-eof-before-string>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-before-string.nt> ;
- mf:name "bad-nt-eof-before-string" .
-
-<#bad-nt-eof-before-string-escape>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-before-string-escape.nt> ;
- mf:name "bad-nt-eof-before-string-escape" .
-
-<#bad-nt-eof-in-iri-path>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-in-iri-path.nt> ;
- mf:name "bad-nt-eof-in-iri-path" .
-
-<#bad-nt-eof-in-iri-scheme>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-in-iri-scheme.nt> ;
- mf:name "bad-nt-eof-in-iri-scheme" .
-
-<#bad-nt-eof-in-string>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-nt-eof-in-string.nt> ;
- mf:name "bad-nt-eof-in-string" .
-
<#bad-nt-syntax-blank-u00F7>
a rdft:TestNTriplesNegativeSyntax ;
mf:action <bad-nt-syntax-blank-u00F7.nt> ;
@@ -428,96 +310,6 @@
mf:action <bad-nt-syntax-uri-opening-brace.nt> ;
mf:name "bad-nt-syntax-uri-opening-brace" .
-<#bad-eof-after-quotes>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-after-quotes.ttl> ;
- mf:name "bad-eof-after-quotes" .
-
-<#bad-eof-at-string-start>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-at-string-start.ttl> ;
- mf:name "bad-eof-at-string-start" .
-
-<#bad-eof-in-blank>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-blank.ttl> ;
- mf:name "bad-eof-in-blank" .
-
-<#bad-eof-in-escape>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-escape.ttl> ;
- mf:name "bad-eof-in-escape" .
-
-<#bad-eof-in-lang>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-lang.ttl> ;
- mf:name "bad-eof-in-lang" .
-
-<#bad-eof-in-lang-suffix>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-lang-suffix.ttl> ;
- mf:name "bad-eof-in-lang-suffix" .
-
-<#bad-eof-in-list>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-list.ttl> ;
- mf:name "bad-eof-in-list" .
-
-<#bad-eof-in-long-string>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-long-string.ttl> ;
- mf:name "bad-eof-in-long-string" .
-
-<#bad-eof-in-object-list>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-object-list.ttl> ;
- mf:name "bad-eof-in-object-list" .
-
-<#bad-eof-in-object-list2>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-object-list2.ttl> ;
- mf:name "bad-eof-in-object-list2" .
-
-<#bad-eof-in-predicate-list>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-predicate-list.ttl> ;
- mf:name "bad-eof-in-predicate-list" .
-
-<#bad-eof-in-string>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-string.ttl> ;
- mf:name "bad-eof-in-string" .
-
-<#bad-eof-in-text-character>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-text-character.ttl> ;
- mf:name "bad-eof-in-text-character" .
-
-<#bad-eof-in-triple-quote>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-triple-quote.ttl> ;
- mf:name "bad-eof-in-triple-quote" .
-
-<#bad-eof-in-uri>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-uri.ttl> ;
- mf:name "bad-eof-in-uri" .
-
-<#bad-eof-in-uri-character>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-uri-character.ttl> ;
- mf:name "bad-eof-in-uri-character" .
-
-<#bad-eof-in-uri-scheme>
- a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-eof-in-uri-scheme.nt> ;
- mf:name "bad-eof-in-uri-scheme" .
-
-<#bad-eof-in-utf8-character>
- a rdft:TestTurtleNegativeSyntax ;
- mf:action <bad-eof-in-utf8-character.ttl> ;
- mf:name "bad-eof-in-utf8-character" .
-
<#bad-equivalence>
a rdft:TestTurtleNegativeSyntax ;
mf:action <bad-equivalence.ttl> ;
@@ -598,6 +390,26 @@
mf:action <bad-lang.ttl> ;
mf:name "bad-lang" .
+<#bad-lang-start-delete>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-lang-start-delete.nt> ;
+ mf:name "bad-lang-start-delete" .
+
+<#bad-lang-start-space>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-lang-start-space.nt> ;
+ mf:name "bad-lang-start-space" .
+
+<#bad-lang-start-tab>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-lang-start-tab.nt> ;
+ mf:name "bad-lang-start-tab" .
+
+<#bad-lang-start-wide>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-lang-start-wide.nt> ;
+ mf:name "bad-lang-start-wide" .
+
<#bad-list>
a rdft:TestTurtleNegativeSyntax ;
mf:action <bad-list.ttl> ;
@@ -683,6 +495,11 @@
mf:action <bad-pn-escape.ttl> ;
mf:name "bad-pn-escape" .
+<#bad-predicate-in-blank>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-predicate-in-blank.ttl> ;
+ mf:name "bad-predicate-in-blank" .
+
<#bad-prefix>
a rdft:TestTurtleNegativeSyntax ;
mf:action <bad-prefix.ttl> ;
@@ -743,10 +560,30 @@
mf:action <bad-uri-scheme.nt> ;
mf:name "bad-uri-scheme" .
-<#bad-uri-scheme-start>
+<#bad-uri-scheme-start-apostrophe>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-uri-scheme-start-apostrophe.nt> ;
+ mf:name "bad-uri-scheme-start-apostrophe" .
+
+<#bad-uri-scheme-start-delete>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-uri-scheme-start-delete.nt> ;
+ mf:name "bad-uri-scheme-start-delete" .
+
+<#bad-uri-scheme-start-space>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-uri-scheme-start-space.nt> ;
+ mf:name "bad-uri-scheme-start-space" .
+
+<#bad-uri-scheme-start-tab>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-uri-scheme-start-tab.nt> ;
+ mf:name "bad-uri-scheme-start-tab" .
+
+<#bad-uri-scheme-start-wide>
a rdft:TestNTriplesNegativeSyntax ;
- mf:action <bad-uri-scheme-start.nt> ;
- mf:name "bad-uri-scheme-start" .
+ mf:action <bad-uri-scheme-start-wide.nt> ;
+ mf:name "bad-uri-scheme-start-wide" .
<#bad-uri-truncated>
a rdft:TestNTriplesNegativeSyntax ;
diff --git a/test/extra/eof/README.md b/test/extra/eof/README.md
new file mode 100644
index 00000000..1e461f79
--- /dev/null
+++ b/test/extra/eof/README.md
@@ -0,0 +1,5 @@
+EOF Test Suite
+==============
+
+This simple suite tests that inputs truncated in various places are handled
+correctly.
diff --git a/test/extra/bad/bad-nt-eof-after-blank.nt b/test/extra/eof/bad-nt-eof-after-blank.nt
index bc66ca37..bc66ca37 100644
--- a/test/extra/bad/bad-nt-eof-after-blank.nt
+++ b/test/extra/eof/bad-nt-eof-after-blank.nt
diff --git a/test/extra/bad/bad-nt-eof-after-lang-hyphen.nt b/test/extra/eof/bad-nt-eof-after-lang-hyphen.nt
index 9e885d80..9e885d80 100644
--- a/test/extra/bad/bad-nt-eof-after-lang-hyphen.nt
+++ b/test/extra/eof/bad-nt-eof-after-lang-hyphen.nt
diff --git a/test/extra/bad/bad-nt-eof-after-lang-subtag.nt b/test/extra/eof/bad-nt-eof-after-lang-subtag.nt
index f8158aec..f8158aec 100644
--- a/test/extra/bad/bad-nt-eof-after-lang-subtag.nt
+++ b/test/extra/eof/bad-nt-eof-after-lang-subtag.nt
diff --git a/test/extra/bad/bad-nt-eof-after-lang.nt b/test/extra/eof/bad-nt-eof-after-lang.nt
index 7ab04b06..7ab04b06 100644
--- a/test/extra/bad/bad-nt-eof-after-lang.nt
+++ b/test/extra/eof/bad-nt-eof-after-lang.nt
diff --git a/test/extra/bad/bad-nt-eof-after-object.nt b/test/extra/eof/bad-nt-eof-after-object.nt
index e796f2b0..e796f2b0 100644
--- a/test/extra/bad/bad-nt-eof-after-object.nt
+++ b/test/extra/eof/bad-nt-eof-after-object.nt
diff --git a/test/extra/bad/bad-nt-eof-after-predicate.nt b/test/extra/eof/bad-nt-eof-after-predicate.nt
index ea47bfbe..ea47bfbe 100644
--- a/test/extra/bad/bad-nt-eof-after-predicate.nt
+++ b/test/extra/eof/bad-nt-eof-after-predicate.nt
diff --git a/test/extra/bad/bad-nt-eof-after-string-escape.nt b/test/extra/eof/bad-nt-eof-after-string-escape.nt
index 869907e8..869907e8 100644
--- a/test/extra/bad/bad-nt-eof-after-string-escape.nt
+++ b/test/extra/eof/bad-nt-eof-after-string-escape.nt
diff --git a/test/extra/bad/bad-nt-eof-after-string.nt b/test/extra/eof/bad-nt-eof-after-string.nt
index 32dda36d..32dda36d 100644
--- a/test/extra/bad/bad-nt-eof-after-string.nt
+++ b/test/extra/eof/bad-nt-eof-after-string.nt
diff --git a/test/extra/bad/bad-nt-eof-after-subject.nt b/test/extra/eof/bad-nt-eof-after-subject.nt
index 21fa07f4..21fa07f4 100644
--- a/test/extra/bad/bad-nt-eof-after-subject.nt
+++ b/test/extra/eof/bad-nt-eof-after-subject.nt
diff --git a/test/extra/bad/bad-nt-eof-after-underscore.nt b/test/extra/eof/bad-nt-eof-after-underscore.nt
index 4b05f2ac..4b05f2ac 100644
--- a/test/extra/bad/bad-nt-eof-after-underscore.nt
+++ b/test/extra/eof/bad-nt-eof-after-underscore.nt
diff --git a/test/extra/bad/bad-nt-eof-before-blank.nt b/test/extra/eof/bad-nt-eof-before-blank.nt
index 99f70844..99f70844 100644
--- a/test/extra/bad/bad-nt-eof-before-blank.nt
+++ b/test/extra/eof/bad-nt-eof-before-blank.nt
diff --git a/test/extra/bad/bad-nt-eof-before-iri.nt b/test/extra/eof/bad-nt-eof-before-iri.nt
index c5fa7845..c5fa7845 100644
--- a/test/extra/bad/bad-nt-eof-before-iri.nt
+++ b/test/extra/eof/bad-nt-eof-before-iri.nt
diff --git a/test/extra/bad/bad-nt-eof-before-lang.nt b/test/extra/eof/bad-nt-eof-before-lang.nt
index f1a9d0df..f1a9d0df 100644
--- a/test/extra/bad/bad-nt-eof-before-lang.nt
+++ b/test/extra/eof/bad-nt-eof-before-lang.nt
diff --git a/test/extra/bad/bad-nt-eof-before-string-escape.nt b/test/extra/eof/bad-nt-eof-before-string-escape.nt
index 30443488..30443488 100644
--- a/test/extra/bad/bad-nt-eof-before-string-escape.nt
+++ b/test/extra/eof/bad-nt-eof-before-string-escape.nt
diff --git a/test/extra/bad/bad-nt-eof-before-string.nt b/test/extra/eof/bad-nt-eof-before-string.nt
index 6a2a7543..6a2a7543 100644
--- a/test/extra/bad/bad-nt-eof-before-string.nt
+++ b/test/extra/eof/bad-nt-eof-before-string.nt
diff --git a/test/extra/bad/bad-nt-eof-in-iri-path.nt b/test/extra/eof/bad-nt-eof-in-iri-path.nt
index e8555e2b..e8555e2b 100644
--- a/test/extra/bad/bad-nt-eof-in-iri-path.nt
+++ b/test/extra/eof/bad-nt-eof-in-iri-path.nt
diff --git a/test/extra/bad/bad-nt-eof-in-iri-scheme.nt b/test/extra/eof/bad-nt-eof-in-iri-scheme.nt
index 2c071547..2c071547 100644
--- a/test/extra/bad/bad-nt-eof-in-iri-scheme.nt
+++ b/test/extra/eof/bad-nt-eof-in-iri-scheme.nt
diff --git a/test/extra/bad/bad-nt-eof-in-string.nt b/test/extra/eof/bad-nt-eof-in-string.nt
index 98944654..98944654 100644
--- a/test/extra/bad/bad-nt-eof-in-string.nt
+++ b/test/extra/eof/bad-nt-eof-in-string.nt
diff --git a/test/extra/bad/bad-eof-after-quotes.ttl b/test/extra/eof/bad-ttl-eof-after-quotes.ttl
index 40e429cb..40e429cb 100644
--- a/test/extra/bad/bad-eof-after-quotes.ttl
+++ b/test/extra/eof/bad-ttl-eof-after-quotes.ttl
diff --git a/test/extra/bad/bad-eof-at-string-start.ttl b/test/extra/eof/bad-ttl-eof-at-string-start.ttl
index 93d20bcc..93d20bcc 100644
--- a/test/extra/bad/bad-eof-at-string-start.ttl
+++ b/test/extra/eof/bad-ttl-eof-at-string-start.ttl
diff --git a/test/extra/bad/bad-eof-in-blank.ttl b/test/extra/eof/bad-ttl-eof-in-blank.ttl
index 8cf4ee84..8cf4ee84 100644
--- a/test/extra/bad/bad-eof-in-blank.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-blank.ttl
diff --git a/test/extra/bad/bad-eof-in-escape.ttl b/test/extra/eof/bad-ttl-eof-in-escape.ttl
index 24b4eec6..24b4eec6 100644
--- a/test/extra/bad/bad-eof-in-escape.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-escape.ttl
diff --git a/test/extra/bad/bad-eof-in-lang-suffix.ttl b/test/extra/eof/bad-ttl-eof-in-lang-suffix.ttl
index f46a7763..f46a7763 100644
--- a/test/extra/bad/bad-eof-in-lang-suffix.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-lang-suffix.ttl
diff --git a/test/extra/bad/bad-eof-in-lang.ttl b/test/extra/eof/bad-ttl-eof-in-lang.ttl
index bfdffd02..bfdffd02 100644
--- a/test/extra/bad/bad-eof-in-lang.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-lang.ttl
diff --git a/test/extra/bad/bad-eof-in-list.ttl b/test/extra/eof/bad-ttl-eof-in-list.ttl
index 13eeb88d..13eeb88d 100644
--- a/test/extra/bad/bad-eof-in-list.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-list.ttl
diff --git a/test/extra/bad/bad-eof-in-long-string.ttl b/test/extra/eof/bad-ttl-eof-in-long-string.ttl
index 2ef179a8..2ef179a8 100644
--- a/test/extra/bad/bad-eof-in-long-string.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-long-string.ttl
diff --git a/test/extra/bad/bad-eof-in-object-list.ttl b/test/extra/eof/bad-ttl-eof-in-object-list.ttl
index 9bbcd17a..9bbcd17a 100644
--- a/test/extra/bad/bad-eof-in-object-list.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-object-list.ttl
diff --git a/test/extra/bad/bad-eof-in-object-list2.ttl b/test/extra/eof/bad-ttl-eof-in-object-list2.ttl
index 9186fb9f..9186fb9f 100644
--- a/test/extra/bad/bad-eof-in-object-list2.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-object-list2.ttl
diff --git a/test/extra/bad/bad-eof-in-predicate-list.ttl b/test/extra/eof/bad-ttl-eof-in-predicate-list.ttl
index eab5b05b..eab5b05b 100644
--- a/test/extra/bad/bad-eof-in-predicate-list.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-predicate-list.ttl
diff --git a/test/extra/bad/bad-eof-in-string.ttl b/test/extra/eof/bad-ttl-eof-in-string.ttl
index bb6e817f..bb6e817f 100644
--- a/test/extra/bad/bad-eof-in-string.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-string.ttl
diff --git a/test/extra/bad/bad-eof-in-text-character.ttl b/test/extra/eof/bad-ttl-eof-in-text-character.ttl
index a614803a..a614803a 100644
--- a/test/extra/bad/bad-eof-in-text-character.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-text-character.ttl
diff --git a/test/extra/bad/bad-eof-in-triple-quote.ttl b/test/extra/eof/bad-ttl-eof-in-triple-quote.ttl
index fb935441..fb935441 100644
--- a/test/extra/bad/bad-eof-in-triple-quote.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-triple-quote.ttl
diff --git a/test/extra/bad/bad-eof-in-uri-character.ttl b/test/extra/eof/bad-ttl-eof-in-uri-character.ttl
index eda70770..eda70770 100644
--- a/test/extra/bad/bad-eof-in-uri-character.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-uri-character.ttl
diff --git a/test/extra/bad/bad-eof-in-uri-scheme.nt b/test/extra/eof/bad-ttl-eof-in-uri-scheme.ttl
index de892dcf..de892dcf 100644
--- a/test/extra/bad/bad-eof-in-uri-scheme.nt
+++ b/test/extra/eof/bad-ttl-eof-in-uri-scheme.ttl
diff --git a/test/extra/bad/bad-eof-in-uri.ttl b/test/extra/eof/bad-ttl-eof-in-uri.ttl
index 07b6e6ab..07b6e6ab 100644
--- a/test/extra/bad/bad-eof-in-uri.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-uri.ttl
diff --git a/test/extra/bad/bad-eof-in-utf8-character.ttl b/test/extra/eof/bad-ttl-eof-in-utf8-character.ttl
index 16784e88..16784e88 100644
--- a/test/extra/bad/bad-eof-in-utf8-character.ttl
+++ b/test/extra/eof/bad-ttl-eof-in-utf8-character.ttl
diff --git a/test/extra/eof/manifest.ttl b/test/extra/eof/manifest.ttl
new file mode 100644
index 00000000..465333f7
--- /dev/null
+++ b/test/extra/eof/manifest.ttl
@@ -0,0 +1,225 @@
+@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix rdft: <http://www.w3.org/ns/rdftest#> .
+
+<>
+ a mf:Manifest ;
+ rdfs:comment "Serd EOF (truncated input) test suite" ;
+ mf:entries (
+ <#bad-nt-eof-after-blank>
+ <#bad-nt-eof-after-lang>
+ <#bad-nt-eof-after-lang-hyphen>
+ <#bad-nt-eof-after-lang-subtag>
+ <#bad-nt-eof-after-object>
+ <#bad-nt-eof-after-predicate>
+ <#bad-nt-eof-after-string>
+ <#bad-nt-eof-after-string-escape>
+ <#bad-nt-eof-after-subject>
+ <#bad-nt-eof-after-underscore>
+ <#bad-nt-eof-before-blank>
+ <#bad-nt-eof-before-iri>
+ <#bad-nt-eof-before-lang>
+ <#bad-nt-eof-before-string>
+ <#bad-nt-eof-before-string-escape>
+ <#bad-nt-eof-in-iri-path>
+ <#bad-nt-eof-in-iri-scheme>
+ <#bad-nt-eof-in-string>
+ <#bad-ttl-eof-after-quotes>
+ <#bad-ttl-eof-at-string-start>
+ <#bad-ttl-eof-in-blank>
+ <#bad-ttl-eof-in-escape>
+ <#bad-ttl-eof-in-lang>
+ <#bad-ttl-eof-in-lang-suffix>
+ <#bad-ttl-eof-in-list>
+ <#bad-ttl-eof-in-long-string>
+ <#bad-ttl-eof-in-object-list>
+ <#bad-ttl-eof-in-object-list2>
+ <#bad-ttl-eof-in-predicate-list>
+ <#bad-ttl-eof-in-string>
+ <#bad-ttl-eof-in-text-character>
+ <#bad-ttl-eof-in-triple-quote>
+ <#bad-ttl-eof-in-uri>
+ <#bad-ttl-eof-in-uri-character>
+ <#bad-ttl-eof-in-uri-scheme>
+ <#bad-ttl-eof-in-utf8-character>
+ ) .
+
+<#bad-nt-eof-after-blank>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-blank.nt> ;
+ mf:name "bad-nt-eof-after-blank" .
+
+<#bad-nt-eof-after-lang>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-lang.nt> ;
+ mf:name "bad-nt-eof-after-lang" .
+
+<#bad-nt-eof-after-lang-hyphen>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-lang-hyphen.nt> ;
+ mf:name "bad-nt-eof-after-lang-hyphen" .
+
+<#bad-nt-eof-after-lang-subtag>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-lang-subtag.nt> ;
+ mf:name "bad-nt-eof-after-lang-subtag" .
+
+<#bad-nt-eof-after-object>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-object.nt> ;
+ mf:name "bad-nt-eof-after-object" .
+
+<#bad-nt-eof-after-predicate>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-predicate.nt> ;
+ mf:name "bad-nt-eof-after-predicate" .
+
+<#bad-nt-eof-after-string>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-string.nt> ;
+ mf:name "bad-nt-eof-after-string" .
+
+<#bad-nt-eof-after-string-escape>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-string-escape.nt> ;
+ mf:name "bad-nt-eof-after-string-escape" .
+
+<#bad-nt-eof-after-subject>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-subject.nt> ;
+ mf:name "bad-nt-eof-after-subject" .
+
+<#bad-nt-eof-after-underscore>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-after-underscore.nt> ;
+ mf:name "bad-nt-eof-after-underscore" .
+
+<#bad-nt-eof-before-blank>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-before-blank.nt> ;
+ mf:name "bad-nt-eof-before-blank" .
+
+<#bad-nt-eof-before-iri>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-before-iri.nt> ;
+ mf:name "bad-nt-eof-before-iri" .
+
+<#bad-nt-eof-before-lang>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-before-lang.nt> ;
+ mf:name "bad-nt-eof-before-lang" .
+
+<#bad-nt-eof-before-string>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-before-string.nt> ;
+ mf:name "bad-nt-eof-before-string" .
+
+<#bad-nt-eof-before-string-escape>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-before-string-escape.nt> ;
+ mf:name "bad-nt-eof-before-string-escape" .
+
+<#bad-nt-eof-in-iri-path>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-in-iri-path.nt> ;
+ mf:name "bad-nt-eof-in-iri-path" .
+
+<#bad-nt-eof-in-iri-scheme>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-in-iri-scheme.nt> ;
+ mf:name "bad-nt-eof-in-iri-scheme" .
+
+<#bad-nt-eof-in-string>
+ a rdft:TestNTriplesNegativeSyntax ;
+ mf:action <bad-nt-eof-in-string.nt> ;
+ mf:name "bad-nt-eof-in-string" .
+
+<#bad-ttl-eof-after-quotes>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-after-quotes.ttl> ;
+ mf:name "bad-ttl-eof-after-quotes" .
+
+<#bad-ttl-eof-at-string-start>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-at-string-start.ttl> ;
+ mf:name "bad-ttl-eof-at-string-start" .
+
+<#bad-ttl-eof-in-blank>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-blank.ttl> ;
+ mf:name "bad-ttl-eof-in-blank" .
+
+<#bad-ttl-eof-in-escape>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-escape.ttl> ;
+ mf:name "bad-ttl-eof-in-escape" .
+
+<#bad-ttl-eof-in-lang>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-lang.ttl> ;
+ mf:name "bad-ttl-eof-in-lang" .
+
+<#bad-ttl-eof-in-lang-suffix>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-lang-suffix.ttl> ;
+ mf:name "bad-ttl-eof-in-lang-suffix" .
+
+<#bad-ttl-eof-in-list>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-list.ttl> ;
+ mf:name "bad-ttl-eof-in-list" .
+
+<#bad-ttl-eof-in-long-string>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-long-string.ttl> ;
+ mf:name "bad-ttl-eof-in-long-string" .
+
+<#bad-ttl-eof-in-object-list>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-object-list.ttl> ;
+ mf:name "bad-ttl-eof-in-object-list" .
+
+<#bad-ttl-eof-in-object-list2>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-object-list2.ttl> ;
+ mf:name "bad-ttl-eof-in-object-list2" .
+
+<#bad-ttl-eof-in-predicate-list>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-predicate-list.ttl> ;
+ mf:name "bad-ttl-eof-in-predicate-list" .
+
+<#bad-ttl-eof-in-string>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-string.ttl> ;
+ mf:name "bad-ttl-eof-in-string" .
+
+<#bad-ttl-eof-in-text-character>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-text-character.ttl> ;
+ mf:name "bad-ttl-eof-in-text-character" .
+
+<#bad-ttl-eof-in-triple-quote>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-triple-quote.ttl> ;
+ mf:name "bad-ttl-eof-in-triple-quote" .
+
+<#bad-ttl-eof-in-uri>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-uri.ttl> ;
+ mf:name "bad-ttl-eof-in-uri" .
+
+<#bad-ttl-eof-in-uri-character>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-uri-character.ttl> ;
+ mf:name "bad-ttl-eof-in-uri-character" .
+
+<#bad-ttl-eof-in-uri-scheme>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-uri-scheme.ttl> ;
+ mf:name "bad-ttl-eof-in-uri-scheme" .
+
+<#bad-ttl-eof-in-utf8-character>
+ a rdft:TestTurtleNegativeSyntax ;
+ mf:action <bad-ttl-eof-in-utf8-character.ttl> ;
+ mf:name "bad-ttl-eof-in-utf8-character" .
diff --git a/test/extra/good/manifest.ttl b/test/extra/good/manifest.ttl
index 350d7d9c..659f8fd1 100644
--- a/test/extra/good/manifest.ttl
+++ b/test/extra/good/manifest.ttl
@@ -16,16 +16,21 @@
<#test-blank-node-statement>
<#test-blankdot>
<#test-bom>
+ <#test-boolish-prefix>
<#test-changing-base>
<#test-comment-whitespace>
<#test-cr>
<#test-digit-start-pname>
+ <#test-decimal-ends-with-dot>
<#test-double>
+ <#test-double-ends-with-dot>
<#test-empty-path-base>
<#test-eof-at-page-end>
<#test-ext-namedblank-iri>
<#test-ext-namedblank-prefix>
+ <#test-false-ends-with-dot>
<#test-id>
+ <#test-integer-ends-with-dot>
<#test-list-in-blank>
<#test-list-subject>
<#test-local-name-ends-with-dot>
@@ -56,6 +61,7 @@
<#test-several-eaten-dots>
<#test-string-escapes>
<#test-trig-syntax-all-rules>
+ <#test-true-ends-with-dot>
<#test-ttl-syntax-all-rules>
<#test-uri>
) .
@@ -120,6 +126,18 @@
mf:name "test-bom" ;
mf:result <test-bom.nt> .
+<#test-bom-only>
+ a rdft:TestTurtleEval ;
+ mf:action <test-bom-only.ttl> ;
+ mf:name "test-bom-only" ;
+ mf:result <test-bom-only.nt> .
+
+<#test-boolish-prefix>
+ a rdft:TestTurtleEval ;
+ mf:action <test-boolish-prefix.ttl> ;
+ mf:name "test-boolish-prefix" ;
+ mf:result <test-boolish-prefix.nt> .
+
<#test-changing-base>
a rdft:TestTurtleEval ;
mf:action <test-changing-base.ttl> ;
@@ -144,12 +162,24 @@
mf:name "test-digit-start-pname" ;
mf:result <test-digit-start-pname.nt> .
+<#test-decimal-ends-with-dot>
+ a rdft:TestTurtleEval ;
+ mf:action <test-decimal-ends-with-dot.ttl> ;
+ mf:name "test-decimal-ends-with-dot" ;
+ mf:result <test-decimal-ends-with-dot.nt> .
+
<#test-double>
a rdft:TestTurtleEval ;
mf:action <test-double.ttl> ;
mf:name "test-double" ;
mf:result <test-double.nt> .
+<#test-double-ends-with-dot>
+ a rdft:TestTurtleEval ;
+ mf:action <test-double-ends-with-dot.ttl> ;
+ mf:name "test-double-ends-with-dot" ;
+ mf:result <test-double-ends-with-dot.nt> .
+
<#test-empty-path-base>
a rdft:TestTurtleEval ;
mf:action <test-empty-path-base.ttl> ;
@@ -174,12 +204,24 @@
mf:name "test-ext-namedblank-prefix" ;
mf:result <test-ext-namedblank-prefix.nt> .
+<#test-false-ends-with-dot>
+ a rdft:TestTurtleEval ;
+ mf:action <test-false-ends-with-dot.ttl> ;
+ mf:name "test-false-ends-with-dot" ;
+ mf:result <test-false-ends-with-dot.nt> .
+
<#test-id>
a rdft:TestTurtleEval ;
mf:action <test-id.ttl> ;
mf:name "test-id" ;
mf:result <test-id.nt> .
+<#test-integer-ends-with-dot>
+ a rdft:TestTurtleEval ;
+ mf:action <test-integer-ends-with-dot.ttl> ;
+ mf:name "test-integer-ends-with-dot" ;
+ mf:result <test-integer-ends-with-dot.nt> .
+
<#test-list-in-blank>
a rdft:TestTurtleEval ;
mf:action <test-list-in-blank.ttl> ;
@@ -344,6 +386,12 @@
mf:action <test-trig-syntax-all-rules.trig> ;
mf:name "test-trig-syntax-all-rules" .
+<#test-true-ends-with-dot>
+ a rdft:TestTurtleEval ;
+ mf:action <test-true-ends-with-dot.ttl> ;
+ mf:name "test-true-ends-with-dot" ;
+ mf:result <test-true-ends-with-dot.nt> .
+
<#test-ttl-syntax-all-rules>
a rdft:TestTurtlePositiveSyntax ;
mf:action <test-ttl-syntax-all-rules.ttl> ;
diff --git a/test/extra/good/test-bom-only.nt b/test/extra/good/test-bom-only.nt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/extra/good/test-bom-only.nt
diff --git a/test/extra/good/test-bom-only.ttl b/test/extra/good/test-bom-only.ttl
new file mode 100644
index 00000000..5f282702
--- /dev/null
+++ b/test/extra/good/test-bom-only.ttl
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/test/extra/good/test-boolish-prefix.nt b/test/extra/good/test-boolish-prefix.nt
new file mode 100644
index 00000000..d49eeab2
--- /dev/null
+++ b/test/extra/good/test-boolish-prefix.nt
@@ -0,0 +1,2 @@
+<http://example.org/s> <http://example.org/p> <http://example.org/falseish#o> .
+<http://example.org/s> <http://example.org/p> <http://example.org/trueish#o> .
diff --git a/test/extra/good/test-boolish-prefix.ttl b/test/extra/good/test-boolish-prefix.ttl
new file mode 100644
index 00000000..81ffdf11
--- /dev/null
+++ b/test/extra/good/test-boolish-prefix.ttl
@@ -0,0 +1,5 @@
+@prefix false.ish: <http://example.org/falseish#> .
+@prefix true.ish: <http://example.org/trueish#> .
+
+<http://example.org/s> <http://example.org/p> false.ish:o .
+<http://example.org/s> <http://example.org/p> true.ish:o .
diff --git a/test/extra/good/test-decimal-ends-with-dot.nt b/test/extra/good/test-decimal-ends-with-dot.nt
new file mode 100644
index 00000000..be0802bc
--- /dev/null
+++ b/test/extra/good/test-decimal-ends-with-dot.nt
@@ -0,0 +1 @@
+<http://example.org/eg#s> <http://example.org/eg#p> "12.3"^^<http://www.w3.org/2001/XMLSchema#decimal> .
diff --git a/test/extra/good/test-decimal-ends-with-dot.ttl b/test/extra/good/test-decimal-ends-with-dot.ttl
new file mode 100644
index 00000000..a63970d3
--- /dev/null
+++ b/test/extra/good/test-decimal-ends-with-dot.ttl
@@ -0,0 +1,4 @@
+@prefix eg: <http://example.org/eg#> .
+
+eg:s
+ eg:p 12.3.
diff --git a/test/extra/good/test-double-ends-with-dot.nt b/test/extra/good/test-double-ends-with-dot.nt
new file mode 100644
index 00000000..20e4395e
--- /dev/null
+++ b/test/extra/good/test-double-ends-with-dot.nt
@@ -0,0 +1 @@
+<http://example.org/eg#s> <http://example.org/eg#p> "12.3e4"^^<http://www.w3.org/2001/XMLSchema#double> .
diff --git a/test/extra/good/test-double-ends-with-dot.ttl b/test/extra/good/test-double-ends-with-dot.ttl
new file mode 100644
index 00000000..4bd17a0b
--- /dev/null
+++ b/test/extra/good/test-double-ends-with-dot.ttl
@@ -0,0 +1,4 @@
+@prefix eg: <http://example.org/eg#> .
+
+eg:s
+ eg:p 12.3e4.
diff --git a/test/extra/good/test-false-ends-with-dot.nt b/test/extra/good/test-false-ends-with-dot.nt
new file mode 100644
index 00000000..3b811813
--- /dev/null
+++ b/test/extra/good/test-false-ends-with-dot.nt
@@ -0,0 +1 @@
+<http://example.org/eg#s> <http://example.org/eg#p> "false"^^<http://www.w3.org/2001/XMLSchema#boolean> .
diff --git a/test/extra/good/test-false-ends-with-dot.ttl b/test/extra/good/test-false-ends-with-dot.ttl
new file mode 100644
index 00000000..14e2aa90
--- /dev/null
+++ b/test/extra/good/test-false-ends-with-dot.ttl
@@ -0,0 +1,4 @@
+@prefix eg: <http://example.org/eg#> .
+
+eg:s
+ eg:p false.
diff --git a/test/extra/good/test-integer-ends-with-dot.nt b/test/extra/good/test-integer-ends-with-dot.nt
new file mode 100644
index 00000000..7d6ff362
--- /dev/null
+++ b/test/extra/good/test-integer-ends-with-dot.nt
@@ -0,0 +1 @@
+<http://example.org/eg#s> <http://example.org/eg#p> "12"^^<http://www.w3.org/2001/XMLSchema#integer> .
diff --git a/test/extra/good/test-integer-ends-with-dot.ttl b/test/extra/good/test-integer-ends-with-dot.ttl
new file mode 100644
index 00000000..350ea41d
--- /dev/null
+++ b/test/extra/good/test-integer-ends-with-dot.ttl
@@ -0,0 +1,4 @@
+@prefix eg: <http://example.org/eg#> .
+
+eg:s
+ eg:p 12.
diff --git a/test/extra/good/test-nq-syntax-all-rules.nq b/test/extra/good/test-nq-syntax-all-rules.nq
index a8b80b9a..f9bffe20 100644
--- a/test/extra/good/test-nq-syntax-all-rules.nq
+++ b/test/extra/good/test-nq-syntax-all-rules.nq
@@ -2,6 +2,6 @@
_:e.u.s <http://example.org/p> _:o.
_:e.u.s <http://example.org/p> "o"@en-gb _:g.
_:s <http://example.org/p> "ob\t\b\n\r\f\\\"\'\u0025\U00015678ject" <http://example.org/g> .
-_:s <http://example.org/p> "€߿ࠀ࿿က쿿퀀퟿�𐀀𿿽񀀀󿿽􀀀􏿽" <http://example.org/g> .
+_:Σ <http://example.org/p> "€߿ࠀ࿿က쿿퀀퟿�𐀀𿿽񀀀󿿽􀀀􏿽" <http://example.org/g> .
_:s <http://example.org/p> "o"^^<http://example.org/T> <http://example.org/g> .
_:s <http://example.org/p> "o"@en _:g .
diff --git a/test/extra/good/test-nt-syntax-all-rules.nt b/test/extra/good/test-nt-syntax-all-rules.nt
index ed84f410..c626a702 100644
--- a/test/extra/good/test-nt-syntax-all-rules.nt
+++ b/test/extra/good/test-nt-syntax-all-rules.nt
@@ -4,4 +4,4 @@ _:s <http://example.org/p> "€߿ࠀ࿿က쿿퀀퟿�𐀀𿿽񀀀󿿽􀀀􏿽
_:s <http://example.org/p> "o"^^<http://example.org/T> .
_:s <http://example.org/p> "o"@en .
_:e.u.s <http://example.org/p> "o"@en-gb .
-_:e.u.s <http://example.org/p> _:o.
+_:e.u.s <http://example.org/p> _:Ω.
diff --git a/test/extra/good/test-trig-syntax-all-rules.trig b/test/extra/good/test-trig-syntax-all-rules.trig
index 97557b86..c824ffae 100644
--- a/test/extra/good/test-trig-syntax-all-rules.trig
+++ b/test/extra/good/test-trig-syntax-all-rules.trig
@@ -7,13 +7,13 @@
""string""\t\b\n\r\f\'\u0025\U00015678""" .
eg:s eg:p "€߿ࠀ࿿က쿿퀀퟿�𐀀𿿽񀀀󿿽􀀀􏿽" .
eg:sub%25ject eg:pr\~d "o"^^eg:T .
-eg:sub%25ject eg:pr\~d "o"@en .
+eg:s\@bject eg:pr\~d "o"@en .
_:e.u.s eg:p "o"@en-gb .
_:e.u.s eg:p _:o.
_:e.u.s eg:p‿r⁀d 2.
_:e.u.s eg:prèd 3 .
_:e.u.s eg:pͯ 4.5.
-eg:s eg:p 0 , .1 , 2.3 , 4E5, 6e07 .
+eg:Σ eg:p 0 , .1 , 2.3 , 4E5, 6e07 .
eg:s eg:p .7e8 , .9E0 , 1.e2 , 3.E4 .
eg:s eg:p .2E3 , .4e5 , 6.7E8 , 9.
[ ] eg:p 0.e1, 2.E3.
diff --git a/test/extra/good/test-true-ends-with-dot.nt b/test/extra/good/test-true-ends-with-dot.nt
new file mode 100644
index 00000000..9938065b
--- /dev/null
+++ b/test/extra/good/test-true-ends-with-dot.nt
@@ -0,0 +1 @@
+<http://example.org/eg#s> <http://example.org/eg#p> "true"^^<http://www.w3.org/2001/XMLSchema#boolean> .
diff --git a/test/extra/good/test-true-ends-with-dot.ttl b/test/extra/good/test-true-ends-with-dot.ttl
new file mode 100644
index 00000000..ebd3b6e1
--- /dev/null
+++ b/test/extra/good/test-true-ends-with-dot.ttl
@@ -0,0 +1,4 @@
+@prefix eg: <http://example.org/eg#> .
+
+eg:s
+ eg:p true.
diff --git a/test/extra/good/test-ttl-syntax-all-rules.ttl b/test/extra/good/test-ttl-syntax-all-rules.ttl
index ead2e8a8..dd4fa315 100644
--- a/test/extra/good/test-ttl-syntax-all-rules.ttl
+++ b/test/extra/good/test-ttl-syntax-all-rules.ttl
@@ -6,13 +6,13 @@
""string""\t\b\n\r\f\'\u0025\U00015678""" .
eg:s eg:p "€߿ࠀ࿿က쿿퀀퟿�𐀀𿿽񀀀󿿽􀀀􏿽" .
eg:sub%25ject eg:pr\~d "o"^^eg:T .
-eg:sub%25ject eg:pr\~d "o"@en .
+eg:s\@bject eg:pr\~d "o"@en .
_:e.u.s eg:p "o"@en-gb .
_:e.u.s eg:p _:o.
_:e.u.s eg:p‿r⁀d 2.
_:e.u.s eg:prèd 3 .
_:e.u.s eg:pͯ 4.5.
-eg:s eg:p 0 , .1 , 2.3 , 4E5, 6e07 .
+eg:Σ eg:p 0 , .1 , 2.3 , 4E5, 6e07 .
eg:s eg:p .7e8 , .9E0 , 1.e2 , 3.E4 .
eg:s eg:p .2E3 , .4e5 , 6.7E8 , 9.
[ ] eg:p 0.e1, 2.E3.
@@ -20,7 +20,7 @@ eg:s eg:p .2E3 , .4e5 , 6.7E8 , 9.
eg:s eg:p [] .
[
eg:p1 eg:o1 ;
- eg:p2 _:o2 ;
+ eg:p2 _:β2 ;
eg:p3 "o3" ;
] a eg:S .
diff --git a/test/extra/pretty/manifest.ttl b/test/extra/pretty/manifest.ttl
index 6422c9e0..26ed63be 100644
--- a/test/extra/pretty/manifest.ttl
+++ b/test/extra/pretty/manifest.ttl
@@ -35,6 +35,7 @@
<#nested-list-object>
<#nested-list-object-with-empty-lists>
<#relative-uris>
+ <#repeated-directives>
<#short-string-escapes>
<#uri-escapes>
<#nested-list-subject>
@@ -214,6 +215,12 @@
mf:name "relative-uris" ;
mf:result <relative-uris.ttl> .
+<#repeated-directives>
+ a rdft:TestTurtleEval ;
+ mf:action <repeated-directives.ttl> ;
+ mf:name "repeated-directives" ;
+ mf:result <repeated-directives.ttl> .
+
<#short-string-escapes>
a rdft:TestTurtleEval ;
mf:action <short-string-escapes.ttl> ;
diff --git a/test/extra/pretty/repeated-directives.ttl b/test/extra/pretty/repeated-directives.ttl
new file mode 100644
index 00000000..c7875c31
--- /dev/null
+++ b/test/extra/pretty/repeated-directives.ttl
@@ -0,0 +1,17 @@
+@base <http://example.org/base> .
+@base <http://example.org/base> .
+@base <base> .
+@prefix eg: <http://example.org/eg#> .
+@prefix eg: <http://example.org/eg#> .
+@prefix eg: <eg#> .
+
+eg:s
+ eg:p eg:o ,
+ eg:o .
+
+@prefix eg: <eg#> .
+@prefix eg: <http://example.org/eg#> .
+@prefix eg: <http://example.org/eg#> .
+@base <base> .
+@base <http://example.org/base> .
+@base <http://example.org/base> .
diff --git a/test/headers/meson.build b/test/headers/meson.build
index 6cb14f6e..b9125d2b 100644
--- a/test/headers/meson.build
+++ b/test/headers/meson.build
@@ -38,6 +38,7 @@ test(
files('test_headers.c'),
c_args: header_c_suppressions,
dependencies: serd_dep,
+ implicit_include_directories: false,
),
suite: 'unit',
)
diff --git a/test/headers/test_headers.c b/test/headers/test_headers.c
index c855c103..2f923c7e 100644
--- a/test/headers/test_headers.c
+++ b/test/headers/test_headers.c
@@ -1,10 +1,9 @@
// Copyright 2022 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
-#include "serd/serd.h" // IWYU pragma: keep
+#include <serd/serd.h> // IWYU pragma: keep
-SERD_CONST_FUNC
-int
+SERD_CONST_FUNC int
main(void)
{
return 0;
diff --git a/test/lint/meson.build b/test/lint/meson.build
new file mode 100644
index 00000000..cca43342
--- /dev/null
+++ b/test/lint/meson.build
@@ -0,0 +1,133 @@
+# Copyright 2020-2023 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+plot_script_paths = [
+ '../../scripts/serd_bench.py',
+]
+
+simple_script_paths = [
+ '../../scripts/check_formatting.py',
+ '../serd_test_util/__init__.py',
+ '../run_suite.py',
+ '../test_quiet.py',
+ '../test_stdin.py',
+ '../test_write_error.py',
+]
+
+ttl_metadata_file_paths = [
+ '../../serd.ttl',
+ '../extra/abbreviate/manifest.ttl',
+ '../extra/bad/manifest.ttl',
+ '../extra/big/manifest.ttl',
+ '../extra/full/manifest.ttl',
+ '../extra/good/manifest.ttl',
+ '../extra/lax/manifest.ttl',
+ '../extra/perfect/manifest.ttl',
+ '../extra/prefix/manifest.ttl',
+ '../extra/pretty/manifest.ttl',
+ '../extra/qualify/manifest.ttl',
+ '../extra/root/manifest.ttl',
+]
+
+plot_scripts = files(plot_script_paths)
+simple_scripts = files(simple_script_paths)
+python_script_paths = simple_script_paths + plot_script_paths
+python_scripts = plot_scripts + simple_scripts
+
+all_sources = sources + unit_test_sources + files('../../src/serdi.c')
+
+# Check licensing metadata
+reuse = find_program('reuse', required: false)
+if reuse.found()
+ test(
+ 'REUSE',
+ reuse,
+ args: ['--root', serd_src_root, 'lint'],
+ suite: 'data',
+ )
+endif
+
+# Check code formatting
+clang_format = find_program('clang-format', required: false)
+if clang_format.found()
+ test(
+ 'format',
+ clang_format,
+ args: ['--Werror', '--dry-run'] + c_headers + all_sources,
+ suite: 'code',
+ )
+endif
+
+# Check script formatting
+black = find_program('black', required: false)
+if black.found()
+ black_opts = ['--check', '-q', '-l', '79']
+ foreach script_path : python_script_paths
+ script = files(script_path)
+ name = 'black_' + script_path.substring(3).underscorify()
+ test(name, black, args: black_opts + [script], suite: 'scripts')
+ endforeach
+endif
+
+# Check scripts for errors with flake8
+flake8 = find_program('flake8', required: false)
+if flake8.found()
+ test('flake8', flake8, args: python_scripts, suite: 'scripts')
+endif
+
+# Check scripts for errors with pylint
+pylint = find_program('pylint', required: false)
+if pylint.found()
+ pymod = import('python')
+ plot_py = pymod.find_installation(
+ 'python3',
+ modules: ['matplotlib'],
+ required: false,
+ )
+
+ pylint_args = ['--disable', 'bad-option-value'] + simple_scripts
+ if plot_py.found()
+ pylint_args += plot_scripts
+ endif
+
+ test('pylint', pylint, args: pylint_args, suite: 'scripts')
+endif
+
+# Check Turtle formatting with serdi
+if is_variable('serdi')
+ foreach ttl_file_path : ttl_metadata_file_paths
+ test(
+ ttl_file_path.substring(3).underscorify(),
+ check_formatting_py,
+ args: [files(ttl_file_path), serdi, '-o', 'turtle'],
+ suite: 'data',
+ )
+ endforeach
+endif
+
+if not meson.is_subproject()
+ # Check release metadata
+ autoship = find_program('autoship', required: false)
+ if autoship.found()
+ test('autoship', autoship, args: ['test', serd_src_root], suite: 'data')
+ endif
+
+ # Check code with cppcheck
+ cppcheck = find_program('cppcheck', required: false)
+ if cppcheck.found()
+ compdb_path = join_paths(serd_build_root, 'compile_commands.json')
+ suppress_path = join_paths(serd_src_root, '.suppress.cppcheck')
+ test(
+ 'cppcheck',
+ cppcheck,
+ args: [
+ '--enable=warning,style,performance,portability',
+ '--error-exitcode=1',
+ '--project=' + compdb_path,
+ '--suppressions-list=' + suppress_path,
+ '-q',
+ ],
+ suite: 'code',
+ )
+ endif
+endif
diff --git a/test/meson.build b/test/meson.build
index 33f86dfb..223e279d 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -4,109 +4,6 @@
run_suite = find_program('run_suite.py')
wrapper = meson.get_external_property('exe_wrapper', '')
-########################
-# Scripts and Metadata #
-########################
-
-plot_script_paths = [
- '../scripts/serd_bench.py',
-]
-
-simple_script_paths = [
- '../scripts/check_formatting.py',
- 'serd_test_util/__init__.py',
- 'run_suite.py',
- 'test_quiet.py',
- 'test_stdin.py',
- 'test_write_error.py',
-]
-
-ttl_metadata_file_paths = [
- '../serd.ttl',
- 'extra/abbreviate/manifest.ttl',
- 'extra/bad/manifest.ttl',
- 'extra/big/manifest.ttl',
- 'extra/full/manifest.ttl',
- 'extra/good/manifest.ttl',
- 'extra/lax/manifest.ttl',
- 'extra/perfect/manifest.ttl',
- 'extra/prefix/manifest.ttl',
- 'extra/pretty/manifest.ttl',
- 'extra/qualify/manifest.ttl',
- 'extra/root/manifest.ttl',
-]
-
-plot_scripts = files(plot_script_paths)
-simple_scripts = files(simple_script_paths)
-python_script_paths = simple_script_paths + plot_script_paths
-python_scripts = plot_scripts + simple_scripts
-
-if get_option('lint')
- # Check release metadata
- if not meson.is_subproject()
- autoship = find_program('autoship', required: false)
- if autoship.found()
- test('autoship', autoship, args: ['test', serd_src_root], suite: 'data')
- endif
- endif
-
- # Check licensing metadata
- reuse = find_program('reuse', required: false)
- if reuse.found()
- test(
- 'REUSE',
- reuse,
- args: ['--root', serd_src_root, 'lint'],
- suite: 'data',
- )
- endif
-
- # Check script formatting
- black = find_program('black', required: false)
- if black.found()
- black_opts = ['--check', '-q', '-l', '79']
- foreach script_path : python_script_paths
- script = files(script_path)
- name = script_path.underscorify()
- test(name, black, args: black_opts + [script], suite: 'scripts')
- endforeach
- endif
-
- # Check scripts for errors with flake8
- flake8 = find_program('flake8', required: false)
- if flake8.found()
- test('flake8', flake8, args: python_scripts, suite: 'scripts')
- endif
-
- # Check scripts for errors with pylint
- pylint = find_program('pylint', required: false)
- if pylint.found()
- pymod = import('python')
- plot_py = pymod.find_installation(
- 'python3',
- modules: ['matplotlib'],
- required: false,
- )
-
- pylint_args = ['--disable', 'bad-option-value'] + simple_scripts
- if plot_py.found()
- pylint_args += plot_scripts
- endif
-
- test('pylint', pylint, args: pylint_args, suite: 'scripts')
- endif
-
- # Check Turtle formatting with serdi
- foreach ttl_file_path : ttl_metadata_file_paths
- test(
- ttl_file_path.underscorify(),
- check_formatting_py,
- args: [files(ttl_file_path), serdi, '-o', 'turtle'],
- suite: 'data',
- )
- endforeach
-endif
-
###################
# Header Warnings #
###################
@@ -117,7 +14,7 @@ subdir('headers')
# Unit Tests #
##############
-unit_tests = [
+unit_test_names = [
'env',
'free_null',
'node',
@@ -128,14 +25,19 @@ unit_tests = [
'writer',
]
-foreach unit : unit_tests
+unit_test_sources = files('headers/test_headers.c')
+
+foreach name : unit_test_names
+ source = files('test_@0@.c'.format(name))
+ unit_test_sources += source
test(
- unit,
+ name,
executable(
- 'test_@0@'.format(unit),
- files('test_@0@.c'.format(unit)),
+ 'test_@0@'.format(name),
+ source,
c_args: c_suppressions,
dependencies: serd_dep,
+ implicit_include_directories: false,
),
suite: 'unit',
)
@@ -311,6 +213,17 @@ test_suites = {
'--',
'-b',
],
+ 'eof': [
+ files('extra/eof/manifest.ttl'),
+ ns_serdtest + 'eof/',
+ ],
+ 'eof_lax': [
+ '--ignore',
+ files('extra/eof/manifest.ttl'),
+ ns_serdtest + 'eof/',
+ '--',
+ '-l'
+ ],
'fast': [
files('extra/good/manifest.ttl'),
ns_serdtest + 'good/',
@@ -392,3 +305,11 @@ if is_variable('serdi')
)
endforeach
endif
+
+########
+# Lint #
+########
+
+if get_option('lint')
+ subdir('lint')
+endif
diff --git a/test/run_suite.py b/test/run_suite.py
index 2e93502f..84c74dd1 100755
--- a/test/run_suite.py
+++ b/test/run_suite.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright 2022-2023 David Robillard <d@drobilla.net>
+# Copyright 2022-2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: ISC
"""Run a "simple" one-pass RDF-based test suite for serd."""
@@ -43,7 +43,7 @@ def run_eval_test(base_uri, command, in_path, good_path, out_path):
syntax = util.syntax_from_path(out_path)
command = command + ["-o", syntax, in_path, base_uri]
- with subprocess.Popen(command, stdout=PIPE, encoding="utf-8") as proc:
+ with subprocess.Popen(command, encoding="utf-8", stdout=PIPE) as proc:
out = list(proc.stdout)
with open(good_path, "r", encoding="utf-8") as good:
@@ -58,19 +58,25 @@ def run_positive_test(base_uri, command, in_path):
return True
-def run_negative_test(base_uri, command, in_path):
+def run_negative_test(base_uri, command, in_path, ignore):
"""Run a negative syntax test and return whether the error was detected."""
if not os.path.exists(in_path):
raise RuntimeError("Input file missing: " + in_path)
command = command + [in_path, base_uri]
- proc = subprocess.run(command, check=False, stderr=PIPE, stdout=DEVNULL)
+ proc = subprocess.run(
+ command, check=False, encoding="utf-8", stderr=PIPE, stdout=DEVNULL
+ )
- if proc.returncode == 0:
+ if not ignore and proc.returncode == 0:
util.error("Unexpected successful return: " + in_path)
return False
+ if proc.returncode < 0:
+ util.error("Command seems to have crashed: " + in_path)
+ return False
+
if len(proc.stderr) == 0:
util.error("Command failed with no error output: " + in_path)
return False
@@ -86,7 +92,7 @@ def run_entry(args, entry, command, out_dir, suite_dir):
negative = "Negative" in entry[NS_RDF + "type"][0]
if negative and not args.lax:
- return run_negative_test(base, command, in_path)
+ return run_negative_test(base, command, in_path, args.ignore)
if NS_MF + "result" not in entry:
return run_positive_test(base, command, in_path)
@@ -151,6 +157,7 @@ def main():
)
parser.add_argument("--asserter", help="asserter URI for test report")
+ parser.add_argument("--ignore", action="store_true", help="ignore status")
parser.add_argument("--lax", action="store_true", help="tolerate errors")
parser.add_argument("--report", help="path to write result report to")
parser.add_argument("--reverse", action="store_true", help="reverse test")
diff --git a/test/serd_test_util/__init__.py b/test/serd_test_util/__init__.py
index 8027462b..c38100b5 100644
--- a/test/serd_test_util/__init__.py
+++ b/test/serd_test_util/__init__.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright 2022-2023 David Robillard <d@drobilla.net>
+# Copyright 2022-2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: ISC
"""Utilities for data-driven tests."""
@@ -8,10 +8,12 @@
# pylint: disable=consider-using-f-string
# pylint: disable=invalid-name
+import argparse
import datetime
import difflib
import os
import re
+import shlex
import subprocess
import sys
import urllib.parse
@@ -51,6 +53,33 @@ def error(message):
sys.stderr.write("\n")
+def wrapper_args(description, with_input=False):
+ """Return the command line arguments for a wrapped test."""
+
+ parser = argparse.ArgumentParser(description)
+ parser.add_argument("--serdi", default="./serdi", help="serdi executable")
+ parser.add_argument("--wrapper", default="", help="executable wrapper")
+ if with_input:
+ parser.add_argument("input", help="input file")
+
+ return parser.parse_args(sys.argv[1:])
+
+
+def command_output(wrapper, command, stdin=None):
+ """Run a command and check that stdout matches the expected output."""
+
+ proc = subprocess.run(
+ shlex.split(wrapper) + command,
+ capture_output=True,
+ check=True,
+ encoding="utf-8",
+ input=stdin,
+ )
+
+ assert wrapper or not proc.stderr
+ return proc.stdout
+
+
def print_result_summary(results):
"""Print test result summary to stdout or stderr as appropriate."""
@@ -128,12 +157,14 @@ def load_rdf(filename, base_uri, command_prefix):
cmd = command_prefix + [filename, base_uri]
proc = subprocess.run(
- cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True
+ cmd,
+ encoding="utf-8",
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ check=True,
)
for line in proc.stdout.splitlines():
- matches = re.match(
- r"<([^ ]*)> <([^ ]*)> <([^ ]*)> \.", line.decode("utf-8")
- )
+ matches = re.match(r"<([^ ]*)> <([^ ]*)> <([^ ]*)> \.", line)
if matches:
s, p, o = (matches.group(1), matches.group(2), matches.group(3))
if s not in model:
diff --git a/test/test_env.c b/test/test_env.c
index d51e0595..bd55e47e 100644
--- a/test/test_env.c
+++ b/test/test_env.c
@@ -3,7 +3,7 @@
#undef NDEBUG
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdint.h>
@@ -13,7 +13,9 @@
#define USTR(s) ((const uint8_t*)(s))
static SerdStatus
-count_prefixes(void* handle, const SerdNode* name, const SerdNode* uri)
+count_prefixes(void* const handle,
+ const SerdNode* const name,
+ const SerdNode* const uri)
{
(void)name;
(void)uri;
diff --git a/test/test_free_null.c b/test/test_free_null.c
index 96153c8d..2bc0b10c 100644
--- a/test/test_free_null.c
+++ b/test/test_free_null.c
@@ -3,7 +3,7 @@
#undef NDEBUG
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <stddef.h>
diff --git a/test/test_node.c b/test/test_node.c
index af14171b..c2fccf08 100644
--- a/test/test_node.c
+++ b/test/test_node.c
@@ -3,7 +3,7 @@
#undef NDEBUG
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <float.h>
@@ -24,7 +24,7 @@
#endif
static void
-test_strtod(double dbl, double max_delta)
+check_strtod(const double dbl, const double max_delta)
{
char buf[1024];
snprintf(buf, sizeof(buf), "%f", dbl);
@@ -56,7 +56,7 @@ test_string_to_double(void)
const double delta = fabs(num - expt_test_nums[i]);
assert(delta <= DBL_EPSILON);
- test_strtod(expt_test_nums[i], DBL_EPSILON);
+ check_strtod(expt_test_nums[i], DBL_EPSILON);
}
}
@@ -71,8 +71,8 @@ test_double_to_node(void)
-16.00001,
5.000000005,
0.0000000001,
- NAN,
- INFINITY};
+ (double)NAN,
+ (double)INFINITY};
const char* dbl_test_strs[] = {"0.0",
"9.0",
@@ -123,6 +123,7 @@ test_blob_to_node(void)
{
for (size_t size = 1; size < 256; ++size) {
uint8_t* const data = (uint8_t*)malloc(size);
+ assert(data);
for (size_t i = 0; i < size; ++i) {
data[i] = (uint8_t)((size + i) % 256);
}
@@ -285,7 +286,5 @@ main(void)
test_node_from_string();
test_node_from_substring();
test_uri_node_from_node();
-
- printf("Success\n");
return 0;
}
diff --git a/test/test_quiet.py b/test/test_quiet.py
index 42d05785..ff53e26e 100755
--- a/test/test_quiet.py
+++ b/test/test_quiet.py
@@ -1,25 +1,23 @@
#!/usr/bin/env python3
-# Copyright 2022 David Robillard <d@drobilla.net>
+# Copyright 2022-2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: ISC
-"""Test serdi quiet option."""
+"""Test quiet command-line option."""
-import argparse
-import sys
import shlex
import subprocess
-parser = argparse.ArgumentParser(description=__doc__)
+import serd_test_util as util
-parser.add_argument("--serdi", default="./serdi", help="path to serdi")
-parser.add_argument("--wrapper", default="", help="executable wrapper")
-parser.add_argument("input", help="invalid input file")
-
-args = parser.parse_args(sys.argv[1:])
+args = util.wrapper_args(__doc__, True)
command = shlex.split(args.wrapper) + [args.serdi, "-q", args.input]
proc = subprocess.run(
- command, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+ command,
+ encoding="utf-8",
+ check=False,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
)
assert proc.returncode != 0
diff --git a/test/test_reader.c b/test/test_reader.c
index b102f8a6..6c38e447 100644
--- a/test/test_reader.c
+++ b/test/test_reader.c
@@ -3,7 +3,7 @@
#undef NDEBUG
-#include "serd/serd.h"
+#include <serd/serd.h>
#ifdef _WIN32
# include <windows.h>
@@ -26,7 +26,7 @@ typedef struct {
} ReaderTest;
static SerdStatus
-test_base_sink(void* const handle, const SerdNode* const uri)
+base_sink(void* const handle, const SerdNode* const uri)
{
(void)uri;
@@ -36,9 +36,9 @@ test_base_sink(void* const handle, const SerdNode* const uri)
}
static SerdStatus
-test_prefix_sink(void* const handle,
- const SerdNode* const name,
- const SerdNode* const uri)
+prefix_sink(void* const handle,
+ const SerdNode* const name,
+ const SerdNode* const uri)
{
(void)name;
(void)uri;
@@ -49,14 +49,14 @@ test_prefix_sink(void* const handle,
}
static SerdStatus
-test_statement_sink(void* const handle,
- SerdStatementFlags flags,
- const SerdNode* const graph,
- const SerdNode* const subject,
- const SerdNode* const predicate,
- const SerdNode* const object,
- const SerdNode* const object_datatype,
- const SerdNode* const object_lang)
+statement_sink(void* const handle,
+ SerdStatementFlags flags,
+ const SerdNode* const graph,
+ const SerdNode* const subject,
+ const SerdNode* const predicate,
+ const SerdNode* const object,
+ const SerdNode* const object_datatype,
+ const SerdNode* const object_lang)
{
(void)flags;
(void)graph;
@@ -72,7 +72,7 @@ test_statement_sink(void* const handle,
}
static SerdStatus
-test_end_sink(void* const handle, const SerdNode* const node)
+end_sink(void* const handle, const SerdNode* const node)
{
(void)node;
@@ -85,13 +85,8 @@ static void
test_read_string(void)
{
ReaderTest rt = {0, 0, 0, 0};
- SerdReader* const reader = serd_reader_new(SERD_TURTLE,
- &rt,
- NULL,
- test_base_sink,
- test_prefix_sink,
- test_statement_sink,
- test_end_sink);
+ SerdReader* const reader = serd_reader_new(
+ SERD_TURTLE, &rt, NULL, base_sink, prefix_sink, statement_sink, end_sink);
assert(reader);
assert(serd_reader_get_handle(reader) == &rt);
@@ -113,7 +108,10 @@ test_read_string(void)
/// Reads a null byte after a statement, then succeeds again (like a socket)
static size_t
-eof_test_read(void* buf, size_t size, size_t nmemb, void* stream)
+eof_test_read(void* const buf,
+ const size_t size,
+ const size_t nmemb,
+ void* const stream)
{
assert(size == 1);
assert(nmemb == 1);
@@ -149,15 +147,15 @@ eof_test_read(void* buf, size_t size, size_t nmemb, void* stream)
}
static int
-eof_test_error(void* stream)
+eof_test_error(void* const stream)
{
(void)stream;
return 0;
}
-/// A read of a big page hits EOF then fails to read chunks immediately
+/// A read of a file stream hits EOF then fails to read chunks immediately
static void
-test_read_eof_by_page(const char* const path)
+test_read_eof_file(const char* const path)
{
FILE* const f = fopen(path, "w+b");
assert(f);
@@ -167,23 +165,25 @@ test_read_eof_by_page(const char* const path)
fseek(f, 0L, SEEK_SET);
ReaderTest rt = {0, 0, 0, 0};
- SerdReader* const reader = serd_reader_new(SERD_TURTLE,
- &rt,
- NULL,
- test_base_sink,
- test_prefix_sink,
- test_statement_sink,
- test_end_sink);
+ SerdReader* const reader = serd_reader_new(
+ SERD_TURTLE, &rt, NULL, base_sink, prefix_sink, statement_sink, end_sink);
+ fseek(f, 0L, SEEK_SET);
serd_reader_start_stream(reader, f, (const uint8_t*)"test", true);
-
assert(serd_reader_read_chunk(reader) == SERD_SUCCESS);
assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
+ serd_reader_end_stream(reader);
+ fseek(f, 0L, SEEK_SET);
+ serd_reader_start_stream(reader, f, (const uint8_t*)"test", false);
+ assert(serd_reader_read_chunk(reader) == SERD_SUCCESS);
+ assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
+ assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
serd_reader_end_stream(reader);
+
serd_reader_free(reader);
- fclose(f);
+ assert(!fclose(f));
}
// A byte-wise reader hits EOF once then continues (like a socket)
@@ -191,13 +191,8 @@ static void
test_read_eof_by_byte(void)
{
ReaderTest rt = {0, 0, 0, 0};
- SerdReader* const reader = serd_reader_new(SERD_TURTLE,
- &rt,
- NULL,
- test_base_sink,
- test_prefix_sink,
- test_statement_sink,
- test_end_sink);
+ SerdReader* const reader = serd_reader_new(
+ SERD_TURTLE, &rt, NULL, base_sink, prefix_sink, statement_sink, end_sink);
size_t n_reads = 0U;
serd_reader_start_source_stream(reader,
@@ -212,6 +207,7 @@ test_read_eof_by_byte(void)
assert(serd_reader_read_chunk(reader) == SERD_SUCCESS);
assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
+ serd_reader_end_stream(reader);
serd_reader_free(reader);
}
@@ -221,6 +217,7 @@ test_read_nquads_chunks(const char* const path)
static const char null = 0;
FILE* const f = fopen(path, "w+b");
+ assert(f);
// Write two statements, a null separator, then another statement
@@ -236,24 +233,21 @@ test_read_nquads_chunks(const char* const path)
fprintf(f,
"<http://example.org/s> <http://example.org/p3> "
- "<http://example.org/o3> .\n");
+ "<http://example.org/o3> .");
fseek(f, 0, SEEK_SET);
ReaderTest rt = {0, 0, 0, 0};
- SerdReader* const reader = serd_reader_new(SERD_NQUADS,
- &rt,
- NULL,
- test_base_sink,
- test_prefix_sink,
- test_statement_sink,
- test_end_sink);
+ SerdReader* const reader = serd_reader_new(
+ SERD_NQUADS, &rt, NULL, base_sink, prefix_sink, statement_sink, end_sink);
assert(reader);
assert(serd_reader_get_handle(reader) == &rt);
assert(f);
- SerdStatus st = serd_reader_start_stream(reader, f, NULL, false);
+ SerdStatus st = serd_reader_start_source_stream(
+ reader, (SerdSource)fread, (SerdStreamErrorFunc)ferror, f, NULL, 32U);
+
assert(st == SERD_SUCCESS);
// Read first statement
@@ -297,10 +291,11 @@ test_read_nquads_chunks(const char* const path)
assert(rt.n_end == 0);
assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
-
+ serd_reader_end_stream(reader);
serd_reader_free(reader);
- fclose(f);
- remove(path);
+
+ assert(!fclose(f));
+ assert(!remove(path));
}
static void
@@ -309,6 +304,7 @@ test_read_turtle_chunks(const char* const path)
static const char null = 0;
FILE* const f = fopen(path, "w+b");
+ assert(f);
// Write two statements separated by null characters
fprintf(f, "@base <http://example.org/base/> .\n");
@@ -316,24 +312,19 @@ test_read_turtle_chunks(const char* const path)
fprintf(f, "eg:s eg:p1 eg:o1 ;\n");
fprintf(f, " eg:p2 eg:o2 .\n");
fwrite(&null, sizeof(null), 1, f);
- fprintf(f, "eg:s eg:p [ eg:sp eg:so ] .\n");
- fwrite(&null, sizeof(null), 1, f);
+ fprintf(f, "eg:s eg:p [ eg:sp eg:so ] .");
fseek(f, 0, SEEK_SET);
ReaderTest rt = {0, 0, 0, 0};
- SerdReader* const reader = serd_reader_new(SERD_TURTLE,
- &rt,
- NULL,
- test_base_sink,
- test_prefix_sink,
- test_statement_sink,
- test_end_sink);
+ SerdReader* const reader = serd_reader_new(
+ SERD_TURTLE, &rt, NULL, base_sink, prefix_sink, statement_sink, end_sink);
assert(reader);
assert(serd_reader_get_handle(reader) == &rt);
assert(f);
- SerdStatus st = serd_reader_start_stream(reader, f, NULL, false);
+ SerdStatus st = serd_reader_start_source_stream(
+ reader, (SerdSource)fread, (SerdStreamErrorFunc)ferror, f, NULL, 32U);
assert(st == SERD_SUCCESS);
// Read base
@@ -393,10 +384,10 @@ test_read_turtle_chunks(const char* const path)
assert(rt.n_end == 1);
assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
-
+ serd_reader_end_stream(reader);
serd_reader_free(reader);
- fclose(f);
- remove(path);
+ assert(!fclose(f));
+ assert(!remove(path));
}
int
@@ -417,6 +408,7 @@ main(void)
const size_t nq_name_len = strlen(nq_name);
const size_t path_len = tmp_len + 1 + ttl_name_len;
char* const path = (char*)calloc(path_len + 1, 1);
+ assert(path);
memcpy(path, tmp, tmp_len + 1);
path[tmp_len] = '/';
@@ -428,7 +420,7 @@ main(void)
test_read_turtle_chunks(path);
test_read_string();
- test_read_eof_by_page(path);
+ test_read_eof_file(path);
test_read_eof_by_byte();
assert(!remove(path));
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index c229d1c5..78123110 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -1,9 +1,9 @@
-// Copyright 2011-2024 David Robillard <d@drobilla.net>
+// Copyright 2011-2025 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#undef NDEBUG
-#include "serd/serd.h"
+#include <serd/serd.h>
#ifdef _WIN32
# include <windows.h>
@@ -54,14 +54,14 @@ static const char* const doc_string =
"( eg:o ) eg:t eg:u .\n";
static SerdStatus
-test_statement_sink(void* handle,
- SerdStatementFlags flags,
- const SerdNode* graph,
- const SerdNode* subject,
- const SerdNode* predicate,
- const SerdNode* object,
- const SerdNode* object_datatype,
- const SerdNode* object_lang)
+test_statement_sink(void* const handle,
+ const SerdStatementFlags flags,
+ const SerdNode* const graph,
+ const SerdNode* const subject,
+ const SerdNode* const predicate,
+ const SerdNode* const object,
+ const SerdNode* const object_datatype,
+ const SerdNode* const object_lang)
{
(void)flags;
(void)subject;
@@ -103,42 +103,58 @@ quiet_error_sink(void* const handle, const SerdError* const e)
}
static void
-test_write_errors(void)
+check_write_error_offset(const SerdSyntax syntax,
+ const size_t offset,
+ const SerdStatus expected_status)
{
- ErrorContext ctx = {0U, 0U};
+ ErrorContext ctx = {0U, offset};
const SerdStyle style = (SerdStyle)(SERD_STYLE_STRICT | SERD_STYLE_CURIED);
+ SerdEnv* const env = serd_env_new(NULL);
+ assert(env);
+
+ SerdWriter* const writer =
+ serd_writer_new(syntax, style, env, NULL, faulty_sink, &ctx);
+ assert(writer);
+
+ SerdReader* const reader =
+ serd_reader_new(SERD_TRIG,
+ writer,
+ NULL,
+ (SerdBaseSink)serd_writer_set_base_uri,
+ (SerdPrefixSink)serd_writer_set_prefix,
+ (SerdStatementSink)serd_writer_write_statement,
+ (SerdEndSink)serd_writer_end_anon);
+ assert(reader);
+
+ serd_writer_set_error_sink(writer, quiet_error_sink, NULL);
+ serd_reader_set_error_sink(reader, quiet_error_sink, NULL);
+
+ const SerdStatus rst = serd_reader_read_string(reader, USTR(doc_string));
+ const SerdStatus wst = serd_writer_finish(writer);
+
+ serd_reader_free(reader);
+ serd_writer_free(writer);
+ serd_env_free(env);
- const size_t max_offsets[] = {0, 462, 1911, 2003, 462};
+ const SerdStatus st = rst ? rst : wst;
+ assert(st == expected_status);
+}
+
+static void
+test_write_errors(void)
+{
+ // Syntax-keyed array of output document sizes
+ static const size_t max_offsets[] = {0, 452, 1911, 2003, 466};
- // Test errors at different offsets to hit different code paths
for (unsigned s = 1; s <= (unsigned)SERD_TRIG; ++s) {
const SerdSyntax syntax = (SerdSyntax)s;
+
+ // Check successfully writing with enough space
+ check_write_error_offset(syntax, max_offsets[s], SERD_SUCCESS);
+
+ // Check write error at every offset in the output
for (size_t o = 0; o < max_offsets[s]; ++o) {
- ctx.n_written = 0;
- ctx.error_offset = o;
-
- SerdEnv* const env = serd_env_new(NULL);
- SerdWriter* const writer =
- serd_writer_new(syntax, style, env, NULL, faulty_sink, &ctx);
-
- SerdReader* const reader =
- serd_reader_new(SERD_TRIG,
- writer,
- NULL,
- (SerdBaseSink)serd_writer_set_base_uri,
- (SerdPrefixSink)serd_writer_set_prefix,
- (SerdStatementSink)serd_writer_write_statement,
- (SerdEndSink)serd_writer_end_anon);
-
- serd_reader_set_error_sink(reader, quiet_error_sink, NULL);
- serd_writer_set_error_sink(writer, quiet_error_sink, NULL);
-
- const SerdStatus st = serd_reader_read_string(reader, USTR(doc_string));
- assert(st == SERD_ERR_BAD_WRITE);
-
- serd_reader_free(reader);
- serd_writer_free(writer);
- serd_env_free(env);
+ check_write_error_offset(syntax, o, SERD_ERR_BAD_WRITE);
}
}
}
@@ -146,10 +162,12 @@ test_write_errors(void)
static void
test_writer(const char* const path)
{
- FILE* fd = fopen(path, "wb");
- SerdEnv* env = serd_env_new(NULL);
+ FILE* const fd = fopen(path, "wb");
assert(fd);
+ SerdEnv* const env = serd_env_new(NULL);
+ assert(env);
+
SerdWriter* writer =
serd_writer_new(SERD_TURTLE, (SerdStyle)0, env, NULL, serd_file_sink, fd);
assert(writer);
@@ -164,16 +182,21 @@ test_writer(const char* const path)
assert(serd_writer_end_anon(writer, NULL));
assert(serd_writer_get_env(writer) == env);
- uint8_t buf[] = {0x80, 0, 0, 0, 0};
- SerdNode s = serd_node_from_string(SERD_URI, USTR(""));
- SerdNode p = serd_node_from_string(SERD_URI, USTR("http://example.org/pred"));
- SerdNode o = serd_node_from_string(SERD_LITERAL, buf);
+ const uint8_t buf[] = {0x80, 0, 0, 0, 0};
+
+ const SerdNode s = serd_node_from_string(SERD_URI, USTR(""));
+ const SerdNode p =
+ serd_node_from_string(SERD_URI, USTR("http://example.org/pred"));
+ const SerdNode o = serd_node_from_string(SERD_LITERAL, buf);
+ const SerdNode t = serd_node_from_string(SERD_URI, USTR("urn:Type"));
+ const SerdNode l = serd_node_from_string(SERD_LITERAL, USTR("en"));
- // Write 3 invalid statements (should write nothing)
+ // Attempt to write invalid statements (should write nothing)
const SerdNode* junk[][5] = {{&s, &p, &SERD_NODE_NULL, NULL, NULL},
{&s, &SERD_NODE_NULL, &o, NULL, NULL},
{&SERD_NODE_NULL, &p, &o, NULL, NULL},
{&s, &o, &o, NULL, NULL},
+ {&s, &o, &o, &t, &l},
{&o, &p, &o, NULL, NULL},
{&s, &p, &SERD_NODE_NULL, NULL, NULL}};
for (size_t i = 0; i < sizeof(junk) / (sizeof(SerdNode*) * 5); ++i) {
@@ -187,13 +210,12 @@ test_writer(const char* const path)
junk[i][4]));
}
- const SerdNode t = serd_node_from_string(SERD_URI, USTR("urn:Type"));
- const SerdNode l = serd_node_from_string(SERD_LITERAL, USTR("en"));
+ // Write some valid statements
const SerdNode* good[][5] = {{&s, &p, &o, NULL, NULL},
+ {&s, &p, &lit, NULL, NULL},
{&s, &p, &o, &SERD_NODE_NULL, &SERD_NODE_NULL},
{&s, &p, &o, &t, NULL},
{&s, &p, &o, NULL, &l},
- {&s, &p, &o, &t, &l},
{&s, &p, &o, &t, &SERD_NODE_NULL},
{&s, &p, &o, &SERD_NODE_NULL, &l},
{&s, &p, &o, NULL, &SERD_NODE_NULL},
@@ -219,54 +241,17 @@ test_writer(const char* const path)
assert(!serd_writer_write_statement(
writer, 0, NULL, &s, &p, &bad_uri, NULL, NULL));
- // Write 1 valid statement
- o = serd_node_from_string(SERD_LITERAL, USTR("hello"));
- assert(!serd_writer_write_statement(writer, 0, NULL, &s, &p, &o, NULL, NULL));
-
- serd_writer_free(writer);
-
- // Test chunk sink
- SerdChunk chunk = {NULL, 0};
- writer = serd_writer_new(
- SERD_TURTLE, (SerdStyle)0, env, NULL, serd_chunk_sink, &chunk);
-
- o = serd_node_from_string(SERD_URI, USTR("http://example.org/base"));
- assert(!serd_writer_set_base_uri(writer, &o));
-
- serd_writer_free(writer);
- uint8_t* out = serd_chunk_sink_finish(&chunk);
-
- assert(!strcmp((const char*)out, "@base <http://example.org/base> .\n"));
- serd_free(out);
-
- // Test writing empty node
- SerdNode nothing = serd_node_from_string(SERD_NOTHING, USTR(""));
-
- chunk.buf = NULL;
- chunk.len = 0;
- writer = serd_writer_new(
- SERD_TURTLE, (SerdStyle)0, env, NULL, serd_chunk_sink, &chunk);
-
- assert(!serd_writer_write_statement(
- writer, 0, NULL, &s, &p, &nothing, NULL, NULL));
-
- assert(
- !strncmp((const char*)chunk.buf, "<>\n\t<http://example.org/pred> ", 30));
-
serd_writer_free(writer);
- out = serd_chunk_sink_finish(&chunk);
-
- assert(!strcmp((const char*)out, "<>\n\t<http://example.org/pred> .\n"));
- serd_free(out);
-
serd_env_free(env);
- fclose(fd);
+ assert(!fclose(fd));
}
static void
-test_reader(const char* path)
+test_reader(const char* const path)
{
- ReaderTest* rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
+ ReaderTest* const rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
+ assert(rt);
+
SerdReader* reader = serd_reader_new(
SERD_TURTLE, rt, free, NULL, NULL, test_statement_sink, NULL);
@@ -294,7 +279,7 @@ test_reader(const char* path)
const SerdStatus st = serd_reader_read_file(reader, USTR(path));
assert(!st);
- assert(rt->n_statement == 13);
+ assert(rt->n_statement == 12);
assert(rt->graph && rt->graph->buf &&
!strcmp((const char*)rt->graph->buf, "http://example.org/"));
@@ -319,6 +304,7 @@ main(void)
const size_t ttl_name_len = strlen(ttl_name);
const size_t path_len = tmp_len + 1 + ttl_name_len;
char* const path = (char*)calloc(path_len + 1, 1);
+ assert(path);
memcpy(path, tmp, tmp_len + 1);
path[tmp_len] = '/';
@@ -332,6 +318,5 @@ main(void)
assert(!remove(path));
free(path);
- printf("Success\n");
return 0;
}
diff --git a/test/test_stdin.py b/test/test_stdin.py
index f976ca52..fb01f4ee 100755
--- a/test/test_stdin.py
+++ b/test/test_stdin.py
@@ -7,37 +7,14 @@
# pylint: disable=consider-using-f-string
-import argparse
-import sys
-import shlex
-import subprocess
-import tempfile
+import serd_test_util as util
-parser = argparse.ArgumentParser(description=__doc__)
+args = util.wrapper_args(__doc__)
+command = [args.serdi, "-i", "ntriples", "-", "http://example.org"]
-parser.add_argument("--serdi", default="./serdi", help="path to serdi")
-parser.add_argument("--wrapper", default="", help="executable wrapper")
+DOC = "<{0}s> <{0}p> <{0}o> .".format("http://example.org/")
-args = parser.parse_args(sys.argv[1:])
-command = shlex.split(args.wrapper) + [args.serdi, "-"]
+lines = util.command_output(args.wrapper, command, DOC).splitlines(True)
-DOCUMENT = "<{0}s> <{0}p> <{0}o> .".format("http://example.org/")
-
-with tempfile.TemporaryFile() as out:
- proc = subprocess.run(
- command,
- check=False,
- encoding="utf-8",
- input=DOCUMENT,
- stdout=out,
- stderr=subprocess.PIPE,
- )
-
- assert proc.returncode == 0
- assert args.wrapper or len(proc.stderr) == 0
-
- out.seek(0)
- lines = out.readlines()
-
- assert len(lines) == 1
- assert lines[0].decode("utf-8").strip() == DOCUMENT
+assert len(lines) == 1
+assert lines[0].strip() == DOC
diff --git a/test/test_string.c b/test/test_string.c
index 23835ca9..86b5d19e 100644
--- a/test/test_string.c
+++ b/test/test_string.c
@@ -3,11 +3,10 @@
#undef NDEBUG
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdint.h>
-#include <stdio.h>
#include <string.h>
static void
@@ -57,7 +56,5 @@ main(void)
{
test_strlen();
test_strerror();
-
- printf("Success\n");
return 0;
}
diff --git a/test/test_uri.c b/test/test_uri.c
index fc5eab71..96fde600 100644
--- a/test/test_uri.c
+++ b/test/test_uri.c
@@ -3,12 +3,11 @@
#undef NDEBUG
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <string.h>
#define USTR(s) ((const uint8_t*)(s))
@@ -37,11 +36,11 @@ test_uri_string_has_scheme(void)
}
static void
-test_file_uri(const char* const hostname,
- const char* const path,
- const bool escape,
- const char* const expected_uri,
- const char* expected_path)
+check_file_uri(const char* const hostname,
+ const char* const path,
+ const bool escape,
+ const char* const expected_uri,
+ const char* expected_path)
{
if (!expected_path) {
expected_path = path;
@@ -53,6 +52,7 @@ test_file_uri(const char* const hostname,
uint8_t* out_path =
serd_file_uri_parse((const uint8_t*)node.buf, &out_hostname);
+ assert(out_path);
assert(!strcmp((const char*)node.buf, expected_uri));
assert((hostname && out_hostname) || (!hostname && !out_hostname));
assert(!hostname || !strcmp(hostname, (const char*)out_hostname));
@@ -69,40 +69,27 @@ test_file_uri(const char* const hostname,
#endif
static void
-test_uri_to_path(void)
+check_uri_to_path(const char* const uri, const char* const expected_path)
{
- assert(!strcmp(
- (const char*)serd_uri_to_path((const uint8_t*)"file:///home/user/foo.ttl"),
- "/home/user/foo.ttl"));
-
- assert(!strcmp((const char*)serd_uri_to_path(
- (const uint8_t*)"file://localhost/home/user/foo.ttl"),
- "/home/user/foo.ttl"));
-
- assert(!serd_uri_to_path((const uint8_t*)"file:illegal/file/uri"));
-
- assert(!strcmp(
- (const char*)serd_uri_to_path((const uint8_t*)"file:///c:/awful/system"),
- "c:/awful/system"));
-
- assert(!strcmp(
- (const char*)serd_uri_to_path((const uint8_t*)"file:///c:awful/system"),
- "/c:awful/system"));
-
- assert(!strcmp((const char*)serd_uri_to_path((const uint8_t*)"file:///0/1"),
- "/0/1"));
-
- assert(
- !strcmp((const char*)serd_uri_to_path((const uint8_t*)"C:\\Windows\\Sucks"),
- "C:\\Windows\\Sucks"));
-
- assert(
- !strcmp((const char*)serd_uri_to_path((const uint8_t*)"C|/Windows/Sucks"),
- "C|/Windows/Sucks"));
+ const uint8_t* const path = serd_uri_to_path((const uint8_t*)uri);
+ assert(path);
+ assert(!strcmp((const char*)path, expected_path));
+}
+static void
+test_uri_to_path(void)
+{
+ assert(!serd_uri_to_path((const uint8_t*)"file:invalid/file/uri"));
assert(!serd_uri_to_path((const uint8_t*)"http://example.org/path"));
- assert(!strcmp((const char*)serd_uri_to_path((const uint8_t*)"rel"), "rel"));
+ check_uri_to_path("file:///home/user/foo.ttl", "/home/user/foo.ttl");
+ check_uri_to_path("file://localhost/home/user/foo.ttl", "/home/user/foo.ttl");
+ check_uri_to_path("file:///c:/awful/system", "c:/awful/system");
+ check_uri_to_path("file:///c:awful/system", "/c:awful/system");
+ check_uri_to_path("file:///0/1", "/0/1");
+ check_uri_to_path("C:\\Windows\\Sucks", "C:\\Windows\\Sucks");
+ check_uri_to_path("C|/Windows/Sucks", "C|/Windows/Sucks");
+ check_uri_to_path("rel", "rel");
}
#if defined(__GNUC__)
@@ -112,76 +99,79 @@ test_uri_to_path(void)
static void
test_uri_parsing(void)
{
- test_file_uri(NULL, "C:/My 100%", true, "file:///C:/My%20100%%", NULL);
- test_file_uri(NULL, "/foo/bar", true, "file:///foo/bar", NULL);
- test_file_uri("bhost", "/foo/bar", true, "file://bhost/foo/bar", NULL);
- test_file_uri(NULL, "a/relative path", false, "a/relative path", NULL);
- test_file_uri(
+ check_file_uri(NULL, "C:/My 100%", true, "file:///C:/My%20100%%", NULL);
+ check_file_uri(NULL, "/foo/bar", true, "file:///foo/bar", NULL);
+ check_file_uri("bhost", "/foo/bar", true, "file://bhost/foo/bar", NULL);
+ check_file_uri(NULL, "a/relative path", false, "a/relative path", NULL);
+ check_file_uri(
NULL, "a/relative <path>", true, "a/relative%20%3Cpath%3E", NULL);
#ifdef _WIN32
- test_file_uri(
+ check_file_uri(
NULL, "C:\\My 100%", true, "file:///C:/My%20100%%", "C:/My 100%");
- test_file_uri(NULL,
- "\\drive\\relative",
- true,
- "file:///drive/relative",
- "/drive/relative");
-
- test_file_uri(NULL,
- "C:\\Program Files\\Serd",
- true,
- "file:///C:/Program%20Files/Serd",
- "C:/Program Files/Serd");
-
- test_file_uri("ahost",
- "C:\\Pointless Space",
- true,
- "file://ahost/C:/Pointless%20Space",
- "C:/Pointless Space");
+ check_file_uri(NULL,
+ "\\drive\\relative",
+ true,
+ "file:///drive/relative",
+ "/drive/relative");
+
+ check_file_uri(NULL,
+ "C:\\Program Files\\Serd",
+ true,
+ "file:///C:/Program%20Files/Serd",
+ "C:/Program Files/Serd");
+
+ check_file_uri("ahost",
+ "C:\\Pointless Space",
+ true,
+ "file://ahost/C:/Pointless%20Space",
+ "C:/Pointless Space");
#else
/* What happens with Windows paths on other platforms is a bit weird, but
more or less unavoidable. It doesn't work to interpret backslashes as
path separators on any other platform. */
- test_file_uri("ahost",
- "C:\\Pointless Space",
- true,
- "file://ahost/C:%5CPointless%20Space",
- "/C:\\Pointless Space");
-
- test_file_uri(NULL,
- "\\drive\\relative",
- true,
- "%5Cdrive%5Crelative",
- "\\drive\\relative");
-
- test_file_uri(NULL,
- "C:\\Program Files\\Serd",
- true,
- "file:///C:%5CProgram%20Files%5CSerd",
- "/C:\\Program Files\\Serd");
-
- test_file_uri("ahost",
- "C:\\Pointless Space",
- true,
- "file://ahost/C:%5CPointless%20Space",
- "/C:\\Pointless Space");
+ check_file_uri("ahost",
+ "C:\\Pointless Space",
+ true,
+ "file://ahost/C:%5CPointless%20Space",
+ "/C:\\Pointless Space");
+
+ check_file_uri(NULL,
+ "\\drive\\relative",
+ true,
+ "%5Cdrive%5Crelative",
+ "\\drive\\relative");
+
+ check_file_uri(NULL,
+ "C:\\Program Files\\Serd",
+ true,
+ "file:///C:%5CProgram%20Files%5CSerd",
+ "/C:\\Program Files\\Serd");
+
+ check_file_uri("ahost",
+ "C:\\Pointless Space",
+ true,
+ "file://ahost/C:%5CPointless%20Space",
+ "/C:\\Pointless Space");
#endif
// Test tolerance of NULL hostname parameter
uint8_t* const hosted = serd_file_uri_parse(USTR("file://host/path"), NULL);
+ assert(hosted);
assert(!strcmp((const char*)hosted, "/path"));
serd_free(hosted);
// Test tolerance of parsing junk URI escapes
uint8_t* const junk1 = serd_file_uri_parse(USTR("file:///foo/%0Xbar"), NULL);
+ assert(junk1);
assert(!strcmp((const char*)junk1, "/foo/bar"));
serd_free(junk1);
uint8_t* const junk2 = serd_file_uri_parse(USTR("file:///foo/%X0bar"), NULL);
+ assert(junk2);
assert(!strcmp((const char*)junk2, "/foo/bar"));
serd_free(junk2);
}
@@ -207,8 +197,8 @@ test_uri_from_string(void)
serd_node_free(&base);
}
-static inline bool
-chunk_equals(const SerdChunk* a, const SerdChunk* b)
+static bool
+chunk_equals(const SerdChunk* const a, const SerdChunk* const b)
{
return (!a->len && !b->len && !a->buf && !b->buf) ||
(a->len && b->len && a->buf && b->buf &&
@@ -382,7 +372,5 @@ main(void)
test_uri_parsing();
test_uri_from_string();
test_relative_uri();
-
- printf("Success\n");
return 0;
}
diff --git a/test/test_write_error.py b/test/test_write_error.py
index 35fde232..93b0249a 100755
--- a/test/test_write_error.py
+++ b/test/test_write_error.py
@@ -1,33 +1,32 @@
#!/usr/bin/env python3
-# Copyright 2022-2023 David Robillard <d@drobilla.net>
+# Copyright 2022-2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: ISC
"""Test errors writing to a file."""
-import argparse
import sys
import shlex
import subprocess
import os
-parser = argparse.ArgumentParser(description=__doc__)
+import serd_test_util as util
-parser.add_argument("--serdi", default="./serdi", help="path to serdi")
-parser.add_argument("--wrapper", default="", help="executable wrapper")
-parser.add_argument("input", help="valid input file")
-
-args = parser.parse_args(sys.argv[1:])
+args = util.wrapper_args(__doc__, True)
command = shlex.split(args.wrapper) + [args.serdi, args.input]
if os.path.exists("/dev/full"):
with open("/dev/full", "w", encoding="utf-8") as out:
proc = subprocess.run(
- command, check=False, stdout=out, stderr=subprocess.PIPE
+ command,
+ encoding="utf-8",
+ check=False,
+ stdout=out,
+ stderr=subprocess.PIPE,
)
assert proc.returncode != 0
- assert "error" in proc.stderr.decode("utf-8")
+ assert "error" in proc.stderr
else:
sys.stderr.write("warning: /dev/full not present, skipping test")
diff --git a/test/test_writer.c b/test/test_writer.c
index 6c765148..51f5d9ed 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -1,9 +1,9 @@
-// Copyright 2011-2023 David Robillard <d@drobilla.net>
+// Copyright 2011-2025 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#undef NDEBUG
-#include "serd/serd.h"
+#include <serd/serd.h>
#include <assert.h>
#include <stdint.h>
@@ -21,7 +21,6 @@ test_write_long_literal(void)
SerdChunk chunk = {NULL, 0};
SerdWriter* writer = serd_writer_new(
SERD_TURTLE, (SerdStyle)0, env, NULL, serd_chunk_sink, &chunk);
-
assert(writer);
SerdNode s = serd_node_from_string(SERD_URI, USTR(NS_EG "s"));
@@ -30,18 +29,19 @@ test_write_long_literal(void)
serd_node_from_string(SERD_LITERAL, USTR("hello \"\"\"world\"\"\"!"));
assert(!serd_writer_write_statement(writer, 0, NULL, &s, &p, &o, NULL, NULL));
-
- serd_writer_free(writer);
- serd_env_free(env);
-
- uint8_t* out = serd_chunk_sink_finish(&chunk);
+ assert(!serd_writer_finish(writer));
static const char* const expected =
"<http://example.org/s>\n"
"\t<http://example.org/p> \"\"\"hello \"\"\\\"world\"\"\\\"!\"\"\" .\n";
+ uint8_t* const out = serd_chunk_sink_finish(&chunk);
+ assert(out);
assert(!strcmp((char*)out, expected));
serd_free(out);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
}
static void
@@ -51,7 +51,6 @@ test_write_nested_anon(void)
SerdChunk chunk = {NULL, 0};
SerdWriter* writer = serd_writer_new(
SERD_TURTLE, (SerdStyle)0, env, NULL, serd_chunk_sink, &chunk);
-
assert(writer);
SerdNode s0 = serd_node_from_string(SERD_URI, USTR(NS_EG "s0"));
@@ -96,11 +95,7 @@ test_write_nested_anon(void)
writer, SERD_ANON_CONT, NULL, &b0, &p4, &o4, NULL, NULL));
assert(!serd_writer_end_anon(writer, &b0));
-
- serd_writer_free(writer);
- serd_env_free(env);
-
- uint8_t* const out = serd_chunk_sink_finish(&chunk);
+ assert(!serd_writer_finish(writer));
static const char* const expected =
"<http://example.org/s0>\n"
@@ -112,9 +107,13 @@ test_write_nested_anon(void)
"\t\t<http://example.org/p4> <http://example.org/o4>\n"
"\t] .\n";
- fprintf(stderr, "%s\n", out);
+ uint8_t* const out = serd_chunk_sink_finish(&chunk);
+ assert(out);
assert(!strcmp((char*)out, expected));
serd_free(out);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
}
static size_t
@@ -133,6 +132,7 @@ test_writer_cleanup(void)
SerdEnv* env = serd_env_new(NULL);
SerdWriter* writer =
serd_writer_new(SERD_TURTLE, (SerdStyle)0U, env, NULL, null_sink, NULL);
+ assert(writer);
SerdNode s = serd_node_from_string(SERD_URI, USTR(NS_EG "s"));
SerdNode p = serd_node_from_string(SERD_URI, USTR(NS_EG "p"));
@@ -167,10 +167,12 @@ test_writer_cleanup(void)
}
// Finish writing without terminating nodes
- assert(!(st = serd_writer_finish(writer)));
+ st = serd_writer_finish(writer);
+ assert(!st);
// Set the base to an empty URI
- assert(!(st = serd_writer_set_base_uri(writer, NULL)));
+ st = serd_writer_set_base_uri(writer, NULL);
+ assert(!st);
// Free (which could leak if the writer doesn't clean up the stack properly)
serd_writer_free(writer);
@@ -184,6 +186,7 @@ test_write_bad_anon_stack(void)
SerdEnv* env = serd_env_new(NULL);
SerdWriter* writer =
serd_writer_new(SERD_TURTLE, (SerdStyle)0U, env, NULL, null_sink, NULL);
+ assert(writer);
SerdNode s = serd_node_from_string(SERD_URI, USTR(NS_EG "s"));
SerdNode p = serd_node_from_string(SERD_URI, USTR(NS_EG "p"));
@@ -191,8 +194,9 @@ test_write_bad_anon_stack(void)
SerdNode b1 = serd_node_from_string(SERD_BLANK, USTR("b1"));
SerdNode b2 = serd_node_from_string(SERD_BLANK, USTR("b2"));
- assert(!(st = serd_writer_write_statement(
- writer, SERD_ANON_O_BEGIN, NULL, &s, &p, &b0, NULL, NULL)));
+ st = serd_writer_write_statement(
+ writer, SERD_ANON_O_BEGIN, NULL, &s, &p, &b0, NULL, NULL);
+ assert(!st);
// (missing call to end the anonymous node here)
@@ -201,7 +205,8 @@ test_write_bad_anon_stack(void)
assert(st == SERD_ERR_BAD_ARG);
- assert(!(st = serd_writer_finish(writer)));
+ st = serd_writer_finish(writer);
+ assert(!st);
serd_writer_free(writer);
serd_env_free(env);
}
@@ -209,14 +214,9 @@ test_write_bad_anon_stack(void)
static void
test_strict_write(void)
{
- const char* const path = "serd_strict_write_test.ttl";
- FILE* const fd = fopen(path, "wb");
- assert(fd);
-
SerdEnv* const env = serd_env_new(NULL);
SerdWriter* const writer = serd_writer_new(
- SERD_TURTLE, (SerdStyle)SERD_STYLE_STRICT, env, NULL, null_sink, fd);
-
+ SERD_TURTLE, (SerdStyle)SERD_STYLE_STRICT, env, NULL, null_sink, NULL);
assert(writer);
const uint8_t bad_str[] = {0xFF, 0x90, 'h', 'i', 0};
@@ -235,8 +235,6 @@ test_strict_write(void)
serd_writer_free(writer);
serd_env_free(env);
- fclose(fd);
- remove(path);
}
// Produce a write error without setting errno
@@ -252,19 +250,129 @@ error_sink(const void* const buf, const size_t len, void* const stream)
static void
test_write_error(void)
{
- SerdEnv* const env = serd_env_new(NULL);
- SerdWriter* writer = NULL;
- SerdStatus st = SERD_SUCCESS;
-
- SerdNode u = serd_node_from_string(SERD_URI, USTR("http://example.com/u"));
+ SerdEnv* const env = serd_env_new(NULL);
- writer =
+ SerdWriter* const writer =
serd_writer_new(SERD_TURTLE, (SerdStyle)0, env, NULL, error_sink, NULL);
assert(writer);
- st = serd_writer_write_statement(writer, 0U, NULL, &u, &u, &u, NULL, NULL);
+
+ SerdNode u = serd_node_from_string(SERD_URI, USTR("http://example.com/u"));
+
+ const SerdStatus st =
+ serd_writer_write_statement(writer, 0U, NULL, &u, &u, &u, NULL, NULL);
assert(st == SERD_ERR_BAD_WRITE);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+}
+
+static void
+test_chunk_sink(void)
+{
+ SerdEnv* const env = serd_env_new(NULL);
+ assert(env);
+
+ SerdChunk chunk = {NULL, 0};
+ SerdWriter* const writer = serd_writer_new(
+ SERD_TURTLE, (SerdStyle)0, env, NULL, serd_chunk_sink, &chunk);
+ assert(writer);
+
+ const SerdNode base =
+ serd_node_from_string(SERD_URI, USTR("http://example.org/base"));
+ assert(!serd_writer_set_base_uri(writer, &base));
+ assert(!serd_writer_finish(writer));
+
+ uint8_t* const out = serd_chunk_sink_finish(&chunk);
+ assert(out);
+ assert(!strcmp((const char*)out, "@base <http://example.org/base> .\n"));
+ serd_free(out);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+}
+
+static void
+test_write_nothing_node(void)
+{
+ SerdEnv* const env = serd_env_new(NULL);
+ assert(env);
+
+ SerdChunk chunk = {NULL, 0};
+ SerdWriter* const writer = serd_writer_new(
+ SERD_TURTLE, (SerdStyle)0, env, NULL, serd_chunk_sink, &chunk);
+ assert(writer);
+
+ SerdNode s = serd_node_from_string(SERD_URI, USTR(""));
+ SerdNode p = serd_node_from_string(SERD_URI, USTR("http://example.org/pred"));
+ SerdNode o = serd_node_from_string(SERD_NOTHING, USTR(""));
+ assert(serd_writer_write_statement(writer, 0, NULL, &s, &p, &o, NULL, NULL) ==
+ SERD_ERR_BAD_ARG);
+
+ assert(!chunk.buf);
serd_writer_free(writer);
+ serd_env_free(env);
+}
+
+static void
+test_write_bad_statement(void)
+{
+ SerdEnv* const env = serd_env_new(NULL);
+ assert(env);
+
+ SerdChunk chunk = {NULL, 0};
+ SerdWriter* const writer = serd_writer_new(
+ SERD_TURTLE, (SerdStyle)0, env, NULL, serd_chunk_sink, &chunk);
+ assert(writer);
+ SerdNode s = serd_node_from_string(SERD_URI, USTR("http://example.org/s"));
+ SerdNode p = serd_node_from_string(SERD_URI, USTR("http://example.org/p"));
+ SerdNode o = serd_node_from_string(SERD_URI, USTR("http://example.org/o"));
+ SerdNode l = serd_node_from_string(SERD_LITERAL, USTR("lang"));
+
+ assert(serd_writer_write_statement(
+ writer,
+ (SerdStatementFlags)(SERD_ANON_S_BEGIN | SERD_LIST_S_BEGIN),
+ NULL,
+ &s,
+ &p,
+ &o,
+ NULL,
+ NULL) == SERD_ERR_BAD_ARG);
+
+ assert(serd_writer_write_statement(
+ writer,
+ (SerdStatementFlags)(SERD_EMPTY_S | SERD_LIST_S_BEGIN),
+ NULL,
+ &s,
+ &p,
+ &o,
+ NULL,
+ NULL) == SERD_ERR_BAD_ARG);
+
+ assert(serd_writer_write_statement(
+ writer,
+ (SerdStatementFlags)(SERD_ANON_O_BEGIN | SERD_LIST_O_BEGIN),
+ NULL,
+ &s,
+ &p,
+ &o,
+ NULL,
+ NULL) == SERD_ERR_BAD_ARG);
+
+ assert(serd_writer_write_statement(
+ writer,
+ (SerdStatementFlags)(SERD_EMPTY_O | SERD_LIST_O_BEGIN),
+ NULL,
+ &s,
+ &p,
+ &o,
+ NULL,
+ NULL) == SERD_ERR_BAD_ARG);
+
+ assert(serd_writer_write_statement(writer, 0U, NULL, &s, &p, &o, &o, &l) ==
+ SERD_ERR_BAD_ARG);
+
+ serd_writer_free(writer);
serd_env_free(env);
}
@@ -277,6 +385,9 @@ main(void)
test_write_bad_anon_stack();
test_strict_write();
test_write_error();
+ test_chunk_sink();
+ test_write_nothing_node();
+ test_write_bad_statement();
return 0;
}