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