-static struct roffnode *roffnode_new(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 roffpurgepunct(struct rofftree *, char **);
-static int roffscan(int, const int *);
-static int rofffindtok(const char *);
-static int rofffindarg(const char *);
-static int rofffindcallable(const char *);
-static int roffargs(const struct rofftree *,
- int, char *, char **);
-static int roffargok(int, int);
-static int roffnextopt(const struct rofftree *,
- int, char ***, char **);
-static int roffparseopts(struct rofftree *, int,
- char ***, int *, char **);
-static int roffcall(struct rofftree *, int, char **);
-static int roffparse(struct rofftree *, char *);
-static int textparse(struct rofftree *, char *);
-static int roffdata(struct rofftree *, int, char *);
-static int roffspecial(struct rofftree *, int,
- const char *, const int *,
- const char **, size_t, char **);
-static int roffsetname(struct rofftree *, char **);
-
-#ifdef __linux__
-extern size_t strlcat(char *, const char *, size_t);
-extern size_t strlcpy(char *, const char *, size_t);
-extern int vsnprintf(char *, size_t,
- const char *, va_list);
-extern char *strptime(const char *, const char *,
- struct tm *);
-#endif
-
-int
-roff_free(struct rofftree *tree, int flush)
-{
- int error, t;
- struct roffnode *n;
-
- error = 0;
-
- if ( ! flush)
- goto end;
-
- error = 1;
-
- if (ROFF_PRELUDE & tree->state) {
- roff_err(tree, NULL, "prelude never finished");
- goto end;
- }
-
- for (n = tree->last; n; n = n->parent) {
- if (0 != tokens[n->tok].ctx)
- continue;
- roff_err(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;
- }
-
- if ( ! (*tree->cb.rofftail)(tree->arg))
- goto end;
-
- error = 0;
-
-end:
-
- while (tree->last)
- roffnode_free(tree);
-
- free(tree);
-
- return(error ? 0 : 1);
-}
-
-
-struct rofftree *
-roff_alloc(const struct roffcb *cb, void *args)
-{
- struct rofftree *tree;
-
- assert(args);
- assert(cb);
-
- if (NULL == (tree = calloc(1, sizeof(struct rofftree))))
- err(1, "calloc");
-
- tree->state = ROFF_PRELUDE;
- tree->arg = args;
-
- (void)memcpy(&tree->cb, cb, sizeof(struct roffcb));
-
- return(tree);
-}
-
-
-int
-roff_engine(struct rofftree *tree, char *buf)
-{
-
- tree->cur = buf;
- assert(buf);
-
- if (0 == *buf) {
- roff_err(tree, buf, "blank line");
- return(0);
- } else if ('.' != *buf)
- return(textparse(tree, buf));
-
- return(roffparse(tree, buf));
-}
-
-
-static int
-textparse(struct rofftree *tree, char *buf)
-{
- char *bufp;
-
- /* TODO: literal parsing. */
-
- if ( ! (ROFF_BODY & tree->state)) {
- roff_err(tree, buf, "data not in body");
- return(0);
- }
-
- /* LINTED */
- while (*buf) {
- while (*buf && isspace(*buf))
- buf++;
-
- if (0 == *buf)
- break;
-
- bufp = buf++;
-
- while (*buf && ! isspace(*buf))
- buf++;
-
- if (0 != *buf) {
- *buf++ = 0;
- if ( ! roffdata(tree, 1, bufp))
- return(0);
- continue;
- }
-
- if ( ! roffdata(tree, 1, bufp))
- return(0);
- break;
- }
-
- return(1);
-}
-
-
-static int
-roffargs(const struct rofftree *tree,
- int tok, char *buf, char **argv)
-{
- int i;
- char *p;
-
- assert(tok >= 0 && tok < ROFF_MAX);
- assert('.' == *buf);
-
- p = buf;
-
- /*
- * This is an ugly little loop. It parses a line into
- * space-delimited tokens. If a quote mark is encountered, a
- * token is alloted the entire quoted text. If whitespace is
- * escaped, it's included in the prior alloted token.
- */
-
- /* LINTED */
- for (i = 0; *buf && i < ROFF_MAXLINEARG; i++) {
- if ('\"' == *buf) {
- argv[i] = ++buf;
- while (*buf && '\"' != *buf)
- buf++;
- if (0 == *buf) {
- roff_err(tree, argv[i], "unclosed "
- "quote in argument "
- "list for `%s'",
- toknames[tok]);
- return(0);
- }
- } else {
- argv[i] = buf++;
- while (*buf) {
- if ( ! isspace(*buf)) {
- buf++;
- continue;
- }
- if (*(buf - 1) == '\\') {
- buf++;
- continue;
- }
- break;
- }
- if (0 == *buf)
- continue;
- }
- *buf++ = 0;
- while (*buf && isspace(*buf))
- buf++;
- }
-
- assert(i > 0);
- if (ROFF_MAXLINEARG == i && *buf) {
- roff_err(tree, p, "too many arguments for `%s'", toknames
- [tok]);
- return(0);
- }
-
- argv[i] = NULL;
- return(1);
-}
-
-
-static int
-roffscan(int tok, const int *tokv)
-{
-
- if (NULL == tokv)
- return(1);
-
- for ( ; ROFF_MAX != *tokv; tokv++)
- if (tok == *tokv)
- return(1);
-
- return(0);
-}
-
-
-static int
-roffparse(struct rofftree *tree, char *buf)
-{
- int tok, t;
- struct roffnode *n;
- char *argv[ROFF_MAXLINEARG];
- char **argvp;
-
- if (0 != *buf && 0 != *(buf + 1) && 0 != *(buf + 2))
- if (0 == strncmp(buf, ".\\\"", 3))
- return(1);
-
- if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {
- roff_err(tree, buf + 1, "bogus line macro");
- return(0);
- } else if (NULL == tokens[tok].cb) {
- roff_err(tree, buf + 1, "unsupported macro `%s'",
- toknames[tok]);
- return(0);
- }
-
- assert(ROFF___ != tok);
- if ( ! roffargs(tree, tok, buf, argv))
- return(0);
-
- argvp = (char **)argv;
-
- /*
- * Prelude macros break some assumptions, so branch now.
- */
-
- if (ROFF_PRELUDE & tree->state) {
- assert(NULL == tree->last);
- return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
- }
-
- assert(ROFF_BODY & tree->state);
-
- /*
- * First check that our possible parents and parent's possible
- * children are satisfied.
- */
-
- if (tree->last && ! roffscan
- (tree->last->tok, tokens[tok].parents)) {
- roff_err(tree, *argvp, "`%s' has invalid parent `%s'",
- toknames[tok],
- toknames[tree->last->tok]);
- return(0);
- }
-
- if (tree->last && ! roffscan
- (tok, tokens[tree->last->tok].children)) {
- roff_err(tree, *argvp, "`%s' is invalid child of `%s'",
- toknames[tok],
- toknames[tree->last->tok]);
- return(0);
- }
-
- /*
- * Branch if we're not a layout token.
- */
-
- if (ROFF_LAYOUT != tokens[tok].type)
- return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
- 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);
- if (n->tok == tok)
- break;
- if (ROFF_SHALLOW & tokens[tok].flags) {
- n = NULL;
- break;
- }
- if (tokens[n->tok].ctx == n->tok)
- continue;
- roff_err(tree, *argv, "`%s' breaks `%s' scope",
- toknames[tok], toknames[n->tok]);
- return(0);
- }
-
- /*
- * Create a new scope, as no previous one exists to
- * close out.
- */
-
- if (NULL == n)
- return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
-
- /*
- * Close out all intermediary scoped blocks, then hang
- * the current scope from our predecessor's parent.
- */
-
- do {
- t = tree->last->tok;
- if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
- return(0);
- } while (t != tok);
-
- 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(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);
-
- /* LINTED */
- for (n = tree->last; n; n = n->parent)
- if (n->tok != tokens[tok].ctx) {
- if (n->tok == tokens[n->tok].ctx)
- continue;
- roff_err(tree, *argv, "`%s' breaks `%s' scope",
- toknames[tok], toknames[n->tok]);
- return(0);
- } else
- break;
-
-
- if (NULL == n) {
- roff_err(tree, *argv, "`%s' has no starting tag `%s'",
- toknames[tok],
- toknames[tokens[tok].ctx]);
- return(0);
- }
-
- /* LINTED */
- do {
- t = tree->last->tok;
- if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
- return(0);
- } while (t != tokens[tok].ctx);