aboutsummaryrefslogtreecommitdiffstats
path: root/tools/console.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/console.c')
-rw-r--r--tools/console.c288
1 files changed, 272 insertions, 16 deletions
diff --git a/tools/console.c b/tools/console.c
index ea5fd7ee..f1e78d75 100644
--- a/tools/console.c
+++ b/tools/console.c
@@ -26,9 +26,64 @@
# include <io.h>
#endif
+#include <errno.h>
+#include <limits.h>
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
+SerdStatus
+serd_tool_setup(SerdTool* const tool,
+ const char* const program,
+ SerdCommonOptions options)
+{
+ // Open the output first, since if that fails we have nothing to do
+ const char* const out_path = options.out_filename;
+ if (!(tool->out = serd_open_output(out_path, options.block_size))) {
+ fprintf(stderr,
+ "%s: failed to open output file (%s)\n",
+ program,
+ strerror(errno));
+ return SERD_ERR_UNKNOWN;
+ }
+
+ // We have something to write to, so build the writing environment
+ if (!(tool->world = serd_world_new()) ||
+ !(tool->env =
+ serd_create_env(program, options.base_uri, options.out_filename)) ||
+ !(tool->writer = serd_writer_new(
+ tool->world,
+ serd_choose_syntax(
+ tool->world, options.output, options.out_filename, SERD_NQUADS),
+ options.output.flags,
+ tool->env,
+ tool->out))) {
+ fprintf(stderr, "%s: failed to set up writing environment\n", program);
+ return SERD_ERR_INTERNAL;
+ }
+
+ return SERD_SUCCESS;
+}
+
+SerdStatus
+serd_tool_cleanup(const SerdTool tool)
+{
+ SerdStatus st = SERD_SUCCESS;
+ if (tool.out) {
+ // Close the output stream explicitly to check if there were any errors
+ if (serd_byte_sink_close(tool.out)) {
+ perror("write error");
+ st = SERD_ERR_BAD_WRITE;
+ }
+ }
+
+ serd_writer_free(tool.writer);
+ serd_env_free(tool.env);
+ serd_world_free(tool.world);
+ serd_byte_sink_free(tool.out);
+ return st;
+}
+
void
serd_set_stream_utf8_mode(FILE* const stream)
{
@@ -39,7 +94,7 @@ serd_set_stream_utf8_mode(FILE* const stream)
#endif
}
-int
+SerdStatus
serd_print_version(const char* const program)
{
printf("%s %d.%d.%d <http://drobilla.net/software/serd>\n",
@@ -53,7 +108,43 @@ serd_print_version(const char* const program)
"This is free software; you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n");
- return 0;
+ return SERD_FAILURE;
+}
+
+SerdStatus
+serd_get_argument(OptionIter* const iter, const char** const argument)
+{
+ const char flag = iter->argv[iter->a][iter->f++];
+
+ if (iter->argv[iter->a][iter->f] || (iter->a + 1) == iter->argc) {
+ fprintf(
+ stderr, "%s: option requires an argument -- %c\n", iter->argv[0], flag);
+ return SERD_ERR_BAD_ARG;
+ }
+
+ *argument = iter->argv[++iter->a];
+ ++iter->a;
+ iter->f = 1;
+ return SERD_SUCCESS;
+}
+
+SerdStatus
+serd_get_size_argument(OptionIter* const iter, size_t* const argument)
+{
+ SerdStatus st = SERD_SUCCESS;
+ const char* string = NULL;
+ if ((st = serd_get_argument(iter, &string))) {
+ return st;
+ }
+
+ char* endptr = NULL;
+ const long size = strtol(string, &endptr, 10);
+ if (size <= 0 || size == LONG_MAX || *endptr != '\0') {
+ return SERD_ERR_BAD_ARG;
+ }
+
+ *argument = (size_t)size;
+ return SERD_SUCCESS;
}
SerdStatus
@@ -89,8 +180,26 @@ serd_set_input_option(const SerdStringView name,
}
}
- // SERDI_ERRORF("invalid input option `%s'\n", name.buf);
- return SERD_FAILURE;
+ return SERD_ERR_BAD_ARG;
+}
+
+SerdStatus
+serd_parse_input_argument(OptionIter* const iter,
+ SerdSyntaxOptions* const options)
+{
+ SerdStatus st = SERD_SUCCESS;
+ const char* argument = NULL;
+
+ if (!(st = serd_get_argument(iter, &argument))) {
+ if ((st = serd_set_input_option(
+ SERD_STRING(argument), &options->syntax, &options->flags))) {
+ fprintf(stderr, "%s: unknown option \"%s\"\n", iter->argv[0], argument);
+ } else if (!strcmp(argument, "empty") || options->syntax) {
+ options->overridden = true;
+ }
+ }
+
+ return st;
}
SerdStatus
@@ -126,16 +235,90 @@ serd_set_output_option(const SerdStringView name,
}
}
+ return SERD_ERR_BAD_ARG;
+}
+
+SerdStatus
+serd_parse_output_argument(OptionIter* const iter,
+ SerdSyntaxOptions* const options)
+{
+ SerdStatus st = SERD_SUCCESS;
+ const char* argument = NULL;
+
+ if (!(st = serd_get_argument(iter, &argument))) {
+ if ((st = serd_set_output_option(
+ SERD_STRING(argument), &options->syntax, &options->flags))) {
+ fprintf(stderr, "%s: unknown option \"%s\"\n", iter->argv[0], argument);
+ } else if (!strcmp(argument, "empty") || options->syntax) {
+ options->overridden = true;
+ }
+ }
+
+ return st;
+}
+
+SerdStatus
+serd_parse_common_option(OptionIter* const iter, SerdCommonOptions* const opts)
+{
+ const char opt = iter->argv[iter->a][iter->f];
+ switch (opt) {
+ case 'B':
+ return serd_get_argument(iter, &opts->base_uri);
+
+ case 'I':
+ return serd_parse_input_argument(iter, &opts->input);
+
+ case 'O':
+ return serd_parse_output_argument(iter, &opts->output);
+
+ case 'b':
+ return serd_get_size_argument(iter, &opts->block_size);
+
+ case 'k':
+ return serd_get_size_argument(iter, &opts->stack_size);
+
+ case 'o':
+ return serd_get_argument(iter, &opts->out_filename);
+
+ default:
+ break;
+ }
+
return SERD_FAILURE;
}
+SerdEnv*
+serd_create_env(const char* const program,
+ const char* const base_string,
+ const char* const out_filename)
+{
+ const bool is_rebase = base_string && !strcmp(base_string, "rebase");
+ if (is_rebase && !out_filename) {
+ fprintf(stderr, "%s: rebase requires an output filename\n", program);
+ return NULL;
+ }
+
+ if (base_string && serd_uri_string_has_scheme(base_string)) {
+ return serd_env_new(SERD_STRING(base_string));
+ }
+
+ SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING());
+ serd_set_base_uri_from_path(env, is_rebase ? out_filename : base_string);
+ return env;
+}
+
SerdSyntax
-serd_choose_input_syntax(SerdWorld* const world,
- const SerdSyntax requested,
- const char* const filename)
+serd_choose_syntax(SerdWorld* const world,
+ const SerdSyntaxOptions options,
+ const char* const filename,
+ const SerdSyntax fallback)
{
- if (requested) {
- return requested;
+ if (options.overridden || options.syntax != SERD_SYNTAX_EMPTY) {
+ return options.syntax;
+ }
+
+ if (!filename || !strcmp(filename, "-")) {
+ return fallback;
}
const SerdSyntax guessed = serd_guess_syntax(filename);
@@ -202,17 +385,90 @@ serd_open_output(const char* const filename, const size_t block_size)
SerdStatus
serd_set_base_uri_from_path(SerdEnv* const env, const char* const path)
{
- char* const input_path = serd_canonical_path(path);
- if (!input_path) {
+ const size_t path_len = path ? strlen(path) : 0u;
+ if (!path_len) {
return SERD_ERR_BAD_ARG;
}
- SerdNode* const file_uri =
- serd_new_file_uri(SERD_STRING(input_path), SERD_EMPTY_STRING());
+ char* const real_path = serd_canonical_path(path);
+ if (!real_path) {
+ return SERD_ERR_BAD_ARG;
+ }
+
+ const size_t real_path_len = strlen(real_path);
+ SerdNode* base_node = NULL;
+ if (path[path_len - 1] == '/' || path[path_len - 1] == '\\') {
+ char* const base_path = (char*)calloc(real_path_len + 2, 1);
+ memcpy(base_path, real_path, real_path_len);
+ base_path[real_path_len] = path[path_len - 1];
+
+ base_node = serd_new_file_uri(SERD_STRING(base_path), SERD_EMPTY_STRING());
+ free(base_path);
+ } else {
+ base_node = serd_new_file_uri(SERD_STRING(real_path), SERD_EMPTY_STRING());
+ }
- serd_env_set_base_uri(env, serd_node_string_view(file_uri));
- serd_node_free(file_uri);
- serd_free(input_path);
+ serd_env_set_base_uri(env, serd_node_string_view(base_node));
+ serd_node_free(base_node);
+ serd_free(real_path);
return SERD_SUCCESS;
}
+
+SerdStatus
+serd_read_source(SerdWorld* const world,
+ const SerdCommonOptions opts,
+ SerdEnv* const env,
+ const SerdSyntax syntax,
+ SerdByteSource* const in,
+ const SerdSink* const sink)
+{
+ SerdReader* const reader = serd_reader_new(
+ world, syntax, opts.input.flags, env, sink, opts.stack_size);
+
+ SerdStatus st = serd_reader_start(reader, in);
+ if (!st) {
+ st = serd_reader_read_document(reader);
+ }
+
+ serd_reader_free(reader);
+ return st;
+}
+
+SerdStatus
+serd_read_inputs(SerdWorld* const world,
+ const SerdCommonOptions opts,
+ SerdEnv* const env,
+ const intptr_t n_inputs,
+ char* const* const inputs,
+ const SerdSink* const sink)
+{
+ SerdStatus st = SERD_SUCCESS;
+
+ for (intptr_t i = 0; !st && i < n_inputs; ++i) {
+ // Use the filename as the base URI if possible if user didn't override it
+ const char* const in_path = inputs[i];
+ if (!opts.base_uri[0] && strcmp(in_path, "-")) {
+ serd_set_base_uri_from_path(env, in_path);
+ }
+
+ // Open the input stream
+ SerdByteSource* const in = serd_open_input(in_path, opts.block_size);
+ if (!in) {
+ return SERD_ERR_BAD_ARG;
+ }
+
+ // Read the entire file
+ st = serd_read_source(
+ world,
+ opts,
+ env,
+ serd_choose_syntax(world, opts.input, in_path, SERD_TRIG),
+ in,
+ sink);
+
+ serd_byte_source_free(in);
+ }
+
+ return st;
+}