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