GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/mdoc_validate.c Lines: 1288 1383 93.1 %
Date: 2017-11-07 Branches: 867 1029 84.3 %

Line Branch Exec Source
1
/*	$OpenBSD: mdoc_validate.c,v 1.268 2017/09/12 18:20:32 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4
 * Copyright (c) 2010-2017 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 "mandoc_xr.h"
35
#include "roff.h"
36
#include "mdoc.h"
37
#include "libmandoc.h"
38
#include "roff_int.h"
39
#include "libmdoc.h"
40
41
/* FIXME: .Bl -diag can't have non-text children in HEAD. */
42
43
#define	POST_ARGS struct roff_man *mdoc
44
45
enum	check_ineq {
46
	CHECK_LT,
47
	CHECK_GT,
48
	CHECK_EQ
49
};
50
51
typedef	void	(*v_post)(POST_ARGS);
52
53
static	int	 build_list(struct roff_man *, int);
54
static	void	 check_text(struct roff_man *, int, int, char *);
55
static	void	 check_argv(struct roff_man *,
56
			struct roff_node *, struct mdoc_argv *);
57
static	void	 check_args(struct roff_man *, struct roff_node *);
58
static	void	 check_toptext(struct roff_man *, int, int, const char *);
59
static	int	 child_an(const struct roff_node *);
60
static	size_t		macro2len(enum roff_tok);
61
static	void	 rewrite_macro2len(struct roff_man *, char **);
62
static	int	 similar(const char *, const char *);
63
64
static	void	 post_an(POST_ARGS);
65
static	void	 post_an_norm(POST_ARGS);
66
static	void	 post_at(POST_ARGS);
67
static	void	 post_bd(POST_ARGS);
68
static	void	 post_bf(POST_ARGS);
69
static	void	 post_bk(POST_ARGS);
70
static	void	 post_bl(POST_ARGS);
71
static	void	 post_bl_block(POST_ARGS);
72
static	void	 post_bl_head(POST_ARGS);
73
static	void	 post_bl_norm(POST_ARGS);
74
static	void	 post_bx(POST_ARGS);
75
static	void	 post_defaults(POST_ARGS);
76
static	void	 post_display(POST_ARGS);
77
static	void	 post_dd(POST_ARGS);
78
static	void	 post_delim(POST_ARGS);
79
static	void	 post_delim_nb(POST_ARGS);
80
static	void	 post_dt(POST_ARGS);
81
static	void	 post_en(POST_ARGS);
82
static	void	 post_es(POST_ARGS);
83
static	void	 post_eoln(POST_ARGS);
84
static	void	 post_ex(POST_ARGS);
85
static	void	 post_fa(POST_ARGS);
86
static	void	 post_fn(POST_ARGS);
87
static	void	 post_fname(POST_ARGS);
88
static	void	 post_fo(POST_ARGS);
89
static	void	 post_hyph(POST_ARGS);
90
static	void	 post_ignpar(POST_ARGS);
91
static	void	 post_it(POST_ARGS);
92
static	void	 post_lb(POST_ARGS);
93
static	void	 post_nd(POST_ARGS);
94
static	void	 post_nm(POST_ARGS);
95
static	void	 post_ns(POST_ARGS);
96
static	void	 post_obsolete(POST_ARGS);
97
static	void	 post_os(POST_ARGS);
98
static	void	 post_par(POST_ARGS);
99
static	void	 post_prevpar(POST_ARGS);
100
static	void	 post_root(POST_ARGS);
101
static	void	 post_rs(POST_ARGS);
102
static	void	 post_rv(POST_ARGS);
103
static	void	 post_sh(POST_ARGS);
104
static	void	 post_sh_head(POST_ARGS);
105
static	void	 post_sh_name(POST_ARGS);
106
static	void	 post_sh_see_also(POST_ARGS);
107
static	void	 post_sh_authors(POST_ARGS);
108
static	void	 post_sm(POST_ARGS);
109
static	void	 post_st(POST_ARGS);
110
static	void	 post_std(POST_ARGS);
111
static	void	 post_sx(POST_ARGS);
112
static	void	 post_useless(POST_ARGS);
113
static	void	 post_xr(POST_ARGS);
114
static	void	 post_xx(POST_ARGS);
115
116
static	const v_post __mdoc_valids[MDOC_MAX - MDOC_Dd] = {
117
	post_dd,	/* Dd */
118
	post_dt,	/* Dt */
119
	post_os,	/* Os */
120
	post_sh,	/* Sh */
121
	post_ignpar,	/* Ss */
122
	post_par,	/* Pp */
123
	post_display,	/* D1 */
124
	post_display,	/* Dl */
125
	post_display,	/* Bd */
126
	NULL,		/* Ed */
127
	post_bl,	/* Bl */
128
	NULL,		/* El */
129
	post_it,	/* It */
130
	post_delim_nb,	/* Ad */
131
	post_an,	/* An */
132
	NULL,		/* Ap */
133
	post_defaults,	/* Ar */
134
	NULL,		/* Cd */
135
	post_delim_nb,	/* Cm */
136
	post_delim_nb,	/* Dv */
137
	post_delim_nb,	/* Er */
138
	post_delim_nb,	/* Ev */
139
	post_ex,	/* Ex */
140
	post_fa,	/* Fa */
141
	NULL,		/* Fd */
142
	post_delim_nb,	/* Fl */
143
	post_fn,	/* Fn */
144
	post_delim_nb,	/* Ft */
145
	post_delim_nb,	/* Ic */
146
	post_delim_nb,	/* In */
147
	post_defaults,	/* Li */
148
	post_nd,	/* Nd */
149
	post_nm,	/* Nm */
150
	post_delim_nb,	/* Op */
151
	post_obsolete,	/* Ot */
152
	post_defaults,	/* Pa */
153
	post_rv,	/* Rv */
154
	post_st,	/* St */
155
	post_delim_nb,	/* Va */
156
	post_delim_nb,	/* Vt */
157
	post_xr,	/* Xr */
158
	NULL,		/* %A */
159
	post_hyph,	/* %B */ /* FIXME: can be used outside Rs/Re. */
160
	NULL,		/* %D */
161
	NULL,		/* %I */
162
	NULL,		/* %J */
163
	post_hyph,	/* %N */
164
	post_hyph,	/* %O */
165
	NULL,		/* %P */
166
	post_hyph,	/* %R */
167
	post_hyph,	/* %T */ /* FIXME: can be used outside Rs/Re. */
168
	NULL,		/* %V */
169
	NULL,		/* Ac */
170
	post_delim_nb,	/* Ao */
171
	post_delim_nb,	/* Aq */
172
	post_at,	/* At */
173
	NULL,		/* Bc */
174
	post_bf,	/* Bf */
175
	post_delim_nb,	/* Bo */
176
	NULL,		/* Bq */
177
	post_xx,	/* Bsx */
178
	post_bx,	/* Bx */
179
	post_obsolete,	/* Db */
180
	NULL,		/* Dc */
181
	NULL,		/* Do */
182
	NULL,		/* Dq */
183
	NULL,		/* Ec */
184
	NULL,		/* Ef */
185
	post_delim_nb,	/* Em */
186
	NULL,		/* Eo */
187
	post_xx,	/* Fx */
188
	post_delim_nb,	/* Ms */
189
	NULL,		/* No */
190
	post_ns,	/* Ns */
191
	post_xx,	/* Nx */
192
	post_xx,	/* Ox */
193
	NULL,		/* Pc */
194
	NULL,		/* Pf */
195
	post_delim_nb,	/* Po */
196
	post_delim_nb,	/* Pq */
197
	NULL,		/* Qc */
198
	post_delim_nb,	/* Ql */
199
	post_delim_nb,	/* Qo */
200
	post_delim_nb,	/* Qq */
201
	NULL,		/* Re */
202
	post_rs,	/* Rs */
203
	NULL,		/* Sc */
204
	post_delim_nb,	/* So */
205
	post_delim_nb,	/* Sq */
206
	post_sm,	/* Sm */
207
	post_sx,	/* Sx */
208
	post_delim_nb,	/* Sy */
209
	post_useless,	/* Tn */
210
	post_xx,	/* Ux */
211
	NULL,		/* Xc */
212
	NULL,		/* Xo */
213
	post_fo,	/* Fo */
214
	NULL,		/* Fc */
215
	post_delim_nb,	/* Oo */
216
	NULL,		/* Oc */
217
	post_bk,	/* Bk */
218
	NULL,		/* Ek */
219
	post_eoln,	/* Bt */
220
	post_obsolete,	/* Hf */
221
	post_obsolete,	/* Fr */
222
	post_eoln,	/* Ud */
223
	post_lb,	/* Lb */
224
	post_par,	/* Lp */
225
	post_delim_nb,	/* Lk */
226
	post_defaults,	/* Mt */
227
	post_delim_nb,	/* Brq */
228
	post_delim_nb,	/* Bro */
229
	NULL,		/* Brc */
230
	NULL,		/* %C */
231
	post_es,	/* Es */
232
	post_en,	/* En */
233
	post_xx,	/* Dx */
234
	NULL,		/* %Q */
235
	NULL,		/* %U */
236
	NULL,		/* Ta */
237
};
238
static	const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd;
239
240
#define	RSORD_MAX 14 /* Number of `Rs' blocks. */
241
242
static	const enum roff_tok rsord[RSORD_MAX] = {
243
	MDOC__A,
244
	MDOC__T,
245
	MDOC__B,
246
	MDOC__I,
247
	MDOC__J,
248
	MDOC__R,
249
	MDOC__N,
250
	MDOC__V,
251
	MDOC__U,
252
	MDOC__P,
253
	MDOC__Q,
254
	MDOC__C,
255
	MDOC__D,
256
	MDOC__O
257
};
258
259
static	const char * const secnames[SEC__MAX] = {
260
	NULL,
261
	"NAME",
262
	"LIBRARY",
263
	"SYNOPSIS",
264
	"DESCRIPTION",
265
	"CONTEXT",
266
	"IMPLEMENTATION NOTES",
267
	"RETURN VALUES",
268
	"ENVIRONMENT",
269
	"FILES",
270
	"EXIT STATUS",
271
	"EXAMPLES",
272
	"DIAGNOSTICS",
273
	"COMPATIBILITY",
274
	"ERRORS",
275
	"SEE ALSO",
276
	"STANDARDS",
277
	"HISTORY",
278
	"AUTHORS",
279
	"CAVEATS",
280
	"BUGS",
281
	"SECURITY CONSIDERATIONS",
282
	NULL
283
};
284
285
286
void
287
mdoc_node_validate(struct roff_man *mdoc)
288
{
289
	struct roff_node *n;
290
	const v_post *p;
291
292
7192472
	n = mdoc->last;
293
3596236
	mdoc->last = mdoc->last->child;
294
10771458
	while (mdoc->last != NULL) {
295
3578986
		mdoc_node_validate(mdoc);
296
3578986
		if (mdoc->last == n)
297
216
			mdoc->last = mdoc->last->child;
298
		else
299
3578770
			mdoc->last = mdoc->last->next;
300
	}
301
302
7162493
	mdoc->last = n;
303
7162493
	mdoc->next = ROFF_NEXT_SIBLING;
304

7162493
	switch (n->type) {
305
	case ROFFT_TEXT:
306

1966281
		if (n->sec != SEC_SYNOPSIS ||
307
250358
		    (n->parent->tok != MDOC_Cd && n->parent->tok != MDOC_Fd))
308
1837516
			check_text(mdoc, n->line, n->pos, n->string);
309

2005014
		if (n->parent->tok == MDOC_It ||
310
1496291
		    (n->parent->type == ROFFT_BODY &&
311
605326
		     (n->parent->tok == MDOC_Sh ||
312
161218
		      n->parent->tok == MDOC_Ss)))
313
833491
			check_toptext(mdoc, n->line, n->pos, n->string);
314
		break;
315
	case ROFFT_EQN:
316
	case ROFFT_TBL:
317
		break;
318
	case ROFFT_ROOT:
319
17250
		post_root(mdoc);
320
17250
		break;
321
	default:
322
1715801
		check_args(mdoc, mdoc->last);
323
324
		/*
325
		 * Closing delimiters are not special at the
326
		 * beginning of a block, opening delimiters
327
		 * are not special at the end.
328
		 */
329
330
1715801
		if (n->child != NULL)
331
1419680
			n->child->flags &= ~NODE_DELIMC;
332
1715801
		if (n->last != NULL)
333
1419680
			n->last->flags &= ~NODE_DELIMO;
334
335
		/* Call the macro's postprocessor. */
336
337
1715801
		if (n->tok < ROFF_MAX) {
338
10590
			switch(n->tok) {
339
			case ROFF_br:
340
			case ROFF_sp:
341
10272
				post_par(mdoc);
342
10272
				break;
343
			default:
344
318
				roff_validate(mdoc);
345
318
				break;
346
			}
347
			break;
348
		}
349
350

3410422
		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
351
1705211
		p = mdoc_valids + n->tok;
352
1705211
		if (*p)
353
1628563
			(*p)(mdoc);
354
1705211
		if (mdoc->last == n)
355
1704041
			mdoc_state(mdoc, n);
356
		break;
357
	}
358
3596236
}
359
360
static void
361
check_args(struct roff_man *mdoc, struct roff_node *n)
362
{
363
	int		 i;
364
365
3431602
	if (NULL == n->args)
366
1682053
		return;
367
368
33748
	assert(n->args->argc);
369
202672
	for (i = 0; i < (int)n->args->argc; i++)
370
67588
		check_argv(mdoc, n, &n->args->argv[i]);
371
1749549
}
372
373
static void
374
check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
375
{
376
	int		 i;
377
378
263666
	for (i = 0; i < (int)v->sz; i++)
379
30451
		check_text(mdoc, v->line, v->pos, v->value[i]);
380
67588
}
381
382
static void
383
check_text(struct roff_man *mdoc, int ln, int pos, char *p)
384
{
385
	char		*cp;
386
387
3735934
	if (MDOC_LITERAL & mdoc->flags)
388
55352
		return;
389
390
3627066
	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
391
1836
		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
392
918
		    ln, pos + (int)(p - cp), NULL);
