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