aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2025-02-25 08:57:57 -0500
committerDavid Robillard <d@drobilla.net>2025-03-16 20:22:36 -0400
commit5ecac71066475b2bb55758ae138afeebeaa5b948 (patch)
tree2bbf63c80c713420d846105ac9eae0753e39fc4e
parenteb7e973b25a8e5556b2f1175d8375e5e513ba664 (diff)
downloadserd-5ecac71066475b2bb55758ae138afeebeaa5b948.tar.gz
serd-5ecac71066475b2bb55758ae138afeebeaa5b948.tar.bz2
serd-5ecac71066475b2bb55758ae138afeebeaa5b948.zip
Refuse to write incoherent statements
-rw-r--r--NEWS3
-rw-r--r--src/writer.c17
-rw-r--r--test/test_reader_writer.c15
-rw-r--r--test/test_writer.c72
4 files changed, 87 insertions, 20 deletions
diff --git a/NEWS b/NEWS
index 3b923a9e..12d5b583 100644
--- a/NEWS
+++ b/NEWS
@@ -2,9 +2,10 @@ serd (0.32.5) unstable; urgency=medium
* Fix handling of some invalid EOF cases in lax mode
* Fix invalid characters in error messages
+ * Refuse to write incoherent statements
* Remove project and version number from man page OS field
- -- David Robillard <d@drobilla.net> Thu, 30 Jan 2025 20:28:00 +0000
+ -- David Robillard <d@drobilla.net> Tue, 25 Feb 2025 14:22:43 +0000
serd (0.32.4) stable; urgency=medium
diff --git a/src/writer.c b/src/writer.c
index f0a9ad16..ab237099 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"
@@ -926,10 +926,6 @@ serd_writer_write_statement(SerdWriter* const 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
@@ -938,6 +934,17 @@ serd_writer_write_statement(SerdWriter* const 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));
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index 61874bc7..829caff5 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -168,15 +168,19 @@ test_writer(const char* const path)
const 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 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"));
// 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) {
@@ -191,14 +195,11 @@ test_writer(const char* const path)
}
// Write some valid statements
- const SerdNode t = serd_node_from_string(SERD_URI, USTR("urn:Type"));
- const SerdNode l = serd_node_from_string(SERD_LITERAL, USTR("en"));
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},
@@ -260,7 +261,7 @@ test_reader(const char* const 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/"));
diff --git a/test/test_writer.c b/test/test_writer.c
index 27ce6138..7f8d2f94 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -309,17 +309,74 @@ test_write_nothing_node(void)
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));
-
- assert(
- !strncmp((const char*)chunk.buf, "<>\n\t<http://example.org/pred> ", 30));
+ 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);
+}
- uint8_t* const out = serd_chunk_sink_finish(&chunk);
- assert(!strcmp((const char*)out, "<>\n\t<http://example.org/pred> .\n"));
- serd_free(out);
+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);
}
@@ -334,6 +391,7 @@ main(void)
test_write_error();
test_chunk_sink();
test_write_nothing_node();
+ test_write_bad_statement();
return 0;
}