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