aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/roff.c
diff options
context:
space:
mode:
authorKristaps Dzonsons <kristaps@bsd.lv>2010-05-16 22:28:33 +0000
committerKristaps Dzonsons <kristaps@bsd.lv>2010-05-16 22:28:33 +0000
commitce6c60c32bf1ef48cae539a850d142548b24d8ad (patch)
tree67f9314d3b2e5e1ccf9a2d368b8eadbe6eba8879 /roff.c
parentf34d66a2879319e93c3b35ed619dbb00a3a7885f (diff)
downloadmandoc-ce6c60c32bf1ef48cae539a850d142548b24d8ad.tar.gz
mandoc-ce6c60c32bf1ef48cae539a850d142548b24d8ad.tar.zst
mandoc-ce6c60c32bf1ef48cae539a850d142548b24d8ad.zip
`ig' support in all its glory. Try
.ig ig asdf .ig fdsa .. or .ig if asdf .if n \ foo for a laugh. It all works. Lots of regression tests supporting this and documentation for the same.
Diffstat (limited to 'roff.c')
-rw-r--r--roff.c147
1 files changed, 125 insertions, 22 deletions
diff --git a/roff.c b/roff.c
index 09d3527b..a6544c9f 100644
--- a/roff.c
+++ b/roff.c
@@ -1,4 +1,4 @@
-/* $Id: roff.c,v 1.77 2010/05/16 19:08:11 kristaps Exp $ */
+/* $Id: roff.c,v 1.78 2010/05/16 22:28:33 kristaps Exp $ */
/*
* Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
*
@@ -81,24 +81,21 @@ typedef enum rofferr (*roffproc)(ROFF_ARGS);
struct roffmac {
const char *name; /* macro name */
roffproc proc;
+ roffproc text;
};
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_text(ROFF_ARGS);
static enum rofferr roff_cblock(ROFF_ARGS);
static enum rofferr roff_ccond(ROFF_ARGS);
const struct roffmac roffs[ROFF_MAX] = {
- { "if", roff_if },
- { "ig", roff_ig },
- { ".", roff_cblock },
- { "\\}", roff_ccond },
-#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
+ { "if", roff_if, roff_if_text },
+ { "ig", roff_ig, roff_ig_text },
+ { ".", roff_cblock, NULL },
+ { "\\}", roff_ccond, NULL },
};
static void roff_free1(struct roff *);
@@ -218,28 +215,72 @@ roff_parseln(struct roff *r, int ln,
char **bufp, size_t *szp, int pos, int *offs)
{
enum rofft t;
- int ppos;
+ int ppos, i, j, wtf;
if (r->last && ! ROFF_CTL((*bufp)[pos])) {
- if (ROFF_ig == r->last->tok)
- return(ROFF_IGN);
- roffnode_cleanscope(r);
- /* FIXME: this assumes we're discarding! */
- return(ROFF_IGN);
+ /*
+ * If a scope is open and we're not a macro, pass it
+ * through our text detector and continue as quickly as
+ * possible.
+ */
+ 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]))
+ /*
+ * Don't do anything if we're free-form text.
+ */
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;
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_CONT);
+ else if (NULL == r->last)
+ return(ROFF_CONT);
+
+ /* FIXME: this assumes that we ignore!? */
+ return(ROFF_IGN);
}
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));
}
@@ -375,11 +416,42 @@ roff_ccond(ROFF_ARGS)
static enum rofferr
roff_ig(ROFF_ARGS)
{
+ int sv;
+ size_t sz;
if ( ! roffnode_push(r, tok, ln, ppos))
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 ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
@@ -391,6 +463,37 @@ roff_ig(ROFF_ARGS)
/* 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);
+}
+
+
+/* ARGSUSED */
+static enum rofferr
roff_if(ROFF_ARGS)
{
int sv;