Import mdocml-1.11.1
[dragonfly.git] / contrib / mdocml / mdoc_validate.c
CommitLineData
60e1e752 1/* $Id: mdoc_validate.c,v 1.166 2011/04/03 09:53:50 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 };
158static v_post posts_sh[] = { post_ignpar, hwarn_ge1, bwarn_ge1, post_sh, NULL };
159static v_post posts_sp[] = { ewarn_le1, NULL };
160static v_post posts_ss[] = { post_ignpar, hwarn_ge1, bwarn_ge1, NULL };
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{
548 int c;
549 size_t sz;
550
551 for ( ; *p; p++, pos++) {
552 sz = strcspn(p, "\t\\");
553 p += (int)sz;
554
555 if ('\0' == *p)
556 break;
557
558 pos += (int)sz;
559
560 if ('\t' == *p) {
561 if ( ! (MDOC_LITERAL & m->flags))
562 mdoc_pmsg(m, ln, pos, MANDOCERR_BADTAB);
563 continue;
564 }
565
566 if (0 == (c = mandoc_special(p))) {
567 mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE);
568 continue;
569 }
570
571 p += c - 1;
572 pos += c - 1;
573 }
574}
575
576static int
577check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
578{
579
580 assert(n->parent);
581 if ((MDOC_ROOT == t || tok == n->parent->tok) &&
582 (t == n->parent->type))
583 return(1);
584
60e1e752
SW
585 mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line,
586 n->pos, "want parent %s", MDOC_ROOT == t ?
587 "<root>" : mdoc_macronames[tok]);
80387638
SW
588 return(0);
589}
590
591
592static int
593pre_display(PRE_ARGS)
594{
595 struct mdoc_node *node;
596
597 if (MDOC_BLOCK != n->type)
598 return(1);
599
600 for (node = mdoc->last->parent; node; node = node->parent)
601 if (MDOC_BLOCK == node->type)
602 if (MDOC_Bd == node->tok)
603 break;
604
605 if (node)
606 mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
607
608 return(1);
609}
610
611
612static int
613pre_bl(PRE_ARGS)
614{
615 int i, comp, dup;
616 const char *offs, *width;
617 enum mdoc_list lt;
618 struct mdoc_node *np;
619
620 if (MDOC_BLOCK != n->type) {
621 if (ENDBODY_NOT != n->end) {
622 assert(n->pending);
623 np = n->pending->parent;
624 } else
625 np = n->parent;
626
627 assert(np);
628 assert(MDOC_BLOCK == np->type);
629 assert(MDOC_Bl == np->tok);
630 return(1);
631 }
632
633 /*
634 * First figure out which kind of list to use: bind ourselves to
635 * the first mentioned list type and warn about any remaining
636 * ones. If we find no list type, we default to LIST_item.
637 */
638
639 /* LINTED */
640 for (i = 0; n->args && i < (int)n->args->argc; i++) {
641 lt = LIST__NONE;
642 dup = comp = 0;
643 width = offs = NULL;
644 switch (n->args->argv[i].arg) {
645 /* Set list types. */
646 case (MDOC_Bullet):
647 lt = LIST_bullet;
648 break;
649 case (MDOC_Dash):
650 lt = LIST_dash;
651 break;
652 case (MDOC_Enum):
653 lt = LIST_enum;
654 break;
655 case (MDOC_Hyphen):
656 lt = LIST_hyphen;
657 break;
658 case (MDOC_Item):
659 lt = LIST_item;
660 break;
661 case (MDOC_Tag):
662 lt = LIST_tag;
663 break;
664 case (MDOC_Diag):
665 lt = LIST_diag;
666 break;
667 case (MDOC_Hang):
668 lt = LIST_hang;
669 break;
670 case (MDOC_Ohang):
671 lt = LIST_ohang;
672 break;
673 case (MDOC_Inset):
674 lt = LIST_inset;
675 break;
676 case (MDOC_Column):
677 lt = LIST_column;
678 break;
679 /* Set list arguments. */
680 case (MDOC_Compact):
681 dup = n->norm->Bl.comp;
682 comp = 1;
683 break;
684 case (MDOC_Width):
685 dup = (NULL != n->norm->Bl.width);
686 width = n->args->argv[i].value[0];
687 break;
688 case (MDOC_Offset):
689 /* NB: this can be empty! */
690 if (n->args->argv[i].sz) {
691 offs = n->args->argv[i].value[0];
692 dup = (NULL != n->norm->Bl.offs);
693 break;
694 }
695 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
696 break;
697 default:
698 continue;
699 }
700
701 /* Check: duplicate auxiliary arguments. */
702
703 if (dup)
704 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
705
706 if (comp && ! dup)
707 n->norm->Bl.comp = comp;
708 if (offs && ! dup)
709 n->norm->Bl.offs = offs;
710 if (width && ! dup)
711 n->norm->Bl.width = width;
712
713 /* Check: multiple list types. */
714
715 if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
716 mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
717
718 /* Assign list type. */
719
720 if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
721 n->norm->Bl.type = lt;
722 /* Set column information, too. */
723 if (LIST_column == lt) {
724 n->norm->Bl.ncols =
725 n->args->argv[i].sz;
726 n->norm->Bl.cols = (const char **)
727 n->args->argv[i].value;
728 }
729 }
730
731 /* The list type should come first. */
732
733 if (n->norm->Bl.type == LIST__NONE)
734 if (n->norm->Bl.width ||
735 n->norm->Bl.offs ||
736 n->norm->Bl.comp)
737 mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
738
739 continue;
740 }
741
742 /* Allow lists to default to LIST_item. */
743
744 if (LIST__NONE == n->norm->Bl.type) {
745 mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
746 n->norm->Bl.type = LIST_item;
747 }
748
749 /*
750 * Validate the width field. Some list types don't need width
751 * types and should be warned about them. Others should have it
752 * and must also be warned.
753 */
754
755 switch (n->norm->Bl.type) {
756 case (LIST_tag):
757 if (n->norm->Bl.width)
758 break;
759 mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
760 break;
761 case (LIST_column):
762 /* FALLTHROUGH */
763 case (LIST_diag):
764 /* FALLTHROUGH */
765 case (LIST_ohang):
766 /* FALLTHROUGH */
767 case (LIST_inset):
768 /* FALLTHROUGH */
769 case (LIST_item):
770 if (n->norm->Bl.width)
771 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
772 break;
773 default:
774 break;
775 }
776
777 return(1);
778}
779
780
781static int
782pre_bd(PRE_ARGS)
783{
784 int i, dup, comp;
785 enum mdoc_disp dt;
786 const char *offs;
787 struct mdoc_node *np;
788
789 if (MDOC_BLOCK != n->type) {
790 if (ENDBODY_NOT != n->end) {
791 assert(n->pending);
792 np = n->pending->parent;
793 } else
794 np = n->parent;
795
796 assert(np);
797 assert(MDOC_BLOCK == np->type);
798 assert(MDOC_Bd == np->tok);
799 return(1);
800 }
801
802 /* LINTED */
803 for (i = 0; n->args && i < (int)n->args->argc; i++) {
804 dt = DISP__NONE;
805 dup = comp = 0;
806 offs = NULL;
807
808 switch (n->args->argv[i].arg) {
809 case (MDOC_Centred):
810 dt = DISP_centred;
811 break;
812 case (MDOC_Ragged):
813 dt = DISP_ragged;
814 break;
815 case (MDOC_Unfilled):
816 dt = DISP_unfilled;
817 break;
818 case (MDOC_Filled):
819 dt = DISP_filled;
820 break;
821 case (MDOC_Literal):
822 dt = DISP_literal;
823 break;
824 case (MDOC_File):
825 mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
826 return(0);
827 case (MDOC_Offset):
828 /* NB: this can be empty! */
829 if (n->args->argv[i].sz) {
830 offs = n->args->argv[i].value[0];
831 dup = (NULL != n->norm->Bd.offs);
832 break;
833 }
834 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
835 break;
836 case (MDOC_Compact):
837 comp = 1;
838 dup = n->norm->Bd.comp;
839 break;
840 default:
841 abort();
842 /* NOTREACHED */
843 }
844
845 /* Check whether we have duplicates. */
846
847 if (dup)
848 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
849
850 /* Make our auxiliary assignments. */
851
852 if (offs && ! dup)
853 n->norm->Bd.offs = offs;
854 if (comp && ! dup)
855 n->norm->Bd.comp = comp;
856
857 /* Check whether a type has already been assigned. */
858
859 if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
860 mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
861
862 /* Make our type assignment. */
863
864 if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
865 n->norm->Bd.type = dt;
866 }
867
868 if (DISP__NONE == n->norm->Bd.type) {
869 mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
870 n->norm->Bd.type = DISP_ragged;
871 }
872
873 return(1);
874}
875
876
877static int
878pre_ss(PRE_ARGS)
879{
880
881 if (MDOC_BLOCK != n->type)
882 return(1);
883 return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
884}
885
886
887static int
888pre_sh(PRE_ARGS)
889{
890
891 if (MDOC_BLOCK != n->type)
892 return(1);
893
894 mdoc->regs->regs[(int)REG_nS].set = 0;
895 return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
896}
897
898
899static int
900pre_it(PRE_ARGS)
901{
902
903 if (MDOC_BLOCK != n->type)
904 return(1);
905
906 return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
907}
908
909
910static int
911pre_an(PRE_ARGS)
912{
913 int i;
914
915 if (NULL == n->args)
916 return(1);
917
918 for (i = 1; i < (int)n->args->argc; i++)
919 mdoc_pmsg(mdoc, n->args->argv[i].line,
920 n->args->argv[i].pos, MANDOCERR_IGNARGV);
921
922 if (MDOC_Split == n->args->argv[0].arg)
923 n->norm->An.auth = AUTH_split;
924 else if (MDOC_Nosplit == n->args->argv[0].arg)
925 n->norm->An.auth = AUTH_nosplit;
926 else
927 abort();
928
929 return(1);
930}
931
932static int
933pre_std(PRE_ARGS)
934{
935
936 if (n->args && 1 == n->args->argc)
937 if (MDOC_Std == n->args->argv[0].arg)
938 return(1);
939
940 mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
941 return(1);
942}
943
944static int
945pre_dt(PRE_ARGS)
946{
947
60e1e752 948 if (NULL == mdoc->meta.date || mdoc->meta.os)
80387638
SW
949 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
950
951 if (mdoc->meta.title)
952 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
953
954 return(1);
955}
956
957static int
958pre_os(PRE_ARGS)
959{
960
60e1e752 961 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
80387638
SW
962 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
963
964 if (mdoc->meta.os)
965 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
966
967 return(1);
968}
969
970static int
971pre_dd(PRE_ARGS)
972{
973
974 if (mdoc->meta.title || mdoc->meta.os)
975 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
976
977 if (mdoc->meta.date)
978 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
979
980 return(1);
981}
982
983
984static int
985post_bf(POST_ARGS)
986{
987 struct mdoc_node *np;
988 enum mdocargt arg;
989
990 /*
991 * Unlike other data pointers, these are "housed" by the HEAD
992 * element, which contains the goods.
993 */
994
995 if (MDOC_HEAD != mdoc->last->type) {
996 if (ENDBODY_NOT != mdoc->last->end) {
997 assert(mdoc->last->pending);
998 np = mdoc->last->pending->parent->head;
999 } else if (MDOC_BLOCK != mdoc->last->type) {
1000 np = mdoc->last->parent->head;
1001 } else
1002 np = mdoc->last->head;
1003
1004 assert(np);
1005 assert(MDOC_HEAD == np->type);
1006 assert(MDOC_Bf == np->tok);
1007 return(1);
1008 }
1009
1010 np = mdoc->last;
1011 assert(MDOC_BLOCK == np->parent->type);
1012 assert(MDOC_Bf == np->parent->tok);
1013
1014 /*
1015 * Cannot have both argument and parameter.
1016 * If neither is specified, let it through with a warning.
1017 */
1018
1019 if (np->parent->args && np->child) {
1020 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1021 return(0);
1022 } else if (NULL == np->parent->args && NULL == np->child) {
1023 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1024 return(1);
1025 }
1026
1027 /* Extract argument into data. */
1028
1029 if (np->parent->args) {
1030 arg = np->parent->args->argv[0].arg;
1031 if (MDOC_Emphasis == arg)
1032 np->norm->Bf.font = FONT_Em;
1033 else if (MDOC_Literal == arg)
1034 np->norm->Bf.font = FONT_Li;
1035 else if (MDOC_Symbolic == arg)
1036 np->norm->Bf.font = FONT_Sy;
1037 else
1038 abort();
1039 return(1);
1040 }
1041
1042 /* Extract parameter into data. */
1043
1044 if (0 == strcmp(np->child->string, "Em"))
1045 np->norm->Bf.font = FONT_Em;
1046 else if (0 == strcmp(np->child->string, "Li"))
1047 np->norm->Bf.font = FONT_Li;
1048 else if (0 == strcmp(np->child->string, "Sy"))
1049 np->norm->Bf.font = FONT_Sy;
1050 else
1051 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1052
1053 return(1);
1054}
1055
1056static int
1057post_lb(POST_ARGS)
1058{
1059 const char *p;
1060 char *buf;
1061 size_t sz;
1062
1063 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1064
1065 assert(mdoc->last->child);
1066 assert(MDOC_TEXT == mdoc->last->child->type);
1067
1068 p = mdoc_a2lib(mdoc->last->child->string);
1069
1070 /* If lookup ok, replace with table value. */
1071
1072 if (p) {
1073 free(mdoc->last->child->string);
1074 mdoc->last->child->string = mandoc_strdup(p);
1075 return(1);
1076 }
1077
1078 /* If not, use "library ``xxxx''. */
1079
1080 sz = strlen(mdoc->last->child->string) +
1081 2 + strlen("\\(lqlibrary\\(rq");
1082 buf = mandoc_malloc(sz);
1083 snprintf(buf, sz, "library \\(lq%s\\(rq",
1084 mdoc->last->child->string);
1085 free(mdoc->last->child->string);
1086 mdoc->last->child->string = buf;
1087 return(1);
1088}
1089
1090static int
1091post_eoln(POST_ARGS)
1092{
1093
1094 if (mdoc->last->child)
1095 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1096 return(1);
1097}
1098
1099
1100static int
1101post_vt(POST_ARGS)
1102{
1103 const struct mdoc_node *n;
1104
1105 /*
1106 * The Vt macro comes in both ELEM and BLOCK form, both of which
1107 * have different syntaxes (yet more context-sensitive
60e1e752
SW
1108 * behaviour). ELEM types must have a child, which is already
1109 * guaranteed by the in_line parsing routine; BLOCK types,
80387638
SW
1110 * specifically the BODY, should only have TEXT children.
1111 */
1112
80387638
SW
1113 if (MDOC_BODY != mdoc->last->type)
1114 return(1);
1115
1116 for (n = mdoc->last->child; n; n = n->next)
1117 if (MDOC_TEXT != n->type)
1118 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1119
1120 return(1);
1121}
1122
1123
1124static int
1125post_nm(POST_ARGS)
1126{
1127 char buf[BUFSIZ];
1128
1129 /* If no child specified, make sure we have the meta name. */
1130
1131 if (NULL == mdoc->last->child && NULL == mdoc->meta.name) {
1132 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1133 return(1);
1134 } else if (mdoc->meta.name)
1135 return(1);
1136
1137 /* If no meta name, set it from the child. */
1138
1139 if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ))
1140 return(0);
1141
1142 mdoc->meta.name = mandoc_strdup(buf);
1143
1144 return(1);
1145}
1146
1147static int
1148post_literal(POST_ARGS)
1149{
1150
1151 /*
1152 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1153 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1154 * this in literal mode, but it doesn't hurt to just switch it
1155 * off in general since displays can't be nested.
1156 */
1157
1158 if (MDOC_BODY == mdoc->last->type)
1159 mdoc->flags &= ~MDOC_LITERAL;
1160
1161 return(1);
1162}
1163
1164static int
1165post_defaults(POST_ARGS)
1166{
1167 struct mdoc_node *nn;
1168
1169 /*
1170 * The `Ar' defaults to "file ..." if no value is provided as an
1171 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1172 * gets an empty string.
1173 */
1174
1175 if (mdoc->last->child)
1176 return(1);
1177
1178 nn = mdoc->last;
1179 mdoc->next = MDOC_NEXT_CHILD;
1180
1181 switch (nn->tok) {
1182 case (MDOC_Ar):
1183 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1184 return(0);
1185 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1186 return(0);
1187 break;
1188 case (MDOC_At):
1189 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1190 return(0);
1191 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1192 return(0);
1193 break;
1194 case (MDOC_Li):
1195 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1196 return(0);
1197 break;
1198 case (MDOC_Pa):
1199 /* FALLTHROUGH */
1200 case (MDOC_Mt):
1201 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1202 return(0);
1203 break;
1204 default:
1205 abort();
1206 /* NOTREACHED */
1207 }
1208
1209 mdoc->last = nn;
1210 return(1);
1211}
1212
1213static int
1214post_at(POST_ARGS)
1215{
1216 const char *p, *q;
1217 char *buf;
1218 size_t sz;
1219
1220 /*
1221 * If we have a child, look it up in the standard keys. If a
1222 * key exist, use that instead of the child; if it doesn't,
1223 * prefix "AT&T UNIX " to the existing data.
1224 */
1225
1226 if (NULL == mdoc->last->child)
1227 return(1);
1228
1229 assert(MDOC_TEXT == mdoc->last->child->type);
1230 p = mdoc_a2att(mdoc->last->child->string);
1231
1232 if (p) {
1233 free(mdoc->last->child->string);
1234 mdoc->last->child->string = mandoc_strdup(p);
1235 } else {
1236 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1237 p = "AT&T UNIX ";
1238 q = mdoc->last->child->string;
1239 sz = strlen(p) + strlen(q) + 1;
1240 buf = mandoc_malloc(sz);
1241 strlcpy(buf, p, sz);
1242 strlcat(buf, q, sz);
1243 free(mdoc->last->child->string);
1244 mdoc->last->child->string = buf;
1245 }
1246
1247 return(1);
1248}
1249
1250static int
1251post_an(POST_ARGS)
1252{
1253 struct mdoc_node *np;
1254
1255 np = mdoc->last;
60e1e752
SW
1256 if (AUTH__NONE == np->norm->An.auth) {
1257 if (0 == np->child)
1258 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1259 } else if (np->child)
80387638 1260 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
80387638 1261
80387638
SW
1262 return(1);
1263}
1264
1265
1266static int
1267post_it(POST_ARGS)
1268{
60e1e752 1269 int i, cols;
80387638
SW
1270 enum mdoc_list lt;
1271 struct mdoc_node *n, *c;
1272 enum mandocerr er;
1273
1274 if (MDOC_BLOCK != mdoc->last->type)
1275 return(1);
1276
1277 n = mdoc->last->parent->parent;
1278 lt = n->norm->Bl.type;
1279
1280 if (LIST__NONE == lt) {
1281 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1282 return(1);
1283 }
1284
1285 switch (lt) {
1286 case (LIST_tag):
1287 if (mdoc->last->head->child)
1288 break;
1289 /* FIXME: give this a dummy value. */
1290 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1291 break;
1292 case (LIST_hang):
1293 /* FALLTHROUGH */
1294 case (LIST_ohang):
1295 /* FALLTHROUGH */
1296 case (LIST_inset):
1297 /* FALLTHROUGH */
1298 case (LIST_diag):
1299 if (NULL == mdoc->last->head->child)
1300 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1301 break;
1302 case (LIST_bullet):
1303 /* FALLTHROUGH */
1304 case (LIST_dash):
1305 /* FALLTHROUGH */
1306 case (LIST_enum):
1307 /* FALLTHROUGH */
1308 case (LIST_hyphen):
1309 if (NULL == mdoc->last->body->child)
1310 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1311 /* FALLTHROUGH */
1312 case (LIST_item):
1313 if (mdoc->last->head->child)
1314 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1315 break;
1316 case (LIST_column):
1317 cols = (int)n->norm->Bl.ncols;
1318
1319 assert(NULL == mdoc->last->head->child);
1320
1321 if (NULL == mdoc->last->body->child)
1322 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1323
1324 for (i = 0, c = mdoc->last->child; c; c = c->next)
1325 if (MDOC_BODY == c->type)
1326 i++;
1327
1328 if (i < cols)
1329 er = MANDOCERR_ARGCOUNT;
1330 else if (i == cols || i == cols + 1)
1331 break;
1332 else
1333 er = MANDOCERR_SYNTARGCOUNT;
1334
60e1e752
SW
1335 mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1336 mdoc->last->pos,
80387638 1337 "columns == %d (have %d)", cols, i);
60e1e752 1338 return(MANDOCERR_ARGCOUNT == er);
80387638
SW
1339 default:
1340 break;
1341 }
1342
1343 return(1);
1344}
1345
1346static int
1347post_bl_block(POST_ARGS)
1348{
1349 struct mdoc_node *n;
1350
1351 /*
1352 * These are fairly complicated, so we've broken them into two
1353 * functions. post_bl_block_tag() is called when a -tag is
1354 * specified, but no -width (it must be guessed). The second
1355 * when a -width is specified (macro indicators must be
1356 * rewritten into real lengths).
1357 */
1358
1359 n = mdoc->last;
1360
1361 if (LIST_tag == n->norm->Bl.type &&
1362 NULL == n->norm->Bl.width) {
1363 if ( ! post_bl_block_tag(mdoc))
1364 return(0);
1365 } else if (NULL != n->norm->Bl.width) {
1366 if ( ! post_bl_block_width(mdoc))
1367 return(0);
1368 } else
1369 return(1);
1370
1371 assert(n->norm->Bl.width);
1372 return(1);
1373}
1374
1375static int
1376post_bl_block_width(POST_ARGS)
1377{
1378 size_t width;
1379 int i;
1380 enum mdoct tok;
1381 struct mdoc_node *n;
1382 char buf[NUMSIZ];
1383
1384 n = mdoc->last;
1385
1386 /*
1387 * Calculate the real width of a list from the -width string,
1388 * which may contain a macro (with a known default width), a
1389 * literal string, or a scaling width.
1390 *
1391 * If the value to -width is a macro, then we re-write it to be
1392 * the macro's width as set in share/tmac/mdoc/doc-common.
1393 */
1394
1395 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1396 width = 6;
1397 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1398 return(1);
60e1e752 1399 else if (0 == (width = macro2len(tok))) {
80387638
SW
1400 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1401 return(1);
1402 }
1403
1404 /* The value already exists: free and reallocate it. */
1405
1406 assert(n->args);
1407
1408 for (i = 0; i < (int)n->args->argc; i++)
1409 if (MDOC_Width == n->args->argv[i].arg)
1410 break;
1411
1412 assert(i < (int)n->args->argc);
1413
1414 snprintf(buf, NUMSIZ, "%zun", width);
1415 free(n->args->argv[i].value[0]);
1416 n->args->argv[i].value[0] = mandoc_strdup(buf);
1417
1418 /* Set our width! */
1419 n->norm->Bl.width = n->args->argv[i].value[0];
1420 return(1);
1421}
1422
1423static int
1424post_bl_block_tag(POST_ARGS)
1425{
1426 struct mdoc_node *n, *nn;
1427 size_t sz, ssz;
1428 int i;
1429 char buf[NUMSIZ];
1430
1431 /*
1432 * Calculate the -width for a `Bl -tag' list if it hasn't been
1433 * provided. Uses the first head macro. NOTE AGAIN: this is
1434 * ONLY if the -width argument has NOT been provided. See
1435 * post_bl_block_width() for converting the -width string.
1436 */
1437
1438 sz = 10;
1439 n = mdoc->last;
1440
1441 for (nn = n->body->child; nn; nn = nn->next) {
1442 if (MDOC_It != nn->tok)
1443 continue;
1444
1445 assert(MDOC_BLOCK == nn->type);
1446 nn = nn->head->child;
1447
1448 if (nn == NULL)
1449 break;
1450
1451 if (MDOC_TEXT == nn->type) {
1452 sz = strlen(nn->string) + 1;
1453 break;
1454 }
1455
60e1e752 1456 if (0 != (ssz = macro2len(nn->tok)))
80387638
SW
1457 sz = ssz;
1458
1459 break;
1460 }
1461
1462 /* Defaults to ten ens. */
1463
1464 snprintf(buf, NUMSIZ, "%zun", sz);
1465
1466 /*
1467 * We have to dynamically add this to the macro's argument list.
1468 * We're guaranteed that a MDOC_Width doesn't already exist.
1469 */
1470
1471 assert(n->args);
1472 i = (int)(n->args->argc)++;
1473
1474 n->args->argv = mandoc_realloc(n->args->argv,
1475 n->args->argc * sizeof(struct mdoc_argv));
1476
1477 n->args->argv[i].arg = MDOC_Width;
1478 n->args->argv[i].line = n->line;
1479 n->args->argv[i].pos = n->pos;
1480 n->args->argv[i].sz = 1;
1481 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1482 n->args->argv[i].value[0] = mandoc_strdup(buf);
1483
1484 /* Set our width! */
1485 n->norm->Bl.width = n->args->argv[i].value[0];
1486 return(1);
1487}
1488
1489
1490static int
1491post_bl_head(POST_ARGS)
1492{
1493 struct mdoc_node *np, *nn, *nnp;
1494 int i, j;
1495
1496 if (LIST_column != mdoc->last->norm->Bl.type)
1497 /* FIXME: this should be ERROR class... */
1498 return(hwarn_eq0(mdoc));
1499
1500 /*
1501 * Convert old-style lists, where the column width specifiers
1502 * trail as macro parameters, to the new-style ("normal-form")
1503 * lists where they're argument values following -column.
1504 */
1505
1506 /* First, disallow both types and allow normal-form. */
1507
1508 /*
1509 * TODO: technically, we can accept both and just merge the two
1510 * lists, but I'll leave that for another day.
1511 */
1512
1513 if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1514 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1515 return(0);
1516 } else if (NULL == mdoc->last->child)
1517 return(1);
1518
1519 np = mdoc->last->parent;
1520 assert(np->args);
1521
1522 for (j = 0; j < (int)np->args->argc; j++)
1523 if (MDOC_Column == np->args->argv[j].arg)
1524 break;
1525
1526 assert(j < (int)np->args->argc);
1527 assert(0 == np->args->argv[j].sz);
1528
1529 /*
1530 * Accomodate for new-style groff column syntax. Shuffle the
1531 * child nodes, all of which must be TEXT, as arguments for the
1532 * column field. Then, delete the head children.
1533 */
1534
1535 np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1536 np->args->argv[j].value = mandoc_malloc
1537 ((size_t)mdoc->last->nchild * sizeof(char *));
1538
1539 mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1540 mdoc->last->norm->Bl.cols = (const char **)np->args->argv[j].value;
1541
1542 for (i = 0, nn = mdoc->last->child; nn; i++) {
1543 np->args->argv[j].value[i] = nn->string;
1544 nn->string = NULL;
1545 nnp = nn;
1546 nn = nn->next;
1547 mdoc_node_delete(NULL, nnp);
1548 }
1549
1550 mdoc->last->nchild = 0;
1551 mdoc->last->child = NULL;
1552
1553 return(1);
1554}
1555
1556static int
1557post_bl(POST_ARGS)
1558{
1559 struct mdoc_node *n;
1560
1561 if (MDOC_HEAD == mdoc->last->type)
1562 return(post_bl_head(mdoc));
1563 if (MDOC_BLOCK == mdoc->last->type)
1564 return(post_bl_block(mdoc));
1565 if (MDOC_BODY != mdoc->last->type)
1566 return(1);
1567
1568 for (n = mdoc->last->child; n; n = n->next) {
1569 switch (n->tok) {
1570 case (MDOC_Lp):
1571 /* FALLTHROUGH */
1572 case (MDOC_Pp):
1573 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1574 /* FALLTHROUGH */
1575 case (MDOC_It):
1576 /* FALLTHROUGH */
1577 case (MDOC_Sm):
1578 continue;
1579 default:
1580 break;
1581 }
1582
1583 mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1584 return(0);
1585 }
1586
1587 return(1);
1588}
1589
1590static int
1591ebool(struct mdoc *mdoc)
1592{
1593
1594 if (NULL == mdoc->last->child) {
1595 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1596 mdoc_node_delete(mdoc, mdoc->last);
1597 return(1);
1598 }
1599 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1600
1601 assert(MDOC_TEXT == mdoc->last->child->type);
1602
1603 if (0 == strcmp(mdoc->last->child->string, "on"))
1604 return(1);
1605 if (0 == strcmp(mdoc->last->child->string, "off"))
1606 return(1);
1607
1608 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1609 return(1);
1610}
1611
1612static int
1613post_root(POST_ARGS)
1614{
1615 int erc;
1616 struct mdoc_node *n;
1617
1618 erc = 0;
1619
1620 /* Check that we have a finished prologue. */
1621
1622 if ( ! (MDOC_PBODY & mdoc->flags)) {
1623 erc++;
1624 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1625 }
1626
1627 n = mdoc->first;
1628 assert(n);
1629
1630 /* Check that we begin with a proper `Sh'. */
1631
1632 if (NULL == n->child) {
1633 erc++;
1634 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1635 } else if (MDOC_BLOCK != n->child->type ||
1636 MDOC_Sh != n->child->tok) {
1637 erc++;
1638 /* Can this be lifted? See rxdebug.1 for example. */
1639 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1640 }
1641
1642 return(erc ? 0 : 1);
1643}
1644
1645static int
1646post_st(POST_ARGS)
1647{
1648 struct mdoc_node *ch;
1649 const char *p;
1650
1651 if (NULL == (ch = mdoc->last->child)) {
1652 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1653 mdoc_node_delete(mdoc, mdoc->last);
1654 return(1);
1655 }
1656
1657 assert(MDOC_TEXT == ch->type);
1658
1659 if (NULL == (p = mdoc_a2st(ch->string))) {
1660 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1661 mdoc_node_delete(mdoc, mdoc->last);
1662 } else {
1663 free(ch->string);
1664 ch->string = mandoc_strdup(p);
1665 }
1666
1667 return(1);
1668}
1669
1670static int
1671post_rs(POST_ARGS)
1672{
1673 struct mdoc_node *nn, *next, *prev;
1674 int i, j;
1675
1676 switch (mdoc->last->type) {
1677 case (MDOC_HEAD):
1678 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1679 return(1);
1680 case (MDOC_BODY):
1681 if (mdoc->last->child)
1682 break;
1683 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1684 return(1);
1685 default:
1686 return(1);
1687 }
1688
1689 /*
1690 * Make sure only certain types of nodes are allowed within the
1691 * the `Rs' body. Delete offending nodes and raise a warning.
1692 * Do this before re-ordering for the sake of clarity.
1693 */
1694
1695 next = NULL;
1696 for (nn = mdoc->last->child; nn; nn = next) {
1697 for (i = 0; i < RSORD_MAX; i++)
1698 if (nn->tok == rsord[i])
1699 break;
1700
1701 if (i < RSORD_MAX) {
60e1e752
SW
1702 if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1703 mdoc->last->norm->Rs.quote_T++;
80387638
SW
1704 next = nn->next;
1705 continue;
1706 }
1707
1708 next = nn->next;
1709 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1710 mdoc_node_delete(mdoc, nn);
1711 }
1712
1713 /*
1714 * The full `Rs' block needs special handling to order the
1715 * sub-elements according to `rsord'. Pick through each element
1716 * and correctly order it. This is a insertion sort.
1717 */
1718
1719 next = NULL;
1720 for (nn = mdoc->last->child->next; nn; nn = next) {
1721 /* Determine order of `nn'. */
1722 for (i = 0; i < RSORD_MAX; i++)
1723 if (rsord[i] == nn->tok)
1724 break;
1725
1726 /*
1727 * Remove `nn' from the chain. This somewhat
1728 * repeats mdoc_node_unlink(), but since we're
1729 * just re-ordering, there's no need for the
1730 * full unlink process.
1731 */
1732
1733 if (NULL != (next = nn->next))
1734 next->prev = nn->prev;
1735
1736 if (NULL != (prev = nn->prev))
1737 prev->next = nn->next;
1738
1739 nn->prev = nn->next = NULL;
1740
1741 /*
1742 * Scan back until we reach a node that's
1743 * ordered before `nn'.
1744 */
1745
1746 for ( ; prev ; prev = prev->prev) {
1747 /* Determine order of `prev'. */
1748 for (j = 0; j < RSORD_MAX; j++)
1749 if (rsord[j] == prev->tok)
1750 break;
1751
1752 if (j <= i)
1753 break;
1754 }
1755
1756 /*
1757 * Set `nn' back into its correct place in front
1758 * of the `prev' node.
1759 */
1760
1761 nn->prev = prev;
1762
1763 if (prev) {
1764 if (prev->next)
1765 prev->next->prev = nn;
1766 nn->next = prev->next;
1767 prev->next = nn;
1768 } else {
1769 mdoc->last->child->prev = nn;
1770 nn->next = mdoc->last->child;
1771 mdoc->last->child = nn;
1772 }
1773 }
1774
1775 return(1);
1776}
1777
60e1e752
SW
1778static int
1779post_ns(POST_ARGS)
1780{
1781
1782 if (MDOC_LINE & mdoc->last->flags)
1783 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1784 return(1);
1785}
1786
80387638
SW
1787static int
1788post_sh(POST_ARGS)
1789{
1790
1791 if (MDOC_HEAD == mdoc->last->type)
1792 return(post_sh_head(mdoc));
1793 if (MDOC_BODY == mdoc->last->type)
1794 return(post_sh_body(mdoc));
1795
1796 return(1);
1797}
1798
1799static int
1800post_sh_body(POST_ARGS)
1801{
1802 struct mdoc_node *n;
1803
1804 if (SEC_NAME != mdoc->lastsec)
1805 return(1);
1806
1807 /*
1808 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1809 * macros (can have multiple `Nm' and one `Nd'). Note that the
1810 * children of the BODY declaration can also be "text".
1811 */
1812
1813 if (NULL == (n = mdoc->last->child)) {
1814 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1815 return(1);
1816 }
1817
1818 for ( ; n && n->next; n = n->next) {
1819 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1820 continue;
1821 if (MDOC_TEXT == n->type)
1822 continue;
1823 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1824 }
1825
1826 assert(n);
1827 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1828 return(1);
1829
1830 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1831 return(1);
1832}
1833
1834static int
1835post_sh_head(POST_ARGS)
1836{
1837 char buf[BUFSIZ];
1838 enum mdoc_sec sec;
1839
1840 /*
1841 * Process a new section. Sections are either "named" or
1842 * "custom". Custom sections are user-defined, while named ones
1843 * follow a conventional order and may only appear in certain
1844 * manual sections.
1845 */
1846
1847 if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ))
1848 return(0);
1849
60e1e752 1850 sec = a2sec(buf);
80387638
SW
1851
1852 /* The NAME should be first. */
1853
1854 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1855 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1856
1857 /* The SYNOPSIS gets special attention in other areas. */
1858
1859 if (SEC_SYNOPSIS == sec)
1860 mdoc->flags |= MDOC_SYNOPSIS;
1861 else
1862 mdoc->flags &= ~MDOC_SYNOPSIS;
1863
1864 /* Mark our last section. */
1865
1866 mdoc->lastsec = sec;
1867
1868 /* We don't care about custom sections after this. */
1869
1870 if (SEC_CUSTOM == sec)
1871 return(1);
1872
1873 /*
1874 * Check whether our non-custom section is being repeated or is
1875 * out of order.
1876 */
1877
1878 if (sec == mdoc->lastnamed)
1879 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1880
1881 if (sec < mdoc->lastnamed)
1882 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1883
1884 /* Mark the last named section. */
1885
1886 mdoc->lastnamed = sec;
1887
1888 /* Check particular section/manual conventions. */
1889
1890 assert(mdoc->meta.msec);
1891
1892 switch (sec) {
1893 case (SEC_RETURN_VALUES):
1894 /* FALLTHROUGH */
1895 case (SEC_ERRORS):
1896 /* FALLTHROUGH */
1897 case (SEC_LIBRARY):
1898 if (*mdoc->meta.msec == '2')
1899 break;
1900 if (*mdoc->meta.msec == '3')
1901 break;
1902 if (*mdoc->meta.msec == '9')
1903 break;
1904 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1905 break;
1906 default:
1907 break;
1908 }
1909
1910 return(1);
1911}
1912
1913static int
1914post_ignpar(POST_ARGS)
1915{
1916 struct mdoc_node *np;
1917
1918 if (MDOC_BODY != mdoc->last->type)
1919 return(1);
1920
1921 if (NULL != (np = mdoc->last->child))
1922 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1923 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1924 mdoc_node_delete(mdoc, np);
1925 }
1926
1927 if (NULL != (np = mdoc->last->last))
1928 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1929 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1930 mdoc_node_delete(mdoc, np);
1931 }
1932
1933 return(1);
1934}
1935
1936static int
1937pre_par(PRE_ARGS)
1938{
1939
1940 if (NULL == mdoc->last)
1941 return(1);
1942 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
1943 return(1);
1944
1945 /*
1946 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1947 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
1948 */
1949
1950 if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
1951 return(1);
1952 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
1953 return(1);
1954 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
1955 return(1);
1956 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
1957 return(1);
1958
1959 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
1960 mdoc_node_delete(mdoc, mdoc->last);
1961 return(1);
1962}
1963
1964static int
1965pre_literal(PRE_ARGS)
1966{
1967
1968 if (MDOC_BODY != n->type)
1969 return(1);
1970
1971 /*
1972 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
1973 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
1974 */
1975
1976 switch (n->tok) {
1977 case (MDOC_Dl):
1978 mdoc->flags |= MDOC_LITERAL;
1979 break;
1980 case (MDOC_Bd):
1981 if (DISP_literal == n->norm->Bd.type)
1982 mdoc->flags |= MDOC_LITERAL;
1983 if (DISP_unfilled == n->norm->Bd.type)
1984 mdoc->flags |= MDOC_LITERAL;
1985 break;
1986 default:
1987 abort();
1988 /* NOTREACHED */
1989 }
1990
1991 return(1);
1992}
1993
1994static int
1995post_dd(POST_ARGS)
1996{
1997 char buf[DATESIZE];
1998 struct mdoc_node *n;
1999
60e1e752
SW
2000 if (mdoc->meta.date)
2001 free(mdoc->meta.date);
80387638 2002
60e1e752
SW
2003 n = mdoc->last;
2004 if (NULL == n->child || '\0' == n->child->string[0]) {
2005 mdoc->meta.date = mandoc_normdate
2006 (mdoc->parse, NULL, n->line, n->pos);
80387638
SW
2007 return(1);
2008 }
2009
2010 if ( ! concat(mdoc, buf, n->child, DATESIZE))
2011 return(0);
2012
60e1e752
SW
2013 mdoc->meta.date = mandoc_normdate
2014 (mdoc->parse, buf, n->line, n->pos);
80387638
SW
2015
2016 return(1);
2017}
2018
2019static int
2020post_dt(POST_ARGS)
2021{
2022 struct mdoc_node *nn, *n;
2023 const char *cp;
2024 char *p;
2025
2026 n = mdoc->last;
2027
2028 if (mdoc->meta.title)
2029 free(mdoc->meta.title);
2030 if (mdoc->meta.vol)
2031 free(mdoc->meta.vol);
2032 if (mdoc->meta.arch)
2033 free(mdoc->meta.arch);
2034
2035 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2036
2037 /* First make all characters uppercase. */
2038
2039 if (NULL != (nn = n->child))
2040 for (p = nn->string; *p; p++) {
2041 if (toupper((u_char)*p) == *p)
2042 continue;
2043
2044 /*
2045 * FIXME: don't be lazy: have this make all
2046 * characters be uppercase and just warn once.
2047 */
2048 mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2049 break;
2050 }
2051
2052 /* Handles: `.Dt'
2053 * --> title = unknown, volume = local, msec = 0, arch = NULL
2054 */
2055
2056 if (NULL == (nn = n->child)) {
2057 /* XXX: make these macro values. */
2058 /* FIXME: warn about missing values. */
2059 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2060 mdoc->meta.vol = mandoc_strdup("LOCAL");
2061 mdoc->meta.msec = mandoc_strdup("1");
2062 return(1);
2063 }
2064
2065 /* Handles: `.Dt TITLE'
2066 * --> title = TITLE, volume = local, msec = 0, arch = NULL
2067 */
2068
2069 mdoc->meta.title = mandoc_strdup
2070 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2071
2072 if (NULL == (nn = nn->next)) {
2073 /* FIXME: warn about missing msec. */
2074 /* XXX: make this a macro value. */
2075 mdoc->meta.vol = mandoc_strdup("LOCAL");
2076 mdoc->meta.msec = mandoc_strdup("1");
2077 return(1);
2078 }
2079
2080 /* Handles: `.Dt TITLE SEC'
2081 * --> title = TITLE, volume = SEC is msec ?
2082 * format(msec) : SEC,
2083 * msec = SEC is msec ? atoi(msec) : 0,
2084 * arch = NULL
2085 */
2086
2087 cp = mdoc_a2msec(nn->string);
2088 if (cp) {
2089 mdoc->meta.vol = mandoc_strdup(cp);
2090 mdoc->meta.msec = mandoc_strdup(nn->string);
2091 } else {
2092 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2093 mdoc->meta.vol = mandoc_strdup(nn->string);
2094 mdoc->meta.msec = mandoc_strdup(nn->string);
2095 }
2096
2097 if (NULL == (nn = nn->next))
2098 return(1);
2099
2100 /* Handles: `.Dt TITLE SEC VOL'
2101 * --> title = TITLE, volume = VOL is vol ?
2102 * format(VOL) :
2103 * VOL is arch ? format(arch) :
2104 * VOL
2105 */
2106
2107 cp = mdoc_a2vol(nn->string);
2108 if (cp) {
2109 free(mdoc->meta.vol);
2110 mdoc->meta.vol = mandoc_strdup(cp);
2111 } else {
2112 /* FIXME: warn about bad arch. */
2113 cp = mdoc_a2arch(nn->string);
2114 if (NULL == cp) {
2115 free(mdoc->meta.vol);
2116 mdoc->meta.vol = mandoc_strdup(nn->string);
2117 } else
2118 mdoc->meta.arch = mandoc_strdup(cp);
2119 }
2120
2121 /* Ignore any subsequent parameters... */
2122 /* FIXME: warn about subsequent parameters. */
2123
2124 return(1);
2125}
2126
2127static int
2128post_prol(POST_ARGS)
2129{
2130 /*
2131 * Remove prologue macros from the document after they're
2132 * processed. The final document uses mdoc_meta for these
2133 * values and discards the originals.
2134 */
2135
2136 mdoc_node_delete(mdoc, mdoc->last);
2137 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2138 mdoc->flags |= MDOC_PBODY;
2139
2140 return(1);
2141}
2142
60e1e752
SW
2143static int
2144post_bx(POST_ARGS)
2145{
2146 struct mdoc_node *n;
2147
2148 /*
2149 * Make `Bx's second argument always start with an uppercase
2150 * letter. Groff checks if it's an "accepted" term, but we just
2151 * uppercase blindly.
2152 */
2153
2154 n = mdoc->last->child;
2155 if (n && NULL != (n = n->next))
2156 *n->string = (char)toupper
2157 ((unsigned char)*n->string);
2158
2159 return(1);
2160}
2161
80387638
SW
2162static int
2163post_os(POST_ARGS)
2164{
2165 struct mdoc_node *n;
2166 char buf[BUFSIZ];
2167#ifndef OSNAME
2168 struct utsname utsname;
2169#endif
2170
2171 n = mdoc->last;
2172
2173 /*
2174 * Set the operating system by way of the `Os' macro. Note that
2175 * if an argument isn't provided and -DOSNAME="\"foo\"" is
2176 * provided during compilation, this value will be used instead
2177 * of filling in "sysname release" from uname().
2178 */
2179
2180 if (mdoc->meta.os)
2181 free(mdoc->meta.os);
2182
2183 if ( ! concat(mdoc, buf, n->child, BUFSIZ))
2184 return(0);
2185
2186 /* XXX: yes, these can all be dynamically-adjusted buffers, but
2187 * it's really not worth the extra hackery.
2188 */
2189
2190 if ('\0' == buf[0]) {
2191#ifdef OSNAME
2192 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2193 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2194 return(0);
2195 }
2196#else /*!OSNAME */
60e1e752 2197 if (-1 == uname(&utsname)) {
80387638
SW
2198 mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2199 mdoc->meta.os = mandoc_strdup("UNKNOWN");
2200 return(post_prol(mdoc));
2201 }
2202
2203 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2204 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2205 return(0);
2206 }
2207 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2208 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2209 return(0);
2210 }
2211 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2212 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2213 return(0);
2214 }
2215#endif /*!OSNAME*/
2216 }
2217
2218 mdoc->meta.os = mandoc_strdup(buf);
2219 return(1);
2220}
2221
2222static int
2223post_std(POST_ARGS)
2224{
2225 struct mdoc_node *nn, *n;
2226
2227 n = mdoc->last;
2228
2229 /*
2230 * Macros accepting `-std' as an argument have the name of the
2231 * current document (`Nm') filled in as the argument if it's not
2232 * provided.
2233 */
2234
2235 if (n->child)
2236 return(1);
2237
2238 if (NULL == mdoc->meta.name)
2239 return(1);
2240
2241 nn = n;
2242 mdoc->next = MDOC_NEXT_CHILD;
2243
2244 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2245 return(0);
2246
2247 mdoc->last = nn;
2248 return(1);
2249}
2250
2251static int
2252concat(struct mdoc *m, char *p, const struct mdoc_node *n, size_t sz)
2253{
2254
2255 p[0] = '\0';
2256
2257 /*
2258 * Concatenate sibling nodes together. All siblings must be of
2259 * type MDOC_TEXT or an assertion is raised. Concatenation is
2260 * separated by a single whitespace. Returns 0 on fatal (string
2261 * overrun) error.
2262 */
2263
2264 for ( ; n; n = n->next) {
2265 assert(MDOC_TEXT == n->type);
2266
2267 if (strlcat(p, n->string, sz) >= sz) {
2268 mdoc_nmsg(m, n, MANDOCERR_MEM);
2269 return(0);
2270 }
2271
2272 if (NULL == n->next)
2273 continue;
2274
2275 if (strlcat(p, " ", sz) >= sz) {
2276 mdoc_nmsg(m, n, MANDOCERR_MEM);
2277 return(0);
2278 }
2279 }
2280
2281 return(1);
2282}
2283
60e1e752
SW
2284static enum mdoc_sec
2285a2sec(const char *p)
2286{
2287 int i;
2288
2289 for (i = 0; i < (int)SEC__MAX; i++)
2290 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2291 return((enum mdoc_sec)i);
2292
2293 return(SEC_CUSTOM);
2294}
2295
2296static size_t
2297macro2len(enum mdoct macro)
2298{
2299
2300 switch (macro) {
2301 case(MDOC_Ad):
2302 return(12);
2303 case(MDOC_Ao):
2304 return(12);
2305 case(MDOC_An):
2306 return(12);
2307 case(MDOC_Aq):
2308 return(12);
2309 case(MDOC_Ar):
2310 return(12);
2311 case(MDOC_Bo):
2312 return(12);
2313 case(MDOC_Bq):
2314 return(12);
2315 case(MDOC_Cd):
2316 return(12);
2317 case(MDOC_Cm):
2318 return(10);
2319 case(MDOC_Do):
2320 return(10);
2321 case(MDOC_Dq):
2322 return(12);
2323 case(MDOC_Dv):
2324 return(12);
2325 case(MDOC_Eo):
2326 return(12);
2327 case(MDOC_Em):
2328 return(10);
2329 case(MDOC_Er):
2330 return(17);
2331 case(MDOC_Ev):
2332 return(15);
2333 case(MDOC_Fa):
2334 return(12);
2335 case(MDOC_Fl):
2336 return(10);
2337 case(MDOC_Fo):
2338 return(16);
2339 case(MDOC_Fn):
2340 return(16);
2341 case(MDOC_Ic):
2342 return(10);
2343 case(MDOC_Li):
2344 return(16);
2345 case(MDOC_Ms):
2346 return(6);
2347 case(MDOC_Nm):
2348 return(10);
2349 case(MDOC_No):
2350 return(12);
2351 case(MDOC_Oo):
2352 return(10);
2353 case(MDOC_Op):
2354 return(14);
2355 case(MDOC_Pa):
2356 return(32);
2357 case(MDOC_Pf):
2358 return(12);
2359 case(MDOC_Po):
2360 return(12);
2361 case(MDOC_Pq):
2362 return(12);
2363 case(MDOC_Ql):
2364 return(16);
2365 case(MDOC_Qo):
2366 return(12);
2367 case(MDOC_So):
2368 return(12);
2369 case(MDOC_Sq):
2370 return(12);
2371 case(MDOC_Sy):
2372 return(6);
2373 case(MDOC_Sx):
2374 return(16);
2375 case(MDOC_Tn):
2376 return(10);
2377 case(MDOC_Va):
2378 return(12);
2379 case(MDOC_Vt):
2380 return(12);
2381 case(MDOC_Xr):
2382 return(10);
2383 default:
2384 break;
2385 };
2386 return(0);
2387}