version 1.1, 2009/03/25 15:17:49 |
version 1.5, 2009/04/12 19:45:26 |
|
|
/* $Id$ */ |
/* $Id$ */ |
/* |
/* |
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org> |
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the |
* purpose with or without fee is hereby granted, provided that the above |
* above copyright notice and this permission notice appear in all |
* copyright notice and this permission notice appear in all copies. |
* copies. |
|
* |
* |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* PERFORMANCE OF THIS SOFTWARE. |
|
*/ |
*/ |
#include <sys/types.h> |
#include <sys/types.h> |
|
|
|
|
#include <ctype.h> |
#include <ctype.h> |
#include <stdarg.h> |
#include <stdarg.h> |
#include <stdlib.h> |
#include <stdlib.h> |
|
#include <string.h> |
|
|
#include "libmdoc.h" |
#include "libmdoc.h" |
|
|
/* FIXME: .Bl -diag can't have non-text children in HEAD. */ |
/* FIXME: .Bl -diag can't have non-text children in HEAD. */ |
/* TODO: ignoring Pp (it's superfluous in some invocations). */ |
/* TODO: ignoring Pp (it's superfluous in some invocations). */ |
|
|
/* |
|
* Pre- and post-validate macros as they're parsed. Pre-validation |
|
* occurs when the macro has been detected and its arguments parsed. |
|
* Post-validation occurs when all child macros have also been parsed. |
|
* In the ELEMENT case, this is simply the parameters of the macro; in |
|
* the BLOCK case, this is the HEAD, BODY, TAIL and so on. |
|
*/ |
|
|
|
#define PRE_ARGS struct mdoc *mdoc, const struct mdoc_node *n |
#define PRE_ARGS struct mdoc *mdoc, const struct mdoc_node *n |
#define POST_ARGS struct mdoc *mdoc |
#define POST_ARGS struct mdoc *mdoc |
|
|
enum merr { |
enum merr { |
|
ETOOLONG, |
EESCAPE, |
EESCAPE, |
EPRINT, |
EPRINT, |
ENODATA, |
ENODATA, |
|
|
}; |
}; |
|
|
enum mwarn { |
enum mwarn { |
|
WPRINT, |
WESCAPE, |
WESCAPE, |
WWRONGMSEC, |
WWRONGMSEC, |
WSECOOO, |
WSECOOO, |
|
|
v_post *post; |
v_post *post; |
}; |
}; |
|
|
/* Utility checks. */ |
|
|
|
static int pwarn(struct mdoc *, int, int, enum mwarn); |
static int pwarn(struct mdoc *, int, int, enum mwarn); |
static int perr(struct mdoc *, int, int, enum merr); |
static int perr(struct mdoc *, int, int, enum merr); |
static int check_parent(PRE_ARGS, int, enum mdoc_type); |
static int check_parent(PRE_ARGS, int, enum mdoc_type); |
Line 102 static int err_child_gt(struct mdoc *, const char *, i |
|
Line 93 static int err_child_gt(struct mdoc *, const char *, i |
|
static int warn_child_gt(struct mdoc *, const char *, int); |
static int warn_child_gt(struct mdoc *, const char *, int); |
static int err_child_eq(struct mdoc *, const char *, int); |
static int err_child_eq(struct mdoc *, const char *, int); |
static int warn_child_eq(struct mdoc *, const char *, int); |
static int warn_child_eq(struct mdoc *, const char *, int); |
static inline int count_child(struct mdoc *); |
static int count_child(struct mdoc *); |
static inline int warn_count(struct mdoc *, const char *, |
static int warn_print(struct mdoc *, int, int); |
|
static int warn_count(struct mdoc *, const char *, |
int, const char *, int); |
int, const char *, int); |
static inline int err_count(struct mdoc *, const char *, |
static int err_count(struct mdoc *, const char *, |
int, const char *, int); |
int, const char *, int); |
static int pre_an(PRE_ARGS); |
static int pre_an(PRE_ARGS); |
static int pre_bd(PRE_ARGS); |
static int pre_bd(PRE_ARGS); |
Line 137 static int bwarn_ge1(POST_ARGS); |
|
Line 129 static int bwarn_ge1(POST_ARGS); |
|
static int hwarn_eq1(POST_ARGS); |
static int hwarn_eq1(POST_ARGS); |
static int ewarn_ge1(POST_ARGS); |
static int ewarn_ge1(POST_ARGS); |
static int ebool(POST_ARGS); |
static int ebool(POST_ARGS); |
|
|
static int post_an(POST_ARGS); |
static int post_an(POST_ARGS); |
static int post_args(POST_ARGS); |
static int post_args(POST_ARGS); |
static int post_at(POST_ARGS); |
static int post_at(POST_ARGS); |
Line 151 static int post_sh_body(POST_ARGS); |
|
Line 142 static int post_sh_body(POST_ARGS); |
|
static int post_sh_head(POST_ARGS); |
static int post_sh_head(POST_ARGS); |
static int post_st(POST_ARGS); |
static int post_st(POST_ARGS); |
|
|
#define mwarn(m, t) nwarn((m), (m)->last, (t)) |
#define vwarn(m, t) nwarn((m), (m)->last, (t)) |
#define merr(m, t) nerr((m), (m)->last, (t)) |
#define verr(m, t) nerr((m), (m)->last, (t)) |
#define nwarn(m, n, t) pwarn((m), (n)->line, (n)->pos, (t)) |
#define nwarn(m, n, t) pwarn((m), (n)->line, (n)->pos, (t)) |
#define nerr(m, n, t) perr((m), (n)->line, (n)->pos, (t)) |
#define nerr(m, n, t) perr((m), (n)->line, (n)->pos, (t)) |
|
|
Line 319 const struct valids mdoc_valids[MDOC_MAX] = { |
|
Line 310 const struct valids mdoc_valids[MDOC_MAX] = { |
|
}; |
}; |
|
|
|
|
|
#ifdef __linux__ |
|
extern size_t strlcat(char *, const char *, size_t); |
|
#endif |
|
|
|
|
int |
int |
mdoc_valid_pre(struct mdoc *mdoc, |
mdoc_valid_pre(struct mdoc *mdoc, |
const struct mdoc_node *n) |
const struct mdoc_node *n) |
Line 384 perr(struct mdoc *m, int line, int pos, enum merr type |
|
Line 380 perr(struct mdoc *m, int line, int pos, enum merr type |
|
|
|
p = NULL; |
p = NULL; |
switch (type) { |
switch (type) { |
|
case (ETOOLONG): |
|
p = "text argument too long"; |
|
break; |
case (EESCAPE): |
case (EESCAPE): |
p = "invalid escape sequence"; |
p = "invalid escape sequence"; |
break; |
break; |
Line 461 pwarn(struct mdoc *m, int line, int pos, enum mwarn ty |
|
Line 460 pwarn(struct mdoc *m, int line, int pos, enum mwarn ty |
|
p = "prologue macros out-of-order"; |
p = "prologue macros out-of-order"; |
c = WARN_COMPAT; |
c = WARN_COMPAT; |
break; |
break; |
|
case (WPRINT): |
|
p = "invalid character"; |
|
break; |
case (WESCAPE): |
case (WESCAPE): |
p = "invalid escape sequence"; |
p = "invalid escape sequence"; |
break; |
break; |
Line 498 pwarn(struct mdoc *m, int line, int pos, enum mwarn ty |
|
Line 500 pwarn(struct mdoc *m, int line, int pos, enum mwarn ty |
|
} |
} |
|
|
|
|
|
static int |
|
warn_print(struct mdoc *m, int ln, int pos) |
|
{ |
|
if (MDOC_IGN_CHARS & m->pflags) |
|
return(pwarn(m, ln, pos, WPRINT)); |
|
return(perr(m, ln, pos, EPRINT)); |
|
} |
|
|
|
|
static inline int |
static inline int |
warn_count(struct mdoc *m, const char *k, |
warn_count(struct mdoc *m, const char *k, |
int want, const char *v, int has) |
int want, const char *v, int has) |
Line 700 check_text(struct mdoc *mdoc, int line, int pos, const |
|
Line 710 check_text(struct mdoc *mdoc, int line, int pos, const |
|
for ( ; *p; p++) { |
for ( ; *p; p++) { |
if ('\t' == *p) { |
if ('\t' == *p) { |
if ( ! (MDOC_LITERAL & mdoc->flags)) |
if ( ! (MDOC_LITERAL & mdoc->flags)) |
return(perr(mdoc, line, pos, EPRINT)); |
if ( ! warn_print(mdoc, line, pos)) |
|
return(0); |
} else if ( ! isprint((u_char)*p)) |
} else if ( ! isprint((u_char)*p)) |
return(perr(mdoc, line, pos, EPRINT)); |
if ( ! warn_print(mdoc, line, pos)) |
|
return(0); |
|
|
if ('\\' != *p) |
if ('\\' != *p) |
continue; |
continue; |
Line 1039 post_bf(POST_ARGS) |
|
Line 1051 post_bf(POST_ARGS) |
|
return(mdoc_err(mdoc, "text argument expected")); |
return(mdoc_err(mdoc, "text argument expected")); |
|
|
p = head->child->string; |
p = head->child->string; |
if (xstrcmp(p, "Em")) |
if (0 == strcmp(p, "Em")) |
return(1); |
return(1); |
else if (xstrcmp(p, "Li")) |
else if (0 == strcmp(p, "Li")) |
return(1); |
return(1); |
else if (xstrcmp(p, "Sm")) |
else if (0 == strcmp(p, "Sm")) |
return(1); |
return(1); |
return(mdoc_nerr(mdoc, head->child, "invalid font")); |
return(mdoc_nerr(mdoc, head->child, "invalid font")); |
} |
} |
Line 1063 post_nm(POST_ARGS) |
|
Line 1075 post_nm(POST_ARGS) |
|
return(1); |
return(1); |
if (mdoc->meta.name) |
if (mdoc->meta.name) |
return(1); |
return(1); |
return(merr(mdoc, ENAME)); |
return(verr(mdoc, ENAME)); |
} |
} |
|
|
|
|
Line 1074 post_at(POST_ARGS) |
|
Line 1086 post_at(POST_ARGS) |
|
if (NULL == mdoc->last->child) |
if (NULL == mdoc->last->child) |
return(1); |
return(1); |
if (MDOC_TEXT != mdoc->last->child->type) |
if (MDOC_TEXT != mdoc->last->child->type) |
return(merr(mdoc, EATT)); |
return(verr(mdoc, EATT)); |
if (mdoc_a2att(mdoc->last->child->string)) |
if (mdoc_a2att(mdoc->last->child->string)) |
return(1); |
return(1); |
return(merr(mdoc, EATT)); |
return(verr(mdoc, EATT)); |
} |
} |
|
|
|
|
Line 1088 post_an(POST_ARGS) |
|
Line 1100 post_an(POST_ARGS) |
|
if (mdoc->last->args) { |
if (mdoc->last->args) { |
if (NULL == mdoc->last->child) |
if (NULL == mdoc->last->child) |
return(1); |
return(1); |
return(merr(mdoc, ELINE)); |
return(verr(mdoc, ELINE)); |
} |
} |
|
|
if (mdoc->last->child) |
if (mdoc->last->child) |
return(1); |
return(1); |
return(merr(mdoc, ELINE)); |
return(verr(mdoc, ELINE)); |
} |
} |
|
|
|
|
Line 1103 post_args(POST_ARGS) |
|
Line 1115 post_args(POST_ARGS) |
|
|
|
if (mdoc->last->args) |
if (mdoc->last->args) |
return(1); |
return(1); |
return(merr(mdoc, ELINE)); |
return(verr(mdoc, ELINE)); |
} |
} |
|
|
|
|
Line 1118 post_it(POST_ARGS) |
|
Line 1130 post_it(POST_ARGS) |
|
|
|
n = mdoc->last->parent->parent; |
n = mdoc->last->parent->parent; |
if (NULL == n->args) |
if (NULL == n->args) |
return(merr(mdoc, ELISTTYPE)); |
return(verr(mdoc, ELISTTYPE)); |
|
|
/* Some types require block-head, some not. */ |
/* Some types require block-head, some not. */ |
|
|
Line 1156 post_it(POST_ARGS) |
|
Line 1168 post_it(POST_ARGS) |
|
} |
} |
|
|
if (-1 == type) |
if (-1 == type) |
return(merr(mdoc, ELISTTYPE)); |
return(verr(mdoc, ELISTTYPE)); |
|
|
switch (type) { |
switch (type) { |
case (MDOC_Tag): |
case (MDOC_Tag): |
if (NULL == mdoc->last->head->child) |
if (NULL == mdoc->last->head->child) |
if ( ! mwarn(mdoc, WLINE)) |
if ( ! vwarn(mdoc, WLINE)) |
return(0); |
return(0); |
break; |
break; |
case (MDOC_Hang): |
case (MDOC_Hang): |
Line 1172 post_it(POST_ARGS) |
|
Line 1184 post_it(POST_ARGS) |
|
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
case (MDOC_Diag): |
case (MDOC_Diag): |
if (NULL == mdoc->last->head->child) |
if (NULL == mdoc->last->head->child) |
if ( ! mwarn(mdoc, WLINE)) |
if ( ! vwarn(mdoc, WLINE)) |
return(0); |
return(0); |
if (NULL == mdoc->last->body->child) |
if (NULL == mdoc->last->body->child) |
if ( ! mwarn(mdoc, WMULTILINE)) |
if ( ! vwarn(mdoc, WMULTILINE)) |
return(0); |
return(0); |
break; |
break; |
case (MDOC_Bullet): |
case (MDOC_Bullet): |
Line 1188 post_it(POST_ARGS) |
|
Line 1200 post_it(POST_ARGS) |
|
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
case (MDOC_Item): |
case (MDOC_Item): |
if (mdoc->last->head->child) |
if (mdoc->last->head->child) |
if ( ! mwarn(mdoc, WNOLINE)) |
if ( ! vwarn(mdoc, WNOLINE)) |
return(0); |
return(0); |
if (NULL == mdoc->last->body->child) |
if (NULL == mdoc->last->body->child) |
if ( ! mwarn(mdoc, WMULTILINE)) |
if ( ! vwarn(mdoc, WMULTILINE)) |
return(0); |
return(0); |
break; |
break; |
case (MDOC_Column): |
case (MDOC_Column): |
if (NULL == mdoc->last->head->child) |
if (NULL == mdoc->last->head->child) |
if ( ! mwarn(mdoc, WLINE)) |
if ( ! vwarn(mdoc, WLINE)) |
return(0); |
return(0); |
if (mdoc->last->body->child) |
if (mdoc->last->body->child) |
if ( ! mwarn(mdoc, WNOMULTILINE)) |
if ( ! vwarn(mdoc, WNOMULTILINE)) |
return(0); |
return(0); |
c = mdoc->last->child; |
c = mdoc->last->child; |
for (i = 0; c && MDOC_HEAD == c->type; c = c->next) |
for (i = 0; c && MDOC_HEAD == c->type; c = c->next) |
Line 1248 ebool(struct mdoc *mdoc) |
|
Line 1260 ebool(struct mdoc *mdoc) |
|
for (n = mdoc->last->child; n; n = n->next) { |
for (n = mdoc->last->child; n; n = n->next) { |
if (MDOC_TEXT != n->type) |
if (MDOC_TEXT != n->type) |
break; |
break; |
if (xstrcmp(n->string, "on")) |
if (0 == strcmp(n->string, "on")) |
continue; |
continue; |
if (xstrcmp(n->string, "off")) |
if (0 == strcmp(n->string, "off")) |
continue; |
continue; |
break; |
break; |
} |
} |
Line 1266 post_root(POST_ARGS) |
|
Line 1278 post_root(POST_ARGS) |
|
{ |
{ |
|
|
if (NULL == mdoc->first->child) |
if (NULL == mdoc->first->child) |
return(merr(mdoc, ENODATA)); |
return(verr(mdoc, ENODATA)); |
if (SEC_PROLOGUE == mdoc->lastnamed) |
if (SEC_PROLOGUE == mdoc->lastnamed) |
return(merr(mdoc, ENOPROLOGUE)); |
return(verr(mdoc, ENOPROLOGUE)); |
|
|
if (MDOC_BLOCK != mdoc->first->child->type) |
if (MDOC_BLOCK != mdoc->first->child->type) |
return(merr(mdoc, ENODATA)); |
return(verr(mdoc, ENODATA)); |
if (MDOC_Sh != mdoc->first->child->tok) |
if (MDOC_Sh != mdoc->first->child->tok) |
return(merr(mdoc, ENODATA)); |
return(verr(mdoc, ENODATA)); |
|
|
return(1); |
return(1); |
} |
} |
Line 1285 post_st(POST_ARGS) |
|
Line 1297 post_st(POST_ARGS) |
|
|
|
if (mdoc_a2st(mdoc->last->child->string)) |
if (mdoc_a2st(mdoc->last->child->string)) |
return(1); |
return(1); |
return(mwarn(mdoc, WBADSTAND)); |
return(vwarn(mdoc, WBADSTAND)); |
} |
} |
|
|
|
|
Line 1317 post_sh_body(POST_ARGS) |
|
Line 1329 post_sh_body(POST_ARGS) |
|
*/ |
*/ |
|
|
if (NULL == (n = mdoc->last->child)) |
if (NULL == (n = mdoc->last->child)) |
return(mwarn(mdoc, WNAMESECINC)); |
return(vwarn(mdoc, WNAMESECINC)); |
|
|
for ( ; n && n->next; n = n->next) { |
for ( ; n && n->next; n = n->next) { |
if (MDOC_ELEM == n->type && MDOC_Nm == n->tok) |
if (MDOC_ELEM == n->type && MDOC_Nm == n->tok) |
continue; |
continue; |
if (MDOC_TEXT == n->type) |
if (MDOC_TEXT == n->type) |
continue; |
continue; |
if ( ! mwarn(mdoc, WNAMESECINC)) |
if ( ! vwarn(mdoc, WNAMESECINC)) |
return(0); |
return(0); |
} |
} |
|
|
if (MDOC_ELEM == n->type && MDOC_Nd == n->tok) |
if (MDOC_ELEM == n->type && MDOC_Nd == n->tok) |
return(1); |
return(1); |
return(mwarn(mdoc, WNAMESECINC)); |
return(vwarn(mdoc, WNAMESECINC)); |
} |
} |
|
|
|
|
static int |
static int |
post_sh_head(POST_ARGS) |
post_sh_head(POST_ARGS) |
{ |
{ |
char buf[64]; |
char buf[64]; |
enum mdoc_sec sec; |
enum mdoc_sec sec; |
|
const struct mdoc_node *n; |
|
|
/* |
/* |
* Process a new section. Sections are either "named" or |
* Process a new section. Sections are either "named" or |
Line 1349 post_sh_head(POST_ARGS) |
|
Line 1362 post_sh_head(POST_ARGS) |
|
|
|
assert(MDOC_Sh == mdoc->last->tok); |
assert(MDOC_Sh == mdoc->last->tok); |
|
|
(void)xstrlcpys(buf, mdoc->last->child, sizeof(buf)); |
/* This is just concat() inlined, which is irritating. */ |
|
|
|
buf[0] = 0; |
|
for (n = mdoc->last->child; n; n = n->next) { |
|
assert(MDOC_TEXT == n->type); |
|
if (strlcat(buf, n->string, 64) >= 64) |
|
return(nerr(mdoc, n, ETOOLONG)); |
|
if (NULL == n->next) |
|
continue; |
|
if (strlcat(buf, " ", 64) >= 64) |
|
return(nerr(mdoc, n, ETOOLONG)); |
|
} |
|
|
sec = mdoc_atosec(buf); |
sec = mdoc_atosec(buf); |
|
|
/* The NAME section should always be first. */ |
/* The NAME section should always be first. */ |
|
|
if (SEC_BODY == mdoc->lastnamed && SEC_NAME != sec) |
if (SEC_BODY == mdoc->lastnamed && SEC_NAME != sec) |
return(mwarn(mdoc, WSECOOO)); |
return(vwarn(mdoc, WSECOOO)); |
if (SEC_CUSTOM == sec) |
if (SEC_CUSTOM == sec) |
return(1); |
return(1); |
|
|
/* Check for repeated or out-of-order sections. */ |
/* Check for repeated or out-of-order sections. */ |
|
|
if (sec == mdoc->lastnamed) |
if (sec == mdoc->lastnamed) |
return(mwarn(mdoc, WSECREP)); |
return(vwarn(mdoc, WSECREP)); |
if (sec < mdoc->lastnamed) |
if (sec < mdoc->lastnamed) |
return(mwarn(mdoc, WSECOOO)); |
return(vwarn(mdoc, WSECOOO)); |
|
|
/* Check particular section/manual section conventions. */ |
/* Check particular section/manual section conventions. */ |
|
|
Line 1377 post_sh_head(POST_ARGS) |
|
Line 1401 post_sh_head(POST_ARGS) |
|
case (3): |
case (3): |
break; |
break; |
default: |
default: |
return(mwarn(mdoc, WWRONGMSEC)); |
return(vwarn(mdoc, WWRONGMSEC)); |
} |
} |
break; |
break; |
default: |
default: |