GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/mdoc_validate.c Lines: 1286 1383 93.0 %
Date: 2017-11-13 Branches: 864 1029 84.0 %

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
2396826
	n = mdoc->last;
293
1198413
	mdoc->last = mdoc->last->child;
294
4782130
	while (mdoc->last != NULL) {
295
1192652
		mdoc_node_validate(mdoc);
296
1192652
		if (mdoc->last == n)
297
72
			mdoc->last = mdoc->last->child;
298
		else
299
1192580
			mdoc->last = mdoc->last->next;
300
	}
301
302
2386833
	mdoc->last = n;
303
2386833
	mdoc->next = ROFF_NEXT_SIBLING;
304

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

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

668253
		if (n->parent->tok == MDOC_It ||
310
499045
		    (n->parent->type == ROFFT_BODY &&
311
201901
		     (n->parent->tok == MDOC_Sh ||
312
53773
		      n->parent->tok == MDOC_Ss)))
313
277536
			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
5761
		post_root(mdoc);
320
5761
		break;
321
	default:
322
571709
		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
571709
		if (n->child != NULL)
331
473052
			n->child->flags &= ~NODE_DELIMC;
332
571709
		if (n->last != NULL)
333
473052
			n->last->flags &= ~NODE_DELIMO;
334
335
		/* Call the macro's postprocessor. */
336
337
571709
		if (n->tok < ROFF_MAX) {
338
3530
			switch(n->tok) {
339
			case ROFF_br:
340
			case ROFF_sp:
341
3424
				post_par(mdoc);
342
3424
				break;
343
			default:
344
106
				roff_validate(mdoc);
345
106
				break;
346
			}
347
			break;
348
		}
349
350

1136358
		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
351
568179
		p = mdoc_valids + n->tok;
352
568179
		if (*p)
353
542609
			(*p)(mdoc);
354
568179
		if (mdoc->last == n)
355
567789
			mdoc_state(mdoc, n);
356
		break;
357
	}
358
1198413
}
359
360
static void
361
check_args(struct roff_man *mdoc, struct roff_node *n)
362
{
363
	int		 i;
364
365
1143418
	if (NULL == n->args)
366
560471
		return;
367
368
11238
	assert(n->args->argc);
369
67472
	for (i = 0; i < (int)n->args->argc; i++)
370
22498
		check_argv(mdoc, n, &n->args->argv[i]);
371
582947
}
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
87778
	for (i = 0; i < (int)v->sz; i++)
379
10142
		check_text(mdoc, v->line, v->pos, v->value[i]);
380
22498
}
381
382
static void
383
check_text(struct roff_man *mdoc, int ln, int pos, char *p)
384
{
385
	char		*cp;
386
387
1245052
	if (MDOC_LITERAL & mdoc->flags)
388
18446
		return;
389
390
1208772
	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
391
612
		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
392
306
		    ln, pos + (int)(p - cp), NULL);
393
1226606
}
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
555072
	if (*p == '\0')
401
471
		return;
402
403
277065
	if ((cp = strstr(p, "OpenBSD")) != NULL)
404
38
		mandoc_msg(MANDOCERR_BX, mdoc->parse,
405
19
		    ln, pos + (cp - p), "Ox");
406
277065
	if ((cp = strstr(p, "NetBSD")) != NULL)
407
		mandoc_msg(MANDOCERR_BX, mdoc->parse,
408
		    ln, pos + (cp - p), "Nx");
409
277065
	if ((cp = strstr(p, "FreeBSD")) != NULL)
410
2
		mandoc_msg(MANDOCERR_BX, mdoc->parse,
411
1
		    ln, pos + (cp - p), "Fx");
412
277065
	if ((cp = strstr(p, "DragonFly")) != NULL)
413
		mandoc_msg(MANDOCERR_BX, mdoc->parse,
414
		    ln, pos + (cp - p), "Dx");
415
416
	cp = p;
417
554206
	while ((cp = strstr(cp + 1, "()")) != NULL) {
418
524
		for (cpr = cp - 1; cpr >= p; cpr--)
419

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

103
		if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
422
35
			cpr++;
423
70
			mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse,
424
35
			    ln, pos + (cpr - p),
425
35
			    "%.*s()", (int)(cp - cpr), cpr);
426
35
		}
427
	}
428
554601
}
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
85712
	tok = mdoc->last->tok;
439
42856
	nch = mdoc->last->last;
440

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

490
	if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
449
212
	    tok == MDOC_Ss || tok == MDOC_Fo))
450
106
		return;
451
452
132
	mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse,
453
66
	    nch->line, nch->pos + (lc - nch->string),
454
66
	    "%s%s %s", roff_name[tok],
455
66
	    nch == mdoc->last->child ? "" : " ...", nch->string);
456
42922
}
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
457212
	tok = mdoc->last->tok;
473
228606
	nch = mdoc->last->last;
474

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

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

2593
	switch (*lc) {
494
	case ')':
495
10442
		for (cp = lc; cp >= nch->string; cp--)
496
5123
			if (*cp == '(')
497
277
				return;
498
		break;
499
	case '.':
500

2651
		if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
501
669
			return;
502
367
		if (lc[-1] == '.')
503
29
			return;
504
		break;
505
	case ';':
506
41
		if (tok == MDOC_Vt)
507
28
			return;
508
		break;
509
	case '?':
510
82
		if (lc[-1] == '?')
511
11
			return;
512
		break;
513
	case ']':
514
2694
		for (cp = lc; cp >= nch->string; cp--)
515
1328
			if (*cp == '[')
516
208
				return;
517
		break;
518
	case '|':
519

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

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

3356
	if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
531
1575
	    tok == MDOC_Li || tok == MDOC_Po || tok == MDOC_Pq ||
532
525
	    tok == MDOC_Sy)) {
533
		nw = 0;
534
1486
		for (cp = lc - 1; cp >= nch->string; cp--) {
535
661
			if (*cp == ' ') {
536
58
				nw++;
537

116
				if (cp > nch->string && cp[-1] == ',')
538
6
					cp--;
539
603
			} else if (isalpha((unsigned int)*cp)) {
540
594
				if (nw > 1)
541
23
					return;
542
			} else
543
				break;
544
		}
545
	}
546
547
1416
	mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse,
548
708
	    nch->line, nch->pos + (lc - nch->string),
549
708
	    "%s%s %s", roff_name[tok],
550
708
	    nch == mdoc->last->child ? "" : " ...", nch->string);
551
229314
}
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
14574
	n = mdoc->last->parent;
563
7287
	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
21849
	wa = (n->args == NULL) ? NULL : n->args->argv;
572
	mdoclt = MDOC_ARG_MAX;
573

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



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

14514
		if (n->norm->Bl.width ||
670
7263
		    n->norm->Bl.offs ||
671
7251
		    n->norm->Bl.comp)
672
12
			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
673
12
			    mdoc->parse, n->line, n->pos, "Bl -%s",
674
12
			    mdoc_argnames[n->args->argv[0].arg]);
