/* Copyright 2011-2021 David Robillard 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 #include #include /* 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, 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) { const char* position = opts.input_string; SerdInputStream in = serd_open_input_string(&position); st = serd_read_source( app.world, opts.common, app.env, serd_choose_syntax(app.world, opts.common.input, NULL, SERD_TRIG), &in, "string", sink); serd_close_input(&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_sink_free(canon); serd_tool_cleanup(app); return st; } serd_sink_free(canon); 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 default_input[] = "-"; char* default_inputs[] = {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_inputs; } // 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; }