From 5a8dc5b083d2bade011c458f35eb196bcc221b20 Mon Sep 17 00:00:00 2001 From: Ingo Schwarze Date: Fri, 13 Mar 2020 00:31:07 +0000 Subject: 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 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. --- usr.bin/mandoc/Makefile | 18 +-- usr.bin/mandoc/html.c | 78 +++++++++-- usr.bin/mandoc/html.h | 9 +- usr.bin/mandoc/main.c | 38 +++--- usr.bin/mandoc/man_html.c | 16 +-- usr.bin/mandoc/man_term.c | 87 ++----------- usr.bin/mandoc/man_validate.c | 108 +++++++++++++++- usr.bin/mandoc/mdoc_html.c | 200 ++++++----------------------- usr.bin/mandoc/mdoc_term.c | 284 ++++++++++++----------------------------- usr.bin/mandoc/mdoc_validate.c | 200 ++++++++++++++++++----------- usr.bin/mandoc/read.c | 13 +- usr.bin/mandoc/tag.c | 280 +++++++++++----------------------------- usr.bin/mandoc/tag.h | 29 +++-- usr.bin/mandoc/term_tag.c | 204 +++++++++++++++++++++++++++++ usr.bin/mandoc/term_tag.h | 35 +++++ usr.bin/mandoc/tree.c | 12 +- 16 files changed, 823 insertions(+), 788 deletions(-) create mode 100644 usr.bin/mandoc/term_tag.c create mode 100644 usr.bin/mandoc/term_tag.h (limited to 'usr.bin/mandoc') 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 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 * Copyright (c) 2011-2015, 2017-2020 Ingo Schwarze + * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons * * 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 #include @@ -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 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 * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons - * Copyright (c) 2017, 2018, 2019 Ingo Schwarze * * 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 * Copyright (c) 2010-2012, 2014-2020 Ingo Schwarze + * Copyright (c) 2008-2012 Kristaps Dzonsons * Copyright (c) 2010 Joerg Sonnenberger * * 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 #include #include /* 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 * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze + * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons * * 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 @@ -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 * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze + * Copyright (c) 2008-2012 Kristaps Dzonsons * * 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 @@ -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 * Copyright (c) 2010, 2012-2020 Ingo Schwarze + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * * 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 @@ -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 * Copyright (c) 2014-2020 Ingo Schwarze + * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons * * 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 @@ -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 && @@ -641,17 +616,6 @@ mdoc_fl_pre(MDOC_ARGS) return 1; } -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) { @@ -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; } @@ -1049,45 +1013,6 @@ mdoc_cd_pre(MDOC_ARGS) return 1; } -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) { @@ -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,21 +1351,9 @@ 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) { @@ -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; @@ -1491,21 +1401,9 @@ mdoc_bf_pre(MDOC_ARGS) return 1; } -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 * Copyright (c) 2010, 2012-2020 Ingo Schwarze + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2013 Franco Fichtner * * 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 @@ -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); @@ -1550,22 +1522,11 @@ termp_ss_post(DECL_ARGS) term_newln(p); } -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,106 +1906,46 @@ 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) { 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 * Copyright (c) 2010-2020 Ingo Schwarze + * Copyright (c) 2008-2012 Kristaps Dzonsons * Copyright (c) 2010 Joerg Sonnenberger * * 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 #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 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 * Copyright (c) 2010-2019 Ingo Schwarze + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2010, 2012 Joerg Sonnenberger * * 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 #include @@ -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 * @@ -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 #include -#include #include -#include #include -#include -#include #include #include -#include #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 * @@ -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 + * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze + * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons * * 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 @@ -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'); -- cgit v1.2.3