diff options
author | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2019-04-21 22:43:01 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2019-04-21 22:43:01 +0000 |
commit | 4f5bfee9461d5b9b6911f86d1ab77417eb7a6c70 (patch) | |
tree | 09eea9727827fa02bff54cc1080cc3b8ca3d72ff | |
parent | 9947a2ff62b4914537d5e8bacc03372dbba1d937 (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/Makefile | 4 | ||||
-rw-r--r-- | regress/usr.bin/mandoc/roff/while/break.in | 16 | ||||
-rw-r--r-- | regress/usr.bin/mandoc/roff/while/break.out_ascii | 9 | ||||
-rw-r--r-- | share/man/man7/roff.7 | 7 | ||||
-rw-r--r-- | usr.bin/mandoc/roff.c | 55 |
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) { |