summaryrefslogtreecommitdiff
path: root/usr.bin/mandoc
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@cvs.openbsd.org>2020-03-13 00:31:07 +0000
committerIngo Schwarze <schwarze@cvs.openbsd.org>2020-03-13 00:31:07 +0000
commit5a8dc5b083d2bade011c458f35eb196bcc221b20 (patch)
treea0cd3ee8339d3a6ae418ef4dde4c2f1257b682e3 /usr.bin/mandoc
parentc36c15a6ac30c2c450f6854e51dd494932b163d6 (diff)
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.
Diffstat (limited to 'usr.bin/mandoc')
-rw-r--r--usr.bin/mandoc/Makefile18
-rw-r--r--usr.bin/mandoc/html.c78
-rw-r--r--usr.bin/mandoc/html.h9
-rw-r--r--usr.bin/mandoc/main.c38
-rw-r--r--usr.bin/mandoc/man_html.c16
-rw-r--r--usr.bin/mandoc/man_term.c87
-rw-r--r--usr.bin/mandoc/man_validate.c108
-rw-r--r--usr.bin/mandoc/mdoc_html.c200
-rw-r--r--usr.bin/mandoc/mdoc_term.c284
-rw-r--r--usr.bin/mandoc/mdoc_validate.c200
-rw-r--r--usr.bin/mandoc/read.c13
-rw-r--r--usr.bin/mandoc/tag.c280
-rw-r--r--usr.bin/mandoc/tag.h29
-rw-r--r--usr.bin/mandoc/term_tag.c204
-rw-r--r--usr.bin/mandoc/term_tag.h35
-rw-r--r--usr.bin/mandoc/tree.c12
16 files changed, 823 insertions, 788 deletions
diff --git a/usr.bin/mandoc/Makefile b/usr.bin/mandoc/Makefile
index 8c5a8f7d46b..8d6fec5e060 100644
--- a/usr.bin/mandoc/Makefile
+++ b/usr.bin/mandoc/Makefile
@@ -1,19 +1,19 @@
-# $OpenBSD: Makefile,v 1.117 2019/03/06 12:26:30 schwarze Exp $
+# $OpenBSD: Makefile,v 1.118 2020/03/13 00:31:04 schwarze Exp $
.include <bsd.own.mk>
CFLAGS += -W -Wall -Wstrict-prototypes -Wno-unused-parameter
-DPADD += ${LIBUTIL}
+DPADD += ${LIBUTIL}
LDADD += -lutil -lz
SRCS= mandoc_aux.c mandoc_ohash.c mandoc.c mandoc_msg.c mandoc_xr.c \
- arch.c chars.c preconv.c read.c \
- roff.c roff_validate.c tbl.c tbl_opts.c tbl_layout.c tbl_data.c eqn.c
-SRCS+= mdoc_macro.c mdoc.c \
- mdoc_argv.c mdoc_state.c mdoc_validate.c att.c msec.c st.c
+ arch.c chars.c msec.c preconv.c read.c tag.c
+SRCS+= roff.c roff_validate.c tbl.c tbl_opts.c tbl_layout.c tbl_data.c eqn.c
+SRCS+= mdoc.c mdoc_argv.c mdoc_macro.c mdoc_state.c mdoc_validate.c \
+ att.c st.c
SRCS+= man_macro.c man.c man_validate.c
-SRCS+= main.c out.c tag.c tree.c
-SRCS+= term.c term_tab.c term_ascii.c term_ps.c
+SRCS+= main.c out.c tree.c
+SRCS+= term.c term_ascii.c term_ps.c term_tab.c term_tag.c
SRCS+= roff_term.c mdoc_term.c man_term.c eqn_term.c tbl_term.c
SRCS+= mdoc_man.c
SRCS+= html.c roff_html.c mdoc_html.c man_html.c eqn_html.c tbl_html.c
@@ -55,7 +55,7 @@ LIBROFF_OBJS = roff.o roff_validate.o eqn.o \
tbl.o tbl_data.o tbl_layout.o tbl_opts.o
LIBMANDOC_OBJS = ${LIBMDOC_OBJS} ${LIBMAN_OBJS} ${LIBROFF_OBJS} \
arch.o mandoc.o mandoc_aux.o mandoc_msg.o mandoc_ohash.o \
- mandoc_xr.o chars.o msec.o preconv.o read.o
+ mandoc_xr.o chars.o msec.o preconv.o read.o tag.o
HTML_OBJS = html.o roff_html.o mdoc_html.o man_html.o \
tbl_html.o eqn_html.o out.o
CGI_OBJS = ${LIBMANDOC_OBJS} ${HTML_OBJS} \
diff --git a/usr.bin/mandoc/html.c b/usr.bin/mandoc/html.c
index 102daabf3d9..c7c87c67a4e 100644
--- a/usr.bin/mandoc/html.c
+++ b/usr.bin/mandoc/html.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: html.c,v 1.134 2020/02/27 22:26:26 schwarze Exp $ */
+/* $OpenBSD: html.c,v 1.135 2020/03/13 00:31:04 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 <sys/types.h>
#include <sys/stat.h>
@@ -319,6 +322,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)
{
@@ -327,14 +342,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
@@ -734,6 +765,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/usr.bin/mandoc/html.h b/usr.bin/mandoc/html.h
index f0c9fe6d7a4..7bd55d48af5 100644
--- a/usr.bin/mandoc/html.h
+++ b/usr.bin/mandoc/html.h
@@ -1,7 +1,7 @@
-/* $OpenBSD: html.h,v 1.68 2020/01/19 17:59:01 schwarze Exp $ */
+/* $OpenBSD: html.h,v 1.69 2020/03/13 00:31:04 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/usr.bin/mandoc/main.c b/usr.bin/mandoc/main.c
index 5c72f60ce7b..8931df51ea1 100644
--- a/usr.bin/mandoc/main.c
+++ b/usr.bin/mandoc/main.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: main.c,v 1.247 2020/02/24 21:15:05 schwarze Exp $ */
+/* $OpenBSD: main.c,v 1.248 2020/03/13 00:31:04 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,8 +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.
+ *
+ * Main program for mandoc(1), man(1), apropos(1), whatis(1), and help(1).
*/
-
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/param.h> /* MACHINE */
@@ -46,7 +47,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"
@@ -577,7 +578,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)
@@ -587,7 +587,6 @@ main(int argc, char *argv[])
(void)fchdir(startdir);
close(startdir);
}
-
if (outst.outdata != NULL) {
switch (outst.outtype) {
case OUTT_HTML:
@@ -596,6 +595,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:
@@ -617,9 +617,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();
@@ -810,15 +809,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)
@@ -832,7 +832,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",
@@ -1251,7 +1251,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/usr.bin/mandoc/man_html.c b/usr.bin/mandoc/man_html.c
index 589b4114138..dd360649d6d 100644
--- a/usr.bin/mandoc/man_html.c
+++ b/usr.bin/mandoc/man_html.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: man_html.c,v 1.129 2020/02/27 01:25:57 schwarze Exp $ */
+/* $OpenBSD: man_html.c,v 1.130 2020/03/13 00:31:05 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 <sys/types.h>
@@ -308,7 +310,6 @@ static int
man_SH_pre(MAN_ARGS)
{
const char *class;
- char *id;
enum htmltag tag;
if (n->tok == MAN_SH) {
@@ -324,10 +325,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;
@@ -487,7 +486,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, "");
@@ -495,7 +494,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/usr.bin/mandoc/man_term.c b/usr.bin/mandoc/man_term.c
index 33ae377ffb9..3bf25adbf54 100644
--- a/usr.bin/mandoc/man_term.c
+++ b/usr.bin/mandoc/man_term.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: man_term.c,v 1.187 2020/02/27 01:25:57 schwarze Exp $ */
+/* $OpenBSD: man_term.c,v 1.188 2020/03/13 00:31:05 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 <sys/types.h>
@@ -30,7 +33,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 */
@@ -92,8 +95,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 */
@@ -537,10 +538,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;
@@ -620,18 +619,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;
@@ -911,6 +898,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:
/*
@@ -1157,60 +1147,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/usr.bin/mandoc/man_validate.c b/usr.bin/mandoc/man_validate.c
index 897fead07cf..4ab8c2cce11 100644
--- a/usr.bin/mandoc/man_validate.c
+++ b/usr.bin/mandoc/man_validate.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: man_validate.c,v 1.120 2020/01/19 16:16:32 schwarze Exp $ */
+/* $OpenBSD: man_validate.c,v 1.121 2020/03/13 00:31:05 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 <sys/types.h>
@@ -30,6 +32,7 @@
#include "mandoc_aux.h"
#include "mandoc.h"
#include "roff.h"
+#include "tag.h"
#include "man.h"
#include "libmandoc.h"
#include "roff_int.h"
@@ -43,6 +46,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);
@@ -52,6 +56,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);
@@ -60,8 +65,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 */
@@ -199,6 +204,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)
{
@@ -330,12 +395,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,
@@ -346,6 +413,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/usr.bin/mandoc/mdoc_html.c b/usr.bin/mandoc/mdoc_html.c
index 2dfc43a2123..e20126a9bd4 100644
--- a/usr.bin/mandoc/mdoc_html.c
+++ b/usr.bin/mandoc/mdoc_html.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: mdoc_html.c,v 1.210 2020/02/27 22:26:26 schwarze Exp $ */
+/* $OpenBSD: mdoc_html.c,v 1.211 2020/03/13 00:31:05 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 <sys/types.h>
@@ -45,7 +47,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);
@@ -70,9 +71,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);
@@ -81,20 +81,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);
@@ -137,19 +132,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 */
@@ -190,7 +185,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 */
@@ -505,20 +500,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
@@ -581,10 +567,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)
@@ -599,25 +583,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;
}
@@ -625,12 +604,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 &&
@@ -642,17 +617,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) {
@@ -924,7 +888,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;
}
@@ -1050,45 +1014,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;
@@ -1220,7 +1145,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);
@@ -1339,14 +1264,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;
}
@@ -1355,30 +1278,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;
@@ -1428,22 +1351,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");
@@ -1453,7 +1364,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;
@@ -1492,20 +1402,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;
}
@@ -1513,7 +1411,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;
}
@@ -1542,29 +1439,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/usr.bin/mandoc/mdoc_term.c b/usr.bin/mandoc/mdoc_term.c
index 54252473702..edbbec08797 100644
--- a/usr.bin/mandoc/mdoc_term.c
+++ b/usr.bin/mandoc/mdoc_term.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: mdoc_term.c,v 1.277 2020/02/27 21:38:27 schwarze Exp $ */
+/* $OpenBSD: mdoc_term.c,v 1.278 2020/03/13 00:31:05 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 <sys/types.h>
@@ -31,7 +34,7 @@
#include "mdoc.h"
#include "out.h"
#include "term.h"
-#include "tag.h"
+#include "term_tag.h"
#include "main.h"
struct termpair {
@@ -87,11 +90,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);
@@ -113,8 +113,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);
@@ -138,11 +136,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 */
@@ -189,7 +187,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 */
@@ -212,7 +210,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 */
@@ -244,8 +242,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)
@@ -298,7 +294,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;
@@ -339,8 +334,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
@@ -1006,24 +1000,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
@@ -1031,7 +1031,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, "\\-");
@@ -1217,24 +1216,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;
}
@@ -1242,7 +1239,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);
}
@@ -1250,7 +1246,6 @@ termp_fd_pre(DECL_ARGS)
static void
termp_fd_post(DECL_ARGS)
{
-
term_newln(p);
}
@@ -1271,23 +1266,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;
@@ -1298,7 +1284,6 @@ termp_sh_pre(DECL_ARGS)
static void
termp_sh_post(DECL_ARGS)
{
-
switch (n->type) {
case ROFFT_HEAD:
term_newln(p);
@@ -1315,15 +1300,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);
@@ -1337,11 +1320,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
@@ -1350,11 +1330,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) {
@@ -1368,9 +1346,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);
@@ -1405,7 +1380,6 @@ termp_fn_pre(DECL_ARGS)
term_word(p, ";");
term_flushln(p);
}
-
return 0;
}
@@ -1414,10 +1388,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;
@@ -1528,9 +1501,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);
@@ -1551,21 +1523,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, "<");
@@ -1573,7 +1534,6 @@ termp_in_pre(DECL_ARGS)
term_word(p, "<");
term_fontpush(p, TERMFONT_UNDER);
}
-
p->flags |= TERMP_NOSPACE;
return 1;
}
@@ -1581,21 +1541,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;
}
@@ -1603,14 +1559,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;
@@ -1767,17 +1721,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;
@@ -1785,7 +1737,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);
@@ -1794,30 +1746,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);
@@ -1827,29 +1770,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;
@@ -1863,7 +1807,6 @@ termp_sm_pre(DECL_ARGS)
static int
termp_ap_pre(DECL_ARGS)
{
-
p->flags |= TERMP_NOSPACE;
term_word(p, "'");
p->flags |= TERMP_NOSPACE;
@@ -1902,8 +1845,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;
}
@@ -1953,7 +1894,6 @@ termp_lk_pre(DECL_ARGS)
static int
termp_bk_pre(DECL_ARGS)
{
-
switch (n->type) {
case ROFFT_BLOCK:
break;
@@ -1966,107 +1906,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/usr.bin/mandoc/mdoc_validate.c b/usr.bin/mandoc/mdoc_validate.c
index 033a368cd75..c5497fb87f2 100644
--- a/usr.bin/mandoc/mdoc_validate.c
+++ b/usr.bin/mandoc/mdoc_validate.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: mdoc_validate.c,v 1.294 2020/02/27 21:38:27 schwarze Exp $ */
+/* $OpenBSD: mdoc_validate.c,v 1.295 2020/03/13 00:31:05 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 <sys/types.h>
#ifndef OSNAME
@@ -33,6 +35,7 @@
#include "mandoc.h"
#include "mandoc_xr.h"
#include "roff.h"
+#include "tag.h"
#include "mdoc.h"
#include "libmandoc.h"
#include "roff_int.h"
@@ -80,7 +83,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);
@@ -111,6 +116,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);
@@ -135,19 +141,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 */
@@ -185,11 +191,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 */
@@ -208,7 +214,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 */
@@ -285,6 +291,8 @@ static const char * const secnames[SEC__MAX] = {
NULL
};
+static int fn_prio = TAG_STRONG;
+
/* Validate the subtree rooted at mdoc->last. */
void
@@ -1079,8 +1087,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;
@@ -1091,30 +1102,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)
@@ -1122,32 +1129,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
@@ -1242,28 +1261,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);
}
@@ -1427,38 +1450,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
@@ -1512,18 +1526,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;
}
@@ -1620,8 +1670,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;
@@ -2137,7 +2187,6 @@ post_sx(POST_ARGS)
static void
post_sh(POST_ARGS)
{
-
post_ignpar(mdoc);
switch (mdoc->last->type) {
@@ -2369,6 +2418,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. */
@@ -2540,6 +2591,7 @@ post_par(POST_ARGS)
{
struct roff_node *np;
+ fn_prio = TAG_STRONG;
post_prevpar(mdoc);
np = mdoc->last;
diff --git a/usr.bin/mandoc/read.c b/usr.bin/mandoc/read.c
index 6dfe2003abd..a32274c5d0a 100644
--- a/usr.bin/mandoc/read.c
+++ b/usr.bin/mandoc/read.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: read.c,v 1.185 2019/07/10 19:38:56 schwarze Exp $ */
+/* $OpenBSD: read.c,v 1.186 2020/03/13 00:31:05 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 <sys/types.h>
#include <sys/mman.h>
@@ -34,6 +40,7 @@
#include "mandoc_aux.h"
#include "mandoc.h"
#include "roff.h"
+#include "tag.h"
#include "mdoc.h"
#include "man.h"
#include "mandoc_parse.h"
@@ -662,6 +669,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;
}
@@ -678,6 +686,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/usr.bin/mandoc/tag.c b/usr.bin/mandoc/tag.c
index 5c0854315dd..8bdf9943597 100644
--- a/usr.bin/mandoc/tag.c
+++ b/usr.bin/mandoc/tag.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tag.c,v 1.27 2020/01/20 10:29:31 schwarze Exp $ */
+/* $OpenBSD: tag.c,v 1.28 2020/03/13 00:31:05 schwarze Exp $ */
/*
* Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org>
*
@@ -13,132 +13,65 @@
* 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 <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;
@@ -146,11 +79,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,
@@ -168,131 +104,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/usr.bin/mandoc/tag.h b/usr.bin/mandoc/tag.h
index a4a4128eef0..494b72bb2ca 100644
--- a/usr.bin/mandoc/tag.h
+++ b/usr.bin/mandoc/tag.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tag.h,v 1.10 2020/01/20 10:29:31 schwarze Exp $ */
+/* $OpenBSD: tag.h,v 1.11 2020/03/13 00:31:05 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/usr.bin/mandoc/term_tag.c b/usr.bin/mandoc/term_tag.c
new file mode 100644
index 00000000000..9a15b562036
--- /dev/null
+++ b/usr.bin/mandoc/term_tag.c
@@ -0,0 +1,204 @@
+/* $OpenBSD: term_tag.c,v 1.1 2020/03/13 00:31:05 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 <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/usr.bin/mandoc/term_tag.h b/usr.bin/mandoc/term_tag.h
new file mode 100644
index 00000000000..d1f31c4fba4
--- /dev/null
+++ b/usr.bin/mandoc/term_tag.h
@@ -0,0 +1,35 @@
+/* $OpenBSD: term_tag.h,v 1.1 2020/03/13 00:31:05 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/usr.bin/mandoc/tree.c b/usr.bin/mandoc/tree.c
index cc123ccf308..5d82f866056 100644
--- a/usr.bin/mandoc/tree.c
+++ b/usr.bin/mandoc/tree.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: tree.c,v 1.53 2020/02/27 21:38:27 schwarze Exp $ */
+/* $OpenBSD: tree.c,v 1.54 2020/03/13 00:31:05 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 <sys/types.h>
@@ -303,6 +306,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');