675
676
7263
		n->norm->Bl.type = lt;
677
7263
		if (LIST_column == lt) {
678
427
			n->norm->Bl.ncols = argv->sz;
679
427
			n->norm->Bl.cols = (void *)argv->value;
680
427
		}
681
	}
682
683
	/* Allow lists to default to LIST_item. */
684
685
7287
	if (LIST__NONE == n->norm->Bl.type) {
686
48
		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
687
24
		    n->line, n->pos, "Bl");
688
24
		n->norm->Bl.type = LIST_item;
689
		mdoclt = MDOC_Item;
690
24
	}
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


14334
	switch (n->norm->Bl.type) {
700
	case LIST_tag:
701
4598
		if (n->norm->Bl.width == NULL)
702
18
			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
703
9
			    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
1242
		if (n->norm->Bl.width != NULL)
711
90
			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
712
45
			    wa->line, wa->pos, "Bl -%s",
713
45
			    mdoc_argnames[mdoclt]);
714
1242
		n->norm->Bl.width = NULL;
715
1242
		break;
716
	case LIST_bullet:
717
	case LIST_dash:
718
	case LIST_hyphen:
719
903
		if (n->norm->Bl.width == NULL)
720
602
			n->norm->Bl.width = "2n";
721
		break;
722
	case LIST_enum:
723
304
		if (n->norm->Bl.width == NULL)
724
191
			n->norm->Bl.width = "3n";
725
		break;
726
	default:
727
		break;
728
	}
729
7287
}
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
5850
	n = mdoc->last;
740

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


5429
		switch (argv->arg) {
745
		case MDOC_Centred:
746
			dt = DISP_centered;
747
15
			break;
748
		case MDOC_Ragged:
749
			dt = DISP_ragged;
750
407
			break;
751
		case MDOC_Unfilled:
752
			dt = DISP_unfilled;
753
163
			break;
754
		case MDOC_Filled:
755
			dt = DISP_filled;
756
59
			break;
757
		case MDOC_Literal:
758
			dt = DISP_literal;
759
2293
			break;
760
		case MDOC_File:
761
72
			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
762
36
			    n->line, n->pos, NULL);
763
36
			break;
764
		case MDOC_Offset:
765
2341
			if (0 == argv->sz) {
766
21
				mandoc_msg(MANDOCERR_ARG_EMPTY,
767
21
				    mdoc->parse, argv->line,
768
21
				    argv->pos, "Bd -offset");
769
21
				break;
770
			}
771
2320
			if (NULL != n->norm->Bd.offs)
772
12
				mandoc_vmsg(MANDOCERR_ARG_REP,
773
12
				    mdoc->parse, argv->line,
774
12
				    argv->pos, "Bd -offset %s",
775
12
				    argv->value[0]);
776
2320
			rewrite_macro2len(mdoc, argv->value);
777
2320
			n->norm->Bd.offs = argv->value[0];
778
2320
			break;
779
		case MDOC_Compact:
780
115
			if (n->norm->Bd.comp)
781
12
				mandoc_msg(MANDOCERR_ARG_REP,
782
12
				    mdoc->parse, argv->line,
783
12
				    argv->pos, "Bd -compact");
784
115
			n->norm->Bd.comp = 1;
785
115
			break;
786
		default:
787
			abort();
788
		}
789
5429
		if (DISP__NONE == dt)
790
			continue;
791
792
2937
		if (DISP__NONE == n->norm->Bd.type)
793
2913
			n->norm->Bd.type = dt;
794
		else
795
24
			mandoc_vmsg(MANDOCERR_BD_REP,
796
24
			    mdoc->parse, n->line, n->pos,
797
24
			    "Bd -%s", mdoc_argnames[argv->arg]);
798
	}
799
800
2925
	if (DISP__NONE == n->norm->Bd.type) {
801
24
		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
802
12
		    n->line, n->pos, "Bd");
803
12
		n->norm->Bd.type = DISP_ragged;
804
12
	}
805
2925
}
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
3888
	n = mdoc->last;
819
1944
	if (n->args == NULL)
820
1556
		return;
821
822
824
	for (i = 1; i < n->args->argc; i++) {
823
24
		argv = n->args->argv + i;
824
24
		mandoc_vmsg(MANDOCERR_AN_REP,
825
24
		    mdoc->parse, argv->line, argv->pos,
826
24
		    "An -%s", mdoc_argnames[argv->arg]);
827
	}
828
829
	argv = n->args->argv;
830
388
	if (argv->arg == MDOC_Split)
831
12
		n->norm->An.auth = AUTH_split;
832
376
	else if (argv->arg == MDOC_Nosplit)
833
		n->norm->An.auth = AUTH_nosplit;
834
	else
835
		abort();
836
2332
}
837
838
static void
839
post_eoln(POST_ARGS)
840
{
841
	struct roff_node	*n;
842
843
180
	post_useless(mdoc);
844
90
	n = mdoc->last;
845
90
	if (n->child != NULL)
846
96
		mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line,
847
48
		    n->pos, "%s %s", roff_name[n->tok], n->child->string);
848
849
324
	while (n->child != NULL)
850
72
		roff_node_delete(mdoc, n->child);
851
852
90
	roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
853
	    "is currently in beta test." : "currently under development.");
854
90
	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
855
90
	mdoc->last = n;
856
90
}
857
858
static int
859
build_list(struct roff_man *mdoc, int tok)
860
{
861
	struct roff_node	*n;
862
	int			 ic;
863
864
510
	n = mdoc->last->next;
865
336
	for (ic = 1;; ic++) {
866
336
		roff_elem_alloc(mdoc, n->line, n->pos, tok);
867
336
		mdoc->last->flags |= NODE_NOSRC;
868
336
		mdoc_node_relink(mdoc, n);
869
336
		n = mdoc->last = mdoc->last->parent;
870
336
		mdoc->next = ROFF_NEXT_SIBLING;
871
336
		if (n->next == NULL)
872
255
			return ic;
873

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

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

363
	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
1029
246
		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
1030
123
		    n->line, n->pos, roff_name[n->tok]);
1031
219
}
1032
1033
static void
1034
post_useless(POST_ARGS)
1035
{
1036
	struct roff_node *n;
1037
1038
2548
	n = mdoc->last;
1039
2548
	mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse,
1040
1274
	    n->line, n->pos, roff_name[n->tok]);
1041
1274
}
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
1050
	np = mdoc->last;
1058
525
	if (np->type != ROFFT_HEAD)
1059
352
		return;
1060
1061
173
	assert(np->parent->type == ROFFT_BLOCK);
1062
173
	assert(np->parent->tok == MDOC_Bf);
1063
1064
	/* Check the number of arguments. */
1065
1066
173
	nch = np->child;
1067
173
	if (np->parent->args == NULL) {
1068
79
		if (nch == NULL) {
1069
18
			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
1070
9
			    np->line, np->pos, "Bf");
1071
9
			return;
1072
		}
1073
70
		nch = nch->next;
1074
70
	}
1075
164
	if (nch != NULL)
