diff options
Diffstat (limited to 'usr/src')
74 files changed, 4371 insertions, 2648 deletions
diff --git a/usr/src/cmd/mandoc/Makefile.common b/usr/src/cmd/mandoc/Makefile.common index 1695aaddb7..0e773e097e 100644 --- a/usr/src/cmd/mandoc/Makefile.common +++ b/usr/src/cmd/mandoc/Makefile.common @@ -74,12 +74,14 @@ OBJS= arch.o \ term_ascii.o \ term_ps.o \ term_tab.o \ + term_tag.o \ tree.o -OBJS += compat_ohash.o +OBJS += compat_ohash.o compat_stringlist.o -# XXX gcc 4.4.4 false positive +# gcc 4.4.4 false positive roff.o := CERRWARN += $(CNOWARN_UNINIT) +main.o := CERRWARN += $(CNOWARN_UNINIT) # not linted SMATCH=off diff --git a/usr/src/cmd/mandoc/THIRDPARTYLICENSE b/usr/src/cmd/mandoc/THIRDPARTYLICENSE index 45ac298093..0a0fc1acd2 100644 --- a/usr/src/cmd/mandoc/THIRDPARTYLICENSE +++ b/usr/src/cmd/mandoc/THIRDPARTYLICENSE @@ -1,11 +1,11 @@ -$Id: LICENSE,v 1.21 2018/11/26 17:11:11 schwarze Exp $ +$Id: LICENSE,v 1.22 2021/09/19 11:02:09 schwarze Exp $ 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-2018 Ingo Schwarze <schwarze@openbsd.org> +Copyright (c) 2010-2021 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> @@ -13,7 +13,7 @@ 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) 2017 Anthony Bentley <bentley@openbsd.org> -Copyright (c) 1998, 2004, 2010 Todd C. Miller <Todd.Miller@courtesan.com> +Copyright (c) 1998, 2004, 2010, 2015 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> @@ -37,3 +37,19 @@ 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. + + +The following files included from outside sources are protected by +other people's Copyright and are distributed under various 2-clause +and 3-clause BSD licenses; see these individual files for details. + +soelim.c, soelim.1: +Copyright (c) 2014 Baptiste Daroussin <bapt@freebsd.org> + +compat_err.c, compat_fts.c, compat_fts.h, +compat_getsubopt.c, compat_strcasestr.c, compat_strsep.c, +man.1: +Copyright (c) 1989,1990,1993,1994 The Regents of the University of California + +compat_stringlist.c, compat_stringlist.h: +Copyright (c) 1994 Christos Zoulas <christos@netbsd.org> diff --git a/usr/src/cmd/mandoc/arch.c b/usr/src/cmd/mandoc/arch.c index 56b937ec84..41a23cbbc3 100644 --- a/usr/src/cmd/mandoc/arch.c +++ b/usr/src/cmd/mandoc/arch.c @@ -1,4 +1,4 @@ -/* $Id: arch.c,v 1.14 2019/03/04 13:01:57 schwarze Exp $ */ +/* $Id: arch.c,v 1.17 2021/05/13 13:33:11 schwarze Exp $ */ /* * Copyright (c) 2017, 2019 Ingo Schwarze <schwarze@openbsd.org> * @@ -26,7 +26,7 @@ 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 + "octeon", "powerpc64", "riscv64", "sparc64", NULL }; const char *netbsd_arch[] = { "acorn26", "acorn32", "algor", "alpha", "amiga", diff --git a/usr/src/cmd/mandoc/att.c b/usr/src/cmd/mandoc/att.c index 5575bed54a..abde95d739 100644 --- a/usr/src/cmd/mandoc/att.c +++ b/usr/src/cmd/mandoc/att.c @@ -1,4 +1,4 @@ -/* $Id: att.c,v 1.18 2018/12/13 11:55:46 schwarze Exp $ */ +/* $Id: att.c,v 1.19 2021/09/04 20:26:43 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -37,7 +37,7 @@ mdoc_a2att(const char *p) LINE("v5", "Version\\~5 AT&T UNIX"); LINE("v6", "Version\\~6 AT&T UNIX"); LINE("v7", "Version\\~7 AT&T UNIX"); - LINE("32v", "Version\\~32V AT&T UNIX"); + LINE("32v", "Version\\~7 AT&T UNIX/32V"); LINE("III", "AT&T System\\~III UNIX"); LINE("V", "AT&T System\\~V UNIX"); LINE("V.1", "AT&T System\\~V Release\\~1 UNIX"); diff --git a/usr/src/cmd/mandoc/chars.c b/usr/src/cmd/mandoc/chars.c index 24166dbd9f..d54fc458ae 100644 --- a/usr/src/cmd/mandoc/chars.c +++ b/usr/src/cmd/mandoc/chars.c @@ -1,7 +1,8 @@ -/* $Id: chars.c,v 1.78 2018/12/15 19:30:26 schwarze Exp $ */ +/* $Id: chars.c,v 1.79 2020/02/13 16:18:29 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2014, 2015, 2017, 2018, 2020 + * Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -47,7 +48,7 @@ static struct ln lines[] = { /* Spacing. */ { " ", ascii_nbrsp, 0x00a0 }, { "~", ascii_nbrsp, 0x00a0 }, - { "0", " ", 0x2002 }, + { "0", ascii_nbrsp, 0x00a0 }, { ":", ascii_break, 0 }, /* Lines. */ diff --git a/usr/src/cmd/mandoc/compat_ohash.c b/usr/src/cmd/mandoc/compat_ohash.c index cbd6052182..f29c086c6d 100644 --- a/usr/src/cmd/mandoc/compat_ohash.c +++ b/usr/src/cmd/mandoc/compat_ohash.c @@ -1,11 +1,4 @@ -#include "config.h" - -#if HAVE_OHASH - -int dummy; - -#else - +/* $Id: compat_ohash.c,v 1.7 2020/06/15 01:37:15 schwarze Exp $ */ /* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */ /* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> @@ -22,9 +15,9 @@ int dummy; * 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 <sys/types.h> - #include <stddef.h> #include <stdint.h> #include <stdlib.h> @@ -335,5 +328,3 @@ ohash_qlookupi(struct ohash *h, const char *s, const char **e) hv = ohash_interval(s, e); return ohash_lookup_interval(h, s, *e, hv); } - -#endif /*!HAVE_OHASH*/ diff --git a/usr/src/cmd/mandoc/compat_stringlist.c b/usr/src/cmd/mandoc/compat_stringlist.c new file mode 100644 index 0000000000..fb8e56aeba --- /dev/null +++ b/usr/src/cmd/mandoc/compat_stringlist.c @@ -0,0 +1,135 @@ +/* $Id: compat_stringlist.c,v 1.8 2020/06/15 21:48:09 schwarze Exp $ */ +/* $NetBSD: stringlist.c,v 1.14 2015/05/21 01:29:13 christos Exp $ */ + +/*- + * Copyright (c) 1994, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include "compat_stringlist.h" + +#define _SL_CHUNKSIZE 20 + +/* + * sl_init(): Initialize a string list + */ +StringList * +sl_init(void) +{ + StringList *sl; + + sl = malloc(sizeof(StringList)); + if (sl == NULL) + return NULL; + + sl->sl_cur = 0; + sl->sl_max = _SL_CHUNKSIZE; + sl->sl_str = reallocarray(NULL, sl->sl_max, sizeof(char *)); + if (sl->sl_str == NULL) { + free(sl); + sl = NULL; + } + return sl; +} + + +/* + * sl_add(): Add an item to the string list + */ +int +sl_add(StringList *sl, char *name) +{ + if (sl->sl_cur == sl->sl_max - 1) { + char **new; + + new = reallocarray(sl->sl_str, (sl->sl_max + _SL_CHUNKSIZE), + sizeof(char *)); + if (new == NULL) + return -1; + sl->sl_max += _SL_CHUNKSIZE; + sl->sl_str = new; + } + sl->sl_str[sl->sl_cur++] = name; + return 0; +} + + +/* + * sl_free(): Free a stringlist + */ +void +sl_free(StringList *sl, int all) +{ + size_t i; + + if (sl == NULL) + return; + if (sl->sl_str) { + if (all) + for (i = 0; i < sl->sl_cur; i++) + free(sl->sl_str[i]); + free(sl->sl_str); + } + free(sl); +} + + +/* + * sl_find(): Find a name in the string list + */ +char * +sl_find(StringList *sl, const char *name) +{ + size_t i; + + for (i = 0; i < sl->sl_cur; i++) + if (strcmp(sl->sl_str[i], name) == 0) + return sl->sl_str[i]; + + return NULL; +} + +int +sl_delete(StringList *sl, const char *name, int all) +{ + size_t i, j; + + for (i = 0; i < sl->sl_cur; i++) + if (strcmp(sl->sl_str[i], name) == 0) { + if (all) + free(sl->sl_str[i]); + for (j = i + 1; j < sl->sl_cur; j++) + sl->sl_str[j - 1] = sl->sl_str[j]; + sl->sl_str[--sl->sl_cur] = NULL; + return 0; + } + return -1; +} + diff --git a/usr/src/cmd/mandoc/compat_stringlist.h b/usr/src/cmd/mandoc/compat_stringlist.h new file mode 100644 index 0000000000..5777b1cb28 --- /dev/null +++ b/usr/src/cmd/mandoc/compat_stringlist.h @@ -0,0 +1,48 @@ +/* $Id: compat_stringlist.h,v 1.5 2020/06/15 21:48:09 schwarze Exp $ */ +/* $NetBSD: stringlist.h,v 1.7 2008/04/28 20:22:54 martin Exp $ */ + +/*- + * Copyright (c) 1994 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> + +/* + * Simple string list + */ +typedef struct _stringlist { + char **sl_str; + size_t sl_max; + size_t sl_cur; +} StringList; + +StringList *sl_init(void); +int sl_add(StringList *, char *); +void sl_free(StringList *, int); +char *sl_find(StringList *, const char *); +int sl_delete(StringList *, const char *, int); diff --git a/usr/src/cmd/mandoc/config.h b/usr/src/cmd/mandoc/config.h index e4bc9a6a3b..3e569e6082 100644 --- a/usr/src/cmd/mandoc/config.h +++ b/usr/src/cmd/mandoc/config.h @@ -13,12 +13,18 @@ #define O_DIRECTORY 0 #endif +#ifdef __cplusplus +#error "Do not use C++. See the INSTALL file." +#endif + + #define MAN_CONF_FILE "/etc/man.conf" #define MANPATH_BASE "/usr/share/man" -#define MANPATH_DEFAULT "/usr/share/man:/usr/gnu/share/man" +#define MANPATH_DEFAULT "/usr/share/man" +#define OSENUM MANDOC_OS_OTHER #define UTF8_LOCALE "en_US.UTF-8" #define EFTYPE EINVAL -#define HAVE_CMSG_XPG42 0 + #define HAVE_DIRENT_NAMLEN 0 #define HAVE_ENDIAN 1 #define HAVE_ERR 1 @@ -27,7 +33,9 @@ #define HAVE_GETLINE 1 #define HAVE_GETSUBOPT 1 #define HAVE_ISBLANK 1 +#define HAVE_LESS_T 1 #define HAVE_MKDTEMP 1 +#define HAVE_MKSTEMPS 1 #define HAVE_NTOHL 1 #define HAVE_PLEDGE 0 #define HAVE_PROGNAME 1 @@ -48,12 +56,14 @@ #define HAVE_VASPRINTF 1 #define HAVE_WCHAR 1 #define HAVE_OHASH 0 - -#define OSENUM MANDOC_OS_OTHER +#define NEED_XPG4_2 1 #define BINM_APROPOS "apropos" #define BINM_MAKEWHATIS "man -w" +#define BINM_CATMAN "catman" #define BINM_MAN "man" +#define BINM_SOELIM "soelim" #define BINM_WHATIS "whatis" +#define BINM_PAGER "less" #endif /* MANDOC_CONFIG_H */ diff --git a/usr/src/cmd/mandoc/dba_array.c b/usr/src/cmd/mandoc/dba_array.c index 18c9f09f1a..aaf02baa7c 100644 --- a/usr/src/cmd/mandoc/dba_array.c +++ b/usr/src/cmd/mandoc/dba_array.c @@ -1,4 +1,4 @@ -/* $Id: dba_array.c,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */ +/* $Id: dba_array.c,v 1.2 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> * @@ -17,6 +17,8 @@ * Allocation-based arrays for the mandoc database, for read-write access. * The interface is defined in "dba_array.h". */ +#include "config.h" + #include <assert.h> #include <stdint.h> #include <stdlib.h> diff --git a/usr/src/cmd/mandoc/dba_read.c b/usr/src/cmd/mandoc/dba_read.c index e976057064..4fc3ee5edd 100644 --- a/usr/src/cmd/mandoc/dba_read.c +++ b/usr/src/cmd/mandoc/dba_read.c @@ -1,4 +1,4 @@ -/* $Id: dba_read.c,v 1.4 2016/08/17 20:46:56 schwarze Exp $ */ +/* $Id: dba_read.c,v 1.5 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> * @@ -19,6 +19,8 @@ * The interface is defined in "dba.h". * This file is seperate from dba.c because this also uses "dbm.h". */ +#include "config.h" + #include <regex.h> #include <stdint.h> #include <stdlib.h> diff --git a/usr/src/cmd/mandoc/dbm.c b/usr/src/cmd/mandoc/dbm.c index 7637a036a5..7dc07d0674 100644 --- a/usr/src/cmd/mandoc/dbm.c +++ b/usr/src/cmd/mandoc/dbm.c @@ -1,4 +1,4 @@ -/* $Id: dbm.c,v 1.6 2018/11/19 19:22:07 schwarze Exp $ */ +/* $Id: dbm.c,v 1.7 2019/07/01 22:56:24 schwarze Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> * diff --git a/usr/src/cmd/mandoc/dbm_map.h b/usr/src/cmd/mandoc/dbm_map.h index 9768fc5f2d..dc072aef54 100644 --- a/usr/src/cmd/mandoc/dbm_map.h +++ b/usr/src/cmd/mandoc/dbm_map.h @@ -1,4 +1,4 @@ -/* $Id: dbm_map.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */ +/* $Id: dbm_map.h,v 1.2 2019/07/01 22:56:24 schwarze Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> * @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * Private interface for low-level routines for the map-based version + * Private interface for low-level routines for the map-based version * of the mandoc database, for read-only access. * To be used by dbm*.c only. */ diff --git a/usr/src/cmd/mandoc/eqn.c b/usr/src/cmd/mandoc/eqn.c index 3d63382ab3..27f5cac396 100644 --- a/usr/src/cmd/mandoc/eqn.c +++ b/usr/src/cmd/mandoc/eqn.c @@ -1,7 +1,7 @@ -/* $Id: eqn.c,v 1.83 2018/12/14 06:33:14 schwarze Exp $ */ +/* $Id: eqn.c,v 1.84 2020/01/08 12:16:24 schwarze Exp $ */ /* * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -399,6 +399,14 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode) case '"': quoted = 1; break; + case ' ': + case '\t': + case '~': + case '^': + if (quoted) + break; + ep->start++; + continue; default: break; } @@ -669,7 +677,7 @@ eqn_parse(struct eqn_node *ep) if (ep->data == NULL) return; - ep->start = ep->end = ep->data + strspn(ep->data, " ^~"); + ep->start = ep->end = ep->data; next_tok: tok = eqn_next(ep, MODE_TOK); diff --git a/usr/src/cmd/mandoc/eqn_html.c b/usr/src/cmd/mandoc/eqn_html.c index 1fe41ecbfa..64d0664999 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.18 2018/12/13 05:23:38 schwarze Exp $ */ +/* $Id: eqn_html.c,v 1.19 2019/03/17 18:21:45 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 "roff.h" #include "eqn.h" #include "out.h" #include "html.h" diff --git a/usr/src/cmd/mandoc/html.c b/usr/src/cmd/mandoc/html.c index 1302972a4e..71c9c711e8 100644 --- a/usr/src/cmd/mandoc/html.c +++ b/usr/src/cmd/mandoc/html.c @@ -1,7 +1,7 @@ -/* $Id: html.c,v 1.254 2019/03/03 13:02:11 schwarze Exp $ */ +/* $Id: html.c,v 1.275 2021/09/09 14:47:24 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Common functions for mandoc(1) HTML formatters. + * For use by individual formatters and by the main program. */ #include "config.h" @@ -42,34 +45,30 @@ struct htmldata { const char *name; int flags; -#define HTML_NOSTACK (1 << 0) -#define HTML_AUTOCLOSE (1 << 1) -#define HTML_NLBEFORE (1 << 2) -#define HTML_NLBEGIN (1 << 3) -#define HTML_NLEND (1 << 4) -#define HTML_NLAFTER (1 << 5) +#define HTML_INPHRASE (1 << 0) /* Can appear in phrasing context. */ +#define HTML_TOPHRASE (1 << 1) /* Establishes phrasing context. */ +#define HTML_NOSTACK (1 << 2) /* Does not have an end tag. */ +#define HTML_NLBEFORE (1 << 3) /* Output line break before opening. */ +#define HTML_NLBEGIN (1 << 4) /* Output line break after opening. */ +#define HTML_NLEND (1 << 5) /* Output line break before closing. */ +#define HTML_NLAFTER (1 << 6) /* Output line break after closing. */ #define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER) #define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND) #define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE) -#define HTML_INDENT (1 << 6) -#define HTML_NOINDENT (1 << 7) +#define HTML_INDENT (1 << 7) /* Indent content by two spaces. */ +#define HTML_NOINDENT (1 << 8) /* Exception: never indent content. */ }; static const struct htmldata htmltags[TAG_MAX] = { {"html", HTML_NLALL}, {"head", HTML_NLALL | HTML_INDENT}, - {"body", HTML_NLALL}, - {"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, + {"meta", HTML_NOSTACK | HTML_NLALL}, + {"link", HTML_NOSTACK | HTML_NLALL}, + {"style", HTML_NLALL | HTML_INDENT}, {"title", HTML_NLAROUND}, + {"body", HTML_NLALL}, {"div", HTML_NLAROUND}, - {"div", 0}, {"section", HTML_NLALL}, - {"h1", HTML_NLAROUND}, - {"h2", HTML_NLAROUND}, - {"span", 0}, - {"link", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, - {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, - {"a", 0}, {"table", HTML_NLALL | HTML_INDENT}, {"tr", HTML_NLALL | HTML_INDENT}, {"td", HTML_NLAROUND}, @@ -79,16 +78,22 @@ 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}, - {"b", 0}, - {"i", 0}, - {"code", 0}, - {"small", 0}, - {"style", HTML_NLALL | HTML_INDENT}, - {"math", HTML_NLALL | HTML_INDENT}, + {"h1", HTML_TOPHRASE | HTML_NLAROUND}, + {"h2", HTML_TOPHRASE | HTML_NLAROUND}, + {"p", HTML_TOPHRASE | HTML_NLAROUND | HTML_INDENT}, + {"pre", HTML_TOPHRASE | HTML_NLAROUND | HTML_NOINDENT}, + {"a", HTML_INPHRASE | HTML_TOPHRASE}, + {"b", HTML_INPHRASE | HTML_TOPHRASE}, + {"cite", HTML_INPHRASE | HTML_TOPHRASE}, + {"code", HTML_INPHRASE | HTML_TOPHRASE}, + {"i", HTML_INPHRASE | HTML_TOPHRASE}, + {"small", HTML_INPHRASE | HTML_TOPHRASE}, + {"span", HTML_INPHRASE | HTML_TOPHRASE}, + {"var", HTML_INPHRASE | HTML_TOPHRASE}, + {"br", HTML_INPHRASE | HTML_NOSTACK | HTML_NLALL}, + {"hr", HTML_INPHRASE | HTML_NOSTACK}, + {"mark", HTML_INPHRASE }, + {"math", HTML_INPHRASE | HTML_NLALL | HTML_INDENT}, {"mrow", 0}, {"mi", 0}, {"mn", 0}, @@ -108,6 +113,11 @@ static const struct htmldata htmltags[TAG_MAX] = { }; /* Avoid duplicate HTML id= attributes. */ + +struct id_entry { + int ord; /* Ordinal number of the latest occurrence. */ + char id[]; /* The id= attribute without any ordinal suffix. */ +}; static struct ohash id_unique; static void html_reset_internal(struct html *); @@ -120,6 +130,7 @@ 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 *); void * @@ -130,6 +141,7 @@ html_alloc(const struct manoutput *outopts) h = mandoc_calloc(1, sizeof(struct html)); h->tag = NULL; + h->metac = h->metal = ESCAPE_FONTROMAN; h->style = outopts->style; if ((h->base_man1 = outopts->man) == NULL) h->base_man2 = NULL; @@ -141,7 +153,7 @@ html_alloc(const struct manoutput *outopts) if (outopts->toc) h->oflags |= HTML_TOC; - mandoc_ohash_init(&id_unique, 4, 0); + mandoc_ohash_init(&id_unique, 4, offsetof(struct id_entry, id)); return h; } @@ -150,17 +162,17 @@ static void html_reset_internal(struct html *h) { struct tag *tag; - char *cp; + struct id_entry *entry; unsigned int slot; while ((tag = h->tag) != NULL) { h->tag = tag->next; free(tag); } - cp = ohash_first(&id_unique, &slot); - while (cp != NULL) { - free(cp); - cp = ohash_next(&id_unique, &slot); + entry = ohash_first(&id_unique, &slot); + while (entry != NULL) { + free(entry); + entry = ohash_next(&id_unique, &slot); } ohash_delete(&id_unique); } @@ -169,7 +181,7 @@ void html_reset(void *p) { html_reset_internal(p); - mandoc_ohash_init(&id_unique, 4, 0); + mandoc_ohash_init(&id_unique, 4, offsetof(struct id_entry, id)); } void @@ -185,6 +197,8 @@ print_gen_head(struct html *h) struct tag *t; print_otag(h, TAG_META, "?", "charset", "utf-8"); + print_otag(h, TAG_META, "??", "name", "viewport", + "content", "width=device-width, initial-scale=1.0"); if (h->style != NULL) { print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet", h->style, "type", "text/css", "media", "all"); @@ -202,76 +216,75 @@ print_gen_head(struct html *h) print_endline(h); print_text(h, "td.head-vol { text-align: center; }"); print_endline(h); - print_text(h, "div.Pp { margin: 1ex 0ex; }"); + print_text(h, ".Nd, .Bf, .Op { display: inline; }"); print_endline(h); - print_text(h, "div.Nd, div.Bf, div.Op { display: inline; }"); + print_text(h, ".Pa, .Ad { font-style: italic; }"); print_endline(h); - print_text(h, "span.Pa, span.Ad { font-style: italic; }"); + print_text(h, ".Ms { font-weight: bold; }"); print_endline(h); - print_text(h, "span.Ms { font-weight: bold; }"); - print_endline(h); - print_text(h, "dl.Bl-diag "); + print_text(h, ".Bl-diag "); print_byte(h, '>'); print_text(h, " dt { font-weight: bold; }"); print_endline(h); - print_text(h, "code.Nm, code.Fl, code.Cm, code.Ic, " - "code.In, code.Fd, code.Fn,"); - print_endline(h); - print_text(h, "code.Cd { font-weight: bold; " - "font-family: inherit; }"); + print_text(h, "code.Nm, .Fl, .Cm, .Ic, code.In, .Fd, .Fn, .Cd " + "{ font-weight: bold; font-family: inherit; }"); print_tagq(h, t); } -void -print_metaf(struct html *h, enum mandoc_esc deco) +int +html_setfont(struct html *h, enum mandoc_esc font) { - enum htmlfont font; - - switch (deco) { + switch (font) { case ESCAPE_FONTPREV: font = h->metal; break; case ESCAPE_FONTITALIC: - font = HTMLFONT_ITALIC; - break; case ESCAPE_FONTBOLD: - font = HTMLFONT_BOLD; - break; case ESCAPE_FONTBI: - font = HTMLFONT_BI; - break; - case ESCAPE_FONTCW: - font = HTMLFONT_CW; + case ESCAPE_FONTROMAN: + case ESCAPE_FONTCR: + case ESCAPE_FONTCB: + case ESCAPE_FONTCI: break; case ESCAPE_FONT: - case ESCAPE_FONTROMAN: - font = HTMLFONT_NONE; + font = ESCAPE_FONTROMAN; break; default: - return; + return 0; } + h->metal = h->metac; + h->metac = font; + return 1; +} +static void +print_metaf(struct html *h) +{ if (h->metaf) { print_tagq(h, h->metaf); h->metaf = NULL; } - - h->metal = h->metac; - h->metac = font; - - switch (font) { - case HTMLFONT_ITALIC: + switch (h->metac) { + case ESCAPE_FONTITALIC: h->metaf = print_otag(h, TAG_I, ""); break; - case HTMLFONT_BOLD: + case ESCAPE_FONTBOLD: h->metaf = print_otag(h, TAG_B, ""); break; - case HTMLFONT_BI: + case ESCAPE_FONTBI: h->metaf = print_otag(h, TAG_B, ""); print_otag(h, TAG_I, ""); break; - case HTMLFONT_CW: + case ESCAPE_FONTCR: + h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); + break; + case ESCAPE_FONTCB: h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); + print_otag(h, TAG_B, ""); + break; + case ESCAPE_FONTCI: + h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); + print_otag(h, TAG_I, ""); break; default: break; @@ -281,21 +294,18 @@ print_metaf(struct html *h, enum mandoc_esc deco) void html_close_paragraph(struct html *h) { - struct tag *t; + struct tag *this, *next; + int flags; - 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); + this = h->tag; + for (;;) { + next = this->next; + flags = htmltags[this->tag].flags; + if (flags & (HTML_INPHRASE | HTML_TOPHRASE)) + print_ctag(h, this); + if ((flags & HTML_INPHRASE) == 0) break; - case TAG_A: - print_tagq(h, t); - continue; - default: - continue; - } - break; + this = next; } } @@ -333,33 +343,66 @@ html_fillmode(struct html *h, enum roff_tok want) return had; } +/* + * Allocate a string to be used for the "id=" attribute of an HTML + * element and/or as a segment identifier for a URI in an <a> element. + * The function may fail and return NULL if the node lacks text data + * to create the attribute from. + * The caller is responsible for free(3)ing the returned string. + * + * If the "unique" argument is non-zero, the "id_unique" ohash table + * is used for de-duplication. If the "unique" argument is 1, + * it is the first time the function is called for this tag and + * location, so if an ordinal suffix is needed, it is incremented. + * If the "unique" argument is 2, it is the second time the function + * is called for this tag and location, so the ordinal suffix + * remains unchanged. + */ char * html_make_id(const struct roff_node *n, int unique) { const struct roff_node *nch; - char *buf, *bufs, *cp; + struct id_entry *entry; + char *buf, *cp; + size_t len; unsigned int slot; - int suffix; - - for (nch = n->child; nch != NULL; nch = nch->next) - if (nch->type != ROFFT_TEXT) - return NULL; - buf = NULL; - deroff(&buf, n); - if (buf == NULL) - return NULL; + if (n->tag != NULL) + buf = mandoc_strdup(n->tag); + else { + switch (n->tok) { + case MDOC_Sh: + case MDOC_Ss: + case MDOC_Sx: + case MAN_SH: + case MAN_SS: + for (nch = n->child; nch != NULL; nch = nch->next) + if (nch->type != ROFFT_TEXT) + return NULL; + buf = NULL; + deroff(&buf, n); + if (buf == NULL) + return NULL; + break; + default: + if (n->child == NULL || n->child->type != ROFFT_TEXT) + return NULL; + buf = mandoc_strdup(n->child->string); + break; + } + } /* * In ID attributes, only use ASCII characters that are * permitted in URL-fragment strings according to the * explicit list at: * https://url.spec.whatwg.org/#url-fragment-string + * In addition, reserve '~' for ordinal suffixes. */ for (cp = buf; *cp != '\0'; cp++) if (isalnum((unsigned char)*cp) == 0 && - strchr("!$&'()*+,-./:;=?@_~", *cp) == NULL) + strchr("!$&'()*+,-./:;=?@_", *cp) == NULL) *cp = '_'; if (unique == 0) @@ -367,25 +410,21 @@ html_make_id(const struct roff_node *n, int unique) /* Avoid duplicate HTML id= attributes. */ - bufs = NULL; - suffix = 1; slot = ohash_qlookup(&id_unique, buf); - cp = ohash_find(&id_unique, slot); - if (cp != NULL) { - while (cp != NULL) { - free(bufs); - if (++suffix > 127) { - free(buf); - return NULL; - } - mandoc_asprintf(&bufs, "%s_%d", buf, suffix); - slot = ohash_qlookup(&id_unique, bufs); - cp = ohash_find(&id_unique, slot); - } - free(buf); - buf = bufs; + if ((entry = ohash_find(&id_unique, slot)) == NULL) { + len = strlen(buf) + 1; + entry = mandoc_malloc(sizeof(*entry) + len); + entry->ord = 1; + memcpy(entry->id, buf, len); + ohash_insert(&id_unique, slot, entry); + } else if (unique == 1) + entry->ord++; + + if (entry->ord > 1) { + cp = buf; + mandoc_asprintf(&buf, "%s~%d", cp, entry->ord); + free(cp); } - ohash_insert(&id_unique, slot, buf); return buf; } @@ -475,11 +514,14 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse) case ESCAPE_FONTBOLD: case ESCAPE_FONTITALIC: case ESCAPE_FONTBI: - case ESCAPE_FONTCW: case ESCAPE_FONTROMAN: + case ESCAPE_FONTCR: + case ESCAPE_FONTCB: + case ESCAPE_FONTCI: if (0 == norecurse) { h->flags |= HTML_NOSPACE; - print_metaf(h, esc); + if (html_setfont(h, esc)) + print_metaf(h); h->flags &= ~HTML_NOSPACE; } continue; @@ -593,6 +635,25 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) tflags = htmltags[tag].flags; + /* Flow content is not allowed in phrasing context. */ + + if ((tflags & HTML_INPHRASE) == 0) { + for (t = h->tag; t != NULL; t = t->next) { + if (t->closed) + continue; + assert((htmltags[t->tag].flags & HTML_TOPHRASE) == 0); + break; + } + + /* + * Always wrap phrasing elements in a paragraph + * unless already contained in some flow container; + * never put them directly into a section. + */ + + } else if (tflags & HTML_TOPHRASE && h->tag->tag == TAG_SECTION) + print_otag(h, TAG_P, "c", "Pp"); + /* Push this tag onto the stack of open scopes. */ if ((tflags & HTML_NOSTACK) == 0) { @@ -710,7 +771,7 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) /* Accommodate for "well-formed" singleton escaping. */ - if (HTML_AUTOCLOSE & htmltags[tag].flags) + if (htmltags[tag].flags & HTML_NOSTACK) print_byte(h, '/'); print_byte(h, '>'); @@ -728,6 +789,49 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) return t; } +/* + * Print an element with an optional "id=" attribute. + * If the element has phrasing content and an "id=" attribute, + * also add a permalink: outside if it can be in phrasing context, + * inside otherwise. + */ +struct tag * +print_otag_id(struct html *h, enum htmltag elemtype, const char *cattr, + struct roff_node *n) +{ + struct roff_node *nch; + struct tag *ret, *t; + char *id, *href; + + ret = NULL; + id = href = NULL; + if (n->flags & NODE_ID) + id = html_make_id(n, 1); + if (n->flags & NODE_HREF) + href = id == NULL ? html_make_id(n, 2) : id; + if (href != NULL && htmltags[elemtype].flags & HTML_INPHRASE) + ret = print_otag(h, TAG_A, "chR", "permalink", href); + t = print_otag(h, elemtype, "ci", cattr, id); + if (ret == NULL) { + ret = t; + if (href != NULL && (nch = n->child) != NULL) { + /* man(7) is safe, it tags phrasing content only. */ + if (n->tok > MDOC_MAX || + htmltags[elemtype].flags & HTML_TOPHRASE) + nch = NULL; + else /* For mdoc(7), beware of nested blocks. */ + while (nch != NULL && nch->type == ROFFT_TEXT) + nch = nch->next; + if (nch == NULL) + print_otag(h, TAG_A, "chR", "permalink", href); + } + } + free(id); + if (id == NULL) + free(href); + return ret; +} + static void print_ctag(struct html *h, struct tag *tag) { @@ -797,6 +901,25 @@ print_gen_comment(struct html *h, struct roff_node *n) void print_text(struct html *h, const char *word) { + print_tagged_text(h, word, NULL); +} + +void +print_tagged_text(struct html *h, const char *word, struct roff_node *n) +{ + struct tag *t; + char *href; + + /* + * Always wrap text in a paragraph unless already contained in + * some flow container; never put it directly into a section. + */ + + if (h->tag->tag == TAG_SECTION) + print_otag(h, TAG_P, "c", "Pp"); + + /* Output whitespace before this text? */ + if (h->col && (h->flags & HTML_NOSPACE) == 0) { if ( ! (HTML_KEEP & h->flags)) { if (HTML_PREKEEP & h->flags) @@ -806,27 +929,21 @@ print_text(struct html *h, const char *word) print_word(h, " "); } - assert(NULL == h->metaf); - switch (h->metac) { - case HTMLFONT_ITALIC: - h->metaf = print_otag(h, TAG_I, ""); - break; - case HTMLFONT_BOLD: - h->metaf = print_otag(h, TAG_B, ""); - break; - case HTMLFONT_BI: - 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; - } + /* + * Optionally switch fonts, optionally write a permalink, then + * print the text, optionally surrounded by HTML whitespace. + */ + + assert(h->metaf == NULL); + print_metaf(h); + print_indent(h); + + if (n != NULL && (href = html_make_id(n, 2)) != NULL) { + t = print_otag(h, TAG_A, "chR", "permalink", href); + free(href); + } else + t = NULL; - assert(word); if ( ! print_encode(h, word, NULL, 0)) { if ( ! (h->flags & HTML_NONOSPACE)) h->flags &= ~HTML_NOSPACE; @@ -834,10 +951,11 @@ print_text(struct html *h, const char *word) } else h->flags |= HTML_NOSPACE | HTML_NONEWLINE; - if (h->metaf) { + if (h->metaf != NULL) { print_tagq(h, h->metaf); h->metaf = NULL; - } + } else if (t != NULL) + print_tagq(h, t); h->flags &= ~HTML_IGNDELIM; } @@ -964,15 +1082,12 @@ print_indent(struct html *h) { size_t i; - if (h->col) + if (h->col || h->noindent) return; - if (h->noindent == 0) { - h->col = h->indent * 2; - for (i = 0; i < h->col; i++) - putchar(' '); - } - h->flags &= ~HTML_NOSPACE; + h->col = h->indent * 2; + for (i = 0; i < h->col; i++) + putchar(' '); } /* diff --git a/usr/src/cmd/mandoc/html.h b/usr/src/cmd/mandoc/html.h index a6bf891190..3d20140334 100644 --- a/usr/src/cmd/mandoc/html.h +++ b/usr/src/cmd/mandoc/html.h @@ -1,7 +1,7 @@ -/* $Id: html.h,v 1.102 2019/03/01 10:57:18 schwarze Exp $ */ +/* $Id: html.h,v 1.109 2021/09/09 14:47:24 schwarze Exp $ */ /* + * Copyright (c) 2017, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2017, 2018, 2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,23 +14,21 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internal interfaces for mandoc(1) HTML formatters. + * For use by the individual HTML formatters only. */ enum htmltag { TAG_HTML, TAG_HEAD, - TAG_BODY, TAG_META, + TAG_LINK, + TAG_STYLE, TAG_TITLE, + TAG_BODY, TAG_DIV, - TAG_IDIV, TAG_SECTION, - TAG_H1, - TAG_H2, - TAG_SPAN, - TAG_LINK, - TAG_BR, - TAG_A, TAG_TABLE, TAG_TR, TAG_TD, @@ -40,15 +38,21 @@ enum htmltag { TAG_DL, TAG_DT, TAG_DD, + TAG_H1, + TAG_H2, TAG_P, TAG_PRE, - TAG_VAR, - TAG_CITE, + TAG_A, TAG_B, - TAG_I, + TAG_CITE, TAG_CODE, + TAG_I, TAG_SMALL, - TAG_STYLE, + TAG_SPAN, + TAG_VAR, + TAG_BR, + TAG_HR, + TAG_MARK, TAG_MATH, TAG_MROW, TAG_MI, @@ -69,15 +73,6 @@ enum htmltag { TAG_MAX }; -enum htmlfont { - HTMLFONT_NONE = 0, - HTMLFONT_BOLD, - HTMLFONT_ITALIC, - HTMLFONT_BI, - HTMLFONT_CW, - HTMLFONT_MAX -}; - struct tag { struct tag *next; int refcnt; @@ -111,8 +106,8 @@ struct html { char *base_includes; /* base for include href */ char *style; /* style-sheet URI */ struct tag *metaf; /* current open font scope */ - enum htmlfont metal; /* last used font */ - enum htmlfont metac; /* current font mode */ + enum mandoc_esc metal; /* last used font */ + enum mandoc_esc 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 */ @@ -128,10 +123,13 @@ 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 *, ...); +struct tag *print_otag_id(struct html *, enum htmltag, const char *, + struct roff_node *); void print_tagq(struct html *, const struct tag *); void print_stagq(struct html *, const struct tag *); +void print_tagged_text(struct html *, const char *, + struct roff_node *); void print_text(struct html *, const char *); void print_tblclose(struct html *); void print_tbl(struct html *, const struct tbl_span *); @@ -141,3 +139,4 @@ 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); +int html_setfont(struct html *, enum mandoc_esc); diff --git a/usr/src/cmd/mandoc/libmandoc.h b/usr/src/cmd/mandoc/libmandoc.h index ff6f4692f0..ab7c29be51 100644 --- a/usr/src/cmd/mandoc/libmandoc.h +++ b/usr/src/cmd/mandoc/libmandoc.h @@ -1,7 +1,7 @@ -/* $Id: libmandoc.h,v 1.77 2018/12/21 17:15:18 schwarze Exp $ */ +/* $Id: libmandoc.h,v 1.80 2021/06/27 17:57:54 schwarze Exp $ */ /* + * Copyright (c) 2013-2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internal interfaces for parser utilities needed by multiple parsers + * and the top-level functions to call the mdoc, man, and roff parsers. */ /* @@ -47,8 +50,9 @@ struct buf { struct roff; struct roff_man; +struct roff_node; -char *mandoc_normdate(struct roff_man *, char *, int, int); +char *mandoc_normdate(struct roff_node *, struct roff_node *); int mandoc_eos(const char *, size_t); int mandoc_strntoi(const char *, size_t, int); const char *mandoc_a2msec(const char*); @@ -69,10 +73,10 @@ void roff_reset(struct roff *); void roff_man_free(struct roff_man *); struct roff_man *roff_man_alloc(struct roff *, const char *, int); void roff_man_reset(struct roff_man *); -int roff_parseln(struct roff *, int, struct buf *, int *); +int roff_parseln(struct roff *, int, struct buf *, int *, size_t); void roff_userret(struct roff *); void roff_endparse(struct roff *); -void roff_setreg(struct roff *, const char *, int, char sign); +void roff_setreg(struct roff *, const char *, int, char); int roff_getreg(struct roff *, const char *); char *roff_strdup(const struct roff *, const char *); char *roff_getarg(struct roff *, char **, int, int *); diff --git a/usr/src/cmd/mandoc/main.c b/usr/src/cmd/mandoc/main.c index b91c15860a..c5a7cff918 100644 --- a/usr/src/cmd/mandoc/main.c +++ b/usr/src/cmd/mandoc/main.c @@ -1,7 +1,7 @@ -/* $Id: main.c,v 1.322 2019/03/06 10:18:58 schwarze Exp $ */ +/* $Id: main.c,v 1.358 2021/09/04 22:38:46 schwarze Exp $ */ /* + * Copyright (c) 2010-2012, 2014-2021 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -15,12 +15,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. + * + * Main program for mandoc(1), man(1), apropos(1), whatis(1), and help(1). */ #include "config.h" #include <sys/types.h> #include <sys/ioctl.h> #include <sys/param.h> /* MACHINE */ +#include <sys/stat.h> #include <sys/wait.h> #include <assert.h> @@ -31,6 +34,7 @@ #include <errno.h> #include <fcntl.h> #include <glob.h> +#include <limits.h> #if HAVE_SANDBOX_INIT #include <sandbox.h> #endif @@ -51,6 +55,7 @@ #include "man.h" #include "mandoc_parse.h" #include "tag.h" +#include "term_tag.h" #include "main.h" #include "manconf.h" #include "mansearch.h" @@ -76,36 +81,39 @@ enum outt { OUTT_PDF /* -Tpdf */ }; -struct curparse { - struct mparse *mp; - struct manoutput *outopts; /* output options */ +struct outstate { + struct tag_files *tag_files; /* Tagging state variables. */ void *outdata; /* data for output */ - char *os_s; /* operating system for display */ + int use_pager; int wstop; /* stop after a file with a warning */ - enum mandoc_os os_e; /* check base system conventions */ + int had_output; /* Some output was generated. */ enum outt outtype; /* which output to use */ }; int mandocdb(int, char *[]); -static void check_xr(void); -static int fs_lookup(const struct manpaths *, - size_t ipath, const char *, - const char *, const char *, +static void check_xr(struct manpaths *); +static void fs_append(char **, size_t, int, + size_t, const char *, enum form, + struct manpage **, size_t *); +static int fs_lookup(const struct manpaths *, size_t, + const char *, const char *, const char *, struct manpage **, size_t *); static int fs_search(const struct mansearch *, - const struct manpaths *, int, char**, + const struct manpaths *, const char *, struct manpage **, size_t *); -static int koptions(int *, char *); -static void moptions(int *, char *); -static void outdata_alloc(struct curparse *); -static void parse(struct curparse *, int, const char *); -static void passthrough(const char *, int, int); -static pid_t spawn_pager(struct tag_files *); -static int toptions(struct curparse *, char *); +static void glob_esc(char **, const char *, const char *); +static void outdata_alloc(struct outstate *, struct manoutput *); +static void parse(struct mparse *, int, const char *, + struct outstate *, struct manconf *); +static void passthrough(int, int); +static void process_onefile(struct mparse *, struct manpage *, + int, struct outstate *, struct manconf *); +static void run_pager(struct outstate *, char *); +static pid_t spawn_pager(struct outstate *, char *); static void usage(enum argmode) __attribute__((__noreturn__)); -static int woptions(struct curparse *, char *); +static int woptions(char *, enum mandoc_os *, int *); static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; static char help_arg[] = "help"; @@ -115,26 +123,31 @@ static char *help_argv[] = {help_arg, NULL}; int main(int argc, char *argv[]) { - struct manconf conf; - struct mansearch search; - struct curparse curp; - struct winsize ws; - struct tag_files *tag_files; - struct manpage *res, *resp; - const char *progname, *sec, *thisarg; - char *conf_file, *defpaths, *auxpaths; - char *oarg, *tagarg; + struct manconf conf; /* Manpaths and output options. */ + struct outstate outst; /* Output state. */ + struct winsize ws; /* Result of ioctl(TIOCGWINSZ). */ + struct mansearch search; /* Search options. */ + struct manpage *res; /* Complete list of search results. */ + struct manpage *resn; /* Search results for one name. */ + struct mparse *mp; /* Opaque parser object. */ + const char *conf_file; /* -C: alternate config file. */ + const char *os_s; /* -I: Operating system for display. */ + const char *progname, *sec, *ep; + char *defpaths; /* -M: override manpaths. */ + char *auxpaths; /* -m: additional manpaths. */ + char *oarg; /* -O: output option string. */ + char *tagarg; /* -O tag: default value. */ unsigned char *uc; - size_t i, sz; + size_t ressz; /* Number of elements in res[]. */ + size_t resnsz; /* Number of elements in resn[]. */ + size_t i, ib, ssz; + int options; /* Parser options. */ + int show_usage; /* Invalid argument: give up. */ int prio, best_prio; - enum outmode outmode; - int fd, startdir; - int show_usage; - int options; - int use_pager; - int status, signum; + int startdir; int c; - pid_t pager_pid, tc_pgid, man_pgid, pid; + enum mandoc_os os_e; /* Check base system conventions. */ + enum outmode outmode; /* According to command line. */ #if HAVE_PROGNAME progname = getprogname(); @@ -154,10 +167,11 @@ main(int argc, char *argv[]) return mandocdb(argc, argv); #if HAVE_PLEDGE - if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) - err((int)MANDOCLEVEL_SYSERR, "pledge"); + if (pledge("stdio rpath wpath cpath tmppath tty proc exec", NULL) == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); + } #endif - #if HAVE_SANDBOX_INIT if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); @@ -166,8 +180,8 @@ main(int argc, char *argv[]) /* Search options. */ memset(&conf, 0, sizeof(conf)); - conf_file = defpaths = NULL; - auxpaths = NULL; + conf_file = NULL; + defpaths = auxpaths = NULL; memset(&search, 0, sizeof(struct mansearch)); search.outkey = "Nd"; @@ -184,15 +198,19 @@ main(int argc, char *argv[]) else search.argmode = ARG_FILE; - /* Parser and formatter options. */ + /* Parser options. */ - memset(&curp, 0, sizeof(struct curparse)); - curp.outtype = OUTT_LOCALE; - curp.outopts = &conf.output; options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; + os_e = MANDOC_OS_OTHER; + os_s = NULL; + + /* Formatter options. */ + + memset(&outst, 0, sizeof(outst)); + outst.tag_files = NULL; + outst.outtype = OUTT_LOCALE; + outst.use_pager = 1; - use_pager = 1; - tag_files = NULL; show_usage = 0; outmode = OUTMODE_DEF; @@ -210,30 +228,40 @@ main(int argc, char *argv[]) conf_file = optarg; break; case 'c': - use_pager = 0; + outst.use_pager = 0; break; case 'f': search.argmode = ARG_WORD; break; case 'h': conf.output.synopsisonly = 1; - use_pager = 0; + outst.use_pager = 0; outmode = OUTMODE_ALL; break; case 'I': - if (strncmp(optarg, "os=", 3)) { - warnx("-I %s: Bad argument", optarg); - return (int)MANDOCLEVEL_BADARG; + if (strncmp(optarg, "os=", 3) != 0) { + mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, + "-I %s", optarg); + return mandoc_msg_getrc(); } - if (curp.os_s != NULL) { - warnx("-I %s: Duplicate argument", optarg); - return (int)MANDOCLEVEL_BADARG; + if (os_s != NULL) { + mandoc_msg(MANDOCERR_BADARG_DUPE, 0, 0, + "-I %s", optarg); + return mandoc_msg_getrc(); } - curp.os_s = mandoc_strdup(optarg + 3); + os_s = optarg + 3; break; case 'K': - if ( ! koptions(&options, optarg)) - return (int)MANDOCLEVEL_BADARG; + options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); + if (strcmp(optarg, "utf-8") == 0) + options |= MPARSE_UTF8; + else if (strcmp(optarg, "iso-8859-1") == 0) + options |= MPARSE_LATIN1; + else if (strcmp(optarg, "us-ascii") != 0) { + mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, + "-K %s", optarg); + return mandoc_msg_getrc(); + } break; case 'k': search.argmode = ARG_EXPR; @@ -258,12 +286,37 @@ main(int argc, char *argv[]) search.sec = optarg; break; case 'T': - if ( ! toptions(&curp, optarg)) - return (int)MANDOCLEVEL_BADARG; + if (strcmp(optarg, "ascii") == 0) + outst.outtype = OUTT_ASCII; + else if (strcmp(optarg, "lint") == 0) { + outst.outtype = OUTT_LINT; + mandoc_msg_setoutfile(stdout); + mandoc_msg_setmin(MANDOCERR_BASE); + } else if (strcmp(optarg, "tree") == 0) + outst.outtype = OUTT_TREE; + else if (strcmp(optarg, "man") == 0) + outst.outtype = OUTT_MAN; + else if (strcmp(optarg, "html") == 0) + outst.outtype = OUTT_HTML; + else if (strcmp(optarg, "markdown") == 0) + outst.outtype = OUTT_MARKDOWN; + else if (strcmp(optarg, "utf8") == 0) + outst.outtype = OUTT_UTF8; + else if (strcmp(optarg, "locale") == 0) + outst.outtype = OUTT_LOCALE; + else if (strcmp(optarg, "ps") == 0) + outst.outtype = OUTT_PS; + else if (strcmp(optarg, "pdf") == 0) + outst.outtype = OUTT_PDF; + else { + mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, + "-T %s", optarg); + return mandoc_msg_getrc(); + } break; case 'W': - if ( ! woptions(&curp, optarg)) - return (int)MANDOCLEVEL_BADARG; + if (woptions(optarg, &os_e, &outst.wstop) == -1) + return mandoc_msg_getrc(); break; case 'w': outmode = OUTMODE_FLN; @@ -279,11 +332,12 @@ main(int argc, char *argv[]) /* Postprocess options. */ - if (outmode == OUTMODE_DEF) { + switch (outmode) { + case OUTMODE_DEF: switch (search.argmode) { case ARG_FILE: outmode = OUTMODE_ALL; - use_pager = 0; + outst.use_pager = 0; break; case ARG_NAME: outmode = OUTMODE_ONE; @@ -292,6 +346,16 @@ main(int argc, char *argv[]) outmode = OUTMODE_LST; break; } + break; + case OUTMODE_FLN: + if (search.argmode == ARG_FILE) + outmode = OUTMODE_ALL; + break; + case OUTMODE_ALL: + break; + case OUTMODE_LST: + case OUTMODE_ONE: + abort(); } if (oarg != NULL) { @@ -299,25 +363,24 @@ main(int argc, char *argv[]) search.outkey = oarg; else { while (oarg != NULL) { - thisarg = oarg; if (manconf_output(&conf.output, - strsep(&oarg, ","), 0) == 0) - continue; - warnx("-O %s: Bad argument", thisarg); - return (int)MANDOCLEVEL_BADARG; + strsep(&oarg, ","), 0) == -1) + return mandoc_msg_getrc(); } } } - if (curp.outtype != OUTT_TREE || !curp.outopts->noval) + if (outst.outtype != OUTT_TREE || conf.output.noval == 0) options |= MPARSE_VALIDATE; if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST || - !isatty(STDOUT_FILENO)) - use_pager = 0; + (conf.output.outfilename == NULL && + conf.output.tagfilename == NULL && + isatty(STDOUT_FILENO) == 0)) + outst.use_pager = 0; - if (use_pager && + if (outst.use_pager && (conf.output.width == 0 || conf.output.indent == 0) && ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 1) { @@ -328,9 +391,17 @@ main(int argc, char *argv[]) } #if HAVE_PLEDGE - if (!use_pager) - if (pledge("stdio rpath", NULL) == -1) - err((int)MANDOCLEVEL_SYSERR, "pledge"); + if (outst.use_pager == 0) + c = pledge("stdio rpath", NULL); + else if (conf.output.outfilename != NULL || + conf.output.tagfilename != NULL) + c = pledge("stdio rpath wpath cpath", NULL); + else + c = pledge("stdio rpath tmppath tty proc exec", NULL); + if (c == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); + } #endif /* Parse arguments. */ @@ -339,11 +410,10 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; } - resp = NULL; /* - * Quirks for help(1) - * and for a man(1) section argument without -s. + * Quirks for help(1) and man(1), + * in particular for a section argument without -s. */ if (search.argmode == ARG_NAME) { @@ -355,7 +425,7 @@ main(int argc, char *argv[]) } else if (argc > 1 && ((uc = (unsigned char *)argv[0]) != NULL) && ((isdigit(uc[0]) && (uc[1] == '\0' || - (isalpha(uc[1]) && uc[2] == '\0'))) || + isalpha(uc[1]))) || (uc[0] == 'n' && uc[1] == '\0'))) { search.sec = (char *)uc; argv++; @@ -367,6 +437,8 @@ main(int argc, char *argv[]) if (search.arch == NULL) search.arch = MACHINE; #endif + if (outmode == OUTMODE_ONE) + search.firstmatch = 1; } /* @@ -380,128 +452,161 @@ main(int argc, char *argv[]) conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; } - /* man(1), whatis(1), apropos(1) */ - - if (search.argmode != ARG_FILE) { - if (search.argmode == ARG_NAME && - outmode == OUTMODE_ONE) - search.firstmatch = 1; - - /* Access the mandoc database. */ + /* Read the configuration file. */ + if (search.argmode != ARG_FILE || + mandoc_msg_getmin() == MANDOCERR_STYLE) manconf_parse(&conf, conf_file, defpaths, auxpaths); - if ( ! mansearch(&search, &conf.manpath, - argc, argv, &res, &sz)) - usage(search.argmode); - if (sz == 0 && search.argmode == ARG_NAME) - fs_search(&search, &conf.manpath, - argc, argv, &res, &sz); + /* man(1): Resolve each name individually. */ - if (search.argmode == ARG_NAME) { - for (c = 0; c < argc; c++) { - if (strchr(argv[c], '/') == NULL) - continue; - if (access(argv[c], R_OK) == -1) { - warn("%s", argv[c]); + if (search.argmode == ARG_NAME) { + if (argc < 1) { + if (outmode != OUTMODE_FLN) + usage(ARG_NAME); + if (conf.manpath.sz == 0) { + warnx("The manpath is empty."); + mandoc_msg_setrc(MANDOCLEVEL_BADARG); + } else { + for (i = 0; i + 1 < conf.manpath.sz; i++) + printf("%s:", conf.manpath.paths[i]); + printf("%s\n", conf.manpath.paths[i]); + } + manconf_free(&conf); + return (int)mandoc_msg_getrc(); + } + for (res = NULL, ressz = 0; argc > 0; argc--, argv++) { + (void)mansearch(&search, &conf.manpath, + 1, argv, &resn, &resnsz); + if (resnsz == 0) + (void)fs_search(&search, &conf.manpath, + *argv, &resn, &resnsz); + if (resnsz == 0 && strchr(*argv, '/') == NULL) { + if (search.arch != NULL && + arch_valid(search.arch, OSENUM) == 0) + warnx("Unknown architecture \"%s\".", + search.arch); + else if (search.sec != NULL) + warnx("No entry for %s in " + "section %s of the manual.", + *argv, search.sec); + else + warnx("No entry for %s in " + "the manual.", *argv); + mandoc_msg_setrc(MANDOCLEVEL_BADARG); + continue; + } + if (resnsz == 0) { + if (access(*argv, R_OK) == -1) { + mandoc_msg_setinfilename(*argv); + mandoc_msg(MANDOCERR_BADARG_BAD, + 0, 0, "%s", strerror(errno)); + mandoc_msg_setinfilename(NULL); continue; } + resnsz = 1; + resn = mandoc_calloc(resnsz, sizeof(*res)); + resn->file = mandoc_strdup(*argv); + resn->ipath = SIZE_MAX; + resn->form = FORM_SRC; + } + if (outmode != OUTMODE_ONE || resnsz == 1) { res = mandoc_reallocarray(res, - sz + 1, sizeof(*res)); - res[sz].file = mandoc_strdup(argv[c]); - res[sz].names = NULL; - res[sz].output = NULL; - res[sz].ipath = SIZE_MAX; - res[sz].sec = 10; - res[sz].form = FORM_SRC; - sz++; + ressz + resnsz, sizeof(*res)); + memcpy(res + ressz, resn, + sizeof(*resn) * resnsz); + ressz += resnsz; + continue; } - } - if (sz == 0) { - if (search.argmode != ARG_NAME) - warnx("nothing appropriate"); - mandoc_msg_setrc(MANDOCLEVEL_BADARG); - goto out; - } + /* Search for the best section. */ - /* - * For standard man(1) and -a output mode, - * prepare for copying filename pointers - * into the program parameter array. - */ - - if (outmode == OUTMODE_ONE) { - argc = 1; - best_prio = 20; - } else if (outmode == OUTMODE_ALL) - argc = (int)sz; - - /* Iterate all matching manuals. */ - - resp = res; - for (i = 0; i < sz; i++) { - if (outmode == OUTMODE_FLN) - puts(res[i].file); - else if (outmode == OUTMODE_LST) - printf("%s - %s\n", res[i].names, - res[i].output == NULL ? "" : - res[i].output); - else if (outmode == OUTMODE_ONE) { - /* Search for the best section. */ - sec = res[i].file; + best_prio = 40; + for (ib = i = 0; i < resnsz; i++) { + sec = resn[i].file; sec += strcspn(sec, "123456789"); if (sec[0] == '\0') - continue; + continue; /* No section at all. */ prio = sec_prios[sec[0] - '1']; - if (sec[1] != '/') - prio += 10; + if (search.sec != NULL) { + ssz = strlen(search.sec); + if (strncmp(sec, search.sec, ssz) == 0) + sec += ssz; + } else + sec++; /* Prefer without suffix. */ + if (*sec != '/') + prio += 10; /* Wrong dir name. */ + if (search.sec != NULL) { + ep = strchr(sec, '\0'); + if (ep - sec > 3 && + strncmp(ep - 3, ".gz", 3) == 0) + ep -= 3; + if ((size_t)(ep - sec) < ssz + 3 || + strncmp(ep - ssz, search.sec, + ssz) != 0) /* Wrong file */ + prio += 20; /* extension. */ + } if (prio >= best_prio) continue; best_prio = prio; - resp = res + i; + ib = i; } + res = mandoc_reallocarray(res, ressz + 1, + sizeof(*res)); + memcpy(res + ressz++, resn + ib, sizeof(*resn)); } - /* - * For man(1), -a and -i output mode, fall through - * to the main mandoc(1) code iterating files - * and running the parsers on each of them. - */ + /* apropos(1), whatis(1): Process the full search expression. */ - if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) + } else if (search.argmode != ARG_FILE) { + if (mansearch(&search, &conf.manpath, + argc, argv, &res, &ressz) == 0) + usage(search.argmode); + + if (ressz == 0) { + warnx("nothing appropriate"); + mandoc_msg_setrc(MANDOCLEVEL_BADARG); goto out; - } + } - /* mandoc(1) */ + /* mandoc(1): Take command line arguments as file names. */ -#if HAVE_PLEDGE - if (use_pager) { - if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) - err((int)MANDOCLEVEL_SYSERR, "pledge"); } else { - if (pledge("stdio rpath", NULL) == -1) - err((int)MANDOCLEVEL_SYSERR, "pledge"); + ressz = argc > 0 ? argc : 1; + res = mandoc_calloc(ressz, sizeof(*res)); + for (i = 0; i < ressz; i++) { + if (argc > 0) + res[i].file = mandoc_strdup(argv[i]); + res[i].ipath = SIZE_MAX; + res[i].form = FORM_SRC; + } } -#endif - - if (search.argmode == ARG_FILE) - moptions(&options, auxpaths); - mchars_alloc(); - curp.mp = mparse_alloc(options, curp.os_e, curp.os_s); + switch (outmode) { + case OUTMODE_FLN: + for (i = 0; i < ressz; i++) + puts(res[i].file); + goto out; + case OUTMODE_LST: + for (i = 0; i < ressz; i++) + printf("%s - %s\n", res[i].names, + res[i].output == NULL ? "" : + res[i].output); + goto out; + default: + break; + } - if (argc < 1) { - if (use_pager) { - tag_files = tag_init(); - tag_files->tagname = conf.output.tag; - } - thisarg = "<stdin>"; - mandoc_msg_setinfilename(thisarg); - parse(&curp, STDIN_FILENO, thisarg); - mandoc_msg_setinfilename(NULL); + if (search.argmode == ARG_FILE && auxpaths != NULL) { + if (strcmp(auxpaths, "doc") == 0) + options |= MPARSE_MDOC; + else if (strcmp(auxpaths, "an") == 0) + options |= MPARSE_MAN; } + mchars_alloc(); + mp = mparse_alloc(options, os_e, os_s); + /* * Remember the original working directory, if possible. * This will be needed if some names on the command line @@ -510,167 +615,61 @@ main(int argc, char *argv[]) * readable: Maybe it won't be needed after all. */ startdir = open(".", O_RDONLY | O_DIRECTORY); - - while (argc > 0) { - - /* - * Changing directories is not needed in ARG_FILE mode. - * Do it on a best-effort basis. Even in case of - * failure, some functionality may still work. - */ - if (resp != NULL) { - if (resp->ipath != SIZE_MAX) - (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, thisarg); - if (fd != -1) { - if (use_pager) { - use_pager = 0; - tag_files = tag_init(); - tag_files->tagname = conf.output.tag; - } - - 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) { - warn("%s", tag_files->ofn); - tag_unlink(); - tag_files = NULL; - } else - warn("stdout"); - mandoc_msg_setrc(MANDOCLEVEL_SYSERR); - break; - } - - if (argc > 1 && curp.outtype <= OUTT_UTF8) { - if (curp.outdata == NULL) - outdata_alloc(&curp); - terminal_sepline(curp.outdata); - } - } else - mandoc_msg(MANDOCERR_FILE, 0, 0, - "%s: %s", thisarg, strerror(errno)); - - if (curp.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) + for (i = 0; i < ressz; i++) { + process_onefile(mp, res + i, startdir, &outst, &conf); + if (outst.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) break; - - if (resp != NULL) - resp++; - else - argv++; - if (--argc) - mparse_reset(curp.mp); } if (startdir != -1) { (void)fchdir(startdir); close(startdir); } - - if (curp.outdata != NULL) { - switch (curp.outtype) { + if (conf.output.tag != NULL && conf.output.tag_found == 0) { + mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", conf.output.tag); + conf.output.tag = NULL; + } + if (outst.outdata != NULL) { + switch (outst.outtype) { case OUTT_HTML: - html_free(curp.outdata); + html_free(outst.outdata); break; case OUTT_UTF8: case OUTT_LOCALE: case OUTT_ASCII: - ascii_free(curp.outdata); + ascii_free(outst.outdata); break; case OUTT_PDF: case OUTT_PS: - pspdf_free(curp.outdata); + pspdf_free(outst.outdata); break; default: break; } } mandoc_xr_free(); - mparse_free(curp.mp); + mparse_free(mp); mchars_free(); out: - if (search.argmode != ARG_FILE) { + mansearch_free(res, ressz); + if (search.argmode != ARG_FILE) manconf_free(&conf); - mansearch_free(res, sz); - } - - free(curp.os_s); - - /* - * When using a pager, finish writing both temporary files, - * fork it, wait for the user to close it, and clean up. - */ - - if (tag_files != NULL) { - fclose(stdout); - tag_write(); - man_pgid = getpgid(0); - tag_files->tcpgid = man_pgid == getpid() ? - getpgid(getppid()) : man_pgid; - pager_pid = 0; - signum = SIGSTOP; - for (;;) { - - /* Stop here until moved to the foreground. */ - - tc_pgid = tcgetpgrp(tag_files->ofd); - if (tc_pgid != man_pgid) { - if (tc_pgid == pager_pid) { - (void)tcsetpgrp(tag_files->ofd, - man_pgid); - if (signum == SIGTTIN) - continue; - } else - tag_files->tcpgid = tc_pgid; - kill(0, signum); - continue; - } - /* Once in the foreground, activate the pager. */ + if (outst.tag_files != NULL) { + if (term_tag_close() != -1 && + conf.output.outfilename == NULL && + conf.output.tagfilename == NULL) + run_pager(&outst, conf.output.tag); + term_tag_unlink(); + } else if (outst.had_output && outst.outtype != OUTT_LINT) + mandoc_msg_summary(); - if (pager_pid) { - (void)tcsetpgrp(tag_files->ofd, pager_pid); - kill(pager_pid, SIGCONT); - } else - pager_pid = spawn_pager(tag_files); - - /* Wait for the pager to stop or exit. */ - - while ((pid = waitpid(pager_pid, &status, - WUNTRACED)) == -1 && errno == EINTR) - continue; - - if (pid == -1) { - warn("wait"); - mandoc_msg_setrc(MANDOCLEVEL_SYSERR); - break; - } - if (!WIFSTOPPED(status)) - break; - - signum = WSTOPSIG(status); - } - tag_unlink(); - } return (int)mandoc_msg_getrc(); } static void usage(enum argmode argmode) { - switch (argmode) { case ARG_FILE: fputs("usage: mandoc [-ac] [-I os=name] " @@ -696,27 +695,71 @@ usage(enum argmode argmode) exit((int)MANDOCLEVEL_BADARG); } +static void +glob_esc(char **dst, const char *src, const char *suffix) +{ + while (*src != '\0') { + if (strchr("*?[", *src) != NULL) + *(*dst)++ = '\\'; + *(*dst)++ = *src++; + } + while (*suffix != '\0') + *(*dst)++ = *suffix++; +} + +static void +fs_append(char **file, size_t filesz, int copy, size_t ipath, + const char *sec, enum form form, struct manpage **res, size_t *ressz) +{ + struct manpage *page; + + *res = mandoc_reallocarray(*res, *ressz + filesz, sizeof(**res)); + page = *res + *ressz; + *ressz += filesz; + for (;;) { + page->file = copy ? mandoc_strdup(*file) : *file; + page->names = NULL; + page->output = NULL; + page->bits = NAME_FILE & NAME_MASK; + page->ipath = ipath; + page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; + page->form = form; + if (--filesz == 0) + break; + file++; + page++; + } +} + static int fs_lookup(const struct manpaths *paths, size_t ipath, const char *sec, const char *arch, const char *name, struct manpage **res, size_t *ressz) { + struct stat sb; glob_t globinfo; - struct manpage *page; - char *file; + char *file, *cp, secnum[2]; int globres; enum form form; + const char *const slman = "/man"; + const char *const slash = "/"; + const char *const sglob = ".[01-9]*"; + const char *const dot = "."; + const char *const aster = "*"; + + memset(&globinfo, 0, sizeof(globinfo)); form = FORM_SRC; + mandoc_asprintf(&file, "%s/man%s/%s.%s", paths->paths[ipath], sec, name, sec); - if (access(file, R_OK) != -1) + if (stat(file, &sb) != -1) goto found; free(file); mandoc_asprintf(&file, "%s/cat%s/%s.0", paths->paths[ipath], sec, name); - if (access(file, R_OK) != -1) { + if (stat(file, &sb) != -1) { form = FORM_CAT; goto found; } @@ -725,107 +768,190 @@ fs_lookup(const struct manpaths *paths, size_t ipath, if (arch != NULL) { mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", paths->paths[ipath], sec, arch, name, sec); - if (access(file, R_OK) != -1) + if (stat(file, &sb) != -1) goto found; free(file); } - mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", - paths->paths[ipath], sec, name); + cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 + + strlen(slman) + strlen(sec) * 2 + strlen(slash) + + strlen(name) * 2 + strlen(sglob) + 1); + glob_esc(&cp, paths->paths[ipath], slman); + glob_esc(&cp, sec, slash); + glob_esc(&cp, name, sglob); + *cp = '\0'; globres = glob(file, 0, NULL, &globinfo); if (globres != 0 && globres != GLOB_NOMATCH) - warn("%s: glob", file); + mandoc_msg(MANDOCERR_GLOB, 0, 0, + "%s: %s", file, strerror(errno)); free(file); - if (globres == 0) - file = mandoc_strdup(*globinfo.gl_pathv); - globfree(&globinfo); + file = NULL; if (globres == 0) goto found; + globfree(&globinfo); + + if (sec[1] != '\0' && *ressz == 0) { + secnum[0] = sec[0]; + secnum[1] = '\0'; + cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 + + strlen(slman) + strlen(secnum) * 2 + strlen(slash) + + strlen(name) * 2 + strlen(dot) + + strlen(sec) * 2 + strlen(aster) + 1); + glob_esc(&cp, paths->paths[ipath], slman); + glob_esc(&cp, secnum, slash); + glob_esc(&cp, name, dot); + glob_esc(&cp, sec, aster); + *cp = '\0'; + globres = glob(file, 0, NULL, &globinfo); + if (globres != 0 && globres != GLOB_NOMATCH) + mandoc_msg(MANDOCERR_GLOB, 0, 0, + "%s: %s", file, strerror(errno)); + free(file); + file = NULL; + if (globres == 0) + goto found; + globfree(&globinfo); + } + if (res != NULL || ipath + 1 != paths->sz) - return 0; + return -1; mandoc_asprintf(&file, "%s.%s", name, sec); - globres = access(file, R_OK); + globres = stat(file, &sb); free(file); - return globres != -1; + return globres; found: warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); - if (res == NULL) { + if (res == NULL) free(file); - return 1; - } - *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); - page = *res + (*ressz - 1); - page->file = file; - page->names = NULL; - page->output = NULL; - page->ipath = ipath; - page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; - page->form = form; - return 1; + else if (file == NULL) + fs_append(globinfo.gl_pathv, globinfo.gl_pathc, 1, + ipath, sec, form, res, ressz); + else + fs_append(&file, 1, 0, ipath, sec, form, res, ressz); + globfree(&globinfo); + return 0; } static int fs_search(const struct mansearch *cfg, const struct manpaths *paths, - int argc, char **argv, struct manpage **res, size_t *ressz) + const char *name, struct manpage **res, size_t *ressz) { const char *const sections[] = {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; const size_t nsec = sizeof(sections)/sizeof(sections[0]); - size_t ipath, isec, lastsz; + size_t ipath, isec; assert(cfg->argmode == ARG_NAME); - if (res != NULL) *res = NULL; - *ressz = lastsz = 0; - while (argc) { - for (ipath = 0; ipath < paths->sz; ipath++) { - if (cfg->sec != NULL) { - if (fs_lookup(paths, ipath, cfg->sec, - cfg->arch, *argv, res, ressz) && - cfg->firstmatch) - return 1; - } else for (isec = 0; isec < nsec; isec++) + *ressz = 0; + for (ipath = 0; ipath < paths->sz; ipath++) { + if (cfg->sec != NULL) { + if (fs_lookup(paths, ipath, cfg->sec, cfg->arch, + name, res, ressz) != -1 && cfg->firstmatch) + return 0; + } else { + for (isec = 0; isec < nsec; isec++) if (fs_lookup(paths, ipath, sections[isec], - cfg->arch, *argv, res, ressz) && + cfg->arch, name, res, ressz) != -1 && cfg->firstmatch) - return 1; + return 0; } - if (res != NULL && *ressz == lastsz && - 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--; } - return 0; + return -1; } static void -parse(struct curparse *curp, int fd, const char *file) +process_onefile(struct mparse *mp, struct manpage *resp, int startdir, + struct outstate *outst, struct manconf *conf) { - struct roff_meta *meta; + int fd; + + /* + * Changing directories is not needed in ARG_FILE mode. + * Do it on a best-effort basis. Even in case of + * failure, some functionality may still work. + */ + if (resp->ipath != SIZE_MAX) + (void)chdir(conf->manpath.paths[resp->ipath]); + else if (startdir != -1) + (void)fchdir(startdir); + + mandoc_msg_setinfilename(resp->file); + if (resp->file != NULL) { + if ((fd = mparse_open(mp, resp->file)) == -1) { + mandoc_msg(resp->ipath == SIZE_MAX ? + MANDOCERR_BADARG_BAD : MANDOCERR_OPEN, + 0, 0, "%s", strerror(errno)); + mandoc_msg_setinfilename(NULL); + return; + } + } else + fd = STDIN_FILENO; + + if (outst->use_pager) { + outst->use_pager = 0; + outst->tag_files = term_tag_init(conf->output.outfilename, + outst->outtype == OUTT_HTML ? ".html" : "", + conf->output.tagfilename); +#if HAVE_PLEDGE + if ((conf->output.outfilename != NULL || + conf->output.tagfilename != NULL) && + pledge("stdio rpath cpath", NULL) == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, + "%s", strerror(errno)); + exit(mandoc_msg_getrc()); + } +#endif + } + if (outst->had_output && outst->outtype <= OUTT_UTF8) { + if (outst->outdata == NULL) + outdata_alloc(outst, &conf->output); + terminal_sepline(outst->outdata); + } - /* Begin by parsing the file itself. */ + if (resp->form == FORM_SRC) + parse(mp, fd, resp->file, outst, conf); + else { + passthrough(fd, conf->output.synopsisonly); + outst->had_output = 1; + } + + if (ferror(stdout)) { + if (outst->tag_files != NULL) { + mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s: %s", + outst->tag_files->ofn, strerror(errno)); + term_tag_unlink(); + outst->tag_files = NULL; + } else + mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s", + strerror(errno)); + } + mandoc_msg_setinfilename(NULL); +} + +static void +parse(struct mparse *mp, int fd, const char *file, + struct outstate *outst, struct manconf *conf) +{ + static struct manpaths basepaths; + static int previous; + struct roff_meta *meta; - assert(file); assert(fd >= 0); + if (file == NULL) + file = "<stdin>"; + + if (previous) + mparse_reset(mp); + else + previous = 1; - mparse_readfd(curp->mp, fd, file); + mparse_readfd(mp, fd, file); if (fd != STDIN_FILENO) close(fd); @@ -834,81 +960,89 @@ parse(struct curparse *curp, int fd, const char *file) * level, do not produce output. */ - if (curp->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) + if (outst->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) return; - if (curp->outdata == NULL) - outdata_alloc(curp); - else if (curp->outtype == OUTT_HTML) - html_reset(curp); + if (outst->outdata == NULL) + outdata_alloc(outst, &conf->output); + else if (outst->outtype == OUTT_HTML) + html_reset(outst->outdata); mandoc_xr_reset(); - meta = mparse_result(curp->mp); + meta = mparse_result(mp); /* Execute the out device, if it exists. */ + outst->had_output = 1; if (meta->macroset == MACROSET_MDOC) { - switch (curp->outtype) { + switch (outst->outtype) { case OUTT_HTML: - html_mdoc(curp->outdata, meta); + html_mdoc(outst->outdata, meta); break; case OUTT_TREE: - tree_mdoc(curp->outdata, meta); + tree_mdoc(outst->outdata, meta); break; case OUTT_MAN: - man_mdoc(curp->outdata, meta); + man_mdoc(outst->outdata, meta); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: - terminal_mdoc(curp->outdata, meta); + terminal_mdoc(outst->outdata, meta); break; case OUTT_MARKDOWN: - markdown_mdoc(curp->outdata, meta); + markdown_mdoc(outst->outdata, meta); break; default: break; } } if (meta->macroset == MACROSET_MAN) { - switch (curp->outtype) { + switch (outst->outtype) { case OUTT_HTML: - html_man(curp->outdata, meta); + html_man(outst->outdata, meta); break; case OUTT_TREE: - tree_man(curp->outdata, meta); + tree_man(outst->outdata, meta); break; case OUTT_MAN: - mparse_copy(curp->mp); + mparse_copy(mp); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: - terminal_man(curp->outdata, meta); + terminal_man(outst->outdata, meta); + break; + case OUTT_MARKDOWN: + mandoc_msg(MANDOCERR_MAN_TMARKDOWN, 0, 0, NULL); break; default: break; } } - if (mandoc_msg_getmin() < MANDOCERR_STYLE) - check_xr(); + if (conf->output.tag != NULL && conf->output.tag_found == 0 && + tag_exists(conf->output.tag)) + conf->output.tag_found = 1; + + if (mandoc_msg_getmin() < MANDOCERR_STYLE) { + if (basepaths.sz == 0) + manpath_base(&basepaths); + check_xr(&basepaths); + } else if (mandoc_msg_getmin() < MANDOCERR_WARNING) + check_xr(&conf->manpath); } static void -check_xr(void) +check_xr(struct manpaths *paths) { - static struct manpaths paths; struct mansearch search; struct mandoc_xr *xr; size_t sz; - if (paths.sz == 0) - manpath_base(&paths); - for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { if (xr->line == -1) continue; @@ -917,9 +1051,9 @@ check_xr(void) search.outkey = NULL; search.argmode = ARG_NAME; search.firstmatch = 1; - if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz)) + if (mansearch(&search, paths, 1, &xr->name, NULL, &sz)) continue; - if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz)) + if (fs_search(&search, paths, xr->name, NULL, &sz) != -1) continue; if (xr->count == 1) mandoc_msg(MANDOCERR_XR_BAD, xr->line, @@ -932,26 +1066,26 @@ check_xr(void) } static void -outdata_alloc(struct curparse *curp) +outdata_alloc(struct outstate *outst, struct manoutput *outconf) { - switch (curp->outtype) { + switch (outst->outtype) { case OUTT_HTML: - curp->outdata = html_alloc(curp->outopts); + outst->outdata = html_alloc(outconf); break; case OUTT_UTF8: - curp->outdata = utf8_alloc(curp->outopts); + outst->outdata = utf8_alloc(outconf); break; case OUTT_LOCALE: - curp->outdata = locale_alloc(curp->outopts); + outst->outdata = locale_alloc(outconf); break; case OUTT_ASCII: - curp->outdata = ascii_alloc(curp->outopts); + outst->outdata = ascii_alloc(outconf); break; case OUTT_PDF: - curp->outdata = pdf_alloc(curp->outopts); + outst->outdata = pdf_alloc(outconf); break; case OUTT_PS: - curp->outdata = ps_alloc(curp->outopts); + outst->outdata = ps_alloc(outconf); break; default: break; @@ -959,34 +1093,34 @@ outdata_alloc(struct curparse *curp) } static void -passthrough(const char *file, int fd, int synopsis_only) +passthrough(int fd, int synopsis_only) { const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; const char synr[] = "SYNOPSIS"; FILE *stream; - const char *syscall; char *line, *cp; size_t linesz; ssize_t len, written; - int print; + int lno, print; + stream = NULL; line = NULL; linesz = 0; if (fflush(stdout) == EOF) { - syscall = "fflush"; - goto fail; + mandoc_msg(MANDOCERR_FFLUSH, 0, 0, "%s", strerror(errno)); + goto done; } - if ((stream = fdopen(fd, "r")) == NULL) { close(fd); - syscall = "fdopen"; - goto fail; + mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); + goto done; } - print = 0; + lno = print = 0; while ((len = getline(&line, &linesz, stream)) != -1) { + lno++; cp = line; if (synopsis_only) { if (print) { @@ -1004,98 +1138,24 @@ passthrough(const char *file, int fd, int synopsis_only) } } for (; len > 0; len -= written) { - if ((written = write(STDOUT_FILENO, cp, len)) != -1) - continue; - fclose(stream); - syscall = "write"; - goto fail; + if ((written = write(STDOUT_FILENO, cp, len)) == -1) { + mandoc_msg(MANDOCERR_WRITE, 0, 0, + "%s", strerror(errno)); + goto done; + } } } - - if (ferror(stream)) { - fclose(stream); - syscall = "getline"; - goto fail; - } + if (ferror(stream)) + mandoc_msg(MANDOCERR_GETLINE, lno, 0, "%s", strerror(errno)); done: free(line); - fclose(stream); - return; - -fail: - free(line); - warn("%s: SYSERR: %s", file, syscall); - mandoc_msg_setrc(MANDOCLEVEL_SYSERR); -} - -static int -koptions(int *options, char *arg) -{ - - if ( ! strcmp(arg, "utf-8")) { - *options |= MPARSE_UTF8; - *options &= ~MPARSE_LATIN1; - } else if ( ! strcmp(arg, "iso-8859-1")) { - *options |= MPARSE_LATIN1; - *options &= ~MPARSE_UTF8; - } else if ( ! strcmp(arg, "us-ascii")) { - *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); - } else { - warnx("-K %s: Bad argument", arg); - return 0; - } - return 1; -} - -static void -moptions(int *options, char *arg) -{ - - if (arg == NULL) - return; - if (strcmp(arg, "doc") == 0) - *options |= MPARSE_MDOC; - else if (strcmp(arg, "an") == 0) - *options |= MPARSE_MAN; -} - -static int -toptions(struct curparse *curp, char *arg) -{ - - if (0 == strcmp(arg, "ascii")) - curp->outtype = OUTT_ASCII; - else if (0 == strcmp(arg, "lint")) { - curp->outtype = OUTT_LINT; - 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")) - curp->outtype = OUTT_MAN; - else if (0 == strcmp(arg, "html")) - curp->outtype = OUTT_HTML; - else if (0 == strcmp(arg, "markdown")) - curp->outtype = OUTT_MARKDOWN; - else if (0 == strcmp(arg, "utf8")) - curp->outtype = OUTT_UTF8; - else if (0 == strcmp(arg, "locale")) - curp->outtype = OUTT_LOCALE; - else if (0 == strcmp(arg, "ps")) - curp->outtype = OUTT_PS; - else if (0 == strcmp(arg, "pdf")) - curp->outtype = OUTT_PDF; - else { - warnx("-T %s: Bad argument", arg); - return 0; - } - - return 1; + if (stream != NULL) + fclose(stream); } static int -woptions(struct curparse *curp, char *arg) +woptions(char *arg, enum mandoc_os *os_e, int *wstop) { char *v, *o; const char *toks[11]; @@ -1116,7 +1176,7 @@ woptions(struct curparse *curp, char *arg) o = arg; switch (getsubopt(&arg, (char * const *)toks, &v)) { case 0: - curp->wstop = 1; + *wstop = 1; break; case 1: case 2: @@ -1135,26 +1195,84 @@ woptions(struct curparse *curp, char *arg) mandoc_msg_setmin(MANDOCERR_UNSUPP); break; case 7: - mandoc_msg_setmin(MANDOCERR_MAX); + mandoc_msg_setmin(MANDOCERR_BADARG); break; case 8: mandoc_msg_setmin(MANDOCERR_BASE); - curp->os_e = MANDOC_OS_OPENBSD; + *os_e = MANDOC_OS_OPENBSD; break; case 9: mandoc_msg_setmin(MANDOCERR_BASE); - curp->os_e = MANDOC_OS_NETBSD; + *os_e = MANDOC_OS_NETBSD; break; default: - warnx("-W %s: Bad argument", o); - return 0; + mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-W %s", o); + return -1; + } + } + return 0; +} + +/* + * Wait until moved to the foreground, + * then fork the pager and wait for the user to close it. + */ +static void +run_pager(struct outstate *outst, char *tag_target) +{ + int signum, status; + pid_t man_pgid, tc_pgid; + pid_t pager_pid, wait_pid; + + man_pgid = getpgid(0); + outst->tag_files->tcpgid = + man_pgid == getpid() ? getpgid(getppid()) : man_pgid; + pager_pid = 0; + signum = SIGSTOP; + + for (;;) { + /* Stop here until moved to the foreground. */ + + tc_pgid = tcgetpgrp(STDOUT_FILENO); + if (tc_pgid != man_pgid) { + if (tc_pgid == pager_pid) { + (void)tcsetpgrp(STDOUT_FILENO, man_pgid); + if (signum == SIGTTIN) + continue; + } else + outst->tag_files->tcpgid = tc_pgid; + kill(0, signum); + continue; + } + + /* Once in the foreground, activate the pager. */ + + if (pager_pid) { + (void)tcsetpgrp(STDOUT_FILENO, pager_pid); + kill(pager_pid, SIGCONT); + } else + pager_pid = spawn_pager(outst, tag_target); + + /* Wait for the pager to stop or exit. */ + + while ((wait_pid = waitpid(pager_pid, &status, + WUNTRACED)) == -1 && errno == EINTR) + continue; + + if (wait_pid == -1) { + mandoc_msg(MANDOCERR_WAIT, 0, 0, + "%s", strerror(errno)); + break; } + if (!WIFSTOPPED(status)) + break; + + signum = WSTOPSIG(status); } - return 1; } static pid_t -spawn_pager(struct tag_files *tag_files) +spawn_pager(struct outstate *outst, char *tag_target) { const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ #define MAX_PAGER_ARGS 16 @@ -1167,11 +1285,14 @@ spawn_pager(struct tag_files *tag_files) int argc, use_ofn; pid_t pager_pid; + assert(outst->tag_files->ofd == -1); + assert(outst->tag_files->tfs == NULL); + pager = getenv("MANPAGER"); if (pager == NULL || *pager == '\0') pager = getenv("PAGER"); if (pager == NULL || *pager == '\0') - pager = "more -s"; + pager = BINM_PAGER; cp = mandoc_strdup(pager); /* @@ -1196,51 +1317,59 @@ spawn_pager(struct tag_files *tag_files) use_ofn = 1; #if HAVE_LESS_T - if ((cmdlen = strlen(argv[0])) >= 4) { + if (*outst->tag_files->tfn != '\0' && + (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++] = outst->tag_files->tfn; + if (tag_target != NULL) { argv[argc++] = mandoc_strdup("-t"); - argv[argc++] = tag_files->tagname; + argv[argc++] = tag_target; use_ofn = 0; } } } #endif - if (use_ofn) - argv[argc++] = tag_files->ofn; + if (use_ofn) { + if (outst->outtype == OUTT_HTML && tag_target != NULL) + mandoc_asprintf(&argv[argc], "file://%s#%s", + outst->tag_files->ofn, tag_target); + else + argv[argc] = outst->tag_files->ofn; + argc++; + } argv[argc] = NULL; switch (pager_pid = fork()) { case -1: - err((int)MANDOCLEVEL_SYSERR, "fork"); + mandoc_msg(MANDOCERR_FORK, 0, 0, "%s", strerror(errno)); + exit(mandoc_msg_getrc()); case 0: break; default: (void)setpgid(pager_pid, 0); - (void)tcsetpgrp(tag_files->ofd, pager_pid); + (void)tcsetpgrp(STDOUT_FILENO, pager_pid); #if HAVE_PLEDGE - if (pledge("stdio rpath tmppath tty proc", NULL) == -1) - err((int)MANDOCLEVEL_SYSERR, "pledge"); + if (pledge("stdio rpath tmppath tty proc", NULL) == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, + "%s", strerror(errno)); + exit(mandoc_msg_getrc()); + } #endif - tag_files->pager_pid = pager_pid; + outst->tag_files->pager_pid = pager_pid; return pager_pid; } - /* The child process becomes the pager. */ - - if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) - err((int)MANDOCLEVEL_SYSERR, "pager stdout"); - close(tag_files->ofd); - assert(tag_files->tfd == -1); - - /* Do not start the pager before controlling the terminal. */ + /* + * The child process becomes the pager. + * Do not start it before controlling the terminal. + */ while (tcgetpgrp(STDOUT_FILENO) != getpid()) nanosleep(&timeout, NULL); execvp(argv[0], argv); - err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]); + mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", argv[0], strerror(errno)); + _exit(mandoc_msg_getrc()); } diff --git a/usr/src/cmd/mandoc/man_html.c b/usr/src/cmd/mandoc/man_html.c index 994a208aa0..147c20e464 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.173 2019/03/02 16:30:53 schwarze Exp $ */ +/* $Id: man_html.c,v 1.179 2020/10/16 17:22:43 schwarze Exp $ */ /* + * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * 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,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * HTML formatter for man(7) used by mandoc(1). */ #include "config.h" @@ -34,7 +36,7 @@ #include "main.h" #define MAN_ARGS const struct roff_meta *man, \ - const struct roff_node *n, \ + struct roff_node *n, \ struct html *h struct man_html_act { @@ -167,7 +169,12 @@ print_man_node(MAN_ARGS) if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) return; - html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi); + if ((n->flags & NODE_NOFILL) == 0) + html_fillmode(h, ROFF_fi); + else if (html_fillmode(h, ROFF_nf) == ROFF_nf && + n->tok != ROFF_fi && n->flags & NODE_LINE && + (n->prev == NULL || n->prev->tok != MAN_YS)) + print_endline(h); child = 1; switch (n->type) { @@ -178,7 +185,7 @@ print_man_node(MAN_ARGS) } if (*n->string == ' ' && n->flags & NODE_LINE && (h->flags & HTML_NONEWLINE) == 0) - print_endline(h); + print_otag(h, TAG_BR, ""); else if (n->flags & NODE_DELIMC) h->flags |= HTML_NOSPACE; t = h->tag; @@ -203,9 +210,9 @@ print_man_node(MAN_ARGS) * Close out scope of font prior to opening a macro * scope. */ - if (HTMLFONT_NONE != h->metac) { + if (h->metac != ESCAPE_FONTROMAN) { h->metal = h->metac; - h->metac = HTMLFONT_NONE; + h->metac = ESCAPE_FONTROMAN; } /* @@ -244,20 +251,13 @@ print_man_node(MAN_ARGS) * Close the list if no further item of the same type * follows; otherwise, close the item only. */ - if (list_continues(n, n->next) == '\0') { + if (list_continues(n, roff_node_next(n)) == '\0') { print_tagq(h, t); t = NULL; } } if (t != NULL) print_stagq(h, t); - - 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); - } } static void @@ -310,7 +310,6 @@ static int man_SH_pre(MAN_ARGS) { const char *class; - char *id; enum htmltag tag; if (n->tok == MAN_SH) { @@ -326,10 +325,7 @@ man_SH_pre(MAN_ARGS) print_otag(h, TAG_SECTION, "c", class); break; case ROFFT_HEAD: - id = html_make_id(n, 1); - print_otag(h, tag, "ci", class, id); - if (id != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); + print_otag_id(h, tag, class, n); break; case ROFFT_BODY: break; @@ -445,15 +441,17 @@ list_continues(const struct roff_node *n1, const struct roff_node *n2) static int man_IP_pre(MAN_ARGS) { - const struct roff_node *nn; + 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') { + list_type = list_continues(roff_node_prev(nn), nn); + if (list_type == '\0') { /* Start a new list. */ - if ((list_type = list_continues(nn, nn->next)) == '\0') + list_type = list_continues(nn, roff_node_next(nn)); + if (list_type == '\0') list_type = ' '; switch (list_type) { case ' ': @@ -487,7 +485,7 @@ man_IP_pre(MAN_ARGS) case ROFFT_HEAD: if (body_elem == TAG_LI) return 0; - print_otag(h, TAG_DT, ""); + print_otag_id(h, TAG_DT, NULL, n); break; case ROFFT_BODY: print_otag(h, body_elem, ""); @@ -495,7 +493,6 @@ man_IP_pre(MAN_ARGS) default: abort(); } - switch(n->tok) { case MAN_IP: /* Only print the first header element. */ if (n->child != NULL) diff --git a/usr/src/cmd/mandoc/man_macro.c b/usr/src/cmd/mandoc/man_macro.c index d195576dee..b3c3a3cb48 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.144 2019/01/05 18:59:46 schwarze Exp $ */ +/* $Id: man_macro.c,v 1.145 2020/09/09 17:01:10 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> * * Permission to use, copy, modify, and distribute this software for any @@ -107,9 +107,11 @@ man_unscope(struct roff_man *man, const struct roff_node *to) 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; - else { + if (man->flags & MAN_ELINE) { + if ((man_macro(n->parent->tok)->flags & + MAN_ESCOPED) == 0) + man->flags &= ~MAN_ELINE; + } else { assert(n->type == ROFFT_HEAD); n = n->parent; man->flags &= ~MAN_BLINE; diff --git a/usr/src/cmd/mandoc/man_term.c b/usr/src/cmd/mandoc/man_term.c index 4bb3f9da28..d289f2d12b 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.228 2019/01/05 21:18:26 schwarze Exp $ */ +/* $Id: man_term.c,v 1.236 2021/06/28 19:50:15 schwarze Exp $ */ /* + * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Plain text formatter for man(7), used by mandoc(1) + * for ASCII, UTF-8, PostScript, and PDF output. */ #include "config.h" @@ -27,10 +30,12 @@ #include <string.h> #include "mandoc_aux.h" +#include "mandoc.h" #include "roff.h" #include "man.h" #include "out.h" #include "term.h" +#include "term_tag.h" #include "main.h" #define MAXMARGINS 64 /* maximum number of indented scopes */ @@ -62,7 +67,7 @@ static void print_man_head(struct termp *, static void print_man_foot(struct termp *, const struct roff_meta *); static void print_bvspace(struct termp *, - const struct roff_node *, int); + struct roff_node *, int); static int pre_B(DECL_ARGS); static int pre_DT(DECL_ARGS); @@ -146,7 +151,7 @@ terminal_man(void *arg, const struct roff_meta *man) { struct mtermp mt; struct termp *p; - struct roff_node *n; + struct roff_node *n, *nc, *nn; size_t save_defindent; p = (struct termp *)arg; @@ -165,18 +170,23 @@ terminal_man(void *arg, const struct roff_meta *man) n = man->first->child; if (p->synopsisonly) { - while (n != NULL) { - if (n->tok == MAN_SH && - n->child->child->type == ROFFT_TEXT && - !strcmp(n->child->child->string, "SYNOPSIS")) { - if (n->child->next->child != NULL) - print_man_nodelist(p, &mt, - n->child->next->child, man); - term_newln(p); + for (nn = NULL; n != NULL; n = n->next) { + if (n->tok != MAN_SH) + continue; + nc = n->child->child; + if (nc->type != ROFFT_TEXT) + continue; + if (strcmp(nc->string, "SYNOPSIS") == 0) break; - } - n = n->next; + if (nn == NULL && strcmp(nc->string, "NAME") == 0) + nn = n; } + if (n == NULL) + n = nn; + p->flags |= TERMP_NOSPACE; + if (n != NULL && (n = n->child->next->child) != NULL) + print_man_nodelist(p, &mt, n, man); + term_newln(p); } else { term_begin(p, print_man_head, print_man_foot, man); p->flags |= TERMP_NOSPACE; @@ -196,19 +206,20 @@ terminal_man(void *arg, const struct roff_meta *man) * first, print it. */ static void -print_bvspace(struct termp *p, const struct roff_node *n, int pardist) +print_bvspace(struct termp *p, struct roff_node *n, int pardist) { - int i; + struct roff_node *nch; + int i; term_newln(p); - if (n->body != NULL && n->body->child != NULL) - if (n->body->child->type == ROFFT_TBL) - return; + if (n->body != NULL && + (nch = roff_node_child(n->body)) != NULL && + nch->type == ROFFT_TBL) + return; - if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS) - if (n->prev == NULL) - return; + if (n->parent->tok != MAN_RS && roff_node_prev(n) == NULL) + return; for (i = 0; i < pardist; i++) term_vspace(p); @@ -660,12 +671,8 @@ pre_SS(DECL_ARGS) * and after an empty subsection. */ - do { - n = n->prev; - } while (n != NULL && n->tok >= MAN_TH && - man_term_act(n->tok)->flags & MAN_NOTEXT); - if (n == NULL || n->type == ROFFT_COMMENT || - (n->tok == MAN_SS && n->body->child == NULL)) + if ((n = roff_node_prev(n)) == NULL || + (n->tok == MAN_SS && roff_node_child(n->body) == NULL)) break; for (i = 0; i < mt->pardist; i++) @@ -705,12 +712,8 @@ pre_SH(DECL_ARGS) * and after an empty section. */ - do { - n = n->prev; - } while (n != NULL && n->tok >= MAN_TH && - man_term_act(n->tok)->flags & MAN_NOTEXT); - if (n == NULL || n->type == ROFFT_COMMENT || - (n->tok == MAN_SH && n->body->child == NULL)) + if ((n = roff_node_prev(n)) == NULL || + (n->tok == MAN_SH && roff_node_child(n->body) == NULL)) break; for (i = 0; i < mt->pardist; i++) @@ -816,7 +819,7 @@ pre_SY(DECL_ARGS) switch (n->type) { case ROFFT_BLOCK: - if (n->prev == NULL || n->prev->tok != MAN_SY) + if ((nn = roff_node_prev(n)) == NULL || nn->tok != MAN_SY) print_bvspace(p, n, mt->pardist); return 1; case ROFFT_HEAD: @@ -897,6 +900,9 @@ print_man_node(DECL_ARGS) const struct man_term_act *act; int c; + if (n->flags & NODE_ID) + term_tag_write(n, p->line); + switch (n->type) { case ROFFT_TEXT: /* @@ -1015,10 +1021,6 @@ print_man_foot(struct termp *p, const struct roff_meta *meta) */ if ( ! p->mdocstyle) { - if (meta->hasbody) { - term_vspace(p); - term_vspace(p); - } mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec); } else if (meta->os != NULL) { @@ -1137,9 +1139,5 @@ print_man_head(struct termp *p, const struct roff_meta *meta) */ term_vspace(p); - if ( ! p->mdocstyle) { - term_vspace(p); - term_vspace(p); - } free(title); } diff --git a/usr/src/cmd/mandoc/man_validate.c b/usr/src/cmd/mandoc/man_validate.c index 4bfaf764e6..404b223f2b 100644 --- a/usr/src/cmd/mandoc/man_validate.c +++ b/usr/src/cmd/mandoc/man_validate.c @@ -1,7 +1,7 @@ -/* $Id: man_validate.c,v 1.146 2018/12/31 10:04:39 schwarze Exp $ */ +/* $Id: man_validate.c,v 1.156 2021/08/10 12:55:03 schwarze Exp $ */ /* + * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Validation module for man(7) syntax trees used by mandoc(1). */ #include "config.h" @@ -36,15 +38,17 @@ #include "libmandoc.h" #include "roff_int.h" #include "libman.h" +#include "tag.h" #define CHKARGS struct roff_man *man, struct roff_node *n typedef void (*v_check)(CHKARGS); -static void check_abort(CHKARGS); +static void check_abort(CHKARGS) __attribute__((__noreturn__)); static void check_par(CHKARGS); static void check_part(CHKARGS); static void check_root(CHKARGS); +static void check_tag(struct roff_node *, struct roff_node *); static void check_text(CHKARGS); static void post_AT(CHKARGS); @@ -54,6 +58,7 @@ static void post_IP(CHKARGS); static void post_OP(CHKARGS); static void post_SH(CHKARGS); static void post_TH(CHKARGS); +static void post_TP(CHKARGS); static void post_UC(CHKARGS); static void post_UR(CHKARGS); static void post_in(CHKARGS); @@ -62,8 +67,8 @@ static const v_check man_valids[MAN_MAX - MAN_TH] = { post_TH, /* TH */ post_SH, /* SH */ post_SH, /* SS */ - NULL, /* TP */ - NULL, /* TQ */ + post_TP, /* TP */ + post_TP, /* TQ */ check_abort,/* LP */ check_par, /* PP */ check_abort,/* P */ @@ -185,8 +190,7 @@ check_root(CHKARGS) man->meta.title = mandoc_strdup(""); man->meta.msec = mandoc_strdup(""); - man->meta.date = man->quick ? mandoc_strdup("") : - mandoc_normdate(man, NULL, n->line, n->pos); + man->meta.date = mandoc_normdate(NULL, NULL); } if (man->meta.os_e && @@ -202,6 +206,68 @@ check_abort(CHKARGS) abort(); } +/* + * Skip leading whitespace, dashes, backslashes, and font escapes, + * then create a tag if the first following byte is a letter. + * Priority is high unless whitespace is present. + */ +static void +check_tag(struct roff_node *n, struct roff_node *nt) +{ + const char *cp, *arg; + int prio, sz; + + if (nt == NULL || nt->type != ROFFT_TEXT) + return; + + cp = nt->string; + prio = TAG_STRONG; + for (;;) { + switch (*cp) { + case ' ': + case '\t': + prio = TAG_WEAK; + /* FALLTHROUGH */ + case '-': + cp++; + break; + case '\\': + cp++; + switch (mandoc_escape(&cp, &arg, &sz)) { + case ESCAPE_FONT: + case ESCAPE_FONTBOLD: + case ESCAPE_FONTITALIC: + case ESCAPE_FONTBI: + case ESCAPE_FONTROMAN: + case ESCAPE_FONTCR: + case ESCAPE_FONTCB: + case ESCAPE_FONTCI: + case ESCAPE_FONTPREV: + case ESCAPE_IGNORE: + break; + case ESCAPE_SPECIAL: + if (sz != 1) + return; + switch (*arg) { + case '-': + case 'e': + break; + default: + return; + } + break; + default: + return; + } + break; + default: + if (isalpha((unsigned char)*cp)) + tag_put(cp, prio, n); + return; + } + } +} + static void check_text(CHKARGS) { @@ -247,9 +313,32 @@ static void post_SH(CHKARGS) { struct roff_node *nc; + char *cp, *tag; - if (n->type != ROFFT_BODY || (nc = n->child) == NULL) + nc = n->child; + switch (n->type) { + case ROFFT_HEAD: + tag = NULL; + deroff(&tag, n); + if (tag != NULL) { + for (cp = tag; *cp != '\0'; cp++) + if (*cp == ' ') + *cp = '_'; + if (nc != NULL && nc->type == ROFFT_TEXT && + strcmp(nc->string, tag) == 0) + tag_put(NULL, TAG_STRONG, n); + else + tag_put(tag, TAG_FALLBACK, n); + free(tag); + } + return; + case ROFFT_BODY: + if (nc != NULL) + break; return; + default: + return; + } if (nc->tok == MAN_PP && nc->body->child != NULL) { while (nc->body->last != NULL) { @@ -333,12 +422,14 @@ check_par(CHKARGS) static void post_IP(CHKARGS) { - switch (n->type) { case ROFFT_BLOCK: if (n->head->child == NULL && n->body->child == NULL) roff_node_delete(man, n); break; + case ROFFT_HEAD: + check_tag(n, n->child); + break; case ROFFT_BODY: if (n->parent->head->child == NULL && n->child == NULL) mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos, @@ -349,6 +440,37 @@ post_IP(CHKARGS) } } +/* + * The first next-line element in the head is the tag. + * If that's a font macro, use its first child instead. + */ +static void +post_TP(CHKARGS) +{ + struct roff_node *nt; + + if (n->type != ROFFT_HEAD || (nt = n->child) == NULL) + return; + + while ((nt->flags & NODE_LINE) == 0) + if ((nt = nt->next) == NULL) + return; + + switch (nt->tok) { + case MAN_B: + case MAN_BI: + case MAN_BR: + case MAN_I: + case MAN_IB: + case MAN_IR: + nt = nt->child; + break; + default: + break; + } + check_tag(n, nt); +} + static void post_TH(CHKARGS) { @@ -369,8 +491,8 @@ post_TH(CHKARGS) /* ->TITLE<- MSEC DATE OS VOL */ n = n->child; - if (n && n->string) { - for (p = n->string; '\0' != *p; p++) { + if (n != NULL && n->string != NULL) { + for (p = n->string; *p != '\0'; p++) { /* Only warn about this once... */ if (isalpha((unsigned char)*p) && ! isupper((unsigned char)*p)) { @@ -388,11 +510,16 @@ post_TH(CHKARGS) /* TITLE ->MSEC<- DATE OS VOL */ - if (n) + if (n != NULL) n = n->next; - if (n && n->string) + if (n != NULL && n->string != NULL) { man->meta.msec = mandoc_strdup(n->string); - else { + if (man->filesec != '\0' && + man->filesec != *n->string && + *n->string >= '1' && *n->string <= '9') + mandoc_msg(MANDOCERR_MSEC_FILE, n->line, n->pos, + "*.%c vs TH ... %c", man->filesec, *n->string); + } else { man->meta.msec = mandoc_strdup(""); mandoc_msg(MANDOCERR_MSEC_MISSING, nb->line, nb->pos, "TH %s", man->meta.title); @@ -400,18 +527,12 @@ post_TH(CHKARGS) /* TITLE MSEC ->DATE<- OS VOL */ - if (n) + if (n != NULL) n = n->next; - if (n && n->string && '\0' != n->string[0]) { - man->meta.date = man->quick ? - mandoc_strdup(n->string) : - mandoc_normdate(man, n->string, n->line, n->pos); - } else { + if (man->quick && n != NULL) man->meta.date = mandoc_strdup(""); - mandoc_msg(MANDOCERR_DATE_MISSING, - n ? n->line : nb->line, - n ? n->pos : nb->pos, "TH"); - } + else + man->meta.date = mandoc_normdate(n, nb); /* TITLE MSEC DATE ->OS<- VOL */ diff --git a/usr/src/cmd/mandoc/manconf.h b/usr/src/cmd/mandoc/manconf.h index bb3761998c..c84409d7b5 100644 --- a/usr/src/cmd/mandoc/manconf.h +++ b/usr/src/cmd/mandoc/manconf.h @@ -1,6 +1,6 @@ -/* $Id: manconf.h,v 1.7 2018/11/22 11:30:23 schwarze Exp $ */ +/* $OpenBSD: manconf.h,v 1.7 2018/11/22 11:30:15 schwarze Exp $ */ /* - * Copyright (c) 2011, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Public interface to man(1) configuration management. + * For use by the main program and by the formatters. */ /* List of unique, absolute paths to manual trees. */ @@ -28,15 +31,18 @@ struct manpaths { struct manoutput { char *includes; char *man; + char *outfilename; char *paper; char *style; char *tag; + char *tagfilename; size_t indent; size_t width; int fragment; int mdoc; int noval; int synopsisonly; + int tag_found; int toc; }; diff --git a/usr/src/cmd/mandoc/mandoc.c b/usr/src/cmd/mandoc/mandoc.c index fb9395a585..6adf1a4318 100644 --- a/usr/src/cmd/mandoc/mandoc.c +++ b/usr/src/cmd/mandoc/mandoc.c @@ -1,7 +1,7 @@ -/* $Id: mandoc.c,v 1.114 2018/12/30 00:49:55 schwarze Exp $ */ +/* $Id: mandoc.c,v 1.119 2021/08/10 12:55:03 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-2021 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 @@ -74,12 +74,12 @@ mandoc_font(const char *cp, int sz) case 'C': switch (cp[1]) { case 'B': - return ESCAPE_FONTBOLD; + return ESCAPE_FONTCB; case 'I': - return ESCAPE_FONTITALIC; + return ESCAPE_FONTCI; case 'R': case 'W': - return ESCAPE_FONTCW; + return ESCAPE_FONTCR; default: return ESCAPE_ERROR; } @@ -203,7 +203,18 @@ mandoc_escape(const char **end, const char **start, int *sz) case 'O': case 'V': case 'Y': - gly = (*start)[-1] == 'f' ? ESCAPE_FONT : ESCAPE_IGNORE; + case '*': + switch ((*start)[-1]) { + case 'f': + gly = ESCAPE_FONT; + break; + case '*': + gly = ESCAPE_DEVICE; + break; + default: + gly = ESCAPE_IGNORE; + break; + } switch (**start) { case '(': if ((*start)[-1] == 'O') @@ -238,13 +249,6 @@ mandoc_escape(const char **end, const char **start, int *sz) 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 @@ -459,6 +463,9 @@ mandoc_escape(const char **end, const char **start, int *sz) + 1 == *sz) gly = ESCAPE_UNICODE; break; + case ESCAPE_DEVICE: + assert(*sz == 2 && (*start)[0] == '.' && (*start)[1] == 'T'); + break; default: break; } @@ -494,9 +501,10 @@ time2a(time_t t) size_t ssz; int isz; + buf = NULL; tm = localtime(&t); if (tm == NULL) - return NULL; + goto fail; /* * Reserve space: @@ -520,7 +528,8 @@ time2a(time_t t) * of looking at LC_TIME. */ - if ((isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)) == -1) + isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday); + if (isz < 0 || isz > 4) goto fail; p += isz; @@ -530,46 +539,63 @@ time2a(time_t t) fail: free(buf); - return NULL; + return mandoc_strdup(""); } char * -mandoc_normdate(struct roff_man *man, char *in, int ln, int pos) +mandoc_normdate(struct roff_node *nch, struct roff_node *nbl) { char *cp; time_t t; - /* No date specified: use today's date. */ + /* No date specified. */ - if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) { - mandoc_msg(MANDOCERR_DATE_MISSING, ln, pos, NULL); - return time2a(time(NULL)); + if (nch == NULL) { + if (nbl == NULL) + mandoc_msg(MANDOCERR_DATE_MISSING, 0, 0, NULL); + else + mandoc_msg(MANDOCERR_DATE_MISSING, nbl->line, + nbl->pos, "%s", roff_name[nbl->tok]); + return mandoc_strdup(""); } + if (*nch->string == '\0') { + mandoc_msg(MANDOCERR_DATE_MISSING, nch->line, + nch->pos, "%s", roff_name[nbl->tok]); + return mandoc_strdup(""); + } + if (strcmp(nch->string, "$" "Mdocdate$") == 0) + return time2a(time(NULL)); /* Valid mdoc(7) date format. */ - if (a2time(&t, "$" "Mdocdate: %b %d %Y $", in) || - a2time(&t, "%b %d, %Y", in)) { + if (a2time(&t, "$" "Mdocdate: %b %d %Y $", nch->string) || + a2time(&t, "%b %d, %Y", nch->string)) { cp = time2a(t); if (t > time(NULL) + 86400) - mandoc_msg(MANDOCERR_DATE_FUTURE, ln, pos, "%s", cp); - else if (*in != '$' && strcmp(in, cp) != 0) - mandoc_msg(MANDOCERR_DATE_NORM, ln, pos, "%s", cp); + mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, + nch->pos, "%s %s", roff_name[nbl->tok], cp); + else if (*nch->string != '$' && + strcmp(nch->string, cp) != 0) + mandoc_msg(MANDOCERR_DATE_NORM, nch->line, + nch->pos, "%s %s", roff_name[nbl->tok], 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, ln, pos, "%s", in); + if (a2time(&t, "%Y-%m-%d", nch->string) == 0) + mandoc_msg(MANDOCERR_DATE_BAD, nch->line, nch->pos, + "%s %s", roff_name[nbl->tok], nch->string); else if (t > time(NULL) + 86400) - 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); + mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, nch->pos, + "%s %s", roff_name[nbl->tok], nch->string); + else if (nbl->tok == MDOC_Dd) + mandoc_msg(MANDOCERR_DATE_LEGACY, nch->line, nch->pos, + "Dd %s", nch->string); /* Use any non-mdoc(7) date verbatim. */ - return mandoc_strdup(in); + return mandoc_strdup(nch->string); } int diff --git a/usr/src/cmd/mandoc/mandoc.h b/usr/src/cmd/mandoc/mandoc.h index a44b192e0f..9837ff2ae9 100644 --- a/usr/src/cmd/mandoc/mandoc.h +++ b/usr/src/cmd/mandoc/mandoc.h @@ -1,7 +1,7 @@ -/* $Id: mandoc.h,v 1.262 2018/12/16 00:17:02 schwarze Exp $ */ +/* $Id: mandoc.h,v 1.274 2021/08/14 13:53:08 schwarze Exp $ */ /* + * Copyright (c) 2012-2021 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -16,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Error handling, escape sequence, and character utilities. + * Can be used by all code in the mandoc package. */ #define ASCII_NBRSP 31 /* non-breaking space */ @@ -53,7 +54,6 @@ enum mandocerr { MANDOCERR_ARCH_BAD, /* unknown architecture: Dt ... arch */ MANDOCERR_OS_ARG, /* operating system explicitly specified: Os ... */ MANDOCERR_RCS_MISSING, /* RCS id missing */ - MANDOCERR_XR_BAD, /* referenced manual not found: Xr name sec */ MANDOCERR_STYLE, /* ===== start of style suggestions ===== */ @@ -67,10 +67,12 @@ enum mandocerr { MANDOCERR_BX, /* consider using OS macro: macro */ MANDOCERR_ER_ORDER, /* errnos out of order: Er ... */ MANDOCERR_ER_REP, /* duplicate errno: Er ... */ + MANDOCERR_XR_BAD, /* referenced manual not found: Xr name sec */ MANDOCERR_DELIM, /* trailing delimiter: macro ... */ MANDOCERR_DELIM_NB, /* no blank before trailing delimiter: macro ... */ MANDOCERR_FI_SKIP, /* fill mode already enabled, skipping: fi */ MANDOCERR_NF_SKIP, /* fill mode already disabled, skipping: nf */ + MANDOCERR_TEXT_LONG, /* input text line longer than 80 bytes */ MANDOCERR_DASHDASH, /* verbatim "--", maybe consider using \(em */ MANDOCERR_FUNC, /* function name without markup: name() */ MANDOCERR_SPACE_EOL, /* whitespace at end of input line */ @@ -83,7 +85,8 @@ enum mandocerr { MANDOCERR_TH_NOTITLE, /* missing manual title, using "": [macro] */ MANDOCERR_MSEC_MISSING, /* missing manual section, using "": macro */ MANDOCERR_MSEC_BAD, /* unknown manual section: Dt ... section */ - MANDOCERR_DATE_MISSING, /* missing date, using today's date */ + MANDOCERR_MSEC_FILE, /* filename/section mismatch: ... */ + MANDOCERR_DATE_MISSING, /* missing date, using "": [macro] */ MANDOCERR_DATE_BAD, /* cannot parse date, using it verbatim: date */ MANDOCERR_DATE_FUTURE, /* date in the future, using it anyway: date */ MANDOCERR_OS_MISSING, /* missing Os macro, using "" */ @@ -187,13 +190,13 @@ enum mandocerr { MANDOCERR_TBLLAYOUT_NONE, /* empty tbl layout */ MANDOCERR_TBLLAYOUT_CHAR, /* invalid character in tbl layout: char */ MANDOCERR_TBLLAYOUT_PAR, /* unmatched parenthesis in tbl layout */ + MANDOCERR_TBLLAYOUT_SPC, /* ignoring excessive spacing in tbl layout */ MANDOCERR_TBLDATA_NONE, /* tbl without any data cells */ MANDOCERR_TBLDATA_SPAN, /* ignoring data in spanned tbl cell: data */ MANDOCERR_TBLDATA_EXTRA, /* ignoring extra tbl data cells: data */ MANDOCERR_TBLDATA_BLK, /* data block open at end of tbl: macro */ /* related to document structure and macros */ - MANDOCERR_FILE, /* cannot open file */ MANDOCERR_PROLOG_REP, /* duplicate prologue macro: macro */ MANDOCERR_DT_LATE, /* skipping late title macro: Dt args */ MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */ @@ -224,6 +227,7 @@ enum mandocerr { MANDOCERR_SHIFT, /* excessive shift: ..., but max is ... */ MANDOCERR_SO_PATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */ MANDOCERR_SO_FAIL, /* .so request failed */ + MANDOCERR_TG_SPC, /* skipping tag containing whitespace: tag */ MANDOCERR_ARG_SKIP, /* skipping all arguments: macro args */ MANDOCERR_ARG_EXCESS, /* skipping excess arguments: macro ... args */ MANDOCERR_DIVZERO, /* divide by zero */ @@ -241,6 +245,38 @@ enum mandocerr { MANDOCERR_TBLOPT_EQN, /* eqn delim option in tbl: arg */ MANDOCERR_TBLLAYOUT_MOD, /* unsupported tbl layout modifier: m */ MANDOCERR_TBLMACRO, /* ignoring macro in table: macro */ + MANDOCERR_TBL_TMAN, /* skipping tbl in -Tman mode */ + MANDOCERR_EQN_TMAN, /* skipping eqn in -Tman mode */ + + MANDOCERR_BADARG, /* ===== start of bad invocations ===== */ + + MANDOCERR_BADARG_BAD, /* bad argument */ + MANDOCERR_BADARG_DUPE, /* duplicate argument */ + MANDOCERR_BADVAL, /* does not take a value */ + MANDOCERR_BADVAL_MISS, /* missing argument value */ + MANDOCERR_BADVAL_BAD, /* bad argument value */ + MANDOCERR_BADVAL_DUPE, /* duplicate argument value */ + MANDOCERR_TAG, /* no such tag */ + MANDOCERR_MAN_TMARKDOWN, /* -Tmarkdown unsupported for man(7) input */ + + MANDOCERR_SYSERR, /* ===== start of system errors ===== */ + + MANDOCERR_DUP, + MANDOCERR_EXEC, + MANDOCERR_FDOPEN, + MANDOCERR_FFLUSH, + MANDOCERR_FORK, + MANDOCERR_FSTAT, + MANDOCERR_GETLINE, + MANDOCERR_GLOB, + MANDOCERR_GZCLOSE, + MANDOCERR_GZDOPEN, + MANDOCERR_MKSTEMP, + MANDOCERR_OPEN, + MANDOCERR_PLEDGE, + MANDOCERR_READ, + MANDOCERR_WAIT, + MANDOCERR_WRITE, MANDOCERR_MAX }; @@ -256,7 +292,9 @@ enum mandoc_esc { ESCAPE_FONTITALIC, /* italic font mode */ ESCAPE_FONTBI, /* bold italic font mode */ ESCAPE_FONTROMAN, /* roman font mode */ - ESCAPE_FONTCW, /* constant width font mode */ + ESCAPE_FONTCR, /* constant width font mode */ + ESCAPE_FONTCB, /* constant width bold font mode */ + ESCAPE_FONTCI, /* constant width italic font mode */ ESCAPE_FONTPREV, /* previous font mode */ ESCAPE_NUMBERED, /* a numbered glyph */ ESCAPE_UNICODE, /* a unicode codepoint */ @@ -270,7 +308,7 @@ enum mandoc_esc { }; -enum mandoc_esc mandoc_font(const char *, int sz); +enum mandoc_esc mandoc_font(const char *, int); enum mandoc_esc mandoc_escape(const char **, const char **, int *); void mandoc_msg_setoutfile(FILE *); const char *mandoc_msg_getinfilename(void); @@ -281,6 +319,7 @@ 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 mandoc_msg_summary(void); void mchars_alloc(void); void mchars_free(void); int mchars_num2char(const char *, size_t); diff --git a/usr/src/cmd/mandoc/mandoc_msg.c b/usr/src/cmd/mandoc/mandoc_msg.c index ff4d80313c..edd804d9a3 100644 --- a/usr/src/cmd/mandoc/mandoc_msg.c +++ b/usr/src/cmd/mandoc/mandoc_msg.c @@ -1,7 +1,7 @@ -/* $Id: mandoc_msg.c,v 1.6 2019/03/06 15:55:38 schwarze Exp $ */ +/* $OpenBSD: mandoc_msg.c,v 1.8 2020/01/19 17:59:01 schwarze Exp $ */ /* + * Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org> * 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 @@ -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. + * + * Implementation of warning and error messages for mandoc(1). */ #include "config.h" @@ -29,8 +31,8 @@ static const enum mandocerr lowest_type[MANDOCLEVEL_MAX] = { MANDOCERR_WARNING, MANDOCERR_ERROR, MANDOCERR_UNSUPP, - MANDOCERR_MAX, - MANDOCERR_MAX + MANDOCERR_BADARG, + MANDOCERR_SYSERR }; static const char *const level_name[MANDOCLEVEL_MAX] = { @@ -53,11 +55,10 @@ static const char *const type_message[MANDOCERR_MAX] = { "unknown architecture", "operating system explicitly specified", "RCS id missing", - "referenced manual not found", "generic style suggestion", - "legacy man(7) date format", + "legacy man(5) date format", "normalizing date format to", "lower case character in document title", "duplicate RCS id", @@ -67,10 +68,12 @@ static const char *const type_message[MANDOCERR_MAX] = { "consider using OS macro", "errnos out of order", "duplicate errno", + "referenced manual not found", "trailing delimiter", "no blank before trailing delimiter", "fill mode already enabled, skipping", "fill mode already disabled, skipping", + "input text line longer than 80 bytes", "verbatim \"--\", maybe consider using \\(em", "function name without markup", "whitespace at end of input line", @@ -83,7 +86,8 @@ static const char *const type_message[MANDOCERR_MAX] = { "missing manual title, using \"\"", "missing manual section, using \"\"", "unknown manual section", - "missing date, using today's date", + "filename/section mismatch", + "missing date, using \"\"", "cannot parse date, using it verbatim", "date in the future, using it anyway", "missing Os macro, using \"\"", @@ -187,13 +191,13 @@ static const char *const type_message[MANDOCERR_MAX] = { "empty tbl layout", "invalid character in tbl layout", "unmatched parenthesis in tbl layout", + "ignoring excessive spacing 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?", @@ -224,6 +228,7 @@ static const char *const type_message[MANDOCERR_MAX] = { "excessive shift", "NOT IMPLEMENTED: .so with absolute path or \"..\"", ".so request failed", + "skipping tag containing whitespace", "skipping all arguments", "skipping excess arguments", "divide by zero", @@ -240,11 +245,43 @@ static const char *const type_message[MANDOCERR_MAX] = { "eqn delim option in tbl", "unsupported tbl layout modifier", "ignoring macro in table", + "skipping tbl in -Tman mode", + "skipping eqn in -Tman mode", + + /* bad command line arguments */ + NULL, + "bad command line argument", + "duplicate command line argument", + "option has a superfluous value", + "missing option value", + "bad option value", + "duplicate option value", + "no such tag", + "-Tmarkdown unsupported for man(5) input", + + /* system errors */ + NULL, + "dup", + "exec", + "fdopen", + "fflush", + "fork", + "fstat", + "getline", + "glob", + "gzclose", + "gzdopen", + "mkstemp", + "open", + "pledge", + "read", + "wait", + "write", }; static FILE *fileptr = NULL; static const char *filename = NULL; -static enum mandocerr min_type = MANDOCERR_MAX; +static enum mandocerr min_type = MANDOCERR_BADARG; static enum mandoclevel rc = MANDOCLEVEL_OK; @@ -297,10 +334,10 @@ 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) + if (t < min_type) return; - level = MANDOCLEVEL_UNSUPP; + level = MANDOCLEVEL_SYSERR; while (t < lowest_type[level]) level--; mandoc_msg_setrc(level); @@ -327,3 +364,12 @@ mandoc_msg(enum mandocerr t, int line, int col, const char *fmt, ...) } fputc('\n', fileptr); } + +void +mandoc_msg_summary(void) +{ + if (fileptr != NULL && rc != MANDOCLEVEL_OK) + fprintf(fileptr, + "%s: see above the output for %s messages\n", + getprogname(), level_name[rc]); +} diff --git a/usr/src/cmd/mandoc/mandoc_ohash.c b/usr/src/cmd/mandoc/mandoc_ohash.c index 0627b469c6..c4ebfcd428 100644 --- a/usr/src/cmd/mandoc/mandoc_ohash.c +++ b/usr/src/cmd/mandoc/mandoc_ohash.c @@ -1,4 +1,4 @@ -/* $Id: mandoc_ohash.c,v 1.2 2015/10/19 18:58:47 schwarze Exp $ */ +/* $Id: mandoc_ohash.c,v 1.3 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> * @@ -14,6 +14,8 @@ * 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 <sys/types.h> #include <stddef.h> #include <stdint.h> diff --git a/usr/src/cmd/mandoc/mandoc_parse.h b/usr/src/cmd/mandoc/mandoc_parse.h index 61341f0d7f..b8b29dd931 100644 --- a/usr/src/cmd/mandoc/mandoc_parse.h +++ b/usr/src/cmd/mandoc/mandoc_parse.h @@ -1,4 +1,4 @@ -/* $Id: mandoc_parse.h,v 1.4 2018/12/30 00:49:55 schwarze Exp $ */ +/* $Id: mandoc_parse.h,v 1.5 2019/11/09 14:39:49 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org> @@ -29,6 +29,7 @@ #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 */ +#define MPARSE_COMMENT (1 << 7) /* save comments in the tree */ struct roff_meta; diff --git a/usr/src/cmd/mandoc/mandoc_xr.c b/usr/src/cmd/mandoc/mandoc_xr.c index da0a7f0cf2..6d1b1a8d8a 100644 --- a/usr/src/cmd/mandoc/mandoc_xr.c +++ b/usr/src/cmd/mandoc/mandoc_xr.c @@ -1,4 +1,4 @@ -/* $Id: mandoc_xr.c,v 1.3 2017/07/02 21:18:29 schwarze Exp $ */ +/* $Id: mandoc_xr.c,v 1.4 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> * @@ -14,6 +14,8 @@ * 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 <sys/types.h> #include <assert.h> diff --git a/usr/src/cmd/mandoc/mandocdb.c b/usr/src/cmd/mandoc/mandocdb.c index 222350c987..a3360fe44d 100644 --- a/usr/src/cmd/mandoc/mandocdb.c +++ b/usr/src/cmd/mandoc/mandocdb.c @@ -1,7 +1,7 @@ -/* $Id: mandocdb.c,v 1.262 2018/12/30 00:49:55 schwarze Exp $ */ +/* $Id: mandocdb.c,v 1.269 2021/08/19 16:55:31 schwarze Exp $ */ /* + * Copyright (c) 2011-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -15,6 +15,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Implementation of the makewhatis(8) program. */ #include "config.h" @@ -118,7 +120,7 @@ struct mdoc_handler { int mandocdb(int, char *[]); static void dbadd(struct dba *, struct mpage *); -static void dbadd_mlink(const struct mlink *mlink); +static void dbadd_mlink(const struct mlink *); static void dbprune(struct dba *); static void dbwrite(struct dba *); static void filescan(const char *); @@ -163,6 +165,9 @@ static void putkey(const struct mpage *, char *, uint64_t); static void putkeys(const struct mpage *, char *, size_t, uint64_t); static void putmdockey(const struct mpage *, const struct roff_node *, uint64_t, int); +#ifdef READ_ALLOWED_PATH +static int read_allowed(const char *); +#endif static int render_string(char **, size_t *); static void say(const char *, const char *, ...) __attribute__((__format__ (__printf__, 2, 3))); @@ -179,6 +184,7 @@ static int write_utf8; /* write UTF-8 output; else ASCII */ static int exitcode; /* to be returned by main */ static enum op op; /* operational mode */ static char basedir[PATH_MAX]; /* current base directory */ +static size_t basedir_len; /* strlen(basedir) */ static struct mpage *mpage_head; /* list of distinct manual pages */ static struct ohash mpages; /* table of distinct manual pages */ static struct ohash mlinks; /* table of directory entries */ @@ -342,7 +348,7 @@ mandocdb(int argc, char *argv[]) * clobber each other. */ #define CHECKOP(_op, _ch) do \ - if (OP_DEFAULT != (_op)) { \ + if ((_op) != OP_DEFAULT) { \ warnx("-%c: Conflicting option", (_ch)); \ goto usage; \ } while (/*CONSTCOND*/0) @@ -351,7 +357,7 @@ mandocdb(int argc, char *argv[]) path_arg = NULL; op = OP_DEFAULT; - while (-1 != (ch = getopt(argc, argv, "aC:Dd:npQT:tu:v"))) + while ((ch = getopt(argc, argv, "aC:Dd:npQT:tu:v")) != -1) switch (ch) { case 'a': use_all = 1; @@ -379,7 +385,7 @@ mandocdb(int argc, char *argv[]) mparse_options |= MPARSE_QUICK; break; case 'T': - if (strcmp(optarg, "utf8")) { + if (strcmp(optarg, "utf8") != 0) { warnx("-T%s: Unsupported output format", optarg); goto usage; @@ -416,7 +422,7 @@ mandocdb(int argc, char *argv[]) } #endif - if (OP_CONFFILE == op && argc > 0) { + if (op == OP_CONFFILE && argc > 0) { warnx("-C: Too many arguments"); goto usage; } @@ -427,13 +433,13 @@ mandocdb(int argc, char *argv[]) mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); - if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) { + if (op == OP_UPDATE || op == OP_DELETE || op == OP_TEST) { /* * Most of these deal with a specific directory. * Jump into that directory first. */ - if (OP_TEST != op && 0 == set_basedir(path_arg, 1)) + if (op != OP_TEST && set_basedir(path_arg, 1) == 0) goto out; dba = nodb ? dba_new(128) : dba_read(MANDOC_DB); @@ -454,11 +460,11 @@ mandocdb(int argc, char *argv[]) " from scratch", strerror(errno)); exitcode = (int)MANDOCLEVEL_OK; op = OP_DEFAULT; - if (0 == treescan()) + if (treescan() == 0) goto out; dba = dba_new(128); } - if (OP_DELETE != op) + if (op != OP_DELETE) mpages_merge(dba, mp); if (nodb == 0) dbwrite(dba); @@ -492,7 +498,7 @@ mandocdb(int argc, char *argv[]) sz = strlen(conf.manpath.paths[j]); if (sz && conf.manpath.paths[j][sz - 1] == '/') conf.manpath.paths[j][--sz] = '\0'; - if (0 == sz) + if (sz == 0) continue; if (j) { @@ -502,9 +508,9 @@ mandocdb(int argc, char *argv[]) offsetof(struct mlink, file)); } - if ( ! set_basedir(conf.manpath.paths[j], argc > 0)) + if (set_basedir(conf.manpath.paths[j], argc > 0) == 0) continue; - if (0 == treescan()) + if (treescan() == 0) continue; dba = dba_new(128); mpages_merge(dba, mp); @@ -608,9 +614,9 @@ treescan(void) say(path, "&realpath"); continue; } - if (strstr(buf, basedir) != buf -#ifdef HOMEBREWDIR - && strstr(buf, HOMEBREWDIR) != buf + if (strncmp(buf, basedir, basedir_len) != 0 +#ifdef READ_ALLOWED_PATH + && !read_allowed(buf) #endif ) { if (warnings) say("", @@ -623,6 +629,8 @@ treescan(void) say(path, "&stat"); continue; } + if ((ff->fts_statp->st_mode & S_IFMT) != S_IFREG) + continue; /* FALLTHROUGH */ /* @@ -777,17 +785,17 @@ treescan(void) * See treescan() for the fts(3) version of this. */ static void -filescan(const char *file) +filescan(const char *infile) { - char buf[PATH_MAX]; struct stat st; struct mlink *mlink; - char *p, *start; + char *linkfile, *p, *realdir, *start, *usefile; + size_t realdir_len; assert(use_all); - if (0 == strncmp(file, "./", 2)) - file += 2; + if (strncmp(infile, "./", 2) == 0) + infile += 2; /* * We have to do lstat(2) before realpath(3) loses @@ -796,13 +804,13 @@ filescan(const char *file) * we want to use the orginal file name, while for * regular files, we want to use the real path. */ - if (-1 == lstat(file, &st)) { + if (lstat(infile, &st) == -1) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&lstat"); + say(infile, "&lstat"); return; - } else if (0 == ((S_IFREG | S_IFLNK) & st.st_mode)) { + } else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "Not a regular file"); + say(infile, "Not a regular file"); return; } @@ -810,23 +818,24 @@ filescan(const char *file) * We have to resolve the file name to the real path * in any case for the base directory check. */ - if (NULL == realpath(file, buf)) { + if ((usefile = realpath(infile, NULL)) == NULL) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&realpath"); + say(infile, "&realpath"); return; } - if (OP_TEST == op) - start = buf; - else if (strstr(buf, basedir) == buf) - start = buf + strlen(basedir); -#ifdef HOMEBREWDIR - else if (strstr(buf, HOMEBREWDIR) == buf) - start = buf; + if (op == OP_TEST) + start = usefile; + else if (strncmp(usefile, basedir, basedir_len) == 0) + start = usefile + basedir_len; +#ifdef READ_ALLOWED_PATH + else if (read_allowed(usefile)) + start = usefile; #endif else { exitcode = (int)MANDOCLEVEL_BADARG; - say("", "%s: outside base directory", buf); + say("", "%s: outside base directory", infile); + free(usefile); return; } @@ -834,25 +843,72 @@ filescan(const char *file) * Now we are sure the file is inside our tree. * If it is a symbolic link, ignore the real path * and use the original name. - * This implies passing stuff like "cat1/../man1/foo.1" - * on the command line won't work. So don't do that. - * Note the stat(2) can still fail if the link target - * doesn't exist. */ - if (S_IFLNK & st.st_mode) { - if (-1 == stat(buf, &st)) { + do { + if (S_ISLNK(st.st_mode) == 0) + break; + + /* + * Some implementations of realpath(3) may succeed + * even if the target of the link does not exist, + * so check again for extra safety. + */ + if (stat(usefile, &st) == -1) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&stat"); + say(infile, "&stat"); + free(usefile); return; } - if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) { - say(file, "Filename too long"); - return; + linkfile = mandoc_strdup(infile); + if (op == OP_TEST) { + free(usefile); + start = usefile = linkfile; + break; } - start = buf; - if (OP_TEST != op && strstr(buf, basedir) == buf) - start += strlen(basedir); - } + if (strncmp(infile, basedir, basedir_len) == 0) { + free(usefile); + usefile = linkfile; + start = usefile + basedir_len; + break; + } + + /* + * This symbolic link points into the basedir + * from the outside. Let's see whether any of + * the parent directories resolve to the basedir. + */ + p = strchr(linkfile, '\0'); + do { + while (*--p != '/') + continue; + *p = '\0'; + if ((realdir = realpath(linkfile, NULL)) == NULL) { + exitcode = (int)MANDOCLEVEL_BADARG; + say(infile, "&realpath"); + free(linkfile); + free(usefile); + return; + } + realdir_len = strlen(realdir) + 1; + free(realdir); + *p = '/'; + } while (realdir_len > basedir_len); + + /* + * If one of the directories resolves to the basedir, + * use the rest of the original name. + * Otherwise, the best we can do + * is to use the filename pointed to. + */ + if (realdir_len == basedir_len) { + free(usefile); + usefile = linkfile; + start = p + 1; + } else { + free(linkfile); + start = usefile + basedir_len; + } + } while (/* CONSTCOND */ 0); mlink = mandoc_calloc(1, sizeof(struct mlink)); mlink->dform = FORM_NONE; @@ -860,6 +916,7 @@ filescan(const char *file) sizeof(mlink->file)) { say(start, "Filename too long"); free(mlink); + free(usefile); return; } @@ -868,13 +925,13 @@ filescan(const char *file) * but outside our tree, guess the base directory. */ - if (op == OP_TEST || (start == buf && *start == '/')) { - if (strncmp(buf, "man/", 4) == 0) - start = buf + 4; - else if ((start = strstr(buf, "/man/")) != NULL) + if (op == OP_TEST || (start == usefile && *start == '/')) { + if (strncmp(usefile, "man/", 4) == 0) + start = usefile + 4; + else if ((start = strstr(usefile, "/man/")) != NULL) start += 5; else - start = buf; + start = usefile; } /* @@ -883,18 +940,18 @@ filescan(const char *file) * If we find one of these and what's underneath is a directory, * assume it's an architecture. */ - if (NULL != (p = strchr(start, '/'))) { + if ((p = strchr(start, '/')) != NULL) { *p++ = '\0'; - if (0 == strncmp(start, "man", 3)) { + if (strncmp(start, "man", 3) == 0) { mlink->dform = FORM_SRC; mlink->dsec = start + 3; - } else if (0 == strncmp(start, "cat", 3)) { + } else if (strncmp(start, "cat", 3) == 0) { mlink->dform = FORM_CAT; mlink->dsec = start + 3; } start = p; - if (NULL != mlink->dsec && NULL != (p = strchr(start, '/'))) { + if (mlink->dsec != NULL && (p = strchr(start, '/')) != NULL) { *p++ = '\0'; mlink->arch = start; start = p; @@ -906,10 +963,10 @@ filescan(const char *file) * Suffix of `.0' indicates a catpage, `.1-9' is a manpage. */ p = strrchr(start, '\0'); - while (p-- > start && '/' != *p && '.' != *p) - /* Loop. */ ; + while (p-- > start && *p != '/' && *p != '.') + continue; - if ('.' == *p) { + if (*p == '.') { *p++ = '\0'; mlink->fsec = p; } @@ -919,11 +976,12 @@ filescan(const char *file) * Use the filename portion of the path. */ mlink->name = start; - if (NULL != (p = strrchr(start, '/'))) { + if ((p = strrchr(start, '/')) != NULL) { mlink->name = p + 1; *p = '\0'; } mlink_add(mlink, &st); + free(usefile); } static void @@ -1186,9 +1244,11 @@ mpages_merge(struct dba *dba, struct mparse *mp) mlink->next = mlink_dest->next; mlink_dest->next = mpage->mlinks; mpage->mlinks = NULL; + goto nextpage; } - goto nextpage; - } else if (meta != NULL && meta->macroset == MACROSET_MDOC) { + meta->macroset = MACROSET_NONE; + } + if (meta != NULL && meta->macroset == MACROSET_MDOC) { mpage->form = FORM_SRC; mpage->sec = meta->msec; mpage->sec = mandoc_strdup( @@ -1208,12 +1268,15 @@ mpages_merge(struct dba *dba, struct mparse *mp) } assert(mpage->desc == NULL); - if (meta == NULL) { - mpage->form = FORM_CAT; + if (meta == NULL || meta->sodest != NULL) { mpage->sec = mandoc_strdup(mlink->dsec); mpage->arch = mandoc_strdup(mlink->arch); mpage->title = mandoc_strdup(mlink->name); - parse_cat(mpage, fd); + if (meta == NULL) { + mpage->form = FORM_CAT; + parse_cat(mpage, fd); + } else + mpage->form = FORM_SRC; } else if (meta->macroset == MACROSET_MDOC) parse_mdoc(mpage, meta, meta->first); else @@ -2245,7 +2308,6 @@ set_basedir(const char *targetdir, int report_baddir) static char startdir[PATH_MAX]; static int getcwd_status; /* 1 = ok, 2 = failure */ static int chdir_status; /* 1 = changed directory */ - char *cp; /* * Remember the original working directory, if possible. @@ -2254,8 +2316,8 @@ set_basedir(const char *targetdir, int report_baddir) * Do not error out if the current directory is not * searchable: Maybe it won't be needed after all. */ - if (0 == getcwd_status) { - if (NULL == getcwd(startdir, sizeof(startdir))) { + if (getcwd_status == 0) { + if (getcwd(startdir, sizeof(startdir)) == NULL) { getcwd_status = 2; (void)strlcpy(startdir, strerror(errno), sizeof(startdir)); @@ -2268,19 +2330,20 @@ set_basedir(const char *targetdir, int report_baddir) * Do not use it any longer, not even for messages. */ *basedir = '\0'; + basedir_len = 0; /* * If and only if the directory was changed earlier and * the next directory to process is given as a relative path, * first go back, or bail out if that is impossible. */ - if (chdir_status && '/' != *targetdir) { - if (2 == getcwd_status) { + if (chdir_status && *targetdir != '/') { + if (getcwd_status == 2) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "getcwd: %s", startdir); return 0; } - if (-1 == chdir(startdir)) { + if (chdir(startdir) == -1) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&chdir %s", startdir); return 0; @@ -2292,48 +2355,71 @@ set_basedir(const char *targetdir, int report_baddir) * pathname and append a trailing slash, such that * we can reliably check whether files are inside. */ - if (NULL == realpath(targetdir, basedir)) { + if (realpath(targetdir, basedir) == NULL) { if (report_baddir || errno != ENOENT) { exitcode = (int)MANDOCLEVEL_BADARG; say("", "&%s: realpath", targetdir); } + *basedir = '\0'; return 0; - } else if (-1 == chdir(basedir)) { + } else if (chdir(basedir) == -1) { if (report_baddir || errno != ENOENT) { exitcode = (int)MANDOCLEVEL_BADARG; say("", "&chdir"); } + *basedir = '\0'; return 0; } chdir_status = 1; - cp = strchr(basedir, '\0'); - if ('/' != cp[-1]) { - if (cp - basedir >= PATH_MAX - 1) { + basedir_len = strlen(basedir); + if (basedir[basedir_len - 1] != '/') { + if (basedir_len >= PATH_MAX - 1) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "Filename too long"); + *basedir = '\0'; + basedir_len = 0; return 0; } - *cp++ = '/'; - *cp = '\0'; + basedir[basedir_len++] = '/'; + basedir[basedir_len] = '\0'; } return 1; } +#ifdef READ_ALLOWED_PATH +static int +read_allowed(const char *candidate) +{ + const char *cp; + size_t len; + + for (cp = READ_ALLOWED_PATH;; cp += len) { + while (*cp == ':') + cp++; + if (*cp == '\0') + return 0; + len = strcspn(cp, ":"); + if (strncmp(candidate, cp, len) == 0) + return 1; + } +} +#endif + static void say(const char *file, const char *format, ...) { va_list ap; int use_errno; - if ('\0' != *basedir) + if (*basedir != '\0') fprintf(stderr, "%s", basedir); - if ('\0' != *basedir && '\0' != *file) + if (*basedir != '\0' && *file != '\0') fputc('/', stderr); - if ('\0' != *file) + if (*file != '\0') fprintf(stderr, "%s", file); use_errno = 1; - if (NULL != format) { + if (format != NULL) { switch (*format) { case '&': format++; @@ -2346,15 +2432,15 @@ say(const char *file, const char *format, ...) break; } } - if (NULL != format) { - if ('\0' != *basedir || '\0' != *file) + if (format != NULL) { + if (*basedir != '\0' || *file != '\0') fputs(": ", stderr); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); } if (use_errno) { - if ('\0' != *basedir || '\0' != *file || NULL != format) + if (*basedir != '\0' || *file != '\0' || format != NULL) fputs(": ", stderr); perror(NULL); } else diff --git a/usr/src/cmd/mandoc/manpath.c b/usr/src/cmd/mandoc/manpath.c index 74f38a95db..255d748246 100644 --- a/usr/src/cmd/mandoc/manpath.c +++ b/usr/src/cmd/mandoc/manpath.c @@ -1,6 +1,6 @@ -/* $Id: manpath.c,v 1.37 2018/11/22 11:30:23 schwarze Exp $ */ +/* $Id: manpath.c,v 1.43 2020/08/27 14:59:47 schwarze Exp $ */ /* - * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any @@ -21,20 +21,19 @@ #include <sys/stat.h> #include <ctype.h> -#if HAVE_ERR -#include <err.h> -#endif +#include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "mandoc_aux.h" +#include "mandoc.h" #include "manconf.h" static void manconf_file(struct manconf *, const char *); -static void manpath_add(struct manpaths *, const char *, int); -static void manpath_parseline(struct manpaths *, char *, int); +static void manpath_add(struct manpaths *, const char *, char); +static void manpath_parseline(struct manpaths *, char *, char); void @@ -44,11 +43,11 @@ manconf_parse(struct manconf *conf, const char *file, char *insert; /* Always prepend -m. */ - manpath_parseline(&conf->manpath, auxp, 1); + manpath_parseline(&conf->manpath, auxp, 'm'); /* If -M is given, it overrides everything else. */ if (NULL != defp) { - manpath_parseline(&conf->manpath, defp, 1); + manpath_parseline(&conf->manpath, defp, 'M'); return; } @@ -66,13 +65,13 @@ manconf_parse(struct manconf *conf, const char *file, /* Prepend man.conf(5) to MANPATH. */ if (':' == defp[0]) { manconf_file(conf, file); - manpath_parseline(&conf->manpath, defp, 0); + manpath_parseline(&conf->manpath, defp, '\0'); return; } /* Append man.conf(5) to MANPATH. */ if (':' == defp[strlen(defp) - 1]) { - manpath_parseline(&conf->manpath, defp, 0); + manpath_parseline(&conf->manpath, defp, '\0'); manconf_file(conf, file); return; } @@ -81,28 +80,28 @@ manconf_parse(struct manconf *conf, const char *file, insert = strstr(defp, "::"); if (NULL != insert) { *insert++ = '\0'; - manpath_parseline(&conf->manpath, defp, 0); + manpath_parseline(&conf->manpath, defp, '\0'); manconf_file(conf, file); - manpath_parseline(&conf->manpath, insert + 1, 0); + manpath_parseline(&conf->manpath, insert + 1, '\0'); return; } /* MANPATH overrides man.conf(5) completely. */ - manpath_parseline(&conf->manpath, defp, 0); + manpath_parseline(&conf->manpath, defp, '\0'); } void manpath_base(struct manpaths *dirs) { char path_base[] = MANPATH_BASE; - manpath_parseline(dirs, path_base, 0); + manpath_parseline(dirs, path_base, '\0'); } /* * Parse a FULL pathname from a colon-separated list of arrays. */ static void -manpath_parseline(struct manpaths *dirs, char *path, int complain) +manpath_parseline(struct manpaths *dirs, char *path, char option) { char *dir; @@ -110,7 +109,7 @@ manpath_parseline(struct manpaths *dirs, char *path, int complain) return; for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) - manpath_add(dirs, dir, complain); + manpath_add(dirs, dir, option); } /* @@ -118,33 +117,32 @@ manpath_parseline(struct manpaths *dirs, char *path, int complain) * Grow the array one-by-one for simplicity's sake. */ static void -manpath_add(struct manpaths *dirs, const char *dir, int complain) +manpath_add(struct manpaths *dirs, const char *dir, char option) { char buf[PATH_MAX]; struct stat sb; char *cp; size_t i; - if (NULL == (cp = realpath(dir, buf))) { - if (complain) - warn("manpath: %s", dir); - return; - } + if ((cp = realpath(dir, buf)) == NULL) + goto fail; for (i = 0; i < dirs->sz; i++) - if (0 == strcmp(dirs->paths[i], dir)) + if (strcmp(dirs->paths[i], dir) == 0) return; - if (stat(cp, &sb) == -1) { - if (complain) - warn("manpath: %s", dir); - return; - } + if (stat(cp, &sb) == -1) + goto fail; dirs->paths = mandoc_reallocarray(dirs->paths, - dirs->sz + 1, sizeof(char *)); - + dirs->sz + 1, sizeof(*dirs->paths)); dirs->paths[dirs->sz++] = mandoc_strdup(cp); + return; + +fail: + if (option != '\0') + mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, + "-%c %s: %s", option, dir, strerror(errno)); } void @@ -165,7 +163,7 @@ manconf_free(struct manconf *conf) static void manconf_file(struct manconf *conf, const char *file) { - const char *const toks[] = { "manpath", "output", "_whatdb" }; + const char *const toks[] = { "manpath", "output" }; char manpath_default[] = MANPATH_DEFAULT; FILE *stream; @@ -202,15 +200,8 @@ manconf_file(struct manconf *conf, const char *file) } switch (tok) { - case 2: /* _whatdb */ - while (ep > cp && ep[-1] != '/') - ep--; - if (ep == cp) - continue; - *ep = '\0'; - /* FALLTHROUGH */ case 0: /* manpath */ - manpath_add(&conf->manpath, cp, 0); + manpath_add(&conf->manpath, cp, '\0'); *manpath_default = '\0'; break; case 1: /* output */ @@ -225,24 +216,30 @@ manconf_file(struct manconf *conf, const char *file) out: if (*manpath_default != '\0') - manpath_parseline(&conf->manpath, manpath_default, 0); + manpath_parseline(&conf->manpath, manpath_default, '\0'); } int manconf_output(struct manoutput *conf, const char *cp, int fromfile) { const char *const toks[] = { + /* Tokens requiring an argument. */ "includes", "man", "paper", "style", "indent", "width", - "tag", "fragment", "mdoc", "noval", "toc" + "outfilename", "tagfilename", + /* Token taking an optional argument. */ + "tag", + /* Tokens not taking arguments. */ + "fragment", "mdoc", "noval", "toc" }; + const size_t ntoks = sizeof(toks) / sizeof(toks[0]); const char *errstr; char *oldval; size_t len, tok; - for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { + for (tok = 0; tok < ntoks; tok++) { len = strlen(toks[tok]); - if ( ! strncmp(cp, toks[tok], len) && + if (strncmp(cp, toks[tok], len) == 0 && strchr(" = ", cp[len]) != NULL) { cp += len; if (*cp == '=') @@ -253,12 +250,12 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile) } } - if (tok < 6 && *cp == '\0') { - warnx("-O %s=?: Missing argument value", toks[tok]); + if (tok < 8 && *cp == '\0') { + mandoc_msg(MANDOCERR_BADVAL_MISS, 0, 0, "-O %s=?", toks[tok]); return -1; } - if (tok > 6 && *cp != '\0') { - warnx("-O %s: Does not take a value: %s", toks[tok], cp); + if (tok > 8 && tok < ntoks && *cp != '\0') { + mandoc_msg(MANDOCERR_BADVAL, 0, 0, "-O %s=%s", toks[tok], cp); return -1; } @@ -299,7 +296,8 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile) conf->indent = strtonum(cp, 0, 1000, &errstr); if (errstr == NULL) return 0; - warnx("-O indent=%s is %s", cp, errstr); + mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0, + "-O indent=%s is %s", cp, errstr); return -1; case 5: if (conf->width) { @@ -309,35 +307,57 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile) conf->width = strtonum(cp, 1, 1000, &errstr); if (errstr == NULL) return 0; - warnx("-O width=%s is %s", cp, errstr); + mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0, + "-O width=%s is %s", cp, errstr); return -1; case 6: + if (conf->outfilename != NULL) { + oldval = mandoc_strdup(conf->outfilename); + break; + } + conf->outfilename = mandoc_strdup(cp); + return 0; + case 7: + if (conf->tagfilename != NULL) { + oldval = mandoc_strdup(conf->tagfilename); + break; + } + conf->tagfilename = mandoc_strdup(cp); + return 0; + /* + * If the index of the following token changes, + * do not forget to adjust the range check above the switch. + */ + case 8: if (conf->tag != NULL) { oldval = mandoc_strdup(conf->tag); break; } conf->tag = mandoc_strdup(cp); return 0; - case 7: + case 9: conf->fragment = 1; return 0; - case 8: + case 10: conf->mdoc = 1; return 0; - case 9: + case 11: conf->noval = 1; return 0; - case 10: + case 12: conf->toc = 1; return 0; default: - if (fromfile) - warnx("-O %s: Bad argument", cp); + mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-O %s", cp); + return -1; + } + if (fromfile) { + free(oldval); + return 0; + } else { + mandoc_msg(MANDOCERR_BADVAL_DUPE, 0, 0, + "-O %s=%s: already set to %s", toks[tok], cp, oldval); + free(oldval); return -1; } - if (fromfile == 0) - warnx("-O %s=%s: Option already set to %s", - toks[tok], cp, oldval); - free(oldval); - return -1; } diff --git a/usr/src/cmd/mandoc/mansearch.c b/usr/src/cmd/mandoc/mansearch.c index 4e968332e3..59a3577197 100644 --- a/usr/src/cmd/mandoc/mansearch.c +++ b/usr/src/cmd/mandoc/mansearch.c @@ -1,4 +1,4 @@ -/* $Id: mansearch.c,v 1.80 2018/12/13 11:55:46 schwarze Exp $ */ +/* $Id: mansearch.c,v 1.82 2019/07/01 22:56:24 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2013-2018 Ingo Schwarze <schwarze@openbsd.org> @@ -199,6 +199,7 @@ mansearch(const struct mansearch *search, } mpage->names = buildnames(page); mpage->output = buildoutput(outkey, page); + mpage->bits = search->firstmatch ? rp->bits : 0; mpage->ipath = i; mpage->sec = *page->sect - '0'; if (mpage->sec < 0 || mpage->sec > 9) @@ -294,8 +295,10 @@ 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) + if ((rp = ohash_find(htab, slot)) != NULL) { + rp->bits |= res.bits; continue; + } rp = mandoc_malloc(sizeof(*rp)); *rp = res; ohash_insert(htab, slot, rp); @@ -408,7 +411,8 @@ manpage_compare(const void *vp1, const void *vp2) mp1 = vp1; mp2 = vp2; - if ((diff = mp1->sec - mp2->sec)) + if ((diff = mp2->bits - mp1->bits) || + (diff = mp1->sec - mp2->sec)) return diff; /* Fall back to alphabetic ordering of names. */ diff --git a/usr/src/cmd/mandoc/mansearch.h b/usr/src/cmd/mandoc/mansearch.h index 355873f83e..8a0aa121d0 100644 --- a/usr/src/cmd/mandoc/mansearch.h +++ b/usr/src/cmd/mandoc/mansearch.h @@ -1,4 +1,4 @@ -/* $Id: mansearch.h,v 1.29 2018/11/22 12:01:46 schwarze Exp $ */ +/* $Id: mansearch.h,v 1.30 2019/04/30 18:51:57 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2013, 2014, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -92,6 +92,7 @@ struct manpage { char *file; /* to be prefixed by manpath */ char *names; /* a list of names with sections */ char *output; /* user-defined additional output */ + uint64_t bits; /* name type mask */ size_t ipath; /* number of the manpath */ 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 bb3ec58bd0..475a6aa0dd 100644 --- a/usr/src/cmd/mandoc/mdoc.c +++ b/usr/src/cmd/mandoc/mdoc.c @@ -1,7 +1,7 @@ -/* $Id: mdoc.c,v 1.274 2018/12/31 07:46:07 schwarze Exp $ */ +/* $Id: mdoc.c,v 1.275 2020/04/06 10:16:17 schwarze Exp $ */ /* + * Copyright (c) 2010, 2012-2018, 2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -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. + * + * Top level and utility functions of the mdoc(7) parser for mandoc(1). */ #include "config.h" @@ -352,12 +354,13 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); /* - * If an initial macro or a list invocation, divert directly - * into macro processing. + * If an initial or transparent macro or a list invocation, + * divert directly into macro processing. */ n = mdoc->last; - if (n == NULL || tok == MDOC_It || tok == MDOC_El) { + if (n == NULL || tok == MDOC_It || tok == MDOC_El || + roff_tok_transparent(tok)) { (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); return 1; } diff --git a/usr/src/cmd/mandoc/mdoc_argv.c b/usr/src/cmd/mandoc/mdoc_argv.c index f4ce4a8730..1bfd336b7a 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.119 2018/12/21 17:15:19 schwarze Exp $ */ +/* $Id: mdoc_argv.c,v 1.120 2019/07/11 17:06:17 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012, 2014-2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012, 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 @@ -454,6 +454,7 @@ args(struct roff_man *mdoc, int line, int *pos, mandoc_msg(MANDOCERR_ARG_QUOTE, line, *pos, NULL); mdoc->flags &= ~MDOC_PHRASELIT; } + mdoc->flags &= ~MDOC_PHRASEQL; return ARGS_EOLN; } diff --git a/usr/src/cmd/mandoc/mdoc_html.c b/usr/src/cmd/mandoc/mdoc_html.c index 87bf42a72b..c0a0a6a569 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.328 2019/03/01 10:57:18 schwarze Exp $ */ +/* $Id: mdoc_html.c,v 1.342 2021/03/30 19:26:20 schwarze Exp $ */ /* + * Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * HTML formatter for mdoc(7) used by mandoc(1). */ #include "config.h" @@ -47,13 +49,11 @@ struct mdoc_html_act { void (*post)(MDOC_ARGS); }; -static char *cond_id(const struct roff_node *); static void print_mdoc_head(const struct roff_meta *, struct html *); static void print_mdoc_node(MDOC_ARGS); static void print_mdoc_nodelist(MDOC_ARGS); -static void synopsis_pre(struct html *, - const struct roff_node *); +static void synopsis_pre(struct html *, struct roff_node *); static void mdoc_root_post(const struct roff_meta *, struct html *); @@ -73,9 +73,8 @@ static void mdoc_bk_post(MDOC_ARGS); static int mdoc_bk_pre(MDOC_ARGS); static int mdoc_bl_pre(MDOC_ARGS); static int mdoc_cd_pre(MDOC_ARGS); -static int mdoc_cm_pre(MDOC_ARGS); +static int mdoc_code_pre(MDOC_ARGS); static int mdoc_d1_pre(MDOC_ARGS); -static int mdoc_dv_pre(MDOC_ARGS); static int mdoc_fa_pre(MDOC_ARGS); static int mdoc_fd_pre(MDOC_ARGS); static int mdoc_fl_pre(MDOC_ARGS); @@ -84,20 +83,15 @@ static int mdoc_ft_pre(MDOC_ARGS); static int mdoc_em_pre(MDOC_ARGS); static void mdoc_eo_post(MDOC_ARGS); static int mdoc_eo_pre(MDOC_ARGS); -static int mdoc_er_pre(MDOC_ARGS); -static int mdoc_ev_pre(MDOC_ARGS); static int mdoc_ex_pre(MDOC_ARGS); static void mdoc_fo_post(MDOC_ARGS); static int mdoc_fo_pre(MDOC_ARGS); -static int mdoc_ic_pre(MDOC_ARGS); static int mdoc_igndelim_pre(MDOC_ARGS); static int mdoc_in_pre(MDOC_ARGS); static int mdoc_it_pre(MDOC_ARGS); static int mdoc_lb_pre(MDOC_ARGS); -static int mdoc_li_pre(MDOC_ARGS); static int mdoc_lk_pre(MDOC_ARGS); static int mdoc_mt_pre(MDOC_ARGS); -static int mdoc_ms_pre(MDOC_ARGS); static int mdoc_nd_pre(MDOC_ARGS); static int mdoc_nm_pre(MDOC_ARGS); static int mdoc_no_pre(MDOC_ARGS); @@ -115,6 +109,7 @@ static int mdoc_ss_pre(MDOC_ARGS); static int mdoc_st_pre(MDOC_ARGS); static int mdoc_sx_pre(MDOC_ARGS); static int mdoc_sy_pre(MDOC_ARGS); +static int mdoc_tg_pre(MDOC_ARGS); static int mdoc_va_pre(MDOC_ARGS); static int mdoc_vt_pre(MDOC_ARGS); static int mdoc_xr_pre(MDOC_ARGS); @@ -139,19 +134,19 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { {mdoc_ap_pre, NULL}, /* Ap */ {mdoc_ar_pre, NULL}, /* Ar */ {mdoc_cd_pre, NULL}, /* Cd */ - {mdoc_cm_pre, NULL}, /* Cm */ - {mdoc_dv_pre, NULL}, /* Dv */ - {mdoc_er_pre, NULL}, /* Er */ - {mdoc_ev_pre, NULL}, /* Ev */ + {mdoc_code_pre, NULL}, /* Cm */ + {mdoc_code_pre, NULL}, /* Dv */ + {mdoc_code_pre, NULL}, /* Er */ + {mdoc_code_pre, NULL}, /* Ev */ {mdoc_ex_pre, NULL}, /* Ex */ {mdoc_fa_pre, NULL}, /* Fa */ {mdoc_fd_pre, NULL}, /* Fd */ {mdoc_fl_pre, NULL}, /* Fl */ {mdoc_fn_pre, NULL}, /* Fn */ {mdoc_ft_pre, NULL}, /* Ft */ - {mdoc_ic_pre, NULL}, /* Ic */ + {mdoc_code_pre, NULL}, /* Ic */ {mdoc_in_pre, NULL}, /* In */ - {mdoc_li_pre, NULL}, /* Li */ + {mdoc_code_pre, NULL}, /* Li */ {mdoc_nd_pre, NULL}, /* Nd */ {mdoc_nm_pre, NULL}, /* Nm */ {mdoc_quote_pre, mdoc_quote_post}, /* Op */ @@ -192,7 +187,7 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { {mdoc_em_pre, NULL}, /* Em */ {mdoc_eo_pre, mdoc_eo_post}, /* Eo */ {mdoc_xx_pre, NULL}, /* Fx */ - {mdoc_ms_pre, NULL}, /* Ms */ + {mdoc_no_pre, NULL}, /* Ms */ {mdoc_no_pre, NULL}, /* No */ {mdoc_ns_pre, NULL}, /* Ns */ {mdoc_xx_pre, NULL}, /* Nx */ @@ -241,6 +236,7 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { {mdoc__x_pre, mdoc__x_post}, /* %Q */ {mdoc__x_pre, mdoc__x_post}, /* %U */ {NULL, NULL}, /* Ta */ + {mdoc_tg_pre, NULL}, /* Tg */ }; @@ -248,13 +244,15 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { * See the same function in mdoc_term.c for documentation. */ static void -synopsis_pre(struct html *h, const struct roff_node *n) +synopsis_pre(struct html *h, struct roff_node *n) { + struct roff_node *np; - if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags)) + if ((n->flags & NODE_SYNPRETTY) == 0 || + (np = roff_node_prev(n)) == NULL) return; - if (n->prev->tok == n->tok && + if (np->tok == n->tok && MDOC_Fo != n->tok && MDOC_Ft != n->tok && MDOC_Fn != n->tok) { @@ -262,7 +260,7 @@ synopsis_pre(struct html *h, const struct roff_node *n) return; } - switch (n->prev->tok) { + switch (np->tok) { case MDOC_Fd: case MDOC_Fn: case MDOC_Fo: @@ -351,30 +349,40 @@ print_mdoc_node(MDOC_ARGS) if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) return; - html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi); + if ((n->flags & NODE_NOFILL) == 0) + html_fillmode(h, ROFF_fi); + else if (html_fillmode(h, ROFF_nf) == ROFF_nf && + n->tok != ROFF_fi && n->flags & NODE_LINE) + print_endline(h); child = 1; n->flags &= ~NODE_ENDED; switch (n->type) { case ROFFT_TEXT: + if (n->flags & NODE_LINE) { + switch (*n->string) { + case '\0': + h->col = 1; + print_endline(h); + return; + case ' ': + if ((h->flags & HTML_NONEWLINE) == 0 && + (n->flags & NODE_NOFILL) == 0) + print_otag(h, TAG_BR, ""); + break; + default: + break; + } + } t = h->tag; t->refcnt++; - - /* No tables in this mode... */ - assert(NULL == h->tblt); - - /* - * Make sure that if we're in a literal mode already - * (i.e., within a <PRE>) don't print the newline. - */ - if (*n->string == ' ' && n->flags & NODE_LINE && - (h->flags & HTML_NONEWLINE) == 0 && - (n->flags & NODE_NOFILL) == 0) - print_otag(h, TAG_BR, ""); - if (NODE_DELIMC & n->flags) + if (n->flags & NODE_DELIMC) h->flags |= HTML_NOSPACE; - print_text(h, n->string); - if (NODE_DELIMO & n->flags) + if (n->flags & NODE_HREF) + print_tagged_text(h, n->string, n); + else + print_text(h, n->string); + if (n->flags & NODE_DELIMO) h->flags |= HTML_NOSPACE; break; case ROFFT_EQN: @@ -439,12 +447,6 @@ print_mdoc_node(MDOC_ARGS) 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 @@ -502,20 +504,11 @@ mdoc_root_pre(const struct roff_meta *meta, struct html *h) return 1; } -static char * -cond_id(const struct roff_node *n) +static int +mdoc_code_pre(MDOC_ARGS) { - if (n->child != NULL && - n->child->type == ROFFT_TEXT && - (n->prev == NULL || - (n->prev->type == ROFFT_TEXT && - strcmp(n->prev->string, "|") == 0)) && - (n->parent->tok == MDOC_It || - (n->parent->tok == MDOC_Xo && - n->parent->parent->prev == NULL && - n->parent->parent->parent->tok == MDOC_It))) - return html_make_id(n, 1); - return NULL; + print_otag_id(h, TAG_CODE, roff_name[n->tok], n); + return 1; } static int @@ -578,10 +571,7 @@ mdoc_sh_pre(MDOC_ARGS) print_otag(h, TAG_SECTION, "c", "Sh"); break; case ROFFT_HEAD: - id = html_make_id(n, 1); - print_otag(h, TAG_H1, "ci", "Sh", id); - if (id != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); + print_otag_id(h, TAG_H1, "Sh", n); break; case ROFFT_BODY: if (n->sec == SEC_AUTHORS) @@ -596,64 +586,43 @@ mdoc_sh_pre(MDOC_ARGS) static int mdoc_ss_pre(MDOC_ARGS) { - char *id; - switch (n->type) { case ROFFT_BLOCK: html_close_paragraph(h); print_otag(h, TAG_SECTION, "c", "Ss"); - return 1; + break; case ROFFT_HEAD: + print_otag_id(h, TAG_H2, "Ss", n); break; case ROFFT_BODY: - return 1; + break; default: abort(); } - - id = html_make_id(n, 1); - print_otag(h, TAG_H2, "ci", "Ss", id); - if (id != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); return 1; } static int mdoc_fl_pre(MDOC_ARGS) { - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Fl", id); + struct roff_node *nn; + print_otag_id(h, TAG_CODE, "Fl", n); print_text(h, "\\-"); - if (!(n->child == NULL && - (n->next == NULL || - n->next->type == ROFFT_TEXT || - n->next->flags & NODE_LINE))) + if (n->child != NULL || + ((nn = roff_node_next(n)) != NULL && + nn->type != ROFFT_TEXT && + (nn->flags & NODE_LINE) == 0)) h->flags |= HTML_NOSPACE; return 1; } static int -mdoc_cm_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Cm", id); - return 1; -} - -static int mdoc_nd_pre(MDOC_ARGS) { switch (n->type) { case ROFFT_BLOCK: - html_close_paragraph(h); return 1; case ROFFT_HEAD: return 0; @@ -663,8 +632,7 @@ mdoc_nd_pre(MDOC_ARGS) abort(); } print_text(h, "\\(em"); - /* Cannot use TAG_SPAN because it may contain blocks. */ - print_otag(h, TAG_DIV, "c", "Nd"); + print_otag(h, TAG_SPAN, "c", "Nd"); return 1; } @@ -722,6 +690,18 @@ mdoc_xr_pre(MDOC_ARGS) } static int +mdoc_tg_pre(MDOC_ARGS) +{ + char *id; + + if ((id = html_make_id(n, 1)) != NULL) { + print_tagq(h, print_otag(h, TAG_MARK, "i", id)); + free(id); + } + return 0; +} + +static int mdoc_ns_pre(MDOC_ARGS) { @@ -765,7 +745,7 @@ mdoc_it_pre(MDOC_ARGS) case ROFFT_HEAD: return 0; case ROFFT_BODY: - print_otag(h, TAG_LI, ""); + print_otag_id(h, TAG_LI, NULL, n); break; default: break; @@ -777,7 +757,7 @@ mdoc_it_pre(MDOC_ARGS) case LIST_ohang: switch (n->type) { case ROFFT_HEAD: - print_otag(h, TAG_DT, ""); + print_otag_id(h, TAG_DT, NULL, n); break; case ROFFT_BODY: print_otag(h, TAG_DD, ""); @@ -789,7 +769,7 @@ mdoc_it_pre(MDOC_ARGS) case LIST_tag: switch (n->type) { case ROFFT_HEAD: - print_otag(h, TAG_DT, ""); + print_otag_id(h, TAG_DT, NULL, n); break; case ROFFT_BODY: if (n->child == NULL) { @@ -810,7 +790,7 @@ mdoc_it_pre(MDOC_ARGS) print_otag(h, TAG_TD, ""); break; default: - print_otag(h, TAG_TR, ""); + print_otag_id(h, TAG_TR, NULL, n); } default: break; @@ -876,8 +856,8 @@ mdoc_bl_pre(MDOC_ARGS) case LIST_tag: if (bl->offs) print_otag(h, TAG_DIV, "c", "Bd-indent"); - print_otag(h, TAG_DL, "c", bl->comp ? - "Bl-tag Bl-compact" : "Bl-tag"); + print_otag_id(h, TAG_DL, + bl->comp ? "Bl-tag Bl-compact" : "Bl-tag", n->body); return 1; case LIST_column: elemtype = TAG_TABLE; @@ -890,14 +870,14 @@ mdoc_bl_pre(MDOC_ARGS) (void)strlcat(cattr, " Bd-indent", sizeof(cattr)); if (bl->comp) (void)strlcat(cattr, " Bl-compact", sizeof(cattr)); - print_otag(h, elemtype, "c", cattr); + print_otag_id(h, elemtype, cattr, n->body); return 1; } static int mdoc_ex_pre(MDOC_ARGS) { - if (n->prev) + if (roff_node_prev(n) != NULL) print_otag(h, TAG_BR, ""); return 1; } @@ -912,7 +892,7 @@ mdoc_st_pre(MDOC_ARGS) static int mdoc_em_pre(MDOC_ARGS) { - print_otag(h, TAG_I, "c", "Em"); + print_otag_id(h, TAG_I, "Em", n); return 1; } @@ -922,15 +902,15 @@ mdoc_d1_pre(MDOC_ARGS) switch (n->type) { case ROFFT_BLOCK: html_close_paragraph(h); - break; + return 1; case ROFFT_HEAD: return 0; case ROFFT_BODY: - return 1; + break; default: abort(); } - print_otag(h, TAG_DIV, "c", "Bd Bd-indent"); + print_otag_id(h, TAG_DIV, "Bd Bd-indent", n); if (n->tok == MDOC_Dl) print_otag(h, TAG_CODE, "c", "Li"); return 1; @@ -950,7 +930,7 @@ mdoc_sx_pre(MDOC_ARGS) static int mdoc_bd_pre(MDOC_ARGS) { - char buf[16]; + char buf[20]; struct roff_node *nn; int comp; @@ -974,7 +954,7 @@ mdoc_bd_pre(MDOC_ARGS) continue; if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss) comp = 1; - if (nn->prev != NULL) + if (roff_node_prev(nn) != NULL) break; } (void)strlcpy(buf, "Bd", sizeof(buf)); @@ -987,7 +967,10 @@ mdoc_bd_pre(MDOC_ARGS) strcmp(n->norm->Bd.offs, "left") != 0) (void)strlcat(buf, " Bd-indent", sizeof(buf)); - print_otag(h, TAG_DIV, "c", buf); + if (n->norm->Bd.type == DISP_literal) + (void)strlcat(buf, " Li", sizeof(buf)); + + print_otag_id(h, TAG_DIV, buf, n); return 1; } @@ -1038,45 +1021,6 @@ mdoc_cd_pre(MDOC_ARGS) } static int -mdoc_dv_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Dv", id); - return 1; -} - -static int -mdoc_ev_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Ev", id); - return 1; -} - -static int -mdoc_er_pre(MDOC_ARGS) -{ - char *id; - - id = n->sec == SEC_ERRORS && - (n->parent->tok == MDOC_It || - (n->parent->tok == MDOC_Bq && - n->parent->parent->parent->tok == MDOC_It)) ? - html_make_id(n, 1) : NULL; - - if (id != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Er", id); - return 1; -} - -static int mdoc_fa_pre(MDOC_ARGS) { const struct roff_node *nn; @@ -1086,22 +1030,21 @@ mdoc_fa_pre(MDOC_ARGS) print_otag(h, TAG_VAR, "c", "Fa"); return 1; } - - for (nn = n->child; nn; nn = nn->next) { + for (nn = n->child; nn != NULL; nn = nn->next) { t = print_otag(h, TAG_VAR, "c", "Fa"); print_text(h, nn->string); print_tagq(h, t); - if (nn->next) { + if (nn->next != NULL) { h->flags |= HTML_NOSPACE; print_text(h, ","); } } - - if (n->child && n->next && n->next->tok == MDOC_Fa) { + if (n->child != NULL && + (nn = roff_node_next(n)) != NULL && + nn->tok == MDOC_Fa) { h->flags |= HTML_NOSPACE; print_text(h, ","); } - return 0; } @@ -1209,7 +1152,7 @@ mdoc_fn_pre(MDOC_ARGS) print_tagq(h, t); } - t = print_otag(h, TAG_CODE, "c", "Fn"); + t = print_otag_id(h, TAG_CODE, "Fn", n); if (sp) print_text(h, sp); @@ -1272,9 +1215,21 @@ mdoc_skip_pre(MDOC_ARGS) static int mdoc_pp_pre(MDOC_ARGS) { - if ((n->flags & NODE_NOFILL) == 0) { + char *id; + + if (n->flags & NODE_NOFILL) { + print_endline(h); + if (n->flags & NODE_ID) + mdoc_tg_pre(meta, n, h); + else { + h->col = 1; + print_endline(h); + } + } else { html_close_paragraph(h); - print_otag(h, TAG_P, "c", "Pp"); + id = n->flags & NODE_ID ? html_make_id(n, 1) : NULL; + print_otag(h, TAG_P, "ci", "Pp", id); + free(id); } return 0; } @@ -1324,14 +1279,12 @@ mdoc_mt_pre(MDOC_ARGS) for (n = n->child; n; n = n->next) { assert(n->type == ROFFT_TEXT); - mandoc_asprintf(&cp, "mailto:%s", n->string); t = print_otag(h, TAG_A, "ch", "Mt", cp); print_text(h, n->string); print_tagq(h, t); free(cp); } - return 0; } @@ -1340,30 +1293,30 @@ mdoc_fo_pre(MDOC_ARGS) { struct tag *t; - if (n->type == ROFFT_BODY) { + switch (n->type) { + case ROFFT_BLOCK: + synopsis_pre(h, n); + return 1; + case ROFFT_HEAD: + if (n->child != NULL) { + t = print_otag_id(h, TAG_CODE, "Fn", n); + print_text(h, n->child->string); + print_tagq(h, t); + } + return 0; + case ROFFT_BODY: h->flags |= HTML_NOSPACE; print_text(h, "("); h->flags |= HTML_NOSPACE; return 1; - } else if (n->type == ROFFT_BLOCK) { - synopsis_pre(h, n); - return 1; + default: + abort(); } - - if (n->child == NULL) - return 0; - - assert(n->child->string); - t = print_otag(h, TAG_CODE, "c", "Fn"); - print_text(h, n->child->string); - print_tagq(h, t); - return 0; } static void mdoc_fo_post(MDOC_ARGS) { - if (n->type != ROFFT_BODY) return; h->flags |= HTML_NOSPACE; @@ -1413,22 +1366,10 @@ mdoc_in_pre(MDOC_ARGS) assert(n->type == ROFFT_TEXT); print_text(h, n->string); } - return 0; } static int -mdoc_ic_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Ic", id); - return 1; -} - -static int mdoc_va_pre(MDOC_ARGS) { print_otag(h, TAG_VAR, "c", "Va"); @@ -1438,7 +1379,6 @@ mdoc_va_pre(MDOC_ARGS) static int mdoc_ap_pre(MDOC_ARGS) { - h->flags |= HTML_NOSPACE; print_text(h, "\\(aq"); h->flags |= HTML_NOSPACE; @@ -1477,20 +1417,8 @@ mdoc_bf_pre(MDOC_ARGS) } static int -mdoc_ms_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_SPAN, "ci", "Ms", id); - return 1; -} - -static int mdoc_igndelim_pre(MDOC_ARGS) { - h->flags |= HTML_IGNDELIM; return 1; } @@ -1498,7 +1426,6 @@ mdoc_igndelim_pre(MDOC_ARGS) static void mdoc_pf_post(MDOC_ARGS) { - if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) h->flags |= HTML_NOSPACE; } @@ -1527,36 +1454,23 @@ mdoc_rs_pre(MDOC_ARGS) static int mdoc_no_pre(MDOC_ARGS) { - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_SPAN, "ci", "No", id); - return 1; -} - -static int -mdoc_li_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Li", id); + print_otag_id(h, TAG_SPAN, roff_name[n->tok], n); return 1; } static int mdoc_sy_pre(MDOC_ARGS) { - print_otag(h, TAG_B, "c", "Sy"); + print_otag_id(h, TAG_B, "Sy", n); return 1; } static int mdoc_lb_pre(MDOC_ARGS) { - if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev) + if (n->sec == SEC_LIBRARY && + n->flags & NODE_LINE && + roff_node_prev(n) != NULL) print_otag(h, TAG_BR, ""); print_otag(h, TAG_SPAN, "c", "Lb"); @@ -1566,17 +1480,18 @@ mdoc_lb_pre(MDOC_ARGS) static int mdoc__x_pre(MDOC_ARGS) { - const char *cattr; - enum htmltag t; + struct roff_node *nn; + const char *cattr; + enum htmltag t; t = TAG_SPAN; switch (n->tok) { case MDOC__A: cattr = "RsA"; - if (n->prev && MDOC__A == n->prev->tok) - if (NULL == n->next || MDOC__A != n->next->tok) - print_text(h, "and"); + if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A && + ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A)) + print_text(h, "and"); break; case MDOC__B: t = TAG_I; @@ -1631,19 +1546,21 @@ mdoc__x_pre(MDOC_ARGS) static void mdoc__x_post(MDOC_ARGS) { + struct roff_node *nn; - if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) - if (NULL == n->next->next || MDOC__A != n->next->next->tok) - if (NULL == n->prev || MDOC__A != n->prev->tok) - return; + if (n->tok == MDOC__A && + (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A && + ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) && + ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A)) + return; /* TODO: %U */ - if (NULL == n->parent || MDOC_Rs != n->parent->tok) + if (n->parent == NULL || n->parent->tok != MDOC_Rs) return; h->flags |= HTML_NOSPACE; - print_text(h, n->next ? "," : "."); + print_text(h, roff_node_next(n) ? "," : "."); } static int @@ -1700,7 +1617,7 @@ mdoc_quote_pre(MDOC_ARGS) /* * 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 + * We cannot use TAG_DIV 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. @@ -1715,9 +1632,11 @@ mdoc_quote_pre(MDOC_ARGS) break; case MDOC_Do: case MDOC_Dq: + print_text(h, "\\(lq"); + break; case MDOC_Qo: case MDOC_Qq: - print_text(h, "\\(lq"); + print_text(h, "\""); break; case MDOC_Po: case MDOC_Pq: @@ -1773,12 +1692,14 @@ mdoc_quote_post(MDOC_ARGS) else print_text(h, n->norm->Es->child->next->string); break; - case MDOC_Qo: - case MDOC_Qq: case MDOC_Do: case MDOC_Dq: print_text(h, "\\(rq"); break; + case MDOC_Qo: + case MDOC_Qq: + print_text(h, "\""); + break; case MDOC_Po: case MDOC_Pq: print_text(h, ")"); diff --git a/usr/src/cmd/mandoc/mdoc_macro.c b/usr/src/cmd/mandoc/mdoc_macro.c index 3422945814..dd3885c702 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.232 2019/01/07 07:26:29 schwarze Exp $ */ +/* $Id: mdoc_macro.c,v 1.234 2020/01/19 18:02:00 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -61,7 +61,7 @@ static void rew_pending(struct roff_man *, const struct roff_node *); static const struct mdoc_macro mdoc_macros[MDOC_MAX - MDOC_Dd] = { - { in_line_eoln, MDOC_PROLOGUE }, /* Dd */ + { in_line_eoln, MDOC_PROLOGUE | MDOC_JOIN }, /* Dd */ { in_line_eoln, MDOC_PROLOGUE }, /* Dt */ { in_line_eoln, MDOC_PROLOGUE }, /* Os */ { blk_full, MDOC_PARSED | MDOC_JOIN }, /* Sh */ @@ -200,6 +200,7 @@ static const struct mdoc_macro mdoc_macros[MDOC_MAX - MDOC_Dd] = { { in_line_eoln, MDOC_JOIN }, /* %Q */ { in_line_eoln, 0 }, /* %U */ { phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */ + { in_line_eoln, 0 }, /* Tg */ }; diff --git a/usr/src/cmd/mandoc/mdoc_man.c b/usr/src/cmd/mandoc/mdoc_man.c index 2e8f02ae56..0964cc6160 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.132 2019/01/04 03:17:36 schwarze Exp $ */ +/* $Id: mdoc_man.c,v 1.137 2021/07/04 15:38:26 schwarze Exp $ */ /* - * Copyright (c) 2011-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2021 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 @@ -113,7 +113,7 @@ static int pre_sm(DECL_ARGS); static void pre_sp(DECL_ARGS); static int pre_sect(DECL_ARGS); static int pre_sy(DECL_ARGS); -static void pre_syn(const struct roff_node *); +static void pre_syn(struct roff_node *); static void pre_ta(DECL_ARGS); static int pre_vt(DECL_ARGS); static int pre_xr(DECL_ARGS); @@ -262,6 +262,7 @@ static const struct mdoc_man_act mdoc_man_acts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL, post_percent, NULL, NULL }, /* %Q */ { NULL, NULL, post_percent, NULL, NULL }, /* %U */ { NULL, NULL, NULL, NULL, NULL }, /* Ta */ + { NULL, pre_skip, NULL, NULL, NULL }, /* Tg */ }; static const struct mdoc_man_act *mdoc_man_act(enum roff_tok); @@ -582,9 +583,9 @@ print_width(const struct mdoc_bl *bl, const struct roff_node *child) /* Set up the current list. */ if (chsz > sz && bl->type != LIST_tag) - print_block(".HP", 0); + print_block(".HP", MMAN_spc); else { - print_block(".TP", 0); + print_block(".TP", MMAN_spc); remain = sz + 2; } if (numeric) { @@ -649,7 +650,9 @@ print_node(DECL_ARGS) * Break the line if we were parsed subsequent the current node. * This makes the page structure be more consistent. */ - if (MMAN_spc & outflags && NODE_LINE & n->flags) + if (outflags & MMAN_spc && + n->flags & NODE_LINE && + !roff_node_transparent(n)) outflags |= MMAN_nl; act = NULL; @@ -657,7 +660,20 @@ print_node(DECL_ARGS) do_sub = 1; n->flags &= ~NODE_ENDED; - if (n->type == ROFFT_TEXT) { + switch (n->type) { + case ROFFT_EQN: + case ROFFT_TBL: + mandoc_msg(n->type == ROFFT_EQN ? MANDOCERR_EQN_TMAN : + MANDOCERR_TBL_TMAN, n->line, n->pos, NULL); + outflags |= MMAN_PP | MMAN_sp | MMAN_nl; + print_word("The"); + print_line(".B \\-T man", MMAN_nl); + print_word("output mode does not support"); + print_word(n->type == ROFFT_EQN ? "eqn(7)" : "tbl(7)"); + print_word("input."); + outflags |= MMAN_PP | MMAN_sp | MMAN_nl; + return; + case ROFFT_TEXT: /* * Make sure that we don't happen to start with a * control character at the start of a line. @@ -677,19 +693,18 @@ print_node(DECL_ARGS) outflags &= ~(MMAN_spc | MMAN_spc_force); else if (outflags & MMAN_Sm) outflags |= MMAN_spc; - } else if (n->tok < ROFF_MAX) { - (*roff_man_acts[n->tok])(meta, n); - return; - } else { - /* - * Conditionally run the pre-node action handler for a - * node. - */ + break; + default: + if (n->tok < ROFF_MAX) { + (*roff_man_acts[n->tok])(meta, n); + return; + } 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)) do_sub = (*act->pre)(meta, n); + break; } /* @@ -776,13 +791,20 @@ post_font(DECL_ARGS) static void post_percent(DECL_ARGS) { + struct roff_node *np, *nn, *nnn; if (mdoc_man_act(n->tok)->pre == pre_em) font_pop(); - if (n->next) { - print_word(","); - if (n->prev && n->prev->tok == n->tok && - n->next->tok == n->tok) + + if ((nn = roff_node_next(n)) != NULL) { + np = roff_node_prev(n); + nnn = nn == NULL ? NULL : roff_node_next(nn); + if (nn->tok != n->tok || + (np != NULL && np->tok == n->tok) || + (nnn != NULL && nnn->tok == n->tok)) + print_word(","); + if (nn->tok == n->tok && + (nnn == NULL || nnn->tok != n->tok)) print_word("and"); } else { print_word("."); @@ -850,13 +872,15 @@ post_sect(DECL_ARGS) /* See mdoc_term.c, synopsis_pre() for comments. */ static void -pre_syn(const struct roff_node *n) +pre_syn(struct roff_node *n) { + struct roff_node *np; - if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags)) + if ((n->flags & NODE_SYNPRETTY) == 0 || + (np = roff_node_prev(n)) == NULL) return; - if (n->prev->tok == n->tok && + if (np->tok == n->tok && MDOC_Ft != n->tok && MDOC_Fo != n->tok && MDOC_Fn != n->tok) { @@ -864,7 +888,7 @@ pre_syn(const struct roff_node *n) return; } - switch (n->prev->tok) { + switch (np->tok) { case MDOC_Fd: case MDOC_Fn: case MDOC_Fo: @@ -940,11 +964,10 @@ static int pre_bd(DECL_ARGS) { outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br); - - if (DISP_unfilled == n->norm->Bd.type || - DISP_literal == n->norm->Bd.type) + if (n->norm->Bd.type == DISP_unfilled || + n->norm->Bd.type == DISP_literal) print_line(".nf", 0); - if (0 == n->norm->Bd.comp && NULL != n->parent->prev) + if (n->norm->Bd.comp == 0 && roff_node_prev(n->parent) != NULL) outflags |= MMAN_sp; print_offs(n->norm->Bd.offs, 1); return 1; @@ -976,7 +999,7 @@ post_bd(DECL_ARGS) } /* Maybe we are inside an enclosing list? */ - if (NULL != n->parent->next) + if (roff_node_next(n->parent) != NULL) mid_it(); } @@ -1101,16 +1124,15 @@ post_bl(DECL_ARGS) print_line(".RE", MMAN_nl); assert(Bl_stack_len); Bl_stack_len--; - assert(0 == Bl_stack[Bl_stack_len]); + assert(Bl_stack[Bl_stack_len] == 0); } else { outflags |= MMAN_PP | MMAN_nl; outflags &= ~(MMAN_sp | MMAN_br); } /* Maybe we are inside an enclosing list? */ - if (NULL != n->parent->next) + if (roff_node_next(n->parent) != NULL) mid_it(); - } static void @@ -1122,7 +1144,6 @@ pre_br(DECL_ARGS) static int pre_dl(DECL_ARGS) { - print_offs("6n", 0); return 1; } @@ -1130,11 +1151,10 @@ pre_dl(DECL_ARGS) static void post_dl(DECL_ARGS) { - print_line(".RE", MMAN_nl); /* Maybe we are inside an enclosing list? */ - if (NULL != n->parent->next) + if (roff_node_next(n->parent) != NULL) mid_it(); } @@ -1235,15 +1255,15 @@ pre_fa(DECL_ARGS) static void post_fa(DECL_ARGS) { + struct roff_node *nn; - if (NULL != n->next && MDOC_Fa == n->next->tok) + if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa) print_word(","); } static int pre_fd(DECL_ARGS) { - pre_syn(n); font_push('B'); return 1; @@ -1252,7 +1272,6 @@ pre_fd(DECL_ARGS) static void post_fd(DECL_ARGS) { - font_pop(); outflags |= MMAN_br; } @@ -1260,7 +1279,6 @@ post_fd(DECL_ARGS) static int pre_fl(DECL_ARGS) { - font_push('B'); print_word("\\-"); if (n->child != NULL) @@ -1271,12 +1289,13 @@ pre_fl(DECL_ARGS) static void post_fl(DECL_ARGS) { + struct roff_node *nn; font_pop(); - if (!(n->child != NULL || - n->next == NULL || - n->next->type == ROFFT_TEXT || - n->next->flags & NODE_LINE)) + if (n->child == NULL && + ((nn = roff_node_next(n)) != NULL && + nn->type != ROFFT_TEXT && + (nn->flags & NODE_LINE) == 0)) outflags &= ~MMAN_spc; } @@ -1419,9 +1438,9 @@ pre_it(DECL_ARGS) case ROFFT_HEAD: outflags |= MMAN_PP | MMAN_nl; bln = n->parent->parent; - if (0 == bln->norm->Bl.comp || - (NULL == n->parent->prev && - NULL == bln->parent->prev)) + if (bln->norm->Bl.comp == 0 || + (n->parent->prev == NULL && + roff_node_prev(bln->parent) == NULL)) outflags |= MMAN_sp; outflags &= ~MMAN_br; switch (bln->norm->Bl.type) { @@ -1633,17 +1652,22 @@ pre_nm(DECL_ARGS) { char *name; - if (n->type == ROFFT_BLOCK) { + switch (n->type) { + case ROFFT_BLOCK: outflags |= MMAN_Bk; pre_syn(n); - } - if (n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) return 1; + case ROFFT_HEAD: + case ROFFT_ELEM: + break; + default: + return 1; + } name = n->child == NULL ? NULL : n->child->string; - if (NULL == name) + if (name == NULL) return 0; if (n->type == ROFFT_HEAD) { - if (NULL == n->parent->prev) + if (roff_node_prev(n->parent) == NULL) outflags |= MMAN_sp; print_block(".HP", 0); printf(" %dn", man_strlen(name) + 1); diff --git a/usr/src/cmd/mandoc/mdoc_markdown.c b/usr/src/cmd/mandoc/mdoc_markdown.c index b73811b76b..63d8e17055 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.30 2018/12/30 00:49:55 schwarze Exp $ */ +/* $Id: mdoc_markdown.c,v 1.37 2021/08/10 12:55:03 schwarze Exp $ */ /* - * Copyright (c) 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2017, 2018, 2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,7 +13,11 @@ * 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. + * + * Markdown formatter for mdoc(7) used by mandoc(1). */ +#include "config.h" + #include <sys/types.h> #include <assert.h> @@ -29,16 +33,16 @@ #include "main.h" struct md_act { - int (*cond)(struct roff_node *n); - int (*pre)(struct roff_node *n); - void (*post)(struct roff_node *n); + int (*cond)(struct roff_node *); + int (*pre)(struct roff_node *); + void (*post)(struct roff_node *); const char *prefix; /* pre-node string constant */ const char *suffix; /* post-node string constant */ }; static void md_nodelist(struct roff_node *); static void md_node(struct roff_node *); -static const char *md_stack(char c); +static const char *md_stack(char); static void md_preword(void); static void md_rawword(const char *); static void md_word(const char *); @@ -226,6 +230,7 @@ static const struct md_act md_acts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */ { NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */ { NULL, NULL, NULL, NULL, NULL }, /* Ta */ + { NULL, md_pre_skip, NULL, NULL, NULL }, /* Tg */ }; static const struct md_act *md_act(enum roff_tok); @@ -309,7 +314,9 @@ md_node(struct roff_node *n) if (outflags & MD_nonl) outflags &= ~(MD_nl | MD_sp); - else if (outflags & MD_spc && n->flags & NODE_LINE) + else if (outflags & MD_spc && + n->flags & NODE_LINE && + !roff_node_transparent(n)) outflags |= MD_nl; act = NULL; @@ -596,16 +603,18 @@ md_word(const char *s) md_rawword("markdown"); continue; case ESCAPE_FONTBOLD: + case ESCAPE_FONTCB: nextfont = "**"; break; case ESCAPE_FONTITALIC: + case ESCAPE_FONTCI: nextfont = "*"; break; case ESCAPE_FONTBI: nextfont = "***"; break; case ESCAPE_FONT: - case ESCAPE_FONTCW: + case ESCAPE_FONTCR: case ESCAPE_FONTROMAN: nextfont = ""; break; @@ -786,14 +795,17 @@ md_post_word(struct roff_node *n) static void md_post_pc(struct roff_node *n) { + struct roff_node *nn; + md_post_raw(n); if (n->parent->tok != MDOC_Rs) return; - if (n->next != NULL) { + + if ((nn = roff_node_next(n)) != NULL) { md_word(","); - if (n->prev != NULL && - n->prev->tok == n->tok && - n->next->tok == n->tok) + if (nn->tok == n->tok && + (nn = roff_node_prev(n)) != NULL && + nn->tok == n->tok) md_word("and"); } else { md_word("."); @@ -810,10 +822,13 @@ md_pre_skip(struct roff_node *n) static void md_pre_syn(struct roff_node *n) { - if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY)) + struct roff_node *np; + + if ((n->flags & NODE_SYNPRETTY) == 0 || + (np = roff_node_prev(n)) == NULL) return; - if (n->prev->tok == n->tok && + if (np->tok == n->tok && n->tok != MDOC_Ft && n->tok != MDOC_Fo && n->tok != MDOC_Fn) { @@ -821,7 +836,7 @@ md_pre_syn(struct roff_node *n) return; } - switch (n->prev->tok) { + switch (np->tok) { case MDOC_Fd: case MDOC_Fn: case MDOC_Fo: @@ -1052,7 +1067,9 @@ md_pre_Fa(struct roff_node *n) static void md_post_Fa(struct roff_node *n) { - if (n->next != NULL && n->next->tok == MDOC_Fa) + struct roff_node *nn; + + if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa) md_word(","); } @@ -1074,9 +1091,11 @@ md_post_Fd(struct roff_node *n) static void md_post_Fl(struct roff_node *n) { + struct roff_node *nn; + md_post_raw(n); - if (n->child == NULL && n->next != NULL && - n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE)) + if (n->child == NULL && (nn = roff_node_next(n)) != NULL && + nn->type != ROFFT_TEXT && (nn->flags & NODE_LINE) == 0) outflags &= ~MD_spc; } diff --git a/usr/src/cmd/mandoc/mdoc_state.c b/usr/src/cmd/mandoc/mdoc_state.c index f9a585e736..d696ff27e0 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.15 2019/01/01 07:42:04 schwarze Exp $ */ +/* $Id: mdoc_state.c,v 1.17 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * @@ -14,6 +14,8 @@ * 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 <sys/types.h> #include <assert.h> @@ -157,6 +159,7 @@ static const state_handler state_handlers[MDOC_MAX - MDOC_Dd] = { NULL, /* %Q */ NULL, /* %U */ NULL, /* Ta */ + NULL, /* Tg */ }; diff --git a/usr/src/cmd/mandoc/mdoc_term.c b/usr/src/cmd/mandoc/mdoc_term.c index c2f265c4e7..42392c7c0a 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.373 2019/06/03 19:50:33 schwarze Exp $ */ +/* $Id: mdoc_term.c,v 1.380 2020/04/06 10:16:17 schwarze Exp $ */ /* + * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -15,6 +15,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Plain text formatter for mdoc(7), used by mandoc(1) + * for ASCII, UTF-8, PostScript, and PDF output. */ #include "config.h" @@ -33,7 +36,7 @@ #include "mdoc.h" #include "out.h" #include "term.h" -#include "tag.h" +#include "term_tag.h" #include "main.h" struct termpair { @@ -54,14 +57,12 @@ struct mdoc_term_act { static int a2width(const struct termp *, const char *); static void print_bvspace(struct termp *, - const struct roff_node *, - const struct roff_node *); + struct roff_node *, struct roff_node *); static void print_mdoc_node(DECL_ARGS); static void print_mdoc_nodelist(DECL_ARGS); static void print_mdoc_head(struct termp *, const struct roff_meta *); static void print_mdoc_foot(struct termp *, const struct roff_meta *); -static void synopsis_pre(struct termp *, - const struct roff_node *); +static void synopsis_pre(struct termp *, struct roff_node *); static void termp____post(DECL_ARGS); static void termp__t_post(DECL_ARGS); @@ -91,11 +92,8 @@ static int termp_bf_pre(DECL_ARGS); static int termp_bk_pre(DECL_ARGS); static int termp_bl_pre(DECL_ARGS); static int termp_bold_pre(DECL_ARGS); -static int termp_cd_pre(DECL_ARGS); static int termp_d1_pre(DECL_ARGS); static int termp_eo_pre(DECL_ARGS); -static int termp_em_pre(DECL_ARGS); -static int termp_er_pre(DECL_ARGS); static int termp_ex_pre(DECL_ARGS); static int termp_fa_pre(DECL_ARGS); static int termp_fd_pre(DECL_ARGS); @@ -117,8 +115,6 @@ static int termp_skip_pre(DECL_ARGS); static int termp_sm_pre(DECL_ARGS); static int termp_pp_pre(DECL_ARGS); static int termp_ss_pre(DECL_ARGS); -static int termp_sy_pre(DECL_ARGS); -static int termp_tag_pre(DECL_ARGS); static int termp_under_pre(DECL_ARGS); static int termp_vt_pre(DECL_ARGS); static int termp_xr_pre(DECL_ARGS); @@ -142,11 +138,11 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { { termp_an_pre, NULL }, /* An */ { termp_ap_pre, NULL }, /* Ap */ { termp_under_pre, NULL }, /* Ar */ - { termp_cd_pre, NULL }, /* Cd */ + { termp_fd_pre, NULL }, /* Cd */ { termp_bold_pre, NULL }, /* Cm */ { termp_li_pre, NULL }, /* Dv */ - { termp_er_pre, NULL }, /* Er */ - { termp_tag_pre, NULL }, /* Ev */ + { NULL, NULL }, /* Er */ + { NULL, NULL }, /* Ev */ { termp_ex_pre, NULL }, /* Ex */ { termp_fa_pre, NULL }, /* Fa */ { termp_fd_pre, termp_fd_post }, /* Fd */ @@ -193,7 +189,7 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { { termp_quote_pre, termp_quote_post }, /* Dq */ { NULL, NULL }, /* Ec */ /* FIXME: no space */ { NULL, NULL }, /* Ef */ - { termp_em_pre, NULL }, /* Em */ + { termp_under_pre, NULL }, /* Em */ { termp_eo_pre, termp_eo_post }, /* Eo */ { termp_xx_pre, termp_xx_post }, /* Fx */ { termp_bold_pre, NULL }, /* Ms */ @@ -216,7 +212,7 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { { termp_quote_pre, termp_quote_post }, /* Sq */ { termp_sm_pre, NULL }, /* Sm */ { termp_under_pre, NULL }, /* Sx */ - { termp_sy_pre, NULL }, /* Sy */ + { termp_bold_pre, NULL }, /* Sy */ { NULL, NULL }, /* Tn */ { termp_xx_pre, termp_xx_post }, /* Ux */ { NULL, NULL }, /* Xc */ @@ -245,15 +241,14 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { { NULL, termp____post }, /* %Q */ { NULL, termp____post }, /* %U */ { NULL, NULL }, /* Ta */ + { termp_skip_pre, NULL }, /* Tg */ }; -static int fn_prio; - void terminal_mdoc(void *arg, const struct roff_meta *mdoc) { - struct roff_node *n; + struct roff_node *n, *nn; struct termp *p; size_t save_defindent; @@ -265,16 +260,20 @@ terminal_mdoc(void *arg, const struct roff_meta *mdoc) n = mdoc->first->child; if (p->synopsisonly) { - while (n != NULL) { - if (n->tok == MDOC_Sh && n->sec == SEC_SYNOPSIS) { - if (n->child->next->child != NULL) - print_mdoc_nodelist(p, NULL, - mdoc, n->child->next->child); - term_newln(p); + for (nn = NULL; n != NULL; n = n->next) { + if (n->tok != MDOC_Sh) + continue; + if (n->sec == SEC_SYNOPSIS) break; - } - n = n->next; + if (nn == NULL && n->sec == SEC_NAME) + nn = n; } + if (n == NULL) + n = nn; + p->flags |= TERMP_NOSPACE; + if (n != NULL && (n = n->child->next->child) != NULL) + print_mdoc_nodelist(p, NULL, mdoc, n); + term_newln(p); } else { save_defindent = p->defindent; if (p->defindent == 0) @@ -297,7 +296,6 @@ terminal_mdoc(void *arg, const struct roff_meta *mdoc) static void print_mdoc_nodelist(DECL_ARGS) { - while (n != NULL) { print_mdoc_node(p, pair, meta, n); n = n->next; @@ -337,6 +335,10 @@ print_mdoc_node(DECL_ARGS) memset(&npair, 0, sizeof(struct termpair)); npair.ppair = pair; + if (n->flags & NODE_ID && n->tok != MDOC_Pp && + (n->tok != MDOC_It || n->type != ROFFT_BLOCK)) + term_tag_write(n, p->line); + /* * Keeps only work until the end of a line. If a keep was * invoked in a prior line, revert it to PREKEEP. @@ -576,29 +578,20 @@ a2width(const struct termp *p, const char *v) * too. */ static void -print_bvspace(struct termp *p, - const struct roff_node *bl, - const struct roff_node *n) +print_bvspace(struct termp *p, struct roff_node *bl, struct roff_node *n) { - const struct roff_node *nn; - - assert(n); + struct roff_node *nn; term_newln(p); - if (MDOC_Bd == bl->tok && bl->norm->Bd.comp) - return; - if (MDOC_Bl == bl->tok && bl->norm->Bl.comp) + if ((bl->tok == MDOC_Bd && bl->norm->Bd.comp) || + (bl->tok == MDOC_Bl && bl->norm->Bl.comp)) return; /* Do not vspace directly after Ss/Sh. */ nn = n; - while (nn->prev != NULL && - (nn->prev->type == ROFFT_COMMENT || - nn->prev->flags & NODE_NOPRT)) - nn = nn->prev; - while (nn->prev == NULL) { + while (roff_node_prev(nn) == NULL) { do { nn = nn->parent; if (nn->type == ROFFT_ROOT) @@ -611,22 +604,18 @@ print_bvspace(struct termp *p, break; } - /* A `-column' does not assert vspace within the list. */ - - if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type) - if (n->prev && MDOC_It == n->prev->tok) - return; - - /* A `-diag' without body does not vspace. */ - - if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type) - if (n->prev && MDOC_It == n->prev->tok) { - assert(n->prev->body); - if (NULL == n->prev->body->child) - return; - } + /* + * No vertical space after: + * items in .Bl -column + * items without a body in .Bl -diag + */ - term_vspace(p); + if (bl->tok != MDOC_Bl || + n->prev == NULL || n->prev->tok != MDOC_It || + (bl->norm->Bl.type != LIST_column && + (bl->norm->Bl.type != LIST_diag || + n->prev->body->child != NULL))) + term_vspace(p); } @@ -642,6 +631,8 @@ termp_it_pre(DECL_ARGS) if (n->type == ROFFT_BLOCK) { print_bvspace(p, n->parent->parent, n); + if (n->flags & NODE_ID) + term_tag_write(n, p->line); return 1; } @@ -1014,38 +1005,44 @@ termp_nm_pre(DECL_ARGS) p->flags |= TERMP_HANG; } } - - term_fontpush(p, TERMFONT_BOLD); - return 1; + return termp_bold_pre(p, pair, meta, n); } static void termp_nm_post(DECL_ARGS) { - - if (n->type == ROFFT_BLOCK) { + switch (n->type) { + case ROFFT_BLOCK: p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); - } else if (n->type == ROFFT_HEAD && - NULL != n->next && NULL != n->next->child) { + break; + case ROFFT_HEAD: + if (n->next == NULL || n->next->child == NULL) + break; term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); p->trailspace = 0; - } else if (n->type == ROFFT_BODY && n->child != NULL) - term_flushln(p); + break; + case ROFFT_BODY: + if (n->child != NULL) + term_flushln(p); + break; + default: + break; + } } static int termp_fl_pre(DECL_ARGS) { + struct roff_node *nn; - termp_tag_pre(p, pair, meta, n); term_fontpush(p, TERMFONT_BOLD); term_word(p, "\\-"); - if (!(n->child == NULL && - (n->next == NULL || - n->next->type == ROFFT_TEXT || - n->next->flags & NODE_LINE))) + if (n->child != NULL || + ((nn = roff_node_next(n)) != NULL && + nn->type != ROFFT_TEXT && + (nn->flags & NODE_LINE) == 0)) p->flags |= TERMP_NOSPACE; return 1; @@ -1054,10 +1051,11 @@ termp_fl_pre(DECL_ARGS) static int termp__a_pre(DECL_ARGS) { + struct roff_node *nn; - if (n->prev && MDOC__A == n->prev->tok) - if (NULL == n->next || MDOC__A != n->next->tok) - term_word(p, "and"); + if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A && + ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A)) + term_word(p, "and"); return 1; } @@ -1098,10 +1096,9 @@ termp_ns_pre(DECL_ARGS) static int termp_rs_pre(DECL_ARGS) { - if (SEC_SEE_ALSO != n->sec) return 1; - if (n->type == ROFFT_BLOCK && n->prev != NULL) + if (n->type == ROFFT_BLOCK && roff_node_prev(n) != NULL) term_vspace(p); return 1; } @@ -1116,7 +1113,6 @@ termp_ex_pre(DECL_ARGS) static int termp_nd_pre(DECL_ARGS) { - if (n->type == ROFFT_BODY) term_word(p, "\\(en"); return 1; @@ -1125,14 +1121,20 @@ termp_nd_pre(DECL_ARGS) static int termp_bl_pre(DECL_ARGS) { - - return n->type != ROFFT_HEAD; + switch (n->type) { + case ROFFT_BLOCK: + term_newln(p); + return 1; + case ROFFT_HEAD: + return 0; + default: + return 1; + } } static void termp_bl_post(DECL_ARGS) { - if (n->type != ROFFT_BLOCK) return; term_newln(p); @@ -1146,7 +1148,6 @@ termp_bl_post(DECL_ARGS) static int termp_xr_pre(DECL_ARGS) { - if (NULL == (n = n->child)) return 0; @@ -1175,13 +1176,12 @@ termp_xr_pre(DECL_ARGS) * macro combos). */ static void -synopsis_pre(struct termp *p, const struct roff_node *n) +synopsis_pre(struct termp *p, struct roff_node *n) { - /* - * Obviously, if we're not in a SYNOPSIS or no prior macros - * exist, do nothing. - */ - if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags)) + struct roff_node *np; + + if ((n->flags & NODE_SYNPRETTY) == 0 || + (np = roff_node_prev(n)) == NULL) return; /* @@ -1189,7 +1189,7 @@ synopsis_pre(struct termp *p, const struct roff_node *n) * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which * case we soldier on. */ - if (n->prev->tok == n->tok && + if (np->tok == n->tok && MDOC_Ft != n->tok && MDOC_Fo != n->tok && MDOC_Fn != n->tok) { @@ -1202,7 +1202,7 @@ synopsis_pre(struct termp *p, const struct roff_node *n) * another (or Fn/Fo, which we've let slip through) then assert * vertical space, else only newline and move on. */ - switch (n->prev->tok) { + switch (np->tok) { case MDOC_Fd: case MDOC_Fn: case MDOC_Fo: @@ -1211,7 +1211,7 @@ synopsis_pre(struct termp *p, const struct roff_node *n) term_vspace(p); break; case MDOC_Ft: - if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { + if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) { term_vspace(p); break; } @@ -1225,24 +1225,22 @@ synopsis_pre(struct termp *p, const struct roff_node *n) static int termp_vt_pre(DECL_ARGS) { - - if (n->type == ROFFT_ELEM) { - synopsis_pre(p, n); - return termp_under_pre(p, pair, meta, n); - } else if (n->type == ROFFT_BLOCK) { + switch (n->type) { + case ROFFT_ELEM: + return termp_ft_pre(p, pair, meta, n); + case ROFFT_BLOCK: synopsis_pre(p, n); return 1; - } else if (n->type == ROFFT_HEAD) + case ROFFT_HEAD: return 0; - - return termp_under_pre(p, pair, meta, n); + default: + return termp_under_pre(p, pair, meta, n); + } } static int termp_bold_pre(DECL_ARGS) { - - termp_tag_pre(p, pair, meta, n); term_fontpush(p, TERMFONT_BOLD); return 1; } @@ -1250,7 +1248,6 @@ termp_bold_pre(DECL_ARGS) static int termp_fd_pre(DECL_ARGS) { - synopsis_pre(p, n); return termp_bold_pre(p, pair, meta, n); } @@ -1258,13 +1255,13 @@ termp_fd_pre(DECL_ARGS) static void termp_fd_post(DECL_ARGS) { - term_newln(p); } static int termp_sh_pre(DECL_ARGS) { + struct roff_node *np; switch (n->type) { case ROFFT_BLOCK: @@ -1272,30 +1269,20 @@ termp_sh_pre(DECL_ARGS) * Vertical space before sections, except * when the previous section was empty. */ - if (n->prev == NULL || - n->prev->tok != MDOC_Sh || - (n->prev->body != NULL && - n->prev->body->child != NULL)) + if ((np = roff_node_prev(n)) == NULL || + np->tok != MDOC_Sh || + (np->body != NULL && np->body->child != NULL)) term_vspace(p); break; case ROFFT_HEAD: - term_fontpush(p, TERMFONT_BOLD); - break; + return termp_bold_pre(p, pair, meta, n); case ROFFT_BODY: p->tcol->offset = term_len(p, p->defindent); term_tab_set(p, NULL); term_tab_set(p, "T"); term_tab_set(p, ".5i"); - switch (n->sec) { - case SEC_DESCRIPTION: - fn_prio = 0; - break; - case SEC_AUTHORS: + if (n->sec == SEC_AUTHORS) p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT); - break; - default: - break; - } break; default: break; @@ -1306,7 +1293,6 @@ termp_sh_pre(DECL_ARGS) static void termp_sh_post(DECL_ARGS) { - switch (n->type) { case ROFFT_HEAD: term_newln(p); @@ -1323,15 +1309,13 @@ termp_sh_post(DECL_ARGS) static void termp_lb_post(DECL_ARGS) { - - if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags) + if (n->sec == SEC_LIBRARY && n->flags & NODE_LINE) term_newln(p); } static int termp_d1_pre(DECL_ARGS) { - if (n->type != ROFFT_BLOCK) return 1; term_newln(p); @@ -1345,11 +1329,8 @@ termp_d1_pre(DECL_ARGS) static int termp_ft_pre(DECL_ARGS) { - - /* NB: NODE_LINE does not effect this! */ synopsis_pre(p, n); - term_fontpush(p, TERMFONT_UNDER); - return 1; + return termp_under_pre(p, pair, meta, n); } static int @@ -1358,11 +1339,9 @@ termp_fn_pre(DECL_ARGS) size_t rmargin = 0; int pretty; - pretty = NODE_SYNPRETTY & n->flags; - synopsis_pre(p, n); - - if (NULL == (n = n->child)) + pretty = n->flags & NODE_SYNPRETTY; + if ((n = n->child) == NULL) return 0; if (pretty) { @@ -1376,9 +1355,6 @@ termp_fn_pre(DECL_ARGS) term_word(p, n->string); term_fontpop(p); - if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM) - tag_put(n->string, ++fn_prio, p->line); - if (pretty) { term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); @@ -1413,7 +1389,6 @@ termp_fn_pre(DECL_ARGS) term_word(p, ";"); term_flushln(p); } - return 0; } @@ -1422,23 +1397,25 @@ termp_fa_pre(DECL_ARGS) { const struct roff_node *nn; - if (n->parent->tok != MDOC_Fo) { - term_fontpush(p, TERMFONT_UNDER); - return 1; - } + if (n->parent->tok != MDOC_Fo) + return termp_under_pre(p, pair, meta, n); - for (nn = n->child; nn; nn = nn->next) { + for (nn = n->child; nn != NULL; nn = nn->next) { term_fontpush(p, TERMFONT_UNDER); p->flags |= TERMP_NBRWORD; term_word(p, nn->string); term_fontpop(p); - - if (nn->next || (n->next && n->next->tok == MDOC_Fa)) { + if (nn->next != NULL) { p->flags |= TERMP_NOSPACE; term_word(p, ","); } } - + if (n->child != NULL && + (nn = roff_node_next(n)) != NULL && + nn->tok == MDOC_Fa) { + p->flags |= TERMP_NOSPACE; + term_word(p, ","); + } return 0; } @@ -1518,30 +1495,23 @@ termp_xx_post(DECL_ARGS) static void termp_pf_post(DECL_ARGS) { - - if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) + if (n->next != NULL && (n->next->flags & NODE_LINE) == 0) p->flags |= TERMP_NOSPACE; } static int termp_ss_pre(DECL_ARGS) { - struct roff_node *nn; - switch (n->type) { case ROFFT_BLOCK: - term_newln(p); - for (nn = n->prev; nn != NULL; nn = nn->prev) - if (nn->type != ROFFT_COMMENT && - (nn->flags & NODE_NOPRT) == 0) - break; - if (nn != NULL) + if (roff_node_prev(n) == NULL) + term_newln(p); + else term_vspace(p); break; case ROFFT_HEAD: - term_fontpush(p, TERMFONT_BOLD); p->tcol->offset = term_len(p, (p->defindent+1)/2); - break; + return termp_bold_pre(p, pair, meta, n); case ROFFT_BODY: p->tcol->offset = term_len(p, p->defindent); term_tab_set(p, NULL); @@ -1551,34 +1521,21 @@ termp_ss_pre(DECL_ARGS) default: break; } - return 1; } static void termp_ss_post(DECL_ARGS) { - if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY) term_newln(p); } static int -termp_cd_pre(DECL_ARGS) -{ - - synopsis_pre(p, n); - term_fontpush(p, TERMFONT_BOLD); - return 1; -} - -static int termp_in_pre(DECL_ARGS) { - synopsis_pre(p, n); - - if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) { + if (n->flags & NODE_SYNPRETTY && n->flags & NODE_LINE) { term_fontpush(p, TERMFONT_BOLD); term_word(p, "#include"); term_word(p, "<"); @@ -1586,7 +1543,6 @@ termp_in_pre(DECL_ARGS) term_word(p, "<"); term_fontpush(p, TERMFONT_UNDER); } - p->flags |= TERMP_NOSPACE; return 1; } @@ -1594,36 +1550,32 @@ termp_in_pre(DECL_ARGS) static void termp_in_post(DECL_ARGS) { - - if (NODE_SYNPRETTY & n->flags) + if (n->flags & NODE_SYNPRETTY) term_fontpush(p, TERMFONT_BOLD); - p->flags |= TERMP_NOSPACE; term_word(p, ">"); - - if (NODE_SYNPRETTY & n->flags) + if (n->flags & NODE_SYNPRETTY) term_fontpop(p); } static int termp_pp_pre(DECL_ARGS) { - fn_prio = 0; term_vspace(p); + if (n->flags & NODE_ID) + term_tag_write(n, p->line); return 0; } static int termp_skip_pre(DECL_ARGS) { - return 0; } static int termp_quote_pre(DECL_ARGS) { - if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) return 1; @@ -1780,17 +1732,15 @@ termp_eo_post(DECL_ARGS) static int termp_fo_pre(DECL_ARGS) { - size_t rmargin = 0; - int pretty; - - pretty = NODE_SYNPRETTY & n->flags; + size_t rmargin; - if (n->type == ROFFT_BLOCK) { + switch (n->type) { + case ROFFT_BLOCK: synopsis_pre(p, n); return 1; - } else if (n->type == ROFFT_BODY) { - if (pretty) { - rmargin = p->tcol->rmargin; + case ROFFT_BODY: + rmargin = p->tcol->rmargin; + if (n->flags & NODE_SYNPRETTY) { p->tcol->rmargin = p->tcol->offset + term_len(p, 4); p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG; @@ -1798,7 +1748,7 @@ termp_fo_pre(DECL_ARGS) p->flags |= TERMP_NOSPACE; term_word(p, "("); p->flags |= TERMP_NOSPACE; - if (pretty) { + if (n->flags & NODE_SYNPRETTY) { term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); @@ -1807,30 +1757,21 @@ termp_fo_pre(DECL_ARGS) p->tcol->rmargin = rmargin; } return 1; + default: + return termp_bold_pre(p, pair, meta, n); } - - if (NULL == n->child) - return 0; - - /* XXX: we drop non-initial arguments as per groff. */ - - assert(n->child->string); - term_fontpush(p, TERMFONT_BOLD); - term_word(p, n->child->string); - return 0; } static void termp_fo_post(DECL_ARGS) { - if (n->type != ROFFT_BODY) return; p->flags |= TERMP_NOSPACE; term_word(p, ")"); - if (NODE_SYNPRETTY & n->flags) { + if (n->flags & NODE_SYNPRETTY) { p->flags |= TERMP_NOSPACE; term_word(p, ";"); term_flushln(p); @@ -1840,29 +1781,30 @@ termp_fo_post(DECL_ARGS) static int termp_bf_pre(DECL_ARGS) { - - if (n->type == ROFFT_HEAD) + switch (n->type) { + case ROFFT_HEAD: return 0; - else if (n->type != ROFFT_BODY) + case ROFFT_BODY: + break; + default: return 1; - - if (FONT_Em == n->norm->Bf.font) - term_fontpush(p, TERMFONT_UNDER); - else if (FONT_Sy == n->norm->Bf.font) - term_fontpush(p, TERMFONT_BOLD); - else - term_fontpush(p, TERMFONT_NONE); - - return 1; + } + switch (n->norm->Bf.font) { + case FONT_Em: + return termp_under_pre(p, pair, meta, n); + case FONT_Sy: + return termp_bold_pre(p, pair, meta, n); + default: + return termp_li_pre(p, pair, meta, n); + } } static int termp_sm_pre(DECL_ARGS) { - - if (NULL == n->child) + if (n->child == NULL) p->flags ^= TERMP_NONOSPACE; - else if (0 == strcmp("on", n->child->string)) + else if (strcmp(n->child->string, "on") == 0) p->flags &= ~TERMP_NONOSPACE; else p->flags |= TERMP_NONOSPACE; @@ -1876,7 +1818,6 @@ termp_sm_pre(DECL_ARGS) static int termp_ap_pre(DECL_ARGS) { - p->flags |= TERMP_NOSPACE; term_word(p, "'"); p->flags |= TERMP_NOSPACE; @@ -1886,24 +1827,26 @@ termp_ap_pre(DECL_ARGS) static void termp____post(DECL_ARGS) { + struct roff_node *nn; /* * Handle lists of authors. In general, print each followed by * a comma. Don't print the comma if there are only two * authors. */ - if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) - if (NULL == n->next->next || MDOC__A != n->next->next->tok) - if (NULL == n->prev || MDOC__A != n->prev->tok) - return; + if (n->tok == MDOC__A && + (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A && + ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) && + ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A)) + return; /* TODO: %U. */ - if (NULL == n->parent || MDOC_Rs != n->parent->tok) + if (n->parent == NULL || n->parent->tok != MDOC_Rs) return; p->flags |= TERMP_NOSPACE; - if (NULL == n->next) { + if (roff_node_next(n) == NULL) { term_word(p, "."); p->flags |= TERMP_SENTENCE; } else @@ -1913,8 +1856,6 @@ termp____post(DECL_ARGS) static int termp_li_pre(DECL_ARGS) { - - termp_tag_pre(p, pair, meta, n); term_fontpush(p, TERMFONT_NONE); return 1; } @@ -1964,7 +1905,6 @@ termp_lk_pre(DECL_ARGS) static int termp_bk_pre(DECL_ARGS) { - switch (n->type) { case ROFFT_BLOCK: break; @@ -1977,107 +1917,47 @@ termp_bk_pre(DECL_ARGS) default: abort(); } - return 1; } static void termp_bk_post(DECL_ARGS) { - if (n->type == ROFFT_BODY) p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); } +/* + * If we are in an `Rs' and there is a journal present, + * then quote us instead of underlining us (for disambiguation). + */ static void termp__t_post(DECL_ARGS) { - - /* - * If we're in an `Rs' and there's a journal present, then quote - * us instead of underlining us (for disambiguation). - */ - if (n->parent && MDOC_Rs == n->parent->tok && + if (n->parent != NULL && n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) termp_quote_post(p, pair, meta, n); - termp____post(p, pair, meta, n); } static int termp__t_pre(DECL_ARGS) { - - /* - * If we're in an `Rs' and there's a journal present, then quote - * us instead of underlining us (for disambiguation). - */ - if (n->parent && MDOC_Rs == n->parent->tok && + if (n->parent != NULL && n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) return termp_quote_pre(p, pair, meta, n); - - term_fontpush(p, TERMFONT_UNDER); - return 1; + else + return termp_under_pre(p, pair, meta, n); } static int termp_under_pre(DECL_ARGS) { - - term_fontpush(p, TERMFONT_UNDER); - return 1; -} - -static int -termp_em_pre(DECL_ARGS) -{ - if (n->child != NULL && - n->child->type == ROFFT_TEXT) - tag_put(n->child->string, 0, p->line); term_fontpush(p, TERMFONT_UNDER); return 1; } static int -termp_sy_pre(DECL_ARGS) -{ - if (n->child != NULL && - n->child->type == ROFFT_TEXT) - tag_put(n->child->string, 0, p->line); - term_fontpush(p, TERMFONT_BOLD); - return 1; -} - -static int -termp_er_pre(DECL_ARGS) -{ - - if (n->sec == SEC_ERRORS && - (n->parent->tok == MDOC_It || - (n->parent->tok == MDOC_Bq && - n->parent->parent->parent->tok == MDOC_It))) - tag_put(n->child->string, 1, p->line); - return 1; -} - -static int -termp_tag_pre(DECL_ARGS) -{ - - if (n->child != NULL && - n->child->type == ROFFT_TEXT && - (n->prev == NULL || - (n->prev->type == ROFFT_TEXT && - strcmp(n->prev->string, "|") == 0)) && - (n->parent->tok == MDOC_It || - (n->parent->tok == MDOC_Xo && - n->parent->parent->prev == NULL && - n->parent->parent->parent->tok == MDOC_It))) - tag_put(n->child->string, 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 95cb0b0f0a..5a12c7f440 100644 --- a/usr/src/cmd/mandoc/mdoc_validate.c +++ b/usr/src/cmd/mandoc/mdoc_validate.c @@ -1,9 +1,8 @@ -/* $Id: mdoc_validate.c,v 1.371 2019/03/04 13:01:57 schwarze Exp $ */ +/* $Id: mdoc_validate.c,v 1.389 2021/07/18 11:41:23 schwarze Exp $ */ /* + * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> - * Copyright 2019 Joyent, Inc. * * 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 +15,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Validation module for mdoc(7) syntax trees used by mandoc(1). */ #include "config.h" @@ -40,6 +41,7 @@ #include "libmandoc.h" #include "roff_int.h" #include "libmdoc.h" +#include "tag.h" /* FIXME: .Bl -diag can't have non-text children in HEAD. */ @@ -64,8 +66,8 @@ 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_abort(POST_ARGS) __attribute__((__noreturn__)); static void post_an(POST_ARGS); static void post_an_norm(POST_ARGS); static void post_at(POST_ARGS); @@ -83,16 +85,18 @@ static void post_dd(POST_ARGS); static void post_delim(POST_ARGS); static void post_delim_nb(POST_ARGS); static void post_dt(POST_ARGS); +static void post_em(POST_ARGS); static void post_en(POST_ARGS); +static void post_er(POST_ARGS); static void post_es(POST_ARGS); static void post_eoln(POST_ARGS); static void post_ex(POST_ARGS); static void post_fa(POST_ARGS); +static void post_fl(POST_ARGS); static void post_fn(POST_ARGS); static void post_fname(POST_ARGS); static void post_fo(POST_ARGS); static void post_hyph(POST_ARGS); -static void post_ignpar(POST_ARGS); static void post_it(POST_ARGS); static void post_lb(POST_ARGS); static void post_nd(POST_ARGS); @@ -105,6 +109,7 @@ static void post_prevpar(POST_ARGS); static void post_root(POST_ARGS); static void post_rs(POST_ARGS); static void post_rv(POST_ARGS); +static void post_section(POST_ARGS); static void post_sh(POST_ARGS); static void post_sh_head(POST_ARGS); static void post_sh_name(POST_ARGS); @@ -114,6 +119,8 @@ static void post_sm(POST_ARGS); static void post_st(POST_ARGS); static void post_std(POST_ARGS); static void post_sx(POST_ARGS); +static void post_tag(POST_ARGS); +static void post_tg(POST_ARGS); static void post_useless(POST_ARGS); static void post_xr(POST_ARGS); static void post_xx(POST_ARGS); @@ -123,7 +130,7 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_dt, /* Dt */ post_os, /* Os */ post_sh, /* Sh */ - post_ignpar, /* Ss */ + post_section, /* Ss */ post_par, /* Pp */ post_display, /* D1 */ post_display, /* Dl */ @@ -137,19 +144,19 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { NULL, /* Ap */ post_defaults, /* Ar */ NULL, /* Cd */ - post_delim_nb, /* Cm */ - post_delim_nb, /* Dv */ - post_delim_nb, /* Er */ - post_delim_nb, /* Ev */ + post_tag, /* Cm */ + post_tag, /* Dv */ + post_er, /* Er */ + post_tag, /* Ev */ post_ex, /* Ex */ post_fa, /* Fa */ NULL, /* Fd */ - post_delim_nb, /* Fl */ + post_fl, /* Fl */ post_fn, /* Fn */ post_delim_nb, /* Ft */ - post_delim_nb, /* Ic */ + post_tag, /* Ic */ post_delim_nb, /* In */ - post_defaults, /* Li */ + post_tag, /* Li */ post_nd, /* Nd */ post_nm, /* Nm */ post_delim_nb, /* Op */ @@ -157,7 +164,7 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_defaults, /* Pa */ post_rv, /* Rv */ post_st, /* St */ - post_delim_nb, /* Va */ + post_tag, /* Va */ post_delim_nb, /* Vt */ post_xr, /* Xr */ NULL, /* %A */ @@ -187,11 +194,11 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { NULL, /* Dq */ NULL, /* Ec */ NULL, /* Ef */ - post_delim_nb, /* Em */ + post_em, /* Em */ NULL, /* Eo */ post_xx, /* Fx */ - post_delim_nb, /* Ms */ - NULL, /* No */ + post_tag, /* Ms */ + post_tag, /* No */ post_ns, /* Ns */ post_xx, /* Nx */ post_xx, /* Ox */ @@ -210,7 +217,7 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_delim_nb, /* Sq */ post_sm, /* Sm */ post_sx, /* Sx */ - post_delim_nb, /* Sy */ + post_em, /* Sy */ post_useless, /* Tn */ post_xx, /* Ux */ NULL, /* Xc */ @@ -239,6 +246,7 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { NULL, /* %Q */ NULL, /* %U */ NULL, /* Ta */ + post_tg, /* Tg */ }; #define RSORD_MAX 14 /* Number of `Rs' blocks. */ @@ -286,6 +294,8 @@ static const char * const secnames[SEC__MAX] = { NULL }; +static int fn_prio = TAG_STRONG; + /* Validate the subtree rooted at mdoc->last. */ void @@ -1091,6 +1101,125 @@ post_st(POST_ARGS) } static void +post_tg(POST_ARGS) +{ + struct roff_node *n; /* The .Tg node. */ + struct roff_node *nch; /* The first child of the .Tg node. */ + struct roff_node *nn; /* The next node after the .Tg node. */ + struct roff_node *np; /* The parent of the next node. */ + struct roff_node *nt; /* The TEXT node containing the tag. */ + size_t len; /* The number of bytes in the tag. */ + + /* Find the next node. */ + n = mdoc->last; + for (nn = n; nn != NULL; nn = nn->parent) { + if (nn->next != NULL) { + nn = nn->next; + break; + } + } + + /* Find the tag. */ + nt = nch = n->child; + if (nch == NULL && nn != NULL && nn->child != NULL && + nn->child->type == ROFFT_TEXT) + nt = nn->child; + + /* Validate the tag. */ + if (nt == NULL || *nt->string == '\0') + mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg"); + if (nt == NULL) { + roff_node_delete(mdoc, n); + return; + } + len = strcspn(nt->string, " \t\\"); + if (nt->string[len] != '\0') + mandoc_msg(MANDOCERR_TG_SPC, nt->line, + nt->pos + len, "Tg %s", nt->string); + + /* Keep only the first argument. */ + if (nch != NULL && nch->next != NULL) { + mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line, + nch->next->pos, "Tg ... %s", nch->next->string); + while (nch->next != NULL) + roff_node_delete(mdoc, nch->next); + } + + /* Drop the macro if the first argument is invalid. */ + if (len == 0 || nt->string[len] != '\0') { + roff_node_delete(mdoc, n); + return; + } + + /* By default, tag the .Tg node itself. */ + if (nn == NULL || nn->flags & NODE_ID) + nn = n; + + /* Explicit tagging of specific macros. */ + switch (nn->tok) { + case MDOC_Sh: + case MDOC_Ss: + case MDOC_Fo: + nn = nn->head->child == NULL ? n : nn->head; + break; + case MDOC_It: + np = nn->parent; + while (np->tok != MDOC_Bl) + np = np->parent; + switch (np->norm->Bl.type) { + case LIST_column: + break; + case LIST_diag: + case LIST_hang: + case LIST_inset: + case LIST_ohang: + case LIST_tag: + nn = nn->head; + break; + case LIST_bullet: + case LIST_dash: + case LIST_enum: + case LIST_hyphen: + case LIST_item: + nn = nn->body->child == NULL ? n : nn->body; + break; + default: + abort(); + } + break; + case MDOC_Bd: + case MDOC_Bl: + case MDOC_D1: + case MDOC_Dl: + nn = nn->body->child == NULL ? n : nn->body; + break; + case MDOC_Pp: + break; + case MDOC_Cm: + case MDOC_Dv: + case MDOC_Em: + case MDOC_Er: + case MDOC_Ev: + case MDOC_Fl: + case MDOC_Fn: + case MDOC_Ic: + case MDOC_Li: + case MDOC_Ms: + case MDOC_No: + case MDOC_Sy: + if (nn->child == NULL) + nn = n; + break; + default: + nn = n; + break; + } + tag_put(nt->string, TAG_MANUAL, nn); + if (nn != n) + n->flags |= NODE_NOPRT; +} + +static void post_obsolete(POST_ARGS) { struct roff_node *n; @@ -1182,22 +1311,32 @@ post_bf(POST_ARGS) static void post_fname(POST_ARGS) { - const struct roff_node *n; + struct roff_node *n, *nch; const char *cp; size_t pos; - n = mdoc->last->child; - pos = strcspn(n->string, "()"); - cp = n->string + pos; - if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*'))) - mandoc_msg(MANDOCERR_FN_PAREN, n->line, n->pos + pos, - "%s", n->string); + n = mdoc->last; + nch = n->child; + cp = nch->string; + if (*cp == '(') { + if (cp[strlen(cp + 1)] == ')') + return; + pos = 0; + } else { + pos = strcspn(cp, "()"); + if (cp[pos] == '\0') { + if (n->sec == SEC_DESCRIPTION || + n->sec == SEC_CUSTOM) + tag_put(NULL, fn_prio++, n); + return; + } + } + mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp); } static void post_fn(POST_ARGS) { - post_fname(mdoc); post_fa(mdoc); } @@ -1361,38 +1500,29 @@ post_display(POST_ARGS) static void post_defaults(POST_ARGS) { - struct roff_node *nn; + struct roff_node *n; - if (mdoc->last->child != NULL) { + n = mdoc->last; + if (n->child != NULL) { post_delim_nb(mdoc); return; } - - /* - * The `Ar' defaults to "file ..." if no value is provided as an - * argument; the `Mt' and `Pa' macros use "~"; the `Li' just - * gets an empty string. - */ - - nn = mdoc->last; - switch (nn->tok) { + mdoc->next = ROFF_NEXT_CHILD; + switch (n->tok) { case MDOC_Ar: - mdoc->next = ROFF_NEXT_CHILD; - roff_word_alloc(mdoc, nn->line, nn->pos, "file"); - mdoc->last->flags |= NODE_NOSRC; - roff_word_alloc(mdoc, nn->line, nn->pos, "..."); + roff_word_alloc(mdoc, n->line, n->pos, "file"); mdoc->last->flags |= NODE_NOSRC; + roff_word_alloc(mdoc, n->line, n->pos, "..."); break; case MDOC_Pa: case MDOC_Mt: - mdoc->next = ROFF_NEXT_CHILD; - roff_word_alloc(mdoc, nn->line, nn->pos, "~"); - mdoc->last->flags |= NODE_NOSRC; + roff_word_alloc(mdoc, n->line, n->pos, "~"); break; default: abort(); } - mdoc->last = nn; + mdoc->last->flags |= NODE_NOSRC; + mdoc->last = n; } static void @@ -1446,23 +1576,82 @@ post_an(POST_ARGS) } static void -post_en(POST_ARGS) +post_em(POST_ARGS) { + post_tag(mdoc); + tag_put(NULL, TAG_FALLBACK, mdoc->last); +} +static void +post_en(POST_ARGS) +{ post_obsolete(mdoc); if (mdoc->last->type == ROFFT_BLOCK) mdoc->last->norm->Es = mdoc->last_es; } static void -post_es(POST_ARGS) +post_er(POST_ARGS) { + struct roff_node *n; + + n = mdoc->last; + if (n->sec == SEC_ERRORS && + (n->parent->tok == MDOC_It || + (n->parent->tok == MDOC_Bq && + n->parent->parent->parent->tok == MDOC_It))) + tag_put(NULL, TAG_STRONG, n); + post_delim_nb(mdoc); +} + +static void +post_tag(POST_ARGS) +{ + struct roff_node *n; + + n = mdoc->last; + if ((n->prev == NULL || + (n->prev->type == ROFFT_TEXT && + strcmp(n->prev->string, "|") == 0)) && + (n->parent->tok == MDOC_It || + (n->parent->tok == MDOC_Xo && + n->parent->parent->prev == NULL && + n->parent->parent->parent->tok == MDOC_It))) + tag_put(NULL, TAG_STRONG, n); + post_delim_nb(mdoc); +} +static void +post_es(POST_ARGS) +{ post_obsolete(mdoc); mdoc->last_es = mdoc->last; } static void +post_fl(POST_ARGS) +{ + struct roff_node *n; + char *cp; + + /* + * Transform ".Fl Fl long" to ".Fl \-long", + * resulting for example in better HTML output. + */ + + n = mdoc->last; + if (n->prev != NULL && n->prev->tok == MDOC_Fl && + n->prev->child == NULL && n->child != NULL && + (n->flags & NODE_LINE) == 0) { + mandoc_asprintf(&cp, "\\-%s", n->child->string); + free(n->child->string); + n->child->string = cp; + roff_node_delete(mdoc, n->prev); + } + post_tag(mdoc); +} + +static void post_xx(POST_ARGS) { struct roff_node *n; @@ -1554,8 +1743,8 @@ post_it(POST_ARGS) if ((nch = nit->head->child) != NULL) mandoc_msg(MANDOCERR_ARG_SKIP, nit->line, nit->pos, "It %s", - nch->string == NULL ? roff_name[nch->tok] : - nch->string); + nch->type == ROFFT_TEXT ? nch->string : + roff_name[nch->tok]); break; case LIST_column: cols = (int)nbl->norm->Bl.ncols; @@ -1718,8 +1907,7 @@ post_bl_head(POST_ARGS) static void post_bl(POST_ARGS) { - struct roff_node *nparent, *nprev; /* of the Bl block */ - struct roff_node *nblock, *nbody; /* of the Bl */ + struct roff_node *nbody; /* of the Bl */ struct roff_node *nchild, *nnext; /* of the Bl body */ const char *prev_Er; int order; @@ -1740,88 +1928,73 @@ post_bl(POST_ARGS) if (nbody->end != ENDBODY_NOT) return; - nchild = nbody->child; - if (nchild == NULL) { - mandoc_msg(MANDOCERR_BLK_EMPTY, - nbody->line, nbody->pos, "Bl"); - return; + /* + * Up to the first item, move nodes before the list, + * but leave transparent nodes where they are + * if they precede an item. + * The next non-transparent node is kept in nchild. + * It only needs to be updated after a non-transparent + * node was moved out, and at the very beginning + * when no node at all was moved yet. + */ + + nchild = mdoc->last; + for (;;) { + if (nchild == mdoc->last) + nchild = roff_node_child(nbody); + if (nchild == NULL) { + mdoc->last = nbody; + mandoc_msg(MANDOCERR_BLK_EMPTY, + nbody->line, nbody->pos, "Bl"); + return; + } + if (nchild->tok == MDOC_It) { + mdoc->last = nbody; + break; + } + mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line, + nbody->child->pos, "%s", roff_name[nbody->child->tok]); + if (nbody->parent->prev == NULL) { + mdoc->last = nbody->parent->parent; + mdoc->next = ROFF_NEXT_CHILD; + } else { + mdoc->last = nbody->parent->prev; + mdoc->next = ROFF_NEXT_SIBLING; + } + roff_node_relink(mdoc, nbody->child); } + + /* + * We have reached the first item, + * so moving nodes out is no longer possible. + * But in .Bl -column, the first rows may be implicit, + * that is, they may not start with .It macros. + * Such rows may be followed by nodes generated on the + * roff level, for example .TS. + * Wrap such roff nodes into an implicit row. + */ + while (nchild != NULL) { - nnext = nchild->next; - if (nchild->tok == MDOC_It || - (nchild->tok == MDOC_Sm && - nnext != NULL && nnext->tok == MDOC_It)) { - nchild = nnext; + if (nchild->tok == MDOC_It) { + nchild = roff_node_next(nchild); continue; } - - /* - * In .Bl -column, the first rows may be implicit, - * that is, they may not start with .It macros. - * Such rows may be followed by nodes generated on the - * roff level, for example .TS, which cannot be moved - * out of the list. In that case, wrap such roff nodes - * into an implicit row. - */ - - if (nchild->prev != NULL) { - mdoc->last = nchild; - mdoc->next = ROFF_NEXT_SIBLING; - roff_block_alloc(mdoc, nchild->line, - nchild->pos, MDOC_It); - roff_head_alloc(mdoc, nchild->line, - nchild->pos, MDOC_It); + nnext = nchild->next; + mdoc->last = nchild->prev; + mdoc->next = ROFF_NEXT_SIBLING; + roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); + roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); + mdoc->next = ROFF_NEXT_SIBLING; + roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); + while (nchild->tok != MDOC_It) { + roff_node_relink(mdoc, nchild); + if (nnext == NULL) + break; + nchild = nnext; + nnext = nchild->next; mdoc->next = ROFF_NEXT_SIBLING; - roff_body_alloc(mdoc, nchild->line, - nchild->pos, MDOC_It); - while (nchild->tok != MDOC_It) { - roff_node_relink(mdoc, nchild); - if ((nchild = nnext) == NULL) - break; - nnext = nchild->next; - mdoc->next = ROFF_NEXT_SIBLING; - } - mdoc->last = nbody; - continue; } - - mandoc_msg(MANDOCERR_BL_MOVE, nchild->line, nchild->pos, - "%s", roff_name[nchild->tok]); - - /* - * Move the node out of the Bl block. - * First, collect all required node pointers. - */ - - nblock = nbody->parent; - nprev = nblock->prev; - nparent = nblock->parent; - - /* - * Unlink this child. - */ - - nbody->child = nnext; - if (nnext == NULL) - nbody->last = NULL; - else - nnext->prev = NULL; - - /* - * Relink this child. - */ - - nchild->parent = nparent; - nchild->prev = nprev; - nchild->next = nblock; - - nblock->prev = nchild; - if (nprev == NULL) - nparent->child = nchild; - else - nprev->next = nchild; - - nchild = nnext; + mdoc->last = nbody; } if (mdoc->meta.os_e != MANDOC_OS_NETBSD) @@ -1904,8 +2077,7 @@ post_root(POST_ARGS) /* Add missing prologue data. */ if (mdoc->meta.date == NULL) - mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : - mandoc_normdate(mdoc, NULL, 0, 0); + mdoc->meta.date = mandoc_normdate(NULL, NULL); if (mdoc->meta.title == NULL) { mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF"); @@ -2050,10 +2222,11 @@ post_rs(POST_ARGS) static void post_hyph(POST_ARGS) { - struct roff_node *nch; + struct roff_node *n, *nch; char *cp; - for (nch = mdoc->last->child; nch != NULL; nch = nch->next) { + n = mdoc->last; + for (nch = n->child; nch != NULL; nch = nch->next) { if (nch->type != ROFFT_TEXT) continue; cp = nch->string; @@ -2062,8 +2235,11 @@ post_hyph(POST_ARGS) while (*(++cp) != '\0') if (*cp == '-' && isalpha((unsigned char)cp[-1]) && - isalpha((unsigned char)cp[1])) + isalpha((unsigned char)cp[1])) { + if (n->tag == NULL && n->flags & NODE_ID) + n->tag = mandoc_strdup(nch->string); *cp = ASCII_HYPH; + } } } @@ -2088,8 +2264,7 @@ post_sx(POST_ARGS) static void post_sh(POST_ARGS) { - - post_ignpar(mdoc); + post_section(mdoc); switch (mdoc->last->type) { case ROFFT_HEAD: @@ -2320,6 +2495,8 @@ post_sh_head(POST_ARGS) roff_setreg(mdoc->roff, "nS", 0, '='); mdoc->flags &= ~MDOC_SYNOPSIS; } + if (sec == SEC_DESCRIPTION) + fn_prio = TAG_STRONG; /* Mark our last section. */ @@ -2427,15 +2604,31 @@ post_xr(POST_ARGS) } static void -post_ignpar(POST_ARGS) +post_section(POST_ARGS) { - struct roff_node *np; + struct roff_node *n, *nch; + char *cp, *tag; - switch (mdoc->last->type) { + n = mdoc->last; + switch (n->type) { case ROFFT_BLOCK: post_prevpar(mdoc); return; case ROFFT_HEAD: + tag = NULL; + deroff(&tag, n); + if (tag != NULL) { + for (cp = tag; *cp != '\0'; cp++) + if (*cp == ' ') + *cp = '_'; + if ((nch = n->child) != NULL && + nch->type == ROFFT_TEXT && + strcmp(nch->string, tag) == 0) + tag_put(NULL, TAG_STRONG, n); + else + tag_put(tag, TAG_FALLBACK, n); + free(tag); + } post_delim(mdoc); post_hyph(mdoc); return; @@ -2444,42 +2637,40 @@ post_ignpar(POST_ARGS) default: return; } - - if ((np = mdoc->last->child) != NULL) - 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 == 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); - } + if ((nch = n->child) != NULL && + (nch->tok == MDOC_Pp || nch->tok == ROFF_br || + nch->tok == ROFF_sp)) { + mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, + "%s after %s", roff_name[nch->tok], + roff_name[n->tok]); + roff_node_delete(mdoc, nch); + } + if ((nch = n->last) != NULL && + (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) { + mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, + "%s at the end of %s", roff_name[nch->tok], + roff_name[n->tok]); + roff_node_delete(mdoc, nch); + } } static void post_prevpar(POST_ARGS) { - struct roff_node *n; + struct roff_node *n, *np; n = mdoc->last; - if (NULL == n->prev) - return; if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) return; + if ((np = roff_node_prev(n)) == NULL) + return; /* * 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 != ROFF_br) + if (np->tok != MDOC_Pp && np->tok != ROFF_br) return; if (n->tok == MDOC_Bl && n->norm->Bl.comp) return; @@ -2488,9 +2679,9 @@ post_prevpar(POST_ARGS) if (n->tok == MDOC_It && n->parent->norm->Bl.comp) return; - 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); + mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, + "%s before %s", roff_name[np->tok], roff_name[n->tok]); + roff_node_delete(mdoc, np); } static void @@ -2498,6 +2689,7 @@ post_par(POST_ARGS) { struct roff_node *np; + fn_prio = TAG_STRONG; post_prevpar(mdoc); np = mdoc->last; @@ -2510,7 +2702,6 @@ static void post_dd(POST_ARGS) { struct roff_node *n; - char *datestr; n = mdoc->last; n->flags |= NODE_NOPRT; @@ -2527,21 +2718,10 @@ post_dd(POST_ARGS) mandoc_msg(MANDOCERR_PROLOG_ORDER, n->line, n->pos, "Dd after Os"); - if (n->child == NULL || n->child->string[0] == '\0') { - mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : - mandoc_normdate(mdoc, NULL, n->line, n->pos); - return; - } - - datestr = NULL; - deroff(&datestr, n); - if (mdoc->quick) - mdoc->meta.date = datestr; - else { - mdoc->meta.date = mandoc_normdate(mdoc, - datestr, n->line, n->pos); - free(datestr); - } + if (mdoc->quick && n != NULL) + mdoc->meta.date = mandoc_strdup(""); + else + mdoc->meta.date = mandoc_normdate(n->child, n); } static void @@ -2616,8 +2796,14 @@ post_dt(POST_ARGS) mandoc_msg(MANDOCERR_MSEC_BAD, nn->line, nn->pos, "Dt ... %s", nn->string); mdoc->meta.vol = mandoc_strdup(nn->string); - } else + } else { mdoc->meta.vol = mandoc_strdup(cp); + if (mdoc->filesec != '\0' && + mdoc->filesec != *nn->string && + *nn->string >= '1' && *nn->string <= '9') + mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos, + "*.%c vs Dt ... %c", mdoc->filesec, *nn->string); + } /* Optional third argument: architecture. */ diff --git a/usr/src/cmd/mandoc/out.c b/usr/src/cmd/mandoc/out.c index d0b0d0a2ac..12333e38ff 100644 --- a/usr/src/cmd/mandoc/out.c +++ b/usr/src/cmd/mandoc/out.c @@ -1,7 +1,8 @@ -/* $Id: out.c,v 1.78 2019/03/29 21:27:06 schwarze Exp $ */ +/* $Id: out.c,v 1.82 2021/09/07 17:07:58 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2014, 2015, 2017, 2018, 2019, 2021 + * 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 @@ -22,11 +23,13 @@ #include <assert.h> #include <ctype.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "mandoc_aux.h" +#include "mandoc.h" #include "tbl.h" #include "out.h" @@ -120,7 +123,6 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, const struct tbl_dat *dp; struct roffcol *col; 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; @@ -209,13 +211,25 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, } /* - * Column spacings are needed for span width calculations, - * so set the default values now. + * The minimum width of columns explicitly specified + * in the layout is 1n. */ - for (icol = 0; icol <= maxcol; icol++) - if (tbl->cols[icol].spacing == SIZE_MAX || icol == maxcol) - tbl->cols[icol].spacing = 3; + if (maxcol < sp_first->opts->cols - 1) + maxcol = sp_first->opts->cols - 1; + for (icol = 0; icol <= maxcol; icol++) { + col = tbl->cols + icol; + if (col->width < 1) + col->width = 1; + + /* + * Column spacings are needed for span width + * calculations, so set the default values now. + */ + + if (col->spacing == SIZE_MAX || icol == maxcol) + col->spacing = 3; + } /* * Replace the minimum widths with the missing widths, @@ -242,33 +256,21 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, 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]) { + width = tbl->cols[icol].width; + if (min1 > width) { min2 = min1; - min1 = colwidth[icol]; - } else if (min1 < colwidth[icol] && - min2 > colwidth[icol]) - min2 = colwidth[icol]; + min1 = width; + } else if (min1 < width && min2 > width) + min2 = width; } /* @@ -290,26 +292,22 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, 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. */ + /* Record the effect of the widening. */ 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) + if (tbl->cols[icol].width != min1) continue; if (g->wanted <= wanted - min1) { + tbl->cols[icol].width += g->wanted; done = 1; break; } + tbl->cols[icol].width = wanted; g->wanted -= wanted - min1; } if (done) { @@ -318,14 +316,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, } 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. @@ -340,8 +331,6 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, col = tbl->cols + icol; 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) @@ -549,5 +538,7 @@ tblcalc_number(struct rofftbl *tbl, struct roffcol *col, if (totsz > col->nwidth) col->nwidth = totsz; + if (col->nwidth > col->width) + col->width = col->nwidth; return totsz; } diff --git a/usr/src/cmd/mandoc/out.h b/usr/src/cmd/mandoc/out.h index dec6a8f873..e621cbb7b9 100644 --- a/usr/src/cmd/mandoc/out.h +++ b/usr/src/cmd/mandoc/out.h @@ -1,4 +1,4 @@ -/* $Id: out.h,v 1.33 2018/08/18 20:18:14 schwarze Exp $ */ +/* $Id: out.h,v 1.34 2020/04/03 11:35:01 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> @@ -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. + * + * Utilities for use by multiple mandoc(1) formatters. */ enum roffscale { @@ -64,5 +66,5 @@ struct rofftbl { struct tbl_span; const char *a2roffsu(const char *, struct roffsu *, enum roffscale); -void tblcalc(struct rofftbl *tbl, +void tblcalc(struct rofftbl *, const struct tbl_span *, size_t, size_t); diff --git a/usr/src/cmd/mandoc/read.c b/usr/src/cmd/mandoc/read.c index 0f25ab967c..5b33edbe9c 100644 --- a/usr/src/cmd/mandoc/read.c +++ b/usr/src/cmd/mandoc/read.c @@ -1,7 +1,7 @@ -/* $Id: read.c,v 1.213 2019/06/03 19:58:02 schwarze Exp $ */ +/* $Id: read.c,v 1.220 2021/06/27 17:57:54 schwarze Exp $ */ /* + * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -15,6 +15,12 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Top-level functions of the mandoc(3) parser: + * Parser and input encoding selection, decompression, + * handling of input bytes, characters, lines, and files, + * handling of roff(7) loops and file inclusion, + * and steering of the various parsers. */ #include "config.h" @@ -41,6 +47,7 @@ #include "mandoc_parse.h" #include "libmandoc.h" #include "roff_int.h" +#include "tag.h" #define REPARSE_LIMIT 1000 @@ -147,6 +154,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) struct buf *firstln, *lastln, *thisln, *loop; char *cp; size_t pos; /* byte number in the ln buffer */ + size_t spos; /* at the start of the current line parse */ int line_result, result; int of; int lnn; /* line number in the real file */ @@ -173,6 +181,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) curp->filenc & MPARSE_LATIN1) curp->filenc = preconv_cue(&blk, i); } + spos = pos; while (i < blk.sz && (start || blk.buf[i] != '\0')) { @@ -255,6 +264,8 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) /* XXX Ugly hack to mark the end of the input. */ if (i == blk.sz || blk.buf[i] == '\0') { + if (pos + 2 > ln.sz) + resize_buf(&ln, 256); ln.buf[pos++] = '\n'; ln.buf[pos] = '\0'; } @@ -270,7 +281,8 @@ 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); + line_result = roff_parseln(curp->roff, curp->line, + &ln, &of, start && spos == 0 ? pos : 0); /* Process options. */ @@ -429,9 +441,8 @@ read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) int gzerrnum, retval; if (fstat(fd, &st) == -1) { - mandoc_msg(MANDOCERR_FILE, 0, 0, - "fstat: %s", strerror(errno)); - return 0; + mandoc_msg(MANDOCERR_FSTAT, 0, 0, "%s", strerror(errno)); + return -1; } /* @@ -444,13 +455,13 @@ read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) if (curp->gzip == 0 && S_ISREG(st.st_mode)) { if (st.st_size > 0x7fffffff) { mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL); - return 0; + return -1; } *with_mmap = 1; fb->sz = (size_t)st.st_size; fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0); if (fb->buf != MAP_FAILED) - return 1; + return 0; } if (curp->gzip) { @@ -462,15 +473,15 @@ read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) * which this function must not do. */ if ((fd = dup(fd)) == -1) { - mandoc_msg(MANDOCERR_FILE, 0, 0, - "dup: %s", strerror(errno)); - return 0; + mandoc_msg(MANDOCERR_DUP, 0, 0, + "%s", strerror(errno)); + return -1; } if ((gz = gzdopen(fd, "rb")) == NULL) { - mandoc_msg(MANDOCERR_FILE, 0, 0, - "gzdopen: %s", strerror(errno)); + mandoc_msg(MANDOCERR_GZDOPEN, 0, 0, + "%s", strerror(errno)); close(fd); - return 0; + return -1; } } else gz = NULL; @@ -482,7 +493,7 @@ read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) *with_mmap = 0; off = 0; - retval = 0; + retval = -1; fb->sz = 0; fb->buf = NULL; for (;;) { @@ -498,13 +509,13 @@ read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) read(fd, fb->buf + (int)off, fb->sz - off); if (ssz == 0) { fb->sz = off; - retval = 1; + retval = 0; break; } if (ssz == -1) { if (curp->gzip) (void)gzerror(gz, &gzerrnum); - mandoc_msg(MANDOCERR_FILE, 0, 0, "read: %s", + mandoc_msg(MANDOCERR_READ, 0, 0, "%s", curp->gzip && gzerrnum != Z_ERRNO ? zError(gzerrnum) : strerror(errno)); break; @@ -513,10 +524,10 @@ read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) } if (curp->gzip && (gzerrnum = gzclose(gz)) != Z_OK) - mandoc_msg(MANDOCERR_FILE, 0, 0, "gzclose: %s", + mandoc_msg(MANDOCERR_GZCLOSE, 0, 0, "%s", gzerrnum == Z_ERRNO ? strerror(errno) : zError(gzerrnum)); - if (retval == 0) { + if (retval == -1) { free(fb->buf); fb->buf = NULL; } @@ -546,7 +557,7 @@ mparse_readfd(struct mparse *curp, int fd, const char *filename) struct buf blk; struct buf *save_primary; - const char *save_filename; + const char *save_filename, *cp; size_t offset; int save_filenc, save_lineno; int with_mmap; @@ -554,8 +565,14 @@ mparse_readfd(struct mparse *curp, int fd, const char *filename) if (recursion_depth > 64) { mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, 0, NULL); return; - } - if (read_whole_file(curp, fd, &blk, &with_mmap) == 0) + } else if (recursion_depth == 0 && + (cp = strrchr(filename, '.')) != NULL && + cp[1] >= '1' && cp[1] <= '9') + curp->man->filesec = cp[1]; + else + curp->man->filesec = '\0'; + + if (read_whole_file(curp, fd, &blk, &with_mmap) == -1) return; /* @@ -663,22 +680,26 @@ mparse_alloc(int options, enum mandoc_os os_e, const char *os_s) } curp->man->meta.first->tok = TOKEN_NONE; curp->man->meta.os_e = os_e; + tag_alloc(); return curp; } void mparse_reset(struct mparse *curp) { + tag_free(); roff_reset(curp->roff); roff_man_reset(curp->man); free_buf_list(curp->secondary); curp->secondary = NULL; curp->gzip = 0; + tag_alloc(); } void mparse_free(struct mparse *curp) { + tag_free(); roffhash_free(curp->man->mdocmac); roffhash_free(curp->man->manmac); roff_man_free(curp->man); @@ -696,6 +717,7 @@ mparse_result(struct mparse *curp) mdoc_validate(curp->man); else man_validate(curp->man); + tag_postprocess(curp->man, curp->man->meta.first); } return &curp->man->meta; } diff --git a/usr/src/cmd/mandoc/roff.c b/usr/src/cmd/mandoc/roff.c index e84295aac3..de75a260f5 100644 --- a/usr/src/cmd/mandoc/roff.c +++ b/usr/src/cmd/mandoc/roff.c @@ -1,7 +1,7 @@ -/* $Id: roff.c,v 1.363 2019/02/06 21:11:43 schwarze Exp $ */ +/* $Id: roff.c,v 1.378 2021/08/10 12:55:04 schwarze Exp $ */ /* + * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -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. + * + * Implementation of the roff(7) parser for mandoc(1). */ #include "config.h" @@ -133,15 +135,18 @@ struct roff { char escape; /* escape character */ }; +/* + * A macro definition, condition, or ignored block. + */ struct roffnode { enum roff_tok tok; /* type of node */ struct roffnode *parent; /* up one in stack */ int line; /* parse line */ int col; /* parse col */ char *name; /* node name, e.g. macro name */ - char *end; /* end-rules: custom token */ - int endspan; /* end-rules: next-line or infty */ - int rule; /* current evaluation rule */ + char *end; /* custom end macro of the block */ + int endspan; /* scope to: 1=eol 2=next line -1=\} */ + int rule; /* content is: 1=evaluated 0=skipped */ }; #define ROFF_ARGS struct roff *r, /* parse ctx */ \ @@ -181,18 +186,20 @@ 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_break(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_checkend(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_evalcond(struct roff *, int, char *, int *); static int roff_evalnum(struct roff *, int, const char *, int *, int *, int); static int roff_evalpar(struct roff *, int, @@ -351,7 +358,7 @@ const char *__roff_name[MAN_MAX + 1] = { "Lk", "Mt", "Brq", "Bro", "Brc", "%C", "Es", "En", "Dx", "%Q", "%U", "Ta", - NULL, + "Tg", NULL, "TH", "SH", "SS", "TP", "TQ", "LP", "PP", "P", "IP", @@ -400,7 +407,7 @@ static struct roffmac roffs[TOKEN_NONE] = { { roff_unsupp, NULL, NULL, 0 }, /* boxa */ { roff_line_ignore, NULL, NULL, 0 }, /* bp */ { roff_unsupp, NULL, NULL, 0 }, /* BP */ - { roff_unsupp, NULL, NULL, 0 }, /* break */ + { roff_break, NULL, NULL, 0 }, /* break */ { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */ { roff_line_ignore, NULL, NULL, 0 }, /* brnl */ { roff_noarg, NULL, NULL, 0 }, /* brp */ @@ -685,7 +692,7 @@ roffhash_find(struct ohash *htab, const char *name, size_t sz) /* * Pop the current node off of the stack of roff instructions currently - * pending. + * pending. Return 1 if it is a loop or 0 otherwise. */ static int roffnode_pop(struct roff *r) @@ -767,6 +774,7 @@ void roff_reset(struct roff *r) { roff_free1(r); + r->options |= MPARSE_COMMENT; r->format = r->options & (MPARSE_MDOC | MPARSE_MAN); r->control = '\0'; r->escape = '\\'; @@ -779,7 +787,7 @@ roff_reset(struct roff *r) void roff_free(struct roff *r) { - int i; + int i; roff_free1(r); for (i = 0; i < r->mstacksz; i++) @@ -796,7 +804,7 @@ roff_alloc(int options) r = mandoc_calloc(1, sizeof(struct roff)); r->reqtab = roffhash_alloc(0, ROFF_RENAMED); - r->options = options; + r->options = options | MPARSE_COMMENT; r->format = options & (MPARSE_MDOC | MPARSE_MAN); r->mstackpos = -1; r->rstackpos = -1; @@ -1096,6 +1104,7 @@ roff_node_free(struct roff_node *n) free(n->norm); eqn_box_free(n->eqn); free(n->string); + free(n->tag); free(n); } @@ -1109,13 +1118,72 @@ roff_node_delete(struct roff_man *man, struct roff_node *n) roff_node_free(n); } +int +roff_node_transparent(struct roff_node *n) +{ + if (n == NULL) + return 0; + if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) + return 1; + return roff_tok_transparent(n->tok); +} + +int +roff_tok_transparent(enum roff_tok tok) +{ + switch (tok) { + case ROFF_ft: + case ROFF_ll: + case ROFF_mc: + case ROFF_po: + case ROFF_ta: + case MDOC_Db: + case MDOC_Es: + case MDOC_Sm: + case MDOC_Tg: + case MAN_DT: + case MAN_UC: + case MAN_PD: + case MAN_AT: + return 1; + default: + return 0; + } +} + +struct roff_node * +roff_node_child(struct roff_node *n) +{ + for (n = n->child; roff_node_transparent(n); n = n->next) + continue; + return n; +} + +struct roff_node * +roff_node_prev(struct roff_node *n) +{ + do { + n = n->prev; + } while (roff_node_transparent(n)); + return n; +} + +struct roff_node * +roff_node_next(struct roff_node *n) +{ + do { + n = n->next; + } while (roff_node_transparent(n)); + return n; +} + void deroff(char **dest, const struct roff_node *n) { char *cp; size_t sz; - if (n->type != ROFFT_TEXT) { + if (n->string == NULL) { for (n = n->child; n != NULL; n = n->next) deroff(dest, n); return; @@ -1242,7 +1310,7 @@ roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char newesc) * in the syntax tree. */ - if (newesc != ASCII_ESC && r->format == 0) { + if (newesc != ASCII_ESC && r->options & MPARSE_COMMENT) { while (*ep == ' ' || *ep == '\t') ep--; ep[1] = '\0'; @@ -1755,7 +1823,7 @@ roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs) } int -roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) +roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs, size_t len) { enum roff_tok t; int e; @@ -1766,6 +1834,14 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) ppos = pos = *offs; + if (len > 80 && r->tbl == NULL && r->eqn == NULL && + (r->man->flags & ROFF_NOFILL) == 0 && + strchr(" .\\", buf->buf[pos]) == NULL && + buf->buf[pos] != r->control && + strcspn(buf->buf, " ") < 80) + mandoc_msg(MANDOCERR_TEXT_LONG, ln, (int)len - 1, + "%.20s...", buf->buf + pos); + /* Handle in-line equation delimiters. */ if (r->tbl == NULL && @@ -1811,8 +1887,10 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) roff_addtbl(r->man, ln, r->tbl); return e; } - if ( ! ctl) + if ( ! ctl) { + r->options &= ~MPARSE_COMMENT; return roff_parsetext(r, buf, pos, offs) | e; + } /* Skip empty request lines. */ @@ -1835,6 +1913,7 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) /* No scope is open. This is a new request or macro. */ + r->options &= ~MPARSE_COMMENT; spos = pos; t = roff_parse(r, buf->buf, &pos, ln, ppos); @@ -1964,14 +2043,13 @@ roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos) /* --- handling of request blocks ----------------------------------------- */ +/* + * Close a macro definition block or an "ignore" block. + */ static int roff_cblock(ROFF_ARGS) { - - /* - * A block-close `..' should only be invoked as a child of an - * ignore macro, otherwise raise a warning and just ignore it. - */ + int rr; if (r->last == NULL) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); @@ -1980,35 +2058,51 @@ roff_cblock(ROFF_ARGS) switch (r->last->tok) { case ROFF_am: - /* ROFF_am1 is remapped to ROFF_am in roff_block(). */ case ROFF_ami: case ROFF_de: - /* ROFF_de1 is remapped to ROFF_de in roff_block(). */ case ROFF_dei: case ROFF_ig: break; + case ROFF_am1: + case ROFF_de1: + /* Remapped in roff_block(). */ + abort(); default: mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); return ROFF_IGN; } + roffnode_pop(r); + roffnode_cleanscope(r); + + /* + * If a conditional block with braces is still open, + * check for "\}" block end markers. + */ + + if (r->last != NULL && r->last->endspan < 0) { + rr = 1; /* If arguments follow "\}", warn about them. */ + roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr); + } + if (buf->buf[pos] != '\0') mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, ".. %s", buf->buf + pos); - roffnode_pop(r); - roffnode_cleanscope(r); return ROFF_IGN; - } +/* + * Pop all nodes ending at the end of the current input line. + * Return the number of loops ended. + */ static int roffnode_cleanscope(struct roff *r) { int inloop; inloop = 0; - while (r->last != NULL) { + while (r->last != NULL && r->last->endspan > 0) { if (--r->last->endspan != 0) break; inloop += roffnode_pop(r); @@ -2016,6 +2110,11 @@ roffnode_cleanscope(struct roff *r) return inloop; } +/* + * Handle the closing "\}" of a conditional block. + * Apart from generating warnings, this only pops nodes. + * Return the number of loops ended. + */ static int roff_ccond(struct roff *r, int ln, int ppos) { @@ -2232,12 +2331,20 @@ roff_block_text(ROFF_ARGS) return ROFF_IGN; } +/* + * Check for a closing "\}" and handle it. + * In this function, the final "int *offs" argument is used for + * different purposes than elsewhere: + * Input: *offs == 0: caller wants to discard arguments following \} + * *offs == 1: caller wants to preserve text following \} + * Output: *offs = 0: tell caller to discard input line + * *offs = 1: tell caller to use input line + */ static int -roff_cond_sub(ROFF_ARGS) +roff_cond_checkend(ROFF_ARGS) { char *ep; int endloop, irc, rr; - enum roff_tok t; irc = ROFF_IGN; rr = r->last->rule; @@ -2247,23 +2354,28 @@ roff_cond_sub(ROFF_ARGS) irc |= endloop; /* - * If `\}' occurs on a macro line without a preceding macro, - * drop the line completely. + * If "\}" occurs on a macro line without a preceding macro or + * a text line contains nothing else, drop the line completely. */ ep = buf->buf + pos; - if (ep[0] == '\\' && ep[1] == '}') + if (ep[0] == '\\' && ep[1] == '}' && (ep[2] == '\0' || *offs == 0)) rr = 0; /* - * The closing delimiter `\}' rewinds the conditional scope + * 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); + if (ep[2] == '\0') + ep[0] = '\0'; + else if (rr) + ep[1] = '&'; + else + memmove(ep, ep + 2, strlen(ep + 2) + 1); if (roff_ccond(r, ln, ep - buf->buf)) irc |= endloop; break; @@ -2275,61 +2387,68 @@ roff_cond_sub(ROFF_ARGS) break; } } - - /* - * Fully handle known macros when they are structurally - * required or when the conditional evaluated to true. - */ - - t = roff_parse(r, buf->buf, &pos, ln, ppos); - 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; + *offs = rr; return irc; } +/* + * Parse and process a request or macro line in conditional scope. + */ static int -roff_cond_text(ROFF_ARGS) +roff_cond_sub(ROFF_ARGS) { - char *ep; - int endloop, irc, rr; + struct roffnode *bl; + int irc, rr; + enum roff_tok t; - irc = ROFF_IGN; - rr = r->last->rule; - endloop = tok != ROFF_while ? ROFF_IGN : - rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT; - if (roffnode_cleanscope(r)) - irc |= endloop; + rr = 0; /* If arguments follow "\}", skip them. */ + irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr); + t = roff_parse(r, buf->buf, &pos, ln, ppos); - /* - * If `\}' occurs on a text line with neither preceding - * nor following characters, drop the line completely. - */ + /* For now, let high level macros abort .ce mode. */ - ep = buf->buf + pos; - if (strcmp(ep, "\\}") == 0) - rr = 0; + if (roffce_node != NULL && + (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ || + t == ROFF_TH || t == ROFF_TS)) { + r->man->last = roffce_node; + r->man->next = ROFF_NEXT_SIBLING; + roffce_lines = 0; + roffce_node = NULL; + } /* - * The closing delimiter `\}' rewinds the conditional scope - * but is otherwise ignored when interpreting the line. + * Fully handle known macros when they are structurally + * required or when the conditional evaluated to true. */ - while ((ep = strchr(ep, '\\')) != NULL) { - 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; + if (t == ROFF_break) { + if (irc & ROFF_LOOPMASK) + irc = ROFF_IGN | ROFF_LOOPEXIT; + else if (rr) { + for (bl = r->last; bl != NULL; bl = bl->parent) { + bl->rule = 0; + if (bl->tok == ROFF_while) + break; + } } - } + } else if (t != TOKEN_NONE && + (rr || roffs[t].flags & ROFFMAC_STRUCT)) + irc |= (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); + else + irc |= rr ? ROFF_CONT : ROFF_IGN; + return irc; +} + +/* + * Parse and process a text line in conditional scope. + */ +static int +roff_cond_text(ROFF_ARGS) +{ + int irc, rr; + + rr = 1; /* If arguments follow "\}", preserve them. */ + irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr); if (rr) irc |= ROFF_CONT; return irc; @@ -3482,6 +3601,17 @@ roff_als(ROFF_ARGS) return ROFF_IGN; } +/* + * The .break request only makes sense inside conditionals, + * and that case is already handled in roff_cond_sub(). + */ +static int +roff_break(ROFF_ARGS) +{ + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break"); + return ROFF_IGN; +} + static int roff_cc(ROFF_ARGS) { @@ -3537,7 +3667,9 @@ roff_char(ROFF_ARGS) case ESCAPE_FONTITALIC: case ESCAPE_FONTBOLD: case ESCAPE_FONTBI: - case ESCAPE_FONTCW: + case ESCAPE_FONTCR: + case ESCAPE_FONTCB: + case ESCAPE_FONTCI: case ESCAPE_FONTPREV: font++; break; @@ -3804,6 +3936,11 @@ roff_userdef(ROFF_ARGS) char *arg, *ap, *dst, *src; size_t sz; + /* If the macro is empty, ignore it altogether. */ + + if (*r->current_string == '\0') + return ROFF_IGN; + /* Initialize a new macro stack context. */ if (++r->mstackpos == r->mstacksz) { @@ -3851,7 +3988,7 @@ roff_userdef(ROFF_ARGS) buf->sz = strlen(buf->buf) + 1; *offs = 0; - return buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ? + return buf->buf[buf->sz - 2] == '\n' ? ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND; } diff --git a/usr/src/cmd/mandoc/roff.h b/usr/src/cmd/mandoc/roff.h index 49b0927513..2933eb9c0b 100644 --- a/usr/src/cmd/mandoc/roff.h +++ b/usr/src/cmd/mandoc/roff.h @@ -1,7 +1,7 @@ -/* $Id: roff.h,v 1.69 2019/03/04 13:01:57 schwarze Exp $ */ +/* $Id: roff.h,v 1.74 2020/04/08 11:56:03 schwarze Exp $ */ /* + * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -437,6 +437,7 @@ enum roff_tok { MDOC__Q, MDOC__U, MDOC_Ta, + MDOC_Tg, MDOC_MAX, MAN_TH, MAN_SH, @@ -505,6 +506,7 @@ struct roff_node { struct mdoc_arg *args; /* BLOCK/ELEM */ union mdoc_data *norm; /* Normalized arguments. */ char *string; /* TEXT */ + char *tag; /* For less(1) :t and HTML id=. */ struct tbl_span *span; /* TBL */ struct eqn_box *eqn; /* EQN */ int line; /* Input file line number. */ @@ -521,6 +523,8 @@ struct roff_node { #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. */ +#define NODE_ID (1 << 11) /* Target for deep linking. */ +#define NODE_HREF (1 << 12) /* Link to another place in this page. */ int prev_font; /* Before entering this node. */ int aux; /* Decoded node data, type-dependent. */ enum roff_tok tok; /* Request or macro ID. */ @@ -548,5 +552,10 @@ struct roff_meta { extern const char *const *roff_name; -int arch_valid(const char *, enum mandoc_os); -void deroff(char **, const struct roff_node *); +int arch_valid(const char *, enum mandoc_os); +void deroff(char **, const struct roff_node *); +struct roff_node *roff_node_child(struct roff_node *); +struct roff_node *roff_node_next(struct roff_node *); +struct roff_node *roff_node_prev(struct roff_node *); +int roff_node_transparent(struct roff_node *); +int roff_tok_transparent(enum roff_tok); diff --git a/usr/src/cmd/mandoc/roff_html.c b/usr/src/cmd/mandoc/roff_html.c index 02b8beea3b..3cc7c19a8a 100644 --- a/usr/src/cmd/mandoc/roff_html.c +++ b/usr/src/cmd/mandoc/roff_html.c @@ -1,4 +1,4 @@ -/* $Id: roff_html.c,v 1.19 2019/01/07 07:26:29 schwarze Exp $ */ +/* $Id: roff_html.c,v 1.21 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2017, 2018, 2019 Ingo Schwarze <schwarze@openbsd.org> @@ -15,6 +15,8 @@ * 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 <sys/types.h> #include <assert.h> @@ -94,7 +96,7 @@ roff_html_pre_ft(ROFF_HTML_ARGS) const char *cp; cp = n->child->string; - print_metaf(h, mandoc_font(cp, (int)strlen(cp))); + html_setfont(h, mandoc_font(cp, (int)strlen(cp))); } static void diff --git a/usr/src/cmd/mandoc/roff_int.h b/usr/src/cmd/mandoc/roff_int.h index d033f86ace..e0700a7429 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.16 2019/01/05 00:36:50 schwarze Exp $ */ +/* $OpenBSD: roff_int.h,v 1.16 2019/01/05 00:36:46 schwarze Exp $ */ /* + * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * 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 @@ -59,6 +59,7 @@ struct roff_man { 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. */ + char filesec; /* Section digit in the file name. */ }; diff --git a/usr/src/cmd/mandoc/roff_term.c b/usr/src/cmd/mandoc/roff_term.c index f10bb61d2c..115d850fb2 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.19 2019/01/04 03:24:33 schwarze Exp $ */ +/* $OpenBSD: roff_term.c,v 1.20 2020/09/03 17:37:06 schwarze Exp $ */ /* - * Copyright (c) 2010,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010,2014,2015,2017-2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * 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 <sys/types.h> #include <assert.h> @@ -110,9 +112,11 @@ roff_term_pre_ft(ROFF_TERM_ARGS) cp = n->child->string; switch (mandoc_font(cp, (int)strlen(cp))) { case ESCAPE_FONTBOLD: + case ESCAPE_FONTCB: term_fontrepl(p, TERMFONT_BOLD); break; case ESCAPE_FONTITALIC: + case ESCAPE_FONTCI: term_fontrepl(p, TERMFONT_UNDER); break; case ESCAPE_FONTBI: @@ -122,7 +126,7 @@ roff_term_pre_ft(ROFF_TERM_ARGS) term_fontlast(p); break; case ESCAPE_FONTROMAN: - case ESCAPE_FONTCW: + case ESCAPE_FONTCR: term_fontrepl(p, TERMFONT_NONE); break; default: @@ -155,9 +159,13 @@ static void roff_term_pre_po(ROFF_TERM_ARGS) { struct roffsu su; - static int po, polast; + static int po, pouse, polast; int ponew; + /* Revert the currently active page offset. */ + p->tcol->offset -= pouse; + + /* Determine the requested page offset. */ if (n->child != NULL && a2roffsu(n->child->string, &su, SCALE_EM) != NULL) { ponew = term_hen(p, &su); @@ -166,11 +174,15 @@ roff_term_pre_po(ROFF_TERM_ARGS) ponew += po; } else ponew = polast; + + /* Remeber both the previous and the newly requested offset. */ polast = po; po = ponew; - ponew = po - polast + (int)p->tcol->offset; - p->tcol->offset = ponew > 0 ? ponew : 0; + /* Truncate to the range [-offset, 60], remember, and apply it. */ + pouse = po >= 60 ? 60 : + po < -(int)p->tcol->offset ? -(int)p->tcol->offset : po; + p->tcol->offset += pouse; } static void @@ -208,6 +220,7 @@ roff_term_pre_ti(ROFF_TERM_ARGS) { struct roffsu su; const char *cp; + const size_t maxoff = 72; int len, sign; roff_term_pre_br(p, n); @@ -228,17 +241,26 @@ roff_term_pre_ti(ROFF_TERM_ARGS) return; len = term_hen(p, &su); - if (sign == 0) { + switch (sign) { + case 1: + if (p->tcol->offset + len <= maxoff) + p->ti = len; + else if (p->tcol->offset < maxoff) + p->ti = maxoff - p->tcol->offset; + else + p->ti = 0; + break; + case -1: + if ((size_t)len < p->tcol->offset) + p->ti = -len; + else + p->ti = -p->tcol->offset; + break; + default: + if ((size_t)len > maxoff) + len = maxoff; p->ti = len - p->tcol->offset; - p->tcol->offset = len; - } else if (sign == 1) { - p->ti = len; - p->tcol->offset += len; - } else if ((size_t)len < p->tcol->offset) { - p->ti = -len; - p->tcol->offset -= len; - } else { - p->ti = -p->tcol->offset; - p->tcol->offset = 0; + break; } + p->tcol->offset += p->ti; } diff --git a/usr/src/cmd/mandoc/roff_validate.c b/usr/src/cmd/mandoc/roff_validate.c index 9080f28797..74eedafb95 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.18 2018/12/31 09:02:37 schwarze Exp $ */ +/* $Id: roff_validate.c,v 1.20 2020/06/22 19:20:40 schwarze Exp $ */ /* - * Copyright (c) 2010, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2017, 2018, 2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * 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 <sys/types.h> #include <assert.h> @@ -75,7 +77,7 @@ roff_valid_br(ROFF_VALID_ARGS) return; } - if ((np = n->prev) == NULL) + if ((np = roff_node_prev(n)) == NULL) return; switch (np->tok) { @@ -129,7 +131,7 @@ roff_valid_sp(ROFF_VALID_ARGS) { struct roff_node *np; - if ((np = n->prev) == NULL) + if ((np = roff_node_prev(n)) == NULL) return; switch (np->tok) { diff --git a/usr/src/cmd/mandoc/tag.c b/usr/src/cmd/mandoc/tag.c index 473ea7b6f4..fcaad99f2e 100644 --- a/usr/src/cmd/mandoc/tag.c +++ b/usr/src/cmd/mandoc/tag.c @@ -1,6 +1,6 @@ -/* $Id: tag.c,v 1.21 2018/11/22 11:30:23 schwarze Exp $ */ +/* $Id: tag.c,v 1.36 2020/04/19 16:36:16 schwarze Exp $ */ /* - * Copyright (c) 2015, 2016, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,265 +13,315 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Functions to tag syntax tree nodes. + * For internal use by mandoc(1) validation modules only. */ #include "config.h" #include <sys/types.h> -#if HAVE_ERR -#include <err.h> -#endif +#include <assert.h> #include <limits.h> -#include <signal.h> #include <stddef.h> #include <stdint.h> -#include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include "mandoc_aux.h" #include "mandoc_ohash.h" +#include "roff.h" +#include "mdoc.h" +#include "roff_int.h" #include "tag.h" struct tag_entry { - size_t *lines; - size_t maxlines; - size_t nlines; + struct roff_node **nodes; + size_t maxnodes; + size_t nnodes; int prio; char s[]; }; -static void tag_signal(int) __attribute__((__noreturn__)); +static void tag_move_href(struct roff_man *, + struct roff_node *, const char *); +static void tag_move_id(struct roff_node *); static struct ohash tag_data; -static struct tag_files tag_files; /* - * Prepare for using a pager. - * Not all pagers are capable of using a tag file, - * but for simplicity, create it anyway. + * Set up the ohash table to collect nodes + * where various marked-up terms are documented. */ -struct tag_files * -tag_init(void) +void +tag_alloc(void) { - struct sigaction sa; - int ofd; - - ofd = -1; - tag_files.tfd = -1; - tag_files.tcpgid = -1; - - /* Clean up when dying from a signal. */ - - memset(&sa, 0, sizeof(sa)); - sigfillset(&sa.sa_mask); - sa.sa_handler = tag_signal; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - /* - * POSIX requires that a process calling tcsetpgrp(3) - * from the background gets a SIGTTOU signal. - * In that case, do not stop. - */ - - sa.sa_handler = SIG_IGN; - sigaction(SIGTTOU, &sa, NULL); - - /* Save the original standard output for use by the pager. */ - - if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1) - goto fail; - - /* Create both temporary output files. */ - - (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX", - sizeof(tag_files.ofn)); - (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX", - sizeof(tag_files.tfn)); - if ((ofd = mkstemp(tag_files.ofn)) == -1) - goto fail; - if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1) - goto fail; - if (dup2(ofd, STDOUT_FILENO) == -1) - goto fail; - close(ofd); + mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s)); +} - /* - * Set up the ohash table to collect output line numbers - * where various marked-up terms are documented. - */ +void +tag_free(void) +{ + struct tag_entry *entry; + unsigned int slot; - mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s)); - return &tag_files; - -fail: - tag_unlink(); - if (ofd != -1) - close(ofd); - if (tag_files.ofd != -1) - close(tag_files.ofd); - if (tag_files.tfd != -1) - close(tag_files.tfd); - *tag_files.ofn = '\0'; - *tag_files.tfn = '\0'; - tag_files.ofd = -1; - tag_files.tfd = -1; - return NULL; + if (tag_data.info.free == NULL) + return; + entry = ohash_first(&tag_data, &slot); + while (entry != NULL) { + free(entry->nodes); + free(entry); + entry = ohash_next(&tag_data, &slot); + } + ohash_delete(&tag_data); + tag_data.info.free = NULL; } /* - * Set the line number where a term is defined, + * Set a node where a term is defined, * unless it is already defined at a lower priority. */ void -tag_put(const char *s, int prio, size_t line) +tag_put(const char *s, int prio, struct roff_node *n) { struct tag_entry *entry; + struct roff_node *nold; const char *se; size_t len; unsigned int slot; - if (tag_files.tfd <= 0) - return; + assert(prio <= TAG_FALLBACK); - if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e')) - s += 2; + if (s == NULL) { + if (n->child == NULL || n->child->type != ROFFT_TEXT) + return; + s = n->child->string; + switch (s[0]) { + case '-': + s++; + break; + case '\\': + switch (s[1]) { + case '&': + case '-': + case 'e': + s += 2; + break; + default: + break; + } + break; + default: + break; + } + } /* - * Skip whitespace and whatever follows it, + * Skip whitespace and escapes and whatever follows, * and if there is any, downgrade the priority. */ - len = strcspn(s, " \t"); + len = strcspn(s, " \t\\"); if (len == 0) return; se = s + len; - if (*se != '\0') - prio = INT_MAX; + if (*se != '\0' && prio < TAG_WEAK) + prio = TAG_WEAK; slot = ohash_qlookupi(&tag_data, s, &se); entry = ohash_find(&tag_data, slot); - if (entry == NULL) { - - /* Build a new entry. */ + /* Build a new entry. */ + if (entry == NULL) { entry = mandoc_malloc(sizeof(*entry) + len + 1); memcpy(entry->s, s, len); entry->s[len] = '\0'; - entry->lines = NULL; - entry->maxlines = entry->nlines = 0; + entry->nodes = NULL; + entry->maxnodes = entry->nnodes = 0; ohash_insert(&tag_data, slot, entry); + } - } else { - - /* - * Lower priority numbers take precedence, - * but 0 is special. - * A tag with priority 0 is only used - * if the tag occurs exactly once. - */ + /* + * Lower priority numbers take precedence. + * If a better entry is already present, ignore the new one. + */ - if (prio == 0) { - if (entry->prio == 0) - entry->prio = -1; + else if (entry->prio < prio) return; - } - /* A better entry is already present, ignore the new one. */ + /* + * If the existing entry is worse, clear it. + * In addition, a tag with priority TAG_FALLBACK + * is only used if the tag occurs exactly once. + */ - if (entry->prio > 0 && entry->prio < prio) + else if (entry->prio > prio || prio == TAG_FALLBACK) { + while (entry->nnodes > 0) { + nold = entry->nodes[--entry->nnodes]; + nold->flags &= ~NODE_ID; + free(nold->tag); + nold->tag = NULL; + } + if (prio == TAG_FALLBACK) { + entry->prio = TAG_DELETE; return; - - /* The existing entry is worse, clear it. */ - - if (entry->prio < 1 || entry->prio > prio) - entry->nlines = 0; + } } - /* Remember the new line. */ + /* Remember the new node. */ - if (entry->maxlines == entry->nlines) { - entry->maxlines += 4; - entry->lines = mandoc_reallocarray(entry->lines, - entry->maxlines, sizeof(*entry->lines)); + if (entry->maxnodes == entry->nnodes) { + entry->maxnodes += 4; + entry->nodes = mandoc_reallocarray(entry->nodes, + entry->maxnodes, sizeof(*entry->nodes)); } - entry->lines[entry->nlines++] = line; + entry->nodes[entry->nnodes++] = n; entry->prio = prio; + n->flags |= NODE_ID; + if (n->child == NULL || n->child->string != s || *se != '\0') { + assert(n->tag == NULL); + n->tag = mandoc_strndup(s, len); + } +} + +int +tag_exists(const char *tag) +{ + return ohash_find(&tag_data, ohash_qlookup(&tag_data, tag)) != NULL; } /* - * Write out the tags file using the previously collected - * information and clear the ohash table while going along. + * For in-line elements, move the link target + * to the enclosing paragraph when appropriate. */ -void -tag_write(void) +static void +tag_move_id(struct roff_node *n) { - FILE *stream; - struct tag_entry *entry; - size_t i; - unsigned int slot; + struct roff_node *np; - 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) { - if (stream != NULL && entry->prio >= 0) - for (i = 0; i < entry->nlines; i++) - fprintf(stream, "%s %s %zu\n", - entry->s, tag_files.ofn, entry->lines[i]); - free(entry->lines); - free(entry); - entry = ohash_next(&tag_data, &slot); + np = n; + for (;;) { + if (np->prev != NULL) + np = np->prev; + else if ((np = np->parent) == NULL) + return; + switch (np->tok) { + case MDOC_It: + switch (np->parent->parent->norm->Bl.type) { + case LIST_column: + /* Target the ROFFT_BLOCK = <tr>. */ + np = np->parent; + break; + case LIST_diag: + case LIST_hang: + case LIST_inset: + case LIST_ohang: + case LIST_tag: + /* Target the ROFFT_HEAD = <dt>. */ + np = np->parent->head; + break; + default: + /* Target the ROFF_BODY = <li>. */ + break; + } + /* FALLTHROUGH */ + case MDOC_Pp: /* Target the ROFFT_ELEM = <p>. */ + if (np->tag == NULL) { + np->tag = mandoc_strdup(n->tag == NULL ? + n->child->string : n->tag); + np->flags |= NODE_ID; + n->flags &= ~NODE_ID; + } + return; + case MDOC_Sh: + case MDOC_Ss: + case MDOC_Bd: + case MDOC_Bl: + case MDOC_D1: + case MDOC_Dl: + case MDOC_Rs: + /* Do not move past major blocks. */ + return; + default: + /* + * Move past in-line content and partial + * blocks, for example .It Xo or .It Bq Er. + */ + break; + } } - ohash_delete(&tag_data); - if (stream != NULL) - fclose(stream); - else - close(tag_files.tfd); - tag_files.tfd = -1; } -void -tag_unlink(void) +/* + * When a paragraph is tagged and starts with text, + * move the permalink to the first few words. + */ +static void +tag_move_href(struct roff_man *man, struct roff_node *n, const char *tag) { - pid_t tc_pgid; - - if (tag_files.tcpgid != -1) { - tc_pgid = tcgetpgrp(tag_files.ofd); - if (tc_pgid == tag_files.pager_pid || - tc_pgid == getpgid(0) || - getpgid(tc_pgid) == -1) - (void)tcsetpgrp(tag_files.ofd, tag_files.tcpgid); + char *cp; + + if (n == NULL || n->type != ROFFT_TEXT || + *n->string == '\0' || *n->string == ' ') + return; + + cp = n->string; + while (cp != NULL && cp - n->string < 5) + cp = strchr(cp + 1, ' '); + + /* If the first text node is longer, split it. */ + + if (cp != NULL && cp[1] != '\0') { + man->last = n; + man->next = ROFF_NEXT_SIBLING; + roff_word_alloc(man, n->line, + n->pos + (cp - n->string), cp + 1); + man->last->flags = n->flags & ~NODE_LINE; + *cp = '\0'; } - if (*tag_files.ofn != '\0') - unlink(tag_files.ofn); - if (*tag_files.tfn != '\0') - unlink(tag_files.tfn); + + assert(n->tag == NULL); + n->tag = mandoc_strdup(tag); + n->flags |= NODE_HREF; } -static void -tag_signal(int signum) +/* + * When all tags have been set, decide where to put + * the associated permalinks, and maybe move some tags + * to the beginning of the respective paragraphs. + */ +void +tag_postprocess(struct roff_man *man, struct roff_node *n) { - struct sigaction sa; - - tag_unlink(); - memset(&sa, 0, sizeof(sa)); - sigemptyset(&sa.sa_mask); - sa.sa_handler = SIG_DFL; - sigaction(signum, &sa, NULL); - kill(getpid(), signum); - /* NOTREACHED */ - _exit(1); + if (n->flags & NODE_ID) { + switch (n->tok) { + case MDOC_Pp: + tag_move_href(man, n->next, n->tag); + break; + case MDOC_Bd: + case MDOC_D1: + case MDOC_Dl: + tag_move_href(man, n->child, n->tag); + break; + case MDOC_Bl: + /* XXX No permalink for now. */ + break; + default: + if (n->type == ROFFT_ELEM || n->tok == MDOC_Fo) + tag_move_id(n); + if (n->tok != MDOC_Tg) + n->flags |= NODE_HREF; + else if ((n->flags & NODE_ID) == 0) { + n->flags |= NODE_NOPRT; + free(n->tag); + n->tag = NULL; + } + break; + } + } + for (n = n->child; n != NULL; n = n->next) + tag_postprocess(man, n); } diff --git a/usr/src/cmd/mandoc/tag.h b/usr/src/cmd/mandoc/tag.h index bbd40b737e..1eace6fd51 100644 --- a/usr/src/cmd/mandoc/tag.h +++ b/usr/src/cmd/mandoc/tag.h @@ -1,6 +1,6 @@ -/* $Id: tag.h,v 1.8 2018/11/22 11:30:23 schwarze Exp $ */ +/* $Id: tag.h,v 1.14 2020/04/18 20:40:10 schwarze Exp $ */ /* - * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2015, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,20 +13,23 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internal interfaces to tag syntax tree nodes. + * For use by mandoc(1) validation modules only. */ -struct tag_files { - char ofn[20]; - char tfn[20]; - char *tagname; - int ofd; - int tfd; - pid_t tcpgid; - pid_t pager_pid; -}; - +/* + * Tagging priorities. + * Lower numbers indicate higher importance. + */ +#define TAG_MANUAL 1 /* Set with a .Tg macro. */ +#define TAG_STRONG 2 /* Good automatic tagging. */ +#define TAG_WEAK (INT_MAX - 2) /* Dubious automatic tagging. */ +#define TAG_FALLBACK (INT_MAX - 1) /* Tag only used if unique. */ +#define TAG_DELETE (INT_MAX) /* Tag not used at all. */ -struct tag_files *tag_init(void); -void tag_put(const char *, int, size_t); -void tag_write(void); -void tag_unlink(void); +void tag_alloc(void); +int tag_exists(const char *); +void tag_put(const char *, int, struct roff_node *); +void tag_postprocess(struct roff_man *, struct roff_node *); +void tag_free(void); diff --git a/usr/src/cmd/mandoc/tbl.h b/usr/src/cmd/mandoc/tbl.h index 365ae929ed..0fccb44055 100644 --- a/usr/src/cmd/mandoc/tbl.h +++ b/usr/src/cmd/mandoc/tbl.h @@ -1,7 +1,7 @@ -/* $Id: tbl.h,v 1.1 2018/12/12 21:54:35 schwarze Exp $ */ +/* $Id: tbl.h,v 1.2 2021/08/10 12:55:04 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2017,2018,2021 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 @@ -57,14 +57,13 @@ struct tbl_cell { 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 mandoc_esc font; enum tbl_cellt pos; }; diff --git a/usr/src/cmd/mandoc/tbl_data.c b/usr/src/cmd/mandoc/tbl_data.c index 9a58d32562..fe0259f922 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.52 2019/02/09 16:00:39 schwarze Exp $ */ +/* $Id: tbl_data.c,v 1.59 2021/09/10 13:24:38 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011,2015,2017,2018,2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2015,2017-2019,2021 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> @@ -45,16 +46,20 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, struct tbl_dat *dat, *pdat; struct tbl_cell *cp; struct tbl_span *pdp; - int sv; + const char *ccp; + int startpos, endpos; /* * 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)++; + startpos = *pos; + ccp = p + startpos; + while (*ccp != '\0' && *ccp != tbl->opts.tab) + if (*ccp++ == '\\') + mandoc_escape(&ccp, NULL, NULL); + *pos = ccp - p; /* Advance to the next layout cell, skipping spanners. */ @@ -73,12 +78,14 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, if (dp->layout->last->col + 1 < dp->opts->cols) { cp = mandoc_calloc(1, sizeof(*cp)); cp->pos = TBL_CELL_LEFT; + cp->font = ESCAPE_FONTROMAN; + cp->spacing = SIZE_MAX; dp->layout->last->next = cp; cp->col = dp->layout->last->col + 1; dp->layout->last = cp; } else { mandoc_msg(MANDOCERR_TBLDATA_EXTRA, - ln, sv, "%s", p + sv); + ln, startpos, "%s", p + startpos); while (p[*pos] != '\0') (*pos)++; return; @@ -103,7 +110,8 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, */ if (cp->pos == TBL_CELL_DOWN || - (*pos - sv == 2 && p[sv] == '\\' && p[sv + 1] == '^')) { + (*pos - startpos == 2 && + p[startpos] == '\\' && p[startpos + 1] == '^')) { pdp = dp; while ((pdp = pdp->prev) != NULL) { pdat = pdp->first; @@ -139,18 +147,29 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, dp->last->next = dat; dp->last = dat; + /* Strip leading and trailing spaces, if requested. */ + + endpos = *pos; + if (dp->opts->opts & TBL_OPT_NOSPACE) { + while (p[startpos] == ' ') + startpos++; + while (endpos > startpos && p[endpos - 1] == ' ') + endpos--; + } + /* * Check for a continued-data scope opening. This consists of a * trailing `T{' at the end of the line. Subsequent lines, * until a standalone `T}', are included in our cell. */ - if (*pos - sv == 2 && p[sv] == 'T' && p[sv + 1] == '{') { + if (endpos - startpos == 2 && + p[startpos] == 'T' && p[startpos + 1] == '{') { tbl->part = TBL_PART_CDATA; return; } - dat->string = mandoc_strndup(p + sv, *pos - sv); + dat->string = mandoc_strndup(p + startpos, endpos - startpos); if (p[*pos] != '\0') (*pos)++; @@ -171,7 +190,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, - ln, sv, "%s", dat->string); + ln, startpos, "%s", dat->string); } void @@ -184,6 +203,9 @@ tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos) if (p[pos] == 'T' && p[pos + 1] == '}') { pos += 2; + if (tbl->opts.opts & TBL_OPT_NOSPACE) + while (p[pos] == ' ') + pos++; if (p[pos] == tbl->opts.tab) { tbl->part = TBL_PART_DATA; pos++; @@ -242,10 +264,11 @@ tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos) struct tbl_cell *cp; struct tbl_span *sp; - rp = (sp = tbl->last_span) == NULL ? tbl->first_row : - sp->pos == TBL_SPAN_DATA && sp->layout->next != NULL ? - sp->layout->next : sp->layout; - + for (sp = tbl->last_span; sp != NULL; sp = sp->prev) + if (sp->pos == TBL_SPAN_DATA) + break; + rp = sp == NULL ? tbl->first_row : + sp->layout->next == NULL ? sp->layout : sp->layout->next; assert(rp != NULL); if (p[1] == '\0') { diff --git a/usr/src/cmd/mandoc/tbl_html.c b/usr/src/cmd/mandoc/tbl_html.c index 4ab6bed10b..65c8ae8f88 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.32 2019/01/06 04:55:09 schwarze Exp $ */ +/* $Id: tbl_html.c,v 1.38 2021/09/09 16:52:52 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2017,2018,2021 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 "roff.h" #include "tbl.h" #include "out.h" #include "html.h" @@ -114,10 +115,14 @@ print_tbl(struct html *h, const struct tbl_span *sp) const struct tbl_dat *dp; const struct tbl_cell *cp; const struct tbl_span *psp; + const struct roffcol *col; struct tag *tt; const char *hspans, *vspans, *halign, *valign; const char *bborder, *lborder, *rborder; + const char *ccp; char hbuf[4], vbuf[4]; + size_t sz; + enum mandoc_esc save_font; int i; if (h->tblt == NULL) @@ -239,8 +244,40 @@ print_tbl(struct html *h, const struct tbl_span *sp) "vertical-align", valign, "text-align", halign, "border-right-style", rborder); - if (dp->string != NULL) + if (dp->layout->pos == TBL_CELL_HORIZ || + dp->layout->pos == TBL_CELL_DHORIZ || + dp->pos == TBL_DATA_HORIZ || + dp->pos == TBL_DATA_DHORIZ) + print_otag(h, TAG_HR, ""); + else if (dp->string != NULL) { + save_font = h->metac; + html_setfont(h, dp->layout->font); + if (dp->layout->pos == TBL_CELL_LONG) + print_text(h, "\\[u2003]"); /* em space */ print_text(h, dp->string); + if (dp->layout->pos == TBL_CELL_NUMBER) { + col = h->tbl.cols + dp->layout->col; + if (col->decimal < col->nwidth) { + if ((ccp = strrchr(dp->string, + sp->opts->decimal)) == NULL) { + /* Punctuation space. */ + print_text(h, "\\[u2008]"); + ccp = strchr(dp->string, '\0'); + } else + ccp++; + sz = col->nwidth - col->decimal; + while (--sz > 0) { + if (*ccp == '\0') + /* Figure space. */ + print_text(h, + "\\[u2007]"); + else + ccp++; + } + } + } + html_setfont(h, save_font); + } } print_tagq(h, tt); diff --git a/usr/src/cmd/mandoc/tbl_layout.c b/usr/src/cmd/mandoc/tbl_layout.c index 58599705c1..171a8dbb1b 100644 --- a/usr/src/cmd/mandoc/tbl_layout.c +++ b/usr/src/cmd/mandoc/tbl_layout.c @@ -1,7 +1,8 @@ -/* $Id: tbl_layout.c,v 1.48 2018/12/14 05:18:03 schwarze Exp $ */ +/* $Id: tbl_layout.c,v 1.50 2021/08/10 12:55:04 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012, 2014, 2015, 2017, 2020, 2021 + * 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 @@ -65,7 +66,10 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp, int ln, const char *p, int *pos) { char *endptr; + unsigned long spacing; size_t sz; + int isz; + enum mandoc_esc fontesc; mod: while (p[*pos] == ' ' || p[*pos] == '\t') @@ -93,14 +97,18 @@ mod: /* Parse numerical spacing from modifier string. */ if (isdigit((unsigned char)p[*pos])) { - cp->spacing = strtoull(p + *pos, &endptr, 10); + if ((spacing = strtoul(p + *pos, &endptr, 10)) > 9) + mandoc_msg(MANDOCERR_TBLLAYOUT_SPC, ln, *pos, + "%lu", spacing); + else + cp->spacing = spacing; *pos = endptr - p; goto mod; } switch (tolower((unsigned char)p[(*pos)++])) { case 'b': - cp->flags |= TBL_CELL_BOLD; + cp->font = ESCAPE_FONTBOLD; goto mod; case 'd': cp->flags |= TBL_CELL_BALIGN; @@ -111,7 +119,7 @@ mod: case 'f': break; case 'i': - cp->flags |= TBL_CELL_ITALIC; + cp->font = ESCAPE_FONTITALIC; goto mod; case 'm': mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, ln, *pos, "m"); @@ -165,40 +173,34 @@ mod: goto mod; } + while (p[*pos] == ' ' || p[*pos] == '\t') + (*pos)++; + /* Ignore parenthised font names for now. */ if (p[*pos] == '(') goto mod; - /* Support only one-character font-names for now. */ - - if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) { + isz = 0; + if (p[*pos] != '\0') + isz++; + if (strchr(" \t.", p[*pos + isz]) == NULL) + isz++; + + fontesc = mandoc_font(p + *pos, isz); + + switch (fontesc) { + case ESCAPE_FONTPREV: + case ESCAPE_ERROR: mandoc_msg(MANDOCERR_FT_BAD, ln, *pos, "TS %s", p + *pos - 1); - if (p[*pos] != '\0') - (*pos)++; - if (p[*pos] != '\0') - (*pos)++; - goto mod; - } - - switch (p[(*pos)++]) { - case '3': - case 'B': - cp->flags |= TBL_CELL_BOLD; - goto mod; - case '2': - case 'I': - cp->flags |= TBL_CELL_ITALIC; - goto mod; - case '1': - case 'R': - goto mod; + break; default: - mandoc_msg(MANDOCERR_FT_BAD, - ln, *pos - 1, "TS f%c", p[*pos - 1]); - goto mod; + cp->font = fontesc; + break; } + *pos += isz; + goto mod; } static void @@ -357,6 +359,7 @@ cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) p = mandoc_calloc(1, sizeof(*p)); p->spacing = SIZE_MAX; + p->font = ESCAPE_FONTROMAN; p->pos = pos; if ((pp = rp->last) != NULL) { diff --git a/usr/src/cmd/mandoc/tbl_term.c b/usr/src/cmd/mandoc/tbl_term.c index a411107bd1..eac125586c 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.68 2019/02/09 21:02:47 schwarze Exp $ */ +/* $Id: tbl_term.c,v 1.75 2021/08/10 12:55:04 schwarze Exp $ */ /* * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2021 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 @@ -46,7 +46,8 @@ 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); + 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 *, @@ -189,17 +190,6 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin); - /* Tables leak .ta settings to subsequent text. */ - - term_tab_set(tp, NULL); - coloff = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) || - sp->opts->lvert; - for (ic = 0; ic < sp->opts->cols; ic++) { - coloff += tp->tbl.cols[ic].width; - term_tab_iset(coloff); - coloff += tp->tbl.cols[ic].spacing; - } - /* Center the table as a whole. */ offset = tp->tcol->offset; @@ -222,9 +212,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) if (tp->enc == TERMENC_ASCII && sp->opts->opts & TBL_OPT_DBOX) - tbl_hrule(tp, NULL, sp, TBL_OPT_DBOX); + tbl_hrule(tp, NULL, sp, sp, TBL_OPT_DBOX); if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) - tbl_hrule(tp, NULL, sp, TBL_OPT_BOX); + tbl_hrule(tp, NULL, sp, sp, TBL_OPT_BOX); } /* Set up the columns. */ @@ -266,11 +256,11 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) hspans--; continue; } - if (dp == NULL) - continue; - hspans = dp->hspans; - if (ic || sp->layout->first->pos != TBL_CELL_SPAN) + if (dp != NULL && + (ic || sp->layout->first->pos != TBL_CELL_SPAN)) { + hspans = dp->hspans; dp = dp->next; + } } /* Set up a column for a right vertical frame. */ @@ -301,11 +291,11 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tp->tcol++; tp->col = 0; tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic); - if (dp == NULL) - continue; - hspans = dp->hspans; - if (cp->pos != TBL_CELL_SPAN) + if (dp != NULL && + (ic || sp->layout->first->pos != TBL_CELL_SPAN)) { + hspans = dp->hspans; dp = dp->next; + } } break; } @@ -342,7 +332,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) more = 0; if (horiz) - tbl_hrule(tp, sp->prev, sp, 0); + tbl_hrule(tp, sp->prev, sp, sp->next, 0); else { cp = sp->layout->first; cpn = sp->next == NULL ? NULL : @@ -424,11 +414,10 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) cp = cp->next; continue; } - if (dp != NULL) { + if (dp != NULL && (ic || + sp->layout->first->pos != TBL_CELL_SPAN)) { hspans = dp->hspans; - if (ic || sp->layout->first->pos - != TBL_CELL_SPAN) - dp = dp->next; + dp = dp->next; } /* @@ -557,12 +546,12 @@ 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, NULL, TBL_OPT_BOX); + tbl_hrule(tp, sp, sp, NULL, TBL_OPT_BOX); tp->skipvsp = 1; } if (tp->enc == TERMENC_ASCII && sp->opts->opts & TBL_OPT_DBOX) { - tbl_hrule(tp, sp, NULL, TBL_OPT_DBOX); + tbl_hrule(tp, sp, sp, NULL, TBL_OPT_DBOX); tp->skipvsp = 2; } assert(tp->tbl.cols); @@ -571,7 +560,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) } 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, sp->next, TBL_OPT_ALLBOX); + tbl_hrule(tp, sp, sp, sp->next, TBL_OPT_ALLBOX); tp->tcol->offset = save_offset; tp->flags &= ~TERMP_NONOSPACE; @@ -579,9 +568,10 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) static void tbl_hrule(struct termp *tp, const struct tbl_span *spp, - const struct tbl_span *spn, int flags) + const struct tbl_span *sp, const struct tbl_span *spn, int flags) { const struct tbl_cell *cpp; /* Layout cell above this line. */ + const struct tbl_cell *cp; /* Layout cell in 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. */ @@ -592,6 +582,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp, int uw, dw; /* Vertical line widths. */ cpp = spp == NULL ? NULL : spp->layout->first; + cp = sp == NULL ? NULL : sp->layout->first; cpn = spn == NULL ? NULL : spn->layout->first; dpn = NULL; if (spn != NULL) { @@ -600,11 +591,11 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp, else if (spn->next != NULL) dpn = spn->next->first; } - opts = spn == NULL ? spp->opts->opts : spn->opts->opts; + opts = sp->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; + sp->pos == TBL_SPAN_DHORIZ ? 2 : 1; /* Print the left end of the line. */ @@ -619,14 +610,19 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp, (spp == NULL || cpn == NULL || cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1); + col = tp->tbl.cols; for (;;) { - col = tp->tbl.cols + (cpn == NULL ? cpp->col : cpn->col); + if (cp == NULL) + col++; + else + col = tp->tbl.cols + cp->col; /* Print the horizontal line inside this column. */ lw = cpp == NULL || cpn == NULL || (cpn->pos != TBL_CELL_DOWN && - (dpn == NULL || strcmp(dpn->string, "\\^") != 0)) + (dpn == NULL || dpn->string == NULL || + strcmp(dpn->string, "\\^") != 0)) ? hw : 0; tbl_direct_border(tp, BHORIZ * lw, col->width + col->spacing / 2); @@ -645,7 +641,10 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp, uw = 1; } cpp = cpp->next; - } + } else if (spp != NULL && opts & TBL_OPT_ALLBOX) + uw = 1; + if (cp != NULL) + cp = cp->next; if (cpn != NULL) { if (flags != TBL_OPT_DBOX) { dw = cpn->vert; @@ -655,8 +654,9 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp, cpn = cpn->next; while (dpn != NULL && dpn->layout != cpn) dpn = dpn->next; - } - if (cpp == NULL && cpn == NULL) + } else if (spn != NULL && opts & TBL_OPT_ALLBOX) + dw = 1; + if (col + 1 == tp->tbl.cols + sp->opts->cols) break; /* Vertical lines do not cross spanned cells. */ @@ -670,7 +670,8 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp, rw = cpp == NULL || cpn == NULL || (cpn->pos != TBL_CELL_DOWN && - (dpn == NULL || strcmp(dpn->string, "\\^") != 0)) + (dpn == NULL || dpn->string == NULL || + strcmp(dpn->string, "\\^") != 0)) ? hw : 0; /* The line crossing at the end of this column. */ @@ -922,10 +923,24 @@ tbl_word(struct termp *tp, const struct tbl_dat *dp) int prev_font; prev_font = tp->fonti; - if (dp->layout->flags & TBL_CELL_BOLD) - term_fontpush(tp, TERMFONT_BOLD); - else if (dp->layout->flags & TBL_CELL_ITALIC) - term_fontpush(tp, TERMFONT_UNDER); + switch (dp->layout->font) { + case ESCAPE_FONTBI: + term_fontpush(tp, TERMFONT_BI); + break; + case ESCAPE_FONTBOLD: + case ESCAPE_FONTCB: + term_fontpush(tp, TERMFONT_BOLD); + break; + case ESCAPE_FONTITALIC: + case ESCAPE_FONTCI: + term_fontpush(tp, TERMFONT_UNDER); + break; + case ESCAPE_FONTROMAN: + case ESCAPE_FONTCR: + break; + default: + abort(); + } term_word(tp, dp->string); diff --git a/usr/src/cmd/mandoc/term.c b/usr/src/cmd/mandoc/term.c index 3b9277aaab..fb0351d91f 100644 --- a/usr/src/cmd/mandoc/term.c +++ b/usr/src/cmd/mandoc/term.c @@ -1,7 +1,7 @@ -/* $Id: term.c,v 1.281 2019/06/03 20:23:41 schwarze Exp $ */ +/* $Id: term.c,v 1.283 2021/08/10 12:55:04 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -38,8 +38,7 @@ 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_field(struct termp *, size_t, size_t); static void term_fill(struct termp *, size_t *, size_t *, size_t); @@ -127,8 +126,7 @@ term_flushln(struct termp *p) * and with the BRNEVER flag, never break it at all. */ - vtarget = p->flags & TERMP_BRNEVER ? SIZE_MAX : - (p->flags & TERMP_NOBREAK) == 0 ? vfield : + vtarget = (p->flags & TERMP_NOBREAK) == 0 ? vfield : p->maxrmargin > p->viscol + vbl ? p->maxrmargin - p->viscol - vbl : 0; @@ -137,7 +135,8 @@ term_flushln(struct termp *p) * If there is whitespace only, print nothing. */ - term_fill(p, &nbr, &vbr, vtarget); + term_fill(p, &nbr, &vbr, + p->flags & TERMP_BRNEVER ? SIZE_MAX : vtarget); if (nbr == 0) break; @@ -156,7 +155,7 @@ term_flushln(struct termp *p) /* Finally, print the field content. */ - term_field(p, vbl, nbr, vbr, vtarget); + term_field(p, vbl, nbr); /* * If there is no text left in the field, exit the loop. @@ -345,12 +344,10 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget) /* * 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. + * and an input string length of nbr characters. */ static void -term_field(struct termp *p, size_t vbl, size_t nbr, size_t vbr, size_t vtarget) +term_field(struct termp *p, size_t vbl, size_t nbr) { size_t ic; /* Character position in the input buffer. */ size_t vis; /* Visual position of the current character. */ @@ -592,16 +589,18 @@ term_word(struct termp *p, const char *word) uc = *seq; break; case ESCAPE_FONTBOLD: + case ESCAPE_FONTCB: term_fontrepl(p, TERMFONT_BOLD); continue; case ESCAPE_FONTITALIC: + case ESCAPE_FONTCI: term_fontrepl(p, TERMFONT_UNDER); continue; case ESCAPE_FONTBI: term_fontrepl(p, TERMFONT_BI); continue; case ESCAPE_FONT: - case ESCAPE_FONTCW: + case ESCAPE_FONTCR: case ESCAPE_FONTROMAN: term_fontrepl(p, TERMFONT_NONE); continue; diff --git a/usr/src/cmd/mandoc/term_ascii.c b/usr/src/cmd/mandoc/term_ascii.c index 368623cac1..bf7e9b639e 100644 --- a/usr/src/cmd/mandoc/term_ascii.c +++ b/usr/src/cmd/mandoc/term_ascii.c @@ -1,7 +1,7 @@ -/* $Id: term_ascii.c,v 1.64 2018/11/28 14:23:06 schwarze Exp $ */ +/* $Id: term_ascii.c,v 1.66 2020/09/09 13:45:05 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -232,7 +232,10 @@ ascii_endline(struct termp *p) { p->line++; - p->tcol->offset -= p->ti; + if ((int)p->tcol->offset > p->ti) + p->tcol->offset -= p->ti; + else + p->tcol->offset = 0; p->ti = 0; putchar('\n'); } @@ -242,7 +245,14 @@ ascii_advance(struct termp *p, size_t len) { size_t i; - assert(len < UINT16_MAX); + /* + * XXX We used to have "assert(len < UINT16_MAX)" here. + * that is not quite right because the input document + * can trigger that by merely providing large input. + * For now, simply truncate. + */ + if (len > 256) + len = 256; for (i = 0; i < len; i++) putchar(' '); } @@ -380,7 +390,14 @@ locale_advance(struct termp *p, size_t len) { size_t i; - assert(len < UINT16_MAX); + /* + * XXX We used to have "assert(len < UINT16_MAX)" here. + * that is not quite right because the input document + * can trigger that by merely providing large input. + * For now, simply truncate. + */ + if (len > 256) + len = 256; for (i = 0; i < len; i++) putwchar(L' '); } @@ -390,7 +407,10 @@ locale_endline(struct termp *p) { p->line++; - p->tcol->offset -= p->ti; + if ((int)p->tcol->offset > p->ti) + p->tcol->offset -= p->ti; + else + p->tcol->offset = 0; p->ti = 0; putwchar(L'\n'); } diff --git a/usr/src/cmd/mandoc/term_ps.c b/usr/src/cmd/mandoc/term_ps.c index 2cd94c9231..374d3d9a6a 100644 --- a/usr/src/cmd/mandoc/term_ps.c +++ b/usr/src/cmd/mandoc/term_ps.c @@ -1,7 +1,7 @@ -/* $Id: term_ps.c,v 1.91 2017/11/10 23:42:52 schwarze Exp $ */ +/* $Id: term_ps.c,v 1.92 2020/09/06 14:45:22 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2016,2017,2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2017 Marc Espie <espie@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -1252,7 +1252,10 @@ ps_endline(struct termp *p) ps_closepage(p); - p->tcol->offset -= p->ti; + if ((int)p->tcol->offset > p->ti) + p->tcol->offset -= p->ti; + else + p->tcol->offset = 0; p->ti = 0; } diff --git a/usr/src/cmd/mandoc/term_tab.c b/usr/src/cmd/mandoc/term_tab.c index 3343244f3c..84b4c00c6e 100644 --- a/usr/src/cmd/mandoc/term_tab.c +++ b/usr/src/cmd/mandoc/term_tab.c @@ -1,4 +1,4 @@ -/* $Id: term_tab.c,v 1.5 2018/12/16 00:21:05 schwarze Exp $ */ +/* $Id: term_tab.c,v 1.6 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> * @@ -14,6 +14,8 @@ * 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 <sys/types.h> #include <stddef.h> diff --git a/usr/src/cmd/mandoc/term_tag.c b/usr/src/cmd/mandoc/term_tag.c new file mode 100644 index 0000000000..c26b942531 --- /dev/null +++ b/usr/src/cmd/mandoc/term_tag.c @@ -0,0 +1,227 @@ +/* $Id: term_tag.c,v 1.6 2021/03/30 17:16:55 schwarze Exp $ */ +/* + * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Functions to write a ctags(1) file. + * For use by the mandoc(1) ASCII and UTF-8 formatters only. + */ +#include "config.h" + +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mandoc.h" +#include "roff.h" +#include "roff_int.h" +#include "tag.h" +#include "term_tag.h" + +static void tag_signal(int) __attribute__((__noreturn__)); + +static struct tag_files tag_files; + + +/* + * Prepare for using a pager. + * Not all pagers are capable of using a tag file, + * but for simplicity, create it anyway. + */ +struct tag_files * +term_tag_init(const char *outfilename, const char *suffix, + const char *tagfilename) +{ + struct sigaction sa; + int ofd; /* In /tmp/, dup(2)ed to stdout. */ + int tfd; + + ofd = tfd = -1; + tag_files.tfs = NULL; + tag_files.tcpgid = -1; + + /* Clean up when dying from a signal. */ + + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = tag_signal; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + /* + * POSIX requires that a process calling tcsetpgrp(3) + * from the background gets a SIGTTOU signal. + * In that case, do not stop. + */ + + sa.sa_handler = SIG_IGN; + sigaction(SIGTTOU, &sa, NULL); + + /* Save the original standard output for use by the pager. */ + + if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1) { + mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); + goto fail; + } + + /* Create both temporary output files. */ + + if (outfilename == NULL) { + (void)snprintf(tag_files.ofn, sizeof(tag_files.ofn), + "/tmp/man.XXXXXXXXXX%s", suffix); + if ((ofd = mkstemps(tag_files.ofn, strlen(suffix))) == -1) { + mandoc_msg(MANDOCERR_MKSTEMP, 0, 0, + "%s: %s", tag_files.ofn, strerror(errno)); + goto fail; + } + } else { + (void)strlcpy(tag_files.ofn, outfilename, + sizeof(tag_files.ofn)); + unlink(outfilename); + ofd = open(outfilename, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (ofd == -1) { + mandoc_msg(MANDOCERR_OPEN, 0, 0, + "%s: %s", outfilename, strerror(errno)); + goto fail; + } + } + if (tagfilename == NULL) { + (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX", + sizeof(tag_files.tfn)); + if ((tfd = mkstemp(tag_files.tfn)) == -1) { + mandoc_msg(MANDOCERR_MKSTEMP, 0, 0, + "%s: %s", tag_files.tfn, strerror(errno)); + goto fail; + } + } else { + (void)strlcpy(tag_files.tfn, tagfilename, + sizeof(tag_files.tfn)); + unlink(tagfilename); + tfd = open(tagfilename, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (tfd == -1) { + mandoc_msg(MANDOCERR_OPEN, 0, 0, + "%s: %s", tagfilename, strerror(errno)); + goto fail; + } + } + if ((tag_files.tfs = fdopen(tfd, "w")) == NULL) { + mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); + goto fail; + } + tfd = -1; + if (dup2(ofd, STDOUT_FILENO) == -1) { + mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); + goto fail; + } + close(ofd); + return &tag_files; + +fail: + term_tag_unlink(); + if (ofd != -1) + close(ofd); + if (tfd != -1) + close(tfd); + if (tag_files.ofd != -1) { + close(tag_files.ofd); + tag_files.ofd = -1; + } + return NULL; +} + +void +term_tag_write(struct roff_node *n, size_t line) +{ + const char *cp; + int len; + + if (tag_files.tfs == NULL) + return; + cp = n->tag == NULL ? n->child->string : n->tag; + if (cp[0] == '\\' && (cp[1] == '&' || cp[1] == 'e')) + cp += 2; + len = strcspn(cp, " \t\\"); + fprintf(tag_files.tfs, "%.*s %s %zu\n", + len, cp, tag_files.ofn, line); +} + +/* + * Close both output files and restore the original standard output + * to the terminal. In the unlikely case that the latter fails, + * trying to start a pager would be useless, so report the failure + * to the main program. + */ +int +term_tag_close(void) +{ + int irc = 0; + + if (tag_files.tfs != NULL) { + fclose(tag_files.tfs); + tag_files.tfs = NULL; + } + if (tag_files.ofd != -1) { + fflush(stdout); + if ((irc = dup2(tag_files.ofd, STDOUT_FILENO)) == -1) + mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); + close(tag_files.ofd); + tag_files.ofd = -1; + } + return irc; +} + +void +term_tag_unlink(void) +{ + pid_t tc_pgid; + + if (tag_files.tcpgid != -1) { + tc_pgid = tcgetpgrp(STDOUT_FILENO); + if (tc_pgid == tag_files.pager_pid || + tc_pgid == getpgid(0) || + getpgid(tc_pgid) == -1) + (void)tcsetpgrp(STDOUT_FILENO, tag_files.tcpgid); + } + if (strncmp(tag_files.ofn, "/tmp/man.", 9) == 0) { + unlink(tag_files.ofn); + *tag_files.ofn = '\0'; + } + if (strncmp(tag_files.tfn, "/tmp/man.", 9) == 0) { + unlink(tag_files.tfn); + *tag_files.tfn = '\0'; + } +} + +static void +tag_signal(int signum) +{ + struct sigaction sa; + + term_tag_unlink(); + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_DFL; + sigaction(signum, &sa, NULL); + kill(getpid(), signum); + /* NOTREACHED */ + _exit(1); +} diff --git a/usr/src/cmd/mandoc/term_tag.h b/usr/src/cmd/mandoc/term_tag.h new file mode 100644 index 0000000000..f82b1a6e92 --- /dev/null +++ b/usr/src/cmd/mandoc/term_tag.h @@ -0,0 +1,34 @@ +/* $Id: term_tag.h,v 1.4 2021/03/30 17:16:55 schwarze Exp $ */ +/* + * Copyright (c) 2015, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internal interfaces to write a ctags(1) file. + * For use by the mandoc(1) ASCII and UTF-8 formatters only. + */ + +struct tag_files { + char ofn[80]; /* Output file name. */ + char tfn[80]; /* Tag file name. */ + FILE *tfs; /* Tag file object. */ + int ofd; /* Original output file descriptor. */ + pid_t tcpgid; /* Process group controlling the terminal. */ + pid_t pager_pid; /* Process ID of the pager. */ +}; + + +struct tag_files *term_tag_init(const char *, const char *, const char *); +void term_tag_write(struct roff_node *, size_t); +int term_tag_close(void); +void term_tag_unlink(void); diff --git a/usr/src/cmd/mandoc/tree.c b/usr/src/cmd/mandoc/tree.c index 649c0804c8..12e841a50b 100644 --- a/usr/src/cmd/mandoc/tree.c +++ b/usr/src/cmd/mandoc/tree.c @@ -1,7 +1,7 @@ -/* $Id: tree.c,v 1.84 2019/01/01 05:56:34 schwarze Exp $ */ +/* $Id: tree.c,v 1.91 2021/09/07 10:59:18 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Formatting module to let mandoc(1) show + * a human readable representation of the syntax tree. */ #include "config.h" @@ -33,7 +36,9 @@ #include "eqn.h" #include "main.h" +static void print_attr(const struct roff_node *); static void print_box(const struct eqn_box *, int); +static void print_cellt(enum tbl_cellt); static void print_man(const struct roff_node *, int); static void print_meta(const struct roff_meta *); static void print_mdoc(const struct roff_node *, int); @@ -187,28 +192,8 @@ print_mdoc(const struct roff_node *n, int indent) if (argv[i].sz > 0) printf(" ]"); } - - putchar(' '); - if (n->flags & NODE_DELIMO) - putchar('('); - if (n->flags & NODE_LINE) - putchar('*'); - printf("%d:%d", n->line, n->pos + 1); - if (n->flags & NODE_DELIMC) - putchar(')'); - if (n->flags & NODE_EOS) - putchar('.'); - if (n->flags & NODE_BROKEN) - printf(" BROKEN"); - if (n->flags & NODE_NOFILL) - printf(" NOFILL"); - if (n->flags & NODE_NOSRC) - printf(" NOSRC"); - if (n->flags & NODE_NOPRT) - printf(" NOPRT"); - putchar('\n'); + print_attr(n); } - if (n->eqn) print_box(n->eqn->first, indent + 4); if (n->child) @@ -289,19 +274,9 @@ print_man(const struct roff_node *n, int indent) } else { for (i = 0; i < indent; i++) putchar(' '); - printf("%s (%s) ", p, t); - if (n->flags & NODE_LINE) - putchar('*'); - printf("%d:%d", n->line, n->pos + 1); - if (n->flags & NODE_DELIMC) - putchar(')'); - if (n->flags & NODE_EOS) - putchar('.'); - if (n->flags & NODE_NOFILL) - printf(" NOFILL"); - putchar('\n'); + printf("%s (%s)", p, t); + print_attr(n); } - if (n->eqn) print_box(n->eqn->first, indent + 4); if (n->child) @@ -312,6 +287,40 @@ print_man(const struct roff_node *n, int indent) } static void +print_attr(const struct roff_node *n) +{ + putchar(' '); + if (n->flags & NODE_DELIMO) + putchar('('); + if (n->flags & NODE_LINE) + putchar('*'); + printf("%d:%d", n->line, n->pos + 1); + if (n->flags & NODE_DELIMC) + putchar(')'); + if (n->flags & NODE_EOS) + putchar('.'); + if (n->flags & NODE_ID) { + printf(" ID"); + if (n->flags & NODE_HREF) + printf("=HREF"); + } else if (n->flags & NODE_HREF) + printf(" HREF"); + else if (n->tag != NULL) + printf(" STRAYTAG"); + if (n->tag != NULL) + printf("=%s", n->tag); + if (n->flags & NODE_BROKEN) + printf(" BROKEN"); + if (n->flags & NODE_NOFILL) + printf(" NOFILL"); + if (n->flags & NODE_NOSRC) + printf(" NOSRC"); + if (n->flags & NODE_NOPRT) + printf(" NOPRT"); + putchar('\n'); +} + +static void print_box(const struct eqn_box *ep, int indent) { int i; @@ -374,11 +383,72 @@ print_box(const struct eqn_box *ep, int indent) } static void +print_cellt(enum tbl_cellt pos) +{ + switch(pos) { + case TBL_CELL_LEFT: + putchar('L'); + break; + case TBL_CELL_LONG: + putchar('a'); + break; + case TBL_CELL_CENTRE: + putchar('c'); + break; + case TBL_CELL_RIGHT: + putchar('r'); + break; + case TBL_CELL_NUMBER: + putchar('n'); + break; + case TBL_CELL_SPAN: + putchar('s'); + break; + case TBL_CELL_DOWN: + putchar('^'); + break; + case TBL_CELL_HORIZ: + putchar('-'); + break; + case TBL_CELL_DHORIZ: + putchar('='); + break; + case TBL_CELL_MAX: + putchar('#'); + break; + } +} + +static void print_span(const struct tbl_span *sp, int indent) { const struct tbl_dat *dp; + const struct tbl_cell *cp; int i; + if (sp->prev == NULL) { + for (i = 0; i < indent; i++) + putchar(' '); + printf("%d", sp->opts->cols); + if (sp->opts->opts & TBL_OPT_CENTRE) + fputs(" center", stdout); + if (sp->opts->opts & TBL_OPT_EXPAND) + fputs(" expand", stdout); + if (sp->opts->opts & TBL_OPT_ALLBOX) + fputs(" allbox", stdout); + if (sp->opts->opts & TBL_OPT_BOX) + fputs(" box", stdout); + if (sp->opts->opts & TBL_OPT_DBOX) + fputs(" doublebox", stdout); + if (sp->opts->opts & TBL_OPT_NOKEEP) + fputs(" nokeep", stdout); + if (sp->opts->opts & TBL_OPT_NOSPACE) + fputs(" nospaces", stdout); + if (sp->opts->opts & TBL_OPT_NOWARN) + fputs(" nowarn", stdout); + printf(" (tbl options) %d:1\n", sp->line); + } + for (i = 0; i < indent; i++) putchar(' '); @@ -392,31 +462,72 @@ print_span(const struct tbl_span *sp, int indent) putchar(' '); break; default: + for (cp = sp->layout->first; cp != NULL; cp = cp->next) + print_cellt(cp->pos); + putchar(' '); for (dp = sp->first; dp; dp = dp->next) { + if ((cp = dp->layout) == NULL) + putchar('*'); + else { + printf("%d", cp->col); + print_cellt(dp->layout->pos); + switch (cp->font) { + case ESCAPE_FONTROMAN: + break; + case ESCAPE_FONTBOLD: + putchar('b'); + break; + case ESCAPE_FONTITALIC: + putchar('i'); + break; + case ESCAPE_FONTBI: + fputs("bi", stdout); + break; + case ESCAPE_FONTCR: + putchar('c'); + break; + case ESCAPE_FONTCB: + fputs("cb", stdout); + break; + case ESCAPE_FONTCI: + fputs("ci", stdout); + break; + default: + abort(); + } + if (cp->flags & TBL_CELL_TALIGN) + putchar('t'); + if (cp->flags & TBL_CELL_UP) + putchar('u'); + if (cp->flags & TBL_CELL_BALIGN) + putchar('d'); + if (cp->flags & TBL_CELL_WIGN) + putchar('z'); + if (cp->flags & TBL_CELL_EQUAL) + putchar('e'); + if (cp->flags & TBL_CELL_WMAX) + putchar('x'); + } switch (dp->pos) { case TBL_DATA_HORIZ: case TBL_DATA_NHORIZ: putchar('-'); - putchar(' '); - continue; + break; case TBL_DATA_DHORIZ: case TBL_DATA_NDHORIZ: putchar('='); - putchar(' '); - continue; + break; default: + putchar(dp->block ? '{' : '['); + if (dp->string != NULL) + fputs(dp->string, stdout); + putchar(dp->block ? '}' : ']'); 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(' '); } break; diff --git a/usr/src/man/man1/mandoc.1 b/usr/src/man/man1/mandoc.1 index f2765db33a..eaafde344f 100644 --- a/usr/src/man/man1/mandoc.1 +++ b/usr/src/man/man1/mandoc.1 @@ -1,7 +1,7 @@ -.\" $Id: mandoc.1,v 1.237 2019/02/23 18:53:54 schwarze Exp $ +.\" $OpenBSD: mandoc.1,v 1.166 2020/02/15 15:28:01 schwarze Exp $ .\" +.\" Copyright (c) 2012, 2014-2021 Ingo Schwarze <schwarze@openbsd.org> .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> -.\" 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 @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: February 23 2019 $ +.Dd $Mdocdate: August 14 2021 $ .Dt MANDOC 1 .Os .Sh NAME @@ -52,13 +52,13 @@ The options are as follows: If the standard output is a terminal device and .Fl c is not specified, use -.Xr more 1 +.Xr less 1 to paginate the output, just like .Xr man 1 would. .It Fl c Copy the formatted manual pages to the standard output without using -.Xr more 1 +.Xr less 1 to paginate them. This is the default. It can be specified to override @@ -238,10 +238,28 @@ where is the back-space character number 8. Emboldened characters are rendered as .Sq c Ns \e[bs] Ns c . +This markup is typically converted to appropriate terminal sequences by +the pager or +.Xr ul 1 . +To remove the markup, pipe the output to +.Xr col 1 +.Fl b +instead. .Pp The special characters documented in .Xr mandoc_char 5 are rendered best-effort in an ASCII equivalent. +In particular, opening and closing +.Sq single quotes +are represented as characters number 0x60 and 0x27, respectively, +which agrees with all ASCII standards from 1965 to the latest +revision (2012) and which matches the traditional way in which +.Xr roff 5 +formatters represent single quotes in ASCII output. +This correct ASCII rendering may look strange with modern +Unicode-compatible fonts because contrary to ASCII, Unicode uses +the code point U+0060 for the grave accent only, never for an opening +quote. .Pp The following .Fl O @@ -256,34 +274,16 @@ and seven for .Xr man 5 . Increasing this is not recommended; it may result in degraded formatting, for example overfull lines or ugly line breaks. -This markup is typically converted to appropriate terminal sequences by -the pager or -.Xr ul 1 . -To remove the markup, pipe the output to -.Xr col 1 -.Fl b -instead. When output is to a pager on a terminal that is less than 66 columns wide, the default is reduced to three columns. .It Cm mdoc Format -In particular, opening and closing -.Sq single quotes -are represented as characters number 0x60 and 0x27, respectively, -which agrees with all ASCII standards from 1965 to the latest -revision (2012) and which matches the traditional way in which -.Xr roff 7 -formatters represent single quotes in ASCII output. -This correct ASCII rendering may look strange with modern -Unicode-compatible fonts because contrary to ASCII, Unicode uses -the code point U+0060 for the grave accent only, never for an opening -quote. .Xr man 5 input files in .Xr mdoc 5 output style. -Specifically, this suppresses the two additional blank lines near the -top and the bottom of each page, and it implies +This prints the operating system name rather than the page title +on the right side of the footer line, and it implies .Fl O Cm indent Ns =5 . One useful application is for checking that .Fl T Cm man @@ -391,6 +391,30 @@ The file is used for an external style-sheet. This must be a valid absolute or relative URI. +.It Cm tag Ns Op = Ns Ar term +Same syntax and semantics as for +.Sx ASCII Output . +This is implemented by passing a +.Ic file:// +URI ending in a fragment identifier to the pager +rather than passing merely a file name. +When using this argument, use a pager supporting such URIs, for example +.Bd -literal -offset 3n +MANPAGER='lynx -force_html' man -T html -O tag=MANPAGER man +MANPAGER='w3m -T text/html' man -T html -O tag=toc mandoc +.Ed +.Pp +Consequently, for HTML output, this argument does not work with +.Xr more 1 +or +.Xr less 1 . +For example, +.Ql MANPAGER=less man -T html -O tag=toc mandoc +does not work because +.Xr less 1 +does not support +.Ic file:// +URIs. .It Cm toc If an input file contains at least two non-standard sections, print a table of contents near the beginning of the output. @@ -424,13 +448,15 @@ This is useful for distributing manual sources to legacy systems lacking .Xr mdoc 5 formatters. +Embedded +.Xr eqn 5 +and +.Xr tbl 5 +code is not supported. .Pp If the input format of a file is .Xr man 5 , -the input is copied to the output, expanding any -.Xr mandoc_roff 5 -.Ic so -requests. +the input is copied to the output. The parser is also run, and as usual, the .Fl W level controls which @@ -605,6 +631,31 @@ When .Sx Locale Output is selected, it decides whether to use ASCII or UTF-8 output format. It never affects the interpretation of input files. +.It Ev MANPAGER +Any non-empty value of the environment variable +.Ev MANPAGER +is used instead of the standard pagination program, +.Xr less 1 ; +see +.Xr man 1 +for details. +Only used if +.Fl a +or +.Fl l +is specified. +.It Ev PAGER +Specifies the pagination program to use when +.Ev MANPAGER +is not defined. +If neither PAGER nor MANPAGER is defined, +.Xr less 1 +is used. +Only used if +.Fl a +or +.Fl l +is specified. .El .Sh EXIT STATUS The @@ -653,7 +704,7 @@ No input files have been read. .It 6 An operating system error occurred, for example exhaustion of memory, file descriptors, or process table entries. -Such errors cause +Such errors may cause .Nm to exit at once, possibly in the middle of parsing or formatting a file. .El @@ -733,6 +784,13 @@ fields. .Pp Message levels have the following meanings: .Bl -tag -width "warning" +.It Cm syserr +An operating system error occurred. +There isn't necessarily anything wrong with the input files. +Output may all the same be missing or incomplete. +.It Cm badarg +Invalid command line arguments were specified. +No input files have been read and no output is produced. .It Cm unsupp An input file uses unsupported low-level .Xr mandoc_roff 5 @@ -781,8 +839,7 @@ Messages of the .Cm error , and .Cm unsupp -levels except those about non-existent or unreadable input files -are hidden unless their level, or a lower level, is requested using a +levels are hidden unless their level, or a lower level, is requested using a .Fl W option or .Fl T Cm lint @@ -846,18 +903,10 @@ generated by CVS or .Ic NetBSD keyword substitution as conventionally used in these operating systems. -.It Sy "referenced manual not found" -.Pq mdoc -An -.Ic \&Xr -macro references a manual page that is not found in the base system. -The path to look for base system manuals is configurable at compile -time and defaults to -.Pa /usr/share/man : /usr/X11R6/man . .El .Ss Style suggestions .Bl -ohang -.It Sy "legacy man(7) date format" +.It Sy "legacy man(5) date format" .Pq mdoc The .Ic \&Dd @@ -940,6 +989,35 @@ list contains two consecutive entries describing the same .Ic \&Er number. +.It Sy "referenced manual not found" +.Pq mdoc +An +.Ic \&Xr +macro references a manual page that was not found. +When running with +.Fl W Cm base , +the search is restricted to the base system, by default to +.Pa /usr/share/man . +This path can be configured at compile time using the +.Dv MANPATH_BASE +preprocessor macro. +When running with +.Fl W Cm style , +the search is done along the full search path as described in the +.Xr man 1 +manual page, respecting the +.Fl m +and +.Fl M +command line options, the +.Ev MANPATH +environment variable, the +.Xr man.conf 5 +file and falling back to the default of +.Pa /usr/share/man : Ns Pa /usr/X11R6/man : Ns Pa /usr/local/man , +also configurable at compile time using the +.Dv MANPATH_DEFAULT +preprocessor macro. .It Sy "trailing delimiter" .Pq mdoc The last argument of an @@ -969,6 +1047,9 @@ An request occurs even though the document already switched to no-fill mode and did not switch back to fill mode yet. It has no effect. +.It Sy "input text line longer than 80 bytes" +Consider breaking the input text line +at one of the blank characters before column 80. .It Sy "verbatim \(dq--\(dq, maybe consider using \e(em" .Pq mdoc Even though the ASCII output device renders an em-dash as @@ -1022,7 +1103,21 @@ macro lacks the mandatory section argument. The section number in a .Ic \&Dt line is invalid, but still used. -.It Sy "missing date, using today's date" +.It Sy "filename/section mismatch" +.Pq mdoc , man +The name of the input file being processed is known and its file +name extension starts with a non-zero digit, but the +.Ic \&Dt +or +.Ic \&TH +macro contains a +.Ar section +argument that starts with a different non-zero digit. +The +.Ar section +argument is used as provided anyway. +Consider checking whether the file name or the argument need a correction. +.It Sy "missing date, using \(dq\(dq" .Pq mdoc, man The document was parsed as .Xr mdoc 5 @@ -1623,15 +1718,15 @@ An macro has an argument other than .Cm on or +.Cm off . +The invalid argument is moved out of the macro, which leaves the macro +empty, causing it to toggle the spacing mode. .It Sy "argument contains two font escapes" .Pq roff The second argument of a .Ic char request contains more than one font escape sequence. A wrong font may remain active after using the character. -.Cm off . -The invalid argument is moved out of the macro, which leaves the macro -empty, causing it to toggle the spacing mode. .It Sy "unknown font, skipping request" .Pq man , tbl A @@ -1661,6 +1756,9 @@ However, for compatibility with groff, blank lines in fill mode are replaced with .Ic \&sp requests. +To request a paragraph break, use +.Ic \&Pp +instead of a blank line. .It Sy "tab in filled text" .Pq mdoc , man The meaning of tab characters is only well-defined in non-fill mode: @@ -1760,6 +1858,10 @@ The invalid character is discarded. A table layout specification contains an opening parenthesis, but no matching closing parenthesis. The rest of the input line, starting from the parenthesis, has no effect. +.It Sy "ignoring excessive spacing in tbl layout" +.Pq tbl +A spacing modifier in a table layout is unreasonably large. +The default spacing of 3n is used instead. .It Sy "tbl without any data cells" .Pq tbl A table does not contain any data cells. @@ -2196,6 +2298,71 @@ or macro or of an undefined macro. The macro is ignored, and its arguments are handled as if they were a text line. +.It Sy "skipping tbl in -Tman mode" +.Pq mdoc , tbl +An input file contains the +.Ic \&TS +macro. +This message is only generated in +.Fl T Cm man +output mode, where +.Xr tbl 5 +input is not supported. +.It Sy "skipping eqn in -Tman mode" +.Pq mdoc , eqn +An input file contains the +.Ic \&EQ +macro. +This message is only generated in +.Fl T Cm man +output mode, where +.Xr eqn 5 +input is not supported. +.El +.Ss Bad command line arguments +.Bl -ohang +.It Sy "bad command line argument" +The argument following one of the +.Fl IKMmOTW +command line options is invalid, or a +.Ar file +given as a command line argument cannot be opened. +.It Sy "duplicate command line argument" +The +.Fl I +command line option was specified twice. +.It Sy "option has a superfluous value" +An argument to the +.Fl O +option has a value but does not accept one. +.It Sy "missing option value" +An argument to the +.Fl O +option has no argument but requires one. +.It Sy "bad option value" +An argument to the +.Fl O +.Cm indent +or +.Cm width +option has an invalid value. +.It Sy "duplicate option value" +The same +.Fl O +option is specified more than once. +.It Sy "no such tag" +The +.Fl O Cm tag +option was specified but the tag was not found in any of the displayed +manual pages. +.It Sy "\-Tmarkdown unsupported for man(5) input" +.Pq man +The +.Fl T Cm markdown +option was specified but an input file uses the +.Xr man 5 +language. +No output is produced for that input file. .El .Sh SEE ALSO .Xr eqn 5 , diff --git a/usr/src/man/man5/eqn.5 b/usr/src/man/man5/eqn.5 index 28baf08109..c8c33e7214 100644 --- a/usr/src/man/man5/eqn.5 +++ b/usr/src/man/man5/eqn.5 @@ -1,4 +1,4 @@ -.\" $Id: eqn.7,v 1.37 2017/09/04 10:35:27 schwarze Exp $ +.\" $Id: eqn.7,v 1.39 2020/01/10 11:55:04 schwarze Exp $ .\" .\" Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 4 2017 $ +.Dd $Mdocdate: January 10 2020 $ .Dt EQN 5 .Os .Sh NAME @@ -44,28 +44,16 @@ specification (see .Sx SEE ALSO for references). .Pp -Equations within -.Xr mdoc 5 -or -.Xr man 5 -documents are enclosed by the standalone -.Sq \&.EQ -and -.Sq \&.EN -tags. -Equations are multi-line blocks consisting of formulas and control -statements. -.Sh EQUATION STRUCTURE -Each equation is bracketed by -.Sq \&.EQ -and -.Sq \&.EN -strings. -.Em Note : -these are not the same as -.Xr mandoc_roff 5 -macros, and may only be invoked as -.Sq \&.EQ . +An equation starts with an input line containing exactly the characters +.Sq \&.EQ , +may contain multiple input lines, and ends with an input line +containing exactly the characters +.Sq \&.EN . +Equivalently, an equation can be given in the middle of a single +text input line by surrounding it with the equation delimiters +defined with the +.Cm delim +statement. .Pp The equation grammar is as follows, where quoted strings are case-sensitive literals in the input: @@ -178,6 +166,25 @@ statement is a synonym for while .Cm tdefine is discarded. +.It Cm delim +This statement takes a string argument consisting of two bytes, +to be used as the opening and closing delimiters for equations +in the middle of text input lines. +Conventionally, the dollar sign is used for both delimiters, +as follows: +.Bd -literal -offset indent +\&.EQ +delim $$ +\&.EN +An equation like $sin pi = 0$ can now be entered +in the middle of a text input line. +.Ed +.Pp +The special statement +.Cm delim off +temporarily disables previously declared delimiters and +.Cm delim on +reenables them. .It Cm gfont Set the default font of subsequent output. Its syntax is as follows: @@ -470,7 +477,7 @@ commands are also ignored. .%T System for Typesetting Mathematics .%J Communications of the ACM .%V 18 -.%P 151\(en157 +.%P pp. 151\(en157 .%D March, 1975 .Re .Rs diff --git a/usr/src/man/man5/mandoc_char.5 b/usr/src/man/man5/mandoc_char.5 index b6b1d7848b..fd2b91800f 100644 --- a/usr/src/man/man5/mandoc_char.5 +++ b/usr/src/man/man5/mandoc_char.5 @@ -1,8 +1,8 @@ -.\" $Id: mandoc_char.7,v 1.75 2018/12/15 19:30:26 schwarze Exp $ +.\" $Id: mandoc_char.7,v 1.78 2020/10/31 11:45:16 schwarze Exp $ .\" .\" Copyright (c) 2003 Jason McIntyre <jmc@openbsd.org> .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2011,2013,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2011,2013,2015,2017-2020 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: December 15 2018 $ +.Dd $Mdocdate: October 31 2020 $ .Dt MANDOC_CHAR 5 .Os .Sh NAME @@ -107,8 +107,8 @@ supporting it, for example in .Fl T Cm utf8 and .Fl T Cm html . -But currently, no practically relevant manual page formatter actually -requires that subtlety, so in manual pages just write plain +But currently, no practically relevant manual page formatter requires +that subtlety, so in manual pages, it is sufficient to write plain .Sq - to represent hyphen, minus, and hyphen-minus. .Pp @@ -261,15 +261,15 @@ subsection of the .Xr mandoc_roff 5 manual. .Pp -Spacing: +Spaces, non-breaking unless stated otherwise: .Bl -column "Input" "Description" -offset indent -compact .It Em Input Ta Em Description -.It Sq \e\ \& Ta unpaddable non-breaking space -.It \e\(ti Ta paddable non-breaking space -.It \e0 Ta digit-width space allowing line break +.It Sq \e\ \& Ta unpaddable space +.It \e\(ti Ta paddable space +.It \e0 Ta digit-width space .It \e| Ta one-sixth \e(em narrow space, zero width in nroff mode .It \e^ Ta one-twelfth \e(em half-narrow space, zero width in nroff -.It \e& Ta zero-width non-breaking space +.It \e& Ta zero-width space .It \e) Ta zero-width space transparent to end-of-sentence detection .It \e% Ta zero-width space allowing hyphenation .It \e: Ta zero-width space allowing line break @@ -709,11 +709,6 @@ Their syntax is similar to special characters, using and .Sq \e*[N] .Pq N-character . -For details, see the -.Em Predefined Strings -subsection of the -.Xr mandoc_roff 5 -manual. .Bl -column "Input" "Rendered" "Description" -offset indent .It Em Input Ta Em Rendered Ta Em Description .It \e*(Ba Ta \*(Ba Ta vertical bar diff --git a/usr/src/man/man5/mandoc_roff.5 b/usr/src/man/man5/mandoc_roff.5 index 0a9aa7cbd8..17e9d2e09c 100644 --- a/usr/src/man/man5/mandoc_roff.5 +++ b/usr/src/man/man5/mandoc_roff.5 @@ -1,4 +1,4 @@ -.\" $Id: roff.7,v 1.111 2019/01/01 03:45:29 schwarze Exp $ +.\" $Id: roff.7,v 1.116 2021/09/18 12:23:06 schwarze Exp $ .\" .\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 1 2019 $ +.Dd $Mdocdate: September 18 2021 $ .Dt ROFF 5 .Os .Sh NAME @@ -315,12 +315,18 @@ delimiters The proper spacing is also intelligently preserved if a sentence ends at the boundary of a macro line. .Pp +If an input line happens to end with a period, exclamation or question +mark that isn't the end of a sentence, append a zero-width space +.Pq Sq \e& . +.Pp Examples: .Bd -literal -offset indent -compact Do not end sentences mid-line like this. Instead, end a sentence like this. A macro would end like this: \&.Xr mandoc 1 \&. +An abbreviation at the end of an input line needs escaping, e.g.\e& +like this. .Ed .Sh REQUEST SYNTAX A request or macro line consists of: @@ -503,10 +509,9 @@ This is a Heirloom extension and currently unsupported. .It Ic \&br Break the output line. .It Ic \&break -Break out of a +Break out of the innermost .Ic \&while loop. -Currently unsupported. .It Ic \&breakchar Ar char ... Optional line break characters. This is a Heirloom extension and currently ignored. @@ -619,7 +624,7 @@ Its syntax can be either .Pp or .Bd -literal -offset indent -.Pf . Ic \&de Ar macroname Ar endmacro +.Pf . Ic \&de Ar macroname endmacro .Ar definition .Pf . Ar endmacro .Ed @@ -1306,7 +1311,7 @@ Change the output line length. If the .Ar width argument is omitted, the line length is reset to its previous value. -The default setting for terminal output is 58n. +The default setting for terminal output is 78n. If a sign is given, the line length is added to or subtracted from; otherwise, it is set to the provided value. Using this request in new manuals is discouraged for several reasons, @@ -1815,7 +1820,7 @@ Each number may be followed by one optional scaling unit described below .Sx Scaling Widths . The following equations hold: .Bd -literal -offset indent -1i = 6v = 6P = 10m = 10n = 52p = 1000M = 240u = 240 +1i = 6v = 6P = 10m = 10n = 72p = 1000M = 240u = 240 254c = 100i = 24000u = 24000 1f = 65536u = 65536 .Ed @@ -2326,7 +2331,7 @@ for .At v2 , then ported nroff to C as troff, which Brian W. Kernighan released with .At v7 . -In 1989, James Clarke re-implemented troff in C++, naming it groff. +In 1989, James Clark re-implemented troff in C++, naming it groff. .Sh AUTHORS .An -nosplit This diff --git a/usr/src/man/man5/mdoc.5 b/usr/src/man/man5/mdoc.5 index 603755894e..4c85b861a7 100644 --- a/usr/src/man/man5/mdoc.5 +++ b/usr/src/man/man5/mdoc.5 @@ -1,7 +1,7 @@ -.\" $Id: mdoc.7,v 1.276 2019/02/07 15:45:53 schwarze Exp $ +.\" $Id: mdoc.7,v 1.287 2021/07/29 17:32:01 schwarze Exp $ .\" .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2010, 2011, 2013-2018 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2010, 2011, 2013-2020 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -19,7 +19,7 @@ .\" Copyright 2014 Garrett D'Amore <garrett@damore.org> .\" Copyright 2018 Nexenta Systems, Inc. .\" -.Dd $Mdocdate: February 7 2019 $ +.Dd $Mdocdate: July 29 2021 $ .Dt MDOC 5 .Os .Sh NAME @@ -140,7 +140,7 @@ The utility processes files ... \&.\e\(dq .Sh IMPLEMENTATION NOTES \&.\e\(dq .Sh RETURN VALUES -\&.\e\(dq For sections 2, 3, and 9 only. +\&.\e\(dq For sections 2, 3, 7, and 9 only. \&.\e\(dq .Sh CONTEXT \&.\e\(dq For section 9 functions only. \&.\e\(dq .Sh ENVIRONMENT @@ -151,7 +151,7 @@ utility processes files ... \&.\e\(dq .Sh EXAMPLES \&.\e\(dq .Sh DIAGNOSTICS \&.\e\(dq .Sh ERRORS -\&.\e\(dq For sections 2, 3, and 9 only. +\&.\e\(dq For sections 2, 3, 7, and 9 only. \&.\e\(dq .Sh ARCHITECTURE \&.\e\(dq .Sh CODE SET INDEPENDENCE \&.\e\(dq For sections 1, 1M, and 3 only. @@ -302,7 +302,7 @@ utility does this, that, and the other. It usually follows with a breakdown of the options (if documenting a command), such as: .Bd -literal -offset indent -The arguments are as follows: +The options are as follows: \&.Bl \-tag \-width Ds \&.It Fl v Print verbose information. @@ -385,7 +385,7 @@ See .Ic \&Bl .Fl diag . .It Em ERRORS -Documents error handling in sections 2, 3, and 9. +Documents error handling in sections 2, 3, 7, and 9. .Pp See .Ic \&Er . @@ -562,11 +562,11 @@ in the alphabetical .Sx MACRO REFERENCE . .Ss Document preamble and NAME section macros .Bl -column "Brq, Bro, Brc" description -.It Sx \&Dd Ta document date: Cm $\&Mdocdate$ | Ar month day , year -.It Sx \&Dt Ta document title: Ar TITLE section Op Ar arch -.It Sx \&Os Ta operating system version: Op Ar system Op Ar version -.It Sx \&Nm Ta document name (one argument) -.It Sx \&Nd Ta document description (one line) +.It Ic \&Dd Ta document date: Cm $\&Mdocdate$ | Ar month day , year +.It Ic \&Dt Ta document title: Ar TITLE section Op Ar arch +.It Ic \&Os Ta operating system version: Op Ar system Op Ar version +.It Ic \&Nm Ta document name (one argument) +.It Ic \&Nd Ta document description (one line) .El .Ss Sections and cross references .Bl -column "Brq, Bro, Brc" description @@ -574,6 +574,7 @@ in the alphabetical .It Ic \&Ss Ta subsection header (one line) .It Ic \&Sx Ta internal cross reference to a section or subsection .It Ic \&Xr Ta cross reference to another manual page: Ar name section +.It Ic \&Tg Ta tag the definition of a Ar term Pq <= 1 arguments .It Ic \&Pp Ta start a text paragraph (no arguments) .El .Ss Displays and lists @@ -721,6 +722,13 @@ block. Book or journal page number of an .Ic \&Rs block. +Conventionally, the argument starts with +.Ql p.\& +for a single page or +.Ql pp.\& +for a range of pages, for example: +.Pp +.Dl .%P pp. 42\e(en47 .It Ic \&%Q Ar name Institutional author (school, government, etc.) of an .Ic \&Rs @@ -749,6 +757,7 @@ Close an .Ic \&Ao block. Does not have any tail arguments. +.Tg Ad .It Ic \&Ad Ar address Memory address. Do not use this for postal addresses. @@ -756,6 +765,7 @@ Do not use this for postal addresses. Examples: .Dl \&.Ad [0,$] .Dl \&.Ad 0x00000000 +.Tg An .It Ic \&An Fl split | nosplit | Ar first_name ... last_name Author name. Can be used both for the authors of the program, function, or driver @@ -796,6 +806,7 @@ This macro is almost never useful. See .Ic \&Aq for more details. +.Tg Ap .It Ic \&Ap Inserts an apostrophe without any surrounding whitespace. This is generally used as a grammatical device when referring to the verb @@ -803,6 +814,7 @@ form of a function. .Pp Examples: .Dl \&.Fn execve \&Ap d +.Tg Aq .It Ic \&Aq Ar line Enclose the rest of the input line in angle brackets. The only important use case is for email addresses. @@ -847,6 +859,7 @@ as needed. .Pp See also .Ic \&Ao . +.Tg Ar .It Ic \&Ar Op Ar placeholder ... Command arguments. If an argument is not provided, the string @@ -865,6 +878,7 @@ for fixed strings to be passed verbatim as arguments, use .Ic \&Fl or .Ic \&Cm . +.Tg At .It Ic \&At Op Ar version Formats an .At @@ -902,6 +916,7 @@ Close a .Ic \&Bo block. Does not have any tail arguments. +.Tg Bd .It Ic \&Bd Fl Ns Ar type Oo Fl offset Ar width Oc Op Fl compact Begin a display block. Display blocks are used to select a different indentation and @@ -992,6 +1007,7 @@ See also .Ic \&D1 and .Ic \&Dl . +.Tg Bf .It Ic \&Bf Fl emphasis | literal | symbolic | Cm \&Em | \&Li | \&Sy Change the font mode for a scoped block of text. The @@ -1018,6 +1034,7 @@ See also .Ic \&Em , and .Ic \&Sy . +.Tg Bk .It Ic \&Bk Fl words For each macro, keep its output together on the same output line, until the end of the macro or the end of the input line is reached, @@ -1040,6 +1057,7 @@ macro line: .Pp Be careful in using over-long lines within a keep block! Doing so will clobber the right margin. +.Tg Bl .It Xo .Ic \&Bl .Fl Ns Ar type @@ -1182,6 +1200,7 @@ Examples: .Pp See also .Ic \&Bq . +.Tg Bq .It Ic \&Bq Ar line Encloses its arguments in square brackets. .Pp @@ -1215,6 +1234,7 @@ Examples: .Pp See also .Ic \&Brq . +.Tg Brq .It Ic \&Brq Ar line Encloses its arguments in curly braces. .Pp @@ -1223,6 +1243,7 @@ Examples: .Pp See also .Ic \&Bro . +.Tg Bsx .It Ic \&Bsx Op Ar version Format the .Bsx @@ -1245,6 +1266,7 @@ and Supported only for compatibility, do not use this in new manuals. Prints .Dq is currently in beta test. +.Tg Bx .It Ic \&Bx Op Ar version Op Ar variant Format the .Bx @@ -1264,6 +1286,7 @@ See also .Ic \&Nx , and .Ic \&Ox . +.Tg Cd .It Ic \&Cd Ar line Kernel configuration declaration. It is found in pages for @@ -1279,9 +1302,12 @@ whitespace and align consecutive .Ic \&Cd declarations. This practise is discouraged. +.Tg Cm .It Ic \&Cm Ar keyword ... Command modifiers. -Typically used for fixed strings passed as arguments, unless +Typically used for fixed strings passed as arguments to interactive +commands, to commands in interpreted scripts, or to configuration +file directives, unless .Ic \&Fl is more appropriate. Also useful when specifying configuration options or keys. @@ -1290,8 +1316,10 @@ Examples: .Dl ".Nm mt Fl f Ar device Cm rewind" .Dl ".Nm ps Fl o Cm pid , Ns Cm command" .Dl ".Nm dd Cm if= Ns Ar file1 Cm of= Ns Ar file2" -.Dl ".Cm IdentityFile Pa ~/.ssh/id_rsa" -.Dl ".Cm LogLevel Dv DEBUG" +.Dl ".Ic set Fl o Cm vi" +.Dl ".Ic lookup Cm file bind" +.Dl ".Ic permit Ar identity Op Cm as Ar target" +.Tg D1 .It Ic \&D1 Ar line One-line indented display. This is formatted by the default rules and is useful for simple indented @@ -1317,8 +1345,10 @@ Close a .Ic \&Do block. Does not have any tail arguments. +.Tg Dd .It Ic \&Dd Cm $\&Mdocdate$ | Ar month day , year -Document date for display in the page footer. +Document date for display in the page footer, +by convention the date of the last change. This is the mandatory first macro of any .Nm manual. @@ -1364,6 +1394,7 @@ See also .Ic \&Dt and .Ic \&Os . +.Tg Dl .It Ic \&Dl Ar line One-line indented display. This is formatted as literal text and is useful for commands and @@ -1392,6 +1423,7 @@ April is the cruellest month .Pp See also .Ic \&Dq . +.Tg Dq .It Ic \&Dq Ar line Encloses its arguments in .Dq typographic @@ -1408,6 +1440,7 @@ See also .Ic \&Sq , and .Ic \&Do . +.Tg Dt .It Ic \&Dt Ar TITLE section Op Ar arch Document title for display in the page header. This is the mandatory second macro of any @@ -1452,6 +1485,7 @@ for variable symbols, and .Ic \&Fd for listing preprocessor variable definitions in the .Em SYNOPSIS . +.Tg Dx .It Ic \&Dx Op Ar version Format the .Dx @@ -1493,6 +1527,7 @@ End a list context started by .Ic \&Bl . See also .Ic \&It . +.Tg Em .It Ic \&Em Ar word ... Request an italic font. If the output device does not provide that, underline. @@ -1532,6 +1567,7 @@ or any of the other enclosure macros. It encloses its argument in the delimiters specified by the last .Ic \&Es macro. +.Tg Eo .It Ic \&Eo Op Ar opening_delimiter An arbitrary enclosure. The @@ -1539,6 +1575,7 @@ The argument is used as the enclosure head, for example, specifying \e(lq will emulate .Ic \&Do . +.Tg Er .It Ic \&Er Ar identifier ... Error constants for definitions of the .Va errno @@ -1561,6 +1598,7 @@ or any of the other enclosure macros. It takes two arguments, defining the delimiters to be used by subsequent .Ic \&En macros. +.Tg Ev .It Ic \&Ev Ar identifier ... Environmental variables such as those specified in .Xr environ 5 . @@ -1572,6 +1610,7 @@ Examples: See also .Ic \&Dv for general constants. +.Tg Ex .It Ic \&Ex Fl std Op Ar utility ... Insert a standard sentence regarding command exit values of 0 on success and >0 on failure. @@ -1588,6 +1627,7 @@ arguments are treated as separate utilities. .Pp See also .Ic \&Rv . +.Tg Fa .It Ic \&Fa Ar argument ... Function argument or parameter. Each argument may be a name and a type (recommended for the @@ -1625,6 +1665,7 @@ See also .It Ic \&Fc End a function context started by .Ic \&Fo . +.Tg Fd .It Ic \&Fd Pf # Ar directive Op Ar argument ... Preprocessor directive, in particular for listing it in the .Em SYNOPSIS . @@ -1645,25 +1686,33 @@ See also .Ic \&In , and .Ic \&Dv . +.Tg Fl .It Ic \&Fl Op Ar word ... Command-line flag or option. Used when listing arguments to command-line utilities. -Prints a fixed-width hyphen -.Sq \- -directly followed by each argument. -If no arguments are provided, a hyphen is printed followed by a space. -If the argument is a macro, a hyphen is prefixed to the subsequent macro -output. +For each argument, prints an ASCII hyphen-minus character +.Sq \- , +immediately followed by the argument. +If no arguments are provided, a hyphen-minus is printed followed by a space. +If the argument is a macro, a hyphen-minus is prefixed +to the subsequent macro output. .Pp Examples: -.Dl ".Fl R Op Fl H | L | P" -.Dl ".Op Fl 1AaCcdFfgHhikLlmnopqRrSsTtux" -.Dl ".Fl type Cm d Fl name Pa CVS" -.Dl ".Fl Ar signal_number" -.Dl ".Fl o Fl" +.Dl ".Nm du Op Fl H | L | P" +.Dl ".Nm ls Op Fl 1AaCcdFfgHhikLlmnopqRrSsTtux" +.Dl ".Nm route Cm add Fl inet Ar destination gateway" +.Dl ".Nm locate.updatedb Op Fl \e-fcodes Ns = Ns Ar dbfile" +.Dl ".Nm aucat Fl o Fl" +.Dl ".Nm kill Fl Ar signal_number" +.Pp +For GNU-sytle long options, escaping the additional hyphen-minus is not +strictly required, but may be safer with future versions of GNU troff; see +.Xr mandoc_char 5 +for details. .Pp See also .Ic \&Cm . +.Tg Fn .It Ic \&Fn Ar funcname Op Ar argument ... A function name. .Pp @@ -1692,6 +1741,7 @@ See also .Ic \&Fo , and .Ic \&Ft . +.Tg Fo .It Ic \&Fo Ar funcname Begin a function block. This is a multi-line version of @@ -1726,6 +1776,7 @@ This macro is obsolete. No replacement markup is needed. .Pp It was used to show numerical function return values in an italic font. +.Tg Ft .It Ic \&Ft Ar functype A function type. .Pp @@ -1745,6 +1796,7 @@ See also .Ic \&Fn , and .Ic \&Fo . +.Tg Fx .It Ic \&Fx Op Ar version Format the .Fx @@ -1767,11 +1819,12 @@ and This macro is not implemented in .Xr mandoc 1 . It was used to include the contents of a (header) file literally. +.Tg Ic .It Ic \&Ic Ar keyword ... -Designate an internal or interactive command. -This is similar to -.Ic \&Cm -but used for instructions rather than values. +Internal or interactive command, or configuration instruction +in a configuration file. +See also +.Ic \&Cm . .Pp Examples: .Dl \&.Ic :wq @@ -1786,6 +1839,7 @@ or is preferred for displaying code samples; the .Ic \&Ic macro is used when referring to an individual command name. +.Tg In .It Ic \&In Ar filename The name of an include file. This macro is most often used in section 2, 3, and 9 manual pages. @@ -1805,6 +1859,7 @@ Examples: .Pp See also .Sx MANUAL STRUCTURE . +.Tg It .It Ic \&It Op Ar head A list item. The syntax of this macro depends on the list type. @@ -1895,6 +1950,7 @@ but not the whitespace before the semicolon. .Pp See also .Ic \&Bl . +.Tg Lb .It Ic \&Lb Cm lib Ns Ar name Specify a library. .Pp @@ -1914,7 +1970,8 @@ section as described in .Pp Examples: .Dl \&.Lb libz -.Dl \&.Lb mdoc +.Dl \&.Lb libmandoc +.Tg Li .It Ic \&Li Ar word ... Request a typewriter (literal) font. Deprecated because on terminal output devices, this is usually @@ -1925,24 +1982,27 @@ For literal displays, use or .Ic \&Bd Fl literal Pq multi-line instead. +.Tg Lk .It Ic \&Lk Ar uri Op Ar display_name Format a hyperlink. .Pp Examples: -.Dl \&.Lk http://bsd.lv \(dqThe BSD.lv Project\(dq -.Dl \&.Lk http://bsd.lv +.Dl \&.Lk https://bsd.lv \(dqThe BSD.lv Project\(dq +.Dl \&.Lk https://bsd.lv .Pp See also .Ic \&Mt . .It Ic \&Lp Deprecated synonym for .Ic \&Pp . +.Tg Ms .It Ic \&Ms Ar name Display a mathematical symbol. .Pp Examples: .Dl \&.Ms sigma .Dl \&.Ms aleph +.Tg Mt .It Ic \&Mt Ar localpart Ns @ Ns Ar domain Format a .Dq mailto: @@ -1951,6 +2011,7 @@ hyperlink. Examples: .Dl \&.Mt discuss@manpages.bsd.lv .Dl \&.An Kristaps Dzonsons \&Aq \&Mt kristaps@bsd.lv +.Tg Nd .It Ic \&Nd Ar line A one line description of the manual's content. This is the mandatory last macro of the @@ -1973,6 +2034,7 @@ arguments and will display macros verbatim. .Pp See also .Ic \&Nm . +.Tg Nm .It Ic \&Nm Op Ar name The name of the manual page, or \(em in particular in section 1 pages \(em of an additional command or feature documented in @@ -2010,6 +2072,7 @@ of section 2, 3 and 9 manual pages, use the macro rather than .Ic \&Nm to mark up the name of the manual page. +.Tg No .It Ic \&No Ar word ... Normal text. Closes the scope of any preceding in-line macro. @@ -2034,6 +2097,7 @@ See also .Ic \&Ql , and .Ic \&Sy . +.Tg Ns .It Ic \&Ns Suppress a space between the output of the preceding macro and the following text or macro. @@ -2053,6 +2117,7 @@ See also .Ic \&No and .Ic \&Sm . +.Tg Nx .It Ic \&Nx Op Ar version Format the .Nx @@ -2085,6 +2150,7 @@ Examples: \&.Op Fl flag Ns Ar value \&.Oc .Ed +.Tg Op .It Ic \&Op Ar line Optional part of a command line. Prints the argument(s) in brackets. @@ -2098,6 +2164,7 @@ Examples: .Pp See also .Ic \&Oo . +.Tg Os .It Ic \&Os Op Ar system Op Ar version Operating system version for display in the page footer. This is the mandatory third macro of @@ -2140,6 +2207,7 @@ Historical .Nm packages described it as .Dq "old function type (FORTRAN)" . +.Tg Ox .It Ic \&Ox Op Ar version Format the .Ox @@ -2158,6 +2226,7 @@ See also .Ic \&Fx , and .Ic \&Nx . +.Tg Pa .It Ic \&Pa Ar name ... An absolute or relative file system path, or a file or directory name. If an argument is not provided, the character @@ -2173,6 +2242,7 @@ See also .It Ic \&Pc Close parenthesised context opened by .Ic \&Po . +.Tg Pf .It Ic \&Pf Ar prefix macro Op Ar argument ... Removes the space between its argument and the following macro. It is equivalent to: @@ -2196,6 +2266,7 @@ and .It Ic \&Po Ar block Multi-line version of .Ic \&Pq . +.Tg Pp .It Ic \&Pp Break a paragraph. This will assert vertical space between prior and subsequent macros @@ -2212,6 +2283,7 @@ or lists unless the .Fl compact flag is given. +.Tg Pq .It Ic \&Pq Ar line Parenthesised enclosure. .Pp @@ -2220,6 +2292,7 @@ See also .It Ic \&Qc Close quoted context opened by .Ic \&Qo . +.Tg Ql .It Ic \&Ql Ar line In-line literal display. This can be used for complete command invocations and for multi-word @@ -2233,6 +2306,7 @@ and .It Ic \&Qo Ar block Multi-line version of .Ic \&Qq . +.Tg Qq .It Ic \&Qq Ar line Encloses its arguments in .Qq typewriter @@ -2250,6 +2324,7 @@ Close an .Ic \&Rs block. Does not have any tail arguments. +.Tg Rs .It Ic \&Rs Begin a bibliographic .Pq Dq reference @@ -2290,6 +2365,7 @@ If an block is used within a SEE ALSO section, a vertical space is asserted before the rendered output, else the block continues on the current line. +.Tg Rv .It Ic \&Rv Fl std Op Ar function ... Insert a standard sentence regarding a function call's return value of 0 on success and \-1 on error, with the @@ -2310,6 +2386,7 @@ See also .It Ic \&Sc Close single-quoted context opened by .Ic \&So . +.Tg Sh .It Ic \&Sh Ar TITLE LINE Begin a new section. For a list of conventional manual sections, see @@ -2328,6 +2405,7 @@ See also .Ic \&Ss , and .Ic \&Sx . +.Tg Sm .It Ic \&Sm Op Cm on | off Switches the spacing mode for output generated from macros. .Pp @@ -2346,6 +2424,7 @@ Using this is not recommended because it makes the code harder to read. .It Ic \&So Ar block Multi-line version of .Ic \&Sq . +.Tg Sq .It Ic \&Sq Ar line Encloses its arguments in .Sq typewriter @@ -2356,6 +2435,7 @@ See also .Ic \&Qq , and .Ic \&So . +.Tg Ss .It Ic \&Ss Ar Title line Begin a new subsection. Unlike with @@ -2378,6 +2458,7 @@ See also .Ic \&Sh , and .Ic \&Sx . +.Tg St .It Ic \&St Fl Ns Ar abbreviation Replace an abbreviation for a standard with the full form. The following standards are recognised. @@ -2588,6 +2669,7 @@ Ethernet local area networks. .St -ieee1275-94 .El .El +.Tg Sx .It Ic \&Sx Ar Title line Reference a section or subsection in the same manual page. The referenced section or subsection name must be identical to the @@ -2600,6 +2682,7 @@ See also .Ic \&Sh and .Ic \&Ss . +.Tg Sy .It Ic \&Sy Ar word ... Request a boldface font. .Pp @@ -2625,11 +2708,56 @@ See also .Ic \&No , and .Ic \&Ql . +.Tg Ta .It Ic \&Ta Table cell separator in .Ic \&Bl Fl column lists; can only be used below .Ic \&It . +.Tg Tg +.It Ic \&Tg Op Ar term +Announce that the next input line starts a definition of the +.Ar term . +This macro must appear alone on its own input line. +The argument defaults to the first argument of the first macro +on the next line. +The argument may not contain whitespace characters, not even when it is quoted. +This macro is a +.Xr mandoc 1 +extension and is typically ignored by other formatters. +.Pp +When viewing terminal output with +.Xr less 1 , +the interactive +.Ic :t +command can be used to go to the definition of the +.Ar term +as described for the +.Ev MANPAGER +variable in +.Xr man 1 ; +when producing HTML output, a fragment identifier +.Pq Ic id No attribute +is generated, to be used for deep linking to this place of the document. +.Pp +In most cases, adding a +.Ic \&Tg +macro would be redundant because +.Xr mandoc 1 +is able to automatically tag most definitions. +This macro is intended for cases where automatic tagging of a +.Ar term +is unsatisfactory, for example if a definition is not tagged +automatically (false negative) or if places are tagged that do +not define the +.Ar term +(false positives). +When there is at least one +.Ic \&Tg +macro for a +.Ar term , +no other places are automatically marked as definitions of that +.Ar term . .It Ic \&Tn Ar word ... Supported only for compatibility, do not use this in new manuals. Even though the macro name @@ -2644,6 +2772,7 @@ Prints out Supported only for compatibility, do not use this in new manuals. Prints out .Dq Ux . +.Tg Va .It Ic \&Va Oo Ar type Oc Ar identifier ... A variable name. .Pp @@ -2658,6 +2787,7 @@ For declarations of global variables in the .Em SYNOPSIS section, use .Ic \&Vt . +.Tg Vt .It Ic \&Vt Ar type Op Ar identifier A variable type. .Pp @@ -2701,6 +2831,7 @@ beyond the end of the input line. This macro originally existed to work around the 9-argument limit of historic .Xr mandoc_roff 5 . +.Tg Xr .It Ic \&Xr Ar name section Link to another manual .Pq Qq cross-reference . @@ -2763,7 +2894,7 @@ column, if applicable, describes closure rules. .Ss Block full-explicit Multi-line scope closed by an explicit closing macro. All macros contains bodies; only -.Ic \s&Bf +.Ic \&Bf and .Pq optionally .Ic \&Bl @@ -2994,6 +3125,7 @@ then the macro accepts an arbitrary number of arguments. .It Ic \&St Ta \&No Ta Yes Ta 1 .It Ic \&Sx Ta Yes Ta Yes Ta >0 .It Ic \&Sy Ta Yes Ta Yes Ta >0 +.It Ic \&Tg Ta \&No Ta \&No Ta <2 .It Ic \&Tn Ta Yes Ta Yes Ta >0 .It Ic \&Ud Ta \&No Ta \&No Ta 0 .It Ic \&Ux Ta Yes Ta Yes Ta n @@ -3060,7 +3192,7 @@ exclamation mark Note that even a period preceded by a backslash .Pq Sq \e.\& gets this special handling; use -.Sq \e&. +.Sq \e&.\& to prevent that. .Pp Many in-line macros interrupt their scope when they encounter @@ -3087,6 +3219,13 @@ in the same way as a plain .Sq \&| character. Using this predefined string is not recommended in new manuals. +.Pp +Appending a zero-width space +.Pq Sq \e& +to the end of an input line is also useful to prevent the interpretation +of a trailing period, exclamation or question mark as the end of a +sentence, for example when an abbreviation happens to occur +at the end of a text or macro input line. .Ss Font handling In .Nm @@ -3114,17 +3253,6 @@ The following problematic behaviour is found in groff: .Pp .Bl -dash -compact .It -.Ic \&Dd -with non-standard arguments behaves very strangely. -When there are three arguments, they are printed verbatim. -Any other number of arguments is replaced by the current date, -but without any arguments the string -.Dq Epoch -is printed. -.It -.Ic \&Lk -only accepts a single link-name argument; the remainder is misformatted. -.It .Ic \&Pa does not format its arguments when used in the FILES section under certain list types. @@ -3132,9 +3260,6 @@ certain list types. .Ic \&Ta can only be called by other macros, but not at the beginning of a line. .It -.Ic \&%C -is not implemented (up to and including groff-1.22.2). -.It .Sq \ef .Pq font face and @@ -3184,10 +3309,16 @@ but produces large indentations. .Xr tbl 5 .Pp The web page -.Lk http://mandoc.bsd.lv/mdoc/ "extended documentation for the mdoc language" +.Lk https://mandoc.bsd.lv/mdoc/ "extended documentation for the mdoc language" provides a few tutorial-style pages for beginners, an extensive style guide for advanced authors, and an alphabetic index helping to choose the best macros for various kinds of content. +.Pp +The manual page +.Lk https://man.voidlinux.org/groff_mdoc "groff_mdoc(7)" +contained in the +.Dq groff +package documents exactly the same language in a somewhat different style. .Sh HISTORY The .Nm diff --git a/usr/src/man/man5/tbl.5 b/usr/src/man/man5/tbl.5 index 21cb9ddcdd..4bbeb9379e 100644 --- a/usr/src/man/man5/tbl.5 +++ b/usr/src/man/man5/tbl.5 @@ -1,4 +1,4 @@ -.\" $Id: tbl.7,v 1.34 2019/03/02 21:03:02 schwarze Exp $ +.\" $Id: tbl.7,v 1.37 2021/09/18 12:34:27 schwarze Exp $ .\" .\" Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2014,2015,2017,2018,2019 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 2 2019 $ +.Dd $Mdocdate: September 18 2021 $ .Dt TBL 5 .Os .Sh NAME @@ -94,7 +94,7 @@ Allow page breaks within the table. This is a GNU extension and currently ignored. .It Cm nospaces Ignore leading and trailing spaces in data cells. -This is a GNU extension and currently ignored. +This is a GNU extension. .It Cm nowarn Suppress warnings about tables exceeding the current line length. This is a GNU extension and currently ignored. @@ -178,10 +178,11 @@ of any other column also having the .Cm e modifier. .It Cm f -The next character selects the font to use for this cell. +The next one or two characters select the font to use for this cell. +One-character font names must be followed by a blank or period. See the .Xr mandoc_roff 5 -manual for supported one-character font names. +manual for supported font names. .It Cm i Use an italic font for the contents of this cell. .It Cm m @@ -416,7 +417,7 @@ equations inside tables. .Xr mdoc 5 .Rs .%A M. E. Lesk -.%T Tbl\(emA Program to Format Tables +.%T Tbl \(em A Program to Format Tables .%D June 11, 1976 .Re .Sh HISTORY diff --git a/usr/src/tools/mandoc/Makefile b/usr/src/tools/mandoc/Makefile index 8a865d599e..5a0d085b3c 100644 --- a/usr/src/tools/mandoc/Makefile +++ b/usr/src/tools/mandoc/Makefile @@ -20,13 +20,7 @@ LCDIR= $(SRC)/lib/libc/port include $(SRC)/tools/Makefile.tools include $(CMDDIR)/Makefile.common -OBJS += fts.o \ - reallocarray.o \ - recallocarray.o \ - strtonum.o - CPPFLAGS += -_gcc=-nostdinc -CPPFLAGS += -I. -include fts.h CPPFLAGS += -I$(NATIVE_ADJUNCT)/include LDFLAGS += -L$(NATIVE_ADJUNCT)/lib -R$(NATIVE_ADJUNCT)/lib diff --git a/usr/src/tools/mandoc/fts.h b/usr/src/tools/mandoc/fts.h deleted file mode 100644 index 2a59cabb72..0000000000 --- a/usr/src/tools/mandoc/fts.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * This file provides required compatibility definitions and prototypes to build - * tools version of mandoc on older systems. - */ - -#ifndef _COMPAT_H -#define _COMPAT_H - -#include <sys/types.h> - -extern void *reallocarray(void *, size_t, size_t); -extern void *recallocarray(void *, size_t, size_t, size_t); -extern long long strtonum(const char *, long long, long long, const char **); - -#define be32toh ntohl -#define htobe32 htonl - -#endif /* !_COMPAT_H */ - -/* - * Below is the copy of fts.h. - */ - -#ifndef _FTS_H -#define _FTS_H - -#include <sys/types.h> - -typedef struct { - struct _ftsent *fts_cur; /* current node */ - struct _ftsent *fts_child; /* linked list of children */ - struct _ftsent **fts_array; /* sort array */ - dev_t fts_dev; /* starting device # */ - char *fts_path; /* path for this descent */ - int fts_rfd; /* fd for root */ - size_t fts_pathlen; /* sizeof(path) */ - int fts_nitems; /* elements in the sort array */ - int (*fts_compar)(); /* compare function */ - -#define FTS_COMFOLLOW 0x0001 /* follow command line symlinks */ -#define FTS_LOGICAL 0x0002 /* logical walk */ -#define FTS_NOCHDIR 0x0004 /* don't change directories */ -#define FTS_NOSTAT 0x0008 /* don't get stat info */ -#define FTS_PHYSICAL 0x0010 /* physical walk */ -#define FTS_SEEDOT 0x0020 /* return dot and dot-dot */ -#define FTS_XDEV 0x0040 /* don't cross devices */ -#define FTS_OPTIONMASK 0x00ff /* valid user option mask */ - -#define FTS_NAMEONLY 0x1000 /* (private) child names only */ -#define FTS_STOP 0x2000 /* (private) unrecoverable error */ - int fts_options; /* fts_open options, global flags */ -} FTS; - -typedef struct _ftsent { - struct _ftsent *fts_cycle; /* cycle node */ - struct _ftsent *fts_parent; /* parent directory */ - struct _ftsent *fts_link; /* next file in directory */ - long fts_number; /* local numeric value */ - void *fts_pointer; /* local address value */ - char *fts_accpath; /* access path */ - char *fts_path; /* root path */ - int fts_errno; /* errno for this node */ - int fts_symfd; /* fd for symlink */ - size_t fts_pathlen; /* strlen(fts_path) */ - size_t fts_namelen; /* strlen(fts_name) */ - - ino_t fts_ino; /* inode */ - dev_t fts_dev; /* device */ - nlink_t fts_nlink; /* link count */ - -#define FTS_ROOTPARENTLEVEL -1 -#define FTS_ROOTLEVEL 0 -#define FTS_MAXLEVEL 0x7fffffff - int fts_level; /* depth (-1 to N) */ - -#define FTS_D 1 /* preorder directory */ -#define FTS_DC 2 /* directory that causes cycles */ -#define FTS_DEFAULT 3 /* none of the above */ -#define FTS_DNR 4 /* unreadable directory */ -#define FTS_DOT 5 /* dot or dot-dot */ -#define FTS_DP 6 /* postorder directory */ -#define FTS_ERR 7 /* error; errno is set */ -#define FTS_F 8 /* regular file */ -#define FTS_INIT 9 /* initialized only */ -#define FTS_NS 10 /* stat(2) failed */ -#define FTS_NSOK 11 /* no stat(2) requested */ -#define FTS_SL 12 /* symbolic link */ -#define FTS_SLNONE 13 /* symbolic link without target */ - unsigned short fts_info; /* user flags for FTSENT structure */ - -#define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ -#define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ - unsigned short fts_flags; /* private flags for FTSENT structure */ - -#define FTS_AGAIN 1 /* read node again */ -#define FTS_FOLLOW 2 /* follow symbolic link */ -#define FTS_NOINSTR 3 /* no instructions */ -#define FTS_SKIP 4 /* discard node */ - unsigned short fts_instr; /* fts_set() instructions */ - - unsigned short fts_spare; /* unused */ - - struct stat *fts_statp; /* stat(2) information */ - char fts_name[1]; /* file name */ -} FTSENT; - -FTSENT *fts_children(FTS *, int); -int fts_close(FTS *); -FTS *fts_open(char * const *, int, - int (*)(const FTSENT **, const FTSENT **)); -FTSENT *fts_read(FTS *); -int fts_set(FTS *, FTSENT *, int); - -#endif /* !_FTS_H */ |