393
3680582
}
394
395
static void
396
check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
397
{
398
	const char	*cp, *cpr;
399
400
1666982
	if (*p == '\0')
401
1407
		return;
402
403
832084
	if ((cp = strstr(p, "OpenBSD")) != NULL)
404
114
		mandoc_msg(MANDOCERR_BX, mdoc->parse,
405
57
		    ln, pos + (cp - p), "Ox");
406
832084
	if ((cp = strstr(p, "NetBSD")) != NULL)
407
		mandoc_msg(MANDOCERR_BX, mdoc->parse,
408
		    ln, pos + (cp - p), "Nx");
409
832084
	if ((cp = strstr(p, "FreeBSD")) != NULL)
410
6
		mandoc_msg(MANDOCERR_BX, mdoc->parse,
411
3
		    ln, pos + (cp - p), "Fx");
412
832084
	if ((cp = strstr(p, "DragonFly")) != NULL)
413
		mandoc_msg(MANDOCERR_BX, mdoc->parse,
414
		    ln, pos + (cp - p), "Dx");
415
416
	cp = p;
417
1664283
	while ((cp = strstr(cp + 1, "()")) != NULL) {
418
1586
		for (cpr = cp - 1; cpr >= p; cpr--)
419

1502
			if (*cpr != '_' && !isalnum((unsigned char)*cpr))
420
				break;
421

312
		if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
422
106
			cpr++;
423
212
			mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse,
424
106
			    ln, pos + (cpr - p),
425
106
			    "%.*s()", (int)(cp - cpr), cpr);
426
106
		}
427
	}
428
1665575
}
429
430
static void
431
post_delim(POST_ARGS)
432
{
433
	const struct roff_node	*nch;
434
	const char		*lc;
435
	enum mdelim		 delim;
436
	enum roff_tok		 tok;
437
438
256704
	tok = mdoc->last->tok;
439
128352
	nch = mdoc->last->last;
440

238332
	if (nch == NULL || nch->type != ROFFT_TEXT)
441
18555
		return;
442
109797
	lc = strchr(nch->string, '\0') - 1;
443
109797
	if (lc < nch->string)
444
		return;
445
109797
	delim = mdoc_isdelim(lc);
446
109797
	if (delim == DELIM_NONE || delim == DELIM_OPEN)
447
109281
		return;
448

1470
	if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
449
636
	    tok == MDOC_Ss || tok == MDOC_Fo))
450
318
		return;
451
452
396
	mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse,
453
198
	    nch->line, nch->pos + (lc - nch->string),
454
198
	    "%s%s %s", roff_name[tok],
455
198
	    nch == mdoc->last->child ? "" : " ...", nch->string);
456
128550
}
457
458
static void
459
post_delim_nb(POST_ARGS)
460
{
461
	const struct roff_node	*nch;
462
	const char		*lc, *cp;
463
	int			 nw;
464
	enum mdelim		 delim;
465
	enum roff_tok		 tok;
466
467
	/*
468
	 * Find candidates: at least two bytes,
469
	 * the last one a closing or middle delimiter.
470
	 */
471
472
1371782
	tok = mdoc->last->tok;
473
685891
	nch = mdoc->last->last;
474

1291139
	if (nch == NULL || nch->type != ROFFT_TEXT)
475
153389
		return;
476
532502
	lc = strchr(nch->string, '\0') - 1;
477
532502
	if (lc <= nch->string)
478
145094
		return;
479
387408
	delim = mdoc_isdelim(lc);
480
387408
	if (delim == DELIM_NONE || delim == DELIM_OPEN)
481
379868
		return;
482
483
	/*
484
	 * Reduce false positives by allowing various cases.
485
	 */
486
487
	/* Escaped delimiters. */
488

14378
	if (lc > nch->string + 1 && lc[-2] == '\\' &&
489
1404
	    (lc[-1] == '&' || lc[-1] == 'e'))
490
1365
		return;
491
492
	/* Specific byte sequences. */
493

7832
	switch (*lc) {
494
	case ')':
495
31702
		for (cp = lc; cp >= nch->string; cp--)
496
15557
			if (*cp == '(')
497
841
				return;
498
		break;
499
	case '.':
500

7982
		if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
501
2000
			return;
502
1126
		if (lc[-1] == '.')
503
87
			return;
504
		break;
505
	case ';':
506
123
		if (tok == MDOC_Vt)
507
84
			return;
508
		break;
509
	case '?':
510
249
		if (lc[-1] == '?')
511
33
			return;
512
		break;
513
	case ']':
514
8034
		for (cp = lc; cp >= nch->string; cp--)
515
3960
			if (*cp == '[')
516
618
				return;
517
		break;
518
	case '|':
519

186
		if (lc == nch->string + 1 && lc[-1] == '|')
520
81
			return;
521
	default:
522
		break;
523
	}
524
525
	/* Exactly two non-alphanumeric bytes. */
526

2998
	if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
527
210
		return;
528
529
	/* At least three alphabetic words with a sentence ending. */
530

10236
	if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
531
4809
	    tok == MDOC_Li || tok == MDOC_Po || tok == MDOC_Pq ||
532
1603
	    tok == MDOC_Sy)) {
533
		nw = 0;
534
4506
		for (cp = lc - 1; cp >= nch->string; cp--) {
535
2007
			if (*cp == ' ') {
536
176
				nw++;
537

352
				if (cp > nch->string && cp[-1] == ',')
538
18
					cp--;
539
1831
			} else if (isalpha((unsigned int)*cp)) {
540
1804
				if (nw > 1)
541
70
					return;
542
			} else
543
				break;
544
		}
545
	}
546
547
4302
	mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse,
548
2151
	    nch->line, nch->pos + (lc - nch->string),
549
2151
	    "%s%s %s", roff_name[tok],
550
2151
	    nch == mdoc->last->child ? "" : " ...", nch->string);
551
688042
}
552
553
static void
554
post_bl_norm(POST_ARGS)
555
{
556
	struct roff_node *n;
557
	struct mdoc_argv *argv, *wa;
558
	int		  i;
559
	enum mdocargt	  mdoclt;
560
	enum mdoc_list	  lt;
561
562
43780
	n = mdoc->last->parent;
563
21890
	n->norm->Bl.type = LIST__NONE;
564
565
	/*
566
	 * First figure out which kind of list to use: bind ourselves to
567
	 * the first mentioned list type and warn about any remaining
568
	 * ones.  If we find no list type, we default to LIST_item.
569
	 */
570
571
65634
	wa = (n->args == NULL) ? NULL : n->args->argv;
572
	mdoclt = MDOC_ARG_MAX;
573

279848
	for (i = 0; n->args && i < (int)n->args->argc; i++) {
574
96162
		argv = n->args->argv + i;
575
		lt = LIST__NONE;
576



96162
		switch (argv->arg) {
577
		/* Set list types. */
578
		case MDOC_Bullet:
579
			lt = LIST_bullet;
580
1473
			break;
581
		case MDOC_Dash:
582
			lt = LIST_dash;
583
1053
			break;
584
		case MDOC_Enum:
585
			lt = LIST_enum;
586
948
			break;
587
		case MDOC_Hyphen:
588
			lt = LIST_hyphen;
589
234
			break;
590
		case MDOC_Item:
591
			lt = LIST_item;
592
819
			break;
593
		case MDOC_Tag:
594
			lt = LIST_tag;
595
13826
			break;
596
		case MDOC_Diag:
597
			lt = LIST_diag;
598
633
			break;
599
		case MDOC_Hang:
600
			lt = LIST_hang;
601
720
			break;
602
		case MDOC_Ohang:
603
			lt = LIST_ohang;
604
282
			break;
605
		case MDOC_Inset:
606
			lt = LIST_inset;
607
639
			break;
608
		case MDOC_Column:
609
			lt = LIST_column;
610
1281
			break;
611
		/* Set list arguments. */
612
		case MDOC_Compact:
613
5897
			if (n->norm->Bl.comp)
614
36
				mandoc_msg(MANDOCERR_ARG_REP,
615
36
				    mdoc->parse, argv->line,
616
36
				    argv->pos, "Bl -compact");
617
5897
			n->norm->Bl.comp = 1;
618
5897
			break;
619
		case MDOC_Width:
620
			wa = argv;
621
15842
			if (0 == argv->sz) {
622
180
				mandoc_msg(MANDOCERR_ARG_EMPTY,
623
180
				    mdoc->parse, argv->line,
624
180
				    argv->pos, "Bl -width");
625
180
				n->norm->Bl.width = "0n";
626
180
				break;
627
			}
628
15662
			if (NULL != n->norm->Bl.width)
629
36
				mandoc_vmsg(MANDOCERR_ARG_REP,
630
36
				    mdoc->parse, argv->line,
631
36
				    argv->pos, "Bl -width %s",
632
36
				    argv->value[0]);
633
15662
			rewrite_macro2len(mdoc, argv->value);
634
15662
			n->norm->Bl.width = argv->value[0];
635
15662
			break;
636
		case MDOC_Offset:
637
4434
			if (0 == argv->sz) {
638
90
				mandoc_msg(MANDOCERR_ARG_EMPTY,
639
90
				    mdoc->parse, argv->line,
640
90
				    argv->pos, "Bl -offset");
641
90
				break;
642
			}
643
4344
			if (NULL != n->norm->Bl.offs)
644
36
				mandoc_vmsg(MANDOCERR_ARG_REP,
645
36
				    mdoc->parse, argv->line,
646
36
				    argv->pos, "Bl -offset %s",
647
36
				    argv->value[0]);
648
4344
			rewrite_macro2len(mdoc, argv->value);
649
4344
			n->norm->Bl.offs = argv->value[0];
650
4344
			break;
651
		default:
652
			continue;
653
		}
654
48081
		if (LIST__NONE == lt)
655
			continue;
656
21908
		mdoclt = argv->arg;
657
658
		/* Check: multiple list types. */
659
660
21908
		if (LIST__NONE != n->norm->Bl.type) {
661
90
			mandoc_vmsg(MANDOCERR_BL_REP,
662
90
			    mdoc->parse, n->line, n->pos,
663
90
			    "Bl -%s", mdoc_argnames[argv->arg]);
664
90
			continue;
665
		}
666
667
		/* The list type should come first. */
668
669

43600
		if (n->norm->Bl.width ||
670
21818
		    n->norm->Bl.offs ||
671
21782
		    n->norm->Bl.comp)
672
36
			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
673
36
			    mdoc->parse, n->line, n->pos, "Bl -%s",
674
36
			    mdoc_argnames[n->args->argv[0].arg]);
675
676
21818
		n->norm->Bl.type = lt;
677
21818
		if (LIST_column == lt) {
678
1281
			n->norm->Bl.ncols = argv->sz;
679
1281
			n->norm->Bl.cols = (void *)argv->value;
680
1281
		}
681
	}
682
683
	/* Allow lists to default to LIST_item. */
684
685
21890
	if (LIST__NONE == n->norm->Bl.type) {
686
144
		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
687
72
		    n->line, n->pos, "Bl");
688
72
		n->norm->Bl.type = LIST_item;
689
		mdoclt = MDOC_Item;
690
72
	}
691
692
	/*
693
	 * Validate the width field.  Some list types don't need width
694
	 * types and should be warned about them.  Others should have it
695
	 * and must also be warned.  Yet others have a default and need
696
	 * no warning.
697
	 */
698
699


43060
	switch (n->norm->Bl.type) {
700
	case LIST_tag:
701
13826
		if (n->norm->Bl.width == NULL)
702
54
			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
703
27
			    n->line, n->pos, "Bl -tag");
704
		break;
705
	case LIST_column:
706
	case LIST_diag:
707
	case LIST_ohang:
708
	case LIST_inset:
709
	case LIST_item:
710
3726
		if (n->norm->Bl.width != NULL)
711
270
			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
712
135
			    wa->line, wa->pos, "Bl -%s",
713
135
			    mdoc_argnames[mdoclt]);
714
3726
		n->norm->Bl.width = NULL;
715
3726
		break;
716
	case LIST_bullet:
717
	case LIST_dash:
718
	case LIST_hyphen:
719
2706
		if (n->norm->Bl.width == NULL)
