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