]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
Delete the undocumented and unimplemented man(1) -i (interactive
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.319 2017/03/06 17:26:04 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->sec != SEC_NAME)
1039 mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse,
1040 n->line, n->pos, "Nd");
1041
1042 if (n->child == NULL)
1043 mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
1044 n->line, n->pos, "Nd");
1045
1046 post_hyph(mdoc);
1047 }
1048
1049 static void
1050 post_display(POST_ARGS)
1051 {
1052 struct roff_node *n, *np;
1053
1054 n = mdoc->last;
1055 switch (n->type) {
1056 case ROFFT_BODY:
1057 if (n->end != ENDBODY_NOT) {
1058 if (n->tok == MDOC_Bd &&
1059 n->body->parent->args == NULL)
1060 roff_node_delete(mdoc, n);
1061 } else if (n->child == NULL)
1062 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1063 n->line, n->pos, mdoc_macronames[n->tok]);
1064 else if (n->tok == MDOC_D1)
1065 post_hyph(mdoc);
1066 break;
1067 case ROFFT_BLOCK:
1068 if (n->tok == MDOC_Bd) {
1069 if (n->args == NULL) {
1070 mandoc_msg(MANDOCERR_BD_NOARG,
1071 mdoc->parse, n->line, n->pos, "Bd");
1072 mdoc->next = ROFF_NEXT_SIBLING;
1073 while (n->body->child != NULL)
1074 mdoc_node_relink(mdoc,
1075 n->body->child);
1076 roff_node_delete(mdoc, n);
1077 break;
1078 }
1079 post_bd(mdoc);
1080 post_prevpar(mdoc);
1081 }
1082 for (np = n->parent; np != NULL; np = np->parent) {
1083 if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1084 mandoc_vmsg(MANDOCERR_BD_NEST,
1085 mdoc->parse, n->line, n->pos,
1086 "%s in Bd", mdoc_macronames[n->tok]);
1087 break;
1088 }
1089 }
1090 break;
1091 default:
1092 break;
1093 }
1094 }
1095
1096 static void
1097 post_defaults(POST_ARGS)
1098 {
1099 struct roff_node *nn;
1100
1101 /*
1102 * The `Ar' defaults to "file ..." if no value is provided as an
1103 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1104 * gets an empty string.
1105 */
1106
1107 if (mdoc->last->child != NULL)
1108 return;
1109
1110 nn = mdoc->last;
1111
1112 switch (nn->tok) {
1113 case MDOC_Ar:
1114 mdoc->next = ROFF_NEXT_CHILD;
1115 roff_word_alloc(mdoc, nn->line, nn->pos, "file");
1116 mdoc->last->flags |= NODE_NOSRC;
1117 roff_word_alloc(mdoc, nn->line, nn->pos, "...");
1118 mdoc->last->flags |= NODE_NOSRC;
1119 break;
1120 case MDOC_Pa:
1121 case MDOC_Mt:
1122 mdoc->next = ROFF_NEXT_CHILD;
1123 roff_word_alloc(mdoc, nn->line, nn->pos, "~");
1124 mdoc->last->flags |= NODE_NOSRC;
1125 break;
1126 default:
1127 abort();
1128 }
1129 mdoc->last = nn;
1130 }
1131
1132 static void
1133 post_at(POST_ARGS)
1134 {
1135 struct roff_node *n, *nch;
1136 const char *att;
1137
1138 n = mdoc->last;
1139 nch = n->child;
1140
1141 /*
1142 * If we have a child, look it up in the standard keys. If a
1143 * key exist, use that instead of the child; if it doesn't,
1144 * prefix "AT&T UNIX " to the existing data.
1145 */
1146
1147 att = NULL;
1148 if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1149 mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1150 nch->line, nch->pos, "At %s", nch->string);
1151
1152 mdoc->next = ROFF_NEXT_CHILD;
1153 if (att != NULL) {
1154 roff_word_alloc(mdoc, nch->line, nch->pos, att);
1155 nch->flags |= NODE_NOPRT;
1156 } else
1157 roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1158 mdoc->last->flags |= NODE_NOSRC;
1159 mdoc->last = n;
1160 }
1161
1162 static void
1163 post_an(POST_ARGS)
1164 {
1165 struct roff_node *np, *nch;
1166
1167 post_an_norm(mdoc);
1168
1169 np = mdoc->last;
1170 nch = np->child;
1171 if (np->norm->An.auth == AUTH__NONE) {
1172 if (nch == NULL)
1173 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1174 np->line, np->pos, "An");
1175 } else if (nch != NULL)
1176 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1177 nch->line, nch->pos, "An ... %s", nch->string);
1178 }
1179
1180 static void
1181 post_en(POST_ARGS)
1182 {
1183
1184 post_obsolete(mdoc);
1185 if (mdoc->last->type == ROFFT_BLOCK)
1186 mdoc->last->norm->Es = mdoc->last_es;
1187 }
1188
1189 static void
1190 post_es(POST_ARGS)
1191 {
1192
1193 post_obsolete(mdoc);
1194 mdoc->last_es = mdoc->last;
1195 }
1196
1197 static void
1198 post_xx(POST_ARGS)
1199 {
1200 struct roff_node *n;
1201 const char *os;
1202
1203 n = mdoc->last;
1204 switch (n->tok) {
1205 case MDOC_Bsx:
1206 os = "BSD/OS";
1207 break;
1208 case MDOC_Dx:
1209 os = "DragonFly";
1210 break;
1211 case MDOC_Fx:
1212 os = "FreeBSD";
1213 break;
1214 case MDOC_Nx:
1215 os = "NetBSD";
1216 break;
1217 case MDOC_Ox:
1218 os = "OpenBSD";
1219 break;
1220 case MDOC_Ux:
1221 os = "UNIX";
1222 break;
1223 default:
1224 abort();
1225 }
1226 mdoc->next = ROFF_NEXT_CHILD;
1227 roff_word_alloc(mdoc, n->line, n->pos, os);
1228 mdoc->last->flags |= NODE_NOSRC;
1229 mdoc->last = n;
1230 }
1231
1232 static void
1233 post_it(POST_ARGS)
1234 {
1235 struct roff_node *nbl, *nit, *nch;
1236 int i, cols;
1237 enum mdoc_list lt;
1238
1239 post_prevpar(mdoc);
1240
1241 nit = mdoc->last;
1242 if (nit->type != ROFFT_BLOCK)
1243 return;
1244
1245 nbl = nit->parent->parent;
1246 lt = nbl->norm->Bl.type;
1247
1248 switch (lt) {
1249 case LIST_tag:
1250 case LIST_hang:
1251 case LIST_ohang:
1252 case LIST_inset:
1253 case LIST_diag:
1254 if (nit->head->child == NULL)
1255 mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1256 mdoc->parse, nit->line, nit->pos,
1257 "Bl -%s It",
1258 mdoc_argnames[nbl->args->argv[0].arg]);
1259 break;
1260 case LIST_bullet:
1261 case LIST_dash:
1262 case LIST_enum:
1263 case LIST_hyphen:
1264 if (nit->body == NULL || nit->body->child == NULL)
1265 mandoc_vmsg(MANDOCERR_IT_NOBODY,
1266 mdoc->parse, nit->line, nit->pos,
1267 "Bl -%s It",
1268 mdoc_argnames[nbl->args->argv[0].arg]);
1269 /* FALLTHROUGH */
1270 case LIST_item:
1271 if ((nch = nit->head->child) != NULL)
1272 mandoc_vmsg(MANDOCERR_ARG_SKIP,
1273 mdoc->parse, nit->line, nit->pos,
1274 "It %s", nch->string == NULL ?
1275 mdoc_macronames[nch->tok] : nch->string);
1276 break;
1277 case LIST_column:
1278 cols = (int)nbl->norm->Bl.ncols;
1279
1280 assert(nit->head->child == NULL);
1281
1282 i = 0;
1283 for (nch = nit->child; nch != NULL; nch = nch->next)
1284 if (nch->type == ROFFT_BODY)
1285 i++;
1286
1287 if (i < cols || i > cols + 1)
1288 mandoc_vmsg(MANDOCERR_BL_COL,
1289 mdoc->parse, nit->line, nit->pos,
1290 "%d columns, %d cells", cols, i);
1291 break;
1292 default:
1293 abort();
1294 }
1295 }
1296
1297 static void
1298 post_bl_block(POST_ARGS)
1299 {
1300 struct roff_node *n, *ni, *nc;
1301
1302 post_prevpar(mdoc);
1303
1304 n = mdoc->last;
1305 for (ni = n->body->child; ni != NULL; ni = ni->next) {
1306 if (ni->body == NULL)
1307 continue;
1308 nc = ni->body->last;
1309 while (nc != NULL) {
1310 switch (nc->tok) {
1311 case MDOC_Pp:
1312 case MDOC_Lp:
1313 case MDOC_br:
1314 break;
1315 default:
1316 nc = NULL;
1317 continue;
1318 }
1319 if (ni->next == NULL) {
1320 mandoc_msg(MANDOCERR_PAR_MOVE,
1321 mdoc->parse, nc->line, nc->pos,
1322 mdoc_macronames[nc->tok]);
1323 mdoc_node_relink(mdoc, nc);
1324 } else if (n->norm->Bl.comp == 0 &&
1325 n->norm->Bl.type != LIST_column) {
1326 mandoc_vmsg(MANDOCERR_PAR_SKIP,
1327 mdoc->parse, nc->line, nc->pos,
1328 "%s before It",
1329 mdoc_macronames[nc->tok]);
1330 roff_node_delete(mdoc, nc);
1331 } else
1332 break;
1333 nc = ni->body->last;
1334 }
1335 }
1336 }
1337
1338 /*
1339 * If the argument of -offset or -width is a macro,
1340 * replace it with the associated default width.
1341 */
1342 void
1343 rewrite_macro2len(char **arg)
1344 {
1345 size_t width;
1346 int tok;
1347
1348 if (*arg == NULL)
1349 return;
1350 else if ( ! strcmp(*arg, "Ds"))
1351 width = 6;
1352 else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE)
1353 return;
1354 else
1355 width = macro2len(tok);
1356
1357 free(*arg);
1358 mandoc_asprintf(arg, "%zun", width);
1359 }
1360
1361 static void
1362 post_bl_head(POST_ARGS)
1363 {
1364 struct roff_node *nbl, *nh, *nch, *nnext;
1365 struct mdoc_argv *argv;
1366 int i, j;
1367
1368 post_bl_norm(mdoc);
1369
1370 nh = mdoc->last;
1371 if (nh->norm->Bl.type != LIST_column) {
1372 if ((nch = nh->child) == NULL)
1373 return;
1374 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1375 nch->line, nch->pos, "Bl ... %s", nch->string);
1376 while (nch != NULL) {
1377 roff_node_delete(mdoc, nch);
1378 nch = nh->child;
1379 }
1380 return;
1381 }
1382
1383 /*
1384 * Append old-style lists, where the column width specifiers
1385 * trail as macro parameters, to the new-style ("normal-form")
1386 * lists where they're argument values following -column.
1387 */
1388
1389 if (nh->child == NULL)
1390 return;
1391
1392 nbl = nh->parent;
1393 for (j = 0; j < (int)nbl->args->argc; j++)
1394 if (nbl->args->argv[j].arg == MDOC_Column)
1395 break;
1396
1397 assert(j < (int)nbl->args->argc);
1398
1399 /*
1400 * Accommodate for new-style groff column syntax. Shuffle the
1401 * child nodes, all of which must be TEXT, as arguments for the
1402 * column field. Then, delete the head children.
1403 */
1404
1405 argv = nbl->args->argv + j;
1406 i = argv->sz;
1407 for (nch = nh->child; nch != NULL; nch = nch->next)
1408 argv->sz++;
1409 argv->value = mandoc_reallocarray(argv->value,
1410 argv->sz, sizeof(char *));
1411
1412 nh->norm->Bl.ncols = argv->sz;
1413 nh->norm->Bl.cols = (void *)argv->value;
1414
1415 for (nch = nh->child; nch != NULL; nch = nnext) {
1416 argv->value[i++] = nch->string;
1417 nch->string = NULL;
1418 nnext = nch->next;
1419 roff_node_delete(NULL, nch);
1420 }
1421 nh->child = NULL;
1422 }
1423
1424 static void
1425 post_bl(POST_ARGS)
1426 {
1427 struct roff_node *nparent, *nprev; /* of the Bl block */
1428 struct roff_node *nblock, *nbody; /* of the Bl */
1429 struct roff_node *nchild, *nnext; /* of the Bl body */
1430
1431 nbody = mdoc->last;
1432 switch (nbody->type) {
1433 case ROFFT_BLOCK:
1434 post_bl_block(mdoc);
1435 return;
1436 case ROFFT_HEAD:
1437 post_bl_head(mdoc);
1438 return;
1439 case ROFFT_BODY:
1440 break;
1441 default:
1442 return;
1443 }
1444 if (nbody->end != ENDBODY_NOT)
1445 return;
1446
1447 nchild = nbody->child;
1448 if (nchild == NULL) {
1449 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1450 nbody->line, nbody->pos, "Bl");
1451 return;
1452 }
1453 while (nchild != NULL) {
1454 nnext = nchild->next;
1455 if (nchild->tok == MDOC_It ||
1456 (nchild->tok == MDOC_Sm &&
1457 nnext != NULL && nnext->tok == MDOC_It)) {
1458 nchild = nnext;
1459 continue;
1460 }
1461
1462 /*
1463 * In .Bl -column, the first rows may be implicit,
1464 * that is, they may not start with .It macros.
1465 * Such rows may be followed by nodes generated on the
1466 * roff level, for example .TS, which cannot be moved
1467 * out of the list. In that case, wrap such roff nodes
1468 * into an implicit row.
1469 */
1470
1471 if (nchild->prev != NULL) {
1472 mdoc->last = nchild;
1473 mdoc->next = ROFF_NEXT_SIBLING;
1474 roff_block_alloc(mdoc, nchild->line,
1475 nchild->pos, MDOC_It);
1476 roff_head_alloc(mdoc, nchild->line,
1477 nchild->pos, MDOC_It);
1478 mdoc->next = ROFF_NEXT_SIBLING;
1479 roff_body_alloc(mdoc, nchild->line,
1480 nchild->pos, MDOC_It);
1481 while (nchild->tok != MDOC_It) {
1482 mdoc_node_relink(mdoc, nchild);
1483 if ((nchild = nnext) == NULL)
1484 break;
1485 nnext = nchild->next;
1486 mdoc->next = ROFF_NEXT_SIBLING;
1487 }
1488 mdoc->last = nbody;
1489 continue;
1490 }
1491
1492 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1493 nchild->line, nchild->pos,
1494 mdoc_macronames[nchild->tok]);
1495
1496 /*
1497 * Move the node out of the Bl block.
1498 * First, collect all required node pointers.
1499 */
1500
1501 nblock = nbody->parent;
1502 nprev = nblock->prev;
1503 nparent = nblock->parent;
1504
1505 /*
1506 * Unlink this child.
1507 */
1508
1509 nbody->child = nnext;
1510 if (nnext == NULL)
1511 nbody->last = NULL;
1512 else
1513 nnext->prev = NULL;
1514
1515 /*
1516 * Relink this child.
1517 */
1518
1519 nchild->parent = nparent;
1520 nchild->prev = nprev;
1521 nchild->next = nblock;
1522
1523 nblock->prev = nchild;
1524 if (nprev == NULL)
1525 nparent->child = nchild;
1526 else
1527 nprev->next = nchild;
1528
1529 nchild = nnext;
1530 }
1531 }
1532
1533 static void
1534 post_bk(POST_ARGS)
1535 {
1536 struct roff_node *n;
1537
1538 n = mdoc->last;
1539
1540 if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1541 mandoc_msg(MANDOCERR_BLK_EMPTY,
1542 mdoc->parse, n->line, n->pos, "Bk");
1543 roff_node_delete(mdoc, n);
1544 }
1545 }
1546
1547 static void
1548 post_sm(POST_ARGS)
1549 {
1550 struct roff_node *nch;
1551
1552 nch = mdoc->last->child;
1553
1554 if (nch == NULL) {
1555 mdoc->flags ^= MDOC_SMOFF;
1556 return;
1557 }
1558
1559 assert(nch->type == ROFFT_TEXT);
1560
1561 if ( ! strcmp(nch->string, "on")) {
1562 mdoc->flags &= ~MDOC_SMOFF;
1563 return;
1564 }
1565 if ( ! strcmp(nch->string, "off")) {
1566 mdoc->flags |= MDOC_SMOFF;
1567 return;
1568 }
1569
1570 mandoc_vmsg(MANDOCERR_SM_BAD,
1571 mdoc->parse, nch->line, nch->pos,
1572 "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1573 mdoc_node_relink(mdoc, nch);
1574 return;
1575 }
1576
1577 static void
1578 post_root(POST_ARGS)
1579 {
1580 struct roff_node *n;
1581
1582 /* Add missing prologue data. */
1583
1584 if (mdoc->meta.date == NULL)
1585 mdoc->meta.date = mdoc->quick ?
1586 mandoc_strdup("") :
1587 mandoc_normdate(mdoc->parse, NULL, 0, 0);
1588
1589 if (mdoc->meta.title == NULL) {
1590 mandoc_msg(MANDOCERR_DT_NOTITLE,
1591 mdoc->parse, 0, 0, "EOF");
1592 mdoc->meta.title = mandoc_strdup("UNTITLED");
1593 }
1594
1595 if (mdoc->meta.vol == NULL)
1596 mdoc->meta.vol = mandoc_strdup("LOCAL");
1597
1598 if (mdoc->meta.os == NULL) {
1599 mandoc_msg(MANDOCERR_OS_MISSING,
1600 mdoc->parse, 0, 0, NULL);
1601 mdoc->meta.os = mandoc_strdup("");
1602 }
1603
1604 /* Check that we begin with a proper `Sh'. */
1605
1606 n = mdoc->first->child;
1607 while (n != NULL && n->tok != TOKEN_NONE &&
1608 mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1609 n = n->next;
1610
1611 if (n == NULL)
1612 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1613 else if (n->tok != MDOC_Sh)
1614 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1615 n->line, n->pos, mdoc_macronames[n->tok]);
1616 }
1617
1618 static void
1619 post_rs(POST_ARGS)
1620 {
1621 struct roff_node *np, *nch, *next, *prev;
1622 int i, j;
1623
1624 np = mdoc->last;
1625
1626 if (np->type != ROFFT_BODY)
1627 return;
1628
1629 if (np->child == NULL) {
1630 mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1631 np->line, np->pos, "Rs");
1632 return;
1633 }
1634
1635 /*
1636 * The full `Rs' block needs special handling to order the
1637 * sub-elements according to `rsord'. Pick through each element
1638 * and correctly order it. This is an insertion sort.
1639 */
1640
1641 next = NULL;
1642 for (nch = np->child->next; nch != NULL; nch = next) {
1643 /* Determine order number of this child. */
1644 for (i = 0; i < RSORD_MAX; i++)
1645 if (rsord[i] == nch->tok)
1646 break;
1647
1648 if (i == RSORD_MAX) {
1649 mandoc_msg(MANDOCERR_RS_BAD,
1650 mdoc->parse, nch->line, nch->pos,
1651 mdoc_macronames[nch->tok]);
1652 i = -1;
1653 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1654 np->norm->Rs.quote_T++;
1655
1656 /*
1657 * Remove this child from the chain. This somewhat
1658 * repeats roff_node_unlink(), but since we're
1659 * just re-ordering, there's no need for the
1660 * full unlink process.
1661 */
1662
1663 if ((next = nch->next) != NULL)
1664 next->prev = nch->prev;
1665
1666 if ((prev = nch->prev) != NULL)
1667 prev->next = nch->next;
1668
1669 nch->prev = nch->next = NULL;
1670
1671 /*
1672 * Scan back until we reach a node that's
1673 * to be ordered before this child.
1674 */
1675
1676 for ( ; prev ; prev = prev->prev) {
1677 /* Determine order of `prev'. */
1678 for (j = 0; j < RSORD_MAX; j++)
1679 if (rsord[j] == prev->tok)
1680 break;
1681 if (j == RSORD_MAX)
1682 j = -1;
1683
1684 if (j <= i)
1685 break;
1686 }
1687
1688 /*
1689 * Set this child back into its correct place
1690 * in front of the `prev' node.
1691 */
1692
1693 nch->prev = prev;
1694
1695 if (prev == NULL) {
1696 np->child->prev = nch;
1697 nch->next = np->child;
1698 np->child = nch;
1699 } else {
1700 if (prev->next)
1701 prev->next->prev = nch;
1702 nch->next = prev->next;
1703 prev->next = nch;
1704 }
1705 }
1706 }
1707
1708 /*
1709 * For some arguments of some macros,
1710 * convert all breakable hyphens into ASCII_HYPH.
1711 */
1712 static void
1713 post_hyph(POST_ARGS)
1714 {
1715 struct roff_node *nch;
1716 char *cp;
1717
1718 for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1719 if (nch->type != ROFFT_TEXT)
1720 continue;
1721 cp = nch->string;
1722 if (*cp == '\0')
1723 continue;
1724 while (*(++cp) != '\0')
1725 if (*cp == '-' &&
1726 isalpha((unsigned char)cp[-1]) &&
1727 isalpha((unsigned char)cp[1]))
1728 *cp = ASCII_HYPH;
1729 }
1730 }
1731
1732 static void
1733 post_ns(POST_ARGS)
1734 {
1735
1736 if (mdoc->last->flags & NODE_LINE)
1737 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1738 mdoc->last->line, mdoc->last->pos, NULL);
1739 }
1740
1741 static void
1742 post_sh(POST_ARGS)
1743 {
1744
1745 post_ignpar(mdoc);
1746
1747 switch (mdoc->last->type) {
1748 case ROFFT_HEAD:
1749 post_sh_head(mdoc);
1750 break;
1751 case ROFFT_BODY:
1752 switch (mdoc->lastsec) {
1753 case SEC_NAME:
1754 post_sh_name(mdoc);
1755 break;
1756 case SEC_SEE_ALSO:
1757 post_sh_see_also(mdoc);
1758 break;
1759 case SEC_AUTHORS:
1760 post_sh_authors(mdoc);
1761 break;
1762 default:
1763 break;
1764 }
1765 break;
1766 default:
1767 break;
1768 }
1769 }
1770
1771 static void
1772 post_sh_name(POST_ARGS)
1773 {
1774 struct roff_node *n;
1775 int hasnm, hasnd;
1776
1777 hasnm = hasnd = 0;
1778
1779 for (n = mdoc->last->child; n != NULL; n = n->next) {
1780 switch (n->tok) {
1781 case MDOC_Nm:
1782 if (hasnm && n->child != NULL)
1783 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
1784 mdoc->parse, n->line, n->pos,
1785 "Nm %s", n->child->string);
1786 hasnm = 1;
1787 continue;
1788 case MDOC_Nd:
1789 hasnd = 1;
1790 if (n->next != NULL)
1791 mandoc_msg(MANDOCERR_NAMESEC_ND,
1792 mdoc->parse, n->line, n->pos, NULL);
1793 break;
1794 case TOKEN_NONE:
1795 if (n->type == ROFFT_TEXT &&
1796 n->string[0] == ',' && n->string[1] == '\0' &&
1797 n->next != NULL && n->next->tok == MDOC_Nm) {
1798 n = n->next;
1799 continue;
1800 }
1801 /* FALLTHROUGH */
1802 default:
1803 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1804 n->line, n->pos, mdoc_macronames[n->tok]);
1805 continue;
1806 }
1807 break;
1808 }
1809
1810 if ( ! hasnm)
1811 mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1812 mdoc->last->line, mdoc->last->pos, NULL);
1813 if ( ! hasnd)
1814 mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1815 mdoc->last->line, mdoc->last->pos, NULL);
1816 }
1817
1818 static void
1819 post_sh_see_also(POST_ARGS)
1820 {
1821 const struct roff_node *n;
1822 const char *name, *sec;
1823 const char *lastname, *lastsec, *lastpunct;
1824 int cmp;
1825
1826 n = mdoc->last->child;
1827 lastname = lastsec = lastpunct = NULL;
1828 while (n != NULL) {
1829 if (n->tok != MDOC_Xr ||
1830 n->child == NULL ||
1831 n->child->next == NULL)
1832 break;
1833
1834 /* Process one .Xr node. */
1835
1836 name = n->child->string;
1837 sec = n->child->next->string;
1838 if (lastsec != NULL) {
1839 if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1840 mandoc_vmsg(MANDOCERR_XR_PUNCT,
1841 mdoc->parse, n->line, n->pos,
1842 "%s before %s(%s)", lastpunct,
1843 name, sec);
1844 cmp = strcmp(lastsec, sec);
1845 if (cmp > 0)
1846 mandoc_vmsg(MANDOCERR_XR_ORDER,
1847 mdoc->parse, n->line, n->pos,
1848 "%s(%s) after %s(%s)", name,
1849 sec, lastname, lastsec);
1850 else if (cmp == 0 &&
1851 strcasecmp(lastname, name) > 0)
1852 mandoc_vmsg(MANDOCERR_XR_ORDER,
1853 mdoc->parse, n->line, n->pos,
1854 "%s after %s", name, lastname);
1855 }
1856 lastname = name;
1857 lastsec = sec;
1858
1859 /* Process the following node. */
1860
1861 n = n->next;
1862 if (n == NULL)
1863 break;
1864 if (n->tok == MDOC_Xr) {
1865 lastpunct = "none";
1866 continue;
1867 }
1868 if (n->type != ROFFT_TEXT)
1869 break;
1870 for (name = n->string; *name != '\0'; name++)
1871 if (isalpha((const unsigned char)*name))
1872 return;
1873 lastpunct = n->string;
1874 if (n->next == NULL)
1875 mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1876 n->line, n->pos, "%s after %s(%s)",
1877 lastpunct, lastname, lastsec);
1878 n = n->next;
1879 }
1880 }
1881
1882 static int
1883 child_an(const struct roff_node *n)
1884 {
1885
1886 for (n = n->child; n != NULL; n = n->next)
1887 if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
1888 return 1;
1889 return 0;
1890 }
1891
1892 static void
1893 post_sh_authors(POST_ARGS)
1894 {
1895
1896 if ( ! child_an(mdoc->last))
1897 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1898 mdoc->last->line, mdoc->last->pos, NULL);
1899 }
1900
1901 static void
1902 post_sh_head(POST_ARGS)
1903 {
1904 struct roff_node *nch;
1905 const char *goodsec;
1906 enum roff_sec sec;
1907
1908 /*
1909 * Process a new section. Sections are either "named" or
1910 * "custom". Custom sections are user-defined, while named ones
1911 * follow a conventional order and may only appear in certain
1912 * manual sections.
1913 */
1914
1915 sec = mdoc->last->sec;
1916
1917 /* The NAME should be first. */
1918
1919 if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
1920 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1921 mdoc->last->line, mdoc->last->pos, "Sh %s",
1922 sec != SEC_CUSTOM ? secnames[sec] :
1923 (nch = mdoc->last->child) == NULL ? "" :
1924 nch->type == ROFFT_TEXT ? nch->string :
1925 mdoc_macronames[nch->tok]);
1926
1927 /* The SYNOPSIS gets special attention in other areas. */
1928
1929 if (sec == SEC_SYNOPSIS) {
1930 roff_setreg(mdoc->roff, "nS", 1, '=');
1931 mdoc->flags |= MDOC_SYNOPSIS;
1932 } else {
1933 roff_setreg(mdoc->roff, "nS", 0, '=');
1934 mdoc->flags &= ~MDOC_SYNOPSIS;
1935 }
1936
1937 /* Mark our last section. */
1938
1939 mdoc->lastsec = sec;
1940
1941 /* We don't care about custom sections after this. */
1942
1943 if (sec == SEC_CUSTOM)
1944 return;
1945
1946 /*
1947 * Check whether our non-custom section is being repeated or is
1948 * out of order.
1949 */
1950
1951 if (sec == mdoc->lastnamed)
1952 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1953 mdoc->last->line, mdoc->last->pos,
1954 "Sh %s", secnames[sec]);
1955
1956 if (sec < mdoc->lastnamed)
1957 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1958 mdoc->last->line, mdoc->last->pos,
1959 "Sh %s", secnames[sec]);
1960
1961 /* Mark the last named section. */
1962
1963 mdoc->lastnamed = sec;
1964
1965 /* Check particular section/manual conventions. */
1966
1967 if (mdoc->meta.msec == NULL)
1968 return;
1969
1970 goodsec = NULL;
1971 switch (sec) {
1972 case SEC_ERRORS:
1973 if (*mdoc->meta.msec == '4')
1974 break;
1975 goodsec = "2, 3, 4, 9";
1976 /* FALLTHROUGH */
1977 case SEC_RETURN_VALUES:
1978 case SEC_LIBRARY:
1979 if (*mdoc->meta.msec == '2')
1980 break;
1981 if (*mdoc->meta.msec == '3')
1982 break;
1983 if (NULL == goodsec)
1984 goodsec = "2, 3, 9";
1985 /* FALLTHROUGH */
1986 case SEC_CONTEXT:
1987 if (*mdoc->meta.msec == '9')
1988 break;
1989 if (NULL == goodsec)
1990 goodsec = "9";
1991 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1992 mdoc->last->line, mdoc->last->pos,
1993 "Sh %s for %s only", secnames[sec], goodsec);
1994 break;
1995 default:
1996 break;
1997 }
1998 }
1999
2000 static void
2001 post_xr(POST_ARGS)
2002 {
2003 struct roff_node *n, *nch;
2004
2005 n = mdoc->last;
2006 nch = n->child;
2007 if (nch->next == NULL) {
2008 mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
2009 n->line, n->pos, "Xr %s", nch->string);
2010 return;
2011 }
2012 assert(nch->next == n->last);
2013 }
2014
2015 static void
2016 post_ignpar(POST_ARGS)
2017 {
2018 struct roff_node *np;
2019
2020 switch (mdoc->last->type) {
2021 case ROFFT_HEAD:
2022 post_hyph(mdoc);
2023 return;
2024 case ROFFT_BODY:
2025 break;
2026 default:
2027 return;
2028 }
2029
2030 if ((np = mdoc->last->child) != NULL)
2031 if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2032 mandoc_vmsg(MANDOCERR_PAR_SKIP,
2033 mdoc->parse, np->line, np->pos,
2034 "%s after %s", mdoc_macronames[np->tok],
2035 mdoc_macronames[mdoc->last->tok]);
2036 roff_node_delete(mdoc, np);
2037 }
2038
2039 if ((np = mdoc->last->last) != NULL)
2040 if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2041 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2042 np->line, np->pos, "%s at the end of %s",
2043 mdoc_macronames[np->tok],
2044 mdoc_macronames[mdoc->last->tok]);
2045 roff_node_delete(mdoc, np);
2046 }
2047 }
2048
2049 static void
2050 post_prevpar(POST_ARGS)
2051 {
2052 struct roff_node *n;
2053
2054 n = mdoc->last;
2055 if (NULL == n->prev)
2056 return;
2057 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2058 return;
2059
2060 /*
2061 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2062 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2063 */
2064
2065 if (n->prev->tok != MDOC_Pp &&
2066 n->prev->tok != MDOC_Lp &&
2067 n->prev->tok != MDOC_br)
2068 return;
2069 if (n->tok == MDOC_Bl && n->norm->Bl.comp)
2070 return;
2071 if (n->tok == MDOC_Bd && n->norm->Bd.comp)
2072 return;
2073 if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
2074 return;
2075
2076 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2077 n->prev->line, n->prev->pos,
2078 "%s before %s", mdoc_macronames[n->prev->tok],
2079 mdoc_macronames[n->tok]);
2080 roff_node_delete(mdoc, n->prev);
2081 }
2082
2083 static void
2084 post_par(POST_ARGS)
2085 {
2086 struct roff_node *np;
2087
2088 np = mdoc->last;
2089 if (np->tok != MDOC_br && np->tok != MDOC_sp)
2090 post_prevpar(mdoc);
2091
2092 if (np->tok == MDOC_sp) {
2093 if (np->child != NULL && np->child->next != NULL)
2094 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2095 np->child->next->line, np->child->next->pos,
2096 "sp ... %s", np->child->next->string);
2097 } else if (np->child != NULL)
2098 mandoc_vmsg(MANDOCERR_ARG_SKIP,
2099 mdoc->parse, np->line, np->pos, "%s %s",
2100 mdoc_macronames[np->tok], np->child->string);
2101
2102 if ((np = mdoc->last->prev) == NULL) {
2103 np = mdoc->last->parent;
2104 if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
2105 return;
2106 } else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
2107 (mdoc->last->tok != MDOC_br ||
2108 (np->tok != MDOC_sp && np->tok != MDOC_br)))
2109 return;
2110
2111 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2112 mdoc->last->line, mdoc->last->pos,
2113 "%s after %s", mdoc_macronames[mdoc->last->tok],
2114 mdoc_macronames[np->tok]);
2115 roff_node_delete(mdoc, mdoc->last);
2116 }
2117
2118 static void
2119 post_dd(POST_ARGS)
2120 {
2121 struct roff_node *n;
2122 char *datestr;
2123
2124 n = mdoc->last;
2125 n->flags |= NODE_NOPRT;
2126
2127 if (mdoc->meta.date != NULL) {
2128 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2129 n->line, n->pos, "Dd");
2130 free(mdoc->meta.date);
2131 } else if (mdoc->flags & MDOC_PBODY)
2132 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2133 n->line, n->pos, "Dd");
2134 else if (mdoc->meta.title != NULL)
2135 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2136 n->line, n->pos, "Dd after Dt");
2137 else if (mdoc->meta.os != NULL)
2138 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2139 n->line, n->pos, "Dd after Os");
2140
2141 if (n->child == NULL || n->child->string[0] == '\0') {
2142 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2143 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2144 return;
2145 }
2146
2147 datestr = NULL;
2148 deroff(&datestr, n);
2149 if (mdoc->quick)
2150 mdoc->meta.date = datestr;
2151 else {
2152 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2153 datestr, n->line, n->pos);
2154 free(datestr);
2155 }
2156 }
2157
2158 static void
2159 post_dt(POST_ARGS)
2160 {
2161 struct roff_node *nn, *n;
2162 const char *cp;
2163 char *p;
2164
2165 n = mdoc->last;
2166 n->flags |= NODE_NOPRT;
2167
2168 if (mdoc->flags & MDOC_PBODY) {
2169 mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2170 n->line, n->pos, "Dt");
2171 return;
2172 }
2173
2174 if (mdoc->meta.title != NULL)
2175 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2176 n->line, n->pos, "Dt");
2177 else if (mdoc->meta.os != NULL)
2178 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2179 n->line, n->pos, "Dt after Os");
2180
2181 free(mdoc->meta.title);
2182 free(mdoc->meta.msec);
2183 free(mdoc->meta.vol);
2184 free(mdoc->meta.arch);
2185
2186 mdoc->meta.title = NULL;
2187 mdoc->meta.msec = NULL;
2188 mdoc->meta.vol = NULL;
2189 mdoc->meta.arch = NULL;
2190
2191 /* Mandatory first argument: title. */
2192
2193 nn = n->child;
2194 if (nn == NULL || *nn->string == '\0') {
2195 mandoc_msg(MANDOCERR_DT_NOTITLE,
2196 mdoc->parse, n->line, n->pos, "Dt");
2197 mdoc->meta.title = mandoc_strdup("UNTITLED");
2198 } else {
2199 mdoc->meta.title = mandoc_strdup(nn->string);
2200
2201 /* Check that all characters are uppercase. */
2202
2203 for (p = nn->string; *p != '\0'; p++)
2204 if (islower((unsigned char)*p)) {
2205 mandoc_vmsg(MANDOCERR_TITLE_CASE,
2206 mdoc->parse, nn->line,
2207 nn->pos + (p - nn->string),
2208 "Dt %s", nn->string);
2209 break;
2210 }
2211 }
2212
2213 /* Mandatory second argument: section. */
2214
2215 if (nn != NULL)
2216 nn = nn->next;
2217
2218 if (nn == NULL) {
2219 mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2220 mdoc->parse, n->line, n->pos,
2221 "Dt %s", mdoc->meta.title);
2222 mdoc->meta.vol = mandoc_strdup("LOCAL");
2223 return; /* msec and arch remain NULL. */
2224 }
2225
2226 mdoc->meta.msec = mandoc_strdup(nn->string);
2227
2228 /* Infer volume title from section number. */
2229
2230 cp = mandoc_a2msec(nn->string);
2231 if (cp == NULL) {
2232 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2233 nn->line, nn->pos, "Dt ... %s", nn->string);
2234 mdoc->meta.vol = mandoc_strdup(nn->string);
2235 } else
2236 mdoc->meta.vol = mandoc_strdup(cp);
2237
2238 /* Optional third argument: architecture. */
2239
2240 if ((nn = nn->next) == NULL)
2241 return;
2242
2243 for (p = nn->string; *p != '\0'; p++)
2244 *p = tolower((unsigned char)*p);
2245 mdoc->meta.arch = mandoc_strdup(nn->string);
2246
2247 /* Ignore fourth and later arguments. */
2248
2249 if ((nn = nn->next) != NULL)
2250 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2251 nn->line, nn->pos, "Dt ... %s", nn->string);
2252 }
2253
2254 static void
2255 post_bx(POST_ARGS)
2256 {
2257 struct roff_node *n, *nch;
2258
2259 n = mdoc->last;
2260 nch = n->child;
2261
2262 if (nch != NULL) {
2263 mdoc->last = nch;
2264 nch = nch->next;
2265 mdoc->next = ROFF_NEXT_SIBLING;
2266 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2267 mdoc->last->flags |= NODE_NOSRC;
2268 mdoc->next = ROFF_NEXT_SIBLING;
2269 } else
2270 mdoc->next = ROFF_NEXT_CHILD;
2271 roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2272 mdoc->last->flags |= NODE_NOSRC;
2273
2274 if (nch == NULL) {
2275 mdoc->last = n;
2276 return;
2277 }
2278
2279 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2280 mdoc->last->flags |= NODE_NOSRC;
2281 mdoc->next = ROFF_NEXT_SIBLING;
2282 roff_word_alloc(mdoc, n->line, n->pos, "-");
2283 mdoc->last->flags |= NODE_NOSRC;
2284 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2285 mdoc->last->flags |= NODE_NOSRC;
2286 mdoc->last = n;
2287
2288 /*
2289 * Make `Bx's second argument always start with an uppercase
2290 * letter. Groff checks if it's an "accepted" term, but we just
2291 * uppercase blindly.
2292 */
2293
2294 *nch->string = (char)toupper((unsigned char)*nch->string);
2295 }
2296
2297 static void
2298 post_os(POST_ARGS)
2299 {
2300 #ifndef OSNAME
2301 struct utsname utsname;
2302 static char *defbuf;
2303 #endif
2304 struct roff_node *n;
2305
2306 n = mdoc->last;
2307 n->flags |= NODE_NOPRT;
2308
2309 if (mdoc->meta.os != NULL)
2310 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2311 n->line, n->pos, "Os");
2312 else if (mdoc->flags & MDOC_PBODY)
2313 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2314 n->line, n->pos, "Os");
2315
2316 /*
2317 * Set the operating system by way of the `Os' macro.
2318 * The order of precedence is:
2319 * 1. the argument of the `Os' macro, unless empty
2320 * 2. the -Ios=foo command line argument, if provided
2321 * 3. -DOSNAME="\"foo\"", if provided during compilation
2322 * 4. "sysname release" from uname(3)
2323 */
2324
2325 free(mdoc->meta.os);
2326 mdoc->meta.os = NULL;
2327 deroff(&mdoc->meta.os, n);
2328 if (mdoc->meta.os)
2329 return;
2330
2331 if (mdoc->defos) {
2332 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2333 return;
2334 }
2335
2336 #ifdef OSNAME
2337 mdoc->meta.os = mandoc_strdup(OSNAME);
2338 #else /*!OSNAME */
2339 if (defbuf == NULL) {
2340 if (uname(&utsname) == -1) {
2341 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2342 n->line, n->pos, "Os");
2343 defbuf = mandoc_strdup("UNKNOWN");
2344 } else
2345 mandoc_asprintf(&defbuf, "%s %s",
2346 utsname.sysname, utsname.release);
2347 }
2348 mdoc->meta.os = mandoc_strdup(defbuf);
2349 #endif /*!OSNAME*/
2350 }
2351
2352 enum roff_sec
2353 mdoc_a2sec(const char *p)
2354 {
2355 int i;
2356
2357 for (i = 0; i < (int)SEC__MAX; i++)
2358 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2359 return (enum roff_sec)i;
2360
2361 return SEC_CUSTOM;
2362 }
2363
2364 static size_t
2365 macro2len(int macro)
2366 {
2367
2368 switch (macro) {
2369 case MDOC_Ad:
2370 return 12;
2371 case MDOC_Ao:
2372 return 12;
2373 case MDOC_An:
2374 return 12;
2375 case MDOC_Aq:
2376 return 12;
2377 case MDOC_Ar:
2378 return 12;
2379 case MDOC_Bo:
2380 return 12;
2381 case MDOC_Bq:
2382 return 12;
2383 case MDOC_Cd:
2384 return 12;
2385 case MDOC_Cm:
2386 return 10;
2387 case MDOC_Do:
2388 return 10;
2389 case MDOC_Dq:
2390 return 12;
2391 case MDOC_Dv:
2392 return 12;
2393 case MDOC_Eo:
2394 return 12;
2395 case MDOC_Em:
2396 return 10;
2397 case MDOC_Er:
2398 return 17;
2399 case MDOC_Ev:
2400 return 15;
2401 case MDOC_Fa:
2402 return 12;
2403 case MDOC_Fl:
2404 return 10;
2405 case MDOC_Fo:
2406 return 16;
2407 case MDOC_Fn:
2408 return 16;
2409 case MDOC_Ic:
2410 return 10;
2411 case MDOC_Li:
2412 return 16;
2413 case MDOC_Ms:
2414 return 6;
2415 case MDOC_Nm:
2416 return 10;
2417 case MDOC_No:
2418 return 12;
2419 case MDOC_Oo:
2420 return 10;
2421 case MDOC_Op:
2422 return 14;
2423 case MDOC_Pa:
2424 return 32;
2425 case MDOC_Pf:
2426 return 12;
2427 case MDOC_Po:
2428 return 12;
2429 case MDOC_Pq:
2430 return 12;
2431 case MDOC_Ql:
2432 return 16;
2433 case MDOC_Qo:
2434 return 12;
2435 case MDOC_So:
2436 return 12;
2437 case MDOC_Sq:
2438 return 12;
2439 case MDOC_Sy:
2440 return 6;
2441 case MDOC_Sx:
2442 return 16;
2443 case MDOC_Tn:
2444 return 10;
2445 case MDOC_Va:
2446 return 12;
2447 case MDOC_Vt:
2448 return 12;
2449 case MDOC_Xr:
2450 return 10;
2451 default:
2452 break;
2453 };
2454 return 0;
2455 }