720
1803
			n->norm->Bl.width = "2n";
721
		break;
722
	case LIST_enum:
723
912
		if (n->norm->Bl.width == NULL)
724
573
			n->norm->Bl.width = "3n";
725
		break;
726
	default:
727
		break;
728
	}
729
21890
}
730
731
static void
732
post_bd(POST_ARGS)
733
{
734
	struct roff_node *n;
735
	struct mdoc_argv *argv;
736
	int		  i;
737
	enum mdoc_disp	  dt;
738
739
17542
	n = mdoc->last;
740

100224
	for (i = 0; n->args && i < (int)n->args->argc; i++) {
741
16285
		argv = n->args->argv + i;
742
		dt = DISP__NONE;
743
744


16285
		switch (argv->arg) {
745
		case MDOC_Centred:
746
			dt = DISP_centered;
747
45
			break;
748
		case MDOC_Ragged:
749
			dt = DISP_ragged;
750
1221
			break;
751
		case MDOC_Unfilled:
752
			dt = DISP_unfilled;
753
483
			break;
754
		case MDOC_Filled:
755
			dt = DISP_filled;
756
178
			break;
757
		case MDOC_Literal:
758
			dt = DISP_literal;
759
6880
			break;
760
		case MDOC_File:
761
216
			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
762
108
			    n->line, n->pos, NULL);
763
108
			break;
764
		case MDOC_Offset:
765
7025
			if (0 == argv->sz) {
766
63
				mandoc_msg(MANDOCERR_ARG_EMPTY,
767
63
				    mdoc->parse, argv->line,
768
63
				    argv->pos, "Bd -offset");
769
63
				break;
770
			}
771
6962
			if (NULL != n->norm->Bd.offs)
772
36
				mandoc_vmsg(MANDOCERR_ARG_REP,
773
36
				    mdoc->parse, argv->line,
774
36
				    argv->pos, "Bd -offset %s",
775
36
				    argv->value[0]);
776
6962
			rewrite_macro2len(mdoc, argv->value);
777
6962
			n->norm->Bd.offs = argv->value[0];
778
6962
			break;
779
		case MDOC_Compact:
780
345
			if (n->norm->Bd.comp)
781
36
				mandoc_msg(MANDOCERR_ARG_REP,
782
36
				    mdoc->parse, argv->line,
783
36
				    argv->pos, "Bd -compact");
784
345
			n->norm->Bd.comp = 1;
785
345
			break;
786
		default:
787
			abort();
788
		}
789
16285
		if (DISP__NONE == dt)
790
			continue;
791
792
8807
		if (DISP__NONE == n->norm->Bd.type)
793
8735
			n->norm->Bd.type = dt;
794
		else
795
72
			mandoc_vmsg(MANDOCERR_BD_REP,
796
72
			    mdoc->parse, n->line, n->pos,
797
72
			    "Bd -%s", mdoc_argnames[argv->arg]);
798
	}
799
800
8771
	if (DISP__NONE == n->norm->Bd.type) {
801
72
		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
802
36
		    n->line, n->pos, "Bd");
803
36
		n->norm->Bd.type = DISP_ragged;
804
36
	}
805
8771
}
806
807
/*
808
 * Stand-alone line macros.
809
 */
810
811
static void
812
post_an_norm(POST_ARGS)
813
{
814
	struct roff_node *n;
815
	struct mdoc_argv *argv;
816
	size_t	 i;
817
818
11570
	n = mdoc->last;
819
5785
	if (n->args == NULL)
820
4624
		return;
821
822
2466
	for (i = 1; i < n->args->argc; i++) {
823
72
		argv = n->args->argv + i;
824
72
		mandoc_vmsg(MANDOCERR_AN_REP,
825
72
		    mdoc->parse, argv->line, argv->pos,
826
72
		    "An -%s", mdoc_argnames[argv->arg]);
827
	}
828
829
	argv = n->args->argv;
830
1161
	if (argv->arg == MDOC_Split)
831
36
		n->norm->An.auth = AUTH_split;
832
1125
	else if (argv->arg == MDOC_Nosplit)
833
		n->norm->An.auth = AUTH_nosplit;
834
	else
835
		abort();
836
6946
}
837
838
static void
839
post_eoln(POST_ARGS)
840
{
841
	struct roff_node	*n;
842
843
540
	post_useless(mdoc);
844
270
	n = mdoc->last;
845
270
	if (n->child != NULL)
846
288
		mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line,
847
144
		    n->pos, "%s %s", roff_name[n->tok], n->child->string);
848
849
702
	while (n->child != NULL)
850
216
		roff_node_delete(mdoc, n->child);
851
852
270
	roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
853
	    "is currently in beta test." : "currently under development.");
854
270
	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
855
270
	mdoc->last = n;
856
270
}
857
858
static int
859
build_list(struct roff_man *mdoc, int tok)
860
{
861
	struct roff_node	*n;
862
	int			 ic;
863
864
1536
	n = mdoc->last->next;
865
1011
	for (ic = 1;; ic++) {
866
1011
		roff_elem_alloc(mdoc, n->line, n->pos, tok);
867
1011
		mdoc->last->flags |= NODE_NOSRC;
868
1011
		mdoc_node_relink(mdoc, n);
869
1011
		n = mdoc->last = mdoc->last->parent;
870
1011
		mdoc->next = ROFF_NEXT_SIBLING;
871
1011
		if (n->next == NULL)
872
768
			return ic;
873

468
		if (ic > 1 || n->next->next != NULL) {
874
30
			roff_word_alloc(mdoc, n->line, n->pos, ",");
875
30
			mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
876
30
		}
877
243
		n = mdoc->last->next;
878
243
		if (n->next == NULL) {
879
225
			roff_word_alloc(mdoc, n->line, n->pos, "and");
880
225
			mdoc->last->flags |= NODE_NOSRC;
881
225
		}
882
	}
883
}
884
885
static void
886
post_ex(POST_ARGS)
887
{
888
	struct roff_node	*n;
889
	int			 ic;
890
891
1314
	post_std(mdoc);
892
893
657
	n = mdoc->last;
894
657
	mdoc->next = ROFF_NEXT_CHILD;
895
657
	roff_word_alloc(mdoc, n->line, n->pos, "The");
896
657
	mdoc->last->flags |= NODE_NOSRC;
897
898
657
	if (mdoc->last->next != NULL)
899
534
		ic = build_list(mdoc, MDOC_Nm);
900
123
	else if (mdoc->meta.name != NULL) {
901
87
		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
902
87
		mdoc->last->flags |= NODE_NOSRC;
903
87
		roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
904
87
		mdoc->last->flags |= NODE_NOSRC;
905
87
		mdoc->last = mdoc->last->parent;
906
87
		mdoc->next = ROFF_NEXT_SIBLING;
907
		ic = 1;
908
87
	} else {
909
72
		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
910
36
		    n->line, n->pos, "Ex");
911
		ic = 0;
912
	}
913
914
1314
	roff_word_alloc(mdoc, n->line, n->pos,
915
657
	    ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
916
657
	mdoc->last->flags |= NODE_NOSRC;
917
657
	roff_word_alloc(mdoc, n->line, n->pos,
918
	    "on success, and\\~>0 if an error occurs.");
919
657
	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
920
657
	mdoc->last = n;
921
657
}
922
923
static void
924
post_lb(POST_ARGS)
925
{
926
	struct roff_node	*n;
927
928
306
	post_delim_nb(mdoc);
929
930
153
	n = mdoc->last;
931
153
	assert(n->child->type == ROFFT_TEXT);
932
153
	mdoc->next = ROFF_NEXT_CHILD;
933
153
	roff_word_alloc(mdoc, n->line, n->pos, "library");
934
153
	mdoc->last->flags = NODE_NOSRC;
935
153
	roff_word_alloc(mdoc, n->line, n->pos, "\\(Lq");
936
153
	mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
937
153
	mdoc->last = mdoc->last->next;
938
153
	roff_word_alloc(mdoc, n->line, n->pos, "\\(Rq");
939
153
	mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
940
153
	mdoc->last = n;
941
153
}
942
943
static void
944
post_rv(POST_ARGS)
945
{
946
	struct roff_node	*n;
947
	int			 ic;
948
949
1080
	post_std(mdoc);
950
951
540
	n = mdoc->last;
952
540
	mdoc->next = ROFF_NEXT_CHILD;
953
540
	if (n->child != NULL) {
954
234
		roff_word_alloc(mdoc, n->line, n->pos, "The");
955
234
		mdoc->last->flags |= NODE_NOSRC;
956
234
		ic = build_list(mdoc, MDOC_Fn);
957
468
		roff_word_alloc(mdoc, n->line, n->pos,
958
234
		    ic > 1 ? "functions return" : "function returns");
959
234
		mdoc->last->flags |= NODE_NOSRC;
960
234
		roff_word_alloc(mdoc, n->line, n->pos,
961
		    "the value\\~0 if successful;");
962
234
	} else
963
306
		roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
964
		    "completion, the value\\~0 is returned;");
965
540
	mdoc->last->flags |= NODE_NOSRC;
966
967
540
	roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
968
	    "the value\\~\\-1 is returned and the global variable");
969
540
	mdoc->last->flags |= NODE_NOSRC;
970
540
	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
971
540
	mdoc->last->flags |= NODE_NOSRC;
972
540
	roff_word_alloc(mdoc, n->line, n->pos, "errno");
973
540
	mdoc->last->flags |= NODE_NOSRC;
974
540
	mdoc->last = mdoc->last->parent;
975
540
	mdoc->next = ROFF_NEXT_SIBLING;
976
540
	roff_word_alloc(mdoc, n->line, n->pos,
977
	    "is set to indicate the error.");
978
540
	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
979
540
	mdoc->last = n;
980
540
}
981
982
static void
983
post_std(POST_ARGS)
984
{
985
	struct roff_node *n;
986
987
2394
	post_delim(mdoc);
988
989
1197
	n = mdoc->last;
990

2178
	if (n->args && n->args->argc == 1)
991
981
		if (n->args->argv[0].arg == MDOC_Std)
992
981
			return;
993
994
432
	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
995
216
	    n->line, n->pos, roff_name[n->tok]);
996
1413
}
997
998
static void
999
post_st(POST_ARGS)
1000
{
1001
	struct roff_node	 *n, *nch;
1002
	const char		 *p;
1003
1004
4762
	n = mdoc->last;
1005
2381
	nch = n->child;
1006
2381
	assert(nch->type == ROFFT_TEXT);
1007
1008
2381
	if ((p = mdoc_a2st(nch->string)) == NULL) {
1009
72
		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1010
36
		    nch->line, nch->pos, "St %s", nch->string);
1011
36
		roff_node_delete(mdoc, n);
1012
36
		return;
1013
	}
1014
1015
2345
	nch->flags |= NODE_NOPRT;
1016
2345
	mdoc->next = ROFF_NEXT_CHILD;
1017
2345
	roff_word_alloc(mdoc, nch->line, nch->pos, p);
1018
2345
	mdoc->last->flags |= NODE_NOSRC;
1019
2345
	mdoc->last= n;
1020
4726
}
1021
1022
static void
1023
post_obsolete(POST_ARGS)
1024
{
1025
	struct roff_node *n;
1026
1027
1314
	n = mdoc->last;
1028

1089
	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
1029
738
		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
1030
369
		    n->line, n->pos, roff_name[n->tok]);
1031
657
}
1032
1033
static void
1034
post_useless(POST_ARGS)
1035
{
1036
	struct roff_node *n;
1037
1038
7668
	n = mdoc->last;
1039
7668
	mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse,
1040
3834
	    n->line, n->pos, roff_name[n->tok]);
1041
3834
}
1042
1043
/*
1044
 * Block macros.
1045
 */
1046
1047
static void
1048
post_bf(POST_ARGS)
1049
{
1050
	struct roff_node *np, *nch;
1051
1052
	/*
1053
	 * Unlike other data pointers, these are "housed" by the HEAD
1054
	 * element, which contains the goods.
1055
	 */
1056
1057
3150
	np = mdoc->last;
1058
1575
	if (np->type != ROFFT_HEAD)
1059
1056
		return;
1060
1061
519
	assert(np->parent->type == ROFFT_BLOCK);
1062
519
	assert(np->parent->tok == MDOC_Bf);
1063
1064
	/* Check the number of arguments. */
1065
1066
519
	nch = np->child;
1067
519
	if (np->parent->args == NULL) {
1068
237
		if (nch == NULL) {
1069
54
			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
1070
27
			    np->line, np->pos, "Bf");
1071
27
			return;
1072
		}
1073
210
		nch = nch->next;
1074
210
	}
1075
492
	if (nch != NULL)
1076
270
		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1077
135
		    nch->line, nch->pos, "Bf ... %s", nch->string);
1078
1079
	/* Extract argument into data. */
1080
1081
492
	if (np->parent->args != NULL) {
1082

282
		switch (np->parent->args->argv[0].arg) {
1083
		case MDOC_Emphasis:
1084
			np->norm->Bf.font = FONT_Em;
1085
51
			break;
1086
		case MDOC_Literal:
1087
			np->norm->Bf.font = FONT_Li;
1088
36
			break;
1089
		case MDOC_Symbolic:
1090
			np->norm->Bf.font = FONT_Sy;
1091
195
			break;
1092
		default:
1093
			abort();
1094
		}
1095
282
		return;
1096
	}
