cannam@226: /* cannam@226: Copyright 2011-2017 David Robillard 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 "serd_internal.h" cannam@226: cannam@226: #include cannam@226: #include cannam@226: #include cannam@226: #include cannam@226: cannam@226: #define SERDI_ERROR(msg) fprintf(stderr, "serdi: " msg); cannam@226: #define SERDI_ERRORF(fmt, ...) fprintf(stderr, "serdi: " fmt, __VA_ARGS__); cannam@226: cannam@226: static int cannam@226: print_version(void) cannam@226: { cannam@226: printf("serdi " SERD_VERSION " \n"); cannam@226: printf("Copyright 2011-2017 David Robillard .\n" cannam@226: "License: \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, "Read and write RDF syntax.\n"); cannam@226: fprintf(os, "Use - for INPUT to read from standard input.\n\n"); cannam@226: fprintf(os, " -b Fast bulk output for large serialisations.\n"); cannam@226: fprintf(os, " -c PREFIX Chop PREFIX from matching blank node IDs.\n"); cannam@226: fprintf(os, " -e Eat input one character at a time.\n"); cannam@226: fprintf(os, " -f Keep full URIs in input (don't qualify).\n"); cannam@226: fprintf(os, " -h Display this help and exit.\n"); cannam@226: fprintf(os, " -i SYNTAX Input syntax: turtle/ntriples/trig/nquads.\n"); cannam@226: fprintf(os, " -l Lax (non-strict) parsing.\n"); cannam@226: fprintf(os, " -o SYNTAX Output syntax: turtle/ntriples/nquads.\n"); cannam@226: fprintf(os, " -p PREFIX Add PREFIX to blank node IDs.\n"); cannam@226: fprintf(os, " -q Suppress all output except data.\n"); cannam@226: fprintf(os, " -r ROOT_URI Keep relative URIs within ROOT_URI.\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 if (!strcmp(name, "nquads")) { cannam@226: *syntax = SERD_NQUADS; cannam@226: } else if (!strcmp(name, "trig")) { cannam@226: *syntax = SERD_TRIG; cannam@226: } else { cannam@226: SERDI_ERRORF("unknown syntax `%s'\n", name); cannam@226: return false; cannam@226: } cannam@226: return true; cannam@226: } cannam@226: cannam@226: static int cannam@226: missing_arg(const char* name, char opt) cannam@226: { cannam@226: SERDI_ERRORF("option requires an argument -- '%c'\n", opt); cannam@226: return print_usage(name, true); cannam@226: } cannam@226: cannam@226: static SerdStatus cannam@226: quiet_error_sink(void* handle, const SerdError* e) cannam@226: { cannam@226: return SERD_SUCCESS; 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: bool bulk_read = true; cannam@226: bool bulk_write = false; cannam@226: bool full_uris = false; cannam@226: bool lax = false; cannam@226: bool quiet = false; cannam@226: const uint8_t* in_name = NULL; cannam@226: const uint8_t* add_prefix = NULL; cannam@226: const uint8_t* chop_prefix = NULL; cannam@226: const uint8_t* root_uri = 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] == 'b') { cannam@226: bulk_write = true; cannam@226: } else if (argv[a][1] == 'e') { cannam@226: bulk_read = false; cannam@226: } else if (argv[a][1] == 'f') { cannam@226: full_uris = true; cannam@226: } else if (argv[a][1] == 'h') { cannam@226: return print_usage(argv[0], false); cannam@226: } else if (argv[a][1] == 'l') { cannam@226: lax = true; cannam@226: } else if (argv[a][1] == 'q') { cannam@226: quiet = true; 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: return missing_arg(argv[0], 'i'); cannam@226: } else 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: return missing_arg(argv[0], 'o'); cannam@226: } else if (!set_syntax(&output_syntax, argv[a])) { cannam@226: return print_usage(argv[0], true); cannam@226: } cannam@226: } else if (argv[a][1] == 'p') { cannam@226: if (++a == argc) { cannam@226: return missing_arg(argv[0], 'p'); cannam@226: } cannam@226: add_prefix = (const uint8_t*)argv[a]; cannam@226: } else if (argv[a][1] == 'c') { cannam@226: if (++a == argc) { cannam@226: return missing_arg(argv[0], 'c'); cannam@226: } cannam@226: chop_prefix = (const uint8_t*)argv[a]; cannam@226: } else if (argv[a][1] == 'r') { cannam@226: if (++a == argc) { cannam@226: return missing_arg(argv[0], 'r'); cannam@226: } cannam@226: root_uri = (const uint8_t*)argv[a]; cannam@226: } else { cannam@226: SERDI_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: SERDI_ERROR("missing input\n"); cannam@226: return 1; 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 = serd_fopen((const char*)input, "r"))) { 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: FILE* out_fd = stdout; cannam@226: SerdEnv* env = serd_env_new(&base); cannam@226: cannam@226: int output_style = 0; cannam@226: if (output_syntax == SERD_NTRIPLES || output_syntax == SERD_NQUADS) { cannam@226: output_style |= SERD_STYLE_ASCII; cannam@226: } else if (output_syntax == SERD_TURTLE) { cannam@226: output_style |= SERD_STYLE_ABBREVIATED; cannam@226: if (!full_uris) { cannam@226: output_style |= SERD_STYLE_CURIED; cannam@226: } cannam@226: } cannam@226: cannam@226: if ((input_syntax == SERD_TURTLE || input_syntax == SERD_TRIG) || cannam@226: (output_style & SERD_STYLE_CURIED)) { cannam@226: // Base URI may change and/or we're abbreviating URIs, so must resolve cannam@226: output_style |= SERD_STYLE_RESOLVED; cannam@226: } cannam@226: cannam@226: if (bulk_write) { cannam@226: output_style |= SERD_STYLE_BULK; cannam@226: } cannam@226: cannam@226: SerdWriter* writer = serd_writer_new( cannam@226: output_syntax, (SerdStyle)output_style, cannam@226: env, &base_uri, serd_file_sink, out_fd); cannam@226: cannam@226: SerdReader* reader = serd_reader_new( cannam@226: input_syntax, writer, NULL, cannam@226: (SerdBaseSink)serd_writer_set_base_uri, cannam@226: (SerdPrefixSink)serd_writer_set_prefix, cannam@226: (SerdStatementSink)serd_writer_write_statement, cannam@226: (SerdEndSink)serd_writer_end_anon); cannam@226: cannam@226: serd_reader_set_strict(reader, !lax); cannam@226: if (quiet) { cannam@226: serd_reader_set_error_sink(reader, quiet_error_sink, NULL); cannam@226: serd_writer_set_error_sink(writer, quiet_error_sink, NULL); cannam@226: } cannam@226: cannam@226: SerdNode root = serd_node_from_string(SERD_URI, root_uri); cannam@226: serd_writer_set_root_uri(writer, &root); cannam@226: serd_writer_chop_blank_prefix(writer, chop_prefix); cannam@226: serd_reader_add_blank_prefix(reader, add_prefix); cannam@226: cannam@226: SerdStatus status = SERD_SUCCESS; cannam@226: if (!from_file) { cannam@226: status = serd_reader_read_string(reader, input); cannam@226: } else if (bulk_read) { cannam@226: status = serd_reader_read_file_handle(reader, in_fd, in_name); cannam@226: } else { cannam@226: status = serd_reader_start_stream(reader, in_fd, in_name, false); cannam@226: while (!status) { cannam@226: status = serd_reader_read_chunk(reader); cannam@226: } cannam@226: serd_reader_end_stream(reader); cannam@226: } cannam@226: cannam@226: serd_reader_free(reader); cannam@226: serd_writer_finish(writer); cannam@226: serd_writer_free(writer); cannam@226: serd_env_free(env); cannam@226: serd_node_free(&base); 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("serdi: write error"); cannam@226: status = SERD_ERR_UNKNOWN; cannam@226: } cannam@226: cannam@226: return (status > SERD_FAILURE) ? 1 : 0; cannam@226: }