1076
90
		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1077
45
		    nch->line, nch->pos, "Bf ... %s", nch->string);
1078
1079
	/* Extract argument into data. */
1080
1081
164
	if (np->parent->args != NULL) {
1082

94
		switch (np->parent->args->argv[0].arg) {
1083
		case MDOC_Emphasis:
1084
			np->norm->Bf.font = FONT_Em;
1085
17
			break;
1086
		case MDOC_Literal:
1087
			np->norm->Bf.font = FONT_Li;
1088
12
			break;
1089
		case MDOC_Symbolic:
1090
			np->norm->Bf.font = FONT_Sy;
1091
65
			break;
1092
		default:
1093
			abort();
1094
		}
1095
94
		return;
1096
	}
1097
1098
	/* Extract parameter into data. */
1099
1100
70
	if ( ! strcmp(np->child->string, "Em"))
1101
22
		np->norm->Bf.font = FONT_Em;
1102
48
	else if ( ! strcmp(np->child->string, "Li"))
1103
6
		np->norm->Bf.font = FONT_Li;
1104
42
	else if ( ! strcmp(np->child->string, "Sy"))
1105
33
		np->norm->Bf.font = FONT_Sy;
1106
	else
1107
18
		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
1108
9
		    np->child->line, np->child->pos,
1109
9
		    "Bf %s", np->child->string);
1110
595
}
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
43574
	n = mdoc->last->child;
1120
21787
	pos = strcspn(n->string, "()");
1121
21787
	cp = n->string + pos;
1122

21949
	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
1123
24
		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
1124
12
		    n->line, n->pos + pos, n->string);
1125
21787
}
1126
1127
static void
1128
post_fn(POST_ARGS)
1129
{
1130
1131
39230
	post_fname(mdoc);
1132
19615
	post_fa(mdoc);
1133
19615
}
1134
1135
static void
1136
post_fo(POST_ARGS)
1137
{
1138
	const struct roff_node	*n;
1139
1140
13104
	n = mdoc->last;
1141
1142
6552
	if (n->type != ROFFT_HEAD)
1143
4368
		return;
1144
1145
2184
	if (n->child == NULL) {
1146
24
		mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
1147
12
		    n->line, n->pos, "Fo");
1148
12
		return;
1149
	}
1150
2172
	if (n->child != n->last) {
1151
24
		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1152
12
		    n->child->next->line, n->child->next->pos,
1153
12
		    "Fo ... %s", n->child->next->string);
1154
48
		while (n->child != n->last)
1155
12
			roff_node_delete(mdoc, n->last);
1156
	} else
1157
2160
		post_delim(mdoc);
1158
1159
2172
	post_fname(mdoc);
1160
8724
}
1161
1162
static void
1163
post_fa(POST_ARGS)
1164
{
1165
	const struct roff_node *n;
1166
	const char *cp;
1167
1168
201775
	for (n = mdoc->last->child; n != NULL; n = n->next) {
1169
1051524
		for (cp = n->string; *cp != '\0'; cp++) {
1170
			/* Ignore callbacks and alterations. */
1171

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

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

38479
	if (n->last != NULL &&
1196
15357
	    (n->last->tok == MDOC_Pp ||
1197
15165
	     n->last->tok == MDOC_Lp))
1198
192
		mdoc_node_relink(mdoc, n->last);
1199
1200
23314
	if (mdoc->meta.name == NULL)
1201
5740
		deroff(&mdoc->meta.name, n);
1202
1203

33597
	if (mdoc->meta.name == NULL ||
1204
33546
	    (mdoc->lastsec == SEC_NAME && n->child == NULL))
1205
102
		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1206
51
		    n->line, n->pos, "Nm");
1207
1208
23314
	switch (n->type) {
1209
	case ROFFT_ELEM:
1210
19801
		post_delim_nb(mdoc);
1211
19801
		break;
1212
	case ROFFT_HEAD:
1213
1174
		post_delim(mdoc);
1214
1174
		break;
1215
	default:
1216
2339
		return;
1217
	}
1218
1219

41950
	if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
1220
7860
	    mdoc->meta.name == NULL)
1221
13166
		return;
1222
1223
7809
	mdoc->next = ROFF_NEXT_CHILD;
1224
7809
	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
1225
7809
	mdoc->last->flags |= NODE_NOSRC;
1226
7809
	mdoc->last = n;
1227
31123
}
1228
1229
static void
1230
post_nd(POST_ARGS)
1231
{
1232
	struct roff_node	*n;
1233
1234
34440
	n = mdoc->last;
1235
1236
17220
	if (n->type != ROFFT_BODY)
1237
11480
		return;
1238
1239
5740
	if (n->sec != SEC_NAME)
1240
54
		mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse,
1241
27
		    n->line, n->pos, "Nd");
1242
1243
5740
	if (n->child == NULL)
1244
24
		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
1245
12
		    n->line, n->pos, "Nd");
1246
	else
1247
5728
		post_delim(mdoc);
1248
1249
5740
	post_hyph(mdoc);
1250
22960
}
1251
1252
static void
1253
post_display(POST_ARGS)
1254
{
1255
	struct roff_node *n, *np;
1256
1257
32516
	n = mdoc->last;
1258
20324
	switch (n->type) {
1259
	case ROFFT_BODY:
1260
4072
		if (n->end != ENDBODY_NOT) {
1261

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

22858
			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1287
42
				mandoc_vmsg(MANDOCERR_BD_NEST,
1288
42
				    mdoc->parse, n->line, n->pos,
1289
42
				    "%s in Bd", roff_name[n->tok]);
1290
42
				break;
1291
			}
1292
		}
1293
		break;
1294
	default:
1295
		break;
1296
	}
