Return to roff.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.77, 2010/05/16 19:08:11 | version 1.78, 2010/05/16 22:28:33 | ||
---|---|---|---|
|
|
||
struct roffmac { | struct roffmac { | ||
const char *name; /* macro name */ | const char *name; /* macro name */ | ||
roffproc proc; | roffproc proc; | ||
roffproc text; | |||
}; | }; | ||
static enum rofferr roff_if(ROFF_ARGS); | static enum rofferr roff_if(ROFF_ARGS); | ||
static enum rofferr roff_if_text(ROFF_ARGS); | |||
static enum rofferr roff_ig(ROFF_ARGS); | static enum rofferr roff_ig(ROFF_ARGS); | ||
static enum rofferr roff_ig_text(ROFF_ARGS); | |||
static enum rofferr roff_cblock(ROFF_ARGS); | static enum rofferr roff_cblock(ROFF_ARGS); | ||
static enum rofferr roff_ccond(ROFF_ARGS); | static enum rofferr roff_ccond(ROFF_ARGS); | ||
const struct roffmac roffs[ROFF_MAX] = { | const struct roffmac roffs[ROFF_MAX] = { | ||
{ "if", roff_if }, | { "if", roff_if, roff_if_text }, | ||
{ "ig", roff_ig }, | { "ig", roff_ig, roff_ig_text }, | ||
{ ".", roff_cblock }, | { ".", roff_cblock, NULL }, | ||
{ "\\}", roff_ccond }, | { "\\}", roff_ccond, NULL }, | ||
#if 0 | |||
{ "am", roff_sub_ig, roff_new_ig }, | |||
{ "ami", roff_sub_ig, roff_new_ig }, | |||
{ "de", roff_sub_ig, roff_new_ig }, | |||
{ "dei", roff_sub_ig, roff_new_ig }, | |||
#endif | |||
}; | }; | ||
static void roff_free1(struct roff *); | static void roff_free1(struct roff *); | ||
|
|
||
char **bufp, size_t *szp, int pos, int *offs) | char **bufp, size_t *szp, int pos, int *offs) | ||
{ | { | ||
enum rofft t; | enum rofft t; | ||
int ppos; | int ppos, i, j, wtf; | ||
if (r->last && ! ROFF_CTL((*bufp)[pos])) { | if (r->last && ! ROFF_CTL((*bufp)[pos])) { | ||
if (ROFF_ig == r->last->tok) | /* | ||
return(ROFF_IGN); | * If a scope is open and we're not a macro, pass it | ||
roffnode_cleanscope(r); | * through our text detector and continue as quickly as | ||
/* FIXME: this assumes we're discarding! */ | * possible. | ||
return(ROFF_IGN); | */ | ||
t = r->last->tok; | |||
assert(roffs[t].text); | |||
return((*roffs[t].text) | |||
(r, t, bufp, szp, ln, pos, pos, offs)); | |||
} else if ( ! ROFF_CTL((*bufp)[pos])) | } else if ( ! ROFF_CTL((*bufp)[pos])) | ||
/* | |||
* Don't do anything if we're free-form text. | |||
*/ | |||
return(ROFF_CONT); | return(ROFF_CONT); | ||
/* There's nothing on the stack: make us anew. */ | /* A macro-ish line with a possibly-open macro context. */ | ||
wtf = 0; | |||
if (r->last && r->last->end) { | |||
/* | |||
* We have a scope open that has a custom end-macro | |||
* handler. Try to match it against the input. | |||
*/ | |||
i = pos + 1; | |||
while (' ' == (*bufp)[i] || '\t' == (*bufp)[i]) | |||
i++; | |||
for (j = 0; r->last->end[j]; j++, i++) | |||
if ((*bufp)[i] != r->last->end[j]) | |||
break; | |||
if ('\0' == r->last->end[j] && | |||
('\0' == (*bufp)[i] || | |||
' ' == (*bufp)[i] || | |||
'\t' == (*bufp)[i])) { | |||
roffnode_pop(r); | |||
roffnode_cleanscope(r); | |||
wtf = 1; | |||
} | |||
} | |||
ppos = pos; | ppos = pos; | ||
if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) { | if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) { | ||
if (r->last && ROFF_ig == r->last->tok) | /* | ||
* This is some of groff's stranger behaviours. If we | |||
* encountered a custom end-scope tag and that tag also | |||
* happens to be a "real" macro, then we need to try | |||
* interpreting it again as a real macro. If it's not, | |||
* then return ignore. Else continue. | |||
*/ | |||
if (wtf) | |||
return(ROFF_IGN); | return(ROFF_IGN); | ||
return(ROFF_CONT); | else if (NULL == r->last) | ||
return(ROFF_CONT); | |||
/* FIXME: this assumes that we ignore!? */ | |||
return(ROFF_IGN); | |||
} | } | ||
assert(roffs[t].proc); | assert(roffs[t].proc); | ||
return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs)); | return((*roffs[t].proc) | ||
(r, t, bufp, szp, ln, ppos, pos, offs)); | |||
} | } | ||
|
|
||
static enum rofferr | static enum rofferr | ||
roff_ig(ROFF_ARGS) | roff_ig(ROFF_ARGS) | ||
{ | { | ||
int sv; | |||
size_t sz; | |||
if ( ! roffnode_push(r, tok, ln, ppos)) | if ( ! roffnode_push(r, tok, ln, ppos)) | ||
return(ROFF_ERR); | return(ROFF_ERR); | ||
ROFF_MDEBUG(r, "opening ignore block"); | if ('\0' == (*bufp)[pos]) { | ||
ROFF_MDEBUG(r, "opening ignore block"); | |||
return(ROFF_IGN); | |||
} | |||
sv = pos; | |||
while ((*bufp)[pos] && ' ' != (*bufp)[pos] && | |||
'\t' != (*bufp)[pos]) | |||
pos++; | |||
/* | |||
* Note: groff does NOT like escape characters in the input. | |||
* Instead of detecting this, we're just going to let it fly and | |||
* to hell with it. | |||
*/ | |||
assert(pos > sv); | |||
sz = (size_t)(pos - sv); | |||
r->last->end = malloc(sz + 1); | |||
if (NULL == r->last->end) { | |||
(*r->msg)(MANDOCERR_MEM, r->data, ln, pos, NULL); | |||
return(ROFF_ERR); | |||
} | |||
memcpy(r->last->end, *bufp + sv, sz); | |||
r->last->end[(int)sz] = '\0'; | |||
ROFF_MDEBUG(r, "opening explicit ignore block"); | |||
if ((*bufp)[pos]) | if ((*bufp)[pos]) | ||
if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL)) | if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL)) | ||
return(ROFF_ERR); | return(ROFF_ERR); | ||
return(ROFF_IGN); | |||
} | |||
/* ARGSUSED */ | |||
static enum rofferr | |||
roff_ig_text(ROFF_ARGS) | |||
{ | |||
return(ROFF_IGN); | |||
} | |||
/* ARGSUSED */ | |||
static enum rofferr | |||
roff_if_text(ROFF_ARGS) | |||
{ | |||
char *ep, *st; | |||
st = &(*bufp)[pos]; | |||
if (NULL == (ep = strstr(st, "\\}"))) { | |||
roffnode_cleanscope(r); | |||
return(ROFF_IGN); | |||
} | |||
if (ep > st && '\\' != *(ep - 1)) { | |||
ROFF_MDEBUG(r, "closing explicit scope (in-line)"); | |||
roffnode_pop(r); | |||
} | |||
roffnode_cleanscope(r); | |||
return(ROFF_IGN); | return(ROFF_IGN); | ||
} | } | ||