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