summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main.c4
-rw-r--r--mandoc.h4
-rw-r--r--regress/roff/if/multiline-free0.in10
-rw-r--r--regress/roff/if/multiline-free1.in11
-rw-r--r--regress/roff/ig/end0.in12
-rw-r--r--regress/roff/ig/end1.in12
-rw-r--r--regress/roff/ig/redef0.in13
-rw-r--r--regress/roff/ig/simple0.in12
-rw-r--r--regress/roff/ig/simple1.in12
-rw-r--r--regress/roff/ig/simple2.in15
-rw-r--r--roff.770
-rw-r--r--roff.c145
12 files changed, 290 insertions, 30 deletions
diff --git a/main.c b/main.c
index d6f3c008..583f9f61 100644
--- a/main.c
+++ b/main.c
@@ -798,8 +798,8 @@ mwarn(void *arg, int line, int col, const char *msg)
static const char * const mandocerrs[MANDOCERR_MAX] = {
"ok",
"multi-line scope open on exit",
- "request for scope closure when no matching scope is open",
- "macro requires line argument(s)",
+ "request for scope closure when no matching scope is open: ignored",
+ "macro requires line argument(s): ignored",
"line arguments will be lost",
"memory exhausted"
};
diff --git a/mandoc.h b/mandoc.h
index d127625b..ff784a60 100644
--- a/mandoc.h
+++ b/mandoc.h
@@ -22,10 +22,10 @@ __BEGIN_DECLS
enum mandocerr {
MANDOCERR_OK,
MANDOCERR_SCOPEEXIT, /* scope open on exit */
- MANDOCERR_NOSCOPE, /* request scope close w/none open */
- MANDOCERR_NOARGS, /* macro requires argument(s) */
#define MANDOCERR_WARNING MANDOCERR_SCOPEEXIT
+ MANDOCERR_NOSCOPE, /* request scope close w/none open */
+ MANDOCERR_NOARGS, /* macro requires argument(s) */
MANDOCERR_ARGSLOST, /* line arguments will be lost */
#define MANDOCERR_ERROR MANDOCERR_ARGSLOST
diff --git a/regress/roff/if/multiline-free0.in b/regress/roff/if/multiline-free0.in
new file mode 100644
index 00000000..f9fb0fd9
--- /dev/null
+++ b/regress/roff/if/multiline-free0.in
@@ -0,0 +1,10 @@
+.Dd $Mdocdate$
+.Dt FOO 1
+.Os
+.Sh NAME
+.Nm foo
+.Nd bar
+.Sh DESCRIPTION
+.if t \{\
+there \} dude
+fdsa
diff --git a/regress/roff/if/multiline-free1.in b/regress/roff/if/multiline-free1.in
new file mode 100644
index 00000000..ac147435
--- /dev/null
+++ b/regress/roff/if/multiline-free1.in
@@ -0,0 +1,11 @@
+.Dd $Mdocdate$
+.Dt FOO 1
+.Os
+.Sh NAME
+.Nm foo
+.Nd bar
+.Sh DESCRIPTION
+.if t \{\
+there \\} dude
+.\}
+fdsa
diff --git a/regress/roff/ig/end0.in b/regress/roff/ig/end0.in
new file mode 100644
index 00000000..ad35726d
--- /dev/null
+++ b/regress/roff/ig/end0.in
@@ -0,0 +1,12 @@
+.Dd $Mdocdate$
+.Dt FOO 1
+.Os
+.Sh NAME
+.Nm foo
+.Nd bar
+.Sh DESCRIPTION
+a
+.ig foo
+asdf
+.foo
+b
diff --git a/regress/roff/ig/end1.in b/regress/roff/ig/end1.in
new file mode 100644
index 00000000..c1d075af
--- /dev/null
+++ b/regress/roff/ig/end1.in
@@ -0,0 +1,12 @@
+.Dd $Mdocdate$
+.Dt FOO 1
+.Os
+.Sh NAME
+.Nm foo
+.Nd bar
+.Sh DESCRIPTION
+a
+.ig foo
+asdf
+.foo
+b
diff --git a/regress/roff/ig/redef0.in b/regress/roff/ig/redef0.in
new file mode 100644
index 00000000..4d5a7e76
--- /dev/null
+++ b/regress/roff/ig/redef0.in
@@ -0,0 +1,13 @@
+.Dd $Mdocdate$
+.Dt FOO 1
+.Os
+.Sh NAME
+.Nm foo
+.Nd bar
+.Sh DESCRIPTION
+a
+.ig if
+asdf
+.if t \
+b
+c
diff --git a/regress/roff/ig/simple0.in b/regress/roff/ig/simple0.in
new file mode 100644
index 00000000..d693ce77
--- /dev/null
+++ b/regress/roff/ig/simple0.in
@@ -0,0 +1,12 @@
+.Dd $Mdocdate$
+.Dt FOO 1
+.Os
+.Sh NAME
+.Nm foo
+.Nd bar
+.Sh DESCRIPTION
+123
+.ig
+hello
+..
+12124
diff --git a/regress/roff/ig/simple1.in b/regress/roff/ig/simple1.in
new file mode 100644
index 00000000..d693ce77
--- /dev/null
+++ b/regress/roff/ig/simple1.in
@@ -0,0 +1,12 @@
+.Dd $Mdocdate$
+.Dt FOO 1
+.Os
+.Sh NAME
+.Nm foo
+.Nd bar
+.Sh DESCRIPTION
+123
+.ig
+hello
+..
+12124
diff --git a/regress/roff/ig/simple2.in b/regress/roff/ig/simple2.in
new file mode 100644
index 00000000..4e82458c
--- /dev/null
+++ b/regress/roff/ig/simple2.in
@@ -0,0 +1,15 @@
+.Dd $Mdocdate$
+.Dt FOO 1
+.Os
+.Sh NAME
+.Nm foo
+.Nd bar
+.Sh DESCRIPTION
+123
+.ig
+hello
+.if t \{\
+hello
+.\}
+..
+12124
diff --git a/roff.7 b/roff.7
index 7a41aeee..10b319a5 100644
--- a/roff.7
+++ b/roff.7
@@ -76,6 +76,10 @@ BODY...
.Ed
.Bd -literal -offset indent -compact
\&.if COND \e{ BODY
+BODY... \e}
+.Ed
+.Bd -literal -offset indent -compact
+\&.if COND \e{ BODY
BODY...
\&.\e}
.Ed
@@ -112,15 +116,71 @@ The scope of a conditional is always parsed, but only executed if the
conditional evaluates to true.
.Pp
Note that text subsequent a
+.Sq \&.\e}
+macro is discarded.
+Furthermore, if an explicit closing sequence
.Sq \e}
-is discarded.
+is specified in a free-form line, the entire line is accepted within the
+scope of the prior macro, not only the text preceding the close.
.Ss \&ig
-Ignore input until a
-.Sq \.\.
+Ignore input.
+Accepts the following syntax:
+.Pp
+.Bd -literal -offset indent -compact
+\&.ig
+BODY...
+\&..
+.Ed
+.Bd -literal -offset indent -compact
+\&.ig END
+BODY...
+\&.END
+.Ed
+.Pp
+In the first case, input is ignored until a
+.Sq \&..
macro is encountered on its own line.
-Note that text subsequent the
-.Sq \.\.
+In the second case, input is ignored until a
+.Sq \&.END
+is encountered.
+Text subsequent the
+.Sq \&.END
+or
+.Sq \&..
is discarded.
+.Pp
+Do not use the escape
+.Sq \e
+anywhere in the definition of END.
+It causes very strange behaviour.
+Furthermore, if you redefine a
+.Nm
+macro, such as
+.Pp
+.D1 \&.ig if
+.Pp
+the subsequent invocation of
+.Sx \&if
+will first signify the end of comment, then be invoked as a macro.
+This behaviour really shouldn't be counted upon.
+.Sh COMPATIBILITY
+This section documents compatibility between mandoc and other other
+troff implementations, at this time limited to GNU troff
+.Pq Qq groff .
+The term
+.Qq historic groff
+refers to groff versions before the
+.Pa doc.tmac
+file re-write
+.Pq somewhere between 1.15 and 1.19 .
+.Pp
+.Bl -dash -compact
+.It
+Historic groff did not accept white-space buffering the custom END tag
+for the
+.Sx \&ig
+macro.
+.El
.Sh AUTHORS
The
.Nm
diff --git a/roff.c b/roff.c
index e606bcd5..a1606e20 100644
--- a/roff.c
+++ b/roff.c
@@ -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;