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