1097
1098
	/* Extract parameter into data. */
1099
1100
210
	if ( ! strcmp(np->child->string, "Em"))
1101
66
		np->norm->Bf.font = FONT_Em;
1102
144
	else if ( ! strcmp(np->child->string, "Li"))
1103
18
		np->norm->Bf.font = FONT_Li;
1104
126
	else if ( ! strcmp(np->child->string, "Sy"))
1105
99
		np->norm->Bf.font = FONT_Sy;
1106
	else
1107
54
		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
1108
27
		    np->child->line, np->child->pos,
1109
27
		    "Bf %s", np->child->string);
1110
1785
}
1111
1112
static void
1113
post_fname(POST_ARGS)
1114
{
1115
	const struct roff_node	*n;
1116
	const char		*cp;
1117
	size_t			 pos;
1118
1119
130514
	n = mdoc->last->child;
1120
65257
	pos = strcspn(n->string, "()");
1121
65257
	cp = n->string + pos;
1122

65743
	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
1123
72
		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
1124
36
		    n->line, n->pos + pos, n->string);
1125
65257
}
1126
1127
static void
1128
post_fn(POST_ARGS)
1129
{
1130
1131
117490
	post_fname(mdoc);
1132
58745
	post_fa(mdoc);
1133
58745
}
1134
1135
static void
1136
post_fo(POST_ARGS)
1137
{
1138
	const struct roff_node	*n;
1139
1140
39288
	n = mdoc->last;
1141
1142
19644
	if (n->type != ROFFT_HEAD)
1143
13096
		return;
1144
1145
6548
	if (n->child == NULL) {
1146
72
		mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
1147
36
		    n->line, n->pos, "Fo");
1148
36
		return;
1149
	}
1150
6512
	if (n->child != n->last) {
1151
72
		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1152
36
		    n->child->next->line, n->child->next->pos,
1153
36
		    "Fo ... %s", n->child->next->string);
1154
144
		while (n->child != n->last)
1155
36
			roff_node_delete(mdoc, n->last);
1156
	} else
1157
6476
		post_delim(mdoc);
1158
1159
6512
	post_fname(mdoc);
1160
26156
}
1161
1162
static void
1163
post_fa(POST_ARGS)
1164
{
1165
	const struct roff_node *n;
1166
	const char *cp;
1167
1168
604404
	for (n = mdoc->last->child; n != NULL; n = n->next) {
1169
3149610
		for (cp = n->string; *cp != '\0'; cp++) {
1170
			/* Ignore callbacks and alterations. */
1171

2873358
			if (*cp == '(' || *cp == '{')
1172
				break;
1173
1436295
			if (*cp != ',')
1174
				continue;
1175
78
			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
1176
39
			    n->line, n->pos + (cp - n->string),
1177
			    n->string);
1178
39
			break;
1179
		}
1180
	}
1181
109102
	post_delim_nb(mdoc);
1182
109102
}
1183
1184
static void
1185
post_nm(POST_ARGS)
1186
{
1187
	struct roff_node	*n;
1188
1189
139762
	n = mdoc->last;
1190
1191

131604
	if (n->sec == SEC_NAME && n->child != NULL &&
1192
61570
	    n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL)
1193
30677
		mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
1194
1195

115334
	if (n->last != NULL &&
1196
46029
	    (n->last->tok == MDOC_Pp ||
1197
45453
	     n->last->tok == MDOC_Lp))
1198
576
		mdoc_node_relink(mdoc, n->last);
1199
1200
69881
	if (mdoc->meta.name == NULL)
1201
17187
		deroff(&mdoc->meta.name, n);
1202
1203

100666
	if (mdoc->meta.name == NULL ||
1204
100513
	    (mdoc->lastsec == SEC_NAME && n->child == NULL))
1205
306
		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1206
153
		    n->line, n->pos, "Nm");
1207
1208
69881
	switch (n->type) {
1209
	case ROFFT_ELEM:
1210
59300
		post_delim_nb(mdoc);
1211
59300
		break;
1212
	case ROFFT_HEAD:
1213
3536
		post_delim(mdoc);
1214
3536
		break;
1215
	default:
1216
7045
		return;
1217
	}
1218
1219

125672
	if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
1220
23563
	    mdoc->meta.name == NULL)
1221
39426
		return;
1222
1223
23410
	mdoc->next = ROFF_NEXT_CHILD;
1224
23410
	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
1225
23410
	mdoc->last->flags |= NODE_NOSRC;
1226
23410
	mdoc->last = n;
1227
93291
}
1228
1229
static void
1230
post_nd(POST_ARGS)
1231
{
1232
	struct roff_node	*n;
1233
1234
103122
	n = mdoc->last;
1235
1236
51561
	if (n->type != ROFFT_BODY)
1237
34374
		return;
1238
1239
17187
	if (n->sec != SEC_NAME)
1240
162
		mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse,
1241
81
		    n->line, n->pos, "Nd");
1242
1243
17187
	if (n->child == NULL)
1244
72
		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
1245
36
		    n->line, n->pos, "Nd");
1246
	else
1247
17151
		post_delim(mdoc);
1248
1249
17187
	post_hyph(mdoc);
1250
68748
}
1251
1252
static void
1253
post_display(POST_ARGS)
1254
{
1255
	struct roff_node *n, *np;
1256
1257
97588
	n = mdoc->last;
1258
60997
	switch (n->type) {
1259
	case ROFFT_BODY:
1260
12221
		if (n->end != ENDBODY_NOT) {
1261

72
			if (n->tok == MDOC_Bd &&
1262
36
			    n->body->parent->args == NULL)
1263
				roff_node_delete(mdoc, n);
1264
12185
		} else if (n->child == NULL)
1265
432
			mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1266
216
			    n->line, n->pos, roff_name[n->tok]);
1267
11969
		else if (n->tok == MDOC_D1)
1268
745
			post_hyph(mdoc);
1269
		break;
1270
	case ROFFT_BLOCK:
1271
12185
		if (n->tok == MDOC_Bd) {
1272
8807
			if (n->args == NULL) {
1273
36
				mandoc_msg(MANDOCERR_BD_NOARG,
1274
36
				    mdoc->parse, n->line, n->pos, "Bd");
1275
36
				mdoc->next = ROFF_NEXT_SIBLING;
1276
216
				while (n->body->child != NULL)
1277
72
					mdoc_node_relink(mdoc,
1278
					    n->body->child);
1279
36
				roff_node_delete(mdoc, n);
1280
36
				break;
1281
			}
1282
8771
			post_bd(mdoc);
1283
8771
			post_prevpar(mdoc);
1284
8771
		}
1285
123548
		for (np = n->parent; np != NULL; np = np->parent) {
1286

68615
			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1287
126
				mandoc_vmsg(MANDOCERR_BD_NEST,
1288
126
				    mdoc->parse, n->line, n->pos,
1289
126
				    "%s in Bd", roff_name[n->tok]);
1290
126
				break;
1291
			}
1292
		}
1293
		break;
1294
	default:
1295
		break;
1296
	}
1297
36591
}
1298
1299
static void
1300
post_defaults(POST_ARGS)
1301
{
1302
	struct roff_node *nn;
1303
1304
172198
	if (mdoc->last->child != NULL) {
1305
84828
		post_delim_nb(mdoc);
1306
84828
		return;
1307
	}
1308
1309
	/*
1310
	 * The `Ar' defaults to "file ..." if no value is provided as an
1311
	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1312
	 * gets an empty string.
1313
	 */
1314
1315
	nn = mdoc->last;
1316

1271
	switch (nn->tok) {
1317
	case MDOC_Ar:
1318
839
		mdoc->next = ROFF_NEXT_CHILD;
1319
839
		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
1320
839
		mdoc->last->flags |= NODE_NOSRC;
1321
839
		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
1322
		mdoc->last->flags |= NODE_NOSRC;
1323
839
		break;
1324
	case MDOC_Pa:
1325
	case MDOC_Mt:
1326
432
		mdoc->next = ROFF_NEXT_CHILD;
1327
432
		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
1328
		mdoc->last->flags |= NODE_NOSRC;
1329
432
		break;
1330
	default:
1331
		abort();
1332
	}
1333
1271
	mdoc->last = nn;
1334
87370
}
1335
1336
static void
1337
post_at(POST_ARGS)
1338
{
1339
	struct roff_node	*n, *nch;
1340
	const char		*att;
1341
1342
2288
	n = mdoc->last;
1343
1144
	nch = n->child;
1344
1345
	/*
1346
	 * If we have a child, look it up in the standard keys.  If a
1347
	 * key exist, use that instead of the child; if it doesn't,
1348
	 * prefix "AT&T UNIX " to the existing data.
1349
	 */
1350
1351
	att = NULL;
1352

2207
	if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1353
72
		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1354
36
		    nch->line, nch->pos, "At %s", nch->string);
1355
1356
1144
	mdoc->next = ROFF_NEXT_CHILD;
1357
1144
	if (att != NULL) {
1358
1027
		roff_word_alloc(mdoc, nch->line, nch->pos, att);
1359
1027
		nch->flags |= NODE_NOPRT;
1360
1027
	} else
1361
117
		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1362
1144
	mdoc->last->flags |= NODE_NOSRC;
1363
1144
	mdoc->last = n;
1364
1144
}
1365
1366
static void
1367
post_an(POST_ARGS)
1368
{
1369
	struct roff_node *np, *nch;
1370
1371
11570
	post_an_norm(mdoc);
1372
1373
5785
	np = mdoc->last;
1374
5785
	nch = np->child;
1375
5785
	if (np->norm->An.auth == AUTH__NONE) {
1376
4624
		if (nch == NULL)
1377
576
			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1378
288
			    np->line, np->pos, "An");
1379
		else
1380
4336
			post_delim_nb(mdoc);
1381
1161
	} else if (nch != NULL)
1382
144
		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1383
72
		    nch->line, nch->pos, "An ... %s", nch->string);
1384
5785
}
1385
1386
static void
1387
post_en(POST_ARGS)
1388
{
1389
1390
864
	post_obsolete(mdoc);
1391
432
	if (mdoc->last->type == ROFFT_BLOCK)
1392
144
		mdoc->last->norm->Es = mdoc->last_es;
1393
432
}
1394
1395
static void
1396
post_es(POST_ARGS)
1397
{
1398
1399
144
	post_obsolete(mdoc);
1400
72
	mdoc->last_es = mdoc->last;
1401
72
}
1402
1403
static void
1404
post_xx(POST_ARGS)
1405
{
1406
	struct roff_node	*n;
1407
	const char		*os;
1408
	char			*v;
1409
1410
16000
	post_delim_nb(mdoc);
1411
1412
8000
	n = mdoc->last;
1413

8000
	switch (n->tok) {
1414
	case MDOC_Bsx:
1415
		os = "BSD/OS";
1416
114
		break;
1417
	case MDOC_Dx:
1418
		os = "DragonFly";
1419
108
		break;
1420
	case MDOC_Fx:
1421
		os = "FreeBSD";
1422
498
		break;
1423
	case MDOC_Nx:
1424
		os = "NetBSD";
1425
714
		if (n->child == NULL)
1426
			break;
1427
411
		v = n->child->string;
1428


1140
		if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
1429

654
		    v[2] < '0' || v[2] > '9' ||
1430

387
		    v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
1431
			break;
1432
30
		n->child->flags |= NODE_NOPRT;
1433
30
		mdoc->next = ROFF_NEXT_CHILD;
1434
30
		roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
1435
30
		v = mdoc->last->string;
1436
30
		v[3] = toupper((unsigned char)v[3]);
1437
30
		mdoc->last->flags |= NODE_NOSRC;
1438
30
		mdoc->last = n;
1439
30
		break;
1440
	case MDOC_Ox:
1441
		os = "OpenBSD";
1442
6064
		break;
1443
	case MDOC_Ux:
1444
		os = "UNIX";
1445
502
		break;
1446
	default:
1447
		abort();
1448
	}
1449
8000
	mdoc->next = ROFF_NEXT_CHILD;
1450
8000
	roff_word_alloc(mdoc, n->line, n->pos, os);
1451
8000
	mdoc->last->flags |= NODE_NOSRC;
1452
8000
	mdoc->last = n;
1453
8000
}
1454
1455
static void
1456
post_it(POST_ARGS)
1457
{
1458
	struct roff_node *nbl, *nit, *nch;
1459
	int		  i, cols;
1460
	enum mdoc_list	  lt;
1461
1462
717774
	post_prevpar(mdoc);
1463
1464
358887
	nit = mdoc->last;
1465
358887
	if (nit->type != ROFFT_BLOCK)
1466
248238
		return;
1467
1468
120354
	nbl = nit->parent->parent;
1469
120354
	lt = nbl->norm->Bl.type;
1470
1471



120354
	switch (lt) {
1472
	case LIST_tag:
1473
	case LIST_hang:
1474
	case LIST_ohang:
1475
	case LIST_inset:
1476
	case LIST_diag:
1477
86118
		if (nit->head->child == NULL)
1478
135
			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1479
135
			    mdoc->parse, nit->line, nit->pos,
1480
			    "Bl -%s It",
1481
135
			    mdoc_argnames[nbl->args->argv[0].arg]);
1482
		break;
1483
	case LIST_bullet:
1484
	case LIST_dash:
1485
	case LIST_enum:
1486
	case LIST_hyphen:
1487

19392
		if (nit->body == NULL || nit->body->child == NULL)
1488
162
			mandoc_vmsg(MANDOCERR_IT_NOBODY,
1489
162
			    mdoc->parse, nit->line, nit->pos,
1490
			    "Bl -%s It",
1491
162
			    mdoc_argnames[nbl->args->argv[0].arg]);
1492
		/* FALLTHROUGH */
1493
	case LIST_item:
1494
11547
		if ((nch = nit->head->child) != NULL)
1495
486
			mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse,
1496
243
			    nit->line, nit->pos, "It %s",
1497
486
			    nch->string == NULL ? roff_name[nch->tok] :
1498
			    nch->string);
