summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@cvs.openbsd.org>2019-04-21 22:43:01 +0000
committerIngo Schwarze <schwarze@cvs.openbsd.org>2019-04-21 22:43:01 +0000
commit4f5bfee9461d5b9b6911f86d1ab77417eb7a6c70 (patch)
tree09eea9727827fa02bff54cc1080cc3b8ca3d72ff
parent9947a2ff62b4914537d5e8bacc03372dbba1d937 (diff)
Implement the roff .break request (break out of a .while loop).
Jan Stary <hans at stare dot cz> found it in an ancient groffer(1) manual page (version 1.19) on MacOS X Mojave. Having .break not implemented wasn't a particularly bright idea because obviously, it tended to cause infinite loops.
-rw-r--r--regress/usr.bin/mandoc/roff/while/Makefile4
-rw-r--r--regress/usr.bin/mandoc/roff/while/break.in16
-rw-r--r--regress/usr.bin/mandoc/roff/while/break.out_ascii9
-rw-r--r--share/man/man7/roff.77
-rw-r--r--usr.bin/mandoc/roff.c55
5 files changed, 76 insertions, 15 deletions
diff --git a/regress/usr.bin/mandoc/roff/while/Makefile b/regress/usr.bin/mandoc/roff/while/Makefile
index ed970474667..42ce590a865 100644
--- a/regress/usr.bin/mandoc/roff/while/Makefile
+++ b/regress/usr.bin/mandoc/roff/while/Makefile
@@ -1,6 +1,6 @@
-# $OpenBSD: Makefile,v 1.1 2018/08/24 22:56:37 schwarze Exp $
+# $OpenBSD: Makefile,v 1.2 2019/04/21 22:43:00 schwarze Exp $
-REGRESS_TARGETS = basic badargs into nesting outof
+REGRESS_TARGETS = basic badargs break into nesting outof
LINT_TARGETS = badargs into nesting outof
# mandoc defects:
diff --git a/regress/usr.bin/mandoc/roff/while/break.in b/regress/usr.bin/mandoc/roff/while/break.in
new file mode 100644
index 00000000000..11e7d2f5e99
--- /dev/null
+++ b/regress/usr.bin/mandoc/roff/while/break.in
@@ -0,0 +1,16 @@
+.\" $OpenBSD: break.in,v 1.1 2019/04/21 22:43:00 schwarze Exp $
+.Dd $Mdocdate: April 21 2019 $
+.Dt WHILE-BREAK 1
+.Os
+.Sh NAME
+.Nm while-break
+.Nd break request inside a while loop
+.Sh DESCRIPTION
+initial text
+.nr cnt 11 1
+.while n \{\
+\n-[cnt]
+.if !\n[cnt] .break
+\(en
+.\}
+final text
diff --git a/regress/usr.bin/mandoc/roff/while/break.out_ascii b/regress/usr.bin/mandoc/roff/while/break.out_ascii
new file mode 100644
index 00000000000..8e3f39b941b
--- /dev/null
+++ b/regress/usr.bin/mandoc/roff/while/break.out_ascii
@@ -0,0 +1,9 @@
+WHILE-BREAK(1) General Commands Manual WHILE-BREAK(1)
+
+NNAAMMEE
+ wwhhiillee--bbrreeaakk - break request inside a while loop
+
+DDEESSCCRRIIPPTTIIOONN
+ initial text 10 - 9 - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0 final text
+
+OpenBSD April 21, 2019 OpenBSD
diff --git a/share/man/man7/roff.7 b/share/man/man7/roff.7
index f6806d2bce8..c380510768d 100644
--- a/share/man/man7/roff.7
+++ b/share/man/man7/roff.7
@@ -1,4 +1,4 @@
-.\" $OpenBSD: roff.7,v 1.91 2019/01/01 03:44:48 schwarze Exp $
+.\" $OpenBSD: roff.7,v 1.92 2019/04/21 22:43:00 schwarze Exp $
.\"
.\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org>
@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: January 1 2019 $
+.Dd $Mdocdate: April 21 2019 $
.Dt ROFF 7
.Os
.Sh NAME
@@ -503,10 +503,9 @@ This is a Heirloom extension and currently unsupported.
.It Ic \&br
Break the output line.
.It Ic \&break
-Break out of a
+Break out of the innermost
.Ic \&while
loop.
-Currently unsupported.
.It Ic \&breakchar Ar char ...
Optional line break characters.
This is a Heirloom extension and currently ignored.
diff --git a/usr.bin/mandoc/roff.c b/usr.bin/mandoc/roff.c
index b14aebd8071..27ef98143f5 100644
--- a/usr.bin/mandoc/roff.c
+++ b/usr.bin/mandoc/roff.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: roff.c,v 1.235 2019/02/06 20:54:28 schwarze Exp $ */
+/* $OpenBSD: roff.c,v 1.236 2019/04/21 22:43:00 schwarze Exp $ */
/*
* Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
@@ -131,15 +131,18 @@ struct roff {
char escape; /* escape character */
};
+/*
+ * A macro definition, condition, or ignored block.
+ */
struct roffnode {
enum roff_tok tok; /* type of node */
struct roffnode *parent; /* up one in stack */
int line; /* parse line */
int col; /* parse col */
char *name; /* node name, e.g. macro name */
- char *end; /* end-rules: custom token */
- int endspan; /* end-rules: next-line or infty */
- int rule; /* current evaluation rule */
+ char *end; /* custom end macro of the block */
+ int endspan; /* scope to: 1=eol 2=next line -1=\} */
+ int rule; /* content is: 1=evaluated 0=skipped */
};
#define ROFF_ARGS struct roff *r, /* parse ctx */ \
@@ -179,6 +182,7 @@ static int roff_als(ROFF_ARGS);
static int roff_block(ROFF_ARGS);
static int roff_block_text(ROFF_ARGS);
static int roff_block_sub(ROFF_ARGS);
+static int roff_break(ROFF_ARGS);
static int roff_cblock(ROFF_ARGS);
static int roff_cc(ROFF_ARGS);
static int roff_ccond(struct roff *, int, int);
@@ -398,7 +402,7 @@ static struct roffmac roffs[TOKEN_NONE] = {
{ roff_unsupp, NULL, NULL, 0 }, /* boxa */
{ roff_line_ignore, NULL, NULL, 0 }, /* bp */
{ roff_unsupp, NULL, NULL, 0 }, /* BP */
- { roff_unsupp, NULL, NULL, 0 }, /* break */
+ { roff_break, NULL, NULL, 0 }, /* break */
{ roff_line_ignore, NULL, NULL, 0 }, /* breakchar */
{ roff_line_ignore, NULL, NULL, 0 }, /* brnl */
{ roff_noarg, NULL, NULL, 0 }, /* brp */
@@ -683,7 +687,7 @@ roffhash_find(struct ohash *htab, const char *name, size_t sz)
/*
* Pop the current node off of the stack of roff instructions currently
- * pending.
+ * pending. Return 1 if it is a loop or 0 otherwise.
*/
static int
roffnode_pop(struct roff *r)
@@ -2000,6 +2004,10 @@ roff_cblock(ROFF_ARGS)
}
+/*
+ * Pop all nodes ending at the end of the current input line.
+ * Return the number of loops ended.
+ */
static int
roffnode_cleanscope(struct roff *r)
{
@@ -2014,6 +2022,11 @@ roffnode_cleanscope(struct roff *r)
return inloop;
}
+/*
+ * Handle the closing \} of a conditional block.
+ * Apart from generating warnings, this only pops nodes.
+ * Return the number of loops ended.
+ */
static int
roff_ccond(struct roff *r, int ln, int ppos)
{
@@ -2233,6 +2246,7 @@ roff_block_text(ROFF_ARGS)
static int
roff_cond_sub(ROFF_ARGS)
{
+ struct roffnode *bl;
char *ep;
int endloop, irc, rr;
enum roff_tok t;
@@ -2280,9 +2294,21 @@ roff_cond_sub(ROFF_ARGS)
*/
t = roff_parse(r, buf->buf, &pos, ln, ppos);
- irc |= t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT) ?
- (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) :
- rr ? ROFF_CONT : ROFF_IGN;
+ if (t == ROFF_break) {
+ if (irc & ROFF_LOOPMASK)
+ irc = ROFF_IGN | ROFF_LOOPEXIT;
+ else if (rr) {
+ for (bl = r->last; bl != NULL; bl = bl->parent) {
+ bl->rule = 0;
+ if (bl->tok == ROFF_while)
+ break;
+ }
+ }
+ } else if (t != TOKEN_NONE &&
+ (rr || roffs[t].flags & ROFFMAC_STRUCT))
+ irc |= (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
+ else
+ irc |= rr ? ROFF_CONT : ROFF_IGN;
return irc;
}
@@ -3480,6 +3506,17 @@ roff_als(ROFF_ARGS)
return ROFF_IGN;
}
+/*
+ * The .break request only makes sense inside conditionals,
+ * and that case is already handled in roff_cond_sub().
+ */
+static int
+roff_break(ROFF_ARGS)
+{
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break");
+ return ROFF_IGN;
+}
+
static int
roff_cc(ROFF_ARGS)
{