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