Element next-line scopes may nest, so man_breakscope() may have to
authorschwarze <schwarze@openbsd.org>
Thu, 28 Apr 2022 10:17:37 +0000 (10:17 +0000)
committerschwarze <schwarze@openbsd.org>
Thu, 28 Apr 2022 10:17:37 +0000 (10:17 +0000)
break multiple element next-line scopes at the same time, similar to
what man_descope() already does for unconditional rewinding.

This fixes an assertion failure that tb@ found with afl(1), caused
by .SH .I .I .BI and similar sequences of macros without arguments.

regress/usr.bin/mandoc/man/B/Makefile
regress/usr.bin/mandoc/man/B/nest.in [new file with mode: 0644]
regress/usr.bin/mandoc/man/B/nest.out_ascii [new file with mode: 0644]
regress/usr.bin/mandoc/man/B/nest.out_lint [new file with mode: 0644]
usr.bin/mandoc/man.c

index ac010e1..a3ac625 100644 (file)
@@ -1,7 +1,7 @@
-# $OpenBSD: Makefile,v 1.2 2017/06/03 15:54:09 schwarze Exp $
+# $OpenBSD: Makefile,v 1.3 2022/04/28 10:17:38 schwarze Exp $
 
-REGRESS_TARGETS        = args blank
-LINT_TARGETS   = args blank
+REGRESS_TARGETS        = args blank nest
+LINT_TARGETS   = args blank nest
 
 # groff-1.22.3 defect:
 # - A blank line in next line scope causes a blank line.
diff --git a/regress/usr.bin/mandoc/man/B/nest.in b/regress/usr.bin/mandoc/man/B/nest.in
new file mode 100644 (file)
index 0000000..3edf207
--- /dev/null
@@ -0,0 +1,20 @@
+.\" $OpenBSD: nest.in,v 1.1 2022/04/28 10:17:38 schwarze Exp $
+.TH B-ARGS 1 "April 28, 2022"
+.SH NAME
+B-nest \- nested next-line scopes
+.SH DESCRIPTION
+initial text
+.B
+.SM
+bold text
+back to roman
+.B
+.SM
+.IR italic roman
+.B
+.SM
+.SH
+.B
+.B
+.RI section header
+final text
diff --git a/regress/usr.bin/mandoc/man/B/nest.out_ascii b/regress/usr.bin/mandoc/man/B/nest.out_ascii
new file mode 100644 (file)
index 0000000..82ae6a7
--- /dev/null
@@ -0,0 +1,12 @@
+B-ARGS(1)                   General Commands Manual                  B-ARGS(1)
+
+N\bNA\bAM\bME\bE
+       B-nest - nested next-line scopes
+
+D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
+       initial text b\bbo\bol\bld\bd t\bte\bex\bxt\bt back to roman _\bi_\bt_\ba_\bl_\bi_\bcroman
+
+section_\bh_\be_\ba_\bd_\be_\br
+       final text
+
+OpenBSD                         April 28, 2022                       B-ARGS(1)
diff --git a/regress/usr.bin/mandoc/man/B/nest.out_lint b/regress/usr.bin/mandoc/man/B/nest.out_lint
new file mode 100644 (file)
index 0000000..dd6ba7b
--- /dev/null
@@ -0,0 +1,6 @@
+mandoc: nest.in:12:2: WARNING: line scope broken: IR breaks SM
+mandoc: nest.in:11:2: WARNING: line scope broken: IR breaks B
+mandoc: nest.in:15:2: WARNING: line scope broken: SH breaks SM
+mandoc: nest.in:14:2: WARNING: line scope broken: SH breaks B
+mandoc: nest.in:18:2: WARNING: line scope broken: RI breaks B
+mandoc: nest.in:17:2: WARNING: line scope broken: RI breaks B
index 934f2b3..0a522b6 100644 (file)
@@ -1,7 +1,7 @@
-/*     $OpenBSD: man.c,v 1.135 2019/01/05 00:36:46 schwarze Exp $ */
+/* $OpenBSD: man.c,v 1.136 2022/04/28 10:17:37 schwarze Exp $ */
 /*
+ * Copyright (c) 2013-2015,2017-2019,2022 Ingo Schwarze <schwarze@openbsd.org>
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
  * Copyright (c) 2011 Joerg Sonnenberger <joerg@netbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -68,6 +68,9 @@ man_hasc(char *start)
        return (ep - cp) % 2 ? NULL : ep;
 }
 
+/*
+ * Rewind all open next-line scopes.
+ */
 void
 man_descope(struct roff_man *man, int line, int offs, char *start)
 {
@@ -272,6 +275,10 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs)
        return 1;
 }
 
+/*
+ * Rewind open next-line scopes
+ * unless the tok request or macro is allowed inside them.
+ */
 void
 man_breakscope(struct roff_man *man, int tok)
 {
@@ -292,10 +299,15 @@ man_breakscope(struct roff_man *man, int tok)
                    (man_macro(n->tok)->flags & (MAN_NSCOPED | MAN_ESCOPED))
                     == MAN_NSCOPED)
                        n = n->parent;
-
-               mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos,
-                   "%s breaks %s", roff_name[tok], roff_name[n->tok]);
-
+               for (;;) {
+                       mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos,
+                           "%s breaks %s", roff_name[tok], roff_name[n->tok]);
+                       if (n->parent->type != ROFFT_ELEM ||
+                           (man_macro(n->parent->tok)->flags &
+                            MAN_ESCOPED) == 0)
+                               break;
+                       n = n->parent;
+               }
                roff_node_delete(man, n);
                man->flags &= ~MAN_ELINE;
        }