]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
Cleanup naming of local variables to make the code easier on the eye:
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.191 2012/11/17 00:26:33 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #ifndef OSNAME
23 #include <sys/utsname.h>
24 #endif
25
26 #include <sys/types.h>
27
28 #include <assert.h>
29 #include <ctype.h>
30 #include <limits.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35
36 #include "mdoc.h"
37 #include "mandoc.h"
38 #include "libmdoc.h"
39 #include "libmandoc.h"
40
41 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
42
43 #define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n
44 #define POST_ARGS struct mdoc *mdoc
45
46 #define NUMSIZ 32
47 #define DATESIZE 32
48
49 enum check_ineq {
50 CHECK_LT,
51 CHECK_GT,
52 CHECK_EQ
53 };
54
55 enum check_lvl {
56 CHECK_WARN,
57 CHECK_ERROR,
58 };
59
60 typedef int (*v_pre)(PRE_ARGS);
61 typedef int (*v_post)(POST_ARGS);
62
63 struct valids {
64 v_pre *pre;
65 v_post *post;
66 };
67
68 static int check_count(struct mdoc *, enum mdoc_type,
69 enum check_lvl, enum check_ineq, int);
70 static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
71 static void check_text(struct mdoc *, int, int, char *);
72 static void check_argv(struct mdoc *,
73 struct mdoc_node *, struct mdoc_argv *);
74 static void check_args(struct mdoc *, struct mdoc_node *);
75 static int concat(char *, const struct mdoc_node *, size_t);
76 static enum mdoc_sec a2sec(const char *);
77 static size_t macro2len(enum mdoct);
78
79 static int ebool(POST_ARGS);
80 static int berr_ge1(POST_ARGS);
81 static int bwarn_ge1(POST_ARGS);
82 static int ewarn_eq0(POST_ARGS);
83 static int ewarn_eq1(POST_ARGS);
84 static int ewarn_ge1(POST_ARGS);
85 static int ewarn_le1(POST_ARGS);
86 static int hwarn_eq0(POST_ARGS);
87 static int hwarn_eq1(POST_ARGS);
88 static int hwarn_ge1(POST_ARGS);
89 static int hwarn_le1(POST_ARGS);
90
91 static int post_an(POST_ARGS);
92 static int post_at(POST_ARGS);
93 static int post_bf(POST_ARGS);
94 static int post_bl(POST_ARGS);
95 static int post_bl_block(POST_ARGS);
96 static int post_bl_block_width(POST_ARGS);
97 static int post_bl_block_tag(POST_ARGS);
98 static int post_bl_head(POST_ARGS);
99 static int post_bx(POST_ARGS);
100 static int post_dd(POST_ARGS);
101 static int post_dt(POST_ARGS);
102 static int post_defaults(POST_ARGS);
103 static int post_literal(POST_ARGS);
104 static int post_eoln(POST_ARGS);
105 static int post_it(POST_ARGS);
106 static int post_lb(POST_ARGS);
107 static int post_nm(POST_ARGS);
108 static int post_ns(POST_ARGS);
109 static int post_os(POST_ARGS);
110 static int post_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__D,
323 MDOC__O,
324 MDOC__C
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
892 roff_regunset(mdoc->roff, REG_nS);
893 return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
894 }
895
896
897 static int
898 pre_it(PRE_ARGS)
899 {
900
901 if (MDOC_BLOCK != n->type)
902 return(1);
903
904 return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
905 }
906
907
908 static int
909 pre_an(PRE_ARGS)
910 {
911 int i;
912
913 if (NULL == n->args)
914 return(1);
915
916 for (i = 1; i < (int)n->args->argc; i++)
917 mdoc_pmsg(mdoc, n->args->argv[i].line,
918 n->args->argv[i].pos, MANDOCERR_IGNARGV);
919
920 if (MDOC_Split == n->args->argv[0].arg)
921 n->norm->An.auth = AUTH_split;
922 else if (MDOC_Nosplit == n->args->argv[0].arg)
923 n->norm->An.auth = AUTH_nosplit;
924 else
925 abort();
926
927 return(1);
928 }
929
930 static int
931 pre_std(PRE_ARGS)
932 {
933
934 if (n->args && 1 == n->args->argc)
935 if (MDOC_Std == n->args->argv[0].arg)
936 return(1);
937
938 mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
939 return(1);
940 }
941
942 static int
943 pre_dt(PRE_ARGS)
944 {
945
946 if (NULL == mdoc->meta.date || mdoc->meta.os)
947 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
948
949 if (mdoc->meta.title)
950 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
951
952 return(1);
953 }
954
955 static int
956 pre_os(PRE_ARGS)
957 {
958
959 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
960 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
961
962 if (mdoc->meta.os)
963 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
964
965 return(1);
966 }
967
968 static int
969 pre_dd(PRE_ARGS)
970 {
971
972 if (mdoc->meta.title || mdoc->meta.os)
973 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
974
975 if (mdoc->meta.date)
976 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
977
978 return(1);
979 }
980
981
982 static int
983 post_bf(POST_ARGS)
984 {
985 struct mdoc_node *np;
986 enum mdocargt arg;
987
988 /*
989 * Unlike other data pointers, these are "housed" by the HEAD
990 * element, which contains the goods.
991 */
992
993 if (MDOC_HEAD != mdoc->last->type) {
994 if (ENDBODY_NOT != mdoc->last->end) {
995 assert(mdoc->last->pending);
996 np = mdoc->last->pending->parent->head;
997 } else if (MDOC_BLOCK != mdoc->last->type) {
998 np = mdoc->last->parent->head;
999 } else
1000 np = mdoc->last->head;
1001
1002 assert(np);
1003 assert(MDOC_HEAD == np->type);
1004 assert(MDOC_Bf == np->tok);
1005 return(1);
1006 }
1007
1008 np = mdoc->last;
1009 assert(MDOC_BLOCK == np->parent->type);
1010 assert(MDOC_Bf == np->parent->tok);
1011
1012 /*
1013 * Cannot have both argument and parameter.
1014 * If neither is specified, let it through with a warning.
1015 */
1016
1017 if (np->parent->args && np->child) {
1018 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1019 return(0);
1020 } else if (NULL == np->parent->args && NULL == np->child) {
1021 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1022 return(1);
1023 }
1024
1025 /* Extract argument into data. */
1026
1027 if (np->parent->args) {
1028 arg = np->parent->args->argv[0].arg;
1029 if (MDOC_Emphasis == arg)
1030 np->norm->Bf.font = FONT_Em;
1031 else if (MDOC_Literal == arg)
1032 np->norm->Bf.font = FONT_Li;
1033 else if (MDOC_Symbolic == arg)
1034 np->norm->Bf.font = FONT_Sy;
1035 else
1036 abort();
1037 return(1);
1038 }
1039
1040 /* Extract parameter into data. */
1041
1042 if (0 == strcmp(np->child->string, "Em"))
1043 np->norm->Bf.font = FONT_Em;
1044 else if (0 == strcmp(np->child->string, "Li"))
1045 np->norm->Bf.font = FONT_Li;
1046 else if (0 == strcmp(np->child->string, "Sy"))
1047 np->norm->Bf.font = FONT_Sy;
1048 else
1049 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1050
1051 return(1);
1052 }
1053
1054 static int
1055 post_lb(POST_ARGS)
1056 {
1057 const char *p;
1058 char *buf;
1059 size_t sz;
1060
1061 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1062
1063 assert(mdoc->last->child);
1064 assert(MDOC_TEXT == mdoc->last->child->type);
1065
1066 p = mdoc_a2lib(mdoc->last->child->string);
1067
1068 /* If lookup ok, replace with table value. */
1069
1070 if (p) {
1071 free(mdoc->last->child->string);
1072 mdoc->last->child->string = mandoc_strdup(p);
1073 return(1);
1074 }
1075
1076 /* If not, use "library ``xxxx''. */
1077
1078 sz = strlen(mdoc->last->child->string) +
1079 2 + strlen("\\(lqlibrary\\(rq");
1080 buf = mandoc_malloc(sz);
1081 snprintf(buf, sz, "library \\(lq%s\\(rq",
1082 mdoc->last->child->string);
1083 free(mdoc->last->child->string);
1084 mdoc->last->child->string = buf;
1085 return(1);
1086 }
1087
1088 static int
1089 post_eoln(POST_ARGS)
1090 {
1091
1092 if (mdoc->last->child)
1093 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1094 return(1);
1095 }
1096
1097
1098 static int
1099 post_vt(POST_ARGS)
1100 {
1101 const struct mdoc_node *n;
1102
1103 /*
1104 * The Vt macro comes in both ELEM and BLOCK form, both of which
1105 * have different syntaxes (yet more context-sensitive
1106 * behaviour). ELEM types must have a child, which is already
1107 * guaranteed by the in_line parsing routine; BLOCK types,
1108 * specifically the BODY, should only have TEXT children.
1109 */
1110
1111 if (MDOC_BODY != mdoc->last->type)
1112 return(1);
1113
1114 for (n = mdoc->last->child; n; n = n->next)
1115 if (MDOC_TEXT != n->type)
1116 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1117
1118 return(1);
1119 }
1120
1121
1122 static int
1123 post_nm(POST_ARGS)
1124 {
1125 char buf[BUFSIZ];
1126 int c;
1127
1128 if (NULL != mdoc->meta.name)
1129 return(1);
1130
1131 /* Try to use our children for setting the meta name. */
1132
1133 if (NULL != mdoc->last->child) {
1134 buf[0] = '\0';
1135 c = concat(buf, mdoc->last->child, BUFSIZ);
1136 } else
1137 c = 0;
1138
1139 switch (c) {
1140 case (-1):
1141 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1142 return(0);
1143 case (0):
1144 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1145 mdoc->meta.name = mandoc_strdup("UNKNOWN");
1146 break;
1147 default:
1148 mdoc->meta.name = mandoc_strdup(buf);
1149 break;
1150 }
1151 return(1);
1152 }
1153
1154 static int
1155 post_literal(POST_ARGS)
1156 {
1157
1158 /*
1159 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1160 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1161 * this in literal mode, but it doesn't hurt to just switch it
1162 * off in general since displays can't be nested.
1163 */
1164
1165 if (MDOC_BODY == mdoc->last->type)
1166 mdoc->flags &= ~MDOC_LITERAL;
1167
1168 return(1);
1169 }
1170
1171 static int
1172 post_defaults(POST_ARGS)
1173 {
1174 struct mdoc_node *nn;
1175
1176 /*
1177 * The `Ar' defaults to "file ..." if no value is provided as an
1178 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1179 * gets an empty string.
1180 */
1181
1182 if (mdoc->last->child)
1183 return(1);
1184
1185 nn = mdoc->last;
1186 mdoc->next = MDOC_NEXT_CHILD;
1187
1188 switch (nn->tok) {
1189 case (MDOC_Ar):
1190 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1191 return(0);
1192 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1193 return(0);
1194 break;
1195 case (MDOC_At):
1196 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1197 return(0);
1198 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1199 return(0);
1200 break;
1201 case (MDOC_Li):
1202 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1203 return(0);
1204 break;
1205 case (MDOC_Pa):
1206 /* FALLTHROUGH */
1207 case (MDOC_Mt):
1208 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1209 return(0);
1210 break;
1211 default:
1212 abort();
1213 /* NOTREACHED */
1214 }
1215
1216 mdoc->last = nn;
1217 return(1);
1218 }
1219
1220 static int
1221 post_at(POST_ARGS)
1222 {
1223 const char *p, *q;
1224 char *buf;
1225 size_t sz;
1226
1227 /*
1228 * If we have a child, look it up in the standard keys. If a
1229 * key exist, use that instead of the child; if it doesn't,
1230 * prefix "AT&T UNIX " to the existing data.
1231 */
1232
1233 if (NULL == mdoc->last->child)
1234 return(1);
1235
1236 assert(MDOC_TEXT == mdoc->last->child->type);
1237 p = mdoc_a2att(mdoc->last->child->string);
1238
1239 if (p) {
1240 free(mdoc->last->child->string);
1241 mdoc->last->child->string = mandoc_strdup(p);
1242 } else {
1243 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1244 p = "AT&T UNIX ";
1245 q = mdoc->last->child->string;
1246 sz = strlen(p) + strlen(q) + 1;
1247 buf = mandoc_malloc(sz);
1248 strlcpy(buf, p, sz);
1249 strlcat(buf, q, sz);
1250 free(mdoc->last->child->string);
1251 mdoc->last->child->string = buf;
1252 }
1253
1254 return(1);
1255 }
1256
1257 static int
1258 post_an(POST_ARGS)
1259 {
1260 struct mdoc_node *np;
1261
1262 np = mdoc->last;
1263 if (AUTH__NONE == np->norm->An.auth) {
1264 if (0 == np->child)
1265 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1266 } else if (np->child)
1267 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1268
1269 return(1);
1270 }
1271
1272
1273 static int
1274 post_it(POST_ARGS)
1275 {
1276 int i, cols;
1277 enum mdoc_list lt;
1278 struct mdoc_node *n, *c;
1279 enum mandocerr er;
1280
1281 if (MDOC_BLOCK != mdoc->last->type)
1282 return(1);
1283
1284 n = mdoc->last->parent->parent;
1285 lt = n->norm->Bl.type;
1286
1287 if (LIST__NONE == lt) {
1288 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1289 return(1);
1290 }
1291
1292 switch (lt) {
1293 case (LIST_tag):
1294 if (mdoc->last->head->child)
1295 break;
1296 /* FIXME: give this a dummy value. */
1297 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1298 break;
1299 case (LIST_hang):
1300 /* FALLTHROUGH */
1301 case (LIST_ohang):
1302 /* FALLTHROUGH */
1303 case (LIST_inset):
1304 /* FALLTHROUGH */
1305 case (LIST_diag):
1306 if (NULL == mdoc->last->head->child)
1307 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1308 break;
1309 case (LIST_bullet):
1310 /* FALLTHROUGH */
1311 case (LIST_dash):
1312 /* FALLTHROUGH */
1313 case (LIST_enum):
1314 /* FALLTHROUGH */
1315 case (LIST_hyphen):
1316 if (NULL == mdoc->last->body->child)
1317 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1318 /* FALLTHROUGH */
1319 case (LIST_item):
1320 if (mdoc->last->head->child)
1321 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1322 break;
1323 case (LIST_column):
1324 cols = (int)n->norm->Bl.ncols;
1325
1326 assert(NULL == mdoc->last->head->child);
1327
1328 if (NULL == mdoc->last->body->child)
1329 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1330
1331 for (i = 0, c = mdoc->last->child; c; c = c->next)
1332 if (MDOC_BODY == c->type)
1333 i++;
1334
1335 if (i < cols)
1336 er = MANDOCERR_ARGCOUNT;
1337 else if (i == cols || i == cols + 1)
1338 break;
1339 else
1340 er = MANDOCERR_SYNTARGCOUNT;
1341
1342 mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1343 mdoc->last->pos,
1344 "columns == %d (have %d)", cols, i);
1345 return(MANDOCERR_ARGCOUNT == er);
1346 default:
1347 break;
1348 }
1349
1350 return(1);
1351 }
1352
1353 static int
1354 post_bl_block(POST_ARGS)
1355 {
1356 struct mdoc_node *n, *ni, *nc;
1357
1358 /*
1359 * These are fairly complicated, so we've broken them into two
1360 * functions. post_bl_block_tag() is called when a -tag is
1361 * specified, but no -width (it must be guessed). The second
1362 * when a -width is specified (macro indicators must be
1363 * rewritten into real lengths).
1364 */
1365
1366 n = mdoc->last;
1367
1368 if (LIST_tag == n->norm->Bl.type &&
1369 NULL == n->norm->Bl.width) {
1370 if ( ! post_bl_block_tag(mdoc))
1371 return(0);
1372 assert(n->norm->Bl.width);
1373 } else if (NULL != n->norm->Bl.width) {
1374 if ( ! post_bl_block_width(mdoc))
1375 return(0);
1376 assert(n->norm->Bl.width);
1377 }
1378
1379 for (ni = n->body->child; ni; ni = ni->next) {
1380 if (NULL == ni->body)
1381 continue;
1382 nc = ni->body->last;
1383 while (NULL != nc) {
1384 switch (nc->tok) {
1385 case (MDOC_Pp):
1386 /* FALLTHROUGH */
1387 case (MDOC_Lp):
1388 /* FALLTHROUGH */
1389 case (MDOC_br):
1390 break;
1391 default:
1392 nc = NULL;
1393 continue;
1394 }
1395 if (NULL == ni->next) {
1396 mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR);
1397 if ( ! mdoc_node_relink(mdoc, nc))
1398 return(0);
1399 } else if (0 == n->norm->Bl.comp &&
1400 LIST_column != n->norm->Bl.type) {
1401 mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR);
1402 mdoc_node_delete(mdoc, nc);
1403 } else
1404 break;
1405 nc = ni->body->last;
1406 }
1407 }
1408 return(1);
1409 }
1410
1411 static int
1412 post_bl_block_width(POST_ARGS)
1413 {
1414 size_t width;
1415 int i;
1416 enum mdoct tok;
1417 struct mdoc_node *n;
1418 char buf[NUMSIZ];
1419
1420 n = mdoc->last;
1421
1422 /*
1423 * Calculate the real width of a list from the -width string,
1424 * which may contain a macro (with a known default width), a
1425 * literal string, or a scaling width.
1426 *
1427 * If the value to -width is a macro, then we re-write it to be
1428 * the macro's width as set in share/tmac/mdoc/doc-common.
1429 */
1430
1431 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1432 width = 6;
1433 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1434 return(1);
1435 else if (0 == (width = macro2len(tok))) {
1436 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1437 return(1);
1438 }
1439
1440 /* The value already exists: free and reallocate it. */
1441
1442 assert(n->args);
1443
1444 for (i = 0; i < (int)n->args->argc; i++)
1445 if (MDOC_Width == n->args->argv[i].arg)
1446 break;
1447
1448 assert(i < (int)n->args->argc);
1449
1450 snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1451 free(n->args->argv[i].value[0]);
1452 n->args->argv[i].value[0] = mandoc_strdup(buf);
1453
1454 /* Set our width! */
1455 n->norm->Bl.width = n->args->argv[i].value[0];
1456 return(1);
1457 }
1458
1459 static int
1460 post_bl_block_tag(POST_ARGS)
1461 {
1462 struct mdoc_node *n, *nn;
1463 size_t sz, ssz;
1464 int i;
1465 char buf[NUMSIZ];
1466
1467 /*
1468 * Calculate the -width for a `Bl -tag' list if it hasn't been
1469 * provided. Uses the first head macro. NOTE AGAIN: this is
1470 * ONLY if the -width argument has NOT been provided. See
1471 * post_bl_block_width() for converting the -width string.
1472 */
1473
1474 sz = 10;
1475 n = mdoc->last;
1476
1477 for (nn = n->body->child; nn; nn = nn->next) {
1478 if (MDOC_It != nn->tok)
1479 continue;
1480
1481 assert(MDOC_BLOCK == nn->type);
1482 nn = nn->head->child;
1483
1484 if (nn == NULL)
1485 break;
1486
1487 if (MDOC_TEXT == nn->type) {
1488 sz = strlen(nn->string) + 1;
1489 break;
1490 }
1491
1492 if (0 != (ssz = macro2len(nn->tok)))
1493 sz = ssz;
1494
1495 break;
1496 }
1497
1498 /* Defaults to ten ens. */
1499
1500 snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1501
1502 /*
1503 * We have to dynamically add this to the macro's argument list.
1504 * We're guaranteed that a MDOC_Width doesn't already exist.
1505 */
1506
1507 assert(n->args);
1508 i = (int)(n->args->argc)++;
1509
1510 n->args->argv = mandoc_realloc(n->args->argv,
1511 n->args->argc * sizeof(struct mdoc_argv));
1512
1513 n->args->argv[i].arg = MDOC_Width;
1514 n->args->argv[i].line = n->line;
1515 n->args->argv[i].pos = n->pos;
1516 n->args->argv[i].sz = 1;
1517 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1518 n->args->argv[i].value[0] = mandoc_strdup(buf);
1519
1520 /* Set our width! */
1521 n->norm->Bl.width = n->args->argv[i].value[0];
1522 return(1);
1523 }
1524
1525
1526 static int
1527 post_bl_head(POST_ARGS)
1528 {
1529 struct mdoc_node *np, *nn, *nnp;
1530 int i, j;
1531
1532 if (LIST_column != mdoc->last->norm->Bl.type)
1533 /* FIXME: this should be ERROR class... */
1534 return(hwarn_eq0(mdoc));
1535
1536 /*
1537 * Convert old-style lists, where the column width specifiers
1538 * trail as macro parameters, to the new-style ("normal-form")
1539 * lists where they're argument values following -column.
1540 */
1541
1542 /* First, disallow both types and allow normal-form. */
1543
1544 /*
1545 * TODO: technically, we can accept both and just merge the two
1546 * lists, but I'll leave that for another day.
1547 */
1548
1549 if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1550 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1551 return(0);
1552 } else if (NULL == mdoc->last->child)
1553 return(1);
1554
1555 np = mdoc->last->parent;
1556 assert(np->args);
1557
1558 for (j = 0; j < (int)np->args->argc; j++)
1559 if (MDOC_Column == np->args->argv[j].arg)
1560 break;
1561
1562 assert(j < (int)np->args->argc);
1563 assert(0 == np->args->argv[j].sz);
1564
1565 /*
1566 * Accommodate for new-style groff column syntax. Shuffle the
1567 * child nodes, all of which must be TEXT, as arguments for the
1568 * column field. Then, delete the head children.
1569 */
1570
1571 np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1572 np->args->argv[j].value = mandoc_malloc
1573 ((size_t)mdoc->last->nchild * sizeof(char *));
1574
1575 mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1576 mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1577
1578 for (i = 0, nn = mdoc->last->child; nn; i++) {
1579 np->args->argv[j].value[i] = nn->string;
1580 nn->string = NULL;
1581 nnp = nn;
1582 nn = nn->next;
1583 mdoc_node_delete(NULL, nnp);
1584 }
1585
1586 mdoc->last->nchild = 0;
1587 mdoc->last->child = NULL;
1588
1589 return(1);
1590 }
1591
1592 static int
1593 post_bl(POST_ARGS)
1594 {
1595 struct mdoc_node *n;
1596
1597 if (MDOC_HEAD == mdoc->last->type)
1598 return(post_bl_head(mdoc));
1599 if (MDOC_BLOCK == mdoc->last->type)
1600 return(post_bl_block(mdoc));
1601 if (MDOC_BODY != mdoc->last->type)
1602 return(1);
1603
1604 for (n = mdoc->last->child; n; n = n->next) {
1605 switch (n->tok) {
1606 case (MDOC_Lp):
1607 /* FALLTHROUGH */
1608 case (MDOC_Pp):
1609 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1610 /* FALLTHROUGH */
1611 case (MDOC_It):
1612 /* FALLTHROUGH */
1613 case (MDOC_Sm):
1614 continue;
1615 default:
1616 break;
1617 }
1618
1619 mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1620 return(0);
1621 }
1622
1623 return(1);
1624 }
1625
1626 static int
1627 ebool(struct mdoc *mdoc)
1628 {
1629
1630 if (NULL == mdoc->last->child) {
1631 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1632 mdoc_node_delete(mdoc, mdoc->last);
1633 return(1);
1634 }
1635 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1636
1637 assert(MDOC_TEXT == mdoc->last->child->type);
1638
1639 if (0 == strcmp(mdoc->last->child->string, "on"))
1640 return(1);
1641 if (0 == strcmp(mdoc->last->child->string, "off"))
1642 return(1);
1643
1644 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1645 return(1);
1646 }
1647
1648 static int
1649 post_root(POST_ARGS)
1650 {
1651 int erc;
1652 struct mdoc_node *n;
1653
1654 erc = 0;
1655
1656 /* Check that we have a finished prologue. */
1657
1658 if ( ! (MDOC_PBODY & mdoc->flags)) {
1659 erc++;
1660 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1661 }
1662
1663 n = mdoc->first;
1664 assert(n);
1665
1666 /* Check that we begin with a proper `Sh'. */
1667
1668 if (NULL == n->child) {
1669 erc++;
1670 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1671 } else if (MDOC_BLOCK != n->child->type ||
1672 MDOC_Sh != n->child->tok) {
1673 erc++;
1674 /* Can this be lifted? See rxdebug.1 for example. */
1675 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1676 }
1677
1678 return(erc ? 0 : 1);
1679 }
1680
1681 static int
1682 post_st(POST_ARGS)
1683 {
1684 struct mdoc_node *ch;
1685 const char *p;
1686
1687 if (NULL == (ch = mdoc->last->child)) {
1688 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1689 mdoc_node_delete(mdoc, mdoc->last);
1690 return(1);
1691 }
1692
1693 assert(MDOC_TEXT == ch->type);
1694
1695 if (NULL == (p = mdoc_a2st(ch->string))) {
1696 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1697 mdoc_node_delete(mdoc, mdoc->last);
1698 } else {
1699 free(ch->string);
1700 ch->string = mandoc_strdup(p);
1701 }
1702
1703 return(1);
1704 }
1705
1706 static int
1707 post_rs(POST_ARGS)
1708 {
1709 struct mdoc_node *nn, *next, *prev;
1710 int i, j;
1711
1712 switch (mdoc->last->type) {
1713 case (MDOC_HEAD):
1714 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1715 return(1);
1716 case (MDOC_BODY):
1717 if (mdoc->last->child)
1718 break;
1719 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1720 return(1);
1721 default:
1722 return(1);
1723 }
1724
1725 /*
1726 * Make sure only certain types of nodes are allowed within the
1727 * the `Rs' body. Delete offending nodes and raise a warning.
1728 * Do this before re-ordering for the sake of clarity.
1729 */
1730
1731 next = NULL;
1732 for (nn = mdoc->last->child; nn; nn = next) {
1733 for (i = 0; i < RSORD_MAX; i++)
1734 if (nn->tok == rsord[i])
1735 break;
1736
1737 if (i < RSORD_MAX) {
1738 if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1739 mdoc->last->norm->Rs.quote_T++;
1740 next = nn->next;
1741 continue;
1742 }
1743
1744 next = nn->next;
1745 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1746 mdoc_node_delete(mdoc, nn);
1747 }
1748
1749 /*
1750 * Nothing to sort if only invalid nodes were found
1751 * inside the `Rs' body.
1752 */
1753
1754 if (NULL == mdoc->last->child)
1755 return(1);
1756
1757 /*
1758 * The full `Rs' block needs special handling to order the
1759 * sub-elements according to `rsord'. Pick through each element
1760 * and correctly order it. This is a insertion sort.
1761 */
1762
1763 next = NULL;
1764 for (nn = mdoc->last->child->next; nn; nn = next) {
1765 /* Determine order of `nn'. */
1766 for (i = 0; i < RSORD_MAX; i++)
1767 if (rsord[i] == nn->tok)
1768 break;
1769
1770 /*
1771 * Remove `nn' from the chain. This somewhat
1772 * repeats mdoc_node_unlink(), but since we're
1773 * just re-ordering, there's no need for the
1774 * full unlink process.
1775 */
1776
1777 if (NULL != (next = nn->next))
1778 next->prev = nn->prev;
1779
1780 if (NULL != (prev = nn->prev))
1781 prev->next = nn->next;
1782
1783 nn->prev = nn->next = NULL;
1784
1785 /*
1786 * Scan back until we reach a node that's
1787 * ordered before `nn'.
1788 */
1789
1790 for ( ; prev ; prev = prev->prev) {
1791 /* Determine order of `prev'. */
1792 for (j = 0; j < RSORD_MAX; j++)
1793 if (rsord[j] == prev->tok)
1794 break;
1795
1796 if (j <= i)
1797 break;
1798 }
1799
1800 /*
1801 * Set `nn' back into its correct place in front
1802 * of the `prev' node.
1803 */
1804
1805 nn->prev = prev;
1806
1807 if (prev) {
1808 if (prev->next)
1809 prev->next->prev = nn;
1810 nn->next = prev->next;
1811 prev->next = nn;
1812 } else {
1813 mdoc->last->child->prev = nn;
1814 nn->next = mdoc->last->child;
1815 mdoc->last->child = nn;
1816 }
1817 }
1818
1819 return(1);
1820 }
1821
1822 static int
1823 post_ns(POST_ARGS)
1824 {
1825
1826 if (MDOC_LINE & mdoc->last->flags)
1827 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1828 return(1);
1829 }
1830
1831 static int
1832 post_sh(POST_ARGS)
1833 {
1834
1835 if (MDOC_HEAD == mdoc->last->type)
1836 return(post_sh_head(mdoc));
1837 if (MDOC_BODY == mdoc->last->type)
1838 return(post_sh_body(mdoc));
1839
1840 return(1);
1841 }
1842
1843 static int
1844 post_sh_body(POST_ARGS)
1845 {
1846 struct mdoc_node *n;
1847
1848 if (SEC_NAME != mdoc->lastsec)
1849 return(1);
1850
1851 /*
1852 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1853 * macros (can have multiple `Nm' and one `Nd'). Note that the
1854 * children of the BODY declaration can also be "text".
1855 */
1856
1857 if (NULL == (n = mdoc->last->child)) {
1858 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1859 return(1);
1860 }
1861
1862 for ( ; n && n->next; n = n->next) {
1863 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1864 continue;
1865 if (MDOC_TEXT == n->type)
1866 continue;
1867 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1868 }
1869
1870 assert(n);
1871 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1872 return(1);
1873
1874 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1875 return(1);
1876 }
1877
1878 static int
1879 post_sh_head(POST_ARGS)
1880 {
1881 char buf[BUFSIZ];
1882 struct mdoc_node *n;
1883 enum mdoc_sec sec;
1884 int c;
1885
1886 /*
1887 * Process a new section. Sections are either "named" or
1888 * "custom". Custom sections are user-defined, while named ones
1889 * follow a conventional order and may only appear in certain
1890 * manual sections.
1891 */
1892
1893 sec = SEC_CUSTOM;
1894 buf[0] = '\0';
1895 if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1896 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1897 return(0);
1898 } else if (1 == c)
1899 sec = a2sec(buf);
1900
1901 /* The NAME should be first. */
1902
1903 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1904 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1905
1906 /* The SYNOPSIS gets special attention in other areas. */
1907
1908 if (SEC_SYNOPSIS == sec)
1909 mdoc->flags |= MDOC_SYNOPSIS;
1910 else
1911 mdoc->flags &= ~MDOC_SYNOPSIS;
1912
1913 /* Mark our last section. */
1914
1915 mdoc->lastsec = sec;
1916
1917 /*
1918 * Set the section attribute for the current HEAD, for its
1919 * parent BLOCK, and for the HEAD children; the latter can
1920 * only be TEXT nodes, so no recursion is needed.
1921 * For other blocks and elements, including .Sh BODY, this is
1922 * done when allocating the node data structures, but for .Sh
1923 * BLOCK and HEAD, the section is still unknown at that time.
1924 */
1925
1926 mdoc->last->parent->sec = sec;
1927 mdoc->last->sec = sec;
1928 for (n = mdoc->last->child; n; n = n->next)
1929 n->sec = sec;
1930
1931 /* We don't care about custom sections after this. */
1932
1933 if (SEC_CUSTOM == sec)
1934 return(1);
1935
1936 /*
1937 * Check whether our non-custom section is being repeated or is
1938 * out of order.
1939 */
1940
1941 if (sec == mdoc->lastnamed)
1942 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1943
1944 if (sec < mdoc->lastnamed)
1945 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1946
1947 /* Mark the last named section. */
1948
1949 mdoc->lastnamed = sec;
1950
1951 /* Check particular section/manual conventions. */
1952
1953 assert(mdoc->meta.msec);
1954
1955 switch (sec) {
1956 case (SEC_RETURN_VALUES):
1957 /* FALLTHROUGH */
1958 case (SEC_ERRORS):
1959 /* FALLTHROUGH */
1960 case (SEC_LIBRARY):
1961 if (*mdoc->meta.msec == '2')
1962 break;
1963 if (*mdoc->meta.msec == '3')
1964 break;
1965 if (*mdoc->meta.msec == '9')
1966 break;
1967 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1968 break;
1969 default:
1970 break;
1971 }
1972
1973 return(1);
1974 }
1975
1976 static int
1977 post_ignpar(POST_ARGS)
1978 {
1979 struct mdoc_node *np;
1980
1981 if (MDOC_BODY != mdoc->last->type)
1982 return(1);
1983
1984 if (NULL != (np = mdoc->last->child))
1985 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1986 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1987 mdoc_node_delete(mdoc, np);
1988 }
1989
1990 if (NULL != (np = mdoc->last->last))
1991 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1992 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1993 mdoc_node_delete(mdoc, np);
1994 }
1995
1996 return(1);
1997 }
1998
1999 static int
2000 pre_par(PRE_ARGS)
2001 {
2002
2003 if (NULL == mdoc->last)
2004 return(1);
2005 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2006 return(1);
2007
2008 /*
2009 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2010 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
2011 */
2012
2013 if (MDOC_Pp != mdoc->last->tok &&
2014 MDOC_Lp != mdoc->last->tok &&
2015 MDOC_br != mdoc->last->tok)
2016 return(1);
2017 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2018 return(1);
2019 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2020 return(1);
2021 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2022 return(1);
2023
2024 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2025 mdoc_node_delete(mdoc, mdoc->last);
2026 return(1);
2027 }
2028
2029 static int
2030 post_par(POST_ARGS)
2031 {
2032
2033 if (MDOC_ELEM != mdoc->last->type &&
2034 MDOC_BLOCK != mdoc->last->type)
2035 return(1);
2036
2037 if (NULL == mdoc->last->prev) {
2038 if (MDOC_Sh != mdoc->last->parent->tok &&
2039 MDOC_Ss != mdoc->last->parent->tok)
2040 return(1);
2041 } else {
2042 if (MDOC_Pp != mdoc->last->prev->tok &&
2043 MDOC_Lp != mdoc->last->prev->tok &&
2044 (MDOC_br != mdoc->last->tok ||
2045 (MDOC_sp != mdoc->last->prev->tok &&
2046 MDOC_br != mdoc->last->prev->tok)))
2047 return(1);
2048 }
2049
2050 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2051 mdoc_node_delete(mdoc, mdoc->last);
2052 return(1);
2053 }
2054
2055 static int
2056 pre_literal(PRE_ARGS)
2057 {
2058
2059 if (MDOC_BODY != n->type)
2060 return(1);
2061
2062 /*
2063 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2064 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2065 */
2066
2067 switch (n->tok) {
2068 case (MDOC_Dl):
2069 mdoc->flags |= MDOC_LITERAL;
2070 break;
2071 case (MDOC_Bd):
2072 if (DISP_literal == n->norm->Bd.type)
2073 mdoc->flags |= MDOC_LITERAL;
2074 if (DISP_unfilled == n->norm->Bd.type)
2075 mdoc->flags |= MDOC_LITERAL;
2076 break;
2077 default:
2078 abort();
2079 /* NOTREACHED */
2080 }
2081
2082 return(1);
2083 }
2084
2085 static int
2086 post_dd(POST_ARGS)
2087 {
2088 char buf[DATESIZE];
2089 struct mdoc_node *n;
2090 int c;
2091
2092 if (mdoc->meta.date)
2093 free(mdoc->meta.date);
2094
2095 n = mdoc->last;
2096 if (NULL == n->child || '\0' == n->child->string[0]) {
2097 mdoc->meta.date = mandoc_normdate
2098 (mdoc->parse, NULL, n->line, n->pos);
2099 return(1);
2100 }
2101
2102 buf[0] = '\0';
2103 if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2104 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2105 return(0);
2106 }
2107
2108 assert(c);
2109 mdoc->meta.date = mandoc_normdate
2110 (mdoc->parse, buf, n->line, n->pos);
2111
2112 return(1);
2113 }
2114
2115 static int
2116 post_dt(POST_ARGS)
2117 {
2118 struct mdoc_node *nn, *n;
2119 const char *cp;
2120 char *p;
2121
2122 n = mdoc->last;
2123
2124 if (mdoc->meta.title)
2125 free(mdoc->meta.title);
2126 if (mdoc->meta.vol)
2127 free(mdoc->meta.vol);
2128 if (mdoc->meta.arch)
2129 free(mdoc->meta.arch);
2130
2131 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2132
2133 /* First make all characters uppercase. */
2134
2135 if (NULL != (nn = n->child))
2136 for (p = nn->string; *p; p++) {
2137 if (toupper((unsigned char)*p) == *p)
2138 continue;
2139
2140 /*
2141 * FIXME: don't be lazy: have this make all
2142 * characters be uppercase and just warn once.
2143 */
2144 mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2145 break;
2146 }
2147
2148 /* Handles: `.Dt'
2149 * --> title = unknown, volume = local, msec = 0, arch = NULL
2150 */
2151
2152 if (NULL == (nn = n->child)) {
2153 /* XXX: make these macro values. */
2154 /* FIXME: warn about missing values. */
2155 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2156 mdoc->meta.vol = mandoc_strdup("LOCAL");
2157 mdoc->meta.msec = mandoc_strdup("1");
2158 return(1);
2159 }
2160
2161 /* Handles: `.Dt TITLE'
2162 * --> title = TITLE, volume = local, msec = 0, arch = NULL
2163 */
2164
2165 mdoc->meta.title = mandoc_strdup
2166 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2167
2168 if (NULL == (nn = nn->next)) {
2169 /* FIXME: warn about missing msec. */
2170 /* XXX: make this a macro value. */
2171 mdoc->meta.vol = mandoc_strdup("LOCAL");
2172 mdoc->meta.msec = mandoc_strdup("1");
2173 return(1);
2174 }
2175
2176 /* Handles: `.Dt TITLE SEC'
2177 * --> title = TITLE, volume = SEC is msec ?
2178 * format(msec) : SEC,
2179 * msec = SEC is msec ? atoi(msec) : 0,
2180 * arch = NULL
2181 */
2182
2183 cp = mandoc_a2msec(nn->string);
2184 if (cp) {
2185 mdoc->meta.vol = mandoc_strdup(cp);
2186 mdoc->meta.msec = mandoc_strdup(nn->string);
2187 } else {
2188 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2189 mdoc->meta.vol = mandoc_strdup(nn->string);
2190 mdoc->meta.msec = mandoc_strdup(nn->string);
2191 }
2192
2193 if (NULL == (nn = nn->next))
2194 return(1);
2195
2196 /* Handles: `.Dt TITLE SEC VOL'
2197 * --> title = TITLE, volume = VOL is vol ?
2198 * format(VOL) :
2199 * VOL is arch ? format(arch) :
2200 * VOL
2201 */
2202
2203 cp = mdoc_a2vol(nn->string);
2204 if (cp) {
2205 free(mdoc->meta.vol);
2206 mdoc->meta.vol = mandoc_strdup(cp);
2207 } else {
2208 cp = mdoc_a2arch(nn->string);
2209 if (NULL == cp) {
2210 mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH);
2211 free(mdoc->meta.vol);
2212 mdoc->meta.vol = mandoc_strdup(nn->string);
2213 } else
2214 mdoc->meta.arch = mandoc_strdup(cp);
2215 }
2216
2217 /* Ignore any subsequent parameters... */
2218 /* FIXME: warn about subsequent parameters. */
2219
2220 return(1);
2221 }
2222
2223 static int
2224 post_prol(POST_ARGS)
2225 {
2226 /*
2227 * Remove prologue macros from the document after they're
2228 * processed. The final document uses mdoc_meta for these
2229 * values and discards the originals.
2230 */
2231
2232 mdoc_node_delete(mdoc, mdoc->last);
2233 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2234 mdoc->flags |= MDOC_PBODY;
2235
2236 return(1);
2237 }
2238
2239 static int
2240 post_bx(POST_ARGS)
2241 {
2242 struct mdoc_node *n;
2243
2244 /*
2245 * Make `Bx's second argument always start with an uppercase
2246 * letter. Groff checks if it's an "accepted" term, but we just
2247 * uppercase blindly.
2248 */
2249
2250 n = mdoc->last->child;
2251 if (n && NULL != (n = n->next))
2252 *n->string = (char)toupper
2253 ((unsigned char)*n->string);
2254
2255 return(1);
2256 }
2257
2258 static int
2259 post_os(POST_ARGS)
2260 {
2261 struct mdoc_node *n;
2262 char buf[BUFSIZ];
2263 int c;
2264 #ifndef OSNAME
2265 struct utsname utsname;
2266 #endif
2267
2268 n = mdoc->last;
2269
2270 /*
2271 * Set the operating system by way of the `Os' macro.
2272 * The order of precedence is:
2273 * 1. the argument of the `Os' macro, unless empty
2274 * 2. the -Ios=foo command line argument, if provided
2275 * 3. -DOSNAME="\"foo\"", if provided during compilation
2276 * 4. "sysname release" from uname(3)
2277 */
2278
2279 free(mdoc->meta.os);
2280
2281 buf[0] = '\0';
2282 if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2283 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2284 return(0);
2285 }
2286
2287 assert(c);
2288
2289 if ('\0' == buf[0]) {
2290 if (mdoc->defos) {
2291 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2292 return(1);
2293 }
2294 #ifdef OSNAME
2295 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2296 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2297 return(0);
2298 }
2299 #else /*!OSNAME */
2300 if (-1 == uname(&utsname)) {
2301 mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2302 mdoc->meta.os = mandoc_strdup("UNKNOWN");
2303 return(post_prol(mdoc));
2304 }
2305
2306 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2307 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2308 return(0);
2309 }
2310 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2311 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2312 return(0);
2313 }
2314 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2315 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2316 return(0);
2317 }
2318 #endif /*!OSNAME*/
2319 }
2320
2321 mdoc->meta.os = mandoc_strdup(buf);
2322 return(1);
2323 }
2324
2325 static int
2326 post_std(POST_ARGS)
2327 {
2328 struct mdoc_node *nn, *n;
2329
2330 n = mdoc->last;
2331
2332 /*
2333 * Macros accepting `-std' as an argument have the name of the
2334 * current document (`Nm') filled in as the argument if it's not
2335 * provided.
2336 */
2337
2338 if (n->child)
2339 return(1);
2340
2341 if (NULL == mdoc->meta.name)
2342 return(1);
2343
2344 nn = n;
2345 mdoc->next = MDOC_NEXT_CHILD;
2346
2347 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2348 return(0);
2349
2350 mdoc->last = nn;
2351 return(1);
2352 }
2353
2354 /*
2355 * Concatenate a node, stopping at the first non-text.
2356 * Concatenation is separated by a single whitespace.
2357 * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2358 * encountered, 1 otherwise.
2359 */
2360 static int
2361 concat(char *p, const struct mdoc_node *n, size_t sz)
2362 {
2363
2364 for ( ; NULL != n; n = n->next) {
2365 if (MDOC_TEXT != n->type)
2366 return(0);
2367 if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2368 return(-1);
2369 if (strlcat(p, n->string, sz) >= sz)
2370 return(-1);
2371 concat(p, n->child, sz);
2372 }
2373
2374 return(1);
2375 }
2376
2377 static enum mdoc_sec
2378 a2sec(const char *p)
2379 {
2380 int i;
2381
2382 for (i = 0; i < (int)SEC__MAX; i++)
2383 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2384 return((enum mdoc_sec)i);
2385
2386 return(SEC_CUSTOM);
2387 }
2388
2389 static size_t
2390 macro2len(enum mdoct macro)
2391 {
2392
2393 switch (macro) {
2394 case(MDOC_Ad):
2395 return(12);
2396 case(MDOC_Ao):
2397 return(12);
2398 case(MDOC_An):
2399 return(12);
2400 case(MDOC_Aq):
2401 return(12);
2402 case(MDOC_Ar):
2403 return(12);
2404 case(MDOC_Bo):
2405 return(12);
2406 case(MDOC_Bq):
2407 return(12);
2408 case(MDOC_Cd):
2409 return(12);
2410 case(MDOC_Cm):
2411 return(10);
2412 case(MDOC_Do):
2413 return(10);
2414 case(MDOC_Dq):
2415 return(12);
2416 case(MDOC_Dv):
2417 return(12);
2418 case(MDOC_Eo):
2419 return(12);
2420 case(MDOC_Em):
2421 return(10);
2422 case(MDOC_Er):
2423 return(17);
2424 case(MDOC_Ev):
2425 return(15);
2426 case(MDOC_Fa):
2427 return(12);
2428 case(MDOC_Fl):
2429 return(10);
2430 case(MDOC_Fo):
2431 return(16);
2432 case(MDOC_Fn):
2433 return(16);
2434 case(MDOC_Ic):
2435 return(10);
2436 case(MDOC_Li):
2437 return(16);
2438 case(MDOC_Ms):
2439 return(6);
2440 case(MDOC_Nm):
2441 return(10);
2442 case(MDOC_No):
2443 return(12);
2444 case(MDOC_Oo):
2445 return(10);
2446 case(MDOC_Op):
2447 return(14);
2448 case(MDOC_Pa):
2449 return(32);
2450 case(MDOC_Pf):
2451 return(12);
2452 case(MDOC_Po):
2453 return(12);
2454 case(MDOC_Pq):
2455 return(12);
2456 case(MDOC_Ql):
2457 return(16);
2458 case(MDOC_Qo):
2459 return(12);
2460 case(MDOC_So):
2461 return(12);
2462 case(MDOC_Sq):
2463 return(12);
2464 case(MDOC_Sy):
2465 return(6);
2466 case(MDOC_Sx):
2467 return(16);
2468 case(MDOC_Tn):
2469 return(10);
2470 case(MDOC_Va):
2471 return(12);
2472 case(MDOC_Vt):
2473 return(12);
2474 case(MDOC_Xr):
2475 return(10);
2476 default:
2477 break;
2478 };
2479 return(0);
2480 }