=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.43 retrieving revision 1.52 diff -u -p -r1.43 -r1.52 --- mandoc/roff.c 2008/12/05 22:54:44 1.43 +++ mandoc/roff.c 2008/12/08 16:29:57 1.52 @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.43 2008/12/05 22:54:44 kristaps Exp $ */ +/* $Id: roff.c,v 1.52 2008/12/08 16:29:57 kristaps Exp $ */ /* * Copyright (c) 2008 Kristaps Dzonsons * @@ -35,7 +35,6 @@ /* FIXME: First letters of quoted-text interpreted in rofffindtok. */ /* FIXME: `No' not implemented. */ /* TODO: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */ -/* TODO: warn about "X section only" macros. */ /* TODO: warn about empty lists. */ /* TODO: (warn) some sections need specific elements. */ /* TODO: (warn) NAME section has particular order. */ @@ -57,7 +56,7 @@ struct rofftree { char name[64]; /* `Nm' results. */ char os[64]; /* `Os' results. */ char title[64]; /* `Dt' results. */ - char section[64]; /* `Dt' results. */ + enum roffmsec section; char volume[64]; /* `Dt' results. */ int state; #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */ @@ -67,6 +66,8 @@ struct rofftree { #define ROFF_BODY (1 << 5) /* In roff body. */ struct roffcb cb; /* Callbacks. */ void *arg; /* Callbacks' arg. */ + int csec; /* Current section. */ + int asec; /* Thus-far sections. */ }; static struct roffnode *roffnode_new(int, struct rofftree *); @@ -80,6 +81,11 @@ static int roffscan(int, const int *); static int rofffindtok(const char *); static int rofffindarg(const char *); static int rofffindcallable(const char *); +static int roffismsec(const char *); +static int roffissec(const char **); +static int roffispunct(const char *); +static int roffchecksec(struct rofftree *, + const char *, int); static int roffargs(const struct rofftree *, int, char *, char **); static int roffargok(int, int); @@ -88,13 +94,14 @@ static int roffnextopt(const struct rofftree *, static int roffparseopts(struct rofftree *, int, char ***, int *, char **); static int roffcall(struct rofftree *, int, char **); +static int roffexit(struct rofftree *, int); 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, char **); +static int roffspecial(struct rofftree *, int, + const char *, const int *, + const char **, size_t, char **); static int roffsetname(struct rofftree *, char **); -static int roffgetname(struct rofftree *, char **, - const char *); #ifdef __linux__ extern size_t strlcat(char *, const char *, size_t); @@ -121,7 +128,12 @@ roff_free(struct rofftree *tree, int flush) if (ROFF_PRELUDE & tree->state) { roff_err(tree, NULL, "prelude never finished"); goto end; - } + } else if ( ! (ROFFSec_NAME & tree->asec)) { + roff_err(tree, NULL, "missing `NAME' section"); + goto end; + } else if ( ! (ROFFSec_NMASK & tree->asec)) + roff_warn(tree, NULL, "missing suggested `NAME', " + "`SYNOPSIS', `DESCRIPTION' sections"); for (n = tree->last; n; n = n->parent) { if (0 != tokens[n->tok].ctx) @@ -133,7 +145,7 @@ roff_free(struct rofftree *tree, int flush) while (tree->last) { t = tree->last->tok; - if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT)) + if ( ! roffexit(tree, t)) goto end; } @@ -166,6 +178,7 @@ roff_alloc(const struct roffcb *cb, void *args) tree->state = ROFF_PRELUDE; tree->arg = args; + tree->section = ROFF_MSEC_MAX; (void)memcpy(&tree->cb, cb, sizeof(struct roffcb)); @@ -324,18 +337,11 @@ roffparse(struct rofftree *tree, char *buf) return(1); if (ROFF_MAX == (tok = rofffindtok(buf + 1))) { - roff_err(tree, buf + 1, "bogus line macro"); + roff_err(tree, buf, "bogus line macro"); return(0); - } else if (NULL == tokens[tok].cb) { - roff_err(tree, buf + 1, "unsupported macro `%s'", - toknames[tok]); + } else if ( ! roffargs(tree, tok, buf, argv)) return(0); - } - assert(ROFF___ != tok); - if ( ! roffargs(tree, tok, buf, argv)) - return(0); - argvp = (char **)argv; /* @@ -344,7 +350,7 @@ roffparse(struct rofftree *tree, char *buf) if (ROFF_PRELUDE & tree->state) { assert(NULL == tree->last); - return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER)); + return(roffcall(tree, tok, argvp)); } assert(ROFF_BODY & tree->state); @@ -375,9 +381,9 @@ roffparse(struct rofftree *tree, char *buf) */ if (ROFF_LAYOUT != tokens[tok].type) - return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER)); + return(roffcall(tree, tok, argvp)); if (0 == tokens[tok].ctx) - return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER)); + return(roffcall(tree, tok, argvp)); /* * First consider implicit-end tags, like as follows: @@ -419,7 +425,7 @@ roffparse(struct rofftree *tree, char *buf) */ if (NULL == n) - return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER)); + return(roffcall(tree, tok, argvp)); /* * Close out all intermediary scoped blocks, then hang @@ -428,11 +434,11 @@ roffparse(struct rofftree *tree, char *buf) do { t = tree->last->tok; - if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT)) + if ( ! roffexit(tree, t)) return(0); } while (t != tok); - return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER)); + return(roffcall(tree, tok, argvp)); } /* @@ -468,7 +474,7 @@ roffparse(struct rofftree *tree, char *buf) /* LINTED */ do { t = tree->last->tok; - if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT)) + if ( ! roffexit(tree, t)) return(0); } while (t != tokens[tok].ctx); @@ -520,6 +526,160 @@ rofffindtok(const char *buf) static int +roffchecksec(struct rofftree *tree, const char *start, int sec) +{ + int prior; + + switch (sec) { + case(ROFFSec_SYNOP): + if ((prior = ROFFSec_NAME) & tree->asec) + return(1); + break; + case(ROFFSec_DESC): + if ((prior = ROFFSec_SYNOP) & tree->asec) + return(1); + break; + case(ROFFSec_RETVAL): + if ((prior = ROFFSec_DESC) & tree->asec) + return(1); + break; + case(ROFFSec_ENV): + if ((prior = ROFFSec_RETVAL) & tree->asec) + return(1); + break; + case(ROFFSec_FILES): + if ((prior = ROFFSec_ENV) & tree->asec) + return(1); + break; + case(ROFFSec_EX): + if ((prior = ROFFSec_FILES) & tree->asec) + return(1); + break; + case(ROFFSec_DIAG): + if ((prior = ROFFSec_EX) & tree->asec) + return(1); + break; + case(ROFFSec_ERRS): + if ((prior = ROFFSec_DIAG) & tree->asec) + return(1); + break; + case(ROFFSec_SEEALSO): + if ((prior = ROFFSec_ERRS) & tree->asec) + return(1); + break; + case(ROFFSec_STAND): + if ((prior = ROFFSec_SEEALSO) & tree->asec) + return(1); + break; + case(ROFFSec_HIST): + if ((prior = ROFFSec_STAND) & tree->asec) + return(1); + break; + case(ROFFSec_AUTH): + if ((prior = ROFFSec_HIST) & tree->asec) + return(1); + break; + case(ROFFSec_CAVEATS): + if ((prior = ROFFSec_AUTH) & tree->asec) + return(1); + break; + case(ROFFSec_BUGS): + if ((prior = ROFFSec_CAVEATS) & tree->asec) + return(1); + break; + default: + return(1); + } + + roff_warn(tree, start, "section violates conventional order"); + return(1); +} + + +static int +roffissec(const char **p) +{ + + assert(*p); + if (NULL != *(p + 1)) { + if (NULL != *(p + 2)) + return(ROFFSec_OTHER); + if (0 == strcmp(*p, "RETURN") && + 0 == strcmp(*(p + 1), "VALUES")) + return(ROFFSec_RETVAL); + if (0 == strcmp(*p, "SEE") && + 0 == strcmp(*(p + 1), "ALSO")) + return(ROFFSec_SEEALSO); + return(ROFFSec_OTHER); + } + + if (0 == strcmp(*p, "NAME")) + return(ROFFSec_NAME); + else if (0 == strcmp(*p, "SYNOPSIS")) + return(ROFFSec_SYNOP); + else if (0 == strcmp(*p, "DESCRIPTION")) + return(ROFFSec_DESC); + else if (0 == strcmp(*p, "ENVIRONMENT")) + return(ROFFSec_ENV); + else if (0 == strcmp(*p, "FILES")) + return(ROFFSec_FILES); + else if (0 == strcmp(*p, "EXAMPLES")) + return(ROFFSec_EX); + else if (0 == strcmp(*p, "DIAGNOSTICS")) + return(ROFFSec_DIAG); + else if (0 == strcmp(*p, "ERRORS")) + return(ROFFSec_ERRS); + else if (0 == strcmp(*p, "STANDARDS")) + return(ROFFSec_STAND); + else if (0 == strcmp(*p, "HISTORY")) + return(ROFFSec_HIST); + else if (0 == strcmp(*p, "AUTHORS")) + return(ROFFSec_AUTH); + else if (0 == strcmp(*p, "CAVEATS")) + return(ROFFSec_CAVEATS); + else if (0 == strcmp(*p, "BUGS")) + return(ROFFSec_BUGS); + + return(ROFFSec_OTHER); +} + + +static int +roffismsec(const char *p) +{ + + if (0 == strcmp(p, "1")) + return(ROFF_MSEC_1); + else if (0 == strcmp(p, "2")) + return(ROFF_MSEC_2); + else if (0 == strcmp(p, "3")) + return(ROFF_MSEC_3); + else if (0 == strcmp(p, "3p")) + return(ROFF_MSEC_3p); + else if (0 == strcmp(p, "4")) + return(ROFF_MSEC_4); + else if (0 == strcmp(p, "5")) + return(ROFF_MSEC_5); + else if (0 == strcmp(p, "6")) + return(ROFF_MSEC_6); + else if (0 == strcmp(p, "7")) + return(ROFF_MSEC_7); + else if (0 == strcmp(p, "8")) + return(ROFF_MSEC_8); + else if (0 == strcmp(p, "9")) + return(ROFF_MSEC_9); + else if (0 == strcmp(p, "unass")) + return(ROFF_MSEC_UNASS); + else if (0 == strcmp(p, "draft")) + return(ROFF_MSEC_DRAFT); + else if (0 == strcmp(p, "paper")) + return(ROFF_MSEC_PAPER); + + return(ROFF_MSEC_MAX); +} + + +static int roffispunct(const char *p) { @@ -619,26 +779,141 @@ roffnode_free(struct rofftree *tree) static int -roffspecial(struct rofftree *tree, int tok, char **ordp) +roffspecial(struct rofftree *tree, int tok, const char *start, + const int *argc, const char **argv, + size_t sz, char **ordp) { - return((*tree->cb.roffspecial)(tree->arg, tok, - tree->cur, ordp)); + switch (tok) { + case (ROFF_At): + if (0 == sz) + break; + if (0 == strcmp(*ordp, "v1")) + break; + else if (0 == strcmp(*ordp, "v2")) + break; + else if (0 == strcmp(*ordp, "v3")) + break; + else if (0 == strcmp(*ordp, "v6")) + break; + else if (0 == strcmp(*ordp, "v7")) + break; + else if (0 == strcmp(*ordp, "32v")) + break; + else if (0 == strcmp(*ordp, "V.1")) + break; + else if (0 == strcmp(*ordp, "V.4")) + break; + roff_err(tree, *ordp, "invalid `At' arg"); + return(0); + + case (ROFF_Xr): + if (2 == sz) { + assert(ordp[1]); + if (ROFF_MSEC_MAX != roffismsec(ordp[1])) + break; + roff_warn(tree, start, "invalid `%s' manual " + "section", toknames[tok]); + } + /* FALLTHROUGH */ + + case (ROFF_Sx): + /* FALLTHROUGH*/ + case (ROFF_Fn): + if (0 != sz) + break; + roff_err(tree, start, "`%s' expects at least " + "one arg", toknames[tok]); + return(0); + + case (ROFF_Nm): + if (0 == sz) { + if (0 == tree->name[0]) { + roff_err(tree, start, "`Nm' not set"); + return(0); + } + ordp[0] = tree->name; + ordp[1] = NULL; + } else if ( ! roffsetname(tree, ordp)) + return(0); + break; + + case (ROFF_Rv): + /* FALLTHROUGH*/ + case (ROFF_Ex): + if (1 == sz) + break; + roff_err(tree, start, "`%s' expects one arg", + toknames[tok]); + return(0); + + case (ROFF_Sm): + if (1 != sz) { + roff_err(tree, start, "`Sm' expects one arg"); + return(0); + } + + if (0 != strcmp(ordp[0], "on") && + 0 != strcmp(ordp[0], "off")) { + roff_err(tree, start, "`Sm' has invalid argument"); + return(0); + } + break; + + case (ROFF_Ud): + /* FALLTHROUGH */ + case (ROFF_Ux): + /* FALLTHROUGH */ + case (ROFF_Bt): + if (0 != sz) { + roff_err(tree, start, "`%s' expects no args", + toknames[tok]); + return(0); + } + break; + default: + break; + } + + return((*tree->cb.roffspecial)(tree->arg, tok, tree->cur, + argc, argv, (const char **)ordp)); } static int +roffexit(struct rofftree *tree, int tok) +{ + + assert(tokens[tok].cb); + return((*tokens[tok].cb)(tok, tree, NULL, ROFF_EXIT)); +} + + +static int roffcall(struct rofftree *tree, int tok, char **argv) { + int i; + enum roffmsec c; if (NULL == tokens[tok].cb) { - roff_err(tree, *argv, "unsupported macro `%s'", + roff_err(tree, *argv, "`%s' is unsupported", toknames[tok]); return(0); } - if ( ! (*tokens[tok].cb)(tok, tree, argv, ROFF_ENTER)) - return(0); - return(1); + if (tokens[tok].sections && ROFF_MSEC_MAX != tree->section) { + i = 0; + while (ROFF_MSEC_MAX != + (c = tokens[tok].sections[i++])) + if (c == tree->section) + break; + if (ROFF_MSEC_MAX == c) { + roff_warn(tree, *argv, "`%s' is not a valid " + "macro in this manual section", + toknames[tok]); + } + } + + return((*tokens[tok].cb)(tok, tree, argv, ROFF_ENTER)); } @@ -772,7 +1047,7 @@ roff_Dd(ROFFCALL_ARGS) argv++; - if (0 == strcmp(*argv, "$Mdocdate: December 5 2008 $")) { + if (0 == strcmp(*argv, "$Mdocdate: December 8 2008 $")) { t = time(NULL); if (NULL == localtime_r(&t, &tree->tm)) err(1, "localtime_r"); @@ -865,9 +1140,10 @@ roff_Dt(ROFFCALL_ARGS) if (NULL == *argv) { roff_err(tree, *argv, "`Dt' needs section"); return(0); - } else if (strlcpy(tree->section, *argv, sizeof(tree->section)) - >= sizeof(tree->section)) { - roff_err(tree, *argv, "`Dt' section too long"); + } + + if (ROFF_MSEC_MAX == (tree->section = roffismsec(*argv))) { + roff_err(tree, *argv, "bad `Dt' section"); return(0); } @@ -888,19 +1164,6 @@ roff_Dt(ROFFCALL_ARGS) static int -roffgetname(struct rofftree *tree, char **ordp, const char *start) -{ - if (0 == tree->name[0]) { - roff_err(tree, start, "`Nm' name not set"); - return(0); - } - *ordp++ = tree->name; - *ordp = NULL; - return(1); -} - - -static int roffsetname(struct rofftree *tree, char **ordp) { @@ -924,37 +1187,6 @@ roffsetname(struct rofftree *tree, char **ordp) /* ARGSUSED */ -static int -roff_Sm(ROFFCALL_ARGS) -{ - char *morep[1], *p; - - p = *argv++; - - if (NULL == (morep[0] = *argv++)) { - roff_err(tree, p, "`Sm' expects an argument"); - return(0); - } else if (0 != strcmp(morep[0], "on") && - 0 != strcmp(morep[0], "off")) { - roff_err(tree, p, "`Sm' has invalid argument"); - return(0); - } - - if (*argv) - roff_warn(tree, *argv, "`Sm' shouldn't have arguments"); - - if ( ! roffspecial(tree, tok, morep)) - return(0); - - while (*argv) - if ( ! roffdata(tree, 1, *argv++)) - return(0); - - return(1); -} - - -/* ARGSUSED */ static int roff_Ns(ROFFCALL_ARGS) { @@ -964,7 +1196,7 @@ roff_Ns(ROFFCALL_ARGS) first = (*argv++ == tree->cur); morep[0] = NULL; - if ( ! roffspecial(tree, tok, morep)) + if ( ! roffspecial(tree, tok, *argv, NULL, NULL, 0, morep)) return(0); while (*argv) { @@ -1042,6 +1274,10 @@ roff_Os(ROFFCALL_ARGS) tree->state &= ~ROFF_PRELUDE; tree->state |= ROFF_BODY; + assert(ROFF_MSEC_MAX != tree->section); + assert(0 != tree->title[0]); + assert(0 != tree->os[0]); + assert(NULL == tree->last); return((*tree->cb.roffhead)(tree->arg, &tree->tm, @@ -1057,6 +1293,20 @@ roff_layout(ROFFCALL_ARGS) int i, c, argcp[ROFF_MAXLINEARG]; char *argvp[ROFF_MAXLINEARG]; + /* + * The roff_layout function is for multi-line macros. A layout + * has a start and end point, which is either declared + * explicitly or implicitly. An explicit start and end is + * embodied by `.Bl' and `.El', with the former being the start + * and the latter being an end. The `.Sh' and `.Ss' tags, on + * the other hand, are implicit. The scope of a layout is the + * space between start and end. Explicit layouts may not close + * out implicit ones and vice versa; implicit layouts may close + * out other implicit layouts. + */ + + assert( ! (ROFF_CALLABLE & tokens[tok].flags)); + if (ROFF_PRELUDE & tree->state) { roff_err(tree, *argv, "bad `%s' in prelude", toknames[tok]); @@ -1081,15 +1331,63 @@ roff_layout(ROFFCALL_ARGS) * Layouts have two parts: the layout body and header. The * layout header is the trailing text of the line macro, while * the layout body is everything following until termination. + * Example: + * + * .It Fl f ) ; + * Bar. + * + * ...Produces... + * + * + * + * ; + * + * + * + * Bar. + * + * */ - if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp, argvp)) + if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp, + (const char **)argvp)) return(0); + + /* +++ Begin run macro-specific hooks over argv. */ + + switch (tok) { + case (ROFF_Sh): + if (NULL == *argv) { + roff_err(tree, *(argv - 1), + "`Sh' expects arguments"); + return(0); + } + tree->csec = roffissec((const char **)argv); + if ( ! (ROFFSec_OTHER & tree->csec) && + tree->asec & tree->csec) + roff_warn(tree, *argv, "section repeated"); + if (0 == tree->asec && ! (ROFFSec_NAME & tree->csec)) { + roff_err(tree, *argv, "`NAME' section " + "must be first"); + return(0); + } else if ( ! roffchecksec(tree, *argv, tree->csec)) + return(0); + + tree->asec |= tree->csec; + break; + default: + break; + } + + /* --- End run macro-specific hooks over argv. */ + if (NULL == *argv) return((*tree->cb.roffblkbodyin) - (tree->arg, tok, argcp, argvp)); + (tree->arg, tok, argcp, + (const char **)argvp)); - if ( ! (*tree->cb.roffblkheadin)(tree->arg, tok, argcp, argvp)) + if ( ! (*tree->cb.roffblkheadin)(tree->arg, tok, argcp, + (const char **)argvp)) return(0); /* @@ -1105,8 +1403,8 @@ roff_layout(ROFFCALL_ARGS) if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok)) return(0); - return((*tree->cb.roffblkbodyin) - (tree->arg, tok, argcp, argvp)); + return((*tree->cb.roffblkbodyin)(tree->arg, tok, argcp, + (const char **)argvp)); } /* @@ -1137,7 +1435,8 @@ roff_layout(ROFFCALL_ARGS) if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok)) return(0); return((*tree->cb.roffblkbodyin) - (tree->arg, tok, argcp, argvp)); + (tree->arg, tok, argcp, + (const char **)argvp)); } /* @@ -1147,11 +1446,10 @@ roff_layout(ROFFCALL_ARGS) if ( ! roffpurgepunct(tree, argv)) return(0); - if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok)) return(0); - return((*tree->cb.roffblkbodyin) - (tree->arg, tok, argcp, argvp)); + return((*tree->cb.roffblkbodyin)(tree->arg, + tok, argcp, (const char **)argvp)); } @@ -1159,9 +1457,20 @@ roff_layout(ROFFCALL_ARGS) static int roff_ordered(ROFFCALL_ARGS) { - int i, first, c; - char *ordp[ROFF_MAXLINEARG]; + int i, first, c, argcp[ROFF_MAXLINEARG]; + char *ordp[ROFF_MAXLINEARG], *p, + *argvp[ROFF_MAXLINEARG]; + /* + * Ordered macros pass their arguments directly to handlers, + * instead of considering it free-form text. Thus, the + * following macro looks as follows: + * + * .Xr foo 1 ) , + * + * .Xr arg1 arg2 punctuation + */ + if (ROFF_PRELUDE & tree->state) { roff_err(tree, *argv, "`%s' disallowed in prelude", toknames[tok]); @@ -1169,25 +1478,20 @@ roff_ordered(ROFFCALL_ARGS) } first = (*argv == tree->cur); - argv++; + p = *argv++; + ordp[0] = NULL; - if (NULL == *argv) { - switch (tok) { - case (ROFF_Nm): - if ( ! roffgetname(tree, ordp, *(argv - 1))) - return(0); - break; - default: - *ordp = NULL; - break; - } + if ( ! roffparseopts(tree, tok, &argv, argcp, argvp)) + return(0); - return(roffspecial(tree, tok, ordp)); - } + if (NULL == *argv) + return(roffspecial(tree, tok, p, argcp, + (const char **)argvp, 0, ordp)); i = 0; while (*argv && i < ROFF_MAXLINEARG) { - c = rofffindcallable(*argv); + c = ROFF_PARSED & tokens[tok].flags ? + rofffindcallable(*argv) : ROFF_MAX; if (ROFF_MAX == c && ! roffispunct(*argv)) { ordp[i++] = *argv++; @@ -1198,34 +1502,20 @@ roff_ordered(ROFFCALL_ARGS) if (ROFF_MAX == c) break; - switch (tok) { - case (ROFF_Nm): - if ( ! roffsetname(tree, ordp)) - return(0); - break; - default: - break; - } - - if ( ! roffspecial(tree, tok, ordp)) + if ( ! roffspecial(tree, tok, p, argcp, + (const char **)argvp, + (size_t)i, ordp)) return(0); - return(roffcall(tree, c, ordp)); + return(roffcall(tree, c, argv)); } assert(i != ROFF_MAXLINEARG); ordp[i] = NULL; - switch (tok) { - case (ROFF_Nm): - if ( ! roffsetname(tree, ordp)) - return(0); - break; - default: - break; - } - - if ( ! roffspecial(tree, tok, ordp)) + if ( ! roffspecial(tree, tok, p, argcp, + (const char**)argvp, + (size_t)i, ordp)) return(0); /* FIXME: error if there's stuff after the punctuation. */ @@ -1244,6 +1534,18 @@ roff_text(ROFFCALL_ARGS) int i, j, first, c, argcp[ROFF_MAXLINEARG]; char *argvp[ROFF_MAXLINEARG]; + /* + * Text macros are similar to special tokens, except that + * arguments are instead flushed as pure data: we're only + * concerned with the macro and its arguments. Example: + * + * .Fl v W f ; + * + * ...Produces... + * + * v W f ; + */ + if (ROFF_PRELUDE & tree->state) { roff_err(tree, *argv, "`%s' disallowed in prelude", toknames[tok]); @@ -1255,7 +1557,8 @@ roff_text(ROFFCALL_ARGS) if ( ! roffparseopts(tree, tok, &argv, argcp, argvp)) return(0); - if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp)) + if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, + (const char **)argvp)) return(0); if (NULL == *argv) return((*tree->cb.roffout)(tree->arg, tok)); @@ -1274,6 +1577,8 @@ roff_text(ROFFCALL_ARGS) * terminating punctuation. If we encounter it and all * subsequent tokens are punctuation, then stop processing (the * line-dominant macro will print these tokens after closure). + * If the punctuation is followed by non-punctuation, then close + * and re-open our scope, then continue. */ i = 0; @@ -1305,8 +1610,21 @@ roff_text(ROFFCALL_ARGS) break; if (argv[j]) { + if (ROFF_LSCOPE & tokens[tok].flags) { + if ( ! roffdata(tree, 0, *argv++)) + return(0); + continue; + } + if ( ! (*tree->cb.roffout)(tree->arg, tok)) + return(0); if ( ! roffdata(tree, 0, *argv++)) return(0); + if ( ! (*tree->cb.roffin)(tree->arg, tok, + argcp, + (const char **)argvp)) + return(0); + + i = 0; continue; }