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