1499
		break;
1500
	case LIST_column:
1501
12984
		cols = (int)nbl->norm->Bl.ncols;
1502
1503
12984
		assert(nit->head->child == NULL);
1504
1505

13212
		if (nit->head->next->child == NULL &&
1506
228
		    nit->head->next->next == NULL) {
1507
162
			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1508
81
			    nit->line, nit->pos, "It");
1509
81
			roff_node_delete(mdoc, nit);
1510
81
			break;
1511
		}
1512
1513
		i = 0;
1514
131334
		for (nch = nit->child; nch != NULL; nch = nch->next) {
1515
52764
			if (nch->type != ROFFT_BODY)
1516
				continue;
1517

66819
			if (i++ && nch->flags & NODE_LINE)
1518
54
				mandoc_msg(MANDOCERR_TA_LINE, mdoc->parse,
1519
27
				    nch->line, nch->pos, "Ta");
1520
		}
1521

25779
		if (i < cols || i > cols + 1)
1522
81
			mandoc_vmsg(MANDOCERR_BL_COL,
1523
81
			    mdoc->parse, nit->line, nit->pos,
1524
			    "%d columns, %d cells", cols, i);
1525

25497
		else if (nit->head->next->child != NULL &&
1526
12675
		    nit->head->next->child->line > nit->line)
1527
54
			mandoc_msg(MANDOCERR_IT_NOARG, mdoc->parse,
1528
27
			    nit->line, nit->pos, "Bl -column It");
1529
		break;
1530
	default:
1531
		abort();
1532
	}
1533
469536
}
1534
1535
static void
1536
post_bl_block(POST_ARGS)
1537
{
1538
	struct roff_node *n, *ni, *nc;
1539
1540
43780
	post_prevpar(mdoc);
1541
1542
21890
	n = mdoc->last;
1543
265084
	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1544
110652
		if (ni->body == NULL)
1545
			continue;
1546
110559
		nc = ni->body->last;
1547
322625
		while (nc != NULL) {
1548

104607
			switch (nc->tok) {
1549
			case MDOC_Pp:
1550
			case MDOC_Lp:
1551
			case ROFF_br:
1552
				break;
1553
			default:
1554
				nc = NULL;
1555
101237
				continue;
1556
			}
1557
3370
			if (ni->next == NULL) {
1558
108
				mandoc_msg(MANDOCERR_PAR_MOVE,
1559
108
				    mdoc->parse, nc->line, nc->pos,
1560
108
				    roff_name[nc->tok]);
1561
108
				mdoc_node_relink(mdoc, nc);
1562

3529
			} else if (n->norm->Bl.comp == 0 &&
1563
159
			    n->norm->Bl.type != LIST_column) {
1564
27
				mandoc_vmsg(MANDOCERR_PAR_SKIP,
1565
27
				    mdoc->parse, nc->line, nc->pos,
1566
27
				    "%s before It", roff_name[nc->tok]);
1567
27
				roff_node_delete(mdoc, nc);
1568
			} else
1569
				break;
1570
135
			nc = ni->body->last;
1571
		}
1572
	}
1573
21890
}
1574
1575
/*
1576
 * If the argument of -offset or -width is a macro,
1577
 * replace it with the associated default width.
1578
 */
1579
static void
1580
rewrite_macro2len(struct roff_man *mdoc, char **arg)
1581
{
1582
	size_t		  width;
1583
	enum roff_tok	  tok;
1584
1585
53936
	if (*arg == NULL)
1586
		return;
1587
26968
	else if ( ! strcmp(*arg, "Ds"))
1588
4396
		width = 6;
1589
22572
	else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
1590
21213
		return;
1591
	else
1592
1359
		width = macro2len(tok);
1593
1594
5755
	free(*arg);
1595
5755
	mandoc_asprintf(arg, "%zun", width);
1596
32723
}
1597
1598
static void
1599
post_bl_head(POST_ARGS)
1600
{
1601
	struct roff_node *nbl, *nh, *nch, *nnext;
1602
	struct mdoc_argv *argv;
1603
	int		  i, j;
1604
1605
43780
	post_bl_norm(mdoc);
1606
1607
21890
	nh = mdoc->last;
1608
21890
	if (nh->norm->Bl.type != LIST_column) {
1609
20609
		if ((nch = nh->child) == NULL)
1610
20393
			return;
1611
432
		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1612
216
		    nch->line, nch->pos, "Bl ... %s", nch->string);
1613
1296
		while (nch != NULL) {
1614
432
			roff_node_delete(mdoc, nch);
1615
432
			nch = nh->child;
1616
		}
1617
216
		return;
1618
	}
1619
1620
	/*
1621
	 * Append old-style lists, where the column width specifiers
1622
	 * trail as macro parameters, to the new-style ("normal-form")
1623
	 * lists where they're argument values following -column.
1624
	 */
1625
1626
1281
	if (nh->child == NULL)
1627
1242
		return;
1628
1629
39
	nbl = nh->parent;
1630
78
	for (j = 0; j < (int)nbl->args->argc; j++)
1631
39
		if (nbl->args->argv[j].arg == MDOC_Column)
1632
			break;
1633
1634
39
	assert(j < (int)nbl->args->argc);
1635
1636
	/*
1637
	 * Accommodate for new-style groff column syntax.  Shuffle the
1638
	 * child nodes, all of which must be TEXT, as arguments for the
1639
	 * column field.  Then, delete the head children.
1640
	 */
1641
1642
39
	argv = nbl->args->argv + j;
1643
39
	i = argv->sz;
1644
234
	for (nch = nh->child; nch != NULL; nch = nch->next)
1645
78
		argv->sz++;
1646
78
	argv->value = mandoc_reallocarray(argv->value,
1647
39
	    argv->sz, sizeof(char *));
1648
1649
39
	nh->norm->Bl.ncols = argv->sz;
1650
39
	nh->norm->Bl.cols = (void *)argv->value;
1651
1652
234
	for (nch = nh->child; nch != NULL; nch = nnext) {
1653
78
		argv->value[i++] = nch->string;
1654
78
		nch->string = NULL;
1655
78
		nnext = nch->next;
1656
78
		roff_node_delete(NULL, nch);
1657
	}
1658
39
	nh->child = NULL;
1659
21929
}
1660
1661
static void
1662
post_bl(POST_ARGS)
1663
{
1664
	struct roff_node	*nparent, *nprev; /* of the Bl block */
1665
	struct roff_node	*nblock, *nbody;  /* of the Bl */
1666
	struct roff_node	*nchild, *nnext;  /* of the Bl body */
1667
	const char		*prev_Er;
1668
	int			 order;
1669
1670
131664
	nbody = mdoc->last;
1671

65832
	switch (nbody->type) {
1672
	case ROFFT_BLOCK:
1673
21890
		post_bl_block(mdoc);
1674
21890
		return;
1675
	case ROFFT_HEAD:
1676
21890
		post_bl_head(mdoc);
1677
21890
		return;
1678
	case ROFFT_BODY:
1679
		break;
1680
	default:
1681
		return;
1682
	}
1683
22052
	if (nbody->end != ENDBODY_NOT)
1684
162
		return;
1685
1686
21890
	nchild = nbody->child;
1687
21890
	if (nchild == NULL) {
1688
798
		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1689
399
		    nbody->line, nbody->pos, "Bl");
1690
399
		return;
1691
	}
1692
133529
	while (nchild != NULL) {
1693
111345
		nnext = nchild->next;
1694

111456
		if (nchild->tok == MDOC_It ||
1695
1554
		    (nchild->tok == MDOC_Sm &&
1696
888
		     nnext != NULL && nnext->tok == MDOC_It)) {
1697
			nchild = nnext;
1698
110643
			continue;
1699
		}
1700
1701
		/*
1702
		 * In .Bl -column, the first rows may be implicit,
1703
		 * that is, they may not start with .It macros.
1704
		 * Such rows may be followed by nodes generated on the
1705
		 * roff level, for example .TS, which cannot be moved
1706
		 * out of the list.  In that case, wrap such roff nodes
1707
		 * into an implicit row.
1708
		 */
1709
1710
702
		if (nchild->prev != NULL) {
1711
9
			mdoc->last = nchild;
1712
9
			mdoc->next = ROFF_NEXT_SIBLING;
1713
18
			roff_block_alloc(mdoc, nchild->line,
1714
9
			    nchild->pos, MDOC_It);
1715
18
			roff_head_alloc(mdoc, nchild->line,
1716
9
			    nchild->pos, MDOC_It);
1717
9
			mdoc->next = ROFF_NEXT_SIBLING;
1718
18
			roff_body_alloc(mdoc, nchild->line,
1719
9
			    nchild->pos, MDOC_It);
1720
54
			while (nchild->tok != MDOC_It) {
1721
18
				mdoc_node_relink(mdoc, nchild);
1722
18
				if ((nchild = nnext) == NULL)
1723
					break;
1724
18
				nnext = nchild->next;
1725
18
				mdoc->next = ROFF_NEXT_SIBLING;
1726
			}
1727
9
			mdoc->last = nbody;
1728
9
			continue;
1729
		}
1730
1731
1386
		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1732
693
		    nchild->line, nchild->pos, roff_name[nchild->tok]);
1733
1734
		/*
1735
		 * Move the node out of the Bl block.
1736
		 * First, collect all required node pointers.
1737
		 */
1738
1739
693
		nblock  = nbody->parent;
1740
693
		nprev   = nblock->prev;
1741
693
		nparent = nblock->parent;
1742
1743
		/*
1744
		 * Unlink this child.
1745
		 */
1746
1747
693
		nbody->child = nnext;
1748
1386
		if (nnext == NULL)
1749
693
			nbody->last  = NULL;
1750
		else
1751
693
			nnext->prev = NULL;
1752
1753
		/*
1754
		 * Relink this child.
1755
		 */
1756
1757
693
		nchild->parent = nparent;
1758
693
		nchild->prev   = nprev;
1759
693
		nchild->next   = nblock;
1760
1761
693
		nblock->prev = nchild;
1762
1386
		if (nprev == NULL)
1763
693
			nparent->child = nchild;
1764
		else
1765
693
			nprev->next = nchild;
1766
1767
		nchild = nnext;
1768
	}
1769
1770
21491
	if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
1771
21491
		return;
1772
1773
	prev_Er = NULL;
1774
	for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
1775
		if (nchild->tok != MDOC_It)
1776
			continue;
1777
		if ((nnext = nchild->head->child) == NULL)
1778
			continue;
1779
		if (nnext->type == ROFFT_BLOCK)
1780
			nnext = nnext->body->child;
1781
		if (nnext == NULL || nnext->tok != MDOC_Er)
1782
			continue;
1783
		nnext = nnext->child;
1784
		if (prev_Er != NULL) {
1785
			order = strcmp(prev_Er, nnext->string);
1786
			if (order > 0)
1787
				mandoc_vmsg(MANDOCERR_ER_ORDER,
1788
				    mdoc->parse, nnext->line, nnext->pos,
1789
				    "Er %s %s (NetBSD)",
1790
				    prev_Er, nnext->string);
1791
			else if (order == 0)
1792
				mandoc_vmsg(MANDOCERR_ER_REP,
1793
				    mdoc->parse, nnext->line, nnext->pos,
1794
				    "Er %s (NetBSD)", prev_Er);
1795
		}
1796
		prev_Er = nnext->string;
1797
	}
1798
65832
}
1799
1800
static void
1801
post_bk(POST_ARGS)
1802
{
1803
	struct roff_node	*n;
1804
1805
4734
	n = mdoc->last;
1806
1807

3156
	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1808
63
		mandoc_msg(MANDOCERR_BLK_EMPTY,
1809
63
		    mdoc->parse, n->line, n->pos, "Bk");
1810
63
		roff_node_delete(mdoc, n);
1811
63
	}
