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