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