libc: remove draft versions of asctime_r and ctime_r
[unleashed.git] / bin / mandoc / mdoc_validate.c
blobe58e7a4721a9a43b290e9c23b9f593f94a643824
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>
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.
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.
19 #include "config.h"
21 #include <sys/types.h>
22 #ifndef OSNAME
23 #include <sys/utsname.h>
24 #endif
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>
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"
42 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
44 #define POST_ARGS struct roff_man *mdoc
46 enum check_ineq {
47 CHECK_LT,
48 CHECK_GT,
49 CHECK_EQ
52 typedef void (*v_post)(POST_ARGS);
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 **);
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);
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 */
237 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
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
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
283 void
284 mdoc_node_validate(struct roff_man *mdoc)
286 struct roff_node *n;
287 v_post *p;
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;
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);
317 * Closing delimiters are not special at the
318 * beginning of a block, opening delimiters
319 * are not special at the end.
322 if (n->child != NULL)
323 n->child->flags &= ~NODE_DELIMC;
324 if (n->last != NULL)
325 n->last->flags &= ~NODE_DELIMO;
327 /* Call the macro's postprocessor. */
329 p = mdoc_valids + n->tok;
330 if (*p)
331 (*p)(mdoc);
332 if (mdoc->last == n)
333 mdoc_state(mdoc, n);
334 break;
338 static void
339 check_args(struct roff_man *mdoc, struct roff_node *n)
341 int i;
343 if (NULL == n->args)
344 return;
346 assert(n->args->argc);
347 for (i = 0; i < (int)n->args->argc; i++)
348 check_argv(mdoc, n, &n->args->argv[i]);
351 static void
352 check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
354 int i;
356 for (i = 0; i < (int)v->sz; i++)
357 check_text(mdoc, v->line, v->pos, v->value[i]);
360 static void
361 check_text(struct roff_man *mdoc, int ln, int pos, char *p)
363 char *cp;
365 if (MDOC_LITERAL & mdoc->flags)
366 return;
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);
373 static void
374 post_bl_norm(POST_ARGS)
376 struct roff_node *n;
377 struct mdoc_argv *argv, *wa;
378 int i;
379 enum mdocargt mdoclt;
380 enum mdoc_list lt;
382 n = mdoc->last->parent;
383 n->norm->Bl.type = LIST__NONE;
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.
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;
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;
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;
474 if (LIST__NONE == lt)
475 continue;
476 mdoclt = argv->arg;
478 /* Check: multiple list types. */
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;
487 /* The list type should come first. */
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]);
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;
503 /* Allow lists to default to LIST_item. */
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;
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.
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;
550 static void
551 post_bd(POST_ARGS)
553 struct roff_node *n;
554 struct mdoc_argv *argv;
555 int i;
556 enum mdoc_disp dt;
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;
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;
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();
608 if (DISP__NONE == dt)
609 continue;
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]);
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;
627 * Stand-alone line macros.
630 static void
631 post_an_norm(POST_ARGS)
633 struct roff_node *n;
634 struct mdoc_argv *argv;
635 size_t i;
637 n = mdoc->last;
638 if (n->args == NULL)
639 return;
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]);
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();
657 static void
658 post_eoln(POST_ARGS)
660 struct roff_node *n;
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);
668 while (n->child != NULL)
669 roff_node_delete(mdoc, n->child);
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;
677 static int
678 build_list(struct roff_man *mdoc, int tok)
680 struct roff_node *n;
681 int ic;
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;
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;
704 static void
705 post_ex(POST_ARGS)
707 struct roff_node *n;
708 int ic;
710 post_std(mdoc);
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;
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;
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;
742 static void
743 post_lb(POST_ARGS)
745 struct roff_node *n;
746 const char *p;
748 n = mdoc->last;
749 assert(n->child->type == ROFFT_TEXT);
750 mdoc->next = ROFF_NEXT_CHILD;
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;
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;
770 static void
771 post_rv(POST_ARGS)
773 struct roff_node *n;
774 int ic;
776 post_std(mdoc);
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;
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;
809 static void
810 post_std(POST_ARGS)
812 struct roff_node *n;
814 n = mdoc->last;
815 if (n->args && n->args->argc == 1)
816 if (n->args->argv[0].arg == MDOC_Std)
817 return;
819 mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
820 n->line, n->pos, mdoc_macronames[n->tok]);
823 static void
824 post_st(POST_ARGS)
826 struct roff_node *n, *nch;
827 const char *p;
829 n = mdoc->last;
830 nch = n->child;
831 assert(nch->type == ROFFT_TEXT);
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;
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;
847 static void
848 post_obsolete(POST_ARGS)
850 struct roff_node *n;
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]);
859 * Block macros.
862 static void
863 post_bf(POST_ARGS)
865 struct roff_node *np, *nch;
868 * Unlike other data pointers, these are "housed" by the HEAD
869 * element, which contains the goods.
872 np = mdoc->last;
873 if (np->type != ROFFT_HEAD)
874 return;
876 assert(np->parent->type == ROFFT_BLOCK);
877 assert(np->parent->tok == MDOC_Bf);
879 /* Check the number of arguments. */
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;
888 nch = nch->next;
890 if (nch != NULL)
891 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
892 nch->line, nch->pos, "Bf ... %s", nch->string);
894 /* Extract argument into data. */
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();
910 return;
913 /* Extract parameter into data. */
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);
927 static void
928 post_fname(POST_ARGS)
930 const struct roff_node *n;
931 const char *cp;
932 size_t pos;
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);
942 static void
943 post_fn(POST_ARGS)
946 post_fname(mdoc);
947 post_fa(mdoc);
950 static void
951 post_fo(POST_ARGS)
953 const struct roff_node *n;
955 n = mdoc->last;
957 if (n->type != ROFFT_HEAD)
958 return;
960 if (n->child == NULL) {
961 mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
962 n->line, n->pos, "Fo");
963 return;
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);
973 post_fname(mdoc);
976 static void
977 post_fa(POST_ARGS)
979 const struct roff_node *n;
980 const char *cp;
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;
997 static void
998 post_nm(POST_ARGS)
1000 struct roff_node *n;
1002 n = mdoc->last;
1004 if (n->last != NULL &&
1005 (n->last->tok == MDOC_Pp ||
1006 n->last->tok == MDOC_Lp))
1007 mdoc_node_relink(mdoc, n->last);
1009 if (mdoc->meta.name == NULL)
1010 deroff(&mdoc->meta.name, n);
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");
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;
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;
1028 static void
1029 post_nd(POST_ARGS)
1031 struct roff_node *n;
1033 n = mdoc->last;
1035 if (n->type != ROFFT_BODY)
1036 return;
1038 if (n->child == NULL)
1039 mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
1040 n->line, n->pos, "Nd");
1042 post_hyph(mdoc);
1045 static void
1046 post_display(POST_ARGS)
1048 struct roff_node *n, *np;
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;
1075 post_bd(mdoc);
1076 post_prevpar(mdoc);
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;
1086 break;
1087 default:
1088 break;
1092 static void
1093 post_defaults(POST_ARGS)
1095 struct roff_node *nn;
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.
1103 if (mdoc->last->child != NULL)
1104 return;
1106 nn = mdoc->last;
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();
1125 mdoc->last = nn;
1128 static void
1129 post_at(POST_ARGS)
1131 struct roff_node *n, *nch;
1132 const char *att;
1134 n = mdoc->last;
1135 nch = n->child;
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.
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);
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;
1158 static void
1159 post_an(POST_ARGS)
1161 struct roff_node *np, *nch;
1163 post_an_norm(mdoc);
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);
1176 static void
1177 post_en(POST_ARGS)
1180 post_obsolete(mdoc);
1181 if (mdoc->last->type == ROFFT_BLOCK)
1182 mdoc->last->norm->Es = mdoc->last_es;
1185 static void
1186 post_es(POST_ARGS)
1189 post_obsolete(mdoc);
1190 mdoc->last_es = mdoc->last;
1193 static void
1194 post_xx(POST_ARGS)
1196 struct roff_node *n;
1197 const char *os;
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();
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;
1228 static void
1229 post_it(POST_ARGS)
1231 struct roff_node *nbl, *nit, *nch;
1232 int i, cols;
1233 enum mdoc_list lt;
1235 post_prevpar(mdoc);
1237 nit = mdoc->last;
1238 if (nit->type != ROFFT_BLOCK)
1239 return;
1241 nbl = nit->parent->parent;
1242 lt = nbl->norm->Bl.type;
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;
1276 assert(nit->head->child == NULL);
1278 i = 0;
1279 for (nch = nit->child; nch != NULL; nch = nch->next)
1280 if (nch->type == ROFFT_BODY)
1281 i++;
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();
1293 static void
1294 post_bl_block(POST_ARGS)
1296 struct roff_node *n, *ni, *nc;
1298 post_prevpar(mdoc);
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;
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;
1335 * If the argument of -offset or -width is a macro,
1336 * replace it with the associated default width.
1338 void
1339 rewrite_macro2len(char **arg)
1341 size_t width;
1342 int tok;
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);
1353 free(*arg);
1354 mandoc_asprintf(arg, "%zun", width);
1357 static void
1358 post_bl_head(POST_ARGS)
1360 struct roff_node *nbl, *nh, *nch, *nnext;
1361 struct mdoc_argv *argv;
1362 int i, j;
1364 post_bl_norm(mdoc);
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;
1376 return;
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.
1385 if (nh->child == NULL)
1386 return;
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;
1393 assert(j < (int)nbl->args->argc);
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.
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 *));
1408 nh->norm->Bl.ncols = argv->sz;
1409 nh->norm->Bl.cols = (void *)argv->value;
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);
1417 nh->child = NULL;
1420 static void
1421 post_bl(POST_ARGS)
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 */
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;
1440 if (nbody->end != ENDBODY_NOT)
1441 return;
1443 nchild = nbody->child;
1444 if (nchild == NULL) {
1445 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1446 nbody->line, nbody->pos, "Bl");
1447 return;
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;
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.
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;
1484 mdoc->last = nbody;
1485 continue;
1488 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1489 nchild->line, nchild->pos,
1490 mdoc_macronames[nchild->tok]);
1493 * Move the node out of the Bl block.
1494 * First, collect all required node pointers.
1497 nblock = nbody->parent;
1498 nprev = nblock->prev;
1499 nparent = nblock->parent;
1502 * Unlink this child.
1505 nbody->child = nnext;
1506 if (nnext == NULL)
1507 nbody->last = NULL;
1508 else
1509 nnext->prev = NULL;
1512 * Relink this child.
1515 nchild->parent = nparent;
1516 nchild->prev = nprev;
1517 nchild->next = nblock;
1519 nblock->prev = nchild;
1520 if (nprev == NULL)
1521 nparent->child = nchild;
1522 else
1523 nprev->next = nchild;
1525 nchild = nnext;
1529 static void
1530 post_bk(POST_ARGS)
1532 struct roff_node *n;
1534 n = mdoc->last;
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);
1543 static void
1544 post_sm(POST_ARGS)
1546 struct roff_node *nch;
1548 nch = mdoc->last->child;
1550 if (nch == NULL) {
1551 mdoc->flags ^= MDOC_SMOFF;
1552 return;
1555 assert(nch->type == ROFFT_TEXT);
1557 if ( ! strcmp(nch->string, "on")) {
1558 mdoc->flags &= ~MDOC_SMOFF;
1559 return;
1561 if ( ! strcmp(nch->string, "off")) {
1562 mdoc->flags |= MDOC_SMOFF;
1563 return;
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;
1573 static void
1574 post_root(POST_ARGS)
1576 struct roff_node *n;
1578 /* Add missing prologue data. */
1580 if (mdoc->meta.date == NULL)
1581 mdoc->meta.date = mdoc->quick ?
1582 mandoc_strdup("") :
1583 mandoc_normdate(mdoc->parse, NULL, 0, 0);
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");
1591 if (mdoc->meta.vol == NULL)
1592 mdoc->meta.vol = mandoc_strdup("LOCAL");
1594 if (mdoc->meta.os == NULL) {
1595 mandoc_msg(MANDOCERR_OS_MISSING,
1596 mdoc->parse, 0, 0, NULL);
1597 mdoc->meta.os = mandoc_strdup("");
1600 /* Check that we begin with a proper `Sh'. */
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;
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]);
1614 static void
1615 post_rs(POST_ARGS)
1617 struct roff_node *np, *nch, *next, *prev;
1618 int i, j;
1620 np = mdoc->last;
1622 if (np->type != ROFFT_BODY)
1623 return;
1625 if (np->child == NULL) {
1626 mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1627 np->line, np->pos, "Rs");
1628 return;
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.
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;
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++;
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.
1659 if ((next = nch->next) != NULL)
1660 next->prev = nch->prev;
1662 if ((prev = nch->prev) != NULL)
1663 prev->next = nch->next;
1665 nch->prev = nch->next = NULL;
1668 * Scan back until we reach a node that's
1669 * to be ordered before this child.
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;
1680 if (j <= i)
1681 break;
1685 * Set this child back into its correct place
1686 * in front of the `prev' node.
1689 nch->prev = prev;
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;
1705 * For some arguments of some macros,
1706 * convert all breakable hyphens into ASCII_HYPH.
1708 static void
1709 post_hyph(POST_ARGS)
1711 struct roff_node *nch;
1712 char *cp;
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;
1728 static void
1729 post_ns(POST_ARGS)
1732 if (mdoc->last->flags & NODE_LINE)
1733 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1734 mdoc->last->line, mdoc->last->pos, NULL);
1737 static void
1738 post_sh(POST_ARGS)
1741 post_ignpar(mdoc);
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;
1761 break;
1762 default:
1763 break;
1767 static void
1768 post_sh_name(POST_ARGS)
1770 struct roff_node *n;
1771 int hasnm, hasnd;
1773 hasnm = hasnd = 0;
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;
1797 /* FALLTHROUGH */
1798 default:
1799 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1800 n->line, n->pos, mdoc_macronames[n->tok]);
1801 continue;
1803 break;
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);
1814 static void
1815 post_sh_see_also(POST_ARGS)
1817 const struct roff_node *n;
1818 const char *name, *sec;
1819 const char *lastname, *lastsec, *lastpunct;
1820 int cmp;
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;
1830 /* Process one .Xr node. */
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);
1852 lastname = name;
1853 lastsec = sec;
1855 /* Process the following node. */
1857 n = n->next;
1858 if (n == NULL)
1859 break;
1860 if (n->tok == MDOC_Xr) {
1861 lastpunct = "none";
1862 continue;
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;
1878 static int
1879 child_an(const struct roff_node *n)
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;
1888 static void
1889 post_sh_authors(POST_ARGS)
1892 if ( ! child_an(mdoc->last))
1893 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1894 mdoc->last->line, mdoc->last->pos, NULL);
1897 static void
1898 post_sh_head(POST_ARGS)
1900 struct roff_node *nch;
1901 const char *goodsec;
1902 enum roff_sec sec;
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.
1911 sec = mdoc->last->sec;
1913 /* The NAME should be first. */
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]);
1923 /* The SYNOPSIS gets special attention in other areas. */
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;
1933 /* Mark our last section. */
1935 mdoc->lastsec = sec;
1937 /* We don't care about custom sections after this. */
1939 if (sec == SEC_CUSTOM)
1940 return;
1943 * Check whether our non-custom section is being repeated or is
1944 * out of order.
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]);
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]);
1957 /* Mark the last named section. */
1959 mdoc->lastnamed = sec;
1961 /* Check particular section/manual conventions. */
1963 if (mdoc->meta.msec == NULL)
1964 return;
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;
1996 static void
1997 post_xr(POST_ARGS)
1999 struct roff_node *n, *nch;
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;
2008 assert(nch->next == n->last);
2011 static void
2012 post_ignpar(POST_ARGS)
2014 struct roff_node *np;
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;
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);
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);
2045 static void
2046 post_prevpar(POST_ARGS)
2048 struct roff_node *n;
2050 n = mdoc->last;
2051 if (NULL == n->prev)
2052 return;
2053 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2054 return;
2057 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2058 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
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;
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);
2079 static void
2080 post_par(POST_ARGS)
2082 struct roff_node *np;
2084 np = mdoc->last;
2085 if (np->tok != MDOC_br && np->tok != MDOC_sp)
2086 post_prevpar(mdoc);
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);
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;
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);
2114 static void
2115 post_dd(POST_ARGS)
2117 struct roff_node *n;
2118 char *datestr;
2120 n = mdoc->last;
2121 n->flags |= NODE_NOPRT;
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");
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;
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);
2154 static void
2155 post_dt(POST_ARGS)
2157 struct roff_node *nn, *n;
2158 const char *cp;
2159 char *p;
2161 n = mdoc->last;
2162 n->flags |= NODE_NOPRT;
2164 if (mdoc->flags & MDOC_PBODY) {
2165 mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2166 n->line, n->pos, "Dt");
2167 return;
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");
2177 free(mdoc->meta.title);
2178 free(mdoc->meta.msec);
2179 free(mdoc->meta.vol);
2180 free(mdoc->meta.arch);
2182 mdoc->meta.title = NULL;
2183 mdoc->meta.msec = NULL;
2184 mdoc->meta.vol = NULL;
2185 mdoc->meta.arch = NULL;
2187 /* Mandatory first argument: title. */
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);
2197 /* Check that all characters are uppercase. */
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;
2209 /* Mandatory second argument: section. */
2211 if (nn != NULL)
2212 nn = nn->next;
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. */
2222 mdoc->meta.msec = mandoc_strdup(nn->string);
2224 /* Infer volume title from section number. */
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);
2234 /* Optional third argument: architecture. */
2236 if ((nn = nn->next) == NULL)
2237 return;
2239 for (p = nn->string; *p != '\0'; p++)
2240 *p = tolower((unsigned char)*p);
2241 mdoc->meta.arch = mandoc_strdup(nn->string);
2243 /* Ignore fourth and later arguments. */
2245 if ((nn = nn->next) != NULL)
2246 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2247 nn->line, nn->pos, "Dt ... %s", nn->string);
2250 static void
2251 post_bx(POST_ARGS)
2253 struct roff_node *n, *nch;
2255 n = mdoc->last;
2256 nch = n->child;
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;
2270 if (nch == NULL) {
2271 mdoc->last = n;
2272 return;
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;
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.
2290 *nch->string = (char)toupper((unsigned char)*nch->string);
2293 static void
2294 post_os(POST_ARGS)
2296 #ifndef OSNAME
2297 struct utsname utsname;
2298 static char *defbuf;
2299 #endif
2300 struct roff_node *n;
2302 n = mdoc->last;
2303 n->flags |= NODE_NOPRT;
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");
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)
2321 free(mdoc->meta.os);
2322 mdoc->meta.os = NULL;
2323 deroff(&mdoc->meta.os, n);
2324 if (mdoc->meta.os)
2325 return;
2327 if (mdoc->defos) {
2328 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2329 return;
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);
2344 mdoc->meta.os = mandoc_strdup(defbuf);
2345 #endif /*!OSNAME*/
2348 enum roff_sec
2349 mdoc_a2sec(const char *p)
2351 int i;
2353 for (i = 0; i < (int)SEC__MAX; i++)
2354 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2355 return (enum roff_sec)i;
2357 return SEC_CUSTOM;
2360 static size_t
2361 macro2len(int macro)
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;
2450 return 0;