-/* $Id: action.c,v 1.3 2009/01/07 15:53:00 kristaps Exp $ */
+/* $Id: action.c,v 1.19 2009/02/22 14:31:08 kristaps Exp $ */
/*
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
*
*/
#include <assert.h>
#include <stdlib.h>
+#include <time.h>
#include "private.h"
-typedef int (*a_act)(struct mdoc *, int, int);
-
+/*
+ * Actions are executed on macros after they've been post-validated: in
+ * other words, a macro will not be "acted upon" until all of its
+ * children have been filled in (post-fix order).
+ */
struct actions {
- a_act action;
+ int (*post)(struct mdoc *);
};
+/* Per-macro action routines. */
-static int action_sh(struct mdoc *, int, int);
+static int post_sh(struct mdoc *);
+static int post_os(struct mdoc *);
+static int post_dt(struct mdoc *);
+static int post_dd(struct mdoc *);
+static int post_nm(struct mdoc *);
+static int post_prologue(struct mdoc *);
+
+/* Array of macro action routines. */
const struct actions mdoc_actions[MDOC_MAX] = {
{ NULL }, /* \" */
- { NULL }, /* Dd */
- { NULL }, /* Dt */
- { NULL }, /* Os */
- { action_sh }, /* Sh */
+ { post_dd }, /* Dd */
+ { post_dt }, /* Dt */
+ { post_os }, /* Os */
+ { post_sh }, /* Sh */
{ NULL }, /* Ss */
{ NULL }, /* Pp */
{ NULL }, /* D1 */
{ NULL }, /* In */
{ NULL }, /* Li */
{ NULL }, /* Nd */
- { NULL }, /* Nm */
+ { post_nm }, /* Nm */
{ NULL }, /* Op */
{ NULL }, /* Ot */
{ NULL }, /* Pa */
};
+/*
+ * The `Nm' macro sets the document's name when used the first time with
+ * an argument. Subsequent calls without a value will result in the
+ * name value being used.
+ */
static int
-action_sh(struct mdoc *mdoc, int tok, int pos)
+post_nm(struct mdoc *mdoc)
{
- enum mdoc_sec sec;
- int i;
- struct mdoc_node *n;
- char *args[MDOC_LINEARG_MAX];
+ char buf[64];
+
+ assert(MDOC_ELEM == mdoc->last->type);
+ assert(MDOC_Nm == mdoc->last->tok);
+
+ if (mdoc->meta.name)
+ return(1);
+
+ if (xstrlcats(buf, mdoc->last->child, 64)) {
+ mdoc->meta.name = xstrdup(buf);
+ return(1);
+ }
+
+ return(mdoc_err(mdoc, "macro parameters too long"));
+}
+
+
+/*
+ * We keep track of the current section in order to provide warnings on
+ * section ordering, per-section macros, and so on.
+ */
+static int
+post_sh(struct mdoc *mdoc)
+{
+ enum mdoc_sec sec;
+ char buf[64];
if (MDOC_HEAD != mdoc->last->type)
return(1);
+ if (xstrlcats(buf, mdoc->last->child, 64)) {
+ if (SEC_CUSTOM != (sec = mdoc_atosec(buf)))
+ mdoc->lastnamed = sec;
+ mdoc->lastsec = sec;
+ return(1);
+ }
+
+ return(mdoc_err(mdoc, "macro parameters too long"));
+}
- n = mdoc->last->child;
- assert(n);
- for (i = 0; n && i < MDOC_LINEARG_MAX; n = n->next, i++) {
+/*
+ * Prologue title must be parsed into document meta-data.
+ */
+static int
+post_dt(struct mdoc *mdoc)
+{
+ int i;
+ char *p;
+ struct mdoc_node *n;
+
+ assert(MDOC_ELEM == mdoc->last->type);
+ assert(MDOC_Dt == mdoc->last->tok);
+
+ assert(NULL == mdoc->meta.title);
+
+ /* LINTED */
+ for (i = 0, n = mdoc->last->child; n; n = n->next, i++) {
assert(MDOC_TEXT == n->type);
- assert(NULL == n->child);
- assert(n->data.text.string);
- args[i] = n->data.text.string;
+ p = n->data.text.string;
+
+ switch (i) {
+ case (0):
+ mdoc->meta.title = xstrdup(p);
+ break;
+ case (1):
+ mdoc->meta.msec = mdoc_atomsec(p);
+ if (MSEC_DEFAULT != mdoc->meta.msec)
+ break;
+ return(mdoc_nerr(mdoc, n, "invalid parameter syntax"));
+ case (2):
+ mdoc->meta.vol = mdoc_atovol(p);
+ if (VOL_DEFAULT != mdoc->meta.vol)
+ break;
+ mdoc->meta.arch = mdoc_atoarch(p);
+ if (ARCH_DEFAULT != mdoc->meta.arch)
+ break;
+ return(mdoc_nerr(mdoc, n, "invalid parameter syntax"));
+ default:
+ return(mdoc_nerr(mdoc, n, "too many parameters"));
+ }
}
- sec = mdoc_atosec((size_t)i, (const char **)args);
- if (SEC_CUSTOM != sec)
- mdoc->sec_lastn = sec;
- mdoc->sec_last = sec;
+ if (NULL == mdoc->meta.title)
+ mdoc->meta.title = xstrdup("UNTITLED");
+
+ mdoc_msg(mdoc, "title: %s", mdoc->meta.title);
+
+ return(post_prologue(mdoc));
+}
+
+/*
+ * Prologue operating system must be parsed into document meta-data.
+ */
+static int
+post_os(struct mdoc *mdoc)
+{
+ char buf[64];
+
+ assert(MDOC_ELEM == mdoc->last->type);
+ assert(MDOC_Os == mdoc->last->tok);
+ assert(NULL == mdoc->meta.os);
+
+ if ( ! xstrlcats(buf, mdoc->last->child, 64))
+ return(mdoc_err(mdoc, "macro parameters too long"));
+
+ mdoc->meta.os = xstrdup(buf[0] ? buf : "local");
+ mdoc->lastnamed = SEC_BODY;
+
+ return(post_prologue(mdoc));
+}
+
+
+/*
+ * Prologue date must be parsed into document meta-data.
+ */
+static int
+post_dd(struct mdoc *mdoc)
+{
+ char buf[64];
+
+ assert(MDOC_ELEM == mdoc->last->type);
+ assert(MDOC_Dd == mdoc->last->tok);
+
+ assert(0 == mdoc->meta.date);
+
+ if ( ! xstrlcats(buf, mdoc->last->child, 64))
+ return(mdoc_err(mdoc, "macro parameters too long"));
+ if (0 == (mdoc->meta.date = mdoc_atotime(buf)))
+ return(mdoc_err(mdoc, "invalid parameter syntax"));
+
+ mdoc_msg(mdoc, "date: %u", mdoc->meta.date);
+
+ return(post_prologue(mdoc));
+}
+
+
+/*
+ * The end document shouldn't have the prologue macros as part of the
+ * syntax tree (they encompass only meta-data).
+ */
+static int
+post_prologue(struct mdoc *mdoc)
+{
+ struct mdoc_node *n;
+
+ if (mdoc->last->parent->child == mdoc->last)
+ mdoc->last->parent->child = mdoc->last->prev;
+ if (mdoc->last->prev)
+ mdoc->last->prev->next = NULL;
+
+ n = mdoc->last;
+ assert(NULL == mdoc->last->next);
+
+ if (mdoc->last->prev) {
+ mdoc->last = mdoc->last->prev;
+ mdoc->next = MDOC_NEXT_SIBLING;
+ } else {
+ mdoc->last = mdoc->last->parent;
+ mdoc->next = MDOC_NEXT_CHILD;
+ }
+
+ mdoc_node_freelist(n);
return(1);
}
int
-mdoc_action(struct mdoc *mdoc, int pos)
+mdoc_action_post(struct mdoc *mdoc)
{
- int t;
-
- switch (mdoc->last->type) {
- case (MDOC_BODY):
- t = mdoc->last->data.body.tok;
- break;
- case (MDOC_ELEM):
- t = mdoc->last->data.elem.tok;
- break;
- case (MDOC_BLOCK):
- t = mdoc->last->data.block.tok;
- break;
- case (MDOC_HEAD):
- t = mdoc->last->data.head.tok;
- break;
- default:
+
+ if (MDOC_ACTED & mdoc->last->flags)
return(1);
- }
+ mdoc->last->flags |= MDOC_ACTED;
- if (NULL == mdoc_actions[t].action)
+ if (MDOC_TEXT == mdoc->last->type)
+ return(1);
+ if (MDOC_ROOT == mdoc->last->type)
return(1);
- /* TODO: MDOC_Nm... ? */
- return((*mdoc_actions[t].action)(mdoc, t, pos));
+ if (NULL == mdoc_actions[mdoc->last->tok].post)
+ return(1);
+ return((*mdoc_actions[mdoc->last->tok].post)(mdoc));
}
-