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