-/* $Id: roff.c,v 1.15 2008/11/28 15:25:49 kristaps Exp $ */
+/* $Id: roff.c,v 1.21 2008/11/30 18:50:44 kristaps Exp $ */
/*
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
*
/* FIXME: warn about empty lists. */
-/* FIXME: ; : } ) (etc.) after text macros? */
+/* FIXME: roff_layout and roff_text have identical-ish lower bodies. */
/* FIXME: NAME section needs specific elements. */
#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 {
int, char *, char **);
static int roffargok(int, int);
static int roffnextopt(const struct rofftree *,
- int, const char ***, char **);
-static int roffparse(struct rofftree *, char *, size_t);
-static int textparse(const struct rofftree *,
- const char *, size_t);
+ 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_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_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 */
{roff_special, NULL, NULL, NULL, 0, ROFF_SPECIAL, 0 }, /* Db */
0, ROFF_VALUE, ROFF_VALUE, 0,
0, 0, 0, 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,
int
roff_free(struct rofftree *tree, int flush)
{
- int error;
+ int error, t;
struct roffnode *n;
error = 0;
- for (n = tree->last; n->parent; n = n->parent)
- if (tokens[n->tok].ctx == 0) {
- roff_warn(tree, NULL, "closing explicit scope "
- "of `%s'", toknames[n->tok]);
- error = 1;
- }
+ if ( ! flush)
+ goto end;
+
+ error = 1;
- if (0 == error && (ROFF_PRELUDE & tree->state)) {
+ if (ROFF_PRELUDE & tree->state) {
roff_warn(tree, NULL, "prelude never finished");
- error = 1;
+ 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 *tree;
+ assert(args);
+ assert(cb);
+
if (NULL == (tree = calloc(1, sizeof(struct rofftree))))
err(1, "calloc");
int
-roff_engine(struct rofftree *tree, char *buf, size_t sz)
+roff_engine(struct rofftree *tree, char *buf)
{
- tree->cur = NULL;
+ tree->cur = buf;
+ assert(buf);
- if (0 == sz) {
+ 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)
{
-
- /* Print text. */
- return(1);
+
+ return((*tree->cb.roffdata)(tree->arg, 1, buf));
}
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))) {
roff_err(tree, buf + 1, "bogus line macro");
if ( ! roffargs(tree, tok, buf, argv))
return(0);
- argvp = (const char **)argv;
+ argvp = (char **)argv;
/*
* Prelude macros break some assumptions, so branch now.
}
if ( ! roffscan(tok, tokens[tree->last->tok].children)) {
- roff_err(tree, *argvp, "`%s' is invalid child `%s'",
- toknames[tree->last->tok],
- toknames[tok]);
+ roff_err(tree, *argvp, "`%s' is invalid child of `%s'",
+ toknames[tok],
+ toknames[tree->last->tok]);
return(0);
}
}
+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)
{
static int
roffnextopt(const struct rofftree *tree, int tok,
- const char ***in, char **val)
+ char ***in, char **val)
{
- const char *arg, **argv;
+ char *arg, **argv;
int v;
*val = NULL;
{
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(NULL == tree->last);
- return(roff_layout(tok, tree, argv, type));
+ if (NULL == roffnode_new(tok, tree))
+ return(0);
+
+ return((*tree->cb.roffhead)(tree->arg));
}
if (NULL == roffnode_new(tok, tree))
return(0);
+ if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp, argvp))
+ return(0);
+
+ if (NULL == *argv)
+ return(1);
+
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, *argv++))
+ if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
return(0);
+ i = 1;
}
-
- if ( ! ((*tree->cb.roffout)(tree->arg, tok)))
- return(0);
- return((*tree->cb.roffblkin)(tree->arg, tok));
+ return((*tree->cb.roffout)(tree->arg, tok));
}
+ i = 0;
while (*argv) {
if (ROFF_MAX != (c = rofffindcallable(*argv))) {
if (NULL == tokens[c].cb) {
break;
}
- if ( ! (*tree->cb.roffdata)(tree->arg, *argv++))
+ assert(tree->arg);
+ if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
return(0);
+ i = 1;
}
- if ( ! ((*tree->cb.roffout)(tree->arg, tok)))
- return(0);
- return((*tree->cb.roffblkin)(tree->arg, tok));
+ /*
+ * 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 (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) {
return(0);
}
+ /* FIXME: breaks if passed from roff_layout. */
+ first = *argv == tree->cur;
+
i = 0;
argv++;
return(0);
if ( ! (ROFF_PARSED & tokens[tok].flags)) {
+ i = 0;
while (*argv) {
- if ( ! (*tree->cb.roffdata)(tree->arg, *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) {
- roff_err(tree, *argv, "unsupported "
- "macro `%s'",
- toknames[c]);
- 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, ROFF_ENTER))
+
+ if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))
return(0);
- break;
+
+ i = 1;
+ continue;
}
- if ( ! (*tree->cb.roffdata)(tree->arg, *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;
}
- return((*tree->cb.roffout)(tree->arg, tok));
+ 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(1);
}