1297
12192
}
1298
1299
static void
1300
post_defaults(POST_ARGS)
1301
{
1302
	struct roff_node *nn;
1303
1304
57288
	if (mdoc->last->child != NULL) {
1305
28222
		post_delim_nb(mdoc);
1306
28222
		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

422
	switch (nn->tok) {
1317
	case MDOC_Ar:
1318
278
		mdoc->next = ROFF_NEXT_CHILD;
1319
278
		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
1320
278
		mdoc->last->flags |= NODE_NOSRC;
1321
278
		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
1322
		mdoc->last->flags |= NODE_NOSRC;
1323
278
		break;
1324
	case MDOC_Pa:
1325
	case MDOC_Mt:
1326
144
		mdoc->next = ROFF_NEXT_CHILD;
1327
144
		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
1328
		mdoc->last->flags |= NODE_NOSRC;
1329
144
		break;
1330
	default:
1331
		abort();
1332
	}
1333
422
	mdoc->last = nn;
1334
29066
}
1335
1336
static void
1337
post_at(POST_ARGS)
1338
{
1339
	struct roff_node	*n, *nch;
1340
	const char		*att;
1341
1342
760
	n = mdoc->last;
1343
380
	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

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

2704
	switch (n->tok) {
1414
	case MDOC_Bsx:
1415
		os = "BSD/OS";
1416
38
		break;
1417
	case MDOC_Dx:
1418
		os = "DragonFly";
1419
36
		break;
1420
	case MDOC_Fx:
1421
		os = "FreeBSD";
1422
167
		break;
1423
	case MDOC_Nx:
1424
		os = "NetBSD";
1425
242
		if (n->child == NULL)
1426
			break;
1427
139
		v = n->child->string;
1428


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

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

131
		    v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
1431
			break;
1432
10
		n->child->flags |= NODE_NOPRT;
1433
10
		mdoc->next = ROFF_NEXT_CHILD;
1434
10
		roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
1435
10
		v = mdoc->last->string;
1436
10
		v[3] = toupper((unsigned char)v[3]);
1437
10
		mdoc->last->flags |= NODE_NOSRC;
1438
10
		mdoc->last = n;
1439
10
		break;
1440
	case MDOC_Ox:
1441
		os = "OpenBSD";
1442
2054
		break;
1443
	case MDOC_Ux:
1444
		os = "UNIX";
1445
167
		break;
1446
	default:
1447
		abort();
1448
	}
1449
2704
	mdoc->next = ROFF_NEXT_CHILD;
1450
2704
	roff_word_alloc(mdoc, n->line, n->pos, os);
1451
2704
	mdoc->last->flags |= NODE_NOSRC;
1452
2704
	mdoc->last = n;
1453
2704
}
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
238654
	post_prevpar(mdoc);
1463
1464
119327
	nit = mdoc->last;
1465
119327
	if (nit->type != ROFFT_BLOCK)
1466
82553
		return;
1467
1468
40011
	nbl = nit->parent->parent;
1469
40011
	lt = nbl->norm->Bl.type;
1470
1471



40011
	switch (lt) {
1472
	case LIST_tag:
1473
	case LIST_hang:
1474
	case LIST_ohang:
1475
	case LIST_inset:
1476
	case LIST_diag:
1477
28580
		if (nit->head->child == NULL)
1478
45
			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1479
45
			    mdoc->parse, nit->line, nit->pos,
1480
			    "Bl -%s It",
1481
45
			    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

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

4418
		if (nit->head->next->child == NULL &&
1506
76
		    nit->head->next->next == NULL) {
1507
54
			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1508
27
			    nit->line, nit->pos, "It");
1509
27
			roff_node_delete(mdoc, nit);
1510
27
			break;
1511
		}
1512
1513
		i = 0;
1514
43912
		for (nch = nit->child; nch != NULL; nch = nch->next) {
1515
17641
			if (nch->type != ROFFT_BODY)
1516
				continue;
1517

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

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

8527
		else if (nit->head->next->child != NULL &&
1526
4239
		    nit->head->next->child->line > nit->line)
1527
18
			mandoc_msg(MANDOCERR_IT_NOARG, mdoc->parse,
1528
9
			    nit->line, nit->pos, "Bl -column It");
1529
		break;
1530
	default:
1531
		abort();
1532
	}
1533
156101
}
1534
1535
static void
1536
post_bl_block(POST_ARGS)
1537
{
1538
	struct roff_node *n, *ni, *nc;
1539
1540
14574
	post_prevpar(mdoc);
1541
1542
7287
	n = mdoc->last;
1543
88124
	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1544
36775
		if (ni->body == NULL)
1545
			continue;
1546
36744
		nc = ni->body->last;
1547
107224
		while (nc != NULL) {
1548

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

1168
			} else if (n->norm->Bl.comp == 0 &&
1563
53
			    n->norm->Bl.type != LIST_column) {
1564
9
				mandoc_vmsg(MANDOCERR_PAR_SKIP,
1565
9
				    mdoc->parse, nc->line, nc->pos,
1566
9
				    "%s before It", roff_name[nc->tok]);
1567
9
				roff_node_delete(mdoc, nc);
1568
			} else
1569
				break;
1570
45
			nc = ni->body->last;
1571
		}
1572
	}
1573
7287
}
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
17962
	if (*arg == NULL)
1586
		return;
1587
8981
	else if ( ! strcmp(*arg, "Ds"))
1588
1463
		width = 6;
1589
7518
	else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
1590
7067
		return;
1591
	else
1592
451
		width = macro2len(tok);
1593
1594
1914
	free(*arg);
1595
1914
	mandoc_asprintf(arg, "%zun", width);
1596
10895
}
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
14574
	post_bl_norm(mdoc);
1606
1607
7287
	nh = mdoc->last;
1608
7287
	if (nh->norm->Bl.type != LIST_column) {
1609
6860
		if ((nch = nh->child) == NULL)
1610
6788
			return;
1611
144
		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1612
72
		    nch->line, nch->pos, "Bl ... %s", nch->string);
1613
432
		while (nch != NULL) {
1614
144
			roff_node_delete(mdoc, nch);
1615
144
			nch = nh->child;
1616
		}
1617
72
		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
427
	if (nh->child == NULL)
1627
414
		return;
1628
1629
13
	nbl = nh->parent;
1630
26
	for (j = 0; j < (int)nbl->args->argc; j++)
1631
13
		if (nbl->args->argv[j].arg == MDOC_Column)
1632
			break;
1633
1634
13
	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
13
	argv = nbl->args->argv + j;
1643
13
	i = argv->sz;
1644
78
	for (nch = nh->child; nch != NULL; nch = nch->next)
1645
26
		argv->sz++;
1646
26
	argv->value = mandoc_reallocarray(argv->value,
1647
13
	    argv->sz, sizeof(char *));
1648
1649
13
	nh->norm->Bl.ncols = argv->sz;
1650
13
	nh->norm->Bl.cols = (void *)argv->value;
1651
1652
78
	for (nch = nh->child; nch != NULL; nch = nnext) {
1653
26
		argv->value[i++] = nch->string;
1654
26
		nch->string = NULL;
1655
26
		nnext = nch->next;
1656
26
		roff_node_delete(NULL, nch);
1657
	}
1658
13
	nh->child = NULL;
1659
7300
}
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
43830
	nbody = mdoc->last;
1671

21915
	switch (nbody->type) {
1672
	case ROFFT_BLOCK:
1673
7287
		post_bl_block(mdoc);
1674
7287
		return;
1675
	case ROFFT_HEAD:
1676
7287
		post_bl_head(mdoc);
1677
7287
		return;
1678
	case ROFFT_BODY:
1679
		break;
1680
	default:
1681
		return;
1682
	}
1683
7341
	if (nbody->end != ENDBODY_NOT)
1684
54
		return;
1685
1686
7287
	nchild = nbody->child;
1687
7287
	if (nchild == NULL) {
1688
266
		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1689
133
		    nbody->line, nbody->pos, "Bl");
1690
133
		return;
1691
	}
