diff options
| author | Michal Nowak <mnowak@startmail.com> | 2019-05-30 07:50:30 +0200 |
|---|---|---|
| committer | Dan McDonald <danmcd@joyent.com> | 2019-06-21 14:59:14 -0400 |
| commit | cec8643b41ebefad6c677010fc784dc4bb0550f3 (patch) | |
| tree | c4eca564abb29cbe5a704a1a1b182a6d0cb526fc /usr/src/cmd | |
| parent | 07188943efdbeedd24142a14db7384af1478ba54 (diff) | |
| download | illumos-joyent-cec8643b41ebefad6c677010fc784dc4bb0550f3.tar.gz | |
11190 Update mandoc to 1.14.5
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Gergő Doma <domag02@gmail.com>
Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src/cmd')
75 files changed, 5927 insertions, 4137 deletions
diff --git a/usr/src/cmd/mandoc/Makefile.common b/usr/src/cmd/mandoc/Makefile.common index f8834bb4a8..42d73be9f5 100644 --- a/usr/src/cmd/mandoc/Makefile.common +++ b/usr/src/cmd/mandoc/Makefile.common @@ -17,7 +17,8 @@ PROG= mandoc -OBJS= att.o \ +OBJS= arch.o \ + att.o \ chars.o \ dba.o \ dba_array.o \ @@ -39,6 +40,7 @@ OBJS= att.o \ man_validate.o \ mandoc.o \ mandoc_aux.o \ + mandoc_msg.o \ mandoc_ohash.o \ mandoc_xr.o \ mandocdb.o \ diff --git a/usr/src/cmd/mandoc/THIRDPARTYLICENSE b/usr/src/cmd/mandoc/THIRDPARTYLICENSE index 34420d2920..45ac298093 100644 --- a/usr/src/cmd/mandoc/THIRDPARTYLICENSE +++ b/usr/src/cmd/mandoc/THIRDPARTYLICENSE @@ -1,24 +1,25 @@ -$Id: LICENSE,v 1.17 2017/06/23 15:58:14 schwarze Exp $ +$Id: LICENSE,v 1.21 2018/11/26 17:11:11 schwarze Exp $ -With the exceptions noted below, all code and documentation -contained in the mandoc toolkit is protected by the Copyright -of the following developers: +With the exceptions noted below, all non-trivial files contained +in the mandoc toolkit are protected by the Copyright of the following +developers: Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> -Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> +Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org> +Copyright (c) 1999, 2004, 2017 Marc Espie <espie@openbsd.org> Copyright (c) 2009, 2010, 2011, 2012 Joerg Sonnenberger <joerg@netbsd.org> Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> Copyright (c) 2014 Baptiste Daroussin <bapt@freebsd.org> Copyright (c) 2016 Ed Maste <emaste@freebsd.org> Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> -Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> +Copyright (c) 2017 Anthony Bentley <bentley@openbsd.org> Copyright (c) 1998, 2004, 2010 Todd C. Miller <Todd.Miller@courtesan.com> Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net> Copyright (c) 2004 Ted Unangst <tedu@openbsd.org> Copyright (c) 1994 Christos Zoulas <christos@netbsd.org> Copyright (c) 2003, 2007, 2008, 2014 Jason McIntyre <jmc@openbsd.org> -See the individual source files for information about who contributed +See the individual files for information about who contributed to which file during which years. diff --git a/usr/src/cmd/mandoc/arch.c b/usr/src/cmd/mandoc/arch.c new file mode 100644 index 0000000000..56b937ec84 --- /dev/null +++ b/usr/src/cmd/mandoc/arch.c @@ -0,0 +1,54 @@ +/* $Id: arch.c,v 1.14 2019/03/04 13:01:57 schwarze Exp $ */ +/* + * Copyright (c) 2017, 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 + * 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. + */ +#include "config.h" + +#include <string.h> + +#include "roff.h" + +int +arch_valid(const char *arch, enum mandoc_os os) +{ + const char *openbsd_arch[] = { + "alpha", "amd64", "arm64", "armv7", "hppa", "i386", + "landisk", "loongson", "luna88k", "macppc", "mips64", + "octeon", "sgi", "socppc", "sparc64", NULL + }; + const char *netbsd_arch[] = { + "acorn26", "acorn32", "algor", "alpha", "amiga", + "arc", "atari", + "bebox", "cats", "cesfic", "cobalt", "dreamcast", + "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5", + "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa", + "i386", "ibmnws", "luna68k", + "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc", + "netwinder", "news68k", "newsmips", "next68k", + "pc532", "playstation2", "pmax", "pmppc", "prep", + "sandpoint", "sbmips", "sgimips", "shark", + "sparc", "sparc64", "sun2", "sun3", + "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL + }; + const char **arches[] = { NULL, netbsd_arch, openbsd_arch }; + const char **arch_p; + + if ((arch_p = arches[os]) == NULL) + return 1; + for (; *arch_p != NULL; arch_p++) + if (strcmp(*arch_p, arch) == 0) + return 1; + return 0; +} diff --git a/usr/src/cmd/mandoc/att.c b/usr/src/cmd/mandoc/att.c index dd7f2a0d77..5575bed54a 100644 --- a/usr/src/cmd/mandoc/att.c +++ b/usr/src/cmd/mandoc/att.c @@ -1,4 +1,4 @@ -/* $Id: att.c,v 1.16 2017/06/24 14:38:32 schwarze Exp $ */ +/* $Id: att.c,v 1.18 2018/12/13 11:55:46 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -19,9 +19,7 @@ #include <sys/types.h> #include <string.h> -#include "mandoc.h" #include "roff.h" -#include "mdoc.h" #include "libmdoc.h" #define LINE(x, y) \ diff --git a/usr/src/cmd/mandoc/chars.c b/usr/src/cmd/mandoc/chars.c index fb9ded8bae..24166dbd9f 100644 --- a/usr/src/cmd/mandoc/chars.c +++ b/usr/src/cmd/mandoc/chars.c @@ -1,7 +1,7 @@ -/* $Id: chars.c,v 1.73 2017/08/23 13:01:29 schwarze Exp $ */ +/* $Id: chars.c,v 1.78 2018/12/15 19:30:26 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2014,2015,2017,2018 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 @@ -23,6 +23,7 @@ #include <ctype.h> #include <stddef.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> @@ -47,20 +48,13 @@ static struct ln lines[] = { { " ", ascii_nbrsp, 0x00a0 }, { "~", ascii_nbrsp, 0x00a0 }, { "0", " ", 0x2002 }, - { "|", "", 0 }, - { "^", "", 0 }, - { "&", "", 0 }, - { "%", "", 0 }, { ":", ascii_break, 0 }, - /* XXX The following three do not really belong here. */ - { "t", "", 0 }, - { "c", "", 0 }, - { "}", "", 0 }, /* Lines. */ { "ba", "|", 0x007c }, { "br", "|", 0x2502 }, { "ul", "_", 0x005f }, + { "_", "_", 0x005f }, { "ru", "_", 0x005f }, { "rn", "-", 0x203e }, { "bb", "|", 0x00a6 }, @@ -82,10 +76,10 @@ static struct ln lines[] = { { "sh", "#", 0x0023 }, { "CR", "<cr>", 0x21b5 }, { "OK", "\\/", 0x2713 }, - { "CL", "<club>", 0x2663 }, - { "SP", "<spade>", 0x2660 }, - { "HE", "<heart>", 0x2665 }, - { "DI", "<diamond>", 0x2666 }, + { "CL", "C", 0x2663 }, + { "SP", "S", 0x2660 }, + { "HE", "H", 0x2665 }, + { "DI", "D", 0x2666 }, /* Legal symbols. */ { "co", "(C)", 0x00a9 }, @@ -240,7 +234,7 @@ static struct ln lines[] = { { "Ah", "<Aleph>", 0x2135 }, { "Im", "<Im>", 0x2111 }, { "Re", "<Re>", 0x211c }, - { "wp", "P", 0x2118 }, + { "wp", "p", 0x2118 }, { "pd", "<del>", 0x2202 }, { "-h", "/h", 0x210f }, { "hbar", "/h", 0x210f }, @@ -287,6 +281,7 @@ static struct ln lines[] = { { "ho", ",", 0x02db }, { "ha", "^", 0x005e }, { "ti", "~", 0x007e }, + { "u02DC", "~", 0x02dc }, /* Accented letters. */ { "'A", "'\bA", 0x00c1 }, @@ -294,11 +289,13 @@ static struct ln lines[] = { { "'I", "'\bI", 0x00cd }, { "'O", "'\bO", 0x00d3 }, { "'U", "'\bU", 0x00da }, + { "'Y", "'\bY", 0x00dd }, { "'a", "'\ba", 0x00e1 }, { "'e", "'\be", 0x00e9 }, { "'i", "'\bi", 0x00ed }, { "'o", "'\bo", 0x00f3 }, { "'u", "'\bu", 0x00fa }, + { "'y", "'\by", 0x00fd }, { "`A", "`\bA", 0x00c0 }, { "`E", "`\bE", 0x00c8 }, { "`I", "`\bI", 0x00cc }, @@ -359,7 +356,7 @@ static struct ln lines[] = { { "Eu", "EUR", 0x20ac }, { "eu", "EUR", 0x20ac }, { "Ye", "=\bY", 0x00a5 }, - { "Po", "GBP", 0x00a3 }, + { "Po", "-\bL", 0x00a3 }, { "Cs", "o\bx", 0x00a4 }, { "Fn", ",\bf", 0x0192 }, @@ -460,7 +457,7 @@ mchars_spec2cp(const char *p, size_t sz) end = p + sz; ln = ohash_find(&mchars, ohash_qlookupi(&mchars, p, &end)); - return ln != NULL ? ln->unicode : sz == 1 ? (unsigned char)*p : -1; + return ln != NULL ? ln->unicode : -1; } int @@ -490,10 +487,8 @@ mchars_spec2str(const char *p, size_t sz, size_t *rsz) end = p + sz; ln = ohash_find(&mchars, ohash_qlookupi(&mchars, p, &end)); - if (ln == NULL) { - *rsz = 1; - return sz == 1 ? p : NULL; - } + if (ln == NULL) + return NULL; *rsz = strlen(ln->ascii); return ln->ascii; diff --git a/usr/src/cmd/mandoc/config.h b/usr/src/cmd/mandoc/config.h index 75f62a9339..3ae0245c08 100644 --- a/usr/src/cmd/mandoc/config.h +++ b/usr/src/cmd/mandoc/config.h @@ -40,6 +40,8 @@ #define HAVE_WCHAR 1 #define HAVE_OHASH 0 +#define OSENUM MANDOC_OS_OTHER + #define BINM_APROPOS "apropos" #define BINM_MAKEWHATIS "man -w" #define BINM_MAN "man" diff --git a/usr/src/cmd/mandoc/dbm.c b/usr/src/cmd/mandoc/dbm.c index 4aedf66d13..7637a036a5 100644 --- a/usr/src/cmd/mandoc/dbm.c +++ b/usr/src/cmd/mandoc/dbm.c @@ -1,4 +1,4 @@ -/* $Id: dbm.c,v 1.5 2016/10/18 22:27:25 schwarze Exp $ */ +/* $Id: dbm.c,v 1.6 2018/11/19 19:22:07 schwarze Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> * @@ -151,17 +151,17 @@ dbm_page_get(int32_t ip) assert(ip < npages); res.name = dbm_get(pages[ip].name); if (res.name == NULL) - res.name = "(NULL)"; + res.name = "(NULL)\0"; res.sect = dbm_get(pages[ip].sect); if (res.sect == NULL) - res.sect = "(NULL)"; + res.sect = "(NULL)\0"; res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL; res.desc = dbm_get(pages[ip].desc); if (res.desc == NULL) res.desc = "(NULL)"; res.file = dbm_get(pages[ip].file); if (res.file == NULL) - res.file = " (NULL)"; + res.file = " (NULL)\0"; res.addr = dbm_addr(pages + ip); return &res; } @@ -233,7 +233,7 @@ static struct dbm_res page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match) { static const struct dbm_match *match; - static const char *cp; + static const char *cp; static int32_t ip; struct dbm_res res = {-1, 0}; @@ -315,7 +315,7 @@ page_byarch(const struct dbm_match *arg_match) static const struct dbm_match *match; struct dbm_res res = {-1, 0}; static int32_t ip; - const char *cp; + const char *cp; /* Initialize for a new iteration. */ diff --git a/usr/src/cmd/mandoc/eqn.c b/usr/src/cmd/mandoc/eqn.c index 01601a7137..3d63382ab3 100644 --- a/usr/src/cmd/mandoc/eqn.c +++ b/usr/src/cmd/mandoc/eqn.c @@ -1,7 +1,7 @@ -/* $Id: eqn.c,v 1.78 2017/07/15 16:26:17 schwarze Exp $ */ +/* $Id: eqn.c,v 1.83 2018/12/14 06:33:14 schwarze Exp $ */ /* * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2017, 2018 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 @@ -30,8 +30,9 @@ #include "mandoc_aux.h" #include "mandoc.h" #include "roff.h" +#include "eqn.h" #include "libmandoc.h" -#include "libroff.h" +#include "eqn_parse.h" #define EQN_NEST_MAX 128 /* maximum nesting of defines */ #define STRNEQ(p1, sz1, p2, sz2) \ @@ -284,6 +285,13 @@ enum parse_mode { MODE_TOK }; +struct eqn_def { + char *key; + size_t keysz; + char *val; + size_t valsz; +}; + static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *); static struct eqn_box *eqn_box_makebinary(struct eqn_node *, struct eqn_box *); @@ -295,12 +303,11 @@ static void eqn_undef(struct eqn_node *); struct eqn_node * -eqn_alloc(struct mparse *parse) +eqn_alloc(void) { struct eqn_node *ep; ep = mandoc_calloc(1, sizeof(*ep)); - ep->parse = parse; ep->gsize = EQN_DEFSIZE; return ep; } @@ -399,7 +406,7 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode) ep->end = strchr(ep->start + 1, *ep->start); ep->start++; /* Skip opening quote. */ if (ep->end == NULL) { - mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse, + mandoc_msg(MANDOCERR_ARG_QUOTE, ep->node->line, ep->node->pos, NULL); ep->end = strchr(ep->start, '\0'); } @@ -420,7 +427,7 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode) if ((def = eqn_def_find(ep)) == NULL) break; if (++lim > EQN_NEST_MAX) { - mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse, + mandoc_msg(MANDOCERR_ROFFLOOP, ep->node->line, ep->node->pos, NULL); return EQN_TOK_EOF; } @@ -468,6 +475,8 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode) void eqn_box_free(struct eqn_box *bp) { + if (bp == NULL) + return; if (bp->first) eqn_box_free(bp->first); @@ -482,6 +491,16 @@ eqn_box_free(struct eqn_box *bp) free(bp); } +struct eqn_box * +eqn_box_new(void) +{ + struct eqn_box *bp; + + bp = mandoc_calloc(1, sizeof(*bp)); + bp->expectargs = UINT_MAX; + return bp; +} + /* * Allocate a box as the last child of the parent node. */ @@ -490,10 +509,9 @@ eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) { struct eqn_box *bp; - bp = mandoc_calloc(1, sizeof(struct eqn_box)); + bp = eqn_box_new(); bp->parent = parent; bp->parent->args++; - bp->expectargs = UINT_MAX; bp->font = bp->parent->font; bp->size = ep->gsize; @@ -542,7 +560,7 @@ static void eqn_delim(struct eqn_node *ep) { if (ep->end[0] == '\0' || ep->end[1] == '\0') { - mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, ep->node->pos, "delim"); if (ep->end[0] != '\0') ep->end++; @@ -569,7 +587,7 @@ eqn_undef(struct eqn_node *ep) struct eqn_def *def; if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { - mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, ep->node->pos, "undef"); return; } @@ -588,7 +606,7 @@ eqn_def(struct eqn_node *ep) int i; if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { - mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, ep->node->pos, "define"); return; } @@ -617,7 +635,7 @@ eqn_def(struct eqn_node *ep) } if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) { - mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse, + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, ep->node->pos, "define %s", def->key); free(def->key); free(def->val); @@ -666,7 +684,7 @@ next_tok: case EQN_TOK_TDEFINE: if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF || eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) - mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, ep->node->pos, "tdefine"); break; case EQN_TOK_DELIM: @@ -674,8 +692,8 @@ next_tok: break; case EQN_TOK_GFONT: if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) - mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); break; case EQN_TOK_MARK: case EQN_TOK_LINEUP: @@ -690,8 +708,8 @@ next_tok: case EQN_TOK_DOT: case EQN_TOK_DOTDOT: if (parent->last == NULL) { - mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; cur->text = mandoc_strdup(""); @@ -735,8 +753,8 @@ next_tok: case EQN_TOK_DOWN: case EQN_TOK_UP: if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) - mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); break; case EQN_TOK_FAT: case EQN_TOK_ROMAN: @@ -773,14 +791,14 @@ next_tok: case EQN_TOK_GSIZE: /* Accept two values: integral size and a single. */ if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { - mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); break; } size = mandoc_strntoi(ep->start, ep->toksz, 10); if (-1 == size) { - mandoc_msg(MANDOCERR_IT_NONUM, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); break; } if (EQN_TOK_GSIZE == tok) { @@ -804,8 +822,8 @@ next_tok: * and keep on reading. */ if (parent->last == NULL) { - mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; cur->text = mandoc_strdup(""); @@ -871,8 +889,8 @@ next_tok: * rebalance and continue reading. */ if (parent->last == NULL) { - mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; cur->text = mandoc_strdup(""); @@ -898,16 +916,16 @@ next_tok: cur->left != NULL)) break; if (cur == NULL) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); break; } parent = cur; if (EQN_TOK_RIGHT == tok) { if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { mandoc_msg(MANDOCERR_REQ_EMPTY, - ep->parse, ep->node->line, - ep->node->pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, + "%s", eqn_toks[tok]); break; } /* Handling depends on right/left. */ @@ -941,8 +959,8 @@ next_tok: parent = parent->parent; if (EQN_TOK_LEFT == tok && eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { - mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); break; } parent = eqn_box_alloc(ep, parent); @@ -975,8 +993,8 @@ next_tok: if (cur->type == EQN_PILE) break; if (cur == NULL) { - mandoc_msg(MANDOCERR_IT_STRAY, ep->parse, - ep->node->line, ep->node->pos, eqn_toks[tok]); + mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line, + ep->node->pos, "%s", eqn_toks[tok]); break; } parent = eqn_box_alloc(ep, cur); @@ -1092,6 +1110,9 @@ eqn_free(struct eqn_node *p) { int i; + if (p == NULL) + return; + for (i = 0; i < (int)p->defsz; i++) { free(p->defs[i].key); free(p->defs[i].val); diff --git a/usr/src/cmd/mandoc/eqn.h b/usr/src/cmd/mandoc/eqn.h new file mode 100644 index 0000000000..c7c7aa15a7 --- /dev/null +++ b/usr/src/cmd/mandoc/eqn.h @@ -0,0 +1,72 @@ +/* $Id: eqn.h,v 1.1 2018/12/13 05:23:38 schwarze Exp $ */ +/* + * Copyright (c) 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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. + * + * Public data types for eqn(7) syntax trees. + */ + +enum eqn_boxt { + EQN_TEXT, /* Text, e.g. number, variable, operator, ... */ + EQN_SUBEXPR, /* Nested eqn(7) subexpression. */ + EQN_LIST, /* List, for example in braces. */ + EQN_PILE, /* Vertical pile. */ + EQN_MATRIX /* List of columns. */ +}; + +enum eqn_fontt { + EQNFONT_NONE = 0, + EQNFONT_ROMAN, + EQNFONT_BOLD, + EQNFONT_FAT, + EQNFONT_ITALIC, + EQNFONT__MAX +}; + +enum eqn_post { + EQNPOS_NONE = 0, + EQNPOS_SUP, + EQNPOS_SUBSUP, + EQNPOS_SUB, + EQNPOS_TO, + EQNPOS_FROM, + EQNPOS_FROMTO, + EQNPOS_OVER, + EQNPOS_SQRT, + EQNPOS__MAX +}; + + /* + * A "box" is a parsed mathematical expression as defined by the eqn.7 + * grammar. + */ +struct eqn_box { + struct eqn_box *parent; + struct eqn_box *prev; + struct eqn_box *next; + struct eqn_box *first; /* First child node. */ + struct eqn_box *last; /* Last child node. */ + char *text; /* Text (or NULL). */ + char *left; /* Left-hand fence. */ + char *right; /* Right-hand fence. */ + char *top; /* Symbol above. */ + char *bottom; /* Symbol below. */ + size_t expectargs; /* Maximal number of arguments. */ + size_t args; /* Actual number of arguments. */ + int size; /* Font size. */ +#define EQN_DEFSIZE INT_MIN + enum eqn_boxt type; /* Type of node. */ + enum eqn_fontt font; /* Font in this box. */ + enum eqn_post pos; /* Position of the next box. */ +}; diff --git a/usr/src/cmd/mandoc/eqn_html.c b/usr/src/cmd/mandoc/eqn_html.c index 51f1442342..1fe41ecbfa 100644 --- a/usr/src/cmd/mandoc/eqn_html.c +++ b/usr/src/cmd/mandoc/eqn_html.c @@ -1,4 +1,4 @@ -/* $Id: eqn_html.c,v 1.17 2017/07/14 13:32:35 schwarze Exp $ */ +/* $Id: eqn_html.c,v 1.18 2018/12/13 05:23:38 schwarze Exp $ */ /* * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -26,6 +26,7 @@ #include <string.h> #include "mandoc.h" +#include "eqn.h" #include "out.h" #include "html.h" diff --git a/usr/src/cmd/mandoc/eqn_parse.h b/usr/src/cmd/mandoc/eqn_parse.h new file mode 100644 index 0000000000..a2a4e6fd7d --- /dev/null +++ b/usr/src/cmd/mandoc/eqn_parse.h @@ -0,0 +1,48 @@ +/* $Id: eqn_parse.h,v 1.3 2018/12/14 06:33:14 schwarze Exp $ */ +/* + * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2014, 2017, 2018 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 AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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. + * + * External interface of the eqn(7) parser. + * For use in the roff(7) and eqn(7) parsers only. + */ + +struct roff_node; +struct eqn_box; +struct eqn_def; + +struct eqn_node { + struct roff_node *node; /* Syntax tree of this equation. */ + struct eqn_def *defs; /* Array of definitions. */ + char *data; /* Source code of this equation. */ + char *start; /* First byte of the current token. */ + char *end; /* First byte of the next token. */ + size_t defsz; /* Number of definitions. */ + size_t sz; /* Length of the source code. */ + size_t toksz; /* Length of the current token. */ + int gsize; /* Default point size. */ + int delim; /* In-line delimiters enabled. */ + char odelim; /* In-line opening delimiter. */ + char cdelim; /* In-line closing delimiter. */ +}; + + +struct eqn_node *eqn_alloc(void); +struct eqn_box *eqn_box_new(void); +void eqn_box_free(struct eqn_box *); +void eqn_free(struct eqn_node *); +void eqn_parse(struct eqn_node *); +void eqn_read(struct eqn_node *, const char *); +void eqn_reset(struct eqn_node *); diff --git a/usr/src/cmd/mandoc/eqn_term.c b/usr/src/cmd/mandoc/eqn_term.c index 669c3c56cf..3e27233b30 100644 --- a/usr/src/cmd/mandoc/eqn_term.c +++ b/usr/src/cmd/mandoc/eqn_term.c @@ -1,4 +1,4 @@ -/* $Id: eqn_term.c,v 1.17 2017/08/23 21:56:20 schwarze Exp $ */ +/* $Id: eqn_term.c,v 1.19 2018/12/13 05:23:38 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -25,7 +25,7 @@ #include <stdlib.h> #include <string.h> -#include "mandoc.h" +#include "eqn.h" #include "out.h" #include "term.h" @@ -106,7 +106,7 @@ eqn_box(struct termp *p, const struct eqn_box *bp) /* Special box types. */ if (bp->pos == EQNPOS_SQRT) { - term_word(p, "sqrt"); + term_word(p, "\\(sr"); if (bp->first != NULL) { p->flags |= TERMP_NOSPACE; eqn_box(p, bp->first); diff --git a/usr/src/cmd/mandoc/html.c b/usr/src/cmd/mandoc/html.c index 70935dcd57..1302972a4e 100644 --- a/usr/src/cmd/mandoc/html.c +++ b/usr/src/cmd/mandoc/html.c @@ -1,7 +1,7 @@ -/* $Id: html.c,v 1.238 2018/06/25 16:54:59 schwarze Exp $ */ +/* $Id: html.c,v 1.254 2019/03/03 13:02:11 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2015, 2017-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 @@ -18,6 +18,7 @@ #include "config.h" #include <sys/types.h> +#include <sys/stat.h> #include <assert.h> #include <ctype.h> @@ -62,6 +63,7 @@ static const struct htmldata htmltags[TAG_MAX] = { {"title", HTML_NLAROUND}, {"div", HTML_NLAROUND}, {"div", 0}, + {"section", HTML_NLALL}, {"h1", HTML_NLAROUND}, {"h2", HTML_NLAROUND}, {"span", 0}, @@ -77,6 +79,7 @@ static const struct htmldata htmltags[TAG_MAX] = { {"dl", HTML_NLALL | HTML_INDENT}, {"dt", HTML_NLAROUND}, {"dd", HTML_NLAROUND | HTML_INDENT}, + {"p", HTML_NLAROUND | HTML_INDENT}, {"pre", HTML_NLALL | HTML_NOINDENT}, {"var", 0}, {"cite", 0}, @@ -107,6 +110,7 @@ static const struct htmldata htmltags[TAG_MAX] = { /* Avoid duplicate HTML id= attributes. */ static struct ohash id_unique; +static void html_reset_internal(struct html *); static void print_byte(struct html *, char); static void print_endword(struct html *); static void print_indent(struct html *); @@ -116,7 +120,6 @@ static void print_ctag(struct html *, struct tag *); static int print_escape(struct html *, char); static int print_encode(struct html *, const char *, const char *, int); static void print_href(struct html *, const char *, const char *, int); -static void print_metaf(struct html *, enum mandoc_esc); void * @@ -128,31 +131,32 @@ html_alloc(const struct manoutput *outopts) h->tag = NULL; h->style = outopts->style; - h->base_man = outopts->man; + if ((h->base_man1 = outopts->man) == NULL) + h->base_man2 = NULL; + else if ((h->base_man2 = strchr(h->base_man1, ';')) != NULL) + *h->base_man2++ = '\0'; h->base_includes = outopts->includes; if (outopts->fragment) h->oflags |= HTML_FRAGMENT; + if (outopts->toc) + h->oflags |= HTML_TOC; mandoc_ohash_init(&id_unique, 4, 0); return h; } -void -html_free(void *p) +static void +html_reset_internal(struct html *h) { struct tag *tag; - struct html *h; char *cp; unsigned int slot; - h = (struct html *)p; while ((tag = h->tag) != NULL) { h->tag = tag->next; free(tag); } - free(h); - cp = ohash_first(&id_unique, &slot); while (cp != NULL) { free(cp); @@ -162,6 +166,20 @@ html_free(void *p) } void +html_reset(void *p) +{ + html_reset_internal(p); + mandoc_ohash_init(&id_unique, 4, 0); +} + +void +html_free(void *p) +{ + html_reset_internal(p); + free(p); +} + +void print_gen_head(struct html *h) { struct tag *t; @@ -204,7 +222,7 @@ print_gen_head(struct html *h) print_tagq(h, t); } -static void +void print_metaf(struct html *h, enum mandoc_esc deco) { enum htmlfont font; @@ -222,12 +240,15 @@ print_metaf(struct html *h, enum mandoc_esc deco) case ESCAPE_FONTBI: font = HTMLFONT_BI; break; + case ESCAPE_FONTCW: + font = HTMLFONT_CW; + break; case ESCAPE_FONT: case ESCAPE_FONTROMAN: font = HTMLFONT_NONE; break; default: - abort(); + return; } if (h->metaf) { @@ -249,11 +270,69 @@ print_metaf(struct html *h, enum mandoc_esc deco) h->metaf = print_otag(h, TAG_B, ""); print_otag(h, TAG_I, ""); break; + case HTMLFONT_CW: + h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); + break; default: break; } } +void +html_close_paragraph(struct html *h) +{ + struct tag *t; + + for (t = h->tag; t != NULL && t->closed == 0; t = t->next) { + switch(t->tag) { + case TAG_P: + case TAG_PRE: + print_tagq(h, t); + break; + case TAG_A: + print_tagq(h, t); + continue; + default: + continue; + } + break; + } +} + +/* + * ROFF_nf switches to no-fill mode, ROFF_fi to fill mode. + * TOKEN_NONE does not switch. The old mode is returned. + */ +enum roff_tok +html_fillmode(struct html *h, enum roff_tok want) +{ + struct tag *t; + enum roff_tok had; + + for (t = h->tag; t != NULL; t = t->next) + if (t->tag == TAG_PRE) + break; + + had = t == NULL ? ROFF_fi : ROFF_nf; + + if (want != had) { + switch (want) { + case ROFF_fi: + print_tagq(h, t); + break; + case ROFF_nf: + html_close_paragraph(h); + print_otag(h, TAG_PRE, ""); + break; + case TOKEN_NONE: + break; + default: + abort(); + } + } + return had; +} + char * html_make_id(const struct roff_node *n, int unique) { @@ -345,7 +424,6 @@ static int print_encode(struct html *h, const char *p, const char *pend, int norecurse) { char numbuf[16]; - struct tag *t; const char *seq; size_t sz; int c, len, breakline, nospace; @@ -371,9 +449,7 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse) if (breakline && (p >= pend || *p == ' ' || *p == ASCII_NBRSP)) { - t = print_otag(h, TAG_DIV, ""); - print_text(h, "\\~"); - print_tagq(h, t); + print_otag(h, TAG_BR, ""); breakline = 0; while (p < pend && (*p == ' ' || *p == ASCII_NBRSP)) p++; @@ -393,22 +469,25 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse) continue; esc = mandoc_escape(&p, &seq, &len); - if (ESCAPE_ERROR == esc) - break; - switch (esc) { case ESCAPE_FONT: case ESCAPE_FONTPREV: case ESCAPE_FONTBOLD: case ESCAPE_FONTITALIC: case ESCAPE_FONTBI: + case ESCAPE_FONTCW: case ESCAPE_FONTROMAN: - if (0 == norecurse) + if (0 == norecurse) { + h->flags |= HTML_NOSPACE; print_metaf(h, esc); + h->flags &= ~HTML_NOSPACE; + } continue; case ESCAPE_SKIPCHAR: h->flags |= HTML_SKIPCHAR; continue; + case ESCAPE_ERROR: + continue; default: break; } @@ -433,6 +512,12 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse) if (c <= 0) continue; break; + case ESCAPE_UNDEF: + c = *seq; + break; + case ESCAPE_DEVICE: + print_word(h, "html"); + continue; case ESCAPE_BREAK: breakline = 1; continue; @@ -464,9 +549,21 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse) static void print_href(struct html *h, const char *name, const char *sec, int man) { + struct stat sb; const char *p, *pp; + char *filename; + + if (man) { + pp = h->base_man1; + if (h->base_man2 != NULL) { + mandoc_asprintf(&filename, "%s.%s", name, sec); + if (stat(filename, &sb) == -1) + pp = h->base_man2; + free(filename); + } + } else + pp = h->base_includes; - pp = man ? h->base_man : h->base_includes; while ((p = strchr(pp, '%')) != NULL) { print_encode(h, pp, p, 1); if (man && p[1] == 'S') { @@ -492,7 +589,7 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) struct tag *t; const char *attr; char *arg1, *arg2; - int tflags; + int style_written, tflags; tflags = htmltags[tag].flags; @@ -502,6 +599,8 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) t = mandoc_malloc(sizeof(struct tag)); t->tag = tag; t->next = h->tag; + t->refcnt = 0; + t->closed = 0; h->tag = t; } else t = NULL; @@ -532,7 +631,7 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) va_start(ap, fmt); - while (*fmt != '\0') { + while (*fmt != '\0' && *fmt != 's') { /* Parse attributes and arguments. */ @@ -548,10 +647,6 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) case 'i': attr = "id"; break; - case 's': - attr = "style"; - arg2 = va_arg(ap, char *); - break; case '?': attr = arg1; arg1 = va_arg(ap, char *); @@ -584,26 +679,33 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) print_encode(h, arg1, NULL, 1); fmt++; break; - case 'T': - print_encode(h, arg1, NULL, 1); - print_word(h, "\" title=\""); - print_encode(h, arg1, NULL, 1); - fmt++; - break; default: - if (arg2 == NULL) - print_encode(h, arg1, NULL, 1); - else { - print_word(h, arg1); - print_byte(h, ':'); - print_byte(h, ' '); - print_word(h, arg2); - print_byte(h, ';'); - } + print_encode(h, arg1, NULL, 1); break; } print_byte(h, '"'); } + + style_written = 0; + while (*fmt++ == 's') { + arg1 = va_arg(ap, char *); + arg2 = va_arg(ap, char *); + if (arg2 == NULL) + continue; + print_byte(h, ' '); + if (style_written == 0) { + print_word(h, "style=\""); + style_written = 1; + } + print_word(h, arg1); + print_byte(h, ':'); + print_byte(h, ' '); + print_word(h, arg2); + print_byte(h, ';'); + } + if (style_written) + print_byte(h, '"'); + va_end(ap); /* Accommodate for "well-formed" singleton escaping. */ @@ -631,33 +733,32 @@ print_ctag(struct html *h, struct tag *tag) { int tflags; - /* - * Remember to close out and nullify the current - * meta-font and table, if applicable. - */ - if (tag == h->metaf) - h->metaf = NULL; - if (tag == h->tblt) - h->tblt = NULL; - - tflags = htmltags[tag->tag].flags; - - if (tflags & HTML_INDENT) - h->indent--; - if (tflags & HTML_NOINDENT) - h->noindent--; - if (tflags & HTML_NLEND) - print_endline(h); - print_indent(h); - print_byte(h, '<'); - print_byte(h, '/'); - print_word(h, htmltags[tag->tag].name); - print_byte(h, '>'); - if (tflags & HTML_NLAFTER) - print_endline(h); - - h->tag = tag->next; - free(tag); + if (tag->closed == 0) { + tag->closed = 1; + if (tag == h->metaf) + h->metaf = NULL; + if (tag == h->tblt) + h->tblt = NULL; + + tflags = htmltags[tag->tag].flags; + if (tflags & HTML_INDENT) + h->indent--; + if (tflags & HTML_NOINDENT) + h->noindent--; + if (tflags & HTML_NLEND) + print_endline(h); + print_indent(h); + print_byte(h, '<'); + print_byte(h, '/'); + print_word(h, htmltags[tag->tag].name); + print_byte(h, '>'); + if (tflags & HTML_NLAFTER) + print_endline(h); + } + if (tag->refcnt == 0) { + h->tag = tag->next; + free(tag); + } } void @@ -717,6 +818,9 @@ print_text(struct html *h, const char *word) h->metaf = print_otag(h, TAG_B, ""); print_otag(h, TAG_I, ""); break; + case HTMLFONT_CW: + h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); + break; default: print_indent(h); break; @@ -741,36 +845,33 @@ print_text(struct html *h, const char *word) void print_tagq(struct html *h, const struct tag *until) { - struct tag *tag; + struct tag *this, *next; - while ((tag = h->tag) != NULL) { - print_ctag(h, tag); - if (until && tag == until) - return; + for (this = h->tag; this != NULL; this = next) { + next = this == until ? NULL : this->next; + print_ctag(h, this); } } +/* + * Close out all open elements up to but excluding suntil. + * Note that a paragraph just inside stays open together with it + * because paragraphs include subsequent phrasing content. + */ void print_stagq(struct html *h, const struct tag *suntil) { - struct tag *tag; + struct tag *this, *next; - while ((tag = h->tag) != NULL) { - if (suntil && tag == suntil) - return; - print_ctag(h, tag); + for (this = h->tag; this != NULL; this = next) { + next = this->next; + if (this == suntil || (next == suntil && + (this->tag == TAG_P || this->tag == TAG_PRE))) + break; + print_ctag(h, this); } } -void -print_paragraph(struct html *h) -{ - struct tag *t; - - t = print_otag(h, TAG_DIV, "c", "Pp"); - print_tagq(h, t); -} - /*********************************************************************** * Low level output functions. diff --git a/usr/src/cmd/mandoc/html.h b/usr/src/cmd/mandoc/html.h index 6d44a47383..a6bf891190 100644 --- a/usr/src/cmd/mandoc/html.h +++ b/usr/src/cmd/mandoc/html.h @@ -1,7 +1,7 @@ -/* $Id: html.h,v 1.92 2018/06/25 16:54:59 schwarze Exp $ */ +/* $Id: html.h,v 1.102 2019/03/01 10:57:18 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * 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 @@ -24,6 +24,7 @@ enum htmltag { TAG_TITLE, TAG_DIV, TAG_IDIV, + TAG_SECTION, TAG_H1, TAG_H2, TAG_SPAN, @@ -39,6 +40,7 @@ enum htmltag { TAG_DL, TAG_DT, TAG_DD, + TAG_P, TAG_PRE, TAG_VAR, TAG_CITE, @@ -72,11 +74,14 @@ enum htmlfont { HTMLFONT_BOLD, HTMLFONT_ITALIC, HTMLFONT_BI, + HTMLFONT_CW, HTMLFONT_MAX }; struct tag { struct tag *next; + int refcnt; + int closed; enum htmltag tag; }; @@ -87,12 +92,12 @@ struct html { #define HTML_KEEP (1 << 2) #define HTML_PREKEEP (1 << 3) #define HTML_NONOSPACE (1 << 4) /* never add spaces */ -#define HTML_LITERAL (1 << 5) /* literal (e.g., <PRE>) context */ #define HTML_SKIPCHAR (1 << 6) /* skip the next character */ #define HTML_NOSPLIT (1 << 7) /* do not break line before .An */ #define HTML_SPLIT (1 << 8) /* break line before .An */ #define HTML_NONEWLINE (1 << 9) /* No line break in nofill mode. */ #define HTML_BUFFER (1 << 10) /* Collect a word to see if it fits. */ +#define HTML_TOCDONE (1 << 11) /* The TOC was already written. */ size_t indent; /* current output indentation level */ int noindent; /* indent disabled by <pre> */ size_t col; /* current output byte position */ @@ -101,7 +106,8 @@ struct html { struct tag *tag; /* last open tag */ struct rofftbl tbl; /* current table */ struct tag *tblt; /* current open table scope */ - char *base_man; /* base for manpage href */ + char *base_man1; /* bases for manpage href */ + char *base_man2; char *base_includes; /* base for include href */ char *style; /* style-sheet URI */ struct tag *metaf; /* current open font scope */ @@ -109,6 +115,7 @@ struct html { enum htmlfont metac; /* current font mode */ int oflags; /* output options */ #define HTML_FRAGMENT (1 << 0) /* don't emit HTML/HEAD/BODY */ +#define HTML_TOC (1 << 1) /* emit a table of contents */ }; @@ -121,6 +128,7 @@ void roff_html_pre(struct html *, const struct roff_node *); void print_gen_comment(struct html *, struct roff_node *); void print_gen_decls(struct html *); void print_gen_head(struct html *); +void print_metaf(struct html *, enum mandoc_esc); struct tag *print_otag(struct html *, enum htmltag, const char *, ...); void print_tagq(struct html *, const struct tag *); void print_stagq(struct html *, const struct tag *); @@ -128,7 +136,8 @@ void print_text(struct html *, const char *); void print_tblclose(struct html *); void print_tbl(struct html *, const struct tbl_span *); void print_eqn(struct html *, const struct eqn_box *); -void print_paragraph(struct html *); void print_endline(struct html *); +void html_close_paragraph(struct html *); +enum roff_tok html_fillmode(struct html *, enum roff_tok); char *html_make_id(const struct roff_node *, int); diff --git a/usr/src/cmd/mandoc/lib.c b/usr/src/cmd/mandoc/lib.c index 0474924d73..e377c1bced 100644 --- a/usr/src/cmd/mandoc/lib.c +++ b/usr/src/cmd/mandoc/lib.c @@ -1,4 +1,4 @@ -/* $Id: lib.c,v 1.14 2017/06/24 14:38:32 schwarze Exp $ */ +/* $Id: lib.c,v 1.15 2018/12/13 11:55:46 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -17,12 +17,9 @@ #include "config.h" #include <sys/types.h> - #include <string.h> -#include "mandoc.h" #include "roff.h" -#include "mdoc.h" #include "libmdoc.h" #define LINE(x, y) \ diff --git a/usr/src/cmd/mandoc/libman.h b/usr/src/cmd/mandoc/libman.h index 312093dd01..ac960a6858 100644 --- a/usr/src/cmd/mandoc/libman.h +++ b/usr/src/cmd/mandoc/libman.h @@ -1,7 +1,7 @@ -/* $Id: libman.h,v 1.81 2017/04/29 12:45:41 schwarze Exp $ */ +/* $Id: libman.h,v 1.86 2018/12/31 10:04:39 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2018 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 @@ -16,6 +16,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +struct roff_node; +struct roff_man; + #define MACRO_PROT_ARGS struct roff_man *man, \ enum roff_tok tok, \ int line, \ @@ -26,15 +29,14 @@ struct man_macro { void (*fp)(MACRO_PROT_ARGS); int flags; -#define MAN_SCOPED (1 << 0) /* Optional next-line scope. */ -#define MAN_NSCOPED (1 << 1) /* Allowed in next-line element scope. */ -#define MAN_BSCOPE (1 << 2) /* Break next-line block scope. */ -#define MAN_JOIN (1 << 3) /* Join arguments together. */ +#define MAN_BSCOPED (1 << 0) /* Optional next-line block scope. */ +#define MAN_ESCOPED (1 << 1) /* Optional next-line element scope. */ +#define MAN_NSCOPED (1 << 2) /* Allowed in next-line element scope. */ +#define MAN_XSCOPE (1 << 3) /* Exit next-line block scope. */ +#define MAN_JOIN (1 << 4) /* Join arguments together. */ }; -extern const struct man_macro *const man_macros; - +const struct man_macro *man_macro(enum roff_tok); -void man_node_validate(struct roff_man *); -void man_state(struct roff_man *, struct roff_node *); +void man_descope(struct roff_man *, int, int, char *); void man_unscope(struct roff_man *, const struct roff_node *); diff --git a/usr/src/cmd/mandoc/libmandoc.h b/usr/src/cmd/mandoc/libmandoc.h index 9cc8cce4ec..ff6f4692f0 100644 --- a/usr/src/cmd/mandoc/libmandoc.h +++ b/usr/src/cmd/mandoc/libmandoc.h @@ -1,7 +1,7 @@ -/* $Id: libmandoc.h,v 1.71 2018/04/09 22:27:04 schwarze Exp $ */ +/* $Id: libmandoc.h,v 1.77 2018/12/21 17:15:18 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013,2014,2015,2017,2018 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 @@ -16,31 +16,38 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -enum rofferr { - ROFF_CONT, /* continue processing line */ - ROFF_RERUN, /* re-run roff interpreter with offset */ - ROFF_APPEND, /* re-run main parser, appending next line */ - ROFF_REPARSE, /* re-run main parser on the result */ - ROFF_SO, /* include another file */ - ROFF_IGN, /* ignore current line */ -}; +/* + * Return codes passed from the roff parser to the main parser. + */ + +/* Main instruction: what to do with the returned line. */ +#define ROFF_IGN 0x000 /* Don't do anything with it. */ +#define ROFF_CONT 0x001 /* Give it to the high-level parser. */ +#define ROFF_RERUN 0x002 /* Re-run the roff parser with an offset. */ +#define ROFF_REPARSE 0x004 /* Recursively run the main parser on it. */ +#define ROFF_SO 0x008 /* Include the named file. */ +#define ROFF_MASK 0x00f /* Only one of these bits should be set. */ + +/* Options for further parsing, to be OR'ed with the above. */ +#define ROFF_APPEND 0x010 /* Append the next line to this one. */ +#define ROFF_USERCALL 0x020 /* Start execution of a new macro. */ +#define ROFF_USERRET 0x040 /* Abort execution of the current macro. */ +#define ROFF_WHILE 0x100 /* Start a new .while loop. */ +#define ROFF_LOOPCONT 0x200 /* Iterate the current .while loop. */ +#define ROFF_LOOPEXIT 0x400 /* Exit the current .while loop. */ +#define ROFF_LOOPMASK 0xf00 + struct buf { - char *buf; - size_t sz; + char *buf; + size_t sz; + struct buf *next; }; -struct mparse; struct roff; struct roff_man; -void mandoc_msg(enum mandocerr, struct mparse *, - int, int, const char *); -void mandoc_vmsg(enum mandocerr, struct mparse *, - int, int, const char *, ...) - __attribute__((__format__ (__printf__, 5, 6))); -char *mandoc_getarg(struct mparse *, char **, int, int *); char *mandoc_normdate(struct roff_man *, char *, int, int); int mandoc_eos(const char *, size_t); int mandoc_strntoi(const char *, size_t, int); @@ -57,17 +64,18 @@ int preconv_encode(const struct buf *, size_t *, struct buf *, size_t *, int *); void roff_free(struct roff *); -struct roff *roff_alloc(struct mparse *, int); +struct roff *roff_alloc(int); void roff_reset(struct roff *); void roff_man_free(struct roff_man *); -struct roff_man *roff_man_alloc(struct roff *, struct mparse *, - const char *, int); +struct roff_man *roff_man_alloc(struct roff *, const char *, int); void roff_man_reset(struct roff_man *); -enum rofferr roff_parseln(struct roff *, int, struct buf *, int *); +int roff_parseln(struct roff *, int, struct buf *, int *); +void roff_userret(struct roff *); void roff_endparse(struct roff *); void roff_setreg(struct roff *, const char *, int, char sign); int roff_getreg(struct roff *, const char *); char *roff_strdup(const struct roff *, const char *); +char *roff_getarg(struct roff *, char **, int, int *); int roff_getcontrol(const struct roff *, const char *, int *); int roff_getformat(const struct roff *); diff --git a/usr/src/cmd/mandoc/libmdoc.h b/usr/src/cmd/mandoc/libmdoc.h index 57dff61b4a..ff29625194 100644 --- a/usr/src/cmd/mandoc/libmdoc.h +++ b/usr/src/cmd/mandoc/libmdoc.h @@ -1,7 +1,7 @@ -/* $Id: libmdoc.h,v 1.112 2017/05/30 16:22:03 schwarze Exp $ */ +/* $Id: libmdoc.h,v 1.117 2018/12/31 04:55:46 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013,2014,2015,2017,2018 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 @@ -16,6 +16,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +struct roff_node; +struct roff_man; +struct mdoc_arg; + #define MACRO_PROT_ARGS struct roff_man *mdoc, \ enum roff_tok tok, \ int line, \ @@ -38,6 +42,7 @@ enum margserr { ARGS_ERROR, ARGS_EOLN, /* end-of-line */ ARGS_WORD, /* normal word */ + ARGS_ALLOC, /* normal word from roff_getarg() */ ARGS_PUNCT, /* series of punctuation */ ARGS_PHRASE /* Bl -column phrase */ }; @@ -59,10 +64,8 @@ enum mdelim { DELIM_MAX }; -extern const struct mdoc_macro *const mdoc_macros; - +const struct mdoc_macro *mdoc_macro(enum roff_tok); -void mdoc_macro(MACRO_PROT_ARGS); void mdoc_elem_alloc(struct roff_man *, int, int, enum roff_tok, struct mdoc_arg *); struct roff_node *mdoc_block_alloc(struct roff_man *, int, int, @@ -71,10 +74,7 @@ void mdoc_tail_alloc(struct roff_man *, int, int, enum roff_tok); struct roff_node *mdoc_endbody_alloc(struct roff_man *, int, int, enum roff_tok, struct roff_node *); -void mdoc_node_relink(struct roff_man *, struct roff_node *); -void mdoc_node_validate(struct roff_man *); void mdoc_state(struct roff_man *, struct roff_node *); -void mdoc_state_reset(struct roff_man *); const char *mdoc_a2arch(const char *); const char *mdoc_a2att(const char *); const char *mdoc_a2lib(const char *); diff --git a/usr/src/cmd/mandoc/libroff.h b/usr/src/cmd/mandoc/libroff.h deleted file mode 100644 index b6a026d820..0000000000 --- a/usr/src/cmd/mandoc/libroff.h +++ /dev/null @@ -1,80 +0,0 @@ -/* $Id: libroff.h,v 1.42 2017/07/08 17:52:49 schwarze Exp $ */ -/* - * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017 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. - */ - -enum tbl_part { - TBL_PART_OPTS, /* in options (first line) */ - TBL_PART_LAYOUT, /* describing layout */ - TBL_PART_DATA, /* creating data rows */ - TBL_PART_CDATA /* continue previous row */ -}; - -struct tbl_node { - struct mparse *parse; /* parse point */ - int pos; /* invocation column */ - int line; /* invocation line */ - enum tbl_part part; - struct tbl_opts opts; - struct tbl_row *first_row; - struct tbl_row *last_row; - struct tbl_span *first_span; - struct tbl_span *current_span; - struct tbl_span *last_span; - struct tbl_node *next; -}; - -struct eqn_node { - struct mparse *parse; /* main parser, for error reporting */ - struct roff_node *node; /* syntax tree of this equation */ - struct eqn_def *defs; /* array of definitions */ - char *data; /* source code of this equation */ - char *start; /* first byte of the current token */ - char *end; /* first byte of the next token */ - size_t defsz; /* number of definitions */ - size_t sz; /* length of the source code */ - size_t toksz; /* length of the current token */ - int gsize; /* default point size */ - int delim; /* in-line delimiters enabled */ - char odelim; /* in-line opening delimiter */ - char cdelim; /* in-line closing delimiter */ -}; - -struct eqn_def { - char *key; - size_t keysz; - char *val; - size_t valsz; -}; - - -struct tbl_node *tbl_alloc(int, int, struct mparse *); -void tbl_restart(int, int, struct tbl_node *); -void tbl_free(struct tbl_node *); -void tbl_reset(struct tbl_node *); -void tbl_read(struct tbl_node *, int, const char *, int); -void tbl_option(struct tbl_node *, int, const char *, int *); -void tbl_layout(struct tbl_node *, int, const char *, int); -void tbl_data(struct tbl_node *, int, const char *, int); -void tbl_cdata(struct tbl_node *, int, const char *, int); -const struct tbl_span *tbl_span(struct tbl_node *); -int tbl_end(struct tbl_node *); -struct eqn_node *eqn_alloc(struct mparse *); -void eqn_box_free(struct eqn_box *); -void eqn_free(struct eqn_node *); -void eqn_parse(struct eqn_node *); -void eqn_read(struct eqn_node *, const char *); -void eqn_reset(struct eqn_node *); diff --git a/usr/src/cmd/mandoc/main.c b/usr/src/cmd/mandoc/main.c index 600bc9bb4b..b91c15860a 100644 --- a/usr/src/cmd/mandoc/main.c +++ b/usr/src/cmd/mandoc/main.c @@ -1,7 +1,7 @@ -/* $Id: main.c,v 1.306 2018/05/14 14:10:23 schwarze Exp $ */ +/* $Id: main.c,v 1.322 2019/03/06 10:18:58 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2012, 2014-2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010-2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -21,7 +21,6 @@ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/param.h> /* MACHINE */ -#include <sys/termios.h> #include <sys/wait.h> #include <assert.h> @@ -40,6 +39,7 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <termios.h> #include <time.h> #include <unistd.h> @@ -49,6 +49,7 @@ #include "roff.h" #include "mdoc.h" #include "man.h" +#include "mandoc_parse.h" #include "tag.h" #include "main.h" #include "manconf.h" @@ -81,7 +82,6 @@ struct curparse { void *outdata; /* data for output */ char *os_s; /* operating system for display */ int wstop; /* stop after a file with a warning */ - enum mandocerr mmin; /* ignore messages below this */ enum mandoc_os os_e; /* check base system conventions */ enum outt outtype; /* which output to use */ }; @@ -89,7 +89,7 @@ struct curparse { int mandocdb(int, char *[]); -static void check_xr(const char *); +static void check_xr(void); static int fs_lookup(const struct manpaths *, size_t ipath, const char *, const char *, const char *, @@ -99,8 +99,6 @@ static int fs_search(const struct mansearch *, struct manpage **, size_t *); static int koptions(int *, char *); static void moptions(int *, char *); -static void mmsg(enum mandocerr, enum mandoclevel, - const char *, int, int, const char *); static void outdata_alloc(struct curparse *); static void parse(struct curparse *, int, const char *); static void passthrough(const char *, int, int); @@ -112,8 +110,6 @@ static int woptions(struct curparse *, char *); static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; static char help_arg[] = "help"; static char *help_argv[] = {help_arg, NULL}; -static enum mandoclevel rc; -static FILE *mmsg_stream; int @@ -127,7 +123,7 @@ main(int argc, char *argv[]) struct manpage *res, *resp; const char *progname, *sec, *thisarg; char *conf_file, *defpaths, *auxpaths; - char *oarg; + char *oarg, *tagarg; unsigned char *uc; size_t i, sz; int prio, best_prio; @@ -152,6 +148,7 @@ main(int argc, char *argv[]) setprogname(progname); #endif + mandoc_msg_setoutfile(stderr); if (strncmp(progname, "mandocdb", 8) == 0 || strcmp(progname, BINM_MAKEWHATIS) == 0) return mandocdb(argc, argv); @@ -191,10 +188,8 @@ main(int argc, char *argv[]) memset(&curp, 0, sizeof(struct curparse)); curp.outtype = OUTT_LOCALE; - curp.mmin = MANDOCERR_MAX; curp.outopts = &conf.output; options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; - mmsg_stream = stderr; use_pager = 1; tag_files = NULL; @@ -314,6 +309,9 @@ main(int argc, char *argv[]) } } + if (curp.outtype != OUTT_TREE || !curp.outopts->noval) + options |= MPARSE_VALIDATE; + if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST || !isatty(STDOUT_FILENO)) @@ -371,7 +369,16 @@ main(int argc, char *argv[]) #endif } - rc = MANDOCLEVEL_OK; + /* + * Use the first argument for -O tag in addition to + * using it as a search term for man(1) or apropos(1). + */ + + if (conf.output.tag != NULL && *conf.output.tag == '\0') { + tagarg = argc > 0 && search.argmode == ARG_EXPR ? + strchr(*argv, '=') : NULL; + conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; + } /* man(1), whatis(1), apropos(1) */ @@ -405,7 +412,6 @@ main(int argc, char *argv[]) res[sz].names = NULL; res[sz].output = NULL; res[sz].ipath = SIZE_MAX; - res[sz].bits = 0; res[sz].sec = 10; res[sz].form = FORM_SRC; sz++; @@ -415,7 +421,7 @@ main(int argc, char *argv[]) if (sz == 0) { if (search.argmode != ARG_NAME) warnx("nothing appropriate"); - rc = MANDOCLEVEL_BADARG; + mandoc_msg_setrc(MANDOCLEVEL_BADARG); goto out; } @@ -483,19 +489,17 @@ main(int argc, char *argv[]) moptions(&options, auxpaths); mchars_alloc(); - curp.mp = mparse_alloc(options, curp.mmin, mmsg, - curp.os_e, curp.os_s); - - /* - * Conditionally start up the lookaside buffer before parsing. - */ - if (OUTT_MAN == curp.outtype) - mparse_keep(curp.mp); + curp.mp = mparse_alloc(options, curp.os_e, curp.os_s); if (argc < 1) { - if (use_pager) + if (use_pager) { tag_files = tag_init(); - parse(&curp, STDIN_FILENO, "<stdin>"); + tag_files->tagname = conf.output.tag; + } + thisarg = "<stdin>"; + mandoc_msg_setinfilename(thisarg); + parse(&curp, STDIN_FILENO, thisarg); + mandoc_msg_setinfilename(NULL); } /* @@ -519,22 +523,25 @@ main(int argc, char *argv[]) (void)chdir(conf.manpath.paths[resp->ipath]); else if (startdir != -1) (void)fchdir(startdir); - } + thisarg = resp->file; + } else + thisarg = *argv; - fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv); + fd = mparse_open(curp.mp, thisarg); if (fd != -1) { if (use_pager) { - tag_files = tag_init(); use_pager = 0; + tag_files = tag_init(); + tag_files->tagname = conf.output.tag; } - if (resp == NULL) - parse(&curp, fd, *argv); - else if (resp->form == FORM_SRC) - parse(&curp, fd, resp->file); + mandoc_msg_setinfilename(thisarg); + if (resp == NULL || resp->form == FORM_SRC) + parse(&curp, fd, thisarg); else passthrough(resp->file, fd, conf.output.synopsisonly); + mandoc_msg_setinfilename(NULL); if (ferror(stdout)) { if (tag_files != NULL) { @@ -543,7 +550,7 @@ main(int argc, char *argv[]) tag_files = NULL; } else warn("stdout"); - rc = MANDOCLEVEL_SYSERR; + mandoc_msg_setrc(MANDOCLEVEL_SYSERR); break; } @@ -552,10 +559,11 @@ main(int argc, char *argv[]) outdata_alloc(&curp); terminal_sepline(curp.outdata); } - } else if (rc < MANDOCLEVEL_ERROR) - rc = MANDOCLEVEL_ERROR; + } else + mandoc_msg(MANDOCERR_FILE, 0, 0, + "%s: %s", thisarg, strerror(errno)); - if (MANDOCLEVEL_OK != rc && curp.wstop) + if (curp.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) break; if (resp != NULL) @@ -646,7 +654,7 @@ out: if (pid == -1) { warn("wait"); - rc = MANDOCLEVEL_SYSERR; + mandoc_msg_setrc(MANDOCLEVEL_SYSERR); break; } if (!WIFSTOPPED(status)) @@ -656,8 +664,7 @@ out: } tag_unlink(); } - - return (int)rc; + return (int)mandoc_msg_getrc(); } static void @@ -755,7 +762,6 @@ found: page->names = NULL; page->output = NULL; page->ipath = ipath; - page->bits = NAME_FILE & NAME_MASK; page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; page->form = form; return 1; @@ -790,8 +796,18 @@ fs_search(const struct mansearch *cfg, const struct manpaths *paths, return 1; } if (res != NULL && *ressz == lastsz && - strchr(*argv, '/') == NULL) - warnx("No entry for %s in the manual.", *argv); + strchr(*argv, '/') == NULL) { + if (cfg->arch != NULL && + arch_valid(cfg->arch, OSENUM) == 0) + warnx("Unknown architecture \"%s\".", + cfg->arch); + else if (cfg->sec == NULL) + warnx("No entry for %s in the manual.", + *argv); + else + warnx("No entry for %s in section %s " + "of the manual.", *argv, cfg->sec); + } lastsz = *ressz; argv++; argc--; @@ -802,101 +818,92 @@ fs_search(const struct mansearch *cfg, const struct manpaths *paths, static void parse(struct curparse *curp, int fd, const char *file) { - enum mandoclevel rctmp; - struct roff_man *man; + struct roff_meta *meta; /* Begin by parsing the file itself. */ assert(file); assert(fd >= 0); - rctmp = mparse_readfd(curp->mp, fd, file); + mparse_readfd(curp->mp, fd, file); if (fd != STDIN_FILENO) close(fd); - if (rc < rctmp) - rc = rctmp; /* * With -Wstop and warnings or errors of at least the requested * level, do not produce output. */ - if (rctmp != MANDOCLEVEL_OK && curp->wstop) + if (curp->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) return; if (curp->outdata == NULL) outdata_alloc(curp); + else if (curp->outtype == OUTT_HTML) + html_reset(curp); - mparse_result(curp->mp, &man, NULL); + mandoc_xr_reset(); + meta = mparse_result(curp->mp); /* Execute the out device, if it exists. */ - if (man == NULL) - return; - mandoc_xr_reset(); - if (man->macroset == MACROSET_MDOC) { - if (curp->outtype != OUTT_TREE || !curp->outopts->noval) - mdoc_validate(man); + if (meta->macroset == MACROSET_MDOC) { switch (curp->outtype) { case OUTT_HTML: - html_mdoc(curp->outdata, man); + html_mdoc(curp->outdata, meta); break; case OUTT_TREE: - tree_mdoc(curp->outdata, man); + tree_mdoc(curp->outdata, meta); break; case OUTT_MAN: - man_mdoc(curp->outdata, man); + man_mdoc(curp->outdata, meta); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: - terminal_mdoc(curp->outdata, man); + terminal_mdoc(curp->outdata, meta); break; case OUTT_MARKDOWN: - markdown_mdoc(curp->outdata, man); + markdown_mdoc(curp->outdata, meta); break; default: break; } } - if (man->macroset == MACROSET_MAN) { - if (curp->outtype != OUTT_TREE || !curp->outopts->noval) - man_validate(man); + if (meta->macroset == MACROSET_MAN) { switch (curp->outtype) { case OUTT_HTML: - html_man(curp->outdata, man); + html_man(curp->outdata, meta); break; case OUTT_TREE: - tree_man(curp->outdata, man); + tree_man(curp->outdata, meta); break; case OUTT_MAN: - man_man(curp->outdata, man); + mparse_copy(curp->mp); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: - terminal_man(curp->outdata, man); + terminal_man(curp->outdata, meta); break; default: break; } } - if (curp->mmin < MANDOCERR_STYLE) - check_xr(file); - mparse_updaterc(curp->mp, &rc); + if (mandoc_msg_getmin() < MANDOCERR_STYLE) + check_xr(); } static void -check_xr(const char *file) +check_xr(void) { static struct manpaths paths; struct mansearch search; struct mandoc_xr *xr; - char *cp; size_t sz; if (paths.sz == 0) @@ -915,13 +922,12 @@ check_xr(const char *file) if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz)) continue; if (xr->count == 1) - mandoc_asprintf(&cp, "Xr %s %s", xr->name, xr->sec); + mandoc_msg(MANDOCERR_XR_BAD, xr->line, + xr->pos + 1, "Xr %s %s", xr->name, xr->sec); else - mandoc_asprintf(&cp, "Xr %s %s (%d times)", + mandoc_msg(MANDOCERR_XR_BAD, xr->line, + xr->pos + 1, "Xr %s %s (%d times)", xr->name, xr->sec, xr->count); - mmsg(MANDOCERR_XR_BAD, MANDOCLEVEL_STYLE, - file, xr->line, xr->pos + 1, cp); - free(cp); } } @@ -1020,8 +1026,7 @@ done: fail: free(line); warn("%s: SYSERR: %s", file, syscall); - if (rc < MANDOCLEVEL_SYSERR) - rc = MANDOCLEVEL_SYSERR; + mandoc_msg_setrc(MANDOCLEVEL_SYSERR); } static int @@ -1063,8 +1068,8 @@ toptions(struct curparse *curp, char *arg) curp->outtype = OUTT_ASCII; else if (0 == strcmp(arg, "lint")) { curp->outtype = OUTT_LINT; - curp->mmin = MANDOCERR_BASE; - mmsg_stream = stdout; + mandoc_msg_setoutfile(stdout); + mandoc_msg_setmin(MANDOCERR_BASE); } else if (0 == strcmp(arg, "tree")) curp->outtype = OUTT_TREE; else if (0 == strcmp(arg, "man")) @@ -1115,29 +1120,29 @@ woptions(struct curparse *curp, char *arg) break; case 1: case 2: - curp->mmin = MANDOCERR_BASE; + mandoc_msg_setmin(MANDOCERR_BASE); break; case 3: - curp->mmin = MANDOCERR_STYLE; + mandoc_msg_setmin(MANDOCERR_STYLE); break; case 4: - curp->mmin = MANDOCERR_WARNING; + mandoc_msg_setmin(MANDOCERR_WARNING); break; case 5: - curp->mmin = MANDOCERR_ERROR; + mandoc_msg_setmin(MANDOCERR_ERROR); break; case 6: - curp->mmin = MANDOCERR_UNSUPP; + mandoc_msg_setmin(MANDOCERR_UNSUPP); break; case 7: - curp->mmin = MANDOCERR_MAX; + mandoc_msg_setmin(MANDOCERR_MAX); break; case 8: - curp->mmin = MANDOCERR_BASE; + mandoc_msg_setmin(MANDOCERR_BASE); curp->os_e = MANDOC_OS_OPENBSD; break; case 9: - curp->mmin = MANDOCERR_BASE; + mandoc_msg_setmin(MANDOCERR_BASE); curp->os_e = MANDOC_OS_NETBSD; break; default: @@ -1148,29 +1153,6 @@ woptions(struct curparse *curp, char *arg) return 1; } -static void -mmsg(enum mandocerr t, enum mandoclevel lvl, - const char *file, int line, int col, const char *msg) -{ - const char *mparse_msg; - - fprintf(mmsg_stream, "%s: %s:", getprogname(), - file == NULL ? "<stdin>" : file); - - if (line) - fprintf(mmsg_stream, "%d:%d:", line, col + 1); - - fprintf(mmsg_stream, " %s", mparse_strlevel(lvl)); - - if ((mparse_msg = mparse_strerror(t)) != NULL) - fprintf(mmsg_stream, ": %s", mparse_msg); - - if (msg) - fprintf(mmsg_stream, ": %s", msg); - - fputc('\n', mmsg_stream); -} - static pid_t spawn_pager(struct tag_files *tag_files) { @@ -1179,8 +1161,10 @@ spawn_pager(struct tag_files *tag_files) char *argv[MAX_PAGER_ARGS]; const char *pager; char *cp; +#if HAVE_LESS_T size_t cmdlen; - int argc; +#endif + int argc, use_ofn; pid_t pager_pid; pager = getenv("MANPAGER"); @@ -1196,7 +1180,7 @@ spawn_pager(struct tag_files *tag_files) */ argc = 0; - while (argc + 4 < MAX_PAGER_ARGS) { + while (argc + 5 < MAX_PAGER_ARGS) { argv[argc++] = cp; cp = strchr(cp, ' '); if (cp == NULL) @@ -1210,14 +1194,23 @@ spawn_pager(struct tag_files *tag_files) /* For less(1), use the tag file. */ + use_ofn = 1; +#if HAVE_LESS_T if ((cmdlen = strlen(argv[0])) >= 4) { cp = argv[0] + cmdlen - 4; if (strcmp(cp, "less") == 0) { argv[argc++] = mandoc_strdup("-T"); argv[argc++] = tag_files->tfn; + if (tag_files->tagname != NULL) { + argv[argc++] = mandoc_strdup("-t"); + argv[argc++] = tag_files->tagname; + use_ofn = 0; + } } } - argv[argc++] = tag_files->ofn; +#endif + if (use_ofn) + argv[argc++] = tag_files->ofn; argv[argc] = NULL; switch (pager_pid = fork()) { diff --git a/usr/src/cmd/mandoc/main.h b/usr/src/cmd/mandoc/main.h index f9b8f135f8..19a630cde8 100644 --- a/usr/src/cmd/mandoc/main.h +++ b/usr/src/cmd/mandoc/main.h @@ -1,7 +1,7 @@ -/* $Id: main.h,v 1.27 2017/03/03 14:23:23 schwarze Exp $ */ +/* $Id: main.h,v 1.30 2019/03/03 13:02:11 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 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 @@ -16,7 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -struct roff_man; +struct roff_meta; struct manoutput; /* @@ -27,15 +27,15 @@ struct manoutput; */ void *html_alloc(const struct manoutput *); -void html_mdoc(void *, const struct roff_man *); -void html_man(void *, const struct roff_man *); +void html_mdoc(void *, const struct roff_meta *); +void html_man(void *, const struct roff_meta *); +void html_reset(void *); void html_free(void *); -void tree_mdoc(void *, const struct roff_man *); -void tree_man(void *, const struct roff_man *); +void tree_mdoc(void *, const struct roff_meta *); +void tree_man(void *, const struct roff_meta *); -void man_mdoc(void *, const struct roff_man *); -void man_man(void *, const struct roff_man *); +void man_mdoc(void *, const struct roff_meta *); void *locale_alloc(const struct manoutput *); void *utf8_alloc(const struct manoutput *); @@ -46,8 +46,8 @@ void *pdf_alloc(const struct manoutput *); void *ps_alloc(const struct manoutput *); void pspdf_free(void *); -void terminal_mdoc(void *, const struct roff_man *); -void terminal_man(void *, const struct roff_man *); +void terminal_mdoc(void *, const struct roff_meta *); +void terminal_man(void *, const struct roff_meta *); void terminal_sepline(void *); -void markdown_mdoc(void *, const struct roff_man *); +void markdown_mdoc(void *, const struct roff_meta *); diff --git a/usr/src/cmd/mandoc/man.c b/usr/src/cmd/mandoc/man.c index 7a2bcc9688..f0e4002b2a 100644 --- a/usr/src/cmd/mandoc/man.c +++ b/usr/src/cmd/mandoc/man.c @@ -1,7 +1,7 @@ -/* $Id: man.c,v 1.176 2017/06/28 12:52:45 schwarze Exp $ */ +/* $Id: man.c,v 1.187 2019/01/05 00:36:50 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Joerg Sonnenberger <joerg@netbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -35,7 +35,7 @@ #include "roff_int.h" #include "libman.h" -static void man_descope(struct roff_man *, int, int); +static char *man_hasc(char *); static int man_ptext(struct roff_man *, int, char *, int); static int man_pmacro(struct roff_man *, int, char *, int); @@ -52,38 +52,62 @@ man_parseln(struct roff_man *man, int ln, char *buf, int offs) man_ptext(man, ln, buf, offs); } -static void -man_descope(struct roff_man *man, int line, int offs) +/* + * If the string ends with \c, return a pointer to the backslash. + * Otherwise, return NULL. + */ +static char * +man_hasc(char *start) { + char *cp, *ep; + + ep = strchr(start, '\0') - 2; + if (ep < start || ep[0] != '\\' || ep[1] != 'c') + return NULL; + for (cp = ep; cp > start; cp--) + if (cp[-1] != '\\') + break; + return (ep - cp) % 2 ? NULL : ep; +} + +void +man_descope(struct roff_man *man, int line, int offs, char *start) +{ + /* Trailing \c keeps next-line scope open. */ + + if (start != NULL && man_hasc(start) != NULL) + return; + /* * Co-ordinate what happens with having a next-line scope open: - * first close out the element scope (if applicable), then close - * out the block scope (also if applicable). + * first close out the element scopes (if applicable), + * then close out the block scope (also if applicable). */ if (man->flags & MAN_ELINE) { + while (man->last->parent->type != ROFFT_ROOT && + man_macro(man->last->parent->tok)->flags & MAN_ESCOPED) + man_unscope(man, man->last->parent); man->flags &= ~MAN_ELINE; - man_unscope(man, man->last->parent); } if ( ! (man->flags & MAN_BLINE)) return; - man->flags &= ~MAN_BLINE; man_unscope(man, man->last->parent); roff_body_alloc(man, line, offs, man->last->tok); + man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); } static int man_ptext(struct roff_man *man, int line, char *buf, int offs) { int i; - const char *cp, *sp; char *ep; - /* Literal free-form text whitespace is preserved. */ + /* In no-fill mode, whitespace is preserved on text lines. */ - if (man->flags & MAN_LITERAL) { + if (man->flags & ROFF_NOFILL) { roff_word_alloc(man, line, offs, buf + offs); - man_descope(man, line, offs); + man_descope(man, line, offs, buf + offs); return 1; } @@ -98,26 +122,15 @@ man_ptext(struct roff_man *man, int line, char *buf, int offs) if (buf[i] == '\0') { if (man->flags & (MAN_ELINE | MAN_BLINE)) { - mandoc_msg(MANDOCERR_BLK_BLANK, man->parse, - line, 0, NULL); + mandoc_msg(MANDOCERR_BLK_BLANK, line, 0, NULL); return 1; } if (man->last->tok == MAN_SH || man->last->tok == MAN_SS) return 1; - switch (man->last->type) { - case ROFFT_TEXT: - sp = man->last->string; - cp = ep = strchr(sp, '\0') - 2; - if (cp < sp || cp[0] != '\\' || cp[1] != 'c') - break; - while (cp > sp && cp[-1] == '\\') - cp--; - if ((ep - cp) % 2) - break; + if (man->last->type == ROFFT_TEXT && + ((ep = man_hasc(man->last->string)) != NULL)) { *ep = '\0'; return 1; - default: - break; } roff_elem_alloc(man, line, offs, ROFF_sp); man->next = ROFF_NEXT_SIBLING; @@ -134,8 +147,7 @@ man_ptext(struct roff_man *man, int line, char *buf, int offs) if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { if (i > 1 && '\\' != buf[i - 2]) - mandoc_msg(MANDOCERR_SPACE_EOL, man->parse, - line, i - 1, NULL); + mandoc_msg(MANDOCERR_SPACE_EOL, line, i - 1, NULL); for (--i; i && ' ' == buf[i]; i--) /* Spin back to non-space. */ ; @@ -157,7 +169,7 @@ man_ptext(struct roff_man *man, int line, char *buf, int offs) if (mandoc_eos(buf, (size_t)i)) man->last->flags |= NODE_EOS; - man_descope(man, line, offs); + man_descope(man, line, offs, buf + offs); return 1; } @@ -180,8 +192,7 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs) if (sz > 0 && sz < 4) tok = roffhash_find(man->manmac, buf + ppos, sz); if (tok == TOKEN_NONE) { - mandoc_msg(MANDOCERR_MACRO, man->parse, - ln, ppos, buf + ppos - 1); + mandoc_msg(MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1); return 1; } @@ -211,8 +222,7 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs) */ if (buf[offs] == '\0' && buf[offs - 1] == ' ') - mandoc_msg(MANDOCERR_SPACE_EOL, man->parse, - ln, offs - 1, NULL); + mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); /* * Some macros break next-line scopes; otherwise, remember @@ -230,16 +240,12 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs) * page, that's very likely what the author intended. */ - if (bline) { - cp = strchr(buf + offs, '\0') - 2; - if (cp >= buf && cp[0] == '\\' && cp[1] == 'c') - bline = 0; - } + if (bline && man_hasc(buf + offs)) + bline = 0; /* Call to handler... */ - assert(man_macros[tok].fp); - (*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf); + (*man_macro(tok)->fp)(man, tok, ln, ppos, &offs, buf); /* In quick mode (for mandocdb), abort after the NAME section. */ @@ -256,15 +262,15 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs) * unless the next-line scope is allowed to continue. */ - if ( ! bline || man->flags & MAN_ELINE || - man_macros[tok].flags & MAN_NSCOPED) + if (bline == 0 || + (man->flags & MAN_BLINE) == 0 || + man->flags & MAN_ELINE || + man_macro(tok)->flags & MAN_NSCOPED) return 1; - assert(man->flags & MAN_BLINE); - man->flags &= ~MAN_BLINE; - man_unscope(man, man->last->parent); roff_body_alloc(man, ln, ppos, man->last->tok); + man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); return 1; } @@ -280,17 +286,17 @@ man_breakscope(struct roff_man *man, int tok) */ if (man->flags & MAN_ELINE && (tok < MAN_TH || - ! (man_macros[tok].flags & MAN_NSCOPED))) { + (man_macro(tok)->flags & MAN_NSCOPED) == 0)) { n = man->last; if (n->type == ROFFT_TEXT) n = n->parent; if (n->tok < MAN_TH || - man_macros[n->tok].flags & MAN_NSCOPED) + (man_macro(n->tok)->flags & (MAN_NSCOPED | MAN_ESCOPED)) + == MAN_NSCOPED) n = n->parent; - mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, - n->line, n->pos, "%s breaks %s", - roff_name[tok], roff_name[n->tok]); + mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, + "%s breaks %s", roff_name[tok], roff_name[n->tok]); roff_node_delete(man, n); man->flags &= ~MAN_ELINE; @@ -302,12 +308,12 @@ man_breakscope(struct roff_man *man, int tok) */ if (man->flags & MAN_BLINE && - (tok == MAN_nf || tok == MAN_fi) && + (tok == ROFF_nf || tok == ROFF_fi) && (man->last->tok == MAN_SH || man->last->tok == MAN_SS)) { n = man->last; man_unscope(man, n); roff_body_alloc(man, n->line, n->pos, n->tok); - man->flags &= ~MAN_BLINE; + man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); } /* @@ -316,68 +322,24 @@ man_breakscope(struct roff_man *man, int tok) * Delete the block that is being broken. */ - if (man->flags & MAN_BLINE && (tok < MAN_TH || - man_macros[tok].flags & MAN_BSCOPE)) { + if (man->flags & MAN_BLINE && tok != ROFF_nf && tok != ROFF_fi && + (tok < MAN_TH || man_macro(tok)->flags & MAN_XSCOPE)) { n = man->last; if (n->type == ROFFT_TEXT) n = n->parent; if (n->tok < MAN_TH || - (man_macros[n->tok].flags & MAN_BSCOPE) == 0) + (man_macro(n->tok)->flags & MAN_XSCOPE) == 0) n = n->parent; assert(n->type == ROFFT_HEAD); n = n->parent; assert(n->type == ROFFT_BLOCK); - assert(man_macros[n->tok].flags & MAN_SCOPED); + assert(man_macro(n->tok)->flags & MAN_BSCOPED); - mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, - n->line, n->pos, "%s breaks %s", - roff_name[tok], roff_name[n->tok]); + mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, + "%s breaks %s", roff_name[tok], roff_name[n->tok]); roff_node_delete(man, n); - man->flags &= ~MAN_BLINE; + man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); } } - -const struct mparse * -man_mparse(const struct roff_man *man) -{ - - assert(man && man->parse); - return man->parse; -} - -void -man_state(struct roff_man *man, struct roff_node *n) -{ - - switch(n->tok) { - case MAN_nf: - case MAN_EX: - if (man->flags & MAN_LITERAL && ! (n->flags & NODE_VALID)) - mandoc_msg(MANDOCERR_NF_SKIP, man->parse, - n->line, n->pos, "nf"); - man->flags |= MAN_LITERAL; - break; - case MAN_fi: - case MAN_EE: - if ( ! (man->flags & MAN_LITERAL) && - ! (n->flags & NODE_VALID)) - mandoc_msg(MANDOCERR_FI_SKIP, man->parse, - n->line, n->pos, "fi"); - man->flags &= ~MAN_LITERAL; - break; - default: - break; - } - man->last->flags |= NODE_VALID; -} - -void -man_validate(struct roff_man *man) -{ - - man->last = man->first; - man_node_validate(man); - man->flags &= ~MAN_LITERAL; -} diff --git a/usr/src/cmd/mandoc/man.h b/usr/src/cmd/mandoc/man.h index d671f9a9e4..ed4375c0c0 100644 --- a/usr/src/cmd/mandoc/man.h +++ b/usr/src/cmd/mandoc/man.h @@ -1,4 +1,4 @@ -/* $Id: man.h,v 1.78 2017/04/24 23:06:18 schwarze Exp $ */ +/* $Id: man.h,v 1.79 2018/08/23 19:33:27 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -18,5 +18,4 @@ struct roff_man; -const struct mparse *man_mparse(const struct roff_man *); void man_validate(struct roff_man *); diff --git a/usr/src/cmd/mandoc/man_html.c b/usr/src/cmd/mandoc/man_html.c index ae5dac1ad9..994a208aa0 100644 --- a/usr/src/cmd/mandoc/man_html.c +++ b/usr/src/cmd/mandoc/man_html.c @@ -1,7 +1,7 @@ -/* $Id: man_html.c,v 1.153 2018/07/27 17:49:31 schwarze Exp $ */ +/* $Id: man_html.c,v 1.173 2019/03/02 16:30:53 schwarze Exp $ */ /* * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013-2015, 2017-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 @@ -33,26 +33,22 @@ #include "html.h" #include "main.h" -/* FIXME: have PD set the default vspace width. */ - #define MAN_ARGS const struct roff_meta *man, \ const struct roff_node *n, \ struct html *h -struct htmlman { +struct man_html_act { int (*pre)(MAN_ARGS); int (*post)(MAN_ARGS); }; -static void print_bvspace(struct html *, - const struct roff_node *); static void print_man_head(const struct roff_meta *, struct html *); static void print_man_nodelist(MAN_ARGS); static void print_man_node(MAN_ARGS); -static int fillmode(struct html *, int); +static char list_continues(const struct roff_node *, + const struct roff_node *); static int man_B_pre(MAN_ARGS); -static int man_HP_pre(MAN_ARGS); static int man_IP_pre(MAN_ARGS); static int man_I_pre(MAN_ARGS); static int man_OP_pre(MAN_ARGS); @@ -60,8 +56,9 @@ static int man_PP_pre(MAN_ARGS); static int man_RS_pre(MAN_ARGS); static int man_SH_pre(MAN_ARGS); static int man_SM_pre(MAN_ARGS); -static int man_SS_pre(MAN_ARGS); +static int man_SY_pre(MAN_ARGS); static int man_UR_pre(MAN_ARGS); +static int man_abort_pre(MAN_ARGS); static int man_alt_pre(MAN_ARGS); static int man_ign_pre(MAN_ARGS); static int man_in_pre(MAN_ARGS); @@ -70,16 +67,17 @@ static void man_root_post(const struct roff_meta *, static void man_root_pre(const struct roff_meta *, struct html *); -static const struct htmlman __mans[MAN_MAX - MAN_TH] = { +static const struct man_html_act man_html_acts[MAN_MAX - MAN_TH] = { { NULL, NULL }, /* TH */ { man_SH_pre, NULL }, /* SH */ - { man_SS_pre, NULL }, /* SS */ + { man_SH_pre, NULL }, /* SS */ { man_IP_pre, NULL }, /* TP */ - { man_PP_pre, NULL }, /* LP */ + { man_IP_pre, NULL }, /* TQ */ + { man_abort_pre, NULL }, /* LP */ { man_PP_pre, NULL }, /* PP */ - { man_PP_pre, NULL }, /* P */ + { man_abort_pre, NULL }, /* P */ { man_IP_pre, NULL }, /* IP */ - { man_HP_pre, NULL }, /* HP */ + { man_PP_pre, NULL }, /* HP */ { man_SM_pre, NULL }, /* SM */ { man_SM_pre, NULL }, /* SB */ { man_alt_pre, NULL }, /* BI */ @@ -91,8 +89,6 @@ static const struct htmlman __mans[MAN_MAX - MAN_TH] = { { man_I_pre, NULL }, /* I */ { man_alt_pre, NULL }, /* IR */ { man_alt_pre, NULL }, /* RI */ - { NULL, NULL }, /* nf */ - { NULL, NULL }, /* fi */ { NULL, NULL }, /* RE */ { man_RS_pre, NULL }, /* RS */ { man_ign_pre, NULL }, /* DT */ @@ -100,6 +96,8 @@ static const struct htmlman __mans[MAN_MAX - MAN_TH] = { { man_ign_pre, NULL }, /* PD */ { man_ign_pre, NULL }, /* AT */ { man_in_pre, NULL }, /* in */ + { man_SY_pre, NULL }, /* SY */ + { NULL, NULL }, /* YS */ { man_OP_pre, NULL }, /* OP */ { NULL, NULL }, /* EX */ { NULL, NULL }, /* EE */ @@ -108,34 +106,10 @@ static const struct htmlman __mans[MAN_MAX - MAN_TH] = { { man_UR_pre, NULL }, /* MT */ { NULL, NULL }, /* ME */ }; -static const struct htmlman *const mans = __mans - MAN_TH; - - -/* - * Printing leading vertical space before a block. - * This is used for the paragraph macros. - * The rules are pretty simple, since there's very little nesting going - * on here. Basically, if we're the first within another block (SS/SH), - * then don't emit vertical space. If we are (RS), then do. If not the - * first, print it. - */ -static void -print_bvspace(struct html *h, const struct roff_node *n) -{ - if (n->body && n->body->child) - if (n->body->child->type == ROFFT_TBL) - return; - - if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS) - if (NULL == n->prev) - return; - - print_paragraph(h); -} void -html_man(void *arg, const struct roff_man *man) +html_man(void *arg, const struct roff_meta *man) { struct html *h; struct roff_node *n; @@ -147,19 +121,19 @@ html_man(void *arg, const struct roff_man *man) if ((h->oflags & HTML_FRAGMENT) == 0) { print_gen_decls(h); print_otag(h, TAG_HTML, ""); - if (n->type == ROFFT_COMMENT) + if (n != NULL && n->type == ROFFT_COMMENT) print_gen_comment(h, n); t = print_otag(h, TAG_HEAD, ""); - print_man_head(&man->meta, h); + print_man_head(man, h); print_tagq(h, t); print_otag(h, TAG_BODY, ""); } - man_root_pre(&man->meta, h); + man_root_pre(man, h); t = print_otag(h, TAG_DIV, "c", "manual-text"); - print_man_nodelist(&man->meta, n, h); + print_man_nodelist(man, n, h); print_tagq(h, t); - man_root_post(&man->meta, h); + man_root_post(man, h); print_tagq(h, NULL); } @@ -178,7 +152,6 @@ print_man_head(const struct roff_meta *man, struct html *h) static void print_man_nodelist(MAN_ARGS) { - while (n != NULL) { print_man_node(man, n, h); n = n->next; @@ -188,99 +161,33 @@ print_man_nodelist(MAN_ARGS) static void print_man_node(MAN_ARGS) { - static int want_fillmode = MAN_fi; - static int save_fillmode; - struct tag *t; int child; - /* - * Handle fill mode switch requests up front, - * they would just cause trouble in the subsequent code. - */ - - switch (n->tok) { - case MAN_nf: - case MAN_EX: - want_fillmode = MAN_nf; + if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) return; - case MAN_fi: - case MAN_EE: - want_fillmode = MAN_fi; - if (fillmode(h, 0) == MAN_fi) - print_otag(h, TAG_BR, ""); - return; - default: - break; - } - /* Set up fill mode for the upcoming node. */ - - switch (n->type) { - case ROFFT_BLOCK: - save_fillmode = 0; - /* Some block macros suspend or cancel .nf. */ - switch (n->tok) { - case MAN_TP: /* Tagged paragraphs */ - case MAN_IP: /* temporarily disable .nf */ - case MAN_HP: /* for the head. */ - save_fillmode = want_fillmode; - /* FALLTHROUGH */ - case MAN_SH: /* Section headers */ - case MAN_SS: /* permanently cancel .nf. */ - want_fillmode = MAN_fi; - /* FALLTHROUGH */ - case MAN_PP: /* These have no head. */ - case MAN_LP: /* They will simply */ - case MAN_P: /* reopen .nf in the body. */ - case MAN_RS: - case MAN_UR: - case MAN_MT: - fillmode(h, MAN_fi); - break; - default: - break; - } - break; - case ROFFT_TBL: - fillmode(h, MAN_fi); - break; - case ROFFT_ELEM: - /* - * Some in-line macros produce tags and/or text - * in the handler, so they require fill mode to be - * configured up front just like for text nodes. - * For the others, keep the traditional approach - * of doing the same, for now. - */ - fillmode(h, want_fillmode); - break; - case ROFFT_TEXT: - if (fillmode(h, want_fillmode) == MAN_fi && - want_fillmode == MAN_fi && - n->flags & NODE_LINE && *n->string == ' ' && - (h->flags & HTML_NONEWLINE) == 0) - print_otag(h, TAG_BR, ""); - if (*n->string != '\0') - break; - print_paragraph(h); - return; - case ROFFT_COMMENT: - return; - default: - break; - } - - /* Produce output for this node. */ + html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi); child = 1; switch (n->type) { case ROFFT_TEXT: + if (*n->string == '\0') { + print_endline(h); + return; + } + if (*n->string == ' ' && n->flags & NODE_LINE && + (h->flags & HTML_NONEWLINE) == 0) + print_endline(h); + else if (n->flags & NODE_DELIMC) + h->flags |= HTML_NOSPACE; t = h->tag; + t->refcnt++; print_text(h, n->string); break; case ROFFT_EQN: t = h->tag; + t->refcnt++; print_eqn(h, n->eqn); break; case ROFFT_TBL: @@ -306,62 +213,51 @@ print_man_node(MAN_ARGS) * the "meta" table state. This will be reopened on the * next table element. */ - if (h->tblt) + if (h->tblt != NULL) print_tblclose(h); - t = h->tag; + t->refcnt++; if (n->tok < ROFF_MAX) { roff_html_pre(h, n); - child = 0; - break; + t->refcnt--; + print_stagq(h, t); + return; } - assert(n->tok >= MAN_TH && n->tok < MAN_MAX); - if (mans[n->tok].pre) - child = (*mans[n->tok].pre)(man, n, h); - - /* Some block macros resume .nf in the body. */ - if (save_fillmode && n->type == ROFFT_BODY) - want_fillmode = save_fillmode; - + if (man_html_acts[n->tok - MAN_TH].pre != NULL) + child = (*man_html_acts[n->tok - MAN_TH].pre)(man, + n, h); break; } - if (child && n->child) + if (child && n->child != NULL) print_man_nodelist(man, n->child, h); /* This will automatically close out any font scope. */ - print_stagq(h, t); + t->refcnt--; + if (n->type == ROFFT_BLOCK && + (n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) { + t = h->tag; + while (t->tag != TAG_DL && t->tag != TAG_UL) + t = t->next; + /* + * Close the list if no further item of the same type + * follows; otherwise, close the item only. + */ + if (list_continues(n, n->next) == '\0') { + print_tagq(h, t); + t = NULL; + } + } + if (t != NULL) + print_stagq(h, t); - if (fillmode(h, 0) == MAN_nf && - n->next != NULL && n->next->flags & NODE_LINE) + if (n->flags & NODE_NOFILL && n->tok != MAN_YS && + (n->next != NULL && n->next->flags & NODE_LINE)) { + /* In .nf = <pre>, print even empty lines. */ + h->col++; print_endline(h); -} - -/* - * MAN_nf switches to no-fill mode, MAN_fi to fill mode. - * Other arguments do not switch. - * The old mode is returned. - */ -static int -fillmode(struct html *h, int want) -{ - struct tag *pre; - int had; - - for (pre = h->tag; pre != NULL; pre = pre->next) - if (pre->tag == TAG_PRE) - break; - - had = pre == NULL ? MAN_fi : MAN_nf; - - if (want && want != had) { - if (want == MAN_nf) - print_otag(h, TAG_PRE, ""); - else - print_tagq(h, pre); } - return had; } static void @@ -382,7 +278,7 @@ man_root_pre(const struct roff_meta *man, struct html *h) print_stagq(h, tt); print_otag(h, TAG_TD, "c", "head-vol"); - if (NULL != man->vol) + if (man->vol != NULL) print_text(h, man->vol); print_stagq(h, tt); @@ -405,7 +301,7 @@ man_root_post(const struct roff_meta *man, struct html *h) print_stagq(h, tt); print_otag(h, TAG_TD, "c", "foot-os"); - if (man->os) + if (man->os != NULL) print_text(h, man->os); print_tagq(h, t); } @@ -413,13 +309,32 @@ man_root_post(const struct roff_meta *man, struct html *h) static int man_SH_pre(MAN_ARGS) { - char *id; - - if (n->type == ROFFT_HEAD) { + const char *class; + char *id; + enum htmltag tag; + + if (n->tok == MAN_SH) { + tag = TAG_H1; + class = "Sh"; + } else { + tag = TAG_H2; + class = "Ss"; + } + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); + print_otag(h, TAG_SECTION, "c", class); + break; + case ROFFT_HEAD: id = html_make_id(n, 1); - print_otag(h, TAG_H1, "cTi", "Sh", id); + print_otag(h, tag, "ci", class, id); if (id != NULL) print_otag(h, TAG_A, "chR", "permalink", id); + break; + case ROFFT_BODY: + break; + default: + abort(); } return 1; } @@ -428,11 +343,11 @@ static int man_alt_pre(MAN_ARGS) { const struct roff_node *nn; + struct tag *t; int i; enum htmltag fp; - struct tag *t; - for (i = 0, nn = n->child; nn; nn = nn->next, i++) { + for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i++) { switch (n->tok) { case MAN_BI: fp = i % 2 ? TAG_I : TAG_B; @@ -474,88 +389,135 @@ static int man_SM_pre(MAN_ARGS) { print_otag(h, TAG_SMALL, ""); - if (MAN_SB == n->tok) + if (n->tok == MAN_SB) print_otag(h, TAG_B, ""); return 1; } static int -man_SS_pre(MAN_ARGS) +man_PP_pre(MAN_ARGS) { - char *id; - - if (n->type == ROFFT_HEAD) { - id = html_make_id(n, 1); - print_otag(h, TAG_H2, "cTi", "Ss", id); - if (id != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); + break; + case ROFFT_HEAD: + return 0; + case ROFFT_BODY: + if (n->child != NULL && + (n->child->flags & NODE_NOFILL) == 0) + print_otag(h, TAG_P, "c", + n->tok == MAN_PP ? "Pp" : "Pp HP"); + break; + default: + abort(); } return 1; } -static int -man_PP_pre(MAN_ARGS) +static char +list_continues(const struct roff_node *n1, const struct roff_node *n2) { - - if (n->type == ROFFT_HEAD) - return 0; - else if (n->type == ROFFT_BLOCK) - print_bvspace(h, n); - - return 1; + const char *s1, *s2; + char c1, c2; + + if (n1 == NULL || n1->type != ROFFT_BLOCK || + n2 == NULL || n2->type != ROFFT_BLOCK) + return '\0'; + if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) && + (n2->tok == MAN_TP || n2->tok == MAN_TQ)) + return ' '; + if (n1->tok != MAN_IP || n2->tok != MAN_IP) + return '\0'; + n1 = n1->head->child; + n2 = n2->head->child; + s1 = n1 == NULL ? "" : n1->string; + s2 = n2 == NULL ? "" : n2->string; + c1 = strcmp(s1, "*") == 0 ? '*' : + strcmp(s1, "\\-") == 0 ? '-' : + strcmp(s1, "\\(bu") == 0 ? 'b' : ' '; + c2 = strcmp(s2, "*") == 0 ? '*' : + strcmp(s2, "\\-") == 0 ? '-' : + strcmp(s2, "\\(bu") == 0 ? 'b' : ' '; + return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1; } static int man_IP_pre(MAN_ARGS) { const struct roff_node *nn; + const char *list_class; + enum htmltag list_elem, body_elem; + char list_type; + + nn = n->type == ROFFT_BLOCK ? n : n->parent; + if ((list_type = list_continues(nn->prev, nn)) == '\0') { + /* Start a new list. */ + if ((list_type = list_continues(nn, nn->next)) == '\0') + list_type = ' '; + switch (list_type) { + case ' ': + list_class = "Bl-tag"; + list_elem = TAG_DL; + break; + case '*': + list_class = "Bl-bullet"; + list_elem = TAG_UL; + break; + case '-': + list_class = "Bl-dash"; + list_elem = TAG_UL; + break; + default: + abort(); + } + } else { + /* Continue a list that was started earlier. */ + list_class = NULL; + list_elem = TAG_MAX; + } + body_elem = list_type == ' ' ? TAG_DD : TAG_LI; - if (n->type == ROFFT_BODY) { - print_otag(h, TAG_DD, ""); + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); + if (list_elem != TAG_MAX) + print_otag(h, list_elem, "c", list_class); return 1; - } else if (n->type != ROFFT_HEAD) { - print_otag(h, TAG_DL, "c", "Bl-tag"); + case ROFFT_HEAD: + if (body_elem == TAG_LI) + return 0; + print_otag(h, TAG_DT, ""); + break; + case ROFFT_BODY: + print_otag(h, body_elem, ""); return 1; + default: + abort(); } - /* FIXME: width specification. */ - - print_otag(h, TAG_DT, ""); - - /* For IP, only print the first header element. */ - - if (MAN_IP == n->tok && n->child) - print_man_node(man, n->child, h); - - /* For TP, only print next-line header elements. */ - - if (MAN_TP == n->tok) { + switch(n->tok) { + case MAN_IP: /* Only print the first header element. */ + if (n->child != NULL) + print_man_node(man, n->child, h); + break; + case MAN_TP: /* Only print next-line header elements. */ + case MAN_TQ: nn = n->child; - while (NULL != nn && 0 == (NODE_LINE & nn->flags)) + while (nn != NULL && (NODE_LINE & nn->flags) == 0) nn = nn->next; - while (NULL != nn) { + while (nn != NULL) { print_man_node(man, nn, h); nn = nn->next; } + break; + default: + abort(); } - return 0; } static int -man_HP_pre(MAN_ARGS) -{ - if (n->type == ROFFT_HEAD) - return 0; - - if (n->type == ROFFT_BLOCK) { - print_bvspace(h, n); - print_otag(h, TAG_DIV, "c", "HP"); - } - return 1; -} - -static int man_OP_pre(MAN_ARGS) { struct tag *tt; @@ -564,14 +526,14 @@ man_OP_pre(MAN_ARGS) h->flags |= HTML_NOSPACE; tt = print_otag(h, TAG_SPAN, "c", "Op"); - if (NULL != (n = n->child)) { + if ((n = n->child) != NULL) { print_otag(h, TAG_B, ""); print_text(h, n->string); } print_stagq(h, tt); - if (NULL != n && NULL != n->next) { + if (n != NULL && n->next != NULL) { print_otag(h, TAG_I, ""); print_text(h, n->next->string); } @@ -606,17 +568,46 @@ man_in_pre(MAN_ARGS) static int man_ign_pre(MAN_ARGS) { - return 0; } static int man_RS_pre(MAN_ARGS) { - if (n->type == ROFFT_HEAD) + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); + break; + case ROFFT_HEAD: return 0; - if (n->type == ROFFT_BLOCK) + case ROFFT_BODY: print_otag(h, TAG_DIV, "c", "Bd-indent"); + break; + default: + abort(); + } + return 1; +} + +static int +man_SY_pre(MAN_ARGS) +{ + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); + print_otag(h, TAG_TABLE, "c", "Nm"); + print_otag(h, TAG_TR, ""); + break; + case ROFFT_HEAD: + print_otag(h, TAG_TD, ""); + print_otag(h, TAG_CODE, "c", "Nm"); + break; + case ROFFT_BODY: + print_otag(h, TAG_TD, ""); + break; + default: + abort(); + } return 1; } @@ -624,16 +615,17 @@ static int man_UR_pre(MAN_ARGS) { char *cp; + n = n->child; assert(n->type == ROFFT_HEAD); if (n->child != NULL) { assert(n->child->type == ROFFT_TEXT); if (n->tok == MAN_MT) { mandoc_asprintf(&cp, "mailto:%s", n->child->string); - print_otag(h, TAG_A, "cTh", "Mt", cp); + print_otag(h, TAG_A, "ch", "Mt", cp); free(cp); } else - print_otag(h, TAG_A, "cTh", "Lk", n->child->string); + print_otag(h, TAG_A, "ch", "Lk", n->child->string); } assert(n->next->type == ROFFT_BODY); @@ -641,6 +633,11 @@ man_UR_pre(MAN_ARGS) n = n->next; print_man_nodelist(man, n->child, h); - return 0; } + +static int +man_abort_pre(MAN_ARGS) +{ + abort(); +} diff --git a/usr/src/cmd/mandoc/man_macro.c b/usr/src/cmd/mandoc/man_macro.c index aa8b200196..d195576dee 100644 --- a/usr/src/cmd/mandoc/man_macro.c +++ b/usr/src/cmd/mandoc/man_macro.c @@ -1,7 +1,7 @@ -/* $Id: man_macro.c,v 1.123 2017/06/25 11:45:37 schwarze Exp $ */ +/* $Id: man_macro.c,v 1.144 2019/01/05 18:59:46 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> * * Permission to use, copy, modify, and distribute this software for any @@ -22,6 +22,7 @@ #include <assert.h> #include <ctype.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> @@ -40,47 +41,54 @@ static int man_args(struct roff_man *, int, int *, char *, char **); static void rew_scope(struct roff_man *, enum roff_tok); -const struct man_macro __man_macros[MAN_MAX - MAN_TH] = { - { in_line_eoln, MAN_BSCOPE }, /* TH */ - { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SH */ - { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SS */ - { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* TP */ - { blk_imp, MAN_BSCOPE }, /* LP */ - { blk_imp, MAN_BSCOPE }, /* PP */ - { blk_imp, MAN_BSCOPE }, /* P */ - { blk_imp, MAN_BSCOPE }, /* IP */ - { blk_imp, MAN_BSCOPE }, /* HP */ - { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SM */ - { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SB */ +static const struct man_macro man_macros[MAN_MAX - MAN_TH] = { + { in_line_eoln, MAN_XSCOPE }, /* TH */ + { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SH */ + { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SS */ + { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TP */ + { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TQ */ + { blk_imp, MAN_XSCOPE }, /* LP */ + { blk_imp, MAN_XSCOPE }, /* PP */ + { blk_imp, MAN_XSCOPE }, /* P */ + { blk_imp, MAN_XSCOPE }, /* IP */ + { blk_imp, MAN_XSCOPE }, /* HP */ + { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SM */ + { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SB */ { in_line_eoln, 0 }, /* BI */ { in_line_eoln, 0 }, /* IB */ { in_line_eoln, 0 }, /* BR */ { in_line_eoln, 0 }, /* RB */ - { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* R */ - { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* B */ - { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* I */ + { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* R */ + { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* B */ + { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* I */ { in_line_eoln, 0 }, /* IR */ { in_line_eoln, 0 }, /* RI */ - { in_line_eoln, MAN_NSCOPED }, /* nf */ - { in_line_eoln, MAN_NSCOPED }, /* fi */ - { blk_close, MAN_BSCOPE }, /* RE */ - { blk_exp, MAN_BSCOPE }, /* RS */ + { blk_close, MAN_XSCOPE }, /* RE */ + { blk_exp, MAN_XSCOPE }, /* RS */ { in_line_eoln, 0 }, /* DT */ { in_line_eoln, 0 }, /* UC */ { in_line_eoln, MAN_NSCOPED }, /* PD */ { in_line_eoln, 0 }, /* AT */ { in_line_eoln, MAN_NSCOPED }, /* in */ + { blk_imp, MAN_XSCOPE }, /* SY */ + { blk_close, MAN_XSCOPE }, /* YS */ { in_line_eoln, 0 }, /* OP */ - { in_line_eoln, MAN_BSCOPE }, /* EX */ - { in_line_eoln, MAN_BSCOPE }, /* EE */ - { blk_exp, MAN_BSCOPE }, /* UR */ - { blk_close, MAN_BSCOPE }, /* UE */ - { blk_exp, MAN_BSCOPE }, /* MT */ - { blk_close, MAN_BSCOPE }, /* ME */ + { in_line_eoln, MAN_XSCOPE }, /* EX */ + { in_line_eoln, MAN_XSCOPE }, /* EE */ + { blk_exp, MAN_XSCOPE }, /* UR */ + { blk_close, MAN_XSCOPE }, /* UE */ + { blk_exp, MAN_XSCOPE }, /* MT */ + { blk_close, MAN_XSCOPE }, /* ME */ }; -const struct man_macro *const man_macros = __man_macros - MAN_TH; +const struct man_macro * +man_macro(enum roff_tok tok) +{ + assert(tok >= MAN_TH && tok <= MAN_MAX); + return man_macros + (tok - MAN_TH); +} + void man_unscope(struct roff_man *man, const struct roff_node *to) { @@ -94,9 +102,10 @@ man_unscope(struct roff_man *man, const struct roff_node *to) if (to == NULL && ! (n->flags & NODE_VALID)) { if (man->flags & (MAN_BLINE | MAN_ELINE) && - man_macros[n->tok].flags & MAN_SCOPED) { - mandoc_vmsg(MANDOCERR_BLK_LINE, - man->parse, n->line, n->pos, + man_macro(n->tok)->flags & + (MAN_BSCOPED | MAN_NSCOPED)) { + mandoc_msg(MANDOCERR_BLK_LINE, + n->line, n->pos, "EOF breaks %s", roff_name[n->tok]); if (man->flags & MAN_ELINE) man->flags &= ~MAN_ELINE; @@ -111,9 +120,9 @@ man_unscope(struct roff_man *man, const struct roff_node *to) continue; } if (n->type == ROFFT_BLOCK && - man_macros[n->tok].fp == blk_exp) + man_macro(n->tok)->fp == blk_exp) mandoc_msg(MANDOCERR_BLK_NOEND, - man->parse, n->line, n->pos, + n->line, n->pos, "%s", roff_name[n->tok]); } @@ -175,7 +184,7 @@ rew_scope(struct roff_man *man, enum roff_tok tok) } if (tok != MAN_SH && (n->tok == MAN_SH || (tok != MAN_SS && (n->tok == MAN_SS || - man_macros[n->tok].fp == blk_exp)))) + man_macro(n->tok)->fp == blk_exp)))) return; man_unscope(man, n); n = man->last; @@ -189,33 +198,39 @@ rew_scope(struct roff_man *man, enum roff_tok tok) void blk_close(MACRO_PROT_ARGS) { - enum roff_tok ntok; + enum roff_tok ctok, ntok; const struct roff_node *nn; - char *p; - int nrew, target; + char *p, *ep; + int cline, cpos, la, nrew, target; nrew = 1; switch (tok) { case MAN_RE: ntok = MAN_RS; + la = *pos; if ( ! man_args(man, line, pos, buf, &p)) break; for (nn = man->last->parent; nn; nn = nn->parent) if (nn->tok == ntok && nn->type == ROFFT_BLOCK) nrew++; - target = strtol(p, &p, 10); - if (*p != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, - line, p - buf, "RE ... %s", p); + target = strtol(p, &ep, 10); + if (*ep != '\0') + mandoc_msg(MANDOCERR_ARG_EXCESS, line, + la + (buf[la] == '"') + (int)(ep - p), + "RE ... %s", ep); + free(p); if (target == 0) target = 1; nrew -= target; if (nrew < 1) { - mandoc_vmsg(MANDOCERR_RE_NOTOPEN, man->parse, + mandoc_msg(MANDOCERR_RE_NOTOPEN, line, ppos, "RE %d", target); return; } break; + case MAN_YS: + ntok = MAN_SY; + break; case MAN_UE: ntok = MAN_UR; break; @@ -231,25 +246,47 @@ blk_close(MACRO_PROT_ARGS) break; if (nn == NULL) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, man->parse, - line, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, + line, ppos, "%s", roff_name[tok]); rew_scope(man, MAN_PP); - } else { - line = man->last->line; - ppos = man->last->pos; - ntok = man->last->tok; - man_unscope(man, nn); + if (tok == MAN_RE) { + roff_elem_alloc(man, line, ppos, ROFF_br); + man->last->flags |= NODE_LINE | + NODE_VALID | NODE_ENDED; + man->next = ROFF_NEXT_SIBLING; + } + return; + } - if (tok == MAN_RE && nn->head->aux > 0) - roff_setreg(man->roff, "an-margin", - nn->head->aux, '-'); + cline = man->last->line; + cpos = man->last->pos; + ctok = man->last->tok; + man_unscope(man, nn); - /* Move a trailing paragraph behind the block. */ + if (tok == MAN_RE && nn->head->aux > 0) + roff_setreg(man->roff, "an-margin", nn->head->aux, '-'); - if (ntok == MAN_LP || ntok == MAN_PP || ntok == MAN_P) { - *pos = strlen(buf); - blk_imp(man, ntok, line, ppos, pos, buf); - } + /* Trailing text. */ + + if (buf[*pos] != '\0') { + roff_word_alloc(man, line, ppos, buf + *pos); + man->last->flags |= NODE_DELIMC; + if (mandoc_eos(man->last->string, strlen(man->last->string))) + man->last->flags |= NODE_EOS; + } + + /* Move a trailing paragraph behind the block. */ + + if (ctok == MAN_LP || ctok == MAN_PP || ctok == MAN_P) { + *pos = strlen(buf); + blk_imp(man, ctok, cline, cpos, pos, buf); + } + + /* Synopsis blocks need an explicit end marker for spacing. */ + + if (tok == MAN_YS && man->last == nn) { + roff_elem_alloc(man, line, ppos, tok); + man_unscope(man, man->last); } } @@ -260,7 +297,10 @@ blk_exp(MACRO_PROT_ARGS) char *p; int la; - rew_scope(man, tok); + if (tok == MAN_RS) { + rew_scope(man, tok); + man->flags |= ROFF_NONOFILL; + } roff_block_alloc(man, line, ppos, tok); head = roff_head_alloc(man, line, ppos, tok); @@ -275,14 +315,16 @@ blk_exp(MACRO_PROT_ARGS) roff_setreg(man->roff, "an-margin", head->aux, '+'); } + free(p); } if (buf[*pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, line, - *pos, "%s ... %s", roff_name[tok], buf + *pos); + mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos, + "%s ... %s", roff_name[tok], buf + *pos); man_unscope(man, head); roff_body_alloc(man, line, ppos, tok); + man->flags &= ~ROFF_NONOFILL; } /* @@ -299,9 +341,10 @@ blk_imp(MACRO_PROT_ARGS) struct roff_node *n; rew_scope(man, tok); - n = roff_block_alloc(man, line, ppos, tok); - if (n->tok == MAN_SH || n->tok == MAN_SS) - man->flags &= ~MAN_LITERAL; + man->flags |= ROFF_NONOFILL; + if (tok == MAN_SH || tok == MAN_SS) + man->flags &= ~ROFF_NOFILL; + roff_block_alloc(man, line, ppos, tok); n = roff_head_alloc(man, line, ppos, tok); /* Add line arguments. */ @@ -311,16 +354,17 @@ blk_imp(MACRO_PROT_ARGS) if ( ! man_args(man, line, pos, buf, &p)) break; roff_word_alloc(man, line, la, p); + free(p); } /* * For macros having optional next-line scope, * keep the head open if there were no arguments. - * For `TP', always keep the head open. + * For `TP' and `TQ', always keep the head open. */ - if (man_macros[tok].flags & MAN_SCOPED && - (tok == MAN_TP || n == man->last)) { + if (man_macro(tok)->flags & MAN_BSCOPED && + (tok == MAN_TP || tok == MAN_TQ || n == man->last)) { man->flags |= MAN_BLINE; return; } @@ -329,6 +373,7 @@ blk_imp(MACRO_PROT_ARGS) man_unscope(man, n); roff_body_alloc(man, line, ppos, tok); + man->flags &= ~ROFF_NONOFILL; } void @@ -341,27 +386,26 @@ in_line_eoln(MACRO_PROT_ARGS) roff_elem_alloc(man, line, ppos, tok); n = man->last; + if (tok == MAN_EX) + man->flags |= ROFF_NOFILL; + else if (tok == MAN_EE) + man->flags &= ~ROFF_NOFILL; + for (;;) { - if (buf[*pos] != '\0' && (tok == MAN_fi || tok == MAN_nf)) { - mandoc_vmsg(MANDOCERR_ARG_SKIP, - man->parse, line, *pos, "%s %s", - roff_name[tok], buf + *pos); - break; - } if (buf[*pos] != '\0' && man->last != n && tok == MAN_PD) { - mandoc_vmsg(MANDOCERR_ARG_EXCESS, - man->parse, line, *pos, "%s ... %s", - roff_name[tok], buf + *pos); + mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos, + "%s ... %s", roff_name[tok], buf + *pos); break; } la = *pos; if ( ! man_args(man, line, pos, buf, &p)) break; - if (man_macros[tok].flags & MAN_JOIN && + if (man_macro(tok)->flags & MAN_JOIN && man->last->type == ROFFT_TEXT) roff_word_append(man, p); else roff_word_alloc(man, line, la, p); + free(p); } /* @@ -374,13 +418,12 @@ in_line_eoln(MACRO_PROT_ARGS) man->last->flags |= NODE_EOS; /* - * If no arguments are specified and this is MAN_SCOPED (i.e., + * If no arguments are specified and this is MAN_ESCOPED (i.e., * next-line scoped), then set our mode to indicate that we're * waiting for terms to load into our context. */ - if (n == man->last && man_macros[tok].flags & MAN_SCOPED) { - assert( ! (man_macros[tok].flags & MAN_NSCOPED)); + if (n == man->last && man_macro(tok)->flags & MAN_ESCOPED) { man->flags |= MAN_ELINE; return; } @@ -391,18 +434,21 @@ in_line_eoln(MACRO_PROT_ARGS) /* Rewind our element scope. */ for ( ; man->last; man->last = man->last->parent) { - man_state(man, man->last); + man->last->flags |= NODE_VALID; if (man->last == n) break; } + + /* Rewind next-line scoped ancestors, if any. */ + + if (man_macro(tok)->flags & MAN_ESCOPED) + man_descope(man, line, ppos, NULL); } void man_endparse(struct roff_man *man) { - - man_unscope(man, man->first); - man->flags &= ~MAN_LITERAL; + man_unscope(man, man->meta.first); } static int @@ -417,6 +463,6 @@ man_args(struct roff_man *man, int line, int *pos, char *buf, char **v) if ('\0' == *start) return 0; - *v = mandoc_getarg(man->parse, v, line, pos); + *v = roff_getarg(man->roff, v, line, pos); return 1; } diff --git a/usr/src/cmd/mandoc/man_term.c b/usr/src/cmd/mandoc/man_term.c index b5723ccf83..4bb3f9da28 100644 --- a/usr/src/cmd/mandoc/man_term.c +++ b/usr/src/cmd/mandoc/man_term.c @@ -1,7 +1,7 @@ -/* $Id: man_term.c,v 1.211 2018/06/10 15:12:35 schwarze Exp $ */ +/* $Id: man_term.c,v 1.228 2019/01/05 21:18:26 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010-2015, 2017-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 @@ -27,7 +27,6 @@ #include <string.h> #include "mandoc_aux.h" -#include "mandoc.h" #include "roff.h" #include "man.h" #include "out.h" @@ -37,8 +36,6 @@ #define MAXMARGINS 64 /* maximum number of indented scopes */ struct mtermp { - int fl; -#define MANT_LITERAL (1 << 0) int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */ int lmargincur; /* index of current margin */ int lmarginsz; /* actual number of nested margins */ @@ -51,7 +48,7 @@ struct mtermp { struct roff_node *n, \ const struct roff_meta *meta -struct termact { +struct man_term_act { int (*pre)(DECL_ARGS); void (*post)(DECL_ARGS); int flags; @@ -78,8 +75,10 @@ static int pre_PP(DECL_ARGS); static int pre_RS(DECL_ARGS); static int pre_SH(DECL_ARGS); static int pre_SS(DECL_ARGS); +static int pre_SY(DECL_ARGS); static int pre_TP(DECL_ARGS); static int pre_UR(DECL_ARGS); +static int pre_abort(DECL_ARGS); static int pre_alternate(DECL_ARGS); static int pre_ign(DECL_ARGS); static int pre_in(DECL_ARGS); @@ -89,18 +88,19 @@ static void post_IP(DECL_ARGS); static void post_HP(DECL_ARGS); static void post_RS(DECL_ARGS); static void post_SH(DECL_ARGS); -static void post_SS(DECL_ARGS); +static void post_SY(DECL_ARGS); static void post_TP(DECL_ARGS); static void post_UR(DECL_ARGS); -static const struct termact __termacts[MAN_MAX - MAN_TH] = { +static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = { { NULL, NULL, 0 }, /* TH */ { pre_SH, post_SH, 0 }, /* SH */ - { pre_SS, post_SS, 0 }, /* SS */ + { pre_SS, post_SH, 0 }, /* SS */ { pre_TP, post_TP, 0 }, /* TP */ - { pre_PP, NULL, 0 }, /* LP */ + { pre_TP, post_TP, 0 }, /* TQ */ + { pre_abort, NULL, 0 }, /* LP */ { pre_PP, NULL, 0 }, /* PP */ - { pre_PP, NULL, 0 }, /* P */ + { pre_abort, NULL, 0 }, /* P */ { pre_IP, post_IP, 0 }, /* IP */ { pre_HP, post_HP, 0 }, /* HP */ { NULL, NULL, 0 }, /* SM */ @@ -114,8 +114,6 @@ static const struct termact __termacts[MAN_MAX - MAN_TH] = { { pre_I, NULL, 0 }, /* I */ { pre_alternate, NULL, 0 }, /* IR */ { pre_alternate, NULL, 0 }, /* RI */ - { pre_literal, NULL, 0 }, /* nf */ - { pre_literal, NULL, 0 }, /* fi */ { NULL, NULL, 0 }, /* RE */ { pre_RS, post_RS, 0 }, /* RS */ { pre_DT, NULL, 0 }, /* DT */ @@ -123,6 +121,8 @@ static const struct termact __termacts[MAN_MAX - MAN_TH] = { { pre_PD, NULL, MAN_NOTEXT }, /* PD */ { pre_ign, NULL, 0 }, /* AT */ { pre_in, NULL, MAN_NOTEXT }, /* in */ + { pre_SY, post_SY, 0 }, /* SY */ + { NULL, NULL, 0 }, /* YS */ { pre_OP, NULL, 0 }, /* OP */ { pre_literal, NULL, 0 }, /* EX */ { pre_literal, NULL, 0 }, /* EE */ @@ -131,15 +131,22 @@ static const struct termact __termacts[MAN_MAX - MAN_TH] = { { pre_UR, post_UR, 0 }, /* MT */ { NULL, NULL, 0 }, /* ME */ }; -static const struct termact *termacts = __termacts - MAN_TH; +static const struct man_term_act *man_term_act(enum roff_tok); +static const struct man_term_act * +man_term_act(enum roff_tok tok) +{ + assert(tok >= MAN_TH && tok <= MAN_MAX); + return man_term_acts + (tok - MAN_TH); +} + void -terminal_man(void *arg, const struct roff_man *man) +terminal_man(void *arg, const struct roff_meta *man) { + struct mtermp mt; struct termp *p; struct roff_node *n; - struct mtermp mt; size_t save_defindent; p = (struct termp *)arg; @@ -151,7 +158,7 @@ terminal_man(void *arg, const struct roff_man *man) term_tab_set(p, "T"); term_tab_set(p, ".5i"); - memset(&mt, 0, sizeof(struct mtermp)); + memset(&mt, 0, sizeof(mt)); mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); mt.offset = term_len(p, p->defindent); mt.pardist = 1; @@ -164,18 +171,17 @@ terminal_man(void *arg, const struct roff_man *man) !strcmp(n->child->child->string, "SYNOPSIS")) { if (n->child->next->child != NULL) print_man_nodelist(p, &mt, - n->child->next->child, - &man->meta); + n->child->next->child, man); term_newln(p); break; } n = n->next; } } else { - term_begin(p, print_man_head, print_man_foot, &man->meta); + term_begin(p, print_man_head, print_man_foot, man); p->flags |= TERMP_NOSPACE; if (n != NULL) - print_man_nodelist(p, &mt, n, &man->meta); + print_man_nodelist(p, &mt, n, man); term_end(p); } p->defindent = save_defindent; @@ -196,12 +202,12 @@ print_bvspace(struct termp *p, const struct roff_node *n, int pardist) term_newln(p); - if (n->body && n->body->child) + if (n->body != NULL && n->body->child != NULL) if (n->body->child->type == ROFFT_TBL) return; if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS) - if (NULL == n->prev) + if (n->prev == NULL) return; for (i = 0; i < pardist; i++) @@ -210,16 +216,20 @@ print_bvspace(struct termp *p, const struct roff_node *n, int pardist) static int -pre_ign(DECL_ARGS) +pre_abort(DECL_ARGS) { + abort(); +} +static int +pre_ign(DECL_ARGS) +{ return 0; } static int pre_I(DECL_ARGS) { - term_fontrepl(p, TERMFONT_UNDER); return 1; } @@ -227,14 +237,8 @@ pre_I(DECL_ARGS) static int pre_literal(DECL_ARGS) { - term_newln(p); - if (n->tok == MAN_nf || n->tok == MAN_EX) - mt->fl |= MANT_LITERAL; - else - mt->fl &= ~MANT_LITERAL; - /* * Unlike .IP and .TP, .HP does not have a HEAD. * So in case a second call to term_flushln() is needed, @@ -247,7 +251,6 @@ pre_literal(DECL_ARGS) p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); p->flags |= TERMP_NOSPACE; } - return 0; } @@ -272,7 +275,7 @@ pre_alternate(DECL_ARGS) { enum termfont font[2]; struct roff_node *nn; - int savelit, i; + int i; switch (n->tok) { case MAN_RB: @@ -302,29 +305,21 @@ pre_alternate(DECL_ARGS) default: abort(); } - - savelit = MANT_LITERAL & mt->fl; - mt->fl &= ~MANT_LITERAL; - - for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { + for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i = 1 - i) { term_fontrepl(p, font[i]); - if (savelit && NULL == nn->next) - mt->fl |= MANT_LITERAL; assert(nn->type == ROFFT_TEXT); term_word(p, nn->string); if (nn->flags & NODE_EOS) - p->flags |= TERMP_SENTENCE; - if (nn->next) + p->flags |= TERMP_SENTENCE; + if (nn->next != NULL) p->flags |= TERMP_NOSPACE; } - return 0; } static int pre_B(DECL_ARGS) { - term_fontrepl(p, TERMFONT_BOLD); return 1; } @@ -332,20 +327,19 @@ pre_B(DECL_ARGS) static int pre_OP(DECL_ARGS) { - term_word(p, "["); - p->flags |= TERMP_NOSPACE; + p->flags |= TERMP_KEEP | TERMP_NOSPACE; - if (NULL != (n = n->child)) { + if ((n = n->child) != NULL) { term_fontrepl(p, TERMFONT_BOLD); term_word(p, n->string); } - if (NULL != n && NULL != n->next) { + if (n != NULL && n->next != NULL) { term_fontrepl(p, TERMFONT_UNDER); term_word(p, n->next->string); } - term_fontrepl(p, TERMFONT_NONE); + p->flags &= ~TERMP_KEEP; p->flags |= TERMP_NOSPACE; term_word(p, "]"); return 0; @@ -369,9 +363,9 @@ pre_in(DECL_ARGS) cp = n->child->string; less = 0; - if ('-' == *cp) + if (*cp == '-') less = -1; - else if ('+' == *cp) + else if (*cp == '+') less = 1; else cp--; @@ -413,13 +407,18 @@ pre_HP(DECL_ARGS) case ROFFT_BLOCK: print_bvspace(p, n, mt->pardist); return 1; + case ROFFT_HEAD: + return 0; case ROFFT_BODY: break; default: - return 0; + abort(); } - if ( ! (MANT_LITERAL & mt->fl)) { + if (n->child == NULL) + return 0; + + if ((n->child->flags & NODE_NOFILL) == 0) { p->flags |= TERMP_NOBREAK | TERMP_BRIND; p->trailspace = 2; } @@ -445,8 +444,10 @@ pre_HP(DECL_ARGS) static void post_HP(DECL_ARGS) { - switch (n->type) { + case ROFFT_BLOCK: + case ROFFT_HEAD: + break; case ROFFT_BODY: term_newln(p); @@ -466,25 +467,27 @@ post_HP(DECL_ARGS) p->tcol->rmargin = p->maxrmargin; break; default: - break; + abort(); } } static int pre_PP(DECL_ARGS) { - switch (n->type) { case ROFFT_BLOCK: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); print_bvspace(p, n, mt->pardist); break; - default: + case ROFFT_HEAD: + return 0; + case ROFFT_BODY: p->tcol->offset = mt->offset; break; + default: + abort(); } - - return n->type != ROFFT_HEAD; + return 1; } static int @@ -492,21 +495,21 @@ pre_IP(DECL_ARGS) { struct roffsu su; const struct roff_node *nn; - int len, savelit; + int len; switch (n->type) { - case ROFFT_BODY: - p->flags |= TERMP_NOSPACE; - break; + case ROFFT_BLOCK: + print_bvspace(p, n, mt->pardist); + return 1; case ROFFT_HEAD: p->flags |= TERMP_NOBREAK; p->trailspace = 1; break; - case ROFFT_BLOCK: - print_bvspace(p, n, mt->pardist); - /* FALLTHROUGH */ + case ROFFT_BODY: + p->flags |= TERMP_NOSPACE; + break; default: - return 1; + abort(); } /* Calculate the offset from the optional second argument. */ @@ -526,33 +529,25 @@ pre_IP(DECL_ARGS) case ROFFT_HEAD: p->tcol->offset = mt->offset; p->tcol->rmargin = mt->offset + len; - - savelit = MANT_LITERAL & mt->fl; - mt->fl &= ~MANT_LITERAL; - - if (n->child) + if (n->child != NULL) print_man_node(p, mt, n->child, meta); - - if (savelit) - mt->fl |= MANT_LITERAL; - return 0; case ROFFT_BODY: p->tcol->offset = mt->offset + len; p->tcol->rmargin = p->maxrmargin; break; default: - break; + abort(); } - return 1; } static void post_IP(DECL_ARGS) { - switch (n->type) { + case ROFFT_BLOCK: + break; case ROFFT_HEAD: term_flushln(p); p->flags &= ~TERMP_NOBREAK; @@ -564,7 +559,7 @@ post_IP(DECL_ARGS) p->tcol->offset = mt->offset; break; default: - break; + abort(); } } @@ -573,9 +568,13 @@ pre_TP(DECL_ARGS) { struct roffsu su; struct roff_node *nn; - int len, savelit; + int len; switch (n->type) { + case ROFFT_BLOCK: + if (n->tok == MAN_TP) + print_bvspace(p, n, mt->pardist); + return 1; case ROFFT_HEAD: p->flags |= TERMP_NOBREAK | TERMP_BRTRSP; p->trailspace = 1; @@ -583,11 +582,8 @@ pre_TP(DECL_ARGS) case ROFFT_BODY: p->flags |= TERMP_NOSPACE; break; - case ROFFT_BLOCK: - print_bvspace(p, n, mt->pardist); - /* FALLTHROUGH */ default: - return 1; + abort(); } /* Calculate offset. */ @@ -609,21 +605,15 @@ pre_TP(DECL_ARGS) p->tcol->offset = mt->offset; p->tcol->rmargin = mt->offset + len; - savelit = MANT_LITERAL & mt->fl; - mt->fl &= ~MANT_LITERAL; - /* Don't print same-line elements. */ nn = n->child; - while (NULL != nn && 0 == (NODE_LINE & nn->flags)) + while (nn != NULL && (nn->flags & NODE_LINE) == 0) nn = nn->next; - while (NULL != nn) { + while (nn != NULL) { print_man_node(p, mt, nn, meta); nn = nn->next; } - - if (savelit) - mt->fl |= MANT_LITERAL; return 0; case ROFFT_BODY: p->tcol->offset = mt->offset + len; @@ -632,17 +622,17 @@ pre_TP(DECL_ARGS) p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP); break; default: - break; + abort(); } - return 1; } static void post_TP(DECL_ARGS) { - switch (n->type) { + case ROFFT_BLOCK: + break; case ROFFT_HEAD: term_flushln(p); break; @@ -651,7 +641,7 @@ post_TP(DECL_ARGS) p->tcol->offset = mt->offset; break; default: - break; + abort(); } } @@ -662,7 +652,6 @@ pre_SS(DECL_ARGS) switch (n->type) { case ROFFT_BLOCK: - mt->fl &= ~MANT_LITERAL; mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); mt->offset = term_len(p, p->defindent); @@ -674,7 +663,7 @@ pre_SS(DECL_ARGS) do { n = n->prev; } while (n != NULL && n->tok >= MAN_TH && - termacts[n->tok].flags & MAN_NOTEXT); + man_term_act(n->tok)->flags & MAN_NOTEXT); if (n == NULL || n->type == ROFFT_COMMENT || (n->tok == MAN_SS && n->body->child == NULL)) break; @@ -698,26 +687,9 @@ pre_SS(DECL_ARGS) default: break; } - return 1; } -static void -post_SS(DECL_ARGS) -{ - - switch (n->type) { - case ROFFT_HEAD: - term_newln(p); - break; - case ROFFT_BODY: - term_newln(p); - break; - default: - break; - } -} - static int pre_SH(DECL_ARGS) { @@ -725,7 +697,6 @@ pre_SH(DECL_ARGS) switch (n->type) { case ROFFT_BLOCK: - mt->fl &= ~MANT_LITERAL; mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); mt->offset = term_len(p, p->defindent); @@ -737,7 +708,7 @@ pre_SH(DECL_ARGS) do { n = n->prev; } while (n != NULL && n->tok >= MAN_TH && - termacts[n->tok].flags & MAN_NOTEXT); + man_term_act(n->tok)->flags & MAN_NOTEXT); if (n == NULL || n->type == ROFFT_COMMENT || (n->tok == MAN_SH && n->body->child == NULL)) break; @@ -759,25 +730,23 @@ pre_SH(DECL_ARGS) p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); break; default: - break; + abort(); } - return 1; } static void post_SH(DECL_ARGS) { - switch (n->type) { - case ROFFT_HEAD: - term_newln(p); + case ROFFT_BLOCK: break; + case ROFFT_HEAD: case ROFFT_BODY: term_newln(p); break; default: - break; + abort(); } } @@ -792,8 +761,10 @@ pre_RS(DECL_ARGS) return 1; case ROFFT_HEAD: return 0; - default: + case ROFFT_BODY: break; + default: + abort(); } n = n->parent->head; @@ -821,42 +792,99 @@ pre_RS(DECL_ARGS) static void post_RS(DECL_ARGS) { - switch (n->type) { case ROFFT_BLOCK: - return; case ROFFT_HEAD: return; - default: - term_newln(p); + case ROFFT_BODY: break; + default: + abort(); } - + term_newln(p); mt->offset -= n->parent->head->aux; p->tcol->offset = mt->offset; - if (--mt->lmarginsz < MAXMARGINS) mt->lmargincur = mt->lmarginsz; } static int -pre_UR(DECL_ARGS) +pre_SY(DECL_ARGS) +{ + const struct roff_node *nn; + int len; + + switch (n->type) { + case ROFFT_BLOCK: + if (n->prev == NULL || n->prev->tok != MAN_SY) + print_bvspace(p, n, mt->pardist); + return 1; + case ROFFT_HEAD: + case ROFFT_BODY: + break; + default: + abort(); + } + + nn = n->parent->head->child; + len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1; + + switch (n->type) { + case ROFFT_HEAD: + p->tcol->offset = mt->offset; + p->tcol->rmargin = mt->offset + len; + if (n->next->child == NULL || + (n->next->child->flags & NODE_NOFILL) == 0) + p->flags |= TERMP_NOBREAK; + term_fontrepl(p, TERMFONT_BOLD); + break; + case ROFFT_BODY: + mt->lmargin[mt->lmargincur] = len; + p->tcol->offset = mt->offset + len; + p->tcol->rmargin = p->maxrmargin; + p->flags |= TERMP_NOSPACE; + break; + default: + abort(); + } + return 1; +} + +static void +post_SY(DECL_ARGS) { + switch (n->type) { + case ROFFT_BLOCK: + break; + case ROFFT_HEAD: + term_flushln(p); + p->flags &= ~TERMP_NOBREAK; + break; + case ROFFT_BODY: + term_newln(p); + p->tcol->offset = mt->offset; + break; + default: + abort(); + } +} +static int +pre_UR(DECL_ARGS) +{ return n->type != ROFFT_HEAD; } static void post_UR(DECL_ARGS) { - if (n->type != ROFFT_BLOCK) return; term_word(p, "<"); p->flags |= TERMP_NOSPACE; - if (NULL != n->child->child) + if (n->child->child != NULL) print_man_node(p, mt, n->child->child, meta); p->flags |= TERMP_NOSPACE; @@ -866,7 +894,8 @@ post_UR(DECL_ARGS) static void print_man_node(DECL_ARGS) { - int c; + const struct man_term_act *act; + int c; switch (n->type) { case ROFFT_TEXT: @@ -884,6 +913,8 @@ print_man_node(DECL_ARGS) } else if (*n->string == ' ' && n->flags & NODE_LINE && (p->flags & TERMP_NONEWLINE) == 0) term_newln(p); + else if (n->flags & NODE_DELIMC) + p->flags |= TERMP_NOSPACE; term_word(p, n->string); goto out; @@ -910,20 +941,20 @@ print_man_node(DECL_ARGS) return; } - assert(n->tok >= MAN_TH && n->tok <= MAN_MAX); - if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) + act = man_term_act(n->tok); + if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM) term_fontrepl(p, TERMFONT_NONE); c = 1; - if (termacts[n->tok].pre) - c = (*termacts[n->tok].pre)(p, mt, n, meta); + if (act->pre != NULL) + c = (*act->pre)(p, mt, n, meta); - if (c && n->child) + if (c && n->child != NULL) print_man_nodelist(p, mt, n->child, meta); - if (termacts[n->tok].post) - (*termacts[n->tok].post)(p, mt, n, meta); - if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) + if (act->post != NULL) + (*act->post)(p, mt, n, meta); + if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM) term_fontrepl(p, TERMFONT_NONE); out: @@ -934,7 +965,7 @@ out: * -man doesn't have nested macros, we don't need to be * more specific than this. */ - if (mt->fl & MANT_LITERAL && + if (n->flags & NODE_NOFILL && ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) && (n->next == NULL || n->next->flags & NODE_LINE)) { p->flags |= TERMP_BRNEVER | TERMP_NOSPACE; @@ -949,15 +980,13 @@ out: p->tcol->rmargin = p->maxrmargin; } } - if (NODE_EOS & n->flags) + if (n->flags & NODE_EOS) p->flags |= TERMP_SENTENCE; } - static void print_man_nodelist(DECL_ARGS) { - while (n != NULL) { print_man_node(p, mt, n, meta); n = n->next; @@ -992,7 +1021,7 @@ print_man_foot(struct termp *p, const struct roff_meta *meta) } mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec); - } else if (meta->os) { + } else if (meta->os != NULL) { title = mandoc_strdup(meta->os); } else { title = mandoc_strdup(""); diff --git a/usr/src/cmd/mandoc/man_validate.c b/usr/src/cmd/mandoc/man_validate.c index d6c51af525..4bfaf764e6 100644 --- a/usr/src/cmd/mandoc/man_validate.c +++ b/usr/src/cmd/mandoc/man_validate.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $Id: man_validate.c,v 1.146 2018/12/31 10:04:39 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org> @@ -24,6 +24,7 @@ #include <errno.h> #include <limits.h> #include <stdarg.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> @@ -40,28 +41,32 @@ typedef void (*v_check)(CHKARGS); +static void check_abort(CHKARGS); static void check_par(CHKARGS); static void check_part(CHKARGS); static void check_root(CHKARGS); static void check_text(CHKARGS); static void post_AT(CHKARGS); +static void post_EE(CHKARGS); +static void post_EX(CHKARGS); static void post_IP(CHKARGS); static void post_OP(CHKARGS); +static void post_SH(CHKARGS); static void post_TH(CHKARGS); static void post_UC(CHKARGS); static void post_UR(CHKARGS); static void post_in(CHKARGS); -static void post_vs(CHKARGS); -static const v_check __man_valids[MAN_MAX - MAN_TH] = { +static const v_check man_valids[MAN_MAX - MAN_TH] = { post_TH, /* TH */ - NULL, /* SH */ - NULL, /* SS */ + post_SH, /* SH */ + post_SH, /* SS */ NULL, /* TP */ - check_par, /* LP */ + NULL, /* TQ */ + check_abort,/* LP */ check_par, /* PP */ - check_par, /* P */ + check_abort,/* P */ post_IP, /* IP */ NULL, /* HP */ NULL, /* SM */ @@ -75,8 +80,6 @@ static const v_check __man_valids[MAN_MAX - MAN_TH] = { NULL, /* I */ NULL, /* IR */ NULL, /* RI */ - NULL, /* nf */ - NULL, /* fi */ NULL, /* RE */ check_part, /* RS */ NULL, /* DT */ @@ -84,33 +87,56 @@ static const v_check __man_valids[MAN_MAX - MAN_TH] = { NULL, /* PD */ post_AT, /* AT */ post_in, /* in */ + NULL, /* SY */ + NULL, /* YS */ post_OP, /* OP */ - NULL, /* EX */ - NULL, /* EE */ + post_EX, /* EX */ + post_EE, /* EE */ post_UR, /* UR */ NULL, /* UE */ post_UR, /* MT */ NULL, /* ME */ }; -static const v_check *man_valids = __man_valids - MAN_TH; +/* Validate the subtree rooted at man->last. */ void -man_node_validate(struct roff_man *man) +man_validate(struct roff_man *man) { struct roff_node *n; const v_check *cp; + /* + * Translate obsolete macros such that later code + * does not need to look for them. + */ + n = man->last; + switch (n->tok) { + case MAN_LP: + case MAN_P: + n->tok = MAN_PP; + break; + default: + break; + } + + /* + * Iterate over all children, recursing into each one + * in turn, depth-first. + */ + man->last = man->last->child; while (man->last != NULL) { - man_node_validate(man); + man_validate(man); if (man->last == n) man->last = man->last->child; else man->last = man->last->next; } + /* Finally validate the macro itself. */ + man->last = n; man->next = ROFF_NEXT_SIBLING; switch (n->type) { @@ -126,23 +152,15 @@ man_node_validate(struct roff_man *man) break; default: if (n->tok < ROFF_MAX) { - switch (n->tok) { - case ROFF_br: - case ROFF_sp: - post_vs(man, n); - break; - default: - roff_validate(man); - break; - } + roff_validate(man); break; } assert(n->tok >= MAN_TH && n->tok < MAN_MAX); - cp = man_valids + n->tok; + cp = man_valids + (n->tok - MAN_TH); if (*cp) (*cp)(man, n); if (man->last == n) - man_state(man, n); + n->flags |= NODE_VALID; break; } } @@ -153,14 +171,12 @@ check_root(CHKARGS) assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0); if (n->last == NULL || n->last->type == ROFFT_COMMENT) - mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse, - n->line, n->pos, NULL); + mandoc_msg(MANDOCERR_DOC_EMPTY, n->line, n->pos, NULL); else man->meta.hasbody = 1; if (NULL == man->meta.title) { - mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse, - n->line, n->pos, NULL); + mandoc_msg(MANDOCERR_TH_NOTITLE, n->line, n->pos, NULL); /* * If a title hasn't been set, do so now (by @@ -175,23 +191,43 @@ check_root(CHKARGS) if (man->meta.os_e && (man->meta.rcsids & (1 << man->meta.os_e)) == 0) - mandoc_msg(MANDOCERR_RCS_MISSING, man->parse, 0, 0, + mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0, man->meta.os_e == MANDOC_OS_OPENBSD ? "(OpenBSD)" : "(NetBSD)"); } static void +check_abort(CHKARGS) +{ + abort(); +} + +static void check_text(CHKARGS) { char *cp, *p; - if (MAN_LITERAL & man->flags) + if (n->flags & NODE_NOFILL) return; cp = n->string; for (p = cp; NULL != (p = strchr(p, '\t')); p++) - mandoc_msg(MANDOCERR_FI_TAB, man->parse, - n->line, n->pos + (p - cp), NULL); + mandoc_msg(MANDOCERR_FI_TAB, + n->line, n->pos + (int)(p - cp), NULL); +} + +static void +post_EE(CHKARGS) +{ + if ((n->flags & NODE_NOFILL) == 0) + mandoc_msg(MANDOCERR_FI_SKIP, n->line, n->pos, "EE"); +} + +static void +post_EX(CHKARGS) +{ + if (n->flags & NODE_NOFILL) + mandoc_msg(MANDOCERR_NF_SKIP, n->line, n->pos, "EX"); } static void @@ -199,21 +235,55 @@ post_OP(CHKARGS) { if (n->child == NULL) - mandoc_msg(MANDOCERR_OP_EMPTY, man->parse, - n->line, n->pos, "OP"); + mandoc_msg(MANDOCERR_OP_EMPTY, n->line, n->pos, "OP"); else if (n->child->next != NULL && n->child->next->next != NULL) { n = n->child->next->next; - mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, n->line, n->pos, "OP ... %s", n->string); } } static void +post_SH(CHKARGS) +{ + struct roff_node *nc; + + if (n->type != ROFFT_BODY || (nc = n->child) == NULL) + return; + + if (nc->tok == MAN_PP && nc->body->child != NULL) { + while (nc->body->last != NULL) { + man->next = ROFF_NEXT_CHILD; + roff_node_relink(man, nc->body->last); + man->last = n; + } + } + + if (nc->tok == MAN_PP || nc->tok == ROFF_sp || nc->tok == ROFF_br) { + mandoc_msg(MANDOCERR_PAR_SKIP, nc->line, nc->pos, + "%s after %s", roff_name[nc->tok], roff_name[n->tok]); + roff_node_delete(man, nc); + } + + /* + * Trailing PP is empty, so it is deleted by check_par(). + * Trailing sp is significant. + */ + + if ((nc = n->last) != NULL && nc->tok == ROFF_br) { + mandoc_msg(MANDOCERR_PAR_SKIP, + nc->line, nc->pos, "%s at the end of %s", + roff_name[nc->tok], roff_name[n->tok]); + roff_node_delete(man, nc); + } +} + +static void post_UR(CHKARGS) { if (n->type == ROFFT_HEAD && n->child == NULL) - mandoc_msg(MANDOCERR_UR_NOHEAD, man->parse, - n->line, n->pos, roff_name[n->tok]); + mandoc_msg(MANDOCERR_UR_NOHEAD, n->line, n->pos, + "%s", roff_name[n->tok]); check_part(man, n); } @@ -222,8 +292,8 @@ check_part(CHKARGS) { if (n->type == ROFFT_BODY && n->child == NULL) - mandoc_msg(MANDOCERR_BLK_EMPTY, man->parse, - n->line, n->pos, roff_name[n->tok]); + mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, + "%s", roff_name[n->tok]); } static void @@ -236,15 +306,22 @@ check_par(CHKARGS) roff_node_delete(man, n); break; case ROFFT_BODY: + if (n->child != NULL && + (n->child->tok == ROFF_sp || n->child->tok == ROFF_br)) { + mandoc_msg(MANDOCERR_PAR_SKIP, + n->child->line, n->child->pos, + "%s after %s", roff_name[n->child->tok], + roff_name[n->tok]); + roff_node_delete(man, n->child); + } if (n->child == NULL) - mandoc_vmsg(MANDOCERR_PAR_SKIP, - man->parse, n->line, n->pos, + mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos, "%s empty", roff_name[n->tok]); break; case ROFFT_HEAD: if (n->child != NULL) - mandoc_vmsg(MANDOCERR_ARG_SKIP, - man->parse, n->line, n->pos, "%s %s%s", + mandoc_msg(MANDOCERR_ARG_SKIP, + n->line, n->pos, "%s %s%s", roff_name[n->tok], n->child->string, n->child->next != NULL ? " ..." : ""); break; @@ -264,8 +341,7 @@ post_IP(CHKARGS) break; case ROFFT_BODY: if (n->parent->head->child == NULL && n->child == NULL) - mandoc_vmsg(MANDOCERR_PAR_SKIP, - man->parse, n->line, n->pos, + mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos, "%s empty", roff_name[n->tok]); break; default: @@ -298,9 +374,8 @@ post_TH(CHKARGS) /* Only warn about this once... */ if (isalpha((unsigned char)*p) && ! isupper((unsigned char)*p)) { - mandoc_vmsg(MANDOCERR_TITLE_CASE, - man->parse, n->line, - n->pos + (p - n->string), + mandoc_msg(MANDOCERR_TITLE_CASE, n->line, + n->pos + (int)(p - n->string), "TH %s", n->string); break; } @@ -308,8 +383,7 @@ post_TH(CHKARGS) man->meta.title = mandoc_strdup(n->string); } else { man->meta.title = mandoc_strdup(""); - mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse, - nb->line, nb->pos, "TH"); + mandoc_msg(MANDOCERR_TH_NOTITLE, nb->line, nb->pos, "TH"); } /* TITLE ->MSEC<- DATE OS VOL */ @@ -320,7 +394,7 @@ post_TH(CHKARGS) man->meta.msec = mandoc_strdup(n->string); else { man->meta.msec = mandoc_strdup(""); - mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse, + mandoc_msg(MANDOCERR_MSEC_MISSING, nb->line, nb->pos, "TH %s", man->meta.title); } @@ -334,7 +408,7 @@ post_TH(CHKARGS) mandoc_normdate(man, n->string, n->line, n->pos); } else { man->meta.date = mandoc_strdup(""); - mandoc_msg(MANDOCERR_DATE_MISSING, man->parse, + mandoc_msg(MANDOCERR_DATE_MISSING, n ? n->line : nb->line, n ? n->pos : nb->pos, "TH"); } @@ -362,7 +436,7 @@ post_TH(CHKARGS) man->meta.vol = mandoc_strdup(p); if (n != NULL && (n = n->next) != NULL) - mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, n->line, n->pos, "TH ... %s", n->string); /* @@ -463,32 +537,3 @@ post_in(CHKARGS) free(n->child->string); n->child->string = s; } - -static void -post_vs(CHKARGS) -{ - - if (NULL != n->prev) - return; - - switch (n->parent->tok) { - case MAN_SH: - case MAN_SS: - case MAN_PP: - case MAN_LP: - case MAN_P: - mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos, - "%s after %s", roff_name[n->tok], - roff_name[n->parent->tok]); - /* FALLTHROUGH */ - case TOKEN_NONE: - /* - * Don't warn about this because it occurs in pod2man - * and would cause considerable (unfixable) warnage. - */ - roff_node_delete(man, n); - break; - default: - break; - } -} diff --git a/usr/src/cmd/mandoc/manconf.h b/usr/src/cmd/mandoc/manconf.h index b4cd31646c..bb3761998c 100644 --- a/usr/src/cmd/mandoc/manconf.h +++ b/usr/src/cmd/mandoc/manconf.h @@ -1,6 +1,6 @@ -/* $Id: manconf.h,v 1.5 2017/07/01 09:47:30 schwarze Exp $ */ +/* $Id: manconf.h,v 1.7 2018/11/22 11:30:23 schwarze Exp $ */ /* - * Copyright (c) 2011, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any @@ -30,12 +30,14 @@ struct manoutput { char *man; char *paper; char *style; + char *tag; size_t indent; size_t width; int fragment; int mdoc; - int synopsisonly; int noval; + int synopsisonly; + int toc; }; struct manconf { diff --git a/usr/src/cmd/mandoc/mandoc.c b/usr/src/cmd/mandoc/mandoc.c index 1279b52ed5..fb9395a585 100644 --- a/usr/src/cmd/mandoc/mandoc.c +++ b/usr/src/cmd/mandoc/mandoc.c @@ -1,4 +1,4 @@ -/* $Id: mandoc.c,v 1.104 2018/07/28 18:34:15 schwarze Exp $ */ +/* $Id: mandoc.c,v 1.114 2018/12/30 00:49:55 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> @@ -32,16 +32,70 @@ #include "mandoc.h" #include "roff.h" #include "libmandoc.h" +#include "roff_int.h" static int a2time(time_t *, const char *, const char *); static char *time2a(time_t); enum mandoc_esc +mandoc_font(const char *cp, int sz) +{ + switch (sz) { + case 0: + return ESCAPE_FONTPREV; + case 1: + switch (cp[0]) { + case 'B': + case '3': + return ESCAPE_FONTBOLD; + case 'I': + case '2': + return ESCAPE_FONTITALIC; + case 'P': + return ESCAPE_FONTPREV; + case 'R': + case '1': + return ESCAPE_FONTROMAN; + case '4': + return ESCAPE_FONTBI; + default: + return ESCAPE_ERROR; + } + case 2: + switch (cp[0]) { + case 'B': + switch (cp[1]) { + case 'I': + return ESCAPE_FONTBI; + default: + return ESCAPE_ERROR; + } + case 'C': + switch (cp[1]) { + case 'B': + return ESCAPE_FONTBOLD; + case 'I': + return ESCAPE_FONTITALIC; + case 'R': + case 'W': + return ESCAPE_FONTCW; + default: + return ESCAPE_ERROR; + } + default: + return ESCAPE_ERROR; + } + default: + return ESCAPE_ERROR; + } +} + +enum mandoc_esc mandoc_escape(const char **end, const char **start, int *sz) { const char *local_start; - int local_sz; + int local_sz, c, i; char term; enum mandoc_esc gly; @@ -56,6 +110,14 @@ mandoc_escape(const char **end, const char **start, int *sz) sz = &local_sz; /* + * Treat "\E" just like "\"; + * it only makes a difference in copy mode. + */ + + if (**end == 'E') + ++*end; + + /* * Beyond the backslash, at least one input character * is part of the escape sequence. With one exception * (see below), that character won't be returned. @@ -77,6 +139,10 @@ mandoc_escape(const char **end, const char **start, int *sz) *sz = 2; break; case '[': + if (**start == ' ') { + ++*end; + return ESCAPE_ERROR; + } gly = ESCAPE_SPECIAL; term = ']'; break; @@ -91,11 +157,26 @@ mandoc_escape(const char **end, const char **start, int *sz) /* * Escapes taking no arguments at all. */ - case 'd': - case 'u': + case '!': + case '?': + return ESCAPE_UNSUPP; + case '%': + case '&': + case ')': case ',': case '/': + case '^': + case 'a': + case 'd': + case 'r': + case 't': + case 'u': + case '{': + case '|': + case '}': return ESCAPE_IGNORE; + case 'c': + return ESCAPE_NOSPACE; case 'p': return ESCAPE_BREAK; @@ -113,32 +194,57 @@ mandoc_escape(const char **end, const char **start, int *sz) * 'X' is the trigger. These have opaque sub-strings. */ case 'F': + case 'f': case 'g': case 'k': case 'M': case 'm': case 'n': + case 'O': case 'V': case 'Y': - gly = ESCAPE_IGNORE; - /* FALLTHROUGH */ - case 'f': - if (ESCAPE_ERROR == gly) - gly = ESCAPE_FONT; + gly = (*start)[-1] == 'f' ? ESCAPE_FONT : ESCAPE_IGNORE; switch (**start) { case '(': + if ((*start)[-1] == 'O') + gly = ESCAPE_ERROR; *start = ++*end; *sz = 2; break; case '[': + if ((*start)[-1] == 'O') + gly = (*start)[1] == '5' ? + ESCAPE_UNSUPP : ESCAPE_ERROR; *start = ++*end; term = ']'; break; default: + if ((*start)[-1] == 'O') { + switch (**start) { + case '0': + gly = ESCAPE_UNSUPP; + break; + case '1': + case '2': + case '3': + case '4': + break; + default: + gly = ESCAPE_ERROR; + break; + } + } *sz = 1; break; } break; + case '*': + if (strncmp(*start, "(.T", 3) != 0) + abort(); + gly = ESCAPE_DEVICE; + *start = ++*end; + *sz = 2; + break; /* * These escapes are of the form \X'Y', where 'X' is the trigger @@ -250,18 +356,29 @@ mandoc_escape(const char **end, const char **start, int *sz) break; /* - * Anything else is assumed to be a glyph. - * In this case, pass back the character after the backslash. + * Several special characters can be encoded as + * one-byte escape sequences without using \[]. */ - default: + case ' ': + case '\'': + case '-': + case '.': + case '0': + case ':': + case '_': + case '`': + case 'e': + case '~': gly = ESCAPE_SPECIAL; + /* FALLTHROUGH */ + default: + if (gly == ESCAPE_ERROR) + gly = ESCAPE_UNDEF; *start = --*end; *sz = 1; break; } - assert(ESCAPE_ERROR != gly); - /* * Read up to the terminating character, * paying attention to nested escapes. @@ -284,6 +401,15 @@ mandoc_escape(const char **end, const char **start, int *sz) } } *sz = (*end)++ - *start; + + /* + * The file chars.c only provides one common list + * of character names, but \[-] == \- is the only + * one of the characters with one-byte names that + * allows enclosing the name in brackets. + */ + if (gly == ESCAPE_SPECIAL && *sz == 1 && **start != '-') + return ESCAPE_ERROR; } else { assert(*sz > 0); if ((size_t)*sz > strlen(*start)) @@ -295,43 +421,25 @@ mandoc_escape(const char **end, const char **start, int *sz) switch (gly) { case ESCAPE_FONT: - if (2 == *sz) { - if ('C' == **start) { - /* - * Treat constant-width font modes - * just like regular font modes. - */ - (*start)++; - (*sz)--; - } else { - if ('B' == (*start)[0] && 'I' == (*start)[1]) - gly = ESCAPE_FONTBI; + gly = mandoc_font(*start, *sz); + break; + case ESCAPE_SPECIAL: + if (**start == 'c') { + if (*sz < 6 || *sz > 7 || + strncmp(*start, "char", 4) != 0 || + (int)strspn(*start + 4, "0123456789") + 4 < *sz) break; - } - } else if (1 != *sz) - break; - - switch (**start) { - case '3': - case 'B': - gly = ESCAPE_FONTBOLD; - break; - case '2': - case 'I': - gly = ESCAPE_FONTITALIC; - break; - case 'P': - gly = ESCAPE_FONTPREV; - break; - case '1': - case 'R': - gly = ESCAPE_FONTROMAN; + c = 0; + for (i = 4; i < *sz; i++) + c = 10 * c + ((*start)[i] - '0'); + if (c < 0x21 || (c > 0x7e && c < 0xa0) || c > 0xff) + break; + *start += 4; + *sz -= 4; + gly = ESCAPE_NUMBERED; break; } - break; - case ESCAPE_SPECIAL: - if (1 == *sz && 'c' == **start) - gly = ESCAPE_NOSPACE; + /* * Unicode escapes are defined in groff as \[u0000] * to \[u10FFFF], where the contained value must be @@ -358,101 +466,6 @@ mandoc_escape(const char **end, const char **start, int *sz) return gly; } -/* - * Parse a quoted or unquoted roff-style request or macro argument. - * Return a pointer to the parsed argument, which is either the original - * pointer or advanced by one byte in case the argument is quoted. - * NUL-terminate the argument in place. - * Collapse pairs of quotes inside quoted arguments. - * Advance the argument pointer to the next argument, - * or to the NUL byte terminating the argument line. - */ -char * -mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos) -{ - char *start, *cp; - int quoted, pairs, white; - - /* Quoting can only start with a new word. */ - start = *cpp; - quoted = 0; - if ('"' == *start) { - quoted = 1; - start++; - } - - pairs = 0; - white = 0; - for (cp = start; '\0' != *cp; cp++) { - - /* - * Move the following text left - * after quoted quotes and after "\\" and "\t". - */ - if (pairs) - cp[-pairs] = cp[0]; - - if ('\\' == cp[0]) { - /* - * In copy mode, translate double to single - * backslashes and backslash-t to literal tabs. - */ - switch (cp[1]) { - case 't': - cp[0] = '\t'; - /* FALLTHROUGH */ - case '\\': - pairs++; - cp++; - break; - case ' ': - /* Skip escaped blanks. */ - if (0 == quoted) - cp++; - break; - default: - break; - } - } else if (0 == quoted) { - if (' ' == cp[0]) { - /* Unescaped blanks end unquoted args. */ - white = 1; - break; - } - } else if ('"' == cp[0]) { - if ('"' == cp[1]) { - /* Quoted quotes collapse. */ - pairs++; - cp++; - } else { - /* Unquoted quotes end quoted args. */ - quoted = 2; - break; - } - } - } - - /* Quoted argument without a closing quote. */ - if (1 == quoted) - mandoc_msg(MANDOCERR_ARG_QUOTE, parse, ln, *pos, NULL); - - /* NUL-terminate this argument and move to the next one. */ - if (pairs) - cp[-pairs] = '\0'; - if ('\0' != *cp) { - *cp++ = '\0'; - while (' ' == *cp) - cp++; - } - *pos += (int)(cp - start) + (quoted ? 1 : 0); - *cpp = cp; - - if ('\0' == *cp && (white || ' ' == cp[-1])) - mandoc_msg(MANDOCERR_SPACE_EOL, parse, ln, *pos, NULL); - - return start; -} - static int a2time(time_t *t, const char *fmt, const char *p) { @@ -529,7 +542,7 @@ mandoc_normdate(struct roff_man *man, char *in, int ln, int pos) /* No date specified: use today's date. */ if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) { - mandoc_msg(MANDOCERR_DATE_MISSING, man->parse, ln, pos, NULL); + mandoc_msg(MANDOCERR_DATE_MISSING, ln, pos, NULL); return time2a(time(NULL)); } @@ -539,23 +552,20 @@ mandoc_normdate(struct roff_man *man, char *in, int ln, int pos) a2time(&t, "%b %d, %Y", in)) { cp = time2a(t); if (t > time(NULL) + 86400) - mandoc_msg(MANDOCERR_DATE_FUTURE, man->parse, - ln, pos, cp); + mandoc_msg(MANDOCERR_DATE_FUTURE, ln, pos, "%s", cp); else if (*in != '$' && strcmp(in, cp) != 0) - mandoc_msg(MANDOCERR_DATE_NORM, man->parse, - ln, pos, cp); + mandoc_msg(MANDOCERR_DATE_NORM, ln, pos, "%s", cp); return cp; } /* In man(7), do not warn about the legacy format. */ if (a2time(&t, "%Y-%m-%d", in) == 0) - mandoc_msg(MANDOCERR_DATE_BAD, man->parse, ln, pos, in); + mandoc_msg(MANDOCERR_DATE_BAD, ln, pos, "%s", in); else if (t > time(NULL) + 86400) - mandoc_msg(MANDOCERR_DATE_FUTURE, man->parse, ln, pos, in); - else if (man->macroset == MACROSET_MDOC) - mandoc_vmsg(MANDOCERR_DATE_LEGACY, man->parse, - ln, pos, "Dd %s", in); + mandoc_msg(MANDOCERR_DATE_FUTURE, ln, pos, "%s", in); + else if (man->meta.macroset == MACROSET_MDOC) + mandoc_msg(MANDOCERR_DATE_LEGACY, ln, pos, "Dd %s", in); /* Use any non-mdoc(7) date verbatim. */ diff --git a/usr/src/cmd/mandoc/mandoc.h b/usr/src/cmd/mandoc/mandoc.h index dbc266cc3b..a44b192e0f 100644 --- a/usr/src/cmd/mandoc/mandoc.h +++ b/usr/src/cmd/mandoc/mandoc.h @@ -1,7 +1,7 @@ -/* $Id: mandoc.h,v 1.248 2018/07/28 18:34:15 schwarze Exp $ */ +/* $Id: mandoc.h,v 1.262 2018/12/16 00:17:02 schwarze Exp $ */ /* * Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012-2018 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,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. + * + * Error handling, escape sequence, and character utilities. */ #define ASCII_NBRSP 31 /* non-breaking space */ @@ -158,6 +160,7 @@ enum mandocerr { MANDOCERR_LB_BAD, /* unknown library name: Lb ... */ MANDOCERR_RS_BAD, /* invalid content in Rs block: macro */ MANDOCERR_SM_BAD, /* invalid Boolean argument: macro arg */ + MANDOCERR_CHAR_FONT, /* argument contains two font escapes */ MANDOCERR_FT_BAD, /* unknown font, skipping request: ft font */ MANDOCERR_TR_ODD, /* odd number of characters in request: tr char */ @@ -166,6 +169,7 @@ enum mandocerr { MANDOCERR_FI_TAB, /* tab in filled text */ MANDOCERR_EOS, /* new sentence, new line */ MANDOCERR_ESC_BAD, /* invalid escape sequence: esc */ + MANDOCERR_ESC_UNDEF, /* undefined escape, printing literally: char */ MANDOCERR_STR_UNDEF, /* undefined string, using "": name */ /* related to tables */ @@ -195,6 +199,7 @@ enum mandocerr { MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */ MANDOCERR_CHAR_BAD, /* skipping bad character: number */ MANDOCERR_MACRO, /* skipping unknown macro: macro */ + MANDOCERR_REQ_NOMAC, /* skipping request outside macro: ... */ MANDOCERR_REQ_INSEC, /* skipping insecure request: request */ MANDOCERR_IT_STRAY, /* skipping item outside list: It ... */ MANDOCERR_TA_STRAY, /* skipping column outside column list: Ta */ @@ -205,14 +210,18 @@ enum mandocerr { /* related to request and macro arguments */ MANDOCERR_NAMESC, /* escaped character not allowed in a name: name */ + MANDOCERR_ARG_UNDEF, /* using macro argument outside macro */ + MANDOCERR_ARG_NONUM, /* argument number is not numeric */ MANDOCERR_BD_FILE, /* NOT IMPLEMENTED: Bd -file */ MANDOCERR_BD_NOARG, /* skipping display without arguments: Bd */ MANDOCERR_BL_NOTYPE, /* missing list type, using -item: Bl */ MANDOCERR_CE_NONUM, /* argument is not numeric, using 1: ce ... */ + MANDOCERR_CHAR_ARG, /* argument is not a character: char ... */ MANDOCERR_NM_NONAME, /* missing manual name, using "": Nm */ MANDOCERR_OS_UNAME, /* uname(3) system call failed, using UNKNOWN */ MANDOCERR_ST_BAD, /* unknown standard specifier: St standard */ MANDOCERR_IT_NONUM, /* skipping request without numeric argument */ + MANDOCERR_SHIFT, /* excessive shift: ..., but max is ... */ MANDOCERR_SO_PATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */ MANDOCERR_SO_FAIL, /* .so request failed */ MANDOCERR_ARG_SKIP, /* skipping all arguments: macro args */ @@ -223,7 +232,12 @@ enum mandocerr { MANDOCERR_TOOLARGE, /* input too large */ MANDOCERR_CHAR_UNSUPP, /* unsupported control character: number */ + MANDOCERR_ESC_UNSUPP, /* unsupported escape sequence: escape */ MANDOCERR_REQ_UNSUPP, /* unsupported roff request: request */ + MANDOCERR_WHILE_NEST, /* nested .while loops */ + MANDOCERR_WHILE_OUTOF, /* end of scope with open .while loop */ + MANDOCERR_WHILE_INTO, /* end of .while loop in inner scope */ + MANDOCERR_WHILE_FAIL, /* cannot continue this .while loop */ MANDOCERR_TBLOPT_EQN, /* eqn delim option in tbl: arg */ MANDOCERR_TBLLAYOUT_MOD, /* unsupported tbl layout modifier: m */ MANDOCERR_TBLMACRO, /* ignoring macro in table: macro */ @@ -231,206 +245,22 @@ enum mandocerr { MANDOCERR_MAX }; -struct tbl_opts { - char tab; /* cell-separator */ - char decimal; /* decimal point */ - int opts; -#define TBL_OPT_CENTRE (1 << 0) -#define TBL_OPT_EXPAND (1 << 1) -#define TBL_OPT_BOX (1 << 2) -#define TBL_OPT_DBOX (1 << 3) -#define TBL_OPT_ALLBOX (1 << 4) -#define TBL_OPT_NOKEEP (1 << 5) -#define TBL_OPT_NOSPACE (1 << 6) -#define TBL_OPT_NOWARN (1 << 7) - int cols; /* number of columns */ - int lvert; /* width of left vertical line */ - int rvert; /* width of right vertical line */ -}; - -enum tbl_cellt { - TBL_CELL_CENTRE, /* c, C */ - TBL_CELL_RIGHT, /* r, R */ - TBL_CELL_LEFT, /* l, L */ - TBL_CELL_NUMBER, /* n, N */ - TBL_CELL_SPAN, /* s, S */ - TBL_CELL_LONG, /* a, A */ - TBL_CELL_DOWN, /* ^ */ - TBL_CELL_HORIZ, /* _, - */ - TBL_CELL_DHORIZ, /* = */ - TBL_CELL_MAX -}; - -/* - * A cell in a layout row. - */ -struct tbl_cell { - struct tbl_cell *next; - char *wstr; /* min width represented as a string */ - size_t width; /* minimum column width */ - size_t spacing; /* to the right of the column */ - int vert; /* width of subsequent vertical line */ - int col; /* column number, starting from 0 */ - int flags; -#define TBL_CELL_TALIGN (1 << 0) /* t, T */ -#define TBL_CELL_BALIGN (1 << 1) /* d, D */ -#define TBL_CELL_BOLD (1 << 2) /* fB, B, b */ -#define TBL_CELL_ITALIC (1 << 3) /* fI, I, i */ -#define TBL_CELL_EQUAL (1 << 4) /* e, E */ -#define TBL_CELL_UP (1 << 5) /* u, U */ -#define TBL_CELL_WIGN (1 << 6) /* z, Z */ -#define TBL_CELL_WMAX (1 << 7) /* x, X */ - enum tbl_cellt pos; -}; - -/* - * A layout row. - */ -struct tbl_row { - struct tbl_row *next; - struct tbl_cell *first; - struct tbl_cell *last; - int vert; /* width of left vertical line */ -}; - -enum tbl_datt { - TBL_DATA_NONE, /* has no data */ - TBL_DATA_DATA, /* consists of data/string */ - TBL_DATA_HORIZ, /* horizontal line */ - TBL_DATA_DHORIZ, /* double-horizontal line */ - TBL_DATA_NHORIZ, /* squeezed horizontal line */ - TBL_DATA_NDHORIZ /* squeezed double-horizontal line */ -}; - -/* - * A cell within a row of data. The "string" field contains the actual - * string value that's in the cell. The rest is layout. - */ -struct tbl_dat { - struct tbl_cell *layout; /* layout cell */ - struct tbl_dat *next; - char *string; /* data (NULL if not TBL_DATA_DATA) */ - int spans; /* how many spans follow */ - int block; /* T{ text block T} */ - enum tbl_datt pos; -}; - -enum tbl_spant { - TBL_SPAN_DATA, /* span consists of data */ - TBL_SPAN_HORIZ, /* span is horizontal line */ - TBL_SPAN_DHORIZ /* span is double horizontal line */ -}; - -/* - * A row of data in a table. - */ -struct tbl_span { - struct tbl_opts *opts; - struct tbl_row *layout; /* layout row */ - struct tbl_dat *first; - struct tbl_dat *last; - struct tbl_span *prev; - struct tbl_span *next; - int line; /* parse line */ - enum tbl_spant pos; -}; - -enum eqn_boxt { - EQN_TEXT, /* text (number, variable, whatever) */ - EQN_SUBEXPR, /* nested `eqn' subexpression */ - EQN_LIST, /* list (braces, etc.) */ - EQN_PILE, /* vertical pile */ - EQN_MATRIX /* pile of piles */ -}; - -enum eqn_fontt { - EQNFONT_NONE = 0, - EQNFONT_ROMAN, - EQNFONT_BOLD, - EQNFONT_FAT, - EQNFONT_ITALIC, - EQNFONT__MAX -}; - -enum eqn_post { - EQNPOS_NONE = 0, - EQNPOS_SUP, - EQNPOS_SUBSUP, - EQNPOS_SUB, - EQNPOS_TO, - EQNPOS_FROM, - EQNPOS_FROMTO, - EQNPOS_OVER, - EQNPOS_SQRT, - EQNPOS__MAX -}; - -enum eqn_pilet { - EQNPILE_NONE = 0, - EQNPILE_PILE, - EQNPILE_CPILE, - EQNPILE_RPILE, - EQNPILE_LPILE, - EQNPILE_COL, - EQNPILE_CCOL, - EQNPILE_RCOL, - EQNPILE_LCOL, - EQNPILE__MAX -}; - - /* - * A "box" is a parsed mathematical expression as defined by the eqn.7 - * grammar. - */ -struct eqn_box { - int size; /* font size of expression */ -#define EQN_DEFSIZE INT_MIN - enum eqn_boxt type; /* type of node */ - struct eqn_box *first; /* first child node */ - struct eqn_box *last; /* last child node */ - struct eqn_box *next; /* node sibling */ - struct eqn_box *prev; /* node sibling */ - struct eqn_box *parent; /* node sibling */ - char *text; /* text (or NULL) */ - char *left; /* fence left-hand */ - char *right; /* fence right-hand */ - char *top; /* expression over-symbol */ - char *bottom; /* expression under-symbol */ - size_t args; /* arguments in parent */ - size_t expectargs; /* max arguments in parent */ - enum eqn_post pos; /* position of next box */ - enum eqn_fontt font; /* font of box */ - enum eqn_pilet pile; /* equation piling */ -}; - -/* - * Parse options. - */ -#define MPARSE_MDOC 1 /* assume -mdoc */ -#define MPARSE_MAN 2 /* assume -man */ -#define MPARSE_SO 4 /* honour .so requests */ -#define MPARSE_QUICK 8 /* abort the parse early */ -#define MPARSE_UTF8 16 /* accept UTF-8 input */ -#define MPARSE_LATIN1 32 /* accept ISO-LATIN-1 input */ - -enum mandoc_os { - MANDOC_OS_OTHER = 0, - MANDOC_OS_NETBSD, - MANDOC_OS_OPENBSD -}; - enum mandoc_esc { ESCAPE_ERROR = 0, /* bail! unparsable escape */ + ESCAPE_UNSUPP, /* unsupported escape; ignore it */ ESCAPE_IGNORE, /* escape to be ignored */ + ESCAPE_UNDEF, /* undefined escape; print literal character */ ESCAPE_SPECIAL, /* a regular special character */ ESCAPE_FONT, /* a generic font mode */ ESCAPE_FONTBOLD, /* bold font mode */ ESCAPE_FONTITALIC, /* italic font mode */ ESCAPE_FONTBI, /* bold italic font mode */ ESCAPE_FONTROMAN, /* roman font mode */ + ESCAPE_FONTCW, /* constant width font mode */ ESCAPE_FONTPREV, /* previous font mode */ ESCAPE_NUMBERED, /* a numbered glyph */ ESCAPE_UNICODE, /* a unicode codepoint */ + ESCAPE_DEVICE, /* print the output device name */ ESCAPE_BREAK, /* break the output line */ ESCAPE_NOSPACE, /* suppress space if the last on a line */ ESCAPE_HORIZ, /* horizontal movement */ @@ -439,14 +269,18 @@ enum mandoc_esc { ESCAPE_OVERSTRIKE /* overstrike all chars in the argument */ }; -typedef void (*mandocmsg)(enum mandocerr, enum mandoclevel, - const char *, int, int, const char *); - - -struct mparse; -struct roff_man; +enum mandoc_esc mandoc_font(const char *, int sz); enum mandoc_esc mandoc_escape(const char **, const char **, int *); +void mandoc_msg_setoutfile(FILE *); +const char *mandoc_msg_getinfilename(void); +void mandoc_msg_setinfilename(const char *); +enum mandocerr mandoc_msg_getmin(void); +void mandoc_msg_setmin(enum mandocerr); +enum mandoclevel mandoc_msg_getrc(void); +void mandoc_msg_setrc(enum mandoclevel); +void mandoc_msg(enum mandocerr, int, int, const char *, ...) + __attribute__((__format__ (__printf__, 4, 5))); void mchars_alloc(void); void mchars_free(void); int mchars_num2char(const char *, size_t); @@ -454,18 +288,3 @@ const char *mchars_uc2str(int); int mchars_num2uc(const char *, size_t); int mchars_spec2cp(const char *, size_t); const char *mchars_spec2str(const char *, size_t, size_t *); -struct mparse *mparse_alloc(int, enum mandocerr, mandocmsg, - enum mandoc_os, const char *); -void mparse_free(struct mparse *); -void mparse_keep(struct mparse *); -int mparse_open(struct mparse *, const char *); -enum mandoclevel mparse_readfd(struct mparse *, int, const char *); -enum mandoclevel mparse_readmem(struct mparse *, void *, size_t, - const char *); -void mparse_reset(struct mparse *); -void mparse_result(struct mparse *, - struct roff_man **, char **); -const char *mparse_getkeep(const struct mparse *); -const char *mparse_strerror(enum mandocerr); -const char *mparse_strlevel(enum mandoclevel); -void mparse_updaterc(struct mparse *, enum mandoclevel *); diff --git a/usr/src/cmd/mandoc/mandoc_msg.c b/usr/src/cmd/mandoc/mandoc_msg.c new file mode 100644 index 0000000000..ff4d80313c --- /dev/null +++ b/usr/src/cmd/mandoc/mandoc_msg.c @@ -0,0 +1,329 @@ +/* $Id: mandoc_msg.c,v 1.6 2019/03/06 15:55:38 schwarze Exp $ */ +/* + * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2014,2015,2016,2017,2018 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 AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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. + */ +#include "config.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "mandoc.h" + +static const enum mandocerr lowest_type[MANDOCLEVEL_MAX] = { + MANDOCERR_OK, + MANDOCERR_OK, + MANDOCERR_WARNING, + MANDOCERR_ERROR, + MANDOCERR_UNSUPP, + MANDOCERR_MAX, + MANDOCERR_MAX +}; + +static const char *const level_name[MANDOCLEVEL_MAX] = { + "SUCCESS", + "STYLE", + "WARNING", + "ERROR", + "UNSUPP", + "BADARG", + "SYSERR" +}; + +static const char *const type_message[MANDOCERR_MAX] = { + "ok", + + "base system convention", + + "Mdocdate found", + "Mdocdate missing", + "unknown architecture", + "operating system explicitly specified", + "RCS id missing", + "referenced manual not found", + + "generic style suggestion", + + "legacy man(7) date format", + "normalizing date format to", + "lower case character in document title", + "duplicate RCS id", + "possible typo in section name", + "unterminated quoted argument", + "useless macro", + "consider using OS macro", + "errnos out of order", + "duplicate errno", + "trailing delimiter", + "no blank before trailing delimiter", + "fill mode already enabled, skipping", + "fill mode already disabled, skipping", + "verbatim \"--\", maybe consider using \\(em", + "function name without markup", + "whitespace at end of input line", + "bad comment style", + + "generic warning", + + /* related to the prologue */ + "missing manual title, using UNTITLED", + "missing manual title, using \"\"", + "missing manual section, using \"\"", + "unknown manual section", + "missing date, using today's date", + "cannot parse date, using it verbatim", + "date in the future, using it anyway", + "missing Os macro, using \"\"", + "late prologue macro", + "prologue macros out of order", + + /* related to document structure */ + ".so is fragile, better use ln(1)", + "no document body", + "content before first section header", + "first section is not \"NAME\"", + "NAME section without Nm before Nd", + "NAME section without description", + "description not at the end of NAME", + "bad NAME section content", + "missing comma before name", + "missing description line, using \"\"", + "description line outside NAME section", + "sections out of conventional order", + "duplicate section title", + "unexpected section", + "cross reference to self", + "unusual Xr order", + "unusual Xr punctuation", + "AUTHORS section without An macro", + + /* related to macros and nesting */ + "obsolete macro", + "macro neither callable nor escaped", + "skipping paragraph macro", + "moving paragraph macro out of list", + "skipping no-space macro", + "blocks badly nested", + "nested displays are not portable", + "moving content out of list", + "first macro on line", + "line scope broken", + "skipping blank line in line scope", + + /* related to missing macro arguments */ + "skipping empty request", + "conditional request controls empty scope", + "skipping empty macro", + "empty block", + "empty argument, using 0n", + "missing display type, using -ragged", + "list type is not the first argument", + "missing -width in -tag list, using 6n", + "missing utility name, using \"\"", + "missing function name, using \"\"", + "empty head in list item", + "empty list item", + "missing argument, using next line", + "missing font type, using \\fR", + "unknown font type, using \\fR", + "nothing follows prefix", + "empty reference block", + "missing section argument", + "missing -std argument, adding it", + "missing option string, using \"\"", + "missing resource identifier, using \"\"", + "missing eqn box, using \"\"", + + /* related to bad macro arguments */ + "duplicate argument", + "skipping duplicate argument", + "skipping duplicate display type", + "skipping duplicate list type", + "skipping -width argument", + "wrong number of cells", + "unknown AT&T UNIX version", + "comma in function argument", + "parenthesis in function name", + "unknown library name", + "invalid content in Rs block", + "invalid Boolean argument", + "argument contains two font escapes", + "unknown font, skipping request", + "odd number of characters in request", + + /* related to plain text */ + "blank line in fill mode, using .sp", + "tab in filled text", + "new sentence, new line", + "invalid escape sequence", + "undefined escape, printing literally", + "undefined string, using \"\"", + + /* related to tables */ + "tbl line starts with span", + "tbl column starts with span", + "skipping vertical bar in tbl layout", + + "generic error", + + /* related to tables */ + "non-alphabetic character in tbl options", + "skipping unknown tbl option", + "missing tbl option argument", + "wrong tbl option argument size", + "empty tbl layout", + "invalid character in tbl layout", + "unmatched parenthesis in tbl layout", + "tbl without any data cells", + "ignoring data in spanned tbl cell", + "ignoring extra tbl data cells", + "data block open at end of tbl", + + /* related to document structure and macros */ + NULL, + "duplicate prologue macro", + "skipping late title macro", + "input stack limit exceeded, infinite loop?", + "skipping bad character", + "skipping unknown macro", + "ignoring request outside macro", + "skipping insecure request", + "skipping item outside list", + "skipping column outside column list", + "skipping end of block that is not open", + "fewer RS blocks open, skipping", + "inserting missing end of block", + "appending missing end of block", + + /* related to request and macro arguments */ + "escaped character not allowed in a name", + "using macro argument outside macro", + "argument number is not numeric", + "NOT IMPLEMENTED: Bd -file", + "skipping display without arguments", + "missing list type, using -item", + "argument is not numeric, using 1", + "argument is not a character", + "missing manual name, using \"\"", + "uname(3) system call failed, using UNKNOWN", + "unknown standard specifier", + "skipping request without numeric argument", + "excessive shift", + "NOT IMPLEMENTED: .so with absolute path or \"..\"", + ".so request failed", + "skipping all arguments", + "skipping excess arguments", + "divide by zero", + + "unsupported feature", + "input too large", + "unsupported control character", + "unsupported escape sequence", + "unsupported roff request", + "nested .while loops", + "end of scope with open .while loop", + "end of .while loop in inner scope", + "cannot continue this .while loop", + "eqn delim option in tbl", + "unsupported tbl layout modifier", + "ignoring macro in table", +}; + +static FILE *fileptr = NULL; +static const char *filename = NULL; +static enum mandocerr min_type = MANDOCERR_MAX; +static enum mandoclevel rc = MANDOCLEVEL_OK; + + +void +mandoc_msg_setoutfile(FILE *fp) +{ + fileptr = fp; +} + +const char * +mandoc_msg_getinfilename(void) +{ + return filename; +} + +void +mandoc_msg_setinfilename(const char *fn) +{ + filename = fn; +} + +enum mandocerr +mandoc_msg_getmin(void) +{ + return min_type; +} + +void +mandoc_msg_setmin(enum mandocerr t) +{ + min_type = t; +} + +enum mandoclevel +mandoc_msg_getrc(void) +{ + return rc; +} + +void +mandoc_msg_setrc(enum mandoclevel level) +{ + if (rc < level) + rc = level; +} + +void +mandoc_msg(enum mandocerr t, int line, int col, const char *fmt, ...) +{ + va_list ap; + enum mandoclevel level; + + if (t < min_type && t != MANDOCERR_FILE) + return; + + level = MANDOCLEVEL_UNSUPP; + while (t < lowest_type[level]) + level--; + mandoc_msg_setrc(level); + + if (fileptr == NULL) + return; + + fprintf(fileptr, "%s:", getprogname()); + if (filename != NULL) + fprintf(fileptr, " %s:", filename); + + if (line > 0) + fprintf(fileptr, "%d:%d:", line, col + 1); + + fprintf(fileptr, " %s", level_name[level]); + if (type_message[t] != NULL) + fprintf(fileptr, ": %s", type_message[t]); + + if (fmt != NULL) { + fprintf(fileptr, ": "); + va_start(ap, fmt); + vfprintf(fileptr, fmt, ap); + va_end(ap); + } + fputc('\n', fileptr); +} diff --git a/usr/src/cmd/mandoc/mandoc_parse.h b/usr/src/cmd/mandoc/mandoc_parse.h new file mode 100644 index 0000000000..61341f0d7f --- /dev/null +++ b/usr/src/cmd/mandoc/mandoc_parse.h @@ -0,0 +1,43 @@ +/* $Id: mandoc_parse.h,v 1.4 2018/12/30 00:49:55 schwarze Exp $ */ +/* + * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2014,2015,2016,2017,2018 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 AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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. + * + * Top level parser interface. For use in the main program + * and in the main parser, but not in formatters. + */ + +/* + * Parse options. + */ +#define MPARSE_MDOC (1 << 0) /* assume -mdoc */ +#define MPARSE_MAN (1 << 1) /* assume -man */ +#define MPARSE_SO (1 << 2) /* honour .so requests */ +#define MPARSE_QUICK (1 << 3) /* abort the parse early */ +#define MPARSE_UTF8 (1 << 4) /* accept UTF-8 input */ +#define MPARSE_LATIN1 (1 << 5) /* accept ISO-LATIN-1 input */ +#define MPARSE_VALIDATE (1 << 6) /* call validation functions */ + + +struct roff_meta; +struct mparse; + +struct mparse *mparse_alloc(int, enum mandoc_os, const char *); +void mparse_copy(const struct mparse *); +void mparse_free(struct mparse *); +int mparse_open(struct mparse *, const char *); +void mparse_readfd(struct mparse *, int, const char *); +void mparse_reset(struct mparse *); +struct roff_meta *mparse_result(struct mparse *); diff --git a/usr/src/cmd/mandoc/mandocdb.c b/usr/src/cmd/mandoc/mandocdb.c index 86dbce2d9f..222350c987 100644 --- a/usr/src/cmd/mandoc/mandocdb.c +++ b/usr/src/cmd/mandoc/mandocdb.c @@ -1,7 +1,7 @@ -/* $Id: mandocdb.c,v 1.258 2018/02/23 18:25:57 schwarze Exp $ */ +/* $Id: mandocdb.c,v 1.262 2018/12/30 00:49:55 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2018 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2016 Ed Maste <emaste@freebsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -52,6 +52,7 @@ #include "roff.h" #include "mdoc.h" #include "man.h" +#include "mandoc_parse.h" #include "manconf.h" #include "mansearch.h" #include "dba_array.h" @@ -185,7 +186,7 @@ static struct ohash names; /* table of all names */ static struct ohash strings; /* table of all strings */ static uint64_t name_mask; -static const struct mdoc_handler __mdocs[MDOC_MAX - MDOC_Dd] = { +static const struct mdoc_handler mdoc_handlers[MDOC_MAX - MDOC_Dd] = { { NULL, 0, NODE_NOPRT }, /* Dd */ { NULL, 0, NODE_NOPRT }, /* Dt */ { NULL, 0, NODE_NOPRT }, /* Os */ @@ -307,7 +308,6 @@ static const struct mdoc_handler __mdocs[MDOC_MAX - MDOC_Dd] = { { NULL, 0, 0 }, /* %U */ { NULL, 0, 0 }, /* Ta */ }; -static const struct mdoc_handler *const mdocs = __mdocs - MDOC_Dd; int @@ -347,6 +347,7 @@ mandocdb(int argc, char *argv[]) goto usage; \ } while (/*CONSTCOND*/0) + mparse_options = MPARSE_VALIDATE; path_arg = NULL; op = OP_DEFAULT; @@ -422,8 +423,7 @@ mandocdb(int argc, char *argv[]) exitcode = (int)MANDOCLEVEL_OK; mchars_alloc(); - mp = mparse_alloc(mparse_options, MANDOCERR_MAX, NULL, - MANDOC_OS_OTHER, NULL); + mp = mparse_alloc(mparse_options, MANDOC_OS_OTHER, NULL); mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); @@ -1116,8 +1116,7 @@ mpages_merge(struct dba *dba, struct mparse *mp) { struct mpage *mpage, *mpage_dest; struct mlink *mlink, *mlink_dest; - struct roff_man *man; - char *sodest; + struct roff_meta *meta; char *cp; int fd; @@ -1130,8 +1129,7 @@ mpages_merge(struct dba *dba, struct mparse *mp) mandoc_ohash_init(&names, 4, offsetof(struct str, key)); mandoc_ohash_init(&strings, 6, offsetof(struct str, key)); mparse_reset(mp); - man = NULL; - sodest = NULL; + meta = NULL; if ((fd = mparse_open(mp, mlink->file)) == -1) { say(mlink->file, "&open"); @@ -1146,14 +1144,14 @@ mpages_merge(struct dba *dba, struct mparse *mp) mparse_readfd(mp, fd, mlink->file); close(fd); fd = -1; - mparse_result(mp, &man, &sodest); + meta = mparse_result(mp); } - if (sodest != NULL) { + if (meta != NULL && meta->sodest != NULL) { mlink_dest = ohash_find(&mlinks, - ohash_qlookup(&mlinks, sodest)); + ohash_qlookup(&mlinks, meta->sodest)); if (mlink_dest == NULL) { - mandoc_asprintf(&cp, "%s.gz", sodest); + mandoc_asprintf(&cp, "%s.gz", meta->sodest); mlink_dest = ohash_find(&mlinks, ohash_qlookup(&mlinks, cp)); free(cp); @@ -1190,39 +1188,36 @@ mpages_merge(struct dba *dba, struct mparse *mp) mpage->mlinks = NULL; } goto nextpage; - } else if (man != NULL && man->macroset == MACROSET_MDOC) { - mdoc_validate(man); + } else if (meta != NULL && meta->macroset == MACROSET_MDOC) { mpage->form = FORM_SRC; - mpage->sec = man->meta.msec; + mpage->sec = meta->msec; mpage->sec = mandoc_strdup( mpage->sec == NULL ? "" : mpage->sec); - mpage->arch = man->meta.arch; + mpage->arch = meta->arch; mpage->arch = mandoc_strdup( mpage->arch == NULL ? "" : mpage->arch); - mpage->title = mandoc_strdup(man->meta.title); - } else if (man != NULL && man->macroset == MACROSET_MAN) { - man_validate(man); - if (*man->meta.msec != '\0' || - *man->meta.title != '\0') { + mpage->title = mandoc_strdup(meta->title); + } else if (meta != NULL && meta->macroset == MACROSET_MAN) { + if (*meta->msec != '\0' || *meta->title != '\0') { mpage->form = FORM_SRC; - mpage->sec = mandoc_strdup(man->meta.msec); + mpage->sec = mandoc_strdup(meta->msec); mpage->arch = mandoc_strdup(mlink->arch); - mpage->title = mandoc_strdup(man->meta.title); + mpage->title = mandoc_strdup(meta->title); } else - man = NULL; + meta = NULL; } assert(mpage->desc == NULL); - if (man == NULL) { + if (meta == NULL) { mpage->form = FORM_CAT; mpage->sec = mandoc_strdup(mlink->dsec); mpage->arch = mandoc_strdup(mlink->arch); mpage->title = mandoc_strdup(mlink->name); parse_cat(mpage, fd); - } else if (man->macroset == MACROSET_MDOC) - parse_mdoc(mpage, &man->meta, man->first); + } else if (meta->macroset == MACROSET_MDOC) + parse_mdoc(mpage, meta, meta->first); else - parse_man(mpage, &man->meta, man->first); + parse_man(mpage, meta, meta->first); if (mpage->desc == NULL) { mpage->desc = mandoc_strdup(mlink->name); if (warnings) @@ -1546,25 +1541,28 @@ static void parse_mdoc(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { + const struct mdoc_handler *handler; for (n = n->child; n != NULL; n = n->next) { - if (n->tok == TOKEN_NONE || - n->tok < ROFF_MAX || - n->flags & mdocs[n->tok].taboo) + if (n->tok == TOKEN_NONE || n->tok < ROFF_MAX) continue; assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); + handler = mdoc_handlers + (n->tok - MDOC_Dd); + if (n->flags & handler->taboo) + continue; + switch (n->type) { case ROFFT_ELEM: case ROFFT_BLOCK: case ROFFT_HEAD: case ROFFT_BODY: case ROFFT_TAIL: - if (mdocs[n->tok].fp != NULL && - (*mdocs[n->tok].fp)(mpage, meta, n) == 0) + if (handler->fp != NULL && + (*handler->fp)(mpage, meta, n) == 0) break; - if (mdocs[n->tok].mask) + if (handler->mask) putmdockey(mpage, n->child, - mdocs[n->tok].mask, mdocs[n->tok].taboo); + handler->mask, handler->taboo); break; default: continue; diff --git a/usr/src/cmd/mandoc/manpath.c b/usr/src/cmd/mandoc/manpath.c index 54f7a6b110..74f38a95db 100644 --- a/usr/src/cmd/mandoc/manpath.c +++ b/usr/src/cmd/mandoc/manpath.c @@ -1,6 +1,6 @@ -/* $Id: manpath.c,v 1.35 2017/07/01 09:47:30 schwarze Exp $ */ +/* $Id: manpath.c,v 1.37 2018/11/22 11:30:23 schwarze Exp $ */ /* - * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any @@ -232,8 +232,8 @@ int manconf_output(struct manoutput *conf, const char *cp, int fromfile) { const char *const toks[] = { - "includes", "man", "paper", "style", - "indent", "width", "fragment", "mdoc", "noval" + "includes", "man", "paper", "style", "indent", "width", + "tag", "fragment", "mdoc", "noval", "toc" }; const char *errstr; @@ -257,7 +257,7 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile) warnx("-O %s=?: Missing argument value", toks[tok]); return -1; } - if ((tok == 6 || tok == 7) && *cp != '\0') { + if (tok > 6 && *cp != '\0') { warnx("-O %s: Does not take a value: %s", toks[tok], cp); return -1; } @@ -312,14 +312,24 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile) warnx("-O width=%s is %s", cp, errstr); return -1; case 6: - conf->fragment = 1; + if (conf->tag != NULL) { + oldval = mandoc_strdup(conf->tag); + break; + } + conf->tag = mandoc_strdup(cp); return 0; case 7: - conf->mdoc = 1; + conf->fragment = 1; return 0; case 8: + conf->mdoc = 1; + return 0; + case 9: conf->noval = 1; return 0; + case 10: + conf->toc = 1; + return 0; default: if (fromfile) warnx("-O %s: Bad argument", cp); diff --git a/usr/src/cmd/mandoc/mansearch.c b/usr/src/cmd/mandoc/mansearch.c index 784c17bee7..4e968332e3 100644 --- a/usr/src/cmd/mandoc/mansearch.c +++ b/usr/src/cmd/mandoc/mansearch.c @@ -1,7 +1,7 @@ -/* $Id: mansearch.c,v 1.77 2017/08/22 17:50:11 schwarze Exp $ */ +/* $Id: mansearch.c,v 1.80 2018/12/13 11:55:46 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013-2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013-2018 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 @@ -36,7 +36,6 @@ #include <string.h> #include <unistd.h> -#include "mandoc.h" #include "mandoc_aux.h" #include "mandoc_ohash.h" #include "manconf.h" @@ -192,7 +191,7 @@ mansearch(const struct mansearch *search, mpage->file, R_OK) == -1) { warn("%s", mpage->file); warnx("outdated mandoc.db contains " - "bogus %s entry, run makewhatis %s", + "bogus %s entry, run makewhatis %s", page->file + 1, paths->paths[i]); free(mpage->file); free(rp); @@ -201,7 +200,6 @@ mansearch(const struct mansearch *search, mpage->names = buildnames(page); mpage->output = buildoutput(outkey, page); mpage->ipath = i; - mpage->bits = rp->bits; mpage->sec = *page->sect - '0'; if (mpage->sec < 0 || mpage->sec > 9) mpage->sec = 10; @@ -296,10 +294,8 @@ manmerge_term(struct expr *e, struct ohash *htab) break; slot = ohash_lookup_memory(htab, (char *)&res, sizeof(res.page), res.page); - if ((rp = ohash_find(htab, slot)) != NULL) { - rp->bits |= res.bits; + if ((rp = ohash_find(htab, slot)) != NULL) continue; - } rp = mandoc_malloc(sizeof(*rp)); *rp = res; ohash_insert(htab, slot, rp); @@ -412,8 +408,7 @@ manpage_compare(const void *vp1, const void *vp2) mp1 = vp1; mp2 = vp2; - if ((diff = mp2->bits - mp1->bits) || - (diff = mp1->sec - mp2->sec)) + if ((diff = mp1->sec - mp2->sec)) return diff; /* Fall back to alphabetic ordering of names. */ @@ -774,8 +769,9 @@ exprterm(const struct mansearch *search, int argc, char *argv[], int *argi) cs = 0; } else if ((val = strpbrk(argv[*argi], "=~")) == NULL) { e->bits = TYPE_Nm | TYPE_Nd; - e->match.type = DBM_SUB; - e->match.str = argv[*argi]; + e->match.type = DBM_REGEX; + val = argv[*argi]; + cs = 0; } else { if (val == argv[*argi]) e->bits = TYPE_Nm | TYPE_Nd; diff --git a/usr/src/cmd/mandoc/mansearch.h b/usr/src/cmd/mandoc/mansearch.h index cc4f364f69..355873f83e 100644 --- a/usr/src/cmd/mandoc/mansearch.h +++ b/usr/src/cmd/mandoc/mansearch.h @@ -1,4 +1,4 @@ -/* $Id: mansearch.h,v 1.28 2017/04/17 20:05:08 schwarze Exp $ */ +/* $Id: mansearch.h,v 1.29 2018/11/22 12:01:46 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2013, 2014, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -93,7 +93,6 @@ struct manpage { char *names; /* a list of names with sections */ char *output; /* user-defined additional output */ size_t ipath; /* number of the manpath */ - uint64_t bits; /* name type mask */ int sec; /* section number, 10 means invalid */ enum form form; }; diff --git a/usr/src/cmd/mandoc/mdoc.c b/usr/src/cmd/mandoc/mdoc.c index 71803531dd..bb3ec58bd0 100644 --- a/usr/src/cmd/mandoc/mdoc.c +++ b/usr/src/cmd/mandoc/mdoc.c @@ -1,7 +1,7 @@ -/* $Id: mdoc.c,v 1.268 2017/08/11 16:56:21 schwarze Exp $ */ +/* $Id: mdoc.c,v 1.274 2018/12/31 07:46:07 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2012-2018 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 @@ -80,13 +80,6 @@ mdoc_parseln(struct roff_man *mdoc, int ln, char *buf, int offs) } void -mdoc_macro(MACRO_PROT_ARGS) -{ - assert(tok >= MDOC_Dd && tok < MDOC_MAX); - (*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf); -} - -void mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, enum roff_tok tok) { struct roff_node *p; @@ -162,15 +155,6 @@ mdoc_elem_alloc(struct roff_man *mdoc, int line, int pos, mdoc->next = ROFF_NEXT_CHILD; } -void -mdoc_node_relink(struct roff_man *mdoc, struct roff_node *p) -{ - - roff_node_unlink(mdoc, p); - p->prev = p->next = NULL; - roff_node_append(mdoc, p); -} - /* * Parse free-form text, that is, a line that does not begin with the * control character. @@ -196,7 +180,8 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) (n->parent != NULL && n->parent->tok == MDOC_Bl && n->parent->norm->Bl.type == LIST_column)) { mdoc->flags |= MDOC_FREECOL; - mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf); + (*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, + line, offs, &offs, buf); return 1; } @@ -225,7 +210,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) * Strip trailing tabs in literal context only; * outside, they affect the next line. */ - if (MDOC_LITERAL & mdoc->flags) + if (mdoc->flags & ROFF_NOFILL) continue; break; case '\\': @@ -242,8 +227,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) *end = '\0'; if (ws) - mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, - line, (int)(ws-buf), NULL); + mandoc_msg(MANDOCERR_SPACE_EOL, line, (int)(ws - buf), NULL); /* * Blank lines are allowed in no-fill mode @@ -251,7 +235,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) * but add a single vertical space elsewhere. */ - if (buf[offs] == '\0' && ! (mdoc->flags & MDOC_LITERAL)) { + if (buf[offs] == '\0' && (mdoc->flags & ROFF_NOFILL) == 0) { switch (mdoc->last->type) { case ROFFT_TEXT: sp = mdoc->last->string; @@ -267,8 +251,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) default: break; } - mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse, - line, (int)(c - buf), NULL); + mandoc_msg(MANDOCERR_FI_BLANK, line, (int)(c - buf), NULL); roff_elem_alloc(mdoc, line, offs, ROFF_sp); mdoc->last->flags |= NODE_VALID | NODE_ENDED; mdoc->next = ROFF_NEXT_SIBLING; @@ -277,7 +260,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) roff_word_alloc(mdoc, line, offs, buf+offs); - if (mdoc->flags & MDOC_LITERAL) + if (mdoc->flags & ROFF_NOFILL) return 1; /* @@ -308,8 +291,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) if (*c == ' ') c++; if (isupper((unsigned char)(*c))) - mandoc_msg(MANDOCERR_EOS, mdoc->parse, - line, (int)(c - buf), NULL); + mandoc_msg(MANDOCERR_EOS, line, (int)(c - buf), NULL); } return 1; @@ -337,8 +319,7 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) if (sz == 2 || sz == 3) tok = roffhash_find(mdoc->mdocmac, buf + sv, sz); if (tok == TOKEN_NONE) { - mandoc_msg(MANDOCERR_MACRO, mdoc->parse, - ln, sv, buf + sv - 1); + mandoc_msg(MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1); return 1; } @@ -368,8 +349,7 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) */ if ('\0' == buf[offs] && ' ' == buf[offs - 1]) - mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, - ln, offs - 1, NULL); + mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); /* * If an initial macro or a list invocation, divert directly @@ -378,7 +358,7 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) n = mdoc->last; if (n == NULL || tok == MDOC_It || tok == MDOC_El) { - mdoc_macro(mdoc, tok, ln, sv, &offs, buf); + (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); return 1; } @@ -394,13 +374,13 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) (n->parent != NULL && n->parent->tok == MDOC_Bl && n->parent->norm->Bl.type == LIST_column)) { mdoc->flags |= MDOC_FREECOL; - mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf); + (*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, ln, sv, &sv, buf); return 1; } /* Normal processing of a macro. */ - mdoc_macro(mdoc, tok, ln, sv, &offs, buf); + (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); /* In quick mode (for mandocdb), abort after the NAME section. */ @@ -448,12 +428,3 @@ mdoc_isdelim(const char *p) return DELIM_NONE; } - -void -mdoc_validate(struct roff_man *mdoc) -{ - - mdoc->last = mdoc->first; - mdoc_node_validate(mdoc); - mdoc_state_reset(mdoc); -} diff --git a/usr/src/cmd/mandoc/mdoc.h b/usr/src/cmd/mandoc/mdoc.h index 1628e0c80c..2f32fa4962 100644 --- a/usr/src/cmd/mandoc/mdoc.h +++ b/usr/src/cmd/mandoc/mdoc.h @@ -1,4 +1,4 @@ -/* $Id: mdoc.h,v 1.145 2017/04/24 23:06:18 schwarze Exp $ */ +/* $Id: mdoc.h,v 1.146 2018/12/30 00:49:55 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -16,6 +16,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +struct roff_node; +struct roff_man; + enum mdocargt { MDOC_Split, /* -split */ MDOC_Nosplit, /* -nospli */ diff --git a/usr/src/cmd/mandoc/mdoc_argv.c b/usr/src/cmd/mandoc/mdoc_argv.c index db4c63f0d6..f4ce4a8730 100644 --- a/usr/src/cmd/mandoc/mdoc_argv.c +++ b/usr/src/cmd/mandoc/mdoc_argv.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_argv.c,v 1.115 2017/05/30 16:22:03 schwarze Exp $ */ +/* $Id: mdoc_argv.c,v 1.119 2018/12/21 17:15:19 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012, 2014-2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012, 2014-2018 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 @@ -144,7 +144,7 @@ static const enum mdocargt args_Bl[] = { MDOC_ARG_MAX }; -static const struct mdocarg __mdocargs[MDOC_MAX - MDOC_Dd] = { +static const struct mdocarg mdocargs[MDOC_MAX - MDOC_Dd] = { { ARGSFL_NONE, NULL }, /* Dd */ { ARGSFL_NONE, NULL }, /* Dt */ { ARGSFL_NONE, NULL }, /* Os */ @@ -266,7 +266,6 @@ static const struct mdocarg __mdocargs[MDOC_MAX - MDOC_Dd] = { { ARGSFL_NONE, NULL }, /* %U */ { ARGSFL_NONE, NULL }, /* Ta */ }; -static const struct mdocarg *const mdocargs = __mdocargs - MDOC_Dd; /* @@ -290,7 +289,7 @@ mdoc_argv(struct roff_man *mdoc, int line, enum roff_tok tok, /* Which flags does this macro support? */ assert(tok >= MDOC_Dd && tok < MDOC_MAX); - argtable = mdocargs[tok].argvs; + argtable = mdocargs[tok - MDOC_Dd].argvs; if (argtable == NULL) return; @@ -368,7 +367,7 @@ mdoc_argv(struct roff_man *mdoc, int line, enum roff_tok tok, /* Prepare for parsing the next flag. */ *pos = ipos; - argtable = mdocargs[tok].argvs; + argtable = mdocargs[tok - MDOC_Dd].argvs; } } @@ -417,12 +416,9 @@ mdoc_args(struct roff_man *mdoc, int line, int *pos, char *buf, enum roff_tok tok, char **v) { struct roff_node *n; - char *v_local; enum argsflag fl; - if (v == NULL) - v = &v_local; - fl = tok == TOKEN_NONE ? ARGSFL_NONE : mdocargs[tok].flags; + fl = tok == TOKEN_NONE ? ARGSFL_NONE : mdocargs[tok - MDOC_Dd].flags; /* * We know that we're in an `It', so it's reasonable to expect @@ -449,18 +445,20 @@ args(struct roff_man *mdoc, int line, int *pos, char *buf, enum argsflag fl, char **v) { char *p; + char *v_local; int pairs; if (buf[*pos] == '\0') { if (mdoc->flags & MDOC_PHRASELIT && ! (mdoc->flags & MDOC_PHRASE)) { - mandoc_msg(MANDOCERR_ARG_QUOTE, - mdoc->parse, line, *pos, NULL); + mandoc_msg(MANDOCERR_ARG_QUOTE, line, *pos, NULL); mdoc->flags &= ~MDOC_PHRASELIT; } return ARGS_EOLN; } + if (v == NULL) + v = &v_local; *v = buf + *pos; if (fl == ARGSFL_DELIM && args_checkpunct(buf, *pos)) @@ -506,7 +504,7 @@ args(struct roff_man *mdoc, int line, int *pos, p = strchr(*v, '\0'); if (p[-1] == ' ') mandoc_msg(MANDOCERR_SPACE_EOL, - mdoc->parse, line, *pos, NULL); + line, *pos, NULL); *pos += (int)(p - *v); } @@ -527,13 +525,12 @@ args(struct roff_man *mdoc, int line, int *pos, * Whitespace is NOT involved in literal termination. */ - if (mdoc->flags & MDOC_PHRASELIT || buf[*pos] == '\"') { - if ( ! (mdoc->flags & MDOC_PHRASELIT)) + if (mdoc->flags & MDOC_PHRASELIT || + (mdoc->flags & MDOC_PHRASE && buf[*pos] == '\"')) { + if ((mdoc->flags & MDOC_PHRASELIT) == 0) { *v = &buf[++(*pos)]; - - if (mdoc->flags & MDOC_PHRASE) mdoc->flags |= MDOC_PHRASELIT; - + } pairs = 0; for ( ; buf[*pos]; (*pos)++) { /* Move following text left after quoted quotes. */ @@ -554,7 +551,7 @@ args(struct roff_man *mdoc, int line, int *pos, if (buf[*pos] == '\0') { if ( ! (mdoc->flags & MDOC_PHRASE)) mandoc_msg(MANDOCERR_ARG_QUOTE, - mdoc->parse, line, *pos, NULL); + line, *pos, NULL); return ARGS_WORD; } @@ -568,14 +565,15 @@ args(struct roff_man *mdoc, int line, int *pos, (*pos)++; if ('\0' == buf[*pos]) - mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, - line, *pos, NULL); + mandoc_msg(MANDOCERR_SPACE_EOL, line, *pos, NULL); return ARGS_WORD; } p = &buf[*pos]; - *v = mandoc_getarg(mdoc->parse, &p, line, pos); + *v = roff_getarg(mdoc->roff, &p, line, pos); + if (v == &v_local) + free(*v); /* * After parsing the last word in this phrase, @@ -586,7 +584,7 @@ args(struct roff_man *mdoc, int line, int *pos, mdoc->flags &= ~MDOC_PHRASEQL; mdoc->flags |= MDOC_PHRASEQF; } - return ARGS_WORD; + return ARGS_ALLOC; } /* @@ -657,7 +655,9 @@ argv_multi(struct roff_man *mdoc, int line, v->value = mandoc_reallocarray(v->value, v->sz + MULTI_STEP, sizeof(char *)); - v->value[(int)v->sz] = mandoc_strdup(p); + if (ac != ARGS_ALLOC) + p = mandoc_strdup(p); + v->value[(int)v->sz] = p; } } @@ -672,7 +672,10 @@ argv_single(struct roff_man *mdoc, int line, if (ac == ARGS_EOLN) return; + if (ac != ARGS_ALLOC) + p = mandoc_strdup(p); + v->sz = 1; v->value = mandoc_malloc(sizeof(char *)); - v->value[0] = mandoc_strdup(p); + v->value[0] = p; } diff --git a/usr/src/cmd/mandoc/mdoc_html.c b/usr/src/cmd/mandoc/mdoc_html.c index f50de8a77a..87bf42a72b 100644 --- a/usr/src/cmd/mandoc/mdoc_html.c +++ b/usr/src/cmd/mandoc/mdoc_html.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_html.c,v 1.310 2018/07/27 17:49:31 schwarze Exp $ */ +/* $Id: mdoc_html.c,v 1.328 2019/03/01 10:57:18 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014-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 @@ -42,7 +42,7 @@ #define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b)) #endif -struct htmlmdoc { +struct mdoc_html_act { int (*pre)(MDOC_ARGS); void (*post)(MDOC_ARGS); }; @@ -62,6 +62,7 @@ static int mdoc_root_pre(const struct roff_meta *, static void mdoc__x_post(MDOC_ARGS); static int mdoc__x_pre(MDOC_ARGS); +static int mdoc_abort_pre(MDOC_ARGS); static int mdoc_ad_pre(MDOC_ARGS); static int mdoc_an_pre(MDOC_ARGS); static int mdoc_ap_pre(MDOC_ARGS); @@ -119,7 +120,7 @@ static int mdoc_vt_pre(MDOC_ARGS); static int mdoc_xr_pre(MDOC_ARGS); static int mdoc_xx_pre(MDOC_ARGS); -static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = { +static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { {NULL, NULL}, /* Dd */ {NULL, NULL}, /* Dt */ {NULL, NULL}, /* Os */ @@ -154,7 +155,7 @@ static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = { {mdoc_nd_pre, NULL}, /* Nd */ {mdoc_nm_pre, NULL}, /* Nm */ {mdoc_quote_pre, mdoc_quote_post}, /* Op */ - {mdoc_ft_pre, NULL}, /* Ot */ + {mdoc_abort_pre, NULL}, /* Ot */ {mdoc_pa_pre, NULL}, /* Pa */ {mdoc_ex_pre, NULL}, /* Rv */ {mdoc_st_pre, NULL}, /* St */ @@ -227,7 +228,7 @@ static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = { {mdoc_em_pre, NULL}, /* Fr */ {NULL, NULL}, /* Ud */ {mdoc_lb_pre, NULL}, /* Lb */ - {mdoc_pp_pre, NULL}, /* Lp */ + {mdoc_abort_pre, NULL}, /* Lp */ {mdoc_lk_pre, NULL}, /* Lk */ {mdoc_mt_pre, NULL}, /* Mt */ {mdoc_quote_pre, mdoc_quote_post}, /* Brq */ @@ -241,7 +242,6 @@ static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = { {mdoc__x_pre, mdoc__x_post}, /* %U */ {NULL, NULL}, /* Ta */ }; -static const struct htmlmdoc *const mdocs = __mdocs - MDOC_Dd; /* @@ -268,22 +268,21 @@ synopsis_pre(struct html *h, const struct roff_node *n) case MDOC_Fo: case MDOC_In: case MDOC_Vt: - print_paragraph(h); break; case MDOC_Ft: - if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { - print_paragraph(h); + if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) break; - } /* FALLTHROUGH */ default: print_otag(h, TAG_BR, ""); - break; + return; } + html_close_paragraph(h); + print_otag(h, TAG_P, "c", "Pp"); } void -html_mdoc(void *arg, const struct roff_man *mdoc) +html_mdoc(void *arg, const struct roff_meta *mdoc) { struct html *h; struct roff_node *n; @@ -295,19 +294,19 @@ html_mdoc(void *arg, const struct roff_man *mdoc) if ((h->oflags & HTML_FRAGMENT) == 0) { print_gen_decls(h); print_otag(h, TAG_HTML, ""); - if (n->type == ROFFT_COMMENT) + if (n != NULL && n->type == ROFFT_COMMENT) print_gen_comment(h, n); t = print_otag(h, TAG_HEAD, ""); - print_mdoc_head(&mdoc->meta, h); + print_mdoc_head(mdoc, h); print_tagq(h, t); print_otag(h, TAG_BODY, ""); } - mdoc_root_pre(&mdoc->meta, h); + mdoc_root_pre(mdoc, h); t = print_otag(h, TAG_DIV, "c", "manual-text"); - print_mdoc_nodelist(&mdoc->meta, n, h); + print_mdoc_nodelist(mdoc, n, h); print_tagq(h, t); - mdoc_root_post(&mdoc->meta, h); + mdoc_root_post(mdoc, h); print_tagq(h, NULL); } @@ -346,18 +345,21 @@ print_mdoc_nodelist(MDOC_ARGS) static void print_mdoc_node(MDOC_ARGS) { - int child; struct tag *t; + int child; if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) return; + html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi); + child = 1; - t = h->tag; n->flags &= ~NODE_ENDED; - switch (n->type) { case ROFFT_TEXT: + t = h->tag; + t->refcnt++; + /* No tables in this mode... */ assert(NULL == h->tblt); @@ -366,15 +368,18 @@ print_mdoc_node(MDOC_ARGS) * (i.e., within a <PRE>) don't print the newline. */ if (*n->string == ' ' && n->flags & NODE_LINE && - (h->flags & (HTML_LITERAL | HTML_NONEWLINE)) == 0) + (h->flags & HTML_NONEWLINE) == 0 && + (n->flags & NODE_NOFILL) == 0) print_otag(h, TAG_BR, ""); if (NODE_DELIMC & n->flags) h->flags |= HTML_NOSPACE; print_text(h, n->string); if (NODE_DELIMO & n->flags) h->flags |= HTML_NOSPACE; - return; + break; case ROFFT_EQN: + t = h->tag; + t->refcnt++; print_eqn(h, n->eqn); break; case ROFFT_TBL: @@ -391,20 +396,22 @@ print_mdoc_node(MDOC_ARGS) * the "meta" table state. This will be reopened on the * next table element. */ - if (h->tblt != NULL) { + if (h->tblt != NULL) print_tblclose(h); - t = h->tag; - } assert(h->tblt == NULL); + t = h->tag; + t->refcnt++; if (n->tok < ROFF_MAX) { roff_html_pre(h, n); - child = 0; - break; + t->refcnt--; + print_stagq(h, t); + return; } assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); - if (mdocs[n->tok].pre != NULL && + if (mdoc_html_acts[n->tok - MDOC_Dd].pre != NULL && (n->end == ENDBODY_NOT || n->child != NULL)) - child = (*mdocs[n->tok].pre)(meta, n, h); + child = (*mdoc_html_acts[n->tok - MDOC_Dd].pre)(meta, + n, h); break; } @@ -413,24 +420,31 @@ print_mdoc_node(MDOC_ARGS) h->flags |= HTML_PREKEEP; } - if (child && n->child) + if (child && n->child != NULL) print_mdoc_nodelist(meta, n->child, h); + t->refcnt--; print_stagq(h, t); switch (n->type) { + case ROFFT_TEXT: case ROFFT_EQN: break; default: - if (n->tok < ROFF_MAX || - mdocs[n->tok].post == NULL || + if (mdoc_html_acts[n->tok - MDOC_Dd].post == NULL || n->flags & NODE_ENDED) break; - (*mdocs[n->tok].post)(meta, n, h); + (*mdoc_html_acts[n->tok - MDOC_Dd].post)(meta, n, h); if (n->end != ENDBODY_NOT) n->body->flags |= NODE_ENDED; break; } + + if (n->flags & NODE_NOFILL && + (n->next == NULL || n->next->flags & NODE_LINE)) { + h->col++; + print_endline(h); + } } static void @@ -507,12 +521,65 @@ cond_id(const struct roff_node *n) static int mdoc_sh_pre(MDOC_ARGS) { - char *id; + struct roff_node *sn, *subn; + struct tag *t, *tsec, *tsub; + char *id; + int sc; switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); + if ((h->oflags & HTML_TOC) == 0 || + h->flags & HTML_TOCDONE || + n->sec <= SEC_SYNOPSIS) { + print_otag(h, TAG_SECTION, "c", "Sh"); + break; + } + h->flags |= HTML_TOCDONE; + sc = 0; + for (sn = n->next; sn != NULL; sn = sn->next) + if (sn->sec == SEC_CUSTOM) + if (++sc == 2) + break; + if (sc < 2) + break; + t = print_otag(h, TAG_H1, "c", "Sh"); + print_text(h, "TABLE OF CONTENTS"); + print_tagq(h, t); + t = print_otag(h, TAG_UL, "c", "Bl-compact"); + for (sn = n; sn != NULL; sn = sn->next) { + tsec = print_otag(h, TAG_LI, ""); + id = html_make_id(sn->head, 0); + tsub = print_otag(h, TAG_A, "hR", id); + free(id); + print_mdoc_nodelist(meta, sn->head->child, h); + print_tagq(h, tsub); + tsub = NULL; + for (subn = sn->body->child; subn != NULL; + subn = subn->next) { + if (subn->tok != MDOC_Ss) + continue; + id = html_make_id(subn->head, 0); + if (id == NULL) + continue; + if (tsub == NULL) + print_otag(h, TAG_UL, + "c", "Bl-compact"); + tsub = print_otag(h, TAG_LI, ""); + print_otag(h, TAG_A, "hR", id); + free(id); + print_mdoc_nodelist(meta, + subn->head->child, h); + print_tagq(h, tsub); + } + print_tagq(h, tsec); + } + print_tagq(h, t); + print_otag(h, TAG_SECTION, "c", "Sh"); + break; case ROFFT_HEAD: id = html_make_id(n, 1); - print_otag(h, TAG_H1, "cTi", "Sh", id); + print_otag(h, TAG_H1, "ci", "Sh", id); if (id != NULL) print_otag(h, TAG_A, "chR", "permalink", id); break; @@ -531,11 +598,21 @@ mdoc_ss_pre(MDOC_ARGS) { char *id; - if (n->type != ROFFT_HEAD) + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); + print_otag(h, TAG_SECTION, "c", "Ss"); return 1; + case ROFFT_HEAD: + break; + case ROFFT_BODY: + return 1; + default: + abort(); + } id = html_make_id(n, 1); - print_otag(h, TAG_H2, "cTi", "Ss", id); + print_otag(h, TAG_H2, "ci", "Ss", id); if (id != NULL) print_otag(h, TAG_A, "chR", "permalink", id); return 1; @@ -548,7 +625,7 @@ mdoc_fl_pre(MDOC_ARGS) if ((id = cond_id(n)) != NULL) print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "cTi", "Fl", id); + print_otag(h, TAG_CODE, "ci", "Fl", id); print_text(h, "\\-"); if (!(n->child == NULL && @@ -567,19 +644,27 @@ mdoc_cm_pre(MDOC_ARGS) if ((id = cond_id(n)) != NULL) print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "cTi", "Cm", id); + print_otag(h, TAG_CODE, "ci", "Cm", id); return 1; } static int mdoc_nd_pre(MDOC_ARGS) { - if (n->type != ROFFT_BODY) + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); return 1; - + case ROFFT_HEAD: + return 0; + case ROFFT_BODY: + break; + default: + abort(); + } print_text(h, "\\(em"); /* Cannot use TAG_SPAN because it may contain blocks. */ - print_otag(h, TAG_DIV, "cT", "Nd"); + print_otag(h, TAG_DIV, "c", "Nd"); return 1; } @@ -587,18 +672,21 @@ static int mdoc_nm_pre(MDOC_ARGS) { switch (n->type) { + case ROFFT_BLOCK: + break; case ROFFT_HEAD: print_otag(h, TAG_TD, ""); /* FALLTHROUGH */ case ROFFT_ELEM: - print_otag(h, TAG_CODE, "cT", "Nm"); + print_otag(h, TAG_CODE, "c", "Nm"); return 1; case ROFFT_BODY: print_otag(h, TAG_TD, ""); return 1; default: - break; + abort(); } + html_close_paragraph(h); synopsis_pre(h, n); print_otag(h, TAG_TABLE, "c", "Nm"); print_otag(h, TAG_TR, ""); @@ -611,12 +699,12 @@ mdoc_xr_pre(MDOC_ARGS) if (NULL == n->child) return 0; - if (h->base_man) - print_otag(h, TAG_A, "cThM", "Xr", + if (h->base_man1) + print_otag(h, TAG_A, "chM", "Xr", n->child->string, n->child->next == NULL ? NULL : n->child->next->string); else - print_otag(h, TAG_A, "cT", "Xr"); + print_otag(h, TAG_A, "c", "Xr"); n = n->child; print_text(h, n->string); @@ -645,7 +733,7 @@ mdoc_ns_pre(MDOC_ARGS) static int mdoc_ar_pre(MDOC_ARGS) { - print_otag(h, TAG_VAR, "cT", "Ar"); + print_otag(h, TAG_VAR, "c", "Ar"); return 1; } @@ -660,7 +748,6 @@ static int mdoc_it_pre(MDOC_ARGS) { const struct roff_node *bl; - struct tag *t; enum mdoc_list type; bl = n->parent; @@ -702,17 +789,6 @@ mdoc_it_pre(MDOC_ARGS) case LIST_tag: switch (n->type) { case ROFFT_HEAD: - if (h->style != NULL && !bl->norm->Bl.comp && - (n->parent->prev == NULL || - n->parent->prev->body == NULL || - n->parent->prev->body->child != NULL)) { - t = print_otag(h, TAG_DT, ""); - print_text(h, "\\ "); - print_tagq(h, t); - t = print_otag(h, TAG_DD, ""); - print_text(h, "\\ "); - print_tagq(h, t); - } print_otag(h, TAG_DT, ""); break; case ROFFT_BODY: @@ -746,17 +822,20 @@ mdoc_it_pre(MDOC_ARGS) static int mdoc_bl_pre(MDOC_ARGS) { - char cattr[28]; + char cattr[32]; struct mdoc_bl *bl; enum htmltag elemtype; switch (n->type) { - case ROFFT_BODY: - return 1; + case ROFFT_BLOCK: + html_close_paragraph(h); + break; case ROFFT_HEAD: return 0; + case ROFFT_BODY: + return 1; default: - break; + abort(); } bl = &n->norm->Bl; @@ -826,28 +905,34 @@ mdoc_ex_pre(MDOC_ARGS) static int mdoc_st_pre(MDOC_ARGS) { - print_otag(h, TAG_SPAN, "cT", "St"); + print_otag(h, TAG_SPAN, "c", "St"); return 1; } static int mdoc_em_pre(MDOC_ARGS) { - print_otag(h, TAG_I, "cT", "Em"); + print_otag(h, TAG_I, "c", "Em"); return 1; } static int mdoc_d1_pre(MDOC_ARGS) { - if (n->type != ROFFT_BLOCK) + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); + break; + case ROFFT_HEAD: + return 0; + case ROFFT_BODY: return 1; - + default: + abort(); + } print_otag(h, TAG_DIV, "c", "Bd Bd-indent"); - if (n->tok == MDOC_Dl) print_otag(h, TAG_CODE, "c", "Li"); - return 1; } @@ -857,7 +942,7 @@ mdoc_sx_pre(MDOC_ARGS) char *id; id = html_make_id(n, 0); - print_otag(h, TAG_A, "cThR", "Sx", id); + print_otag(h, TAG_A, "chR", "Sx", id); free(id); return 1; } @@ -865,86 +950,51 @@ mdoc_sx_pre(MDOC_ARGS) static int mdoc_bd_pre(MDOC_ARGS) { - int comp, sv; + char buf[16]; struct roff_node *nn; + int comp; - if (n->type == ROFFT_HEAD) - return 0; - - if (n->type == ROFFT_BLOCK) { - comp = n->norm->Bd.comp; - for (nn = n; nn && ! comp; nn = nn->parent) { - if (nn->type != ROFFT_BLOCK) - continue; - if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok) - comp = 1; - if (nn->prev) - break; - } - if ( ! comp) - print_paragraph(h); + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); return 1; + case ROFFT_HEAD: + return 0; + case ROFFT_BODY: + break; + default: + abort(); } - /* Handle the -offset argument. */ - - if (n->norm->Bd.offs == NULL || - ! strcmp(n->norm->Bd.offs, "left")) - print_otag(h, TAG_DIV, "c", "Bd"); - else - print_otag(h, TAG_DIV, "c", "Bd Bd-indent"); - - if (n->norm->Bd.type != DISP_unfilled && - n->norm->Bd.type != DISP_literal) - return 1; - - print_otag(h, TAG_PRE, "c", "Li"); + /* Handle preceding whitespace. */ - /* This can be recursive: save & set our literal state. */ - - sv = h->flags & HTML_LITERAL; - h->flags |= HTML_LITERAL; - - for (nn = n->child; nn; nn = nn->next) { - print_mdoc_node(meta, nn, h); - /* - * If the printed node flushes its own line, then we - * needn't do it here as well. This is hacky, but the - * notion of selective eoln whitespace is pretty dumb - * anyway, so don't sweat it. - */ - switch (nn->tok) { - case ROFF_br: - case ROFF_sp: - case MDOC_Sm: - case MDOC_Bl: - case MDOC_D1: - case MDOC_Dl: - case MDOC_Lp: - case MDOC_Pp: + comp = n->norm->Bd.comp; + for (nn = n; nn != NULL && comp == 0; nn = nn->parent) { + if (nn->type != ROFFT_BLOCK) continue; - default: + if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss) + comp = 1; + if (nn->prev != NULL) break; - } - if (h->flags & HTML_NONEWLINE || - (nn->next && ! (nn->next->flags & NODE_LINE))) - continue; - else if (nn->next) - print_text(h, "\n"); - - h->flags |= HTML_NOSPACE; } + (void)strlcpy(buf, "Bd", sizeof(buf)); + if (comp == 0) + (void)strlcat(buf, " Pp", sizeof(buf)); - if (0 == sv) - h->flags &= ~HTML_LITERAL; + /* Handle the -offset argument. */ - return 0; + if (n->norm->Bd.offs != NULL && + strcmp(n->norm->Bd.offs, "left") != 0) + (void)strlcat(buf, " Bd-indent", sizeof(buf)); + + print_otag(h, TAG_DIV, "c", buf); + return 1; } static int mdoc_pa_pre(MDOC_ARGS) { - print_otag(h, TAG_SPAN, "cT", "Pa"); + print_otag(h, TAG_SPAN, "c", "Pa"); return 1; } @@ -975,7 +1025,7 @@ mdoc_an_pre(MDOC_ARGS) if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT)) h->flags |= HTML_SPLIT; - print_otag(h, TAG_SPAN, "cT", "An"); + print_otag(h, TAG_SPAN, "c", "An"); return 1; } @@ -983,7 +1033,7 @@ static int mdoc_cd_pre(MDOC_ARGS) { synopsis_pre(h, n); - print_otag(h, TAG_CODE, "cT", "Cd"); + print_otag(h, TAG_CODE, "c", "Cd"); return 1; } @@ -994,7 +1044,7 @@ mdoc_dv_pre(MDOC_ARGS) if ((id = cond_id(n)) != NULL) print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "cTi", "Dv", id); + print_otag(h, TAG_CODE, "ci", "Dv", id); return 1; } @@ -1005,7 +1055,7 @@ mdoc_ev_pre(MDOC_ARGS) if ((id = cond_id(n)) != NULL) print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "cTi", "Ev", id); + print_otag(h, TAG_CODE, "ci", "Ev", id); return 1; } @@ -1022,7 +1072,7 @@ mdoc_er_pre(MDOC_ARGS) if (id != NULL) print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "cTi", "Er", id); + print_otag(h, TAG_CODE, "ci", "Er", id); return 1; } @@ -1033,12 +1083,12 @@ mdoc_fa_pre(MDOC_ARGS) struct tag *t; if (n->parent->tok != MDOC_Fo) { - print_otag(h, TAG_VAR, "cT", "Fa"); + print_otag(h, TAG_VAR, "c", "Fa"); return 1; } for (nn = n->child; nn; nn = nn->next) { - t = print_otag(h, TAG_VAR, "cT", "Fa"); + t = print_otag(h, TAG_VAR, "c", "Fa"); print_text(h, nn->string); print_tagq(h, t); if (nn->next) { @@ -1069,11 +1119,11 @@ mdoc_fd_pre(MDOC_ARGS) assert(n->type == ROFFT_TEXT); if (strcmp(n->string, "#include")) { - print_otag(h, TAG_CODE, "cT", "Fd"); + print_otag(h, TAG_CODE, "c", "Fd"); return 1; } - print_otag(h, TAG_CODE, "cT", "In"); + print_otag(h, TAG_CODE, "c", "In"); print_text(h, n->string); if (NULL != (n = n->next)) { @@ -1087,10 +1137,10 @@ mdoc_fd_pre(MDOC_ARGS) cp = strchr(buf, '\0') - 1; if (cp >= buf && (*cp == '>' || *cp == '"')) *cp = '\0'; - t = print_otag(h, TAG_A, "cThI", "In", buf); + t = print_otag(h, TAG_A, "chI", "In", buf); free(buf); } else - t = print_otag(h, TAG_A, "cT", "In"); + t = print_otag(h, TAG_A, "c", "In"); print_text(h, n->string); print_tagq(h, t); @@ -1117,7 +1167,7 @@ mdoc_vt_pre(MDOC_ARGS) } else if (n->type == ROFFT_HEAD) return 0; - print_otag(h, TAG_VAR, "cT", "Vt"); + print_otag(h, TAG_VAR, "c", "Vt"); return 1; } @@ -1125,7 +1175,7 @@ static int mdoc_ft_pre(MDOC_ARGS) { synopsis_pre(h, n); - print_otag(h, TAG_VAR, "cT", "Ft"); + print_otag(h, TAG_VAR, "c", "Ft"); return 1; } @@ -1146,7 +1196,7 @@ mdoc_fn_pre(MDOC_ARGS) ep = strchr(sp, ' '); if (NULL != ep) { - t = print_otag(h, TAG_VAR, "cT", "Ft"); + t = print_otag(h, TAG_VAR, "c", "Ft"); while (ep) { sz = MIN((int)(ep - sp), BUFSIZ - 1); @@ -1159,7 +1209,7 @@ mdoc_fn_pre(MDOC_ARGS) print_tagq(h, t); } - t = print_otag(h, TAG_CODE, "cT", "Fn"); + t = print_otag(h, TAG_CODE, "c", "Fn"); if (sp) print_text(h, sp); @@ -1172,10 +1222,10 @@ mdoc_fn_pre(MDOC_ARGS) for (n = n->child->next; n; n = n->next) { if (NODE_SYNPRETTY & n->flags) - t = print_otag(h, TAG_VAR, "cTs", "Fa", + t = print_otag(h, TAG_VAR, "cs", "Fa", "white-space", "nowrap"); else - t = print_otag(h, TAG_VAR, "cT", "Fa"); + t = print_otag(h, TAG_VAR, "c", "Fa"); print_text(h, n->string); print_tagq(h, t); if (n->next) { @@ -1222,8 +1272,10 @@ mdoc_skip_pre(MDOC_ARGS) static int mdoc_pp_pre(MDOC_ARGS) { - - print_paragraph(h); + if ((n->flags & NODE_NOFILL) == 0) { + html_close_paragraph(h); + print_otag(h, TAG_P, "c", "Pp"); + } return 0; } @@ -1246,7 +1298,7 @@ mdoc_lk_pre(MDOC_ARGS) descr = link->next; if (descr == punct) descr = link; /* no text */ - t = print_otag(h, TAG_A, "cTh", "Lk", link->string); + t = print_otag(h, TAG_A, "ch", "Lk", link->string); do { if (descr->flags & (NODE_DELIMC | NODE_DELIMO)) h->flags |= HTML_NOSPACE; @@ -1274,7 +1326,7 @@ mdoc_mt_pre(MDOC_ARGS) assert(n->type == ROFFT_TEXT); mandoc_asprintf(&cp, "mailto:%s", n->string); - t = print_otag(h, TAG_A, "cTh", "Mt", cp); + t = print_otag(h, TAG_A, "ch", "Mt", cp); print_text(h, n->string); print_tagq(h, t); free(cp); @@ -1302,7 +1354,7 @@ mdoc_fo_pre(MDOC_ARGS) return 0; assert(n->child->string); - t = print_otag(h, TAG_CODE, "cT", "Fn"); + t = print_otag(h, TAG_CODE, "c", "Fn"); print_text(h, n->child->string); print_tagq(h, t); return 0; @@ -1326,7 +1378,7 @@ mdoc_in_pre(MDOC_ARGS) struct tag *t; synopsis_pre(h, n); - print_otag(h, TAG_CODE, "cT", "In"); + print_otag(h, TAG_CODE, "c", "In"); /* * The first argument of the `In' gets special treatment as @@ -1345,9 +1397,9 @@ mdoc_in_pre(MDOC_ARGS) assert(n->type == ROFFT_TEXT); if (h->base_includes) - t = print_otag(h, TAG_A, "cThI", "In", n->string); + t = print_otag(h, TAG_A, "chI", "In", n->string); else - t = print_otag(h, TAG_A, "cT", "In"); + t = print_otag(h, TAG_A, "c", "In"); print_text(h, n->string); print_tagq(h, t); @@ -1372,14 +1424,14 @@ mdoc_ic_pre(MDOC_ARGS) if ((id = cond_id(n)) != NULL) print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "cTi", "Ic", id); + print_otag(h, TAG_CODE, "ci", "Ic", id); return 1; } static int mdoc_va_pre(MDOC_ARGS) { - print_otag(h, TAG_VAR, "cT", "Va"); + print_otag(h, TAG_VAR, "c", "Va"); return 1; } @@ -1398,10 +1450,17 @@ mdoc_bf_pre(MDOC_ARGS) { const char *cattr; - if (n->type == ROFFT_HEAD) - return 0; - else if (n->type != ROFFT_BODY) + switch (n->type) { + case ROFFT_BLOCK: + html_close_paragraph(h); return 1; + case ROFFT_HEAD: + return 0; + case ROFFT_BODY: + break; + default: + abort(); + } if (FONT_Em == n->norm->Bf.font) cattr = "Bf Em"; @@ -1424,7 +1483,7 @@ mdoc_ms_pre(MDOC_ARGS) if ((id = cond_id(n)) != NULL) print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_SPAN, "cTi", "Ms", id); + print_otag(h, TAG_SPAN, "ci", "Ms", id); return 1; } @@ -1447,13 +1506,21 @@ mdoc_pf_post(MDOC_ARGS) static int mdoc_rs_pre(MDOC_ARGS) { - if (n->type != ROFFT_BLOCK) - return 1; - - if (n->prev && SEC_SEE_ALSO == n->sec) - print_paragraph(h); - - print_otag(h, TAG_CITE, "cT", "Rs"); + switch (n->type) { + case ROFFT_BLOCK: + if (n->sec == SEC_SEE_ALSO) + html_close_paragraph(h); + break; + case ROFFT_HEAD: + return 0; + case ROFFT_BODY: + if (n->sec == SEC_SEE_ALSO) + print_otag(h, TAG_P, "c", "Pp"); + print_otag(h, TAG_CITE, "c", "Rs"); + break; + default: + abort(); + } return 1; } @@ -1482,7 +1549,7 @@ mdoc_li_pre(MDOC_ARGS) static int mdoc_sy_pre(MDOC_ARGS) { - print_otag(h, TAG_B, "cT", "Sy"); + print_otag(h, TAG_B, "c", "Sy"); return 1; } @@ -1492,7 +1559,7 @@ mdoc_lb_pre(MDOC_ARGS) if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev) print_otag(h, TAG_BR, ""); - print_otag(h, TAG_SPAN, "cT", "Lb"); + print_otag(h, TAG_SPAN, "c", "Lb"); return 1; } @@ -1630,9 +1697,15 @@ mdoc_quote_pre(MDOC_ARGS) case MDOC_Oo: case MDOC_Op: print_text(h, "\\(lB"); - h->flags |= HTML_NOSPACE; - /* Cannot use TAG_SPAN because it may contain blocks. */ - print_otag(h, TAG_IDIV, "c", "Op"); + /* + * Give up on semantic markup for now. + * We cannot use TAG_SPAN because .Oo may contain blocks. + * We cannot use TAG_IDIV because we might be in a + * phrasing context (like .Dl or .Pp); we cannot + * close out a .Pp at this point either because + * that would break the line. + */ + /* XXX print_otag(h, TAG_???, "c", "Op"); */ break; case MDOC_En: if (NULL == n->norm->Es || @@ -1760,3 +1833,9 @@ mdoc_eo_post(MDOC_ARGS) else if ( ! tail) h->flags &= ~HTML_NOSPACE; } + +static int +mdoc_abort_pre(MDOC_ARGS) +{ + abort(); +} diff --git a/usr/src/cmd/mandoc/mdoc_macro.c b/usr/src/cmd/mandoc/mdoc_macro.c index b463d03e22..3422945814 100644 --- a/usr/src/cmd/mandoc/mdoc_macro.c +++ b/usr/src/cmd/mandoc/mdoc_macro.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_macro.c,v 1.224 2017/05/30 16:22:03 schwarze Exp $ */ +/* $Id: mdoc_macro.c,v 1.232 2019/01/07 07:26:29 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2012-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 @@ -49,7 +49,7 @@ static void dword(struct roff_man *, int, int, const char *, static int find_pending(struct roff_man *, enum roff_tok, int, int, struct roff_node *); static int lookup(struct roff_man *, int, int, int, const char *); -static int macro_or_word(MACRO_PROT_ARGS, int); +static int macro_or_word(MACRO_PROT_ARGS, char *, int); static void break_intermediate(struct roff_node *, struct roff_node *); static int parse_rest(struct roff_man *, enum roff_tok, @@ -60,7 +60,7 @@ static void rew_last(struct roff_man *, const struct roff_node *); static void rew_pending(struct roff_man *, const struct roff_node *); -const struct mdoc_macro __mdoc_macros[MDOC_MAX - MDOC_Dd] = { +static const struct mdoc_macro mdoc_macros[MDOC_MAX - MDOC_Dd] = { { in_line_eoln, MDOC_PROLOGUE }, /* Dd */ { in_line_eoln, MDOC_PROLOGUE }, /* Dt */ { in_line_eoln, MDOC_PROLOGUE }, /* Os */ @@ -201,9 +201,15 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX - MDOC_Dd] = { { in_line_eoln, 0 }, /* %U */ { phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */ }; -const struct mdoc_macro *const mdoc_macros = __mdoc_macros - MDOC_Dd; +const struct mdoc_macro * +mdoc_macro(enum roff_tok tok) +{ + assert(tok >= MDOC_Dd && tok < MDOC_MAX); + return mdoc_macros + (tok - MDOC_Dd); +} + /* * This is called at the end of parsing. It must traverse up the tree, * closing out open [implicit] scopes. Obviously, open explicit scopes @@ -221,14 +227,13 @@ mdoc_endparse(struct roff_man *mdoc) for ( ; n; n = n->parent) if (n->type == ROFFT_BLOCK && - mdoc_macros[n->tok].flags & MDOC_EXPLICIT) - mandoc_msg(MANDOCERR_BLK_NOEND, mdoc->parse, - n->line, n->pos, roff_name[n->tok]); + mdoc_macro(n->tok)->flags & MDOC_EXPLICIT) + mandoc_msg(MANDOCERR_BLK_NOEND, + n->line, n->pos, "%s", roff_name[n->tok]); /* Rewind to the first. */ - rew_last(mdoc, mdoc->first); - mdoc_state_reset(mdoc); + rew_last(mdoc, mdoc->meta.first); } /* @@ -244,13 +249,12 @@ lookup(struct roff_man *mdoc, int from, int line, int ppos, const char *p) mdoc->flags &= ~MDOC_PHRASEQF; return TOKEN_NONE; } - if (from == TOKEN_NONE || mdoc_macros[from].flags & MDOC_PARSED) { + if (from == TOKEN_NONE || mdoc_macro(from)->flags & MDOC_PARSED) { res = roffhash_find(mdoc->mdocmac, p, 0); if (res != TOKEN_NONE) { - if (mdoc_macros[res].flags & MDOC_CALLABLE) + if (mdoc_macro(res)->flags & MDOC_CALLABLE) return res; - mandoc_msg(MANDOCERR_MACRO_CALL, - mdoc->parse, line, ppos, p); + mandoc_msg(MANDOCERR_MACRO_CALL, line, ppos, "%s", p); } } return TOKEN_NONE; @@ -291,6 +295,8 @@ rew_pending(struct roff_man *mdoc, const struct roff_node *n) case ROFFT_HEAD: roff_body_alloc(mdoc, n->line, n->pos, n->tok); + if (n->tok == MDOC_Ss) + mdoc->flags &= ~ROFF_NONOFILL; break; case ROFFT_BLOCK: break; @@ -409,16 +415,15 @@ find_pending(struct roff_man *mdoc, enum roff_tok tok, int line, int ppos, if (n->flags & NODE_ENDED) continue; if (n->type == ROFFT_BLOCK && - mdoc_macros[n->tok].flags & MDOC_EXPLICIT) { + mdoc_macro(n->tok)->flags & MDOC_EXPLICIT) { irc = 1; break_intermediate(mdoc->last, target); if (target->type == ROFFT_HEAD) target->flags |= NODE_ENDED; else if ( ! (target->flags & NODE_ENDED)) { - mandoc_vmsg(MANDOCERR_BLK_NEST, - mdoc->parse, line, ppos, - "%s breaks %s", roff_name[tok], - roff_name[n->tok]); + mandoc_msg(MANDOCERR_BLK_NEST, + line, ppos, "%s breaks %s", + roff_name[tok], roff_name[n->tok]); mdoc_endbody_alloc(mdoc, line, ppos, tok, target); } @@ -470,14 +475,15 @@ append_delims(struct roff_man *mdoc, int line, int *pos, char *buf) { char *p; int la; + enum margserr ac; if (buf[*pos] == '\0') return; for (;;) { la = *pos; - if (mdoc_args(mdoc, line, pos, buf, TOKEN_NONE, &p) == - ARGS_EOLN) + ac = mdoc_args(mdoc, line, pos, buf, TOKEN_NONE, &p); + if (ac == ARGS_EOLN) break; dword(mdoc, line, la, p, DELIM_MAX, 1); @@ -495,6 +501,8 @@ append_delims(struct roff_man *mdoc, int line, int *pos, char *buf) if (mandoc_eos(p, strlen(p))) mdoc->last->flags |= NODE_EOS; + if (ac == ARGS_ALLOC) + free(p); } } @@ -504,27 +512,23 @@ append_delims(struct roff_man *mdoc, int line, int *pos, char *buf) * Otherwise, allocate it and return 0. */ static int -macro_or_word(MACRO_PROT_ARGS, int parsed) +macro_or_word(MACRO_PROT_ARGS, char *p, int parsed) { - char *p; int ntok; - p = buf + ppos; - ntok = TOKEN_NONE; - if (*p == '"') - p++; - else if (parsed && ! (mdoc->flags & MDOC_PHRASELIT)) - ntok = lookup(mdoc, tok, line, ppos, p); + ntok = buf[ppos] == '"' || parsed == 0 || + mdoc->flags & MDOC_PHRASELIT ? TOKEN_NONE : + lookup(mdoc, tok, line, ppos, p); if (ntok == TOKEN_NONE) { dword(mdoc, line, ppos, p, DELIM_MAX, tok == TOKEN_NONE || - mdoc_macros[tok].flags & MDOC_JOIN); + mdoc_macro(tok)->flags & MDOC_JOIN); return 0; } else { if (tok != TOKEN_NONE && - mdoc_macros[tok].fp == in_line_eoln) + mdoc_macro(tok)->fp == in_line_eoln) rew_elem(mdoc, tok); - mdoc_macro(mdoc, ntok, line, ppos, pos, buf); + (*mdoc_macro(ntok)->fp)(mdoc, ntok, line, ppos, pos, buf); if (tok == TOKEN_NONE) append_delims(mdoc, line, pos, buf); return 1; @@ -629,7 +633,7 @@ blk_exp_close(MACRO_PROT_ARGS) * the scope - of the current block ends. */ - mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, + mandoc_msg(MANDOCERR_BLK_NEST, line, ppos, "%s breaks %s", roff_name[atok], roff_name[later->tok]); @@ -672,8 +676,8 @@ blk_exp_close(MACRO_PROT_ARGS) } if (body == NULL) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, mdoc->parse, - line, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, line, ppos, + "%s", roff_name[tok]); if (maxargs && endbody == NULL) { /* * Stray .Ec without previous .Eo: @@ -688,14 +692,25 @@ blk_exp_close(MACRO_PROT_ARGS) mdoc_tail_alloc(mdoc, line, ppos, atok); } - if ( ! (mdoc_macros[tok].flags & MDOC_PARSED)) { + if ((mdoc_macro(tok)->flags & MDOC_PARSED) == 0) { if (buf[*pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, - mdoc->parse, line, ppos, - "%s %s", roff_name[tok], - buf + *pos); + mandoc_msg(MANDOCERR_ARG_SKIP, line, ppos, + "%s %s", roff_name[tok], buf + *pos); if (endbody == NULL && n != NULL) rew_pending(mdoc, n); + + /* + * Restore the fill mode that was set before the display. + * This needs to be done here rather than during validation + * such that subsequent nodes get the right flags. + */ + + if (tok == MDOC_Ed && body != NULL) { + if (body->flags & NODE_NOFILL) + mdoc->flags |= ROFF_NOFILL; + else + mdoc->flags &= ~ROFF_NOFILL; + } return; } @@ -717,14 +732,18 @@ blk_exp_close(MACRO_PROT_ARGS) if (ntok == TOKEN_NONE) { dword(mdoc, line, lastarg, p, DELIM_MAX, - MDOC_JOIN & mdoc_macros[tok].flags); + mdoc_macro(tok)->flags & MDOC_JOIN); + if (ac == ARGS_ALLOC) + free(p); continue; } + if (ac == ARGS_ALLOC) + free(p); if (n != NULL) rew_last(mdoc, n); mdoc->flags &= ~MDOC_NEWLINE; - mdoc_macro(mdoc, ntok, line, lastarg, pos, buf); + (*mdoc_macro(ntok)->fp)(mdoc, ntok, line, lastarg, pos, buf); break; } @@ -828,12 +847,14 @@ in_line(MACRO_PROT_ARGS) } else if ( ! nc && ! cnt) { mdoc_argv_free(arg); mandoc_msg(MANDOCERR_MACRO_EMPTY, - mdoc->parse, line, ppos, - roff_name[tok]); + line, ppos, "%s", roff_name[tok]); } - mdoc_macro(mdoc, ntok, line, la, pos, buf); + (*mdoc_macro(ntok)->fp)(mdoc, ntok, + line, la, pos, buf); if (nl) append_delims(mdoc, line, pos, buf); + if (ac == ARGS_ALLOC) + free(p); return; } @@ -875,7 +896,10 @@ in_line(MACRO_PROT_ARGS) } dword(mdoc, line, la, p, d, - mdoc_macros[tok].flags & MDOC_JOIN); + mdoc_macro(tok)->flags & MDOC_JOIN); + + if (ac == ARGS_ALLOC) + free(p); /* * If the first argument is a closing delimiter, @@ -914,8 +938,8 @@ in_line(MACRO_PROT_ARGS) rew_last(mdoc, mdoc->last); } else { mdoc_argv_free(arg); - mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, - line, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_MACRO_EMPTY, + line, ppos, "%s", roff_name[tok]); } } if (nl) @@ -927,24 +951,25 @@ in_line(MACRO_PROT_ARGS) static void blk_full(MACRO_PROT_ARGS) { - int la, nl, parsed; struct mdoc_arg *arg; struct roff_node *blk; /* Our own or a broken block. */ struct roff_node *head; /* Our own head. */ struct roff_node *body; /* Our own body. */ struct roff_node *n; - enum margserr ac, lac; char *p; + size_t iarg; + int done, la, nl, parsed; + enum margserr ac, lac; nl = MDOC_NEWLINE & mdoc->flags; if (buf[*pos] == '\0' && (tok == MDOC_Sh || tok == MDOC_Ss)) { - mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, - line, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_MACRO_EMPTY, + line, ppos, "%s", roff_name[tok]); return; } - if ( ! (mdoc_macros[tok].flags & MDOC_EXPLICIT)) { + if ((mdoc_macro(tok)->flags & MDOC_EXPLICIT) == 0) { /* Here, tok is one of Sh Ss Nm Nd It. */ @@ -960,21 +985,20 @@ blk_full(MACRO_PROT_ARGS) if (tok == MDOC_It && n->tok == MDOC_Bl) { if (blk != NULL) { - mandoc_vmsg(MANDOCERR_BLK_BROKEN, - mdoc->parse, line, ppos, - "It breaks %s", + mandoc_msg(MANDOCERR_BLK_BROKEN, + line, ppos, "It breaks %s", roff_name[blk->tok]); rew_pending(mdoc, blk); } break; } - if (mdoc_macros[n->tok].flags & MDOC_EXPLICIT) { + if (mdoc_macro(n->tok)->flags & MDOC_EXPLICIT) { switch (tok) { case MDOC_Sh: case MDOC_Ss: - mandoc_vmsg(MANDOCERR_BLK_BROKEN, - mdoc->parse, line, ppos, + mandoc_msg(MANDOCERR_BLK_BROKEN, + line, ppos, "%s breaks %s", roff_name[tok], roff_name[n->tok]); rew_pending(mdoc, n); @@ -1000,8 +1024,7 @@ blk_full(MACRO_PROT_ARGS) /* Item breaking an explicit block. */ if (blk != NULL) { - mandoc_vmsg(MANDOCERR_BLK_BROKEN, - mdoc->parse, line, ppos, + mandoc_msg(MANDOCERR_BLK_BROKEN, line, ppos, "It breaks %s", roff_name[blk->tok]); rew_pending(mdoc, blk); blk = NULL; @@ -1015,7 +1038,7 @@ blk_full(MACRO_PROT_ARGS) /* Skip items outside lists. */ if (tok == MDOC_It && (n == NULL || n->tok != MDOC_Bl)) { - mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse, + mandoc_msg(MANDOCERR_IT_STRAY, line, ppos, "It %s", buf + *pos); roff_elem_alloc(mdoc, line, ppos, ROFF_br); rew_elem(mdoc, ROFF_br); @@ -1032,6 +1055,16 @@ blk_full(MACRO_PROT_ARGS) * regular child nodes. */ + switch (tok) { + case MDOC_Sh: + mdoc->flags &= ~ROFF_NOFILL; + break; + case MDOC_Ss: + mdoc->flags |= ROFF_NONOFILL; + break; + default: + break; + } mdoc_argv(mdoc, line, tok, &arg, pos, buf); blk = mdoc_block_alloc(mdoc, line, ppos, tok, arg); head = body = NULL; @@ -1093,14 +1126,17 @@ blk_full(MACRO_PROT_ARGS) } if (tok == MDOC_Bd || tok == MDOC_Bk) { - mandoc_vmsg(MANDOCERR_ARG_EXCESS, - mdoc->parse, line, la, "%s ... %s", - roff_name[tok], buf + la); + mandoc_msg(MANDOCERR_ARG_EXCESS, line, la, + "%s ... %s", roff_name[tok], buf + la); + if (ac == ARGS_ALLOC) + free(p); break; } if (tok == MDOC_Rs) { - mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, + mandoc_msg(MANDOCERR_ARG_SKIP, line, la, "Rs %s", buf + la); + if (ac == ARGS_ALLOC) + free(p); break; } if (ac == ARGS_PUNCT) @@ -1115,6 +1151,8 @@ blk_full(MACRO_PROT_ARGS) ac != ARGS_PHRASE && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); + if (ac == ARGS_ALLOC) + free(p); continue; } @@ -1146,7 +1184,10 @@ blk_full(MACRO_PROT_ARGS) continue; } - if (macro_or_word(mdoc, tok, line, la, pos, buf, parsed)) + done = macro_or_word(mdoc, tok, line, la, pos, buf, p, parsed); + if (ac == ARGS_ALLOC) + free(p); + if (done) break; } @@ -1165,6 +1206,33 @@ blk_full(MACRO_PROT_ARGS) rew_last(mdoc, head); body = roff_body_alloc(mdoc, line, ppos, tok); + if (tok == MDOC_Ss) + mdoc->flags &= ~ROFF_NONOFILL; + + /* + * Set up fill mode for display blocks. + * This needs to be done here up front rather than during + * validation such that child nodes get the right flags. + */ + + if (tok == MDOC_Bd && arg != NULL) { + for (iarg = 0; iarg < arg->argc; iarg++) { + switch (arg->argv[iarg].arg) { + case MDOC_Unfilled: + case MDOC_Literal: + mdoc->flags |= ROFF_NOFILL; + break; + case MDOC_Filled: + case MDOC_Ragged: + case MDOC_Centred: + mdoc->flags &= ~ROFF_NOFILL; + break; + default: + continue; + } + break; + } + } out: if (mdoc->flags & MDOC_FREECOL) { rew_last(mdoc, body); @@ -1176,7 +1244,7 @@ out: static void blk_part_imp(MACRO_PROT_ARGS) { - int la, nl; + int done, la, nl; enum margserr ac; char *p; struct roff_node *blk; /* saved block context */ @@ -1211,13 +1279,18 @@ blk_part_imp(MACRO_PROT_ARGS) if (body == NULL && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); + if (ac == ARGS_ALLOC) + free(p); continue; } if (body == NULL) body = roff_body_alloc(mdoc, line, ppos, tok); - if (macro_or_word(mdoc, tok, line, la, pos, buf, 1)) + done = macro_or_word(mdoc, tok, line, la, pos, buf, p, 1); + if (ac == ARGS_ALLOC) + free(p); + if (done) break; } if (body == NULL) @@ -1236,13 +1309,13 @@ blk_part_imp(MACRO_PROT_ARGS) for (n = body->child; n && n->next; n = n->next) /* Do nothing. */ ; if (n && n->tok == MDOC_Ns) - mdoc_node_relink(mdoc, n); + roff_node_relink(mdoc, n); } static void blk_part_exp(MACRO_PROT_ARGS) { - int la, nl; + int done, la, nl; enum margserr ac; struct roff_node *head; /* keep track of head */ char *p; @@ -1267,6 +1340,8 @@ blk_part_exp(MACRO_PROT_ARGS) if (head == NULL && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); + if (ac == ARGS_ALLOC) + free(p); continue; } @@ -1276,11 +1351,17 @@ blk_part_exp(MACRO_PROT_ARGS) dword(mdoc, line, la, p, DELIM_MAX, 0); rew_last(mdoc, head); roff_body_alloc(mdoc, line, ppos, tok); - if (tok == MDOC_Eo) + if (tok == MDOC_Eo) { + if (ac == ARGS_ALLOC) + free(p); continue; + } } - if (macro_or_word(mdoc, tok, line, la, pos, buf, 1)) + done = macro_or_word(mdoc, tok, line, la, pos, buf, p, 1); + if (ac == ARGS_ALLOC) + free(p); + if (done) break; } @@ -1338,10 +1419,12 @@ in_line_argn(MACRO_PROT_ARGS) la = *pos; ac = mdoc_args(mdoc, line, pos, buf, tok, &p); - if (ac == ARGS_WORD && state == -1 && - ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) && + if ((ac == ARGS_WORD || ac == ARGS_ALLOC) && state == -1 && + (mdoc_macro(tok)->flags & MDOC_IGNDELIM) == 0 && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); + if (ac == ARGS_ALLOC) + free(p); continue; } @@ -1353,8 +1436,8 @@ in_line_argn(MACRO_PROT_ARGS) if (ac == ARGS_PUNCT || ac == ARGS_EOLN) { if (abs(state) < 2 && tok == MDOC_Pf) - mandoc_vmsg(MANDOCERR_PF_SKIP, - mdoc->parse, line, ppos, "Pf %s", + mandoc_msg(MANDOCERR_PF_SKIP, + line, ppos, "Pf %s", p == NULL ? "at eol" : p); break; } @@ -1372,11 +1455,14 @@ in_line_argn(MACRO_PROT_ARGS) rew_elem(mdoc, tok); state = -2; } - mdoc_macro(mdoc, ntok, line, la, pos, buf); + (*mdoc_macro(ntok)->fp)(mdoc, ntok, + line, la, pos, buf); + if (ac == ARGS_ALLOC) + free(p); break; } - if (mdoc_macros[tok].flags & MDOC_IGNDELIM || + if (mdoc_macro(tok)->flags & MDOC_IGNDELIM || mdoc_isdelim(p) == DELIM_NONE) { if (state == -1) { mdoc_elem_alloc(mdoc, line, ppos, tok, arg); @@ -1389,12 +1475,15 @@ in_line_argn(MACRO_PROT_ARGS) } dword(mdoc, line, la, p, DELIM_MAX, - mdoc_macros[tok].flags & MDOC_JOIN); + mdoc_macro(tok)->flags & MDOC_JOIN); + if (ac == ARGS_ALLOC) + free(p); + p = mdoc->last->string; } if (state == -1) { - mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, - line, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_MACRO_EMPTY, + line, ppos, "%s", roff_name[tok]); return; } @@ -1423,8 +1512,8 @@ in_line_eoln(MACRO_PROT_ARGS) if (buf[*pos] == '\0' && (tok == MDOC_Fd || *roff_name[tok] == '%')) { - mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, - line, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_MACRO_EMPTY, + line, ppos, "%s", roff_name[tok]); return; } @@ -1444,13 +1533,19 @@ static int parse_rest(struct roff_man *mdoc, enum roff_tok tok, int line, int *pos, char *buf) { - int la; + char *p; + int done, la; + enum margserr ac; for (;;) { la = *pos; - if (mdoc_args(mdoc, line, pos, buf, tok, NULL) == ARGS_EOLN) + ac = mdoc_args(mdoc, line, pos, buf, tok, &p); + if (ac == ARGS_EOLN) return 0; - if (macro_or_word(mdoc, tok, line, la, pos, buf, 1)) + done = macro_or_word(mdoc, tok, line, la, pos, buf, p, 1); + if (ac == ARGS_ALLOC) + free(p); + if (done) return 1; } } @@ -1492,8 +1587,7 @@ phrase_ta(MACRO_PROT_ARGS) } if (n == NULL || n->norm->Bl.type != LIST_column) { - mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse, - line, ppos, "Ta"); + mandoc_msg(MANDOCERR_TA_STRAY, line, ppos, "Ta"); return; } diff --git a/usr/src/cmd/mandoc/mdoc_man.c b/usr/src/cmd/mandoc/mdoc_man.c index bcf9207f79..2e8f02ae56 100644 --- a/usr/src/cmd/mandoc/mdoc_man.c +++ b/usr/src/cmd/mandoc/mdoc_man.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_man.c,v 1.126 2018/04/11 17:11:13 schwarze Exp $ */ +/* $Id: mdoc_man.c,v 1.132 2019/01/04 03:17:36 schwarze Exp $ */ /* - * Copyright (c) 2011-2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-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 @@ -36,7 +36,7 @@ typedef int (*int_fp)(DECL_ARGS); typedef void (*void_fp)(DECL_ARGS); -struct manact { +struct mdoc_man_act { int_fp cond; /* DON'T run actions */ int_fp pre; /* pre-node action */ void_fp post; /* post-node action */ @@ -75,6 +75,7 @@ static void post_pf(DECL_ARGS); static void post_sect(DECL_ARGS); static void post_vt(DECL_ARGS); static int pre__t(DECL_ARGS); +static int pre_abort(DECL_ARGS); static int pre_an(DECL_ARGS); static int pre_ap(DECL_ARGS); static int pre_aq(DECL_ARGS); @@ -103,6 +104,7 @@ static int pre_lk(DECL_ARGS); static int pre_li(DECL_ARGS); static int pre_nm(DECL_ARGS); static int pre_no(DECL_ARGS); +static void pre_noarg(DECL_ARGS); static int pre_ns(DECL_ARGS); static void pre_onearg(DECL_ARGS); static int pre_pp(DECL_ARGS); @@ -124,12 +126,14 @@ static void print_width(const struct mdoc_bl *, static void print_count(int *); static void print_node(DECL_ARGS); -static const void_fp roff_manacts[ROFF_MAX] = { +static const void_fp roff_man_acts[ROFF_MAX] = { pre_br, /* br */ pre_onearg, /* ce */ + pre_noarg, /* fi */ pre_ft, /* ft */ pre_onearg, /* ll */ pre_onearg, /* mc */ + pre_noarg, /* nf */ pre_onearg, /* po */ pre_onearg, /* rj */ pre_sp, /* sp */ @@ -137,7 +141,7 @@ static const void_fp roff_manacts[ROFF_MAX] = { pre_onearg, /* ti */ }; -static const struct manact __manacts[MDOC_MAX - MDOC_Dd] = { +static const struct mdoc_man_act mdoc_man_acts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL, NULL, NULL, NULL }, /* Dd */ { NULL, NULL, NULL, NULL, NULL }, /* Dt */ { NULL, NULL, NULL, NULL, NULL }, /* Os */ @@ -172,7 +176,7 @@ static const struct manact __manacts[MDOC_MAX - MDOC_Dd] = { { cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */ { NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */ { cond_body, pre_enc, post_enc, "[", "]" }, /* Op */ - { NULL, pre_Ft, post_font, NULL, NULL }, /* Ot */ + { NULL, pre_abort, NULL, NULL, NULL }, /* Ot */ { NULL, pre_em, post_font, NULL, NULL }, /* Pa */ { NULL, pre_ex, NULL, NULL, NULL }, /* Rv */ { NULL, NULL, NULL, NULL, NULL }, /* St */ @@ -245,7 +249,7 @@ static const struct manact __manacts[MDOC_MAX - MDOC_Dd] = { { NULL, pre_em, post_font, NULL, NULL }, /* Fr */ { NULL, NULL, NULL, NULL, NULL }, /* Ud */ { NULL, NULL, post_lb, NULL, NULL }, /* Lb */ - { NULL, pre_pp, NULL, NULL, NULL }, /* Lp */ + { NULL, pre_abort, NULL, NULL, NULL }, /* Lp */ { NULL, pre_lk, NULL, NULL, NULL }, /* Lk */ { NULL, pre_em, post_font, NULL, NULL }, /* Mt */ { cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */ @@ -259,7 +263,7 @@ static const struct manact __manacts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL, post_percent, NULL, NULL }, /* %U */ { NULL, NULL, NULL, NULL, NULL }, /* Ta */ }; -static const struct manact *const manacts = __manacts - MDOC_Dd; +static const struct mdoc_man_act *mdoc_man_act(enum roff_tok); static int outflags; #define MMAN_spc (1 << 0) /* blank character before next word */ @@ -290,6 +294,13 @@ static struct { } fontqueue; +static const struct mdoc_man_act * +mdoc_man_act(enum roff_tok tok) +{ + assert(tok >= MDOC_Dd && tok <= MDOC_MAX); + return mdoc_man_acts + (tok - MDOC_Dd); +} + static int man_strlen(const char *cp) { @@ -317,6 +328,7 @@ man_strlen(const char *cp) case ESCAPE_UNICODE: case ESCAPE_NUMBERED: case ESCAPE_SPECIAL: + case ESCAPE_UNDEF: case ESCAPE_OVERSTRIKE: if (skip) skip = 0; @@ -593,20 +605,7 @@ print_count(int *count) } void -man_man(void *arg, const struct roff_man *man) -{ - - /* - * Dump the keep buffer. - * We're guaranteed by now that this exists (is non-NULL). - * Flush stdout afterward, just in case. - */ - fputs(mparse_getkeep(man_mparse(man)), stdout); - fflush(stdout); -} - -void -man_mdoc(void *arg, const struct roff_man *mdoc) +man_mdoc(void *arg, const struct roff_meta *mdoc) { struct roff_node *n; @@ -619,9 +618,8 @@ man_mdoc(void *arg, const struct roff_man *mdoc) } printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", - mdoc->meta.title, - (mdoc->meta.msec == NULL ? "" : mdoc->meta.msec), - mdoc->meta.date, mdoc->meta.os, mdoc->meta.vol); + mdoc->title, (mdoc->msec == NULL ? "" : mdoc->msec), + mdoc->date, mdoc->os, mdoc->vol); /* Disable hyphenation and if nroff, disable justification. */ printf(".nh\n.if n .ad l"); @@ -633,16 +631,16 @@ man_mdoc(void *arg, const struct roff_man *mdoc) *fontqueue.tail = 'R'; } for (; n != NULL; n = n->next) - print_node(&mdoc->meta, n); + print_node(mdoc, n); putchar('\n'); } static void print_node(DECL_ARGS) { - const struct manact *act; - struct roff_node *sub; - int cond, do_sub; + const struct mdoc_man_act *act; + struct roff_node *sub; + int cond, do_sub; if (n->flags & NODE_NOPRT) return; @@ -680,15 +678,14 @@ print_node(DECL_ARGS) else if (outflags & MMAN_Sm) outflags |= MMAN_spc; } else if (n->tok < ROFF_MAX) { - (*roff_manacts[n->tok])(meta, n); + (*roff_man_acts[n->tok])(meta, n); return; } else { - assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); /* * Conditionally run the pre-node action handler for a * node. */ - act = manacts + n->tok; + act = mdoc_man_act(n->tok); cond = act->cond == NULL || (*act->cond)(meta, n); if (cond && act->pre != NULL && (n->end == ENDBODY_NOT || n->child != NULL)) @@ -732,11 +729,17 @@ cond_body(DECL_ARGS) } static int +pre_abort(DECL_ARGS) +{ + abort(); +} + +static int pre_enc(DECL_ARGS) { const char *prefix; - prefix = manacts[n->tok].prefix; + prefix = mdoc_man_act(n->tok)->prefix; if (NULL == prefix) return 1; print_word(prefix); @@ -749,7 +752,7 @@ post_enc(DECL_ARGS) { const char *suffix; - suffix = manacts[n->tok].suffix; + suffix = mdoc_man_act(n->tok)->suffix; if (NULL == suffix) return; outflags &= ~(MMAN_spc | MMAN_nl); @@ -774,7 +777,7 @@ static void post_percent(DECL_ARGS) { - if (pre_em == manacts[n->tok].pre) + if (mdoc_man_act(n->tok)->pre == pre_em) font_pop(); if (n->next) { print_word(","); @@ -820,7 +823,7 @@ pre_sect(DECL_ARGS) if (n->type == ROFFT_HEAD) { outflags |= MMAN_sp; - print_block(manacts[n->tok].prefix, 0); + print_block(mdoc_man_act(n->tok)->prefix, 0); print_word(""); putchar('\"'); outflags &= ~MMAN_spc; @@ -936,7 +939,6 @@ post_aq(DECL_ARGS) static int pre_bd(DECL_ARGS) { - outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br); if (DISP_unfilled == n->norm->Bd.type || @@ -951,12 +953,27 @@ pre_bd(DECL_ARGS) static void post_bd(DECL_ARGS) { + enum roff_tok bef, now; /* Close out this display. */ print_line(".RE", MMAN_nl); - if (DISP_unfilled == n->norm->Bd.type || - DISP_literal == n->norm->Bd.type) - print_line(".fi", MMAN_nl); + bef = n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi; + if (n->last == NULL) + now = n->norm->Bd.type == DISP_unfilled || + n->norm->Bd.type == DISP_literal ? ROFF_nf : ROFF_fi; + else if (n->last->tok == ROFF_nf) + now = ROFF_nf; + else if (n->last->tok == ROFF_fi) + now = ROFF_fi; + else + now = n->last->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi; + if (bef != now) { + outflags |= MMAN_nl; + print_word("."); + outflags &= ~MMAN_spc; + print_word(roff_name[bef]); + outflags |= MMAN_nl; + } /* Maybe we are inside an enclosing list? */ if (NULL != n->parent->next) @@ -1607,7 +1624,6 @@ pre_onearg(DECL_ARGS) static int pre_li(DECL_ARGS) { - font_push('R'); return 1; } @@ -1640,7 +1656,6 @@ pre_nm(DECL_ARGS) static void post_nm(DECL_ARGS) { - switch (n->type) { case ROFFT_BLOCK: outflags &= ~MMAN_Bk; @@ -1658,15 +1673,23 @@ post_nm(DECL_ARGS) static int pre_no(DECL_ARGS) { - outflags |= MMAN_spc_force; return 1; } +static void +pre_noarg(DECL_ARGS) +{ + outflags |= MMAN_nl; + print_word("."); + outflags &= ~MMAN_spc; + print_word(roff_name[n->tok]); + outflags |= MMAN_nl; +} + static int pre_ns(DECL_ARGS) { - outflags &= ~MMAN_spc; return 0; } diff --git a/usr/src/cmd/mandoc/mdoc_markdown.c b/usr/src/cmd/mandoc/mdoc_markdown.c index e73440a4e5..b73811b76b 100644 --- a/usr/src/cmd/mandoc/mdoc_markdown.c +++ b/usr/src/cmd/mandoc/mdoc_markdown.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_markdown.c,v 1.24 2018/04/11 17:11:13 schwarze Exp $ */ +/* $Id: mdoc_markdown.c,v 1.30 2018/12/30 00:49:55 schwarze Exp $ */ /* - * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2017, 2018 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 @@ -19,6 +19,7 @@ #include <assert.h> #include <ctype.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "mandoc_aux.h" @@ -48,6 +49,7 @@ static void md_uri(const char *); static int md_cond_head(struct roff_node *); static int md_cond_body(struct roff_node *); +static int md_pre_abort(struct roff_node *); static int md_pre_raw(struct roff_node *); static int md_pre_word(struct roff_node *); static int md_pre_skip(struct roff_node *); @@ -103,7 +105,7 @@ static void md_post_Pf(struct roff_node *); static void md_post_Vt(struct roff_node *); static void md_post__T(struct roff_node *); -static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = { +static const struct md_act md_acts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL, NULL, NULL, NULL }, /* Dd */ { NULL, NULL, NULL, NULL, NULL }, /* Dt */ { NULL, NULL, NULL, NULL, NULL }, /* Os */ @@ -138,7 +140,7 @@ static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = { { md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */ { NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */ { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */ - { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ot */ + { NULL, md_pre_abort, NULL, NULL, NULL }, /* Ot */ { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */ { NULL, NULL, NULL, NULL, NULL }, /* Rv */ { NULL, NULL, NULL, NULL, NULL }, /* St */ @@ -211,7 +213,7 @@ static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = { { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */ { NULL, NULL, NULL, NULL, NULL }, /* Ud */ { NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */ - { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Lp */ + { NULL, md_pre_abort, NULL, NULL, NULL }, /* Lp */ { NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */ { NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */ { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */ @@ -225,7 +227,7 @@ static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = { { NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */ { NULL, NULL, NULL, NULL, NULL }, /* Ta */ }; -static const struct md_act *const md_acts = __md_acts - MDOC_Dd; +static const struct md_act *md_act(enum roff_tok); static int outflags; #define MD_spc (1 << 0) /* Blank character before next word. */ @@ -250,22 +252,30 @@ static int escflags; /* Escape in generated markdown code: */ static int code_blocks, quote_blocks, list_blocks; static int outcount; + +static const struct md_act * +md_act(enum roff_tok tok) +{ + assert(tok >= MDOC_Dd && tok <= MDOC_MAX); + return md_acts + (tok - MDOC_Dd); +} + void -markdown_mdoc(void *arg, const struct roff_man *mdoc) +markdown_mdoc(void *arg, const struct roff_meta *mdoc) { outflags = MD_Sm; - md_word(mdoc->meta.title); - if (mdoc->meta.msec != NULL) { + md_word(mdoc->title); + if (mdoc->msec != NULL) { outflags &= ~MD_spc; md_word("("); - md_word(mdoc->meta.msec); + md_word(mdoc->msec); md_word(")"); } md_word("-"); - md_word(mdoc->meta.vol); - if (mdoc->meta.arch != NULL) { + md_word(mdoc->vol); + if (mdoc->arch != NULL) { md_word("("); - md_word(mdoc->meta.arch); + md_word(mdoc->arch); md_word(")"); } outflags |= MD_sp; @@ -273,9 +283,9 @@ markdown_mdoc(void *arg, const struct roff_man *mdoc) md_nodelist(mdoc->first->child); outflags |= MD_sp; - md_word(mdoc->meta.os); + md_word(mdoc->os); md_word("-"); - md_word(mdoc->meta.date); + md_word(mdoc->date); putchar('\n'); } @@ -330,8 +340,7 @@ md_node(struct roff_node *n) break; } } else { - assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); - act = md_acts + n->tok; + act = md_act(n->tok); cond = act->cond == NULL || (*act->cond)(n); if (cond && act->pre != NULL && (n->end == ENDBODY_NOT || n->child != NULL)) @@ -580,6 +589,12 @@ md_word(const char *s) case ESCAPE_SPECIAL: uc = mchars_spec2cp(seq, sz); break; + case ESCAPE_UNDEF: + uc = *seq; + break; + case ESCAPE_DEVICE: + md_rawword("markdown"); + continue; case ESCAPE_FONTBOLD: nextfont = "**"; break; @@ -590,6 +605,7 @@ md_word(const char *s) nextfont = "***"; break; case ESCAPE_FONT: + case ESCAPE_FONTCW: case ESCAPE_FONTROMAN: nextfont = ""; break; @@ -712,11 +728,17 @@ md_cond_body(struct roff_node *n) } static int +md_pre_abort(struct roff_node *n) +{ + abort(); +} + +static int md_pre_raw(struct roff_node *n) { const char *prefix; - if ((prefix = md_acts[n->tok].prefix) != NULL) { + if ((prefix = md_act(n->tok)->prefix) != NULL) { md_rawword(prefix); outflags &= ~MD_spc; if (*prefix == '`') @@ -730,7 +752,7 @@ md_post_raw(struct roff_node *n) { const char *suffix; - if ((suffix = md_acts[n->tok].suffix) != NULL) { + if ((suffix = md_act(n->tok)->suffix) != NULL) { outflags &= ~(MD_spc | MD_nl); md_rawword(suffix); if (*suffix == '`') @@ -743,7 +765,7 @@ md_pre_word(struct roff_node *n) { const char *prefix; - if ((prefix = md_acts[n->tok].prefix) != NULL) { + if ((prefix = md_act(n->tok)->prefix) != NULL) { md_word(prefix); outflags &= ~MD_spc; } @@ -755,7 +777,7 @@ md_post_word(struct roff_node *n) { const char *suffix; - if ((suffix = md_acts[n->tok].suffix) != NULL) { + if ((suffix = md_act(n->tok)->suffix) != NULL) { outflags &= ~(MD_spc | MD_nl); md_word(suffix); } @@ -1268,7 +1290,7 @@ md_post_It(struct roff_node *n) while ((n = n->prev) != NULL && n->type != ROFFT_HEAD) i++; - /* + /* * If a width was specified for this column, * subtract what printed, and * add the same spacing as in mdoc_term.c. diff --git a/usr/src/cmd/mandoc/mdoc_state.c b/usr/src/cmd/mandoc/mdoc_state.c index 2d8563f5bf..f9a585e736 100644 --- a/usr/src/cmd/mandoc/mdoc_state.c +++ b/usr/src/cmd/mandoc/mdoc_state.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_state.c,v 1.9 2017/11/29 20:05:33 schwarze Exp $ */ +/* $Id: mdoc_state.c,v 1.15 2019/01/01 07:42:04 schwarze Exp $ */ /* * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * @@ -17,6 +17,7 @@ #include <sys/types.h> #include <assert.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> @@ -24,19 +25,18 @@ #include "roff.h" #include "mdoc.h" #include "libmandoc.h" +#include "roff_int.h" #include "libmdoc.h" #define STATE_ARGS struct roff_man *mdoc, struct roff_node *n typedef void (*state_handler)(STATE_ARGS); -static void state_bd(STATE_ARGS); static void state_bl(STATE_ARGS); -static void state_dl(STATE_ARGS); static void state_sh(STATE_ARGS); static void state_sm(STATE_ARGS); -static const state_handler __state_handlers[MDOC_MAX - MDOC_Dd] = { +static const state_handler state_handlers[MDOC_MAX - MDOC_Dd] = { NULL, /* Dd */ NULL, /* Dt */ NULL, /* Os */ @@ -44,8 +44,8 @@ static const state_handler __state_handlers[MDOC_MAX - MDOC_Dd] = { NULL, /* Ss */ NULL, /* Pp */ NULL, /* D1 */ - state_dl, /* Dl */ - state_bd, /* Bd */ + NULL, /* Dl */ + NULL, /* Bd */ NULL, /* Ed */ state_bl, /* Bl */ NULL, /* El */ @@ -158,7 +158,6 @@ static const state_handler __state_handlers[MDOC_MAX - MDOC_Dd] = { NULL, /* %U */ NULL, /* Ta */ }; -static const state_handler *const state_handlers = __state_handlers - MDOC_Dd; void @@ -170,41 +169,14 @@ mdoc_state(struct roff_man *mdoc, struct roff_node *n) return; assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); - if ( ! (mdoc_macros[n->tok].flags & MDOC_PROLOGUE)) + if ((mdoc_macro(n->tok)->flags & MDOC_PROLOGUE) == 0) mdoc->flags |= MDOC_PBODY; - handler = state_handlers[n->tok]; + handler = state_handlers[n->tok - MDOC_Dd]; if (*handler) (*handler)(mdoc, n); } -void -mdoc_state_reset(struct roff_man *mdoc) -{ - - roff_setreg(mdoc->roff, "nS", 0, '='); - mdoc->flags = 0; -} - -static void -state_bd(STATE_ARGS) -{ - enum mdocargt arg; - - if (n->type != ROFFT_HEAD && - (n->type != ROFFT_BODY || n->end != ENDBODY_NOT)) - return; - - if (n->parent->args == NULL) - return; - - arg = n->parent->args->argv[0].arg; - if (arg != MDOC_Literal && arg != MDOC_Unfilled) - return; - - state_dl(mdoc, n); -} - static void state_bl(STATE_ARGS) { @@ -230,22 +202,6 @@ state_bl(STATE_ARGS) } static void -state_dl(STATE_ARGS) -{ - - switch (n->type) { - case ROFFT_HEAD: - mdoc->flags |= MDOC_LITERAL; - break; - case ROFFT_BODY: - mdoc->flags &= ~MDOC_LITERAL; - break; - default: - break; - } -} - -static void state_sh(STATE_ARGS) { struct roff_node *nch; diff --git a/usr/src/cmd/mandoc/mdoc_term.c b/usr/src/cmd/mandoc/mdoc_term.c index cf3e7ef3dd..c2f265c4e7 100644 --- a/usr/src/cmd/mandoc/mdoc_term.c +++ b/usr/src/cmd/mandoc/mdoc_term.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_term.c,v 1.367 2018/04/11 17:11:13 schwarze Exp $ */ +/* $Id: mdoc_term.c,v 1.373 2019/06/03 19:50:33 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2012-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> * * Permission to use, copy, modify, and distribute this software for any @@ -29,7 +29,6 @@ #include <string.h> #include "mandoc_aux.h" -#include "mandoc.h" #include "roff.h" #include "mdoc.h" #include "out.h" @@ -47,7 +46,7 @@ struct termpair { const struct roff_meta *meta, \ struct roff_node *n -struct termact { +struct mdoc_term_act { int (*pre)(DECL_ARGS); void (*post)(DECL_ARGS); }; @@ -84,6 +83,7 @@ static void termp_xx_post(DECL_ARGS); static int termp__a_pre(DECL_ARGS); static int termp__t_pre(DECL_ARGS); +static int termp_abort_pre(DECL_ARGS); static int termp_an_pre(DECL_ARGS); static int termp_ap_pre(DECL_ARGS); static int termp_bd_pre(DECL_ARGS); @@ -124,7 +124,7 @@ static int termp_vt_pre(DECL_ARGS); static int termp_xr_pre(DECL_ARGS); static int termp_xx_pre(DECL_ARGS); -static const struct termact __termacts[MDOC_MAX - MDOC_Dd] = { +static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL }, /* Dd */ { NULL, NULL }, /* Dt */ { NULL, NULL }, /* Os */ @@ -159,7 +159,7 @@ static const struct termact __termacts[MDOC_MAX - MDOC_Dd] = { { termp_nd_pre, NULL }, /* Nd */ { termp_nm_pre, termp_nm_post }, /* Nm */ { termp_quote_pre, termp_quote_post }, /* Op */ - { termp_ft_pre, NULL }, /* Ot */ + { termp_abort_pre, NULL }, /* Ot */ { termp_under_pre, NULL }, /* Pa */ { termp_ex_pre, NULL }, /* Rv */ { NULL, NULL }, /* St */ @@ -232,7 +232,7 @@ static const struct termact __termacts[MDOC_MAX - MDOC_Dd] = { { termp_under_pre, NULL }, /* Fr */ { NULL, NULL }, /* Ud */ { NULL, termp_lb_post }, /* Lb */ - { termp_pp_pre, NULL }, /* Lp */ + { termp_abort_pre, NULL }, /* Lp */ { termp_lk_pre, NULL }, /* Lk */ { termp_under_pre, NULL }, /* Mt */ { termp_quote_pre, termp_quote_post }, /* Brq */ @@ -246,13 +246,12 @@ static const struct termact __termacts[MDOC_MAX - MDOC_Dd] = { { NULL, termp____post }, /* %U */ { NULL, NULL }, /* Ta */ }; -static const struct termact *const termacts = __termacts - MDOC_Dd; static int fn_prio; void -terminal_mdoc(void *arg, const struct roff_man *mdoc) +terminal_mdoc(void *arg, const struct roff_meta *mdoc) { struct roff_node *n; struct termp *p; @@ -270,8 +269,7 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc) if (n->tok == MDOC_Sh && n->sec == SEC_SYNOPSIS) { if (n->child->next->child != NULL) print_mdoc_nodelist(p, NULL, - &mdoc->meta, - n->child->next->child); + mdoc, n->child->next->child); term_newln(p); break; } @@ -281,8 +279,7 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc) save_defindent = p->defindent; if (p->defindent == 0) p->defindent = 5; - term_begin(p, print_mdoc_head, print_mdoc_foot, - &mdoc->meta); + term_begin(p, print_mdoc_head, print_mdoc_foot, mdoc); while (n != NULL && (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)) @@ -290,7 +287,7 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc) if (n != NULL) { if (n->tok != MDOC_Sh) term_vspace(p); - print_mdoc_nodelist(p, NULL, &mdoc->meta, n); + print_mdoc_nodelist(p, NULL, mdoc, n); } term_end(p); p->defindent = save_defindent; @@ -310,9 +307,23 @@ print_mdoc_nodelist(DECL_ARGS) static void print_mdoc_node(DECL_ARGS) { - int chld; + const struct mdoc_term_act *act; struct termpair npair; size_t offset, rmargin; + int chld; + + /* + * In no-fill mode, break the output line at the beginning + * of new input lines except after \c, and nowhere else. + */ + + if (n->flags & NODE_NOFILL) { + if (n->flags & NODE_LINE && + (p->flags & TERMP_NONEWLINE) == 0) + term_newln(p); + p->flags |= TERMP_BRNEVER; + } else + p->flags &= ~TERMP_BRNEVER; if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) return; @@ -341,11 +352,25 @@ print_mdoc_node(DECL_ARGS) * produce output. Note that some pre-handlers do so. */ + act = NULL; switch (n->type) { case ROFFT_TEXT: - if (*n->string == ' ' && n->flags & NODE_LINE && - (p->flags & TERMP_NONEWLINE) == 0) - term_newln(p); + if (n->flags & NODE_LINE) { + switch (*n->string) { + case '\0': + if (p->flags & TERMP_NONEWLINE) + term_newln(p); + else + term_vspace(p); + return; + case ' ': + if ((p->flags & TERMP_NONEWLINE) == 0) + term_newln(p); + break; + default: + break; + } + } if (NODE_DELIMC & n->flags) p->flags |= TERMP_NOSPACE; term_word(p, n->string); @@ -370,10 +395,10 @@ print_mdoc_node(DECL_ARGS) return; } assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); - if (termacts[n->tok].pre != NULL && + act = mdoc_term_acts + (n->tok - MDOC_Dd); + if (act->pre != NULL && (n->end == ENDBODY_NOT || n->child != NULL)) - chld = (*termacts[n->tok].pre) - (p, &npair, meta, n); + chld = (*act->pre)(p, &npair, meta, n); break; } @@ -391,9 +416,9 @@ print_mdoc_node(DECL_ARGS) case ROFFT_EQN: break; default: - if (termacts[n->tok].post == NULL || n->flags & NODE_ENDED) + if (act->post == NULL || n->flags & NODE_ENDED) break; - (void)(*termacts[n->tok].post)(p, &npair, meta, n); + (void)(*act->post)(p, &npair, meta, n); /* * Explicit end tokens not only call the post @@ -1420,8 +1445,6 @@ termp_fa_pre(DECL_ARGS) static int termp_bd_pre(DECL_ARGS) { - size_t lm, len; - struct roff_node *nn; int offset; if (n->type == ROFFT_BLOCK) { @@ -1447,66 +1470,19 @@ termp_bd_pre(DECL_ARGS) p->tcol->offset += offset; } - /* - * If -ragged or -filled are specified, the block does nothing - * but change the indentation. If -unfilled or -literal are - * specified, text is printed exactly as entered in the display: - * for macro lines, a newline is appended to the line. Blank - * lines are allowed. - */ - - if (n->norm->Bd.type != DISP_literal && - n->norm->Bd.type != DISP_unfilled && - n->norm->Bd.type != DISP_centered) - return 1; - - if (n->norm->Bd.type == DISP_literal) { + switch (n->norm->Bd.type) { + case DISP_literal: term_tab_set(p, NULL); term_tab_set(p, "T"); term_tab_set(p, "8n"); + break; + case DISP_centered: + p->flags |= TERMP_CENTER; + break; + default: + break; } - - lm = p->tcol->offset; - p->flags |= TERMP_BRNEVER; - for (nn = n->child; nn != NULL; nn = nn->next) { - if (n->norm->Bd.type == DISP_centered) { - if (nn->type == ROFFT_TEXT) { - len = term_strlen(p, nn->string); - p->tcol->offset = len >= p->tcol->rmargin ? - 0 : lm + len >= p->tcol->rmargin ? - p->tcol->rmargin - len : - (lm + p->tcol->rmargin - len) / 2; - } else - p->tcol->offset = lm; - } - print_mdoc_node(p, pair, meta, nn); - /* - * If the printed node flushes its own line, then we - * needn't do it here as well. This is hacky, but the - * notion of selective eoln whitespace is pretty dumb - * anyway, so don't sweat it. - */ - if (nn->tok < ROFF_MAX) - continue; - switch (nn->tok) { - case MDOC_Sm: - case MDOC_Bl: - case MDOC_D1: - case MDOC_Dl: - case MDOC_Lp: - case MDOC_Pp: - continue; - default: - break; - } - if (p->flags & TERMP_NONEWLINE || - (nn->next && ! (nn->next->flags & NODE_LINE))) - continue; - term_flushln(p); - p->flags |= TERMP_NOSPACE; - } - p->flags &= ~TERMP_BRNEVER; - return 0; + return 1; } static void @@ -1514,12 +1490,14 @@ termp_bd_post(DECL_ARGS) { if (n->type != ROFFT_BODY) return; - if (DISP_literal == n->norm->Bd.type || - DISP_unfilled == n->norm->Bd.type) + if (n->norm->Bd.type == DISP_unfilled || + n->norm->Bd.type == DISP_literal) p->flags |= TERMP_BRNEVER; p->flags |= TERMP_NOSPACE; term_newln(p); p->flags &= ~TERMP_BRNEVER; + if (n->norm->Bd.type == DISP_centered) + p->flags &= ~TERMP_CENTER; } static int @@ -2098,3 +2076,9 @@ termp_tag_pre(DECL_ARGS) tag_put(n->child->string, 1, p->line); return 1; } + +static int +termp_abort_pre(DECL_ARGS) +{ + abort(); +} diff --git a/usr/src/cmd/mandoc/mdoc_validate.c b/usr/src/cmd/mandoc/mdoc_validate.c index 52545e3c46..a31310ec45 100644 --- a/usr/src/cmd/mandoc/mdoc_validate.c +++ b/usr/src/cmd/mandoc/mdoc_validate.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_validate.c,v 1.360 2018/08/01 16:00:58 schwarze Exp $ */ +/* $Id: mdoc_validate.c,v 1.371 2019/03/04 13:01:57 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> * Copyright 2018, Joyent, Inc. * @@ -64,6 +64,7 @@ static int child_an(const struct roff_node *); static size_t macro2len(enum roff_tok); static void rewrite_macro2len(struct roff_man *, char **); static int similar(const char *, const char *); +static void post_abort(POST_ARGS); static void post_an(POST_ARGS); static void post_an_norm(POST_ARGS); @@ -117,7 +118,7 @@ static void post_useless(POST_ARGS); static void post_xr(POST_ARGS); static void post_xx(POST_ARGS); -static const v_post __mdoc_valids[MDOC_MAX - MDOC_Dd] = { +static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_dd, /* Dd */ post_dt, /* Dt */ post_os, /* Os */ @@ -152,7 +153,7 @@ static const v_post __mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_nd, /* Nd */ post_nm, /* Nm */ post_delim_nb, /* Op */ - post_obsolete, /* Ot */ + post_abort, /* Ot */ post_defaults, /* Pa */ post_rv, /* Rv */ post_st, /* St */ @@ -225,7 +226,7 @@ static const v_post __mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_obsolete, /* Fr */ post_eoln, /* Ud */ post_lb, /* Lb */ - post_par, /* Lp */ + post_abort, /* Lp */ post_delim_nb, /* Lk */ post_defaults, /* Mt */ post_delim_nb, /* Brq */ @@ -239,7 +240,6 @@ static const v_post __mdoc_valids[MDOC_MAX - MDOC_Dd] = { NULL, /* %U */ NULL, /* Ta */ }; -static const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd; #define RSORD_MAX 14 /* Number of `Rs' blocks. */ @@ -287,22 +287,48 @@ static const char * const secnames[SEC__MAX] = { }; +/* Validate the subtree rooted at mdoc->last. */ void -mdoc_node_validate(struct roff_man *mdoc) +mdoc_validate(struct roff_man *mdoc) { struct roff_node *n, *np; const v_post *p; + /* + * Translate obsolete macros to modern macros first + * such that later code does not need to look + * for the obsolete versions. + */ + n = mdoc->last; + switch (n->tok) { + case MDOC_Lp: + n->tok = MDOC_Pp; + break; + case MDOC_Ot: + post_obsolete(mdoc); + n->tok = MDOC_Ft; + break; + default: + break; + } + + /* + * Iterate over all children, recursing into each one + * in turn, depth-first. + */ + mdoc->last = mdoc->last->child; while (mdoc->last != NULL) { - mdoc_node_validate(mdoc); + mdoc_validate(mdoc); if (mdoc->last == n) mdoc->last = mdoc->last->child; else mdoc->last = mdoc->last->next; } + /* Finally validate the macro itself. */ + mdoc->last = n; mdoc->next = ROFF_NEXT_SIBLING; switch (n->type) { @@ -311,9 +337,7 @@ mdoc_node_validate(struct roff_man *mdoc) if (n->sec != SEC_SYNOPSIS || (np->tok != MDOC_Cd && np->tok != MDOC_Fd)) check_text(mdoc, n->line, n->pos, n->string); - if (np->tok != MDOC_Ql && np->tok != MDOC_Dl && - (np->tok != MDOC_Bd || - (mdoc->flags & MDOC_LITERAL) == 0) && + if ((n->flags & NODE_NOFILL) == 0 && (np->tok != MDOC_It || np->type != ROFFT_HEAD || np->parent->parent->norm->Bl.type != LIST_diag)) check_text_em(mdoc, n->line, n->pos, n->string); @@ -345,20 +369,12 @@ mdoc_node_validate(struct roff_man *mdoc) /* Call the macro's postprocessor. */ if (n->tok < ROFF_MAX) { - switch(n->tok) { - case ROFF_br: - case ROFF_sp: - post_par(mdoc); - break; - default: - roff_validate(mdoc); - break; - } + roff_validate(mdoc); break; } assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); - p = mdoc_valids + n->tok; + p = mdoc_valids + (n->tok - MDOC_Dd); if (*p) (*p)(mdoc); if (mdoc->last == n) @@ -394,12 +410,11 @@ check_text(struct roff_man *mdoc, int ln, int pos, char *p) { char *cp; - if (MDOC_LITERAL & mdoc->flags) + if (mdoc->last->flags & NODE_NOFILL) return; for (cp = p; NULL != (p = strchr(p, '\t')); p++) - mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse, - ln, pos + (int)(p - cp), NULL); + mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL); } static void @@ -446,7 +461,7 @@ check_text_em(struct roff_man *mdoc, int ln, int pos, char *p) nn != NULL && nn->type == ROFFT_TEXT && isalpha((unsigned char)*nn->string))) { - mandoc_msg(MANDOCERR_DASHDASH, mdoc->parse, + mandoc_msg(MANDOCERR_DASHDASH, ln, pos + (int)(cp - p) - 1, NULL); break; } @@ -462,17 +477,13 @@ check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p) return; if ((cp = strstr(p, "OpenBSD")) != NULL) - mandoc_msg(MANDOCERR_BX, mdoc->parse, - ln, pos + (cp - p), "Ox"); + mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox"); if ((cp = strstr(p, "NetBSD")) != NULL) - mandoc_msg(MANDOCERR_BX, mdoc->parse, - ln, pos + (cp - p), "Nx"); + mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx"); if ((cp = strstr(p, "FreeBSD")) != NULL) - mandoc_msg(MANDOCERR_BX, mdoc->parse, - ln, pos + (cp - p), "Fx"); + mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx"); if ((cp = strstr(p, "DragonFly")) != NULL) - mandoc_msg(MANDOCERR_BX, mdoc->parse, - ln, pos + (cp - p), "Dx"); + mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx"); cp = p; while ((cp = strstr(cp + 1, "()")) != NULL) { @@ -481,14 +492,19 @@ check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p) break; if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) { cpr++; - mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse, - ln, pos + (cpr - p), + mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p), "%.*s()", (int)(cp - cpr), cpr); } } } static void +post_abort(POST_ARGS) +{ + abort(); +} + +static void post_delim(POST_ARGS) { const struct roff_node *nch; @@ -510,9 +526,8 @@ post_delim(POST_ARGS) tok == MDOC_Ss || tok == MDOC_Fo)) return; - mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse, - nch->line, nch->pos + (lc - nch->string), - "%s%s %s", roff_name[tok], + mandoc_msg(MANDOCERR_DELIM, nch->line, + nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok], nch == mdoc->last->child ? "" : " ...", nch->string); } @@ -604,9 +619,8 @@ post_delim_nb(POST_ARGS) } } - mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse, - nch->line, nch->pos + (lc - nch->string), - "%s%s %s", roff_name[tok], + mandoc_msg(MANDOCERR_DELIM_NB, nch->line, + nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok], nch == mdoc->last->child ? "" : " ...", nch->string); } @@ -672,39 +686,34 @@ post_bl_norm(POST_ARGS) case MDOC_Compact: if (n->norm->Bl.comp) mandoc_msg(MANDOCERR_ARG_REP, - mdoc->parse, argv->line, - argv->pos, "Bl -compact"); + argv->line, argv->pos, "Bl -compact"); n->norm->Bl.comp = 1; break; case MDOC_Width: wa = argv; if (0 == argv->sz) { mandoc_msg(MANDOCERR_ARG_EMPTY, - mdoc->parse, argv->line, - argv->pos, "Bl -width"); + argv->line, argv->pos, "Bl -width"); n->norm->Bl.width = "0n"; break; } if (NULL != n->norm->Bl.width) - mandoc_vmsg(MANDOCERR_ARG_REP, - mdoc->parse, argv->line, - argv->pos, "Bl -width %s", - argv->value[0]); + mandoc_msg(MANDOCERR_ARG_REP, + argv->line, argv->pos, + "Bl -width %s", argv->value[0]); rewrite_macro2len(mdoc, argv->value); n->norm->Bl.width = argv->value[0]; break; case MDOC_Offset: if (0 == argv->sz) { mandoc_msg(MANDOCERR_ARG_EMPTY, - mdoc->parse, argv->line, - argv->pos, "Bl -offset"); + argv->line, argv->pos, "Bl -offset"); break; } if (NULL != n->norm->Bl.offs) - mandoc_vmsg(MANDOCERR_ARG_REP, - mdoc->parse, argv->line, - argv->pos, "Bl -offset %s", - argv->value[0]); + mandoc_msg(MANDOCERR_ARG_REP, + argv->line, argv->pos, + "Bl -offset %s", argv->value[0]); rewrite_macro2len(mdoc, argv->value); n->norm->Bl.offs = argv->value[0]; break; @@ -718,8 +727,7 @@ post_bl_norm(POST_ARGS) /* Check: multiple list types. */ if (LIST__NONE != n->norm->Bl.type) { - mandoc_vmsg(MANDOCERR_BL_REP, - mdoc->parse, n->line, n->pos, + mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos, "Bl -%s", mdoc_argnames[argv->arg]); continue; } @@ -729,8 +737,8 @@ post_bl_norm(POST_ARGS) if (n->norm->Bl.width || n->norm->Bl.offs || n->norm->Bl.comp) - mandoc_vmsg(MANDOCERR_BL_LATETYPE, - mdoc->parse, n->line, n->pos, "Bl -%s", + mandoc_msg(MANDOCERR_BL_LATETYPE, + n->line, n->pos, "Bl -%s", mdoc_argnames[n->args->argv[0].arg]); n->norm->Bl.type = lt; @@ -743,8 +751,7 @@ post_bl_norm(POST_ARGS) /* Allow lists to default to LIST_item. */ if (LIST__NONE == n->norm->Bl.type) { - mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse, - n->line, n->pos, "Bl"); + mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl"); n->norm->Bl.type = LIST_item; mdoclt = MDOC_Item; } @@ -759,7 +766,7 @@ post_bl_norm(POST_ARGS) switch (n->norm->Bl.type) { case LIST_tag: if (n->norm->Bl.width == NULL) - mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse, + mandoc_msg(MANDOCERR_BL_NOWIDTH, n->line, n->pos, "Bl -tag"); break; case LIST_column: @@ -768,9 +775,8 @@ post_bl_norm(POST_ARGS) case LIST_inset: case LIST_item: if (n->norm->Bl.width != NULL) - mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse, - wa->line, wa->pos, "Bl -%s", - mdoc_argnames[mdoclt]); + mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos, + "Bl -%s", mdoc_argnames[mdoclt]); n->norm->Bl.width = NULL; break; case LIST_bullet: @@ -818,29 +824,25 @@ post_bd(POST_ARGS) dt = DISP_literal; break; case MDOC_File: - mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse, - n->line, n->pos, NULL); + mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL); break; case MDOC_Offset: if (0 == argv->sz) { mandoc_msg(MANDOCERR_ARG_EMPTY, - mdoc->parse, argv->line, - argv->pos, "Bd -offset"); + argv->line, argv->pos, "Bd -offset"); break; } if (NULL != n->norm->Bd.offs) - mandoc_vmsg(MANDOCERR_ARG_REP, - mdoc->parse, argv->line, - argv->pos, "Bd -offset %s", - argv->value[0]); + mandoc_msg(MANDOCERR_ARG_REP, + argv->line, argv->pos, + "Bd -offset %s", argv->value[0]); rewrite_macro2len(mdoc, argv->value); n->norm->Bd.offs = argv->value[0]; break; case MDOC_Compact: if (n->norm->Bd.comp) mandoc_msg(MANDOCERR_ARG_REP, - mdoc->parse, argv->line, - argv->pos, "Bd -compact"); + argv->line, argv->pos, "Bd -compact"); n->norm->Bd.comp = 1; break; default: @@ -852,14 +854,12 @@ post_bd(POST_ARGS) if (DISP__NONE == n->norm->Bd.type) n->norm->Bd.type = dt; else - mandoc_vmsg(MANDOCERR_BD_REP, - mdoc->parse, n->line, n->pos, + mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos, "Bd -%s", mdoc_argnames[argv->arg]); } if (DISP__NONE == n->norm->Bd.type) { - mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse, - n->line, n->pos, "Bd"); + mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd"); n->norm->Bd.type = DISP_ragged; } } @@ -881,8 +881,7 @@ post_an_norm(POST_ARGS) for (i = 1; i < n->args->argc; i++) { argv = n->args->argv + i; - mandoc_vmsg(MANDOCERR_AN_REP, - mdoc->parse, argv->line, argv->pos, + mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos, "An -%s", mdoc_argnames[argv->arg]); } @@ -903,7 +902,7 @@ post_eoln(POST_ARGS) post_useless(mdoc); n = mdoc->last; if (n->child != NULL) - mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line, + mandoc_msg(MANDOCERR_ARG_SKIP, n->line, n->pos, "%s %s", roff_name[n->tok], n->child->string); while (n->child != NULL) @@ -925,7 +924,7 @@ build_list(struct roff_man *mdoc, int tok) for (ic = 1;; ic++) { roff_elem_alloc(mdoc, n->line, n->pos, tok); mdoc->last->flags |= NODE_NOSRC; - mdoc_node_relink(mdoc, n); + roff_node_relink(mdoc, n); n = mdoc->last = mdoc->last->parent; mdoc->next = ROFF_NEXT_SIBLING; if (n->next == NULL) @@ -966,8 +965,7 @@ post_ex(POST_ARGS) mdoc->next = ROFF_NEXT_SIBLING; ic = 1; } else { - mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse, - n->line, n->pos, "Ex"); + mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex"); ic = 0; } @@ -1000,7 +998,7 @@ post_lb(POST_ARGS) return; } - mandoc_vmsg(MANDOCERR_LB_BAD, mdoc->parse, n->child->line, + mandoc_msg(MANDOCERR_LB_BAD, n->child->line, n->child->pos, "Lb %s", n->child->string); roff_word_alloc(mdoc, n->line, n->pos, "library"); @@ -1064,8 +1062,8 @@ post_std(POST_ARGS) if (n->args->argv[0].arg == MDOC_Std) return; - mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse, - n->line, n->pos, roff_name[n->tok]); + mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos, + "%s", roff_name[n->tok]); } static void @@ -1079,7 +1077,7 @@ post_st(POST_ARGS) assert(nch->type == ROFFT_TEXT); if ((p = mdoc_a2st(nch->string)) == NULL) { - mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse, + mandoc_msg(MANDOCERR_ST_BAD, nch->line, nch->pos, "St %s", nch->string); roff_node_delete(mdoc, n); return; @@ -1099,8 +1097,8 @@ post_obsolete(POST_ARGS) n = mdoc->last; if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) - mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse, - n->line, n->pos, roff_name[n->tok]); + mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos, + "%s", roff_name[n->tok]); } static void @@ -1109,8 +1107,8 @@ post_useless(POST_ARGS) struct roff_node *n; n = mdoc->last; - mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse, - n->line, n->pos, roff_name[n->tok]); + mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos, + "%s", roff_name[n->tok]); } /* @@ -1139,14 +1137,14 @@ post_bf(POST_ARGS) nch = np->child; if (np->parent->args == NULL) { if (nch == NULL) { - mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse, + mandoc_msg(MANDOCERR_BF_NOFONT, np->line, np->pos, "Bf"); return; } nch = nch->next; } if (nch != NULL) - mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, nch->line, nch->pos, "Bf ... %s", nch->string); /* Extract argument into data. */ @@ -1177,9 +1175,8 @@ post_bf(POST_ARGS) else if ( ! strcmp(np->child->string, "Sy")) np->norm->Bf.font = FONT_Sy; else - mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse, - np->child->line, np->child->pos, - "Bf %s", np->child->string); + mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line, + np->child->pos, "Bf %s", np->child->string); } static void @@ -1193,8 +1190,8 @@ post_fname(POST_ARGS) pos = strcspn(n->string, "()"); cp = n->string + pos; if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*'))) - mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse, - n->line, n->pos + pos, n->string); + mandoc_msg(MANDOCERR_FN_PAREN, n->line, n->pos + pos, + "%s", n->string); } static void @@ -1216,12 +1213,11 @@ post_fo(POST_ARGS) return; if (n->child == NULL) { - mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse, - n->line, n->pos, "Fo"); + mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo"); return; } if (n->child != n->last) { - mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, n->child->next->line, n->child->next->pos, "Fo ... %s", n->child->next->string); while (n->child != n->last) @@ -1245,9 +1241,8 @@ post_fa(POST_ARGS) break; if (*cp != ',') continue; - mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse, - n->line, n->pos + (cp - n->string), - n->string); + mandoc_msg(MANDOCERR_FA_COMMA, n->line, + n->pos + (int)(cp - n->string), "%s", n->string); break; } } @@ -1265,18 +1260,15 @@ post_nm(POST_ARGS) n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL) mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1); - if (n->last != NULL && - (n->last->tok == MDOC_Pp || - n->last->tok == MDOC_Lp)) - mdoc_node_relink(mdoc, n->last); + if (n->last != NULL && n->last->tok == MDOC_Pp) + roff_node_relink(mdoc, n->last); if (mdoc->meta.name == NULL) deroff(&mdoc->meta.name, n); if (mdoc->meta.name == NULL || (mdoc->lastsec == SEC_NAME && n->child == NULL)) - mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse, - n->line, n->pos, "Nm"); + mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm"); switch (n->type) { case ROFFT_ELEM: @@ -1310,12 +1302,10 @@ post_nd(POST_ARGS) return; if (n->sec != SEC_NAME) - mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse, - n->line, n->pos, "Nd"); + mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd"); if (n->child == NULL) - mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse, - n->line, n->pos, "Nd"); + mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd"); else post_delim(mdoc); @@ -1335,8 +1325,8 @@ post_display(POST_ARGS) n->body->parent->args == NULL) roff_node_delete(mdoc, n); } else if (n->child == NULL) - mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, - n->line, n->pos, roff_name[n->tok]); + mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, + "%s", roff_name[n->tok]); else if (n->tok == MDOC_D1) post_hyph(mdoc); break; @@ -1344,10 +1334,10 @@ post_display(POST_ARGS) if (n->tok == MDOC_Bd) { if (n->args == NULL) { mandoc_msg(MANDOCERR_BD_NOARG, - mdoc->parse, n->line, n->pos, "Bd"); + n->line, n->pos, "Bd"); mdoc->next = ROFF_NEXT_SIBLING; while (n->body->child != NULL) - mdoc_node_relink(mdoc, + roff_node_relink(mdoc, n->body->child); roff_node_delete(mdoc, n); break; @@ -1357,9 +1347,8 @@ post_display(POST_ARGS) } for (np = n->parent; np != NULL; np = np->parent) { if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { - mandoc_vmsg(MANDOCERR_BD_NEST, - mdoc->parse, n->line, n->pos, - "%s in Bd", roff_name[n->tok]); + mandoc_msg(MANDOCERR_BD_NEST, n->line, + n->pos, "%s in Bd", roff_name[n->tok]); break; } } @@ -1423,7 +1412,7 @@ post_at(POST_ARGS) att = NULL; if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL)) - mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse, + mandoc_msg(MANDOCERR_AT_BAD, nch->line, nch->pos, "At %s", nch->string); mdoc->next = ROFF_NEXT_CHILD; @@ -1447,12 +1436,12 @@ post_an(POST_ARGS) nch = np->child; if (np->norm->An.auth == AUTH__NONE) { if (nch == NULL) - mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, + mandoc_msg(MANDOCERR_MACRO_EMPTY, np->line, np->pos, "An"); else post_delim_nb(mdoc); } else if (nch != NULL) - mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, nch->line, nch->pos, "An ... %s", nch->string); } @@ -1548,9 +1537,8 @@ post_it(POST_ARGS) case LIST_inset: case LIST_diag: if (nit->head->child == NULL) - mandoc_vmsg(MANDOCERR_IT_NOHEAD, - mdoc->parse, nit->line, nit->pos, - "Bl -%s It", + mandoc_msg(MANDOCERR_IT_NOHEAD, + nit->line, nit->pos, "Bl -%s It", mdoc_argnames[nbl->args->argv[0].arg]); break; case LIST_bullet: @@ -1558,14 +1546,13 @@ post_it(POST_ARGS) case LIST_enum: case LIST_hyphen: if (nit->body == NULL || nit->body->child == NULL) - mandoc_vmsg(MANDOCERR_IT_NOBODY, - mdoc->parse, nit->line, nit->pos, - "Bl -%s It", + mandoc_msg(MANDOCERR_IT_NOBODY, + nit->line, nit->pos, "Bl -%s It", mdoc_argnames[nbl->args->argv[0].arg]); /* FALLTHROUGH */ case LIST_item: if ((nch = nit->head->child) != NULL) - mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, + mandoc_msg(MANDOCERR_ARG_SKIP, nit->line, nit->pos, "It %s", nch->string == NULL ? roff_name[nch->tok] : nch->string); @@ -1577,7 +1564,7 @@ post_it(POST_ARGS) if (nit->head->next->child == NULL && nit->head->next->next == NULL) { - mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, + mandoc_msg(MANDOCERR_MACRO_EMPTY, nit->line, nit->pos, "It"); roff_node_delete(mdoc, nit); break; @@ -1588,16 +1575,15 @@ post_it(POST_ARGS) if (nch->type != ROFFT_BODY) continue; if (i++ && nch->flags & NODE_LINE) - mandoc_msg(MANDOCERR_TA_LINE, mdoc->parse, + mandoc_msg(MANDOCERR_TA_LINE, nch->line, nch->pos, "Ta"); } if (i < cols || i > cols + 1) - mandoc_vmsg(MANDOCERR_BL_COL, - mdoc->parse, nit->line, nit->pos, + mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos, "%d columns, %d cells", cols, i); else if (nit->head->next->child != NULL && - nit->head->next->child->line > nit->line) - mandoc_msg(MANDOCERR_IT_NOARG, mdoc->parse, + nit->head->next->child->flags & NODE_LINE) + mandoc_msg(MANDOCERR_IT_NOARG, nit->line, nit->pos, "Bl -column It"); break; default: @@ -1620,7 +1606,6 @@ post_bl_block(POST_ARGS) while (nc != NULL) { switch (nc->tok) { case MDOC_Pp: - case MDOC_Lp: case ROFF_br: break; default: @@ -1628,14 +1613,13 @@ post_bl_block(POST_ARGS) continue; } if (ni->next == NULL) { - mandoc_msg(MANDOCERR_PAR_MOVE, - mdoc->parse, nc->line, nc->pos, - roff_name[nc->tok]); - mdoc_node_relink(mdoc, nc); + mandoc_msg(MANDOCERR_PAR_MOVE, nc->line, + nc->pos, "%s", roff_name[nc->tok]); + roff_node_relink(mdoc, nc); } else if (n->norm->Bl.comp == 0 && n->norm->Bl.type != LIST_column) { - mandoc_vmsg(MANDOCERR_PAR_SKIP, - mdoc->parse, nc->line, nc->pos, + mandoc_msg(MANDOCERR_PAR_SKIP, + nc->line, nc->pos, "%s before It", roff_name[nc->tok]); roff_node_delete(mdoc, nc); } else @@ -1681,7 +1665,7 @@ post_bl_head(POST_ARGS) if (nh->norm->Bl.type != LIST_column) { if ((nch = nh->child) == NULL) return; - mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, nch->line, nch->pos, "Bl ... %s", nch->string); while (nch != NULL) { roff_node_delete(mdoc, nch); @@ -1758,7 +1742,7 @@ post_bl(POST_ARGS) nchild = nbody->child; if (nchild == NULL) { - mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, + mandoc_msg(MANDOCERR_BLK_EMPTY, nbody->line, nbody->pos, "Bl"); return; } @@ -1791,7 +1775,7 @@ post_bl(POST_ARGS) roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); while (nchild->tok != MDOC_It) { - mdoc_node_relink(mdoc, nchild); + roff_node_relink(mdoc, nchild); if ((nchild = nnext) == NULL) break; nnext = nchild->next; @@ -1801,8 +1785,8 @@ post_bl(POST_ARGS) continue; } - mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse, - nchild->line, nchild->pos, roff_name[nchild->tok]); + mandoc_msg(MANDOCERR_BL_MOVE, nchild->line, nchild->pos, + "%s", roff_name[nchild->tok]); /* * Move the node out of the Bl block. @@ -1857,13 +1841,13 @@ post_bl(POST_ARGS) if (prev_Er != NULL) { order = strcmp(prev_Er, nnext->string); if (order > 0) - mandoc_vmsg(MANDOCERR_ER_ORDER, - mdoc->parse, nnext->line, nnext->pos, + mandoc_msg(MANDOCERR_ER_ORDER, + nnext->line, nnext->pos, "Er %s %s (NetBSD)", prev_Er, nnext->string); else if (order == 0) - mandoc_vmsg(MANDOCERR_ER_REP, - mdoc->parse, nnext->line, nnext->pos, + mandoc_msg(MANDOCERR_ER_REP, + nnext->line, nnext->pos, "Er %s (NetBSD)", prev_Er); } prev_Er = nnext->string; @@ -1878,8 +1862,7 @@ post_bk(POST_ARGS) n = mdoc->last; if (n->type == ROFFT_BLOCK && n->body->child == NULL) { - mandoc_msg(MANDOCERR_BLK_EMPTY, - mdoc->parse, n->line, n->pos, "Bk"); + mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk"); roff_node_delete(mdoc, n); } } @@ -1907,39 +1890,16 @@ post_sm(POST_ARGS) return; } - mandoc_vmsg(MANDOCERR_SM_BAD, - mdoc->parse, nch->line, nch->pos, + mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos, "%s %s", roff_name[mdoc->last->tok], nch->string); - mdoc_node_relink(mdoc, nch); + roff_node_relink(mdoc, nch); return; } static void post_root(POST_ARGS) { - const char *openbsd_arch[] = { - "alpha", "amd64", "arm64", "armv7", "hppa", "i386", - "landisk", "loongson", "luna88k", "macppc", "mips64", - "octeon", "sgi", "socppc", "sparc64", NULL - }; - const char *netbsd_arch[] = { - "acorn26", "acorn32", "algor", "alpha", "amiga", - "arc", "atari", - "bebox", "cats", "cesfic", "cobalt", "dreamcast", - "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5", - "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa", - "i386", "ibmnws", "luna68k", - "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc", - "netwinder", "news68k", "newsmips", "next68k", - "pc532", "playstation2", "pmax", "pmppc", "prep", - "sandpoint", "sbmips", "sgimips", "shark", - "sparc", "sparc64", "sun2", "sun3", - "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL - }; - const char **arches[] = { NULL, netbsd_arch, openbsd_arch }; - struct roff_node *n; - const char **arch; /* Add missing prologue data. */ @@ -1948,8 +1908,7 @@ post_root(POST_ARGS) mandoc_normdate(mdoc, NULL, 0, 0); if (mdoc->meta.title == NULL) { - mandoc_msg(MANDOCERR_DT_NOTITLE, - mdoc->parse, 0, 0, "EOF"); + mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF"); mdoc->meta.title = mandoc_strdup("UNTITLED"); } @@ -1957,49 +1916,43 @@ post_root(POST_ARGS) mdoc->meta.vol = mandoc_strdup("LOCAL"); if (mdoc->meta.os == NULL) { - mandoc_msg(MANDOCERR_OS_MISSING, - mdoc->parse, 0, 0, NULL); + mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL); mdoc->meta.os = mandoc_strdup(""); } else if (mdoc->meta.os_e && (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0) - mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0, + mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0, mdoc->meta.os_e == MANDOC_OS_OPENBSD ? "(OpenBSD)" : "(NetBSD)"); if (mdoc->meta.arch != NULL && - (arch = arches[mdoc->meta.os_e]) != NULL) { - while (*arch != NULL && strcmp(*arch, mdoc->meta.arch)) - arch++; - if (*arch == NULL) { - n = mdoc->first->child; - while (n->tok != MDOC_Dt || - n->child == NULL || - n->child->next == NULL || - n->child->next->next == NULL) - n = n->next; - n = n->child->next->next; - mandoc_vmsg(MANDOCERR_ARCH_BAD, - mdoc->parse, n->line, n->pos, - "Dt ... %s %s", mdoc->meta.arch, - mdoc->meta.os_e == MANDOC_OS_OPENBSD ? - "(OpenBSD)" : "(NetBSD)"); - } + arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) { + n = mdoc->meta.first->child; + while (n->tok != MDOC_Dt || + n->child == NULL || + n->child->next == NULL || + n->child->next->next == NULL) + n = n->next; + n = n->child->next->next; + mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos, + "Dt ... %s %s", mdoc->meta.arch, + mdoc->meta.os_e == MANDOC_OS_OPENBSD ? + "(OpenBSD)" : "(NetBSD)"); } /* Check that we begin with a proper `Sh'. */ - n = mdoc->first->child; + n = mdoc->meta.first->child; while (n != NULL && (n->type == ROFFT_COMMENT || (n->tok >= MDOC_Dd && - mdoc_macros[n->tok].flags & MDOC_PROLOGUE))) + mdoc_macro(n->tok)->flags & MDOC_PROLOGUE))) n = n->next; if (n == NULL) - mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL); + mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL); else if (n->tok != MDOC_Sh) - mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse, - n->line, n->pos, roff_name[n->tok]); + mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos, + "%s", roff_name[n->tok]); } static void @@ -2014,8 +1967,7 @@ post_rs(POST_ARGS) return; if (np->child == NULL) { - mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse, - np->line, np->pos, "Rs"); + mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs"); return; } @@ -2033,8 +1985,8 @@ post_rs(POST_ARGS) break; if (i == RSORD_MAX) { - mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse, - nch->line, nch->pos, roff_name[nch->tok]); + mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos, + "%s", roff_name[nch->tok]); i = -1; } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) np->norm->Rs.quote_T++; @@ -2123,8 +2075,7 @@ post_ns(POST_ARGS) n = mdoc->last; if (n->flags & NODE_LINE || (n->next != NULL && n->next->flags & NODE_DELIMC)) - mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse, - n->line, n->pos, NULL); + mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL); } static void @@ -2176,8 +2127,8 @@ post_sh_name(POST_ARGS) switch (n->tok) { case MDOC_Nm: if (hasnm && n->child != NULL) - mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT, - mdoc->parse, n->line, n->pos, + mandoc_msg(MANDOCERR_NAMESEC_PUNCT, + n->line, n->pos, "Nm %s", n->child->string); hasnm = 1; continue; @@ -2185,7 +2136,7 @@ post_sh_name(POST_ARGS) hasnd = 1; if (n->next != NULL) mandoc_msg(MANDOCERR_NAMESEC_ND, - mdoc->parse, n->line, n->pos, NULL); + n->line, n->pos, NULL); break; case TOKEN_NONE: if (n->type == ROFFT_TEXT && @@ -2196,18 +2147,18 @@ post_sh_name(POST_ARGS) } /* FALLTHROUGH */ default: - mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, - n->line, n->pos, roff_name[n->tok]); + mandoc_msg(MANDOCERR_NAMESEC_BAD, + n->line, n->pos, "%s", roff_name[n->tok]); continue; } break; } if ( ! hasnm) - mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse, + mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->last->line, mdoc->last->pos, NULL); if ( ! hasnd) - mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse, + mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->last->line, mdoc->last->pos, NULL); } @@ -2233,21 +2184,18 @@ post_sh_see_also(POST_ARGS) sec = n->child->next->string; if (lastsec != NULL) { if (lastpunct[0] != ',' || lastpunct[1] != '\0') - mandoc_vmsg(MANDOCERR_XR_PUNCT, - mdoc->parse, n->line, n->pos, - "%s before %s(%s)", lastpunct, - name, sec); + mandoc_msg(MANDOCERR_XR_PUNCT, n->line, + n->pos, "%s before %s(%s)", + lastpunct, name, sec); cmp = strcmp(lastsec, sec); if (cmp > 0) - mandoc_vmsg(MANDOCERR_XR_ORDER, - mdoc->parse, n->line, n->pos, - "%s(%s) after %s(%s)", name, - sec, lastname, lastsec); + mandoc_msg(MANDOCERR_XR_ORDER, n->line, + n->pos, "%s(%s) after %s(%s)", + name, sec, lastname, lastsec); else if (cmp == 0 && strcasecmp(lastname, name) > 0) - mandoc_vmsg(MANDOCERR_XR_ORDER, - mdoc->parse, n->line, n->pos, - "%s after %s", name, lastname); + mandoc_msg(MANDOCERR_XR_ORDER, n->line, + n->pos, "%s after %s", name, lastname); } lastname = name; lastsec = sec; @@ -2268,8 +2216,8 @@ post_sh_see_also(POST_ARGS) return; lastpunct = n->string; if (n->next == NULL || n->next->tok == MDOC_Rs) - mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse, - n->line, n->pos, "%s after %s(%s)", + mandoc_msg(MANDOCERR_XR_PUNCT, n->line, + n->pos, "%s after %s(%s)", lastpunct, lastname, lastsec); n = n->next; } @@ -2290,7 +2238,7 @@ post_sh_authors(POST_ARGS) { if ( ! child_an(mdoc->last)) - mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse, + mandoc_msg(MANDOCERR_AN_MISSING, mdoc->last->line, mdoc->last->pos, NULL); } @@ -2356,7 +2304,7 @@ post_sh_head(POST_ARGS) /* The NAME should be first. */ if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) - mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse, + mandoc_msg(MANDOCERR_NAMESEC_FIRST, mdoc->last->line, mdoc->last->pos, "Sh %s", sec != SEC_CUSTOM ? secnames[sec] : (nch = mdoc->last->child) == NULL ? "" : @@ -2393,9 +2341,8 @@ post_sh_head(POST_ARGS) } } if (goodsec != NULL) - mandoc_vmsg(MANDOCERR_SEC_TYPO, mdoc->parse, - nch->line, nch->pos, "Sh %s instead of %s", - nch->string, goodsec); + mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos, + "Sh %s instead of %s", nch->string, goodsec); return; } @@ -2405,14 +2352,12 @@ post_sh_head(POST_ARGS) */ if (sec == mdoc->lastnamed) - mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse, - mdoc->last->line, mdoc->last->pos, - "Sh %s", secnames[sec]); + mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line, + mdoc->last->pos, "Sh %s", secnames[sec]); if (sec < mdoc->lastnamed) - mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse, - mdoc->last->line, mdoc->last->pos, - "Sh %s", secnames[sec]); + mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line, + mdoc->last->pos, "Sh %s", secnames[sec]); /* Mark the last named section. */ @@ -2446,7 +2391,7 @@ post_sh_head(POST_ARGS) break; if (NULL == goodsec) goodsec = "9"; - mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse, + mandoc_msg(MANDOCERR_SEC_MSEC, mdoc->last->line, mdoc->last->pos, "Sh %s for %s only", secnames[sec], goodsec); break; @@ -2463,13 +2408,13 @@ post_xr(POST_ARGS) n = mdoc->last; nch = n->child; if (nch->next == NULL) { - mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse, + mandoc_msg(MANDOCERR_XR_NOSEC, n->line, n->pos, "Xr %s", nch->string); } else { assert(nch->next == n->last); if(mandoc_xr_add(nch->next->string, nch->string, nch->line, nch->pos)) - mandoc_vmsg(MANDOCERR_XR_SELF, mdoc->parse, + mandoc_msg(MANDOCERR_XR_SELF, nch->line, nch->pos, "Xr %s %s", nch->string, nch->next->string); } @@ -2496,19 +2441,18 @@ post_ignpar(POST_ARGS) } if ((np = mdoc->last->child) != NULL) - if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { - mandoc_vmsg(MANDOCERR_PAR_SKIP, - mdoc->parse, np->line, np->pos, + if (np->tok == MDOC_Pp || + np->tok == ROFF_br || np->tok == ROFF_sp) { + mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, "%s after %s", roff_name[np->tok], roff_name[mdoc->last->tok]); roff_node_delete(mdoc, np); } if ((np = mdoc->last->last) != NULL) - if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { - mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, - np->line, np->pos, "%s at the end of %s", - roff_name[np->tok], + if (np->tok == MDOC_Pp || np->tok == ROFF_br) { + mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, + "%s at the end of %s", roff_name[np->tok], roff_name[mdoc->last->tok]); roff_node_delete(mdoc, np); } @@ -2526,13 +2470,11 @@ post_prevpar(POST_ARGS) return; /* - * Don't allow prior `Lp' or `Pp' prior to a paragraph-type - * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. + * Don't allow `Pp' prior to a paragraph-type + * block: `Pp' or non-compact `Bd' or `Bl'. */ - if (n->prev->tok != MDOC_Pp && - n->prev->tok != MDOC_Lp && - n->prev->tok != ROFF_br) + if (n->prev->tok != MDOC_Pp && n->prev->tok != ROFF_br) return; if (n->tok == MDOC_Bl && n->norm->Bl.comp) return; @@ -2541,9 +2483,8 @@ post_prevpar(POST_ARGS) if (n->tok == MDOC_It && n->parent->norm->Bl.comp) return; - mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, - n->prev->line, n->prev->pos, "%s before %s", - roff_name[n->prev->tok], roff_name[n->tok]); + mandoc_msg(MANDOCERR_PAR_SKIP, n->prev->line, n->prev->pos, + "%s before %s", roff_name[n->prev->tok], roff_name[n->tok]); roff_node_delete(mdoc, n->prev); } @@ -2552,33 +2493,12 @@ post_par(POST_ARGS) { struct roff_node *np; - np = mdoc->last; - if (np->tok != ROFF_br && np->tok != ROFF_sp) - post_prevpar(mdoc); - - if (np->tok == ROFF_sp) { - if (np->child != NULL && np->child->next != NULL) - mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, - np->child->next->line, np->child->next->pos, - "sp ... %s", np->child->next->string); - } else if (np->child != NULL) - mandoc_vmsg(MANDOCERR_ARG_SKIP, - mdoc->parse, np->line, np->pos, "%s %s", - roff_name[np->tok], np->child->string); - - if ((np = mdoc->last->prev) == NULL) { - np = mdoc->last->parent; - if (np->tok != MDOC_Sh && np->tok != MDOC_Ss) - return; - } else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp && - (mdoc->last->tok != ROFF_br || - (np->tok != ROFF_sp && np->tok != ROFF_br))) - return; + post_prevpar(mdoc); - mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, - mdoc->last->line, mdoc->last->pos, "%s after %s", - roff_name[mdoc->last->tok], roff_name[np->tok]); - roff_node_delete(mdoc, mdoc->last); + np = mdoc->last; + if (np->child != NULL) + mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos, + "%s %s", roff_name[np->tok], np->child->string); } static void @@ -2591,17 +2511,15 @@ post_dd(POST_ARGS) n->flags |= NODE_NOPRT; if (mdoc->meta.date != NULL) { - mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, - n->line, n->pos, "Dd"); + mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd"); free(mdoc->meta.date); } else if (mdoc->flags & MDOC_PBODY) - mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, - n->line, n->pos, "Dd"); + mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd"); else if (mdoc->meta.title != NULL) - mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, + mandoc_msg(MANDOCERR_PROLOG_ORDER, n->line, n->pos, "Dd after Dt"); else if (mdoc->meta.os != NULL) - mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, + mandoc_msg(MANDOCERR_PROLOG_ORDER, n->line, n->pos, "Dd after Os"); if (n->child == NULL || n->child->string[0] == '\0') { @@ -2632,16 +2550,14 @@ post_dt(POST_ARGS) n->flags |= NODE_NOPRT; if (mdoc->flags & MDOC_PBODY) { - mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse, - n->line, n->pos, "Dt"); + mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt"); return; } if (mdoc->meta.title != NULL) - mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, - n->line, n->pos, "Dt"); + mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt"); else if (mdoc->meta.os != NULL) - mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, + mandoc_msg(MANDOCERR_PROLOG_ORDER, n->line, n->pos, "Dt after Os"); free(mdoc->meta.title); @@ -2658,8 +2574,7 @@ post_dt(POST_ARGS) nn = n->child; if (nn == NULL || *nn->string == '\0') { - mandoc_msg(MANDOCERR_DT_NOTITLE, - mdoc->parse, n->line, n->pos, "Dt"); + mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt"); mdoc->meta.title = mandoc_strdup("UNTITLED"); } else { mdoc->meta.title = mandoc_strdup(nn->string); @@ -2668,9 +2583,8 @@ post_dt(POST_ARGS) for (p = nn->string; *p != '\0'; p++) if (islower((unsigned char)*p)) { - mandoc_vmsg(MANDOCERR_TITLE_CASE, - mdoc->parse, nn->line, - nn->pos + (p - nn->string), + mandoc_msg(MANDOCERR_TITLE_CASE, nn->line, + nn->pos + (int)(p - nn->string), "Dt %s", nn->string); break; } @@ -2682,8 +2596,7 @@ post_dt(POST_ARGS) nn = nn->next; if (nn == NULL) { - mandoc_vmsg(MANDOCERR_MSEC_MISSING, - mdoc->parse, n->line, n->pos, + mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos, "Dt %s", mdoc->meta.title); mdoc->meta.vol = mandoc_strdup("LOCAL"); return; /* msec and arch remain NULL. */ @@ -2695,7 +2608,7 @@ post_dt(POST_ARGS) cp = mandoc_a2msec(nn->string); if (cp == NULL) { - mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse, + mandoc_msg(MANDOCERR_MSEC_BAD, nn->line, nn->pos, "Dt ... %s", nn->string); mdoc->meta.vol = mandoc_strdup(nn->string); } else @@ -2713,7 +2626,7 @@ post_dt(POST_ARGS) /* Ignore fourth and later arguments. */ if ((nn = nn->next) != NULL) - mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, nn->line, nn->pos, "Dt ... %s", nn->string); } @@ -2734,8 +2647,8 @@ post_bx(POST_ARGS) !strcmp(nch->string, "Free") ? "Fx" : !strcmp(nch->string, "DragonFly") ? "Dx" : NULL; if (macro != NULL) - mandoc_msg(MANDOCERR_BX, mdoc->parse, - n->line, n->pos, macro); + mandoc_msg(MANDOCERR_BX, + n->line, n->pos, "%s", macro); mdoc->last = nch; nch = nch->next; mdoc->next = ROFF_NEXT_SIBLING; @@ -2783,11 +2696,9 @@ post_os(POST_ARGS) n->flags |= NODE_NOPRT; if (mdoc->meta.os != NULL) - mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, - n->line, n->pos, "Os"); + mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os"); else if (mdoc->flags & MDOC_PBODY) - mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, - n->line, n->pos, "Os"); + mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os"); post_delim(mdoc); @@ -2816,8 +2727,7 @@ post_os(POST_ARGS) #else /*!OSNAME */ if (defbuf == NULL) { if (uname(&utsname) == -1) { - mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse, - n->line, n->pos, "Os"); + mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os"); defbuf = mandoc_strdup("UNKNOWN"); } else mandoc_asprintf(&defbuf, "%s %s", @@ -2841,8 +2751,7 @@ out: */ if (n->child != NULL) - mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse, - n->child->line, n->child->pos, + mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos, "Os %s (%s)", n->child->string, mdoc->meta.os_e == MANDOC_OS_OPENBSD ? "OpenBSD" : "NetBSD"); @@ -2854,14 +2763,12 @@ out: return; if (strncmp(n->string, "$" "Mdocdate", 9)) { if (mdoc->meta.os_e == MANDOC_OS_OPENBSD) - mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING, - mdoc->parse, n->line, n->pos, - "Dd %s (OpenBSD)", n->string); + mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line, + n->pos, "Dd %s (OpenBSD)", n->string); } else { if (mdoc->meta.os_e == MANDOC_OS_NETBSD) - mandoc_vmsg(MANDOCERR_MDOCDATE, - mdoc->parse, n->line, n->pos, - "Dd %s (NetBSD)", n->string); + mandoc_msg(MANDOCERR_MDOCDATE, n->line, + n->pos, "Dd %s (NetBSD)", n->string); } } diff --git a/usr/src/cmd/mandoc/msec.c b/usr/src/cmd/mandoc/msec.c index 9d41511696..9a52213d95 100644 --- a/usr/src/cmd/mandoc/msec.c +++ b/usr/src/cmd/mandoc/msec.c @@ -1,4 +1,4 @@ -/* $Id: msec.c,v 1.15 2015/10/06 18:32:19 schwarze Exp $ */ +/* $Id: msec.c,v 1.16 2018/12/14 01:18:26 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -18,6 +18,7 @@ #include <sys/types.h> +#include <stdio.h> #include <string.h> #include "mandoc.h" diff --git a/usr/src/cmd/mandoc/out.c b/usr/src/cmd/mandoc/out.c index b2b643787e..d0b0d0a2ac 100644 --- a/usr/src/cmd/mandoc/out.c +++ b/usr/src/cmd/mandoc/out.c @@ -1,7 +1,7 @@ -/* $Id: out.c,v 1.70 2017/06/27 18:25:02 schwarze Exp $ */ +/* $Id: out.c,v 1.78 2019/03/29 21:27:06 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2014,2015,2017,2018 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 @@ -20,21 +20,29 @@ #include <sys/types.h> #include <assert.h> +#include <ctype.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "mandoc_aux.h" -#include "mandoc.h" +#include "tbl.h" #include "out.h" -static void tblcalc_data(struct rofftbl *, struct roffcol *, +struct tbl_colgroup { + struct tbl_colgroup *next; + size_t wanted; + int startcol; + int endcol; +}; + +static size_t tblcalc_data(struct rofftbl *, struct roffcol *, const struct tbl_opts *, const struct tbl_dat *, size_t); -static void tblcalc_literal(struct rofftbl *, struct roffcol *, +static size_t tblcalc_literal(struct rofftbl *, struct roffcol *, const struct tbl_dat *, size_t); -static void tblcalc_number(struct rofftbl *, struct roffcol *, +static size_t tblcalc_number(struct rofftbl *, struct roffcol *, const struct tbl_opts *, const struct tbl_dat *); @@ -103,16 +111,18 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) * used for the actual width calculations. */ void -tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, +tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, size_t offset, size_t rmargin) { struct roffsu su; const struct tbl_opts *opts; + const struct tbl_span *sp; const struct tbl_dat *dp; struct roffcol *col; - size_t ewidth, xwidth; - int spans; - int icol, maxcol, necol, nxcol, quirkcol; + struct tbl_colgroup *first_group, **gp, *g; + size_t *colwidth; + size_t ewidth, min1, min2, wanted, width, xwidth; + int done, icol, maxcol, necol, nxcol, quirkcol; /* * Allocate the master column specifiers. These will hold the @@ -120,33 +130,34 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, * must be freed and nullified by the caller. */ - assert(NULL == tbl->cols); - tbl->cols = mandoc_calloc((size_t)sp->opts->cols, + assert(tbl->cols == NULL); + tbl->cols = mandoc_calloc((size_t)sp_first->opts->cols, sizeof(struct roffcol)); - opts = sp->opts; + opts = sp_first->opts; - for (maxcol = -1; sp; sp = sp->next) { - if (TBL_SPAN_DATA != sp->pos) + maxcol = -1; + first_group = NULL; + for (sp = sp_first; sp != NULL; sp = sp->next) { + if (sp->pos != TBL_SPAN_DATA) continue; - spans = 1; + /* * Account for the data cells in the layout, matching it * to data cells in the data section. */ - for (dp = sp->first; dp; dp = dp->next) { - /* Do not used spanned cells in the calculation. */ - if (0 < --spans) - continue; - spans = dp->spans; - if (1 < spans) - continue; + + gp = &first_group; + for (dp = sp->first; dp != NULL; dp = dp->next) { icol = dp->layout->col; - while (maxcol < icol) + while (maxcol < icol + dp->hspans) tbl->cols[++maxcol].spacing = SIZE_MAX; col = tbl->cols + icol; col->flags |= dp->layout->flags; if (dp->layout->flags & TBL_CELL_WIGN) continue; + + /* Handle explicit width specifications. */ + if (dp->layout->wstr != NULL && dp->layout->width == 0 && a2roffsu(dp->layout->wstr, &su, SCALE_EN) @@ -159,15 +170,165 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, (col->spacing == SIZE_MAX || col->spacing < dp->layout->spacing)) col->spacing = dp->layout->spacing; - tblcalc_data(tbl, col, opts, dp, + + /* + * Calculate an automatic width. + * Except for spanning cells, apply it. + */ + + width = tblcalc_data(tbl, + dp->hspans == 0 ? col : NULL, + opts, dp, dp->block == 0 ? 0 : dp->layout->width ? dp->layout->width : rmargin ? (rmargin + sp->opts->cols / 2) / (sp->opts->cols + 1) : 0); + if (dp->hspans == 0) + continue; + + /* + * Build an ordered, singly linked list + * of all groups of columns joined by spans, + * recording the minimum width for each group. + */ + + while (*gp != NULL && ((*gp)->startcol < icol || + (*gp)->endcol < icol + dp->hspans)) + gp = &(*gp)->next; + if (*gp == NULL || (*gp)->startcol > icol || + (*gp)->endcol > icol + dp->hspans) { + g = mandoc_malloc(sizeof(*g)); + g->next = *gp; + g->wanted = width; + g->startcol = icol; + g->endcol = icol + dp->hspans; + *gp = g; + } else if ((*gp)->wanted < width) + (*gp)->wanted = width; + } + } + + /* + * Column spacings are needed for span width calculations, + * so set the default values now. + */ + + for (icol = 0; icol <= maxcol; icol++) + if (tbl->cols[icol].spacing == SIZE_MAX || icol == maxcol) + tbl->cols[icol].spacing = 3; + + /* + * Replace the minimum widths with the missing widths, + * and dismiss groups that are already wide enough. + */ + + gp = &first_group; + while ((g = *gp) != NULL) { + done = 0; + for (icol = g->startcol; icol <= g->endcol; icol++) { + width = tbl->cols[icol].width; + if (icol < g->endcol) + width += tbl->cols[icol].spacing; + if (g->wanted <= width) { + done = 1; + break; + } else + (*gp)->wanted -= width; + } + if (done) { + *gp = g->next; + free(g); + } else + gp = &(*gp)->next; + } + + colwidth = mandoc_reallocarray(NULL, maxcol + 1, sizeof(*colwidth)); + while (first_group != NULL) { + + /* + * Rebuild the array of the widths of all columns + * participating in spans that require expansion. + */ + + for (icol = 0; icol <= maxcol; icol++) + colwidth[icol] = SIZE_MAX; + for (g = first_group; g != NULL; g = g->next) + for (icol = g->startcol; icol <= g->endcol; icol++) + colwidth[icol] = tbl->cols[icol].width; + + /* + * Find the smallest and second smallest column width + * among the columns which may need expamsion. + */ + + min1 = min2 = SIZE_MAX; + for (icol = 0; icol <= maxcol; icol++) { + if (min1 > colwidth[icol]) { + min2 = min1; + min1 = colwidth[icol]; + } else if (min1 < colwidth[icol] && + min2 > colwidth[icol]) + min2 = colwidth[icol]; + } + + /* + * Find the minimum wanted width + * for any one of the narrowest columns, + * and mark the columns wanting that width. + */ + + wanted = min2; + for (g = first_group; g != NULL; g = g->next) { + necol = 0; + for (icol = g->startcol; icol <= g->endcol; icol++) + if (tbl->cols[icol].width == min1) + necol++; + if (necol == 0) + continue; + width = min1 + (g->wanted - 1) / necol + 1; + if (width > min2) + width = min2; + if (wanted > width) + wanted = width; + for (icol = g->startcol; icol <= g->endcol; icol++) + if (colwidth[icol] == min1 || + (colwidth[icol] < min2 && + colwidth[icol] > width)) + colwidth[icol] = width; + } + + /* Record the effect of the widening on the group list. */ + + gp = &first_group; + while ((g = *gp) != NULL) { + done = 0; + for (icol = g->startcol; icol <= g->endcol; icol++) { + if (colwidth[icol] != wanted || + tbl->cols[icol].width == wanted) + continue; + if (g->wanted <= wanted - min1) { + done = 1; + break; + } + g->wanted -= wanted - min1; + } + if (done) { + *gp = g->next; + free(g); + } else + gp = &(*gp)->next; } + + /* Record the effect of the widening on the columns. */ + + for (icol = 0; icol <= maxcol; icol++) + if (colwidth[icol] == wanted) + tbl->cols[icol].width = wanted; } + free(colwidth); /* + * Align numbers with text. * Count columns to equalize and columns to maximize. * Find maximum width of the columns to equalize. * Find total width of the columns *not* to maximize. @@ -177,8 +338,10 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, ewidth = xwidth = 0; for (icol = 0; icol <= maxcol; icol++) { col = tbl->cols + icol; - if (col->spacing == SIZE_MAX || icol == maxcol) - col->spacing = 3; + if (col->width > col->nwidth) + col->decimal += (col->width - col->nwidth) / 2; + else + col->width = col->nwidth; if (col->flags & TBL_CELL_EQUAL) { necol++; if (ewidth < col->width) @@ -251,7 +414,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, } } -static void +static size_t tblcalc_data(struct rofftbl *tbl, struct roffcol *col, const struct tbl_opts *opts, const struct tbl_dat *dp, size_t mw) { @@ -263,26 +426,24 @@ tblcalc_data(struct rofftbl *tbl, struct roffcol *col, case TBL_CELL_HORIZ: case TBL_CELL_DHORIZ: sz = (*tbl->len)(1, tbl->arg); - if (col->width < sz) + if (col != NULL && col->width < sz) col->width = sz; - break; + return sz; case TBL_CELL_LONG: case TBL_CELL_CENTRE: case TBL_CELL_LEFT: case TBL_CELL_RIGHT: - tblcalc_literal(tbl, col, dp, mw); - break; + return tblcalc_literal(tbl, col, dp, mw); case TBL_CELL_NUMBER: - tblcalc_number(tbl, col, opts, dp); - break; + return tblcalc_number(tbl, col, opts, dp); case TBL_CELL_DOWN: - break; + return 0; default: abort(); } } -static void +static size_t tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, const struct tbl_dat *dp, size_t mw) { @@ -291,11 +452,12 @@ tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, char *end; /* End of the current line. */ size_t lsz; /* Length of the current line. */ size_t wsz; /* Length of the current word. */ + size_t msz; /* Length of the longest line. */ if (dp->string == NULL || *dp->string == '\0') - return; + return 0; str = mw ? mandoc_strdup(dp->string) : dp->string; - lsz = 0; + msz = lsz = 0; for (beg = str; beg != NULL && *beg != '\0'; beg = end) { end = mw ? strchr(beg, ' ') : NULL; if (end != NULL) { @@ -308,62 +470,84 @@ tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, lsz += 1 + wsz; else lsz = wsz; - if (col->width < lsz) - col->width = lsz; + if (msz < lsz) + msz = lsz; } if (mw) free((void *)str); + if (col != NULL && col->width < msz) + col->width = msz; + return msz; } -static void +static size_t tblcalc_number(struct rofftbl *tbl, struct roffcol *col, const struct tbl_opts *opts, const struct tbl_dat *dp) { - int i; - size_t sz, psz, ssz, d; - const char *str; - char *cp; + const char *cp, *lastdigit, *lastpoint; + size_t intsz, totsz; char buf[2]; + if (dp->string == NULL || *dp->string == '\0') + return 0; + + totsz = (*tbl->slen)(dp->string, tbl->arg); + if (col == NULL) + return totsz; + /* - * First calculate number width and decimal place (last + 1 for - * non-decimal numbers). If the stored decimal is subsequent to - * ours, make our size longer by that difference - * (right-"shifting"); similarly, if ours is subsequent the - * stored, then extend the stored size by the difference. - * Finally, re-assign the stored values. + * Find the last digit and + * the last decimal point that is adjacent to a digit. + * The alignment indicator "\&" overrides everything. */ - str = dp->string ? dp->string : ""; - sz = (*tbl->slen)(str, tbl->arg); + lastdigit = lastpoint = NULL; + for (cp = dp->string; cp[0] != '\0'; cp++) { + if (cp[0] == '\\' && cp[1] == '&') { + lastdigit = lastpoint = cp; + break; + } else if (cp[0] == opts->decimal && + (isdigit((unsigned char)cp[1]) || + (cp > dp->string && isdigit((unsigned char)cp[-1])))) + lastpoint = cp; + else if (isdigit((unsigned char)cp[0])) + lastdigit = cp; + } - /* FIXME: TBL_DATA_HORIZ et al.? */ + /* Not a number, treat as a literal string. */ - buf[0] = opts->decimal; - buf[1] = '\0'; + if (lastdigit == NULL) { + if (col != NULL && col->width < totsz) + col->width = totsz; + return totsz; + } - psz = (*tbl->slen)(buf, tbl->arg); + /* Measure the width of the integer part. */ - if (NULL != (cp = strrchr(str, opts->decimal))) { - buf[1] = '\0'; - for (ssz = 0, i = 0; cp != &str[i]; i++) { - buf[0] = str[i]; - ssz += (*tbl->slen)(buf, tbl->arg); - } - d = ssz + psz; - } else - d = sz + psz; + if (lastpoint == NULL) + lastpoint = lastdigit + 1; + intsz = 0; + buf[1] = '\0'; + for (cp = dp->string; cp < lastpoint; cp++) { + buf[0] = cp[0]; + intsz += (*tbl->slen)(buf, tbl->arg); + } - /* Adjust the settings for this column. */ + /* + * If this number has more integer digits than all numbers + * seen on earlier lines, shift them all to the right. + * If it has fewer, shift this number to the right. + */ - if (col->decimal > d) { - sz += col->decimal - d; - d = col->decimal; + if (intsz > col->decimal) { + col->nwidth += intsz - col->decimal; + col->decimal = intsz; } else - col->width += d - col->decimal; + totsz += col->decimal - intsz; + + /* Update the maximum total width seen so far. */ - if (sz > col->width) - col->width = sz; - if (d > col->decimal) - col->decimal = d; + if (totsz > col->nwidth) + col->nwidth = totsz; + return totsz; } diff --git a/usr/src/cmd/mandoc/out.h b/usr/src/cmd/mandoc/out.h index 9f0a541d5d..dec6a8f873 100644 --- a/usr/src/cmd/mandoc/out.h +++ b/usr/src/cmd/mandoc/out.h @@ -1,7 +1,7 @@ -/* $Id: out.h,v 1.32 2018/06/25 16:54:59 schwarze Exp $ */ +/* $Id: out.h,v 1.33 2018/08/18 20:18:14 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2017, 2018 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 @@ -32,6 +32,7 @@ enum roffscale { struct roffcol { size_t width; /* width of cell */ + size_t nwidth; /* max. width of number in cell */ size_t decimal; /* decimal position in cell */ size_t spacing; /* spacing after the column */ int flags; /* layout flags, see tbl_cell */ diff --git a/usr/src/cmd/mandoc/preconv.c b/usr/src/cmd/mandoc/preconv.c index 08f8df86a6..9ed627d446 100644 --- a/usr/src/cmd/mandoc/preconv.c +++ b/usr/src/cmd/mandoc/preconv.c @@ -1,4 +1,4 @@ -/* $Id: preconv.c,v 1.16 2017/02/18 13:43:52 schwarze Exp $ */ +/* $Id: preconv.c,v 1.17 2018/12/13 11:55:47 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org> @@ -22,7 +22,10 @@ #include <assert.h> #include <stdio.h> #include <string.h> + #include "mandoc.h" +#include "roff.h" +#include "mandoc_parse.h" #include "libmandoc.h" int diff --git a/usr/src/cmd/mandoc/read.c b/usr/src/cmd/mandoc/read.c index 0a583445f2..0f25ab967c 100644 --- a/usr/src/cmd/mandoc/read.c +++ b/usr/src/cmd/mandoc/read.c @@ -1,7 +1,7 @@ -/* $Id: read.c,v 1.196 2018/07/28 18:34:15 schwarze Exp $ */ +/* $Id: read.c,v 1.213 2019/06/03 19:58:02 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -38,21 +38,19 @@ #include "roff.h" #include "mdoc.h" #include "man.h" +#include "mandoc_parse.h" #include "libmandoc.h" +#include "roff_int.h" #define REPARSE_LIMIT 1000 struct mparse { struct roff *roff; /* roff parser (!NULL) */ struct roff_man *man; /* man parser */ - char *sodest; /* filename pointed to by .so */ - const char *file; /* filename of current input file */ struct buf *primary; /* buffer currently being parsed */ - struct buf *secondary; /* preprocessed copy of input */ + struct buf *secondary; /* copy of top level input */ + struct buf *loop; /* open .while request line */ const char *os_s; /* default operating system */ - mandocmsg mmsg; /* warning/error message handler */ - enum mandoclevel file_status; /* status of current parse */ - enum mandocerr mmin; /* ignore messages below this */ int options; /* parser options */ int gzip; /* current input file is gzipped */ int filenc; /* encoding of the current file */ @@ -61,220 +59,11 @@ struct mparse { }; static void choose_parser(struct mparse *); +static void free_buf_list(struct buf *); static void resize_buf(struct buf *, size_t); static int mparse_buf_r(struct mparse *, struct buf, size_t, int); -static int read_whole_file(struct mparse *, const char *, int, - struct buf *, int *); +static int read_whole_file(struct mparse *, int, struct buf *, int *); static void mparse_end(struct mparse *); -static void mparse_parse_buffer(struct mparse *, struct buf, - const char *); - -static const enum mandocerr mandoclimits[MANDOCLEVEL_MAX] = { - MANDOCERR_OK, - MANDOCERR_OK, - MANDOCERR_WARNING, - MANDOCERR_ERROR, - MANDOCERR_UNSUPP, - MANDOCERR_MAX, - MANDOCERR_MAX -}; - -static const char * const mandocerrs[MANDOCERR_MAX] = { - "ok", - - "base system convention", - - "Mdocdate found", - "Mdocdate missing", - "unknown architecture", - "operating system explicitly specified", - "RCS id missing", - "referenced manual not found", - - "generic style suggestion", - - "legacy man(7) date format", - "normalizing date format to", - "lower case character in document title", - "duplicate RCS id", - "possible typo in section name", - "unterminated quoted argument", - "useless macro", - "consider using OS macro", - "errnos out of order", - "duplicate errno", - "trailing delimiter", - "no blank before trailing delimiter", - "fill mode already enabled, skipping", - "fill mode already disabled, skipping", - "verbatim \"--\", maybe consider using \\(em", - "function name without markup", - "whitespace at end of input line", - "bad comment style", - - "generic warning", - - /* related to the prologue */ - "missing manual title, using UNTITLED", - "missing manual title, using \"\"", - "missing manual section, using \"\"", - "unknown manual section", - "missing date, using today's date", - "cannot parse date, using it verbatim", - "date in the future, using it anyway", - "missing Os macro, using \"\"", - "late prologue macro", - "prologue macros out of order", - - /* related to document structure */ - ".so is fragile, better use ln(1)", - "no document body", - "content before first section header", - "first section is not \"NAME\"", - "NAME section without Nm before Nd", - "NAME section without description", - "description not at the end of NAME", - "bad NAME section content", - "missing comma before name", - "missing description line, using \"\"", - "description line outside NAME section", - "sections out of conventional order", - "duplicate section title", - "unexpected section", - "cross reference to self", - "unusual Xr order", - "unusual Xr punctuation", - "AUTHORS section without An macro", - - /* related to macros and nesting */ - "obsolete macro", - "macro neither callable nor escaped", - "skipping paragraph macro", - "moving paragraph macro out of list", - "skipping no-space macro", - "blocks badly nested", - "nested displays are not portable", - "moving content out of list", - "first macro on line", - "line scope broken", - "skipping blank line in line scope", - - /* related to missing macro arguments */ - "skipping empty request", - "conditional request controls empty scope", - "skipping empty macro", - "empty block", - "empty argument, using 0n", - "missing display type, using -ragged", - "list type is not the first argument", - "missing -width in -tag list, using 6n", - "missing utility name, using \"\"", - "missing function name, using \"\"", - "empty head in list item", - "empty list item", - "missing argument, using next line", - "missing font type, using \\fR", - "unknown font type, using \\fR", - "nothing follows prefix", - "empty reference block", - "missing section argument", - "missing -std argument, adding it", - "missing option string, using \"\"", - "missing resource identifier, using \"\"", - "missing eqn box, using \"\"", - - /* related to bad macro arguments */ - "duplicate argument", - "skipping duplicate argument", - "skipping duplicate display type", - "skipping duplicate list type", - "skipping -width argument", - "wrong number of cells", - "unknown AT&T UNIX version", - "comma in function argument", - "parenthesis in function name", - "unknown library name", - "invalid content in Rs block", - "invalid Boolean argument", - "unknown font, skipping request", - "odd number of characters in request", - - /* related to plain text */ - "blank line in fill mode, using .sp", - "tab in filled text", - "new sentence, new line", - "invalid escape sequence", - "undefined string, using \"\"", - - /* related to tables */ - "tbl line starts with span", - "tbl column starts with span", - "skipping vertical bar in tbl layout", - - "generic error", - - /* related to tables */ - "non-alphabetic character in tbl options", - "skipping unknown tbl option", - "missing tbl option argument", - "wrong tbl option argument size", - "empty tbl layout", - "invalid character in tbl layout", - "unmatched parenthesis in tbl layout", - "tbl without any data cells", - "ignoring data in spanned tbl cell", - "ignoring extra tbl data cells", - "data block open at end of tbl", - - /* related to document structure and macros */ - NULL, - "duplicate prologue macro", - "skipping late title macro", - "input stack limit exceeded, infinite loop?", - "skipping bad character", - "skipping unknown macro", - "skipping insecure request", - "skipping item outside list", - "skipping column outside column list", - "skipping end of block that is not open", - "fewer RS blocks open, skipping", - "inserting missing end of block", - "appending missing end of block", - - /* related to request and macro arguments */ - "escaped character not allowed in a name", - "NOT IMPLEMENTED: Bd -file", - "skipping display without arguments", - "missing list type, using -item", - "argument is not numeric, using 1", - "missing manual name, using \"\"", - "uname(3) system call failed, using UNKNOWN", - "unknown standard specifier", - "skipping request without numeric argument", - "NOT IMPLEMENTED: .so with absolute path or \"..\"", - ".so request failed", - "skipping all arguments", - "skipping excess arguments", - "divide by zero", - - "unsupported feature", - "input too large", - "unsupported control character", - "unsupported roff request", - "eqn delim option in tbl", - "unsupported tbl layout modifier", - "ignoring macro in table", -}; - -static const char * const mandoclevels[MANDOCLEVEL_MAX] = { - "SUCCESS", - "STYLE", - "WARNING", - "ERROR", - "UNSUPP", - "BADARG", - "SYSERR" -}; static void @@ -286,6 +75,19 @@ resize_buf(struct buf *buf, size_t initial) } static void +free_buf_list(struct buf *buf) +{ + struct buf *tmp; + + while (buf != NULL) { + tmp = buf; + buf = tmp->next; + free(tmp->buf); + free(tmp); + } +} + +static void choose_parser(struct mparse *curp) { char *cp, *ep; @@ -320,15 +122,15 @@ choose_parser(struct mparse *curp) } if (format == MPARSE_MDOC) { - curp->man->macroset = MACROSET_MDOC; + curp->man->meta.macroset = MACROSET_MDOC; if (curp->man->mdocmac == NULL) curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); } else { - curp->man->macroset = MACROSET_MAN; + curp->man->meta.macroset = MACROSET_MAN; if (curp->man->manmac == NULL) curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); } - curp->man->first->tok = TOKEN_NONE; + curp->man->meta.first->tok = TOKEN_NONE; } /* @@ -342,24 +144,26 @@ static int mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) { struct buf ln; - const char *save_file; + struct buf *firstln, *lastln, *thisln, *loop; char *cp; size_t pos; /* byte number in the ln buffer */ - enum rofferr rr; + int line_result, result; int of; int lnn; /* line number in the real file */ int fd; + int inloop; /* Saw .while on this level. */ unsigned char c; - memset(&ln, 0, sizeof(ln)); - + ln.sz = 256; + ln.buf = mandoc_malloc(ln.sz); + ln.next = NULL; + firstln = lastln = loop = NULL; lnn = curp->line; pos = 0; + inloop = 0; + result = ROFF_CONT; - while (i < blk.sz) { - if (0 == pos && '\0' == blk.buf[i]) - break; - + while (i < blk.sz && (blk.buf[i] != '\0' || pos != 0)) { if (start) { curp->line = lnn; curp->reparse_count = 0; @@ -389,10 +193,10 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) /* * Make sure we have space for the worst - * case of 11 bytes: "\\[u10ffff]\0" + * case of 12 bytes: "\\[u10ffff]\n\0" */ - if (pos + 11 > ln.sz) + if (pos + 12 > ln.sz) resize_buf(&ln, 256); /* @@ -403,7 +207,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) if (c & 0x80) { if ( ! (curp->filenc && preconv_encode( &blk, &i, &ln, &pos, &curp->filenc))) { - mandoc_vmsg(MANDOCERR_CHAR_BAD, curp, + mandoc_msg(MANDOCERR_CHAR_BAD, curp->line, pos, "0x%x", c); ln.buf[pos++] = '?'; i++; @@ -416,10 +220,10 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) */ if (c == 0x7f || (c < 0x20 && c != 0x09)) { - mandoc_vmsg(c == 0x00 || c == 0x04 || + mandoc_msg(c == 0x00 || c == 0x04 || c > 0x0a ? MANDOCERR_CHAR_BAD : MANDOCERR_CHAR_UNSUPP, - curp, curp->line, pos, "0x%x", c); + curp->line, pos, "0x%x", c); i++; if (c != '\r') ln.buf[pos++] = '?'; @@ -428,13 +232,32 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) ln.buf[pos++] = blk.buf[i++]; } + ln.buf[pos] = '\0'; - if (pos + 1 >= ln.sz) - resize_buf(&ln, 256); + /* + * Maintain a lookaside buffer of all lines. + * parsed from this input source. + */ + + thisln = mandoc_malloc(sizeof(*thisln)); + thisln->buf = mandoc_strdup(ln.buf); + thisln->sz = strlen(ln.buf) + 1; + thisln->next = NULL; + if (firstln == NULL) { + firstln = lastln = thisln; + if (curp->secondary == NULL) + curp->secondary = firstln; + } else { + lastln->next = thisln; + lastln = thisln; + } - if (i == blk.sz || blk.buf[i] == '\0') + /* XXX Ugly hack to mark the end of the input. */ + + if (i == blk.sz || blk.buf[i] == '\0') { ln.buf[pos++] = '\n'; - ln.buf[pos] = '\0'; + ln.buf[pos] = '\0'; + } /* * A significant amount of complexity is contained by @@ -446,74 +269,112 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) */ of = 0; +rerun: + line_result = roff_parseln(curp->roff, curp->line, &ln, &of); - /* - * Maintain a lookaside buffer of all parsed lines. We - * only do this if mparse_keep() has been invoked (the - * buffer may be accessed with mparse_getkeep()). - */ + /* Process options. */ + + if (line_result & ROFF_APPEND) + assert(line_result == (ROFF_IGN | ROFF_APPEND)); - if (curp->secondary) { - curp->secondary->buf = mandoc_realloc( - curp->secondary->buf, - curp->secondary->sz + pos + 2); - memcpy(curp->secondary->buf + - curp->secondary->sz, - ln.buf, pos); - curp->secondary->sz += pos; - curp->secondary->buf - [curp->secondary->sz] = '\n'; - curp->secondary->sz++; - curp->secondary->buf - [curp->secondary->sz] = '\0'; + if (line_result & ROFF_USERCALL) + assert((line_result & ROFF_MASK) == ROFF_REPARSE); + + if (line_result & ROFF_USERRET) { + assert(line_result == (ROFF_IGN | ROFF_USERRET)); + if (start == 0) { + /* Return from the current macro. */ + result = ROFF_USERRET; + goto out; + } } -rerun: - rr = roff_parseln(curp->roff, curp->line, &ln, &of); - switch (rr) { - case ROFF_REPARSE: - if (++curp->reparse_count > REPARSE_LIMIT) - mandoc_msg(MANDOCERR_ROFFLOOP, curp, + switch (line_result & ROFF_LOOPMASK) { + case ROFF_IGN: + break; + case ROFF_WHILE: + if (curp->loop != NULL) { + if (loop == curp->loop) + break; + mandoc_msg(MANDOCERR_WHILE_NEST, curp->line, pos, NULL); - else if (mparse_buf_r(curp, ln, of, 0) == 1 || - start == 1) { - pos = 0; - continue; } - free(ln.buf); - return 0; - case ROFF_APPEND: - pos = strlen(ln.buf); - continue; + curp->loop = thisln; + loop = NULL; + inloop = 1; + break; + case ROFF_LOOPCONT: + case ROFF_LOOPEXIT: + if (curp->loop == NULL) { + mandoc_msg(MANDOCERR_WHILE_FAIL, + curp->line, pos, NULL); + break; + } + if (inloop == 0) { + mandoc_msg(MANDOCERR_WHILE_INTO, + curp->line, pos, NULL); + curp->loop = loop = NULL; + break; + } + if (line_result & ROFF_LOOPCONT) + loop = curp->loop; + else { + curp->loop = loop = NULL; + inloop = 0; + } + break; + default: + abort(); + } + + /* Process the main instruction from the roff parser. */ + + switch (line_result & ROFF_MASK) { + case ROFF_IGN: + break; + case ROFF_CONT: + if (curp->man->meta.macroset == MACROSET_NONE) + choose_parser(curp); + if ((curp->man->meta.macroset == MACROSET_MDOC ? + mdoc_parseln(curp->man, curp->line, ln.buf, of) : + man_parseln(curp->man, curp->line, ln.buf, of) + ) == 2) + goto out; + break; case ROFF_RERUN: goto rerun; - case ROFF_IGN: - pos = 0; - continue; + case ROFF_REPARSE: + if (++curp->reparse_count > REPARSE_LIMIT) { + /* Abort and return to the top level. */ + result = ROFF_IGN; + mandoc_msg(MANDOCERR_ROFFLOOP, + curp->line, pos, NULL); + goto out; + } + result = mparse_buf_r(curp, ln, of, 0); + if (line_result & ROFF_USERCALL) { + roff_userret(curp->roff); + /* Continue normally. */ + if (result & ROFF_USERRET) + result = ROFF_CONT; + } + if (start == 0 && result != ROFF_CONT) + goto out; + break; case ROFF_SO: if ( ! (curp->options & MPARSE_SO) && (i >= blk.sz || blk.buf[i] == '\0')) { - curp->sodest = mandoc_strdup(ln.buf + of); - free(ln.buf); - return 1; + curp->man->meta.sodest = + mandoc_strdup(ln.buf + of); + goto out; } - /* - * We remove `so' clauses from our lookaside - * buffer because we're going to descend into - * the file recursively. - */ - if (curp->secondary) - curp->secondary->sz -= pos + 1; - save_file = curp->file; if ((fd = mparse_open(curp, ln.buf + of)) != -1) { mparse_readfd(curp, fd, ln.buf + of); close(fd); - curp->file = save_file; } else { - curp->file = save_file; - mandoc_vmsg(MANDOCERR_SO_FAIL, - curp, curp->line, pos, - ".so %s", ln.buf + of); + mandoc_msg(MANDOCERR_SO_FAIL, + curp->line, of, ".so %s: %s", + ln.buf + of, strerror(errno)); ln.sz = mandoc_asprintf(&cp, ".sp\nSee the file %s.\n.sp", ln.buf + of); @@ -522,37 +383,44 @@ rerun: of = 0; mparse_buf_r(curp, ln, of, 0); } - pos = 0; - continue; - default: break; + default: + abort(); } - if (curp->man->macroset == MACROSET_NONE) - choose_parser(curp); - - if ((curp->man->macroset == MACROSET_MDOC ? - mdoc_parseln(curp->man, curp->line, ln.buf, of) : - man_parseln(curp->man, curp->line, ln.buf, of)) == 2) - break; - - /* Temporary buffers typically are not full. */ - - if (0 == start && '\0' == blk.buf[i]) - break; - /* Start the next input line. */ - pos = 0; - } + if (loop != NULL && + (line_result & ROFF_LOOPMASK) == ROFF_IGN) + loop = loop->next; + + if (loop != NULL) { + if ((line_result & ROFF_APPEND) == 0) + *ln.buf = '\0'; + if (ln.sz < loop->sz) + resize_buf(&ln, loop->sz); + (void)strlcat(ln.buf, loop->buf, ln.sz); + of = 0; + goto rerun; + } + pos = (line_result & ROFF_APPEND) ? strlen(ln.buf) : 0; + } +out: + if (inloop) { + if (result != ROFF_USERRET) + mandoc_msg(MANDOCERR_WHILE_OUTOF, + curp->line, pos, NULL); + curp->loop = NULL; + } free(ln.buf); - return 1; + if (firstln != curp->secondary) + free_buf_list(firstln); + return result; } static int -read_whole_file(struct mparse *curp, const char *file, int fd, - struct buf *fb, int *with_mmap) +read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) { struct stat st; gzFile gz; @@ -561,7 +429,7 @@ read_whole_file(struct mparse *curp, const char *file, int fd, int gzerrnum, retval; if (fstat(fd, &st) == -1) { - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, + mandoc_msg(MANDOCERR_FILE, 0, 0, "fstat: %s", strerror(errno)); return 0; } @@ -575,7 +443,7 @@ read_whole_file(struct mparse *curp, const char *file, int fd, if (curp->gzip == 0 && S_ISREG(st.st_mode)) { if (st.st_size > 0x7fffffff) { - mandoc_msg(MANDOCERR_TOOLARGE, curp, 0, 0, NULL); + mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL); return 0; } *with_mmap = 1; @@ -594,12 +462,12 @@ read_whole_file(struct mparse *curp, const char *file, int fd, * which this function must not do. */ if ((fd = dup(fd)) == -1) { - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, + mandoc_msg(MANDOCERR_FILE, 0, 0, "dup: %s", strerror(errno)); return 0; } if ((gz = gzdopen(fd, "rb")) == NULL) { - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, + mandoc_msg(MANDOCERR_FILE, 0, 0, "gzdopen: %s", strerror(errno)); close(fd); return 0; @@ -620,8 +488,7 @@ read_whole_file(struct mparse *curp, const char *file, int fd, for (;;) { if (off == fb->sz) { if (fb->sz == (1U << 31)) { - mandoc_msg(MANDOCERR_TOOLARGE, curp, - 0, 0, NULL); + mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL); break; } resize_buf(fb, 65536); @@ -637,7 +504,7 @@ read_whole_file(struct mparse *curp, const char *file, int fd, if (ssz == -1) { if (curp->gzip) (void)gzerror(gz, &gzerrnum); - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, "read: %s", + mandoc_msg(MANDOCERR_FILE, 0, 0, "read: %s", curp->gzip && gzerrnum != Z_ERRNO ? zError(gzerrnum) : strerror(errno)); break; @@ -646,7 +513,7 @@ read_whole_file(struct mparse *curp, const char *file, int fd, } if (curp->gzip && (gzerrnum = gzclose(gz)) != Z_OK) - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, "gzclose: %s", + mandoc_msg(MANDOCERR_FILE, 0, 0, "gzclose: %s", gzerrnum == Z_ERRNO ? strerror(errno) : zError(gzerrnum)); if (retval == 0) { @@ -659,35 +526,51 @@ read_whole_file(struct mparse *curp, const char *file, int fd, static void mparse_end(struct mparse *curp) { - if (curp->man->macroset == MACROSET_NONE) - curp->man->macroset = MACROSET_MAN; - if (curp->man->macroset == MACROSET_MDOC) + if (curp->man->meta.macroset == MACROSET_NONE) + curp->man->meta.macroset = MACROSET_MAN; + if (curp->man->meta.macroset == MACROSET_MDOC) mdoc_endparse(curp->man); else man_endparse(curp->man); roff_endparse(curp->roff); } -static void -mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file) +/* + * Read the whole file into memory and call the parsers. + * Called recursively when an .so request is encountered. + */ +void +mparse_readfd(struct mparse *curp, int fd, const char *filename) { - struct buf *svprimary; - const char *svfile; - size_t offset; static int recursion_depth; - if (64 < recursion_depth) { - mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, 0, NULL); + struct buf blk; + struct buf *save_primary; + const char *save_filename; + size_t offset; + int save_filenc, save_lineno; + int with_mmap; + + if (recursion_depth > 64) { + mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, 0, NULL); return; } + if (read_whole_file(curp, fd, &blk, &with_mmap) == 0) + return; + + /* + * Save some properties of the parent file. + */ + + save_primary = curp->primary; + save_filenc = curp->filenc; + save_lineno = curp->line; + save_filename = mandoc_msg_getinfilename(); - /* Line number is per-file. */ - svfile = curp->file; - curp->file = file; - svprimary = curp->primary; curp->primary = &blk; + curp->filenc = curp->options & (MPARSE_UTF8 | MPARSE_LATIN1); curp->line = 1; - recursion_depth++; + mandoc_msg_setinfilename(filename); /* Skip an UTF-8 byte order mark. */ if (curp->filenc & MPARSE_UTF8 && blk.sz > 2 && @@ -699,60 +582,33 @@ mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file) } else offset = 0; + recursion_depth++; mparse_buf_r(curp, blk, offset, 1); - if (--recursion_depth == 0) mparse_end(curp); - curp->primary = svprimary; - curp->file = svfile; -} - -enum mandoclevel -mparse_readmem(struct mparse *curp, void *buf, size_t len, - const char *file) -{ - struct buf blk; - - blk.buf = buf; - blk.sz = len; + /* + * Clean up and restore saved parent properties. + */ - mparse_parse_buffer(curp, blk, file); - return curp->file_status; -} + if (with_mmap) + munmap(blk.buf, blk.sz); + else + free(blk.buf); -/* - * Read the whole file into memory and call the parsers. - * Called recursively when an .so request is encountered. - */ -enum mandoclevel -mparse_readfd(struct mparse *curp, int fd, const char *file) -{ - struct buf blk; - int with_mmap; - int save_filenc; - - if (read_whole_file(curp, file, fd, &blk, &with_mmap)) { - save_filenc = curp->filenc; - curp->filenc = curp->options & - (MPARSE_UTF8 | MPARSE_LATIN1); - mparse_parse_buffer(curp, blk, file); - curp->filenc = save_filenc; - if (with_mmap) - munmap(blk.buf, blk.sz); - else - free(blk.buf); - } - return curp->file_status; + curp->primary = save_primary; + curp->filenc = save_filenc; + curp->line = save_lineno; + if (save_filename != NULL) + mandoc_msg_setinfilename(save_filename); } int mparse_open(struct mparse *curp, const char *file) { char *cp; - int fd; + int fd, save_errno; - curp->file = file; cp = strrchr(file, '.'); curp->gzip = (cp != NULL && ! strcmp(cp + 1, "gz")); @@ -767,9 +623,11 @@ mparse_open(struct mparse *curp, const char *file) */ if ( ! curp->gzip) { + save_errno = errno; mandoc_asprintf(&cp, "%s.gz", file); fd = open(cp, O_RDONLY); free(cp); + errno = save_errno; if (fd != -1) { curp->gzip = 1; return fd; @@ -778,36 +636,32 @@ mparse_open(struct mparse *curp, const char *file) /* Neither worked, give up. */ - mandoc_msg(MANDOCERR_FILE, curp, 0, 0, strerror(errno)); return -1; } struct mparse * -mparse_alloc(int options, enum mandocerr mmin, mandocmsg mmsg, - enum mandoc_os os_e, const char *os_s) +mparse_alloc(int options, enum mandoc_os os_e, const char *os_s) { struct mparse *curp; curp = mandoc_calloc(1, sizeof(struct mparse)); curp->options = options; - curp->mmin = mmin; - curp->mmsg = mmsg; curp->os_s = os_s; - curp->roff = roff_alloc(curp, options); - curp->man = roff_man_alloc(curp->roff, curp, curp->os_s, + curp->roff = roff_alloc(options); + curp->man = roff_man_alloc(curp->roff, curp->os_s, curp->options & MPARSE_QUICK ? 1 : 0); if (curp->options & MPARSE_MDOC) { - curp->man->macroset = MACROSET_MDOC; + curp->man->meta.macroset = MACROSET_MDOC; if (curp->man->mdocmac == NULL) curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); } else if (curp->options & MPARSE_MAN) { - curp->man->macroset = MACROSET_MAN; + curp->man->meta.macroset = MACROSET_MAN; if (curp->man->manmac == NULL) curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); } - curp->man->first->tok = TOKEN_NONE; + curp->man->meta.first->tok = TOKEN_NONE; curp->man->meta.os_e = os_e; return curp; } @@ -817,112 +671,40 @@ mparse_reset(struct mparse *curp) { roff_reset(curp->roff); roff_man_reset(curp->man); - - free(curp->sodest); - curp->sodest = NULL; - - if (curp->secondary) - curp->secondary->sz = 0; - - curp->file_status = MANDOCLEVEL_OK; + free_buf_list(curp->secondary); + curp->secondary = NULL; curp->gzip = 0; } void mparse_free(struct mparse *curp) { - roffhash_free(curp->man->mdocmac); roffhash_free(curp->man->manmac); roff_man_free(curp->man); roff_free(curp->roff); - if (curp->secondary) - free(curp->secondary->buf); - - free(curp->secondary); - free(curp->sodest); + free_buf_list(curp->secondary); free(curp); } -void -mparse_result(struct mparse *curp, struct roff_man **man, - char **sodest) +struct roff_meta * +mparse_result(struct mparse *curp) { - - if (sodest && NULL != (*sodest = curp->sodest)) { - *man = NULL; - return; + roff_state_reset(curp->man); + if (curp->options & MPARSE_VALIDATE) { + if (curp->man->meta.macroset == MACROSET_MDOC) + mdoc_validate(curp->man); + else + man_validate(curp->man); } - if (man) - *man = curp->man; + return &curp->man->meta; } void -mparse_updaterc(struct mparse *curp, enum mandoclevel *rc) -{ - if (curp->file_status > *rc) - *rc = curp->file_status; -} - -void -mandoc_vmsg(enum mandocerr t, struct mparse *m, - int ln, int pos, const char *fmt, ...) -{ - char buf[256]; - va_list ap; - - va_start(ap, fmt); - (void)vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - mandoc_msg(t, m, ln, pos, buf); -} - -void -mandoc_msg(enum mandocerr er, struct mparse *m, - int ln, int col, const char *msg) -{ - enum mandoclevel level; - - if (er < m->mmin && er != MANDOCERR_FILE) - return; - - level = MANDOCLEVEL_UNSUPP; - while (er < mandoclimits[level]) - level--; - - if (m->mmsg) - (*m->mmsg)(er, level, m->file, ln, col, msg); - - if (m->file_status < level) - m->file_status = level; -} - -const char * -mparse_strerror(enum mandocerr er) -{ - - return mandocerrs[er]; -} - -const char * -mparse_strlevel(enum mandoclevel lvl) -{ - return mandoclevels[lvl]; -} - -void -mparse_keep(struct mparse *p) -{ - - assert(NULL == p->secondary); - p->secondary = mandoc_calloc(1, sizeof(struct buf)); -} - -const char * -mparse_getkeep(const struct mparse *p) +mparse_copy(const struct mparse *p) { + struct buf *buf; - assert(p->secondary); - return p->secondary->sz ? p->secondary->buf : NULL; + for (buf = p->secondary; buf != NULL; buf = buf->next) + puts(buf->buf); } diff --git a/usr/src/cmd/mandoc/roff.c b/usr/src/cmd/mandoc/roff.c index 86e145e366..e84295aac3 100644 --- a/usr/src/cmd/mandoc/roff.c +++ b/usr/src/cmd/mandoc/roff.c @@ -1,7 +1,7 @@ -/* $Id: roff.c,v 1.329 2018/08/01 15:40:17 schwarze Exp $ */ +/* $Id: roff.c,v 1.363 2019/02/06 21:11:43 schwarze Exp $ */ /* * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010-2015, 2017-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 @@ -28,13 +28,23 @@ #include <stdlib.h> #include <string.h> -#include "mandoc.h" #include "mandoc_aux.h" #include "mandoc_ohash.h" +#include "mandoc.h" #include "roff.h" +#include "mandoc_parse.h" #include "libmandoc.h" #include "roff_int.h" -#include "libroff.h" +#include "tbl_parse.h" +#include "eqn_parse.h" + +/* + * ASCII_ESC is used to signal from roff_getarg() to roff_expand() + * that an escape sequence resulted from copy-in processing and + * needs to be checked or interpolated. As it is used nowhere + * else, it is defined here rather than in a header file. + */ +#define ASCII_ESC 27 /* Maximum number of string expansions per line, to break infinite loops. */ #define EXPAND_LIMIT 1000 @@ -85,10 +95,20 @@ struct roffreq { char name[]; }; +/* + * A macro processing context. + * More than one is needed when macro calls are nested. + */ +struct mctx { + char **argv; + int argc; + int argsz; +}; + struct roff { - struct mparse *parse; /* parse point */ struct roff_man *man; /* mdoc or man parser */ struct roffnode *last; /* leaf of stack */ + struct mctx *mstack; /* stack of macro contexts */ int *rstack; /* stack of inverted `ie' values */ struct ohash *reqtab; /* request lookup table */ struct roffreg *regtab; /* number registers */ @@ -104,10 +124,11 @@ struct roff { struct eqn_node *eqn; /* active equation parser */ int eqn_inline; /* current equation is inline */ int options; /* parse options */ + int mstacksz; /* current size of mstack */ + int mstackpos; /* position in mstack */ int rstacksz; /* current size limit of rstack */ int rstackpos; /* position in rstack */ int format; /* current file in mdoc or man format */ - int argc; /* number of args of the last macro */ char control; /* control character */ char escape; /* escape character */ }; @@ -131,7 +152,7 @@ struct roffnode { int pos, /* current pos in buffer */ \ int *offs /* reset offset of buffer data */ -typedef enum rofferr (*roffproc)(ROFF_ARGS); +typedef int (*roffproc)(ROFF_ARGS); struct roffmac { roffproc proc; /* process new macro */ @@ -151,32 +172,34 @@ struct predef { /* --- function prototypes ------------------------------------------------ */ -static void roffnode_cleanscope(struct roff *); -static void roffnode_pop(struct roff *); +static int roffnode_cleanscope(struct roff *); +static int roffnode_pop(struct roff *); static void roffnode_push(struct roff *, enum roff_tok, const char *, int, int); -static void roff_addtbl(struct roff_man *, struct tbl_node *); -static enum rofferr roff_als(ROFF_ARGS); -static enum rofferr roff_block(ROFF_ARGS); -static enum rofferr roff_block_text(ROFF_ARGS); -static enum rofferr roff_block_sub(ROFF_ARGS); -static enum rofferr roff_br(ROFF_ARGS); -static enum rofferr roff_cblock(ROFF_ARGS); -static enum rofferr roff_cc(ROFF_ARGS); -static void roff_ccond(struct roff *, int, int); -static enum rofferr roff_cond(ROFF_ARGS); -static enum rofferr roff_cond_text(ROFF_ARGS); -static enum rofferr roff_cond_sub(ROFF_ARGS); -static enum rofferr roff_ds(ROFF_ARGS); -static enum rofferr roff_ec(ROFF_ARGS); -static enum rofferr roff_eo(ROFF_ARGS); -static enum rofferr roff_eqndelim(struct roff *, struct buf *, int); +static void roff_addtbl(struct roff_man *, int, struct tbl_node *); +static int roff_als(ROFF_ARGS); +static int roff_block(ROFF_ARGS); +static int roff_block_text(ROFF_ARGS); +static int roff_block_sub(ROFF_ARGS); +static int roff_cblock(ROFF_ARGS); +static int roff_cc(ROFF_ARGS); +static int roff_ccond(struct roff *, int, int); +static int roff_char(ROFF_ARGS); +static int roff_cond(ROFF_ARGS); +static int roff_cond_text(ROFF_ARGS); +static int roff_cond_sub(ROFF_ARGS); +static int roff_ds(ROFF_ARGS); +static int roff_ec(ROFF_ARGS); +static int roff_eo(ROFF_ARGS); +static int roff_eqndelim(struct roff *, struct buf *, int); static int roff_evalcond(struct roff *r, int, char *, int *); static int roff_evalnum(struct roff *, int, const char *, int *, int *, int); static int roff_evalpar(struct roff *, int, const char *, int *, int *, int); static int roff_evalstrcond(const char *, int *); +static int roff_expand(struct roff *, struct buf *, + int, int, char); static void roff_free1(struct roff *); static void roff_freereg(struct roffreg *); static void roff_freestr(struct roffkv *); @@ -191,39 +214,42 @@ static const char *roff_getstrn(struct roff *, const char *, size_t, int *); static int roff_hasregn(const struct roff *, const char *, size_t); -static enum rofferr roff_insec(ROFF_ARGS); -static enum rofferr roff_it(ROFF_ARGS); -static enum rofferr roff_line_ignore(ROFF_ARGS); +static int roff_insec(ROFF_ARGS); +static int roff_it(ROFF_ARGS); +static int roff_line_ignore(ROFF_ARGS); static void roff_man_alloc1(struct roff_man *); static void roff_man_free1(struct roff_man *); -static enum rofferr roff_manyarg(ROFF_ARGS); -static enum rofferr roff_nr(ROFF_ARGS); -static enum rofferr roff_onearg(ROFF_ARGS); +static int roff_manyarg(ROFF_ARGS); +static int roff_noarg(ROFF_ARGS); +static int roff_nop(ROFF_ARGS); +static int roff_nr(ROFF_ARGS); +static int roff_onearg(ROFF_ARGS); static enum roff_tok roff_parse(struct roff *, char *, int *, int, int); -static enum rofferr roff_parsetext(struct roff *, struct buf *, +static int roff_parsetext(struct roff *, struct buf *, int, int *); -static enum rofferr roff_renamed(ROFF_ARGS); -static enum rofferr roff_res(struct roff *, struct buf *, int, int); -static enum rofferr roff_rm(ROFF_ARGS); -static enum rofferr roff_rn(ROFF_ARGS); -static enum rofferr roff_rr(ROFF_ARGS); +static int roff_renamed(ROFF_ARGS); +static int roff_return(ROFF_ARGS); +static int roff_rm(ROFF_ARGS); +static int roff_rn(ROFF_ARGS); +static int roff_rr(ROFF_ARGS); static void roff_setregn(struct roff *, const char *, size_t, int, char, int); static void roff_setstr(struct roff *, const char *, const char *, int); static void roff_setstrn(struct roffkv **, const char *, size_t, const char *, size_t, int); -static enum rofferr roff_so(ROFF_ARGS); -static enum rofferr roff_tr(ROFF_ARGS); -static enum rofferr roff_Dd(ROFF_ARGS); -static enum rofferr roff_TE(ROFF_ARGS); -static enum rofferr roff_TS(ROFF_ARGS); -static enum rofferr roff_EQ(ROFF_ARGS); -static enum rofferr roff_EN(ROFF_ARGS); -static enum rofferr roff_T_(ROFF_ARGS); -static enum rofferr roff_unsupp(ROFF_ARGS); -static enum rofferr roff_userdef(ROFF_ARGS); +static int roff_shift(ROFF_ARGS); +static int roff_so(ROFF_ARGS); +static int roff_tr(ROFF_ARGS); +static int roff_Dd(ROFF_ARGS); +static int roff_TE(ROFF_ARGS); +static int roff_TS(ROFF_ARGS); +static int roff_EQ(ROFF_ARGS); +static int roff_EN(ROFF_ARGS); +static int roff_T_(ROFF_ARGS); +static int roff_unsupp(ROFF_ARGS); +static int roff_userdef(ROFF_ARGS); /* --- constant data ------------------------------------------------------ */ @@ -231,8 +257,9 @@ static enum rofferr roff_userdef(ROFF_ARGS); #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */ const char *__roff_name[MAN_MAX + 1] = { - "br", "ce", "ft", "ll", - "mc", "po", "rj", "sp", + "br", "ce", "fi", "ft", + "ll", "mc", "nf", + "po", "rj", "sp", "ta", "ti", NULL, "ab", "ad", "af", "aln", "als", "am", "am1", "ami", @@ -326,24 +353,27 @@ const char *__roff_name[MAN_MAX + 1] = { "Dx", "%Q", "%U", "Ta", NULL, "TH", "SH", "SS", "TP", + "TQ", "LP", "PP", "P", "IP", "HP", "SM", "SB", "BI", "IB", "BR", "RB", "R", "B", "I", "IR", "RI", - "nf", "fi", "RE", "RS", "DT", "UC", "PD", "AT", "in", - "OP", "EX", "EE", "UR", + "SY", "YS", "OP", + "EX", "EE", "UR", "UE", "MT", "ME", NULL }; const char *const *roff_name = __roff_name; static struct roffmac roffs[TOKEN_NONE] = { - { roff_br, NULL, NULL, 0 }, /* br */ + { roff_noarg, NULL, NULL, 0 }, /* br */ { roff_onearg, NULL, NULL, 0 }, /* ce */ + { roff_noarg, NULL, NULL, 0 }, /* fi */ { roff_onearg, NULL, NULL, 0 }, /* ft */ { roff_onearg, NULL, NULL, 0 }, /* ll */ { roff_onearg, NULL, NULL, 0 }, /* mc */ + { roff_noarg, NULL, NULL, 0 }, /* nf */ { roff_onearg, NULL, NULL, 0 }, /* po */ { roff_onearg, NULL, NULL, 0 }, /* rj */ { roff_onearg, NULL, NULL, 0 }, /* sp */ @@ -373,14 +403,14 @@ static struct roffmac roffs[TOKEN_NONE] = { { roff_unsupp, NULL, NULL, 0 }, /* break */ { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */ { roff_line_ignore, NULL, NULL, 0 }, /* brnl */ - { roff_br, NULL, NULL, 0 }, /* brp */ + { roff_noarg, NULL, NULL, 0 }, /* brp */ { roff_line_ignore, NULL, NULL, 0 }, /* brpnl */ { roff_unsupp, NULL, NULL, 0 }, /* c2 */ { roff_cc, NULL, NULL, 0 }, /* cc */ { roff_insec, NULL, NULL, 0 }, /* cf */ { roff_line_ignore, NULL, NULL, 0 }, /* cflags */ { roff_line_ignore, NULL, NULL, 0 }, /* ch */ - { roff_unsupp, NULL, NULL, 0 }, /* char */ + { roff_char, NULL, NULL, 0 }, /* char */ { roff_unsupp, NULL, NULL, 0 }, /* chop */ { roff_line_ignore, NULL, NULL, 0 }, /* class */ { roff_insec, NULL, NULL, 0 }, /* close */ @@ -490,7 +520,7 @@ static struct roffmac roffs[TOKEN_NONE] = { { roff_line_ignore, NULL, NULL, 0 }, /* nhychar */ { roff_unsupp, NULL, NULL, 0 }, /* nm */ { roff_unsupp, NULL, NULL, 0 }, /* nn */ - { roff_unsupp, NULL, NULL, 0 }, /* nop */ + { roff_nop, NULL, NULL, 0 }, /* nop */ { roff_nr, NULL, NULL, 0 }, /* nr */ { roff_unsupp, NULL, NULL, 0 }, /* nrf */ { roff_line_ignore, NULL, NULL, 0 }, /* nroff */ @@ -519,7 +549,7 @@ static struct roffmac roffs[TOKEN_NONE] = { { roff_unsupp, NULL, NULL, 0 }, /* rchar */ { roff_line_ignore, NULL, NULL, 0 }, /* rd */ { roff_line_ignore, NULL, NULL, 0 }, /* recursionlimit */ - { roff_unsupp, NULL, NULL, 0 }, /* return */ + { roff_return, NULL, NULL, 0 }, /* return */ { roff_unsupp, NULL, NULL, 0 }, /* rfschar */ { roff_line_ignore, NULL, NULL, 0 }, /* rhang */ { roff_rm, NULL, NULL, 0 }, /* rm */ @@ -531,7 +561,7 @@ static struct roffmac roffs[TOKEN_NONE] = { { roff_unsupp, NULL, NULL, 0 }, /* schar */ { roff_line_ignore, NULL, NULL, 0 }, /* sentchar */ { roff_line_ignore, NULL, NULL, 0 }, /* shc */ - { roff_unsupp, NULL, NULL, 0 }, /* shift */ + { roff_shift, NULL, NULL, 0 }, /* shift */ { roff_line_ignore, NULL, NULL, 0 }, /* sizes */ { roff_so, NULL, NULL, 0 }, /* so */ { roff_line_ignore, NULL, NULL, 0 }, /* spacewidth */ @@ -573,7 +603,7 @@ static struct roffmac roffs[TOKEN_NONE] = { { roff_line_ignore, NULL, NULL, 0 }, /* watchlength */ { roff_line_ignore, NULL, NULL, 0 }, /* watchn */ { roff_unsupp, NULL, NULL, 0 }, /* wh */ - { roff_unsupp, NULL, NULL, 0 }, /* while */ + { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /*while*/ { roff_insec, NULL, NULL, 0 }, /* write */ { roff_insec, NULL, NULL, 0 }, /* writec */ { roff_insec, NULL, NULL, 0 }, /* writem */ @@ -657,18 +687,19 @@ roffhash_find(struct ohash *htab, const char *name, size_t sz) * Pop the current node off of the stack of roff instructions currently * pending. */ -static void +static int roffnode_pop(struct roff *r) { struct roffnode *p; + int inloop; - assert(r->last); p = r->last; - - r->last = r->last->parent; + inloop = p->tok == ROFF_while; + r->last = p->parent; free(p->name); free(p->end); free(p); + return inloop; } /* @@ -698,19 +729,17 @@ roffnode_push(struct roff *r, enum roff_tok tok, const char *name, static void roff_free1(struct roff *r) { - struct tbl_node *tbl; int i; - while (NULL != (tbl = r->first_tbl)) { - r->first_tbl = tbl->next; - tbl_free(tbl); - } + tbl_free(r->first_tbl); r->first_tbl = r->last_tbl = r->tbl = NULL; - if (r->last_eqn != NULL) - eqn_free(r->last_eqn); + eqn_free(r->last_eqn); r->last_eqn = r->eqn = NULL; + while (r->mstackpos >= 0) + roff_userret(r); + while (r->last) roffnode_pop(r); @@ -750,21 +779,26 @@ roff_reset(struct roff *r) void roff_free(struct roff *r) { + int i; + roff_free1(r); + for (i = 0; i < r->mstacksz; i++) + free(r->mstack[i].argv); + free(r->mstack); roffhash_free(r->reqtab); free(r); } struct roff * -roff_alloc(struct mparse *parse, int options) +roff_alloc(int options) { struct roff *r; r = mandoc_calloc(1, sizeof(struct roff)); - r->parse = parse; r->reqtab = roffhash_alloc(0, ROFF_RENAMED); r->options = options; r->format = options & (MPARSE_MDOC | MPARSE_MAN); + r->mstackpos = -1; r->rstackpos = -1; r->escape = '\\'; return r; @@ -775,9 +809,8 @@ roff_alloc(struct mparse *parse, int options) static void roff_man_free1(struct roff_man *man) { - - if (man->first != NULL) - roff_node_delete(man, man->first); + if (man->meta.first != NULL) + roff_node_delete(man, man->meta.first); free(man->meta.msec); free(man->meta.vol); free(man->meta.os); @@ -785,27 +818,33 @@ roff_man_free1(struct roff_man *man) free(man->meta.title); free(man->meta.name); free(man->meta.date); + free(man->meta.sodest); } -static void -roff_man_alloc1(struct roff_man *man) +void +roff_state_reset(struct roff_man *man) { - - memset(&man->meta, 0, sizeof(man->meta)); - man->first = mandoc_calloc(1, sizeof(*man->first)); - man->first->type = ROFFT_ROOT; - man->last = man->first; + man->last = man->meta.first; man->last_es = NULL; man->flags = 0; - man->macroset = MACROSET_NONE; man->lastsec = man->lastnamed = SEC_NONE; man->next = ROFF_NEXT_CHILD; + roff_setreg(man->roff, "nS", 0, '='); +} + +static void +roff_man_alloc1(struct roff_man *man) +{ + memset(&man->meta, 0, sizeof(man->meta)); + man->meta.first = mandoc_calloc(1, sizeof(*man->meta.first)); + man->meta.first->type = ROFFT_ROOT; + man->meta.macroset = MACROSET_NONE; + roff_state_reset(man); } void roff_man_reset(struct roff_man *man) { - roff_man_free1(man); roff_man_alloc1(man); } @@ -813,19 +852,16 @@ roff_man_reset(struct roff_man *man) void roff_man_free(struct roff_man *man) { - roff_man_free1(man); free(man); } struct roff_man * -roff_man_alloc(struct roff *roff, struct mparse *parse, - const char *os_s, int quick) +roff_man_alloc(struct roff *roff, const char *os_s, int quick) { struct roff_man *man; man = mandoc_calloc(1, sizeof(*man)); - man->parse = parse; man->roff = roff; man->os_s = os_s; man->quick = quick; @@ -853,6 +889,10 @@ roff_node_alloc(struct roff_man *man, int line, int pos, n->flags |= NODE_SYNPRETTY; else n->flags &= ~NODE_SYNPRETTY; + if ((man->flags & (ROFF_NOFILL | ROFF_NONOFILL)) == ROFF_NOFILL) + n->flags |= NODE_NOFILL; + else + n->flags &= ~NODE_NOFILL; if (man->flags & MDOC_NEWLINE) n->flags |= NODE_LINE; man->flags &= ~MDOC_NEWLINE; @@ -985,15 +1025,15 @@ roff_body_alloc(struct roff_man *man, int line, int pos, int tok) } static void -roff_addtbl(struct roff_man *man, struct tbl_node *tbl) +roff_addtbl(struct roff_man *man, int line, struct tbl_node *tbl) { struct roff_node *n; - const struct tbl_span *span; + struct tbl_span *span; - if (man->macroset == MACROSET_MAN) + if (man->meta.macroset == MACROSET_MAN) man_breakscope(man, ROFF_TS); while ((span = tbl_span(tbl)) != NULL) { - n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE); + n = roff_node_alloc(man, line, 0, ROFFT_TBL, TOKEN_NONE); n->span = span; roff_node_append(man, n); n->flags |= NODE_VALID | NODE_ENDED; @@ -1034,8 +1074,16 @@ roff_node_unlink(struct roff_man *man, struct roff_node *n) man->next = ROFF_NEXT_SIBLING; } } - if (man->first == n) - man->first = NULL; + if (man->meta.first == n) + man->meta.first = NULL; +} + +void +roff_node_relink(struct roff_man *man, struct roff_node *n) +{ + roff_node_unlink(man, n); + n->prev = n->next = NULL; + roff_node_append(man, n); } void @@ -1046,8 +1094,7 @@ roff_node_free(struct roff_node *n) mdoc_argv_free(n->args); if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM) free(n->norm); - if (n->eqn != NULL) - eqn_box_free(n->eqn); + eqn_box_free(n->eqn); free(n->string); free(n); } @@ -1114,17 +1161,19 @@ deroff(char **dest, const struct roff_node *n) /* --- main functions of the roff parser ---------------------------------- */ /* - * In the current line, expand escape sequences that tend to get - * used in numerical expressions and conditional requests. - * Also check the syntax of the remaining escape sequences. + * In the current line, expand escape sequences that produce parsable + * input text. Also check the syntax of the remaining escape sequences, + * which typically produce output glyphs or change formatter state. */ -static enum rofferr -roff_res(struct roff *r, struct buf *buf, int ln, int pos) +static int +roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char newesc) { + struct mctx *ctx; /* current macro call context */ char ubuf[24]; /* buffer to print the number */ struct roff_node *n; /* used for header comments */ const char *start; /* start of the string to process */ char *stesc; /* start of an escape sequence ('\\') */ + const char *esct; /* type of esccape sequence */ char *ep; /* end of comment string */ const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ @@ -1132,14 +1181,17 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) char *nbuf; /* new buffer to copy buf->buf to */ size_t maxl; /* expected length of the escape name */ size_t naml; /* actual length of the escape name */ - enum mandoc_esc esc; /* type of the escape sequence */ + size_t asz; /* length of the replacement */ + size_t rsz; /* length of the rest of the string */ int inaml; /* length returned from mandoc_escape() */ int expand_count; /* to avoid infinite loops */ int npos; /* position in numeric expression */ int arg_complete; /* argument not interrupted by eol */ + int quote_args; /* true for \\$@, false for \\$* */ int done; /* no more input available */ int deftype; /* type of definition to paste */ int rcsid; /* kind of RCS id seen */ + enum mandocerr err; /* for escape sequence problems */ char sign; /* increment number register */ char term; /* character terminating the escape */ @@ -1148,7 +1200,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) done = 0; start = buf->buf + pos; for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) { - if (stesc[0] != r->escape || stesc[1] == '\0') + if (stesc[0] != newesc || stesc[1] == '\0') continue; stesc++; if (*stesc != '"' && *stesc != '#') @@ -1168,8 +1220,9 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) isalnum((unsigned char)*cp) == 0 && strchr(cp, '$') != NULL) { if (r->man->meta.rcsids & rcsid) - mandoc_msg(MANDOCERR_RCS_REP, r->parse, - ln, stesc + 1 - buf->buf, stesc + 1); + mandoc_msg(MANDOCERR_RCS_REP, ln, + (int)(stesc - buf->buf) + 1, + "%s", stesc + 1); r->man->meta.rcsids |= rcsid; } @@ -1181,15 +1234,15 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) ep--; } if (*ep == ' ' || *ep == '\t') - mandoc_msg(MANDOCERR_SPACE_EOL, r->parse, - ln, ep - buf->buf, NULL); + mandoc_msg(MANDOCERR_SPACE_EOL, + ln, (int)(ep - buf->buf), NULL); /* * Save comments preceding the title macro * in the syntax tree. */ - if (r->format == 0) { + if (newesc != ASCII_ESC && r->format == 0) { while (*ep == ' ' || *ep == '\t') ep--; ep[1] = '\0'; @@ -1202,9 +1255,17 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) r->man->next = ROFF_NEXT_SIBLING; } - /* Discard comments. */ + /* Line continuation with comment. */ + + if (stesc[1] == '#') { + *stesc = '\0'; + return ROFF_IGN | ROFF_APPEND; + } + + /* Discard normal comments. */ - while (stesc > start && stesc[-1] == ' ') + while (stesc > start && stesc[-1] == ' ' && + (stesc == start + 1 || stesc[-2] != '\\')) stesc--; *stesc = '\0'; break; @@ -1222,11 +1283,16 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) expand_count = 0; while (stesc >= start) { + if (*stesc != newesc) { - /* Search backwards for the next backslash. */ + /* + * If we have a non-standard escape character, + * escape literal backslashes because all + * processing in subsequent functions uses + * the standard escaping rules. + */ - if (*stesc != r->escape) { - if (*stesc == '\\') { + if (newesc != ASCII_ESC && *stesc == '\\') { *stesc = '\0'; buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s", buf->buf, stesc + 1) + 1; @@ -1235,6 +1301,9 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) free(buf->buf); buf->buf = nbuf; } + + /* Search backwards for the next escape. */ + stesc--; continue; } @@ -1256,15 +1325,19 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) if (done) continue; else - return ROFF_APPEND; + return ROFF_IGN | ROFF_APPEND; } /* Decide whether to expand or to check only. */ term = '\0'; cp = stesc + 1; - switch (*cp) { + if (*cp == 'E') + cp++; + esct = cp; + switch (*esct) { case '*': + case '$': res = NULL; break; case 'B': @@ -1278,19 +1351,33 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) res = ubuf; break; default: - esc = mandoc_escape(&cp, &stnam, &inaml); - if (esc == ESCAPE_ERROR || - (esc == ESCAPE_SPECIAL && - mchars_spec2cp(stnam, inaml) < 0)) - mandoc_vmsg(MANDOCERR_ESC_BAD, - r->parse, ln, (int)(stesc - buf->buf), + err = MANDOCERR_OK; + switch(mandoc_escape(&cp, &stnam, &inaml)) { + case ESCAPE_SPECIAL: + if (mchars_spec2cp(stnam, inaml) >= 0) + break; + /* FALLTHROUGH */ + case ESCAPE_ERROR: + err = MANDOCERR_ESC_BAD; + break; + case ESCAPE_UNDEF: + err = MANDOCERR_ESC_UNDEF; + break; + case ESCAPE_UNSUPP: + err = MANDOCERR_ESC_UNSUPP; + break; + default: + break; + } + if (err != MANDOCERR_OK) + mandoc_msg(err, ln, (int)(stesc - buf->buf), "%.*s", (int)(cp - stesc), stesc); stesc--; continue; } if (EXPAND_LIMIT < ++expand_count) { - mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, + mandoc_msg(MANDOCERR_ROFFLOOP, ln, (int)(stesc - buf->buf), NULL); return ROFF_IGN; } @@ -1331,8 +1418,8 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) arg_complete = 1; while (maxl == 0 || naml < maxl) { if (*cp == '\0') { - mandoc_msg(MANDOCERR_ESC_BAD, r->parse, - ln, (int)(stesc - buf->buf), stesc); + mandoc_msg(MANDOCERR_ESC_BAD, ln, + (int)(stesc - buf->buf), "%s", stesc); arg_complete = 0; break; } @@ -1340,7 +1427,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) cp++; break; } - if (*cp++ != '\\' || stesc[1] != 'w') { + if (*cp++ != '\\' || *esct != 'w') { naml++; continue; } @@ -1348,6 +1435,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) case ESCAPE_SPECIAL: case ESCAPE_UNICODE: case ESCAPE_NUMBERED: + case ESCAPE_UNDEF: case ESCAPE_OVERSTRIKE: naml++; break; @@ -1361,13 +1449,80 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) * undefined, resume searching for escapes. */ - switch (stesc[1]) { + switch (*esct) { case '*': if (arg_complete) { deftype = ROFFDEF_USER | ROFFDEF_PRE; res = roff_getstrn(r, stnam, naml, &deftype); + + /* + * If not overriden, let \*(.T + * through to the formatters. + */ + + if (res == NULL && naml == 2 && + stnam[0] == '.' && stnam[1] == 'T') { + roff_setstrn(&r->strtab, + ".T", 2, NULL, 0, 0); + stesc--; + continue; + } } break; + case '$': + if (r->mstackpos < 0) { + mandoc_msg(MANDOCERR_ARG_UNDEF, ln, + (int)(stesc - buf->buf), "%.3s", stesc); + break; + } + ctx = r->mstack + r->mstackpos; + npos = esct[1] - '1'; + if (npos >= 0 && npos <= 8) { + res = npos < ctx->argc ? + ctx->argv[npos] : ""; + break; + } + if (esct[1] == '*') + quote_args = 0; + else if (esct[1] == '@') + quote_args = 1; + else { + mandoc_msg(MANDOCERR_ARG_NONUM, ln, + (int)(stesc - buf->buf), "%.3s", stesc); + break; + } + asz = 0; + for (npos = 0; npos < ctx->argc; npos++) { + if (npos) + asz++; /* blank */ + if (quote_args) + asz += 2; /* quotes */ + asz += strlen(ctx->argv[npos]); + } + if (asz != 3) { + rsz = buf->sz - (stesc - buf->buf) - 3; + if (asz < 3) + memmove(stesc + asz, stesc + 3, rsz); + buf->sz += asz - 3; + nbuf = mandoc_realloc(buf->buf, buf->sz); + start = nbuf + pos; + stesc = nbuf + (stesc - buf->buf); + buf->buf = nbuf; + if (asz > 3) + memmove(stesc + asz, stesc + 3, rsz); + } + for (npos = 0; npos < ctx->argc; npos++) { + if (npos) + *stesc++ = ' '; + if (quote_args) + *stesc++ = '"'; + cp = ctx->argv[npos]; + while (*cp != '\0') + *stesc++ = *cp++; + if (quote_args) + *stesc++ = '"'; + } + continue; case 'B': npos = 0; ubuf[0] = arg_complete && @@ -1391,12 +1546,13 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) } if (res == NULL) { - mandoc_vmsg(MANDOCERR_STR_UNDEF, - r->parse, ln, (int)(stesc - buf->buf), - "%.*s", (int)naml, stnam); + if (*esct == '*') + mandoc_msg(MANDOCERR_STR_UNDEF, + ln, (int)(stesc - buf->buf), + "%.*s", (int)naml, stnam); res = ""; } else if (buf->sz + strlen(res) > SHRT_MAX) { - mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, + mandoc_msg(MANDOCERR_ROFFLOOP, ln, (int)(stesc - buf->buf), NULL); return ROFF_IGN; } @@ -1418,9 +1574,121 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) } /* + * Parse a quoted or unquoted roff-style request or macro argument. + * Return a pointer to the parsed argument, which is either the original + * pointer or advanced by one byte in case the argument is quoted. + * NUL-terminate the argument in place. + * Collapse pairs of quotes inside quoted arguments. + * Advance the argument pointer to the next argument, + * or to the NUL byte terminating the argument line. + */ +char * +roff_getarg(struct roff *r, char **cpp, int ln, int *pos) +{ + struct buf buf; + char *cp, *start; + int newesc, pairs, quoted, white; + + /* Quoting can only start with a new word. */ + start = *cpp; + quoted = 0; + if ('"' == *start) { + quoted = 1; + start++; + } + + newesc = pairs = white = 0; + for (cp = start; '\0' != *cp; cp++) { + + /* + * Move the following text left + * after quoted quotes and after "\\" and "\t". + */ + if (pairs) + cp[-pairs] = cp[0]; + + if ('\\' == cp[0]) { + /* + * In copy mode, translate double to single + * backslashes and backslash-t to literal tabs. + */ + switch (cp[1]) { + case 'a': + case 't': + cp[-pairs] = '\t'; + pairs++; + cp++; + break; + case '\\': + newesc = 1; + cp[-pairs] = ASCII_ESC; + pairs++; + cp++; + break; + case ' ': + /* Skip escaped blanks. */ + if (0 == quoted) + cp++; + break; + default: + break; + } + } else if (0 == quoted) { + if (' ' == cp[0]) { + /* Unescaped blanks end unquoted args. */ + white = 1; + break; + } + } else if ('"' == cp[0]) { + if ('"' == cp[1]) { + /* Quoted quotes collapse. */ + pairs++; + cp++; + } else { + /* Unquoted quotes end quoted args. */ + quoted = 2; + break; + } + } + } + + /* Quoted argument without a closing quote. */ + if (1 == quoted) + mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL); + + /* NUL-terminate this argument and move to the next one. */ + if (pairs) + cp[-pairs] = '\0'; + if ('\0' != *cp) { + *cp++ = '\0'; + while (' ' == *cp) + cp++; + } + *pos += (int)(cp - start) + (quoted ? 1 : 0); + *cpp = cp; + + if ('\0' == *cp && (white || ' ' == cp[-1])) + mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL); + + start = mandoc_strdup(start); + if (newesc == 0) + return start; + + buf.buf = start; + buf.sz = strlen(start) + 1; + buf.next = NULL; + if (roff_expand(r, &buf, ln, 0, ASCII_ESC) & ROFF_IGN) { + free(buf.buf); + buf.buf = mandoc_strdup(""); + } + return buf.buf; +} + + +/* * Process text streams. */ -static enum rofferr +static int roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs) { size_t sz; @@ -1486,11 +1754,11 @@ roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs) return ROFF_CONT; } -enum rofferr +int roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) { enum roff_tok t; - enum rofferr e; + int e; int pos; /* parse point */ int spos; /* saved parse point for messages */ int ppos; /* original offset in buf->buf */ @@ -1511,8 +1779,8 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) /* Expand some escape sequences. */ - e = roff_res(r, buf, ln, pos); - if (e == ROFF_IGN || e == ROFF_APPEND) + e = roff_expand(r, buf, ln, pos, r->escape); + if ((e & ROFF_MASK) == ROFF_IGN) return e; assert(e == ROFF_CONT); @@ -1529,27 +1797,27 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) if (r->last != NULL && ! ctl) { t = r->last->tok; e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs); - if (e == ROFF_IGN) + if ((e & ROFF_MASK) == ROFF_IGN) return e; - assert(e == ROFF_CONT); - } + e &= ~ROFF_MASK; + } else + e = ROFF_IGN; if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) { eqn_read(r->eqn, buf->buf + ppos); - return ROFF_IGN; + return e; } if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) { tbl_read(r->tbl, ln, buf->buf, ppos); - roff_addtbl(r->man, r->tbl); - return ROFF_IGN; + roff_addtbl(r->man, ln, r->tbl); + return e; } if ( ! ctl) - return roff_parsetext(r, buf, pos, offs); + return roff_parsetext(r, buf, pos, offs) | e; /* Skip empty request lines. */ if (buf->buf[pos] == '"') { - mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse, - ln, pos, NULL); + mandoc_msg(MANDOCERR_COMMENT_BAD, ln, pos, NULL); return ROFF_IGN; } else if (buf->buf[pos] == '\0') return ROFF_IGN; @@ -1574,8 +1842,8 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS || t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) { - mandoc_msg(MANDOCERR_TBLMACRO, r->parse, - ln, pos, buf->buf + spos); + mandoc_msg(MANDOCERR_TBLMACRO, + ln, pos, "%s", buf->buf + spos); if (t != TOKEN_NONE) return ROFF_IGN; while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ') @@ -1583,7 +1851,7 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) while (buf->buf[pos] == ' ') pos++; tbl_read(r->tbl, ln, buf->buf, pos); - roff_addtbl(r->man, r->tbl); + roff_addtbl(r->man, ln, r->tbl); return ROFF_IGN; } @@ -1611,25 +1879,41 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs); } +/* + * Internal interface function to tell the roff parser that execution + * of the current macro ended. This is required because macro + * definitions usually do not end with a .return request. + */ +void +roff_userret(struct roff *r) +{ + struct mctx *ctx; + int i; + + assert(r->mstackpos >= 0); + ctx = r->mstack + r->mstackpos; + for (i = 0; i < ctx->argc; i++) + free(ctx->argv[i]); + ctx->argc = 0; + r->mstackpos--; +} + void roff_endparse(struct roff *r) { if (r->last != NULL) - mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, - r->last->line, r->last->col, - roff_name[r->last->tok]); + mandoc_msg(MANDOCERR_BLK_NOEND, r->last->line, + r->last->col, "%s", roff_name[r->last->tok]); if (r->eqn != NULL) { - mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, + mandoc_msg(MANDOCERR_BLK_NOEND, r->eqn->node->line, r->eqn->node->pos, "EQ"); eqn_parse(r->eqn); r->eqn = NULL; } if (r->tbl != NULL) { - mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, - r->tbl->line, r->tbl->pos, "TS"); - tbl_end(r->tbl); + tbl_end(r->tbl, 1); r->tbl = NULL; } } @@ -1680,7 +1964,7 @@ roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos) /* --- handling of request blocks ----------------------------------------- */ -static enum rofferr +static int roff_cblock(ROFF_ARGS) { @@ -1690,8 +1974,7 @@ roff_cblock(ROFF_ARGS) */ if (r->last == NULL) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, ".."); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); return ROFF_IGN; } @@ -1705,13 +1988,12 @@ roff_cblock(ROFF_ARGS) case ROFF_ig: break; default: - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, ".."); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); return ROFF_IGN; } if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, ".. %s", buf->buf + pos); roffnode_pop(r); @@ -1720,50 +2002,48 @@ roff_cblock(ROFF_ARGS) } -static void +static int roffnode_cleanscope(struct roff *r) { + int inloop; - while (r->last) { + inloop = 0; + while (r->last != NULL) { if (--r->last->endspan != 0) break; - roffnode_pop(r); + inloop += roffnode_pop(r); } + return inloop; } -static void +static int roff_ccond(struct roff *r, int ln, int ppos) { - if (NULL == r->last) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "\\}"); - return; + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}"); + return 0; } switch (r->last->tok) { case ROFF_el: case ROFF_ie: case ROFF_if: + case ROFF_while: break; default: - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "\\}"); - return; + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}"); + return 0; } if (r->last->endspan > -1) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "\\}"); - return; + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}"); + return 0; } - roffnode_pop(r); - roffnode_cleanscope(r); - return; + return roffnode_pop(r) + roffnode_cleanscope(r); } -static enum rofferr +static int roff_block(ROFF_ARGS) { const char *name, *value; @@ -1800,8 +2080,8 @@ roff_block(ROFF_ARGS) deftype = ROFFDEF_USER; name = roff_getstrn(r, iname, namesz, &deftype); if (name == NULL) { - mandoc_vmsg(MANDOCERR_STR_UNDEF, - r->parse, ln, (int)(iname - buf->buf), + mandoc_msg(MANDOCERR_STR_UNDEF, + ln, (int)(iname - buf->buf), "%.*s", (int)namesz, iname); namesz = 0; } else @@ -1810,8 +2090,8 @@ roff_block(ROFF_ARGS) name = iname; if (namesz == 0 && tok != ROFF_ig) { - mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, - ln, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_REQ_EMPTY, + ln, ppos, "%s", roff_name[tok]); return ROFF_IGN; } @@ -1869,8 +2149,8 @@ roff_block(ROFF_ARGS) deftype = ROFFDEF_USER; name = roff_getstrn(r, iname, namesz, &deftype); if (name == NULL) { - mandoc_vmsg(MANDOCERR_STR_UNDEF, - r->parse, ln, (int)(iname - buf->buf), + mandoc_msg(MANDOCERR_STR_UNDEF, + ln, (int)(iname - buf->buf), "%.*s", (int)namesz, iname); namesz = 0; } else @@ -1882,13 +2162,13 @@ roff_block(ROFF_ARGS) r->last->end = mandoc_strndup(name, namesz); if (*cp != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, ln, pos, ".%s ... %s", roff_name[tok], cp); return ROFF_IGN; } -static enum rofferr +static int roff_block_sub(ROFF_ARGS) { enum roff_tok t; @@ -1942,7 +2222,7 @@ roff_block_sub(ROFF_ARGS) return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); } -static enum rofferr +static int roff_block_text(ROFF_ARGS) { @@ -1952,15 +2232,19 @@ roff_block_text(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_cond_sub(ROFF_ARGS) { - enum roff_tok t; char *ep; - int rr; + int endloop, irc, rr; + enum roff_tok t; + irc = ROFF_IGN; rr = r->last->rule; - roffnode_cleanscope(r); + endloop = tok != ROFF_while ? ROFF_IGN : + rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT; + if (roffnode_cleanscope(r)) + irc |= endloop; /* * If `\}' occurs on a macro line without a preceding macro, @@ -1971,13 +2255,17 @@ roff_cond_sub(ROFF_ARGS) if (ep[0] == '\\' && ep[1] == '}') rr = 0; - /* Always check for the closing delimiter `\}'. */ + /* + * The closing delimiter `\}' rewinds the conditional scope + * but is otherwise ignored when interpreting the line. + */ while ((ep = strchr(ep, '\\')) != NULL) { switch (ep[1]) { case '}': memmove(ep, ep + 2, strlen(ep + 2) + 1); - roff_ccond(r, ln, ep - buf->buf); + if (roff_ccond(r, ln, ep - buf->buf)) + irc |= endloop; break; case '\0': ++ep; @@ -1994,30 +2282,57 @@ roff_cond_sub(ROFF_ARGS) */ t = roff_parse(r, buf->buf, &pos, ln, ppos); - return t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT) - ? (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) : rr - ? ROFF_CONT : ROFF_IGN; + irc |= t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT) ? + (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) : + rr ? ROFF_CONT : ROFF_IGN; + return irc; } -static enum rofferr +static int roff_cond_text(ROFF_ARGS) { char *ep; - int rr; + int endloop, irc, rr; + irc = ROFF_IGN; rr = r->last->rule; - roffnode_cleanscope(r); + endloop = tok != ROFF_while ? ROFF_IGN : + rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT; + if (roffnode_cleanscope(r)) + irc |= endloop; + + /* + * If `\}' occurs on a text line with neither preceding + * nor following characters, drop the line completely. + */ ep = buf->buf + pos; + if (strcmp(ep, "\\}") == 0) + rr = 0; + + /* + * The closing delimiter `\}' rewinds the conditional scope + * but is otherwise ignored when interpreting the line. + */ + while ((ep = strchr(ep, '\\')) != NULL) { - if (*(++ep) == '}') { - *ep = '&'; - roff_ccond(r, ln, ep - buf->buf - 1); - } - if (*ep != '\0') + switch (ep[1]) { + case '}': + memmove(ep, ep + 2, strlen(ep + 2) + 1); + if (roff_ccond(r, ln, ep - buf->buf)) + irc |= endloop; + break; + case '\0': ++ep; + break; + default: + ep += 2; + break; + } } - return rr ? ROFF_CONT : ROFF_IGN; + if (rr) + irc |= ROFF_CONT; + return irc; } /* --- handling of numeric and conditional expressions -------------------- */ @@ -2144,9 +2459,10 @@ out: static int roff_evalcond(struct roff *r, int ln, char *v, int *pos) { - char *cp, *name; - size_t sz; - int deftype, number, savepos, istrue, wanttrue; + const char *start, *end; + char *cp, *name; + size_t sz; + int deftype, len, number, savepos, istrue, wanttrue; if ('!' == v[*pos]) { wanttrue = 0; @@ -2161,12 +2477,50 @@ roff_evalcond(struct roff *r, int ln, char *v, int *pos) case 'o': (*pos)++; return wanttrue; - case 'c': case 'e': case 't': case 'v': (*pos)++; return !wanttrue; + case 'c': + do { + (*pos)++; + } while (v[*pos] == ' '); + + /* + * Quirk for groff compatibility: + * The horizontal tab is neither available nor unavailable. + */ + + if (v[*pos] == '\t') { + (*pos)++; + return 0; + } + + /* Printable ASCII characters are available. */ + + if (v[*pos] != '\\') { + (*pos)++; + return wanttrue; + } + + end = v + ++*pos; + switch (mandoc_escape(&end, &start, &len)) { + case ESCAPE_SPECIAL: + istrue = mchars_spec2cp(start, len) != -1; + break; + case ESCAPE_UNICODE: + istrue = 1; + break; + case ESCAPE_NUMBERED: + istrue = mchars_num2char(start, len) != -1; + break; + default: + istrue = !wanttrue; + break; + } + *pos = end - v; + return istrue == wanttrue; case 'd': case 'r': cp = v + *pos + 1; @@ -2183,7 +2537,7 @@ roff_evalcond(struct roff *r, int ln, char *v, int *pos) roff_getstrn(r, name, sz, &deftype); istrue = !!deftype; } - *pos = cp - v; + *pos = (name + sz) - v; return istrue == wanttrue; default: break; @@ -2198,34 +2552,33 @@ roff_evalcond(struct roff *r, int ln, char *v, int *pos) return 0; } -static enum rofferr +static int roff_line_ignore(ROFF_ARGS) { return ROFF_IGN; } -static enum rofferr +static int roff_insec(ROFF_ARGS) { - mandoc_msg(MANDOCERR_REQ_INSEC, r->parse, - ln, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_REQ_INSEC, ln, ppos, "%s", roff_name[tok]); return ROFF_IGN; } -static enum rofferr +static int roff_unsupp(ROFF_ARGS) { - mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse, - ln, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_REQ_UNSUPP, ln, ppos, "%s", roff_name[tok]); return ROFF_IGN; } -static enum rofferr +static int roff_cond(ROFF_ARGS) { + int irc; roffnode_push(r, tok, NULL, ln, ppos); @@ -2264,9 +2617,10 @@ roff_cond(ROFF_ARGS) * Determine scope. * If there is nothing on the line after the conditional, * not even whitespace, use next-line scope. + * Except that .while does not support next-line scope. */ - if (buf->buf[pos] == '\0') { + if (buf->buf[pos] == '\0' && tok != ROFF_while) { r->last->endspan = 2; goto out; } @@ -2291,17 +2645,20 @@ roff_cond(ROFF_ARGS) */ if (buf->buf[pos] == '\0') - mandoc_msg(MANDOCERR_COND_EMPTY, r->parse, - ln, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_COND_EMPTY, + ln, ppos, "%s", roff_name[tok]); r->last->endspan = 1; out: *offs = pos; - return ROFF_RERUN; + irc = ROFF_RERUN; + if (tok == ROFF_while) + irc |= ROFF_WHILE; + return irc; } -static enum rofferr +static int roff_ds(ROFF_ARGS) { char *string; @@ -2326,8 +2683,15 @@ roff_ds(ROFF_ARGS) return ROFF_IGN; namesz = roff_getname(r, &string, ln, pos); - if (name[namesz] == '\\') + switch (name[namesz]) { + case '\\': return ROFF_IGN; + case '\t': + string = buf->buf + pos + namesz; + break; + default: + break; + } /* Read past the initial double-quote, if any. */ if (*string == '"') @@ -2492,7 +2856,7 @@ roff_evalnum(struct roff *r, int ln, const char *v, case '/': if (operand2 == 0) { mandoc_msg(MANDOCERR_DIVZERO, - r->parse, ln, *pos, v); + ln, *pos, "%s", v); *res = 0; break; } @@ -2501,7 +2865,7 @@ roff_evalnum(struct roff *r, int ln, const char *v, case '%': if (operand2 == 0) { mandoc_msg(MANDOCERR_DIVZERO, - r->parse, ln, *pos, v); + ln, *pos, "%s", v); *res = 0; break; } @@ -2600,7 +2964,7 @@ roff_getregro(const struct roff *r, const char *name) switch (*name) { case '$': /* Number of arguments of the last macro evaluated. */ - return r->argc; + return r->mstackpos < 0 ? 0 : r->mstack[r->mstackpos].argc; case 'A': /* ASCII approximation mode is always off. */ return 0; case 'g': /* Groff compatibility mode is always on. */ @@ -2690,7 +3054,7 @@ roff_freereg(struct roffreg *reg) } } -static enum rofferr +static int roff_nr(ROFF_ARGS) { char *key, *val, *step; @@ -2703,7 +3067,7 @@ roff_nr(ROFF_ARGS) return ROFF_IGN; keysz = roff_getname(r, &val, ln, pos); - if (key[keysz] == '\\') + if (key[keysz] == '\\' || key[keysz] == '\t') return ROFF_IGN; sign = *val; @@ -2724,7 +3088,7 @@ roff_nr(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_rr(ROFF_ARGS) { struct roffreg *reg, **prev; @@ -2754,7 +3118,7 @@ roff_rr(ROFF_ARGS) /* --- handler functions for roff requests -------------------------------- */ -static enum rofferr +static int roff_rm(ROFF_ARGS) { const char *name; @@ -2767,13 +3131,13 @@ roff_rm(ROFF_ARGS) namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf)); roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0); roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); - if (name[namesz] == '\\') + if (name[namesz] == '\\' || name[namesz] == '\t') break; } return ROFF_IGN; } -static enum rofferr +static int roff_it(ROFF_ARGS) { int iv; @@ -2781,8 +3145,8 @@ roff_it(ROFF_ARGS) /* Parse the number of lines. */ if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) { - mandoc_msg(MANDOCERR_IT_NONUM, r->parse, - ln, ppos, buf->buf + 1); + mandoc_msg(MANDOCERR_IT_NONUM, + ln, ppos, "%s", buf->buf + 1); return ROFF_IGN; } @@ -2802,7 +3166,7 @@ roff_it(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_Dd(ROFF_ARGS) { int mask; @@ -2832,15 +3196,15 @@ roff_Dd(ROFF_ARGS) return ROFF_CONT; } -static enum rofferr +static int roff_TE(ROFF_ARGS) { + r->man->flags &= ~ROFF_NONOFILL; if (r->tbl == NULL) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "TE"); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE"); return ROFF_IGN; } - if (tbl_end(r->tbl) == 0) { + if (tbl_end(r->tbl, 0) == 0) { r->tbl = NULL; free(buf->buf); buf->buf = mandoc_strdup(".sp"); @@ -2852,13 +3216,12 @@ roff_TE(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_T_(ROFF_ARGS) { if (NULL == r->tbl) - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "T&"); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "T&"); else tbl_restart(ln, ppos, r->tbl); @@ -2868,7 +3231,7 @@ roff_T_(ROFF_ARGS) /* * Handle in-line equation delimiters. */ -static enum rofferr +static int roff_eqndelim(struct roff *r, struct buf *buf, int pos) { char *cp1, *cp2; @@ -2931,68 +3294,85 @@ roff_eqndelim(struct roff *r, struct buf *buf, int pos) return ROFF_REPARSE; } -static enum rofferr +static int roff_EQ(ROFF_ARGS) { struct roff_node *n; - if (r->man->macroset == MACROSET_MAN) + if (r->man->meta.macroset == MACROSET_MAN) man_breakscope(r->man, ROFF_EQ); n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE); if (ln > r->man->last->line) n->flags |= NODE_LINE; - n->eqn = mandoc_calloc(1, sizeof(*n->eqn)); - n->eqn->expectargs = UINT_MAX; + n->eqn = eqn_box_new(); roff_node_append(r->man, n); r->man->next = ROFF_NEXT_SIBLING; assert(r->eqn == NULL); if (r->last_eqn == NULL) - r->last_eqn = eqn_alloc(r->parse); + r->last_eqn = eqn_alloc(); else eqn_reset(r->last_eqn); r->eqn = r->last_eqn; r->eqn->node = n; if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, ".EQ %s", buf->buf + pos); return ROFF_IGN; } -static enum rofferr +static int roff_EN(ROFF_ARGS) { if (r->eqn != NULL) { eqn_parse(r->eqn); r->eqn = NULL; } else - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN"); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "EN"); if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, "EN %s", buf->buf + pos); return ROFF_IGN; } -static enum rofferr +static int roff_TS(ROFF_ARGS) { if (r->tbl != NULL) { - mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse, - ln, ppos, "TS breaks TS"); - tbl_end(r->tbl); + mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS"); + tbl_end(r->tbl, 0); } - r->tbl = tbl_alloc(ppos, ln, r->parse); - if (r->last_tbl) - r->last_tbl->next = r->tbl; - else + r->man->flags |= ROFF_NONOFILL; + r->tbl = tbl_alloc(ppos, ln, r->last_tbl); + if (r->last_tbl == NULL) r->first_tbl = r->tbl; r->last_tbl = r->tbl; return ROFF_IGN; } -static enum rofferr +static int +roff_noarg(ROFF_ARGS) +{ + if (r->man->flags & (MAN_BLINE | MAN_ELINE)) + man_breakscope(r->man, tok); + if (tok == ROFF_brp) + tok = ROFF_br; + roff_elem_alloc(r->man, ln, ppos, tok); + if (buf->buf[pos] != '\0') + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, + "%s %s", roff_name[tok], buf->buf + pos); + if (tok == ROFF_nf) + r->man->flags |= ROFF_NOFILL; + else if (tok == ROFF_fi) + r->man->flags &= ~ROFF_NOFILL; + r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; + r->man->next = ROFF_NEXT_SIBLING; + return ROFF_IGN; +} + +static int roff_onearg(ROFF_ARGS) { struct roff_node *n; @@ -3019,8 +3399,8 @@ roff_onearg(ROFF_ARGS) while (*cp == ' ') *cp++ = '\0'; if (*cp != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, - r->parse, ln, cp - buf->buf, + mandoc_msg(MANDOCERR_ARG_EXCESS, + ln, (int)(cp - buf->buf), "%s ... %s", roff_name[tok], cp); roff_word_alloc(r->man, ln, pos, buf->buf + pos); } @@ -3033,8 +3413,8 @@ roff_onearg(ROFF_ARGS) npos = 0; if (roff_evalnum(r, ln, r->man->last->string, &npos, &roffce_lines, 0) == 0) { - mandoc_vmsg(MANDOCERR_CE_NONUM, - r->parse, ln, pos, "ce %s", buf->buf + pos); + mandoc_msg(MANDOCERR_CE_NONUM, + ln, pos, "ce %s", buf->buf + pos); roffce_lines = 1; } if (roffce_lines < 1) { @@ -3052,7 +3432,7 @@ roff_onearg(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_manyarg(ROFF_ARGS) { struct roff_node *n; @@ -3075,7 +3455,7 @@ roff_manyarg(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_als(ROFF_ARGS) { char *oldn, *newn, *end, *value; @@ -3086,7 +3466,7 @@ roff_als(ROFF_ARGS) return ROFF_IGN; newsz = roff_getname(r, &oldn, ln, pos); - if (newn[newsz] == '\\' || *oldn == '\0') + if (newn[newsz] == '\\' || newn[newsz] == '\t' || *oldn == '\0') return ROFF_IGN; end = oldn; @@ -3094,7 +3474,7 @@ roff_als(ROFF_ARGS) if (oldsz == 0) return ROFF_IGN; - valsz = mandoc_asprintf(&value, ".%.*s \\$*\\\"\n", + valsz = mandoc_asprintf(&value, ".%.*s \\$@\\\"\n", (int)oldsz, oldn); roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0); roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); @@ -3102,21 +3482,7 @@ roff_als(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr -roff_br(ROFF_ARGS) -{ - if (r->man->flags & (MAN_BLINE | MAN_ELINE)) - man_breakscope(r->man, ROFF_br); - roff_elem_alloc(r->man, ln, ppos, ROFF_br); - if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, - "%s %s", roff_name[tok], buf->buf + pos); - r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; - r->man->next = ROFF_NEXT_SIBLING; - return ROFF_IGN; -} - -static enum rofferr +static int roff_cc(ROFF_ARGS) { const char *p; @@ -3127,13 +3493,83 @@ roff_cc(ROFF_ARGS) r->control = '\0'; if (*p != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, ln, p - buf->buf, "cc ... %s", p); return ROFF_IGN; } -static enum rofferr +static int +roff_char(ROFF_ARGS) +{ + const char *p, *kp, *vp; + size_t ksz, vsz; + int font; + + /* Parse the character to be replaced. */ + + kp = buf->buf + pos; + p = kp + 1; + if (*kp == '\0' || (*kp == '\\' && + mandoc_escape(&p, NULL, NULL) != ESCAPE_SPECIAL) || + (*p != ' ' && *p != '\0')) { + mandoc_msg(MANDOCERR_CHAR_ARG, ln, pos, "char %s", kp); + return ROFF_IGN; + } + ksz = p - kp; + while (*p == ' ') + p++; + + /* + * If the replacement string contains a font escape sequence, + * we have to restore the font at the end. + */ + + vp = p; + vsz = strlen(p); + font = 0; + while (*p != '\0') { + if (*p++ != '\\') + continue; + switch (mandoc_escape(&p, NULL, NULL)) { + case ESCAPE_FONT: + case ESCAPE_FONTROMAN: + case ESCAPE_FONTITALIC: + case ESCAPE_FONTBOLD: + case ESCAPE_FONTBI: + case ESCAPE_FONTCW: + case ESCAPE_FONTPREV: + font++; + break; + default: + break; + } + } + if (font > 1) + mandoc_msg(MANDOCERR_CHAR_FONT, + ln, (int)(vp - buf->buf), "%s", vp); + + /* + * Approximate the effect of .char using the .tr tables. + * XXX In groff, .char and .tr interact differently. + */ + + if (ksz == 1) { + if (r->xtab == NULL) + r->xtab = mandoc_calloc(128, sizeof(*r->xtab)); + assert((unsigned int)*kp < 128); + free(r->xtab[(int)*kp].p); + r->xtab[(int)*kp].sz = mandoc_asprintf(&r->xtab[(int)*kp].p, + "%s%s", vp, font ? "\fP" : ""); + } else { + roff_setstrn(&r->xmbtab, kp, ksz, vp, vsz, 0); + if (font) + roff_setstrn(&r->xmbtab, kp, ksz, "\\fP", 3, 1); + } + return ROFF_IGN; +} + +static int roff_ec(ROFF_ARGS) { const char *p; @@ -3144,23 +3580,32 @@ roff_ec(ROFF_ARGS) else { r->escape = *p; if (*++p != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, - ln, p - buf->buf, "ec ... %s", p); + mandoc_msg(MANDOCERR_ARG_EXCESS, ln, + (int)(p - buf->buf), "ec ... %s", p); } return ROFF_IGN; } -static enum rofferr +static int roff_eo(ROFF_ARGS) { r->escape = '\0'; if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, "eo %s", buf->buf + pos); return ROFF_IGN; } -static enum rofferr +static int +roff_nop(ROFF_ARGS) +{ + while (buf->buf[pos] == ' ') + pos++; + *offs = pos; + return ROFF_RERUN; +} + +static int roff_tr(ROFF_ARGS) { const char *p, *first, *second; @@ -3170,7 +3615,7 @@ roff_tr(ROFF_ARGS) p = buf->buf + pos; if (*p == '\0') { - mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, ln, ppos, "tr"); + mandoc_msg(MANDOCERR_REQ_EMPTY, ln, ppos, "tr"); return ROFF_IGN; } @@ -3181,8 +3626,8 @@ roff_tr(ROFF_ARGS) if (*first == '\\') { esc = mandoc_escape(&p, NULL, NULL); if (esc == ESCAPE_ERROR) { - mandoc_msg(MANDOCERR_ESC_BAD, r->parse, - ln, (int)(p - buf->buf), first); + mandoc_msg(MANDOCERR_ESC_BAD, ln, + (int)(p - buf->buf), "%s", first); return ROFF_IGN; } fsz = (size_t)(p - first); @@ -3192,14 +3637,14 @@ roff_tr(ROFF_ARGS) if (*second == '\\') { esc = mandoc_escape(&p, NULL, NULL); if (esc == ESCAPE_ERROR) { - mandoc_msg(MANDOCERR_ESC_BAD, r->parse, - ln, (int)(p - buf->buf), second); + mandoc_msg(MANDOCERR_ESC_BAD, ln, + (int)(p - buf->buf), "%s", second); return ROFF_IGN; } ssz = (size_t)(p - second); } else if (*second == '\0') { - mandoc_vmsg(MANDOCERR_TR_ODD, r->parse, - ln, first - buf->buf, "tr %s", first); + mandoc_msg(MANDOCERR_TR_ODD, ln, + (int)(first - buf->buf), "tr %s", first); second = " "; p--; } @@ -3222,7 +3667,23 @@ roff_tr(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +/* + * Implementation of the .return request. + * There is no need to call roff_userret() from here. + * The read module will call that after rewinding the reader stack + * to the place from where the current macro was called. + */ +static int +roff_return(ROFF_ARGS) +{ + if (r->mstackpos >= 0) + return ROFF_IGN | ROFF_USERRET; + + mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "return"); + return ROFF_IGN; +} + +static int roff_rn(ROFF_ARGS) { const char *value; @@ -3235,7 +3696,7 @@ roff_rn(ROFF_ARGS) return ROFF_IGN; oldsz = roff_getname(r, &newn, ln, pos); - if (oldn[oldsz] == '\\' || *newn == '\0') + if (oldn[oldsz] == '\\' || oldn[oldsz] == '\t' || *newn == '\0') return ROFF_IGN; end = newn; @@ -3272,13 +3733,46 @@ roff_rn(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int +roff_shift(ROFF_ARGS) +{ + struct mctx *ctx; + int levels, i; + + levels = 1; + if (buf->buf[pos] != '\0' && + roff_evalnum(r, ln, buf->buf, &pos, &levels, 0) == 0) { + mandoc_msg(MANDOCERR_CE_NONUM, + ln, pos, "shift %s", buf->buf + pos); + levels = 1; + } + if (r->mstackpos < 0) { + mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "shift"); + return ROFF_IGN; + } + ctx = r->mstack + r->mstackpos; + if (levels > ctx->argc) { + mandoc_msg(MANDOCERR_SHIFT, + ln, pos, "%d, but max is %d", levels, ctx->argc); + levels = ctx->argc; + } + if (levels == 0) + return ROFF_IGN; + for (i = 0; i < levels; i++) + free(ctx->argv[i]); + ctx->argc -= levels; + for (i = 0; i < ctx->argc; i++) + ctx->argv[i] = ctx->argv[i + levels]; + return ROFF_IGN; +} + +static int roff_so(ROFF_ARGS) { char *name, *cp; name = buf->buf + pos; - mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name); + mandoc_msg(MANDOCERR_SO, ln, ppos, "so %s", name); /* * Handle `so'. Be EXTREMELY careful, as we shouldn't be @@ -3288,8 +3782,7 @@ roff_so(ROFF_ARGS) */ if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) { - mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos, - ".so %s", name); + mandoc_msg(MANDOCERR_SO_PATH, ln, ppos, ".so %s", name); buf->sz = mandoc_asprintf(&cp, ".sp\nSee the file %s.\n.sp", name) + 1; free(buf->buf); @@ -3304,154 +3797,69 @@ roff_so(ROFF_ARGS) /* --- user defined strings and macros ------------------------------------ */ -static enum rofferr +static int roff_userdef(ROFF_ARGS) { - const char *arg[16], *ap; - char *cp, *n1, *n2; - int expand_count, i, ib, ie; - size_t asz, rsz; + struct mctx *ctx; + char *arg, *ap, *dst, *src; + size_t sz; - /* - * Collect pointers to macro argument strings - * and NUL-terminate them. - */ + /* Initialize a new macro stack context. */ - r->argc = 0; - cp = buf->buf + pos; - for (i = 0; i < 16; i++) { - if (*cp == '\0') - arg[i] = ""; - else { - arg[i] = mandoc_getarg(r->parse, &cp, ln, &pos); - r->argc = i + 1; - } + if (++r->mstackpos == r->mstacksz) { + r->mstack = mandoc_recallocarray(r->mstack, + r->mstacksz, r->mstacksz + 8, sizeof(*r->mstack)); + r->mstacksz += 8; } + ctx = r->mstack + r->mstackpos; + ctx->argsz = 0; + ctx->argc = 0; + ctx->argv = NULL; /* - * Expand macro arguments. + * Collect pointers to macro argument strings, + * NUL-terminating them and escaping quotes. */ - buf->sz = strlen(r->current_string) + 1; - n1 = n2 = cp = mandoc_malloc(buf->sz); - memcpy(n1, r->current_string, buf->sz); - expand_count = 0; - while (*cp != '\0') { - - /* Scan ahead for the next argument invocation. */ - - if (*cp++ != '\\') - continue; - if (*cp++ != '$') - continue; - if (*cp == '*') { /* \\$* inserts all arguments */ - ib = 0; - ie = r->argc - 1; - } else { /* \\$1 .. \\$9 insert one argument */ - ib = ie = *cp - '1'; - if (ib < 0 || ib > 8) - continue; - } - cp -= 2; - - /* - * Prevent infinite recursion. - */ - - if (cp >= n2) - expand_count = 1; - else if (++expand_count > EXPAND_LIMIT) { - mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, - ln, (int)(cp - n1), NULL); - free(buf->buf); - buf->buf = n1; - *offs = 0; - return ROFF_IGN; - } - - /* - * Determine the size of the expanded argument, - * taking escaping of quotes into account. - */ - - asz = ie > ib ? ie - ib : 0; /* for blanks */ - for (i = ib; i <= ie; i++) { - for (ap = arg[i]; *ap != '\0'; ap++) { - asz++; - if (*ap == '"') - asz += 3; - } - } - if (asz != 3) { - - /* - * Determine the size of the rest of the - * unexpanded macro, including the NUL. - */ - - rsz = buf->sz - (cp - n1) - 3; - - /* - * When shrinking, move before - * releasing the storage. - */ - - if (asz < 3) - memmove(cp + asz, cp + 3, rsz); - - /* - * Resize the storage for the macro - * and readjust the parse pointer. - */ - - buf->sz += asz - 3; - n2 = mandoc_realloc(n1, buf->sz); - cp = n2 + (cp - n1); - n1 = n2; - - /* - * When growing, make room - * for the expanded argument. - */ - - if (asz > 3) - memmove(cp + asz, cp + 3, rsz); + src = buf->buf + pos; + while (*src != '\0') { + if (ctx->argc == ctx->argsz) { + ctx->argsz += 8; + ctx->argv = mandoc_reallocarray(ctx->argv, + ctx->argsz, sizeof(*ctx->argv)); } - - /* Copy the expanded argument, escaping quotes. */ - - n2 = cp; - for (i = ib; i <= ie; i++) { - for (ap = arg[i]; *ap != '\0'; ap++) { - if (*ap == '"') { - memcpy(n2, "\\(dq", 4); - n2 += 4; - } else - *n2++ = *ap; - } - if (i < ie) - *n2++ = ' '; + arg = roff_getarg(r, &src, ln, &pos); + sz = 1; /* For the terminating NUL. */ + for (ap = arg; *ap != '\0'; ap++) + sz += *ap == '"' ? 4 : 1; + ctx->argv[ctx->argc++] = dst = mandoc_malloc(sz); + for (ap = arg; *ap != '\0'; ap++) { + if (*ap == '"') { + memcpy(dst, "\\(dq", 4); + dst += 4; + } else + *dst++ = *ap; } + *dst = '\0'; + free(arg); } - /* - * Replace the macro invocation - * by the expanded macro. - */ + /* Replace the macro invocation by the macro definition. */ free(buf->buf); - buf->buf = n1; + buf->buf = mandoc_strdup(r->current_string); + buf->sz = strlen(buf->buf) + 1; *offs = 0; return buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ? - ROFF_REPARSE : ROFF_APPEND; + ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND; } /* * Calling a high-level macro that was renamed with .rn. * r->current_string has already been set up by roff_parse(). */ -static enum rofferr +static int roff_renamed(ROFF_ARGS) { char *nbuf; @@ -3464,6 +3872,10 @@ roff_renamed(ROFF_ARGS) return ROFF_CONT; } +/* + * Measure the length in bytes of the roff identifier at *cpp + * and advance the pointer to the next word. + */ static size_t roff_getname(struct roff *r, char **cpp, int ln, int pos) { @@ -3471,31 +3883,34 @@ roff_getname(struct roff *r, char **cpp, int ln, int pos) size_t namesz; name = *cpp; - if ('\0' == *name) + if (*name == '\0') return 0; - /* Read until end of name and terminate it with NUL. */ + /* Advance cp to the byte after the end of the name. */ + for (cp = name; 1; cp++) { - if ('\0' == *cp || ' ' == *cp) { - namesz = cp - name; + namesz = cp - name; + if (*cp == '\0') + break; + if (*cp == ' ' || *cp == '\t') { + cp++; break; } - if ('\\' != *cp) + if (*cp != '\\') continue; - namesz = cp - name; - if ('{' == cp[1] || '}' == cp[1]) + if (cp[1] == '{' || cp[1] == '}') break; - cp++; - if ('\\' == *cp) + if (*++cp == '\\') continue; - mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos, + mandoc_msg(MANDOCERR_NAMESC, ln, pos, "%.*s", (int)(cp - name + 1), name); mandoc_escape((const char **)&cp, NULL, NULL); break; } /* Read past spaces. */ - while (' ' == *cp) + + while (*cp == ' ') cp++; *cpp = cp; @@ -3638,7 +4053,7 @@ roff_getstrn(struct roff *r, const char *name, size_t len, break; } } - if (r->man->macroset != MACROSET_MAN) { + if (r->man->meta.macroset != MACROSET_MAN) { for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) { if (strncmp(name, roff_name[tok], len) != 0 || roff_name[tok][len] != '\0') @@ -3652,7 +4067,7 @@ roff_getstrn(struct roff *r, const char *name, size_t len, } } } - if (r->man->macroset != MACROSET_MDOC) { + if (r->man->meta.macroset != MACROSET_MDOC) { for (tok = MAN_TH; tok < MAN_MAX; tok++) { if (strncmp(name, roff_name[tok], len) != 0 || roff_name[tok][len] != '\0') @@ -3783,7 +4198,7 @@ roff_strdup(const struct roff *r, const char *p) /* * We bail out on bad escapes. * No need to warn: we already did so when - * roff_res() was called. + * roff_expand() was called. */ sz = (int)(p - pp); res = mandoc_realloc(res, ssz + sz + 1); diff --git a/usr/src/cmd/mandoc/roff.h b/usr/src/cmd/mandoc/roff.h index f0da74bd9a..49b0927513 100644 --- a/usr/src/cmd/mandoc/roff.h +++ b/usr/src/cmd/mandoc/roff.h @@ -1,7 +1,7 @@ -/* $Id: roff.h,v 1.59 2018/04/11 17:11:13 schwarze Exp $ */ +/* $Id: roff.h,v 1.69 2019/03/04 13:01:57 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013-2015, 2017-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,11 +14,15 @@ * 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 data types for all syntax trees and related functions. */ struct ohash; struct mdoc_arg; union mdoc_data; +struct tbl_span; +struct eqn_box; enum roff_macroset { MACROSET_NONE = 0, @@ -69,9 +73,11 @@ enum roff_type { enum roff_tok { ROFF_br = 0, ROFF_ce, + ROFF_fi, ROFF_ft, ROFF_ll, ROFF_mc, + ROFF_nf, ROFF_po, ROFF_rj, ROFF_sp, @@ -156,7 +162,6 @@ enum roff_tok { ROFF_fcolor, ROFF_fdeferlig, ROFF_feature, - /* MAN_fi; ignored in mdoc(7) */ ROFF_fkern, ROFF_fl, ROFF_flig, @@ -216,7 +221,6 @@ enum roff_tok { ROFF_mso, ROFF_na, ROFF_ne, - /* MAN_nf; ignored in mdoc(7) */ ROFF_nh, ROFF_nhychar, ROFF_nm, @@ -438,6 +442,7 @@ enum roff_tok { MAN_SH, MAN_SS, MAN_TP, + MAN_TQ, MAN_LP, MAN_PP, MAN_P, @@ -454,8 +459,6 @@ enum roff_tok { MAN_I, MAN_IR, MAN_RI, - MAN_nf, - MAN_fi, MAN_RE, MAN_RS, MAN_DT, @@ -463,6 +466,8 @@ enum roff_tok { MAN_PD, MAN_AT, MAN_in, + MAN_SY, + MAN_YS, MAN_OP, MAN_EX, MAN_EE, @@ -473,11 +478,6 @@ enum roff_tok { MAN_MAX }; -enum roff_next { - ROFF_NEXT_SIBLING = 0, - ROFF_NEXT_CHILD -}; - /* * Indicates that a BODY's formatting has ended, but * the scope is still open. Used for badly nested blocks. @@ -487,6 +487,12 @@ enum mdoc_endbody { ENDBODY_SPACE /* Is broken: append a space. */ }; +enum mandoc_os { + MANDOC_OS_OTHER = 0, + MANDOC_OS_NETBSD, + MANDOC_OS_OPENBSD +}; + struct roff_node { struct roff_node *parent; /* Parent AST node. */ struct roff_node *child; /* First child AST node. */ @@ -499,21 +505,22 @@ struct roff_node { struct mdoc_arg *args; /* BLOCK/ELEM */ union mdoc_data *norm; /* Normalized arguments. */ char *string; /* TEXT */ - const struct tbl_span *span; /* TBL */ + struct tbl_span *span; /* TBL */ struct eqn_box *eqn; /* EQN */ int line; /* Input file line number. */ int pos; /* Input file column number. */ int flags; #define NODE_VALID (1 << 0) /* Has been validated. */ #define NODE_ENDED (1 << 1) /* Gone past body end mark. */ -#define NODE_EOS (1 << 2) /* At sentence boundary. */ +#define NODE_BROKEN (1 << 2) /* Must validate parent when ending. */ #define NODE_LINE (1 << 3) /* First macro/text on line. */ -#define NODE_SYNPRETTY (1 << 4) /* SYNOPSIS-style formatting. */ -#define NODE_BROKEN (1 << 5) /* Must validate parent when ending. */ -#define NODE_DELIMO (1 << 6) -#define NODE_DELIMC (1 << 7) -#define NODE_NOSRC (1 << 8) /* Generated node, not in input file. */ -#define NODE_NOPRT (1 << 9) /* Shall not print anything. */ +#define NODE_DELIMO (1 << 4) +#define NODE_DELIMC (1 << 5) +#define NODE_EOS (1 << 6) /* At sentence boundary. */ +#define NODE_SYNPRETTY (1 << 7) /* SYNOPSIS-style formatting. */ +#define NODE_NOFILL (1 << 8) /* Fill mode switched off. */ +#define NODE_NOSRC (1 << 9) /* Generated node, not in input file. */ +#define NODE_NOPRT (1 << 10) /* Shall not print anything. */ int prev_font; /* Before entering this node. */ int aux; /* Decoded node data, type-dependent. */ enum roff_tok tok; /* Request or macro ID. */ @@ -523,6 +530,7 @@ struct roff_node { }; struct roff_meta { + struct roff_node *first; /* The first node parsed. */ char *msec; /* Manual section, usually a digit. */ char *vol; /* Manual volume title. */ char *os; /* Operating system. */ @@ -530,51 +538,15 @@ struct roff_meta { char *title; /* Manual title, usually CAPS. */ char *name; /* Leading manual name. */ char *date; /* Normalized date. */ + char *sodest; /* .so target file name or NULL. */ int hasbody; /* Document is not empty. */ int rcsids; /* Bits indexed by enum mandoc_os. */ enum mandoc_os os_e; /* Operating system. */ -}; - -struct roff_man { - struct roff_meta meta; /* Document meta-data. */ - struct mparse *parse; /* Parse pointer. */ - struct roff *roff; /* Roff parser state data. */ - struct ohash *mdocmac; /* Mdoc macro lookup table. */ - struct ohash *manmac; /* Man macro lookup table. */ - const char *os_s; /* Default operating system. */ - struct roff_node *first; /* The first node parsed. */ - struct roff_node *last; /* The last node parsed. */ - struct roff_node *last_es; /* The most recent Es node. */ - int quick; /* Abort parse early. */ - int flags; /* Parse flags. */ -#define MDOC_LITERAL (1 << 1) /* In a literal scope. */ -#define MDOC_PBODY (1 << 2) /* In the document body. */ -#define MDOC_NEWLINE (1 << 3) /* First macro/text in a line. */ -#define MDOC_PHRASE (1 << 4) /* In a Bl -column phrase. */ -#define MDOC_PHRASELIT (1 << 5) /* Literal within a phrase. */ -#define MDOC_FREECOL (1 << 6) /* `It' invocation should close. */ -#define MDOC_SYNOPSIS (1 << 7) /* SYNOPSIS-style formatting. */ -#define MDOC_KEEP (1 << 8) /* In a word keep. */ -#define MDOC_SMOFF (1 << 9) /* Spacing is off. */ -#define MDOC_NODELIMC (1 << 10) /* Disable closing delimiter handling. */ -#define MAN_ELINE (1 << 11) /* Next-line element scope. */ -#define MAN_BLINE (1 << 12) /* Next-line block scope. */ -#define MDOC_PHRASEQF (1 << 13) /* Quote first word encountered. */ -#define MDOC_PHRASEQL (1 << 14) /* Quote last word of this phrase. */ -#define MDOC_PHRASEQN (1 << 15) /* Quote first word of the next phrase. */ -#define MAN_LITERAL MDOC_LITERAL -#define MAN_NEWLINE MDOC_NEWLINE enum roff_macroset macroset; /* Kind of high-level macros used. */ - enum roff_sec lastsec; /* Last section seen. */ - enum roff_sec lastnamed; /* Last standard section seen. */ - enum roff_next next; /* Where to put the next node. */ }; extern const char *const *roff_name; +int arch_valid(const char *, enum mandoc_os); void deroff(char **, const struct roff_node *); -struct ohash *roffhash_alloc(enum roff_tok, enum roff_tok); -enum roff_tok roffhash_find(struct ohash *, const char *, size_t); -void roffhash_free(struct ohash *); -void roff_validate(struct roff_man *); diff --git a/usr/src/cmd/mandoc/roff_html.c b/usr/src/cmd/mandoc/roff_html.c index 6a06c0d878..02b8beea3b 100644 --- a/usr/src/cmd/mandoc/roff_html.c +++ b/usr/src/cmd/mandoc/roff_html.c @@ -1,7 +1,7 @@ -/* $Id: roff_html.c,v 1.12 2018/06/25 14:53:58 schwarze Exp $ */ +/* $Id: roff_html.c,v 1.19 2019/01/07 07:26:29 schwarze Exp $ */ /* * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 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 @@ -18,7 +18,8 @@ #include <sys/types.h> #include <assert.h> -#include <stddef.h> +#include <stdio.h> +#include <string.h> #include "mandoc.h" #include "roff.h" @@ -31,14 +32,19 @@ typedef void (*roff_html_pre_fp)(ROFF_HTML_ARGS); static void roff_html_pre_br(ROFF_HTML_ARGS); static void roff_html_pre_ce(ROFF_HTML_ARGS); +static void roff_html_pre_fi(ROFF_HTML_ARGS); +static void roff_html_pre_ft(ROFF_HTML_ARGS); +static void roff_html_pre_nf(ROFF_HTML_ARGS); static void roff_html_pre_sp(ROFF_HTML_ARGS); static const roff_html_pre_fp roff_html_pre_acts[ROFF_MAX] = { roff_html_pre_br, /* br */ roff_html_pre_ce, /* ce */ - NULL, /* ft */ + roff_html_pre_fi, /* fi */ + roff_html_pre_ft, /* ft */ NULL, /* ll */ NULL, /* mc */ + roff_html_pre_nf, /* nf */ NULL, /* po */ roff_html_pre_ce, /* rj */ roff_html_pre_sp, /* sp */ @@ -58,11 +64,7 @@ roff_html_pre(struct html *h, const struct roff_node *n) static void roff_html_pre_br(ROFF_HTML_ARGS) { - struct tag *t; - - t = print_otag(h, TAG_DIV, ""); - print_text(h, "\\~"); /* So the div isn't empty. */ - print_tagq(h, t); + print_otag(h, TAG_BR, ""); } static void @@ -80,7 +82,36 @@ roff_html_pre_ce(ROFF_HTML_ARGS) } static void +roff_html_pre_fi(ROFF_HTML_ARGS) +{ + if (html_fillmode(h, TOKEN_NONE) == ROFF_fi) + print_otag(h, TAG_BR, ""); +} + +static void +roff_html_pre_ft(ROFF_HTML_ARGS) +{ + const char *cp; + + cp = n->child->string; + print_metaf(h, mandoc_font(cp, (int)strlen(cp))); +} + +static void +roff_html_pre_nf(ROFF_HTML_ARGS) +{ + if (html_fillmode(h, TOKEN_NONE) == ROFF_nf) + print_otag(h, TAG_BR, ""); +} + +static void roff_html_pre_sp(ROFF_HTML_ARGS) { - print_paragraph(h); + if (html_fillmode(h, TOKEN_NONE) == ROFF_nf) { + h->col++; + print_endline(h); + } else { + html_close_paragraph(h); + print_otag(h, TAG_P, "c", "Pp"); + } } diff --git a/usr/src/cmd/mandoc/roff_int.h b/usr/src/cmd/mandoc/roff_int.h index 48996dce53..d033f86ace 100644 --- a/usr/src/cmd/mandoc/roff_int.h +++ b/usr/src/cmd/mandoc/roff_int.h @@ -1,7 +1,7 @@ -/* $Id: roff_int.h,v 1.9 2017/07/08 17:52:50 schwarze Exp $ */ +/* $Id: roff_int.h,v 1.16 2019/01/05 00:36:50 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013-2015, 2017-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,8 +14,54 @@ * 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. + * + * Parser internals shared by multiple parsers. */ +struct ohash; +struct roff_node; +struct roff_meta; +struct roff; +struct mdoc_arg; + +enum roff_next { + ROFF_NEXT_SIBLING = 0, + ROFF_NEXT_CHILD +}; + +struct roff_man { + struct roff_meta meta; /* Public parse results. */ + struct roff *roff; /* Roff parser state data. */ + struct ohash *mdocmac; /* Mdoc macro lookup table. */ + struct ohash *manmac; /* Man macro lookup table. */ + const char *os_s; /* Default operating system. */ + struct roff_node *last; /* The last node parsed. */ + struct roff_node *last_es; /* The most recent Es node. */ + int quick; /* Abort parse early. */ + int flags; /* Parse flags. */ +#define ROFF_NOFILL (1 << 1) /* Fill mode switched off. */ +#define MDOC_PBODY (1 << 2) /* In the document body. */ +#define MDOC_NEWLINE (1 << 3) /* First macro/text in a line. */ +#define MDOC_PHRASE (1 << 4) /* In a Bl -column phrase. */ +#define MDOC_PHRASELIT (1 << 5) /* Literal within a phrase. */ +#define MDOC_FREECOL (1 << 6) /* `It' invocation should close. */ +#define MDOC_SYNOPSIS (1 << 7) /* SYNOPSIS-style formatting. */ +#define MDOC_KEEP (1 << 8) /* In a word keep. */ +#define MDOC_SMOFF (1 << 9) /* Spacing is off. */ +#define MDOC_NODELIMC (1 << 10) /* Disable closing delimiter handling. */ +#define MAN_ELINE (1 << 11) /* Next-line element scope. */ +#define MAN_BLINE (1 << 12) /* Next-line block scope. */ +#define MDOC_PHRASEQF (1 << 13) /* Quote first word encountered. */ +#define MDOC_PHRASEQL (1 << 14) /* Quote last word of this phrase. */ +#define MDOC_PHRASEQN (1 << 15) /* Quote first word of the next phrase. */ +#define ROFF_NONOFILL (1 << 16) /* Temporarily suspend no-fill mode. */ +#define MAN_NEWLINE MDOC_NEWLINE + enum roff_sec lastsec; /* Last section seen. */ + enum roff_sec lastnamed; /* Last standard section seen. */ + enum roff_next next; /* Where to put the next node. */ +}; + + struct roff_node *roff_node_alloc(struct roff_man *, int, int, enum roff_type, int); void roff_node_append(struct roff_man *, struct roff_node *); @@ -26,9 +72,17 @@ struct roff_node *roff_block_alloc(struct roff_man *, int, int, int); struct roff_node *roff_head_alloc(struct roff_man *, int, int, int); struct roff_node *roff_body_alloc(struct roff_man *, int, int, int); void roff_node_unlink(struct roff_man *, struct roff_node *); +void roff_node_relink(struct roff_man *, struct roff_node *); void roff_node_free(struct roff_node *); void roff_node_delete(struct roff_man *, struct roff_node *); +struct ohash *roffhash_alloc(enum roff_tok, enum roff_tok); +enum roff_tok roffhash_find(struct ohash *, const char *, size_t); +void roffhash_free(struct ohash *); + +void roff_state_reset(struct roff_man *); +void roff_validate(struct roff_man *); + /* * Functions called from roff.c need to be declared here, * not in libmdoc.h or libman.h, even if they are specific diff --git a/usr/src/cmd/mandoc/roff_term.c b/usr/src/cmd/mandoc/roff_term.c index b5ec764963..f10bb61d2c 100644 --- a/usr/src/cmd/mandoc/roff_term.c +++ b/usr/src/cmd/mandoc/roff_term.c @@ -1,6 +1,6 @@ -/* $Id: roff_term.c,v 1.14 2017/06/24 14:38:33 schwarze Exp $ */ +/* $Id: roff_term.c,v 1.19 2019/01/04 03:24:33 schwarze Exp $ */ /* - * Copyright (c) 2010, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010,2014,2015,2017-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 @@ -17,7 +17,8 @@ #include <sys/types.h> #include <assert.h> -#include <stddef.h> +#include <stdio.h> +#include <string.h> #include "mandoc.h" #include "roff.h" @@ -41,9 +42,11 @@ static void roff_term_pre_ti(ROFF_TERM_ARGS); static const roff_term_pre_fp roff_term_pre_acts[ROFF_MAX] = { roff_term_pre_br, /* br */ roff_term_pre_ce, /* ce */ + roff_term_pre_br, /* fi */ roff_term_pre_ft, /* ft */ roff_term_pre_ll, /* ll */ roff_term_pre_mc, /* mc */ + roff_term_pre_br, /* nf */ roff_term_pre_po, /* po */ roff_term_pre_ce, /* rj */ roff_term_pre_sp, /* sp */ @@ -66,7 +69,9 @@ roff_term_pre_br(ROFF_TERM_ARGS) if (p->flags & TERMP_BRIND) { p->tcol->offset = p->tcol->rmargin; p->tcol->rmargin = p->maxrmargin; + p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); + p->flags |= TERMP_NOSPACE; } } @@ -74,27 +79,16 @@ static void roff_term_pre_ce(ROFF_TERM_ARGS) { const struct roff_node *nc1, *nc2; - size_t len, lm; roff_term_pre_br(p, n); - lm = p->tcol->offset; + p->flags |= n->tok == ROFF_ce ? TERMP_CENTER : TERMP_RIGHT; nc1 = n->child->next; while (nc1 != NULL) { nc2 = nc1; - len = 0; do { - if (nc2->type == ROFFT_TEXT) { - if (len) - len++; - len += term_strlen(p, nc2->string); - } nc2 = nc2->next; } while (nc2 != NULL && (nc2->type != ROFFT_TEXT || (nc2->flags & NODE_LINE) == 0)); - p->tcol->offset = len >= p->tcol->rmargin ? 0 : - lm + len >= p->tcol->rmargin ? p->tcol->rmargin - len : - n->tok == ROFF_rj ? p->tcol->rmargin - len : - (lm + p->tcol->rmargin - len) / 2; while (nc1 != nc2) { if (nc1->type == ROFFT_TEXT) term_word(p, nc1->string); @@ -105,28 +99,30 @@ roff_term_pre_ce(ROFF_TERM_ARGS) p->flags |= TERMP_NOSPACE; term_flushln(p); } - p->tcol->offset = lm; + p->flags &= ~(TERMP_CENTER | TERMP_RIGHT); } static void roff_term_pre_ft(ROFF_TERM_ARGS) { - switch (*n->child->string) { - case '4': - case '3': - case 'B': + const char *cp; + + cp = n->child->string; + switch (mandoc_font(cp, (int)strlen(cp))) { + case ESCAPE_FONTBOLD: term_fontrepl(p, TERMFONT_BOLD); break; - case '2': - case 'I': + case ESCAPE_FONTITALIC: term_fontrepl(p, TERMFONT_UNDER); break; - case 'P': + case ESCAPE_FONTBI: + term_fontrepl(p, TERMFONT_BI); + break; + case ESCAPE_FONTPREV: term_fontlast(p); break; - case '1': - case 'C': - case 'R': + case ESCAPE_FONTROMAN: + case ESCAPE_FONTCW: term_fontrepl(p, TERMFONT_NONE); break; default: diff --git a/usr/src/cmd/mandoc/roff_validate.c b/usr/src/cmd/mandoc/roff_validate.c index 801e931485..9080f28797 100644 --- a/usr/src/cmd/mandoc/roff_validate.c +++ b/usr/src/cmd/mandoc/roff_validate.c @@ -1,6 +1,6 @@ -/* $Id: roff_validate.c,v 1.9 2017/06/14 22:51:25 schwarze Exp $ */ +/* $Id: roff_validate.c,v 1.18 2018/12/31 09:02:37 schwarze Exp $ */ /* - * Copyright (c) 2010, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2017, 2018 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 @@ -17,7 +17,8 @@ #include <sys/types.h> #include <assert.h> -#include <stddef.h> +#include <stdio.h> +#include <string.h> #include "mandoc.h" #include "roff.h" @@ -28,17 +29,23 @@ typedef void (*roff_valid_fp)(ROFF_VALID_ARGS); +static void roff_valid_br(ROFF_VALID_ARGS); +static void roff_valid_fi(ROFF_VALID_ARGS); static void roff_valid_ft(ROFF_VALID_ARGS); +static void roff_valid_nf(ROFF_VALID_ARGS); +static void roff_valid_sp(ROFF_VALID_ARGS); static const roff_valid_fp roff_valids[ROFF_MAX] = { - NULL, /* br */ + roff_valid_br, /* br */ NULL, /* ce */ + roff_valid_fi, /* fi */ roff_valid_ft, /* ft */ NULL, /* ll */ NULL, /* mc */ + roff_valid_nf, /* nf */ NULL, /* po */ NULL, /* rj */ - NULL, /* sp */ + roff_valid_sp, /* sp */ NULL, /* ta */ NULL, /* ti */ }; @@ -56,9 +63,45 @@ roff_validate(struct roff_man *man) } static void +roff_valid_br(ROFF_VALID_ARGS) +{ + struct roff_node *np; + + if (n->next != NULL && n->next->type == ROFFT_TEXT && + *n->next->string == ' ') { + mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos, + "br before text line with leading blank"); + roff_node_delete(man, n); + return; + } + + if ((np = n->prev) == NULL) + return; + + switch (np->tok) { + case ROFF_br: + case ROFF_sp: + case MDOC_Pp: + mandoc_msg(MANDOCERR_PAR_SKIP, + n->line, n->pos, "br after %s", roff_name[np->tok]); + roff_node_delete(man, n); + break; + default: + break; + } +} + +static void +roff_valid_fi(ROFF_VALID_ARGS) +{ + if ((n->flags & NODE_NOFILL) == 0) + mandoc_msg(MANDOCERR_FI_SKIP, n->line, n->pos, "fi"); +} + +static void roff_valid_ft(ROFF_VALID_ARGS) { - char *cp; + const char *cp; if (n->child == NULL) { man->next = ROFF_NEXT_CHILD; @@ -68,30 +111,39 @@ roff_valid_ft(ROFF_VALID_ARGS) } cp = n->child->string; - switch (*cp) { - case '1': - case '2': - case '3': - case '4': - case 'I': - case 'P': - case 'R': - if (cp[1] == '\0') - return; - break; - case 'B': - if (cp[1] == '\0' || (cp[1] == 'I' && cp[2] == '\0')) - return; + if (mandoc_font(cp, (int)strlen(cp)) != ESCAPE_ERROR) + return; + mandoc_msg(MANDOCERR_FT_BAD, n->line, n->pos, "ft %s", cp); + roff_node_delete(man, n); +} + +static void +roff_valid_nf(ROFF_VALID_ARGS) +{ + if (n->flags & NODE_NOFILL) + mandoc_msg(MANDOCERR_NF_SKIP, n->line, n->pos, "nf"); +} + +static void +roff_valid_sp(ROFF_VALID_ARGS) +{ + struct roff_node *np; + + if ((np = n->prev) == NULL) + return; + + switch (np->tok) { + case ROFF_br: + mandoc_msg(MANDOCERR_PAR_SKIP, + np->line, np->pos, "br before sp"); + roff_node_delete(man, np); break; - case 'C': - if (cp[1] == 'W' && cp[2] == '\0') - return; + case MDOC_Pp: + mandoc_msg(MANDOCERR_PAR_SKIP, + n->line, n->pos, "sp after Pp"); + roff_node_delete(man, n); break; default: break; } - - mandoc_vmsg(MANDOCERR_FT_BAD, man->parse, - n->line, n->pos, "ft %s", cp); - roff_node_delete(man, n); } diff --git a/usr/src/cmd/mandoc/st.c b/usr/src/cmd/mandoc/st.c index d166566ece..c4d86e33fe 100644 --- a/usr/src/cmd/mandoc/st.c +++ b/usr/src/cmd/mandoc/st.c @@ -1,6 +1,6 @@ -/* $Id: st.c,v 1.14 2017/06/24 14:38:33 schwarze Exp $ */ +/* $Id: st.c,v 1.16 2018/12/14 01:18:26 schwarze Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2009, 2010 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 @@ -18,11 +18,11 @@ #include <sys/types.h> +#include <stdio.h> #include <string.h> #include "mandoc.h" #include "roff.h" -#include "mdoc.h" #include "libmdoc.h" #define LINE(x, y) \ @@ -31,8 +31,52 @@ const char * mdoc_a2st(const char *p) { - -#include "st.in" +LINE("-p1003.1-88", "IEEE Std 1003.1-1988 (\\(lqPOSIX.1\\(rq)") +LINE("-p1003.1-90", "IEEE Std 1003.1-1990 (\\(lqPOSIX.1\\(rq)") +LINE("-p1003.1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)") +LINE("-p1003.1-2001", "IEEE Std 1003.1-2001 (\\(lqPOSIX.1\\(rq)") +LINE("-p1003.1-2004", "IEEE Std 1003.1-2004 (\\(lqPOSIX.1\\(rq)") +LINE("-p1003.1-2008", "IEEE Std 1003.1-2008 (\\(lqPOSIX.1\\(rq)") +LINE("-p1003.1", "IEEE Std 1003.1 (\\(lqPOSIX.1\\(rq)") +LINE("-p1003.1b", "IEEE Std 1003.1b (\\(lqPOSIX.1b\\(rq)") +LINE("-p1003.1b-93", "IEEE Std 1003.1b-1993 (\\(lqPOSIX.1b\\(rq)") +LINE("-p1003.1c-95", "IEEE Std 1003.1c-1995 (\\(lqPOSIX.1c\\(rq)") +LINE("-p1003.1g-2000", "IEEE Std 1003.1g-2000 (\\(lqPOSIX.1g\\(rq)") +LINE("-p1003.1i-95", "IEEE Std 1003.1i-1995 (\\(lqPOSIX.1i\\(rq)") +LINE("-p1003.2", "IEEE Std 1003.2 (\\(lqPOSIX.2\\(rq)") +LINE("-p1003.2-92", "IEEE Std 1003.2-1992 (\\(lqPOSIX.2\\(rq)") +LINE("-p1003.2a-92", "IEEE Std 1003.2a-1992 (\\(lqPOSIX.2\\(rq)") +LINE("-isoC", "ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)") +LINE("-isoC-90", "ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)") +LINE("-isoC-amd1", "ISO/IEC 9899/AMD1:1995 (\\(lqISO\\~C90, Amendment 1\\(rq)") +LINE("-isoC-tcor1", "ISO/IEC 9899/TCOR1:1994 (\\(lqISO\\~C90, Technical Corrigendum 1\\(rq)") +LINE("-isoC-tcor2", "ISO/IEC 9899/TCOR2:1995 (\\(lqISO\\~C90, Technical Corrigendum 2\\(rq)") +LINE("-isoC-99", "ISO/IEC 9899:1999 (\\(lqISO\\~C99\\(rq)") +LINE("-isoC-2011", "ISO/IEC 9899:2011 (\\(lqISO\\~C11\\(rq)") +LINE("-iso9945-1-90", "ISO/IEC 9945-1:1990 (\\(lqPOSIX.1\\(rq)") +LINE("-iso9945-1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)") +LINE("-iso9945-2-93", "ISO/IEC 9945-2:1993 (\\(lqPOSIX.2\\(rq)") +LINE("-ansiC", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)") +LINE("-ansiC-89", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)") +LINE("-ieee754", "IEEE Std 754-1985") +LINE("-iso8802-3", "ISO 8802-3: 1989") +LINE("-iso8601", "ISO 8601") +LINE("-ieee1275-94", "IEEE Std 1275-1994 (\\(lqOpen Firmware\\(rq)") +LINE("-xpg3", "X/Open Portability Guide Issue\\~3 (\\(lqXPG3\\(rq)") +LINE("-xpg4", "X/Open Portability Guide Issue\\~4 (\\(lqXPG4\\(rq)") +LINE("-xpg4.2", "X/Open Portability Guide Issue\\~4, Version\\~2 (\\(lqXPG4.2\\(rq)") +LINE("-xbd5", "X/Open Base Definitions Issue\\~5 (\\(lqXBD5\\(rq)") +LINE("-xcu5", "X/Open Commands and Utilities Issue\\~5 (\\(lqXCU5\\(rq)") +LINE("-xsh4.2", "X/Open System Interfaces and Headers Issue\\~4, Version\\~2 (\\(lqXSH4.2\\(rq)") +LINE("-xsh5", "X/Open System Interfaces and Headers Issue\\~5 (\\(lqXSH5\\(rq)") +LINE("-xns5", "X/Open Networking Services Issue\\~5 (\\(lqXNS5\\(rq)") +LINE("-xns5.2", "X/Open Networking Services Issue\\~5.2 (\\(lqXNS5.2\\(rq)") +LINE("-xcurses4.2", "X/Open Curses Issue\\~4, Version\\~2 (\\(lqXCURSES4.2\\(rq)") +LINE("-susv1", "Version\\~1 of the Single UNIX Specification (\\(lqSUSv1\\(rq)") +LINE("-susv2", "Version\\~2 of the Single UNIX Specification (\\(lqSUSv2\\(rq)") +LINE("-susv3", "Version\\~3 of the Single UNIX Specification (\\(lqSUSv3\\(rq)") +LINE("-susv4", "Version\\~4 of the Single UNIX Specification (\\(lqSUSv4\\(rq)") +LINE("-svid4", "System\\~V Interface Definition, Fourth Edition (\\(lqSVID4\\(rq)") return NULL; } diff --git a/usr/src/cmd/mandoc/st.in b/usr/src/cmd/mandoc/st.in deleted file mode 100644 index 557a70d10d..0000000000 --- a/usr/src/cmd/mandoc/st.in +++ /dev/null @@ -1,76 +0,0 @@ -/* $Id: st.in,v 1.30 2018/04/05 09:17:26 schwarze Exp $ */ -/* - * Copyright (c) 2009, 2010 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 - * 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. - */ - -/* - * This file defines the .St macro arguments. If you add a new - * standard, make sure that the left-and side corresponds to the .St - * argument (like .St -p1003.1) and the right-hand side corresponds to - * the formatted output string. - * - * Be sure to escape strings. - * The non-breaking blanks prevent ending an output line right before - * a number. Groff prevent line breaks at the same places. - * - * REMEMBER TO ADD NEW STANDARDS TO MDOC.7! - */ - -LINE("-p1003.1-88", "IEEE Std 1003.1-1988 (\\(lqPOSIX.1\\(rq)") -LINE("-p1003.1-90", "IEEE Std 1003.1-1990 (\\(lqPOSIX.1\\(rq)") -LINE("-p1003.1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)") -LINE("-p1003.1-2001", "IEEE Std 1003.1-2001 (\\(lqPOSIX.1\\(rq)") -LINE("-p1003.1-2004", "IEEE Std 1003.1-2004 (\\(lqPOSIX.1\\(rq)") -LINE("-p1003.1-2008", "IEEE Std 1003.1-2008 (\\(lqPOSIX.1\\(rq)") -LINE("-p1003.1", "IEEE Std 1003.1 (\\(lqPOSIX.1\\(rq)") -LINE("-p1003.1b", "IEEE Std 1003.1b (\\(lqPOSIX.1b\\(rq)") -LINE("-p1003.1b-93", "IEEE Std 1003.1b-1993 (\\(lqPOSIX.1b\\(rq)") -LINE("-p1003.1c-95", "IEEE Std 1003.1c-1995 (\\(lqPOSIX.1c\\(rq)") -LINE("-p1003.1g-2000", "IEEE Std 1003.1g-2000 (\\(lqPOSIX.1g\\(rq)") -LINE("-p1003.1i-95", "IEEE Std 1003.1i-1995 (\\(lqPOSIX.1i\\(rq)") -LINE("-p1003.2", "IEEE Std 1003.2 (\\(lqPOSIX.2\\(rq)") -LINE("-p1003.2-92", "IEEE Std 1003.2-1992 (\\(lqPOSIX.2\\(rq)") -LINE("-p1003.2a-92", "IEEE Std 1003.2a-1992 (\\(lqPOSIX.2\\(rq)") -LINE("-isoC", "ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)") -LINE("-isoC-90", "ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)") -LINE("-isoC-amd1", "ISO/IEC 9899/AMD1:1995 (\\(lqISO\\~C90, Amendment 1\\(rq)") -LINE("-isoC-tcor1", "ISO/IEC 9899/TCOR1:1994 (\\(lqISO\\~C90, Technical Corrigendum 1\\(rq)") -LINE("-isoC-tcor2", "ISO/IEC 9899/TCOR2:1995 (\\(lqISO\\~C90, Technical Corrigendum 2\\(rq)") -LINE("-isoC-99", "ISO/IEC 9899:1999 (\\(lqISO\\~C99\\(rq)") -LINE("-isoC-2011", "ISO/IEC 9899:2011 (\\(lqISO\\~C11\\(rq)") -LINE("-iso9945-1-90", "ISO/IEC 9945-1:1990 (\\(lqPOSIX.1\\(rq)") -LINE("-iso9945-1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)") -LINE("-iso9945-2-93", "ISO/IEC 9945-2:1993 (\\(lqPOSIX.2\\(rq)") -LINE("-ansiC", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)") -LINE("-ansiC-89", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)") -LINE("-ieee754", "IEEE Std 754-1985") -LINE("-iso8802-3", "ISO 8802-3: 1989") -LINE("-iso8601", "ISO 8601") -LINE("-ieee1275-94", "IEEE Std 1275-1994 (\\(lqOpen Firmware\\(rq)") -LINE("-xpg3", "X/Open Portability Guide Issue\\~3 (\\(lqXPG3\\(rq)") -LINE("-xpg4", "X/Open Portability Guide Issue\\~4 (\\(lqXPG4\\(rq)") -LINE("-xpg4.2", "X/Open Portability Guide Issue\\~4, Version\\~2 (\\(lqXPG4.2\\(rq)") -LINE("-xbd5", "X/Open Base Definitions Issue\\~5 (\\(lqXBD5\\(rq)") -LINE("-xcu5", "X/Open Commands and Utilities Issue\\~5 (\\(lqXCU5\\(rq)") -LINE("-xsh4.2", "X/Open System Interfaces and Headers Issue\\~4, Version\\~2 (\\(lqXSH4.2\\(rq)") -LINE("-xsh5", "X/Open System Interfaces and Headers Issue\\~5 (\\(lqXSH5\\(rq)") -LINE("-xns5", "X/Open Networking Services Issue\\~5 (\\(lqXNS5\\(rq)") -LINE("-xns5.2", "X/Open Networking Services Issue\\~5.2 (\\(lqXNS5.2\\(rq)") -LINE("-xcurses4.2", "X/Open Curses Issue\\~4, Version\\~2 (\\(lqXCURSES4.2\\(rq)") -LINE("-susv1", "Version\\~1 of the Single UNIX Specification (\\(lqSUSv1\\(rq)") -LINE("-susv2", "Version\\~2 of the Single UNIX Specification (\\(lqSUSv2\\(rq)") -LINE("-susv3", "Version\\~3 of the Single UNIX Specification (\\(lqSUSv3\\(rq)") -LINE("-susv4", "Version\\~4 of the Single UNIX Specification (\\(lqSUSv4\\(rq)") -LINE("-svid4", "System\\~V Interface Definition, Fourth Edition (\\(lqSVID4\\(rq)") diff --git a/usr/src/cmd/mandoc/tag.c b/usr/src/cmd/mandoc/tag.c index c0832c4f74..473ea7b6f4 100644 --- a/usr/src/cmd/mandoc/tag.c +++ b/usr/src/cmd/mandoc/tag.c @@ -1,6 +1,6 @@ -/* $Id: tag.c,v 1.19 2018/02/23 16:47:10 schwarze Exp $ */ +/* $Id: tag.c,v 1.21 2018/11/22 11:30:23 schwarze Exp $ */ /* - * Copyright (c) 2015, 2016 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2015, 2016, 2018 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 @@ -18,6 +18,10 @@ #include <sys/types.h> +#if HAVE_ERR +#include <err.h> +#endif +#include <limits.h> #include <signal.h> #include <stddef.h> #include <stdint.h> @@ -121,41 +125,57 @@ fail: /* * Set the line number where a term is defined, - * unless it is already defined at a higher priority. + * unless it is already defined at a lower priority. */ void tag_put(const char *s, int prio, size_t line) { struct tag_entry *entry; + const char *se; size_t len; unsigned int slot; - /* Sanity checks. */ - if (tag_files.tfd <= 0) return; + if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e')) s += 2; - if (*s == '\0' || strchr(s, ' ') != NULL) + + /* + * Skip whitespace and whatever follows it, + * and if there is any, downgrade the priority. + */ + + len = strcspn(s, " \t"); + if (len == 0) return; - slot = ohash_qlookup(&tag_data, s); + se = s + len; + if (*se != '\0') + prio = INT_MAX; + + slot = ohash_qlookupi(&tag_data, s, &se); entry = ohash_find(&tag_data, slot); if (entry == NULL) { /* Build a new entry. */ - len = strlen(s) + 1; - entry = mandoc_malloc(sizeof(*entry) + len); + entry = mandoc_malloc(sizeof(*entry) + len + 1); memcpy(entry->s, s, len); + entry->s[len] = '\0'; entry->lines = NULL; entry->maxlines = entry->nlines = 0; ohash_insert(&tag_data, slot, entry); } else { - /* Handle priority 0 entries. */ + /* + * Lower priority numbers take precedence, + * but 0 is special. + * A tag with priority 0 is only used + * if the tag occurs exactly once. + */ if (prio == 0) { if (entry->prio == 0) @@ -199,6 +219,11 @@ tag_write(void) if (tag_files.tfd <= 0) return; + if (tag_files.tagname != NULL && ohash_find(&tag_data, + ohash_qlookup(&tag_data, tag_files.tagname)) == NULL) { + warnx("%s: no such tag", tag_files.tagname); + tag_files.tagname = NULL; + } stream = fdopen(tag_files.tfd, "w"); entry = ohash_first(&tag_data, &slot); while (entry != NULL) { diff --git a/usr/src/cmd/mandoc/tag.h b/usr/src/cmd/mandoc/tag.h index ab1388d798..bbd40b737e 100644 --- a/usr/src/cmd/mandoc/tag.h +++ b/usr/src/cmd/mandoc/tag.h @@ -1,4 +1,4 @@ -/* $Id: tag.h,v 1.7 2015/11/20 21:59:54 schwarze Exp $ */ +/* $Id: tag.h,v 1.8 2018/11/22 11:30:23 schwarze Exp $ */ /* * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> * @@ -18,6 +18,7 @@ struct tag_files { char ofn[20]; char tfn[20]; + char *tagname; int ofd; int tfd; pid_t tcpgid; diff --git a/usr/src/cmd/mandoc/tbl.c b/usr/src/cmd/mandoc/tbl.c index 3fb8e52a4c..036a117752 100644 --- a/usr/src/cmd/mandoc/tbl.c +++ b/usr/src/cmd/mandoc/tbl.c @@ -1,4 +1,4 @@ -/* $Id: tbl.c,v 1.42 2017/07/08 17:52:50 schwarze Exp $ */ +/* $Id: tbl.c,v 1.46 2018/12/14 06:33:14 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2011, 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -25,10 +25,12 @@ #include <string.h> #include <time.h> -#include "mandoc.h" #include "mandoc_aux.h" +#include "mandoc.h" +#include "tbl.h" #include "libmandoc.h" -#include "libroff.h" +#include "tbl_parse.h" +#include "tbl_int.h" void @@ -86,14 +88,15 @@ tbl_read(struct tbl_node *tbl, int ln, const char *p, int pos) } struct tbl_node * -tbl_alloc(int pos, int line, struct mparse *parse) +tbl_alloc(int pos, int line, struct tbl_node *last_tbl) { struct tbl_node *tbl; tbl = mandoc_calloc(1, sizeof(*tbl)); + if (last_tbl != NULL) + last_tbl->next = tbl; tbl->line = line; tbl->pos = pos; - tbl->parse = parse; tbl->part = TBL_PART_OPTS; tbl->opts.tab = '\t'; tbl->opts.decimal = '.'; @@ -103,76 +106,77 @@ tbl_alloc(int pos, int line, struct mparse *parse) void tbl_free(struct tbl_node *tbl) { + struct tbl_node *old_tbl; struct tbl_row *rp; struct tbl_cell *cp; struct tbl_span *sp; struct tbl_dat *dp; - while ((rp = tbl->first_row) != NULL) { - tbl->first_row = rp->next; - while (rp->first != NULL) { - cp = rp->first; - rp->first = cp->next; - free(cp->wstr); - free(cp); + while (tbl != NULL) { + while ((rp = tbl->first_row) != NULL) { + tbl->first_row = rp->next; + while (rp->first != NULL) { + cp = rp->first; + rp->first = cp->next; + free(cp->wstr); + free(cp); + } + free(rp); } - free(rp); - } - - while ((sp = tbl->first_span) != NULL) { - tbl->first_span = sp->next; - while (sp->first != NULL) { - dp = sp->first; - sp->first = dp->next; - free(dp->string); - free(dp); + while ((sp = tbl->first_span) != NULL) { + tbl->first_span = sp->next; + while (sp->first != NULL) { + dp = sp->first; + sp->first = dp->next; + free(dp->string); + free(dp); + } + free(sp); } - free(sp); + old_tbl = tbl; + tbl = tbl->next; + free(old_tbl); } - - free(tbl); } void tbl_restart(int line, int pos, struct tbl_node *tbl) { if (tbl->part == TBL_PART_CDATA) - mandoc_msg(MANDOCERR_TBLDATA_BLK, tbl->parse, - line, pos, "T&"); + mandoc_msg(MANDOCERR_TBLDATA_BLK, line, pos, "T&"); tbl->part = TBL_PART_LAYOUT; tbl->line = line; tbl->pos = pos; } -const struct tbl_span * +struct tbl_span * tbl_span(struct tbl_node *tbl) { struct tbl_span *span; - assert(tbl); span = tbl->current_span ? tbl->current_span->next : tbl->first_span; - if (span) + if (span != NULL) tbl->current_span = span; return span; } int -tbl_end(struct tbl_node *tbl) +tbl_end(struct tbl_node *tbl, int still_open) { struct tbl_span *sp; - if (tbl->part == TBL_PART_CDATA) - mandoc_msg(MANDOCERR_TBLDATA_BLK, tbl->parse, - tbl->line, tbl->pos, "TE"); + if (still_open) + mandoc_msg(MANDOCERR_BLK_NOEND, tbl->line, tbl->pos, "TS"); + else if (tbl->part == TBL_PART_CDATA) + mandoc_msg(MANDOCERR_TBLDATA_BLK, tbl->line, tbl->pos, "TE"); sp = tbl->first_span; while (sp != NULL && sp->first == NULL) sp = sp->next; if (sp == NULL) { - mandoc_msg(MANDOCERR_TBLDATA_NONE, tbl->parse, - tbl->line, tbl->pos, NULL); + mandoc_msg(MANDOCERR_TBLDATA_NONE, tbl->line, tbl->pos, NULL); return 0; } return 1; diff --git a/usr/src/cmd/mandoc/tbl.h b/usr/src/cmd/mandoc/tbl.h new file mode 100644 index 0000000000..365ae929ed --- /dev/null +++ b/usr/src/cmd/mandoc/tbl.h @@ -0,0 +1,122 @@ +/* $Id: tbl.h,v 1.1 2018/12/12 21:54:35 schwarze Exp $ */ +/* + * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2014, 2015, 2017, 2018 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 AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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. + */ + +struct tbl_opts { + int opts; +#define TBL_OPT_ALLBOX (1 << 0) /* Option "allbox". */ +#define TBL_OPT_BOX (1 << 1) /* Option "box". */ +#define TBL_OPT_CENTRE (1 << 2) /* Option "center". */ +#define TBL_OPT_DBOX (1 << 3) /* Option "doublebox". */ +#define TBL_OPT_EXPAND (1 << 4) /* Option "expand". */ +#define TBL_OPT_NOKEEP (1 << 5) /* Option "nokeep". */ +#define TBL_OPT_NOSPACE (1 << 6) /* Option "nospaces". */ +#define TBL_OPT_NOWARN (1 << 7) /* Option "nowarn". */ + int cols; /* Number of columns. */ + int lvert; /* Width of left vertical line. */ + int rvert; /* Width of right vertical line. */ + char tab; /* Option "tab": cell separator. */ + char decimal; /* Option "decimalpoint". */ +}; + +enum tbl_cellt { + TBL_CELL_CENTRE, /* c, C */ + TBL_CELL_RIGHT, /* r, R */ + TBL_CELL_LEFT, /* l, L */ + TBL_CELL_NUMBER, /* n, N */ + TBL_CELL_SPAN, /* s, S */ + TBL_CELL_LONG, /* a, A */ + TBL_CELL_DOWN, /* ^ */ + TBL_CELL_HORIZ, /* _, - */ + TBL_CELL_DHORIZ, /* = */ + TBL_CELL_MAX +}; + +/* + * A cell in a layout row. + */ +struct tbl_cell { + struct tbl_cell *next; /* Layout cell to the right. */ + char *wstr; /* Min width represented as a string. */ + size_t width; /* Minimum column width. */ + size_t spacing; /* To the right of the column. */ + int vert; /* Width of subsequent vertical line. */ + int col; /* Column number, starting from 0. */ + int flags; +#define TBL_CELL_BOLD (1 << 0) /* b, B, fB */ +#define TBL_CELL_ITALIC (1 << 1) /* i, I, fI */ +#define TBL_CELL_TALIGN (1 << 2) /* t, T */ +#define TBL_CELL_UP (1 << 3) /* u, U */ +#define TBL_CELL_BALIGN (1 << 4) /* d, D */ +#define TBL_CELL_WIGN (1 << 5) /* z, Z */ +#define TBL_CELL_EQUAL (1 << 6) /* e, E */ +#define TBL_CELL_WMAX (1 << 7) /* x, X */ + enum tbl_cellt pos; +}; + +/* + * A layout row. + */ +struct tbl_row { + struct tbl_row *next; /* Layout row below. */ + struct tbl_cell *first; /* Leftmost layout cell. */ + struct tbl_cell *last; /* Rightmost layout cell. */ + int vert; /* Width of left vertical line. */ +}; + +enum tbl_datt { + TBL_DATA_NONE, /* Uninitialized row. */ + TBL_DATA_DATA, /* Contains data rather than a line. */ + TBL_DATA_HORIZ, /* _: connecting horizontal line. */ + TBL_DATA_DHORIZ, /* =: connecting double horizontal line. */ + TBL_DATA_NHORIZ, /* \_: isolated horizontal line. */ + TBL_DATA_NDHORIZ /* \=: isolated double horizontal line. */ +}; + +/* + * A cell within a row of data. The "string" field contains the + * actual string value that's in the cell. The rest is layout. + */ +struct tbl_dat { + struct tbl_dat *next; /* Data cell to the right. */ + struct tbl_cell *layout; /* Associated layout cell. */ + char *string; /* Data, or NULL if not TBL_DATA_DATA. */ + int hspans; /* How many horizontal spans follow. */ + int vspans; /* How many vertical spans follow. */ + int block; /* T{ text block T} */ + enum tbl_datt pos; +}; + +enum tbl_spant { + TBL_SPAN_DATA, /* Contains data rather than a line. */ + TBL_SPAN_HORIZ, /* _: horizontal line. */ + TBL_SPAN_DHORIZ /* =: double horizontal line. */ +}; + +/* + * A row of data in a table. + */ +struct tbl_span { + struct tbl_opts *opts; /* Options for the table as a whole. */ + struct tbl_span *prev; /* Data row above. */ + struct tbl_span *next; /* Data row below. */ + struct tbl_row *layout; /* Associated layout row. */ + struct tbl_dat *first; /* Leftmost data cell. */ + struct tbl_dat *last; /* Rightmost data cell. */ + int line; /* Input file line number. */ + enum tbl_spant pos; +}; diff --git a/usr/src/cmd/mandoc/tbl_data.c b/usr/src/cmd/mandoc/tbl_data.c index ae1906ef73..9a58d32562 100644 --- a/usr/src/cmd/mandoc/tbl_data.c +++ b/usr/src/cmd/mandoc/tbl_data.c @@ -1,7 +1,7 @@ -/* $Id: tbl_data.c,v 1.45 2017/07/08 17:52:50 schwarze Exp $ */ +/* $Id: tbl_data.c,v 1.52 2019/02/09 16:00:39 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2015,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 @@ -21,14 +21,16 @@ #include <assert.h> #include <ctype.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> -#include "mandoc.h" #include "mandoc_aux.h" +#include "mandoc.h" +#include "tbl.h" #include "libmandoc.h" -#include "libroff.h" +#include "tbl_int.h" static void getdata(struct tbl_node *, struct tbl_span *, int, const char *, int *); @@ -40,10 +42,20 @@ static void getdata(struct tbl_node *tbl, struct tbl_span *dp, int ln, const char *p, int *pos) { - struct tbl_dat *dat; + struct tbl_dat *dat, *pdat; struct tbl_cell *cp; + struct tbl_span *pdp; int sv; + /* + * Determine the length of the string in the cell + * and advance the parse point to the end of the cell. + */ + + sv = *pos; + while (p[*pos] != '\0' && p[*pos] != tbl->opts.tab) + (*pos)++; + /* Advance to the next layout cell, skipping spanners. */ cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next; @@ -65,34 +77,68 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, cp->col = dp->layout->last->col + 1; dp->layout->last = cp; } else { - mandoc_msg(MANDOCERR_TBLDATA_EXTRA, tbl->parse, - ln, *pos, p + *pos); - while (p[*pos]) + mandoc_msg(MANDOCERR_TBLDATA_EXTRA, + ln, sv, "%s", p + sv); + while (p[*pos] != '\0') (*pos)++; return; } } - dat = mandoc_calloc(1, sizeof(*dat)); + dat = mandoc_malloc(sizeof(*dat)); dat->layout = cp; + dat->next = NULL; + dat->string = NULL; + dat->hspans = 0; + dat->vspans = 0; + dat->block = 0; dat->pos = TBL_DATA_NONE; - dat->spans = 0; + + /* + * Increment the number of vertical spans in a data cell above, + * if this cell vertically extends one or more cells above. + * The iteration must be done over data rows, + * not over layout rows, because one layout row + * can be reused for more than one data row. + */ + + if (cp->pos == TBL_CELL_DOWN || + (*pos - sv == 2 && p[sv] == '\\' && p[sv + 1] == '^')) { + pdp = dp; + while ((pdp = pdp->prev) != NULL) { + pdat = pdp->first; + while (pdat != NULL && + pdat->layout->col < dat->layout->col) + pdat = pdat->next; + if (pdat == NULL) + break; + if (pdat->layout->pos != TBL_CELL_DOWN && + strcmp(pdat->string, "\\^") != 0) { + pdat->vspans++; + break; + } + } + } + + /* + * Count the number of horizontal spans to the right of this cell. + * This is purely a matter of the layout, independent of the data. + */ + for (cp = cp->next; cp != NULL; cp = cp->next) if (cp->pos == TBL_CELL_SPAN) - dat->spans++; + dat->hspans++; else break; + /* Append the new data cell to the data row. */ + if (dp->last == NULL) dp->first = dat; else dp->last->next = dat; dp->last = dat; - sv = *pos; - while (p[*pos] && p[*pos] != tbl->opts.tab) - (*pos)++; - /* * Check for a continued-data scope opening. This consists of a * trailing `T{' at the end of the line. Subsequent lines, @@ -106,7 +152,7 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, dat->string = mandoc_strndup(p + sv, *pos - sv); - if (p[*pos]) + if (p[*pos] != '\0') (*pos)++; if ( ! strcmp(dat->string, "_")) @@ -125,7 +171,7 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, dat->layout->pos == TBL_CELL_DOWN) && dat->pos == TBL_DATA_DATA && *dat->string != '\0') mandoc_msg(MANDOCERR_TBLDATA_SPAN, - tbl->parse, ln, sv, dat->string); + ln, sv, "%s", dat->string); } void @@ -164,8 +210,8 @@ tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos) dat->string = mandoc_strdup(p + pos); if (dat->layout->pos == TBL_CELL_DOWN) - mandoc_msg(MANDOCERR_TBLDATA_SPAN, tbl->parse, - ln, pos, dat->string); + mandoc_msg(MANDOCERR_TBLDATA_SPAN, + ln, pos, "%s", dat->string); } static struct tbl_span * @@ -202,14 +248,27 @@ tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos) assert(rp != NULL); - if ( ! strcmp(p, "_")) { - sp = newspan(tbl, ln, rp); - sp->pos = TBL_SPAN_HORIZ; - return; - } else if ( ! strcmp(p, "=")) { - sp = newspan(tbl, ln, rp); - sp->pos = TBL_SPAN_DHORIZ; - return; + if (p[1] == '\0') { + switch (p[0]) { + case '.': + /* + * Empty request lines must be handled here + * and cannot be discarded in roff_parseln() + * because in the layout section, they + * are significant and end the layout. + */ + return; + case '_': + sp = newspan(tbl, ln, rp); + sp->pos = TBL_SPAN_HORIZ; + return; + case '=': + sp = newspan(tbl, ln, rp); + sp->pos = TBL_SPAN_DHORIZ; + return; + default: + break; + } } /* diff --git a/usr/src/cmd/mandoc/tbl_html.c b/usr/src/cmd/mandoc/tbl_html.c index b87804fda3..4ab6bed10b 100644 --- a/usr/src/cmd/mandoc/tbl_html.c +++ b/usr/src/cmd/mandoc/tbl_html.c @@ -1,7 +1,7 @@ -/* $Id: tbl_html.c,v 1.24 2018/06/25 13:45:57 schwarze Exp $ */ +/* $Id: tbl_html.c,v 1.32 2019/01/06 04:55:09 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2017, 2018 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 @@ -25,6 +25,7 @@ #include <string.h> #include "mandoc.h" +#include "tbl.h" #include "out.h" #include "html.h" @@ -79,6 +80,7 @@ html_tbl_sulen(const struct roffsu *su, void *arg) static void html_tblopen(struct html *h, const struct tbl_span *sp) { + html_close_paragraph(h); if (h->tbl.cols == NULL) { h->tbl.len = html_tbl_len; h->tbl.slen = html_tbl_strlen; @@ -86,7 +88,15 @@ html_tblopen(struct html *h, const struct tbl_span *sp) tblcalc(&h->tbl, sp, 0, 0); } assert(NULL == h->tblt); - h->tblt = print_otag(h, TAG_TABLE, "c", "tbl"); + h->tblt = print_otag(h, TAG_TABLE, "c?ss", "tbl", + "border", + sp->opts->opts & TBL_OPT_ALLBOX ? "1" : NULL, + "border-style", + sp->opts->opts & TBL_OPT_DBOX ? "double" : + sp->opts->opts & TBL_OPT_BOX ? "solid" : NULL, + "border-top-style", + sp->pos == TBL_SPAN_DHORIZ ? "double" : + sp->pos == TBL_SPAN_HORIZ ? "solid" : NULL); } void @@ -101,43 +111,138 @@ print_tblclose(struct html *h) void print_tbl(struct html *h, const struct tbl_span *sp) { - const struct tbl_dat *dp; - struct tag *tt; - int ic; - - /* Inhibit printing of spaces: we do padding ourselves. */ + const struct tbl_dat *dp; + const struct tbl_cell *cp; + const struct tbl_span *psp; + struct tag *tt; + const char *hspans, *vspans, *halign, *valign; + const char *bborder, *lborder, *rborder; + char hbuf[4], vbuf[4]; + int i; if (h->tblt == NULL) html_tblopen(h, sp); - assert(h->tblt); + /* + * Horizontal lines spanning the whole table + * are handled by previous or following table rows. + */ + + if (sp->pos != TBL_SPAN_DATA) + return; + + /* Inhibit printing of spaces: we do padding ourselves. */ h->flags |= HTML_NONOSPACE; h->flags |= HTML_NOSPACE; - tt = print_otag(h, TAG_TR, ""); + /* Draw a vertical line left of this row? */ - switch (sp->pos) { - case TBL_SPAN_HORIZ: - case TBL_SPAN_DHORIZ: - print_otag(h, TAG_TD, "?", "colspan", "0"); + switch (sp->layout->vert) { + case 2: + lborder = "double"; + break; + case 1: + lborder = "solid"; break; default: - dp = sp->first; - for (ic = 0; ic < sp->opts->cols; ic++) { - print_stagq(h, tt); - print_otag(h, TAG_TD, ""); - - if (dp == NULL || dp->layout->col > ic) - continue; - if (dp->layout->pos != TBL_CELL_DOWN) - if (dp->string != NULL) - print_text(h, dp->string); - dp = dp->next; - } + lborder = NULL; break; } + /* Draw a horizontal line below this row? */ + + bborder = NULL; + if ((psp = sp->next) != NULL) { + switch (psp->pos) { + case TBL_SPAN_DHORIZ: + bborder = "double"; + break; + case TBL_SPAN_HORIZ: + bborder = "solid"; + break; + default: + break; + } + } + + tt = print_otag(h, TAG_TR, "ss", + "border-left-style", lborder, + "border-bottom-style", bborder); + + for (dp = sp->first; dp != NULL; dp = dp->next) { + print_stagq(h, tt); + + /* + * Do not generate <td> elements for continuations + * of spanned cells. Larger <td> elements covering + * this space were already generated earlier. + */ + + cp = dp->layout; + if (cp->pos == TBL_CELL_SPAN || cp->pos == TBL_CELL_DOWN || + (dp->string != NULL && strcmp(dp->string, "\\^") == 0)) + continue; + + /* Determine the attribute values. */ + + if (dp->hspans > 0) { + (void)snprintf(hbuf, sizeof(hbuf), + "%d", dp->hspans + 1); + hspans = hbuf; + } else + hspans = NULL; + if (dp->vspans > 0) { + (void)snprintf(vbuf, sizeof(vbuf), + "%d", dp->vspans + 1); + vspans = vbuf; + } else + vspans = NULL; + + switch (cp->pos) { + case TBL_CELL_CENTRE: + halign = "center"; + break; + case TBL_CELL_RIGHT: + case TBL_CELL_NUMBER: + halign = "right"; + break; + default: + halign = NULL; + break; + } + if (cp->flags & TBL_CELL_TALIGN) + valign = "top"; + else if (cp->flags & TBL_CELL_BALIGN) + valign = "bottom"; + else + valign = NULL; + + for (i = dp->hspans; i > 0; i--) + cp = cp->next; + switch (cp->vert) { + case 2: + rborder = "double"; + break; + case 1: + rborder = "solid"; + break; + default: + rborder = NULL; + break; + } + + /* Print the element and the attributes. */ + + print_otag(h, TAG_TD, "??sss", + "colspan", hspans, "rowspan", vspans, + "vertical-align", valign, + "text-align", halign, + "border-right-style", rborder); + if (dp->string != NULL) + print_text(h, dp->string); + } + print_tagq(h, tt); h->flags &= ~HTML_NONOSPACE; @@ -148,5 +253,4 @@ print_tbl(struct html *h, const struct tbl_span *sp) h->tbl.cols = NULL; print_tblclose(h); } - } diff --git a/usr/src/cmd/mandoc/tbl_int.h b/usr/src/cmd/mandoc/tbl_int.h new file mode 100644 index 0000000000..fc32910fc7 --- /dev/null +++ b/usr/src/cmd/mandoc/tbl_int.h @@ -0,0 +1,47 @@ +/* $Id: tbl_int.h,v 1.2 2018/12/14 06:33:14 schwarze Exp $ */ +/* + * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2011,2013,2015,2017,2018 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 AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 of the tbl(7) parser. + * For use inside the tbl(7) parser only. + */ + +enum tbl_part { + TBL_PART_OPTS, /* In the first line, ends with semicolon. */ + TBL_PART_LAYOUT, /* In the layout section, ends with full stop. */ + TBL_PART_DATA, /* In the data section, ends with TE. */ + TBL_PART_CDATA /* In a T{ block, ends with T} */ +}; + +struct tbl_node { + struct tbl_opts opts; /* Options for the whole table. */ + struct tbl_node *next; /* Next table. */ + struct tbl_row *first_row; /* First layout row. */ + struct tbl_row *last_row; /* Last layout row. */ + struct tbl_span *first_span; /* First data row. */ + struct tbl_span *current_span; /* Data row being parsed. */ + struct tbl_span *last_span; /* Last data row. */ + int line; /* Line number in input file. */ + int pos; /* Column number in input file. */ + enum tbl_part part; /* Table section being parsed. */ +}; + + +void tbl_option(struct tbl_node *, int, const char *, int *); +void tbl_layout(struct tbl_node *, int, const char *, int); +void tbl_data(struct tbl_node *, int, const char *, int); +void tbl_cdata(struct tbl_node *, int, const char *, int); +void tbl_reset(struct tbl_node *); diff --git a/usr/src/cmd/mandoc/tbl_layout.c b/usr/src/cmd/mandoc/tbl_layout.c index 42fc0e8296..58599705c1 100644 --- a/usr/src/cmd/mandoc/tbl_layout.c +++ b/usr/src/cmd/mandoc/tbl_layout.c @@ -1,4 +1,4 @@ -/* $Id: tbl_layout.c,v 1.44 2017/06/27 18:25:02 schwarze Exp $ */ +/* $Id: tbl_layout.c,v 1.48 2018/12/14 05:18:03 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2012, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -21,14 +21,16 @@ #include <ctype.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> -#include "mandoc.h" #include "mandoc_aux.h" +#include "mandoc.h" +#include "tbl.h" #include "libmandoc.h" -#include "libroff.h" +#include "tbl_int.h" struct tbl_phrase { char name; @@ -84,8 +86,7 @@ mod: (*pos)++; goto mod; } - mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, tbl->parse, - ln, *pos, NULL); + mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, ln, *pos, NULL); return; } @@ -113,8 +114,7 @@ mod: cp->flags |= TBL_CELL_ITALIC; goto mod; case 'm': - mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, tbl->parse, - ln, *pos, "m"); + mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, ln, *pos, "m"); goto mod; case 'p': case 'v': @@ -157,10 +157,10 @@ mod: cp->vert++; else mandoc_msg(MANDOCERR_TBLLAYOUT_VERT, - tbl->parse, ln, *pos - 1, NULL); + ln, *pos - 1, NULL); goto mod; default: - mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse, + mandoc_msg(MANDOCERR_TBLLAYOUT_CHAR, ln, *pos - 1, "%c", p[*pos - 1]); goto mod; } @@ -173,7 +173,7 @@ mod: /* Support only one-character font-names for now. */ if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) { - mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse, + mandoc_msg(MANDOCERR_FT_BAD, ln, *pos, "TS %s", p + *pos - 1); if (p[*pos] != '\0') (*pos)++; @@ -195,7 +195,7 @@ mod: case 'R': goto mod; default: - mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse, + mandoc_msg(MANDOCERR_FT_BAD, ln, *pos - 1, "TS f%c", p[*pos - 1]); goto mod; } @@ -216,7 +216,7 @@ cell(struct tbl_node *tbl, struct tbl_row *rp, rp->vert++; else mandoc_msg(MANDOCERR_TBLLAYOUT_VERT, - tbl->parse, ln, *pos, NULL); + ln, *pos, NULL); } (*pos)++; } @@ -235,7 +235,7 @@ again: break; if (i == KEYS_MAX) { - mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse, + mandoc_msg(MANDOCERR_TBLLAYOUT_CHAR, ln, *pos, "%c", p[*pos]); (*pos)++; goto again; @@ -246,14 +246,12 @@ again: if (c == TBL_CELL_SPAN) { if (rp->last == NULL) - mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN, - tbl->parse, ln, *pos, NULL); + mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN, ln, *pos, NULL); else if (rp->last->pos == TBL_CELL_HORIZ || rp->last->pos == TBL_CELL_DHORIZ) c = rp->last->pos; } else if (c == TBL_CELL_DOWN && rp == tbl->first_row) - mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN, - tbl->parse, ln, *pos, NULL); + mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN, ln, *pos, NULL); (*pos)++; @@ -296,7 +294,7 @@ tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos) } if (tbl->first_row->first == NULL) { mandoc_msg(MANDOCERR_TBLLAYOUT_NONE, - tbl->parse, ln, pos, NULL); + ln, pos, NULL); cell_alloc(tbl, tbl->first_row, TBL_CELL_LEFT); if (tbl->opts.lvert < tbl->first_row->vert) diff --git a/usr/src/cmd/mandoc/tbl_opts.c b/usr/src/cmd/mandoc/tbl_opts.c index f2f5942ae6..e3a8373702 100644 --- a/usr/src/cmd/mandoc/tbl_opts.c +++ b/usr/src/cmd/mandoc/tbl_opts.c @@ -1,4 +1,4 @@ -/* $Id: tbl_opts.c,v 1.21 2015/09/26 00:54:04 schwarze Exp $ */ +/* $Id: tbl_opts.c,v 1.24 2018/12/14 05:18:03 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -25,8 +25,9 @@ #include <string.h> #include "mandoc.h" +#include "tbl.h" #include "libmandoc.h" -#include "libroff.h" +#include "tbl_int.h" #define KEY_DPOINT 0 #define KEY_DELIM 1 @@ -80,7 +81,7 @@ arg(struct tbl_node *tbl, int ln, const char *p, int *pos, int key) switch (key) { case KEY_DELIM: - mandoc_vmsg(MANDOCERR_TBLOPT_EQN, tbl->parse, + mandoc_msg(MANDOCERR_TBLOPT_EQN, ln, *pos, "%.*s", len, p + *pos); want = 2; break; @@ -102,12 +103,11 @@ arg(struct tbl_node *tbl, int ln, const char *p, int *pos, int key) } if (len == 0) - mandoc_msg(MANDOCERR_TBLOPT_NOARG, - tbl->parse, ln, *pos, keys[key].name); + mandoc_msg(MANDOCERR_TBLOPT_NOARG, ln, *pos, + "%s", keys[key].name); else if (want && len != want) - mandoc_vmsg(MANDOCERR_TBLOPT_ARGSZ, - tbl->parse, ln, *pos, "%s want %d have %d", - keys[key].name, want, len); + mandoc_msg(MANDOCERR_TBLOPT_ARGSZ, ln, *pos, + "%s want %d have %d", keys[key].name, want, len); *pos += len; if (p[*pos] == ')') @@ -141,8 +141,8 @@ tbl_option(struct tbl_node *tbl, int ln, const char *p, int *offs) len++; if (len == 0) { - mandoc_vmsg(MANDOCERR_TBLOPT_ALPHA, - tbl->parse, ln, pos, "%c", p[pos]); + mandoc_msg(MANDOCERR_TBLOPT_ALPHA, + ln, pos, "%c", p[pos]); pos++; continue; } @@ -156,7 +156,7 @@ tbl_option(struct tbl_node *tbl, int ln, const char *p, int *offs) i++; if (i == KEY_MAXKEYS) { - mandoc_vmsg(MANDOCERR_TBLOPT_BAD, tbl->parse, + mandoc_msg(MANDOCERR_TBLOPT_BAD, ln, pos, "%.*s", len, p + pos); pos += len; continue; diff --git a/usr/src/cmd/mandoc/tbl_parse.h b/usr/src/cmd/mandoc/tbl_parse.h new file mode 100644 index 0000000000..bb65536f28 --- /dev/null +++ b/usr/src/cmd/mandoc/tbl_parse.h @@ -0,0 +1,30 @@ +/* $Id: tbl_parse.h,v 1.2 2018/12/14 06:33:14 schwarze Exp $ */ +/* + * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2011, 2017 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 AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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. + * + * External interface of the tbl(7) parser. + * For use in the roff(7) and tbl(7) parsers only. + */ + +struct tbl_node; +struct tbl_span; + +struct tbl_node *tbl_alloc(int, int, struct tbl_node *); +int tbl_end(struct tbl_node *, int); +void tbl_free(struct tbl_node *); +void tbl_read(struct tbl_node *, int, const char *, int); +void tbl_restart(int, int, struct tbl_node *); +struct tbl_span *tbl_span(struct tbl_node *); diff --git a/usr/src/cmd/mandoc/tbl_term.c b/usr/src/cmd/mandoc/tbl_term.c index c154a0e9b9..a411107bd1 100644 --- a/usr/src/cmd/mandoc/tbl_term.c +++ b/usr/src/cmd/mandoc/tbl_term.c @@ -1,7 +1,7 @@ -/* $Id: tbl_term.c,v 1.57 2017/07/31 16:14:10 schwarze Exp $ */ +/* $Id: tbl_term.c,v 1.68 2019/02/09 21:02:47 schwarze Exp $ */ /* * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011,2012,2014,2015,2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-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 @@ -20,34 +20,121 @@ #include <sys/types.h> #include <assert.h> +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "mandoc.h" +#include "tbl.h" #include "out.h" #include "term.h" #define IS_HORIZ(cp) ((cp)->pos == TBL_CELL_HORIZ || \ (cp)->pos == TBL_CELL_DHORIZ) + static size_t term_tbl_len(size_t, void *); static size_t term_tbl_strlen(const char *, void *); static size_t term_tbl_sulen(const struct roffsu *, void *); -static void tbl_char(struct termp *, char, size_t); static void tbl_data(struct termp *, const struct tbl_opts *, const struct tbl_cell *, const struct tbl_dat *, const struct roffcol *); +static void tbl_direct_border(struct termp *, int, size_t); +static void tbl_fill_border(struct termp *, int, size_t); +static void tbl_fill_char(struct termp *, char, size_t); +static void tbl_fill_string(struct termp *, const char *, size_t); +static void tbl_hrule(struct termp *, const struct tbl_span *, + const struct tbl_span *, int); static void tbl_literal(struct termp *, const struct tbl_dat *, const struct roffcol *); static void tbl_number(struct termp *, const struct tbl_opts *, const struct tbl_dat *, const struct roffcol *); -static void tbl_hrule(struct termp *, const struct tbl_span *, int); static void tbl_word(struct termp *, const struct tbl_dat *); +/* + * The following border-character tables are indexed + * by ternary (3-based) numbers, as opposed to binary or decimal. + * Each ternary digit describes the line width in one direction: + * 0 means no line, 1 single or light line, 2 double or heavy line. + */ + +/* Positional values of the four directions. */ +#define BRIGHT 1 +#define BDOWN 3 +#define BLEFT (3 * 3) +#define BUP (3 * 3 * 3) +#define BHORIZ (BLEFT + BRIGHT) + +/* Code points to use for each combination of widths. */ +static const int borders_utf8[81] = { + 0x0020, 0x2576, 0x257a, /* 000 right */ + 0x2577, 0x250c, 0x250d, /* 001 down */ + 0x257b, 0x250e, 0x250f, /* 002 */ + 0x2574, 0x2500, 0x257c, /* 010 left */ + 0x2510, 0x252c, 0x252e, /* 011 left down */ + 0x2512, 0x2530, 0x2532, /* 012 */ + 0x2578, 0x257e, 0x2501, /* 020 left */ + 0x2511, 0x252d, 0x252f, /* 021 left down */ + 0x2513, 0x2531, 0x2533, /* 022 */ + 0x2575, 0x2514, 0x2515, /* 100 up */ + 0x2502, 0x251c, 0x251d, /* 101 up down */ + 0x257d, 0x251f, 0x2522, /* 102 */ + 0x2518, 0x2534, 0x2536, /* 110 up left */ + 0x2524, 0x253c, 0x253e, /* 111 all */ + 0x2527, 0x2541, 0x2546, /* 112 */ + 0x2519, 0x2535, 0x2537, /* 120 up left */ + 0x2525, 0x253d, 0x253f, /* 121 all */ + 0x252a, 0x2545, 0x2548, /* 122 */ + 0x2579, 0x2516, 0x2517, /* 200 up */ + 0x257f, 0x251e, 0x2521, /* 201 up down */ + 0x2503, 0x2520, 0x2523, /* 202 */ + 0x251a, 0x2538, 0x253a, /* 210 up left */ + 0x2526, 0x2540, 0x2544, /* 211 all */ + 0x2528, 0x2542, 0x254a, /* 212 */ + 0x251b, 0x2539, 0x253b, /* 220 up left */ + 0x2529, 0x2543, 0x2547, /* 221 all */ + 0x252b, 0x2549, 0x254b, /* 222 */ +}; + +/* ASCII approximations for these code points, compatible with groff. */ +static const int borders_ascii[81] = { + ' ', '-', '=', /* 000 right */ + '|', '+', '+', /* 001 down */ + '|', '+', '+', /* 002 */ + '-', '-', '=', /* 010 left */ + '+', '+', '+', /* 011 left down */ + '+', '+', '+', /* 012 */ + '=', '=', '=', /* 020 left */ + '+', '+', '+', /* 021 left down */ + '+', '+', '+', /* 022 */ + '|', '+', '+', /* 100 up */ + '|', '+', '+', /* 101 up down */ + '|', '+', '+', /* 102 */ + '+', '+', '+', /* 110 up left */ + '+', '+', '+', /* 111 all */ + '+', '+', '+', /* 112 */ + '+', '+', '+', /* 120 up left */ + '+', '+', '+', /* 121 all */ + '+', '+', '+', /* 122 */ + '|', '+', '+', /* 200 up */ + '|', '+', '+', /* 201 up down */ + '|', '+', '+', /* 202 */ + '+', '+', '+', /* 210 up left */ + '+', '+', '+', /* 211 all */ + '+', '+', '+', /* 212 */ + '+', '+', '+', /* 220 up left */ + '+', '+', '+', /* 221 all */ + '+', '+', '+', /* 222 */ +}; + +/* Either of the above according to the selected output encoding. */ +static const int *borders_locale; + + static size_t term_tbl_sulen(const struct roffsu *su, void *arg) { @@ -69,19 +156,22 @@ term_tbl_len(size_t sz, void *arg) return term_len((const struct termp *)arg, sz); } + void term_tbl(struct termp *tp, const struct tbl_span *sp) { - const struct tbl_cell *cp, *cpn, *cpp; + const struct tbl_cell *cp, *cpn, *cpp, *cps; const struct tbl_dat *dp; static size_t offset; + size_t save_offset; size_t coloff, tsz; - int ic, horiz, spans, vert, more; - char fc; + int hspans, ic, more; + int dvert, fc, horiz, lhori, rhori, uvert; /* Inhibit printing of spaces: we do padding ourselves. */ tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE; + save_offset = tp->tcol->offset; /* * The first time we're invoked for a given table block, @@ -89,6 +179,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) */ if (tp->tbl.cols == NULL) { + borders_locale = tp->enc == TERMENC_UTF8 ? + borders_utf8 : borders_ascii; + tp->tbl.len = term_tbl_len; tp->tbl.slen = term_tbl_strlen; tp->tbl.sulen = term_tbl_sulen; @@ -120,21 +213,24 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tsz += tp->tbl.cols[sp->opts->cols - 1].width; if (offset + tsz > tp->tcol->rmargin) tsz -= 1; - tp->tcol->offset = offset + tp->tcol->rmargin > tsz ? + offset = offset + tp->tcol->rmargin > tsz ? (offset + tp->tcol->rmargin - tsz) / 2 : 0; + tp->tcol->offset = offset; } /* Horizontal frame at the start of boxed tables. */ - if (sp->opts->opts & TBL_OPT_DBOX) - tbl_hrule(tp, sp, 3); + if (tp->enc == TERMENC_ASCII && + sp->opts->opts & TBL_OPT_DBOX) + tbl_hrule(tp, NULL, sp, TBL_OPT_DBOX); if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) - tbl_hrule(tp, sp, 2); + tbl_hrule(tp, NULL, sp, TBL_OPT_BOX); } /* Set up the columns. */ tp->flags |= TERMP_MULTICOL; + tp->tcol->offset = offset; horiz = 0; switch (sp->pos) { case TBL_SPAN_HORIZ: @@ -156,9 +252,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) /* Set up the data columns. */ dp = sp->first; - spans = 0; + hspans = 0; for (ic = 0; ic < sp->opts->cols; ic++) { - if (spans == 0) { + if (hspans == 0) { tp->tcol++; tp->tcol->offset = coloff; } @@ -166,13 +262,13 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tp->tcol->rmargin = coloff; if (ic + 1 < sp->opts->cols) coloff += tp->tbl.cols[ic].spacing; - if (spans) { - spans--; + if (hspans) { + hspans--; continue; } if (dp == NULL) continue; - spans = dp->spans; + hspans = dp->hspans; if (ic || sp->layout->first->pos != TBL_CELL_SPAN) dp = dp->next; } @@ -192,14 +288,14 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tp->tcol = tp->tcols; cp = cpn = sp->layout->first; dp = sp->first; - spans = 0; + hspans = 0; for (ic = 0; ic < sp->opts->cols; ic++) { if (cpn != NULL) { cp = cpn; cpn = cpn->next; } - if (spans) { - spans--; + if (hspans) { + hspans--; continue; } tp->tcol++; @@ -207,7 +303,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic); if (dp == NULL) continue; - spans = dp->spans; + hspans = dp->hspans; if (cp->pos != TBL_CELL_SPAN) dp = dp->next; } @@ -218,37 +314,43 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) /* Print the vertical frame at the start of each row. */ tp->tcol = tp->tcols; - fc = '\0'; - if (sp->layout->vert || - (sp->next != NULL && sp->next->layout->vert && - sp->next->pos == TBL_SPAN_DATA) || - (sp->prev != NULL && sp->prev->layout->vert && - (horiz || (IS_HORIZ(sp->layout->first) && - !IS_HORIZ(sp->prev->layout->first)))) || - sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)) - fc = horiz || IS_HORIZ(sp->layout->first) ? '+' : '|'; - else if (horiz && sp->opts->lvert) - fc = '-'; - if (fc != '\0') { + uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 : + sp->opts->opts & TBL_OPT_BOX ? 1 : 0; + if (sp->pos == TBL_SPAN_DATA && uvert < sp->layout->vert) + uvert = dvert = sp->layout->vert; + if (sp->next != NULL && sp->next->pos == TBL_SPAN_DATA && + dvert < sp->next->layout->vert) + dvert = sp->next->layout->vert; + if (sp->prev != NULL && uvert < sp->prev->layout->vert && + (horiz || (IS_HORIZ(sp->layout->first) && + !IS_HORIZ(sp->prev->layout->first)))) + uvert = sp->prev->layout->vert; + rhori = sp->pos == TBL_SPAN_DHORIZ || + (sp->first != NULL && sp->first->pos == TBL_DATA_DHORIZ) || + sp->layout->first->pos == TBL_CELL_DHORIZ ? 2 : + sp->pos == TBL_SPAN_HORIZ || + (sp->first != NULL && sp->first->pos == TBL_DATA_HORIZ) || + sp->layout->first->pos == TBL_CELL_HORIZ ? 1 : 0; + fc = BUP * uvert + BDOWN * dvert + BRIGHT * rhori; + if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) { (*tp->advance)(tp, tp->tcols->offset); - (*tp->letter)(tp, fc); - tp->viscol = tp->tcol->offset + 1; + tp->viscol = tp->tcol->offset; + tbl_direct_border(tp, fc, 1); } /* Print the data cells. */ more = 0; - if (horiz) { - tbl_hrule(tp, sp, 0); - term_flushln(tp); - } else { + if (horiz) + tbl_hrule(tp, sp->prev, sp, 0); + else { cp = sp->layout->first; cpn = sp->next == NULL ? NULL : sp->next->layout->first; cpp = sp->prev == NULL ? NULL : sp->prev->layout->first; dp = sp->first; - spans = 0; + hspans = 0; for (ic = 0; ic < sp->opts->cols; ic++) { /* @@ -257,25 +359,27 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) * and advance to next layout cell. */ + uvert = dvert = fc = 0; if (cp != NULL) { - vert = cp->vert; + cps = cp; + while (cps->next != NULL && + cps->next->pos == TBL_CELL_SPAN) + cps = cps->next; + if (sp->pos == TBL_SPAN_DATA) + uvert = dvert = cps->vert; switch (cp->pos) { case TBL_CELL_HORIZ: - fc = '-'; + fc = BHORIZ; break; case TBL_CELL_DHORIZ: - fc = '='; + fc = BHORIZ * 2; break; default: - fc = ' '; break; } - } else { - vert = 0; - fc = ' '; } if (cpp != NULL) { - if (vert == 0 && + if (uvert < cpp->vert && cp != NULL && ((IS_HORIZ(cp) && !IS_HORIZ(cpp)) || @@ -283,19 +387,31 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) cpp->next != NULL && IS_HORIZ(cp->next) && !IS_HORIZ(cpp->next)))) - vert = cpp->vert; + uvert = cpp->vert; cpp = cpp->next; } - if (vert == 0 && - sp->opts->opts & TBL_OPT_ALLBOX) - vert = 1; + if (sp->opts->opts & TBL_OPT_ALLBOX) { + if (uvert == 0) + uvert = 1; + if (dvert == 0) + dvert = 1; + } if (cpn != NULL) { - if (vert == 0) - vert = cpn->vert; + if (dvert == 0 || + (dvert < cpn->vert && + tp->enc == TERMENC_UTF8)) + dvert = cpn->vert; cpn = cpn->next; } - if (cp != NULL) - cp = cp->next; + + lhori = (cp != NULL && + cp->pos == TBL_CELL_DHORIZ) || + (dp != NULL && + dp->pos == TBL_DATA_DHORIZ) ? 2 : + (cp != NULL && + cp->pos == TBL_CELL_HORIZ) || + (dp != NULL && + dp->pos == TBL_DATA_HORIZ) ? 1 : 0; /* * Skip later cells in a span, @@ -303,12 +419,13 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) * and advance to next data cell. */ - if (spans) { - spans--; + if (hspans) { + hspans--; + cp = cp->next; continue; } if (dp != NULL) { - spans = dp->spans; + hspans = dp->hspans; if (ic || sp->layout->first->pos != TBL_CELL_SPAN) dp = dp->next; @@ -330,10 +447,16 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) * but not after the last column. */ - if (fc == ' ' && ((vert == 0 && - (cp == NULL || !IS_HORIZ(cp))) || - tp->tcol + 1 == tp->tcols + tp->lasttcol)) + if (fc == 0 && + ((uvert == 0 && dvert == 0 && + cp != NULL && (cp->next == NULL || + !IS_HORIZ(cp->next))) || + tp->tcol + 1 == + tp->tcols + tp->lasttcol)) { + if (cp != NULL) + cp = cp->next; continue; + } if (tp->viscol < tp->tcol->rmargin) { (*tp->advance)(tp, tp->tcol->rmargin @@ -341,77 +464,83 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tp->viscol = tp->tcol->rmargin; } while (tp->viscol < tp->tcol->rmargin + - tp->tbl.cols[ic].spacing / 2) { - (*tp->letter)(tp, fc); - tp->viscol++; - } + tp->tbl.cols[ic].spacing / 2) + tbl_direct_border(tp, + BHORIZ * lhori, 1); if (tp->tcol + 1 == tp->tcols + tp->lasttcol) continue; - if (fc == ' ' && cp != NULL) { - switch (cp->pos) { - case TBL_CELL_HORIZ: - fc = '-'; - break; - case TBL_CELL_DHORIZ: - fc = '='; - break; - default: - break; - } - } - if (tp->tbl.cols[ic].spacing) { - (*tp->letter)(tp, fc == ' ' ? '|' : - vert ? '+' : fc); - tp->viscol++; - } + if (cp != NULL) + cp = cp->next; + + rhori = (cp != NULL && + cp->pos == TBL_CELL_DHORIZ) || + (dp != NULL && + dp->pos == TBL_DATA_DHORIZ) ? 2 : + (cp != NULL && + cp->pos == TBL_CELL_HORIZ) || + (dp != NULL && + dp->pos == TBL_DATA_HORIZ) ? 1 : 0; + + if (tp->tbl.cols[ic].spacing) + tbl_direct_border(tp, + BLEFT * lhori + BRIGHT * rhori + + BUP * uvert + BDOWN * dvert, 1); + + if (tp->enc == TERMENC_UTF8) + uvert = dvert = 0; - if (fc != ' ') { - if (cp != NULL && - cp->pos == TBL_CELL_HORIZ) - fc = '-'; - else if (cp != NULL && - cp->pos == TBL_CELL_DHORIZ) - fc = '='; - else - fc = ' '; - } if (tp->tbl.cols[ic].spacing > 2 && - (vert > 1 || fc != ' ')) { - (*tp->letter)(tp, fc == ' ' ? '|' : - vert > 1 ? '+' : fc); - tp->viscol++; - } + (uvert > 1 || dvert > 1 || rhori)) + tbl_direct_border(tp, + BHORIZ * rhori + + BUP * (uvert > 1) + + BDOWN * (dvert > 1), 1); } } /* Print the vertical frame at the end of each row. */ - fc = '\0'; - if ((sp->layout->last->vert && - sp->layout->last->col + 1 == sp->opts->cols) || - (sp->next != NULL && - sp->next->layout->last->vert && - sp->next->layout->last->col + 1 == sp->opts->cols) || - (sp->prev != NULL && - sp->prev->layout->last->vert && - sp->prev->layout->last->col + 1 == sp->opts->cols && - (horiz || (IS_HORIZ(sp->layout->last) && - !IS_HORIZ(sp->prev->layout->last)))) || - (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))) - fc = horiz || IS_HORIZ(sp->layout->last) ? '+' : '|'; - else if (horiz && sp->opts->rvert) - fc = '-'; - if (fc != '\0') { + uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 : + sp->opts->opts & TBL_OPT_BOX ? 1 : 0; + if (sp->pos == TBL_SPAN_DATA && + uvert < sp->layout->last->vert && + sp->layout->last->col + 1 == sp->opts->cols) + uvert = dvert = sp->layout->last->vert; + if (sp->next != NULL && + dvert < sp->next->layout->last->vert && + sp->next->layout->last->col + 1 == sp->opts->cols) + dvert = sp->next->layout->last->vert; + if (sp->prev != NULL && + uvert < sp->prev->layout->last->vert && + sp->prev->layout->last->col + 1 == sp->opts->cols && + (horiz || (IS_HORIZ(sp->layout->last) && + !IS_HORIZ(sp->prev->layout->last)))) + uvert = sp->prev->layout->last->vert; + lhori = sp->pos == TBL_SPAN_DHORIZ || + (sp->last != NULL && + sp->last->pos == TBL_DATA_DHORIZ && + sp->last->layout->col + 1 == sp->opts->cols) || + (sp->layout->last->pos == TBL_CELL_DHORIZ && + sp->layout->last->col + 1 == sp->opts->cols) ? 2 : + sp->pos == TBL_SPAN_HORIZ || + (sp->last != NULL && + sp->last->pos == TBL_DATA_HORIZ && + sp->last->layout->col + 1 == sp->opts->cols) || + (sp->layout->last->pos == TBL_CELL_HORIZ && + sp->layout->last->col + 1 == sp->opts->cols) ? 1 : 0; + fc = BUP * uvert + BDOWN * dvert + BLEFT * lhori; + if (uvert > 0 || dvert > 0 || (horiz && sp->opts->rvert)) { if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 || sp->layout->last->col + 1 < sp->opts->cols)) { tp->tcol++; - (*tp->advance)(tp, - tp->tcol->offset > tp->viscol ? - tp->tcol->offset - tp->viscol : 1); + do { + tbl_direct_border(tp, + BHORIZ * lhori, 1); + } while (tp->viscol < tp->tcol->offset); } - (*tp->letter)(tp, fc); + tbl_direct_border(tp, fc, 1); } (*tp->endline)(tp); tp->viscol = 0; @@ -428,80 +557,156 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tp->tcol->rmargin = tp->maxrmargin; if (sp->next == NULL) { if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) { - tbl_hrule(tp, sp, 2); + tbl_hrule(tp, sp, NULL, TBL_OPT_BOX); tp->skipvsp = 1; } - if (sp->opts->opts & TBL_OPT_DBOX) { - tbl_hrule(tp, sp, 3); + if (tp->enc == TERMENC_ASCII && + sp->opts->opts & TBL_OPT_DBOX) { + tbl_hrule(tp, sp, NULL, TBL_OPT_DBOX); tp->skipvsp = 2; } assert(tp->tbl.cols); free(tp->tbl.cols); tp->tbl.cols = NULL; - tp->tcol->offset = offset; } else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX && (sp->next == NULL || sp->next->pos == TBL_SPAN_DATA || sp->next->next != NULL)) - tbl_hrule(tp, sp, 1); + tbl_hrule(tp, sp, sp->next, TBL_OPT_ALLBOX); + tp->tcol->offset = save_offset; tp->flags &= ~TERMP_NONOSPACE; } -/* - * Kinds of horizontal rulers: - * 0: inside the table (single or double line with crossings) - * 1: inside the table (single or double line with crossings and ends) - * 2: inner frame (single line with crossings and ends) - * 3: outer frame (single line without crossings with ends) - */ static void -tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind) +tbl_hrule(struct termp *tp, const struct tbl_span *spp, + const struct tbl_span *spn, int flags) { - const struct tbl_cell *cp, *cpn, *cpp; - const struct roffcol *col; - int vert; - char line, cross; - - line = (kind < 2 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-'; - cross = (kind < 3) ? '+' : '-'; - - if (kind) - term_word(tp, "+"); - cp = sp->layout->first; - cpp = kind || sp->prev == NULL ? NULL : sp->prev->layout->first; - if (cpp == cp) - cpp = NULL; - cpn = kind > 1 || sp->next == NULL ? NULL : sp->next->layout->first; - if (cpn == cp) - cpn = NULL; + const struct tbl_cell *cpp; /* Layout cell above this line. */ + const struct tbl_cell *cpn; /* Layout cell below this line. */ + const struct tbl_dat *dpn; /* Data cell below this line. */ + const struct roffcol *col; /* Contains width and spacing. */ + int opts; /* For the table as a whole. */ + int bw; /* Box line width. */ + int hw; /* Horizontal line width. */ + int lw, rw; /* Left and right line widths. */ + int uw, dw; /* Vertical line widths. */ + + cpp = spp == NULL ? NULL : spp->layout->first; + cpn = spn == NULL ? NULL : spn->layout->first; + dpn = NULL; + if (spn != NULL) { + if (spn->pos == TBL_SPAN_DATA) + dpn = spn->first; + else if (spn->next != NULL) + dpn = spn->next->first; + } + opts = spn == NULL ? spp->opts->opts : spn->opts->opts; + bw = opts & TBL_OPT_DBOX ? (tp->enc == TERMENC_UTF8 ? 2 : 1) : + opts & (TBL_OPT_BOX | TBL_OPT_ALLBOX) ? 1 : 0; + hw = flags == TBL_OPT_DBOX || flags == TBL_OPT_BOX ? bw : + spn->pos == TBL_SPAN_DHORIZ ? 2 : 1; + + /* Print the left end of the line. */ + + if (tp->viscol == 0) { + (*tp->advance)(tp, tp->tcols->offset); + tp->viscol = tp->tcols->offset; + } + if (flags != 0) + tbl_direct_border(tp, + (spp == NULL ? 0 : BUP * bw) + + (spn == NULL ? 0 : BDOWN * bw) + + (spp == NULL || cpn == NULL || + cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1); + for (;;) { - col = tp->tbl.cols + cp->col; - tbl_char(tp, line, col->width + col->spacing / 2); - vert = cp->vert; - if ((cp = cp->next) == NULL) - break; + col = tp->tbl.cols + (cpn == NULL ? cpp->col : cpn->col); + + /* Print the horizontal line inside this column. */ + + lw = cpp == NULL || cpn == NULL || + (cpn->pos != TBL_CELL_DOWN && + (dpn == NULL || strcmp(dpn->string, "\\^") != 0)) + ? hw : 0; + tbl_direct_border(tp, BHORIZ * lw, + col->width + col->spacing / 2); + + /* + * Figure out whether a vertical line is crossing + * at the end of this column, + * and advance to the next column. + */ + + uw = dw = 0; if (cpp != NULL) { - if (vert < cpp->vert) - vert = cpp->vert; + if (flags != TBL_OPT_DBOX) { + uw = cpp->vert; + if (uw == 0 && opts & TBL_OPT_ALLBOX) + uw = 1; + } cpp = cpp->next; } if (cpn != NULL) { - if (vert < cpn->vert) - vert = cpn->vert; + if (flags != TBL_OPT_DBOX) { + dw = cpn->vert; + if (dw == 0 && opts & TBL_OPT_ALLBOX) + dw = 1; + } cpn = cpn->next; + while (dpn != NULL && dpn->layout != cpn) + dpn = dpn->next; } - if (sp->opts->opts & TBL_OPT_ALLBOX && !vert) - vert = 1; + if (cpp == NULL && cpn == NULL) + break; + + /* Vertical lines do not cross spanned cells. */ + + if (cpp != NULL && cpp->pos == TBL_CELL_SPAN) + uw = 0; + if (cpn != NULL && cpn->pos == TBL_CELL_SPAN) + dw = 0; + + /* The horizontal line inside the next column. */ + + rw = cpp == NULL || cpn == NULL || + (cpn->pos != TBL_CELL_DOWN && + (dpn == NULL || strcmp(dpn->string, "\\^") != 0)) + ? hw : 0; + + /* The line crossing at the end of this column. */ + if (col->spacing) - tbl_char(tp, vert ? cross : line, 1); + tbl_direct_border(tp, BLEFT * lw + + BRIGHT * rw + BUP * uw + BDOWN * dw, 1); + + /* + * In ASCII output, a crossing may print two characters. + */ + + if (tp->enc != TERMENC_ASCII || (uw < 2 && dw < 2)) + uw = dw = 0; if (col->spacing > 2) - tbl_char(tp, vert > 1 ? cross : line, 1); + tbl_direct_border(tp, + BHORIZ * rw + BUP * uw + BDOWN * dw, 1); + + /* Padding before the start of the next column. */ + if (col->spacing > 4) - tbl_char(tp, line, (col->spacing - 3) / 2); + tbl_direct_border(tp, + BHORIZ * rw, (col->spacing - 3) / 2); } - if (kind) { - term_word(tp, "+"); - term_flushln(tp); + + /* Print the right end of the line. */ + + if (flags != 0) { + tbl_direct_border(tp, + (spp == NULL ? 0 : BUP * bw) + + (spn == NULL ? 0 : BDOWN * bw) + + (spp == NULL || spn == NULL || + spn->layout->last->pos != TBL_CELL_DOWN ? + BLEFT * hw : 0), 1); + (*tp->endline)(tp); + tp->viscol = 0; } } @@ -512,10 +717,10 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts, { switch (cp->pos) { case TBL_CELL_HORIZ: - tbl_char(tp, '-', col->width); + tbl_fill_border(tp, BHORIZ, col->width); return; case TBL_CELL_DHORIZ: - tbl_char(tp, '=', col->width); + tbl_fill_border(tp, BHORIZ * 2, col->width); return; default: break; @@ -529,11 +734,11 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts, return; case TBL_DATA_HORIZ: case TBL_DATA_NHORIZ: - tbl_char(tp, '-', col->width); + tbl_fill_border(tp, BHORIZ, col->width); return; case TBL_DATA_NDHORIZ: case TBL_DATA_DHORIZ: - tbl_char(tp, '=', col->width); + tbl_fill_border(tp, BHORIZ * 2, col->width); return; default: break; @@ -558,18 +763,48 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts, } static void -tbl_char(struct termp *tp, char c, size_t len) +tbl_fill_string(struct termp *tp, const char *cp, size_t len) { - size_t i, sz; - char cp[2]; + size_t i, sz; + + sz = term_strlen(tp, cp); + for (i = 0; i < len; i += sz) + term_word(tp, cp); +} + +static void +tbl_fill_char(struct termp *tp, char c, size_t len) +{ + char cp[2]; cp[0] = c; cp[1] = '\0'; + tbl_fill_string(tp, cp, len); +} - sz = term_strlen(tp, cp); +static void +tbl_fill_border(struct termp *tp, int c, size_t len) +{ + char buf[12]; - for (i = 0; i < len; i += sz) - term_word(tp, cp); + if ((c = borders_locale[c]) > 127) { + (void)snprintf(buf, sizeof(buf), "\\[u%04x]", c); + tbl_fill_string(tp, buf, len); + } else + tbl_fill_char(tp, c, len); +} + +static void +tbl_direct_border(struct termp *tp, int c, size_t len) +{ + size_t i, sz; + + c = borders_locale[c]; + sz = (*tp->width)(tp, c); + for (i = 0; i < len; i += sz) { + (*tp->letter)(tp, c); + tp->viscol += sz; + } } static void @@ -577,14 +812,14 @@ tbl_literal(struct termp *tp, const struct tbl_dat *dp, const struct roffcol *col) { size_t len, padl, padr, width; - int ic, spans; + int ic, hspans; assert(dp->string); len = term_strlen(tp, dp->string); width = col->width; ic = dp->layout->col; - spans = dp->spans; - while (spans--) + hspans = dp->hspans; + while (hspans--) width += tp->tbl.cols[++ic].width + 3; padr = width > len ? width - len : 0; @@ -609,9 +844,9 @@ tbl_literal(struct termp *tp, const struct tbl_dat *dp, break; } - tbl_char(tp, ASCII_NBRSP, padl); + tbl_fill_char(tp, ASCII_NBRSP, padl); tbl_word(tp, dp); - tbl_char(tp, ASCII_NBRSP, padr); + tbl_fill_char(tp, ASCII_NBRSP, padr); } static void @@ -619,44 +854,66 @@ tbl_number(struct termp *tp, const struct tbl_opts *opts, const struct tbl_dat *dp, const struct roffcol *col) { - char *cp; + const char *cp, *lastdigit, *lastpoint; + size_t intsz, padl, totsz; char buf[2]; - size_t sz, psz, ssz, d, padl; - int i; /* - * See calc_data_number(). Left-pad by taking the offset of our - * and the maximum decimal; right-pad by the remaining amount. + * Almost the same code as in tblcalc_number(): + * First find the position of the decimal point. */ assert(dp->string); + lastdigit = lastpoint = NULL; + for (cp = dp->string; cp[0] != '\0'; cp++) { + if (cp[0] == '\\' && cp[1] == '&') { + lastdigit = lastpoint = cp; + break; + } else if (cp[0] == opts->decimal && + (isdigit((unsigned char)cp[1]) || + (cp > dp->string && isdigit((unsigned char)cp[-1])))) + lastpoint = cp; + else if (isdigit((unsigned char)cp[0])) + lastdigit = cp; + } - sz = term_strlen(tp, dp->string); + /* Then measure both widths. */ - buf[0] = opts->decimal; - buf[1] = '\0'; + padl = 0; + totsz = term_strlen(tp, dp->string); + if (lastdigit != NULL) { + if (lastpoint == NULL) + lastpoint = lastdigit + 1; + intsz = 0; + buf[1] = '\0'; + for (cp = dp->string; cp < lastpoint; cp++) { + buf[0] = cp[0]; + intsz += term_strlen(tp, buf); + } - psz = term_strlen(tp, buf); + /* + * Pad left to match the decimal position, + * but avoid exceeding the total column width. + */ - if ((cp = strrchr(dp->string, opts->decimal)) != NULL) { - for (ssz = 0, i = 0; cp != &dp->string[i]; i++) { - buf[0] = dp->string[i]; - ssz += term_strlen(tp, buf); + if (col->decimal > intsz && col->width > totsz) { + padl = col->decimal - intsz; + if (padl + totsz > col->width) + padl = col->width - totsz; } - d = ssz + psz; - } else - d = sz + psz; - if (col->decimal > d && col->width > sz) { - padl = col->decimal - d; - if (padl + sz > col->width) - padl = col->width - sz; - tbl_char(tp, ASCII_NBRSP, padl); - } else - padl = 0; + /* If it is not a number, simply center the string. */ + + } else if (col->width > totsz) + padl = (col->width - totsz) / 2; + + tbl_fill_char(tp, ASCII_NBRSP, padl); tbl_word(tp, dp); - if (col->width > sz + padl) - tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); + + /* Pad right to fill the column. */ + + if (col->width > padl + totsz) + tbl_fill_char(tp, ASCII_NBRSP, col->width - padl - totsz); } static void diff --git a/usr/src/cmd/mandoc/term.c b/usr/src/cmd/mandoc/term.c index f67fcf9d95..3b9277aaab 100644 --- a/usr/src/cmd/mandoc/term.c +++ b/usr/src/cmd/mandoc/term.c @@ -1,7 +1,7 @@ -/* $Id: term.c,v 1.274 2017/07/28 14:25:48 schwarze Exp $ */ +/* $Id: term.c,v 1.281 2019/06/03 20:23:41 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010-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 @@ -21,6 +21,7 @@ #include <assert.h> #include <ctype.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -37,6 +38,10 @@ static void bufferc(struct termp *, char); static void encode(struct termp *, const char *, size_t); static void encode1(struct termp *, int); static void endline(struct termp *); +static void term_field(struct termp *, size_t, size_t, + size_t, size_t); +static void term_fill(struct termp *, size_t *, size_t *, + size_t); void @@ -83,241 +88,327 @@ term_end(struct termp *p) * Flush a chunk of text. By default, break the output line each time * the right margin is reached, and continue output on the next line * at the same offset as the chunk itself. By default, also break the - * output line at the end of the chunk. - * The following flags may be specified: - * - * - TERMP_NOBREAK: Do not break the output line at the right margin, - * but only at the max right margin. Also, do not break the output - * line at the end of the chunk, such that the next call can pad to - * the next column. However, if less than p->trailspace blanks, - * which can be 0, 1, or 2, remain to the right margin, the line - * will be broken. - * - TERMP_BRTRSP: Consider trailing whitespace significant - * when deciding whether the chunk fits or not. - * - TERMP_BRIND: If the chunk does not fit and the output line has - * to be broken, start the next line at the right margin instead - * of at the offset. Used together with TERMP_NOBREAK for the tags - * in various kinds of tagged lists. - * - TERMP_HANG: Do not break the output line at the right margin, - * append the next chunk after it even if this one is too long. - * To be used together with TERMP_NOBREAK. - * - TERMP_NOPAD: Start writing at the current position, - * do not pad with blank characters up to the offset. + * output line at the end of the chunk. There are many flags modifying + * this behaviour, see the comments in the body of the function. */ void term_flushln(struct termp *p) { - size_t vis; /* current visual position on output */ - size_t vbl; /* number of blanks to prepend to output */ - size_t vend; /* end of word visual position on output */ - size_t bp; /* visual right border position */ - size_t dv; /* temporary for visual pos calculations */ - size_t j; /* temporary loop index for p->tcol->buf */ - size_t jhy; /* last hyph before overflow w/r/t j */ - size_t maxvis; /* output position of visible boundary */ - int ntab; /* number of tabs to prepend */ - int breakline; /* after this word */ + size_t vbl; /* Number of blanks to prepend to the output. */ + size_t vbr; /* Actual visual position of the end of field. */ + size_t vfield; /* Desired visual field width. */ + size_t vtarget; /* Desired visual position of the right margin. */ + size_t ic; /* Character position in the input buffer. */ + size_t nbr; /* Number of characters to print in this field. */ + + /* + * Normally, start writing at the left margin, but with the + * NOPAD flag, start writing at the current position instead. + */ vbl = (p->flags & TERMP_NOPAD) || p->tcol->offset < p->viscol ? 0 : p->tcol->offset - p->viscol; if (p->minbl && vbl < p->minbl) vbl = p->minbl; - maxvis = p->tcol->rmargin > p->viscol + vbl ? - p->tcol->rmargin - p->viscol - vbl : 0; - bp = !(p->flags & TERMP_NOBREAK) ? maxvis : - p->maxrmargin > p->viscol + vbl ? - p->maxrmargin - p->viscol - vbl : 0; - vis = vend = 0; if ((p->flags & TERMP_MULTICOL) == 0) p->tcol->col = 0; - while (p->tcol->col < p->tcol->lastcol) { + + /* Loop over output lines. */ + + for (;;) { + vfield = p->tcol->rmargin > p->viscol + vbl ? + p->tcol->rmargin - p->viscol - vbl : 0; /* - * Handle literal tab characters: collapse all - * subsequent tabs into a single huge set of spaces. + * Normally, break the line at the the right margin + * of the field, but with the NOBREAK flag, only + * break it at the max right margin of the screen, + * and with the BRNEVER flag, never break it at all. */ - ntab = 0; - while (p->tcol->col < p->tcol->lastcol && - p->tcol->buf[p->tcol->col] == '\t') { - vend = term_tab_next(vis); - vbl += vend - vis; - vis = vend; - ntab++; - p->tcol->col++; + vtarget = p->flags & TERMP_BRNEVER ? SIZE_MAX : + (p->flags & TERMP_NOBREAK) == 0 ? vfield : + p->maxrmargin > p->viscol + vbl ? + p->maxrmargin - p->viscol - vbl : 0; + + /* + * Figure out how much text will fit in the field. + * If there is whitespace only, print nothing. + */ + + term_fill(p, &nbr, &vbr, vtarget); + if (nbr == 0) + break; + + /* + * With the CENTER or RIGHT flag, increase the indentation + * to center the text between the left and right margins + * or to adjust it to the right margin, respectively. + */ + + if (vbr < vtarget) { + if (p->flags & TERMP_CENTER) + vbl += (vtarget - vbr) / 2; + else if (p->flags & TERMP_RIGHT) + vbl += vtarget - vbr; } + /* Finally, print the field content. */ + + term_field(p, vbl, nbr, vbr, vtarget); + /* - * Count up visible word characters. Control sequences - * (starting with the CSI) aren't counted. A space - * generates a non-printing word, which is valid (the - * space is printed according to regular spacing rules). + * If there is no text left in the field, exit the loop. + * If the BRTRSP flag is set, consider trailing + * whitespace significant when deciding whether + * the field fits or not. */ - jhy = 0; - breakline = 0; - for (j = p->tcol->col; j < p->tcol->lastcol; j++) { - if (p->tcol->buf[j] == '\n') { - if ((p->flags & TERMP_BRIND) == 0) - breakline = 1; + for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) { + switch (p->tcol->buf[ic]) { + case '\t': + if (p->flags & TERMP_BRTRSP) + vbr = term_tab_next(vbr); continue; - } - if (p->tcol->buf[j] == ' ' || p->tcol->buf[j] == '\t') - break; - - /* Back over the last printed character. */ - if (p->tcol->buf[j] == '\b') { - assert(j); - vend -= (*p->width)(p, p->tcol->buf[j - 1]); + case ' ': + if (p->flags & TERMP_BRTRSP) + vbr += (*p->width)(p, ' '); continue; + case '\n': + case ASCII_BREAK: + continue; + default: + break; } + break; + } + if (ic == p->tcol->lastcol) + break; - /* Regular word. */ - /* Break at the hyphen point if we overrun. */ - if (vend > vis && vend < bp && - (p->tcol->buf[j] == ASCII_HYPH|| - p->tcol->buf[j] == ASCII_BREAK)) - jhy = j; + /* + * At the location of an automtic line break, input + * space characters are consumed by the line break. + */ - /* - * Hyphenation now decided, put back a real - * hyphen such that we get the correct width. - */ - if (p->tcol->buf[j] == ASCII_HYPH) - p->tcol->buf[j] = '-'; + while (p->tcol->col < p->tcol->lastcol && + p->tcol->buf[p->tcol->col] == ' ') + p->tcol->col++; - vend += (*p->width)(p, p->tcol->buf[j]); - } + /* + * In multi-column mode, leave the rest of the text + * in the buffer to be handled by a subsequent + * invocation, such that the other columns of the + * table can be handled first. + * In single-column mode, simply break the line. + */ + + if (p->flags & TERMP_MULTICOL) + return; + + endline(p); + p->viscol = 0; /* - * Find out whether we would exceed the right margin. - * If so, break to the next line. + * Normally, start the next line at the same indentation + * as this one, but with the BRIND flag, start it at the + * right margin instead. This is used together with + * NOBREAK for the tags in various kinds of tagged lists. */ - if (vend > bp && jhy == 0 && vis > 0 && - (p->flags & TERMP_BRNEVER) == 0) { - if (p->flags & TERMP_MULTICOL) - return; + vbl = p->flags & TERMP_BRIND ? + p->tcol->rmargin : p->tcol->offset; + } - endline(p); - vend -= vis; + /* Reset output state in preparation for the next field. */ - /* Use pending tabs on the new line. */ + p->col = p->tcol->col = p->tcol->lastcol = 0; + p->minbl = p->trailspace; + p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE | TERMP_NOPAD); - vbl = 0; - while (ntab--) - vbl = term_tab_next(vbl); + if (p->flags & TERMP_MULTICOL) + return; - /* Re-establish indentation. */ + /* + * The HANG flag means that the next field + * always follows on the same line. + * The NOBREAK flag means that the next field + * follows on the same line unless the field was overrun. + * Normally, break the line at the end of each field. + */ - if (p->flags & TERMP_BRIND) - vbl += p->tcol->rmargin; - else - vbl += p->tcol->offset; - maxvis = p->tcol->rmargin > vbl ? - p->tcol->rmargin - vbl : 0; - bp = !(p->flags & TERMP_NOBREAK) ? maxvis : - p->maxrmargin > vbl ? p->maxrmargin - vbl : 0; - } + if ((p->flags & TERMP_HANG) == 0 && + ((p->flags & TERMP_NOBREAK) == 0 || + vbr + term_len(p, p->trailspace) > vfield)) + endline(p); +} - /* - * Write out the rest of the word. - */ +/* + * Store the number of input characters to print in this field in *nbr + * and their total visual width to print in *vbr. + * If there is only whitespace in the field, both remain zero. + * The desired visual width of the field is provided by vtarget. + * If the first word is longer, the field will be overrun. + */ +static void +term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget) +{ + size_t ic; /* Character position in the input buffer. */ + size_t vis; /* Visual position of the current character. */ + size_t vn; /* Visual position of the next character. */ + int breakline; /* Break at the end of this word. */ + int graph; /* Last character was non-blank. */ + + *nbr = *vbr = vis = 0; + breakline = graph = 0; + for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) { + switch (p->tcol->buf[ic]) { + case '\b': /* Escape \o (overstrike) or backspace markup. */ + assert(ic > 0); + vis -= (*p->width)(p, p->tcol->buf[ic - 1]); + continue; - for ( ; p->tcol->col < p->tcol->lastcol; p->tcol->col++) { - if (vend > bp && jhy > 0 && p->tcol->col > jhy) + case '\t': /* Normal ASCII whitespace. */ + case ' ': + case ASCII_BREAK: /* Escape \: (breakpoint). */ + switch (p->tcol->buf[ic]) { + case '\t': + vn = term_tab_next(vis); break; - if (p->tcol->buf[p->tcol->col] == '\n') - continue; - if (p->tcol->buf[p->tcol->col] == '\t') + case ' ': + vn = vis + (*p->width)(p, ' '); break; - if (p->tcol->buf[p->tcol->col] == ' ') { - j = p->tcol->col; - while (p->tcol->col < p->tcol->lastcol && - p->tcol->buf[p->tcol->col] == ' ') - p->tcol->col++; - dv = (p->tcol->col - j) * (*p->width)(p, ' '); - vbl += dv; - vend += dv; + case ASCII_BREAK: + vn = vis; break; + default: + abort(); } - if (p->tcol->buf[p->tcol->col] == ASCII_NBRSP) { - vbl += (*p->width)(p, ' '); - continue; + /* Can break at the end of a word. */ + if (breakline || vn > vtarget) + break; + if (graph) { + *nbr = ic; + *vbr = vis; + graph = 0; } - if (p->tcol->buf[p->tcol->col] == ASCII_BREAK) - continue; + vis = vn; + continue; + case '\n': /* Escape \p (break at the end of the word). */ + breakline = 1; + continue; + + case ASCII_HYPH: /* Breakable hyphen. */ + graph = 1; /* - * Now we definitely know there will be - * printable characters to output, - * so write preceding white space now. + * We are about to decide whether to break the + * line or not, so we no longer need this hyphen + * to be marked as breakable. Put back a real + * hyphen such that we get the correct width. */ - if (vbl) { - (*p->advance)(p, vbl); - p->viscol += vbl; - vbl = 0; + p->tcol->buf[ic] = '-'; + vis += (*p->width)(p, '-'); + if (vis > vtarget) { + ic++; + break; } - - (*p->letter)(p, p->tcol->buf[p->tcol->col]); - if (p->tcol->buf[p->tcol->col] == '\b') - p->viscol -= (*p->width)(p, - p->tcol->buf[p->tcol->col - 1]); - else - p->viscol += (*p->width)(p, - p->tcol->buf[p->tcol->col]); - } - vis = vend; - - if (breakline == 0) + *nbr = ic + 1; + *vbr = vis; continue; - /* Explicitly requested output line break. */ - - if (p->flags & TERMP_MULTICOL) - return; - - endline(p); - breakline = 0; - vis = vend = 0; - - /* Re-establish indentation. */ - - vbl = p->tcol->offset; - maxvis = p->tcol->rmargin > vbl ? - p->tcol->rmargin - vbl : 0; - bp = !(p->flags & TERMP_NOBREAK) ? maxvis : - p->maxrmargin > vbl ? p->maxrmargin - vbl : 0; + case ASCII_NBRSP: /* Non-breakable space. */ + p->tcol->buf[ic] = ' '; + /* FALLTHROUGH */ + default: /* Printable character. */ + graph = 1; + vis += (*p->width)(p, p->tcol->buf[ic]); + if (vis > vtarget && *nbr > 0) + return; + continue; + } + break; } /* - * If there was trailing white space, it was not printed; - * so reset the cursor position accordingly. + * If the last word extends to the end of the field without any + * trailing whitespace, the loop could not check yet whether it + * can remain on this line. So do the check now. */ - if (vis > vbl) - vis -= vbl; - else - vis = 0; + if (graph && (vis <= vtarget || *nbr == 0)) { + *nbr = ic; + *vbr = vis; + } +} - p->col = p->tcol->col = p->tcol->lastcol = 0; - p->minbl = p->trailspace; - p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE | TERMP_NOPAD); +/* + * Print the contents of one field + * with an indentation of vbl visual columns, + * an input string length of nbr characters, + * an output width of vbr visual columns, + * and a desired field width of vtarget visual columns. + */ +static void +term_field(struct termp *p, size_t vbl, size_t nbr, size_t vbr, size_t vtarget) +{ + size_t ic; /* Character position in the input buffer. */ + size_t vis; /* Visual position of the current character. */ + size_t dv; /* Visual width of the current character. */ + size_t vn; /* Visual position of the next character. */ - if (p->flags & TERMP_MULTICOL) - return; + vis = 0; + for (ic = p->tcol->col; ic < nbr; ic++) { + + /* + * To avoid the printing of trailing whitespace, + * do not print whitespace right away, only count it. + */ - /* Trailing whitespace is significant in some columns. */ + switch (p->tcol->buf[ic]) { + case '\n': + case ASCII_BREAK: + continue; + case '\t': + vn = term_tab_next(vis); + vbl += vn - vis; + vis = vn; + continue; + case ' ': + case ASCII_NBRSP: + dv = (*p->width)(p, ' '); + vbl += dv; + vis += dv; + continue; + default: + break; + } - if (vis && vbl && (TERMP_BRTRSP & p->flags)) - vis += vbl; + /* + * We found a non-blank character to print, + * so write preceding white space now. + */ - /* If the column was overrun, break the line. */ - if ((p->flags & TERMP_NOBREAK) == 0 || - ((p->flags & TERMP_HANG) == 0 && - vis + p->trailspace * (*p->width)(p, ' ') > maxvis)) - endline(p); + if (vbl > 0) { + (*p->advance)(p, vbl); + p->viscol += vbl; + vbl = 0; + } + + /* Print the character and adjust the visual position. */ + + (*p->letter)(p, p->tcol->buf[ic]); + if (p->tcol->buf[ic] == '\b') { + dv = (*p->width)(p, p->tcol->buf[ic - 1]); + p->viscol -= dv; + vis -= dv; + } else { + dv = (*p->width)(p, p->tcol->buf[ic]); + p->viscol += dv; + vis += dv; + } + } + p->tcol->col = nbr; } static void @@ -477,9 +568,6 @@ term_word(struct termp *p, const char *word) word++; esc = mandoc_escape(&word, &seq, &sz); - if (ESCAPE_ERROR == esc) - continue; - switch (esc) { case ESCAPE_UNICODE: uc = mchars_num2uc(seq + 1, sz - 1); @@ -500,6 +588,9 @@ term_word(struct termp *p, const char *word) encode1(p, uc); } continue; + case ESCAPE_UNDEF: + uc = *seq; + break; case ESCAPE_FONTBOLD: term_fontrepl(p, TERMFONT_BOLD); continue; @@ -510,6 +601,7 @@ term_word(struct termp *p, const char *word) term_fontrepl(p, TERMFONT_BI); continue; case ESCAPE_FONT: + case ESCAPE_FONTCW: case ESCAPE_FONTROMAN: term_fontrepl(p, TERMFONT_NONE); continue; @@ -525,6 +617,16 @@ term_word(struct termp *p, const char *word) else if (*word == '\0') p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE); continue; + case ESCAPE_DEVICE: + if (p->type == TERMTYPE_PDF) + encode(p, "pdf", 3); + else if (p->type == TERMTYPE_PS) + encode(p, "ps", 2); + else if (p->enc == TERMENC_ASCII) + encode(p, "ascii", 5); + else + encode(p, "utf8", 4); + continue; case ESCAPE_HORIZ: if (*seq == '|') { seq++; @@ -576,6 +678,9 @@ term_word(struct termp *p, const char *word) case ESCAPE_SPECIAL: uc = mchars_spec2cp(cp, sz); break; + case ESCAPE_UNDEF: + uc = *seq; + break; default: uc = -1; break; @@ -834,12 +939,8 @@ term_strlen(const struct termp *p, const char *cp) switch (*cp) { case '\\': cp++; - esc = mandoc_escape(&cp, &seq, &ssz); - if (ESCAPE_ERROR == esc) - continue; - rhs = NULL; - + esc = mandoc_escape(&cp, &seq, &ssz); switch (esc) { case ESCAPE_UNICODE: uc = mchars_num2uc(seq + 1, ssz - 1); @@ -860,6 +961,24 @@ term_strlen(const struct termp *p, const char *cp) sz += cond_width(p, uc, &skip); } continue; + case ESCAPE_UNDEF: + uc = *seq; + break; + case ESCAPE_DEVICE: + if (p->type == TERMTYPE_PDF) { + rhs = "pdf"; + rsz = 3; + } else if (p->type == TERMTYPE_PS) { + rhs = "ps"; + rsz = 2; + } else if (p->enc == TERMENC_ASCII) { + rhs = "ascii"; + rsz = 5; + } else { + rhs = "utf8"; + rsz = 4; + } + break; case ESCAPE_SKIPCHAR: skip = 1; continue; diff --git a/usr/src/cmd/mandoc/term.h b/usr/src/cmd/mandoc/term.h index 493191d7d3..f0a033a46f 100644 --- a/usr/src/cmd/mandoc/term.h +++ b/usr/src/cmd/mandoc/term.h @@ -1,7 +1,7 @@ -/* $Id: term.h,v 1.130 2017/07/08 14:51:05 schwarze Exp $ */ +/* $Id: term.h,v 1.131 2019/01/04 03:21:02 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2015, 2017, 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 @@ -99,6 +99,8 @@ struct termp { #define TERMP_NEWMC (1 << 18) /* No .mc printed yet. */ #define TERMP_ENDMC (1 << 19) /* Next break ends .mc mode. */ #define TERMP_MULTICOL (1 << 20) /* Multiple column mode. */ +#define TERMP_CENTER (1 << 21) /* Center output lines. */ +#define TERMP_RIGHT (1 << 22) /* Adjust to the right margin. */ enum termtype type; /* Terminal, PS, or PDF. */ enum termenc enc; /* Type of encoding. */ enum termfont fontl; /* Last font set. */ diff --git a/usr/src/cmd/mandoc/term_ascii.c b/usr/src/cmd/mandoc/term_ascii.c index f47ffd75d9..368623cac1 100644 --- a/usr/src/cmd/mandoc/term_ascii.c +++ b/usr/src/cmd/mandoc/term_ascii.c @@ -1,4 +1,4 @@ -/* $Id: term_ascii.c,v 1.61 2018/05/20 21:37:34 schwarze Exp $ */ +/* $Id: term_ascii.c,v 1.64 2018/11/28 14:23:06 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> @@ -90,7 +90,7 @@ ascii_init(enum termenc enc, const struct manoutput *outopts) p->width = ascii_width; #if HAVE_WCHAR - if (TERMENC_ASCII != enc) { + if (enc != TERMENC_ASCII) { /* * Do not change any of this to LC_ALL. It might break @@ -99,7 +99,7 @@ ascii_init(enum termenc enc, const struct manoutput *outopts) * worst case, it might even cause buffer overflows. */ - v = TERMENC_LOCALE == enc ? + v = enc == TERMENC_LOCALE ? setlocale(LC_CTYPE, "") : setlocale(LC_CTYPE, UTF8_LOCALE); @@ -113,7 +113,7 @@ ascii_init(enum termenc enc, const struct manoutput *outopts) v = setlocale(LC_CTYPE, "C"); if (v != NULL && MB_CUR_MAX > 1) { - p->enc = enc; + p->enc = TERMENC_UTF8; p->advance = locale_advance; p->endline = locale_endline; p->letter = locale_letter; @@ -196,8 +196,7 @@ terminal_sepline(void *arg) static size_t ascii_width(const struct termp *p, int c) { - - return 1; + return c != ASCII_BREAK; } void @@ -311,7 +310,7 @@ ascii_uc2str(int uc) "<88>", "<89>", "<8A>", "<8B>", "<8C>", "<8D>", "<8E>", "<8F>", "<90>", "<91>", "<92>", "<93>", "<94>", "<95>", "<96>", "<97>", "<98>", "<99>", "<9A>", "<9B>", "<9C>", "<9D>", "<9E>", "<9F>", - nbrsp, "!", "/\bc", "GBP", "o\bx", "=\bY", "|", "<section>", + nbrsp, "!", "/\bc", "-\bL", "o\bx", "=\bY", "|", "<section>", "\"", "(C)", "_\ba", "<<", "~", "", "(R)", "-", "<degree>","+-","^2", "^3", "'","<micro>","<paragraph>",".", ",", "^1", "_\bo", ">>", "1/4", "1/2", "3/4", "?", diff --git a/usr/src/cmd/mandoc/term_tab.c b/usr/src/cmd/mandoc/term_tab.c index 5251a8425a..3343244f3c 100644 --- a/usr/src/cmd/mandoc/term_tab.c +++ b/usr/src/cmd/mandoc/term_tab.c @@ -1,4 +1,4 @@ -/* $OpenBSD: term.c,v 1.119 2017/01/08 18:08:44 schwarze Exp $ */ +/* $Id: term_tab.c,v 1.5 2018/12/16 00:21:05 schwarze Exp $ */ /* * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> * diff --git a/usr/src/cmd/mandoc/tree.c b/usr/src/cmd/mandoc/tree.c index b9774e1cd8..649c0804c8 100644 --- a/usr/src/cmd/mandoc/tree.c +++ b/usr/src/cmd/mandoc/tree.c @@ -1,7 +1,7 @@ -/* $Id: tree.c,v 1.78 2018/04/11 17:11:13 schwarze Exp $ */ +/* $Id: tree.c,v 1.84 2019/01/01 05:56:34 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013-2015, 2017-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 @@ -29,6 +29,8 @@ #include "roff.h" #include "mdoc.h" #include "man.h" +#include "tbl.h" +#include "eqn.h" #include "main.h" static void print_box(const struct eqn_box *, int); @@ -39,18 +41,18 @@ static void print_span(const struct tbl_span *, int); void -tree_mdoc(void *arg, const struct roff_man *mdoc) +tree_mdoc(void *arg, const struct roff_meta *mdoc) { - print_meta(&mdoc->meta); + print_meta(mdoc); putchar('\n'); print_mdoc(mdoc->first->child, 0); } void -tree_man(void *arg, const struct roff_man *man) +tree_man(void *arg, const struct roff_meta *man) { - print_meta(&man->meta); - if (man->meta.hasbody == 0) + print_meta(man); + if (man->hasbody == 0) puts("body = empty"); putchar('\n'); print_man(man->first->child, 0); @@ -187,20 +189,22 @@ print_mdoc(const struct roff_node *n, int indent) } putchar(' '); - if (NODE_DELIMO & n->flags) + if (n->flags & NODE_DELIMO) putchar('('); - if (NODE_LINE & n->flags) + if (n->flags & NODE_LINE) putchar('*'); printf("%d:%d", n->line, n->pos + 1); - if (NODE_DELIMC & n->flags) + if (n->flags & NODE_DELIMC) putchar(')'); - if (NODE_EOS & n->flags) + if (n->flags & NODE_EOS) putchar('.'); - if (NODE_BROKEN & n->flags) + if (n->flags & NODE_BROKEN) printf(" BROKEN"); - if (NODE_NOSRC & n->flags) + if (n->flags & NODE_NOFILL) + printf(" NOFILL"); + if (n->flags & NODE_NOSRC) printf(" NOSRC"); - if (NODE_NOPRT & n->flags) + if (n->flags & NODE_NOPRT) printf(" NOPRT"); putchar('\n'); } @@ -286,11 +290,15 @@ print_man(const struct roff_node *n, int indent) for (i = 0; i < indent; i++) putchar(' '); printf("%s (%s) ", p, t); - if (NODE_LINE & n->flags) + if (n->flags & NODE_LINE) putchar('*'); printf("%d:%d", n->line, n->pos + 1); - if (NODE_EOS & n->flags) + if (n->flags & NODE_DELIMC) + putchar(')'); + if (n->flags & NODE_EOS) putchar('.'); + if (n->flags & NODE_NOFILL) + printf(" NOFILL"); putchar('\n'); } @@ -377,35 +385,41 @@ print_span(const struct tbl_span *sp, int indent) switch (sp->pos) { case TBL_SPAN_HORIZ: putchar('-'); - return; + putchar(' '); + break; case TBL_SPAN_DHORIZ: putchar('='); - return; - default: + putchar(' '); break; - } - - for (dp = sp->first; dp; dp = dp->next) { - switch (dp->pos) { - case TBL_DATA_HORIZ: - case TBL_DATA_NHORIZ: - putchar('-'); - continue; - case TBL_DATA_DHORIZ: - case TBL_DATA_NDHORIZ: - putchar('='); - continue; - default: - break; + default: + for (dp = sp->first; dp; dp = dp->next) { + switch (dp->pos) { + case TBL_DATA_HORIZ: + case TBL_DATA_NHORIZ: + putchar('-'); + putchar(' '); + continue; + case TBL_DATA_DHORIZ: + case TBL_DATA_NDHORIZ: + putchar('='); + putchar(' '); + continue; + default: + break; + } + printf("[\"%s\"", dp->string ? dp->string : ""); + if (dp->hspans) + printf(">%d", dp->hspans); + if (dp->vspans) + printf("v%d", dp->vspans); + if (dp->layout == NULL) + putchar('*'); + else if (dp->layout->pos == TBL_CELL_DOWN) + putchar('^'); + putchar(']'); + putchar(' '); } - printf("[\"%s\"", dp->string ? dp->string : ""); - if (dp->spans) - printf("(%d)", dp->spans); - if (NULL == dp->layout) - putchar('*'); - putchar(']'); - putchar(' '); + break; } - printf("(tbl) %d:1\n", sp->line); } |
