GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/mdoc_validate.c Lines: 0 823 0.0 %
Date: 2016-12-06 Branches: 0 678 0.0 %

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