diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | html.c | 15 | ||||
-rw-r--r-- | index.7 | 4 | ||||
-rw-r--r-- | libmdocml.c | 18 | ||||
-rw-r--r-- | literals.c | 8 | ||||
-rw-r--r-- | mdocml.1 | 2 | ||||
-rw-r--r-- | mlg.c | 37 | ||||
-rw-r--r-- | private.h | 22 | ||||
-rw-r--r-- | roff.c | 243 |
9 files changed, 301 insertions, 53 deletions
@@ -35,7 +35,8 @@ INSTALL = Makefile $(HEADS) $(SRCS) $(MANS) FAIL = test.0 test.1 test.2 test.3 test.4 test.5 test.6 \ test.15 test.20 test.22 test.24 test.26 test.27 test.30 \ - test.36 test.37 test.40 test.50 test.61 + test.36 test.37 test.40 test.50 test.61 test.64 test.65 \ + test.66 SUCCEED = test.7 test.8 test.9 test.10 test.11 test.12 test.13 \ test.14 test.16 test.17 test.18 test.19 test.21 test.23 \ @@ -43,7 +44,7 @@ SUCCEED = test.7 test.8 test.9 test.10 test.11 test.12 test.13 \ test.35 test.38 test.39 test.41 test.42 test.43 test.44 \ test.45 test.46 test.47 test.48 test.49 test.51 test.52 \ test.54 test.55 test.56 test.57 test.58 test.59 test.60 \ - test.62 + test.62 test.63 test.67 all: mdocml @@ -141,7 +141,6 @@ html_It_headtagname(struct md_mbuf *mbuf, struct htmlq *q, switch (n->argc[i]) { case (ROFF_Ohang): return(ml_nputs(mbuf, "div", 3, res)); - case (ROFF_Tag): /* FALLTHROUGH */ case (ROFF_Column): @@ -151,8 +150,7 @@ html_It_headtagname(struct md_mbuf *mbuf, struct htmlq *q, } } - abort(); - /* NOTREACHED */ + return(0); } @@ -202,10 +200,7 @@ html_It_bodytagname(struct md_mbuf *mbuf, struct htmlq *q, } assert(i != ROFF_MAXLINEARG); - abort(); - /* NOTREACHED */ - - return(1); + return(0); } @@ -247,8 +242,7 @@ html_Bl_bodytagname(struct md_mbuf *mbuf, struct htmlq *q, } assert(i != ROFF_MAXLINEARG); - abort(); - /* NOTREACHED */ + return(0); } @@ -298,8 +292,7 @@ html_It_blocktagname(struct md_mbuf *mbuf, struct htmlq *q, } assert(i != ROFF_MAXLINEARG); - abort(); - /* NOTREACHED */ + return(0); } @@ -54,7 +54,7 @@ scope), .It valid predefined characters (such as \\*(>= and \\*q), .It -correctly-ordered document prelude, +correctly-ordered prelude and sections, .It sane macro argument values (such as those for .Sq \&.Dt @@ -103,7 +103,7 @@ Download at http://mdocml.bsd.lv/mdocml.tgz .Ns . .\" - UPDATE ME WITH EVERY RELEASE. ---------------------------------- -The current version is 1.0.0, dated 08/12/2008. +The current version is 1.0.1, dated 08/12/2008. .\" ------------------------------------------------------------------ .Pp Previous versions are archived as mdocml-x.y.z.tgz, with the appropriate diff --git a/libmdocml.c b/libmdocml.c index ba4e850f..5dbc6364 100644 --- a/libmdocml.c +++ b/libmdocml.c @@ -28,8 +28,6 @@ #include "libmdocml.h" #include "private.h" -#define BUFFER_LINE BUFSIZ /* Default line-buffer size. */ - static int md_run_enter(const struct md_args *, struct md_mbuf *, struct md_rbuf *, void *); static int md_run_leave(const struct md_args *, struct md_mbuf *, @@ -162,8 +160,8 @@ md_run_enter(const struct md_args *args, struct md_mbuf *mbuf, struct md_rbuf *rbuf, void *p) { ssize_t sz, i; - char line[BUFFER_LINE]; size_t pos; + char line[MD_LINE]; md_line fp; assert(args); @@ -192,17 +190,10 @@ again: return(md_run_leave(args, mbuf, rbuf, 0, p)); for (i = 0; i < sz; i++) { - /* - if ( ! isascii(rbuf->buf[i])) { - warnx("%s: non-ascii char (line %zu, col %zu)", - rbuf->name, rbuf->line, pos); - return(md_run_leave(args, mbuf, rbuf, -1, p)); - } - */ if ('\n' != rbuf->buf[i]) { - if (pos < BUFFER_LINE) { + if (pos < MD_LINE) { /* LINTED */ - line[pos++] = rbuf->buf[i]; + rbuf->linebuf[pos++] = rbuf->buf[i]; continue; } warnx("%s: line %zu too long", @@ -210,7 +201,8 @@ again: return(md_run_leave(args, mbuf, rbuf, -1, p)); } - line[(int)pos] = 0; + rbuf->linebuf[(int)pos] = 0; + (void)memcpy(line, rbuf->linebuf, sizeof(line)); if ( ! (*fp)(p, line)) return(md_run_leave(args, mbuf, rbuf, -1, p)); rbuf->line++; @@ -88,7 +88,13 @@ ml_literal(int tok, const int *argc, assert(ROFF_ARGMAX == *argc); if (NULL == *morep) return("AT&T UNIX"); - if (0 == strcmp(*morep, "v6")) + if (0 == strcmp(*morep, "v1")) + return("Version 1 AT&T UNIX"); + else if (0 == strcmp(*morep, "v2")) + return("Version 2 AT&T UNIX"); + else if (0 == strcmp(*morep, "v3")) + return("Version 3 AT&T UNIX"); + else if (0 == strcmp(*morep, "v6")) return("Version 6 AT&T UNIX"); else if (0 == strcmp(*morep, "v7")) return("Version 7 AT&T UNIX"); @@ -34,6 +34,8 @@ which may be for stdout. .It Fl W Print warnings to stderr. +.It Fl v +Make warning and error messages verbose. .It Ar infile Read input from .Ar infile , @@ -543,7 +543,7 @@ mlg_atom_special(struct md_mlg *p, int tok, if ( ! mlg_string(p, start, *more++)) return(0); - assert(NULL == *more); + /*assert(NULL == *more);*/ /* FIXME: ROFF_Sx */ return(mlg_endtag(p, MD_NS_INLINE, tok)); } @@ -790,6 +790,8 @@ mlg_msg(struct md_mlg *p, enum roffmsg lvl, const char *buf, const char *pos, const char *msg) { char *level; + char b[256]; + int i; switch (lvl) { case (ROFF_WARN): @@ -803,12 +805,31 @@ mlg_msg(struct md_mlg *p, enum roffmsg lvl, default: abort(); } - - if (pos) - (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n", - p->rbuf->name, p->rbuf->line, level, - msg, pos - buf); - else - (void)fprintf(stderr, "%s: %s: %s\n", + + if (pos) { + assert(pos >= buf); + if (0 < p->args->verbosity) { + (void)snprintf(b, sizeof(b), + "%s:%zu: %s: %s\n", + p->rbuf->name, p->rbuf->line, + level, msg); + (void)strlcat(b, "Error at: ", sizeof(b)); + (void)strlcat(b, p->rbuf->linebuf, sizeof(b)); + + (void)strlcat(b, "\n ", sizeof(b)); + for (i = 0; i < pos - buf; i++) + (void)strlcat(b, " ", sizeof(b)); + (void)strlcat(b, "^", sizeof(b)); + + } else + (void)snprintf(b, sizeof(b), + "%s:%zu: %s: %s (col %zu)", + p->rbuf->name, p->rbuf->line, + level, msg, pos - buf); + } else + (void)snprintf(b, sizeof(b), "%s: %s: %s", p->rbuf->name, level, msg); + + (void)fprintf(stderr, "%s\n", b); } + @@ -19,6 +19,7 @@ #ifndef PRIVATE_H #define PRIVATE_H +#include <stdio.h> #include <time.h> struct md_rbuf { @@ -27,6 +28,8 @@ struct md_rbuf { char *buf; /* Buffer. */ size_t bufsz; /* Size of buffer. */ size_t line; /* Current line number. */ +#define MD_LINE (BUFSIZ) + char linebuf[MD_LINE]; }; struct md_mbuf { @@ -266,6 +269,25 @@ enum roffmsec { ROFF_MSEC_MAX }; +#define ROFFSec_NMASK (0x07) + +#define ROFFSec_NAME (1 << 0) +#define ROFFSec_SYNOP (1 << 1) +#define ROFFSec_DESC (1 << 2) +#define ROFFSec_RETVAL (1 << 3) +#define ROFFSec_ENV (1 << 4) +#define ROFFSec_FILES (1 << 5) +#define ROFFSec_EX (1 << 6) +#define ROFFSec_DIAG (1 << 7) +#define ROFFSec_ERRS (1 << 8) +#define ROFFSec_SEEALSO (1 << 9) +#define ROFFSec_STAND (1 << 10) +#define ROFFSec_HIST (1 << 11) +#define ROFFSec_AUTH (1 << 12) +#define ROFFSec_CAVEATS (1 << 13) +#define ROFFSec_BUGS (1 << 14) +#define ROFFSec_OTHER (1 << 15) + struct roffcb { void (*roffmsg)(void *, enum roffmsg, const char *, const char *, const char *); @@ -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. */ @@ -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 *); @@ -81,7 +82,10 @@ 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); @@ -124,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) @@ -328,7 +337,7 @@ 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 ( ! roffargs(tree, tok, buf, argv)) return(0); @@ -517,6 +526,125 @@ 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) { @@ -660,7 +788,13 @@ roffspecial(struct rofftree *tree, int tok, const char *start, case (ROFF_At): if (0 == sz) break; - if (0 == strcmp(*ordp, "v6")) + 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; @@ -670,7 +804,7 @@ roffspecial(struct rofftree *tree, int tok, const char *start, break; else if (0 == strcmp(*ordp, "V.4")) break; - roff_err(tree, start, "invalid `At' arg"); + roff_err(tree, *ordp, "invalid `At' arg"); return(0); case (ROFF_Xr): @@ -683,6 +817,8 @@ roffspecial(struct rofftree *tree, int tok, const char *start, } /* FALLTHROUGH */ + case (ROFF_Sx): + /* FALLTHROUGH*/ case (ROFF_Fn): if (0 != sz) break; @@ -704,8 +840,6 @@ roffspecial(struct rofftree *tree, int tok, const char *start, case (ROFF_Rv): /* FALLTHROUGH*/ - case (ROFF_Sx): - /* FALLTHROUGH*/ case (ROFF_Ex): if (1 == sz) break; @@ -1159,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]); @@ -1183,11 +1331,56 @@ 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... + * + * <block> + * <head> + * <!Fl f!> ; + * </head> + * + * <body> + * Bar. + * </body> + * </block> */ 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, @@ -1210,8 +1403,7 @@ roff_layout(ROFFCALL_ARGS) if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok)) return(0); - return((*tree->cb.roffblkbodyin) - (tree->arg, tok, argcp, + return((*tree->cb.roffblkbodyin)(tree->arg, tok, argcp, (const char **)argvp)); } @@ -1254,12 +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, - (const char **)argvp)); + return((*tree->cb.roffblkbodyin)(tree->arg, + tok, argcp, (const char **)argvp)); } @@ -1271,6 +1461,16 @@ roff_ordered(ROFFCALL_ARGS) 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]); @@ -1279,15 +1479,14 @@ roff_ordered(ROFFCALL_ARGS) first = (*argv == tree->cur); p = *argv++; + ordp[0] = NULL; if ( ! roffparseopts(tree, tok, &argv, argcp, argvp)) return(0); - if (NULL == *argv) { - ordp[0] = NULL; + if (NULL == *argv) return(roffspecial(tree, tok, p, argcp, (const char **)argvp, 0, ordp)); - } i = 0; while (*argv && i < ROFF_MAXLINEARG) { @@ -1335,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... + * + * <fl> v W f </fl> ; + */ + if (ROFF_PRELUDE & tree->state) { roff_err(tree, *argv, "`%s' disallowed in prelude", toknames[tok]); |