1812
2367
}
1813
1814
static void
1815
post_sm(POST_ARGS)
1816
{
1817
	struct roff_node	*nch;
1818
1819
5332
	nch = mdoc->last->child;
1820
1821
2666
	if (nch == NULL) {
1822
54
		mdoc->flags ^= MDOC_SMOFF;
1823
54
		return;
1824
	}
1825
1826
2612
	assert(nch->type == ROFFT_TEXT);
1827
1828
2612
	if ( ! strcmp(nch->string, "on")) {
1829
1261
		mdoc->flags &= ~MDOC_SMOFF;
1830
1261
		return;
1831
	}
1832
1351
	if ( ! strcmp(nch->string, "off")) {
1833
1243
		mdoc->flags |= MDOC_SMOFF;
1834
1243
		return;
1835
	}
1836
1837
108
	mandoc_vmsg(MANDOCERR_SM_BAD,
1838
108
	    mdoc->parse, nch->line, nch->pos,
1839
108
	    "%s %s", roff_name[mdoc->last->tok], nch->string);
1840
108
	mdoc_node_relink(mdoc, nch);
1841
108
	return;
1842
2666
}
1843
1844
static void
1845
post_root(POST_ARGS)
1846
{
1847
34500
	const char *openbsd_arch[] = {
1848
		"alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1849
		"landisk", "loongson", "luna88k", "macppc", "mips64",
1850
		"octeon", "sgi", "socppc", "sparc64", NULL
1851
	};
1852
17250
	const char *netbsd_arch[] = {
1853
		"acorn26", "acorn32", "algor", "alpha", "amiga",
1854
		"arc", "atari",
1855
		"bebox", "cats", "cesfic", "cobalt", "dreamcast",
1856
		"emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1857
		"hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1858
		"i386", "ibmnws", "luna68k",
1859
		"mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1860
		"netwinder", "news68k", "newsmips", "next68k",
1861
		"pc532", "playstation2", "pmax", "pmppc", "prep",
1862
		"sandpoint", "sbmips", "sgimips", "shark",
1863
		"sparc", "sparc64", "sun2", "sun3",
1864
		"vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1865
        };
1866
17250
	const char **arches[] = { NULL, netbsd_arch, openbsd_arch };
1867
1868
	struct roff_node *n;
1869
	const char **arch;
1870
1871
	/* Add missing prologue data. */
1872
1873
17250
	if (mdoc->meta.date == NULL)
1874
		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
1875
		    mandoc_normdate(mdoc, NULL, 0, 0);
1876
1877
17250
	if (mdoc->meta.title == NULL) {
1878
54
		mandoc_msg(MANDOCERR_DT_NOTITLE,
1879
54
		    mdoc->parse, 0, 0, "EOF");
1880
54
		mdoc->meta.title = mandoc_strdup("UNTITLED");
1881
54
	}
1882
1883
17250
	if (mdoc->meta.vol == NULL)
1884
54
		mdoc->meta.vol = mandoc_strdup("LOCAL");
1885
1886
17250
	if (mdoc->meta.os == NULL) {
1887
36
		mandoc_msg(MANDOCERR_OS_MISSING,
1888
36
		    mdoc->parse, 0, 0, NULL);
1889
36
		mdoc->meta.os = mandoc_strdup("");
1890

34436
	} else if (mdoc->meta.os_e &&
1891
17186
	    (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1892
324
		mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0,
1893
162
		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1894
		    "(OpenBSD)" : "(NetBSD)");
1895
1896

18408
	if (mdoc->meta.arch != NULL &&
1897
1158
	    (arch = arches[mdoc->meta.os_e]) != NULL) {
1898

39216
		while (*arch != NULL && strcmp(*arch, mdoc->meta.arch))
1899
8937
			arch++;
1900
1158
		if (*arch == NULL) {
1901
6
			n = mdoc->first->child;
1902

36
			while (n->tok != MDOC_Dt ||
1903
6
			    n->child == NULL ||
1904
12
			    n->child->next == NULL ||
1905
6
			    n->child->next->next == NULL)
1906
6
				n = n->next;
1907
6
			n = n->child->next->next;
1908
6
			mandoc_vmsg(MANDOCERR_ARCH_BAD,
1909
6
			    mdoc->parse, n->line, n->pos,
1910
6
			    "Dt ... %s %s", mdoc->meta.arch,
1911
6
			    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1912
			    "(OpenBSD)" : "(NetBSD)");
1913
6
		}
1914
	}
1915
1916
	/* Check that we begin with a proper `Sh'. */
1917
1918
17250
	n = mdoc->first->child;
1919

344622
	while (n != NULL && n->tok >= MDOC_Dd &&
1920
68874
	    mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1921
51696
		n = n->next;
1922
1923
17250
	if (n == NULL)
1924
36
		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1925
17214
	else if (n->tok != MDOC_Sh)
1926
198
		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1927
99
		    n->line, n->pos, roff_name[n->tok]);
1928
17250
}
1929
1930
static void
1931
post_rs(POST_ARGS)
1932
{
1933
	struct roff_node *np, *nch, *next, *prev;
1934
	int		  i, j;
1935
1936
10182
	np = mdoc->last;
1937
1938
5091
	if (np->type != ROFFT_BODY)
1939
3394
		return;
1940
1941
1697
	if (np->child == NULL) {
1942
180
		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1943
90
		    np->line, np->pos, "Rs");
1944
90
		return;
1945
	}
1946
1947
	/*
1948
	 * The full `Rs' block needs special handling to order the
1949
	 * sub-elements according to `rsord'.  Pick through each element
1950
	 * and correctly order it.  This is an insertion sort.
1951
	 */
1952
1953
	next = NULL;
1954
16028
	for (nch = np->child->next; nch != NULL; nch = next) {
1955
		/* Determine order number of this child. */
1956
82600
		for (i = 0; i < RSORD_MAX; i++)
1957
41030
			if (rsord[i] == nch->tok)
1958
				break;
1959
1960
6407
		if (i == RSORD_MAX) {
1961
540
			mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse,
1962
270
			    nch->line, nch->pos, roff_name[nch->tok]);
1963
			i = -1;
1964

12214
		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1965
566
			np->norm->Rs.quote_T++;
1966
1967
		/*
1968
		 * Remove this child from the chain.  This somewhat
1969
		 * repeats roff_node_unlink(), but since we're
1970
		 * just re-ordering, there's no need for the
1971
		 * full unlink process.
1972
		 */
1973
1974
6407
		if ((next = nch->next) != NULL)
1975
4860
			next->prev = nch->prev;
1976
1977
6407
		if ((prev = nch->prev) != NULL)
1978
6407
			prev->next = nch->next;
1979
1980
6407
		nch->prev = nch->next = NULL;
1981
1982
		/*
1983
		 * Scan back until we reach a node that's
1984
		 * to be ordered before this child.
1985
		 */
1986
1987
21424
		for ( ; prev ; prev = prev->prev) {
1988
			/* Determine order of `prev'. */
1989
108208
			for (j = 0; j < RSORD_MAX; j++)
1990
53924
				if (rsord[j] == prev->tok)
1991
					break;
1992
10463
			if (j == RSORD_MAX)
1993
180
				j = -1;
1994
1995
10463
			if (j <= i)
1996
				break;
1997
		}
1998
1999
		/*
2000
		 * Set this child back into its correct place
2001
		 * in front of the `prev' node.
2002
		 */
2003
2004
6407
		nch->prev = prev;
2005
2006
6407
		if (prev == NULL) {
2007
249
			np->child->prev = nch;
2008
249
			nch->next = np->child;
2009
249
			np->child = nch;
2010
249
		} else {
2011
6158
			if (prev->next)
2012
5493
				prev->next->prev = nch;
2013
6158
			nch->next = prev->next;
2014
6158
			prev->next = nch;
2015
		}
2016
	}
2017
6698
}
2018
2019
/*
2020
 * For some arguments of some macros,
2021
 * convert all breakable hyphens into ASCII_HYPH.
2022
 */
2023
static void
2024
post_hyph(POST_ARGS)
2025
{
2026
	struct roff_node	*nch;
2027
	char			*cp;
2028
2029
521639
	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
2030
105454
		if (nch->type != ROFFT_TEXT)
2031
			continue;
2032
103765
		cp = nch->string;
2033
103765
		if (*cp == '\0')
2034
			continue;
2035
1351222
		while (*(++cp) != '\0')
2036

1249290
			if (*cp == '-' &&
2037
2175
			    isalpha((unsigned char)cp[-1]) &&
2038
1833
			    isalpha((unsigned char)cp[1]))
2039
1650
				*cp = ASCII_HYPH;
2040
	}
2041
103577
}
2042
2043
static void
2044
post_ns(POST_ARGS)
2045
{
2046
	struct roff_node	*n;
2047
2048
20390
	n = mdoc->last;
2049

20318
	if (n->flags & NODE_LINE ||
2050
20282
	    (n->next != NULL && n->next->flags & NODE_DELIMC))
2051
144
		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
2052
72
		    n->line, n->pos, NULL);
2053
10195
}
2054
2055
static void
2056
post_sx(POST_ARGS)
2057
{
2058
7824
	post_delim(mdoc);
2059
3912
	post_hyph(mdoc);
2060
3912
}
2061
2062
static void
2063
post_sh(POST_ARGS)
2064
{
2065
2066
529585
	post_ignpar(mdoc);
2067
2068
302620
	switch (mdoc->last->type) {
2069
	case ROFFT_HEAD:
2070
75655
		post_sh_head(mdoc);
2071
75655
		break;
2072
	case ROFFT_BODY:
2073

103876
		switch (mdoc->lastsec)  {
2074
		case SEC_NAME:
2075
17178
			post_sh_name(mdoc);
2076
17178
			break;
2077
		case SEC_SEE_ALSO:
2078
8570
			post_sh_see_also(mdoc);
2079
8570
			break;
2080
		case SEC_AUTHORS:
2081
2473
			post_sh_authors(mdoc);
2082
2473
			break;
2083
		default:
2084
			break;
2085
		}
2086
		break;
2087
	default:
2088
		break;
2089
	}
2090
226965
}
2091
2092
static void
2093
post_sh_name(POST_ARGS)
2094
{
2095
	struct roff_node *n;
2096
	int hasnm, hasnd;
2097
2098
	hasnm = hasnd = 0;
2099
2100
113716
	for (n = mdoc->last->child; n != NULL; n = n->next) {
2101

48269
		switch (n->tok) {
2102
		case MDOC_Nm:
2103

17223
			if (hasnm && n->child != NULL)
2104
72
				mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
2105
72
				    mdoc->parse, n->line, n->pos,
2106
72
				    "Nm %s", n->child->string);
2107
			hasnm = 1;
2108
17151
			continue;
2109
		case MDOC_Nd:
2110
			hasnd = 1;
2111
17079
			if (n->next != NULL)
2112
36
				mandoc_msg(MANDOCERR_NAMESEC_ND,
2113
36
				    mdoc->parse, n->line, n->pos, NULL);
2114
			break;
2115
		case TOKEN_NONE:
2116

27583
			if (n->type == ROFFT_TEXT &&
2117

27583
			    n->string[0] == ',' && n->string[1] == '\0' &&
2118
27520
			    n->next != NULL && n->next->tok == MDOC_Nm) {
2119
				n = n->next;
2120
13724
				continue;
2121
			}
2122
			/* FALLTHROUGH */
2123
		default:
2124
432
			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
2125
216
			    n->line, n->pos, roff_name[n->tok]);
2126
216
			continue;
2127
		}
2128
		break;
2129
	}
2130
2131
17178
	if ( ! hasnm)
2132
198
		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
2133
99
		    mdoc->last->line, mdoc->last->pos, NULL);
2134
17178
	if ( ! hasnd)
2135
198
		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
2136
99
		    mdoc->last->line, mdoc->last->pos, NULL);
2137
17178
}
2138
2139
static void
2140
post_sh_see_also(POST_ARGS)
2141
{
2142
	const struct roff_node	*n;
2143
	const char		*name, *sec;
2144
	const char		*lastname, *lastsec, *lastpunct;
2145
	int			 cmp;
2146
2147
17140
	n = mdoc->last->child;
2148
	lastname = lastsec = lastpunct = NULL;
2149
72469
	while (n != NULL) {
2150

72205
		if (n->tok != MDOC_Xr ||
2151
35966
		    n->child == NULL ||
2152
35966
		    n->child->next == NULL)
2153
			break;
2154
2155
		/* Process one .Xr node. */
2156
2157
35966
		name = n->child->string;
2158
35966
		sec = n->child->next->string;
2159
35966
		if (lastsec != NULL) {
2160

55327
			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
2161
11
				mandoc_vmsg(MANDOCERR_XR_PUNCT,
2162
11
				    mdoc->parse, n->line, n->pos,
2163
				    "%s before %s(%s)", lastpunct,
2164
				    name, sec);
2165
27669
			cmp = strcmp(lastsec, sec);
2166
27669
			if (cmp > 0)
2167
				mandoc_vmsg(MANDOCERR_XR_ORDER,
2168
				    mdoc->parse, n->line, n->pos,
2169
				    "%s(%s) after %s(%s)", name,
2170
				    sec, lastname, lastsec);
2171

49423
			else if (cmp == 0 &&
2172
21754
			    strcasecmp(lastname, name) > 0)
2173
				mandoc_vmsg(MANDOCERR_XR_ORDER,
2174
				    mdoc->parse, n->line, n->pos,
2175
				    "%s after %s", name, lastname);
2176
		}
2177
		lastname = name;
2178
		lastsec = sec;
2179
2180
		/* Process the following node. */
2181
2182
35966
		n = n->next;
2183
35966
		if (n == NULL)
2184
			break;
2185
28134
		if (n->tok == MDOC_Xr) {
2186
			lastpunct = "none";
2187
11
			continue;
2188
		}
2189
28123
		if (n->type != ROFFT_TEXT)
2190
			break;
2191
110660
		for (name = n->string; *name != '\0'; name++)
2192
27671
			if (isalpha((const unsigned char)*name))
2193
3
				return;
2194
27659
		lastpunct = n->string;
2195

55317
		if (n->next == NULL || n->next->tok == MDOC_Rs)
2196
2
			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
2197
1
			    n->line, n->pos, "%s after %s(%s)",
2198
			    lastpunct, lastname, lastsec);
2199
27659
		n = n->next;
2200
	}
