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