Permalink
Browse files

Add reference/footnote validation

  • Loading branch information...
1 parent cc5da22 commit 1616e5ebe120b36174647b0119949e8fd180f850 Nikolai Weibull committed Aug 8, 2012
Showing with 136 additions and 10 deletions.
  1. +2 −1 Makefile.am
  2. +11 −0 src/nmc.c
  3. +108 −0 src/validator.c
  4. +6 −0 src/validator.h
  5. +9 −9 test/testsuite.at
View
@@ -10,7 +10,8 @@ bin_PROGRAMS = src/nmc
src_nmc_SOURCES = \
src/parser.c \
src/nmc.c \
- src/grammar.y
+ src/grammar.y \
+ src/validator.c
src_nmc_LDADD = $(XML_LIBS)
View
@@ -8,9 +8,17 @@
#include "grammar.h"
#include "nmc.h"
#include "parser.h"
+#include "validator.h"
extern int nmc_grammar_debug;
+static int
+report_error(const char *error)
+{
+ fputs(error, stderr);
+ return 1;
+}
+
int
main(UNUSED(int argc), UNUSED(char **argv))
{
@@ -21,6 +29,9 @@ main(UNUSED(int argc), UNUSED(char **argv))
if (getenv("NMC_DEBUG"))
nmc_grammar_debug = 1;
xmlDocPtr doc = nmc_parse(BAD_CAST buffer);
+ xmlListPtr errors = nmc_validate(doc);
+ xmlListWalk(errors, (xmlListWalker)report_error, NULL);
+ xmlListDelete(errors);
xmlSaveFormatFileEnc("-", doc, "UTF-8", 1);
xmlFreeDoc(doc);
xmlCleanupParser();
View
@@ -0,0 +1,108 @@
+#include <config.h>
+
+#include <libxml/tree.h>
+#include <stdbool.h>
+
+#include "validator.h"
+
+struct nmc_validator
+{
+ xmlListPtr footnotes;
+ xmlListPtr errors;
+};
+
+struct nmc_reference_closure
+{
+ const xmlChar *id;
+ bool found;
+};
+
+static void
+error(struct nmc_validator *validator, const char *message, ...)
+{
+ va_list args;
+ xmlChar buf[1];
+
+ va_start(args, message);
+ int size = xmlStrVPrintf(buf, sizeof(buf), (const xmlChar *)message, args);
+ va_end(args);
+
+ char *error = (char *)xmlMalloc(size);
+ va_start(args, message);
+ xmlStrVPrintf((xmlChar *)error, size, (const xmlChar *)message, args);
+ va_end(args);
+
+ xmlListPushBack(validator->errors, error);
+}
+
+static void
+error_free(xmlLinkPtr link)
+{
+ xmlFree(xmlLinkGetData(link));
+}
+
+static const xmlChar *
+id(xmlNodePtr node)
+{
+ return xmlHasProp(node, BAD_CAST "id")->children->content;
+}
+
+static int
+validate_reference(xmlNodePtr footnotes, struct nmc_reference_closure *closure)
+{
+ for (xmlNodePtr p = footnotes->children; p != NULL; p = p->next)
+ if (xmlStrEqual(id(p), closure->id)) {
+ xmlNewProp(p, BAD_CAST "referenced", BAD_CAST "true");
+ closure->found = true;
+ return 0;
+ }
+ return 1;
+}
+
+static void
+validate_footnotes(struct nmc_validator *validator, xmlNodePtr footnotes)
+{
+ for (xmlNodePtr p = footnotes->children; p != NULL; p = p->next)
+ if (!xmlHasProp(p, BAD_CAST "referenced"))
+ error(validator, "unreferenced footnote: %s\n", id(p));
+}
+
+static void
+validate_node(struct nmc_validator *validator, xmlNodePtr node)
+{
+ if (node->type != XML_ELEMENT_NODE)
+ return;
+
+ if (xmlStrEqual(node->name, BAD_CAST "footnoted")) {
+ xmlListPushFront(validator->footnotes, xmlGetLastChild(node));
+ validate_node(validator, node->children);
+ xmlNodePtr footnotes = xmlLinkGetData(xmlListFront(validator->footnotes));
+ xmlListPopFront(validator->footnotes);
+ validate_footnotes(validator, footnotes);
+ return;
+ } else if (xmlStrEqual(node->name, BAD_CAST "reference")) {
+ struct nmc_reference_closure closure = { id(node), false };
+ xmlListWalk(validator->footnotes,
+ (xmlListWalker)validate_reference,
+ &closure);
+ if (!closure.found)
+ error(validator,
+ "reference to undefined footnote: %s",
+ closure.id);
+ }
+
+ for (xmlNodePtr p = node->children; p != NULL; p = p->next)
+ validate_node(validator, p);
+}
+
+xmlListPtr
+nmc_validate(xmlDocPtr doc)
+{
+ struct nmc_validator validator;
+ validator.footnotes = xmlListCreate(NULL, NULL);
+ validator.errors = xmlListCreate(error_free, NULL);
+
+ validate_node(&validator, xmlDocGetRootElement(doc));
+
+ return validator.errors;
+}
View
@@ -0,0 +1,6 @@
+#ifndef SRC_VALIDATOR_H
+#define SRC_VALIDATOR_H
+
+xmlListPtr nmc_validate(xmlDocPtr doc);
+
+#endif
View
@@ -335,9 +335,9 @@ AT_NMC_CHECK([title],
<footnoted>
<p><reference id="²"><reference id="¹">Word1</reference></reference> Word2 <reference id="³³">Word3</reference></p>
<footnotes>
- <footnote id="¹">Footnote1</footnote>
- <footnote id="²">Footnote2</footnote>
- <footnote id="³³">Footnote33</footnote>
+ <footnote id="¹" referenced="true">Footnote1</footnote>
+ <footnote id="²" referenced="true">Footnote2</footnote>
+ <footnote id="³³" referenced="true">Footnote33</footnote>
</footnotes>
</footnoted>]])
@@ -350,7 +350,7 @@ AT_DATA([title],
§ Section
- Paragraph
+ Word1 Word2¹ Word3
¹ Footnote
]])
@@ -360,9 +360,9 @@ AT_NMC_CHECK([title],
<section>
<title>Section</title>
<footnoted>
- <p>Paragraph</p>
+ <p>Word1 <reference id="¹">Word2</reference> Word3</p>
<footnotes>
- <footnote id="¹">Footnote</footnote>
+ <footnote id="¹" referenced="true">Footnote</footnote>
</footnotes>
</footnoted>
</section>]])
@@ -376,7 +376,7 @@ AT_DATA([title],
§ Section
- Paragraph
+ Word1 Word2¹ Word3
¹ Footnote
]])
@@ -386,10 +386,10 @@ AT_NMC_CHECK([title],
<footnoted>
<section>
<title>Section</title>
- <p>Paragraph</p>
+ <p>Word1 <reference id="¹">Word2</reference> Word3</p>
</section>
<footnotes>
- <footnote id="¹">Footnote</footnote>
+ <footnote id="¹" referenced="true">Footnote</footnote>
</footnotes>
</footnoted>]])

0 comments on commit 1616e5e

Please sign in to comment.