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