diff options
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 @@ -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 @@ -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. @@ -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 { @@ -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> @@ -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; } @@ -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> @@ -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))) { \ @@ -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, ¬hing, 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; } |