1*55818fdcSschwarze /* $Id: mdoc_validate.c,v 1.108 2012/11/16 22:20:40 schwarze Exp $ */ 2f73abda9Skristaps /* 322972b14Sschwarze * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4353fa9ecSschwarze * Copyright (c) 2010, 2011, 2012 Ingo Schwarze <schwarze@openbsd.org> 5f73abda9Skristaps * 6f73abda9Skristaps * Permission to use, copy, modify, and distribute this software for any 7a6464425Sschwarze * purpose with or without fee is hereby granted, provided that the above 8a6464425Sschwarze * copyright notice and this permission notice appear in all copies. 9f73abda9Skristaps * 10a6464425Sschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11a6464425Sschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12a6464425Sschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13a6464425Sschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14a6464425Sschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15a6464425Sschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16a6464425Sschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17f73abda9Skristaps */ 1820fa2881Sschwarze #ifndef OSNAME 1920fa2881Sschwarze #include <sys/utsname.h> 2020fa2881Sschwarze #endif 2120fa2881Sschwarze 22f73abda9Skristaps #include <sys/types.h> 23f73abda9Skristaps 24f73abda9Skristaps #include <assert.h> 25f73abda9Skristaps #include <ctype.h> 26d92dc4efSschwarze #include <limits.h> 273216dddfSschwarze #include <stdio.h> 28f73abda9Skristaps #include <stdlib.h> 29f73abda9Skristaps #include <string.h> 3020fa2881Sschwarze #include <time.h> 31f73abda9Skristaps 32a35fc07aSschwarze #include "mdoc.h" 336e03d529Sschwarze #include "mandoc.h" 34f73abda9Skristaps #include "libmdoc.h" 35f6854d5cSschwarze #include "libmandoc.h" 36f73abda9Skristaps 37f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */ 38f73abda9Skristaps 396093755cSschwarze #define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n 40f73abda9Skristaps #define POST_ARGS struct mdoc *mdoc 41f73abda9Skristaps 4220fa2881Sschwarze #define NUMSIZ 32 4320fa2881Sschwarze #define DATESIZE 32 4420fa2881Sschwarze 457c2be9f8Sschwarze enum check_ineq { 467c2be9f8Sschwarze CHECK_LT, 477c2be9f8Sschwarze CHECK_GT, 487c2be9f8Sschwarze CHECK_EQ 497c2be9f8Sschwarze }; 507c2be9f8Sschwarze 517c2be9f8Sschwarze enum check_lvl { 527c2be9f8Sschwarze CHECK_WARN, 537c2be9f8Sschwarze CHECK_ERROR, 547c2be9f8Sschwarze }; 557c2be9f8Sschwarze 56f73abda9Skristaps typedef int (*v_pre)(PRE_ARGS); 57f73abda9Skristaps typedef int (*v_post)(POST_ARGS); 58f73abda9Skristaps 59f73abda9Skristaps struct valids { 60f73abda9Skristaps v_pre *pre; 61f73abda9Skristaps v_post *post; 62f73abda9Skristaps }; 63f73abda9Skristaps 647c2be9f8Sschwarze static int check_count(struct mdoc *, enum mdoc_type, 657c2be9f8Sschwarze enum check_lvl, enum check_ineq, int); 66dd94fa3aSschwarze static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type); 6720fa2881Sschwarze static void check_text(struct mdoc *, int, int, char *); 6820fa2881Sschwarze static void check_argv(struct mdoc *, 6931e23753Sschwarze struct mdoc_node *, struct mdoc_argv *); 7020fa2881Sschwarze static void check_args(struct mdoc *, struct mdoc_node *); 7104e980cbSschwarze static int concat(char *, const struct mdoc_node *, size_t); 7219a69263Sschwarze static enum mdoc_sec a2sec(const char *); 7319a69263Sschwarze static size_t macro2len(enum mdoct); 7467c719adSschwarze 757c2be9f8Sschwarze static int ebool(POST_ARGS); 7667c719adSschwarze static int berr_ge1(POST_ARGS); 7767c719adSschwarze static int bwarn_ge1(POST_ARGS); 78da272f5eSschwarze static int ewarn_eq0(POST_ARGS); 79bb648afaSschwarze static int ewarn_eq1(POST_ARGS); 8067c719adSschwarze static int ewarn_ge1(POST_ARGS); 81bb648afaSschwarze static int ewarn_le1(POST_ARGS); 82b16e7ddfSschwarze static int hwarn_eq0(POST_ARGS); 837c2be9f8Sschwarze static int hwarn_eq1(POST_ARGS); 84bb648afaSschwarze static int hwarn_ge1(POST_ARGS); 8567c719adSschwarze static int hwarn_le1(POST_ARGS); 8667c719adSschwarze 8767c719adSschwarze static int post_an(POST_ARGS); 8867c719adSschwarze static int post_at(POST_ARGS); 8967c719adSschwarze static int post_bf(POST_ARGS); 9067c719adSschwarze static int post_bl(POST_ARGS); 9120fa2881Sschwarze static int post_bl_block(POST_ARGS); 9220fa2881Sschwarze static int post_bl_block_width(POST_ARGS); 9320fa2881Sschwarze static int post_bl_block_tag(POST_ARGS); 9467c719adSschwarze static int post_bl_head(POST_ARGS); 95992063deSschwarze static int post_bx(POST_ARGS); 9620fa2881Sschwarze static int post_dd(POST_ARGS); 976093755cSschwarze static int post_dt(POST_ARGS); 9820fa2881Sschwarze static int post_defaults(POST_ARGS); 9920fa2881Sschwarze static int post_literal(POST_ARGS); 10020fa2881Sschwarze static int post_eoln(POST_ARGS); 10167c719adSschwarze static int post_it(POST_ARGS); 10267c719adSschwarze static int post_lb(POST_ARGS); 10367c719adSschwarze static int post_nm(POST_ARGS); 104af216717Sschwarze static int post_ns(POST_ARGS); 10520fa2881Sschwarze static int post_os(POST_ARGS); 106e0dd4c9cSschwarze static int post_par(POST_ARGS); 107f6127a73Sschwarze static int post_ignpar(POST_ARGS); 10820fa2881Sschwarze static int post_prol(POST_ARGS); 10967c719adSschwarze static int post_root(POST_ARGS); 110011fe33bSschwarze static int post_rs(POST_ARGS); 11167c719adSschwarze static int post_sh(POST_ARGS); 11267c719adSschwarze static int post_sh_body(POST_ARGS); 11367c719adSschwarze static int post_sh_head(POST_ARGS); 11467c719adSschwarze static int post_st(POST_ARGS); 11520fa2881Sschwarze static int post_std(POST_ARGS); 1168521b0bcSschwarze static int post_vt(POST_ARGS); 117f73abda9Skristaps static int pre_an(PRE_ARGS); 118f73abda9Skristaps static int pre_bd(PRE_ARGS); 119f73abda9Skristaps static int pre_bl(PRE_ARGS); 120f73abda9Skristaps static int pre_dd(PRE_ARGS); 121f73abda9Skristaps static int pre_display(PRE_ARGS); 122f73abda9Skristaps static int pre_dt(PRE_ARGS); 123f73abda9Skristaps static int pre_it(PRE_ARGS); 12420fa2881Sschwarze static int pre_literal(PRE_ARGS); 125f73abda9Skristaps static int pre_os(PRE_ARGS); 12620fa2881Sschwarze static int pre_par(PRE_ARGS); 127f73abda9Skristaps static int pre_sh(PRE_ARGS); 128f73abda9Skristaps static int pre_ss(PRE_ARGS); 12920fa2881Sschwarze static int pre_std(PRE_ARGS); 130f73abda9Skristaps 13167c719adSschwarze static v_post posts_an[] = { post_an, NULL }; 13220fa2881Sschwarze static v_post posts_at[] = { post_at, post_defaults, NULL }; 13320fa2881Sschwarze static v_post posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL }; 13467c719adSschwarze static v_post posts_bf[] = { hwarn_le1, post_bf, NULL }; 13520fa2881Sschwarze static v_post posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL }; 13667c719adSschwarze static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL }; 137992063deSschwarze static v_post posts_bx[] = { post_bx, NULL }; 138bb648afaSschwarze static v_post posts_bool[] = { ebool, NULL }; 139b31af00dSschwarze static v_post posts_eoln[] = { post_eoln, NULL }; 14020fa2881Sschwarze static v_post posts_defaults[] = { post_defaults, NULL }; 141b058e777Sschwarze static v_post posts_dd[] = { post_dd, post_prol, NULL }; 142bb648afaSschwarze static v_post posts_dl[] = { post_literal, bwarn_ge1, NULL }; 14320fa2881Sschwarze static v_post posts_dt[] = { post_dt, post_prol, NULL }; 14467c719adSschwarze static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL }; 14567c719adSschwarze static v_post posts_it[] = { post_it, NULL }; 146bb648afaSschwarze static v_post posts_lb[] = { post_lb, NULL }; 14767c719adSschwarze static v_post posts_nd[] = { berr_ge1, NULL }; 14867c719adSschwarze static v_post posts_nm[] = { post_nm, NULL }; 149da272f5eSschwarze static v_post posts_notext[] = { ewarn_eq0, NULL }; 150af216717Sschwarze static v_post posts_ns[] = { post_ns, NULL }; 15120fa2881Sschwarze static v_post posts_os[] = { post_os, post_prol, NULL }; 152e0dd4c9cSschwarze static v_post posts_pp[] = { post_par, ewarn_eq0, NULL }; 153bb648afaSschwarze static v_post posts_rs[] = { post_rs, NULL }; 154a35fc07aSschwarze static v_post posts_sh[] = { post_ignpar, hwarn_ge1, post_sh, NULL }; 155e0dd4c9cSschwarze static v_post posts_sp[] = { post_par, ewarn_le1, NULL }; 156a35fc07aSschwarze static v_post posts_ss[] = { post_ignpar, hwarn_ge1, NULL }; 157bb648afaSschwarze static v_post posts_st[] = { post_st, NULL }; 15820fa2881Sschwarze static v_post posts_std[] = { post_std, NULL }; 159e7a93ef3Sschwarze static v_post posts_text[] = { ewarn_ge1, NULL }; 160bb648afaSschwarze static v_post posts_text1[] = { ewarn_eq1, NULL }; 1618521b0bcSschwarze static v_post posts_vt[] = { post_vt, NULL }; 162bb648afaSschwarze static v_post posts_wline[] = { bwarn_ge1, NULL }; 163f73abda9Skristaps static v_pre pres_an[] = { pre_an, NULL }; 16420fa2881Sschwarze static v_pre pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL }; 16520fa2881Sschwarze static v_pre pres_bl[] = { pre_bl, pre_par, NULL }; 166f73abda9Skristaps static v_pre pres_d1[] = { pre_display, NULL }; 16720fa2881Sschwarze static v_pre pres_dl[] = { pre_literal, pre_display, NULL }; 16867c719adSschwarze static v_pre pres_dd[] = { pre_dd, NULL }; 1696be99f77Sschwarze static v_pre pres_dt[] = { pre_dt, NULL }; 170b16e7ddfSschwarze static v_pre pres_er[] = { NULL, NULL }; 171974b5ecdSschwarze static v_pre pres_fd[] = { NULL, NULL }; 172f6127a73Sschwarze static v_pre pres_it[] = { pre_it, pre_par, NULL }; 1736be99f77Sschwarze static v_pre pres_os[] = { pre_os, NULL }; 17420fa2881Sschwarze static v_pre pres_pp[] = { pre_par, NULL }; 175f73abda9Skristaps static v_pre pres_sh[] = { pre_sh, NULL }; 176f73abda9Skristaps static v_pre pres_ss[] = { pre_ss, NULL }; 17720fa2881Sschwarze static v_pre pres_std[] = { pre_std, NULL }; 178f73abda9Skristaps 17919a69263Sschwarze static const struct valids mdoc_valids[MDOC_MAX] = { 180099cfa7eSschwarze { NULL, NULL }, /* Ap */ 18120fa2881Sschwarze { pres_dd, posts_dd }, /* Dd */ 1826093755cSschwarze { pres_dt, posts_dt }, /* Dt */ 18320fa2881Sschwarze { pres_os, posts_os }, /* Os */ 184f73abda9Skristaps { pres_sh, posts_sh }, /* Sh */ 185f73abda9Skristaps { pres_ss, posts_ss }, /* Ss */ 186e0dd4c9cSschwarze { pres_pp, posts_pp }, /* Pp */ 187f73abda9Skristaps { pres_d1, posts_wline }, /* D1 */ 18820fa2881Sschwarze { pres_dl, posts_dl }, /* Dl */ 18920fa2881Sschwarze { pres_bd, posts_bd }, /* Bd */ 190f73abda9Skristaps { NULL, NULL }, /* Ed */ 191f73abda9Skristaps { pres_bl, posts_bl }, /* Bl */ 192f73abda9Skristaps { NULL, NULL }, /* El */ 193f73abda9Skristaps { pres_it, posts_it }, /* It */ 194e7a93ef3Sschwarze { NULL, NULL }, /* Ad */ 195f73abda9Skristaps { pres_an, posts_an }, /* An */ 19620fa2881Sschwarze { NULL, posts_defaults }, /* Ar */ 197e7a93ef3Sschwarze { NULL, NULL }, /* Cd */ 198f73abda9Skristaps { NULL, NULL }, /* Cm */ 199f73abda9Skristaps { NULL, NULL }, /* Dv */ 200e7a93ef3Sschwarze { pres_er, NULL }, /* Er */ 201f73abda9Skristaps { NULL, NULL }, /* Ev */ 20220fa2881Sschwarze { pres_std, posts_std }, /* Ex */ 203f73abda9Skristaps { NULL, NULL }, /* Fa */ 204e7a93ef3Sschwarze { pres_fd, posts_text }, /* Fd */ 205f73abda9Skristaps { NULL, NULL }, /* Fl */ 206e7a93ef3Sschwarze { NULL, NULL }, /* Fn */ 207e7a93ef3Sschwarze { NULL, NULL }, /* Ft */ 208e7a93ef3Sschwarze { NULL, NULL }, /* Ic */ 209b822ca0dSschwarze { NULL, posts_text1 }, /* In */ 21020fa2881Sschwarze { NULL, posts_defaults }, /* Li */ 2114602e85cSschwarze { NULL, posts_nd }, /* Nd */ 212f73abda9Skristaps { NULL, posts_nm }, /* Nm */ 213bca76d61Sschwarze { NULL, NULL }, /* Op */ 214f73abda9Skristaps { NULL, NULL }, /* Ot */ 21520fa2881Sschwarze { NULL, posts_defaults }, /* Pa */ 21620fa2881Sschwarze { pres_std, posts_std }, /* Rv */ 217f73abda9Skristaps { NULL, posts_st }, /* St */ 218f73abda9Skristaps { NULL, NULL }, /* Va */ 2198521b0bcSschwarze { NULL, posts_vt }, /* Vt */ 220e7a93ef3Sschwarze { NULL, posts_text }, /* Xr */ 221f73abda9Skristaps { NULL, posts_text }, /* %A */ 2224175bdabSschwarze { NULL, posts_text }, /* %B */ /* FIXME: can be used outside Rs/Re. */ 223b058e777Sschwarze { NULL, posts_text }, /* %D */ 224f73abda9Skristaps { NULL, posts_text }, /* %I */ 225f73abda9Skristaps { NULL, posts_text }, /* %J */ 226f73abda9Skristaps { NULL, posts_text }, /* %N */ 227f73abda9Skristaps { NULL, posts_text }, /* %O */ 228f73abda9Skristaps { NULL, posts_text }, /* %P */ 229f73abda9Skristaps { NULL, posts_text }, /* %R */ 2304175bdabSschwarze { NULL, posts_text }, /* %T */ /* FIXME: can be used outside Rs/Re. */ 231f73abda9Skristaps { NULL, posts_text }, /* %V */ 232f73abda9Skristaps { NULL, NULL }, /* Ac */ 233f73abda9Skristaps { NULL, NULL }, /* Ao */ 234bca76d61Sschwarze { NULL, NULL }, /* Aq */ 235f73abda9Skristaps { NULL, posts_at }, /* At */ 236f73abda9Skristaps { NULL, NULL }, /* Bc */ 237f73abda9Skristaps { NULL, posts_bf }, /* Bf */ 238f73abda9Skristaps { NULL, NULL }, /* Bo */ 239bca76d61Sschwarze { NULL, NULL }, /* Bq */ 240f73abda9Skristaps { NULL, NULL }, /* Bsx */ 241992063deSschwarze { NULL, posts_bx }, /* Bx */ 242f73abda9Skristaps { NULL, posts_bool }, /* Db */ 243f73abda9Skristaps { NULL, NULL }, /* Dc */ 244f73abda9Skristaps { NULL, NULL }, /* Do */ 245bca76d61Sschwarze { NULL, NULL }, /* Dq */ 246f73abda9Skristaps { NULL, NULL }, /* Ec */ 247f73abda9Skristaps { NULL, NULL }, /* Ef */ 248f73abda9Skristaps { NULL, NULL }, /* Em */ 249f73abda9Skristaps { NULL, NULL }, /* Eo */ 250f73abda9Skristaps { NULL, NULL }, /* Fx */ 251e7a93ef3Sschwarze { NULL, NULL }, /* Ms */ 252f73abda9Skristaps { NULL, posts_notext }, /* No */ 253af216717Sschwarze { NULL, posts_ns }, /* Ns */ 254f73abda9Skristaps { NULL, NULL }, /* Nx */ 255f73abda9Skristaps { NULL, NULL }, /* Ox */ 256f73abda9Skristaps { NULL, NULL }, /* Pc */ 257b822ca0dSschwarze { NULL, posts_text1 }, /* Pf */ 258f73abda9Skristaps { NULL, NULL }, /* Po */ 259bca76d61Sschwarze { NULL, NULL }, /* Pq */ 260f73abda9Skristaps { NULL, NULL }, /* Qc */ 261bca76d61Sschwarze { NULL, NULL }, /* Ql */ 262f73abda9Skristaps { NULL, NULL }, /* Qo */ 263bca76d61Sschwarze { NULL, NULL }, /* Qq */ 264f73abda9Skristaps { NULL, NULL }, /* Re */ 2658c62fbf5Sschwarze { NULL, posts_rs }, /* Rs */ 266f73abda9Skristaps { NULL, NULL }, /* Sc */ 267f73abda9Skristaps { NULL, NULL }, /* So */ 268bca76d61Sschwarze { NULL, NULL }, /* Sq */ 269f73abda9Skristaps { NULL, posts_bool }, /* Sm */ 270e7a93ef3Sschwarze { NULL, NULL }, /* Sx */ 271e7a93ef3Sschwarze { NULL, NULL }, /* Sy */ 272e7a93ef3Sschwarze { NULL, NULL }, /* Tn */ 273f73abda9Skristaps { NULL, NULL }, /* Ux */ 274f73abda9Skristaps { NULL, NULL }, /* Xc */ 275f73abda9Skristaps { NULL, NULL }, /* Xo */ 276f73abda9Skristaps { NULL, posts_fo }, /* Fo */ 277f73abda9Skristaps { NULL, NULL }, /* Fc */ 278f73abda9Skristaps { NULL, NULL }, /* Oo */ 279f73abda9Skristaps { NULL, NULL }, /* Oc */ 28020fa2881Sschwarze { NULL, posts_bk }, /* Bk */ 281f73abda9Skristaps { NULL, NULL }, /* Ek */ 282b31af00dSschwarze { NULL, posts_eoln }, /* Bt */ 283f73abda9Skristaps { NULL, NULL }, /* Hf */ 284f73abda9Skristaps { NULL, NULL }, /* Fr */ 285b31af00dSschwarze { NULL, posts_eoln }, /* Ud */ 286b31af00dSschwarze { NULL, posts_lb }, /* Lb */ 287e0dd4c9cSschwarze { pres_pp, posts_pp }, /* Lp */ 288e7a93ef3Sschwarze { NULL, NULL }, /* Lk */ 28920fa2881Sschwarze { NULL, posts_defaults }, /* Mt */ 290bca76d61Sschwarze { NULL, NULL }, /* Brq */ 291f73abda9Skristaps { NULL, NULL }, /* Bro */ 292f73abda9Skristaps { NULL, NULL }, /* Brc */ 293f73abda9Skristaps { NULL, posts_text }, /* %C */ 294f73abda9Skristaps { NULL, NULL }, /* Es */ 295f73abda9Skristaps { NULL, NULL }, /* En */ 296f73abda9Skristaps { NULL, NULL }, /* Dx */ 297f73abda9Skristaps { NULL, posts_text }, /* %Q */ 298e0dd4c9cSschwarze { NULL, posts_pp }, /* br */ 299e0dd4c9cSschwarze { NULL, posts_sp }, /* sp */ 300b822ca0dSschwarze { NULL, posts_text1 }, /* %U */ 3016093755cSschwarze { NULL, NULL }, /* Ta */ 302f73abda9Skristaps }; 303f73abda9Skristaps 30420fa2881Sschwarze #define RSORD_MAX 14 /* Number of `Rs' blocks. */ 30520fa2881Sschwarze 30620fa2881Sschwarze static const enum mdoct rsord[RSORD_MAX] = { 30720fa2881Sschwarze MDOC__A, 30820fa2881Sschwarze MDOC__T, 30920fa2881Sschwarze MDOC__B, 31020fa2881Sschwarze MDOC__I, 31120fa2881Sschwarze MDOC__J, 31220fa2881Sschwarze MDOC__R, 31320fa2881Sschwarze MDOC__N, 31420fa2881Sschwarze MDOC__V, 3150397c682Sschwarze MDOC__U, 31620fa2881Sschwarze MDOC__P, 31720fa2881Sschwarze MDOC__Q, 31820fa2881Sschwarze MDOC__D, 31920fa2881Sschwarze MDOC__O, 3200397c682Sschwarze MDOC__C 32120fa2881Sschwarze }; 32220fa2881Sschwarze 32319a69263Sschwarze static const char * const secnames[SEC__MAX] = { 32419a69263Sschwarze NULL, 32519a69263Sschwarze "NAME", 32619a69263Sschwarze "LIBRARY", 32719a69263Sschwarze "SYNOPSIS", 32819a69263Sschwarze "DESCRIPTION", 32919a69263Sschwarze "IMPLEMENTATION NOTES", 33019a69263Sschwarze "RETURN VALUES", 33119a69263Sschwarze "ENVIRONMENT", 33219a69263Sschwarze "FILES", 33319a69263Sschwarze "EXIT STATUS", 33419a69263Sschwarze "EXAMPLES", 33519a69263Sschwarze "DIAGNOSTICS", 33619a69263Sschwarze "COMPATIBILITY", 33719a69263Sschwarze "ERRORS", 33819a69263Sschwarze "SEE ALSO", 33919a69263Sschwarze "STANDARDS", 34019a69263Sschwarze "HISTORY", 34119a69263Sschwarze "AUTHORS", 34219a69263Sschwarze "CAVEATS", 34319a69263Sschwarze "BUGS", 34419a69263Sschwarze "SECURITY CONSIDERATIONS", 34519a69263Sschwarze NULL 34619a69263Sschwarze }; 347f73abda9Skristaps 348f73abda9Skristaps int 3496093755cSschwarze mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n) 350f73abda9Skristaps { 351f73abda9Skristaps v_pre *p; 352f73abda9Skristaps int line, pos; 35331e23753Sschwarze char *tp; 354f73abda9Skristaps 3552791bd1cSschwarze switch (n->type) { 3562791bd1cSschwarze case (MDOC_TEXT): 357f73abda9Skristaps tp = n->string; 358f73abda9Skristaps line = n->line; 359f73abda9Skristaps pos = n->pos; 36020fa2881Sschwarze check_text(mdoc, line, pos, tp); 3612791bd1cSschwarze /* FALLTHROUGH */ 3622791bd1cSschwarze case (MDOC_TBL): 3632791bd1cSschwarze /* FALLTHROUGH */ 3648d973ab1Sschwarze case (MDOC_EQN): 3658d973ab1Sschwarze /* FALLTHROUGH */ 3662791bd1cSschwarze case (MDOC_ROOT): 36720fa2881Sschwarze return(1); 3682791bd1cSschwarze default: 3692791bd1cSschwarze break; 370f73abda9Skristaps } 371f73abda9Skristaps 37220fa2881Sschwarze check_args(mdoc, n); 37320fa2881Sschwarze 374f73abda9Skristaps if (NULL == mdoc_valids[n->tok].pre) 375f73abda9Skristaps return(1); 376f73abda9Skristaps for (p = mdoc_valids[n->tok].pre; *p; p++) 377f73abda9Skristaps if ( ! (*p)(mdoc, n)) 378f73abda9Skristaps return(0); 379f73abda9Skristaps return(1); 380f73abda9Skristaps } 381f73abda9Skristaps 382f73abda9Skristaps 383f73abda9Skristaps int 384f73abda9Skristaps mdoc_valid_post(struct mdoc *mdoc) 385f73abda9Skristaps { 386f73abda9Skristaps v_post *p; 387f73abda9Skristaps 388f73abda9Skristaps if (MDOC_VALID & mdoc->last->flags) 389f73abda9Skristaps return(1); 390f73abda9Skristaps mdoc->last->flags |= MDOC_VALID; 391f73abda9Skristaps 3922791bd1cSschwarze switch (mdoc->last->type) { 3932791bd1cSschwarze case (MDOC_TEXT): 3942791bd1cSschwarze /* FALLTHROUGH */ 3958d973ab1Sschwarze case (MDOC_EQN): 3968d973ab1Sschwarze /* FALLTHROUGH */ 3972791bd1cSschwarze case (MDOC_TBL): 398f73abda9Skristaps return(1); 3992791bd1cSschwarze case (MDOC_ROOT): 400f73abda9Skristaps return(post_root(mdoc)); 4012791bd1cSschwarze default: 4022791bd1cSschwarze break; 4032791bd1cSschwarze } 404f73abda9Skristaps 405f73abda9Skristaps if (NULL == mdoc_valids[mdoc->last->tok].post) 406f73abda9Skristaps return(1); 407f73abda9Skristaps for (p = mdoc_valids[mdoc->last->tok].post; *p; p++) 408f73abda9Skristaps if ( ! (*p)(mdoc)) 409f73abda9Skristaps return(0); 410f73abda9Skristaps 411f73abda9Skristaps return(1); 412f73abda9Skristaps } 413f73abda9Skristaps 4147c2be9f8Sschwarze static int 4157c2be9f8Sschwarze check_count(struct mdoc *m, enum mdoc_type type, 4167c2be9f8Sschwarze enum check_lvl lvl, enum check_ineq ineq, int val) 417f73abda9Skristaps { 4187c2be9f8Sschwarze const char *p; 419bb648afaSschwarze enum mandocerr t; 4207c2be9f8Sschwarze 4217c2be9f8Sschwarze if (m->last->type != type) 4227c2be9f8Sschwarze return(1); 4237c2be9f8Sschwarze 4247c2be9f8Sschwarze switch (ineq) { 4257c2be9f8Sschwarze case (CHECK_LT): 4267c2be9f8Sschwarze p = "less than "; 4277c2be9f8Sschwarze if (m->last->nchild < val) 4287c2be9f8Sschwarze return(1); 4297c2be9f8Sschwarze break; 4307c2be9f8Sschwarze case (CHECK_GT): 431bb648afaSschwarze p = "more than "; 4327c2be9f8Sschwarze if (m->last->nchild > val) 4337c2be9f8Sschwarze return(1); 4347c2be9f8Sschwarze break; 4357c2be9f8Sschwarze case (CHECK_EQ): 4367c2be9f8Sschwarze p = ""; 4377c2be9f8Sschwarze if (val == m->last->nchild) 4387c2be9f8Sschwarze return(1); 4397c2be9f8Sschwarze break; 44020fa2881Sschwarze default: 44120fa2881Sschwarze abort(); 44220fa2881Sschwarze /* NOTREACHED */ 4437c2be9f8Sschwarze } 4447c2be9f8Sschwarze 445bb648afaSschwarze t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT; 446a35fc07aSschwarze mandoc_vmsg(t, m->parse, m->last->line, m->last->pos, 4477c2be9f8Sschwarze "want %s%d children (have %d)", 44819a69263Sschwarze p, val, m->last->nchild); 44919a69263Sschwarze return(1); 4507c2be9f8Sschwarze } 451f73abda9Skristaps 4527c2be9f8Sschwarze static int 4537c2be9f8Sschwarze berr_ge1(POST_ARGS) 454f73abda9Skristaps { 455f73abda9Skristaps 456bb648afaSschwarze return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0)); 457f73abda9Skristaps } 458f73abda9Skristaps 4597c2be9f8Sschwarze static int 4607c2be9f8Sschwarze bwarn_ge1(POST_ARGS) 4617c2be9f8Sschwarze { 4627c2be9f8Sschwarze return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0)); 463f73abda9Skristaps } 464f73abda9Skristaps 4657c2be9f8Sschwarze static int 4667c2be9f8Sschwarze ewarn_eq0(POST_ARGS) 4677c2be9f8Sschwarze { 4687c2be9f8Sschwarze return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0)); 4697c2be9f8Sschwarze } 4707c2be9f8Sschwarze 4717c2be9f8Sschwarze static int 472bb648afaSschwarze ewarn_eq1(POST_ARGS) 473bb648afaSschwarze { 474bb648afaSschwarze return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1)); 475bb648afaSschwarze } 476bb648afaSschwarze 477bb648afaSschwarze static int 4787c2be9f8Sschwarze ewarn_ge1(POST_ARGS) 4797c2be9f8Sschwarze { 4807c2be9f8Sschwarze return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0)); 4817c2be9f8Sschwarze } 4827c2be9f8Sschwarze 4837c2be9f8Sschwarze static int 484bb648afaSschwarze ewarn_le1(POST_ARGS) 4857c2be9f8Sschwarze { 486bb648afaSschwarze return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2)); 4877c2be9f8Sschwarze } 4887c2be9f8Sschwarze 4897c2be9f8Sschwarze static int 4907c2be9f8Sschwarze hwarn_eq0(POST_ARGS) 4917c2be9f8Sschwarze { 4927c2be9f8Sschwarze return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0)); 4937c2be9f8Sschwarze } 4947c2be9f8Sschwarze 4957c2be9f8Sschwarze static int 4967c2be9f8Sschwarze hwarn_eq1(POST_ARGS) 4977c2be9f8Sschwarze { 4987c2be9f8Sschwarze return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1)); 4997c2be9f8Sschwarze } 5007c2be9f8Sschwarze 5017c2be9f8Sschwarze static int 502bb648afaSschwarze hwarn_ge1(POST_ARGS) 503bb648afaSschwarze { 504bb648afaSschwarze return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0)); 505bb648afaSschwarze } 506bb648afaSschwarze 507bb648afaSschwarze static int 5087c2be9f8Sschwarze hwarn_le1(POST_ARGS) 5097c2be9f8Sschwarze { 5107c2be9f8Sschwarze return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2)); 5117c2be9f8Sschwarze } 512f73abda9Skristaps 51320fa2881Sschwarze static void 51431e23753Sschwarze check_args(struct mdoc *m, struct mdoc_node *n) 515f73abda9Skristaps { 516f73abda9Skristaps int i; 517f73abda9Skristaps 518f73abda9Skristaps if (NULL == n->args) 51920fa2881Sschwarze return; 520f73abda9Skristaps 521f73abda9Skristaps assert(n->args->argc); 522f73abda9Skristaps for (i = 0; i < (int)n->args->argc; i++) 52320fa2881Sschwarze check_argv(m, n, &n->args->argv[i]); 524f73abda9Skristaps } 525f73abda9Skristaps 52620fa2881Sschwarze static void 52731e23753Sschwarze check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v) 528f73abda9Skristaps { 529f73abda9Skristaps int i; 530f73abda9Skristaps 531f73abda9Skristaps for (i = 0; i < (int)v->sz; i++) 53220fa2881Sschwarze check_text(m, v->line, v->pos, v->value[i]); 533f73abda9Skristaps 53420fa2881Sschwarze /* FIXME: move to post_std(). */ 53520fa2881Sschwarze 53620fa2881Sschwarze if (MDOC_Std == v->arg) 53720fa2881Sschwarze if ( ! (v->sz || m->meta.name)) 53820fa2881Sschwarze mdoc_nmsg(m, n, MANDOCERR_NONAME); 539f73abda9Skristaps } 540f73abda9Skristaps 54120fa2881Sschwarze static void 542ddce0b0cSschwarze check_text(struct mdoc *m, int ln, int pos, char *p) 543f73abda9Skristaps { 54404e980cbSschwarze char *cp; 545769ee804Sschwarze 54604e980cbSschwarze if (MDOC_LITERAL & m->flags) 5471cdbf331Sschwarze return; 5481cdbf331Sschwarze 5491cdbf331Sschwarze for (cp = p; NULL != (p = strchr(p, '\t')); p++) 55004e980cbSschwarze mdoc_pmsg(m, ln, pos + (int)(p - cp), MANDOCERR_BADTAB); 551f73abda9Skristaps } 552f73abda9Skristaps 553f73abda9Skristaps static int 554dd94fa3aSschwarze check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t) 555f73abda9Skristaps { 556f73abda9Skristaps 557f73abda9Skristaps assert(n->parent); 558f73abda9Skristaps if ((MDOC_ROOT == t || tok == n->parent->tok) && 559f73abda9Skristaps (t == n->parent->type)) 560f73abda9Skristaps return(1); 561f73abda9Skristaps 562a35fc07aSschwarze mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line, 563a35fc07aSschwarze n->pos, "want parent %s", MDOC_ROOT == t ? 564a35fc07aSschwarze "<root>" : mdoc_macronames[tok]); 5656e03d529Sschwarze return(0); 566f73abda9Skristaps } 567f73abda9Skristaps 568f73abda9Skristaps 569f73abda9Skristaps static int 570f73abda9Skristaps pre_display(PRE_ARGS) 571f73abda9Skristaps { 572f73abda9Skristaps struct mdoc_node *node; 573f73abda9Skristaps 574f73abda9Skristaps if (MDOC_BLOCK != n->type) 575f73abda9Skristaps return(1); 576f73abda9Skristaps 577f73abda9Skristaps for (node = mdoc->last->parent; node; node = node->parent) 578f73abda9Skristaps if (MDOC_BLOCK == node->type) 579f73abda9Skristaps if (MDOC_Bd == node->tok) 580f73abda9Skristaps break; 58120fa2881Sschwarze 58205c39368Sschwarze if (node) 5836e03d529Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP); 58405c39368Sschwarze 58505c39368Sschwarze return(1); 586f73abda9Skristaps } 587f73abda9Skristaps 588f73abda9Skristaps 589f73abda9Skristaps static int 590f73abda9Skristaps pre_bl(PRE_ARGS) 591f73abda9Skristaps { 59231e23753Sschwarze int i, comp, dup; 59331e23753Sschwarze const char *offs, *width; 5946093755cSschwarze enum mdoc_list lt; 595769ee804Sschwarze struct mdoc_node *np; 596f73abda9Skristaps 5976093755cSschwarze if (MDOC_BLOCK != n->type) { 598769ee804Sschwarze if (ENDBODY_NOT != n->end) { 599769ee804Sschwarze assert(n->pending); 600769ee804Sschwarze np = n->pending->parent; 601769ee804Sschwarze } else 602769ee804Sschwarze np = n->parent; 603769ee804Sschwarze 604769ee804Sschwarze assert(np); 605769ee804Sschwarze assert(MDOC_BLOCK == np->type); 606769ee804Sschwarze assert(MDOC_Bl == np->tok); 607f73abda9Skristaps return(1); 6086e03d529Sschwarze } 609f73abda9Skristaps 6106093755cSschwarze /* 6116093755cSschwarze * First figure out which kind of list to use: bind ourselves to 6126093755cSschwarze * the first mentioned list type and warn about any remaining 6136093755cSschwarze * ones. If we find no list type, we default to LIST_item. 6146093755cSschwarze */ 615f73abda9Skristaps 616f73abda9Skristaps /* LINTED */ 6176093755cSschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) { 6186093755cSschwarze lt = LIST__NONE; 61931e23753Sschwarze dup = comp = 0; 62031e23753Sschwarze width = offs = NULL; 6216093755cSschwarze switch (n->args->argv[i].arg) { 6226093755cSschwarze /* Set list types. */ 623f73abda9Skristaps case (MDOC_Bullet): 6246093755cSschwarze lt = LIST_bullet; 6256093755cSschwarze break; 626f73abda9Skristaps case (MDOC_Dash): 6276093755cSschwarze lt = LIST_dash; 6286093755cSschwarze break; 629f73abda9Skristaps case (MDOC_Enum): 6306093755cSschwarze lt = LIST_enum; 6316093755cSschwarze break; 632f73abda9Skristaps case (MDOC_Hyphen): 6336093755cSschwarze lt = LIST_hyphen; 6346093755cSschwarze break; 635f73abda9Skristaps case (MDOC_Item): 6366093755cSschwarze lt = LIST_item; 6376093755cSschwarze break; 638f73abda9Skristaps case (MDOC_Tag): 6396093755cSschwarze lt = LIST_tag; 6406093755cSschwarze break; 641f73abda9Skristaps case (MDOC_Diag): 6426093755cSschwarze lt = LIST_diag; 6436093755cSschwarze break; 644f73abda9Skristaps case (MDOC_Hang): 6456093755cSschwarze lt = LIST_hang; 6466093755cSschwarze break; 647f73abda9Skristaps case (MDOC_Ohang): 6486093755cSschwarze lt = LIST_ohang; 6496093755cSschwarze break; 650f73abda9Skristaps case (MDOC_Inset): 6516093755cSschwarze lt = LIST_inset; 6526093755cSschwarze break; 653f73abda9Skristaps case (MDOC_Column): 6546093755cSschwarze lt = LIST_column; 65564d728e4Sschwarze break; 6566093755cSschwarze /* Set list arguments. */ 65750e63e03Sschwarze case (MDOC_Compact): 6588c62fbf5Sschwarze dup = n->norm->Bl.comp; 65931e23753Sschwarze comp = 1; 66050e63e03Sschwarze break; 661f73abda9Skristaps case (MDOC_Width): 66222972b14Sschwarze /* NB: this can be empty! */ 66322972b14Sschwarze if (n->args->argv[i].sz) { 66431e23753Sschwarze width = n->args->argv[i].value[0]; 66522972b14Sschwarze dup = (NULL != n->norm->Bl.width); 66622972b14Sschwarze break; 66722972b14Sschwarze } 66822972b14Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 66964d728e4Sschwarze break; 670f73abda9Skristaps case (MDOC_Offset): 67131e23753Sschwarze /* NB: this can be empty! */ 67231e23753Sschwarze if (n->args->argv[i].sz) { 67331e23753Sschwarze offs = n->args->argv[i].value[0]; 6748c62fbf5Sschwarze dup = (NULL != n->norm->Bl.offs); 67531e23753Sschwarze break; 67631e23753Sschwarze } 67720fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 678f73abda9Skristaps break; 679ddce0b0cSschwarze default: 680ddce0b0cSschwarze continue; 681f73abda9Skristaps } 682f73abda9Skristaps 6836093755cSschwarze /* Check: duplicate auxiliary arguments. */ 6846093755cSschwarze 68520fa2881Sschwarze if (dup) 68620fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); 6876093755cSschwarze 68831e23753Sschwarze if (comp && ! dup) 6898c62fbf5Sschwarze n->norm->Bl.comp = comp; 69031e23753Sschwarze if (offs && ! dup) 6918c62fbf5Sschwarze n->norm->Bl.offs = offs; 69231e23753Sschwarze if (width && ! dup) 6938c62fbf5Sschwarze n->norm->Bl.width = width; 69431e23753Sschwarze 6956093755cSschwarze /* Check: multiple list types. */ 6966093755cSschwarze 6978c62fbf5Sschwarze if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE) 69820fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP); 6996093755cSschwarze 7006093755cSschwarze /* Assign list type. */ 7016093755cSschwarze 7028c62fbf5Sschwarze if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) { 7038c62fbf5Sschwarze n->norm->Bl.type = lt; 704769ee804Sschwarze /* Set column information, too. */ 705769ee804Sschwarze if (LIST_column == lt) { 7068c62fbf5Sschwarze n->norm->Bl.ncols = 707769ee804Sschwarze n->args->argv[i].sz; 70804e980cbSschwarze n->norm->Bl.cols = (void *) 709769ee804Sschwarze n->args->argv[i].value; 710769ee804Sschwarze } 711769ee804Sschwarze } 7126093755cSschwarze 7136093755cSschwarze /* The list type should come first. */ 7146093755cSschwarze 7158c62fbf5Sschwarze if (n->norm->Bl.type == LIST__NONE) 7168c62fbf5Sschwarze if (n->norm->Bl.width || 7178c62fbf5Sschwarze n->norm->Bl.offs || 7188c62fbf5Sschwarze n->norm->Bl.comp) 71920fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST); 7206093755cSschwarze 7216093755cSschwarze continue; 7226093755cSschwarze } 7236093755cSschwarze 7246093755cSschwarze /* Allow lists to default to LIST_item. */ 7256093755cSschwarze 7268c62fbf5Sschwarze if (LIST__NONE == n->norm->Bl.type) { 72720fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE); 7288c62fbf5Sschwarze n->norm->Bl.type = LIST_item; 7296e03d529Sschwarze } 730f73abda9Skristaps 73164d728e4Sschwarze /* 73264d728e4Sschwarze * Validate the width field. Some list types don't need width 73364d728e4Sschwarze * types and should be warned about them. Others should have it 7345eced068Sschwarze * and must also be warned. Yet others have a default and need 7355eced068Sschwarze * no warning. 73664d728e4Sschwarze */ 73764d728e4Sschwarze 7388c62fbf5Sschwarze switch (n->norm->Bl.type) { 7396093755cSschwarze case (LIST_tag): 7405eced068Sschwarze if (NULL == n->norm->Bl.width) 74120fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG); 742f73abda9Skristaps break; 7436093755cSschwarze case (LIST_column): 7446093755cSschwarze /* FALLTHROUGH */ 7456093755cSschwarze case (LIST_diag): 7466093755cSschwarze /* FALLTHROUGH */ 7476093755cSschwarze case (LIST_ohang): 7486093755cSschwarze /* FALLTHROUGH */ 7496093755cSschwarze case (LIST_inset): 7506093755cSschwarze /* FALLTHROUGH */ 7516093755cSschwarze case (LIST_item): 7528c62fbf5Sschwarze if (n->norm->Bl.width) 753817ac90bSschwarze mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 7546093755cSschwarze break; 7555eced068Sschwarze case (LIST_bullet): 7565eced068Sschwarze /* FALLTHROUGH */ 7575eced068Sschwarze case (LIST_dash): 7585eced068Sschwarze /* FALLTHROUGH */ 7595eced068Sschwarze case (LIST_hyphen): 7605eced068Sschwarze if (NULL == n->norm->Bl.width) 7615eced068Sschwarze n->norm->Bl.width = "2n"; 7625eced068Sschwarze break; 7635eced068Sschwarze case (LIST_enum): 7645eced068Sschwarze if (NULL == n->norm->Bl.width) 7655eced068Sschwarze n->norm->Bl.width = "3n"; 7665eced068Sschwarze break; 76764d728e4Sschwarze default: 768f73abda9Skristaps break; 76964d728e4Sschwarze } 77064d728e4Sschwarze 771f73abda9Skristaps return(1); 772f73abda9Skristaps } 773f73abda9Skristaps 774f73abda9Skristaps 775f73abda9Skristaps static int 776f73abda9Skristaps pre_bd(PRE_ARGS) 777f73abda9Skristaps { 77831e23753Sschwarze int i, dup, comp; 77931e23753Sschwarze enum mdoc_disp dt; 78031e23753Sschwarze const char *offs; 781769ee804Sschwarze struct mdoc_node *np; 782f73abda9Skristaps 78331e23753Sschwarze if (MDOC_BLOCK != n->type) { 784769ee804Sschwarze if (ENDBODY_NOT != n->end) { 785769ee804Sschwarze assert(n->pending); 786769ee804Sschwarze np = n->pending->parent; 787769ee804Sschwarze } else 788769ee804Sschwarze np = n->parent; 789769ee804Sschwarze 790769ee804Sschwarze assert(np); 791769ee804Sschwarze assert(MDOC_BLOCK == np->type); 792769ee804Sschwarze assert(MDOC_Bd == np->tok); 793f73abda9Skristaps return(1); 7946e03d529Sschwarze } 795f73abda9Skristaps 796f73abda9Skristaps /* LINTED */ 79731e23753Sschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) { 79831e23753Sschwarze dt = DISP__NONE; 79931e23753Sschwarze dup = comp = 0; 80031e23753Sschwarze offs = NULL; 80131e23753Sschwarze 802f73abda9Skristaps switch (n->args->argv[i].arg) { 803b822ca0dSschwarze case (MDOC_Centred): 80431e23753Sschwarze dt = DISP_centred; 80531e23753Sschwarze break; 806f73abda9Skristaps case (MDOC_Ragged): 80731e23753Sschwarze dt = DISP_ragged; 80831e23753Sschwarze break; 809f73abda9Skristaps case (MDOC_Unfilled): 81031e23753Sschwarze dt = DISP_unfilled; 81131e23753Sschwarze break; 812f73abda9Skristaps case (MDOC_Filled): 81331e23753Sschwarze dt = DISP_filled; 81431e23753Sschwarze break; 815f73abda9Skristaps case (MDOC_Literal): 81631e23753Sschwarze dt = DISP_literal; 817f73abda9Skristaps break; 81831e23753Sschwarze case (MDOC_File): 81931e23753Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP); 8206e03d529Sschwarze return(0); 82131e23753Sschwarze case (MDOC_Offset): 82231e23753Sschwarze /* NB: this can be empty! */ 82331e23753Sschwarze if (n->args->argv[i].sz) { 82431e23753Sschwarze offs = n->args->argv[i].value[0]; 8258c62fbf5Sschwarze dup = (NULL != n->norm->Bd.offs); 826f73abda9Skristaps break; 827f73abda9Skristaps } 82820fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 82931e23753Sschwarze break; 83031e23753Sschwarze case (MDOC_Compact): 83131e23753Sschwarze comp = 1; 8328c62fbf5Sschwarze dup = n->norm->Bd.comp; 83331e23753Sschwarze break; 83431e23753Sschwarze default: 83531e23753Sschwarze abort(); 83631e23753Sschwarze /* NOTREACHED */ 83731e23753Sschwarze } 83831e23753Sschwarze 83931e23753Sschwarze /* Check whether we have duplicates. */ 84031e23753Sschwarze 84120fa2881Sschwarze if (dup) 84220fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); 84331e23753Sschwarze 84431e23753Sschwarze /* Make our auxiliary assignments. */ 84531e23753Sschwarze 84631e23753Sschwarze if (offs && ! dup) 8478c62fbf5Sschwarze n->norm->Bd.offs = offs; 84831e23753Sschwarze if (comp && ! dup) 8498c62fbf5Sschwarze n->norm->Bd.comp = comp; 85031e23753Sschwarze 85131e23753Sschwarze /* Check whether a type has already been assigned. */ 85231e23753Sschwarze 8538c62fbf5Sschwarze if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE) 85420fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP); 85531e23753Sschwarze 85631e23753Sschwarze /* Make our type assignment. */ 85731e23753Sschwarze 8588c62fbf5Sschwarze if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE) 8598c62fbf5Sschwarze n->norm->Bd.type = dt; 86031e23753Sschwarze } 86131e23753Sschwarze 8628c62fbf5Sschwarze if (DISP__NONE == n->norm->Bd.type) { 86320fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE); 8648c62fbf5Sschwarze n->norm->Bd.type = DISP_ragged; 86531e23753Sschwarze } 86631e23753Sschwarze 86731e23753Sschwarze return(1); 868f73abda9Skristaps } 869f73abda9Skristaps 870f73abda9Skristaps 871f73abda9Skristaps static int 872f73abda9Skristaps pre_ss(PRE_ARGS) 873f73abda9Skristaps { 874f73abda9Skristaps 875f73abda9Skristaps if (MDOC_BLOCK != n->type) 876f73abda9Skristaps return(1); 877f73abda9Skristaps return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY)); 878f73abda9Skristaps } 879f73abda9Skristaps 880f73abda9Skristaps 881f73abda9Skristaps static int 882f73abda9Skristaps pre_sh(PRE_ARGS) 883f73abda9Skristaps { 884f73abda9Skristaps 885f73abda9Skristaps if (MDOC_BLOCK != n->type) 886f73abda9Skristaps return(1); 8879830bf9fSschwarze 888f8618d99Sschwarze roff_regunset(mdoc->roff, REG_nS); 889a4c002ecSschwarze return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT)); 890f73abda9Skristaps } 891f73abda9Skristaps 892f73abda9Skristaps 893f73abda9Skristaps static int 894f73abda9Skristaps pre_it(PRE_ARGS) 895f73abda9Skristaps { 896f73abda9Skristaps 897f73abda9Skristaps if (MDOC_BLOCK != n->type) 898f73abda9Skristaps return(1); 89920fa2881Sschwarze 900f73abda9Skristaps return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY)); 901f73abda9Skristaps } 902f73abda9Skristaps 903f73abda9Skristaps 904f73abda9Skristaps static int 905f73abda9Skristaps pre_an(PRE_ARGS) 906f73abda9Skristaps { 9076475d5b0Sschwarze int i; 908f73abda9Skristaps 909769ee804Sschwarze if (NULL == n->args) 910f73abda9Skristaps return(1); 911769ee804Sschwarze 9126475d5b0Sschwarze for (i = 1; i < (int)n->args->argc; i++) 91320fa2881Sschwarze mdoc_pmsg(mdoc, n->args->argv[i].line, 91420fa2881Sschwarze n->args->argv[i].pos, MANDOCERR_IGNARGV); 9157c2be9f8Sschwarze 916769ee804Sschwarze if (MDOC_Split == n->args->argv[0].arg) 9178c62fbf5Sschwarze n->norm->An.auth = AUTH_split; 918769ee804Sschwarze else if (MDOC_Nosplit == n->args->argv[0].arg) 9198c62fbf5Sschwarze n->norm->An.auth = AUTH_nosplit; 920769ee804Sschwarze else 921769ee804Sschwarze abort(); 922769ee804Sschwarze 923769ee804Sschwarze return(1); 924f73abda9Skristaps } 925f73abda9Skristaps 926f73abda9Skristaps static int 92720fa2881Sschwarze pre_std(PRE_ARGS) 928f73abda9Skristaps { 929f73abda9Skristaps 93020fa2881Sschwarze if (n->args && 1 == n->args->argc) 93120fa2881Sschwarze if (MDOC_Std == n->args->argv[0].arg) 93220fa2881Sschwarze return(1); 933f73abda9Skristaps 93420fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV); 9356093755cSschwarze return(1); 9366093755cSschwarze } 9376093755cSschwarze 9386093755cSschwarze static int 939f73abda9Skristaps pre_dt(PRE_ARGS) 940f73abda9Skristaps { 941f73abda9Skristaps 942b058e777Sschwarze if (NULL == mdoc->meta.date || mdoc->meta.os) 94320fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); 94420fa2881Sschwarze 945f73abda9Skristaps if (mdoc->meta.title) 94620fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); 94720fa2881Sschwarze 948f73abda9Skristaps return(1); 949f73abda9Skristaps } 950f73abda9Skristaps 951f73abda9Skristaps static int 952f73abda9Skristaps pre_os(PRE_ARGS) 953f73abda9Skristaps { 954f73abda9Skristaps 955b058e777Sschwarze if (NULL == mdoc->meta.title || NULL == mdoc->meta.date) 95620fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); 95720fa2881Sschwarze 958f73abda9Skristaps if (mdoc->meta.os) 95920fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); 96020fa2881Sschwarze 961f73abda9Skristaps return(1); 962f73abda9Skristaps } 963f73abda9Skristaps 964f73abda9Skristaps static int 965f73abda9Skristaps pre_dd(PRE_ARGS) 966f73abda9Skristaps { 967f73abda9Skristaps 968f73abda9Skristaps if (mdoc->meta.title || mdoc->meta.os) 96920fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); 97020fa2881Sschwarze 971f73abda9Skristaps if (mdoc->meta.date) 97220fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); 97320fa2881Sschwarze 974f73abda9Skristaps return(1); 975f73abda9Skristaps } 976f73abda9Skristaps 977f73abda9Skristaps 978f73abda9Skristaps static int 979f73abda9Skristaps post_bf(POST_ARGS) 980f73abda9Skristaps { 981769ee804Sschwarze struct mdoc_node *np; 982ddce0b0cSschwarze enum mdocargt arg; 983f73abda9Skristaps 984769ee804Sschwarze /* 985769ee804Sschwarze * Unlike other data pointers, these are "housed" by the HEAD 986769ee804Sschwarze * element, which contains the goods. 987769ee804Sschwarze */ 988769ee804Sschwarze 989769ee804Sschwarze if (MDOC_HEAD != mdoc->last->type) { 990769ee804Sschwarze if (ENDBODY_NOT != mdoc->last->end) { 991769ee804Sschwarze assert(mdoc->last->pending); 992769ee804Sschwarze np = mdoc->last->pending->parent->head; 993769ee804Sschwarze } else if (MDOC_BLOCK != mdoc->last->type) { 994769ee804Sschwarze np = mdoc->last->parent->head; 995769ee804Sschwarze } else 996769ee804Sschwarze np = mdoc->last->head; 997769ee804Sschwarze 998769ee804Sschwarze assert(np); 999769ee804Sschwarze assert(MDOC_HEAD == np->type); 1000769ee804Sschwarze assert(MDOC_Bf == np->tok); 1001f73abda9Skristaps return(1); 10026e03d529Sschwarze } 1003f73abda9Skristaps 1004769ee804Sschwarze np = mdoc->last; 1005769ee804Sschwarze assert(MDOC_BLOCK == np->parent->type); 1006769ee804Sschwarze assert(MDOC_Bf == np->parent->tok); 100750d41253Sschwarze 1008769ee804Sschwarze /* 1009769ee804Sschwarze * Cannot have both argument and parameter. 1010769ee804Sschwarze * If neither is specified, let it through with a warning. 1011769ee804Sschwarze */ 1012f73abda9Skristaps 1013769ee804Sschwarze if (np->parent->args && np->child) { 1014769ee804Sschwarze mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT); 10156e03d529Sschwarze return(0); 101620fa2881Sschwarze } else if (NULL == np->parent->args && NULL == np->child) { 101720fa2881Sschwarze mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); 101820fa2881Sschwarze return(1); 101920fa2881Sschwarze } 1020769ee804Sschwarze 1021769ee804Sschwarze /* Extract argument into data. */ 1022769ee804Sschwarze 1023769ee804Sschwarze if (np->parent->args) { 1024769ee804Sschwarze arg = np->parent->args->argv[0].arg; 1025769ee804Sschwarze if (MDOC_Emphasis == arg) 10268c62fbf5Sschwarze np->norm->Bf.font = FONT_Em; 1027769ee804Sschwarze else if (MDOC_Literal == arg) 10288c62fbf5Sschwarze np->norm->Bf.font = FONT_Li; 1029769ee804Sschwarze else if (MDOC_Symbolic == arg) 10308c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy; 1031769ee804Sschwarze else 1032769ee804Sschwarze abort(); 1033769ee804Sschwarze return(1); 1034769ee804Sschwarze } 1035769ee804Sschwarze 1036769ee804Sschwarze /* Extract parameter into data. */ 1037769ee804Sschwarze 1038769ee804Sschwarze if (0 == strcmp(np->child->string, "Em")) 10398c62fbf5Sschwarze np->norm->Bf.font = FONT_Em; 1040769ee804Sschwarze else if (0 == strcmp(np->child->string, "Li")) 10418c62fbf5Sschwarze np->norm->Bf.font = FONT_Li; 1042769ee804Sschwarze else if (0 == strcmp(np->child->string, "Sy")) 10438c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy; 104420fa2881Sschwarze else 104520fa2881Sschwarze mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); 1046769ee804Sschwarze 1047769ee804Sschwarze return(1); 1048f73abda9Skristaps } 1049f73abda9Skristaps 1050f73abda9Skristaps static int 105171719887Sschwarze post_lb(POST_ARGS) 105271719887Sschwarze { 105320fa2881Sschwarze const char *p; 105420fa2881Sschwarze char *buf; 105520fa2881Sschwarze size_t sz; 105671719887Sschwarze 1057bb648afaSschwarze check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); 1058bb648afaSschwarze 105920fa2881Sschwarze assert(mdoc->last->child); 106020fa2881Sschwarze assert(MDOC_TEXT == mdoc->last->child->type); 106120fa2881Sschwarze 106220fa2881Sschwarze p = mdoc_a2lib(mdoc->last->child->string); 106320fa2881Sschwarze 106420fa2881Sschwarze /* If lookup ok, replace with table value. */ 106520fa2881Sschwarze 106620fa2881Sschwarze if (p) { 106720fa2881Sschwarze free(mdoc->last->child->string); 106820fa2881Sschwarze mdoc->last->child->string = mandoc_strdup(p); 106971719887Sschwarze return(1); 107071719887Sschwarze } 107171719887Sschwarze 107220fa2881Sschwarze /* If not, use "library ``xxxx''. */ 107320fa2881Sschwarze 107420fa2881Sschwarze sz = strlen(mdoc->last->child->string) + 107520fa2881Sschwarze 2 + strlen("\\(lqlibrary\\(rq"); 107620fa2881Sschwarze buf = mandoc_malloc(sz); 107720fa2881Sschwarze snprintf(buf, sz, "library \\(lq%s\\(rq", 107820fa2881Sschwarze mdoc->last->child->string); 107920fa2881Sschwarze free(mdoc->last->child->string); 108020fa2881Sschwarze mdoc->last->child->string = buf; 108120fa2881Sschwarze return(1); 108220fa2881Sschwarze } 108371719887Sschwarze 108471719887Sschwarze static int 1085b31af00dSschwarze post_eoln(POST_ARGS) 1086b31af00dSschwarze { 1087b31af00dSschwarze 108820fa2881Sschwarze if (mdoc->last->child) 108920fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST); 1090b31af00dSschwarze return(1); 1091b31af00dSschwarze } 1092b31af00dSschwarze 1093b31af00dSschwarze 1094b31af00dSschwarze static int 10958521b0bcSschwarze post_vt(POST_ARGS) 10968521b0bcSschwarze { 10978521b0bcSschwarze const struct mdoc_node *n; 10988521b0bcSschwarze 10998521b0bcSschwarze /* 11008521b0bcSschwarze * The Vt macro comes in both ELEM and BLOCK form, both of which 11018521b0bcSschwarze * have different syntaxes (yet more context-sensitive 1102e7a93ef3Sschwarze * behaviour). ELEM types must have a child, which is already 1103e7a93ef3Sschwarze * guaranteed by the in_line parsing routine; BLOCK types, 11048521b0bcSschwarze * specifically the BODY, should only have TEXT children. 11058521b0bcSschwarze */ 11068521b0bcSschwarze 11078521b0bcSschwarze if (MDOC_BODY != mdoc->last->type) 11088521b0bcSschwarze return(1); 11098521b0bcSschwarze 11108521b0bcSschwarze for (n = mdoc->last->child; n; n = n->next) 1111bc49dbe1Sschwarze if (MDOC_TEXT != n->type) 111220fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_CHILD); 11138521b0bcSschwarze 11148521b0bcSschwarze return(1); 11158521b0bcSschwarze } 11168521b0bcSschwarze 11178521b0bcSschwarze 11188521b0bcSschwarze static int 1119f73abda9Skristaps post_nm(POST_ARGS) 1120f73abda9Skristaps { 112120fa2881Sschwarze char buf[BUFSIZ]; 112204e980cbSschwarze int c; 112320fa2881Sschwarze 1124160ac481Sschwarze if (NULL != mdoc->meta.name) 112520fa2881Sschwarze return(1); 112620fa2881Sschwarze 1127160ac481Sschwarze /* Try to use our children for setting the meta name. */ 112820fa2881Sschwarze 1129160ac481Sschwarze if (NULL != mdoc->last->child) { 113004e980cbSschwarze buf[0] = '\0'; 1131160ac481Sschwarze c = concat(buf, mdoc->last->child, BUFSIZ); 1132160ac481Sschwarze } else 1133160ac481Sschwarze c = 0; 1134160ac481Sschwarze 1135160ac481Sschwarze switch (c) { 1136160ac481Sschwarze case (-1): 113704e980cbSschwarze mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM); 113820fa2881Sschwarze return(0); 1139160ac481Sschwarze case (0): 1140160ac481Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME); 1141160ac481Sschwarze mdoc->meta.name = mandoc_strdup("UNKNOWN"); 1142160ac481Sschwarze break; 1143160ac481Sschwarze default: 114420fa2881Sschwarze mdoc->meta.name = mandoc_strdup(buf); 1145160ac481Sschwarze break; 1146160ac481Sschwarze } 114720fa2881Sschwarze return(1); 114820fa2881Sschwarze } 114920fa2881Sschwarze 115020fa2881Sschwarze static int 115120fa2881Sschwarze post_literal(POST_ARGS) 115220fa2881Sschwarze { 115320fa2881Sschwarze 115420fa2881Sschwarze /* 115520fa2881Sschwarze * The `Dl' (note "el" not "one") and `Bd' macros unset the 115620fa2881Sschwarze * MDOC_LITERAL flag as they leave. Note that `Bd' only sets 115720fa2881Sschwarze * this in literal mode, but it doesn't hurt to just switch it 115820fa2881Sschwarze * off in general since displays can't be nested. 115920fa2881Sschwarze */ 116020fa2881Sschwarze 116120fa2881Sschwarze if (MDOC_BODY == mdoc->last->type) 116220fa2881Sschwarze mdoc->flags &= ~MDOC_LITERAL; 116320fa2881Sschwarze 116420fa2881Sschwarze return(1); 116520fa2881Sschwarze } 116620fa2881Sschwarze 116720fa2881Sschwarze static int 116820fa2881Sschwarze post_defaults(POST_ARGS) 116920fa2881Sschwarze { 117020fa2881Sschwarze struct mdoc_node *nn; 117120fa2881Sschwarze 117220fa2881Sschwarze /* 117320fa2881Sschwarze * The `Ar' defaults to "file ..." if no value is provided as an 117420fa2881Sschwarze * argument; the `Mt' and `Pa' macros use "~"; the `Li' just 117520fa2881Sschwarze * gets an empty string. 117620fa2881Sschwarze */ 1177f73abda9Skristaps 1178f73abda9Skristaps if (mdoc->last->child) 1179f73abda9Skristaps return(1); 118020fa2881Sschwarze 118120fa2881Sschwarze nn = mdoc->last; 118220fa2881Sschwarze mdoc->next = MDOC_NEXT_CHILD; 118320fa2881Sschwarze 118420fa2881Sschwarze switch (nn->tok) { 118520fa2881Sschwarze case (MDOC_Ar): 118620fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file")) 118720fa2881Sschwarze return(0); 118820fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "...")) 118920fa2881Sschwarze return(0); 119020fa2881Sschwarze break; 119120fa2881Sschwarze case (MDOC_At): 119220fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T")) 119320fa2881Sschwarze return(0); 119420fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX")) 119520fa2881Sschwarze return(0); 119620fa2881Sschwarze break; 119720fa2881Sschwarze case (MDOC_Li): 119820fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "")) 119920fa2881Sschwarze return(0); 120020fa2881Sschwarze break; 120120fa2881Sschwarze case (MDOC_Pa): 120220fa2881Sschwarze /* FALLTHROUGH */ 120320fa2881Sschwarze case (MDOC_Mt): 120420fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~")) 120520fa2881Sschwarze return(0); 120620fa2881Sschwarze break; 120720fa2881Sschwarze default: 120820fa2881Sschwarze abort(); 120920fa2881Sschwarze /* NOTREACHED */ 1210f73abda9Skristaps } 1211f73abda9Skristaps 121220fa2881Sschwarze mdoc->last = nn; 121320fa2881Sschwarze return(1); 121420fa2881Sschwarze } 1215f73abda9Skristaps 1216f73abda9Skristaps static int 1217f73abda9Skristaps post_at(POST_ARGS) 1218f73abda9Skristaps { 121920fa2881Sschwarze const char *p, *q; 122020fa2881Sschwarze char *buf; 122120fa2881Sschwarze size_t sz; 122220fa2881Sschwarze 122320fa2881Sschwarze /* 122420fa2881Sschwarze * If we have a child, look it up in the standard keys. If a 122520fa2881Sschwarze * key exist, use that instead of the child; if it doesn't, 122620fa2881Sschwarze * prefix "AT&T UNIX " to the existing data. 122720fa2881Sschwarze */ 1228f73abda9Skristaps 1229f73abda9Skristaps if (NULL == mdoc->last->child) 1230f73abda9Skristaps return(1); 123120fa2881Sschwarze 12326e03d529Sschwarze assert(MDOC_TEXT == mdoc->last->child->type); 123320fa2881Sschwarze p = mdoc_a2att(mdoc->last->child->string); 123420fa2881Sschwarze 123520fa2881Sschwarze if (p) { 123620fa2881Sschwarze free(mdoc->last->child->string); 123720fa2881Sschwarze mdoc->last->child->string = mandoc_strdup(p); 123820fa2881Sschwarze } else { 123920fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT); 124020fa2881Sschwarze p = "AT&T UNIX "; 124120fa2881Sschwarze q = mdoc->last->child->string; 124220fa2881Sschwarze sz = strlen(p) + strlen(q) + 1; 124320fa2881Sschwarze buf = mandoc_malloc(sz); 124420fa2881Sschwarze strlcpy(buf, p, sz); 124520fa2881Sschwarze strlcat(buf, q, sz); 124620fa2881Sschwarze free(mdoc->last->child->string); 124720fa2881Sschwarze mdoc->last->child->string = buf; 1248f73abda9Skristaps } 1249f73abda9Skristaps 125020fa2881Sschwarze return(1); 125120fa2881Sschwarze } 1252f73abda9Skristaps 1253f73abda9Skristaps static int 1254f73abda9Skristaps post_an(POST_ARGS) 1255f73abda9Skristaps { 1256769ee804Sschwarze struct mdoc_node *np; 1257f73abda9Skristaps 1258769ee804Sschwarze np = mdoc->last; 1259e7a93ef3Sschwarze if (AUTH__NONE == np->norm->An.auth) { 1260e7a93ef3Sschwarze if (0 == np->child) 1261e7a93ef3Sschwarze check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0); 1262e7a93ef3Sschwarze } else if (np->child) 1263bb648afaSschwarze check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0); 126420fa2881Sschwarze 126520fa2881Sschwarze return(1); 1266f73abda9Skristaps } 1267f73abda9Skristaps 1268f73abda9Skristaps 1269f73abda9Skristaps static int 1270f73abda9Skristaps post_it(POST_ARGS) 1271f73abda9Skristaps { 127219a69263Sschwarze int i, cols; 12736093755cSschwarze enum mdoc_list lt; 1274f73abda9Skristaps struct mdoc_node *n, *c; 12756093755cSschwarze enum mandocerr er; 1276f73abda9Skristaps 1277f73abda9Skristaps if (MDOC_BLOCK != mdoc->last->type) 1278f73abda9Skristaps return(1); 1279f73abda9Skristaps 1280f73abda9Skristaps n = mdoc->last->parent->parent; 12818c62fbf5Sschwarze lt = n->norm->Bl.type; 12826093755cSschwarze 12836093755cSschwarze if (LIST__NONE == lt) { 12846e03d529Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE); 128520fa2881Sschwarze return(1); 12866e03d529Sschwarze } 1287f73abda9Skristaps 12886093755cSschwarze switch (lt) { 12896093755cSschwarze case (LIST_tag): 12906093755cSschwarze if (mdoc->last->head->child) 1291f73abda9Skristaps break; 12926093755cSschwarze /* FIXME: give this a dummy value. */ 129320fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS); 1294f73abda9Skristaps break; 12956093755cSschwarze case (LIST_hang): 1296f73abda9Skristaps /* FALLTHROUGH */ 12976093755cSschwarze case (LIST_ohang): 1298f73abda9Skristaps /* FALLTHROUGH */ 12996093755cSschwarze case (LIST_inset): 1300f73abda9Skristaps /* FALLTHROUGH */ 13016093755cSschwarze case (LIST_diag): 1302f73abda9Skristaps if (NULL == mdoc->last->head->child) 130320fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS); 1304f73abda9Skristaps break; 13056093755cSschwarze case (LIST_bullet): 1306f73abda9Skristaps /* FALLTHROUGH */ 13076093755cSschwarze case (LIST_dash): 1308f73abda9Skristaps /* FALLTHROUGH */ 13096093755cSschwarze case (LIST_enum): 1310f73abda9Skristaps /* FALLTHROUGH */ 13116093755cSschwarze case (LIST_hyphen): 13129f373962Sschwarze if (NULL == mdoc->last->body->child) 131320fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY); 1314f73abda9Skristaps /* FALLTHROUGH */ 13156093755cSschwarze case (LIST_item): 1316f73abda9Skristaps if (mdoc->last->head->child) 131720fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST); 1318f73abda9Skristaps break; 13196093755cSschwarze case (LIST_column): 13208c62fbf5Sschwarze cols = (int)n->norm->Bl.ncols; 13216093755cSschwarze 13226093755cSschwarze assert(NULL == mdoc->last->head->child); 13236093755cSschwarze 13246093755cSschwarze if (NULL == mdoc->last->body->child) 132520fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY); 13266093755cSschwarze 13276093755cSschwarze for (i = 0, c = mdoc->last->child; c; c = c->next) 13286093755cSschwarze if (MDOC_BODY == c->type) 1329f73abda9Skristaps i++; 133053292e81Sschwarze 13316093755cSschwarze if (i < cols) 13326093755cSschwarze er = MANDOCERR_ARGCOUNT; 13336093755cSschwarze else if (i == cols || i == cols + 1) 1334f73abda9Skristaps break; 13356093755cSschwarze else 13366093755cSschwarze er = MANDOCERR_SYNTARGCOUNT; 133753292e81Sschwarze 1338a35fc07aSschwarze mandoc_vmsg(er, mdoc->parse, mdoc->last->line, 1339a35fc07aSschwarze mdoc->last->pos, 13406e03d529Sschwarze "columns == %d (have %d)", cols, i); 134119a69263Sschwarze return(MANDOCERR_ARGCOUNT == er); 1342f73abda9Skristaps default: 1343f73abda9Skristaps break; 1344f73abda9Skristaps } 1345f73abda9Skristaps 1346f73abda9Skristaps return(1); 1347f73abda9Skristaps } 1348f73abda9Skristaps 134920fa2881Sschwarze static int 135020fa2881Sschwarze post_bl_block(POST_ARGS) 135120fa2881Sschwarze { 1352bb99f0faSschwarze struct mdoc_node *n, *ni, *nc; 135320fa2881Sschwarze 135420fa2881Sschwarze /* 135520fa2881Sschwarze * These are fairly complicated, so we've broken them into two 135620fa2881Sschwarze * functions. post_bl_block_tag() is called when a -tag is 135720fa2881Sschwarze * specified, but no -width (it must be guessed). The second 135820fa2881Sschwarze * when a -width is specified (macro indicators must be 135920fa2881Sschwarze * rewritten into real lengths). 136020fa2881Sschwarze */ 136120fa2881Sschwarze 136220fa2881Sschwarze n = mdoc->last; 136320fa2881Sschwarze 13648c62fbf5Sschwarze if (LIST_tag == n->norm->Bl.type && 13658c62fbf5Sschwarze NULL == n->norm->Bl.width) { 136620fa2881Sschwarze if ( ! post_bl_block_tag(mdoc)) 136720fa2881Sschwarze return(0); 1368bb99f0faSschwarze assert(n->norm->Bl.width); 13698c62fbf5Sschwarze } else if (NULL != n->norm->Bl.width) { 137020fa2881Sschwarze if ( ! post_bl_block_width(mdoc)) 137120fa2881Sschwarze return(0); 13728c62fbf5Sschwarze assert(n->norm->Bl.width); 1373bb99f0faSschwarze } 1374bb99f0faSschwarze 1375bb99f0faSschwarze for (ni = n->body->child; ni; ni = ni->next) { 1376bb99f0faSschwarze if (NULL == ni->body) 1377bb99f0faSschwarze continue; 1378bb99f0faSschwarze nc = ni->body->last; 1379bb99f0faSschwarze while (NULL != nc) { 1380bb99f0faSschwarze switch (nc->tok) { 1381bb99f0faSschwarze case (MDOC_Pp): 1382bb99f0faSschwarze /* FALLTHROUGH */ 1383bb99f0faSschwarze case (MDOC_Lp): 1384bb99f0faSschwarze /* FALLTHROUGH */ 1385bb99f0faSschwarze case (MDOC_br): 1386bb99f0faSschwarze break; 1387bb99f0faSschwarze default: 1388bb99f0faSschwarze nc = NULL; 1389bb99f0faSschwarze continue; 1390bb99f0faSschwarze } 1391bb99f0faSschwarze if (NULL == ni->next) { 1392bb99f0faSschwarze mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR); 1393bb99f0faSschwarze if ( ! mdoc_node_relink(mdoc, nc)) 1394bb99f0faSschwarze return(0); 1395bb99f0faSschwarze } else if (0 == n->norm->Bl.comp && 1396bb99f0faSschwarze LIST_column != n->norm->Bl.type) { 1397bb99f0faSschwarze mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR); 1398bb99f0faSschwarze mdoc_node_delete(mdoc, nc); 1399bb99f0faSschwarze } else 1400bb99f0faSschwarze break; 1401bb99f0faSschwarze nc = ni->body->last; 1402bb99f0faSschwarze } 1403bb99f0faSschwarze } 140420fa2881Sschwarze return(1); 140520fa2881Sschwarze } 140620fa2881Sschwarze 140720fa2881Sschwarze static int 140820fa2881Sschwarze post_bl_block_width(POST_ARGS) 140920fa2881Sschwarze { 141020fa2881Sschwarze size_t width; 141120fa2881Sschwarze int i; 141220fa2881Sschwarze enum mdoct tok; 141320fa2881Sschwarze struct mdoc_node *n; 141420fa2881Sschwarze char buf[NUMSIZ]; 141520fa2881Sschwarze 141620fa2881Sschwarze n = mdoc->last; 141720fa2881Sschwarze 141820fa2881Sschwarze /* 141920fa2881Sschwarze * Calculate the real width of a list from the -width string, 142020fa2881Sschwarze * which may contain a macro (with a known default width), a 142120fa2881Sschwarze * literal string, or a scaling width. 142220fa2881Sschwarze * 142320fa2881Sschwarze * If the value to -width is a macro, then we re-write it to be 142420fa2881Sschwarze * the macro's width as set in share/tmac/mdoc/doc-common. 142520fa2881Sschwarze */ 142620fa2881Sschwarze 14278c62fbf5Sschwarze if (0 == strcmp(n->norm->Bl.width, "Ds")) 142820fa2881Sschwarze width = 6; 14298c62fbf5Sschwarze else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width))) 143020fa2881Sschwarze return(1); 143119a69263Sschwarze else if (0 == (width = macro2len(tok))) { 143220fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH); 143320fa2881Sschwarze return(1); 143420fa2881Sschwarze } 143520fa2881Sschwarze 143620fa2881Sschwarze /* The value already exists: free and reallocate it. */ 143720fa2881Sschwarze 143820fa2881Sschwarze assert(n->args); 143920fa2881Sschwarze 144020fa2881Sschwarze for (i = 0; i < (int)n->args->argc; i++) 144120fa2881Sschwarze if (MDOC_Width == n->args->argv[i].arg) 144220fa2881Sschwarze break; 144320fa2881Sschwarze 144420fa2881Sschwarze assert(i < (int)n->args->argc); 144520fa2881Sschwarze 144604e980cbSschwarze snprintf(buf, NUMSIZ, "%un", (unsigned int)width); 144720fa2881Sschwarze free(n->args->argv[i].value[0]); 144820fa2881Sschwarze n->args->argv[i].value[0] = mandoc_strdup(buf); 144920fa2881Sschwarze 145020fa2881Sschwarze /* Set our width! */ 14518c62fbf5Sschwarze n->norm->Bl.width = n->args->argv[i].value[0]; 145220fa2881Sschwarze return(1); 145320fa2881Sschwarze } 145420fa2881Sschwarze 145520fa2881Sschwarze static int 145620fa2881Sschwarze post_bl_block_tag(POST_ARGS) 145720fa2881Sschwarze { 145820fa2881Sschwarze struct mdoc_node *n, *nn; 145920fa2881Sschwarze size_t sz, ssz; 146020fa2881Sschwarze int i; 146120fa2881Sschwarze char buf[NUMSIZ]; 146220fa2881Sschwarze 146320fa2881Sschwarze /* 146420fa2881Sschwarze * Calculate the -width for a `Bl -tag' list if it hasn't been 146520fa2881Sschwarze * provided. Uses the first head macro. NOTE AGAIN: this is 146620fa2881Sschwarze * ONLY if the -width argument has NOT been provided. See 146720fa2881Sschwarze * post_bl_block_width() for converting the -width string. 146820fa2881Sschwarze */ 146920fa2881Sschwarze 147020fa2881Sschwarze sz = 10; 147120fa2881Sschwarze n = mdoc->last; 147220fa2881Sschwarze 147320fa2881Sschwarze for (nn = n->body->child; nn; nn = nn->next) { 147420fa2881Sschwarze if (MDOC_It != nn->tok) 147520fa2881Sschwarze continue; 147620fa2881Sschwarze 147720fa2881Sschwarze assert(MDOC_BLOCK == nn->type); 147820fa2881Sschwarze nn = nn->head->child; 147920fa2881Sschwarze 148020fa2881Sschwarze if (nn == NULL) 148120fa2881Sschwarze break; 148220fa2881Sschwarze 148320fa2881Sschwarze if (MDOC_TEXT == nn->type) { 148420fa2881Sschwarze sz = strlen(nn->string) + 1; 148520fa2881Sschwarze break; 148620fa2881Sschwarze } 148720fa2881Sschwarze 148819a69263Sschwarze if (0 != (ssz = macro2len(nn->tok))) 148920fa2881Sschwarze sz = ssz; 149020fa2881Sschwarze 149120fa2881Sschwarze break; 149220fa2881Sschwarze } 149320fa2881Sschwarze 149420fa2881Sschwarze /* Defaults to ten ens. */ 149520fa2881Sschwarze 149604e980cbSschwarze snprintf(buf, NUMSIZ, "%un", (unsigned int)sz); 149720fa2881Sschwarze 149820fa2881Sschwarze /* 149920fa2881Sschwarze * We have to dynamically add this to the macro's argument list. 150020fa2881Sschwarze * We're guaranteed that a MDOC_Width doesn't already exist. 150120fa2881Sschwarze */ 150220fa2881Sschwarze 150320fa2881Sschwarze assert(n->args); 150420fa2881Sschwarze i = (int)(n->args->argc)++; 150520fa2881Sschwarze 150620fa2881Sschwarze n->args->argv = mandoc_realloc(n->args->argv, 150720fa2881Sschwarze n->args->argc * sizeof(struct mdoc_argv)); 150820fa2881Sschwarze 150920fa2881Sschwarze n->args->argv[i].arg = MDOC_Width; 151020fa2881Sschwarze n->args->argv[i].line = n->line; 151120fa2881Sschwarze n->args->argv[i].pos = n->pos; 151220fa2881Sschwarze n->args->argv[i].sz = 1; 151320fa2881Sschwarze n->args->argv[i].value = mandoc_malloc(sizeof(char *)); 151420fa2881Sschwarze n->args->argv[i].value[0] = mandoc_strdup(buf); 151520fa2881Sschwarze 151620fa2881Sschwarze /* Set our width! */ 15178c62fbf5Sschwarze n->norm->Bl.width = n->args->argv[i].value[0]; 151820fa2881Sschwarze return(1); 151920fa2881Sschwarze } 152020fa2881Sschwarze 1521f73abda9Skristaps 1522f73abda9Skristaps static int 1523395185ccSschwarze post_bl_head(POST_ARGS) 1524395185ccSschwarze { 152520fa2881Sschwarze struct mdoc_node *np, *nn, *nnp; 152620fa2881Sschwarze int i, j; 1527395185ccSschwarze 15288c62fbf5Sschwarze if (LIST_column != mdoc->last->norm->Bl.type) 152920fa2881Sschwarze /* FIXME: this should be ERROR class... */ 153020fa2881Sschwarze return(hwarn_eq0(mdoc)); 1531395185ccSschwarze 153220fa2881Sschwarze /* 153320fa2881Sschwarze * Convert old-style lists, where the column width specifiers 153420fa2881Sschwarze * trail as macro parameters, to the new-style ("normal-form") 153520fa2881Sschwarze * lists where they're argument values following -column. 153620fa2881Sschwarze */ 153720fa2881Sschwarze 153820fa2881Sschwarze /* First, disallow both types and allow normal-form. */ 153920fa2881Sschwarze 154020fa2881Sschwarze /* 154120fa2881Sschwarze * TODO: technically, we can accept both and just merge the two 154220fa2881Sschwarze * lists, but I'll leave that for another day. 154320fa2881Sschwarze */ 154420fa2881Sschwarze 15458c62fbf5Sschwarze if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) { 154620fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS); 15476093755cSschwarze return(0); 154820fa2881Sschwarze } else if (NULL == mdoc->last->child) 154920fa2881Sschwarze return(1); 155020fa2881Sschwarze 155120fa2881Sschwarze np = mdoc->last->parent; 155220fa2881Sschwarze assert(np->args); 155320fa2881Sschwarze 155420fa2881Sschwarze for (j = 0; j < (int)np->args->argc; j++) 155520fa2881Sschwarze if (MDOC_Column == np->args->argv[j].arg) 155620fa2881Sschwarze break; 155720fa2881Sschwarze 155820fa2881Sschwarze assert(j < (int)np->args->argc); 155920fa2881Sschwarze assert(0 == np->args->argv[j].sz); 156020fa2881Sschwarze 156120fa2881Sschwarze /* 1562a5e11edeSschwarze * Accommodate for new-style groff column syntax. Shuffle the 156320fa2881Sschwarze * child nodes, all of which must be TEXT, as arguments for the 156420fa2881Sschwarze * column field. Then, delete the head children. 156520fa2881Sschwarze */ 156620fa2881Sschwarze 156720fa2881Sschwarze np->args->argv[j].sz = (size_t)mdoc->last->nchild; 156820fa2881Sschwarze np->args->argv[j].value = mandoc_malloc 156920fa2881Sschwarze ((size_t)mdoc->last->nchild * sizeof(char *)); 157020fa2881Sschwarze 15718c62fbf5Sschwarze mdoc->last->norm->Bl.ncols = np->args->argv[j].sz; 157204e980cbSschwarze mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value; 157320fa2881Sschwarze 157420fa2881Sschwarze for (i = 0, nn = mdoc->last->child; nn; i++) { 157520fa2881Sschwarze np->args->argv[j].value[i] = nn->string; 157620fa2881Sschwarze nn->string = NULL; 157720fa2881Sschwarze nnp = nn; 157820fa2881Sschwarze nn = nn->next; 157920fa2881Sschwarze mdoc_node_delete(NULL, nnp); 1580395185ccSschwarze } 158120fa2881Sschwarze 158220fa2881Sschwarze mdoc->last->nchild = 0; 158320fa2881Sschwarze mdoc->last->child = NULL; 158420fa2881Sschwarze 15856093755cSschwarze return(1); 1586b16e7ddfSschwarze } 1587b16e7ddfSschwarze 1588395185ccSschwarze static int 1589f73abda9Skristaps post_bl(POST_ARGS) 1590f73abda9Skristaps { 1591f73abda9Skristaps struct mdoc_node *n; 1592f73abda9Skristaps 1593395185ccSschwarze if (MDOC_HEAD == mdoc->last->type) 1594395185ccSschwarze return(post_bl_head(mdoc)); 159520fa2881Sschwarze if (MDOC_BLOCK == mdoc->last->type) 159620fa2881Sschwarze return(post_bl_block(mdoc)); 1597f73abda9Skristaps if (MDOC_BODY != mdoc->last->type) 1598f73abda9Skristaps return(1); 1599f73abda9Skristaps 1600f73abda9Skristaps for (n = mdoc->last->child; n; n = n->next) { 1601f6127a73Sschwarze switch (n->tok) { 1602f6127a73Sschwarze case (MDOC_Lp): 1603f6127a73Sschwarze /* FALLTHROUGH */ 1604f6127a73Sschwarze case (MDOC_Pp): 1605f6127a73Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_CHILD); 1606f6127a73Sschwarze /* FALLTHROUGH */ 1607f6127a73Sschwarze case (MDOC_It): 1608f6127a73Sschwarze /* FALLTHROUGH */ 1609f6127a73Sschwarze case (MDOC_Sm): 161039bb0097Sschwarze continue; 1611f6127a73Sschwarze default: 1612f6127a73Sschwarze break; 1613f6127a73Sschwarze } 1614f6127a73Sschwarze 16156e03d529Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD); 16166e03d529Sschwarze return(0); 1617f73abda9Skristaps } 1618f73abda9Skristaps 1619f73abda9Skristaps return(1); 1620f73abda9Skristaps } 1621f73abda9Skristaps 1622f73abda9Skristaps static int 1623f73abda9Skristaps ebool(struct mdoc *mdoc) 1624f73abda9Skristaps { 1625f73abda9Skristaps 1626bb648afaSschwarze if (NULL == mdoc->last->child) { 1627bb648afaSschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY); 1628bb648afaSschwarze mdoc_node_delete(mdoc, mdoc->last); 1629f73abda9Skristaps return(1); 1630bb648afaSschwarze } 1631bb648afaSschwarze check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); 1632f73abda9Skristaps 163320fa2881Sschwarze assert(MDOC_TEXT == mdoc->last->child->type); 163420fa2881Sschwarze 163520fa2881Sschwarze if (0 == strcmp(mdoc->last->child->string, "on")) 163620fa2881Sschwarze return(1); 163720fa2881Sschwarze if (0 == strcmp(mdoc->last->child->string, "off")) 163820fa2881Sschwarze return(1); 163920fa2881Sschwarze 164020fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL); 164120fa2881Sschwarze return(1); 164220fa2881Sschwarze } 1643f73abda9Skristaps 1644f73abda9Skristaps static int 1645f73abda9Skristaps post_root(POST_ARGS) 1646f73abda9Skristaps { 164720fa2881Sschwarze int erc; 164820fa2881Sschwarze struct mdoc_node *n; 1649f73abda9Skristaps 165020fa2881Sschwarze erc = 0; 165120fa2881Sschwarze 165220fa2881Sschwarze /* Check that we have a finished prologue. */ 165320fa2881Sschwarze 165420fa2881Sschwarze if ( ! (MDOC_PBODY & mdoc->flags)) { 165520fa2881Sschwarze erc++; 16566e03d529Sschwarze mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG); 1657f73abda9Skristaps } 1658f73abda9Skristaps 165920fa2881Sschwarze n = mdoc->first; 166020fa2881Sschwarze assert(n); 166120fa2881Sschwarze 166220fa2881Sschwarze /* Check that we begin with a proper `Sh'. */ 166320fa2881Sschwarze 166420fa2881Sschwarze if (NULL == n->child) { 166520fa2881Sschwarze erc++; 166620fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY); 166720fa2881Sschwarze } else if (MDOC_BLOCK != n->child->type || 166820fa2881Sschwarze MDOC_Sh != n->child->tok) { 166920fa2881Sschwarze erc++; 167020fa2881Sschwarze /* Can this be lifted? See rxdebug.1 for example. */ 167120fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY); 167220fa2881Sschwarze } 167320fa2881Sschwarze 167420fa2881Sschwarze return(erc ? 0 : 1); 167520fa2881Sschwarze } 1676f73abda9Skristaps 1677f73abda9Skristaps static int 1678f73abda9Skristaps post_st(POST_ARGS) 1679f73abda9Skristaps { 1680bb648afaSschwarze struct mdoc_node *ch; 168120fa2881Sschwarze const char *p; 1682f73abda9Skristaps 1683bb648afaSschwarze if (NULL == (ch = mdoc->last->child)) { 1684bb648afaSschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY); 1685bb648afaSschwarze mdoc_node_delete(mdoc, mdoc->last); 1686bb648afaSschwarze return(1); 1687bb648afaSschwarze } 168820fa2881Sschwarze 1689bb648afaSschwarze assert(MDOC_TEXT == ch->type); 169020fa2881Sschwarze 1691bb648afaSschwarze if (NULL == (p = mdoc_a2st(ch->string))) { 169220fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD); 169320fa2881Sschwarze mdoc_node_delete(mdoc, mdoc->last); 169420fa2881Sschwarze } else { 1695bb648afaSschwarze free(ch->string); 1696bb648afaSschwarze ch->string = mandoc_strdup(p); 1697f73abda9Skristaps } 1698f73abda9Skristaps 169920fa2881Sschwarze return(1); 170020fa2881Sschwarze } 1701f73abda9Skristaps 1702f73abda9Skristaps static int 1703011fe33bSschwarze post_rs(POST_ARGS) 1704011fe33bSschwarze { 170520fa2881Sschwarze struct mdoc_node *nn, *next, *prev; 170620fa2881Sschwarze int i, j; 1707011fe33bSschwarze 1708bb648afaSschwarze switch (mdoc->last->type) { 1709bb648afaSschwarze case (MDOC_HEAD): 1710bb648afaSschwarze check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0); 1711011fe33bSschwarze return(1); 1712bb648afaSschwarze case (MDOC_BODY): 1713bb648afaSschwarze if (mdoc->last->child) 1714bb648afaSschwarze break; 1715bb648afaSschwarze check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0); 1716bb648afaSschwarze return(1); 1717bb648afaSschwarze default: 1718bb648afaSschwarze return(1); 1719bb648afaSschwarze } 1720011fe33bSschwarze 172120fa2881Sschwarze /* 172220fa2881Sschwarze * Make sure only certain types of nodes are allowed within the 172320fa2881Sschwarze * the `Rs' body. Delete offending nodes and raise a warning. 172420fa2881Sschwarze * Do this before re-ordering for the sake of clarity. 172520fa2881Sschwarze */ 172620fa2881Sschwarze 172720fa2881Sschwarze next = NULL; 172820fa2881Sschwarze for (nn = mdoc->last->child; nn; nn = next) { 172920fa2881Sschwarze for (i = 0; i < RSORD_MAX; i++) 173020fa2881Sschwarze if (nn->tok == rsord[i]) 1731011fe33bSschwarze break; 173220fa2881Sschwarze 173320fa2881Sschwarze if (i < RSORD_MAX) { 17345d273f35Sschwarze if (MDOC__J == rsord[i] || MDOC__B == rsord[i]) 17355d273f35Sschwarze mdoc->last->norm->Rs.quote_T++; 173620fa2881Sschwarze next = nn->next; 173720fa2881Sschwarze continue; 173820fa2881Sschwarze } 173920fa2881Sschwarze 174020fa2881Sschwarze next = nn->next; 174120fa2881Sschwarze mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD); 174220fa2881Sschwarze mdoc_node_delete(mdoc, nn); 174320fa2881Sschwarze } 174420fa2881Sschwarze 174520fa2881Sschwarze /* 1746c6176538Sschwarze * Nothing to sort if only invalid nodes were found 1747c6176538Sschwarze * inside the `Rs' body. 1748c6176538Sschwarze */ 1749c6176538Sschwarze 1750c6176538Sschwarze if (NULL == mdoc->last->child) 1751c6176538Sschwarze return(1); 1752c6176538Sschwarze 1753c6176538Sschwarze /* 175420fa2881Sschwarze * The full `Rs' block needs special handling to order the 175520fa2881Sschwarze * sub-elements according to `rsord'. Pick through each element 175620fa2881Sschwarze * and correctly order it. This is a insertion sort. 175720fa2881Sschwarze */ 175820fa2881Sschwarze 175920fa2881Sschwarze next = NULL; 176020fa2881Sschwarze for (nn = mdoc->last->child->next; nn; nn = next) { 176120fa2881Sschwarze /* Determine order of `nn'. */ 176220fa2881Sschwarze for (i = 0; i < RSORD_MAX; i++) 176320fa2881Sschwarze if (rsord[i] == nn->tok) 176420fa2881Sschwarze break; 176520fa2881Sschwarze 176620fa2881Sschwarze /* 176720fa2881Sschwarze * Remove `nn' from the chain. This somewhat 176820fa2881Sschwarze * repeats mdoc_node_unlink(), but since we're 176920fa2881Sschwarze * just re-ordering, there's no need for the 177020fa2881Sschwarze * full unlink process. 177120fa2881Sschwarze */ 177220fa2881Sschwarze 177320fa2881Sschwarze if (NULL != (next = nn->next)) 177420fa2881Sschwarze next->prev = nn->prev; 177520fa2881Sschwarze 177620fa2881Sschwarze if (NULL != (prev = nn->prev)) 177720fa2881Sschwarze prev->next = nn->next; 177820fa2881Sschwarze 177920fa2881Sschwarze nn->prev = nn->next = NULL; 178020fa2881Sschwarze 178120fa2881Sschwarze /* 178220fa2881Sschwarze * Scan back until we reach a node that's 178320fa2881Sschwarze * ordered before `nn'. 178420fa2881Sschwarze */ 178520fa2881Sschwarze 178620fa2881Sschwarze for ( ; prev ; prev = prev->prev) { 178720fa2881Sschwarze /* Determine order of `prev'. */ 178820fa2881Sschwarze for (j = 0; j < RSORD_MAX; j++) 178920fa2881Sschwarze if (rsord[j] == prev->tok) 179020fa2881Sschwarze break; 179120fa2881Sschwarze 179220fa2881Sschwarze if (j <= i) 179320fa2881Sschwarze break; 179420fa2881Sschwarze } 179520fa2881Sschwarze 179620fa2881Sschwarze /* 179720fa2881Sschwarze * Set `nn' back into its correct place in front 179820fa2881Sschwarze * of the `prev' node. 179920fa2881Sschwarze */ 180020fa2881Sschwarze 180120fa2881Sschwarze nn->prev = prev; 180220fa2881Sschwarze 180320fa2881Sschwarze if (prev) { 180420fa2881Sschwarze if (prev->next) 180520fa2881Sschwarze prev->next->prev = nn; 180620fa2881Sschwarze nn->next = prev->next; 180720fa2881Sschwarze prev->next = nn; 180820fa2881Sschwarze } else { 180920fa2881Sschwarze mdoc->last->child->prev = nn; 181020fa2881Sschwarze nn->next = mdoc->last->child; 181120fa2881Sschwarze mdoc->last->child = nn; 181220fa2881Sschwarze } 1813011fe33bSschwarze } 1814011fe33bSschwarze 1815011fe33bSschwarze return(1); 1816011fe33bSschwarze } 1817011fe33bSschwarze 1818011fe33bSschwarze static int 1819af216717Sschwarze post_ns(POST_ARGS) 1820af216717Sschwarze { 1821af216717Sschwarze 1822af216717Sschwarze if (MDOC_LINE & mdoc->last->flags) 1823af216717Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS); 1824af216717Sschwarze return(1); 1825af216717Sschwarze } 1826af216717Sschwarze 1827af216717Sschwarze static int 1828f73abda9Skristaps post_sh(POST_ARGS) 1829f73abda9Skristaps { 1830f73abda9Skristaps 1831f73abda9Skristaps if (MDOC_HEAD == mdoc->last->type) 1832f73abda9Skristaps return(post_sh_head(mdoc)); 1833f73abda9Skristaps if (MDOC_BODY == mdoc->last->type) 1834f73abda9Skristaps return(post_sh_body(mdoc)); 1835f73abda9Skristaps 1836f73abda9Skristaps return(1); 1837f73abda9Skristaps } 1838f73abda9Skristaps 1839f73abda9Skristaps static int 1840f73abda9Skristaps post_sh_body(POST_ARGS) 1841f73abda9Skristaps { 1842f73abda9Skristaps struct mdoc_node *n; 1843f73abda9Skristaps 1844f8c9d6f2Sschwarze if (SEC_NAME != mdoc->lastsec) 1845f73abda9Skristaps return(1); 1846f73abda9Skristaps 1847f73abda9Skristaps /* 1848f73abda9Skristaps * Warn if the NAME section doesn't contain the `Nm' and `Nd' 1849f73abda9Skristaps * macros (can have multiple `Nm' and one `Nd'). Note that the 1850f73abda9Skristaps * children of the BODY declaration can also be "text". 1851f73abda9Skristaps */ 1852f73abda9Skristaps 185320fa2881Sschwarze if (NULL == (n = mdoc->last->child)) { 185420fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); 185520fa2881Sschwarze return(1); 185620fa2881Sschwarze } 1857f73abda9Skristaps 1858f73abda9Skristaps for ( ; n && n->next; n = n->next) { 1859f73abda9Skristaps if (MDOC_ELEM == n->type && MDOC_Nm == n->tok) 1860f73abda9Skristaps continue; 1861f73abda9Skristaps if (MDOC_TEXT == n->type) 1862f73abda9Skristaps continue; 186320fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); 1864f73abda9Skristaps } 1865f73abda9Skristaps 186649d529b5Sschwarze assert(n); 18674602e85cSschwarze if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok) 1868f73abda9Skristaps return(1); 1869f73abda9Skristaps 187020fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); 187120fa2881Sschwarze return(1); 187220fa2881Sschwarze } 1873f73abda9Skristaps 1874f73abda9Skristaps static int 1875f73abda9Skristaps post_sh_head(POST_ARGS) 1876f73abda9Skristaps { 18773216dddfSschwarze char buf[BUFSIZ]; 1878a2cff342Sschwarze struct mdoc_node *n; 1879f73abda9Skristaps enum mdoc_sec sec; 188004e980cbSschwarze int c; 1881f73abda9Skristaps 1882f73abda9Skristaps /* 1883f73abda9Skristaps * Process a new section. Sections are either "named" or 188420fa2881Sschwarze * "custom". Custom sections are user-defined, while named ones 188520fa2881Sschwarze * follow a conventional order and may only appear in certain 188620fa2881Sschwarze * manual sections. 1887f73abda9Skristaps */ 1888f73abda9Skristaps 188904e980cbSschwarze sec = SEC_CUSTOM; 189004e980cbSschwarze buf[0] = '\0'; 189104e980cbSschwarze if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) { 189204e980cbSschwarze mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM); 18936e03d529Sschwarze return(0); 189404e980cbSschwarze } else if (1 == c) 189519a69263Sschwarze sec = a2sec(buf); 1896f73abda9Skristaps 189720fa2881Sschwarze /* The NAME should be first. */ 1898f73abda9Skristaps 1899fccfce9dSschwarze if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed) 190020fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST); 190120fa2881Sschwarze 190220fa2881Sschwarze /* The SYNOPSIS gets special attention in other areas. */ 190320fa2881Sschwarze 190420fa2881Sschwarze if (SEC_SYNOPSIS == sec) 190520fa2881Sschwarze mdoc->flags |= MDOC_SYNOPSIS; 190620fa2881Sschwarze else 190720fa2881Sschwarze mdoc->flags &= ~MDOC_SYNOPSIS; 190820fa2881Sschwarze 190920fa2881Sschwarze /* Mark our last section. */ 191020fa2881Sschwarze 191120fa2881Sschwarze mdoc->lastsec = sec; 19121eccdf28Sschwarze 19131eccdf28Sschwarze /* 19141eccdf28Sschwarze * Set the section attribute for the current HEAD, for its 19151eccdf28Sschwarze * parent BLOCK, and for the HEAD children; the latter can 19161eccdf28Sschwarze * only be TEXT nodes, so no recursion is needed. 19171eccdf28Sschwarze * For other blocks and elements, including .Sh BODY, this is 19181eccdf28Sschwarze * done when allocating the node data structures, but for .Sh 19191eccdf28Sschwarze * BLOCK and HEAD, the section is still unknown at that time. 19201eccdf28Sschwarze */ 19211eccdf28Sschwarze 1922a2cff342Sschwarze mdoc->last->parent->sec = sec; 1923a2cff342Sschwarze mdoc->last->sec = sec; 1924a2cff342Sschwarze for (n = mdoc->last->child; n; n = n->next) 1925a2cff342Sschwarze n->sec = sec; 192620fa2881Sschwarze 192720fa2881Sschwarze /* We don't care about custom sections after this. */ 1928fccfce9dSschwarze 1929f73abda9Skristaps if (SEC_CUSTOM == sec) 1930f73abda9Skristaps return(1); 1931fccfce9dSschwarze 19326be99f77Sschwarze /* 193320fa2881Sschwarze * Check whether our non-custom section is being repeated or is 193420fa2881Sschwarze * out of order. 19356be99f77Sschwarze */ 1936f73abda9Skristaps 193720fa2881Sschwarze if (sec == mdoc->lastnamed) 193820fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP); 193920fa2881Sschwarze 194020fa2881Sschwarze if (sec < mdoc->lastnamed) 194120fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO); 194220fa2881Sschwarze 194320fa2881Sschwarze /* Mark the last named section. */ 194420fa2881Sschwarze 194520fa2881Sschwarze mdoc->lastnamed = sec; 194620fa2881Sschwarze 194720fa2881Sschwarze /* Check particular section/manual conventions. */ 194820fa2881Sschwarze 194992c0ca7fSschwarze assert(mdoc->meta.msec); 195020fa2881Sschwarze 195120fa2881Sschwarze switch (sec) { 195220fa2881Sschwarze case (SEC_RETURN_VALUES): 195320fa2881Sschwarze /* FALLTHROUGH */ 195420fa2881Sschwarze case (SEC_ERRORS): 195520fa2881Sschwarze /* FALLTHROUGH */ 195620fa2881Sschwarze case (SEC_LIBRARY): 195792c0ca7fSschwarze if (*mdoc->meta.msec == '2') 1958f73abda9Skristaps break; 195992c0ca7fSschwarze if (*mdoc->meta.msec == '3') 196092c0ca7fSschwarze break; 196192c0ca7fSschwarze if (*mdoc->meta.msec == '9') 196292c0ca7fSschwarze break; 196320fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC); 196420fa2881Sschwarze break; 1965f73abda9Skristaps default: 1966f73abda9Skristaps break; 1967f73abda9Skristaps } 1968f73abda9Skristaps 1969f73abda9Skristaps return(1); 1970f73abda9Skristaps } 1971d39b9a9cSschwarze 197220fa2881Sschwarze static int 1973f6127a73Sschwarze post_ignpar(POST_ARGS) 1974f6127a73Sschwarze { 1975f6127a73Sschwarze struct mdoc_node *np; 1976f6127a73Sschwarze 1977f6127a73Sschwarze if (MDOC_BODY != mdoc->last->type) 1978f6127a73Sschwarze return(1); 1979f6127a73Sschwarze 1980f6127a73Sschwarze if (NULL != (np = mdoc->last->child)) 1981f6127a73Sschwarze if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { 1982f6127a73Sschwarze mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR); 1983f6127a73Sschwarze mdoc_node_delete(mdoc, np); 1984f6127a73Sschwarze } 1985f6127a73Sschwarze 1986f6127a73Sschwarze if (NULL != (np = mdoc->last->last)) 1987f6127a73Sschwarze if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { 1988f6127a73Sschwarze mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR); 1989f6127a73Sschwarze mdoc_node_delete(mdoc, np); 1990f6127a73Sschwarze } 1991f6127a73Sschwarze 1992f6127a73Sschwarze return(1); 1993f6127a73Sschwarze } 1994f6127a73Sschwarze 1995f6127a73Sschwarze static int 199620fa2881Sschwarze pre_par(PRE_ARGS) 1997d39b9a9cSschwarze { 1998d39b9a9cSschwarze 1999d39b9a9cSschwarze if (NULL == mdoc->last) 2000d39b9a9cSschwarze return(1); 2001f6127a73Sschwarze if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type) 2002f6127a73Sschwarze return(1); 2003d39b9a9cSschwarze 200420fa2881Sschwarze /* 200520fa2881Sschwarze * Don't allow prior `Lp' or `Pp' prior to a paragraph-type 200620fa2881Sschwarze * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. 200720fa2881Sschwarze */ 2008d39b9a9cSschwarze 2009e0dd4c9cSschwarze if (MDOC_Pp != mdoc->last->tok && 2010e0dd4c9cSschwarze MDOC_Lp != mdoc->last->tok && 2011e0dd4c9cSschwarze MDOC_br != mdoc->last->tok) 2012d39b9a9cSschwarze return(1); 20138c62fbf5Sschwarze if (MDOC_Bl == n->tok && n->norm->Bl.comp) 2014d39b9a9cSschwarze return(1); 20158c62fbf5Sschwarze if (MDOC_Bd == n->tok && n->norm->Bd.comp) 2016d39b9a9cSschwarze return(1); 20178c62fbf5Sschwarze if (MDOC_It == n->tok && n->parent->norm->Bl.comp) 2018f6127a73Sschwarze return(1); 2019d39b9a9cSschwarze 2020d39b9a9cSschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR); 2021d39b9a9cSschwarze mdoc_node_delete(mdoc, mdoc->last); 2022d39b9a9cSschwarze return(1); 2023d39b9a9cSschwarze } 202420fa2881Sschwarze 202520fa2881Sschwarze static int 2026e0dd4c9cSschwarze post_par(POST_ARGS) 2027e0dd4c9cSschwarze { 2028e0dd4c9cSschwarze 2029e0dd4c9cSschwarze if (MDOC_ELEM != mdoc->last->type && 2030e0dd4c9cSschwarze MDOC_BLOCK != mdoc->last->type) 2031e0dd4c9cSschwarze return(1); 2032e0dd4c9cSschwarze 2033e0dd4c9cSschwarze if (NULL == mdoc->last->prev) { 2034e0dd4c9cSschwarze if (MDOC_Sh != mdoc->last->parent->tok && 2035e0dd4c9cSschwarze MDOC_Ss != mdoc->last->parent->tok) 2036e0dd4c9cSschwarze return(1); 2037e0dd4c9cSschwarze } else { 2038e0dd4c9cSschwarze if (MDOC_Pp != mdoc->last->prev->tok && 2039e0dd4c9cSschwarze MDOC_Lp != mdoc->last->prev->tok && 2040e0dd4c9cSschwarze (MDOC_br != mdoc->last->tok || 2041e0dd4c9cSschwarze (MDOC_sp != mdoc->last->prev->tok && 2042e0dd4c9cSschwarze MDOC_br != mdoc->last->prev->tok))) 2043e0dd4c9cSschwarze return(1); 2044e0dd4c9cSschwarze } 2045e0dd4c9cSschwarze 2046e0dd4c9cSschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR); 2047e0dd4c9cSschwarze mdoc_node_delete(mdoc, mdoc->last); 2048e0dd4c9cSschwarze return(1); 2049e0dd4c9cSschwarze } 2050e0dd4c9cSschwarze 2051e0dd4c9cSschwarze static int 205220fa2881Sschwarze pre_literal(PRE_ARGS) 205320fa2881Sschwarze { 205420fa2881Sschwarze 205520fa2881Sschwarze if (MDOC_BODY != n->type) 205620fa2881Sschwarze return(1); 205720fa2881Sschwarze 205820fa2881Sschwarze /* 205920fa2881Sschwarze * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd 206020fa2881Sschwarze * -unfilled' macros set MDOC_LITERAL on entrance to the body. 206120fa2881Sschwarze */ 206220fa2881Sschwarze 206320fa2881Sschwarze switch (n->tok) { 206420fa2881Sschwarze case (MDOC_Dl): 206520fa2881Sschwarze mdoc->flags |= MDOC_LITERAL; 206620fa2881Sschwarze break; 206720fa2881Sschwarze case (MDOC_Bd): 20688c62fbf5Sschwarze if (DISP_literal == n->norm->Bd.type) 206920fa2881Sschwarze mdoc->flags |= MDOC_LITERAL; 20708c62fbf5Sschwarze if (DISP_unfilled == n->norm->Bd.type) 207120fa2881Sschwarze mdoc->flags |= MDOC_LITERAL; 207220fa2881Sschwarze break; 207320fa2881Sschwarze default: 207420fa2881Sschwarze abort(); 207520fa2881Sschwarze /* NOTREACHED */ 207620fa2881Sschwarze } 207720fa2881Sschwarze 207820fa2881Sschwarze return(1); 207920fa2881Sschwarze } 208020fa2881Sschwarze 208120fa2881Sschwarze static int 208220fa2881Sschwarze post_dd(POST_ARGS) 208320fa2881Sschwarze { 208420fa2881Sschwarze char buf[DATESIZE]; 208520fa2881Sschwarze struct mdoc_node *n; 208604e980cbSschwarze int c; 208720fa2881Sschwarze 2088b058e777Sschwarze if (mdoc->meta.date) 2089b058e777Sschwarze free(mdoc->meta.date); 209020fa2881Sschwarze 2091b058e777Sschwarze n = mdoc->last; 2092b058e777Sschwarze if (NULL == n->child || '\0' == n->child->string[0]) { 2093a35fc07aSschwarze mdoc->meta.date = mandoc_normdate 2094a35fc07aSschwarze (mdoc->parse, NULL, n->line, n->pos); 209520fa2881Sschwarze return(1); 209620fa2881Sschwarze } 209720fa2881Sschwarze 209804e980cbSschwarze buf[0] = '\0'; 209904e980cbSschwarze if (-1 == (c = concat(buf, n->child, DATESIZE))) { 210004e980cbSschwarze mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM); 210120fa2881Sschwarze return(0); 210204e980cbSschwarze } 210320fa2881Sschwarze 210404e980cbSschwarze assert(c); 2105a35fc07aSschwarze mdoc->meta.date = mandoc_normdate 2106a35fc07aSschwarze (mdoc->parse, buf, n->line, n->pos); 210720fa2881Sschwarze 210820fa2881Sschwarze return(1); 210920fa2881Sschwarze } 211020fa2881Sschwarze 211120fa2881Sschwarze static int 211220fa2881Sschwarze post_dt(POST_ARGS) 211320fa2881Sschwarze { 211420fa2881Sschwarze struct mdoc_node *nn, *n; 211520fa2881Sschwarze const char *cp; 211620fa2881Sschwarze char *p; 211720fa2881Sschwarze 211820fa2881Sschwarze n = mdoc->last; 211920fa2881Sschwarze 212020fa2881Sschwarze if (mdoc->meta.title) 212120fa2881Sschwarze free(mdoc->meta.title); 212220fa2881Sschwarze if (mdoc->meta.vol) 212320fa2881Sschwarze free(mdoc->meta.vol); 212420fa2881Sschwarze if (mdoc->meta.arch) 212520fa2881Sschwarze free(mdoc->meta.arch); 212620fa2881Sschwarze 212720fa2881Sschwarze mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL; 212820fa2881Sschwarze 212920fa2881Sschwarze /* First make all characters uppercase. */ 213020fa2881Sschwarze 213120fa2881Sschwarze if (NULL != (nn = n->child)) 213220fa2881Sschwarze for (p = nn->string; *p; p++) { 213304e980cbSschwarze if (toupper((unsigned char)*p) == *p) 213420fa2881Sschwarze continue; 213520fa2881Sschwarze 213620fa2881Sschwarze /* 213720fa2881Sschwarze * FIXME: don't be lazy: have this make all 213820fa2881Sschwarze * characters be uppercase and just warn once. 213920fa2881Sschwarze */ 214020fa2881Sschwarze mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE); 214120fa2881Sschwarze break; 214220fa2881Sschwarze } 214320fa2881Sschwarze 214420fa2881Sschwarze /* Handles: `.Dt' 214520fa2881Sschwarze * --> title = unknown, volume = local, msec = 0, arch = NULL 214620fa2881Sschwarze */ 214720fa2881Sschwarze 214820fa2881Sschwarze if (NULL == (nn = n->child)) { 214920fa2881Sschwarze /* XXX: make these macro values. */ 215020fa2881Sschwarze /* FIXME: warn about missing values. */ 215120fa2881Sschwarze mdoc->meta.title = mandoc_strdup("UNKNOWN"); 215220fa2881Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL"); 215320fa2881Sschwarze mdoc->meta.msec = mandoc_strdup("1"); 215420fa2881Sschwarze return(1); 215520fa2881Sschwarze } 215620fa2881Sschwarze 215720fa2881Sschwarze /* Handles: `.Dt TITLE' 215820fa2881Sschwarze * --> title = TITLE, volume = local, msec = 0, arch = NULL 215920fa2881Sschwarze */ 216020fa2881Sschwarze 216120fa2881Sschwarze mdoc->meta.title = mandoc_strdup 216220fa2881Sschwarze ('\0' == nn->string[0] ? "UNKNOWN" : nn->string); 216320fa2881Sschwarze 216420fa2881Sschwarze if (NULL == (nn = nn->next)) { 216520fa2881Sschwarze /* FIXME: warn about missing msec. */ 216620fa2881Sschwarze /* XXX: make this a macro value. */ 216720fa2881Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL"); 216820fa2881Sschwarze mdoc->meta.msec = mandoc_strdup("1"); 216920fa2881Sschwarze return(1); 217020fa2881Sschwarze } 217120fa2881Sschwarze 217220fa2881Sschwarze /* Handles: `.Dt TITLE SEC' 217320fa2881Sschwarze * --> title = TITLE, volume = SEC is msec ? 217420fa2881Sschwarze * format(msec) : SEC, 217520fa2881Sschwarze * msec = SEC is msec ? atoi(msec) : 0, 217620fa2881Sschwarze * arch = NULL 217720fa2881Sschwarze */ 217820fa2881Sschwarze 217988ec69e3Sschwarze cp = mandoc_a2msec(nn->string); 218020fa2881Sschwarze if (cp) { 218120fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(cp); 218220fa2881Sschwarze mdoc->meta.msec = mandoc_strdup(nn->string); 218320fa2881Sschwarze } else { 218420fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC); 218520fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(nn->string); 218620fa2881Sschwarze mdoc->meta.msec = mandoc_strdup(nn->string); 218720fa2881Sschwarze } 218820fa2881Sschwarze 218920fa2881Sschwarze if (NULL == (nn = nn->next)) 219020fa2881Sschwarze return(1); 219120fa2881Sschwarze 219220fa2881Sschwarze /* Handles: `.Dt TITLE SEC VOL' 219320fa2881Sschwarze * --> title = TITLE, volume = VOL is vol ? 219420fa2881Sschwarze * format(VOL) : 219520fa2881Sschwarze * VOL is arch ? format(arch) : 219620fa2881Sschwarze * VOL 219720fa2881Sschwarze */ 219820fa2881Sschwarze 219920fa2881Sschwarze cp = mdoc_a2vol(nn->string); 220020fa2881Sschwarze if (cp) { 220120fa2881Sschwarze free(mdoc->meta.vol); 220220fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(cp); 220320fa2881Sschwarze } else { 220420fa2881Sschwarze cp = mdoc_a2arch(nn->string); 220520fa2881Sschwarze if (NULL == cp) { 2206*55818fdcSschwarze mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH); 220720fa2881Sschwarze free(mdoc->meta.vol); 220820fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(nn->string); 220920fa2881Sschwarze } else 221020fa2881Sschwarze mdoc->meta.arch = mandoc_strdup(cp); 221120fa2881Sschwarze } 221220fa2881Sschwarze 221320fa2881Sschwarze /* Ignore any subsequent parameters... */ 221420fa2881Sschwarze /* FIXME: warn about subsequent parameters. */ 221520fa2881Sschwarze 221620fa2881Sschwarze return(1); 221720fa2881Sschwarze } 221820fa2881Sschwarze 221920fa2881Sschwarze static int 222020fa2881Sschwarze post_prol(POST_ARGS) 222120fa2881Sschwarze { 222220fa2881Sschwarze /* 222320fa2881Sschwarze * Remove prologue macros from the document after they're 222420fa2881Sschwarze * processed. The final document uses mdoc_meta for these 222520fa2881Sschwarze * values and discards the originals. 222620fa2881Sschwarze */ 222720fa2881Sschwarze 222820fa2881Sschwarze mdoc_node_delete(mdoc, mdoc->last); 222920fa2881Sschwarze if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os) 223020fa2881Sschwarze mdoc->flags |= MDOC_PBODY; 223120fa2881Sschwarze 223220fa2881Sschwarze return(1); 223320fa2881Sschwarze } 223420fa2881Sschwarze 223520fa2881Sschwarze static int 2236992063deSschwarze post_bx(POST_ARGS) 2237992063deSschwarze { 2238992063deSschwarze struct mdoc_node *n; 2239992063deSschwarze 2240992063deSschwarze /* 2241992063deSschwarze * Make `Bx's second argument always start with an uppercase 2242992063deSschwarze * letter. Groff checks if it's an "accepted" term, but we just 2243992063deSschwarze * uppercase blindly. 2244992063deSschwarze */ 2245992063deSschwarze 2246992063deSschwarze n = mdoc->last->child; 2247992063deSschwarze if (n && NULL != (n = n->next)) 224819a69263Sschwarze *n->string = (char)toupper 224919a69263Sschwarze ((unsigned char)*n->string); 2250992063deSschwarze 2251992063deSschwarze return(1); 2252992063deSschwarze } 2253992063deSschwarze 2254992063deSschwarze static int 225520fa2881Sschwarze post_os(POST_ARGS) 225620fa2881Sschwarze { 225720fa2881Sschwarze struct mdoc_node *n; 225820fa2881Sschwarze char buf[BUFSIZ]; 225904e980cbSschwarze int c; 226020fa2881Sschwarze #ifndef OSNAME 226120fa2881Sschwarze struct utsname utsname; 226220fa2881Sschwarze #endif 226320fa2881Sschwarze 226420fa2881Sschwarze n = mdoc->last; 226520fa2881Sschwarze 226620fa2881Sschwarze /* 2267353fa9ecSschwarze * Set the operating system by way of the `Os' macro. 2268353fa9ecSschwarze * The order of precedence is: 2269353fa9ecSschwarze * 1. the argument of the `Os' macro, unless empty 2270353fa9ecSschwarze * 2. the -Ios=foo command line argument, if provided 2271353fa9ecSschwarze * 3. -DOSNAME="\"foo\"", if provided during compilation 2272353fa9ecSschwarze * 4. "sysname release" from uname(3) 227320fa2881Sschwarze */ 227420fa2881Sschwarze 227520fa2881Sschwarze free(mdoc->meta.os); 227620fa2881Sschwarze 227704e980cbSschwarze buf[0] = '\0'; 227804e980cbSschwarze if (-1 == (c = concat(buf, n->child, BUFSIZ))) { 227904e980cbSschwarze mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM); 228020fa2881Sschwarze return(0); 228104e980cbSschwarze } 228204e980cbSschwarze 228304e980cbSschwarze assert(c); 228420fa2881Sschwarze 228520fa2881Sschwarze if ('\0' == buf[0]) { 2286353fa9ecSschwarze if (mdoc->defos) { 2287353fa9ecSschwarze mdoc->meta.os = mandoc_strdup(mdoc->defos); 2288353fa9ecSschwarze return(1); 2289353fa9ecSschwarze } 229020fa2881Sschwarze #ifdef OSNAME 229120fa2881Sschwarze if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) { 229220fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_MEM); 229320fa2881Sschwarze return(0); 229420fa2881Sschwarze } 229520fa2881Sschwarze #else /*!OSNAME */ 2296a35fc07aSschwarze if (-1 == uname(&utsname)) { 229720fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_UNAME); 229820fa2881Sschwarze mdoc->meta.os = mandoc_strdup("UNKNOWN"); 229920fa2881Sschwarze return(post_prol(mdoc)); 230020fa2881Sschwarze } 230120fa2881Sschwarze 230220fa2881Sschwarze if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) { 230320fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_MEM); 230420fa2881Sschwarze return(0); 230520fa2881Sschwarze } 230620fa2881Sschwarze if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) { 230720fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_MEM); 230820fa2881Sschwarze return(0); 230920fa2881Sschwarze } 231020fa2881Sschwarze if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) { 231120fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_MEM); 231220fa2881Sschwarze return(0); 231320fa2881Sschwarze } 231420fa2881Sschwarze #endif /*!OSNAME*/ 231520fa2881Sschwarze } 231620fa2881Sschwarze 231720fa2881Sschwarze mdoc->meta.os = mandoc_strdup(buf); 231820fa2881Sschwarze return(1); 231920fa2881Sschwarze } 232020fa2881Sschwarze 232120fa2881Sschwarze static int 232220fa2881Sschwarze post_std(POST_ARGS) 232320fa2881Sschwarze { 232420fa2881Sschwarze struct mdoc_node *nn, *n; 232520fa2881Sschwarze 232620fa2881Sschwarze n = mdoc->last; 232720fa2881Sschwarze 232820fa2881Sschwarze /* 232920fa2881Sschwarze * Macros accepting `-std' as an argument have the name of the 233020fa2881Sschwarze * current document (`Nm') filled in as the argument if it's not 233120fa2881Sschwarze * provided. 233220fa2881Sschwarze */ 233320fa2881Sschwarze 233420fa2881Sschwarze if (n->child) 233520fa2881Sschwarze return(1); 233620fa2881Sschwarze 233720fa2881Sschwarze if (NULL == mdoc->meta.name) 233820fa2881Sschwarze return(1); 233920fa2881Sschwarze 234020fa2881Sschwarze nn = n; 234120fa2881Sschwarze mdoc->next = MDOC_NEXT_CHILD; 234220fa2881Sschwarze 234320fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name)) 234420fa2881Sschwarze return(0); 234520fa2881Sschwarze 234620fa2881Sschwarze mdoc->last = nn; 234720fa2881Sschwarze return(1); 234820fa2881Sschwarze } 234920fa2881Sschwarze 235004e980cbSschwarze /* 235104e980cbSschwarze * Concatenate a node, stopping at the first non-text. 235204e980cbSschwarze * Concatenation is separated by a single whitespace. 235304e980cbSschwarze * Returns -1 on fatal (string overrun) error, 0 if child nodes were 235404e980cbSschwarze * encountered, 1 otherwise. 235504e980cbSschwarze */ 235620fa2881Sschwarze static int 235704e980cbSschwarze concat(char *p, const struct mdoc_node *n, size_t sz) 235820fa2881Sschwarze { 235920fa2881Sschwarze 236004e980cbSschwarze for ( ; NULL != n; n = n->next) { 236104e980cbSschwarze if (MDOC_TEXT != n->type) 236220fa2881Sschwarze return(0); 236304e980cbSschwarze if ('\0' != p[0] && strlcat(p, " ", sz) >= sz) 236404e980cbSschwarze return(-1); 236504e980cbSschwarze if (strlcat(p, n->string, sz) >= sz) 236604e980cbSschwarze return(-1); 236704e980cbSschwarze concat(p, n->child, sz); 236820fa2881Sschwarze } 236920fa2881Sschwarze 237020fa2881Sschwarze return(1); 237120fa2881Sschwarze } 237220fa2881Sschwarze 237319a69263Sschwarze static enum mdoc_sec 237419a69263Sschwarze a2sec(const char *p) 237519a69263Sschwarze { 237619a69263Sschwarze int i; 237719a69263Sschwarze 237819a69263Sschwarze for (i = 0; i < (int)SEC__MAX; i++) 237919a69263Sschwarze if (secnames[i] && 0 == strcmp(p, secnames[i])) 238019a69263Sschwarze return((enum mdoc_sec)i); 238119a69263Sschwarze 238219a69263Sschwarze return(SEC_CUSTOM); 238319a69263Sschwarze } 238419a69263Sschwarze 238519a69263Sschwarze static size_t 238619a69263Sschwarze macro2len(enum mdoct macro) 238719a69263Sschwarze { 238819a69263Sschwarze 238919a69263Sschwarze switch (macro) { 239019a69263Sschwarze case(MDOC_Ad): 239119a69263Sschwarze return(12); 239219a69263Sschwarze case(MDOC_Ao): 239319a69263Sschwarze return(12); 239419a69263Sschwarze case(MDOC_An): 239519a69263Sschwarze return(12); 239619a69263Sschwarze case(MDOC_Aq): 239719a69263Sschwarze return(12); 239819a69263Sschwarze case(MDOC_Ar): 239919a69263Sschwarze return(12); 240019a69263Sschwarze case(MDOC_Bo): 240119a69263Sschwarze return(12); 240219a69263Sschwarze case(MDOC_Bq): 240319a69263Sschwarze return(12); 240419a69263Sschwarze case(MDOC_Cd): 240519a69263Sschwarze return(12); 240619a69263Sschwarze case(MDOC_Cm): 240719a69263Sschwarze return(10); 240819a69263Sschwarze case(MDOC_Do): 240919a69263Sschwarze return(10); 241019a69263Sschwarze case(MDOC_Dq): 241119a69263Sschwarze return(12); 241219a69263Sschwarze case(MDOC_Dv): 241319a69263Sschwarze return(12); 241419a69263Sschwarze case(MDOC_Eo): 241519a69263Sschwarze return(12); 241619a69263Sschwarze case(MDOC_Em): 241719a69263Sschwarze return(10); 241819a69263Sschwarze case(MDOC_Er): 241919a69263Sschwarze return(17); 242019a69263Sschwarze case(MDOC_Ev): 242119a69263Sschwarze return(15); 242219a69263Sschwarze case(MDOC_Fa): 242319a69263Sschwarze return(12); 242419a69263Sschwarze case(MDOC_Fl): 242519a69263Sschwarze return(10); 242619a69263Sschwarze case(MDOC_Fo): 242719a69263Sschwarze return(16); 242819a69263Sschwarze case(MDOC_Fn): 242919a69263Sschwarze return(16); 243019a69263Sschwarze case(MDOC_Ic): 243119a69263Sschwarze return(10); 243219a69263Sschwarze case(MDOC_Li): 243319a69263Sschwarze return(16); 243419a69263Sschwarze case(MDOC_Ms): 243519a69263Sschwarze return(6); 243619a69263Sschwarze case(MDOC_Nm): 243719a69263Sschwarze return(10); 243819a69263Sschwarze case(MDOC_No): 243919a69263Sschwarze return(12); 244019a69263Sschwarze case(MDOC_Oo): 244119a69263Sschwarze return(10); 244219a69263Sschwarze case(MDOC_Op): 244319a69263Sschwarze return(14); 244419a69263Sschwarze case(MDOC_Pa): 244519a69263Sschwarze return(32); 244619a69263Sschwarze case(MDOC_Pf): 244719a69263Sschwarze return(12); 244819a69263Sschwarze case(MDOC_Po): 244919a69263Sschwarze return(12); 245019a69263Sschwarze case(MDOC_Pq): 245119a69263Sschwarze return(12); 245219a69263Sschwarze case(MDOC_Ql): 245319a69263Sschwarze return(16); 245419a69263Sschwarze case(MDOC_Qo): 245519a69263Sschwarze return(12); 245619a69263Sschwarze case(MDOC_So): 245719a69263Sschwarze return(12); 245819a69263Sschwarze case(MDOC_Sq): 245919a69263Sschwarze return(12); 246019a69263Sschwarze case(MDOC_Sy): 246119a69263Sschwarze return(6); 246219a69263Sschwarze case(MDOC_Sx): 246319a69263Sschwarze return(16); 246419a69263Sschwarze case(MDOC_Tn): 246519a69263Sschwarze return(10); 246619a69263Sschwarze case(MDOC_Va): 246719a69263Sschwarze return(12); 246819a69263Sschwarze case(MDOC_Vt): 246919a69263Sschwarze return(12); 247019a69263Sschwarze case(MDOC_Xr): 247119a69263Sschwarze return(10); 247219a69263Sschwarze default: 247319a69263Sschwarze break; 247419a69263Sschwarze }; 247519a69263Sschwarze return(0); 247619a69263Sschwarze } 2477