Home | History | Annotate | Line # | Download | only in dist
man_validate.c revision 1.1.1.18
      1  1.1.1.16  christos /*	$OpenBSD$ */
      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.18  christos  * Copyright (c) 2010, 2012-2018 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.1.17  christos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
     11       1.1     joerg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  1.1.1.17  christos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 #include "config.h"
     19   1.1.1.4     joerg 
     20       1.1     joerg #include <sys/types.h>
     21       1.1     joerg 
     22       1.1     joerg #include <assert.h>
     23       1.1     joerg #include <ctype.h>
     24       1.1     joerg #include <errno.h>
     25       1.1     joerg #include <limits.h>
     26       1.1     joerg #include <stdarg.h>
     27       1.1     joerg #include <stdlib.h>
     28  1.1.1.10     joerg #include <string.h>
     29  1.1.1.11     joerg #include <time.h>
     30       1.1     joerg 
     31  1.1.1.16  christos #include "mandoc_aux.h"
     32  1.1.1.17  christos #include "mandoc.h"
     33  1.1.1.17  christos #include "roff.h"
     34  1.1.1.17  christos #include "man.h"
     35       1.1     joerg #include "libmandoc.h"
     36  1.1.1.17  christos #include "roff_int.h"
     37  1.1.1.17  christos #include "libman.h"
     38       1.1     joerg 
     39  1.1.1.17  christos #define	CHKARGS	  struct roff_man *man, struct roff_node *n
     40       1.1     joerg 
     41  1.1.1.16  christos typedef	void	(*v_check)(CHKARGS);
     42       1.1     joerg 
     43  1.1.1.16  christos static	void	  check_par(CHKARGS);
     44  1.1.1.16  christos static	void	  check_part(CHKARGS);
     45  1.1.1.16  christos static	void	  check_root(CHKARGS);
     46  1.1.1.13     joerg static	void	  check_text(CHKARGS);
     47       1.1     joerg 
     48  1.1.1.16  christos static	void	  post_AT(CHKARGS);
     49  1.1.1.16  christos static	void	  post_IP(CHKARGS);
     50  1.1.1.16  christos static	void	  post_OP(CHKARGS);
     51  1.1.1.16  christos static	void	  post_TH(CHKARGS);
     52  1.1.1.16  christos static	void	  post_UC(CHKARGS);
     53  1.1.1.16  christos static	void	  post_UR(CHKARGS);
     54  1.1.1.18  christos static	void	  post_in(CHKARGS);
     55  1.1.1.18  christos static	void	  post_vs(CHKARGS);
     56  1.1.1.16  christos 
     57  1.1.1.18  christos static	const v_check __man_valids[MAN_MAX - MAN_TH] = {
     58  1.1.1.16  christos 	post_TH,    /* TH */
     59  1.1.1.16  christos 	NULL,       /* SH */
     60  1.1.1.16  christos 	NULL,       /* SS */
     61  1.1.1.16  christos 	NULL,       /* TP */
     62  1.1.1.16  christos 	check_par,  /* LP */
     63  1.1.1.16  christos 	check_par,  /* PP */
     64  1.1.1.16  christos 	check_par,  /* P */
     65  1.1.1.16  christos 	post_IP,    /* IP */
     66  1.1.1.16  christos 	NULL,       /* HP */
     67  1.1.1.16  christos 	NULL,       /* SM */
     68  1.1.1.16  christos 	NULL,       /* SB */
     69  1.1.1.16  christos 	NULL,       /* BI */
     70  1.1.1.16  christos 	NULL,       /* IB */
     71  1.1.1.16  christos 	NULL,       /* BR */
     72  1.1.1.16  christos 	NULL,       /* RB */
     73  1.1.1.16  christos 	NULL,       /* R */
     74  1.1.1.16  christos 	NULL,       /* B */
     75  1.1.1.16  christos 	NULL,       /* I */
     76  1.1.1.16  christos 	NULL,       /* IR */
     77  1.1.1.16  christos 	NULL,       /* RI */
     78  1.1.1.17  christos 	NULL,       /* nf */
     79  1.1.1.17  christos 	NULL,       /* fi */
     80  1.1.1.16  christos 	NULL,       /* RE */
     81  1.1.1.16  christos 	check_part, /* RS */
     82  1.1.1.16  christos 	NULL,       /* DT */
     83  1.1.1.16  christos 	post_UC,    /* UC */
     84  1.1.1.16  christos 	NULL,       /* PD */
     85  1.1.1.16  christos 	post_AT,    /* AT */
     86  1.1.1.18  christos 	post_in,    /* in */
     87  1.1.1.16  christos 	post_OP,    /* OP */
     88  1.1.1.17  christos 	NULL,       /* EX */
     89  1.1.1.17  christos 	NULL,       /* EE */
     90  1.1.1.16  christos 	post_UR,    /* UR */
     91  1.1.1.16  christos 	NULL,       /* UE */
     92  1.1.1.18  christos 	post_UR,    /* MT */
     93  1.1.1.18  christos 	NULL,       /* ME */
     94       1.1     joerg };
     95  1.1.1.18  christos static	const v_check *man_valids = __man_valids - MAN_TH;
     96       1.1     joerg 
     97       1.1     joerg 
     98  1.1.1.16  christos void
     99  1.1.1.17  christos man_node_validate(struct roff_man *man)
    100       1.1     joerg {
    101  1.1.1.17  christos 	struct roff_node *n;
    102  1.1.1.18  christos 	const v_check	 *cp;
    103       1.1     joerg 
    104  1.1.1.16  christos 	n = man->last;
    105  1.1.1.17  christos 	man->last = man->last->child;
    106  1.1.1.17  christos 	while (man->last != NULL) {
    107  1.1.1.17  christos 		man_node_validate(man);
    108  1.1.1.17  christos 		if (man->last == n)
    109  1.1.1.17  christos 			man->last = man->last->child;
    110  1.1.1.17  christos 		else
    111  1.1.1.17  christos 			man->last = man->last->next;
    112  1.1.1.17  christos 	}
    113  1.1.1.16  christos 
    114  1.1.1.17  christos 	man->last = n;
    115  1.1.1.17  christos 	man->next = ROFF_NEXT_SIBLING;
    116  1.1.1.11     joerg 	switch (n->type) {
    117  1.1.1.17  christos 	case ROFFT_TEXT:
    118  1.1.1.16  christos 		check_text(man, n);
    119  1.1.1.11     joerg 		break;
    120  1.1.1.17  christos 	case ROFFT_ROOT:
    121  1.1.1.16  christos 		check_root(man, n);
    122  1.1.1.16  christos 		break;
    123  1.1.1.18  christos 	case ROFFT_COMMENT:
    124  1.1.1.17  christos 	case ROFFT_EQN:
    125  1.1.1.17  christos 	case ROFFT_TBL:
    126  1.1.1.16  christos 		break;
    127       1.1     joerg 	default:
    128  1.1.1.18  christos 		if (n->tok < ROFF_MAX) {
    129  1.1.1.18  christos 			switch (n->tok) {
    130  1.1.1.18  christos 			case ROFF_br:
    131  1.1.1.18  christos 			case ROFF_sp:
    132  1.1.1.18  christos 				post_vs(man, n);
    133  1.1.1.18  christos 				break;
    134  1.1.1.18  christos 			default:
    135  1.1.1.18  christos 				roff_validate(man);
    136  1.1.1.18  christos 				break;
    137  1.1.1.18  christos 			}
    138  1.1.1.18  christos 			break;
    139  1.1.1.18  christos 		}
    140  1.1.1.18  christos 		assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
    141  1.1.1.16  christos 		cp = man_valids + n->tok;
    142  1.1.1.16  christos 		if (*cp)
    143  1.1.1.16  christos 			(*cp)(man, n);
    144  1.1.1.17  christos 		if (man->last == n)
    145  1.1.1.17  christos 			man_state(man, n);
    146       1.1     joerg 		break;
    147       1.1     joerg 	}
    148       1.1     joerg }
    149       1.1     joerg 
    150  1.1.1.16  christos static void
    151  1.1.1.16  christos check_root(CHKARGS)
    152       1.1     joerg {
    153  1.1.1.16  christos 	assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0);
    154       1.1     joerg 
    155  1.1.1.18  christos 	if (n->last == NULL || n->last->type == ROFFT_COMMENT)
    156  1.1.1.16  christos 		mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse,
    157  1.1.1.16  christos 		    n->line, n->pos, NULL);
    158  1.1.1.16  christos 	else
    159  1.1.1.16  christos 		man->meta.hasbody = 1;
    160       1.1     joerg 
    161  1.1.1.16  christos 	if (NULL == man->meta.title) {
    162  1.1.1.16  christos 		mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
    163  1.1.1.16  christos 		    n->line, n->pos, NULL);
    164  1.1.1.11     joerg 
    165   1.1.1.6     joerg 		/*
    166   1.1.1.6     joerg 		 * If a title hasn't been set, do so now (by
    167   1.1.1.6     joerg 		 * implication, date and section also aren't set).
    168   1.1.1.6     joerg 		 */
    169  1.1.1.11     joerg 
    170  1.1.1.16  christos 		man->meta.title = mandoc_strdup("");
    171  1.1.1.16  christos 		man->meta.msec = mandoc_strdup("");
    172  1.1.1.16  christos 		man->meta.date = man->quick ? mandoc_strdup("") :
    173  1.1.1.18  christos 		    mandoc_normdate(man, NULL, n->line, n->pos);
    174   1.1.1.6     joerg 	}
    175  1.1.1.18  christos 
    176  1.1.1.18  christos 	if (man->meta.os_e &&
    177  1.1.1.18  christos 	    (man->meta.rcsids & (1 << man->meta.os_e)) == 0)
    178  1.1.1.18  christos 		mandoc_msg(MANDOCERR_RCS_MISSING, man->parse, 0, 0,
    179  1.1.1.18  christos 		    man->meta.os_e == MANDOC_OS_OPENBSD ?
    180  1.1.1.18  christos 		    "(OpenBSD)" : "(NetBSD)");
    181       1.1     joerg }
    182       1.1     joerg 
    183  1.1.1.13     joerg static void
    184  1.1.1.13     joerg check_text(CHKARGS)
    185       1.1     joerg {
    186  1.1.1.13     joerg 	char		*cp, *p;
    187       1.1     joerg 
    188  1.1.1.15     joerg 	if (MAN_LITERAL & man->flags)
    189  1.1.1.14     joerg 		return;
    190  1.1.1.14     joerg 
    191  1.1.1.14     joerg 	cp = n->string;
    192  1.1.1.14     joerg 	for (p = cp; NULL != (p = strchr(p, '\t')); p++)
    193  1.1.1.16  christos 		mandoc_msg(MANDOCERR_FI_TAB, man->parse,
    194  1.1.1.16  christos 		    n->line, n->pos + (p - cp), NULL);
    195       1.1     joerg }
    196       1.1     joerg 
    197  1.1.1.16  christos static void
    198  1.1.1.16  christos post_OP(CHKARGS)
    199  1.1.1.16  christos {
    200       1.1     joerg 
    201  1.1.1.17  christos 	if (n->child == NULL)
    202  1.1.1.16  christos 		mandoc_msg(MANDOCERR_OP_EMPTY, man->parse,
    203  1.1.1.16  christos 		    n->line, n->pos, "OP");
    204  1.1.1.17  christos 	else if (n->child->next != NULL && n->child->next->next != NULL) {
    205  1.1.1.16  christos 		n = n->child->next->next;
    206  1.1.1.16  christos 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
    207  1.1.1.16  christos 		    n->line, n->pos, "OP ... %s", n->string);
    208  1.1.1.16  christos 	}
    209  1.1.1.16  christos }
    210       1.1     joerg 
    211  1.1.1.16  christos static void
    212  1.1.1.16  christos post_UR(CHKARGS)
    213  1.1.1.15     joerg {
    214  1.1.1.17  christos 	if (n->type == ROFFT_HEAD && n->child == NULL)
    215  1.1.1.18  christos 		mandoc_msg(MANDOCERR_UR_NOHEAD, man->parse,
    216  1.1.1.18  christos 		    n->line, n->pos, roff_name[n->tok]);
    217  1.1.1.16  christos 	check_part(man, n);
    218  1.1.1.15     joerg }
    219  1.1.1.15     joerg 
    220  1.1.1.16  christos static void
    221       1.1     joerg check_part(CHKARGS)
    222       1.1     joerg {
    223       1.1     joerg 
    224  1.1.1.17  christos 	if (n->type == ROFFT_BODY && n->child == NULL)
    225  1.1.1.16  christos 		mandoc_msg(MANDOCERR_BLK_EMPTY, man->parse,
    226  1.1.1.18  christos 		    n->line, n->pos, roff_name[n->tok]);
    227       1.1     joerg }
    228       1.1     joerg 
    229  1.1.1.16  christos static void
    230       1.1     joerg check_par(CHKARGS)
    231       1.1     joerg {
    232       1.1     joerg 
    233  1.1.1.12     joerg 	switch (n->type) {
    234  1.1.1.17  christos 	case ROFFT_BLOCK:
    235  1.1.1.17  christos 		if (n->body->child == NULL)
    236  1.1.1.17  christos 			roff_node_delete(man, n);
    237  1.1.1.12     joerg 		break;
    238  1.1.1.17  christos 	case ROFFT_BODY:
    239  1.1.1.17  christos 		if (n->child == NULL)
    240  1.1.1.16  christos 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
    241  1.1.1.16  christos 			    man->parse, n->line, n->pos,
    242  1.1.1.18  christos 			    "%s empty", roff_name[n->tok]);
    243  1.1.1.12     joerg 		break;
    244  1.1.1.17  christos 	case ROFFT_HEAD:
    245  1.1.1.17  christos 		if (n->child != NULL)
    246  1.1.1.16  christos 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
    247  1.1.1.18  christos 			    man->parse, n->line, n->pos, "%s %s%s",
    248  1.1.1.18  christos 			    roff_name[n->tok], n->child->string,
    249  1.1.1.17  christos 			    n->child->next != NULL ? " ..." : "");
    250  1.1.1.12     joerg 		break;
    251  1.1.1.12     joerg 	default:
    252  1.1.1.12     joerg 		break;
    253  1.1.1.12     joerg 	}
    254       1.1     joerg }
    255       1.1     joerg 
    256  1.1.1.16  christos static void
    257  1.1.1.15     joerg post_IP(CHKARGS)
    258  1.1.1.15     joerg {
    259  1.1.1.15     joerg 
    260  1.1.1.15     joerg 	switch (n->type) {
    261  1.1.1.17  christos 	case ROFFT_BLOCK:
    262  1.1.1.17  christos 		if (n->head->child == NULL && n->body->child == NULL)
    263  1.1.1.17  christos 			roff_node_delete(man, n);
    264  1.1.1.15     joerg 		break;
    265  1.1.1.17  christos 	case ROFFT_BODY:
    266  1.1.1.17  christos 		if (n->parent->head->child == NULL && n->child == NULL)
    267  1.1.1.16  christos 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
    268  1.1.1.16  christos 			    man->parse, n->line, n->pos,
    269  1.1.1.18  christos 			    "%s empty", roff_name[n->tok]);
    270  1.1.1.15     joerg 		break;
    271  1.1.1.15     joerg 	default:
    272  1.1.1.15     joerg 		break;
    273  1.1.1.15     joerg 	}
    274  1.1.1.15     joerg }
    275       1.1     joerg 
    276  1.1.1.16  christos static void
    277  1.1.1.11     joerg post_TH(CHKARGS)
    278  1.1.1.11     joerg {
    279  1.1.1.17  christos 	struct roff_node *nb;
    280  1.1.1.12     joerg 	const char	*p;
    281  1.1.1.11     joerg 
    282  1.1.1.15     joerg 	free(man->meta.title);
    283  1.1.1.15     joerg 	free(man->meta.vol);
    284  1.1.1.17  christos 	free(man->meta.os);
    285  1.1.1.15     joerg 	free(man->meta.msec);
    286  1.1.1.15     joerg 	free(man->meta.date);
    287  1.1.1.15     joerg 
    288  1.1.1.15     joerg 	man->meta.title = man->meta.vol = man->meta.date =
    289  1.1.1.17  christos 	    man->meta.msec = man->meta.os = NULL;
    290  1.1.1.16  christos 
    291  1.1.1.16  christos 	nb = n;
    292  1.1.1.11     joerg 
    293  1.1.1.17  christos 	/* ->TITLE<- MSEC DATE OS VOL */
    294  1.1.1.11     joerg 
    295  1.1.1.11     joerg 	n = n->child;
    296  1.1.1.12     joerg 	if (n && n->string) {
    297  1.1.1.12     joerg 		for (p = n->string; '\0' != *p; p++) {
    298  1.1.1.12     joerg 			/* Only warn about this once... */
    299  1.1.1.16  christos 			if (isalpha((unsigned char)*p) &&
    300  1.1.1.16  christos 			    ! isupper((unsigned char)*p)) {
    301  1.1.1.16  christos 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
    302  1.1.1.16  christos 				    man->parse, n->line,
    303  1.1.1.16  christos 				    n->pos + (p - n->string),
    304  1.1.1.16  christos 				    "TH %s", n->string);
    305  1.1.1.12     joerg 				break;
    306  1.1.1.12     joerg 			}
    307  1.1.1.12     joerg 		}
    308  1.1.1.15     joerg 		man->meta.title = mandoc_strdup(n->string);
    309  1.1.1.16  christos 	} else {
    310  1.1.1.15     joerg 		man->meta.title = mandoc_strdup("");
    311  1.1.1.16  christos 		mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
    312  1.1.1.16  christos 		    nb->line, nb->pos, "TH");
    313  1.1.1.16  christos 	}
    314  1.1.1.11     joerg 
    315  1.1.1.17  christos 	/* TITLE ->MSEC<- DATE OS VOL */
    316  1.1.1.11     joerg 
    317  1.1.1.12     joerg 	if (n)
    318  1.1.1.12     joerg 		n = n->next;
    319  1.1.1.12     joerg 	if (n && n->string)
    320  1.1.1.15     joerg 		man->meta.msec = mandoc_strdup(n->string);
    321  1.1.1.16  christos 	else {
    322  1.1.1.15     joerg 		man->meta.msec = mandoc_strdup("");
    323  1.1.1.16  christos 		mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse,
    324  1.1.1.16  christos 		    nb->line, nb->pos, "TH %s", man->meta.title);
    325  1.1.1.16  christos 	}
    326  1.1.1.11     joerg 
    327  1.1.1.17  christos 	/* TITLE MSEC ->DATE<- OS VOL */
    328  1.1.1.11     joerg 
    329  1.1.1.12     joerg 	if (n)
    330  1.1.1.12     joerg 		n = n->next;
    331  1.1.1.14     joerg 	if (n && n->string && '\0' != n->string[0]) {
    332  1.1.1.16  christos 		man->meta.date = man->quick ?
    333  1.1.1.16  christos 		    mandoc_strdup(n->string) :
    334  1.1.1.18  christos 		    mandoc_normdate(man, n->string, n->line, n->pos);
    335  1.1.1.16  christos 	} else {
    336  1.1.1.15     joerg 		man->meta.date = mandoc_strdup("");
    337  1.1.1.16  christos 		mandoc_msg(MANDOCERR_DATE_MISSING, man->parse,
    338  1.1.1.16  christos 		    n ? n->line : nb->line,
    339  1.1.1.16  christos 		    n ? n->pos : nb->pos, "TH");
    340  1.1.1.16  christos 	}
    341  1.1.1.11     joerg 
    342  1.1.1.17  christos 	/* TITLE MSEC DATE ->OS<- VOL */
    343  1.1.1.11     joerg 
    344  1.1.1.11     joerg 	if (n && (n = n->next))
    345  1.1.1.17  christos 		man->meta.os = mandoc_strdup(n->string);
    346  1.1.1.18  christos 	else if (man->os_s != NULL)
    347  1.1.1.18  christos 		man->meta.os = mandoc_strdup(man->os_s);
    348  1.1.1.18  christos 	if (man->meta.os_e == MANDOC_OS_OTHER && man->meta.os != NULL) {
    349  1.1.1.18  christos 		if (strstr(man->meta.os, "OpenBSD") != NULL)
    350  1.1.1.18  christos 			man->meta.os_e = MANDOC_OS_OPENBSD;
    351  1.1.1.18  christos 		else if (strstr(man->meta.os, "NetBSD") != NULL)
    352  1.1.1.18  christos 			man->meta.os_e = MANDOC_OS_NETBSD;
    353  1.1.1.18  christos 	}
    354  1.1.1.11     joerg 
    355  1.1.1.17  christos 	/* TITLE MSEC DATE OS ->VOL<- */
    356  1.1.1.14     joerg 	/* If missing, use the default VOL name for MSEC. */
    357  1.1.1.11     joerg 
    358  1.1.1.11     joerg 	if (n && (n = n->next))
    359  1.1.1.15     joerg 		man->meta.vol = mandoc_strdup(n->string);
    360  1.1.1.15     joerg 	else if ('\0' != man->meta.msec[0] &&
    361  1.1.1.15     joerg 	    (NULL != (p = mandoc_a2msec(man->meta.msec))))
    362  1.1.1.15     joerg 		man->meta.vol = mandoc_strdup(p);
    363  1.1.1.11     joerg 
    364  1.1.1.16  christos 	if (n != NULL && (n = n->next) != NULL)
    365  1.1.1.16  christos 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
    366  1.1.1.16  christos 		    n->line, n->pos, "TH ... %s", n->string);
    367  1.1.1.16  christos 
    368  1.1.1.11     joerg 	/*
    369  1.1.1.11     joerg 	 * Remove the `TH' node after we've processed it for our
    370  1.1.1.11     joerg 	 * meta-data.
    371  1.1.1.11     joerg 	 */
    372  1.1.1.17  christos 	roff_node_delete(man, man->last);
    373  1.1.1.11     joerg }
    374  1.1.1.11     joerg 
    375  1.1.1.16  christos static void
    376  1.1.1.11     joerg post_UC(CHKARGS)
    377  1.1.1.11     joerg {
    378  1.1.1.11     joerg 	static const char * const bsd_versions[] = {
    379  1.1.1.11     joerg 	    "3rd Berkeley Distribution",
    380  1.1.1.11     joerg 	    "4th Berkeley Distribution",
    381  1.1.1.11     joerg 	    "4.2 Berkeley Distribution",
    382  1.1.1.11     joerg 	    "4.3 Berkeley Distribution",
    383  1.1.1.11     joerg 	    "4.4 Berkeley Distribution",
    384  1.1.1.11     joerg 	};
    385  1.1.1.11     joerg 
    386  1.1.1.11     joerg 	const char	*p, *s;
    387  1.1.1.11     joerg 
    388  1.1.1.11     joerg 	n = n->child;
    389  1.1.1.11     joerg 
    390  1.1.1.17  christos 	if (n == NULL || n->type != ROFFT_TEXT)
    391  1.1.1.11     joerg 		p = bsd_versions[0];
    392  1.1.1.11     joerg 	else {
    393  1.1.1.11     joerg 		s = n->string;
    394  1.1.1.11     joerg 		if (0 == strcmp(s, "3"))
    395  1.1.1.11     joerg 			p = bsd_versions[0];
    396  1.1.1.11     joerg 		else if (0 == strcmp(s, "4"))
    397  1.1.1.11     joerg 			p = bsd_versions[1];
    398  1.1.1.11     joerg 		else if (0 == strcmp(s, "5"))
    399  1.1.1.11     joerg 			p = bsd_versions[2];
    400  1.1.1.11     joerg 		else if (0 == strcmp(s, "6"))
    401  1.1.1.11     joerg 			p = bsd_versions[3];
    402  1.1.1.11     joerg 		else if (0 == strcmp(s, "7"))
    403  1.1.1.11     joerg 			p = bsd_versions[4];
    404  1.1.1.11     joerg 		else
    405  1.1.1.11     joerg 			p = bsd_versions[0];
    406  1.1.1.11     joerg 	}
    407  1.1.1.11     joerg 
    408  1.1.1.17  christos 	free(man->meta.os);
    409  1.1.1.17  christos 	man->meta.os = mandoc_strdup(p);
    410  1.1.1.11     joerg }
    411  1.1.1.11     joerg 
    412  1.1.1.16  christos static void
    413  1.1.1.11     joerg post_AT(CHKARGS)
    414  1.1.1.11     joerg {
    415  1.1.1.11     joerg 	static const char * const unix_versions[] = {
    416  1.1.1.11     joerg 	    "7th Edition",
    417  1.1.1.11     joerg 	    "System III",
    418  1.1.1.11     joerg 	    "System V",
    419  1.1.1.11     joerg 	    "System V Release 2",
    420  1.1.1.11     joerg 	};
    421  1.1.1.11     joerg 
    422  1.1.1.17  christos 	struct roff_node *nn;
    423  1.1.1.11     joerg 	const char	*p, *s;
    424  1.1.1.11     joerg 
    425  1.1.1.11     joerg 	n = n->child;
    426  1.1.1.11     joerg 
    427  1.1.1.17  christos 	if (n == NULL || n->type != ROFFT_TEXT)
    428  1.1.1.11     joerg 		p = unix_versions[0];
    429  1.1.1.11     joerg 	else {
    430  1.1.1.11     joerg 		s = n->string;
    431  1.1.1.11     joerg 		if (0 == strcmp(s, "3"))
    432  1.1.1.11     joerg 			p = unix_versions[0];
    433  1.1.1.11     joerg 		else if (0 == strcmp(s, "4"))
    434  1.1.1.11     joerg 			p = unix_versions[1];
    435  1.1.1.11     joerg 		else if (0 == strcmp(s, "5")) {
    436  1.1.1.11     joerg 			nn = n->next;
    437  1.1.1.17  christos 			if (nn != NULL &&
    438  1.1.1.17  christos 			    nn->type == ROFFT_TEXT &&
    439  1.1.1.17  christos 			    nn->string[0] != '\0')
    440  1.1.1.11     joerg 				p = unix_versions[3];
    441  1.1.1.11     joerg 			else
    442  1.1.1.11     joerg 				p = unix_versions[2];
    443  1.1.1.11     joerg 		} else
    444  1.1.1.11     joerg 			p = unix_versions[0];
    445  1.1.1.11     joerg 	}
    446  1.1.1.11     joerg 
    447  1.1.1.17  christos 	free(man->meta.os);
    448  1.1.1.17  christos 	man->meta.os = mandoc_strdup(p);
    449  1.1.1.11     joerg }
    450  1.1.1.13     joerg 
    451  1.1.1.16  christos static void
    452  1.1.1.18  christos post_in(CHKARGS)
    453  1.1.1.18  christos {
    454  1.1.1.18  christos 	char	*s;
    455  1.1.1.18  christos 
    456  1.1.1.18  christos 	if (n->parent->tok != MAN_TP ||
    457  1.1.1.18  christos 	    n->parent->type != ROFFT_HEAD ||
    458  1.1.1.18  christos 	    n->child == NULL ||
    459  1.1.1.18  christos 	    *n->child->string == '+' ||
    460  1.1.1.18  christos 	    *n->child->string == '-')
    461  1.1.1.18  christos 		return;
    462  1.1.1.18  christos 	mandoc_asprintf(&s, "+%s", n->child->string);
    463  1.1.1.18  christos 	free(n->child->string);
    464  1.1.1.18  christos 	n->child->string = s;
    465  1.1.1.18  christos }
    466  1.1.1.18  christos 
    467  1.1.1.18  christos static void
    468  1.1.1.13     joerg post_vs(CHKARGS)
    469  1.1.1.13     joerg {
    470  1.1.1.13     joerg 
    471  1.1.1.15     joerg 	if (NULL != n->prev)
    472  1.1.1.16  christos 		return;
    473  1.1.1.15     joerg 
    474  1.1.1.15     joerg 	switch (n->parent->tok) {
    475  1.1.1.16  christos 	case MAN_SH:
    476  1.1.1.16  christos 	case MAN_SS:
    477  1.1.1.18  christos 	case MAN_PP:
    478  1.1.1.18  christos 	case MAN_LP:
    479  1.1.1.18  christos 	case MAN_P:
    480  1.1.1.16  christos 		mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos,
    481  1.1.1.18  christos 		    "%s after %s", roff_name[n->tok],
    482  1.1.1.18  christos 		    roff_name[n->parent->tok]);
    483  1.1.1.15     joerg 		/* FALLTHROUGH */
    484  1.1.1.17  christos 	case TOKEN_NONE:
    485  1.1.1.16  christos 		/*
    486  1.1.1.15     joerg 		 * Don't warn about this because it occurs in pod2man
    487  1.1.1.15     joerg 		 * and would cause considerable (unfixable) warnage.
    488  1.1.1.15     joerg 		 */
    489  1.1.1.17  christos 		roff_node_delete(man, n);
    490  1.1.1.15     joerg 		break;
    491  1.1.1.15     joerg 	default:
    492  1.1.1.15     joerg 		break;
    493  1.1.1.15     joerg 	}
    494  1.1.1.13     joerg }
    495