Home | History | Annotate | Line # | Download | only in dist
man_validate.c revision 1.1.1.11
      1  1.1.1.11  joerg /*	$Vendor-Id: man_validate.c,v 1.57 2011/01/01 12:59:17 kristaps Exp $ */
      2       1.1  joerg /*
      3  1.1.1.11  joerg  * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps (at) bsd.lv>
      4       1.1  joerg  *
      5       1.1  joerg  * Permission to use, copy, modify, and distribute this software for any
      6       1.1  joerg  * purpose with or without fee is hereby granted, provided that the above
      7       1.1  joerg  * copyright notice and this permission notice appear in all copies.
      8       1.1  joerg  *
      9       1.1  joerg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     10       1.1  joerg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11       1.1  joerg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     12       1.1  joerg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13       1.1  joerg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14       1.1  joerg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     15       1.1  joerg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16       1.1  joerg  */
     17   1.1.1.4  joerg #ifdef HAVE_CONFIG_H
     18   1.1.1.4  joerg #include "config.h"
     19   1.1.1.4  joerg #endif
     20   1.1.1.4  joerg 
     21       1.1  joerg #include <sys/types.h>
     22       1.1  joerg 
     23       1.1  joerg #include <assert.h>
     24       1.1  joerg #include <ctype.h>
     25       1.1  joerg #include <errno.h>
     26       1.1  joerg #include <limits.h>
     27       1.1  joerg #include <stdarg.h>
     28       1.1  joerg #include <stdlib.h>
     29  1.1.1.10  joerg #include <string.h>
     30  1.1.1.11  joerg #include <time.h>
     31       1.1  joerg 
     32   1.1.1.7  joerg #include "mandoc.h"
     33       1.1  joerg #include "libman.h"
     34       1.1  joerg #include "libmandoc.h"
     35       1.1  joerg 
     36   1.1.1.8  joerg #define	CHKARGS	  struct man *m, struct man_node *n
     37       1.1  joerg 
     38       1.1  joerg typedef	int	(*v_check)(CHKARGS);
     39       1.1  joerg 
     40       1.1  joerg struct	man_valid {
     41       1.1  joerg 	v_check	 *pres;
     42       1.1  joerg 	v_check	 *posts;
     43       1.1  joerg };
     44       1.1  joerg 
     45       1.1  joerg static	int	  check_bline(CHKARGS);
     46       1.1  joerg static	int	  check_eq0(CHKARGS);
     47  1.1.1.11  joerg static	int	  check_ft(CHKARGS);
     48       1.1  joerg static	int	  check_le1(CHKARGS);
     49       1.1  joerg static	int	  check_ge2(CHKARGS);
     50       1.1  joerg static	int	  check_le5(CHKARGS);
     51       1.1  joerg static	int	  check_par(CHKARGS);
     52       1.1  joerg static	int	  check_part(CHKARGS);
     53       1.1  joerg static	int	  check_root(CHKARGS);
     54       1.1  joerg static	int	  check_sec(CHKARGS);
     55       1.1  joerg static	int	  check_text(CHKARGS);
     56   1.1.1.5  joerg static	int	  check_title(CHKARGS);
     57       1.1  joerg 
     58  1.1.1.11  joerg static	int	  post_AT(CHKARGS);
     59  1.1.1.11  joerg static	int	  post_fi(CHKARGS);
     60  1.1.1.11  joerg static	int	  post_nf(CHKARGS);
     61  1.1.1.11  joerg static	int	  post_TH(CHKARGS);
     62  1.1.1.11  joerg static	int	  post_UC(CHKARGS);
     63  1.1.1.11  joerg 
     64  1.1.1.11  joerg static	v_check	  posts_at[] = { post_AT, NULL };
     65       1.1  joerg static	v_check	  posts_eq0[] = { check_eq0, NULL };
     66  1.1.1.11  joerg static	v_check	  posts_fi[] = { check_eq0, post_fi, NULL };
     67  1.1.1.11  joerg static	v_check	  posts_le1[] = { check_le1, NULL };
     68  1.1.1.11  joerg static	v_check	  posts_ft[] = { check_ft, NULL };
     69  1.1.1.11  joerg static	v_check	  posts_nf[] = { check_eq0, post_nf, NULL };
     70       1.1  joerg static	v_check	  posts_par[] = { check_par, NULL };
     71       1.1  joerg static	v_check	  posts_part[] = { check_part, NULL };
     72       1.1  joerg static	v_check	  posts_sec[] = { check_sec, NULL };
     73  1.1.1.11  joerg static	v_check	  posts_th[] = { check_ge2, check_le5, check_title, post_TH, NULL };
     74  1.1.1.11  joerg static	v_check	  posts_uc[] = { post_UC, NULL };
     75       1.1  joerg static	v_check	  pres_bline[] = { check_bline, NULL };
     76       1.1  joerg 
     77  1.1.1.11  joerg 
     78       1.1  joerg static	const struct man_valid man_valids[MAN_MAX] = {
     79   1.1.1.5  joerg 	{ NULL, posts_eq0 }, /* br */
     80   1.1.1.5  joerg 	{ pres_bline, posts_th }, /* TH */
     81       1.1  joerg 	{ pres_bline, posts_sec }, /* SH */
     82       1.1  joerg 	{ pres_bline, posts_sec }, /* SS */
     83       1.1  joerg 	{ pres_bline, posts_par }, /* TP */
     84       1.1  joerg 	{ pres_bline, posts_par }, /* LP */
     85       1.1  joerg 	{ pres_bline, posts_par }, /* PP */
     86       1.1  joerg 	{ pres_bline, posts_par }, /* P */
     87       1.1  joerg 	{ pres_bline, posts_par }, /* IP */
     88       1.1  joerg 	{ pres_bline, posts_par }, /* HP */
     89       1.1  joerg 	{ NULL, NULL }, /* SM */
     90       1.1  joerg 	{ NULL, NULL }, /* SB */
     91       1.1  joerg 	{ NULL, NULL }, /* BI */
     92       1.1  joerg 	{ NULL, NULL }, /* IB */
     93       1.1  joerg 	{ NULL, NULL }, /* BR */
     94       1.1  joerg 	{ NULL, NULL }, /* RB */
     95       1.1  joerg 	{ NULL, NULL }, /* R */
     96       1.1  joerg 	{ NULL, NULL }, /* B */
     97       1.1  joerg 	{ NULL, NULL }, /* I */
     98       1.1  joerg 	{ NULL, NULL }, /* IR */
     99       1.1  joerg 	{ NULL, NULL }, /* RI */
    100  1.1.1.10  joerg 	{ NULL, posts_eq0 }, /* na */ /* FIXME: should warn only. */
    101  1.1.1.10  joerg 	{ NULL, posts_le1 }, /* sp */ /* FIXME: should warn only. */
    102  1.1.1.11  joerg 	{ pres_bline, posts_nf }, /* nf */
    103  1.1.1.11  joerg 	{ pres_bline, posts_fi }, /* fi */
    104       1.1  joerg 	{ NULL, NULL }, /* RE */
    105       1.1  joerg 	{ NULL, posts_part }, /* RS */
    106       1.1  joerg 	{ NULL, NULL }, /* DT */
    107  1.1.1.11  joerg 	{ NULL, posts_uc }, /* UC */
    108   1.1.1.2  joerg 	{ NULL, NULL }, /* PD */
    109  1.1.1.11  joerg 	{ NULL, posts_at }, /* AT */
    110  1.1.1.10  joerg 	{ NULL, NULL }, /* in */
    111  1.1.1.11  joerg 	{ NULL, posts_ft }, /* ft */
    112       1.1  joerg };
    113       1.1  joerg 
    114       1.1  joerg 
    115       1.1  joerg int
    116   1.1.1.8  joerg man_valid_pre(struct man *m, struct man_node *n)
    117       1.1  joerg {
    118       1.1  joerg 	v_check		*cp;
    119       1.1  joerg 
    120  1.1.1.11  joerg 	switch (n->type) {
    121  1.1.1.11  joerg 	case (MAN_TEXT):
    122  1.1.1.11  joerg 		/* FALLTHROUGH */
    123  1.1.1.11  joerg 	case (MAN_ROOT):
    124  1.1.1.11  joerg 		/* FALLTHROUGH */
    125  1.1.1.11  joerg 	case (MAN_TBL):
    126       1.1  joerg 		return(1);
    127  1.1.1.11  joerg 	default:
    128  1.1.1.11  joerg 		break;
    129  1.1.1.11  joerg 	}
    130       1.1  joerg 
    131       1.1  joerg 	if (NULL == (cp = man_valids[n->tok].pres))
    132       1.1  joerg 		return(1);
    133       1.1  joerg 	for ( ; *cp; cp++)
    134       1.1  joerg 		if ( ! (*cp)(m, n))
    135       1.1  joerg 			return(0);
    136       1.1  joerg 	return(1);
    137       1.1  joerg }
    138       1.1  joerg 
    139       1.1  joerg 
    140       1.1  joerg int
    141       1.1  joerg man_valid_post(struct man *m)
    142       1.1  joerg {
    143       1.1  joerg 	v_check		*cp;
    144       1.1  joerg 
    145       1.1  joerg 	if (MAN_VALID & m->last->flags)
    146       1.1  joerg 		return(1);
    147       1.1  joerg 	m->last->flags |= MAN_VALID;
    148       1.1  joerg 
    149       1.1  joerg 	switch (m->last->type) {
    150       1.1  joerg 	case (MAN_TEXT):
    151       1.1  joerg 		return(check_text(m, m->last));
    152       1.1  joerg 	case (MAN_ROOT):
    153       1.1  joerg 		return(check_root(m, m->last));
    154  1.1.1.11  joerg 	case (MAN_TBL):
    155  1.1.1.11  joerg 		return(1);
    156       1.1  joerg 	default:
    157       1.1  joerg 		break;
    158       1.1  joerg 	}
    159       1.1  joerg 
    160       1.1  joerg 	if (NULL == (cp = man_valids[m->last->tok].posts))
    161       1.1  joerg 		return(1);
    162       1.1  joerg 	for ( ; *cp; cp++)
    163       1.1  joerg 		if ( ! (*cp)(m, m->last))
    164       1.1  joerg 			return(0);
    165       1.1  joerg 
    166       1.1  joerg 	return(1);
    167       1.1  joerg }
    168       1.1  joerg 
    169       1.1  joerg 
    170       1.1  joerg static int
    171       1.1  joerg check_root(CHKARGS)
    172       1.1  joerg {
    173       1.1  joerg 
    174       1.1  joerg 	if (MAN_BLINE & m->flags)
    175  1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
    176  1.1.1.11  joerg 	else if (MAN_ELINE & m->flags)
    177  1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
    178       1.1  joerg 
    179       1.1  joerg 	m->flags &= ~MAN_BLINE;
    180       1.1  joerg 	m->flags &= ~MAN_ELINE;
    181       1.1  joerg 
    182   1.1.1.7  joerg 	if (NULL == m->first->child) {
    183   1.1.1.7  joerg 		man_nmsg(m, n, MANDOCERR_NODOCBODY);
    184   1.1.1.7  joerg 		return(0);
    185   1.1.1.7  joerg 	} else if (NULL == m->meta.title) {
    186  1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_NOTITLE);
    187  1.1.1.11  joerg 
    188   1.1.1.6  joerg 		/*
    189   1.1.1.6  joerg 		 * If a title hasn't been set, do so now (by
    190   1.1.1.6  joerg 		 * implication, date and section also aren't set).
    191   1.1.1.6  joerg 		 */
    192  1.1.1.11  joerg 
    193   1.1.1.6  joerg 	        m->meta.title = mandoc_strdup("unknown");
    194   1.1.1.6  joerg 		m->meta.date = time(NULL);
    195   1.1.1.7  joerg 		m->meta.msec = mandoc_strdup("1");
    196   1.1.1.6  joerg 	}
    197       1.1  joerg 
    198       1.1  joerg 	return(1);
    199       1.1  joerg }
    200       1.1  joerg 
    201       1.1  joerg 
    202       1.1  joerg static int
    203   1.1.1.5  joerg check_title(CHKARGS)
    204   1.1.1.5  joerg {
    205   1.1.1.5  joerg 	const char	*p;
    206   1.1.1.5  joerg 
    207   1.1.1.5  joerg 	assert(n->child);
    208   1.1.1.7  joerg 	/* FIXME: is this sufficient? */
    209   1.1.1.7  joerg 	if ('\0' == *n->child->string) {
    210   1.1.1.7  joerg 		man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
    211   1.1.1.7  joerg 		return(0);
    212   1.1.1.7  joerg 	}
    213   1.1.1.5  joerg 
    214   1.1.1.5  joerg 	for (p = n->child->string; '\0' != *p; p++)
    215  1.1.1.11  joerg 		/* Only warn about this once... */
    216  1.1.1.11  joerg 		if (isalpha((u_char)*p) && ! isupper((u_char)*p)) {
    217  1.1.1.11  joerg 			man_nmsg(m, n, MANDOCERR_UPPERCASE);
    218  1.1.1.11  joerg 			break;
    219  1.1.1.11  joerg 		}
    220   1.1.1.5  joerg 
    221   1.1.1.5  joerg 	return(1);
    222   1.1.1.5  joerg }
    223   1.1.1.5  joerg 
    224   1.1.1.5  joerg 
    225   1.1.1.5  joerg static int
    226       1.1  joerg check_text(CHKARGS)
    227       1.1  joerg {
    228   1.1.1.8  joerg 	char		*p;
    229       1.1  joerg 	int		 pos, c;
    230  1.1.1.10  joerg 	size_t		 sz;
    231       1.1  joerg 
    232       1.1  joerg 	for (p = n->string, pos = n->pos + 1; *p; p++, pos++) {
    233  1.1.1.10  joerg 		sz = strcspn(p, "\t\\");
    234  1.1.1.10  joerg 		p += (int)sz;
    235  1.1.1.10  joerg 
    236  1.1.1.10  joerg 		if ('\0' == *p)
    237  1.1.1.10  joerg 			break;
    238  1.1.1.10  joerg 
    239  1.1.1.10  joerg 		pos += (int)sz;
    240   1.1.1.7  joerg 
    241  1.1.1.10  joerg 		if ('\t' == *p) {
    242  1.1.1.10  joerg 			if (MAN_LITERAL & m->flags)
    243  1.1.1.10  joerg 				continue;
    244  1.1.1.10  joerg 			if (man_pmsg(m, n->line, pos, MANDOCERR_BADTAB))
    245  1.1.1.10  joerg 				continue;
    246  1.1.1.10  joerg 			return(0);
    247       1.1  joerg 		}
    248       1.1  joerg 
    249  1.1.1.10  joerg 		/* Check the special character. */
    250   1.1.1.9  joerg 
    251  1.1.1.10  joerg 		c = mandoc_special(p);
    252  1.1.1.10  joerg 		if (c) {
    253  1.1.1.10  joerg 			p += c - 1;
    254  1.1.1.10  joerg 			pos += c - 1;
    255  1.1.1.11  joerg 		} else
    256  1.1.1.11  joerg 			man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE);
    257       1.1  joerg 	}
    258       1.1  joerg 
    259       1.1  joerg 	return(1);
    260       1.1  joerg }
    261       1.1  joerg 
    262       1.1  joerg 
    263       1.1  joerg #define	INEQ_DEFINE(x, ineq, name) \
    264       1.1  joerg static int \
    265       1.1  joerg check_##name(CHKARGS) \
    266       1.1  joerg { \
    267       1.1  joerg 	if (n->nchild ineq (x)) \
    268       1.1  joerg 		return(1); \
    269   1.1.1.7  joerg 	man_vmsg(m, MANDOCERR_SYNTARGCOUNT, n->line, n->pos, \
    270   1.1.1.7  joerg 			"line arguments %s %d (have %d)", \
    271   1.1.1.7  joerg 			#ineq, (x), n->nchild); \
    272   1.1.1.7  joerg 	return(0); \
    273       1.1  joerg }
    274       1.1  joerg 
    275       1.1  joerg INEQ_DEFINE(0, ==, eq0)
    276       1.1  joerg INEQ_DEFINE(1, <=, le1)
    277       1.1  joerg INEQ_DEFINE(2, >=, ge2)
    278       1.1  joerg INEQ_DEFINE(5, <=, le5)
    279       1.1  joerg 
    280  1.1.1.11  joerg static int
    281  1.1.1.11  joerg check_ft(CHKARGS)
    282  1.1.1.11  joerg {
    283  1.1.1.11  joerg 	char	*cp;
    284  1.1.1.11  joerg 	int	 ok;
    285  1.1.1.11  joerg 
    286  1.1.1.11  joerg 	if (0 == n->nchild)
    287  1.1.1.11  joerg 		return(1);
    288  1.1.1.11  joerg 
    289  1.1.1.11  joerg 	ok = 0;
    290  1.1.1.11  joerg 	cp = n->child->string;
    291  1.1.1.11  joerg 	switch (*cp) {
    292  1.1.1.11  joerg 	case ('1'):
    293  1.1.1.11  joerg 		/* FALLTHROUGH */
    294  1.1.1.11  joerg 	case ('2'):
    295  1.1.1.11  joerg 		/* FALLTHROUGH */
    296  1.1.1.11  joerg 	case ('3'):
    297  1.1.1.11  joerg 		/* FALLTHROUGH */
    298  1.1.1.11  joerg 	case ('4'):
    299  1.1.1.11  joerg 		/* FALLTHROUGH */
    300  1.1.1.11  joerg 	case ('I'):
    301  1.1.1.11  joerg 		/* FALLTHROUGH */
    302  1.1.1.11  joerg 	case ('P'):
    303  1.1.1.11  joerg 		/* FALLTHROUGH */
    304  1.1.1.11  joerg 	case ('R'):
    305  1.1.1.11  joerg 		if ('\0' == cp[1])
    306  1.1.1.11  joerg 			ok = 1;
    307  1.1.1.11  joerg 		break;
    308  1.1.1.11  joerg 	case ('B'):
    309  1.1.1.11  joerg 		if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
    310  1.1.1.11  joerg 			ok = 1;
    311  1.1.1.11  joerg 		break;
    312  1.1.1.11  joerg 	case ('C'):
    313  1.1.1.11  joerg 		if ('W' == cp[1] && '\0' == cp[2])
    314  1.1.1.11  joerg 			ok = 1;
    315  1.1.1.11  joerg 		break;
    316  1.1.1.11  joerg 	default:
    317  1.1.1.11  joerg 		break;
    318  1.1.1.11  joerg 	}
    319  1.1.1.11  joerg 
    320  1.1.1.11  joerg 	if (0 == ok) {
    321  1.1.1.11  joerg 		man_vmsg(m, MANDOCERR_BADFONT,
    322  1.1.1.11  joerg 				n->line, n->pos, "%s", cp);
    323  1.1.1.11  joerg 		*cp = '\0';
    324  1.1.1.11  joerg 	}
    325  1.1.1.11  joerg 
    326  1.1.1.11  joerg 	if (1 < n->nchild)
    327  1.1.1.11  joerg 		man_vmsg(m, MANDOCERR_ARGCOUNT, n->line, n->pos,
    328  1.1.1.11  joerg 				"want one child (have %d)", n->nchild);
    329  1.1.1.11  joerg 
    330  1.1.1.11  joerg 	return(1);
    331  1.1.1.11  joerg }
    332       1.1  joerg 
    333       1.1  joerg static int
    334       1.1  joerg check_sec(CHKARGS)
    335       1.1  joerg {
    336       1.1  joerg 
    337   1.1.1.7  joerg 	if (MAN_HEAD == n->type && 0 == n->nchild) {
    338   1.1.1.7  joerg 		man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
    339   1.1.1.7  joerg 		return(0);
    340   1.1.1.7  joerg 	} else if (MAN_BODY == n->type && 0 == n->nchild)
    341  1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_NOBODY);
    342   1.1.1.7  joerg 
    343       1.1  joerg 	return(1);
    344       1.1  joerg }
    345       1.1  joerg 
    346       1.1  joerg 
    347       1.1  joerg static int
    348       1.1  joerg check_part(CHKARGS)
    349       1.1  joerg {
    350       1.1  joerg 
    351       1.1  joerg 	if (MAN_BODY == n->type && 0 == n->nchild)
    352  1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_NOBODY);
    353  1.1.1.11  joerg 
    354       1.1  joerg 	return(1);
    355       1.1  joerg }
    356       1.1  joerg 
    357       1.1  joerg 
    358       1.1  joerg static int
    359       1.1  joerg check_par(CHKARGS)
    360       1.1  joerg {
    361       1.1  joerg 
    362       1.1  joerg 	if (MAN_BODY == n->type)
    363       1.1  joerg 		switch (n->tok) {
    364       1.1  joerg 		case (MAN_IP):
    365       1.1  joerg 			/* FALLTHROUGH */
    366       1.1  joerg 		case (MAN_HP):
    367       1.1  joerg 			/* FALLTHROUGH */
    368       1.1  joerg 		case (MAN_TP):
    369       1.1  joerg 			/* Body-less lists are ok. */
    370       1.1  joerg 			break;
    371       1.1  joerg 		default:
    372  1.1.1.11  joerg 			if (0 == n->nchild)
    373  1.1.1.11  joerg 				man_nmsg(m, n, MANDOCERR_NOBODY);
    374  1.1.1.11  joerg 			break;
    375       1.1  joerg 		}
    376       1.1  joerg 	if (MAN_HEAD == n->type)
    377       1.1  joerg 		switch (n->tok) {
    378       1.1  joerg 		case (MAN_PP):
    379       1.1  joerg 			/* FALLTHROUGH */
    380       1.1  joerg 		case (MAN_P):
    381       1.1  joerg 			/* FALLTHROUGH */
    382       1.1  joerg 		case (MAN_LP):
    383       1.1  joerg 			if (n->nchild)
    384  1.1.1.11  joerg 				man_nmsg(m, n, MANDOCERR_ARGSLOST);
    385  1.1.1.11  joerg 			break;
    386  1.1.1.11  joerg 		default:
    387  1.1.1.11  joerg 			break;
    388       1.1  joerg 		}
    389       1.1  joerg 
    390       1.1  joerg 	return(1);
    391       1.1  joerg }
    392       1.1  joerg 
    393       1.1  joerg 
    394       1.1  joerg static int
    395       1.1  joerg check_bline(CHKARGS)
    396       1.1  joerg {
    397       1.1  joerg 
    398       1.1  joerg 	assert( ! (MAN_ELINE & m->flags));
    399   1.1.1.7  joerg 	if (MAN_BLINE & m->flags) {
    400   1.1.1.7  joerg 		man_nmsg(m, n, MANDOCERR_SYNTLINESCOPE);
    401   1.1.1.7  joerg 		return(0);
    402   1.1.1.7  joerg 	}
    403   1.1.1.5  joerg 
    404       1.1  joerg 	return(1);
    405       1.1  joerg }
    406       1.1  joerg 
    407  1.1.1.11  joerg static int
    408  1.1.1.11  joerg post_TH(CHKARGS)
    409  1.1.1.11  joerg {
    410  1.1.1.11  joerg 
    411  1.1.1.11  joerg 	if (m->meta.title)
    412  1.1.1.11  joerg 		free(m->meta.title);
    413  1.1.1.11  joerg 	if (m->meta.vol)
    414  1.1.1.11  joerg 		free(m->meta.vol);
    415  1.1.1.11  joerg 	if (m->meta.source)
    416  1.1.1.11  joerg 		free(m->meta.source);
    417  1.1.1.11  joerg 	if (m->meta.msec)
    418  1.1.1.11  joerg 		free(m->meta.msec);
    419  1.1.1.11  joerg 	if (m->meta.rawdate)
    420  1.1.1.11  joerg 		free(m->meta.rawdate);
    421  1.1.1.11  joerg 
    422  1.1.1.11  joerg 	m->meta.title = m->meta.vol = m->meta.rawdate =
    423  1.1.1.11  joerg 		m->meta.msec = m->meta.source = NULL;
    424  1.1.1.11  joerg 	m->meta.date = 0;
    425  1.1.1.11  joerg 
    426  1.1.1.11  joerg 	/* ->TITLE<- MSEC DATE SOURCE VOL */
    427  1.1.1.11  joerg 
    428  1.1.1.11  joerg 	n = n->child;
    429  1.1.1.11  joerg 	assert(n);
    430  1.1.1.11  joerg 	m->meta.title = mandoc_strdup(n->string);
    431  1.1.1.11  joerg 
    432  1.1.1.11  joerg 	/* TITLE ->MSEC<- DATE SOURCE VOL */
    433  1.1.1.11  joerg 
    434  1.1.1.11  joerg 	n = n->next;
    435  1.1.1.11  joerg 	assert(n);
    436  1.1.1.11  joerg 	m->meta.msec = mandoc_strdup(n->string);
    437  1.1.1.11  joerg 
    438  1.1.1.11  joerg 	/* TITLE MSEC ->DATE<- SOURCE VOL */
    439  1.1.1.11  joerg 
    440  1.1.1.11  joerg 	/*
    441  1.1.1.11  joerg 	 * Try to parse the date.  If this works, stash the epoch (this
    442  1.1.1.11  joerg 	 * is optimal because we can reformat it in the canonical form).
    443  1.1.1.11  joerg 	 * If it doesn't parse, isn't specified at all, or is an empty
    444  1.1.1.11  joerg 	 * string, then use the current date.
    445  1.1.1.11  joerg 	 */
    446  1.1.1.11  joerg 
    447  1.1.1.11  joerg 	n = n->next;
    448  1.1.1.11  joerg 	if (n && n->string && *n->string) {
    449  1.1.1.11  joerg 		m->meta.date = mandoc_a2time
    450  1.1.1.11  joerg 			(MTIME_ISO_8601, n->string);
    451  1.1.1.11  joerg 		if (0 == m->meta.date) {
    452  1.1.1.11  joerg 			man_nmsg(m, n, MANDOCERR_BADDATE);
    453  1.1.1.11  joerg 			m->meta.rawdate = mandoc_strdup(n->string);
    454  1.1.1.11  joerg 		}
    455  1.1.1.11  joerg 	} else
    456  1.1.1.11  joerg 		m->meta.date = time(NULL);
    457  1.1.1.11  joerg 
    458  1.1.1.11  joerg 	/* TITLE MSEC DATE ->SOURCE<- VOL */
    459  1.1.1.11  joerg 
    460  1.1.1.11  joerg 	if (n && (n = n->next))
    461  1.1.1.11  joerg 		m->meta.source = mandoc_strdup(n->string);
    462  1.1.1.11  joerg 
    463  1.1.1.11  joerg 	/* TITLE MSEC DATE SOURCE ->VOL<- */
    464  1.1.1.11  joerg 
    465  1.1.1.11  joerg 	if (n && (n = n->next))
    466  1.1.1.11  joerg 		m->meta.vol = mandoc_strdup(n->string);
    467  1.1.1.11  joerg 
    468  1.1.1.11  joerg 	/*
    469  1.1.1.11  joerg 	 * Remove the `TH' node after we've processed it for our
    470  1.1.1.11  joerg 	 * meta-data.
    471  1.1.1.11  joerg 	 */
    472  1.1.1.11  joerg 	man_node_delete(m, m->last);
    473  1.1.1.11  joerg 	return(1);
    474  1.1.1.11  joerg }
    475  1.1.1.11  joerg 
    476  1.1.1.11  joerg static int
    477  1.1.1.11  joerg post_nf(CHKARGS)
    478  1.1.1.11  joerg {
    479  1.1.1.11  joerg 
    480  1.1.1.11  joerg 	if (MAN_LITERAL & m->flags)
    481  1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_SCOPEREP);
    482  1.1.1.11  joerg 
    483  1.1.1.11  joerg 	m->flags |= MAN_LITERAL;
    484  1.1.1.11  joerg 	return(1);
    485  1.1.1.11  joerg }
    486  1.1.1.11  joerg 
    487  1.1.1.11  joerg static int
    488  1.1.1.11  joerg post_fi(CHKARGS)
    489  1.1.1.11  joerg {
    490  1.1.1.11  joerg 
    491  1.1.1.11  joerg 	if ( ! (MAN_LITERAL & m->flags))
    492  1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_NOSCOPE);
    493  1.1.1.11  joerg 
    494  1.1.1.11  joerg 	m->flags &= ~MAN_LITERAL;
    495  1.1.1.11  joerg 	return(1);
    496  1.1.1.11  joerg }
    497  1.1.1.11  joerg 
    498  1.1.1.11  joerg static int
    499  1.1.1.11  joerg post_UC(CHKARGS)
    500  1.1.1.11  joerg {
    501  1.1.1.11  joerg 	static const char * const bsd_versions[] = {
    502  1.1.1.11  joerg 	    "3rd Berkeley Distribution",
    503  1.1.1.11  joerg 	    "4th Berkeley Distribution",
    504  1.1.1.11  joerg 	    "4.2 Berkeley Distribution",
    505  1.1.1.11  joerg 	    "4.3 Berkeley Distribution",
    506  1.1.1.11  joerg 	    "4.4 Berkeley Distribution",
    507  1.1.1.11  joerg 	};
    508  1.1.1.11  joerg 
    509  1.1.1.11  joerg 	const char	*p, *s;
    510  1.1.1.11  joerg 
    511  1.1.1.11  joerg 	n = n->child;
    512  1.1.1.11  joerg 	n = m->last->child;
    513  1.1.1.11  joerg 
    514  1.1.1.11  joerg 	if (NULL == n || MAN_TEXT != n->type)
    515  1.1.1.11  joerg 		p = bsd_versions[0];
    516  1.1.1.11  joerg 	else {
    517  1.1.1.11  joerg 		s = n->string;
    518  1.1.1.11  joerg 		if (0 == strcmp(s, "3"))
    519  1.1.1.11  joerg 			p = bsd_versions[0];
    520  1.1.1.11  joerg 		else if (0 == strcmp(s, "4"))
    521  1.1.1.11  joerg 			p = bsd_versions[1];
    522  1.1.1.11  joerg 		else if (0 == strcmp(s, "5"))
    523  1.1.1.11  joerg 			p = bsd_versions[2];
    524  1.1.1.11  joerg 		else if (0 == strcmp(s, "6"))
    525  1.1.1.11  joerg 			p = bsd_versions[3];
    526  1.1.1.11  joerg 		else if (0 == strcmp(s, "7"))
    527  1.1.1.11  joerg 			p = bsd_versions[4];
    528  1.1.1.11  joerg 		else
    529  1.1.1.11  joerg 			p = bsd_versions[0];
    530  1.1.1.11  joerg 	}
    531  1.1.1.11  joerg 
    532  1.1.1.11  joerg 	if (m->meta.source)
    533  1.1.1.11  joerg 		free(m->meta.source);
    534  1.1.1.11  joerg 
    535  1.1.1.11  joerg 	m->meta.source = mandoc_strdup(p);
    536  1.1.1.11  joerg 	return(1);
    537  1.1.1.11  joerg }
    538  1.1.1.11  joerg 
    539  1.1.1.11  joerg static int
    540  1.1.1.11  joerg post_AT(CHKARGS)
    541  1.1.1.11  joerg {
    542  1.1.1.11  joerg 	static const char * const unix_versions[] = {
    543  1.1.1.11  joerg 	    "7th Edition",
    544  1.1.1.11  joerg 	    "System III",
    545  1.1.1.11  joerg 	    "System V",
    546  1.1.1.11  joerg 	    "System V Release 2",
    547  1.1.1.11  joerg 	};
    548  1.1.1.11  joerg 
    549  1.1.1.11  joerg 	const char	*p, *s;
    550  1.1.1.11  joerg 	struct man_node	*nn;
    551  1.1.1.11  joerg 
    552  1.1.1.11  joerg 	n = n->child;
    553  1.1.1.11  joerg 
    554  1.1.1.11  joerg 	if (NULL == n || MAN_TEXT != n->type)
    555  1.1.1.11  joerg 		p = unix_versions[0];
    556  1.1.1.11  joerg 	else {
    557  1.1.1.11  joerg 		s = n->string;
    558  1.1.1.11  joerg 		if (0 == strcmp(s, "3"))
    559  1.1.1.11  joerg 			p = unix_versions[0];
    560  1.1.1.11  joerg 		else if (0 == strcmp(s, "4"))
    561  1.1.1.11  joerg 			p = unix_versions[1];
    562  1.1.1.11  joerg 		else if (0 == strcmp(s, "5")) {
    563  1.1.1.11  joerg 			nn = n->next;
    564  1.1.1.11  joerg 			if (nn && MAN_TEXT == nn->type && nn->string[0])
    565  1.1.1.11  joerg 				p = unix_versions[3];
    566  1.1.1.11  joerg 			else
    567  1.1.1.11  joerg 				p = unix_versions[2];
    568  1.1.1.11  joerg 		} else
    569  1.1.1.11  joerg 			p = unix_versions[0];
    570  1.1.1.11  joerg 	}
    571  1.1.1.11  joerg 
    572  1.1.1.11  joerg 	if (m->meta.source)
    573  1.1.1.11  joerg 		free(m->meta.source);
    574  1.1.1.11  joerg 
    575  1.1.1.11  joerg 	m->meta.source = mandoc_strdup(p);
    576  1.1.1.11  joerg 	return(1);
    577  1.1.1.11  joerg }
    578