]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
in the base system, suggest leaving .Os blank; inspired by mdoclint
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.341 2017/06/24 15:59:50 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 != MANDOC_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 (NetBSD)",
1732 prev_Er, nnext->string);
1733 else if (order == 0)
1734 mandoc_vmsg(MANDOCERR_ER_REP,
1735 mdoc->parse, nnext->line, nnext->pos,
1736 "Er %s (NetBSD)", prev_Er);
1737 }
1738 prev_Er = nnext->string;
1739 }
1740 }
1741
1742 static void
1743 post_bk(POST_ARGS)
1744 {
1745 struct roff_node *n;
1746
1747 n = mdoc->last;
1748
1749 if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1750 mandoc_msg(MANDOCERR_BLK_EMPTY,
1751 mdoc->parse, n->line, n->pos, "Bk");
1752 roff_node_delete(mdoc, n);
1753 }
1754 }
1755
1756 static void
1757 post_sm(POST_ARGS)
1758 {
1759 struct roff_node *nch;
1760
1761 nch = mdoc->last->child;
1762
1763 if (nch == NULL) {
1764 mdoc->flags ^= MDOC_SMOFF;
1765 return;
1766 }
1767
1768 assert(nch->type == ROFFT_TEXT);
1769
1770 if ( ! strcmp(nch->string, "on")) {
1771 mdoc->flags &= ~MDOC_SMOFF;
1772 return;
1773 }
1774 if ( ! strcmp(nch->string, "off")) {
1775 mdoc->flags |= MDOC_SMOFF;
1776 return;
1777 }
1778
1779 mandoc_vmsg(MANDOCERR_SM_BAD,
1780 mdoc->parse, nch->line, nch->pos,
1781 "%s %s", roff_name[mdoc->last->tok], nch->string);
1782 mdoc_node_relink(mdoc, nch);
1783 return;
1784 }
1785
1786 static void
1787 post_root(POST_ARGS)
1788 {
1789 struct roff_node *n;
1790
1791 /* Add missing prologue data. */
1792
1793 if (mdoc->meta.date == NULL)
1794 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
1795 mandoc_normdate(mdoc, NULL, 0, 0);
1796
1797 if (mdoc->meta.title == NULL) {
1798 mandoc_msg(MANDOCERR_DT_NOTITLE,
1799 mdoc->parse, 0, 0, "EOF");
1800 mdoc->meta.title = mandoc_strdup("UNTITLED");
1801 }
1802
1803 if (mdoc->meta.vol == NULL)
1804 mdoc->meta.vol = mandoc_strdup("LOCAL");
1805
1806 if (mdoc->meta.os == NULL) {
1807 mandoc_msg(MANDOCERR_OS_MISSING,
1808 mdoc->parse, 0, 0, NULL);
1809 mdoc->meta.os = mandoc_strdup("");
1810 } else if (mdoc->meta.os_e &&
1811 (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1812 mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0,
1813 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1814 "(OpenBSD)" : "(NetBSD)");
1815
1816 /* Check that we begin with a proper `Sh'. */
1817
1818 n = mdoc->first->child;
1819 while (n != NULL && n->tok != TOKEN_NONE &&
1820 mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1821 n = n->next;
1822
1823 if (n == NULL)
1824 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1825 else if (n->tok != MDOC_Sh)
1826 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1827 n->line, n->pos, roff_name[n->tok]);
1828 }
1829
1830 static void
1831 post_rs(POST_ARGS)
1832 {
1833 struct roff_node *np, *nch, *next, *prev;
1834 int i, j;
1835
1836 np = mdoc->last;
1837
1838 if (np->type != ROFFT_BODY)
1839 return;
1840
1841 if (np->child == NULL) {
1842 mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1843 np->line, np->pos, "Rs");
1844 return;
1845 }
1846
1847 /*
1848 * The full `Rs' block needs special handling to order the
1849 * sub-elements according to `rsord'. Pick through each element
1850 * and correctly order it. This is an insertion sort.
1851 */
1852
1853 next = NULL;
1854 for (nch = np->child->next; nch != NULL; nch = next) {
1855 /* Determine order number of this child. */
1856 for (i = 0; i < RSORD_MAX; i++)
1857 if (rsord[i] == nch->tok)
1858 break;
1859
1860 if (i == RSORD_MAX) {
1861 mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse,
1862 nch->line, nch->pos, roff_name[nch->tok]);
1863 i = -1;
1864 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1865 np->norm->Rs.quote_T++;
1866
1867 /*
1868 * Remove this child from the chain. This somewhat
1869 * repeats roff_node_unlink(), but since we're
1870 * just re-ordering, there's no need for the
1871 * full unlink process.
1872 */
1873
1874 if ((next = nch->next) != NULL)
1875 next->prev = nch->prev;
1876
1877 if ((prev = nch->prev) != NULL)
1878 prev->next = nch->next;
1879
1880 nch->prev = nch->next = NULL;
1881
1882 /*
1883 * Scan back until we reach a node that's
1884 * to be ordered before this child.
1885 */
1886
1887 for ( ; prev ; prev = prev->prev) {
1888 /* Determine order of `prev'. */
1889 for (j = 0; j < RSORD_MAX; j++)
1890 if (rsord[j] == prev->tok)
1891 break;
1892 if (j == RSORD_MAX)
1893 j = -1;
1894
1895 if (j <= i)
1896 break;
1897 }
1898
1899 /*
1900 * Set this child back into its correct place
1901 * in front of the `prev' node.
1902 */
1903
1904 nch->prev = prev;
1905
1906 if (prev == NULL) {
1907 np->child->prev = nch;
1908 nch->next = np->child;
1909 np->child = nch;
1910 } else {
1911 if (prev->next)
1912 prev->next->prev = nch;
1913 nch->next = prev->next;
1914 prev->next = nch;
1915 }
1916 }
1917 }
1918
1919 /*
1920 * For some arguments of some macros,
1921 * convert all breakable hyphens into ASCII_HYPH.
1922 */
1923 static void
1924 post_hyph(POST_ARGS)
1925 {
1926 struct roff_node *nch;
1927 char *cp;
1928
1929 for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1930 if (nch->type != ROFFT_TEXT)
1931 continue;
1932 cp = nch->string;
1933 if (*cp == '\0')
1934 continue;
1935 while (*(++cp) != '\0')
1936 if (*cp == '-' &&
1937 isalpha((unsigned char)cp[-1]) &&
1938 isalpha((unsigned char)cp[1]))
1939 *cp = ASCII_HYPH;
1940 }
1941 }
1942
1943 static void
1944 post_ns(POST_ARGS)
1945 {
1946
1947 if (mdoc->last->flags & NODE_LINE)
1948 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1949 mdoc->last->line, mdoc->last->pos, NULL);
1950 }
1951
1952 static void
1953 post_sh(POST_ARGS)
1954 {
1955
1956 post_ignpar(mdoc);
1957
1958 switch (mdoc->last->type) {
1959 case ROFFT_HEAD:
1960 post_sh_head(mdoc);
1961 break;
1962 case ROFFT_BODY:
1963 switch (mdoc->lastsec) {
1964 case SEC_NAME:
1965 post_sh_name(mdoc);
1966 break;
1967 case SEC_SEE_ALSO:
1968 post_sh_see_also(mdoc);
1969 break;
1970 case SEC_AUTHORS:
1971 post_sh_authors(mdoc);
1972 break;
1973 default:
1974 break;
1975 }
1976 break;
1977 default:
1978 break;
1979 }
1980 }
1981
1982 static void
1983 post_sh_name(POST_ARGS)
1984 {
1985 struct roff_node *n;
1986 int hasnm, hasnd;
1987
1988 hasnm = hasnd = 0;
1989
1990 for (n = mdoc->last->child; n != NULL; n = n->next) {
1991 switch (n->tok) {
1992 case MDOC_Nm:
1993 if (hasnm && n->child != NULL)
1994 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
1995 mdoc->parse, n->line, n->pos,
1996 "Nm %s", n->child->string);
1997 hasnm = 1;
1998 continue;
1999 case MDOC_Nd:
2000 hasnd = 1;
2001 if (n->next != NULL)
2002 mandoc_msg(MANDOCERR_NAMESEC_ND,
2003 mdoc->parse, n->line, n->pos, NULL);
2004 break;
2005 case TOKEN_NONE:
2006 if (n->type == ROFFT_TEXT &&
2007 n->string[0] == ',' && n->string[1] == '\0' &&
2008 n->next != NULL && n->next->tok == MDOC_Nm) {
2009 n = n->next;
2010 continue;
2011 }
2012 /* FALLTHROUGH */
2013 default:
2014 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
2015 n->line, n->pos, roff_name[n->tok]);
2016 continue;
2017 }
2018 break;
2019 }
2020
2021 if ( ! hasnm)
2022 mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
2023 mdoc->last->line, mdoc->last->pos, NULL);
2024 if ( ! hasnd)
2025 mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
2026 mdoc->last->line, mdoc->last->pos, NULL);
2027 }
2028
2029 static void
2030 post_sh_see_also(POST_ARGS)
2031 {
2032 const struct roff_node *n;
2033 const char *name, *sec;
2034 const char *lastname, *lastsec, *lastpunct;
2035 int cmp;
2036
2037 n = mdoc->last->child;
2038 lastname = lastsec = lastpunct = NULL;
2039 while (n != NULL) {
2040 if (n->tok != MDOC_Xr ||
2041 n->child == NULL ||
2042 n->child->next == NULL)
2043 break;
2044
2045 /* Process one .Xr node. */
2046
2047 name = n->child->string;
2048 sec = n->child->next->string;
2049 if (lastsec != NULL) {
2050 if (lastpunct[0] != ',' || lastpunct[1] != '\0')
2051 mandoc_vmsg(MANDOCERR_XR_PUNCT,
2052 mdoc->parse, n->line, n->pos,
2053 "%s before %s(%s)", lastpunct,
2054 name, sec);
2055 cmp = strcmp(lastsec, sec);
2056 if (cmp > 0)
2057 mandoc_vmsg(MANDOCERR_XR_ORDER,
2058 mdoc->parse, n->line, n->pos,
2059 "%s(%s) after %s(%s)", name,
2060 sec, lastname, lastsec);
2061 else if (cmp == 0 &&
2062 strcasecmp(lastname, name) > 0)
2063 mandoc_vmsg(MANDOCERR_XR_ORDER,
2064 mdoc->parse, n->line, n->pos,
2065 "%s after %s", name, lastname);
2066 }
2067 lastname = name;
2068 lastsec = sec;
2069
2070 /* Process the following node. */
2071
2072 n = n->next;
2073 if (n == NULL)
2074 break;
2075 if (n->tok == MDOC_Xr) {
2076 lastpunct = "none";
2077 continue;
2078 }
2079 if (n->type != ROFFT_TEXT)
2080 break;
2081 for (name = n->string; *name != '\0'; name++)
2082 if (isalpha((const unsigned char)*name))
2083 return;
2084 lastpunct = n->string;
2085 if (n->next == NULL || n->next->tok == MDOC_Rs)
2086 mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
2087 n->line, n->pos, "%s after %s(%s)",
2088 lastpunct, lastname, lastsec);
2089 n = n->next;
2090 }
2091 }
2092
2093 static int
2094 child_an(const struct roff_node *n)
2095 {
2096
2097 for (n = n->child; n != NULL; n = n->next)
2098 if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
2099 return 1;
2100 return 0;
2101 }
2102
2103 static void
2104 post_sh_authors(POST_ARGS)
2105 {
2106
2107 if ( ! child_an(mdoc->last))
2108 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
2109 mdoc->last->line, mdoc->last->pos, NULL);
2110 }
2111
2112 static void
2113 post_sh_head(POST_ARGS)
2114 {
2115 struct roff_node *nch;
2116 const char *goodsec;
2117 enum roff_sec sec;
2118
2119 /*
2120 * Process a new section. Sections are either "named" or
2121 * "custom". Custom sections are user-defined, while named ones
2122 * follow a conventional order and may only appear in certain
2123 * manual sections.
2124 */
2125
2126 sec = mdoc->last->sec;
2127
2128 /* The NAME should be first. */
2129
2130 if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
2131 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
2132 mdoc->last->line, mdoc->last->pos, "Sh %s",
2133 sec != SEC_CUSTOM ? secnames[sec] :
2134 (nch = mdoc->last->child) == NULL ? "" :
2135 nch->type == ROFFT_TEXT ? nch->string :
2136 roff_name[nch->tok]);
2137
2138 /* The SYNOPSIS gets special attention in other areas. */
2139
2140 if (sec == SEC_SYNOPSIS) {
2141 roff_setreg(mdoc->roff, "nS", 1, '=');
2142 mdoc->flags |= MDOC_SYNOPSIS;
2143 } else {
2144 roff_setreg(mdoc->roff, "nS", 0, '=');
2145 mdoc->flags &= ~MDOC_SYNOPSIS;
2146 }
2147
2148 /* Mark our last section. */
2149
2150 mdoc->lastsec = sec;
2151
2152 /* We don't care about custom sections after this. */
2153
2154 if (sec == SEC_CUSTOM)
2155 return;
2156
2157 /*
2158 * Check whether our non-custom section is being repeated or is
2159 * out of order.
2160 */
2161
2162 if (sec == mdoc->lastnamed)
2163 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
2164 mdoc->last->line, mdoc->last->pos,
2165 "Sh %s", secnames[sec]);
2166
2167 if (sec < mdoc->lastnamed)
2168 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
2169 mdoc->last->line, mdoc->last->pos,
2170 "Sh %s", secnames[sec]);
2171
2172 /* Mark the last named section. */
2173
2174 mdoc->lastnamed = sec;
2175
2176 /* Check particular section/manual conventions. */
2177
2178 if (mdoc->meta.msec == NULL)
2179 return;
2180
2181 goodsec = NULL;
2182 switch (sec) {
2183 case SEC_ERRORS:
2184 if (*mdoc->meta.msec == '4')
2185 break;
2186 goodsec = "2, 3, 4, 9";
2187 /* FALLTHROUGH */
2188 case SEC_RETURN_VALUES:
2189 case SEC_LIBRARY:
2190 if (*mdoc->meta.msec == '2')
2191 break;
2192 if (*mdoc->meta.msec == '3')
2193 break;
2194 if (NULL == goodsec)
2195 goodsec = "2, 3, 9";
2196 /* FALLTHROUGH */
2197 case SEC_CONTEXT:
2198 if (*mdoc->meta.msec == '9')
2199 break;
2200 if (NULL == goodsec)
2201 goodsec = "9";
2202 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
2203 mdoc->last->line, mdoc->last->pos,
2204 "Sh %s for %s only", secnames[sec], goodsec);
2205 break;
2206 default:
2207 break;
2208 }
2209 }
2210
2211 static void
2212 post_xr(POST_ARGS)
2213 {
2214 struct roff_node *n, *nch;
2215
2216 n = mdoc->last;
2217 nch = n->child;
2218 if (nch->next == NULL) {
2219 mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
2220 n->line, n->pos, "Xr %s", nch->string);
2221 } else
2222 assert(nch->next == n->last);
2223 post_delim(mdoc);
2224 }
2225
2226 static void
2227 post_ignpar(POST_ARGS)
2228 {
2229 struct roff_node *np;
2230
2231 switch (mdoc->last->type) {
2232 case ROFFT_BLOCK:
2233 post_prevpar(mdoc);
2234 return;
2235 case ROFFT_HEAD:
2236 post_hyph(mdoc);
2237 return;
2238 case ROFFT_BODY:
2239 break;
2240 default:
2241 return;
2242 }
2243
2244 if ((np = mdoc->last->child) != NULL)
2245 if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2246 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2247 mdoc->parse, np->line, np->pos,
2248 "%s after %s", roff_name[np->tok],
2249 roff_name[mdoc->last->tok]);
2250 roff_node_delete(mdoc, np);
2251 }
2252
2253 if ((np = mdoc->last->last) != NULL)
2254 if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2255 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2256 np->line, np->pos, "%s at the end of %s",
2257 roff_name[np->tok],
2258 roff_name[mdoc->last->tok]);
2259 roff_node_delete(mdoc, np);
2260 }
2261 }
2262
2263 static void
2264 post_prevpar(POST_ARGS)
2265 {
2266 struct roff_node *n;
2267
2268 n = mdoc->last;
2269 if (NULL == n->prev)
2270 return;
2271 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2272 return;
2273
2274 /*
2275 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2276 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2277 */
2278
2279 if (n->prev->tok != MDOC_Pp &&
2280 n->prev->tok != MDOC_Lp &&
2281 n->prev->tok != ROFF_br)
2282 return;
2283 if (n->tok == MDOC_Bl && n->norm->Bl.comp)
2284 return;
2285 if (n->tok == MDOC_Bd && n->norm->Bd.comp)
2286 return;
2287 if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
2288 return;
2289
2290 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2291 n->prev->line, n->prev->pos, "%s before %s",
2292 roff_name[n->prev->tok], roff_name[n->tok]);
2293 roff_node_delete(mdoc, n->prev);
2294 }
2295
2296 static void
2297 post_par(POST_ARGS)
2298 {
2299 struct roff_node *np;
2300
2301 np = mdoc->last;
2302 if (np->tok != ROFF_br && np->tok != ROFF_sp)
2303 post_prevpar(mdoc);
2304
2305 if (np->tok == ROFF_sp) {
2306 if (np->child != NULL && np->child->next != NULL)
2307 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2308 np->child->next->line, np->child->next->pos,
2309 "sp ... %s", np->child->next->string);
2310 } else if (np->child != NULL)
2311 mandoc_vmsg(MANDOCERR_ARG_SKIP,
2312 mdoc->parse, np->line, np->pos, "%s %s",
2313 roff_name[np->tok], np->child->string);
2314
2315 if ((np = mdoc->last->prev) == NULL) {
2316 np = mdoc->last->parent;
2317 if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
2318 return;
2319 } else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
2320 (mdoc->last->tok != ROFF_br ||
2321 (np->tok != ROFF_sp && np->tok != ROFF_br)))
2322 return;
2323
2324 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2325 mdoc->last->line, mdoc->last->pos, "%s after %s",
2326 roff_name[mdoc->last->tok], roff_name[np->tok]);
2327 roff_node_delete(mdoc, mdoc->last);
2328 }
2329
2330 static void
2331 post_dd(POST_ARGS)
2332 {
2333 struct roff_node *n;
2334 char *datestr;
2335
2336 n = mdoc->last;
2337 n->flags |= NODE_NOPRT;
2338
2339 if (mdoc->meta.date != NULL) {
2340 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2341 n->line, n->pos, "Dd");
2342 free(mdoc->meta.date);
2343 } else if (mdoc->flags & MDOC_PBODY)
2344 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2345 n->line, n->pos, "Dd");
2346 else if (mdoc->meta.title != NULL)
2347 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2348 n->line, n->pos, "Dd after Dt");
2349 else if (mdoc->meta.os != NULL)
2350 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2351 n->line, n->pos, "Dd after Os");
2352
2353 if (n->child == NULL || n->child->string[0] == '\0') {
2354 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2355 mandoc_normdate(mdoc, NULL, n->line, n->pos);
2356 return;
2357 }
2358
2359 datestr = NULL;
2360 deroff(&datestr, n);
2361 if (mdoc->quick)
2362 mdoc->meta.date = datestr;
2363 else {
2364 mdoc->meta.date = mandoc_normdate(mdoc,
2365 datestr, n->line, n->pos);
2366 free(datestr);
2367 }
2368 }
2369
2370 static void
2371 post_dt(POST_ARGS)
2372 {
2373 struct roff_node *nn, *n;
2374 const char *cp;
2375 char *p;
2376
2377 n = mdoc->last;
2378 n->flags |= NODE_NOPRT;
2379
2380 if (mdoc->flags & MDOC_PBODY) {
2381 mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2382 n->line, n->pos, "Dt");
2383 return;
2384 }
2385
2386 if (mdoc->meta.title != NULL)
2387 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2388 n->line, n->pos, "Dt");
2389 else if (mdoc->meta.os != NULL)
2390 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2391 n->line, n->pos, "Dt after Os");
2392
2393 free(mdoc->meta.title);
2394 free(mdoc->meta.msec);
2395 free(mdoc->meta.vol);
2396 free(mdoc->meta.arch);
2397
2398 mdoc->meta.title = NULL;
2399 mdoc->meta.msec = NULL;
2400 mdoc->meta.vol = NULL;
2401 mdoc->meta.arch = NULL;
2402
2403 /* Mandatory first argument: title. */
2404
2405 nn = n->child;
2406 if (nn == NULL || *nn->string == '\0') {
2407 mandoc_msg(MANDOCERR_DT_NOTITLE,
2408 mdoc->parse, n->line, n->pos, "Dt");
2409 mdoc->meta.title = mandoc_strdup("UNTITLED");
2410 } else {
2411 mdoc->meta.title = mandoc_strdup(nn->string);
2412
2413 /* Check that all characters are uppercase. */
2414
2415 for (p = nn->string; *p != '\0'; p++)
2416 if (islower((unsigned char)*p)) {
2417 mandoc_vmsg(MANDOCERR_TITLE_CASE,
2418 mdoc->parse, nn->line,
2419 nn->pos + (p - nn->string),
2420 "Dt %s", nn->string);
2421 break;
2422 }
2423 }
2424
2425 /* Mandatory second argument: section. */
2426
2427 if (nn != NULL)
2428 nn = nn->next;
2429
2430 if (nn == NULL) {
2431 mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2432 mdoc->parse, n->line, n->pos,
2433 "Dt %s", mdoc->meta.title);
2434 mdoc->meta.vol = mandoc_strdup("LOCAL");
2435 return; /* msec and arch remain NULL. */
2436 }
2437
2438 mdoc->meta.msec = mandoc_strdup(nn->string);
2439
2440 /* Infer volume title from section number. */
2441
2442 cp = mandoc_a2msec(nn->string);
2443 if (cp == NULL) {
2444 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2445 nn->line, nn->pos, "Dt ... %s", nn->string);
2446 mdoc->meta.vol = mandoc_strdup(nn->string);
2447 } else
2448 mdoc->meta.vol = mandoc_strdup(cp);
2449
2450 /* Optional third argument: architecture. */
2451
2452 if ((nn = nn->next) == NULL)
2453 return;
2454
2455 for (p = nn->string; *p != '\0'; p++)
2456 *p = tolower((unsigned char)*p);
2457 mdoc->meta.arch = mandoc_strdup(nn->string);
2458
2459 /* Ignore fourth and later arguments. */
2460
2461 if ((nn = nn->next) != NULL)
2462 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2463 nn->line, nn->pos, "Dt ... %s", nn->string);
2464 }
2465
2466 static void
2467 post_bx(POST_ARGS)
2468 {
2469 struct roff_node *n, *nch;
2470 const char *macro;
2471
2472 post_delim(mdoc);
2473
2474 n = mdoc->last;
2475 nch = n->child;
2476
2477 if (nch != NULL) {
2478 macro = !strcmp(nch->string, "Open") ? "Ox" :
2479 !strcmp(nch->string, "Net") ? "Nx" :
2480 !strcmp(nch->string, "Free") ? "Fx" :
2481 !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
2482 if (macro != NULL)
2483 mandoc_msg(MANDOCERR_BX, mdoc->parse,
2484 n->line, n->pos, macro);
2485 mdoc->last = nch;
2486 nch = nch->next;
2487 mdoc->next = ROFF_NEXT_SIBLING;
2488 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2489 mdoc->last->flags |= NODE_NOSRC;
2490 mdoc->next = ROFF_NEXT_SIBLING;
2491 } else
2492 mdoc->next = ROFF_NEXT_CHILD;
2493 roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2494 mdoc->last->flags |= NODE_NOSRC;
2495
2496 if (nch == NULL) {
2497 mdoc->last = n;
2498 return;
2499 }
2500
2501 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2502 mdoc->last->flags |= NODE_NOSRC;
2503 mdoc->next = ROFF_NEXT_SIBLING;
2504 roff_word_alloc(mdoc, n->line, n->pos, "-");
2505 mdoc->last->flags |= NODE_NOSRC;
2506 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2507 mdoc->last->flags |= NODE_NOSRC;
2508 mdoc->last = n;
2509
2510 /*
2511 * Make `Bx's second argument always start with an uppercase
2512 * letter. Groff checks if it's an "accepted" term, but we just
2513 * uppercase blindly.
2514 */
2515
2516 *nch->string = (char)toupper((unsigned char)*nch->string);
2517 }
2518
2519 static void
2520 post_os(POST_ARGS)
2521 {
2522 #ifndef OSNAME
2523 struct utsname utsname;
2524 static char *defbuf;
2525 #endif
2526 struct roff_node *n;
2527
2528 n = mdoc->last;
2529 n->flags |= NODE_NOPRT;
2530
2531 if (mdoc->meta.os != NULL)
2532 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2533 n->line, n->pos, "Os");
2534 else if (mdoc->flags & MDOC_PBODY)
2535 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2536 n->line, n->pos, "Os");
2537
2538 /*
2539 * Set the operating system by way of the `Os' macro.
2540 * The order of precedence is:
2541 * 1. the argument of the `Os' macro, unless empty
2542 * 2. the -Ios=foo command line argument, if provided
2543 * 3. -DOSNAME="\"foo\"", if provided during compilation
2544 * 4. "sysname release" from uname(3)
2545 */
2546
2547 free(mdoc->meta.os);
2548 mdoc->meta.os = NULL;
2549 deroff(&mdoc->meta.os, n);
2550 if (mdoc->meta.os)
2551 goto out;
2552
2553 if (mdoc->os_s != NULL) {
2554 mdoc->meta.os = mandoc_strdup(mdoc->os_s);
2555 goto out;
2556 }
2557
2558 #ifdef OSNAME
2559 mdoc->meta.os = mandoc_strdup(OSNAME);
2560 #else /*!OSNAME */
2561 if (defbuf == NULL) {
2562 if (uname(&utsname) == -1) {
2563 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2564 n->line, n->pos, "Os");
2565 defbuf = mandoc_strdup("UNKNOWN");
2566 } else
2567 mandoc_asprintf(&defbuf, "%s %s",
2568 utsname.sysname, utsname.release);
2569 }
2570 mdoc->meta.os = mandoc_strdup(defbuf);
2571 #endif /*!OSNAME*/
2572
2573 out:
2574 if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
2575 if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
2576 mdoc->meta.os_e = MANDOC_OS_OPENBSD;
2577 else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
2578 mdoc->meta.os_e = MANDOC_OS_NETBSD;
2579 }
2580
2581 /*
2582 * This is the earliest point where we can check
2583 * Mdocdate conventions because we don't know
2584 * the operating system earlier.
2585 */
2586
2587 if (n->child != NULL)
2588 mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse,
2589 n->child->line, n->child->pos,
2590 "Os %s (%s)", n->child->string,
2591 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2592 "OpenBSD" : "NetBSD");
2593
2594 while (n->tok != MDOC_Dd)
2595 if ((n = n->prev) == NULL)
2596 return;
2597 if ((n = n->child) == NULL)
2598 return;
2599 if (strncmp(n->string, "$" "Mdocdate", 9)) {
2600 if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
2601 mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING,
2602 mdoc->parse, n->line, n->pos,
2603 "Dd %s (OpenBSD)", n->string);
2604 } else {
2605 if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
2606 mandoc_vmsg(MANDOCERR_MDOCDATE,
2607 mdoc->parse, n->line, n->pos,
2608 "Dd %s (NetBSD)", n->string);
2609 }
2610 }
2611
2612 enum roff_sec
2613 mdoc_a2sec(const char *p)
2614 {
2615 int i;
2616
2617 for (i = 0; i < (int)SEC__MAX; i++)
2618 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2619 return (enum roff_sec)i;
2620
2621 return SEC_CUSTOM;
2622 }
2623
2624 static size_t
2625 macro2len(enum roff_tok macro)
2626 {
2627
2628 switch (macro) {
2629 case MDOC_Ad:
2630 return 12;
2631 case MDOC_Ao:
2632 return 12;
2633 case MDOC_An:
2634 return 12;
2635 case MDOC_Aq:
2636 return 12;
2637 case MDOC_Ar:
2638 return 12;
2639 case MDOC_Bo:
2640 return 12;
2641 case MDOC_Bq:
2642 return 12;
2643 case MDOC_Cd:
2644 return 12;
2645 case MDOC_Cm:
2646 return 10;
2647 case MDOC_Do:
2648 return 10;
2649 case MDOC_Dq:
2650 return 12;
2651 case MDOC_Dv:
2652 return 12;
2653 case MDOC_Eo:
2654 return 12;
2655 case MDOC_Em:
2656 return 10;
2657 case MDOC_Er:
2658 return 17;
2659 case MDOC_Ev:
2660 return 15;
2661 case MDOC_Fa:
2662 return 12;
2663 case MDOC_Fl:
2664 return 10;
2665 case MDOC_Fo:
2666 return 16;
2667 case MDOC_Fn:
2668 return 16;
2669 case MDOC_Ic:
2670 return 10;
2671 case MDOC_Li:
2672 return 16;
2673 case MDOC_Ms:
2674 return 6;
2675 case MDOC_Nm:
2676 return 10;
2677 case MDOC_No:
2678 return 12;
2679 case MDOC_Oo:
2680 return 10;
2681 case MDOC_Op:
2682 return 14;
2683 case MDOC_Pa:
2684 return 32;
2685 case MDOC_Pf:
2686 return 12;
2687 case MDOC_Po:
2688 return 12;
2689 case MDOC_Pq:
2690 return 12;
2691 case MDOC_Ql:
2692 return 16;
2693 case MDOC_Qo:
2694 return 12;
2695 case MDOC_So:
2696 return 12;
2697 case MDOC_Sq:
2698 return 12;
2699 case MDOC_Sy:
2700 return 6;
2701 case MDOC_Sx:
2702 return 16;
2703 case MDOC_Tn:
2704 return 10;
2705 case MDOC_Va:
2706 return 12;
2707 case MDOC_Vt:
2708 return 12;
2709 case MDOC_Xr:
2710 return 10;
2711 default:
2712 break;
2713 };
2714 return 0;
2715 }