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