comparison ext/sord/src/sord_validate.c @ 226:c5cdc9e6a4bf

Add these external library files
author Chris Cannam <cannam@all-day-breakfast.com>
date Fri, 09 Jun 2017 16:41:31 +0100
parents
children
comparison
equal deleted inserted replaced
225:025b3e2f7c17 226:c5cdc9e6a4bf
1 /*
2 Copyright 2012-2017 David Robillard <http://drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #define _BSD_SOURCE 1 // for realpath
18 #define _DEFAULT_SOURCE 1 // for realpath
19
20 #include <assert.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #ifdef _WIN32
25 # include <windows.h>
26 #endif
27
28 #include "serd/serd.h"
29 #include "sord/sord.h"
30 #include "sord_config.h"
31
32 #ifdef HAVE_PCRE
33 # include <pcre.h>
34 #endif
35
36 #define USTR(s) ((const uint8_t*)s)
37
38 #define NS_foaf (const uint8_t*)"http://xmlns.com/foaf/0.1/"
39 #define NS_owl (const uint8_t*)"http://www.w3.org/2002/07/owl#"
40 #define NS_rdf (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#"
41 #define NS_rdfs (const uint8_t*)"http://www.w3.org/2000/01/rdf-schema#"
42 #define NS_xsd (const uint8_t*)"http://www.w3.org/2001/XMLSchema#"
43
44 typedef struct {
45 SordNode* foaf_Document;
46 SordNode* owl_AnnotationProperty;
47 SordNode* owl_Class;
48 SordNode* owl_DatatypeProperty;
49 SordNode* owl_FunctionalProperty;
50 SordNode* owl_InverseFunctionalProperty;
51 SordNode* owl_ObjectProperty;
52 SordNode* owl_OntologyProperty;
53 SordNode* owl_Restriction;
54 SordNode* owl_Thing;
55 SordNode* owl_cardinality;
56 SordNode* owl_equivalentClass;
57 SordNode* owl_maxCardinality;
58 SordNode* owl_minCardinality;
59 SordNode* owl_onDatatype;
60 SordNode* owl_onProperty;
61 SordNode* owl_someValuesFrom;
62 SordNode* owl_withRestrictions;
63 SordNode* rdf_PlainLiteral;
64 SordNode* rdf_Property;
65 SordNode* rdf_first;
66 SordNode* rdf_rest;
67 SordNode* rdf_type;
68 SordNode* rdfs_Class;
69 SordNode* rdfs_Literal;
70 SordNode* rdfs_Resource;
71 SordNode* rdfs_domain;
72 SordNode* rdfs_label;
73 SordNode* rdfs_range;
74 SordNode* rdfs_subClassOf;
75 SordNode* xsd_anyURI;
76 SordNode* xsd_decimal;
77 SordNode* xsd_double;
78 SordNode* xsd_maxInclusive;
79 SordNode* xsd_minInclusive;
80 SordNode* xsd_pattern;
81 SordNode* xsd_string;
82 } URIs;
83
84 int n_errors = 0;
85 int n_restrictions = 0;
86 bool one_line_errors = false;
87
88 static int
89 print_version(void)
90 {
91 printf("sord_validate " SORD_VERSION
92 " <http://drobilla.net/software/sord>\n");
93 printf("Copyright 2012-2017 David Robillard <http://drobilla.net>.\n"
94 "License: <http://www.opensource.org/licenses/isc>\n"
95 "This is free software; you are free to change and redistribute it."
96 "\nThere is NO WARRANTY, to the extent permitted by law.\n");
97 return 0;
98 }
99
100 static int
101 print_usage(const char* name, bool error)
102 {
103 FILE* const os = error ? stderr : stdout;
104 fprintf(os, "Usage: %s [OPTION]... INPUT...\n", name);
105 fprintf(os, "Validate RDF data\n\n");
106 fprintf(os, " -h Display this help and exit\n");
107 fprintf(os, " -l Print errors on a single line.\n");
108 fprintf(os, " -v Display version information and exit\n");
109 fprintf(os,
110 "Validate RDF data. This is a simple validator which checks\n"
111 "that all used properties are actually defined. It does not do\n"
112 "any fancy file retrieval, the files passed on the command line\n"
113 "are the only data that is read. In other words, you must pass\n"
114 "the definition of all vocabularies used on the command line.\n");
115 return error ? 1 : 0;
116 }
117
118 static uint8_t*
119 absolute_path(const uint8_t* path)
120 {
121 #ifdef _WIN32
122 char* out = (char*)malloc(MAX_PATH);
123 GetFullPathName((const char*)path, MAX_PATH, out, NULL);
124 return (uint8_t*)out;
125 #else
126 return (uint8_t*)realpath((const char*)path, NULL);
127 #endif
128 }
129
130 static int
131 errorf(const SordQuad quad, const char* fmt, ...)
132 {
133 va_list args;
134 va_start(args, fmt);
135 fprintf(stderr, "error: ");
136 vfprintf(stderr, fmt, args);
137 va_end(args);
138
139 const char* sep = one_line_errors ? "\t" : "\n ";
140 fprintf(stderr, "%s%s%s%s%s%s\n",
141 sep, (const char*)sord_node_get_string(quad[SORD_SUBJECT]),
142 sep, (const char*)sord_node_get_string(quad[SORD_PREDICATE]),
143 sep, (const char*)sord_node_get_string(quad[SORD_OBJECT]));
144
145 ++n_errors;
146 return 1;
147 }
148
149 static bool
150 is_descendant_of(SordModel* model,
151 const URIs* uris,
152 const SordNode* child,
153 const SordNode* parent,
154 const SordNode* pred)
155 {
156 if (!child) {
157 return false;
158 } else if (sord_node_equals(child, parent) ||
159 sord_ask(model, child, uris->owl_equivalentClass, parent, NULL)) {
160 return true;
161 }
162
163 SordIter* i = sord_search(model, child, pred, NULL, NULL);
164 for (; !sord_iter_end(i); sord_iter_next(i)) {
165 const SordNode* o = sord_iter_get_node(i, SORD_OBJECT);
166 if (sord_node_equals(child, o)) {
167 continue; // Weird class is explicitly a descendent of itself
168 }
169 if (is_descendant_of(model, uris, o, parent, pred)) {
170 sord_iter_free(i);
171 return true;
172 }
173 }
174 sord_iter_free(i);
175
176 return false;
177 }
178
179 static bool
180 regexp_match(const uint8_t* pat, const char* str)
181 {
182 #ifdef HAVE_PCRE
183 // Append a $ to the pattern so we only match if the entire string matches
184 const size_t len = strlen((const char*)pat);
185 char* const regx = (char*)malloc(len + 2);
186 memcpy(regx, pat, len);
187 regx[len] = '$';
188 regx[len + 1] = '\0';
189
190 const char* err;
191 int erroffset;
192 pcre* re = pcre_compile(regx, PCRE_ANCHORED, &err, &erroffset, NULL);
193 free(regx);
194 if (!re) {
195 fprintf(stderr, "Error in pattern `%s' at offset %d (%s)\n",
196 pat, erroffset, err);
197 return false;
198 }
199
200 const bool ret = pcre_exec(re, NULL, str, strlen(str), 0, 0, NULL, 0) >= 0;
201 pcre_free(re);
202 return ret;
203 #endif // HAVE_PCRE
204 return true;
205 }
206
207 static int
208 bound_cmp(SordModel* model,
209 const URIs* uris,
210 const SordNode* literal,
211 const SordNode* type,
212 const SordNode* bound)
213 {
214 const char* str = (const char*)sord_node_get_string(literal);
215 const char* bound_str = (const char*)sord_node_get_string(bound);
216 const bool is_numeric =
217 is_descendant_of(model, uris, type, uris->xsd_decimal, uris->owl_onDatatype) ||
218 is_descendant_of(model, uris, type, uris->xsd_double, uris->owl_onDatatype);
219
220 if (is_numeric) {
221 const double fbound = serd_strtod(bound_str, NULL);
222 const double fliteral = serd_strtod(str, NULL);
223 return ((fliteral < fbound) ? -1 :
224 (fliteral > fbound) ? 1 :
225 0);
226 } else {
227 return strcmp(str, bound_str);
228 }
229 }
230
231 static bool
232 check_restriction(SordModel* model,
233 const URIs* uris,
234 const SordNode* literal,
235 const SordNode* type,
236 const SordNode* restriction)
237 {
238 size_t len = 0;
239 const char* str = (const char*)sord_node_get_string_counted(literal, &len);
240
241 // Check xsd:pattern
242 SordIter* p = sord_search(model, restriction, uris->xsd_pattern, 0, 0);
243 if (p) {
244 const SordNode* pat = sord_iter_get_node(p, SORD_OBJECT);
245 if (!regexp_match(sord_node_get_string(pat), str)) {
246 fprintf(stderr, "`%s' does not match <%s> pattern `%s'\n",
247 sord_node_get_string(literal),
248 sord_node_get_string(type),
249 sord_node_get_string(pat));
250 sord_iter_free(p);
251 return false;
252 }
253 sord_iter_free(p);
254 ++n_restrictions;
255 }
256
257 // Check xsd:minInclusive
258 SordIter* l = sord_search(model, restriction, uris->xsd_minInclusive, 0, 0);
259 if (l) {
260 const SordNode* lower = sord_iter_get_node(l, SORD_OBJECT);
261 if (bound_cmp(model, uris, literal, type, lower) < 0) {
262 fprintf(stderr, "`%s' is not >= <%s> minimum `%s'\n",
263 sord_node_get_string(literal),
264 sord_node_get_string(type),
265 sord_node_get_string(lower));
266 sord_iter_free(l);
267 return false;
268 }
269 sord_iter_free(l);
270 ++n_restrictions;
271 }
272
273 // Check xsd:maxInclusive
274 SordIter* u = sord_search(model, restriction, uris->xsd_maxInclusive, 0, 0);
275 if (u) {
276 const SordNode* upper = sord_iter_get_node(u, SORD_OBJECT);
277 if (bound_cmp(model, uris, literal, type, upper) > 0) {
278 fprintf(stderr, "`%s' is not <= <%s> maximum `%s'\n",
279 sord_node_get_string(literal),
280 sord_node_get_string(type),
281 sord_node_get_string(upper));
282 sord_iter_free(u);
283 return false;
284 }
285 sord_iter_free(u);
286 ++n_restrictions;
287 }
288
289 return true; // Unknown restriction, be quietly tolerant
290 }
291
292 static bool
293 literal_is_valid(SordModel* model,
294 const URIs* uris,
295 const SordQuad quad,
296 const SordNode* literal,
297 const SordNode* type)
298 {
299 if (!type) {
300 return true;
301 }
302
303 /* Check that literal data is related to required type. We don't do a
304 strict subtype check here because e.g. an xsd:decimal might be a valid
305 xsd:unsignedInt, which the pattern checks will verify, but if the
306 literal type is not related to the required type at all
307 (e.g. xsd:decimal and xsd:string) there is a problem. */
308 const SordNode* datatype = sord_node_get_datatype(literal);
309 if (datatype && datatype != type) {
310 if (!is_descendant_of(
311 model, uris,
312 datatype, type, uris->owl_onDatatype) &&
313 !is_descendant_of(
314 model, uris,
315 type, datatype, uris->owl_onDatatype) &&
316 !(sord_node_equals(datatype, uris->xsd_decimal) &&
317 is_descendant_of(
318 model, uris,
319 type, uris->xsd_double, uris->owl_onDatatype))) {
320 errorf(quad,
321 "Literal `%s' datatype <%s> is not compatible with <%s>\n",
322 sord_node_get_string(literal),
323 sord_node_get_string(datatype),
324 sord_node_get_string(type));
325 return false;
326 }
327 }
328
329 // Find restrictions list
330 SordIter* rs = sord_search(model, type, uris->owl_withRestrictions, 0, 0);
331 if (sord_iter_end(rs)) {
332 return true; // No restrictions
333 }
334
335 // Walk list, checking each restriction
336 const SordNode* head = sord_iter_get_node(rs, SORD_OBJECT);
337 while (head) {
338 SordIter* f = sord_search(model, head, uris->rdf_first, 0, 0);
339 if (!f) {
340 break; // Reached end of restrictions list without failure
341 }
342
343 // Check this restriction
344 const bool good = check_restriction(
345 model, uris, literal, type, sord_iter_get_node(f, SORD_OBJECT));
346 sord_iter_free(f);
347
348 if (!good) {
349 sord_iter_free(rs);
350 return false; // Failed, literal is invalid
351 }
352
353 // Seek to next list node
354 SordIter* n = sord_search(model, head, uris->rdf_rest, 0, 0);
355 head = n ? sord_iter_get_node(n, SORD_OBJECT) : NULL;
356 sord_iter_free(n);
357 }
358
359 sord_iter_free(rs);
360
361 SordIter* s = sord_search(model, type, uris->owl_onDatatype, 0, 0);
362 if (s) {
363 const SordNode* super = sord_iter_get_node(s, SORD_OBJECT);
364 const bool good = literal_is_valid(
365 model, uris, quad, literal, super);
366 sord_iter_free(s);
367 return good; // Match iff literal also matches supertype
368 }
369
370 return true; // Matches top level type
371 }
372
373 static bool
374 check_type(SordModel* model,
375 const URIs* uris,
376 const SordQuad quad,
377 const SordNode* node,
378 const SordNode* type)
379 {
380 if (sord_node_equals(type, uris->rdfs_Resource) ||
381 sord_node_equals(type, uris->owl_Thing)) {
382 return true;
383 }
384
385 if (sord_node_get_type(node) == SORD_LITERAL) {
386 if (sord_node_equals(type, uris->rdfs_Literal)) {
387 return true;
388 } else if (sord_node_equals(type, uris->rdf_PlainLiteral)) {
389 return !sord_node_get_language(node);
390 } else {
391 return literal_is_valid(model, uris, quad, node, type);
392 }
393 } else if (sord_node_get_type(node) == SORD_URI) {
394 if (sord_node_equals(type, uris->foaf_Document)) {
395 return true; // Questionable...
396 } else if (is_descendant_of(
397 model, uris,
398 type, uris->xsd_anyURI, uris->owl_onDatatype)) {
399 /* Type is any URI and this is a URI, so pass. Restrictions on
400 anyURI subtypes are not currently checked (very uncommon). */
401 return true; // Type is anyURI, and this is a URI
402 } else {
403 SordIter* t = sord_search(model, node, uris->rdf_type, NULL, NULL);
404 for (; !sord_iter_end(t); sord_iter_next(t)) {
405 if (is_descendant_of(model, uris,
406 sord_iter_get_node(t, SORD_OBJECT),
407 type,
408 uris->rdfs_subClassOf)) {
409 sord_iter_free(t);
410 return true;
411 }
412 }
413 sord_iter_free(t);
414 return false;
415 }
416 } else {
417 return true; // Blanks often lack explicit types, ignore
418 }
419
420 return false;
421 }
422
423 static uint64_t
424 count_non_blanks(SordIter* i, SordQuadIndex field)
425 {
426 uint64_t n = 0;
427 for (; !sord_iter_end(i); sord_iter_next(i)) {
428 const SordNode* node = sord_iter_get_node(i, field);
429 if (sord_node_get_type(node) != SORD_BLANK) {
430 ++n;
431 }
432 }
433 return n;
434 }
435
436 static int
437 check_properties(SordModel* model, URIs* uris)
438 {
439 int st = 0;
440 SordIter* i = sord_begin(model);
441 for (; !sord_iter_end(i); sord_iter_next(i)) {
442 SordQuad quad;
443 sord_iter_get(i, quad);
444
445 const SordNode* subj = quad[SORD_SUBJECT];
446 const SordNode* pred = quad[SORD_PREDICATE];
447 const SordNode* obj = quad[SORD_OBJECT];
448
449 bool is_any_property = false;
450 SordIter* t = sord_search(model, pred, uris->rdf_type, NULL, NULL);
451 for (; !sord_iter_end(t); sord_iter_next(t)) {
452 if (is_descendant_of(model, uris,
453 sord_iter_get_node(t, SORD_OBJECT),
454 uris->rdf_Property,
455 uris->rdfs_subClassOf)) {
456 is_any_property = true;
457 break;
458 }
459 }
460 sord_iter_free(t);
461
462 const bool is_ObjectProperty = sord_ask(
463 model, pred, uris->rdf_type, uris->owl_ObjectProperty, 0);
464 const bool is_FunctionalProperty = sord_ask(
465 model, pred, uris->rdf_type, uris->owl_FunctionalProperty, 0);
466 const bool is_InverseFunctionalProperty = sord_ask(
467 model, pred, uris->rdf_type, uris->owl_InverseFunctionalProperty, 0);
468 const bool is_DatatypeProperty = sord_ask(
469 model, pred, uris->rdf_type, uris->owl_DatatypeProperty, 0);
470
471 if (!is_any_property) {
472 st = errorf(quad, "Use of undefined property");
473 }
474
475 if (!sord_ask(model, pred, uris->rdfs_label, NULL, NULL)) {
476 st = errorf(quad, "Property <%s> has no label",
477 sord_node_get_string(pred));
478 }
479
480 if (is_DatatypeProperty &&
481 sord_node_get_type(obj) != SORD_LITERAL) {
482 st = errorf(quad, "Datatype property with non-literal value");
483 }
484
485 if (is_ObjectProperty &&
486 sord_node_get_type(obj) == SORD_LITERAL) {
487 st = errorf(quad, "Object property with literal value");
488 }
489
490 if (is_FunctionalProperty) {
491 SordIter* o = sord_search(model, subj, pred, NULL, NULL);
492 const uint64_t n = count_non_blanks(o, SORD_OBJECT);
493 if (n > 1) {
494 st = errorf(quad, "Functional property with %u objects", n);
495 }
496 sord_iter_free(o);
497 }
498
499 if (is_InverseFunctionalProperty) {
500 SordIter* s = sord_search(model, NULL, pred, obj, NULL);
501 const unsigned n = count_non_blanks(s, SORD_SUBJECT);
502 if (n > 1) {
503 st = errorf(
504 quad, "Inverse functional property with %u subjects", n);
505 }
506 sord_iter_free(s);
507 }
508
509 if (sord_node_equals(pred, uris->rdf_type) &&
510 !sord_ask(model, obj, uris->rdf_type, uris->rdfs_Class, NULL) &&
511 !sord_ask(model, obj, uris->rdf_type, uris->owl_Class, NULL)) {
512 st = errorf(quad, "Type is not a rdfs:Class or owl:Class");
513 }
514
515 if (sord_node_get_type(obj) == SORD_LITERAL &&
516 !literal_is_valid(model, uris, quad,
517 obj, sord_node_get_datatype(obj))) {
518 st = errorf(quad, "Literal does not match datatype");
519 }
520
521 SordIter* r = sord_search(model, pred, uris->rdfs_range, NULL, NULL);
522 for (; !sord_iter_end(r); sord_iter_next(r)) {
523 const SordNode* range = sord_iter_get_node(r, SORD_OBJECT);
524 if (!check_type(model, uris, quad, obj, range)) {
525 st = errorf(quad, "Object not in range <%s>\n",
526 sord_node_get_string(range));
527 }
528 }
529 sord_iter_free(r);
530
531 SordIter* d = sord_search(model, pred, uris->rdfs_domain, NULL, NULL);
532 if (d) {
533 const SordNode* domain = sord_iter_get_node(d, SORD_OBJECT);
534 if (!check_type(model, uris, quad, subj, domain)) {
535 st = errorf(quad, "Subject not in domain <%s>",
536 sord_node_get_string(domain));
537 }
538 sord_iter_free(d);
539 }
540 }
541 sord_iter_free(i);
542
543 return st;
544 }
545
546 static int
547 check_instance(SordModel* model,
548 const URIs* uris,
549 const SordNode* restriction,
550 const SordQuad quad)
551 {
552 const SordNode* instance = quad[SORD_SUBJECT];
553 int st = 0;
554
555 const SordNode* prop = sord_get(
556 model, restriction, uris->owl_onProperty, NULL, NULL);
557 if (!prop) {
558 return 0;
559 }
560
561 const unsigned values = sord_count(model, instance, prop, NULL, NULL);
562
563 // Check exact cardinality
564 const SordNode* card = sord_get(
565 model, restriction, uris->owl_cardinality, NULL, NULL);
566 if (card) {
567 const unsigned c = atoi((const char*)sord_node_get_string(card));
568 if (values != c) {
569 st = errorf(quad, "Property %s on %s has %u != %u values",
570 sord_node_get_string(prop),
571 sord_node_get_string(instance),
572 values, c);
573 }
574 }
575
576 // Check minimum cardinality
577 const SordNode* minCard = sord_get(
578 model, restriction, uris->owl_minCardinality, NULL, NULL);
579 if (minCard) {
580 const unsigned m = atoi((const char*)sord_node_get_string(minCard));
581 if (values < m) {
582 st = errorf(quad, "Property %s on %s has %u < %u values",
583 sord_node_get_string(prop),
584 sord_node_get_string(instance),
585 values, m);
586 }
587 }
588
589 // Check maximum cardinality
590 const SordNode* maxCard = sord_get(
591 model, restriction, uris->owl_maxCardinality, NULL, NULL);
592 if (maxCard) {
593 const unsigned m = atoi((const char*)sord_node_get_string(maxCard));
594 if (values < m) {
595 st = errorf(quad, "Property %s on %s has %u > %u values",
596 sord_node_get_string(prop),
597 sord_node_get_string(instance),
598 values, m);
599 }
600 }
601
602 // Check someValuesFrom
603 SordIter* sf = sord_search(
604 model, restriction, uris->owl_someValuesFrom, NULL, NULL);
605 if (sf) {
606 const SordNode* type = sord_iter_get_node(sf, SORD_OBJECT);
607
608 SordIter* v = sord_search(model, instance, prop, NULL, NULL);
609 bool found = false;
610 for (; !sord_iter_end(v); sord_iter_next(v)) {
611 const SordNode* value = sord_iter_get_node(v, SORD_OBJECT);
612 if (check_type(model, uris, quad, value, type)) {
613 found = true;
614 break;
615 }
616 }
617 if (!found) {
618 st = errorf(quad, "%s has no <%s> values of type <%s>\n",
619 sord_node_get_string(instance),
620 sord_node_get_string(prop),
621 sord_node_get_string(type));
622 }
623 sord_iter_free(v);
624 }
625 sord_iter_free(sf);
626
627 return st;
628 }
629
630 static int
631 check_class_instances(SordModel* model,
632 const URIs* uris,
633 const SordNode* restriction,
634 const SordNode* klass)
635 {
636 // Check immediate instances of this class
637 SordIter* i = sord_search(model, NULL, uris->rdf_type, klass, NULL);
638 for (; !sord_iter_end(i); sord_iter_next(i)) {
639 SordQuad quad;
640 sord_iter_get(i, quad);
641 check_instance(model, uris, restriction, quad);
642 }
643 sord_iter_free(i);
644
645 // Check instances of all subclasses recursively
646 SordIter* s = sord_search(model, NULL, uris->rdfs_subClassOf, klass, NULL);
647 for (; !sord_iter_end(s); sord_iter_next(s)) {
648 const SordNode* subklass = sord_iter_get_node(s, SORD_SUBJECT);
649 check_class_instances(model, uris, restriction, subklass);
650 }
651 sord_iter_free(s);
652
653 return 0;
654 }
655
656 static int
657 check_instances(SordModel* model, const URIs* uris)
658 {
659 int st = 0;
660 SordIter* r = sord_search(
661 model, NULL, uris->rdf_type, uris->owl_Restriction, NULL);
662 for (; !sord_iter_end(r); sord_iter_next(r)) {
663 const SordNode* restriction = sord_iter_get_node(r, SORD_SUBJECT);
664 const SordNode* prop = sord_get(
665 model, restriction, uris->owl_onProperty, NULL, NULL);
666 if (!prop) {
667 continue;
668 }
669
670 SordIter* c = sord_search(
671 model, NULL, uris->rdfs_subClassOf, restriction, NULL);
672 for (; !sord_iter_end(c); sord_iter_next(c)) {
673 const SordNode* klass = sord_iter_get_node(c, SORD_SUBJECT);
674 check_class_instances(model, uris, restriction, klass);
675 }
676 sord_iter_free(c);
677 }
678 sord_iter_free(r);
679
680 return st;
681 }
682
683 int
684 main(int argc, char** argv)
685 {
686 if (argc < 2) {
687 return print_usage(argv[0], true);
688 }
689
690 int a = 1;
691 for (; a < argc && argv[a][0] == '-'; ++a) {
692 if (argv[a][1] == 'l') {
693 one_line_errors = true;
694 } else if (argv[a][1] == 'v') {
695 return print_version();
696 } else {
697 fprintf(stderr, "%s: Unknown option `%s'\n", argv[0], argv[a]);
698 return print_usage(argv[0], true);
699 }
700 }
701
702 SordWorld* world = sord_world_new();
703 SordModel* model = sord_new(world, SORD_SPO|SORD_OPS, false);
704 SerdEnv* env = serd_env_new(&SERD_NODE_NULL);
705 SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
706
707 for (; a < argc; ++a) {
708 const uint8_t* input = (const uint8_t*)argv[a];
709 uint8_t* in_path = absolute_path(serd_uri_to_path(input));
710
711 if (!in_path) {
712 fprintf(stderr, "Skipping file %s\n", input);
713 continue;
714 }
715
716 SerdURI base_uri;
717 SerdNode base_uri_node = serd_node_new_file_uri(
718 in_path, NULL, &base_uri, true);
719
720 serd_env_set_base_uri(env, &base_uri_node);
721 const SerdStatus st = serd_reader_read_file(reader, in_path);
722 if (st) {
723 fprintf(stderr, "error reading %s: %s\n",
724 in_path, serd_strerror(st));
725 }
726
727 serd_node_free(&base_uri_node);
728 free(in_path);
729 }
730 serd_reader_free(reader);
731 serd_env_free(env);
732
733 #define URI(prefix, suffix) \
734 uris.prefix##_##suffix = sord_new_uri(world, NS_##prefix #suffix)
735
736 URIs uris;
737 URI(foaf, Document);
738 URI(owl, AnnotationProperty);
739 URI(owl, Class);
740 URI(owl, DatatypeProperty);
741 URI(owl, FunctionalProperty);
742 URI(owl, InverseFunctionalProperty);
743 URI(owl, ObjectProperty);
744 URI(owl, OntologyProperty);
745 URI(owl, Restriction);
746 URI(owl, Thing);
747 URI(owl, cardinality);
748 URI(owl, equivalentClass);
749 URI(owl, maxCardinality);
750 URI(owl, minCardinality);
751 URI(owl, onDatatype);
752 URI(owl, onProperty);
753 URI(owl, someValuesFrom);
754 URI(owl, withRestrictions);
755 URI(rdf, PlainLiteral);
756 URI(rdf, Property);
757 URI(rdf, first);
758 URI(rdf, rest);
759 URI(rdf, type);
760 URI(rdfs, Class);
761 URI(rdfs, Literal);
762 URI(rdfs, Resource);
763 URI(rdfs, domain);
764 URI(rdfs, label);
765 URI(rdfs, range);
766 URI(rdfs, subClassOf);
767 URI(xsd, anyURI);
768 URI(xsd, decimal);
769 URI(xsd, double);
770 URI(xsd, maxInclusive);
771 URI(xsd, minInclusive);
772 URI(xsd, pattern);
773 URI(xsd, string);
774
775 #ifndef HAVE_PCRE
776 fprintf(stderr, "warning: Built without PCRE, datatypes not checked.\n");
777 #endif
778
779 const int prop_st = check_properties(model, &uris);
780 const int inst_st = check_instances(model, &uris);
781
782 printf("Found %d errors among %d files (checked %d restrictions)\n",
783 n_errors, argc - 1, n_restrictions);
784
785 sord_free(model);
786 sord_world_free(world);
787 return prop_st || inst_st;
788 }