]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
In man(7), when no explicit volume name is given, use the default
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.180 2011/12/02 01:37:14 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011 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 dup = (NULL != n->norm->Bl.width);
665 width = n->args->argv[i].value[0];
666 break;
667 case (MDOC_Offset):
668 /* NB: this can be empty! */
669 if (n->args->argv[i].sz) {
670 offs = n->args->argv[i].value[0];
671 dup = (NULL != n->norm->Bl.offs);
672 break;
673 }
674 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
675 break;
676 default:
677 continue;
678 }
679
680 /* Check: duplicate auxiliary arguments. */
681
682 if (dup)
683 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
684
685 if (comp && ! dup)
686 n->norm->Bl.comp = comp;
687 if (offs && ! dup)
688 n->norm->Bl.offs = offs;
689 if (width && ! dup)
690 n->norm->Bl.width = width;
691
692 /* Check: multiple list types. */
693
694 if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
695 mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
696
697 /* Assign list type. */
698
699 if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
700 n->norm->Bl.type = lt;
701 /* Set column information, too. */
702 if (LIST_column == lt) {
703 n->norm->Bl.ncols =
704 n->args->argv[i].sz;
705 n->norm->Bl.cols = (void *)
706 n->args->argv[i].value;
707 }
708 }
709
710 /* The list type should come first. */
711
712 if (n->norm->Bl.type == LIST__NONE)
713 if (n->norm->Bl.width ||
714 n->norm->Bl.offs ||
715 n->norm->Bl.comp)
716 mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
717
718 continue;
719 }
720
721 /* Allow lists to default to LIST_item. */
722
723 if (LIST__NONE == n->norm->Bl.type) {
724 mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
725 n->norm->Bl.type = LIST_item;
726 }
727
728 /*
729 * Validate the width field. Some list types don't need width
730 * types and should be warned about them. Others should have it
731 * and must also be warned.
732 */
733
734 switch (n->norm->Bl.type) {
735 case (LIST_tag):
736 if (n->norm->Bl.width)
737 break;
738 mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
739 break;
740 case (LIST_column):
741 /* FALLTHROUGH */
742 case (LIST_diag):
743 /* FALLTHROUGH */
744 case (LIST_ohang):
745 /* FALLTHROUGH */
746 case (LIST_inset):
747 /* FALLTHROUGH */
748 case (LIST_item):
749 if (n->norm->Bl.width)
750 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
751 break;
752 default:
753 break;
754 }
755
756 return(1);
757 }
758
759
760 static int
761 pre_bd(PRE_ARGS)
762 {
763 int i, dup, comp;
764 enum mdoc_disp dt;
765 const char *offs;
766 struct mdoc_node *np;
767
768 if (MDOC_BLOCK != n->type) {
769 if (ENDBODY_NOT != n->end) {
770 assert(n->pending);
771 np = n->pending->parent;
772 } else
773 np = n->parent;
774
775 assert(np);
776 assert(MDOC_BLOCK == np->type);
777 assert(MDOC_Bd == np->tok);
778 return(1);
779 }
780
781 /* LINTED */
782 for (i = 0; n->args && i < (int)n->args->argc; i++) {
783 dt = DISP__NONE;
784 dup = comp = 0;
785 offs = NULL;
786
787 switch (n->args->argv[i].arg) {
788 case (MDOC_Centred):
789 dt = DISP_centred;
790 break;
791 case (MDOC_Ragged):
792 dt = DISP_ragged;
793 break;
794 case (MDOC_Unfilled):
795 dt = DISP_unfilled;
796 break;
797 case (MDOC_Filled):
798 dt = DISP_filled;
799 break;
800 case (MDOC_Literal):
801 dt = DISP_literal;
802 break;
803 case (MDOC_File):
804 mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
805 return(0);
806 case (MDOC_Offset):
807 /* NB: this can be empty! */
808 if (n->args->argv[i].sz) {
809 offs = n->args->argv[i].value[0];
810 dup = (NULL != n->norm->Bd.offs);
811 break;
812 }
813 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
814 break;
815 case (MDOC_Compact):
816 comp = 1;
817 dup = n->norm->Bd.comp;
818 break;
819 default:
820 abort();
821 /* NOTREACHED */
822 }
823
824 /* Check whether we have duplicates. */
825
826 if (dup)
827 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
828
829 /* Make our auxiliary assignments. */
830
831 if (offs && ! dup)
832 n->norm->Bd.offs = offs;
833 if (comp && ! dup)
834 n->norm->Bd.comp = comp;
835
836 /* Check whether a type has already been assigned. */
837
838 if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
839 mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
840
841 /* Make our type assignment. */
842
843 if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
844 n->norm->Bd.type = dt;
845 }
846
847 if (DISP__NONE == n->norm->Bd.type) {
848 mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
849 n->norm->Bd.type = DISP_ragged;
850 }
851
852 return(1);
853 }
854
855
856 static int
857 pre_ss(PRE_ARGS)
858 {
859
860 if (MDOC_BLOCK != n->type)
861 return(1);
862 return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
863 }
864
865
866 static int
867 pre_sh(PRE_ARGS)
868 {
869
870 if (MDOC_BLOCK != n->type)
871 return(1);
872
873 roff_regunset(mdoc->roff, REG_nS);
874 return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
875 }
876
877
878 static int
879 pre_it(PRE_ARGS)
880 {
881
882 if (MDOC_BLOCK != n->type)
883 return(1);
884
885 return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
886 }
887
888
889 static int
890 pre_an(PRE_ARGS)
891 {
892 int i;
893
894 if (NULL == n->args)
895 return(1);
896
897 for (i = 1; i < (int)n->args->argc; i++)
898 mdoc_pmsg(mdoc, n->args->argv[i].line,
899 n->args->argv[i].pos, MANDOCERR_IGNARGV);
900
901 if (MDOC_Split == n->args->argv[0].arg)
902 n->norm->An.auth = AUTH_split;
903 else if (MDOC_Nosplit == n->args->argv[0].arg)
904 n->norm->An.auth = AUTH_nosplit;
905 else
906 abort();
907
908 return(1);
909 }
910
911 static int
912 pre_std(PRE_ARGS)
913 {
914
915 if (n->args && 1 == n->args->argc)
916 if (MDOC_Std == n->args->argv[0].arg)
917 return(1);
918
919 mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
920 return(1);
921 }
922
923 static int
924 pre_dt(PRE_ARGS)
925 {
926
927 if (NULL == mdoc->meta.date || mdoc->meta.os)
928 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
929
930 if (mdoc->meta.title)
931 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
932
933 return(1);
934 }
935
936 static int
937 pre_os(PRE_ARGS)
938 {
939
940 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
941 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
942
943 if (mdoc->meta.os)
944 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
945
946 return(1);
947 }
948
949 static int
950 pre_dd(PRE_ARGS)
951 {
952
953 if (mdoc->meta.title || mdoc->meta.os)
954 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
955
956 if (mdoc->meta.date)
957 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
958
959 return(1);
960 }
961
962
963 static int
964 post_bf(POST_ARGS)
965 {
966 struct mdoc_node *np;
967 enum mdocargt arg;
968
969 /*
970 * Unlike other data pointers, these are "housed" by the HEAD
971 * element, which contains the goods.
972 */
973
974 if (MDOC_HEAD != mdoc->last->type) {
975 if (ENDBODY_NOT != mdoc->last->end) {
976 assert(mdoc->last->pending);
977 np = mdoc->last->pending->parent->head;
978 } else if (MDOC_BLOCK != mdoc->last->type) {
979 np = mdoc->last->parent->head;
980 } else
981 np = mdoc->last->head;
982
983 assert(np);
984 assert(MDOC_HEAD == np->type);
985 assert(MDOC_Bf == np->tok);
986 return(1);
987 }
988
989 np = mdoc->last;
990 assert(MDOC_BLOCK == np->parent->type);
991 assert(MDOC_Bf == np->parent->tok);
992
993 /*
994 * Cannot have both argument and parameter.
995 * If neither is specified, let it through with a warning.
996 */
997
998 if (np->parent->args && np->child) {
999 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1000 return(0);
1001 } else if (NULL == np->parent->args && NULL == np->child) {
1002 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1003 return(1);
1004 }
1005
1006 /* Extract argument into data. */
1007
1008 if (np->parent->args) {
1009 arg = np->parent->args->argv[0].arg;
1010 if (MDOC_Emphasis == arg)
1011 np->norm->Bf.font = FONT_Em;
1012 else if (MDOC_Literal == arg)
1013 np->norm->Bf.font = FONT_Li;
1014 else if (MDOC_Symbolic == arg)
1015 np->norm->Bf.font = FONT_Sy;
1016 else
1017 abort();
1018 return(1);
1019 }
1020
1021 /* Extract parameter into data. */
1022
1023 if (0 == strcmp(np->child->string, "Em"))
1024 np->norm->Bf.font = FONT_Em;
1025 else if (0 == strcmp(np->child->string, "Li"))
1026 np->norm->Bf.font = FONT_Li;
1027 else if (0 == strcmp(np->child->string, "Sy"))
1028 np->norm->Bf.font = FONT_Sy;
1029 else
1030 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1031
1032 return(1);
1033 }
1034
1035 static int
1036 post_lb(POST_ARGS)
1037 {
1038 const char *p;
1039 char *buf;
1040 size_t sz;
1041
1042 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1043
1044 assert(mdoc->last->child);
1045 assert(MDOC_TEXT == mdoc->last->child->type);
1046
1047 p = mdoc_a2lib(mdoc->last->child->string);
1048
1049 /* If lookup ok, replace with table value. */
1050
1051 if (p) {
1052 free(mdoc->last->child->string);
1053 mdoc->last->child->string = mandoc_strdup(p);
1054 return(1);
1055 }
1056
1057 /* If not, use "library ``xxxx''. */
1058
1059 sz = strlen(mdoc->last->child->string) +
1060 2 + strlen("\\(lqlibrary\\(rq");
1061 buf = mandoc_malloc(sz);
1062 snprintf(buf, sz, "library \\(lq%s\\(rq",
1063 mdoc->last->child->string);
1064 free(mdoc->last->child->string);
1065 mdoc->last->child->string = buf;
1066 return(1);
1067 }
1068
1069 static int
1070 post_eoln(POST_ARGS)
1071 {
1072
1073 if (mdoc->last->child)
1074 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1075 return(1);
1076 }
1077
1078
1079 static int
1080 post_vt(POST_ARGS)
1081 {
1082 const struct mdoc_node *n;
1083
1084 /*
1085 * The Vt macro comes in both ELEM and BLOCK form, both of which
1086 * have different syntaxes (yet more context-sensitive
1087 * behaviour). ELEM types must have a child, which is already
1088 * guaranteed by the in_line parsing routine; BLOCK types,
1089 * specifically the BODY, should only have TEXT children.
1090 */
1091
1092 if (MDOC_BODY != mdoc->last->type)
1093 return(1);
1094
1095 for (n = mdoc->last->child; n; n = n->next)
1096 if (MDOC_TEXT != n->type)
1097 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1098
1099 return(1);
1100 }
1101
1102
1103 static int
1104 post_nm(POST_ARGS)
1105 {
1106 char buf[BUFSIZ];
1107 int c;
1108
1109 /* If no child specified, make sure we have the meta name. */
1110
1111 if (NULL == mdoc->last->child && NULL == mdoc->meta.name) {
1112 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1113 return(1);
1114 } else if (mdoc->meta.name)
1115 return(1);
1116
1117 /* If no meta name, set it from the child. */
1118
1119 buf[0] = '\0';
1120 if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1121 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1122 return(0);
1123 }
1124
1125 assert(c);
1126 mdoc->meta.name = mandoc_strdup(buf);
1127 return(1);
1128 }
1129
1130 static int
1131 post_literal(POST_ARGS)
1132 {
1133
1134 /*
1135 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1136 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1137 * this in literal mode, but it doesn't hurt to just switch it
1138 * off in general since displays can't be nested.
1139 */
1140
1141 if (MDOC_BODY == mdoc->last->type)
1142 mdoc->flags &= ~MDOC_LITERAL;
1143
1144 return(1);
1145 }
1146
1147 static int
1148 post_defaults(POST_ARGS)
1149 {
1150 struct mdoc_node *nn;
1151
1152 /*
1153 * The `Ar' defaults to "file ..." if no value is provided as an
1154 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1155 * gets an empty string.
1156 */
1157
1158 if (mdoc->last->child)
1159 return(1);
1160
1161 nn = mdoc->last;
1162 mdoc->next = MDOC_NEXT_CHILD;
1163
1164 switch (nn->tok) {
1165 case (MDOC_Ar):
1166 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1167 return(0);
1168 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1169 return(0);
1170 break;
1171 case (MDOC_At):
1172 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1173 return(0);
1174 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1175 return(0);
1176 break;
1177 case (MDOC_Li):
1178 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1179 return(0);
1180 break;
1181 case (MDOC_Pa):
1182 /* FALLTHROUGH */
1183 case (MDOC_Mt):
1184 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1185 return(0);
1186 break;
1187 default:
1188 abort();
1189 /* NOTREACHED */
1190 }
1191
1192 mdoc->last = nn;
1193 return(1);
1194 }
1195
1196 static int
1197 post_at(POST_ARGS)
1198 {
1199 const char *p, *q;
1200 char *buf;
1201 size_t sz;
1202
1203 /*
1204 * If we have a child, look it up in the standard keys. If a
1205 * key exist, use that instead of the child; if it doesn't,
1206 * prefix "AT&T UNIX " to the existing data.
1207 */
1208
1209 if (NULL == mdoc->last->child)
1210 return(1);
1211
1212 assert(MDOC_TEXT == mdoc->last->child->type);
1213 p = mdoc_a2att(mdoc->last->child->string);
1214
1215 if (p) {
1216 free(mdoc->last->child->string);
1217 mdoc->last->child->string = mandoc_strdup(p);
1218 } else {
1219 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1220 p = "AT&T UNIX ";
1221 q = mdoc->last->child->string;
1222 sz = strlen(p) + strlen(q) + 1;
1223 buf = mandoc_malloc(sz);
1224 strlcpy(buf, p, sz);
1225 strlcat(buf, q, sz);
1226 free(mdoc->last->child->string);
1227 mdoc->last->child->string = buf;
1228 }
1229
1230 return(1);
1231 }
1232
1233 static int
1234 post_an(POST_ARGS)
1235 {
1236 struct mdoc_node *np;
1237
1238 np = mdoc->last;
1239 if (AUTH__NONE == np->norm->An.auth) {
1240 if (0 == np->child)
1241 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1242 } else if (np->child)
1243 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1244
1245 return(1);
1246 }
1247
1248
1249 static int
1250 post_it(POST_ARGS)
1251 {
1252 int i, cols;
1253 enum mdoc_list lt;
1254 struct mdoc_node *n, *c;
1255 enum mandocerr er;
1256
1257 if (MDOC_BLOCK != mdoc->last->type)
1258 return(1);
1259
1260 n = mdoc->last->parent->parent;
1261 lt = n->norm->Bl.type;
1262
1263 if (LIST__NONE == lt) {
1264 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1265 return(1);
1266 }
1267
1268 switch (lt) {
1269 case (LIST_tag):
1270 if (mdoc->last->head->child)
1271 break;
1272 /* FIXME: give this a dummy value. */
1273 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1274 break;
1275 case (LIST_hang):
1276 /* FALLTHROUGH */
1277 case (LIST_ohang):
1278 /* FALLTHROUGH */
1279 case (LIST_inset):
1280 /* FALLTHROUGH */
1281 case (LIST_diag):
1282 if (NULL == mdoc->last->head->child)
1283 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1284 break;
1285 case (LIST_bullet):
1286 /* FALLTHROUGH */
1287 case (LIST_dash):
1288 /* FALLTHROUGH */
1289 case (LIST_enum):
1290 /* FALLTHROUGH */
1291 case (LIST_hyphen):
1292 if (NULL == mdoc->last->body->child)
1293 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1294 /* FALLTHROUGH */
1295 case (LIST_item):
1296 if (mdoc->last->head->child)
1297 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1298 break;
1299 case (LIST_column):
1300 cols = (int)n->norm->Bl.ncols;
1301
1302 assert(NULL == mdoc->last->head->child);
1303
1304 if (NULL == mdoc->last->body->child)
1305 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1306
1307 for (i = 0, c = mdoc->last->child; c; c = c->next)
1308 if (MDOC_BODY == c->type)
1309 i++;
1310
1311 if (i < cols)
1312 er = MANDOCERR_ARGCOUNT;
1313 else if (i == cols || i == cols + 1)
1314 break;
1315 else
1316 er = MANDOCERR_SYNTARGCOUNT;
1317
1318 mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1319 mdoc->last->pos,
1320 "columns == %d (have %d)", cols, i);
1321 return(MANDOCERR_ARGCOUNT == er);
1322 default:
1323 break;
1324 }
1325
1326 return(1);
1327 }
1328
1329 static int
1330 post_bl_block(POST_ARGS)
1331 {
1332 struct mdoc_node *n;
1333
1334 /*
1335 * These are fairly complicated, so we've broken them into two
1336 * functions. post_bl_block_tag() is called when a -tag is
1337 * specified, but no -width (it must be guessed). The second
1338 * when a -width is specified (macro indicators must be
1339 * rewritten into real lengths).
1340 */
1341
1342 n = mdoc->last;
1343
1344 if (LIST_tag == n->norm->Bl.type &&
1345 NULL == n->norm->Bl.width) {
1346 if ( ! post_bl_block_tag(mdoc))
1347 return(0);
1348 } else if (NULL != n->norm->Bl.width) {
1349 if ( ! post_bl_block_width(mdoc))
1350 return(0);
1351 } else
1352 return(1);
1353
1354 assert(n->norm->Bl.width);
1355 return(1);
1356 }
1357
1358 static int
1359 post_bl_block_width(POST_ARGS)
1360 {
1361 size_t width;
1362 int i;
1363 enum mdoct tok;
1364 struct mdoc_node *n;
1365 char buf[NUMSIZ];
1366
1367 n = mdoc->last;
1368
1369 /*
1370 * Calculate the real width of a list from the -width string,
1371 * which may contain a macro (with a known default width), a
1372 * literal string, or a scaling width.
1373 *
1374 * If the value to -width is a macro, then we re-write it to be
1375 * the macro's width as set in share/tmac/mdoc/doc-common.
1376 */
1377
1378 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1379 width = 6;
1380 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1381 return(1);
1382 else if (0 == (width = macro2len(tok))) {
1383 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1384 return(1);
1385 }
1386
1387 /* The value already exists: free and reallocate it. */
1388
1389 assert(n->args);
1390
1391 for (i = 0; i < (int)n->args->argc; i++)
1392 if (MDOC_Width == n->args->argv[i].arg)
1393 break;
1394
1395 assert(i < (int)n->args->argc);
1396
1397 snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1398 free(n->args->argv[i].value[0]);
1399 n->args->argv[i].value[0] = mandoc_strdup(buf);
1400
1401 /* Set our width! */
1402 n->norm->Bl.width = n->args->argv[i].value[0];
1403 return(1);
1404 }
1405
1406 static int
1407 post_bl_block_tag(POST_ARGS)
1408 {
1409 struct mdoc_node *n, *nn;
1410 size_t sz, ssz;
1411 int i;
1412 char buf[NUMSIZ];
1413
1414 /*
1415 * Calculate the -width for a `Bl -tag' list if it hasn't been
1416 * provided. Uses the first head macro. NOTE AGAIN: this is
1417 * ONLY if the -width argument has NOT been provided. See
1418 * post_bl_block_width() for converting the -width string.
1419 */
1420
1421 sz = 10;
1422 n = mdoc->last;
1423
1424 for (nn = n->body->child; nn; nn = nn->next) {
1425 if (MDOC_It != nn->tok)
1426 continue;
1427
1428 assert(MDOC_BLOCK == nn->type);
1429 nn = nn->head->child;
1430
1431 if (nn == NULL)
1432 break;
1433
1434 if (MDOC_TEXT == nn->type) {
1435 sz = strlen(nn->string) + 1;
1436 break;
1437 }
1438
1439 if (0 != (ssz = macro2len(nn->tok)))
1440 sz = ssz;
1441
1442 break;
1443 }
1444
1445 /* Defaults to ten ens. */
1446
1447 snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1448
1449 /*
1450 * We have to dynamically add this to the macro's argument list.
1451 * We're guaranteed that a MDOC_Width doesn't already exist.
1452 */
1453
1454 assert(n->args);
1455 i = (int)(n->args->argc)++;
1456
1457 n->args->argv = mandoc_realloc(n->args->argv,
1458 n->args->argc * sizeof(struct mdoc_argv));
1459
1460 n->args->argv[i].arg = MDOC_Width;
1461 n->args->argv[i].line = n->line;
1462 n->args->argv[i].pos = n->pos;
1463 n->args->argv[i].sz = 1;
1464 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1465 n->args->argv[i].value[0] = mandoc_strdup(buf);
1466
1467 /* Set our width! */
1468 n->norm->Bl.width = n->args->argv[i].value[0];
1469 return(1);
1470 }
1471
1472
1473 static int
1474 post_bl_head(POST_ARGS)
1475 {
1476 struct mdoc_node *np, *nn, *nnp;
1477 int i, j;
1478
1479 if (LIST_column != mdoc->last->norm->Bl.type)
1480 /* FIXME: this should be ERROR class... */
1481 return(hwarn_eq0(mdoc));
1482
1483 /*
1484 * Convert old-style lists, where the column width specifiers
1485 * trail as macro parameters, to the new-style ("normal-form")
1486 * lists where they're argument values following -column.
1487 */
1488
1489 /* First, disallow both types and allow normal-form. */
1490
1491 /*
1492 * TODO: technically, we can accept both and just merge the two
1493 * lists, but I'll leave that for another day.
1494 */
1495
1496 if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1497 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1498 return(0);
1499 } else if (NULL == mdoc->last->child)
1500 return(1);
1501
1502 np = mdoc->last->parent;
1503 assert(np->args);
1504
1505 for (j = 0; j < (int)np->args->argc; j++)
1506 if (MDOC_Column == np->args->argv[j].arg)
1507 break;
1508
1509 assert(j < (int)np->args->argc);
1510 assert(0 == np->args->argv[j].sz);
1511
1512 /*
1513 * Accommodate for new-style groff column syntax. Shuffle the
1514 * child nodes, all of which must be TEXT, as arguments for the
1515 * column field. Then, delete the head children.
1516 */
1517
1518 np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1519 np->args->argv[j].value = mandoc_malloc
1520 ((size_t)mdoc->last->nchild * sizeof(char *));
1521
1522 mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1523 mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1524
1525 for (i = 0, nn = mdoc->last->child; nn; i++) {
1526 np->args->argv[j].value[i] = nn->string;
1527 nn->string = NULL;
1528 nnp = nn;
1529 nn = nn->next;
1530 mdoc_node_delete(NULL, nnp);
1531 }
1532
1533 mdoc->last->nchild = 0;
1534 mdoc->last->child = NULL;
1535
1536 return(1);
1537 }
1538
1539 static int
1540 post_bl(POST_ARGS)
1541 {
1542 struct mdoc_node *n;
1543
1544 if (MDOC_HEAD == mdoc->last->type)
1545 return(post_bl_head(mdoc));
1546 if (MDOC_BLOCK == mdoc->last->type)
1547 return(post_bl_block(mdoc));
1548 if (MDOC_BODY != mdoc->last->type)
1549 return(1);
1550
1551 for (n = mdoc->last->child; n; n = n->next) {
1552 switch (n->tok) {
1553 case (MDOC_Lp):
1554 /* FALLTHROUGH */
1555 case (MDOC_Pp):
1556 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1557 /* FALLTHROUGH */
1558 case (MDOC_It):
1559 /* FALLTHROUGH */
1560 case (MDOC_Sm):
1561 continue;
1562 default:
1563 break;
1564 }
1565
1566 mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1567 return(0);
1568 }
1569
1570 return(1);
1571 }
1572
1573 static int
1574 ebool(struct mdoc *mdoc)
1575 {
1576
1577 if (NULL == mdoc->last->child) {
1578 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1579 mdoc_node_delete(mdoc, mdoc->last);
1580 return(1);
1581 }
1582 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1583
1584 assert(MDOC_TEXT == mdoc->last->child->type);
1585
1586 if (0 == strcmp(mdoc->last->child->string, "on"))
1587 return(1);
1588 if (0 == strcmp(mdoc->last->child->string, "off"))
1589 return(1);
1590
1591 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1592 return(1);
1593 }
1594
1595 static int
1596 post_root(POST_ARGS)
1597 {
1598 int erc;
1599 struct mdoc_node *n;
1600
1601 erc = 0;
1602
1603 /* Check that we have a finished prologue. */
1604
1605 if ( ! (MDOC_PBODY & mdoc->flags)) {
1606 erc++;
1607 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1608 }
1609
1610 n = mdoc->first;
1611 assert(n);
1612
1613 /* Check that we begin with a proper `Sh'. */
1614
1615 if (NULL == n->child) {
1616 erc++;
1617 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1618 } else if (MDOC_BLOCK != n->child->type ||
1619 MDOC_Sh != n->child->tok) {
1620 erc++;
1621 /* Can this be lifted? See rxdebug.1 for example. */
1622 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1623 }
1624
1625 return(erc ? 0 : 1);
1626 }
1627
1628 static int
1629 post_st(POST_ARGS)
1630 {
1631 struct mdoc_node *ch;
1632 const char *p;
1633
1634 if (NULL == (ch = mdoc->last->child)) {
1635 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1636 mdoc_node_delete(mdoc, mdoc->last);
1637 return(1);
1638 }
1639
1640 assert(MDOC_TEXT == ch->type);
1641
1642 if (NULL == (p = mdoc_a2st(ch->string))) {
1643 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1644 mdoc_node_delete(mdoc, mdoc->last);
1645 } else {
1646 free(ch->string);
1647 ch->string = mandoc_strdup(p);
1648 }
1649
1650 return(1);
1651 }
1652
1653 static int
1654 post_rs(POST_ARGS)
1655 {
1656 struct mdoc_node *nn, *next, *prev;
1657 int i, j;
1658
1659 switch (mdoc->last->type) {
1660 case (MDOC_HEAD):
1661 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1662 return(1);
1663 case (MDOC_BODY):
1664 if (mdoc->last->child)
1665 break;
1666 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1667 return(1);
1668 default:
1669 return(1);
1670 }
1671
1672 /*
1673 * Make sure only certain types of nodes are allowed within the
1674 * the `Rs' body. Delete offending nodes and raise a warning.
1675 * Do this before re-ordering for the sake of clarity.
1676 */
1677
1678 next = NULL;
1679 for (nn = mdoc->last->child; nn; nn = next) {
1680 for (i = 0; i < RSORD_MAX; i++)
1681 if (nn->tok == rsord[i])
1682 break;
1683
1684 if (i < RSORD_MAX) {
1685 if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1686 mdoc->last->norm->Rs.quote_T++;
1687 next = nn->next;
1688 continue;
1689 }
1690
1691 next = nn->next;
1692 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1693 mdoc_node_delete(mdoc, nn);
1694 }
1695
1696 /*
1697 * Nothing to sort if only invalid nodes were found
1698 * inside the `Rs' body.
1699 */
1700
1701 if (NULL == mdoc->last->child)
1702 return(1);
1703
1704 /*
1705 * The full `Rs' block needs special handling to order the
1706 * sub-elements according to `rsord'. Pick through each element
1707 * and correctly order it. This is a insertion sort.
1708 */
1709
1710 next = NULL;
1711 for (nn = mdoc->last->child->next; nn; nn = next) {
1712 /* Determine order of `nn'. */
1713 for (i = 0; i < RSORD_MAX; i++)
1714 if (rsord[i] == nn->tok)
1715 break;
1716
1717 /*
1718 * Remove `nn' from the chain. This somewhat
1719 * repeats mdoc_node_unlink(), but since we're
1720 * just re-ordering, there's no need for the
1721 * full unlink process.
1722 */
1723
1724 if (NULL != (next = nn->next))
1725 next->prev = nn->prev;
1726
1727 if (NULL != (prev = nn->prev))
1728 prev->next = nn->next;
1729
1730 nn->prev = nn->next = NULL;
1731
1732 /*
1733 * Scan back until we reach a node that's
1734 * ordered before `nn'.
1735 */
1736
1737 for ( ; prev ; prev = prev->prev) {
1738 /* Determine order of `prev'. */
1739 for (j = 0; j < RSORD_MAX; j++)
1740 if (rsord[j] == prev->tok)
1741 break;
1742
1743 if (j <= i)
1744 break;
1745 }
1746
1747 /*
1748 * Set `nn' back into its correct place in front
1749 * of the `prev' node.
1750 */
1751
1752 nn->prev = prev;
1753
1754 if (prev) {
1755 if (prev->next)
1756 prev->next->prev = nn;
1757 nn->next = prev->next;
1758 prev->next = nn;
1759 } else {
1760 mdoc->last->child->prev = nn;
1761 nn->next = mdoc->last->child;
1762 mdoc->last->child = nn;
1763 }
1764 }
1765
1766 return(1);
1767 }
1768
1769 static int
1770 post_ns(POST_ARGS)
1771 {
1772
1773 if (MDOC_LINE & mdoc->last->flags)
1774 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1775 return(1);
1776 }
1777
1778 static int
1779 post_sh(POST_ARGS)
1780 {
1781
1782 if (MDOC_HEAD == mdoc->last->type)
1783 return(post_sh_head(mdoc));
1784 if (MDOC_BODY == mdoc->last->type)
1785 return(post_sh_body(mdoc));
1786
1787 return(1);
1788 }
1789
1790 static int
1791 post_sh_body(POST_ARGS)
1792 {
1793 struct mdoc_node *n;
1794
1795 if (SEC_NAME != mdoc->lastsec)
1796 return(1);
1797
1798 /*
1799 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1800 * macros (can have multiple `Nm' and one `Nd'). Note that the
1801 * children of the BODY declaration can also be "text".
1802 */
1803
1804 if (NULL == (n = mdoc->last->child)) {
1805 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1806 return(1);
1807 }
1808
1809 for ( ; n && n->next; n = n->next) {
1810 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1811 continue;
1812 if (MDOC_TEXT == n->type)
1813 continue;
1814 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1815 }
1816
1817 assert(n);
1818 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1819 return(1);
1820
1821 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1822 return(1);
1823 }
1824
1825 static int
1826 post_sh_head(POST_ARGS)
1827 {
1828 char buf[BUFSIZ];
1829 enum mdoc_sec sec;
1830 int c;
1831
1832 /*
1833 * Process a new section. Sections are either "named" or
1834 * "custom". Custom sections are user-defined, while named ones
1835 * follow a conventional order and may only appear in certain
1836 * manual sections.
1837 */
1838
1839 sec = SEC_CUSTOM;
1840 buf[0] = '\0';
1841 if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1842 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1843 return(0);
1844 } else if (1 == c)
1845 sec = a2sec(buf);
1846
1847 /* The NAME should be first. */
1848
1849 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1850 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1851
1852 /* The SYNOPSIS gets special attention in other areas. */
1853
1854 if (SEC_SYNOPSIS == sec)
1855 mdoc->flags |= MDOC_SYNOPSIS;
1856 else
1857 mdoc->flags &= ~MDOC_SYNOPSIS;
1858
1859 /* Mark our last section. */
1860
1861 mdoc->lastsec = sec;
1862
1863 /* We don't care about custom sections after this. */
1864
1865 if (SEC_CUSTOM == sec)
1866 return(1);
1867
1868 /*
1869 * Check whether our non-custom section is being repeated or is
1870 * out of order.
1871 */
1872
1873 if (sec == mdoc->lastnamed)
1874 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1875
1876 if (sec < mdoc->lastnamed)
1877 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1878
1879 /* Mark the last named section. */
1880
1881 mdoc->lastnamed = sec;
1882
1883 /* Check particular section/manual conventions. */
1884
1885 assert(mdoc->meta.msec);
1886
1887 switch (sec) {
1888 case (SEC_RETURN_VALUES):
1889 /* FALLTHROUGH */
1890 case (SEC_ERRORS):
1891 /* FALLTHROUGH */
1892 case (SEC_LIBRARY):
1893 if (*mdoc->meta.msec == '2')
1894 break;
1895 if (*mdoc->meta.msec == '3')
1896 break;
1897 if (*mdoc->meta.msec == '9')
1898 break;
1899 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1900 break;
1901 default:
1902 break;
1903 }
1904
1905 return(1);
1906 }
1907
1908 static int
1909 post_ignpar(POST_ARGS)
1910 {
1911 struct mdoc_node *np;
1912
1913 if (MDOC_BODY != mdoc->last->type)
1914 return(1);
1915
1916 if (NULL != (np = mdoc->last->child))
1917 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1918 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1919 mdoc_node_delete(mdoc, np);
1920 }
1921
1922 if (NULL != (np = mdoc->last->last))
1923 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1924 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1925 mdoc_node_delete(mdoc, np);
1926 }
1927
1928 return(1);
1929 }
1930
1931 static int
1932 pre_par(PRE_ARGS)
1933 {
1934
1935 if (NULL == mdoc->last)
1936 return(1);
1937 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
1938 return(1);
1939
1940 /*
1941 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1942 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
1943 */
1944
1945 if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
1946 return(1);
1947 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
1948 return(1);
1949 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
1950 return(1);
1951 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
1952 return(1);
1953
1954 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
1955 mdoc_node_delete(mdoc, mdoc->last);
1956 return(1);
1957 }
1958
1959 static int
1960 pre_literal(PRE_ARGS)
1961 {
1962
1963 if (MDOC_BODY != n->type)
1964 return(1);
1965
1966 /*
1967 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
1968 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
1969 */
1970
1971 switch (n->tok) {
1972 case (MDOC_Dl):
1973 mdoc->flags |= MDOC_LITERAL;
1974 break;
1975 case (MDOC_Bd):
1976 if (DISP_literal == n->norm->Bd.type)
1977 mdoc->flags |= MDOC_LITERAL;
1978 if (DISP_unfilled == n->norm->Bd.type)
1979 mdoc->flags |= MDOC_LITERAL;
1980 break;
1981 default:
1982 abort();
1983 /* NOTREACHED */
1984 }
1985
1986 return(1);
1987 }
1988
1989 static int
1990 post_dd(POST_ARGS)
1991 {
1992 char buf[DATESIZE];
1993 struct mdoc_node *n;
1994 int c;
1995
1996 if (mdoc->meta.date)
1997 free(mdoc->meta.date);
1998
1999 n = mdoc->last;
2000 if (NULL == n->child || '\0' == n->child->string[0]) {
2001 mdoc->meta.date = mandoc_normdate
2002 (mdoc->parse, NULL, n->line, n->pos);
2003 return(1);
2004 }
2005
2006 buf[0] = '\0';
2007 if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2008 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2009 return(0);
2010 }
2011
2012 assert(c);
2013 mdoc->meta.date = mandoc_normdate
2014 (mdoc->parse, buf, n->line, n->pos);
2015
2016 return(1);
2017 }
2018
2019 static int
2020 post_dt(POST_ARGS)
2021 {
2022 struct mdoc_node *nn, *n;
2023 const char *cp;
2024 char *p;
2025
2026 n = mdoc->last;
2027
2028 if (mdoc->meta.title)
2029 free(mdoc->meta.title);
2030 if (mdoc->meta.vol)
2031 free(mdoc->meta.vol);
2032 if (mdoc->meta.arch)
2033 free(mdoc->meta.arch);
2034
2035 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2036
2037 /* First make all characters uppercase. */
2038
2039 if (NULL != (nn = n->child))
2040 for (p = nn->string; *p; p++) {
2041 if (toupper((unsigned char)*p) == *p)
2042 continue;
2043
2044 /*
2045 * FIXME: don't be lazy: have this make all
2046 * characters be uppercase and just warn once.
2047 */
2048 mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2049 break;
2050 }
2051
2052 /* Handles: `.Dt'
2053 * --> title = unknown, volume = local, msec = 0, arch = NULL
2054 */
2055
2056 if (NULL == (nn = n->child)) {
2057 /* XXX: make these macro values. */
2058 /* FIXME: warn about missing values. */
2059 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2060 mdoc->meta.vol = mandoc_strdup("LOCAL");
2061 mdoc->meta.msec = mandoc_strdup("1");
2062 return(1);
2063 }
2064
2065 /* Handles: `.Dt TITLE'
2066 * --> title = TITLE, volume = local, msec = 0, arch = NULL
2067 */
2068
2069 mdoc->meta.title = mandoc_strdup
2070 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2071
2072 if (NULL == (nn = nn->next)) {
2073 /* FIXME: warn about missing msec. */
2074 /* XXX: make this a macro value. */
2075 mdoc->meta.vol = mandoc_strdup("LOCAL");
2076 mdoc->meta.msec = mandoc_strdup("1");
2077 return(1);
2078 }
2079
2080 /* Handles: `.Dt TITLE SEC'
2081 * --> title = TITLE, volume = SEC is msec ?
2082 * format(msec) : SEC,
2083 * msec = SEC is msec ? atoi(msec) : 0,
2084 * arch = NULL
2085 */
2086
2087 cp = mandoc_a2msec(nn->string);
2088 if (cp) {
2089 mdoc->meta.vol = mandoc_strdup(cp);
2090 mdoc->meta.msec = mandoc_strdup(nn->string);
2091 } else {
2092 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2093 mdoc->meta.vol = mandoc_strdup(nn->string);
2094 mdoc->meta.msec = mandoc_strdup(nn->string);
2095 }
2096
2097 if (NULL == (nn = nn->next))
2098 return(1);
2099
2100 /* Handles: `.Dt TITLE SEC VOL'
2101 * --> title = TITLE, volume = VOL is vol ?
2102 * format(VOL) :
2103 * VOL is arch ? format(arch) :
2104 * VOL
2105 */
2106
2107 cp = mdoc_a2vol(nn->string);
2108 if (cp) {
2109 free(mdoc->meta.vol);
2110 mdoc->meta.vol = mandoc_strdup(cp);
2111 } else {
2112 /* FIXME: warn about bad arch. */
2113 cp = mdoc_a2arch(nn->string);
2114 if (NULL == cp) {
2115 free(mdoc->meta.vol);
2116 mdoc->meta.vol = mandoc_strdup(nn->string);
2117 } else
2118 mdoc->meta.arch = mandoc_strdup(cp);
2119 }
2120
2121 /* Ignore any subsequent parameters... */
2122 /* FIXME: warn about subsequent parameters. */
2123
2124 return(1);
2125 }
2126
2127 static int
2128 post_prol(POST_ARGS)
2129 {
2130 /*
2131 * Remove prologue macros from the document after they're
2132 * processed. The final document uses mdoc_meta for these
2133 * values and discards the originals.
2134 */
2135
2136 mdoc_node_delete(mdoc, mdoc->last);
2137 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2138 mdoc->flags |= MDOC_PBODY;
2139
2140 return(1);
2141 }
2142
2143 static int
2144 post_bx(POST_ARGS)
2145 {
2146 struct mdoc_node *n;
2147
2148 /*
2149 * Make `Bx's second argument always start with an uppercase
2150 * letter. Groff checks if it's an "accepted" term, but we just
2151 * uppercase blindly.
2152 */
2153
2154 n = mdoc->last->child;
2155 if (n && NULL != (n = n->next))
2156 *n->string = (char)toupper
2157 ((unsigned char)*n->string);
2158
2159 return(1);
2160 }
2161
2162 static int
2163 post_os(POST_ARGS)
2164 {
2165 struct mdoc_node *n;
2166 char buf[BUFSIZ];
2167 int c;
2168 #ifndef OSNAME
2169 struct utsname utsname;
2170 #endif
2171
2172 n = mdoc->last;
2173
2174 /*
2175 * Set the operating system by way of the `Os' macro. Note that
2176 * if an argument isn't provided and -DOSNAME="\"foo\"" is
2177 * provided during compilation, this value will be used instead
2178 * of filling in "sysname release" from uname().
2179 */
2180
2181 if (mdoc->meta.os)
2182 free(mdoc->meta.os);
2183
2184 buf[0] = '\0';
2185 if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2186 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2187 return(0);
2188 }
2189
2190 assert(c);
2191
2192 /* XXX: yes, these can all be dynamically-adjusted buffers, but
2193 * it's really not worth the extra hackery.
2194 */
2195
2196 if ('\0' == buf[0]) {
2197 #ifdef OSNAME
2198 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2199 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2200 return(0);
2201 }
2202 #else /*!OSNAME */
2203 if (-1 == uname(&utsname)) {
2204 mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2205 mdoc->meta.os = mandoc_strdup("UNKNOWN");
2206 return(post_prol(mdoc));
2207 }
2208
2209 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2210 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2211 return(0);
2212 }
2213 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2214 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2215 return(0);
2216 }
2217 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2218 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2219 return(0);
2220 }
2221 #endif /*!OSNAME*/
2222 }
2223
2224 mdoc->meta.os = mandoc_strdup(buf);
2225 return(1);
2226 }
2227
2228 static int
2229 post_std(POST_ARGS)
2230 {
2231 struct mdoc_node *nn, *n;
2232
2233 n = mdoc->last;
2234
2235 /*
2236 * Macros accepting `-std' as an argument have the name of the
2237 * current document (`Nm') filled in as the argument if it's not
2238 * provided.
2239 */
2240
2241 if (n->child)
2242 return(1);
2243
2244 if (NULL == mdoc->meta.name)
2245 return(1);
2246
2247 nn = n;
2248 mdoc->next = MDOC_NEXT_CHILD;
2249
2250 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2251 return(0);
2252
2253 mdoc->last = nn;
2254 return(1);
2255 }
2256
2257 /*
2258 * Concatenate a node, stopping at the first non-text.
2259 * Concatenation is separated by a single whitespace.
2260 * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2261 * encountered, 1 otherwise.
2262 */
2263 static int
2264 concat(char *p, const struct mdoc_node *n, size_t sz)
2265 {
2266
2267 for ( ; NULL != n; n = n->next) {
2268 if (MDOC_TEXT != n->type)
2269 return(0);
2270 if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2271 return(-1);
2272 if (strlcat(p, n->string, sz) >= sz)
2273 return(-1);
2274 concat(p, n->child, sz);
2275 }
2276
2277 return(1);
2278 }
2279
2280 static enum mdoc_sec
2281 a2sec(const char *p)
2282 {
2283 int i;
2284
2285 for (i = 0; i < (int)SEC__MAX; i++)
2286 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2287 return((enum mdoc_sec)i);
2288
2289 return(SEC_CUSTOM);
2290 }
2291
2292 static size_t
2293 macro2len(enum mdoct macro)
2294 {
2295
2296 switch (macro) {
2297 case(MDOC_Ad):
2298 return(12);
2299 case(MDOC_Ao):
2300 return(12);
2301 case(MDOC_An):
2302 return(12);
2303 case(MDOC_Aq):
2304 return(12);
2305 case(MDOC_Ar):
2306 return(12);
2307 case(MDOC_Bo):
2308 return(12);
2309 case(MDOC_Bq):
2310 return(12);
2311 case(MDOC_Cd):
2312 return(12);
2313 case(MDOC_Cm):
2314 return(10);
2315 case(MDOC_Do):
2316 return(10);
2317 case(MDOC_Dq):
2318 return(12);
2319 case(MDOC_Dv):
2320 return(12);
2321 case(MDOC_Eo):
2322 return(12);
2323 case(MDOC_Em):
2324 return(10);
2325 case(MDOC_Er):
2326 return(17);
2327 case(MDOC_Ev):
2328 return(15);
2329 case(MDOC_Fa):
2330 return(12);
2331 case(MDOC_Fl):
2332 return(10);
2333 case(MDOC_Fo):
2334 return(16);
2335 case(MDOC_Fn):
2336 return(16);
2337 case(MDOC_Ic):
2338 return(10);
2339 case(MDOC_Li):
2340 return(16);
2341 case(MDOC_Ms):
2342 return(6);
2343 case(MDOC_Nm):
2344 return(10);
2345 case(MDOC_No):
2346 return(12);
2347 case(MDOC_Oo):
2348 return(10);
2349 case(MDOC_Op):
2350 return(14);
2351 case(MDOC_Pa):
2352 return(32);
2353 case(MDOC_Pf):
2354 return(12);
2355 case(MDOC_Po):
2356 return(12);
2357 case(MDOC_Pq):
2358 return(12);
2359 case(MDOC_Ql):
2360 return(16);
2361 case(MDOC_Qo):
2362 return(12);
2363 case(MDOC_So):
2364 return(12);
2365 case(MDOC_Sq):
2366 return(12);
2367 case(MDOC_Sy):
2368 return(6);
2369 case(MDOC_Sx):
2370 return(16);
2371 case(MDOC_Tn):
2372 return(10);
2373 case(MDOC_Va):
2374 return(12);
2375 case(MDOC_Vt):
2376 return(12);
2377 case(MDOC_Xr):
2378 return(10);
2379 default:
2380 break;
2381 };
2382 return(0);
2383 }