Print this page
9718 update mandoc to 1.14.4
   1 /*      $Id: mdoc_validate.c,v 1.352 2017/08/02 13:29:04 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
   5  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
   6  *
   7  * Permission to use, copy, modify, and distribute this software for any
   8  * purpose with or without fee is hereby granted, provided that the above
   9  * copyright notice and this permission notice appear in all copies.
  10  *
  11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18  */
  19 #include "config.h"
  20 
  21 #include <sys/types.h>
  22 #ifndef OSNAME
  23 #include <sys/utsname.h>
  24 #endif


  36 #include "mandoc_xr.h"
  37 #include "roff.h"
  38 #include "mdoc.h"
  39 #include "libmandoc.h"
  40 #include "roff_int.h"
  41 #include "libmdoc.h"
  42 
  43 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
  44 
  45 #define POST_ARGS struct roff_man *mdoc
  46 
  47 enum    check_ineq {
  48         CHECK_LT,
  49         CHECK_GT,
  50         CHECK_EQ
  51 };
  52 
  53 typedef void    (*v_post)(POST_ARGS);
  54 
  55 static  int      build_list(struct roff_man *, int);
  56 static  void     check_text(struct roff_man *, int, int, char *);
  57 static  void     check_argv(struct roff_man *,
  58                         struct roff_node *, struct mdoc_argv *);
  59 static  void     check_args(struct roff_man *, struct roff_node *);


  60 static  void     check_toptext(struct roff_man *, int, int, const char *);
  61 static  int      child_an(const struct roff_node *);
  62 static  size_t          macro2len(enum roff_tok);
  63 static  void     rewrite_macro2len(struct roff_man *, char **);
  64 static  int      similar(const char *, const char *);
  65 
  66 static  void     post_an(POST_ARGS);
  67 static  void     post_an_norm(POST_ARGS);
  68 static  void     post_at(POST_ARGS);
  69 static  void     post_bd(POST_ARGS);
  70 static  void     post_bf(POST_ARGS);
  71 static  void     post_bk(POST_ARGS);
  72 static  void     post_bl(POST_ARGS);
  73 static  void     post_bl_block(POST_ARGS);
  74 static  void     post_bl_head(POST_ARGS);
  75 static  void     post_bl_norm(POST_ARGS);
  76 static  void     post_bx(POST_ARGS);
  77 static  void     post_defaults(POST_ARGS);
  78 static  void     post_display(POST_ARGS);
  79 static  void     post_dd(POST_ARGS);


 152         post_delim_nb,  /* Op */
 153         post_obsolete,  /* Ot */
 154         post_defaults,  /* Pa */
 155         post_rv,        /* Rv */
 156         post_st,        /* St */
 157         post_delim_nb,  /* Va */
 158         post_delim_nb,  /* Vt */
 159         post_xr,        /* Xr */
 160         NULL,           /* %A */
 161         post_hyph,      /* %B */ /* FIXME: can be used outside Rs/Re. */
 162         NULL,           /* %D */
 163         NULL,           /* %I */
 164         NULL,           /* %J */
 165         post_hyph,      /* %N */
 166         post_hyph,      /* %O */
 167         NULL,           /* %P */
 168         post_hyph,      /* %R */
 169         post_hyph,      /* %T */ /* FIXME: can be used outside Rs/Re. */
 170         NULL,           /* %V */
 171         NULL,           /* Ac */
 172         post_delim_nb,  /* Ao */
 173         post_delim_nb,  /* Aq */
 174         post_at,        /* At */
 175         NULL,           /* Bc */
 176         post_bf,        /* Bf */
 177         post_delim_nb,  /* Bo */
 178         NULL,           /* Bq */
 179         post_xx,        /* Bsx */
 180         post_bx,        /* Bx */
 181         post_obsolete,  /* Db */
 182         NULL,           /* Dc */
 183         NULL,           /* Do */
 184         NULL,           /* Dq */
 185         NULL,           /* Ec */
 186         NULL,           /* Ef */
 187         post_delim_nb,  /* Em */
 188         NULL,           /* Eo */
 189         post_xx,        /* Fx */
 190         post_delim_nb,  /* Ms */
 191         NULL,           /* No */
 192         post_ns,        /* Ns */
 193         post_xx,        /* Nx */
 194         post_xx,        /* Ox */
 195         NULL,           /* Pc */
 196         NULL,           /* Pf */
 197         post_delim_nb,  /* Po */
 198         post_delim_nb,  /* Pq */
 199         NULL,           /* Qc */
 200         post_delim_nb,  /* Ql */
 201         post_delim_nb,  /* Qo */
 202         post_delim_nb,  /* Qq */
 203         NULL,           /* Re */
 204         post_rs,        /* Rs */
 205         NULL,           /* Sc */
 206         post_delim_nb,  /* So */
 207         post_delim_nb,  /* Sq */
 208         post_sm,        /* Sm */
 209         post_sx,        /* Sx */
 210         post_delim_nb,  /* Sy */
 211         post_useless,   /* Tn */
 212         post_xx,        /* Ux */
 213         NULL,           /* Xc */
 214         NULL,           /* Xo */
 215         post_fo,        /* Fo */
 216         NULL,           /* Fc */
 217         post_delim_nb,  /* Oo */
 218         NULL,           /* Oc */
 219         post_bk,        /* Bk */
 220         NULL,           /* Ek */
 221         post_eoln,      /* Bt */
 222         post_obsolete,  /* Hf */
 223         post_obsolete,  /* Fr */
 224         post_eoln,      /* Ud */
 225         post_lb,        /* Lb */
 226         post_par,       /* Lp */
 227         post_delim_nb,  /* Lk */
 228         post_defaults,  /* Mt */
 229         post_delim_nb,  /* Brq */
 230         post_delim_nb,  /* Bro */
 231         NULL,           /* Brc */
 232         NULL,           /* %C */
 233         post_es,        /* Es */
 234         post_en,        /* En */
 235         post_xx,        /* Dx */
 236         NULL,           /* %Q */
 237         NULL,           /* %U */
 238         NULL,           /* Ta */
 239 };
 240 static  const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd;
 241 
 242 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
 243 
 244 static  const enum roff_tok rsord[RSORD_MAX] = {
 245         MDOC__A,
 246         MDOC__T,
 247         MDOC__B,
 248         MDOC__I,
 249         MDOC__J,
 250         MDOC__R,


 271         "FILES",
 272         "EXIT STATUS",
 273         "EXAMPLES",
 274         "DIAGNOSTICS",
 275         "COMPATIBILITY",
 276         "ERRORS",
 277         "SEE ALSO",
 278         "STANDARDS",
 279         "HISTORY",
 280         "AUTHORS",
 281         "CAVEATS",
 282         "BUGS",
 283         "SECURITY CONSIDERATIONS",
 284         NULL
 285 };
 286 
 287 
 288 void
 289 mdoc_node_validate(struct roff_man *mdoc)
 290 {
 291         struct roff_node *n;
 292         const v_post *p;
 293 
 294         n = mdoc->last;
 295         mdoc->last = mdoc->last->child;
 296         while (mdoc->last != NULL) {
 297                 mdoc_node_validate(mdoc);
 298                 if (mdoc->last == n)
 299                         mdoc->last = mdoc->last->child;
 300                 else
 301                         mdoc->last = mdoc->last->next;
 302         }
 303 
 304         mdoc->last = n;
 305         mdoc->next = ROFF_NEXT_SIBLING;
 306         switch (n->type) {
 307         case ROFFT_TEXT:

 308                 if (n->sec != SEC_SYNOPSIS ||
 309                     (n->parent->tok != MDOC_Cd && n->parent->tok != MDOC_Fd))
 310                         check_text(mdoc, n->line, n->pos, n->string);
 311                 if (n->parent->tok == MDOC_It ||
 312                     (n->parent->type == ROFFT_BODY &&
 313                      (n->parent->tok == MDOC_Sh ||
 314                       n->parent->tok == MDOC_Ss)))




 315                         check_toptext(mdoc, n->line, n->pos, n->string);
 316                 break;

 317         case ROFFT_EQN:
 318         case ROFFT_TBL:
 319                 break;
 320         case ROFFT_ROOT:
 321                 post_root(mdoc);
 322                 break;
 323         default:
 324                 check_args(mdoc, mdoc->last);
 325 
 326                 /*
 327                  * Closing delimiters are not special at the
 328                  * beginning of a block, opening delimiters
 329                  * are not special at the end.
 330                  */
 331 
 332                 if (n->child != NULL)
 333                         n->child->flags &= ~NODE_DELIMC;
 334                 if (n->last != NULL)
 335                         n->last->flags &= ~NODE_DELIMO;
 336 


 378         int              i;
 379 
 380         for (i = 0; i < (int)v->sz; i++)
 381                 check_text(mdoc, v->line, v->pos, v->value[i]);
 382 }
 383 
 384 static void
 385 check_text(struct roff_man *mdoc, int ln, int pos, char *p)
 386 {
 387         char            *cp;
 388 
 389         if (MDOC_LITERAL & mdoc->flags)
 390                 return;
 391 
 392         for (cp = p; NULL != (p = strchr(p, '\t')); p++)
 393                 mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
 394                     ln, pos + (int)(p - cp), NULL);
 395 }
 396 
 397 static void



















































 398 check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
 399 {
 400         const char      *cp, *cpr;
 401 
 402         if (*p == '\0')
 403                 return;
 404 
 405         if ((cp = strstr(p, "OpenBSD")) != NULL)
 406                 mandoc_msg(MANDOCERR_BX, mdoc->parse,
 407                     ln, pos + (cp - p), "Ox");
 408         if ((cp = strstr(p, "NetBSD")) != NULL)
 409                 mandoc_msg(MANDOCERR_BX, mdoc->parse,
 410                     ln, pos + (cp - p), "Nx");
 411         if ((cp = strstr(p, "FreeBSD")) != NULL)
 412                 mandoc_msg(MANDOCERR_BX, mdoc->parse,
 413                     ln, pos + (cp - p), "Fx");
 414         if ((cp = strstr(p, "DragonFly")) != NULL)
 415                 mandoc_msg(MANDOCERR_BX, mdoc->parse,
 416                     ln, pos + (cp - p), "Dx");
 417 


 513                         return;
 514                 break;
 515         case ']':
 516                 for (cp = lc; cp >= nch->string; cp--)
 517                         if (*cp == '[')
 518                                 return;
 519                 break;
 520         case '|':
 521                 if (lc == nch->string + 1 && lc[-1] == '|')
 522                         return;
 523         default:
 524                 break;
 525         }
 526 
 527         /* Exactly two non-alphanumeric bytes. */
 528         if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
 529                 return;
 530 
 531         /* At least three alphabetic words with a sentence ending. */
 532         if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
 533             tok == MDOC_Li || tok == MDOC_Po || tok == MDOC_Pq ||
 534             tok == MDOC_Sy)) {
 535                 nw = 0;
 536                 for (cp = lc - 1; cp >= nch->string; cp--) {
 537                         if (*cp == ' ') {
 538                                 nw++;
 539                                 if (cp > nch->string && cp[-1] == ',')
 540                                         cp--;
 541                         } else if (isalpha((unsigned int)*cp)) {
 542                                 if (nw > 1)
 543                                         return;
 544                         } else
 545                                 break;
 546                 }
 547         }
 548 
 549         mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse,
 550             nch->line, nch->pos + (lc - nch->string),
 551             "%s%s %s", roff_name[tok],
 552             nch == mdoc->last->child ? "" : " ...", nch->string);
 553 }
 554 


 930 
 931         post_delim_nb(mdoc);
 932 
 933         n = mdoc->last;
 934         assert(n->child->type == ROFFT_TEXT);
 935         mdoc->next = ROFF_NEXT_CHILD;
 936 
 937         if ((p = mdoc_a2lib(n->child->string)) != NULL) {
 938                 n->child->flags |= NODE_NOPRT;
 939                 roff_word_alloc(mdoc, n->line, n->pos, p);
 940                 mdoc->last->flags = NODE_NOSRC;
 941                 mdoc->last = n;
 942                 return;
 943         }
 944 
 945         mandoc_vmsg(MANDOCERR_LB_BAD, mdoc->parse, n->child->line,
 946             n->child->pos, "Lb %s", n->child->string);
 947 
 948         roff_word_alloc(mdoc, n->line, n->pos, "library");
 949         mdoc->last->flags = NODE_NOSRC;
 950         roff_word_alloc(mdoc, n->line, n->pos, "\\(Lq");
 951         mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
 952         mdoc->last = mdoc->last->next;
 953         roff_word_alloc(mdoc, n->line, n->pos, "\\(Rq");
 954         mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
 955         mdoc->last = n;
 956 }
 957 
 958 static void
 959 post_rv(POST_ARGS)
 960 {
 961         struct roff_node        *n;
 962         int                      ic;
 963 
 964         post_std(mdoc);
 965 
 966         n = mdoc->last;
 967         mdoc->next = ROFF_NEXT_CHILD;
 968         if (n->child != NULL) {
 969                 roff_word_alloc(mdoc, n->line, n->pos, "The");
 970                 mdoc->last->flags |= NODE_NOSRC;
 971                 ic = build_list(mdoc, MDOC_Fn);
 972                 roff_word_alloc(mdoc, n->line, n->pos,
 973                     ic > 1 ? "functions return" : "function returns");


1897 
1898         if (mdoc->meta.vol == NULL)
1899                 mdoc->meta.vol = mandoc_strdup("LOCAL");
1900 
1901         if (mdoc->meta.os == NULL) {
1902                 mandoc_msg(MANDOCERR_OS_MISSING,
1903                     mdoc->parse, 0, 0, NULL);
1904                 mdoc->meta.os = mandoc_strdup("");
1905         } else if (mdoc->meta.os_e &&
1906             (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1907                 mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0,
1908                     mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1909                     "(OpenBSD)" : "(NetBSD)");
1910 
1911         if (mdoc->meta.arch != NULL &&
1912             (arch = arches[mdoc->meta.os_e]) != NULL) {
1913                 while (*arch != NULL && strcmp(*arch, mdoc->meta.arch))
1914                         arch++;
1915                 if (*arch == NULL) {
1916                         n = mdoc->first->child;
1917                         while (n->tok != MDOC_Dt)



1918                                 n = n->next;
1919                         n = n->child->next->next;
1920                         mandoc_vmsg(MANDOCERR_ARCH_BAD,
1921                             mdoc->parse, n->line, n->pos,
1922                             "Dt ... %s %s", mdoc->meta.arch,
1923                             mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1924                             "(OpenBSD)" : "(NetBSD)");
1925                 }
1926         }
1927 
1928         /* Check that we begin with a proper `Sh'. */
1929 
1930         n = mdoc->first->child;
1931         while (n != NULL && n->tok >= MDOC_Dd &&
1932             mdoc_macros[n->tok].flags & MDOC_PROLOGUE)


1933                 n = n->next;
1934 
1935         if (n == NULL)
1936                 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1937         else if (n->tok != MDOC_Sh)
1938                 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1939                     n->line, n->pos, roff_name[n->tok]);
1940 }
1941 
1942 static void
1943 post_rs(POST_ARGS)
1944 {
1945         struct roff_node *np, *nch, *next, *prev;
1946         int               i, j;
1947 
1948         np = mdoc->last;
1949 
1950         if (np->type != ROFFT_BODY)
1951                 return;
1952 


   1 /*      $Id: mdoc_validate.c,v 1.360 2018/08/01 16:00:58 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org>
   5  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
   6  *
   7  * Permission to use, copy, modify, and distribute this software for any
   8  * purpose with or without fee is hereby granted, provided that the above
   9  * copyright notice and this permission notice appear in all copies.
  10  *
  11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18  */
  19 #include "config.h"
  20 
  21 #include <sys/types.h>
  22 #ifndef OSNAME
  23 #include <sys/utsname.h>
  24 #endif


  36 #include "mandoc_xr.h"
  37 #include "roff.h"
  38 #include "mdoc.h"
  39 #include "libmandoc.h"
  40 #include "roff_int.h"
  41 #include "libmdoc.h"
  42 
  43 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
  44 
  45 #define POST_ARGS struct roff_man *mdoc
  46 
  47 enum    check_ineq {
  48         CHECK_LT,
  49         CHECK_GT,
  50         CHECK_EQ
  51 };
  52 
  53 typedef void    (*v_post)(POST_ARGS);
  54 
  55 static  int      build_list(struct roff_man *, int);

  56 static  void     check_argv(struct roff_man *,
  57                         struct roff_node *, struct mdoc_argv *);
  58 static  void     check_args(struct roff_man *, struct roff_node *);
  59 static  void     check_text(struct roff_man *, int, int, char *);
  60 static  void     check_text_em(struct roff_man *, int, int, char *);
  61 static  void     check_toptext(struct roff_man *, int, int, const char *);
  62 static  int      child_an(const struct roff_node *);
  63 static  size_t          macro2len(enum roff_tok);
  64 static  void     rewrite_macro2len(struct roff_man *, char **);
  65 static  int      similar(const char *, const char *);
  66 
  67 static  void     post_an(POST_ARGS);
  68 static  void     post_an_norm(POST_ARGS);
  69 static  void     post_at(POST_ARGS);
  70 static  void     post_bd(POST_ARGS);
  71 static  void     post_bf(POST_ARGS);
  72 static  void     post_bk(POST_ARGS);
  73 static  void     post_bl(POST_ARGS);
  74 static  void     post_bl_block(POST_ARGS);
  75 static  void     post_bl_head(POST_ARGS);
  76 static  void     post_bl_norm(POST_ARGS);
  77 static  void     post_bx(POST_ARGS);
  78 static  void     post_defaults(POST_ARGS);
  79 static  void     post_display(POST_ARGS);
  80 static  void     post_dd(POST_ARGS);


 153         post_delim_nb,  /* Op */
 154         post_obsolete,  /* Ot */
 155         post_defaults,  /* Pa */
 156         post_rv,        /* Rv */
 157         post_st,        /* St */
 158         post_delim_nb,  /* Va */
 159         post_delim_nb,  /* Vt */
 160         post_xr,        /* Xr */
 161         NULL,           /* %A */
 162         post_hyph,      /* %B */ /* FIXME: can be used outside Rs/Re. */
 163         NULL,           /* %D */
 164         NULL,           /* %I */
 165         NULL,           /* %J */
 166         post_hyph,      /* %N */
 167         post_hyph,      /* %O */
 168         NULL,           /* %P */
 169         post_hyph,      /* %R */
 170         post_hyph,      /* %T */ /* FIXME: can be used outside Rs/Re. */
 171         NULL,           /* %V */
 172         NULL,           /* Ac */
 173         NULL,           /* Ao */
 174         post_delim_nb,  /* Aq */
 175         post_at,        /* At */
 176         NULL,           /* Bc */
 177         post_bf,        /* Bf */
 178         NULL,           /* Bo */
 179         NULL,           /* Bq */
 180         post_xx,        /* Bsx */
 181         post_bx,        /* Bx */
 182         post_obsolete,  /* Db */
 183         NULL,           /* Dc */
 184         NULL,           /* Do */
 185         NULL,           /* Dq */
 186         NULL,           /* Ec */
 187         NULL,           /* Ef */
 188         post_delim_nb,  /* Em */
 189         NULL,           /* Eo */
 190         post_xx,        /* Fx */
 191         post_delim_nb,  /* Ms */
 192         NULL,           /* No */
 193         post_ns,        /* Ns */
 194         post_xx,        /* Nx */
 195         post_xx,        /* Ox */
 196         NULL,           /* Pc */
 197         NULL,           /* Pf */
 198         NULL,           /* Po */
 199         post_delim_nb,  /* Pq */
 200         NULL,           /* Qc */
 201         post_delim_nb,  /* Ql */
 202         NULL,           /* Qo */
 203         post_delim_nb,  /* Qq */
 204         NULL,           /* Re */
 205         post_rs,        /* Rs */
 206         NULL,           /* Sc */
 207         NULL,           /* So */
 208         post_delim_nb,  /* Sq */
 209         post_sm,        /* Sm */
 210         post_sx,        /* Sx */
 211         post_delim_nb,  /* Sy */
 212         post_useless,   /* Tn */
 213         post_xx,        /* Ux */
 214         NULL,           /* Xc */
 215         NULL,           /* Xo */
 216         post_fo,        /* Fo */
 217         NULL,           /* Fc */
 218         NULL,           /* Oo */
 219         NULL,           /* Oc */
 220         post_bk,        /* Bk */
 221         NULL,           /* Ek */
 222         post_eoln,      /* Bt */
 223         post_obsolete,  /* Hf */
 224         post_obsolete,  /* Fr */
 225         post_eoln,      /* Ud */
 226         post_lb,        /* Lb */
 227         post_par,       /* Lp */
 228         post_delim_nb,  /* Lk */
 229         post_defaults,  /* Mt */
 230         post_delim_nb,  /* Brq */
 231         NULL,           /* Bro */
 232         NULL,           /* Brc */
 233         NULL,           /* %C */
 234         post_es,        /* Es */
 235         post_en,        /* En */
 236         post_xx,        /* Dx */
 237         NULL,           /* %Q */
 238         NULL,           /* %U */
 239         NULL,           /* Ta */
 240 };
 241 static  const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd;
 242 
 243 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
 244 
 245 static  const enum roff_tok rsord[RSORD_MAX] = {
 246         MDOC__A,
 247         MDOC__T,
 248         MDOC__B,
 249         MDOC__I,
 250         MDOC__J,
 251         MDOC__R,


 272         "FILES",
 273         "EXIT STATUS",
 274         "EXAMPLES",
 275         "DIAGNOSTICS",
 276         "COMPATIBILITY",
 277         "ERRORS",
 278         "SEE ALSO",
 279         "STANDARDS",
 280         "HISTORY",
 281         "AUTHORS",
 282         "CAVEATS",
 283         "BUGS",
 284         "SECURITY CONSIDERATIONS",
 285         NULL
 286 };
 287 
 288 
 289 void
 290 mdoc_node_validate(struct roff_man *mdoc)
 291 {
 292         struct roff_node *n, *np;
 293         const v_post *p;
 294 
 295         n = mdoc->last;
 296         mdoc->last = mdoc->last->child;
 297         while (mdoc->last != NULL) {
 298                 mdoc_node_validate(mdoc);
 299                 if (mdoc->last == n)
 300                         mdoc->last = mdoc->last->child;
 301                 else
 302                         mdoc->last = mdoc->last->next;
 303         }
 304 
 305         mdoc->last = n;
 306         mdoc->next = ROFF_NEXT_SIBLING;
 307         switch (n->type) {
 308         case ROFFT_TEXT:
 309                 np = n->parent;
 310                 if (n->sec != SEC_SYNOPSIS ||
 311                     (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
 312                         check_text(mdoc, n->line, n->pos, n->string);
 313                 if (np->tok != MDOC_Ql && np->tok != MDOC_Dl &&
 314                     (np->tok != MDOC_Bd ||
 315                      (mdoc->flags & MDOC_LITERAL) == 0) &&
 316                     (np->tok != MDOC_It || np->type != ROFFT_HEAD ||
 317                      np->parent->parent->norm->Bl.type != LIST_diag))
 318                         check_text_em(mdoc, n->line, n->pos, n->string);
 319                 if (np->tok == MDOC_It || (np->type == ROFFT_BODY &&
 320                     (np->tok == MDOC_Sh || np->tok == MDOC_Ss)))
 321                         check_toptext(mdoc, n->line, n->pos, n->string);
 322                 break;
 323         case ROFFT_COMMENT:
 324         case ROFFT_EQN:
 325         case ROFFT_TBL:
 326                 break;
 327         case ROFFT_ROOT:
 328                 post_root(mdoc);
 329                 break;
 330         default:
 331                 check_args(mdoc, mdoc->last);
 332 
 333                 /*
 334                  * Closing delimiters are not special at the
 335                  * beginning of a block, opening delimiters
 336                  * are not special at the end.
 337                  */
 338 
 339                 if (n->child != NULL)
 340                         n->child->flags &= ~NODE_DELIMC;
 341                 if (n->last != NULL)
 342                         n->last->flags &= ~NODE_DELIMO;
 343 


 385         int              i;
 386 
 387         for (i = 0; i < (int)v->sz; i++)
 388                 check_text(mdoc, v->line, v->pos, v->value[i]);
 389 }
 390 
 391 static void
 392 check_text(struct roff_man *mdoc, int ln, int pos, char *p)
 393 {
 394         char            *cp;
 395 
 396         if (MDOC_LITERAL & mdoc->flags)
 397                 return;
 398 
 399         for (cp = p; NULL != (p = strchr(p, '\t')); p++)
 400                 mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
 401                     ln, pos + (int)(p - cp), NULL);
 402 }
 403 
 404 static void
 405 check_text_em(struct roff_man *mdoc, int ln, int pos, char *p)
 406 {
 407         const struct roff_node  *np, *nn;
 408         char                    *cp;
 409 
 410         np = mdoc->last->prev;
 411         nn = mdoc->last->next;
 412 
 413         /* Look for em-dashes wrongly encoded as "--". */
 414 
 415         for (cp = p; *cp != '\0'; cp++) {
 416                 if (cp[0] != '-' || cp[1] != '-')
 417                         continue;
 418                 cp++;
 419 
 420                 /* Skip input sequences of more than two '-'. */
 421 
 422                 if (cp[1] == '-') {
 423                         while (cp[1] == '-')
 424                                 cp++;
 425                         continue;
 426                 }
 427 
 428                 /* Skip "--" directly attached to something else. */
 429 
 430                 if ((cp - p > 1 && cp[-2] != ' ') ||
 431                     (cp[1] != '\0' && cp[1] != ' '))
 432                         continue;
 433 
 434                 /* Require a letter right before or right afterwards. */
 435 
 436                 if ((cp - p > 2 ?
 437                      isalpha((unsigned char)cp[-3]) :
 438                      np != NULL &&
 439                      np->type == ROFFT_TEXT &&
 440                      *np->string != '\0' &&
 441                      isalpha((unsigned char)np->string[
 442                        strlen(np->string) - 1])) ||
 443                     (cp[1] != '\0' && cp[2] != '\0' ?
 444                      isalpha((unsigned char)cp[2]) :
 445                      nn != NULL &&
 446                      nn->type == ROFFT_TEXT &&
 447                      isalpha((unsigned char)*nn->string))) {
 448                         mandoc_msg(MANDOCERR_DASHDASH, mdoc->parse,
 449                             ln, pos + (int)(cp - p) - 1, NULL);
 450                         break;
 451                 }
 452         }
 453 }
 454 
 455 static void
 456 check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
 457 {
 458         const char      *cp, *cpr;
 459 
 460         if (*p == '\0')
 461                 return;
 462 
 463         if ((cp = strstr(p, "OpenBSD")) != NULL)
 464                 mandoc_msg(MANDOCERR_BX, mdoc->parse,
 465                     ln, pos + (cp - p), "Ox");
 466         if ((cp = strstr(p, "NetBSD")) != NULL)
 467                 mandoc_msg(MANDOCERR_BX, mdoc->parse,
 468                     ln, pos + (cp - p), "Nx");
 469         if ((cp = strstr(p, "FreeBSD")) != NULL)
 470                 mandoc_msg(MANDOCERR_BX, mdoc->parse,
 471                     ln, pos + (cp - p), "Fx");
 472         if ((cp = strstr(p, "DragonFly")) != NULL)
 473                 mandoc_msg(MANDOCERR_BX, mdoc->parse,
 474                     ln, pos + (cp - p), "Dx");
 475 


 571                         return;
 572                 break;
 573         case ']':
 574                 for (cp = lc; cp >= nch->string; cp--)
 575                         if (*cp == '[')
 576                                 return;
 577                 break;
 578         case '|':
 579                 if (lc == nch->string + 1 && lc[-1] == '|')
 580                         return;
 581         default:
 582                 break;
 583         }
 584 
 585         /* Exactly two non-alphanumeric bytes. */
 586         if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
 587                 return;
 588 
 589         /* At least three alphabetic words with a sentence ending. */
 590         if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
 591             tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {

 592                 nw = 0;
 593                 for (cp = lc - 1; cp >= nch->string; cp--) {
 594                         if (*cp == ' ') {
 595                                 nw++;
 596                                 if (cp > nch->string && cp[-1] == ',')
 597                                         cp--;
 598                         } else if (isalpha((unsigned int)*cp)) {
 599                                 if (nw > 1)
 600                                         return;
 601                         } else
 602                                 break;
 603                 }
 604         }
 605 
 606         mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse,
 607             nch->line, nch->pos + (lc - nch->string),
 608             "%s%s %s", roff_name[tok],
 609             nch == mdoc->last->child ? "" : " ...", nch->string);
 610 }
 611 


 987 
 988         post_delim_nb(mdoc);
 989 
 990         n = mdoc->last;
 991         assert(n->child->type == ROFFT_TEXT);
 992         mdoc->next = ROFF_NEXT_CHILD;
 993 
 994         if ((p = mdoc_a2lib(n->child->string)) != NULL) {
 995                 n->child->flags |= NODE_NOPRT;
 996                 roff_word_alloc(mdoc, n->line, n->pos, p);
 997                 mdoc->last->flags = NODE_NOSRC;
 998                 mdoc->last = n;
 999                 return;
1000         }
1001 
1002         mandoc_vmsg(MANDOCERR_LB_BAD, mdoc->parse, n->child->line,
1003             n->child->pos, "Lb %s", n->child->string);
1004 
1005         roff_word_alloc(mdoc, n->line, n->pos, "library");
1006         mdoc->last->flags = NODE_NOSRC;
1007         roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
1008         mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
1009         mdoc->last = mdoc->last->next;
1010         roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
1011         mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
1012         mdoc->last = n;
1013 }
1014 
1015 static void
1016 post_rv(POST_ARGS)
1017 {
1018         struct roff_node        *n;
1019         int                      ic;
1020 
1021         post_std(mdoc);
1022 
1023         n = mdoc->last;
1024         mdoc->next = ROFF_NEXT_CHILD;
1025         if (n->child != NULL) {
1026                 roff_word_alloc(mdoc, n->line, n->pos, "The");
1027                 mdoc->last->flags |= NODE_NOSRC;
1028                 ic = build_list(mdoc, MDOC_Fn);
1029                 roff_word_alloc(mdoc, n->line, n->pos,
1030                     ic > 1 ? "functions return" : "function returns");


1954 
1955         if (mdoc->meta.vol == NULL)
1956                 mdoc->meta.vol = mandoc_strdup("LOCAL");
1957 
1958         if (mdoc->meta.os == NULL) {
1959                 mandoc_msg(MANDOCERR_OS_MISSING,
1960                     mdoc->parse, 0, 0, NULL);
1961                 mdoc->meta.os = mandoc_strdup("");
1962         } else if (mdoc->meta.os_e &&
1963             (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1964                 mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0,
1965                     mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1966                     "(OpenBSD)" : "(NetBSD)");
1967 
1968         if (mdoc->meta.arch != NULL &&
1969             (arch = arches[mdoc->meta.os_e]) != NULL) {
1970                 while (*arch != NULL && strcmp(*arch, mdoc->meta.arch))
1971                         arch++;
1972                 if (*arch == NULL) {
1973                         n = mdoc->first->child;
1974                         while (n->tok != MDOC_Dt ||
1975                             n->child == NULL ||
1976                             n->child->next == NULL ||
1977                             n->child->next->next == NULL)
1978                                 n = n->next;
1979                         n = n->child->next->next;
1980                         mandoc_vmsg(MANDOCERR_ARCH_BAD,
1981                             mdoc->parse, n->line, n->pos,
1982                             "Dt ... %s %s", mdoc->meta.arch,
1983                             mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1984                             "(OpenBSD)" : "(NetBSD)");
1985                 }
1986         }
1987 
1988         /* Check that we begin with a proper `Sh'. */
1989 
1990         n = mdoc->first->child;
1991         while (n != NULL &&
1992             (n->type == ROFFT_COMMENT ||
1993              (n->tok >= MDOC_Dd &&
1994               mdoc_macros[n->tok].flags & MDOC_PROLOGUE)))
1995                 n = n->next;
1996 
1997         if (n == NULL)
1998                 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1999         else if (n->tok != MDOC_Sh)
2000                 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
2001                     n->line, n->pos, roff_name[n->tok]);
2002 }
2003 
2004 static void
2005 post_rs(POST_ARGS)
2006 {
2007         struct roff_node *np, *nch, *next, *prev;
2008         int               i, j;
2009 
2010         np = mdoc->last;
2011 
2012         if (np->type != ROFFT_BODY)
2013                 return;
2014