]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
Vastly simplify man(7) block unwinding, similar to mdoc_macro.c 1.171.
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.285 2015/04/02 22:48:17 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 "libmdoc.h"
40
41 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
42
43 #define PRE_ARGS struct mdoc *mdoc, struct roff_node *n
44 #define POST_ARGS struct mdoc *mdoc
45
46 enum check_ineq {
47 CHECK_LT,
48 CHECK_GT,
49 CHECK_EQ
50 };
51
52 typedef void (*v_pre)(PRE_ARGS);
53 typedef void (*v_post)(POST_ARGS);
54
55 struct valids {
56 v_pre pre;
57 v_post post;
58 };
59
60 static void check_text(struct mdoc *, int, int, char *);
61 static void check_argv(struct mdoc *,
62 struct roff_node *, struct mdoc_argv *);
63 static void check_args(struct mdoc *, struct roff_node *);
64 static int child_an(const struct roff_node *);
65 static enum roff_sec a2sec(const char *);
66 static size_t macro2len(int);
67 static void rewrite_macro2len(char **);
68
69 static void post_an(POST_ARGS);
70 static void post_at(POST_ARGS);
71 static void post_bf(POST_ARGS);
72 static void post_bk(POST_ARGS);
73 static void post_bl(POST_ARGS);
74 static void post_bl_block(POST_ARGS);
75 static void post_bl_block_tag(POST_ARGS);
76 static void post_bl_head(POST_ARGS);
77 static void post_bx(POST_ARGS);
78 static void post_d1(POST_ARGS);
79 static void post_defaults(POST_ARGS);
80 static void post_dd(POST_ARGS);
81 static void post_dt(POST_ARGS);
82 static void post_en(POST_ARGS);
83 static void post_es(POST_ARGS);
84 static void post_eoln(POST_ARGS);
85 static void post_ex(POST_ARGS);
86 static void post_fa(POST_ARGS);
87 static void post_fn(POST_ARGS);
88 static void post_fname(POST_ARGS);
89 static void post_fo(POST_ARGS);
90 static void post_hyph(POST_ARGS);
91 static void post_ignpar(POST_ARGS);
92 static void post_it(POST_ARGS);
93 static void post_lb(POST_ARGS);
94 static void post_literal(POST_ARGS);
95 static void post_nd(POST_ARGS);
96 static void post_nm(POST_ARGS);
97 static void post_ns(POST_ARGS);
98 static void post_os(POST_ARGS);
99 static void post_par(POST_ARGS);
100 static void post_root(POST_ARGS);
101 static void post_rs(POST_ARGS);
102 static void post_sh(POST_ARGS);
103 static void post_sh_head(POST_ARGS);
104 static void post_sh_name(POST_ARGS);
105 static void post_sh_see_also(POST_ARGS);
106 static void post_sh_authors(POST_ARGS);
107 static void post_sm(POST_ARGS);
108 static void post_st(POST_ARGS);
109 static void post_vt(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, post_vt }, /* 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 mdoc *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 mdoc *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 mdoc *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 mdoc *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 mdoc *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 /* NOTREACHED */
661 }
662 if (DISP__NONE == dt)
663 continue;
664
665 if (DISP__NONE == n->norm->Bd.type)
666 n->norm->Bd.type = dt;
667 else
668 mandoc_vmsg(MANDOCERR_BD_REP,
669 mdoc->parse, n->line, n->pos,
670 "Bd -%s", mdoc_argnames[argv->arg]);
671 }
672
673 if (DISP__NONE == n->norm->Bd.type) {
674 mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
675 n->line, n->pos, "Bd");
676 n->norm->Bd.type = DISP_ragged;
677 }
678 pre_par(mdoc, n);
679 }
680
681 static void
682 pre_an(PRE_ARGS)
683 {
684 struct mdoc_argv *argv;
685 size_t i;
686
687 if (n->args == NULL)
688 return;
689
690 for (i = 1; i < n->args->argc; i++) {
691 argv = n->args->argv + i;
692 mandoc_vmsg(MANDOCERR_AN_REP,
693 mdoc->parse, argv->line, argv->pos,
694 "An -%s", mdoc_argnames[argv->arg]);
695 }
696
697 argv = n->args->argv;
698 if (argv->arg == MDOC_Split)
699 n->norm->An.auth = AUTH_split;
700 else if (argv->arg == MDOC_Nosplit)
701 n->norm->An.auth = AUTH_nosplit;
702 else
703 abort();
704 }
705
706 static void
707 pre_std(PRE_ARGS)
708 {
709
710 if (n->args && 1 == n->args->argc)
711 if (MDOC_Std == n->args->argv[0].arg)
712 return;
713
714 mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
715 n->line, n->pos, mdoc_macronames[n->tok]);
716 }
717
718 static void
719 pre_obsolete(PRE_ARGS)
720 {
721
722 if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
723 mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
724 n->line, n->pos, mdoc_macronames[n->tok]);
725 }
726
727 static void
728 pre_dt(PRE_ARGS)
729 {
730
731 if (mdoc->meta.title != NULL)
732 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
733 n->line, n->pos, "Dt");
734 else if (mdoc->meta.os != NULL)
735 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
736 n->line, n->pos, "Dt after Os");
737 }
738
739 static void
740 pre_os(PRE_ARGS)
741 {
742
743 if (mdoc->meta.os != NULL)
744 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
745 n->line, n->pos, "Os");
746 else if (mdoc->flags & MDOC_PBODY)
747 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
748 n->line, n->pos, "Os");
749 }
750
751 static void
752 pre_dd(PRE_ARGS)
753 {
754
755 if (mdoc->meta.date != NULL)
756 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
757 n->line, n->pos, "Dd");
758 else if (mdoc->flags & MDOC_PBODY)
759 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
760 n->line, n->pos, "Dd");
761 else if (mdoc->meta.title != NULL)
762 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
763 n->line, n->pos, "Dd after Dt");
764 else if (mdoc->meta.os != NULL)
765 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
766 n->line, n->pos, "Dd after Os");
767 }
768
769 static void
770 post_bf(POST_ARGS)
771 {
772 struct roff_node *np, *nch;
773 enum mdocargt arg;
774
775 /*
776 * Unlike other data pointers, these are "housed" by the HEAD
777 * element, which contains the goods.
778 */
779
780 np = mdoc->last;
781 if (np->type != ROFFT_HEAD)
782 return;
783
784 assert(np->parent->type == ROFFT_BLOCK);
785 assert(MDOC_Bf == np->parent->tok);
786
787 /* Check the number of arguments. */
788
789 nch = np->child;
790 if (NULL == np->parent->args) {
791 if (NULL == nch) {
792 mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
793 np->line, np->pos, "Bf");
794 return;
795 }
796 nch = nch->next;
797 }
798 if (NULL != nch)
799 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
800 nch->line, nch->pos, "Bf ... %s", nch->string);
801
802 /* Extract argument into data. */
803
804 if (np->parent->args) {
805 arg = np->parent->args->argv[0].arg;
806 if (MDOC_Emphasis == arg)
807 np->norm->Bf.font = FONT_Em;
808 else if (MDOC_Literal == arg)
809 np->norm->Bf.font = FONT_Li;
810 else if (MDOC_Symbolic == arg)
811 np->norm->Bf.font = FONT_Sy;
812 else
813 abort();
814 return;
815 }
816
817 /* Extract parameter into data. */
818
819 if (0 == strcmp(np->child->string, "Em"))
820 np->norm->Bf.font = FONT_Em;
821 else if (0 == strcmp(np->child->string, "Li"))
822 np->norm->Bf.font = FONT_Li;
823 else if (0 == strcmp(np->child->string, "Sy"))
824 np->norm->Bf.font = FONT_Sy;
825 else
826 mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
827 np->child->line, np->child->pos,
828 "Bf %s", np->child->string);
829 }
830
831 static void
832 post_lb(POST_ARGS)
833 {
834 struct roff_node *n;
835 const char *stdlibname;
836 char *libname;
837
838 n = mdoc->last->child;
839 assert(n->type == ROFFT_TEXT);
840
841 if (NULL == (stdlibname = mdoc_a2lib(n->string)))
842 mandoc_asprintf(&libname,
843 "library \\(Lq%s\\(Rq", n->string);
844 else
845 libname = mandoc_strdup(stdlibname);
846
847 free(n->string);
848 n->string = libname;
849 }
850
851 static void
852 post_eoln(POST_ARGS)
853 {
854 const struct roff_node *n;
855
856 n = mdoc->last;
857 if (n->child)
858 mandoc_vmsg(MANDOCERR_ARG_SKIP,
859 mdoc->parse, n->line, n->pos,
860 "%s %s", mdoc_macronames[n->tok],
861 n->child->string);
862 }
863
864 static void
865 post_fname(POST_ARGS)
866 {
867 const struct roff_node *n;
868 const char *cp;
869 size_t pos;
870
871 n = mdoc->last->child;
872 pos = strcspn(n->string, "()");
873 cp = n->string + pos;
874 if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
875 mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
876 n->line, n->pos + pos, n->string);
877 }
878
879 static void
880 post_fn(POST_ARGS)
881 {
882
883 post_fname(mdoc);
884 post_fa(mdoc);
885 }
886
887 static void
888 post_fo(POST_ARGS)
889 {
890 const struct roff_node *n;
891
892 n = mdoc->last;
893
894 if (n->type != ROFFT_HEAD)
895 return;
896
897 if (n->child == NULL) {
898 mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
899 n->line, n->pos, "Fo");
900 return;
901 }
902 if (n->child != n->last) {
903 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
904 n->child->next->line, n->child->next->pos,
905 "Fo ... %s", n->child->next->string);
906 while (n->child != n->last)
907 mdoc_node_delete(mdoc, n->last);
908 }
909
910 post_fname(mdoc);
911 }
912
913 static void
914 post_fa(POST_ARGS)
915 {
916 const struct roff_node *n;
917 const char *cp;
918
919 for (n = mdoc->last->child; n != NULL; n = n->next) {
920 for (cp = n->string; *cp != '\0'; cp++) {
921 /* Ignore callbacks and alterations. */
922 if (*cp == '(' || *cp == '{')
923 break;
924 if (*cp != ',')
925 continue;
926 mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
927 n->line, n->pos + (cp - n->string),
928 n->string);
929 break;
930 }
931 }
932 }
933
934 static void
935 post_vt(POST_ARGS)
936 {
937 const struct roff_node *n;
938
939 /*
940 * The Vt macro comes in both ELEM and BLOCK form, both of which
941 * have different syntaxes (yet more context-sensitive
942 * behaviour). ELEM types must have a child, which is already
943 * guaranteed by the in_line parsing routine; BLOCK types,
944 * specifically the BODY, should only have TEXT children.
945 */
946
947 if (mdoc->last->type != ROFFT_BODY)
948 return;
949
950 for (n = mdoc->last->child; n; n = n->next)
951 if (n->type != ROFFT_TEXT)
952 mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
953 n->line, n->pos, mdoc_macronames[n->tok]);
954 }
955
956 static void
957 post_nm(POST_ARGS)
958 {
959 struct roff_node *n;
960
961 n = mdoc->last;
962
963 if (n->last != NULL &&
964 (n->last->tok == MDOC_Pp ||
965 n->last->tok == MDOC_Lp))
966 mdoc_node_relink(mdoc, n->last);
967
968 if (NULL != mdoc->meta.name)
969 return;
970
971 mdoc_deroff(&mdoc->meta.name, n);
972
973 if (NULL == mdoc->meta.name)
974 mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
975 n->line, n->pos, "Nm");
976 }
977
978 static void
979 post_nd(POST_ARGS)
980 {
981 struct roff_node *n;
982
983 n = mdoc->last;
984
985 if (n->type != ROFFT_BODY)
986 return;
987
988 if (n->child == NULL)
989 mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
990 n->line, n->pos, "Nd");
991
992 post_hyph(mdoc);
993 }
994
995 static void
996 post_d1(POST_ARGS)
997 {
998 struct roff_node *n;
999
1000 n = mdoc->last;
1001
1002 if (n->type != ROFFT_BODY)
1003 return;
1004
1005 if (n->child == NULL)
1006 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1007 n->line, n->pos, "D1");
1008
1009 post_hyph(mdoc);
1010 }
1011
1012 static void
1013 post_literal(POST_ARGS)
1014 {
1015 struct roff_node *n;
1016
1017 n = mdoc->last;
1018
1019 if (n->type != ROFFT_BODY)
1020 return;
1021
1022 if (n->child == NULL)
1023 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1024 n->line, n->pos, mdoc_macronames[n->tok]);
1025
1026 if (n->tok == MDOC_Bd &&
1027 n->norm->Bd.type != DISP_literal &&
1028 n->norm->Bd.type != DISP_unfilled)
1029 return;
1030
1031 mdoc->flags &= ~MDOC_LITERAL;
1032 }
1033
1034 static void
1035 post_defaults(POST_ARGS)
1036 {
1037 struct roff_node *nn;
1038
1039 /*
1040 * The `Ar' defaults to "file ..." if no value is provided as an
1041 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1042 * gets an empty string.
1043 */
1044
1045 if (mdoc->last->child)
1046 return;
1047
1048 nn = mdoc->last;
1049 mdoc->next = MDOC_NEXT_CHILD;
1050
1051 switch (nn->tok) {
1052 case MDOC_Ar:
1053 mdoc_word_alloc(mdoc, nn->line, nn->pos, "file");
1054 mdoc_word_alloc(mdoc, nn->line, nn->pos, "...");
1055 break;
1056 case MDOC_Pa:
1057 /* FALLTHROUGH */
1058 case MDOC_Mt:
1059 mdoc_word_alloc(mdoc, nn->line, nn->pos, "~");
1060 break;
1061 default:
1062 abort();
1063 /* NOTREACHED */
1064 }
1065 mdoc->last = nn;
1066 }
1067
1068 static void
1069 post_at(POST_ARGS)
1070 {
1071 struct roff_node *n;
1072 const char *std_att;
1073 char *att;
1074
1075 n = mdoc->last;
1076 if (n->child == NULL) {
1077 mdoc->next = MDOC_NEXT_CHILD;
1078 mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1079 mdoc->last = n;
1080 return;
1081 }
1082
1083 /*
1084 * If we have a child, look it up in the standard keys. If a
1085 * key exist, use that instead of the child; if it doesn't,
1086 * prefix "AT&T UNIX " to the existing data.
1087 */
1088
1089 n = n->child;
1090 assert(n->type == ROFFT_TEXT);
1091 if (NULL == (std_att = mdoc_a2att(n->string))) {
1092 mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1093 n->line, n->pos, "At %s", n->string);
1094 mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
1095 } else
1096 att = mandoc_strdup(std_att);
1097
1098 free(n->string);
1099 n->string = att;
1100 }
1101
1102 static void
1103 post_an(POST_ARGS)
1104 {
1105 struct roff_node *np, *nch;
1106
1107 np = mdoc->last;
1108 nch = np->child;
1109 if (np->norm->An.auth == AUTH__NONE) {
1110 if (nch == NULL)
1111 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1112 np->line, np->pos, "An");
1113 } else if (nch != NULL)
1114 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1115 nch->line, nch->pos, "An ... %s", nch->string);
1116 }
1117
1118 static void
1119 post_en(POST_ARGS)
1120 {
1121
1122 if (mdoc->last->type == ROFFT_BLOCK)
1123 mdoc->last->norm->Es = mdoc->last_es;
1124 }
1125
1126 static void
1127 post_es(POST_ARGS)
1128 {
1129
1130 mdoc->last_es = mdoc->last;
1131 }
1132
1133 static void
1134 post_it(POST_ARGS)
1135 {
1136 struct roff_node *nbl, *nit, *nch;
1137 int i, cols;
1138 enum mdoc_list lt;
1139
1140 nit = mdoc->last;
1141 if (nit->type != ROFFT_BLOCK)
1142 return;
1143
1144 nbl = nit->parent->parent;
1145 lt = nbl->norm->Bl.type;
1146
1147 switch (lt) {
1148 case LIST_tag:
1149 /* FALLTHROUGH */
1150 case LIST_hang:
1151 /* FALLTHROUGH */
1152 case LIST_ohang:
1153 /* FALLTHROUGH */
1154 case LIST_inset:
1155 /* FALLTHROUGH */
1156 case LIST_diag:
1157 if (nit->head->child == NULL)
1158 mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1159 mdoc->parse, nit->line, nit->pos,
1160 "Bl -%s It",
1161 mdoc_argnames[nbl->args->argv[0].arg]);
1162 break;
1163 case LIST_bullet:
1164 /* FALLTHROUGH */
1165 case LIST_dash:
1166 /* FALLTHROUGH */
1167 case LIST_enum:
1168 /* FALLTHROUGH */
1169 case LIST_hyphen:
1170 if (nit->body == NULL || nit->body->child == NULL)
1171 mandoc_vmsg(MANDOCERR_IT_NOBODY,
1172 mdoc->parse, nit->line, nit->pos,
1173 "Bl -%s It",
1174 mdoc_argnames[nbl->args->argv[0].arg]);
1175 /* FALLTHROUGH */
1176 case LIST_item:
1177 if (nit->head->child != NULL)
1178 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1179 mdoc->parse, nit->line, nit->pos,
1180 "It %s", nit->head->child->string);
1181 break;
1182 case LIST_column:
1183 cols = (int)nbl->norm->Bl.ncols;
1184
1185 assert(nit->head->child == NULL);
1186
1187 for (i = 0, nch = nit->child; nch; nch = nch->next)
1188 if (nch->type == ROFFT_BODY)
1189 i++;
1190
1191 if (i < cols || i > cols + 1)
1192 mandoc_vmsg(MANDOCERR_BL_COL,
1193 mdoc->parse, nit->line, nit->pos,
1194 "%d columns, %d cells", cols, i);
1195 break;
1196 default:
1197 abort();
1198 }
1199 }
1200
1201 static void
1202 post_bl_block(POST_ARGS)
1203 {
1204 struct roff_node *n, *ni, *nc;
1205
1206 /*
1207 * These are fairly complicated, so we've broken them into two
1208 * functions. post_bl_block_tag() is called when a -tag is
1209 * specified, but no -width (it must be guessed). The second
1210 * when a -width is specified (macro indicators must be
1211 * rewritten into real lengths).
1212 */
1213
1214 n = mdoc->last;
1215
1216 if (LIST_tag == n->norm->Bl.type &&
1217 NULL == n->norm->Bl.width) {
1218 post_bl_block_tag(mdoc);
1219 assert(n->norm->Bl.width);
1220 }
1221
1222 for (ni = n->body->child; ni; ni = ni->next) {
1223 if (NULL == ni->body)
1224 continue;
1225 nc = ni->body->last;
1226 while (NULL != nc) {
1227 switch (nc->tok) {
1228 case MDOC_Pp:
1229 /* FALLTHROUGH */
1230 case MDOC_Lp:
1231 /* FALLTHROUGH */
1232 case MDOC_br:
1233 break;
1234 default:
1235 nc = NULL;
1236 continue;
1237 }
1238 if (NULL == ni->next) {
1239 mandoc_msg(MANDOCERR_PAR_MOVE,
1240 mdoc->parse, nc->line, nc->pos,
1241 mdoc_macronames[nc->tok]);
1242 mdoc_node_relink(mdoc, nc);
1243 } else if (0 == n->norm->Bl.comp &&
1244 LIST_column != n->norm->Bl.type) {
1245 mandoc_vmsg(MANDOCERR_PAR_SKIP,
1246 mdoc->parse, nc->line, nc->pos,
1247 "%s before It",
1248 mdoc_macronames[nc->tok]);
1249 mdoc_node_delete(mdoc, nc);
1250 } else
1251 break;
1252 nc = ni->body->last;
1253 }
1254 }
1255 }
1256
1257 /*
1258 * If the argument of -offset or -width is a macro,
1259 * replace it with the associated default width.
1260 */
1261 void
1262 rewrite_macro2len(char **arg)
1263 {
1264 size_t width;
1265 int tok;
1266
1267 if (*arg == NULL)
1268 return;
1269 else if ( ! strcmp(*arg, "Ds"))
1270 width = 6;
1271 else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX)
1272 return;
1273 else
1274 width = macro2len(tok);
1275
1276 free(*arg);
1277 mandoc_asprintf(arg, "%zun", width);
1278 }
1279
1280 static void
1281 post_bl_block_tag(POST_ARGS)
1282 {
1283 struct roff_node *n, *nn;
1284 size_t sz, ssz;
1285 int i;
1286 char buf[24];
1287
1288 /*
1289 * Calculate the -width for a `Bl -tag' list if it hasn't been
1290 * provided. Uses the first head macro. NOTE AGAIN: this is
1291 * ONLY if the -width argument has NOT been provided. See
1292 * rewrite_macro2len() for converting the -width string.
1293 */
1294
1295 sz = 10;
1296 n = mdoc->last;
1297
1298 for (nn = n->body->child; nn; nn = nn->next) {
1299 if (MDOC_It != nn->tok)
1300 continue;
1301
1302 assert(nn->type == ROFFT_BLOCK);
1303 nn = nn->head->child;
1304
1305 if (nn == NULL)
1306 break;
1307
1308 if (nn->type == ROFFT_TEXT) {
1309 sz = strlen(nn->string) + 1;
1310 break;
1311 }
1312
1313 if (0 != (ssz = macro2len(nn->tok)))
1314 sz = ssz;
1315
1316 break;
1317 }
1318
1319 /* Defaults to ten ens. */
1320
1321 (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
1322
1323 /*
1324 * We have to dynamically add this to the macro's argument list.
1325 * We're guaranteed that a MDOC_Width doesn't already exist.
1326 */
1327
1328 assert(n->args);
1329 i = (int)(n->args->argc)++;
1330
1331 n->args->argv = mandoc_reallocarray(n->args->argv,
1332 n->args->argc, sizeof(struct mdoc_argv));
1333
1334 n->args->argv[i].arg = MDOC_Width;
1335 n->args->argv[i].line = n->line;
1336 n->args->argv[i].pos = n->pos;
1337 n->args->argv[i].sz = 1;
1338 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1339 n->args->argv[i].value[0] = mandoc_strdup(buf);
1340
1341 /* Set our width! */
1342 n->norm->Bl.width = n->args->argv[i].value[0];
1343 }
1344
1345 static void
1346 post_bl_head(POST_ARGS)
1347 {
1348 struct roff_node *nbl, *nh, *nch, *nnext;
1349 struct mdoc_argv *argv;
1350 int i, j;
1351
1352 nh = mdoc->last;
1353
1354 if (nh->norm->Bl.type != LIST_column) {
1355 if ((nch = nh->child) == NULL)
1356 return;
1357 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1358 nch->line, nch->pos, "Bl ... %s", nch->string);
1359 while (nch != NULL) {
1360 mdoc_node_delete(mdoc, nch);
1361 nch = nh->child;
1362 }
1363 return;
1364 }
1365
1366 /*
1367 * Append old-style lists, where the column width specifiers
1368 * trail as macro parameters, to the new-style ("normal-form")
1369 * lists where they're argument values following -column.
1370 */
1371
1372 if (nh->child == NULL)
1373 return;
1374
1375 nbl = nh->parent;
1376 for (j = 0; j < (int)nbl->args->argc; j++)
1377 if (nbl->args->argv[j].arg == MDOC_Column)
1378 break;
1379
1380 assert(j < (int)nbl->args->argc);
1381
1382 /*
1383 * Accommodate for new-style groff column syntax. Shuffle the
1384 * child nodes, all of which must be TEXT, as arguments for the
1385 * column field. Then, delete the head children.
1386 */
1387
1388 argv = nbl->args->argv + j;
1389 i = argv->sz;
1390 argv->sz += nh->nchild;
1391 argv->value = mandoc_reallocarray(argv->value,
1392 argv->sz, sizeof(char *));
1393
1394 nh->norm->Bl.ncols = argv->sz;
1395 nh->norm->Bl.cols = (void *)argv->value;
1396
1397 for (nch = nh->child; nch != NULL; nch = nnext) {
1398 argv->value[i++] = nch->string;
1399 nch->string = NULL;
1400 nnext = nch->next;
1401 mdoc_node_delete(NULL, nch);
1402 }
1403 nh->nchild = 0;
1404 nh->child = NULL;
1405 }
1406
1407 static void
1408 post_bl(POST_ARGS)
1409 {
1410 struct roff_node *nparent, *nprev; /* of the Bl block */
1411 struct roff_node *nblock, *nbody; /* of the Bl */
1412 struct roff_node *nchild, *nnext; /* of the Bl body */
1413
1414 nbody = mdoc->last;
1415 switch (nbody->type) {
1416 case ROFFT_BLOCK:
1417 post_bl_block(mdoc);
1418 return;
1419 case ROFFT_HEAD:
1420 post_bl_head(mdoc);
1421 return;
1422 case ROFFT_BODY:
1423 break;
1424 default:
1425 return;
1426 }
1427
1428 nchild = nbody->child;
1429 if (nchild == NULL) {
1430 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1431 nbody->line, nbody->pos, "Bl");
1432 return;
1433 }
1434 while (nchild != NULL) {
1435 if (nchild->tok == MDOC_It ||
1436 (nchild->tok == MDOC_Sm &&
1437 nchild->next != NULL &&
1438 nchild->next->tok == MDOC_It)) {
1439 nchild = nchild->next;
1440 continue;
1441 }
1442
1443 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1444 nchild->line, nchild->pos,
1445 mdoc_macronames[nchild->tok]);
1446
1447 /*
1448 * Move the node out of the Bl block.
1449 * First, collect all required node pointers.
1450 */
1451
1452 nblock = nbody->parent;
1453 nprev = nblock->prev;
1454 nparent = nblock->parent;
1455 nnext = nchild->next;
1456
1457 /*
1458 * Unlink this child.
1459 */
1460
1461 assert(NULL == nchild->prev);
1462 if (0 == --nbody->nchild) {
1463 nbody->child = NULL;
1464 nbody->last = NULL;
1465 assert(NULL == nnext);
1466 } else {
1467 nbody->child = nnext;
1468 nnext->prev = NULL;
1469 }
1470
1471 /*
1472 * Relink this child.
1473 */
1474
1475 nchild->parent = nparent;
1476 nchild->prev = nprev;
1477 nchild->next = nblock;
1478
1479 nblock->prev = nchild;
1480 nparent->nchild++;
1481 if (NULL == nprev)
1482 nparent->child = nchild;
1483 else
1484 nprev->next = nchild;
1485
1486 nchild = nnext;
1487 }
1488 }
1489
1490 static void
1491 post_bk(POST_ARGS)
1492 {
1493 struct roff_node *n;
1494
1495 n = mdoc->last;
1496
1497 if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1498 mandoc_msg(MANDOCERR_BLK_EMPTY,
1499 mdoc->parse, n->line, n->pos, "Bk");
1500 mdoc_node_delete(mdoc, n);
1501 }
1502 }
1503
1504 static void
1505 post_sm(struct mdoc *mdoc)
1506 {
1507 struct roff_node *nch;
1508
1509 nch = mdoc->last->child;
1510
1511 if (nch == NULL) {
1512 mdoc->flags ^= MDOC_SMOFF;
1513 return;
1514 }
1515
1516 assert(nch->type == ROFFT_TEXT);
1517
1518 if ( ! strcmp(nch->string, "on")) {
1519 mdoc->flags &= ~MDOC_SMOFF;
1520 return;
1521 }
1522 if ( ! strcmp(nch->string, "off")) {
1523 mdoc->flags |= MDOC_SMOFF;
1524 return;
1525 }
1526
1527 mandoc_vmsg(MANDOCERR_SM_BAD,
1528 mdoc->parse, nch->line, nch->pos,
1529 "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1530 mdoc_node_relink(mdoc, nch);
1531 return;
1532 }
1533
1534 static void
1535 post_root(POST_ARGS)
1536 {
1537 struct roff_node *n;
1538
1539 /* Add missing prologue data. */
1540
1541 if (mdoc->meta.date == NULL)
1542 mdoc->meta.date = mdoc->quick ?
1543 mandoc_strdup("") :
1544 mandoc_normdate(mdoc->parse, NULL, 0, 0);
1545
1546 if (mdoc->meta.title == NULL) {
1547 mandoc_msg(MANDOCERR_DT_NOTITLE,
1548 mdoc->parse, 0, 0, "EOF");
1549 mdoc->meta.title = mandoc_strdup("UNTITLED");
1550 }
1551
1552 if (mdoc->meta.vol == NULL)
1553 mdoc->meta.vol = mandoc_strdup("LOCAL");
1554
1555 if (mdoc->meta.os == NULL) {
1556 mandoc_msg(MANDOCERR_OS_MISSING,
1557 mdoc->parse, 0, 0, NULL);
1558 mdoc->meta.os = mandoc_strdup("");
1559 }
1560
1561 /* Check that we begin with a proper `Sh'. */
1562
1563 n = mdoc->first->child;
1564 while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1565 n = n->next;
1566
1567 if (n == NULL)
1568 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1569 else if (n->tok != MDOC_Sh)
1570 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1571 n->line, n->pos, mdoc_macronames[n->tok]);
1572 }
1573
1574 static void
1575 post_st(POST_ARGS)
1576 {
1577 struct roff_node *n, *nch;
1578 const char *p;
1579
1580 n = mdoc->last;
1581 nch = n->child;
1582
1583 assert(nch->type == ROFFT_TEXT);
1584
1585 if (NULL == (p = mdoc_a2st(nch->string))) {
1586 mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1587 nch->line, nch->pos, "St %s", nch->string);
1588 mdoc_node_delete(mdoc, n);
1589 } else {
1590 free(nch->string);
1591 nch->string = mandoc_strdup(p);
1592 }
1593 }
1594
1595 static void
1596 post_rs(POST_ARGS)
1597 {
1598 struct roff_node *np, *nch, *next, *prev;
1599 int i, j;
1600
1601 np = mdoc->last;
1602
1603 if (np->type != ROFFT_BODY)
1604 return;
1605
1606 if (np->child == NULL) {
1607 mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1608 np->line, np->pos, "Rs");
1609 return;
1610 }
1611
1612 /*
1613 * The full `Rs' block needs special handling to order the
1614 * sub-elements according to `rsord'. Pick through each element
1615 * and correctly order it. This is an insertion sort.
1616 */
1617
1618 next = NULL;
1619 for (nch = np->child->next; nch != NULL; nch = next) {
1620 /* Determine order number of this child. */
1621 for (i = 0; i < RSORD_MAX; i++)
1622 if (rsord[i] == nch->tok)
1623 break;
1624
1625 if (i == RSORD_MAX) {
1626 mandoc_msg(MANDOCERR_RS_BAD,
1627 mdoc->parse, nch->line, nch->pos,
1628 mdoc_macronames[nch->tok]);
1629 i = -1;
1630 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1631 np->norm->Rs.quote_T++;
1632
1633 /*
1634 * Remove this child from the chain. This somewhat
1635 * repeats mdoc_node_unlink(), but since we're
1636 * just re-ordering, there's no need for the
1637 * full unlink process.
1638 */
1639
1640 if ((next = nch->next) != NULL)
1641 next->prev = nch->prev;
1642
1643 if ((prev = nch->prev) != NULL)
1644 prev->next = nch->next;
1645
1646 nch->prev = nch->next = NULL;
1647
1648 /*
1649 * Scan back until we reach a node that's
1650 * to be ordered before this child.
1651 */
1652
1653 for ( ; prev ; prev = prev->prev) {
1654 /* Determine order of `prev'. */
1655 for (j = 0; j < RSORD_MAX; j++)
1656 if (rsord[j] == prev->tok)
1657 break;
1658 if (j == RSORD_MAX)
1659 j = -1;
1660
1661 if (j <= i)
1662 break;
1663 }
1664
1665 /*
1666 * Set this child back into its correct place
1667 * in front of the `prev' node.
1668 */
1669
1670 nch->prev = prev;
1671
1672 if (prev == NULL) {
1673 np->child->prev = nch;
1674 nch->next = np->child;
1675 np->child = nch;
1676 } else {
1677 if (prev->next)
1678 prev->next->prev = nch;
1679 nch->next = prev->next;
1680 prev->next = nch;
1681 }
1682 }
1683 }
1684
1685 /*
1686 * For some arguments of some macros,
1687 * convert all breakable hyphens into ASCII_HYPH.
1688 */
1689 static void
1690 post_hyph(POST_ARGS)
1691 {
1692 struct roff_node *nch;
1693 char *cp;
1694
1695 for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1696 if (nch->type != ROFFT_TEXT)
1697 continue;
1698 cp = nch->string;
1699 if (*cp == '\0')
1700 continue;
1701 while (*(++cp) != '\0')
1702 if (*cp == '-' &&
1703 isalpha((unsigned char)cp[-1]) &&
1704 isalpha((unsigned char)cp[1]))
1705 *cp = ASCII_HYPH;
1706 }
1707 }
1708
1709 static void
1710 post_ns(POST_ARGS)
1711 {
1712
1713 if (MDOC_LINE & mdoc->last->flags)
1714 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1715 mdoc->last->line, mdoc->last->pos, NULL);
1716 }
1717
1718 static void
1719 post_sh(POST_ARGS)
1720 {
1721
1722 post_ignpar(mdoc);
1723
1724 switch (mdoc->last->type) {
1725 case ROFFT_HEAD:
1726 post_sh_head(mdoc);
1727 break;
1728 case ROFFT_BODY:
1729 switch (mdoc->lastsec) {
1730 case SEC_NAME:
1731 post_sh_name(mdoc);
1732 break;
1733 case SEC_SEE_ALSO:
1734 post_sh_see_also(mdoc);
1735 break;
1736 case SEC_AUTHORS:
1737 post_sh_authors(mdoc);
1738 break;
1739 default:
1740 break;
1741 }
1742 break;
1743 default:
1744 break;
1745 }
1746 }
1747
1748 static void
1749 post_sh_name(POST_ARGS)
1750 {
1751 struct roff_node *n;
1752 int hasnm, hasnd;
1753
1754 hasnm = hasnd = 0;
1755
1756 for (n = mdoc->last->child; n != NULL; n = n->next) {
1757 switch (n->tok) {
1758 case MDOC_Nm:
1759 hasnm = 1;
1760 break;
1761 case MDOC_Nd:
1762 hasnd = 1;
1763 if (n->next != NULL)
1764 mandoc_msg(MANDOCERR_NAMESEC_ND,
1765 mdoc->parse, n->line, n->pos, NULL);
1766 break;
1767 case MDOC_MAX:
1768 if (hasnm)
1769 break;
1770 /* FALLTHROUGH */
1771 default:
1772 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1773 n->line, n->pos, mdoc_macronames[n->tok]);
1774 break;
1775 }
1776 }
1777
1778 if ( ! hasnm)
1779 mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1780 mdoc->last->line, mdoc->last->pos, NULL);
1781 if ( ! hasnd)
1782 mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1783 mdoc->last->line, mdoc->last->pos, NULL);
1784 }
1785
1786 static void
1787 post_sh_see_also(POST_ARGS)
1788 {
1789 const struct roff_node *n;
1790 const char *name, *sec;
1791 const char *lastname, *lastsec, *lastpunct;
1792 int cmp;
1793
1794 n = mdoc->last->child;
1795 lastname = lastsec = lastpunct = NULL;
1796 while (n != NULL) {
1797 if (n->tok != MDOC_Xr || n->nchild < 2)
1798 break;
1799
1800 /* Process one .Xr node. */
1801
1802 name = n->child->string;
1803 sec = n->child->next->string;
1804 if (lastsec != NULL) {
1805 if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1806 mandoc_vmsg(MANDOCERR_XR_PUNCT,
1807 mdoc->parse, n->line, n->pos,
1808 "%s before %s(%s)", lastpunct,
1809 name, sec);
1810 cmp = strcmp(lastsec, sec);
1811 if (cmp > 0)
1812 mandoc_vmsg(MANDOCERR_XR_ORDER,
1813 mdoc->parse, n->line, n->pos,
1814 "%s(%s) after %s(%s)", name,
1815 sec, lastname, lastsec);
1816 else if (cmp == 0 &&
1817 strcasecmp(lastname, name) > 0)
1818 mandoc_vmsg(MANDOCERR_XR_ORDER,
1819 mdoc->parse, n->line, n->pos,
1820 "%s after %s", name, lastname);
1821 }
1822 lastname = name;
1823 lastsec = sec;
1824
1825 /* Process the following node. */
1826
1827 n = n->next;
1828 if (n == NULL)
1829 break;
1830 if (n->tok == MDOC_Xr) {
1831 lastpunct = "none";
1832 continue;
1833 }
1834 if (n->type != ROFFT_TEXT)
1835 break;
1836 for (name = n->string; *name != '\0'; name++)
1837 if (isalpha((const unsigned char)*name))
1838 return;
1839 lastpunct = n->string;
1840 if (n->next == NULL)
1841 mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1842 n->line, n->pos, "%s after %s(%s)",
1843 lastpunct, lastname, lastsec);
1844 n = n->next;
1845 }
1846 }
1847
1848 static int
1849 child_an(const struct roff_node *n)
1850 {
1851
1852 for (n = n->child; n != NULL; n = n->next)
1853 if ((n->tok == MDOC_An && n->nchild) || child_an(n))
1854 return(1);
1855 return(0);
1856 }
1857
1858 static void
1859 post_sh_authors(POST_ARGS)
1860 {
1861
1862 if ( ! child_an(mdoc->last))
1863 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1864 mdoc->last->line, mdoc->last->pos, NULL);
1865 }
1866
1867 static void
1868 post_sh_head(POST_ARGS)
1869 {
1870 struct roff_node *n;
1871 const char *goodsec;
1872 char *secname;
1873 enum roff_sec sec;
1874
1875 /*
1876 * Process a new section. Sections are either "named" or
1877 * "custom". Custom sections are user-defined, while named ones
1878 * follow a conventional order and may only appear in certain
1879 * manual sections.
1880 */
1881
1882 secname = NULL;
1883 sec = SEC_CUSTOM;
1884 mdoc_deroff(&secname, mdoc->last);
1885 sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1886
1887 /* The NAME should be first. */
1888
1889 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1890 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1891 mdoc->last->line, mdoc->last->pos,
1892 "Sh %s", secname);
1893
1894 /* The SYNOPSIS gets special attention in other areas. */
1895
1896 if (SEC_SYNOPSIS == sec) {
1897 roff_setreg(mdoc->roff, "nS", 1, '=');
1898 mdoc->flags |= MDOC_SYNOPSIS;
1899 } else {
1900 roff_setreg(mdoc->roff, "nS", 0, '=');
1901 mdoc->flags &= ~MDOC_SYNOPSIS;
1902 }
1903
1904 /* Mark our last section. */
1905
1906 mdoc->lastsec = sec;
1907
1908 /*
1909 * Set the section attribute for the current HEAD, for its
1910 * parent BLOCK, and for the HEAD children; the latter can
1911 * only be TEXT nodes, so no recursion is needed.
1912 * For other blocks and elements, including .Sh BODY, this is
1913 * done when allocating the node data structures, but for .Sh
1914 * BLOCK and HEAD, the section is still unknown at that time.
1915 */
1916
1917 mdoc->last->parent->sec = sec;
1918 mdoc->last->sec = sec;
1919 for (n = mdoc->last->child; n; n = n->next)
1920 n->sec = sec;
1921
1922 /* We don't care about custom sections after this. */
1923
1924 if (SEC_CUSTOM == sec) {
1925 free(secname);
1926 return;
1927 }
1928
1929 /*
1930 * Check whether our non-custom section is being repeated or is
1931 * out of order.
1932 */
1933
1934 if (sec == mdoc->lastnamed)
1935 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1936 mdoc->last->line, mdoc->last->pos,
1937 "Sh %s", secname);
1938
1939 if (sec < mdoc->lastnamed)
1940 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1941 mdoc->last->line, mdoc->last->pos,
1942 "Sh %s", secname);
1943
1944 /* Mark the last named section. */
1945
1946 mdoc->lastnamed = sec;
1947
1948 /* Check particular section/manual conventions. */
1949
1950 if (mdoc->meta.msec == NULL) {
1951 free(secname);
1952 return;
1953 }
1954
1955 goodsec = NULL;
1956 switch (sec) {
1957 case SEC_ERRORS:
1958 if (*mdoc->meta.msec == '4')
1959 break;
1960 goodsec = "2, 3, 4, 9";
1961 /* FALLTHROUGH */
1962 case SEC_RETURN_VALUES:
1963 /* FALLTHROUGH */
1964 case SEC_LIBRARY:
1965 if (*mdoc->meta.msec == '2')
1966 break;
1967 if (*mdoc->meta.msec == '3')
1968 break;
1969 if (NULL == goodsec)
1970 goodsec = "2, 3, 9";
1971 /* FALLTHROUGH */
1972 case SEC_CONTEXT:
1973 if (*mdoc->meta.msec == '9')
1974 break;
1975 if (NULL == goodsec)
1976 goodsec = "9";
1977 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1978 mdoc->last->line, mdoc->last->pos,
1979 "Sh %s for %s only", secname, goodsec);
1980 break;
1981 default:
1982 break;
1983 }
1984 free(secname);
1985 }
1986
1987 static void
1988 post_ignpar(POST_ARGS)
1989 {
1990 struct roff_node *np;
1991
1992 switch (mdoc->last->type) {
1993 case ROFFT_HEAD:
1994 post_hyph(mdoc);
1995 return;
1996 case ROFFT_BODY:
1997 break;
1998 default:
1999 return;
2000 }
2001
2002 if (NULL != (np = mdoc->last->child))
2003 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2004 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2005 mdoc->parse, np->line, np->pos,
2006 "%s after %s", mdoc_macronames[np->tok],
2007 mdoc_macronames[mdoc->last->tok]);
2008 mdoc_node_delete(mdoc, np);
2009 }
2010
2011 if (NULL != (np = mdoc->last->last))
2012 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2013 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2014 np->line, np->pos, "%s at the end of %s",
2015 mdoc_macronames[np->tok],
2016 mdoc_macronames[mdoc->last->tok]);
2017 mdoc_node_delete(mdoc, np);
2018 }
2019 }
2020
2021 static void
2022 pre_par(PRE_ARGS)
2023 {
2024
2025 if (NULL == mdoc->last)
2026 return;
2027 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2028 return;
2029
2030 /*
2031 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2032 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2033 */
2034
2035 if (MDOC_Pp != mdoc->last->tok &&
2036 MDOC_Lp != mdoc->last->tok &&
2037 MDOC_br != mdoc->last->tok)
2038 return;
2039 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2040 return;
2041 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2042 return;
2043 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2044 return;
2045
2046 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2047 mdoc->last->line, mdoc->last->pos,
2048 "%s before %s", mdoc_macronames[mdoc->last->tok],
2049 mdoc_macronames[n->tok]);
2050 mdoc_node_delete(mdoc, mdoc->last);
2051 }
2052
2053 static void
2054 post_par(POST_ARGS)
2055 {
2056 struct roff_node *np;
2057
2058 np = mdoc->last;
2059
2060 if (np->tok == MDOC_sp) {
2061 if (np->nchild > 1)
2062 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2063 np->child->next->line, np->child->next->pos,
2064 "sp ... %s", np->child->next->string);
2065 } else if (np->child != NULL)
2066 mandoc_vmsg(MANDOCERR_ARG_SKIP,
2067 mdoc->parse, np->line, np->pos, "%s %s",
2068 mdoc_macronames[np->tok], np->child->string);
2069
2070 if (NULL == (np = mdoc->last->prev)) {
2071 np = mdoc->last->parent;
2072 if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2073 return;
2074 } else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2075 (MDOC_br != mdoc->last->tok ||
2076 (MDOC_sp != np->tok && MDOC_br != np->tok)))
2077 return;
2078
2079 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2080 mdoc->last->line, mdoc->last->pos,
2081 "%s after %s", mdoc_macronames[mdoc->last->tok],
2082 mdoc_macronames[np->tok]);
2083 mdoc_node_delete(mdoc, mdoc->last);
2084 }
2085
2086 static void
2087 pre_literal(PRE_ARGS)
2088 {
2089
2090 pre_display(mdoc, n);
2091
2092 if (n->type != ROFFT_BODY)
2093 return;
2094
2095 /*
2096 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2097 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2098 */
2099
2100 switch (n->tok) {
2101 case MDOC_Dl:
2102 mdoc->flags |= MDOC_LITERAL;
2103 break;
2104 case MDOC_Bd:
2105 if (DISP_literal == n->norm->Bd.type)
2106 mdoc->flags |= MDOC_LITERAL;
2107 if (DISP_unfilled == n->norm->Bd.type)
2108 mdoc->flags |= MDOC_LITERAL;
2109 break;
2110 default:
2111 abort();
2112 /* NOTREACHED */
2113 }
2114 }
2115
2116 static void
2117 post_dd(POST_ARGS)
2118 {
2119 struct roff_node *n;
2120 char *datestr;
2121
2122 if (mdoc->meta.date)
2123 free(mdoc->meta.date);
2124
2125 n = mdoc->last;
2126 if (NULL == n->child || '\0' == n->child->string[0]) {
2127 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2128 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2129 goto out;
2130 }
2131
2132 datestr = NULL;
2133 mdoc_deroff(&datestr, n);
2134 if (mdoc->quick)
2135 mdoc->meta.date = datestr;
2136 else {
2137 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2138 datestr, n->line, n->pos);
2139 free(datestr);
2140 }
2141 out:
2142 mdoc_node_delete(mdoc, n);
2143 }
2144
2145 static void
2146 post_dt(POST_ARGS)
2147 {
2148 struct roff_node *nn, *n;
2149 const char *cp;
2150 char *p;
2151
2152 n = mdoc->last;
2153
2154 free(mdoc->meta.title);
2155 free(mdoc->meta.msec);
2156 free(mdoc->meta.vol);
2157 free(mdoc->meta.arch);
2158
2159 mdoc->meta.title = NULL;
2160 mdoc->meta.msec = NULL;
2161 mdoc->meta.vol = NULL;
2162 mdoc->meta.arch = NULL;
2163
2164 /* Mandatory first argument: title. */
2165
2166 nn = n->child;
2167 if (nn == NULL || *nn->string == '\0') {
2168 mandoc_msg(MANDOCERR_DT_NOTITLE,
2169 mdoc->parse, n->line, n->pos, "Dt");
2170 mdoc->meta.title = mandoc_strdup("UNTITLED");
2171 } else {
2172 mdoc->meta.title = mandoc_strdup(nn->string);
2173
2174 /* Check that all characters are uppercase. */
2175
2176 for (p = nn->string; *p != '\0'; p++)
2177 if (islower((unsigned char)*p)) {
2178 mandoc_vmsg(MANDOCERR_TITLE_CASE,
2179 mdoc->parse, nn->line,
2180 nn->pos + (p - nn->string),
2181 "Dt %s", nn->string);
2182 break;
2183 }
2184 }
2185
2186 /* Mandatory second argument: section. */
2187
2188 if (nn != NULL)
2189 nn = nn->next;
2190
2191 if (nn == NULL) {
2192 mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2193 mdoc->parse, n->line, n->pos,
2194 "Dt %s", mdoc->meta.title);
2195 mdoc->meta.vol = mandoc_strdup("LOCAL");
2196 goto out; /* msec and arch remain NULL. */
2197 }
2198
2199 mdoc->meta.msec = mandoc_strdup(nn->string);
2200
2201 /* Infer volume title from section number. */
2202
2203 cp = mandoc_a2msec(nn->string);
2204 if (cp == NULL) {
2205 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2206 nn->line, nn->pos, "Dt ... %s", nn->string);
2207 mdoc->meta.vol = mandoc_strdup(nn->string);
2208 } else
2209 mdoc->meta.vol = mandoc_strdup(cp);
2210
2211 /* Optional third argument: architecture. */
2212
2213 if ((nn = nn->next) == NULL)
2214 goto out;
2215
2216 for (p = nn->string; *p != '\0'; p++)
2217 *p = tolower((unsigned char)*p);
2218 mdoc->meta.arch = mandoc_strdup(nn->string);
2219
2220 /* Ignore fourth and later arguments. */
2221
2222 if ((nn = nn->next) != NULL)
2223 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2224 nn->line, nn->pos, "Dt ... %s", nn->string);
2225
2226 out:
2227 mdoc_node_delete(mdoc, n);
2228 }
2229
2230 static void
2231 post_bx(POST_ARGS)
2232 {
2233 struct roff_node *n;
2234
2235 /*
2236 * Make `Bx's second argument always start with an uppercase
2237 * letter. Groff checks if it's an "accepted" term, but we just
2238 * uppercase blindly.
2239 */
2240
2241 n = mdoc->last->child;
2242 if (n && NULL != (n = n->next))
2243 *n->string = (char)toupper((unsigned char)*n->string);
2244 }
2245
2246 static void
2247 post_os(POST_ARGS)
2248 {
2249 #ifndef OSNAME
2250 struct utsname utsname;
2251 static char *defbuf;
2252 #endif
2253 struct roff_node *n;
2254
2255 n = mdoc->last;
2256
2257 /*
2258 * Set the operating system by way of the `Os' macro.
2259 * The order of precedence is:
2260 * 1. the argument of the `Os' macro, unless empty
2261 * 2. the -Ios=foo command line argument, if provided
2262 * 3. -DOSNAME="\"foo\"", if provided during compilation
2263 * 4. "sysname release" from uname(3)
2264 */
2265
2266 free(mdoc->meta.os);
2267 mdoc->meta.os = NULL;
2268 mdoc_deroff(&mdoc->meta.os, n);
2269 if (mdoc->meta.os)
2270 goto out;
2271
2272 if (mdoc->defos) {
2273 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2274 goto out;
2275 }
2276
2277 #ifdef OSNAME
2278 mdoc->meta.os = mandoc_strdup(OSNAME);
2279 #else /*!OSNAME */
2280 if (NULL == defbuf) {
2281 if (-1 == uname(&utsname)) {
2282 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2283 n->line, n->pos, "Os");
2284 defbuf = mandoc_strdup("UNKNOWN");
2285 } else
2286 mandoc_asprintf(&defbuf, "%s %s",
2287 utsname.sysname, utsname.release);
2288 }
2289 mdoc->meta.os = mandoc_strdup(defbuf);
2290 #endif /*!OSNAME*/
2291
2292 out:
2293 mdoc_node_delete(mdoc, n);
2294 }
2295
2296 /*
2297 * If no argument is provided,
2298 * fill in the name of the current manual page.
2299 */
2300 static void
2301 post_ex(POST_ARGS)
2302 {
2303 struct roff_node *n;
2304
2305 n = mdoc->last;
2306
2307 if (n->child)
2308 return;
2309
2310 if (mdoc->meta.name == NULL) {
2311 mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2312 n->line, n->pos, "Ex");
2313 return;
2314 }
2315
2316 mdoc->next = MDOC_NEXT_CHILD;
2317 mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2318 mdoc->last = n;
2319 }
2320
2321 static enum roff_sec
2322 a2sec(const char *p)
2323 {
2324 int i;
2325
2326 for (i = 0; i < (int)SEC__MAX; i++)
2327 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2328 return((enum roff_sec)i);
2329
2330 return(SEC_CUSTOM);
2331 }
2332
2333 static size_t
2334 macro2len(int macro)
2335 {
2336
2337 switch (macro) {
2338 case MDOC_Ad:
2339 return(12);
2340 case MDOC_Ao:
2341 return(12);
2342 case MDOC_An:
2343 return(12);
2344 case MDOC_Aq:
2345 return(12);
2346 case MDOC_Ar:
2347 return(12);
2348 case MDOC_Bo:
2349 return(12);
2350 case MDOC_Bq:
2351 return(12);
2352 case MDOC_Cd:
2353 return(12);
2354 case MDOC_Cm:
2355 return(10);
2356 case MDOC_Do:
2357 return(10);
2358 case MDOC_Dq:
2359 return(12);
2360 case MDOC_Dv:
2361 return(12);
2362 case MDOC_Eo:
2363 return(12);
2364 case MDOC_Em:
2365 return(10);
2366 case MDOC_Er:
2367 return(17);
2368 case MDOC_Ev:
2369 return(15);
2370 case MDOC_Fa:
2371 return(12);
2372 case MDOC_Fl:
2373 return(10);
2374 case MDOC_Fo:
2375 return(16);
2376 case MDOC_Fn:
2377 return(16);
2378 case MDOC_Ic:
2379 return(10);
2380 case MDOC_Li:
2381 return(16);
2382 case MDOC_Ms:
2383 return(6);
2384 case MDOC_Nm:
2385 return(10);
2386 case MDOC_No:
2387 return(12);
2388 case MDOC_Oo:
2389 return(10);
2390 case MDOC_Op:
2391 return(14);
2392 case MDOC_Pa:
2393 return(32);
2394 case MDOC_Pf:
2395 return(12);
2396 case MDOC_Po:
2397 return(12);
2398 case MDOC_Pq:
2399 return(12);
2400 case MDOC_Ql:
2401 return(16);
2402 case MDOC_Qo:
2403 return(12);
2404 case MDOC_So:
2405 return(12);
2406 case MDOC_Sq:
2407 return(12);
2408 case MDOC_Sy:
2409 return(6);
2410 case MDOC_Sx:
2411 return(16);
2412 case MDOC_Tn:
2413 return(10);
2414 case MDOC_Va:
2415 return(12);
2416 case MDOC_Vt:
2417 return(12);
2418 case MDOC_Xr:
2419 return(10);
2420 default:
2421 break;
2422 };
2423 return(0);
2424 }