-/* $Id: mdoc_validate.c,v 1.245 2014/08/19 17:31:55 schwarze Exp $ */
+/* $Id: mdoc_validate.c,v 1.254 2014/10/30 20:10:02 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
static void check_argv(struct mdoc *,
struct mdoc_node *, struct mdoc_argv *);
static void check_args(struct mdoc *, struct mdoc_node *);
+static int child_an(const struct mdoc_node *);
static enum mdoc_sec a2sec(const char *);
static size_t macro2len(enum mdoct);
+static void rewrite_macro2len(char **);
static int ebool(POST_ARGS);
static int berr_ge1(POST_ARGS);
static int post_bk(POST_ARGS);
static int post_bl(POST_ARGS);
static int post_bl_block(POST_ARGS);
-static int post_bl_block_width(POST_ARGS);
static int post_bl_block_tag(POST_ARGS);
static int post_bl_head(POST_ARGS);
static int post_bx(POST_ARGS);
static int post_es(POST_ARGS);
static int post_eoln(POST_ARGS);
static int post_ex(POST_ARGS);
+static int post_fa(POST_ARGS);
+static int post_fn(POST_ARGS);
+static int post_fname(POST_ARGS);
static int post_fo(POST_ARGS);
static int post_hyph(POST_ARGS);
static int post_hyphtext(POST_ARGS);
static int post_root(POST_ARGS);
static int post_rs(POST_ARGS);
static int post_sh(POST_ARGS);
-static int post_sh_body(POST_ARGS);
static int post_sh_head(POST_ARGS);
+static int post_sh_name(POST_ARGS);
+static int post_sh_see_also(POST_ARGS);
+static int post_sh_authors(POST_ARGS);
static int post_st(POST_ARGS);
static int post_vt(POST_ARGS);
static int pre_an(PRE_ARGS);
{ NULL, NULL }, /* Er */
{ NULL, NULL }, /* Ev */
{ pre_std, post_ex }, /* Ex */
- { NULL, NULL }, /* Fa */
+ { NULL, post_fa }, /* Fa */
{ NULL, ewarn_ge1 }, /* Fd */
{ NULL, NULL }, /* Fl */
- { NULL, NULL }, /* Fn */
+ { NULL, post_fn }, /* Fn */
{ NULL, NULL }, /* Ft */
{ NULL, NULL }, /* Ic */
{ NULL, ewarn_eq1 }, /* In */
mdoc->parse, argv->line,
argv->pos, "Bl -width %s",
argv->value[0]);
+ rewrite_macro2len(argv->value);
n->norm->Bl.width = argv->value[0];
break;
case MDOC_Offset:
mdoc->parse, argv->line,
argv->pos, "Bl -offset %s",
argv->value[0]);
+ rewrite_macro2len(argv->value);
n->norm->Bl.offs = argv->value[0];
break;
default:
mdoc->parse, argv->line,
argv->pos, "Bd -offset %s",
argv->value[0]);
+ rewrite_macro2len(argv->value);
n->norm->Bd.offs = argv->value[0];
break;
case MDOC_Compact:
return(1);
}
+static int
+post_fname(POST_ARGS)
+{
+ const struct mdoc_node *n;
+ const char *cp;
+ size_t pos;
+
+ n = mdoc->last->child;
+ pos = strcspn(n->string, "()");
+ cp = n->string + pos;
+ if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
+ mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
+ n->line, n->pos + pos, n->string);
+ return(1);
+}
+
+static int
+post_fn(POST_ARGS)
+{
+
+ post_fname(mdoc);
+ post_fa(mdoc);
+ return(1);
+}
+
static int
post_fo(POST_ARGS)
{
hwarn_eq1(mdoc);
bwarn_ge1(mdoc);
+ if (mdoc->last->type == MDOC_HEAD && mdoc->last->nchild)
+ post_fname(mdoc);
+ return(1);
+}
+
+static int
+post_fa(POST_ARGS)
+{
+ const struct mdoc_node *n;
+ const char *cp;
+
+ for (n = mdoc->last->child; n != NULL; n = n->next) {
+ for (cp = n->string; *cp != '\0'; cp++) {
+ /* Ignore callbacks and alterations. */
+ if (*cp == '(' || *cp == '{')
+ break;
+ if (*cp != ',')
+ continue;
+ mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
+ n->line, n->pos + (cp - n->string),
+ n->string);
+ break;
+ }
+ }
return(1);
}
if ( ! post_bl_block_tag(mdoc))
return(0);
assert(n->norm->Bl.width);
- } else if (NULL != n->norm->Bl.width) {
- if ( ! post_bl_block_width(mdoc))
- return(0);
- assert(n->norm->Bl.width);
}
for (ni = n->body->child; ni; ni = ni->next) {
return(1);
}
-static int
-post_bl_block_width(POST_ARGS)
+/*
+ * If the argument of -offset or -width is a macro,
+ * replace it with the associated default width.
+ */
+void
+rewrite_macro2len(char **arg)
{
size_t width;
- int i;
enum mdoct tok;
- struct mdoc_node *n;
- char buf[24];
- n = mdoc->last;
-
- /*
- * Calculate the real width of a list from the -width string,
- * which may contain a macro (with a known default width), a
- * literal string, or a scaling width.
- *
- * If the value to -width is a macro, then we re-write it to be
- * the macro's width as set in share/tmac/mdoc/doc-common.
- */
-
- if (0 == strcmp(n->norm->Bl.width, "Ds"))
+ if (*arg == NULL)
+ return;
+ else if ( ! strcmp(*arg, "Ds"))
width = 6;
- else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
- return(1);
+ else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX)
+ return;
else
width = macro2len(tok);
- /* The value already exists: free and reallocate it. */
-
- assert(n->args);
-
- for (i = 0; i < (int)n->args->argc; i++)
- if (MDOC_Width == n->args->argv[i].arg)
- break;
-
- assert(i < (int)n->args->argc);
-
- (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
- free(n->args->argv[i].value[0]);
- n->args->argv[i].value[0] = mandoc_strdup(buf);
-
- /* Set our width! */
- n->norm->Bl.width = n->args->argv[i].value[0];
- return(1);
+ free(*arg);
+ mandoc_asprintf(arg, "%zun", width);
}
static int
* Calculate the -width for a `Bl -tag' list if it hasn't been
* provided. Uses the first head macro. NOTE AGAIN: this is
* ONLY if the -width argument has NOT been provided. See
- * post_bl_block_width() for converting the -width string.
+ * rewrite_macro2len() for converting the -width string.
*/
sz = 10;
mdoc->meta.os = mandoc_strdup("");
}
- n = mdoc->first;
- assert(n);
-
/* Check that we begin with a proper `Sh'. */
- if (NULL == n->child)
- mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse,
- n->line, n->pos, NULL);
- else if (MDOC_Sh != n->child->tok)
+ n = mdoc->first->child;
+ while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
+ n = n->next;
+
+ if (n == NULL)
+ mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
+ else if (n->tok != MDOC_Sh)
mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
- n->child->line, n->child->pos,
- mdoc_macronames[n->child->tok]);
+ n->line, n->pos, mdoc_macronames[n->tok]);
return(1);
}
post_ignpar(mdoc);
- if (MDOC_HEAD == mdoc->last->type)
+ switch (mdoc->last->type) {
+ case MDOC_HEAD:
return(post_sh_head(mdoc));
- if (MDOC_BODY == mdoc->last->type)
- return(post_sh_body(mdoc));
+ case MDOC_BODY:
+ switch (mdoc->lastsec) {
+ case SEC_NAME:
+ return(post_sh_name(mdoc));
+ case SEC_SEE_ALSO:
+ return(post_sh_see_also(mdoc));
+ case SEC_AUTHORS:
+ return(post_sh_authors(mdoc));
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
return(1);
}
static int
-post_sh_body(POST_ARGS)
+post_sh_name(POST_ARGS)
{
struct mdoc_node *n;
- if (SEC_NAME != mdoc->lastsec)
- return(1);
-
/*
* Warn if the NAME section doesn't contain the `Nm' and `Nd'
* macros (can have multiple `Nm' and one `Nd'). Note that the
return(1);
}
+static int
+post_sh_see_also(POST_ARGS)
+{
+ const struct mdoc_node *n;
+ const char *name, *sec;
+ const char *lastname, *lastsec, *lastpunct;
+ int cmp;
+
+ n = mdoc->last->child;
+ lastname = lastsec = lastpunct = NULL;
+ while (n != NULL) {
+ if (n->tok != MDOC_Xr || n->nchild < 2)
+ break;
+
+ /* Process one .Xr node. */
+
+ name = n->child->string;
+ sec = n->child->next->string;
+ if (lastsec != NULL) {
+ if (lastpunct[0] != ',' || lastpunct[1] != '\0')
+ mandoc_vmsg(MANDOCERR_XR_PUNCT,
+ mdoc->parse, n->line, n->pos,
+ "%s before %s(%s)", lastpunct,
+ name, sec);
+ cmp = strcmp(lastsec, sec);
+ if (cmp > 0)
+ mandoc_vmsg(MANDOCERR_XR_ORDER,
+ mdoc->parse, n->line, n->pos,
+ "%s(%s) after %s(%s)", name,
+ sec, lastname, lastsec);
+ else if (cmp == 0 &&
+ strcasecmp(lastname, name) > 0)
+ mandoc_vmsg(MANDOCERR_XR_ORDER,
+ mdoc->parse, n->line, n->pos,
+ "%s after %s", name, lastname);
+ }
+ lastname = name;
+ lastsec = sec;
+
+ /* Process the following node. */
+
+ n = n->next;
+ if (n == NULL)
+ break;
+ if (n->tok == MDOC_Xr) {
+ lastpunct = "none";
+ continue;
+ }
+ if (n->type != MDOC_TEXT)
+ break;
+ for (name = n->string; *name != '\0'; name++)
+ if (isalpha((const unsigned char)*name))
+ return(1);
+ lastpunct = n->string;
+ if (n->next == NULL)
+ mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
+ n->line, n->pos, "%s after %s(%s)",
+ lastpunct, lastname, lastsec);
+ n = n->next;
+ }
+ return(1);
+}
+
+static int
+child_an(const struct mdoc_node *n)
+{
+
+ for (n = n->child; n != NULL; n = n->next)
+ if ((n->tok == MDOC_An && n->nchild) || child_an(n))
+ return(1);
+ return(0);
+}
+
+static int
+post_sh_authors(POST_ARGS)
+{
+
+ if ( ! child_an(mdoc->last))
+ mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
+ mdoc->last->line, mdoc->last->pos, NULL);
+ return(1);
+}
+
static int
post_sh_head(POST_ARGS)
{