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