2201
17137
}
2202
2203
static int
2204
child_an(const struct roff_node *n)
2205
{
2206
2207
50916
	for (n = n->child; n != NULL; n = n->next)
2208

23252
		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
2209
2437
			return 1;
2210
8757
	return 0;
2211
11194
}
2212
2213
static void
2214
post_sh_authors(POST_ARGS)
2215
{
2216
2217
4946
	if ( ! child_an(mdoc->last))
2218
180
		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
2219
90
		    mdoc->last->line, mdoc->last->pos, NULL);
2220
2473
}
2221
2222
/*
2223
 * Return an upper bound for the string distance (allowing
2224
 * transpositions).  Not a full Levenshtein implementation
2225
 * because Levenshtein is quadratic in the string length
2226
 * and this function is called for every standard name,
2227
 * so the check for each custom name would be cubic.
2228
 * The following crude heuristics is linear, resulting
2229
 * in quadratic behaviour for checking one custom name,
2230
 * which does not cause measurable slowdown.
2231
 */
2232
static int
2233
similar(const char *s1, const char *s2)
2234
{
2235
	const int	maxdist = 3;
2236
	int		dist = 0;
2237
2238

442968
	while (s1[0] != '\0' && s2[0] != '\0') {
2239
102554
		if (s1[0] == s2[0]) {
2240
21565
			s1++;
2241
21565
			s2++;
2242
21565
			continue;
2243
		}
2244
80989
		if (++dist > maxdist)
2245
486
			return INT_MAX;
2246
80503
		if (s1[1] == s2[1]) {  /* replacement */
2247
6237
			s1++;
2248
6237
			s2++;
2249

83891
		} else if (s1[0] == s2[1] && s1[1] == s2[0]) {
2250
249
			s1 += 2;	/* transposition */
2251
249
			s2 += 2;
2252
74266
		} else if (s1[0] == s2[1])  /* insertion */
2253
3139
			s2++;
2254
70878
		else if (s1[1] == s2[0])  /* deletion */
2255
4434
			s1++;
2256
		else
2257
66444
			return INT_MAX;
2258
	}
2259
312
	dist += strlen(s1) + strlen(s2);
2260
312
	return dist > maxdist ? INT_MAX : dist;
2261
67242
}
2262
2263
static void
2264
post_sh_head(POST_ARGS)
2265
{
2266
	struct roff_node	*nch;
2267
	const char		*goodsec;
2268
	const char *const	*testsec;
2269
	int			 dist, mindist;
2270
	enum roff_sec		 sec;
2271
2272
	/*
2273
	 * Process a new section.  Sections are either "named" or
2274
	 * "custom".  Custom sections are user-defined, while named ones
2275
	 * follow a conventional order and may only appear in certain
2276
	 * manual sections.
2277
	 */
2278
2279
151310
	sec = mdoc->last->sec;
2280
2281
	/* The NAME should be first. */
2282
2283

134132
	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
2284
108
		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
2285
36
		    mdoc->last->line, mdoc->last->pos, "Sh %s",
2286
108
		    sec != SEC_CUSTOM ? secnames[sec] :
2287
		    (nch = mdoc->last->child) == NULL ? "" :
2288
		    nch->type == ROFFT_TEXT ? nch->string :
2289
		    roff_name[nch->tok]);
2290
2291
	/* The SYNOPSIS gets special attention in other areas. */
2292
2293
75655
	if (sec == SEC_SYNOPSIS) {
2294
8778
		roff_setreg(mdoc->roff, "nS", 1, '=');
2295
8778
		mdoc->flags |= MDOC_SYNOPSIS;
2296
8778
	} else {
2297
66877
		roff_setreg(mdoc->roff, "nS", 0, '=');
2298
66877
		mdoc->flags &= ~MDOC_SYNOPSIS;
2299
	}
2300
2301
	/* Mark our last section. */
2302
2303
75655
	mdoc->lastsec = sec;
2304
2305
	/* We don't care about custom sections after this. */
2306
2307
75655
	if (sec == SEC_CUSTOM) {
2308

6404
		if ((nch = mdoc->last->child) == NULL ||
2309
6404
		    nch->type != ROFFT_TEXT || nch->next != NULL)
2310
			return;
2311
		goodsec = NULL;
2312
		mindist = INT_MAX;
2313
140888
		for (testsec = secnames + 1; *testsec != NULL; testsec++) {
2314
67242
			dist = similar(nch->string, *testsec);
2315
67242
			if (dist < mindist) {
2316
24
				goodsec = *testsec;
2317
				mindist = dist;
2318
24
			}
2319
		}
2320
3202
		if (goodsec != NULL)
2321
48
			mandoc_vmsg(MANDOCERR_SEC_TYPO, mdoc->parse,
2322
24
			    nch->line, nch->pos, "Sh %s instead of %s",
2323
24
			    nch->string, goodsec);
2324
3202
		return;
2325
	}
2326
2327
	/*
2328
	 * Check whether our non-custom section is being repeated or is
2329
	 * out of order.
2330
	 */
2331
2332
72453
	if (sec == mdoc->lastnamed)
2333
144
		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
2334
72
		    mdoc->last->line, mdoc->last->pos,
2335
72
		    "Sh %s", secnames[sec]);
2336
2337
72453
	if (sec < mdoc->lastnamed)
2338
108
		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
2339
54
		    mdoc->last->line, mdoc->last->pos,
2340
54
		    "Sh %s", secnames[sec]);
2341
2342
	/* Mark the last named section. */
2343
2344
72453
	mdoc->lastnamed = sec;
2345
2346
	/* Check particular section/manual conventions. */
2347
2348
72453
	if (mdoc->meta.msec == NULL)
2349
216
		return;
2350
2351
	goodsec = NULL;
2352

77589
	switch (sec) {
2353
	case SEC_ERRORS:
2354
1065
		if (*mdoc->meta.msec == '4')
2355
			break;
2356
1023
		goodsec = "2, 3, 4, 9";
2357
		/* FALLTHROUGH */
2358
	case SEC_RETURN_VALUES:
2359
	case SEC_LIBRARY:
2360
3909
		if (*mdoc->meta.msec == '2')
2361
			break;
2362
3114
		if (*mdoc->meta.msec == '3')
2363
			break;
2364
243
		if (NULL == goodsec)
2365
192
			goodsec = "2, 3, 9";
2366
		/* FALLTHROUGH */
2367
	case SEC_CONTEXT:
2368
378
		if (*mdoc->meta.msec == '9')
2369
			break;
2370
36
		if (NULL == goodsec)
2371
			goodsec = "9";
2372
72
		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
2373
36
		    mdoc->last->line, mdoc->last->pos,
2374
36
		    "Sh %s for %s only", secnames[sec], goodsec);
2375
36
		break;
2376
	default:
2377
		break;
2378
	}
2379
147892
}
2380
2381
static void
2382
post_xr(POST_ARGS)
2383
{
2384
	struct roff_node *n, *nch;
2385
2386
143558
	n = mdoc->last;
2387
71779
	nch = n->child;
2388
71779
	if (nch->next == NULL) {
2389
154
		mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
2390
77
		    n->line, n->pos, "Xr %s", nch->string);
2391
77
	} else {
2392
71702
		assert(nch->next == n->last);
2393
143404
		if(mandoc_xr_add(nch->next->string, nch->string,
2394
71702
		    nch->line, nch->pos))
2395
			mandoc_vmsg(MANDOCERR_XR_SELF, mdoc->parse,
2396
			    nch->line, nch->pos, "Xr %s %s",
2397
			    nch->string, nch->next->string);
2398
	}
2399
71779
	post_delim_nb(mdoc);
2400
71779
}
2401
2402
static void
2403
post_ignpar(POST_ARGS)
2404
{
2405
	struct roff_node *np;
2406
2407

472764
	switch (mdoc->last->type) {
2408
	case ROFFT_BLOCK:
2409
78794
		post_prevpar(mdoc);
2410
78794
		return;
2411
	case ROFFT_HEAD:
2412
78794
		post_delim(mdoc);
2413
78794
		post_hyph(mdoc);
2414
78794
		return;
2415
	case ROFFT_BODY:
2416
		break;
2417
	default:
2418
		return;
2419
	}
2420
2421
78794
	if ((np = mdoc->last->child) != NULL)
2422

157228
		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2423
			mandoc_vmsg(MANDOCERR_PAR_SKIP,
2424
			    mdoc->parse, np->line, np->pos,
2425
			    "%s after %s", roff_name[np->tok],
2426
			    roff_name[mdoc->last->tok]);
2427
			roff_node_delete(mdoc, np);
2428
		}
2429
2430
78794
	if ((np = mdoc->last->last) != NULL)
2431

157066
		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2432
324
			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2433
162
			    np->line, np->pos, "%s at the end of %s",
2434
162
			    roff_name[np->tok],
2435
162
			    roff_name[mdoc->last->tok]);
2436
162
			roff_node_delete(mdoc, np);
2437
162
		}
2438
315176
}
2439
2440
static void
2441
post_prevpar(POST_ARGS)
2442
{
2443
	struct roff_node *n;
2444
2445
1077910
	n = mdoc->last;
2446
538955
	if (NULL == n->prev)
2447
136096
		return;
2448

735522
	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2449
137589
		return;
2450
2451
	/*
2452
	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2453
	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
2454
	 */
2455
2456

527514
	if (n->prev->tok != MDOC_Pp &&
2457
262244
	    n->prev->tok != MDOC_Lp &&
2458
262244
	    n->prev->tok != ROFF_br)
2459
262163
		return;
2460

6007
	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
2461
2864
		return;
2462

270
	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
2463
9
		return;
2464

234
	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
2465
		return;
2466
2467
468
	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2468
234
	    n->prev->line, n->prev->pos, "%s before %s",
2469
234
	    roff_name[n->prev->tok], roff_name[n->tok]);
2470
234
	roff_node_delete(mdoc, n->prev);
2471
539189
}
2472
2473
static void
2474
post_par(POST_ARGS)
2475
{
2476
	struct roff_node *np;
2477
2478
161770
	np = mdoc->last;
2479

158011
	if (np->tok != ROFF_br && np->tok != ROFF_sp)
2480
70613
		post_prevpar(mdoc);
2481
2482
80885
	if (np->tok == ROFF_sp) {
2483

9015
		if (np->child != NULL && np->child->next != NULL)
2484
			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2485
			    np->child->next->line, np->child->next->pos,
2486
			    "sp ... %s", np->child->next->string);
2487
74372
	} else if (np->child != NULL)
2488
36
		mandoc_vmsg(MANDOCERR_ARG_SKIP,
2489
36
		    mdoc->parse, np->line, np->pos, "%s %s",
2490
36
		    roff_name[np->tok], np->child->string);
2491
2492
80885
	if ((np = mdoc->last->prev) == NULL) {
2493
543
		np = mdoc->last->parent;
2494

960
		if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
2495
354
			return;
2496

164209
	} else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
2497
80234
	    (mdoc->last->tok != ROFF_br ||
2498
7320
	     (np->tok != ROFF_sp && np->tok != ROFF_br)))
2499
80153
		return;
2500
2501
756
	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2502
378
	    mdoc->last->line, mdoc->last->pos, "%s after %s",
2503
378
	    roff_name[mdoc->last->tok], roff_name[np->tok]);
2504
378
	roff_node_delete(mdoc, mdoc->last);
2505
81263
}
2506
2507
static void
2508
post_dd(POST_ARGS)
2509
{
2510
	struct roff_node *n;
2511
34644
	char		 *datestr;
2512
2513
17322
	n = mdoc->last;
2514
17322
	n->flags |= NODE_NOPRT;
2515
2516
17322
	if (mdoc->meta.date != NULL) {
2517
144
		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2518
72
		    n->line, n->pos, "Dd");
2519
72
		free(mdoc->meta.date);
2520
17322
	} else if (mdoc->flags & MDOC_PBODY)
2521
72
		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2522
36
		    n->line, n->pos, "Dd");
2523
17214
	else if (mdoc->meta.title != NULL)
2524
72
		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2525
36
		    n->line, n->pos, "Dd after Dt");
2526
17178
	else if (mdoc->meta.os != NULL)
2527
		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2528
		    n->line, n->pos, "Dd after Os");
2529
2530

