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