-/* $Id: mdoc.c,v 1.66 2009/03/16 23:37:28 kristaps Exp $ */
+/* $Id: mdoc.c,v 1.74 2009/04/02 06:51:44 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
*
*/
#include <assert.h>
#include <ctype.h>
-#include <err.h>
#include <stdarg.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include "private.h"
-
-/*
- * Main caller in the libmdoc library. This begins the parsing routine,
- * handles allocation of data, and so forth. Most of the "work" is done
- * in macro.c and validate.c.
- */
-
-static struct mdoc_node *mdoc_node_alloc(const struct mdoc *);
-static int mdoc_node_append(struct mdoc *,
- struct mdoc_node *);
-
-static int parsetext(struct mdoc *, int, char *);
-static int parsemacro(struct mdoc *, int, char *);
-static int macrowarn(struct mdoc *, int, const char *);
+#include "libmdoc.h"
+enum merr {
+ ENOCALL,
+ EBODYPROL,
+ EPROLBODY,
+ ESPACE,
+ ETEXTPROL,
+ ENOBLANK,
+ EMALLOC
+};
const char *const __mdoc_macronames[MDOC_MAX] = {
"\\\"", "Dd", "Dt", "Os",
"Lp", "Lk", "Mt", "Brq",
/* LINTED */
"Bro", "Brc", "\%C", "Es",
- "En", "Dx"
+ /* LINTED */
+ "En", "Dx", "\%Q"
};
const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
const char * const *mdoc_macronames = __mdoc_macronames;
const char * const *mdoc_argnames = __mdoc_argnames;
+static void mdoc_free1(struct mdoc *);
+static int mdoc_alloc1(struct mdoc *);
+static struct mdoc_node *node_alloc(struct mdoc *, int, int,
+ int, enum mdoc_type);
+static int node_append(struct mdoc *,
+ struct mdoc_node *);
+static int parsetext(struct mdoc *, int, char *);
+static int parsemacro(struct mdoc *, int, char *);
+static int macrowarn(struct mdoc *, int, const char *);
+static int perr(struct mdoc *, int, int, enum merr);
+
+#define verr(m, t) perr((m), (m)->last->line, (m)->last->pos, (t))
+/*
+ * Get the first (root) node of the parse tree.
+ */
const struct mdoc_node *
-mdoc_node(const struct mdoc *mdoc)
+mdoc_node(const struct mdoc *m)
{
- return(mdoc->first);
+ return(MDOC_HALT & m->flags ? NULL : m->first);
}
const struct mdoc_meta *
-mdoc_meta(const struct mdoc *mdoc)
+mdoc_meta(const struct mdoc *m)
{
- return(&mdoc->meta);
+ return(MDOC_HALT & m->flags ? NULL : &m->meta);
}
-void
-mdoc_free(struct mdoc *mdoc)
+static void
+mdoc_free1(struct mdoc *mdoc)
{
if (mdoc->first)
mdoc_node_freelist(mdoc->first);
- if (mdoc->htab)
- mdoc_tokhash_free(mdoc->htab);
if (mdoc->meta.title)
free(mdoc->meta.title);
if (mdoc->meta.os)
free(mdoc->meta.arch);
if (mdoc->meta.vol)
free(mdoc->meta.vol);
+}
+
+
+static int
+mdoc_alloc1(struct mdoc *mdoc)
+{
+
+ bzero(&mdoc->meta, sizeof(struct mdoc_meta));
+ mdoc->flags = 0;
+ mdoc->lastnamed = mdoc->lastsec = 0;
+ mdoc->last = calloc(1, sizeof(struct mdoc_node));
+ if (NULL == mdoc->last)
+ return(0);
+
+ mdoc->first = mdoc->last;
+ mdoc->last->type = MDOC_ROOT;
+ mdoc->next = MDOC_NEXT_CHILD;
+ return(1);
+}
+
+/*
+ * Free up all resources contributed by a parse: the node tree,
+ * meta-data and so on. Then reallocate the root node for another
+ * parse.
+ */
+int
+mdoc_reset(struct mdoc *mdoc)
+{
+
+ mdoc_free1(mdoc);
+ return(mdoc_alloc1(mdoc));
+}
+
+
+/*
+ * Completely free up all resources.
+ */
+void
+mdoc_free(struct mdoc *mdoc)
+{
+
+ mdoc_free1(mdoc);
+ if (mdoc->htab)
+ mdoc_hash_free(mdoc->htab);
free(mdoc);
}
{
struct mdoc *p;
- p = xcalloc(1, sizeof(struct mdoc));
-
- p->data = data;
+ if (NULL == (p = calloc(1, sizeof(struct mdoc))))
+ return(NULL);
if (cb)
(void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
- p->last = xcalloc(1, sizeof(struct mdoc_node));
- p->last->type = MDOC_ROOT;
- p->first = p->last;
+ p->data = data;
p->pflags = pflags;
- p->next = MDOC_NEXT_CHILD;
- p->htab = mdoc_tokhash_alloc();
- return(p);
+ if (NULL == (p->htab = mdoc_hash_alloc())) {
+ free(p);
+ return(NULL);
+ } else if (mdoc_alloc1(p))
+ return(p);
+
+ free(p);
+ return(NULL);
}
+/*
+ * Climb back up the parse tree, validating open scopes. Mostly calls
+ * through to macro_end in macro.c.
+ */
int
-mdoc_endparse(struct mdoc *mdoc)
+mdoc_endparse(struct mdoc *m)
{
- if (MDOC_HALT & mdoc->flags)
+ if (MDOC_HALT & m->flags)
return(0);
- if (NULL == mdoc->first)
+ else if (mdoc_macroend(m))
return(1);
-
- assert(mdoc->last);
- if ( ! macro_end(mdoc)) {
- mdoc->flags |= MDOC_HALT;
- return(0);
- }
- return(1);
+ m->flags |= MDOC_HALT;
+ return(0);
}
if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
SEC_PROLOGUE != m->lastnamed)
- return(mdoc_perr(m, ln, pp,
- "disallowed in document body"));
+ return(perr(m, ln, pp, EPROLBODY));
if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
SEC_PROLOGUE == m->lastnamed)
- return(mdoc_perr(m, ln, pp,
- "disallowed in prologue"));
+ return(perr(m, ln, pp, EBODYPROL));
if (1 != pp && ! (MDOC_CALLABLE & mdoc_macros[tok].flags))
- return(mdoc_perr(m, ln, pp, "%s not callable",
- mdoc_macronames[tok]));
+ return(perr(m, ln, pp, ENOCALL));
return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
}
static int
-mdoc_node_append(struct mdoc *mdoc, struct mdoc_node *p)
+perr(struct mdoc *m, int line, int pos, enum merr type)
+{
+ char *p;
+
+ p = NULL;
+ switch (type) {
+ case (ENOCALL):
+ p = "not callable";
+ break;
+ case (EPROLBODY):
+ p = "macro disallowed in document body";
+ break;
+ case (EBODYPROL):
+ p = "macro disallowed in document prologue";
+ break;
+ case (EMALLOC):
+ p = "memory exhausted";
+ break;
+ case (ETEXTPROL):
+ p = "text disallowed in document prologue";
+ break;
+ case (ENOBLANK):
+ p = "blank lines disallowed in non-literal contexts";
+ break;
+ case (ESPACE):
+ p = "whitespace disallowed after delimiter";
+ break;
+ }
+ assert(p);
+ return(mdoc_perr(m, line, pos, p));
+}
+
+
+static int
+node_append(struct mdoc *mdoc, struct mdoc_node *p)
{
assert(mdoc->last);
if ( ! mdoc_valid_pre(mdoc, p))
return(0);
+ if ( ! mdoc_action_pre(mdoc, p))
+ return(0);
switch (p->type) {
case (MDOC_HEAD):
}
mdoc->last = p;
+
+ switch (p->type) {
+ case (MDOC_TEXT):
+ if ( ! mdoc_valid_post(mdoc))
+ return(0);
+ if ( ! mdoc_action_post(mdoc))
+ return(0);
+ break;
+ default:
+ break;
+ }
+
return(1);
}
static struct mdoc_node *
-mdoc_node_alloc(const struct mdoc *mdoc)
+node_alloc(struct mdoc *mdoc, int line,
+ int pos, int tok, enum mdoc_type type)
{
struct mdoc_node *p;
- p = xcalloc(1, sizeof(struct mdoc_node));
+ if (NULL == (p = calloc(1, sizeof(struct mdoc_node)))) {
+ (void)verr(mdoc, EMALLOC);
+ return(NULL);
+ }
+
p->sec = mdoc->lastsec;
+ p->line = line;
+ p->pos = pos;
+ p->tok = tok;
+ if (MDOC_TEXT != (p->type = type))
+ assert(p->tok >= 0);
return(p);
}
{
struct mdoc_node *p;
- assert(mdoc->first);
- assert(mdoc->last);
-
- p = mdoc_node_alloc(mdoc);
-
- p->line = line;
- p->pos = pos;
- p->type = MDOC_TAIL;
- p->tok = tok;
-
- return(mdoc_node_append(mdoc, p));
+ p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL);
+ if (NULL == p)
+ return(0);
+ return(node_append(mdoc, p));
}
assert(mdoc->first);
assert(mdoc->last);
- p = mdoc_node_alloc(mdoc);
-
- p->line = line;
- p->pos = pos;
- p->type = MDOC_HEAD;
- p->tok = tok;
-
- return(mdoc_node_append(mdoc, p));
+ p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD);
+ if (NULL == p)
+ return(0);
+ return(node_append(mdoc, p));
}
{
struct mdoc_node *p;
- assert(mdoc->first);
- assert(mdoc->last);
-
- p = mdoc_node_alloc(mdoc);
-
- p->line = line;
- p->pos = pos;
- p->type = MDOC_BODY;
- p->tok = tok;
-
- return(mdoc_node_append(mdoc, p));
-}
-
-
-int
-mdoc_root_alloc(struct mdoc *mdoc)
-{
- struct mdoc_node *p;
-
- p = mdoc_node_alloc(mdoc);
-
- p->type = MDOC_ROOT;
-
- return(mdoc_node_append(mdoc, p));
+ p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
+ if (NULL == p)
+ return(0);
+ return(node_append(mdoc, p));
}
{
struct mdoc_node *p;
- p = mdoc_node_alloc(mdoc);
-
- p->pos = pos;
- p->line = line;
- p->type = MDOC_BLOCK;
- p->tok = tok;
- p->args = args;
-
- if (args)
+ p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK);
+ if (NULL == p)
+ return(0);
+ if ((p->args = args))
(args->refcnt)++;
-
- return(mdoc_node_append(mdoc, p));
+ return(node_append(mdoc, p));
}
{
struct mdoc_node *p;
- p = mdoc_node_alloc(mdoc);
-
- p->line = line;
- p->pos = pos;
- p->type = MDOC_ELEM;
- p->tok = tok;
- p->args = args;
-
- if (args)
+ p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM);
+ if (NULL == p)
+ return(0);
+ if ((p->args = args))
(args->refcnt)++;
-
- return(mdoc_node_append(mdoc, p));
+ return(node_append(mdoc, p));
}
{
struct mdoc_node *p;
- p = mdoc_node_alloc(mdoc);
-
- p->line = line;
- p->pos = pos;
- p->type = MDOC_TEXT;
- p->string = xstrdup(word);
-
- return(mdoc_node_append(mdoc, p));
+ p = node_alloc(mdoc, line, pos, -1, MDOC_TEXT);
+ if (NULL == p)
+ return(0);
+ if (NULL == (p->string = strdup(word))) {
+ (void)verr(mdoc, EMALLOC);
+ return(0);
+ }
+ return(node_append(mdoc, p));
}
* control character.
*/
static int
-parsetext(struct mdoc *mdoc, int line, char *buf)
+parsetext(struct mdoc *m, int line, char *buf)
{
- if (SEC_PROLOGUE == mdoc->lastnamed)
- return(mdoc_perr(mdoc, line, 0,
- "text disallowed in prologue"));
+ if (SEC_PROLOGUE == m->lastnamed)
+ return(perr(m, line, 0, ETEXTPROL));
+
+ if (0 == buf[0] && ! (MDOC_LITERAL & m->flags))
+ return(perr(m, line, 0, ENOBLANK));
- if ( ! mdoc_word_alloc(mdoc, line, 0, buf))
+ if ( ! mdoc_word_alloc(m, line, 0, buf))
return(0);
- mdoc->next = MDOC_NEXT_SIBLING;
+ m->next = MDOC_NEXT_SIBLING;
return(1);
}
macrowarn(struct mdoc *m, int ln, const char *buf)
{
if ( ! (MDOC_IGN_MACRO & m->pflags))
- return(mdoc_perr(m, ln, 1, "unknown macro: %s%s",
+ return(mdoc_perr(m, ln, 1,
+ "unknown macro: %s%s",
buf, strlen(buf) > 3 ? "..." : ""));
return(mdoc_pwarn(m, ln, 1, WARN_SYNTAX,
"unknown macro: %s%s",
i++;
if (0 == buf[i])
return(1);
- return(mdoc_perr(m, ln, 1, "invalid syntax"));
+ return(perr(m, ln, 1, ESPACE));
}
if (buf[1] && '\\' == buf[1])
return(1);
}
- if (MDOC_MAX == (c = mdoc_tokhash_find(m->htab, mac))) {
+ if (MDOC_MAX == (c = mdoc_hash_find(m->htab, mac))) {
if ( ! macrowarn(m, ln, mac))
goto err;
return(1);