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