mdoc_validate.c revision 1.94
1/* $Id: mdoc_validate.c,v 1.94 2011/09/18 10:25:28 schwarze Exp $ */ 2/* 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18#ifndef OSNAME 19#include <sys/utsname.h> 20#endif 21 22#include <sys/types.h> 23 24#include <assert.h> 25#include <ctype.h> 26#include <limits.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <time.h> 31 32#include "mdoc.h" 33#include "mandoc.h" 34#include "libmdoc.h" 35#include "libmandoc.h" 36 37/* FIXME: .Bl -diag can't have non-text children in HEAD. */ 38 39#define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n 40#define POST_ARGS struct mdoc *mdoc 41 42#define NUMSIZ 32 43#define DATESIZE 32 44 45enum check_ineq { 46 CHECK_LT, 47 CHECK_GT, 48 CHECK_EQ 49}; 50 51enum check_lvl { 52 CHECK_WARN, 53 CHECK_ERROR, 54}; 55 56typedef int (*v_pre)(PRE_ARGS); 57typedef int (*v_post)(POST_ARGS); 58 59struct valids { 60 v_pre *pre; 61 v_post *post; 62}; 63 64static int check_count(struct mdoc *, enum mdoc_type, 65 enum check_lvl, enum check_ineq, int); 66static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type); 67static void check_text(struct mdoc *, int, int, char *); 68static void check_argv(struct mdoc *, 69 struct mdoc_node *, struct mdoc_argv *); 70static void check_args(struct mdoc *, struct mdoc_node *); 71 72static int concat(struct mdoc *, char *, 73 const struct mdoc_node *, size_t); 74static enum mdoc_sec a2sec(const char *); 75static size_t macro2len(enum mdoct); 76 77static int ebool(POST_ARGS); 78static int berr_ge1(POST_ARGS); 79static int bwarn_ge1(POST_ARGS); 80static int ewarn_eq0(POST_ARGS); 81static int ewarn_eq1(POST_ARGS); 82static int ewarn_ge1(POST_ARGS); 83static int ewarn_le1(POST_ARGS); 84static int hwarn_eq0(POST_ARGS); 85static int hwarn_eq1(POST_ARGS); 86static int hwarn_ge1(POST_ARGS); 87static int hwarn_le1(POST_ARGS); 88 89static int post_an(POST_ARGS); 90static int post_at(POST_ARGS); 91static int post_bf(POST_ARGS); 92static int post_bl(POST_ARGS); 93static int post_bl_block(POST_ARGS); 94static int post_bl_block_width(POST_ARGS); 95static int post_bl_block_tag(POST_ARGS); 96static int post_bl_head(POST_ARGS); 97static int post_bx(POST_ARGS); 98static int post_dd(POST_ARGS); 99static int post_dt(POST_ARGS); 100static int post_defaults(POST_ARGS); 101static int post_literal(POST_ARGS); 102static int post_eoln(POST_ARGS); 103static int post_it(POST_ARGS); 104static int post_lb(POST_ARGS); 105static int post_nm(POST_ARGS); 106static int post_ns(POST_ARGS); 107static int post_os(POST_ARGS); 108static int post_ignpar(POST_ARGS); 109static int post_prol(POST_ARGS); 110static int post_root(POST_ARGS); 111static int post_rs(POST_ARGS); 112static int post_sh(POST_ARGS); 113static int post_sh_body(POST_ARGS); 114static int post_sh_head(POST_ARGS); 115static int post_st(POST_ARGS); 116static int post_std(POST_ARGS); 117static int post_vt(POST_ARGS); 118static int pre_an(PRE_ARGS); 119static int pre_bd(PRE_ARGS); 120static int pre_bl(PRE_ARGS); 121static int pre_dd(PRE_ARGS); 122static int pre_display(PRE_ARGS); 123static int pre_dt(PRE_ARGS); 124static int pre_it(PRE_ARGS); 125static int pre_literal(PRE_ARGS); 126static int pre_os(PRE_ARGS); 127static int pre_par(PRE_ARGS); 128static int pre_sh(PRE_ARGS); 129static int pre_ss(PRE_ARGS); 130static int pre_std(PRE_ARGS); 131 132static v_post posts_an[] = { post_an, NULL }; 133static v_post posts_at[] = { post_at, post_defaults, NULL }; 134static v_post posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL }; 135static v_post posts_bf[] = { hwarn_le1, post_bf, NULL }; 136static v_post posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL }; 137static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL }; 138static v_post posts_bx[] = { post_bx, NULL }; 139static v_post posts_bool[] = { ebool, NULL }; 140static v_post posts_eoln[] = { post_eoln, NULL }; 141static v_post posts_defaults[] = { post_defaults, NULL }; 142static v_post posts_dd[] = { post_dd, post_prol, NULL }; 143static v_post posts_dl[] = { post_literal, bwarn_ge1, NULL }; 144static v_post posts_dt[] = { post_dt, post_prol, NULL }; 145static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL }; 146static v_post posts_it[] = { post_it, NULL }; 147static v_post posts_lb[] = { post_lb, NULL }; 148static v_post posts_nd[] = { berr_ge1, NULL }; 149static v_post posts_nm[] = { post_nm, NULL }; 150static v_post posts_notext[] = { ewarn_eq0, NULL }; 151static v_post posts_ns[] = { post_ns, NULL }; 152static v_post posts_os[] = { post_os, post_prol, NULL }; 153static v_post posts_rs[] = { post_rs, NULL }; 154static v_post posts_sh[] = { post_ignpar, hwarn_ge1, post_sh, NULL }; 155static v_post posts_sp[] = { ewarn_le1, NULL }; 156static v_post posts_ss[] = { post_ignpar, hwarn_ge1, NULL }; 157static v_post posts_st[] = { post_st, NULL }; 158static v_post posts_std[] = { post_std, NULL }; 159static v_post posts_text[] = { ewarn_ge1, NULL }; 160static v_post posts_text1[] = { ewarn_eq1, NULL }; 161static v_post posts_vt[] = { post_vt, NULL }; 162static v_post posts_wline[] = { bwarn_ge1, NULL }; 163static v_pre pres_an[] = { pre_an, NULL }; 164static v_pre pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL }; 165static v_pre pres_bl[] = { pre_bl, pre_par, NULL }; 166static v_pre pres_d1[] = { pre_display, NULL }; 167static v_pre pres_dl[] = { pre_literal, pre_display, NULL }; 168static v_pre pres_dd[] = { pre_dd, NULL }; 169static v_pre pres_dt[] = { pre_dt, NULL }; 170static v_pre pres_er[] = { NULL, NULL }; 171static v_pre pres_fd[] = { NULL, NULL }; 172static v_pre pres_it[] = { pre_it, pre_par, NULL }; 173static v_pre pres_os[] = { pre_os, NULL }; 174static v_pre pres_pp[] = { pre_par, NULL }; 175static v_pre pres_sh[] = { pre_sh, NULL }; 176static v_pre pres_ss[] = { pre_ss, NULL }; 177static v_pre pres_std[] = { pre_std, NULL }; 178 179static const struct valids mdoc_valids[MDOC_MAX] = { 180 { NULL, NULL }, /* Ap */ 181 { pres_dd, posts_dd }, /* Dd */ 182 { pres_dt, posts_dt }, /* Dt */ 183 { pres_os, posts_os }, /* Os */ 184 { pres_sh, posts_sh }, /* Sh */ 185 { pres_ss, posts_ss }, /* Ss */ 186 { pres_pp, posts_notext }, /* Pp */ 187 { pres_d1, posts_wline }, /* D1 */ 188 { pres_dl, posts_dl }, /* Dl */ 189 { pres_bd, posts_bd }, /* Bd */ 190 { NULL, NULL }, /* Ed */ 191 { pres_bl, posts_bl }, /* Bl */ 192 { NULL, NULL }, /* El */ 193 { pres_it, posts_it }, /* It */ 194 { NULL, NULL }, /* Ad */ 195 { pres_an, posts_an }, /* An */ 196 { NULL, posts_defaults }, /* Ar */ 197 { NULL, NULL }, /* Cd */ 198 { NULL, NULL }, /* Cm */ 199 { NULL, NULL }, /* Dv */ 200 { pres_er, NULL }, /* Er */ 201 { NULL, NULL }, /* Ev */ 202 { pres_std, posts_std }, /* Ex */ 203 { NULL, NULL }, /* Fa */ 204 { pres_fd, posts_text }, /* Fd */ 205 { NULL, NULL }, /* Fl */ 206 { NULL, NULL }, /* Fn */ 207 { NULL, NULL }, /* Ft */ 208 { NULL, NULL }, /* Ic */ 209 { NULL, posts_text1 }, /* In */ 210 { NULL, posts_defaults }, /* Li */ 211 { NULL, posts_nd }, /* Nd */ 212 { NULL, posts_nm }, /* Nm */ 213 { NULL, NULL }, /* Op */ 214 { NULL, NULL }, /* Ot */ 215 { NULL, posts_defaults }, /* Pa */ 216 { pres_std, posts_std }, /* Rv */ 217 { NULL, posts_st }, /* St */ 218 { NULL, NULL }, /* Va */ 219 { NULL, posts_vt }, /* Vt */ 220 { NULL, posts_text }, /* Xr */ 221 { NULL, posts_text }, /* %A */ 222 { NULL, posts_text }, /* %B */ /* FIXME: can be used outside Rs/Re. */ 223 { NULL, posts_text }, /* %D */ 224 { NULL, posts_text }, /* %I */ 225 { NULL, posts_text }, /* %J */ 226 { NULL, posts_text }, /* %N */ 227 { NULL, posts_text }, /* %O */ 228 { NULL, posts_text }, /* %P */ 229 { NULL, posts_text }, /* %R */ 230 { NULL, posts_text }, /* %T */ /* FIXME: can be used outside Rs/Re. */ 231 { NULL, posts_text }, /* %V */ 232 { NULL, NULL }, /* Ac */ 233 { NULL, NULL }, /* Ao */ 234 { NULL, NULL }, /* Aq */ 235 { NULL, posts_at }, /* At */ 236 { NULL, NULL }, /* Bc */ 237 { NULL, posts_bf }, /* Bf */ 238 { NULL, NULL }, /* Bo */ 239 { NULL, NULL }, /* Bq */ 240 { NULL, NULL }, /* Bsx */ 241 { NULL, posts_bx }, /* Bx */ 242 { NULL, posts_bool }, /* Db */ 243 { NULL, NULL }, /* Dc */ 244 { NULL, NULL }, /* Do */ 245 { NULL, NULL }, /* Dq */ 246 { NULL, NULL }, /* Ec */ 247 { NULL, NULL }, /* Ef */ 248 { NULL, NULL }, /* Em */ 249 { NULL, NULL }, /* Eo */ 250 { NULL, NULL }, /* Fx */ 251 { NULL, NULL }, /* Ms */ 252 { NULL, posts_notext }, /* No */ 253 { NULL, posts_ns }, /* Ns */ 254 { NULL, NULL }, /* Nx */ 255 { NULL, NULL }, /* Ox */ 256 { NULL, NULL }, /* Pc */ 257 { NULL, posts_text1 }, /* Pf */ 258 { NULL, NULL }, /* Po */ 259 { NULL, NULL }, /* Pq */ 260 { NULL, NULL }, /* Qc */ 261 { NULL, NULL }, /* Ql */ 262 { NULL, NULL }, /* Qo */ 263 { NULL, NULL }, /* Qq */ 264 { NULL, NULL }, /* Re */ 265 { NULL, posts_rs }, /* Rs */ 266 { NULL, NULL }, /* Sc */ 267 { NULL, NULL }, /* So */ 268 { NULL, NULL }, /* Sq */ 269 { NULL, posts_bool }, /* Sm */ 270 { NULL, NULL }, /* Sx */ 271 { NULL, NULL }, /* Sy */ 272 { NULL, NULL }, /* Tn */ 273 { NULL, NULL }, /* Ux */ 274 { NULL, NULL }, /* Xc */ 275 { NULL, NULL }, /* Xo */ 276 { NULL, posts_fo }, /* Fo */ 277 { NULL, NULL }, /* Fc */ 278 { NULL, NULL }, /* Oo */ 279 { NULL, NULL }, /* Oc */ 280 { NULL, posts_bk }, /* Bk */ 281 { NULL, NULL }, /* Ek */ 282 { NULL, posts_eoln }, /* Bt */ 283 { NULL, NULL }, /* Hf */ 284 { NULL, NULL }, /* Fr */ 285 { NULL, posts_eoln }, /* Ud */ 286 { NULL, posts_lb }, /* Lb */ 287 { NULL, posts_notext }, /* Lp */ 288 { NULL, NULL }, /* Lk */ 289 { NULL, posts_defaults }, /* Mt */ 290 { NULL, NULL }, /* Brq */ 291 { NULL, NULL }, /* Bro */ 292 { NULL, NULL }, /* Brc */ 293 { NULL, posts_text }, /* %C */ 294 { NULL, NULL }, /* Es */ 295 { NULL, NULL }, /* En */ 296 { NULL, NULL }, /* Dx */ 297 { NULL, posts_text }, /* %Q */ 298 { NULL, posts_notext }, /* br */ 299 { pres_pp, posts_sp }, /* sp */ 300 { NULL, posts_text1 }, /* %U */ 301 { NULL, NULL }, /* Ta */ 302}; 303 304#define RSORD_MAX 14 /* Number of `Rs' blocks. */ 305 306static const enum mdoct rsord[RSORD_MAX] = { 307 MDOC__A, 308 MDOC__T, 309 MDOC__B, 310 MDOC__I, 311 MDOC__J, 312 MDOC__R, 313 MDOC__N, 314 MDOC__V, 315 MDOC__P, 316 MDOC__Q, 317 MDOC__D, 318 MDOC__O, 319 MDOC__C, 320 MDOC__U 321}; 322 323static const char * const secnames[SEC__MAX] = { 324 NULL, 325 "NAME", 326 "LIBRARY", 327 "SYNOPSIS", 328 "DESCRIPTION", 329 "IMPLEMENTATION NOTES", 330 "RETURN VALUES", 331 "ENVIRONMENT", 332 "FILES", 333 "EXIT STATUS", 334 "EXAMPLES", 335 "DIAGNOSTICS", 336 "COMPATIBILITY", 337 "ERRORS", 338 "SEE ALSO", 339 "STANDARDS", 340 "HISTORY", 341 "AUTHORS", 342 "CAVEATS", 343 "BUGS", 344 "SECURITY CONSIDERATIONS", 345 NULL 346}; 347 348int 349mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n) 350{ 351 v_pre *p; 352 int line, pos; 353 char *tp; 354 355 switch (n->type) { 356 case (MDOC_TEXT): 357 tp = n->string; 358 line = n->line; 359 pos = n->pos; 360 check_text(mdoc, line, pos, tp); 361 /* FALLTHROUGH */ 362 case (MDOC_TBL): 363 /* FALLTHROUGH */ 364 case (MDOC_EQN): 365 /* FALLTHROUGH */ 366 case (MDOC_ROOT): 367 return(1); 368 default: 369 break; 370 } 371 372 check_args(mdoc, n); 373 374 if (NULL == mdoc_valids[n->tok].pre) 375 return(1); 376 for (p = mdoc_valids[n->tok].pre; *p; p++) 377 if ( ! (*p)(mdoc, n)) 378 return(0); 379 return(1); 380} 381 382 383int 384mdoc_valid_post(struct mdoc *mdoc) 385{ 386 v_post *p; 387 388 if (MDOC_VALID & mdoc->last->flags) 389 return(1); 390 mdoc->last->flags |= MDOC_VALID; 391 392 switch (mdoc->last->type) { 393 case (MDOC_TEXT): 394 /* FALLTHROUGH */ 395 case (MDOC_EQN): 396 /* FALLTHROUGH */ 397 case (MDOC_TBL): 398 return(1); 399 case (MDOC_ROOT): 400 return(post_root(mdoc)); 401 default: 402 break; 403 } 404 405 if (NULL == mdoc_valids[mdoc->last->tok].post) 406 return(1); 407 for (p = mdoc_valids[mdoc->last->tok].post; *p; p++) 408 if ( ! (*p)(mdoc)) 409 return(0); 410 411 return(1); 412} 413 414static int 415check_count(struct mdoc *m, enum mdoc_type type, 416 enum check_lvl lvl, enum check_ineq ineq, int val) 417{ 418 const char *p; 419 enum mandocerr t; 420 421 if (m->last->type != type) 422 return(1); 423 424 switch (ineq) { 425 case (CHECK_LT): 426 p = "less than "; 427 if (m->last->nchild < val) 428 return(1); 429 break; 430 case (CHECK_GT): 431 p = "more than "; 432 if (m->last->nchild > val) 433 return(1); 434 break; 435 case (CHECK_EQ): 436 p = ""; 437 if (val == m->last->nchild) 438 return(1); 439 break; 440 default: 441 abort(); 442 /* NOTREACHED */ 443 } 444 445 t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT; 446 mandoc_vmsg(t, m->parse, m->last->line, m->last->pos, 447 "want %s%d children (have %d)", 448 p, val, m->last->nchild); 449 return(1); 450} 451 452static int 453berr_ge1(POST_ARGS) 454{ 455 456 return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0)); 457} 458 459static int 460bwarn_ge1(POST_ARGS) 461{ 462 return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0)); 463} 464 465static int 466ewarn_eq0(POST_ARGS) 467{ 468 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0)); 469} 470 471static int 472ewarn_eq1(POST_ARGS) 473{ 474 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1)); 475} 476 477static int 478ewarn_ge1(POST_ARGS) 479{ 480 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0)); 481} 482 483static int 484ewarn_le1(POST_ARGS) 485{ 486 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2)); 487} 488 489static int 490hwarn_eq0(POST_ARGS) 491{ 492 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0)); 493} 494 495static int 496hwarn_eq1(POST_ARGS) 497{ 498 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1)); 499} 500 501static int 502hwarn_ge1(POST_ARGS) 503{ 504 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0)); 505} 506 507static int 508hwarn_le1(POST_ARGS) 509{ 510 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2)); 511} 512 513static void 514check_args(struct mdoc *m, struct mdoc_node *n) 515{ 516 int i; 517 518 if (NULL == n->args) 519 return; 520 521 assert(n->args->argc); 522 for (i = 0; i < (int)n->args->argc; i++) 523 check_argv(m, n, &n->args->argv[i]); 524} 525 526static void 527check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v) 528{ 529 int i; 530 531 for (i = 0; i < (int)v->sz; i++) 532 check_text(m, v->line, v->pos, v->value[i]); 533 534 /* FIXME: move to post_std(). */ 535 536 if (MDOC_Std == v->arg) 537 if ( ! (v->sz || m->meta.name)) 538 mdoc_nmsg(m, n, MANDOCERR_NONAME); 539} 540 541static void 542check_text(struct mdoc *m, int ln, int pos, char *p) 543{ 544 char *cpp, *pp; 545 size_t sz; 546 547 while ('\0' != *p) { 548 sz = strcspn(p, "\t\\"); 549 550 p += (int)sz; 551 pos += (int)sz; 552 553 if ('\t' == *p) { 554 if ( ! (MDOC_LITERAL & m->flags)) 555 mdoc_pmsg(m, ln, pos, MANDOCERR_BADTAB); 556 p++; 557 pos++; 558 continue; 559 } else if ('\0' == *p) 560 break; 561 562 pos++; 563 pp = ++p; 564 565 if (ESCAPE_ERROR == mandoc_escape 566 ((const char **)&pp, NULL, NULL)) { 567 mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE); 568 break; 569 } 570 571 cpp = p; 572 while (NULL != (cpp = memchr(cpp, ASCII_HYPH, pp - cpp))) 573 *cpp = '-'; 574 575 pos += pp - p; 576 p = pp; 577 } 578} 579 580static int 581check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t) 582{ 583 584 assert(n->parent); 585 if ((MDOC_ROOT == t || tok == n->parent->tok) && 586 (t == n->parent->type)) 587 return(1); 588 589 mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line, 590 n->pos, "want parent %s", MDOC_ROOT == t ? 591 "<root>" : mdoc_macronames[tok]); 592 return(0); 593} 594 595 596static int 597pre_display(PRE_ARGS) 598{ 599 struct mdoc_node *node; 600 601 if (MDOC_BLOCK != n->type) 602 return(1); 603 604 for (node = mdoc->last->parent; node; node = node->parent) 605 if (MDOC_BLOCK == node->type) 606 if (MDOC_Bd == node->tok) 607 break; 608 609 if (node) 610 mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP); 611 612 return(1); 613} 614 615 616static int 617pre_bl(PRE_ARGS) 618{ 619 int i, comp, dup; 620 const char *offs, *width; 621 enum mdoc_list lt; 622 struct mdoc_node *np; 623 624 if (MDOC_BLOCK != n->type) { 625 if (ENDBODY_NOT != n->end) { 626 assert(n->pending); 627 np = n->pending->parent; 628 } else 629 np = n->parent; 630 631 assert(np); 632 assert(MDOC_BLOCK == np->type); 633 assert(MDOC_Bl == np->tok); 634 return(1); 635 } 636 637 /* 638 * First figure out which kind of list to use: bind ourselves to 639 * the first mentioned list type and warn about any remaining 640 * ones. If we find no list type, we default to LIST_item. 641 */ 642 643 /* LINTED */ 644 for (i = 0; n->args && i < (int)n->args->argc; i++) { 645 lt = LIST__NONE; 646 dup = comp = 0; 647 width = offs = NULL; 648 switch (n->args->argv[i].arg) { 649 /* Set list types. */ 650 case (MDOC_Bullet): 651 lt = LIST_bullet; 652 break; 653 case (MDOC_Dash): 654 lt = LIST_dash; 655 break; 656 case (MDOC_Enum): 657 lt = LIST_enum; 658 break; 659 case (MDOC_Hyphen): 660 lt = LIST_hyphen; 661 break; 662 case (MDOC_Item): 663 lt = LIST_item; 664 break; 665 case (MDOC_Tag): 666 lt = LIST_tag; 667 break; 668 case (MDOC_Diag): 669 lt = LIST_diag; 670 break; 671 case (MDOC_Hang): 672 lt = LIST_hang; 673 break; 674 case (MDOC_Ohang): 675 lt = LIST_ohang; 676 break; 677 case (MDOC_Inset): 678 lt = LIST_inset; 679 break; 680 case (MDOC_Column): 681 lt = LIST_column; 682 break; 683 /* Set list arguments. */ 684 case (MDOC_Compact): 685 dup = n->norm->Bl.comp; 686 comp = 1; 687 break; 688 case (MDOC_Width): 689 dup = (NULL != n->norm->Bl.width); 690 width = n->args->argv[i].value[0]; 691 break; 692 case (MDOC_Offset): 693 /* NB: this can be empty! */ 694 if (n->args->argv[i].sz) { 695 offs = n->args->argv[i].value[0]; 696 dup = (NULL != n->norm->Bl.offs); 697 break; 698 } 699 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 700 break; 701 default: 702 continue; 703 } 704 705 /* Check: duplicate auxiliary arguments. */ 706 707 if (dup) 708 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); 709 710 if (comp && ! dup) 711 n->norm->Bl.comp = comp; 712 if (offs && ! dup) 713 n->norm->Bl.offs = offs; 714 if (width && ! dup) 715 n->norm->Bl.width = width; 716 717 /* Check: multiple list types. */ 718 719 if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE) 720 mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP); 721 722 /* Assign list type. */ 723 724 if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) { 725 n->norm->Bl.type = lt; 726 /* Set column information, too. */ 727 if (LIST_column == lt) { 728 n->norm->Bl.ncols = 729 n->args->argv[i].sz; 730 n->norm->Bl.cols = (const char **) 731 n->args->argv[i].value; 732 } 733 } 734 735 /* The list type should come first. */ 736 737 if (n->norm->Bl.type == LIST__NONE) 738 if (n->norm->Bl.width || 739 n->norm->Bl.offs || 740 n->norm->Bl.comp) 741 mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST); 742 743 continue; 744 } 745 746 /* Allow lists to default to LIST_item. */ 747 748 if (LIST__NONE == n->norm->Bl.type) { 749 mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE); 750 n->norm->Bl.type = LIST_item; 751 } 752 753 /* 754 * Validate the width field. Some list types don't need width 755 * types and should be warned about them. Others should have it 756 * and must also be warned. 757 */ 758 759 switch (n->norm->Bl.type) { 760 case (LIST_tag): 761 if (n->norm->Bl.width) 762 break; 763 mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG); 764 break; 765 case (LIST_column): 766 /* FALLTHROUGH */ 767 case (LIST_diag): 768 /* FALLTHROUGH */ 769 case (LIST_ohang): 770 /* FALLTHROUGH */ 771 case (LIST_inset): 772 /* FALLTHROUGH */ 773 case (LIST_item): 774 if (n->norm->Bl.width) 775 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 776 break; 777 default: 778 break; 779 } 780 781 return(1); 782} 783 784 785static int 786pre_bd(PRE_ARGS) 787{ 788 int i, dup, comp; 789 enum mdoc_disp dt; 790 const char *offs; 791 struct mdoc_node *np; 792 793 if (MDOC_BLOCK != n->type) { 794 if (ENDBODY_NOT != n->end) { 795 assert(n->pending); 796 np = n->pending->parent; 797 } else 798 np = n->parent; 799 800 assert(np); 801 assert(MDOC_BLOCK == np->type); 802 assert(MDOC_Bd == np->tok); 803 return(1); 804 } 805 806 /* LINTED */ 807 for (i = 0; n->args && i < (int)n->args->argc; i++) { 808 dt = DISP__NONE; 809 dup = comp = 0; 810 offs = NULL; 811 812 switch (n->args->argv[i].arg) { 813 case (MDOC_Centred): 814 dt = DISP_centred; 815 break; 816 case (MDOC_Ragged): 817 dt = DISP_ragged; 818 break; 819 case (MDOC_Unfilled): 820 dt = DISP_unfilled; 821 break; 822 case (MDOC_Filled): 823 dt = DISP_filled; 824 break; 825 case (MDOC_Literal): 826 dt = DISP_literal; 827 break; 828 case (MDOC_File): 829 mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP); 830 return(0); 831 case (MDOC_Offset): 832 /* NB: this can be empty! */ 833 if (n->args->argv[i].sz) { 834 offs = n->args->argv[i].value[0]; 835 dup = (NULL != n->norm->Bd.offs); 836 break; 837 } 838 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 839 break; 840 case (MDOC_Compact): 841 comp = 1; 842 dup = n->norm->Bd.comp; 843 break; 844 default: 845 abort(); 846 /* NOTREACHED */ 847 } 848 849 /* Check whether we have duplicates. */ 850 851 if (dup) 852 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); 853 854 /* Make our auxiliary assignments. */ 855 856 if (offs && ! dup) 857 n->norm->Bd.offs = offs; 858 if (comp && ! dup) 859 n->norm->Bd.comp = comp; 860 861 /* Check whether a type has already been assigned. */ 862 863 if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE) 864 mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP); 865 866 /* Make our type assignment. */ 867 868 if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE) 869 n->norm->Bd.type = dt; 870 } 871 872 if (DISP__NONE == n->norm->Bd.type) { 873 mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE); 874 n->norm->Bd.type = DISP_ragged; 875 } 876 877 return(1); 878} 879 880 881static int 882pre_ss(PRE_ARGS) 883{ 884 885 if (MDOC_BLOCK != n->type) 886 return(1); 887 return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY)); 888} 889 890 891static int 892pre_sh(PRE_ARGS) 893{ 894 895 if (MDOC_BLOCK != n->type) 896 return(1); 897 898 roff_regunset(mdoc->roff, REG_nS); 899 return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT)); 900} 901 902 903static int 904pre_it(PRE_ARGS) 905{ 906 907 if (MDOC_BLOCK != n->type) 908 return(1); 909 910 return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY)); 911} 912 913 914static int 915pre_an(PRE_ARGS) 916{ 917 int i; 918 919 if (NULL == n->args) 920 return(1); 921 922 for (i = 1; i < (int)n->args->argc; i++) 923 mdoc_pmsg(mdoc, n->args->argv[i].line, 924 n->args->argv[i].pos, MANDOCERR_IGNARGV); 925 926 if (MDOC_Split == n->args->argv[0].arg) 927 n->norm->An.auth = AUTH_split; 928 else if (MDOC_Nosplit == n->args->argv[0].arg) 929 n->norm->An.auth = AUTH_nosplit; 930 else 931 abort(); 932 933 return(1); 934} 935 936static int 937pre_std(PRE_ARGS) 938{ 939 940 if (n->args && 1 == n->args->argc) 941 if (MDOC_Std == n->args->argv[0].arg) 942 return(1); 943 944 mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV); 945 return(1); 946} 947 948static int 949pre_dt(PRE_ARGS) 950{ 951 952 if (NULL == mdoc->meta.date || mdoc->meta.os) 953 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); 954 955 if (mdoc->meta.title) 956 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); 957 958 return(1); 959} 960 961static int 962pre_os(PRE_ARGS) 963{ 964 965 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date) 966 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); 967 968 if (mdoc->meta.os) 969 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); 970 971 return(1); 972} 973 974static int 975pre_dd(PRE_ARGS) 976{ 977 978 if (mdoc->meta.title || mdoc->meta.os) 979 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); 980 981 if (mdoc->meta.date) 982 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); 983 984 return(1); 985} 986 987 988static int 989post_bf(POST_ARGS) 990{ 991 struct mdoc_node *np; 992 enum mdocargt arg; 993 994 /* 995 * Unlike other data pointers, these are "housed" by the HEAD 996 * element, which contains the goods. 997 */ 998 999 if (MDOC_HEAD != mdoc->last->type) { 1000 if (ENDBODY_NOT != mdoc->last->end) { 1001 assert(mdoc->last->pending); 1002 np = mdoc->last->pending->parent->head; 1003 } else if (MDOC_BLOCK != mdoc->last->type) { 1004 np = mdoc->last->parent->head; 1005 } else 1006 np = mdoc->last->head; 1007 1008 assert(np); 1009 assert(MDOC_HEAD == np->type); 1010 assert(MDOC_Bf == np->tok); 1011 return(1); 1012 } 1013 1014 np = mdoc->last; 1015 assert(MDOC_BLOCK == np->parent->type); 1016 assert(MDOC_Bf == np->parent->tok); 1017 1018 /* 1019 * Cannot have both argument and parameter. 1020 * If neither is specified, let it through with a warning. 1021 */ 1022 1023 if (np->parent->args && np->child) { 1024 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT); 1025 return(0); 1026 } else if (NULL == np->parent->args && NULL == np->child) { 1027 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); 1028 return(1); 1029 } 1030 1031 /* Extract argument into data. */ 1032 1033 if (np->parent->args) { 1034 arg = np->parent->args->argv[0].arg; 1035 if (MDOC_Emphasis == arg) 1036 np->norm->Bf.font = FONT_Em; 1037 else if (MDOC_Literal == arg) 1038 np->norm->Bf.font = FONT_Li; 1039 else if (MDOC_Symbolic == arg) 1040 np->norm->Bf.font = FONT_Sy; 1041 else 1042 abort(); 1043 return(1); 1044 } 1045 1046 /* Extract parameter into data. */ 1047 1048 if (0 == strcmp(np->child->string, "Em")) 1049 np->norm->Bf.font = FONT_Em; 1050 else if (0 == strcmp(np->child->string, "Li")) 1051 np->norm->Bf.font = FONT_Li; 1052 else if (0 == strcmp(np->child->string, "Sy")) 1053 np->norm->Bf.font = FONT_Sy; 1054 else 1055 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); 1056 1057 return(1); 1058} 1059 1060static int 1061post_lb(POST_ARGS) 1062{ 1063 const char *p; 1064 char *buf; 1065 size_t sz; 1066 1067 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); 1068 1069 assert(mdoc->last->child); 1070 assert(MDOC_TEXT == mdoc->last->child->type); 1071 1072 p = mdoc_a2lib(mdoc->last->child->string); 1073 1074 /* If lookup ok, replace with table value. */ 1075 1076 if (p) { 1077 free(mdoc->last->child->string); 1078 mdoc->last->child->string = mandoc_strdup(p); 1079 return(1); 1080 } 1081 1082 /* If not, use "library ``xxxx''. */ 1083 1084 sz = strlen(mdoc->last->child->string) + 1085 2 + strlen("\\(lqlibrary\\(rq"); 1086 buf = mandoc_malloc(sz); 1087 snprintf(buf, sz, "library \\(lq%s\\(rq", 1088 mdoc->last->child->string); 1089 free(mdoc->last->child->string); 1090 mdoc->last->child->string = buf; 1091 return(1); 1092} 1093 1094static int 1095post_eoln(POST_ARGS) 1096{ 1097 1098 if (mdoc->last->child) 1099 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST); 1100 return(1); 1101} 1102 1103 1104static int 1105post_vt(POST_ARGS) 1106{ 1107 const struct mdoc_node *n; 1108 1109 /* 1110 * The Vt macro comes in both ELEM and BLOCK form, both of which 1111 * have different syntaxes (yet more context-sensitive 1112 * behaviour). ELEM types must have a child, which is already 1113 * guaranteed by the in_line parsing routine; BLOCK types, 1114 * specifically the BODY, should only have TEXT children. 1115 */ 1116 1117 if (MDOC_BODY != mdoc->last->type) 1118 return(1); 1119 1120 for (n = mdoc->last->child; n; n = n->next) 1121 if (MDOC_TEXT != n->type) 1122 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD); 1123 1124 return(1); 1125} 1126 1127 1128static int 1129post_nm(POST_ARGS) 1130{ 1131 char buf[BUFSIZ]; 1132 1133 /* If no child specified, make sure we have the meta name. */ 1134 1135 if (NULL == mdoc->last->child && NULL == mdoc->meta.name) { 1136 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME); 1137 return(1); 1138 } else if (mdoc->meta.name) 1139 return(1); 1140 1141 /* If no meta name, set it from the child. */ 1142 1143 if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ)) 1144 return(0); 1145 1146 mdoc->meta.name = mandoc_strdup(buf); 1147 1148 return(1); 1149} 1150 1151static int 1152post_literal(POST_ARGS) 1153{ 1154 1155 /* 1156 * The `Dl' (note "el" not "one") and `Bd' macros unset the 1157 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets 1158 * this in literal mode, but it doesn't hurt to just switch it 1159 * off in general since displays can't be nested. 1160 */ 1161 1162 if (MDOC_BODY == mdoc->last->type) 1163 mdoc->flags &= ~MDOC_LITERAL; 1164 1165 return(1); 1166} 1167 1168static int 1169post_defaults(POST_ARGS) 1170{ 1171 struct mdoc_node *nn; 1172 1173 /* 1174 * The `Ar' defaults to "file ..." if no value is provided as an 1175 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just 1176 * gets an empty string. 1177 */ 1178 1179 if (mdoc->last->child) 1180 return(1); 1181 1182 nn = mdoc->last; 1183 mdoc->next = MDOC_NEXT_CHILD; 1184 1185 switch (nn->tok) { 1186 case (MDOC_Ar): 1187 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file")) 1188 return(0); 1189 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "...")) 1190 return(0); 1191 break; 1192 case (MDOC_At): 1193 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T")) 1194 return(0); 1195 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX")) 1196 return(0); 1197 break; 1198 case (MDOC_Li): 1199 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "")) 1200 return(0); 1201 break; 1202 case (MDOC_Pa): 1203 /* FALLTHROUGH */ 1204 case (MDOC_Mt): 1205 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~")) 1206 return(0); 1207 break; 1208 default: 1209 abort(); 1210 /* NOTREACHED */ 1211 } 1212 1213 mdoc->last = nn; 1214 return(1); 1215} 1216 1217static int 1218post_at(POST_ARGS) 1219{ 1220 const char *p, *q; 1221 char *buf; 1222 size_t sz; 1223 1224 /* 1225 * If we have a child, look it up in the standard keys. If a 1226 * key exist, use that instead of the child; if it doesn't, 1227 * prefix "AT&T UNIX " to the existing data. 1228 */ 1229 1230 if (NULL == mdoc->last->child) 1231 return(1); 1232 1233 assert(MDOC_TEXT == mdoc->last->child->type); 1234 p = mdoc_a2att(mdoc->last->child->string); 1235 1236 if (p) { 1237 free(mdoc->last->child->string); 1238 mdoc->last->child->string = mandoc_strdup(p); 1239 } else { 1240 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT); 1241 p = "AT&T UNIX "; 1242 q = mdoc->last->child->string; 1243 sz = strlen(p) + strlen(q) + 1; 1244 buf = mandoc_malloc(sz); 1245 strlcpy(buf, p, sz); 1246 strlcat(buf, q, sz); 1247 free(mdoc->last->child->string); 1248 mdoc->last->child->string = buf; 1249 } 1250 1251 return(1); 1252} 1253 1254static int 1255post_an(POST_ARGS) 1256{ 1257 struct mdoc_node *np; 1258 1259 np = mdoc->last; 1260 if (AUTH__NONE == np->norm->An.auth) { 1261 if (0 == np->child) 1262 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0); 1263 } else if (np->child) 1264 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0); 1265 1266 return(1); 1267} 1268 1269 1270static int 1271post_it(POST_ARGS) 1272{ 1273 int i, cols; 1274 enum mdoc_list lt; 1275 struct mdoc_node *n, *c; 1276 enum mandocerr er; 1277 1278 if (MDOC_BLOCK != mdoc->last->type) 1279 return(1); 1280 1281 n = mdoc->last->parent->parent; 1282 lt = n->norm->Bl.type; 1283 1284 if (LIST__NONE == lt) { 1285 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE); 1286 return(1); 1287 } 1288 1289 switch (lt) { 1290 case (LIST_tag): 1291 if (mdoc->last->head->child) 1292 break; 1293 /* FIXME: give this a dummy value. */ 1294 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS); 1295 break; 1296 case (LIST_hang): 1297 /* FALLTHROUGH */ 1298 case (LIST_ohang): 1299 /* FALLTHROUGH */ 1300 case (LIST_inset): 1301 /* FALLTHROUGH */ 1302 case (LIST_diag): 1303 if (NULL == mdoc->last->head->child) 1304 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS); 1305 break; 1306 case (LIST_bullet): 1307 /* FALLTHROUGH */ 1308 case (LIST_dash): 1309 /* FALLTHROUGH */ 1310 case (LIST_enum): 1311 /* FALLTHROUGH */ 1312 case (LIST_hyphen): 1313 if (NULL == mdoc->last->body->child) 1314 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY); 1315 /* FALLTHROUGH */ 1316 case (LIST_item): 1317 if (mdoc->last->head->child) 1318 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST); 1319 break; 1320 case (LIST_column): 1321 cols = (int)n->norm->Bl.ncols; 1322 1323 assert(NULL == mdoc->last->head->child); 1324 1325 if (NULL == mdoc->last->body->child) 1326 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY); 1327 1328 for (i = 0, c = mdoc->last->child; c; c = c->next) 1329 if (MDOC_BODY == c->type) 1330 i++; 1331 1332 if (i < cols) 1333 er = MANDOCERR_ARGCOUNT; 1334 else if (i == cols || i == cols + 1) 1335 break; 1336 else 1337 er = MANDOCERR_SYNTARGCOUNT; 1338 1339 mandoc_vmsg(er, mdoc->parse, mdoc->last->line, 1340 mdoc->last->pos, 1341 "columns == %d (have %d)", cols, i); 1342 return(MANDOCERR_ARGCOUNT == er); 1343 default: 1344 break; 1345 } 1346 1347 return(1); 1348} 1349 1350static int 1351post_bl_block(POST_ARGS) 1352{ 1353 struct mdoc_node *n; 1354 1355 /* 1356 * These are fairly complicated, so we've broken them into two 1357 * functions. post_bl_block_tag() is called when a -tag is 1358 * specified, but no -width (it must be guessed). The second 1359 * when a -width is specified (macro indicators must be 1360 * rewritten into real lengths). 1361 */ 1362 1363 n = mdoc->last; 1364 1365 if (LIST_tag == n->norm->Bl.type && 1366 NULL == n->norm->Bl.width) { 1367 if ( ! post_bl_block_tag(mdoc)) 1368 return(0); 1369 } else if (NULL != n->norm->Bl.width) { 1370 if ( ! post_bl_block_width(mdoc)) 1371 return(0); 1372 } else 1373 return(1); 1374 1375 assert(n->norm->Bl.width); 1376 return(1); 1377} 1378 1379static int 1380post_bl_block_width(POST_ARGS) 1381{ 1382 size_t width; 1383 int i; 1384 enum mdoct tok; 1385 struct mdoc_node *n; 1386 char buf[NUMSIZ]; 1387 1388 n = mdoc->last; 1389 1390 /* 1391 * Calculate the real width of a list from the -width string, 1392 * which may contain a macro (with a known default width), a 1393 * literal string, or a scaling width. 1394 * 1395 * If the value to -width is a macro, then we re-write it to be 1396 * the macro's width as set in share/tmac/mdoc/doc-common. 1397 */ 1398 1399 if (0 == strcmp(n->norm->Bl.width, "Ds")) 1400 width = 6; 1401 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width))) 1402 return(1); 1403 else if (0 == (width = macro2len(tok))) { 1404 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH); 1405 return(1); 1406 } 1407 1408 /* The value already exists: free and reallocate it. */ 1409 1410 assert(n->args); 1411 1412 for (i = 0; i < (int)n->args->argc; i++) 1413 if (MDOC_Width == n->args->argv[i].arg) 1414 break; 1415 1416 assert(i < (int)n->args->argc); 1417 1418 snprintf(buf, NUMSIZ, "%zun", width); 1419 free(n->args->argv[i].value[0]); 1420 n->args->argv[i].value[0] = mandoc_strdup(buf); 1421 1422 /* Set our width! */ 1423 n->norm->Bl.width = n->args->argv[i].value[0]; 1424 return(1); 1425} 1426 1427static int 1428post_bl_block_tag(POST_ARGS) 1429{ 1430 struct mdoc_node *n, *nn; 1431 size_t sz, ssz; 1432 int i; 1433 char buf[NUMSIZ]; 1434 1435 /* 1436 * Calculate the -width for a `Bl -tag' list if it hasn't been 1437 * provided. Uses the first head macro. NOTE AGAIN: this is 1438 * ONLY if the -width argument has NOT been provided. See 1439 * post_bl_block_width() for converting the -width string. 1440 */ 1441 1442 sz = 10; 1443 n = mdoc->last; 1444 1445 for (nn = n->body->child; nn; nn = nn->next) { 1446 if (MDOC_It != nn->tok) 1447 continue; 1448 1449 assert(MDOC_BLOCK == nn->type); 1450 nn = nn->head->child; 1451 1452 if (nn == NULL) 1453 break; 1454 1455 if (MDOC_TEXT == nn->type) { 1456 sz = strlen(nn->string) + 1; 1457 break; 1458 } 1459 1460 if (0 != (ssz = macro2len(nn->tok))) 1461 sz = ssz; 1462 1463 break; 1464 } 1465 1466 /* Defaults to ten ens. */ 1467 1468 snprintf(buf, NUMSIZ, "%zun", sz); 1469 1470 /* 1471 * We have to dynamically add this to the macro's argument list. 1472 * We're guaranteed that a MDOC_Width doesn't already exist. 1473 */ 1474 1475 assert(n->args); 1476 i = (int)(n->args->argc)++; 1477 1478 n->args->argv = mandoc_realloc(n->args->argv, 1479 n->args->argc * sizeof(struct mdoc_argv)); 1480 1481 n->args->argv[i].arg = MDOC_Width; 1482 n->args->argv[i].line = n->line; 1483 n->args->argv[i].pos = n->pos; 1484 n->args->argv[i].sz = 1; 1485 n->args->argv[i].value = mandoc_malloc(sizeof(char *)); 1486 n->args->argv[i].value[0] = mandoc_strdup(buf); 1487 1488 /* Set our width! */ 1489 n->norm->Bl.width = n->args->argv[i].value[0]; 1490 return(1); 1491} 1492 1493 1494static int 1495post_bl_head(POST_ARGS) 1496{ 1497 struct mdoc_node *np, *nn, *nnp; 1498 int i, j; 1499 1500 if (LIST_column != mdoc->last->norm->Bl.type) 1501 /* FIXME: this should be ERROR class... */ 1502 return(hwarn_eq0(mdoc)); 1503 1504 /* 1505 * Convert old-style lists, where the column width specifiers 1506 * trail as macro parameters, to the new-style ("normal-form") 1507 * lists where they're argument values following -column. 1508 */ 1509 1510 /* First, disallow both types and allow normal-form. */ 1511 1512 /* 1513 * TODO: technically, we can accept both and just merge the two 1514 * lists, but I'll leave that for another day. 1515 */ 1516 1517 if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) { 1518 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS); 1519 return(0); 1520 } else if (NULL == mdoc->last->child) 1521 return(1); 1522 1523 np = mdoc->last->parent; 1524 assert(np->args); 1525 1526 for (j = 0; j < (int)np->args->argc; j++) 1527 if (MDOC_Column == np->args->argv[j].arg) 1528 break; 1529 1530 assert(j < (int)np->args->argc); 1531 assert(0 == np->args->argv[j].sz); 1532 1533 /* 1534 * Accommodate for new-style groff column syntax. Shuffle the 1535 * child nodes, all of which must be TEXT, as arguments for the 1536 * column field. Then, delete the head children. 1537 */ 1538 1539 np->args->argv[j].sz = (size_t)mdoc->last->nchild; 1540 np->args->argv[j].value = mandoc_malloc 1541 ((size_t)mdoc->last->nchild * sizeof(char *)); 1542 1543 mdoc->last->norm->Bl.ncols = np->args->argv[j].sz; 1544 mdoc->last->norm->Bl.cols = (const char **)np->args->argv[j].value; 1545 1546 for (i = 0, nn = mdoc->last->child; nn; i++) { 1547 np->args->argv[j].value[i] = nn->string; 1548 nn->string = NULL; 1549 nnp = nn; 1550 nn = nn->next; 1551 mdoc_node_delete(NULL, nnp); 1552 } 1553 1554 mdoc->last->nchild = 0; 1555 mdoc->last->child = NULL; 1556 1557 return(1); 1558} 1559 1560static int 1561post_bl(POST_ARGS) 1562{ 1563 struct mdoc_node *n; 1564 1565 if (MDOC_HEAD == mdoc->last->type) 1566 return(post_bl_head(mdoc)); 1567 if (MDOC_BLOCK == mdoc->last->type) 1568 return(post_bl_block(mdoc)); 1569 if (MDOC_BODY != mdoc->last->type) 1570 return(1); 1571 1572 for (n = mdoc->last->child; n; n = n->next) { 1573 switch (n->tok) { 1574 case (MDOC_Lp): 1575 /* FALLTHROUGH */ 1576 case (MDOC_Pp): 1577 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD); 1578 /* FALLTHROUGH */ 1579 case (MDOC_It): 1580 /* FALLTHROUGH */ 1581 case (MDOC_Sm): 1582 continue; 1583 default: 1584 break; 1585 } 1586 1587 mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD); 1588 return(0); 1589 } 1590 1591 return(1); 1592} 1593 1594static int 1595ebool(struct mdoc *mdoc) 1596{ 1597 1598 if (NULL == mdoc->last->child) { 1599 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY); 1600 mdoc_node_delete(mdoc, mdoc->last); 1601 return(1); 1602 } 1603 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); 1604 1605 assert(MDOC_TEXT == mdoc->last->child->type); 1606 1607 if (0 == strcmp(mdoc->last->child->string, "on")) 1608 return(1); 1609 if (0 == strcmp(mdoc->last->child->string, "off")) 1610 return(1); 1611 1612 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL); 1613 return(1); 1614} 1615 1616static int 1617post_root(POST_ARGS) 1618{ 1619 int erc; 1620 struct mdoc_node *n; 1621 1622 erc = 0; 1623 1624 /* Check that we have a finished prologue. */ 1625 1626 if ( ! (MDOC_PBODY & mdoc->flags)) { 1627 erc++; 1628 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG); 1629 } 1630 1631 n = mdoc->first; 1632 assert(n); 1633 1634 /* Check that we begin with a proper `Sh'. */ 1635 1636 if (NULL == n->child) { 1637 erc++; 1638 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY); 1639 } else if (MDOC_BLOCK != n->child->type || 1640 MDOC_Sh != n->child->tok) { 1641 erc++; 1642 /* Can this be lifted? See rxdebug.1 for example. */ 1643 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY); 1644 } 1645 1646 return(erc ? 0 : 1); 1647} 1648 1649static int 1650post_st(POST_ARGS) 1651{ 1652 struct mdoc_node *ch; 1653 const char *p; 1654 1655 if (NULL == (ch = mdoc->last->child)) { 1656 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY); 1657 mdoc_node_delete(mdoc, mdoc->last); 1658 return(1); 1659 } 1660 1661 assert(MDOC_TEXT == ch->type); 1662 1663 if (NULL == (p = mdoc_a2st(ch->string))) { 1664 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD); 1665 mdoc_node_delete(mdoc, mdoc->last); 1666 } else { 1667 free(ch->string); 1668 ch->string = mandoc_strdup(p); 1669 } 1670 1671 return(1); 1672} 1673 1674static int 1675post_rs(POST_ARGS) 1676{ 1677 struct mdoc_node *nn, *next, *prev; 1678 int i, j; 1679 1680 switch (mdoc->last->type) { 1681 case (MDOC_HEAD): 1682 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0); 1683 return(1); 1684 case (MDOC_BODY): 1685 if (mdoc->last->child) 1686 break; 1687 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0); 1688 return(1); 1689 default: 1690 return(1); 1691 } 1692 1693 /* 1694 * Make sure only certain types of nodes are allowed within the 1695 * the `Rs' body. Delete offending nodes and raise a warning. 1696 * Do this before re-ordering for the sake of clarity. 1697 */ 1698 1699 next = NULL; 1700 for (nn = mdoc->last->child; nn; nn = next) { 1701 for (i = 0; i < RSORD_MAX; i++) 1702 if (nn->tok == rsord[i]) 1703 break; 1704 1705 if (i < RSORD_MAX) { 1706 if (MDOC__J == rsord[i] || MDOC__B == rsord[i]) 1707 mdoc->last->norm->Rs.quote_T++; 1708 next = nn->next; 1709 continue; 1710 } 1711 1712 next = nn->next; 1713 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD); 1714 mdoc_node_delete(mdoc, nn); 1715 } 1716 1717 /* 1718 * The full `Rs' block needs special handling to order the 1719 * sub-elements according to `rsord'. Pick through each element 1720 * and correctly order it. This is a insertion sort. 1721 */ 1722 1723 next = NULL; 1724 for (nn = mdoc->last->child->next; nn; nn = next) { 1725 /* Determine order of `nn'. */ 1726 for (i = 0; i < RSORD_MAX; i++) 1727 if (rsord[i] == nn->tok) 1728 break; 1729 1730 /* 1731 * Remove `nn' from the chain. This somewhat 1732 * repeats mdoc_node_unlink(), but since we're 1733 * just re-ordering, there's no need for the 1734 * full unlink process. 1735 */ 1736 1737 if (NULL != (next = nn->next)) 1738 next->prev = nn->prev; 1739 1740 if (NULL != (prev = nn->prev)) 1741 prev->next = nn->next; 1742 1743 nn->prev = nn->next = NULL; 1744 1745 /* 1746 * Scan back until we reach a node that's 1747 * ordered before `nn'. 1748 */ 1749 1750 for ( ; prev ; prev = prev->prev) { 1751 /* Determine order of `prev'. */ 1752 for (j = 0; j < RSORD_MAX; j++) 1753 if (rsord[j] == prev->tok) 1754 break; 1755 1756 if (j <= i) 1757 break; 1758 } 1759 1760 /* 1761 * Set `nn' back into its correct place in front 1762 * of the `prev' node. 1763 */ 1764 1765 nn->prev = prev; 1766 1767 if (prev) { 1768 if (prev->next) 1769 prev->next->prev = nn; 1770 nn->next = prev->next; 1771 prev->next = nn; 1772 } else { 1773 mdoc->last->child->prev = nn; 1774 nn->next = mdoc->last->child; 1775 mdoc->last->child = nn; 1776 } 1777 } 1778 1779 return(1); 1780} 1781 1782static int 1783post_ns(POST_ARGS) 1784{ 1785 1786 if (MDOC_LINE & mdoc->last->flags) 1787 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS); 1788 return(1); 1789} 1790 1791static int 1792post_sh(POST_ARGS) 1793{ 1794 1795 if (MDOC_HEAD == mdoc->last->type) 1796 return(post_sh_head(mdoc)); 1797 if (MDOC_BODY == mdoc->last->type) 1798 return(post_sh_body(mdoc)); 1799 1800 return(1); 1801} 1802 1803static int 1804post_sh_body(POST_ARGS) 1805{ 1806 struct mdoc_node *n; 1807 1808 if (SEC_NAME != mdoc->lastsec) 1809 return(1); 1810 1811 /* 1812 * Warn if the NAME section doesn't contain the `Nm' and `Nd' 1813 * macros (can have multiple `Nm' and one `Nd'). Note that the 1814 * children of the BODY declaration can also be "text". 1815 */ 1816 1817 if (NULL == (n = mdoc->last->child)) { 1818 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); 1819 return(1); 1820 } 1821 1822 for ( ; n && n->next; n = n->next) { 1823 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok) 1824 continue; 1825 if (MDOC_TEXT == n->type) 1826 continue; 1827 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); 1828 } 1829 1830 assert(n); 1831 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok) 1832 return(1); 1833 1834 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); 1835 return(1); 1836} 1837 1838static int 1839post_sh_head(POST_ARGS) 1840{ 1841 char buf[BUFSIZ]; 1842 enum mdoc_sec sec; 1843 1844 /* 1845 * Process a new section. Sections are either "named" or 1846 * "custom". Custom sections are user-defined, while named ones 1847 * follow a conventional order and may only appear in certain 1848 * manual sections. 1849 */ 1850 1851 if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ)) 1852 return(0); 1853 1854 sec = a2sec(buf); 1855 1856 /* The NAME should be first. */ 1857 1858 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed) 1859 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST); 1860 1861 /* The SYNOPSIS gets special attention in other areas. */ 1862 1863 if (SEC_SYNOPSIS == sec) 1864 mdoc->flags |= MDOC_SYNOPSIS; 1865 else 1866 mdoc->flags &= ~MDOC_SYNOPSIS; 1867 1868 /* Mark our last section. */ 1869 1870 mdoc->lastsec = sec; 1871 1872 /* We don't care about custom sections after this. */ 1873 1874 if (SEC_CUSTOM == sec) 1875 return(1); 1876 1877 /* 1878 * Check whether our non-custom section is being repeated or is 1879 * out of order. 1880 */ 1881 1882 if (sec == mdoc->lastnamed) 1883 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP); 1884 1885 if (sec < mdoc->lastnamed) 1886 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO); 1887 1888 /* Mark the last named section. */ 1889 1890 mdoc->lastnamed = sec; 1891 1892 /* Check particular section/manual conventions. */ 1893 1894 assert(mdoc->meta.msec); 1895 1896 switch (sec) { 1897 case (SEC_RETURN_VALUES): 1898 /* FALLTHROUGH */ 1899 case (SEC_ERRORS): 1900 /* FALLTHROUGH */ 1901 case (SEC_LIBRARY): 1902 if (*mdoc->meta.msec == '2') 1903 break; 1904 if (*mdoc->meta.msec == '3') 1905 break; 1906 if (*mdoc->meta.msec == '9') 1907 break; 1908 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC); 1909 break; 1910 default: 1911 break; 1912 } 1913 1914 return(1); 1915} 1916 1917static int 1918post_ignpar(POST_ARGS) 1919{ 1920 struct mdoc_node *np; 1921 1922 if (MDOC_BODY != mdoc->last->type) 1923 return(1); 1924 1925 if (NULL != (np = mdoc->last->child)) 1926 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { 1927 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR); 1928 mdoc_node_delete(mdoc, np); 1929 } 1930 1931 if (NULL != (np = mdoc->last->last)) 1932 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { 1933 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR); 1934 mdoc_node_delete(mdoc, np); 1935 } 1936 1937 return(1); 1938} 1939 1940static int 1941pre_par(PRE_ARGS) 1942{ 1943 1944 if (NULL == mdoc->last) 1945 return(1); 1946 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type) 1947 return(1); 1948 1949 /* 1950 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type 1951 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. 1952 */ 1953 1954 if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok) 1955 return(1); 1956 if (MDOC_Bl == n->tok && n->norm->Bl.comp) 1957 return(1); 1958 if (MDOC_Bd == n->tok && n->norm->Bd.comp) 1959 return(1); 1960 if (MDOC_It == n->tok && n->parent->norm->Bl.comp) 1961 return(1); 1962 1963 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR); 1964 mdoc_node_delete(mdoc, mdoc->last); 1965 return(1); 1966} 1967 1968static int 1969pre_literal(PRE_ARGS) 1970{ 1971 1972 if (MDOC_BODY != n->type) 1973 return(1); 1974 1975 /* 1976 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd 1977 * -unfilled' macros set MDOC_LITERAL on entrance to the body. 1978 */ 1979 1980 switch (n->tok) { 1981 case (MDOC_Dl): 1982 mdoc->flags |= MDOC_LITERAL; 1983 break; 1984 case (MDOC_Bd): 1985 if (DISP_literal == n->norm->Bd.type) 1986 mdoc->flags |= MDOC_LITERAL; 1987 if (DISP_unfilled == n->norm->Bd.type) 1988 mdoc->flags |= MDOC_LITERAL; 1989 break; 1990 default: 1991 abort(); 1992 /* NOTREACHED */ 1993 } 1994 1995 return(1); 1996} 1997 1998static int 1999post_dd(POST_ARGS) 2000{ 2001 char buf[DATESIZE]; 2002 struct mdoc_node *n; 2003 2004 if (mdoc->meta.date) 2005 free(mdoc->meta.date); 2006 2007 n = mdoc->last; 2008 if (NULL == n->child || '\0' == n->child->string[0]) { 2009 mdoc->meta.date = mandoc_normdate 2010 (mdoc->parse, NULL, n->line, n->pos); 2011 return(1); 2012 } 2013 2014 if ( ! concat(mdoc, buf, n->child, DATESIZE)) 2015 return(0); 2016 2017 mdoc->meta.date = mandoc_normdate 2018 (mdoc->parse, buf, n->line, n->pos); 2019 2020 return(1); 2021} 2022 2023static int 2024post_dt(POST_ARGS) 2025{ 2026 struct mdoc_node *nn, *n; 2027 const char *cp; 2028 char *p; 2029 2030 n = mdoc->last; 2031 2032 if (mdoc->meta.title) 2033 free(mdoc->meta.title); 2034 if (mdoc->meta.vol) 2035 free(mdoc->meta.vol); 2036 if (mdoc->meta.arch) 2037 free(mdoc->meta.arch); 2038 2039 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL; 2040 2041 /* First make all characters uppercase. */ 2042 2043 if (NULL != (nn = n->child)) 2044 for (p = nn->string; *p; p++) { 2045 if (toupper((u_char)*p) == *p) 2046 continue; 2047 2048 /* 2049 * FIXME: don't be lazy: have this make all 2050 * characters be uppercase and just warn once. 2051 */ 2052 mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE); 2053 break; 2054 } 2055 2056 /* Handles: `.Dt' 2057 * --> title = unknown, volume = local, msec = 0, arch = NULL 2058 */ 2059 2060 if (NULL == (nn = n->child)) { 2061 /* XXX: make these macro values. */ 2062 /* FIXME: warn about missing values. */ 2063 mdoc->meta.title = mandoc_strdup("UNKNOWN"); 2064 mdoc->meta.vol = mandoc_strdup("LOCAL"); 2065 mdoc->meta.msec = mandoc_strdup("1"); 2066 return(1); 2067 } 2068 2069 /* Handles: `.Dt TITLE' 2070 * --> title = TITLE, volume = local, msec = 0, arch = NULL 2071 */ 2072 2073 mdoc->meta.title = mandoc_strdup 2074 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string); 2075 2076 if (NULL == (nn = nn->next)) { 2077 /* FIXME: warn about missing msec. */ 2078 /* XXX: make this a macro value. */ 2079 mdoc->meta.vol = mandoc_strdup("LOCAL"); 2080 mdoc->meta.msec = mandoc_strdup("1"); 2081 return(1); 2082 } 2083 2084 /* Handles: `.Dt TITLE SEC' 2085 * --> title = TITLE, volume = SEC is msec ? 2086 * format(msec) : SEC, 2087 * msec = SEC is msec ? atoi(msec) : 0, 2088 * arch = NULL 2089 */ 2090 2091 cp = mdoc_a2msec(nn->string); 2092 if (cp) { 2093 mdoc->meta.vol = mandoc_strdup(cp); 2094 mdoc->meta.msec = mandoc_strdup(nn->string); 2095 } else { 2096 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC); 2097 mdoc->meta.vol = mandoc_strdup(nn->string); 2098 mdoc->meta.msec = mandoc_strdup(nn->string); 2099 } 2100 2101 if (NULL == (nn = nn->next)) 2102 return(1); 2103 2104 /* Handles: `.Dt TITLE SEC VOL' 2105 * --> title = TITLE, volume = VOL is vol ? 2106 * format(VOL) : 2107 * VOL is arch ? format(arch) : 2108 * VOL 2109 */ 2110 2111 cp = mdoc_a2vol(nn->string); 2112 if (cp) { 2113 free(mdoc->meta.vol); 2114 mdoc->meta.vol = mandoc_strdup(cp); 2115 } else { 2116 /* FIXME: warn about bad arch. */ 2117 cp = mdoc_a2arch(nn->string); 2118 if (NULL == cp) { 2119 free(mdoc->meta.vol); 2120 mdoc->meta.vol = mandoc_strdup(nn->string); 2121 } else 2122 mdoc->meta.arch = mandoc_strdup(cp); 2123 } 2124 2125 /* Ignore any subsequent parameters... */ 2126 /* FIXME: warn about subsequent parameters. */ 2127 2128 return(1); 2129} 2130 2131static int 2132post_prol(POST_ARGS) 2133{ 2134 /* 2135 * Remove prologue macros from the document after they're 2136 * processed. The final document uses mdoc_meta for these 2137 * values and discards the originals. 2138 */ 2139 2140 mdoc_node_delete(mdoc, mdoc->last); 2141 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os) 2142 mdoc->flags |= MDOC_PBODY; 2143 2144 return(1); 2145} 2146 2147static int 2148post_bx(POST_ARGS) 2149{ 2150 struct mdoc_node *n; 2151 2152 /* 2153 * Make `Bx's second argument always start with an uppercase 2154 * letter. Groff checks if it's an "accepted" term, but we just 2155 * uppercase blindly. 2156 */ 2157 2158 n = mdoc->last->child; 2159 if (n && NULL != (n = n->next)) 2160 *n->string = (char)toupper 2161 ((unsigned char)*n->string); 2162 2163 return(1); 2164} 2165 2166static int 2167post_os(POST_ARGS) 2168{ 2169 struct mdoc_node *n; 2170 char buf[BUFSIZ]; 2171#ifndef OSNAME 2172 struct utsname utsname; 2173#endif 2174 2175 n = mdoc->last; 2176 2177 /* 2178 * Set the operating system by way of the `Os' macro. Note that 2179 * if an argument isn't provided and -DOSNAME="\"foo\"" is 2180 * provided during compilation, this value will be used instead 2181 * of filling in "sysname release" from uname(). 2182 */ 2183 2184 if (mdoc->meta.os) 2185 free(mdoc->meta.os); 2186 2187 if ( ! concat(mdoc, buf, n->child, BUFSIZ)) 2188 return(0); 2189 2190 /* XXX: yes, these can all be dynamically-adjusted buffers, but 2191 * it's really not worth the extra hackery. 2192 */ 2193 2194 if ('\0' == buf[0]) { 2195#ifdef OSNAME 2196 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) { 2197 mdoc_nmsg(mdoc, n, MANDOCERR_MEM); 2198 return(0); 2199 } 2200#else /*!OSNAME */ 2201 if (-1 == uname(&utsname)) { 2202 mdoc_nmsg(mdoc, n, MANDOCERR_UNAME); 2203 mdoc->meta.os = mandoc_strdup("UNKNOWN"); 2204 return(post_prol(mdoc)); 2205 } 2206 2207 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) { 2208 mdoc_nmsg(mdoc, n, MANDOCERR_MEM); 2209 return(0); 2210 } 2211 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) { 2212 mdoc_nmsg(mdoc, n, MANDOCERR_MEM); 2213 return(0); 2214 } 2215 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) { 2216 mdoc_nmsg(mdoc, n, MANDOCERR_MEM); 2217 return(0); 2218 } 2219#endif /*!OSNAME*/ 2220 } 2221 2222 mdoc->meta.os = mandoc_strdup(buf); 2223 return(1); 2224} 2225 2226static int 2227post_std(POST_ARGS) 2228{ 2229 struct mdoc_node *nn, *n; 2230 2231 n = mdoc->last; 2232 2233 /* 2234 * Macros accepting `-std' as an argument have the name of the 2235 * current document (`Nm') filled in as the argument if it's not 2236 * provided. 2237 */ 2238 2239 if (n->child) 2240 return(1); 2241 2242 if (NULL == mdoc->meta.name) 2243 return(1); 2244 2245 nn = n; 2246 mdoc->next = MDOC_NEXT_CHILD; 2247 2248 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name)) 2249 return(0); 2250 2251 mdoc->last = nn; 2252 return(1); 2253} 2254 2255static int 2256concat(struct mdoc *m, char *p, const struct mdoc_node *n, size_t sz) 2257{ 2258 2259 p[0] = '\0'; 2260 2261 /* 2262 * Concatenate sibling nodes together. All siblings must be of 2263 * type MDOC_TEXT or an assertion is raised. Concatenation is 2264 * separated by a single whitespace. Returns 0 on fatal (string 2265 * overrun) error. 2266 */ 2267 2268 for ( ; n; n = n->next) { 2269 assert(MDOC_TEXT == n->type); 2270 2271 if (strlcat(p, n->string, sz) >= sz) { 2272 mdoc_nmsg(m, n, MANDOCERR_MEM); 2273 return(0); 2274 } 2275 2276 if (NULL == n->next) 2277 continue; 2278 2279 if (strlcat(p, " ", sz) >= sz) { 2280 mdoc_nmsg(m, n, MANDOCERR_MEM); 2281 return(0); 2282 } 2283 } 2284 2285 return(1); 2286} 2287 2288static enum mdoc_sec 2289a2sec(const char *p) 2290{ 2291 int i; 2292 2293 for (i = 0; i < (int)SEC__MAX; i++) 2294 if (secnames[i] && 0 == strcmp(p, secnames[i])) 2295 return((enum mdoc_sec)i); 2296 2297 return(SEC_CUSTOM); 2298} 2299 2300static size_t 2301macro2len(enum mdoct macro) 2302{ 2303 2304 switch (macro) { 2305 case(MDOC_Ad): 2306 return(12); 2307 case(MDOC_Ao): 2308 return(12); 2309 case(MDOC_An): 2310 return(12); 2311 case(MDOC_Aq): 2312 return(12); 2313 case(MDOC_Ar): 2314 return(12); 2315 case(MDOC_Bo): 2316 return(12); 2317 case(MDOC_Bq): 2318 return(12); 2319 case(MDOC_Cd): 2320 return(12); 2321 case(MDOC_Cm): 2322 return(10); 2323 case(MDOC_Do): 2324 return(10); 2325 case(MDOC_Dq): 2326 return(12); 2327 case(MDOC_Dv): 2328 return(12); 2329 case(MDOC_Eo): 2330 return(12); 2331 case(MDOC_Em): 2332 return(10); 2333 case(MDOC_Er): 2334 return(17); 2335 case(MDOC_Ev): 2336 return(15); 2337 case(MDOC_Fa): 2338 return(12); 2339 case(MDOC_Fl): 2340 return(10); 2341 case(MDOC_Fo): 2342 return(16); 2343 case(MDOC_Fn): 2344 return(16); 2345 case(MDOC_Ic): 2346 return(10); 2347 case(MDOC_Li): 2348 return(16); 2349 case(MDOC_Ms): 2350 return(6); 2351 case(MDOC_Nm): 2352 return(10); 2353 case(MDOC_No): 2354 return(12); 2355 case(MDOC_Oo): 2356 return(10); 2357 case(MDOC_Op): 2358 return(14); 2359 case(MDOC_Pa): 2360 return(32); 2361 case(MDOC_Pf): 2362 return(12); 2363 case(MDOC_Po): 2364 return(12); 2365 case(MDOC_Pq): 2366 return(12); 2367 case(MDOC_Ql): 2368 return(16); 2369 case(MDOC_Qo): 2370 return(12); 2371 case(MDOC_So): 2372 return(12); 2373 case(MDOC_Sq): 2374 return(12); 2375 case(MDOC_Sy): 2376 return(6); 2377 case(MDOC_Sx): 2378 return(16); 2379 case(MDOC_Tn): 2380 return(10); 2381 case(MDOC_Va): 2382 return(12); 2383 case(MDOC_Vt): 2384 return(12); 2385 case(MDOC_Xr): 2386 return(10); 2387 default: 2388 break; 2389 }; 2390 return(0); 2391} 2392