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