cannam@226: /*
cannam@226:   Copyright 2011-2016 David Robillard <http://drobilla.net>
cannam@226: 
cannam@226:   Permission to use, copy, modify, and/or distribute this software for any
cannam@226:   purpose with or without fee is hereby granted, provided that the above
cannam@226:   copyright notice and this permission notice appear in all copies.
cannam@226: 
cannam@226:   THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
cannam@226:   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
cannam@226:   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
cannam@226:   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
cannam@226:   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
cannam@226:   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
cannam@226:   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
cannam@226: */
cannam@226: 
cannam@226: #include <assert.h>
cannam@226: #include <stdlib.h>
cannam@226: #include <string.h>
cannam@226: 
cannam@226: #ifdef _WIN32
cannam@226: #    include <windows.h>
cannam@226: #endif
cannam@226: 
cannam@226: #include "serd/serd.h"
cannam@226: #include "sord/sord.h"
cannam@226: #include "sord_config.h"
cannam@226: 
cannam@226: #define SORDI_ERROR(msg)       fprintf(stderr, "sordi: " msg);
cannam@226: #define SORDI_ERRORF(fmt, ...) fprintf(stderr, "sordi: " fmt, __VA_ARGS__);
cannam@226: 
cannam@226: typedef struct {
cannam@226: 	SerdWriter* writer;
cannam@226: 	SerdEnv*    env;
cannam@226: 	SerdNode    base_uri_node;
cannam@226: 	SerdURI     base_uri;
cannam@226: 	SordModel*  sord;
cannam@226: } State;
cannam@226: 
cannam@226: static int
cannam@226: print_version(void)
cannam@226: {
cannam@226: 	printf("sordi " SORD_VERSION " <http://drobilla.net/software/sord>\n");
cannam@226: 	printf("Copyright 2011-2016 David Robillard <http://drobilla.net>.\n"
cannam@226: 	       "License: <http://www.opensource.org/licenses/isc>\n"
cannam@226: 	       "This is free software; you are free to change and redistribute it."
cannam@226: 	       "\nThere is NO WARRANTY, to the extent permitted by law.\n");
cannam@226: 	return 0;
cannam@226: }
cannam@226: 
cannam@226: static int
cannam@226: print_usage(const char* name, bool error)
cannam@226: {
cannam@226: 	FILE* const os = error ? stderr : stdout;
cannam@226: 	fprintf(os, "%s", error ? "\n" : "");
cannam@226: 	fprintf(os, "Usage: %s [OPTION]... INPUT [BASE_URI]\n", name);
cannam@226: 	fprintf(os, "Load and re-serialise RDF data.\n");
cannam@226: 	fprintf(os, "Use - for INPUT to read from standard input.\n\n");
cannam@226: 	fprintf(os, "  -h           Display this help and exit\n");
cannam@226: 	fprintf(os, "  -i SYNTAX    Input syntax (`turtle' or `ntriples')\n");
cannam@226: 	fprintf(os, "  -o SYNTAX    Output syntax (`turtle' or `ntriples')\n");
cannam@226: 	fprintf(os, "  -s INPUT     Parse INPUT as string (terminates options)\n");
cannam@226: 	fprintf(os, "  -v           Display version information and exit\n");
cannam@226: 	return error ? 1 : 0;
cannam@226: }
cannam@226: 
cannam@226: static bool
cannam@226: set_syntax(SerdSyntax* syntax, const char* name)
cannam@226: {
cannam@226: 	if (!strcmp(name, "turtle")) {
cannam@226: 		*syntax = SERD_TURTLE;
cannam@226: 	} else if (!strcmp(name, "ntriples")) {
cannam@226: 		*syntax = SERD_NTRIPLES;
cannam@226: 	} else {
cannam@226: 		SORDI_ERRORF("unknown syntax `%s'\n", name);
cannam@226: 		return false;
cannam@226: 	}
cannam@226: 	return true;
cannam@226: }
cannam@226: 
cannam@226: int
cannam@226: main(int argc, char** argv)
cannam@226: {
cannam@226: 	if (argc < 2) {
cannam@226: 		return print_usage(argv[0], true);
cannam@226: 	}
cannam@226: 
cannam@226: 	FILE*          in_fd         = NULL;
cannam@226: 	SerdSyntax     input_syntax  = SERD_TURTLE;
cannam@226: 	SerdSyntax     output_syntax = SERD_NTRIPLES;
cannam@226: 	bool           from_file     = true;
cannam@226: 	const uint8_t* in_name       = NULL;
cannam@226: 	int a = 1;
cannam@226: 	for (; a < argc && argv[a][0] == '-'; ++a) {
cannam@226: 		if (argv[a][1] == '\0') {
cannam@226: 			in_name = (const uint8_t*)"(stdin)";
cannam@226: 			in_fd   = stdin;
cannam@226: 			break;
cannam@226: 		} else if (argv[a][1] == 'h') {
cannam@226: 			return print_usage(argv[0], false);
cannam@226: 		} else if (argv[a][1] == 'v') {
cannam@226: 			return print_version();
cannam@226: 		} else if (argv[a][1] == 's') {
cannam@226: 			in_name = (const uint8_t*)"(string)";
cannam@226: 			from_file = false;
cannam@226: 			++a;
cannam@226: 			break;
cannam@226: 		} else if (argv[a][1] == 'i') {
cannam@226: 			if (++a == argc) {
cannam@226: 				SORDI_ERROR("option requires an argument -- 'i'\n\n");
cannam@226: 				return print_usage(argv[0], true);
cannam@226: 			}
cannam@226: 			if (!set_syntax(&input_syntax, argv[a])) {
cannam@226: 				return print_usage(argv[0], true);
cannam@226: 			}
cannam@226: 		} else if (argv[a][1] == 'o') {
cannam@226: 			if (++a == argc) {
cannam@226: 				SORDI_ERROR("option requires an argument -- 'o'\n\n");
cannam@226: 				return print_usage(argv[0], true);
cannam@226: 			}
cannam@226: 			if (!set_syntax(&output_syntax, argv[a])) {
cannam@226: 				return print_usage(argv[0], true);
cannam@226: 			}
cannam@226: 		} else {
cannam@226: 			SORDI_ERRORF("invalid option -- '%s'\n", argv[a] + 1);
cannam@226: 			return print_usage(argv[0], true);
cannam@226: 		}
cannam@226: 	}
cannam@226: 
cannam@226: 	if (a == argc) {
cannam@226: 		SORDI_ERROR("missing input\n");
cannam@226: 		return print_usage(argv[0], true);
cannam@226: 	}
cannam@226: 
cannam@226: 	const uint8_t* input = (const uint8_t*)argv[a++];
cannam@226: 	if (from_file) {
cannam@226: 		in_name = in_name ? in_name : input;
cannam@226: 		if (!in_fd) {
cannam@226: 			input = serd_uri_to_path(in_name);
cannam@226: 			if (!input || !(in_fd = fopen((const char*)input, "rb"))) {
cannam@226: 				return 1;
cannam@226: 			}
cannam@226: 		}
cannam@226: 	}
cannam@226: 
cannam@226: 	SerdURI  base_uri = SERD_URI_NULL;
cannam@226: 	SerdNode base     = SERD_NODE_NULL;
cannam@226: 	if (a < argc) {  // Base URI given on command line
cannam@226: 		base = serd_node_new_uri_from_string(
cannam@226: 			(const uint8_t*)argv[a], NULL, &base_uri);
cannam@226: 	} else if (from_file && in_fd != stdin) {  // Use input file URI
cannam@226: 		base = serd_node_new_file_uri(input, NULL, &base_uri, true);
cannam@226: 	}
cannam@226: 
cannam@226: 	SordWorld*  world  = sord_world_new();
cannam@226: 	SordModel*  sord   = sord_new(world, SORD_SPO|SORD_OPS, false);
cannam@226: 	SerdEnv*    env    = serd_env_new(&base);
cannam@226: 	SerdReader* reader = sord_new_reader(sord, env, input_syntax, NULL);
cannam@226: 
cannam@226: 	SerdStatus status = (from_file)
cannam@226: 		? serd_reader_read_file_handle(reader, in_fd, in_name)
cannam@226: 		: serd_reader_read_string(reader, input);
cannam@226: 
cannam@226: 	serd_reader_free(reader);
cannam@226: 
cannam@226: 	FILE*    out_fd    = stdout;
cannam@226: 	SerdEnv* write_env = serd_env_new(&base);
cannam@226: 
cannam@226: 	int output_style = SERD_STYLE_RESOLVED;
cannam@226: 	if (output_syntax == SERD_NTRIPLES) {
cannam@226: 		output_style |= SERD_STYLE_ASCII;
cannam@226: 	} else {
cannam@226: 		output_style |= SERD_STYLE_CURIED | SERD_STYLE_ABBREVIATED;
cannam@226: 	}
cannam@226: 
cannam@226: 	SerdWriter* writer = serd_writer_new(
cannam@226: 		output_syntax,
cannam@226: 		(SerdStyle)output_style,
cannam@226: 		write_env, &base_uri, serd_file_sink, stdout);
cannam@226: 
cannam@226: 	// Write @prefix directives
cannam@226: 	serd_env_foreach(env,
cannam@226: 	                 (SerdPrefixSink)serd_writer_set_prefix,
cannam@226: 	                 writer);
cannam@226: 
cannam@226: 	// Write statements
cannam@226: 	sord_write(sord, writer, NULL);
cannam@226: 
cannam@226: 	serd_writer_finish(writer);
cannam@226: 	serd_writer_free(writer);
cannam@226: 
cannam@226: 	serd_env_free(env);
cannam@226: 	serd_env_free(write_env);
cannam@226: 	serd_node_free(&base);
cannam@226: 
cannam@226: 	sord_free(sord);
cannam@226: 	sord_world_free(world);
cannam@226: 
cannam@226: 	if (from_file) {
cannam@226: 		fclose(in_fd);
cannam@226: 	}
cannam@226: 
cannam@226: 	if (fclose(out_fd)) {
cannam@226: 		perror("sordi: write error");
cannam@226: 		status = SERD_ERR_UNKNOWN;
cannam@226: 	}
cannam@226: 
cannam@226: 	return (status > SERD_FAILURE) ? 1 : 0;
cannam@226: }