]> git.cameronkatri.com Git - mandoc.git/blobdiff - mdoc_validate.c
implement -h (synopsis only) for preformatted (cat) pages;
[mandoc.git] / mdoc_validate.c
index 15397b926a86ec2b48044d0b7e3c6785cf89322a..c0ed2621f30771841c649e61cdf30e8da067079d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
@@ -67,8 +67,10 @@ static       void     check_text(struct mdoc *, int, int, char *);
 static void     check_argv(struct mdoc *,
                        struct mdoc_node *, struct mdoc_argv *);
 static void     check_args(struct mdoc *, struct mdoc_node *);
 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 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      ebool(POST_ARGS);
 static int      berr_ge1(POST_ARGS);
@@ -87,7 +89,6 @@ static        int      post_bf(POST_ARGS);
 static int      post_bk(POST_ARGS);
 static int      post_bl(POST_ARGS);
 static int      post_bl_block(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_bl_block_tag(POST_ARGS);
 static int      post_bl_head(POST_ARGS);
 static int      post_bx(POST_ARGS);
@@ -99,6 +100,9 @@ static       int      post_en(POST_ARGS);
 static int      post_es(POST_ARGS);
 static int      post_eoln(POST_ARGS);
 static int      post_ex(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_fo(POST_ARGS);
 static int      post_hyph(POST_ARGS);
 static int      post_hyphtext(POST_ARGS);
@@ -114,8 +118,10 @@ static     int      post_par(POST_ARGS);
 static int      post_root(POST_ARGS);
 static int      post_rs(POST_ARGS);
 static int      post_sh(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_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);
 static int      post_st(POST_ARGS);
 static int      post_vt(POST_ARGS);
 static int      pre_an(PRE_ARGS);
@@ -154,10 +160,10 @@ static    const struct valids mdoc_valids[MDOC_MAX] = {
        { NULL, NULL },                         /* Er */
        { NULL, NULL },                         /* Ev */
        { pre_std, post_ex },                   /* Ex */
        { 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, ewarn_ge1 },                    /* Fd */
        { NULL, NULL },                         /* Fl */
-       { NULL, NULL },                         /* Fn */
+       { NULL, post_fn },                      /* Fn */
        { NULL, NULL },                         /* Ft */
        { NULL, NULL },                         /* Ic */
        { NULL, ewarn_eq1 },                    /* In */
        { NULL, NULL },                         /* Ft */
        { NULL, NULL },                         /* Ic */
        { NULL, ewarn_eq1 },                    /* In */
@@ -591,6 +597,7 @@ pre_bl(PRE_ARGS)
                                    mdoc->parse, argv->line,
                                    argv->pos, "Bl -width %s",
                                    argv->value[0]);
                                    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:
                        n->norm->Bl.width = argv->value[0];
                        break;
                case MDOC_Offset:
@@ -605,6 +612,7 @@ pre_bl(PRE_ARGS)
                                    mdoc->parse, argv->line,
                                    argv->pos, "Bl -offset %s",
                                    argv->value[0]);
                                    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:
                        n->norm->Bl.offs = argv->value[0];
                        break;
                default:
@@ -752,6 +760,7 @@ pre_bd(PRE_ARGS)
                                    mdoc->parse, argv->line,
                                    argv->pos, "Bd -offset %s",
                                    argv->value[0]);
                                    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:
                        n->norm->Bd.offs = argv->value[0];
                        break;
                case MDOC_Compact:
@@ -995,12 +1004,61 @@ post_eoln(POST_ARGS)
        return(1);
 }
 
        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);
 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);
 }
 
        return(1);
 }
 
@@ -1281,10 +1339,6 @@ post_bl_block(POST_ARGS)
                if ( ! post_bl_block_tag(mdoc))
                        return(0);
                assert(n->norm->Bl.width);
                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) {
        }
 
        for (ni = n->body->child; ni; ni = ni->next) {
@@ -1324,50 +1378,27 @@ post_bl_block(POST_ARGS)
        return(1);
 }
 
        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;
 {
        size_t            width;
-       int               i;
        enum mdoct        tok;
        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;
                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);
 
        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
 }
 
 static int
@@ -1382,7 +1413,7 @@ post_bl_block_tag(POST_ARGS)
         * 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
         * 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;
         */
 
        sz = 10;
@@ -1643,18 +1674,17 @@ post_root(POST_ARGS)
                mdoc->meta.os = mandoc_strdup("");
        }
 
                mdoc->meta.os = mandoc_strdup("");
        }
 
-       n = mdoc->first;
-       assert(n);
-
        /* Check that we begin with a proper `Sh'. */
 
        /* 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,
                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);
 }
 
        return(1);
 }
@@ -1848,22 +1878,33 @@ post_sh(POST_ARGS)
 
        post_ignpar(mdoc);
 
 
        post_ignpar(mdoc);
 
-       if (MDOC_HEAD == mdoc->last->type)
+       switch (mdoc->last->type) {
+       case MDOC_HEAD:
                return(post_sh_head(mdoc));
                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
 
        return(1);
 }
 
 static int
-post_sh_body(POST_ARGS)
+post_sh_name(POST_ARGS)
 {
        struct mdoc_node *n;
 
 {
        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
        /*
         * Warn if the NAME section doesn't contain the `Nm' and `Nd'
         * macros (can have multiple `Nm' and one `Nd').  Note that the
@@ -1894,6 +1935,89 @@ post_sh_body(POST_ARGS)
        return(1);
 }
 
        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)
 {
 static int
 post_sh_head(POST_ARGS)
 {