1692
51545
	while (nchild != NULL) {
1693
37006
		nnext = nchild->next;
1694

37043
		if (nchild->tok == MDOC_It ||
1695
518
		    (nchild->tok == MDOC_Sm &&
1696
296
		     nnext != NULL && nnext->tok == MDOC_It)) {
1697
			nchild = nnext;
1698
36772
			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
234
		if (nchild->prev != NULL) {
1711
3
			mdoc->last = nchild;
1712
3
			mdoc->next = ROFF_NEXT_SIBLING;
1713
6
			roff_block_alloc(mdoc, nchild->line,
1714
3
			    nchild->pos, MDOC_It);
1715
6
			roff_head_alloc(mdoc, nchild->line,
1716
3
			    nchild->pos, MDOC_It);
1717
3
			mdoc->next = ROFF_NEXT_SIBLING;
1718
6
			roff_body_alloc(mdoc, nchild->line,
1719
3
			    nchild->pos, MDOC_It);
1720
18
			while (nchild->tok != MDOC_It) {
1721
6
				mdoc_node_relink(mdoc, nchild);
1722
6
				if ((nchild = nnext) == NULL)
1723
					break;
1724
6
				nnext = nchild->next;
1725
6
				mdoc->next = ROFF_NEXT_SIBLING;
1726
			}
1727
3
			mdoc->last = nbody;
1728
3
			continue;
1729
		}
1730
1731
462
		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1732
231
		    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
231
		nblock  = nbody->parent;
1740
231
		nprev   = nblock->prev;
1741
231
		nparent = nblock->parent;
1742
1743
		/*
1744
		 * Unlink this child.
1745
		 */
1746
1747
231
		nbody->child = nnext;
1748
462
		if (nnext == NULL)
1749
231
			nbody->last  = NULL;
1750
		else
1751
231
			nnext->prev = NULL;
1752
1753
		/*
1754
		 * Relink this child.
1755
		 */
1756
1757
231
		nchild->parent = nparent;
1758
231
		nchild->prev   = nprev;
1759
231
		nchild->next   = nblock;
1760
1761
231
		nblock->prev = nchild;
1762
462
		if (nprev == NULL)
1763
231
			nparent->child = nchild;
1764
		else
1765
231
			nprev->next = nchild;
1766
1767
		nchild = nnext;
1768
	}
1769
1770
7154
	if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
1771
7154
		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
21915
}
1799
1800
static void
1801
post_bk(POST_ARGS)
1802
{
1803
	struct roff_node	*n;
1804
1805
1560
	n = mdoc->last;
1806
1807

1040
	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1808
21
		mandoc_msg(MANDOCERR_BLK_EMPTY,
1809
21
		    mdoc->parse, n->line, n->pos, "Bk");
1810
21
		roff_node_delete(mdoc, n);
1811
21
	}
1812
780
}
1813
1814
static void
1815
post_sm(POST_ARGS)
1816
{
1817
	struct roff_node	*nch;
1818
1819
1792
	nch = mdoc->last->child;
1820
1821
896
	if (nch == NULL) {
1822
18
		mdoc->flags ^= MDOC_SMOFF;
1823
18
		return;
1824
	}
1825
1826
878
	assert(nch->type == ROFFT_TEXT);
1827
1828
878
	if ( ! strcmp(nch->string, "on")) {
1829
424
		mdoc->flags &= ~MDOC_SMOFF;
1830
424
		return;
1831
	}
1832
454
	if ( ! strcmp(nch->string, "off")) {
1833
418
		mdoc->flags |= MDOC_SMOFF;
1834
418
		return;
1835
	}
1836
1837
36
	mandoc_vmsg(MANDOCERR_SM_BAD,
1838
36
	    mdoc->parse, nch->line, nch->pos,
1839
36
	    "%s %s", roff_name[mdoc->last->tok], nch->string);
1840
36
	mdoc_node_relink(mdoc, nch);
1841
36
	return;
1842
896
}
1843
1844
static void
1845
post_root(POST_ARGS)
1846
{
1847
11522
	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
5761
	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
5761
	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
5761
	if (mdoc->meta.date == NULL)
1874
		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
1875
		    mandoc_normdate(mdoc, NULL, 0, 0);
1876
1877
5761
	if (mdoc->meta.title == NULL) {
1878
18
		mandoc_msg(MANDOCERR_DT_NOTITLE,
1879
18
		    mdoc->parse, 0, 0, "EOF");
1880
18
		mdoc->meta.title = mandoc_strdup("UNTITLED");
1881
18
	}
1882
1883
5761
	if (mdoc->meta.vol == NULL)
1884
18
		mdoc->meta.vol = mandoc_strdup("LOCAL");
1885
1886
5761
	if (mdoc->meta.os == NULL) {
1887
12
		mandoc_msg(MANDOCERR_OS_MISSING,
1888
12
		    mdoc->parse, 0, 0, NULL);
1889
12
		mdoc->meta.os = mandoc_strdup("");
1890

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

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

13550
		while (*arch != NULL && strcmp(*arch, mdoc->meta.arch))
1899
2999
			arch++;
1900
389
		if (*arch == NULL) {
1901
2
			n = mdoc->first->child;
1902

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

115094
	while (n != NULL && n->tok >= MDOC_Dd &&
1920
23002
	    mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1921
17265
		n = n->next;
1922
1923
5761
	if (n == NULL)
1924
12
		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1925
5749
	else if (n->tok != MDOC_Sh)
1926
66
		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1927
33
		    n->line, n->pos, roff_name[n->tok]);
1928
5761
}
1929
1930
static void
1931
post_rs(POST_ARGS)
1932
{
1933
	struct roff_node *np, *nch, *next, *prev;
1934
	int		  i, j;
1935
1936
3372
	np = mdoc->last;
1937
1938
1686
	if (np->type != ROFFT_BODY)
1939
1124
		return;
1940
1941
562
	if (np->child == NULL) {
1942
60
		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1943
30
		    np->line, np->pos, "Rs");
1944
30
		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
5312
	for (nch = np->child->next; nch != NULL; nch = next) {
1955
		/* Determine order number of this child. */
1956
27362
		for (i = 0; i < RSORD_MAX; i++)
1957
13591
			if (rsord[i] == nch->tok)
1958
				break;
1959
1960
2124
		if (i == RSORD_MAX) {
1961
180
			mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse,
1962
90
			    nch->line, nch->pos, roff_name[nch->tok]);
1963
			i = -1;
1964

4048
		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1965
188
			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
2124
		if ((next = nch->next) != NULL)
1975
1612
			next->prev = nch->prev;
1976
1977
2124
		if ((prev = nch->prev) != NULL)
1978
2124
			prev->next = nch->next;
1979
1980
2124
		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
7100
		for ( ; prev ; prev = prev->prev) {
1988
			/* Determine order of `prev'. */
1989
35830
			for (j = 0; j < RSORD_MAX; j++)
1990
17855
				if (rsord[j] == prev->tok)
1991
					break;
1992
3467
			if (j == RSORD_MAX)
1993
60
				j = -1;
1994
1995
3467
			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
2124
		nch->prev = prev;
2005
2006
2124
		if (prev == NULL) {
2007
83
			np->child->prev = nch;
2008
83
			nch->next = np->child;
2009
83
			np->child = nch;
2010
83
		} else {
2011
2041
			if (prev->next)
2012
1820
				prev->next->prev = nch;
2013
2041
			nch->next = prev->next;
2014
2041
			prev->next = nch;
2015
		}
2016
	}
2017
2218
}
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
174170
	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
2030
35209
		if (nch->type != ROFFT_TEXT)
2031
			continue;
2032
34647
		cp = nch->string;
2033
34647
		if (*cp == '\0')
2034
			continue;
2035
901560
		while (*(++cp) != '\0')
2036

416745
			if (*cp == '-' &&
2037
726
			    isalpha((unsigned char)cp[-1]) &&
2038
612
			    isalpha((unsigned char)cp[1]))
2039
551
				*cp = ASCII_HYPH;
2040
	}
2041
34584
}
2042
2043
static void
2044
post_ns(POST_ARGS)
2045
{
2046
	struct roff_node	*n;
2047
2048
6754
	n = mdoc->last;
2049

6730
	if (n->flags & NODE_LINE ||
2050
6718
	    (n->next != NULL && n->next->flags & NODE_DELIMC))
2051
48
		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
2052
24
		    n->line, n->pos, NULL);
2053
3377
}
2054
2055
static void
2056
post_sx(POST_ARGS)
2057
{
2058
2618
	post_delim(mdoc);
2059
1309
	post_hyph(mdoc);
2060
1309
}
2061
2062
static void
2063
post_sh(POST_ARGS)
2064
{
2065
2066
176876
	post_ignpar(mdoc);
2067
2068
101072
	switch (mdoc->last->type) {
2069
	case ROFFT_HEAD:
2070
25268
		post_sh_head(mdoc);
2071
25268
		break;
2072
	case ROFFT_BODY:
2073

34698
		switch (mdoc->lastsec)  {
2074
		case SEC_NAME:
2075
5737
			post_sh_name(mdoc);
2076
5737
			break;
2077
		case SEC_SEE_ALSO:
2078
2858
			post_sh_see_also(mdoc);
2079
2858
			break;
2080
		case SEC_AUTHORS:
2081
835
			post_sh_authors(mdoc);
2082
835
			break;
2083
		default:
2084
			break;
2085
		}
2086
		break;
2087
	default:
2088
		break;
2089
	}
2090
75804
}
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
37981
	for (n = mdoc->last->child; n != NULL; n = n->next) {
2101

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

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

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

9215
			    n->string[0] == ',' && n->string[1] == '\0' &&
2118
9194
			    n->next != NULL && n->next->tok == MDOC_Nm) {
2119
				n = n->next;
2120
4585
				continue;
2121
			}
