]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
Unify some node handling functions that use TOKEN_NONE.
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.289 2015/04/19 14:25:41 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 mdoc_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 && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1566 n = n->next;
1567
1568 if (n == NULL)
1569 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1570 else if (n->tok != MDOC_Sh)
1571 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1572 n->line, n->pos, mdoc_macronames[n->tok]);
1573 }
1574
1575 static void
1576 post_st(POST_ARGS)
1577 {
1578 struct roff_node *n, *nch;
1579 const char *p;
1580
1581 n = mdoc->last;
1582 nch = n->child;
1583
1584 assert(nch->type == ROFFT_TEXT);
1585
1586 if (NULL == (p = mdoc_a2st(nch->string))) {
1587 mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1588 nch->line, nch->pos, "St %s", nch->string);
1589 roff_node_delete(mdoc, n);
1590 } else {
1591 free(nch->string);
1592 nch->string = mandoc_strdup(p);
1593 }
1594 }
1595
1596 static void
1597 post_rs(POST_ARGS)
1598 {
1599 struct roff_node *np, *nch, *next, *prev;
1600 int i, j;
1601
1602 np = mdoc->last;
1603
1604 if (np->type != ROFFT_BODY)
1605 return;
1606
1607 if (np->child == NULL) {
1608 mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1609 np->line, np->pos, "Rs");
1610 return;
1611 }
1612
1613 /*
1614 * The full `Rs' block needs special handling to order the
1615 * sub-elements according to `rsord'. Pick through each element
1616 * and correctly order it. This is an insertion sort.
1617 */
1618
1619 next = NULL;
1620 for (nch = np->child->next; nch != NULL; nch = next) {
1621 /* Determine order number of this child. */
1622 for (i = 0; i < RSORD_MAX; i++)
1623 if (rsord[i] == nch->tok)
1624 break;
1625
1626 if (i == RSORD_MAX) {
1627 mandoc_msg(MANDOCERR_RS_BAD,
1628 mdoc->parse, nch->line, nch->pos,
1629 mdoc_macronames[nch->tok]);
1630 i = -1;
1631 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1632 np->norm->Rs.quote_T++;
1633
1634 /*
1635 * Remove this child from the chain. This somewhat
1636 * repeats roff_node_unlink(), but since we're
1637 * just re-ordering, there's no need for the
1638 * full unlink process.
1639 */
1640
1641 if ((next = nch->next) != NULL)
1642 next->prev = nch->prev;
1643
1644 if ((prev = nch->prev) != NULL)
1645 prev->next = nch->next;
1646
1647 nch->prev = nch->next = NULL;
1648
1649 /*
1650 * Scan back until we reach a node that's
1651 * to be ordered before this child.
1652 */
1653
1654 for ( ; prev ; prev = prev->prev) {
1655 /* Determine order of `prev'. */
1656 for (j = 0; j < RSORD_MAX; j++)
1657 if (rsord[j] == prev->tok)
1658 break;
1659 if (j == RSORD_MAX)
1660 j = -1;
1661
1662 if (j <= i)
1663 break;
1664 }
1665
1666 /*
1667 * Set this child back into its correct place
1668 * in front of the `prev' node.
1669 */
1670
1671 nch->prev = prev;
1672
1673 if (prev == NULL) {
1674 np->child->prev = nch;
1675 nch->next = np->child;
1676 np->child = nch;
1677 } else {
1678 if (prev->next)
1679 prev->next->prev = nch;
1680 nch->next = prev->next;
1681 prev->next = nch;
1682 }
1683 }
1684 }
1685
1686 /*
1687 * For some arguments of some macros,
1688 * convert all breakable hyphens into ASCII_HYPH.
1689 */
1690 static void
1691 post_hyph(POST_ARGS)
1692 {
1693 struct roff_node *nch;
1694 char *cp;
1695
1696 for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1697 if (nch->type != ROFFT_TEXT)
1698 continue;
1699 cp = nch->string;
1700 if (*cp == '\0')
1701 continue;
1702 while (*(++cp) != '\0')
1703 if (*cp == '-' &&
1704 isalpha((unsigned char)cp[-1]) &&
1705 isalpha((unsigned char)cp[1]))
1706 *cp = ASCII_HYPH;
1707 }
1708 }
1709
1710 static void
1711 post_ns(POST_ARGS)
1712 {
1713
1714 if (MDOC_LINE & mdoc->last->flags)
1715 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1716 mdoc->last->line, mdoc->last->pos, NULL);
1717 }
1718
1719 static void
1720 post_sh(POST_ARGS)
1721 {
1722
1723 post_ignpar(mdoc);
1724
1725 switch (mdoc->last->type) {
1726 case ROFFT_HEAD:
1727 post_sh_head(mdoc);
1728 break;
1729 case ROFFT_BODY:
1730 switch (mdoc->lastsec) {
1731 case SEC_NAME:
1732 post_sh_name(mdoc);
1733 break;
1734 case SEC_SEE_ALSO:
1735 post_sh_see_also(mdoc);
1736 break;
1737 case SEC_AUTHORS:
1738 post_sh_authors(mdoc);
1739 break;
1740 default:
1741 break;
1742 }
1743 break;
1744 default:
1745 break;
1746 }
1747 }
1748
1749 static void
1750 post_sh_name(POST_ARGS)
1751 {
1752 struct roff_node *n;
1753 int hasnm, hasnd;
1754
1755 hasnm = hasnd = 0;
1756
1757 for (n = mdoc->last->child; n != NULL; n = n->next) {
1758 switch (n->tok) {
1759 case MDOC_Nm:
1760 hasnm = 1;
1761 break;
1762 case MDOC_Nd:
1763 hasnd = 1;
1764 if (n->next != NULL)
1765 mandoc_msg(MANDOCERR_NAMESEC_ND,
1766 mdoc->parse, n->line, n->pos, NULL);
1767 break;
1768 case TOKEN_NONE:
1769 if (hasnm)
1770 break;
1771 /* FALLTHROUGH */
1772 default:
1773 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1774 n->line, n->pos, mdoc_macronames[n->tok]);
1775 break;
1776 }
1777 }
1778
1779 if ( ! hasnm)
1780 mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1781 mdoc->last->line, mdoc->last->pos, NULL);
1782 if ( ! hasnd)
1783 mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1784 mdoc->last->line, mdoc->last->pos, NULL);
1785 }
1786
1787 static void
1788 post_sh_see_also(POST_ARGS)
1789 {
1790 const struct roff_node *n;
1791 const char *name, *sec;
1792 const char *lastname, *lastsec, *lastpunct;
1793 int cmp;
1794
1795 n = mdoc->last->child;
1796 lastname = lastsec = lastpunct = NULL;
1797 while (n != NULL) {
1798 if (n->tok != MDOC_Xr || n->nchild < 2)
1799 break;
1800
1801 /* Process one .Xr node. */
1802
1803 name = n->child->string;
1804 sec = n->child->next->string;
1805 if (lastsec != NULL) {
1806 if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1807 mandoc_vmsg(MANDOCERR_XR_PUNCT,
1808 mdoc->parse, n->line, n->pos,
1809 "%s before %s(%s)", lastpunct,
1810 name, sec);
1811 cmp = strcmp(lastsec, sec);
1812 if (cmp > 0)
1813 mandoc_vmsg(MANDOCERR_XR_ORDER,
1814 mdoc->parse, n->line, n->pos,
1815 "%s(%s) after %s(%s)", name,
1816 sec, lastname, lastsec);
1817 else if (cmp == 0 &&
1818 strcasecmp(lastname, name) > 0)
1819 mandoc_vmsg(MANDOCERR_XR_ORDER,
1820 mdoc->parse, n->line, n->pos,
1821 "%s after %s", name, lastname);
1822 }
1823 lastname = name;
1824 lastsec = sec;
1825
1826 /* Process the following node. */
1827
1828 n = n->next;
1829 if (n == NULL)
1830 break;
1831 if (n->tok == MDOC_Xr) {
1832 lastpunct = "none";
1833 continue;
1834 }
1835 if (n->type != ROFFT_TEXT)
1836 break;
1837 for (name = n->string; *name != '\0'; name++)
1838 if (isalpha((const unsigned char)*name))
1839 return;
1840 lastpunct = n->string;
1841 if (n->next == NULL)
1842 mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1843 n->line, n->pos, "%s after %s(%s)",
1844 lastpunct, lastname, lastsec);
1845 n = n->next;
1846 }
1847 }
1848
1849 static int
1850 child_an(const struct roff_node *n)
1851 {
1852
1853 for (n = n->child; n != NULL; n = n->next)
1854 if ((n->tok == MDOC_An && n->nchild) || child_an(n))
1855 return(1);
1856 return(0);
1857 }
1858
1859 static void
1860 post_sh_authors(POST_ARGS)
1861 {
1862
1863 if ( ! child_an(mdoc->last))
1864 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1865 mdoc->last->line, mdoc->last->pos, NULL);
1866 }
1867
1868 static void
1869 post_sh_head(POST_ARGS)
1870 {
1871 struct roff_node *n;
1872 const char *goodsec;
1873 char *secname;
1874 enum roff_sec sec;
1875
1876 /*
1877 * Process a new section. Sections are either "named" or
1878 * "custom". Custom sections are user-defined, while named ones
1879 * follow a conventional order and may only appear in certain
1880 * manual sections.
1881 */
1882
1883 secname = NULL;
1884 sec = SEC_CUSTOM;
1885 mdoc_deroff(&secname, mdoc->last);
1886 sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1887
1888 /* The NAME should be first. */
1889
1890 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1891 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1892 mdoc->last->line, mdoc->last->pos,
1893 "Sh %s", secname);
1894
1895 /* The SYNOPSIS gets special attention in other areas. */
1896
1897 if (SEC_SYNOPSIS == sec) {
1898 roff_setreg(mdoc->roff, "nS", 1, '=');
1899 mdoc->flags |= MDOC_SYNOPSIS;
1900 } else {
1901 roff_setreg(mdoc->roff, "nS", 0, '=');
1902 mdoc->flags &= ~MDOC_SYNOPSIS;
1903 }
1904
1905 /* Mark our last section. */
1906
1907 mdoc->lastsec = sec;
1908
1909 /*
1910 * Set the section attribute for the current HEAD, for its
1911 * parent BLOCK, and for the HEAD children; the latter can
1912 * only be TEXT nodes, so no recursion is needed.
1913 * For other blocks and elements, including .Sh BODY, this is
1914 * done when allocating the node data structures, but for .Sh
1915 * BLOCK and HEAD, the section is still unknown at that time.
1916 */
1917
1918 mdoc->last->parent->sec = sec;
1919 mdoc->last->sec = sec;
1920 for (n = mdoc->last->child; n; n = n->next)
1921 n->sec = sec;
1922
1923 /* We don't care about custom sections after this. */
1924
1925 if (SEC_CUSTOM == sec) {
1926 free(secname);
1927 return;
1928 }
1929
1930 /*
1931 * Check whether our non-custom section is being repeated or is
1932 * out of order.
1933 */
1934
1935 if (sec == mdoc->lastnamed)
1936 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1937 mdoc->last->line, mdoc->last->pos,
1938 "Sh %s", secname);
1939
1940 if (sec < mdoc->lastnamed)
1941 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1942 mdoc->last->line, mdoc->last->pos,
1943 "Sh %s", secname);
1944
1945 /* Mark the last named section. */
1946
1947 mdoc->lastnamed = sec;
1948
1949 /* Check particular section/manual conventions. */
1950
1951 if (mdoc->meta.msec == NULL) {
1952 free(secname);
1953 return;
1954 }
1955
1956 goodsec = NULL;
1957 switch (sec) {
1958 case SEC_ERRORS:
1959 if (*mdoc->meta.msec == '4')
1960 break;
1961 goodsec = "2, 3, 4, 9";
1962 /* FALLTHROUGH */
1963 case SEC_RETURN_VALUES:
1964 /* FALLTHROUGH */
1965 case SEC_LIBRARY:
1966 if (*mdoc->meta.msec == '2')
1967 break;
1968 if (*mdoc->meta.msec == '3')
1969 break;
1970 if (NULL == goodsec)
1971 goodsec = "2, 3, 9";
1972 /* FALLTHROUGH */
1973 case SEC_CONTEXT:
1974 if (*mdoc->meta.msec == '9')
1975 break;
1976 if (NULL == goodsec)
1977 goodsec = "9";
1978 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1979 mdoc->last->line, mdoc->last->pos,
1980 "Sh %s for %s only", secname, goodsec);
1981 break;
1982 default:
1983 break;
1984 }
1985 free(secname);
1986 }
1987
1988 static void
1989 post_ignpar(POST_ARGS)
1990 {
1991 struct roff_node *np;
1992
1993 switch (mdoc->last->type) {
1994 case ROFFT_HEAD:
1995 post_hyph(mdoc);
1996 return;
1997 case ROFFT_BODY:
1998 break;
1999 default:
2000 return;
2001 }
2002
2003 if (NULL != (np = mdoc->last->child))
2004 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2005 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2006 mdoc->parse, np->line, np->pos,
2007 "%s after %s", mdoc_macronames[np->tok],
2008 mdoc_macronames[mdoc->last->tok]);
2009 roff_node_delete(mdoc, np);
2010 }
2011
2012 if (NULL != (np = mdoc->last->last))
2013 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2014 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2015 np->line, np->pos, "%s at the end of %s",
2016 mdoc_macronames[np->tok],
2017 mdoc_macronames[mdoc->last->tok]);
2018 roff_node_delete(mdoc, np);
2019 }
2020 }
2021
2022 static void
2023 pre_par(PRE_ARGS)
2024 {
2025
2026 if (NULL == mdoc->last)
2027 return;
2028 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2029 return;
2030
2031 /*
2032 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2033 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2034 */
2035
2036 if (MDOC_Pp != mdoc->last->tok &&
2037 MDOC_Lp != mdoc->last->tok &&
2038 MDOC_br != mdoc->last->tok)
2039 return;
2040 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2041 return;
2042 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2043 return;
2044 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2045 return;
2046
2047 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2048 mdoc->last->line, mdoc->last->pos,
2049 "%s before %s", mdoc_macronames[mdoc->last->tok],
2050 mdoc_macronames[n->tok]);
2051 roff_node_delete(mdoc, mdoc->last);
2052 }
2053
2054 static void
2055 post_par(POST_ARGS)
2056 {
2057 struct roff_node *np;
2058
2059 np = mdoc->last;
2060
2061 if (np->tok == MDOC_sp) {
2062 if (np->nchild > 1)
2063 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2064 np->child->next->line, np->child->next->pos,
2065 "sp ... %s", np->child->next->string);
2066 } else if (np->child != NULL)
2067 mandoc_vmsg(MANDOCERR_ARG_SKIP,
2068 mdoc->parse, np->line, np->pos, "%s %s",
2069 mdoc_macronames[np->tok], np->child->string);
2070
2071 if (NULL == (np = mdoc->last->prev)) {
2072 np = mdoc->last->parent;
2073 if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2074 return;
2075 } else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2076 (MDOC_br != mdoc->last->tok ||
2077 (MDOC_sp != np->tok && MDOC_br != np->tok)))
2078 return;
2079
2080 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2081 mdoc->last->line, mdoc->last->pos,
2082 "%s after %s", mdoc_macronames[mdoc->last->tok],
2083 mdoc_macronames[np->tok]);
2084 roff_node_delete(mdoc, mdoc->last);
2085 }
2086
2087 static void
2088 pre_literal(PRE_ARGS)
2089 {
2090
2091 pre_display(mdoc, n);
2092
2093 if (n->type != ROFFT_BODY)
2094 return;
2095
2096 /*
2097 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2098 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2099 */
2100
2101 switch (n->tok) {
2102 case MDOC_Dl:
2103 mdoc->flags |= MDOC_LITERAL;
2104 break;
2105 case MDOC_Bd:
2106 if (DISP_literal == n->norm->Bd.type)
2107 mdoc->flags |= MDOC_LITERAL;
2108 if (DISP_unfilled == n->norm->Bd.type)
2109 mdoc->flags |= MDOC_LITERAL;
2110 break;
2111 default:
2112 abort();
2113 /* NOTREACHED */
2114 }
2115 }
2116
2117 static void
2118 post_dd(POST_ARGS)
2119 {
2120 struct roff_node *n;
2121 char *datestr;
2122
2123 if (mdoc->meta.date)
2124 free(mdoc->meta.date);
2125
2126 n = mdoc->last;
2127 if (NULL == n->child || '\0' == n->child->string[0]) {
2128 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2129 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2130 goto out;
2131 }
2132
2133 datestr = NULL;
2134 mdoc_deroff(&datestr, n);
2135 if (mdoc->quick)
2136 mdoc->meta.date = datestr;
2137 else {
2138 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2139 datestr, n->line, n->pos);
2140 free(datestr);
2141 }
2142 out:
2143 roff_node_delete(mdoc, n);
2144 }
2145
2146 static void
2147 post_dt(POST_ARGS)
2148 {
2149 struct roff_node *nn, *n;
2150 const char *cp;
2151 char *p;
2152
2153 n = mdoc->last;
2154
2155 free(mdoc->meta.title);
2156 free(mdoc->meta.msec);
2157 free(mdoc->meta.vol);
2158 free(mdoc->meta.arch);
2159
2160 mdoc->meta.title = NULL;
2161 mdoc->meta.msec = NULL;
2162 mdoc->meta.vol = NULL;
2163 mdoc->meta.arch = NULL;
2164
2165 /* Mandatory first argument: title. */
2166
2167 nn = n->child;
2168 if (nn == NULL || *nn->string == '\0') {
2169 mandoc_msg(MANDOCERR_DT_NOTITLE,
2170 mdoc->parse, n->line, n->pos, "Dt");
2171 mdoc->meta.title = mandoc_strdup("UNTITLED");
2172 } else {
2173 mdoc->meta.title = mandoc_strdup(nn->string);
2174
2175 /* Check that all characters are uppercase. */
2176
2177 for (p = nn->string; *p != '\0'; p++)
2178 if (islower((unsigned char)*p)) {
2179 mandoc_vmsg(MANDOCERR_TITLE_CASE,
2180 mdoc->parse, nn->line,
2181 nn->pos + (p - nn->string),
2182 "Dt %s", nn->string);
2183 break;
2184 }
2185 }
2186
2187 /* Mandatory second argument: section. */
2188
2189 if (nn != NULL)
2190 nn = nn->next;
2191
2192 if (nn == NULL) {
2193 mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2194 mdoc->parse, n->line, n->pos,
2195 "Dt %s", mdoc->meta.title);
2196 mdoc->meta.vol = mandoc_strdup("LOCAL");
2197 goto out; /* msec and arch remain NULL. */
2198 }
2199
2200 mdoc->meta.msec = mandoc_strdup(nn->string);
2201
2202 /* Infer volume title from section number. */
2203
2204 cp = mandoc_a2msec(nn->string);
2205 if (cp == NULL) {
2206 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2207 nn->line, nn->pos, "Dt ... %s", nn->string);
2208 mdoc->meta.vol = mandoc_strdup(nn->string);
2209 } else
2210 mdoc->meta.vol = mandoc_strdup(cp);
2211
2212 /* Optional third argument: architecture. */
2213
2214 if ((nn = nn->next) == NULL)
2215 goto out;
2216
2217 for (p = nn->string; *p != '\0'; p++)
2218 *p = tolower((unsigned char)*p);
2219 mdoc->meta.arch = mandoc_strdup(nn->string);
2220
2221 /* Ignore fourth and later arguments. */
2222
2223 if ((nn = nn->next) != NULL)
2224 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2225 nn->line, nn->pos, "Dt ... %s", nn->string);
2226
2227 out:
2228 roff_node_delete(mdoc, n);
2229 }
2230
2231 static void
2232 post_bx(POST_ARGS)
2233 {
2234 struct roff_node *n;
2235
2236 /*
2237 * Make `Bx's second argument always start with an uppercase
2238 * letter. Groff checks if it's an "accepted" term, but we just
2239 * uppercase blindly.
2240 */
2241
2242 n = mdoc->last->child;
2243 if (n && NULL != (n = n->next))
2244 *n->string = (char)toupper((unsigned char)*n->string);
2245 }
2246
2247 static void
2248 post_os(POST_ARGS)
2249 {
2250 #ifndef OSNAME
2251 struct utsname utsname;
2252 static char *defbuf;
2253 #endif
2254 struct roff_node *n;
2255
2256 n = mdoc->last;
2257
2258 /*
2259 * Set the operating system by way of the `Os' macro.
2260 * The order of precedence is:
2261 * 1. the argument of the `Os' macro, unless empty
2262 * 2. the -Ios=foo command line argument, if provided
2263 * 3. -DOSNAME="\"foo\"", if provided during compilation
2264 * 4. "sysname release" from uname(3)
2265 */
2266
2267 free(mdoc->meta.os);
2268 mdoc->meta.os = NULL;
2269 mdoc_deroff(&mdoc->meta.os, n);
2270 if (mdoc->meta.os)
2271 goto out;
2272
2273 if (mdoc->defos) {
2274 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2275 goto out;
2276 }
2277
2278 #ifdef OSNAME
2279 mdoc->meta.os = mandoc_strdup(OSNAME);
2280 #else /*!OSNAME */
2281 if (NULL == defbuf) {
2282 if (-1 == uname(&utsname)) {
2283 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2284 n->line, n->pos, "Os");
2285 defbuf = mandoc_strdup("UNKNOWN");
2286 } else
2287 mandoc_asprintf(&defbuf, "%s %s",
2288 utsname.sysname, utsname.release);
2289 }
2290 mdoc->meta.os = mandoc_strdup(defbuf);
2291 #endif /*!OSNAME*/
2292
2293 out:
2294 roff_node_delete(mdoc, n);
2295 }
2296
2297 /*
2298 * If no argument is provided,
2299 * fill in the name of the current manual page.
2300 */
2301 static void
2302 post_ex(POST_ARGS)
2303 {
2304 struct roff_node *n;
2305
2306 n = mdoc->last;
2307
2308 if (n->child)
2309 return;
2310
2311 if (mdoc->meta.name == NULL) {
2312 mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2313 n->line, n->pos, "Ex");
2314 return;
2315 }
2316
2317 mdoc->next = ROFF_NEXT_CHILD;
2318 roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2319 mdoc->last = n;
2320 }
2321
2322 static enum roff_sec
2323 a2sec(const char *p)
2324 {
2325 int i;
2326
2327 for (i = 0; i < (int)SEC__MAX; i++)
2328 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2329 return((enum roff_sec)i);
2330
2331 return(SEC_CUSTOM);
2332 }
2333
2334 static size_t
2335 macro2len(int macro)
2336 {
2337
2338 switch (macro) {
2339 case MDOC_Ad:
2340 return(12);
2341 case MDOC_Ao:
2342 return(12);
2343 case MDOC_An:
2344 return(12);
2345 case MDOC_Aq:
2346 return(12);
2347 case MDOC_Ar:
2348 return(12);
2349 case MDOC_Bo:
2350 return(12);
2351 case MDOC_Bq:
2352 return(12);
2353 case MDOC_Cd:
2354 return(12);
2355 case MDOC_Cm:
2356 return(10);
2357 case MDOC_Do:
2358 return(10);
2359 case MDOC_Dq:
2360 return(12);
2361 case MDOC_Dv:
2362 return(12);
2363 case MDOC_Eo:
2364 return(12);
2365 case MDOC_Em:
2366 return(10);
2367 case MDOC_Er:
2368 return(17);
2369 case MDOC_Ev:
2370 return(15);
2371 case MDOC_Fa:
2372 return(12);
2373 case MDOC_Fl:
2374 return(10);
2375 case MDOC_Fo:
2376 return(16);
2377 case MDOC_Fn:
2378 return(16);
2379 case MDOC_Ic:
2380 return(10);
2381 case MDOC_Li:
2382 return(16);
2383 case MDOC_Ms:
2384 return(6);
2385 case MDOC_Nm:
2386 return(10);
2387 case MDOC_No:
2388 return(12);
2389 case MDOC_Oo:
2390 return(10);
2391 case MDOC_Op:
2392 return(14);
2393 case MDOC_Pa:
2394 return(32);
2395 case MDOC_Pf:
2396 return(12);
2397 case MDOC_Po:
2398 return(12);
2399 case MDOC_Pq:
2400 return(12);
2401 case MDOC_Ql:
2402 return(16);
2403 case MDOC_Qo:
2404 return(12);
2405 case MDOC_So:
2406 return(12);
2407 case MDOC_Sq:
2408 return(12);
2409 case MDOC_Sy:
2410 return(6);
2411 case MDOC_Sx:
2412 return(16);
2413 case MDOC_Tn:
2414 return(10);
2415 case MDOC_Va:
2416 return(12);
2417 case MDOC_Vt:
2418 return(12);
2419 case MDOC_Xr:
2420 return(10);
2421 default:
2422 break;
2423 };
2424 return(0);
2425 }