/* Copyright 2012-2018 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. */ #define _BSD_SOURCE 1 // for realpath #define _DEFAULT_SOURCE 1 // for realpath #include "serd_config.h" #include "serd/serd.h" #include #include #include #include #ifdef _WIN32 #include #endif #ifdef HAVE_PCRE #include #endif #define NS_foaf "http://xmlns.com/foaf/0.1/" #define NS_owl "http://www.w3.org/2002/07/owl#" #define NS_rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#" #define NS_rdfs "http://www.w3.org/2000/01/rdf-schema#" #define NS_xsd "http://www.w3.org/2001/XMLSchema#" #define CERRORF(fmt, ...) fprintf(stderr, "serd_validate: " fmt, __VA_ARGS__); #define VERRORF(statement, fmt, ...) \ report(statement, "error", fmt, __VA_ARGS__); #define VERROR(statement, fmt) report(statement, "error", fmt); #define VNOTEF(statement, fmt, ...) report(statement, "note", fmt, __VA_ARGS__); #define VNOTE(statement, fmt) report(statement, "note", fmt); #define SERD_FOREACH(name, range) \ for (const SerdStatement* name = NULL; \ !serd_range_empty(range) && (name = serd_range_front(range)); \ serd_range_next(range)) typedef struct { SerdNode* foaf_Document; SerdNode* owl_Class; SerdNode* owl_DatatypeProperty; SerdNode* owl_FunctionalProperty; SerdNode* owl_InverseFunctionalProperty; SerdNode* owl_ObjectProperty; SerdNode* owl_Restriction; SerdNode* owl_Thing; SerdNode* owl_allValuesFrom; SerdNode* owl_cardinality; SerdNode* owl_equivalentClass; SerdNode* owl_maxCardinality; SerdNode* owl_minCardinality; SerdNode* owl_onDatatype; SerdNode* owl_onProperty; SerdNode* owl_someValuesFrom; SerdNode* owl_withRestrictions; SerdNode* rdf_PlainLiteral; SerdNode* rdf_Property; SerdNode* rdf_first; SerdNode* rdf_rest; SerdNode* rdf_type; SerdNode* rdfs_Class; SerdNode* rdfs_Datatype; SerdNode* rdfs_Literal; SerdNode* rdfs_Resource; SerdNode* rdfs_domain; SerdNode* rdfs_label; SerdNode* rdfs_range; SerdNode* rdfs_subClassOf; SerdNode* xsd_anyURI; SerdNode* xsd_float; SerdNode* xsd_decimal; SerdNode* xsd_double; SerdNode* xsd_maxExclusive; SerdNode* xsd_maxInclusive; SerdNode* xsd_minExclusive; SerdNode* xsd_minInclusive; SerdNode* xsd_pattern; SerdNode* xsd_string; } URIs; static int n_errors = 0; static int n_restrictions = 0; static int check_class_restriction(SerdModel* model, const URIs* uris, const SerdNode* restriction, const SerdStatement* statement, const SerdNode* instance); static int print_version(void) { printf("serd_validate " SERD_VERSION " \n"); printf("Copyright 2012-2018 David Robillard .\n" "License: \n" "This is free software; you are free to change and redistribute it." "\nThere is NO WARRANTY, to the extent permitted by law.\n"); return 0; } static int print_usage(const char* name, bool error) { FILE* const os = error ? stderr : stdout; fprintf(os, "Usage: %s [OPTION]... INPUT...\n", name); fprintf(os, "Validate RDF data\n\n"); fprintf(os, " -h Display this help and exit\n"); fprintf(os, " -l Print errors on a single line.\n"); fprintf(os, " -v Display version information and exit\n"); fprintf(os, "Validate RDF data. This is a simple validator which checks\n" "that all used properties are actually defined. It does not do\n" "any fancy file retrieval, the files passed on the command line\n" "are the only data that is read. In other words, you must pass\n" "the definition of all vocabularies used on the command line.\n"); return error ? 1 : 0; } static char* absolute_path(const char* path) { #ifdef _WIN32 char* out = (char*)malloc(MAX_PATH); GetFullPathName(path, MAX_PATH, out, NULL); return out; #else return realpath(path, NULL); #endif } static int report(const SerdStatement* statement, const char* type, const char* fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "%s: ", type); const SerdCursor* cursor = serd_statement_get_cursor(statement); if (cursor) { fprintf(stderr, "%s:%u:%u: ", serd_node_get_string(serd_cursor_get_name(cursor)), serd_cursor_get_line(cursor), serd_cursor_get_column(cursor)); } vfprintf(stderr, fmt, args); va_end(args); ++n_errors; return 1; } static bool check(const bool value) { ++n_restrictions; return value; } /** Return true iff `child` is a descendant of `parent` by `pred` arcs. * * This is, returns true if there is a path from `child` to `parent` by * following `pred` arcs starting from child. */ static bool is_descendant(SerdModel* model, const URIs* uris, const SerdNode* child, const SerdNode* parent, const SerdNode* pred) { if (serd_node_equals(child, parent) || serd_model_ask(model, child, uris->owl_equivalentClass, parent, NULL)) { return true; } SerdRange* i = serd_model_range(model, child, pred, NULL, NULL); SERD_FOREACH (s, i) { const SerdNode* o = serd_statement_get_object(s); if (serd_node_equals(child, o)) { continue; // Weird class is explicitly a descendent of itself } if (is_descendant(model, uris, o, parent, pred)) { serd_range_free(i); return true; } } serd_range_free(i); return false; } /** Return true iff `child` is a subclass of `parent`. */ static bool is_subclass(SerdModel* model, const URIs* uris, const SerdNode* child, const SerdNode* parent) { return is_descendant(model, uris, child, parent, uris->rdfs_subClassOf); } /** Return true iff `child` is a sub-datatype of `parent`. */ static bool is_subdatatype(SerdModel* model, const URIs* uris, const SerdNode* child, const SerdNode* parent) { return is_descendant(model, uris, child, parent, uris->owl_onDatatype); } static bool regexp_match(const SerdStatement* pat_statement, const char* pat, const char* str) { #ifdef HAVE_PCRE // Append a $ to the pattern so we only match if the entire string matches const size_t len = strlen(pat); char* const regx = (char*)malloc(len + 2); memcpy(regx, pat, len); regx[len] = '$'; regx[len + 1] = '\0'; const char* err; int erroffset; pcre* re = pcre_compile(regx, PCRE_ANCHORED, &err, &erroffset, NULL); free(regx); if (!re) { VERRORF(pat_statement, "Error in pattern \"%s\" at offset %d (%s)\n", pat, erroffset, err); return false; } const bool ret = pcre_exec(re, NULL, str, strlen(str), 0, 0, NULL, 0) >= 0; pcre_free(re); return ret; #endif // HAVE_PCRE return true; } static int bound_cmp(SerdModel* model, const URIs* uris, const SerdNode* literal, const SerdNode* type, const SerdNode* bound) { const char* str = serd_node_get_string(literal); const char* bound_str = serd_node_get_string(bound); const bool is_numeric = is_subdatatype(model, uris, type, uris->xsd_decimal) || is_subdatatype(model, uris, type, uris->xsd_double); if (is_numeric) { const double fbound = serd_strtod(bound_str, NULL); const double fliteral = serd_strtod(str, NULL); return ((fliteral < fbound) ? -1 : (fliteral > fbound) ? 1 : 0); } else { return strcmp(str, bound_str); } } static bool check_literal_restriction(SerdModel* model, const URIs* uris, const SerdStatement* statement, const SerdNode* literal, const SerdNode* type, const SerdNode* restriction) { const char* str = serd_node_get_string(literal); // Check xsd:pattern const SerdStatement* pat_statement = serd_model_get_statement( model, restriction, uris->xsd_pattern, 0, 0); if (pat_statement) { const SerdNode* pat_node = serd_statement_get_object(pat_statement); const char* pat = serd_node_get_string(pat_node); if (check(!regexp_match(pat_statement, pat, str))) { VERRORF(statement, "Value \"%s\" does not match pattern \"%s\"\n", serd_node_get_string(literal), pat); return false; } } // Check xsd:minInclusive const SerdNode* lower = serd_model_get(model, restriction, uris->xsd_minInclusive, 0, 0); if (lower) { if (check(bound_cmp(model, uris, literal, type, lower) < 0)) { VERRORF(statement, "Value \"%s\" < minimum \"%s\"\n", serd_node_get_string(literal), serd_node_get_string(lower)); return false; } } // Check xsd:maxInclusive const SerdNode* upper = serd_model_get(model, restriction, uris->xsd_maxInclusive, 0, 0); if (upper) { if (check(bound_cmp(model, uris, literal, type, upper) > 0)) { VERRORF(statement, "Value \"%s\" > than maximum \"%s\"\n", serd_node_get_string(literal), serd_node_get_string(upper)); return false; } } // Check xsd:minExclusive const SerdNode* elower = serd_model_get(model, restriction, uris->xsd_minExclusive, 0, 0); if (elower) { if (check(bound_cmp(model, uris, literal, type, elower) <= 0)) { VERRORF(statement, "Value \"%s\" <= exclusive minimum \"%s\"\n", serd_node_get_string(literal), serd_node_get_string(elower)); return false; } } // Check xsd:maxExclusive const SerdNode* eupper = serd_model_get(model, restriction, uris->xsd_maxExclusive, 0, 0); if (eupper) { if (check(bound_cmp(model, uris, literal, type, eupper) >= 0)) { VERRORF(statement, "Value \"%s\" >= exclusive maximum \"%s\"\n", serd_node_get_string(literal), serd_node_get_string(eupper)); return false; } ++n_restrictions; } return true; // Unknown restriction, be quietly tolerant } static bool is_datatype(SerdModel* model, const URIs* uris, const SerdNode* dtype) { SerdRange* t = serd_model_range(model, dtype, uris->rdf_type, NULL, NULL); SERD_FOREACH (s, t) { const SerdNode* type = serd_statement_get_object(s); if (is_subdatatype(model, uris, type, uris->rdfs_Datatype)) { return true; // Subdatatype of rdfs:Datatype } } return false; } static bool literal_is_valid(SerdModel* model, const URIs* uris, const SerdStatement* statement, const SerdNode* literal, const SerdNode* type) { if (!type) { return true; } // Check that datatype is defined const SerdNode* datatype = serd_node_get_datatype(literal); if (datatype && !is_datatype(model, uris, datatype)) { VERRORF(statement, "Datatype <%s> is not defined\n", serd_node_get_string(datatype)); return false; } // Find restrictions list const SerdNode* head = serd_model_get(model, type, uris->owl_withRestrictions, 0, 0); // Walk list, checking each restriction while (head) { SerdIter* f = serd_model_find(model, head, uris->rdf_first, 0, 0); if (!f) { break; } const SerdNode* first = serd_statement_get_object(serd_iter_get(f)); // Check this restriction if (!check_literal_restriction( model, uris, statement, literal, type, first)) { VNOTEF(serd_iter_get(f), "Restriction on <%s>\n", serd_node_get_string(type)); return false; } // Seek to next list node head = serd_model_get(model, head, uris->rdf_rest, 0, 0); } // Recurse up datatype hierarchy const SerdNode* super = serd_model_get(model, type, uris->owl_onDatatype, 0, 0); return super ? literal_is_valid(model, uris, statement, literal, super) : true; } static bool has_explicit_type(SerdModel* model, const URIs* uris, const SerdNode* node, const SerdNode* klass) { if (serd_model_ask(model, node, uris->rdf_type, klass, NULL)) { return true; // Directly stated to be an instance } SerdRange* t = serd_model_range(model, node, uris->rdf_type, NULL, NULL); SERD_FOREACH (s, t) { if (is_subclass(model, uris, serd_statement_get_object(s), klass)) { return true; // Explicit instance of a subclass } } return false; } static bool is_instance_of(SerdModel* model, const URIs* uris, const SerdStatement* statement, const SerdNode* node, const SerdNode* klass) { if (!serd_model_ask(model, node, NULL, NULL, NULL)) { /* Nothing about this node known in the model at all, assume it is some external resource we can't validate. */ return true; } else if (serd_node_get_type(node) == SERD_BLANK) { /* Be permissive for blank nodes and don't require explicit type annotation, to avoid countless errors with things like lists. */ return true; } return (has_explicit_type(model, uris, node, klass) || serd_node_equals(klass, uris->rdfs_Resource) || serd_node_equals(klass, uris->owl_Thing)); } static bool check_instance_type(SerdModel* model, const URIs* uris, const SerdStatement* statement, const SerdNode* node, const SerdNode* klass) { if (is_subclass(model, uris, klass, uris->rdfs_Literal) || serd_model_ask(model, klass, uris->rdf_type, uris->rdfs_Datatype, 0)) { VERROR(statement, "Class instance found where literal expected\n"); return false; } if (serd_model_ask( model, klass, uris->rdf_type, uris->owl_Restriction, NULL)) { if (check_class_restriction(model, uris, klass, statement, node)) { return false; } } SerdRange* r = serd_model_range(model, klass, uris->rdfs_subClassOf, NULL, NULL); SERD_FOREACH (s, r) { const SerdNode* super = serd_statement_get_object(s); if (!serd_node_equals(super, klass)) { if (!check_instance_type(model, uris, statement, node, super)) { return false; } } } if (!is_instance_of(model, uris, statement, node, klass)) { VERRORF(statement, "Node %s is not an instance of %s\n", serd_node_get_string(node), serd_node_get_string(klass)); return false; } return true; // FIXME? } static bool check_type(SerdModel* model, const URIs* uris, const SerdStatement* statement, const SerdNode* node, const SerdNode* type) { if (serd_node_equals(type, uris->rdfs_Resource) || serd_node_equals(type, uris->owl_Thing)) { return true; // Trivially true for everything (more or less) } if (serd_node_get_type(node) == SERD_LITERAL) { if (serd_node_equals(type, uris->rdfs_Literal)) { return true; // Trivially true for a literal } else if (serd_node_equals(type, uris->rdf_PlainLiteral)) { if (serd_node_get_datatype(node)) { VERRORF(statement, "Literal \"%s\" should be plain, but has datatype " "<%s>\n", serd_node_get_string(node), serd_node_get_string(serd_node_get_datatype(node))); return false; } return true; } else if (!serd_model_ask(model, type, uris->rdf_type, uris->rdfs_Datatype, NULL)) { VERRORF(statement, "Literal \"%s\" where instance of <%s> expected\n", serd_node_get_string(node), serd_node_get_string(type)); return false; } else { return literal_is_valid(model, uris, statement, node, type); } } else if (serd_node_get_type(node) == SERD_URI) { if (is_subdatatype(model, uris, type, uris->xsd_anyURI)) { /* Type is any URI and this is a URI, so pass. Restrictions on anyURI subtypes are not currently checked (very uncommon). */ return true; } else { return check_instance_type(model, uris, statement, node, type); } } else { return check_instance_type(model, uris, statement, node, type); } VERRORF(statement, "Unhandled case checking if %s is of type %s\n", serd_node_get_string(node), serd_node_get_string(type)); return false; } static uint64_t count_non_blanks(SerdRange* i, SerdField field) { uint64_t n = 0; SERD_FOREACH (s, i) { const SerdNode* node = serd_statement_get_node(s, field); if (serd_node_get_type(node) != SERD_BLANK) { ++n; } } return n; } static int check_statement(SerdModel* model, const URIs* uris, const SerdStatement* statement) { int st = 0; const SerdNode* subj = serd_statement_get_subject(statement); const SerdNode* pred = serd_statement_get_predicate(statement); const SerdNode* obj = serd_statement_get_object(statement); // Check that predicate is defined as some kind of propety bool is_property = false; SerdRange* t = serd_model_range(model, pred, uris->rdf_type, NULL, NULL); SERD_FOREACH (s, t) { const SerdNode* type = serd_statement_get_object(s); if (is_subclass(model, uris, type, uris->rdf_Property)) { is_property = true; break; } } serd_range_free(t); if (!is_property) { st = VERROR(statement, "Use of undefined property\n"); } if (serd_node_equals(pred, uris->rdf_type)) { // Type statement, check that object is a valid instance of type check_type(model, uris, statement, subj, obj); } const bool is_ObjectProperty = serd_model_ask( model, pred, uris->rdf_type, uris->owl_ObjectProperty, 0); const bool is_FunctionalProperty = serd_model_ask( model, pred, uris->rdf_type, uris->owl_FunctionalProperty, 0); const bool is_InverseFunctionalProperty = serd_model_ask(model, pred, uris->rdf_type, uris->owl_InverseFunctionalProperty, 0); const bool is_DatatypeProperty = serd_model_ask( model, pred, uris->rdf_type, uris->owl_DatatypeProperty, 0); if (!serd_model_ask(model, pred, uris->rdfs_label, NULL, NULL)) { st = VERRORF(statement, "Property <%s> has no label\n", serd_node_get_string(pred)); } if (is_DatatypeProperty && serd_node_get_type(obj) != SERD_LITERAL) { st = VERROR(statement, "Datatype property with non-literal value\n"); } if (is_ObjectProperty && serd_node_get_type(obj) == SERD_LITERAL) { st = VERROR(statement, "Object property with literal value\n"); } if (is_FunctionalProperty) { SerdRange* o = serd_model_range(model, subj, pred, NULL, NULL); const uint64_t n = count_non_blanks(o, SERD_OBJECT); if (n > 1) { st = VERRORF(statement, "Functional property with %u objects\n", n); } serd_range_free(o); } if (is_InverseFunctionalProperty) { SerdRange* s = serd_model_range(model, NULL, pred, obj, NULL); const unsigned n = count_non_blanks(s, SERD_SUBJECT); if (n > 1) { st = VERRORF(statement, "Inverse functional property with %u subjects\n", n); } serd_range_free(s); } if (serd_node_get_type(obj) == SERD_LITERAL && !literal_is_valid( model, uris, statement, obj, serd_node_get_datatype(obj))) { /* st = VERRORF(statement, "Literal does not match datatype\n"); */ } // Check range SerdRange* r = serd_model_range(model, pred, uris->rdfs_range, NULL, NULL); SERD_FOREACH (s, r) { const SerdNode* range = serd_statement_get_object(s); if (has_explicit_type(model, uris, obj, range)) { continue; // Explicit type will be checked, avoid duplicate reports } if (!check_type(model, uris, statement, obj, range)) { VNOTEF(serd_range_front(r), "Range of <%s> defined here\n", serd_node_get_string(pred)); } } serd_range_free(r); // Check domain SerdRange* d = serd_model_range(model, pred, uris->rdfs_domain, NULL, NULL); SERD_FOREACH (s, d) { const SerdNode* domain = serd_statement_get_object(s); if (has_explicit_type(model, uris, subj, domain)) { continue; // Explicit type will be checked, avoid duplicate reports } if (!check_type(model, uris, statement, subj, domain)) { VNOTEF(serd_range_front(d), "Domain of <%s> defined here\n", serd_node_get_string(pred)); } } serd_range_free(d); return st; } static int check_properties(SerdModel* model, URIs* uris) { int st = 0; SerdRange* i = serd_model_range(model, NULL, NULL, NULL, NULL); SERD_FOREACH (statement, i) { st = check_statement(model, uris, statement) || st; } serd_range_free(i); return st; } static int cardinality_error(const SerdStatement* statement, const SerdStatement* restriction_statement, const SerdNode* property, const SerdNode* instance, const unsigned actual_values, const char* comparison, const unsigned expected_values) { const int st = VERRORF(statement, "Property <%s> has %u %s %u values\n", serd_node_get_string(property), actual_values, comparison, expected_values); VNOTE(restriction_statement, "Restriction here\n"); return st; } static int check_class_restriction(SerdModel* model, const URIs* uris, const SerdNode* restriction, const SerdStatement* statement, const SerdNode* instance) { int st = 0; const SerdNode* prop = serd_model_get( model, restriction, uris->owl_onProperty, NULL, NULL); if (!prop) { return 0; } const unsigned values = serd_model_count(model, instance, prop, NULL, NULL); // Check exact cardinality const SerdStatement* c = serd_model_get_statement( model, restriction, uris->owl_cardinality, NULL, NULL); if (c) { const SerdNode* card = serd_statement_get_object(c); const unsigned count = atoi(serd_node_get_string(card)); if (check(values != count)) { st = cardinality_error( statement, c, prop, instance, values, "!=", count); } } // Check minimum cardinality const SerdStatement* l = serd_model_get_statement( model, restriction, uris->owl_minCardinality, NULL, NULL); if (l) { const SerdNode* card = serd_statement_get_object(l); const unsigned count = atoi(serd_node_get_string(card)); if (check(values < count)) { st = cardinality_error( statement, l, prop, instance, values, "<", count); } } // Check maximum cardinality const SerdStatement* u = serd_model_get_statement( model, restriction, uris->owl_maxCardinality, NULL, NULL); if (u) { const SerdNode* card = serd_statement_get_object(u); const unsigned count = atoi(serd_node_get_string(card)); if (check(values > count)) { st = cardinality_error( statement, u, prop, instance, values, ">", count); } } // Check someValuesFrom const SerdStatement* s = serd_model_get_statement( model, restriction, uris->owl_someValuesFrom, 0, 0); if (s) { const SerdNode* some = serd_statement_get_object(s); SerdRange* v = serd_model_range(model, instance, prop, NULL, NULL); bool found = false; SERD_FOREACH (i, v) { const SerdNode* value = serd_statement_get_object(i); if (check_type(model, uris, statement, value, some)) { found = true; break; } } if (check(!found)) { st = VERRORF(statement, "%s has no <%s> values of type <%s>\n", serd_node_get_string(instance), serd_node_get_string(prop), serd_node_get_string(some)); VNOTE(s, "Restriction here\n"); } serd_range_free(v); } // Check allValuesFrom const SerdStatement* a = serd_model_get_statement( model, restriction, uris->owl_allValuesFrom, 0, 0); if (a) { ++n_restrictions; const SerdNode* all = serd_statement_get_object(a); SerdRange* v = serd_model_range(model, instance, prop, NULL, NULL); SERD_FOREACH (i, v) { const SerdNode* value = serd_statement_get_object(i); if (!check_type(model, uris, statement, value, all)) { st = VERRORF(i, "<%s> value not of type <%s>\n", serd_node_get_string(prop), serd_node_get_string(all)); VNOTE(a, "Restriction here\n"); break; } } serd_range_free(v); } return st; } static int missing_arg(const char* name, char opt) { CERRORF("option requires an argument -- '%c'\n", opt); return print_usage(name, true); } static void init_uris(URIs* uris) { #define URI(prefix, suffix) \ uris->prefix##_##suffix = serd_new_uri(NS_##prefix #suffix) URI(foaf, Document); URI(owl, Class); URI(owl, DatatypeProperty); URI(owl, FunctionalProperty); URI(owl, InverseFunctionalProperty); URI(owl, ObjectProperty); URI(owl, Restriction); URI(owl, Thing); URI(owl, allValuesFrom); URI(owl, cardinality); URI(owl, equivalentClass); URI(owl, maxCardinality); URI(owl, minCardinality); URI(owl, onDatatype); URI(owl, onProperty); URI(owl, someValuesFrom); URI(owl, withRestrictions); URI(rdf, PlainLiteral); URI(rdf, Property); URI(rdf, first); URI(rdf, rest); URI(rdf, type); URI(rdfs, Class); URI(rdfs, Datatype); URI(rdfs, Literal); URI(rdfs, Resource); URI(rdfs, domain); URI(rdfs, label); URI(rdfs, range); URI(rdfs, subClassOf); URI(xsd, anyURI); URI(xsd, float); URI(xsd, decimal); URI(xsd, double); URI(xsd, maxExclusive); URI(xsd, maxInclusive); URI(xsd, minExclusive); URI(xsd, minInclusive); URI(xsd, pattern); URI(xsd, string); } int main(int argc, char** argv) { if (argc < 2) { return print_usage(argv[0], true); } int a = 1; long stack_size = 4194304; for (; a < argc && argv[a][0] == '-'; ++a) { if (argv[a][1] == 'h') { return print_usage(argv[0], false); } else if (argv[a][1] == 'k') { if (++a == argc) { return missing_arg(argv[0], 'k'); } stack_size = strtol(argv[a], NULL, 10); if (stack_size <= 0 || stack_size == LONG_MAX) { CERRORF("stack size `%ld' out of range\n", stack_size); return 1; } } else if (argv[a][1] == 'v') { return print_version(); } else { CERRORF("invalid option -- '%s'\n", argv[a] + 1); return print_usage(argv[0], true); } } SerdWorld* world = serd_world_new(); SerdModel* model = serd_model_new(world, SERD_SPO | SERD_OPS, false); SerdEnv* env = serd_env_new(NULL); SerdInserter* inserter = serd_inserter_new(model, env, NULL); SerdReader* reader = serd_reader_new( world, SERD_TURTLE, serd_inserter_get_sink(inserter), stack_size); for (; a < argc; ++a) { const char* input = argv[a]; char* in_path = absolute_path(input); if (!in_path) { CERRORF("unable to open file %s\n", input); continue; } SerdNode* base_uri_node = serd_new_file_uri(in_path, NULL); serd_env_set_base_uri(env, base_uri_node); SerdStatus st = serd_reader_start_file(reader, input, true); st = serd_reader_read_document(reader); st = serd_reader_finish(reader); if (st) { CERRORF("error reading %s: %s\n", in_path, serd_strerror(st)); return 1; } serd_node_free(base_uri_node); free(in_path); } serd_reader_free(reader); serd_env_free(env); URIs uris; init_uris(&uris); #ifndef HAVE_PCRE fprintf(stderr, "warning: Built without PCRE, datatypes not checked.\n"); #endif const int st = check_properties(model, &uris); printf("Found %d errors among %d files (checked %d restrictions)\n", n_errors, argc - 1, n_restrictions); serd_model_free(model); serd_world_free(world); return !st && n_errors == 0 ? 0 : 2; }