2122
			/* FALLTHROUGH */
2123
		default:
2124
144
			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
2125
72
			    n->line, n->pos, roff_name[n->tok]);
2126
72
			continue;
2127
		}
2128
		break;
2129
	}
2130
2131
5737
	if ( ! hasnm)
2132
66
		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
2133
33
		    mdoc->last->line, mdoc->last->pos, NULL);
2134
5737
	if ( ! hasnd)
2135
66
		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
2136
33
		    mdoc->last->line, mdoc->last->pos, NULL);
2137
5737
}
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
5716
	n = mdoc->last->child;
2148
	lastname = lastsec = lastpunct = NULL;
2149
24160
	while (n != NULL) {
2150

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

18444
			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
2161
4
				mandoc_vmsg(MANDOCERR_XR_PUNCT,
2162
4
				    mdoc->parse, n->line, n->pos,
2163
				    "%s before %s(%s)", lastpunct,
2164
				    name, sec);
2165
9224
			cmp = strcmp(lastsec, sec);
2166
9224
			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

16474
			else if (cmp == 0 &&
2172
7250
			    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
11991
		n = n->next;
2183
11991
		if (n == NULL)
2184
			break;
2185
9378
		if (n->tok == MDOC_Xr) {
2186
			lastpunct = "none";
2187
4
			continue;
2188
		}
2189
9374
		if (n->type != ROFFT_TEXT)
2190
			break;
2191
36888
		for (name = n->string; *name != '\0'; name++)
2192
9224
			if (isalpha((const unsigned char)*name))
2193
1
				return;
2194
9220
		lastpunct = n->string;
2195

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

7781
		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
2209
823
			return 1;
2210
2923
	return 0;
2211
3746
}
2212
2213
static void
2214
post_sh_authors(POST_ARGS)
2215
{
2216
2217
1670
	if ( ! child_an(mdoc->last))
2218
60
		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
2219
30
		    mdoc->last->line, mdoc->last->pos, NULL);
2220
835
}
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

151904
	while (s1[0] != '\0' && s2[0] != '\0') {
2239
34080
		if (s1[0] == s2[0]) {
2240
7159
			s1++;
2241
7159
			s2++;
2242
7159
			continue;
2243
		}
2244
26921
		if (++dist > maxdist)
2245
161
			return INT_MAX;
2246
26760
		if (s1[1] == s2[1]) {  /* replacement */
2247
2069
			s1++;
2248
2069
			s2++;
2249

27882
		} else if (s1[0] == s2[1] && s1[1] == s2[0]) {
2250
83
			s1 += 2;	/* transposition */
2251
83
			s2 += 2;
2252
24691
		} else if (s1[0] == s2[1])  /* insertion */
2253
1039
			s2++;
2254
23569
		else if (s1[1] == s2[0])  /* deletion */
2255
1469
			s1++;
2256
		else
2257
22100
			return INT_MAX;
2258
	}
2259
104
	dist += strlen(s1) + strlen(s2);
2260
104
	return dist > maxdist ? INT_MAX : dist;
2261
22365
}
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
50536
	sec = mdoc->last->sec;
2280
2281
	/* The NAME should be first. */
2282
2283

44799
	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
2284
36
		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
2285
12
		    mdoc->last->line, mdoc->last->pos, "Sh %s",
2286
36
		    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
25268
	if (sec == SEC_SYNOPSIS) {
2294
2930
		roff_setreg(mdoc->roff, "nS", 1, '=');
2295
2930
		mdoc->flags |= MDOC_SYNOPSIS;
2296
2930
	} else {
2297
22338
		roff_setreg(mdoc->roff, "nS", 0, '=');
2298
22338
		mdoc->flags &= ~MDOC_SYNOPSIS;
2299
	}
2300
2301
	/* Mark our last section. */
2302
2303
25268
	mdoc->lastsec = sec;
2304
2305
	/* We don't care about custom sections after this. */
