aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2024-06-23 16:36:12 -0400
committerDavid Robillard <d@drobilla.net>2024-06-23 16:36:20 -0400
commitd2663582ca9305a36f9107cf95b951393a1563dc (patch)
tree39bbc053c24a89f03399a4d5dfd2f63f85b74db2
parent24c22074662711dbb6bac2eadca557f98f356a9a (diff)
downloadserd-d2663582ca9305a36f9107cf95b951393a1563dc.tar.gz
serd-d2663582ca9305a36f9107cf95b951393a1563dc.tar.bz2
serd-d2663582ca9305a36f9107cf95b951393a1563dc.zip
Gracefully handle failure to terminate written anonymous nodes
This case shouldn't be reachable when driven directly from a reader, but can be reached by invalid use of the writer in an application.
-rw-r--r--src/writer.c28
-rw-r--r--test/test_writer.c30
2 files changed, 44 insertions, 14 deletions
diff --git a/src/writer.c b/src/writer.c
index 96e6135d..58d7e64f 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -1041,24 +1041,24 @@ serd_writer_write_statement(SerdWriter* writer,
} else {
// No abbreviation
- if (serd_stack_is_empty(&writer->anon_stack)) {
- if (writer->context.subject.type) {
- TRY(st, write_sep(writer, SEP_END_S));
- }
- if (writer->last_sep == SEP_END_S || writer->last_sep == SEP_END_DIRECT) {
- TRY(st, write_newline(writer));
- }
+ if (!serd_stack_is_empty(&writer->anon_stack)) {
+ return SERD_ERR_BAD_ARG;
+ }
- TRY(st, write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags));
- if ((flags & (SERD_ANON_S_BEGIN | SERD_LIST_S_BEGIN))) {
- TRY(st, write_sep(writer, SEP_ANON_S_P));
- } else {
- TRY(st, write_sep(writer, SEP_S_P));
- }
+ if (writer->context.subject.type) {
+ TRY(st, write_sep(writer, SEP_END_S));
+ }
- } else {
+ if (writer->last_sep == SEP_END_S || writer->last_sep == SEP_END_DIRECT) {
+ TRY(st, write_newline(writer));
+ }
+
+ TRY(st, write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags));
+ if ((flags & (SERD_ANON_S_BEGIN | SERD_LIST_S_BEGIN))) {
TRY(st, write_sep(writer, SEP_ANON_S_P));
+ } else {
+ TRY(st, write_sep(writer, SEP_S_P));
}
reset_context(writer, 0U);
diff --git a/test/test_writer.c b/test/test_writer.c
index 75b311cf..b478f052 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -176,6 +176,35 @@ test_writer_cleanup(void)
}
static void
+test_write_bad_anon_stack(void)
+{
+ SerdStatus st = SERD_SUCCESS;
+ SerdEnv* env = serd_env_new(NULL);
+ SerdWriter* writer =
+ serd_writer_new(SERD_TURTLE, (SerdStyle)0U, env, NULL, null_sink, NULL);
+
+ 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 b0 = serd_node_from_string(SERD_BLANK, USTR("b0"));
+ 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)));
+
+ // (missing call to end the anonymous node here)
+
+ st = serd_writer_write_statement(
+ writer, SERD_ANON_O_BEGIN, NULL, &b1, &p, &b2, NULL, NULL);
+
+ assert(st == SERD_ERR_BAD_ARG);
+
+ assert(!(st = serd_writer_finish(writer)));
+ serd_writer_free(writer);
+ serd_env_free(env);
+}
+
+static void
test_strict_write(void)
{
const char* const path = "serd_strict_write_test.ttl";
@@ -243,6 +272,7 @@ main(void)
test_write_long_literal();
test_write_nested_anon();
test_writer_cleanup();
+ test_write_bad_anon_stack();
test_strict_write();
test_write_error();