34635
	if (n->child == NULL || n->child->string[0] == '\0') {
2531
27
		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2532
9
		    mandoc_normdate(mdoc, NULL, n->line, n->pos);
2533
9
		return;
2534
	}
2535
2536
17313
	datestr = NULL;
2537
17313
	deroff(&datestr, n);
2538
17313
	if (mdoc->quick)
2539
		mdoc->meta.date = datestr;
2540
	else {
2541
17313
		mdoc->meta.date = mandoc_normdate(mdoc,
2542
17313
		    datestr, n->line, n->pos);
2543
17313
		free(datestr);
2544
	}
2545
34635
}
2546
2547
static void
2548
post_dt(POST_ARGS)
2549
{
2550
	struct roff_node *nn, *n;
2551
	const char	 *cp;
2552
	char		 *p;
2553
2554
34590
	n = mdoc->last;
2555
17295
	n->flags |= NODE_NOPRT;
2556
2557
17295
	if (mdoc->flags & MDOC_PBODY) {
2558
126
		mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2559
63
		    n->line, n->pos, "Dt");
2560
63
		return;
2561
	}
2562
2563
17232
	if (mdoc->meta.title != NULL)
2564
72
		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2565
36
		    n->line, n->pos, "Dt");
2566
17196
	else if (mdoc->meta.os != NULL)
2567
144
		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2568
72
		    n->line, n->pos, "Dt after Os");
2569
2570
17232
	free(mdoc->meta.title);
2571
17232
	free(mdoc->meta.msec);
2572
17232
	free(mdoc->meta.vol);
2573
17232
	free(mdoc->meta.arch);
2574
2575
17232
	mdoc->meta.title = NULL;
2576
17232
	mdoc->meta.msec = NULL;
2577
17232
	mdoc->meta.vol = NULL;
2578
17232
	mdoc->meta.arch = NULL;
2579
2580
	/* Mandatory first argument: title. */
2581
2582
17232
	nn = n->child;
2583

34437
	if (nn == NULL || *nn->string == '\0') {
2584
27
		mandoc_msg(MANDOCERR_DT_NOTITLE,
2585
27
		    mdoc->parse, n->line, n->pos, "Dt");
2586
27
		mdoc->meta.title = mandoc_strdup("UNTITLED");
2587
27
	} else {
2588
17205
		mdoc->meta.title = mandoc_strdup(nn->string);
2589
2590
		/* Check that all characters are uppercase. */
2591
2592
324202
		for (p = nn->string; *p != '\0'; p++)
2593
144986
			if (islower((unsigned char)*p)) {
2594
90
				mandoc_vmsg(MANDOCERR_TITLE_CASE,
2595
90
				    mdoc->parse, nn->line,
2596
90
				    nn->pos + (p - nn->string),
2597
				    "Dt %s", nn->string);
2598
90
				break;
2599
			}
2600
	}
2601
2602
	/* Mandatory second argument: section. */
2603
2604
17232
	if (nn != NULL)
2605
17205
		nn = nn->next;
2606
2607
17232
	if (nn == NULL) {
2608
54
		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2609
54
		    mdoc->parse, n->line, n->pos,
2610
54
		    "Dt %s", mdoc->meta.title);
2611
54
		mdoc->meta.vol = mandoc_strdup("LOCAL");
2612
54
		return;  /* msec and arch remain NULL. */
2613
	}
2614
2615
17178
	mdoc->meta.msec = mandoc_strdup(nn->string);
2616
2617
	/* Infer volume title from section number. */
2618
2619
17178
	cp = mandoc_a2msec(nn->string);
2620
17178
	if (cp == NULL) {
2621
18
		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2622
9
		    nn->line, nn->pos, "Dt ... %s", nn->string);
2623
9
		mdoc->meta.vol = mandoc_strdup(nn->string);
2624
9
	} else
2625
17169
		mdoc->meta.vol = mandoc_strdup(cp);
2626
2627
	/* Optional third argument: architecture. */
2628
2629
17178
	if ((nn = nn->next) == NULL)
2630
15984
		return;
2631
2632
15762
	for (p = nn->string; *p != '\0'; p++)
2633
6687
		*p = tolower((unsigned char)*p);
2634
1194
	mdoc->meta.arch = mandoc_strdup(nn->string);
2635
2636
	/* Ignore fourth and later arguments. */
2637
2638
1194
	if ((nn = nn->next) != NULL)
2639
54
		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2640
27
		    nn->line, nn->pos, "Dt ... %s", nn->string);
2641
18489
}
2642
2643
static void
2644
post_bx(POST_ARGS)
2645
{
2646
	struct roff_node	*n, *nch;
2647
	const char		*macro;
2648
2649
3862
	post_delim_nb(mdoc);
2650
2651
1931
	n = mdoc->last;
2652
1931
	nch = n->child;
2653
2654
1931
	if (nch != NULL) {
2655
3064
		macro = !strcmp(nch->string, "Open") ? "Ox" :
2656
1532
		    !strcmp(nch->string, "Net") ? "Nx" :
2657
3064
		    !strcmp(nch->string, "Free") ? "Fx" :
2658
1532
		    !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
2659
1532
		if (macro != NULL)
2660
			mandoc_msg(MANDOCERR_BX, mdoc->parse,
2661
			    n->line, n->pos, macro);
2662
1532
		mdoc->last = nch;
2663
1532
		nch = nch->next;
2664
1532
		mdoc->next = ROFF_NEXT_SIBLING;
2665
1532
		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2666
1532
		mdoc->last->flags |= NODE_NOSRC;
2667
		mdoc->next = ROFF_NEXT_SIBLING;
2668
1532
	} else
2669
		mdoc->next = ROFF_NEXT_CHILD;
2670
1931
	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2671
1931
	mdoc->last->flags |= NODE_NOSRC;
2672
2673
1931
	if (nch == NULL) {
2674
1628
		mdoc->last = n;
2675
1628
		return;
2676
	}
2677
2678
303
	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2679
303
	mdoc->last->flags |= NODE_NOSRC;
2680
303
	mdoc->next = ROFF_NEXT_SIBLING;
2681
303
	roff_word_alloc(mdoc, n->line, n->pos, "-");
2682
303
	mdoc->last->flags |= NODE_NOSRC;
2683
303
	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2684
303
	mdoc->last->flags |= NODE_NOSRC;
2685
303
	mdoc->last = n;
2686
2687
	/*
2688
	 * Make `Bx's second argument always start with an uppercase
2689
	 * letter.  Groff checks if it's an "accepted" term, but we just
2690
	 * uppercase blindly.
2691
	 */
2692
2693
303
	*nch->string = (char)toupper((unsigned char)*nch->string);
2694
2234
}
2695
2696
static void
2697
post_os(POST_ARGS)
2698
{
2699
#ifndef OSNAME
2700
34572
	struct utsname	  utsname;
2701
	static char	 *defbuf;
2702
#endif
2703
	struct roff_node *n;
2704
2705
17286
	n = mdoc->last;
2706
17286
	n->flags |= NODE_NOPRT;
2707
2708
17286
	if (mdoc->meta.os != NULL)
2709
144
		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2710
72
		    n->line, n->pos, "Os");
2711
17214
	else if (mdoc->flags & MDOC_PBODY)
2712
72
		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2713
36
		    n->line, n->pos, "Os");
2714
2715
17286
	post_delim(mdoc);
2716
2717
	/*
2718
	 * Set the operating system by way of the `Os' macro.
2719
	 * The order of precedence is:
2720
	 * 1. the argument of the `Os' macro, unless empty
2721
	 * 2. the -Ios=foo command line argument, if provided
2722
	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2723
	 * 4. "sysname release" from uname(3)
2724
	 */
2725
2726
17286
	free(mdoc->meta.os);
2727
17286
	mdoc->meta.os = NULL;
2728
17286
	deroff(&mdoc->meta.os, n);
2729
17286
	if (mdoc->meta.os)
2730
		goto out;
2731
2732
17150
	if (mdoc->os_s != NULL) {
2733
8199
		mdoc->meta.os = mandoc_strdup(mdoc->os_s);
2734
8199
		goto out;
2735
	}
2736
2737
#ifdef OSNAME
2738
	mdoc->meta.os = mandoc_strdup(OSNAME);
2739
#else /*!OSNAME */
2740
8951
	if (defbuf == NULL) {
2741
246
		if (uname(&utsname) == -1) {
2742
			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2743
			    n->line, n->pos, "Os");
2744
			defbuf = mandoc_strdup("UNKNOWN");
2745
		} else
2746
246
			mandoc_asprintf(&defbuf, "%s %s",
2747
246
			    utsname.sysname, utsname.release);
2748
	}
2749
8951
	mdoc->meta.os = mandoc_strdup(defbuf);
2750
#endif /*!OSNAME*/
2751
2752
out:
2753
34436
	if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
2754
17214
		if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
2755
17150
			mdoc->meta.os_e = MANDOC_OS_OPENBSD;
2756
64
		else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
2757
36
			mdoc->meta.os_e = MANDOC_OS_NETBSD;
2758
	}
2759
2760
	/*
2761
	 * This is the earliest point where we can check
2762
	 * Mdocdate conventions because we don't know
2763
	 * the operating system earlier.
2764
	 */
2765
2766
17286
	if (n->child != NULL)
2767
272
		mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse,
2768
136
		    n->child->line, n->child->pos,
2769
136
		    "Os %s (%s)", n->child->string,
2770
136
		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2771
		    "OpenBSD" : "NetBSD");
2772
2773
51624
	while (n->tok != MDOC_Dd)
2774
34446
		if ((n = n->prev) == NULL)
2775
108
			return;
2776
17178
	if ((n = n->child) == NULL)
2777
9
		return;
2778
17169
	if (strncmp(n->string, "$" "Mdocdate", 9)) {
2779
310
		if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
2780
309
			mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING,
2781
309
			    mdoc->parse, n->line, n->pos,
2782
309
			    "Dd %s (OpenBSD)", n->string);
2783
	} else {
2784
16859
		if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
2785
72
			mandoc_vmsg(MANDOCERR_MDOCDATE,
2786
72
			    mdoc->parse, n->line, n->pos,
2787
72
			    "Dd %s (NetBSD)", n->string);
2788
	}
2789
34455
}
2790
2791
enum roff_sec
2792
mdoc_a2sec(const char *p)
2793
{
2794
	int		 i;
2795
2796
1460139
	for (i = 0; i < (int)SEC__MAX; i++)
2797

1299223
		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2798
72453
			return (enum roff_sec)i;
2799
2800
3202
	return SEC_CUSTOM;
2801
75655
}
2802
2803
static size_t
2804
macro2len(enum roff_tok macro)
2805
{
2806
2807










2718
	switch (macro) {
2808
	case MDOC_Ad:
2809
		return 12;
2810
	case MDOC_Ao:
2811
		return 12;
2812
	case MDOC_An:
2813
		return 12;
2814
	case MDOC_Aq:
2815
		return 12;
2816
	case MDOC_Ar:
2817
12
		return 12;
2818
	case MDOC_Bo:
2819
		return 12;
2820
	case MDOC_Bq:
2821
		return 12;
2822
	case MDOC_Cd:
2823
		return 12;
2824
	case MDOC_Cm:
2825
3
		return 10;
2826
	case MDOC_Do:
2827
		return 10;
2828
	case MDOC_Dq:
2829
		return 12;
2830
	case MDOC_Dv:
2831
21
		return 12;
2832
	case MDOC_Eo:
2833
		return 12;
2834
	case MDOC_Em:
2835
		return 10;
2836
	case MDOC_Er:
2837
1110
		return 17;
2838
	case MDOC_Ev:
2839
6
		return 15;
2840
	case MDOC_Fa:
2841
		return 12;
2842
	case MDOC_Fl:
2843
114
		return 10;
2844
	case MDOC_Fo:
2845
		return 16;
2846
	case MDOC_Fn:
2847
		return 16;
2848
	case MDOC_Ic:
2849
9
		return 10;
2850
	case MDOC_Li:
2851
		return 16;
2852
	case MDOC_Ms:
2853
		return 6;
2854
	case MDOC_Nm:
2855
		return 10;
2856
	case MDOC_No:
2857
3
		return 12;
2858
	case MDOC_Oo:
2859
		return 10;
2860
	case MDOC_Op:
2861
3
		return 14;
2862
	case MDOC_Pa:
2863
75
		return 32;
2864
	case MDOC_Pf:
2865
		return 12;
2866
	case MDOC_Po:
2867
		return 12;
2868
	case MDOC_Pq:
2869
		return 12;
2870
	case MDOC_Ql:
2871
		return 16;
2872
	case MDOC_Qo:
2873
		return 12;
2874
	case MDOC_So:
2875
		return 12;
2876
	case MDOC_Sq:
2877
		return 12;
2878
	case MDOC_Sy:
2879
		return 6;
2880
	case MDOC_Sx:
2881
		return 16;
2882
	case MDOC_Tn:
2883
		return 10;
2884
	case MDOC_Va:
2885
3
		return 12;
2886
	case MDOC_Vt:
2887
		return 12;
2888
	case MDOC_Xr:
2889
		return 10;
2890
	default:
2891
		break;
2892
	};
2893
	return 0;
2894
1359
}