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