-/* $Id: roff.c,v 1.215 2014/07/01 00:32:29 schwarze Exp $ */
+/* $Id: roff.c,v 1.227 2014/09/06 22:39:36 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifdef HAVE_CONFIG_H
#include "config.h"
-#endif
+
+#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include "mandoc.h"
#include "mandoc_aux.h"
-#include "libroff.h"
#include "libmandoc.h"
+#include "libroff.h"
/* Maximum number of nested if-else conditionals. */
#define RSTACK_MAX 128
struct roff {
struct mparse *parse; /* parse point */
- int options; /* parse options */
struct roffnode *last; /* leaf of stack */
- int rstack[RSTACK_MAX]; /* stack of !`ie' rules */
- char control; /* control character */
- int rstackpos; /* position in rstack */
+ int *rstack; /* stack of inverted `ie' values */
struct roffreg *regtab; /* number registers */
struct roffkv *strtab; /* user-defined strings & macros */
struct roffkv *xmbtab; /* multi-byte trans table (`tr') */
struct eqn_node *last_eqn; /* last equation parsed */
struct eqn_node *first_eqn; /* first equation parsed */
struct eqn_node *eqn; /* current equation being parsed */
+ int options; /* parse options */
+ int rstacksz; /* current size limit of rstack */
+ int rstackpos; /* position in rstack */
+ int format; /* current file in mdoc or man format */
+ char control; /* control character */
};
struct roffnode {
r->first_tbl = tbl->next;
tbl_free(tbl);
}
-
r->first_tbl = r->last_tbl = r->tbl = NULL;
while (NULL != (e = r->first_eqn)) {
r->first_eqn = e->next;
eqn_free(e);
}
-
r->first_eqn = r->last_eqn = r->eqn = NULL;
while (r->last)
roffnode_pop(r);
- roff_freestr(r->strtab);
- roff_freestr(r->xmbtab);
-
- r->strtab = r->xmbtab = NULL;
+ free (r->rstack);
+ r->rstack = NULL;
+ r->rstacksz = 0;
+ r->rstackpos = -1;
roff_freereg(r->regtab);
-
r->regtab = NULL;
+ roff_freestr(r->strtab);
+ roff_freestr(r->xmbtab);
+ r->strtab = r->xmbtab = NULL;
+
if (r->xtab)
for (i = 0; i < 128; i++)
free(r->xtab[i].p);
-
free(r->xtab);
r->xtab = NULL;
}
{
roff_free1(r);
+ r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
r->control = 0;
}
r = mandoc_calloc(1, sizeof(struct roff));
r->parse = parse;
r->options = options;
+ r->format = options & (MPARSE_MDOC | MPARSE_MAN);
r->rstackpos = -1;
roffhash_init();
size_t naml; /* actual length of the escape name */
int expand_count; /* to avoid infinite loops */
int npos; /* position in numeric expression */
- int irc; /* return code from roff_evalnum() */
+ int arg_complete; /* argument not interrupted by eol */
char term; /* character terminating the escape */
expand_count = 0;
break;
default:
if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
- mandoc_msg(MANDOCERR_BADESCAPE, r->parse,
- ln, (int)(stesc - *bufp), NULL);
+ mandoc_vmsg(MANDOCERR_ESC_BAD,
+ r->parse, ln, (int)(stesc - *bufp),
+ "%.*s", (int)(cp - stesc), stesc);
continue;
}
/* Advance to the end of the name. */
+ arg_complete = 1;
for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) {
if ('\0' == *cp) {
- mandoc_msg(MANDOCERR_BADESCAPE, r->parse,
- ln, (int)(stesc - *bufp), NULL);
+ mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
+ ln, (int)(stesc - *bufp), stesc);
+ arg_complete = 0;
break;
}
if (0 == maxl && *cp == term) {
switch (stesc[1]) {
case '*':
- res = roff_getstrn(r, stnam, naml);
+ if (arg_complete)
+ res = roff_getstrn(r, stnam, naml);
break;
case 'B':
npos = 0;
- irc = roff_evalnum(stnam, &npos, NULL, 0);
- ubuf[0] = irc && stnam + npos + 1 == cp
- ? '1' : '0';
+ ubuf[0] = arg_complete &&
+ roff_evalnum(stnam, &npos, NULL, 0) &&
+ stnam + npos + 1 == cp ? '1' : '0';
ubuf[1] = '\0';
break;
case 'n':
- (void)snprintf(ubuf, sizeof(ubuf), "%d",
- roff_getregn(r, stnam, naml));
+ if (arg_complete)
+ (void)snprintf(ubuf, sizeof(ubuf), "%d",
+ roff_getregn(r, stnam, naml));
+ else
+ ubuf[0] = '\0';
break;
case 'w':
+ /* use even incomplete args */
(void)snprintf(ubuf, sizeof(ubuf), "%d",
24 * (int)naml);
break;
}
if (NULL == res) {
- mandoc_msg(MANDOCERR_BADESCAPE, r->parse,
- ln, (int)(stesc - *bufp), NULL);
+ mandoc_vmsg(MANDOCERR_STR_UNDEF,
+ r->parse, ln, (int)(stesc - *bufp),
+ "%.*s", (int)naml, stnam);
res = "";
}
{
if (r->last)
- mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
- r->last->line, r->last->col, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
+ r->last->line, r->last->col,
+ roffs[r->last->tok].name);
if (r->eqn) {
- mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
- r->eqn->eqn.ln, r->eqn->eqn.pos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
+ r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ");
eqn_end(&r->eqn);
}
if (r->tbl) {
- mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
- r->tbl->line, r->tbl->pos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
+ r->tbl->line, r->tbl->pos, "TS");
tbl_end(&r->tbl);
}
}
*/
if (NULL == r->last) {
- mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
+ ln, ppos, "..");
return(ROFF_IGN);
}
switch (r->last->tok) {
case ROFF_am:
+ /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
/* FALLTHROUGH */
case ROFF_ami:
/* FALLTHROUGH */
- case ROFF_am1:
- /* FALLTHROUGH */
case ROFF_de:
/* ROFF_de1 is remapped to ROFF_de in roff_block(). */
/* FALLTHROUGH */
case ROFF_ig:
break;
default:
- mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
+ ln, ppos, "..");
return(ROFF_IGN);
}
if ((*bufp)[pos])
- mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
+ mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
+ ".. %s", *bufp + pos);
roffnode_pop(r);
roffnode_cleanscope(r);
{
if (NULL == r->last) {
- mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
+ ln, ppos, "\\}");
return;
}
case ROFF_if:
break;
default:
- mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
+ ln, ppos, "\\}");
return;
}
if (r->last->endspan > -1) {
- mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
+ ln, ppos, "\\}");
return;
}
static enum rofferr
roff_block(ROFF_ARGS)
{
- char *name, *cp;
+ const char *name;
+ char *iname, *cp;
size_t namesz;
- name = cp = *bufp + pos;
- namesz = 0;
+ /* Ignore groff compatibility mode for now. */
- if (ROFF_ig != tok) {
- if ('\0' == *cp) {
- mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
- return(ROFF_IGN);
- }
+ if (ROFF_de1 == tok)
+ tok = ROFF_de;
+ else if (ROFF_am1 == tok)
+ tok = ROFF_am;
- /*
- * Re-write `de1', since we don't really care about
- * groff's strange compatibility mode, into `de'.
- */
-
- if (ROFF_de1 == tok)
- tok = ROFF_de;
- else if (ROFF_de != tok)
- mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos,
- roffs[tok].name);
+ /* Parse the macro name argument. */
+ cp = *bufp + pos;
+ if (ROFF_ig == tok) {
+ iname = NULL;
+ namesz = 0;
+ } else {
+ iname = cp;
namesz = roff_getname(r, &cp, ln, ppos);
- name[namesz] = '\0';
+ iname[namesz] = '\0';
+ }
+
+ /* Resolve the macro name argument if it is indirect. */
+
+ if (namesz && (ROFF_dei == tok || ROFF_ami == tok)) {
+ if (NULL == (name = roff_getstrn(r, iname, namesz))) {
+ mandoc_vmsg(MANDOCERR_STR_UNDEF,
+ r->parse, ln, (int)(iname - *bufp),
+ "%.*s", (int)namesz, iname);
+ namesz = 0;
+ } else
+ namesz = strlen(name);
} else
- name = NULL;
+ name = iname;
+
+ if (0 == namesz && ROFF_ig != tok) {
+ mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse,
+ ln, ppos, roffs[tok].name);
+ return(ROFF_IGN);
+ }
roffnode_push(r, tok, name, ln, ppos);
* appended from roff_block_text() in multiline mode.
*/
- if (namesz && ROFF_de == tok)
+ if (ROFF_de == tok || ROFF_dei == tok)
roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
if ('\0' == *cp)
return(ROFF_IGN);
- /* If present, process the custom end-of-line marker. */
+ /* Get the custom end marker. */
- name = cp;
+ iname = cp;
namesz = roff_getname(r, &cp, ln, ppos);
+
+ /* Resolve the end marker if it is indirect. */
+
+ if (namesz && (ROFF_dei == tok || ROFF_ami == tok)) {
+ if (NULL == (name = roff_getstrn(r, iname, namesz))) {
+ mandoc_vmsg(MANDOCERR_STR_UNDEF,
+ r->parse, ln, (int)(iname - *bufp),
+ "%.*s", (int)namesz, iname);
+ namesz = 0;
+ } else
+ namesz = strlen(name);
+ } else
+ name = iname;
+
if (namesz)
r->last->end = mandoc_strndup(name, namesz);
if ('\0' != *cp)
- mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
+ mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
+ ln, pos, ".%s ... %s", roffs[tok].name, cp);
return(ROFF_IGN);
}
t = roff_parse(r, *bufp, &pos, ln, ppos);
- /*
- * Macros other than block-end are only significant
- * in `de' blocks; elsewhere, simply throw them away.
- */
if (ROFF_cblock != t) {
- if (ROFF_de == tok)
+ if (ROFF_ig != tok)
roff_setstr(r, r->last->name, *bufp + ppos, 2);
return(ROFF_IGN);
}
roff_block_text(ROFF_ARGS)
{
- if (ROFF_de == tok)
+ if (ROFF_ig != tok)
roff_setstr(r, r->last->name, *bufp + pos, 2);
return(ROFF_IGN);
*/
if (ROFF_ie == tok) {
- if (r->rstackpos == RSTACK_MAX - 1) {
- mandoc_msg(MANDOCERR_MEM,
- r->parse, ln, ppos, NULL);
- return(ROFF_ERR);
+ if (r->rstackpos + 1 == r->rstacksz) {
+ r->rstacksz += 16;
+ r->rstack = mandoc_reallocarray(r->rstack,
+ r->rstacksz, sizeof(int));
}
r->rstack[++r->rstackpos] = !r->last->rule;
}
*/
if ('\0' == (*bufp)[pos])
- mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_COND_EMPTY, r->parse,
+ ln, ppos, roffs[tok].name);
r->last->endspan = 1;
len = strcspn(cp, " \t");
cp[len] = '\0';
if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) {
- mandoc_msg(MANDOCERR_NUMERIC, r->parse,
+ mandoc_msg(MANDOCERR_IT_NONUM, r->parse,
ln, ppos, *bufp + 1);
return(ROFF_IGN);
}
{
const char *const *cp;
- if (0 == ((MPARSE_MDOC | MPARSE_QUICK) & r->options))
+ if ((r->options & (MPARSE_MDOC | MPARSE_QUICK)) == 0)
for (cp = __mdoc_reserved; *cp; cp++)
roff_setstr(r, *cp, NULL, 0);
+ if (r->format == 0)
+ r->format = MPARSE_MDOC;
+
return(ROFF_CONT);
}
{
const char *const *cp;
- if (0 == (MPARSE_QUICK & r->options))
+ if ((r->options & MPARSE_QUICK) == 0)
for (cp = __man_reserved; *cp; cp++)
roff_setstr(r, *cp, NULL, 0);
+ if (r->format == 0)
+ r->format = MPARSE_MAN;
+
return(ROFF_CONT);
}
{
if (NULL == r->tbl)
- mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
+ ln, ppos, "TE");
else
tbl_end(&r->tbl);
{
if (NULL == r->tbl)
- mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
+ ln, ppos, "T&");
else
tbl_restart(ppos, ln, r->tbl);
roff_EN(ROFF_ARGS)
{
- mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN");
return(ROFF_IGN);
}
struct tbl_node *tbl;
if (r->tbl) {
- mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
+ mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse,
+ ln, ppos, "TS breaks TS");
tbl_end(&r->tbl);
}
if ('\\' == *first) {
esc = mandoc_escape(&p, NULL, NULL);
if (ESCAPE_ERROR == esc) {
- mandoc_msg(MANDOCERR_BADESCAPE,
- r->parse, ln,
- (int)(p - *bufp), NULL);
+ mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
+ ln, (int)(p - *bufp), first);
return(ROFF_IGN);
}
fsz = (size_t)(p - first);
if ('\\' == *second) {
esc = mandoc_escape(&p, NULL, NULL);
if (ESCAPE_ERROR == esc) {
- mandoc_msg(MANDOCERR_BADESCAPE,
- r->parse, ln,
- (int)(p - *bufp), NULL);
+ mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
+ ln, (int)(p - *bufp), second);
return(ROFF_IGN);
}
ssz = (size_t)(p - second);
char *name;
name = *bufp + pos;
- mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, ".so %s", name);
+ mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name);
/*
* Handle `so'. Be EXTREMELY careful, as we shouldn't be
cp++;
if ('\\' == *cp)
continue;
- mandoc_msg(MANDOCERR_NAMESC, r->parse, ln, pos, NULL);
+ mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos,
+ "%.*s", (int)(cp - name + 1), name);
mandoc_escape((const char **)&cp, NULL, NULL);
break;
}
return(res);
}
+int
+roff_getformat(const struct roff *r)
+{
+
+ return(r->format);
+}
+
/*
* Find out whether a line is a macro line or not.
* If it is, adjust the current position and return one; if it isn't,