aboutsummaryrefslogtreecommitdiffstats
path: root/tools/serd-pipe.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/serd-pipe.c')
-rw-r--r--tools/serd-pipe.c209
1 files changed, 209 insertions, 0 deletions
diff --git a/tools/serd-pipe.c b/tools/serd-pipe.c
new file mode 100644
index 00000000..75b3e0d4
--- /dev/null
+++ b/tools/serd-pipe.c
@@ -0,0 +1,209 @@
+/*
+ Copyright 2011-2021 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.
+
+ THIS 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.
+*/
+
+#include "console.h"
+
+#include "serd/serd.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/* Application (after parsing command-line arguments) */
+
+// All options
+typedef struct {
+ SerdCommonOptions common;
+ const char* root_uri;
+ const char* input_string;
+ char* const* inputs;
+ intptr_t n_inputs;
+ bool canonical;
+ bool quiet;
+} Options;
+
+// Run the tool using the given options
+static SerdStatus
+run(const Options opts)
+{
+ SerdTool app = {NULL, NULL, NULL, NULL};
+
+ // Set up the writing environment
+ SerdStatus st = SERD_SUCCESS;
+ if ((st = serd_tool_setup(&app, "serd-pipe", opts.common))) {
+ serd_tool_cleanup(app);
+ return st;
+ }
+
+ if (opts.quiet) {
+ serd_set_log_func(app.world, serd_quiet_log_func, NULL);
+ }
+
+ serd_writer_set_root_uri(app.writer, SERD_STRING(opts.root_uri));
+
+ // Set up the output pipeline: [canon] -> writer
+ const SerdSink* const target = serd_writer_sink(app.writer);
+ const SerdSink* sink = target;
+ SerdSink* canon = NULL;
+ if (opts.canonical) {
+ canon = serd_canon_new(app.world, target, opts.common.input.flags);
+ sink = canon;
+ }
+
+ if (opts.input_string) {
+ SerdByteSource* const in =
+ serd_byte_source_new_string(opts.input_string, NULL);
+
+ st = serd_read_source(
+ app.world,
+ opts.common,
+ app.env,
+ serd_choose_syntax(app.world, opts.common.input, NULL, SERD_TRIG),
+ in,
+ sink);
+
+ serd_byte_source_free(in);
+ }
+
+ // Read all the inputs, which drives the writer to emit the output
+ if (st ||
+ (st = serd_read_inputs(
+ app.world, opts.common, app.env, opts.n_inputs, opts.inputs, sink)) ||
+ (st = serd_writer_finish(app.writer))) {
+ serd_tool_cleanup(app);
+ return st;
+ }
+
+ return serd_tool_cleanup(app);
+}
+
+/* Command-line interface (before setting up serd) */
+
+static int
+print_usage(const char* const name, const bool error)
+{
+ static const char* const description =
+ "Read and write RDF data.\n"
+ "INPUT can be a local filename, or \"-\" to read from standard input.\n\n"
+ " -B BASE_URI Base URI or path for resolving relative references.\n"
+ " -C Convert literals to canonical form.\n"
+ " -I SYNTAX Input syntax (turtle/ntriples/trig/nquads),\n"
+ " or option (lax/variables/relative/global/generated).\n"
+ " -O SYNTAX Output syntax (empty/turtle/ntriples/nquads),\n"
+ " or option (ascii/expanded/verbatim/terse/lax).\n"
+ " -R ROOT_URI Keep relative URIs within ROOT_URI.\n"
+ " -V Display version information and exit.\n"
+ " -b BYTES I/O block size.\n"
+ " -h Display this help and exit.\n"
+ " -k BYTES Parser stack size.\n"
+ " -o FILENAME Write output to FILENAME instead of stdout.\n"
+ " -q Suppress warning and error output.\n"
+ " -s STRING Parse STRING as input.\n";
+
+ FILE* const os = error ? stderr : stdout;
+ fprintf(os, "%s", error ? "\n" : "");
+ fprintf(os, "Usage: %s [OPTION]... INPUT...\n", name);
+ fprintf(os, "%s", description);
+ return error;
+}
+
+// Parse the option pointed to by `iter`, and advance it to the next one
+static SerdStatus
+parse_option(OptionIter* const iter, Options* const opts)
+{
+#define ARG_ERRORF(fmt, ...) \
+ fprintf(stderr, "%s: " fmt, iter->argv[0], __VA_ARGS__)
+
+ SerdStatus st = serd_parse_common_option(iter, &opts->common);
+ if (st != SERD_FAILURE) {
+ return st;
+ }
+
+ const char opt = iter->argv[iter->a][iter->f];
+ switch (opt) {
+ case 'C':
+ opts->canonical = true;
+ return serd_option_iter_advance(iter);
+
+ case 'R':
+ return serd_get_argument(iter, &opts->root_uri);
+
+ case 'V':
+ return serd_print_version("serd-pipe");
+
+ case 'h':
+ print_usage(iter->argv[0], false);
+ return SERD_FAILURE;
+
+ case 'q':
+ opts->quiet = true;
+ return serd_option_iter_advance(iter);
+
+ case 's':
+ return serd_get_argument(iter, &opts->input_string);
+
+ default:
+ break;
+ }
+
+ ARG_ERRORF("invalid option -- '%c'\n", opt);
+ return SERD_ERR_BAD_ARG;
+
+#undef ARG_ERRORF
+}
+
+int
+main(const int argc, char* const* const argv)
+{
+ char* const default_input[] = {"-"};
+
+ Options opts = {{"",
+ NULL,
+ 4096u,
+ 1048576u,
+ {SERD_SYNTAX_EMPTY, 0u, false},
+ {SERD_SYNTAX_EMPTY, 0u, false}},
+ "",
+ NULL,
+ NULL,
+ 0u,
+ false,
+ false};
+
+ // Parse all command line options (which must precede inputs)
+ SerdStatus st = SERD_SUCCESS;
+ OptionIter iter = {argv, argc, 1, 1};
+ while (!serd_option_iter_is_end(iter)) {
+ if ((st = parse_option(&iter, &opts))) {
+ return (st == SERD_FAILURE) ? 0 : print_usage(argv[0], true);
+ }
+ }
+
+ // Every argument past the last option is an input
+ opts.inputs = argv + iter.a;
+ opts.n_inputs = argc - iter.a;
+ if (opts.n_inputs + (bool)opts.input_string == 0) {
+ opts.n_inputs = 1;
+ opts.inputs = default_input;
+ }
+
+ // Don't add prefixes to blank node labels if there is only one input
+ if (opts.n_inputs + (bool)opts.input_string == 1) {
+ opts.common.input.flags |= SERD_READ_GLOBAL;
+ }
+
+ return run(opts) > SERD_FAILURE;
+}