Home | History | Annotate | Line # | Download | only in dist
man_validate.c revision 1.1.1.13.2.1
      1  1.1.1.13.2.1   yamt /*	$Vendor-Id: man_validate.c,v 1.80 2012/01/03 15:16:24 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_eq0(CHKARGS);
     48  1.1.1.13.2.1   yamt static	int	  check_eq2(CHKARGS);
     49           1.1  joerg static	int	  check_le1(CHKARGS);
     50           1.1  joerg static	int	  check_ge2(CHKARGS);
     51           1.1  joerg static	int	  check_le5(CHKARGS);
     52           1.1  joerg static	int	  check_par(CHKARGS);
     53           1.1  joerg static	int	  check_part(CHKARGS);
     54           1.1  joerg static	int	  check_root(CHKARGS);
     55      1.1.1.13  joerg static	void	  check_text(CHKARGS);
     56           1.1  joerg 
     57      1.1.1.11  joerg static	int	  post_AT(CHKARGS);
     58      1.1.1.13  joerg static	int	  post_vs(CHKARGS);
     59      1.1.1.11  joerg static	int	  post_fi(CHKARGS);
     60      1.1.1.13  joerg static	int	  post_ft(CHKARGS);
     61      1.1.1.11  joerg static	int	  post_nf(CHKARGS);
     62      1.1.1.13  joerg static	int	  post_sec(CHKARGS);
     63      1.1.1.11  joerg static	int	  post_TH(CHKARGS);
     64      1.1.1.11  joerg static	int	  post_UC(CHKARGS);
     65      1.1.1.13  joerg static	int	  pre_sec(CHKARGS);
     66      1.1.1.11  joerg 
     67      1.1.1.11  joerg static	v_check	  posts_at[] = { post_AT, NULL };
     68      1.1.1.13  joerg static	v_check	  posts_br[] = { post_vs, check_eq0, NULL };
     69           1.1  joerg static	v_check	  posts_eq0[] = { check_eq0, NULL };
     70  1.1.1.13.2.1   yamt static	v_check	  posts_eq2[] = { check_eq2, NULL };
     71      1.1.1.11  joerg static	v_check	  posts_fi[] = { check_eq0, post_fi, NULL };
     72      1.1.1.13  joerg static	v_check	  posts_ft[] = { post_ft, NULL };
     73      1.1.1.11  joerg static	v_check	  posts_nf[] = { check_eq0, post_nf, NULL };
     74           1.1  joerg static	v_check	  posts_par[] = { check_par, NULL };
     75           1.1  joerg static	v_check	  posts_part[] = { check_part, NULL };
     76      1.1.1.13  joerg static	v_check	  posts_sec[] = { post_sec, NULL };
     77      1.1.1.13  joerg static	v_check	  posts_sp[] = { post_vs, check_le1, NULL };
     78      1.1.1.12  joerg static	v_check	  posts_th[] = { check_ge2, check_le5, post_TH, NULL };
     79      1.1.1.11  joerg static	v_check	  posts_uc[] = { post_UC, NULL };
     80  1.1.1.13.2.1   yamt static	v_check	  pres_sec[] = { pre_sec, NULL };
     81      1.1.1.11  joerg 
     82           1.1  joerg static	const struct man_valid man_valids[MAN_MAX] = {
     83      1.1.1.13  joerg 	{ NULL, posts_br }, /* br */
     84  1.1.1.13.2.1   yamt 	{ NULL, posts_th }, /* TH */
     85      1.1.1.13  joerg 	{ pres_sec, posts_sec }, /* SH */
     86      1.1.1.13  joerg 	{ pres_sec, posts_sec }, /* SS */
     87  1.1.1.13.2.1   yamt 	{ NULL, NULL }, /* TP */
     88  1.1.1.13.2.1   yamt 	{ NULL, posts_par }, /* LP */
     89  1.1.1.13.2.1   yamt 	{ NULL, posts_par }, /* PP */
     90  1.1.1.13.2.1   yamt 	{ NULL, posts_par }, /* P */
     91  1.1.1.13.2.1   yamt 	{ NULL, NULL }, /* IP */
     92  1.1.1.13.2.1   yamt 	{ NULL, NULL }, /* HP */
     93           1.1  joerg 	{ NULL, NULL }, /* SM */
     94           1.1  joerg 	{ NULL, NULL }, /* SB */
     95           1.1  joerg 	{ NULL, NULL }, /* BI */
     96           1.1  joerg 	{ NULL, NULL }, /* IB */
     97           1.1  joerg 	{ NULL, NULL }, /* BR */
     98           1.1  joerg 	{ NULL, NULL }, /* RB */
     99           1.1  joerg 	{ NULL, NULL }, /* R */
    100           1.1  joerg 	{ NULL, NULL }, /* B */
    101           1.1  joerg 	{ NULL, NULL }, /* I */
    102           1.1  joerg 	{ NULL, NULL }, /* IR */
    103           1.1  joerg 	{ NULL, NULL }, /* RI */
    104  1.1.1.13.2.1   yamt 	{ NULL, posts_eq0 }, /* na */
    105  1.1.1.13.2.1   yamt 	{ NULL, posts_sp }, /* sp */
    106  1.1.1.13.2.1   yamt 	{ NULL, posts_nf }, /* nf */
    107  1.1.1.13.2.1   yamt 	{ NULL, posts_fi }, /* fi */
    108           1.1  joerg 	{ NULL, NULL }, /* RE */
    109           1.1  joerg 	{ NULL, posts_part }, /* RS */
    110           1.1  joerg 	{ NULL, NULL }, /* DT */
    111      1.1.1.11  joerg 	{ NULL, posts_uc }, /* UC */
    112       1.1.1.2  joerg 	{ NULL, NULL }, /* PD */
    113      1.1.1.11  joerg 	{ NULL, posts_at }, /* AT */
    114      1.1.1.10  joerg 	{ NULL, NULL }, /* in */
    115      1.1.1.11  joerg 	{ NULL, posts_ft }, /* ft */
    116  1.1.1.13.2.1   yamt 	{ NULL, posts_eq2 }, /* OP */
    117           1.1  joerg };
    118           1.1  joerg 
    119           1.1  joerg 
    120           1.1  joerg int
    121       1.1.1.8  joerg man_valid_pre(struct man *m, struct man_node *n)
    122           1.1  joerg {
    123           1.1  joerg 	v_check		*cp;
    124           1.1  joerg 
    125      1.1.1.11  joerg 	switch (n->type) {
    126      1.1.1.11  joerg 	case (MAN_TEXT):
    127      1.1.1.11  joerg 		/* FALLTHROUGH */
    128      1.1.1.11  joerg 	case (MAN_ROOT):
    129      1.1.1.11  joerg 		/* FALLTHROUGH */
    130      1.1.1.12  joerg 	case (MAN_EQN):
    131      1.1.1.12  joerg 		/* FALLTHROUGH */
    132      1.1.1.11  joerg 	case (MAN_TBL):
    133           1.1  joerg 		return(1);
    134      1.1.1.11  joerg 	default:
    135      1.1.1.11  joerg 		break;
    136      1.1.1.11  joerg 	}
    137           1.1  joerg 
    138           1.1  joerg 	if (NULL == (cp = man_valids[n->tok].pres))
    139           1.1  joerg 		return(1);
    140           1.1  joerg 	for ( ; *cp; cp++)
    141           1.1  joerg 		if ( ! (*cp)(m, n))
    142           1.1  joerg 			return(0);
    143           1.1  joerg 	return(1);
    144           1.1  joerg }
    145           1.1  joerg 
    146           1.1  joerg 
    147           1.1  joerg int
    148           1.1  joerg man_valid_post(struct man *m)
    149           1.1  joerg {
    150           1.1  joerg 	v_check		*cp;
    151           1.1  joerg 
    152           1.1  joerg 	if (MAN_VALID & m->last->flags)
    153           1.1  joerg 		return(1);
    154           1.1  joerg 	m->last->flags |= MAN_VALID;
    155           1.1  joerg 
    156           1.1  joerg 	switch (m->last->type) {
    157           1.1  joerg 	case (MAN_TEXT):
    158      1.1.1.13  joerg 		check_text(m, m->last);
    159      1.1.1.13  joerg 		return(1);
    160           1.1  joerg 	case (MAN_ROOT):
    161           1.1  joerg 		return(check_root(m, m->last));
    162      1.1.1.12  joerg 	case (MAN_EQN):
    163      1.1.1.12  joerg 		/* FALLTHROUGH */
    164      1.1.1.11  joerg 	case (MAN_TBL):
    165      1.1.1.11  joerg 		return(1);
    166           1.1  joerg 	default:
    167           1.1  joerg 		break;
    168           1.1  joerg 	}
    169           1.1  joerg 
    170           1.1  joerg 	if (NULL == (cp = man_valids[m->last->tok].posts))
    171           1.1  joerg 		return(1);
    172           1.1  joerg 	for ( ; *cp; cp++)
    173           1.1  joerg 		if ( ! (*cp)(m, m->last))
    174           1.1  joerg 			return(0);
    175           1.1  joerg 
    176           1.1  joerg 	return(1);
    177           1.1  joerg }
    178           1.1  joerg 
    179           1.1  joerg 
    180           1.1  joerg static int
    181           1.1  joerg check_root(CHKARGS)
    182           1.1  joerg {
    183           1.1  joerg 
    184           1.1  joerg 	if (MAN_BLINE & m->flags)
    185      1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
    186      1.1.1.11  joerg 	else if (MAN_ELINE & m->flags)
    187      1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
    188           1.1  joerg 
    189           1.1  joerg 	m->flags &= ~MAN_BLINE;
    190           1.1  joerg 	m->flags &= ~MAN_ELINE;
    191           1.1  joerg 
    192       1.1.1.7  joerg 	if (NULL == m->first->child) {
    193       1.1.1.7  joerg 		man_nmsg(m, n, MANDOCERR_NODOCBODY);
    194       1.1.1.7  joerg 		return(0);
    195       1.1.1.7  joerg 	} else if (NULL == m->meta.title) {
    196      1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_NOTITLE);
    197      1.1.1.11  joerg 
    198       1.1.1.6  joerg 		/*
    199       1.1.1.6  joerg 		 * If a title hasn't been set, do so now (by
    200       1.1.1.6  joerg 		 * implication, date and section also aren't set).
    201       1.1.1.6  joerg 		 */
    202      1.1.1.11  joerg 
    203       1.1.1.6  joerg 	        m->meta.title = mandoc_strdup("unknown");
    204       1.1.1.7  joerg 		m->meta.msec = mandoc_strdup("1");
    205      1.1.1.12  joerg 		m->meta.date = mandoc_normdate
    206      1.1.1.12  joerg 			(m->parse, NULL, n->line, n->pos);
    207       1.1.1.6  joerg 	}
    208           1.1  joerg 
    209           1.1  joerg 	return(1);
    210           1.1  joerg }
    211           1.1  joerg 
    212      1.1.1.13  joerg static void
    213      1.1.1.13  joerg check_text(CHKARGS)
    214           1.1  joerg {
    215      1.1.1.13  joerg 	char		*cp, *p;
    216           1.1  joerg 
    217  1.1.1.13.2.1   yamt 	if (MAN_LITERAL & m->flags)
    218  1.1.1.13.2.1   yamt 		return;
    219  1.1.1.13.2.1   yamt 
    220  1.1.1.13.2.1   yamt 	cp = n->string;
    221  1.1.1.13.2.1   yamt 	for (p = cp; NULL != (p = strchr(p, '\t')); p++)
    222      1.1.1.13  joerg 		man_pmsg(m, n->line, (int)(p - cp), MANDOCERR_BADTAB);
    223           1.1  joerg }
    224           1.1  joerg 
    225           1.1  joerg #define	INEQ_DEFINE(x, ineq, name) \
    226           1.1  joerg static int \
    227           1.1  joerg check_##name(CHKARGS) \
    228           1.1  joerg { \
    229           1.1  joerg 	if (n->nchild ineq (x)) \
    230           1.1  joerg 		return(1); \
    231      1.1.1.12  joerg 	mandoc_vmsg(MANDOCERR_ARGCOUNT, m->parse, n->line, n->pos, \
    232       1.1.1.7  joerg 			"line arguments %s %d (have %d)", \
    233       1.1.1.7  joerg 			#ineq, (x), n->nchild); \
    234      1.1.1.12  joerg 	return(1); \
    235           1.1  joerg }
    236           1.1  joerg 
    237           1.1  joerg INEQ_DEFINE(0, ==, eq0)
    238  1.1.1.13.2.1   yamt INEQ_DEFINE(2, ==, eq2)
    239           1.1  joerg INEQ_DEFINE(1, <=, le1)
    240           1.1  joerg INEQ_DEFINE(2, >=, ge2)
    241           1.1  joerg INEQ_DEFINE(5, <=, le5)
    242           1.1  joerg 
    243      1.1.1.11  joerg static int
    244      1.1.1.13  joerg post_ft(CHKARGS)
    245      1.1.1.11  joerg {
    246      1.1.1.11  joerg 	char	*cp;
    247      1.1.1.11  joerg 	int	 ok;
    248      1.1.1.11  joerg 
    249      1.1.1.11  joerg 	if (0 == n->nchild)
    250      1.1.1.11  joerg 		return(1);
    251      1.1.1.11  joerg 
    252      1.1.1.11  joerg 	ok = 0;
    253      1.1.1.11  joerg 	cp = n->child->string;
    254      1.1.1.11  joerg 	switch (*cp) {
    255      1.1.1.11  joerg 	case ('1'):
    256      1.1.1.11  joerg 		/* FALLTHROUGH */
    257      1.1.1.11  joerg 	case ('2'):
    258      1.1.1.11  joerg 		/* FALLTHROUGH */
    259      1.1.1.11  joerg 	case ('3'):
    260      1.1.1.11  joerg 		/* FALLTHROUGH */
    261      1.1.1.11  joerg 	case ('4'):
    262      1.1.1.11  joerg 		/* FALLTHROUGH */
    263      1.1.1.11  joerg 	case ('I'):
    264      1.1.1.11  joerg 		/* FALLTHROUGH */
    265      1.1.1.11  joerg 	case ('P'):
    266      1.1.1.11  joerg 		/* FALLTHROUGH */
    267      1.1.1.11  joerg 	case ('R'):
    268      1.1.1.11  joerg 		if ('\0' == cp[1])
    269      1.1.1.11  joerg 			ok = 1;
    270      1.1.1.11  joerg 		break;
    271      1.1.1.11  joerg 	case ('B'):
    272      1.1.1.11  joerg 		if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
    273      1.1.1.11  joerg 			ok = 1;
    274      1.1.1.11  joerg 		break;
    275      1.1.1.11  joerg 	case ('C'):
    276      1.1.1.11  joerg 		if ('W' == cp[1] && '\0' == cp[2])
    277      1.1.1.11  joerg 			ok = 1;
    278      1.1.1.11  joerg 		break;
    279      1.1.1.11  joerg 	default:
    280      1.1.1.11  joerg 		break;
    281      1.1.1.11  joerg 	}
    282      1.1.1.11  joerg 
    283      1.1.1.11  joerg 	if (0 == ok) {
    284      1.1.1.12  joerg 		mandoc_vmsg
    285      1.1.1.12  joerg 			(MANDOCERR_BADFONT, m->parse,
    286      1.1.1.12  joerg 			 n->line, n->pos, "%s", cp);
    287      1.1.1.11  joerg 		*cp = '\0';
    288      1.1.1.11  joerg 	}
    289      1.1.1.11  joerg 
    290      1.1.1.11  joerg 	if (1 < n->nchild)
    291      1.1.1.12  joerg 		mandoc_vmsg
    292      1.1.1.12  joerg 			(MANDOCERR_ARGCOUNT, m->parse, n->line,
    293      1.1.1.12  joerg 			 n->pos, "want one child (have %d)",
    294      1.1.1.12  joerg 			 n->nchild);
    295      1.1.1.11  joerg 
    296      1.1.1.11  joerg 	return(1);
    297      1.1.1.11  joerg }
    298           1.1  joerg 
    299           1.1  joerg static int
    300      1.1.1.13  joerg pre_sec(CHKARGS)
    301           1.1  joerg {
    302           1.1  joerg 
    303      1.1.1.13  joerg 	if (MAN_BLOCK == n->type)
    304      1.1.1.13  joerg 		m->flags &= ~MAN_LITERAL;
    305           1.1  joerg 	return(1);
    306           1.1  joerg }
    307           1.1  joerg 
    308      1.1.1.13  joerg static int
    309      1.1.1.13  joerg post_sec(CHKARGS)
    310      1.1.1.13  joerg {
    311      1.1.1.13  joerg 
    312      1.1.1.13  joerg 	if ( ! (MAN_HEAD == n->type && 0 == n->nchild))
    313      1.1.1.13  joerg 		return(1);
    314      1.1.1.13  joerg 
    315      1.1.1.13  joerg 	man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
    316      1.1.1.13  joerg 	return(0);
    317      1.1.1.13  joerg }
    318           1.1  joerg 
    319           1.1  joerg static int
    320           1.1  joerg check_part(CHKARGS)
    321           1.1  joerg {
    322           1.1  joerg 
    323           1.1  joerg 	if (MAN_BODY == n->type && 0 == n->nchild)
    324      1.1.1.12  joerg 		mandoc_msg(MANDOCERR_ARGCWARN, m->parse, n->line,
    325      1.1.1.12  joerg 				n->pos, "want children (have none)");
    326      1.1.1.11  joerg 
    327           1.1  joerg 	return(1);
    328           1.1  joerg }
    329           1.1  joerg 
    330           1.1  joerg 
    331           1.1  joerg static int
    332           1.1  joerg check_par(CHKARGS)
    333           1.1  joerg {
    334           1.1  joerg 
    335      1.1.1.12  joerg 	switch (n->type) {
    336      1.1.1.12  joerg 	case (MAN_BLOCK):
    337      1.1.1.12  joerg 		if (0 == n->body->nchild)
    338      1.1.1.12  joerg 			man_node_delete(m, n);
    339      1.1.1.12  joerg 		break;
    340      1.1.1.12  joerg 	case (MAN_BODY):
    341      1.1.1.12  joerg 		if (0 == n->nchild)
    342      1.1.1.12  joerg 			man_nmsg(m, n, MANDOCERR_IGNPAR);
    343      1.1.1.12  joerg 		break;
    344      1.1.1.12  joerg 	case (MAN_HEAD):
    345      1.1.1.12  joerg 		if (n->nchild)
    346      1.1.1.12  joerg 			man_nmsg(m, n, MANDOCERR_ARGSLOST);
    347      1.1.1.12  joerg 		break;
    348      1.1.1.12  joerg 	default:
    349      1.1.1.12  joerg 		break;
    350      1.1.1.12  joerg 	}
    351           1.1  joerg 
    352           1.1  joerg 	return(1);
    353           1.1  joerg }
    354           1.1  joerg 
    355           1.1  joerg 
    356           1.1  joerg static int
    357      1.1.1.11  joerg post_TH(CHKARGS)
    358      1.1.1.11  joerg {
    359      1.1.1.12  joerg 	const char	*p;
    360      1.1.1.12  joerg 	int		 line, pos;
    361      1.1.1.11  joerg 
    362      1.1.1.11  joerg 	if (m->meta.title)
    363      1.1.1.11  joerg 		free(m->meta.title);
    364      1.1.1.11  joerg 	if (m->meta.vol)
    365      1.1.1.11  joerg 		free(m->meta.vol);
    366      1.1.1.11  joerg 	if (m->meta.source)
    367      1.1.1.11  joerg 		free(m->meta.source);
    368      1.1.1.11  joerg 	if (m->meta.msec)
    369      1.1.1.11  joerg 		free(m->meta.msec);
    370      1.1.1.12  joerg 	if (m->meta.date)
    371      1.1.1.12  joerg 		free(m->meta.date);
    372      1.1.1.11  joerg 
    373      1.1.1.12  joerg 	line = n->line;
    374      1.1.1.12  joerg 	pos = n->pos;
    375      1.1.1.12  joerg 	m->meta.title = m->meta.vol = m->meta.date =
    376      1.1.1.11  joerg 		m->meta.msec = m->meta.source = NULL;
    377      1.1.1.11  joerg 
    378      1.1.1.11  joerg 	/* ->TITLE<- MSEC DATE SOURCE VOL */
    379      1.1.1.11  joerg 
    380      1.1.1.11  joerg 	n = n->child;
    381      1.1.1.12  joerg 	if (n && n->string) {
    382      1.1.1.12  joerg 		for (p = n->string; '\0' != *p; p++) {
    383      1.1.1.12  joerg 			/* Only warn about this once... */
    384      1.1.1.13  joerg 			if (isalpha((unsigned char)*p) &&
    385      1.1.1.13  joerg 					! isupper((unsigned char)*p)) {
    386      1.1.1.12  joerg 				man_nmsg(m, n, MANDOCERR_UPPERCASE);
    387      1.1.1.12  joerg 				break;
    388      1.1.1.12  joerg 			}
    389      1.1.1.12  joerg 		}
    390      1.1.1.12  joerg 		m->meta.title = mandoc_strdup(n->string);
    391      1.1.1.12  joerg 	} else
    392      1.1.1.12  joerg 		m->meta.title = mandoc_strdup("");
    393      1.1.1.11  joerg 
    394      1.1.1.11  joerg 	/* TITLE ->MSEC<- DATE SOURCE VOL */
    395      1.1.1.11  joerg 
    396      1.1.1.12  joerg 	if (n)
    397      1.1.1.12  joerg 		n = n->next;
    398      1.1.1.12  joerg 	if (n && n->string)
    399      1.1.1.12  joerg 		m->meta.msec = mandoc_strdup(n->string);
    400      1.1.1.12  joerg 	else
    401      1.1.1.12  joerg 		m->meta.msec = mandoc_strdup("");
    402      1.1.1.11  joerg 
    403      1.1.1.11  joerg 	/* TITLE MSEC ->DATE<- SOURCE VOL */
    404      1.1.1.11  joerg 
    405      1.1.1.12  joerg 	if (n)
    406      1.1.1.12  joerg 		n = n->next;
    407  1.1.1.13.2.1   yamt 	if (n && n->string && '\0' != n->string[0]) {
    408      1.1.1.12  joerg 		pos = n->pos;
    409  1.1.1.13.2.1   yamt 		m->meta.date = mandoc_normdate
    410  1.1.1.13.2.1   yamt 		    (m->parse, n->string, line, pos);
    411  1.1.1.13.2.1   yamt 	} else
    412  1.1.1.13.2.1   yamt 		m->meta.date = mandoc_strdup("");
    413      1.1.1.11  joerg 
    414      1.1.1.11  joerg 	/* TITLE MSEC DATE ->SOURCE<- VOL */
    415      1.1.1.11  joerg 
    416      1.1.1.11  joerg 	if (n && (n = n->next))
    417      1.1.1.11  joerg 		m->meta.source = mandoc_strdup(n->string);
    418      1.1.1.11  joerg 
    419      1.1.1.11  joerg 	/* TITLE MSEC DATE SOURCE ->VOL<- */
    420  1.1.1.13.2.1   yamt 	/* If missing, use the default VOL name for MSEC. */
    421      1.1.1.11  joerg 
    422      1.1.1.11  joerg 	if (n && (n = n->next))
    423      1.1.1.11  joerg 		m->meta.vol = mandoc_strdup(n->string);
    424  1.1.1.13.2.1   yamt 	else if ('\0' != m->meta.msec[0] &&
    425  1.1.1.13.2.1   yamt 	    (NULL != (p = mandoc_a2msec(m->meta.msec))))
    426  1.1.1.13.2.1   yamt 		m->meta.vol = mandoc_strdup(p);
    427      1.1.1.11  joerg 
    428      1.1.1.11  joerg 	/*
    429      1.1.1.11  joerg 	 * Remove the `TH' node after we've processed it for our
    430      1.1.1.11  joerg 	 * meta-data.
    431      1.1.1.11  joerg 	 */
    432      1.1.1.11  joerg 	man_node_delete(m, m->last);
    433      1.1.1.11  joerg 	return(1);
    434      1.1.1.11  joerg }
    435      1.1.1.11  joerg 
    436      1.1.1.11  joerg static int
    437      1.1.1.11  joerg post_nf(CHKARGS)
    438      1.1.1.11  joerg {
    439      1.1.1.11  joerg 
    440      1.1.1.11  joerg 	if (MAN_LITERAL & m->flags)
    441      1.1.1.11  joerg 		man_nmsg(m, n, MANDOCERR_SCOPEREP);
    442      1.1.1.11  joerg 
    443      1.1.1.11  joerg 	m->flags |= MAN_LITERAL;
    444      1.1.1.11  joerg 	return(1);
    445      1.1.1.11  joerg }
    446      1.1.1.11  joerg 
    447      1.1.1.11  joerg static int
    448      1.1.1.11  joerg post_fi(CHKARGS)
    449      1.1.1.11  joerg {
    450      1.1.1.11  joerg 
    451      1.1.1.11  joerg 	if ( ! (MAN_LITERAL & m->flags))
    452      1.1.1.12  joerg 		man_nmsg(m, n, MANDOCERR_WNOSCOPE);
    453      1.1.1.11  joerg 
    454      1.1.1.11  joerg 	m->flags &= ~MAN_LITERAL;
    455      1.1.1.11  joerg 	return(1);
    456      1.1.1.11  joerg }
    457      1.1.1.11  joerg 
    458      1.1.1.11  joerg static int
    459      1.1.1.11  joerg post_UC(CHKARGS)
    460      1.1.1.11  joerg {
    461      1.1.1.11  joerg 	static const char * const bsd_versions[] = {
    462      1.1.1.11  joerg 	    "3rd Berkeley Distribution",
    463      1.1.1.11  joerg 	    "4th Berkeley Distribution",
    464      1.1.1.11  joerg 	    "4.2 Berkeley Distribution",
    465      1.1.1.11  joerg 	    "4.3 Berkeley Distribution",
    466      1.1.1.11  joerg 	    "4.4 Berkeley Distribution",
    467      1.1.1.11  joerg 	};
    468      1.1.1.11  joerg 
    469      1.1.1.11  joerg 	const char	*p, *s;
    470      1.1.1.11  joerg 
    471      1.1.1.11  joerg 	n = n->child;
    472      1.1.1.11  joerg 
    473      1.1.1.11  joerg 	if (NULL == n || MAN_TEXT != n->type)
    474      1.1.1.11  joerg 		p = bsd_versions[0];
    475      1.1.1.11  joerg 	else {
    476      1.1.1.11  joerg 		s = n->string;
    477      1.1.1.11  joerg 		if (0 == strcmp(s, "3"))
    478      1.1.1.11  joerg 			p = bsd_versions[0];
    479      1.1.1.11  joerg 		else if (0 == strcmp(s, "4"))
    480      1.1.1.11  joerg 			p = bsd_versions[1];
    481      1.1.1.11  joerg 		else if (0 == strcmp(s, "5"))
    482      1.1.1.11  joerg 			p = bsd_versions[2];
    483      1.1.1.11  joerg 		else if (0 == strcmp(s, "6"))
    484      1.1.1.11  joerg 			p = bsd_versions[3];
    485      1.1.1.11  joerg 		else if (0 == strcmp(s, "7"))
    486      1.1.1.11  joerg 			p = bsd_versions[4];
    487      1.1.1.11  joerg 		else
    488      1.1.1.11  joerg 			p = bsd_versions[0];
    489      1.1.1.11  joerg 	}
    490      1.1.1.11  joerg 
    491      1.1.1.11  joerg 	if (m->meta.source)
    492      1.1.1.11  joerg 		free(m->meta.source);
    493      1.1.1.11  joerg 
    494      1.1.1.11  joerg 	m->meta.source = mandoc_strdup(p);
    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_AT(CHKARGS)
    500      1.1.1.11  joerg {
    501      1.1.1.11  joerg 	static const char * const unix_versions[] = {
    502      1.1.1.11  joerg 	    "7th Edition",
    503      1.1.1.11  joerg 	    "System III",
    504      1.1.1.11  joerg 	    "System V",
    505      1.1.1.11  joerg 	    "System V Release 2",
    506      1.1.1.11  joerg 	};
    507      1.1.1.11  joerg 
    508      1.1.1.11  joerg 	const char	*p, *s;
    509      1.1.1.11  joerg 	struct man_node	*nn;
    510      1.1.1.11  joerg 
    511      1.1.1.11  joerg 	n = n->child;
    512      1.1.1.11  joerg 
    513      1.1.1.11  joerg 	if (NULL == n || MAN_TEXT != n->type)
    514      1.1.1.11  joerg 		p = unix_versions[0];
    515      1.1.1.11  joerg 	else {
    516      1.1.1.11  joerg 		s = n->string;
    517      1.1.1.11  joerg 		if (0 == strcmp(s, "3"))
    518      1.1.1.11  joerg 			p = unix_versions[0];
    519      1.1.1.11  joerg 		else if (0 == strcmp(s, "4"))
    520      1.1.1.11  joerg 			p = unix_versions[1];
    521      1.1.1.11  joerg 		else if (0 == strcmp(s, "5")) {
    522      1.1.1.11  joerg 			nn = n->next;
    523      1.1.1.11  joerg 			if (nn && MAN_TEXT == nn->type && nn->string[0])
    524      1.1.1.11  joerg 				p = unix_versions[3];
    525      1.1.1.11  joerg 			else
    526      1.1.1.11  joerg 				p = unix_versions[2];
    527      1.1.1.11  joerg 		} else
    528      1.1.1.11  joerg 			p = unix_versions[0];
    529      1.1.1.11  joerg 	}
    530      1.1.1.11  joerg 
    531      1.1.1.11  joerg 	if (m->meta.source)
    532      1.1.1.11  joerg 		free(m->meta.source);
    533      1.1.1.11  joerg 
    534      1.1.1.11  joerg 	m->meta.source = mandoc_strdup(p);
    535      1.1.1.11  joerg 	return(1);
    536      1.1.1.11  joerg }
    537      1.1.1.13  joerg 
    538      1.1.1.13  joerg static int
    539      1.1.1.13  joerg post_vs(CHKARGS)
    540      1.1.1.13  joerg {
    541      1.1.1.13  joerg 
    542      1.1.1.13  joerg 	/*
    543      1.1.1.13  joerg 	 * Don't warn about this because it occurs in pod2man and would
    544      1.1.1.13  joerg 	 * cause considerable (unfixable) warnage.
    545      1.1.1.13  joerg 	 */
    546      1.1.1.13  joerg 	if (NULL == n->prev && MAN_ROOT == n->parent->type)
    547      1.1.1.13  joerg 		man_node_delete(m, n);
    548      1.1.1.13  joerg 
    549      1.1.1.13  joerg 	return(1);
    550      1.1.1.13  joerg }
    551