-/* $Id: roff.c,v 1.10 2008/11/27 13:29:44 kristaps Exp $ */
+/* $Id: roff.c,v 1.21 2008/11/30 18:50:44 kristaps Exp $ */
/*
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
*
#include <assert.h>
#include <ctype.h>
#include <err.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* FIXME: warn about empty lists. */
-/* FIXME: ; : } ) (etc.) after text macros? */
+/* FIXME: roff_layout and roff_text have identical-ish lower bodies. */
-#define ROFF_MAXARG 10
+/* FIXME: NAME section needs specific elements. */
+
+#define ROFF_MAXARG 32
enum roffd {
ROFF_ENTER = 0,
enum rofftype {
ROFF_COMMENT,
ROFF_TEXT,
- ROFF_LAYOUT
+ ROFF_LAYOUT,
+ ROFF_SPECIAL
};
#define ROFFCALL_ARGS \
int tok, struct rofftree *tree, \
- const char *argv[], enum roffd type
+ char *argv[], enum roffd type
struct rofftree;
int flags;
#define ROFF_PARSED (1 << 0) /* "Parsed". */
#define ROFF_CALLABLE (1 << 1) /* "Callable". */
-#define ROFF_QUOTES (1 << 2) /* Quoted args. */
-#define ROFF_SHALLOW (1 << 3) /* Nesting block. */
+#define ROFF_SHALLOW (1 << 2) /* Nesting block. */
};
struct roffarg {
struct roffnode {
int tok; /* Token id. */
struct roffnode *parent; /* Parent (or NULL). */
- size_t line; /* Parsed at line. */
};
struct rofftree {
struct roffnode *last; /* Last parsed node. */
+ char *cur;
+
time_t date; /* `Dd' results. */
char os[64]; /* `Os' results. */
char title[64]; /* `Dt' results. */
char section[64]; /* `Dt' results. */
char volume[64]; /* `Dt' results. */
+
int state;
#define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
- /* FIXME: if we had prev ptrs, this wouldn't be necessary. */
#define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
#define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
#define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
#define ROFF_BODY (1 << 5) /* In roff body. */
- struct md_mbuf *mbuf; /* Output (or NULL). */
- const struct md_args *args; /* Global args. */
- const struct md_rbuf *rbuf; /* Input. */
- const struct roffcb *cb;
+
+ struct roffcb cb;
+ void *arg;
};
static int roff_Dd(ROFFCALL_ARGS);
static int roff_text(ROFFCALL_ARGS);
static int roff_comment(ROFFCALL_ARGS);
static int roff_close(ROFFCALL_ARGS);
+static int roff_special(ROFFCALL_ARGS);
static struct roffnode *roffnode_new(int, struct rofftree *);
-static void roffnode_free(int, struct rofftree *);
+static void roffnode_free(struct rofftree *);
+
+static void roff_warn(const struct rofftree *,
+ const char *, char *, ...);
+static void roff_err(const struct rofftree *,
+ const char *, char *, ...);
static int roffscan(int, const int *);
static int rofffindtok(const char *);
static int roffargs(const struct rofftree *,
int, char *, char **);
static int roffargok(int, int);
-static int roffnextopt(int, const char ***, char **);
-static int roffparse(struct rofftree *, char *, size_t);
-static int textparse(const struct rofftree *,
- const char *, size_t);
+static int roffnextopt(const struct rofftree *,
+ int, char ***, char **);
+static int roffparse(struct rofftree *, char *);
+static int textparse(const struct rofftree *, char *);
static const int roffarg_An[] = { ROFF_Split, ROFF_Nosplit,
ROFF_ARGMAX };
static const int roffarg_Bd[] = { ROFF_Ragged, ROFF_Unfilled,
- ROFF_Literal, ROFF_File, ROFF_Offset, ROFF_ARGMAX };
+ ROFF_Literal, ROFF_File, ROFF_Offset, ROFF_Filled,
+ ROFF_Compact, ROFF_ARGMAX };
+static const int roffarg_Bk[] = { ROFF_Words, ROFF_ARGMAX };
static const int roffarg_Ex[] = { ROFF_Std, ROFF_ARGMAX };
static const int roffarg_Rv[] = { ROFF_Std, ROFF_ARGMAX };
static const int roffarg_Bl[] = { ROFF_Bullet, ROFF_Dash,
ROFF_Hyphen, ROFF_Item, ROFF_Enum, ROFF_Tag, ROFF_Diag,
ROFF_Hang, ROFF_Ohang, ROFF_Inset, ROFF_Column, ROFF_Offset,
- ROFF_ARGMAX };
+ ROFF_Width, ROFF_Compact, ROFF_ARGMAX };
static const int roffarg_St[] = {
ROFF_p1003_1_88, ROFF_p1003_1_90, ROFF_p1003_1_96,
ROFF_p1003_1_2001, ROFF_p1003_1_2004, ROFF_p1003_1,
{roff_comment, NULL, NULL, NULL, 0, ROFF_COMMENT, 0 }, /* \" */
{ roff_Dd, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dd */
{ roff_Dt, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dt */
- { roff_Os, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_QUOTES }, /* Os */
- { roff_layout, NULL, NULL, NULL, ROFF_Sh, ROFF_LAYOUT, ROFF_PARSED }, /* Sh */
- { roff_layout, NULL, NULL, NULL, ROFF_Ss, ROFF_LAYOUT, ROFF_PARSED }, /* Ss */
+ { roff_Os, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Os */
+ { roff_layout, NULL, NULL, NULL, ROFF_Sh, ROFF_LAYOUT, 0 }, /* Sh */
+ { roff_layout, NULL, NULL, NULL, ROFF_Ss, ROFF_LAYOUT, 0 }, /* Ss */
{ roff_text, NULL, NULL, NULL, ROFF_Pp, ROFF_TEXT, 0 }, /* Pp */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* D1 */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Dl */
- { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bd */
+ { roff_layout, roffarg_Bd, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bd */
{ roff_close, NULL, NULL, NULL, ROFF_Bd, ROFF_LAYOUT, 0 }, /* Ed */
{ roff_layout, roffarg_Bl, NULL, roffchild_Bl, 0, ROFF_LAYOUT, 0 }, /* Bl */
{ roff_close, NULL, roffparent_El, NULL, ROFF_Bl, ROFF_LAYOUT, 0 }, /* El */
- { roff_layout, NULL, roffparent_It, NULL, ROFF_It, ROFF_LAYOUT, ROFF_SHALLOW }, /* It */
- { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ad */
+ { roff_layout, NULL, roffparent_It, NULL, ROFF_It, ROFF_LAYOUT, ROFF_PARSED | ROFF_SHALLOW }, /* It */
+ { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ad */ /* FIXME */
{ roff_text, roffarg_An, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* An */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ar */
- { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_QUOTES }, /* Cd */ /* XXX man.4 only */
+ { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Cd */ /* XXX man.4 only */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Cm */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dv */ /* XXX needs arg */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Er */ /* XXX needs arg */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ic */ /* XXX needs arg */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* In */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Li */
- { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_QUOTES }, /* Nd */
+ { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Nd */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Nm */ /* FIXME */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Op */
{ NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ot */ /* XXX deprecated */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bc */
{ NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Bf */ /* FIXME */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bo */
- { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bq */
+ { roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bq */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bsx */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bx */
- { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Db */ /* XXX */
+ {roff_special, NULL, NULL, NULL, 0, ROFF_SPECIAL, 0 }, /* Db */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dc */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Do */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dq */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sc */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* So */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sq */
- { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Sm */
+ {roff_special, NULL, NULL, NULL, 0, ROFF_SPECIAL, 0 }, /* Sm */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sx */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sy */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Tn */
{ roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ux */
{ NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xc */
{ NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xo */
- { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Fo */
+ { roff_layout, NULL, NULL, roffchild_Fo, 0, ROFF_LAYOUT, 0 }, /* Fo */
{ roff_close, NULL, roffparent_Fc, NULL, ROFF_Fo, ROFF_LAYOUT, 0 }, /* Fc */
- { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Oo */
+ { roff_layout, NULL, NULL, roffchild_Oo, 0, ROFF_LAYOUT, 0 }, /* Oo */
{ roff_close, NULL, roffparent_Oc, NULL, ROFF_Oo, ROFF_LAYOUT, 0 }, /* Oc */
- { roff_layout, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bk */
+ { roff_layout, roffarg_Bk, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bk */
{ roff_close, NULL, NULL, NULL, ROFF_Bk, ROFF_LAYOUT, 0 }, /* Ek */
-};
+ };
/* Table of all known token arguments. */
-static const struct roffarg tokenargs[ROFF_ARGMAX] = {
- { 0 }, /* split */
- { 0 }, /* nosplit */
- { 0 }, /* ragged */
- { 0 }, /* unfilled */
- { 0 }, /* literal */
- { ROFF_VALUE }, /* file */
- { ROFF_VALUE }, /* offset */
- { 0 }, /* bullet */
- { 0 }, /* dash */
- { 0 }, /* hyphen */
- { 0 }, /* item */
- { 0 }, /* enum */
- { 0 }, /* tag */
- { 0 }, /* diag */
- { 0 }, /* hang */
- { 0 }, /* ohang */
- { 0 }, /* inset */
- { 0 }, /* column */
- { 0 }, /* width */
- { 0 }, /* compact */
- { 0 }, /* std */
- { 0 }, /* p1003_1_88 */
- { 0 }, /* p1003_1_90 */
- { 0 }, /* p1003_1_96 */
- { 0 }, /* p1003_1_2001 */
- { 0 }, /* p1003_1_2004 */
- { 0 }, /* p1003_1 */
- { 0 }, /* p1003_1b */
- { 0 }, /* p1003_1b_93 */
- { 0 }, /* p1003_1c_95 */
- { 0 }, /* p1003_1g_2000 */
- { 0 }, /* p1003_2_92 */
- { 0 }, /* p1387_2_95 */
- { 0 }, /* p1003_2 */
- { 0 }, /* p1387_2 */
- { 0 }, /* isoC_90 */
- { 0 }, /* isoC_amd1 */
- { 0 }, /* isoC_tcor1 */
- { 0 }, /* isoC_tcor2 */
- { 0 }, /* isoC_99 */
- { 0 }, /* ansiC */
- { 0 }, /* ansiC_89 */
- { 0 }, /* ansiC_99 */
- { 0 }, /* ieee754 */
- { 0 }, /* iso8802_3 */
- { 0 }, /* xpg3 */
- { 0 }, /* xpg4 */
- { 0 }, /* xpg4_2 */
- { 0 }, /* xpg4_3 */
- { 0 }, /* xbd5 */
- { 0 }, /* xcu5 */
- { 0 }, /* xsh5 */
- { 0 }, /* xns5 */
- { 0 }, /* xns5_2d2_0 */
- { 0 }, /* xcurses4_2 */
- { 0 }, /* susv2 */
- { 0 }, /* susv3 */
- { 0 }, /* svid4 */
-};
+static const int tokenargs[ROFF_ARGMAX] = {
+ 0, 0, 0, 0,
+ 0, ROFF_VALUE, ROFF_VALUE, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, ROFF_VALUE, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ };
-const char *const toknamesp[ROFF_MAX] =
- {
+const char *const toknamesp[ROFF_MAX] = {
"\\\"", "Dd", "Dt", "Os",
"Sh", "Ss", "Pp", "D1",
"Dl", "Bd", "Ed", "Bl",
"Bk", "Ek",
};
-const char *const tokargnamesp[ROFF_ARGMAX] =
- {
+const char *const tokargnamesp[ROFF_ARGMAX] = {
"split", "nosplit", "ragged",
"unfilled", "literal", "file",
"offset", "bullet", "dash",
"xpg4.3", "xbd5", "xcu5",
"xsh5", "xns5", "xns5.2d2.0",
"xcurses4.2", "susv2", "susv3",
- "svid4"
+ "svid4", "filled", "words",
};
const char *const *toknames = toknamesp;
int
roff_free(struct rofftree *tree, int flush)
{
- int error;
+ int error, t;
+ struct roffnode *n;
+
+ error = 0;
- assert(tree->mbuf);
if ( ! flush)
- tree->mbuf = NULL;
+ goto end;
- /* LINTED */
- while (tree->last)
- if ( ! (*tokens[tree->last->tok].cb)
- (tree->last->tok, tree, NULL, ROFF_EXIT))
- /* Disallow flushing. */
- tree->mbuf = NULL;
-
- error = tree->mbuf ? 0 : 1;
-
- if (tree->mbuf && (ROFF_PRELUDE & tree->state)) {
- warnx("%s: prelude never finished",
- tree->rbuf->name);
- error = 1;
+ error = 1;
+
+ if (ROFF_PRELUDE & tree->state) {
+ roff_warn(tree, NULL, "prelude never finished");
+ goto end;
+ }
+
+ for (n = tree->last; n->parent; n = n->parent) {
+ if (0 != tokens[n->tok].ctx)
+ continue;
+ roff_warn(tree, NULL, "closing explicit scope `%s'",
+ toknames[n->tok]);
+ goto end;
}
+ while (tree->last) {
+ t = tree->last->tok;
+ if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
+ goto end;
+ }
+
+ error = 0;
+
+end:
+
+ while (tree->last)
+ roffnode_free(tree);
+
free(tree);
+
return(error ? 0 : 1);
}
struct rofftree *
-roff_alloc(const struct md_args *args, struct md_mbuf *out,
- const struct md_rbuf *in, const struct roffcb *cb)
+roff_alloc(const struct roffcb *cb, void *args)
{
struct rofftree *tree;
- if (NULL == (tree = calloc(1, sizeof(struct rofftree)))) {
- warn("malloc");
- return(NULL);
- }
+ assert(args);
+ assert(cb);
+
+ if (NULL == (tree = calloc(1, sizeof(struct rofftree))))
+ err(1, "calloc");
tree->state = ROFF_PRELUDE;
- tree->args = args;
- tree->mbuf = out;
- tree->rbuf = in;
- tree->cb = cb;
+ tree->arg = args;
+
+ (void)memcpy(&tree->cb, cb, sizeof(struct roffcb));
return(tree);
}
int
-roff_engine(struct rofftree *tree, char *buf, size_t sz)
+roff_engine(struct rofftree *tree, char *buf)
{
- if (0 == sz) {
- warnx("%s: blank line (line %zu)",
- tree->rbuf->name,
- tree->rbuf->line);
+ tree->cur = buf;
+ assert(buf);
+
+ if (0 == *buf) {
+ roff_warn(tree, buf, "blank line");
return(0);
} else if ('.' != *buf)
- return(textparse(tree, buf, sz));
+ return(textparse(tree, buf));
- return(roffparse(tree, buf, sz));
+ return(roffparse(tree, buf));
}
static int
-textparse(const struct rofftree *tree, const char *buf, size_t sz)
+textparse(const struct rofftree *tree, char *buf)
{
-
- if (NULL == tree->last) {
- warnx("%s: unexpected text (line %zu)",
- tree->rbuf->name,
- tree->rbuf->line);
- return(0);
- } else if (NULL == tree->last->parent) {
- warnx("%s: disallowed text (line %zu)",
- tree->rbuf->name,
- tree->rbuf->line);
- return(0);
- }
-
- /* Print text. */
- return(1);
+ return((*tree->cb.roffdata)(tree->arg, 1, buf));
}
int tok, char *buf, char **argv)
{
int i;
+ char *p;
assert(tok >= 0 && tok < ROFF_MAX);
assert('.' == *buf);
+ p = buf;
+
/* LINTED */
for (i = 0; *buf && i < ROFF_MAXARG; i++) {
if ('\"' == *buf) {
while (*buf && '\"' != *buf)
buf++;
if (0 == *buf) {
- warnx("%s: unclosed quoted arg for "
- "`%s' (line %zu)",
- tree->rbuf->name,
- toknames[tok],
- tree->rbuf->line);
+ roff_err(tree, argv[i], "unclosed "
+ "quote in argument "
+ "list for `%s'",
+ toknames[tok]);
return(0);
}
} else {
assert(i > 0);
if (ROFF_MAXARG == i && *buf) {
- warnx("%s: too many args for `%s' (line %zu)",
- tree->rbuf->name, toknames[tok],
- tree->rbuf->line);
+ roff_err(tree, p, "too many arguments for `%s'", toknames
+ [tok]);
return(0);
}
-#ifdef DEBUG
- (void)printf("argparse: %d arguments for `%s'\n",
- i, toknames[tok]);
-#endif
-
argv[i] = NULL;
return(1);
}
static int
-roffparse(struct rofftree *tree, char *buf, size_t sz)
+roffparse(struct rofftree *tree, char *buf)
{
int tok, t;
struct roffnode *n;
char *argv[ROFF_MAXARG];
- const char **argvp;
-
- assert(sz > 0);
+ char **argvp;
if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {
- warnx("%s: unknown line macro (line %zu)",
- tree->rbuf->name, tree->rbuf->line);
+ roff_err(tree, buf + 1, "bogus line macro");
return(0);
} else if (NULL == tokens[tok].cb) {
- warnx("%s: macro `%s' not supported (line %zu)",
- tree->rbuf->name, toknames[tok],
- tree->rbuf->line);
+ roff_err(tree, buf + 1, "unsupported macro `%s'",
+ toknames[tok]);
return(0);
} else if (ROFF_COMMENT == tokens[tok].type)
return(1);
if ( ! roffargs(tree, tok, buf, argv))
return(0);
- else
- argvp = (const char **)argv + 1;
+
+ argvp = (char **)argv;
/*
- * Prelude macros break some assumptions: branch now.
+ * Prelude macros break some assumptions, so branch now.
*/
if (ROFF_PRELUDE & tree->state) {
*/
if ( ! roffscan(tree->last->tok, tokens[tok].parents)) {
- warnx("%s: invalid parent `%s' for `%s' (line %zu)",
- tree->rbuf->name,
- toknames[tree->last->tok],
- toknames[tok], tree->rbuf->line);
+ roff_err(tree, *argvp, "`%s' has invalid parent `%s'",
+ toknames[tok],
+ toknames[tree->last->tok]);
return(0);
}
if ( ! roffscan(tok, tokens[tree->last->tok].children)) {
- warnx("%s: invalid child `%s' for `%s' (line %zu)",
- tree->rbuf->name, toknames[tok],
- toknames[tree->last->tok],
- tree->rbuf->line);
+ roff_err(tree, *argvp, "`%s' is invalid child of `%s'",
+ toknames[tok],
+ toknames[tree->last->tok]);
return(0);
}
if (0 == tokens[tok].ctx)
return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
+ /*
+ * First consider implicit-end tags, like as follows:
+ * .Sh SECTION 1
+ * .Sh SECTION 2
+ * In this, we want to close the scope of the NAME section. If
+ * there's an intermediary implicit-end tag, such as
+ * .Sh SECTION 1
+ * .Ss Subsection 1
+ * .Sh SECTION 2
+ * then it must be closed as well.
+ */
+
if (tok == tokens[tok].ctx) {
+ /*
+ * First search up to the point where we must close.
+ * If one doesn't exist, then we can open a new scope.
+ */
+
for (n = tree->last; n; n = n->parent) {
assert(0 == tokens[n->tok].ctx ||
n->tok == tokens[n->tok].ctx);
* Create a new scope, as no previous one exists to
* close out.
*/
- if (NULL == n) {
-#ifdef DEBUG
- (void)printf("scope: new `%s'\n",
- toknames[tok]);
-#endif
+
+ if (NULL == n)
return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
- }
/*
- * Close out all intermediary scoped blocks.
+ * Close out all intermediary scoped blocks, then hang
+ * the current scope from our predecessor's parent.
*/
do {
t = tree->last->tok;
-#ifdef DEBUG
- (void)printf("scope: closing `%s'\n",
- toknames[t]);
-#endif
if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
return(0);
} while (t != tok);
-#ifdef DEBUG
- (void)printf("scope: new parent of `%s' is `%s'\n",
- toknames[tok],
- toknames[tree->last->tok]);
-#endif
-
return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
}
+ /*
+ * Now consider explicit-end tags, where we want to close back
+ * to a specific tag. Example:
+ * .Bl
+ * .It Item.
+ * .El
+ * In this, the `El' tag closes out the scope of `Bl'.
+ */
+
assert(tree->last);
assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);
+ /* LINTED */
do {
t = tree->last->tok;
-#ifdef DEBUG
- (void)printf("scope: closing `%s'\n", toknames[t]);
-#endif
if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
return(0);
} while (t != tokens[tok].ctx);
/* LINTED */
for (i = 0; i < ROFF_MAX; i++)
/* LINTED */
- if (0 == strcmp(toknames[i], token)) {
-#ifdef DEBUG
- (void)printf("lookup (good): `%s' (%d)\n",
- token, (int)i);
-#endif
+ if (0 == strcmp(toknames[i], token))
return((int)i);
- }
-#ifdef DEBUG
- (void)printf("lookup (bad): `%s'\n", token);
-#endif
-
return(ROFF_MAX);
}
+static int
+roffispunct(const char *p)
+{
+
+ if (0 == *p)
+ return(0);
+ if (0 != *(p + 1))
+ return(0);
+
+ switch (*p) {
+ case('{'):
+ /* FALLTHROUGH */
+ case('.'):
+ /* FALLTHROUGH */
+ case(','):
+ /* FALLTHROUGH */
+ case(';'):
+ /* FALLTHROUGH */
+ case(':'):
+ /* FALLTHROUGH */
+ case('?'):
+ /* FALLTHROUGH */
+ case('!'):
+ /* FALLTHROUGH */
+ case('('):
+ /* FALLTHROUGH */
+ case(')'):
+ /* FALLTHROUGH */
+ case('['):
+ /* FALLTHROUGH */
+ case(']'):
+ /* FALLTHROUGH */
+ case('}'):
+ return(1);
+ default:
+ break;
+ }
+
+ return(0);
+}
+
+
static int
rofffindcallable(const char *name)
{
{
struct roffnode *p;
- if (NULL == (p = malloc(sizeof(struct roffnode)))) {
- warn("malloc");
- return(NULL);
- }
+ if (NULL == (p = malloc(sizeof(struct roffnode))))
+ err(1, "malloc");
- p->line = tree->rbuf->line;
p->tok = tokid;
p->parent = tree->last;
tree->last = p;
-#ifdef DEBUG
- (void)printf("scope: new `%s' child of `%s'\n",
- toknames[tree->last->tok],
- tree->last->parent ?
- toknames[tree->last->parent->tok] :
- "<root>");
-#endif
-
return(p);
}
static void
-roffnode_free(int tokid, struct rofftree *tree)
+roffnode_free(struct rofftree *tree)
{
struct roffnode *p;
assert(tree->last);
- assert(tree->last->tok == tokid);
-
-#ifdef DEBUG
- (void)printf("scope: closing `%s' back to `%s'\n",
- toknames[tree->last->tok],
- tree->last->parent ?
- toknames[tree->last->parent->tok] :
- "<root>");
-#endif
p = tree->last;
tree->last = tree->last->parent;
static int
-roffnextopt(int tok, const char ***in, char **val)
+roffnextopt(const struct rofftree *tree, int tok,
+ char ***in, char **val)
{
- const char *arg, **argv;
+ char *arg, **argv;
int v;
*val = NULL;
if ('-' != *arg)
return(-1);
- /* FIXME: should we let this slide... ? */
-
- if (ROFF_ARGMAX == (v = rofffindarg(&arg[1])))
+ if (ROFF_ARGMAX == (v = rofffindarg(arg + 1))) {
+ roff_warn(tree, arg, "argument-like parameter `%s' to "
+ "`%s'", &arg[1], toknames[tok]);
return(-1);
-
- /* FIXME: should we let this slide... ? */
-
- if ( ! roffargok(tok, v))
+ }
+
+ if ( ! roffargok(tok, v)) {
+ roff_warn(tree, arg, "invalid argument parameter `%s' to "
+ "`%s'", tokargnames[v], toknames[tok]);
return(-1);
- if ( ! (ROFF_VALUE & tokenargs[v].flags))
+ }
+
+ if ( ! (ROFF_VALUE & tokenargs[v]))
return(v);
*in = ++argv;
- /* FIXME: what if this looks like a roff token or argument? */
+ if (NULL == *argv) {
+ roff_err(tree, arg, "empty value of `%s' for `%s'",
+ tokargnames[v], toknames[tok]);
+ return(ROFF_ARGMAX);
+ }
- return(*argv ? v : ROFF_ARGMAX);
+ return(v);
}
assert( ! (ROFF_BODY & tree->state));
if (ROFF_PRELUDE_Dd & tree->state) {
- warnx("%s: prelude `Dd' repeated (line %zu)",
- tree->rbuf->name, tree->rbuf->line);
+ roff_err(tree, *argv, "repeated `Dd' in prelude");
return(0);
} else if (ROFF_PRELUDE_Dt & tree->state) {
- warnx("%s: prelude `Dd' out-of-order (line %zu)",
- tree->rbuf->name, tree->rbuf->line);
+ roff_err(tree, *argv, "out-of-order `Dd' in prelude");
return(0);
}
assert( ! (ROFF_BODY & tree->state));
if ( ! (ROFF_PRELUDE_Dd & tree->state)) {
- warnx("%s: prelude `Dt' out-of-order (line %zu)",
- tree->rbuf->name, tree->rbuf->line);
+ roff_err(tree, *argv, "out-of-order `Dt' in prelude");
return(0);
} else if (ROFF_PRELUDE_Dt & tree->state) {
- warnx("%s: prelude `Dt' repeated (line %zu)",
- tree->rbuf->name, tree->rbuf->line);
+ roff_err(tree, *argv, "repeated `Dt' in prelude");
return(0);
}
{
if (ROFF_EXIT == type) {
- assert(ROFF_PRELUDE_Os & tree->state);
- return(roff_layout(tok, tree, argv, type));
+ roffnode_free(tree);
+ return((*tree->cb.rofftail)(tree->arg));
} else if (ROFF_BODY & tree->state) {
assert( ! (ROFF_PRELUDE & tree->state));
assert(ROFF_PRELUDE_Os & tree->state);
assert(ROFF_PRELUDE & tree->state);
if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
! (ROFF_PRELUDE_Dd & tree->state)) {
- warnx("%s: prelude `Os' out-of-order (line %zu)",
- tree->rbuf->name, tree->rbuf->line);
+ roff_err(tree, *argv, "out-of-order `Os' in prelude");
return(0);
}
assert(NULL == tree->last);
- return(roff_layout(tok, tree, argv, type));
+ if (NULL == roffnode_new(tok, tree))
+ return(0);
+
+ return((*tree->cb.roffhead)(tree->arg));
}
char *v, *argvp[ROFF_MAXARG];
if (ROFF_PRELUDE & tree->state) {
- warnx("%s: macro `%s' called in prelude (line %zu)",
- tree->rbuf->name,
- toknames[tok],
- tree->rbuf->line);
+ roff_err(tree, *argv, "`%s' disallowed in prelude",
+ toknames[tok]);
return(0);
}
if (ROFF_EXIT == type) {
- roffnode_free(tok, tree);
- return((*tree->cb->roffblkout)(tok));
+ roffnode_free(tree);
+ return((*tree->cb.roffblkout)(tree->arg, tok));
}
i = 0;
+ argv++;
- while (-1 != (c = roffnextopt(tok, &argv, &v))) {
- if (ROFF_ARGMAX == c) {
- warnx("%s: error parsing `%s' args (line %zu)",
- tree->rbuf->name,
- toknames[tok],
- tree->rbuf->line);
- return(0);
- } else if ( ! roffargok(tok, c)) {
- warnx("%s: arg `%s' not for `%s' (line %zu)",
- tree->rbuf->name,
- tokargnames[c],
- toknames[tok],
- tree->rbuf->line);
+ while (-1 != (c = roffnextopt(tree, tok, &argv, &v))) {
+ if (ROFF_ARGMAX == c)
return(0);
- }
+
argcp[i] = c;
argvp[i] = v;
i++;
if (NULL == roffnode_new(tok, tree))
return(0);
- if ( ! (*tree->cb->roffin)(tok, argcp, argvp))
+ if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp, argvp))
return(0);
- if ( ! (ROFF_PARSED & tokens[tok].flags)) {
- /* TODO: print all tokens. */
+ if (NULL == *argv)
+ return(1);
- if ( ! ((*tree->cb->roffout)(tok)))
- return(0);
- return((*tree->cb->roffblkin)(tok));
+ if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))
+ return(0);
+
+ if ( ! (ROFF_PARSED & tokens[tok].flags)) {
+ i = 0;
+ while (*argv) {
+ if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
+ return(0);
+ i = 1;
+ }
+ return((*tree->cb.roffout)(tree->arg, tok));
}
+ i = 0;
while (*argv) {
if (ROFF_MAX != (c = rofffindcallable(*argv))) {
if (NULL == tokens[c].cb) {
- warnx("%s: macro `%s' not supported "
- "(line %zu)",
- tree->rbuf->name,
- toknames[c],
- tree->rbuf->line);
+ roff_err(tree, *argv, "unsupported "
+ "macro `%s'",
+ toknames[c]);
return(0);
}
- if ( ! (*tokens[c].cb)(c, tree,
- argv + 1, ROFF_ENTER))
+ if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))
return(0);
+ break;
}
- /* TODO: print token. */
- argv++;
+
+ assert(tree->arg);
+ if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
+ return(0);
+ i = 1;
}
- if ( ! ((*tree->cb->roffout)(tok)))
- return(0);
+ /*
+ * If we're the first parser (*argv == tree->cur) then purge out
+ * any additional punctuation, should there be any remaining at
+ * the end of line.
+ */
- return((*tree->cb->roffblkin)(tok));
+ if (ROFF_PARSED & tokens[tok].flags && *argv) {
+ i = 0;
+ while (argv[i])
+ i++;
+
+ assert(i > 0);
+ if ( ! roffispunct(argv[--i]))
+ return(1);
+
+ while (i >= 0 && roffispunct(argv[i]))
+ i--;
+
+ assert(0 != i);
+ i++;
+
+ while (argv[i])
+ if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))
+ return(0);
+ }
+
+ return((*tree->cb.roffout)(tree->arg, tok));
}
static int
roff_text(ROFFCALL_ARGS)
{
- int i, c, argcp[ROFF_MAXARG];
+ int i, j, first, c, argcp[ROFF_MAXARG];
char *v, *argvp[ROFF_MAXARG];
if (ROFF_PRELUDE & tree->state) {
- warnx("%s: macro `%s' called in prelude (line %zu)",
- tree->rbuf->name,
- toknames[tok],
- tree->rbuf->line);
+ roff_err(tree, *argv, "`%s' disallowed in prelude",
+ toknames[tok]);
return(0);
}
+ /* FIXME: breaks if passed from roff_layout. */
+ first = *argv == tree->cur;
+
i = 0;
+ argv++;
- while (-1 != (c = roffnextopt(tok, &argv, &v))) {
- if (ROFF_ARGMAX == c) {
- warnx("%s: error parsing `%s' args (line %zu)",
- tree->rbuf->name,
- toknames[tok],
- tree->rbuf->line);
+ while (-1 != (c = roffnextopt(tree, tok, &argv, &v))) {
+ if (ROFF_ARGMAX == c)
return(0);
- }
+
argcp[i] = c;
argvp[i] = v;
i++;
argcp[i] = ROFF_ARGMAX;
argvp[i] = NULL;
- if ( ! (*tree->cb->roffin)(tok, argcp, argvp))
+ if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))
return(0);
if ( ! (ROFF_PARSED & tokens[tok].flags)) {
- /* TODO: print all tokens. */
- return((*tree->cb->roffout)(tok));
+ i = 0;
+ while (*argv) {
+ if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
+ return(0);
+ i = 1;
+ }
+ return((*tree->cb.roffout)(tree->arg, tok));
}
+ i = 0;
while (*argv) {
- if (ROFF_MAX != (c = rofffindcallable(*argv))) {
- if (NULL == tokens[c].cb) {
- warnx("%s: macro `%s' not supported "
- "(line %zu)",
- tree->rbuf->name,
- toknames[c],
- tree->rbuf->line);
- return(0);
+ if (ROFF_MAX == (c = rofffindcallable(*argv))) {
+ /*
+ * If all that remains is roff punctuation, then
+ * close out our scope and return.
+ */
+ if (roffispunct(*argv)) {
+ for (j = 0; argv[j]; j++)
+ if ( ! roffispunct(argv[j]))
+ break;
+ if (NULL == argv[j])
+ break;
+ i = 1;
}
- if ( ! (*tokens[c].cb)(c, tree,
- argv + 1, ROFF_ENTER))
+
+ if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
return(0);
+
+ i = 1;
+ continue;
}
- /* TODO: print token. */
- argv++;
+
+ /*
+ * A sub-command has been found. Execute it and
+ * discontinue parsing for arguments.
+ */
+
+ if (NULL == tokens[c].cb) {
+ roff_err(tree, *argv, "unsupported macro `%s'",
+ toknames[c]);
+ return(0);
+ }
+
+ if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))
+ return(0);
+
+ break;
+ }
+
+ if ( ! (*tree->cb.roffout)(tree->arg, tok))
+ return(0);
+
+ /*
+ * If we're the first parser (*argv == tree->cur) then purge out
+ * any additional punctuation, should there be any remaining at
+ * the end of line.
+ */
+
+ if (first && *argv) {
+ i = 0;
+ while (argv[i])
+ i++;
+
+ assert(i > 0);
+ if ( ! roffispunct(argv[--i]))
+ return(1);
+
+ while (i >= 0 && roffispunct(argv[i]))
+ i--;
+
+ assert(0 != i);
+ i++;
+
+ while (argv[i])
+ if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))
+ return(0);
}
- return((*tree->cb->roffout)(tok));
+ return(1);
}
return(1);
}
+
+
+/* ARGSUSED */
+static int
+roff_special(ROFFCALL_ARGS)
+{
+
+ return((*tree->cb.roffspecial)(tree->arg, tok));
+}
+
+
+static void
+roff_warn(const struct rofftree *tree, const char *pos, char *fmt, ...)
+{
+ va_list ap;
+ char buf[128];
+
+ va_start(ap, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ (*tree->cb.roffmsg)(tree->arg,
+ ROFF_WARN, tree->cur, pos, buf);
+}
+
+
+static void
+roff_err(const struct rofftree *tree, const char *pos, char *fmt, ...)
+{
+ va_list ap;
+ char buf[128];
+
+ va_start(ap, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ (*tree->cb.roffmsg)(tree->arg,
+ ROFF_ERROR, tree->cur, pos, buf);
+}