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