summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorYuri Pankov <yuri.pankov@nexenta.com>2017-05-28 20:36:38 +0300
committerRichard Lowe <richlowe@richlowe.net>2017-06-21 16:46:29 -0400
commita40ea1a7d80eee1b409e9dcc2e48c730988147ea (patch)
tree83ffaf56553e3c7cda31f650a37a1e0c7f2b5b90 /usr/src
parent299c9e70a71f87365f8f88fdea2cccbd0d5b8db6 (diff)
downloadillumos-joyent-a40ea1a7d80eee1b409e9dcc2e48c730988147ea.tar.gz
8297 update mdocml to 1.14.1
Reviewed by: Robert Mustacchi <rm@joyent.com> Approved by: Richard Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/mandoc/Makefile.common8
-rw-r--r--usr/src/cmd/mandoc/chars.c6
-rw-r--r--usr/src/cmd/mandoc/config.h53
-rw-r--r--usr/src/cmd/mandoc/dba.c508
-rw-r--r--usr/src/cmd/mandoc/dba.h50
-rw-r--r--usr/src/cmd/mandoc/dba_array.c188
-rw-r--r--usr/src/cmd/mandoc/dba_array.h47
-rw-r--r--usr/src/cmd/mandoc/dba_read.c72
-rw-r--r--usr/src/cmd/mandoc/dba_write.c127
-rw-r--r--usr/src/cmd/mandoc/dba_write.h30
-rw-r--r--usr/src/cmd/mandoc/dbm.c480
-rw-r--r--usr/src/cmd/mandoc/dbm.h68
-rw-r--r--usr/src/cmd/mandoc/dbm_map.c194
-rw-r--r--usr/src/cmd/mandoc/dbm_map.h29
-rw-r--r--usr/src/cmd/mandoc/eqn_html.c64
-rw-r--r--usr/src/cmd/mandoc/eqn_term.c13
-rw-r--r--usr/src/cmd/mandoc/html.c733
-rw-r--r--usr/src/cmd/mandoc/html.h74
-rw-r--r--usr/src/cmd/mandoc/libmandoc.h10
-rw-r--r--usr/src/cmd/mandoc/libmdoc.h4
-rw-r--r--usr/src/cmd/mandoc/main.c81
-rw-r--r--usr/src/cmd/mandoc/main.h4
-rw-r--r--usr/src/cmd/mandoc/man.c10
-rw-r--r--usr/src/cmd/mandoc/man_hash.c4
-rw-r--r--usr/src/cmd/mandoc/man_html.c376
-rw-r--r--usr/src/cmd/mandoc/man_macro.c12
-rw-r--r--usr/src/cmd/mandoc/man_term.c26
-rw-r--r--usr/src/cmd/mandoc/manconf.h3
-rw-r--r--usr/src/cmd/mandoc/mandoc.h3
-rw-r--r--usr/src/cmd/mandoc/mandoc_aux.h5
-rw-r--r--usr/src/cmd/mandoc/mandocdb.c2316
-rw-r--r--usr/src/cmd/mandoc/manpath.c153
-rw-r--r--usr/src/cmd/mandoc/mansearch.c751
-rw-r--r--usr/src/cmd/mandoc/mansearch.h21
-rw-r--r--usr/src/cmd/mandoc/mdoc.c91
-rw-r--r--usr/src/cmd/mandoc/mdoc_argv.c5
-rw-r--r--usr/src/cmd/mandoc/mdoc_hash.c4
-rw-r--r--usr/src/cmd/mandoc/mdoc_html.c1158
-rw-r--r--usr/src/cmd/mandoc/mdoc_macro.c145
-rw-r--r--usr/src/cmd/mandoc/mdoc_man.c197
-rw-r--r--usr/src/cmd/mandoc/mdoc_state.c4
-rw-r--r--usr/src/cmd/mandoc/mdoc_term.c323
-rw-r--r--usr/src/cmd/mandoc/mdoc_validate.c627
-rw-r--r--usr/src/cmd/mandoc/preconv.c14
-rw-r--r--usr/src/cmd/mandoc/read.c26
-rw-r--r--usr/src/cmd/mandoc/roff.c52
-rw-r--r--usr/src/cmd/mandoc/roff.h28
-rw-r--r--usr/src/cmd/mandoc/tag.c71
-rw-r--r--usr/src/cmd/mandoc/tbl_html.c33
-rw-r--r--usr/src/cmd/mandoc/term.c24
-rw-r--r--usr/src/cmd/mandoc/term_ascii.c4
-rw-r--r--usr/src/cmd/mandoc/term_ps.c110
-rw-r--r--usr/src/cmd/mandoc/tree.c50
-rw-r--r--usr/src/lib/libc/port/gen/fts.c2
-rw-r--r--usr/src/man/man1/mandoc.191
-rw-r--r--usr/src/man/man5/mandoc_char.528
-rw-r--r--usr/src/man/man5/mdoc.551
-rw-r--r--usr/src/tools/mandoc/Makefile5
-rw-r--r--usr/src/tools/mandoc/fts.h128
-rw-r--r--usr/src/tools/scripts/webrev.sh155
60 files changed, 7452 insertions, 2497 deletions
diff --git a/usr/src/cmd/mandoc/Makefile.common b/usr/src/cmd/mandoc/Makefile.common
index c7df2d15a8..9accbd1244 100644
--- a/usr/src/cmd/mandoc/Makefile.common
+++ b/usr/src/cmd/mandoc/Makefile.common
@@ -18,6 +18,12 @@ PROG= mandoc
OBJS= att.o \
chars.o \
+ dba.o \
+ dba_array.o \
+ dba_read.o \
+ dba_write.o \
+ dbm.o \
+ dbm_map.o \
eqn.o \
eqn_html.o \
eqn_term.o \
@@ -34,6 +40,8 @@ OBJS= att.o \
mandoc.o \
mandoc_aux.o \
mandoc_ohash.o \
+ mandocdb.o \
+ mansearch.o \
mdoc.o \
mdoc_argv.o \
mdoc_hash.o \
diff --git a/usr/src/cmd/mandoc/chars.c b/usr/src/cmd/mandoc/chars.c
index c2cfaf8184..f1f5d5d78c 100644
--- a/usr/src/cmd/mandoc/chars.c
+++ b/usr/src/cmd/mandoc/chars.c
@@ -1,4 +1,4 @@
-/* $Id: chars.c,v 1.68 2015/10/13 22:59:54 schwarze Exp $ */
+/* $Id: chars.c,v 1.69 2017/02/17 18:28:06 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -101,8 +101,8 @@ static struct ln lines[] = {
{ "bq", ",", 0x201a },
{ "lq", "\"", 0x201c },
{ "rq", "\"", 0x201d },
- { "Lq", "``", 0x201c },
- { "Rq", "''", 0x201d },
+ { "Lq", "\"", 0x201c },
+ { "Rq", "\"", 0x201d },
{ "oq", "`", 0x2018 },
{ "cq", "\'", 0x2019 },
{ "aq", "\'", 0x0027 },
diff --git a/usr/src/cmd/mandoc/config.h b/usr/src/cmd/mandoc/config.h
index a9106ae4f4..301e1f218d 100644
--- a/usr/src/cmd/mandoc/config.h
+++ b/usr/src/cmd/mandoc/config.h
@@ -1,28 +1,47 @@
#ifndef MANDOC_CONFIG_H
-#define MANDOC_CONFIG_H
+#define MANDOC_CONFIG_H
#include <sys/types.h>
-#include <stdio.h>
+#define MAN_CONF_FILE "/etc/man.conf"
+#define MANPATH_DEFAULT "/usr/share/man:/usr/gnu/share/man"
+
+#define UTF8_LOCALE "en_US.UTF-8"
+#define EFTYPE EINVAL
+#define O_DIRECTORY 0
+
+#define HAVE_CMSG_XPG42 0
+#define HAVE_DIRENT_NAMLEN 0
+#define HAVE_ENDIAN 1
#define HAVE_ERR 1
-#define HAVE_MANPATH 1
-#define HAVE_MMAP 1
-#define HAVE_OHASH 0
+#define HAVE_FTS 1
+#define HAVE_FTS_COMPARE_CONST 0
+#define HAVE_GETLINE 1
+#define HAVE_GETSUBOPT 1
+#define HAVE_ISBLANK 1
+#define HAVE_MKDTEMP 1
+#define HAVE_NTOHL 1
+#define HAVE_OHASH 0
#define HAVE_PLEDGE 0
#define HAVE_PROGNAME 1
-#define HAVE_REALLOCARRAY 0
+#define HAVE_REALLOCARRAY 1
+#define HAVE_REWB_BSD 1
+#define HAVE_REWB_SYSV 1
#define HAVE_SANDBOX_INIT 0
-#define HAVE_SQLITE3 0
-#define HAVE_STRPTIME 1
-#define HAVE_STRTONUM 0
-#define HAVE_WCHAR 1
-
-#define BINM_APROPOS "apropos"
-#define BINM_MAN "man"
-#define BINM_WHATIS "whatis"
-#define BINM_MAKEWHATIS "man -w"
+#define HAVE_STRCASESTR 1
+#define HAVE_STRINGLIST 0
+#define HAVE_STRLCAT 1
+#define HAVE_STRLCPY 1
+#define HAVE_STRPTIME 1
+#define HAVE_STRSEP 1
+#define HAVE_STRTONUM 1
+#define HAVE_SYS_ENDIAN 0
+#define HAVE_VASPRINTF 1
+#define HAVE_WCHAR 1
-extern void *reallocarray(void *, size_t, size_t);
-extern long long strtonum(const char *, long long, long long, const char **);
+#define BINM_APROPOS "apropos"
+#define BINM_MAN "man"
+#define BINM_WHATIS "whatis"
+#define BINM_MAKEWHATIS "man -w"
#endif /* MANDOC_CONFIG_H */
diff --git a/usr/src/cmd/mandoc/dba.c b/usr/src/cmd/mandoc/dba.c
new file mode 100644
index 0000000000..ee43933de3
--- /dev/null
+++ b/usr/src/cmd/mandoc/dba.c
@@ -0,0 +1,508 @@
+/* $Id: dba.c,v 1.10 2017/02/17 14:43:54 schwarze Exp $ */
+/*
+ * Copyright (c) 2016, 2017 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Allocation-based version of the mandoc database, for read-write access.
+ * The interface is defined in "dba.h".
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#if HAVE_ENDIAN
+#include <endian.h>
+#elif HAVE_SYS_ENDIAN
+#include <sys/endian.h>
+#elif HAVE_NTOHL
+#include <arpa/inet.h>
+#endif
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mandoc_aux.h"
+#include "mandoc_ohash.h"
+#include "mansearch.h"
+#include "dba_write.h"
+#include "dba_array.h"
+#include "dba.h"
+
+struct macro_entry {
+ struct dba_array *pages;
+ char value[];
+};
+
+static void *prepend(const char *, char);
+static void dba_pages_write(struct dba_array *);
+static int compare_names(const void *, const void *);
+static int compare_strings(const void *, const void *);
+
+static struct macro_entry
+ *get_macro_entry(struct ohash *, const char *, int32_t);
+static void dba_macros_write(struct dba_array *);
+static void dba_macro_write(struct ohash *);
+static int compare_entries(const void *, const void *);
+
+
+/*** top-level functions **********************************************/
+
+struct dba *
+dba_new(int32_t npages)
+{
+ struct dba *dba;
+ struct ohash *macro;
+ int32_t im;
+
+ dba = mandoc_malloc(sizeof(*dba));
+ dba->pages = dba_array_new(npages, DBA_GROW);
+ dba->macros = dba_array_new(MACRO_MAX, 0);
+ for (im = 0; im < MACRO_MAX; im++) {
+ macro = mandoc_malloc(sizeof(*macro));
+ mandoc_ohash_init(macro, 4,
+ offsetof(struct macro_entry, value));
+ dba_array_set(dba->macros, im, macro);
+ }
+ return dba;
+}
+
+void
+dba_free(struct dba *dba)
+{
+ struct dba_array *page;
+ struct ohash *macro;
+ struct macro_entry *entry;
+ unsigned int slot;
+
+ dba_array_FOREACH(dba->macros, macro) {
+ for (entry = ohash_first(macro, &slot); entry != NULL;
+ entry = ohash_next(macro, &slot)) {
+ dba_array_free(entry->pages);
+ free(entry);
+ }
+ ohash_delete(macro);
+ free(macro);
+ }
+ dba_array_free(dba->macros);
+
+ dba_array_undel(dba->pages);
+ dba_array_FOREACH(dba->pages, page) {
+ dba_array_free(dba_array_get(page, DBP_NAME));
+ dba_array_free(dba_array_get(page, DBP_SECT));
+ dba_array_free(dba_array_get(page, DBP_ARCH));
+ free(dba_array_get(page, DBP_DESC));
+ dba_array_free(dba_array_get(page, DBP_FILE));
+ dba_array_free(page);
+ }
+ dba_array_free(dba->pages);
+
+ free(dba);
+}
+
+/*
+ * Write the complete mandoc database to disk; the format is:
+ * - One integer each for magic and version.
+ * - One pointer each to the macros table and to the final magic.
+ * - The pages table.
+ * - The macros table.
+ * - And at the very end, the magic integer again.
+ */
+int
+dba_write(const char *fname, struct dba *dba)
+{
+ int save_errno;
+ int32_t pos_end, pos_macros, pos_macros_ptr;
+
+ if (dba_open(fname) == -1)
+ return -1;
+ dba_int_write(MANDOCDB_MAGIC);
+ dba_int_write(MANDOCDB_VERSION);
+ pos_macros_ptr = dba_skip(1, 2);
+ dba_pages_write(dba->pages);
+ pos_macros = dba_tell();
+ dba_macros_write(dba->macros);
+ pos_end = dba_tell();
+ dba_int_write(MANDOCDB_MAGIC);
+ dba_seek(pos_macros_ptr);
+ dba_int_write(pos_macros);
+ dba_int_write(pos_end);
+ if (dba_close() == -1) {
+ save_errno = errno;
+ unlink(fname);
+ errno = save_errno;
+ return -1;
+ }
+ return 0;
+}
+
+
+/*** functions for handling pages *************************************/
+
+/*
+ * Create a new page and append it to the pages table.
+ */
+struct dba_array *
+dba_page_new(struct dba_array *pages, const char *arch,
+ const char *desc, const char *file, enum form form)
+{
+ struct dba_array *page, *entry;
+
+ page = dba_array_new(DBP_MAX, 0);
+ entry = dba_array_new(1, DBA_STR | DBA_GROW);
+ dba_array_add(page, entry);
+ entry = dba_array_new(1, DBA_STR | DBA_GROW);
+ dba_array_add(page, entry);
+ if (arch != NULL && *arch != '\0') {
+ entry = dba_array_new(1, DBA_STR | DBA_GROW);
+ dba_array_add(entry, (void *)arch);
+ } else
+ entry = NULL;
+ dba_array_add(page, entry);
+ dba_array_add(page, mandoc_strdup(desc));
+ entry = dba_array_new(1, DBA_STR | DBA_GROW);
+ dba_array_add(entry, prepend(file, form));
+ dba_array_add(page, entry);
+ dba_array_add(pages, page);
+ return page;
+}
+
+/*
+ * Add a section, architecture, or file name to an existing page.
+ * Passing the NULL pointer for the architecture makes the page MI.
+ * In that case, any earlier or later architectures are ignored.
+ */
+void
+dba_page_add(struct dba_array *page, int32_t ie, const char *str)
+{
+ struct dba_array *entries;
+ char *entry;
+
+ entries = dba_array_get(page, ie);
+ if (ie == DBP_ARCH) {
+ if (entries == NULL)
+ return;
+ if (str == NULL || *str == '\0') {
+ dba_array_free(entries);
+ dba_array_set(page, DBP_ARCH, NULL);
+ return;
+ }
+ }
+ if (*str == '\0')
+ return;
+ dba_array_FOREACH(entries, entry) {
+ if (ie == DBP_FILE && *entry < ' ')
+ entry++;
+ if (strcmp(entry, str) == 0)
+ return;
+ }
+ dba_array_add(entries, (void *)str);
+}
+
+/*
+ * Add an additional name to an existing page.
+ */
+void
+dba_page_alias(struct dba_array *page, const char *name, uint64_t mask)
+{
+ struct dba_array *entries;
+ char *entry;
+ char maskbyte;
+
+ if (*name == '\0')
+ return;
+ maskbyte = mask & NAME_MASK;
+ entries = dba_array_get(page, DBP_NAME);
+ dba_array_FOREACH(entries, entry) {
+ if (strcmp(entry + 1, name) == 0) {
+ *entry |= maskbyte;
+ return;
+ }
+ }
+ dba_array_add(entries, prepend(name, maskbyte));
+}
+
+/*
+ * Return a pointer to a temporary copy of instr with inbyte prepended.
+ */
+static void *
+prepend(const char *instr, char inbyte)
+{
+ static char *outstr = NULL;
+ static size_t outlen = 0;
+ size_t newlen;
+
+ newlen = strlen(instr) + 1;
+ if (newlen > outlen) {
+ outstr = mandoc_realloc(outstr, newlen + 1);
+ outlen = newlen;
+ }
+ *outstr = inbyte;
+ memcpy(outstr + 1, instr, newlen);
+ return outstr;
+}
+
+/*
+ * Write the pages table to disk; the format is:
+ * - One integer containing the number of pages.
+ * - For each page, five pointers to the names, sections,
+ * architectures, description, and file names of the page.
+ * MI pages write 0 instead of the architecture pointer.
+ * - One list each for names, sections, architectures, descriptions and
+ * file names. The description for each page ends with a NUL byte.
+ * For all the other lists, each string ends with a NUL byte,
+ * and the last string for a page ends with two NUL bytes.
+ * - To assure alignment of following integers,
+ * the end is padded with NUL bytes up to a multiple of four bytes.
+ */
+static void
+dba_pages_write(struct dba_array *pages)
+{
+ struct dba_array *page, *entry;
+ int32_t pos_pages, pos_end;
+
+ pos_pages = dba_array_writelen(pages, 5);
+ dba_array_FOREACH(pages, page) {
+ dba_array_setpos(page, DBP_NAME, dba_tell());
+ entry = dba_array_get(page, DBP_NAME);
+ dba_array_sort(entry, compare_names);
+ dba_array_writelst(entry);
+ }
+ dba_array_FOREACH(pages, page) {
+ dba_array_setpos(page, DBP_SECT, dba_tell());
+ entry = dba_array_get(page, DBP_SECT);
+ dba_array_sort(entry, compare_strings);
+ dba_array_writelst(entry);
+ }
+ dba_array_FOREACH(pages, page) {
+ if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) {
+ dba_array_setpos(page, DBP_ARCH, dba_tell());
+ dba_array_sort(entry, compare_strings);
+ dba_array_writelst(entry);
+ } else
+ dba_array_setpos(page, DBP_ARCH, 0);
+ }
+ dba_array_FOREACH(pages, page) {
+ dba_array_setpos(page, DBP_DESC, dba_tell());
+ dba_str_write(dba_array_get(page, DBP_DESC));
+ }
+ dba_array_FOREACH(pages, page) {
+ dba_array_setpos(page, DBP_FILE, dba_tell());
+ dba_array_writelst(dba_array_get(page, DBP_FILE));
+ }
+ pos_end = dba_align();
+ dba_seek(pos_pages);
+ dba_array_FOREACH(pages, page)
+ dba_array_writepos(page);
+ dba_seek(pos_end);
+}
+
+static int
+compare_names(const void *vp1, const void *vp2)
+{
+ const char *cp1, *cp2;
+ int diff;
+
+ cp1 = *(const char * const *)vp1;
+ cp2 = *(const char * const *)vp2;
+ return (diff = *cp2 - *cp1) ? diff :
+ strcasecmp(cp1 + 1, cp2 + 1);
+}
+
+static int
+compare_strings(const void *vp1, const void *vp2)
+{
+ const char *cp1, *cp2;
+
+ cp1 = *(const char * const *)vp1;
+ cp2 = *(const char * const *)vp2;
+ return strcmp(cp1, cp2);
+}
+
+/*** functions for handling macros ************************************/
+
+/*
+ * In the hash table for a single macro, look up an entry by
+ * the macro value or add an empty one if it doesn't exist yet.
+ */
+static struct macro_entry *
+get_macro_entry(struct ohash *macro, const char *value, int32_t np)
+{
+ struct macro_entry *entry;
+ size_t len;
+ unsigned int slot;
+
+ slot = ohash_qlookup(macro, value);
+ if ((entry = ohash_find(macro, slot)) == NULL) {
+ len = strlen(value) + 1;
+ entry = mandoc_malloc(sizeof(*entry) + len);
+ memcpy(&entry->value, value, len);
+ entry->pages = dba_array_new(np, DBA_GROW);
+ ohash_insert(macro, slot, entry);
+ }
+ return entry;
+}
+
+/*
+ * In addition to get_macro_entry(), add multiple page references,
+ * converting them from the on-disk format (byte offsets in the file)
+ * to page pointers in memory.
+ */
+void
+dba_macro_new(struct dba *dba, int32_t im, const char *value,
+ const int32_t *pp)
+{
+ struct macro_entry *entry;
+ const int32_t *ip;
+ int32_t np;
+
+ np = 0;
+ for (ip = pp; *ip; ip++)
+ np++;
+
+ entry = get_macro_entry(dba_array_get(dba->macros, im), value, np);
+ for (ip = pp; *ip; ip++)
+ dba_array_add(entry->pages, dba_array_get(dba->pages,
+ be32toh(*ip) / 5 / sizeof(*ip) - 1));
+}
+
+/*
+ * In addition to get_macro_entry(), add one page reference,
+ * directly taking the in-memory page pointer as an argument.
+ */
+void
+dba_macro_add(struct dba_array *macros, int32_t im, const char *value,
+ struct dba_array *page)
+{
+ struct macro_entry *entry;
+
+ if (*value == '\0')
+ return;
+ entry = get_macro_entry(dba_array_get(macros, im), value, 1);
+ dba_array_add(entry->pages, page);
+}
+
+/*
+ * Write the macros table to disk; the format is:
+ * - The number of macro tables (actually, MACRO_MAX).
+ * - That number of pointers to the individual macro tables.
+ * - The individual macro tables.
+ */
+static void
+dba_macros_write(struct dba_array *macros)
+{
+ struct ohash *macro;
+ int32_t im, pos_macros, pos_end;
+
+ pos_macros = dba_array_writelen(macros, 1);
+ im = 0;
+ dba_array_FOREACH(macros, macro) {
+ dba_array_setpos(macros, im++, dba_tell());
+ dba_macro_write(macro);
+ }
+ pos_end = dba_tell();
+ dba_seek(pos_macros);
+ dba_array_writepos(macros);
+ dba_seek(pos_end);
+}
+
+/*
+ * Write one individual macro table to disk; the format is:
+ * - The number of entries in the table.
+ * - For each entry, two pointers, the first one to the value
+ * and the second one to the list of pages.
+ * - A list of values, each ending in a NUL byte.
+ * - To assure alignment of following integers,
+ * padding with NUL bytes up to a multiple of four bytes.
+ * - A list of pointers to pages, each list ending in a 0 integer.
+ */
+static void
+dba_macro_write(struct ohash *macro)
+{
+ struct macro_entry **entries, *entry;
+ struct dba_array *page;
+ int32_t *kpos, *dpos;
+ unsigned int ie, ne, slot;
+ int use;
+ int32_t addr, pos_macro, pos_end;
+
+ /* Temporary storage for filtering and sorting. */
+
+ ne = ohash_entries(macro);
+ entries = mandoc_reallocarray(NULL, ne, sizeof(*entries));
+ kpos = mandoc_reallocarray(NULL, ne, sizeof(*kpos));
+ dpos = mandoc_reallocarray(NULL, ne, sizeof(*dpos));
+
+ /* Build a list of non-empty entries and sort it. */
+
+ ne = 0;
+ for (entry = ohash_first(macro, &slot); entry != NULL;
+ entry = ohash_next(macro, &slot)) {
+ use = 0;
+ dba_array_FOREACH(entry->pages, page)
+ if (dba_array_getpos(page))
+ use = 1;
+ if (use)
+ entries[ne++] = entry;
+ }
+ qsort(entries, ne, sizeof(*entries), compare_entries);
+
+ /* Number of entries, and space for the pointer pairs. */
+
+ dba_int_write(ne);
+ pos_macro = dba_skip(2, ne);
+
+ /* String table. */
+
+ for (ie = 0; ie < ne; ie++) {
+ kpos[ie] = dba_tell();
+ dba_str_write(entries[ie]->value);
+ }
+ dba_align();
+
+ /* Pages table. */
+
+ for (ie = 0; ie < ne; ie++) {
+ dpos[ie] = dba_tell();
+ dba_array_FOREACH(entries[ie]->pages, page)
+ if ((addr = dba_array_getpos(page)))
+ dba_int_write(addr);
+ dba_int_write(0);
+ }
+ pos_end = dba_tell();
+
+ /* Fill in the pointer pairs. */
+
+ dba_seek(pos_macro);
+ for (ie = 0; ie < ne; ie++) {
+ dba_int_write(kpos[ie]);
+ dba_int_write(dpos[ie]);
+ }
+ dba_seek(pos_end);
+
+ free(entries);
+ free(kpos);
+ free(dpos);
+}
+
+static int
+compare_entries(const void *vp1, const void *vp2)
+{
+ const struct macro_entry *ep1, *ep2;
+
+ ep1 = *(const struct macro_entry * const *)vp1;
+ ep2 = *(const struct macro_entry * const *)vp2;
+ return strcmp(ep1->value, ep2->value);
+}
diff --git a/usr/src/cmd/mandoc/dba.h b/usr/src/cmd/mandoc/dba.h
new file mode 100644
index 0000000000..67a2759a20
--- /dev/null
+++ b/usr/src/cmd/mandoc/dba.h
@@ -0,0 +1,50 @@
+/* $Id: dba.h,v 1.2 2016/08/17 20:46:56 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Public interface of the allocation-based version
+ * of the mandoc database, for read-write access.
+ * To be used by dba.c, dba_read.c, and makewhatis(8).
+ */
+
+#define DBP_NAME 0
+#define DBP_SECT 1
+#define DBP_ARCH 2
+#define DBP_DESC 3
+#define DBP_FILE 4
+#define DBP_MAX 5
+
+struct dba_array;
+
+struct dba {
+ struct dba_array *pages;
+ struct dba_array *macros;
+};
+
+
+struct dba *dba_new(int32_t);
+void dba_free(struct dba *);
+struct dba *dba_read(const char *);
+int dba_write(const char *, struct dba *);
+
+struct dba_array *dba_page_new(struct dba_array *, const char *,
+ const char *, const char *, enum form);
+void dba_page_add(struct dba_array *, int32_t, const char *);
+void dba_page_alias(struct dba_array *, const char *, uint64_t);
+
+void dba_macro_new(struct dba *, int32_t,
+ const char *, const int32_t *);
+void dba_macro_add(struct dba_array *, int32_t,
+ const char *, struct dba_array *);
diff --git a/usr/src/cmd/mandoc/dba_array.c b/usr/src/cmd/mandoc/dba_array.c
new file mode 100644
index 0000000000..18c9f09f1a
--- /dev/null
+++ b/usr/src/cmd/mandoc/dba_array.c
@@ -0,0 +1,188 @@
+/* $Id: dba_array.c,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Allocation-based arrays for the mandoc database, for read-write access.
+ * The interface is defined in "dba_array.h".
+ */
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mandoc_aux.h"
+#include "dba_write.h"
+#include "dba_array.h"
+
+struct dba_array {
+ void **ep; /* Array of entries. */
+ int32_t *em; /* Array of map positions. */
+ int flags;
+ int32_t ea; /* Entries allocated. */
+ int32_t eu; /* Entries used (including deleted). */
+ int32_t ed; /* Entries deleted. */
+ int32_t ec; /* Currently active entry. */
+ int32_t pos; /* Map position of this array. */
+};
+
+
+struct dba_array *
+dba_array_new(int32_t ea, int flags)
+{
+ struct dba_array *array;
+
+ assert(ea > 0);
+ array = mandoc_malloc(sizeof(*array));
+ array->ep = mandoc_reallocarray(NULL, ea, sizeof(*array->ep));
+ array->em = mandoc_reallocarray(NULL, ea, sizeof(*array->em));
+ array->ea = ea;
+ array->eu = 0;
+ array->ed = 0;
+ array->ec = 0;
+ array->flags = flags;
+ array->pos = 0;
+ return array;
+}
+
+void
+dba_array_free(struct dba_array *array)
+{
+ int32_t ie;
+
+ if (array == NULL)
+ return;
+ if (array->flags & DBA_STR)
+ for (ie = 0; ie < array->eu; ie++)
+ free(array->ep[ie]);
+ free(array->ep);
+ free(array->em);
+ free(array);
+}
+
+void
+dba_array_set(struct dba_array *array, int32_t ie, void *entry)
+{
+ assert(ie >= 0);
+ assert(ie < array->ea);
+ assert(ie <= array->eu);
+ if (ie == array->eu)
+ array->eu++;
+ if (array->flags & DBA_STR)
+ entry = mandoc_strdup(entry);
+ array->ep[ie] = entry;
+ array->em[ie] = 0;
+}
+
+void
+dba_array_add(struct dba_array *array, void *entry)
+{
+ if (array->eu == array->ea) {
+ assert(array->flags & DBA_GROW);
+ array->ep = mandoc_reallocarray(array->ep,
+ 2, sizeof(*array->ep) * array->ea);
+ array->em = mandoc_reallocarray(array->em,
+ 2, sizeof(*array->em) * array->ea);
+ array->ea *= 2;
+ }
+ dba_array_set(array, array->eu, entry);
+}
+
+void *
+dba_array_get(struct dba_array *array, int32_t ie)
+{
+ if (ie < 0 || ie >= array->eu || array->em[ie] == -1)
+ return NULL;
+ return array->ep[ie];
+}
+
+void
+dba_array_start(struct dba_array *array)
+{
+ array->ec = array->eu;
+}
+
+void *
+dba_array_next(struct dba_array *array)
+{
+ if (array->ec < array->eu)
+ array->ec++;
+ else
+ array->ec = 0;
+ while (array->ec < array->eu && array->em[array->ec] == -1)
+ array->ec++;
+ return array->ec < array->eu ? array->ep[array->ec] : NULL;
+}
+
+void
+dba_array_del(struct dba_array *array)
+{
+ if (array->ec < array->eu && array->em[array->ec] != -1) {
+ array->em[array->ec] = -1;
+ array->ed++;
+ }
+}
+
+void
+dba_array_undel(struct dba_array *array)
+{
+ memset(array->em, 0, sizeof(*array->em) * array->eu);
+}
+
+void
+dba_array_setpos(struct dba_array *array, int32_t ie, int32_t pos)
+{
+ array->em[ie] = pos;
+}
+
+int32_t
+dba_array_getpos(struct dba_array *array)
+{
+ return array->pos;
+}
+
+void
+dba_array_sort(struct dba_array *array, dba_compare_func func)
+{
+ assert(array->ed == 0);
+ qsort(array->ep, array->eu, sizeof(*array->ep), func);
+}
+
+int32_t
+dba_array_writelen(struct dba_array *array, int32_t nmemb)
+{
+ dba_int_write(array->eu - array->ed);
+ return dba_skip(nmemb, array->eu - array->ed);
+}
+
+void
+dba_array_writepos(struct dba_array *array)
+{
+ int32_t ie;
+
+ array->pos = dba_tell();
+ for (ie = 0; ie < array->eu; ie++)
+ if (array->em[ie] != -1)
+ dba_int_write(array->em[ie]);
+}
+
+void
+dba_array_writelst(struct dba_array *array)
+{
+ const char *str;
+
+ dba_array_FOREACH(array, str)
+ dba_str_write(str);
+ dba_char_write('\0');
+}
diff --git a/usr/src/cmd/mandoc/dba_array.h b/usr/src/cmd/mandoc/dba_array.h
new file mode 100644
index 0000000000..d9a6ee6b1a
--- /dev/null
+++ b/usr/src/cmd/mandoc/dba_array.h
@@ -0,0 +1,47 @@
+/* $Id: dba_array.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Public interface for allocation-based arrays
+ * for the mandoc database, for read-write access.
+ * To be used by dba*.c and by makewhatis(8).
+ */
+
+struct dba_array;
+
+#define DBA_STR 0x01 /* Map contains strings, not pointers. */
+#define DBA_GROW 0x02 /* Allow the array to grow. */
+
+#define dba_array_FOREACH(a, e) \
+ dba_array_start(a); \
+ while (((e) = dba_array_next(a)) != NULL)
+
+typedef int dba_compare_func(const void *, const void *);
+
+struct dba_array *dba_array_new(int32_t, int);
+void dba_array_free(struct dba_array *);
+void dba_array_set(struct dba_array *, int32_t, void *);
+void dba_array_add(struct dba_array *, void *);
+void *dba_array_get(struct dba_array *, int32_t);
+void dba_array_start(struct dba_array *);
+void *dba_array_next(struct dba_array *);
+void dba_array_del(struct dba_array *);
+void dba_array_undel(struct dba_array *);
+void dba_array_setpos(struct dba_array *, int32_t, int32_t);
+int32_t dba_array_getpos(struct dba_array *);
+void dba_array_sort(struct dba_array *, dba_compare_func);
+int32_t dba_array_writelen(struct dba_array *, int32_t);
+void dba_array_writepos(struct dba_array *);
+void dba_array_writelst(struct dba_array *);
diff --git a/usr/src/cmd/mandoc/dba_read.c b/usr/src/cmd/mandoc/dba_read.c
new file mode 100644
index 0000000000..e976057064
--- /dev/null
+++ b/usr/src/cmd/mandoc/dba_read.c
@@ -0,0 +1,72 @@
+/* $Id: dba_read.c,v 1.4 2016/08/17 20:46:56 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Function to read the mandoc database from disk into RAM,
+ * such that data can be added or removed.
+ * The interface is defined in "dba.h".
+ * This file is seperate from dba.c because this also uses "dbm.h".
+ */
+#include <regex.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mandoc_aux.h"
+#include "mansearch.h"
+#include "dba_array.h"
+#include "dba.h"
+#include "dbm.h"
+
+
+struct dba *
+dba_read(const char *fname)
+{
+ struct dba *dba;
+ struct dba_array *page;
+ struct dbm_page *pdata;
+ struct dbm_macro *mdata;
+ const char *cp;
+ int32_t im, ip, iv, npages;
+
+ if (dbm_open(fname) == -1)
+ return NULL;
+ npages = dbm_page_count();
+ dba = dba_new(npages < 128 ? 128 : npages);
+ for (ip = 0; ip < npages; ip++) {
+ pdata = dbm_page_get(ip);
+ page = dba_page_new(dba->pages, pdata->arch,
+ pdata->desc, pdata->file + 1, *pdata->file);
+ for (cp = pdata->name; *cp != '\0'; cp = strchr(cp, '\0') + 1)
+ dba_page_add(page, DBP_NAME, cp);
+ for (cp = pdata->sect; *cp != '\0'; cp = strchr(cp, '\0') + 1)
+ dba_page_add(page, DBP_SECT, cp);
+ if ((cp = pdata->arch) != NULL)
+ while (*(cp = strchr(cp, '\0') + 1) != '\0')
+ dba_page_add(page, DBP_ARCH, cp);
+ cp = pdata->file;
+ while (*(cp = strchr(cp, '\0') + 1) != '\0')
+ dba_page_add(page, DBP_FILE, cp);
+ }
+ for (im = 0; im < MACRO_MAX; im++) {
+ for (iv = 0; iv < dbm_macro_count(im); iv++) {
+ mdata = dbm_macro_get(im, iv);
+ dba_macro_new(dba, im, mdata->value, mdata->pp);
+ }
+ }
+ dbm_close();
+ return dba;
+}
diff --git a/usr/src/cmd/mandoc/dba_write.c b/usr/src/cmd/mandoc/dba_write.c
new file mode 100644
index 0000000000..89883b87c1
--- /dev/null
+++ b/usr/src/cmd/mandoc/dba_write.c
@@ -0,0 +1,127 @@
+/* $Id: dba_write.c,v 1.3 2016/08/05 23:15:08 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Low-level functions for serializing allocation-based data to disk.
+ * The interface is defined in "dba_write.h".
+ */
+#include "config.h"
+
+#include <assert.h>
+#if HAVE_ENDIAN
+#include <endian.h>
+#elif HAVE_SYS_ENDIAN
+#include <sys/endian.h>
+#elif HAVE_NTOHL
+#include <arpa/inet.h>
+#endif
+#if HAVE_ERR
+#include <err.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "dba_write.h"
+
+static FILE *ofp;
+
+
+int
+dba_open(const char *fname)
+{
+ ofp = fopen(fname, "w");
+ return ofp == NULL ? -1 : 0;
+}
+
+int
+dba_close(void)
+{
+ return fclose(ofp) == EOF ? -1 : 0;
+}
+
+int32_t
+dba_tell(void)
+{
+ long pos;
+
+ if ((pos = ftell(ofp)) == -1)
+ err(1, "ftell");
+ if (pos >= INT32_MAX) {
+ errno = EOVERFLOW;
+ err(1, "ftell = %ld", pos);
+ }
+ return pos;
+}
+
+void
+dba_seek(int32_t pos)
+{
+ if (fseek(ofp, pos, SEEK_SET) == -1)
+ err(1, "fseek(%d)", pos);
+}
+
+int32_t
+dba_align(void)
+{
+ int32_t pos;
+
+ pos = dba_tell();
+ while (pos & 3) {
+ dba_char_write('\0');
+ pos++;
+ }
+ return pos;
+}
+
+int32_t
+dba_skip(int32_t nmemb, int32_t sz)
+{
+ const int32_t out[5] = {0, 0, 0, 0, 0};
+ int32_t i, pos;
+
+ assert(sz >= 0);
+ assert(nmemb > 0);
+ assert(nmemb <= 5);
+ pos = dba_tell();
+ for (i = 0; i < sz; i++)
+ if (nmemb - fwrite(&out, sizeof(out[0]), nmemb, ofp))
+ err(1, "fwrite");
+ return pos;
+}
+
+void
+dba_char_write(int c)
+{
+ if (putc(c, ofp) == EOF)
+ err(1, "fputc");
+}
+
+void
+dba_str_write(const char *str)
+{
+ if (fputs(str, ofp) == EOF)
+ err(1, "fputs");
+ dba_char_write('\0');
+}
+
+void
+dba_int_write(int32_t i)
+{
+ i = htobe32(i);
+ if (fwrite(&i, sizeof(i), 1, ofp) != 1)
+ err(1, "fwrite");
+}
diff --git a/usr/src/cmd/mandoc/dba_write.h b/usr/src/cmd/mandoc/dba_write.h
new file mode 100644
index 0000000000..6adda05b65
--- /dev/null
+++ b/usr/src/cmd/mandoc/dba_write.h
@@ -0,0 +1,30 @@
+/* $Id: dba_write.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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 interface to low-level functions
+ * for serializing allocation-based data to disk.
+ * For use by dba_array.c and dba.c only.
+ */
+
+int dba_open(const char *);
+int dba_close(void);
+int32_t dba_tell(void);
+void dba_seek(int32_t);
+int32_t dba_align(void);
+int32_t dba_skip(int32_t, int32_t);
+void dba_char_write(int);
+void dba_str_write(const char *);
+void dba_int_write(int32_t);
diff --git a/usr/src/cmd/mandoc/dbm.c b/usr/src/cmd/mandoc/dbm.c
new file mode 100644
index 0000000000..4aedf66d13
--- /dev/null
+++ b/usr/src/cmd/mandoc/dbm.c
@@ -0,0 +1,480 @@
+/* $Id: dbm.c,v 1.5 2016/10/18 22:27:25 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Map-based version of the mandoc database, for read-only access.
+ * The interface is defined in "dbm.h".
+ */
+#include "config.h"
+
+#include <assert.h>
+#if HAVE_ENDIAN
+#include <endian.h>
+#elif HAVE_SYS_ENDIAN
+#include <sys/endian.h>
+#elif HAVE_NTOHL
+#include <arpa/inet.h>
+#endif
+#if HAVE_ERR
+#include <err.h>
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mansearch.h"
+#include "dbm_map.h"
+#include "dbm.h"
+
+struct macro {
+ int32_t value;
+ int32_t pages;
+};
+
+struct page {
+ int32_t name;
+ int32_t sect;
+ int32_t arch;
+ int32_t desc;
+ int32_t file;
+};
+
+enum iter {
+ ITER_NONE = 0,
+ ITER_NAME,
+ ITER_SECT,
+ ITER_ARCH,
+ ITER_DESC,
+ ITER_MACRO
+};
+
+static struct macro *macros[MACRO_MAX];
+static int32_t nvals[MACRO_MAX];
+static struct page *pages;
+static int32_t npages;
+static enum iter iteration;
+
+static struct dbm_res page_bytitle(enum iter, const struct dbm_match *);
+static struct dbm_res page_byarch(const struct dbm_match *);
+static struct dbm_res page_bymacro(int32_t, const struct dbm_match *);
+static char *macro_bypage(int32_t, int32_t);
+
+
+/*** top level functions **********************************************/
+
+/*
+ * Open a disk-based mandoc database for read-only access.
+ * Map the pages and macros[] arrays.
+ * Return 0 on success. Return -1 and set errno on failure.
+ */
+int
+dbm_open(const char *fname)
+{
+ const int32_t *mp, *ep;
+ int32_t im;
+
+ if (dbm_map(fname) == -1)
+ return -1;
+
+ if ((npages = be32toh(*dbm_getint(4))) < 0) {
+ warnx("dbm_open(%s): Invalid number of pages: %d",
+ fname, npages);
+ goto fail;
+ }
+ pages = (struct page *)dbm_getint(5);
+
+ if ((mp = dbm_get(*dbm_getint(2))) == NULL) {
+ warnx("dbm_open(%s): Invalid offset of macros array", fname);
+ goto fail;
+ }
+ if (be32toh(*mp) != MACRO_MAX) {
+ warnx("dbm_open(%s): Invalid number of macros: %d",
+ fname, be32toh(*mp));
+ goto fail;
+ }
+ for (im = 0; im < MACRO_MAX; im++) {
+ if ((ep = dbm_get(*++mp)) == NULL) {
+ warnx("dbm_open(%s): Invalid offset of macro %d",
+ fname, im);
+ goto fail;
+ }
+ nvals[im] = be32toh(*ep);
+ macros[im] = (struct macro *)++ep;
+ }
+ return 0;
+
+fail:
+ dbm_unmap();
+ errno = EFTYPE;
+ return -1;
+}
+
+void
+dbm_close(void)
+{
+ dbm_unmap();
+}
+
+
+/*** functions for handling pages *************************************/
+
+int32_t
+dbm_page_count(void)
+{
+ return npages;
+}
+
+/*
+ * Give the caller pointers to the data for one manual page.
+ */
+struct dbm_page *
+dbm_page_get(int32_t ip)
+{
+ static struct dbm_page res;
+
+ assert(ip >= 0);
+ assert(ip < npages);
+ res.name = dbm_get(pages[ip].name);
+ if (res.name == NULL)
+ res.name = "(NULL)";
+ res.sect = dbm_get(pages[ip].sect);
+ if (res.sect == NULL)
+ res.sect = "(NULL)";
+ res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
+ res.desc = dbm_get(pages[ip].desc);
+ if (res.desc == NULL)
+ res.desc = "(NULL)";
+ res.file = dbm_get(pages[ip].file);
+ if (res.file == NULL)
+ res.file = " (NULL)";
+ res.addr = dbm_addr(pages + ip);
+ return &res;
+}
+
+/*
+ * Functions to start filtered iterations over manual pages.
+ */
+void
+dbm_page_byname(const struct dbm_match *match)
+{
+ assert(match != NULL);
+ page_bytitle(ITER_NAME, match);
+}
+
+void
+dbm_page_bysect(const struct dbm_match *match)
+{
+ assert(match != NULL);
+ page_bytitle(ITER_SECT, match);
+}
+
+void
+dbm_page_byarch(const struct dbm_match *match)
+{
+ assert(match != NULL);
+ page_byarch(match);
+}
+
+void
+dbm_page_bydesc(const struct dbm_match *match)
+{
+ assert(match != NULL);
+ page_bytitle(ITER_DESC, match);
+}
+
+void
+dbm_page_bymacro(int32_t im, const struct dbm_match *match)
+{
+ assert(im >= 0);
+ assert(im < MACRO_MAX);
+ assert(match != NULL);
+ page_bymacro(im, match);
+}
+
+/*
+ * Return the number of the next manual page in the current iteration.
+ */
+struct dbm_res
+dbm_page_next(void)
+{
+ struct dbm_res res = {-1, 0};
+
+ switch(iteration) {
+ case ITER_NONE:
+ return res;
+ case ITER_ARCH:
+ return page_byarch(NULL);
+ case ITER_MACRO:
+ return page_bymacro(0, NULL);
+ default:
+ return page_bytitle(iteration, NULL);
+ }
+}
+
+/*
+ * Functions implementing the iteration over manual pages.
+ */
+static struct dbm_res
+page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match)
+{
+ static const struct dbm_match *match;
+ static const char *cp;
+ static int32_t ip;
+ struct dbm_res res = {-1, 0};
+
+ assert(arg_iter == ITER_NAME || arg_iter == ITER_DESC ||
+ arg_iter == ITER_SECT);
+
+ /* Initialize for a new iteration. */
+
+ if (arg_match != NULL) {
+ iteration = arg_iter;
+ match = arg_match;
+ switch (iteration) {
+ case ITER_NAME:
+ cp = dbm_get(pages[0].name);
+ break;
+ case ITER_SECT:
+ cp = dbm_get(pages[0].sect);
+ break;
+ case ITER_DESC:
+ cp = dbm_get(pages[0].desc);
+ break;
+ default:
+ abort();
+ }
+ if (cp == NULL) {
+ iteration = ITER_NONE;
+ match = NULL;
+ cp = NULL;
+ ip = npages;
+ } else
+ ip = 0;
+ return res;
+ }
+
+ /* Search for a name. */
+
+ while (ip < npages) {
+ if (iteration == ITER_NAME)
+ cp++;
+ if (dbm_match(match, cp))
+ break;
+ cp = strchr(cp, '\0') + 1;
+ if (iteration == ITER_DESC)
+ ip++;
+ else if (*cp == '\0') {
+ cp++;
+ ip++;
+ }
+ }
+
+ /* Reached the end without a match. */
+
+ if (ip == npages) {
+ iteration = ITER_NONE;
+ match = NULL;
+ cp = NULL;
+ return res;
+ }
+
+ /* Found a match; save the quality for later retrieval. */
+
+ res.page = ip;
+ res.bits = iteration == ITER_NAME ? cp[-1] : 0;
+
+ /* Skip the remaining names of this page. */
+
+ if (++ip < npages) {
+ do {
+ cp++;
+ } while (cp[-1] != '\0' ||
+ (iteration != ITER_DESC && cp[-2] != '\0'));
+ }
+ return res;
+}
+
+static struct dbm_res
+page_byarch(const struct dbm_match *arg_match)
+{
+ static const struct dbm_match *match;
+ struct dbm_res res = {-1, 0};
+ static int32_t ip;
+ const char *cp;
+
+ /* Initialize for a new iteration. */
+
+ if (arg_match != NULL) {
+ iteration = ITER_ARCH;
+ match = arg_match;
+ ip = 0;
+ return res;
+ }
+
+ /* Search for an architecture. */
+
+ for ( ; ip < npages; ip++)
+ if (pages[ip].arch)
+ for (cp = dbm_get(pages[ip].arch);
+ *cp != '\0';
+ cp = strchr(cp, '\0') + 1)
+ if (dbm_match(match, cp)) {
+ res.page = ip++;
+ return res;
+ }
+
+ /* Reached the end without a match. */
+
+ iteration = ITER_NONE;
+ match = NULL;
+ return res;
+}
+
+static struct dbm_res
+page_bymacro(int32_t arg_im, const struct dbm_match *arg_match)
+{
+ static const struct dbm_match *match;
+ static const int32_t *pp;
+ static const char *cp;
+ static int32_t im, iv;
+ struct dbm_res res = {-1, 0};
+
+ assert(im >= 0);
+ assert(im < MACRO_MAX);
+
+ /* Initialize for a new iteration. */
+
+ if (arg_match != NULL) {
+ iteration = ITER_MACRO;
+ match = arg_match;
+ im = arg_im;
+ cp = nvals[im] ? dbm_get(macros[im]->value) : NULL;
+ pp = NULL;
+ iv = -1;
+ return res;
+ }
+ if (iteration != ITER_MACRO)
+ return res;
+
+ /* Find the next matching macro value. */
+
+ while (pp == NULL || *pp == 0) {
+ if (++iv == nvals[im]) {
+ iteration = ITER_NONE;
+ return res;
+ }
+ if (iv)
+ cp = strchr(cp, '\0') + 1;
+ if (dbm_match(match, cp))
+ pp = dbm_get(macros[im][iv].pages);
+ }
+
+ /* Found a matching page. */
+
+ res.page = (struct page *)dbm_get(*pp++) - pages;
+ return res;
+}
+
+
+/*** functions for handling macros ************************************/
+
+int32_t
+dbm_macro_count(int32_t im)
+{
+ assert(im >= 0);
+ assert(im < MACRO_MAX);
+ return nvals[im];
+}
+
+struct dbm_macro *
+dbm_macro_get(int32_t im, int32_t iv)
+{
+ static struct dbm_macro macro;
+
+ assert(im >= 0);
+ assert(im < MACRO_MAX);
+ assert(iv >= 0);
+ assert(iv < nvals[im]);
+ macro.value = dbm_get(macros[im][iv].value);
+ macro.pp = dbm_get(macros[im][iv].pages);
+ return &macro;
+}
+
+/*
+ * Filtered iteration over macro entries.
+ */
+void
+dbm_macro_bypage(int32_t im, int32_t ip)
+{
+ assert(im >= 0);
+ assert(im < MACRO_MAX);
+ assert(ip != 0);
+ macro_bypage(im, ip);
+}
+
+char *
+dbm_macro_next(void)
+{
+ return macro_bypage(MACRO_MAX, 0);
+}
+
+static char *
+macro_bypage(int32_t arg_im, int32_t arg_ip)
+{
+ static const int32_t *pp;
+ static int32_t im, ip, iv;
+
+ /* Initialize for a new iteration. */
+
+ if (arg_im < MACRO_MAX && arg_ip != 0) {
+ im = arg_im;
+ ip = arg_ip;
+ pp = dbm_get(macros[im]->pages);
+ iv = 0;
+ return NULL;
+ }
+ if (im >= MACRO_MAX)
+ return NULL;
+
+ /* Search for the next value. */
+
+ while (iv < nvals[im]) {
+ if (*pp == ip)
+ break;
+ if (*pp == 0)
+ iv++;
+ pp++;
+ }
+
+ /* Reached the end without a match. */
+
+ if (iv == nvals[im]) {
+ im = MACRO_MAX;
+ ip = 0;
+ pp = NULL;
+ return NULL;
+ }
+
+ /* Found a match; skip the remaining pages of this entry. */
+
+ if (++iv < nvals[im])
+ while (*pp++ != 0)
+ continue;
+
+ return dbm_get(macros[im][iv - 1].value);
+}
diff --git a/usr/src/cmd/mandoc/dbm.h b/usr/src/cmd/mandoc/dbm.h
new file mode 100644
index 0000000000..ec2cd479a7
--- /dev/null
+++ b/usr/src/cmd/mandoc/dbm.h
@@ -0,0 +1,68 @@
+/* $Id: dbm.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Public interface for the map-based version
+ * of the mandoc database, for read-only access.
+ * To be used by dbm*.c, dba_read.c, and man(1) and apropos(1).
+ */
+
+enum dbm_mtype {
+ DBM_EXACT = 0,
+ DBM_SUB,
+ DBM_REGEX
+};
+
+struct dbm_match {
+ regex_t *re;
+ const char *str;
+ enum dbm_mtype type;
+};
+
+struct dbm_res {
+ int32_t page;
+ int32_t bits;
+};
+
+struct dbm_page {
+ const char *name;
+ const char *sect;
+ const char *arch;
+ const char *desc;
+ const char *file;
+ int32_t addr;
+};
+
+struct dbm_macro {
+ const char *value;
+ const int32_t *pp;
+};
+
+int dbm_open(const char *);
+void dbm_close(void);
+
+int32_t dbm_page_count(void);
+struct dbm_page *dbm_page_get(int32_t);
+void dbm_page_byname(const struct dbm_match *);
+void dbm_page_bysect(const struct dbm_match *);
+void dbm_page_byarch(const struct dbm_match *);
+void dbm_page_bydesc(const struct dbm_match *);
+void dbm_page_bymacro(int32_t, const struct dbm_match *);
+struct dbm_res dbm_page_next(void);
+
+int32_t dbm_macro_count(int32_t);
+struct dbm_macro *dbm_macro_get(int32_t, int32_t);
+void dbm_macro_bypage(int32_t, int32_t);
+char *dbm_macro_next(void);
diff --git a/usr/src/cmd/mandoc/dbm_map.c b/usr/src/cmd/mandoc/dbm_map.c
new file mode 100644
index 0000000000..87c085d22e
--- /dev/null
+++ b/usr/src/cmd/mandoc/dbm_map.c
@@ -0,0 +1,194 @@
+/* $Id: dbm_map.c,v 1.8 2017/02/17 14:43:54 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Low-level routines for the map-based version
+ * of the mandoc database, for read-only access.
+ * The interface is defined in "dbm_map.h".
+ */
+#include "config.h"
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if HAVE_ENDIAN
+#include <endian.h>
+#elif HAVE_SYS_ENDIAN
+#include <sys/endian.h>
+#elif HAVE_NTOHL
+#include <arpa/inet.h>
+#endif
+#if HAVE_ERR
+#include <err.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mansearch.h"
+#include "dbm_map.h"
+#include "dbm.h"
+
+static struct stat st;
+static char *dbm_base;
+static int ifd;
+static int32_t max_offset;
+
+/*
+ * Open a disk-based database for read-only access.
+ * Validate the file format as far as it is not mandoc-specific.
+ * Return 0 on success. Return -1 and set errno on failure.
+ */
+int
+dbm_map(const char *fname)
+{
+ int save_errno;
+ const int32_t *magic;
+
+ if ((ifd = open(fname, O_RDONLY)) == -1)
+ return -1;
+ if (fstat(ifd, &st) == -1)
+ goto fail;
+ if (st.st_size < 5) {
+ warnx("dbm_map(%s): File too short", fname);
+ errno = EFTYPE;
+ goto fail;
+ }
+ if (st.st_size > INT32_MAX) {
+ errno = EFBIG;
+ goto fail;
+ }
+ if ((dbm_base = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED,
+ ifd, 0)) == MAP_FAILED)
+ goto fail;
+ magic = dbm_getint(0);
+ if (be32toh(*magic) != MANDOCDB_MAGIC) {
+ if (strncmp(dbm_base, "SQLite format 3", 15))
+ warnx("dbm_map(%s): "
+ "Bad initial magic %x (expected %x)",
+ fname, be32toh(*magic), MANDOCDB_MAGIC);
+ else
+ warnx("dbm_map(%s): "
+ "Obsolete format based on SQLite 3",
+ fname);
+ errno = EFTYPE;
+ goto fail;
+ }
+ magic = dbm_getint(1);
+ if (be32toh(*magic) != MANDOCDB_VERSION) {
+ warnx("dbm_map(%s): Bad version number %d (expected %d)",
+ fname, be32toh(*magic), MANDOCDB_VERSION);
+ errno = EFTYPE;
+ goto fail;
+ }
+ max_offset = be32toh(*dbm_getint(3)) + sizeof(int32_t);
+ if (st.st_size != max_offset) {
+ warnx("dbm_map(%s): Inconsistent file size %lld (expected %d)",
+ fname, (long long)st.st_size, max_offset);
+ errno = EFTYPE;
+ goto fail;
+ }
+ if ((magic = dbm_get(*dbm_getint(3))) == NULL) {
+ errno = EFTYPE;
+ goto fail;
+ }
+ if (be32toh(*magic) != MANDOCDB_MAGIC) {
+ warnx("dbm_map(%s): Bad final magic %x (expected %x)",
+ fname, be32toh(*magic), MANDOCDB_MAGIC);
+ errno = EFTYPE;
+ goto fail;
+ }
+ return 0;
+
+fail:
+ save_errno = errno;
+ close(ifd);
+ errno = save_errno;
+ return -1;
+}
+
+void
+dbm_unmap(void)
+{
+ if (munmap(dbm_base, st.st_size) == -1)
+ warn("dbm_unmap: munmap");
+ if (close(ifd) == -1)
+ warn("dbm_unmap: close");
+ dbm_base = (char *)-1;
+}
+
+/*
+ * Take a raw integer as it was read from the database.
+ * Interpret it as an offset into the database file
+ * and return a pointer to that place in the file.
+ */
+void *
+dbm_get(int32_t offset)
+{
+ offset = be32toh(offset);
+ if (offset < 0) {
+ warnx("dbm_get: Database corrupt: offset %d", offset);
+ return NULL;
+ }
+ if (offset >= max_offset) {
+ warnx("dbm_get: Database corrupt: offset %d > %d",
+ offset, max_offset);
+ return NULL;
+ }
+ return dbm_base + offset;
+}
+
+/*
+ * Assume the database starts with some integers.
+ * Assume they are numbered starting from 0, increasing.
+ * Get a pointer to one with the number "offset".
+ */
+int32_t *
+dbm_getint(int32_t offset)
+{
+ return (int32_t *)dbm_base + offset;
+}
+
+/*
+ * The reverse of dbm_get().
+ * Take pointer into the database file
+ * and convert it to the raw integer
+ * that would be used to refer to that place in the file.
+ */
+int32_t
+dbm_addr(const void *p)
+{
+ return htobe32((const char *)p - dbm_base);
+}
+
+int
+dbm_match(const struct dbm_match *match, const char *str)
+{
+ switch (match->type) {
+ case DBM_EXACT:
+ return strcmp(str, match->str) == 0;
+ case DBM_SUB:
+ return strcasestr(str, match->str) != NULL;
+ case DBM_REGEX:
+ return regexec(match->re, str, 0, NULL, 0) == 0;
+ default:
+ abort();
+ }
+}
diff --git a/usr/src/cmd/mandoc/dbm_map.h b/usr/src/cmd/mandoc/dbm_map.h
new file mode 100644
index 0000000000..9768fc5f2d
--- /dev/null
+++ b/usr/src/cmd/mandoc/dbm_map.h
@@ -0,0 +1,29 @@
+/* $Id: dbm_map.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * 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.
+ */
+
+struct dbm_match;
+
+int dbm_map(const char *);
+void dbm_unmap(void);
+void *dbm_get(int32_t);
+int32_t *dbm_getint(int32_t);
+int32_t dbm_addr(const void *);
+int dbm_match(const struct dbm_match *, const char *);
diff --git a/usr/src/cmd/mandoc/eqn_html.c b/usr/src/cmd/mandoc/eqn_html.c
index f29733613b..b6e7d914b8 100644
--- a/usr/src/cmd/mandoc/eqn_html.c
+++ b/usr/src/cmd/mandoc/eqn_html.c
@@ -1,6 +1,7 @@
-/* $Id: eqn_html.c,v 1.10 2014/10/12 19:31:41 schwarze Exp $ */
+/* $Id: eqn_html.c,v 1.11 2017/01/17 01:47:51 schwarze Exp $ */
/*
* Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -31,7 +32,6 @@ static void
eqn_box(struct html *p, const struct eqn_box *bp)
{
struct tag *post, *row, *cell, *t;
- struct htmlpair tag[2];
const struct eqn_box *child, *parent;
size_t i, j, rows;
@@ -59,10 +59,10 @@ eqn_box(struct html *p, const struct eqn_box *bp)
for (rows = 0; NULL != child; rows++)
child = child->next;
/* Print row-by-row. */
- post = print_otag(p, TAG_MTABLE, 0, NULL);
+ post = print_otag(p, TAG_MTABLE, "");
for (i = 0; i < rows; i++) {
parent = bp->first->first;
- row = print_otag(p, TAG_MTR, 0, NULL);
+ row = print_otag(p, TAG_MTR, "");
while (NULL != parent) {
child = parent->first;
for (j = 0; j < i; j++) {
@@ -70,8 +70,7 @@ eqn_box(struct html *p, const struct eqn_box *bp)
break;
child = child->next;
}
- cell = print_otag
- (p, TAG_MTD, 0, NULL);
+ cell = print_otag(p, TAG_MTD, "");
/*
* If we have no data for this
* particular cell, then print a
@@ -89,28 +88,28 @@ eqn_box(struct html *p, const struct eqn_box *bp)
switch (bp->pos) {
case (EQNPOS_TO):
- post = print_otag(p, TAG_MOVER, 0, NULL);
+ post = print_otag(p, TAG_MOVER, "");
break;
case (EQNPOS_SUP):
- post = print_otag(p, TAG_MSUP, 0, NULL);
+ post = print_otag(p, TAG_MSUP, "");
break;
case (EQNPOS_FROM):
- post = print_otag(p, TAG_MUNDER, 0, NULL);
+ post = print_otag(p, TAG_MUNDER, "");
break;
case (EQNPOS_SUB):
- post = print_otag(p, TAG_MSUB, 0, NULL);
+ post = print_otag(p, TAG_MSUB, "");
break;
case (EQNPOS_OVER):
- post = print_otag(p, TAG_MFRAC, 0, NULL);
+ post = print_otag(p, TAG_MFRAC, "");
break;
case (EQNPOS_FROMTO):
- post = print_otag(p, TAG_MUNDEROVER, 0, NULL);
+ post = print_otag(p, TAG_MUNDEROVER, "");
break;
case (EQNPOS_SUBSUP):
- post = print_otag(p, TAG_MSUBSUP, 0, NULL);
+ post = print_otag(p, TAG_MSUBSUP, "");
break;
case (EQNPOS_SQRT):
- post = print_otag(p, TAG_MSQRT, 0, NULL);
+ post = print_otag(p, TAG_MSQRT, "");
break;
default:
break;
@@ -119,52 +118,49 @@ eqn_box(struct html *p, const struct eqn_box *bp)
if (bp->top || bp->bottom) {
assert(NULL == post);
if (bp->top && NULL == bp->bottom)
- post = print_otag(p, TAG_MOVER, 0, NULL);
+ post = print_otag(p, TAG_MOVER, "");
else if (bp->top && bp->bottom)
- post = print_otag(p, TAG_MUNDEROVER, 0, NULL);
+ post = print_otag(p, TAG_MUNDEROVER, "");
else if (bp->bottom)
- post = print_otag(p, TAG_MUNDER, 0, NULL);
+ post = print_otag(p, TAG_MUNDER, "");
}
if (EQN_PILE == bp->type) {
assert(NULL == post);
if (bp->first != NULL && bp->first->type == EQN_LIST)
- post = print_otag(p, TAG_MTABLE, 0, NULL);
+ post = print_otag(p, TAG_MTABLE, "");
} else if (bp->type == EQN_LIST &&
bp->parent && bp->parent->type == EQN_PILE) {
assert(NULL == post);
- post = print_otag(p, TAG_MTR, 0, NULL);
- print_otag(p, TAG_MTD, 0, NULL);
+ post = print_otag(p, TAG_MTR, "");
+ print_otag(p, TAG_MTD, "");
}
if (NULL != bp->text) {
assert(NULL == post);
- post = print_otag(p, TAG_MI, 0, NULL);
+ post = print_otag(p, TAG_MI, "");
print_text(p, bp->text);
} else if (NULL == post) {
- if (NULL != bp->left || NULL != bp->right) {
- PAIR_INIT(&tag[0], ATTR_OPEN,
- NULL == bp->left ? "" : bp->left);
- PAIR_INIT(&tag[1], ATTR_CLOSE,
- NULL == bp->right ? "" : bp->right);
- post = print_otag(p, TAG_MFENCED, 2, tag);
- }
+ if (NULL != bp->left || NULL != bp->right)
+ post = print_otag(p, TAG_MFENCED, "??",
+ "open", bp->left == NULL ? "" : bp->left,
+ "close", bp->right == NULL ? "" : bp->right);
if (NULL == post)
- post = print_otag(p, TAG_MROW, 0, NULL);
+ post = print_otag(p, TAG_MROW, "");
else
- print_otag(p, TAG_MROW, 0, NULL);
+ print_otag(p, TAG_MROW, "");
}
eqn_box(p, bp->first);
out:
if (NULL != bp->bottom) {
- t = print_otag(p, TAG_MO, 0, NULL);
+ t = print_otag(p, TAG_MO, "");
print_text(p, bp->bottom);
print_tagq(p, t);
}
if (NULL != bp->top) {
- t = print_otag(p, TAG_MO, 0, NULL);
+ t = print_otag(p, TAG_MO, "");
print_text(p, bp->top);
print_tagq(p, t);
}
@@ -178,11 +174,9 @@ out:
void
print_eqn(struct html *p, const struct eqn *ep)
{
- struct htmlpair tag;
struct tag *t;
- PAIR_CLASS_INIT(&tag, "eqn");
- t = print_otag(p, TAG_MATH, 1, &tag);
+ t = print_otag(p, TAG_MATH, "c", "eqn");
p->flags |= HTML_NONOSPACE;
eqn_box(p, ep->root);
diff --git a/usr/src/cmd/mandoc/eqn_term.c b/usr/src/cmd/mandoc/eqn_term.c
index 5f2818b405..4358015274 100644
--- a/usr/src/cmd/mandoc/eqn_term.c
+++ b/usr/src/cmd/mandoc/eqn_term.c
@@ -1,7 +1,7 @@
-/* $Id: eqn_term.c,v 1.8 2015/01/01 15:36:08 schwarze Exp $ */
+/* $Id: eqn_term.c,v 1.9 2017/02/12 14:19:01 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -68,8 +68,10 @@ eqn_box(struct termp *p, const struct eqn_box *bp)
if (bp->pos == EQNPOS_SQRT) {
term_word(p, "sqrt");
- p->flags |= TERMP_NOSPACE;
- eqn_box(p, bp->first);
+ if (bp->first != NULL) {
+ p->flags |= TERMP_NOSPACE;
+ eqn_box(p, bp->first);
+ }
} else if (bp->type == EQN_SUBEXPR) {
child = bp->first;
eqn_box(p, child);
@@ -93,7 +95,8 @@ eqn_box(struct termp *p, const struct eqn_box *bp)
}
} else {
child = bp->first;
- if (bp->type == EQN_MATRIX && child->type == EQN_LIST)
+ if (bp->type == EQN_MATRIX &&
+ child != NULL && child->type == EQN_LIST)
child = child->first;
while (child != NULL) {
eqn_box(p,
diff --git a/usr/src/cmd/mandoc/html.c b/usr/src/cmd/mandoc/html.c
index adff053c1a..40f2cc076b 100644
--- a/usr/src/cmd/mandoc/html.c
+++ b/usr/src/cmd/mandoc/html.c
@@ -1,7 +1,7 @@
-/* $Id: html.c,v 1.192 2016/01/04 12:45:29 schwarze Exp $ */
+/* $Id: html.c,v 1.207 2017/02/05 20:22:04 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2011-2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -38,74 +38,67 @@
struct htmldata {
const char *name;
int flags;
-#define HTML_CLRLINE (1 << 0)
-#define HTML_NOSTACK (1 << 1)
-#define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */
+#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_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)
};
static const struct htmldata htmltags[TAG_MAX] = {
- {"html", HTML_CLRLINE}, /* TAG_HTML */
- {"head", HTML_CLRLINE}, /* TAG_HEAD */
- {"body", HTML_CLRLINE}, /* TAG_BODY */
- {"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */
- {"title", HTML_CLRLINE}, /* TAG_TITLE */
- {"div", HTML_CLRLINE}, /* TAG_DIV */
- {"h1", 0}, /* TAG_H1 */
- {"h2", 0}, /* TAG_H2 */
- {"span", 0}, /* TAG_SPAN */
- {"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */
- {"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */
- {"a", 0}, /* TAG_A */
- {"table", HTML_CLRLINE}, /* TAG_TABLE */
- {"tbody", HTML_CLRLINE}, /* TAG_TBODY */
- {"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */
- {"tr", HTML_CLRLINE}, /* TAG_TR */
- {"td", HTML_CLRLINE}, /* TAG_TD */
- {"li", HTML_CLRLINE}, /* TAG_LI */
- {"ul", HTML_CLRLINE}, /* TAG_UL */
- {"ol", HTML_CLRLINE}, /* TAG_OL */
- {"dl", HTML_CLRLINE}, /* TAG_DL */
- {"dt", HTML_CLRLINE}, /* TAG_DT */
- {"dd", HTML_CLRLINE}, /* TAG_DD */
- {"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */
- {"pre", HTML_CLRLINE }, /* TAG_PRE */
- {"b", 0 }, /* TAG_B */
- {"i", 0 }, /* TAG_I */
- {"code", 0 }, /* TAG_CODE */
- {"small", 0 }, /* TAG_SMALL */
- {"style", HTML_CLRLINE}, /* TAG_STYLE */
- {"math", HTML_CLRLINE}, /* TAG_MATH */
- {"mrow", 0}, /* TAG_MROW */
- {"mi", 0}, /* TAG_MI */
- {"mo", 0}, /* TAG_MO */
- {"msup", 0}, /* TAG_MSUP */
- {"msub", 0}, /* TAG_MSUB */
- {"msubsup", 0}, /* TAG_MSUBSUP */
- {"mfrac", 0}, /* TAG_MFRAC */
- {"msqrt", 0}, /* TAG_MSQRT */
- {"mfenced", 0}, /* TAG_MFENCED */
- {"mtable", 0}, /* TAG_MTABLE */
- {"mtr", 0}, /* TAG_MTR */
- {"mtd", 0}, /* TAG_MTD */
- {"munderover", 0}, /* TAG_MUNDEROVER */
- {"munder", 0}, /* TAG_MUNDER*/
- {"mover", 0}, /* TAG_MOVER*/
-};
-
-static const char *const htmlattrs[ATTR_MAX] = {
- "name", /* ATTR_NAME */
- "rel", /* ATTR_REL */
- "href", /* ATTR_HREF */
- "type", /* ATTR_TYPE */
- "media", /* ATTR_MEDIA */
- "class", /* ATTR_CLASS */
- "style", /* ATTR_STYLE */
- "id", /* ATTR_ID */
- "colspan", /* ATTR_COLSPAN */
- "charset", /* ATTR_CHARSET */
- "open", /* ATTR_OPEN */
- "close", /* ATTR_CLOSE */
- "mathvariant", /* ATTR_MATHVARIANT */
+ {"html", HTML_NLALL},
+ {"head", HTML_NLALL | HTML_INDENT},
+ {"body", HTML_NLALL},
+ {"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL},
+ {"title", HTML_NLAROUND},
+ {"div", HTML_NLAROUND},
+ {"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},
+ {"colgroup", HTML_NLALL | HTML_INDENT},
+ {"col", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL},
+ {"tr", HTML_NLALL | HTML_INDENT},
+ {"td", HTML_NLAROUND},
+ {"li", HTML_NLAROUND | HTML_INDENT},
+ {"ul", HTML_NLALL | HTML_INDENT},
+ {"ol", HTML_NLALL | HTML_INDENT},
+ {"dl", HTML_NLALL | HTML_INDENT},
+ {"dt", HTML_NLAROUND},
+ {"dd", 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},
+ {"mrow", 0},
+ {"mi", 0},
+ {"mo", 0},
+ {"msup", 0},
+ {"msub", 0},
+ {"msubsup", 0},
+ {"mfrac", 0},
+ {"msqrt", 0},
+ {"mfenced", 0},
+ {"mtable", 0},
+ {"mtr", 0},
+ {"mtd", 0},
+ {"munderover", 0},
+ {"munder", 0},
+ {"mover", 0},
};
static const char *const roffscales[SCALE_MAX] = {
@@ -121,12 +114,17 @@ static const char *const roffscales[SCALE_MAX] = {
"ex", /* SCALE_FS */
};
-static void bufncat(struct html *, const char *, size_t);
+static void a2width(const char *, struct roffsu *);
+static void print_byte(struct html *, char);
+static void print_endword(struct html *);
+static void print_indent(struct html *);
+static void print_word(struct html *, const char *);
+
static void print_ctag(struct html *, struct tag *);
-static int print_escape(char);
-static int print_encode(struct html *, const char *, int);
+static int print_escape(struct html *, char);
+static int print_encode(struct html *, const char *, const char *, int);
+static void print_href(struct html *, const char *, const char *, int);
static void print_metaf(struct html *, enum mandoc_esc);
-static void print_attr(struct html *, const char *, const char *);
void *
@@ -136,7 +134,7 @@ html_alloc(const struct manoutput *outopts)
h = mandoc_calloc(1, sizeof(struct html));
- h->tags.head = NULL;
+ h->tag = NULL;
h->style = outopts->style;
h->base_man = outopts->man;
h->base_includes = outopts->includes;
@@ -154,8 +152,8 @@ html_free(void *p)
h = (struct html *)p;
- while ((tag = h->tags.head) != NULL) {
- h->tags.head = tag->next;
+ while ((tag = h->tag) != NULL) {
+ h->tag = tag->next;
free(tag);
}
@@ -165,36 +163,27 @@ html_free(void *p)
void
print_gen_head(struct html *h)
{
- struct htmlpair tag[4];
struct tag *t;
- tag[0].key = ATTR_CHARSET;
- tag[0].val = "utf-8";
- print_otag(h, TAG_META, 1, tag);
+ print_otag(h, TAG_META, "?", "charset", "utf-8");
/*
* Print a default style-sheet.
*/
- t = print_otag(h, TAG_STYLE, 0, NULL);
- print_text(h, "table.head, table.foot { width: 100%; }\n"
- "td.head-rtitle, td.foot-os { text-align: right; }\n"
- "td.head-vol { text-align: center; }\n"
- "table.foot td { width: 50%; }\n"
- "table.head td { width: 33%; }\n"
- "div.spacer { margin: 1em 0; }\n");
+
+ t = print_otag(h, TAG_STYLE, "");
+ print_text(h, "table.head, table.foot { width: 100%; }");
+ print_endline(h);
+ print_text(h, "td.head-rtitle, td.foot-os { text-align: right; }");
+ print_endline(h);
+ print_text(h, "td.head-vol { text-align: center; }");
+ print_endline(h);
+ print_text(h, "div.Pp { margin: 1ex 0ex; }");
print_tagq(h, t);
- if (h->style) {
- tag[0].key = ATTR_REL;
- tag[0].val = "stylesheet";
- tag[1].key = ATTR_HREF;
- tag[1].val = h->style;
- tag[2].key = ATTR_TYPE;
- tag[2].val = "text/css";
- tag[3].key = ATTR_MEDIA;
- tag[3].val = "all";
- print_otag(h, TAG_LINK, 4, tag);
- }
+ if (h->style)
+ print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet",
+ h->style, "type", "text/css", "media", "all");
}
static void
@@ -233,14 +222,14 @@ print_metaf(struct html *h, enum mandoc_esc deco)
switch (font) {
case HTMLFONT_ITALIC:
- h->metaf = print_otag(h, TAG_I, 0, NULL);
+ h->metaf = print_otag(h, TAG_I, "");
break;
case HTMLFONT_BOLD:
- h->metaf = print_otag(h, TAG_B, 0, NULL);
+ h->metaf = print_otag(h, TAG_B, "");
break;
case HTMLFONT_BI:
- h->metaf = print_otag(h, TAG_B, 0, NULL);
- print_otag(h, TAG_I, 0, NULL);
+ h->metaf = print_otag(h, TAG_B, "");
+ print_otag(h, TAG_I, "");
break;
default:
break;
@@ -299,27 +288,27 @@ html_strlen(const char *cp)
}
static int
-print_escape(char c)
+print_escape(struct html *h, char c)
{
switch (c) {
case '<':
- printf("&lt;");
+ print_word(h, "&lt;");
break;
case '>':
- printf("&gt;");
+ print_word(h, "&gt;");
break;
case '&':
- printf("&amp;");
+ print_word(h, "&amp;");
break;
case '"':
- printf("&quot;");
+ print_word(h, "&quot;");
break;
case ASCII_NBRSP:
- printf("&nbsp;");
+ print_word(h, "&nbsp;");
break;
case ASCII_HYPH:
- putchar('-');
+ print_byte(h, '-');
break;
case ASCII_BREAK:
break;
@@ -330,8 +319,9 @@ print_escape(char c)
}
static int
-print_encode(struct html *h, const char *p, int norecurse)
+print_encode(struct html *h, const char *p, const char *pend, int norecurse)
{
+ char numbuf[16];
size_t sz;
int c, len, nospace;
const char *seq;
@@ -339,24 +329,28 @@ print_encode(struct html *h, const char *p, int norecurse)
static const char rejs[9] = { '\\', '<', '>', '&', '"',
ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' };
+ if (pend == NULL)
+ pend = strchr(p, '\0');
+
nospace = 0;
- while ('\0' != *p) {
+ while (p < pend) {
if (HTML_SKIPCHAR & h->flags && '\\' != *p) {
h->flags &= ~HTML_SKIPCHAR;
p++;
continue;
}
- sz = strcspn(p, rejs);
-
- fwrite(p, 1, sz, stdout);
- p += (int)sz;
+ for (sz = strcspn(p, rejs); sz-- && p < pend; p++)
+ if (*p == ' ')
+ print_endword(h);
+ else
+ print_byte(h, *p);
- if ('\0' == *p)
+ if (p >= pend)
break;
- if (print_escape(*p++))
+ if (print_escape(h, *p++))
continue;
esc = mandoc_escape(&p, &seq, &len);
@@ -415,50 +409,77 @@ print_encode(struct html *h, const char *p, int norecurse)
if ((c < 0x20 && c != 0x09) ||
(c > 0x7E && c < 0xA0))
c = 0xFFFD;
- if (c > 0x7E)
- printf("&#%d;", c);
- else if ( ! print_escape(c))
- putchar(c);
+ if (c > 0x7E) {
+ (void)snprintf(numbuf, sizeof(numbuf), "&#%d;", c);
+ print_word(h, numbuf);
+ } else if (print_escape(h, c) == 0)
+ print_byte(h, c);
}
return nospace;
}
static void
-print_attr(struct html *h, const char *key, const char *val)
+print_href(struct html *h, const char *name, const char *sec, int man)
{
- printf(" %s=\"", key);
- (void)print_encode(h, val, 1);
- putchar('\"');
+ const char *p, *pp;
+
+ pp = man ? h->base_man : h->base_includes;
+ while ((p = strchr(pp, '%')) != NULL) {
+ print_encode(h, pp, p, 1);
+ if (man && p[1] == 'S') {
+ if (sec == NULL)
+ print_byte(h, '1');
+ else
+ print_encode(h, sec, NULL, 1);
+ } else if ((man && p[1] == 'N') ||
+ (man == 0 && p[1] == 'I'))
+ print_encode(h, name, NULL, 1);
+ else
+ print_encode(h, p, p + 2, 1);
+ pp = p + 2;
+ }
+ if (*pp != '\0')
+ print_encode(h, pp, NULL, 1);
}
struct tag *
-print_otag(struct html *h, enum htmltag tag,
- int sz, const struct htmlpair *p)
+print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
{
- int i;
+ va_list ap;
+ struct roffsu mysu, *su;
+ char numbuf[16];
struct tag *t;
+ const char *attr;
+ char *arg1, *arg2;
+ double v;
+ int i, have_style, tflags;
+
+ tflags = htmltags[tag].flags;
- /* Push this tags onto the stack of open scopes. */
+ /* Push this tag onto the stack of open scopes. */
- if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
+ if ((tflags & HTML_NOSTACK) == 0) {
t = mandoc_malloc(sizeof(struct tag));
t->tag = tag;
- t->next = h->tags.head;
- h->tags.head = t;
+ t->next = h->tag;
+ h->tag = t;
} else
t = NULL;
- if ( ! (HTML_NOSPACE & h->flags))
- if ( ! (HTML_CLRLINE & htmltags[tag].flags)) {
- /* Manage keeps! */
- if ( ! (HTML_KEEP & h->flags)) {
- if (HTML_PREKEEP & h->flags)
- h->flags |= HTML_KEEP;
- putchar(' ');
- } else
- printf("&#160;");
+ if (tflags & HTML_NLBEFORE)
+ print_endline(h);
+ if (h->col == 0)
+ print_indent(h);
+ else if ((h->flags & HTML_NOSPACE) == 0) {
+ if (h->flags & HTML_KEEP)
+ print_word(h, "&#160;");
+ else {
+ if (h->flags & HTML_PREKEEP)
+ h->flags |= HTML_KEEP;
+ print_endword(h);
}
+ }
if ( ! (h->flags & HTML_NONOSPACE))
h->flags &= ~HTML_NOSPACE;
@@ -467,21 +488,185 @@ print_otag(struct html *h, enum htmltag tag,
/* Print out the tag name and attributes. */
- printf("<%s", htmltags[tag].name);
- for (i = 0; i < sz; i++)
- print_attr(h, htmlattrs[p[i].key], p[i].val);
+ print_byte(h, '<');
+ print_word(h, htmltags[tag].name);
+
+ va_start(ap, fmt);
+
+ have_style = 0;
+ while (*fmt != '\0') {
+ if (*fmt == 's') {
+ have_style = 1;
+ fmt++;
+ break;
+ }
+
+ /* Parse a non-style attribute and its arguments. */
+
+ arg1 = va_arg(ap, char *);
+ switch (*fmt++) {
+ case 'c':
+ attr = "class";
+ break;
+ case 'h':
+ attr = "href";
+ break;
+ case 'i':
+ attr = "id";
+ break;
+ case '?':
+ attr = arg1;
+ arg1 = va_arg(ap, char *);
+ break;
+ default:
+ abort();
+ }
+ arg2 = NULL;
+ if (*fmt == 'M')
+ arg2 = va_arg(ap, char *);
+ if (arg1 == NULL)
+ continue;
+
+ /* Print the non-style attributes. */
+
+ print_byte(h, ' ');
+ print_word(h, attr);
+ print_byte(h, '=');
+ print_byte(h, '"');
+ switch (*fmt) {
+ case 'M':
+ print_href(h, arg1, arg2, 1);
+ fmt++;
+ break;
+ case 'I':
+ print_href(h, arg1, NULL, 0);
+ fmt++;
+ break;
+ case 'R':
+ print_byte(h, '#');
+ fmt++;
+ /* FALLTHROUGH */
+ default:
+ print_encode(h, arg1, NULL, 1);
+ break;
+ }
+ print_byte(h, '"');
+ }
+
+ /* Print out styles. */
+
+ while (*fmt != '\0') {
+ arg1 = NULL;
+ su = NULL;
+
+ /* First letter: input argument type. */
+
+ switch (*fmt++) {
+ case 'h':
+ i = va_arg(ap, int);
+ su = &mysu;
+ SCALE_HS_INIT(su, i);
+ break;
+ case 's':
+ arg1 = va_arg(ap, char *);
+ break;
+ case 'u':
+ su = va_arg(ap, struct roffsu *);
+ break;
+ case 'v':
+ i = va_arg(ap, int);
+ su = &mysu;
+ SCALE_VS_INIT(su, i);
+ break;
+ case 'w':
+ case 'W':
+ if ((arg2 = va_arg(ap, char *)) == NULL)
+ break;
+ su = &mysu;
+ a2width(arg2, su);
+ if (fmt[-1] == 'W')
+ su->scale *= -1.0;
+ break;
+ default:
+ abort();
+ }
+
+ /* Second letter: style name. */
+
+ switch (*fmt++) {
+ case 'b':
+ attr = "margin-bottom";
+ break;
+ case 'h':
+ attr = "height";
+ break;
+ case 'i':
+ attr = "text-indent";
+ break;
+ case 'l':
+ attr = "margin-left";
+ break;
+ case 't':
+ attr = "margin-top";
+ break;
+ case 'w':
+ attr = "width";
+ break;
+ case 'W':
+ attr = "min-width";
+ break;
+ case '?':
+ attr = arg1;
+ arg1 = va_arg(ap, char *);
+ break;
+ default:
+ abort();
+ }
+ if (su == NULL && arg1 == NULL)
+ continue;
+
+ if (have_style == 1)
+ print_word(h, " style=\"");
+ else
+ print_byte(h, ' ');
+ print_word(h, attr);
+ print_byte(h, ':');
+ print_byte(h, ' ');
+ if (su != NULL) {
+ v = su->scale;
+ if (su->unit == SCALE_MM && (v /= 100.0) == 0.0)
+ v = 1.0;
+ else if (su->unit == SCALE_BU)
+ v /= 24.0;
+ (void)snprintf(numbuf, sizeof(numbuf), "%.2f", v);
+ print_word(h, numbuf);
+ print_word(h, roffscales[su->unit]);
+ } else
+ print_word(h, arg1);
+ print_byte(h, ';');
+ have_style = 2;
+ }
+ if (have_style == 2)
+ print_byte(h, '"');
+
+ va_end(ap);
/* Accommodate for "well-formed" singleton escaping. */
if (HTML_AUTOCLOSE & htmltags[tag].flags)
- putchar('/');
+ print_byte(h, '/');
- putchar('>');
+ print_byte(h, '>');
- h->flags |= HTML_NOSPACE;
+ if (tflags & HTML_NLBEGIN)
+ print_endline(h);
+ else
+ h->flags |= HTML_NOSPACE;
- if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags)
- putchar('\n');
+ if (tflags & HTML_INDENT)
+ h->indent++;
+ if (tflags & HTML_NOINDENT)
+ h->noindent++;
return t;
}
@@ -489,6 +674,7 @@ print_otag(struct html *h, enum htmltag tag,
static void
print_ctag(struct html *h, struct tag *tag)
{
+ int tflags;
/*
* Remember to close out and nullify the current
@@ -499,55 +685,64 @@ print_ctag(struct html *h, struct tag *tag)
if (tag == h->tblt)
h->tblt = NULL;
- printf("</%s>", htmltags[tag->tag].name);
- if (HTML_CLRLINE & htmltags[tag->tag].flags) {
- h->flags |= HTML_NOSPACE;
- putchar('\n');
- }
-
- h->tags.head = tag->next;
+ tflags = htmltags[tag->tag].flags;
+
+ if (tflags & HTML_INDENT)
+ h->indent--;
+ if (tflags & HTML_NOINDENT)
+ h->noindent--;
+ if (tflags & HTML_NLEND)
+ print_endline(h);
+ print_indent(h);
+ print_byte(h, '<');
+ print_byte(h, '/');
+ print_word(h, htmltags[tag->tag].name);
+ print_byte(h, '>');
+ if (tflags & HTML_NLAFTER)
+ print_endline(h);
+
+ h->tag = tag->next;
free(tag);
}
void
print_gen_decls(struct html *h)
{
-
- puts("<!DOCTYPE html>");
+ print_word(h, "<!DOCTYPE html>");
+ print_endline(h);
}
void
print_text(struct html *h, const char *word)
{
-
- if ( ! (HTML_NOSPACE & h->flags)) {
- /* Manage keeps! */
+ if (h->col && (h->flags & HTML_NOSPACE) == 0) {
if ( ! (HTML_KEEP & h->flags)) {
if (HTML_PREKEEP & h->flags)
h->flags |= HTML_KEEP;
- putchar(' ');
+ print_endword(h);
} else
- printf("&#160;");
+ print_word(h, "&#160;");
}
assert(NULL == h->metaf);
switch (h->metac) {
case HTMLFONT_ITALIC:
- h->metaf = print_otag(h, TAG_I, 0, NULL);
+ h->metaf = print_otag(h, TAG_I, "");
break;
case HTMLFONT_BOLD:
- h->metaf = print_otag(h, TAG_B, 0, NULL);
+ h->metaf = print_otag(h, TAG_B, "");
break;
case HTMLFONT_BI:
- h->metaf = print_otag(h, TAG_B, 0, NULL);
- print_otag(h, TAG_I, 0, NULL);
+ h->metaf = print_otag(h, TAG_B, "");
+ print_otag(h, TAG_I, "");
break;
default:
+ print_indent(h);
break;
}
assert(word);
- if ( ! print_encode(h, word, 0)) {
+ if ( ! print_encode(h, word, NULL, 0)) {
if ( ! (h->flags & HTML_NONOSPACE))
h->flags &= ~HTML_NOSPACE;
h->flags &= ~HTML_NONEWLINE;
@@ -567,7 +762,7 @@ print_tagq(struct html *h, const struct tag *until)
{
struct tag *tag;
- while ((tag = h->tags.head) != NULL) {
+ while ((tag = h->tag) != NULL) {
print_ctag(h, tag);
if (until && tag == until)
return;
@@ -579,7 +774,7 @@ print_stagq(struct html *h, const struct tag *suntil)
{
struct tag *tag;
- while ((tag = h->tags.head) != NULL) {
+ while ((tag = h->tag) != NULL) {
if (suntil && tag == suntil)
return;
print_ctag(h, tag);
@@ -590,138 +785,136 @@ void
print_paragraph(struct html *h)
{
struct tag *t;
- struct htmlpair tag;
- PAIR_CLASS_INIT(&tag, "spacer");
- t = print_otag(h, TAG_DIV, 1, &tag);
+ t = print_otag(h, TAG_DIV, "c", "Pp");
print_tagq(h, t);
}
-void
-bufinit(struct html *h)
-{
-
- h->buf[0] = '\0';
- h->buflen = 0;
-}
-
-void
-bufcat_style(struct html *h, const char *key, const char *val)
-{
-
- bufcat(h, key);
- bufcat(h, ":");
- bufcat(h, val);
- bufcat(h, ";");
-}
+/***********************************************************************
+ * Low level output functions.
+ * They implement line breaking using a short static buffer.
+ ***********************************************************************/
-void
-bufcat(struct html *h, const char *p)
+/*
+ * Buffer one HTML output byte.
+ * If the buffer is full, flush and deactivate it and start a new line.
+ * If the buffer is inactive, print directly.
+ */
+static void
+print_byte(struct html *h, char c)
{
+ if ((h->flags & HTML_BUFFER) == 0) {
+ putchar(c);
+ h->col++;
+ return;
+ }
- /*
- * XXX This is broken and not easy to fix.
- * When using the -Oincludes option, buffmt_includes()
- * may pass in strings overrunning BUFSIZ, causing a crash.
- */
+ if (h->col + h->bufcol < sizeof(h->buf)) {
+ h->buf[h->bufcol++] = c;
+ return;
+ }
- h->buflen = strlcat(h->buf, p, BUFSIZ);
- assert(h->buflen < BUFSIZ);
+ putchar('\n');
+ h->col = 0;
+ print_indent(h);
+ putchar(' ');
+ putchar(' ');
+ fwrite(h->buf, h->bufcol, 1, stdout);
+ putchar(c);
+ h->col = (h->indent + 1) * 2 + h->bufcol + 1;
+ h->bufcol = 0;
+ h->flags &= ~HTML_BUFFER;
}
+/*
+ * If something was printed on the current output line, end it.
+ * Not to be called right after print_indent().
+ */
void
-bufcat_fmt(struct html *h, const char *fmt, ...)
+print_endline(struct html *h)
{
- va_list ap;
+ if (h->col == 0)
+ return;
- va_start(ap, fmt);
- (void)vsnprintf(h->buf + (int)h->buflen,
- BUFSIZ - h->buflen - 1, fmt, ap);
- va_end(ap);
- h->buflen = strlen(h->buf);
+ if (h->bufcol) {
+ putchar(' ');
+ fwrite(h->buf, h->bufcol, 1, stdout);
+ h->bufcol = 0;
+ }
+ putchar('\n');
+ h->col = 0;
+ h->flags |= HTML_NOSPACE;
+ h->flags &= ~HTML_BUFFER;
}
+/*
+ * Flush the HTML output buffer.
+ * If it is inactive, activate it.
+ */
static void
-bufncat(struct html *h, const char *p, size_t sz)
+print_endword(struct html *h)
{
+ if (h->noindent) {
+ print_byte(h, ' ');
+ return;
+ }
- assert(h->buflen + sz + 1 < BUFSIZ);
- strncat(h->buf, p, sz);
- h->buflen += sz;
-}
-
-void
-buffmt_includes(struct html *h, const char *name)
-{
- const char *p, *pp;
-
- pp = h->base_includes;
-
- bufinit(h);
- while (NULL != (p = strchr(pp, '%'))) {
- bufncat(h, pp, (size_t)(p - pp));
- switch (*(p + 1)) {
- case'I':
- bufcat(h, name);
- break;
- default:
- bufncat(h, p, 2);
- break;
- }
- pp = p + 2;
+ if ((h->flags & HTML_BUFFER) == 0) {
+ h->col++;
+ h->flags |= HTML_BUFFER;
+ } else if (h->bufcol) {
+ putchar(' ');
+ fwrite(h->buf, h->bufcol, 1, stdout);
+ h->col += h->bufcol + 1;
}
- if (pp)
- bufcat(h, pp);
+ h->bufcol = 0;
}
-void
-buffmt_man(struct html *h, const char *name, const char *sec)
+/*
+ * If at the beginning of a new output line,
+ * perform indentation and mark the line as containing output.
+ * Make sure to really produce some output right afterwards,
+ * but do not use print_otag() for producing it.
+ */
+static void
+print_indent(struct html *h)
{
- const char *p, *pp;
+ size_t i;
- pp = h->base_man;
+ if (h->col)
+ return;
- bufinit(h);
- while (NULL != (p = strchr(pp, '%'))) {
- bufncat(h, pp, (size_t)(p - pp));
- switch (*(p + 1)) {
- case 'S':
- bufcat(h, sec ? sec : "1");
- break;
- case 'N':
- bufcat_fmt(h, "%s", name);
- break;
- default:
- bufncat(h, p, 2);
- break;
- }
- pp = p + 2;
+ if (h->noindent == 0) {
+ h->col = h->indent * 2;
+ for (i = 0; i < h->col; i++)
+ putchar(' ');
}
- if (pp)
- bufcat(h, pp);
+ h->flags &= ~HTML_NOSPACE;
}
-void
-bufcat_su(struct html *h, const char *p, const struct roffsu *su)
+/*
+ * Print or buffer some characters
+ * depending on the current HTML output buffer state.
+ */
+static void
+print_word(struct html *h, const char *cp)
{
- double v;
-
- v = su->scale;
- if (SCALE_MM == su->unit && 0.0 == (v /= 100.0))
- v = 1.0;
- else if (SCALE_BU == su->unit)
- v /= 24.0;
-
- bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]);
+ while (*cp != '\0')
+ print_byte(h, *cp++);
}
-void
-bufcat_id(struct html *h, const char *src)
+/*
+ * Calculate the scaling unit passed in a `-width' argument. This uses
+ * either a native scaling unit (e.g., 1i, 2m) or the string length of
+ * the value.
+ */
+static void
+a2width(const char *p, struct roffsu *su)
{
-
- /* Cf. <http://www.w3.org/TR/html5/dom.html#the-id-attribute>. */
-
- for (; '\0' != *src; src++)
- bufncat(h, *src == ' ' ? "_" : src, 1);
+ if (a2roffsu(p, su, SCALE_MAX) < 2) {
+ su->unit = SCALE_EN;
+ su->scale = html_strlen(p);
+ } else if (su->scale < 0.0)
+ su->scale = 0.0;
}
diff --git a/usr/src/cmd/mandoc/html.h b/usr/src/cmd/mandoc/html.h
index 27dc140185..5be2f82db6 100644
--- a/usr/src/cmd/mandoc/html.h
+++ b/usr/src/cmd/mandoc/html.h
@@ -1,6 +1,7 @@
-/* $Id: html.h,v 1.72 2015/11/07 14:01:16 schwarze Exp $ */
+/* $Id: html.h,v 1.83 2017/02/05 20:22:04 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -29,7 +30,7 @@ enum htmltag {
TAG_BR,
TAG_A,
TAG_TABLE,
- TAG_TBODY,
+ TAG_COLGROUP,
TAG_COL,
TAG_TR,
TAG_TD,
@@ -39,8 +40,9 @@ enum htmltag {
TAG_DL,
TAG_DT,
TAG_DD,
- TAG_BLOCKQUOTE,
TAG_PRE,
+ TAG_VAR,
+ TAG_CITE,
TAG_B,
TAG_I,
TAG_CODE,
@@ -65,23 +67,6 @@ enum htmltag {
TAG_MAX
};
-enum htmlattr {
- ATTR_NAME,
- ATTR_REL,
- ATTR_HREF,
- ATTR_TYPE,
- ATTR_MEDIA,
- ATTR_CLASS,
- ATTR_STYLE,
- ATTR_ID,
- ATTR_COLSPAN,
- ATTR_CHARSET,
- ATTR_OPEN,
- ATTR_CLOSE,
- ATTR_MATHVARIANT,
- ATTR_MAX
-};
-
enum htmlfont {
HTMLFONT_NONE = 0,
HTMLFONT_BOLD,
@@ -95,26 +80,6 @@ struct tag {
enum htmltag tag;
};
-struct tagq {
- struct tag *head;
-};
-
-struct htmlpair {
- enum htmlattr key;
- const char *val;
-};
-
-#define PAIR_INIT(p, t, v) \
- do { \
- (p)->key = (t); \
- (p)->val = (v); \
- } while (/* CONSTCOND */ 0)
-
-#define PAIR_ID_INIT(p, v) PAIR_INIT(p, ATTR_ID, v)
-#define PAIR_CLASS_INIT(p, v) PAIR_INIT(p, ATTR_CLASS, v)
-#define PAIR_HREF_INIT(p, v) PAIR_INIT(p, ATTR_HREF, v)
-#define PAIR_STYLE_INIT(p, h) PAIR_INIT(p, ATTR_STYLE, (h)->buf)
-
struct html {
int flags;
#define HTML_NOSPACE (1 << 0) /* suppress next space */
@@ -127,14 +92,18 @@ struct html {
#define HTML_NOSPLIT (1 << 7) /* do not break line before .An */
#define HTML_SPLIT (1 << 8) /* break line before .An */
#define HTML_NONEWLINE (1 << 9) /* No line break in nofill mode. */
- struct tagq tags; /* stack of open tags */
+#define HTML_BUFFER (1 << 10) /* Collect a word to see if it fits. */
+ size_t indent; /* current output indentation level */
+ int noindent; /* indent disabled by <pre> */
+ size_t col; /* current output byte position */
+ size_t bufcol; /* current buf byte position */
+ char buf[80]; /* output buffer */
+ struct tag *tag; /* last open tag */
struct rofftbl tbl; /* current table */
struct tag *tblt; /* current open table scope */
char *base_man; /* base for manpage href */
char *base_includes; /* base for include href */
char *style; /* style-sheet URI */
- char buf[BUFSIZ]; /* see bufcat and friends */
- size_t buflen;
struct tag *metaf; /* current open font scope */
enum htmlfont metal; /* last used font */
enum htmlfont metac; /* current font mode */
@@ -148,8 +117,7 @@ struct eqn;
void print_gen_decls(struct html *);
void print_gen_head(struct html *);
-struct tag *print_otag(struct html *, enum htmltag,
- int, const struct htmlpair *);
+struct tag *print_otag(struct html *, enum htmltag, const char *, ...);
void print_tagq(struct html *, const struct tag *);
void print_stagq(struct html *, const struct tag *);
void print_text(struct html *, const char *);
@@ -157,20 +125,6 @@ void print_tblclose(struct html *);
void print_tbl(struct html *, const struct tbl_span *);
void print_eqn(struct html *, const struct eqn *);
void print_paragraph(struct html *);
-
-#if __GNUC__ - 0 >= 4
-__attribute__((__format__ (__printf__, 2, 3)))
-#endif
-void bufcat_fmt(struct html *, const char *, ...);
-void bufcat(struct html *, const char *);
-void bufcat_id(struct html *, const char *);
-void bufcat_style(struct html *,
- const char *, const char *);
-void bufcat_su(struct html *, const char *,
- const struct roffsu *);
-void bufinit(struct html *);
-void buffmt_man(struct html *,
- const char *, const char *);
-void buffmt_includes(struct html *, const char *);
+void print_endline(struct html *);
int html_strlen(const char *);
diff --git a/usr/src/cmd/mandoc/libmandoc.h b/usr/src/cmd/mandoc/libmandoc.h
index 9ed8f15049..04b3a44565 100644
--- a/usr/src/cmd/mandoc/libmandoc.h
+++ b/usr/src/cmd/mandoc/libmandoc.h
@@ -1,4 +1,4 @@
-/* $Id: libmandoc.h,v 1.63 2016/07/07 19:19:01 schwarze Exp $ */
+/* $Id: libmandoc.h,v 1.66 2017/02/18 13:43:52 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -41,11 +41,9 @@ struct roff_man;
void mandoc_msg(enum mandocerr, struct mparse *,
int, int, const char *);
-#if __GNUC__ - 0 >= 4
-__attribute__((__format__ (__printf__, 5, 6)))
-#endif
void mandoc_vmsg(enum mandocerr, struct mparse *,
- int, int, const char *, ...);
+ int, int, const char *, ...)
+ __attribute__((__format__ (__printf__, 5, 6)));
char *mandoc_getarg(struct mparse *, char **, int, int *);
char *mandoc_normdate(struct mparse *, char *, int, int);
int mandoc_eos(const char *, size_t);
@@ -61,7 +59,7 @@ int man_parseln(struct roff_man *, int, char *, int);
void man_endparse(struct roff_man *);
int preconv_cue(const struct buf *, size_t);
-int preconv_encode(struct buf *, size_t *,
+int preconv_encode(const struct buf *, size_t *,
struct buf *, size_t *, int *);
void roff_free(struct roff *);
diff --git a/usr/src/cmd/mandoc/libmdoc.h b/usr/src/cmd/mandoc/libmdoc.h
index 5a6cc3ed9b..ac1521410b 100644
--- a/usr/src/cmd/mandoc/libmdoc.h
+++ b/usr/src/cmd/mandoc/libmdoc.h
@@ -1,4 +1,4 @@
-/* $Id: libmdoc.h,v 1.108 2015/11/07 14:01:16 schwarze Exp $ */
+/* $Id: libmdoc.h,v 1.109 2017/02/16 03:00:23 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -70,7 +70,7 @@ struct roff_node *mdoc_block_alloc(struct roff_man *, int, int,
int, struct mdoc_arg *);
void mdoc_tail_alloc(struct roff_man *, int, int, int);
struct roff_node *mdoc_endbody_alloc(struct roff_man *, int, int, int,
- struct roff_node *, enum mdoc_endbody);
+ struct roff_node *);
void mdoc_node_relink(struct roff_man *, struct roff_node *);
void mdoc_node_validate(struct roff_man *);
void mdoc_state(struct roff_man *, struct roff_node *);
diff --git a/usr/src/cmd/mandoc/main.c b/usr/src/cmd/mandoc/main.c
index 20562c7118..02abaaf791 100644
--- a/usr/src/cmd/mandoc/main.c
+++ b/usr/src/cmd/mandoc/main.c
@@ -1,5 +1,4 @@
-
-/* $Id: main.c,v 1.273.2.3 2017/01/09 02:27:58 schwarze Exp $ */
+/* $Id: main.c,v 1.283 2017/02/17 14:31:52 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2012, 2014-2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -83,9 +82,7 @@ struct curparse {
};
-#if HAVE_SQLITE3
int mandocdb(int, char *[]);
-#endif
static int fs_lookup(const struct manpaths *,
size_t ipath, const char *,
@@ -103,7 +100,7 @@ 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 usage(enum argmode) __attribute__((noreturn));
+static void usage(enum argmode) __attribute__((__noreturn__));
static int woptions(struct curparse *, char *);
static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
@@ -116,16 +113,14 @@ int
main(int argc, char *argv[])
{
struct manconf conf;
- struct curparse curp;
struct mansearch search;
+ struct curparse curp;
struct tag_files *tag_files;
- const char *progname;
- char *auxpaths;
- char *defos;
- unsigned char *uc;
struct manpage *res, *resp;
- char *conf_file, *defpaths;
- const char *sec;
+ const char *progname, *sec, *thisarg;
+ char *conf_file, *defpaths, *auxpaths;
+ char *defos, *oarg;
+ unsigned char *uc;
size_t i, sz;
int prio, best_prio;
enum outmode outmode;
@@ -149,11 +144,9 @@ main(int argc, char *argv[])
setprogname(progname);
#endif
-#if HAVE_SQLITE3
if (strncmp(progname, "mandocdb", 8) == 0 ||
strcmp(progname, BINM_MAKEWHATIS) == 0)
return mandocdb(argc, argv);
-#endif
#if HAVE_PLEDGE
if (pledge("stdio rpath tmppath tty proc exec flock", NULL) == -1)
@@ -173,6 +166,7 @@ main(int argc, char *argv[])
memset(&search, 0, sizeof(struct mansearch));
search.outkey = "Nd";
+ oarg = NULL;
if (strcmp(progname, BINM_MAN) == 0)
search.argmode = ARG_NAME;
@@ -251,10 +245,7 @@ main(int argc, char *argv[])
auxpaths = optarg;
break;
case 'O':
- search.outkey = optarg;
- while (optarg != NULL)
- manconf_output(&conf.output,
- strsep(&optarg, ","));
+ oarg = optarg;
break;
case 'S':
search.arch = optarg;
@@ -299,6 +290,21 @@ main(int argc, char *argv[])
}
}
+ if (oarg != NULL) {
+ if (outmode == OUTMODE_LST)
+ 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;
+ }
+ }
+ }
+
if (outmode == OUTMODE_FLN ||
outmode == OUTMODE_LST ||
!isatty(STDOUT_FILENO))
@@ -351,9 +357,6 @@ main(int argc, char *argv[])
/* man(1), whatis(1), apropos(1) */
if (search.argmode != ARG_FILE) {
- if (argc == 0)
- usage(search.argmode);
-
if (search.argmode == ARG_NAME &&
outmode == OUTMODE_ONE)
search.firstmatch = 1;
@@ -361,19 +364,9 @@ main(int argc, char *argv[])
/* Access the mandoc database. */
manconf_parse(&conf, conf_file, defpaths, auxpaths);
-#if HAVE_SQLITE3
- mansearch_setup(1);
if ( ! mansearch(&search, &conf.manpath,
argc, argv, &res, &sz))
usage(search.argmode);
-#else
- if (search.argmode != ARG_NAME) {
- fputs("mandoc: database support not compiled in\n",
- stderr);
- return (int)MANDOCLEVEL_BADARG;
- }
- sz = 0;
-#endif
if (sz == 0) {
if (search.argmode == ARG_NAME)
@@ -476,7 +469,7 @@ main(int argc, char *argv[])
if (resp == NULL)
parse(&curp, fd, *argv);
- else if (resp->form & FORM_SRC) {
+ else if (resp->form == FORM_SRC) {
/* For .so only; ignore failure. */
chdir(conf.manpath.paths[resp->ipath]);
parse(&curp, fd, resp->file);
@@ -527,10 +520,7 @@ main(int argc, char *argv[])
out:
if (search.argmode != ARG_FILE) {
manconf_free(&conf);
-#if HAVE_SQLITE3
mansearch_free(res, sz);
- mansearch_setup(0);
-#endif
}
free(defos);
@@ -552,10 +542,10 @@ out:
/* Stop here until moved to the foreground. */
- tc_pgid = tcgetpgrp(STDIN_FILENO);
+ tc_pgid = tcgetpgrp(tag_files->ofd);
if (tc_pgid != man_pgid) {
if (tc_pgid == pager_pid) {
- (void)tcsetpgrp(STDIN_FILENO,
+ (void)tcsetpgrp(tag_files->ofd,
man_pgid);
if (signum == SIGTTIN)
continue;
@@ -568,7 +558,7 @@ out:
/* Once in the foreground, activate the pager. */
if (pager_pid) {
- (void)tcsetpgrp(STDIN_FILENO, pager_pid);
+ (void)tcsetpgrp(tag_files->ofd, pager_pid);
kill(pager_pid, SIGCONT);
} else
pager_pid = spawn_pager(tag_files);
@@ -634,7 +624,8 @@ fs_lookup(const struct manpaths *paths, size_t ipath,
glob_t globinfo;
struct manpage *page;
char *file;
- int form, globres;
+ int globres;
+ enum form form;
form = FORM_SRC;
mandoc_asprintf(&file, "%s/man%s/%s.%s",
@@ -672,10 +663,8 @@ fs_lookup(const struct manpaths *paths, size_t ipath,
return 0;
found:
-#if HAVE_SQLITE3
warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s",
name, sec, BINM_MAKEWHATIS, paths->paths[ipath]);
-#endif
*res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage));
page = *res + (*ressz - 1);
page->file = file;
@@ -758,7 +747,8 @@ parse(struct curparse *curp, int fd, const char *file)
if (man == NULL)
return;
if (man->macroset == MACROSET_MDOC) {
- mdoc_validate(man);
+ if (curp->outtype != OUTT_TREE || !curp->outopts->noval)
+ mdoc_validate(man);
switch (curp->outtype) {
case OUTT_HTML:
html_mdoc(curp->outdata, man);
@@ -781,7 +771,8 @@ parse(struct curparse *curp, int fd, const char *file)
}
}
if (man->macroset == MACROSET_MAN) {
- man_validate(man);
+ if (curp->outtype != OUTT_TREE || !curp->outopts->noval)
+ man_validate(man);
switch (curp->outtype) {
case OUTT_HTML:
html_man(curp->outdata, man);
@@ -1098,7 +1089,7 @@ spawn_pager(struct tag_files *tag_files)
break;
default:
(void)setpgid(pager_pid, 0);
- (void)tcsetpgrp(STDIN_FILENO, pager_pid);
+ (void)tcsetpgrp(tag_files->ofd, pager_pid);
#if HAVE_PLEDGE
if (pledge("stdio rpath tmppath tty proc", NULL) == -1)
err((int)MANDOCLEVEL_SYSERR, "pledge");
@@ -1116,7 +1107,7 @@ spawn_pager(struct tag_files *tag_files)
/* Do not start the pager before controlling the terminal. */
- while (tcgetpgrp(STDIN_FILENO) != getpid())
+ while (tcgetpgrp(STDOUT_FILENO) != getpid())
nanosleep(&timeout, NULL);
execvp(argv[0], argv);
diff --git a/usr/src/cmd/mandoc/main.h b/usr/src/cmd/mandoc/main.h
index a53df93c38..f12f3e4c3a 100644
--- a/usr/src/cmd/mandoc/main.h
+++ b/usr/src/cmd/mandoc/main.h
@@ -1,4 +1,4 @@
-/* $Id: main.h,v 1.25 2016/07/08 22:29:05 schwarze Exp $ */
+/* $Id: main.h,v 1.26 2016/07/15 19:33:01 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -16,8 +16,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
-
struct roff_man;
struct manoutput;
diff --git a/usr/src/cmd/mandoc/man.c b/usr/src/cmd/mandoc/man.c
index 31c094e8d6..a2db05fbaa 100644
--- a/usr/src/cmd/mandoc/man.c
+++ b/usr/src/cmd/mandoc/man.c
@@ -1,4 +1,4 @@
-/* $Id: man.c,v 1.166 2015/10/22 21:54:23 schwarze Exp $ */
+/* $Id: man.c,v 1.167 2017/01/10 13:47:00 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -149,7 +149,7 @@ man_ptext(struct roff_man *man, int line, char *buf, int offs)
assert(i);
if (mandoc_eos(buf, (size_t)i))
- man->last->flags |= MAN_EOS;
+ man->last->flags |= NODE_EOS;
man_descope(man, line, offs);
return 1;
@@ -340,7 +340,7 @@ man_state(struct roff_man *man, struct roff_node *n)
switch(n->tok) {
case MAN_nf:
case MAN_EX:
- if (man->flags & MAN_LITERAL && ! (n->flags & MAN_VALID))
+ if (man->flags & MAN_LITERAL && ! (n->flags & NODE_VALID))
mandoc_msg(MANDOCERR_NF_SKIP, man->parse,
n->line, n->pos, "nf");
man->flags |= MAN_LITERAL;
@@ -348,7 +348,7 @@ man_state(struct roff_man *man, struct roff_node *n)
case MAN_fi:
case MAN_EE:
if ( ! (man->flags & MAN_LITERAL) &&
- ! (n->flags & MAN_VALID))
+ ! (n->flags & NODE_VALID))
mandoc_msg(MANDOCERR_FI_SKIP, man->parse,
n->line, n->pos, "fi");
man->flags &= ~MAN_LITERAL;
@@ -356,7 +356,7 @@ man_state(struct roff_man *man, struct roff_node *n)
default:
break;
}
- man->last->flags |= MAN_VALID;
+ man->last->flags |= NODE_VALID;
}
void
diff --git a/usr/src/cmd/mandoc/man_hash.c b/usr/src/cmd/mandoc/man_hash.c
index 8573994e5d..bb7b4665b3 100644
--- a/usr/src/cmd/mandoc/man_hash.c
+++ b/usr/src/cmd/mandoc/man_hash.c
@@ -1,4 +1,4 @@
-/* $Id: man_hash.c,v 1.34 2015/10/06 18:32:19 schwarze Exp $ */
+/* $Id: man_hash.c,v 1.35 2016/07/15 18:03:45 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -24,8 +24,10 @@
#include <limits.h>
#include <string.h>
+#include "mandoc.h"
#include "roff.h"
#include "man.h"
+#include "libmandoc.h"
#include "libman.h"
#define HASH_DEPTH 6
diff --git a/usr/src/cmd/mandoc/man_html.c b/usr/src/cmd/mandoc/man_html.c
index d71eb38237..9151e4c750 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.120 2016/01/08 17:48:09 schwarze Exp $ */
+/* $Id: man_html.c,v 1.133 2017/02/05 18:15:39 schwarze Exp $ */
/*
* Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -32,21 +32,14 @@
#include "html.h"
#include "main.h"
-/* TODO: preserve ident widths. */
/* FIXME: have PD set the default vspace width. */
#define INDENT 5
#define MAN_ARGS const struct roff_meta *man, \
const struct roff_node *n, \
- struct mhtml *mh, \
struct html *h
-struct mhtml {
- int fl;
-#define MANH_LITERAL (1 << 0) /* literal context */
-};
-
struct htmlman {
int (*pre)(MAN_ARGS);
int (*post)(MAN_ARGS);
@@ -57,6 +50,7 @@ static void print_bvspace(struct html *,
static void print_man_head(MAN_ARGS);
static void print_man_nodelist(MAN_ARGS);
static void print_man_node(MAN_ARGS);
+static int fillmode(struct html *, int);
static int a2width(const struct roff_node *,
struct roffsu *);
static int man_B_pre(MAN_ARGS);
@@ -74,7 +68,6 @@ static int man_alt_pre(MAN_ARGS);
static int man_br_pre(MAN_ARGS);
static int man_ign_pre(MAN_ARGS);
static int man_in_pre(MAN_ARGS);
-static int man_literal_pre(MAN_ARGS);
static void man_root_post(MAN_ARGS);
static void man_root_pre(MAN_ARGS);
@@ -101,8 +94,8 @@ static const struct htmlman mans[MAN_MAX] = {
{ man_alt_pre, NULL }, /* IR */
{ man_alt_pre, NULL }, /* RI */
{ man_br_pre, NULL }, /* sp */
- { man_literal_pre, NULL }, /* nf */
- { man_literal_pre, NULL }, /* fi */
+ { NULL, NULL }, /* nf */
+ { NULL, NULL }, /* fi */
{ NULL, NULL }, /* RE */
{ man_RS_pre, NULL }, /* RS */
{ man_ign_pre, NULL }, /* DT */
@@ -112,8 +105,8 @@ static const struct htmlman mans[MAN_MAX] = {
{ man_in_pre, NULL }, /* in */
{ man_ign_pre, NULL }, /* ft */
{ man_OP_pre, NULL }, /* OP */
- { man_literal_pre, NULL }, /* EX */
- { man_literal_pre, NULL }, /* EE */
+ { NULL, NULL }, /* EX */
+ { NULL, NULL }, /* EE */
{ man_UR_pre, NULL }, /* UR */
{ NULL, NULL }, /* UE */
{ man_ign_pre, NULL }, /* ll */
@@ -146,41 +139,38 @@ print_bvspace(struct html *h, const struct roff_node *n)
void
html_man(void *arg, const struct roff_man *man)
{
- struct mhtml mh;
- struct htmlpair tag;
struct html *h;
- struct tag *t, *tt;
+ struct tag *t;
- memset(&mh, 0, sizeof(mh));
- PAIR_CLASS_INIT(&tag, "mandoc");
h = (struct html *)arg;
- if ( ! (HTML_FRAGMENT & h->oflags)) {
+ if ((h->oflags & HTML_FRAGMENT) == 0) {
print_gen_decls(h);
- t = print_otag(h, TAG_HTML, 0, NULL);
- tt = print_otag(h, TAG_HEAD, 0, NULL);
- print_man_head(&man->meta, man->first, &mh, h);
- print_tagq(h, tt);
- print_otag(h, TAG_BODY, 0, NULL);
- print_otag(h, TAG_DIV, 1, &tag);
- } else
- t = print_otag(h, TAG_DIV, 1, &tag);
+ print_otag(h, TAG_HTML, "");
+ t = print_otag(h, TAG_HEAD, "");
+ print_man_head(&man->meta, man->first, h);
+ print_tagq(h, t);
+ print_otag(h, TAG_BODY, "");
+ }
- print_man_nodelist(&man->meta, man->first, &mh, h);
+ man_root_pre(&man->meta, man->first, h);
+ t = print_otag(h, TAG_DIV, "c", "manual-text");
+ print_man_nodelist(&man->meta, man->first->child, h);
print_tagq(h, t);
- putchar('\n');
+ man_root_post(&man->meta, man->first, h);
+ print_tagq(h, NULL);
}
static void
print_man_head(MAN_ARGS)
{
+ char *cp;
print_gen_head(h);
- assert(man->title);
- assert(man->msec);
- bufcat_fmt(h, "%s(%s)", man->title, man->msec);
- print_otag(h, TAG_TITLE, 0, NULL);
- print_text(h, h->buf);
+ mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
+ print_otag(h, TAG_TITLE, "");
+ print_text(h, cp);
+ free(cp);
}
static void
@@ -188,7 +178,7 @@ print_man_nodelist(MAN_ARGS)
{
while (n != NULL) {
- print_man_node(man, n, mh, h);
+ print_man_node(man, n, h);
n = n->next;
}
}
@@ -196,30 +186,95 @@ print_man_nodelist(MAN_ARGS)
static void
print_man_node(MAN_ARGS)
{
- int child;
+ static int want_fillmode = MAN_fi;
+ static int save_fillmode;
+
struct tag *t;
+ int child;
- child = 1;
- t = h->tags.head;
+ /*
+ * Handle fill mode switch requests up front,
+ * they would just cause trouble in the subsequent code.
+ */
+
+ switch (n->tok) {
+ case MAN_nf:
+ case MAN_EX:
+ want_fillmode = MAN_nf;
+ return;
+ case MAN_fi:
+ case MAN_EE:
+ want_fillmode = MAN_fi;
+ if (fillmode(h, 0) == MAN_fi)
+ print_otag(h, TAG_BR, "");
+ return;
+ default:
+ break;
+ }
+
+ /* Set up fill mode for the upcoming node. */
switch (n->type) {
- case ROFFT_ROOT:
- man_root_pre(man, n, mh, h);
+ case ROFFT_BLOCK:
+ save_fillmode = 0;
+ /* Some block macros suspend or cancel .nf. */
+ switch (n->tok) {
+ case MAN_TP: /* Tagged paragraphs */
+ case MAN_IP: /* temporarily disable .nf */
+ case MAN_HP: /* for the head. */
+ save_fillmode = want_fillmode;
+ /* FALLTHROUGH */
+ case MAN_SH: /* Section headers */
+ case MAN_SS: /* permanently cancel .nf. */
+ want_fillmode = MAN_fi;
+ /* FALLTHROUGH */
+ case MAN_PP: /* These have no head. */
+ case MAN_LP: /* They will simply */
+ case MAN_P: /* reopen .nf in the body. */
+ case MAN_RS:
+ case MAN_UR:
+ fillmode(h, MAN_fi);
+ break;
+ default:
+ break;
+ }
+ break;
+ case ROFFT_TBL:
+ fillmode(h, MAN_fi);
+ break;
+ case ROFFT_ELEM:
+ /*
+ * Some in-line macros produce tags and/or text
+ * in the handler, so they require fill mode to be
+ * configured up front just like for text nodes.
+ * For the others, keep the traditional approach
+ * of doing the same, for now.
+ */
+ fillmode(h, want_fillmode);
break;
case ROFFT_TEXT:
- if ('\0' == *n->string) {
- print_paragraph(h);
- return;
- }
- if (n->flags & MAN_LINE && (*n->string == ' ' ||
- (n->prev != NULL && mh->fl & MANH_LITERAL &&
- ! (h->flags & HTML_NONEWLINE))))
- print_otag(h, TAG_BR, 0, NULL);
- print_text(h, n->string);
+ if (fillmode(h, want_fillmode) == MAN_fi &&
+ want_fillmode == MAN_fi &&
+ n->flags & NODE_LINE && *n->string == ' ')
+ print_otag(h, TAG_BR, "");
+ if (*n->string != '\0')
+ break;
+ print_paragraph(h);
return;
+ default:
+ break;
+ }
+
+ /* Produce output for this node. */
+
+ child = 1;
+ switch (n->type) {
+ case ROFFT_TEXT:
+ t = h->tag;
+ print_text(h, n->string);
+ break;
case ROFFT_EQN:
- if (n->flags & MAN_LINE)
- putchar('\n');
+ t = h->tag;
print_eqn(h, n->eqn);
break;
case ROFFT_TBL:
@@ -245,32 +300,55 @@ print_man_node(MAN_ARGS)
* the "meta" table state. This will be reopened on the
* next table element.
*/
- if (h->tblt) {
+ if (h->tblt)
print_tblclose(h);
- t = h->tags.head;
- }
+
+ t = h->tag;
if (mans[n->tok].pre)
- child = (*mans[n->tok].pre)(man, n, mh, h);
+ child = (*mans[n->tok].pre)(man, n, h);
+
+ /* Some block macros resume .nf in the body. */
+ if (save_fillmode && n->type == ROFFT_BODY)
+ want_fillmode = save_fillmode;
+
break;
}
if (child && n->child)
- print_man_nodelist(man, n->child, mh, h);
+ print_man_nodelist(man, n->child, h);
/* This will automatically close out any font scope. */
print_stagq(h, t);
- switch (n->type) {
- case ROFFT_ROOT:
- man_root_post(man, n, mh, h);
- break;
- case ROFFT_EQN:
- break;
- default:
- if (mans[n->tok].post)
- (*mans[n->tok].post)(man, n, mh, h);
- break;
+ if (fillmode(h, 0) == MAN_nf &&
+ n->next != NULL && n->next->flags & NODE_LINE)
+ print_endline(h);
+}
+
+/*
+ * MAN_nf switches to no-fill mode, MAN_fi to fill mode.
+ * Other arguments do not switch.
+ * The old mode is returned.
+ */
+static int
+fillmode(struct html *h, int want)
+{
+ struct tag *pre;
+ int had;
+
+ for (pre = h->tag; pre != NULL; pre = pre->next)
+ if (pre->tag == TAG_PRE)
+ break;
+
+ had = pre == NULL ? MAN_fi : MAN_nf;
+
+ if (want && want != had) {
+ if (want == MAN_nf)
+ print_otag(h, TAG_PRE, "");
+ else
+ print_tagq(h, pre);
}
+ return had;
}
static int
@@ -288,7 +366,6 @@ a2width(const struct roff_node *n, struct roffsu *su)
static void
man_root_pre(MAN_ARGS)
{
- struct htmlpair tag;
struct tag *t, *tt;
char *title;
@@ -296,26 +373,19 @@ man_root_pre(MAN_ARGS)
assert(man->msec);
mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
- PAIR_CLASS_INIT(&tag, "head");
- t = print_otag(h, TAG_TABLE, 1, &tag);
+ t = print_otag(h, TAG_TABLE, "c", "head");
+ tt = print_otag(h, TAG_TR, "");
- print_otag(h, TAG_TBODY, 0, NULL);
-
- tt = print_otag(h, TAG_TR, 0, NULL);
-
- PAIR_CLASS_INIT(&tag, "head-ltitle");
- print_otag(h, TAG_TD, 1, &tag);
+ print_otag(h, TAG_TD, "c", "head-ltitle");
print_text(h, title);
print_stagq(h, tt);
- PAIR_CLASS_INIT(&tag, "head-vol");
- print_otag(h, TAG_TD, 1, &tag);
+ print_otag(h, TAG_TD, "c", "head-vol");
if (NULL != man->vol)
print_text(h, man->vol);
print_stagq(h, tt);
- PAIR_CLASS_INIT(&tag, "head-rtitle");
- print_otag(h, TAG_TD, 1, &tag);
+ print_otag(h, TAG_TD, "c", "head-rtitle");
print_text(h, title);
print_tagq(h, t);
free(title);
@@ -324,24 +394,16 @@ man_root_pre(MAN_ARGS)
static void
man_root_post(MAN_ARGS)
{
- struct htmlpair tag;
struct tag *t, *tt;
- PAIR_CLASS_INIT(&tag, "foot");
- t = print_otag(h, TAG_TABLE, 1, &tag);
-
- tt = print_otag(h, TAG_TR, 0, NULL);
+ t = print_otag(h, TAG_TABLE, "c", "foot");
+ tt = print_otag(h, TAG_TR, "");
- PAIR_CLASS_INIT(&tag, "foot-date");
- print_otag(h, TAG_TD, 1, &tag);
-
- assert(man->date);
+ print_otag(h, TAG_TD, "c", "foot-date");
print_text(h, man->date);
print_stagq(h, tt);
- PAIR_CLASS_INIT(&tag, "foot-os");
- print_otag(h, TAG_TD, 1, &tag);
-
+ print_otag(h, TAG_TD, "c", "foot-os");
if (man->os)
print_text(h, man->os);
print_tagq(h, t);
@@ -352,7 +414,6 @@ static int
man_br_pre(MAN_ARGS)
{
struct roffsu su;
- struct htmlpair tag;
SCALE_VS_INIT(&su, 1);
@@ -363,10 +424,7 @@ man_br_pre(MAN_ARGS)
} else
su.scale = 0.0;
- bufinit(h);
- bufcat_su(h, "height", &su);
- PAIR_STYLE_INIT(&tag, h);
- print_otag(h, TAG_DIV, 1, &tag);
+ print_otag(h, TAG_DIV, "suh", &su);
/* So the div isn't empty: */
print_text(h, "\\~");
@@ -377,17 +435,8 @@ man_br_pre(MAN_ARGS)
static int
man_SH_pre(MAN_ARGS)
{
- struct htmlpair tag;
-
- if (n->type == ROFFT_BLOCK) {
- mh->fl &= ~MANH_LITERAL;
- PAIR_CLASS_INIT(&tag, "section");
- print_otag(h, TAG_DIV, 1, &tag);
- return 1;
- } else if (n->type == ROFFT_BODY)
- return 1;
-
- print_otag(h, TAG_H1, 0, NULL);
+ if (n->type == ROFFT_HEAD)
+ print_otag(h, TAG_H1, "c", "Sh");
return 1;
}
@@ -395,17 +444,11 @@ static int
man_alt_pre(MAN_ARGS)
{
const struct roff_node *nn;
- int i, savelit;
+ int i;
enum htmltag fp;
struct tag *t;
- if ((savelit = mh->fl & MANH_LITERAL))
- print_otag(h, TAG_BR, 0, NULL);
-
- mh->fl &= ~MANH_LITERAL;
-
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
- t = NULL;
switch (n->tok) {
case MAN_BI:
fp = i % 2 ? TAG_I : TAG_B;
@@ -432,45 +475,31 @@ man_alt_pre(MAN_ARGS)
if (i)
h->flags |= HTML_NOSPACE;
- if (TAG_MAX != fp)
- t = print_otag(h, fp, 0, NULL);
+ if (fp != TAG_MAX)
+ t = print_otag(h, fp, "");
- print_man_node(man, nn, mh, h);
+ print_text(h, nn->string);
- if (t)
+ if (fp != TAG_MAX)
print_tagq(h, t);
}
-
- if (savelit)
- mh->fl |= MANH_LITERAL;
-
return 0;
}
static int
man_SM_pre(MAN_ARGS)
{
-
- print_otag(h, TAG_SMALL, 0, NULL);
+ print_otag(h, TAG_SMALL, "");
if (MAN_SB == n->tok)
- print_otag(h, TAG_B, 0, NULL);
+ print_otag(h, TAG_B, "");
return 1;
}
static int
man_SS_pre(MAN_ARGS)
{
- struct htmlpair tag;
-
- if (n->type == ROFFT_BLOCK) {
- mh->fl &= ~MANH_LITERAL;
- PAIR_CLASS_INIT(&tag, "subsection");
- print_otag(h, TAG_DIV, 1, &tag);
- return 1;
- } else if (n->type == ROFFT_BODY)
- return 1;
-
- print_otag(h, TAG_H2, 0, NULL);
+ if (n->type == ROFFT_HEAD)
+ print_otag(h, TAG_H2, "c", "Ss");
return 1;
}
@@ -492,30 +521,30 @@ man_IP_pre(MAN_ARGS)
const struct roff_node *nn;
if (n->type == ROFFT_BODY) {
- print_otag(h, TAG_DD, 0, NULL);
+ print_otag(h, TAG_DD, "c", "It-tag");
return 1;
} else if (n->type != ROFFT_HEAD) {
- print_otag(h, TAG_DL, 0, NULL);
+ print_otag(h, TAG_DL, "c", "Bl-tag");
return 1;
}
/* FIXME: width specification. */
- print_otag(h, TAG_DT, 0, NULL);
+ print_otag(h, TAG_DT, "c", "It-tag");
/* For IP, only print the first header element. */
if (MAN_IP == n->tok && n->child)
- print_man_node(man, n->child, mh, h);
+ print_man_node(man, n->child, h);
/* For TP, only print next-line header elements. */
if (MAN_TP == n->tok) {
nn = n->child;
- while (NULL != nn && 0 == (MAN_LINE & nn->flags))
+ while (NULL != nn && 0 == (NODE_LINE & nn->flags))
nn = nn->next;
while (NULL != nn) {
- print_man_node(man, nn, mh, h);
+ print_man_node(man, nn, h);
nn = nn->next;
}
}
@@ -526,8 +555,7 @@ man_IP_pre(MAN_ARGS)
static int
man_HP_pre(MAN_ARGS)
{
- struct htmlpair tag[2];
- struct roffsu su;
+ struct roffsu sum, sui;
const struct roff_node *np;
if (n->type == ROFFT_HEAD)
@@ -537,18 +565,14 @@ man_HP_pre(MAN_ARGS)
np = n->head->child;
- if (NULL == np || ! a2width(np, &su))
- SCALE_HS_INIT(&su, INDENT);
+ if (np == NULL || !a2width(np, &sum))
+ SCALE_HS_INIT(&sum, INDENT);
- bufinit(h);
+ sui.unit = sum.unit;
+ sui.scale = -sum.scale;
print_bvspace(h, n);
- bufcat_su(h, "margin-left", &su);
- su.scale = -su.scale;
- bufcat_su(h, "text-indent", &su);
- PAIR_STYLE_INIT(&tag[0], h);
- PAIR_CLASS_INIT(&tag[1], "spacer");
- print_otag(h, TAG_DIV, 2, tag);
+ print_otag(h, TAG_DIV, "csului", "Pp", &sum, &sui);
return 1;
}
@@ -556,22 +580,20 @@ static int
man_OP_pre(MAN_ARGS)
{
struct tag *tt;
- struct htmlpair tag;
print_text(h, "[");
h->flags |= HTML_NOSPACE;
- PAIR_CLASS_INIT(&tag, "opt");
- tt = print_otag(h, TAG_SPAN, 1, &tag);
+ tt = print_otag(h, TAG_SPAN, "c", "Op");
if (NULL != (n = n->child)) {
- print_otag(h, TAG_B, 0, NULL);
+ print_otag(h, TAG_B, "");
print_text(h, n->string);
}
print_stagq(h, tt);
if (NULL != n && NULL != n->next) {
- print_otag(h, TAG_I, 0, NULL);
+ print_otag(h, TAG_I, "");
print_text(h, n->next->string);
}
@@ -584,37 +606,21 @@ man_OP_pre(MAN_ARGS)
static int
man_B_pre(MAN_ARGS)
{
-
- print_otag(h, TAG_B, 0, NULL);
+ print_otag(h, TAG_B, "");
return 1;
}
static int
man_I_pre(MAN_ARGS)
{
-
- print_otag(h, TAG_I, 0, NULL);
+ print_otag(h, TAG_I, "");
return 1;
}
static int
-man_literal_pre(MAN_ARGS)
-{
-
- if (MAN_fi == n->tok || MAN_EE == n->tok) {
- print_otag(h, TAG_BR, 0, NULL);
- mh->fl &= ~MANH_LITERAL;
- } else
- mh->fl |= MANH_LITERAL;
-
- return 0;
-}
-
-static int
man_in_pre(MAN_ARGS)
{
-
- print_otag(h, TAG_BR, 0, NULL);
+ print_otag(h, TAG_BR, "");
return 0;
}
@@ -628,7 +634,6 @@ man_ign_pre(MAN_ARGS)
static int
man_RS_pre(MAN_ARGS)
{
- struct htmlpair tag;
struct roffsu su;
if (n->type == ROFFT_HEAD)
@@ -640,32 +645,25 @@ man_RS_pre(MAN_ARGS)
if (n->head->child)
a2width(n->head->child, &su);
- bufinit(h);
- bufcat_su(h, "margin-left", &su);
- PAIR_STYLE_INIT(&tag, h);
- print_otag(h, TAG_DIV, 1, &tag);
+ print_otag(h, TAG_DIV, "sul", &su);
return 1;
}
static int
man_UR_pre(MAN_ARGS)
{
- struct htmlpair tag[2];
-
n = n->child;
assert(n->type == ROFFT_HEAD);
if (n->child != NULL) {
assert(n->child->type == ROFFT_TEXT);
- PAIR_CLASS_INIT(&tag[0], "link-ext");
- PAIR_HREF_INIT(&tag[1], n->child->string);
- print_otag(h, TAG_A, 2, tag);
+ print_otag(h, TAG_A, "ch", "Lk", n->child->string);
}
assert(n->next->type == ROFFT_BODY);
if (n->next->child != NULL)
n = n->next;
- print_man_nodelist(man, n->child, mh, h);
+ print_man_nodelist(man, n->child, h);
return 0;
}
diff --git a/usr/src/cmd/mandoc/man_macro.c b/usr/src/cmd/mandoc/man_macro.c
index d15335709e..7fd17c5348 100644
--- a/usr/src/cmd/mandoc/man_macro.c
+++ b/usr/src/cmd/mandoc/man_macro.c
@@ -1,4 +1,4 @@
-/* $Id: man_macro.c,v 1.114 2016/01/08 17:48:09 schwarze Exp $ */
+/* $Id: man_macro.c,v 1.115 2017/01/10 13:47:00 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2012, 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -95,7 +95,7 @@ man_unscope(struct roff_man *man, const struct roff_node *to)
/* Reached the end of the document? */
- if (to == NULL && ! (n->flags & MAN_VALID)) {
+ if (to == NULL && ! (n->flags & NODE_VALID)) {
if (man->flags & (MAN_BLINE | MAN_ELINE) &&
man_macros[n->tok].flags & MAN_SCOPED) {
mandoc_vmsg(MANDOCERR_BLK_LINE,
@@ -130,7 +130,7 @@ man_unscope(struct roff_man *man, const struct roff_node *to)
man->last = n;
n = n->parent;
- man->last->flags |= MAN_VALID;
+ man->last->flags |= NODE_VALID;
}
/*
@@ -164,7 +164,7 @@ rew_scope(struct roff_man *man, int tok)
for (;;) {
if (n->type == ROFFT_ROOT)
return;
- if (n->flags & MAN_VALID) {
+ if (n->flags & NODE_VALID) {
n = n->parent;
continue;
}
@@ -356,13 +356,13 @@ in_line_eoln(MACRO_PROT_ARGS)
}
/*
- * Append MAN_EOS in case the last snipped argument
+ * Append NODE_EOS in case the last snipped argument
* ends with a dot, e.g. `.IR syslog (3).'
*/
if (n != man->last &&
mandoc_eos(man->last->string, strlen(man->last->string)))
- man->last->flags |= MAN_EOS;
+ man->last->flags |= NODE_EOS;
/*
* If no arguments are specified and this is MAN_SCOPED (i.e.,
diff --git a/usr/src/cmd/mandoc/man_term.c b/usr/src/cmd/mandoc/man_term.c
index f45e24afea..b2732d4558 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.187 2016/01/08 17:48:09 schwarze Exp $ */
+/* $Id: man_term.c,v 1.191 2017/02/15 14:10:08 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -143,6 +143,7 @@ terminal_man(void *arg, const struct roff_man *man)
struct termp *p;
struct roff_node *n;
struct mtermp mt;
+ size_t save_defindent;
p = (struct termp *)arg;
p->overstep = 0;
@@ -170,6 +171,7 @@ terminal_man(void *arg, const struct roff_man *man)
n = n->next;
}
} else {
+ save_defindent = p->defindent;
if (p->defindent == 0)
p->defindent = 7;
term_begin(p, print_man_head, print_man_foot, &man->meta);
@@ -177,6 +179,7 @@ terminal_man(void *arg, const struct roff_man *man)
if (n != NULL)
print_man_nodelist(p, &mt, n, &man->meta);
term_end(p);
+ p->defindent = save_defindent;
}
}
@@ -319,7 +322,7 @@ pre_alternate(DECL_ARGS)
mt->fl |= MANT_LITERAL;
assert(nn->type == ROFFT_TEXT);
term_word(p, nn->string);
- if (nn->flags & MAN_EOS)
+ if (nn->flags & NODE_EOS)
p->flags |= TERMP_SENTENCE;
if (nn->next)
p->flags |= TERMP_NOSPACE;
@@ -677,7 +680,7 @@ pre_TP(DECL_ARGS)
/* Calculate offset. */
if ((nn = n->parent->head->child) != NULL &&
- nn->string != NULL && ! (MAN_LINE & nn->flags) &&
+ nn->string != NULL && ! (NODE_LINE & nn->flags) &&
a2roffsu(nn->string, &su, SCALE_EN)) {
len = term_hspan(p, &su) / 24;
if (len < 0 && (size_t)(-len) > mt->offset)
@@ -698,7 +701,7 @@ pre_TP(DECL_ARGS)
/* Don't print same-line elements. */
nn = n->child;
- while (NULL != nn && 0 == (MAN_LINE & nn->flags))
+ while (NULL != nn && 0 == (NODE_LINE & nn->flags))
nn = nn->next;
while (NULL != nn) {
@@ -819,7 +822,8 @@ pre_SH(DECL_ARGS)
do {
n = n->prev;
- } while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT);
+ } while (n != NULL && n->tok != TOKEN_NONE &&
+ termacts[n->tok].flags & MAN_NOTEXT);
if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL))
break;
@@ -960,17 +964,17 @@ print_man_node(DECL_ARGS)
if ('\0' == *n->string) {
term_vspace(p);
return;
- } else if (' ' == *n->string && MAN_LINE & n->flags)
+ } else if (' ' == *n->string && NODE_LINE & n->flags)
term_newln(p);
term_word(p, n->string);
goto out;
case ROFFT_EQN:
- if ( ! (n->flags & MAN_LINE))
+ if ( ! (n->flags & NODE_LINE))
p->flags |= TERMP_NOSPACE;
term_eqn(p, n->eqn);
- if (n->next != NULL && ! (n->next->flags & MAN_LINE))
+ if (n->next != NULL && ! (n->next->flags & NODE_LINE))
p->flags |= TERMP_NOSPACE;
return;
case ROFFT_TBL:
@@ -1007,7 +1011,7 @@ out:
*/
if (mt->fl & MANT_LITERAL &&
! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
- (n->next == NULL || n->next->flags & MAN_LINE)) {
+ (n->next == NULL || n->next->flags & NODE_LINE)) {
rm = p->rmargin;
rmax = p->maxrmargin;
p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
@@ -1023,7 +1027,7 @@ out:
p->rmargin = rm;
p->maxrmargin = rmax;
}
- if (MAN_EOS & n->flags)
+ if (NODE_EOS & n->flags)
p->flags |= TERMP_SENTENCE;
}
diff --git a/usr/src/cmd/mandoc/manconf.h b/usr/src/cmd/mandoc/manconf.h
index 782269e7b3..f5c678e890 100644
--- a/usr/src/cmd/mandoc/manconf.h
+++ b/usr/src/cmd/mandoc/manconf.h
@@ -35,6 +35,7 @@ struct manoutput {
int fragment;
int mdoc;
int synopsisonly;
+ int noval;
};
struct manconf {
@@ -44,5 +45,5 @@ struct manconf {
void manconf_parse(struct manconf *, const char *, char *, char *);
-void manconf_output(struct manoutput *, const char *);
+int manconf_output(struct manoutput *, const char *, int);
void manconf_free(struct manconf *);
diff --git a/usr/src/cmd/mandoc/mandoc.h b/usr/src/cmd/mandoc/mandoc.h
index 2ea64ea0ae..a80d6ae7b8 100644
--- a/usr/src/cmd/mandoc/mandoc.h
+++ b/usr/src/cmd/mandoc/mandoc.h
@@ -1,4 +1,4 @@
-/* $Id: mandoc.h,v 1.213 2017/01/09 01:37:03 schwarze Exp $ */
+/* $Id: mandoc.h,v 1.214 2017/01/28 23:30:08 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -134,6 +134,7 @@ enum mandocerr {
MANDOCERR_FI_BLANK, /* blank line in fill mode, using .sp */
MANDOCERR_FI_TAB, /* tab in filled text */
MANDOCERR_SPACE_EOL, /* whitespace at end of input line */
+ MANDOCERR_EOS, /* new sentence, new line */
MANDOCERR_COMMENT_BAD, /* bad comment style */
MANDOCERR_ESC_BAD, /* invalid escape sequence: esc */
MANDOCERR_STR_UNDEF, /* undefined string, using "": name */
diff --git a/usr/src/cmd/mandoc/mandoc_aux.h b/usr/src/cmd/mandoc/mandoc_aux.h
index 2ae3a0cd2d..a2425066c1 100644
--- a/usr/src/cmd/mandoc/mandoc_aux.h
+++ b/usr/src/cmd/mandoc/mandoc_aux.h
@@ -1,4 +1,4 @@
-/* $Id: mandoc_aux.h,v 1.4 2015/11/07 14:01:16 schwarze Exp $ */
+/* $Id: mandoc_aux.h,v 1.6 2017/02/17 14:31:52 schwarze Exp $ */
/*
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
@@ -16,7 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-int mandoc_asprintf(char **, const char *, ...);
+int mandoc_asprintf(char **, const char *, ...)
+ __attribute__((__format__ (__printf__, 2, 3)));
void *mandoc_calloc(size_t, size_t);
void *mandoc_malloc(size_t);
void *mandoc_realloc(void *, size_t);
diff --git a/usr/src/cmd/mandoc/mandocdb.c b/usr/src/cmd/mandoc/mandocdb.c
new file mode 100644
index 0000000000..3b26ca9649
--- /dev/null
+++ b/usr/src/cmd/mandoc/mandocdb.c
@@ -0,0 +1,2316 @@
+/* $Id: mandocdb.c,v 1.244 2017/02/17 14:45:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2011-2017 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2016 Ed Maste <emaste@freebsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <ctype.h>
+#if HAVE_ERR
+#include <err.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#if HAVE_FTS
+#include <fts.h>
+#else
+#include "compat_fts.h"
+#endif
+#include <limits.h>
+#if HAVE_SANDBOX_INIT
+#include <sandbox.h>
+#endif
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mandoc_aux.h"
+#include "mandoc_ohash.h"
+#include "mandoc.h"
+#include "roff.h"
+#include "mdoc.h"
+#include "man.h"
+#include "manconf.h"
+#include "mansearch.h"
+#include "dba_array.h"
+#include "dba.h"
+
+extern const char *const mansearch_keynames[];
+
+enum op {
+ OP_DEFAULT = 0, /* new dbs from dir list or default config */
+ OP_CONFFILE, /* new databases from custom config file */
+ OP_UPDATE, /* delete/add entries in existing database */
+ OP_DELETE, /* delete entries from existing database */
+ OP_TEST /* change no databases, report potential problems */
+};
+
+struct str {
+ const struct mpage *mpage; /* if set, the owning parse */
+ uint64_t mask; /* bitmask in sequence */
+ char key[]; /* rendered text */
+};
+
+struct inodev {
+ ino_t st_ino;
+ dev_t st_dev;
+};
+
+struct mpage {
+ struct inodev inodev; /* used for hashing routine */
+ struct dba_array *dba;
+ char *sec; /* section from file content */
+ char *arch; /* architecture from file content */
+ char *title; /* title from file content */
+ char *desc; /* description from file content */
+ struct mpage *next; /* singly linked list */
+ struct mlink *mlinks; /* singly linked list */
+ int name_head_done;
+ enum form form; /* format from file content */
+};
+
+struct mlink {
+ char file[PATH_MAX]; /* filename rel. to manpath */
+ char *dsec; /* section from directory */
+ char *arch; /* architecture from directory */
+ char *name; /* name from file name (not empty) */
+ char *fsec; /* section from file name suffix */
+ struct mlink *next; /* singly linked list */
+ struct mpage *mpage; /* parent */
+ int gzip; /* filename has a .gz suffix */
+ enum form dform; /* format from directory */
+ enum form fform; /* format from file name suffix */
+};
+
+typedef int (*mdoc_fp)(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+
+struct mdoc_handler {
+ mdoc_fp fp; /* optional handler */
+ uint64_t mask; /* set unless handler returns 0 */
+ int taboo; /* node flags that must not be set */
+};
+
+
+int mandocdb(int, char *[]);
+
+static void dbadd(struct dba *, struct mpage *);
+static void dbadd_mlink(const struct mlink *mlink);
+static void dbprune(struct dba *);
+static void dbwrite(struct dba *);
+static void filescan(const char *);
+#if HAVE_FTS_COMPARE_CONST
+static int fts_compare(const FTSENT *const *, const FTSENT *const *);
+#else
+static int fts_compare(const FTSENT **, const FTSENT **);
+#endif
+static void mlink_add(struct mlink *, const struct stat *);
+static void mlink_check(struct mpage *, struct mlink *);
+static void mlink_free(struct mlink *);
+static void mlinks_undupe(struct mpage *);
+static void mpages_free(void);
+static void mpages_merge(struct dba *, struct mparse *);
+static void parse_cat(struct mpage *, int);
+static void parse_man(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static void parse_mdoc(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static int parse_mdoc_head(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static int parse_mdoc_Fd(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static void parse_mdoc_fname(struct mpage *, const struct roff_node *);
+static int parse_mdoc_Fn(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static int parse_mdoc_Fo(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static int parse_mdoc_Nd(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static int parse_mdoc_Nm(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static int parse_mdoc_Sh(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static int parse_mdoc_Va(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+static int parse_mdoc_Xr(struct mpage *, const struct roff_meta *,
+ const struct roff_node *);
+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);
+static int render_string(char **, size_t *);
+static void say(const char *, const char *, ...)
+ __attribute__((__format__ (__printf__, 2, 3)));
+static int set_basedir(const char *, int);
+static int treescan(void);
+static size_t utf8(unsigned int, char [7]);
+
+static int nodb; /* no database changes */
+static int mparse_options; /* abort the parse early */
+static int use_all; /* use all found files */
+static int debug; /* print what we're doing */
+static int warnings; /* warn about crap */
+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 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 */
+static struct ohash names; /* table of all names */
+static struct ohash strings; /* table of all strings */
+static uint64_t name_mask;
+
+static const struct mdoc_handler mdocs[MDOC_MAX] = {
+ { NULL, 0, 0 }, /* Ap */
+ { NULL, 0, NODE_NOPRT }, /* Dd */
+ { NULL, 0, NODE_NOPRT }, /* Dt */
+ { NULL, 0, NODE_NOPRT }, /* Os */
+ { parse_mdoc_Sh, TYPE_Sh, 0 }, /* Sh */
+ { parse_mdoc_head, TYPE_Ss, 0 }, /* Ss */
+ { NULL, 0, 0 }, /* Pp */
+ { NULL, 0, 0 }, /* D1 */
+ { NULL, 0, 0 }, /* Dl */
+ { NULL, 0, 0 }, /* Bd */
+ { NULL, 0, 0 }, /* Ed */
+ { NULL, 0, 0 }, /* Bl */
+ { NULL, 0, 0 }, /* El */
+ { NULL, 0, 0 }, /* It */
+ { NULL, 0, 0 }, /* Ad */
+ { NULL, TYPE_An, 0 }, /* An */
+ { NULL, TYPE_Ar, 0 }, /* Ar */
+ { NULL, TYPE_Cd, 0 }, /* Cd */
+ { NULL, TYPE_Cm, 0 }, /* Cm */
+ { NULL, TYPE_Dv, 0 }, /* Dv */
+ { NULL, TYPE_Er, 0 }, /* Er */
+ { NULL, TYPE_Ev, 0 }, /* Ev */
+ { NULL, 0, 0 }, /* Ex */
+ { NULL, TYPE_Fa, 0 }, /* Fa */
+ { parse_mdoc_Fd, 0, 0 }, /* Fd */
+ { NULL, TYPE_Fl, 0 }, /* Fl */
+ { parse_mdoc_Fn, 0, 0 }, /* Fn */
+ { NULL, TYPE_Ft, 0 }, /* Ft */
+ { NULL, TYPE_Ic, 0 }, /* Ic */
+ { NULL, TYPE_In, 0 }, /* In */
+ { NULL, TYPE_Li, 0 }, /* Li */
+ { parse_mdoc_Nd, 0, 0 }, /* Nd */
+ { parse_mdoc_Nm, 0, 0 }, /* Nm */
+ { NULL, 0, 0 }, /* Op */
+ { NULL, 0, 0 }, /* Ot */
+ { NULL, TYPE_Pa, NODE_NOSRC }, /* Pa */
+ { NULL, 0, 0 }, /* Rv */
+ { NULL, TYPE_St, 0 }, /* St */
+ { parse_mdoc_Va, TYPE_Va, 0 }, /* Va */
+ { parse_mdoc_Va, TYPE_Vt, 0 }, /* Vt */
+ { parse_mdoc_Xr, 0, 0 }, /* Xr */
+ { NULL, 0, 0 }, /* %A */
+ { NULL, 0, 0 }, /* %B */
+ { NULL, 0, 0 }, /* %D */
+ { NULL, 0, 0 }, /* %I */
+ { NULL, 0, 0 }, /* %J */
+ { NULL, 0, 0 }, /* %N */
+ { NULL, 0, 0 }, /* %O */
+ { NULL, 0, 0 }, /* %P */
+ { NULL, 0, 0 }, /* %R */
+ { NULL, 0, 0 }, /* %T */
+ { NULL, 0, 0 }, /* %V */
+ { NULL, 0, 0 }, /* Ac */
+ { NULL, 0, 0 }, /* Ao */
+ { NULL, 0, 0 }, /* Aq */
+ { NULL, TYPE_At, 0 }, /* At */
+ { NULL, 0, 0 }, /* Bc */
+ { NULL, 0, 0 }, /* Bf */
+ { NULL, 0, 0 }, /* Bo */
+ { NULL, 0, 0 }, /* Bq */
+ { NULL, TYPE_Bsx, NODE_NOSRC }, /* Bsx */
+ { NULL, TYPE_Bx, NODE_NOSRC }, /* Bx */
+ { NULL, 0, 0 }, /* Db */
+ { NULL, 0, 0 }, /* Dc */
+ { NULL, 0, 0 }, /* Do */
+ { NULL, 0, 0 }, /* Dq */
+ { NULL, 0, 0 }, /* Ec */
+ { NULL, 0, 0 }, /* Ef */
+ { NULL, TYPE_Em, 0 }, /* Em */
+ { NULL, 0, 0 }, /* Eo */
+ { NULL, TYPE_Fx, NODE_NOSRC }, /* Fx */
+ { NULL, TYPE_Ms, 0 }, /* Ms */
+ { NULL, 0, 0 }, /* No */
+ { NULL, 0, 0 }, /* Ns */
+ { NULL, TYPE_Nx, NODE_NOSRC }, /* Nx */
+ { NULL, TYPE_Ox, NODE_NOSRC }, /* Ox */
+ { NULL, 0, 0 }, /* Pc */
+ { NULL, 0, 0 }, /* Pf */
+ { NULL, 0, 0 }, /* Po */
+ { NULL, 0, 0 }, /* Pq */
+ { NULL, 0, 0 }, /* Qc */
+ { NULL, 0, 0 }, /* Ql */
+ { NULL, 0, 0 }, /* Qo */
+ { NULL, 0, 0 }, /* Qq */
+ { NULL, 0, 0 }, /* Re */
+ { NULL, 0, 0 }, /* Rs */
+ { NULL, 0, 0 }, /* Sc */
+ { NULL, 0, 0 }, /* So */
+ { NULL, 0, 0 }, /* Sq */
+ { NULL, 0, 0 }, /* Sm */
+ { NULL, 0, 0 }, /* Sx */
+ { NULL, TYPE_Sy, 0 }, /* Sy */
+ { NULL, TYPE_Tn, 0 }, /* Tn */
+ { NULL, 0, NODE_NOSRC }, /* Ux */
+ { NULL, 0, 0 }, /* Xc */
+ { NULL, 0, 0 }, /* Xo */
+ { parse_mdoc_Fo, 0, 0 }, /* Fo */
+ { NULL, 0, 0 }, /* Fc */
+ { NULL, 0, 0 }, /* Oo */
+ { NULL, 0, 0 }, /* Oc */
+ { NULL, 0, 0 }, /* Bk */
+ { NULL, 0, 0 }, /* Ek */
+ { NULL, 0, 0 }, /* Bt */
+ { NULL, 0, 0 }, /* Hf */
+ { NULL, 0, 0 }, /* Fr */
+ { NULL, 0, 0 }, /* Ud */
+ { NULL, TYPE_Lb, NODE_NOSRC }, /* Lb */
+ { NULL, 0, 0 }, /* Lp */
+ { NULL, TYPE_Lk, 0 }, /* Lk */
+ { NULL, TYPE_Mt, NODE_NOSRC }, /* Mt */
+ { NULL, 0, 0 }, /* Brq */
+ { NULL, 0, 0 }, /* Bro */
+ { NULL, 0, 0 }, /* Brc */
+ { NULL, 0, 0 }, /* %C */
+ { NULL, 0, 0 }, /* Es */
+ { NULL, 0, 0 }, /* En */
+ { NULL, TYPE_Dx, NODE_NOSRC }, /* Dx */
+ { NULL, 0, 0 }, /* %Q */
+ { NULL, 0, 0 }, /* br */
+ { NULL, 0, 0 }, /* sp */
+ { NULL, 0, 0 }, /* %U */
+ { NULL, 0, 0 }, /* Ta */
+ { NULL, 0, 0 }, /* ll */
+};
+
+
+int
+mandocdb(int argc, char *argv[])
+{
+ struct manconf conf;
+ struct mparse *mp;
+ struct dba *dba;
+ const char *path_arg, *progname;
+ size_t j, sz;
+ int ch, i;
+
+#if HAVE_PLEDGE
+ if (pledge("stdio rpath wpath cpath fattr flock proc exec", NULL) == -1) {
+ warn("pledge");
+ return (int)MANDOCLEVEL_SYSERR;
+ }
+#endif
+
+#if HAVE_SANDBOX_INIT
+ if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) {
+ warnx("sandbox_init");
+ return (int)MANDOCLEVEL_SYSERR;
+ }
+#endif
+
+ memset(&conf, 0, sizeof(conf));
+
+ /*
+ * We accept a few different invocations.
+ * The CHECKOP macro makes sure that invocation styles don't
+ * clobber each other.
+ */
+#define CHECKOP(_op, _ch) do \
+ if (OP_DEFAULT != (_op)) { \
+ warnx("-%c: Conflicting option", (_ch)); \
+ goto usage; \
+ } while (/*CONSTCOND*/0)
+
+ path_arg = NULL;
+ op = OP_DEFAULT;
+
+ while (-1 != (ch = getopt(argc, argv, "aC:Dd:npQT:tu:v")))
+ switch (ch) {
+ case 'a':
+ use_all = 1;
+ break;
+ case 'C':
+ CHECKOP(op, ch);
+ path_arg = optarg;
+ op = OP_CONFFILE;
+ break;
+ case 'D':
+ debug++;
+ break;
+ case 'd':
+ CHECKOP(op, ch);
+ path_arg = optarg;
+ op = OP_UPDATE;
+ break;
+ case 'n':
+ nodb = 1;
+ break;
+ case 'p':
+ warnings = 1;
+ break;
+ case 'Q':
+ mparse_options |= MPARSE_QUICK;
+ break;
+ case 'T':
+ if (strcmp(optarg, "utf8")) {
+ warnx("-T%s: Unsupported output format",
+ optarg);
+ goto usage;
+ }
+ write_utf8 = 1;
+ break;
+ case 't':
+ CHECKOP(op, ch);
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ op = OP_TEST;
+ nodb = warnings = 1;
+ break;
+ case 'u':
+ CHECKOP(op, ch);
+ path_arg = optarg;
+ op = OP_DELETE;
+ break;
+ case 'v':
+ /* Compatibility with espie@'s makewhatis. */
+ break;
+ default:
+ goto usage;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+#if HAVE_PLEDGE
+ if (nodb) {
+ if (pledge("stdio rpath", NULL) == -1) {
+ warn("pledge");
+ return (int)MANDOCLEVEL_SYSERR;
+ }
+ }
+#endif
+
+ if (OP_CONFFILE == op && argc > 0) {
+ warnx("-C: Too many arguments");
+ goto usage;
+ }
+
+ exitcode = (int)MANDOCLEVEL_OK;
+ mchars_alloc();
+ mp = mparse_alloc(mparse_options, MANDOCLEVEL_BADARG, NULL, NULL);
+ 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) {
+
+ /*
+ * Most of these deal with a specific directory.
+ * Jump into that directory first.
+ */
+ if (OP_TEST != op && 0 == set_basedir(path_arg, 1))
+ goto out;
+
+ dba = nodb ? dba_new(128) : dba_read(MANDOC_DB);
+ if (dba != NULL) {
+ /*
+ * The existing database is usable. Process
+ * all files specified on the command-line.
+ */
+#if HAVE_PLEDGE
+ if (!nodb) {
+ if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1) {
+ warn("pledge");
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ goto out;
+ }
+ }
+#endif
+ use_all = 1;
+ for (i = 0; i < argc; i++)
+ filescan(argv[i]);
+ if (nodb == 0)
+ dbprune(dba);
+ } else {
+ /* Database missing or corrupt. */
+ if (op != OP_UPDATE || errno != ENOENT)
+ say(MANDOC_DB, "%s: Automatically recreating"
+ " from scratch", strerror(errno));
+ exitcode = (int)MANDOCLEVEL_OK;
+ op = OP_DEFAULT;
+ if (0 == treescan())
+ goto out;
+ dba = dba_new(128);
+ }
+ if (OP_DELETE != op)
+ mpages_merge(dba, mp);
+ if (nodb == 0)
+ dbwrite(dba);
+ dba_free(dba);
+ } else {
+ /*
+ * If we have arguments, use them as our manpaths.
+ * If we don't, use man.conf(5).
+ */
+ if (argc > 0) {
+ conf.manpath.paths = mandoc_reallocarray(NULL,
+ argc, sizeof(char *));
+ conf.manpath.sz = (size_t)argc;
+ for (i = 0; i < argc; i++)
+ conf.manpath.paths[i] = mandoc_strdup(argv[i]);
+ } else
+ manconf_parse(&conf, path_arg, NULL, NULL);
+
+ if (conf.manpath.sz == 0) {
+ exitcode = (int)MANDOCLEVEL_BADARG;
+ say("", "Empty manpath");
+ }
+
+ /*
+ * First scan the tree rooted at a base directory, then
+ * build a new database and finally move it into place.
+ * Ignore zero-length directories and strip trailing
+ * slashes.
+ */
+ for (j = 0; j < conf.manpath.sz; j++) {
+ sz = strlen(conf.manpath.paths[j]);
+ if (sz && conf.manpath.paths[j][sz - 1] == '/')
+ conf.manpath.paths[j][--sz] = '\0';
+ if (0 == sz)
+ continue;
+
+ if (j) {
+ mandoc_ohash_init(&mpages, 6,
+ offsetof(struct mpage, inodev));
+ mandoc_ohash_init(&mlinks, 6,
+ offsetof(struct mlink, file));
+ }
+
+ if ( ! set_basedir(conf.manpath.paths[j], argc > 0))
+ continue;
+ if (0 == treescan())
+ continue;
+ dba = dba_new(128);
+ mpages_merge(dba, mp);
+ if (nodb == 0)
+ dbwrite(dba);
+ dba_free(dba);
+
+ if (j + 1 < conf.manpath.sz) {
+ mpages_free();
+ ohash_delete(&mpages);
+ ohash_delete(&mlinks);
+ }
+ }
+ }
+out:
+ manconf_free(&conf);
+ mparse_free(mp);
+ mchars_free();
+ mpages_free();
+ ohash_delete(&mpages);
+ ohash_delete(&mlinks);
+ return exitcode;
+usage:
+ progname = getprogname();
+ fprintf(stderr, "usage: %s [-aDnpQ] [-C file] [-Tutf8]\n"
+ " %s [-aDnpQ] [-Tutf8] dir ...\n"
+ " %s [-DnpQ] [-Tutf8] -d dir [file ...]\n"
+ " %s [-Dnp] -u dir [file ...]\n"
+ " %s [-Q] -t file ...\n",
+ progname, progname, progname, progname, progname);
+
+ return (int)MANDOCLEVEL_BADARG;
+}
+
+/*
+ * To get a singly linked list in alpha order while inserting entries
+ * at the beginning, process directory entries in reverse alpha order.
+ */
+static int
+#if HAVE_FTS_COMPARE_CONST
+fts_compare(const FTSENT *const *a, const FTSENT *const *b)
+#else
+fts_compare(const FTSENT **a, const FTSENT **b)
+#endif
+{
+ return -strcmp((*a)->fts_name, (*b)->fts_name);
+}
+
+/*
+ * Scan a directory tree rooted at "basedir" for manpages.
+ * We use fts(), scanning directory parts along the way for clues to our
+ * section and architecture.
+ *
+ * If use_all has been specified, grok all files.
+ * If not, sanitise paths to the following:
+ *
+ * [./]man*[/<arch>]/<name>.<section>
+ * or
+ * [./]cat<section>[/<arch>]/<name>.0
+ *
+ * TODO: accommodate for multi-language directories.
+ */
+static int
+treescan(void)
+{
+ char buf[PATH_MAX];
+ FTS *f;
+ FTSENT *ff;
+ struct mlink *mlink;
+ int gzip;
+ enum form dform;
+ char *dsec, *arch, *fsec, *cp;
+ const char *path;
+ const char *argv[2];
+
+ argv[0] = ".";
+ argv[1] = NULL;
+
+ f = fts_open((char * const *)argv, FTS_PHYSICAL | FTS_NOCHDIR,
+ fts_compare);
+ if (f == NULL) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "&fts_open");
+ return 0;
+ }
+
+ dsec = arch = NULL;
+ dform = FORM_NONE;
+
+ while ((ff = fts_read(f)) != NULL) {
+ path = ff->fts_path + 2;
+ switch (ff->fts_info) {
+
+ /*
+ * Symbolic links require various sanity checks,
+ * then get handled just like regular files.
+ */
+ case FTS_SL:
+ if (realpath(path, buf) == NULL) {
+ if (warnings)
+ say(path, "&realpath");
+ continue;
+ }
+ if (strstr(buf, basedir) != buf
+#ifdef HOMEBREWDIR
+ && strstr(buf, HOMEBREWDIR) != buf
+#endif
+ ) {
+ if (warnings) say("",
+ "%s: outside base directory", buf);
+ continue;
+ }
+ /* Use logical inode to avoid mpages dupe. */
+ if (stat(path, ff->fts_statp) == -1) {
+ if (warnings)
+ say(path, "&stat");
+ continue;
+ }
+ /* FALLTHROUGH */
+
+ /*
+ * If we're a regular file, add an mlink by using the
+ * stored directory data and handling the filename.
+ */
+ case FTS_F:
+ if ( ! strcmp(path, MANDOC_DB))
+ continue;
+ if ( ! use_all && ff->fts_level < 2) {
+ if (warnings)
+ say(path, "Extraneous file");
+ continue;
+ }
+ gzip = 0;
+ fsec = NULL;
+ while (fsec == NULL) {
+ fsec = strrchr(ff->fts_name, '.');
+ if (fsec == NULL || strcmp(fsec+1, "gz"))
+ break;
+ gzip = 1;
+ *fsec = '\0';
+ fsec = NULL;
+ }
+ if (fsec == NULL) {
+ if ( ! use_all) {
+ if (warnings)
+ say(path,
+ "No filename suffix");
+ continue;
+ }
+ } else if ( ! strcmp(++fsec, "html")) {
+ if (warnings)
+ say(path, "Skip html");
+ continue;
+ } else if ( ! strcmp(fsec, "ps")) {
+ if (warnings)
+ say(path, "Skip ps");
+ continue;
+ } else if ( ! strcmp(fsec, "pdf")) {
+ if (warnings)
+ say(path, "Skip pdf");
+ continue;
+ } else if ( ! use_all &&
+ ((dform == FORM_SRC &&
+ strncmp(fsec, dsec, strlen(dsec))) ||
+ (dform == FORM_CAT && strcmp(fsec, "0")))) {
+ if (warnings)
+ say(path, "Wrong filename suffix");
+ continue;
+ } else
+ fsec[-1] = '\0';
+
+ mlink = mandoc_calloc(1, sizeof(struct mlink));
+ if (strlcpy(mlink->file, path,
+ sizeof(mlink->file)) >=
+ sizeof(mlink->file)) {
+ say(path, "Filename too long");
+ free(mlink);
+ continue;
+ }
+ mlink->dform = dform;
+ mlink->dsec = dsec;
+ mlink->arch = arch;
+ mlink->name = ff->fts_name;
+ mlink->fsec = fsec;
+ mlink->gzip = gzip;
+ mlink_add(mlink, ff->fts_statp);
+ continue;
+
+ case FTS_D:
+ case FTS_DP:
+ break;
+
+ default:
+ if (warnings)
+ say(path, "Not a regular file");
+ continue;
+ }
+
+ switch (ff->fts_level) {
+ case 0:
+ /* Ignore the root directory. */
+ break;
+ case 1:
+ /*
+ * This might contain manX/ or catX/.
+ * Try to infer this from the name.
+ * If we're not in use_all, enforce it.
+ */
+ cp = ff->fts_name;
+ if (ff->fts_info == FTS_DP) {
+ dform = FORM_NONE;
+ dsec = NULL;
+ break;
+ }
+
+ if ( ! strncmp(cp, "man", 3)) {
+ dform = FORM_SRC;
+ dsec = cp + 3;
+ } else if ( ! strncmp(cp, "cat", 3)) {
+ dform = FORM_CAT;
+ dsec = cp + 3;
+ } else {
+ dform = FORM_NONE;
+ dsec = NULL;
+ }
+
+ if (dsec != NULL || use_all)
+ break;
+
+ if (warnings)
+ say(path, "Unknown directory part");
+ fts_set(f, ff, FTS_SKIP);
+ break;
+ case 2:
+ /*
+ * Possibly our architecture.
+ * If we're descending, keep tabs on it.
+ */
+ if (ff->fts_info != FTS_DP && dsec != NULL)
+ arch = ff->fts_name;
+ else
+ arch = NULL;
+ break;
+ default:
+ if (ff->fts_info == FTS_DP || use_all)
+ break;
+ if (warnings)
+ say(path, "Extraneous directory part");
+ fts_set(f, ff, FTS_SKIP);
+ break;
+ }
+ }
+
+ fts_close(f);
+ return 1;
+}
+
+/*
+ * Add a file to the mlinks table.
+ * Do not verify that it's a "valid" looking manpage (we'll do that
+ * later).
+ *
+ * Try to infer the manual section, architecture, and page name from the
+ * path, assuming it looks like
+ *
+ * [./]man*[/<arch>]/<name>.<section>
+ * or
+ * [./]cat<section>[/<arch>]/<name>.0
+ *
+ * See treescan() for the fts(3) version of this.
+ */
+static void
+filescan(const char *file)
+{
+ char buf[PATH_MAX];
+ struct stat st;
+ struct mlink *mlink;
+ char *p, *start;
+
+ assert(use_all);
+
+ if (0 == strncmp(file, "./", 2))
+ file += 2;
+
+ /*
+ * We have to do lstat(2) before realpath(3) loses
+ * the information whether this is a symbolic link.
+ * We need to know that because for symbolic links,
+ * we want to use the orginal file name, while for
+ * regular files, we want to use the real path.
+ */
+ if (-1 == lstat(file, &st)) {
+ exitcode = (int)MANDOCLEVEL_BADARG;
+ say(file, "&lstat");
+ return;
+ } else if (0 == ((S_IFREG | S_IFLNK) & st.st_mode)) {
+ exitcode = (int)MANDOCLEVEL_BADARG;
+ say(file, "Not a regular file");
+ return;
+ }
+
+ /*
+ * We have to resolve the file name to the real path
+ * in any case for the base directory check.
+ */
+ if (NULL == realpath(file, buf)) {
+ exitcode = (int)MANDOCLEVEL_BADARG;
+ say(file, "&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;
+#endif
+ else {
+ exitcode = (int)MANDOCLEVEL_BADARG;
+ say("", "%s: outside base directory", buf);
+ return;
+ }
+
+ /*
+ * 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)) {
+ exitcode = (int)MANDOCLEVEL_BADARG;
+ say(file, "&stat");
+ return;
+ }
+ if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) {
+ say(file, "Filename too long");
+ return;
+ }
+ start = buf;
+ if (OP_TEST != op && strstr(buf, basedir) == buf)
+ start += strlen(basedir);
+ }
+
+ mlink = mandoc_calloc(1, sizeof(struct mlink));
+ mlink->dform = FORM_NONE;
+ if (strlcpy(mlink->file, start, sizeof(mlink->file)) >=
+ sizeof(mlink->file)) {
+ say(start, "Filename too long");
+ free(mlink);
+ return;
+ }
+
+ /*
+ * In test mode or when the original name is absolute
+ * 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)
+ start += 5;
+ else
+ start = buf;
+ }
+
+ /*
+ * First try to guess our directory structure.
+ * If we find a separator, try to look for man* or cat*.
+ * If we find one of these and what's underneath is a directory,
+ * assume it's an architecture.
+ */
+ if (NULL != (p = strchr(start, '/'))) {
+ *p++ = '\0';
+ if (0 == strncmp(start, "man", 3)) {
+ mlink->dform = FORM_SRC;
+ mlink->dsec = start + 3;
+ } else if (0 == strncmp(start, "cat", 3)) {
+ mlink->dform = FORM_CAT;
+ mlink->dsec = start + 3;
+ }
+
+ start = p;
+ if (NULL != mlink->dsec && NULL != (p = strchr(start, '/'))) {
+ *p++ = '\0';
+ mlink->arch = start;
+ start = p;
+ }
+ }
+
+ /*
+ * Now check the file suffix.
+ * Suffix of `.0' indicates a catpage, `.1-9' is a manpage.
+ */
+ p = strrchr(start, '\0');
+ while (p-- > start && '/' != *p && '.' != *p)
+ /* Loop. */ ;
+
+ if ('.' == *p) {
+ *p++ = '\0';
+ mlink->fsec = p;
+ }
+
+ /*
+ * Now try to parse the name.
+ * Use the filename portion of the path.
+ */
+ mlink->name = start;
+ if (NULL != (p = strrchr(start, '/'))) {
+ mlink->name = p + 1;
+ *p = '\0';
+ }
+ mlink_add(mlink, &st);
+}
+
+static void
+mlink_add(struct mlink *mlink, const struct stat *st)
+{
+ struct inodev inodev;
+ struct mpage *mpage;
+ unsigned int slot;
+
+ assert(NULL != mlink->file);
+
+ mlink->dsec = mandoc_strdup(mlink->dsec ? mlink->dsec : "");
+ mlink->arch = mandoc_strdup(mlink->arch ? mlink->arch : "");
+ mlink->name = mandoc_strdup(mlink->name ? mlink->name : "");
+ mlink->fsec = mandoc_strdup(mlink->fsec ? mlink->fsec : "");
+
+ if ('0' == *mlink->fsec) {
+ free(mlink->fsec);
+ mlink->fsec = mandoc_strdup(mlink->dsec);
+ mlink->fform = FORM_CAT;
+ } else if ('1' <= *mlink->fsec && '9' >= *mlink->fsec)
+ mlink->fform = FORM_SRC;
+ else
+ mlink->fform = FORM_NONE;
+
+ slot = ohash_qlookup(&mlinks, mlink->file);
+ assert(NULL == ohash_find(&mlinks, slot));
+ ohash_insert(&mlinks, slot, mlink);
+
+ memset(&inodev, 0, sizeof(inodev)); /* Clear padding. */
+ inodev.st_ino = st->st_ino;
+ inodev.st_dev = st->st_dev;
+ slot = ohash_lookup_memory(&mpages, (char *)&inodev,
+ sizeof(struct inodev), inodev.st_ino);
+ mpage = ohash_find(&mpages, slot);
+ if (NULL == mpage) {
+ mpage = mandoc_calloc(1, sizeof(struct mpage));
+ mpage->inodev.st_ino = inodev.st_ino;
+ mpage->inodev.st_dev = inodev.st_dev;
+ mpage->form = FORM_NONE;
+ mpage->next = mpage_head;
+ mpage_head = mpage;
+ ohash_insert(&mpages, slot, mpage);
+ } else
+ mlink->next = mpage->mlinks;
+ mpage->mlinks = mlink;
+ mlink->mpage = mpage;
+}
+
+static void
+mlink_free(struct mlink *mlink)
+{
+
+ free(mlink->dsec);
+ free(mlink->arch);
+ free(mlink->name);
+ free(mlink->fsec);
+ free(mlink);
+}
+
+static void
+mpages_free(void)
+{
+ struct mpage *mpage;
+ struct mlink *mlink;
+
+ while ((mpage = mpage_head) != NULL) {
+ while ((mlink = mpage->mlinks) != NULL) {
+ mpage->mlinks = mlink->next;
+ mlink_free(mlink);
+ }
+ mpage_head = mpage->next;
+ free(mpage->sec);
+ free(mpage->arch);
+ free(mpage->title);
+ free(mpage->desc);
+ free(mpage);
+ }
+}
+
+/*
+ * For each mlink to the mpage, check whether the path looks like
+ * it is formatted, and if it does, check whether a source manual
+ * exists by the same name, ignoring the suffix.
+ * If both conditions hold, drop the mlink.
+ */
+static void
+mlinks_undupe(struct mpage *mpage)
+{
+ char buf[PATH_MAX];
+ struct mlink **prev;
+ struct mlink *mlink;
+ char *bufp;
+
+ mpage->form = FORM_CAT;
+ prev = &mpage->mlinks;
+ while (NULL != (mlink = *prev)) {
+ if (FORM_CAT != mlink->dform) {
+ mpage->form = FORM_NONE;
+ goto nextlink;
+ }
+ (void)strlcpy(buf, mlink->file, sizeof(buf));
+ bufp = strstr(buf, "cat");
+ assert(NULL != bufp);
+ memcpy(bufp, "man", 3);
+ if (NULL != (bufp = strrchr(buf, '.')))
+ *++bufp = '\0';
+ (void)strlcat(buf, mlink->dsec, sizeof(buf));
+ if (NULL == ohash_find(&mlinks,
+ ohash_qlookup(&mlinks, buf)))
+ goto nextlink;
+ if (warnings)
+ say(mlink->file, "Man source exists: %s", buf);
+ if (use_all)
+ goto nextlink;
+ *prev = mlink->next;
+ mlink_free(mlink);
+ continue;
+nextlink:
+ prev = &(*prev)->next;
+ }
+}
+
+static void
+mlink_check(struct mpage *mpage, struct mlink *mlink)
+{
+ struct str *str;
+ unsigned int slot;
+
+ /*
+ * Check whether the manual section given in a file
+ * agrees with the directory where the file is located.
+ * Some manuals have suffixes like (3p) on their
+ * section number either inside the file or in the
+ * directory name, some are linked into more than one
+ * section, like encrypt(1) = makekey(8).
+ */
+
+ if (FORM_SRC == mpage->form &&
+ strcasecmp(mpage->sec, mlink->dsec))
+ say(mlink->file, "Section \"%s\" manual in %s directory",
+ mpage->sec, mlink->dsec);
+
+ /*
+ * Manual page directories exist for each kernel
+ * architecture as returned by machine(1).
+ * However, many manuals only depend on the
+ * application architecture as returned by arch(1).
+ * For example, some (2/ARM) manuals are shared
+ * across the "armish" and "zaurus" kernel
+ * architectures.
+ * A few manuals are even shared across completely
+ * different architectures, for example fdformat(1)
+ * on amd64, i386, and sparc64.
+ */
+
+ if (strcasecmp(mpage->arch, mlink->arch))
+ say(mlink->file, "Architecture \"%s\" manual in "
+ "\"%s\" directory", mpage->arch, mlink->arch);
+
+ /*
+ * XXX
+ * parse_cat() doesn't set NAME_TITLE yet.
+ */
+
+ if (FORM_CAT == mpage->form)
+ return;
+
+ /*
+ * Check whether this mlink
+ * appears as a name in the NAME section.
+ */
+
+ slot = ohash_qlookup(&names, mlink->name);
+ str = ohash_find(&names, slot);
+ assert(NULL != str);
+ if ( ! (NAME_TITLE & str->mask))
+ say(mlink->file, "Name missing in NAME section");
+}
+
+/*
+ * Run through the files in the global vector "mpages"
+ * and add them to the database specified in "basedir".
+ *
+ * This handles the parsing scheme itself, using the cues of directory
+ * and filename to determine whether the file is parsable or not.
+ */
+static void
+mpages_merge(struct dba *dba, struct mparse *mp)
+{
+ struct mpage *mpage, *mpage_dest;
+ struct mlink *mlink, *mlink_dest;
+ struct roff_man *man;
+ char *sodest;
+ char *cp;
+ int fd;
+
+ for (mpage = mpage_head; mpage != NULL; mpage = mpage->next) {
+ mlinks_undupe(mpage);
+ if ((mlink = mpage->mlinks) == NULL)
+ continue;
+
+ name_mask = NAME_MASK;
+ mandoc_ohash_init(&names, 4, offsetof(struct str, key));
+ mandoc_ohash_init(&strings, 6, offsetof(struct str, key));
+ mparse_reset(mp);
+ man = NULL;
+ sodest = NULL;
+
+ if ((fd = mparse_open(mp, mlink->file)) == -1) {
+ say(mlink->file, "&open");
+ goto nextpage;
+ }
+
+ /*
+ * Interpret the file as mdoc(7) or man(7) source
+ * code, unless it is known to be formatted.
+ */
+ if (mlink->dform != FORM_CAT || mlink->fform != FORM_CAT) {
+ mparse_readfd(mp, fd, mlink->file);
+ close(fd);
+ fd = -1;
+ mparse_result(mp, &man, &sodest);
+ }
+
+ if (sodest != NULL) {
+ mlink_dest = ohash_find(&mlinks,
+ ohash_qlookup(&mlinks, sodest));
+ if (mlink_dest == NULL) {
+ mandoc_asprintf(&cp, "%s.gz", sodest);
+ mlink_dest = ohash_find(&mlinks,
+ ohash_qlookup(&mlinks, cp));
+ free(cp);
+ }
+ if (mlink_dest != NULL) {
+
+ /* The .so target exists. */
+
+ mpage_dest = mlink_dest->mpage;
+ while (1) {
+ mlink->mpage = mpage_dest;
+
+ /*
+ * If the target was already
+ * processed, add the links
+ * to the database now.
+ * Otherwise, this will
+ * happen when we come
+ * to the target.
+ */
+
+ if (mpage_dest->dba != NULL)
+ dbadd_mlink(mlink);
+
+ if (mlink->next == NULL)
+ break;
+ mlink = mlink->next;
+ }
+
+ /* Move all links to the target. */
+
+ mlink->next = mlink_dest->next;
+ mlink_dest->next = mpage->mlinks;
+ mpage->mlinks = NULL;
+ }
+ goto nextpage;
+ } else if (man != NULL && man->macroset == MACROSET_MDOC) {
+ mdoc_validate(man);
+ mpage->form = FORM_SRC;
+ mpage->sec = man->meta.msec;
+ mpage->sec = mandoc_strdup(
+ mpage->sec == NULL ? "" : mpage->sec);
+ mpage->arch = man->meta.arch;
+ mpage->arch = mandoc_strdup(
+ mpage->arch == NULL ? "" : mpage->arch);
+ mpage->title = mandoc_strdup(man->meta.title);
+ } else if (man != NULL && man->macroset == MACROSET_MAN) {
+ man_validate(man);
+ if (*man->meta.msec != '\0' ||
+ *man->meta.msec != '\0') {
+ mpage->form = FORM_SRC;
+ mpage->sec = mandoc_strdup(man->meta.msec);
+ mpage->arch = mandoc_strdup(mlink->arch);
+ mpage->title = mandoc_strdup(man->meta.title);
+ } else
+ man = NULL;
+ }
+
+ assert(mpage->desc == NULL);
+ if (man == NULL) {
+ mpage->form = FORM_CAT;
+ mpage->sec = mandoc_strdup(mlink->dsec);
+ mpage->arch = mandoc_strdup(mlink->arch);
+ mpage->title = mandoc_strdup(mlink->name);
+ parse_cat(mpage, fd);
+ } else if (man->macroset == MACROSET_MDOC)
+ parse_mdoc(mpage, &man->meta, man->first);
+ else
+ parse_man(mpage, &man->meta, man->first);
+ if (mpage->desc == NULL) {
+ mpage->desc = mandoc_strdup(mlink->name);
+ if (warnings)
+ say(mlink->file, "No one-line description, "
+ "using filename \"%s\"", mlink->name);
+ }
+
+ for (mlink = mpage->mlinks;
+ mlink != NULL;
+ mlink = mlink->next) {
+ putkey(mpage, mlink->name, NAME_FILE);
+ if (warnings && !use_all)
+ mlink_check(mpage, mlink);
+ }
+
+ dbadd(dba, mpage);
+
+nextpage:
+ ohash_delete(&strings);
+ ohash_delete(&names);
+ }
+}
+
+static void
+parse_cat(struct mpage *mpage, int fd)
+{
+ FILE *stream;
+ struct mlink *mlink;
+ char *line, *p, *title, *sec;
+ size_t linesz, plen, titlesz;
+ ssize_t len;
+ int offs;
+
+ mlink = mpage->mlinks;
+ stream = fd == -1 ? fopen(mlink->file, "r") : fdopen(fd, "r");
+ if (stream == NULL) {
+ if (fd != -1)
+ close(fd);
+ if (warnings)
+ say(mlink->file, "&fopen");
+ return;
+ }
+
+ line = NULL;
+ linesz = 0;
+
+ /* Parse the section number from the header line. */
+
+ while (getline(&line, &linesz, stream) != -1) {
+ if (*line == '\n')
+ continue;
+ if ((sec = strchr(line, '(')) == NULL)
+ break;
+ if ((p = strchr(++sec, ')')) == NULL)
+ break;
+ free(mpage->sec);
+ mpage->sec = mandoc_strndup(sec, p - sec);
+ if (warnings && *mlink->dsec != '\0' &&
+ strcasecmp(mpage->sec, mlink->dsec))
+ say(mlink->file,
+ "Section \"%s\" manual in %s directory",
+ mpage->sec, mlink->dsec);
+ break;
+ }
+
+ /* Skip to first blank line. */
+
+ while (line == NULL || *line != '\n')
+ if (getline(&line, &linesz, stream) == -1)
+ break;
+
+ /*
+ * Assume the first line that is not indented
+ * is the first section header. Skip to it.
+ */
+
+ while (getline(&line, &linesz, stream) != -1)
+ if (*line != '\n' && *line != ' ')
+ break;
+
+ /*
+ * Read up until the next section into a buffer.
+ * Strip the leading and trailing newline from each read line,
+ * appending a trailing space.
+ * Ignore empty (whitespace-only) lines.
+ */
+
+ titlesz = 0;
+ title = NULL;
+
+ while ((len = getline(&line, &linesz, stream)) != -1) {
+ if (*line != ' ')
+ break;
+ offs = 0;
+ while (isspace((unsigned char)line[offs]))
+ offs++;
+ if (line[offs] == '\0')
+ continue;
+ title = mandoc_realloc(title, titlesz + len - offs);
+ memcpy(title + titlesz, line + offs, len - offs);
+ titlesz += len - offs;
+ title[titlesz - 1] = ' ';
+ }
+ free(line);
+
+ /*
+ * If no page content can be found, or the input line
+ * is already the next section header, or there is no
+ * trailing newline, reuse the page title as the page
+ * description.
+ */
+
+ if (NULL == title || '\0' == *title) {
+ if (warnings)
+ say(mlink->file, "Cannot find NAME section");
+ fclose(stream);
+ free(title);
+ return;
+ }
+
+ title[titlesz - 1] = '\0';
+
+ /*
+ * Skip to the first dash.
+ * Use the remaining line as the description (no more than 70
+ * bytes).
+ */
+
+ if (NULL != (p = strstr(title, "- "))) {
+ for (p += 2; ' ' == *p || '\b' == *p; p++)
+ /* Skip to next word. */ ;
+ } else {
+ if (warnings)
+ say(mlink->file, "No dash in title line, "
+ "reusing \"%s\" as one-line description", title);
+ p = title;
+ }
+
+ plen = strlen(p);
+
+ /* Strip backspace-encoding from line. */
+
+ while (NULL != (line = memchr(p, '\b', plen))) {
+ len = line - p;
+ if (0 == len) {
+ memmove(line, line + 1, plen--);
+ continue;
+ }
+ memmove(line - 1, line + 1, plen - len);
+ plen -= 2;
+ }
+
+ mpage->desc = mandoc_strdup(p);
+ fclose(stream);
+ free(title);
+}
+
+/*
+ * Put a type/word pair into the word database for this particular file.
+ */
+static void
+putkey(const struct mpage *mpage, char *value, uint64_t type)
+{
+ putkeys(mpage, value, strlen(value), type);
+}
+
+/*
+ * Grok all nodes at or below a certain mdoc node into putkey().
+ */
+static void
+putmdockey(const struct mpage *mpage,
+ const struct roff_node *n, uint64_t m, int taboo)
+{
+
+ for ( ; NULL != n; n = n->next) {
+ if (n->flags & taboo)
+ continue;
+ if (NULL != n->child)
+ putmdockey(mpage, n->child, m, taboo);
+ if (n->type == ROFFT_TEXT)
+ putkey(mpage, n->string, m);
+ }
+}
+
+static void
+parse_man(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+ const struct roff_node *head, *body;
+ char *start, *title;
+ char byte;
+ size_t sz;
+
+ if (n == NULL)
+ return;
+
+ /*
+ * We're only searching for one thing: the first text child in
+ * the BODY of a NAME section. Since we don't keep track of
+ * sections in -man, run some hoops to find out whether we're in
+ * the correct section or not.
+ */
+
+ if (n->type == ROFFT_BODY && n->tok == MAN_SH) {
+ body = n;
+ if ((head = body->parent->head) != NULL &&
+ (head = head->child) != NULL &&
+ head->next == NULL &&
+ head->type == ROFFT_TEXT &&
+ strcmp(head->string, "NAME") == 0 &&
+ body->child != NULL) {
+
+ /*
+ * Suck the entire NAME section into memory.
+ * Yes, we might run away.
+ * But too many manuals have big, spread-out
+ * NAME sections over many lines.
+ */
+
+ title = NULL;
+ deroff(&title, body);
+ if (NULL == title)
+ return;
+
+ /*
+ * Go through a special heuristic dance here.
+ * Conventionally, one or more manual names are
+ * comma-specified prior to a whitespace, then a
+ * dash, then a description. Try to puzzle out
+ * the name parts here.
+ */
+
+ start = title;
+ for ( ;; ) {
+ sz = strcspn(start, " ,");
+ if ('\0' == start[sz])
+ break;
+
+ byte = start[sz];
+ start[sz] = '\0';
+
+ /*
+ * Assume a stray trailing comma in the
+ * name list if a name begins with a dash.
+ */
+
+ if ('-' == start[0] ||
+ ('\\' == start[0] && '-' == start[1]))
+ break;
+
+ putkey(mpage, start, NAME_TITLE);
+ if ( ! (mpage->name_head_done ||
+ strcasecmp(start, meta->title))) {
+ putkey(mpage, start, NAME_HEAD);
+ mpage->name_head_done = 1;
+ }
+
+ if (' ' == byte) {
+ start += sz + 1;
+ break;
+ }
+
+ assert(',' == byte);
+ start += sz + 1;
+ while (' ' == *start)
+ start++;
+ }
+
+ if (start == title) {
+ putkey(mpage, start, NAME_TITLE);
+ if ( ! (mpage->name_head_done ||
+ strcasecmp(start, meta->title))) {
+ putkey(mpage, start, NAME_HEAD);
+ mpage->name_head_done = 1;
+ }
+ free(title);
+ return;
+ }
+
+ while (isspace((unsigned char)*start))
+ start++;
+
+ if (0 == strncmp(start, "-", 1))
+ start += 1;
+ else if (0 == strncmp(start, "\\-\\-", 4))
+ start += 4;
+ else if (0 == strncmp(start, "\\-", 2))
+ start += 2;
+ else if (0 == strncmp(start, "\\(en", 4))
+ start += 4;
+ else if (0 == strncmp(start, "\\(em", 4))
+ start += 4;
+
+ while (' ' == *start)
+ start++;
+
+ mpage->desc = mandoc_strdup(start);
+ free(title);
+ return;
+ }
+ }
+
+ for (n = n->child; n; n = n->next) {
+ if (NULL != mpage->desc)
+ break;
+ parse_man(mpage, meta, n);
+ }
+}
+
+static void
+parse_mdoc(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+
+ assert(NULL != n);
+ for (n = n->child; NULL != n; n = n->next) {
+ if (n->flags & mdocs[n->tok].taboo)
+ continue;
+ switch (n->type) {
+ case ROFFT_ELEM:
+ case ROFFT_BLOCK:
+ case ROFFT_HEAD:
+ case ROFFT_BODY:
+ case ROFFT_TAIL:
+ if (NULL != mdocs[n->tok].fp)
+ if (0 == (*mdocs[n->tok].fp)(mpage, meta, n))
+ break;
+ if (mdocs[n->tok].mask)
+ putmdockey(mpage, n->child,
+ mdocs[n->tok].mask, mdocs[n->tok].taboo);
+ break;
+ default:
+ assert(n->type != ROFFT_ROOT);
+ continue;
+ }
+ if (NULL != n->child)
+ parse_mdoc(mpage, meta, n);
+ }
+}
+
+static int
+parse_mdoc_Fd(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+ char *start, *end;
+ size_t sz;
+
+ if (SEC_SYNOPSIS != n->sec ||
+ NULL == (n = n->child) ||
+ n->type != ROFFT_TEXT)
+ return 0;
+
+ /*
+ * Only consider those `Fd' macro fields that begin with an
+ * "inclusion" token (versus, e.g., #define).
+ */
+
+ if (strcmp("#include", n->string))
+ return 0;
+
+ if ((n = n->next) == NULL || n->type != ROFFT_TEXT)
+ return 0;
+
+ /*
+ * Strip away the enclosing angle brackets and make sure we're
+ * not zero-length.
+ */
+
+ start = n->string;
+ if ('<' == *start || '"' == *start)
+ start++;
+
+ if (0 == (sz = strlen(start)))
+ return 0;
+
+ end = &start[(int)sz - 1];
+ if ('>' == *end || '"' == *end)
+ end--;
+
+ if (end > start)
+ putkeys(mpage, start, end - start + 1, TYPE_In);
+ return 0;
+}
+
+static void
+parse_mdoc_fname(struct mpage *mpage, const struct roff_node *n)
+{
+ char *cp;
+ size_t sz;
+
+ if (n->type != ROFFT_TEXT)
+ return;
+
+ /* Skip function pointer punctuation. */
+
+ cp = n->string;
+ while (*cp == '(' || *cp == '*')
+ cp++;
+ sz = strcspn(cp, "()");
+
+ putkeys(mpage, cp, sz, TYPE_Fn);
+ if (n->sec == SEC_SYNOPSIS)
+ putkeys(mpage, cp, sz, NAME_SYN);
+}
+
+static int
+parse_mdoc_Fn(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+
+ if (n->child == NULL)
+ return 0;
+
+ parse_mdoc_fname(mpage, n->child);
+
+ for (n = n->child->next; n != NULL; n = n->next)
+ if (n->type == ROFFT_TEXT)
+ putkey(mpage, n->string, TYPE_Fa);
+
+ return 0;
+}
+
+static int
+parse_mdoc_Fo(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+
+ if (n->type != ROFFT_HEAD)
+ return 1;
+
+ if (n->child != NULL)
+ parse_mdoc_fname(mpage, n->child);
+
+ return 0;
+}
+
+static int
+parse_mdoc_Va(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+ char *cp;
+
+ if (n->type != ROFFT_ELEM && n->type != ROFFT_BODY)
+ return 0;
+
+ if (n->child != NULL &&
+ n->child->next == NULL &&
+ n->child->type == ROFFT_TEXT)
+ return 1;
+
+ cp = NULL;
+ deroff(&cp, n);
+ if (cp != NULL) {
+ putkey(mpage, cp, TYPE_Vt | (n->tok == MDOC_Va ||
+ n->type == ROFFT_BODY ? TYPE_Va : 0));
+ free(cp);
+ }
+
+ return 0;
+}
+
+static int
+parse_mdoc_Xr(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+ char *cp;
+
+ if (NULL == (n = n->child))
+ return 0;
+
+ if (NULL == n->next) {
+ putkey(mpage, n->string, TYPE_Xr);
+ return 0;
+ }
+
+ mandoc_asprintf(&cp, "%s(%s)", n->string, n->next->string);
+ putkey(mpage, cp, TYPE_Xr);
+ free(cp);
+ return 0;
+}
+
+static int
+parse_mdoc_Nd(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+
+ if (n->type == ROFFT_BODY)
+ deroff(&mpage->desc, n);
+ return 0;
+}
+
+static int
+parse_mdoc_Nm(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+
+ if (SEC_NAME == n->sec)
+ putmdockey(mpage, n->child, NAME_TITLE, 0);
+ else if (n->sec == SEC_SYNOPSIS && n->type == ROFFT_HEAD) {
+ if (n->child == NULL)
+ putkey(mpage, meta->name, NAME_SYN);
+ else
+ putmdockey(mpage, n->child, NAME_SYN, 0);
+ }
+ if ( ! (mpage->name_head_done ||
+ n->child == NULL || n->child->string == NULL ||
+ strcasecmp(n->child->string, meta->title))) {
+ putkey(mpage, n->child->string, NAME_HEAD);
+ mpage->name_head_done = 1;
+ }
+ return 0;
+}
+
+static int
+parse_mdoc_Sh(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+
+ return n->sec == SEC_CUSTOM && n->type == ROFFT_HEAD;
+}
+
+static int
+parse_mdoc_head(struct mpage *mpage, const struct roff_meta *meta,
+ const struct roff_node *n)
+{
+
+ return n->type == ROFFT_HEAD;
+}
+
+/*
+ * Add a string to the hash table for the current manual.
+ * Each string has a bitmask telling which macros it belongs to.
+ * When we finish the manual, we'll dump the table.
+ */
+static void
+putkeys(const struct mpage *mpage, char *cp, size_t sz, uint64_t v)
+{
+ struct ohash *htab;
+ struct str *s;
+ const char *end;
+ unsigned int slot;
+ int i, mustfree;
+
+ if (0 == sz)
+ return;
+
+ mustfree = render_string(&cp, &sz);
+
+ if (TYPE_Nm & v) {
+ htab = &names;
+ v &= name_mask;
+ if (v & NAME_FIRST)
+ name_mask &= ~NAME_FIRST;
+ if (debug > 1)
+ say(mpage->mlinks->file,
+ "Adding name %*s, bits=0x%llx", (int)sz, cp,
+ (unsigned long long)v);
+ } else {
+ htab = &strings;
+ if (debug > 1)
+ for (i = 0; i < KEY_MAX; i++)
+ if ((uint64_t)1 << i & v)
+ say(mpage->mlinks->file,
+ "Adding key %s=%*s",
+ mansearch_keynames[i], (int)sz, cp);
+ }
+
+ end = cp + sz;
+ slot = ohash_qlookupi(htab, cp, &end);
+ s = ohash_find(htab, slot);
+
+ if (NULL != s && mpage == s->mpage) {
+ s->mask |= v;
+ return;
+ } else if (NULL == s) {
+ s = mandoc_calloc(1, sizeof(struct str) + sz + 1);
+ memcpy(s->key, cp, sz);
+ ohash_insert(htab, slot, s);
+ }
+ s->mpage = mpage;
+ s->mask = v;
+
+ if (mustfree)
+ free(cp);
+}
+
+/*
+ * Take a Unicode codepoint and produce its UTF-8 encoding.
+ * This isn't the best way to do this, but it works.
+ * The magic numbers are from the UTF-8 packaging.
+ * They're not as scary as they seem: read the UTF-8 spec for details.
+ */
+static size_t
+utf8(unsigned int cp, char out[7])
+{
+ size_t rc;
+
+ rc = 0;
+ if (cp <= 0x0000007F) {
+ rc = 1;
+ out[0] = (char)cp;
+ } else if (cp <= 0x000007FF) {
+ rc = 2;
+ out[0] = (cp >> 6 & 31) | 192;
+ out[1] = (cp & 63) | 128;
+ } else if (cp <= 0x0000FFFF) {
+ rc = 3;
+ out[0] = (cp >> 12 & 15) | 224;
+ out[1] = (cp >> 6 & 63) | 128;
+ out[2] = (cp & 63) | 128;
+ } else if (cp <= 0x001FFFFF) {
+ rc = 4;
+ out[0] = (cp >> 18 & 7) | 240;
+ out[1] = (cp >> 12 & 63) | 128;
+ out[2] = (cp >> 6 & 63) | 128;
+ out[3] = (cp & 63) | 128;
+ } else if (cp <= 0x03FFFFFF) {
+ rc = 5;
+ out[0] = (cp >> 24 & 3) | 248;
+ out[1] = (cp >> 18 & 63) | 128;
+ out[2] = (cp >> 12 & 63) | 128;
+ out[3] = (cp >> 6 & 63) | 128;
+ out[4] = (cp & 63) | 128;
+ } else if (cp <= 0x7FFFFFFF) {
+ rc = 6;
+ out[0] = (cp >> 30 & 1) | 252;
+ out[1] = (cp >> 24 & 63) | 128;
+ out[2] = (cp >> 18 & 63) | 128;
+ out[3] = (cp >> 12 & 63) | 128;
+ out[4] = (cp >> 6 & 63) | 128;
+ out[5] = (cp & 63) | 128;
+ } else
+ return 0;
+
+ out[rc] = '\0';
+ return rc;
+}
+
+/*
+ * If the string contains escape sequences,
+ * replace it with an allocated rendering and return 1,
+ * such that the caller can free it after use.
+ * Otherwise, do nothing and return 0.
+ */
+static int
+render_string(char **public, size_t *psz)
+{
+ const char *src, *scp, *addcp, *seq;
+ char *dst;
+ size_t ssz, dsz, addsz;
+ char utfbuf[7], res[6];
+ int seqlen, unicode;
+
+ res[0] = '\\';
+ res[1] = '\t';
+ res[2] = ASCII_NBRSP;
+ res[3] = ASCII_HYPH;
+ res[4] = ASCII_BREAK;
+ res[5] = '\0';
+
+ src = scp = *public;
+ ssz = *psz;
+ dst = NULL;
+ dsz = 0;
+
+ while (scp < src + *psz) {
+
+ /* Leave normal characters unchanged. */
+
+ if (strchr(res, *scp) == NULL) {
+ if (dst != NULL)
+ dst[dsz++] = *scp;
+ scp++;
+ continue;
+ }
+
+ /*
+ * Found something that requires replacing,
+ * make sure we have a destination buffer.
+ */
+
+ if (dst == NULL) {
+ dst = mandoc_malloc(ssz + 1);
+ dsz = scp - src;
+ memcpy(dst, src, dsz);
+ }
+
+ /* Handle single-char special characters. */
+
+ switch (*scp) {
+ case '\\':
+ break;
+ case '\t':
+ case ASCII_NBRSP:
+ dst[dsz++] = ' ';
+ scp++;
+ continue;
+ case ASCII_HYPH:
+ dst[dsz++] = '-';
+ /* FALLTHROUGH */
+ case ASCII_BREAK:
+ scp++;
+ continue;
+ default:
+ abort();
+ }
+
+ /*
+ * Found an escape sequence.
+ * Read past the slash, then parse it.
+ * Ignore everything except characters.
+ */
+
+ scp++;
+ if (mandoc_escape(&scp, &seq, &seqlen) != ESCAPE_SPECIAL)
+ continue;
+
+ /*
+ * Render the special character
+ * as either UTF-8 or ASCII.
+ */
+
+ if (write_utf8) {
+ unicode = mchars_spec2cp(seq, seqlen);
+ if (unicode <= 0)
+ continue;
+ addsz = utf8(unicode, utfbuf);
+ if (addsz == 0)
+ continue;
+ addcp = utfbuf;
+ } else {
+ addcp = mchars_spec2str(seq, seqlen, &addsz);
+ if (addcp == NULL)
+ continue;
+ if (*addcp == ASCII_NBRSP) {
+ addcp = " ";
+ addsz = 1;
+ }
+ }
+
+ /* Copy the rendered glyph into the stream. */
+
+ ssz += addsz;
+ dst = mandoc_realloc(dst, ssz + 1);
+ memcpy(dst + dsz, addcp, addsz);
+ dsz += addsz;
+ }
+ if (dst != NULL) {
+ *public = dst;
+ *psz = dsz;
+ }
+
+ /* Trim trailing whitespace and NUL-terminate. */
+
+ while (*psz > 0 && (*public)[*psz - 1] == ' ')
+ --*psz;
+ if (dst != NULL) {
+ (*public)[*psz] = '\0';
+ return 1;
+ } else
+ return 0;
+}
+
+static void
+dbadd_mlink(const struct mlink *mlink)
+{
+ dba_page_alias(mlink->mpage->dba, mlink->name, NAME_FILE);
+ dba_page_add(mlink->mpage->dba, DBP_SECT, mlink->dsec);
+ dba_page_add(mlink->mpage->dba, DBP_SECT, mlink->fsec);
+ dba_page_add(mlink->mpage->dba, DBP_ARCH, mlink->arch);
+ dba_page_add(mlink->mpage->dba, DBP_FILE, mlink->file);
+}
+
+/*
+ * Flush the current page's terms (and their bits) into the database.
+ * Also, handle escape sequences at the last possible moment.
+ */
+static void
+dbadd(struct dba *dba, struct mpage *mpage)
+{
+ struct mlink *mlink;
+ struct str *key;
+ char *cp;
+ uint64_t mask;
+ size_t i;
+ unsigned int slot;
+ int mustfree;
+
+ mlink = mpage->mlinks;
+
+ if (nodb) {
+ for (key = ohash_first(&names, &slot); NULL != key;
+ key = ohash_next(&names, &slot))
+ free(key);
+ for (key = ohash_first(&strings, &slot); NULL != key;
+ key = ohash_next(&strings, &slot))
+ free(key);
+ if (0 == debug)
+ return;
+ while (NULL != mlink) {
+ fputs(mlink->name, stdout);
+ if (NULL == mlink->next ||
+ strcmp(mlink->dsec, mlink->next->dsec) ||
+ strcmp(mlink->fsec, mlink->next->fsec) ||
+ strcmp(mlink->arch, mlink->next->arch)) {
+ putchar('(');
+ if ('\0' == *mlink->dsec)
+ fputs(mlink->fsec, stdout);
+ else
+ fputs(mlink->dsec, stdout);
+ if ('\0' != *mlink->arch)
+ printf("/%s", mlink->arch);
+ putchar(')');
+ }
+ mlink = mlink->next;
+ if (NULL != mlink)
+ fputs(", ", stdout);
+ }
+ printf(" - %s\n", mpage->desc);
+ return;
+ }
+
+ if (debug)
+ say(mlink->file, "Adding to database");
+
+ cp = mpage->desc;
+ i = strlen(cp);
+ mustfree = render_string(&cp, &i);
+ mpage->dba = dba_page_new(dba->pages,
+ *mpage->arch == '\0' ? mlink->arch : mpage->arch,
+ cp, mlink->file, mpage->form);
+ if (mustfree)
+ free(cp);
+ dba_page_add(mpage->dba, DBP_SECT, mpage->sec);
+
+ while (mlink != NULL) {
+ dbadd_mlink(mlink);
+ mlink = mlink->next;
+ }
+
+ for (key = ohash_first(&names, &slot); NULL != key;
+ key = ohash_next(&names, &slot)) {
+ assert(key->mpage == mpage);
+ dba_page_alias(mpage->dba, key->key, key->mask);
+ free(key);
+ }
+ for (key = ohash_first(&strings, &slot); NULL != key;
+ key = ohash_next(&strings, &slot)) {
+ assert(key->mpage == mpage);
+ i = 0;
+ for (mask = TYPE_Xr; mask <= TYPE_Lb; mask *= 2) {
+ if (key->mask & mask)
+ dba_macro_add(dba->macros, i,
+ key->key, mpage->dba);
+ i++;
+ }
+ free(key);
+ }
+}
+
+static void
+dbprune(struct dba *dba)
+{
+ struct dba_array *page, *files;
+ char *file;
+
+ dba_array_FOREACH(dba->pages, page) {
+ files = dba_array_get(page, DBP_FILE);
+ dba_array_FOREACH(files, file) {
+ if (*file < ' ')
+ file++;
+ if (ohash_find(&mlinks, ohash_qlookup(&mlinks,
+ file)) != NULL) {
+ if (debug)
+ say(file, "Deleting from database");
+ dba_array_del(dba->pages);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Write the database from memory to disk.
+ */
+static void
+dbwrite(struct dba *dba)
+{
+ char tfn[32];
+ int status;
+ pid_t child;
+
+ if (dba_write(MANDOC_DB "~", dba) != -1) {
+ if (rename(MANDOC_DB "~", MANDOC_DB) == -1) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say(MANDOC_DB, "&rename");
+ unlink(MANDOC_DB "~");
+ }
+ return;
+ }
+
+ (void)strlcpy(tfn, "/tmp/mandocdb.XXXXXXXX", sizeof(tfn));
+ if (mkdtemp(tfn) == NULL) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "&%s", tfn);
+ return;
+ }
+
+ (void)strlcat(tfn, "/" MANDOC_DB, sizeof(tfn));
+ if (dba_write(tfn, dba) == -1) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say(tfn, "&dba_write");
+ goto out;
+ }
+
+ switch (child = fork()) {
+ case -1:
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "&fork cmp");
+ return;
+ case 0:
+ execlp("cmp", "cmp", "-s", tfn, MANDOC_DB, (char *)NULL);
+ say("", "&exec cmp");
+ exit(0);
+ default:
+ break;
+ }
+ if (waitpid(child, &status, 0) == -1) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "&wait cmp");
+ } else if (WIFSIGNALED(status)) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "cmp died from signal %d", WTERMSIG(status));
+ } else if (WEXITSTATUS(status)) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say(MANDOC_DB,
+ "Data changed, but cannot replace database");
+ }
+
+out:
+ *strrchr(tfn, '/') = '\0';
+ switch (child = fork()) {
+ case -1:
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "&fork rm");
+ return;
+ case 0:
+ execlp("rm", "rm", "-rf", tfn, (char *)NULL);
+ say("", "&exec rm");
+ exit((int)MANDOCLEVEL_SYSERR);
+ default:
+ break;
+ }
+ if (waitpid(child, &status, 0) == -1) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "&wait rm");
+ } else if (WIFSIGNALED(status) || WEXITSTATUS(status)) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "%s: Cannot remove temporary directory", tfn);
+ }
+}
+
+static int
+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.
+ * This will be needed if the second or a later directory
+ * on the command line is given as a relative path.
+ * 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))) {
+ getcwd_status = 2;
+ (void)strlcpy(startdir, strerror(errno),
+ sizeof(startdir));
+ } else
+ getcwd_status = 1;
+ }
+
+ /*
+ * We are leaving the old base directory.
+ * Do not use it any longer, not even for messages.
+ */
+ *basedir = '\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) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "getcwd: %s", startdir);
+ return 0;
+ }
+ if (-1 == chdir(startdir)) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "&chdir %s", startdir);
+ return 0;
+ }
+ }
+
+ /*
+ * Always resolve basedir to the canonicalized absolute
+ * pathname and append a trailing slash, such that
+ * we can reliably check whether files are inside.
+ */
+ if (NULL == realpath(targetdir, basedir)) {
+ if (report_baddir || errno != ENOENT) {
+ exitcode = (int)MANDOCLEVEL_BADARG;
+ say("", "&%s: realpath", targetdir);
+ }
+ return 0;
+ } else if (-1 == chdir(basedir)) {
+ if (report_baddir || errno != ENOENT) {
+ exitcode = (int)MANDOCLEVEL_BADARG;
+ say("", "&chdir");
+ }
+ return 0;
+ }
+ chdir_status = 1;
+ cp = strchr(basedir, '\0');
+ if ('/' != cp[-1]) {
+ if (cp - basedir >= PATH_MAX - 1) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "Filename too long");
+ return 0;
+ }
+ *cp++ = '/';
+ *cp = '\0';
+ }
+ return 1;
+}
+
+static void
+say(const char *file, const char *format, ...)
+{
+ va_list ap;
+ int use_errno;
+
+ if ('\0' != *basedir)
+ fprintf(stderr, "%s", basedir);
+ if ('\0' != *basedir && '\0' != *file)
+ fputc('/', stderr);
+ if ('\0' != *file)
+ fprintf(stderr, "%s", file);
+
+ use_errno = 1;
+ if (NULL != format) {
+ switch (*format) {
+ case '&':
+ format++;
+ break;
+ case '\0':
+ format = NULL;
+ break;
+ default:
+ use_errno = 0;
+ break;
+ }
+ }
+ if (NULL != format) {
+ if ('\0' != *basedir || '\0' != *file)
+ fputs(": ", stderr);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ }
+ if (use_errno) {
+ if ('\0' != *basedir || '\0' != *file || NULL != format)
+ fputs(": ", stderr);
+ perror(NULL);
+ } else
+ fputc('\n', stderr);
+}
diff --git a/usr/src/cmd/mandoc/manpath.c b/usr/src/cmd/mandoc/manpath.c
index 83c329ec9b..f43ace6069 100644
--- a/usr/src/cmd/mandoc/manpath.c
+++ b/usr/src/cmd/mandoc/manpath.c
@@ -1,6 +1,6 @@
-/* $Id: manpath.c,v 1.30 2016/05/28 13:44:13 schwarze Exp $ */
+/* $Id: manpath.c,v 1.33 2017/02/10 15:45:28 schwarze Exp $ */
/*
- * Copyright (c) 2011, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -32,9 +32,7 @@
#include "mandoc_aux.h"
#include "manconf.h"
-#if !HAVE_MANPATH
static void manconf_file(struct manconf *, const char *);
-#endif
static void manpath_add(struct manpaths *, const char *, int);
static void manpath_parseline(struct manpaths *, char *, int);
@@ -43,52 +41,6 @@ void
manconf_parse(struct manconf *conf, const char *file,
char *defp, char *auxp)
{
-#if HAVE_MANPATH
- char cmd[(PATH_MAX * 3) + 20];
- FILE *stream;
- char *buf;
- size_t sz, bsz;
-
- strlcpy(cmd, "manpath", sizeof(cmd));
- if (file) {
- strlcat(cmd, " -C ", sizeof(cmd));
- strlcat(cmd, file, sizeof(cmd));
- }
- if (auxp) {
- strlcat(cmd, " -m ", sizeof(cmd));
- strlcat(cmd, auxp, sizeof(cmd));
- }
- if (defp) {
- strlcat(cmd, " -M ", sizeof(cmd));
- strlcat(cmd, defp, sizeof(cmd));
- }
-
- /* Open manpath(1). Ignore errors. */
-
- stream = popen(cmd, "r");
- if (NULL == stream)
- return;
-
- buf = NULL;
- bsz = 0;
-
- /* Read in as much output as we can. */
-
- do {
- buf = mandoc_realloc(buf, bsz + 1024);
- sz = fread(buf + bsz, 1, 1024, stream);
- bsz += sz;
- } while (sz > 0);
-
- if ( ! ferror(stream) && feof(stream) &&
- bsz && '\n' == buf[bsz - 1]) {
- buf[bsz - 1] = '\0';
- manpath_parseline(&conf->manpath, buf, 1);
- }
-
- free(buf);
- pclose(stream);
-#else
char *insert;
/* Always prepend -m. */
@@ -137,7 +89,6 @@ manconf_parse(struct manconf *conf, const char *file,
/* MANPATH overrides man.conf(5) completely. */
manpath_parseline(&conf->manpath, defp, 0);
-#endif
}
/*
@@ -204,7 +155,6 @@ manconf_free(struct manconf *conf)
free(conf->output.style);
}
-#if !HAVE_MANPATH
static void
manconf_file(struct manconf *conf, const char *file)
{
@@ -257,7 +207,7 @@ manconf_file(struct manconf *conf, const char *file)
*manpath_default = '\0';
break;
case 1: /* output */
- manconf_output(&conf->output, cp);
+ manconf_output(&conf->output, cp, 1);
break;
default:
break;
@@ -270,17 +220,18 @@ out:
if (*manpath_default != '\0')
manpath_parseline(&conf->manpath, manpath_default, 0);
}
-#endif
-void
-manconf_output(struct manoutput *conf, const char *cp)
+int
+manconf_output(struct manoutput *conf, const char *cp, int fromfile)
{
const char *const toks[] = {
"includes", "man", "paper", "style",
- "indent", "width", "fragment", "mdoc"
+ "indent", "width", "fragment", "mdoc", "noval"
};
- size_t len, tok;
+ const char *errstr;
+ char *oldval;
+ size_t len, tok;
for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
len = strlen(toks[tok]);
@@ -295,41 +246,81 @@ manconf_output(struct manoutput *conf, const char *cp)
}
}
- if (tok < 6 && *cp == '\0')
- return;
+ if (tok < 6 && *cp == '\0') {
+ warnx("-O %s=?: Missing argument value", toks[tok]);
+ return -1;
+ }
+ if ((tok == 6 || tok == 7) && *cp != '\0') {
+ warnx("-O %s: Does not take a value: %s", toks[tok], cp);
+ return -1;
+ }
switch (tok) {
case 0:
- if (conf->includes == NULL)
- conf->includes = mandoc_strdup(cp);
- break;
+ if (conf->includes != NULL) {
+ oldval = mandoc_strdup(conf->includes);
+ break;
+ }
+ conf->includes = mandoc_strdup(cp);
+ return 0;
case 1:
- if (conf->man == NULL)
- conf->man = mandoc_strdup(cp);
- break;
+ if (conf->man != NULL) {
+ oldval = mandoc_strdup(conf->man);
+ break;
+ }
+ conf->man = mandoc_strdup(cp);
+ return 0;
case 2:
- if (conf->paper == NULL)
- conf->paper = mandoc_strdup(cp);
- break;
+ if (conf->paper != NULL) {
+ oldval = mandoc_strdup(conf->paper);
+ break;
+ }
+ conf->paper = mandoc_strdup(cp);
+ return 0;
case 3:
- if (conf->style == NULL)
- conf->style = mandoc_strdup(cp);
- break;
+ if (conf->style != NULL) {
+ oldval = mandoc_strdup(conf->style);
+ break;
+ }
+ conf->style = mandoc_strdup(cp);
+ return 0;
case 4:
- if (conf->indent == 0)
- conf->indent = strtonum(cp, 0, 1000, NULL);
- break;
+ if (conf->indent) {
+ mandoc_asprintf(&oldval, "%zu", conf->indent);
+ break;
+ }
+ conf->indent = strtonum(cp, 0, 1000, &errstr);
+ if (errstr == NULL)
+ return 0;
+ warnx("-O indent=%s is %s", cp, errstr);
+ return -1;
case 5:
- if (conf->width == 0)
- conf->width = strtonum(cp, 58, 1000, NULL);
- break;
+ if (conf->width) {
+ mandoc_asprintf(&oldval, "%zu", conf->width);
+ break;
+ }
+ conf->width = strtonum(cp, 58, 1000, &errstr);
+ if (errstr == NULL)
+ return 0;
+ warnx("-O width=%s is %s", cp, errstr);
+ return -1;
case 6:
conf->fragment = 1;
- break;
+ return 0;
case 7:
conf->mdoc = 1;
- break;
+ return 0;
+ case 8:
+ conf->noval = 1;
+ return 0;
default:
- break;
+ if (fromfile)
+ warnx("-O %s: Bad argument", cp);
+ 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
new file mode 100644
index 0000000000..6e689bd358
--- /dev/null
+++ b/usr/src/cmd/mandoc/mansearch.c
@@ -0,0 +1,751 @@
+/* $OpenBSD: mansearch.c,v 1.50 2016/07/09 15:23:36 schwarze Exp $ */
+/*
+ * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2013, 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "config.h"
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#if HAVE_ERR
+#include <err.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mandoc.h"
+#include "mandoc_aux.h"
+#include "mandoc_ohash.h"
+#include "manconf.h"
+#include "mansearch.h"
+#include "dbm.h"
+
+struct expr {
+ /* Used for terms: */
+ struct dbm_match match; /* Match type and expression. */
+ uint64_t bits; /* Type mask. */
+ /* Used for OR and AND groups: */
+ struct expr *next; /* Next child in the parent group. */
+ struct expr *child; /* First child in this group. */
+ enum { EXPR_TERM, EXPR_OR, EXPR_AND } type;
+};
+
+const char *const mansearch_keynames[KEY_MAX] = {
+ "arch", "sec", "Xr", "Ar", "Fa", "Fl", "Dv", "Fn",
+ "Ic", "Pa", "Cm", "Li", "Em", "Cd", "Va", "Ft",
+ "Tn", "Er", "Ev", "Sy", "Sh", "In", "Ss", "Ox",
+ "An", "Mt", "St", "Bx", "At", "Nx", "Fx", "Lk",
+ "Ms", "Bsx", "Dx", "Rs", "Vt", "Lb", "Nm", "Nd"
+};
+
+
+static struct ohash *manmerge(struct expr *, struct ohash *);
+static struct ohash *manmerge_term(struct expr *, struct ohash *);
+static struct ohash *manmerge_or(struct expr *, struct ohash *);
+static struct ohash *manmerge_and(struct expr *, struct ohash *);
+static char *buildnames(const struct dbm_page *);
+static char *buildoutput(size_t, int32_t);
+static size_t lstlen(const char *);
+static void lstcat(char *, size_t *, const char *);
+static int lstmatch(const char *, const char *);
+static struct expr *exprcomp(const struct mansearch *,
+ int, char *[], int *);
+static struct expr *expr_and(const struct mansearch *,
+ int, char *[], int *);
+static struct expr *exprterm(const struct mansearch *,
+ int, char *[], int *);
+static void exprfree(struct expr *);
+static int manpage_compare(const void *, const void *);
+
+
+int
+mansearch(const struct mansearch *search,
+ const struct manpaths *paths,
+ int argc, char *argv[],
+ struct manpage **res, size_t *sz)
+{
+ char buf[PATH_MAX];
+ struct dbm_res *rp;
+ struct expr *e;
+ struct dbm_page *page;
+ struct manpage *mpage;
+ struct ohash *htab;
+ size_t cur, i, maxres, outkey;
+ unsigned int slot;
+ int argi, chdir_status, getcwd_status, im;
+
+ argi = 0;
+ if ((e = exprcomp(search, argc, argv, &argi)) == NULL) {
+ *sz = 0;
+ return 0;
+ }
+
+ cur = maxres = 0;
+ *res = NULL;
+
+ outkey = KEY_Nd;
+ if (search->outkey != NULL)
+ for (im = 0; im < KEY_MAX; im++)
+ if (0 == strcasecmp(search->outkey,
+ mansearch_keynames[im])) {
+ outkey = im;
+ break;
+ }
+
+ /*
+ * Remember the original working directory, if possible.
+ * This will be needed if the second or a later directory
+ * is given as a relative path.
+ * Do not error out if the current directory is not
+ * searchable: Maybe it won't be needed after all.
+ */
+
+ if (getcwd(buf, PATH_MAX) == NULL) {
+ getcwd_status = 0;
+ (void)strlcpy(buf, strerror(errno), sizeof(buf));
+ } else
+ getcwd_status = 1;
+
+ /*
+ * Loop over the directories (containing databases) for us to
+ * search.
+ * Don't let missing/bad databases/directories phase us.
+ * In each, try to open the resident database and, if it opens,
+ * scan it for our match expression.
+ */
+
+ chdir_status = 0;
+ for (i = 0; i < paths->sz; i++) {
+ if (chdir_status && paths->paths[i][0] != '/') {
+ if ( ! getcwd_status) {
+ warnx("%s: getcwd: %s", paths->paths[i], buf);
+ continue;
+ } else if (chdir(buf) == -1) {
+ warn("%s", buf);
+ continue;
+ }
+ }
+ if (chdir(paths->paths[i]) == -1) {
+ warn("%s", paths->paths[i]);
+ continue;
+ }
+ chdir_status = 1;
+
+ if (dbm_open(MANDOC_DB) == -1) {
+ warn("%s/%s", paths->paths[i], MANDOC_DB);
+ continue;
+ }
+
+ if ((htab = manmerge(e, NULL)) == NULL) {
+ dbm_close();
+ continue;
+ }
+
+ for (rp = ohash_first(htab, &slot); rp != NULL;
+ rp = ohash_next(htab, &slot)) {
+ page = dbm_page_get(rp->page);
+
+ if (lstmatch(search->sec, page->sect) == 0 ||
+ lstmatch(search->arch, page->arch) == 0)
+ continue;
+
+ if (cur + 1 > maxres) {
+ maxres += 1024;
+ *res = mandoc_reallocarray(*res,
+ maxres, sizeof(**res));
+ }
+ mpage = *res + cur;
+ mandoc_asprintf(&mpage->file, "%s/%s",
+ paths->paths[i], page->file + 1);
+ mpage->names = buildnames(page);
+ mpage->output = (int)outkey == KEY_Nd ?
+ mandoc_strdup(page->desc) :
+ buildoutput(outkey, page->addr);
+ mpage->ipath = i;
+ mpage->bits = rp->bits;
+ mpage->sec = *page->sect - '0';
+ if (mpage->sec < 0 || mpage->sec > 9)
+ mpage->sec = 10;
+ mpage->form = *page->file;
+ free(rp);
+ cur++;
+ }
+ ohash_delete(htab);
+ free(htab);
+ dbm_close();
+
+ /*
+ * In man(1) mode, prefer matches in earlier trees
+ * over matches in later trees.
+ */
+
+ if (cur && search->firstmatch)
+ break;
+ }
+ qsort(*res, cur, sizeof(struct manpage), manpage_compare);
+ if (chdir_status && getcwd_status && chdir(buf) == -1)
+ warn("%s", buf);
+ exprfree(e);
+ *sz = cur;
+ return 1;
+}
+
+/*
+ * Merge the results for the expression tree rooted at e
+ * into the the result list htab.
+ */
+static struct ohash *
+manmerge(struct expr *e, struct ohash *htab)
+{
+ switch (e->type) {
+ case EXPR_TERM:
+ return manmerge_term(e, htab);
+ case EXPR_OR:
+ return manmerge_or(e->child, htab);
+ case EXPR_AND:
+ return manmerge_and(e->child, htab);
+ default:
+ abort();
+ }
+}
+
+static struct ohash *
+manmerge_term(struct expr *e, struct ohash *htab)
+{
+ struct dbm_res res, *rp;
+ uint64_t ib;
+ unsigned int slot;
+ int im;
+
+ if (htab == NULL) {
+ htab = mandoc_malloc(sizeof(*htab));
+ mandoc_ohash_init(htab, 4, offsetof(struct dbm_res, page));
+ }
+
+ for (im = 0, ib = 1; im < KEY_MAX; im++, ib <<= 1) {
+ if ((e->bits & ib) == 0)
+ continue;
+
+ switch (ib) {
+ case TYPE_arch:
+ dbm_page_byarch(&e->match);
+ break;
+ case TYPE_sec:
+ dbm_page_bysect(&e->match);
+ break;
+ case TYPE_Nm:
+ dbm_page_byname(&e->match);
+ break;
+ case TYPE_Nd:
+ dbm_page_bydesc(&e->match);
+ break;
+ default:
+ dbm_page_bymacro(im - 2, &e->match);
+ break;
+ }
+
+ /*
+ * When hashing for deduplication, use the unique
+ * page ID itself instead of a hash function;
+ * that is quite efficient.
+ */
+
+ for (;;) {
+ res = dbm_page_next();
+ if (res.page == -1)
+ break;
+ slot = ohash_lookup_memory(htab,
+ (char *)&res, sizeof(res.page), res.page);
+ if ((rp = ohash_find(htab, slot)) != NULL) {
+ rp->bits |= res.bits;
+ continue;
+ }
+ rp = mandoc_malloc(sizeof(*rp));
+ *rp = res;
+ ohash_insert(htab, slot, rp);
+ }
+ }
+ return htab;
+}
+
+static struct ohash *
+manmerge_or(struct expr *e, struct ohash *htab)
+{
+ while (e != NULL) {
+ htab = manmerge(e, htab);
+ e = e->next;
+ }
+ return htab;
+}
+
+static struct ohash *
+manmerge_and(struct expr *e, struct ohash *htab)
+{
+ struct ohash *hand, *h1, *h2;
+ struct dbm_res *res;
+ unsigned int slot1, slot2;
+
+ /* Evaluate the first term of the AND clause. */
+
+ hand = manmerge(e, NULL);
+
+ while ((e = e->next) != NULL) {
+
+ /* Evaluate the next term and prepare for ANDing. */
+
+ h2 = manmerge(e, NULL);
+ if (ohash_entries(h2) < ohash_entries(hand)) {
+ h1 = h2;
+ h2 = hand;
+ } else
+ h1 = hand;
+ hand = mandoc_malloc(sizeof(*hand));
+ mandoc_ohash_init(hand, 4, offsetof(struct dbm_res, page));
+
+ /* Keep all pages that are in both result sets. */
+
+ for (res = ohash_first(h1, &slot1); res != NULL;
+ res = ohash_next(h1, &slot1)) {
+ if (ohash_find(h2, ohash_lookup_memory(h2,
+ (char *)res, sizeof(res->page),
+ res->page)) == NULL)
+ free(res);
+ else
+ ohash_insert(hand, ohash_lookup_memory(hand,
+ (char *)res, sizeof(res->page),
+ res->page), res);
+ }
+
+ /* Discard the merged results. */
+
+ for (res = ohash_first(h2, &slot2); res != NULL;
+ res = ohash_next(h2, &slot2))
+ free(res);
+ ohash_delete(h2);
+ free(h2);
+ ohash_delete(h1);
+ free(h1);
+ }
+
+ /* Merge the result of the AND into htab. */
+
+ if (htab == NULL)
+ return hand;
+
+ for (res = ohash_first(hand, &slot1); res != NULL;
+ res = ohash_next(hand, &slot1)) {
+ slot2 = ohash_lookup_memory(htab,
+ (char *)res, sizeof(res->page), res->page);
+ if (ohash_find(htab, slot2) == NULL)
+ ohash_insert(htab, slot2, res);
+ else
+ free(res);
+ }
+
+ /* Discard the merged result. */
+
+ ohash_delete(hand);
+ free(hand);
+ return htab;
+}
+
+void
+mansearch_free(struct manpage *res, size_t sz)
+{
+ size_t i;
+
+ for (i = 0; i < sz; i++) {
+ free(res[i].file);
+ free(res[i].names);
+ free(res[i].output);
+ }
+ free(res);
+}
+
+static int
+manpage_compare(const void *vp1, const void *vp2)
+{
+ const struct manpage *mp1, *mp2;
+ int diff;
+
+ mp1 = vp1;
+ mp2 = vp2;
+ return (diff = mp2->bits - mp1->bits) ? diff :
+ (diff = mp1->sec - mp2->sec) ? diff :
+ strcasecmp(mp1->names, mp2->names);
+}
+
+static char *
+buildnames(const struct dbm_page *page)
+{
+ char *buf;
+ size_t i, sz;
+
+ sz = lstlen(page->name) + 1 + lstlen(page->sect) +
+ (page->arch == NULL ? 0 : 1 + lstlen(page->arch)) + 2;
+ buf = mandoc_malloc(sz);
+ i = 0;
+ lstcat(buf, &i, page->name);
+ buf[i++] = '(';
+ lstcat(buf, &i, page->sect);
+ if (page->arch != NULL) {
+ buf[i++] = '/';
+ lstcat(buf, &i, page->arch);
+ }
+ buf[i++] = ')';
+ buf[i++] = '\0';
+ assert(i == sz);
+ return buf;
+}
+
+/*
+ * Count the buffer space needed to print the NUL-terminated
+ * list of NUL-terminated strings, when printing two separator
+ * characters between strings.
+ */
+static size_t
+lstlen(const char *cp)
+{
+ size_t sz;
+
+ for (sz = 0;; sz++) {
+ if (cp[0] == '\0') {
+ if (cp[1] == '\0')
+ break;
+ sz++;
+ } else if (cp[0] < ' ')
+ sz--;
+ cp++;
+ }
+ return sz;
+}
+
+/*
+ * Print the NUL-terminated list of NUL-terminated strings
+ * into the buffer, seperating strings with a comma and a blank.
+ */
+static void
+lstcat(char *buf, size_t *i, const char *cp)
+{
+ for (;;) {
+ if (cp[0] == '\0') {
+ if (cp[1] == '\0')
+ break;
+ buf[(*i)++] = ',';
+ buf[(*i)++] = ' ';
+ } else if (cp[0] >= ' ')
+ buf[(*i)++] = cp[0];
+ cp++;
+ }
+}
+
+/*
+ * Return 1 if the string *want occurs in any of the strings
+ * in the NUL-terminated string list *have, or 0 otherwise.
+ * If either argument is NULL or empty, assume no filtering
+ * is desired and return 1.
+ */
+static int
+lstmatch(const char *want, const char *have)
+{
+ if (want == NULL || have == NULL || *have == '\0')
+ return 1;
+ while (*have != '\0') {
+ if (strcasestr(have, want) != NULL)
+ return 1;
+ have = strchr(have, '\0') + 1;
+ }
+ return 0;
+}
+
+/*
+ * Build a list of values taken by the macro im
+ * in the manual page with big-endian address addr.
+ */
+static char *
+buildoutput(size_t im, int32_t addr)
+{
+ const char *oldoutput, *sep;
+ char *output, *newoutput, *value;
+
+ output = NULL;
+ dbm_macro_bypage(im - 2, addr);
+ while ((value = dbm_macro_next()) != NULL) {
+ if (output == NULL) {
+ oldoutput = "";
+ sep = "";
+ } else {
+ oldoutput = output;
+ sep = " # ";
+ }
+ mandoc_asprintf(&newoutput, "%s%s%s", oldoutput, sep, value);
+ free(output);
+ output = newoutput;
+ }
+ return output;
+}
+
+/*
+ * Compile a set of string tokens into an expression.
+ * Tokens in "argv" are assumed to be individual expression atoms (e.g.,
+ * "(", "foo=bar", etc.).
+ */
+static struct expr *
+exprcomp(const struct mansearch *search, int argc, char *argv[], int *argi)
+{
+ struct expr *parent, *child;
+ int needterm, nested;
+
+ if ((nested = *argi) == argc)
+ return NULL;
+ needterm = 1;
+ parent = child = NULL;
+ while (*argi < argc) {
+ if (strcmp(")", argv[*argi]) == 0) {
+ if (needterm)
+ warnx("missing term "
+ "before closing parenthesis");
+ needterm = 0;
+ if (nested)
+ break;
+ warnx("ignoring unmatched right parenthesis");
+ ++*argi;
+ continue;
+ }
+ if (strcmp("-o", argv[*argi]) == 0) {
+ if (needterm) {
+ if (*argi > 0)
+ warnx("ignoring -o after %s",
+ argv[*argi - 1]);
+ else
+ warnx("ignoring initial -o");
+ }
+ needterm = 1;
+ ++*argi;
+ continue;
+ }
+ needterm = 0;
+ if (child == NULL) {
+ child = expr_and(search, argc, argv, argi);
+ continue;
+ }
+ if (parent == NULL) {
+ parent = mandoc_calloc(1, sizeof(*parent));
+ parent->type = EXPR_OR;
+ parent->next = NULL;
+ parent->child = child;
+ }
+ child->next = expr_and(search, argc, argv, argi);
+ child = child->next;
+ }
+ if (needterm && *argi)
+ warnx("ignoring trailing %s", argv[*argi - 1]);
+ return parent == NULL ? child : parent;
+}
+
+static struct expr *
+expr_and(const struct mansearch *search, int argc, char *argv[], int *argi)
+{
+ struct expr *parent, *child;
+ int needterm;
+
+ needterm = 1;
+ parent = child = NULL;
+ while (*argi < argc) {
+ if (strcmp(")", argv[*argi]) == 0) {
+ if (needterm)
+ warnx("missing term "
+ "before closing parenthesis");
+ needterm = 0;
+ break;
+ }
+ if (strcmp("-o", argv[*argi]) == 0)
+ break;
+ if (strcmp("-a", argv[*argi]) == 0) {
+ if (needterm) {
+ if (*argi > 0)
+ warnx("ignoring -a after %s",
+ argv[*argi - 1]);
+ else
+ warnx("ignoring initial -a");
+ }
+ needterm = 1;
+ ++*argi;
+ continue;
+ }
+ if (needterm == 0)
+ break;
+ if (child == NULL) {
+ child = exprterm(search, argc, argv, argi);
+ if (child != NULL)
+ needterm = 0;
+ continue;
+ }
+ needterm = 0;
+ if (parent == NULL) {
+ parent = mandoc_calloc(1, sizeof(*parent));
+ parent->type = EXPR_AND;
+ parent->next = NULL;
+ parent->child = child;
+ }
+ child->next = exprterm(search, argc, argv, argi);
+ if (child->next != NULL) {
+ child = child->next;
+ needterm = 0;
+ }
+ }
+ if (needterm && *argi)
+ warnx("ignoring trailing %s", argv[*argi - 1]);
+ return parent == NULL ? child : parent;
+}
+
+static struct expr *
+exprterm(const struct mansearch *search, int argc, char *argv[], int *argi)
+{
+ char errbuf[BUFSIZ];
+ struct expr *e;
+ char *key, *val;
+ uint64_t iterbit;
+ int cs, i, irc;
+
+ if (strcmp("(", argv[*argi]) == 0) {
+ ++*argi;
+ e = exprcomp(search, argc, argv, argi);
+ if (*argi < argc) {
+ assert(strcmp(")", argv[*argi]) == 0);
+ ++*argi;
+ } else
+ warnx("unclosed parenthesis");
+ return e;
+ }
+
+ e = mandoc_calloc(1, sizeof(*e));
+ e->type = EXPR_TERM;
+ e->bits = 0;
+ e->next = NULL;
+ e->child = NULL;
+
+ if (search->argmode == ARG_NAME) {
+ e->bits = TYPE_Nm;
+ e->match.type = DBM_EXACT;
+ e->match.str = argv[(*argi)++];
+ return e;
+ }
+
+ /*
+ * Separate macro keys from search string.
+ * If needed, request regular expression handling.
+ */
+
+ if (search->argmode == ARG_WORD) {
+ e->bits = TYPE_Nm;
+ e->match.type = DBM_REGEX;
+#if HAVE_REWB_BSD
+ mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", argv[*argi]);
+#elif HAVE_REWB_SYSV
+ mandoc_asprintf(&val, "\\<%s\\>", argv[*argi]);
+#else
+ mandoc_asprintf(&val,
+ "(^|[^a-zA-Z01-9_])%s([^a-zA-Z01-9_]|$)", argv[*argi]);
+#endif
+ cs = 0;
+ } else if ((val = strpbrk(argv[*argi], "=~")) == NULL) {
+ e->bits = TYPE_Nm | TYPE_Nd;
+ e->match.type = DBM_SUB;
+ e->match.str = argv[*argi];
+ } else {
+ if (val == argv[*argi])
+ e->bits = TYPE_Nm | TYPE_Nd;
+ if (*val == '=') {
+ e->match.type = DBM_SUB;
+ e->match.str = val + 1;
+ } else
+ e->match.type = DBM_REGEX;
+ *val++ = '\0';
+ if (strstr(argv[*argi], "arch") != NULL)
+ cs = 0;
+ }
+
+ /* Compile regular expressions. */
+
+ if (e->match.type == DBM_REGEX) {
+ e->match.re = mandoc_malloc(sizeof(*e->match.re));
+ irc = regcomp(e->match.re, val,
+ REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE));
+ if (irc) {
+ regerror(irc, e->match.re, errbuf, sizeof(errbuf));
+ warnx("regcomp /%s/: %s", val, errbuf);
+ }
+ if (search->argmode == ARG_WORD)
+ free(val);
+ if (irc) {
+ free(e->match.re);
+ free(e);
+ ++*argi;
+ return NULL;
+ }
+ }
+
+ if (e->bits) {
+ ++*argi;
+ return e;
+ }
+
+ /*
+ * Parse out all possible fields.
+ * If the field doesn't resolve, bail.
+ */
+
+ while (NULL != (key = strsep(&argv[*argi], ","))) {
+ if ('\0' == *key)
+ continue;
+ for (i = 0, iterbit = 1; i < KEY_MAX; i++, iterbit <<= 1) {
+ if (0 == strcasecmp(key, mansearch_keynames[i])) {
+ e->bits |= iterbit;
+ break;
+ }
+ }
+ if (i == KEY_MAX) {
+ if (strcasecmp(key, "any"))
+ warnx("treating unknown key "
+ "\"%s\" as \"any\"", key);
+ e->bits |= ~0ULL;
+ }
+ }
+
+ ++*argi;
+ return e;
+}
+
+static void
+exprfree(struct expr *e)
+{
+ if (e->next != NULL)
+ exprfree(e->next);
+ if (e->child != NULL)
+ exprfree(e->child);
+ free(e);
+}
diff --git a/usr/src/cmd/mandoc/mansearch.h b/usr/src/cmd/mandoc/mansearch.h
index 7f68ff6767..892c6e1e8a 100644
--- a/usr/src/cmd/mandoc/mansearch.h
+++ b/usr/src/cmd/mandoc/mansearch.h
@@ -1,7 +1,7 @@
-/* $Id: mansearch.h,v 1.24 2015/11/07 14:01:16 schwarze Exp $ */
+/* $Id: mansearch.h,v 1.27 2016/08/01 12:31:00 schwarze Exp $ */
/*
* Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2013, 2014, 2016 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,6 +17,12 @@
*/
#define MANDOC_DB "mandoc.db"
+#define MANDOCDB_MAGIC 0x3a7d0cdb
+#define MANDOCDB_VERSION 1
+
+#define MACRO_MAX 36
+#define KEY_Nd 39
+#define KEY_MAX 40
#define TYPE_arch 0x0000000000000001ULL
#define TYPE_sec 0x0000000000000002ULL
@@ -66,9 +72,11 @@
#define NAME_FILE 0x0000004000000010ULL
#define NAME_MASK 0x000000000000001fULL
-#define FORM_CAT 0 /* manual page is preformatted */
-#define FORM_SRC 1 /* format is mdoc(7) or man(7) */
-#define FORM_NONE 4 /* format is unknown */
+enum form {
+ FORM_SRC = 1, /* Format is mdoc(7) or man(7). */
+ FORM_CAT, /* Manual page is preformatted. */
+ FORM_NONE /* Format is unknown. */
+};
enum argmode {
ARG_FILE = 0,
@@ -84,7 +92,7 @@ struct manpage {
size_t ipath; /* number of the manpath */
uint64_t bits; /* name type mask */
int sec; /* section number, 10 means invalid */
- int form; /* 0 == catpage */
+ enum form form;
};
struct mansearch {
@@ -98,7 +106,6 @@ struct mansearch {
struct manpaths;
-int mansearch_setup(int);
int mansearch(const struct mansearch *cfg, /* options */
const struct manpaths *paths, /* manpaths */
int argc, /* size of argv */
diff --git a/usr/src/cmd/mandoc/mdoc.c b/usr/src/cmd/mandoc/mdoc.c
index 724d45c652..5be1e7810d 100644
--- a/usr/src/cmd/mandoc/mdoc.c
+++ b/usr/src/cmd/mandoc/mdoc.c
@@ -1,7 +1,7 @@
-/* $Id: mdoc.c,v 1.256 2015/10/30 19:04:16 schwarze Exp $ */
+/* $Id: mdoc.c,v 1.260 2017/02/16 03:00:23 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010, 2012-2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -136,16 +136,16 @@ mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, int tok)
struct roff_node *
mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos, int tok,
- struct roff_node *body, enum mdoc_endbody end)
+ struct roff_node *body)
{
struct roff_node *p;
- body->flags |= MDOC_ENDED;
- body->parent->flags |= MDOC_ENDED;
+ body->flags |= NODE_ENDED;
+ body->parent->flags |= NODE_ENDED;
p = roff_node_alloc(mdoc, line, pos, ROFFT_BODY, tok);
p->body = body;
p->norm = body->norm;
- p->end = end;
+ p->end = ENDBODY_SPACE;
roff_node_append(mdoc, p);
mdoc->next = ROFF_NEXT_SIBLING;
return p;
@@ -219,29 +219,19 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
struct roff_node *n;
char *c, *ws, *end;
- assert(mdoc->last);
n = mdoc->last;
/*
- * Divert directly to list processing if we're encountering a
- * columnar ROFFT_BLOCK with or without a prior ROFFT_BLOCK entry
- * (a ROFFT_BODY means it's already open, in which case we should
- * process within its context in the normal way).
+ * If a column list contains plain text, assume an implicit item
+ * macro. This can happen one or more times at the beginning
+ * of such a list, intermixed with non-It mdoc macros and with
+ * nodes generated on the roff level, for example by tbl.
*/
- if (n->tok == MDOC_Bl && n->type == ROFFT_BODY &&
- n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) {
- /* `Bl' is open without any children. */
- mdoc->flags |= MDOC_FREECOL;
- mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf);
- return 1;
- }
-
- if (n->tok == MDOC_It && n->type == ROFFT_BLOCK &&
- NULL != n->parent &&
- MDOC_Bl == n->parent->tok &&
- LIST_column == n->parent->norm->Bl.type) {
- /* `Bl' has block-level `It' children. */
+ if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY &&
+ n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) ||
+ (n->parent != NULL && n->parent->tok == MDOC_Bl &&
+ n->parent->norm->Bl.type == LIST_column)) {
mdoc->flags |= MDOC_FREECOL;
mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf);
return 1;
@@ -302,7 +292,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
* behaviour that we want to work around it.
*/
roff_elem_alloc(mdoc, line, offs, MDOC_sp);
- mdoc->last->flags |= MDOC_VALID | MDOC_ENDED;
+ mdoc->last->flags |= NODE_VALID | NODE_ENDED;
mdoc->next = ROFF_NEXT_SIBLING;
return 1;
}
@@ -321,7 +311,23 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
assert(buf < end);
if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
- mdoc->last->flags |= MDOC_EOS;
+ mdoc->last->flags |= NODE_EOS;
+
+ for (c = buf + offs; c != NULL; c = strchr(c + 1, '.')) {
+ if (c - buf < offs + 2)
+ continue;
+ if (end - c < 4)
+ break;
+ if (isalpha((unsigned char)c[-2]) &&
+ isalpha((unsigned char)c[-1]) &&
+ c[1] == ' ' &&
+ isupper((unsigned char)(c[2] == ' ' ? c[3] : c[2])) &&
+ (c[-2] != 'n' || c[-1] != 'c') &&
+ (c[-2] != 'v' || c[-1] != 's'))
+ mandoc_msg(MANDOCERR_EOS, mdoc->parse,
+ line, (int)(c - buf), NULL);
+ }
+
return 1;
}
@@ -393,36 +399,23 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs)
* into macro processing.
*/
- if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) {
- mdoc_macro(mdoc, tok, ln, sv, &offs, buf);
- return 1;
- }
-
n = mdoc->last;
- assert(mdoc->last);
-
- /*
- * If the first macro of a `Bl -column', open an `It' block
- * context around the parsed macro.
- */
-
- if (n->tok == MDOC_Bl && n->type == ROFFT_BODY &&
- n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) {
- mdoc->flags |= MDOC_FREECOL;
- mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf);
+ if (n == NULL || tok == MDOC_It || tok == MDOC_El) {
+ mdoc_macro(mdoc, tok, ln, sv, &offs, buf);
return 1;
}
/*
- * If we're following a block-level `It' within a `Bl -column'
- * context (perhaps opened in the above block or in ptext()),
- * then open an `It' block context around the parsed macro.
+ * If a column list contains a non-It macro, assume an implicit
+ * item macro. This can happen one or more times at the
+ * beginning of such a list, intermixed with text lines and
+ * with nodes generated on the roff level, for example by tbl.
*/
- if (n->tok == MDOC_It && n->type == ROFFT_BLOCK &&
- NULL != n->parent &&
- MDOC_Bl == n->parent->tok &&
- LIST_column == n->parent->norm->Bl.type) {
+ if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY &&
+ n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) ||
+ (n->parent != NULL && n->parent->tok == MDOC_Bl &&
+ n->parent->norm->Bl.type == LIST_column)) {
mdoc->flags |= MDOC_FREECOL;
mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf);
return 1;
diff --git a/usr/src/cmd/mandoc/mdoc_argv.c b/usr/src/cmd/mandoc/mdoc_argv.c
index 8675bdb2db..b47c7dbdea 100644
--- a/usr/src/cmd/mandoc/mdoc_argv.c
+++ b/usr/src/cmd/mandoc/mdoc_argv.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_argv.c,v 1.107 2015/10/17 00:21:07 schwarze Exp $ */
+/* $Id: mdoc_argv.c,v 1.109 2016/08/28 16:15:12 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -29,6 +29,7 @@
#include "roff.h"
#include "mdoc.h"
#include "libmandoc.h"
+#include "roff_int.h"
#include "libmdoc.h"
#define MULTI_STEP 5 /* pre-allocate argument values */
@@ -479,7 +480,7 @@ args(struct roff_man *mdoc, int line, int *pos,
* unless there is a blank in between.
*/
- if (p[-1] != ' ')
+ if (p > buf && p[-1] != ' ')
mdoc->flags |= MDOC_PHRASEQL;
if (p[1] != ' ')
mdoc->flags |= MDOC_PHRASEQN;
diff --git a/usr/src/cmd/mandoc/mdoc_hash.c b/usr/src/cmd/mandoc/mdoc_hash.c
index 476116d792..cad3c2db1a 100644
--- a/usr/src/cmd/mandoc/mdoc_hash.c
+++ b/usr/src/cmd/mandoc/mdoc_hash.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_hash.c,v 1.26 2015/10/06 18:32:19 schwarze Exp $ */
+/* $Id: mdoc_hash.c,v 1.27 2016/07/15 18:03:45 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -26,8 +26,10 @@
#include <stdio.h>
#include <string.h>
+#include "mandoc.h"
#include "roff.h"
#include "mdoc.h"
+#include "libmandoc.h"
#include "libmdoc.h"
static unsigned char table[27 * 12];
diff --git a/usr/src/cmd/mandoc/mdoc_html.c b/usr/src/cmd/mandoc/mdoc_html.c
index 8e21bc7963..e824514166 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.240 2016/01/08 17:48:09 schwarze Exp $ */
+/* $Id: mdoc_html.c,v 1.271 2017/02/16 03:00:23 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -48,14 +48,13 @@ struct htmlmdoc {
void (*post)(MDOC_ARGS);
};
+static char *make_id(const struct roff_node *);
static void print_mdoc_head(MDOC_ARGS);
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 a2width(const char *, struct roffsu *);
-
static void mdoc_root_post(MDOC_ARGS);
static int mdoc_root_pre(MDOC_ARGS);
@@ -70,9 +69,8 @@ static int mdoc_bf_pre(MDOC_ARGS);
static void mdoc_bk_post(MDOC_ARGS);
static int mdoc_bk_pre(MDOC_ARGS);
static int mdoc_bl_pre(MDOC_ARGS);
-static int mdoc_bt_pre(MDOC_ARGS);
-static int mdoc_bx_pre(MDOC_ARGS);
static int mdoc_cd_pre(MDOC_ARGS);
+static int mdoc_cm_pre(MDOC_ARGS);
static int mdoc_d1_pre(MDOC_ARGS);
static int mdoc_dv_pre(MDOC_ARGS);
static int mdoc_fa_pre(MDOC_ARGS);
@@ -107,15 +105,14 @@ static int mdoc_pp_pre(MDOC_ARGS);
static void mdoc_quote_post(MDOC_ARGS);
static int mdoc_quote_pre(MDOC_ARGS);
static int mdoc_rs_pre(MDOC_ARGS);
-static int mdoc_rv_pre(MDOC_ARGS);
static int mdoc_sh_pre(MDOC_ARGS);
static int mdoc_skip_pre(MDOC_ARGS);
static int mdoc_sm_pre(MDOC_ARGS);
static int mdoc_sp_pre(MDOC_ARGS);
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_ud_pre(MDOC_ARGS);
static int mdoc_va_pre(MDOC_ARGS);
static int mdoc_vt_pre(MDOC_ARGS);
static int mdoc_xr_pre(MDOC_ARGS);
@@ -140,7 +137,7 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = {
{mdoc_an_pre, NULL}, /* An */
{mdoc_ar_pre, NULL}, /* Ar */
{mdoc_cd_pre, NULL}, /* Cd */
- {mdoc_fl_pre, NULL}, /* Cm */
+ {mdoc_cm_pre, NULL}, /* Cm */
{mdoc_dv_pre, NULL}, /* Dv */
{mdoc_er_pre, NULL}, /* Er */
{mdoc_ev_pre, NULL}, /* Ev */
@@ -158,8 +155,8 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = {
{mdoc_quote_pre, mdoc_quote_post}, /* Op */
{mdoc_ft_pre, NULL}, /* Ot */
{mdoc_pa_pre, NULL}, /* Pa */
- {mdoc_rv_pre, NULL}, /* Rv */
- {NULL, NULL}, /* St */
+ {mdoc_ex_pre, NULL}, /* Rv */
+ {mdoc_st_pre, NULL}, /* St */
{mdoc_va_pre, NULL}, /* Va */
{mdoc_vt_pre, NULL}, /* Vt */
{mdoc_xr_pre, NULL}, /* Xr */
@@ -177,13 +174,13 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = {
{NULL, NULL}, /* Ac */
{mdoc_quote_pre, mdoc_quote_post}, /* Ao */
{mdoc_quote_pre, mdoc_quote_post}, /* Aq */
- {NULL, NULL}, /* At */
+ {mdoc_xx_pre, NULL}, /* At */
{NULL, NULL}, /* Bc */
{mdoc_bf_pre, NULL}, /* Bf */
{mdoc_quote_pre, mdoc_quote_post}, /* Bo */
{mdoc_quote_pre, mdoc_quote_post}, /* Bq */
{mdoc_xx_pre, NULL}, /* Bsx */
- {mdoc_bx_pre, NULL}, /* Bx */
+ {mdoc_xx_pre, NULL}, /* Bx */
{mdoc_skip_pre, NULL}, /* Db */
{NULL, NULL}, /* Dc */
{mdoc_quote_pre, mdoc_quote_post}, /* Do */
@@ -224,10 +221,10 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = {
{NULL, NULL}, /* Oc */
{mdoc_bk_pre, mdoc_bk_post}, /* Bk */
{NULL, NULL}, /* Ek */
- {mdoc_bt_pre, NULL}, /* Bt */
+ {NULL, NULL}, /* Bt */
{NULL, NULL}, /* Hf */
{mdoc_em_pre, NULL}, /* Fr */
- {mdoc_ud_pre, NULL}, /* Ud */
+ {NULL, NULL}, /* Ud */
{mdoc_lb_pre, NULL}, /* Lb */
{mdoc_pp_pre, NULL}, /* Lp */
{mdoc_lk_pre, NULL}, /* Lk */
@@ -247,37 +244,6 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = {
{mdoc_skip_pre, NULL}, /* ll */
};
-static const char * const lists[LIST_MAX] = {
- NULL,
- "list-bul",
- "list-col",
- "list-dash",
- "list-diag",
- "list-enum",
- "list-hang",
- "list-hyph",
- "list-inset",
- "list-item",
- "list-ohang",
- "list-tag"
-};
-
-
-/*
- * Calculate the scaling unit passed in a `-width' argument. This uses
- * either a native scaling unit (e.g., 1i, 2m) or the string length of
- * the value.
- */
-static void
-a2width(const char *p, struct roffsu *su)
-{
-
- if (a2roffsu(p, su, SCALE_MAX) < 2) {
- su->unit = SCALE_EN;
- su->scale = html_strlen(p);
- } else if (su->scale < 0.0)
- su->scale = 0.0;
-}
/*
* See the same function in mdoc_term.c for documentation.
@@ -286,14 +252,14 @@ static void
synopsis_pre(struct html *h, const struct roff_node *n)
{
- if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
+ if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
return;
if (n->prev->tok == n->tok &&
MDOC_Fo != n->tok &&
MDOC_Ft != n->tok &&
MDOC_Fn != n->tok) {
- print_otag(h, TAG_BR, 0, NULL);
+ print_otag(h, TAG_BR, "");
return;
}
@@ -312,7 +278,7 @@ synopsis_pre(struct html *h, const struct roff_node *n)
}
/* FALLTHROUGH */
default:
- print_otag(h, TAG_BR, 0, NULL);
+ print_otag(h, TAG_BR, "");
break;
}
}
@@ -320,45 +286,48 @@ synopsis_pre(struct html *h, const struct roff_node *n)
void
html_mdoc(void *arg, const struct roff_man *mdoc)
{
- struct htmlpair tag;
struct html *h;
- struct tag *t, *tt;
+ struct tag *t;
- PAIR_CLASS_INIT(&tag, "mandoc");
h = (struct html *)arg;
- if ( ! (HTML_FRAGMENT & h->oflags)) {
+ if ((h->oflags & HTML_FRAGMENT) == 0) {
print_gen_decls(h);
- t = print_otag(h, TAG_HTML, 0, NULL);
- tt = print_otag(h, TAG_HEAD, 0, NULL);
+ print_otag(h, TAG_HTML, "");
+ t = print_otag(h, TAG_HEAD, "");
print_mdoc_head(&mdoc->meta, mdoc->first->child, h);
- print_tagq(h, tt);
- print_otag(h, TAG_BODY, 0, NULL);
- print_otag(h, TAG_DIV, 1, &tag);
- } else
- t = print_otag(h, TAG_DIV, 1, &tag);
+ print_tagq(h, t);
+ print_otag(h, TAG_BODY, "");
+ }
mdoc_root_pre(&mdoc->meta, mdoc->first->child, h);
+ t = print_otag(h, TAG_DIV, "c", "manual-text");
print_mdoc_nodelist(&mdoc->meta, mdoc->first->child, h);
- mdoc_root_post(&mdoc->meta, mdoc->first->child, h);
print_tagq(h, t);
- putchar('\n');
+ mdoc_root_post(&mdoc->meta, mdoc->first->child, h);
+ print_tagq(h, NULL);
}
static void
print_mdoc_head(MDOC_ARGS)
{
+ char *cp;
print_gen_head(h);
- bufinit(h);
- bufcat(h, meta->title);
- if (meta->msec)
- bufcat_fmt(h, "(%s)", meta->msec);
- if (meta->arch)
- bufcat_fmt(h, " (%s)", meta->arch);
- print_otag(h, TAG_TITLE, 0, NULL);
- print_text(h, h->buf);
+ if (meta->arch != NULL && meta->msec != NULL)
+ mandoc_asprintf(&cp, "%s(%s) (%s)", meta->title,
+ meta->msec, meta->arch);
+ else if (meta->msec != NULL)
+ mandoc_asprintf(&cp, "%s(%s)", meta->title, meta->msec);
+ else if (meta->arch != NULL)
+ mandoc_asprintf(&cp, "%s (%s)", meta->title, meta->arch);
+ else
+ cp = mandoc_strdup(meta->title);
+
+ print_otag(h, TAG_TITLE, "");
+ print_text(h, cp);
+ free(cp);
}
static void
@@ -377,9 +346,12 @@ print_mdoc_node(MDOC_ARGS)
int child;
struct tag *t;
+ if (n->flags & NODE_NOPRT)
+ return;
+
child = 1;
- t = h->tags.head;
- n->flags &= ~MDOC_ENDED;
+ t = h->tag;
+ n->flags &= ~NODE_ENDED;
switch (n->type) {
case ROFFT_TEXT:
@@ -390,18 +362,16 @@ print_mdoc_node(MDOC_ARGS)
* Make sure that if we're in a literal mode already
* (i.e., within a <PRE>) don't print the newline.
*/
- if (' ' == *n->string && MDOC_LINE & n->flags)
+ if (' ' == *n->string && NODE_LINE & n->flags)
if ( ! (HTML_LITERAL & h->flags))
- print_otag(h, TAG_BR, 0, NULL);
- if (MDOC_DELIMC & n->flags)
+ print_otag(h, TAG_BR, "");
+ if (NODE_DELIMC & n->flags)
h->flags |= HTML_NOSPACE;
print_text(h, n->string);
- if (MDOC_DELIMO & n->flags)
+ if (NODE_DELIMO & n->flags)
h->flags |= HTML_NOSPACE;
return;
case ROFFT_EQN:
- if (n->flags & MDOC_LINE)
- putchar('\n');
print_eqn(h, n->eqn);
break;
case ROFFT_TBL:
@@ -420,7 +390,7 @@ print_mdoc_node(MDOC_ARGS)
*/
if (h->tblt != NULL) {
print_tblclose(h);
- t = h->tags.head;
+ t = h->tag;
}
assert(h->tblt == NULL);
if (mdocs[n->tok].pre && (n->end == ENDBODY_NOT || n->child))
@@ -428,7 +398,7 @@ print_mdoc_node(MDOC_ARGS)
break;
}
- if (h->flags & HTML_KEEP && n->flags & MDOC_LINE) {
+ if (h->flags & HTML_KEEP && n->flags & NODE_LINE) {
h->flags &= ~HTML_KEEP;
h->flags |= HTML_PREKEEP;
}
@@ -442,13 +412,11 @@ print_mdoc_node(MDOC_ARGS)
case ROFFT_EQN:
break;
default:
- if ( ! mdocs[n->tok].post || n->flags & MDOC_ENDED)
+ if ( ! mdocs[n->tok].post || n->flags & NODE_ENDED)
break;
(*mdocs[n->tok].post)(meta, n, h);
if (n->end != ENDBODY_NOT)
- n->body->flags |= MDOC_ENDED;
- if (n->end == ENDBODY_NOSPACE)
- h->flags |= HTML_NOSPACE;
+ n->body->flags |= NODE_ENDED;
break;
}
}
@@ -456,23 +424,16 @@ print_mdoc_node(MDOC_ARGS)
static void
mdoc_root_post(MDOC_ARGS)
{
- struct htmlpair tag;
struct tag *t, *tt;
- PAIR_CLASS_INIT(&tag, "foot");
- t = print_otag(h, TAG_TABLE, 1, &tag);
-
- print_otag(h, TAG_TBODY, 0, NULL);
+ t = print_otag(h, TAG_TABLE, "c", "foot");
+ tt = print_otag(h, TAG_TR, "");
- tt = print_otag(h, TAG_TR, 0, NULL);
-
- PAIR_CLASS_INIT(&tag, "foot-date");
- print_otag(h, TAG_TD, 1, &tag);
+ print_otag(h, TAG_TD, "c", "foot-date");
print_text(h, meta->date);
print_stagq(h, tt);
- PAIR_CLASS_INIT(&tag, "foot-os");
- print_otag(h, TAG_TD, 1, &tag);
+ print_otag(h, TAG_TD, "c", "foot-os");
print_text(h, meta->os);
print_tagq(h, t);
}
@@ -480,7 +441,6 @@ mdoc_root_post(MDOC_ARGS)
static int
mdoc_root_pre(MDOC_ARGS)
{
- struct htmlpair tag;
struct tag *t, *tt;
char *volume, *title;
@@ -496,25 +456,18 @@ mdoc_root_pre(MDOC_ARGS)
mandoc_asprintf(&title, "%s(%s)",
meta->title, meta->msec);
- PAIR_CLASS_INIT(&tag, "head");
- t = print_otag(h, TAG_TABLE, 1, &tag);
-
- print_otag(h, TAG_TBODY, 0, NULL);
+ t = print_otag(h, TAG_TABLE, "c", "head");
+ tt = print_otag(h, TAG_TR, "");
- tt = print_otag(h, TAG_TR, 0, NULL);
-
- PAIR_CLASS_INIT(&tag, "head-ltitle");
- print_otag(h, TAG_TD, 1, &tag);
+ print_otag(h, TAG_TD, "c", "head-ltitle");
print_text(h, title);
print_stagq(h, tt);
- PAIR_CLASS_INIT(&tag, "head-vol");
- print_otag(h, TAG_TD, 1, &tag);
+ print_otag(h, TAG_TD, "c", "head-vol");
print_text(h, volume);
print_stagq(h, tt);
- PAIR_CLASS_INIT(&tag, "head-rtitle");
- print_otag(h, TAG_TD, 1, &tag);
+ print_otag(h, TAG_TD, "c", "head-rtitle");
print_text(h, title);
print_tagq(h, t);
@@ -523,137 +476,120 @@ mdoc_root_pre(MDOC_ARGS)
return 1;
}
+static char *
+make_id(const struct roff_node *n)
+{
+ const struct roff_node *nch;
+ char *buf, *cp;
+
+ for (nch = n->child; nch != NULL; nch = nch->next)
+ if (nch->type != ROFFT_TEXT)
+ return NULL;
+
+ buf = NULL;
+ deroff(&buf, n);
+
+ /* http://www.w3.org/TR/html5/dom.html#the-id-attribute */
+
+ for (cp = buf; *cp != '\0'; cp++)
+ if (*cp == ' ')
+ *cp = '_';
+
+ return buf;
+}
+
static int
mdoc_sh_pre(MDOC_ARGS)
{
- struct htmlpair tag;
+ char *id;
switch (n->type) {
- case ROFFT_BLOCK:
- PAIR_CLASS_INIT(&tag, "section");
- print_otag(h, TAG_DIV, 1, &tag);
- return 1;
+ case ROFFT_HEAD:
+ id = make_id(n);
+ print_otag(h, TAG_H1, "ci", "Sh", id);
+ free(id);
+ break;
case ROFFT_BODY:
if (n->sec == SEC_AUTHORS)
h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT);
- return 1;
+ break;
default:
break;
}
-
- bufinit(h);
-
- for (n = n->child; n != NULL && n->type == ROFFT_TEXT; ) {
- bufcat_id(h, n->string);
- if (NULL != (n = n->next))
- bufcat_id(h, " ");
- }
-
- if (NULL == n) {
- PAIR_ID_INIT(&tag, h->buf);
- print_otag(h, TAG_H1, 1, &tag);
- } else
- print_otag(h, TAG_H1, 0, NULL);
-
return 1;
}
static int
mdoc_ss_pre(MDOC_ARGS)
{
- struct htmlpair tag;
+ char *id;
- if (n->type == ROFFT_BLOCK) {
- PAIR_CLASS_INIT(&tag, "subsection");
- print_otag(h, TAG_DIV, 1, &tag);
- return 1;
- } else if (n->type == ROFFT_BODY)
+ if (n->type != ROFFT_HEAD)
return 1;
- bufinit(h);
-
- for (n = n->child; n != NULL && n->type == ROFFT_TEXT; ) {
- bufcat_id(h, n->string);
- if (NULL != (n = n->next))
- bufcat_id(h, " ");
- }
-
- if (NULL == n) {
- PAIR_ID_INIT(&tag, h->buf);
- print_otag(h, TAG_H2, 1, &tag);
- } else
- print_otag(h, TAG_H2, 0, NULL);
-
+ id = make_id(n);
+ print_otag(h, TAG_H2, "ci", "Ss", id);
+ free(id);
return 1;
}
static int
mdoc_fl_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "flag");
- print_otag(h, TAG_B, 1, &tag);
-
- /* `Cm' has no leading hyphen. */
-
- if (MDOC_Cm == n->tok)
- return 1;
-
+ print_otag(h, TAG_B, "c", "Fl");
print_text(h, "\\-");
if (!(n->child == NULL &&
(n->next == NULL ||
n->next->type == ROFFT_TEXT ||
- n->next->flags & MDOC_LINE)))
+ n->next->flags & NODE_LINE)))
h->flags |= HTML_NOSPACE;
return 1;
}
static int
-mdoc_nd_pre(MDOC_ARGS)
+mdoc_cm_pre(MDOC_ARGS)
{
- struct htmlpair tag;
+ print_otag(h, TAG_B, "c", "Cm");
+ return 1;
+}
+static int
+mdoc_nd_pre(MDOC_ARGS)
+{
if (n->type != ROFFT_BODY)
return 1;
/* XXX: this tag in theory can contain block elements. */
print_text(h, "\\(em");
- PAIR_CLASS_INIT(&tag, "desc");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_SPAN, "c", "Nd");
return 1;
}
static int
mdoc_nm_pre(MDOC_ARGS)
{
- struct htmlpair tag;
- struct roffsu su;
+ struct tag *t;
int len;
switch (n->type) {
case ROFFT_HEAD:
- print_otag(h, TAG_TD, 0, NULL);
+ print_otag(h, TAG_TD, "");
/* FALLTHROUGH */
case ROFFT_ELEM:
- PAIR_CLASS_INIT(&tag, "name");
- print_otag(h, TAG_B, 1, &tag);
- if (n->child == NULL && meta->name != NULL)
- print_text(h, meta->name);
+ print_otag(h, TAG_B, "c", "Nm");
return 1;
case ROFFT_BODY:
- print_otag(h, TAG_TD, 0, NULL);
+ print_otag(h, TAG_TD, "");
return 1;
default:
break;
}
synopsis_pre(h, n);
- PAIR_CLASS_INIT(&tag, "synopsis");
- print_otag(h, TAG_TABLE, 1, &tag);
+ print_otag(h, TAG_TABLE, "c", "Nm");
for (len = 0, n = n->head->child; n; n = n->next)
if (n->type == ROFFT_TEXT)
@@ -662,35 +598,26 @@ mdoc_nm_pre(MDOC_ARGS)
if (len == 0 && meta->name != NULL)
len = html_strlen(meta->name);
- SCALE_HS_INIT(&su, len);
- bufinit(h);
- bufcat_su(h, "width", &su);
- PAIR_STYLE_INIT(&tag, h);
- print_otag(h, TAG_COL, 1, &tag);
- print_otag(h, TAG_COL, 0, NULL);
- print_otag(h, TAG_TBODY, 0, NULL);
- print_otag(h, TAG_TR, 0, NULL);
+ t = print_otag(h, TAG_COLGROUP, "");
+ print_otag(h, TAG_COL, "shw", len);
+ print_otag(h, TAG_COL, "");
+ print_tagq(h, t);
+ print_otag(h, TAG_TR, "");
return 1;
}
static int
mdoc_xr_pre(MDOC_ARGS)
{
- struct htmlpair tag[2];
-
if (NULL == n->child)
return 0;
- PAIR_CLASS_INIT(&tag[0], "link-man");
-
- if (h->base_man) {
- buffmt_man(h, n->child->string,
- n->child->next ?
- n->child->next->string : NULL);
- PAIR_HREF_INIT(&tag[1], h->buf);
- print_otag(h, TAG_A, 2, tag);
- } else
- print_otag(h, TAG_A, 1, tag);
+ if (h->base_man)
+ print_otag(h, TAG_A, "chM", "Xr",
+ n->child->string, n->child->next == NULL ?
+ NULL : n->child->next->string);
+ else
+ print_otag(h, TAG_A, "c", "Xr");
n = n->child;
print_text(h, n->string);
@@ -711,7 +638,7 @@ static int
mdoc_ns_pre(MDOC_ARGS)
{
- if ( ! (MDOC_LINE & n->flags))
+ if ( ! (NODE_LINE & n->flags))
h->flags |= HTML_NOSPACE;
return 1;
}
@@ -719,174 +646,150 @@ mdoc_ns_pre(MDOC_ARGS)
static int
mdoc_ar_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "arg");
- print_otag(h, TAG_I, 1, &tag);
+ print_otag(h, TAG_VAR, "c", "Ar");
return 1;
}
static int
mdoc_xx_pre(MDOC_ARGS)
{
- const char *pp;
- struct htmlpair tag;
- int flags;
-
- switch (n->tok) {
- case MDOC_Bsx:
- pp = "BSD/OS";
- break;
- case MDOC_Dx:
- pp = "DragonFly";
- break;
- case MDOC_Fx:
- pp = "FreeBSD";
- break;
- case MDOC_Nx:
- pp = "NetBSD";
- break;
- case MDOC_Ox:
- pp = "OpenBSD";
- break;
- case MDOC_Ux:
- pp = "UNIX";
- break;
- default:
- return 1;
- }
-
- PAIR_CLASS_INIT(&tag, "unix");
- print_otag(h, TAG_SPAN, 1, &tag);
-
- print_text(h, pp);
- if (n->child) {
- flags = h->flags;
- h->flags |= HTML_KEEP;
- print_text(h, n->child->string);
- h->flags = flags;
- }
- return 0;
-}
-
-static int
-mdoc_bx_pre(MDOC_ARGS)
-{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "unix");
- print_otag(h, TAG_SPAN, 1, &tag);
-
- if (NULL != (n = n->child)) {
- print_text(h, n->string);
- h->flags |= HTML_NOSPACE;
- print_text(h, "BSD");
- } else {
- print_text(h, "BSD");
- return 0;
- }
-
- if (NULL != (n = n->next)) {
- h->flags |= HTML_NOSPACE;
- print_text(h, "-");
- h->flags |= HTML_NOSPACE;
- print_text(h, n->string);
- }
-
- return 0;
+ print_otag(h, TAG_SPAN, "c", "Ux");
+ return 1;
}
static int
mdoc_it_pre(MDOC_ARGS)
{
- struct roffsu su;
- enum mdoc_list type;
- struct htmlpair tag[2];
- const struct roff_node *bl;
+ const struct roff_node *bl;
+ struct tag *t;
+ const char *cattr;
+ enum mdoc_list type;
bl = n->parent;
- while (bl && MDOC_Bl != bl->tok)
+ while (bl != NULL && bl->tok != MDOC_Bl)
bl = bl->parent;
-
- assert(bl);
-
type = bl->norm->Bl.type;
- assert(lists[type]);
- PAIR_CLASS_INIT(&tag[0], lists[type]);
-
- bufinit(h);
+ switch (type) {
+ case LIST_bullet:
+ cattr = "It-bullet";
+ break;
+ case LIST_dash:
+ case LIST_hyphen:
+ cattr = "It-dash";
+ break;
+ case LIST_item:
+ cattr = "It-item";
+ break;
+ case LIST_enum:
+ cattr = "It-enum";
+ break;
+ case LIST_diag:
+ cattr = "It-diag";
+ break;
+ case LIST_hang:
+ cattr = "It-hang";
+ break;
+ case LIST_inset:
+ cattr = "It-inset";
+ break;
+ case LIST_ohang:
+ cattr = "It-ohang";
+ break;
+ case LIST_tag:
+ cattr = "It-tag";
+ break;
+ case LIST_column:
+ cattr = "It-column";
+ break;
+ default:
+ break;
+ }
- if (n->type == ROFFT_HEAD) {
- switch (type) {
- case LIST_bullet:
- case LIST_dash:
- case LIST_item:
- case LIST_hyphen:
- case LIST_enum:
+ switch (type) {
+ case LIST_bullet:
+ case LIST_dash:
+ case LIST_hyphen:
+ case LIST_item:
+ case LIST_enum:
+ switch (n->type) {
+ case ROFFT_HEAD:
return 0;
- case LIST_diag:
- case LIST_hang:
- case LIST_inset:
- case LIST_ohang:
- case LIST_tag:
- SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
- bufcat_su(h, "margin-top", &su);
- PAIR_STYLE_INIT(&tag[1], h);
- print_otag(h, TAG_DT, 2, tag);
- if (LIST_diag != type)
- break;
- PAIR_CLASS_INIT(&tag[0], "diag");
- print_otag(h, TAG_B, 1, tag);
- break;
- case LIST_column:
+ case ROFFT_BODY:
+ if (bl->norm->Bl.comp)
+ print_otag(h, TAG_LI, "csvt", cattr, 0);
+ else
+ print_otag(h, TAG_LI, "c", cattr);
break;
default:
break;
}
- } else if (n->type == ROFFT_BODY) {
- switch (type) {
- case LIST_bullet:
- case LIST_hyphen:
- case LIST_dash:
- case LIST_enum:
- case LIST_item:
- SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
- bufcat_su(h, "margin-top", &su);
- PAIR_STYLE_INIT(&tag[1], h);
- print_otag(h, TAG_LI, 2, tag);
+ break;
+ case LIST_diag:
+ case LIST_hang:
+ case LIST_inset:
+ case LIST_ohang:
+ switch (n->type) {
+ case ROFFT_HEAD:
+ if (bl->norm->Bl.comp)
+ print_otag(h, TAG_DT, "csvt", cattr, 0);
+ else
+ print_otag(h, TAG_DT, "c", cattr);
+ if (type == LIST_diag)
+ print_otag(h, TAG_B, "c", cattr);
break;
- case LIST_diag:
- case LIST_hang:
- case LIST_inset:
- case LIST_ohang:
- case LIST_tag:
- if (NULL == bl->norm->Bl.width) {
- print_otag(h, TAG_DD, 1, tag);
- break;
+ case ROFFT_BODY:
+ print_otag(h, TAG_DD, "cswl", cattr,
+ bl->norm->Bl.width);
+ break;
+ default:
+ break;
+ }
+ break;
+ case LIST_tag:
+ switch (n->type) {
+ case ROFFT_HEAD:
+ if (h->style != NULL && !bl->norm->Bl.comp &&
+ (n->parent->prev == NULL ||
+ n->parent->prev->body->child != NULL)) {
+ t = print_otag(h, TAG_DT, "csWl",
+ cattr, bl->norm->Bl.width);
+ print_text(h, "\\ ");
+ print_tagq(h, t);
+ t = print_otag(h, TAG_DD, "c", cattr);
+ print_text(h, "\\ ");
+ print_tagq(h, t);
}
- a2width(bl->norm->Bl.width, &su);
- bufcat_su(h, "margin-left", &su);
- PAIR_STYLE_INIT(&tag[1], h);
- print_otag(h, TAG_DD, 2, tag);
+ print_otag(h, TAG_DT, "csWl", cattr,
+ bl->norm->Bl.width);
break;
- case LIST_column:
- SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
- bufcat_su(h, "margin-top", &su);
- PAIR_STYLE_INIT(&tag[1], h);
- print_otag(h, TAG_TD, 2, tag);
+ case ROFFT_BODY:
+ if (n->child == NULL) {
+ print_otag(h, TAG_DD, "css?", cattr,
+ "width", "auto");
+ print_text(h, "\\ ");
+ } else
+ print_otag(h, TAG_DD, "c", cattr);
break;
default:
break;
}
- } else {
- switch (type) {
- case LIST_column:
- print_otag(h, TAG_TR, 1, tag);
+ break;
+ case LIST_column:
+ switch (n->type) {
+ case ROFFT_HEAD:
break;
- default:
+ case ROFFT_BODY:
+ if (bl->norm->Bl.comp)
+ print_otag(h, TAG_TD, "csvt", cattr, 0);
+ else
+ print_otag(h, TAG_TD, "c", cattr);
break;
+ default:
+ print_otag(h, TAG_TR, "c", cattr);
}
+ default:
+ break;
}
return 1;
@@ -895,19 +798,20 @@ mdoc_it_pre(MDOC_ARGS)
static int
mdoc_bl_pre(MDOC_ARGS)
{
- int i;
- struct htmlpair tag[3];
- struct roffsu su;
- char buf[BUFSIZ];
+ struct tag *t;
+ struct mdoc_bl *bl;
+ const char *cattr;
+ size_t i;
+ enum htmltag elemtype;
- if (n->type == ROFFT_BODY) {
- if (LIST_column == n->norm->Bl.type)
- print_otag(h, TAG_TBODY, 0, NULL);
+ bl = &n->norm->Bl;
+
+ switch (n->type) {
+ case ROFFT_BODY:
return 1;
- }
- if (n->type == ROFFT_HEAD) {
- if (LIST_column != n->norm->Bl.type)
+ case ROFFT_HEAD:
+ if (bl->type != LIST_column || bl->ncols == 0)
return 0;
/*
@@ -917,142 +821,100 @@ mdoc_bl_pre(MDOC_ARGS)
* screen and we want to preserve that behaviour.
*/
- for (i = 0; i < (int)n->norm->Bl.ncols; i++) {
- bufinit(h);
- a2width(n->norm->Bl.cols[i], &su);
- if (i < (int)n->norm->Bl.ncols - 1)
- bufcat_su(h, "width", &su);
- else
- bufcat_su(h, "min-width", &su);
- PAIR_STYLE_INIT(&tag[0], h);
- print_otag(h, TAG_COL, 1, tag);
- }
-
+ t = print_otag(h, TAG_COLGROUP, "");
+ for (i = 0; i < bl->ncols - 1; i++)
+ print_otag(h, TAG_COL, "sww", bl->cols[i]);
+ print_otag(h, TAG_COL, "swW", bl->cols[i]);
+ print_tagq(h, t);
return 0;
- }
-
- SCALE_VS_INIT(&su, 0);
- bufinit(h);
- bufcat_su(h, "margin-top", &su);
- bufcat_su(h, "margin-bottom", &su);
- PAIR_STYLE_INIT(&tag[0], h);
- assert(lists[n->norm->Bl.type]);
- (void)strlcpy(buf, "list ", BUFSIZ);
- (void)strlcat(buf, lists[n->norm->Bl.type], BUFSIZ);
- PAIR_INIT(&tag[1], ATTR_CLASS, buf);
-
- /* Set the block's left-hand margin. */
-
- if (n->norm->Bl.offs) {
- a2width(n->norm->Bl.offs, &su);
- bufcat_su(h, "margin-left", &su);
+ default:
+ break;
}
- switch (n->norm->Bl.type) {
+ switch (bl->type) {
case LIST_bullet:
+ elemtype = TAG_UL;
+ cattr = "Bl-bullet";
+ break;
case LIST_dash:
case LIST_hyphen:
+ elemtype = TAG_UL;
+ cattr = "Bl-dash";
+ break;
case LIST_item:
- print_otag(h, TAG_UL, 2, tag);
+ elemtype = TAG_UL;
+ cattr = "Bl-item";
break;
case LIST_enum:
- print_otag(h, TAG_OL, 2, tag);
+ elemtype = TAG_OL;
+ cattr = "Bl-enum";
break;
case LIST_diag:
+ elemtype = TAG_DL;
+ cattr = "Bl-diag";
+ break;
case LIST_hang:
+ elemtype = TAG_DL;
+ cattr = "Bl-hang";
+ break;
case LIST_inset:
+ elemtype = TAG_DL;
+ cattr = "Bl-inset";
+ break;
case LIST_ohang:
- case LIST_tag:
- print_otag(h, TAG_DL, 2, tag);
+ elemtype = TAG_DL;
+ cattr = "Bl-ohang";
break;
+ case LIST_tag:
+ cattr = "Bl-tag";
+ if (bl->offs)
+ print_otag(h, TAG_DIV, "cswl", cattr, bl->offs);
+ print_otag(h, TAG_DL, "cswl", cattr, bl->width);
+ return 1;
case LIST_column:
- print_otag(h, TAG_TABLE, 2, tag);
+ elemtype = TAG_TABLE;
+ cattr = "Bl-column";
break;
default:
abort();
}
-
+ print_otag(h, elemtype, "cswl", cattr, bl->offs);
return 1;
}
static int
mdoc_ex_pre(MDOC_ARGS)
{
- struct htmlpair tag;
- struct tag *t;
- struct roff_node *nch;
-
if (n->prev)
- print_otag(h, TAG_BR, 0, NULL);
-
- PAIR_CLASS_INIT(&tag, "utility");
-
- print_text(h, "The");
-
- for (nch = n->child; nch != NULL; nch = nch->next) {
- assert(nch->type == ROFFT_TEXT);
-
- t = print_otag(h, TAG_B, 1, &tag);
- print_text(h, nch->string);
- print_tagq(h, t);
-
- if (nch->next == NULL)
- continue;
-
- if (nch->prev != NULL || nch->next->next != NULL) {
- h->flags |= HTML_NOSPACE;
- print_text(h, ",");
- }
-
- if (nch->next->next == NULL)
- print_text(h, "and");
- }
-
- if (n->child != NULL && n->child->next != NULL)
- print_text(h, "utilities exit\\~0");
- else
- print_text(h, "utility exits\\~0");
+ print_otag(h, TAG_BR, "");
+ return 1;
+}
- print_text(h, "on success, and\\~>0 if an error occurs.");
- return 0;
+static int
+mdoc_st_pre(MDOC_ARGS)
+{
+ print_otag(h, TAG_SPAN, "c", "St");
+ return 1;
}
static int
mdoc_em_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "emph");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_I, "c", "Em");
return 1;
}
static int
mdoc_d1_pre(MDOC_ARGS)
{
- struct htmlpair tag[2];
- struct roffsu su;
-
if (n->type != ROFFT_BLOCK)
return 1;
- SCALE_VS_INIT(&su, 0);
- bufinit(h);
- bufcat_su(h, "margin-top", &su);
- bufcat_su(h, "margin-bottom", &su);
- PAIR_STYLE_INIT(&tag[0], h);
- print_otag(h, TAG_BLOCKQUOTE, 1, tag);
+ print_otag(h, TAG_DIV, "c", "D1");
- /* BLOCKQUOTE needs a block body. */
-
- PAIR_CLASS_INIT(&tag[0], "display");
- print_otag(h, TAG_DIV, 1, tag);
-
- if (MDOC_Dl == n->tok) {
- PAIR_CLASS_INIT(&tag[0], "lit");
- print_otag(h, TAG_CODE, 1, tag);
- }
+ if (n->tok == MDOC_Dl)
+ print_otag(h, TAG_CODE, "c", "Li");
return 1;
}
@@ -1060,32 +922,19 @@ mdoc_d1_pre(MDOC_ARGS)
static int
mdoc_sx_pre(MDOC_ARGS)
{
- struct htmlpair tag[2];
-
- bufinit(h);
- bufcat(h, "#");
-
- for (n = n->child; n; ) {
- bufcat_id(h, n->string);
- if (NULL != (n = n->next))
- bufcat_id(h, " ");
- }
-
- PAIR_CLASS_INIT(&tag[0], "link-sec");
- PAIR_HREF_INIT(&tag[1], h->buf);
+ char *id;
- print_otag(h, TAG_I, 1, tag);
- print_otag(h, TAG_A, 2, tag);
+ id = make_id(n);
+ print_otag(h, TAG_A, "chR", "Sx", id);
+ free(id);
return 1;
}
static int
mdoc_bd_pre(MDOC_ARGS)
{
- struct htmlpair tag[2];
- int comp, sv;
+ int comp, offs, sv;
struct roff_node *nn;
- struct roffsu su;
if (n->type == ROFFT_HEAD)
return 0;
@@ -1109,27 +958,24 @@ mdoc_bd_pre(MDOC_ARGS)
if (n->norm->Bd.offs == NULL ||
! strcmp(n->norm->Bd.offs, "left"))
- SCALE_HS_INIT(&su, 0);
+ offs = 0;
else if ( ! strcmp(n->norm->Bd.offs, "indent"))
- SCALE_HS_INIT(&su, INDENT);
+ offs = INDENT;
else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
- SCALE_HS_INIT(&su, INDENT * 2);
+ offs = INDENT * 2;
else
- a2width(n->norm->Bd.offs, &su);
+ offs = -1;
- bufinit(h);
- bufcat_su(h, "margin-left", &su);
- PAIR_STYLE_INIT(&tag[0], h);
+ if (offs == -1)
+ print_otag(h, TAG_DIV, "cswl", "Bd", n->norm->Bd.offs);
+ else
+ print_otag(h, TAG_DIV, "cshl", "Bd", offs);
- if (DISP_unfilled != n->norm->Bd.type &&
- DISP_literal != n->norm->Bd.type) {
- PAIR_CLASS_INIT(&tag[1], "display");
- print_otag(h, TAG_DIV, 2, tag);
+ if (n->norm->Bd.type != DISP_unfilled &&
+ n->norm->Bd.type != DISP_literal)
return 1;
- }
- PAIR_CLASS_INIT(&tag[1], "lit display");
- print_otag(h, TAG_PRE, 2, tag);
+ print_otag(h, TAG_PRE, "c", "Li");
/* This can be recursive: save & set our literal state. */
@@ -1158,7 +1004,7 @@ mdoc_bd_pre(MDOC_ARGS)
break;
}
if (h->flags & HTML_NONEWLINE ||
- (nn->next && ! (nn->next->flags & MDOC_LINE)))
+ (nn->next && ! (nn->next->flags & NODE_LINE)))
continue;
else if (nn->next)
print_text(h, "\n");
@@ -1175,28 +1021,20 @@ mdoc_bd_pre(MDOC_ARGS)
static int
mdoc_pa_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "file");
- print_otag(h, TAG_I, 1, &tag);
+ print_otag(h, TAG_I, "c", "Pa");
return 1;
}
static int
mdoc_ad_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "addr");
- print_otag(h, TAG_I, 1, &tag);
+ print_otag(h, TAG_I, "c", "Ad");
return 1;
}
static int
mdoc_an_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
if (n->norm->An.auth == AUTH_split) {
h->flags &= ~HTML_NOSPLIT;
h->flags |= HTML_SPLIT;
@@ -1209,54 +1047,41 @@ mdoc_an_pre(MDOC_ARGS)
}
if (h->flags & HTML_SPLIT)
- print_otag(h, TAG_BR, 0, NULL);
+ print_otag(h, TAG_BR, "");
if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT))
h->flags |= HTML_SPLIT;
- PAIR_CLASS_INIT(&tag, "author");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_SPAN, "c", "An");
return 1;
}
static int
mdoc_cd_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
synopsis_pre(h, n);
- PAIR_CLASS_INIT(&tag, "config");
- print_otag(h, TAG_B, 1, &tag);
+ print_otag(h, TAG_B, "c", "Cd");
return 1;
}
static int
mdoc_dv_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "define");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_CODE, "c", "Dv");
return 1;
}
static int
mdoc_ev_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "env");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_CODE, "c", "Ev");
return 1;
}
static int
mdoc_er_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "errno");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_CODE, "c", "Er");
return 1;
}
@@ -1264,17 +1089,15 @@ static int
mdoc_fa_pre(MDOC_ARGS)
{
const struct roff_node *nn;
- struct htmlpair tag;
struct tag *t;
- PAIR_CLASS_INIT(&tag, "farg");
if (n->parent->tok != MDOC_Fo) {
- print_otag(h, TAG_I, 1, &tag);
+ print_otag(h, TAG_VAR, "c", "Fa");
return 1;
}
for (nn = n->child; nn; nn = nn->next) {
- t = print_otag(h, TAG_I, 1, &tag);
+ t = print_otag(h, TAG_VAR, "c", "Fa");
print_text(h, nn->string);
print_tagq(h, t);
if (nn->next) {
@@ -1294,11 +1117,8 @@ mdoc_fa_pre(MDOC_ARGS)
static int
mdoc_fd_pre(MDOC_ARGS)
{
- struct htmlpair tag[2];
- char buf[BUFSIZ];
- size_t sz;
- int i;
struct tag *t;
+ char *buf, *cp;
synopsis_pre(h, n);
@@ -1308,43 +1128,29 @@ mdoc_fd_pre(MDOC_ARGS)
assert(n->type == ROFFT_TEXT);
if (strcmp(n->string, "#include")) {
- PAIR_CLASS_INIT(&tag[0], "macro");
- print_otag(h, TAG_B, 1, tag);
+ print_otag(h, TAG_B, "c", "Fd");
return 1;
}
- PAIR_CLASS_INIT(&tag[0], "includes");
- print_otag(h, TAG_B, 1, tag);
+ print_otag(h, TAG_B, "c", "In");
print_text(h, n->string);
if (NULL != (n = n->next)) {
assert(n->type == ROFFT_TEXT);
- /*
- * XXX This is broken and not easy to fix.
- * When using -Oincludes, truncation may occur.
- * Dynamic allocation wouldn't help because
- * passing long strings to buffmt_includes()
- * does not work either.
- */
-
- strlcpy(buf, '<' == *n->string || '"' == *n->string ?
- n->string + 1 : n->string, BUFSIZ);
-
- sz = strlen(buf);
- if (sz && ('>' == buf[sz - 1] || '"' == buf[sz - 1]))
- buf[sz - 1] = '\0';
-
- PAIR_CLASS_INIT(&tag[0], "link-includes");
-
- i = 1;
if (h->base_includes) {
- buffmt_includes(h, buf);
- PAIR_HREF_INIT(&tag[i], h->buf);
- i++;
- }
+ cp = n->string;
+ if (*cp == '<' || *cp == '"')
+ cp++;
+ buf = mandoc_strdup(cp);
+ cp = strchr(buf, '\0') - 1;
+ if (cp >= buf && (*cp == '>' || *cp == '"'))
+ *cp = '\0';
+ t = print_otag(h, TAG_A, "chI", "In", buf);
+ free(buf);
+ } else
+ t = print_otag(h, TAG_A, "c", "In");
- t = print_otag(h, TAG_A, i, tag);
print_text(h, n->string);
print_tagq(h, t);
@@ -1362,8 +1168,6 @@ mdoc_fd_pre(MDOC_ARGS)
static int
mdoc_vt_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
if (n->type == ROFFT_BLOCK) {
synopsis_pre(h, n);
return 1;
@@ -1372,19 +1176,15 @@ mdoc_vt_pre(MDOC_ARGS)
} else if (n->type == ROFFT_HEAD)
return 0;
- PAIR_CLASS_INIT(&tag, "type");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_VAR, "c", "Vt");
return 1;
}
static int
mdoc_ft_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
synopsis_pre(h, n);
- PAIR_CLASS_INIT(&tag, "ftype");
- print_otag(h, TAG_I, 1, &tag);
+ print_otag(h, TAG_VAR, "c", "Ft");
return 1;
}
@@ -1392,12 +1192,11 @@ static int
mdoc_fn_pre(MDOC_ARGS)
{
struct tag *t;
- struct htmlpair tag[2];
char nbuf[BUFSIZ];
const char *sp, *ep;
- int sz, i, pretty;
+ int sz, pretty;
- pretty = MDOC_SYNPRETTY & n->flags;
+ pretty = NODE_SYNPRETTY & n->flags;
synopsis_pre(h, n);
/* Split apart into type and name. */
@@ -1406,8 +1205,7 @@ mdoc_fn_pre(MDOC_ARGS)
ep = strchr(sp, ' ');
if (NULL != ep) {
- PAIR_CLASS_INIT(&tag[0], "ftype");
- t = print_otag(h, TAG_I, 1, tag);
+ t = print_otag(h, TAG_VAR, "c", "Ft");
while (ep) {
sz = MIN((int)(ep - sp), BUFSIZ - 1);
@@ -1420,25 +1218,7 @@ mdoc_fn_pre(MDOC_ARGS)
print_tagq(h, t);
}
- PAIR_CLASS_INIT(&tag[0], "fname");
-
- /*
- * FIXME: only refer to IDs that we know exist.
- */
-
-#if 0
- if (MDOC_SYNPRETTY & n->flags) {
- nbuf[0] = '\0';
- html_idcat(nbuf, sp, BUFSIZ);
- PAIR_ID_INIT(&tag[1], nbuf);
- } else {
- strlcpy(nbuf, "#", BUFSIZ);
- html_idcat(nbuf, sp, BUFSIZ);
- PAIR_HREF_INIT(&tag[1], nbuf);
- }
-#endif
-
- t = print_otag(h, TAG_B, 1, tag);
+ t = print_otag(h, TAG_B, "c", "Fn");
if (sp)
print_text(h, sp);
@@ -1449,16 +1229,12 @@ mdoc_fn_pre(MDOC_ARGS)
print_text(h, "(");
h->flags |= HTML_NOSPACE;
- PAIR_CLASS_INIT(&tag[0], "farg");
- bufinit(h);
- bufcat_style(h, "white-space", "nowrap");
- PAIR_STYLE_INIT(&tag[1], h);
-
for (n = n->child->next; n; n = n->next) {
- i = 1;
- if (MDOC_SYNPRETTY & n->flags)
- i = 2;
- t = print_otag(h, TAG_I, i, tag);
+ if (NODE_SYNPRETTY & n->flags)
+ t = print_otag(h, TAG_VAR, "css?", "Fa",
+ "white-space", "nowrap");
+ else
+ t = print_otag(h, TAG_VAR, "c", "Fa");
print_text(h, n->string);
print_tagq(h, t);
if (n->next) {
@@ -1514,7 +1290,6 @@ static int
mdoc_sp_pre(MDOC_ARGS)
{
struct roffsu su;
- struct htmlpair tag;
SCALE_VS_INIT(&su, 1);
@@ -1528,10 +1303,7 @@ mdoc_sp_pre(MDOC_ARGS)
} else
su.scale = 0.0;
- bufinit(h);
- bufcat_su(h, "height", &su);
- PAIR_STYLE_INIT(&tag, h);
- print_otag(h, TAG_DIV, 1, &tag);
+ print_otag(h, TAG_DIV, "suh", &su);
/* So the div isn't empty: */
print_text(h, "\\~");
@@ -1543,17 +1315,12 @@ mdoc_sp_pre(MDOC_ARGS)
static int
mdoc_lk_pre(MDOC_ARGS)
{
- struct htmlpair tag[2];
-
if (NULL == (n = n->child))
return 0;
assert(n->type == ROFFT_TEXT);
- PAIR_CLASS_INIT(&tag[0], "link-ext");
- PAIR_HREF_INIT(&tag[1], n->string);
-
- print_otag(h, TAG_A, 2, tag);
+ print_otag(h, TAG_A, "ch", "Lk", n->string);
if (NULL == n->next)
print_text(h, n->string);
@@ -1567,22 +1334,17 @@ mdoc_lk_pre(MDOC_ARGS)
static int
mdoc_mt_pre(MDOC_ARGS)
{
- struct htmlpair tag[2];
struct tag *t;
-
- PAIR_CLASS_INIT(&tag[0], "link-mail");
+ char *cp;
for (n = n->child; n; n = n->next) {
assert(n->type == ROFFT_TEXT);
- bufinit(h);
- bufcat(h, "mailto:");
- bufcat(h, n->string);
-
- PAIR_HREF_INIT(&tag[1], h->buf);
- t = print_otag(h, TAG_A, 2, tag);
+ 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;
@@ -1591,7 +1353,6 @@ mdoc_mt_pre(MDOC_ARGS)
static int
mdoc_fo_pre(MDOC_ARGS)
{
- struct htmlpair tag;
struct tag *t;
if (n->type == ROFFT_BODY) {
@@ -1608,8 +1369,7 @@ mdoc_fo_pre(MDOC_ARGS)
return 0;
assert(n->child->string);
- PAIR_CLASS_INIT(&tag, "fname");
- t = print_otag(h, TAG_B, 1, &tag);
+ t = print_otag(h, TAG_B, "c", "Fn");
print_text(h, n->child->string);
print_tagq(h, t);
return 0;
@@ -1631,13 +1391,9 @@ static int
mdoc_in_pre(MDOC_ARGS)
{
struct tag *t;
- struct htmlpair tag[2];
- int i;
synopsis_pre(h, n);
-
- PAIR_CLASS_INIT(&tag[0], "includes");
- print_otag(h, TAG_B, 1, tag);
+ print_otag(h, TAG_B, "c", "In");
/*
* The first argument of the `In' gets special treatment as
@@ -1646,7 +1402,7 @@ mdoc_in_pre(MDOC_ARGS)
* of no children.
*/
- if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags)
+ if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags)
print_text(h, "#include");
print_text(h, "<");
@@ -1655,16 +1411,10 @@ mdoc_in_pre(MDOC_ARGS)
if (NULL != (n = n->child)) {
assert(n->type == ROFFT_TEXT);
- PAIR_CLASS_INIT(&tag[0], "link-includes");
-
- i = 1;
- if (h->base_includes) {
- buffmt_includes(h, n->string);
- PAIR_HREF_INIT(&tag[i], h->buf);
- i++;
- }
-
- t = print_otag(h, TAG_A, i, tag);
+ if (h->base_includes)
+ t = print_otag(h, TAG_A, "chI", "In", n->string);
+ else
+ t = print_otag(h, TAG_A, "c", "In");
print_text(h, n->string);
print_tagq(h, t);
@@ -1685,75 +1435,14 @@ mdoc_in_pre(MDOC_ARGS)
static int
mdoc_ic_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "cmd");
- print_otag(h, TAG_B, 1, &tag);
+ print_otag(h, TAG_B, "c", "Ic");
return 1;
}
static int
-mdoc_rv_pre(MDOC_ARGS)
-{
- struct htmlpair tag;
- struct tag *t;
- struct roff_node *nch;
-
- if (n->prev)
- print_otag(h, TAG_BR, 0, NULL);
-
- PAIR_CLASS_INIT(&tag, "fname");
-
- if (n->child != NULL) {
- print_text(h, "The");
-
- for (nch = n->child; nch != NULL; nch = nch->next) {
- t = print_otag(h, TAG_B, 1, &tag);
- print_text(h, nch->string);
- print_tagq(h, t);
-
- h->flags |= HTML_NOSPACE;
- print_text(h, "()");
-
- if (nch->next == NULL)
- continue;
-
- if (nch->prev != NULL || nch->next->next != NULL) {
- h->flags |= HTML_NOSPACE;
- print_text(h, ",");
- }
- if (nch->next->next == NULL)
- print_text(h, "and");
- }
-
- if (n->child != NULL && n->child->next != NULL)
- print_text(h, "functions return");
- else
- print_text(h, "function returns");
-
- print_text(h, "the value\\~0 if successful;");
- } else
- print_text(h, "Upon successful completion,"
- " the value\\~0 is returned;");
-
- print_text(h, "otherwise the value\\~\\-1 is returned"
- " and the global variable");
-
- PAIR_CLASS_INIT(&tag, "var");
- t = print_otag(h, TAG_B, 1, &tag);
- print_text(h, "errno");
- print_tagq(h, t);
- print_text(h, "is set to indicate the error.");
- return 0;
-}
-
-static int
mdoc_va_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "var");
- print_otag(h, TAG_B, 1, &tag);
+ print_otag(h, TAG_VAR, "c", "Va");
return 1;
}
@@ -1770,8 +1459,7 @@ mdoc_ap_pre(MDOC_ARGS)
static int
mdoc_bf_pre(MDOC_ARGS)
{
- struct htmlpair tag[2];
- struct roffsu su;
+ const char *cattr;
if (n->type == ROFFT_HEAD)
return 0;
@@ -1779,35 +1467,27 @@ mdoc_bf_pre(MDOC_ARGS)
return 1;
if (FONT_Em == n->norm->Bf.font)
- PAIR_CLASS_INIT(&tag[0], "emph");
+ cattr = "Em";
else if (FONT_Sy == n->norm->Bf.font)
- PAIR_CLASS_INIT(&tag[0], "symb");
+ cattr = "Sy";
else if (FONT_Li == n->norm->Bf.font)
- PAIR_CLASS_INIT(&tag[0], "lit");
+ cattr = "Li";
else
- PAIR_CLASS_INIT(&tag[0], "none");
+ cattr = "No";
/*
* We want this to be inline-formatted, but needs to be div to
* accept block children.
*/
- bufinit(h);
- bufcat_style(h, "display", "inline");
- SCALE_HS_INIT(&su, 1);
- /* Needs a left-margin for spacing. */
- bufcat_su(h, "margin-left", &su);
- PAIR_STYLE_INIT(&tag[1], h);
- print_otag(h, TAG_DIV, 2, tag);
+
+ print_otag(h, TAG_DIV, "css?hl", cattr, "display", "inline", 1);
return 1;
}
static int
mdoc_ms_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "symb");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_B, "c", "Ms");
return 1;
}
@@ -1823,154 +1503,116 @@ static void
mdoc_pf_post(MDOC_ARGS)
{
- if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
+ if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
h->flags |= HTML_NOSPACE;
}
static int
mdoc_rs_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
if (n->type != ROFFT_BLOCK)
return 1;
if (n->prev && SEC_SEE_ALSO == n->sec)
print_paragraph(h);
- PAIR_CLASS_INIT(&tag, "ref");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_CITE, "c", "Rs");
return 1;
}
static int
mdoc_no_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "none");
- print_otag(h, TAG_CODE, 1, &tag);
+ print_otag(h, TAG_SPAN, "c", "No");
return 1;
}
static int
mdoc_li_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "lit");
- print_otag(h, TAG_CODE, 1, &tag);
+ print_otag(h, TAG_CODE, "c", "Li");
return 1;
}
static int
mdoc_sy_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- PAIR_CLASS_INIT(&tag, "symb");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_B, "c", "Sy");
return 1;
}
static int
-mdoc_bt_pre(MDOC_ARGS)
-{
-
- print_text(h, "is currently in beta test.");
- return 0;
-}
-
-static int
-mdoc_ud_pre(MDOC_ARGS)
-{
-
- print_text(h, "currently under development.");
- return 0;
-}
-
-static int
mdoc_lb_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
- if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev)
- print_otag(h, TAG_BR, 0, NULL);
+ if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev)
+ print_otag(h, TAG_BR, "");
- PAIR_CLASS_INIT(&tag, "lib");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_SPAN, "c", "Lb");
return 1;
}
static int
mdoc__x_pre(MDOC_ARGS)
{
- struct htmlpair tag[2];
- enum htmltag t;
+ const char *cattr;
+ enum htmltag t;
t = TAG_SPAN;
switch (n->tok) {
case MDOC__A:
- PAIR_CLASS_INIT(&tag[0], "ref-auth");
+ cattr = "RsA";
if (n->prev && MDOC__A == n->prev->tok)
if (NULL == n->next || MDOC__A != n->next->tok)
print_text(h, "and");
break;
case MDOC__B:
- PAIR_CLASS_INIT(&tag[0], "ref-book");
t = TAG_I;
+ cattr = "RsB";
break;
case MDOC__C:
- PAIR_CLASS_INIT(&tag[0], "ref-city");
+ cattr = "RsC";
break;
case MDOC__D:
- PAIR_CLASS_INIT(&tag[0], "ref-date");
+ cattr = "RsD";
break;
case MDOC__I:
- PAIR_CLASS_INIT(&tag[0], "ref-issue");
t = TAG_I;
+ cattr = "RsI";
break;
case MDOC__J:
- PAIR_CLASS_INIT(&tag[0], "ref-jrnl");
t = TAG_I;
+ cattr = "RsJ";
break;
case MDOC__N:
- PAIR_CLASS_INIT(&tag[0], "ref-num");
+ cattr = "RsN";
break;
case MDOC__O:
- PAIR_CLASS_INIT(&tag[0], "ref-opt");
+ cattr = "RsO";
break;
case MDOC__P:
- PAIR_CLASS_INIT(&tag[0], "ref-page");
+ cattr = "RsP";
break;
case MDOC__Q:
- PAIR_CLASS_INIT(&tag[0], "ref-corp");
+ cattr = "RsQ";
break;
case MDOC__R:
- PAIR_CLASS_INIT(&tag[0], "ref-rep");
+ cattr = "RsR";
break;
case MDOC__T:
- PAIR_CLASS_INIT(&tag[0], "ref-title");
+ cattr = "RsT";
break;
case MDOC__U:
- PAIR_CLASS_INIT(&tag[0], "link-ref");
- break;
+ print_otag(h, TAG_A, "ch", "RsU", n->child->string);
+ return 1;
case MDOC__V:
- PAIR_CLASS_INIT(&tag[0], "ref-vol");
+ cattr = "RsV";
break;
default:
abort();
}
- if (MDOC__U != n->tok) {
- print_otag(h, t, 1, tag);
- return 1;
- }
-
- PAIR_HREF_INIT(&tag[1], n->child->string);
- print_otag(h, TAG_A, 2, tag);
-
+ print_otag(h, t, "c", cattr);
return 1;
}
@@ -2023,8 +1665,6 @@ mdoc_bk_post(MDOC_ARGS)
static int
mdoc_quote_pre(MDOC_ARGS)
{
- struct htmlpair tag;
-
if (n->type != ROFFT_BODY)
return 1;
@@ -2046,8 +1686,7 @@ mdoc_quote_pre(MDOC_ARGS)
case MDOC_Op:
print_text(h, "\\(lB");
h->flags |= HTML_NOSPACE;
- PAIR_CLASS_INIT(&tag, "opt");
- print_otag(h, TAG_SPAN, 1, &tag);
+ print_otag(h, TAG_SPAN, "c", "Op");
break;
case MDOC_En:
if (NULL == n->norm->Es ||
@@ -2068,8 +1707,7 @@ mdoc_quote_pre(MDOC_ARGS)
case MDOC_Ql:
print_text(h, "\\(oq");
h->flags |= HTML_NOSPACE;
- PAIR_CLASS_INIT(&tag, "lit");
- print_otag(h, TAG_CODE, 1, &tag);
+ print_otag(h, TAG_CODE, "c", "Li");
break;
case MDOC_So:
case MDOC_Sq:
diff --git a/usr/src/cmd/mandoc/mdoc_macro.c b/usr/src/cmd/mandoc/mdoc_macro.c
index ca959589ac..5ab9c41258 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.206 2015/10/20 02:01:32 schwarze Exp $ */
+/* $Id: mdoc_macro.c,v 1.217 2017/02/16 09:47:31 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010, 2012-2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -50,6 +50,8 @@ static int find_pending(struct roff_man *, int, int, int,
struct roff_node *);
static int lookup(struct roff_man *, int, int, int, const char *);
static int macro_or_word(MACRO_PROT_ARGS, int);
+static void break_intermediate(struct roff_node *,
+ struct roff_node *);
static int parse_rest(struct roff_man *, int, int, int *, char *);
static int rew_alt(int);
static void rew_elem(struct roff_man *, int);
@@ -216,7 +218,7 @@ mdoc_endparse(struct roff_man *mdoc)
/* Scan for open explicit scopes. */
- n = mdoc->last->flags & MDOC_VALID ?
+ n = mdoc->last->flags & NODE_VALID ?
mdoc->last->parent : mdoc->last;
for ( ; n; n = n->parent)
@@ -264,16 +266,16 @@ static void
rew_last(struct roff_man *mdoc, const struct roff_node *to)
{
- if (to->flags & MDOC_VALID)
+ if (to->flags & NODE_VALID)
return;
while (mdoc->last != to) {
mdoc_state(mdoc, mdoc->last);
- mdoc->last->flags |= MDOC_VALID | MDOC_ENDED;
+ mdoc->last->flags |= NODE_VALID | NODE_ENDED;
mdoc->last = mdoc->last->parent;
}
mdoc_state(mdoc, mdoc->last);
- mdoc->last->flags |= MDOC_VALID | MDOC_ENDED;
+ mdoc->last->flags |= NODE_VALID | NODE_ENDED;
mdoc->next = ROFF_NEXT_SIBLING;
}
@@ -292,13 +294,13 @@ rew_pending(struct roff_man *mdoc, const struct roff_node *n)
case ROFFT_HEAD:
roff_body_alloc(mdoc, n->line, n->pos,
n->tok);
- return;
+ break;
case ROFFT_BLOCK:
break;
default:
return;
}
- if ( ! (n->flags & MDOC_BROKEN))
+ if ( ! (n->flags & NODE_BROKEN))
return;
} else
n = mdoc->last;
@@ -309,7 +311,7 @@ rew_pending(struct roff_man *mdoc, const struct roff_node *n)
if (n->type == ROFFT_BLOCK ||
n->type == ROFFT_HEAD) {
- if (n->flags & MDOC_ENDED)
+ if (n->flags & NODE_ENDED)
break;
else
return;
@@ -376,6 +378,20 @@ rew_elem(struct roff_man *mdoc, int tok)
rew_last(mdoc, n);
}
+static void
+break_intermediate(struct roff_node *n, struct roff_node *breaker)
+{
+ if (n != breaker &&
+ n->type != ROFFT_BLOCK && n->type != ROFFT_HEAD &&
+ (n->type != ROFFT_BODY || n->end != ENDBODY_NOT))
+ n = n->parent;
+ while (n != breaker) {
+ if ( ! (n->flags & NODE_VALID))
+ n->flags |= NODE_BROKEN;
+ n = n->parent;
+ }
+}
+
/*
* If there is an open sub-block of the target requiring
* explicit close-out, postpone closing out the target until
@@ -388,26 +404,26 @@ find_pending(struct roff_man *mdoc, int tok, int line, int ppos,
struct roff_node *n;
int irc;
+ if (target->flags & NODE_VALID)
+ return 0;
+
irc = 0;
for (n = mdoc->last; n != NULL && n != target; n = n->parent) {
- if (n->flags & MDOC_ENDED) {
- if ( ! (n->flags & MDOC_VALID))
- n->flags |= MDOC_BROKEN;
+ if (n->flags & NODE_ENDED)
continue;
- }
if (n->type == ROFFT_BLOCK &&
mdoc_macros[n->tok].flags & MDOC_EXPLICIT) {
irc = 1;
- n->flags = MDOC_BROKEN;
+ break_intermediate(mdoc->last, target);
if (target->type == ROFFT_HEAD)
- target->flags = MDOC_ENDED;
- else if ( ! (target->flags & MDOC_ENDED)) {
+ target->flags |= NODE_ENDED;
+ else if ( ! (target->flags & NODE_ENDED)) {
mandoc_vmsg(MANDOCERR_BLK_NEST,
mdoc->parse, line, ppos,
"%s breaks %s", mdoc_macronames[tok],
mdoc_macronames[n->tok]);
mdoc_endbody_alloc(mdoc, line, ppos,
- tok, target, ENDBODY_NOSPACE);
+ tok, target);
}
}
}
@@ -444,11 +460,11 @@ dword(struct roff_man *mdoc, int line, int col, const char *p,
*/
if (d == DELIM_OPEN)
- mdoc->last->flags |= MDOC_DELIMO;
+ mdoc->last->flags |= NODE_DELIMO;
else if (d == DELIM_CLOSE &&
! (mdoc->flags & MDOC_NODELIMC) &&
mdoc->last->parent->tok != MDOC_Fd)
- mdoc->last->flags |= MDOC_DELIMC;
+ mdoc->last->flags |= NODE_DELIMC;
mdoc->flags &= ~MDOC_NODELIMC;
}
@@ -481,7 +497,7 @@ append_delims(struct roff_man *mdoc, int line, int *pos, char *buf)
*/
if (mandoc_eos(p, strlen(p)))
- mdoc->last->flags |= MDOC_EOS;
+ mdoc->last->flags |= NODE_EOS;
}
}
@@ -549,30 +565,46 @@ blk_exp_close(MACRO_PROT_ARGS)
break;
}
+ /* Search backwards for the beginning of our own body. */
+
+ atok = rew_alt(tok);
+ body = NULL;
+ for (n = mdoc->last; n; n = n->parent) {
+ if (n->flags & NODE_ENDED || n->tok != atok ||
+ n->type != ROFFT_BODY || n->end != ENDBODY_NOT)
+ continue;
+ body = n;
+ break;
+ }
+
/*
* Search backwards for beginnings of blocks,
* both of our own and of pending sub-blocks.
*/
- atok = rew_alt(tok);
- body = endbody = itblk = later = NULL;
+ endbody = itblk = later = NULL;
for (n = mdoc->last; n; n = n->parent) {
- if (n->flags & MDOC_ENDED) {
- if ( ! (n->flags & MDOC_VALID))
- n->flags |= MDOC_BROKEN;
+ if (n->flags & NODE_ENDED)
continue;
- }
- /* Remember the start of our own body. */
+ /*
+ * Mismatching end macros can never break anything
+ * and we only care about the breaking of BLOCKs.
+ */
- if (n->type == ROFFT_BODY && atok == n->tok) {
- if (n->end == ENDBODY_NOT)
- body = n;
+ if (body == NULL || n->type != ROFFT_BLOCK)
continue;
- }
- if (n->type != ROFFT_BLOCK || n->tok == MDOC_Nm)
+ /*
+ * SYNOPSIS name blocks can not be broken themselves,
+ * but they do get broken together with a broken child.
+ */
+
+ if (n->tok == MDOC_Nm) {
+ if (later != NULL)
+ n->flags |= NODE_BROKEN | NODE_ENDED;
continue;
+ }
if (n->tok == MDOC_It) {
itblk = n;
@@ -580,7 +612,6 @@ blk_exp_close(MACRO_PROT_ARGS)
}
if (atok == n->tok) {
- assert(body);
/*
* Found the start of our own block.
@@ -606,10 +637,10 @@ blk_exp_close(MACRO_PROT_ARGS)
mdoc_macronames[later->tok]);
endbody = mdoc_endbody_alloc(mdoc, line, ppos,
- atok, body, ENDBODY_SPACE);
+ atok, body);
if (tok == MDOC_El)
- itblk->flags |= MDOC_ENDED | MDOC_BROKEN;
+ itblk->flags |= NODE_ENDED | NODE_BROKEN;
/*
* If a block closing macro taking arguments
@@ -622,16 +653,23 @@ blk_exp_close(MACRO_PROT_ARGS)
break;
}
- /* Explicit blocks close out description lines. */
+ /*
+ * Explicit blocks close out description lines, but
+ * even those can get broken together with a child.
+ */
if (n->tok == MDOC_Nd) {
- rew_last(mdoc, n);
+ if (later != NULL)
+ n->flags |= NODE_BROKEN | NODE_ENDED;
+ else
+ rew_last(mdoc, n);
continue;
}
/* Breaking an open sub block. */
- n->flags |= MDOC_BROKEN;
+ break_intermediate(mdoc->last, body);
+ n->flags |= NODE_BROKEN;
if (later == NULL)
later = n;
}
@@ -639,8 +677,6 @@ blk_exp_close(MACRO_PROT_ARGS)
if (body == NULL) {
mandoc_msg(MANDOCERR_BLK_NOTOPEN, mdoc->parse,
line, ppos, mdoc_macronames[tok]);
- if (later != NULL)
- later->flags &= ~MDOC_BROKEN;
if (maxargs && endbody == NULL) {
/*
* Stray .Ec without previous .Eo:
@@ -697,15 +733,14 @@ blk_exp_close(MACRO_PROT_ARGS)
}
if (n != NULL) {
- if (ntok != TOKEN_NONE && n->flags & MDOC_BROKEN) {
+ pending = 0;
+ if (ntok != TOKEN_NONE && n->flags & NODE_BROKEN) {
target = n;
do
target = target->parent;
- while ( ! (target->flags & MDOC_ENDED));
- pending = find_pending(mdoc, ntok, line, ppos,
- target);
- } else
- pending = 0;
+ while ( ! (target->flags & NODE_ENDED));
+ pending = find_pending(mdoc, ntok, line, ppos, target);
+ }
if ( ! pending)
rew_pending(mdoc, n);
}
@@ -760,7 +795,7 @@ in_line(MACRO_PROT_ARGS)
if (ac == ARGS_EOLN) {
if (d == DELIM_OPEN)
- mdoc->last->flags &= ~MDOC_DELIMO;
+ mdoc->last->flags &= ~NODE_DELIMO;
break;
}
@@ -854,7 +889,7 @@ in_line(MACRO_PROT_ARGS)
*/
if (firstarg && d == DELIM_CLOSE && !nc)
- mdoc->last->flags &= ~MDOC_DELIMC;
+ mdoc->last->flags &= ~NODE_DELIMC;
firstarg = 0;
/*
@@ -917,9 +952,9 @@ blk_full(MACRO_PROT_ARGS)
blk = NULL;
for (n = mdoc->last; n != NULL; n = n->parent) {
- if (n->flags & MDOC_ENDED) {
- if ( ! (n->flags & MDOC_VALID))
- n->flags |= MDOC_BROKEN;
+ if (n->flags & NODE_ENDED) {
+ if ( ! (n->flags & NODE_VALID))
+ n->flags |= NODE_BROKEN;
continue;
}
if (n->type != ROFFT_BLOCK)
@@ -978,7 +1013,7 @@ blk_full(MACRO_PROT_ARGS)
/* Close out prior implicit scopes. */
- rew_last(mdoc, n);
+ rew_pending(mdoc, n);
}
/* Skip items outside lists. */
@@ -1120,7 +1155,7 @@ blk_full(MACRO_PROT_ARGS)
break;
}
- if (blk->flags & MDOC_VALID)
+ if (blk->flags & NODE_VALID)
return;
if (head == NULL)
head = roff_head_alloc(mdoc, line, ppos, tok);
@@ -1455,11 +1490,11 @@ phrase_ta(MACRO_PROT_ARGS)
body = NULL;
for (n = mdoc->last; n != NULL; n = n->parent) {
- if (n->flags & MDOC_ENDED)
+ if (n->flags & NODE_ENDED)
continue;
if (n->tok == MDOC_It && n->type == ROFFT_BODY)
body = n;
- if (n->tok == MDOC_Bl)
+ if (n->tok == MDOC_Bl && n->end == ENDBODY_NOT)
break;
}
diff --git a/usr/src/cmd/mandoc/mdoc_man.c b/usr/src/cmd/mandoc/mdoc_man.c
index ab24531349..88d39370e8 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.96 2016/01/08 17:48:09 schwarze Exp $ */
+/* $Id: mdoc_man.c,v 1.104 2017/02/17 19:15:41 schwarze Exp $ */
/*
- * Copyright (c) 2011-2016 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011-2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -79,7 +79,6 @@ static int pre_bf(DECL_ARGS);
static int pre_bk(DECL_ARGS);
static int pre_bl(DECL_ARGS);
static int pre_br(DECL_ARGS);
-static int pre_bx(DECL_ARGS);
static int pre_dl(DECL_ARGS);
static int pre_en(DECL_ARGS);
static int pre_enc(DECL_ARGS);
@@ -103,14 +102,12 @@ static int pre_no(DECL_ARGS);
static int pre_ns(DECL_ARGS);
static int pre_pp(DECL_ARGS);
static int pre_rs(DECL_ARGS);
-static int pre_rv(DECL_ARGS);
static int pre_sm(DECL_ARGS);
static int 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 int pre_vt(DECL_ARGS);
-static int pre_ux(DECL_ARGS);
static int pre_xr(DECL_ARGS);
static void print_word(const char *);
static void print_line(const char *, int);
@@ -158,7 +155,7 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
{ NULL, pre_ft, post_font, NULL, NULL }, /* Ot */
{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
- { NULL, pre_rv, NULL, NULL, NULL }, /* Rv */
+ { NULL, pre_ex, NULL, NULL, NULL }, /* Rv */
{ NULL, NULL, NULL, NULL, NULL }, /* St */
{ NULL, pre_em, post_font, NULL, NULL }, /* Va */
{ NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
@@ -182,8 +179,8 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
- { NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
- { NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
+ { NULL, NULL, NULL, NULL, NULL }, /* Bsx */
+ { NULL, NULL, NULL, NULL, NULL }, /* Bx */
{ NULL, pre_skip, NULL, NULL, NULL }, /* Db */
{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
{ cond_body, pre_enc, post_enc, "\\(Lq", "\\(Rq" }, /* Do */
@@ -192,12 +189,12 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
{ NULL, pre_em, post_font, NULL, NULL }, /* Em */
{ cond_body, pre_eo, post_eo, NULL, NULL }, /* Eo */
- { NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
+ { NULL, NULL, NULL, NULL, NULL }, /* Fx */
{ NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
{ NULL, pre_no, NULL, NULL, NULL }, /* No */
{ NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
- { NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */
- { NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */
+ { NULL, NULL, NULL, NULL, NULL }, /* Nx */
+ { NULL, NULL, NULL, NULL, NULL }, /* Ox */
{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
{ NULL, NULL, post_pf, NULL, NULL }, /* Pf */
{ cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
@@ -215,7 +212,7 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ NULL, pre_em, post_font, NULL, NULL }, /* Sx */
{ NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
{ NULL, pre_li, post_font, NULL, NULL }, /* Tn */
- { NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
+ { NULL, NULL, NULL, NULL, NULL }, /* Ux */
{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
{ NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
@@ -224,10 +221,10 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
- { NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
+ { NULL, NULL, NULL, NULL, NULL }, /* Bt */
{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
{ NULL, pre_em, post_font, NULL, NULL }, /* Fr */
- { NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
+ { NULL, NULL, NULL, NULL, NULL }, /* Ud */
{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
{ NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
{ NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
@@ -238,7 +235,7 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ NULL, NULL, post_percent, NULL, NULL }, /* %C */
{ NULL, pre_skip, NULL, NULL, NULL }, /* Es */
{ cond_body, pre_en, post_en, NULL, NULL }, /* En */
- { NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
+ { NULL, NULL, NULL, NULL, NULL }, /* Dx */
{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
{ NULL, pre_br, NULL, NULL, NULL }, /* br */
{ NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
@@ -575,17 +572,20 @@ print_node(DECL_ARGS)
struct roff_node *sub;
int cond, do_sub;
+ if (n->flags & NODE_NOPRT)
+ return;
+
/*
* Break the line if we were parsed subsequent the current node.
* This makes the page structure be more consistent.
*/
- if (MMAN_spc & outflags && MDOC_LINE & n->flags)
+ if (MMAN_spc & outflags && NODE_LINE & n->flags)
outflags |= MMAN_nl;
act = NULL;
cond = 0;
do_sub = 1;
- n->flags &= ~MDOC_ENDED;
+ n->flags &= ~NODE_ENDED;
if (n->type == ROFFT_TEXT) {
/*
@@ -598,10 +598,14 @@ print_node(DECL_ARGS)
printf("\\&");
outflags &= ~MMAN_spc;
}
- if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMC))
+ if (n->flags & NODE_DELIMC)
+ outflags &= ~(MMAN_spc | MMAN_spc_force);
+ else if (outflags & MMAN_Sm)
outflags |= MMAN_spc_force;
print_word(n->string);
- if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMO))
+ if (n->flags & NODE_DELIMO)
+ outflags &= ~(MMAN_spc | MMAN_spc_force);
+ else if (outflags & MMAN_Sm)
outflags |= MMAN_spc;
} else {
/*
@@ -627,17 +631,14 @@ print_node(DECL_ARGS)
/*
* Lastly, conditionally run the post-node handler.
*/
- if (MDOC_ENDED & n->flags)
+ if (NODE_ENDED & n->flags)
return;
if (cond && act->post)
(*act->post)(meta, n);
if (ENDBODY_NOT != n->end)
- n->body->flags |= MDOC_ENDED;
-
- if (ENDBODY_NOSPACE == n->end)
- outflags &= ~(MMAN_spc | MMAN_nl);
+ n->body->flags |= NODE_ENDED;
}
static int
@@ -682,36 +683,8 @@ post_enc(DECL_ARGS)
static int
pre_ex(DECL_ARGS)
{
- struct roff_node *nch;
-
outflags |= MMAN_br | MMAN_nl;
-
- print_word("The");
-
- for (nch = n->child; nch != NULL; nch = nch->next) {
- font_push('B');
- print_word(nch->string);
- font_pop();
-
- if (nch->next == NULL)
- continue;
-
- if (nch->prev != NULL || nch->next->next != NULL) {
- outflags &= ~MMAN_spc;
- print_word(",");
- }
- if (nch->next->next == NULL)
- print_word("and");
- }
-
- if (n->child != NULL && n->child->next != NULL)
- print_word("utilities exit\\~0");
- else
- print_word("utility exits\\~0");
-
- print_word("on success, and\\~>0 if an error occurs.");
- outflags |= MMAN_nl;
- return 0;
+ return 1;
}
static void
@@ -744,8 +717,7 @@ pre__t(DECL_ARGS)
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T) {
- print_word("");
- putchar('\"');
+ print_word("\\(lq");
outflags &= ~MMAN_spc;
} else
font_push('I');
@@ -759,8 +731,7 @@ post__t(DECL_ARGS)
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T) {
outflags &= ~MMAN_spc;
- print_word("");
- putchar('\"');
+ print_word("\\(rq");
} else
font_pop();
post_percent(meta, n);
@@ -805,7 +776,7 @@ static void
pre_syn(const struct roff_node *n)
{
- if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
+ if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
return;
if (n->prev->tok == n->tok &&
@@ -1051,26 +1022,6 @@ pre_br(DECL_ARGS)
}
static int
-pre_bx(DECL_ARGS)
-{
-
- n = n->child;
- if (n) {
- print_word(n->string);
- outflags &= ~MMAN_spc;
- n = n->next;
- }
- print_word("BSD");
- if (NULL == n)
- return 0;
- outflags &= ~MMAN_spc;
- print_word("-");
- outflags &= ~MMAN_spc;
- print_word(n->string);
- return 0;
-}
-
-static int
pre_dl(DECL_ARGS)
{
@@ -1173,7 +1124,7 @@ pre_fa(DECL_ARGS)
while (NULL != n) {
font_push('I');
- if (am_Fa || MDOC_SYNPRETTY & n->flags)
+ if (am_Fa || NODE_SYNPRETTY & n->flags)
outflags |= MMAN_nbrword;
print_node(meta, n);
font_pop();
@@ -1227,7 +1178,7 @@ post_fl(DECL_ARGS)
if (!(n->child != NULL ||
n->next == NULL ||
n->next->type == ROFFT_TEXT ||
- n->next->flags & MDOC_LINE))
+ n->next->flags & NODE_LINE))
outflags &= ~MMAN_spc;
}
@@ -1241,7 +1192,7 @@ pre_fn(DECL_ARGS)
if (NULL == n)
return 0;
- if (MDOC_SYNPRETTY & n->flags)
+ if (NODE_SYNPRETTY & n->flags)
print_block(".HP 4n", MMAN_nl);
font_push('B');
@@ -1262,7 +1213,7 @@ post_fn(DECL_ARGS)
{
print_word(")");
- if (MDOC_SYNPRETTY & n->flags) {
+ if (NODE_SYNPRETTY & n->flags) {
print_word(";");
outflags |= MMAN_PP;
}
@@ -1279,7 +1230,7 @@ pre_fo(DECL_ARGS)
case ROFFT_HEAD:
if (n->child == NULL)
return 0;
- if (MDOC_SYNPRETTY & n->flags)
+ if (NODE_SYNPRETTY & n->flags)
print_block(".HP 4n", MMAN_nl);
font_push('B');
break;
@@ -1324,7 +1275,7 @@ static int
pre_in(DECL_ARGS)
{
- if (MDOC_SYNPRETTY & n->flags) {
+ if (NODE_SYNPRETTY & n->flags) {
pre_syn(n);
font_push('B');
print_word("#include <");
@@ -1341,7 +1292,7 @@ static void
post_in(DECL_ARGS)
{
- if (MDOC_SYNPRETTY & n->flags) {
+ if (NODE_SYNPRETTY & n->flags) {
outflags &= ~MMAN_spc;
print_word(">");
font_pop();
@@ -1562,7 +1513,7 @@ pre_nm(DECL_ARGS)
}
if (n->type != ROFFT_ELEM && n->type != ROFFT_HEAD)
return 1;
- name = n->child ? n->child->string : meta->name;
+ name = n->child == NULL ? NULL : n->child->string;
if (NULL == name)
return 0;
if (n->type == ROFFT_HEAD) {
@@ -1573,8 +1524,6 @@ pre_nm(DECL_ARGS)
outflags |= MMAN_nl;
}
font_push('B');
- if (NULL == n->child)
- print_word(meta->name);
return 1;
}
@@ -1588,7 +1537,7 @@ post_nm(DECL_ARGS)
break;
case ROFFT_HEAD:
case ROFFT_ELEM:
- if (n->child != NULL || meta->name != NULL)
+ if (n->child != NULL && n->child->string != NULL)
font_pop();
break;
default:
@@ -1616,7 +1565,7 @@ static void
post_pf(DECL_ARGS)
{
- if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
+ if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
outflags &= ~MMAN_spc;
}
@@ -1643,57 +1592,6 @@ pre_rs(DECL_ARGS)
}
static int
-pre_rv(DECL_ARGS)
-{
- struct roff_node *nch;
-
- outflags |= MMAN_br | MMAN_nl;
-
- if (n->child != NULL) {
- print_word("The");
-
- for (nch = n->child; nch != NULL; nch = nch->next) {
- font_push('B');
- print_word(nch->string);
- font_pop();
-
- outflags &= ~MMAN_spc;
- print_word("()");
-
- if (nch->next == NULL)
- continue;
-
- if (nch->prev != NULL || nch->next->next != NULL) {
- outflags &= ~MMAN_spc;
- print_word(",");
- }
- if (nch->next->next == NULL)
- print_word("and");
- }
-
- if (n->child != NULL && n->child->next != NULL)
- print_word("functions return");
- else
- print_word("function returns");
-
- print_word("the value\\~0 if successful;");
- } else
- print_word("Upon successful completion, "
- "the value\\~0 is returned;");
-
- print_word("otherwise the value\\~\\-1 is returned"
- " and the global variable");
-
- font_push('I');
- print_word("errno");
- font_pop();
-
- print_word("is set to indicate the error.");
- outflags |= MMAN_nl;
- return 0;
-}
-
-static int
pre_skip(DECL_ARGS)
{
@@ -1748,7 +1646,7 @@ static int
pre_vt(DECL_ARGS)
{
- if (MDOC_SYNPRETTY & n->flags) {
+ if (NODE_SYNPRETTY & n->flags) {
switch (n->type) {
case ROFFT_BLOCK:
pre_syn(n);
@@ -1767,7 +1665,7 @@ static void
post_vt(DECL_ARGS)
{
- if (n->flags & MDOC_SYNPRETTY && n->type != ROFFT_BODY)
+ if (n->flags & NODE_SYNPRETTY && n->type != ROFFT_BODY)
return;
font_pop();
}
@@ -1789,16 +1687,3 @@ pre_xr(DECL_ARGS)
print_word(")");
return 0;
}
-
-static int
-pre_ux(DECL_ARGS)
-{
-
- print_word(manacts[n->tok].prefix);
- if (NULL == n->child)
- return 0;
- outflags &= ~MMAN_spc;
- print_word("\\ ");
- outflags &= ~MMAN_spc;
- return 1;
-}
diff --git a/usr/src/cmd/mandoc/mdoc_state.c b/usr/src/cmd/mandoc/mdoc_state.c
index cbd7376309..4e376ef073 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.3 2015/10/30 18:53:54 schwarze Exp $ */
+/* $Id: mdoc_state.c,v 1.4 2017/01/10 13:47:00 schwarze Exp $ */
/*
* Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
*
@@ -249,7 +249,7 @@ state_sh(STATE_ARGS)
if (n->type != ROFFT_HEAD)
return;
- if ( ! (n->flags & MDOC_VALID)) {
+ if ( ! (n->flags & NODE_VALID)) {
secname = NULL;
deroff(&secname, n);
diff --git a/usr/src/cmd/mandoc/mdoc_term.c b/usr/src/cmd/mandoc/mdoc_term.c
index e846436273..e9ea455a48 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.331 2016/01/08 17:48:09 schwarze Exp $ */
+/* $Id: mdoc_term.c,v 1.346 2017/02/17 19:15:41 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010, 2012-2016 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -80,6 +80,7 @@ static void termp_pf_post(DECL_ARGS);
static void termp_quote_post(DECL_ARGS);
static void termp_sh_post(DECL_ARGS);
static void termp_ss_post(DECL_ARGS);
+static void termp_xx_post(DECL_ARGS);
static int termp__a_pre(DECL_ARGS);
static int termp__t_pre(DECL_ARGS);
@@ -90,11 +91,10 @@ 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_bt_pre(DECL_ARGS);
-static int termp_bx_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);
@@ -113,15 +113,14 @@ static int termp_nm_pre(DECL_ARGS);
static int termp_ns_pre(DECL_ARGS);
static int termp_quote_pre(DECL_ARGS);
static int termp_rs_pre(DECL_ARGS);
-static int termp_rv_pre(DECL_ARGS);
static int termp_sh_pre(DECL_ARGS);
static int termp_skip_pre(DECL_ARGS);
static int termp_sm_pre(DECL_ARGS);
static int termp_sp_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_ud_pre(DECL_ARGS);
static int termp_vt_pre(DECL_ARGS);
static int termp_xr_pre(DECL_ARGS);
static int termp_xx_pre(DECL_ARGS);
@@ -163,7 +162,7 @@ static const struct termact termacts[MDOC_MAX] = {
{ termp_quote_pre, termp_quote_post }, /* Op */
{ termp_ft_pre, NULL }, /* Ot */
{ termp_under_pre, NULL }, /* Pa */
- { termp_rv_pre, NULL }, /* Rv */
+ { termp_ex_pre, NULL }, /* Rv */
{ NULL, NULL }, /* St */
{ termp_under_pre, NULL }, /* Va */
{ termp_vt_pre, NULL }, /* Vt */
@@ -187,22 +186,22 @@ static const struct termact termacts[MDOC_MAX] = {
{ termp_bf_pre, NULL }, /* Bf */
{ termp_quote_pre, termp_quote_post }, /* Bo */
{ termp_quote_pre, termp_quote_post }, /* Bq */
- { termp_xx_pre, NULL }, /* Bsx */
- { termp_bx_pre, NULL }, /* Bx */
+ { termp_xx_pre, termp_xx_post }, /* Bsx */
+ { NULL, NULL }, /* Bx */
{ termp_skip_pre, NULL }, /* Db */
{ NULL, NULL }, /* Dc */
{ termp_quote_pre, termp_quote_post }, /* Do */
{ termp_quote_pre, termp_quote_post }, /* Dq */
{ NULL, NULL }, /* Ec */ /* FIXME: no space */
{ NULL, NULL }, /* Ef */
- { termp_under_pre, NULL }, /* Em */
+ { termp_em_pre, NULL }, /* Em */
{ termp_eo_pre, termp_eo_post }, /* Eo */
- { termp_xx_pre, NULL }, /* Fx */
+ { termp_xx_pre, termp_xx_post }, /* Fx */
{ termp_bold_pre, NULL }, /* Ms */
{ termp_li_pre, NULL }, /* No */
{ termp_ns_pre, NULL }, /* Ns */
- { termp_xx_pre, NULL }, /* Nx */
- { termp_xx_pre, NULL }, /* Ox */
+ { termp_xx_pre, termp_xx_post }, /* Nx */
+ { termp_xx_pre, termp_xx_post }, /* Ox */
{ NULL, NULL }, /* Pc */
{ NULL, termp_pf_post }, /* Pf */
{ termp_quote_pre, termp_quote_post }, /* Po */
@@ -218,9 +217,9 @@ static const struct termact termacts[MDOC_MAX] = {
{ termp_quote_pre, termp_quote_post }, /* Sq */
{ termp_sm_pre, NULL }, /* Sm */
{ termp_under_pre, NULL }, /* Sx */
- { termp_bold_pre, NULL }, /* Sy */
+ { termp_sy_pre, NULL }, /* Sy */
{ NULL, NULL }, /* Tn */
- { termp_xx_pre, NULL }, /* Ux */
+ { termp_xx_pre, termp_xx_post }, /* Ux */
{ NULL, NULL }, /* Xc */
{ NULL, NULL }, /* Xo */
{ termp_fo_pre, termp_fo_post }, /* Fo */
@@ -229,10 +228,10 @@ static const struct termact termacts[MDOC_MAX] = {
{ NULL, NULL }, /* Oc */
{ termp_bk_pre, termp_bk_post }, /* Bk */
{ NULL, NULL }, /* Ek */
- { termp_bt_pre, NULL }, /* Bt */
+ { NULL, NULL }, /* Bt */
{ NULL, NULL }, /* Hf */
{ termp_under_pre, NULL }, /* Fr */
- { termp_ud_pre, NULL }, /* Ud */
+ { NULL, NULL }, /* Ud */
{ NULL, termp_lb_post }, /* Lb */
{ termp_sp_pre, NULL }, /* Lp */
{ termp_lk_pre, NULL }, /* Lk */
@@ -243,7 +242,7 @@ static const struct termact termacts[MDOC_MAX] = {
{ NULL, termp____post }, /* %C */
{ termp_skip_pre, NULL }, /* Es */
{ termp_quote_pre, termp_quote_post }, /* En */
- { termp_xx_pre, NULL }, /* Dx */
+ { termp_xx_pre, termp_xx_post }, /* Dx */
{ NULL, termp____post }, /* %Q */
{ termp_sp_pre, NULL }, /* br */
{ termp_sp_pre, NULL }, /* sp */
@@ -259,6 +258,7 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc)
{
struct roff_node *n;
struct termp *p;
+ size_t save_defindent;
p = (struct termp *)arg;
p->overstep = 0;
@@ -279,16 +279,20 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc)
n = n->next;
}
} else {
+ save_defindent = p->defindent;
if (p->defindent == 0)
p->defindent = 5;
term_begin(p, print_mdoc_head, print_mdoc_foot,
&mdoc->meta);
+ while (n != NULL && n->flags & NODE_NOPRT)
+ n = n->next;
if (n != NULL) {
if (n->tok != MDOC_Sh)
term_vspace(p);
print_mdoc_nodelist(p, NULL, &mdoc->meta, n);
}
term_end(p);
+ p->defindent = save_defindent;
}
}
@@ -309,10 +313,13 @@ print_mdoc_node(DECL_ARGS)
struct termpair npair;
size_t offset, rmargin;
+ if (n->flags & NODE_NOPRT)
+ return;
+
chld = 1;
offset = p->offset;
rmargin = p->rmargin;
- n->flags &= ~MDOC_ENDED;
+ n->flags &= ~NODE_ENDED;
n->prev_font = p->fonti;
memset(&npair, 0, sizeof(struct termpair));
@@ -323,7 +330,7 @@ print_mdoc_node(DECL_ARGS)
* invoked in a prior line, revert it to PREKEEP.
*/
- if (p->flags & TERMP_KEEP && n->flags & MDOC_LINE) {
+ if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
p->flags &= ~TERMP_KEEP;
p->flags |= TERMP_PREKEEP;
}
@@ -335,19 +342,19 @@ print_mdoc_node(DECL_ARGS)
switch (n->type) {
case ROFFT_TEXT:
- if (' ' == *n->string && MDOC_LINE & n->flags)
+ if (' ' == *n->string && NODE_LINE & n->flags)
term_newln(p);
- if (MDOC_DELIMC & n->flags)
+ if (NODE_DELIMC & n->flags)
p->flags |= TERMP_NOSPACE;
term_word(p, n->string);
- if (MDOC_DELIMO & n->flags)
+ if (NODE_DELIMO & n->flags)
p->flags |= TERMP_NOSPACE;
break;
case ROFFT_EQN:
- if ( ! (n->flags & MDOC_LINE))
+ if ( ! (n->flags & NODE_LINE))
p->flags |= TERMP_NOSPACE;
term_eqn(p, n->eqn);
- if (n->next != NULL && ! (n->next->flags & MDOC_LINE))
+ if (n->next != NULL && ! (n->next->flags & NODE_LINE))
p->flags |= TERMP_NOSPACE;
break;
case ROFFT_TBL:
@@ -377,7 +384,7 @@ print_mdoc_node(DECL_ARGS)
case ROFFT_EQN:
break;
default:
- if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags)
+ if ( ! termacts[n->tok].post || NODE_ENDED & n->flags)
break;
(void)(*termacts[n->tok].post)(p, &npair, meta, n);
@@ -387,19 +394,11 @@ print_mdoc_node(DECL_ARGS)
* that it must not call the post handler again.
*/
if (ENDBODY_NOT != n->end)
- n->body->flags |= MDOC_ENDED;
-
- /*
- * End of line terminating an implicit block
- * while an explicit block is still open.
- * Continue the explicit block without spacing.
- */
- if (ENDBODY_NOSPACE == n->end)
- p->flags |= TERMP_NOSPACE;
+ n->body->flags |= NODE_ENDED;
break;
}
- if (MDOC_EOS & n->flags)
+ if (NODE_EOS & n->flags)
p->flags |= TERMP_SENTENCE;
if (MDOC_ll != n->tok) {
@@ -562,6 +561,8 @@ print_bvspace(struct termp *p,
/* Do not vspace directly after Ss/Sh. */
nn = n;
+ while (nn->prev != NULL && nn->prev->flags & NODE_NOPRT)
+ nn = nn->prev;
while (nn->prev == NULL) {
do {
nn = nn->parent;
@@ -605,6 +606,7 @@ termp_ll_pre(DECL_ARGS)
static int
termp_it_pre(DECL_ARGS)
{
+ struct roffsu su;
char buf[24];
const struct roff_node *bl, *nn;
size_t ncols, dcol;
@@ -631,10 +633,10 @@ termp_it_pre(DECL_ARGS)
width = term_len(p, 2);
break;
case LIST_hang:
+ case LIST_tag:
width = term_len(p, 8);
break;
case LIST_column:
- case LIST_tag:
width = term_len(p, 10);
break;
default:
@@ -682,9 +684,12 @@ termp_it_pre(DECL_ARGS)
for (i = 0, nn = n->prev;
nn->prev && i < (int)ncols;
- nn = nn->prev, i++)
- offset += dcol + a2width(p,
- bl->norm->Bl.cols[i]);
+ nn = nn->prev, i++) {
+ SCALE_HS_INIT(&su,
+ term_strlen(p, bl->norm->Bl.cols[i]));
+ su.scale /= term_strlen(p, "0");
+ offset += term_hspan(p, &su) / 24 + dcol;
+ }
/*
* When exceeding the declared number of columns, leave
@@ -699,7 +704,9 @@ termp_it_pre(DECL_ARGS)
* Use the declared column widths, extended as explained
* in the preceding paragraph.
*/
- width = a2width(p, bl->norm->Bl.cols[i]) + dcol;
+ SCALE_HS_INIT(&su, term_strlen(p, bl->norm->Bl.cols[i]));
+ su.scale /= term_strlen(p, "0");
+ width = term_hspan(p, &su) / 24 + dcol;
break;
default:
if (NULL == bl->norm->Bl.width)
@@ -987,7 +994,7 @@ termp_nm_pre(DECL_ARGS)
return 1;
}
- if (NULL == n->child && NULL == meta->name)
+ if (n->child == NULL)
return 0;
if (n->type == ROFFT_HEAD)
@@ -1011,8 +1018,6 @@ termp_nm_pre(DECL_ARGS)
}
term_fontpush(p, TERMFONT_BOLD);
- if (NULL == n->child)
- term_word(p, meta->name);
return 1;
}
@@ -1042,7 +1047,7 @@ termp_fl_pre(DECL_ARGS)
if (!(n->child == NULL &&
(n->next == NULL ||
n->next->type == ROFFT_TEXT ||
- n->next->flags & MDOC_LINE)))
+ n->next->flags & NODE_LINE)))
p->flags |= TERMP_NOSPACE;
return 1;
@@ -1087,7 +1092,7 @@ static int
termp_ns_pre(DECL_ARGS)
{
- if ( ! (MDOC_LINE & n->flags))
+ if ( ! (NODE_LINE & n->flags))
p->flags |= TERMP_NOSPACE;
return 1;
}
@@ -1104,91 +1109,10 @@ termp_rs_pre(DECL_ARGS)
}
static int
-termp_rv_pre(DECL_ARGS)
-{
- struct roff_node *nch;
-
- term_newln(p);
-
- if (n->child != NULL) {
- term_word(p, "The");
-
- for (nch = n->child; nch != NULL; nch = nch->next) {
- term_fontpush(p, TERMFONT_BOLD);
- term_word(p, nch->string);
- term_fontpop(p);
-
- p->flags |= TERMP_NOSPACE;
- term_word(p, "()");
-
- if (nch->next == NULL)
- continue;
-
- if (nch->prev != NULL || nch->next->next != NULL) {
- p->flags |= TERMP_NOSPACE;
- term_word(p, ",");
- }
- if (nch->next->next == NULL)
- term_word(p, "and");
- }
-
- if (n->child != NULL && n->child->next != NULL)
- term_word(p, "functions return");
- else
- term_word(p, "function returns");
-
- term_word(p, "the value\\~0 if successful;");
- } else
- term_word(p, "Upon successful completion,"
- " the value\\~0 is returned;");
-
- term_word(p, "otherwise the value\\~\\-1 is returned"
- " and the global variable");
-
- term_fontpush(p, TERMFONT_UNDER);
- term_word(p, "errno");
- term_fontpop(p);
-
- term_word(p, "is set to indicate the error.");
- p->flags |= TERMP_SENTENCE;
-
- return 0;
-}
-
-static int
termp_ex_pre(DECL_ARGS)
{
- struct roff_node *nch;
-
term_newln(p);
- term_word(p, "The");
-
- for (nch = n->child; nch != NULL; nch = nch->next) {
- term_fontpush(p, TERMFONT_BOLD);
- term_word(p, nch->string);
- term_fontpop(p);
-
- if (nch->next == NULL)
- continue;
-
- if (nch->prev != NULL || nch->next->next != NULL) {
- p->flags |= TERMP_NOSPACE;
- term_word(p, ",");
- }
-
- if (nch->next->next == NULL)
- term_word(p, "and");
- }
-
- if (n->child != NULL && n->child->next != NULL)
- term_word(p, "utilities exit\\~0");
- else
- term_word(p, "utility exits\\~0");
-
- term_word(p, "on success, and\\~>0 if an error occurs.");
-
- p->flags |= TERMP_SENTENCE;
- return 0;
+ return 1;
}
static int
@@ -1253,7 +1177,7 @@ synopsis_pre(struct termp *p, const struct roff_node *n)
* Obviously, if we're not in a SYNOPSIS or no prior macros
* exist, do nothing.
*/
- if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
+ if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
return;
/*
@@ -1389,33 +1313,15 @@ termp_sh_post(DECL_ARGS)
}
}
-static int
-termp_bt_pre(DECL_ARGS)
-{
-
- term_word(p, "is currently in beta test.");
- p->flags |= TERMP_SENTENCE;
- return 0;
-}
-
static void
termp_lb_post(DECL_ARGS)
{
- if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags)
+ if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags)
term_newln(p);
}
static int
-termp_ud_pre(DECL_ARGS)
-{
-
- term_word(p, "currently under development.");
- p->flags |= TERMP_SENTENCE;
- return 0;
-}
-
-static int
termp_d1_pre(DECL_ARGS)
{
@@ -1430,7 +1336,7 @@ static int
termp_ft_pre(DECL_ARGS)
{
- /* NB: MDOC_LINE does not effect this! */
+ /* NB: NODE_LINE does not effect this! */
synopsis_pre(p, n);
term_fontpush(p, TERMFONT_UNDER);
return 1;
@@ -1442,7 +1348,7 @@ termp_fn_pre(DECL_ARGS)
size_t rmargin = 0;
int pretty;
- pretty = MDOC_SYNPRETTY & n->flags;
+ pretty = NODE_SYNPRETTY & n->flags;
synopsis_pre(p, n);
@@ -1460,7 +1366,7 @@ termp_fn_pre(DECL_ARGS)
term_word(p, n->string);
term_fontpop(p);
- if (n->sec == SEC_DESCRIPTION)
+ if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM)
tag_put(n->string, ++fn_prio, p->line);
if (pretty) {
@@ -1608,7 +1514,7 @@ termp_bd_pre(DECL_ARGS)
break;
}
if (p->flags & TERMP_NONEWLINE ||
- (nn->next && ! (nn->next->flags & MDOC_LINE)))
+ (nn->next && ! (nn->next->flags & NODE_LINE)))
continue;
term_flushln(p);
p->flags |= TERMP_NOSPACE;
@@ -1643,84 +1549,40 @@ termp_bd_post(DECL_ARGS)
}
static int
-termp_bx_pre(DECL_ARGS)
+termp_xx_pre(DECL_ARGS)
{
-
- if (NULL != (n = n->child)) {
- term_word(p, n->string);
- p->flags |= TERMP_NOSPACE;
- term_word(p, "BSD");
- } else {
- term_word(p, "BSD");
- return 0;
- }
-
- if (NULL != (n = n->next)) {
- p->flags |= TERMP_NOSPACE;
- term_word(p, "-");
- p->flags |= TERMP_NOSPACE;
- term_word(p, n->string);
- }
-
- return 0;
+ if ((n->aux = p->flags & TERMP_PREKEEP) == 0)
+ p->flags |= TERMP_PREKEEP;
+ return 1;
}
-static int
-termp_xx_pre(DECL_ARGS)
+static void
+termp_xx_post(DECL_ARGS)
{
- const char *pp;
- int flags;
-
- pp = NULL;
- switch (n->tok) {
- case MDOC_Bsx:
- pp = "BSD/OS";
- break;
- case MDOC_Dx:
- pp = "DragonFly";
- break;
- case MDOC_Fx:
- pp = "FreeBSD";
- break;
- case MDOC_Nx:
- pp = "NetBSD";
- break;
- case MDOC_Ox:
- pp = "OpenBSD";
- break;
- case MDOC_Ux:
- pp = "UNIX";
- break;
- default:
- abort();
- }
-
- term_word(p, pp);
- if (n->child) {
- flags = p->flags;
- p->flags |= TERMP_KEEP;
- term_word(p, n->child->string);
- p->flags = flags;
- }
- return 0;
+ if (n->aux == 0)
+ p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
}
static void
termp_pf_post(DECL_ARGS)
{
- if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
+ if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
p->flags |= TERMP_NOSPACE;
}
static int
termp_ss_pre(DECL_ARGS)
{
+ struct roff_node *nn;
switch (n->type) {
case ROFFT_BLOCK:
term_newln(p);
- if (n->prev)
+ for (nn = n->prev; nn != NULL; nn = nn->prev)
+ if ((nn->flags & NODE_NOPRT) == 0)
+ break;
+ if (nn != NULL)
term_vspace(p);
break;
case ROFFT_HEAD:
@@ -1760,7 +1622,7 @@ termp_in_pre(DECL_ARGS)
synopsis_pre(p, n);
- if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) {
+ if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) {
term_fontpush(p, TERMFONT_BOLD);
term_word(p, "#include");
term_word(p, "<");
@@ -1777,13 +1639,13 @@ static void
termp_in_post(DECL_ARGS)
{
- if (MDOC_SYNPRETTY & n->flags)
+ if (NODE_SYNPRETTY & n->flags)
term_fontpush(p, TERMFONT_BOLD);
p->flags |= TERMP_NOSPACE;
term_word(p, ">");
- if (MDOC_SYNPRETTY & n->flags)
+ if (NODE_SYNPRETTY & n->flags)
term_fontpop(p);
}
@@ -1852,6 +1714,8 @@ termp_quote_pre(DECL_ARGS)
case MDOC_Bq:
term_word(p, "[");
break;
+ case MDOC__T:
+ /* FALLTHROUGH */
case MDOC_Do:
case MDOC_Dq:
term_word(p, "\\(Lq");
@@ -1866,7 +1730,6 @@ termp_quote_pre(DECL_ARGS)
case MDOC_Pq:
term_word(p, "(");
break;
- case MDOC__T:
case MDOC_Qo:
case MDOC_Qq:
term_word(p, "\"");
@@ -1909,6 +1772,8 @@ termp_quote_post(DECL_ARGS)
case MDOC_Bq:
term_word(p, "]");
break;
+ case MDOC__T:
+ /* FALLTHROUGH */
case MDOC_Do:
case MDOC_Dq:
term_word(p, "\\(Rq");
@@ -1925,7 +1790,6 @@ termp_quote_post(DECL_ARGS)
case MDOC_Pq:
term_word(p, ")");
break;
- case MDOC__T:
case MDOC_Qo:
case MDOC_Qq:
term_word(p, "\"");
@@ -1990,7 +1854,7 @@ termp_fo_pre(DECL_ARGS)
size_t rmargin = 0;
int pretty;
- pretty = MDOC_SYNPRETTY & n->flags;
+ pretty = NODE_SYNPRETTY & n->flags;
if (n->type == ROFFT_BLOCK) {
synopsis_pre(p, n);
@@ -2036,7 +1900,7 @@ termp_fo_post(DECL_ARGS)
p->flags |= TERMP_NOSPACE;
term_word(p, ")");
- if (MDOC_SYNPRETTY & n->flags) {
+ if (NODE_SYNPRETTY & n->flags) {
p->flags |= TERMP_NOSPACE;
term_word(p, ";");
term_flushln(p);
@@ -2120,6 +1984,7 @@ static int
termp_li_pre(DECL_ARGS)
{
+ termp_tag_pre(p, pair, meta, n);
term_fontpush(p, TERMFONT_NONE);
return 1;
}
@@ -2218,6 +2083,26 @@ termp_under_pre(DECL_ARGS)
}
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)
{
@@ -2235,7 +2120,9 @@ termp_tag_pre(DECL_ARGS)
if (n->child != NULL &&
n->child->type == ROFFT_TEXT &&
- n->prev == NULL &&
+ (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 &&
diff --git a/usr/src/cmd/mandoc/mdoc_validate.c b/usr/src/cmd/mandoc/mdoc_validate.c
index e369349c8a..e58e7a4721 100644
--- a/usr/src/cmd/mandoc/mdoc_validate.c
+++ b/usr/src/cmd/mandoc/mdoc_validate.c
@@ -1,7 +1,7 @@
-/* $Id: mdoc_validate.c,v 1.301 2016/01/08 17:48:09 schwarze Exp $ */
+/* $Id: mdoc_validate.c,v 1.318 2017/02/06 03:44:58 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010-2016 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -51,6 +51,7 @@ enum check_ineq {
typedef void (*v_post)(POST_ARGS);
+static int build_list(struct roff_man *, int);
static void check_text(struct roff_man *, int, int, char *);
static void check_argv(struct roff_man *,
struct roff_node *, struct mdoc_argv *);
@@ -67,7 +68,6 @@ static void post_bf(POST_ARGS);
static void post_bk(POST_ARGS);
static void post_bl(POST_ARGS);
static void post_bl_block(POST_ARGS);
-static void post_bl_block_tag(POST_ARGS);
static void post_bl_head(POST_ARGS);
static void post_bl_norm(POST_ARGS);
static void post_bx(POST_ARGS);
@@ -96,6 +96,7 @@ static void post_par(POST_ARGS);
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_sh(POST_ARGS);
static void post_sh_head(POST_ARGS);
static void post_sh_name(POST_ARGS);
@@ -104,6 +105,8 @@ static void post_sh_authors(POST_ARGS);
static void post_sm(POST_ARGS);
static void post_st(POST_ARGS);
static void post_std(POST_ARGS);
+static void post_xr(POST_ARGS);
+static void post_xx(POST_ARGS);
static v_post mdoc_valids[MDOC_MAX] = {
NULL, /* Ap */
@@ -142,11 +145,11 @@ static v_post mdoc_valids[MDOC_MAX] = {
NULL, /* Op */
post_obsolete, /* Ot */
post_defaults, /* Pa */
- post_std, /* Rv */
+ post_rv, /* Rv */
post_st, /* St */
NULL, /* Va */
NULL, /* Vt */
- NULL, /* Xr */
+ post_xr, /* Xr */
NULL, /* %A */
post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */
NULL, /* %D */
@@ -166,7 +169,7 @@ static v_post mdoc_valids[MDOC_MAX] = {
post_bf, /* Bf */
NULL, /* Bo */
NULL, /* Bq */
- NULL, /* Bsx */
+ post_xx, /* Bsx */
post_bx, /* Bx */
post_obsolete, /* Db */
NULL, /* Dc */
@@ -176,12 +179,12 @@ static v_post mdoc_valids[MDOC_MAX] = {
NULL, /* Ef */
NULL, /* Em */
NULL, /* Eo */
- NULL, /* Fx */
+ post_xx, /* Fx */
NULL, /* Ms */
NULL, /* No */
post_ns, /* Ns */
- NULL, /* Nx */
- NULL, /* Ox */
+ post_xx, /* Nx */
+ post_xx, /* Ox */
NULL, /* Pc */
NULL, /* Pf */
NULL, /* Po */
@@ -199,7 +202,7 @@ static v_post mdoc_valids[MDOC_MAX] = {
post_hyph, /* Sx */
NULL, /* Sy */
NULL, /* Tn */
- NULL, /* Ux */
+ post_xx, /* Ux */
NULL, /* Xc */
NULL, /* Xo */
post_fo, /* Fo */
@@ -222,7 +225,7 @@ static v_post mdoc_valids[MDOC_MAX] = {
NULL, /* %C */
post_es, /* Es */
post_en, /* En */
- NULL, /* Dx */
+ post_xx, /* Dx */
NULL, /* %Q */
post_par, /* br */
post_par, /* sp */
@@ -297,7 +300,8 @@ mdoc_node_validate(struct roff_man *mdoc)
mdoc->next = ROFF_NEXT_SIBLING;
switch (n->type) {
case ROFFT_TEXT:
- if (n->sec != SEC_SYNOPSIS || n->parent->tok != MDOC_Fd)
+ if (n->sec != SEC_SYNOPSIS ||
+ (n->parent->tok != MDOC_Cd && n->parent->tok != MDOC_Fd))
check_text(mdoc, n->line, n->pos, n->string);
break;
case ROFFT_EQN:
@@ -316,9 +320,9 @@ mdoc_node_validate(struct roff_man *mdoc)
*/
if (n->child != NULL)
- n->child->flags &= ~MDOC_DELIMC;
+ n->child->flags &= ~NODE_DELIMC;
if (n->last != NULL)
- n->last->flags &= ~MDOC_DELIMO;
+ n->last->flags &= ~NODE_DELIMO;
/* Call the macro's postprocessor. */
@@ -502,6 +506,7 @@ post_bl_norm(POST_ARGS)
mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
n->line, n->pos, "Bl");
n->norm->Bl.type = LIST_item;
+ mdoclt = MDOC_Item;
}
/*
@@ -618,6 +623,10 @@ post_bd(POST_ARGS)
}
}
+/*
+ * Stand-alone line macros.
+ */
+
static void
post_an_norm(POST_ARGS)
{
@@ -646,6 +655,158 @@ post_an_norm(POST_ARGS)
}
static void
+post_eoln(POST_ARGS)
+{
+ struct roff_node *n;
+
+ n = mdoc->last;
+ if (n->child != NULL)
+ mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse,
+ n->line, n->pos, "%s %s",
+ mdoc_macronames[n->tok], n->child->string);
+
+ while (n->child != NULL)
+ roff_node_delete(mdoc, n->child);
+
+ roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
+ "is currently in beta test." : "currently under development.");
+ mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
+ mdoc->last = n;
+}
+
+static int
+build_list(struct roff_man *mdoc, int tok)
+{
+ struct roff_node *n;
+ int ic;
+
+ n = mdoc->last->next;
+ for (ic = 1;; ic++) {
+ roff_elem_alloc(mdoc, n->line, n->pos, tok);
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc_node_relink(mdoc, n);
+ n = mdoc->last = mdoc->last->parent;
+ mdoc->next = ROFF_NEXT_SIBLING;
+ if (n->next == NULL)
+ return ic;
+ if (ic > 1 || n->next->next != NULL) {
+ roff_word_alloc(mdoc, n->line, n->pos, ",");
+ mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
+ }
+ n = mdoc->last->next;
+ if (n->next == NULL) {
+ roff_word_alloc(mdoc, n->line, n->pos, "and");
+ mdoc->last->flags |= NODE_NOSRC;
+ }
+ }
+}
+
+static void
+post_ex(POST_ARGS)
+{
+ struct roff_node *n;
+ int ic;
+
+ post_std(mdoc);
+
+ n = mdoc->last;
+ mdoc->next = ROFF_NEXT_CHILD;
+ roff_word_alloc(mdoc, n->line, n->pos, "The");
+ mdoc->last->flags |= NODE_NOSRC;
+
+ if (mdoc->last->next != NULL)
+ ic = build_list(mdoc, MDOC_Nm);
+ else if (mdoc->meta.name != NULL) {
+ roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
+ mdoc->last->flags |= NODE_NOSRC;
+ roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->last = mdoc->last->parent;
+ mdoc->next = ROFF_NEXT_SIBLING;
+ ic = 1;
+ } else {
+ mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
+ n->line, n->pos, "Ex");
+ ic = 0;
+ }
+
+ roff_word_alloc(mdoc, n->line, n->pos,
+ ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
+ mdoc->last->flags |= NODE_NOSRC;
+ roff_word_alloc(mdoc, n->line, n->pos,
+ "on success, and\\~>0 if an error occurs.");
+ mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
+ mdoc->last = n;
+}
+
+static void
+post_lb(POST_ARGS)
+{
+ struct roff_node *n;
+ const char *p;
+
+ n = mdoc->last;
+ assert(n->child->type == ROFFT_TEXT);
+ mdoc->next = ROFF_NEXT_CHILD;
+
+ if ((p = mdoc_a2lib(n->child->string)) != NULL) {
+ n->child->flags |= NODE_NOPRT;
+ roff_word_alloc(mdoc, n->line, n->pos, p);
+ mdoc->last->flags = NODE_NOSRC;
+ mdoc->last = n;
+ return;
+ }
+
+ roff_word_alloc(mdoc, n->line, n->pos, "library");
+ mdoc->last->flags = NODE_NOSRC;
+ roff_word_alloc(mdoc, n->line, n->pos, "\\(Lq");
+ mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
+ mdoc->last = mdoc->last->next;
+ roff_word_alloc(mdoc, n->line, n->pos, "\\(Rq");
+ mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
+ mdoc->last = n;
+}
+
+static void
+post_rv(POST_ARGS)
+{
+ struct roff_node *n;
+ int ic;
+
+ post_std(mdoc);
+
+ n = mdoc->last;
+ mdoc->next = ROFF_NEXT_CHILD;
+ if (n->child != NULL) {
+ roff_word_alloc(mdoc, n->line, n->pos, "The");
+ mdoc->last->flags |= NODE_NOSRC;
+ ic = build_list(mdoc, MDOC_Fn);
+ roff_word_alloc(mdoc, n->line, n->pos,
+ ic > 1 ? "functions return" : "function returns");
+ mdoc->last->flags |= NODE_NOSRC;
+ roff_word_alloc(mdoc, n->line, n->pos,
+ "the value\\~0 if successful;");
+ } else
+ roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
+ "completion, the value\\~0 is returned;");
+ mdoc->last->flags |= NODE_NOSRC;
+
+ roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
+ "the value\\~\\-1 is returned and the global variable");
+ mdoc->last->flags |= NODE_NOSRC;
+ roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
+ mdoc->last->flags |= NODE_NOSRC;
+ roff_word_alloc(mdoc, n->line, n->pos, "errno");
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->last = mdoc->last->parent;
+ mdoc->next = ROFF_NEXT_SIBLING;
+ roff_word_alloc(mdoc, n->line, n->pos,
+ "is set to indicate the error.");
+ mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
+ mdoc->last = n;
+}
+
+static void
post_std(POST_ARGS)
{
struct roff_node *n;
@@ -660,6 +821,30 @@ post_std(POST_ARGS)
}
static void
+post_st(POST_ARGS)
+{
+ struct roff_node *n, *nch;
+ const char *p;
+
+ n = mdoc->last;
+ nch = n->child;
+ assert(nch->type == ROFFT_TEXT);
+
+ if ((p = mdoc_a2st(nch->string)) == NULL) {
+ mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
+ nch->line, nch->pos, "St %s", nch->string);
+ roff_node_delete(mdoc, n);
+ return;
+ }
+
+ nch->flags |= NODE_NOPRT;
+ mdoc->next = ROFF_NEXT_CHILD;
+ roff_word_alloc(mdoc, nch->line, nch->pos, p);
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->last= n;
+}
+
+static void
post_obsolete(POST_ARGS)
{
struct roff_node *n;
@@ -670,6 +855,10 @@ post_obsolete(POST_ARGS)
n->line, n->pos, mdoc_macronames[n->tok]);
}
+/*
+ * Block macros.
+ */
+
static void
post_bf(POST_ARGS)
{
@@ -736,39 +925,6 @@ post_bf(POST_ARGS)
}
static void
-post_lb(POST_ARGS)
-{
- struct roff_node *n;
- const char *stdlibname;
- char *libname;
-
- n = mdoc->last->child;
- assert(n->type == ROFFT_TEXT);
-
- if (NULL == (stdlibname = mdoc_a2lib(n->string)))
- mandoc_asprintf(&libname,
- "library \\(Lq%s\\(Rq", n->string);
- else
- libname = mandoc_strdup(stdlibname);
-
- free(n->string);
- n->string = libname;
-}
-
-static void
-post_eoln(POST_ARGS)
-{
- const struct roff_node *n;
-
- n = mdoc->last;
- if (n->child != NULL)
- mandoc_vmsg(MANDOCERR_ARG_SKIP,
- mdoc->parse, n->line, n->pos,
- "%s %s", mdoc_macronames[n->tok],
- n->child->string);
-}
-
-static void
post_fname(POST_ARGS)
{
const struct roff_node *n;
@@ -850,14 +1006,23 @@ post_nm(POST_ARGS)
n->last->tok == MDOC_Lp))
mdoc_node_relink(mdoc, n->last);
- if (mdoc->meta.name != NULL)
- return;
-
- deroff(&mdoc->meta.name, n);
-
if (mdoc->meta.name == NULL)
+ deroff(&mdoc->meta.name, n);
+
+ if (mdoc->meta.name == NULL ||
+ (mdoc->lastsec == SEC_NAME && n->child == NULL))
mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
n->line, n->pos, "Nm");
+
+ if ((n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) ||
+ (n->child != NULL && n->child->type == ROFFT_TEXT) ||
+ mdoc->meta.name == NULL)
+ return;
+
+ mdoc->next = ROFF_NEXT_CHILD;
+ roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->last = n;
}
static void
@@ -885,9 +1050,11 @@ post_display(POST_ARGS)
n = mdoc->last;
switch (n->type) {
case ROFFT_BODY:
- if (n->end != ENDBODY_NOT)
- break;
- if (n->child == NULL)
+ if (n->end != ENDBODY_NOT) {
+ if (n->tok == MDOC_Bd &&
+ n->body->parent->args == NULL)
+ roff_node_delete(mdoc, n);
+ } else if (n->child == NULL)
mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
else if (n->tok == MDOC_D1)
@@ -942,12 +1109,15 @@ post_defaults(POST_ARGS)
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, "...");
+ mdoc->last->flags |= NODE_NOSRC;
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;
break;
default:
abort();
@@ -958,17 +1128,11 @@ post_defaults(POST_ARGS)
static void
post_at(POST_ARGS)
{
- struct roff_node *n;
- const char *std_att;
- char *att;
+ struct roff_node *n, *nch;
+ const char *att;
n = mdoc->last;
- if (n->child == NULL) {
- mdoc->next = ROFF_NEXT_CHILD;
- roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
- mdoc->last = n;
- return;
- }
+ nch = n->child;
/*
* If we have a child, look it up in the standard keys. If a
@@ -976,17 +1140,19 @@ post_at(POST_ARGS)
* prefix "AT&T UNIX " to the existing data.
*/
- n = n->child;
- assert(n->type == ROFFT_TEXT);
- if ((std_att = mdoc_a2att(n->string)) == NULL) {
+ att = NULL;
+ if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
- n->line, n->pos, "At %s", n->string);
- mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
- } else
- att = mandoc_strdup(std_att);
+ nch->line, nch->pos, "At %s", nch->string);
- free(n->string);
- n->string = att;
+ mdoc->next = ROFF_NEXT_CHILD;
+ if (att != NULL) {
+ roff_word_alloc(mdoc, nch->line, nch->pos, att);
+ nch->flags |= NODE_NOPRT;
+ } else
+ roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->last = n;
}
static void
@@ -1025,6 +1191,41 @@ post_es(POST_ARGS)
}
static void
+post_xx(POST_ARGS)
+{
+ struct roff_node *n;
+ const char *os;
+
+ n = mdoc->last;
+ switch (n->tok) {
+ case MDOC_Bsx:
+ os = "BSD/OS";
+ break;
+ case MDOC_Dx:
+ os = "DragonFly";
+ break;
+ case MDOC_Fx:
+ os = "FreeBSD";
+ break;
+ case MDOC_Nx:
+ os = "NetBSD";
+ break;
+ case MDOC_Ox:
+ os = "OpenBSD";
+ break;
+ case MDOC_Ux:
+ os = "UNIX";
+ break;
+ default:
+ abort();
+ }
+ mdoc->next = ROFF_NEXT_CHILD;
+ roff_word_alloc(mdoc, n->line, n->pos, os);
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->last = n;
+}
+
+static void
post_it(POST_ARGS)
{
struct roff_node *nbl, *nit, *nch;
@@ -1063,10 +1264,11 @@ post_it(POST_ARGS)
mdoc_argnames[nbl->args->argv[0].arg]);
/* FALLTHROUGH */
case LIST_item:
- if (nit->head->child != NULL)
+ if ((nch = nit->head->child) != NULL)
mandoc_vmsg(MANDOCERR_ARG_SKIP,
mdoc->parse, nit->line, nit->pos,
- "It %s", nit->head->child->string);
+ "It %s", nch->string == NULL ?
+ mdoc_macronames[nch->tok] : nch->string);
break;
case LIST_column:
cols = (int)nbl->norm->Bl.ncols;
@@ -1095,22 +1297,7 @@ post_bl_block(POST_ARGS)
post_prevpar(mdoc);
- /*
- * These are fairly complicated, so we've broken them into two
- * functions. post_bl_block_tag() is called when a -tag is
- * specified, but no -width (it must be guessed). The second
- * when a -width is specified (macro indicators must be
- * rewritten into real lengths).
- */
-
n = mdoc->last;
-
- if (n->norm->Bl.type == LIST_tag &&
- n->norm->Bl.width == NULL) {
- post_bl_block_tag(mdoc);
- assert(n->norm->Bl.width != NULL);
- }
-
for (ni = n->body->child; ni != NULL; ni = ni->next) {
if (ni->body == NULL)
continue;
@@ -1168,71 +1355,6 @@ rewrite_macro2len(char **arg)
}
static void
-post_bl_block_tag(POST_ARGS)
-{
- struct roff_node *n, *nn;
- size_t sz, ssz;
- int i;
- char buf[24];
-
- /*
- * Calculate the -width for a `Bl -tag' list if it hasn't been
- * provided. Uses the first head macro. NOTE AGAIN: this is
- * ONLY if the -width argument has NOT been provided. See
- * rewrite_macro2len() for converting the -width string.
- */
-
- sz = 10;
- n = mdoc->last;
-
- for (nn = n->body->child; nn != NULL; nn = nn->next) {
- if (nn->tok != MDOC_It)
- continue;
-
- assert(nn->type == ROFFT_BLOCK);
- nn = nn->head->child;
-
- if (nn == NULL)
- break;
-
- if (nn->type == ROFFT_TEXT) {
- sz = strlen(nn->string) + 1;
- break;
- }
-
- if (0 != (ssz = macro2len(nn->tok)))
- sz = ssz;
-
- break;
- }
-
- /* Defaults to ten ens. */
-
- (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
-
- /*
- * We have to dynamically add this to the macro's argument list.
- * We're guaranteed that a MDOC_Width doesn't already exist.
- */
-
- assert(n->args != NULL);
- i = (int)(n->args->argc)++;
-
- n->args->argv = mandoc_reallocarray(n->args->argv,
- n->args->argc, sizeof(struct mdoc_argv));
-
- n->args->argv[i].arg = MDOC_Width;
- n->args->argv[i].line = n->line;
- n->args->argv[i].pos = n->pos;
- n->args->argv[i].sz = 1;
- n->args->argv[i].value = mandoc_malloc(sizeof(char *));
- n->args->argv[i].value[0] = mandoc_strdup(buf);
-
- /* Set our width! */
- n->norm->Bl.width = n->args->argv[i].value[0];
-}
-
-static void
post_bl_head(POST_ARGS)
{
struct roff_node *nbl, *nh, *nch, *nnext;
@@ -1325,11 +1447,41 @@ post_bl(POST_ARGS)
return;
}
while (nchild != NULL) {
+ nnext = nchild->next;
if (nchild->tok == MDOC_It ||
(nchild->tok == MDOC_Sm &&
- nchild->next != NULL &&
- nchild->next->tok == MDOC_It)) {
- nchild = nchild->next;
+ nnext != NULL && nnext->tok == MDOC_It)) {
+ nchild = nnext;
+ 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);
+ mdoc->next = ROFF_NEXT_SIBLING;
+ roff_body_alloc(mdoc, nchild->line,
+ nchild->pos, MDOC_It);
+ while (nchild->tok != MDOC_It) {
+ mdoc_node_relink(mdoc, nchild);
+ if ((nchild = nnext) == NULL)
+ break;
+ nnext = nchild->next;
+ mdoc->next = ROFF_NEXT_SIBLING;
+ }
+ mdoc->last = nbody;
continue;
}
@@ -1345,13 +1497,11 @@ post_bl(POST_ARGS)
nblock = nbody->parent;
nprev = nblock->prev;
nparent = nblock->parent;
- nnext = nchild->next;
/*
* Unlink this child.
*/
- assert(nchild->prev == NULL);
nbody->child = nnext;
if (nnext == NULL)
nbody->last = NULL;
@@ -1462,27 +1612,6 @@ post_root(POST_ARGS)
}
static void
-post_st(POST_ARGS)
-{
- struct roff_node *n, *nch;
- const char *p;
-
- n = mdoc->last;
- nch = n->child;
-
- assert(nch->type == ROFFT_TEXT);
-
- if ((p = mdoc_a2st(nch->string)) == NULL) {
- mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
- nch->line, nch->pos, "St %s", nch->string);
- roff_node_delete(mdoc, n);
- } else {
- free(nch->string);
- nch->string = mandoc_strdup(p);
- }
-}
-
-static void
post_rs(POST_ARGS)
{
struct roff_node *np, *nch, *next, *prev;
@@ -1600,7 +1729,7 @@ static void
post_ns(POST_ARGS)
{
- if (mdoc->last->flags & MDOC_LINE)
+ if (mdoc->last->flags & NODE_LINE)
mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
mdoc->last->line, mdoc->last->pos, NULL);
}
@@ -1646,8 +1775,12 @@ post_sh_name(POST_ARGS)
for (n = mdoc->last->child; n != NULL; n = n->next) {
switch (n->tok) {
case MDOC_Nm:
+ if (hasnm && n->child != NULL)
+ mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
+ mdoc->parse, n->line, n->pos,
+ "Nm %s", n->child->string);
hasnm = 1;
- break;
+ continue;
case MDOC_Nd:
hasnd = 1;
if (n->next != NULL)
@@ -1655,14 +1788,19 @@ post_sh_name(POST_ARGS)
mdoc->parse, n->line, n->pos, NULL);
break;
case TOKEN_NONE:
- if (hasnm)
- break;
+ if (n->type == ROFFT_TEXT &&
+ n->string[0] == ',' && n->string[1] == '\0' &&
+ n->next != NULL && n->next->tok == MDOC_Nm) {
+ n = n->next;
+ continue;
+ }
/* FALLTHROUGH */
default:
mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
- break;
+ continue;
}
+ break;
}
if ( ! hasnm)
@@ -1759,8 +1897,9 @@ post_sh_authors(POST_ARGS)
static void
post_sh_head(POST_ARGS)
{
- const char *goodsec;
- enum roff_sec sec;
+ struct roff_node *nch;
+ const char *goodsec;
+ enum roff_sec sec;
/*
* Process a new section. Sections are either "named" or
@@ -1773,10 +1912,13 @@ post_sh_head(POST_ARGS)
/* The NAME should be first. */
- if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
+ if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
- mdoc->last->line, mdoc->last->pos,
- "Sh %s", secnames[sec]);
+ mdoc->last->line, mdoc->last->pos, "Sh %s",
+ sec != SEC_CUSTOM ? secnames[sec] :
+ (nch = mdoc->last->child) == NULL ? "" :
+ nch->type == ROFFT_TEXT ? nch->string :
+ mdoc_macronames[nch->tok]);
/* The SYNOPSIS gets special attention in other areas. */
@@ -1852,6 +1994,21 @@ post_sh_head(POST_ARGS)
}
static void
+post_xr(POST_ARGS)
+{
+ struct roff_node *n, *nch;
+
+ n = mdoc->last;
+ nch = n->child;
+ if (nch->next == NULL) {
+ mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
+ n->line, n->pos, "Xr %s", nch->string);
+ return;
+ }
+ assert(nch->next == n->last);
+}
+
+static void
post_ignpar(POST_ARGS)
{
struct roff_node *np;
@@ -1961,6 +2118,8 @@ post_dd(POST_ARGS)
char *datestr;
n = mdoc->last;
+ n->flags |= NODE_NOPRT;
+
if (mdoc->meta.date != NULL) {
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
n->line, n->pos, "Dd");
@@ -1978,7 +2137,7 @@ post_dd(POST_ARGS)
if (n->child == NULL || n->child->string[0] == '\0') {
mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
- goto out;
+ return;
}
datestr = NULL;
@@ -1990,8 +2149,6 @@ post_dd(POST_ARGS)
datestr, n->line, n->pos);
free(datestr);
}
-out:
- roff_node_delete(mdoc, n);
}
static void
@@ -2002,10 +2159,12 @@ post_dt(POST_ARGS)
char *p;
n = mdoc->last;
+ n->flags |= NODE_NOPRT;
+
if (mdoc->flags & MDOC_PBODY) {
mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
n->line, n->pos, "Dt");
- goto out;
+ return;
}
if (mdoc->meta.title != NULL)
@@ -2047,7 +2206,7 @@ post_dt(POST_ARGS)
}
}
- /* Mandatory second argument: section. */
+ /* Mandatory second argument: section. */
if (nn != NULL)
nn = nn->next;
@@ -2057,7 +2216,7 @@ post_dt(POST_ARGS)
mdoc->parse, n->line, n->pos,
"Dt %s", mdoc->meta.title);
mdoc->meta.vol = mandoc_strdup("LOCAL");
- goto out; /* msec and arch remain NULL. */
+ return; /* msec and arch remain NULL. */
}
mdoc->meta.msec = mandoc_strdup(nn->string);
@@ -2075,7 +2234,7 @@ post_dt(POST_ARGS)
/* Optional third argument: architecture. */
if ((nn = nn->next) == NULL)
- goto out;
+ return;
for (p = nn->string; *p != '\0'; p++)
*p = tolower((unsigned char)*p);
@@ -2086,15 +2245,41 @@ post_dt(POST_ARGS)
if ((nn = nn->next) != NULL)
mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
nn->line, nn->pos, "Dt ... %s", nn->string);
-
-out:
- roff_node_delete(mdoc, n);
}
static void
post_bx(POST_ARGS)
{
- struct roff_node *n;
+ struct roff_node *n, *nch;
+
+ n = mdoc->last;
+ nch = n->child;
+
+ if (nch != NULL) {
+ mdoc->last = nch;
+ nch = nch->next;
+ mdoc->next = ROFF_NEXT_SIBLING;
+ roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->next = ROFF_NEXT_SIBLING;
+ } else
+ mdoc->next = ROFF_NEXT_CHILD;
+ roff_word_alloc(mdoc, n->line, n->pos, "BSD");
+ mdoc->last->flags |= NODE_NOSRC;
+
+ if (nch == NULL) {
+ mdoc->last = n;
+ return;
+ }
+
+ roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->next = ROFF_NEXT_SIBLING;
+ roff_word_alloc(mdoc, n->line, n->pos, "-");
+ mdoc->last->flags |= NODE_NOSRC;
+ roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
+ mdoc->last->flags |= NODE_NOSRC;
+ mdoc->last = n;
/*
* Make `Bx's second argument always start with an uppercase
@@ -2102,8 +2287,7 @@ post_bx(POST_ARGS)
* uppercase blindly.
*/
- if ((n = mdoc->last->child) != NULL && (n = n->next) != NULL)
- *n->string = (char)toupper((unsigned char)*n->string);
+ *nch->string = (char)toupper((unsigned char)*nch->string);
}
static void
@@ -2116,6 +2300,8 @@ post_os(POST_ARGS)
struct roff_node *n;
n = mdoc->last;
+ n->flags |= NODE_NOPRT;
+
if (mdoc->meta.os != NULL)
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
n->line, n->pos, "Os");
@@ -2136,11 +2322,11 @@ post_os(POST_ARGS)
mdoc->meta.os = NULL;
deroff(&mdoc->meta.os, n);
if (mdoc->meta.os)
- goto out;
+ return;
if (mdoc->defos) {
mdoc->meta.os = mandoc_strdup(mdoc->defos);
- goto out;
+ return;
}
#ifdef OSNAME
@@ -2157,35 +2343,6 @@ post_os(POST_ARGS)
}
mdoc->meta.os = mandoc_strdup(defbuf);
#endif /*!OSNAME*/
-
-out:
- roff_node_delete(mdoc, n);
-}
-
-/*
- * If no argument is provided,
- * fill in the name of the current manual page.
- */
-static void
-post_ex(POST_ARGS)
-{
- struct roff_node *n;
-
- post_std(mdoc);
-
- n = mdoc->last;
- if (n->child != NULL)
- return;
-
- if (mdoc->meta.name == NULL) {
- mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
- n->line, n->pos, "Ex");
- return;
- }
-
- mdoc->next = ROFF_NEXT_CHILD;
- roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
- mdoc->last = n;
}
enum roff_sec
diff --git a/usr/src/cmd/mandoc/preconv.c b/usr/src/cmd/mandoc/preconv.c
index 1fc137a964..08f8df86a6 100644
--- a/usr/src/cmd/mandoc/preconv.c
+++ b/usr/src/cmd/mandoc/preconv.c
@@ -1,4 +1,4 @@
-/* $Id: preconv.c,v 1.15 2015/10/06 18:32:19 schwarze Exp $ */
+/* $Id: preconv.c,v 1.16 2017/02/18 13:43:52 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
@@ -26,14 +26,14 @@
#include "libmandoc.h"
int
-preconv_encode(struct buf *ib, size_t *ii, struct buf *ob, size_t *oi,
+preconv_encode(const struct buf *ib, size_t *ii, struct buf *ob, size_t *oi,
int *filenc)
{
- unsigned char *cu;
- int nby;
- unsigned int accum;
+ const unsigned char *cu;
+ int nby;
+ unsigned int accum;
- cu = (unsigned char *)ib->buf + *ii;
+ cu = (const unsigned char *)ib->buf + *ii;
assert(*cu & 0x80);
if ( ! (*filenc & MPARSE_UTF8))
@@ -90,7 +90,7 @@ preconv_encode(struct buf *ib, size_t *ii, struct buf *ob, size_t *oi,
assert(accum < 0xd800 || accum > 0xdfff);
*oi += snprintf(ob->buf + *oi, 11, "\\[u%.4X]", accum);
- *ii = (char *)cu - ib->buf;
+ *ii = (const char *)cu - ib->buf;
*filenc &= ~MPARSE_LATIN1;
return 1;
diff --git a/usr/src/cmd/mandoc/read.c b/usr/src/cmd/mandoc/read.c
index 0c5188b757..3e5d41161a 100644
--- a/usr/src/cmd/mandoc/read.c
+++ b/usr/src/cmd/mandoc/read.c
@@ -1,4 +1,4 @@
-/* $Id: read.c,v 1.150.2.5 2017/01/09 02:25:53 schwarze Exp $ */
+/* $Id: read.c,v 1.161 2017/02/18 17:29:28 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -19,10 +19,8 @@
#include "config.h"
#include <sys/types.h>
-#if HAVE_MMAP
#include <sys/mman.h>
#include <sys/stat.h>
-#endif
#include <assert.h>
#include <ctype.h>
@@ -32,7 +30,6 @@
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
-#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -50,8 +47,8 @@
#define REPARSE_LIMIT 1000
struct mparse {
- struct roff_man *man; /* man parser */
struct roff *roff; /* roff parser (!NULL) */
+ struct roff_man *man; /* man parser */
char *sodest; /* filename pointed to by .so */
const char *file; /* filename of current input file */
struct buf *primary; /* buffer currently being parsed */
@@ -179,6 +176,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = {
"blank line in fill mode, using .sp",
"tab in filled text",
"whitespace at end of input line",
+ "new sentence, new line",
"bad comment style",
"invalid escape sequence",
"undefined string, using \"\"",
@@ -604,13 +602,11 @@ static int
read_whole_file(struct mparse *curp, const char *file, int fd,
struct buf *fb, int *with_mmap)
{
+ struct stat st;
gzFile gz;
size_t off;
ssize_t ssz;
-#if HAVE_MMAP
- struct stat st;
-
if (fstat(fd, &st) == -1)
err((int)MANDOCLEVEL_SYSERR, "%s", file);
@@ -632,7 +628,6 @@ read_whole_file(struct mparse *curp, const char *file, int fd,
if (fb->buf != MAP_FAILED)
return 1;
}
-#endif
if (curp->gzip) {
if ((gz = gzdopen(fd, "rb")) == NULL)
@@ -757,11 +752,9 @@ mparse_readfd(struct mparse *curp, int fd, const char *file)
(MPARSE_UTF8 | MPARSE_LATIN1);
mparse_parse_buffer(curp, blk, file);
curp->filenc = save_filenc;
-#if HAVE_MMAP
if (with_mmap)
munmap(blk.buf, blk.sz);
else
-#endif
free(blk.buf);
}
return curp->file_status;
@@ -835,13 +828,15 @@ mparse_reset(struct mparse *curp)
{
roff_reset(curp->roff);
roff_man_reset(curp->man);
+
+ free(curp->sodest);
+ curp->sodest = NULL;
+
if (curp->secondary)
curp->secondary->sz = 0;
curp->file_status = MANDOCLEVEL_OK;
-
- free(curp->sodest);
- curp->sodest = NULL;
+ curp->gzip = 0;
}
void
@@ -849,8 +844,7 @@ mparse_free(struct mparse *curp)
{
roff_man_free(curp->man);
- if (curp->roff)
- roff_free(curp->roff);
+ roff_free(curp->roff);
if (curp->secondary)
free(curp->secondary->buf);
diff --git a/usr/src/cmd/mandoc/roff.c b/usr/src/cmd/mandoc/roff.c
index 13b9439e4d..ad55d320e4 100644
--- a/usr/src/cmd/mandoc/roff.c
+++ b/usr/src/cmd/mandoc/roff.c
@@ -1,7 +1,7 @@
-/* $Id: roff.c,v 1.284 2016/01/08 17:48:10 schwarze Exp $ */
+/* $Id: roff.c,v 1.289 2017/02/17 03:03:03 schwarze Exp $ */
/*
* Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -991,11 +991,11 @@ roff_node_alloc(struct roff_man *man, int line, int pos,
n->sec = man->lastsec;
if (man->flags & MDOC_SYNOPSIS)
- n->flags |= MDOC_SYNPRETTY;
+ n->flags |= NODE_SYNPRETTY;
else
- n->flags &= ~MDOC_SYNPRETTY;
+ n->flags &= ~NODE_SYNPRETTY;
if (man->flags & MDOC_NEWLINE)
- n->flags |= MDOC_LINE;
+ n->flags |= NODE_LINE;
man->flags &= ~MDOC_NEWLINE;
return n;
@@ -1017,9 +1017,13 @@ roff_node_append(struct roff_man *man, struct roff_node *n)
n->parent = man->last->parent;
break;
case ROFF_NEXT_CHILD:
+ if (man->last->child != NULL) {
+ n->next = man->last->child;
+ man->last->child->prev = n;
+ } else
+ man->last->last = n;
man->last->child = n;
n->parent = man->last;
- n->parent->last = n;
break;
default:
abort();
@@ -1059,10 +1063,7 @@ roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
n->string = roff_strdup(man->roff, word);
roff_node_append(man, n);
- if (man->macroset == MACROSET_MDOC)
- n->flags |= MDOC_VALID | MDOC_ENDED;
- else
- n->flags |= MAN_VALID;
+ n->flags |= NODE_VALID | NODE_ENDED;
man->next = ROFF_NEXT_SIBLING;
}
@@ -1132,7 +1133,7 @@ roff_addeqn(struct roff_man *man, const struct eqn *eqn)
n = roff_node_alloc(man, eqn->ln, eqn->pos, ROFFT_EQN, TOKEN_NONE);
n->eqn = eqn;
if (eqn->ln > man->last->line)
- n->flags |= MDOC_LINE;
+ n->flags |= NODE_LINE;
roff_node_append(man, n);
man->next = ROFF_NEXT_SIBLING;
}
@@ -1147,10 +1148,7 @@ roff_addtbl(struct roff_man *man, const struct tbl_span *tbl)
n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE);
n->span = tbl;
roff_node_append(man, n);
- if (man->macroset == MACROSET_MDOC)
- n->flags |= MDOC_VALID | MDOC_ENDED;
- else
- n->flags |= MAN_VALID;
+ n->flags |= NODE_VALID | NODE_ENDED;
man->next = ROFF_NEXT_SIBLING;
}
@@ -1225,22 +1223,25 @@ deroff(char **dest, const struct roff_node *n)
return;
}
- /* Skip leading whitespace and escape sequences. */
+ /* Skip leading whitespace. */
- cp = n->string;
- while (*cp != '\0') {
- if ('\\' == *cp) {
+ for (cp = n->string; *cp != '\0'; cp++) {
+ if (cp[0] == '\\' && cp[1] != '\0' &&
+ strchr(" %&0^|~", cp[1]) != NULL)
cp++;
- mandoc_escape((const char **)&cp, NULL, NULL);
- } else if (isspace((unsigned char)*cp))
- cp++;
- else
+ else if ( ! isspace((unsigned char)*cp))
break;
}
+ /* Skip trailing backslash. */
+
+ sz = strlen(cp);
+ if (cp[sz - 1] == '\\')
+ sz--;
+
/* Skip trailing whitespace. */
- for (sz = strlen(cp); sz; sz--)
+ for (; sz; sz--)
if ( ! isspace((unsigned char)cp[sz-1]))
break;
@@ -3364,7 +3365,8 @@ roff_strdup(const struct roff *r, const char *p)
ssz = 0;
while ('\0' != *p) {
- if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) {
+ assert((unsigned int)*p < 128);
+ if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
sz = r->xtab[(int)*p].sz;
res = mandoc_realloc(res, ssz + sz + 1);
memcpy(res + ssz, r->xtab[(int)*p].p, sz);
diff --git a/usr/src/cmd/mandoc/roff.h b/usr/src/cmd/mandoc/roff.h
index 19ec50f42e..b87cf132b7 100644
--- a/usr/src/cmd/mandoc/roff.h
+++ b/usr/src/cmd/mandoc/roff.h
@@ -1,7 +1,7 @@
-/* $OpenBSD$ */
+/* $Id: roff.h,v 1.40 2017/02/16 03:00:23 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -75,8 +75,7 @@ enum roff_next {
*/
enum mdoc_endbody {
ENDBODY_NOT = 0,
- ENDBODY_SPACE, /* Is broken: append a space. */
- ENDBODY_NOSPACE /* Is broken: don't append a space. */
+ ENDBODY_SPACE /* Is broken: append a space. */
};
struct roff_node {
@@ -98,17 +97,16 @@ struct roff_node {
int tok; /* Request or macro ID. */
#define TOKEN_NONE (-1) /* No request or macro. */
int flags;
-#define MDOC_VALID (1 << 0) /* Has been validated. */
-#define MDOC_ENDED (1 << 1) /* Gone past body end mark. */
-#define MDOC_EOS (1 << 2) /* At sentence boundary. */
-#define MDOC_LINE (1 << 3) /* First macro/text on line. */
-#define MDOC_SYNPRETTY (1 << 4) /* SYNOPSIS-style formatting. */
-#define MDOC_BROKEN (1 << 5) /* Must validate parent when ending. */
-#define MDOC_DELIMO (1 << 6)
-#define MDOC_DELIMC (1 << 7)
-#define MAN_VALID MDOC_VALID
-#define MAN_EOS MDOC_EOS
-#define MAN_LINE MDOC_LINE
+#define NODE_VALID (1 << 0) /* Has been validated. */
+#define NODE_ENDED (1 << 1) /* Gone past body end mark. */
+#define NODE_EOS (1 << 2) /* At sentence boundary. */
+#define NODE_LINE (1 << 3) /* First macro/text on line. */
+#define NODE_SYNPRETTY (1 << 4) /* SYNOPSIS-style formatting. */
+#define NODE_BROKEN (1 << 5) /* Must validate parent when ending. */
+#define NODE_DELIMO (1 << 6)
+#define NODE_DELIMC (1 << 7)
+#define NODE_NOSRC (1 << 8) /* Generated node, not in input file. */
+#define NODE_NOPRT (1 << 9) /* Shall not print anything. */
int prev_font; /* Before entering this node. */
int aux; /* Decoded node data, type-dependent. */
enum roff_type type; /* AST node type. */
diff --git a/usr/src/cmd/mandoc/tag.c b/usr/src/cmd/mandoc/tag.c
index baedf15ad9..21ac6b32e4 100644
--- a/usr/src/cmd/mandoc/tag.c
+++ b/usr/src/cmd/mandoc/tag.c
@@ -1,6 +1,6 @@
-/* $Id: tag.c,v 1.12 2016/07/08 20:42:15 schwarze Exp $ */
+/* $Id: tag.c,v 1.18 2017/02/17 14:31:52 schwarze Exp $ */
/*
- * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2015, 2016 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
@@ -31,12 +31,14 @@
#include "tag.h"
struct tag_entry {
- size_t line;
+ size_t *lines;
+ size_t maxlines;
+ size_t nlines;
int prio;
char s[];
};
-static void tag_signal(int);
+static void tag_signal(int) __attribute__((__noreturn__));
static struct ohash tag_data;
static struct tag_files tag_files;
@@ -128,18 +130,58 @@ tag_put(const char *s, int prio, size_t line)
size_t len;
unsigned int slot;
- if (tag_files.tfd <= 0 || strchr(s, ' ') != NULL)
+ /* Sanity checks. */
+
+ if (tag_files.tfd <= 0)
+ return;
+ if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e'))
+ s += 2;
+ if (*s == '\0' || strchr(s, ' ') != NULL)
return;
+
slot = ohash_qlookup(&tag_data, s);
entry = ohash_find(&tag_data, slot);
+
if (entry == NULL) {
+
+ /* Build a new entry. */
+
len = strlen(s) + 1;
entry = mandoc_malloc(sizeof(*entry) + len);
memcpy(entry->s, s, len);
+ entry->lines = NULL;
+ entry->maxlines = entry->nlines = 0;
ohash_insert(&tag_data, slot, entry);
- } else if (entry->prio <= prio)
- return;
- entry->line = line;
+
+ } else {
+
+ /* Handle priority 0 entries. */
+
+ if (prio == 0) {
+ if (entry->prio == 0)
+ entry->prio = -1;
+ return;
+ }
+
+ /* A better entry is already present, ignore the new one. */
+
+ if (entry->prio > 0 && entry->prio < prio)
+ return;
+
+ /* The existing entry is worse, clear it. */
+
+ if (entry->prio < 1 || entry->prio > prio)
+ entry->nlines = 0;
+ }
+
+ /* Remember the new line. */
+
+ if (entry->maxlines == entry->nlines) {
+ entry->maxlines += 4;
+ entry->lines = mandoc_reallocarray(entry->lines,
+ entry->maxlines, sizeof(*entry->lines));
+ }
+ entry->lines[entry->nlines++] = line;
entry->prio = prio;
}
@@ -152,6 +194,7 @@ tag_write(void)
{
FILE *stream;
struct tag_entry *entry;
+ size_t i;
unsigned int slot;
if (tag_files.tfd <= 0)
@@ -159,9 +202,11 @@ tag_write(void)
stream = fdopen(tag_files.tfd, "w");
entry = ohash_first(&tag_data, &slot);
while (entry != NULL) {
- if (stream != NULL)
- fprintf(stream, "%s %s %zu\n",
- entry->s, tag_files.ofn, entry->line);
+ 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);
}
@@ -176,11 +221,11 @@ tag_unlink(void)
pid_t tc_pgid;
if (tag_files.tcpgid != -1) {
- tc_pgid = tcgetpgrp(STDIN_FILENO);
+ tc_pgid = tcgetpgrp(tag_files.ofd);
if (tc_pgid == tag_files.pager_pid ||
tc_pgid == getpgid(0) ||
getpgid(tc_pgid) == -1)
- (void)tcsetpgrp(STDIN_FILENO, tag_files.tcpgid);
+ (void)tcsetpgrp(tag_files.ofd, tag_files.tcpgid);
}
if (*tag_files.ofn != '\0')
unlink(tag_files.ofn);
diff --git a/usr/src/cmd/mandoc/tbl_html.c b/usr/src/cmd/mandoc/tbl_html.c
index 51c43286cb..4dff29370a 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.18 2015/10/12 00:08:16 schwarze Exp $ */
+/* $Id: tbl_html.c,v 1.20 2017/02/05 18:15:39 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -50,9 +50,7 @@ html_tbl_strlen(const char *p, void *arg)
static void
html_tblopen(struct html *h, const struct tbl_span *sp)
{
- struct htmlpair tag;
- struct roffsu su;
- struct roffcol *col;
+ struct tag *t;
int ic;
if (h->tbl.cols == NULL) {
@@ -62,19 +60,12 @@ html_tblopen(struct html *h, const struct tbl_span *sp)
}
assert(NULL == h->tblt);
- PAIR_CLASS_INIT(&tag, "tbl");
- h->tblt = print_otag(h, TAG_TABLE, 1, &tag);
-
- for (ic = 0; ic < sp->opts->cols; ic++) {
- bufinit(h);
- col = h->tbl.cols + ic;
- SCALE_HS_INIT(&su, col->width);
- bufcat_su(h, "width", &su);
- PAIR_STYLE_INIT(&tag, h);
- print_otag(h, TAG_COL, 1, &tag);
- }
+ h->tblt = print_otag(h, TAG_TABLE, "c", "tbl");
- print_otag(h, TAG_TBODY, 0, NULL);
+ t = print_otag(h, TAG_COLGROUP, "");
+ for (ic = 0; ic < sp->opts->cols; ic++)
+ print_otag(h, TAG_COL, "shw", h->tbl.cols[ic].width);
+ print_tagq(h, t);
}
void
@@ -90,7 +81,6 @@ void
print_tbl(struct html *h, const struct tbl_span *sp)
{
const struct tbl_dat *dp;
- struct htmlpair tag;
struct tag *tt;
int ic;
@@ -104,19 +94,18 @@ print_tbl(struct html *h, const struct tbl_span *sp)
h->flags |= HTML_NONOSPACE;
h->flags |= HTML_NOSPACE;
- tt = print_otag(h, TAG_TR, 0, NULL);
+ tt = print_otag(h, TAG_TR, "");
switch (sp->pos) {
case TBL_SPAN_HORIZ:
case TBL_SPAN_DHORIZ:
- PAIR_INIT(&tag, ATTR_COLSPAN, "0");
- print_otag(h, TAG_TD, 1, &tag);
+ print_otag(h, TAG_TD, "?", "colspan", "0");
break;
default:
dp = sp->first;
for (ic = 0; ic < sp->opts->cols; ic++) {
print_stagq(h, tt);
- print_otag(h, TAG_TD, 0, NULL);
+ print_otag(h, TAG_TD, "");
if (dp == NULL || dp->layout->col > ic)
continue;
diff --git a/usr/src/cmd/mandoc/term.c b/usr/src/cmd/mandoc/term.c
index 0fd8f7c019..1217d473ca 100644
--- a/usr/src/cmd/mandoc/term.c
+++ b/usr/src/cmd/mandoc/term.c
@@ -1,7 +1,7 @@
-/* $Id: term.c,v 1.257 2016/04/12 15:30:00 schwarze Exp $ */
+/* $Id: term.c,v 1.259 2017/01/08 18:16:58 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -504,7 +504,9 @@ term_word(struct termp *p, const char *word)
}
}
/* Trim trailing backspace/blank pair. */
- if (p->col > 2 && p->buf[p->col - 1] == ' ')
+ if (p->col > 2 &&
+ (p->buf[p->col - 1] == ' ' ||
+ p->buf[p->col - 1] == '\t'))
p->col -= 2;
continue;
default:
@@ -568,7 +570,7 @@ encode1(struct termp *p, int c)
p->fontq[p->fonti] : TERMFONT_NONE;
if (p->flags & TERMP_BACKBEFORE) {
- if (p->buf[p->col - 1] == ' ')
+ if (p->buf[p->col - 1] == ' ' || p->buf[p->col - 1] == '\t')
p->col--;
else
p->buf[p->col++] = 8;
@@ -604,8 +606,20 @@ encode(struct termp *p, const char *word, size_t sz)
if (ASCII_HYPH == word[i] ||
isgraph((unsigned char)word[i]))
encode1(p, word[i]);
- else
+ else {
p->buf[p->col++] = word[i];
+
+ /*
+ * Postpone the effect of \z while handling
+ * an overstrike sequence from ascii_uc2str().
+ */
+
+ if (word[i] == '\b' &&
+ (p->flags & TERMP_BACKBEFORE)) {
+ p->flags &= ~TERMP_BACKBEFORE;
+ p->flags |= TERMP_BACKAFTER;
+ }
+ }
}
}
diff --git a/usr/src/cmd/mandoc/term_ascii.c b/usr/src/cmd/mandoc/term_ascii.c
index fecdb0a964..df5ff13901 100644
--- a/usr/src/cmd/mandoc/term_ascii.c
+++ b/usr/src/cmd/mandoc/term_ascii.c
@@ -1,4 +1,4 @@
-/* $Id: term_ascii.c,v 1.53 2016/07/08 22:29:05 schwarze Exp $ */
+/* $Id: term_ascii.c,v 1.54 2016/07/31 09:29:13 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -98,7 +98,7 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
v = TERMENC_LOCALE == enc ?
setlocale(LC_CTYPE, "") :
- setlocale(LC_CTYPE, "en_US.UTF-8");
+ setlocale(LC_CTYPE, UTF8_LOCALE);
if (NULL != v && MB_CUR_MAX > 1) {
p->enc = enc;
p->advance = locale_advance;
diff --git a/usr/src/cmd/mandoc/term_ps.c b/usr/src/cmd/mandoc/term_ps.c
index 6105d5589f..696ff22435 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.80 2015/12/23 20:50:13 schwarze Exp $ */
+/* $Id: term_ps.c,v 1.83 2017/02/17 14:31:52 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2014, 2015, 2016 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
@@ -98,15 +98,13 @@ static void ps_begin(struct termp *);
static void ps_closepage(struct termp *);
static void ps_end(struct termp *);
static void ps_endline(struct termp *);
-static void ps_fclose(struct termp *);
static void ps_growbuf(struct termp *, size_t);
static void ps_letter(struct termp *, int);
static void ps_pclose(struct termp *);
+static void ps_plast(struct termp *);
static void ps_pletter(struct termp *, int);
-#if __GNUC__ - 0 >= 4
-__attribute__((__format__ (__printf__, 2, 3)))
-#endif
-static void ps_printf(struct termp *, const char *, ...);
+static void ps_printf(struct termp *, const char *, ...)
+ __attribute__((__format__ (__printf__, 2, 3)));
static void ps_putchar(struct termp *, char);
static void ps_setfont(struct termp *, enum termfont);
static void ps_setwidth(struct termp *, int, int);
@@ -782,6 +780,9 @@ ps_end(struct termp *p)
{
size_t i, xref, base;
+ ps_plast(p);
+ ps_pclose(p);
+
/*
* At the end of the file, do one last showpage. This is the
* same behaviour as groff(1) and works for multiple pages as
@@ -1025,39 +1026,53 @@ ps_pclose(struct termp *p)
p->ps->flags &= ~PS_INLINE;
}
+/* If we have a `last' char that wasn't printed yet, print it now. */
static void
-ps_fclose(struct termp *p)
+ps_plast(struct termp *p)
{
+ size_t wx;
+
+ if (p->ps->last == '\0')
+ return;
+
+ /* Check the font mode; open a new scope if it doesn't match. */
+
+ if (p->ps->nextf != p->ps->lastf) {
+ ps_pclose(p);
+ ps_setfont(p, p->ps->nextf);
+ }
+ p->ps->nextf = TERMFONT_NONE;
/*
- * Strong closure: if we have a last-char, spit it out after
- * checking that we're in the right font mode. This will of
- * course open a new scope, if applicable.
- *
- * Following this, close out any scope that's open.
+ * For an overstrike, if a previous character
+ * was wider, advance to center the new one.
*/
- if (p->ps->last != '\0') {
- assert( ! (p->ps->flags & PS_BACKSP));
- if (p->ps->nextf != p->ps->lastf) {
- ps_pclose(p);
- ps_setfont(p, p->ps->nextf);
- }
- p->ps->nextf = TERMFONT_NONE;
- ps_pletter(p, p->ps->last);
- p->ps->last = '\0';
+ if (p->ps->pscolnext) {
+ wx = fonts[p->ps->lastf].gly[(int)p->ps->last-32].wx;
+ if (p->ps->pscol + wx < p->ps->pscolnext)
+ p->ps->pscol = (p->ps->pscol +
+ p->ps->pscolnext - wx) / 2;
}
- if ( ! (PS_INLINE & p->ps->flags))
- return;
+ ps_pletter(p, p->ps->last);
+ p->ps->last = '\0';
- ps_pclose(p);
+ /*
+ * For an overstrike, if a previous character
+ * was wider, advance to the end of the old one.
+ */
+
+ if (p->ps->pscol < p->ps->pscolnext) {
+ ps_pclose(p);
+ p->ps->pscol = p->ps->pscolnext;
+ }
}
static void
ps_letter(struct termp *p, int arg)
{
- size_t savecol, wx;
+ size_t savecol;
char c;
c = arg >= 128 || arg <= 0 ? '?' : arg;
@@ -1123,43 +1138,12 @@ ps_letter(struct termp *p, int arg)
* Use them and print it.
*/
- if (p->ps->last != '\0') {
- if (p->ps->nextf != p->ps->lastf) {
- ps_pclose(p);
- ps_setfont(p, p->ps->nextf);
- }
- p->ps->nextf = TERMFONT_NONE;
-
- /*
- * For an overstrike, if a previous character
- * was wider, advance to center the new one.
- */
-
- if (p->ps->pscolnext) {
- wx = fonts[p->ps->lastf].gly[(int)p->ps->last-32].wx;
- if (p->ps->pscol + wx < p->ps->pscolnext)
- p->ps->pscol = (p->ps->pscol +
- p->ps->pscolnext - wx) / 2;
- }
-
- ps_pletter(p, p->ps->last);
-
- /*
- * For an overstrike, if a previous character
- * was wider, advance to the end of the old one.
- */
-
- if (p->ps->pscol < p->ps->pscolnext) {
- ps_pclose(p);
- p->ps->pscol = p->ps->pscolnext;
- }
- }
+ ps_plast(p);
/*
* Do not print the current character yet because font
- * instructions might follow; only remember it.
- * For the first character, nothing else is done.
- * The final character will get printed from ps_fclose().
+ * instructions might follow; only remember the character.
+ * It will get printed later from ps_plast().
*/
p->ps->last = c;
@@ -1192,7 +1176,8 @@ ps_advance(struct termp *p, size_t len)
* and readjust our column settings.
*/
- ps_fclose(p);
+ ps_plast(p);
+ ps_pclose(p);
p->ps->pscol += len;
}
@@ -1202,7 +1187,8 @@ ps_endline(struct termp *p)
/* Close out any scopes we have open: we're at eoln. */
- ps_fclose(p);
+ ps_plast(p);
+ ps_pclose(p);
/*
* If we're in the margin, don't try to recalculate our current
diff --git a/usr/src/cmd/mandoc/tree.c b/usr/src/cmd/mandoc/tree.c
index 52ca7547f4..dd36ff594e 100644
--- a/usr/src/cmd/mandoc/tree.c
+++ b/usr/src/cmd/mandoc/tree.c
@@ -1,7 +1,7 @@
-/* $Id: tree.c,v 1.69 2015/10/12 00:08:16 schwarze Exp $ */
+/* $Id: tree.c,v 1.73 2017/02/10 15:45:28 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -33,6 +33,7 @@
static void print_box(const struct eqn_box *, int);
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);
static void print_span(const struct tbl_span *, int);
@@ -40,18 +41,41 @@ static void print_span(const struct tbl_span *, int);
void
tree_mdoc(void *arg, const struct roff_man *mdoc)
{
-
+ print_meta(&mdoc->meta);
+ putchar('\n');
print_mdoc(mdoc->first->child, 0);
}
void
tree_man(void *arg, const struct roff_man *man)
{
-
+ print_meta(&man->meta);
+ if (man->meta.hasbody == 0)
+ puts("body = empty");
+ putchar('\n');
print_man(man->first->child, 0);
}
static void
+print_meta(const struct roff_meta *meta)
+{
+ if (meta->title != NULL)
+ printf("title = \"%s\"\n", meta->title);
+ if (meta->name != NULL)
+ printf("name = \"%s\"\n", meta->name);
+ if (meta->msec != NULL)
+ printf("sec = \"%s\"\n", meta->msec);
+ if (meta->vol != NULL)
+ printf("vol = \"%s\"\n", meta->vol);
+ if (meta->arch != NULL)
+ printf("arch = \"%s\"\n", meta->arch);
+ if (meta->os != NULL)
+ printf("os = \"%s\"\n", meta->os);
+ if (meta->date != NULL)
+ printf("date = \"%s\"\n", meta->date);
+}
+
+static void
print_mdoc(const struct roff_node *n, int indent)
{
const char *p, *t;
@@ -159,15 +183,21 @@ print_mdoc(const struct roff_node *n, int indent)
}
putchar(' ');
- if (MDOC_DELIMO & n->flags)
+ if (NODE_DELIMO & n->flags)
putchar('(');
- if (MDOC_LINE & n->flags)
+ if (NODE_LINE & n->flags)
putchar('*');
printf("%d:%d", n->line, n->pos + 1);
- if (MDOC_DELIMC & n->flags)
+ if (NODE_DELIMC & n->flags)
putchar(')');
- if (MDOC_EOS & n->flags)
+ if (NODE_EOS & n->flags)
putchar('.');
+ if (NODE_BROKEN & n->flags)
+ printf(" BROKEN");
+ if (NODE_NOSRC & n->flags)
+ printf(" NOSRC");
+ if (NODE_NOPRT & n->flags)
+ printf(" NOPRT");
putchar('\n');
}
@@ -248,10 +278,10 @@ print_man(const struct roff_node *n, int indent)
for (i = 0; i < indent; i++)
putchar(' ');
printf("%s (%s) ", p, t);
- if (MAN_LINE & n->flags)
+ if (NODE_LINE & n->flags)
putchar('*');
printf("%d:%d", n->line, n->pos + 1);
- if (MAN_EOS & n->flags)
+ if (NODE_EOS & n->flags)
putchar('.');
putchar('\n');
}
diff --git a/usr/src/lib/libc/port/gen/fts.c b/usr/src/lib/libc/port/gen/fts.c
index eac375ee6a..7cdd4e984b 100644
--- a/usr/src/lib/libc/port/gen/fts.c
+++ b/usr/src/lib/libc/port/gen/fts.c
@@ -551,7 +551,7 @@ fts_build(FTS *sp, int type)
size_t len, maxlen;
int nitems, cderrno, descend, level, nlinks, doadjust;
int saved_errno;
- char *cp;
+ char *cp = NULL;
/* Set current node pointer. */
cur = sp->fts_cur;
diff --git a/usr/src/man/man1/mandoc.1 b/usr/src/man/man1/mandoc.1
index d66d47a491..1cddf8ac75 100644
--- a/usr/src/man/man1/mandoc.1
+++ b/usr/src/man/man1/mandoc.1
@@ -1,7 +1,7 @@
-.\" $Id: mandoc.1,v 1.164 2015/11/05 17:47:51 schwarze Exp $
+.\" $Id: mandoc.1,v 1.174 2017/02/10 15:45:28 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
-.\" Copyright (c) 2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2012, 2014-2017 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -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: November 5 2015 $
+.Dd $Mdocdate: February 10 2017 $
.Dt MANDOC 1
.Os
.Sh NAME
@@ -75,6 +75,10 @@ This overrides any earlier
and
.Fl l
options.
+.It Fl h
+Display only the SYNOPSIS lines.
+Implies
+.Fl c .
.It Fl I Cm os Ns = Ns Ar name
Override the default operating system
.Ar name
@@ -85,10 +89,6 @@ and for the
.Xr man 5
.Sq \&TH
macro.
-.It Fl h
-Display only the SYNOPSIS lines.
-Implies
-.Fl c .
.It Fl K Ar encoding
Specify the input encoding.
The supported
@@ -463,7 +463,15 @@ Use
to show a human readable representation of the syntax tree.
It is useful for debugging the source code of manual pages.
The exact format is subject to change, so don't write parsers for it.
-Each output line shows one syntax tree node.
+.Pp
+The first paragraph shows meta data found in the
+.Xr mdoc 5
+prologue, on the
+.Xr man 5
+.Ic \&TH
+line, or the fallbacks used.
+.Pp
+In the tree dump, each output line shows one syntax tree node.
Child nodes are indented with respect to their parent node.
The columns are:
.Pp
@@ -494,8 +502,27 @@ The input column number (starting at one).
A closing parenthesis if the node is a closing delimiter.
.It
A full stop if the node ends a sentence.
+.It
+BROKEN if the node is a block broken by another block.
+.It
+NOSRC if the node is not in the input file,
+but automatically generated from macros.
+.It
+NOPRT if the node is not supposed to generate output
+for any output format.
.El
.El
+.Pp
+The following
+.Fl O
+argument is accepted:
+.Bl -tag -width Ds
+.It Cm noval
+Skip validation and show the unvalidated syntax tree.
+This can help to find out whether a given behaviour is caused by
+the parser or by the validator.
+Meta data is not available in this case.
+.El
.Sh EXIT STATUS
The
.Nm
@@ -758,11 +785,13 @@ This may confuse
.Xr makewhatis 8
and
.Xr apropos 1 .
-.It Sy "NAME section without name"
+.It Sy "NAME section without Nm before Nd"
.Pq mdoc
The NAME section does not contain any
.Ic \&Nm
-child macro.
+child macro before the first
+.Ic \&Nd
+macro.
.It Sy "NAME section without description"
.Pq mdoc
The NAME section lacks the mandatory
@@ -779,6 +808,11 @@ The NAME section contains plain text or macros other than
.Ic \&Nm
and
.Ic \&Nd .
+.It Sy "missing comma before name"
+.Pq mdoc
+The NAME section contains an
+.Ic \&Nm
+macro that is neither the first one nor preceded by a comma.
.It Sy "missing description line, using \(dq\(dq"
.Pq mdoc
The
@@ -1096,6 +1130,13 @@ macro is immediately followed by an
.Ic \&Re
macro on the next input line.
Such an empty block does not produce any output.
+.It Sy "missing section argument"
+.Pq mdoc
+An
+.Ic \&Xr
+macro lacks its second, section number argument.
+The first argument, i.e. the name, is printed, but without subsequent
+parentheses.
.It Sy "missing -std argument, adding it"
.Pq mdoc
An
@@ -1278,6 +1319,10 @@ it is hard to predict which tab stop position the tab will advance to.
Whitespace at the end of input lines is almost never semantically
significant \(em but in the odd case where it might be, it is
extremely confusing when reviewing and maintaining documents.
+.It Sy "new sentence, new line"
+.Pq mdoc
+A new sentence starts in the middle of a text line.
+Start it on a new input line to help formatters produce correct spacing.
.It Sy "bad comment style"
.Pq roff
Comment lines start with a dot, a backslash, and a double-quote character.
@@ -1564,8 +1609,8 @@ macro fails to specify the list type.
.It Sy "missing manual name, using \(dq\(dq"
.Pq mdoc
The first call to
-.Ic \&Nm
-lacks the required argument.
+.Ic \&Nm ,
+or any call in the NAME section, lacks the required argument.
.It Sy "uname(3) system call failed, using UNKNOWN"
.Pq mdoc
The
@@ -1751,6 +1796,19 @@ as if they were a text line.
.Xr mandoc_roff 5 ,
.Xr mdoc 5 ,
.Xr tbl 5
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Ox 4.8 .
+The option
+.Fl I
+appeared in
+.Ox 5.2 ,
+and
+.Fl aCcfhKklMSsw
+in
+.Ox 5.7 .
.Sh AUTHORS
.An -nosplit
The
@@ -1759,12 +1817,3 @@ utility was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
and is maintained by
.An Ingo Schwarze Aq Mt schwarze@openbsd.org .
-.Sh BUGS
-In
-.Fl T Cm html ,
-the maximum size of an element attribute is determined by
-.Dv BUFSIZ ,
-which is usually 1024 bytes.
-Be aware of this when setting long link
-formats such as
-.Fl O Cm style Ns = Ns Ar really/long/link .
diff --git a/usr/src/man/man5/mandoc_char.5 b/usr/src/man/man5/mandoc_char.5
index cf102bb840..16b1f4b94f 100644
--- a/usr/src/man/man5/mandoc_char.5
+++ b/usr/src/man/man5/mandoc_char.5
@@ -1,4 +1,4 @@
-.\" $Id: mandoc_char.7,v 1.63 2015/09/02 15:38:35 schwarze Exp $
+.\" $Id: mandoc_char.7,v 1.64 2017/02/05 21:41:21 schwarze Exp $
.\"
.\" Copyright (c) 2003 Jason McIntyre <jmc@openbsd.org>
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -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: September 2 2015 $
+.Dd $Mdocdate: February 5 2017 $
.Dt MANDOC_CHAR 5
.Os
.Sh NAME
@@ -62,6 +62,30 @@ blue-eyed
lorry-driver
.Ed
.Pp
+If a word on a text input line contains a hyphen, a formatter may decide
+to insert an output line break after the hyphen if that helps filling
+the current output line, but the whole word would overflow the line.
+If it is important that the word is not broken across lines in this
+way, a zero-width space
+.Pq Sq \e&
+can be inserted before or after the hyphen.
+While
+.Xr mandoc 1
+never breaks the output line after hyphens adjacent to a zero-width
+space, after any of the other dash- or hyphen-like characters
+represented by escape sequences, or after hyphens inside words in
+macro arguments, other software may not respect these rules and may
+break the line even in such cases.
+.Pp
+Some
+.Xr mandoc_roff 5
+implementations contains dictionaries allowing to break the line
+at syllable boundaries even inside words that contain no hyphens.
+Such automatic hyphenation is not supported by
+.Xr mandoc 1 ,
+which only breaks the line at whitespace, and inside words only
+after existing hyphens.
+.Pp
The mathematical minus sign is used for negative numbers or subtraction.
It should be written as
.Sq \e(mi :
diff --git a/usr/src/man/man5/mdoc.5 b/usr/src/man/man5/mdoc.5
index 6d81124b02..d0123d86c5 100644
--- a/usr/src/man/man5/mdoc.5
+++ b/usr/src/man/man5/mdoc.5
@@ -1,7 +1,7 @@
-.\" $Id: mdoc.7,v 1.257 2015/11/05 12:06:45 schwarze Exp $
+.\" $Id: mdoc.7,v 1.262 2017/02/16 14:38:12 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
-.\" Copyright (c) 2010, 2011, 2013 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2010, 2011, 2013-2017 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -19,7 +19,7 @@
.\" Copyright 2014 Garrett D'Amore <garrett@damore.org>
.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
.\"
-.Dd $Mdocdate: November 5 2015 $
+.Dd $Mdocdate: February 16 2017 $
.Dt MDOC 5
.Os
.Sh NAME
@@ -1099,10 +1099,8 @@ argument.
A columnated list.
The
.Fl width
-argument has no effect; instead, each argument specifies the width
-of one column, using either the scaling width syntax described in
-.Xr mandoc_roff 5
-or the string length of the argument.
+argument has no effect; instead, the string length of each argument
+specifies the width of one column.
If the first line of the body of a
.Fl column
list is not an
@@ -1922,14 +1920,25 @@ The
list is the most complicated.
Its syntax is as follows:
.Pp
-.D1 Pf \. Sx \&It Ar cell Op <TAB> Ar cell ...
.D1 Pf \. Sx \&It Ar cell Op Sx \&Ta Ar cell ...
+.D1 Pf \. Sx \&It Ar cell Op <TAB> Ar cell ...
.Pp
The arguments consist of one or more lines of text and macros
representing a complete table line.
-Cells within the line are delimited by tabs or by the special
+Cells within the line are delimited by the special
.Sx \&Ta
-block macro.
+block macro or by literal tab characters.
+.Pp
+Using literal tabs is strongly discouraged because they are very
+hard to use correctly and
+.Nm
+code using them is very hard to read.
+In particular, a blank character is syntactically significant
+before and after the literal tab character.
+If a word precedes or follows the tab without an intervening blank,
+that word is never interpreted as a macro call, but always output
+literally.
+.Pp
The tab cell delimiter may only be used within the
.Sx \&It
line itself; on following lines, only the
@@ -1944,9 +1953,10 @@ Note that quoted strings may span tab-delimited cells on an
line.
For example,
.Pp
-.Dl .It \(dqcol1 ; <TAB> col2 ;\(dq \&;
+.Dl .It \(dqcol1 ,\& <TAB> col2 ,\(dq \&;
.Pp
-will preserve the semicolon whitespace except for the last.
+will preserve the whitespace before both commas,
+but not the whitespace before the semicolon.
.Pp
See also
.Sx \&Bl .
@@ -2805,14 +2815,13 @@ Link to another manual
.Pq Qq cross-reference .
Its syntax is as follows:
.Pp
-.D1 Pf \. Sx \&Xr Ar name Op section
+.D1 Pf \. Sx \&Xr Ar name section
.Pp
Cross reference the
.Ar name
and
.Ar section
-number of another man page;
-omitting the section number is rarely useful.
+number of another man page.
.Pp
Examples:
.Dl \&.Xr mandoc 1
@@ -3124,7 +3133,7 @@ then the macro accepts an arbitrary number of arguments.
.It Sx \&Ux Ta Yes Ta Yes Ta n
.It Sx \&Va Ta Yes Ta Yes Ta n
.It Sx \&Vt Ta Yes Ta Yes Ta >0
-.It Sx \&Xr Ta Yes Ta Yes Ta >0
+.It Sx \&Xr Ta Yes Ta Yes Ta 2
.It Sx \&br Ta \&No Ta \&No Ta 0
.It Sx \&sp Ta \&No Ta \&No Ta 1
.El
@@ -3144,6 +3153,8 @@ For many macros, when the leading arguments are opening delimiters,
these delimiters are put before the macro scope,
and when the trailing arguments are closing delimiters,
these delimiters are put after the macro scope.
+Spacing is suppressed after opening delimiters
+and before closing delimiters.
For example,
.Pp
.D1 Pf \. \&Aq "( [ word ] ) ."
@@ -3200,7 +3211,7 @@ renders as:
.D1 Fl a ( b | c \*(Ba d ) e
.Pp
This applies to both opening and closing delimiters,
-and also to the middle delimiter:
+and also to the middle delimiter, which does not suppress spacing:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It \&|
@@ -3308,6 +3319,12 @@ but produces large indentations.
.Xr mandoc_char 5 ,
.Xr mandoc_roff 5 ,
.Xr tbl 5
+.Pp
+The web page
+.Lk http://mdocml.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.
.Sh HISTORY
The
.Nm
diff --git a/usr/src/tools/mandoc/Makefile b/usr/src/tools/mandoc/Makefile
index 7f8b3b8f2a..9412090f4d 100644
--- a/usr/src/tools/mandoc/Makefile
+++ b/usr/src/tools/mandoc/Makefile
@@ -19,9 +19,12 @@ LCDIR= $(SRC)/lib/libc/port
include $(SRC)/tools/Makefile.tools
include $(CMDDIR)/Makefile.common
-OBJS += reallocarray.o \
+OBJS += fts.o \
+ reallocarray.o \
strtonum.o
+CPPFLAGS += -I. -include fts.h
+
.KEEP_STATE:
all: $(PROG)
diff --git a/usr/src/tools/mandoc/fts.h b/usr/src/tools/mandoc/fts.h
new file mode 100644
index 0000000000..c1e4d26603
--- /dev/null
+++ b/usr/src/tools/mandoc/fts.h
@@ -0,0 +1,128 @@
+/*
+ * 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 is a compat version of fts.h including reallocarray() and strtonum()
+ * prototypes to be used for usr/src/tools/mandoc build.
+ */
+
+#ifndef _FTS_H
+#define _FTS_H
+
+#include <sys/types.h>
+
+extern void *reallocarray(void *, size_t, size_t);
+extern long long strtonum(const char *, long long, long long, const char **);
+
+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 */
diff --git a/usr/src/tools/scripts/webrev.sh b/usr/src/tools/scripts/webrev.sh
index fc2eefe5c2..0d47181d62 100644
--- a/usr/src/tools/scripts/webrev.sh
+++ b/usr/src/tools/scripts/webrev.sh
@@ -147,59 +147,114 @@ span.new {
</style>
'
-#
# CSS for the HTML version of the man pages.
-#
+# Current version is from mdocml-1.14.1.
MANCSS='
-html { max-width: 880px; margin-left: 1em; }
-body { font-size: smaller; font-family: Helvetica,Arial,sans-serif; }
-h1 { margin-bottom: 1ex; font-size: 110%; margin-left: -4ex; }
-h2 { margin-bottom: 1ex; font-size: 105%; margin-left: -2ex; }
-table { width: 100%; margin-top: 0ex; margin-bottom: 0ex; }
+html { max-width: 100ex; }
+body { font-family: Helvetica,Arial,sans-serif; }
+table { margin-top: 0em; margin-bottom: 0em; }
td { vertical-align: top; }
-blockquote { margin-left: 5ex; margin-top: 0ex; margin-bottom: 0ex; }
-div.section { margin-bottom: 2ex; margin-left: 5ex; }
-table.foot { font-size: smaller; margin-top: 1em;
- border-top: 1px dotted #dddddd; }
-td.foot-date { width: 50%; }
-td.foot-os { width: 50%; text-align: right; }
-table.head { font-size: smaller; margin-bottom: 1em;
- border-bottom: 1px dotted #dddddd; }
-td.head-ltitle { width: 10%; }
-td.head-vol { width: 80%; text-align: center; }
-td.head-rtitle { width: 10%; text-align: right; }
-.emph { font-style: italic; font-weight: normal; }
-.symb { font-style: normal; font-weight: bold; }
-.lit { font-style: normal; font-weight: normal; font-family: monospace; }
-i.addr { font-weight: normal; }
-i.arg { font-weight: normal; }
-b.cmd { font-style: normal; }
-b.config { font-style: normal; }
-b.diag { font-style: normal; }
-i.farg { font-weight: normal; }
-i.file { font-weight: normal; }
-b.flag { font-style: normal; }
-b.fname { font-style: normal; }
-i.ftype { font-weight: normal; }
-b.includes { font-style: normal; }
-i.link-sec { font-weight: normal; }
-b.macro { font-style: normal; }
-b.name { font-style: normal; }
-i.ref-book { font-weight: normal; }
-i.ref-issue { font-weight: normal; }
-i.ref-jrnl { font-weight: normal; }
-span.ref-title { text-decoration: underline; }
-span.type { font-style: italic; font-weight: normal; }
-b.utility { font-style: normal; }
-b.var { font-style: normal; }
-dd.list-ohang { margin-left: 0ex; }
-ul.list-bul { list-style-type: disc; padding-left: 1em; }
-ul.list-dash { list-style-type: none; padding-left: 0em; }
-li.list-dash:before { content: "\2014 "; }
-ul.list-hyph { list-style-type: none; padding-left: 0em; }
-li.list-hyph:before { content: "\2013 "; }
-ul.list-item { list-style-type: none; padding-left: 0em; }
-ol.list-enum { padding-left: 2em; }
+ul, ol, dl { margin-top: 0em; margin-bottom: 0em; }
+li, dt { margin-top: 1em; }
+fieldset { border: thin solid silver; border-radius: 1em; text-align: center; }
+input[name=expr] { width: 25%; }
+table.results { margin-top: 1em; margin-left: 2em; font-size: smaller; }
+table.head { width: 100%; border-bottom: 1px dotted #808080; margin-bottom: 1em;
+ font-size: smaller; }
+td.head-vol { text-align: center; }
+td.head-rtitle { text-align: right; }
+span.Nd { }
+table.foot { width: 100%; border-top: 1px dotted #808080; margin-top: 1em;
+ font-size: smaller; }
+td.foot-os { text-align: right; }
+div.manual-text { margin-left: 5ex; }
+h1.Sh { margin-top: 2ex; margin-bottom: 1ex; margin-left: -4ex;
+ font-size: 110%; }
+h2.Ss { margin-top: 2ex; margin-bottom: 1ex; margin-left: -2ex;
+ font-size: 105%; }
+div.Pp { margin: 1ex 0ex; }
+a.Sx { }
+a.Xr { }
+div.Bd { }
+div.D1 { margin-left: 5ex; }
+ul.Bl-bullet { list-style-type: disc; padding-left: 1em; }
+li.It-bullet { }
+ul.Bl-dash { list-style-type: none; padding-left: 0em; }
+li.It-dash:before { content: "\2014 "; }
+ul.Bl-item { list-style-type: none; padding-left: 0em; }
+li.It-item { }
+ol.Bl-enum { padding-left: 2em; }
+li.It-enum { }
+dl.Bl-diag { }
+dt.It-diag { }
+dd.It-diag { }
+b.It-diag { font-style: normal; }
+dl.Bl-hang { }
+dt.It-hang { }
+dd.It-hang { }
+dl.Bl-inset { }
+dt.It-inset { }
+dd.It-inset { }
+dl.Bl-ohang { }
+dt.It-ohang { }
+dd.It-ohang { margin-left: 0ex; }
+dl.Bl-tag { margin-left: 8ex; }
+dt.It-tag { float: left; clear: both; margin-top: 0ex; margin-left: -8ex;
+ padding-right: 2ex; vertical-align: top; }
+dd.It-tag { width: 100%; margin-top: 0ex; margin-left: 0ex; vertical-align: top;
+ overflow: auto; }
+table.Bl-column { }
+tr.It-column { }
+td.It-column { margin-top: 1em; }
+cite.Rs { font-style: normal; font-weight: normal; }
+span.RsA { }
+i.RsB { font-weight: normal; }
+span.RsC { }
+span.RsD { }
+i.RsI { font-weight: normal; }
+i.RsJ { font-weight: normal; }
+span.RsN { }
+span.RsO { }
+span.RsP { }
+span.RsQ { }
+span.RsR { }
+span.RsT { text-decoration: underline; }
+a.RsU { }
+span.RsV { }
+span.eqn { }
+table.tbl { }
+table.Nm { }
+b.Nm { font-style: normal; }
+b.Fl { font-style: normal; }
+b.Cm { font-style: normal; }
+var.Ar { font-style: italic; font-weight: normal; }
+span.Op { }
+b.Ic { font-style: normal; }
+code.Ev { font-style: normal; font-weight: normal; font-family: monospace; }
+i.Pa { font-weight: normal; }
+span.Lb { }
+b.In { font-style: normal; }
+a.In { }
+b.Fd { font-style: normal; }
+var.Ft { font-style: italic; font-weight: normal; }
+b.Fn { font-style: normal; }
+var.Fa { font-style: italic; font-weight: normal; }
+var.Vt { font-style: italic; font-weight: normal; }
+var.Va { font-style: italic; font-weight: normal; }
+code.Dv { font-style: normal; font-weight: normal; font-family: monospace; }
+code.Er { font-style: normal; font-weight: normal; font-family: monospace; }
+span.An { }
+a.Lk { }
+a.Mt { }
+b.Cd { font-style: normal; }
+i.Ad { font-weight: normal; }
+b.Ms { font-style: normal; }
+span.St { }
+a.Ux { }
+.No { font-style: normal; font-weight: normal; }
+.Em { font-style: italic; font-weight: normal; }
+.Sy { font-style: normal; font-weight: bold; }
+.Li { font-style: normal; font-weight: normal; font-family: monospace; }
'
#