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