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