=================================================================== RCS file: /cvs/mandoc/mdoc_validate.c,v retrieving revision 1.7 retrieving revision 1.17 diff -u -p -r1.7 -r1.17 --- mandoc/mdoc_validate.c 2009/06/10 20:18:43 1.7 +++ mandoc/mdoc_validate.c 2009/06/27 09:03:03 1.17 @@ -1,4 +1,4 @@ -/* $Id: mdoc_validate.c,v 1.7 2009/06/10 20:18:43 kristaps Exp $ */ +/* $Id: mdoc_validate.c,v 1.17 2009/06/27 09:03:03 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -42,15 +42,20 @@ enum merr { ELISTTYPE, EDISPTYPE, EMULTIDISP, + ESECNAME, EMULTILIST, EARGREP, EBOOL, + ECOLMIS, ENESTDISP }; enum mwarn { WPRINT, + WNOWIDTH, + WMISSWIDTH, WESCAPE, + WDEPCOL, WWRONGMSEC, WSECOOO, WSECREP, @@ -93,7 +98,6 @@ static int err_child_gt(struct mdoc *, const char *, i static int warn_child_gt(struct mdoc *, const char *, int); static int err_child_eq(struct mdoc *, const char *, int); static int warn_child_eq(struct mdoc *, const char *, int); -static int count_child(struct mdoc *); static int warn_print(struct mdoc *, int, int); static int warn_count(struct mdoc *, const char *, int, const char *, int); @@ -112,7 +116,6 @@ static int pre_fd(PRE_ARGS); static int pre_it(PRE_ARGS); static int pre_lb(PRE_ARGS); static int pre_os(PRE_ARGS); -static int pre_prologue(PRE_ARGS); static int pre_rv(PRE_ARGS); static int pre_sh(PRE_ARGS); static int pre_ss(PRE_ARGS); @@ -134,6 +137,7 @@ static int post_args(POST_ARGS); static int post_at(POST_ARGS); static int post_bf(POST_ARGS); static int post_bl(POST_ARGS); +static int post_bl_head(POST_ARGS); static int post_it(POST_ARGS); static int post_nm(POST_ARGS); static int post_root(POST_ARGS); @@ -151,15 +155,15 @@ static v_pre pres_an[] = { pre_an, NULL }; static v_pre pres_bd[] = { pre_display, pre_bd, NULL }; static v_pre pres_bl[] = { pre_bl, NULL }; static v_pre pres_cd[] = { pre_cd, NULL }; -static v_pre pres_dd[] = { pre_prologue, pre_dd, NULL }; +static v_pre pres_dd[] = { pre_dd, NULL }; static v_pre pres_d1[] = { pre_display, NULL }; -static v_pre pres_dt[] = { pre_prologue, pre_dt, NULL }; +static v_pre pres_dt[] = { pre_dt, NULL }; static v_pre pres_er[] = { pre_er, NULL }; static v_pre pres_ex[] = { pre_ex, NULL }; static v_pre pres_fd[] = { pre_fd, NULL }; static v_pre pres_it[] = { pre_it, NULL }; static v_pre pres_lb[] = { pre_lb, NULL }; -static v_pre pres_os[] = { pre_prologue, pre_os, NULL }; +static v_pre pres_os[] = { pre_os, NULL }; static v_pre pres_rv[] = { pre_rv, NULL }; static v_pre pres_sh[] = { pre_sh, NULL }; static v_pre pres_ss[] = { pre_ss, NULL }; @@ -170,7 +174,7 @@ static v_post posts_wtext[] = { ewarn_ge1, NULL }; static v_post posts_notext[] = { eerr_eq0, NULL }; static v_post posts_wline[] = { bwarn_ge1, herr_eq0, NULL }; static v_post posts_sh[] = { herr_ge1, bwarn_ge1, post_sh, NULL }; -static v_post posts_bl[] = { herr_eq0, bwarn_ge1, post_bl, NULL }; +static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL }; static v_post posts_it[] = { post_it, NULL }; static v_post posts_in[] = { ewarn_eq1, NULL }; static v_post posts_ss[] = { herr_ge1, NULL }; @@ -188,7 +192,7 @@ static v_post posts_bf[] = { hwarn_le1, post_bf, NULL static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL }; const struct valids mdoc_valids[MDOC_MAX] = { - { NULL, NULL }, /* \" */ + { NULL, NULL }, /* Ap */ { pres_dd, posts_text }, /* Dd */ { pres_dt, NULL }, /* Dt */ { pres_os, NULL }, /* Os */ @@ -295,9 +299,8 @@ const struct valids mdoc_valids[MDOC_MAX] = { { NULL, NULL }, /* Fr */ { NULL, posts_notext }, /* Ud */ { pres_lb, posts_lb }, /* Lb */ - { NULL, NULL }, /* Ap */ { NULL, posts_pp }, /* Lp */ - { NULL, posts_text }, /* Lk */ + { NULL, NULL }, /* Lk */ { NULL, posts_text }, /* Mt */ { NULL, posts_wline }, /* Brq */ { NULL, NULL }, /* Bro */ @@ -410,6 +413,9 @@ perr(struct mdoc *m, int line, int pos, enum merr type case (EDISPTYPE): p = "missing display type"; break; + case (ESECNAME): + p = "the NAME section must come first"; + break; case (ELINE): p = "expected line arguments"; break; @@ -419,6 +425,9 @@ perr(struct mdoc *m, int line, int pos, enum merr type case (ENODATA): p = "document has no data"; break; + case (ECOLMIS): + p = "column syntax style mismatch"; + break; case (EATT): p = "expected valid AT&T symbol"; break; @@ -460,6 +469,16 @@ pwarn(struct mdoc *m, int line, int pos, enum mwarn ty p = "prologue macros out-of-order"; c = WARN_COMPAT; break; + case (WDEPCOL): + p = "deprecated column argument syntax"; + c = WARN_COMPAT; + break; + case (WNOWIDTH): + p = "superfluous width argument"; + break; + case (WMISSWIDTH): + p = "missing width argument"; + break; case (WPRINT): p = "invalid character"; break; @@ -529,19 +548,6 @@ err_count(struct mdoc *m, const char *k, } -static inline int -count_child(struct mdoc *mdoc) -{ - int i; - struct mdoc_node *n; - - for (i = 0, n = mdoc->last->child; n; n = n->next, i++) - /* Do nothing */ ; - - return(i); -} - - /* * Build these up with macros because they're basically the same check * for different inequalities. Yes, this could be done with functions, @@ -552,10 +558,9 @@ count_child(struct mdoc *mdoc) static int \ lvl##_child_##name(struct mdoc *mdoc, const char *p, int sz) \ { \ - int i; \ - if ((i = count_child(mdoc)) ineq sz) \ + if (mdoc->last->nchild ineq sz) \ return(1); \ - return(lvl##_count(mdoc, #ineq, sz, p, i)); \ + return(lvl##_count(mdoc, #ineq, sz, p, mdoc->last->nchild)); \ } #define CHECK_BODY_DEFN(name, lvl, func, num) \ @@ -705,8 +710,6 @@ check_text(struct mdoc *mdoc, int line, int pos, const { size_t c; - /* FIXME: indicate deprecated escapes \*(xx and \*x. */ - for ( ; *p; p++) { if ('\t' == *p) { if ( ! (MDOC_LITERAL & mdoc->flags)) @@ -776,7 +779,7 @@ pre_display(PRE_ARGS) static int pre_bl(PRE_ARGS) { - int i, type, width, offset; + int pos, type, width, offset; if (MDOC_BLOCK != n->type) return(1); @@ -788,8 +791,8 @@ pre_bl(PRE_ARGS) type = offset = width = -1; /* LINTED */ - for (i = 0; i < (int)n->args->argc; i++) - switch (n->args->argv[i].arg) { + for (pos = 0; pos < (int)n->args->argc; pos++) + switch (n->args->argv[pos].arg) { case (MDOC_Bullet): /* FALLTHROUGH */ case (MDOC_Dash): @@ -811,23 +814,20 @@ pre_bl(PRE_ARGS) case (MDOC_Inset): /* FALLTHROUGH */ case (MDOC_Column): - if (-1 == type) { - type = n->args->argv[i].arg; - break; - } - return(nerr(mdoc, n, EMULTILIST)); + if (-1 != type) + return(nerr(mdoc, n, EMULTILIST)); + type = n->args->argv[pos].arg; + break; case (MDOC_Width): - if (-1 == width) { - width = n->args->argv[i].arg; - break; - } - return(nerr(mdoc, n, EARGREP)); + if (-1 != width) + return(nerr(mdoc, n, EARGREP)); + width = n->args->argv[pos].arg; + break; case (MDOC_Offset): - if (-1 == offset) { - offset = n->args->argv[i].arg; - break; - } - return(nerr(mdoc, n, EARGREP)); + if (-1 != offset) + return(nerr(mdoc, n, EARGREP)); + offset = n->args->argv[pos].arg; + break; default: break; } @@ -835,7 +835,17 @@ pre_bl(PRE_ARGS) if (-1 == type) return(nerr(mdoc, n, ELISTTYPE)); + /* + * Validate the width field. Some list types don't need width + * types and should be warned about them. Others should have it + * and must also be warned. + */ + switch (type) { + case (MDOC_Tag): + if (-1 == width && ! nwarn(mdoc, n, WMISSWIDTH)) + return(0); + break; case (MDOC_Column): /* FALLTHROUGH */ case (MDOC_Diag): @@ -843,17 +853,9 @@ pre_bl(PRE_ARGS) case (MDOC_Inset): /* FALLTHROUGH */ case (MDOC_Item): - if (-1 == width) - break; - return(mdoc_nwarn(mdoc, n, WARN_SYNTAX, - "superfluous %s argument", - mdoc_argnames[MDOC_Width])); - case (MDOC_Tag): - if (-1 != width) - break; - return(mdoc_nwarn(mdoc, n, WARN_SYNTAX, - "suggest %s argument", - mdoc_argnames[MDOC_Width])); + if (-1 != width && ! nwarn(mdoc, n, WNOWIDTH)) + return(0); + break; default: break; } @@ -985,14 +987,6 @@ pre_cd(PRE_ARGS) static int -pre_prologue(PRE_ARGS) -{ - - return(check_sec(mdoc, n, SEC_PROLOGUE, SEC_CUSTOM)); -} - - -static int pre_dt(PRE_ARGS) { @@ -1045,25 +1039,24 @@ post_bf(POST_ARGS) head = mdoc->last->head; - if (NULL == mdoc->last->args) { - if (NULL == head->child || - MDOC_TEXT != head->child->type) - return(mdoc_err(mdoc, "text argument expected")); + if (mdoc->last->args && head->child) + return(mdoc_err(mdoc, "one argument expected")); + else if (mdoc->last->args) + return(1); - p = head->child->string; - if (0 == strcmp(p, "Em")) - return(1); - else if (0 == strcmp(p, "Li")) - return(1); - else if (0 == strcmp(p, "Sm")) - return(1); - return(mdoc_nerr(mdoc, head->child, "invalid font")); - } + if (NULL == head->child || MDOC_TEXT != head->child->type) + return(mdoc_err(mdoc, "text argument expected")); - if (head->child) - return(mdoc_err(mdoc, "one argument expected")); + p = head->child->string; - return(1); + if (0 == strcmp(p, "Em")) + return(1); + else if (0 == strcmp(p, "Li")) + return(1); + else if (0 == strcmp(p, "Sm")) + return(1); + + return(mdoc_nerr(mdoc, head->child, "invalid font mode")); } @@ -1229,10 +1222,35 @@ post_it(POST_ARGS) static int +post_bl_head(POST_ARGS) +{ + int i; + const struct mdoc_node *n; + + n = mdoc->last->parent; + assert(n->args); + + for (i = 0; i < (int)n->args->argc; i++) + if (n->args->argv[i].arg == MDOC_Column) + break; + + if (i == (int)n->args->argc) + return(1); + + if (n->args->argv[i].sz && mdoc->last->child) + return(nerr(mdoc, n, ECOLMIS)); + + return(1); +} + + +static int post_bl(POST_ARGS) { struct mdoc_node *n; + if (MDOC_HEAD == mdoc->last->type) + return(post_bl_head(mdoc)); if (MDOC_BODY != mdoc->last->type) return(1); if (NULL == mdoc->last->child) @@ -1279,7 +1297,7 @@ post_root(POST_ARGS) if (NULL == mdoc->first->child) return(verr(mdoc, ENODATA)); - if (SEC_PROLOGUE == mdoc->lastnamed) + if ( ! (MDOC_PBODY & mdoc->flags)) return(verr(mdoc, ENOPROLOGUE)); if (MDOC_BLOCK != mdoc->first->child->type) @@ -1360,13 +1378,12 @@ post_sh_head(POST_ARGS) * certain manual sections. */ - assert(MDOC_Sh == mdoc->last->tok); - - /* This is just concat() inlined, which is irritating. */ - buf[0] = 0; + for (n = mdoc->last->child; n; n = n->next) { + /* XXX - copied from compact(). */ assert(MDOC_TEXT == n->type); + if (strlcat(buf, n->string, 64) >= 64) return(nerr(mdoc, n, ETOOLONG)); if (NULL == n->next) @@ -1377,21 +1394,24 @@ post_sh_head(POST_ARGS) sec = mdoc_atosec(buf); - /* The NAME section should always be first. */ + /* + * Check: NAME should always be first, CUSTOM has no roles, + * non-CUSTOM has a conventional order to be followed. + */ - if (SEC_BODY == mdoc->lastnamed && SEC_NAME != sec) - return(vwarn(mdoc, WSECOOO)); + if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed) + return(verr(mdoc, ESECNAME)); if (SEC_CUSTOM == sec) return(1); - - /* Check for repeated or out-of-order sections. */ - if (sec == mdoc->lastnamed) return(vwarn(mdoc, WSECREP)); if (sec < mdoc->lastnamed) return(vwarn(mdoc, WSECOOO)); - /* Check particular section/manual section conventions. */ + /* + * Check particular section/manual conventions. LIBRARY can + * only occur in msec 2, 3 (TODO: are there more of these?). + */ switch (sec) { case (SEC_LIBRARY):