2306
2307
25268
	if (sec == SEC_CUSTOM) {
2308

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

25919
	switch (sec) {
2353
	case SEC_ERRORS:
2354
356
		if (*mdoc->meta.msec == '4')
2355
			break;
2356
342
		goodsec = "2, 3, 4, 9";
2357
		/* FALLTHROUGH */
2358
	case SEC_RETURN_VALUES:
2359
	case SEC_LIBRARY:
2360
1306
		if (*mdoc->meta.msec == '2')
2361
			break;
2362
1041
		if (*mdoc->meta.msec == '3')
2363
			break;
2364
81
		if (NULL == goodsec)
2365
64
			goodsec = "2, 3, 9";
2366
		/* FALLTHROUGH */
2367
	case SEC_CONTEXT:
2368
126
		if (*mdoc->meta.msec == '9')
2369
			break;
2370
12
		if (NULL == goodsec)
2371
			goodsec = "9";
2372
24
		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
2373
12
		    mdoc->last->line, mdoc->last->pos,
2374
12
		    "Sh %s for %s only", secnames[sec], goodsec);
2375
12
		break;
2376
	default:
2377
		break;
2378
	}
2379
49399
}
2380
2381
static void
2382
post_xr(POST_ARGS)
2383
{
2384
	struct roff_node *n, *nch;
2385
2386
47844
	n = mdoc->last;
2387
23922
	nch = n->child;
2388
23922
	if (nch->next == NULL) {
2389
52
		mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
2390
26
		    n->line, n->pos, "Xr %s", nch->string);
2391
26
	} else {
2392
23896
		assert(nch->next == n->last);
2393
47792
		if(mandoc_xr_add(nch->next->string, nch->string,
2394
23896
		    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
23922
	post_delim_nb(mdoc);
2400
23922
}
2401
2402
static void
2403
post_ignpar(POST_ARGS)
2404
{
2405
	struct roff_node *np;
2406
2407

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

52508
		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
26314
	if ((np = mdoc->last->last) != NULL)
2431

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

244834
	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2449
45779
		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

175632
	if (n->prev->tok != MDOC_Pp &&
2457
87318
	    n->prev->tok != MDOC_Lp &&
2458
87318
	    n->prev->tok != ROFF_br)
2459
87291
		return;
2460

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

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

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

52577
	if (np->tok != ROFF_br && np->tok != ROFF_sp)
2480
23491
		post_prevpar(mdoc);
2481
2482
26915
	if (np->tok == ROFF_sp) {
2483

3005
		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
24744
	} else if (np->child != NULL)
2488
12
		mandoc_vmsg(MANDOCERR_ARG_SKIP,
2489
12
		    mdoc->parse, np->line, np->pos, "%s %s",
2490
12
		    roff_name[np->tok], np->child->string);
2491
2492
26915
	if ((np = mdoc->last->prev) == NULL) {
2493
181
		np = mdoc->last->parent;
2494

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

54643
	} else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
2497
26698
	    (mdoc->last->tok != ROFF_br ||
2498
2440
	     (np->tok != ROFF_sp && np->tok != ROFF_br)))
2499
26671
		return;
2500
2501
252
	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2502
126
	    mdoc->last->line, mdoc->last->pos, "%s after %s",
2503
126
	    roff_name[mdoc->last->tok], roff_name[np->tok]);
2504
126
	roff_node_delete(mdoc, mdoc->last);
2505
27041
}
2506
2507
static void
2508
post_dd(POST_ARGS)
2509
{
2510
	struct roff_node *n;
2511
11570
	char		 *datestr;
2512
2513
5785
	n = mdoc->last;
2514
5785
	n->flags |= NODE_NOPRT;
2515
2516
5785
	if (mdoc->meta.date != NULL) {
2517
48
		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2518
24
		    n->line, n->pos, "Dd");
2519
24
		free(mdoc->meta.date);
2520
5785
	} else if (mdoc->flags & MDOC_PBODY)
2521
24
		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2522
12
		    n->line, n->pos, "Dd");
2523
5749
	else if (mdoc->meta.title != NULL)
2524
24
		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2525
12
		    n->line, n->pos, "Dd after Dt");
2526
5737
	else if (mdoc->meta.os != NULL)
2527
		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2528
		    n->line, n->pos, "Dd after Os");
2529
2530

11567
	if (n->child == NULL || n->child->string[0] == '\0') {
2531
9
		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2532
3
		    mandoc_normdate(mdoc, NULL, n->line, n->pos);
2533
3
		return;
2534
	}
2535
2536
5782
	datestr = NULL;
2537
5782
	deroff(&datestr, n);
2538
5782
	if (mdoc->quick)
2539
		mdoc->meta.date = datestr;
2540
	else {
2541
5782
		mdoc->meta.date = mandoc_normdate(mdoc,
2542
5782
		    datestr, n->line, n->pos);
2543
5782
		free(datestr);
2544
	}
2545
11567
}
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
11552
	n = mdoc->last;
2555
5776
	n->flags |= NODE_NOPRT;
2556
2557
5776
	if (mdoc->flags & MDOC_PBODY) {
2558
42
		mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2559
21
		    n->line, n->pos, "Dt");
2560
21
		return;
2561
	}
2562
2563
5755
	if (mdoc->meta.title != NULL)
2564
24
		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2565
12
		    n->line, n->pos, "Dt");
2566
5743
	else if (mdoc->meta.os != NULL)
2567
48
		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2568
24
		    n->line, n->pos, "Dt after Os");
2569
2570
5755
	free(mdoc->meta.title);
2571
5755
	free(mdoc->meta.msec);
2572
5755
	free(mdoc->meta.vol);
2573
5755
	free(mdoc->meta.arch);
2574
2575
5755
	mdoc->meta.title = NULL;
2576
5755
	mdoc->meta.msec = NULL;
2577
5755
	mdoc->meta.vol = NULL;
2578
5755
	mdoc->meta.arch = NULL;
2579
2580
	/* Mandatory first argument: title. */
2581
2582
5755
	nn = n->child;
2583

11501
	if (nn == NULL || *nn->string == '\0') {
2584
9
		mandoc_msg(MANDOCERR_DT_NOTITLE,
2585
9
		    mdoc->parse, n->line, n->pos, "Dt");
2586
9
		mdoc->meta.title = mandoc_strdup("UNTITLED");
2587
9
	} else {
2588
5746
		mdoc->meta.title = mandoc_strdup(nn->string);
2589
2590
		/* Check that all characters are uppercase. */
2591
2592
108272
		for (p = nn->string; *p != '\0'; p++)
2593
48420
			if (islower((unsigned char)*p)) {
2594
30
				mandoc_vmsg(MANDOCERR_TITLE_CASE,
2595
30
				    mdoc->parse, nn->line,
2596
30
				    nn->pos + (p - nn->string),
2597
				    "Dt %s", nn->string);
2598
30
				break;
2599
			}
2600
	}
2601
2602
	/* Mandatory second argument: section. */
2603
2604
5755
	if (nn != NULL)
2605
5746
		nn = nn->next;
2606
2607
5755
	if (nn == NULL) {
2608
18
		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2609
18
		    mdoc->parse, n->line, n->pos,
2610
18
		    "Dt %s", mdoc->meta.title);
2611
18
		mdoc->meta.vol = mandoc_strdup("LOCAL");
2612
18
		return;  /* msec and arch remain NULL. */
2613
	}
2614
2615
5737
	mdoc->meta.msec = mandoc_strdup(nn->string);
