]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
Bail out explicitly on invalid .Rs content like:
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.178 2011/11/18 14:58:24 joerg 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 /* Bail out early if a plain text node is found inside .Rs. */
1697 if (NULL == mdoc->last->child)
1698 return(1);
1699
1700 /*
1701 * The full `Rs' block needs special handling to order the
1702 * sub-elements according to `rsord'. Pick through each element
1703 * and correctly order it. This is a insertion sort.
1704 */
1705
1706 next = NULL;
1707 for (nn = mdoc->last->child->next; nn; nn = next) {
1708 /* Determine order of `nn'. */
1709 for (i = 0; i < RSORD_MAX; i++)
1710 if (rsord[i] == nn->tok)
1711 break;
1712
1713 /*
1714 * Remove `nn' from the chain. This somewhat
1715 * repeats mdoc_node_unlink(), but since we're
1716 * just re-ordering, there's no need for the
1717 * full unlink process.
1718 */
1719
1720 if (NULL != (next = nn->next))
1721 next->prev = nn->prev;
1722
1723 if (NULL != (prev = nn->prev))
1724 prev->next = nn->next;
1725
1726 nn->prev = nn->next = NULL;
1727
1728 /*
1729 * Scan back until we reach a node that's
1730 * ordered before `nn'.
1731 */
1732
1733 for ( ; prev ; prev = prev->prev) {
1734 /* Determine order of `prev'. */
1735 for (j = 0; j < RSORD_MAX; j++)
1736 if (rsord[j] == prev->tok)
1737 break;
1738
1739 if (j <= i)
1740 break;
1741 }
1742
1743 /*
1744 * Set `nn' back into its correct place in front
1745 * of the `prev' node.
1746 */
1747
1748 nn->prev = prev;
1749
1750 if (prev) {
1751 if (prev->next)
1752 prev->next->prev = nn;
1753 nn->next = prev->next;
1754 prev->next = nn;
1755 } else {
1756 mdoc->last->child->prev = nn;
1757 nn->next = mdoc->last->child;
1758 mdoc->last->child = nn;
1759 }
1760 }
1761
1762 return(1);
1763 }
1764
1765 static int
1766 post_ns(POST_ARGS)
1767 {
1768
1769 if (MDOC_LINE & mdoc->last->flags)
1770 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1771 return(1);
1772 }
1773
1774 static int
1775 post_sh(POST_ARGS)
1776 {
1777
1778 if (MDOC_HEAD == mdoc->last->type)
1779 return(post_sh_head(mdoc));
1780 if (MDOC_BODY == mdoc->last->type)
1781 return(post_sh_body(mdoc));
1782
1783 return(1);
1784 }
1785
1786 static int
1787 post_sh_body(POST_ARGS)
1788 {
1789 struct mdoc_node *n;
1790
1791 if (SEC_NAME != mdoc->lastsec)
1792 return(1);
1793
1794 /*
1795 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1796 * macros (can have multiple `Nm' and one `Nd'). Note that the
1797 * children of the BODY declaration can also be "text".
1798 */
1799
1800 if (NULL == (n = mdoc->last->child)) {
1801 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1802 return(1);
1803 }
1804
1805 for ( ; n && n->next; n = n->next) {
1806 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1807 continue;
1808 if (MDOC_TEXT == n->type)
1809 continue;
1810 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1811 }
1812
1813 assert(n);
1814 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1815 return(1);
1816
1817 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1818 return(1);
1819 }
1820
1821 static int
1822 post_sh_head(POST_ARGS)
1823 {
1824 char buf[BUFSIZ];
1825 enum mdoc_sec sec;
1826 int c;
1827
1828 /*
1829 * Process a new section. Sections are either "named" or
1830 * "custom". Custom sections are user-defined, while named ones
1831 * follow a conventional order and may only appear in certain
1832 * manual sections.
1833 */
1834
1835 sec = SEC_CUSTOM;
1836 buf[0] = '\0';
1837 if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1838 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1839 return(0);
1840 } else if (1 == c)
1841 sec = a2sec(buf);
1842
1843 /* The NAME should be first. */
1844
1845 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1846 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1847
1848 /* The SYNOPSIS gets special attention in other areas. */
1849
1850 if (SEC_SYNOPSIS == sec)
1851 mdoc->flags |= MDOC_SYNOPSIS;
1852 else
1853 mdoc->flags &= ~MDOC_SYNOPSIS;
1854
1855 /* Mark our last section. */
1856
1857 mdoc->lastsec = sec;
1858
1859 /* We don't care about custom sections after this. */
1860
1861 if (SEC_CUSTOM == sec)
1862 return(1);
1863
1864 /*
1865 * Check whether our non-custom section is being repeated or is
1866 * out of order.
1867 */
1868
1869 if (sec == mdoc->lastnamed)
1870 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1871
1872 if (sec < mdoc->lastnamed)
1873 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1874
1875 /* Mark the last named section. */
1876
1877 mdoc->lastnamed = sec;
1878
1879 /* Check particular section/manual conventions. */
1880
1881 assert(mdoc->meta.msec);
1882
1883 switch (sec) {
1884 case (SEC_RETURN_VALUES):
1885 /* FALLTHROUGH */
1886 case (SEC_ERRORS):
1887 /* FALLTHROUGH */
1888 case (SEC_LIBRARY):
1889 if (*mdoc->meta.msec == '2')
1890 break;
1891 if (*mdoc->meta.msec == '3')
1892 break;
1893 if (*mdoc->meta.msec == '9')
1894 break;
1895 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1896 break;
1897 default:
1898 break;
1899 }
1900
1901 return(1);
1902 }
1903
1904 static int
1905 post_ignpar(POST_ARGS)
1906 {
1907 struct mdoc_node *np;
1908
1909 if (MDOC_BODY != mdoc->last->type)
1910 return(1);
1911
1912 if (NULL != (np = mdoc->last->child))
1913 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1914 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1915 mdoc_node_delete(mdoc, np);
1916 }
1917
1918 if (NULL != (np = mdoc->last->last))
1919 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1920 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1921 mdoc_node_delete(mdoc, np);
1922 }
1923
1924 return(1);
1925 }
1926
1927 static int
1928 pre_par(PRE_ARGS)
1929 {
1930
1931 if (NULL == mdoc->last)
1932 return(1);
1933 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
1934 return(1);
1935
1936 /*
1937 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1938 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
1939 */
1940
1941 if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
1942 return(1);
1943 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
1944 return(1);
1945 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
1946 return(1);
1947 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
1948 return(1);
1949
1950 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
1951 mdoc_node_delete(mdoc, mdoc->last);
1952 return(1);
1953 }
1954
1955 static int
1956 pre_literal(PRE_ARGS)
1957 {
1958
1959 if (MDOC_BODY != n->type)
1960 return(1);
1961
1962 /*
1963 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
1964 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
1965 */
1966
1967 switch (n->tok) {
1968 case (MDOC_Dl):
1969 mdoc->flags |= MDOC_LITERAL;
1970 break;
1971 case (MDOC_Bd):
1972 if (DISP_literal == n->norm->Bd.type)
1973 mdoc->flags |= MDOC_LITERAL;
1974 if (DISP_unfilled == n->norm->Bd.type)
1975 mdoc->flags |= MDOC_LITERAL;
1976 break;
1977 default:
1978 abort();
1979 /* NOTREACHED */
1980 }
1981
1982 return(1);
1983 }
1984
1985 static int
1986 post_dd(POST_ARGS)
1987 {
1988 char buf[DATESIZE];
1989 struct mdoc_node *n;
1990 int c;
1991
1992 if (mdoc->meta.date)
1993 free(mdoc->meta.date);
1994
1995 n = mdoc->last;
1996 if (NULL == n->child || '\0' == n->child->string[0]) {
1997 mdoc->meta.date = mandoc_normdate
1998 (mdoc->parse, NULL, n->line, n->pos);
1999 return(1);
2000 }
2001
2002 buf[0] = '\0';
2003 if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2004 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2005 return(0);
2006 }
2007
2008 assert(c);
2009 mdoc->meta.date = mandoc_normdate
2010 (mdoc->parse, buf, n->line, n->pos);
2011
2012 return(1);
2013 }
2014
2015 static int
2016 post_dt(POST_ARGS)
2017 {
2018 struct mdoc_node *nn, *n;
2019 const char *cp;
2020 char *p;
2021
2022 n = mdoc->last;
2023
2024 if (mdoc->meta.title)
2025 free(mdoc->meta.title);
2026 if (mdoc->meta.vol)
2027 free(mdoc->meta.vol);
2028 if (mdoc->meta.arch)
2029 free(mdoc->meta.arch);
2030
2031 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2032
2033 /* First make all characters uppercase. */
2034
2035 if (NULL != (nn = n->child))
2036 for (p = nn->string; *p; p++) {
2037 if (toupper((unsigned char)*p) == *p)
2038 continue;
2039
2040 /*
2041 * FIXME: don't be lazy: have this make all
2042 * characters be uppercase and just warn once.
2043 */
2044 mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2045 break;
2046 }
2047
2048 /* Handles: `.Dt'
2049 * --> title = unknown, volume = local, msec = 0, arch = NULL
2050 */
2051
2052 if (NULL == (nn = n->child)) {
2053 /* XXX: make these macro values. */
2054 /* FIXME: warn about missing values. */
2055 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2056 mdoc->meta.vol = mandoc_strdup("LOCAL");
2057 mdoc->meta.msec = mandoc_strdup("1");
2058 return(1);
2059 }
2060
2061 /* Handles: `.Dt TITLE'
2062 * --> title = TITLE, volume = local, msec = 0, arch = NULL
2063 */
2064
2065 mdoc->meta.title = mandoc_strdup
2066 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2067
2068 if (NULL == (nn = nn->next)) {
2069 /* FIXME: warn about missing msec. */
2070 /* XXX: make this a macro value. */
2071 mdoc->meta.vol = mandoc_strdup("LOCAL");
2072 mdoc->meta.msec = mandoc_strdup("1");
2073 return(1);
2074 }
2075
2076 /* Handles: `.Dt TITLE SEC'
2077 * --> title = TITLE, volume = SEC is msec ?
2078 * format(msec) : SEC,
2079 * msec = SEC is msec ? atoi(msec) : 0,
2080 * arch = NULL
2081 */
2082
2083 cp = mdoc_a2msec(nn->string);
2084 if (cp) {
2085 mdoc->meta.vol = mandoc_strdup(cp);
2086 mdoc->meta.msec = mandoc_strdup(nn->string);
2087 } else {
2088 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2089 mdoc->meta.vol = mandoc_strdup(nn->string);
2090 mdoc->meta.msec = mandoc_strdup(nn->string);
2091 }
2092
2093 if (NULL == (nn = nn->next))
2094 return(1);
2095
2096 /* Handles: `.Dt TITLE SEC VOL'
2097 * --> title = TITLE, volume = VOL is vol ?
2098 * format(VOL) :
2099 * VOL is arch ? format(arch) :
2100 * VOL
2101 */
2102
2103 cp = mdoc_a2vol(nn->string);
2104 if (cp) {
2105 free(mdoc->meta.vol);
2106 mdoc->meta.vol = mandoc_strdup(cp);
2107 } else {
2108 /* FIXME: warn about bad arch. */
2109 cp = mdoc_a2arch(nn->string);
2110 if (NULL == cp) {
2111 free(mdoc->meta.vol);
2112 mdoc->meta.vol = mandoc_strdup(nn->string);
2113 } else
2114 mdoc->meta.arch = mandoc_strdup(cp);
2115 }
2116
2117 /* Ignore any subsequent parameters... */
2118 /* FIXME: warn about subsequent parameters. */
2119
2120 return(1);
2121 }
2122
2123 static int
2124 post_prol(POST_ARGS)
2125 {
2126 /*
2127 * Remove prologue macros from the document after they're
2128 * processed. The final document uses mdoc_meta for these
2129 * values and discards the originals.
2130 */
2131
2132 mdoc_node_delete(mdoc, mdoc->last);
2133 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2134 mdoc->flags |= MDOC_PBODY;
2135
2136 return(1);
2137 }
2138
2139 static int
2140 post_bx(POST_ARGS)
2141 {
2142 struct mdoc_node *n;
2143
2144 /*
2145 * Make `Bx's second argument always start with an uppercase
2146 * letter. Groff checks if it's an "accepted" term, but we just
2147 * uppercase blindly.
2148 */
2149
2150 n = mdoc->last->child;
2151 if (n && NULL != (n = n->next))
2152 *n->string = (char)toupper
2153 ((unsigned char)*n->string);
2154
2155 return(1);
2156 }
2157
2158 static int
2159 post_os(POST_ARGS)
2160 {
2161 struct mdoc_node *n;
2162 char buf[BUFSIZ];
2163 int c;
2164 #ifndef OSNAME
2165 struct utsname utsname;
2166 #endif
2167
2168 n = mdoc->last;
2169
2170 /*
2171 * Set the operating system by way of the `Os' macro. Note that
2172 * if an argument isn't provided and -DOSNAME="\"foo\"" is
2173 * provided during compilation, this value will be used instead
2174 * of filling in "sysname release" from uname().
2175 */
2176
2177 if (mdoc->meta.os)
2178 free(mdoc->meta.os);
2179
2180 buf[0] = '\0';
2181 if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2182 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2183 return(0);
2184 }
2185
2186 assert(c);
2187
2188 /* XXX: yes, these can all be dynamically-adjusted buffers, but
2189 * it's really not worth the extra hackery.
2190 */
2191
2192 if ('\0' == buf[0]) {
2193 #ifdef OSNAME
2194 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2195 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2196 return(0);
2197 }
2198 #else /*!OSNAME */
2199 if (-1 == uname(&utsname)) {
2200 mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2201 mdoc->meta.os = mandoc_strdup("UNKNOWN");
2202 return(post_prol(mdoc));
2203 }
2204
2205 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2206 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2207 return(0);
2208 }
2209 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2210 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2211 return(0);
2212 }
2213 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2214 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2215 return(0);
2216 }
2217 #endif /*!OSNAME*/
2218 }
2219
2220 mdoc->meta.os = mandoc_strdup(buf);
2221 return(1);
2222 }
2223
2224 static int
2225 post_std(POST_ARGS)
2226 {
2227 struct mdoc_node *nn, *n;
2228
2229 n = mdoc->last;
2230
2231 /*
2232 * Macros accepting `-std' as an argument have the name of the
2233 * current document (`Nm') filled in as the argument if it's not
2234 * provided.
2235 */
2236
2237 if (n->child)
2238 return(1);
2239
2240 if (NULL == mdoc->meta.name)
2241 return(1);
2242
2243 nn = n;
2244 mdoc->next = MDOC_NEXT_CHILD;
2245
2246 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2247 return(0);
2248
2249 mdoc->last = nn;
2250 return(1);
2251 }
2252
2253 /*
2254 * Concatenate a node, stopping at the first non-text.
2255 * Concatenation is separated by a single whitespace.
2256 * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2257 * encountered, 1 otherwise.
2258 */
2259 static int
2260 concat(char *p, const struct mdoc_node *n, size_t sz)
2261 {
2262
2263 for ( ; NULL != n; n = n->next) {
2264 if (MDOC_TEXT != n->type)
2265 return(0);
2266 if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2267 return(-1);
2268 if (strlcat(p, n->string, sz) >= sz)
2269 return(-1);
2270 concat(p, n->child, sz);
2271 }
2272
2273 return(1);
2274 }
2275
2276 static enum mdoc_sec
2277 a2sec(const char *p)
2278 {
2279 int i;
2280
2281 for (i = 0; i < (int)SEC__MAX; i++)
2282 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2283 return((enum mdoc_sec)i);
2284
2285 return(SEC_CUSTOM);
2286 }
2287
2288 static size_t
2289 macro2len(enum mdoct macro)
2290 {
2291
2292 switch (macro) {
2293 case(MDOC_Ad):
2294 return(12);
2295 case(MDOC_Ao):
2296 return(12);
2297 case(MDOC_An):
2298 return(12);
2299 case(MDOC_Aq):
2300 return(12);
2301 case(MDOC_Ar):
2302 return(12);
2303 case(MDOC_Bo):
2304 return(12);
2305 case(MDOC_Bq):
2306 return(12);
2307 case(MDOC_Cd):
2308 return(12);
2309 case(MDOC_Cm):
2310 return(10);
2311 case(MDOC_Do):
2312 return(10);
2313 case(MDOC_Dq):
2314 return(12);
2315 case(MDOC_Dv):
2316 return(12);
2317 case(MDOC_Eo):
2318 return(12);
2319 case(MDOC_Em):
2320 return(10);
2321 case(MDOC_Er):
2322 return(17);
2323 case(MDOC_Ev):
2324 return(15);
2325 case(MDOC_Fa):
2326 return(12);
2327 case(MDOC_Fl):
2328 return(10);
2329 case(MDOC_Fo):
2330 return(16);
2331 case(MDOC_Fn):
2332 return(16);
2333 case(MDOC_Ic):
2334 return(10);
2335 case(MDOC_Li):
2336 return(16);
2337 case(MDOC_Ms):
2338 return(6);
2339 case(MDOC_Nm):
2340 return(10);
2341 case(MDOC_No):
2342 return(12);
2343 case(MDOC_Oo):
2344 return(10);
2345 case(MDOC_Op):
2346 return(14);
2347 case(MDOC_Pa):
2348 return(32);
2349 case(MDOC_Pf):
2350 return(12);
2351 case(MDOC_Po):
2352 return(12);
2353 case(MDOC_Pq):
2354 return(12);
2355 case(MDOC_Ql):
2356 return(16);
2357 case(MDOC_Qo):
2358 return(12);
2359 case(MDOC_So):
2360 return(12);
2361 case(MDOC_Sq):
2362 return(12);
2363 case(MDOC_Sy):
2364 return(6);
2365 case(MDOC_Sx):
2366 return(16);
2367 case(MDOC_Tn):
2368 return(10);
2369 case(MDOC_Va):
2370 return(12);
2371 case(MDOC_Vt):
2372 return(12);
2373 case(MDOC_Xr):
2374 return(10);
2375 default:
2376 break;
2377 };
2378 return(0);
2379 }