Return to man_validate.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.20, 2009/08/19 12:15:58 | version 1.43, 2010/06/09 19:22:56 | ||
---|---|---|---|
|
|
||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | */ | ||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include <sys/types.h> | #include <sys/types.h> | ||
#include <assert.h> | #include <assert.h> | ||
|
|
||
#include <stdarg.h> | #include <stdarg.h> | ||
#include <stdlib.h> | #include <stdlib.h> | ||
#include "mandoc.h" | |||
#include "libman.h" | #include "libman.h" | ||
#include "libmandoc.h" | #include "libmandoc.h" | ||
#define CHKARGS struct man *m, const struct man_node *n | #define CHKARGS struct man *m, struct man_node *n | ||
typedef int (*v_check)(CHKARGS); | typedef int (*v_check)(CHKARGS); | ||
|
|
||
}; | }; | ||
static int check_bline(CHKARGS); | static int check_bline(CHKARGS); | ||
static int check_eline(CHKARGS); | |||
static int check_eq0(CHKARGS); | static int check_eq0(CHKARGS); | ||
static int check_eq1(CHKARGS); | static int check_le1(CHKARGS); | ||
static int check_ge2(CHKARGS); | static int check_ge2(CHKARGS); | ||
static int check_le5(CHKARGS); | static int check_le5(CHKARGS); | ||
static int check_par(CHKARGS); | static int check_par(CHKARGS); | ||
static int check_part(CHKARGS); | |||
static int check_root(CHKARGS); | static int check_root(CHKARGS); | ||
static int check_sec(CHKARGS); | static int check_sec(CHKARGS); | ||
static int check_sp(CHKARGS); | |||
static int check_text(CHKARGS); | static int check_text(CHKARGS); | ||
static int check_title(CHKARGS); | |||
static v_check posts_eq0[] = { check_eq0, NULL }; | static v_check posts_eq0[] = { check_eq0, NULL }; | ||
static v_check posts_ge2_le5[] = { check_ge2, check_le5, NULL }; | static v_check posts_th[] = { check_ge2, check_le5, check_title, NULL }; | ||
static v_check posts_par[] = { check_par, NULL }; | static v_check posts_par[] = { check_par, NULL }; | ||
static v_check posts_part[] = { check_part, NULL }; | |||
static v_check posts_sec[] = { check_sec, NULL }; | static v_check posts_sec[] = { check_sec, NULL }; | ||
static v_check posts_sp[] = { check_sp, NULL }; | static v_check posts_le1[] = { check_le1, NULL }; | ||
static v_check pres_eline[] = { check_eline, NULL }; | |||
static v_check pres_bline[] = { check_bline, NULL }; | static v_check pres_bline[] = { check_bline, NULL }; | ||
static const struct man_valid man_valids[MAN_MAX] = { | static const struct man_valid man_valids[MAN_MAX] = { | ||
{ pres_bline, posts_eq0 }, /* br */ | { NULL, posts_eq0 }, /* br */ | ||
{ pres_bline, posts_ge2_le5 }, /* TH */ | { pres_bline, posts_th }, /* TH */ | ||
{ pres_bline, posts_sec }, /* SH */ | { pres_bline, posts_sec }, /* SH */ | ||
{ pres_bline, posts_sec }, /* SS */ | { pres_bline, posts_sec }, /* SS */ | ||
{ pres_bline, posts_par }, /* TP */ | { pres_bline, posts_par }, /* TP */ | ||
|
|
||
{ pres_bline, posts_par }, /* P */ | { pres_bline, posts_par }, /* P */ | ||
{ pres_bline, posts_par }, /* IP */ | { pres_bline, posts_par }, /* IP */ | ||
{ pres_bline, posts_par }, /* HP */ | { pres_bline, posts_par }, /* HP */ | ||
{ pres_eline, NULL }, /* SM */ | { NULL, NULL }, /* SM */ | ||
{ pres_eline, NULL }, /* SB */ | { NULL, NULL }, /* SB */ | ||
{ NULL, NULL }, /* BI */ | { NULL, NULL }, /* BI */ | ||
{ NULL, NULL }, /* IB */ | { NULL, NULL }, /* IB */ | ||
{ NULL, NULL }, /* BR */ | { NULL, NULL }, /* BR */ | ||
{ NULL, NULL }, /* RB */ | { NULL, NULL }, /* RB */ | ||
{ pres_eline, NULL }, /* R */ | { NULL, NULL }, /* R */ | ||
{ pres_eline, NULL }, /* B */ | { NULL, NULL }, /* B */ | ||
{ pres_eline, NULL }, /* I */ | { NULL, NULL }, /* I */ | ||
{ NULL, NULL }, /* IR */ | { NULL, NULL }, /* IR */ | ||
{ NULL, NULL }, /* RI */ | { NULL, NULL }, /* RI */ | ||
{ pres_bline, posts_eq0 }, /* na */ | { NULL, posts_eq0 }, /* na */ | ||
{ NULL, NULL }, /* i */ | { NULL, NULL }, /* i */ | ||
{ pres_bline, posts_sp }, /* sp */ | { NULL, posts_le1 }, /* sp */ | ||
{ pres_bline, posts_eq0 }, /* nf */ | { pres_bline, posts_eq0 }, /* nf */ | ||
{ pres_bline, posts_eq0 }, /* fi */ | { pres_bline, posts_eq0 }, /* fi */ | ||
{ NULL, NULL }, /* r */ | { NULL, NULL }, /* r */ | ||
{ NULL, NULL }, /* RE */ | { NULL, NULL }, /* RE */ | ||
{ NULL, NULL }, /* RS */ | { NULL, posts_part }, /* RS */ | ||
{ NULL, NULL }, /* DT */ | |||
{ NULL, NULL }, /* UC */ | |||
{ NULL, NULL }, /* PD */ | |||
{ NULL, posts_le1 }, /* Sp */ | |||
{ pres_bline, posts_le1 }, /* Vb */ | |||
{ pres_bline, posts_eq0 }, /* Ve */ | |||
{ NULL, NULL }, /* AT */ | |||
}; | }; | ||
int | int | ||
man_valid_pre(struct man *m, const struct man_node *n) | man_valid_pre(struct man *m, struct man_node *n) | ||
{ | { | ||
v_check *cp; | v_check *cp; | ||
|
|
||
check_root(CHKARGS) | check_root(CHKARGS) | ||
{ | { | ||
/* XXX - make this into a warning? */ | |||
if (MAN_BLINE & m->flags) | if (MAN_BLINE & m->flags) | ||
return(man_nerr(m, n, WEXITSCOPE)); | return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT)); | ||
/* XXX - make this into a warning? */ | |||
if (MAN_ELINE & m->flags) | if (MAN_ELINE & m->flags) | ||
return(man_nerr(m, n, WEXITSCOPE)); | return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT)); | ||
if (NULL == m->first->child) | m->flags &= ~MAN_BLINE; | ||
return(man_nerr(m, n, WNODATA)); | m->flags &= ~MAN_ELINE; | ||
if (NULL == m->meta.title) | |||
return(man_nerr(m, n, WNOTITLE)); | |||
if (NULL == m->first->child) { | |||
man_nmsg(m, n, MANDOCERR_NODOCBODY); | |||
return(0); | |||
} else if (NULL == m->meta.title) { | |||
if ( ! man_nmsg(m, n, MANDOCERR_NOTITLE)) | |||
return(0); | |||
/* | |||
* If a title hasn't been set, do so now (by | |||
* implication, date and section also aren't set). | |||
* | |||
* FIXME: this should be in man_action.c. | |||
*/ | |||
m->meta.title = mandoc_strdup("unknown"); | |||
m->meta.date = time(NULL); | |||
m->meta.msec = mandoc_strdup("1"); | |||
} | |||
return(1); | return(1); | ||
} | } | ||
static int | static int | ||
check_text(CHKARGS) | check_title(CHKARGS) | ||
{ | { | ||
const char *p; | const char *p; | ||
assert(n->child); | |||
/* FIXME: is this sufficient? */ | |||
if ('\0' == *n->child->string) { | |||
man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT); | |||
return(0); | |||
} | |||
for (p = n->child->string; '\0' != *p; p++) | |||
if (isalpha((u_char)*p) && ! isupper((u_char)*p)) | |||
if ( ! man_nmsg(m, n, MANDOCERR_UPPERCASE)) | |||
return(0); | |||
return(1); | |||
} | |||
static int | |||
check_text(CHKARGS) | |||
{ | |||
char *p; | |||
int pos, c; | int pos, c; | ||
assert(n->string); | assert(n->string); | ||
|
|
||
pos += c - 1; | pos += c - 1; | ||
continue; | continue; | ||
} | } | ||
if ( ! (MAN_IGN_ESCAPE & m->pflags)) | |||
return(man_perr(m, n->line, pos, WESCAPE)); | c = man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE); | ||
if ( ! man_pwarn(m, n->line, pos, WESCAPE)) | if ( ! (MAN_IGN_ESCAPE & m->pflags) && ! c) | ||
return(0); | return(c); | ||
continue; | |||
} | } | ||
if ('\t' == *p || isprint((u_char)*p)) | if ('\t' == *p || isprint((u_char)*p) || ASCII_HYPH == *p) | ||
continue; | continue; | ||
if ( ! man_pmsg(m, n->line, pos, MANDOCERR_BADCHAR)) | |||
if (MAN_IGN_CHARS & m->pflags) | return(0); | ||
return(man_pwarn(m, n->line, pos, WNPRINT)); | |||
return(man_perr(m, n->line, pos, WNPRINT)); | |||
} | } | ||
return(1); | return(1); | ||
|
|
||
{ \ | { \ | ||
if (n->nchild ineq (x)) \ | if (n->nchild ineq (x)) \ | ||
return(1); \ | return(1); \ | ||
return(man_verr(m, n->line, n->pos, \ | man_vmsg(m, MANDOCERR_SYNTARGCOUNT, n->line, n->pos, \ | ||
"expected line arguments %s %d, have %d", \ | "line arguments %s %d (have %d)", \ | ||
#ineq, (x), n->nchild)); \ | #ineq, (x), n->nchild); \ | ||
return(0); \ | |||
} | } | ||
INEQ_DEFINE(0, ==, eq0) | INEQ_DEFINE(0, ==, eq0) | ||
INEQ_DEFINE(1, ==, eq1) | INEQ_DEFINE(1, <=, le1) | ||
INEQ_DEFINE(2, >=, ge2) | INEQ_DEFINE(2, >=, ge2) | ||
INEQ_DEFINE(5, <=, le5) | INEQ_DEFINE(5, <=, le5) | ||
static int | static int | ||
check_sp(CHKARGS) | check_sec(CHKARGS) | ||
{ | { | ||
long lval; | |||
char *ep, *buf; | |||
if (NULL == n->child) | if (MAN_HEAD == n->type && 0 == n->nchild) { | ||
return(1); | man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT); | ||
else if ( ! check_eq1(m, n)) | |||
return(0); | return(0); | ||
} else if (MAN_BODY == n->type && 0 == n->nchild) | |||
return(man_nmsg(m, n, MANDOCERR_NOBODY)); | |||
assert(MAN_TEXT == n->child->type); | |||
buf = n->child->string; | |||
assert(buf); | |||
/* From OpenBSD's strtol(3). */ | |||
errno = 0; | |||
lval = strtol(buf, &ep, 10); | |||
if (buf[0] == '\0' || *ep != '\0') | |||
return(man_nerr(m, n->child, WNUMFMT)); | |||
if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) || | |||
(lval > INT_MAX || lval < 0)) | |||
return(man_nerr(m, n->child, WNUMFMT)); | |||
return(1); | return(1); | ||
} | } | ||
static int | static int | ||
check_sec(CHKARGS) | check_part(CHKARGS) | ||
{ | { | ||
if (MAN_BODY == n->type && 0 == n->nchild) | if (MAN_BODY == n->type && 0 == n->nchild) | ||
return(man_nwarn(m, n, WBODYARGS)); | return(man_nmsg(m, n, MANDOCERR_NOBODY)); | ||
if (MAN_HEAD == n->type && 0 == n->nchild) | |||
return(man_nerr(m, n, WHEADARGS)); | |||
return(1); | return(1); | ||
} | } | ||
|
|
||
default: | default: | ||
if (n->nchild) | if (n->nchild) | ||
break; | break; | ||
return(man_nwarn(m, n, WBODYARGS)); | return(man_nmsg(m, n, MANDOCERR_NOBODY)); | ||
} | } | ||
if (MAN_HEAD == n->type) | if (MAN_HEAD == n->type) | ||
switch (n->tok) { | switch (n->tok) { | ||
|
|
||
case (MAN_LP): | case (MAN_LP): | ||
if (0 == n->nchild) | if (0 == n->nchild) | ||
break; | break; | ||
return(man_nwarn(m, n, WNHEADARGS)); | return(man_nmsg(m, n, MANDOCERR_ARGSLOST)); | ||
default: | default: | ||
if (n->nchild) | if (n->nchild) | ||
break; | break; | ||
return(man_nwarn(m, n, WHEADARGS)); | return(man_nmsg(m, n, MANDOCERR_NOARGS)); | ||
} | } | ||
return(1); | return(1); | ||
|
|
||
static int | static int | ||
check_eline(CHKARGS) | |||
{ | |||
if (MAN_ELINE & m->flags) | |||
return(man_nerr(m, n, WLNSCOPE)); | |||
return(1); | |||
} | |||
static int | |||
check_bline(CHKARGS) | check_bline(CHKARGS) | ||
{ | { | ||
if (MAN_BLINE & m->flags) | assert( ! (MAN_ELINE & m->flags)); | ||
return(man_nerr(m, n, WLNSCOPE)); | if (MAN_BLINE & m->flags) { | ||
if (MAN_ELINE & m->flags) | man_nmsg(m, n, MANDOCERR_SYNTLINESCOPE); | ||
return(man_nerr(m, n, WLNSCOPE)); | return(0); | ||
} | |||
return(1); | return(1); | ||
} | } | ||