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