1*9530682eSschwarze /* $Id: mdoc_validate.c,v 1.142 2014/07/03 23:23:45 schwarze Exp $ */ 2f73abda9Skristaps /* 322972b14Sschwarze * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4231c7061Sschwarze * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org> 539c2a57eSschwarze * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> 6f73abda9Skristaps * 7f73abda9Skristaps * Permission to use, copy, modify, and distribute this software for any 8a6464425Sschwarze * purpose with or without fee is hereby granted, provided that the above 9a6464425Sschwarze * copyright notice and this permission notice appear in all copies. 10f73abda9Skristaps * 11a6464425Sschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12a6464425Sschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13a6464425Sschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14a6464425Sschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15a6464425Sschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16a6464425Sschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17a6464425Sschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18f73abda9Skristaps */ 1920fa2881Sschwarze #ifndef OSNAME 2020fa2881Sschwarze #include <sys/utsname.h> 2120fa2881Sschwarze #endif 2220fa2881Sschwarze 23f73abda9Skristaps #include <sys/types.h> 24f73abda9Skristaps 25f73abda9Skristaps #include <assert.h> 26f73abda9Skristaps #include <ctype.h> 27d92dc4efSschwarze #include <limits.h> 283216dddfSschwarze #include <stdio.h> 29f73abda9Skristaps #include <stdlib.h> 30f73abda9Skristaps #include <string.h> 3120fa2881Sschwarze #include <time.h> 32f73abda9Skristaps 33a35fc07aSschwarze #include "mdoc.h" 346e03d529Sschwarze #include "mandoc.h" 354f4f7972Sschwarze #include "mandoc_aux.h" 36f73abda9Skristaps #include "libmdoc.h" 37f6854d5cSschwarze #include "libmandoc.h" 38f73abda9Skristaps 39f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */ 40f73abda9Skristaps 416093755cSschwarze #define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n 42f73abda9Skristaps #define POST_ARGS struct mdoc *mdoc 43f73abda9Skristaps 447c2be9f8Sschwarze enum check_ineq { 457c2be9f8Sschwarze CHECK_LT, 467c2be9f8Sschwarze CHECK_GT, 477c2be9f8Sschwarze CHECK_EQ 487c2be9f8Sschwarze }; 497c2be9f8Sschwarze 507c2be9f8Sschwarze enum check_lvl { 517c2be9f8Sschwarze CHECK_WARN, 527c2be9f8Sschwarze CHECK_ERROR, 537c2be9f8Sschwarze }; 547c2be9f8Sschwarze 55f73abda9Skristaps typedef int (*v_pre)(PRE_ARGS); 56f73abda9Skristaps typedef int (*v_post)(POST_ARGS); 57f73abda9Skristaps 58f73abda9Skristaps struct valids { 59f73abda9Skristaps v_pre *pre; 60f73abda9Skristaps v_post *post; 61f73abda9Skristaps }; 62f73abda9Skristaps 637c2be9f8Sschwarze static int check_count(struct mdoc *, enum mdoc_type, 647c2be9f8Sschwarze enum check_lvl, enum check_ineq, int); 65dd94fa3aSschwarze static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type); 6620fa2881Sschwarze static void check_text(struct mdoc *, int, int, char *); 6720fa2881Sschwarze static void check_argv(struct mdoc *, 6831e23753Sschwarze struct mdoc_node *, struct mdoc_argv *); 6920fa2881Sschwarze static void check_args(struct mdoc *, struct mdoc_node *); 7019a69263Sschwarze static enum mdoc_sec a2sec(const char *); 7119a69263Sschwarze static size_t macro2len(enum mdoct); 7267c719adSschwarze 737c2be9f8Sschwarze static int ebool(POST_ARGS); 7467c719adSschwarze static int berr_ge1(POST_ARGS); 7567c719adSschwarze static int bwarn_ge1(POST_ARGS); 76da272f5eSschwarze static int ewarn_eq0(POST_ARGS); 77bb648afaSschwarze static int ewarn_eq1(POST_ARGS); 7867c719adSschwarze static int ewarn_ge1(POST_ARGS); 79bb648afaSschwarze static int ewarn_le1(POST_ARGS); 80b16e7ddfSschwarze static int hwarn_eq0(POST_ARGS); 817c2be9f8Sschwarze static int hwarn_eq1(POST_ARGS); 82bb648afaSschwarze static int hwarn_ge1(POST_ARGS); 8367c719adSschwarze static int hwarn_le1(POST_ARGS); 8467c719adSschwarze 8567c719adSschwarze static int post_an(POST_ARGS); 8667c719adSschwarze static int post_at(POST_ARGS); 8767c719adSschwarze static int post_bf(POST_ARGS); 8867c719adSschwarze static int post_bl(POST_ARGS); 8920fa2881Sschwarze static int post_bl_block(POST_ARGS); 9020fa2881Sschwarze static int post_bl_block_width(POST_ARGS); 9120fa2881Sschwarze static int post_bl_block_tag(POST_ARGS); 9267c719adSschwarze static int post_bl_head(POST_ARGS); 93992063deSschwarze static int post_bx(POST_ARGS); 944039b21cSschwarze static int post_defaults(POST_ARGS); 9520fa2881Sschwarze static int post_dd(POST_ARGS); 966093755cSschwarze static int post_dt(POST_ARGS); 97551cd4a8Sschwarze static int post_en(POST_ARGS); 98551cd4a8Sschwarze static int post_es(POST_ARGS); 9920fa2881Sschwarze static int post_eoln(POST_ARGS); 1004039b21cSschwarze static int post_hyph(POST_ARGS); 1014039b21cSschwarze static int post_ignpar(POST_ARGS); 10267c719adSschwarze static int post_it(POST_ARGS); 10367c719adSschwarze static int post_lb(POST_ARGS); 1044039b21cSschwarze static int post_literal(POST_ARGS); 10567c719adSschwarze static int post_nm(POST_ARGS); 106af216717Sschwarze static int post_ns(POST_ARGS); 10720fa2881Sschwarze static int post_os(POST_ARGS); 108e0dd4c9cSschwarze static int post_par(POST_ARGS); 10920fa2881Sschwarze static int post_prol(POST_ARGS); 11067c719adSschwarze static int post_root(POST_ARGS); 111011fe33bSschwarze static int post_rs(POST_ARGS); 11267c719adSschwarze static int post_sh(POST_ARGS); 11367c719adSschwarze static int post_sh_body(POST_ARGS); 11467c719adSschwarze static int post_sh_head(POST_ARGS); 11567c719adSschwarze static int post_st(POST_ARGS); 11620fa2881Sschwarze static int post_std(POST_ARGS); 1178521b0bcSschwarze static int post_vt(POST_ARGS); 118f73abda9Skristaps static int pre_an(PRE_ARGS); 119f73abda9Skristaps static int pre_bd(PRE_ARGS); 120f73abda9Skristaps static int pre_bl(PRE_ARGS); 121f73abda9Skristaps static int pre_dd(PRE_ARGS); 122f73abda9Skristaps static int pre_display(PRE_ARGS); 123f73abda9Skristaps static int pre_dt(PRE_ARGS); 124f73abda9Skristaps static int pre_it(PRE_ARGS); 12520fa2881Sschwarze static int pre_literal(PRE_ARGS); 126551cd4a8Sschwarze static int pre_obsolete(PRE_ARGS); 127f73abda9Skristaps static int pre_os(PRE_ARGS); 12820fa2881Sschwarze static int pre_par(PRE_ARGS); 129f73abda9Skristaps static int pre_sh(PRE_ARGS); 130f73abda9Skristaps static int pre_ss(PRE_ARGS); 13120fa2881Sschwarze static int pre_std(PRE_ARGS); 132f73abda9Skristaps 13367c719adSschwarze static v_post posts_an[] = { post_an, NULL }; 13420fa2881Sschwarze static v_post posts_at[] = { post_at, post_defaults, NULL }; 13520fa2881Sschwarze static v_post posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL }; 13667c719adSschwarze static v_post posts_bf[] = { hwarn_le1, post_bf, NULL }; 13720fa2881Sschwarze static v_post posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL }; 13867c719adSschwarze static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL }; 139992063deSschwarze static v_post posts_bx[] = { post_bx, NULL }; 140bb648afaSschwarze static v_post posts_bool[] = { ebool, NULL }; 141b31af00dSschwarze static v_post posts_eoln[] = { post_eoln, NULL }; 14220fa2881Sschwarze static v_post posts_defaults[] = { post_defaults, NULL }; 1434039b21cSschwarze static v_post posts_d1[] = { bwarn_ge1, post_hyph, NULL }; 144b058e777Sschwarze static v_post posts_dd[] = { post_dd, post_prol, NULL }; 145bb648afaSschwarze static v_post posts_dl[] = { post_literal, bwarn_ge1, NULL }; 14620fa2881Sschwarze static v_post posts_dt[] = { post_dt, post_prol, NULL }; 147551cd4a8Sschwarze static v_post posts_en[] = { post_en, NULL }; 148551cd4a8Sschwarze static v_post posts_es[] = { post_es, NULL }; 14967c719adSschwarze static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL }; 1504039b21cSschwarze static v_post posts_hyph[] = { post_hyph, NULL }; 1514039b21cSschwarze static v_post posts_hyphtext[] = { ewarn_ge1, post_hyph, NULL }; 15267c719adSschwarze static v_post posts_it[] = { post_it, NULL }; 153bb648afaSschwarze static v_post posts_lb[] = { post_lb, NULL }; 1544039b21cSschwarze static v_post posts_nd[] = { berr_ge1, post_hyph, NULL }; 15567c719adSschwarze static v_post posts_nm[] = { post_nm, NULL }; 156da272f5eSschwarze static v_post posts_notext[] = { ewarn_eq0, NULL }; 157af216717Sschwarze static v_post posts_ns[] = { post_ns, NULL }; 15820fa2881Sschwarze static v_post posts_os[] = { post_os, post_prol, NULL }; 159e0dd4c9cSschwarze static v_post posts_pp[] = { post_par, ewarn_eq0, NULL }; 160bb648afaSschwarze static v_post posts_rs[] = { post_rs, NULL }; 1614039b21cSschwarze static v_post posts_sh[] = { post_ignpar,hwarn_ge1,post_sh,post_hyph,NULL }; 162e0dd4c9cSschwarze static v_post posts_sp[] = { post_par, ewarn_le1, NULL }; 1634039b21cSschwarze static v_post posts_ss[] = { post_ignpar, hwarn_ge1, post_hyph, NULL }; 164bb648afaSschwarze static v_post posts_st[] = { post_st, NULL }; 16520fa2881Sschwarze static v_post posts_std[] = { post_std, NULL }; 166e7a93ef3Sschwarze static v_post posts_text[] = { ewarn_ge1, NULL }; 167bb648afaSschwarze static v_post posts_text1[] = { ewarn_eq1, NULL }; 1688521b0bcSschwarze static v_post posts_vt[] = { post_vt, NULL }; 169f73abda9Skristaps static v_pre pres_an[] = { pre_an, NULL }; 17020fa2881Sschwarze static v_pre pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL }; 17120fa2881Sschwarze static v_pre pres_bl[] = { pre_bl, pre_par, NULL }; 172f73abda9Skristaps static v_pre pres_d1[] = { pre_display, NULL }; 17320fa2881Sschwarze static v_pre pres_dl[] = { pre_literal, pre_display, NULL }; 17467c719adSschwarze static v_pre pres_dd[] = { pre_dd, NULL }; 1756be99f77Sschwarze static v_pre pres_dt[] = { pre_dt, NULL }; 176f6127a73Sschwarze static v_pre pres_it[] = { pre_it, pre_par, NULL }; 177551cd4a8Sschwarze static v_pre pres_obsolete[] = { pre_obsolete, NULL }; 1786be99f77Sschwarze static v_pre pres_os[] = { pre_os, NULL }; 17920fa2881Sschwarze static v_pre pres_pp[] = { pre_par, NULL }; 180f73abda9Skristaps static v_pre pres_sh[] = { pre_sh, NULL }; 181f73abda9Skristaps static v_pre pres_ss[] = { pre_ss, NULL }; 18220fa2881Sschwarze static v_pre pres_std[] = { pre_std, NULL }; 183f73abda9Skristaps 18419a69263Sschwarze static const struct valids mdoc_valids[MDOC_MAX] = { 185099cfa7eSschwarze { NULL, NULL }, /* Ap */ 18620fa2881Sschwarze { pres_dd, posts_dd }, /* Dd */ 1876093755cSschwarze { pres_dt, posts_dt }, /* Dt */ 18820fa2881Sschwarze { pres_os, posts_os }, /* Os */ 189f73abda9Skristaps { pres_sh, posts_sh }, /* Sh */ 190f73abda9Skristaps { pres_ss, posts_ss }, /* Ss */ 191e0dd4c9cSschwarze { pres_pp, posts_pp }, /* Pp */ 1924039b21cSschwarze { pres_d1, posts_d1 }, /* D1 */ 19320fa2881Sschwarze { pres_dl, posts_dl }, /* Dl */ 19420fa2881Sschwarze { pres_bd, posts_bd }, /* Bd */ 195f73abda9Skristaps { NULL, NULL }, /* Ed */ 196f73abda9Skristaps { pres_bl, posts_bl }, /* Bl */ 197f73abda9Skristaps { NULL, NULL }, /* El */ 198f73abda9Skristaps { pres_it, posts_it }, /* It */ 199e7a93ef3Sschwarze { NULL, NULL }, /* Ad */ 200f73abda9Skristaps { pres_an, posts_an }, /* An */ 20120fa2881Sschwarze { NULL, posts_defaults }, /* Ar */ 202e7a93ef3Sschwarze { NULL, NULL }, /* Cd */ 203f73abda9Skristaps { NULL, NULL }, /* Cm */ 204f73abda9Skristaps { NULL, NULL }, /* Dv */ 2054039b21cSschwarze { NULL, NULL }, /* Er */ 206f73abda9Skristaps { NULL, NULL }, /* Ev */ 20720fa2881Sschwarze { pres_std, posts_std }, /* Ex */ 208f73abda9Skristaps { NULL, NULL }, /* Fa */ 2094039b21cSschwarze { NULL, posts_text }, /* Fd */ 210f73abda9Skristaps { NULL, NULL }, /* Fl */ 211e7a93ef3Sschwarze { NULL, NULL }, /* Fn */ 212e7a93ef3Sschwarze { NULL, NULL }, /* Ft */ 213e7a93ef3Sschwarze { NULL, NULL }, /* Ic */ 214b822ca0dSschwarze { NULL, posts_text1 }, /* In */ 21520fa2881Sschwarze { NULL, posts_defaults }, /* Li */ 2164602e85cSschwarze { NULL, posts_nd }, /* Nd */ 217f73abda9Skristaps { NULL, posts_nm }, /* Nm */ 218bca76d61Sschwarze { NULL, NULL }, /* Op */ 219551cd4a8Sschwarze { pres_obsolete, NULL }, /* Ot */ 22020fa2881Sschwarze { NULL, posts_defaults }, /* Pa */ 22120fa2881Sschwarze { pres_std, posts_std }, /* Rv */ 222f73abda9Skristaps { NULL, posts_st }, /* St */ 223f73abda9Skristaps { NULL, NULL }, /* Va */ 2248521b0bcSschwarze { NULL, posts_vt }, /* Vt */ 225e7a93ef3Sschwarze { NULL, posts_text }, /* Xr */ 226f73abda9Skristaps { NULL, posts_text }, /* %A */ 2274039b21cSschwarze { NULL, posts_hyphtext }, /* %B */ /* FIXME: can be used outside Rs/Re. */ 228b058e777Sschwarze { NULL, posts_text }, /* %D */ 229f73abda9Skristaps { NULL, posts_text }, /* %I */ 230f73abda9Skristaps { NULL, posts_text }, /* %J */ 2314039b21cSschwarze { NULL, posts_hyphtext }, /* %N */ 2324039b21cSschwarze { NULL, posts_hyphtext }, /* %O */ 233f73abda9Skristaps { NULL, posts_text }, /* %P */ 2344039b21cSschwarze { NULL, posts_hyphtext }, /* %R */ 2354039b21cSschwarze { NULL, posts_hyphtext }, /* %T */ /* FIXME: can be used outside Rs/Re. */ 236f73abda9Skristaps { NULL, posts_text }, /* %V */ 237f73abda9Skristaps { NULL, NULL }, /* Ac */ 238f73abda9Skristaps { NULL, NULL }, /* Ao */ 239bca76d61Sschwarze { NULL, NULL }, /* Aq */ 240f73abda9Skristaps { NULL, posts_at }, /* At */ 241f73abda9Skristaps { NULL, NULL }, /* Bc */ 242f73abda9Skristaps { NULL, posts_bf }, /* Bf */ 243f73abda9Skristaps { NULL, NULL }, /* Bo */ 244bca76d61Sschwarze { NULL, NULL }, /* Bq */ 245f73abda9Skristaps { NULL, NULL }, /* Bsx */ 246992063deSschwarze { NULL, posts_bx }, /* Bx */ 247f73abda9Skristaps { NULL, posts_bool }, /* Db */ 248f73abda9Skristaps { NULL, NULL }, /* Dc */ 249f73abda9Skristaps { NULL, NULL }, /* Do */ 250bca76d61Sschwarze { NULL, NULL }, /* Dq */ 251f73abda9Skristaps { NULL, NULL }, /* Ec */ 252f73abda9Skristaps { NULL, NULL }, /* Ef */ 253f73abda9Skristaps { NULL, NULL }, /* Em */ 254f73abda9Skristaps { NULL, NULL }, /* Eo */ 255f73abda9Skristaps { NULL, NULL }, /* Fx */ 256e7a93ef3Sschwarze { NULL, NULL }, /* Ms */ 257f73abda9Skristaps { NULL, posts_notext }, /* No */ 258af216717Sschwarze { NULL, posts_ns }, /* Ns */ 259f73abda9Skristaps { NULL, NULL }, /* Nx */ 260f73abda9Skristaps { NULL, NULL }, /* Ox */ 261f73abda9Skristaps { NULL, NULL }, /* Pc */ 262b822ca0dSschwarze { NULL, posts_text1 }, /* Pf */ 263f73abda9Skristaps { NULL, NULL }, /* Po */ 264bca76d61Sschwarze { NULL, NULL }, /* Pq */ 265f73abda9Skristaps { NULL, NULL }, /* Qc */ 266bca76d61Sschwarze { NULL, NULL }, /* Ql */ 267f73abda9Skristaps { NULL, NULL }, /* Qo */ 268bca76d61Sschwarze { NULL, NULL }, /* Qq */ 269f73abda9Skristaps { NULL, NULL }, /* Re */ 2708c62fbf5Sschwarze { NULL, posts_rs }, /* Rs */ 271f73abda9Skristaps { NULL, NULL }, /* Sc */ 272f73abda9Skristaps { NULL, NULL }, /* So */ 273bca76d61Sschwarze { NULL, NULL }, /* Sq */ 274f73abda9Skristaps { NULL, posts_bool }, /* Sm */ 2754039b21cSschwarze { NULL, posts_hyph }, /* Sx */ 276e7a93ef3Sschwarze { NULL, NULL }, /* Sy */ 277e7a93ef3Sschwarze { NULL, NULL }, /* Tn */ 278f73abda9Skristaps { NULL, NULL }, /* Ux */ 279f73abda9Skristaps { NULL, NULL }, /* Xc */ 280f73abda9Skristaps { NULL, NULL }, /* Xo */ 281f73abda9Skristaps { NULL, posts_fo }, /* Fo */ 282f73abda9Skristaps { NULL, NULL }, /* Fc */ 283f73abda9Skristaps { NULL, NULL }, /* Oo */ 284f73abda9Skristaps { NULL, NULL }, /* Oc */ 28520fa2881Sschwarze { NULL, posts_bk }, /* Bk */ 286f73abda9Skristaps { NULL, NULL }, /* Ek */ 287b31af00dSschwarze { NULL, posts_eoln }, /* Bt */ 288f73abda9Skristaps { NULL, NULL }, /* Hf */ 289551cd4a8Sschwarze { pres_obsolete, NULL }, /* Fr */ 290b31af00dSschwarze { NULL, posts_eoln }, /* Ud */ 291b31af00dSschwarze { NULL, posts_lb }, /* Lb */ 292e0dd4c9cSschwarze { pres_pp, posts_pp }, /* Lp */ 293e7a93ef3Sschwarze { NULL, NULL }, /* Lk */ 29420fa2881Sschwarze { NULL, posts_defaults }, /* Mt */ 295bca76d61Sschwarze { NULL, NULL }, /* Brq */ 296f73abda9Skristaps { NULL, NULL }, /* Bro */ 297f73abda9Skristaps { NULL, NULL }, /* Brc */ 298f73abda9Skristaps { NULL, posts_text }, /* %C */ 299551cd4a8Sschwarze { pres_obsolete, posts_es }, /* Es */ 300551cd4a8Sschwarze { pres_obsolete, posts_en }, /* En */ 301f73abda9Skristaps { NULL, NULL }, /* Dx */ 302f73abda9Skristaps { NULL, posts_text }, /* %Q */ 303e0dd4c9cSschwarze { NULL, posts_pp }, /* br */ 304e0dd4c9cSschwarze { NULL, posts_sp }, /* sp */ 305b822ca0dSschwarze { NULL, posts_text1 }, /* %U */ 3066093755cSschwarze { NULL, NULL }, /* Ta */ 3075281506aSschwarze { NULL, NULL }, /* ll */ 308f73abda9Skristaps }; 309f73abda9Skristaps 31020fa2881Sschwarze #define RSORD_MAX 14 /* Number of `Rs' blocks. */ 31120fa2881Sschwarze 31220fa2881Sschwarze static const enum mdoct rsord[RSORD_MAX] = { 31320fa2881Sschwarze MDOC__A, 31420fa2881Sschwarze MDOC__T, 31520fa2881Sschwarze MDOC__B, 31620fa2881Sschwarze MDOC__I, 31720fa2881Sschwarze MDOC__J, 31820fa2881Sschwarze MDOC__R, 31920fa2881Sschwarze MDOC__N, 32020fa2881Sschwarze MDOC__V, 3210397c682Sschwarze MDOC__U, 32220fa2881Sschwarze MDOC__P, 32320fa2881Sschwarze MDOC__Q, 3244e32ec8fSschwarze MDOC__C, 32520fa2881Sschwarze MDOC__D, 3264e32ec8fSschwarze MDOC__O 32720fa2881Sschwarze }; 32820fa2881Sschwarze 32919a69263Sschwarze static const char * const secnames[SEC__MAX] = { 33019a69263Sschwarze NULL, 33119a69263Sschwarze "NAME", 33219a69263Sschwarze "LIBRARY", 33319a69263Sschwarze "SYNOPSIS", 33419a69263Sschwarze "DESCRIPTION", 33503ab2f23Sdlg "CONTEXT", 33619a69263Sschwarze "IMPLEMENTATION NOTES", 33719a69263Sschwarze "RETURN VALUES", 33819a69263Sschwarze "ENVIRONMENT", 33919a69263Sschwarze "FILES", 34019a69263Sschwarze "EXIT STATUS", 34119a69263Sschwarze "EXAMPLES", 34219a69263Sschwarze "DIAGNOSTICS", 34319a69263Sschwarze "COMPATIBILITY", 34419a69263Sschwarze "ERRORS", 34519a69263Sschwarze "SEE ALSO", 34619a69263Sschwarze "STANDARDS", 34719a69263Sschwarze "HISTORY", 34819a69263Sschwarze "AUTHORS", 34919a69263Sschwarze "CAVEATS", 35019a69263Sschwarze "BUGS", 35119a69263Sschwarze "SECURITY CONSIDERATIONS", 35219a69263Sschwarze NULL 35319a69263Sschwarze }; 354f73abda9Skristaps 35549aff9f8Sschwarze 356f73abda9Skristaps int 3576093755cSschwarze mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n) 358f73abda9Skristaps { 359f73abda9Skristaps v_pre *p; 360f73abda9Skristaps int line, pos; 36131e23753Sschwarze char *tp; 362f73abda9Skristaps 3632791bd1cSschwarze switch (n->type) { 36449aff9f8Sschwarze case MDOC_TEXT: 365f73abda9Skristaps tp = n->string; 366f73abda9Skristaps line = n->line; 367f73abda9Skristaps pos = n->pos; 36820fa2881Sschwarze check_text(mdoc, line, pos, tp); 3692791bd1cSschwarze /* FALLTHROUGH */ 37049aff9f8Sschwarze case MDOC_TBL: 3712791bd1cSschwarze /* FALLTHROUGH */ 37249aff9f8Sschwarze case MDOC_EQN: 3738d973ab1Sschwarze /* FALLTHROUGH */ 37449aff9f8Sschwarze case MDOC_ROOT: 37520fa2881Sschwarze return(1); 3762791bd1cSschwarze default: 3772791bd1cSschwarze break; 378f73abda9Skristaps } 379f73abda9Skristaps 38020fa2881Sschwarze check_args(mdoc, n); 38120fa2881Sschwarze 382f73abda9Skristaps if (NULL == mdoc_valids[n->tok].pre) 383f73abda9Skristaps return(1); 384f73abda9Skristaps for (p = mdoc_valids[n->tok].pre; *p; p++) 385f73abda9Skristaps if ( ! (*p)(mdoc, n)) 386f73abda9Skristaps return(0); 387f73abda9Skristaps return(1); 388f73abda9Skristaps } 389f73abda9Skristaps 390f73abda9Skristaps int 391f73abda9Skristaps mdoc_valid_post(struct mdoc *mdoc) 392f73abda9Skristaps { 393f73abda9Skristaps v_post *p; 394f73abda9Skristaps 395f73abda9Skristaps if (MDOC_VALID & mdoc->last->flags) 396f73abda9Skristaps return(1); 397f73abda9Skristaps mdoc->last->flags |= MDOC_VALID; 398f73abda9Skristaps 3992791bd1cSschwarze switch (mdoc->last->type) { 40049aff9f8Sschwarze case MDOC_TEXT: 4012791bd1cSschwarze /* FALLTHROUGH */ 40249aff9f8Sschwarze case MDOC_EQN: 4038d973ab1Sschwarze /* FALLTHROUGH */ 40449aff9f8Sschwarze case MDOC_TBL: 405f73abda9Skristaps return(1); 40649aff9f8Sschwarze case MDOC_ROOT: 407f73abda9Skristaps return(post_root(mdoc)); 4082791bd1cSschwarze default: 4092791bd1cSschwarze break; 4102791bd1cSschwarze } 411f73abda9Skristaps 412f73abda9Skristaps if (NULL == mdoc_valids[mdoc->last->tok].post) 413f73abda9Skristaps return(1); 414f73abda9Skristaps for (p = mdoc_valids[mdoc->last->tok].post; *p; p++) 415f73abda9Skristaps if ( ! (*p)(mdoc)) 416f73abda9Skristaps return(0); 417f73abda9Skristaps 418f73abda9Skristaps return(1); 419f73abda9Skristaps } 420f73abda9Skristaps 4217c2be9f8Sschwarze static int 4227ead8a4eSschwarze check_count(struct mdoc *mdoc, enum mdoc_type type, 4237c2be9f8Sschwarze enum check_lvl lvl, enum check_ineq ineq, int val) 424f73abda9Skristaps { 4257c2be9f8Sschwarze const char *p; 426bb648afaSschwarze enum mandocerr t; 4277c2be9f8Sschwarze 4287ead8a4eSschwarze if (mdoc->last->type != type) 4297c2be9f8Sschwarze return(1); 4307c2be9f8Sschwarze 4317c2be9f8Sschwarze switch (ineq) { 43249aff9f8Sschwarze case CHECK_LT: 4337c2be9f8Sschwarze p = "less than "; 4347ead8a4eSschwarze if (mdoc->last->nchild < val) 4357c2be9f8Sschwarze return(1); 4367c2be9f8Sschwarze break; 43749aff9f8Sschwarze case CHECK_GT: 438bb648afaSschwarze p = "more than "; 4397ead8a4eSschwarze if (mdoc->last->nchild > val) 4407c2be9f8Sschwarze return(1); 4417c2be9f8Sschwarze break; 44249aff9f8Sschwarze case CHECK_EQ: 4437c2be9f8Sschwarze p = ""; 4447ead8a4eSschwarze if (val == mdoc->last->nchild) 4457c2be9f8Sschwarze return(1); 4467c2be9f8Sschwarze break; 44720fa2881Sschwarze default: 44820fa2881Sschwarze abort(); 44920fa2881Sschwarze /* NOTREACHED */ 4507c2be9f8Sschwarze } 4517c2be9f8Sschwarze 452bb648afaSschwarze t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT; 45349aff9f8Sschwarze mandoc_vmsg(t, mdoc->parse, mdoc->last->line, 45449aff9f8Sschwarze mdoc->last->pos, "want %s%d children (have %d)", 4557ead8a4eSschwarze p, val, mdoc->last->nchild); 45619a69263Sschwarze return(1); 4577c2be9f8Sschwarze } 458f73abda9Skristaps 4597c2be9f8Sschwarze static int 4607c2be9f8Sschwarze berr_ge1(POST_ARGS) 461f73abda9Skristaps { 462f73abda9Skristaps 463bb648afaSschwarze return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0)); 464f73abda9Skristaps } 465f73abda9Skristaps 4667c2be9f8Sschwarze static int 4677c2be9f8Sschwarze bwarn_ge1(POST_ARGS) 4687c2be9f8Sschwarze { 4697c2be9f8Sschwarze return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0)); 470f73abda9Skristaps } 471f73abda9Skristaps 4727c2be9f8Sschwarze static int 4737c2be9f8Sschwarze ewarn_eq0(POST_ARGS) 4747c2be9f8Sschwarze { 4757c2be9f8Sschwarze return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0)); 4767c2be9f8Sschwarze } 4777c2be9f8Sschwarze 4787c2be9f8Sschwarze static int 479bb648afaSschwarze ewarn_eq1(POST_ARGS) 480bb648afaSschwarze { 481bb648afaSschwarze return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1)); 482bb648afaSschwarze } 483bb648afaSschwarze 484bb648afaSschwarze static int 4857c2be9f8Sschwarze ewarn_ge1(POST_ARGS) 4867c2be9f8Sschwarze { 4877c2be9f8Sschwarze return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0)); 4887c2be9f8Sschwarze } 4897c2be9f8Sschwarze 4907c2be9f8Sschwarze static int 491bb648afaSschwarze ewarn_le1(POST_ARGS) 4927c2be9f8Sschwarze { 493bb648afaSschwarze return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2)); 4947c2be9f8Sschwarze } 4957c2be9f8Sschwarze 4967c2be9f8Sschwarze static int 4977c2be9f8Sschwarze hwarn_eq0(POST_ARGS) 4987c2be9f8Sschwarze { 4997c2be9f8Sschwarze return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0)); 5007c2be9f8Sschwarze } 5017c2be9f8Sschwarze 5027c2be9f8Sschwarze static int 5037c2be9f8Sschwarze hwarn_eq1(POST_ARGS) 5047c2be9f8Sschwarze { 5057c2be9f8Sschwarze return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1)); 5067c2be9f8Sschwarze } 5077c2be9f8Sschwarze 5087c2be9f8Sschwarze static int 509bb648afaSschwarze hwarn_ge1(POST_ARGS) 510bb648afaSschwarze { 511bb648afaSschwarze return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0)); 512bb648afaSschwarze } 513bb648afaSschwarze 514bb648afaSschwarze static int 5157c2be9f8Sschwarze hwarn_le1(POST_ARGS) 5167c2be9f8Sschwarze { 5177c2be9f8Sschwarze return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2)); 5187c2be9f8Sschwarze } 519f73abda9Skristaps 52020fa2881Sschwarze static void 5217ead8a4eSschwarze check_args(struct mdoc *mdoc, struct mdoc_node *n) 522f73abda9Skristaps { 523f73abda9Skristaps int i; 524f73abda9Skristaps 525f73abda9Skristaps if (NULL == n->args) 52620fa2881Sschwarze return; 527f73abda9Skristaps 528f73abda9Skristaps assert(n->args->argc); 529f73abda9Skristaps for (i = 0; i < (int)n->args->argc; i++) 5307ead8a4eSschwarze check_argv(mdoc, n, &n->args->argv[i]); 531f73abda9Skristaps } 532f73abda9Skristaps 53320fa2881Sschwarze static void 5347ead8a4eSschwarze check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v) 535f73abda9Skristaps { 536f73abda9Skristaps int i; 537f73abda9Skristaps 538f73abda9Skristaps for (i = 0; i < (int)v->sz; i++) 5397ead8a4eSschwarze check_text(mdoc, v->line, v->pos, v->value[i]); 540f73abda9Skristaps 54120fa2881Sschwarze /* FIXME: move to post_std(). */ 54220fa2881Sschwarze 54320fa2881Sschwarze if (MDOC_Std == v->arg) 5447ead8a4eSschwarze if ( ! (v->sz || mdoc->meta.name)) 5457ead8a4eSschwarze mdoc_nmsg(mdoc, n, MANDOCERR_NONAME); 546f73abda9Skristaps } 547f73abda9Skristaps 54820fa2881Sschwarze static void 5497ead8a4eSschwarze check_text(struct mdoc *mdoc, int ln, int pos, char *p) 550f73abda9Skristaps { 55104e980cbSschwarze char *cp; 552769ee804Sschwarze 5537ead8a4eSschwarze if (MDOC_LITERAL & mdoc->flags) 5541cdbf331Sschwarze return; 5551cdbf331Sschwarze 5561cdbf331Sschwarze for (cp = p; NULL != (p = strchr(p, '\t')); p++) 5577ead8a4eSschwarze mdoc_pmsg(mdoc, ln, pos + (int)(p - cp), MANDOCERR_BADTAB); 558f73abda9Skristaps } 559f73abda9Skristaps 560f73abda9Skristaps static int 561dd94fa3aSschwarze check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t) 562f73abda9Skristaps { 563f73abda9Skristaps 564f73abda9Skristaps assert(n->parent); 565f73abda9Skristaps if ((MDOC_ROOT == t || tok == n->parent->tok) && 566f73abda9Skristaps (t == n->parent->type)) 567f73abda9Skristaps return(1); 568f73abda9Skristaps 56949aff9f8Sschwarze mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, 57049aff9f8Sschwarze n->line, n->pos, "want parent %s", 57149aff9f8Sschwarze MDOC_ROOT == t ? "<root>" : mdoc_macronames[tok]); 5726e03d529Sschwarze return(0); 573f73abda9Skristaps } 574f73abda9Skristaps 575f73abda9Skristaps 576f73abda9Skristaps static int 577f73abda9Skristaps pre_display(PRE_ARGS) 578f73abda9Skristaps { 579f73abda9Skristaps struct mdoc_node *node; 580f73abda9Skristaps 581f73abda9Skristaps if (MDOC_BLOCK != n->type) 582f73abda9Skristaps return(1); 583f73abda9Skristaps 584f73abda9Skristaps for (node = mdoc->last->parent; node; node = node->parent) 585f73abda9Skristaps if (MDOC_BLOCK == node->type) 586f73abda9Skristaps if (MDOC_Bd == node->tok) 587f73abda9Skristaps break; 58820fa2881Sschwarze 58905c39368Sschwarze if (node) 590b723eac2Sschwarze mandoc_vmsg(MANDOCERR_BD_NEST, 591b723eac2Sschwarze mdoc->parse, n->line, n->pos, 592b723eac2Sschwarze "%s in Bd", mdoc_macronames[n->tok]); 59305c39368Sschwarze 59405c39368Sschwarze return(1); 595f73abda9Skristaps } 596f73abda9Skristaps 597f73abda9Skristaps static int 598f73abda9Skristaps pre_bl(PRE_ARGS) 599f73abda9Skristaps { 60031e23753Sschwarze int i, comp, dup; 60131e23753Sschwarze const char *offs, *width; 6026093755cSschwarze enum mdoc_list lt; 603769ee804Sschwarze struct mdoc_node *np; 604f73abda9Skristaps 6056093755cSschwarze if (MDOC_BLOCK != n->type) { 606769ee804Sschwarze if (ENDBODY_NOT != n->end) { 607769ee804Sschwarze assert(n->pending); 608769ee804Sschwarze np = n->pending->parent; 609769ee804Sschwarze } else 610769ee804Sschwarze np = n->parent; 611769ee804Sschwarze 612769ee804Sschwarze assert(np); 613769ee804Sschwarze assert(MDOC_BLOCK == np->type); 614769ee804Sschwarze assert(MDOC_Bl == np->tok); 615f73abda9Skristaps return(1); 6166e03d529Sschwarze } 617f73abda9Skristaps 6186093755cSschwarze /* 6196093755cSschwarze * First figure out which kind of list to use: bind ourselves to 6206093755cSschwarze * the first mentioned list type and warn about any remaining 6216093755cSschwarze * ones. If we find no list type, we default to LIST_item. 6226093755cSschwarze */ 623f73abda9Skristaps 6246093755cSschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) { 6256093755cSschwarze lt = LIST__NONE; 62631e23753Sschwarze dup = comp = 0; 62731e23753Sschwarze width = offs = NULL; 6286093755cSschwarze switch (n->args->argv[i].arg) { 6296093755cSschwarze /* Set list types. */ 63049aff9f8Sschwarze case MDOC_Bullet: 6316093755cSschwarze lt = LIST_bullet; 6326093755cSschwarze break; 63349aff9f8Sschwarze case MDOC_Dash: 6346093755cSschwarze lt = LIST_dash; 6356093755cSschwarze break; 63649aff9f8Sschwarze case MDOC_Enum: 6376093755cSschwarze lt = LIST_enum; 6386093755cSschwarze break; 63949aff9f8Sschwarze case MDOC_Hyphen: 6406093755cSschwarze lt = LIST_hyphen; 6416093755cSschwarze break; 64249aff9f8Sschwarze case MDOC_Item: 6436093755cSschwarze lt = LIST_item; 6446093755cSschwarze break; 64549aff9f8Sschwarze case MDOC_Tag: 6466093755cSschwarze lt = LIST_tag; 6476093755cSschwarze break; 64849aff9f8Sschwarze case MDOC_Diag: 6496093755cSschwarze lt = LIST_diag; 6506093755cSschwarze break; 65149aff9f8Sschwarze case MDOC_Hang: 6526093755cSschwarze lt = LIST_hang; 6536093755cSschwarze break; 65449aff9f8Sschwarze case MDOC_Ohang: 6556093755cSschwarze lt = LIST_ohang; 6566093755cSschwarze break; 65749aff9f8Sschwarze case MDOC_Inset: 6586093755cSschwarze lt = LIST_inset; 6596093755cSschwarze break; 66049aff9f8Sschwarze case MDOC_Column: 6616093755cSschwarze lt = LIST_column; 66264d728e4Sschwarze break; 6636093755cSschwarze /* Set list arguments. */ 66449aff9f8Sschwarze case MDOC_Compact: 6658c62fbf5Sschwarze dup = n->norm->Bl.comp; 66631e23753Sschwarze comp = 1; 66750e63e03Sschwarze break; 66849aff9f8Sschwarze case MDOC_Width: 66922972b14Sschwarze /* NB: this can be empty! */ 67022972b14Sschwarze if (n->args->argv[i].sz) { 67131e23753Sschwarze width = n->args->argv[i].value[0]; 67222972b14Sschwarze dup = (NULL != n->norm->Bl.width); 67322972b14Sschwarze break; 67422972b14Sschwarze } 67522972b14Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 67664d728e4Sschwarze break; 67749aff9f8Sschwarze case MDOC_Offset: 67831e23753Sschwarze /* NB: this can be empty! */ 67931e23753Sschwarze if (n->args->argv[i].sz) { 68031e23753Sschwarze offs = n->args->argv[i].value[0]; 6818c62fbf5Sschwarze dup = (NULL != n->norm->Bl.offs); 68231e23753Sschwarze break; 68331e23753Sschwarze } 68420fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 685f73abda9Skristaps break; 686ddce0b0cSschwarze default: 687ddce0b0cSschwarze continue; 688f73abda9Skristaps } 689f73abda9Skristaps 6906093755cSschwarze /* Check: duplicate auxiliary arguments. */ 6916093755cSschwarze 69220fa2881Sschwarze if (dup) 69320fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); 6946093755cSschwarze 69531e23753Sschwarze if (comp && ! dup) 6968c62fbf5Sschwarze n->norm->Bl.comp = comp; 69731e23753Sschwarze if (offs && ! dup) 6988c62fbf5Sschwarze n->norm->Bl.offs = offs; 69931e23753Sschwarze if (width && ! dup) 7008c62fbf5Sschwarze n->norm->Bl.width = width; 70131e23753Sschwarze 7026093755cSschwarze /* Check: multiple list types. */ 7036093755cSschwarze 7048c62fbf5Sschwarze if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE) 70520fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP); 7066093755cSschwarze 7076093755cSschwarze /* Assign list type. */ 7086093755cSschwarze 7098c62fbf5Sschwarze if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) { 7108c62fbf5Sschwarze n->norm->Bl.type = lt; 711769ee804Sschwarze /* Set column information, too. */ 712769ee804Sschwarze if (LIST_column == lt) { 7138c62fbf5Sschwarze n->norm->Bl.ncols = 714769ee804Sschwarze n->args->argv[i].sz; 71504e980cbSschwarze n->norm->Bl.cols = (void *) 716769ee804Sschwarze n->args->argv[i].value; 717769ee804Sschwarze } 718769ee804Sschwarze } 7196093755cSschwarze 7206093755cSschwarze /* The list type should come first. */ 7216093755cSschwarze 7228c62fbf5Sschwarze if (n->norm->Bl.type == LIST__NONE) 7238c62fbf5Sschwarze if (n->norm->Bl.width || 7248c62fbf5Sschwarze n->norm->Bl.offs || 7258c62fbf5Sschwarze n->norm->Bl.comp) 72620fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST); 7276093755cSschwarze 7286093755cSschwarze continue; 7296093755cSschwarze } 7306093755cSschwarze 7316093755cSschwarze /* Allow lists to default to LIST_item. */ 7326093755cSschwarze 7338c62fbf5Sschwarze if (LIST__NONE == n->norm->Bl.type) { 73420fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE); 7358c62fbf5Sschwarze n->norm->Bl.type = LIST_item; 7366e03d529Sschwarze } 737f73abda9Skristaps 73864d728e4Sschwarze /* 73964d728e4Sschwarze * Validate the width field. Some list types don't need width 74064d728e4Sschwarze * types and should be warned about them. Others should have it 7415eced068Sschwarze * and must also be warned. Yet others have a default and need 7425eced068Sschwarze * no warning. 74364d728e4Sschwarze */ 74464d728e4Sschwarze 7458c62fbf5Sschwarze switch (n->norm->Bl.type) { 74649aff9f8Sschwarze case LIST_tag: 7475eced068Sschwarze if (NULL == n->norm->Bl.width) 74820fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG); 749f73abda9Skristaps break; 75049aff9f8Sschwarze case LIST_column: 7516093755cSschwarze /* FALLTHROUGH */ 75249aff9f8Sschwarze case LIST_diag: 7536093755cSschwarze /* FALLTHROUGH */ 75449aff9f8Sschwarze case LIST_ohang: 7556093755cSschwarze /* FALLTHROUGH */ 75649aff9f8Sschwarze case LIST_inset: 7576093755cSschwarze /* FALLTHROUGH */ 75849aff9f8Sschwarze case LIST_item: 7598c62fbf5Sschwarze if (n->norm->Bl.width) 760817ac90bSschwarze mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 7616093755cSschwarze break; 76249aff9f8Sschwarze case LIST_bullet: 7635eced068Sschwarze /* FALLTHROUGH */ 76449aff9f8Sschwarze case LIST_dash: 7655eced068Sschwarze /* FALLTHROUGH */ 76649aff9f8Sschwarze case LIST_hyphen: 7675eced068Sschwarze if (NULL == n->norm->Bl.width) 7685eced068Sschwarze n->norm->Bl.width = "2n"; 7695eced068Sschwarze break; 77049aff9f8Sschwarze case LIST_enum: 7715eced068Sschwarze if (NULL == n->norm->Bl.width) 7725eced068Sschwarze n->norm->Bl.width = "3n"; 7735eced068Sschwarze break; 77464d728e4Sschwarze default: 775f73abda9Skristaps break; 77664d728e4Sschwarze } 77764d728e4Sschwarze 778f73abda9Skristaps return(1); 779f73abda9Skristaps } 780f73abda9Skristaps 781f73abda9Skristaps static int 782f73abda9Skristaps pre_bd(PRE_ARGS) 783f73abda9Skristaps { 78431e23753Sschwarze int i, dup, comp; 78531e23753Sschwarze enum mdoc_disp dt; 78631e23753Sschwarze const char *offs; 787769ee804Sschwarze struct mdoc_node *np; 788f73abda9Skristaps 78931e23753Sschwarze if (MDOC_BLOCK != n->type) { 790769ee804Sschwarze if (ENDBODY_NOT != n->end) { 791769ee804Sschwarze assert(n->pending); 792769ee804Sschwarze np = n->pending->parent; 793769ee804Sschwarze } else 794769ee804Sschwarze np = n->parent; 795769ee804Sschwarze 796769ee804Sschwarze assert(np); 797769ee804Sschwarze assert(MDOC_BLOCK == np->type); 798769ee804Sschwarze assert(MDOC_Bd == np->tok); 799f73abda9Skristaps return(1); 8006e03d529Sschwarze } 801f73abda9Skristaps 80231e23753Sschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) { 80331e23753Sschwarze dt = DISP__NONE; 80431e23753Sschwarze dup = comp = 0; 80531e23753Sschwarze offs = NULL; 80631e23753Sschwarze 807f73abda9Skristaps switch (n->args->argv[i].arg) { 80849aff9f8Sschwarze case MDOC_Centred: 80931e23753Sschwarze dt = DISP_centred; 81031e23753Sschwarze break; 81149aff9f8Sschwarze case MDOC_Ragged: 81231e23753Sschwarze dt = DISP_ragged; 81331e23753Sschwarze break; 81449aff9f8Sschwarze case MDOC_Unfilled: 81531e23753Sschwarze dt = DISP_unfilled; 81631e23753Sschwarze break; 81749aff9f8Sschwarze case MDOC_Filled: 81831e23753Sschwarze dt = DISP_filled; 81931e23753Sschwarze break; 82049aff9f8Sschwarze case MDOC_Literal: 82131e23753Sschwarze dt = DISP_literal; 822f73abda9Skristaps break; 82349aff9f8Sschwarze case MDOC_File: 82431e23753Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP); 8256e03d529Sschwarze return(0); 82649aff9f8Sschwarze case MDOC_Offset: 82731e23753Sschwarze /* NB: this can be empty! */ 82831e23753Sschwarze if (n->args->argv[i].sz) { 82931e23753Sschwarze offs = n->args->argv[i].value[0]; 8308c62fbf5Sschwarze dup = (NULL != n->norm->Bd.offs); 831f73abda9Skristaps break; 832f73abda9Skristaps } 83320fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); 83431e23753Sschwarze break; 83549aff9f8Sschwarze case MDOC_Compact: 83631e23753Sschwarze comp = 1; 8378c62fbf5Sschwarze dup = n->norm->Bd.comp; 83831e23753Sschwarze break; 83931e23753Sschwarze default: 84031e23753Sschwarze abort(); 84131e23753Sschwarze /* NOTREACHED */ 84231e23753Sschwarze } 84331e23753Sschwarze 84431e23753Sschwarze /* Check whether we have duplicates. */ 84531e23753Sschwarze 84620fa2881Sschwarze if (dup) 84720fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); 84831e23753Sschwarze 84931e23753Sschwarze /* Make our auxiliary assignments. */ 85031e23753Sschwarze 85131e23753Sschwarze if (offs && ! dup) 8528c62fbf5Sschwarze n->norm->Bd.offs = offs; 85331e23753Sschwarze if (comp && ! dup) 8548c62fbf5Sschwarze n->norm->Bd.comp = comp; 85531e23753Sschwarze 85631e23753Sschwarze /* Check whether a type has already been assigned. */ 85731e23753Sschwarze 8588c62fbf5Sschwarze if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE) 85920fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP); 86031e23753Sschwarze 86131e23753Sschwarze /* Make our type assignment. */ 86231e23753Sschwarze 8638c62fbf5Sschwarze if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE) 8648c62fbf5Sschwarze n->norm->Bd.type = dt; 86531e23753Sschwarze } 86631e23753Sschwarze 8678c62fbf5Sschwarze if (DISP__NONE == n->norm->Bd.type) { 86820fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE); 8698c62fbf5Sschwarze n->norm->Bd.type = DISP_ragged; 87031e23753Sschwarze } 87131e23753Sschwarze 87231e23753Sschwarze return(1); 873f73abda9Skristaps } 874f73abda9Skristaps 875f73abda9Skristaps static int 876f73abda9Skristaps pre_ss(PRE_ARGS) 877f73abda9Skristaps { 878f73abda9Skristaps 879f73abda9Skristaps if (MDOC_BLOCK != n->type) 880f73abda9Skristaps return(1); 881f73abda9Skristaps return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY)); 882f73abda9Skristaps } 883f73abda9Skristaps 884f73abda9Skristaps static int 885f73abda9Skristaps pre_sh(PRE_ARGS) 886f73abda9Skristaps { 887f73abda9Skristaps 888f73abda9Skristaps if (MDOC_BLOCK != n->type) 889f73abda9Skristaps return(1); 890a4c002ecSschwarze return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT)); 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 static int 904f73abda9Skristaps pre_an(PRE_ARGS) 905f73abda9Skristaps { 9066475d5b0Sschwarze int i; 907f73abda9Skristaps 908769ee804Sschwarze if (NULL == n->args) 909f73abda9Skristaps return(1); 910769ee804Sschwarze 9116475d5b0Sschwarze for (i = 1; i < (int)n->args->argc; i++) 91220fa2881Sschwarze mdoc_pmsg(mdoc, n->args->argv[i].line, 91320fa2881Sschwarze n->args->argv[i].pos, MANDOCERR_IGNARGV); 9147c2be9f8Sschwarze 915769ee804Sschwarze if (MDOC_Split == n->args->argv[0].arg) 9168c62fbf5Sschwarze n->norm->An.auth = AUTH_split; 917769ee804Sschwarze else if (MDOC_Nosplit == n->args->argv[0].arg) 9188c62fbf5Sschwarze n->norm->An.auth = AUTH_nosplit; 919769ee804Sschwarze else 920769ee804Sschwarze abort(); 921769ee804Sschwarze 922769ee804Sschwarze return(1); 923f73abda9Skristaps } 924f73abda9Skristaps 925f73abda9Skristaps static int 92620fa2881Sschwarze pre_std(PRE_ARGS) 927f73abda9Skristaps { 928f73abda9Skristaps 92920fa2881Sschwarze if (n->args && 1 == n->args->argc) 93020fa2881Sschwarze if (MDOC_Std == n->args->argv[0].arg) 93120fa2881Sschwarze return(1); 932f73abda9Skristaps 93320fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV); 9346093755cSschwarze return(1); 9356093755cSschwarze } 9366093755cSschwarze 9376093755cSschwarze static int 938551cd4a8Sschwarze pre_obsolete(PRE_ARGS) 939551cd4a8Sschwarze { 940551cd4a8Sschwarze 941551cd4a8Sschwarze if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type) 942551cd4a8Sschwarze mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse, 943551cd4a8Sschwarze n->line, n->pos, mdoc_macronames[n->tok]); 944551cd4a8Sschwarze return(1); 945551cd4a8Sschwarze } 946551cd4a8Sschwarze 947551cd4a8Sschwarze static int 948f73abda9Skristaps pre_dt(PRE_ARGS) 949f73abda9Skristaps { 950f73abda9Skristaps 951b058e777Sschwarze if (NULL == mdoc->meta.date || mdoc->meta.os) 95251fcab2fSschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 95351fcab2fSschwarze n->line, n->pos, "Dt"); 95420fa2881Sschwarze 955f73abda9Skristaps if (mdoc->meta.title) 95651fcab2fSschwarze mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 95751fcab2fSschwarze n->line, n->pos, "Dt"); 95820fa2881Sschwarze 959f73abda9Skristaps return(1); 960f73abda9Skristaps } 961f73abda9Skristaps 962f73abda9Skristaps static int 963f73abda9Skristaps pre_os(PRE_ARGS) 964f73abda9Skristaps { 965f73abda9Skristaps 966b058e777Sschwarze if (NULL == mdoc->meta.title || NULL == mdoc->meta.date) 96751fcab2fSschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 96851fcab2fSschwarze n->line, n->pos, "Os"); 96920fa2881Sschwarze 970f73abda9Skristaps if (mdoc->meta.os) 97151fcab2fSschwarze mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 97251fcab2fSschwarze n->line, n->pos, "Os"); 97320fa2881Sschwarze 974f73abda9Skristaps return(1); 975f73abda9Skristaps } 976f73abda9Skristaps 977f73abda9Skristaps static int 978f73abda9Skristaps pre_dd(PRE_ARGS) 979f73abda9Skristaps { 980f73abda9Skristaps 981f73abda9Skristaps if (mdoc->meta.title || mdoc->meta.os) 98251fcab2fSschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 98351fcab2fSschwarze n->line, n->pos, "Dd"); 98420fa2881Sschwarze 985f73abda9Skristaps if (mdoc->meta.date) 98651fcab2fSschwarze mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 98751fcab2fSschwarze n->line, n->pos, "Dd"); 98820fa2881Sschwarze 989f73abda9Skristaps return(1); 990f73abda9Skristaps } 991f73abda9Skristaps 992f73abda9Skristaps static int 993f73abda9Skristaps post_bf(POST_ARGS) 994f73abda9Skristaps { 995769ee804Sschwarze struct mdoc_node *np; 996ddce0b0cSschwarze enum mdocargt arg; 997f73abda9Skristaps 998769ee804Sschwarze /* 999769ee804Sschwarze * Unlike other data pointers, these are "housed" by the HEAD 1000769ee804Sschwarze * element, which contains the goods. 1001769ee804Sschwarze */ 1002769ee804Sschwarze 1003769ee804Sschwarze if (MDOC_HEAD != mdoc->last->type) { 1004769ee804Sschwarze if (ENDBODY_NOT != mdoc->last->end) { 1005769ee804Sschwarze assert(mdoc->last->pending); 1006769ee804Sschwarze np = mdoc->last->pending->parent->head; 1007769ee804Sschwarze } else if (MDOC_BLOCK != mdoc->last->type) { 1008769ee804Sschwarze np = mdoc->last->parent->head; 1009769ee804Sschwarze } else 1010769ee804Sschwarze np = mdoc->last->head; 1011769ee804Sschwarze 1012769ee804Sschwarze assert(np); 1013769ee804Sschwarze assert(MDOC_HEAD == np->type); 1014769ee804Sschwarze assert(MDOC_Bf == np->tok); 1015f73abda9Skristaps return(1); 10166e03d529Sschwarze } 1017f73abda9Skristaps 1018769ee804Sschwarze np = mdoc->last; 1019769ee804Sschwarze assert(MDOC_BLOCK == np->parent->type); 1020769ee804Sschwarze assert(MDOC_Bf == np->parent->tok); 102150d41253Sschwarze 1022769ee804Sschwarze /* 1023769ee804Sschwarze * Cannot have both argument and parameter. 1024769ee804Sschwarze * If neither is specified, let it through with a warning. 1025769ee804Sschwarze */ 1026f73abda9Skristaps 1027769ee804Sschwarze if (np->parent->args && np->child) { 1028769ee804Sschwarze mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT); 10296e03d529Sschwarze return(0); 103020fa2881Sschwarze } else if (NULL == np->parent->args && NULL == np->child) { 103120fa2881Sschwarze mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); 103220fa2881Sschwarze return(1); 103320fa2881Sschwarze } 1034769ee804Sschwarze 1035769ee804Sschwarze /* Extract argument into data. */ 1036769ee804Sschwarze 1037769ee804Sschwarze if (np->parent->args) { 1038769ee804Sschwarze arg = np->parent->args->argv[0].arg; 1039769ee804Sschwarze if (MDOC_Emphasis == arg) 10408c62fbf5Sschwarze np->norm->Bf.font = FONT_Em; 1041769ee804Sschwarze else if (MDOC_Literal == arg) 10428c62fbf5Sschwarze np->norm->Bf.font = FONT_Li; 1043769ee804Sschwarze else if (MDOC_Symbolic == arg) 10448c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy; 1045769ee804Sschwarze else 1046769ee804Sschwarze abort(); 1047769ee804Sschwarze return(1); 1048769ee804Sschwarze } 1049769ee804Sschwarze 1050769ee804Sschwarze /* Extract parameter into data. */ 1051769ee804Sschwarze 1052769ee804Sschwarze if (0 == strcmp(np->child->string, "Em")) 10538c62fbf5Sschwarze np->norm->Bf.font = FONT_Em; 1054769ee804Sschwarze else if (0 == strcmp(np->child->string, "Li")) 10558c62fbf5Sschwarze np->norm->Bf.font = FONT_Li; 1056769ee804Sschwarze else if (0 == strcmp(np->child->string, "Sy")) 10578c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy; 105820fa2881Sschwarze else 105920fa2881Sschwarze mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); 1060769ee804Sschwarze 1061769ee804Sschwarze return(1); 1062f73abda9Skristaps } 1063f73abda9Skristaps 1064f73abda9Skristaps static int 106571719887Sschwarze post_lb(POST_ARGS) 106671719887Sschwarze { 106777e000ffSschwarze struct mdoc_node *n; 106877e000ffSschwarze const char *stdlibname; 106977e000ffSschwarze char *libname; 107071719887Sschwarze 1071bb648afaSschwarze check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); 1072bb648afaSschwarze 107377e000ffSschwarze n = mdoc->last->child; 107420fa2881Sschwarze 107577e000ffSschwarze assert(n); 107677e000ffSschwarze assert(MDOC_TEXT == n->type); 107720fa2881Sschwarze 107877e000ffSschwarze if (NULL == (stdlibname = mdoc_a2lib(n->string))) 107977e000ffSschwarze mandoc_asprintf(&libname, 108077e000ffSschwarze "library \\(lq%s\\(rq", n->string); 108177e000ffSschwarze else 108277e000ffSschwarze libname = mandoc_strdup(stdlibname); 108320fa2881Sschwarze 108477e000ffSschwarze free(n->string); 108577e000ffSschwarze n->string = libname; 108620fa2881Sschwarze return(1); 108720fa2881Sschwarze } 108871719887Sschwarze 108971719887Sschwarze static int 1090b31af00dSschwarze post_eoln(POST_ARGS) 1091b31af00dSschwarze { 1092b31af00dSschwarze 109320fa2881Sschwarze if (mdoc->last->child) 109420fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST); 1095b31af00dSschwarze return(1); 1096b31af00dSschwarze } 1097b31af00dSschwarze 1098b31af00dSschwarze static int 10998521b0bcSschwarze post_vt(POST_ARGS) 11008521b0bcSschwarze { 11018521b0bcSschwarze const struct mdoc_node *n; 11028521b0bcSschwarze 11038521b0bcSschwarze /* 11048521b0bcSschwarze * The Vt macro comes in both ELEM and BLOCK form, both of which 11058521b0bcSschwarze * have different syntaxes (yet more context-sensitive 1106e7a93ef3Sschwarze * behaviour). ELEM types must have a child, which is already 1107e7a93ef3Sschwarze * guaranteed by the in_line parsing routine; BLOCK types, 11088521b0bcSschwarze * specifically the BODY, should only have TEXT children. 11098521b0bcSschwarze */ 11108521b0bcSschwarze 11118521b0bcSschwarze if (MDOC_BODY != mdoc->last->type) 11128521b0bcSschwarze return(1); 11138521b0bcSschwarze 11148521b0bcSschwarze for (n = mdoc->last->child; n; n = n->next) 1115bc49dbe1Sschwarze if (MDOC_TEXT != n->type) 1116dd25b57cSschwarze mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse, 1117dd25b57cSschwarze n->line, n->pos, mdoc_macronames[n->tok]); 11188521b0bcSschwarze 11198521b0bcSschwarze return(1); 11208521b0bcSschwarze } 11218521b0bcSschwarze 11228521b0bcSschwarze static int 1123f73abda9Skristaps post_nm(POST_ARGS) 1124f73abda9Skristaps { 112520fa2881Sschwarze 1126160ac481Sschwarze if (NULL != mdoc->meta.name) 112720fa2881Sschwarze return(1); 112820fa2881Sschwarze 112983af2bccSschwarze mdoc_deroff(&mdoc->meta.name, mdoc->last); 113020fa2881Sschwarze 113183af2bccSschwarze if (NULL == mdoc->meta.name) { 1132160ac481Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME); 1133160ac481Sschwarze mdoc->meta.name = mandoc_strdup("UNKNOWN"); 1134160ac481Sschwarze } 113520fa2881Sschwarze return(1); 113620fa2881Sschwarze } 113720fa2881Sschwarze 113820fa2881Sschwarze static int 113920fa2881Sschwarze post_literal(POST_ARGS) 114020fa2881Sschwarze { 114120fa2881Sschwarze 114220fa2881Sschwarze /* 114320fa2881Sschwarze * The `Dl' (note "el" not "one") and `Bd' macros unset the 114420fa2881Sschwarze * MDOC_LITERAL flag as they leave. Note that `Bd' only sets 114520fa2881Sschwarze * this in literal mode, but it doesn't hurt to just switch it 114620fa2881Sschwarze * off in general since displays can't be nested. 114720fa2881Sschwarze */ 114820fa2881Sschwarze 114920fa2881Sschwarze if (MDOC_BODY == mdoc->last->type) 115020fa2881Sschwarze mdoc->flags &= ~MDOC_LITERAL; 115120fa2881Sschwarze 115220fa2881Sschwarze return(1); 115320fa2881Sschwarze } 115420fa2881Sschwarze 115520fa2881Sschwarze static int 115620fa2881Sschwarze post_defaults(POST_ARGS) 115720fa2881Sschwarze { 115820fa2881Sschwarze struct mdoc_node *nn; 115920fa2881Sschwarze 116020fa2881Sschwarze /* 116120fa2881Sschwarze * The `Ar' defaults to "file ..." if no value is provided as an 116220fa2881Sschwarze * argument; the `Mt' and `Pa' macros use "~"; the `Li' just 116320fa2881Sschwarze * gets an empty string. 116420fa2881Sschwarze */ 1165f73abda9Skristaps 1166f73abda9Skristaps if (mdoc->last->child) 1167f73abda9Skristaps return(1); 116820fa2881Sschwarze 116920fa2881Sschwarze nn = mdoc->last; 117020fa2881Sschwarze mdoc->next = MDOC_NEXT_CHILD; 117120fa2881Sschwarze 117220fa2881Sschwarze switch (nn->tok) { 117349aff9f8Sschwarze case MDOC_Ar: 117420fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file")) 117520fa2881Sschwarze return(0); 117620fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "...")) 117720fa2881Sschwarze return(0); 117820fa2881Sschwarze break; 117949aff9f8Sschwarze case MDOC_At: 118020fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T")) 118120fa2881Sschwarze return(0); 118220fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX")) 118320fa2881Sschwarze return(0); 118420fa2881Sschwarze break; 118549aff9f8Sschwarze case MDOC_Li: 118620fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "")) 118720fa2881Sschwarze return(0); 118820fa2881Sschwarze break; 118949aff9f8Sschwarze case MDOC_Pa: 119020fa2881Sschwarze /* FALLTHROUGH */ 119149aff9f8Sschwarze case MDOC_Mt: 119220fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~")) 119320fa2881Sschwarze return(0); 119420fa2881Sschwarze break; 119520fa2881Sschwarze default: 119620fa2881Sschwarze abort(); 119720fa2881Sschwarze /* NOTREACHED */ 1198f73abda9Skristaps } 1199f73abda9Skristaps 120020fa2881Sschwarze mdoc->last = nn; 120120fa2881Sschwarze return(1); 120220fa2881Sschwarze } 1203f73abda9Skristaps 1204f73abda9Skristaps static int 1205f73abda9Skristaps post_at(POST_ARGS) 1206f73abda9Skristaps { 12070b2f1307Sschwarze struct mdoc_node *n; 12080b2f1307Sschwarze const char *std_att; 12090b2f1307Sschwarze char *att; 121020fa2881Sschwarze 121120fa2881Sschwarze /* 121220fa2881Sschwarze * If we have a child, look it up in the standard keys. If a 121320fa2881Sschwarze * key exist, use that instead of the child; if it doesn't, 121420fa2881Sschwarze * prefix "AT&T UNIX " to the existing data. 121520fa2881Sschwarze */ 1216f73abda9Skristaps 12170b2f1307Sschwarze if (NULL == (n = mdoc->last->child)) 1218f73abda9Skristaps return(1); 121920fa2881Sschwarze 12200b2f1307Sschwarze assert(MDOC_TEXT == n->type); 12210b2f1307Sschwarze if (NULL == (std_att = mdoc_a2att(n->string))) { 122220fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT); 12230b2f1307Sschwarze mandoc_asprintf(&att, "AT&T UNIX %s", n->string); 12240b2f1307Sschwarze } else 12250b2f1307Sschwarze att = mandoc_strdup(std_att); 1226f73abda9Skristaps 12270b2f1307Sschwarze free(n->string); 12280b2f1307Sschwarze n->string = att; 122920fa2881Sschwarze return(1); 123020fa2881Sschwarze } 1231f73abda9Skristaps 1232f73abda9Skristaps static int 1233f73abda9Skristaps post_an(POST_ARGS) 1234f73abda9Skristaps { 1235769ee804Sschwarze struct mdoc_node *np; 1236f73abda9Skristaps 1237769ee804Sschwarze np = mdoc->last; 1238e7a93ef3Sschwarze if (AUTH__NONE == np->norm->An.auth) { 1239e7a93ef3Sschwarze if (0 == np->child) 1240e7a93ef3Sschwarze check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0); 1241e7a93ef3Sschwarze } else if (np->child) 1242bb648afaSschwarze check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0); 124320fa2881Sschwarze 124420fa2881Sschwarze return(1); 1245f73abda9Skristaps } 1246f73abda9Skristaps 1247f73abda9Skristaps static int 1248551cd4a8Sschwarze post_en(POST_ARGS) 1249551cd4a8Sschwarze { 1250551cd4a8Sschwarze 1251551cd4a8Sschwarze if (MDOC_BLOCK == mdoc->last->type) 1252551cd4a8Sschwarze mdoc->last->norm->Es = mdoc->last_es; 1253551cd4a8Sschwarze return(1); 1254551cd4a8Sschwarze } 1255551cd4a8Sschwarze 1256551cd4a8Sschwarze static int 1257551cd4a8Sschwarze post_es(POST_ARGS) 1258551cd4a8Sschwarze { 1259551cd4a8Sschwarze 1260551cd4a8Sschwarze mdoc->last_es = mdoc->last; 1261551cd4a8Sschwarze return(1); 1262551cd4a8Sschwarze } 1263551cd4a8Sschwarze 1264551cd4a8Sschwarze static int 1265f73abda9Skristaps post_it(POST_ARGS) 1266f73abda9Skristaps { 126719a69263Sschwarze int i, cols; 12686093755cSschwarze enum mdoc_list lt; 1269*9530682eSschwarze struct mdoc_node *nbl, *nit, *nch; 12706093755cSschwarze enum mandocerr er; 1271f73abda9Skristaps 1272*9530682eSschwarze nit = mdoc->last; 1273*9530682eSschwarze if (MDOC_BLOCK != nit->type) 1274f73abda9Skristaps return(1); 1275f73abda9Skristaps 1276*9530682eSschwarze nbl = nit->parent->parent; 1277*9530682eSschwarze lt = nbl->norm->Bl.type; 12786093755cSschwarze 12796093755cSschwarze if (LIST__NONE == lt) { 1280*9530682eSschwarze mdoc_nmsg(mdoc, nit, MANDOCERR_LISTTYPE); 128120fa2881Sschwarze return(1); 12826e03d529Sschwarze } 1283f73abda9Skristaps 12846093755cSschwarze switch (lt) { 128549aff9f8Sschwarze case LIST_tag: 1286*9530682eSschwarze /* FALLTHROUGH */ 128749aff9f8Sschwarze case LIST_hang: 1288f73abda9Skristaps /* FALLTHROUGH */ 128949aff9f8Sschwarze case LIST_ohang: 1290f73abda9Skristaps /* FALLTHROUGH */ 129149aff9f8Sschwarze case LIST_inset: 1292f73abda9Skristaps /* FALLTHROUGH */ 129349aff9f8Sschwarze case LIST_diag: 1294*9530682eSschwarze if (NULL == nit->head->child) 1295*9530682eSschwarze mandoc_msg(MANDOCERR_IT_NOHEAD, 1296*9530682eSschwarze mdoc->parse, nit->line, nit->pos, 1297*9530682eSschwarze mdoc_argnames[nbl->args->argv[0].arg]); 1298f73abda9Skristaps break; 129949aff9f8Sschwarze case LIST_bullet: 1300f73abda9Skristaps /* FALLTHROUGH */ 130149aff9f8Sschwarze case LIST_dash: 1302f73abda9Skristaps /* FALLTHROUGH */ 130349aff9f8Sschwarze case LIST_enum: 1304f73abda9Skristaps /* FALLTHROUGH */ 130549aff9f8Sschwarze case LIST_hyphen: 1306*9530682eSschwarze if (NULL == nit->body->child) 1307*9530682eSschwarze mdoc_nmsg(mdoc, nit, MANDOCERR_NOBODY); 1308f73abda9Skristaps /* FALLTHROUGH */ 130949aff9f8Sschwarze case LIST_item: 1310*9530682eSschwarze if (NULL != nit->head->child) 1311*9530682eSschwarze mdoc_nmsg(mdoc, nit, MANDOCERR_ARGSLOST); 1312f73abda9Skristaps break; 131349aff9f8Sschwarze case LIST_column: 1314*9530682eSschwarze cols = (int)nbl->norm->Bl.ncols; 13156093755cSschwarze 1316*9530682eSschwarze assert(NULL == nit->head->child); 13176093755cSschwarze 1318*9530682eSschwarze if (NULL == nit->body->child) 1319*9530682eSschwarze mdoc_nmsg(mdoc, nit, MANDOCERR_NOBODY); 13206093755cSschwarze 1321*9530682eSschwarze for (i = 0, nch = nit->child; nch; nch = nch->next) 1322*9530682eSschwarze if (MDOC_BODY == nch->type) 1323f73abda9Skristaps i++; 132453292e81Sschwarze 13256093755cSschwarze if (i < cols) 13266093755cSschwarze er = MANDOCERR_ARGCOUNT; 13276093755cSschwarze else if (i == cols || i == cols + 1) 1328f73abda9Skristaps break; 13296093755cSschwarze else 13306093755cSschwarze er = MANDOCERR_SYNTARGCOUNT; 133153292e81Sschwarze 1332*9530682eSschwarze mandoc_vmsg(er, mdoc->parse, nit->line, nit->pos, 13336e03d529Sschwarze "columns == %d (have %d)", cols, i); 133419a69263Sschwarze return(MANDOCERR_ARGCOUNT == er); 1335f73abda9Skristaps default: 1336f73abda9Skristaps break; 1337f73abda9Skristaps } 1338f73abda9Skristaps 1339f73abda9Skristaps return(1); 1340f73abda9Skristaps } 1341f73abda9Skristaps 134220fa2881Sschwarze static int 134320fa2881Sschwarze post_bl_block(POST_ARGS) 134420fa2881Sschwarze { 1345bb99f0faSschwarze struct mdoc_node *n, *ni, *nc; 134620fa2881Sschwarze 134720fa2881Sschwarze /* 134820fa2881Sschwarze * These are fairly complicated, so we've broken them into two 134920fa2881Sschwarze * functions. post_bl_block_tag() is called when a -tag is 135020fa2881Sschwarze * specified, but no -width (it must be guessed). The second 135120fa2881Sschwarze * when a -width is specified (macro indicators must be 135220fa2881Sschwarze * rewritten into real lengths). 135320fa2881Sschwarze */ 135420fa2881Sschwarze 135520fa2881Sschwarze n = mdoc->last; 135620fa2881Sschwarze 13578c62fbf5Sschwarze if (LIST_tag == n->norm->Bl.type && 13588c62fbf5Sschwarze NULL == n->norm->Bl.width) { 135920fa2881Sschwarze if ( ! post_bl_block_tag(mdoc)) 136020fa2881Sschwarze return(0); 1361bb99f0faSschwarze assert(n->norm->Bl.width); 13628c62fbf5Sschwarze } else if (NULL != n->norm->Bl.width) { 136320fa2881Sschwarze if ( ! post_bl_block_width(mdoc)) 136420fa2881Sschwarze return(0); 13658c62fbf5Sschwarze assert(n->norm->Bl.width); 1366bb99f0faSschwarze } 1367bb99f0faSschwarze 1368bb99f0faSschwarze for (ni = n->body->child; ni; ni = ni->next) { 1369bb99f0faSschwarze if (NULL == ni->body) 1370bb99f0faSschwarze continue; 1371bb99f0faSschwarze nc = ni->body->last; 1372bb99f0faSschwarze while (NULL != nc) { 1373bb99f0faSschwarze switch (nc->tok) { 137449aff9f8Sschwarze case MDOC_Pp: 1375bb99f0faSschwarze /* FALLTHROUGH */ 137649aff9f8Sschwarze case MDOC_Lp: 1377bb99f0faSschwarze /* FALLTHROUGH */ 137849aff9f8Sschwarze case MDOC_br: 1379bb99f0faSschwarze break; 1380bb99f0faSschwarze default: 1381bb99f0faSschwarze nc = NULL; 1382bb99f0faSschwarze continue; 1383bb99f0faSschwarze } 1384bb99f0faSschwarze if (NULL == ni->next) { 138520369664Sschwarze mandoc_msg(MANDOCERR_PAR_MOVE, 138620369664Sschwarze mdoc->parse, nc->line, nc->pos, 138720369664Sschwarze mdoc_macronames[nc->tok]); 1388bb99f0faSschwarze if ( ! mdoc_node_relink(mdoc, nc)) 1389bb99f0faSschwarze return(0); 1390bb99f0faSschwarze } else if (0 == n->norm->Bl.comp && 1391bb99f0faSschwarze LIST_column != n->norm->Bl.type) { 139220369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, 139320369664Sschwarze mdoc->parse, nc->line, nc->pos, 139420369664Sschwarze "%s before It", 139520369664Sschwarze mdoc_macronames[nc->tok]); 1396bb99f0faSschwarze mdoc_node_delete(mdoc, nc); 1397bb99f0faSschwarze } else 1398bb99f0faSschwarze break; 1399bb99f0faSschwarze nc = ni->body->last; 1400bb99f0faSschwarze } 1401bb99f0faSschwarze } 140220fa2881Sschwarze return(1); 140320fa2881Sschwarze } 140420fa2881Sschwarze 140520fa2881Sschwarze static int 140620fa2881Sschwarze post_bl_block_width(POST_ARGS) 140720fa2881Sschwarze { 140820fa2881Sschwarze size_t width; 140920fa2881Sschwarze int i; 141020fa2881Sschwarze enum mdoct tok; 141120fa2881Sschwarze struct mdoc_node *n; 141247813146Sschwarze char buf[24]; 141320fa2881Sschwarze 141420fa2881Sschwarze n = mdoc->last; 141520fa2881Sschwarze 141620fa2881Sschwarze /* 141720fa2881Sschwarze * Calculate the real width of a list from the -width string, 141820fa2881Sschwarze * which may contain a macro (with a known default width), a 141920fa2881Sschwarze * literal string, or a scaling width. 142020fa2881Sschwarze * 142120fa2881Sschwarze * If the value to -width is a macro, then we re-write it to be 142220fa2881Sschwarze * the macro's width as set in share/tmac/mdoc/doc-common. 142320fa2881Sschwarze */ 142420fa2881Sschwarze 14258c62fbf5Sschwarze if (0 == strcmp(n->norm->Bl.width, "Ds")) 142620fa2881Sschwarze width = 6; 14278c62fbf5Sschwarze else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width))) 142820fa2881Sschwarze return(1); 142919a69263Sschwarze else if (0 == (width = macro2len(tok))) { 143020fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH); 143120fa2881Sschwarze return(1); 143220fa2881Sschwarze } 143320fa2881Sschwarze 143420fa2881Sschwarze /* The value already exists: free and reallocate it. */ 143520fa2881Sschwarze 143620fa2881Sschwarze assert(n->args); 143720fa2881Sschwarze 143820fa2881Sschwarze for (i = 0; i < (int)n->args->argc; i++) 143920fa2881Sschwarze if (MDOC_Width == n->args->argv[i].arg) 144020fa2881Sschwarze break; 144120fa2881Sschwarze 144220fa2881Sschwarze assert(i < (int)n->args->argc); 144320fa2881Sschwarze 144447813146Sschwarze (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width); 144520fa2881Sschwarze free(n->args->argv[i].value[0]); 144620fa2881Sschwarze n->args->argv[i].value[0] = mandoc_strdup(buf); 144720fa2881Sschwarze 144820fa2881Sschwarze /* Set our width! */ 14498c62fbf5Sschwarze n->norm->Bl.width = n->args->argv[i].value[0]; 145020fa2881Sschwarze return(1); 145120fa2881Sschwarze } 145220fa2881Sschwarze 145320fa2881Sschwarze static int 145420fa2881Sschwarze post_bl_block_tag(POST_ARGS) 145520fa2881Sschwarze { 145620fa2881Sschwarze struct mdoc_node *n, *nn; 145720fa2881Sschwarze size_t sz, ssz; 145820fa2881Sschwarze int i; 145947813146Sschwarze char buf[24]; 146020fa2881Sschwarze 146120fa2881Sschwarze /* 146220fa2881Sschwarze * Calculate the -width for a `Bl -tag' list if it hasn't been 146320fa2881Sschwarze * provided. Uses the first head macro. NOTE AGAIN: this is 146420fa2881Sschwarze * ONLY if the -width argument has NOT been provided. See 146520fa2881Sschwarze * post_bl_block_width() for converting the -width string. 146620fa2881Sschwarze */ 146720fa2881Sschwarze 146820fa2881Sschwarze sz = 10; 146920fa2881Sschwarze n = mdoc->last; 147020fa2881Sschwarze 147120fa2881Sschwarze for (nn = n->body->child; nn; nn = nn->next) { 147220fa2881Sschwarze if (MDOC_It != nn->tok) 147320fa2881Sschwarze continue; 147420fa2881Sschwarze 147520fa2881Sschwarze assert(MDOC_BLOCK == nn->type); 147620fa2881Sschwarze nn = nn->head->child; 147720fa2881Sschwarze 147820fa2881Sschwarze if (nn == NULL) 147920fa2881Sschwarze break; 148020fa2881Sschwarze 148120fa2881Sschwarze if (MDOC_TEXT == nn->type) { 148220fa2881Sschwarze sz = strlen(nn->string) + 1; 148320fa2881Sschwarze break; 148420fa2881Sschwarze } 148520fa2881Sschwarze 148619a69263Sschwarze if (0 != (ssz = macro2len(nn->tok))) 148720fa2881Sschwarze sz = ssz; 148820fa2881Sschwarze 148920fa2881Sschwarze break; 149020fa2881Sschwarze } 149120fa2881Sschwarze 149220fa2881Sschwarze /* Defaults to ten ens. */ 149320fa2881Sschwarze 149447813146Sschwarze (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz); 149520fa2881Sschwarze 149620fa2881Sschwarze /* 149720fa2881Sschwarze * We have to dynamically add this to the macro's argument list. 149820fa2881Sschwarze * We're guaranteed that a MDOC_Width doesn't already exist. 149920fa2881Sschwarze */ 150020fa2881Sschwarze 150120fa2881Sschwarze assert(n->args); 150220fa2881Sschwarze i = (int)(n->args->argc)++; 150320fa2881Sschwarze 15048286bf36Sschwarze n->args->argv = mandoc_reallocarray(n->args->argv, 15058286bf36Sschwarze n->args->argc, sizeof(struct mdoc_argv)); 150620fa2881Sschwarze 150720fa2881Sschwarze n->args->argv[i].arg = MDOC_Width; 150820fa2881Sschwarze n->args->argv[i].line = n->line; 150920fa2881Sschwarze n->args->argv[i].pos = n->pos; 151020fa2881Sschwarze n->args->argv[i].sz = 1; 151120fa2881Sschwarze n->args->argv[i].value = mandoc_malloc(sizeof(char *)); 151220fa2881Sschwarze n->args->argv[i].value[0] = mandoc_strdup(buf); 151320fa2881Sschwarze 151420fa2881Sschwarze /* Set our width! */ 15158c62fbf5Sschwarze n->norm->Bl.width = n->args->argv[i].value[0]; 151620fa2881Sschwarze return(1); 151720fa2881Sschwarze } 151820fa2881Sschwarze 1519f73abda9Skristaps static int 1520395185ccSschwarze post_bl_head(POST_ARGS) 1521395185ccSschwarze { 152220fa2881Sschwarze struct mdoc_node *np, *nn, *nnp; 152320fa2881Sschwarze int i, j; 1524395185ccSschwarze 15258c62fbf5Sschwarze if (LIST_column != mdoc->last->norm->Bl.type) 152620fa2881Sschwarze /* FIXME: this should be ERROR class... */ 152720fa2881Sschwarze return(hwarn_eq0(mdoc)); 1528395185ccSschwarze 152920fa2881Sschwarze /* 153020fa2881Sschwarze * Convert old-style lists, where the column width specifiers 153120fa2881Sschwarze * trail as macro parameters, to the new-style ("normal-form") 153220fa2881Sschwarze * lists where they're argument values following -column. 153320fa2881Sschwarze */ 153420fa2881Sschwarze 153520fa2881Sschwarze /* First, disallow both types and allow normal-form. */ 153620fa2881Sschwarze 153720fa2881Sschwarze /* 153820fa2881Sschwarze * TODO: technically, we can accept both and just merge the two 153920fa2881Sschwarze * lists, but I'll leave that for another day. 154020fa2881Sschwarze */ 154120fa2881Sschwarze 15428c62fbf5Sschwarze if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) { 154320fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS); 15446093755cSschwarze return(0); 154520fa2881Sschwarze } else if (NULL == mdoc->last->child) 154620fa2881Sschwarze return(1); 154720fa2881Sschwarze 154820fa2881Sschwarze np = mdoc->last->parent; 154920fa2881Sschwarze assert(np->args); 155020fa2881Sschwarze 155120fa2881Sschwarze for (j = 0; j < (int)np->args->argc; j++) 155220fa2881Sschwarze if (MDOC_Column == np->args->argv[j].arg) 155320fa2881Sschwarze break; 155420fa2881Sschwarze 155520fa2881Sschwarze assert(j < (int)np->args->argc); 155620fa2881Sschwarze assert(0 == np->args->argv[j].sz); 155720fa2881Sschwarze 155820fa2881Sschwarze /* 1559a5e11edeSschwarze * Accommodate for new-style groff column syntax. Shuffle the 156020fa2881Sschwarze * child nodes, all of which must be TEXT, as arguments for the 156120fa2881Sschwarze * column field. Then, delete the head children. 156220fa2881Sschwarze */ 156320fa2881Sschwarze 156420fa2881Sschwarze np->args->argv[j].sz = (size_t)mdoc->last->nchild; 15658286bf36Sschwarze np->args->argv[j].value = mandoc_reallocarray(NULL, 15668286bf36Sschwarze (size_t)mdoc->last->nchild, sizeof(char *)); 156720fa2881Sschwarze 15688c62fbf5Sschwarze mdoc->last->norm->Bl.ncols = np->args->argv[j].sz; 156904e980cbSschwarze mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value; 157020fa2881Sschwarze 157120fa2881Sschwarze for (i = 0, nn = mdoc->last->child; nn; i++) { 157220fa2881Sschwarze np->args->argv[j].value[i] = nn->string; 157320fa2881Sschwarze nn->string = NULL; 157420fa2881Sschwarze nnp = nn; 157520fa2881Sschwarze nn = nn->next; 157620fa2881Sschwarze mdoc_node_delete(NULL, nnp); 1577395185ccSschwarze } 157820fa2881Sschwarze 157920fa2881Sschwarze mdoc->last->nchild = 0; 158020fa2881Sschwarze mdoc->last->child = NULL; 158120fa2881Sschwarze 15826093755cSschwarze return(1); 1583b16e7ddfSschwarze } 1584b16e7ddfSschwarze 1585395185ccSschwarze static int 1586f73abda9Skristaps post_bl(POST_ARGS) 1587f73abda9Skristaps { 15882a427d60Sschwarze struct mdoc_node *nparent, *nprev; /* of the Bl block */ 15892a427d60Sschwarze struct mdoc_node *nblock, *nbody; /* of the Bl */ 15902a427d60Sschwarze struct mdoc_node *nchild, *nnext; /* of the Bl body */ 1591f73abda9Skristaps 15922a427d60Sschwarze nbody = mdoc->last; 15932a427d60Sschwarze switch (nbody->type) { 159449aff9f8Sschwarze case MDOC_BLOCK: 159520fa2881Sschwarze return(post_bl_block(mdoc)); 159649aff9f8Sschwarze case MDOC_HEAD: 15972a427d60Sschwarze return(post_bl_head(mdoc)); 159849aff9f8Sschwarze case MDOC_BODY: 1599f6127a73Sschwarze break; 16002a427d60Sschwarze default: 16012a427d60Sschwarze return(1); 1602f6127a73Sschwarze } 1603f6127a73Sschwarze 16042a427d60Sschwarze nchild = nbody->child; 16052a427d60Sschwarze while (NULL != nchild) { 16062a427d60Sschwarze if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) { 16072a427d60Sschwarze nchild = nchild->next; 16082a427d60Sschwarze continue; 16092a427d60Sschwarze } 16102a427d60Sschwarze 1611dd25b57cSschwarze mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse, 1612dd25b57cSschwarze nchild->line, nchild->pos, 1613dd25b57cSschwarze mdoc_macronames[nchild->tok]); 16142a427d60Sschwarze 16152a427d60Sschwarze /* 16162a427d60Sschwarze * Move the node out of the Bl block. 16172a427d60Sschwarze * First, collect all required node pointers. 16182a427d60Sschwarze */ 16192a427d60Sschwarze 16202a427d60Sschwarze nblock = nbody->parent; 16212a427d60Sschwarze nprev = nblock->prev; 16222a427d60Sschwarze nparent = nblock->parent; 16232a427d60Sschwarze nnext = nchild->next; 16242a427d60Sschwarze 16252a427d60Sschwarze /* 16262a427d60Sschwarze * Unlink this child. 16272a427d60Sschwarze */ 16282a427d60Sschwarze 16292a427d60Sschwarze assert(NULL == nchild->prev); 16302a427d60Sschwarze if (0 == --nbody->nchild) { 16312a427d60Sschwarze nbody->child = NULL; 16322a427d60Sschwarze nbody->last = NULL; 16332a427d60Sschwarze assert(NULL == nnext); 16342a427d60Sschwarze } else { 16352a427d60Sschwarze nbody->child = nnext; 16362a427d60Sschwarze nnext->prev = NULL; 16372a427d60Sschwarze } 16382a427d60Sschwarze 16392a427d60Sschwarze /* 16402a427d60Sschwarze * Relink this child. 16412a427d60Sschwarze */ 16422a427d60Sschwarze 16432a427d60Sschwarze nchild->parent = nparent; 16442a427d60Sschwarze nchild->prev = nprev; 16452a427d60Sschwarze nchild->next = nblock; 16462a427d60Sschwarze 16472a427d60Sschwarze nblock->prev = nchild; 16482a427d60Sschwarze nparent->nchild++; 16492a427d60Sschwarze if (NULL == nprev) 16502a427d60Sschwarze nparent->child = nchild; 16512a427d60Sschwarze else 16522a427d60Sschwarze nprev->next = nchild; 16532a427d60Sschwarze 16542a427d60Sschwarze nchild = nnext; 1655f73abda9Skristaps } 1656f73abda9Skristaps 1657f73abda9Skristaps return(1); 1658f73abda9Skristaps } 1659f73abda9Skristaps 1660f73abda9Skristaps static int 1661f73abda9Skristaps ebool(struct mdoc *mdoc) 1662f73abda9Skristaps { 1663f73abda9Skristaps 1664bb648afaSschwarze if (NULL == mdoc->last->child) { 1665f9e7bf99Sschwarze if (MDOC_Sm == mdoc->last->tok) 1666f9e7bf99Sschwarze mdoc->flags ^= MDOC_SMOFF; 1667f73abda9Skristaps return(1); 1668bb648afaSschwarze } 1669f9e7bf99Sschwarze 1670f9e7bf99Sschwarze check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2); 1671f73abda9Skristaps 167220fa2881Sschwarze assert(MDOC_TEXT == mdoc->last->child->type); 167320fa2881Sschwarze 1674ec2beb53Sschwarze if (0 == strcmp(mdoc->last->child->string, "on")) { 1675ec2beb53Sschwarze if (MDOC_Sm == mdoc->last->tok) 1676ec2beb53Sschwarze mdoc->flags &= ~MDOC_SMOFF; 167720fa2881Sschwarze return(1); 1678ec2beb53Sschwarze } 1679ec2beb53Sschwarze if (0 == strcmp(mdoc->last->child->string, "off")) { 1680ec2beb53Sschwarze if (MDOC_Sm == mdoc->last->tok) 1681ec2beb53Sschwarze mdoc->flags |= MDOC_SMOFF; 168220fa2881Sschwarze return(1); 1683ec2beb53Sschwarze } 168420fa2881Sschwarze 168520fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL); 168620fa2881Sschwarze return(1); 168720fa2881Sschwarze } 1688f73abda9Skristaps 1689f73abda9Skristaps static int 1690f73abda9Skristaps post_root(POST_ARGS) 1691f73abda9Skristaps { 169243edbcc8Sschwarze int ret; 169320fa2881Sschwarze struct mdoc_node *n; 1694f73abda9Skristaps 169543edbcc8Sschwarze ret = 1; 169620fa2881Sschwarze 169720fa2881Sschwarze /* Check that we have a finished prologue. */ 169820fa2881Sschwarze 169920fa2881Sschwarze if ( ! (MDOC_PBODY & mdoc->flags)) { 170043edbcc8Sschwarze ret = 0; 17016e03d529Sschwarze mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG); 1702f73abda9Skristaps } 1703f73abda9Skristaps 170420fa2881Sschwarze n = mdoc->first; 170520fa2881Sschwarze assert(n); 170620fa2881Sschwarze 170720fa2881Sschwarze /* Check that we begin with a proper `Sh'. */ 170820fa2881Sschwarze 170943edbcc8Sschwarze if (NULL == n->child) 171043edbcc8Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_DOC_EMPTY); 171151fcab2fSschwarze else if (MDOC_Sh != n->child->tok) 171251fcab2fSschwarze mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse, 171351fcab2fSschwarze n->child->line, n->child->pos, 171451fcab2fSschwarze mdoc_macronames[n->child->tok]); 171520fa2881Sschwarze 171643edbcc8Sschwarze return(ret); 171720fa2881Sschwarze } 1718f73abda9Skristaps 1719f73abda9Skristaps static int 1720f73abda9Skristaps post_st(POST_ARGS) 1721f73abda9Skristaps { 1722bb648afaSschwarze struct mdoc_node *ch; 172320fa2881Sschwarze const char *p; 1724f73abda9Skristaps 1725bb648afaSschwarze if (NULL == (ch = mdoc->last->child)) { 1726307e5a07Sschwarze mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, 1727307e5a07Sschwarze mdoc->last->line, mdoc->last->pos, 1728307e5a07Sschwarze mdoc_macronames[mdoc->last->tok]); 1729bb648afaSschwarze mdoc_node_delete(mdoc, mdoc->last); 1730bb648afaSschwarze return(1); 1731bb648afaSschwarze } 173220fa2881Sschwarze 1733bb648afaSschwarze assert(MDOC_TEXT == ch->type); 173420fa2881Sschwarze 1735bb648afaSschwarze if (NULL == (p = mdoc_a2st(ch->string))) { 173620fa2881Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD); 173720fa2881Sschwarze mdoc_node_delete(mdoc, mdoc->last); 173820fa2881Sschwarze } else { 1739bb648afaSschwarze free(ch->string); 1740bb648afaSschwarze ch->string = mandoc_strdup(p); 1741f73abda9Skristaps } 1742f73abda9Skristaps 174320fa2881Sschwarze return(1); 174420fa2881Sschwarze } 1745f73abda9Skristaps 1746f73abda9Skristaps static int 1747011fe33bSschwarze post_rs(POST_ARGS) 1748011fe33bSschwarze { 174920fa2881Sschwarze struct mdoc_node *nn, *next, *prev; 175020fa2881Sschwarze int i, j; 1751011fe33bSschwarze 1752bb648afaSschwarze switch (mdoc->last->type) { 175349aff9f8Sschwarze case MDOC_HEAD: 1754bb648afaSschwarze check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0); 1755011fe33bSschwarze return(1); 175649aff9f8Sschwarze case MDOC_BODY: 1757bb648afaSschwarze if (mdoc->last->child) 1758bb648afaSschwarze break; 1759bb648afaSschwarze check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0); 1760bb648afaSschwarze return(1); 1761bb648afaSschwarze default: 1762bb648afaSschwarze return(1); 1763bb648afaSschwarze } 1764011fe33bSschwarze 176520fa2881Sschwarze /* 176620fa2881Sschwarze * Make sure only certain types of nodes are allowed within the 176720fa2881Sschwarze * the `Rs' body. Delete offending nodes and raise a warning. 176820fa2881Sschwarze * Do this before re-ordering for the sake of clarity. 176920fa2881Sschwarze */ 177020fa2881Sschwarze 177120fa2881Sschwarze next = NULL; 177220fa2881Sschwarze for (nn = mdoc->last->child; nn; nn = next) { 177320fa2881Sschwarze for (i = 0; i < RSORD_MAX; i++) 177420fa2881Sschwarze if (nn->tok == rsord[i]) 1775011fe33bSschwarze break; 177620fa2881Sschwarze 177720fa2881Sschwarze if (i < RSORD_MAX) { 17785d273f35Sschwarze if (MDOC__J == rsord[i] || MDOC__B == rsord[i]) 17795d273f35Sschwarze mdoc->last->norm->Rs.quote_T++; 178020fa2881Sschwarze next = nn->next; 178120fa2881Sschwarze continue; 178220fa2881Sschwarze } 178320fa2881Sschwarze 178420fa2881Sschwarze next = nn->next; 1785dd25b57cSschwarze mandoc_msg(MANDOCERR_RS_SKIP, mdoc->parse, 1786dd25b57cSschwarze nn->line, nn->pos, mdoc_macronames[nn->tok]); 178720fa2881Sschwarze mdoc_node_delete(mdoc, nn); 178820fa2881Sschwarze } 178920fa2881Sschwarze 179020fa2881Sschwarze /* 1791c6176538Sschwarze * Nothing to sort if only invalid nodes were found 1792c6176538Sschwarze * inside the `Rs' body. 1793c6176538Sschwarze */ 1794c6176538Sschwarze 1795c6176538Sschwarze if (NULL == mdoc->last->child) 1796c6176538Sschwarze return(1); 1797c6176538Sschwarze 1798c6176538Sschwarze /* 179920fa2881Sschwarze * The full `Rs' block needs special handling to order the 180020fa2881Sschwarze * sub-elements according to `rsord'. Pick through each element 180120fa2881Sschwarze * and correctly order it. This is a insertion sort. 180220fa2881Sschwarze */ 180320fa2881Sschwarze 180420fa2881Sschwarze next = NULL; 180520fa2881Sschwarze for (nn = mdoc->last->child->next; nn; nn = next) { 180620fa2881Sschwarze /* Determine order of `nn'. */ 180720fa2881Sschwarze for (i = 0; i < RSORD_MAX; i++) 180820fa2881Sschwarze if (rsord[i] == nn->tok) 180920fa2881Sschwarze break; 181020fa2881Sschwarze 181120fa2881Sschwarze /* 181220fa2881Sschwarze * Remove `nn' from the chain. This somewhat 181320fa2881Sschwarze * repeats mdoc_node_unlink(), but since we're 181420fa2881Sschwarze * just re-ordering, there's no need for the 181520fa2881Sschwarze * full unlink process. 181620fa2881Sschwarze */ 181720fa2881Sschwarze 181820fa2881Sschwarze if (NULL != (next = nn->next)) 181920fa2881Sschwarze next->prev = nn->prev; 182020fa2881Sschwarze 182120fa2881Sschwarze if (NULL != (prev = nn->prev)) 182220fa2881Sschwarze prev->next = nn->next; 182320fa2881Sschwarze 182420fa2881Sschwarze nn->prev = nn->next = NULL; 182520fa2881Sschwarze 182620fa2881Sschwarze /* 182720fa2881Sschwarze * Scan back until we reach a node that's 182820fa2881Sschwarze * ordered before `nn'. 182920fa2881Sschwarze */ 183020fa2881Sschwarze 183120fa2881Sschwarze for ( ; prev ; prev = prev->prev) { 183220fa2881Sschwarze /* Determine order of `prev'. */ 183320fa2881Sschwarze for (j = 0; j < RSORD_MAX; j++) 183420fa2881Sschwarze if (rsord[j] == prev->tok) 183520fa2881Sschwarze break; 183620fa2881Sschwarze 183720fa2881Sschwarze if (j <= i) 183820fa2881Sschwarze break; 183920fa2881Sschwarze } 184020fa2881Sschwarze 184120fa2881Sschwarze /* 184220fa2881Sschwarze * Set `nn' back into its correct place in front 184320fa2881Sschwarze * of the `prev' node. 184420fa2881Sschwarze */ 184520fa2881Sschwarze 184620fa2881Sschwarze nn->prev = prev; 184720fa2881Sschwarze 184820fa2881Sschwarze if (prev) { 184920fa2881Sschwarze if (prev->next) 185020fa2881Sschwarze prev->next->prev = nn; 185120fa2881Sschwarze nn->next = prev->next; 185220fa2881Sschwarze prev->next = nn; 185320fa2881Sschwarze } else { 185420fa2881Sschwarze mdoc->last->child->prev = nn; 185520fa2881Sschwarze nn->next = mdoc->last->child; 185620fa2881Sschwarze mdoc->last->child = nn; 185720fa2881Sschwarze } 1858011fe33bSschwarze } 1859011fe33bSschwarze 1860011fe33bSschwarze return(1); 1861011fe33bSschwarze } 1862011fe33bSschwarze 18634039b21cSschwarze /* 18644039b21cSschwarze * For some arguments of some macros, 18654039b21cSschwarze * convert all breakable hyphens into ASCII_HYPH. 18664039b21cSschwarze */ 18674039b21cSschwarze static int 18684039b21cSschwarze post_hyph(POST_ARGS) 18694039b21cSschwarze { 18704039b21cSschwarze struct mdoc_node *n, *nch; 18714039b21cSschwarze char *cp; 18724039b21cSschwarze 18734039b21cSschwarze n = mdoc->last; 18744039b21cSschwarze switch (n->type) { 187549aff9f8Sschwarze case MDOC_HEAD: 18764039b21cSschwarze if (MDOC_Sh == n->tok || MDOC_Ss == n->tok) 18774039b21cSschwarze break; 18784039b21cSschwarze return(1); 187949aff9f8Sschwarze case MDOC_BODY: 18804039b21cSschwarze if (MDOC_D1 == n->tok || MDOC_Nd == n->tok) 18814039b21cSschwarze break; 18824039b21cSschwarze return(1); 188349aff9f8Sschwarze case MDOC_ELEM: 18844039b21cSschwarze break; 18854039b21cSschwarze default: 18864039b21cSschwarze return(1); 18874039b21cSschwarze } 18884039b21cSschwarze 18894039b21cSschwarze for (nch = n->child; nch; nch = nch->next) { 18904039b21cSschwarze if (MDOC_TEXT != nch->type) 18914039b21cSschwarze continue; 18924039b21cSschwarze cp = nch->string; 1893b7e2b14eSschwarze if ('\0' == *cp) 18944039b21cSschwarze continue; 18954039b21cSschwarze while ('\0' != *(++cp)) 18964039b21cSschwarze if ('-' == *cp && 18974039b21cSschwarze isalpha((unsigned char)cp[-1]) && 18984039b21cSschwarze isalpha((unsigned char)cp[1])) 18994039b21cSschwarze *cp = ASCII_HYPH; 19004039b21cSschwarze } 19014039b21cSschwarze return(1); 19024039b21cSschwarze } 19034039b21cSschwarze 1904011fe33bSschwarze static int 1905af216717Sschwarze post_ns(POST_ARGS) 1906af216717Sschwarze { 1907af216717Sschwarze 1908af216717Sschwarze if (MDOC_LINE & mdoc->last->flags) 1909b723eac2Sschwarze mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NS_SKIP); 1910af216717Sschwarze return(1); 1911af216717Sschwarze } 1912af216717Sschwarze 1913af216717Sschwarze static int 1914f73abda9Skristaps post_sh(POST_ARGS) 1915f73abda9Skristaps { 1916f73abda9Skristaps 1917f73abda9Skristaps if (MDOC_HEAD == mdoc->last->type) 1918f73abda9Skristaps return(post_sh_head(mdoc)); 1919f73abda9Skristaps if (MDOC_BODY == mdoc->last->type) 1920f73abda9Skristaps return(post_sh_body(mdoc)); 1921f73abda9Skristaps 1922f73abda9Skristaps return(1); 1923f73abda9Skristaps } 1924f73abda9Skristaps 1925f73abda9Skristaps static int 1926f73abda9Skristaps post_sh_body(POST_ARGS) 1927f73abda9Skristaps { 1928f73abda9Skristaps struct mdoc_node *n; 1929f73abda9Skristaps 1930f8c9d6f2Sschwarze if (SEC_NAME != mdoc->lastsec) 1931f73abda9Skristaps return(1); 1932f73abda9Skristaps 1933f73abda9Skristaps /* 1934f73abda9Skristaps * Warn if the NAME section doesn't contain the `Nm' and `Nd' 1935f73abda9Skristaps * macros (can have multiple `Nm' and one `Nd'). Note that the 1936f73abda9Skristaps * children of the BODY declaration can also be "text". 1937f73abda9Skristaps */ 1938f73abda9Skristaps 193920fa2881Sschwarze if (NULL == (n = mdoc->last->child)) { 194051fcab2fSschwarze mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, 194151fcab2fSschwarze mdoc->last->line, mdoc->last->pos, "empty"); 194220fa2881Sschwarze return(1); 194320fa2881Sschwarze } 1944f73abda9Skristaps 1945f73abda9Skristaps for ( ; n && n->next; n = n->next) { 1946f73abda9Skristaps if (MDOC_ELEM == n->type && MDOC_Nm == n->tok) 1947f73abda9Skristaps continue; 1948f73abda9Skristaps if (MDOC_TEXT == n->type) 1949f73abda9Skristaps continue; 195051fcab2fSschwarze mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, 195151fcab2fSschwarze n->line, n->pos, mdoc_macronames[n->tok]); 1952f73abda9Skristaps } 1953f73abda9Skristaps 195449d529b5Sschwarze assert(n); 19554602e85cSschwarze if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok) 1956f73abda9Skristaps return(1); 1957f73abda9Skristaps 195851fcab2fSschwarze mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, 195951fcab2fSschwarze n->line, n->pos, mdoc_macronames[n->tok]); 196020fa2881Sschwarze return(1); 196120fa2881Sschwarze } 1962f73abda9Skristaps 1963f73abda9Skristaps static int 1964f73abda9Skristaps post_sh_head(POST_ARGS) 1965f73abda9Skristaps { 1966a2cff342Sschwarze struct mdoc_node *n; 196751fcab2fSschwarze const char *goodsec; 196846133849Sschwarze char *secname; 1969f73abda9Skristaps enum mdoc_sec sec; 1970f73abda9Skristaps 1971f73abda9Skristaps /* 1972f73abda9Skristaps * Process a new section. Sections are either "named" or 197320fa2881Sschwarze * "custom". Custom sections are user-defined, while named ones 197420fa2881Sschwarze * follow a conventional order and may only appear in certain 197520fa2881Sschwarze * manual sections. 1976f73abda9Skristaps */ 1977f73abda9Skristaps 197883af2bccSschwarze secname = NULL; 197904e980cbSschwarze sec = SEC_CUSTOM; 198046133849Sschwarze mdoc_deroff(&secname, mdoc->last); 198146133849Sschwarze sec = NULL == secname ? SEC_CUSTOM : a2sec(secname); 1982f73abda9Skristaps 198320fa2881Sschwarze /* The NAME should be first. */ 1984f73abda9Skristaps 1985fccfce9dSschwarze if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed) 198651fcab2fSschwarze mandoc_msg(MANDOCERR_NAMESEC_FIRST, mdoc->parse, 198751fcab2fSschwarze mdoc->last->line, mdoc->last->pos, secname); 198820fa2881Sschwarze 198920fa2881Sschwarze /* The SYNOPSIS gets special attention in other areas. */ 199020fa2881Sschwarze 199122881299Sschwarze if (SEC_SYNOPSIS == sec) { 199275088a49Sschwarze roff_setreg(mdoc->roff, "nS", 1, '='); 199320fa2881Sschwarze mdoc->flags |= MDOC_SYNOPSIS; 199422881299Sschwarze } else { 199575088a49Sschwarze roff_setreg(mdoc->roff, "nS", 0, '='); 199620fa2881Sschwarze mdoc->flags &= ~MDOC_SYNOPSIS; 199722881299Sschwarze } 199820fa2881Sschwarze 199920fa2881Sschwarze /* Mark our last section. */ 200020fa2881Sschwarze 200120fa2881Sschwarze mdoc->lastsec = sec; 20021eccdf28Sschwarze 20031eccdf28Sschwarze /* 20041eccdf28Sschwarze * Set the section attribute for the current HEAD, for its 20051eccdf28Sschwarze * parent BLOCK, and for the HEAD children; the latter can 20061eccdf28Sschwarze * only be TEXT nodes, so no recursion is needed. 20071eccdf28Sschwarze * For other blocks and elements, including .Sh BODY, this is 20081eccdf28Sschwarze * done when allocating the node data structures, but for .Sh 20091eccdf28Sschwarze * BLOCK and HEAD, the section is still unknown at that time. 20101eccdf28Sschwarze */ 20111eccdf28Sschwarze 2012a2cff342Sschwarze mdoc->last->parent->sec = sec; 2013a2cff342Sschwarze mdoc->last->sec = sec; 2014a2cff342Sschwarze for (n = mdoc->last->child; n; n = n->next) 2015a2cff342Sschwarze n->sec = sec; 201620fa2881Sschwarze 201720fa2881Sschwarze /* We don't care about custom sections after this. */ 2018fccfce9dSschwarze 201946133849Sschwarze if (SEC_CUSTOM == sec) { 202046133849Sschwarze free(secname); 2021f73abda9Skristaps return(1); 202246133849Sschwarze } 2023fccfce9dSschwarze 20246be99f77Sschwarze /* 202520fa2881Sschwarze * Check whether our non-custom section is being repeated or is 202620fa2881Sschwarze * out of order. 20276be99f77Sschwarze */ 2028f73abda9Skristaps 202920fa2881Sschwarze if (sec == mdoc->lastnamed) 203051fcab2fSschwarze mandoc_msg(MANDOCERR_SEC_REP, mdoc->parse, 203151fcab2fSschwarze mdoc->last->line, mdoc->last->pos, secname); 203220fa2881Sschwarze 203320fa2881Sschwarze if (sec < mdoc->lastnamed) 203451fcab2fSschwarze mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->parse, 203551fcab2fSschwarze mdoc->last->line, mdoc->last->pos, secname); 203620fa2881Sschwarze 203720fa2881Sschwarze /* Mark the last named section. */ 203820fa2881Sschwarze 203920fa2881Sschwarze mdoc->lastnamed = sec; 204020fa2881Sschwarze 204120fa2881Sschwarze /* Check particular section/manual conventions. */ 204220fa2881Sschwarze 204392c0ca7fSschwarze assert(mdoc->meta.msec); 204420fa2881Sschwarze 204551fcab2fSschwarze goodsec = NULL; 204620fa2881Sschwarze switch (sec) { 204749aff9f8Sschwarze case SEC_ERRORS: 2048be89e780Sschwarze if (*mdoc->meta.msec == '4') 2049be89e780Sschwarze break; 205051fcab2fSschwarze goodsec = "2, 3, 4, 9"; 2051be89e780Sschwarze /* FALLTHROUGH */ 205249aff9f8Sschwarze case SEC_RETURN_VALUES: 205320fa2881Sschwarze /* FALLTHROUGH */ 205449aff9f8Sschwarze case SEC_LIBRARY: 205592c0ca7fSschwarze if (*mdoc->meta.msec == '2') 2056f73abda9Skristaps break; 205792c0ca7fSschwarze if (*mdoc->meta.msec == '3') 205892c0ca7fSschwarze break; 205951fcab2fSschwarze if (NULL == goodsec) 206051fcab2fSschwarze goodsec = "2, 3, 9"; 206103ab2f23Sdlg /* FALLTHROUGH */ 206249aff9f8Sschwarze case SEC_CONTEXT: 206392c0ca7fSschwarze if (*mdoc->meta.msec == '9') 206492c0ca7fSschwarze break; 206551fcab2fSschwarze if (NULL == goodsec) 206651fcab2fSschwarze goodsec = "9"; 206751fcab2fSschwarze mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse, 206851fcab2fSschwarze mdoc->last->line, mdoc->last->pos, 206951fcab2fSschwarze "%s for %s only", secname, goodsec); 207020fa2881Sschwarze break; 2071f73abda9Skristaps default: 2072f73abda9Skristaps break; 2073f73abda9Skristaps } 2074f73abda9Skristaps 207546133849Sschwarze free(secname); 2076f73abda9Skristaps return(1); 2077f73abda9Skristaps } 2078d39b9a9cSschwarze 207920fa2881Sschwarze static int 2080f6127a73Sschwarze post_ignpar(POST_ARGS) 2081f6127a73Sschwarze { 2082f6127a73Sschwarze struct mdoc_node *np; 2083f6127a73Sschwarze 2084f6127a73Sschwarze if (MDOC_BODY != mdoc->last->type) 2085f6127a73Sschwarze return(1); 2086f6127a73Sschwarze 2087f6127a73Sschwarze if (NULL != (np = mdoc->last->child)) 2088f6127a73Sschwarze if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { 208920369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, 209020369664Sschwarze mdoc->parse, np->line, np->pos, 209120369664Sschwarze "%s after %s", mdoc_macronames[np->tok], 209220369664Sschwarze mdoc_macronames[mdoc->last->tok]); 2093f6127a73Sschwarze mdoc_node_delete(mdoc, np); 2094f6127a73Sschwarze } 2095f6127a73Sschwarze 2096f6127a73Sschwarze if (NULL != (np = mdoc->last->last)) 2097f6127a73Sschwarze if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { 209820369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 209920369664Sschwarze np->line, np->pos, "%s at the end of %s", 210020369664Sschwarze mdoc_macronames[np->tok], 210120369664Sschwarze mdoc_macronames[mdoc->last->tok]); 2102f6127a73Sschwarze mdoc_node_delete(mdoc, np); 2103f6127a73Sschwarze } 2104f6127a73Sschwarze 2105f6127a73Sschwarze return(1); 2106f6127a73Sschwarze } 2107f6127a73Sschwarze 2108f6127a73Sschwarze static int 210920fa2881Sschwarze pre_par(PRE_ARGS) 2110d39b9a9cSschwarze { 2111d39b9a9cSschwarze 2112d39b9a9cSschwarze if (NULL == mdoc->last) 2113d39b9a9cSschwarze return(1); 2114f6127a73Sschwarze if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type) 2115f6127a73Sschwarze return(1); 2116d39b9a9cSschwarze 211720fa2881Sschwarze /* 211820fa2881Sschwarze * Don't allow prior `Lp' or `Pp' prior to a paragraph-type 211920fa2881Sschwarze * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. 212020fa2881Sschwarze */ 2121d39b9a9cSschwarze 2122e0dd4c9cSschwarze if (MDOC_Pp != mdoc->last->tok && 2123e0dd4c9cSschwarze MDOC_Lp != mdoc->last->tok && 2124e0dd4c9cSschwarze MDOC_br != mdoc->last->tok) 2125d39b9a9cSschwarze return(1); 21268c62fbf5Sschwarze if (MDOC_Bl == n->tok && n->norm->Bl.comp) 2127d39b9a9cSschwarze return(1); 21288c62fbf5Sschwarze if (MDOC_Bd == n->tok && n->norm->Bd.comp) 2129d39b9a9cSschwarze return(1); 21308c62fbf5Sschwarze if (MDOC_It == n->tok && n->parent->norm->Bl.comp) 2131f6127a73Sschwarze return(1); 2132d39b9a9cSschwarze 213320369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 213420369664Sschwarze mdoc->last->line, mdoc->last->pos, 213520369664Sschwarze "%s before %s", mdoc_macronames[mdoc->last->tok], 213620369664Sschwarze mdoc_macronames[n->tok]); 2137d39b9a9cSschwarze mdoc_node_delete(mdoc, mdoc->last); 2138d39b9a9cSschwarze return(1); 2139d39b9a9cSschwarze } 214020fa2881Sschwarze 214120fa2881Sschwarze static int 2142e0dd4c9cSschwarze post_par(POST_ARGS) 2143e0dd4c9cSschwarze { 214420369664Sschwarze struct mdoc_node *np; 2145e0dd4c9cSschwarze 2146e0dd4c9cSschwarze if (MDOC_ELEM != mdoc->last->type && 2147e0dd4c9cSschwarze MDOC_BLOCK != mdoc->last->type) 2148e0dd4c9cSschwarze return(1); 2149e0dd4c9cSschwarze 215020369664Sschwarze if (NULL == (np = mdoc->last->prev)) { 215120369664Sschwarze np = mdoc->last->parent; 215220369664Sschwarze if (MDOC_Sh != np->tok && MDOC_Ss != np->tok) 2153e0dd4c9cSschwarze return(1); 2154e0dd4c9cSschwarze } else { 215520369664Sschwarze if (MDOC_Pp != np->tok && MDOC_Lp != np->tok && 2156e0dd4c9cSschwarze (MDOC_br != mdoc->last->tok || 215720369664Sschwarze (MDOC_sp != np->tok && MDOC_br != np->tok))) 2158e0dd4c9cSschwarze return(1); 2159e0dd4c9cSschwarze } 2160e0dd4c9cSschwarze 216120369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 216220369664Sschwarze mdoc->last->line, mdoc->last->pos, 216320369664Sschwarze "%s after %s", mdoc_macronames[mdoc->last->tok], 216420369664Sschwarze mdoc_macronames[np->tok]); 2165e0dd4c9cSschwarze mdoc_node_delete(mdoc, mdoc->last); 2166e0dd4c9cSschwarze return(1); 2167e0dd4c9cSschwarze } 2168e0dd4c9cSschwarze 2169e0dd4c9cSschwarze static int 217020fa2881Sschwarze pre_literal(PRE_ARGS) 217120fa2881Sschwarze { 217220fa2881Sschwarze 217320fa2881Sschwarze if (MDOC_BODY != n->type) 217420fa2881Sschwarze return(1); 217520fa2881Sschwarze 217620fa2881Sschwarze /* 217720fa2881Sschwarze * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd 217820fa2881Sschwarze * -unfilled' macros set MDOC_LITERAL on entrance to the body. 217920fa2881Sschwarze */ 218020fa2881Sschwarze 218120fa2881Sschwarze switch (n->tok) { 218249aff9f8Sschwarze case MDOC_Dl: 218320fa2881Sschwarze mdoc->flags |= MDOC_LITERAL; 218420fa2881Sschwarze break; 218549aff9f8Sschwarze case MDOC_Bd: 21868c62fbf5Sschwarze if (DISP_literal == n->norm->Bd.type) 218720fa2881Sschwarze mdoc->flags |= MDOC_LITERAL; 21888c62fbf5Sschwarze if (DISP_unfilled == n->norm->Bd.type) 218920fa2881Sschwarze mdoc->flags |= MDOC_LITERAL; 219020fa2881Sschwarze break; 219120fa2881Sschwarze default: 219220fa2881Sschwarze abort(); 219320fa2881Sschwarze /* NOTREACHED */ 219420fa2881Sschwarze } 219520fa2881Sschwarze 219620fa2881Sschwarze return(1); 219720fa2881Sschwarze } 219820fa2881Sschwarze 219920fa2881Sschwarze static int 220020fa2881Sschwarze post_dd(POST_ARGS) 220120fa2881Sschwarze { 220220fa2881Sschwarze struct mdoc_node *n; 220383af2bccSschwarze char *datestr; 220420fa2881Sschwarze 2205b058e777Sschwarze if (mdoc->meta.date) 2206b058e777Sschwarze free(mdoc->meta.date); 220720fa2881Sschwarze 2208b058e777Sschwarze n = mdoc->last; 2209b058e777Sschwarze if (NULL == n->child || '\0' == n->child->string[0]) { 2210231c7061Sschwarze mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : 2211231c7061Sschwarze mandoc_normdate(mdoc->parse, NULL, n->line, n->pos); 221220fa2881Sschwarze return(1); 221320fa2881Sschwarze } 221420fa2881Sschwarze 221583af2bccSschwarze datestr = NULL; 221683af2bccSschwarze mdoc_deroff(&datestr, n); 221783af2bccSschwarze if (mdoc->quick) 221883af2bccSschwarze mdoc->meta.date = datestr; 221983af2bccSschwarze else { 222083af2bccSschwarze mdoc->meta.date = mandoc_normdate(mdoc->parse, 222183af2bccSschwarze datestr, n->line, n->pos); 222283af2bccSschwarze free(datestr); 222304e980cbSschwarze } 222420fa2881Sschwarze return(1); 222520fa2881Sschwarze } 222620fa2881Sschwarze 222720fa2881Sschwarze static int 222820fa2881Sschwarze post_dt(POST_ARGS) 222920fa2881Sschwarze { 223020fa2881Sschwarze struct mdoc_node *nn, *n; 223120fa2881Sschwarze const char *cp; 223220fa2881Sschwarze char *p; 223320fa2881Sschwarze 223420fa2881Sschwarze n = mdoc->last; 223520fa2881Sschwarze 223620fa2881Sschwarze if (mdoc->meta.title) 223720fa2881Sschwarze free(mdoc->meta.title); 223820fa2881Sschwarze if (mdoc->meta.vol) 223920fa2881Sschwarze free(mdoc->meta.vol); 224020fa2881Sschwarze if (mdoc->meta.arch) 224120fa2881Sschwarze free(mdoc->meta.arch); 224220fa2881Sschwarze 224320fa2881Sschwarze mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL; 224420fa2881Sschwarze 224551fcab2fSschwarze /* First check that all characters are uppercase. */ 224620fa2881Sschwarze 224720fa2881Sschwarze if (NULL != (nn = n->child)) 224820fa2881Sschwarze for (p = nn->string; *p; p++) { 224904e980cbSschwarze if (toupper((unsigned char)*p) == *p) 225020fa2881Sschwarze continue; 225151fcab2fSschwarze mandoc_msg(MANDOCERR_TITLE_CASE, 225251fcab2fSschwarze mdoc->parse, nn->line, 225351fcab2fSschwarze nn->pos + (p - nn->string), 225451fcab2fSschwarze nn->string); 225520fa2881Sschwarze break; 225620fa2881Sschwarze } 225720fa2881Sschwarze 225820fa2881Sschwarze /* Handles: `.Dt' 225949aff9f8Sschwarze * title = unknown, volume = local, msec = 0, arch = NULL 226020fa2881Sschwarze */ 226120fa2881Sschwarze 226220fa2881Sschwarze if (NULL == (nn = n->child)) { 226320fa2881Sschwarze /* XXX: make these macro values. */ 226420fa2881Sschwarze /* FIXME: warn about missing values. */ 226520fa2881Sschwarze mdoc->meta.title = mandoc_strdup("UNKNOWN"); 226620fa2881Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL"); 226720fa2881Sschwarze mdoc->meta.msec = mandoc_strdup("1"); 226820fa2881Sschwarze return(1); 226920fa2881Sschwarze } 227020fa2881Sschwarze 227120fa2881Sschwarze /* Handles: `.Dt TITLE' 227249aff9f8Sschwarze * title = TITLE, volume = local, msec = 0, arch = NULL 227320fa2881Sschwarze */ 227420fa2881Sschwarze 227549aff9f8Sschwarze mdoc->meta.title = mandoc_strdup( 227649aff9f8Sschwarze '\0' == nn->string[0] ? "UNKNOWN" : nn->string); 227720fa2881Sschwarze 227820fa2881Sschwarze if (NULL == (nn = nn->next)) { 227920fa2881Sschwarze /* FIXME: warn about missing msec. */ 228020fa2881Sschwarze /* XXX: make this a macro value. */ 228120fa2881Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL"); 228220fa2881Sschwarze mdoc->meta.msec = mandoc_strdup("1"); 228320fa2881Sschwarze return(1); 228420fa2881Sschwarze } 228520fa2881Sschwarze 228620fa2881Sschwarze /* Handles: `.Dt TITLE SEC' 228749aff9f8Sschwarze * title = TITLE, 228849aff9f8Sschwarze * volume = SEC is msec ? format(msec) : SEC, 228920fa2881Sschwarze * msec = SEC is msec ? atoi(msec) : 0, 229020fa2881Sschwarze * arch = NULL 229120fa2881Sschwarze */ 229220fa2881Sschwarze 229388ec69e3Sschwarze cp = mandoc_a2msec(nn->string); 229420fa2881Sschwarze if (cp) { 229520fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(cp); 229620fa2881Sschwarze mdoc->meta.msec = mandoc_strdup(nn->string); 229720fa2881Sschwarze } else { 229851fcab2fSschwarze mandoc_msg(MANDOCERR_MSEC_BAD, mdoc->parse, 229951fcab2fSschwarze nn->line, nn->pos, nn->string); 230020fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(nn->string); 230120fa2881Sschwarze mdoc->meta.msec = mandoc_strdup(nn->string); 230220fa2881Sschwarze } 230320fa2881Sschwarze 230420fa2881Sschwarze if (NULL == (nn = nn->next)) 230520fa2881Sschwarze return(1); 230620fa2881Sschwarze 230720fa2881Sschwarze /* Handles: `.Dt TITLE SEC VOL' 230849aff9f8Sschwarze * title = TITLE, 230949aff9f8Sschwarze * volume = VOL is vol ? format(VOL) : 231020fa2881Sschwarze * VOL is arch ? format(arch) : 231120fa2881Sschwarze * VOL 231220fa2881Sschwarze */ 231320fa2881Sschwarze 231420fa2881Sschwarze cp = mdoc_a2vol(nn->string); 231520fa2881Sschwarze if (cp) { 231620fa2881Sschwarze free(mdoc->meta.vol); 231720fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(cp); 231820fa2881Sschwarze } else { 231920fa2881Sschwarze cp = mdoc_a2arch(nn->string); 232020fa2881Sschwarze if (NULL == cp) { 232151fcab2fSschwarze mandoc_msg(MANDOCERR_ARCH_BAD, mdoc->parse, 232251fcab2fSschwarze nn->line, nn->pos, nn->string); 232320fa2881Sschwarze free(mdoc->meta.vol); 232420fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(nn->string); 232520fa2881Sschwarze } else 232620fa2881Sschwarze mdoc->meta.arch = mandoc_strdup(cp); 232720fa2881Sschwarze } 232820fa2881Sschwarze 232920fa2881Sschwarze /* Ignore any subsequent parameters... */ 233020fa2881Sschwarze /* FIXME: warn about subsequent parameters. */ 233120fa2881Sschwarze 233220fa2881Sschwarze return(1); 233320fa2881Sschwarze } 233420fa2881Sschwarze 233520fa2881Sschwarze static int 233620fa2881Sschwarze post_prol(POST_ARGS) 233720fa2881Sschwarze { 233820fa2881Sschwarze /* 233920fa2881Sschwarze * Remove prologue macros from the document after they're 234020fa2881Sschwarze * processed. The final document uses mdoc_meta for these 234120fa2881Sschwarze * values and discards the originals. 234220fa2881Sschwarze */ 234320fa2881Sschwarze 234420fa2881Sschwarze mdoc_node_delete(mdoc, mdoc->last); 234520fa2881Sschwarze if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os) 234620fa2881Sschwarze mdoc->flags |= MDOC_PBODY; 234720fa2881Sschwarze 234820fa2881Sschwarze return(1); 234920fa2881Sschwarze } 235020fa2881Sschwarze 235120fa2881Sschwarze static int 2352992063deSschwarze post_bx(POST_ARGS) 2353992063deSschwarze { 2354992063deSschwarze struct mdoc_node *n; 2355992063deSschwarze 2356992063deSschwarze /* 2357992063deSschwarze * Make `Bx's second argument always start with an uppercase 2358992063deSschwarze * letter. Groff checks if it's an "accepted" term, but we just 2359992063deSschwarze * uppercase blindly. 2360992063deSschwarze */ 2361992063deSschwarze 2362992063deSschwarze n = mdoc->last->child; 2363992063deSschwarze if (n && NULL != (n = n->next)) 236449aff9f8Sschwarze *n->string = (char)toupper((unsigned char)*n->string); 2365992063deSschwarze 2366992063deSschwarze return(1); 2367992063deSschwarze } 2368992063deSschwarze 2369992063deSschwarze static int 237020fa2881Sschwarze post_os(POST_ARGS) 237120fa2881Sschwarze { 237220fa2881Sschwarze #ifndef OSNAME 237320fa2881Sschwarze struct utsname utsname; 23744c468128Sschwarze static char *defbuf; 237520fa2881Sschwarze #endif 23764c468128Sschwarze struct mdoc_node *n; 237720fa2881Sschwarze 237820fa2881Sschwarze n = mdoc->last; 237920fa2881Sschwarze 238020fa2881Sschwarze /* 2381353fa9ecSschwarze * Set the operating system by way of the `Os' macro. 2382353fa9ecSschwarze * The order of precedence is: 2383353fa9ecSschwarze * 1. the argument of the `Os' macro, unless empty 2384353fa9ecSschwarze * 2. the -Ios=foo command line argument, if provided 2385353fa9ecSschwarze * 3. -DOSNAME="\"foo\"", if provided during compilation 2386353fa9ecSschwarze * 4. "sysname release" from uname(3) 238720fa2881Sschwarze */ 238820fa2881Sschwarze 238920fa2881Sschwarze free(mdoc->meta.os); 239083af2bccSschwarze mdoc->meta.os = NULL; 239183af2bccSschwarze mdoc_deroff(&mdoc->meta.os, n); 239283af2bccSschwarze if (mdoc->meta.os) 23934c468128Sschwarze return(1); 23944c468128Sschwarze 2395353fa9ecSschwarze if (mdoc->defos) { 2396353fa9ecSschwarze mdoc->meta.os = mandoc_strdup(mdoc->defos); 2397353fa9ecSschwarze return(1); 2398353fa9ecSschwarze } 23994c468128Sschwarze 240020fa2881Sschwarze #ifdef OSNAME 24014c468128Sschwarze mdoc->meta.os = mandoc_strdup(OSNAME); 240220fa2881Sschwarze #else /*!OSNAME */ 24034c468128Sschwarze if (NULL == defbuf) { 2404a35fc07aSschwarze if (-1 == uname(&utsname)) { 240520fa2881Sschwarze mdoc_nmsg(mdoc, n, MANDOCERR_UNAME); 24064c468128Sschwarze defbuf = mandoc_strdup("UNKNOWN"); 2407a450f7c4Sschwarze } else 2408a450f7c4Sschwarze mandoc_asprintf(&defbuf, "%s %s", 2409a450f7c4Sschwarze utsname.sysname, utsname.release); 241020fa2881Sschwarze } 24114c468128Sschwarze mdoc->meta.os = mandoc_strdup(defbuf); 241220fa2881Sschwarze #endif /*!OSNAME*/ 241320fa2881Sschwarze return(1); 241420fa2881Sschwarze } 241520fa2881Sschwarze 241620fa2881Sschwarze static int 241720fa2881Sschwarze post_std(POST_ARGS) 241820fa2881Sschwarze { 241920fa2881Sschwarze struct mdoc_node *nn, *n; 242020fa2881Sschwarze 242120fa2881Sschwarze n = mdoc->last; 242220fa2881Sschwarze 242320fa2881Sschwarze /* 242420fa2881Sschwarze * Macros accepting `-std' as an argument have the name of the 242520fa2881Sschwarze * current document (`Nm') filled in as the argument if it's not 242620fa2881Sschwarze * provided. 242720fa2881Sschwarze */ 242820fa2881Sschwarze 242920fa2881Sschwarze if (n->child) 243020fa2881Sschwarze return(1); 243120fa2881Sschwarze 243220fa2881Sschwarze if (NULL == mdoc->meta.name) 243320fa2881Sschwarze return(1); 243420fa2881Sschwarze 243520fa2881Sschwarze nn = n; 243620fa2881Sschwarze mdoc->next = MDOC_NEXT_CHILD; 243720fa2881Sschwarze 243820fa2881Sschwarze if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name)) 243920fa2881Sschwarze return(0); 244020fa2881Sschwarze 244120fa2881Sschwarze mdoc->last = nn; 244220fa2881Sschwarze return(1); 244320fa2881Sschwarze } 244420fa2881Sschwarze 244519a69263Sschwarze static enum mdoc_sec 244619a69263Sschwarze a2sec(const char *p) 244719a69263Sschwarze { 244819a69263Sschwarze int i; 244919a69263Sschwarze 245019a69263Sschwarze for (i = 0; i < (int)SEC__MAX; i++) 245119a69263Sschwarze if (secnames[i] && 0 == strcmp(p, secnames[i])) 245219a69263Sschwarze return((enum mdoc_sec)i); 245319a69263Sschwarze 245419a69263Sschwarze return(SEC_CUSTOM); 245519a69263Sschwarze } 245619a69263Sschwarze 245719a69263Sschwarze static size_t 245819a69263Sschwarze macro2len(enum mdoct macro) 245919a69263Sschwarze { 246019a69263Sschwarze 246119a69263Sschwarze switch (macro) { 246249aff9f8Sschwarze case MDOC_Ad: 246319a69263Sschwarze return(12); 246449aff9f8Sschwarze case MDOC_Ao: 246519a69263Sschwarze return(12); 246649aff9f8Sschwarze case MDOC_An: 246719a69263Sschwarze return(12); 246849aff9f8Sschwarze case MDOC_Aq: 246919a69263Sschwarze return(12); 247049aff9f8Sschwarze case MDOC_Ar: 247119a69263Sschwarze return(12); 247249aff9f8Sschwarze case MDOC_Bo: 247319a69263Sschwarze return(12); 247449aff9f8Sschwarze case MDOC_Bq: 247519a69263Sschwarze return(12); 247649aff9f8Sschwarze case MDOC_Cd: 247719a69263Sschwarze return(12); 247849aff9f8Sschwarze case MDOC_Cm: 247919a69263Sschwarze return(10); 248049aff9f8Sschwarze case MDOC_Do: 248119a69263Sschwarze return(10); 248249aff9f8Sschwarze case MDOC_Dq: 248319a69263Sschwarze return(12); 248449aff9f8Sschwarze case MDOC_Dv: 248519a69263Sschwarze return(12); 248649aff9f8Sschwarze case MDOC_Eo: 248719a69263Sschwarze return(12); 248849aff9f8Sschwarze case MDOC_Em: 248919a69263Sschwarze return(10); 249049aff9f8Sschwarze case MDOC_Er: 249119a69263Sschwarze return(17); 249249aff9f8Sschwarze case MDOC_Ev: 249319a69263Sschwarze return(15); 249449aff9f8Sschwarze case MDOC_Fa: 249519a69263Sschwarze return(12); 249649aff9f8Sschwarze case MDOC_Fl: 249719a69263Sschwarze return(10); 249849aff9f8Sschwarze case MDOC_Fo: 249919a69263Sschwarze return(16); 250049aff9f8Sschwarze case MDOC_Fn: 250119a69263Sschwarze return(16); 250249aff9f8Sschwarze case MDOC_Ic: 250319a69263Sschwarze return(10); 250449aff9f8Sschwarze case MDOC_Li: 250519a69263Sschwarze return(16); 250649aff9f8Sschwarze case MDOC_Ms: 250719a69263Sschwarze return(6); 250849aff9f8Sschwarze case MDOC_Nm: 250919a69263Sschwarze return(10); 251049aff9f8Sschwarze case MDOC_No: 251119a69263Sschwarze return(12); 251249aff9f8Sschwarze case MDOC_Oo: 251319a69263Sschwarze return(10); 251449aff9f8Sschwarze case MDOC_Op: 251519a69263Sschwarze return(14); 251649aff9f8Sschwarze case MDOC_Pa: 251719a69263Sschwarze return(32); 251849aff9f8Sschwarze case MDOC_Pf: 251919a69263Sschwarze return(12); 252049aff9f8Sschwarze case MDOC_Po: 252119a69263Sschwarze return(12); 252249aff9f8Sschwarze case MDOC_Pq: 252319a69263Sschwarze return(12); 252449aff9f8Sschwarze case MDOC_Ql: 252519a69263Sschwarze return(16); 252649aff9f8Sschwarze case MDOC_Qo: 252719a69263Sschwarze return(12); 252849aff9f8Sschwarze case MDOC_So: 252919a69263Sschwarze return(12); 253049aff9f8Sschwarze case MDOC_Sq: 253119a69263Sschwarze return(12); 253249aff9f8Sschwarze case MDOC_Sy: 253319a69263Sschwarze return(6); 253449aff9f8Sschwarze case MDOC_Sx: 253519a69263Sschwarze return(16); 253649aff9f8Sschwarze case MDOC_Tn: 253719a69263Sschwarze return(10); 253849aff9f8Sschwarze case MDOC_Va: 253919a69263Sschwarze return(12); 254049aff9f8Sschwarze case MDOC_Vt: 254119a69263Sschwarze return(12); 254249aff9f8Sschwarze case MDOC_Xr: 254319a69263Sschwarze return(10); 254419a69263Sschwarze default: 254519a69263Sschwarze break; 254619a69263Sschwarze }; 254719a69263Sschwarze return(0); 254819a69263Sschwarze } 2549