116Salm/* $OpenBSD: mdoc_validate.c,v 1.306 2022/06/08 16:29:12 schwarze Exp $ */ 2/* 3 * Copyright (c) 2010-2021 Ingo Schwarze <schwarze@openbsd.org> 4 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 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 * Validation module for mdoc(7) syntax trees used by mandoc(1). 20 */ 21#include <sys/types.h> 22#ifndef OSNAME 23#include <sys/utsname.h> 24#endif 25 26#include <assert.h> 27#include <ctype.h> 28#include <limits.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <time.h> 33 34#include "mandoc_aux.h" 35#include "mandoc.h" 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#include "tag.h" 43 44/* FIXME: .Bl -diag can't have non-text children in HEAD. */ 45 46#define POST_ARGS struct roff_man *mdoc 47 48enum check_ineq { 49 CHECK_LT, 50 CHECK_GT, 51 CHECK_EQ 52}; 53 54typedef void (*v_post)(POST_ARGS); 55 56static int build_list(struct roff_man *, int); 57static void check_argv(struct roff_man *, 58 struct roff_node *, struct mdoc_argv *); 59static void check_args(struct roff_man *, struct roff_node *); 60static void check_text(struct roff_man *, int, int, char *); 61static void check_text_em(struct roff_man *, int, int, char *); 62static void check_toptext(struct roff_man *, int, int, const char *); 63static int child_an(const struct roff_node *); 64static size_t macro2len(enum roff_tok); 65static void rewrite_macro2len(struct roff_man *, char **); 66static int similar(const char *, const char *); 67 68static void post_abort(POST_ARGS) __attribute__((__noreturn__)); 69static void post_an(POST_ARGS); 70static void post_an_norm(POST_ARGS); 71static void post_at(POST_ARGS); 72static void post_bd(POST_ARGS); 73static void post_bf(POST_ARGS); 74static void post_bk(POST_ARGS); 75static void post_bl(POST_ARGS); 76static void post_bl_block(POST_ARGS); 77static void post_bl_head(POST_ARGS); 78static void post_bl_norm(POST_ARGS); 79static void post_bx(POST_ARGS); 80static void post_defaults(POST_ARGS); 81static void post_display(POST_ARGS); 82static void post_dd(POST_ARGS); 83static void post_delim(POST_ARGS); 84static void post_delim_nb(POST_ARGS); 85static void post_dt(POST_ARGS); 86static void post_em(POST_ARGS); 87static void post_en(POST_ARGS); 88static void post_er(POST_ARGS); 89static void post_es(POST_ARGS); 90static void post_eoln(POST_ARGS); 91static void post_ex(POST_ARGS); 92static void post_fa(POST_ARGS); 93static void post_fl(POST_ARGS); 94static void post_fn(POST_ARGS); 95static void post_fname(POST_ARGS); 96static void post_fo(POST_ARGS); 97static void post_hyph(POST_ARGS); 98static void post_it(POST_ARGS); 99static void post_lb(POST_ARGS); 100static void post_nd(POST_ARGS); 101static void post_nm(POST_ARGS); 102static void post_ns(POST_ARGS); 103static void post_obsolete(POST_ARGS); 104static void post_os(POST_ARGS); 105static void post_par(POST_ARGS); 106static void post_prevpar(POST_ARGS); 107static void post_root(POST_ARGS); 108static void post_rs(POST_ARGS); 109static void post_rv(POST_ARGS); 110static void post_section(POST_ARGS); 111static void post_sh(POST_ARGS); 112static void post_sh_head(POST_ARGS); 113static void post_sh_name(POST_ARGS); 114static void post_sh_see_also(POST_ARGS); 115static void post_sh_authors(POST_ARGS); 116static void post_sm(POST_ARGS); 117static void post_st(POST_ARGS); 118static void post_std(POST_ARGS); 119static void post_sx(POST_ARGS); 120static void post_tag(POST_ARGS); 121static void post_tg(POST_ARGS); 122static void post_useless(POST_ARGS); 123static void post_xr(POST_ARGS); 124static void post_xx(POST_ARGS); 125 126static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { 127 post_dd, /* Dd */ 128 post_dt, /* Dt */ 129 post_os, /* Os */ 130 post_sh, /* Sh */ 131 post_section, /* Ss */ 132 post_par, /* Pp */ 133 post_display, /* D1 */ 134 post_display, /* Dl */ 135 post_display, /* Bd */ 136 NULL, /* Ed */ 137 post_bl, /* Bl */ 138 NULL, /* El */ 139 post_it, /* It */ 140 post_delim_nb, /* Ad */ 141 post_an, /* An */ 142 NULL, /* Ap */ 143 post_defaults, /* Ar */ 144 NULL, /* Cd */ 145 post_tag, /* Cm */ 146 post_tag, /* Dv */ 147 post_er, /* Er */ 148 post_tag, /* Ev */ 149 post_ex, /* Ex */ 150 post_fa, /* Fa */ 151 NULL, /* Fd */ 152 post_fl, /* Fl */ 153 post_fn, /* Fn */ 154 post_delim_nb, /* Ft */ 155 post_tag, /* Ic */ 156 post_delim_nb, /* In */ 157 post_tag, /* Li */ 158 post_nd, /* Nd */ 159 post_nm, /* Nm */ 160 post_delim_nb, /* Op */ 161 post_abort, /* Ot */ 162 post_defaults, /* Pa */ 163 post_rv, /* Rv */ 164 post_st, /* St */ 165 post_tag, /* Va */ 166 post_delim_nb, /* Vt */ 167 post_xr, /* Xr */ 168 NULL, /* %A */ 169 post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */ 170 NULL, /* %D */ 171 NULL, /* %I */ 172 NULL, /* %J */ 173 post_hyph, /* %N */ 174 post_hyph, /* %O */ 175 NULL, /* %P */ 176 post_hyph, /* %R */ 177 post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */ 178 NULL, /* %V */ 179 NULL, /* Ac */ 180 NULL, /* Ao */ 181 post_delim_nb, /* Aq */ 182 post_at, /* At */ 183 NULL, /* Bc */ 184 post_bf, /* Bf */ 185 NULL, /* Bo */ 186 NULL, /* Bq */ 187 post_xx, /* Bsx */ 188 post_bx, /* Bx */ 189 post_obsolete, /* Db */ 190 NULL, /* Dc */ 191 NULL, /* Do */ 192 NULL, /* Dq */ 193 NULL, /* Ec */ 194 NULL, /* Ef */ 195 post_em, /* Em */ 196 NULL, /* Eo */ 197 post_xx, /* Fx */ 198 post_tag, /* Ms */ 199 post_tag, /* No */ 200 post_ns, /* Ns */ 201 post_xx, /* Nx */ 202 post_xx, /* Ox */ 203 NULL, /* Pc */ 204 NULL, /* Pf */ 205 NULL, /* Po */ 206 post_delim_nb, /* Pq */ 207 NULL, /* Qc */ 208 post_delim_nb, /* Ql */ 209 NULL, /* Qo */ 210 post_delim_nb, /* Qq */ 211 NULL, /* Re */ 212 post_rs, /* Rs */ 213 NULL, /* Sc */ 214 NULL, /* So */ 215 post_delim_nb, /* Sq */ 216 post_sm, /* Sm */ 217 post_sx, /* Sx */ 218 post_em, /* Sy */ 219 post_useless, /* Tn */ 220 post_xx, /* Ux */ 221 NULL, /* Xc */ 222 NULL, /* Xo */ 223 post_fo, /* Fo */ 224 NULL, /* Fc */ 225 NULL, /* Oo */ 226 NULL, /* Oc */ 227 post_bk, /* Bk */ 228 NULL, /* Ek */ 229 post_eoln, /* Bt */ 230 post_obsolete, /* Hf */ 231 post_obsolete, /* Fr */ 232 post_eoln, /* Ud */ 233 post_lb, /* Lb */ 234 post_abort, /* Lp */ 235 post_delim_nb, /* Lk */ 236 post_defaults, /* Mt */ 237 post_delim_nb, /* Brq */ 238 NULL, /* Bro */ 239 NULL, /* Brc */ 240 NULL, /* %C */ 241 post_es, /* Es */ 242 post_en, /* En */ 243 post_xx, /* Dx */ 244 NULL, /* %Q */ 245 NULL, /* %U */ 246 NULL, /* Ta */ 247 post_tg, /* Tg */ 248}; 249 250#define RSORD_MAX 14 /* Number of `Rs' blocks. */ 251 252static const enum roff_tok rsord[RSORD_MAX] = { 253 MDOC__A, 254 MDOC__T, 255 MDOC__B, 256 MDOC__I, 257 MDOC__J, 258 MDOC__R, 259 MDOC__N, 260 MDOC__V, 261 MDOC__U, 262 MDOC__P, 263 MDOC__Q, 264 MDOC__C, 265 MDOC__D, 266 MDOC__O 267}; 268 269static const char * const secnames[SEC__MAX] = { 270 NULL, 271 "NAME", 272 "LIBRARY", 273 "SYNOPSIS", 274 "DESCRIPTION", 275 "CONTEXT", 276 "IMPLEMENTATION NOTES", 277 "RETURN VALUES", 278 "ENVIRONMENT", 279 "FILES", 280 "EXIT STATUS", 281 "EXAMPLES", 282 "DIAGNOSTICS", 283 "COMPATIBILITY", 284 "ERRORS", 285 "SEE ALSO", 286 "STANDARDS", 287 "HISTORY", 288 "AUTHORS", 289 "CAVEATS", 290 "BUGS", 291 "SECURITY CONSIDERATIONS", 292 NULL 293}; 294 295static int fn_prio = TAG_STRONG; 296 297 298/* Validate the subtree rooted at mdoc->last. */ 299void 300mdoc_validate(struct roff_man *mdoc) 301{ 302 struct roff_node *n, *np; 303 const v_post *p; 304 305 /* 306 * Translate obsolete macros to modern macros first 307 * such that later code does not need to look 308 * for the obsolete versions. 309 */ 310 311 n = mdoc->last; 312 switch (n->tok) { 313 case MDOC_Lp: 314 n->tok = MDOC_Pp; 315 break; 316 case MDOC_Ot: 317 post_obsolete(mdoc); 318 n->tok = MDOC_Ft; 319 break; 320 default: 321 break; 322 } 323 324 /* 325 * Iterate over all children, recursing into each one 326 * in turn, depth-first. 327 */ 328 329 mdoc->last = mdoc->last->child; 330 while (mdoc->last != NULL) { 331 mdoc_validate(mdoc); 332 if (mdoc->last == n) 333 mdoc->last = mdoc->last->child; 334 else 335 mdoc->last = mdoc->last->next; 336 } 337 338 /* Finally validate the macro itself. */ 339 340 mdoc->last = n; 341 mdoc->next = ROFF_NEXT_SIBLING; 342 switch (n->type) { 343 case ROFFT_TEXT: 344 np = n->parent; 345 if (n->sec != SEC_SYNOPSIS || 346 (np->tok != MDOC_Cd && np->tok != MDOC_Fd)) 347 check_text(mdoc, n->line, n->pos, n->string); 348 if ((n->flags & NODE_NOFILL) == 0 && 349 (np->tok != MDOC_It || np->type != ROFFT_HEAD || 350 np->parent->parent->norm->Bl.type != LIST_diag)) 351 check_text_em(mdoc, n->line, n->pos, n->string); 352 if (np->tok == MDOC_It || (np->type == ROFFT_BODY && 353 (np->tok == MDOC_Sh || np->tok == MDOC_Ss))) 354 check_toptext(mdoc, n->line, n->pos, n->string); 355 break; 356 case ROFFT_COMMENT: 357 case ROFFT_EQN: 358 case ROFFT_TBL: 359 break; 360 case ROFFT_ROOT: 361 post_root(mdoc); 362 break; 363 default: 364 check_args(mdoc, mdoc->last); 365 366 /* 367 * Closing delimiters are not special at the 368 * beginning of a block, opening delimiters 369 * are not special at the end. 370 */ 371 372 if (n->child != NULL) 373 n->child->flags &= ~NODE_DELIMC; 374 if (n->last != NULL) 375 n->last->flags &= ~NODE_DELIMO; 376 377 /* Call the macro's postprocessor. */ 378 379 if (n->tok < ROFF_MAX) { 380 roff_validate(mdoc); 381 break; 382 } 383 384 assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); 385 p = mdoc_valids + (n->tok - MDOC_Dd); 386 if (*p) 387 (*p)(mdoc); 388 if (mdoc->last == n) 389 mdoc_state(mdoc, n); 390 break; 391 } 392} 393 394static void 395check_args(struct roff_man *mdoc, struct roff_node *n) 396{ 397 int i; 398 399 if (NULL == n->args) 400 return; 401 402 assert(n->args->argc); 403 for (i = 0; i < (int)n->args->argc; i++) 404 check_argv(mdoc, n, &n->args->argv[i]); 405} 406 407static void 408check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v) 409{ 410 int i; 411 412 for (i = 0; i < (int)v->sz; i++) 413 check_text(mdoc, v->line, v->pos, v->value[i]); 414} 415 416static void 417check_text(struct roff_man *mdoc, int ln, int pos, char *p) 418{ 419 char *cp; 420 421 if (mdoc->last->flags & NODE_NOFILL) 422 return; 423 424 for (cp = p; NULL != (p = strchr(p, '\t')); p++) 425 mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL); 426} 427 428static void 429check_text_em(struct roff_man *mdoc, int ln, int pos, char *p) 430{ 431 const struct roff_node *np, *nn; 432 char *cp; 433 434 np = mdoc->last->prev; 435 nn = mdoc->last->next; 436 437 /* Look for em-dashes wrongly encoded as "--". */ 438 439 for (cp = p; *cp != '\0'; cp++) { 440 if (cp[0] != '-' || cp[1] != '-') 441 continue; 442 cp++; 443 444 /* Skip input sequences of more than two '-'. */ 445 446 if (cp[1] == '-') { 447 while (cp[1] == '-') 448 cp++; 449 continue; 450 } 451 452 /* Skip "--" directly attached to something else. */ 453 454 if ((cp - p > 1 && cp[-2] != ' ') || 455 (cp[1] != '\0' && cp[1] != ' ')) 456 continue; 457 458 /* Require a letter right before or right afterwards. */ 459 460 if ((cp - p > 2 ? 461 isalpha((unsigned char)cp[-3]) : 462 np != NULL && 463 np->type == ROFFT_TEXT && 464 *np->string != '\0' && 465 isalpha((unsigned char)np->string[ 466 strlen(np->string) - 1])) || 467 (cp[1] != '\0' && cp[2] != '\0' ? 468 isalpha((unsigned char)cp[2]) : 469 nn != NULL && 470 nn->type == ROFFT_TEXT && 471 isalpha((unsigned char)*nn->string))) { 472 mandoc_msg(MANDOCERR_DASHDASH, 473 ln, pos + (int)(cp - p) - 1, NULL); 474 break; 475 } 476 } 477} 478 479static void 480check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p) 481{ 482 const char *cp, *cpr; 483 484 if (*p == '\0') 485 return; 486 487 if ((cp = strstr(p, "OpenBSD")) != NULL) 488 mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox"); 489 if ((cp = strstr(p, "NetBSD")) != NULL) 490 mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx"); 491 if ((cp = strstr(p, "FreeBSD")) != NULL) 492 mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx"); 493 if ((cp = strstr(p, "DragonFly")) != NULL) 494 mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx"); 495 496 cp = p; 497 while ((cp = strstr(cp + 1, "()")) != NULL) { 498 for (cpr = cp - 1; cpr >= p; cpr--) 499 if (*cpr != '_' && !isalnum((unsigned char)*cpr)) 500 break; 501 if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) { 502 cpr++; 503 mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p), 504 "%.*s()", (int)(cp - cpr), cpr); 505 } 506 } 507} 508 509static void 510post_abort(POST_ARGS) 511{ 512 abort(); 513} 514 515static void 516post_delim(POST_ARGS) 517{ 518 const struct roff_node *nch; 519 const char *lc; 520 enum mdelim delim; 521 enum roff_tok tok; 522 523 tok = mdoc->last->tok; 524 nch = mdoc->last->last; 525 if (nch == NULL || nch->type != ROFFT_TEXT) 526 return; 527 lc = strchr(nch->string, '\0') - 1; 528 if (lc < nch->string) 529 return; 530 delim = mdoc_isdelim(lc); 531 if (delim == DELIM_NONE || delim == DELIM_OPEN) 532 return; 533 if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh || 534 tok == MDOC_Ss || tok == MDOC_Fo)) 535 return; 536 537 mandoc_msg(MANDOCERR_DELIM, nch->line, 538 nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok], 539 nch == mdoc->last->child ? "" : " ...", nch->string); 540} 541 542static void 543post_delim_nb(POST_ARGS) 544{ 545 const struct roff_node *nch; 546 const char *lc, *cp; 547 int nw; 548 enum mdelim delim; 549 enum roff_tok tok; 550 551 /* 552 * Find candidates: at least two bytes, 553 * the last one a closing or middle delimiter. 554 */ 555 556 tok = mdoc->last->tok; 557 nch = mdoc->last->last; 558 if (nch == NULL || nch->type != ROFFT_TEXT) 559 return; 560 lc = strchr(nch->string, '\0') - 1; 561 if (lc <= nch->string) 562 return; 563 delim = mdoc_isdelim(lc); 564 if (delim == DELIM_NONE || delim == DELIM_OPEN) 565 return; 566 567 /* 568 * Reduce false positives by allowing various cases. 569 */ 570 571 /* Escaped delimiters. */ 572 if (lc > nch->string + 1 && lc[-2] == '\\' && 573 (lc[-1] == '&' || lc[-1] == 'e')) 574 return; 575 576 /* Specific byte sequences. */ 577 switch (*lc) { 578 case ')': 579 for (cp = lc; cp >= nch->string; cp--) 580 if (*cp == '(') 581 return; 582 break; 583 case '.': 584 if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.') 585 return; 586 if (lc[-1] == '.') 587 return; 588 break; 589 case ';': 590 if (tok == MDOC_Vt) 591 return; 592 break; 593 case '?': 594 if (lc[-1] == '?') 595 return; 596 break; 597 case ']': 598 for (cp = lc; cp >= nch->string; cp--) 599 if (*cp == '[') 600 return; 601 break; 602 case '|': 603 if (lc == nch->string + 1 && lc[-1] == '|') 604 return; 605 default: 606 break; 607 } 608 609 /* Exactly two non-alphanumeric bytes. */ 610 if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1])) 611 return; 612 613 /* At least three alphabetic words with a sentence ending. */ 614 if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em || 615 tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) { 616 nw = 0; 617 for (cp = lc - 1; cp >= nch->string; cp--) { 618 if (*cp == ' ') { 619 nw++; 620 if (cp > nch->string && cp[-1] == ',') 621 cp--; 622 } else if (isalpha((unsigned int)*cp)) { 623 if (nw > 1) 624 return; 625 } else 626 break; 627 } 628 } 629 630 mandoc_msg(MANDOCERR_DELIM_NB, nch->line, 631 nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok], 632 nch == mdoc->last->child ? "" : " ...", nch->string); 633} 634 635static void 636post_bl_norm(POST_ARGS) 637{ 638 struct roff_node *n; 639 struct mdoc_argv *argv, *wa; 640 int i; 641 enum mdocargt mdoclt; 642 enum mdoc_list lt; 643 644 n = mdoc->last->parent; 645 n->norm->Bl.type = LIST__NONE; 646 647 /* 648 * First figure out which kind of list to use: bind ourselves to 649 * the first mentioned list type and warn about any remaining 650 * ones. If we find no list type, we default to LIST_item. 651 */ 652 653 wa = (n->args == NULL) ? NULL : n->args->argv; 654 mdoclt = MDOC_ARG_MAX; 655 for (i = 0; n->args && i < (int)n->args->argc; i++) { 656 argv = n->args->argv + i; 657 lt = LIST__NONE; 658 switch (argv->arg) { 659 /* Set list types. */ 660 case MDOC_Bullet: 661 lt = LIST_bullet; 662 break; 663 case MDOC_Dash: 664 lt = LIST_dash; 665 break; 666 case MDOC_Enum: 667 lt = LIST_enum; 668 break; 669 case MDOC_Hyphen: 670 lt = LIST_hyphen; 671 break; 672 case MDOC_Item: 673 lt = LIST_item; 674 break; 675 case MDOC_Tag: 676 lt = LIST_tag; 677 break; 678 case MDOC_Diag: 679 lt = LIST_diag; 680 break; 681 case MDOC_Hang: 682 lt = LIST_hang; 683 break; 684 case MDOC_Ohang: 685 lt = LIST_ohang; 686 break; 687 case MDOC_Inset: 688 lt = LIST_inset; 689 break; 690 case MDOC_Column: 691 lt = LIST_column; 692 break; 693 /* Set list arguments. */ 694 case MDOC_Compact: 695 if (n->norm->Bl.comp) 696 mandoc_msg(MANDOCERR_ARG_REP, 697 argv->line, argv->pos, "Bl -compact"); 698 n->norm->Bl.comp = 1; 699 break; 700 case MDOC_Width: 701 wa = argv; 702 if (0 == argv->sz) { 703 mandoc_msg(MANDOCERR_ARG_EMPTY, 704 argv->line, argv->pos, "Bl -width"); 705 n->norm->Bl.width = "0n"; 706 break; 707 } 708 if (NULL != n->norm->Bl.width) 709 mandoc_msg(MANDOCERR_ARG_REP, 710 argv->line, argv->pos, 711 "Bl -width %s", argv->value[0]); 712 rewrite_macro2len(mdoc, argv->value); 713 n->norm->Bl.width = argv->value[0]; 714 break; 715 case MDOC_Offset: 716 if (0 == argv->sz) { 717 mandoc_msg(MANDOCERR_ARG_EMPTY, 718 argv->line, argv->pos, "Bl -offset"); 719 break; 720 } 721 if (NULL != n->norm->Bl.offs) 722 mandoc_msg(MANDOCERR_ARG_REP, 723 argv->line, argv->pos, 724 "Bl -offset %s", argv->value[0]); 725 rewrite_macro2len(mdoc, argv->value); 726 n->norm->Bl.offs = argv->value[0]; 727 break; 728 default: 729 continue; 730 } 731 if (LIST__NONE == lt) 732 continue; 733 mdoclt = argv->arg; 734 735 /* Check: multiple list types. */ 736 737 if (LIST__NONE != n->norm->Bl.type) { 738 mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos, 739 "Bl -%s", mdoc_argnames[argv->arg]); 740 continue; 741 } 742 743 /* The list type should come first. */ 744 745 if (n->norm->Bl.width || 746 n->norm->Bl.offs || 747 n->norm->Bl.comp) 748 mandoc_msg(MANDOCERR_BL_LATETYPE, 749 n->line, n->pos, "Bl -%s", 750 mdoc_argnames[n->args->argv[0].arg]); 751 752 n->norm->Bl.type = lt; 753 if (LIST_column == lt) { 754 n->norm->Bl.ncols = argv->sz; 755 n->norm->Bl.cols = (void *)argv->value; 756 } 757 } 758 759 /* Allow lists to default to LIST_item. */ 760 761 if (LIST__NONE == n->norm->Bl.type) { 762 mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl"); 763 n->norm->Bl.type = LIST_item; 764 mdoclt = MDOC_Item; 765 } 766 767 /* 768 * Validate the width field. Some list types don't need width 769 * types and should be warned about them. Others should have it 770 * and must also be warned. Yet others have a default and need 771 * no warning. 772 */ 773 774 switch (n->norm->Bl.type) { 775 case LIST_tag: 776 if (n->norm->Bl.width == NULL) 777 mandoc_msg(MANDOCERR_BL_NOWIDTH, 778 n->line, n->pos, "Bl -tag"); 779 break; 780 case LIST_column: 781 case LIST_diag: 782 case LIST_ohang: 783 case LIST_inset: 784 case LIST_item: 785 if (n->norm->Bl.width != NULL) 786 mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos, 787 "Bl -%s", mdoc_argnames[mdoclt]); 788 n->norm->Bl.width = NULL; 789 break; 790 case LIST_bullet: 791 case LIST_dash: 792 case LIST_hyphen: 793 if (n->norm->Bl.width == NULL) 794 n->norm->Bl.width = "2n"; 795 break; 796 case LIST_enum: 797 if (n->norm->Bl.width == NULL) 798 n->norm->Bl.width = "3n"; 799 break; 800 default: 801 break; 802 } 803} 804 805static void 806post_bd(POST_ARGS) 807{ 808 struct roff_node *n; 809 struct mdoc_argv *argv; 810 int i; 811 enum mdoc_disp dt; 812 813 n = mdoc->last; 814 for (i = 0; n->args && i < (int)n->args->argc; i++) { 815 argv = n->args->argv + i; 816 dt = DISP__NONE; 817 818 switch (argv->arg) { 819 case MDOC_Centred: 820 dt = DISP_centered; 821 break; 822 case MDOC_Ragged: 823 dt = DISP_ragged; 824 break; 825 case MDOC_Unfilled: 826 dt = DISP_unfilled; 827 break; 828 case MDOC_Filled: 829 dt = DISP_filled; 830 break; 831 case MDOC_Literal: 832 dt = DISP_literal; 833 break; 834 case MDOC_File: 835 mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL); 836 break; 837 case MDOC_Offset: 838 if (0 == argv->sz) { 839 mandoc_msg(MANDOCERR_ARG_EMPTY, 840 argv->line, argv->pos, "Bd -offset"); 841 break; 842 } 843 if (NULL != n->norm->Bd.offs) 844 mandoc_msg(MANDOCERR_ARG_REP, 845 argv->line, argv->pos, 846 "Bd -offset %s", argv->value[0]); 847 rewrite_macro2len(mdoc, argv->value); 848 n->norm->Bd.offs = argv->value[0]; 849 break; 850 case MDOC_Compact: 851 if (n->norm->Bd.comp) 852 mandoc_msg(MANDOCERR_ARG_REP, 853 argv->line, argv->pos, "Bd -compact"); 854 n->norm->Bd.comp = 1; 855 break; 856 default: 857 abort(); 858 } 859 if (DISP__NONE == dt) 860 continue; 861 862 if (DISP__NONE == n->norm->Bd.type) 863 n->norm->Bd.type = dt; 864 else 865 mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos, 866 "Bd -%s", mdoc_argnames[argv->arg]); 867 } 868 869 if (DISP__NONE == n->norm->Bd.type) { 870 mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd"); 871 n->norm->Bd.type = DISP_ragged; 872 } 873} 874 875/* 876 * Stand-alone line macros. 877 */ 878 879static void 880post_an_norm(POST_ARGS) 881{ 882 struct roff_node *n; 883 struct mdoc_argv *argv; 884 size_t i; 885 886 n = mdoc->last; 887 if (n->args == NULL) 888 return; 889 890 for (i = 1; i < n->args->argc; i++) { 891 argv = n->args->argv + i; 892 mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos, 893 "An -%s", mdoc_argnames[argv->arg]); 894 } 895 896 argv = n->args->argv; 897 if (argv->arg == MDOC_Split) 898 n->norm->An.auth = AUTH_split; 899 else if (argv->arg == MDOC_Nosplit) 900 n->norm->An.auth = AUTH_nosplit; 901 else 902 abort(); 903} 904 905static void 906post_eoln(POST_ARGS) 907{ 908 struct roff_node *n; 909 910 post_useless(mdoc); 911 n = mdoc->last; 912 if (n->child != NULL) 913 mandoc_msg(MANDOCERR_ARG_SKIP, n->line, 914 n->pos, "%s %s", roff_name[n->tok], n->child->string); 915 916 while (n->child != NULL) 917 roff_node_delete(mdoc, n->child); 918 919 roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ? 920 "is currently in beta test." : "currently under development."); 921 mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 922 mdoc->last = n; 923} 924 925static int 926build_list(struct roff_man *mdoc, int tok) 927{ 928 struct roff_node *n; 929 int ic; 930 931 n = mdoc->last->next; 932 for (ic = 1;; ic++) { 933 roff_elem_alloc(mdoc, n->line, n->pos, tok); 934 mdoc->last->flags |= NODE_NOSRC; 935 roff_node_relink(mdoc, n); 936 n = mdoc->last = mdoc->last->parent; 937 mdoc->next = ROFF_NEXT_SIBLING; 938 if (n->next == NULL) 939 return ic; 940 if (ic > 1 || n->next->next != NULL) { 941 roff_word_alloc(mdoc, n->line, n->pos, ","); 942 mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC; 943 } 944 n = mdoc->last->next; 945 if (n->next == NULL) { 946 roff_word_alloc(mdoc, n->line, n->pos, "and"); 947 mdoc->last->flags |= NODE_NOSRC; 948 } 949 } 950} 951 952static void 953post_ex(POST_ARGS) 954{ 955 struct roff_node *n; 956 int ic; 957 958 post_std(mdoc); 959 960 n = mdoc->last; 961 mdoc->next = ROFF_NEXT_CHILD; 962 roff_word_alloc(mdoc, n->line, n->pos, "The"); 963 mdoc->last->flags |= NODE_NOSRC; 964 965 if (mdoc->last->next != NULL) 966 ic = build_list(mdoc, MDOC_Nm); 967 else if (mdoc->meta.name != NULL) { 968 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm); 969 mdoc->last->flags |= NODE_NOSRC; 970 roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 971 mdoc->last->flags |= NODE_NOSRC; 972 mdoc->last = mdoc->last->parent; 973 mdoc->next = ROFF_NEXT_SIBLING; 974 ic = 1; 975 } else { 976 mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex"); 977 ic = 0; 978 } 979 980 roff_word_alloc(mdoc, n->line, n->pos, 981 ic > 1 ? "utilities exit\\~0" : "utility exits\\~0"); 982 mdoc->last->flags |= NODE_NOSRC; 983 roff_word_alloc(mdoc, n->line, n->pos, 984 "on success, and\\~>0 if an error occurs."); 985 mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 986 mdoc->last = n; 987} 988 989static void 990post_lb(POST_ARGS) 991{ 992 struct roff_node *n; 993 994 post_delim_nb(mdoc); 995 996 n = mdoc->last; 997 assert(n->child->type == ROFFT_TEXT); 998 mdoc->next = ROFF_NEXT_CHILD; 999 roff_word_alloc(mdoc, n->line, n->pos, "library"); 1000 mdoc->last->flags = NODE_NOSRC; 1001 roff_word_alloc(mdoc, n->line, n->pos, "\\(lq"); 1002 mdoc->last->flags = NODE_DELIMO | NODE_NOSRC; 1003 mdoc->last = mdoc->last->next; 1004 roff_word_alloc(mdoc, n->line, n->pos, "\\(rq"); 1005 mdoc->last->flags = NODE_DELIMC | NODE_NOSRC; 1006 mdoc->last = n; 1007} 1008 1009static void 1010post_rv(POST_ARGS) 1011{ 1012 struct roff_node *n; 1013 int ic; 1014 1015 post_std(mdoc); 1016 1017 n = mdoc->last; 1018 mdoc->next = ROFF_NEXT_CHILD; 1019 if (n->child != NULL) { 1020 roff_word_alloc(mdoc, n->line, n->pos, "The"); 1021 mdoc->last->flags |= NODE_NOSRC; 1022 ic = build_list(mdoc, MDOC_Fn); 1023 roff_word_alloc(mdoc, n->line, n->pos, 1024 ic > 1 ? "functions return" : "function returns"); 1025 mdoc->last->flags |= NODE_NOSRC; 1026 roff_word_alloc(mdoc, n->line, n->pos, 1027 "the value\\~0 if successful;"); 1028 } else 1029 roff_word_alloc(mdoc, n->line, n->pos, "Upon successful " 1030 "completion, the value\\~0 is returned;"); 1031 mdoc->last->flags |= NODE_NOSRC; 1032 1033 roff_word_alloc(mdoc, n->line, n->pos, "otherwise " 1034 "the value\\~\\-1 is returned and the global variable"); 1035 mdoc->last->flags |= NODE_NOSRC; 1036 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va); 1037 mdoc->last->flags |= NODE_NOSRC; 1038 roff_word_alloc(mdoc, n->line, n->pos, "errno"); 1039 mdoc->last->flags |= NODE_NOSRC; 1040 mdoc->last = mdoc->last->parent; 1041 mdoc->next = ROFF_NEXT_SIBLING; 1042 roff_word_alloc(mdoc, n->line, n->pos, 1043 "is set to indicate the error."); 1044 mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 1045 mdoc->last = n; 1046} 1047 1048static void 1049post_std(POST_ARGS) 1050{ 1051 struct roff_node *n; 1052 1053 post_delim(mdoc); 1054 1055 n = mdoc->last; 1056 if (n->args && n->args->argc == 1) 1057 if (n->args->argv[0].arg == MDOC_Std) 1058 return; 1059 1060 mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos, 1061 "%s", roff_name[n->tok]); 1062} 1063 1064static void 1065post_st(POST_ARGS) 1066{ 1067 struct roff_node *n, *nch; 1068 const char *p; 1069 1070 n = mdoc->last; 1071 nch = n->child; 1072 assert(nch->type == ROFFT_TEXT); 1073 1074 if ((p = mdoc_a2st(nch->string)) == NULL) { 1075 mandoc_msg(MANDOCERR_ST_BAD, 1076 nch->line, nch->pos, "St %s", nch->string); 1077 roff_node_delete(mdoc, n); 1078 return; 1079 } 1080 1081 nch->flags |= NODE_NOPRT; 1082 mdoc->next = ROFF_NEXT_CHILD; 1083 roff_word_alloc(mdoc, nch->line, nch->pos, p); 1084 mdoc->last->flags |= NODE_NOSRC; 1085 mdoc->last= n; 1086} 1087 1088static void 1089post_tg(POST_ARGS) 1090{ 1091 struct roff_node *n; /* The .Tg node. */ 1092 struct roff_node *nch; /* The first child of the .Tg node. */ 1093 struct roff_node *nn; /* The next node after the .Tg node. */ 1094 struct roff_node *np; /* The parent of the next node. */ 1095 struct roff_node *nt; /* The TEXT node containing the tag. */ 1096 size_t len; /* The number of bytes in the tag. */ 1097 1098 /* Find the next node. */ 1099 n = mdoc->last; 1100 for (nn = n; nn != NULL; nn = nn->parent) { 1101 if (nn->type != ROFFT_HEAD && nn->type != ROFFT_BODY && 1102 nn->type != ROFFT_TAIL && nn->next != NULL) { 1103 nn = nn->next; 1104 break; 1105 } 1106 } 1107 1108 /* Find the tag. */ 1109 nt = nch = n->child; 1110 if (nch == NULL && nn != NULL && nn->child != NULL && 1111 nn->child->type == ROFFT_TEXT) 1112 nt = nn->child; 1113 1114 /* Validate the tag. */ 1115 if (nt == NULL || *nt->string == '\0') 1116 mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg"); 1117 if (nt == NULL) { 1118 roff_node_delete(mdoc, n); 1119 return; 1120 } 1121 len = strcspn(nt->string, " \t\\"); 1122 if (nt->string[len] != '\0') 1123 mandoc_msg(MANDOCERR_TG_SPC, nt->line, 1124 nt->pos + len, "Tg %s", nt->string); 1125 1126 /* Keep only the first argument. */ 1127 if (nch != NULL && nch->next != NULL) { 1128 mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line, 1129 nch->next->pos, "Tg ... %s", nch->next->string); 1130 while (nch->next != NULL) 1131 roff_node_delete(mdoc, nch->next); 1132 } 1133 1134 /* Drop the macro if the first argument is invalid. */ 1135 if (len == 0 || nt->string[len] != '\0') { 1136 roff_node_delete(mdoc, n); 1137 return; 1138 } 1139 1140 /* By default, tag the .Tg node itself. */ 1141 if (nn == NULL || nn->flags & NODE_ID) 1142 nn = n; 1143 1144 /* Explicit tagging of specific macros. */ 1145 switch (nn->tok) { 1146 case MDOC_Sh: 1147 case MDOC_Ss: 1148 case MDOC_Fo: 1149 nn = nn->head->child == NULL ? n : nn->head; 1150 break; 1151 case MDOC_It: 1152 np = nn->parent; 1153 while (np->tok != MDOC_Bl) 1154 np = np->parent; 1155 switch (np->norm->Bl.type) { 1156 case LIST_column: 1157 break; 1158 case LIST_diag: 1159 case LIST_hang: 1160 case LIST_inset: 1161 case LIST_ohang: 1162 case LIST_tag: 1163 nn = nn->head; 1164 break; 1165 case LIST_bullet: 1166 case LIST_dash: 1167 case LIST_enum: 1168 case LIST_hyphen: 1169 case LIST_item: 1170 nn = nn->body->child == NULL ? n : nn->body; 1171 break; 1172 default: 1173 abort(); 1174 } 1175 break; 1176 case MDOC_Bd: 1177 case MDOC_Bl: 1178 case MDOC_D1: 1179 case MDOC_Dl: 1180 nn = nn->body->child == NULL ? n : nn->body; 1181 break; 1182 case MDOC_Pp: 1183 break; 1184 case MDOC_Cm: 1185 case MDOC_Dv: 1186 case MDOC_Em: 1187 case MDOC_Er: 1188 case MDOC_Ev: 1189 case MDOC_Fl: 1190 case MDOC_Fn: 1191 case MDOC_Ic: 1192 case MDOC_Li: 1193 case MDOC_Ms: 1194 case MDOC_No: 1195 case MDOC_Sy: 1196 if (nn->child == NULL) 1197 nn = n; 1198 break; 1199 default: 1200 nn = n; 1201 break; 1202 } 1203 tag_put(nt->string, TAG_MANUAL, nn); 1204 if (nn != n) 1205 n->flags |= NODE_NOPRT; 1206} 1207 1208static void 1209post_obsolete(POST_ARGS) 1210{ 1211 struct roff_node *n; 1212 1213 n = mdoc->last; 1214 if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) 1215 mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos, 1216 "%s", roff_name[n->tok]); 1217} 1218 1219static void 1220post_useless(POST_ARGS) 1221{ 1222 struct roff_node *n; 1223 1224 n = mdoc->last; 1225 mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos, 1226 "%s", roff_name[n->tok]); 1227} 1228 1229/* 1230 * Block macros. 1231 */ 1232 1233static void 1234post_bf(POST_ARGS) 1235{ 1236 struct roff_node *np, *nch; 1237 1238 /* 1239 * Unlike other data pointers, these are "housed" by the HEAD 1240 * element, which contains the goods. 1241 */ 1242 1243 np = mdoc->last; 1244 if (np->type != ROFFT_HEAD) 1245 return; 1246 1247 assert(np->parent->type == ROFFT_BLOCK); 1248 assert(np->parent->tok == MDOC_Bf); 1249 1250 /* Check the number of arguments. */ 1251 1252 nch = np->child; 1253 if (np->parent->args == NULL) { 1254 if (nch == NULL) { 1255 mandoc_msg(MANDOCERR_BF_NOFONT, 1256 np->line, np->pos, "Bf"); 1257 return; 1258 } 1259 nch = nch->next; 1260 } 1261 if (nch != NULL) 1262 mandoc_msg(MANDOCERR_ARG_EXCESS, 1263 nch->line, nch->pos, "Bf ... %s", nch->string); 1264 1265 /* Extract argument into data. */ 1266 1267 if (np->parent->args != NULL) { 1268 switch (np->parent->args->argv[0].arg) { 1269 case MDOC_Emphasis: 1270 np->norm->Bf.font = FONT_Em; 1271 break; 1272 case MDOC_Literal: 1273 np->norm->Bf.font = FONT_Li; 1274 break; 1275 case MDOC_Symbolic: 1276 np->norm->Bf.font = FONT_Sy; 1277 break; 1278 default: 1279 abort(); 1280 } 1281 return; 1282 } 1283 1284 /* Extract parameter into data. */ 1285 1286 if ( ! strcmp(np->child->string, "Em")) 1287 np->norm->Bf.font = FONT_Em; 1288 else if ( ! strcmp(np->child->string, "Li")) 1289 np->norm->Bf.font = FONT_Li; 1290 else if ( ! strcmp(np->child->string, "Sy")) 1291 np->norm->Bf.font = FONT_Sy; 1292 else 1293 mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line, 1294 np->child->pos, "Bf %s", np->child->string); 1295} 1296 1297static void 1298post_fname(POST_ARGS) 1299{ 1300 struct roff_node *n, *nch; 1301 const char *cp; 1302 size_t pos; 1303 1304 n = mdoc->last; 1305 nch = n->child; 1306 cp = nch->string; 1307 if (*cp == '(') { 1308 if (cp[strlen(cp + 1)] == ')') 1309 return; 1310 pos = 0; 1311 } else { 1312 pos = strcspn(cp, "()"); 1313 if (cp[pos] == '\0') { 1314 if (n->sec == SEC_DESCRIPTION || 1315 n->sec == SEC_CUSTOM) 1316 tag_put(NULL, fn_prio++, n); 1317 return; 1318 } 1319 } 1320 mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp); 1321} 1322 1323static void 1324post_fn(POST_ARGS) 1325{ 1326 post_fname(mdoc); 1327 post_fa(mdoc); 1328} 1329 1330static void 1331post_fo(POST_ARGS) 1332{ 1333 const struct roff_node *n; 1334 1335 n = mdoc->last; 1336 1337 if (n->type != ROFFT_HEAD) 1338 return; 1339 1340 if (n->child == NULL) { 1341 mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo"); 1342 return; 1343 } 1344 if (n->child != n->last) { 1345 mandoc_msg(MANDOCERR_ARG_EXCESS, 1346 n->child->next->line, n->child->next->pos, 1347 "Fo ... %s", n->child->next->string); 1348 while (n->child != n->last) 1349 roff_node_delete(mdoc, n->last); 1350 } else 1351 post_delim(mdoc); 1352 1353 post_fname(mdoc); 1354} 1355 1356static void 1357post_fa(POST_ARGS) 1358{ 1359 const struct roff_node *n; 1360 const char *cp; 1361 1362 for (n = mdoc->last->child; n != NULL; n = n->next) { 1363 for (cp = n->string; *cp != '\0'; cp++) { 1364 /* Ignore callbacks and alterations. */ 1365 if (*cp == '(' || *cp == '{') 1366 break; 1367 if (*cp != ',') 1368 continue; 1369 mandoc_msg(MANDOCERR_FA_COMMA, n->line, 1370 n->pos + (int)(cp - n->string), "%s", n->string); 1371 break; 1372 } 1373 } 1374 post_delim_nb(mdoc); 1375} 1376 1377static void 1378post_nm(POST_ARGS) 1379{ 1380 struct roff_node *n; 1381 1382 n = mdoc->last; 1383 1384 if (n->sec == SEC_NAME && n->child != NULL && 1385 n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL) 1386 mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1); 1387 1388 if (n->last != NULL && n->last->tok == MDOC_Pp) 1389 roff_node_relink(mdoc, n->last); 1390 1391 if (mdoc->meta.name == NULL) 1392 deroff(&mdoc->meta.name, n); 1393 1394 if (mdoc->meta.name == NULL || 1395 (mdoc->lastsec == SEC_NAME && n->child == NULL)) 1396 mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm"); 1397 1398 switch (n->type) { 1399 case ROFFT_ELEM: 1400 post_delim_nb(mdoc); 1401 break; 1402 case ROFFT_HEAD: 1403 post_delim(mdoc); 1404 break; 1405 default: 1406 return; 1407 } 1408 1409 if ((n->child != NULL && n->child->type == ROFFT_TEXT) || 1410 mdoc->meta.name == NULL) 1411 return; 1412 1413 mdoc->next = ROFF_NEXT_CHILD; 1414 roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 1415 mdoc->last->flags |= NODE_NOSRC; 1416 mdoc->last = n; 1417} 1418 1419static void 1420post_nd(POST_ARGS) 1421{ 1422 struct roff_node *n; 1423 1424 n = mdoc->last; 1425 1426 if (n->type != ROFFT_BODY) 1427 return; 1428 1429 if (n->sec != SEC_NAME) 1430 mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd"); 1431 1432 if (n->child == NULL) 1433 mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd"); 1434 else 1435 post_delim(mdoc); 1436 1437 post_hyph(mdoc); 1438} 1439 1440static void 1441post_display(POST_ARGS) 1442{ 1443 struct roff_node *n, *np; 1444 1445 n = mdoc->last; 1446 switch (n->type) { 1447 case ROFFT_BODY: 1448 if (n->end != ENDBODY_NOT) { 1449 if (n->tok == MDOC_Bd && 1450 n->body->parent->args == NULL) 1451 roff_node_delete(mdoc, n); 1452 } else if (n->child == NULL) 1453 mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, 1454 "%s", roff_name[n->tok]); 1455 else if (n->tok == MDOC_D1) 1456 post_hyph(mdoc); 1457 break; 1458 case ROFFT_BLOCK: 1459 if (n->tok == MDOC_Bd) { 1460 if (n->args == NULL) { 1461 mandoc_msg(MANDOCERR_BD_NOARG, 1462 n->line, n->pos, "Bd"); 1463 mdoc->next = ROFF_NEXT_SIBLING; 1464 while (n->body->child != NULL) 1465 roff_node_relink(mdoc, 1466 n->body->child); 1467 roff_node_delete(mdoc, n); 1468 break; 1469 } 1470 post_bd(mdoc); 1471 post_prevpar(mdoc); 1472 } 1473 for (np = n->parent; np != NULL; np = np->parent) { 1474 if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { 1475 mandoc_msg(MANDOCERR_BD_NEST, n->line, 1476 n->pos, "%s in Bd", roff_name[n->tok]); 1477 break; 1478 } 1479 } 1480 break; 1481 default: 1482 break; 1483 } 1484} 1485 1486static void 1487post_defaults(POST_ARGS) 1488{ 1489 struct roff_node *n; 1490 1491 n = mdoc->last; 1492 if (n->child != NULL) { 1493 post_delim_nb(mdoc); 1494 return; 1495 } 1496 mdoc->next = ROFF_NEXT_CHILD; 1497 switch (n->tok) { 1498 case MDOC_Ar: 1499 roff_word_alloc(mdoc, n->line, n->pos, "file"); 1500 mdoc->last->flags |= NODE_NOSRC; 1501 roff_word_alloc(mdoc, n->line, n->pos, "..."); 1502 break; 1503 case MDOC_Pa: 1504 case MDOC_Mt: 1505 roff_word_alloc(mdoc, n->line, n->pos, "~"); 1506 break; 1507 default: 1508 abort(); 1509 } 1510 mdoc->last->flags |= NODE_NOSRC; 1511 mdoc->last = n; 1512} 1513 1514static void 1515post_at(POST_ARGS) 1516{ 1517 struct roff_node *n, *nch; 1518 const char *att; 1519 1520 n = mdoc->last; 1521 nch = n->child; 1522 1523 /* 1524 * If we have a child, look it up in the standard keys. If a 1525 * key exist, use that instead of the child; if it doesn't, 1526 * prefix "AT&T UNIX " to the existing data. 1527 */ 1528 1529 att = NULL; 1530 if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL)) 1531 mandoc_msg(MANDOCERR_AT_BAD, 1532 nch->line, nch->pos, "At %s", nch->string); 1533 1534 mdoc->next = ROFF_NEXT_CHILD; 1535 if (att != NULL) { 1536 roff_word_alloc(mdoc, nch->line, nch->pos, att); 1537 nch->flags |= NODE_NOPRT; 1538 } else 1539 roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); 1540 mdoc->last->flags |= NODE_NOSRC; 1541 mdoc->last = n; 1542} 1543 1544static void 1545post_an(POST_ARGS) 1546{ 1547 struct roff_node *np, *nch; 1548 1549 post_an_norm(mdoc); 1550 1551 np = mdoc->last; 1552 nch = np->child; 1553 if (np->norm->An.auth == AUTH__NONE) { 1554 if (nch == NULL) 1555 mandoc_msg(MANDOCERR_MACRO_EMPTY, 1556 np->line, np->pos, "An"); 1557 else 1558 post_delim_nb(mdoc); 1559 } else if (nch != NULL) 1560 mandoc_msg(MANDOCERR_ARG_EXCESS, 1561 nch->line, nch->pos, "An ... %s", nch->string); 1562} 1563 1564static void 1565post_em(POST_ARGS) 1566{ 1567 post_tag(mdoc); 1568 tag_put(NULL, TAG_FALLBACK, mdoc->last); 1569} 1570 1571static void 1572post_en(POST_ARGS) 1573{ 1574 post_obsolete(mdoc); 1575 if (mdoc->last->type == ROFFT_BLOCK) 1576 mdoc->last->norm->Es = mdoc->last_es; 1577} 1578 1579static void 1580post_er(POST_ARGS) 1581{ 1582 struct roff_node *n; 1583 1584 n = mdoc->last; 1585 if (n->sec == SEC_ERRORS && 1586 (n->parent->tok == MDOC_It || 1587 (n->parent->tok == MDOC_Bq && 1588 n->parent->parent->parent->tok == MDOC_It))) 1589 tag_put(NULL, TAG_STRONG, n); 1590 post_delim_nb(mdoc); 1591} 1592 1593static void 1594post_tag(POST_ARGS) 1595{ 1596 struct roff_node *n; 1597 1598 n = mdoc->last; 1599 if ((n->prev == NULL || 1600 (n->prev->type == ROFFT_TEXT && 1601 strcmp(n->prev->string, "|") == 0)) && 1602 (n->parent->tok == MDOC_It || 1603 (n->parent->tok == MDOC_Xo && 1604 n->parent->parent->prev == NULL && 1605 n->parent->parent->parent->tok == MDOC_It))) 1606 tag_put(NULL, TAG_STRONG, n); 1607 post_delim_nb(mdoc); 1608} 1609 1610static void 1611post_es(POST_ARGS) 1612{ 1613 post_obsolete(mdoc); 1614 mdoc->last_es = mdoc->last; 1615} 1616 1617static void 1618post_fl(POST_ARGS) 1619{ 1620 struct roff_node *n; 1621 char *cp; 1622 1623 /* 1624 * Transform ".Fl Fl long" to ".Fl \-long", 1625 * resulting for example in better HTML output. 1626 */ 1627 1628 n = mdoc->last; 1629 if (n->prev != NULL && n->prev->tok == MDOC_Fl && 1630 n->prev->child == NULL && n->child != NULL && 1631 (n->flags & NODE_LINE) == 0) { 1632 mandoc_asprintf(&cp, "\\-%s", n->child->string); 1633 free(n->child->string); 1634 n->child->string = cp; 1635 roff_node_delete(mdoc, n->prev); 1636 } 1637 post_tag(mdoc); 1638} 1639 1640static void 1641post_xx(POST_ARGS) 1642{ 1643 struct roff_node *n; 1644 const char *os; 1645 char *v; 1646 1647 post_delim_nb(mdoc); 1648 1649 n = mdoc->last; 1650 switch (n->tok) { 1651 case MDOC_Bsx: 1652 os = "BSD/OS"; 1653 break; 1654 case MDOC_Dx: 1655 os = "DragonFly"; 1656 break; 1657 case MDOC_Fx: 1658 os = "FreeBSD"; 1659 break; 1660 case MDOC_Nx: 1661 os = "NetBSD"; 1662 if (n->child == NULL) 1663 break; 1664 v = n->child->string; 1665 if ((v[0] != '0' && v[0] != '1') || v[1] != '.' || 1666 v[2] < '0' || v[2] > '9' || 1667 v[3] < 'a' || v[3] > 'z' || v[4] != '\0') 1668 break; 1669 n->child->flags |= NODE_NOPRT; 1670 mdoc->next = ROFF_NEXT_CHILD; 1671 roff_word_alloc(mdoc, n->child->line, n->child->pos, v); 1672 v = mdoc->last->string; 1673 v[3] = toupper((unsigned char)v[3]); 1674 mdoc->last->flags |= NODE_NOSRC; 1675 mdoc->last = n; 1676 break; 1677 case MDOC_Ox: 1678 os = "OpenBSD"; 1679 break; 1680 case MDOC_Ux: 1681 os = "UNIX"; 1682 break; 1683 default: 1684 abort(); 1685 } 1686 mdoc->next = ROFF_NEXT_CHILD; 1687 roff_word_alloc(mdoc, n->line, n->pos, os); 1688 mdoc->last->flags |= NODE_NOSRC; 1689 mdoc->last = n; 1690} 1691 1692static void 1693post_it(POST_ARGS) 1694{ 1695 struct roff_node *nbl, *nit, *nch; 1696 int i, cols; 1697 enum mdoc_list lt; 1698 1699 post_prevpar(mdoc); 1700 1701 nit = mdoc->last; 1702 if (nit->type != ROFFT_BLOCK) 1703 return; 1704 1705 nbl = nit->parent->parent; 1706 lt = nbl->norm->Bl.type; 1707 1708 switch (lt) { 1709 case LIST_tag: 1710 case LIST_hang: 1711 case LIST_ohang: 1712 case LIST_inset: 1713 case LIST_diag: 1714 if (nit->head->child == NULL) 1715 mandoc_msg(MANDOCERR_IT_NOHEAD, 1716 nit->line, nit->pos, "Bl -%s It", 1717 mdoc_argnames[nbl->args->argv[0].arg]); 1718 break; 1719 case LIST_bullet: 1720 case LIST_dash: 1721 case LIST_enum: 1722 case LIST_hyphen: 1723 if (nit->body == NULL || nit->body->child == NULL) 1724 mandoc_msg(MANDOCERR_IT_NOBODY, 1725 nit->line, nit->pos, "Bl -%s It", 1726 mdoc_argnames[nbl->args->argv[0].arg]); 1727 /* FALLTHROUGH */ 1728 case LIST_item: 1729 if ((nch = nit->head->child) != NULL) 1730 mandoc_msg(MANDOCERR_ARG_SKIP, 1731 nit->line, nit->pos, "It %s", 1732 nch->type == ROFFT_TEXT ? nch->string : 1733 roff_name[nch->tok]); 1734 break; 1735 case LIST_column: 1736 cols = (int)nbl->norm->Bl.ncols; 1737 1738 assert(nit->head->child == NULL); 1739 1740 if (nit->head->next->child == NULL && 1741 nit->head->next->next == NULL) { 1742 mandoc_msg(MANDOCERR_MACRO_EMPTY, 1743 nit->line, nit->pos, "It"); 1744 roff_node_delete(mdoc, nit); 1745 break; 1746 } 1747 1748 i = 0; 1749 for (nch = nit->child; nch != NULL; nch = nch->next) { 1750 if (nch->type != ROFFT_BODY) 1751 continue; 1752 if (i++ && nch->flags & NODE_LINE) 1753 mandoc_msg(MANDOCERR_TA_LINE, 1754 nch->line, nch->pos, "Ta"); 1755 } 1756 if (i < cols || i > cols + 1) 1757 mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos, 1758 "%d columns, %d cells", cols, i); 1759 else if (nit->head->next->child != NULL && 1760 nit->head->next->child->flags & NODE_LINE) 1761 mandoc_msg(MANDOCERR_IT_NOARG, 1762 nit->line, nit->pos, "Bl -column It"); 1763 break; 1764 default: 1765 abort(); 1766 } 1767} 1768 1769static void 1770post_bl_block(POST_ARGS) 1771{ 1772 struct roff_node *n, *ni, *nc; 1773 1774 post_prevpar(mdoc); 1775 1776 n = mdoc->last; 1777 for (ni = n->body->child; ni != NULL; ni = ni->next) { 1778 if (ni->body == NULL) 1779 continue; 1780 nc = ni->body->last; 1781 while (nc != NULL) { 1782 switch (nc->tok) { 1783 case MDOC_Pp: 1784 case ROFF_br: 1785 break; 1786 default: 1787 nc = NULL; 1788 continue; 1789 } 1790 if (ni->next == NULL) { 1791 mandoc_msg(MANDOCERR_PAR_MOVE, nc->line, 1792 nc->pos, "%s", roff_name[nc->tok]); 1793 roff_node_relink(mdoc, nc); 1794 } else if (n->norm->Bl.comp == 0 && 1795 n->norm->Bl.type != LIST_column) { 1796 mandoc_msg(MANDOCERR_PAR_SKIP, 1797 nc->line, nc->pos, 1798 "%s before It", roff_name[nc->tok]); 1799 roff_node_delete(mdoc, nc); 1800 } else 1801 break; 1802 nc = ni->body->last; 1803 } 1804 } 1805} 1806 1807/* 1808 * If the argument of -offset or -width is a macro, 1809 * replace it with the associated default width. 1810 */ 1811static void 1812rewrite_macro2len(struct roff_man *mdoc, char **arg) 1813{ 1814 size_t width; 1815 enum roff_tok tok; 1816 1817 if (*arg == NULL) 1818 return; 1819 else if ( ! strcmp(*arg, "Ds")) 1820 width = 6; 1821 else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE) 1822 return; 1823 else 1824 width = macro2len(tok); 1825 1826 free(*arg); 1827 mandoc_asprintf(arg, "%zun", width); 1828} 1829 1830static void 1831post_bl_head(POST_ARGS) 1832{ 1833 struct roff_node *nbl, *nh, *nch, *nnext; 1834 struct mdoc_argv *argv; 1835 int i, j; 1836 1837 post_bl_norm(mdoc); 1838 1839 nh = mdoc->last; 1840 if (nh->norm->Bl.type != LIST_column) { 1841 if ((nch = nh->child) == NULL) 1842 return; 1843 mandoc_msg(MANDOCERR_ARG_EXCESS, 1844 nch->line, nch->pos, "Bl ... %s", nch->string); 1845 while (nch != NULL) { 1846 roff_node_delete(mdoc, nch); 1847 nch = nh->child; 1848 } 1849 return; 1850 } 1851 1852 /* 1853 * Append old-style lists, where the column width specifiers 1854 * trail as macro parameters, to the new-style ("normal-form") 1855 * lists where they're argument values following -column. 1856 */ 1857 1858 if (nh->child == NULL) 1859 return; 1860 1861 nbl = nh->parent; 1862 for (j = 0; j < (int)nbl->args->argc; j++) 1863 if (nbl->args->argv[j].arg == MDOC_Column) 1864 break; 1865 1866 assert(j < (int)nbl->args->argc); 1867 1868 /* 1869 * Accommodate for new-style groff column syntax. Shuffle the 1870 * child nodes, all of which must be TEXT, as arguments for the 1871 * column field. Then, delete the head children. 1872 */ 1873 1874 argv = nbl->args->argv + j; 1875 i = argv->sz; 1876 for (nch = nh->child; nch != NULL; nch = nch->next) 1877 argv->sz++; 1878 argv->value = mandoc_reallocarray(argv->value, 1879 argv->sz, sizeof(char *)); 1880 1881 nh->norm->Bl.ncols = argv->sz; 1882 nh->norm->Bl.cols = (void *)argv->value; 1883 1884 for (nch = nh->child; nch != NULL; nch = nnext) { 1885 argv->value[i++] = nch->string; 1886 nch->string = NULL; 1887 nnext = nch->next; 1888 roff_node_delete(NULL, nch); 1889 } 1890 nh->child = NULL; 1891} 1892 1893static void 1894post_bl(POST_ARGS) 1895{ 1896 struct roff_node *nbody; /* of the Bl */ 1897 struct roff_node *nchild, *nnext; /* of the Bl body */ 1898 const char *prev_Er; 1899 int order; 1900 1901 nbody = mdoc->last; 1902 switch (nbody->type) { 1903 case ROFFT_BLOCK: 1904 post_bl_block(mdoc); 1905 return; 1906 case ROFFT_HEAD: 1907 post_bl_head(mdoc); 1908 return; 1909 case ROFFT_BODY: 1910 break; 1911 default: 1912 return; 1913 } 1914 if (nbody->end != ENDBODY_NOT) 1915 return; 1916 1917 /* 1918 * Up to the first item, move nodes before the list, 1919 * but leave transparent nodes where they are 1920 * if they precede an item. 1921 * The next non-transparent node is kept in nchild. 1922 * It only needs to be updated after a non-transparent 1923 * node was moved out, and at the very beginning 1924 * when no node at all was moved yet. 1925 */ 1926 1927 nchild = mdoc->last; 1928 for (;;) { 1929 if (nchild == mdoc->last) 1930 nchild = roff_node_child(nbody); 1931 if (nchild == NULL) { 1932 mdoc->last = nbody; 1933 mandoc_msg(MANDOCERR_BLK_EMPTY, 1934 nbody->line, nbody->pos, "Bl"); 1935 return; 1936 } 1937 if (nchild->tok == MDOC_It) { 1938 mdoc->last = nbody; 1939 break; 1940 } 1941 mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line, 1942 nbody->child->pos, "%s", roff_name[nbody->child->tok]); 1943 if (nbody->parent->prev == NULL) { 1944 mdoc->last = nbody->parent->parent; 1945 mdoc->next = ROFF_NEXT_CHILD; 1946 } else { 1947 mdoc->last = nbody->parent->prev; 1948 mdoc->next = ROFF_NEXT_SIBLING; 1949 } 1950 roff_node_relink(mdoc, nbody->child); 1951 } 1952 1953 /* 1954 * We have reached the first item, 1955 * so moving nodes out is no longer possible. 1956 * But in .Bl -column, the first rows may be implicit, 1957 * that is, they may not start with .It macros. 1958 * Such rows may be followed by nodes generated on the 1959 * roff level, for example .TS. 1960 * Wrap such roff nodes into an implicit row. 1961 */ 1962 1963 while (nchild != NULL) { 1964 if (nchild->tok == MDOC_It) { 1965 nchild = roff_node_next(nchild); 1966 continue; 1967 } 1968 nnext = nchild->next; 1969 mdoc->last = nchild->prev; 1970 mdoc->next = ROFF_NEXT_SIBLING; 1971 roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); 1972 roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); 1973 mdoc->next = ROFF_NEXT_SIBLING; 1974 roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); 1975 while (nchild->tok != MDOC_It) { 1976 roff_node_relink(mdoc, nchild); 1977 if (nnext == NULL) 1978 break; 1979 nchild = nnext; 1980 nnext = nchild->next; 1981 mdoc->next = ROFF_NEXT_SIBLING; 1982 } 1983 mdoc->last = nbody; 1984 } 1985 1986 if (mdoc->meta.os_e != MANDOC_OS_NETBSD) 1987 return; 1988 1989 prev_Er = NULL; 1990 for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) { 1991 if (nchild->tok != MDOC_It) 1992 continue; 1993 if ((nnext = nchild->head->child) == NULL) 1994 continue; 1995 if (nnext->type == ROFFT_BLOCK) 1996 nnext = nnext->body->child; 1997 if (nnext == NULL || nnext->tok != MDOC_Er) 1998 continue; 1999 nnext = nnext->child; 2000 if (prev_Er != NULL) { 2001 order = strcmp(prev_Er, nnext->string); 2002 if (order > 0) 2003 mandoc_msg(MANDOCERR_ER_ORDER, 2004 nnext->line, nnext->pos, 2005 "Er %s %s (NetBSD)", 2006 prev_Er, nnext->string); 2007 else if (order == 0) 2008 mandoc_msg(MANDOCERR_ER_REP, 2009 nnext->line, nnext->pos, 2010 "Er %s (NetBSD)", prev_Er); 2011 } 2012 prev_Er = nnext->string; 2013 } 2014} 2015 2016static void 2017post_bk(POST_ARGS) 2018{ 2019 struct roff_node *n; 2020 2021 n = mdoc->last; 2022 2023 if (n->type == ROFFT_BLOCK && n->body->child == NULL) { 2024 mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk"); 2025 roff_node_delete(mdoc, n); 2026 } 2027} 2028 2029static void 2030post_sm(POST_ARGS) 2031{ 2032 struct roff_node *nch; 2033 2034 nch = mdoc->last->child; 2035 2036 if (nch == NULL) { 2037 mdoc->flags ^= MDOC_SMOFF; 2038 return; 2039 } 2040 2041 assert(nch->type == ROFFT_TEXT); 2042 2043 if ( ! strcmp(nch->string, "on")) { 2044 mdoc->flags &= ~MDOC_SMOFF; 2045 return; 2046 } 2047 if ( ! strcmp(nch->string, "off")) { 2048 mdoc->flags |= MDOC_SMOFF; 2049 return; 2050 } 2051 2052 mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos, 2053 "%s %s", roff_name[mdoc->last->tok], nch->string); 2054 roff_node_relink(mdoc, nch); 2055 return; 2056} 2057 2058static void 2059post_root(POST_ARGS) 2060{ 2061 struct roff_node *n; 2062 2063 /* Add missing prologue data. */ 2064 2065 if (mdoc->meta.date == NULL) 2066 mdoc->meta.date = mandoc_normdate(NULL, NULL); 2067 2068 if (mdoc->meta.title == NULL) { 2069 mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF"); 2070 mdoc->meta.title = mandoc_strdup("UNTITLED"); 2071 } 2072 2073 if (mdoc->meta.vol == NULL) 2074 mdoc->meta.vol = mandoc_strdup("LOCAL"); 2075 2076 if (mdoc->meta.os == NULL) { 2077 mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL); 2078 mdoc->meta.os = mandoc_strdup(""); 2079 } else if (mdoc->meta.os_e && 2080 (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0) 2081 mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0, 2082 mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 2083 "(OpenBSD)" : "(NetBSD)"); 2084 2085 if (mdoc->meta.arch != NULL && 2086 arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) { 2087 n = mdoc->meta.first->child; 2088 while (n->tok != MDOC_Dt || 2089 n->child == NULL || 2090 n->child->next == NULL || 2091 n->child->next->next == NULL) 2092 n = n->next; 2093 n = n->child->next->next; 2094 mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos, 2095 "Dt ... %s %s", mdoc->meta.arch, 2096 mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 2097 "(OpenBSD)" : "(NetBSD)"); 2098 } 2099 2100 /* Check that we begin with a proper `Sh'. */ 2101 2102 n = mdoc->meta.first->child; 2103 while (n != NULL && 2104 (n->type == ROFFT_COMMENT || 2105 (n->tok >= MDOC_Dd && 2106 mdoc_macro(n->tok)->flags & MDOC_PROLOGUE))) 2107 n = n->next; 2108 2109 if (n == NULL) 2110 mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL); 2111 else if (n->tok != MDOC_Sh) 2112 mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos, 2113 "%s", roff_name[n->tok]); 2114} 2115 2116static void 2117post_rs(POST_ARGS) 2118{ 2119 struct roff_node *np, *nch, *next, *prev; 2120 int i, j; 2121 2122 np = mdoc->last; 2123 2124 if (np->type != ROFFT_BODY) 2125 return; 2126 2127 if (np->child == NULL) { 2128 mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs"); 2129 return; 2130 } 2131 2132 /* 2133 * The full `Rs' block needs special handling to order the 2134 * sub-elements according to `rsord'. Pick through each element 2135 * and correctly order it. This is an insertion sort. 2136 */ 2137 2138 next = NULL; 2139 for (nch = np->child->next; nch != NULL; nch = next) { 2140 /* Determine order number of this child. */ 2141 for (i = 0; i < RSORD_MAX; i++) 2142 if (rsord[i] == nch->tok) 2143 break; 2144 2145 if (i == RSORD_MAX) { 2146 mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos, 2147 "%s", roff_name[nch->tok]); 2148 i = -1; 2149 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) 2150 np->norm->Rs.quote_T++; 2151 2152 /* 2153 * Remove this child from the chain. This somewhat 2154 * repeats roff_node_unlink(), but since we're 2155 * just re-ordering, there's no need for the 2156 * full unlink process. 2157 */ 2158 2159 if ((next = nch->next) != NULL) 2160 next->prev = nch->prev; 2161 2162 if ((prev = nch->prev) != NULL) 2163 prev->next = nch->next; 2164 2165 nch->prev = nch->next = NULL; 2166 2167 /* 2168 * Scan back until we reach a node that's 2169 * to be ordered before this child. 2170 */ 2171 2172 for ( ; prev ; prev = prev->prev) { 2173 /* Determine order of `prev'. */ 2174 for (j = 0; j < RSORD_MAX; j++) 2175 if (rsord[j] == prev->tok) 2176 break; 2177 if (j == RSORD_MAX) 2178 j = -1; 2179 2180 if (j <= i) 2181 break; 2182 } 2183 2184 /* 2185 * Set this child back into its correct place 2186 * in front of the `prev' node. 2187 */ 2188 2189 nch->prev = prev; 2190 2191 if (prev == NULL) { 2192 np->child->prev = nch; 2193 nch->next = np->child; 2194 np->child = nch; 2195 } else { 2196 if (prev->next) 2197 prev->next->prev = nch; 2198 nch->next = prev->next; 2199 prev->next = nch; 2200 } 2201 } 2202} 2203 2204/* 2205 * For some arguments of some macros, 2206 * convert all breakable hyphens into ASCII_HYPH. 2207 */ 2208static void 2209post_hyph(POST_ARGS) 2210{ 2211 struct roff_node *n, *nch; 2212 char *cp; 2213 2214 n = mdoc->last; 2215 for (nch = n->child; nch != NULL; nch = nch->next) { 2216 if (nch->type != ROFFT_TEXT) 2217 continue; 2218 cp = nch->string; 2219 if (*cp == '\0') 2220 continue; 2221 while (*(++cp) != '\0') 2222 if (*cp == '-' && 2223 isalpha((unsigned char)cp[-1]) && 2224 isalpha((unsigned char)cp[1])) { 2225 if (n->tag == NULL && n->flags & NODE_ID) 2226 n->tag = mandoc_strdup(nch->string); 2227 *cp = ASCII_HYPH; 2228 } 2229 } 2230} 2231 2232static void 2233post_ns(POST_ARGS) 2234{ 2235 struct roff_node *n; 2236 2237 n = mdoc->last; 2238 if (n->flags & NODE_LINE || 2239 (n->next != NULL && n->next->flags & NODE_DELIMC)) 2240 mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL); 2241} 2242 2243static void 2244post_sx(POST_ARGS) 2245{ 2246 post_delim(mdoc); 2247 post_hyph(mdoc); 2248} 2249 2250static void 2251post_sh(POST_ARGS) 2252{ 2253 post_section(mdoc); 2254 2255 switch (mdoc->last->type) { 2256 case ROFFT_HEAD: 2257 post_sh_head(mdoc); 2258 break; 2259 case ROFFT_BODY: 2260 switch (mdoc->lastsec) { 2261 case SEC_NAME: 2262 post_sh_name(mdoc); 2263 break; 2264 case SEC_SEE_ALSO: 2265 post_sh_see_also(mdoc); 2266 break; 2267 case SEC_AUTHORS: 2268 post_sh_authors(mdoc); 2269 break; 2270 default: 2271 break; 2272 } 2273 break; 2274 default: 2275 break; 2276 } 2277} 2278 2279static void 2280post_sh_name(POST_ARGS) 2281{ 2282 struct roff_node *n; 2283 int hasnm, hasnd; 2284 2285 hasnm = hasnd = 0; 2286 2287 for (n = mdoc->last->child; n != NULL; n = n->next) { 2288 switch (n->tok) { 2289 case MDOC_Nm: 2290 if (hasnm && n->child != NULL) 2291 mandoc_msg(MANDOCERR_NAMESEC_PUNCT, 2292 n->line, n->pos, 2293 "Nm %s", n->child->string); 2294 hasnm = 1; 2295 continue; 2296 case MDOC_Nd: 2297 hasnd = 1; 2298 if (n->next != NULL) 2299 mandoc_msg(MANDOCERR_NAMESEC_ND, 2300 n->line, n->pos, NULL); 2301 break; 2302 case TOKEN_NONE: 2303 if (n->type == ROFFT_TEXT && 2304 n->string[0] == ',' && n->string[1] == '\0' && 2305 n->next != NULL && n->next->tok == MDOC_Nm) { 2306 n = n->next; 2307 continue; 2308 } 2309 /* FALLTHROUGH */ 2310 default: 2311 mandoc_msg(MANDOCERR_NAMESEC_BAD, 2312 n->line, n->pos, "%s", roff_name[n->tok]); 2313 continue; 2314 } 2315 break; 2316 } 2317 2318 if ( ! hasnm) 2319 mandoc_msg(MANDOCERR_NAMESEC_NONM, 2320 mdoc->last->line, mdoc->last->pos, NULL); 2321 if ( ! hasnd) 2322 mandoc_msg(MANDOCERR_NAMESEC_NOND, 2323 mdoc->last->line, mdoc->last->pos, NULL); 2324} 2325 2326static void 2327post_sh_see_also(POST_ARGS) 2328{ 2329 const struct roff_node *n; 2330 const char *name, *sec; 2331 const char *lastname, *lastsec, *lastpunct; 2332 int cmp; 2333 2334 n = mdoc->last->child; 2335 lastname = lastsec = lastpunct = NULL; 2336 while (n != NULL) { 2337 if (n->tok != MDOC_Xr || 2338 n->child == NULL || 2339 n->child->next == NULL) 2340 break; 2341 2342 /* Process one .Xr node. */ 2343 2344 name = n->child->string; 2345 sec = n->child->next->string; 2346 if (lastsec != NULL) { 2347 if (lastpunct[0] != ',' || lastpunct[1] != '\0') 2348 mandoc_msg(MANDOCERR_XR_PUNCT, n->line, 2349 n->pos, "%s before %s(%s)", 2350 lastpunct, name, sec); 2351 cmp = strcmp(lastsec, sec); 2352 if (cmp > 0) 2353 mandoc_msg(MANDOCERR_XR_ORDER, n->line, 2354 n->pos, "%s(%s) after %s(%s)", 2355 name, sec, lastname, lastsec); 2356 else if (cmp == 0 && 2357 strcasecmp(lastname, name) > 0) 2358 mandoc_msg(MANDOCERR_XR_ORDER, n->line, 2359 n->pos, "%s after %s", name, lastname); 2360 } 2361 lastname = name; 2362 lastsec = sec; 2363 2364 /* Process the following node. */ 2365 2366 n = n->next; 2367 if (n == NULL) 2368 break; 2369 if (n->tok == MDOC_Xr) { 2370 lastpunct = "none"; 2371 continue; 2372 } 2373 if (n->type != ROFFT_TEXT) 2374 break; 2375 for (name = n->string; *name != '\0'; name++) 2376 if (isalpha((const unsigned char)*name)) 2377 return; 2378 lastpunct = n->string; 2379 if (n->next == NULL || n->next->tok == MDOC_Rs) 2380 mandoc_msg(MANDOCERR_XR_PUNCT, n->line, 2381 n->pos, "%s after %s(%s)", 2382 lastpunct, lastname, lastsec); 2383 n = n->next; 2384 } 2385} 2386 2387static int 2388child_an(const struct roff_node *n) 2389{ 2390 2391 for (n = n->child; n != NULL; n = n->next) 2392 if ((n->tok == MDOC_An && n->child != NULL) || child_an(n)) 2393 return 1; 2394 return 0; 2395} 2396 2397static void 2398post_sh_authors(POST_ARGS) 2399{ 2400 2401 if ( ! child_an(mdoc->last)) 2402 mandoc_msg(MANDOCERR_AN_MISSING, 2403 mdoc->last->line, mdoc->last->pos, NULL); 2404} 2405 2406/* 2407 * Return an upper bound for the string distance (allowing 2408 * transpositions). Not a full Levenshtein implementation 2409 * because Levenshtein is quadratic in the string length 2410 * and this function is called for every standard name, 2411 * so the check for each custom name would be cubic. 2412 * The following crude heuristics is linear, resulting 2413 * in quadratic behaviour for checking one custom name, 2414 * which does not cause measurable slowdown. 2415 */ 2416static int 2417similar(const char *s1, const char *s2) 2418{ 2419 const int maxdist = 3; 2420 int dist = 0; 2421 2422 while (s1[0] != '\0' && s2[0] != '\0') { 2423 if (s1[0] == s2[0]) { 2424 s1++; 2425 s2++; 2426 continue; 2427 } 2428 if (++dist > maxdist) 2429 return INT_MAX; 2430 if (s1[1] == s2[1]) { /* replacement */ 2431 s1++; 2432 s2++; 2433 } else if (s1[0] == s2[1] && s1[1] == s2[0]) { 2434 s1 += 2; /* transposition */ 2435 s2 += 2; 2436 } else if (s1[0] == s2[1]) /* insertion */ 2437 s2++; 2438 else if (s1[1] == s2[0]) /* deletion */ 2439 s1++; 2440 else 2441 return INT_MAX; 2442 } 2443 dist += strlen(s1) + strlen(s2); 2444 return dist > maxdist ? INT_MAX : dist; 2445} 2446 2447static void 2448post_sh_head(POST_ARGS) 2449{ 2450 struct roff_node *nch; 2451 const char *goodsec; 2452 const char *const *testsec; 2453 int dist, mindist; 2454 enum roff_sec sec; 2455 2456 /* 2457 * Process a new section. Sections are either "named" or 2458 * "custom". Custom sections are user-defined, while named ones 2459 * follow a conventional order and may only appear in certain 2460 * manual sections. 2461 */ 2462 2463 sec = mdoc->last->sec; 2464 2465 /* The NAME should be first. */ 2466 2467 if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) 2468 mandoc_msg(MANDOCERR_NAMESEC_FIRST, 2469 mdoc->last->line, mdoc->last->pos, "Sh %s", 2470 sec != SEC_CUSTOM ? secnames[sec] : 2471 (nch = mdoc->last->child) == NULL ? "" : 2472 nch->type == ROFFT_TEXT ? nch->string : 2473 roff_name[nch->tok]); 2474 2475 /* The SYNOPSIS gets special attention in other areas. */ 2476 2477 if (sec == SEC_SYNOPSIS) { 2478 roff_setreg(mdoc->roff, "nS", 1, '='); 2479 mdoc->flags |= MDOC_SYNOPSIS; 2480 } else { 2481 roff_setreg(mdoc->roff, "nS", 0, '='); 2482 mdoc->flags &= ~MDOC_SYNOPSIS; 2483 } 2484 if (sec == SEC_DESCRIPTION) 2485 fn_prio = TAG_STRONG; 2486 2487 /* Mark our last section. */ 2488 2489 mdoc->lastsec = sec; 2490 2491 /* We don't care about custom sections after this. */ 2492 2493 if (sec == SEC_CUSTOM) { 2494 if ((nch = mdoc->last->child) == NULL || 2495 nch->type != ROFFT_TEXT || nch->next != NULL) 2496 return; 2497 goodsec = NULL; 2498 mindist = INT_MAX; 2499 for (testsec = secnames + 1; *testsec != NULL; testsec++) { 2500 dist = similar(nch->string, *testsec); 2501 if (dist < mindist) { 2502 goodsec = *testsec; 2503 mindist = dist; 2504 } 2505 } 2506 if (goodsec != NULL) 2507 mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos, 2508 "Sh %s instead of %s", nch->string, goodsec); 2509 return; 2510 } 2511 2512 /* 2513 * Check whether our non-custom section is being repeated or is 2514 * out of order. 2515 */ 2516 2517 if (sec == mdoc->lastnamed) 2518 mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line, 2519 mdoc->last->pos, "Sh %s", secnames[sec]); 2520 2521 if (sec < mdoc->lastnamed) 2522 mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line, 2523 mdoc->last->pos, "Sh %s", secnames[sec]); 2524 2525 /* Mark the last named section. */ 2526 2527 mdoc->lastnamed = sec; 2528 2529 /* Check particular section/manual conventions. */ 2530 2531 if (mdoc->meta.msec == NULL) 2532 return; 2533 2534 goodsec = NULL; 2535 switch (sec) { 2536 case SEC_ERRORS: 2537 if (*mdoc->meta.msec == '4') 2538 break; 2539 goodsec = "2, 3, 4, 9"; 2540 /* FALLTHROUGH */ 2541 case SEC_RETURN_VALUES: 2542 case SEC_LIBRARY: 2543 if (*mdoc->meta.msec == '2') 2544 break; 2545 if (*mdoc->meta.msec == '3') 2546 break; 2547 if (NULL == goodsec) 2548 goodsec = "2, 3, 9"; 2549 /* FALLTHROUGH */ 2550 case SEC_CONTEXT: 2551 if (*mdoc->meta.msec == '9') 2552 break; 2553 if (NULL == goodsec) 2554 goodsec = "9"; 2555 mandoc_msg(MANDOCERR_SEC_MSEC, 2556 mdoc->last->line, mdoc->last->pos, 2557 "Sh %s for %s only", secnames[sec], goodsec); 2558 break; 2559 default: 2560 break; 2561 } 2562} 2563 2564static void 2565post_xr(POST_ARGS) 2566{ 2567 struct roff_node *n, *nch; 2568 2569 n = mdoc->last; 2570 nch = n->child; 2571 if (nch->next == NULL) { 2572 mandoc_msg(MANDOCERR_XR_NOSEC, 2573 n->line, n->pos, "Xr %s", nch->string); 2574 } else { 2575 assert(nch->next == n->last); 2576 if(mandoc_xr_add(nch->next->string, nch->string, 2577 nch->line, nch->pos)) 2578 mandoc_msg(MANDOCERR_XR_SELF, 2579 nch->line, nch->pos, "Xr %s %s", 2580 nch->string, nch->next->string); 2581 } 2582 post_delim_nb(mdoc); 2583} 2584 2585static void 2586post_section(POST_ARGS) 2587{ 2588 struct roff_node *n, *nch; 2589 char *cp, *tag; 2590 2591 n = mdoc->last; 2592 switch (n->type) { 2593 case ROFFT_BLOCK: 2594 post_prevpar(mdoc); 2595 return; 2596 case ROFFT_HEAD: 2597 tag = NULL; 2598 deroff(&tag, n); 2599 if (tag != NULL) { 2600 for (cp = tag; *cp != '\0'; cp++) 2601 if (*cp == ' ') 2602 *cp = '_'; 2603 if ((nch = n->child) != NULL && 2604 nch->type == ROFFT_TEXT && 2605 strcmp(nch->string, tag) == 0) 2606 tag_put(NULL, TAG_STRONG, n); 2607 else 2608 tag_put(tag, TAG_FALLBACK, n); 2609 free(tag); 2610 } 2611 post_delim(mdoc); 2612 post_hyph(mdoc); 2613 return; 2614 case ROFFT_BODY: 2615 break; 2616 default: 2617 return; 2618 } 2619 if ((nch = n->child) != NULL && 2620 (nch->tok == MDOC_Pp || nch->tok == ROFF_br || 2621 nch->tok == ROFF_sp)) { 2622 mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, 2623 "%s after %s", roff_name[nch->tok], 2624 roff_name[n->tok]); 2625 roff_node_delete(mdoc, nch); 2626 } 2627 if ((nch = n->last) != NULL && 2628 (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) { 2629 mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, 2630 "%s at the end of %s", roff_name[nch->tok], 2631 roff_name[n->tok]); 2632 roff_node_delete(mdoc, nch); 2633 } 2634} 2635 2636static void 2637post_prevpar(POST_ARGS) 2638{ 2639 struct roff_node *n, *np; 2640 2641 n = mdoc->last; 2642 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) 2643 return; 2644 if ((np = roff_node_prev(n)) == NULL) 2645 return; 2646 2647 /* 2648 * Don't allow `Pp' prior to a paragraph-type 2649 * block: `Pp' or non-compact `Bd' or `Bl'. 2650 */ 2651 2652 if (np->tok != MDOC_Pp && np->tok != ROFF_br) 2653 return; 2654 if (n->tok == MDOC_Bl && n->norm->Bl.comp) 2655 return; 2656 if (n->tok == MDOC_Bd && n->norm->Bd.comp) 2657 return; 2658 if (n->tok == MDOC_It && n->parent->norm->Bl.comp) 2659 return; 2660 2661 mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, 2662 "%s before %s", roff_name[np->tok], roff_name[n->tok]); 2663 roff_node_delete(mdoc, np); 2664} 2665 2666static void 2667post_par(POST_ARGS) 2668{ 2669 struct roff_node *np; 2670 2671 fn_prio = TAG_STRONG; 2672 post_prevpar(mdoc); 2673 2674 np = mdoc->last; 2675 if (np->child != NULL) 2676 mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos, 2677 "%s %s", roff_name[np->tok], np->child->string); 2678} 2679 2680static void 2681post_dd(POST_ARGS) 2682{ 2683 struct roff_node *n; 2684 2685 n = mdoc->last; 2686 n->flags |= NODE_NOPRT; 2687 2688 if (mdoc->meta.date != NULL) { 2689 mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd"); 2690 free(mdoc->meta.date); 2691 } else if (mdoc->flags & MDOC_PBODY) 2692 mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd"); 2693 else if (mdoc->meta.title != NULL) 2694 mandoc_msg(MANDOCERR_PROLOG_ORDER, 2695 n->line, n->pos, "Dd after Dt"); 2696 else if (mdoc->meta.os != NULL) 2697 mandoc_msg(MANDOCERR_PROLOG_ORDER, 2698 n->line, n->pos, "Dd after Os"); 2699 2700 if (mdoc->quick && n != NULL) 2701 mdoc->meta.date = mandoc_strdup(""); 2702 else 2703 mdoc->meta.date = mandoc_normdate(n->child, n); 2704} 2705 2706static void 2707post_dt(POST_ARGS) 2708{ 2709 struct roff_node *nn, *n; 2710 const char *cp; 2711 char *p; 2712 2713 n = mdoc->last; 2714 n->flags |= NODE_NOPRT; 2715 2716 if (mdoc->flags & MDOC_PBODY) { 2717 mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt"); 2718 return; 2719 } 2720 2721 if (mdoc->meta.title != NULL) 2722 mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt"); 2723 else if (mdoc->meta.os != NULL) 2724 mandoc_msg(MANDOCERR_PROLOG_ORDER, 2725 n->line, n->pos, "Dt after Os"); 2726 2727 free(mdoc->meta.title); 2728 free(mdoc->meta.msec); 2729 free(mdoc->meta.vol); 2730 free(mdoc->meta.arch); 2731 2732 mdoc->meta.title = NULL; 2733 mdoc->meta.msec = NULL; 2734 mdoc->meta.vol = NULL; 2735 mdoc->meta.arch = NULL; 2736 2737 /* Mandatory first argument: title. */ 2738 2739 nn = n->child; 2740 if (nn == NULL || *nn->string == '\0') { 2741 mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt"); 2742 mdoc->meta.title = mandoc_strdup("UNTITLED"); 2743 } else { 2744 mdoc->meta.title = mandoc_strdup(nn->string); 2745 2746 /* Check that all characters are uppercase. */ 2747 2748 for (p = nn->string; *p != '\0'; p++) 2749 if (islower((unsigned char)*p)) { 2750 mandoc_msg(MANDOCERR_TITLE_CASE, nn->line, 2751 nn->pos + (int)(p - nn->string), 2752 "Dt %s", nn->string); 2753 break; 2754 } 2755 } 2756 2757 /* Mandatory second argument: section. */ 2758 2759 if (nn != NULL) 2760 nn = nn->next; 2761 2762 if (nn == NULL) { 2763 mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos, 2764 "Dt %s", mdoc->meta.title); 2765 mdoc->meta.vol = mandoc_strdup("LOCAL"); 2766 return; /* msec and arch remain NULL. */ 2767 } 2768 2769 mdoc->meta.msec = mandoc_strdup(nn->string); 2770 2771 /* Infer volume title from section number. */ 2772 2773 cp = mandoc_a2msec(nn->string); 2774 if (cp == NULL) { 2775 mandoc_msg(MANDOCERR_MSEC_BAD, 2776 nn->line, nn->pos, "Dt ... %s", nn->string); 2777 mdoc->meta.vol = mandoc_strdup(nn->string); 2778 } else { 2779 mdoc->meta.vol = mandoc_strdup(cp); 2780 if (mdoc->filesec != '\0' && 2781 mdoc->filesec != *nn->string && 2782 *nn->string >= '1' && *nn->string <= '9') 2783 mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos, 2784 "*.%c vs Dt ... %c", mdoc->filesec, *nn->string); 2785 } 2786 2787 /* Optional third argument: architecture. */ 2788 2789 if ((nn = nn->next) == NULL) 2790 return; 2791 2792 for (p = nn->string; *p != '\0'; p++) 2793 *p = tolower((unsigned char)*p); 2794 mdoc->meta.arch = mandoc_strdup(nn->string); 2795 2796 /* Ignore fourth and later arguments. */ 2797 2798 if ((nn = nn->next) != NULL) 2799 mandoc_msg(MANDOCERR_ARG_EXCESS, 2800 nn->line, nn->pos, "Dt ... %s", nn->string); 2801} 2802 2803static void 2804post_bx(POST_ARGS) 2805{ 2806 struct roff_node *n, *nch; 2807 const char *macro; 2808 2809 post_delim_nb(mdoc); 2810 2811 n = mdoc->last; 2812 nch = n->child; 2813 2814 if (nch != NULL) { 2815 macro = !strcmp(nch->string, "Open") ? "Ox" : 2816 !strcmp(nch->string, "Net") ? "Nx" : 2817 !strcmp(nch->string, "Free") ? "Fx" : 2818 !strcmp(nch->string, "DragonFly") ? "Dx" : NULL; 2819 if (macro != NULL) 2820 mandoc_msg(MANDOCERR_BX, 2821 n->line, n->pos, "%s", macro); 2822 mdoc->last = nch; 2823 nch = nch->next; 2824 mdoc->next = ROFF_NEXT_SIBLING; 2825 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2826 mdoc->last->flags |= NODE_NOSRC; 2827 mdoc->next = ROFF_NEXT_SIBLING; 2828 } else 2829 mdoc->next = ROFF_NEXT_CHILD; 2830 roff_word_alloc(mdoc, n->line, n->pos, "BSD"); 2831 mdoc->last->flags |= NODE_NOSRC; 2832 2833 if (nch == NULL) { 2834 mdoc->last = n; 2835 return; 2836 } 2837 2838 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2839 mdoc->last->flags |= NODE_NOSRC; 2840 mdoc->next = ROFF_NEXT_SIBLING; 2841 roff_word_alloc(mdoc, n->line, n->pos, "-"); 2842 mdoc->last->flags |= NODE_NOSRC; 2843 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2844 mdoc->last->flags |= NODE_NOSRC; 2845 mdoc->last = n; 2846 2847 /* 2848 * Make `Bx's second argument always start with an uppercase 2849 * letter. Groff checks if it's an "accepted" term, but we just 2850 * uppercase blindly. 2851 */ 2852 2853 *nch->string = (char)toupper((unsigned char)*nch->string); 2854} 2855 2856static void 2857post_os(POST_ARGS) 2858{ 2859#ifndef OSNAME 2860 struct utsname utsname; 2861#endif 2862 struct roff_node *n; 2863 2864 n = mdoc->last; 2865 n->flags |= NODE_NOPRT; 2866 2867 if (mdoc->meta.os != NULL) 2868 mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os"); 2869 else if (mdoc->flags & MDOC_PBODY) 2870 mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os"); 2871 2872 post_delim(mdoc); 2873 2874 /* 2875 * Set the operating system by way of the `Os' macro. 2876 * The order of precedence is: 2877 * 1. the argument of the `Os' macro, unless empty 2878 * 2. the -Ios=foo command line argument, if provided 2879 * 3. -DOSNAME="\"foo\"", if provided during compilation 2880 * 4. "sysname release" from uname(3) 2881 */ 2882 2883 free(mdoc->meta.os); 2884 mdoc->meta.os = NULL; 2885 deroff(&mdoc->meta.os, n); 2886 if (mdoc->meta.os) 2887 goto out; 2888 2889 if (mdoc->os_s != NULL) { 2890 mdoc->meta.os = mandoc_strdup(mdoc->os_s); 2891 goto out; 2892 } 2893 2894#ifdef OSNAME 2895 mdoc->meta.os = mandoc_strdup(OSNAME); 2896#else /*!OSNAME */ 2897 if (mdoc->os_r == NULL) { 2898 if (uname(&utsname) == -1) { 2899 mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os"); 2900 mdoc->os_r = mandoc_strdup("UNKNOWN"); 2901 } else 2902 mandoc_asprintf(&mdoc->os_r, "%s %s", 2903 utsname.sysname, utsname.release); 2904 } 2905 mdoc->meta.os = mandoc_strdup(mdoc->os_r); 2906#endif /*!OSNAME*/ 2907 2908out: 2909 if (mdoc->meta.os_e == MANDOC_OS_OTHER) { 2910 if (strstr(mdoc->meta.os, "OpenBSD") != NULL) 2911 mdoc->meta.os_e = MANDOC_OS_OPENBSD; 2912 else if (strstr(mdoc->meta.os, "NetBSD") != NULL) 2913 mdoc->meta.os_e = MANDOC_OS_NETBSD; 2914 } 2915 2916 /* 2917 * This is the earliest point where we can check 2918 * Mdocdate conventions because we don't know 2919 * the operating system earlier. 2920 */ 2921 2922 if (n->child != NULL) 2923 mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos, 2924 "Os %s (%s)", n->child->string, 2925 mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 2926 "OpenBSD" : "NetBSD"); 2927 2928 while (n->tok != MDOC_Dd) 2929 if ((n = n->prev) == NULL) 2930 return; 2931 if ((n = n->child) == NULL) 2932 return; 2933 if (strncmp(n->string, "$" "Mdocdate", 9)) { 2934 if (mdoc->meta.os_e == MANDOC_OS_OPENBSD) 2935 mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line, 2936 n->pos, "Dd %s (OpenBSD)", n->string); 2937 } else { 2938 if (mdoc->meta.os_e == MANDOC_OS_NETBSD) 2939 mandoc_msg(MANDOCERR_MDOCDATE, n->line, 2940 n->pos, "Dd %s (NetBSD)", n->string); 2941 } 2942} 2943 2944enum roff_sec 2945mdoc_a2sec(const char *p) 2946{ 2947 int i; 2948 2949 for (i = 0; i < (int)SEC__MAX; i++) 2950 if (secnames[i] && 0 == strcmp(p, secnames[i])) 2951 return (enum roff_sec)i; 2952 2953 return SEC_CUSTOM; 2954} 2955 2956static size_t 2957macro2len(enum roff_tok macro) 2958{ 2959 2960 switch (macro) { 2961 case MDOC_Ad: 2962 return 12; 2963 case MDOC_Ao: 2964 return 12; 2965 case MDOC_An: 2966 return 12; 2967 case MDOC_Aq: 2968 return 12; 2969 case MDOC_Ar: 2970 return 12; 2971 case MDOC_Bo: 2972 return 12; 2973 case MDOC_Bq: 2974 return 12; 2975 case MDOC_Cd: 2976 return 12; 2977 case MDOC_Cm: 2978 return 10; 2979 case MDOC_Do: 2980 return 10; 2981 case MDOC_Dq: 2982 return 12; 2983 case MDOC_Dv: 2984 return 12; 2985 case MDOC_Eo: 2986 return 12; 2987 case MDOC_Em: 2988 return 10; 2989 case MDOC_Er: 2990 return 17; 2991 case MDOC_Ev: 2992 return 15; 2993 case MDOC_Fa: 2994 return 12; 2995 case MDOC_Fl: 2996 return 10; 2997 case MDOC_Fo: 2998 return 16; 2999 case MDOC_Fn: 3000 return 16; 3001 case MDOC_Ic: 3002 return 10; 3003 case MDOC_Li: 3004 return 16; 3005 case MDOC_Ms: 3006 return 6; 3007 case MDOC_Nm: 3008 return 10; 3009 case MDOC_No: 3010 return 12; 3011 case MDOC_Oo: 3012 return 10; 3013 case MDOC_Op: 3014 return 14; 3015 case MDOC_Pa: 3016 return 32; 3017 case MDOC_Pf: 3018 return 12; 3019 case MDOC_Po: 3020 return 12; 3021 case MDOC_Pq: 3022 return 12; 3023 case MDOC_Ql: 3024 return 16; 3025 case MDOC_Qo: 3026 return 12; 3027 case MDOC_So: 3028 return 12; 3029 case MDOC_Sq: 3030 return 12; 3031 case MDOC_Sy: 3032 return 6; 3033 case MDOC_Sx: 3034 return 16; 3035 case MDOC_Tn: 3036 return 10; 3037 case MDOC_Va: 3038 return 12; 3039 case MDOC_Vt: 3040 return 12; 3041 case MDOC_Xr: 3042 return 10; 3043 default: 3044 break; 3045 }; 3046 return 0; 3047} 3048