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