aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@openbsd.org>2020-03-13 15:32:28 +0000
committerIngo Schwarze <schwarze@openbsd.org>2020-03-13 15:32:28 +0000
commitf10f0fe3970de778125a29d73e65e63f32c138e1 (patch)
tree757b21a5fff7cfdab542f40f9aa0e1306287415c
parent62450180320c529d836c4c25672879a7ce53221a (diff)
downloadmandoc-f10f0fe3970de778125a29d73e65e63f32c138e1.tar.gz
mandoc-f10f0fe3970de778125a29d73e65e63f32c138e1.tar.zst
mandoc-f10f0fe3970de778125a29d73e65e63f32c138e1.zip
Split tagging into a validation part including prioritization
in tag.{h,c} and {mdoc,man}_validate.c and into a formatting part including command line argument checking in term_tag.{h,c}, html.c, and {mdoc|man}_{term|html}.c. Immediate functional benefits include: * Improved prioritization of automatic tags for .Em and .Sy. * Avoiding bogus automatic tags when .Em, .Fn, or .Sy are explicitly tagged. * Explicit tagging of .Er and .Fl now works in HTML output. * Automatic tagging of .IP and .TP now works in HTML output. But mainly, this patch provides clean earth to build further improvements on. Technical changes: * Main program: Write a tag file for ASCII and UTF-8 output only. * All formatters: There is no more need to delay writing the tags. * mdoc(7)+man(7) formatters: No more need for elaborate syntax tree inspection. * HTML formatter: If available, use the "string" attribute as the tag. * HTML formatter: New function to write permalinks, to reduce code duplication. Style cleanup in the vicinity while here: * mdoc(7) terminal formatter: To set up bold font for children, defer to termp_bold_pre() rather than calling term_fontpush() manually. * mdoc(7) terminal formatter: Garbage collect some duplicate functions. * mdoc(7) HTML formatter: Unify <code> handling, delete redundant functions. * Where possible, use switch statements rather than if cascades. * Get rid of some more Yoda notation. The necessity for such changes was first discussed with kn@, but i didn't bother him with a request to review the resulting -673/+782 line patch.
-rw-r--r--Makefile21
-rw-r--r--Makefile.depend15
-rw-r--r--html.c78
-rw-r--r--html.h9
-rw-r--r--main.c37
-rw-r--r--man_html.c16
-rw-r--r--man_term.c87
-rw-r--r--man_validate.c108
-rw-r--r--mandoc_headers.377
-rw-r--r--mandoc_html.3137
-rw-r--r--mdoc_html.c200
-rw-r--r--mdoc_term.c284
-rw-r--r--mdoc_validate.c200
-rw-r--r--read.c13
-rwxr-xr-xregress/copyless14
-rw-r--r--regress/man/IP/Makefile7
-rw-r--r--regress/man/IP/empty.in4
-rw-r--r--regress/man/IP/empty.out_ascii4
-rw-r--r--regress/man/IP/empty.out_html18
-rw-r--r--regress/man/IP/empty.out_lint4
-rw-r--r--regress/man/IP/empty.out_tag3
-rw-r--r--regress/man/IP/literal.out_html8
-rw-r--r--regress/man/IP/tag.in18
-rw-r--r--regress/man/IP/tag.out_ascii23
-rw-r--r--regress/man/IP/tag.out_html10
-rw-r--r--regress/man/IP/tag.out_tag2
-rw-r--r--regress/man/TP/Makefile7
-rw-r--r--regress/man/TP/literal.out_html4
-rw-r--r--regress/man/TP/tag.in31
-rw-r--r--regress/man/TP/tag.out_ascii29
-rw-r--r--regress/man/TP/tag.out_html16
-rw-r--r--regress/man/TP/tag.out_tag3
-rw-r--r--regress/man/TP/vert.out_html4
-rw-r--r--regress/mdoc/Cm/Makefile6
-rw-r--r--regress/mdoc/Cm/tag.in21
-rw-r--r--regress/mdoc/Cm/tag.out_ascii17
-rw-r--r--regress/mdoc/Cm/tag.out_html9
-rw-r--r--regress/mdoc/Cm/tag.out_markdown23
-rw-r--r--regress/mdoc/Cm/tag.out_tag4
-rw-r--r--regress/mdoc/Dv/Makefile6
-rw-r--r--regress/mdoc/Dv/tag.in21
-rw-r--r--regress/mdoc/Dv/tag.out_ascii17
-rw-r--r--regress/mdoc/Dv/tag.out_html9
-rw-r--r--regress/mdoc/Dv/tag.out_markdown23
-rw-r--r--regress/mdoc/Dv/tag.out_tag4
-rw-r--r--regress/mdoc/Em/Makefile6
-rw-r--r--regress/mdoc/Em/tag.in23
-rw-r--r--regress/mdoc/Em/tag.out_ascii17
-rw-r--r--regress/mdoc/Em/tag.out_html10
-rw-r--r--regress/mdoc/Em/tag.out_markdown25
-rw-r--r--regress/mdoc/Em/tag.out_tag5
-rw-r--r--regress/mdoc/Er/Makefile6
-rw-r--r--regress/mdoc/Er/tag.in21
-rw-r--r--regress/mdoc/Er/tag.out_ascii16
-rw-r--r--regress/mdoc/Er/tag.out_html12
-rw-r--r--regress/mdoc/Er/tag.out_markdown25
-rw-r--r--regress/mdoc/Er/tag.out_tag2
-rw-r--r--regress/mdoc/Ev/Makefile6
-rw-r--r--regress/mdoc/Ev/tag.in21
-rw-r--r--regress/mdoc/Ev/tag.out_ascii17
-rw-r--r--regress/mdoc/Ev/tag.out_html9
-rw-r--r--regress/mdoc/Ev/tag.out_markdown23
-rw-r--r--regress/mdoc/Ev/tag.out_tag4
-rw-r--r--regress/mdoc/Fl/Makefile7
-rw-r--r--regress/mdoc/Fl/tag.in21
-rw-r--r--regress/mdoc/Fl/tag.out_ascii17
-rw-r--r--regress/mdoc/Fl/tag.out_html8
-rw-r--r--regress/mdoc/Fl/tag.out_markdown23
-rw-r--r--regress/mdoc/Fl/tag.out_tag4
-rw-r--r--regress/mdoc/Fo/Makefile6
-rw-r--r--regress/mdoc/Fo/tag.in29
-rw-r--r--regress/mdoc/Fo/tag.out_ascii17
-rw-r--r--regress/mdoc/Fo/tag.out_html9
-rw-r--r--regress/mdoc/Fo/tag.out_markdown27
-rw-r--r--regress/mdoc/Fo/tag.out_tag4
-rw-r--r--regress/mdoc/Ic/Makefile6
-rw-r--r--regress/mdoc/Ic/tag.in21
-rw-r--r--regress/mdoc/Ic/tag.out_ascii17
-rw-r--r--regress/mdoc/Ic/tag.out_html9
-rw-r--r--regress/mdoc/Ic/tag.out_markdown23
-rw-r--r--regress/mdoc/Ic/tag.out_tag4
-rw-r--r--regress/mdoc/Li/Makefile6
-rw-r--r--regress/mdoc/Li/tag.in21
-rw-r--r--regress/mdoc/Li/tag.out_ascii17
-rw-r--r--regress/mdoc/Li/tag.out_html9
-rw-r--r--regress/mdoc/Li/tag.out_markdown23
-rw-r--r--regress/mdoc/Li/tag.out_tag4
-rw-r--r--regress/mdoc/Makefile4
-rw-r--r--regress/mdoc/Ms/Makefile6
-rw-r--r--regress/mdoc/Ms/tag.in21
-rw-r--r--regress/mdoc/Ms/tag.out_ascii17
-rw-r--r--regress/mdoc/Ms/tag.out_html9
-rw-r--r--regress/mdoc/Ms/tag.out_markdown23
-rw-r--r--regress/mdoc/Ms/tag.out_tag4
-rw-r--r--regress/mdoc/No/Makefile6
-rw-r--r--regress/mdoc/No/punct.out_lint1
-rw-r--r--regress/mdoc/No/tag.in21
-rw-r--r--regress/mdoc/No/tag.out_ascii17
-rw-r--r--regress/mdoc/No/tag.out_html9
-rw-r--r--regress/mdoc/No/tag.out_markdown23
-rw-r--r--regress/mdoc/No/tag.out_tag4
-rw-r--r--regress/mdoc/Sy/Makefile6
-rw-r--r--regress/mdoc/Sy/tag.in23
-rw-r--r--regress/mdoc/Sy/tag.out_ascii17
-rw-r--r--regress/mdoc/Sy/tag.out_html10
-rw-r--r--regress/mdoc/Sy/tag.out_markdown25
-rw-r--r--regress/mdoc/Sy/tag.out_tag5
-rw-r--r--regress/mdoc/Tg/Makefile8
-rw-r--r--regress/mdoc/Tg/warn.in34
-rw-r--r--regress/mdoc/Tg/warn.out_ascii19
-rw-r--r--regress/mdoc/Tg/warn.out_html11
-rw-r--r--regress/mdoc/Tg/warn.out_lint6
-rw-r--r--regress/mdoc/Tg/warn.out_markdown30
-rw-r--r--regress/mdoc/Tg/warn.out_tag5
-rwxr-xr-xregress/regress.pl90
-rw-r--r--regress/regress.pl.117
-rw-r--r--tag.c280
-rw-r--r--tag.h29
-rw-r--r--term_tag.c206
-rw-r--r--term_tag.h35
-rw-r--r--tree.c12
121 files changed, 2347 insertions, 917 deletions
diff --git a/Makefile b/Makefile
index f4e29540..e0b9a66b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
-# $Id: Makefile,v 1.530 2019/03/06 16:08:41 schwarze Exp $
+# $Id: Makefile,v 1.531 2020/03/13 15:32:28 schwarze Exp $
#
+# Copyright (c) 2011, 2013-2020 Ingo Schwarze <schwarze@openbsd.org>
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
-# Copyright (c) 2011, 2013-2019 Ingo Schwarze <schwarze@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -134,6 +134,7 @@ SRCS = arch.c \
term_ascii.c \
term_ps.c \
term_tab.c \
+ term_tag.c \
tree.c
DISTFILES = INSTALL \
@@ -209,6 +210,7 @@ DISTFILES = INSTALL \
tbl_int.h \
tbl_parse.h \
term.h \
+ term_tag.h \
$(SRCS) \
$(TESTSRCS)
@@ -245,7 +247,8 @@ LIBMANDOC_OBJS = $(LIBMAN_OBJS) \
mandoc_xr.o \
msec.o \
preconv.o \
- read.o
+ read.o \
+ tag.o
COMPAT_OBJS = compat_err.o \
compat_fts.o \
@@ -280,6 +283,7 @@ MANDOC_TERM_OBJS = eqn_term.o \
term_ascii.o \
term_ps.o \
term_tab.o \
+ term_tag.o \
tbl_term.o
DBM_OBJS = dbm.o \
@@ -302,7 +306,6 @@ MAIN_OBJS = $(MANDOC_HTML_OBJS) \
mdoc_man.o \
mdoc_markdown.o \
out.o \
- tag.o \
tree.o
CGI_OBJS = $(MANDOC_HTML_OBJS) \
@@ -313,8 +316,7 @@ CGI_OBJS = $(MANDOC_HTML_OBJS) \
MANDOCD_OBJS = $(MANDOC_HTML_OBJS) \
$(MANDOC_TERM_OBJS) \
mandocd.o \
- out.o \
- tag.o
+ out.o
DEMANDOC_OBJS = demandoc.o
@@ -393,7 +395,7 @@ distclean: clean
clean:
rm -f libmandoc.a $(LIBMANDOC_OBJS) $(COMPAT_OBJS)
- rm -f mandoc $(MAIN_OBJS)
+ rm -f mandoc man $(MAIN_OBJS)
rm -f man.cgi $(CGI_OBJS)
rm -f mandocd catman catman.o $(MANDOCD_OBJS)
rm -f demandoc $(DEMANDOC_OBJS)
@@ -501,7 +503,7 @@ uninstall:
rm -f $(DESTDIR)$(INCLUDEDIR)/tbl.h
[ ! -e $(DESTDIR)$(INCLUDEDIR) ] || rmdir $(DESTDIR)$(INCLUDEDIR)
-regress: all
+regress: all man
cd regress && ./regress.pl
regress-clean:
@@ -517,6 +519,9 @@ libmandoc.a: $(COMPAT_OBJS) $(LIBMANDOC_OBJS)
mandoc: $(MAIN_OBJS) libmandoc.a
$(CC) -o $@ $(LDFLAGS) $(MAIN_OBJS) libmandoc.a $(LDADD)
+man: mandoc
+ $(LN) mandoc man
+
man.cgi: $(CGI_OBJS) libmandoc.a
$(CC) $(STATIC) -o $@ $(LDFLAGS) $(CGI_OBJS) libmandoc.a $(LDADD)
diff --git a/Makefile.depend b/Makefile.depend
index 3540aeda..eb8606d5 100644
--- a/Makefile.depend
+++ b/Makefile.depend
@@ -33,12 +33,12 @@ eqn_html.o: eqn_html.c config.h mandoc.h roff.h eqn.h out.h html.h
eqn_term.o: eqn_term.c config.h eqn.h out.h term.h
html.o: html.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h out.h html.h manconf.h main.h
lib.o: lib.c config.h roff.h libmdoc.h lib.in
-main.o: main.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h man.h mandoc_parse.h tag.h main.h manconf.h mansearch.h
+main.o: main.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h man.h mandoc_parse.h term_tag.h main.h manconf.h mansearch.h
man.o: man.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
man_html.o: man_html.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h html.h main.h
man_macro.o: man_macro.c config.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
-man_term.o: man_term.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h term.h tag.h main.h
-man_validate.o: man_validate.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
+man_term.o: man_term.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h term.h term_tag.h main.h
+man_validate.o: man_validate.c config.h mandoc_aux.h mandoc.h roff.h tag.h man.h libmandoc.h roff_int.h libman.h
mandoc.o: mandoc.c config.h mandoc_aux.h mandoc.h roff.h libmandoc.h roff_int.h
mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h
mandoc_msg.o: mandoc_msg.c config.h mandoc.h
@@ -55,19 +55,19 @@ mdoc_macro.o: mdoc_macro.c config.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.
mdoc_man.o: mdoc_man.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h out.h main.h
mdoc_markdown.o: mdoc_markdown.c mandoc_aux.h mandoc.h roff.h mdoc.h main.h
mdoc_state.o: mdoc_state.c mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
-mdoc_term.o: mdoc_term.c config.h mandoc_aux.h roff.h mdoc.h out.h term.h tag.h main.h
-mdoc_validate.o: mdoc_validate.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
+mdoc_term.o: mdoc_term.c config.h mandoc_aux.h roff.h mdoc.h out.h term.h term_tag.h main.h
+mdoc_validate.o: mdoc_validate.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h tag.h mdoc.h libmandoc.h roff_int.h libmdoc.h
msec.o: msec.c config.h mandoc.h libmandoc.h msec.in
out.o: out.c config.h mandoc_aux.h tbl.h out.h
preconv.o: preconv.c config.h mandoc.h roff.h mandoc_parse.h libmandoc.h
-read.o: read.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h libmandoc.h roff_int.h
+read.o: read.c config.h mandoc_aux.h mandoc.h roff.h tag.h mdoc.h man.h mandoc_parse.h libmandoc.h roff_int.h
roff.o: roff.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mandoc_parse.h libmandoc.h roff_int.h tbl_parse.h eqn_parse.h predefs.in
roff_html.o: roff_html.c mandoc.h roff.h out.h html.h
roff_term.o: roff_term.c mandoc.h roff.h out.h term.h
roff_validate.o: roff_validate.c mandoc.h roff.h libmandoc.h roff_int.h
soelim.o: soelim.c config.h compat_stringlist.h
st.o: st.c config.h mandoc.h roff.h libmdoc.h
-tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h tag.h
+tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h roff.h tag.h
tbl.o: tbl.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_parse.h tbl_int.h
tbl_data.o: tbl_data.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_int.h
tbl_html.o: tbl_html.c config.h mandoc.h roff.h tbl.h out.h html.h
@@ -78,4 +78,5 @@ term.o: term.c config.h mandoc.h mandoc_aux.h out.h term.h main.h
term_ascii.o: term_ascii.c config.h mandoc.h mandoc_aux.h out.h term.h manconf.h main.h
term_ps.o: term_ps.c config.h mandoc_aux.h out.h term.h manconf.h main.h
term_tab.o: term_tab.c mandoc_aux.h out.h term.h
+term_tag.o: term_tag.c config.h mandoc.h roff.h tag.h term_tag.h
tree.o: tree.c config.h mandoc.h roff.h mdoc.h man.h tbl.h eqn.h main.h
diff --git a/html.c b/html.c
index babe237c..769acce8 100644
--- a/html.c
+++ b/html.c
@@ -1,7 +1,7 @@
-/* $Id: html.c,v 1.263 2020/02/27 22:28:13 schwarze Exp $ */
+/* $Id: html.c,v 1.264 2020/03/13 15:32:28 schwarze Exp $ */
/*
- * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,9 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Common functions for mandoc(1) HTML formatters.
+ * For use by individual formatters and by the main program.
*/
#include "config.h"
@@ -321,6 +324,18 @@ html_fillmode(struct html *h, enum roff_tok want)
return had;
}
+/*
+ * Allocate a string to be used for the "id=" attribute of an HTML
+ * element and/or as a segment identifier for a URI in an <a> element.
+ * The function may fail and return NULL if the node lacks text data
+ * to create the attribute from.
+ * If the "unique" argument is 0, the caller is responsible for
+ * free(3)ing the returned string after using it.
+ * If the "unique" argument is non-zero, the "id_unique" ohash table
+ * is used for de-duplication and owns the returned string, so the
+ * caller must not free(3) it. In this case, it will be freed
+ * automatically by html_reset() or html_free().
+ */
char *
html_make_id(const struct roff_node *n, int unique)
{
@@ -329,14 +344,30 @@ html_make_id(const struct roff_node *n, int unique)
unsigned int slot;
int suffix;
- for (nch = n->child; nch != NULL; nch = nch->next)
- if (nch->type != ROFFT_TEXT)
- return NULL;
-
- buf = NULL;
- deroff(&buf, n);
- if (buf == NULL)
- return NULL;
+ if (n->string != NULL)
+ buf = mandoc_strdup(n->string);
+ else {
+ switch (n->tok) {
+ case MDOC_Sh:
+ case MDOC_Ss:
+ case MDOC_Sx:
+ case MAN_SH:
+ case MAN_SS:
+ for (nch = n->child; nch != NULL; nch = nch->next)
+ if (nch->type != ROFFT_TEXT)
+ return NULL;
+ buf = NULL;
+ deroff(&buf, n);
+ if (buf == NULL)
+ return NULL;
+ break;
+ default:
+ if (n->child->type != ROFFT_TEXT)
+ return NULL;
+ buf = mandoc_strdup(n->child->string);
+ break;
+ }
+ }
/*
* In ID attributes, only use ASCII characters that are
@@ -736,6 +767,33 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
return t;
}
+/*
+ * Print an element with an optional "id=" attribute.
+ * If there is an "id=" attribute, also add a permalink:
+ * outside if it's a phrasing element, or inside otherwise.
+ */
+struct tag *
+print_otag_id(struct html *h, enum htmltag elemtype, const char *cattr,
+ struct roff_node *n)
+{
+ struct tag *ret, *t;
+ const char *id;
+
+ ret = NULL;
+ id = NULL;
+ if (n->flags & NODE_ID)
+ id = html_make_id(n, 1);
+ if (id != NULL && htmltags[elemtype].flags & HTML_INPHRASE)
+ ret = print_otag(h, TAG_A, "chR", "permalink", id);
+ t = print_otag(h, elemtype, "ci", cattr, id);
+ if (ret == NULL) {
+ ret = t;
+ if (id != NULL)
+ print_otag(h, TAG_A, "chR", "permalink", id);
+ }
+ return ret;
+}
+
static void
print_ctag(struct html *h, struct tag *tag)
{
diff --git a/html.h b/html.h
index ac463b97..d9e65912 100644
--- a/html.h
+++ b/html.h
@@ -1,7 +1,7 @@
-/* $Id: html.h,v 1.106 2020/01/19 18:02:00 schwarze Exp $ */
+/* $Id: html.h,v 1.107 2020/03/13 15:32:28 schwarze Exp $ */
/*
+ * Copyright (c) 2017, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2017, 2018, 2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,9 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internal interfaces for mandoc(1) HTML formatters.
+ * For use by the individual HTML formatters only.
*/
enum htmltag {
@@ -120,6 +123,8 @@ void print_gen_comment(struct html *, struct roff_node *);
void print_gen_decls(struct html *);
void print_gen_head(struct html *);
struct tag *print_otag(struct html *, enum htmltag, const char *, ...);
+struct tag *print_otag_id(struct html *, enum htmltag, const char *,
+ struct roff_node *);
void print_tagq(struct html *, const struct tag *);
void print_stagq(struct html *, const struct tag *);
void print_text(struct html *, const char *);
diff --git a/main.c b/main.c
index 862eb9bf..8c3ded9b 100644
--- a/main.c
+++ b/main.c
@@ -1,7 +1,7 @@
-/* $Id: main.c,v 1.344 2020/02/24 21:16:31 schwarze Exp $ */
+/* $Id: main.c,v 1.345 2020/03/13 15:32:28 schwarze Exp $ */
/*
- * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2012, 2014-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -15,6 +15,8 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Main program for mandoc(1), man(1), apropos(1), whatis(1), and help(1).
*/
#include "config.h"
@@ -52,7 +54,7 @@
#include "mdoc.h"
#include "man.h"
#include "mandoc_parse.h"
-#include "tag.h"
+#include "term_tag.h"
#include "main.h"
#include "manconf.h"
#include "mansearch.h"
@@ -598,7 +600,6 @@ main(int argc, char *argv[])
* readable: Maybe it won't be needed after all.
*/
startdir = open(".", O_RDONLY | O_DIRECTORY);
-
for (i = 0; i < ressz; i++) {
process_onefile(mp, res + i, startdir, &outst, &conf);
if (outst.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK)
@@ -608,7 +609,6 @@ main(int argc, char *argv[])
(void)fchdir(startdir);
close(startdir);
}
-
if (outst.outdata != NULL) {
switch (outst.outtype) {
case OUTT_HTML:
@@ -617,6 +617,7 @@ main(int argc, char *argv[])
case OUTT_UTF8:
case OUTT_LOCALE:
case OUTT_ASCII:
+ term_tag_finish();
ascii_free(outst.outdata);
break;
case OUTT_PDF:
@@ -638,9 +639,8 @@ out:
if (outst.tag_files != NULL) {
fclose(stdout);
- tag_write();
run_pager(outst.tag_files);
- tag_unlink();
+ term_tag_unlink();
} else if (outst.had_output && outst.outtype != OUTT_LINT)
mandoc_msg_summary();
@@ -831,15 +831,16 @@ process_onefile(struct mparse *mp, struct manpage *resp, int startdir,
} else
fd = STDIN_FILENO;
- if (outst->use_pager) {
- outst->use_pager = 0;
- outst->tag_files = tag_init(conf->output.tag);
- }
-
- if (outst->had_output && outst->outtype <= OUTT_UTF8) {
- if (outst->outdata == NULL)
- outdata_alloc(outst, &conf->output);
- terminal_sepline(outst->outdata);
+ if (outst->outtype <= OUTT_UTF8) {
+ if (outst->use_pager) {
+ outst->use_pager = 0;
+ outst->tag_files = term_tag_init(conf->output.tag);
+ }
+ if (outst->had_output) {
+ if (outst->outdata == NULL)
+ outdata_alloc(outst, &conf->output);
+ terminal_sepline(outst->outdata);
+ }
}
if (resp->form == FORM_SRC)
@@ -853,7 +854,7 @@ process_onefile(struct mparse *mp, struct manpage *resp, int startdir,
if (outst->tag_files != NULL) {
mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s: %s",
outst->tag_files->ofn, strerror(errno));
- tag_unlink();
+ term_tag_unlink();
outst->tag_files = NULL;
} else
mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s",
@@ -1278,7 +1279,7 @@ spawn_pager(struct tag_files *tag_files)
_exit(mandoc_msg_getrc());
}
close(tag_files->ofd);
- assert(tag_files->tfd == -1);
+ assert(tag_files->tfs == NULL);
/* Do not start the pager before controlling the terminal. */
diff --git a/man_html.c b/man_html.c
index 8d5e03c4..202df2b7 100644
--- a/man_html.c
+++ b/man_html.c
@@ -1,7 +1,7 @@
-/* $Id: man_html.c,v 1.176 2020/02/27 01:43:52 schwarze Exp $ */
+/* $Id: man_html.c,v 1.177 2020/03/13 15:32:28 schwarze Exp $ */
/*
- * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,8 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * HTML formatter for man(7) used by mandoc(1).
*/
#include "config.h"
@@ -310,7 +312,6 @@ static int
man_SH_pre(MAN_ARGS)
{
const char *class;
- char *id;
enum htmltag tag;
if (n->tok == MAN_SH) {
@@ -326,10 +327,8 @@ man_SH_pre(MAN_ARGS)
print_otag(h, TAG_SECTION, "c", class);
break;
case ROFFT_HEAD:
- id = html_make_id(n, 1);
- print_otag(h, tag, "ci", class, id);
- if (id != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
+ n->flags |= NODE_ID;
+ print_otag_id(h, tag, class, n);
break;
case ROFFT_BODY:
break;
@@ -489,7 +488,7 @@ man_IP_pre(MAN_ARGS)
case ROFFT_HEAD:
if (body_elem == TAG_LI)
return 0;
- print_otag(h, TAG_DT, "");
+ print_otag_id(h, TAG_DT, NULL, n);
break;
case ROFFT_BODY:
print_otag(h, body_elem, "");
@@ -497,7 +496,6 @@ man_IP_pre(MAN_ARGS)
default:
abort();
}
-
switch(n->tok) {
case MAN_IP: /* Only print the first header element. */
if (n->child != NULL)
diff --git a/man_term.c b/man_term.c
index 4a78d5c4..b71d6bee 100644
--- a/man_term.c
+++ b/man_term.c
@@ -1,7 +1,7 @@
-/* $Id: man_term.c,v 1.234 2020/02/27 01:43:52 schwarze Exp $ */
+/* $Id: man_term.c,v 1.235 2020/03/13 15:32:28 schwarze Exp $ */
/*
- * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,9 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Plain text formatter for man(7), used by mandoc(1)
+ * for ASCII, UTF-8, PostScript, and PDF output.
*/
#include "config.h"
@@ -32,7 +35,7 @@
#include "man.h"
#include "out.h"
#include "term.h"
-#include "tag.h"
+#include "term_tag.h"
#include "main.h"
#define MAXMARGINS 64 /* maximum number of indented scopes */
@@ -94,8 +97,6 @@ static void post_SY(DECL_ARGS);
static void post_TP(DECL_ARGS);
static void post_UR(DECL_ARGS);
-static void tag_man(struct termp *, struct roff_node *);
-
static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = {
{ NULL, NULL, 0 }, /* TH */
{ pre_SH, post_SH, 0 }, /* SH */
@@ -539,10 +540,8 @@ pre_IP(DECL_ARGS)
case ROFFT_HEAD:
p->tcol->offset = mt->offset;
p->tcol->rmargin = mt->offset + len;
- if (n->child != NULL) {
+ if (n->child != NULL)
print_man_node(p, mt, n->child, meta);
- tag_man(p, n->child);
- }
return 0;
case ROFFT_BODY:
p->tcol->offset = mt->offset + len;
@@ -622,18 +621,6 @@ pre_TP(DECL_ARGS)
while (nn != NULL && (nn->flags & NODE_LINE) == 0)
nn = nn->next;
- if (nn == NULL)
- return 0;
-
- if (nn->type == ROFFT_TEXT)
- tag_man(p, nn);
- else if (nn->child != NULL &&
- nn->child->type == ROFFT_TEXT &&
- (nn->tok == MAN_B || nn->tok == MAN_BI ||
- nn->tok == MAN_BR || nn->tok == MAN_I ||
- nn->tok == MAN_IB || nn->tok == MAN_IR))
- tag_man(p, nn->child);
-
while (nn != NULL) {
print_man_node(p, mt, nn, meta);
nn = nn->next;
@@ -913,6 +900,9 @@ print_man_node(DECL_ARGS)
const struct man_term_act *act;
int c;
+ if (n->flags & NODE_ID)
+ term_tag_write(n, p->line);
+
switch (n->type) {
case ROFFT_TEXT:
/*
@@ -1159,60 +1149,3 @@ print_man_head(struct termp *p, const struct roff_meta *meta)
}
free(title);
}
-
-/*
- * Skip leading whitespace, dashes, backslashes, and font escapes,
- * then create a tag if the first following byte is a letter.
- * Priority is high unless whitespace is present.
- */
-static void
-tag_man(struct termp *p, struct roff_node *n)
-{
- const char *cp, *arg;
- int prio, sz;
-
- assert(n->type == ROFFT_TEXT);
- cp = n->string;
- prio = TAG_STRONG;
- for (;;) {
- switch (*cp) {
- case ' ':
- case '\t':
- prio = TAG_WEAK;
- /* FALLTHROUGH */
- case '-':
- cp++;
- break;
- case '\\':
- cp++;
- switch (mandoc_escape(&cp, &arg, &sz)) {
- case ESCAPE_FONT:
- case ESCAPE_FONTROMAN:
- case ESCAPE_FONTITALIC:
- case ESCAPE_FONTBOLD:
- case ESCAPE_FONTPREV:
- case ESCAPE_FONTBI:
- break;
- case ESCAPE_SPECIAL:
- if (sz != 1)
- return;
- switch (*arg) {
- case '&':
- case '-':
- case 'e':
- break;
- default:
- return;
- }
- break;
- default:
- return;
- }
- break;
- default:
- if (isalpha((unsigned char)*cp))
- tag_put(cp, prio, p->line);
- return;
- }
- }
-}
diff --git a/man_validate.c b/man_validate.c
index 374793e4..0c2ed8a4 100644
--- a/man_validate.c
+++ b/man_validate.c
@@ -1,7 +1,7 @@
-/* $Id: man_validate.c,v 1.150 2020/01/19 16:44:50 schwarze Exp $ */
+/* $Id: man_validate.c,v 1.151 2020/03/13 15:32:28 schwarze Exp $ */
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,8 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Validation module for man(7) syntax trees used by mandoc(1).
*/
#include "config.h"
@@ -32,6 +34,7 @@
#include "mandoc_aux.h"
#include "mandoc.h"
#include "roff.h"
+#include "tag.h"
#include "man.h"
#include "libmandoc.h"
#include "roff_int.h"
@@ -45,6 +48,7 @@ static void check_abort(CHKARGS) __attribute__((__noreturn__));
static void check_par(CHKARGS);
static void check_part(CHKARGS);
static void check_root(CHKARGS);
+static void check_tag(struct roff_node *, struct roff_node *);
static void check_text(CHKARGS);
static void post_AT(CHKARGS);
@@ -54,6 +58,7 @@ static void post_IP(CHKARGS);
static void post_OP(CHKARGS);
static void post_SH(CHKARGS);
static void post_TH(CHKARGS);
+static void post_TP(CHKARGS);
static void post_UC(CHKARGS);
static void post_UR(CHKARGS);
static void post_in(CHKARGS);
@@ -62,8 +67,8 @@ static const v_check man_valids[MAN_MAX - MAN_TH] = {
post_TH, /* TH */
post_SH, /* SH */
post_SH, /* SS */
- NULL, /* TP */
- NULL, /* TQ */
+ post_TP, /* TP */
+ post_TP, /* TQ */
check_abort,/* LP */
check_par, /* PP */
check_abort,/* P */
@@ -201,6 +206,66 @@ check_abort(CHKARGS)
abort();
}
+/*
+ * Skip leading whitespace, dashes, backslashes, and font escapes,
+ * then create a tag if the first following byte is a letter.
+ * Priority is high unless whitespace is present.
+ */
+static void
+check_tag(struct roff_node *n, struct roff_node *nt)
+{
+ const char *cp, *arg;
+ int prio, sz;
+
+ if (nt == NULL || nt->type != ROFFT_TEXT)
+ return;
+
+ cp = nt->string;
+ prio = TAG_STRONG;
+ for (;;) {
+ switch (*cp) {
+ case ' ':
+ case '\t':
+ prio = TAG_WEAK;
+ /* FALLTHROUGH */
+ case '-':
+ cp++;
+ break;
+ case '\\':
+ cp++;
+ switch (mandoc_escape(&cp, &arg, &sz)) {
+ case ESCAPE_FONT:
+ case ESCAPE_FONTBOLD:
+ case ESCAPE_FONTITALIC:
+ case ESCAPE_FONTBI:
+ case ESCAPE_FONTROMAN:
+ case ESCAPE_FONTCW:
+ case ESCAPE_FONTPREV:
+ case ESCAPE_IGNORE:
+ break;
+ case ESCAPE_SPECIAL:
+ if (sz != 1)
+ return;
+ switch (*arg) {
+ case '-':
+ case 'e':
+ break;
+ default:
+ return;
+ }
+ break;
+ default:
+ return;
+ }
+ break;
+ default:
+ if (isalpha((unsigned char)*cp))
+ tag_put(cp, prio, n);
+ return;
+ }
+ }
+}
+
static void
check_text(CHKARGS)
{
@@ -332,12 +397,14 @@ check_par(CHKARGS)
static void
post_IP(CHKARGS)
{
-
switch (n->type) {
case ROFFT_BLOCK:
if (n->head->child == NULL && n->body->child == NULL)
roff_node_delete(man, n);
break;
+ case ROFFT_HEAD:
+ check_tag(n, n->child);
+ break;
case ROFFT_BODY:
if (n->parent->head->child == NULL && n->child == NULL)
mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos,
@@ -348,6 +415,37 @@ post_IP(CHKARGS)
}
}
+/*
+ * The first next-line element in the head is the tag.
+ * If that's a font macro, use its first child instead.
+ */
+static void
+post_TP(CHKARGS)
+{
+ struct roff_node *nt;
+
+ if (n->type != ROFFT_HEAD || (nt = n->child) == NULL)
+ return;
+
+ while ((nt->flags & NODE_LINE) == 0)
+ if ((nt = nt->next) == NULL)
+ return;
+
+ switch (nt->tok) {
+ case MAN_B:
+ case MAN_BI:
+ case MAN_BR:
+ case MAN_I:
+ case MAN_IB:
+ case MAN_IR:
+ nt = nt->child;
+ break;
+ default:
+ break;
+ }
+ check_tag(n, nt);
+}
+
static void
post_TH(CHKARGS)
{
diff --git a/mandoc_headers.3 b/mandoc_headers.3
index 1632b07c..36a8a58d 100644
--- a/mandoc_headers.3
+++ b/mandoc_headers.3
@@ -1,6 +1,6 @@
-.\" $Id: mandoc_headers.3,v 1.32 2020/01/20 10:37:15 schwarze Exp $
+.\" $Id: mandoc_headers.3,v 1.33 2020/03/13 15:32:28 schwarze Exp $
.\"
-.\" Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2014-2020 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,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 20 2020 $
+.Dd $Mdocdate: March 13 2020 $
.Dt MANDOC_HEADERS 3
.Os
.Sh NAME
@@ -232,6 +232,30 @@ and the functions
.Fn mandoc_xr_get ,
and
.Fn mandoc_xr_free .
+.It Qq Pa tag.h
+Internal interfaces to tag syntax tree nodes,
+for use by validation modules only.
+.Pp
+Requires
+.In limits.h
+for
+.Dv INT_MAX .
+.Pp
+Provides the functions
+.Fn tag_alloc ,
+.Fn tag_put ,
+.Fn tag_check ,
+and
+.Fn tag_free
+and some
+.Dv TAG_*
+constants.
+.Pp
+Uses the type
+.Vt struct roff_node
+from
+.Qq Pa roff.h
+as an opaque type for function prototypes.
.El
.Pp
The following two require
@@ -587,6 +611,33 @@ When this header is included, the same file should not include
.Qq Pa html.h
or
.Qq Pa mansearch.h .
+.It Qq Pa tag_term.h
+Requires
+.In sys/types.h
+for
+.Vt size_t
+and
+.In stdio.h
+for
+.Vt FILE .
+.Pp
+Provides an interface to generate
+.Xr ctags 1
+files for the
+.Ic :t
+functionality mentioned in
+.Xr man 1 .
+.Pp
+Uses the type
+.Vt struct roff_node
+from
+.Qq Pa roff.h
+as an opaque type for function prototypes.
+.Pp
+When this header is included, the same file should not include
+.Qq Pa html.h
+or
+.Qq Pa mansearch.h .
.It Qq Pa html.h
Requires
.In sys/types.h
@@ -629,25 +680,10 @@ from
as opaque types for function prototypes.
.Pp
When this header is included, the same file should not include
-.Qq Pa term.h
+.Qq Pa term.h ,
+.Qq Pa tab_term.h ,
or
.Qq Pa mansearch.h .
-.It Qq Pa tag.h
-Requires
-.In sys/types.h
-for
-.Vt size_t
-and
-.In limits.h
-for
-.Dv INT_MAX .
-.Pp
-Provides an interface to generate
-.Xr ctags 1
-files for the
-.Ic :t
-functionality mentioned in
-.Xr man 1 .
.It Qq Pa main.h
Provides the top level steering functions for all formatters.
.Pp
@@ -700,6 +736,7 @@ as an opaque type for function prototypes.
When this header is included, the same file should not include
.Qq Pa out.h ,
.Qq Pa term.h ,
+.Qq Pa tab_term.h ,
or
.Qq Pa html.h .
.El
diff --git a/mandoc_html.3 b/mandoc_html.3
index 32407574..e3d6f88e 100644
--- a/mandoc_html.3
+++ b/mandoc_html.3
@@ -1,4 +1,4 @@
-.\" $Id: mandoc_html.3,v 1.19 2019/01/11 12:56:43 schwarze Exp $
+.\" $Id: mandoc_html.3,v 1.20 2020/03/13 15:32:28 schwarze Exp $
.\"
.\" Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
.\"
@@ -14,7 +14,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 11 2019 $
+.Dd $Mdocdate: March 13 2020 $
.Dt MANDOC_HTML 3
.Os
.Sh NAME
@@ -53,10 +53,14 @@
.Ft char *
.Fo html_make_id
.Fa "const struct roff_node *n"
+.Fa "int unique"
.Fc
-.Ft int
-.Fo html_strlen
-.Fa "const char *cp"
+.Ft struct tag *
+.Fo print_otag_id
+.Fa "struct html *h"
+.Fa "enum htmltag tag"
+.Fa "const char *cattr"
+.Fa "struct roff_node *n"
.Fc
.Sh DESCRIPTION
The mandoc HTML formatter is not a formal library.
@@ -257,23 +261,77 @@ functions.
.Pp
The function
.Fn html_make_id
-takes a node containing one or more text children
-and returns a newly allocated string containing the concatenation
-of the child strings, with blanks replaced by underscores.
-If the node
+allocates a string to be used for the
+.Cm id
+attribute of an HTML element and/or as a segment identifier for a URI in an
+.Aq Ic A
+element.
+If
.Fa n
-contains any non-text child node,
-.Fn html_make_id
-returns
+contains a
+.Fa string
+attribute, it is used; otherwise, child nodes are used.
+If
+.Fa n
+is an
+.Ic \&Sh ,
+.Ic \&Ss ,
+.Ic \&Sx ,
+.Ic SH ,
+or
+.Ic SS
+node, the resulting string is the concatenation of the child strings;
+for other node types, only the first child is used.
+Bytes not permitted in URI-fragment strings are replaced by underscores.
+If any of the children to be used is not a text node,
+no string is generated and
.Dv NULL
-instead.
-The caller is responsible for freeing the returned string.
+is returned instead.
+If the
+.Fa unique
+argument is non-zero, deduplication is performed by appending an
+underscore and a decimal integer, if necessary.
.Pp
The function
-.Fn html_strlen
-counts the number of characters in
-.Fa cp .
-It is used as a crude estimate of the width needed to display a string.
+.Fn print_otag_id
+opens a
+.Fa tag
+element of class
+.Fa cattr
+for the node
+.Fa n .
+If the flag
+.Dv NODE_ID
+is set in
+.Fa n ,
+it attempts to generate an
+.Cm id
+attribute with
+.Fn html_make_id .
+If an
+.Cm id
+attribute is written,
+.Fn print_otag_id
+also adds an
+.Aq Ic A
+element of class
+.Qq permalink :
+outside if
+.Fa n
+generates a phrasing element, or inside otherwise.
+This function is a wrapper around
+.Fn html_make_id
+and
+.Fn print_otag ,
+fixing the
+.Fa unique
+argument to 1 and the
+.Fa fmt
+arguments to
+.Qq chR
+and
+.Qq ci ,
+respectively.
.Pp
The functions
.Fn print_eqn ,
@@ -281,6 +339,49 @@ The functions
and
.Fn print_tblclose
are not yet documented.
+.Sh RETURN VALUES
+The functions
+.Fn print_otag
+and
+.Fn print_otag_id
+return a pointer to a new element on the stack of HTML elements.
+When
+.Fn print_otag_id
+opens two elements, a pointer to the outer one is returned.
+The memory pointed to is owned by the library and is automatically
+.Xr free 3 Ns d
+when
+.Fn print_tagq
+is called on it or when
+.Fn print_stagq
+is called on a parent element.
+.Pp
+The function
+.Fn html_make_id
+returns a newly allocated string or
+.Dv NULL
+if
+.Fa n
+lacks text data to create the attribute from.
+If the
+.Fa unique
+argument is 0, the caller is responsible for
+.Xr free 3 Ns ing
+the returned string after using it.
+If the
+.Fa unique
+argument is non-zero, the
+.Va id_unique
+ohash table is used for de-duplication and owns the returned string.
+In this case, it will be freed automatically by
+.Fn html_reset
+or
+.Fn html_free .
+.Pp
+In case of
+.Xr malloc 3
+failure, these functions do not return but call
+.Xr err 3 .
.Sh FILES
.Bl -tag -width mandoc_aux.c -compact
.It Pa main.h
diff --git a/mdoc_html.c b/mdoc_html.c
index 65bd8e03..8ae8703f 100644
--- a/mdoc_html.c
+++ b/mdoc_html.c
@@ -1,7 +1,7 @@
-/* $Id: mdoc_html.c,v 1.335 2020/02/27 22:28:13 schwarze Exp $ */
+/* $Id: mdoc_html.c,v 1.336 2020/03/13 15:32:28 schwarze Exp $ */
/*
- * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,8 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * HTML formatter for mdoc(7) used by mandoc(1).
*/
#include "config.h"
@@ -47,7 +49,6 @@ struct mdoc_html_act {
void (*post)(MDOC_ARGS);
};
-static char *cond_id(const struct roff_node *);
static void print_mdoc_head(const struct roff_meta *,
struct html *);
static void print_mdoc_node(MDOC_ARGS);
@@ -72,9 +73,8 @@ static void mdoc_bk_post(MDOC_ARGS);
static int mdoc_bk_pre(MDOC_ARGS);
static int mdoc_bl_pre(MDOC_ARGS);
static int mdoc_cd_pre(MDOC_ARGS);
-static int mdoc_cm_pre(MDOC_ARGS);
+static int mdoc_code_pre(MDOC_ARGS);
static int mdoc_d1_pre(MDOC_ARGS);
-static int mdoc_dv_pre(MDOC_ARGS);
static int mdoc_fa_pre(MDOC_ARGS);
static int mdoc_fd_pre(MDOC_ARGS);
static int mdoc_fl_pre(MDOC_ARGS);
@@ -83,20 +83,15 @@ static int mdoc_ft_pre(MDOC_ARGS);
static int mdoc_em_pre(MDOC_ARGS);
static void mdoc_eo_post(MDOC_ARGS);
static int mdoc_eo_pre(MDOC_ARGS);
-static int mdoc_er_pre(MDOC_ARGS);
-static int mdoc_ev_pre(MDOC_ARGS);
static int mdoc_ex_pre(MDOC_ARGS);
static void mdoc_fo_post(MDOC_ARGS);
static int mdoc_fo_pre(MDOC_ARGS);
-static int mdoc_ic_pre(MDOC_ARGS);
static int mdoc_igndelim_pre(MDOC_ARGS);
static int mdoc_in_pre(MDOC_ARGS);
static int mdoc_it_pre(MDOC_ARGS);
static int mdoc_lb_pre(MDOC_ARGS);
-static int mdoc_li_pre(MDOC_ARGS);
static int mdoc_lk_pre(MDOC_ARGS);
static int mdoc_mt_pre(MDOC_ARGS);
-static int mdoc_ms_pre(MDOC_ARGS);
static int mdoc_nd_pre(MDOC_ARGS);
static int mdoc_nm_pre(MDOC_ARGS);
static int mdoc_no_pre(MDOC_ARGS);
@@ -139,19 +134,19 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = {
{mdoc_ap_pre, NULL}, /* Ap */
{mdoc_ar_pre, NULL}, /* Ar */
{mdoc_cd_pre, NULL}, /* Cd */
- {mdoc_cm_pre, NULL}, /* Cm */
- {mdoc_dv_pre, NULL}, /* Dv */
- {mdoc_er_pre, NULL}, /* Er */
- {mdoc_ev_pre, NULL}, /* Ev */
+ {mdoc_code_pre, NULL}, /* Cm */
+ {mdoc_code_pre, NULL}, /* Dv */
+ {mdoc_code_pre, NULL}, /* Er */
+ {mdoc_code_pre, NULL}, /* Ev */
{mdoc_ex_pre, NULL}, /* Ex */
{mdoc_fa_pre, NULL}, /* Fa */
{mdoc_fd_pre, NULL}, /* Fd */
{mdoc_fl_pre, NULL}, /* Fl */
{mdoc_fn_pre, NULL}, /* Fn */
{mdoc_ft_pre, NULL}, /* Ft */
- {mdoc_ic_pre, NULL}, /* Ic */
+ {mdoc_code_pre, NULL}, /* Ic */
{mdoc_in_pre, NULL}, /* In */
- {mdoc_li_pre, NULL}, /* Li */
+ {mdoc_code_pre, NULL}, /* Li */
{mdoc_nd_pre, NULL}, /* Nd */
{mdoc_nm_pre, NULL}, /* Nm */
{mdoc_quote_pre, mdoc_quote_post}, /* Op */
@@ -192,7 +187,7 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = {
{mdoc_em_pre, NULL}, /* Em */
{mdoc_eo_pre, mdoc_eo_post}, /* Eo */
{mdoc_xx_pre, NULL}, /* Fx */
- {mdoc_ms_pre, NULL}, /* Ms */
+ {mdoc_no_pre, NULL}, /* Ms */
{mdoc_no_pre, NULL}, /* No */
{mdoc_ns_pre, NULL}, /* Ns */
{mdoc_xx_pre, NULL}, /* Nx */
@@ -507,20 +502,11 @@ mdoc_root_pre(const struct roff_meta *meta, struct html *h)
return 1;
}
-static char *
-cond_id(const struct roff_node *n)
+static int
+mdoc_code_pre(MDOC_ARGS)
{
- if (n->child != NULL &&
- n->child->type == ROFFT_TEXT &&
- (n->prev == NULL ||
- (n->prev->type == ROFFT_TEXT &&
- strcmp(n->prev->string, "|") == 0)) &&
- (n->parent->tok == MDOC_It ||
- (n->parent->tok == MDOC_Xo &&
- n->parent->parent->prev == NULL &&
- n->parent->parent->parent->tok == MDOC_It)))
- return html_make_id(n, 1);
- return NULL;
+ print_otag_id(h, TAG_CODE, roff_name[n->tok], n);
+ return 1;
}
static int
@@ -583,10 +569,8 @@ mdoc_sh_pre(MDOC_ARGS)
print_otag(h, TAG_SECTION, "c", "Sh");
break;
case ROFFT_HEAD:
- id = html_make_id(n, 1);
- print_otag(h, TAG_H1, "ci", "Sh", id);
- if (id != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
+ n->flags |= NODE_ID;
+ print_otag_id(h, TAG_H1, "Sh", n);
break;
case ROFFT_BODY:
if (n->sec == SEC_AUTHORS)
@@ -601,25 +585,20 @@ mdoc_sh_pre(MDOC_ARGS)
static int
mdoc_ss_pre(MDOC_ARGS)
{
- char *id;
-
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
print_otag(h, TAG_SECTION, "c", "Ss");
- return 1;
+ break;
case ROFFT_HEAD:
+ n->flags |= NODE_ID;
+ print_otag_id(h, TAG_H2, "Ss", n);
break;
case ROFFT_BODY:
- return 1;
+ break;
default:
abort();
}
-
- id = html_make_id(n, 1);
- print_otag(h, TAG_H2, "ci", "Ss", id);
- if (id != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
return 1;
}
@@ -627,12 +606,8 @@ static int
mdoc_fl_pre(MDOC_ARGS)
{
struct roff_node *nn;
- char *id;
-
- if ((id = cond_id(n)) != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
- print_otag(h, TAG_CODE, "ci", "Fl", id);
+ print_otag_id(h, TAG_CODE, "Fl", n);
print_text(h, "\\-");
if (n->child != NULL ||
((nn = roff_node_next(n)) != NULL &&
@@ -644,17 +619,6 @@ mdoc_fl_pre(MDOC_ARGS)
}
static int
-mdoc_cm_pre(MDOC_ARGS)
-{
- char *id;
-
- if ((id = cond_id(n)) != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
- print_otag(h, TAG_CODE, "ci", "Cm", id);
- return 1;
-}
-
-static int
mdoc_nd_pre(MDOC_ARGS)
{
switch (n->type) {
@@ -926,7 +890,7 @@ mdoc_st_pre(MDOC_ARGS)
static int
mdoc_em_pre(MDOC_ARGS)
{
- print_otag(h, TAG_I, "c", "Em");
+ print_otag_id(h, TAG_I, "Em", n);
return 1;
}
@@ -1052,45 +1016,6 @@ mdoc_cd_pre(MDOC_ARGS)
}
static int
-mdoc_dv_pre(MDOC_ARGS)
-{
- char *id;
-
- if ((id = cond_id(n)) != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
- print_otag(h, TAG_CODE, "ci", "Dv", id);
- return 1;
-}
-
-static int
-mdoc_ev_pre(MDOC_ARGS)
-{
- char *id;
-
- if ((id = cond_id(n)) != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
- print_otag(h, TAG_CODE, "ci", "Ev", id);
- return 1;
-}
-
-static int
-mdoc_er_pre(MDOC_ARGS)
-{
- char *id;
-
- id = n->sec == SEC_ERRORS &&
- (n->parent->tok == MDOC_It ||
- (n->parent->tok == MDOC_Bq &&
- n->parent->parent->parent->tok == MDOC_It)) ?
- html_make_id(n, 1) : NULL;
-
- if (id != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
- print_otag(h, TAG_CODE, "ci", "Er", id);
- return 1;
-}
-
-static int
mdoc_fa_pre(MDOC_ARGS)
{
const struct roff_node *nn;
@@ -1222,7 +1147,7 @@ mdoc_fn_pre(MDOC_ARGS)
print_tagq(h, t);
}
- t = print_otag(h, TAG_CODE, "c", "Fn");
+ t = print_otag_id(h, TAG_CODE, "Fn", n);
if (sp)
print_text(h, sp);
@@ -1341,14 +1266,12 @@ mdoc_mt_pre(MDOC_ARGS)
for (n = n->child; n; n = n->next) {
assert(n->type == ROFFT_TEXT);
-
mandoc_asprintf(&cp, "mailto:%s", n->string);
t = print_otag(h, TAG_A, "ch", "Mt", cp);
print_text(h, n->string);
print_tagq(h, t);
free(cp);
}
-
return 0;
}
@@ -1357,30 +1280,30 @@ mdoc_fo_pre(MDOC_ARGS)
{
struct tag *t;
- if (n->type == ROFFT_BODY) {
+ switch (n->type) {
+ case ROFFT_BLOCK:
+ synopsis_pre(h, n);
+ return 1;
+ case ROFFT_HEAD:
+ if (n->child != NULL) {
+ t = print_otag_id(h, TAG_CODE, "Fn", n);
+ print_text(h, n->child->string);
+ print_tagq(h, t);
+ }
+ return 0;
+ case ROFFT_BODY:
h->flags |= HTML_NOSPACE;
print_text(h, "(");
h->flags |= HTML_NOSPACE;
return 1;
- } else if (n->type == ROFFT_BLOCK) {
- synopsis_pre(h, n);
- return 1;
+ default:
+ abort();
}
-
- if (n->child == NULL)
- return 0;
-
- assert(n->child->string);
- t = print_otag(h, TAG_CODE, "c", "Fn");
- print_text(h, n->child->string);
- print_tagq(h, t);
- return 0;
}
static void
mdoc_fo_post(MDOC_ARGS)
{
-
if (n->type != ROFFT_BODY)
return;
h->flags |= HTML_NOSPACE;
@@ -1430,22 +1353,10 @@ mdoc_in_pre(MDOC_ARGS)
assert(n->type == ROFFT_TEXT);
print_text(h, n->string);
}
-
return 0;
}
static int
-mdoc_ic_pre(MDOC_ARGS)
-{
- char *id;
-
- if ((id = cond_id(n)) != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
- print_otag(h, TAG_CODE, "ci", "Ic", id);
- return 1;
-}
-
-static int
mdoc_va_pre(MDOC_ARGS)
{
print_otag(h, TAG_VAR, "c", "Va");
@@ -1455,7 +1366,6 @@ mdoc_va_pre(MDOC_ARGS)
static int
mdoc_ap_pre(MDOC_ARGS)
{
-
h->flags |= HTML_NOSPACE;
print_text(h, "\\(aq");
h->flags |= HTML_NOSPACE;
@@ -1494,20 +1404,8 @@ mdoc_bf_pre(MDOC_ARGS)
}
static int
-mdoc_ms_pre(MDOC_ARGS)
-{
- char *id;
-
- if ((id = cond_id(n)) != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
- print_otag(h, TAG_SPAN, "ci", "Ms", id);
- return 1;
-}
-
-static int
mdoc_igndelim_pre(MDOC_ARGS)
{
-
h->flags |= HTML_IGNDELIM;
return 1;
}
@@ -1515,7 +1413,6 @@ mdoc_igndelim_pre(MDOC_ARGS)
static void
mdoc_pf_post(MDOC_ARGS)
{
-
if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
h->flags |= HTML_NOSPACE;
}
@@ -1544,29 +1441,14 @@ mdoc_rs_pre(MDOC_ARGS)
static int
mdoc_no_pre(MDOC_ARGS)
{
- char *id;
-
- if ((id = cond_id(n)) != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
- print_otag(h, TAG_SPAN, "ci", "No", id);
- return 1;
-}
-
-static int
-mdoc_li_pre(MDOC_ARGS)
-{
- char *id;
-
- if ((id = cond_id(n)) != NULL)
- print_otag(h, TAG_A, "chR", "permalink", id);
- print_otag(h, TAG_CODE, "ci", "Li", id);
+ print_otag_id(h, TAG_SPAN, roff_name[n->tok], n);
return 1;
}
static int
mdoc_sy_pre(MDOC_ARGS)
{
- print_otag(h, TAG_B, "c", "Sy");
+ print_otag_id(h, TAG_B, "Sy", n);
return 1;
}
diff --git a/mdoc_term.c b/mdoc_term.c
index dc03b9aa..ba21097e 100644
--- a/mdoc_term.c
+++ b/mdoc_term.c
@@ -1,7 +1,7 @@
-/* $Id: mdoc_term.c,v 1.378 2020/02/27 21:43:44 schwarze Exp $ */
+/* $Id: mdoc_term.c,v 1.379 2020/03/13 15:32:28 schwarze Exp $ */
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -15,6 +15,9 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Plain text formatter for mdoc(7), used by mandoc(1)
+ * for ASCII, UTF-8, PostScript, and PDF output.
*/
#include "config.h"
@@ -33,7 +36,7 @@
#include "mdoc.h"
#include "out.h"
#include "term.h"
-#include "tag.h"
+#include "term_tag.h"
#include "main.h"
struct termpair {
@@ -89,11 +92,8 @@ static int termp_bf_pre(DECL_ARGS);
static int termp_bk_pre(DECL_ARGS);
static int termp_bl_pre(DECL_ARGS);
static int termp_bold_pre(DECL_ARGS);
-static int termp_cd_pre(DECL_ARGS);
static int termp_d1_pre(DECL_ARGS);
static int termp_eo_pre(DECL_ARGS);
-static int termp_em_pre(DECL_ARGS);
-static int termp_er_pre(DECL_ARGS);
static int termp_ex_pre(DECL_ARGS);
static int termp_fa_pre(DECL_ARGS);
static int termp_fd_pre(DECL_ARGS);
@@ -115,8 +115,6 @@ static int termp_skip_pre(DECL_ARGS);
static int termp_sm_pre(DECL_ARGS);
static int termp_pp_pre(DECL_ARGS);
static int termp_ss_pre(DECL_ARGS);
-static int termp_sy_pre(DECL_ARGS);
-static int termp_tag_pre(DECL_ARGS);
static int termp_under_pre(DECL_ARGS);
static int termp_vt_pre(DECL_ARGS);
static int termp_xr_pre(DECL_ARGS);
@@ -140,11 +138,11 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
{ termp_an_pre, NULL }, /* An */
{ termp_ap_pre, NULL }, /* Ap */
{ termp_under_pre, NULL }, /* Ar */
- { termp_cd_pre, NULL }, /* Cd */
+ { termp_fd_pre, NULL }, /* Cd */
{ termp_bold_pre, NULL }, /* Cm */
{ termp_li_pre, NULL }, /* Dv */
- { termp_er_pre, NULL }, /* Er */
- { termp_tag_pre, NULL }, /* Ev */
+ { NULL, NULL }, /* Er */
+ { NULL, NULL }, /* Ev */
{ termp_ex_pre, NULL }, /* Ex */
{ termp_fa_pre, NULL }, /* Fa */
{ termp_fd_pre, termp_fd_post }, /* Fd */
@@ -191,7 +189,7 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
{ termp_quote_pre, termp_quote_post }, /* Dq */
{ NULL, NULL }, /* Ec */ /* FIXME: no space */
{ NULL, NULL }, /* Ef */
- { termp_em_pre, NULL }, /* Em */
+ { termp_under_pre, NULL }, /* Em */
{ termp_eo_pre, termp_eo_post }, /* Eo */
{ termp_xx_pre, termp_xx_post }, /* Fx */
{ termp_bold_pre, NULL }, /* Ms */
@@ -214,7 +212,7 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
{ termp_quote_pre, termp_quote_post }, /* Sq */
{ termp_sm_pre, NULL }, /* Sm */
{ termp_under_pre, NULL }, /* Sx */
- { termp_sy_pre, NULL }, /* Sy */
+ { termp_bold_pre, NULL }, /* Sy */
{ NULL, NULL }, /* Tn */
{ termp_xx_pre, termp_xx_post }, /* Ux */
{ NULL, NULL }, /* Xc */
@@ -246,8 +244,6 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
{ termp_skip_pre, NULL }, /* Tg */
};
-static int fn_prio = TAG_STRONG;
-
void
terminal_mdoc(void *arg, const struct roff_meta *mdoc)
@@ -300,7 +296,6 @@ terminal_mdoc(void *arg, const struct roff_meta *mdoc)
static void
print_mdoc_nodelist(DECL_ARGS)
{
-
while (n != NULL) {
print_mdoc_node(p, pair, meta, n);
n = n->next;
@@ -341,8 +336,7 @@ print_mdoc_node(DECL_ARGS)
npair.ppair = pair;
if (n->flags & NODE_ID)
- tag_put(n->string == NULL ? n->child->string : n->string,
- TAG_MANUAL, p->line);
+ term_tag_write(n, p->line);
/*
* Keeps only work until the end of a line. If a keep was
@@ -1008,24 +1002,30 @@ termp_nm_pre(DECL_ARGS)
p->flags |= TERMP_HANG;
}
}
-
- term_fontpush(p, TERMFONT_BOLD);
- return 1;
+ return termp_bold_pre(p, pair, meta, n);
}
static void
termp_nm_post(DECL_ARGS)
{
-
- if (n->type == ROFFT_BLOCK) {
+ switch (n->type) {
+ case ROFFT_BLOCK:
p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
- } else if (n->type == ROFFT_HEAD &&
- NULL != n->next && NULL != n->next->child) {
+ break;
+ case ROFFT_HEAD:
+ if (n->next == NULL || n->next->child == NULL)
+ break;
term_flushln(p);
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
p->trailspace = 0;
- } else if (n->type == ROFFT_BODY && n->child != NULL)
- term_flushln(p);
+ break;
+ case ROFFT_BODY:
+ if (n->child != NULL)
+ term_flushln(p);
+ break;
+ default:
+ break;
+ }
}
static int
@@ -1033,7 +1033,6 @@ termp_fl_pre(DECL_ARGS)
{
struct roff_node *nn;
- termp_tag_pre(p, pair, meta, n);
term_fontpush(p, TERMFONT_BOLD);
term_word(p, "\\-");
@@ -1219,24 +1218,22 @@ synopsis_pre(struct termp *p, struct roff_node *n)
static int
termp_vt_pre(DECL_ARGS)
{
-
- if (n->type == ROFFT_ELEM) {
- synopsis_pre(p, n);
- return termp_under_pre(p, pair, meta, n);
- } else if (n->type == ROFFT_BLOCK) {
+ switch (n->type) {
+ case ROFFT_ELEM:
+ return termp_ft_pre(p, pair, meta, n);
+ case ROFFT_BLOCK:
synopsis_pre(p, n);
return 1;
- } else if (n->type == ROFFT_HEAD)
+ case ROFFT_HEAD:
return 0;
-
- return termp_under_pre(p, pair, meta, n);
+ default:
+ return termp_under_pre(p, pair, meta, n);
+ }
}
static int
termp_bold_pre(DECL_ARGS)
{
-
- termp_tag_pre(p, pair, meta, n);
term_fontpush(p, TERMFONT_BOLD);
return 1;
}
@@ -1244,7 +1241,6 @@ termp_bold_pre(DECL_ARGS)
static int
termp_fd_pre(DECL_ARGS)
{
-
synopsis_pre(p, n);
return termp_bold_pre(p, pair, meta, n);
}
@@ -1252,7 +1248,6 @@ termp_fd_pre(DECL_ARGS)
static void
termp_fd_post(DECL_ARGS)
{
-
term_newln(p);
}
@@ -1273,23 +1268,14 @@ termp_sh_pre(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
- term_fontpush(p, TERMFONT_BOLD);
- break;
+ return termp_bold_pre(p, pair, meta, n);
case ROFFT_BODY:
p->tcol->offset = term_len(p, p->defindent);
term_tab_set(p, NULL);
term_tab_set(p, "T");
term_tab_set(p, ".5i");
- switch (n->sec) {
- case SEC_DESCRIPTION:
- fn_prio = TAG_STRONG;
- break;
- case SEC_AUTHORS:
+ if (n->sec == SEC_AUTHORS)
p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
- break;
- default:
- break;
- }
break;
default:
break;
@@ -1300,7 +1286,6 @@ termp_sh_pre(DECL_ARGS)
static void
termp_sh_post(DECL_ARGS)
{
-
switch (n->type) {
case ROFFT_HEAD:
term_newln(p);
@@ -1317,15 +1302,13 @@ termp_sh_post(DECL_ARGS)
static void
termp_lb_post(DECL_ARGS)
{
-
- if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags)
+ if (n->sec == SEC_LIBRARY && n->flags & NODE_LINE)
term_newln(p);
}
static int
termp_d1_pre(DECL_ARGS)
{
-
if (n->type != ROFFT_BLOCK)
return 1;
term_newln(p);
@@ -1339,11 +1322,8 @@ termp_d1_pre(DECL_ARGS)
static int
termp_ft_pre(DECL_ARGS)
{
-
- /* NB: NODE_LINE does not effect this! */
synopsis_pre(p, n);
- term_fontpush(p, TERMFONT_UNDER);
- return 1;
+ return termp_under_pre(p, pair, meta, n);
}
static int
@@ -1352,11 +1332,9 @@ termp_fn_pre(DECL_ARGS)
size_t rmargin = 0;
int pretty;
- pretty = NODE_SYNPRETTY & n->flags;
-
synopsis_pre(p, n);
-
- if (NULL == (n = n->child))
+ pretty = n->flags & NODE_SYNPRETTY;
+ if ((n = n->child) == NULL)
return 0;
if (pretty) {
@@ -1370,9 +1348,6 @@ termp_fn_pre(DECL_ARGS)
term_word(p, n->string);
term_fontpop(p);
- if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM)
- tag_put(n->string, fn_prio++, p->line);
-
if (pretty) {
term_flushln(p);
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
@@ -1407,7 +1382,6 @@ termp_fn_pre(DECL_ARGS)
term_word(p, ";");
term_flushln(p);
}
-
return 0;
}
@@ -1416,10 +1390,9 @@ termp_fa_pre(DECL_ARGS)
{
const struct roff_node *nn;
- if (n->parent->tok != MDOC_Fo) {
- term_fontpush(p, TERMFONT_UNDER);
- return 1;
- }
+ if (n->parent->tok != MDOC_Fo)
+ return termp_under_pre(p, pair, meta, n);
+
for (nn = n->child; nn != NULL; nn = nn->next) {
term_fontpush(p, TERMFONT_UNDER);
p->flags |= TERMP_NBRWORD;
@@ -1530,9 +1503,8 @@ termp_ss_pre(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
- term_fontpush(p, TERMFONT_BOLD);
p->tcol->offset = term_len(p, (p->defindent+1)/2);
- break;
+ return termp_bold_pre(p, pair, meta, n);
case ROFFT_BODY:
p->tcol->offset = term_len(p, p->defindent);
term_tab_set(p, NULL);
@@ -1553,21 +1525,10 @@ termp_ss_post(DECL_ARGS)
}
static int
-termp_cd_pre(DECL_ARGS)
-{
-
- synopsis_pre(p, n);
- term_fontpush(p, TERMFONT_BOLD);
- return 1;
-}
-
-static int
termp_in_pre(DECL_ARGS)
{
-
synopsis_pre(p, n);
-
- if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) {
+ if (n->flags & NODE_SYNPRETTY && n->flags & NODE_LINE) {
term_fontpush(p, TERMFONT_BOLD);
term_word(p, "#include");
term_word(p, "<");
@@ -1575,7 +1536,6 @@ termp_in_pre(DECL_ARGS)
term_word(p, "<");
term_fontpush(p, TERMFONT_UNDER);
}
-
p->flags |= TERMP_NOSPACE;
return 1;
}
@@ -1583,21 +1543,17 @@ termp_in_pre(DECL_ARGS)
static void
termp_in_post(DECL_ARGS)
{
-
- if (NODE_SYNPRETTY & n->flags)
+ if (n->flags & NODE_SYNPRETTY)
term_fontpush(p, TERMFONT_BOLD);
-
p->flags |= TERMP_NOSPACE;
term_word(p, ">");
-
- if (NODE_SYNPRETTY & n->flags)
+ if (n->flags & NODE_SYNPRETTY)
term_fontpop(p);
}
static int
termp_pp_pre(DECL_ARGS)
{
- fn_prio = TAG_STRONG;
term_vspace(p);
return 0;
}
@@ -1605,14 +1561,12 @@ termp_pp_pre(DECL_ARGS)
static int
termp_skip_pre(DECL_ARGS)
{
-
return 0;
}
static int
termp_quote_pre(DECL_ARGS)
{
-
if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
return 1;
@@ -1769,17 +1723,15 @@ termp_eo_post(DECL_ARGS)
static int
termp_fo_pre(DECL_ARGS)
{
- size_t rmargin = 0;
- int pretty;
-
- pretty = NODE_SYNPRETTY & n->flags;
+ size_t rmargin;
- if (n->type == ROFFT_BLOCK) {
+ switch (n->type) {
+ case ROFFT_BLOCK:
synopsis_pre(p, n);
return 1;
- } else if (n->type == ROFFT_BODY) {
- if (pretty) {
- rmargin = p->tcol->rmargin;
+ case ROFFT_BODY:
+ rmargin = p->tcol->rmargin;
+ if (n->flags & NODE_SYNPRETTY) {
p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
p->flags |= TERMP_NOBREAK | TERMP_BRIND |
TERMP_HANG;
@@ -1787,7 +1739,7 @@ termp_fo_pre(DECL_ARGS)
p->flags |= TERMP_NOSPACE;
term_word(p, "(");
p->flags |= TERMP_NOSPACE;
- if (pretty) {
+ if (n->flags & NODE_SYNPRETTY) {
term_flushln(p);
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
TERMP_HANG);
@@ -1796,30 +1748,21 @@ termp_fo_pre(DECL_ARGS)
p->tcol->rmargin = rmargin;
}
return 1;
+ default:
+ return termp_bold_pre(p, pair, meta, n);
}
-
- if (NULL == n->child)
- return 0;
-
- /* XXX: we drop non-initial arguments as per groff. */
-
- assert(n->child->string);
- term_fontpush(p, TERMFONT_BOLD);
- term_word(p, n->child->string);
- return 0;
}
static void
termp_fo_post(DECL_ARGS)
{
-
if (n->type != ROFFT_BODY)
return;
p->flags |= TERMP_NOSPACE;
term_word(p, ")");
- if (NODE_SYNPRETTY & n->flags) {
+ if (n->flags & NODE_SYNPRETTY) {
p->flags |= TERMP_NOSPACE;
term_word(p, ";");
term_flushln(p);
@@ -1829,29 +1772,30 @@ termp_fo_post(DECL_ARGS)
static int
termp_bf_pre(DECL_ARGS)
{
-
- if (n->type == ROFFT_HEAD)
+ switch (n->type) {
+ case ROFFT_HEAD:
return 0;
- else if (n->type != ROFFT_BODY)
+ case ROFFT_BODY:
+ break;
+ default:
return 1;
-
- if (FONT_Em == n->norm->Bf.font)
- term_fontpush(p, TERMFONT_UNDER);
- else if (FONT_Sy == n->norm->Bf.font)
- term_fontpush(p, TERMFONT_BOLD);
- else
- term_fontpush(p, TERMFONT_NONE);
-
- return 1;
+ }
+ switch (n->norm->Bf.font) {
+ case FONT_Em:
+ return termp_under_pre(p, pair, meta, n);
+ case FONT_Sy:
+ return termp_bold_pre(p, pair, meta, n);
+ default:
+ return termp_li_pre(p, pair, meta, n);
+ }
}
static int
termp_sm_pre(DECL_ARGS)
{
-
- if (NULL == n->child)
+ if (n->child == NULL)
p->flags ^= TERMP_NONOSPACE;
- else if (0 == strcmp("on", n->child->string))
+ else if (strcmp(n->child->string, "on") == 0)
p->flags &= ~TERMP_NONOSPACE;
else
p->flags |= TERMP_NONOSPACE;
@@ -1865,7 +1809,6 @@ termp_sm_pre(DECL_ARGS)
static int
termp_ap_pre(DECL_ARGS)
{
-
p->flags |= TERMP_NOSPACE;
term_word(p, "'");
p->flags |= TERMP_NOSPACE;
@@ -1904,8 +1847,6 @@ termp____post(DECL_ARGS)
static int
termp_li_pre(DECL_ARGS)
{
-
- termp_tag_pre(p, pair, meta, n);
term_fontpush(p, TERMFONT_NONE);
return 1;
}
@@ -1955,7 +1896,6 @@ termp_lk_pre(DECL_ARGS)
static int
termp_bk_pre(DECL_ARGS)
{
-
switch (n->type) {
case ROFFT_BLOCK:
break;
@@ -1968,107 +1908,47 @@ termp_bk_pre(DECL_ARGS)
default:
abort();
}
-
return 1;
}
static void
termp_bk_post(DECL_ARGS)
{
-
if (n->type == ROFFT_BODY)
p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
}
+/*
+ * If we are in an `Rs' and there is a journal present,
+ * then quote us instead of underlining us (for disambiguation).
+ */
static void
termp__t_post(DECL_ARGS)
{
-
- /*
- * If we're in an `Rs' and there's a journal present, then quote
- * us instead of underlining us (for disambiguation).
- */
- if (n->parent && MDOC_Rs == n->parent->tok &&
+ if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
n->parent->norm->Rs.quote_T)
termp_quote_post(p, pair, meta, n);
-
termp____post(p, pair, meta, n);
}
static int
termp__t_pre(DECL_ARGS)
{
-
- /*
- * If we're in an `Rs' and there's a journal present, then quote
- * us instead of underlining us (for disambiguation).
- */
- if (n->parent && MDOC_Rs == n->parent->tok &&
+ if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
n->parent->norm->Rs.quote_T)
return termp_quote_pre(p, pair, meta, n);
-
- term_fontpush(p, TERMFONT_UNDER);
- return 1;
+ else
+ return termp_under_pre(p, pair, meta, n);
}
static int
termp_under_pre(DECL_ARGS)
{
-
- term_fontpush(p, TERMFONT_UNDER);
- return 1;
-}
-
-static int
-termp_em_pre(DECL_ARGS)
-{
- if (n->child != NULL &&
- n->child->type == ROFFT_TEXT)
- tag_put(n->child->string, TAG_FALLBACK, p->line);
term_fontpush(p, TERMFONT_UNDER);
return 1;
}
static int
-termp_sy_pre(DECL_ARGS)
-{
- if (n->child != NULL &&
- n->child->type == ROFFT_TEXT)
- tag_put(n->child->string, TAG_FALLBACK, p->line);
- term_fontpush(p, TERMFONT_BOLD);
- return 1;
-}
-
-static int
-termp_er_pre(DECL_ARGS)
-{
-
- if (n->sec == SEC_ERRORS &&
- (n->parent->tok == MDOC_It ||
- (n->parent->tok == MDOC_Bq &&
- n->parent->parent->parent->tok == MDOC_It)))
- tag_put(n->child->string, TAG_STRONG, p->line);
- return 1;
-}
-
-static int
-termp_tag_pre(DECL_ARGS)
-{
-
- if (n->child != NULL &&
- n->child->type == ROFFT_TEXT &&
- (n->prev == NULL ||
- (n->prev->type == ROFFT_TEXT &&
- strcmp(n->prev->string, "|") == 0)) &&
- (n->parent->tok == MDOC_It ||
- (n->parent->tok == MDOC_Xo &&
- n->parent->parent->prev == NULL &&
- n->parent->parent->parent->tok == MDOC_It)))
- tag_put(n->child->string, TAG_STRONG, p->line);
- return 1;
-}
-
-static int
termp_abort_pre(DECL_ARGS)
{
abort();
diff --git a/mdoc_validate.c b/mdoc_validate.c
index 0ea41db1..bae3d6b8 100644
--- a/mdoc_validate.c
+++ b/mdoc_validate.c
@@ -1,7 +1,7 @@
-/* $Id: mdoc_validate.c,v 1.379 2020/02/27 21:43:44 schwarze Exp $ */
+/* $Id: mdoc_validate.c,v 1.380 2020/03/13 15:32:28 schwarze Exp $ */
/*
- * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -15,6 +15,8 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Validation module for mdoc(7) syntax trees used by mandoc(1).
*/
#include "config.h"
@@ -35,6 +37,7 @@
#include "mandoc.h"
#include "mandoc_xr.h"
#include "roff.h"
+#include "tag.h"
#include "mdoc.h"
#include "libmandoc.h"
#include "roff_int.h"
@@ -82,7 +85,9 @@ static void post_dd(POST_ARGS);
static void post_delim(POST_ARGS);
static void post_delim_nb(POST_ARGS);
static void post_dt(POST_ARGS);
+static void post_em(POST_ARGS);
static void post_en(POST_ARGS);
+static void post_er(POST_ARGS);
static void post_es(POST_ARGS);
static void post_eoln(POST_ARGS);
static void post_ex(POST_ARGS);
@@ -113,6 +118,7 @@ static void post_sm(POST_ARGS);
static void post_st(POST_ARGS);
static void post_std(POST_ARGS);
static void post_sx(POST_ARGS);
+static void post_tag(POST_ARGS);
static void post_tg(POST_ARGS);
static void post_useless(POST_ARGS);
static void post_xr(POST_ARGS);
@@ -137,19 +143,19 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
NULL, /* Ap */
post_defaults, /* Ar */
NULL, /* Cd */
- post_delim_nb, /* Cm */
- post_delim_nb, /* Dv */
- post_delim_nb, /* Er */
- post_delim_nb, /* Ev */
+ post_tag, /* Cm */
+ post_tag, /* Dv */
+ post_er, /* Er */
+ post_tag, /* Ev */
post_ex, /* Ex */
post_fa, /* Fa */
NULL, /* Fd */
- post_delim_nb, /* Fl */
+ post_tag, /* Fl */
post_fn, /* Fn */
post_delim_nb, /* Ft */
- post_delim_nb, /* Ic */
+ post_tag, /* Ic */
post_delim_nb, /* In */
- post_defaults, /* Li */
+ post_tag, /* Li */
post_nd, /* Nd */
post_nm, /* Nm */
post_delim_nb, /* Op */
@@ -187,11 +193,11 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
NULL, /* Dq */
NULL, /* Ec */
NULL, /* Ef */
- post_delim_nb, /* Em */
+ post_em, /* Em */
NULL, /* Eo */
post_xx, /* Fx */
- post_delim_nb, /* Ms */
- NULL, /* No */
+ post_tag, /* Ms */
+ post_tag, /* No */
post_ns, /* Ns */
post_xx, /* Nx */
post_xx, /* Ox */
@@ -210,7 +216,7 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
post_delim_nb, /* Sq */
post_sm, /* Sm */
post_sx, /* Sx */
- post_delim_nb, /* Sy */
+ post_em, /* Sy */
post_useless, /* Tn */
post_xx, /* Ux */
NULL, /* Xc */
@@ -287,6 +293,8 @@ static const char * const secnames[SEC__MAX] = {
NULL
};
+static int fn_prio = TAG_STRONG;
+
/* Validate the subtree rooted at mdoc->last. */
void
@@ -1094,8 +1102,11 @@ post_st(POST_ARGS)
static void
post_tg(POST_ARGS)
{
- struct roff_node *n, *nch, *nn;
- size_t len;
+ struct roff_node *n; /* The .Tg node. */
+ struct roff_node *nch; /* The first child of the .Tg node. */
+ struct roff_node *nn; /* The next node after the .Tg node. */
+ struct roff_node *nt; /* The TEXT node containing the tag. */
+ size_t len; /* The number of bytes in the tag. */
/* Find the next node. */
n = mdoc->last;
@@ -1106,30 +1117,26 @@ post_tg(POST_ARGS)
}
}
- /* Add the default argument, if needed. */
- nch = n->child;
- if (nch == NULL && nn != NULL && nn->child->type == ROFFT_TEXT) {
- mdoc->next = ROFF_NEXT_CHILD;
- roff_word_alloc(mdoc, n->line, n->pos, n->next->child->string);
- nch = mdoc->last;
- nch->flags |= NODE_NOSRC;
- mdoc->last = n;
- }
+ /* Find the tag. */
+ nt = nch = n->child;
+ if (nch == NULL && nn != NULL && nn->child != NULL &&
+ nn->child->type == ROFFT_TEXT)
+ nt = nn->child;
- /* Validate the first argument. */
- if (nch == NULL || *nch->string == '\0')
+ /* Validate the tag. */
+ if (nt == NULL || *nt->string == '\0')
mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg");
- if (nch == NULL) {
+ if (nt == NULL) {
roff_node_delete(mdoc, n);
return;
}
- len = strcspn(nch->string, " \t");
- if (nch->string[len] != '\0')
- mandoc_msg(MANDOCERR_TG_SPC, nch->line, nch->pos + len + 1,
- "Tg %s", nch->string);
+ len = strcspn(nt->string, " \t\\");
+ if (nt->string[len] != '\0')
+ mandoc_msg(MANDOCERR_TG_SPC, nt->line,
+ nt->pos + len, "Tg %s", nt->string);
/* Keep only the first argument. */
- if (nch->next != NULL) {
+ if (nch != NULL && nch->next != NULL) {
mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line,
nch->next->pos, "Tg ... %s", nch->next->string);
while (nch->next != NULL)
@@ -1137,32 +1144,44 @@ post_tg(POST_ARGS)
}
/* Drop the macro if the first argument is invalid. */
- if (len == 0 || nch->string[len] != '\0') {
+ if (len == 0 || nt->string[len] != '\0') {
roff_node_delete(mdoc, n);
return;
}
- /* By default, write a <mark> element. */
- n->flags |= NODE_ID;
+ /* By default, tag the .Tg node itself. */
if (nn == NULL)
- return;
+ nn = n;
/* Explicit tagging of specific macros. */
switch (nn->tok) {
case MDOC_Sh:
case MDOC_Ss:
- if (nn->head->flags & NODE_ID || nn->head->child == NULL)
+ case MDOC_Fo:
+ nn = nn->head;
+ /* FALLTHROUGH */
+ case MDOC_Cm:
+ case MDOC_Dv:
+ case MDOC_Em:
+ case MDOC_Er:
+ case MDOC_Ev:
+ case MDOC_Fl:
+ case MDOC_Fn:
+ case MDOC_Ic:
+ case MDOC_Li:
+ case MDOC_Ms:
+ case MDOC_No:
+ case MDOC_Sy:
+ if (nn->child != NULL && (nn->flags & NODE_ID) == 0)
break;
- n->flags |= NODE_NOPRT;
- nn->head->flags |= NODE_ID | NODE_HREF;
- assert(nn->head->string == NULL);
- nn->head->string = mandoc_strdup(nch->string);
- break;
+ /* FALLTHROUGH */
default:
+ nn = n;
break;
}
- if (n->flags & NODE_NOPRT)
- n->flags &= ~NODE_ID;
+ tag_put(nt->string, TAG_MANUAL, nn);
+ if (nn != n)
+ n->flags |= NODE_NOPRT;
}
static void
@@ -1257,28 +1276,32 @@ post_bf(POST_ARGS)
static void
post_fname(POST_ARGS)
{
- const struct roff_node *n;
+ struct roff_node *n, *nch;
const char *cp;
size_t pos;
- n = mdoc->last->child;
- cp = n->string;
+ n = mdoc->last;
+ nch = n->child;
+ cp = nch->string;
if (*cp == '(') {
if (cp[strlen(cp + 1)] == ')')
return;
pos = 0;
} else {
pos = strcspn(cp, "()");
- if (cp[pos] == '\0')
+ if (cp[pos] == '\0') {
+ if (n->sec == SEC_DESCRIPTION ||
+ n->sec == SEC_CUSTOM)
+ tag_put(NULL, fn_prio++, n);
return;
+ }
}
- mandoc_msg(MANDOCERR_FN_PAREN, n->line, n->pos + pos, "%s", cp);
+ mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp);
}
static void
post_fn(POST_ARGS)
{
-
post_fname(mdoc);
post_fa(mdoc);
}
@@ -1442,38 +1465,29 @@ post_display(POST_ARGS)
static void
post_defaults(POST_ARGS)
{
- struct roff_node *nn;
+ struct roff_node *n;
- if (mdoc->last->child != NULL) {
+ n = mdoc->last;
+ if (n->child != NULL) {
post_delim_nb(mdoc);
return;
}
-
- /*
- * The `Ar' defaults to "file ..." if no value is provided as an
- * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
- * gets an empty string.
- */
-
- nn = mdoc->last;
- switch (nn->tok) {
+ mdoc->next = ROFF_NEXT_CHILD;
+ switch (n->tok) {
case MDOC_Ar:
- mdoc->next = ROFF_NEXT_CHILD;
- roff_word_alloc(mdoc, nn->line, nn->pos, "file");
- mdoc->last->flags |= NODE_NOSRC;
- roff_word_alloc(mdoc, nn->line, nn->pos, "...");
+ roff_word_alloc(mdoc, n->line, n->pos, "file");
mdoc->last->flags |= NODE_NOSRC;
+ roff_word_alloc(mdoc, n->line, n->pos, "...");
break;
case MDOC_Pa:
case MDOC_Mt:
- mdoc->next = ROFF_NEXT_CHILD;
- roff_word_alloc(mdoc, nn->line, nn->pos, "~");
- mdoc->last->flags |= NODE_NOSRC;
+ roff_word_alloc(mdoc, n->line, n->pos, "~");
break;
default:
abort();
}
- mdoc->last = nn;
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->last = n;
}
static void
@@ -1527,18 +1541,54 @@ post_an(POST_ARGS)
}
static void
-post_en(POST_ARGS)
+post_em(POST_ARGS)
{
+ post_tag(mdoc);
+ tag_put(NULL, TAG_FALLBACK, mdoc->last);
+}
+static void
+post_en(POST_ARGS)
+{
post_obsolete(mdoc);
if (mdoc->last->type == ROFFT_BLOCK)
mdoc->last->norm->Es = mdoc->last_es;
}
static void
-post_es(POST_ARGS)
+post_er(POST_ARGS)
+{
+ struct roff_node *n;
+
+ n = mdoc->last;
+ if (n->sec == SEC_ERRORS &&
+ (n->parent->tok == MDOC_It ||
+ (n->parent->tok == MDOC_Bq &&
+ n->parent->parent->parent->tok == MDOC_It)))
+ tag_put(NULL, TAG_STRONG, n);
+ post_delim_nb(mdoc);
+}
+
+static void
+post_tag(POST_ARGS)
{
+ struct roff_node *n;
+
+ n = mdoc->last;
+ if ((n->prev == NULL ||
+ (n->prev->type == ROFFT_TEXT &&
+ strcmp(n->prev->string, "|") == 0)) &&
+ (n->parent->tok == MDOC_It ||
+ (n->parent->tok == MDOC_Xo &&
+ n->parent->parent->prev == NULL &&
+ n->parent->parent->parent->tok == MDOC_It)))
+ tag_put(NULL, TAG_STRONG, n);
+ post_delim_nb(mdoc);
+}
+static void
+post_es(POST_ARGS)
+{
post_obsolete(mdoc);
mdoc->last_es = mdoc->last;
}
@@ -1635,8 +1685,8 @@ post_it(POST_ARGS)
if ((nch = nit->head->child) != NULL)
mandoc_msg(MANDOCERR_ARG_SKIP,
nit->line, nit->pos, "It %s",
- nch->string == NULL ? roff_name[nch->tok] :
- nch->string);
+ nch->type == ROFFT_TEXT ? nch->string :
+ roff_name[nch->tok]);
break;
case LIST_column:
cols = (int)nbl->norm->Bl.ncols;
@@ -2152,7 +2202,6 @@ post_sx(POST_ARGS)
static void
post_sh(POST_ARGS)
{
-
post_ignpar(mdoc);
switch (mdoc->last->type) {
@@ -2384,6 +2433,8 @@ post_sh_head(POST_ARGS)
roff_setreg(mdoc->roff, "nS", 0, '=');
mdoc->flags &= ~MDOC_SYNOPSIS;
}
+ if (sec == SEC_DESCRIPTION)
+ fn_prio = TAG_STRONG;
/* Mark our last section. */
@@ -2555,6 +2606,7 @@ post_par(POST_ARGS)
{
struct roff_node *np;
+ fn_prio = TAG_STRONG;
post_prevpar(mdoc);
np = mdoc->last;
diff --git a/read.c b/read.c
index b3858061..dc6c2409 100644
--- a/read.c
+++ b/read.c
@@ -1,7 +1,7 @@
-/* $Id: read.c,v 1.214 2019/07/10 19:39:01 schwarze Exp $ */
+/* $Id: read.c,v 1.215 2020/03/13 15:32:28 schwarze Exp $ */
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -15,6 +15,12 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Top-level functions of the mandoc(3) parser:
+ * Parser and input encoding selection, decompression,
+ * handling of input bytes, characters, lines, and files,
+ * handling of roff(7) loops and file inclusion,
+ * and steering of the various parsers.
*/
#include "config.h"
@@ -36,6 +42,7 @@
#include "mandoc_aux.h"
#include "mandoc.h"
#include "roff.h"
+#include "tag.h"
#include "mdoc.h"
#include "man.h"
#include "mandoc_parse.h"
@@ -664,6 +671,7 @@ mparse_alloc(int options, enum mandoc_os os_e, const char *os_s)
}
curp->man->meta.first->tok = TOKEN_NONE;
curp->man->meta.os_e = os_e;
+ tag_alloc();
return curp;
}
@@ -680,6 +688,7 @@ mparse_reset(struct mparse *curp)
void
mparse_free(struct mparse *curp)
{
+ tag_free();
roffhash_free(curp->man->mdocmac);
roffhash_free(curp->man->manmac);
roff_man_free(curp->man);
diff --git a/regress/copyless b/regress/copyless
new file mode 100755
index 00000000..a00c0009
--- /dev/null
+++ b/regress/copyless
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -e
+umask 022
+if [ "$#" -ne 4 ]; then
+ echo "$0 $*: $# args instead of 4" 1>&2
+ exit 1
+fi
+if [ "$2" != "-T" ]; then
+ echo "$0 $*: second arg is not -T" 1>&2
+ exit 1
+fi
+cut -d ' ' -f 1,3 "$3" > "$1.mandoc_tag"
+cp "$4" "$1.mandoc_ascii"
+exit 0
diff --git a/regress/man/IP/Makefile b/regress/man/IP/Makefile
index 70094e5f..8d607c62 100644
--- a/regress/man/IP/Makefile
+++ b/regress/man/IP/Makefile
@@ -1,8 +1,9 @@
-# $OpenBSD: Makefile,v 1.10 2020/02/27 01:25:58 schwarze Exp $
+# $OpenBSD: Makefile,v 1.11 2020/03/13 00:31:05 schwarze Exp $
-REGRESS_TARGETS = bullet empty literal longhead manyargs spacing vert width
+REGRESS_TARGETS = bullet empty literal longhead manyargs spacing tag vert width
+TAG_TARGETS = empty tag
UTF8_TARGETS = bullet
LINT_TARGETS = empty
-HTML_TARGETS = bullet literal
+HTML_TARGETS = bullet empty literal tag
.include <bsd.regress.mk>
diff --git a/regress/man/IP/empty.in b/regress/man/IP/empty.in
index 21c42983..bb814870 100644
--- a/regress/man/IP/empty.in
+++ b/regress/man/IP/empty.in
@@ -1,8 +1,9 @@
-.\" $OpenBSD: empty.in,v 1.2 2017/07/04 14:53:23 schwarze Exp $
+.\" $OpenBSD: empty.in,v 1.3 2020/03/13 00:31:05 schwarze Exp $
.TH IP-EMPTY 1 "July 17, 2012"
.SH NAME
IP-empty \- empty indented paragraphs
.SH DESCRIPTION
+BEGINTEST
regular
text
.IP
@@ -25,3 +26,4 @@ text
.RE
regular
text
+ENDTEST
diff --git a/regress/man/IP/empty.out_ascii b/regress/man/IP/empty.out_ascii
index 24ab17fa..b17190ab 100644
--- a/regress/man/IP/empty.out_ascii
+++ b/regress/man/IP/empty.out_ascii
@@ -6,7 +6,7 @@ NNAAMMEE
IP-empty - empty indented paragraphs
DDEESSCCRRIIPPTTIIOONN
- regular text
+ BEGINTEST regular text
indented text
@@ -19,7 +19,7 @@ DDEESSCCRRIIPPTTIIOONN
Empty IP is deleted, RS does not cause additional spacing:
tag indented text
- regular text
+ regular text ENDTEST
diff --git a/regress/man/IP/empty.out_html b/regress/man/IP/empty.out_html
new file mode 100644
index 00000000..f2498dba
--- /dev/null
+++ b/regress/man/IP/empty.out_html
@@ -0,0 +1,18 @@
+<dl class="Bl-tag">
+ <dt></dt>
+ <dd>indented text</dd>
+</dl>
+<p class="Pp">Empty IP is deleted:</p>
+<dl class="Bl-tag">
+ <dt id="tag1"><a class="permalink" href="#tag1">tag1</a></dt>
+ <dd></dd>
+ <dt id="tag2"><a class="permalink" href="#tag2">tag2</a></dt>
+ <dd>indented text</dd>
+</dl>
+<p class="Pp">Empty IP is deleted, RS does not cause additional spacing:</p>
+<div class="Bd-indent">
+<dl class="Bl-tag">
+ <dt id="tag"><a class="permalink" href="#tag">tag</a></dt>
+ <dd>indented text</dd>
+</dl>
+</div>
diff --git a/regress/man/IP/empty.out_lint b/regress/man/IP/empty.out_lint
index 6c666c88..3514b94f 100644
--- a/regress/man/IP/empty.out_lint
+++ b/regress/man/IP/empty.out_lint
@@ -1,2 +1,2 @@
-mandoc: empty.in:13:2: WARNING: skipping paragraph macro: IP empty
-mandoc: empty.in:20:2: WARNING: skipping paragraph macro: IP empty
+mandoc: empty.in:14:2: WARNING: skipping paragraph macro: IP empty
+mandoc: empty.in:21:2: WARNING: skipping paragraph macro: IP empty
diff --git a/regress/man/IP/empty.out_tag b/regress/man/IP/empty.out_tag
new file mode 100644
index 00000000..f35f1120
--- /dev/null
+++ b/regress/man/IP/empty.out_tag
@@ -0,0 +1,3 @@
+tag1 15
+tag2 17
+tag 21
diff --git a/regress/man/IP/literal.out_html b/regress/man/IP/literal.out_html
index b61fc843..3b9cc429 100644
--- a/regress/man/IP/literal.out_html
+++ b/regress/man/IP/literal.out_html
@@ -1,5 +1,5 @@
<dl class="Bl-tag">
- <dt>tag</dt>
+ <dt id="tag"><a class="permalink" href="#tag">tag</a></dt>
<dd>indented regular text</dd>
</dl>
<p class="Pp">new regular paragraph</p>
@@ -8,7 +8,7 @@ literal
text
</pre>
<dl class="Bl-tag">
- <dt>tag</dt>
+ <dt id="tag_2"><a class="permalink" href="#tag_2">tag</a></dt>
<dd>
<pre>
indented
@@ -32,7 +32,7 @@ literal
text
</pre>
<dl class="Bl-tag">
- <dt>tag</dt>
+ <dt id="tag_3"><a class="permalink" href="#tag_3">tag</a></dt>
<dd>
<pre>
indented
@@ -48,7 +48,7 @@ text
out of indented paragraph</a></h2>
<p class="Pp">regular text</p>
<dl class="Bl-tag">
- <dt>tag</dt>
+ <dt id="tag_4"><a class="permalink" href="#tag_4">tag</a></dt>
<dd>indented regular text
<pre>
indented
diff --git a/regress/man/IP/tag.in b/regress/man/IP/tag.in
new file mode 100644
index 00000000..038fa969
--- /dev/null
+++ b/regress/man/IP/tag.in
@@ -0,0 +1,18 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:05 schwarze Exp $
+.TH IP-TAG 1 "March 10, 2020"
+.SH NAME
+IP-tag \- automatic tagging of indented blocks
+.SH DESCRIPTION
+BEGINTEST
+initial
+text
+.IP " strong" 10n
+text
+.IP "-strong"
+text
+.IP "\&\fI \-weak\fP"
+text
+.IP " strong"
+text
+.PP
+ENDTEST
diff --git a/regress/man/IP/tag.out_ascii b/regress/man/IP/tag.out_ascii
new file mode 100644
index 00000000..afd4f587
--- /dev/null
+++ b/regress/man/IP/tag.out_ascii
@@ -0,0 +1,23 @@
+IP-TAG(1) General Commands Manual IP-TAG(1)
+
+
+
+NNAAMMEE
+ IP-tag - automatic tagging of indented blocks
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST initial text
+
+ strong text
+
+ -strong text
+
+ _-_w_e_a_k text
+
+ strong text
+
+ ENDTEST
+
+
+
+OpenBSD March 10, 2020 IP-TAG(1)
diff --git a/regress/man/IP/tag.out_html b/regress/man/IP/tag.out_html
new file mode 100644
index 00000000..4d25b12b
--- /dev/null
+++ b/regress/man/IP/tag.out_html
@@ -0,0 +1,10 @@
+<dl class="Bl-tag">
+ <dt> strong</dt>
+ <dd>text</dd>
+ <dt id="strong"><a class="permalink" href="#strong">-strong</a></dt>
+ <dd>text</dd>
+ <dt id="weak"><a class="permalink" href="#weak"><i> -weak</i></a></dt>
+ <dd>text</dd>
+ <dt> strong</dt>
+ <dd>text</dd>
+</dl>
diff --git a/regress/man/IP/tag.out_tag b/regress/man/IP/tag.out_tag
new file mode 100644
index 00000000..5be038db
--- /dev/null
+++ b/regress/man/IP/tag.out_tag
@@ -0,0 +1,2 @@
+strong 13
+weak 15
diff --git a/regress/man/TP/Makefile b/regress/man/TP/Makefile
index ca2475b0..9dd2fde7 100644
--- a/regress/man/TP/Makefile
+++ b/regress/man/TP/Makefile
@@ -1,9 +1,10 @@
-# $OpenBSD: Makefile,v 1.16 2020/02/27 01:25:58 schwarze Exp $
+# $OpenBSD: Makefile,v 1.17 2020/03/13 00:31:05 schwarze Exp $
REGRESS_TARGETS = badarg broken double eof fill indent literal longhead
-REGRESS_TARGETS += macrotag manyargs sameline spacing vert width
+REGRESS_TARGETS += macrotag manyargs sameline spacing tag vert width
+TAG_TARGETS = tag
LINT_TARGETS = broken double eof
-HTML_TARGETS = literal vert
+HTML_TARGETS = literal tag vert
# groff-1.22.3 defects:
# - If .TP precedes .RE, the latter does not properly reset indentation.
diff --git a/regress/man/TP/literal.out_html b/regress/man/TP/literal.out_html
index 7f5b1c1f..8b818ed8 100644
--- a/regress/man/TP/literal.out_html
+++ b/regress/man/TP/literal.out_html
@@ -1,5 +1,5 @@
<dl class="Bl-tag">
- <dt>tag</dt>
+ <dt id="tag"><a class="permalink" href="#tag">tag</a></dt>
<dd>regular indented text</dd>
</dl>
<p class="Pp">regular paragraph</p>
@@ -8,7 +8,7 @@ literal
text
</pre>
<dl class="Bl-tag">
- <dt>tag</dt>
+ <dt id="tag_2"><a class="permalink" href="#tag_2">tag</a></dt>
<dd>
<pre>
indented
diff --git a/regress/man/TP/tag.in b/regress/man/TP/tag.in
new file mode 100644
index 00000000..34d1e151
--- /dev/null
+++ b/regress/man/TP/tag.in
@@ -0,0 +1,31 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:05 schwarze Exp $
+.TH IP-TAG 1 "March 10, 2020"
+.SH NAME
+IP-tag \- automatic tagging of indented blocks
+.SH DESCRIPTION
+BEGINTEST
+initial
+text
+.TP 10n
+.I " plain"
+text
+.TP
+plain
+text
+.TP
+.I "plain "
+text
+.TP
+\& strong
+text
+.TP
+.B -strong
+text
+.TP
+\&\fI \-weak\fP
+text
+.TP
+.B "strong "
+text
+.PP
+ENDTEST
diff --git a/regress/man/TP/tag.out_ascii b/regress/man/TP/tag.out_ascii
new file mode 100644
index 00000000..4da2efa0
--- /dev/null
+++ b/regress/man/TP/tag.out_ascii
@@ -0,0 +1,29 @@
+IP-TAG(1) General Commands Manual IP-TAG(1)
+
+
+
+NNAAMMEE
+ IP-tag - automatic tagging of indented blocks
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST initial text
+
+ _p_l_a_i_n text
+
+ plain text
+
+ _p_l_a_i_n text
+
+ strong text
+
+ --ssttrroonngg text
+
+ _-_w_e_a_k text
+
+ ssttrroonngg text
+
+ ENDTEST
+
+
+
+OpenBSD March 10, 2020 IP-TAG(1)
diff --git a/regress/man/TP/tag.out_html b/regress/man/TP/tag.out_html
new file mode 100644
index 00000000..3fbbe41d
--- /dev/null
+++ b/regress/man/TP/tag.out_html
@@ -0,0 +1,16 @@
+<dl class="Bl-tag">
+ <dt><i> plain</i></dt>
+ <dd>text</dd>
+ <dt id="plain"><a class="permalink" href="#plain">plain</a></dt>
+ <dd>text</dd>
+ <dt><i>plain </i></dt>
+ <dd>text</dd>
+ <dt> strong</dt>
+ <dd>text</dd>
+ <dt id="strong"><a class="permalink" href="#strong"><b>-strong</b></a></dt>
+ <dd>text</dd>
+ <dt id="weak"><a class="permalink" href="#weak"><i> -weak</i></a></dt>
+ <dd>text</dd>
+ <dt><b>strong </b></dt>
+ <dd>text</dd>
+</dl>
diff --git a/regress/man/TP/tag.out_tag b/regress/man/TP/tag.out_tag
new file mode 100644
index 00000000..88f6cff8
--- /dev/null
+++ b/regress/man/TP/tag.out_tag
@@ -0,0 +1,3 @@
+plain 13
+strong 19
+weak 21
diff --git a/regress/man/TP/vert.out_html b/regress/man/TP/vert.out_html
index 7301819f..1124ed33 100644
--- a/regress/man/TP/vert.out_html
+++ b/regress/man/TP/vert.out_html
@@ -2,8 +2,8 @@
<section class="Sh">
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
<dl class="Bl-tag">
- <dt>tag</dt>
+ <dt id="tag"><a class="permalink" href="#tag">tag</a></dt>
<dd>text</dd>
- <dt>tag</dt>
+ <dt id="tag_2"><a class="permalink" href="#tag_2">tag</a></dt>
<dd>text</dd>
</dl>
diff --git a/regress/mdoc/Cm/Makefile b/regress/mdoc/Cm/Makefile
index 4b7066ec..7bc3fc20 100644
--- a/regress/mdoc/Cm/Makefile
+++ b/regress/mdoc/Cm/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.3 2014/07/02 20:18:42 schwarze Exp $
+# $OpenBSD: Makefile,v 1.7 2020/03/13 00:31:05 schwarze Exp $
-REGRESS_TARGETS = basic font noarg punct
+REGRESS_TARGETS = basic font noarg punct tag
+TAG_TARGETS = tag
LINT_TARGETS = noarg
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Cm/tag.in b/regress/mdoc/Cm/tag.in
new file mode 100644
index 00000000..3db1d1e0
--- /dev/null
+++ b/regress/mdoc/Cm/tag.in
@@ -0,0 +1,21 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:05 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt CM-TAG 1
+.Os
+.Sh NAME
+.Nm Cm-tag
+.Nd tagging of command modifier macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Cm one | two
+text
+.It Xo
+.Cm three
+.Xc
+text
+.El
+.Tg
+.Cm four
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Cm/tag.out_ascii b/regress/mdoc/Cm/tag.out_ascii
new file mode 100644
index 00000000..071408c7
--- /dev/null
+++ b/regress/mdoc/Cm/tag.out_ascii
@@ -0,0 +1,17 @@
+CM-TAG(1) General Commands Manual CM-TAG(1)
+
+NNAAMMEE
+ CCmm--ttaagg - tagging of command modifier macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ oonnee | ttwwoo
+ text
+
+ tthhrreeee text
+ ffoouurr
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Cm/tag.out_html b/regress/mdoc/Cm/tag.out_html
new file mode 100644
index 00000000..a140fe3e
--- /dev/null
+++ b/regress/mdoc/Cm/tag.out_html
@@ -0,0 +1,9 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#one"><code class="Cm" id="one">one</code></a>
+ |
+ <a class="permalink" href="#two"><code class="Cm" id="two">two</code></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#three"><code class="Cm" id="three">three</code></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#four"><code class="Cm" id="four">four</code></a>
diff --git a/regress/mdoc/Cm/tag.out_markdown b/regress/mdoc/Cm/tag.out_markdown
new file mode 100644
index 00000000..893a8fe5
--- /dev/null
+++ b/regress/mdoc/Cm/tag.out_markdown
@@ -0,0 +1,23 @@
+CM-TAG(1) - General Commands Manual
+
+# NAME
+
+**Cm-tag** - tagging of command modifier macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+**one** | **two**
+
+> text
+
+**three**
+
+> text
+
+**four**
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Cm/tag.out_tag b/regress/mdoc/Cm/tag.out_tag
new file mode 100644
index 00000000..94f0cfb2
--- /dev/null
+++ b/regress/mdoc/Cm/tag.out_tag
@@ -0,0 +1,4 @@
+one 9
+two 9
+three 12
+four 13
diff --git a/regress/mdoc/Dv/Makefile b/regress/mdoc/Dv/Makefile
index cfe3ad0b..979e1826 100644
--- a/regress/mdoc/Dv/Makefile
+++ b/regress/mdoc/Dv/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.2 2014/07/02 20:18:42 schwarze Exp $
+# $OpenBSD: Makefile,v 1.5 2020/03/13 00:31:05 schwarze Exp $
-REGRESS_TARGETS = font noarg
+REGRESS_TARGETS = font noarg tag
+TAG_TARGETS = tag
LINT_TARGETS = noarg
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Dv/tag.in b/regress/mdoc/Dv/tag.in
new file mode 100644
index 00000000..b6c79ad1
--- /dev/null
+++ b/regress/mdoc/Dv/tag.in
@@ -0,0 +1,21 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:05 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt DV-TAG 1
+.Os
+.Sh NAME
+.Nm Dv-tag
+.Nd tagging of defined variable macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Dv one | two
+text
+.It Xo
+.Dv three
+.Xc
+text
+.El
+.Tg
+.Dv four
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Dv/tag.out_ascii b/regress/mdoc/Dv/tag.out_ascii
new file mode 100644
index 00000000..119e3509
--- /dev/null
+++ b/regress/mdoc/Dv/tag.out_ascii
@@ -0,0 +1,17 @@
+DV-TAG(1) General Commands Manual DV-TAG(1)
+
+NNAAMMEE
+ DDvv--ttaagg - tagging of defined variable macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ one | two
+ text
+
+ three text
+ four
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Dv/tag.out_html b/regress/mdoc/Dv/tag.out_html
new file mode 100644
index 00000000..cee78340
--- /dev/null
+++ b/regress/mdoc/Dv/tag.out_html
@@ -0,0 +1,9 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#one"><code class="Dv" id="one">one</code></a>
+ |
+ <a class="permalink" href="#two"><code class="Dv" id="two">two</code></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#three"><code class="Dv" id="three">three</code></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#four"><code class="Dv" id="four">four</code></a>
diff --git a/regress/mdoc/Dv/tag.out_markdown b/regress/mdoc/Dv/tag.out_markdown
new file mode 100644
index 00000000..29b454bb
--- /dev/null
+++ b/regress/mdoc/Dv/tag.out_markdown
@@ -0,0 +1,23 @@
+DV-TAG(1) - General Commands Manual
+
+# NAME
+
+**Dv-tag** - tagging of defined variable macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+`one` | `two`
+
+> text
+
+`three`
+
+> text
+
+`four`
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Dv/tag.out_tag b/regress/mdoc/Dv/tag.out_tag
new file mode 100644
index 00000000..94f0cfb2
--- /dev/null
+++ b/regress/mdoc/Dv/tag.out_tag
@@ -0,0 +1,4 @@
+one 9
+two 9
+three 12
+four 13
diff --git a/regress/mdoc/Em/Makefile b/regress/mdoc/Em/Makefile
index 13de4337..2b500444 100644
--- a/regress/mdoc/Em/Makefile
+++ b/regress/mdoc/Em/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.3 2014/11/17 06:44:35 schwarze Exp $
+# $OpenBSD: Makefile,v 1.6 2020/03/13 00:31:05 schwarze Exp $
-REGRESS_TARGETS = font noarg punct
+REGRESS_TARGETS = font noarg punct tag
+TAG_TARGETS = tag
LINT_TARGETS = noarg punct
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Em/tag.in b/regress/mdoc/Em/tag.in
new file mode 100644
index 00000000..70e6eb44
--- /dev/null
+++ b/regress/mdoc/Em/tag.in
@@ -0,0 +1,23 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:05 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt EM-TAG 1
+.Os
+.Sh NAME
+.Nm Em-tag
+.Nd tagging of emphasis macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Em one | two
+text
+.It Xo
+.Em three
+.Xc
+text
+.El
+.Em four
+.Em one
+.Tg explicit
+.Em five
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Em/tag.out_ascii b/regress/mdoc/Em/tag.out_ascii
new file mode 100644
index 00000000..74f8212b
--- /dev/null
+++ b/regress/mdoc/Em/tag.out_ascii
@@ -0,0 +1,17 @@
+EM-TAG(1) General Commands Manual EM-TAG(1)
+
+NNAAMMEE
+ EEmm--ttaagg - tagging of emphasis macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ _o_n_e | _t_w_o
+ text
+
+ _t_h_r_e_e text
+ _f_o_u_r _o_n_e _f_i_v_e
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Em/tag.out_html b/regress/mdoc/Em/tag.out_html
new file mode 100644
index 00000000..4046673f
--- /dev/null
+++ b/regress/mdoc/Em/tag.out_html
@@ -0,0 +1,10 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#one"><i class="Em" id="one">one</i></a> |
+ <a class="permalink" href="#two"><i class="Em" id="two">two</i></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#three"><i class="Em" id="three">three</i></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#four"><i class="Em" id="four">four</i></a>
+ <i class="Em">one</i>
+ <a class="permalink" href="#explicit"><i class="Em" id="explicit">five</i></a>
diff --git a/regress/mdoc/Em/tag.out_markdown b/regress/mdoc/Em/tag.out_markdown
new file mode 100644
index 00000000..4c107159
--- /dev/null
+++ b/regress/mdoc/Em/tag.out_markdown
@@ -0,0 +1,25 @@
+EM-TAG(1) - General Commands Manual
+
+# NAME
+
+**Em-tag** - tagging of emphasis macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+*one* | *two*
+
+> text
+
+*three*
+
+> text
+
+*four*
+*one*
+*five*
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Em/tag.out_tag b/regress/mdoc/Em/tag.out_tag
new file mode 100644
index 00000000..c2fbaf59
--- /dev/null
+++ b/regress/mdoc/Em/tag.out_tag
@@ -0,0 +1,5 @@
+one 9
+two 9
+three 12
+four 13
+explicit 13
diff --git a/regress/mdoc/Er/Makefile b/regress/mdoc/Er/Makefile
index 1be2a84a..55f6cc76 100644
--- a/regress/mdoc/Er/Makefile
+++ b/regress/mdoc/Er/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.4 2014/07/02 20:18:42 schwarze Exp $
+# $OpenBSD: Makefile,v 1.7 2020/03/13 00:31:05 schwarze Exp $
-REGRESS_TARGETS = noarg font
+REGRESS_TARGETS = noarg font tag
+TAG_TARGETS = tag
LINT_TARGETS = noarg
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Er/tag.in b/regress/mdoc/Er/tag.in
new file mode 100644
index 00000000..4227648b
--- /dev/null
+++ b/regress/mdoc/Er/tag.in
@@ -0,0 +1,21 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:05 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt ER-TAG 1
+.Os
+.Sh NAME
+.Nm Er-tag
+.Nd tagging of error number macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Er one
+text
+.El
+.Tg
+.Er two
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er ENOENT
+text
+.El
+ENDTEST
diff --git a/regress/mdoc/Er/tag.out_ascii b/regress/mdoc/Er/tag.out_ascii
new file mode 100644
index 00000000..62dbf9ca
--- /dev/null
+++ b/regress/mdoc/Er/tag.out_ascii
@@ -0,0 +1,16 @@
+ER-TAG(1) General Commands Manual ER-TAG(1)
+
+NNAAMMEE
+ EErr--ttaagg - tagging of error number macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ one text
+ two
+
+EERRRROORRSS
+ [ENOENT] text
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Er/tag.out_html b/regress/mdoc/Er/tag.out_html
new file mode 100644
index 00000000..a2c2978f
--- /dev/null
+++ b/regress/mdoc/Er/tag.out_html
@@ -0,0 +1,12 @@
+<dl class="Bl-tag">
+ <dt><code class="Er">one</code></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#two"><code class="Er" id="two">two</code></a>
+</section>
+<section class="Sh">
+<h1 class="Sh" id="ERRORS"><a class="permalink" href="#ERRORS">ERRORS</a></h1>
+<dl class="Bl-tag">
+ <dt>[<a class="permalink" href="#ENOENT"><code class="Er" id="ENOENT">ENOENT</code></a>]</dt>
+ <dd>text</dd>
+</dl>
diff --git a/regress/mdoc/Er/tag.out_markdown b/regress/mdoc/Er/tag.out_markdown
new file mode 100644
index 00000000..d11eb4b9
--- /dev/null
+++ b/regress/mdoc/Er/tag.out_markdown
@@ -0,0 +1,25 @@
+ER-TAG(1) - General Commands Manual
+
+# NAME
+
+**Er-tag** - tagging of error number macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+`one`
+
+> text
+
+`two`
+
+# ERRORS
+
+\[`ENOENT`]
+
+> text
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Er/tag.out_tag b/regress/mdoc/Er/tag.out_tag
new file mode 100644
index 00000000..b00a6718
--- /dev/null
+++ b/regress/mdoc/Er/tag.out_tag
@@ -0,0 +1,2 @@
+two 10
+ENOENT 13
diff --git a/regress/mdoc/Ev/Makefile b/regress/mdoc/Ev/Makefile
index cfe3ad0b..979e1826 100644
--- a/regress/mdoc/Ev/Makefile
+++ b/regress/mdoc/Ev/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.2 2014/07/02 20:18:42 schwarze Exp $
+# $OpenBSD: Makefile,v 1.5 2020/03/13 00:31:05 schwarze Exp $
-REGRESS_TARGETS = font noarg
+REGRESS_TARGETS = font noarg tag
+TAG_TARGETS = tag
LINT_TARGETS = noarg
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Ev/tag.in b/regress/mdoc/Ev/tag.in
new file mode 100644
index 00000000..2627fdeb
--- /dev/null
+++ b/regress/mdoc/Ev/tag.in
@@ -0,0 +1,21 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:05 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt EV-TAG 1
+.Os
+.Sh NAME
+.Nm Ev-tag
+.Nd tagging of environment variable macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Ev one | two
+text
+.It Xo
+.Ev three
+.Xc
+text
+.El
+.Tg
+.Ev four
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Ev/tag.out_ascii b/regress/mdoc/Ev/tag.out_ascii
new file mode 100644
index 00000000..5569d1c8
--- /dev/null
+++ b/regress/mdoc/Ev/tag.out_ascii
@@ -0,0 +1,17 @@
+EV-TAG(1) General Commands Manual EV-TAG(1)
+
+NNAAMMEE
+ EEvv--ttaagg - tagging of environment variable macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ one | two
+ text
+
+ three text
+ four
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Ev/tag.out_html b/regress/mdoc/Ev/tag.out_html
new file mode 100644
index 00000000..1e1c92b9
--- /dev/null
+++ b/regress/mdoc/Ev/tag.out_html
@@ -0,0 +1,9 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#one"><code class="Ev" id="one">one</code></a>
+ |
+ <a class="permalink" href="#two"><code class="Ev" id="two">two</code></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#three"><code class="Ev" id="three">three</code></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#four"><code class="Ev" id="four">four</code></a>
diff --git a/regress/mdoc/Ev/tag.out_markdown b/regress/mdoc/Ev/tag.out_markdown
new file mode 100644
index 00000000..f9a6712b
--- /dev/null
+++ b/regress/mdoc/Ev/tag.out_markdown
@@ -0,0 +1,23 @@
+EV-TAG(1) - General Commands Manual
+
+# NAME
+
+**Ev-tag** - tagging of environment variable macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+`one` | `two`
+
+> text
+
+`three`
+
+> text
+
+`four`
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Ev/tag.out_tag b/regress/mdoc/Ev/tag.out_tag
new file mode 100644
index 00000000..94f0cfb2
--- /dev/null
+++ b/regress/mdoc/Ev/tag.out_tag
@@ -0,0 +1,4 @@
+one 9
+two 9
+three 12
+four 13
diff --git a/regress/mdoc/Fl/Makefile b/regress/mdoc/Fl/Makefile
index e4e4a6bc..c061adef 100644
--- a/regress/mdoc/Fl/Makefile
+++ b/regress/mdoc/Fl/Makefile
@@ -1,6 +1,9 @@
-# $OpenBSD: Makefile,v 1.13 2020/02/27 01:25:58 schwarze Exp $
+# $OpenBSD: Makefile,v 1.14 2020/03/13 00:31:06 schwarze Exp $
-REGRESS_TARGETS = font multiarg noarg parsed punct spacing
+REGRESS_TARGETS = font multiarg noarg parsed punct spacing tag
+TAG_TARGETS = tag
LINT_TARGETS = punct
+HTML_TARGETS = tag
+SKIP_TMAN = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Fl/tag.in b/regress/mdoc/Fl/tag.in
new file mode 100644
index 00000000..f7e24b9f
--- /dev/null
+++ b/regress/mdoc/Fl/tag.in
@@ -0,0 +1,21 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:06 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt FL-TAG 1
+.Os
+.Sh NAME
+.Nm Fl-tag
+.Nd tagging of command line option macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Fl a | b
+text
+.It Xo
+.Fl c
+.Xc
+text
+.El
+.Tg
+.Fl d
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Fl/tag.out_ascii b/regress/mdoc/Fl/tag.out_ascii
new file mode 100644
index 00000000..4fcf803c
--- /dev/null
+++ b/regress/mdoc/Fl/tag.out_ascii
@@ -0,0 +1,17 @@
+FL-TAG(1) General Commands Manual FL-TAG(1)
+
+NNAAMMEE
+ FFll--ttaagg - tagging of command line option macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ --aa | --bb
+ text
+
+ --cc text
+ --dd
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Fl/tag.out_html b/regress/mdoc/Fl/tag.out_html
new file mode 100644
index 00000000..f67a30c0
--- /dev/null
+++ b/regress/mdoc/Fl/tag.out_html
@@ -0,0 +1,8 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#a"><code class="Fl" id="a">-a</code></a> |
+ <a class="permalink" href="#b"><code class="Fl" id="b">-b</code></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#c"><code class="Fl" id="c">-c</code></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a>
diff --git a/regress/mdoc/Fl/tag.out_markdown b/regress/mdoc/Fl/tag.out_markdown
new file mode 100644
index 00000000..1e7c6f14
--- /dev/null
+++ b/regress/mdoc/Fl/tag.out_markdown
@@ -0,0 +1,23 @@
+FL-TAG(1) - General Commands Manual
+
+# NAME
+
+**Fl-tag** - tagging of command line option macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+**-a** | **-b**
+
+> text
+
+**-c**
+
+> text
+
+**-d**
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Fl/tag.out_tag b/regress/mdoc/Fl/tag.out_tag
new file mode 100644
index 00000000..89a94151
--- /dev/null
+++ b/regress/mdoc/Fl/tag.out_tag
@@ -0,0 +1,4 @@
+a 9
+b 9
+c 12
+d 13
diff --git a/regress/mdoc/Fo/Makefile b/regress/mdoc/Fo/Makefile
index 7770e9a1..ce25ddf3 100644
--- a/regress/mdoc/Fo/Makefile
+++ b/regress/mdoc/Fo/Makefile
@@ -1,8 +1,10 @@
-# $OpenBSD: Makefile,v 1.17 2020/02/27 01:25:58 schwarze Exp $
+# $OpenBSD: Makefile,v 1.18 2020/03/13 00:31:06 schwarze Exp $
REGRESS_TARGETS = basic break eos font noarg nohead
-REGRESS_TARGETS += obsolete punct section transp warn
+REGRESS_TARGETS += obsolete punct section tag transp warn
+TAG_TARGETS = tag
LINT_TARGETS = noarg nohead obsolete punct warn
+HTML_TARGETS = tag
# groff-1.22.3 defects:
# - .Fo without an argument prints unbalanced parentheses
diff --git a/regress/mdoc/Fo/tag.in b/regress/mdoc/Fo/tag.in
new file mode 100644
index 00000000..29b84ffe
--- /dev/null
+++ b/regress/mdoc/Fo/tag.in
@@ -0,0 +1,29 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:06 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt FO-TAG 1
+.Os
+.Sh NAME
+.Nm Fo-tag
+.Nd tagging of function name macros
+.Sh DESCRIPTION
+BEGINTEST
+.Pp
+automatic:
+.Fn first
+and
+.Fn second
+.Pp
+.Fn second
+and
+.Fn first
+.Pp
+explicit:
+.Tg e3
+.Fn third
+and
+.Tg e4
+.Fo fourth
+.Fa void
+.Fc
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Fo/tag.out_ascii b/regress/mdoc/Fo/tag.out_ascii
new file mode 100644
index 00000000..505c0b7a
--- /dev/null
+++ b/regress/mdoc/Fo/tag.out_ascii
@@ -0,0 +1,17 @@
+FO-TAG(1) General Commands Manual FO-TAG(1)
+
+NNAAMMEE
+ FFoo--ttaagg - tagging of function name macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ automatic: ffiirrsstt() and sseeccoonndd()
+
+ sseeccoonndd() and ffiirrsstt()
+
+ explicit: tthhiirrdd() and ffoouurrtthh(_v_o_i_d)
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Fo/tag.out_html b/regress/mdoc/Fo/tag.out_html
new file mode 100644
index 00000000..b3508614
--- /dev/null
+++ b/regress/mdoc/Fo/tag.out_html
@@ -0,0 +1,9 @@
+<p class="Pp">automatic:
+ <a class="permalink" href="#first"><code class="Fn" id="first">first</code></a>()
+ and <code class="Fn">second</code>()</p>
+<p class="Pp"><a class="permalink" href="#second"><code class="Fn" id="second">second</code></a>()
+ and <code class="Fn">first</code>()</p>
+<p class="Pp">explicit:
+ <a class="permalink" href="#e3"><code class="Fn" id="e3">third</code></a>()
+ and
+ <a class="permalink" href="#e4"><code class="Fn" id="e4">fourth</code></a>(<var class="Fa">void</var>);</p>
diff --git a/regress/mdoc/Fo/tag.out_markdown b/regress/mdoc/Fo/tag.out_markdown
new file mode 100644
index 00000000..273a00d4
--- /dev/null
+++ b/regress/mdoc/Fo/tag.out_markdown
@@ -0,0 +1,27 @@
+FO-TAG(1) - General Commands Manual
+
+# NAME
+
+**Fo-tag** - tagging of function name macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+automatic:
+**first**()
+and
+**second**()
+
+**second**()
+and
+**first**()
+
+explicit:
+**third**()
+and
+**fourth**(*void*)
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Fo/tag.out_tag b/regress/mdoc/Fo/tag.out_tag
new file mode 100644
index 00000000..2387023c
--- /dev/null
+++ b/regress/mdoc/Fo/tag.out_tag
@@ -0,0 +1,4 @@
+first 9
+second 11
+e3 13
+e4 13
diff --git a/regress/mdoc/Ic/Makefile b/regress/mdoc/Ic/Makefile
index 33faa380..4a06ee20 100644
--- a/regress/mdoc/Ic/Makefile
+++ b/regress/mdoc/Ic/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.4 2014/07/02 20:18:42 schwarze Exp $
+# $OpenBSD: Makefile,v 1.8 2020/03/13 00:31:06 schwarze Exp $
-REGRESS_TARGETS = font noarg punct
+REGRESS_TARGETS = font noarg punct tag
+TAG_TARGETS = tag
LINT_TARGETS = noarg
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Ic/tag.in b/regress/mdoc/Ic/tag.in
new file mode 100644
index 00000000..9d21426e
--- /dev/null
+++ b/regress/mdoc/Ic/tag.in
@@ -0,0 +1,21 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:06 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt IC-TAG 1
+.Os
+.Sh NAME
+.Nm Ic-tag
+.Nd tagging of internal command macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Ic one | two
+text
+.It Xo
+.Ic three
+.Xc
+text
+.El
+.Tg
+.Ic four
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Ic/tag.out_ascii b/regress/mdoc/Ic/tag.out_ascii
new file mode 100644
index 00000000..513b2638
--- /dev/null
+++ b/regress/mdoc/Ic/tag.out_ascii
@@ -0,0 +1,17 @@
+IC-TAG(1) General Commands Manual IC-TAG(1)
+
+NNAAMMEE
+ IIcc--ttaagg - tagging of internal command macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ oonnee | ttwwoo
+ text
+
+ tthhrreeee text
+ ffoouurr
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Ic/tag.out_html b/regress/mdoc/Ic/tag.out_html
new file mode 100644
index 00000000..ad3cc1ef
--- /dev/null
+++ b/regress/mdoc/Ic/tag.out_html
@@ -0,0 +1,9 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#one"><code class="Ic" id="one">one</code></a>
+ |
+ <a class="permalink" href="#two"><code class="Ic" id="two">two</code></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#three"><code class="Ic" id="three">three</code></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#four"><code class="Ic" id="four">four</code></a>
diff --git a/regress/mdoc/Ic/tag.out_markdown b/regress/mdoc/Ic/tag.out_markdown
new file mode 100644
index 00000000..c0f13879
--- /dev/null
+++ b/regress/mdoc/Ic/tag.out_markdown
@@ -0,0 +1,23 @@
+IC-TAG(1) - General Commands Manual
+
+# NAME
+
+**Ic-tag** - tagging of internal command macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+**one** | **two**
+
+> text
+
+**three**
+
+> text
+
+**four**
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Ic/tag.out_tag b/regress/mdoc/Ic/tag.out_tag
new file mode 100644
index 00000000..94f0cfb2
--- /dev/null
+++ b/regress/mdoc/Ic/tag.out_tag
@@ -0,0 +1,4 @@
+one 9
+two 9
+three 12
+four 13
diff --git a/regress/mdoc/Li/Makefile b/regress/mdoc/Li/Makefile
index 7b2a3313..44dcf01c 100644
--- a/regress/mdoc/Li/Makefile
+++ b/regress/mdoc/Li/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.7 2018/12/21 16:58:49 schwarze Exp $
+# $OpenBSD: Makefile,v 1.8 2020/03/13 00:31:06 schwarze Exp $
-REGRESS_TARGETS = arg punct font
+REGRESS_TARGETS = arg punct font tag
+TAG_TARGETS = tag
LINT_TARGETS = punct
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Li/tag.in b/regress/mdoc/Li/tag.in
new file mode 100644
index 00000000..0ee9ce82
--- /dev/null
+++ b/regress/mdoc/Li/tag.in
@@ -0,0 +1,21 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:06 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt LI-TAG 1
+.Os
+.Sh NAME
+.Nm Li-tag
+.Nd tagging of literal font macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Li one | two
+text
+.It Xo
+.Li three
+.Xc
+text
+.El
+.Tg
+.Li four
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Li/tag.out_ascii b/regress/mdoc/Li/tag.out_ascii
new file mode 100644
index 00000000..e1afda6e
--- /dev/null
+++ b/regress/mdoc/Li/tag.out_ascii
@@ -0,0 +1,17 @@
+LI-TAG(1) General Commands Manual LI-TAG(1)
+
+NNAAMMEE
+ LLii--ttaagg - tagging of literal font macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ one | two
+ text
+
+ three text
+ four
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Li/tag.out_html b/regress/mdoc/Li/tag.out_html
new file mode 100644
index 00000000..3730caa6
--- /dev/null
+++ b/regress/mdoc/Li/tag.out_html
@@ -0,0 +1,9 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#one"><code class="Li" id="one">one</code></a>
+ |
+ <a class="permalink" href="#two"><code class="Li" id="two">two</code></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#three"><code class="Li" id="three">three</code></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#four"><code class="Li" id="four">four</code></a>
diff --git a/regress/mdoc/Li/tag.out_markdown b/regress/mdoc/Li/tag.out_markdown
new file mode 100644
index 00000000..6467e16d
--- /dev/null
+++ b/regress/mdoc/Li/tag.out_markdown
@@ -0,0 +1,23 @@
+LI-TAG(1) - General Commands Manual
+
+# NAME
+
+**Li-tag** - tagging of literal font macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+`one` | `two`
+
+> text
+
+`three`
+
+> text
+
+`four`
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Li/tag.out_tag b/regress/mdoc/Li/tag.out_tag
new file mode 100644
index 00000000..94f0cfb2
--- /dev/null
+++ b/regress/mdoc/Li/tag.out_tag
@@ -0,0 +1,4 @@
+one 9
+two 9
+three 12
+four 13
diff --git a/regress/mdoc/Makefile b/regress/mdoc/Makefile
index 86bdcb7a..83bd7fb7 100644
--- a/regress/mdoc/Makefile
+++ b/regress/mdoc/Makefile
@@ -1,9 +1,9 @@
-# $OpenBSD: Makefile,v 1.32 2017/01/11 17:39:45 schwarze Exp $
+# $OpenBSD: Makefile,v 1.33 2020/03/13 00:31:05 schwarze Exp $
SUBDIR = Ad An Ap Aq Ar At Bd Bf Bk Bl Brq Bx Cd Cm
SUBDIR += D1 Db Dd Dl Dq Dt Dv Em Eo Er Ev Ex Fd Fl Fo Ft Ic In Lb Li Lk
SUBDIR += Ms Mt Nd Nm No Ns Oo Op Os Ox Pa Pf Pp Qq Rs Rv
-SUBDIR += Sh Sm Sq St Sx Sy Tn Ud Ux Va Vt Xr blank break
+SUBDIR += Sh Sm Sq St Sx Sy Tg Tn Ud Ux Va Vt Xr blank break
.include "../Makefile.sub"
.include <bsd.subdir.mk>
diff --git a/regress/mdoc/Ms/Makefile b/regress/mdoc/Ms/Makefile
index 1be2a84a..618edd3f 100644
--- a/regress/mdoc/Ms/Makefile
+++ b/regress/mdoc/Ms/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.4 2014/07/02 20:18:42 schwarze Exp $
+# $OpenBSD: Makefile,v 1.8 2020/03/13 00:58:48 schwarze Exp $
-REGRESS_TARGETS = noarg font
+REGRESS_TARGETS = noarg font tag
+TAG_TARGETS = tag
LINT_TARGETS = noarg
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Ms/tag.in b/regress/mdoc/Ms/tag.in
new file mode 100644
index 00000000..da65a07e
--- /dev/null
+++ b/regress/mdoc/Ms/tag.in
@@ -0,0 +1,21 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:06 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt MS-TAG 1
+.Os
+.Sh NAME
+.Nm Ms-tag
+.Nd tagging of mathematical symbol macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Ms one | two
+text
+.It Xo
+.Ms three
+.Xc
+text
+.El
+.Tg
+.Ms four
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Ms/tag.out_ascii b/regress/mdoc/Ms/tag.out_ascii
new file mode 100644
index 00000000..2d06f4bf
--- /dev/null
+++ b/regress/mdoc/Ms/tag.out_ascii
@@ -0,0 +1,17 @@
+MS-TAG(1) General Commands Manual MS-TAG(1)
+
+NNAAMMEE
+ MMss--ttaagg - tagging of mathematical symbol macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ oonnee | ttwwoo
+ text
+
+ tthhrreeee text
+ ffoouurr
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Ms/tag.out_html b/regress/mdoc/Ms/tag.out_html
new file mode 100644
index 00000000..0f24b2ac
--- /dev/null
+++ b/regress/mdoc/Ms/tag.out_html
@@ -0,0 +1,9 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#one"><span class="Ms" id="one">one</span></a>
+ |
+ <a class="permalink" href="#two"><span class="Ms" id="two">two</span></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#three"><span class="Ms" id="three">three</span></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#four"><span class="Ms" id="four">four</span></a>
diff --git a/regress/mdoc/Ms/tag.out_markdown b/regress/mdoc/Ms/tag.out_markdown
new file mode 100644
index 00000000..055c3860
--- /dev/null
+++ b/regress/mdoc/Ms/tag.out_markdown
@@ -0,0 +1,23 @@
+MS-TAG(1) - General Commands Manual
+
+# NAME
+
+**Ms-tag** - tagging of mathematical symbol macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+**one** | **two**
+
+> text
+
+**three**
+
+> text
+
+**four**
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Ms/tag.out_tag b/regress/mdoc/Ms/tag.out_tag
new file mode 100644
index 00000000..94f0cfb2
--- /dev/null
+++ b/regress/mdoc/Ms/tag.out_tag
@@ -0,0 +1,4 @@
+one 9
+two 9
+three 12
+four 13
diff --git a/regress/mdoc/No/Makefile b/regress/mdoc/No/Makefile
index 3110bf2b..35aa4e1e 100644
--- a/regress/mdoc/No/Makefile
+++ b/regress/mdoc/No/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.4 2014/11/17 06:44:35 schwarze Exp $
+# $OpenBSD: Makefile,v 1.7 2020/03/13 00:31:06 schwarze Exp $
-REGRESS_TARGETS = punct spacing
+REGRESS_TARGETS = punct spacing tag
+TAG_TARGETS = tag
LINT_TARGETS = punct
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/No/punct.out_lint b/regress/mdoc/No/punct.out_lint
index e046da6a..6a65c3b4 100644
--- a/regress/mdoc/No/punct.out_lint
+++ b/regress/mdoc/No/punct.out_lint
@@ -23,3 +23,4 @@ mandoc: punct.in:72:7: WARNING: skipping empty macro: No
mandoc: punct.in:75:7: WARNING: skipping empty macro: No
mandoc: punct.in:76:7: WARNING: skipping empty macro: No
mandoc: punct.in:84:2: WARNING: skipping empty macro: No
+mandoc: punct.in:87:6: STYLE: no blank before trailing delimiter: No a.
diff --git a/regress/mdoc/No/tag.in b/regress/mdoc/No/tag.in
new file mode 100644
index 00000000..a9d4415f
--- /dev/null
+++ b/regress/mdoc/No/tag.in
@@ -0,0 +1,21 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:06 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt NO-TAG 1
+.Os
+.Sh NAME
+.Nm No-tag
+.Nd tagging of normal font macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It No one | two
+text
+.It Xo
+.No three
+.Xc
+text
+.El
+.Tg
+.No four
+.Pp
+ENDTEST
diff --git a/regress/mdoc/No/tag.out_ascii b/regress/mdoc/No/tag.out_ascii
new file mode 100644
index 00000000..44481b02
--- /dev/null
+++ b/regress/mdoc/No/tag.out_ascii
@@ -0,0 +1,17 @@
+NO-TAG(1) General Commands Manual NO-TAG(1)
+
+NNAAMMEE
+ NNoo--ttaagg - tagging of normal font macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ one | two
+ text
+
+ three text
+ four
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/No/tag.out_html b/regress/mdoc/No/tag.out_html
new file mode 100644
index 00000000..32696917
--- /dev/null
+++ b/regress/mdoc/No/tag.out_html
@@ -0,0 +1,9 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#one"><span class="No" id="one">one</span></a>
+ |
+ <a class="permalink" href="#two"><span class="No" id="two">two</span></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#three"><span class="No" id="three">three</span></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#four"><span class="No" id="four">four</span></a>
diff --git a/regress/mdoc/No/tag.out_markdown b/regress/mdoc/No/tag.out_markdown
new file mode 100644
index 00000000..1590c010
--- /dev/null
+++ b/regress/mdoc/No/tag.out_markdown
@@ -0,0 +1,23 @@
+NO-TAG(1) - General Commands Manual
+
+# NAME
+
+**No-tag** - tagging of normal font macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+one | two
+
+> text
+
+three
+
+> text
+
+four
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/No/tag.out_tag b/regress/mdoc/No/tag.out_tag
new file mode 100644
index 00000000..94f0cfb2
--- /dev/null
+++ b/regress/mdoc/No/tag.out_tag
@@ -0,0 +1,4 @@
+one 9
+two 9
+three 12
+four 13
diff --git a/regress/mdoc/Sy/Makefile b/regress/mdoc/Sy/Makefile
index 36c1859b..752d1382 100644
--- a/regress/mdoc/Sy/Makefile
+++ b/regress/mdoc/Sy/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.5 2014/11/17 06:44:35 schwarze Exp $
+# $OpenBSD: Makefile,v 1.8 2020/03/13 00:31:06 schwarze Exp $
-REGRESS_TARGETS = noarg font punct
+REGRESS_TARGETS = noarg font punct tag
+TAG_TARGETS = tag
LINT_TARGETS = noarg punct
+HTML_TARGETS = tag
.include <bsd.regress.mk>
diff --git a/regress/mdoc/Sy/tag.in b/regress/mdoc/Sy/tag.in
new file mode 100644
index 00000000..1294e17d
--- /dev/null
+++ b/regress/mdoc/Sy/tag.in
@@ -0,0 +1,23 @@
+.\" $OpenBSD: tag.in,v 1.1 2020/03/13 00:31:06 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt SY-TAG 1
+.Os
+.Sh NAME
+.Nm Sy-tag
+.Nd tagging of symbolic font macros
+.Sh DESCRIPTION
+BEGINTEST
+.Bl -tag -width Ds
+.It Sy one | two
+text
+.It Xo
+.Sy three
+.Xc
+text
+.El
+.Sy four
+.Sy one
+.Tg explicit
+.Sy five
+.Pp
+ENDTEST
diff --git a/regress/mdoc/Sy/tag.out_ascii b/regress/mdoc/Sy/tag.out_ascii
new file mode 100644
index 00000000..3b89cb41
--- /dev/null
+++ b/regress/mdoc/Sy/tag.out_ascii
@@ -0,0 +1,17 @@
+SY-TAG(1) General Commands Manual SY-TAG(1)
+
+NNAAMMEE
+ SSyy--ttaagg - tagging of symbolic font macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ oonnee | ttwwoo
+ text
+
+ tthhrreeee text
+ ffoouurr oonnee ffiivvee
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Sy/tag.out_html b/regress/mdoc/Sy/tag.out_html
new file mode 100644
index 00000000..69276a18
--- /dev/null
+++ b/regress/mdoc/Sy/tag.out_html
@@ -0,0 +1,10 @@
+<dl class="Bl-tag">
+ <dt><a class="permalink" href="#one"><b class="Sy" id="one">one</b></a> |
+ <a class="permalink" href="#two"><b class="Sy" id="two">two</b></a></dt>
+ <dd>text</dd>
+ <dt><a class="permalink" href="#three"><b class="Sy" id="three">three</b></a></dt>
+ <dd>text</dd>
+</dl>
+<a class="permalink" href="#four"><b class="Sy" id="four">four</b></a>
+ <b class="Sy">one</b>
+ <a class="permalink" href="#explicit"><b class="Sy" id="explicit">five</b></a>
diff --git a/regress/mdoc/Sy/tag.out_markdown b/regress/mdoc/Sy/tag.out_markdown
new file mode 100644
index 00000000..5382a5e2
--- /dev/null
+++ b/regress/mdoc/Sy/tag.out_markdown
@@ -0,0 +1,25 @@
+SY-TAG(1) - General Commands Manual
+
+# NAME
+
+**Sy-tag** - tagging of symbolic font macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+**one** | **two**
+
+> text
+
+**three**
+
+> text
+
+**four**
+**one**
+**five**
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Sy/tag.out_tag b/regress/mdoc/Sy/tag.out_tag
new file mode 100644
index 00000000..c2fbaf59
--- /dev/null
+++ b/regress/mdoc/Sy/tag.out_tag
@@ -0,0 +1,5 @@
+one 9
+two 9
+three 12
+four 13
+explicit 13
diff --git a/regress/mdoc/Tg/Makefile b/regress/mdoc/Tg/Makefile
new file mode 100644
index 00000000..7f538766
--- /dev/null
+++ b/regress/mdoc/Tg/Makefile
@@ -0,0 +1,8 @@
+# $OpenBSD: Makefile,v 1.1 2020/03/13 00:31:06 schwarze Exp $
+
+REGRESS_TARGETS = warn
+TAG_TARGETS = warn
+LINT_TARGETS = warn
+HTML_TARGETS = warn
+
+.include <bsd.regress.mk>
diff --git a/regress/mdoc/Tg/warn.in b/regress/mdoc/Tg/warn.in
new file mode 100644
index 00000000..e36f4506
--- /dev/null
+++ b/regress/mdoc/Tg/warn.in
@@ -0,0 +1,34 @@
+.\" $OpenBSD: warn.in,v 1.1 2020/03/13 00:31:06 schwarze Exp $
+.Dd $Mdocdate: March 13 2020 $
+.Dt TG-WARN 1
+.Os
+.Sh NAME
+.Nm Tg-warn
+.Nd warnings about tagging macros
+.Sh DESCRIPTION
+BEGINTEST
+.Pp
+.Tg start
+initial
+text
+.Tg
+.Ic macro
+.Tg "" ignored arguments
+too many
+.Tg \&badstart
+badstart
+.Tg badend\&
+badend
+.Tg "white space"
+whitespace
+.Tg sub
+.Tg double
+.Ss Subsection
+subtext
+.Tg examples
+.Sh EXAMPLES
+example
+text
+.Pp
+ENDTEST
+.Tg
diff --git a/regress/mdoc/Tg/warn.out_ascii b/regress/mdoc/Tg/warn.out_ascii
new file mode 100644
index 00000000..8dee9aed
--- /dev/null
+++ b/regress/mdoc/Tg/warn.out_ascii
@@ -0,0 +1,19 @@
+TG-WARN(1) General Commands Manual TG-WARN(1)
+
+NNAAMMEE
+ TTgg--wwaarrnn - warnings about tagging macros
+
+DDEESSCCRRIIPPTTIIOONN
+ BEGINTEST
+
+ initial text mmaaccrroo too many badstart badend whitespace
+
+ SSuubbsseeccttiioonn
+ subtext
+
+EEXXAAMMPPLLEESS
+ example text
+
+ ENDTEST
+
+OpenBSD March 13, 2020 OpenBSD
diff --git a/regress/mdoc/Tg/warn.out_html b/regress/mdoc/Tg/warn.out_html
new file mode 100644
index 00000000..b4d3cf74
--- /dev/null
+++ b/regress/mdoc/Tg/warn.out_html
@@ -0,0 +1,11 @@
+<p class="Pp"><mark id="start"></mark>initial text
+ <a class="permalink" href="#macro"><code class="Ic" id="macro">macro</code></a>
+ too many badstart badend whitespace <mark id="sub"></mark></p>
+<section class="Ss">
+<h2 class="Ss" id="double"><a class="permalink" href="#double">Subsection</a></h2>
+<p class="Pp">subtext</p>
+</section>
+</section>
+<section class="Sh">
+<h1 class="Sh" id="examples"><a class="permalink" href="#examples">EXAMPLES</a></h1>
+<p class="Pp">example text</p>
diff --git a/regress/mdoc/Tg/warn.out_lint b/regress/mdoc/Tg/warn.out_lint
new file mode 100644
index 00000000..39c1f541
--- /dev/null
+++ b/regress/mdoc/Tg/warn.out_lint
@@ -0,0 +1,6 @@
+mandoc: warn.in:16:2: WARNING: skipping empty macro: Tg
+mandoc: warn.in:16:8: ERROR: skipping excess arguments: Tg ... ignored
+mandoc: warn.in:18:5: ERROR: skipping tag containing whitespace: Tg \&badstart
+mandoc: warn.in:20:11: ERROR: skipping tag containing whitespace: Tg badend\&
+mandoc: warn.in:22:10: ERROR: skipping tag containing whitespace: Tg white space
+mandoc: warn.in:34:2: WARNING: skipping empty macro: Tg
diff --git a/regress/mdoc/Tg/warn.out_markdown b/regress/mdoc/Tg/warn.out_markdown
new file mode 100644
index 00000000..87204424
--- /dev/null
+++ b/regress/mdoc/Tg/warn.out_markdown
@@ -0,0 +1,30 @@
+TG-WARN(1) - General Commands Manual
+
+# NAME
+
+**Tg-warn** - warnings about tagging macros
+
+# DESCRIPTION
+
+BEGINTEST
+
+initial
+text
+**macro**
+too many
+badstart
+badend
+whitespace
+
+## Subsection
+
+subtext
+
+# EXAMPLES
+
+example
+text
+
+ENDTEST
+
+OpenBSD - March 13, 2020
diff --git a/regress/mdoc/Tg/warn.out_tag b/regress/mdoc/Tg/warn.out_tag
new file mode 100644
index 00000000..e1fc141c
--- /dev/null
+++ b/regress/mdoc/Tg/warn.out_tag
@@ -0,0 +1,5 @@
+start 9
+macro 9
+sub 9
+double 11
+examples 14
diff --git a/regress/regress.pl b/regress/regress.pl
index 5e7927cb..3c6e64df 100755
--- a/regress/regress.pl
+++ b/regress/regress.pl
@@ -1,8 +1,8 @@
#!/usr/bin/env perl
#
-# $Id: regress.pl,v 1.13 2020/01/08 10:37:53 schwarze Exp $
+# $Id: regress.pl,v 1.14 2020/03/13 15:32:31 schwarze Exp $
#
-# Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
+# Copyright (c) 2017, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -120,16 +120,17 @@ sub fail ($$) {
my $onlytest = shift // '';
for (@ARGV) {
- /^(all|ascii|utf8|man|html|markdown|lint|clean|verbose)$/
+ /^(all|ascii|tag|man|utf8|html|markdown|lint|clean|verbose)$/
or usage "$_: invalid modifier";
$targets{$_} = 1;
}
$targets{all} = 1
- unless $targets{ascii} || $targets{utf8} || $targets{man} ||
- $targets{html} || $targets{markdown} ||
+ unless $targets{ascii} || $targets{tag} || $targets{man} ||
+ $targets{utf8} || $targets{html} || $targets{markdown} ||
$targets{lint} || $targets{clean};
-$targets{ascii} = $targets{utf8} = $targets{man} = $targets{html} =
- $targets{markdown} = $targets{lint} = 1 if $targets{all};
+$targets{ascii} = $targets{tag} = $targets{man} = $targets{utf8} =
+ $targets{html} = $targets{markdown} = $targets{lint} = 1
+ if $targets{all};
# --- parse Makefiles --------------------------------------------------
@@ -157,7 +158,7 @@ sub parse_makefile ($%) {
}
my (@regress_tests, @utf8_tests, @lint_tests, @html_tests);
-my (%skip_ascii, %skip_man, %skip_markdown);
+my (%tag_tests, %skip_ascii, %skip_man, %skip_markdown);
foreach my $module (qw(roff char mdoc man tbl eqn)) {
my %modvars;
parse_makefile "$module/Makefile", \%modvars;
@@ -168,36 +169,41 @@ foreach my $module (qw(roff char mdoc man tbl eqn)) {
delete $subvars{GOPTS};
delete $subvars{SKIP_GROFF};
delete $subvars{SKIP_GROFF_ASCII};
- my @mandoc = ('../mandoc', split ' ', $subvars{MOPTS});
+ my @mopts = split ' ', $subvars{MOPTS};
delete $subvars{MOPTS};
my @regress_testnames;
+ if (defined $subvars{TAG_TARGETS}) {
+ $tag_tests{"$module/$subdir/$_"} = 1
+ for split ' ', $subvars{TAG_TARGETS};
+ delete $subvars{TAG_TARGETS};
+ }
if (defined $subvars{REGRESS_TARGETS}) {
push @regress_testnames,
split ' ', $subvars{REGRESS_TARGETS};
push @regress_tests, {
NAME => "$module/$subdir/$_",
- MANDOC => \@mandoc,
+ MOPTS => \@mopts,
} foreach @regress_testnames;
delete $subvars{REGRESS_TARGETS};
}
if (defined $subvars{UTF8_TARGETS}) {
push @utf8_tests, {
NAME => "$module/$subdir/$_",
- MANDOC => \@mandoc,
+ MOPTS => \@mopts,
} foreach split ' ', $subvars{UTF8_TARGETS};
delete $subvars{UTF8_TARGETS};
}
if (defined $subvars{HTML_TARGETS}) {
push @html_tests, {
NAME => "$module/$subdir/$_",
- MANDOC => \@mandoc,
+ MOPTS => \@mopts,
} foreach split ' ', $subvars{HTML_TARGETS};
delete $subvars{HTML_TARGETS};
}
if (defined $subvars{LINT_TARGETS}) {
push @lint_tests, {
NAME => "$module/$subdir/$_",
- MANDOC => \@mandoc,
+ MOPTS => \@mopts,
} foreach split ' ', $subvars{LINT_TARGETS};
delete $subvars{LINT_TARGETS};
}
@@ -243,22 +249,44 @@ foreach my $module (qw(roff char mdoc man tbl eqn)) {
my $count_total = 0;
my $count_ascii = 0;
+my $count_tag = 0;
my $count_man = 0;
my $count_rm = 0;
-if ($targets{ascii} || $targets{man}) {
- print "Running ascii and man tests ";
+if ($targets{ascii} || $targets{tag} || $targets{man}) {
+ print "Running ascii, tag, and man tests ";
print "...\n" if $targets{verbose};
}
for my $test (@regress_tests) {
my $i = "$test->{NAME}.in";
my $o = "$test->{NAME}.mandoc_ascii";
my $w = "$test->{NAME}.out_ascii";
- if ($targets{ascii} && !$skip_ascii{$test->{NAME}} &&
+ my $to = "$test->{NAME}.mandoc_tag";
+ my $tw = "$test->{NAME}.out_tag";
+ my $diff_ascii;
+ if ($targets{tag} && $tag_tests{$test->{NAME}} &&
$test->{NAME} =~ /^$onlytest/) {
- $count_ascii++;
+ $count_tag++;
$count_total++;
- sysout $o, @{$test->{MANDOC}}, qw(-I os=OpenBSD -T ascii), $i
+ local $ENV{MANPAGER} = "./copyless $test->{NAME}";
+ my @cmd = (qw(../man -l), @{$test->{MOPTS}},
+ qw(-I os=OpenBSD -T ascii), $i);
+ print "@cmd\n" if $targets{verbose};
+ system @cmd
+ and fail $test->{NAME}, 'tag:man';
+ system @diff, $tw, $to
+ and fail $test->{NAME}, 'tag:diff';
+ print "." unless $targets{verbose};
+ $diff_ascii = $targets{ascii};
+ } elsif ($targets{ascii} && !$skip_ascii{$test->{NAME}} &&
+ $test->{NAME} =~ /^$onlytest/) {
+ sysout $o, '../mandoc', @{$test->{MOPTS}},
+ qw(-I os=OpenBSD -T ascii), $i
and fail $test->{NAME}, 'ascii:mandoc';
+ $diff_ascii = 1;
+ }
+ if ($diff_ascii) {
+ $count_ascii++;
+ $count_total++;
system @diff, $w, $o
and fail $test->{NAME}, 'ascii:diff';
print "." unless $targets{verbose};
@@ -269,9 +297,10 @@ for my $test (@regress_tests) {
$test->{NAME} =~ /^$onlytest/) {
$count_man++;
$count_total++;
- sysout $m, @{$test->{MANDOC}}, qw(-I os=OpenBSD -T man), $i
+ sysout $m, '../mandoc', @{$test->{MOPTS}},
+ qw(-I os=OpenBSD -T man), $i
and fail $test->{NAME}, 'man:man';
- sysout $mo, @{$test->{MANDOC}},
+ sysout $mo, '../mandoc', @{$test->{MOPTS}},
qw(-man -I os=OpenBSD -T ascii -O mdoc), $m
and fail $test->{NAME}, 'man:mandoc';
system @diff, $w, $mo
@@ -279,13 +308,13 @@ for my $test (@regress_tests) {
print "." unless $targets{verbose};
}
if ($targets{clean}) {
- print "rm $o $m $mo\n" if $targets{verbose};
- $count_rm += unlink $o, $m, $mo;
+ print "rm $o $to $m $mo\n" if $targets{verbose};
+ $count_rm += unlink $o, $to, $m, $mo;
}
}
-if ($targets{ascii} || $targets{man}) {
- print "Number of ascii and man tests:" if $targets{verbose};
- print " $count_ascii + $count_man tests run.\n";
+if ($targets{ascii} || $targets{tag} || $targets{man}) {
+ print "Number of ascii, tag, and man tests:" if $targets{verbose};
+ print " $count_ascii + $count_tag + $count_man tests run.\n";
}
my $count_utf8 = 0;
@@ -300,7 +329,8 @@ for my $test (@utf8_tests) {
if ($targets{utf8} && $test->{NAME} =~ /^$onlytest/o) {
$count_utf8++;
$count_total++;
- sysout $o, @{$test->{MANDOC}}, qw(-I os=OpenBSD -T utf8), $i
+ sysout $o, '../mandoc', @{$test->{MOPTS}},
+ qw(-I os=OpenBSD -T utf8), $i
and fail $test->{NAME}, 'utf8:mandoc';
system @diff, $w, $o
and fail $test->{NAME}, 'utf8:diff';
@@ -328,7 +358,8 @@ for my $test (@html_tests) {
if ($targets{html} && $test->{NAME} =~ /^$onlytest/) {
$count_html++;
$count_total++;
- syshtml $o, @{$test->{MANDOC}}, qw(-T html), $i
+ syshtml $o, '../mandoc', @{$test->{MOPTS}},
+ qw(-T html), $i
and fail $test->{NAME}, 'html:mandoc';
system @diff, $w, $o
and fail $test->{NAME}, 'html:diff';
@@ -357,7 +388,7 @@ for my $test (@regress_tests) {
$test->{NAME} =~ /^$onlytest/) {
$count_markdown++;
$count_total++;
- sysout $o, @{$test->{MANDOC}},
+ sysout $o, '../mandoc', @{$test->{MOPTS}},
qw(-I os=OpenBSD -T markdown), $i
and fail $test->{NAME}, 'markdown:mandoc';
system @diff, $w, $o
@@ -386,7 +417,7 @@ for my $test (@lint_tests) {
if ($targets{lint} && $test->{NAME} =~ /^$onlytest/) {
$count_lint++;
$count_total++;
- syslint $o, @{$test->{MANDOC}},
+ syslint $o, '../mandoc', @{$test->{MOPTS}},
qw(-I os=OpenBSD -T lint -W all), $i
and fail $test->{NAME}, 'lint:mandoc';
system @diff, $w, $o
@@ -418,6 +449,7 @@ if ($count_total == 1) {
} elsif ($count_total) {
print "All $count_total tests OK:";
print " $count_ascii ascii" if $count_ascii;
+ print " $count_tag tag" if $count_tag;
print " $count_man man" if $count_man;
print " $count_utf8 utf8" if $count_utf8;
print " $count_html html" if $count_html;
diff --git a/regress/regress.pl.1 b/regress/regress.pl.1
index 04a56912..73db6118 100644
--- a/regress/regress.pl.1
+++ b/regress/regress.pl.1
@@ -1,6 +1,6 @@
-.\" $Id: regress.pl.1,v 1.4 2019/03/06 15:58:11 schwarze Exp $
+.\" $Id: regress.pl.1,v 1.5 2020/03/13 15:32:31 schwarze Exp $
.\"
-.\" Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2017, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,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: March 6 2019 $
+.Dd $Mdocdate: March 13 2020 $
.Dt REGRESS.PL 1
.Os
.Sh NAME
@@ -78,6 +78,8 @@ output mode.
Run subtests for
.Fl T Cm markdown
output mode.
+.It Cm tag
+Run subtests for automatic and manual tagging.
.It Cm utf8
Run subtests for
.Fl T Cm utf8
@@ -146,15 +148,6 @@ subdirectory of the regression suite is not included.
It uses a Makefile structure that differs vastly from the
rest of the suite.
.Sh BUGS
-On Oracle Solaris 11,
-.Xr diff 1
-does not support the
-.Fl a
-option.
-Delete that option from the following line in this script:
-.Pp
-.Dl my @diff = qw(diff -au);
-.Pp
The C library function
.Xr wcwidth 3
is known to be buggy on Solaris, which may cause failures in the
diff --git a/tag.c b/tag.c
index 6df91c8b..50e74f48 100644
--- a/tag.c
+++ b/tag.c
@@ -1,4 +1,4 @@
-/* $Id: tag.c,v 1.27 2020/01/20 10:37:15 schwarze Exp $ */
+/* $Id: tag.c,v 1.28 2020/03/13 15:32:29 schwarze Exp $ */
/*
* Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org>
*
@@ -13,134 +13,67 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Functions to tag syntax tree nodes.
+ * For internal use by mandoc(1) validation modules only.
*/
#include "config.h"
#include <sys/types.h>
#include <assert.h>
-#include <errno.h>
#include <limits.h>
-#include <signal.h>
#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include "mandoc_aux.h"
#include "mandoc_ohash.h"
-#include "mandoc.h"
+#include "roff.h"
#include "tag.h"
struct tag_entry {
- size_t *lines;
- size_t maxlines;
- size_t nlines;
+ struct roff_node **nodes;
+ size_t maxnodes;
+ size_t nnodes;
int prio;
char s[];
};
-static void tag_signal(int) __attribute__((__noreturn__));
-
static struct ohash tag_data;
-static struct tag_files tag_files;
/*
- * Prepare for using a pager.
- * Not all pagers are capable of using a tag file,
- * but for simplicity, create it anyway.
+ * Set up the ohash table to collect nodes
+ * where various marked-up terms are documented.
*/
-struct tag_files *
-tag_init(char *tagname)
+void
+tag_alloc(void)
{
- struct sigaction sa;
- int ofd;
-
- ofd = -1;
- tag_files.tfd = -1;
- tag_files.tcpgid = -1;
- tag_files.tagname = tagname;
-
- /* Clean up when dying from a signal. */
-
- memset(&sa, 0, sizeof(sa));
- sigfillset(&sa.sa_mask);
- sa.sa_handler = tag_signal;
- sigaction(SIGHUP, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGTERM, &sa, NULL);
-
- /*
- * POSIX requires that a process calling tcsetpgrp(3)
- * from the background gets a SIGTTOU signal.
- * In that case, do not stop.
- */
-
- sa.sa_handler = SIG_IGN;
- sigaction(SIGTTOU, &sa, NULL);
-
- /* Save the original standard output for use by the pager. */
-
- if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1) {
- mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno));
- goto fail;
- }
+ mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
+}
- /* Create both temporary output files. */
+void
+tag_free(void)
+{
+ struct tag_entry *entry;
+ unsigned int slot;
- (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX",
- sizeof(tag_files.ofn));
- (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
- sizeof(tag_files.tfn));
- if ((ofd = mkstemp(tag_files.ofn)) == -1) {
- mandoc_msg(MANDOCERR_MKSTEMP, 0, 0,
- "%s: %s", tag_files.ofn, strerror(errno));
- goto fail;
- }
- if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1) {
- mandoc_msg(MANDOCERR_MKSTEMP, 0, 0,
- "%s: %s", tag_files.tfn, strerror(errno));
- goto fail;
- }
- if (dup2(ofd, STDOUT_FILENO) == -1) {
- mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno));
- goto fail;
+ entry = ohash_first(&tag_data, &slot);
+ while (entry != NULL) {
+ free(entry->nodes);
+ free(entry);
+ entry = ohash_next(&tag_data, &slot);
}
- close(ofd);
-
- /*
- * Set up the ohash table to collect output line numbers
- * where various marked-up terms are documented.
- */
-
- mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
- return &tag_files;
-
-fail:
- tag_unlink();
- if (ofd != -1)
- close(ofd);
- if (tag_files.ofd != -1)
- close(tag_files.ofd);
- if (tag_files.tfd != -1)
- close(tag_files.tfd);
- *tag_files.ofn = '\0';
- *tag_files.tfn = '\0';
- tag_files.ofd = -1;
- tag_files.tfd = -1;
- tag_files.tagname = NULL;
- return NULL;
+ ohash_delete(&tag_data);
}
/*
- * Set the line number where a term is defined,
+ * Set a node where a term is defined,
* unless it is already defined at a lower priority.
*/
void
-tag_put(const char *s, int prio, size_t line)
+tag_put(const char *s, int prio, struct roff_node *n)
{
struct tag_entry *entry;
const char *se;
@@ -148,11 +81,14 @@ tag_put(const char *s, int prio, size_t line)
unsigned int slot;
assert(prio <= TAG_FALLBACK);
- if (tag_files.tfd <= 0)
- return;
- if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e'))
- s += 2;
+ if (s == NULL) {
+ if (n->child == NULL || n->child->type != ROFFT_TEXT)
+ return;
+ s = n->child->string;
+ if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e'))
+ s += 2;
+ }
/*
* Skip whitespace and escapes and whatever follows,
@@ -170,131 +106,67 @@ tag_put(const char *s, int prio, size_t line)
slot = ohash_qlookupi(&tag_data, s, &se);
entry = ohash_find(&tag_data, slot);
- if (entry == NULL) {
-
- /* Build a new entry. */
+ /* Build a new entry. */
+ if (entry == NULL) {
entry = mandoc_malloc(sizeof(*entry) + len + 1);
memcpy(entry->s, s, len);
entry->s[len] = '\0';
- entry->lines = NULL;
- entry->maxlines = entry->nlines = 0;
+ entry->nodes = NULL;
+ entry->maxnodes = entry->nnodes = 0;
ohash_insert(&tag_data, slot, entry);
+ }
- } else {
-
- /*
- * Lower priority numbers take precedence,
- * but TAG_FALLBACK is special.
- * A tag with priority TAG_FALLBACK is only used
- * if the tag occurs exactly once.
- */
+ /*
+ * Lower priority numbers take precedence.
+ * If a better entry is already present, ignore the new one.
+ */
- if (prio == TAG_FALLBACK) {
- if (entry->prio == TAG_FALLBACK)
- entry->prio = TAG_DELETE;
+ else if (entry->prio < prio)
return;
- }
- /* A better entry is already present, ignore the new one. */
-
- if (entry->prio < prio)
- return;
+ /*
+ * If the existing entry is worse, clear it.
+ * In addition, a tag with priority TAG_FALLBACK
+ * is only used if the tag occurs exactly once.
+ */
- /* The existing entry is worse, clear it. */
+ else if (entry->prio > prio || prio == TAG_FALLBACK) {
+ while (entry->nnodes > 0)
+ entry->nodes[--entry->nnodes]->flags &= ~NODE_ID;
- if (entry->prio > prio)
- entry->nlines = 0;
+ if (prio == TAG_FALLBACK) {
+ entry->prio = TAG_DELETE;
+ return;
+ }
}
- /* Remember the new line. */
+ /* Remember the new node. */
- if (entry->maxlines == entry->nlines) {
- entry->maxlines += 4;
- entry->lines = mandoc_reallocarray(entry->lines,
- entry->maxlines, sizeof(*entry->lines));
+ if (entry->maxnodes == entry->nnodes) {
+ entry->maxnodes += 4;
+ entry->nodes = mandoc_reallocarray(entry->nodes,
+ entry->maxnodes, sizeof(*entry->nodes));
}
- entry->lines[entry->nlines++] = line;
+ entry->nodes[entry->nnodes++] = n;
entry->prio = prio;
-}
-
-/*
- * Write out the tags file using the previously collected
- * information and clear the ohash table while going along.
- */
-void
-tag_write(void)
-{
- FILE *stream;
- struct tag_entry *entry;
- size_t i;
- unsigned int slot;
- int empty;
-
- if (tag_files.tfd <= 0)
- return;
- if (tag_files.tagname != NULL && ohash_find(&tag_data,
- ohash_qlookup(&tag_data, tag_files.tagname)) == NULL) {
- mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", tag_files.tagname);
- tag_files.tagname = NULL;
- }
- if ((stream = fdopen(tag_files.tfd, "w")) == NULL)
- mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno));
- empty = 1;
- entry = ohash_first(&tag_data, &slot);
- while (entry != NULL) {
- if (stream != NULL && entry->prio < TAG_DELETE) {
- for (i = 0; i < entry->nlines; i++) {
- fprintf(stream, "%s %s %zu\n",
- entry->s, tag_files.ofn, entry->lines[i]);
- empty = 0;
- }
- }
- free(entry->lines);
- free(entry);
- entry = ohash_next(&tag_data, &slot);
- }
- ohash_delete(&tag_data);
- if (stream != NULL)
- fclose(stream);
- else
- close(tag_files.tfd);
- tag_files.tfd = -1;
- if (empty) {
- unlink(tag_files.tfn);
- *tag_files.tfn = '\0';
+ n->flags |= NODE_ID;
+ if (n->child == NULL || n->child->string != s || *se != '\0') {
+ assert(n->string == NULL);
+ n->string = mandoc_strndup(s, len);
}
}
-void
-tag_unlink(void)
+enum tag_result
+tag_check(const char *test_tag)
{
- pid_t tc_pgid;
+ unsigned int slot;
- if (tag_files.tcpgid != -1) {
- tc_pgid = tcgetpgrp(tag_files.ofd);
- if (tc_pgid == tag_files.pager_pid ||
- tc_pgid == getpgid(0) ||
- getpgid(tc_pgid) == -1)
- (void)tcsetpgrp(tag_files.ofd, tag_files.tcpgid);
- }
- if (*tag_files.ofn != '\0')
- unlink(tag_files.ofn);
- if (*tag_files.tfn != '\0')
- unlink(tag_files.tfn);
-}
-
-static void
-tag_signal(int signum)
-{
- struct sigaction sa;
-
- tag_unlink();
- memset(&sa, 0, sizeof(sa));
- sigemptyset(&sa.sa_mask);
- sa.sa_handler = SIG_DFL;
- sigaction(signum, &sa, NULL);
- kill(getpid(), signum);
- /* NOTREACHED */
- _exit(1);
+ if (ohash_first(&tag_data, &slot) == NULL)
+ return TAG_EMPTY;
+ else if (test_tag != NULL && ohash_find(&tag_data,
+ ohash_qlookup(&tag_data, test_tag)) == NULL)
+ return TAG_MISS;
+ else
+ return TAG_OK;
}
diff --git a/tag.h b/tag.h
index 81ec35b4..0d37a66a 100644
--- a/tag.h
+++ b/tag.h
@@ -1,4 +1,4 @@
-/* $Id: tag.h,v 1.10 2020/01/20 10:37:15 schwarze Exp $ */
+/* $Id: tag.h,v 1.11 2020/03/13 15:32:29 schwarze Exp $ */
/*
* Copyright (c) 2015, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org>
*
@@ -13,6 +13,9 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internal interfaces to tag syntax tree nodes.
+ * For use by mandoc(1) validation modules only.
*/
/*
@@ -25,19 +28,17 @@
#define TAG_FALLBACK (INT_MAX - 1) /* Tag only used if unique. */
#define TAG_DELETE (INT_MAX) /* Tag not used at all. */
-
-struct tag_files {
- char ofn[20];
- char tfn[20];
- char *tagname;
- int ofd;
- int tfd;
- pid_t tcpgid;
- pid_t pager_pid;
+/*
+ * Return values of tag_check().
+ */
+enum tag_result {
+ TAG_OK, /* Argument exists as a tag. */
+ TAG_MISS, /* Argument not found. */
+ TAG_EMPTY /* No tag exists at all. */
};
-struct tag_files *tag_init(char *);
-void tag_put(const char *, int, size_t);
-void tag_write(void);
-void tag_unlink(void);
+void tag_alloc(void);
+void tag_put(const char *, int, struct roff_node *);
+enum tag_result tag_check(const char *);
+void tag_free(void);
diff --git a/term_tag.c b/term_tag.c
new file mode 100644
index 00000000..cb9fa16d
--- /dev/null
+++ b/term_tag.c
@@ -0,0 +1,206 @@
+/* $Id: term_tag.c,v 1.1 2020/03/13 15:32:29 schwarze Exp $ */
+/*
+ * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Functions to write a ctags(1) file.
+ * For use by the mandoc(1) ASCII and UTF-8 formatters only.
+ */
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mandoc.h"
+#include "roff.h"
+#include "tag.h"
+#include "term_tag.h"
+
+static void tag_signal(int) __attribute__((__noreturn__));
+
+static struct tag_files tag_files;
+
+
+/*
+ * Prepare for using a pager.
+ * Not all pagers are capable of using a tag file,
+ * but for simplicity, create it anyway.
+ */
+struct tag_files *
+term_tag_init(char *tagname)
+{
+ struct sigaction sa;
+ int ofd; /* In /tmp/, dup(2)ed to stdout. */
+ int tfd;
+
+ ofd = tfd = -1;
+ tag_files.tfs = NULL;
+ tag_files.tcpgid = -1;
+ tag_files.tagname = tagname;
+
+ /* Clean up when dying from a signal. */
+
+ memset(&sa, 0, sizeof(sa));
+ sigfillset(&sa.sa_mask);
+ sa.sa_handler = tag_signal;
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ /*
+ * POSIX requires that a process calling tcsetpgrp(3)
+ * from the background gets a SIGTTOU signal.
+ * In that case, do not stop.
+ */
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGTTOU, &sa, NULL);
+
+ /* Save the original standard output for use by the pager. */
+
+ if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1) {
+ mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno));
+ goto fail;
+ }
+
+ /* Create both temporary output files. */
+
+ (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX",
+ sizeof(tag_files.ofn));
+ (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
+ sizeof(tag_files.tfn));
+ if ((ofd = mkstemp(tag_files.ofn)) == -1) {
+ mandoc_msg(MANDOCERR_MKSTEMP, 0, 0,
+ "%s: %s", tag_files.ofn, strerror(errno));
+ goto fail;
+ }
+ if ((tfd = mkstemp(tag_files.tfn)) == -1) {
+ mandoc_msg(MANDOCERR_MKSTEMP, 0, 0,
+ "%s: %s", tag_files.tfn, strerror(errno));
+ goto fail;
+ }
+ if ((tag_files.tfs = fdopen(tfd, "w")) == NULL) {
+ mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno));
+ goto fail;
+ }
+ tfd = -1;
+ if (dup2(ofd, STDOUT_FILENO) == -1) {
+ mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno));
+ goto fail;
+ }
+ close(ofd);
+ return &tag_files;
+
+fail:
+ term_tag_unlink();
+ if (ofd != -1)
+ close(ofd);
+ if (tfd != -1)
+ close(tfd);
+ if (tag_files.ofd != -1) {
+ close(tag_files.ofd);
+ tag_files.ofd = -1;
+ }
+ tag_files.tagname = NULL;
+ return NULL;
+}
+
+void
+term_tag_write(struct roff_node *n, size_t line)
+{
+ const char *cp;
+ int len;
+
+ if (tag_files.tfs == NULL)
+ return;
+ if (n->string == NULL)
+ n = n->child;
+ cp = n->string;
+ if (cp[0] == '\\' && (cp[1] == '&' || cp[1] == 'e'))
+ cp += 2;
+ len = strcspn(cp, " \t\\");
+ fprintf(tag_files.tfs, "%.*s %s %zu\n",
+ len, cp, tag_files.ofn, line);
+}
+
+void
+term_tag_finish(void)
+{
+ if (tag_files.tfs == NULL)
+ return;
+ fclose(tag_files.tfs);
+ tag_files.tfs = NULL;
+ switch (tag_check(tag_files.tagname)) {
+ case TAG_EMPTY:
+ unlink(tag_files.tfn);
+ *tag_files.tfn = '\0';
+ /* FALLTHROUGH */
+ case TAG_MISS:
+ if (tag_files.tagname == NULL)
+ break;
+ mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", tag_files.tagname);
+ tag_files.tagname = NULL;
+ break;
+ case TAG_OK:
+ break;
+ }
+}
+
+void
+term_tag_unlink(void)
+{
+ pid_t tc_pgid;
+
+ if (tag_files.tcpgid != -1) {
+ tc_pgid = tcgetpgrp(tag_files.ofd);
+ if (tc_pgid == tag_files.pager_pid ||
+ tc_pgid == getpgid(0) ||
+ getpgid(tc_pgid) == -1)
+ (void)tcsetpgrp(tag_files.ofd, tag_files.tcpgid);
+ }
+ if (*tag_files.ofn != '\0') {
+ unlink(tag_files.ofn);
+ *tag_files.ofn = '\0';
+ }
+ if (*tag_files.tfn != '\0') {
+ unlink(tag_files.tfn);
+ *tag_files.tfn = '\0';
+ }
+ if (tag_files.tfs != NULL) {
+ fclose(tag_files.tfs);
+ tag_files.tfs = NULL;
+ }
+}
+
+static void
+tag_signal(int signum)
+{
+ struct sigaction sa;
+
+ term_tag_unlink();
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_DFL;
+ sigaction(signum, &sa, NULL);
+ kill(getpid(), signum);
+ /* NOTREACHED */
+ _exit(1);
+}
diff --git a/term_tag.h b/term_tag.h
new file mode 100644
index 00000000..1acde47b
--- /dev/null
+++ b/term_tag.h
@@ -0,0 +1,35 @@
+/* $Id: term_tag.h,v 1.1 2020/03/13 15:32:29 schwarze Exp $ */
+/*
+ * Copyright (c) 2015, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internal interfaces to write a ctags(1) file.
+ * For use by the mandoc(1) ASCII and UTF-8 formatters only.
+ */
+
+struct tag_files {
+ char ofn[20]; /* Output file name. */
+ char tfn[20]; /* Tag file name. */
+ char *tagname; /* Target specified with -O. */
+ FILE *tfs; /* Tag file object. */
+ int ofd; /* Original output file descriptor. */
+ pid_t tcpgid; /* Process group controlling the terminal. */
+ pid_t pager_pid; /* Process ID of the pager. */
+};
+
+
+struct tag_files *term_tag_init(char *);
+void term_tag_write(struct roff_node *, size_t);
+void term_tag_finish(void);
+void term_tag_unlink(void);
diff --git a/tree.c b/tree.c
index c39e89b8..f08695d3 100644
--- a/tree.c
+++ b/tree.c
@@ -1,7 +1,7 @@
-/* $Id: tree.c,v 1.86 2020/02/27 21:43:45 schwarze Exp $ */
+/* $Id: tree.c,v 1.87 2020/03/13 15:32:29 schwarze Exp $ */
/*
- * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,9 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Formatting module to let mandoc(1) show
+ * a human readable representation of the syntax tree.
*/
#include "config.h"
@@ -305,6 +308,11 @@ print_man(const struct roff_node *n, int indent)
putchar(')');
if (n->flags & NODE_EOS)
putchar('.');
+ if (n->flags & NODE_ID) {
+ printf(" ID");
+ if (n->string != NULL)
+ printf("=%s", n->string);
+ }
if (n->flags & NODE_NOFILL)
printf(" NOFILL");
putchar('\n');