2616
2617
	/* Infer volume title from section number. */
2618
2619
5737
	cp = mandoc_a2msec(nn->string);
2620
5737
	if (cp == NULL) {
2621
6
		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2622
3
		    nn->line, nn->pos, "Dt ... %s", nn->string);
2623
3
		mdoc->meta.vol = mandoc_strdup(nn->string);
2624
3
	} else
2625
5734
		mdoc->meta.vol = mandoc_strdup(cp);
2626
2627
	/* Optional third argument: architecture. */
2628
2629
5737
	if ((nn = nn->next) == NULL)
2630
5336
		return;
2631
2632
5294
	for (p = nn->string; *p != '\0'; p++)
2633
2246
		*p = tolower((unsigned char)*p);
2634
401
	mdoc->meta.arch = mandoc_strdup(nn->string);
2635
2636
	/* Ignore fourth and later arguments. */
2637
2638
401
	if ((nn = nn->next) != NULL)
2639
18
		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2640
9
		    nn->line, nn->pos, "Dt ... %s", nn->string);
2641
6177
}
2642
2643
static void
2644
post_bx(POST_ARGS)
2645
{
2646
	struct roff_node	*n, *nch;
2647
	const char		*macro;
2648
2649
1290
	post_delim_nb(mdoc);
2650
2651
645
	n = mdoc->last;
2652
645
	nch = n->child;
2653
2654
645
	if (nch != NULL) {
2655
1028
		macro = !strcmp(nch->string, "Open") ? "Ox" :
2656
514
		    !strcmp(nch->string, "Net") ? "Nx" :
2657
1028
		    !strcmp(nch->string, "Free") ? "Fx" :
2658
514
		    !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
2659
514
		if (macro != NULL)
2660
			mandoc_msg(MANDOCERR_BX, mdoc->parse,
2661
			    n->line, n->pos, macro);
2662
514
		mdoc->last = nch;
2663
514
		nch = nch->next;
2664
514
		mdoc->next = ROFF_NEXT_SIBLING;
2665
514
		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2666
514
		mdoc->last->flags |= NODE_NOSRC;
2667
		mdoc->next = ROFF_NEXT_SIBLING;
2668
514
	} else
2669
		mdoc->next = ROFF_NEXT_CHILD;
2670
645
	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2671
645
	mdoc->last->flags |= NODE_NOSRC;
2672
2673
645
	if (nch == NULL) {
2674
544
		mdoc->last = n;
2675
544
		return;
2676
	}
2677
2678
101
	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2679
101
	mdoc->last->flags |= NODE_NOSRC;
2680
101
	mdoc->next = ROFF_NEXT_SIBLING;
2681
101
	roff_word_alloc(mdoc, n->line, n->pos, "-");
2682
101
	mdoc->last->flags |= NODE_NOSRC;
2683
101
	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2684
101
	mdoc->last->flags |= NODE_NOSRC;
2685
101
	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
101
	*nch->string = (char)toupper((unsigned char)*nch->string);
2694
746
}
2695
2696
static void
2697
post_os(POST_ARGS)
2698
{
2699
#ifndef OSNAME
2700
11546
	struct utsname	  utsname;
2701
	static char	 *defbuf;
2702
#endif
2703
	struct roff_node *n;
2704
2705
5773
	n = mdoc->last;
2706
5773
	n->flags |= NODE_NOPRT;
2707
2708
5773
	if (mdoc->meta.os != NULL)
2709
48
		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2710
24
		    n->line, n->pos, "Os");
2711
5749
	else if (mdoc->flags & MDOC_PBODY)
2712
24
		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2713
12
		    n->line, n->pos, "Os");
2714
2715
5773
	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
5773
	free(mdoc->meta.os);
2727
5773
	mdoc->meta.os = NULL;
2728
5773
	deroff(&mdoc->meta.os, n);
2729
5773
	if (mdoc->meta.os)
2730
		goto out;
2731
2732
5728
	if (mdoc->os_s != NULL) {
2733
2734
		mdoc->meta.os = mandoc_strdup(mdoc->os_s);
2734
2734
		goto out;
2735
	}
2736
2737
#ifdef OSNAME
2738
	mdoc->meta.os = mandoc_strdup(OSNAME);
2739
#else /*!OSNAME */
2740
2994
	if (defbuf == NULL) {
2741
86
		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
86
			mandoc_asprintf(&defbuf, "%s %s",
2747
86
			    utsname.sysname, utsname.release);
2748
	}
2749
2994
	mdoc->meta.os = mandoc_strdup(defbuf);
2750
#endif /*!OSNAME*/
2751
2752
out:
2753
11501
	if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
2754
5749
		if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
2755
5728
			mdoc->meta.os_e = MANDOC_OS_OPENBSD;
2756
21
		else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
2757
12
			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
5773
	if (n->child != NULL)
2767
90
		mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse,
2768
45
		    n->child->line, n->child->pos,
2769
45
		    "Os %s (%s)", n->child->string,
2770
45
		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2771
		    "OpenBSD" : "NetBSD");
2772
2773
34482
	while (n->tok != MDOC_Dd)
2774
11504
		if ((n = n->prev) == NULL)
2775
36
			return;
2776
5737
	if ((n = n->child) == NULL)
2777
3
		return;
2778
5734
	if (strncmp(n->string, "$" "Mdocdate", 9)) {
2779
111
		if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
2780
111
			mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING,
2781
111
			    mdoc->parse, n->line, n->pos,
2782
111
			    "Dd %s (OpenBSD)", n->string);
2783
	} else {
2784
5623
		if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
2785
24
			mandoc_vmsg(MANDOCERR_MDOCDATE,
2786
24
			    mdoc->parse, n->line, n->pos,
2787
24
			    "Dd %s (NetBSD)", n->string);
2788
	}
2789
11507
}
2790
2791
enum roff_sec
2792
mdoc_a2sec(const char *p)
2793
{
2794
	int		 i;
2795
2796
487950
	for (i = 0; i < (int)SEC__MAX; i++)
2797

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










902
	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
4
		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
1
		return 10;
2826
	case MDOC_Do:
2827
		return 10;
2828
	case MDOC_Dq:
2829
		return 12;
2830
	case MDOC_Dv:
2831
7
		return 12;
2832
	case MDOC_Eo:
2833
		return 12;
2834
	case MDOC_Em:
2835
		return 10;
2836
	case MDOC_Er:
2837
371
		return 17;
2838
	case MDOC_Ev:
2839
2
		return 15;
2840
	case MDOC_Fa:
2841
		return 12;
2842
	case MDOC_Fl:
2843
35
		return 10;
2844
	case MDOC_Fo:
2845
		return 16;
2846
	case MDOC_Fn:
2847
		return 16;
2848
	case MDOC_Ic:
2849
3
		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
1
		return 12;
2858
	case MDOC_Oo:
2859
		return 10;
2860
	case MDOC_Op:
2861
1
		return 14;
2862
	case MDOC_Pa:
2863
25
		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
1
		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
451
}