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