1294113Sbapt/* $Id: mdoc_validate.c,v 1.301 2016/01/08 17:48:09 schwarze Exp $ */ 2241675Suqs/* 3261344Suqs * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4294113Sbapt * Copyright (c) 2010-2016 Ingo Schwarze <schwarze@openbsd.org> 5274880Sbapt * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> 6241675Suqs * 7241675Suqs * Permission to use, copy, modify, and distribute this software for any 8241675Suqs * purpose with or without fee is hereby granted, provided that the above 9241675Suqs * copyright notice and this permission notice appear in all copies. 10241675Suqs * 11294113Sbapt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 12241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13294113Sbapt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 14241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18241675Suqs */ 19241675Suqs#include "config.h" 20241675Suqs 21275432Sbapt#include <sys/types.h> 22261344Suqs#ifndef OSNAME 23241675Suqs#include <sys/utsname.h> 24241675Suqs#endif 25241675Suqs 26241675Suqs#include <assert.h> 27241675Suqs#include <ctype.h> 28241675Suqs#include <limits.h> 29241675Suqs#include <stdio.h> 30241675Suqs#include <stdlib.h> 31241675Suqs#include <string.h> 32241675Suqs#include <time.h> 33241675Suqs 34294113Sbapt#include "mandoc_aux.h" 35294113Sbapt#include "mandoc.h" 36294113Sbapt#include "roff.h" 37241675Suqs#include "mdoc.h" 38294113Sbapt#include "libmandoc.h" 39294113Sbapt#include "roff_int.h" 40241675Suqs#include "libmdoc.h" 41241675Suqs 42241675Suqs/* FIXME: .Bl -diag can't have non-text children in HEAD. */ 43241675Suqs 44294113Sbapt#define POST_ARGS struct roff_man *mdoc 45241675Suqs 46241675Suqsenum check_ineq { 47241675Suqs CHECK_LT, 48241675Suqs CHECK_GT, 49241675Suqs CHECK_EQ 50241675Suqs}; 51241675Suqs 52275432Sbapttypedef void (*v_post)(POST_ARGS); 53241675Suqs 54294113Sbaptstatic void check_text(struct roff_man *, int, int, char *); 55294113Sbaptstatic void check_argv(struct roff_man *, 56294113Sbapt struct roff_node *, struct mdoc_argv *); 57294113Sbaptstatic void check_args(struct roff_man *, struct roff_node *); 58294113Sbaptstatic int child_an(const struct roff_node *); 59294113Sbaptstatic size_t macro2len(int); 60275432Sbaptstatic void rewrite_macro2len(char **); 61241675Suqs 62275432Sbaptstatic void post_an(POST_ARGS); 63294113Sbaptstatic void post_an_norm(POST_ARGS); 64275432Sbaptstatic void post_at(POST_ARGS); 65294113Sbaptstatic void post_bd(POST_ARGS); 66275432Sbaptstatic void post_bf(POST_ARGS); 67275432Sbaptstatic void post_bk(POST_ARGS); 68275432Sbaptstatic void post_bl(POST_ARGS); 69275432Sbaptstatic void post_bl_block(POST_ARGS); 70275432Sbaptstatic void post_bl_block_tag(POST_ARGS); 71275432Sbaptstatic void post_bl_head(POST_ARGS); 72294113Sbaptstatic void post_bl_norm(POST_ARGS); 73275432Sbaptstatic void post_bx(POST_ARGS); 74275432Sbaptstatic void post_defaults(POST_ARGS); 75294113Sbaptstatic void post_display(POST_ARGS); 76275432Sbaptstatic void post_dd(POST_ARGS); 77275432Sbaptstatic void post_dt(POST_ARGS); 78275432Sbaptstatic void post_en(POST_ARGS); 79275432Sbaptstatic void post_es(POST_ARGS); 80275432Sbaptstatic void post_eoln(POST_ARGS); 81275432Sbaptstatic void post_ex(POST_ARGS); 82275432Sbaptstatic void post_fa(POST_ARGS); 83275432Sbaptstatic void post_fn(POST_ARGS); 84275432Sbaptstatic void post_fname(POST_ARGS); 85275432Sbaptstatic void post_fo(POST_ARGS); 86275432Sbaptstatic void post_hyph(POST_ARGS); 87275432Sbaptstatic void post_ignpar(POST_ARGS); 88275432Sbaptstatic void post_it(POST_ARGS); 89275432Sbaptstatic void post_lb(POST_ARGS); 90275432Sbaptstatic void post_nd(POST_ARGS); 91275432Sbaptstatic void post_nm(POST_ARGS); 92275432Sbaptstatic void post_ns(POST_ARGS); 93294113Sbaptstatic void post_obsolete(POST_ARGS); 94275432Sbaptstatic void post_os(POST_ARGS); 95275432Sbaptstatic void post_par(POST_ARGS); 96294113Sbaptstatic void post_prevpar(POST_ARGS); 97275432Sbaptstatic void post_root(POST_ARGS); 98275432Sbaptstatic void post_rs(POST_ARGS); 99275432Sbaptstatic void post_sh(POST_ARGS); 100275432Sbaptstatic void post_sh_head(POST_ARGS); 101275432Sbaptstatic void post_sh_name(POST_ARGS); 102275432Sbaptstatic void post_sh_see_also(POST_ARGS); 103275432Sbaptstatic void post_sh_authors(POST_ARGS); 104275432Sbaptstatic void post_sm(POST_ARGS); 105275432Sbaptstatic void post_st(POST_ARGS); 106294113Sbaptstatic void post_std(POST_ARGS); 107241675Suqs 108294113Sbaptstatic v_post mdoc_valids[MDOC_MAX] = { 109294113Sbapt NULL, /* Ap */ 110294113Sbapt post_dd, /* Dd */ 111294113Sbapt post_dt, /* Dt */ 112294113Sbapt post_os, /* Os */ 113294113Sbapt post_sh, /* Sh */ 114294113Sbapt post_ignpar, /* Ss */ 115294113Sbapt post_par, /* Pp */ 116294113Sbapt post_display, /* D1 */ 117294113Sbapt post_display, /* Dl */ 118294113Sbapt post_display, /* Bd */ 119294113Sbapt NULL, /* Ed */ 120294113Sbapt post_bl, /* Bl */ 121294113Sbapt NULL, /* El */ 122294113Sbapt post_it, /* It */ 123294113Sbapt NULL, /* Ad */ 124294113Sbapt post_an, /* An */ 125294113Sbapt post_defaults, /* Ar */ 126294113Sbapt NULL, /* Cd */ 127294113Sbapt NULL, /* Cm */ 128294113Sbapt NULL, /* Dv */ 129294113Sbapt NULL, /* Er */ 130294113Sbapt NULL, /* Ev */ 131294113Sbapt post_ex, /* Ex */ 132294113Sbapt post_fa, /* Fa */ 133294113Sbapt NULL, /* Fd */ 134294113Sbapt NULL, /* Fl */ 135294113Sbapt post_fn, /* Fn */ 136294113Sbapt NULL, /* Ft */ 137294113Sbapt NULL, /* Ic */ 138294113Sbapt NULL, /* In */ 139294113Sbapt post_defaults, /* Li */ 140294113Sbapt post_nd, /* Nd */ 141294113Sbapt post_nm, /* Nm */ 142294113Sbapt NULL, /* Op */ 143294113Sbapt post_obsolete, /* Ot */ 144294113Sbapt post_defaults, /* Pa */ 145294113Sbapt post_std, /* Rv */ 146294113Sbapt post_st, /* St */ 147294113Sbapt NULL, /* Va */ 148294113Sbapt NULL, /* Vt */ 149294113Sbapt NULL, /* Xr */ 150294113Sbapt NULL, /* %A */ 151294113Sbapt post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */ 152294113Sbapt NULL, /* %D */ 153294113Sbapt NULL, /* %I */ 154294113Sbapt NULL, /* %J */ 155294113Sbapt post_hyph, /* %N */ 156294113Sbapt post_hyph, /* %O */ 157294113Sbapt NULL, /* %P */ 158294113Sbapt post_hyph, /* %R */ 159294113Sbapt post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */ 160294113Sbapt NULL, /* %V */ 161294113Sbapt NULL, /* Ac */ 162294113Sbapt NULL, /* Ao */ 163294113Sbapt NULL, /* Aq */ 164294113Sbapt post_at, /* At */ 165294113Sbapt NULL, /* Bc */ 166294113Sbapt post_bf, /* Bf */ 167294113Sbapt NULL, /* Bo */ 168294113Sbapt NULL, /* Bq */ 169294113Sbapt NULL, /* Bsx */ 170294113Sbapt post_bx, /* Bx */ 171294113Sbapt post_obsolete, /* Db */ 172294113Sbapt NULL, /* Dc */ 173294113Sbapt NULL, /* Do */ 174294113Sbapt NULL, /* Dq */ 175294113Sbapt NULL, /* Ec */ 176294113Sbapt NULL, /* Ef */ 177294113Sbapt NULL, /* Em */ 178294113Sbapt NULL, /* Eo */ 179294113Sbapt NULL, /* Fx */ 180294113Sbapt NULL, /* Ms */ 181294113Sbapt NULL, /* No */ 182294113Sbapt post_ns, /* Ns */ 183294113Sbapt NULL, /* Nx */ 184294113Sbapt NULL, /* Ox */ 185294113Sbapt NULL, /* Pc */ 186294113Sbapt NULL, /* Pf */ 187294113Sbapt NULL, /* Po */ 188294113Sbapt NULL, /* Pq */ 189294113Sbapt NULL, /* Qc */ 190294113Sbapt NULL, /* Ql */ 191294113Sbapt NULL, /* Qo */ 192294113Sbapt NULL, /* Qq */ 193294113Sbapt NULL, /* Re */ 194294113Sbapt post_rs, /* Rs */ 195294113Sbapt NULL, /* Sc */ 196294113Sbapt NULL, /* So */ 197294113Sbapt NULL, /* Sq */ 198294113Sbapt post_sm, /* Sm */ 199294113Sbapt post_hyph, /* Sx */ 200294113Sbapt NULL, /* Sy */ 201294113Sbapt NULL, /* Tn */ 202294113Sbapt NULL, /* Ux */ 203294113Sbapt NULL, /* Xc */ 204294113Sbapt NULL, /* Xo */ 205294113Sbapt post_fo, /* Fo */ 206294113Sbapt NULL, /* Fc */ 207294113Sbapt NULL, /* Oo */ 208294113Sbapt NULL, /* Oc */ 209294113Sbapt post_bk, /* Bk */ 210294113Sbapt NULL, /* Ek */ 211294113Sbapt post_eoln, /* Bt */ 212294113Sbapt NULL, /* Hf */ 213294113Sbapt post_obsolete, /* Fr */ 214294113Sbapt post_eoln, /* Ud */ 215294113Sbapt post_lb, /* Lb */ 216294113Sbapt post_par, /* Lp */ 217294113Sbapt NULL, /* Lk */ 218294113Sbapt post_defaults, /* Mt */ 219294113Sbapt NULL, /* Brq */ 220294113Sbapt NULL, /* Bro */ 221294113Sbapt NULL, /* Brc */ 222294113Sbapt NULL, /* %C */ 223294113Sbapt post_es, /* Es */ 224294113Sbapt post_en, /* En */ 225294113Sbapt NULL, /* Dx */ 226294113Sbapt NULL, /* %Q */ 227294113Sbapt post_par, /* br */ 228294113Sbapt post_par, /* sp */ 229294113Sbapt NULL, /* %U */ 230294113Sbapt NULL, /* Ta */ 231294113Sbapt NULL, /* ll */ 232241675Suqs}; 233241675Suqs 234241675Suqs#define RSORD_MAX 14 /* Number of `Rs' blocks. */ 235241675Suqs 236294113Sbaptstatic const int rsord[RSORD_MAX] = { 237241675Suqs MDOC__A, 238241675Suqs MDOC__T, 239241675Suqs MDOC__B, 240241675Suqs MDOC__I, 241241675Suqs MDOC__J, 242241675Suqs MDOC__R, 243241675Suqs MDOC__N, 244241675Suqs MDOC__V, 245261344Suqs MDOC__U, 246241675Suqs MDOC__P, 247241675Suqs MDOC__Q, 248261344Suqs MDOC__C, 249241675Suqs MDOC__D, 250261344Suqs MDOC__O 251241675Suqs}; 252241675Suqs 253241675Suqsstatic const char * const secnames[SEC__MAX] = { 254241675Suqs NULL, 255241675Suqs "NAME", 256241675Suqs "LIBRARY", 257241675Suqs "SYNOPSIS", 258241675Suqs "DESCRIPTION", 259274880Sbapt "CONTEXT", 260241675Suqs "IMPLEMENTATION NOTES", 261241675Suqs "RETURN VALUES", 262241675Suqs "ENVIRONMENT", 263241675Suqs "FILES", 264241675Suqs "EXIT STATUS", 265241675Suqs "EXAMPLES", 266241675Suqs "DIAGNOSTICS", 267241675Suqs "COMPATIBILITY", 268241675Suqs "ERRORS", 269241675Suqs "SEE ALSO", 270241675Suqs "STANDARDS", 271241675Suqs "HISTORY", 272241675Suqs "AUTHORS", 273241675Suqs "CAVEATS", 274241675Suqs "BUGS", 275241675Suqs "SECURITY CONSIDERATIONS", 276241675Suqs NULL 277241675Suqs}; 278241675Suqs 279274880Sbapt 280275432Sbaptvoid 281294113Sbaptmdoc_node_validate(struct roff_man *mdoc) 282241675Suqs{ 283294113Sbapt struct roff_node *n; 284294113Sbapt v_post *p; 285241675Suqs 286294113Sbapt n = mdoc->last; 287294113Sbapt mdoc->last = mdoc->last->child; 288294113Sbapt while (mdoc->last != NULL) { 289294113Sbapt mdoc_node_validate(mdoc); 290294113Sbapt if (mdoc->last == n) 291294113Sbapt mdoc->last = mdoc->last->child; 292294113Sbapt else 293294113Sbapt mdoc->last = mdoc->last->next; 294294113Sbapt } 295294113Sbapt 296294113Sbapt mdoc->last = n; 297294113Sbapt mdoc->next = ROFF_NEXT_SIBLING; 298241675Suqs switch (n->type) { 299294113Sbapt case ROFFT_TEXT: 300279527Sbapt if (n->sec != SEC_SYNOPSIS || n->parent->tok != MDOC_Fd) 301279527Sbapt check_text(mdoc, n->line, n->pos, n->string); 302241675Suqs break; 303294113Sbapt case ROFFT_EQN: 304294113Sbapt case ROFFT_TBL: 305275432Sbapt break; 306294113Sbapt case ROFFT_ROOT: 307275432Sbapt post_root(mdoc); 308275432Sbapt break; 309241675Suqs default: 310294113Sbapt check_args(mdoc, mdoc->last); 311275432Sbapt 312275432Sbapt /* 313275432Sbapt * Closing delimiters are not special at the 314275432Sbapt * beginning of a block, opening delimiters 315275432Sbapt * are not special at the end. 316275432Sbapt */ 317275432Sbapt 318275432Sbapt if (n->child != NULL) 319275432Sbapt n->child->flags &= ~MDOC_DELIMC; 320275432Sbapt if (n->last != NULL) 321275432Sbapt n->last->flags &= ~MDOC_DELIMO; 322275432Sbapt 323275432Sbapt /* Call the macro's postprocessor. */ 324275432Sbapt 325294113Sbapt p = mdoc_valids + n->tok; 326275432Sbapt if (*p) 327275432Sbapt (*p)(mdoc); 328294113Sbapt if (mdoc->last == n) 329294113Sbapt mdoc_state(mdoc, n); 330275432Sbapt break; 331241675Suqs } 332241675Suqs} 333241675Suqs 334275432Sbaptstatic void 335294113Sbaptcheck_args(struct roff_man *mdoc, struct roff_node *n) 336241675Suqs{ 337241675Suqs int i; 338241675Suqs 339241675Suqs if (NULL == n->args) 340241675Suqs return; 341241675Suqs 342241675Suqs assert(n->args->argc); 343241675Suqs for (i = 0; i < (int)n->args->argc; i++) 344261344Suqs check_argv(mdoc, n, &n->args->argv[i]); 345241675Suqs} 346241675Suqs 347241675Suqsstatic void 348294113Sbaptcheck_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v) 349241675Suqs{ 350241675Suqs int i; 351241675Suqs 352241675Suqs for (i = 0; i < (int)v->sz; i++) 353261344Suqs check_text(mdoc, v->line, v->pos, v->value[i]); 354241675Suqs} 355241675Suqs 356241675Suqsstatic void 357294113Sbaptcheck_text(struct roff_man *mdoc, int ln, int pos, char *p) 358241675Suqs{ 359241675Suqs char *cp; 360241675Suqs 361261344Suqs if (MDOC_LITERAL & mdoc->flags) 362241675Suqs return; 363241675Suqs 364241675Suqs for (cp = p; NULL != (p = strchr(p, '\t')); p++) 365274880Sbapt mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse, 366274880Sbapt ln, pos + (int)(p - cp), NULL); 367241675Suqs} 368241675Suqs 369275432Sbaptstatic void 370294113Sbaptpost_bl_norm(POST_ARGS) 371241675Suqs{ 372294113Sbapt struct roff_node *n; 373274880Sbapt struct mdoc_argv *argv, *wa; 374274880Sbapt int i; 375274880Sbapt enum mdocargt mdoclt; 376241675Suqs enum mdoc_list lt; 377241675Suqs 378294113Sbapt n = mdoc->last->parent; 379294113Sbapt n->norm->Bl.type = LIST__NONE; 380241675Suqs 381274880Sbapt /* 382241675Suqs * First figure out which kind of list to use: bind ourselves to 383241675Suqs * the first mentioned list type and warn about any remaining 384241675Suqs * ones. If we find no list type, we default to LIST_item. 385241675Suqs */ 386241675Suqs 387275432Sbapt wa = (n->args == NULL) ? NULL : n->args->argv; 388274880Sbapt mdoclt = MDOC_ARG_MAX; 389241675Suqs for (i = 0; n->args && i < (int)n->args->argc; i++) { 390274880Sbapt argv = n->args->argv + i; 391241675Suqs lt = LIST__NONE; 392274880Sbapt switch (argv->arg) { 393241675Suqs /* Set list types. */ 394274880Sbapt case MDOC_Bullet: 395241675Suqs lt = LIST_bullet; 396241675Suqs break; 397274880Sbapt case MDOC_Dash: 398241675Suqs lt = LIST_dash; 399241675Suqs break; 400274880Sbapt case MDOC_Enum: 401241675Suqs lt = LIST_enum; 402241675Suqs break; 403274880Sbapt case MDOC_Hyphen: 404241675Suqs lt = LIST_hyphen; 405241675Suqs break; 406274880Sbapt case MDOC_Item: 407241675Suqs lt = LIST_item; 408241675Suqs break; 409274880Sbapt case MDOC_Tag: 410241675Suqs lt = LIST_tag; 411241675Suqs break; 412274880Sbapt case MDOC_Diag: 413241675Suqs lt = LIST_diag; 414241675Suqs break; 415274880Sbapt case MDOC_Hang: 416241675Suqs lt = LIST_hang; 417241675Suqs break; 418274880Sbapt case MDOC_Ohang: 419241675Suqs lt = LIST_ohang; 420241675Suqs break; 421274880Sbapt case MDOC_Inset: 422241675Suqs lt = LIST_inset; 423241675Suqs break; 424274880Sbapt case MDOC_Column: 425241675Suqs lt = LIST_column; 426241675Suqs break; 427241675Suqs /* Set list arguments. */ 428274880Sbapt case MDOC_Compact: 429274880Sbapt if (n->norm->Bl.comp) 430274880Sbapt mandoc_msg(MANDOCERR_ARG_REP, 431274880Sbapt mdoc->parse, argv->line, 432274880Sbapt argv->pos, "Bl -compact"); 433274880Sbapt n->norm->Bl.comp = 1; 434241675Suqs break; 435274880Sbapt case MDOC_Width: 436274880Sbapt wa = argv; 437274880Sbapt if (0 == argv->sz) { 438274880Sbapt mandoc_msg(MANDOCERR_ARG_EMPTY, 439274880Sbapt mdoc->parse, argv->line, 440274880Sbapt argv->pos, "Bl -width"); 441274880Sbapt n->norm->Bl.width = "0n"; 442241675Suqs break; 443241675Suqs } 444274880Sbapt if (NULL != n->norm->Bl.width) 445274880Sbapt mandoc_vmsg(MANDOCERR_ARG_REP, 446274880Sbapt mdoc->parse, argv->line, 447274880Sbapt argv->pos, "Bl -width %s", 448274880Sbapt argv->value[0]); 449275432Sbapt rewrite_macro2len(argv->value); 450274880Sbapt n->norm->Bl.width = argv->value[0]; 451241675Suqs break; 452274880Sbapt case MDOC_Offset: 453274880Sbapt if (0 == argv->sz) { 454274880Sbapt mandoc_msg(MANDOCERR_ARG_EMPTY, 455274880Sbapt mdoc->parse, argv->line, 456274880Sbapt argv->pos, "Bl -offset"); 457241675Suqs break; 458241675Suqs } 459274880Sbapt if (NULL != n->norm->Bl.offs) 460274880Sbapt mandoc_vmsg(MANDOCERR_ARG_REP, 461274880Sbapt mdoc->parse, argv->line, 462274880Sbapt argv->pos, "Bl -offset %s", 463274880Sbapt argv->value[0]); 464275432Sbapt rewrite_macro2len(argv->value); 465274880Sbapt n->norm->Bl.offs = argv->value[0]; 466241675Suqs break; 467241675Suqs default: 468241675Suqs continue; 469241675Suqs } 470274880Sbapt if (LIST__NONE == lt) 471274880Sbapt continue; 472274880Sbapt mdoclt = argv->arg; 473241675Suqs 474241675Suqs /* Check: multiple list types. */ 475241675Suqs 476274880Sbapt if (LIST__NONE != n->norm->Bl.type) { 477274880Sbapt mandoc_vmsg(MANDOCERR_BL_REP, 478274880Sbapt mdoc->parse, n->line, n->pos, 479274880Sbapt "Bl -%s", mdoc_argnames[argv->arg]); 480274880Sbapt continue; 481241675Suqs } 482241675Suqs 483241675Suqs /* The list type should come first. */ 484241675Suqs 485274880Sbapt if (n->norm->Bl.width || 486274880Sbapt n->norm->Bl.offs || 487274880Sbapt n->norm->Bl.comp) 488274880Sbapt mandoc_vmsg(MANDOCERR_BL_LATETYPE, 489274880Sbapt mdoc->parse, n->line, n->pos, "Bl -%s", 490274880Sbapt mdoc_argnames[n->args->argv[0].arg]); 491241675Suqs 492274880Sbapt n->norm->Bl.type = lt; 493274880Sbapt if (LIST_column == lt) { 494274880Sbapt n->norm->Bl.ncols = argv->sz; 495274880Sbapt n->norm->Bl.cols = (void *)argv->value; 496274880Sbapt } 497241675Suqs } 498241675Suqs 499241675Suqs /* Allow lists to default to LIST_item. */ 500241675Suqs 501241675Suqs if (LIST__NONE == n->norm->Bl.type) { 502274880Sbapt mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse, 503274880Sbapt n->line, n->pos, "Bl"); 504241675Suqs n->norm->Bl.type = LIST_item; 505241675Suqs } 506241675Suqs 507274880Sbapt /* 508241675Suqs * Validate the width field. Some list types don't need width 509241675Suqs * types and should be warned about them. Others should have it 510261344Suqs * and must also be warned. Yet others have a default and need 511261344Suqs * no warning. 512241675Suqs */ 513241675Suqs 514241675Suqs switch (n->norm->Bl.type) { 515274880Sbapt case LIST_tag: 516261344Suqs if (NULL == n->norm->Bl.width) 517274880Sbapt mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse, 518274880Sbapt n->line, n->pos, "Bl -tag"); 519241675Suqs break; 520274880Sbapt case LIST_column: 521274880Sbapt case LIST_diag: 522274880Sbapt case LIST_ohang: 523274880Sbapt case LIST_inset: 524274880Sbapt case LIST_item: 525241675Suqs if (n->norm->Bl.width) 526274880Sbapt mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse, 527274880Sbapt wa->line, wa->pos, "Bl -%s", 528274880Sbapt mdoc_argnames[mdoclt]); 529241675Suqs break; 530274880Sbapt case LIST_bullet: 531274880Sbapt case LIST_dash: 532274880Sbapt case LIST_hyphen: 533261344Suqs if (NULL == n->norm->Bl.width) 534261344Suqs n->norm->Bl.width = "2n"; 535261344Suqs break; 536274880Sbapt case LIST_enum: 537261344Suqs if (NULL == n->norm->Bl.width) 538261344Suqs n->norm->Bl.width = "3n"; 539261344Suqs break; 540241675Suqs default: 541241675Suqs break; 542241675Suqs } 543241675Suqs} 544241675Suqs 545275432Sbaptstatic void 546294113Sbaptpost_bd(POST_ARGS) 547241675Suqs{ 548294113Sbapt struct roff_node *n; 549274880Sbapt struct mdoc_argv *argv; 550274880Sbapt int i; 551274880Sbapt enum mdoc_disp dt; 552241675Suqs 553294113Sbapt n = mdoc->last; 554241675Suqs for (i = 0; n->args && i < (int)n->args->argc; i++) { 555274880Sbapt argv = n->args->argv + i; 556241675Suqs dt = DISP__NONE; 557241675Suqs 558274880Sbapt switch (argv->arg) { 559274880Sbapt case MDOC_Centred: 560274880Sbapt dt = DISP_centered; 561241675Suqs break; 562274880Sbapt case MDOC_Ragged: 563241675Suqs dt = DISP_ragged; 564241675Suqs break; 565274880Sbapt case MDOC_Unfilled: 566241675Suqs dt = DISP_unfilled; 567241675Suqs break; 568274880Sbapt case MDOC_Filled: 569241675Suqs dt = DISP_filled; 570241675Suqs break; 571274880Sbapt case MDOC_Literal: 572241675Suqs dt = DISP_literal; 573241675Suqs break; 574274880Sbapt case MDOC_File: 575274880Sbapt mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse, 576274880Sbapt n->line, n->pos, NULL); 577275432Sbapt break; 578274880Sbapt case MDOC_Offset: 579274880Sbapt if (0 == argv->sz) { 580274880Sbapt mandoc_msg(MANDOCERR_ARG_EMPTY, 581274880Sbapt mdoc->parse, argv->line, 582274880Sbapt argv->pos, "Bd -offset"); 583241675Suqs break; 584241675Suqs } 585274880Sbapt if (NULL != n->norm->Bd.offs) 586274880Sbapt mandoc_vmsg(MANDOCERR_ARG_REP, 587274880Sbapt mdoc->parse, argv->line, 588274880Sbapt argv->pos, "Bd -offset %s", 589274880Sbapt argv->value[0]); 590275432Sbapt rewrite_macro2len(argv->value); 591274880Sbapt n->norm->Bd.offs = argv->value[0]; 592241675Suqs break; 593274880Sbapt case MDOC_Compact: 594274880Sbapt if (n->norm->Bd.comp) 595274880Sbapt mandoc_msg(MANDOCERR_ARG_REP, 596274880Sbapt mdoc->parse, argv->line, 597274880Sbapt argv->pos, "Bd -compact"); 598274880Sbapt n->norm->Bd.comp = 1; 599241675Suqs break; 600241675Suqs default: 601241675Suqs abort(); 602241675Suqs } 603274880Sbapt if (DISP__NONE == dt) 604274880Sbapt continue; 605241675Suqs 606274880Sbapt if (DISP__NONE == n->norm->Bd.type) 607241675Suqs n->norm->Bd.type = dt; 608274880Sbapt else 609274880Sbapt mandoc_vmsg(MANDOCERR_BD_REP, 610274880Sbapt mdoc->parse, n->line, n->pos, 611274880Sbapt "Bd -%s", mdoc_argnames[argv->arg]); 612241675Suqs } 613241675Suqs 614241675Suqs if (DISP__NONE == n->norm->Bd.type) { 615274880Sbapt mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse, 616274880Sbapt n->line, n->pos, "Bd"); 617241675Suqs n->norm->Bd.type = DISP_ragged; 618241675Suqs } 619241675Suqs} 620241675Suqs 621275432Sbaptstatic void 622294113Sbaptpost_an_norm(POST_ARGS) 623241675Suqs{ 624294113Sbapt struct roff_node *n; 625274880Sbapt struct mdoc_argv *argv; 626274880Sbapt size_t i; 627241675Suqs 628294113Sbapt n = mdoc->last; 629274880Sbapt if (n->args == NULL) 630275432Sbapt return; 631241675Suqs 632274880Sbapt for (i = 1; i < n->args->argc; i++) { 633274880Sbapt argv = n->args->argv + i; 634274880Sbapt mandoc_vmsg(MANDOCERR_AN_REP, 635274880Sbapt mdoc->parse, argv->line, argv->pos, 636274880Sbapt "An -%s", mdoc_argnames[argv->arg]); 637274880Sbapt } 638241675Suqs 639274880Sbapt argv = n->args->argv; 640274880Sbapt if (argv->arg == MDOC_Split) 641241675Suqs n->norm->An.auth = AUTH_split; 642274880Sbapt else if (argv->arg == MDOC_Nosplit) 643241675Suqs n->norm->An.auth = AUTH_nosplit; 644241675Suqs else 645241675Suqs abort(); 646241675Suqs} 647241675Suqs 648275432Sbaptstatic void 649294113Sbaptpost_std(POST_ARGS) 650241675Suqs{ 651294113Sbapt struct roff_node *n; 652241675Suqs 653294113Sbapt n = mdoc->last; 654294113Sbapt if (n->args && n->args->argc == 1) 655294113Sbapt if (n->args->argv[0].arg == MDOC_Std) 656275432Sbapt return; 657241675Suqs 658274880Sbapt mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse, 659274880Sbapt n->line, n->pos, mdoc_macronames[n->tok]); 660241675Suqs} 661241675Suqs 662275432Sbaptstatic void 663294113Sbaptpost_obsolete(POST_ARGS) 664241675Suqs{ 665294113Sbapt struct roff_node *n; 666241675Suqs 667294113Sbapt n = mdoc->last; 668294113Sbapt if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) 669274880Sbapt mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse, 670274880Sbapt n->line, n->pos, mdoc_macronames[n->tok]); 671274880Sbapt} 672241675Suqs 673275432Sbaptstatic void 674241675Suqspost_bf(POST_ARGS) 675241675Suqs{ 676294113Sbapt struct roff_node *np, *nch; 677241675Suqs 678241675Suqs /* 679241675Suqs * Unlike other data pointers, these are "housed" by the HEAD 680241675Suqs * element, which contains the goods. 681241675Suqs */ 682241675Suqs 683279527Sbapt np = mdoc->last; 684294113Sbapt if (np->type != ROFFT_HEAD) 685275432Sbapt return; 686241675Suqs 687294113Sbapt assert(np->parent->type == ROFFT_BLOCK); 688294113Sbapt assert(np->parent->tok == MDOC_Bf); 689241675Suqs 690274880Sbapt /* Check the number of arguments. */ 691241675Suqs 692274880Sbapt nch = np->child; 693294113Sbapt if (np->parent->args == NULL) { 694294113Sbapt if (nch == NULL) { 695274880Sbapt mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse, 696274880Sbapt np->line, np->pos, "Bf"); 697275432Sbapt return; 698274880Sbapt } 699274880Sbapt nch = nch->next; 700241675Suqs } 701294113Sbapt if (nch != NULL) 702274880Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 703274880Sbapt nch->line, nch->pos, "Bf ... %s", nch->string); 704241675Suqs 705241675Suqs /* Extract argument into data. */ 706274880Sbapt 707294113Sbapt if (np->parent->args != NULL) { 708294113Sbapt switch (np->parent->args->argv[0].arg) { 709294113Sbapt case MDOC_Emphasis: 710241675Suqs np->norm->Bf.font = FONT_Em; 711294113Sbapt break; 712294113Sbapt case MDOC_Literal: 713241675Suqs np->norm->Bf.font = FONT_Li; 714294113Sbapt break; 715294113Sbapt case MDOC_Symbolic: 716241675Suqs np->norm->Bf.font = FONT_Sy; 717294113Sbapt break; 718294113Sbapt default: 719241675Suqs abort(); 720294113Sbapt } 721275432Sbapt return; 722241675Suqs } 723241675Suqs 724241675Suqs /* Extract parameter into data. */ 725241675Suqs 726294113Sbapt if ( ! strcmp(np->child->string, "Em")) 727241675Suqs np->norm->Bf.font = FONT_Em; 728294113Sbapt else if ( ! strcmp(np->child->string, "Li")) 729241675Suqs np->norm->Bf.font = FONT_Li; 730294113Sbapt else if ( ! strcmp(np->child->string, "Sy")) 731241675Suqs np->norm->Bf.font = FONT_Sy; 732274880Sbapt else 733274880Sbapt mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse, 734274880Sbapt np->child->line, np->child->pos, 735274880Sbapt "Bf %s", np->child->string); 736241675Suqs} 737241675Suqs 738275432Sbaptstatic void 739241675Suqspost_lb(POST_ARGS) 740241675Suqs{ 741294113Sbapt struct roff_node *n; 742274880Sbapt const char *stdlibname; 743274880Sbapt char *libname; 744241675Suqs 745274880Sbapt n = mdoc->last->child; 746294113Sbapt assert(n->type == ROFFT_TEXT); 747241675Suqs 748274880Sbapt if (NULL == (stdlibname = mdoc_a2lib(n->string))) 749274880Sbapt mandoc_asprintf(&libname, 750279527Sbapt "library \\(Lq%s\\(Rq", n->string); 751274880Sbapt else 752274880Sbapt libname = mandoc_strdup(stdlibname); 753241675Suqs 754274880Sbapt free(n->string); 755274880Sbapt n->string = libname; 756274880Sbapt} 757241675Suqs 758275432Sbaptstatic void 759274880Sbaptpost_eoln(POST_ARGS) 760274880Sbapt{ 761294113Sbapt const struct roff_node *n; 762241675Suqs 763274880Sbapt n = mdoc->last; 764294113Sbapt if (n->child != NULL) 765274880Sbapt mandoc_vmsg(MANDOCERR_ARG_SKIP, 766274880Sbapt mdoc->parse, n->line, n->pos, 767274880Sbapt "%s %s", mdoc_macronames[n->tok], 768274880Sbapt n->child->string); 769241675Suqs} 770241675Suqs 771275432Sbaptstatic void 772275432Sbaptpost_fname(POST_ARGS) 773275432Sbapt{ 774294113Sbapt const struct roff_node *n; 775275432Sbapt const char *cp; 776275432Sbapt size_t pos; 777275432Sbapt 778275432Sbapt n = mdoc->last->child; 779275432Sbapt pos = strcspn(n->string, "()"); 780275432Sbapt cp = n->string + pos; 781275432Sbapt if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*'))) 782275432Sbapt mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse, 783275432Sbapt n->line, n->pos + pos, n->string); 784275432Sbapt} 785275432Sbapt 786275432Sbaptstatic void 787275432Sbaptpost_fn(POST_ARGS) 788275432Sbapt{ 789275432Sbapt 790275432Sbapt post_fname(mdoc); 791275432Sbapt post_fa(mdoc); 792275432Sbapt} 793275432Sbapt 794275432Sbaptstatic void 795274880Sbaptpost_fo(POST_ARGS) 796241675Suqs{ 797294113Sbapt const struct roff_node *n; 798241675Suqs 799279527Sbapt n = mdoc->last; 800279527Sbapt 801294113Sbapt if (n->type != ROFFT_HEAD) 802279527Sbapt return; 803279527Sbapt 804279527Sbapt if (n->child == NULL) { 805279527Sbapt mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse, 806279527Sbapt n->line, n->pos, "Fo"); 807279527Sbapt return; 808279527Sbapt } 809279527Sbapt if (n->child != n->last) { 810279527Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 811279527Sbapt n->child->next->line, n->child->next->pos, 812279527Sbapt "Fo ... %s", n->child->next->string); 813279527Sbapt while (n->child != n->last) 814294113Sbapt roff_node_delete(mdoc, n->last); 815279527Sbapt } 816279527Sbapt 817279527Sbapt post_fname(mdoc); 818241675Suqs} 819241675Suqs 820275432Sbaptstatic void 821275432Sbaptpost_fa(POST_ARGS) 822275432Sbapt{ 823294113Sbapt const struct roff_node *n; 824275432Sbapt const char *cp; 825275432Sbapt 826275432Sbapt for (n = mdoc->last->child; n != NULL; n = n->next) { 827275432Sbapt for (cp = n->string; *cp != '\0'; cp++) { 828275432Sbapt /* Ignore callbacks and alterations. */ 829275432Sbapt if (*cp == '(' || *cp == '{') 830275432Sbapt break; 831275432Sbapt if (*cp != ',') 832275432Sbapt continue; 833275432Sbapt mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse, 834275432Sbapt n->line, n->pos + (cp - n->string), 835275432Sbapt n->string); 836275432Sbapt break; 837275432Sbapt } 838275432Sbapt } 839275432Sbapt} 840275432Sbapt 841275432Sbaptstatic void 842241675Suqspost_nm(POST_ARGS) 843241675Suqs{ 844294113Sbapt struct roff_node *n; 845241675Suqs 846279527Sbapt n = mdoc->last; 847279527Sbapt 848279527Sbapt if (n->last != NULL && 849279527Sbapt (n->last->tok == MDOC_Pp || 850279527Sbapt n->last->tok == MDOC_Lp)) 851279527Sbapt mdoc_node_relink(mdoc, n->last); 852279527Sbapt 853294113Sbapt if (mdoc->meta.name != NULL) 854275432Sbapt return; 855241675Suqs 856294113Sbapt deroff(&mdoc->meta.name, n); 857241675Suqs 858294113Sbapt if (mdoc->meta.name == NULL) 859274880Sbapt mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse, 860279527Sbapt n->line, n->pos, "Nm"); 861241675Suqs} 862241675Suqs 863275432Sbaptstatic void 864274880Sbaptpost_nd(POST_ARGS) 865274880Sbapt{ 866294113Sbapt struct roff_node *n; 867274880Sbapt 868279527Sbapt n = mdoc->last; 869279527Sbapt 870294113Sbapt if (n->type != ROFFT_BODY) 871279527Sbapt return; 872279527Sbapt 873279527Sbapt if (n->child == NULL) 874279527Sbapt mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse, 875279527Sbapt n->line, n->pos, "Nd"); 876279527Sbapt 877275432Sbapt post_hyph(mdoc); 878274880Sbapt} 879274880Sbapt 880275432Sbaptstatic void 881294113Sbaptpost_display(POST_ARGS) 882274880Sbapt{ 883294113Sbapt struct roff_node *n, *np; 884274880Sbapt 885279527Sbapt n = mdoc->last; 886294113Sbapt switch (n->type) { 887294113Sbapt case ROFFT_BODY: 888294113Sbapt if (n->end != ENDBODY_NOT) 889294113Sbapt break; 890294113Sbapt if (n->child == NULL) 891294113Sbapt mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, 892294113Sbapt n->line, n->pos, mdoc_macronames[n->tok]); 893294113Sbapt else if (n->tok == MDOC_D1) 894294113Sbapt post_hyph(mdoc); 895294113Sbapt break; 896294113Sbapt case ROFFT_BLOCK: 897294113Sbapt if (n->tok == MDOC_Bd) { 898294113Sbapt if (n->args == NULL) { 899294113Sbapt mandoc_msg(MANDOCERR_BD_NOARG, 900294113Sbapt mdoc->parse, n->line, n->pos, "Bd"); 901294113Sbapt mdoc->next = ROFF_NEXT_SIBLING; 902294113Sbapt while (n->body->child != NULL) 903294113Sbapt mdoc_node_relink(mdoc, 904294113Sbapt n->body->child); 905294113Sbapt roff_node_delete(mdoc, n); 906294113Sbapt break; 907294113Sbapt } 908294113Sbapt post_bd(mdoc); 909294113Sbapt post_prevpar(mdoc); 910294113Sbapt } 911294113Sbapt for (np = n->parent; np != NULL; np = np->parent) { 912294113Sbapt if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { 913294113Sbapt mandoc_vmsg(MANDOCERR_BD_NEST, 914294113Sbapt mdoc->parse, n->line, n->pos, 915294113Sbapt "%s in Bd", mdoc_macronames[n->tok]); 916294113Sbapt break; 917294113Sbapt } 918294113Sbapt } 919294113Sbapt break; 920294113Sbapt default: 921294113Sbapt break; 922294113Sbapt } 923274880Sbapt} 924274880Sbapt 925275432Sbaptstatic void 926241675Suqspost_defaults(POST_ARGS) 927241675Suqs{ 928294113Sbapt struct roff_node *nn; 929241675Suqs 930241675Suqs /* 931241675Suqs * The `Ar' defaults to "file ..." if no value is provided as an 932241675Suqs * argument; the `Mt' and `Pa' macros use "~"; the `Li' just 933241675Suqs * gets an empty string. 934241675Suqs */ 935241675Suqs 936294113Sbapt if (mdoc->last->child != NULL) 937275432Sbapt return; 938274880Sbapt 939241675Suqs nn = mdoc->last; 940241675Suqs 941241675Suqs switch (nn->tok) { 942274880Sbapt case MDOC_Ar: 943294113Sbapt mdoc->next = ROFF_NEXT_CHILD; 944294113Sbapt roff_word_alloc(mdoc, nn->line, nn->pos, "file"); 945294113Sbapt roff_word_alloc(mdoc, nn->line, nn->pos, "..."); 946241675Suqs break; 947274880Sbapt case MDOC_Pa: 948274880Sbapt case MDOC_Mt: 949294113Sbapt mdoc->next = ROFF_NEXT_CHILD; 950294113Sbapt roff_word_alloc(mdoc, nn->line, nn->pos, "~"); 951241675Suqs break; 952241675Suqs default: 953241675Suqs abort(); 954274880Sbapt } 955241675Suqs mdoc->last = nn; 956241675Suqs} 957241675Suqs 958275432Sbaptstatic void 959241675Suqspost_at(POST_ARGS) 960241675Suqs{ 961294113Sbapt struct roff_node *n; 962274880Sbapt const char *std_att; 963274880Sbapt char *att; 964241675Suqs 965274880Sbapt n = mdoc->last; 966274880Sbapt if (n->child == NULL) { 967294113Sbapt mdoc->next = ROFF_NEXT_CHILD; 968294113Sbapt roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); 969274880Sbapt mdoc->last = n; 970275432Sbapt return; 971274880Sbapt } 972274880Sbapt 973241675Suqs /* 974241675Suqs * If we have a child, look it up in the standard keys. If a 975241675Suqs * key exist, use that instead of the child; if it doesn't, 976241675Suqs * prefix "AT&T UNIX " to the existing data. 977241675Suqs */ 978241675Suqs 979274880Sbapt n = n->child; 980294113Sbapt assert(n->type == ROFFT_TEXT); 981294113Sbapt if ((std_att = mdoc_a2att(n->string)) == NULL) { 982274880Sbapt mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse, 983274880Sbapt n->line, n->pos, "At %s", n->string); 984274880Sbapt mandoc_asprintf(&att, "AT&T UNIX %s", n->string); 985274880Sbapt } else 986274880Sbapt att = mandoc_strdup(std_att); 987241675Suqs 988274880Sbapt free(n->string); 989274880Sbapt n->string = att; 990241675Suqs} 991241675Suqs 992275432Sbaptstatic void 993241675Suqspost_an(POST_ARGS) 994241675Suqs{ 995294113Sbapt struct roff_node *np, *nch; 996241675Suqs 997294113Sbapt post_an_norm(mdoc); 998294113Sbapt 999241675Suqs np = mdoc->last; 1000279527Sbapt nch = np->child; 1001279527Sbapt if (np->norm->An.auth == AUTH__NONE) { 1002279527Sbapt if (nch == NULL) 1003279527Sbapt mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, 1004279527Sbapt np->line, np->pos, "An"); 1005279527Sbapt } else if (nch != NULL) 1006279527Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 1007279527Sbapt nch->line, nch->pos, "An ... %s", nch->string); 1008241675Suqs} 1009241675Suqs 1010275432Sbaptstatic void 1011274880Sbaptpost_en(POST_ARGS) 1012274880Sbapt{ 1013241675Suqs 1014294113Sbapt post_obsolete(mdoc); 1015294113Sbapt if (mdoc->last->type == ROFFT_BLOCK) 1016274880Sbapt mdoc->last->norm->Es = mdoc->last_es; 1017274880Sbapt} 1018274880Sbapt 1019275432Sbaptstatic void 1020274880Sbaptpost_es(POST_ARGS) 1021274880Sbapt{ 1022274880Sbapt 1023294113Sbapt post_obsolete(mdoc); 1024274880Sbapt mdoc->last_es = mdoc->last; 1025274880Sbapt} 1026274880Sbapt 1027275432Sbaptstatic void 1028241675Suqspost_it(POST_ARGS) 1029241675Suqs{ 1030294113Sbapt struct roff_node *nbl, *nit, *nch; 1031241675Suqs int i, cols; 1032241675Suqs enum mdoc_list lt; 1033241675Suqs 1034294113Sbapt post_prevpar(mdoc); 1035294113Sbapt 1036274880Sbapt nit = mdoc->last; 1037294113Sbapt if (nit->type != ROFFT_BLOCK) 1038275432Sbapt return; 1039241675Suqs 1040274880Sbapt nbl = nit->parent->parent; 1041274880Sbapt lt = nbl->norm->Bl.type; 1042241675Suqs 1043241675Suqs switch (lt) { 1044274880Sbapt case LIST_tag: 1045274880Sbapt case LIST_hang: 1046274880Sbapt case LIST_ohang: 1047274880Sbapt case LIST_inset: 1048274880Sbapt case LIST_diag: 1049279527Sbapt if (nit->head->child == NULL) 1050274880Sbapt mandoc_vmsg(MANDOCERR_IT_NOHEAD, 1051274880Sbapt mdoc->parse, nit->line, nit->pos, 1052274880Sbapt "Bl -%s It", 1053274880Sbapt mdoc_argnames[nbl->args->argv[0].arg]); 1054241675Suqs break; 1055274880Sbapt case LIST_bullet: 1056274880Sbapt case LIST_dash: 1057274880Sbapt case LIST_enum: 1058274880Sbapt case LIST_hyphen: 1059279527Sbapt if (nit->body == NULL || nit->body->child == NULL) 1060274880Sbapt mandoc_vmsg(MANDOCERR_IT_NOBODY, 1061274880Sbapt mdoc->parse, nit->line, nit->pos, 1062274880Sbapt "Bl -%s It", 1063274880Sbapt mdoc_argnames[nbl->args->argv[0].arg]); 1064241675Suqs /* FALLTHROUGH */ 1065274880Sbapt case LIST_item: 1066279527Sbapt if (nit->head->child != NULL) 1067274880Sbapt mandoc_vmsg(MANDOCERR_ARG_SKIP, 1068274880Sbapt mdoc->parse, nit->line, nit->pos, 1069274880Sbapt "It %s", nit->head->child->string); 1070241675Suqs break; 1071274880Sbapt case LIST_column: 1072274880Sbapt cols = (int)nbl->norm->Bl.ncols; 1073241675Suqs 1074279527Sbapt assert(nit->head->child == NULL); 1075241675Suqs 1076294113Sbapt i = 0; 1077294113Sbapt for (nch = nit->child; nch != NULL; nch = nch->next) 1078294113Sbapt if (nch->type == ROFFT_BODY) 1079241675Suqs i++; 1080241675Suqs 1081274880Sbapt if (i < cols || i > cols + 1) 1082279527Sbapt mandoc_vmsg(MANDOCERR_BL_COL, 1083274880Sbapt mdoc->parse, nit->line, nit->pos, 1084279527Sbapt "%d columns, %d cells", cols, i); 1085274880Sbapt break; 1086241675Suqs default: 1087274880Sbapt abort(); 1088241675Suqs } 1089241675Suqs} 1090241675Suqs 1091275432Sbaptstatic void 1092274880Sbaptpost_bl_block(POST_ARGS) 1093241675Suqs{ 1094294113Sbapt struct roff_node *n, *ni, *nc; 1095241675Suqs 1096294113Sbapt post_prevpar(mdoc); 1097294113Sbapt 1098241675Suqs /* 1099241675Suqs * These are fairly complicated, so we've broken them into two 1100241675Suqs * functions. post_bl_block_tag() is called when a -tag is 1101241675Suqs * specified, but no -width (it must be guessed). The second 1102241675Suqs * when a -width is specified (macro indicators must be 1103241675Suqs * rewritten into real lengths). 1104241675Suqs */ 1105241675Suqs 1106241675Suqs n = mdoc->last; 1107241675Suqs 1108294113Sbapt if (n->norm->Bl.type == LIST_tag && 1109294113Sbapt n->norm->Bl.width == NULL) { 1110275432Sbapt post_bl_block_tag(mdoc); 1111294113Sbapt assert(n->norm->Bl.width != NULL); 1112261344Suqs } 1113241675Suqs 1114294113Sbapt for (ni = n->body->child; ni != NULL; ni = ni->next) { 1115294113Sbapt if (ni->body == NULL) 1116261344Suqs continue; 1117261344Suqs nc = ni->body->last; 1118294113Sbapt while (nc != NULL) { 1119261344Suqs switch (nc->tok) { 1120274880Sbapt case MDOC_Pp: 1121274880Sbapt case MDOC_Lp: 1122274880Sbapt case MDOC_br: 1123261344Suqs break; 1124261344Suqs default: 1125261344Suqs nc = NULL; 1126261344Suqs continue; 1127261344Suqs } 1128294113Sbapt if (ni->next == NULL) { 1129274880Sbapt mandoc_msg(MANDOCERR_PAR_MOVE, 1130274880Sbapt mdoc->parse, nc->line, nc->pos, 1131274880Sbapt mdoc_macronames[nc->tok]); 1132275432Sbapt mdoc_node_relink(mdoc, nc); 1133294113Sbapt } else if (n->norm->Bl.comp == 0 && 1134294113Sbapt n->norm->Bl.type != LIST_column) { 1135274880Sbapt mandoc_vmsg(MANDOCERR_PAR_SKIP, 1136274880Sbapt mdoc->parse, nc->line, nc->pos, 1137274880Sbapt "%s before It", 1138274880Sbapt mdoc_macronames[nc->tok]); 1139294113Sbapt roff_node_delete(mdoc, nc); 1140261344Suqs } else 1141261344Suqs break; 1142261344Suqs nc = ni->body->last; 1143261344Suqs } 1144261344Suqs } 1145241675Suqs} 1146241675Suqs 1147275432Sbapt/* 1148275432Sbapt * If the argument of -offset or -width is a macro, 1149275432Sbapt * replace it with the associated default width. 1150275432Sbapt */ 1151275432Sbaptvoid 1152275432Sbaptrewrite_macro2len(char **arg) 1153241675Suqs{ 1154241675Suqs size_t width; 1155294113Sbapt int tok; 1156241675Suqs 1157275432Sbapt if (*arg == NULL) 1158275432Sbapt return; 1159275432Sbapt else if ( ! strcmp(*arg, "Ds")) 1160241675Suqs width = 6; 1161294113Sbapt else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE) 1162275432Sbapt return; 1163274880Sbapt else 1164274880Sbapt width = macro2len(tok); 1165241675Suqs 1166275432Sbapt free(*arg); 1167275432Sbapt mandoc_asprintf(arg, "%zun", width); 1168241675Suqs} 1169241675Suqs 1170275432Sbaptstatic void 1171241675Suqspost_bl_block_tag(POST_ARGS) 1172241675Suqs{ 1173294113Sbapt struct roff_node *n, *nn; 1174241675Suqs size_t sz, ssz; 1175241675Suqs int i; 1176274880Sbapt char buf[24]; 1177241675Suqs 1178241675Suqs /* 1179241675Suqs * Calculate the -width for a `Bl -tag' list if it hasn't been 1180241675Suqs * provided. Uses the first head macro. NOTE AGAIN: this is 1181241675Suqs * ONLY if the -width argument has NOT been provided. See 1182275432Sbapt * rewrite_macro2len() for converting the -width string. 1183241675Suqs */ 1184241675Suqs 1185241675Suqs sz = 10; 1186241675Suqs n = mdoc->last; 1187241675Suqs 1188294113Sbapt for (nn = n->body->child; nn != NULL; nn = nn->next) { 1189294113Sbapt if (nn->tok != MDOC_It) 1190241675Suqs continue; 1191241675Suqs 1192294113Sbapt assert(nn->type == ROFFT_BLOCK); 1193241675Suqs nn = nn->head->child; 1194241675Suqs 1195241675Suqs if (nn == NULL) 1196241675Suqs break; 1197241675Suqs 1198294113Sbapt if (nn->type == ROFFT_TEXT) { 1199241675Suqs sz = strlen(nn->string) + 1; 1200241675Suqs break; 1201241675Suqs } 1202241675Suqs 1203241675Suqs if (0 != (ssz = macro2len(nn->tok))) 1204241675Suqs sz = ssz; 1205241675Suqs 1206241675Suqs break; 1207274880Sbapt } 1208241675Suqs 1209241675Suqs /* Defaults to ten ens. */ 1210241675Suqs 1211274880Sbapt (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz); 1212241675Suqs 1213241675Suqs /* 1214241675Suqs * We have to dynamically add this to the macro's argument list. 1215241675Suqs * We're guaranteed that a MDOC_Width doesn't already exist. 1216241675Suqs */ 1217241675Suqs 1218294113Sbapt assert(n->args != NULL); 1219241675Suqs i = (int)(n->args->argc)++; 1220241675Suqs 1221274880Sbapt n->args->argv = mandoc_reallocarray(n->args->argv, 1222274880Sbapt n->args->argc, sizeof(struct mdoc_argv)); 1223241675Suqs 1224241675Suqs n->args->argv[i].arg = MDOC_Width; 1225241675Suqs n->args->argv[i].line = n->line; 1226241675Suqs n->args->argv[i].pos = n->pos; 1227241675Suqs n->args->argv[i].sz = 1; 1228241675Suqs n->args->argv[i].value = mandoc_malloc(sizeof(char *)); 1229241675Suqs n->args->argv[i].value[0] = mandoc_strdup(buf); 1230241675Suqs 1231241675Suqs /* Set our width! */ 1232241675Suqs n->norm->Bl.width = n->args->argv[i].value[0]; 1233241675Suqs} 1234241675Suqs 1235275432Sbaptstatic void 1236274880Sbaptpost_bl_head(POST_ARGS) 1237241675Suqs{ 1238294113Sbapt struct roff_node *nbl, *nh, *nch, *nnext; 1239274880Sbapt struct mdoc_argv *argv; 1240241675Suqs int i, j; 1241241675Suqs 1242294113Sbapt post_bl_norm(mdoc); 1243294113Sbapt 1244279527Sbapt nh = mdoc->last; 1245279527Sbapt if (nh->norm->Bl.type != LIST_column) { 1246279527Sbapt if ((nch = nh->child) == NULL) 1247279527Sbapt return; 1248279527Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 1249279527Sbapt nch->line, nch->pos, "Bl ... %s", nch->string); 1250279527Sbapt while (nch != NULL) { 1251294113Sbapt roff_node_delete(mdoc, nch); 1252279527Sbapt nch = nh->child; 1253279527Sbapt } 1254275432Sbapt return; 1255275432Sbapt } 1256241675Suqs 1257241675Suqs /* 1258274880Sbapt * Append old-style lists, where the column width specifiers 1259241675Suqs * trail as macro parameters, to the new-style ("normal-form") 1260241675Suqs * lists where they're argument values following -column. 1261241675Suqs */ 1262241675Suqs 1263279527Sbapt if (nh->child == NULL) 1264275432Sbapt return; 1265241675Suqs 1266279527Sbapt nbl = nh->parent; 1267279527Sbapt for (j = 0; j < (int)nbl->args->argc; j++) 1268279527Sbapt if (nbl->args->argv[j].arg == MDOC_Column) 1269241675Suqs break; 1270241675Suqs 1271279527Sbapt assert(j < (int)nbl->args->argc); 1272241675Suqs 1273241675Suqs /* 1274241675Suqs * Accommodate for new-style groff column syntax. Shuffle the 1275241675Suqs * child nodes, all of which must be TEXT, as arguments for the 1276241675Suqs * column field. Then, delete the head children. 1277241675Suqs */ 1278241675Suqs 1279279527Sbapt argv = nbl->args->argv + j; 1280274880Sbapt i = argv->sz; 1281294113Sbapt for (nch = nh->child; nch != NULL; nch = nch->next) 1282294113Sbapt argv->sz++; 1283274880Sbapt argv->value = mandoc_reallocarray(argv->value, 1284274880Sbapt argv->sz, sizeof(char *)); 1285241675Suqs 1286279527Sbapt nh->norm->Bl.ncols = argv->sz; 1287279527Sbapt nh->norm->Bl.cols = (void *)argv->value; 1288241675Suqs 1289279527Sbapt for (nch = nh->child; nch != NULL; nch = nnext) { 1290279527Sbapt argv->value[i++] = nch->string; 1291279527Sbapt nch->string = NULL; 1292279527Sbapt nnext = nch->next; 1293294113Sbapt roff_node_delete(NULL, nch); 1294241675Suqs } 1295279527Sbapt nh->child = NULL; 1296241675Suqs} 1297241675Suqs 1298275432Sbaptstatic void 1299241675Suqspost_bl(POST_ARGS) 1300241675Suqs{ 1301294113Sbapt struct roff_node *nparent, *nprev; /* of the Bl block */ 1302294113Sbapt struct roff_node *nblock, *nbody; /* of the Bl */ 1303294113Sbapt struct roff_node *nchild, *nnext; /* of the Bl body */ 1304241675Suqs 1305261344Suqs nbody = mdoc->last; 1306261344Suqs switch (nbody->type) { 1307294113Sbapt case ROFFT_BLOCK: 1308275432Sbapt post_bl_block(mdoc); 1309275432Sbapt return; 1310294113Sbapt case ROFFT_HEAD: 1311275432Sbapt post_bl_head(mdoc); 1312275432Sbapt return; 1313294113Sbapt case ROFFT_BODY: 1314261344Suqs break; 1315261344Suqs default: 1316275432Sbapt return; 1317261344Suqs } 1318294113Sbapt if (nbody->end != ENDBODY_NOT) 1319294113Sbapt return; 1320241675Suqs 1321261344Suqs nchild = nbody->child; 1322279527Sbapt if (nchild == NULL) { 1323279527Sbapt mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, 1324279527Sbapt nbody->line, nbody->pos, "Bl"); 1325279527Sbapt return; 1326279527Sbapt } 1327279527Sbapt while (nchild != NULL) { 1328279527Sbapt if (nchild->tok == MDOC_It || 1329279527Sbapt (nchild->tok == MDOC_Sm && 1330279527Sbapt nchild->next != NULL && 1331279527Sbapt nchild->next->tok == MDOC_It)) { 1332261344Suqs nchild = nchild->next; 1333241675Suqs continue; 1334241675Suqs } 1335241675Suqs 1336274880Sbapt mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse, 1337274880Sbapt nchild->line, nchild->pos, 1338274880Sbapt mdoc_macronames[nchild->tok]); 1339261344Suqs 1340261344Suqs /* 1341261344Suqs * Move the node out of the Bl block. 1342261344Suqs * First, collect all required node pointers. 1343261344Suqs */ 1344261344Suqs 1345261344Suqs nblock = nbody->parent; 1346261344Suqs nprev = nblock->prev; 1347261344Suqs nparent = nblock->parent; 1348261344Suqs nnext = nchild->next; 1349261344Suqs 1350261344Suqs /* 1351261344Suqs * Unlink this child. 1352261344Suqs */ 1353261344Suqs 1354294113Sbapt assert(nchild->prev == NULL); 1355294113Sbapt nbody->child = nnext; 1356294113Sbapt if (nnext == NULL) 1357261344Suqs nbody->last = NULL; 1358294113Sbapt else 1359261344Suqs nnext->prev = NULL; 1360261344Suqs 1361261344Suqs /* 1362261344Suqs * Relink this child. 1363261344Suqs */ 1364261344Suqs 1365261344Suqs nchild->parent = nparent; 1366261344Suqs nchild->prev = nprev; 1367261344Suqs nchild->next = nblock; 1368261344Suqs 1369261344Suqs nblock->prev = nchild; 1370294113Sbapt if (nprev == NULL) 1371261344Suqs nparent->child = nchild; 1372261344Suqs else 1373261344Suqs nprev->next = nchild; 1374261344Suqs 1375261344Suqs nchild = nnext; 1376241675Suqs } 1377241675Suqs} 1378241675Suqs 1379275432Sbaptstatic void 1380274880Sbaptpost_bk(POST_ARGS) 1381274880Sbapt{ 1382294113Sbapt struct roff_node *n; 1383274880Sbapt 1384279527Sbapt n = mdoc->last; 1385279527Sbapt 1386294113Sbapt if (n->type == ROFFT_BLOCK && n->body->child == NULL) { 1387279527Sbapt mandoc_msg(MANDOCERR_BLK_EMPTY, 1388279527Sbapt mdoc->parse, n->line, n->pos, "Bk"); 1389294113Sbapt roff_node_delete(mdoc, n); 1390279527Sbapt } 1391274880Sbapt} 1392274880Sbapt 1393275432Sbaptstatic void 1394294113Sbaptpost_sm(POST_ARGS) 1395241675Suqs{ 1396294113Sbapt struct roff_node *nch; 1397241675Suqs 1398274880Sbapt nch = mdoc->last->child; 1399274880Sbapt 1400275432Sbapt if (nch == NULL) { 1401275432Sbapt mdoc->flags ^= MDOC_SMOFF; 1402275432Sbapt return; 1403241675Suqs } 1404241675Suqs 1405294113Sbapt assert(nch->type == ROFFT_TEXT); 1406241675Suqs 1407275432Sbapt if ( ! strcmp(nch->string, "on")) { 1408275432Sbapt mdoc->flags &= ~MDOC_SMOFF; 1409275432Sbapt return; 1410261344Suqs } 1411275432Sbapt if ( ! strcmp(nch->string, "off")) { 1412275432Sbapt mdoc->flags |= MDOC_SMOFF; 1413275432Sbapt return; 1414261344Suqs } 1415241675Suqs 1416274880Sbapt mandoc_vmsg(MANDOCERR_SM_BAD, 1417274880Sbapt mdoc->parse, nch->line, nch->pos, 1418275432Sbapt "%s %s", mdoc_macronames[mdoc->last->tok], nch->string); 1419275432Sbapt mdoc_node_relink(mdoc, nch); 1420275432Sbapt return; 1421241675Suqs} 1422241675Suqs 1423275432Sbaptstatic void 1424241675Suqspost_root(POST_ARGS) 1425241675Suqs{ 1426294113Sbapt struct roff_node *n; 1427241675Suqs 1428274880Sbapt /* Add missing prologue data. */ 1429241675Suqs 1430274880Sbapt if (mdoc->meta.date == NULL) 1431274880Sbapt mdoc->meta.date = mdoc->quick ? 1432274880Sbapt mandoc_strdup("") : 1433274880Sbapt mandoc_normdate(mdoc->parse, NULL, 0, 0); 1434241675Suqs 1435274880Sbapt if (mdoc->meta.title == NULL) { 1436274880Sbapt mandoc_msg(MANDOCERR_DT_NOTITLE, 1437274880Sbapt mdoc->parse, 0, 0, "EOF"); 1438274880Sbapt mdoc->meta.title = mandoc_strdup("UNTITLED"); 1439241675Suqs } 1440241675Suqs 1441274880Sbapt if (mdoc->meta.vol == NULL) 1442274880Sbapt mdoc->meta.vol = mandoc_strdup("LOCAL"); 1443274880Sbapt 1444274880Sbapt if (mdoc->meta.os == NULL) { 1445274880Sbapt mandoc_msg(MANDOCERR_OS_MISSING, 1446274880Sbapt mdoc->parse, 0, 0, NULL); 1447274880Sbapt mdoc->meta.os = mandoc_strdup(""); 1448274880Sbapt } 1449274880Sbapt 1450241675Suqs /* Check that we begin with a proper `Sh'. */ 1451241675Suqs 1452275432Sbapt n = mdoc->first->child; 1453294113Sbapt while (n != NULL && n->tok != TOKEN_NONE && 1454294113Sbapt mdoc_macros[n->tok].flags & MDOC_PROLOGUE) 1455275432Sbapt n = n->next; 1456275432Sbapt 1457275432Sbapt if (n == NULL) 1458275432Sbapt mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL); 1459275432Sbapt else if (n->tok != MDOC_Sh) 1460274880Sbapt mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse, 1461275432Sbapt n->line, n->pos, mdoc_macronames[n->tok]); 1462241675Suqs} 1463241675Suqs 1464275432Sbaptstatic void 1465241675Suqspost_st(POST_ARGS) 1466241675Suqs{ 1467294113Sbapt struct roff_node *n, *nch; 1468241675Suqs const char *p; 1469241675Suqs 1470274880Sbapt n = mdoc->last; 1471274880Sbapt nch = n->child; 1472274880Sbapt 1473294113Sbapt assert(nch->type == ROFFT_TEXT); 1474241675Suqs 1475294113Sbapt if ((p = mdoc_a2st(nch->string)) == NULL) { 1476274880Sbapt mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse, 1477274880Sbapt nch->line, nch->pos, "St %s", nch->string); 1478294113Sbapt roff_node_delete(mdoc, n); 1479241675Suqs } else { 1480274880Sbapt free(nch->string); 1481274880Sbapt nch->string = mandoc_strdup(p); 1482241675Suqs } 1483241675Suqs} 1484241675Suqs 1485275432Sbaptstatic void 1486241675Suqspost_rs(POST_ARGS) 1487241675Suqs{ 1488294113Sbapt struct roff_node *np, *nch, *next, *prev; 1489241675Suqs int i, j; 1490241675Suqs 1491279527Sbapt np = mdoc->last; 1492279527Sbapt 1493294113Sbapt if (np->type != ROFFT_BODY) 1494275432Sbapt return; 1495279527Sbapt 1496279527Sbapt if (np->child == NULL) { 1497279527Sbapt mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse, 1498279527Sbapt np->line, np->pos, "Rs"); 1499275432Sbapt return; 1500241675Suqs } 1501241675Suqs 1502241675Suqs /* 1503241675Suqs * The full `Rs' block needs special handling to order the 1504241675Suqs * sub-elements according to `rsord'. Pick through each element 1505274880Sbapt * and correctly order it. This is an insertion sort. 1506241675Suqs */ 1507241675Suqs 1508241675Suqs next = NULL; 1509279527Sbapt for (nch = np->child->next; nch != NULL; nch = next) { 1510279527Sbapt /* Determine order number of this child. */ 1511241675Suqs for (i = 0; i < RSORD_MAX; i++) 1512279527Sbapt if (rsord[i] == nch->tok) 1513241675Suqs break; 1514241675Suqs 1515274880Sbapt if (i == RSORD_MAX) { 1516274880Sbapt mandoc_msg(MANDOCERR_RS_BAD, 1517279527Sbapt mdoc->parse, nch->line, nch->pos, 1518279527Sbapt mdoc_macronames[nch->tok]); 1519274880Sbapt i = -1; 1520279527Sbapt } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) 1521279527Sbapt np->norm->Rs.quote_T++; 1522274880Sbapt 1523274880Sbapt /* 1524279527Sbapt * Remove this child from the chain. This somewhat 1525294113Sbapt * repeats roff_node_unlink(), but since we're 1526241675Suqs * just re-ordering, there's no need for the 1527241675Suqs * full unlink process. 1528241675Suqs */ 1529274880Sbapt 1530279527Sbapt if ((next = nch->next) != NULL) 1531279527Sbapt next->prev = nch->prev; 1532241675Suqs 1533279527Sbapt if ((prev = nch->prev) != NULL) 1534279527Sbapt prev->next = nch->next; 1535241675Suqs 1536279527Sbapt nch->prev = nch->next = NULL; 1537241675Suqs 1538274880Sbapt /* 1539241675Suqs * Scan back until we reach a node that's 1540279527Sbapt * to be ordered before this child. 1541241675Suqs */ 1542241675Suqs 1543241675Suqs for ( ; prev ; prev = prev->prev) { 1544241675Suqs /* Determine order of `prev'. */ 1545241675Suqs for (j = 0; j < RSORD_MAX; j++) 1546241675Suqs if (rsord[j] == prev->tok) 1547241675Suqs break; 1548274880Sbapt if (j == RSORD_MAX) 1549274880Sbapt j = -1; 1550241675Suqs 1551241675Suqs if (j <= i) 1552241675Suqs break; 1553241675Suqs } 1554241675Suqs 1555241675Suqs /* 1556279527Sbapt * Set this child back into its correct place 1557279527Sbapt * in front of the `prev' node. 1558241675Suqs */ 1559241675Suqs 1560279527Sbapt nch->prev = prev; 1561241675Suqs 1562279527Sbapt if (prev == NULL) { 1563279527Sbapt np->child->prev = nch; 1564279527Sbapt nch->next = np->child; 1565279527Sbapt np->child = nch; 1566279527Sbapt } else { 1567241675Suqs if (prev->next) 1568279527Sbapt prev->next->prev = nch; 1569279527Sbapt nch->next = prev->next; 1570279527Sbapt prev->next = nch; 1571241675Suqs } 1572241675Suqs } 1573241675Suqs} 1574241675Suqs 1575261344Suqs/* 1576261344Suqs * For some arguments of some macros, 1577261344Suqs * convert all breakable hyphens into ASCII_HYPH. 1578261344Suqs */ 1579275432Sbaptstatic void 1580261344Suqspost_hyph(POST_ARGS) 1581261344Suqs{ 1582294113Sbapt struct roff_node *nch; 1583261344Suqs char *cp; 1584261344Suqs 1585279527Sbapt for (nch = mdoc->last->child; nch != NULL; nch = nch->next) { 1586294113Sbapt if (nch->type != ROFFT_TEXT) 1587261344Suqs continue; 1588261344Suqs cp = nch->string; 1589279527Sbapt if (*cp == '\0') 1590261344Suqs continue; 1591279527Sbapt while (*(++cp) != '\0') 1592279527Sbapt if (*cp == '-' && 1593261344Suqs isalpha((unsigned char)cp[-1]) && 1594261344Suqs isalpha((unsigned char)cp[1])) 1595261344Suqs *cp = ASCII_HYPH; 1596261344Suqs } 1597261344Suqs} 1598261344Suqs 1599275432Sbaptstatic void 1600241675Suqspost_ns(POST_ARGS) 1601241675Suqs{ 1602241675Suqs 1603294113Sbapt if (mdoc->last->flags & MDOC_LINE) 1604274880Sbapt mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse, 1605274880Sbapt mdoc->last->line, mdoc->last->pos, NULL); 1606241675Suqs} 1607241675Suqs 1608275432Sbaptstatic void 1609241675Suqspost_sh(POST_ARGS) 1610241675Suqs{ 1611241675Suqs 1612274880Sbapt post_ignpar(mdoc); 1613274880Sbapt 1614275432Sbapt switch (mdoc->last->type) { 1615294113Sbapt case ROFFT_HEAD: 1616275432Sbapt post_sh_head(mdoc); 1617275432Sbapt break; 1618294113Sbapt case ROFFT_BODY: 1619275432Sbapt switch (mdoc->lastsec) { 1620275432Sbapt case SEC_NAME: 1621275432Sbapt post_sh_name(mdoc); 1622275432Sbapt break; 1623275432Sbapt case SEC_SEE_ALSO: 1624275432Sbapt post_sh_see_also(mdoc); 1625275432Sbapt break; 1626275432Sbapt case SEC_AUTHORS: 1627275432Sbapt post_sh_authors(mdoc); 1628275432Sbapt break; 1629275432Sbapt default: 1630275432Sbapt break; 1631275432Sbapt } 1632275432Sbapt break; 1633275432Sbapt default: 1634275432Sbapt break; 1635275432Sbapt } 1636241675Suqs} 1637241675Suqs 1638275432Sbaptstatic void 1639275432Sbaptpost_sh_name(POST_ARGS) 1640241675Suqs{ 1641294113Sbapt struct roff_node *n; 1642279527Sbapt int hasnm, hasnd; 1643241675Suqs 1644279527Sbapt hasnm = hasnd = 0; 1645241675Suqs 1646279527Sbapt for (n = mdoc->last->child; n != NULL; n = n->next) { 1647279527Sbapt switch (n->tok) { 1648279527Sbapt case MDOC_Nm: 1649279527Sbapt hasnm = 1; 1650279527Sbapt break; 1651279527Sbapt case MDOC_Nd: 1652279527Sbapt hasnd = 1; 1653279527Sbapt if (n->next != NULL) 1654279527Sbapt mandoc_msg(MANDOCERR_NAMESEC_ND, 1655279527Sbapt mdoc->parse, n->line, n->pos, NULL); 1656279527Sbapt break; 1657294113Sbapt case TOKEN_NONE: 1658279527Sbapt if (hasnm) 1659279527Sbapt break; 1660279527Sbapt /* FALLTHROUGH */ 1661279527Sbapt default: 1662279527Sbapt mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, 1663279527Sbapt n->line, n->pos, mdoc_macronames[n->tok]); 1664279527Sbapt break; 1665279527Sbapt } 1666241675Suqs } 1667241675Suqs 1668279527Sbapt if ( ! hasnm) 1669279527Sbapt mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse, 1670279527Sbapt mdoc->last->line, mdoc->last->pos, NULL); 1671279527Sbapt if ( ! hasnd) 1672279527Sbapt mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse, 1673279527Sbapt mdoc->last->line, mdoc->last->pos, NULL); 1674241675Suqs} 1675241675Suqs 1676275432Sbaptstatic void 1677275432Sbaptpost_sh_see_also(POST_ARGS) 1678275432Sbapt{ 1679294113Sbapt const struct roff_node *n; 1680279527Sbapt const char *name, *sec; 1681275432Sbapt const char *lastname, *lastsec, *lastpunct; 1682275432Sbapt int cmp; 1683275432Sbapt 1684275432Sbapt n = mdoc->last->child; 1685275432Sbapt lastname = lastsec = lastpunct = NULL; 1686275432Sbapt while (n != NULL) { 1687294113Sbapt if (n->tok != MDOC_Xr || 1688294113Sbapt n->child == NULL || 1689294113Sbapt n->child->next == NULL) 1690275432Sbapt break; 1691275432Sbapt 1692275432Sbapt /* Process one .Xr node. */ 1693275432Sbapt 1694275432Sbapt name = n->child->string; 1695275432Sbapt sec = n->child->next->string; 1696275432Sbapt if (lastsec != NULL) { 1697275432Sbapt if (lastpunct[0] != ',' || lastpunct[1] != '\0') 1698275432Sbapt mandoc_vmsg(MANDOCERR_XR_PUNCT, 1699275432Sbapt mdoc->parse, n->line, n->pos, 1700275432Sbapt "%s before %s(%s)", lastpunct, 1701275432Sbapt name, sec); 1702275432Sbapt cmp = strcmp(lastsec, sec); 1703275432Sbapt if (cmp > 0) 1704275432Sbapt mandoc_vmsg(MANDOCERR_XR_ORDER, 1705275432Sbapt mdoc->parse, n->line, n->pos, 1706275432Sbapt "%s(%s) after %s(%s)", name, 1707275432Sbapt sec, lastname, lastsec); 1708275432Sbapt else if (cmp == 0 && 1709275432Sbapt strcasecmp(lastname, name) > 0) 1710275432Sbapt mandoc_vmsg(MANDOCERR_XR_ORDER, 1711275432Sbapt mdoc->parse, n->line, n->pos, 1712275432Sbapt "%s after %s", name, lastname); 1713275432Sbapt } 1714275432Sbapt lastname = name; 1715275432Sbapt lastsec = sec; 1716275432Sbapt 1717275432Sbapt /* Process the following node. */ 1718275432Sbapt 1719275432Sbapt n = n->next; 1720275432Sbapt if (n == NULL) 1721275432Sbapt break; 1722275432Sbapt if (n->tok == MDOC_Xr) { 1723275432Sbapt lastpunct = "none"; 1724275432Sbapt continue; 1725275432Sbapt } 1726294113Sbapt if (n->type != ROFFT_TEXT) 1727275432Sbapt break; 1728275432Sbapt for (name = n->string; *name != '\0'; name++) 1729275432Sbapt if (isalpha((const unsigned char)*name)) 1730275432Sbapt return; 1731275432Sbapt lastpunct = n->string; 1732275432Sbapt if (n->next == NULL) 1733275432Sbapt mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse, 1734275432Sbapt n->line, n->pos, "%s after %s(%s)", 1735275432Sbapt lastpunct, lastname, lastsec); 1736275432Sbapt n = n->next; 1737275432Sbapt } 1738275432Sbapt} 1739275432Sbapt 1740241675Suqsstatic int 1741294113Sbaptchild_an(const struct roff_node *n) 1742275432Sbapt{ 1743275432Sbapt 1744275432Sbapt for (n = n->child; n != NULL; n = n->next) 1745294113Sbapt if ((n->tok == MDOC_An && n->child != NULL) || child_an(n)) 1746294113Sbapt return 1; 1747294113Sbapt return 0; 1748275432Sbapt} 1749275432Sbapt 1750275432Sbaptstatic void 1751275432Sbaptpost_sh_authors(POST_ARGS) 1752275432Sbapt{ 1753275432Sbapt 1754275432Sbapt if ( ! child_an(mdoc->last)) 1755275432Sbapt mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse, 1756275432Sbapt mdoc->last->line, mdoc->last->pos, NULL); 1757275432Sbapt} 1758275432Sbapt 1759275432Sbaptstatic void 1760241675Suqspost_sh_head(POST_ARGS) 1761241675Suqs{ 1762274880Sbapt const char *goodsec; 1763294113Sbapt enum roff_sec sec; 1764241675Suqs 1765241675Suqs /* 1766241675Suqs * Process a new section. Sections are either "named" or 1767241675Suqs * "custom". Custom sections are user-defined, while named ones 1768241675Suqs * follow a conventional order and may only appear in certain 1769241675Suqs * manual sections. 1770241675Suqs */ 1771241675Suqs 1772294113Sbapt sec = mdoc->last->sec; 1773241675Suqs 1774241675Suqs /* The NAME should be first. */ 1775241675Suqs 1776241675Suqs if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed) 1777274880Sbapt mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse, 1778274880Sbapt mdoc->last->line, mdoc->last->pos, 1779294113Sbapt "Sh %s", secnames[sec]); 1780241675Suqs 1781241675Suqs /* The SYNOPSIS gets special attention in other areas. */ 1782241675Suqs 1783294113Sbapt if (sec == SEC_SYNOPSIS) { 1784261344Suqs roff_setreg(mdoc->roff, "nS", 1, '='); 1785241675Suqs mdoc->flags |= MDOC_SYNOPSIS; 1786261344Suqs } else { 1787261344Suqs roff_setreg(mdoc->roff, "nS", 0, '='); 1788241675Suqs mdoc->flags &= ~MDOC_SYNOPSIS; 1789261344Suqs } 1790241675Suqs 1791241675Suqs /* Mark our last section. */ 1792241675Suqs 1793241675Suqs mdoc->lastsec = sec; 1794241675Suqs 1795241675Suqs /* We don't care about custom sections after this. */ 1796241675Suqs 1797294113Sbapt if (sec == SEC_CUSTOM) 1798275432Sbapt return; 1799241675Suqs 1800241675Suqs /* 1801241675Suqs * Check whether our non-custom section is being repeated or is 1802241675Suqs * out of order. 1803241675Suqs */ 1804241675Suqs 1805241675Suqs if (sec == mdoc->lastnamed) 1806274880Sbapt mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse, 1807274880Sbapt mdoc->last->line, mdoc->last->pos, 1808294113Sbapt "Sh %s", secnames[sec]); 1809241675Suqs 1810241675Suqs if (sec < mdoc->lastnamed) 1811274880Sbapt mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse, 1812274880Sbapt mdoc->last->line, mdoc->last->pos, 1813294113Sbapt "Sh %s", secnames[sec]); 1814241675Suqs 1815241675Suqs /* Mark the last named section. */ 1816241675Suqs 1817241675Suqs mdoc->lastnamed = sec; 1818241675Suqs 1819241675Suqs /* Check particular section/manual conventions. */ 1820241675Suqs 1821294113Sbapt if (mdoc->meta.msec == NULL) 1822275432Sbapt return; 1823241675Suqs 1824274880Sbapt goodsec = NULL; 1825241675Suqs switch (sec) { 1826274880Sbapt case SEC_ERRORS: 1827274880Sbapt if (*mdoc->meta.msec == '4') 1828274880Sbapt break; 1829274880Sbapt goodsec = "2, 3, 4, 9"; 1830241675Suqs /* FALLTHROUGH */ 1831274880Sbapt case SEC_RETURN_VALUES: 1832274880Sbapt case SEC_LIBRARY: 1833241675Suqs if (*mdoc->meta.msec == '2') 1834241675Suqs break; 1835241675Suqs if (*mdoc->meta.msec == '3') 1836241675Suqs break; 1837274880Sbapt if (NULL == goodsec) 1838274880Sbapt goodsec = "2, 3, 9"; 1839274880Sbapt /* FALLTHROUGH */ 1840274880Sbapt case SEC_CONTEXT: 1841241675Suqs if (*mdoc->meta.msec == '9') 1842241675Suqs break; 1843274880Sbapt if (NULL == goodsec) 1844274880Sbapt goodsec = "9"; 1845274880Sbapt mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse, 1846274880Sbapt mdoc->last->line, mdoc->last->pos, 1847294113Sbapt "Sh %s for %s only", secnames[sec], goodsec); 1848241675Suqs break; 1849241675Suqs default: 1850241675Suqs break; 1851241675Suqs } 1852241675Suqs} 1853241675Suqs 1854275432Sbaptstatic void 1855241675Suqspost_ignpar(POST_ARGS) 1856241675Suqs{ 1857294113Sbapt struct roff_node *np; 1858241675Suqs 1859279527Sbapt switch (mdoc->last->type) { 1860294113Sbapt case ROFFT_HEAD: 1861279527Sbapt post_hyph(mdoc); 1862275432Sbapt return; 1863294113Sbapt case ROFFT_BODY: 1864279527Sbapt break; 1865279527Sbapt default: 1866279527Sbapt return; 1867279527Sbapt } 1868241675Suqs 1869294113Sbapt if ((np = mdoc->last->child) != NULL) 1870294113Sbapt if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { 1871274880Sbapt mandoc_vmsg(MANDOCERR_PAR_SKIP, 1872274880Sbapt mdoc->parse, np->line, np->pos, 1873274880Sbapt "%s after %s", mdoc_macronames[np->tok], 1874274880Sbapt mdoc_macronames[mdoc->last->tok]); 1875294113Sbapt roff_node_delete(mdoc, np); 1876241675Suqs } 1877241675Suqs 1878294113Sbapt if ((np = mdoc->last->last) != NULL) 1879294113Sbapt if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { 1880274880Sbapt mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 1881274880Sbapt np->line, np->pos, "%s at the end of %s", 1882274880Sbapt mdoc_macronames[np->tok], 1883274880Sbapt mdoc_macronames[mdoc->last->tok]); 1884294113Sbapt roff_node_delete(mdoc, np); 1885241675Suqs } 1886241675Suqs} 1887241675Suqs 1888275432Sbaptstatic void 1889294113Sbaptpost_prevpar(POST_ARGS) 1890241675Suqs{ 1891294113Sbapt struct roff_node *n; 1892241675Suqs 1893294113Sbapt n = mdoc->last; 1894294113Sbapt if (NULL == n->prev) 1895275432Sbapt return; 1896294113Sbapt if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) 1897275432Sbapt return; 1898241675Suqs 1899274880Sbapt /* 1900241675Suqs * Don't allow prior `Lp' or `Pp' prior to a paragraph-type 1901241675Suqs * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. 1902241675Suqs */ 1903241675Suqs 1904294113Sbapt if (n->prev->tok != MDOC_Pp && 1905294113Sbapt n->prev->tok != MDOC_Lp && 1906294113Sbapt n->prev->tok != MDOC_br) 1907275432Sbapt return; 1908294113Sbapt if (n->tok == MDOC_Bl && n->norm->Bl.comp) 1909275432Sbapt return; 1910294113Sbapt if (n->tok == MDOC_Bd && n->norm->Bd.comp) 1911275432Sbapt return; 1912294113Sbapt if (n->tok == MDOC_It && n->parent->norm->Bl.comp) 1913275432Sbapt return; 1914241675Suqs 1915274880Sbapt mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 1916294113Sbapt n->prev->line, n->prev->pos, 1917294113Sbapt "%s before %s", mdoc_macronames[n->prev->tok], 1918274880Sbapt mdoc_macronames[n->tok]); 1919294113Sbapt roff_node_delete(mdoc, n->prev); 1920241675Suqs} 1921241675Suqs 1922275432Sbaptstatic void 1923261344Suqspost_par(POST_ARGS) 1924261344Suqs{ 1925294113Sbapt struct roff_node *np; 1926261344Suqs 1927279527Sbapt np = mdoc->last; 1928294113Sbapt if (np->tok != MDOC_br && np->tok != MDOC_sp) 1929294113Sbapt post_prevpar(mdoc); 1930274880Sbapt 1931279527Sbapt if (np->tok == MDOC_sp) { 1932294113Sbapt if (np->child != NULL && np->child->next != NULL) 1933279527Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 1934279527Sbapt np->child->next->line, np->child->next->pos, 1935279527Sbapt "sp ... %s", np->child->next->string); 1936279527Sbapt } else if (np->child != NULL) 1937279527Sbapt mandoc_vmsg(MANDOCERR_ARG_SKIP, 1938279527Sbapt mdoc->parse, np->line, np->pos, "%s %s", 1939279527Sbapt mdoc_macronames[np->tok], np->child->string); 1940261344Suqs 1941294113Sbapt if ((np = mdoc->last->prev) == NULL) { 1942274880Sbapt np = mdoc->last->parent; 1943294113Sbapt if (np->tok != MDOC_Sh && np->tok != MDOC_Ss) 1944275432Sbapt return; 1945294113Sbapt } else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp && 1946294113Sbapt (mdoc->last->tok != MDOC_br || 1947294113Sbapt (np->tok != MDOC_sp && np->tok != MDOC_br))) 1948275432Sbapt return; 1949261344Suqs 1950274880Sbapt mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 1951274880Sbapt mdoc->last->line, mdoc->last->pos, 1952274880Sbapt "%s after %s", mdoc_macronames[mdoc->last->tok], 1953274880Sbapt mdoc_macronames[np->tok]); 1954294113Sbapt roff_node_delete(mdoc, mdoc->last); 1955261344Suqs} 1956261344Suqs 1957275432Sbaptstatic void 1958241675Suqspost_dd(POST_ARGS) 1959241675Suqs{ 1960294113Sbapt struct roff_node *n; 1961274880Sbapt char *datestr; 1962241675Suqs 1963294113Sbapt n = mdoc->last; 1964294113Sbapt if (mdoc->meta.date != NULL) { 1965294113Sbapt mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 1966294113Sbapt n->line, n->pos, "Dd"); 1967241675Suqs free(mdoc->meta.date); 1968294113Sbapt } else if (mdoc->flags & MDOC_PBODY) 1969294113Sbapt mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, 1970294113Sbapt n->line, n->pos, "Dd"); 1971294113Sbapt else if (mdoc->meta.title != NULL) 1972294113Sbapt mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 1973294113Sbapt n->line, n->pos, "Dd after Dt"); 1974294113Sbapt else if (mdoc->meta.os != NULL) 1975294113Sbapt mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 1976294113Sbapt n->line, n->pos, "Dd after Os"); 1977241675Suqs 1978294113Sbapt if (n->child == NULL || n->child->string[0] == '\0') { 1979274880Sbapt mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : 1980274880Sbapt mandoc_normdate(mdoc->parse, NULL, n->line, n->pos); 1981274880Sbapt goto out; 1982241675Suqs } 1983241675Suqs 1984274880Sbapt datestr = NULL; 1985294113Sbapt deroff(&datestr, n); 1986274880Sbapt if (mdoc->quick) 1987274880Sbapt mdoc->meta.date = datestr; 1988274880Sbapt else { 1989274880Sbapt mdoc->meta.date = mandoc_normdate(mdoc->parse, 1990274880Sbapt datestr, n->line, n->pos); 1991274880Sbapt free(datestr); 1992241675Suqs } 1993274880Sbaptout: 1994294113Sbapt roff_node_delete(mdoc, n); 1995241675Suqs} 1996241675Suqs 1997275432Sbaptstatic void 1998241675Suqspost_dt(POST_ARGS) 1999241675Suqs{ 2000294113Sbapt struct roff_node *nn, *n; 2001241675Suqs const char *cp; 2002241675Suqs char *p; 2003241675Suqs 2004241675Suqs n = mdoc->last; 2005294113Sbapt if (mdoc->flags & MDOC_PBODY) { 2006294113Sbapt mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse, 2007294113Sbapt n->line, n->pos, "Dt"); 2008294113Sbapt goto out; 2009294113Sbapt } 2010241675Suqs 2011294113Sbapt if (mdoc->meta.title != NULL) 2012294113Sbapt mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2013294113Sbapt n->line, n->pos, "Dt"); 2014294113Sbapt else if (mdoc->meta.os != NULL) 2015294113Sbapt mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2016294113Sbapt n->line, n->pos, "Dt after Os"); 2017294113Sbapt 2018274880Sbapt free(mdoc->meta.title); 2019274880Sbapt free(mdoc->meta.msec); 2020274880Sbapt free(mdoc->meta.vol); 2021274880Sbapt free(mdoc->meta.arch); 2022241675Suqs 2023274880Sbapt mdoc->meta.title = NULL; 2024274880Sbapt mdoc->meta.msec = NULL; 2025274880Sbapt mdoc->meta.vol = NULL; 2026274880Sbapt mdoc->meta.arch = NULL; 2027241675Suqs 2028279527Sbapt /* Mandatory first argument: title. */ 2029241675Suqs 2030279527Sbapt nn = n->child; 2031279527Sbapt if (nn == NULL || *nn->string == '\0') { 2032274880Sbapt mandoc_msg(MANDOCERR_DT_NOTITLE, 2033274880Sbapt mdoc->parse, n->line, n->pos, "Dt"); 2034274880Sbapt mdoc->meta.title = mandoc_strdup("UNTITLED"); 2035279527Sbapt } else { 2036279527Sbapt mdoc->meta.title = mandoc_strdup(nn->string); 2037279527Sbapt 2038279527Sbapt /* Check that all characters are uppercase. */ 2039279527Sbapt 2040279527Sbapt for (p = nn->string; *p != '\0'; p++) 2041279527Sbapt if (islower((unsigned char)*p)) { 2042279527Sbapt mandoc_vmsg(MANDOCERR_TITLE_CASE, 2043279527Sbapt mdoc->parse, nn->line, 2044279527Sbapt nn->pos + (p - nn->string), 2045279527Sbapt "Dt %s", nn->string); 2046279527Sbapt break; 2047279527Sbapt } 2048241675Suqs } 2049241675Suqs 2050279527Sbapt /* Mandatory second argument: section.�*/ 2051241675Suqs 2052279527Sbapt if (nn != NULL) 2053279527Sbapt nn = nn->next; 2054241675Suqs 2055279527Sbapt if (nn == NULL) { 2056274880Sbapt mandoc_vmsg(MANDOCERR_MSEC_MISSING, 2057274880Sbapt mdoc->parse, n->line, n->pos, 2058274880Sbapt "Dt %s", mdoc->meta.title); 2059241675Suqs mdoc->meta.vol = mandoc_strdup("LOCAL"); 2060279527Sbapt goto out; /* msec and arch remain NULL. */ 2061241675Suqs } 2062241675Suqs 2063279527Sbapt mdoc->meta.msec = mandoc_strdup(nn->string); 2064241675Suqs 2065279527Sbapt /* Infer volume title from section number. */ 2066279527Sbapt 2067241675Suqs cp = mandoc_a2msec(nn->string); 2068279527Sbapt if (cp == NULL) { 2069274880Sbapt mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse, 2070274880Sbapt nn->line, nn->pos, "Dt ... %s", nn->string); 2071241675Suqs mdoc->meta.vol = mandoc_strdup(nn->string); 2072279527Sbapt } else 2073279527Sbapt mdoc->meta.vol = mandoc_strdup(cp); 2074241675Suqs 2075279527Sbapt /* Optional third argument: architecture. */ 2076241675Suqs 2077279527Sbapt if ((nn = nn->next) == NULL) 2078279527Sbapt goto out; 2079241675Suqs 2080279527Sbapt for (p = nn->string; *p != '\0'; p++) 2081279527Sbapt *p = tolower((unsigned char)*p); 2082279527Sbapt mdoc->meta.arch = mandoc_strdup(nn->string); 2083279527Sbapt 2084279527Sbapt /* Ignore fourth and later arguments. */ 2085279527Sbapt 2086279527Sbapt if ((nn = nn->next) != NULL) 2087279527Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 2088279527Sbapt nn->line, nn->pos, "Dt ... %s", nn->string); 2089279527Sbapt 2090274880Sbaptout: 2091294113Sbapt roff_node_delete(mdoc, n); 2092241675Suqs} 2093241675Suqs 2094275432Sbaptstatic void 2095241675Suqspost_bx(POST_ARGS) 2096241675Suqs{ 2097294113Sbapt struct roff_node *n; 2098241675Suqs 2099274880Sbapt /* 2100241675Suqs * Make `Bx's second argument always start with an uppercase 2101241675Suqs * letter. Groff checks if it's an "accepted" term, but we just 2102241675Suqs * uppercase blindly. 2103241675Suqs */ 2104241675Suqs 2105294113Sbapt if ((n = mdoc->last->child) != NULL && (n = n->next) != NULL) 2106274880Sbapt *n->string = (char)toupper((unsigned char)*n->string); 2107241675Suqs} 2108241675Suqs 2109275432Sbaptstatic void 2110241675Suqspost_os(POST_ARGS) 2111241675Suqs{ 2112241675Suqs#ifndef OSNAME 2113241675Suqs struct utsname utsname; 2114274880Sbapt static char *defbuf; 2115241675Suqs#endif 2116294113Sbapt struct roff_node *n; 2117241675Suqs 2118241675Suqs n = mdoc->last; 2119294113Sbapt if (mdoc->meta.os != NULL) 2120294113Sbapt mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2121294113Sbapt n->line, n->pos, "Os"); 2122294113Sbapt else if (mdoc->flags & MDOC_PBODY) 2123294113Sbapt mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, 2124294113Sbapt n->line, n->pos, "Os"); 2125241675Suqs 2126241675Suqs /* 2127261344Suqs * Set the operating system by way of the `Os' macro. 2128261344Suqs * The order of precedence is: 2129261344Suqs * 1. the argument of the `Os' macro, unless empty 2130261344Suqs * 2. the -Ios=foo command line argument, if provided 2131261344Suqs * 3. -DOSNAME="\"foo\"", if provided during compilation 2132261344Suqs * 4. "sysname release" from uname(3) 2133274880Sbapt */ 2134241675Suqs 2135261344Suqs free(mdoc->meta.os); 2136274880Sbapt mdoc->meta.os = NULL; 2137294113Sbapt deroff(&mdoc->meta.os, n); 2138274880Sbapt if (mdoc->meta.os) 2139274880Sbapt goto out; 2140241675Suqs 2141274880Sbapt if (mdoc->defos) { 2142274880Sbapt mdoc->meta.os = mandoc_strdup(mdoc->defos); 2143274880Sbapt goto out; 2144241675Suqs } 2145241675Suqs 2146241675Suqs#ifdef OSNAME 2147274880Sbapt mdoc->meta.os = mandoc_strdup(OSNAME); 2148241675Suqs#else /*!OSNAME */ 2149294113Sbapt if (defbuf == NULL) { 2150294113Sbapt if (uname(&utsname) == -1) { 2151274880Sbapt mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse, 2152274880Sbapt n->line, n->pos, "Os"); 2153274880Sbapt defbuf = mandoc_strdup("UNKNOWN"); 2154274880Sbapt } else 2155274880Sbapt mandoc_asprintf(&defbuf, "%s %s", 2156274880Sbapt utsname.sysname, utsname.release); 2157274880Sbapt } 2158274880Sbapt mdoc->meta.os = mandoc_strdup(defbuf); 2159241675Suqs#endif /*!OSNAME*/ 2160241675Suqs 2161274880Sbaptout: 2162294113Sbapt roff_node_delete(mdoc, n); 2163241675Suqs} 2164241675Suqs 2165274880Sbapt/* 2166274880Sbapt * If no argument is provided, 2167274880Sbapt * fill in the name of the current manual page. 2168274880Sbapt */ 2169275432Sbaptstatic void 2170274880Sbaptpost_ex(POST_ARGS) 2171241675Suqs{ 2172294113Sbapt struct roff_node *n; 2173241675Suqs 2174294113Sbapt post_std(mdoc); 2175294113Sbapt 2176241675Suqs n = mdoc->last; 2177294113Sbapt if (n->child != NULL) 2178275432Sbapt return; 2179241675Suqs 2180274880Sbapt if (mdoc->meta.name == NULL) { 2181274880Sbapt mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse, 2182274880Sbapt n->line, n->pos, "Ex"); 2183275432Sbapt return; 2184274880Sbapt } 2185274880Sbapt 2186294113Sbapt mdoc->next = ROFF_NEXT_CHILD; 2187294113Sbapt roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 2188274880Sbapt mdoc->last = n; 2189241675Suqs} 2190241675Suqs 2191294113Sbaptenum roff_sec 2192294113Sbaptmdoc_a2sec(const char *p) 2193241675Suqs{ 2194241675Suqs int i; 2195241675Suqs 2196274880Sbapt for (i = 0; i < (int)SEC__MAX; i++) 2197241675Suqs if (secnames[i] && 0 == strcmp(p, secnames[i])) 2198294113Sbapt return (enum roff_sec)i; 2199241675Suqs 2200294113Sbapt return SEC_CUSTOM; 2201241675Suqs} 2202241675Suqs 2203241675Suqsstatic size_t 2204294113Sbaptmacro2len(int macro) 2205241675Suqs{ 2206241675Suqs 2207241675Suqs switch (macro) { 2208274880Sbapt case MDOC_Ad: 2209294113Sbapt return 12; 2210274880Sbapt case MDOC_Ao: 2211294113Sbapt return 12; 2212274880Sbapt case MDOC_An: 2213294113Sbapt return 12; 2214274880Sbapt case MDOC_Aq: 2215294113Sbapt return 12; 2216274880Sbapt case MDOC_Ar: 2217294113Sbapt return 12; 2218274880Sbapt case MDOC_Bo: 2219294113Sbapt return 12; 2220274880Sbapt case MDOC_Bq: 2221294113Sbapt return 12; 2222274880Sbapt case MDOC_Cd: 2223294113Sbapt return 12; 2224274880Sbapt case MDOC_Cm: 2225294113Sbapt return 10; 2226274880Sbapt case MDOC_Do: 2227294113Sbapt return 10; 2228274880Sbapt case MDOC_Dq: 2229294113Sbapt return 12; 2230274880Sbapt case MDOC_Dv: 2231294113Sbapt return 12; 2232274880Sbapt case MDOC_Eo: 2233294113Sbapt return 12; 2234274880Sbapt case MDOC_Em: 2235294113Sbapt return 10; 2236274880Sbapt case MDOC_Er: 2237294113Sbapt return 17; 2238274880Sbapt case MDOC_Ev: 2239294113Sbapt return 15; 2240274880Sbapt case MDOC_Fa: 2241294113Sbapt return 12; 2242274880Sbapt case MDOC_Fl: 2243294113Sbapt return 10; 2244274880Sbapt case MDOC_Fo: 2245294113Sbapt return 16; 2246274880Sbapt case MDOC_Fn: 2247294113Sbapt return 16; 2248274880Sbapt case MDOC_Ic: 2249294113Sbapt return 10; 2250274880Sbapt case MDOC_Li: 2251294113Sbapt return 16; 2252274880Sbapt case MDOC_Ms: 2253294113Sbapt return 6; 2254274880Sbapt case MDOC_Nm: 2255294113Sbapt return 10; 2256274880Sbapt case MDOC_No: 2257294113Sbapt return 12; 2258274880Sbapt case MDOC_Oo: 2259294113Sbapt return 10; 2260274880Sbapt case MDOC_Op: 2261294113Sbapt return 14; 2262274880Sbapt case MDOC_Pa: 2263294113Sbapt return 32; 2264274880Sbapt case MDOC_Pf: 2265294113Sbapt return 12; 2266274880Sbapt case MDOC_Po: 2267294113Sbapt return 12; 2268274880Sbapt case MDOC_Pq: 2269294113Sbapt return 12; 2270274880Sbapt case MDOC_Ql: 2271294113Sbapt return 16; 2272274880Sbapt case MDOC_Qo: 2273294113Sbapt return 12; 2274274880Sbapt case MDOC_So: 2275294113Sbapt return 12; 2276274880Sbapt case MDOC_Sq: 2277294113Sbapt return 12; 2278274880Sbapt case MDOC_Sy: 2279294113Sbapt return 6; 2280274880Sbapt case MDOC_Sx: 2281294113Sbapt return 16; 2282274880Sbapt case MDOC_Tn: 2283294113Sbapt return 10; 2284274880Sbapt case MDOC_Va: 2285294113Sbapt return 12; 2286274880Sbapt case MDOC_Vt: 2287294113Sbapt return 12; 2288274880Sbapt case MDOC_Xr: 2289294113Sbapt return 10; 2290241675Suqs default: 2291241675Suqs break; 2292241675Suqs }; 2293294113Sbapt return 0; 2294241675Suqs} 2295