Import mandoc 1.13.1

This commit is contained in:
Baptiste Daroussin 2014-11-22 18:08:25 +00:00
parent 53f5f26c3e
commit 52c0e9552d
120 changed files with 14293 additions and 10724 deletions

187
INSTALL Normal file
View File

@ -0,0 +1,187 @@
$Id: INSTALL,v 1.2 2014/08/10 17:22:26 schwarze Exp $
About mdocml, the portable mandoc distribution
----------------------------------------------
The mandoc manpage compiler toolset is a suite of tools compiling
mdoc(7), the roff(7) macro language of choice for BSD manual pages,
and man(7), the predominant historical language for UNIX manuals.
The toolset does not yet implement man(1); that is only scheduled
for the next release, 1.13.2. It can, however, already serve to
translate source manpages to the output displayed by man(1).
For general information, see <http://mdocml.bsd.lv/>.
In this document, we describe the installation and deployment of
mandoc(1), first as a simple, standalone formatter, and then as part of
the man(1) system.
In case you have questions or want to provide feedback, read
<http://mdocml.bsd.lv/contact.html>. Consider subscribing to the
discuss@ mailing list mentioned on that page. If you intend to
help with the development of mandoc, consider subscribing to the
tech@ mailing list, too.
Enjoy using the mandoc toolset!
Ingo Schwarze, Karlsruhe, August 2014
Installation
------------
Before manually installing mandoc on your system, please check
whether the newest version of mandoc is already installed by default
or available via a binary package or a ports system. A list of the
latest bundled and ported versions of mandoc for various operating
systems is maintained at <http://mdocml.bsd.lv/ports.html>.
If mandoc is installed, you can check the version by running "mandoc -V".
The version contained in this distribution tarball is listed near
the beginning of the file "Makefile".
Regarding how packages and ports are maintained for your operating
system, please consult your operating system documentation.
To install mandoc manually, the following steps are needed:
1. Decide whether you want to build the base tools mandoc(1),
preconv(1) and demandoc(1) only or whether you also want to build the
database tools apropos(1) and makewhatis(8). For the latter,
the following dependencies are required:
1.1. The SQLite database system, see <http://sqlite.org/>.
The recommended version of SQLite is 3.8.4.3 or newer. The mandoc
toolset is known to work with version 3.7.5 or newer. Versions
older than 3.8.3 may not achieve full performance due to the
missing SQLITE_DETERMINISTIC optimization flag. Versions older
than 3.8.0 may not show full error information if opening a database
fails due to the missing sqlite3_errstr() API. Both are very minor
problems, apropos(1) is fully usable with SQLite 3.7.5. Versions
older than 3.7.5 may or may not work, they have not been tested.
1.2. The fts(3) directory traversion functions.
A compatibility version will be bundled for 1.13.2 but is not available
yet. If you want apropos(1) and makewhatis(8) but do not have fts(3),
please stay with mandoc 1.12.3 for now and upgrade first to 1.12.4,
then to 1.13.2 when these versionns are released. Be careful: the
glibc version of fts(3) is known to be broken on 32bit platforms,
see <https://sourceware.org/bugzilla/show_bug.cgi?id=15838>.
1.3. Marc Espie's ohash(3) library.
If your system does not have it, the bundled compatibility version
will be used, so you probably need not worry about it.
2. If you choose to build the database tools, too, decide whether
you also want to build the CGI program, man.cgi(8).
3. Read the beginning of the file "Makefile" from "USER SETTINGS"
to "END OF USER SETTINGS" and edit it as required. In particular,
disable "BUILD_TARGETS += db-build" if you do not want database
support or enable "BUILD_TARGETS += cgi-build" if you do want
the CGI program.
4. Run "make". No separate "./configure" or "make depend" steps
are needed. The former is run automatically by "make". The latter
is a maintainer target. If you merely want to build the released
version as opposed to doing active development, there is no need
to regenerate the dependency specifications. Any POSIX-compatible
make, in particular both BSD make and GNU make, should work.
5. Run "make -n install" and check whether everything will be
installed to the intended places. Otherwise, edit the *DIR variables
in the Makefile until it is.
6. Run "sudo make install". If you intend to build a binary
package using some kind of fake root mechanism, you may need a
command like "make DESTDIR=... install". Read the *-install targets
in the "Makefile" to understand how DESTDIR is used.
7. To set up a man.cgi(8) server, read its manual page.
8. To use mandoc(1) as your man(1) formatter, read the "Deployment"
section below.
Checking autoconfiguration quality
----------------------------------
If you want to check whether automatic configuration works well
on your platform, consider the following:
The mandoc package intentionally does not use GNU autoconf because
we consider that toolset a blatant example of overengineering that
is obsolete nowadays, since all modern operating systems are now
reasonably close to POSIX and do not need arcane shell magic any
longer. If your system does need such magic, consider upgrading
to reasonably modern POSIX-compliant tools rather than asking for
autoconf-style workarounds.
As far as mandoc is using any features not mandated by ANSI X3.159-1989
("ANSI C") or IEEE Std 1003.1-2008 ("POSIX") that some modern systems
do not have, we intend to provide autoconfiguration tests and
compat_*.c implementations. Please report any that turn out to be
missing. Note that while we do strive to produce portable code,
we do not slavishly restrict ourselves to POSIX-only interfaces.
For improved security and readability, we do use well-designed,
modern interfaces like reallocarray(3) even if they are still rather
uncommon, of course bundling compat_*.c implementations as needed.
Where mandoc is using ANSI C or POSIX features that some systems
still lack and that compat_*.c implementations can be provided for
without too much hassle, we will consider adding them, too, so
please report whatever is missing on your platform.
The following steps can be used to manually check the automatic
configuration on your platform:
1. Run "make clean".
2. Run "make config.h"
3. Read the file "config.log". It shows the compiler commands used
to test the libraries installed on your system and the standard
output and standard error output these commands produce. Watch out
for unexpected failures. Those are most likely to happen if headers
or libraries are installed in unusual places or interfaces defined
in unusual headers. You can also look at the file "config.h" and
check that no expected "#define HAVE_*" lines are missing. The
list of tests run can be found in the file "configure".
Deployment
----------
If you want to integrate the mandoc(1) tools with your existing
man(1) system as a formatter, then contact us first: on systems without
mandoc(1) as the default, you may have your work cut out for you!
Usually, you can have your default installation and mandoc(1) work right
alongside each other by using user-specific versions of the files
mentioned below.
0. Back up each file you want to change!
1. First see whether your system has "/etc/man.conf" or "/etc/manpath.conf"
(if it has neither, but man(1) is functional, then let us know) or,
if running as your own user, a per-user override file. In either
case, find where man(1) is executing nroff(1) or groff(1) to format
manuals. Replace these calls with mandoc(1).
2. Then make sure that man(1) isn't running preprocessors, so you may
need to replace tbl(1), eqn(1), and similar references with cat(1).
Some man(1) implementations, like that on Mac OSX, let you run "man -d"
to see how the formatter is invoked. Use this to test your changes. On
Mac OS X, for instance, man(1) will prepend all files with ".ll" and
".nr" to set the terminal size, so you need to pass "tail -n+2 |
mandoc(1)" to disregard them.
3. Finally, make sure that mandoc(1) is actually being invoked instead
of cached pages being pulled up. You can usually do this by commenting
out NOCACHE or similar.
mandoc(1) still has a long way to go in understanding non-trivial
low-level roff(7) markup embedded in some man(7) pages. On the BSD
systems using mandoc(1), third-party software is generally vetted
on whether it may be formatted with mandoc(1). If not, groff(1)
is pulled in as a dependency and used to install a pre-formatted
"catpage" intead of directly as manual page source.
For more background on switching operating systems to use mandoc(1)
instead of groff(1) to format manuals, see the two BSDCan presentations
by Ingo Schwarze:
<http://www.openbsd.org/papers/bsdcan11-mandoc-openbsd.html>
<http://www.openbsd.org/papers/bsdcan14-mandoc.pdf>

44
LICENSE Normal file
View File

@ -0,0 +1,44 @@
$Id: LICENSE,v 1.2 2014/04/23 21:06:41 schwarze Exp $
With the exceptions noted below, all code and documentation
contained in the mdocml toolkit is protected by the Copyright
of the following developers:
Copyright (c) 2008, 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
Copyright (c) 2010, 2011, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
Copyright (c) 2009, 2010, 2011, 2012 Joerg Sonnenberger <joerg@netbsd.org>
Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
Copyright (c) 1998, 2010 Todd C. Miller <Todd.Miller@courtesan.com>
Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
Copyright (c) 2003 Jason McIntyre <jmc@openbsd.org>
See the individual source files for information about who contributed
to which file during which years.
The mdocml distribution as a whole is distributed by its developers
under the following license:
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.
The following files included from outside sources are protected by
other people's Copyright and are distributed under a 3-clause BSD
license; see these individual files for details.
compat_getsubopt.c, compat_strcasestr.c, compat_strsep.c:
Copyright (c) 1990, 1993 The Regents of the University of California
compat_fgetln.c:
Copyright (c) 1998 The NetBSD Foundation, Inc.

505
Makefile
View File

@ -1,15 +1,30 @@
.PHONY: clean install installwww
.SUFFIXES: .sgml .html .md5 .h .h.html
.SUFFIXES: .1 .3 .7 .8
.SUFFIXES: .1.html .3.html .7.html .8.html
# $Id: Makefile,v 1.435 2014/08/10 02:45:04 schwarze Exp $
#
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
# Copyright (c) 2011, 2013, 2014 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.
VERSION = 1.13.1
# === USER SETTINGS ====================================================
# --- user settings relevant for all builds ----------------------------
# Specify this if you want to hard-code the operating system to appear
# in the lower-left hand corner of -mdoc manuals.
#
# CFLAGS += -DOSNAME="\"OpenBSD 5.4\""
VERSION = 1.12.3
VDATE = 31 December 2013
# CFLAGS += -DOSNAME="\"OpenBSD 5.5\""
# IFF your system supports multi-byte functions (setlocale(), wcwidth(),
# putwchar()) AND has __STDC_ISO_10646__ (that is, wchar_t is simply a
@ -19,113 +34,136 @@ VDATE = 31 December 2013
#
CFLAGS += -DUSE_WCHAR
# If your system has manpath(1), uncomment this. This is most any
# system that's not OpenBSD or NetBSD. If uncommented, apropos(1),
# mandocdb(8), and man.cgi will popen(3) manpath(1) to get the MANPATH
# variable.
#CFLAGS += -DUSE_MANPATH
# If your system does not support static binaries, comment this,
# for example on Mac OS X.
STATIC = -static
# Linux requires -pthread to statically link with libdb.
#STATIC += -pthread
CFLAGS += -g -DHAVE_CONFIG_H
CFLAGS += -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings
PREFIX = /usr/local
WWWPREFIX = /var/www
HTDOCDIR = $(WWWPREFIX)/htdocs
CGIBINDIR = $(WWWPREFIX)/cgi-bin
BINDIR = $(PREFIX)/bin
INCLUDEDIR = $(PREFIX)/include/mandoc
LIBDIR = $(PREFIX)/lib/mandoc
MANDIR = $(PREFIX)/man
EXAMPLEDIR = $(PREFIX)/share/examples/mandoc
INSTALL = install
INSTALL_PROGRAM = $(INSTALL) -m 0755
INSTALL_PROGRAM = $(INSTALL) -m 0555
INSTALL_DATA = $(INSTALL) -m 0444
INSTALL_LIB = $(INSTALL) -m 0644
INSTALL_LIB = $(INSTALL) -m 0444
INSTALL_SOURCE = $(INSTALL) -m 0644
INSTALL_MAN = $(INSTALL_DATA)
# Non-BSD systems (Linux, etc.) need -ldb to compile mandocdb and
# apropos.
# However, if you don't have -ldb at all (or it's not native), then
# comment out apropos and mandocdb.
# --- user settings related to database support ------------------------
# Building apropos(1) and makewhatis(8) requires both SQLite3 and fts(3).
# To avoid those dependencies, comment the following line.
# Be careful: the fts(3) implementation in glibc is broken on 32bit
# machines, see: https://sourceware.org/bugzilla/show_bug.cgi?id=15838
#
#DBLIB = -ldb
DBBIN = apropos mandocdb man.cgi catman whatis
DBLN = llib-lapropos.ln llib-lmandocdb.ln llib-lman.cgi.ln llib-lcatman.ln
BUILD_TARGETS += db-build
all: mandoc preconv demandoc $(DBBIN)
# The remaining settings in this section
# are only relevant if db-build is enabled.
# Otherwise, they have no effect either way.
SRCS = Makefile \
NEWS \
TODO \
apropos.1 \
apropos.c \
apropos_db.c \
apropos_db.h \
# If your system has manpath(1), uncomment this. This is most any
# system that's not OpenBSD or NetBSD. If uncommented, apropos(1)
# and makewhatis(8) will use manpath(1) to get the MANPATH variable.
#
#CFLAGS += -DUSE_MANPATH
# On some systems, SQLite3 may be installed below /usr/local.
# In that case, uncomment the following two lines.
#
#CFLAGS += -I/usr/local/include
#DBLIB += -L/usr/local/lib
# OpenBSD has the ohash functions in libutil.
# Comment the following line if your system doesn't.
#
DBLIB += -lutil
SBINDIR = $(PREFIX)/sbin
# --- user settings related to man.cgi ---------------------------------
# To build man.cgi, copy cgi.h.example to cgi.h, edit it,
# and enable the following line.
# Obviously, this requires that db-build is enabled, too.
#
#BUILD_TARGETS += cgi-build
# The remaining settings in this section
# are only relevant if cgi-build is enabled.
# Otherwise, they have no effect either way.
# If your system does not support static binaries, comment this,
# for example on Mac OS X.
#
STATIC = -static
# Linux requires -pthread for statical linking.
#
#STATIC += -pthread
WWWPREFIX = /var/www
HTDOCDIR = $(WWWPREFIX)/htdocs
CGIBINDIR = $(WWWPREFIX)/cgi-bin
# === END OF USER SETTINGS =============================================
INSTALL_TARGETS = $(BUILD_TARGETS:-build=-install)
BASEBIN = mandoc preconv demandoc
DBBIN = apropos makewhatis
CGIBIN = man.cgi
DBLIB += -lsqlite3
TESTSRCS = test-fgetln.c \
test-getsubopt.c \
test-mmap.c \
test-ohash.c \
test-reallocarray.c \
test-sqlite3_errstr.c \
test-strcasestr.c \
test-strlcat.c \
test-strlcpy.c \
test-strptime.c \
test-strsep.c
SRCS = apropos.c \
arch.c \
arch.in \
att.c \
att.in \
catman.8 \
catman.c \
cgi.c \
chars.c \
chars.in \
compat_fgetln.c \
compat_getsubopt.c \
compat_ohash.c \
compat_reallocarray.c \
compat_sqlite3_errstr.c \
compat_strcasestr.c \
compat_strlcat.c \
compat_strlcpy.c \
config.h.post \
config.h.pre \
demandoc.1 \
compat_strsep.c \
demandoc.c \
eqn.7 \
eqn.c \
eqn_html.c \
eqn_term.c \
example.style.css \
external.png \
gmdiff \
html.c \
html.h \
index.css \
index.sgml \
lib.c \
lib.in \
libman.h \
libmandoc.h \
libmdoc.h \
libroff.h \
main.c \
main.h \
man.7 \
man.c \
man.cgi.7 \
man-cgi.css \
man.h \
man_hash.c \
man_html.c \
man_macro.c \
man_term.c \
man_validate.c \
mandoc.1 \
mandoc.3 \
mandoc.c \
mandoc.h \
mandoc_char.7 \
mandocdb.8 \
mandoc_aux.c \
mandocdb.c \
mandocdb.h \
manpage.c \
manpath.c \
manpath.h \
mdoc.7 \
mansearch.c \
mansearch_const.c \
mdoc.c \
mdoc.h \
mdoc_argv.c \
mdoc_hash.c \
mdoc_html.c \
@ -134,20 +172,11 @@ SRCS = Makefile \
mdoc_term.c \
mdoc_validate.c \
msec.c \
msec.in \
out.c \
out.h \
preconv.1 \
preconv.c \
predefs.in \
read.c \
roff.7 \
roff.c \
st.c \
st.in \
style.css \
tbl.3 \
tbl.7 \
tbl.c \
tbl_data.c \
tbl_html.c \
@ -155,20 +184,70 @@ SRCS = Makefile \
tbl_opts.c \
tbl_term.c \
term.c \
term.h \
term_ascii.c \
term_ps.c \
test-betoh64.c \
test-fgetln.c \
test-getsubopt.c \
test-mmap.c \
test-strlcat.c \
test-strlcpy.c \
test-strptime.c \
tree.c \
vol.c \
$(TESTSRCS)
DISTFILES = INSTALL \
LICENSE \
Makefile \
Makefile.depend \
NEWS \
TODO \
apropos.1 \
arch.in \
att.in \
cgi.h.example \
chars.in \
compat_ohash.h \
config.h.post \
config.h.pre \
configure \
demandoc.1 \
eqn.7 \
example.style.css \
gmdiff \
html.h \
lib.in \
libman.h \
libmandoc.h \
libmdoc.h \
libroff.h \
main.h \
makewhatis.8 \
man-cgi.css \
man.7 \
man.cgi.8 \
man.h \
mandoc.1 \
mandoc.3 \
mandoc.db.5 \
mandoc.h \
mandoc_aux.h \
mandoc_char.7 \
mandoc_escape.3 \
mandoc_html.3 \
mandoc_malloc.3 \
manpath.h \
mansearch.3 \
mansearch.h \
mchars_alloc.3 \
mdoc.7 \
mdoc.h \
msec.in \
out.h \
preconv.1 \
predefs.in \
roff.7 \
st.in \
style.css \
tbl.3 \
tbl.7 \
term.h \
vol.in \
whatis.1
$(SRCS)
LIBMAN_OBJS = man.o \
man_hash.o \
@ -198,35 +277,25 @@ LIBMANDOC_OBJS = $(LIBMAN_OBJS) \
$(LIBROFF_OBJS) \
chars.o \
mandoc.o \
mandoc_aux.o \
msec.o \
read.o
COMPAT_OBJS = compat_fgetln.o \
compat_getsubopt.o \
compat_ohash.o \
compat_reallocarray.o \
compat_sqlite3_errstr.o \
compat_strcasestr.o \
compat_strlcat.o \
compat_strlcpy.o
arch.o: arch.in
att.o: att.in
chars.o: chars.in
lib.o: lib.in
msec.o: msec.in
roff.o: predefs.in
st.o: st.in
vol.o: vol.in
$(LIBMAN_OBJS): libman.h
$(LIBMDOC_OBJS): libmdoc.h
$(LIBROFF_OBJS): libroff.h
$(LIBMANDOC_OBJS): mandoc.h mdoc.h man.h libmandoc.h config.h
$(COMPAT_OBJS): config.h
compat_strlcpy.o \
compat_strsep.o
MANDOC_HTML_OBJS = eqn_html.o \
html.o \
man_html.o \
mdoc_html.o \
tbl_html.o
$(MANDOC_HTML_OBJS): html.h
MANDOC_MAN_OBJS = mdoc_man.o
@ -237,7 +306,6 @@ MANDOC_TERM_OBJS = eqn_term.o \
term_ascii.o \
term_ps.o \
tbl_term.o
$(MANDOC_TERM_OBJS): term.h
MANDOC_OBJS = $(MANDOC_HTML_OBJS) \
$(MANDOC_MAN_OBJS) \
@ -245,76 +313,85 @@ MANDOC_OBJS = $(MANDOC_HTML_OBJS) \
main.o \
out.o \
tree.o
$(MANDOC_OBJS): main.h mandoc.h mdoc.h man.h config.h out.h
MANDOCDB_OBJS = mandocdb.o manpath.o
$(MANDOCDB_OBJS): mandocdb.h mandoc.h mdoc.h man.h config.h manpath.h
MAKEWHATIS_OBJS = mandocdb.o mansearch_const.o manpath.o
PRECONV_OBJS = preconv.o
$(PRECONV_OBJS): config.h
APROPOS_OBJS = apropos.o apropos_db.o manpath.o
$(APROPOS_OBJS): config.h mandoc.h apropos_db.h manpath.h mandocdb.h
APROPOS_OBJS = apropos.o mansearch.o mansearch_const.o manpath.o
CGI_OBJS = $(MANDOC_HTML_OBJS) \
$(MANDOC_MAN_OBJS) \
$(MANDOC_TERM_OBJS) \
cgi.o \
apropos_db.o \
manpath.o \
out.o \
tree.o
$(CGI_OBJS): main.h mdoc.h man.h out.h config.h mandoc.h apropos_db.h manpath.h mandocdb.h
mansearch.o \
mansearch_const.o \
out.o
CATMAN_OBJS = catman.o manpath.o
$(CATMAN_OBJS): config.h mandoc.h manpath.h mandocdb.h
MANPAGE_OBJS = manpage.o mansearch.o mansearch_const.o manpath.o
DEMANDOC_OBJS = demandoc.o
$(DEMANDOC_OBJS): config.h
INDEX_MANS = apropos.1.html \
catman.8.html \
WWW_MANS = apropos.1.html \
demandoc.1.html \
mandoc.1.html \
whatis.1.html \
preconv.1.html \
mandoc.3.html \
mandoc_escape.3.html \
mandoc_html.3.html \
mandoc_malloc.3.html \
mansearch.3.html \
mchars_alloc.3.html \
tbl.3.html \
mandoc.db.5.html \
eqn.7.html \
man.7.html \
man.cgi.7.html \
mandoc_char.7.html \
mdoc.7.html \
preconv.1.html \
roff.7.html \
tbl.7.html \
mandocdb.8.html
$(INDEX_MANS): mandoc
INDEX_OBJS = $(INDEX_MANS) \
makewhatis.8.html \
man.cgi.8.html \
man.h.html \
mandoc.h.html \
mdoc.h.html \
mdocml.tar.gz \
mdocml.md5
mandoc_aux.h.html \
manpath.h.html \
mansearch.h.html \
mdoc.h.html
www: index.html
WWW_OBJS = mdocml.tar.gz \
mdocml.sha256
# === DEPENDENCY HANDLING ==============================================
all: base-build $(BUILD_TARGETS)
base-build: $(BASEBIN)
db-build: $(DBBIN)
cgi-build: $(CGIBIN)
install: base-install $(INSTALL_TARGETS)
www: $(WWW_OBJS) $(WWW_MANS)
include Makefile.depend
# === TARGETS CONTAINING SHELL COMMANDS ================================
clean:
rm -f libmandoc.a $(LIBMANDOC_OBJS)
rm -f mandocdb $(MANDOCDB_OBJS)
rm -f apropos $(APROPOS_OBJS)
rm -f makewhatis $(MAKEWHATIS_OBJS)
rm -f preconv $(PRECONV_OBJS)
rm -f apropos whatis $(APROPOS_OBJS)
rm -f man.cgi $(CGI_OBJS)
rm -f catman $(CATMAN_OBJS)
rm -f manpage $(MANPAGE_OBJS)
rm -f demandoc $(DEMANDOC_OBJS)
rm -f mandoc $(MANDOC_OBJS)
rm -f config.h config.log $(COMPAT_OBJS)
rm -f mdocml.tar.gz
rm -f index.html $(INDEX_OBJS)
rm -f $(WWW_MANS) $(WWW_OBJS)
rm -rf *.dSYM
install: all
base-install: base-build
mkdir -p $(DESTDIR)$(BINDIR)
mkdir -p $(DESTDIR)$(EXAMPLEDIR)
mkdir -p $(DESTDIR)$(LIBDIR)
@ -322,31 +399,59 @@ install: all
mkdir -p $(DESTDIR)$(MANDIR)/man1
mkdir -p $(DESTDIR)$(MANDIR)/man3
mkdir -p $(DESTDIR)$(MANDIR)/man7
$(INSTALL_PROGRAM) mandoc preconv demandoc $(DESTDIR)$(BINDIR)
$(INSTALL_PROGRAM) $(BASEBIN) $(DESTDIR)$(BINDIR)
$(INSTALL_LIB) libmandoc.a $(DESTDIR)$(LIBDIR)
$(INSTALL_LIB) man.h mdoc.h mandoc.h $(DESTDIR)$(INCLUDEDIR)
$(INSTALL_LIB) man.h mandoc.h mandoc_aux.h mdoc.h \
$(DESTDIR)$(INCLUDEDIR)
$(INSTALL_MAN) mandoc.1 preconv.1 demandoc.1 $(DESTDIR)$(MANDIR)/man1
$(INSTALL_MAN) mandoc.3 tbl.3 $(DESTDIR)$(MANDIR)/man3
$(INSTALL_MAN) man.7 mdoc.7 roff.7 eqn.7 tbl.7 mandoc_char.7 $(DESTDIR)$(MANDIR)/man7
$(INSTALL_MAN) mandoc.3 mandoc_escape.3 mandoc_malloc.3 \
mchars_alloc.3 tbl.3 $(DESTDIR)$(MANDIR)/man3
$(INSTALL_MAN) man.7 mdoc.7 roff.7 eqn.7 tbl.7 mandoc_char.7 \
$(DESTDIR)$(MANDIR)/man7
$(INSTALL_DATA) example.style.css $(DESTDIR)$(EXAMPLEDIR)
installcgi: all
db-install: db-build
mkdir -p $(DESTDIR)$(BINDIR)
mkdir -p $(DESTDIR)$(SBINDIR)
mkdir -p $(DESTDIR)$(MANDIR)/man1
mkdir -p $(DESTDIR)$(MANDIR)/man3
mkdir -p $(DESTDIR)$(MANDIR)/man5
mkdir -p $(DESTDIR)$(MANDIR)/man8
$(INSTALL_PROGRAM) apropos $(DESTDIR)$(BINDIR)
ln -f $(DESTDIR)$(BINDIR)/apropos $(DESTDIR)$(BINDIR)/whatis
$(INSTALL_PROGRAM) makewhatis $(DESTDIR)$(SBINDIR)
$(INSTALL_MAN) apropos.1 $(DESTDIR)$(MANDIR)/man1
ln -f $(DESTDIR)$(MANDIR)/man1/apropos.1 \
$(DESTDIR)$(MANDIR)/man1/whatis.1
$(INSTALL_MAN) mansearch.3 $(DESTDIR)$(MANDIR)/man3
$(INSTALL_MAN) mandoc.db.5 $(DESTDIR)$(MANDIR)/man5
$(INSTALL_MAN) makewhatis.8 $(DESTDIR)$(MANDIR)/man8
cgi-install: cgi-build
mkdir -p $(DESTDIR)$(CGIBINDIR)
mkdir -p $(DESTDIR)$(HTDOCDIR)
mkdir -p $(DESTDIR)$(WWWPREFIX)/man/mandoc/man1
mkdir -p $(DESTDIR)$(WWWPREFIX)/man/mandoc/man8
$(INSTALL_PROGRAM) man.cgi $(DESTDIR)$(CGIBINDIR)
$(INSTALL_DATA) example.style.css $(DESTDIR)$(HTDOCDIR)/man.css
$(INSTALL_DATA) man-cgi.css $(DESTDIR)$(HTDOCDIR)
$(INSTALL_MAN) apropos.1 $(DESTDIR)$(WWWPREFIX)/man/mandoc/man1/
$(INSTALL_MAN) man.cgi.8 $(DESTDIR)$(WWWPREFIX)/man/mandoc/man8/
installwww: www
mkdir -p $(PREFIX)/snapshots
mkdir -p $(PREFIX)/binaries
$(INSTALL_DATA) index.html external.png index.css $(PREFIX)
$(INSTALL_DATA) $(INDEX_MANS) style.css $(PREFIX)
$(INSTALL_DATA) mandoc.h.html man.h.html mdoc.h.html $(PREFIX)
$(INSTALL_DATA) mdocml.tar.gz $(PREFIX)/snapshots
$(INSTALL_DATA) mdocml.md5 $(PREFIX)/snapshots
$(INSTALL_DATA) mdocml.tar.gz $(PREFIX)/snapshots/mdocml-$(VERSION).tar.gz
$(INSTALL_DATA) mdocml.md5 $(PREFIX)/snapshots/mdocml-$(VERSION).md5
www-install: www
mkdir -p $(DESTDIR)$(HTDOCDIR)/snapshots
$(INSTALL_DATA) $(WWW_MANS) style.css $(DESTDIR)$(HTDOCDIR)
$(INSTALL_DATA) $(WWW_OBJS) $(DESTDIR)$(HTDOCDIR)/snapshots
$(INSTALL_DATA) mdocml.tar.gz \
$(DESTDIR)$(HTDOCDIR)/snapshots/mdocml-$(VERSION).tar.gz
$(INSTALL_DATA) mdocml.sha256 \
$(DESTDIR)$(HTDOCDIR)/snapshots/mdocml-$(VERSION).sha256
depend: config.h
mkdep -f Makefile.depend $(CFLAGS) $(SRCS)
perl -e 'undef $$/; $$_ = <>; s|/usr/include/\S+||g; \
s|\\\n||g; s| +| |g; print;' Makefile.depend > Makefile.tmp
mv Makefile.tmp Makefile.depend
libmandoc.a: $(COMPAT_OBJS) $(LIBMANDOC_OBJS)
$(AR) rs $@ $(COMPAT_OBJS) $(LIBMANDOC_OBJS)
@ -354,81 +459,47 @@ libmandoc.a: $(COMPAT_OBJS) $(LIBMANDOC_OBJS)
mandoc: $(MANDOC_OBJS) libmandoc.a
$(CC) $(LDFLAGS) -o $@ $(MANDOC_OBJS) libmandoc.a
mandocdb: $(MANDOCDB_OBJS) libmandoc.a
$(CC) $(LDFLAGS) -o $@ $(MANDOCDB_OBJS) libmandoc.a $(DBLIB)
makewhatis: $(MAKEWHATIS_OBJS) libmandoc.a
$(CC) $(LDFLAGS) -o $@ $(MAKEWHATIS_OBJS) libmandoc.a $(DBLIB)
preconv: $(PRECONV_OBJS)
$(CC) $(LDFLAGS) -o $@ $(PRECONV_OBJS)
whatis: apropos
cp -f apropos whatis
manpage: $(MANPAGE_OBJS) libmandoc.a
$(CC) $(LDFLAGS) -o $@ $(MANPAGE_OBJS) libmandoc.a $(DBLIB)
apropos: $(APROPOS_OBJS) libmandoc.a
$(CC) $(LDFLAGS) -o $@ $(APROPOS_OBJS) libmandoc.a $(DBLIB)
catman: $(CATMAN_OBJS) libmandoc.a
$(CC) $(LDFLAGS) -o $@ $(CATMAN_OBJS) libmandoc.a $(DBLIB)
man.cgi: $(CGI_OBJS) libmandoc.a
$(CC) $(LDFLAGS) $(STATIC) -o $@ $(CGI_OBJS) libmandoc.a $(DBLIB)
demandoc: $(DEMANDOC_OBJS) libmandoc.a
$(CC) $(LDFLAGS) -o $@ $(DEMANDOC_OBJS) libmandoc.a
mdocml.md5: mdocml.tar.gz
md5 mdocml.tar.gz >$@
mdocml.sha256: mdocml.tar.gz
sha256 mdocml.tar.gz > $@
mdocml.tar.gz: $(SRCS)
mdocml.tar.gz: $(DISTFILES)
mkdir -p .dist/mdocml-$(VERSION)/
$(INSTALL_SOURCE) $(SRCS) .dist/mdocml-$(VERSION)
( cd .dist/ && tar zcf ../$@ ./ )
$(INSTALL_SOURCE) $(DISTFILES) .dist/mdocml-$(VERSION)
chmod 755 .dist/mdocml-$(VERSION)/configure
( cd .dist/ && tar zcf ../$@ mdocml-$(VERSION) )
rm -rf .dist/
index.html: $(INDEX_OBJS)
config.h: config.h.pre config.h.post
config.h: configure config.h.pre config.h.post $(TESTSRCS)
rm -f config.log
( cat config.h.pre; \
echo; \
echo '#define VERSION "$(VERSION)"'; \
if $(CC) $(CFLAGS) -Werror -Wno-unused -o test-fgetln test-fgetln.c >> config.log 2>&1; then \
echo '#define HAVE_FGETLN'; \
rm test-fgetln; \
fi; \
if $(CC) $(CFLAGS) -Werror -Wno-unused -o test-strptime test-strptime.c >> config.log 2>&1; then \
echo '#define HAVE_STRPTIME'; \
rm test-strptime; \
fi; \
if $(CC) $(CFLAGS) -Werror -Wno-unused -o test-getsubopt test-getsubopt.c >> config.log 2>&1; then \
echo '#define HAVE_GETSUBOPT'; \
rm test-getsubopt; \
fi; \
if $(CC) $(CFLAGS) -Werror -Wno-unused -o test-strlcat test-strlcat.c >> config.log 2>&1; then \
echo '#define HAVE_STRLCAT'; \
rm test-strlcat; \
fi; \
if $(CC) $(CFLAGS) -Werror -Wno-unused -o test-mmap test-mmap.c >> config.log 2>&1; then \
echo '#define HAVE_MMAP'; \
rm test-mmap; \
fi; \
if $(CC) $(CFLAGS) -Werror -Wno-unused -o test-strlcpy test-strlcpy.c >> config.log 2>&1; then \
echo '#define HAVE_STRLCPY'; \
rm test-strlcpy; \
fi; \
if $(CC) $(CFLAGS) -Werror -Wno-unused -o test-betoh64 test-betoh64.c >> config.log 2>&1; then \
echo '#define HAVE_BETOH64'; \
rm test-betoh64; \
fi; \
echo; \
cat config.h.post \
) > $@
CC="$(CC)" CFLAGS="$(CFLAGS)" DBLIB="$(DBLIB)" \
VERSION="$(VERSION)" ./configure
.PHONY: base-install cgi-install db-install install www-install
.PHONY: clean depend
.SUFFIXES: .1 .3 .5 .7 .8 .h
.SUFFIXES: .1.html .3.html .5.html .7.html .8.html .h.html
.h.h.html:
highlight -I $< >$@
highlight -I $< > $@
.1.1.html .3.3.html .7.7.html .8.8.html:
./mandoc -Thtml -Wall,stop -Ostyle=style.css,man=%N.%S.html,includes=%I.html $< >$@
.sgml.html:
validate --warn $<
sed -e "s!@VERSION@!$(VERSION)!" -e "s!@VDATE@!$(VDATE)!" $< >$@
.1.1.html .3.3.html .5.5.html .7.7.html .8.8.html: mandoc
./mandoc -Thtml -Wall,stop \
-Ostyle=style.css,man=%N.%S.html,includes=%I.html $< > $@

70
Makefile.depend Normal file
View File

@ -0,0 +1,70 @@
apropos.o: apropos.c config.h manpath.h mansearch.h
arch.o: arch.c config.h mdoc.h libmdoc.h arch.in
att.o: att.c config.h mdoc.h libmdoc.h att.in
cgi.o: cgi.c config.h mandoc.h mandoc_aux.h main.h manpath.h mansearch.h cgi.h
chars.o: chars.c config.h mandoc.h mandoc_aux.h libmandoc.h chars.in
compat_fgetln.o: compat_fgetln.c config.h
compat_getsubopt.o: compat_getsubopt.c config.h
compat_ohash.o: compat_ohash.c config.h
compat_reallocarray.o: compat_reallocarray.c config.h
compat_sqlite3_errstr.o: compat_sqlite3_errstr.c config.h
compat_strcasestr.o: compat_strcasestr.c config.h
compat_strlcat.o: compat_strlcat.c config.h
compat_strlcpy.o: compat_strlcpy.c config.h
compat_strsep.o: compat_strsep.c config.h
demandoc.o: demandoc.c config.h man.h mdoc.h mandoc.h
eqn.o: eqn.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
eqn_html.o: eqn_html.c config.h mandoc.h out.h html.h
eqn_term.o: eqn_term.c config.h mandoc.h out.h term.h
html.o: html.c config.h mandoc.h mandoc_aux.h libmandoc.h out.h html.h main.h
lib.o: lib.c config.h mdoc.h libmdoc.h lib.in
main.o: main.c config.h mandoc.h mandoc_aux.h main.h mdoc.h man.h
man.o: man.c config.h man.h mandoc.h mandoc_aux.h libman.h libmandoc.h
man_hash.o: man_hash.c config.h man.h mandoc.h libman.h
man_html.o: man_html.c config.h mandoc.h mandoc_aux.h out.h html.h man.h main.h
man_macro.o: man_macro.c config.h man.h mandoc.h libmandoc.h libman.h
man_term.o: man_term.c config.h mandoc.h mandoc_aux.h out.h man.h term.h main.h
man_validate.o: man_validate.c config.h man.h mandoc.h mandoc_aux.h libman.h libmandoc.h
mandoc.o: mandoc.c config.h mandoc.h mandoc_aux.h libmandoc.h
mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h
mandocdb.o: mandocdb.c config.h mdoc.h man.h mandoc.h mandoc_aux.h manpath.h mansearch.h
manpage.o: manpage.c config.h manpath.h mansearch.h
manpath.o: manpath.c config.h mandoc_aux.h manpath.h
mansearch.o: mansearch.c config.h mandoc.h mandoc_aux.h manpath.h mansearch.h
mansearch_const.o: mansearch_const.c config.h manpath.h mansearch.h
mdoc.o: mdoc.c config.h mdoc.h mandoc.h mandoc_aux.h libmdoc.h libmandoc.h
mdoc_argv.o: mdoc_argv.c config.h mdoc.h mandoc.h mandoc_aux.h libmdoc.h libmandoc.h
mdoc_hash.o: mdoc_hash.c config.h mdoc.h libmdoc.h
mdoc_html.o: mdoc_html.c config.h mandoc.h mandoc_aux.h out.h html.h mdoc.h main.h
mdoc_macro.o: mdoc_macro.c config.h mdoc.h mandoc.h libmdoc.h libmandoc.h
mdoc_man.o: mdoc_man.c config.h mandoc.h mandoc_aux.h out.h man.h mdoc.h main.h
mdoc_term.o: mdoc_term.c config.h mandoc.h mandoc_aux.h out.h term.h mdoc.h main.h
mdoc_validate.o: mdoc_validate.c config.h mdoc.h mandoc.h mandoc_aux.h libmdoc.h libmandoc.h
msec.o: msec.c config.h mandoc.h libmandoc.h msec.in
out.o: out.c config.h mandoc_aux.h mandoc.h out.h
preconv.o: preconv.c config.h
read.o: read.c config.h mandoc.h mandoc_aux.h libmandoc.h mdoc.h man.h main.h
roff.o: roff.c config.h mandoc.h mandoc_aux.h libroff.h libmandoc.h predefs.in
st.o: st.c config.h mdoc.h libmdoc.h st.in
tbl.o: tbl.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
tbl_data.o: tbl_data.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
tbl_html.o: tbl_html.c config.h mandoc.h out.h html.h
tbl_layout.o: tbl_layout.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
tbl_opts.o: tbl_opts.c config.h mandoc.h libmandoc.h libroff.h
tbl_term.o: tbl_term.c config.h mandoc.h out.h term.h
term.o: term.c config.h mandoc.h mandoc_aux.h out.h term.h main.h
term_ascii.o: term_ascii.c config.h mandoc.h mandoc_aux.h out.h term.h main.h
term_ps.o: term_ps.c config.h mandoc.h mandoc_aux.h out.h main.h term.h
tree.o: tree.c config.h mandoc.h mdoc.h man.h main.h
vol.o: vol.c config.h mdoc.h libmdoc.h vol.in
test-fgetln.o: test-fgetln.c
test-getsubopt.o: test-getsubopt.c
test-mmap.o: test-mmap.c
test-ohash.o: test-ohash.c
test-reallocarray.o: test-reallocarray.c
test-sqlite3_errstr.o: test-sqlite3_errstr.c
test-strcasestr.o: test-strcasestr.c
test-strlcat.o: test-strlcat.c
test-strlcpy.o: test-strlcpy.c
test-strptime.o: test-strptime.c
test-strsep.o: test-strsep.c

82
NEWS
View File

@ -1,7 +1,87 @@
$Id: NEWS,v 1.3 2013/10/13 16:06:50 schwarze Exp $
$Id: NEWS,v 1.5 2014/08/10 16:32:57 schwarze Exp $
This file lists the most important changes in the mdocml.bsd.lv distribution.
Changes in version 1.13.1, released on August 10, 2014
--- MAJOR NEW FEATURES ---
* A complete apropos(1)/makewhatis(8)/man.cgi(8) suite
based on SQLite3 is now included.
CAVEAT: This also requires a working fts(3) implementation.
If your system lacks that *and* you want apropos(1)/makewhatis(8),
stay with 1.12.3 for now, then go to 1.12.4 and 1.13.2.
* The roff(7) parser now provides an almost complete implementation
of numerical expressions.
* Warning and error messages have been improved in many ways.
Almost all fatal errors were downgraded to normal errors and some
even to warnings. Almost all messages now mention the macro where
the issue is detected and many indicate the workaround employed.
The mandoc(1) manual now includes a list explaining all messages.
--- MINOR NEW FEATURES ---
* The roff(7) parser now supports the .ami (append to macro with
indirectly specified name), .as (append to user-defined
string), .dei (define macro with indirectly specified name),
.ll (line length), and .rr (remove register) requests.
* The roff(7) parser now supports string comparison and numerical
conditionals in the .if and .ie requests.
* The roff parser now fully supports the \B (validate numerical
expression) and partially supports the \w (measure text width)
escape sequences.
* The terminal formatter now supports the \: (optional line break)
escape sequence.
* The roff parser now supports expansion of user-defined strings
involving indirect references.
* The roff(7) parser now handles some pre-defined read-only
number registers that occur in the pod2man(1) preamble.
* For backward compatibility, the mdoc(7) parser and formatters
now support the obsolete macros .En, .Es, .Fr, and .Ot.
* The mdoc(7) formatter non partially supports .Bd -centered.
* tbl(7) now handles leading and trailing vertical lines.
* The build system now provides fallback versions of strcasestr(3)
and strsep(3) for systems lacking them.
* The mdoc(7) manual now explains how various standards
supported by the .St macro are related to each other.
--- BUGFIXES ---
* In the roff(7) parser, several bugs were fixed with respect
to closing conditional blocks on macro lines.
* Parsing of roff(7) identifiers and escape sequences was improved
in multiple respects.
* In the mdoc(7) parser, the handling of defective document
prologues was improved in multiple ways.
* The mdoc(7) parser no longer skips content before the first section
header, and it no longer deletes non-.% content from .Rs blocks.
* In the mdoc(7) parser, a crash was fixed related to weird .Sh headers.
* In the mdoc(7) parser, handling of .Sm with missing or invalid
arguments was corrected.
* In the mdoc(7) parser, trailing punctuation at the end of partial
implicit macros no longer triggers end-of-sentence spacing.
* In the terminal formatter, two crashes were fixed: one triggered by
excessive indentation and another by excessively long .Nm arguments.
* In the terminal formatter, a floating point rounding bug was
fixed that sometimes caused an off-by-one error in indentation.
* In the UTF-8 formatter, rendering of accents, breakable hyphens,
and non-breakable spaces was corrected.
* In the HTML formatter, encoding of special characters was
corrected in multiple respects.
* In the mdoc(7) formatter, rendering of .Ex and .Rv was
improved for various edge cases.
* In the mdoc(7) formatter, handling of empty .Bl -inset item
heads was improved.
* In the man(7) formatter, some bugs were fixed with respect
to same-line detection in the context of .TP and .nf macros,
and the indentation of .IP and .TP blocks was improved.
* The mandoc(3) library no longer prints to stderr.
--- THANKS TO ---
Abhinav Upadhyay (NetBSD), Andreas Voegele, Anthony Bentley (OpenBSD),
Christian Weisgerber (OpenBSD), Havard Eidnes (NetBSD), Jan Stary,
Jason McIntyre (OpenBSD), Jeremie Courreges-Anglas (OpenBSD),
Joerg Sonnenberger (NetBSD), Juan Francisco Cantero Hurtado (OpenBSD),
Marc Espie (OpenBSD), Matthias Scheler (NetBSD), Pascal Stumpf (OpenBSD),
Paul Onyschuk (Alpine Linux), Sebastien Marie, Steffen Nurpmeso,
Stuart Henderson (OpenBSD), Ted Unangst (OpenBSD), Theo de Raadt (OpenBSD),
Thomas Klausner (NetBSD), and Ulrich Spoerlein (FreeBSD)
for reporting bugs and missing features.
Changes in version 1.12.3, released on December 31, 2013
* In the mdoc(7) SYNOPSIS, line breaks and hanging indentation

175
TODO
View File

@ -1,13 +1,15 @@
************************************************************************
* Official mandoc TODO.
* $Id: TODO,v 1.162 2013/12/25 14:40:34 schwarze Exp $
* $Id: TODO,v 1.176 2014/08/09 14:24:53 schwarze Exp $
************************************************************************
************************************************************************
* crashes
************************************************************************
None known.
- The abort() in bufcat(), html.c, can be triggered via buffmt_includes()
by running -Thtml -Oincludes on a file containing a long .In argument.
Fixing this will probably require reworking the whole bufcat() concept.
************************************************************************
* missing features
@ -15,11 +17,6 @@ None known.
--- missing roff features ----------------------------------------------
- roff.c should treat \n(.H>23 and \n(.V>19 in the pod2man(1)
preamble as true, see for example AUTHORS in MooseX::Getopt.3p
reported by Andreas Voegele <mail at andreasvoegele dot com>
Tue, 22 Nov 2011 15:34:47 +0100 on ports@
- .ad (adjust margins)
.ad l -- adjust left margin only (flush left)
.ad r -- adjust right margin only (flush right)
@ -29,20 +26,9 @@ None known.
.ad -- re-enable adjustment without changing the mode
Adjustment mode is ignored while in no-fill mode (.nf).
- .as (append to string)
found by jca@ in ratpoison(1) Sun, 30 Jun 2013 12:01:09 +0200
- .ce (center N lines)
found by naddy@ in xloadimage(1)
found by Juan Francisco Cantero Hurtado <iam at juanfra dot info>
in lang/racket(1) Thu, 20 Jun 2013 03:19:11 +0200
- .fc (field control)
found by naddy@ in xloadimage(1)
- .ll (line length)
found by naddy@ in textproc/enchant(1) Sat, 12 Oct 2013 03:27:10 +0200
- .nr third argument (auto-increment step size, requires \n+)
found by bentley@ in sbcl(1) Mon, 9 Dec 2013 18:36:57 -0700
@ -51,6 +37,7 @@ None known.
- .ta (tab settings) occurs in ircbug(1) and probably gnats(1)
reported by brad@ Sat, 15 Jan 2011 15:50:51 -0500
also Tcl_NewStringObj(3) via wiz@ Wed, 5 Mar 2014 22:27:43 +0100
- .ti (temporary indent)
found by naddy@ in xloadimage(1)
@ -70,6 +57,10 @@ None known.
- \n+ and \n- numerical register increment and decrement
found by bentley@ in sbcl(1) Mon, 9 Dec 2013 18:36:57 -0700
- \w'' width measurements
would not be very useful without an expression parser, see below
needed for Tcl_NewStringObj(3) via wiz@ Wed, 5 Mar 2014 22:27:43 +0100
- using undefined strings or macros defines them to be empty
wl@ Mon, 14 Nov 2011 14:37:01 +0000
@ -96,6 +87,12 @@ None known.
because libmdoc does not yet use mandoc_getarg().
Also check what happens in plain text, it must be identical to \e.
- .Bd -centered implies -filled, not -unfilled, which is not
easy to implement; it requires code similar to .ce, which
we don't have either.
Besides, groff has bug causing text right *before* .Bd -centered
to be centered as well.
- .Bd -filled should not be the same as .Bd -ragged, but align both
the left and right margin. In groff, it is implemented in terms
of .ad b, which we don't have either. Found in cksum(1).
@ -129,10 +126,19 @@ None known.
- have a blank `It' head for `Bl -tag' not puke
- check whether it is correct that `D1' uses INDENT+1;
does it need its own constant?
- prohibit `Nm' from having non-text HEAD children
(e.g., NetBSD mDNSShared/dns-sd.1)
(mdoc_html.c and mdoc_term.c `Nm' handlers can be slightly simplified)
- support translated section names
e.g. x11/scrotwm scrotwm_es.1:21:2: error: NAME section must be first
that one uses NOMBRE because it is spanish...
deraadt tends to think that section-dependent macro behaviour
is a bad idea in the first place, so this may be irrelevant
- When there is free text in the SYNOPSIS and that free text contains
the .Nm macro, groff somehow understands to treat the .Nm as an in-line
macro, while mandoc treats it as a block macro and breaks the line.
@ -143,18 +149,15 @@ None known.
--- missing man features -----------------------------------------------
- groff an-ext.tmac macros (.UR, .UE) occur in xine(5)
reported by brad@ Sat, 15 Jan 2011 15:45:23 -0500
also occur in freeciv-client(6) freeciv-server(6) freeciv-modpack(6)
reported by bentley@ Tue, 30 Oct 2012 01:05:57 -0600
- -T[x]html doesn't stipulate non-collapsing spaces in literal mode
--- missing tbl features -----------------------------------------------
- implement basic non-parametric .de to support e.g. sox(1)
reported by naddy@ Sat, 16 Oct 2010 23:51:57 +0200
*** sox(1) still doesn't work, tbl(1) errors need investigation
- look at the POSIX manuals in the books/man-pages-posix port,
they use some unsupported tbl(7) features.
- investigate tbl(1) errors in sox(1)
see also naddy@ Sat, 16 Oct 2010 23:51:57 +0200
- allow standalone `.' to be interpreted as an end-of-layout
delimiter instead of being thrown away as a no-op roff line
@ -165,14 +168,19 @@ None known.
- italic correction (\/) in PostScript mode
Werner LEMBERG on groff at gnu dot org Sun, 10 Nov 2013 12:47:46
- The whatis(1) utility looks for whole words in Nm.
If the file name of a page does not agree with the contents of any
of its Nm macros (e.g. pool(9)), add the file name as an Nm entry
to the mandoc.db as well, such that whatis(1) finds it.
If there is a page with a file name that does not appear as a substring
neither in Nm nor in Nd, the same fix would allow finding that page
with apropos(1) using the file name as a key, as well.
Issue reported by tedu@ Fri, 05 Jul 2013 21:15:23 -0400
- When makewhatis(8) encounters a FATAL parse error,
it silently treats the file as formatted, which makes no sense
at all for paths like man1/foo.1 - and which also contradicts
what the manual says at the end of the description.
The end result will be ENOENT for file names returned
by mansearch() in manpage.file.
- makewhatis(8) for preformatted pages:
parse the section number from the header line
and compare to the section number from the directory name
- Does makewhatis(8) detect missing NAME sections, missing names,
and missing descriptions in all the file formats?
- clean up escape sequence handling, creating three classes:
(1) fully implemented, or parsed and ignored without loss of content
@ -181,6 +189,16 @@ None known.
see textproc/mgdiff(1) for nice examples
(3) undefined, just output the character -> perhaps WARNING
- kettenis wants base roff, ms, and me Fri, 1 Jan 2010 22:13:15 +0100 (CET)
--- compatibility checks -----------------------------------------------
- is .Bk implemented correctly in modern groff?
sobrado@ Tue, 19 Apr 2011 22:12:55 +0200
- compare output to Heirloom roff, Solaris roff, and
http://repo.or.cz/w/neatroff.git http://litcave.rudi.ir/
- look at pages generated from reStructeredText, e.g. devel/mercurial hg(1)
These are a weird mixture of man(7) and custom autogenerated low-level
roff stuff. Figure out to what extent we can cope.
@ -188,14 +206,24 @@ None known.
noted by stsp@ Sat, 24 Apr 2010 09:17:55 +0200
reminded by nicm@ Mon, 3 May 2010 09:52:41 +0100
- look at pages generated from ronn(1) github.com/rtomayko/ronn
(based on markdown)
- look at pages generated from Texinfo source by yat2m, e.g. security/gnupg
First impression is not that bad.
- look at pages generated by pandoc; see
https://github.com/jgm/pandoc/blob/master/src/Text/Pandoc/Writers/Man.hs
porting planned by kili@ Thu, 19 Jun 2014 19:46:28 +0200
- check compatibility with Plan9:
http://swtch.com/usr/local/plan9/tmac/tmac.an
http://swtch.com/plan9port/man/man7/man.html
"Anthony J. Bentley" <anthonyjbentley@gmail.com> 28 Dec 2010 21:58:40 -0700
- check compatibility with the man(7) formatter
https://raw.githubusercontent.com/rofl0r/hardcore-utils/master/man.c
************************************************************************
* formatting issues: ugly output
************************************************************************
@ -227,6 +255,10 @@ None known.
the right solution, it sends mandoc into an endless loop.
reported by Nicolas Joly Sat, 17 Nov 2012 11:49:54 +0100
- global variables in the SYNOPSIS of section 3 pages
.Vt vs .Vt/.Va vs .Ft/.Va vs .Ft/.Fa ...
from kristaps@ Tue, 08 Jun 2010 11:13:32 +0200
- in enclosures, mandoc sometimes fancies a bogus end of sentence
reminded by jmc@ Thu, 23 Sep 2010 18:13:39 +0059
@ -234,6 +266,23 @@ None known.
reveals lots of bugs both in groff and mandoc...
reported by bentley@ Wed, 22 May 2013 23:49:30 -0600
--- PDF issues ---------------------------------------------------------
- PDF output doesn't use a monospaced font for .Bd -literal
Example: "mandoc -Tpdf afterboot.8 > output.pdf && pdfviewer output.pdf".
Search the text "Routing tables".
Also check what PostScript mode does when fixing this.
reported by juanfra@ Wed, 04 Jun 2014 21:44:58 +0200
--- HTML issues --------------------------------------------------------
- <dl><dt><dd> formatting is ugly
hints are easy to find on the web, e.g.
http://stackoverflow.com/questions/1713048/
see also matthew@ Fri, 18 Jul 2014 19:25:12 -0700
- check https://github.com/trentm/mdocml
************************************************************************
* formatting issues: gratuitous differences
************************************************************************
@ -246,6 +295,10 @@ None known.
is just "o\bo".
see for example OpenBSD ksh(1)
- In .Bl -enum -width 0n, groff continues one the same line after
the number, mandoc breaks the line.
mail to kristaps@ Mon, 20 Jul 2009 02:21:39 +0200
- .Pp between two .It in .Bl -column should produce one,
not two blank lines, see e.g. login.conf(5).
reported by jmc@ Sun, 17 Apr 2011 14:04:58 +0059
@ -298,10 +351,58 @@ None known.
operate in batch mode
in dig(1).
************************************************************************
* warning issues
************************************************************************
- check that MANDOCERR_BADTAB is thrown in the right cases,
i.e. when finding a literal tab character in fill mode,
and possibly change the wording of the warning message
to refer to fill mode, not literal mode
See the mail from Werner LEMBERG on the groff list,
Fri, 14 Feb 2014 18:54:42 +0100 (CET)
- warn about "new sentence, new line"
- mandoc_special does not really check the escape sequence,
but just the overall format
- integrate mdoclint into mandoc ("end-of-line whitespace" thread)
from jmc@ Mon, 13 Jul 2009 17:12:09 +0100
from kristaps@ Mon, 13 Jul 2009 18:34:53 +0200
from jmc@ Mon, 13 Jul 2009 17:45:37 +0059
from kristaps@ Mon, 13 Jul 2009 19:02:03 +0200
- -Tlint parser errors and warnings to stdout
to tech@mdocml, naddy@ Wed, 28 Sep 2011 11:21:46 +0200
wait! kristaps@ Sun, 02 Oct 2011 17:12:52 +0200
- for system errors, use errno/strerror/warn/err
************************************************************************
* documentation issues
************************************************************************
- mention hyphenation rules:
breaking at letter-letter in text mode (not macro args)
proper hyphenation is unimplemented
- talk about spacing around delimiters
to jmc@, kristaps@ Sat, 23 Apr 2011 17:41:27 +0200
- mark macros as: page structure domain, manual domain, general text domain
is this useful?
- mention /usr/share/misc/mdoc.template in mdoc(7)?
************************************************************************
* performance issues
************************************************************************
- Why are we using MAP_SHARED, not MAP_PRIVATE for mmap(2)?
How does SQLITE_CONFIG_PAGECACHE actually work? Document it!
from kristaps@ Sat, 09 Aug 2014 13:51:36 +0200
Several areas can be cleaned up to make mandoc even faster. These are
- improve hashing mechanism for macros (quite important: performance)
@ -328,3 +429,9 @@ Several areas can be cleaned up to make mandoc even faster. These are
Decide which formats should be recognized where.
Update both mdoc(7) and man(7) documentation.
Triggered by Tim van der Molen Tue, 22 Feb 2011 20:30:45 +0100
- Consider creating some views that will make the database more
readable from the sqlite3 shell. Consider using them to
abstract from the database structure, too.
suggested by espie@ Sat, 19 Apr 2014 14:52:57 +0200

222
apropos.1
View File

@ -1,6 +1,7 @@
.\" $Id: apropos.1,v 1.16.2.3 2013/10/05 01:25:20 schwarze Exp $
.\" $Id: apropos.1,v 1.29 2014/04/24 00:28:19 schwarze Exp $
.\"
.\" Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -14,41 +15,49 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: October 5 2013 $
.Dd $Mdocdate: April 24 2014 $
.Dt APROPOS 1
.Os
.Sh NAME
.Nm apropos
.Nm apropos ,
.Nm whatis
.Nd search manual page databases
.Sh SYNOPSIS
.Nm
.Op Fl C Ar file
.Op Fl M Ar path
.Op Fl m Ar path
.Op Fl O Ar outkey
.Op Fl S Ar arch
.Op Fl s Ar section
.Ar expression ...
.Sh DESCRIPTION
The
.Nm
utility queries manual page databases generated by
.Xr mandocdb 8 ,
evaluating on
.Nm apropos
and
.Nm whatis
utilities query manual page databases generated by
.Xr makewhatis 8 ,
evaluating
.Ar expression
for each file in each database.
By default, it displays the names, section numbers, and description lines
of all matching manuals.
.Pp
By default,
.Nm
searches for
.Xr mandocdb 8
.Xr makewhatis 8
databases in the default paths stipulated by
.Xr man 1 ,
parses terms as case-sensitive regular expressions
.Pq the Li \&~ operator
.Xr man 1
and uses case-insensitive substring matching
.Pq the Cm = No operator
over manual names and descriptions
.Pq the Li \&Nm No and Li \&Nd No macro keys .
Multiple terms imply pairwise
.Fl o .
.Nm whatis
maps terms only to case-sensitive manual names.
.Pp
Its arguments are as follows:
.Bl -tag -width Ds
@ -61,22 +70,32 @@ format.
.It Fl M Ar path
Use the colon-separated path instead of the default list of paths
searched for
.Xr mandocdb 8
.Xr makewhatis 8
databases.
Invalid paths, or paths without manual databases, are ignored.
.It Fl m Ar path
Prepend the colon-separated paths to the list of paths searched
for
.Xr mandocdb 8
.Xr makewhatis 8
databases.
Invalid paths, or paths without manual databases, are ignored.
.It Fl O Ar outkey
Show the values associated with the key
.Ar outkey
instead of the manual descriptions.
.It Fl S Ar arch
Search only for a particular architecture.
.It Fl s Ar cat
Search only for a manual section.
Restrict the search to pages for the specified
.Xr machine 1
architecture.
.Ar arch
is case insensitive.
By default, pages for all architectures are shown.
.It Fl s Ar section
Restrict the search to the specified section of the manual.
By default, pages from all sections are shown.
See
.Xr man 1
for a listing of manual sections.
for a listing of sections.
.El
.Pp
An
@ -103,34 +122,40 @@ True if both
and
.Ar expr2
are true (logical
.Qq and ) .
.Sq and ) .
.It Ar expr1 Oo Fl o Oc Ar expr2
True if
.Ar expr1
and/or
.Ar expr2
evaluate to true (logical
.Qq or ) .
.Sq or ) .
.It Ar term
True if
.Ar term
is satisfied.
This has syntax
.Li [key[,key]*(=~)]?val ,
where operand
.Cm key
.Sm off
.Oo
.Op Ar key Op , Ar key ...
.Pq Cm = | ~
.Oc
.Ar val ,
.Sm on
where
.Ar key
is an
.Xr mdoc 7
macro to query and
.Cm val
.Ar val
is its value.
See
.Sx Macro Keys
for a list of available keys.
Operator
.Li \&=
.Cm =
evaluates a substring, while
.Li \&~
.Cm ~
evaluates a regular expression.
.It Fl i Ar term
If
@ -140,34 +165,38 @@ is evaluated case-insensitively.
Has no effect on substring terms.
.El
.Pp
Results are sorted by manual title, with output formatted as
.Nm whatis
considers an
.Ar expression
to consist of an opaque keyword.
.Pp
.D1 title(sec) \- description
Results are sorted by manual sections and names, with output formatted as
.Pp
.D1 name[, name...](sec) \- description
.Pp
Where
.Qq title
is the manual's title (note multiple manual names may exist for one
title),
.Qq sec
.Dq name
is the manual's name,
.Dq sec
is the manual section, and
.Qq description
.Dq description
is the manual's short description.
If an architecture is specified for the manual, it is displayed as
.Pp
.D1 title(cat/arch) \- description
.D1 name(sec/arch) \- description
.Pp
Resulting manuals may be accessed as
.Pp
.Dl $ man \-s sec title
.Dl $ man \-s sec name
.Pp
If an architecture is specified in the output, use
.Pp
.Dl $ man \-s sec \-S arch title
.Dl $ man \-s sec \-S arch name
.Ss Macro Keys
Queries evaluate over a subset of
.Xr mdoc 7
macros indexed by
.Xr mandocdb 8 .
.Xr makewhatis 8 .
In addition to the macro keys listed below, the special key
.Cm any
may be used to match any available macro key.
@ -176,6 +205,8 @@ Names and description:
.Bl -column "xLix" description -offset indent -compact
.It Li \&Nm Ta manual name
.It Li \&Nd Ta one-line manual description
.It Li arch Ta machine architecture (case-insensitive)
.It Li sec Ta manual section number
.El
.Pp
Sections and cross references:
@ -239,35 +270,31 @@ Text production:
.It Li \&Dx Ta Dx No version reference
.El
.Sh ENVIRONMENT
.Bl -tag -width Ds
.Bl -tag -width MANPATH
.It Ev MANPATH
Colon-separated paths modifying the default list of paths searched for
manual databases.
The standard search path used by
.Xr man 1
may be changed by specifying a path in the
.Ev MANPATH
environment variable.
Invalid paths, or paths without manual databases, are ignored.
Overridden by
.Fl M .
If
.Ev MANPATH
begins with a
.Sq \&: ,
it is appended to the default list;
else if it ends with
.Sq \&: ,
it is prepended to the default list; else if it contains
.Sq \&:: ,
the default list is inserted between the colons.
If none of these conditions are met, it overrides the default list.
begins with a colon, it is appended to the default list;
if it ends with a colon, it is prepended to the default list;
or if it contains two adjacent colons,
the standard search path is inserted between the colons.
If none of these conditions are met, it overrides the
standard search path.
.El
.Sh FILES
.Bl -tag -width "/etc/man.conf" -compact
.It Pa mandoc.db
name of the
.Xr mandocdb 8
.Xr makewhatis 8
keyword database
.It Pa mandoc.index
name of the
.Xr mandocdb 8
filename database
.It Pa /etc/man.conf
default
.Xr man 1
@ -277,35 +304,84 @@ configuration file
.Ex -std
.Sh EXAMPLES
Search for
.Qq mdoc
as a substring and regular expression
within each manual name and description:
.Qq .cf
as a substring of manual names and descriptions:
.Pp
.Dl $ apropos mdoc
.Dl $ apropos ~^mdoc$
.Dl $ apropos .cf
.Pp
Include matches for
.Qq roff
.Qq .cnf
and
.Qq man
for the regular expression case:
.Qq .conf
as well:
.Pp
.Dl $ apropos ~^mdoc$ roff man
.Dl $ apropos ~^mdoc$ \-o roff \-o man
.Dl $ apropos .cf .cnf .conf
.Pp
Search for
Search in names and descriptions using a regular expression:
.Pp
.Dl $ apropos '~set.?[ug]id'
.Pp
Search for manuals in the library section mentioning both the
.Qq optind
and
and the
.Qq optarg
as variable names in the library category:
variables:
.Pp
.Dl $ apropos \-s 3 Va~^optind \-a Va~^optarg$
.Dl $ apropos \-s 3 Va=optind \-a Va=optarg
.Pp
Do exactly the same as calling
.Xr whatis 1
with the argument
.Qq ssh :
.Pp
.Dl $ apropos \-\- \-i 'Nm~[[:<:]]ssh[[:>:]]'
.Pp
The following two invocations are equivalent:
.Pp
.D1 Li $ apropos -S Ar arch Li -s Ar section expression
.Bd -ragged -offset indent
.Li $ apropos \e( Ar expression Li \e)
.Li -a arch~^( Ns Ar arch Ns Li |any)$
.Li -a sec~^ Ns Ar section Ns Li $
.Ed
.Sh SEE ALSO
.Xr man 1 ,
.Xr re_format 7 ,
.Xr mandocdb 8
.Sh AUTHORS
The
.Xr makewhatis 8
.Sh HISTORY
An
.Nm
utility was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
utility first appeared in
.Bx 2 .
It was rewritten from scratch for
.Ox 5.6 .
.Pp
The
.Fl M
option and the
.Ev MANPATH
variable first appeared in
.Bx 4.3 ;
.Fl m
in
.Bx 4.3 Reno ;
.Fl C
in
.Bx 4.4 Lite1 ;
and
.Fl S
and
.Fl s
in
.Ox 4.5 .
.Sh AUTHORS
.An -nosplit
.An Bill Joy
wrote the original
.Bx
.Nm
in February 1979.
The current version was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
and
.An Ingo Schwarze Aq Mt schwarze@openbsd.org .

121
apropos.c
View File

@ -1,7 +1,7 @@
/* $Id: apropos.c,v 1.27.2.1 2013/09/17 23:23:10 schwarze Exp $ */
/* $Id: apropos.c,v 1.39 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -22,30 +22,28 @@
#include <assert.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "apropos_db.h"
#include "mandoc.h"
#include "manpath.h"
#include "mansearch.h"
static int cmp(const void *, const void *);
static void list(struct res *, size_t, void *);
static char *progname;
int
main(int argc, char *argv[])
{
int ch, rc, whatis;
struct res *res;
int ch, whatis;
struct mansearch search;
size_t i, sz;
struct manpage *res;
struct manpaths paths;
size_t terms, ressz;
struct opts opts;
struct expr *e;
char *defpaths, *auxpaths;
char *conf_file;
char *progname;
const char *outkey;
extern char *optarg;
extern int optind;
@ -58,30 +56,31 @@ main(int argc, char *argv[])
whatis = (0 == strncmp(progname, "whatis", 6));
memset(&paths, 0, sizeof(struct manpaths));
memset(&opts, 0, sizeof(struct opts));
memset(&search, 0, sizeof(struct mansearch));
ressz = 0;
res = NULL;
auxpaths = defpaths = NULL;
conf_file = NULL;
e = NULL;
outkey = "Nd";
while (-1 != (ch = getopt(argc, argv, "C:M:m:S:s:")))
while (-1 != (ch = getopt(argc, argv, "C:M:m:O:S:s:")))
switch (ch) {
case ('C'):
case 'C':
conf_file = optarg;
break;
case ('M'):
case 'M':
defpaths = optarg;
break;
case ('m'):
case 'm':
auxpaths = optarg;
break;
case ('S'):
opts.arch = optarg;
case 'O':
outkey = optarg;
break;
case ('s'):
opts.cat = optarg;
case 'S':
search.arch = optarg;
break;
case 's':
search.sec = optarg;
break;
default:
goto usage;
@ -93,64 +92,32 @@ main(int argc, char *argv[])
if (0 == argc)
goto usage;
rc = 0;
search.deftype = whatis ? TYPE_Nm : TYPE_Nm | TYPE_Nd;
search.flags = whatis ? MANSEARCH_WHATIS : 0;
manpath_parse(&paths, conf_file, defpaths, auxpaths);
e = whatis ? termcomp(argc, argv, &terms) :
exprcomp(argc, argv, &terms);
if (NULL == e) {
fprintf(stderr, "%s: Bad expression\n", progname);
goto out;
}
rc = apropos_search
(paths.sz, paths.paths, &opts,
e, terms, NULL, &ressz, &res, list);
if (0 == rc) {
fprintf(stderr, "%s: Bad database\n", progname);
goto out;
}
out:
mansearch_setup(1);
ch = mansearch(&search, &paths, argc, argv, outkey, &res, &sz);
manpath_free(&paths);
resfree(res, ressz);
exprfree(e);
return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
if (0 == ch)
goto usage;
for (i = 0; i < sz; i++) {
printf("%s - %s\n", res[i].names,
NULL == res[i].output ? "" : res[i].output);
free(res[i].file);
free(res[i].names);
free(res[i].output);
}
free(res);
mansearch_setup(0);
return(sz ? EXIT_SUCCESS : EXIT_FAILURE);
usage:
fprintf(stderr, "usage: %s [-C file] [-M path] [-m path] "
"[-O outkey] "
"[-S arch] [-s section]%s ...\n", progname,
whatis ? " name" : "\n expression");
return(EXIT_FAILURE);
}
/* ARGSUSED */
static void
list(struct res *res, size_t sz, void *arg)
{
size_t i;
qsort(res, sz, sizeof(struct res), cmp);
for (i = 0; i < sz; i++) {
if ( ! res[i].matched)
continue;
printf("%s(%s%s%s) - %.70s\n",
res[i].title,
res[i].cat,
*res[i].arch ? "/" : "",
*res[i].arch ? res[i].arch : "",
res[i].desc);
}
}
static int
cmp(const void *p1, const void *p2)
{
return(strcasecmp(((const struct res *)p1)->title,
((const struct res *)p2)->title));
}

View File

@ -1,884 +0,0 @@
/* $Id: apropos_db.c,v 1.32.2.3 2013/10/10 23:43:04 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/param.h>
#include <assert.h>
#include <fcntl.h>
#include <regex.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if defined(__APPLE__)
# include <libkern/OSByteOrder.h>
#elif defined(__linux__)
# include <endian.h>
#elif defined(__sun)
# include <sys/byteorder.h>
#else
# include <sys/endian.h>
#endif
#if defined(__linux__) || defined(__sun)
# include <db_185.h>
#else
# include <db.h>
#endif
#include "mandocdb.h"
#include "apropos_db.h"
#include "mandoc.h"
#define RESFREE(_x) \
do { \
free((_x)->file); \
free((_x)->cat); \
free((_x)->title); \
free((_x)->arch); \
free((_x)->desc); \
free((_x)->matches); \
} while (/*CONSTCOND*/0)
struct expr {
int regex; /* is regex? */
int index; /* index in match array */
uint64_t mask; /* type-mask */
int and; /* is rhs of logical AND? */
char *v; /* search value */
regex_t re; /* compiled re, if regex */
struct expr *next; /* next in sequence */
struct expr *subexpr;
};
struct type {
uint64_t mask;
const char *name;
};
struct rectree {
struct res *node; /* record array for dir tree */
int len; /* length of record array */
};
static const struct type types[] = {
{ TYPE_An, "An" },
{ TYPE_Ar, "Ar" },
{ TYPE_At, "At" },
{ TYPE_Bsx, "Bsx" },
{ TYPE_Bx, "Bx" },
{ TYPE_Cd, "Cd" },
{ TYPE_Cm, "Cm" },
{ TYPE_Dv, "Dv" },
{ TYPE_Dx, "Dx" },
{ TYPE_Em, "Em" },
{ TYPE_Er, "Er" },
{ TYPE_Ev, "Ev" },
{ TYPE_Fa, "Fa" },
{ TYPE_Fl, "Fl" },
{ TYPE_Fn, "Fn" },
{ TYPE_Fn, "Fo" },
{ TYPE_Ft, "Ft" },
{ TYPE_Fx, "Fx" },
{ TYPE_Ic, "Ic" },
{ TYPE_In, "In" },
{ TYPE_Lb, "Lb" },
{ TYPE_Li, "Li" },
{ TYPE_Lk, "Lk" },
{ TYPE_Ms, "Ms" },
{ TYPE_Mt, "Mt" },
{ TYPE_Nd, "Nd" },
{ TYPE_Nm, "Nm" },
{ TYPE_Nx, "Nx" },
{ TYPE_Ox, "Ox" },
{ TYPE_Pa, "Pa" },
{ TYPE_Rs, "Rs" },
{ TYPE_Sh, "Sh" },
{ TYPE_Ss, "Ss" },
{ TYPE_St, "St" },
{ TYPE_Sy, "Sy" },
{ TYPE_Tn, "Tn" },
{ TYPE_Va, "Va" },
{ TYPE_Va, "Vt" },
{ TYPE_Xr, "Xr" },
{ UINT64_MAX, "any" },
{ 0, NULL }
};
static DB *btree_open(void);
static int btree_read(const DBT *, const DBT *,
const struct mchars *,
uint64_t *, recno_t *, char **);
static int expreval(const struct expr *, int *);
static void exprexec(const struct expr *,
const char *, uint64_t, struct res *);
static int exprmark(const struct expr *,
const char *, uint64_t, int *);
static struct expr *exprexpr(int, char *[], int *, int *, size_t *);
static struct expr *exprterm(char *, int);
static DB *index_open(void);
static int index_read(const DBT *, const DBT *, int,
const struct mchars *, struct res *);
static void norm_string(const char *,
const struct mchars *, char **);
static size_t norm_utf8(unsigned int, char[7]);
static int single_search(struct rectree *, const struct opts *,
const struct expr *, size_t terms,
struct mchars *, int);
/*
* Open the keyword mandoc-db database.
*/
static DB *
btree_open(void)
{
BTREEINFO info;
DB *db;
memset(&info, 0, sizeof(BTREEINFO));
info.lorder = 4321;
info.flags = R_DUP;
db = dbopen(MANDOC_DB, O_RDONLY, 0, DB_BTREE, &info);
if (NULL != db)
return(db);
return(NULL);
}
/*
* Read a keyword from the database and normalise it.
* Return 0 if the database is insane, else 1.
*/
static int
btree_read(const DBT *k, const DBT *v, const struct mchars *mc,
uint64_t *mask, recno_t *rec, char **buf)
{
uint64_t vbuf[2];
/* Are our sizes sane? */
if (k->size < 2 || sizeof(vbuf) != v->size)
return(0);
/* Is our string nil-terminated? */
if ('\0' != ((const char *)k->data)[(int)k->size - 1])
return(0);
norm_string((const char *)k->data, mc, buf);
memcpy(vbuf, v->data, v->size);
*mask = betoh64(vbuf[0]);
*rec = betoh64(vbuf[1]);
return(1);
}
/*
* 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
norm_utf8(unsigned int cp, char out[7])
{
int 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((size_t)rc);
}
/*
* Normalise strings from the index and database.
* These strings are escaped as defined by mandoc_char(7) along with
* other goop in mandoc.h (e.g., soft hyphens).
* This function normalises these into a nice UTF-8 string.
* Returns 0 if the database is fucked.
*/
static void
norm_string(const char *val, const struct mchars *mc, char **buf)
{
size_t sz, bsz;
char utfbuf[7];
const char *seq, *cpp;
int len, u, pos;
enum mandoc_esc esc;
static const char res[] = { '\\', '\t',
ASCII_NBRSP, ASCII_HYPH, '\0' };
/* Pre-allocate by the length of the input */
bsz = strlen(val) + 1;
*buf = mandoc_realloc(*buf, bsz);
pos = 0;
while ('\0' != *val) {
/*
* Halt on the first escape sequence.
* This also halts on the end of string, in which case
* we just copy, fallthrough, and exit the loop.
*/
if ((sz = strcspn(val, res)) > 0) {
memcpy(&(*buf)[pos], val, sz);
pos += (int)sz;
val += (int)sz;
}
if (ASCII_HYPH == *val) {
(*buf)[pos++] = '-';
val++;
continue;
} else if ('\t' == *val || ASCII_NBRSP == *val) {
(*buf)[pos++] = ' ';
val++;
continue;
} else if ('\\' != *val)
break;
/* Read past the slash. */
val++;
u = 0;
/*
* Parse the escape sequence and see if it's a
* predefined character or special character.
*/
esc = mandoc_escape(&val, &seq, &len);
if (ESCAPE_ERROR == esc)
break;
/*
* XXX - this just does UTF-8, but we need to know
* beforehand whether we should do text substitution.
*/
switch (esc) {
case (ESCAPE_SPECIAL):
if (0 != (u = mchars_spec2cp(mc, seq, len)))
break;
/* FALLTHROUGH */
default:
continue;
}
/*
* If we have a Unicode codepoint, try to convert that
* to a UTF-8 byte string.
*/
cpp = utfbuf;
if (0 == (sz = norm_utf8(u, utfbuf)))
continue;
/* Copy the rendered glyph into the stream. */
sz = strlen(cpp);
bsz += sz;
*buf = mandoc_realloc(*buf, bsz);
memcpy(&(*buf)[pos], cpp, sz);
pos += (int)sz;
}
(*buf)[pos] = '\0';
}
/*
* Open the filename-index mandoc-db database.
* Returns NULL if opening failed.
*/
static DB *
index_open(void)
{
DB *db;
db = dbopen(MANDOC_IDX, O_RDONLY, 0, DB_RECNO, NULL);
if (NULL != db)
return(db);
return(NULL);
}
/*
* Safely unpack from an index file record into the structure.
* Returns 1 if an entry was unpacked, 0 if the database is insane.
*/
static int
index_read(const DBT *key, const DBT *val, int index,
const struct mchars *mc, struct res *rec)
{
size_t left;
char *np, *cp;
char type;
#define INDEX_BREAD(_dst) \
do { \
if (NULL == (np = memchr(cp, '\0', left))) \
return(0); \
norm_string(cp, mc, &(_dst)); \
left -= (np - cp) + 1; \
cp = np + 1; \
} while (/* CONSTCOND */ 0)
if (0 == (left = val->size))
return(0);
cp = val->data;
assert(sizeof(recno_t) == key->size);
memcpy(&rec->rec, key->data, key->size);
rec->volume = index;
if ('d' == (type = *cp++))
rec->type = RESTYPE_MDOC;
else if ('a' == type)
rec->type = RESTYPE_MAN;
else if ('c' == type)
rec->type = RESTYPE_CAT;
else
return(0);
left--;
INDEX_BREAD(rec->file);
INDEX_BREAD(rec->cat);
INDEX_BREAD(rec->title);
INDEX_BREAD(rec->arch);
INDEX_BREAD(rec->desc);
return(1);
}
/*
* Search mandocdb databases in paths for expression "expr".
* Filter out by "opts".
* Call "res" with the results, which may be zero.
* Return 0 if there was a database error, else return 1.
*/
int
apropos_search(int pathsz, char **paths, const struct opts *opts,
const struct expr *expr, size_t terms, void *arg,
size_t *sz, struct res **resp,
void (*res)(struct res *, size_t, void *))
{
struct rectree tree;
struct mchars *mc;
int i;
memset(&tree, 0, sizeof(struct rectree));
mc = mchars_alloc();
*sz = 0;
*resp = NULL;
/*
* Main loop. Change into the directory containing manpage
* databases. Run our expession over each database in the set.
*/
for (i = 0; i < pathsz; i++) {
assert('/' == paths[i][0]);
if (chdir(paths[i]))
continue;
if (single_search(&tree, opts, expr, terms, mc, i))
continue;
resfree(tree.node, tree.len);
mchars_free(mc);
return(0);
}
(*res)(tree.node, tree.len, arg);
*sz = tree.len;
*resp = tree.node;
mchars_free(mc);
return(1);
}
static int
single_search(struct rectree *tree, const struct opts *opts,
const struct expr *expr, size_t terms,
struct mchars *mc, int vol)
{
int root, leaf, ch;
DBT key, val;
DB *btree, *idx;
char *buf;
struct res *rs;
struct res r;
uint64_t mask;
recno_t rec;
root = -1;
leaf = -1;
btree = NULL;
idx = NULL;
buf = NULL;
rs = tree->node;
memset(&r, 0, sizeof(struct res));
if (NULL == (btree = btree_open()))
return(1);
if (NULL == (idx = index_open())) {
(*btree->close)(btree);
return(1);
}
while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) {
if ( ! btree_read(&key, &val, mc, &mask, &rec, &buf))
break;
/*
* See if this keyword record matches any of the
* expressions we have stored.
*/
if ( ! exprmark(expr, buf, mask, NULL))
continue;
/*
* O(log n) scan for prior records. Since a record
* number is unbounded, this has decent performance over
* a complex hash function.
*/
for (leaf = root; leaf >= 0; )
if (rec > rs[leaf].rec &&
rs[leaf].rhs >= 0)
leaf = rs[leaf].rhs;
else if (rec < rs[leaf].rec &&
rs[leaf].lhs >= 0)
leaf = rs[leaf].lhs;
else
break;
/*
* If we find a record, see if it has already evaluated
* to true. If it has, great, just keep going. If not,
* try to evaluate it now and continue anyway.
*/
if (leaf >= 0 && rs[leaf].rec == rec) {
if (0 == rs[leaf].matched)
exprexec(expr, buf, mask, &rs[leaf]);
continue;
}
/*
* We have a new file to examine.
* Extract the manpage's metadata from the index
* database, then begin partial evaluation.
*/
key.data = &rec;
key.size = sizeof(recno_t);
if (0 != (*idx->get)(idx, &key, &val, 0))
break;
r.lhs = r.rhs = -1;
if ( ! index_read(&key, &val, vol, mc, &r))
break;
/* XXX: this should be elsewhere, I guess? */
if (opts->cat && strcasecmp(opts->cat, r.cat))
continue;
if (opts->arch && *r.arch)
if (strcasecmp(opts->arch, r.arch))
continue;
tree->node = rs = mandoc_realloc
(rs, (tree->len + 1) * sizeof(struct res));
memcpy(&rs[tree->len], &r, sizeof(struct res));
memset(&r, 0, sizeof(struct res));
rs[tree->len].matches =
mandoc_calloc(terms, sizeof(int));
exprexec(expr, buf, mask, &rs[tree->len]);
/* Append to our tree. */
if (leaf >= 0) {
if (rec > rs[leaf].rec)
rs[leaf].rhs = tree->len;
else
rs[leaf].lhs = tree->len;
} else
root = tree->len;
tree->len++;
}
(*btree->close)(btree);
(*idx->close)(idx);
free(buf);
RESFREE(&r);
return(1 == ch);
}
void
resfree(struct res *rec, size_t sz)
{
size_t i;
for (i = 0; i < sz; i++)
RESFREE(&rec[i]);
free(rec);
}
/*
* Compile a list of straight-up terms.
* The arguments are re-written into ~[[:<:]]term[[:>:]], or "term"
* surrounded by word boundaries, then pumped through exprterm().
* Terms are case-insensitive.
* This emulates whatis(1) behaviour.
*/
struct expr *
termcomp(int argc, char *argv[], size_t *tt)
{
char *buf;
int pos;
struct expr *e, *next;
size_t sz;
buf = NULL;
e = NULL;
*tt = 0;
for (pos = argc - 1; pos >= 0; pos--) {
sz = strlen(argv[pos]) + 18;
buf = mandoc_realloc(buf, sz);
strlcpy(buf, "Nm~[[:<:]]", sz);
strlcat(buf, argv[pos], sz);
strlcat(buf, "[[:>:]]", sz);
if (NULL == (next = exprterm(buf, 0))) {
free(buf);
exprfree(e);
return(NULL);
}
next->next = e;
e = next;
(*tt)++;
}
free(buf);
return(e);
}
/*
* Compile a sequence of logical expressions.
* See apropos.1 for a grammar of this sequence.
*/
struct expr *
exprcomp(int argc, char *argv[], size_t *tt)
{
int pos, lvl;
struct expr *e;
pos = lvl = 0;
*tt = 0;
e = exprexpr(argc, argv, &pos, &lvl, tt);
if (0 == lvl && pos >= argc)
return(e);
exprfree(e);
return(NULL);
}
/*
* Compile an array of tokens into an expression.
* An informal expression grammar is defined in apropos(1).
* Return NULL if we fail doing so. All memory will be cleaned up.
* Return the root of the expression sequence if alright.
*/
static struct expr *
exprexpr(int argc, char *argv[], int *pos, int *lvl, size_t *tt)
{
struct expr *e, *first, *next;
int log;
first = next = NULL;
for ( ; *pos < argc; (*pos)++) {
e = next;
/*
* Close out a subexpression.
*/
if (NULL != e && 0 == strcmp(")", argv[*pos])) {
if (--(*lvl) < 0)
goto err;
break;
}
/*
* Small note: if we're just starting, don't let "-a"
* and "-o" be considered logical operators: they're
* just tokens unless pairwise joining, in which case we
* record their existence (or assume "OR").
*/
log = 0;
if (NULL != e && 0 == strcmp("-a", argv[*pos]))
log = 1;
else if (NULL != e && 0 == strcmp("-o", argv[*pos]))
log = 2;
if (log > 0 && ++(*pos) >= argc)
goto err;
/*
* Now we parse the term part. This can begin with
* "-i", in which case the expression is case
* insensitive.
*/
if (0 == strcmp("(", argv[*pos])) {
++(*pos);
++(*lvl);
next = mandoc_calloc(1, sizeof(struct expr));
next->subexpr = exprexpr(argc, argv, pos, lvl, tt);
if (NULL == next->subexpr) {
free(next);
next = NULL;
}
} else if (0 == strcmp("-i", argv[*pos])) {
if (++(*pos) >= argc)
goto err;
next = exprterm(argv[*pos], 0);
} else
next = exprterm(argv[*pos], 1);
if (NULL == next)
goto err;
next->and = log == 1;
next->index = (int)(*tt)++;
/* Append to our chain of expressions. */
if (NULL == first) {
assert(NULL == e);
first = next;
} else {
assert(NULL != e);
e->next = next;
}
}
return(first);
err:
exprfree(first);
return(NULL);
}
/*
* Parse a terminal expression with the grammar as defined in
* apropos(1).
* Return NULL if we fail the parse.
*/
static struct expr *
exprterm(char *buf, int cs)
{
struct expr e;
struct expr *p;
char *key;
int i;
memset(&e, 0, sizeof(struct expr));
/* Choose regex or substring match. */
if (NULL == (e.v = strpbrk(buf, "=~"))) {
e.regex = 0;
e.v = buf;
} else {
e.regex = '~' == *e.v;
*e.v++ = '\0';
}
/* Determine the record types to search for. */
e.mask = 0;
if (buf < e.v) {
while (NULL != (key = strsep(&buf, ","))) {
i = 0;
while (types[i].mask &&
strcmp(types[i].name, key))
i++;
e.mask |= types[i].mask;
}
}
if (0 == e.mask)
e.mask = TYPE_Nm | TYPE_Nd;
if (e.regex) {
i = REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE);
if (regcomp(&e.re, e.v, i))
return(NULL);
}
e.v = mandoc_strdup(e.v);
p = mandoc_calloc(1, sizeof(struct expr));
memcpy(p, &e, sizeof(struct expr));
return(p);
}
void
exprfree(struct expr *p)
{
struct expr *pp;
while (NULL != p) {
if (p->subexpr)
exprfree(p->subexpr);
if (p->regex)
regfree(&p->re);
free(p->v);
pp = p->next;
free(p);
p = pp;
}
}
static int
exprmark(const struct expr *p, const char *cp,
uint64_t mask, int *ms)
{
for ( ; p; p = p->next) {
if (p->subexpr) {
if (exprmark(p->subexpr, cp, mask, ms))
return(1);
continue;
} else if ( ! (mask & p->mask))
continue;
if (p->regex) {
if (regexec(&p->re, cp, 0, NULL, 0))
continue;
} else if (NULL == strcasestr(cp, p->v))
continue;
if (NULL == ms)
return(1);
else
ms[p->index] = 1;
}
return(0);
}
static int
expreval(const struct expr *p, int *ms)
{
int match;
/*
* AND has precedence over OR. Analysis is left-right, though
* it doesn't matter because there are no side-effects.
* Thus, step through pairwise ANDs and accumulate their Boolean
* evaluation. If we encounter a single true AND collection or
* standalone term, the whole expression is true (by definition
* of OR).
*/
for (match = 0; p && ! match; p = p->next) {
/* Evaluate a subexpression, if applicable. */
if (p->subexpr && ! ms[p->index])
ms[p->index] = expreval(p->subexpr, ms);
match = ms[p->index];
for ( ; p->next && p->next->and; p = p->next) {
/* Evaluate a subexpression, if applicable. */
if (p->next->subexpr && ! ms[p->next->index])
ms[p->next->index] =
expreval(p->next->subexpr, ms);
match = match && ms[p->next->index];
}
}
return(match);
}
/*
* First, update the array of terms for which this expression evaluates
* to true.
* Second, logically evaluate all terms over the updated array of truth
* values.
* If this evaluates to true, mark the expression as satisfied.
*/
static void
exprexec(const struct expr *e, const char *cp,
uint64_t mask, struct res *r)
{
assert(0 == r->matched);
exprmark(e, cp, mask, r->matches);
r->matched = expreval(e, r->matches);
}

View File

@ -1,73 +0,0 @@
/* $Id: apropos_db.h,v 1.13 2012/03/24 01:46:25 kristaps Exp $ */
/*
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* 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.
*/
#ifndef APROPOS_H
#define APROPOS_H
enum restype {
RESTYPE_MAN, /* man(7) file */
RESTYPE_MDOC, /* mdoc(7) file */
RESTYPE_CAT /* pre-formatted file */
};
struct res {
enum restype type; /* input file type */
char *file; /* file in file-system */
char *cat; /* category (3p, 3, etc.) */
char *title; /* title (FOO, etc.) */
char *arch; /* arch (or empty string) */
char *desc; /* description (from Nd) */
unsigned int rec; /* record in index */
/*
* The index volume. This indexes into the array of directories
* searched for manual page databases.
*/
unsigned int volume;
/*
* The following fields are used internally.
*
* Maintain a binary tree for checking the uniqueness of `rec'
* when adding elements to the results array.
* Since the results array is dynamic, use offset in the array
* instead of a pointer to the structure.
*/
int lhs;
int rhs;
int matched; /* expression is true */
int *matches; /* partial truth evaluations */
};
struct opts {
const char *arch; /* restrict to architecture */
const char *cat; /* restrict to manual section */
};
__BEGIN_DECLS
struct expr;
int apropos_search(int, char **, const struct opts *,
const struct expr *, size_t,
void *, size_t *, struct res **,
void (*)(struct res *, size_t, void *));
struct expr *exprcomp(int, char *[], size_t *);
void exprfree(struct expr *);
void resfree(struct res *, size_t);
struct expr *termcomp(int, char *[], size_t *);
__END_DECLS
#endif /*!APROPOS_H*/

8
arch.c
View File

@ -1,4 +1,4 @@
/* $Id: arch.c,v 1.9 2011/03/22 14:33:05 kristaps Exp $ */
/* $Id: arch.c,v 1.11 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -18,22 +18,20 @@
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mdoc.h"
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mdoc_a2arch(const char *p)
{
#include "arch.in"
#include "arch.in"
return(NULL);
}

View File

@ -1,4 +1,4 @@
/* $Id: arch.in,v 1.14 2013/09/16 22:12:57 schwarze Exp $ */
/* $Id: arch.in,v 1.15 2014/04/27 22:42:15 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -65,8 +65,8 @@ LINE("ibmnws", "IBMNWS")
LINE("iyonix", "Iyonix")
LINE("landisk", "LANDISK")
LINE("loongson", "Loongson")
LINE("luna68k", "Luna68k")
LINE("luna88k", "Luna88k")
LINE("luna68k", "LUNA68K")
LINE("luna88k", "LUNA88K")
LINE("m68k", "m68k")
LINE("mac68k", "Mac68k")
LINE("macppc", "MacPPC")

8
att.c
View File

@ -1,4 +1,4 @@
/* $Id: att.c,v 1.9 2011/03/22 14:33:05 kristaps Exp $ */
/* $Id: att.c,v 1.11 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -18,22 +18,20 @@
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mdoc.h"
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mdoc_a2att(const char *p)
{
#include "att.in"
#include "att.in"
return(NULL);
}

111
catman.8
View File

@ -1,111 +0,0 @@
.\" $Id: catman.8,v 1.5 2011/12/25 19:35:44 kristaps Exp $
.\"
.\" Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" 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.
.\"
.Dd $Mdocdate: December 25 2011 $
.Dt CATMAN 8
.Os
.Sh NAME
.Nm catman
.Nd update a man.cgi manpage cache
.Sh SYNOPSIS
.Nm catman
.Op Fl fv
.Op Fl C Ar file
.Op Fl M Ar manpath
.Op Fl m Ar manpath
.Op Fl o Ar path
.Sh DESCRIPTION
The
.Nm
utility updates cached manpages for a jailed
.Xr man.cgi 7 .
.Pp
By default,
.Nm
searches for
.Xr mandocdb 8
databases in the default paths stipulated by
.Xr man 1
and updates the cache in
.Pa /var/www/cache/man.cgi .
.Pp
Its arguments are as follows:
.Bl -tag -width Ds
.It Fl f
Force an update to all files.
.It Fl v
Print each file being updated.
.It Fl C Ar file
Specify an alternative configuration
.Ar file
in
.Xr man.conf 5
format.
.It Fl M Ar manpath
Use the colon-separated path instead of the default list of paths
searched for
.Xr mandocdb 8
databases.
Invalid paths, or paths without manual databases, are ignored.
.It Fl m Ar manpath
Prepend the colon-separated paths to the list of paths searched
for
.Xr mandocdb 8
databases.
Invalid paths, or paths without manual databases, are ignored.
.It Fl o Ar path
Update into the directory tree under
.Ar path .
.El
.Pp
Cache updates occur when a
.Xr mandocdb 8
database is older than the cached copy unless
.Fl f
is specified, in which case files are always considered out of date.
Cached manual pages are only updated if older than the master copy.
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It Ev MANPATH
Colon-separated paths modifying the default list of paths searched for
manual databases.
Invalid paths, or paths without manual databases, are ignored.
Overridden by
.Fl M .
If
.Ev MANPATH
begins with a
.Sq \&: ,
it is appended to the default list;
else if it ends with
.Sq \&: ,
it is prepended to the default list; else if it contains
.Sq \&:: ,
the default list is inserted between the colons.
If none of these conditions are met, it overrides the default list.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr man.cgi 7 ,
.Xr mandocdb 8
.Sh AUTHORS
The
.Nm
utility was written by
.An Kristaps Dzonsons ,
.Mt kristaps@bsd.lv .

509
catman.c
View File

@ -1,509 +0,0 @@
/* $Id: catman.c,v 1.11.2.2 2013/10/11 00:06:48 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if defined(__linux__) || defined(__sun)
# include <db_185.h>
#else
# include <db.h>
#endif
#include "manpath.h"
#include "mandocdb.h"
#define xstrlcpy(_dst, _src, _sz) \
do if (strlcpy((_dst), (_src), (_sz)) >= (_sz)) { \
fprintf(stderr, "%s: Path too long", (_dst)); \
exit(EXIT_FAILURE); \
} while (/* CONSTCOND */0)
#define xstrlcat(_dst, _src, _sz) \
do if (strlcat((_dst), (_src), (_sz)) >= (_sz)) { \
fprintf(stderr, "%s: Path too long", (_dst)); \
exit(EXIT_FAILURE); \
} while (/* CONSTCOND */0)
static int indexhtml(char *, size_t, char *, size_t);
static int manup(const struct manpaths *, char *);
static int mkpath(char *, mode_t, mode_t);
static int treecpy(char *, char *);
static int update(char *, char *);
static void usage(void);
static const char *progname;
static int verbose;
static int force;
int
main(int argc, char *argv[])
{
int ch;
char *aux, *base, *conf_file;
struct manpaths dirs;
char buf[MAXPATHLEN];
extern char *optarg;
extern int optind;
progname = strrchr(argv[0], '/');
if (progname == NULL)
progname = argv[0];
else
++progname;
aux = base = conf_file = NULL;
xstrlcpy(buf, "/var/www/cache/man.cgi", MAXPATHLEN);
while (-1 != (ch = getopt(argc, argv, "C:fm:M:o:v")))
switch (ch) {
case ('C'):
conf_file = optarg;
break;
case ('f'):
force = 1;
break;
case ('m'):
aux = optarg;
break;
case ('M'):
base = optarg;
break;
case ('o'):
xstrlcpy(buf, optarg, MAXPATHLEN);
break;
case ('v'):
verbose++;
break;
default:
usage();
return(EXIT_FAILURE);
}
argc -= optind;
argv += optind;
if (argc > 0) {
usage();
return(EXIT_FAILURE);
}
memset(&dirs, 0, sizeof(struct manpaths));
manpath_parse(&dirs, conf_file, base, aux);
ch = manup(&dirs, buf);
manpath_free(&dirs);
return(ch ? EXIT_SUCCESS : EXIT_FAILURE);
}
static void
usage(void)
{
fprintf(stderr, "usage: %s "
"[-fv] "
"[-C file] "
"[-o path] "
"[-m manpath] "
"[-M manpath]\n",
progname);
}
/*
* If "src" file doesn't exist (errors out), return -1. Otherwise,
* return 1 if "src" is newer (which also happens "dst" doesn't exist)
* and 0 otherwise.
*/
static int
isnewer(const char *dst, const char *src)
{
struct stat s1, s2;
if (-1 == stat(src, &s1))
return(-1);
if (force)
return(1);
return(-1 == stat(dst, &s2) ? 1 : s1.st_mtime > s2.st_mtime);
}
/*
* Copy the contents of one file into another.
* Returns 0 on failure, 1 on success.
*/
static int
filecpy(const char *dst, const char *src)
{
char buf[BUFSIZ];
int sfd, dfd, rc;
ssize_t rsz, wsz;
sfd = dfd = -1;
rc = 0;
if (-1 == (dfd = open(dst, O_CREAT|O_TRUNC|O_WRONLY, 0644))) {
perror(dst);
goto out;
} else if (-1 == (sfd = open(src, O_RDONLY, 0))) {
perror(src);
goto out;
}
while ((rsz = read(sfd, buf, BUFSIZ)) > 0)
if (-1 == (wsz = write(dfd, buf, (size_t)rsz))) {
perror(dst);
goto out;
} else if (wsz < rsz) {
fprintf(stderr, "%s: Short write\n", dst);
goto out;
}
if (rsz < 0)
perror(src);
else
rc = 1;
out:
if (-1 != sfd)
close(sfd);
if (-1 != dfd)
close(dfd);
return(rc);
}
/*
* Pass over the recno database and re-create HTML pages if they're
* found to be out of date.
* Returns -1 on fatal error, 1 on success.
*/
static int
indexhtml(char *src, size_t ssz, char *dst, size_t dsz)
{
DB *idx;
DBT key, val;
int c, rc;
unsigned int fl;
const char *f;
char *d;
char fname[MAXPATHLEN];
xstrlcpy(fname, dst, MAXPATHLEN);
xstrlcat(fname, "/", MAXPATHLEN);
xstrlcat(fname, MANDOC_IDX, MAXPATHLEN);
idx = dbopen(fname, O_RDONLY, 0, DB_RECNO, NULL);
if (NULL == idx) {
perror(fname);
return(-1);
}
fl = R_FIRST;
while (0 == (c = (*idx->seq)(idx, &key, &val, fl))) {
fl = R_NEXT;
/*
* If the record is zero-length, then it's unassigned.
* Skip past these.
*/
if (0 == val.size)
continue;
f = (const char *)val.data + 1;
if (NULL == memchr(f, '\0', val.size - 1))
break;
src[(int)ssz] = dst[(int)dsz] = '\0';
xstrlcat(dst, "/", MAXPATHLEN);
xstrlcat(dst, f, MAXPATHLEN);
xstrlcat(src, "/", MAXPATHLEN);
xstrlcat(src, f, MAXPATHLEN);
if (-1 == (rc = isnewer(dst, src))) {
fprintf(stderr, "%s: File missing\n", f);
break;
} else if (0 == rc)
continue;
d = strrchr(dst, '/');
assert(NULL != d);
*d = '\0';
if (-1 == mkpath(dst, 0755, 0755)) {
perror(dst);
break;
}
*d = '/';
if ( ! filecpy(dst, src))
break;
if (verbose)
printf("%s\n", dst);
}
(*idx->close)(idx);
if (c < 0)
perror(fname);
else if (0 == c)
fprintf(stderr, "%s: Corrupt index\n", fname);
return(1 == c ? 1 : -1);
}
/*
* Copy both recno and btree databases into the destination.
* Call in to begin recreating HTML files.
* Return -1 on fatal error and 1 if the update went well.
*/
static int
update(char *dst, char *src)
{
size_t dsz, ssz;
dsz = strlen(dst);
ssz = strlen(src);
xstrlcat(src, "/", MAXPATHLEN);
xstrlcat(dst, "/", MAXPATHLEN);
xstrlcat(src, MANDOC_DB, MAXPATHLEN);
xstrlcat(dst, MANDOC_DB, MAXPATHLEN);
if ( ! filecpy(dst, src))
return(-1);
if (verbose)
printf("%s\n", dst);
dst[(int)dsz] = src[(int)ssz] = '\0';
xstrlcat(src, "/", MAXPATHLEN);
xstrlcat(dst, "/", MAXPATHLEN);
xstrlcat(src, MANDOC_IDX, MAXPATHLEN);
xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);
if ( ! filecpy(dst, src))
return(-1);
if (verbose)
printf("%s\n", dst);
dst[(int)dsz] = src[(int)ssz] = '\0';
return(indexhtml(src, ssz, dst, dsz));
}
/*
* See if btree or recno databases in the destination are out of date
* with respect to a single manpath component.
* Return -1 on fatal error, 0 if the source is no longer valid (and
* shouldn't be listed), and 1 if the update went well.
*/
static int
treecpy(char *dst, char *src)
{
size_t dsz, ssz;
int rc;
dsz = strlen(dst);
ssz = strlen(src);
xstrlcat(src, "/", MAXPATHLEN);
xstrlcat(dst, "/", MAXPATHLEN);
xstrlcat(src, MANDOC_IDX, MAXPATHLEN);
xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);
if (-1 == (rc = isnewer(dst, src)))
return(0);
dst[(int)dsz] = src[(int)ssz] = '\0';
if (1 == rc)
return(update(dst, src));
xstrlcat(src, "/", MAXPATHLEN);
xstrlcat(dst, "/", MAXPATHLEN);
xstrlcat(src, MANDOC_DB, MAXPATHLEN);
xstrlcat(dst, MANDOC_DB, MAXPATHLEN);
if (-1 == (rc = isnewer(dst, src)))
return(0);
else if (rc == 0)
return(1);
dst[(int)dsz] = src[(int)ssz] = '\0';
return(update(dst, src));
}
/*
* Update the destination's file-tree with respect to changes in the
* source manpath components.
* "Change" is defined by an updated index or btree database.
* Returns 1 on success, 0 on failure.
*/
static int
manup(const struct manpaths *dirs, char *base)
{
char dst[MAXPATHLEN],
src[MAXPATHLEN];
const char *path;
size_t i;
int c;
size_t sz;
FILE *f;
/* Create the path and file for the catman.conf file. */
sz = strlen(base);
xstrlcpy(dst, base, MAXPATHLEN);
xstrlcat(dst, "/etc", MAXPATHLEN);
if (-1 == mkpath(dst, 0755, 0755)) {
perror(dst);
return(0);
}
xstrlcat(dst, "/catman.conf", MAXPATHLEN);
if (NULL == (f = fopen(dst, "w"))) {
perror(dst);
return(0);
} else if (verbose)
printf("%s\n", dst);
for (i = 0; i < dirs->sz; i++) {
path = dirs->paths[i];
dst[(int)sz] = '\0';
xstrlcat(dst, path, MAXPATHLEN);
if (-1 == mkpath(dst, 0755, 0755)) {
perror(dst);
break;
}
xstrlcpy(src, path, MAXPATHLEN);
if (-1 == (c = treecpy(dst, src)))
break;
else if (0 == c)
continue;
/*
* We want to use a relative path here because manpath.h
* will realpath() when invoked with man.cgi, and we'll
* make sure to chdir() into the cache directory before.
*
* This allows the cache directory to be in an arbitrary
* place, working in both chroot() and non-chroot()
* "safe" modes.
*/
assert('/' == path[0]);
fprintf(f, "_whatdb %s/whatis.db\n", path + 1);
}
fclose(f);
return(i == dirs->sz);
}
/*
* Copyright (c) 1983, 1992, 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.
*/
static int
mkpath(char *path, mode_t mode, mode_t dir_mode)
{
struct stat sb;
char *slash;
int done, exists;
slash = path;
for (;;) {
/* LINTED */
slash += strspn(slash, "/");
/* LINTED */
slash += strcspn(slash, "/");
done = (*slash == '\0');
*slash = '\0';
/* skip existing path components */
exists = !stat(path, &sb);
if (!done && exists && S_ISDIR(sb.st_mode)) {
*slash = '/';
continue;
}
if (mkdir(path, done ? mode : dir_mode) == 0) {
if (mode > 0777 && chmod(path, mode) < 0)
return (-1);
} else {
if (!exists) {
/* Not there */
return (-1);
}
if (!S_ISDIR(sb.st_mode)) {
/* Is there, but isn't a directory */
errno = ENOTDIR;
return (-1);
}
}
if (done)
break;
*slash = '/';
}
return (0);
}

1281
cgi.c

File diff suppressed because it is too large Load Diff

9
cgi.h.example Normal file
View File

@ -0,0 +1,9 @@
/* Example compile-time configuration file for man.cgi(8). */
#define HTTP_HOST "mdocml.bsd.lv"
#define MAN_DIR "/var/www/man"
#define CSS_DIR ""
#define CUSTOMIZE_TITLE "Manual pages with mandoc"
#define CUSTOMIZE_BEGIN "<H2>\nManual pages with " \
"<A HREF=\"http://mdocml.bsd.lv/\">mandoc</A>\n</H2>"
#define COMPAT_OLDURI Yes

35
chars.c
View File

@ -1,4 +1,4 @@
/* $Id: chars.c,v 1.54 2013/06/20 22:39:30 schwarze Exp $ */
/* $Id: chars.c,v 1.58 2014/07/23 15:00:08 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@ -25,6 +25,7 @@
#include <string.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmandoc.h"
#define PRINT_HI 126
@ -37,7 +38,7 @@ struct ln {
int unicode;
};
#define LINES_MAX 329
#define LINES_MAX 330
#define CHAR(in, ch, code) \
{ NULL, (in), (ch), (code) },
@ -51,9 +52,10 @@ struct mchars {
struct ln **htab;
};
static const struct ln *find(const struct mchars *,
static const struct ln *find(const struct mchars *,
const char *, size_t);
void
mchars_free(struct mchars *arg)
{
@ -110,27 +112,38 @@ mchars_spec2cp(const struct mchars *arg, const char *p, size_t sz)
char
mchars_num2char(const char *p, size_t sz)
{
int i;
int i;
if ((i = mandoc_strntoi(p, sz, 10)) < 0)
return('\0');
return(i > 0 && i < 256 && isprint(i) ?
/* LINTED */ i : '\0');
return(i > 0 && i < 256 && isprint(i) ? i : '\0');
}
int
mchars_num2uc(const char *p, size_t sz)
{
int i;
int i;
if ((i = mandoc_strntoi(p, sz, 16)) < 0)
return('\0');
/* FIXME: make sure we're not in a bogus range. */
/*
* Security warning:
* Never extend the range of accepted characters
* to overlap with the ASCII range, 0x00-0x7F
* without re-auditing the callers of this function.
* Some callers might relay on the fact that we never
* return ASCII characters for their escaping decisions.
*
* XXX Code is missing here to exclude bogus ranges.
*/
return(i > 0x80 && i <= 0x10FFFF ? i : '\0');
}
const char *
mchars_spec2str(const struct mchars *arg,
mchars_spec2str(const struct mchars *arg,
const char *p, size_t sz, size_t *rsz)
{
const struct ln *ln;
@ -159,8 +172,8 @@ find(const struct mchars *tab, const char *p, size_t sz)
hash = (int)p[0] - PRINT_LO;
for (pp = tab->htab[hash]; pp; pp = pp->next)
if (0 == strncmp(pp->code, p, sz) &&
'\0' == pp->code[(int)sz])
if (0 == strncmp(pp->code, p, sz) &&
'\0' == pp->code[(int)sz])
return(pp);
return(NULL);

View File

@ -1,6 +1,7 @@
/* $Id: chars.in,v 1.43 2013/06/20 22:39:30 schwarze Exp $ */
/* $Id: chars.in,v 1.46 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -16,7 +17,7 @@
*/
/*
* The ASCII translation tables.
* The ASCII translation tables.
*
* The left-hand side corresponds to the input sequence (\x, \(xx, \*(xx
* and so on) whose length is listed second element. The right-hand
@ -27,39 +28,42 @@
* XXX - update LINES_MAX if adding more!
*/
/* Non-breaking, non-collapsing space uses unit separator. */
/* Special break control characters. */
static const char ascii_nbrsp[2] = { ASCII_NBRSP, '\0' };
static const char ascii_break[2] = { ASCII_BREAK, '\0' };
CHAR_TBL_START
/* Spacing. */
CHAR("c", "", 0)
CHAR("0", " ", 8194)
CHAR(" ", ascii_nbrsp, 160)
CHAR("~", ascii_nbrsp, 160)
CHAR("%", "", 0)
CHAR("&", "", 0)
CHAR("^", "", 0)
CHAR("0", " ", 8194)
CHAR("|", "", 0)
CHAR("}", "", 0)
CHAR("^", "", 0)
CHAR("&", "", 0)
CHAR("%", "", 0)
CHAR(":", ascii_break, 0)
/* XXX The following three do not really belong into this file. */
CHAR("t", "", 0)
CHAR("c", "", 0)
CHAR("}", "", 0)
/* Accents. */
CHAR("a\"", "\"", 779)
CHAR("a\"", "\"", 733)
CHAR("a-", "-", 175)
CHAR("a.", ".", 729)
CHAR("a^", "^", 770)
CHAR("\'", "\'", 769)
CHAR("aa", "\'", 769)
CHAR("ga", "`", 768)
CHAR("`", "`", 768)
CHAR("ab", "`", 774)
CHAR("ac", ",", 807)
CHAR("ad", "\"", 776)
CHAR("a^", "^", 94)
CHAR("\'", "\'", 180)
CHAR("aa", "\'", 180)
CHAR("ga", "`", 96)
CHAR("`", "`", 96)
CHAR("ab", "`", 728)
CHAR("ac", ",", 184)
CHAR("ad", "\"", 168)
CHAR("ah", "v", 711)
CHAR("ao", "o", 730)
CHAR("a~", "~", 771)
CHAR("ho", ",", 808)
CHAR("a~", "~", 126)
CHAR("ho", ",", 731)
CHAR("ha", "^", 94)
CHAR("ti", "~", 126)

339
compat_ohash.c Normal file
View File

@ -0,0 +1,339 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_OHASH
int dummy;
#else
/* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */
/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "compat_ohash.h"
struct _ohash_record {
uint32_t hv;
const char *p;
};
#define DELETED ((const char *)h)
#define NONE (h->size)
/* Don't bother changing the hash table if the change is small enough. */
#define MINSIZE (1UL << 4)
#define MINDELETED 4
static void ohash_resize(struct ohash *);
/* This handles the common case of variable length keys, where the
* key is stored at the end of the record.
*/
void *
ohash_create_entry(struct ohash_info *i, const char *start, const char **end)
{
char *p;
if (!*end)
*end = start + strlen(start);
p = (i->alloc)(i->key_offset + (*end - start) + 1, i->data);
if (p) {
memcpy(p+i->key_offset, start, *end-start);
p[i->key_offset + (*end - start)] = '\0';
}
return (void *)p;
}
/* hash_delete only frees the hash structure. Use hash_first/hash_next
* to free entries as well. */
void
ohash_delete(struct ohash *h)
{
(h->info.free)(h->t, h->info.data);
#ifndef NDEBUG
h->t = NULL;
#endif
}
static void
ohash_resize(struct ohash *h)
{
struct _ohash_record *n;
size_t ns;
unsigned int j;
unsigned int i, incr;
if (4 * h->deleted < h->total) {
if (h->size >= (UINT_MAX >> 1U))
ns = UINT_MAX;
else
ns = h->size << 1U;
} else if (3 * h->deleted > 2 * h->total)
ns = h->size >> 1U;
else
ns = h->size;
if (ns < MINSIZE)
ns = MINSIZE;
#ifdef STATS_HASH
STAT_HASH_EXPAND++;
STAT_HASH_SIZE += ns - h->size;
#endif
n = (h->info.calloc)(ns, sizeof(struct _ohash_record), h->info.data);
if (!n)
return;
for (j = 0; j < h->size; j++) {
if (h->t[j].p != NULL && h->t[j].p != DELETED) {
i = h->t[j].hv % ns;
incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1;
while (n[i].p != NULL) {
i += incr;
if (i >= ns)
i -= ns;
}
n[i].hv = h->t[j].hv;
n[i].p = h->t[j].p;
}
}
(h->info.free)(h->t, h->info.data);
h->t = n;
h->size = ns;
h->total -= h->deleted;
h->deleted = 0;
}
void *
ohash_remove(struct ohash *h, unsigned int i)
{
void *result = (void *)h->t[i].p;
if (result == NULL || result == DELETED)
return NULL;
#ifdef STATS_HASH
STAT_HASH_ENTRIES--;
#endif
h->t[i].p = DELETED;
h->deleted++;
if (h->deleted >= MINDELETED && 4 * h->deleted > h->total)
ohash_resize(h);
return result;
}
void *
ohash_find(struct ohash *h, unsigned int i)
{
if (h->t[i].p == DELETED)
return NULL;
else
return (void *)h->t[i].p;
}
void *
ohash_insert(struct ohash *h, unsigned int i, void *p)
{
#ifdef STATS_HASH
STAT_HASH_ENTRIES++;
#endif
if (h->t[i].p == DELETED) {
h->deleted--;
h->t[i].p = p;
} else {
h->t[i].p = p;
/* Arbitrary resize boundary. Tweak if not efficient enough. */
if (++h->total * 4 > h->size * 3)
ohash_resize(h);
}
return p;
}
unsigned int
ohash_entries(struct ohash *h)
{
return h->total - h->deleted;
}
void *
ohash_first(struct ohash *h, unsigned int *pos)
{
*pos = 0;
return ohash_next(h, pos);
}
void *
ohash_next(struct ohash *h, unsigned int *pos)
{
for (; *pos < h->size; (*pos)++)
if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL)
return (void *)h->t[(*pos)++].p;
return NULL;
}
void
ohash_init(struct ohash *h, unsigned int size, struct ohash_info *info)
{
h->size = 1UL << size;
if (h->size < MINSIZE)
h->size = MINSIZE;
#ifdef STATS_HASH
STAT_HASH_CREATION++;
STAT_HASH_SIZE += h->size;
#endif
/* Copy info so that caller may free it. */
h->info.key_offset = info->key_offset;
h->info.calloc = info->calloc;
h->info.free = info->free;
h->info.alloc = info->alloc;
h->info.data = info->data;
h->t = (h->info.calloc)(h->size, sizeof(struct _ohash_record),
h->info.data);
h->total = h->deleted = 0;
}
uint32_t
ohash_interval(const char *s, const char **e)
{
uint32_t k;
if (!*e)
*e = s + strlen(s);
if (s == *e)
k = 0;
else
k = *s++;
while (s != *e)
k = ((k << 2) | (k >> 30)) ^ *s++;
return k;
}
unsigned int
ohash_lookup_interval(struct ohash *h, const char *start, const char *end,
uint32_t hv)
{
unsigned int i, incr;
unsigned int empty;
#ifdef STATS_HASH
STAT_HASH_LOOKUP++;
#endif
empty = NONE;
i = hv % h->size;
incr = ((hv % (h->size-2)) & ~1) + 1;
while (h->t[i].p != NULL) {
#ifdef STATS_HASH
STAT_HASH_LENGTH++;
#endif
if (h->t[i].p == DELETED) {
if (empty == NONE)
empty = i;
} else if (h->t[i].hv == hv &&
strncmp(h->t[i].p+h->info.key_offset, start,
end - start) == 0 &&
(h->t[i].p+h->info.key_offset)[end-start] == '\0') {
if (empty != NONE) {
h->t[empty].hv = hv;
h->t[empty].p = h->t[i].p;
h->t[i].p = DELETED;
return empty;
} else {
#ifdef STATS_HASH
STAT_HASH_POSITIVE++;
#endif
return i;
}
}
i += incr;
if (i >= h->size)
i -= h->size;
}
/* Found an empty position. */
if (empty != NONE)
i = empty;
h->t[i].hv = hv;
return i;
}
unsigned int
ohash_lookup_memory(struct ohash *h, const char *k, size_t size, uint32_t hv)
{
unsigned int i, incr;
unsigned int empty;
#ifdef STATS_HASH
STAT_HASH_LOOKUP++;
#endif
empty = NONE;
i = hv % h->size;
incr = ((hv % (h->size-2)) & ~1) + 1;
while (h->t[i].p != NULL) {
#ifdef STATS_HASH
STAT_HASH_LENGTH++;
#endif
if (h->t[i].p == DELETED) {
if (empty == NONE)
empty = i;
} else if (h->t[i].hv == hv &&
memcmp(h->t[i].p+h->info.key_offset, k, size) == 0) {
if (empty != NONE) {
h->t[empty].hv = hv;
h->t[empty].p = h->t[i].p;
h->t[i].p = DELETED;
return empty;
} else {
#ifdef STATS_HASH
STAT_HASH_POSITIVE++;
#endif
} return i;
}
i += incr;
if (i >= h->size)
i -= h->size;
}
/* Found an empty position. */
if (empty != NONE)
i = empty;
h->t[i].hv = hv;
return i;
}
unsigned int
ohash_qlookup(struct ohash *h, const char *s)
{
const char *e = NULL;
return ohash_qlookupi(h, s, &e);
}
unsigned int
ohash_qlookupi(struct ohash *h, const char *s, const char **e)
{
uint32_t hv;
hv = ohash_interval(s, e);
return ohash_lookup_interval(h, s, *e, hv);
}
#endif /*!HAVE_OHASH*/

73
compat_ohash.h Normal file
View File

@ -0,0 +1,73 @@
/* $OpenBSD: ohash.h,v 1.2 2014/06/02 18:52:03 deraadt Exp $ */
/* Copyright (c) 1999, 2004 Marc Espie <espie@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.
*/
#ifndef OHASH_H
#define OHASH_H
/* Open hashing support.
* Open hashing was chosen because it is much lighter than other hash
* techniques, and more efficient in most cases.
*/
/* user-visible data structure */
struct ohash_info {
ptrdiff_t key_offset;
void *data; /* user data */
void *(*calloc)(size_t, size_t, void *);
void (*free)(void *, void *);
void *(*alloc)(size_t, void *);
};
struct _ohash_record;
/* private structure. It's there just so you can do a sizeof */
struct ohash {
struct _ohash_record *t;
struct ohash_info info;
unsigned int size;
unsigned int total;
unsigned int deleted;
};
/* For this to be tweakable, we use small primitives, and leave part of the
* logic to the client application. e.g., hashing is left to the client
* application. We also provide a simple table entry lookup that yields
* a hashing table index (opaque) to be used in find/insert/remove.
* The keys are stored at a known position in the client data.
*/
__BEGIN_DECLS
void ohash_init(struct ohash *, unsigned, struct ohash_info *);
void ohash_delete(struct ohash *);
unsigned int ohash_lookup_interval(struct ohash *, const char *,
const char *, uint32_t);
unsigned int ohash_lookup_memory(struct ohash *, const char *,
size_t, uint32_t);
void *ohash_find(struct ohash *, unsigned int);
void *ohash_remove(struct ohash *, unsigned int);
void *ohash_insert(struct ohash *, unsigned int, void *);
void *ohash_first(struct ohash *, unsigned int *);
void *ohash_next(struct ohash *, unsigned int *);
unsigned int ohash_entries(struct ohash *);
void *ohash_create_entry(struct ohash_info *, const char *, const char **);
uint32_t ohash_interval(const char *, const char **);
unsigned int ohash_qlookupi(struct ohash *, const char *, const char **);
unsigned int ohash_qlookup(struct ohash *, const char *);
__END_DECLS
#endif

45
compat_reallocarray.c Normal file
View File

@ -0,0 +1,45 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_REALLOCARRAY
int dummy;
#else
/* $OpenBSD: malloc.c,v 1.158 2014/04/23 15:07:27 tedu Exp $ */
/*
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4))
void *
reallocarray(void *optr, size_t nmemb, size_t size)
{
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
errno = ENOMEM;
return NULL;
}
return realloc(optr, size * nmemb);
}
#endif /*!HAVE_REALLOCARRAY*/

18
compat_sqlite3_errstr.c Normal file
View File

@ -0,0 +1,18 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SQLITE3_ERRSTR
int dummy;
#else
const char *
sqlite3_errstr(int rc)
{
return(rc ? "unknown error" : "not an error");
}
#endif

74
compat_strcasestr.c Normal file
View File

@ -0,0 +1,74 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STRCASESTR
int dummy;
#else
/* ($)NetBSD: strcasestr.c,v 1.2 2005/02/09 21:35:47 kleink Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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.
*/
#include <sys/types.h>
#include <ctype.h>
#include <string.h>
#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
/*
* Find the first occurrence of find in s, ignore case.
*/
char *
strcasestr(const char *s, const char *find)
{
char c, sc;
size_t len;
if ((c = *find++) != 0) {
c = tolower((unsigned char)c);
len = strlen(find);
do {
do {
if ((sc = *s++) == 0)
return (NULL);
} while ((char)tolower((unsigned char)sc) != c);
} while (strncasecmp(s, find, len) != 0);
s--;
}
return __UNCONST(s);
}
#endif

80
compat_strsep.c Normal file
View File

@ -0,0 +1,80 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STRSEP
int dummy;
#else
/* ($)OpenBSD: strsep.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */
/*-
* Copyright (c) 1990, 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.
*/
/*
* Get next token from string *stringp, where tokens are possibly-empty
* strings separated by characters from delim.
*
* Writes NULs into the string at *stringp to end tokens.
* delim need not remain constant from call to call.
* On return, *stringp points past the last NUL written (if there might
* be further tokens), or is NULL (if there are definitely no more tokens).
*
* If *stringp is NULL, strsep returns NULL.
*/
char *
strsep(char **stringp, const char *delim)
{
char *s;
const char *spanp;
int c, sc;
char *tok;
if ((s = *stringp) == NULL)
return (NULL);
for (tok = s;;) {
c = *s++;
spanp = delim;
do {
if ((sc = *spanp++) == c) {
if (c == 0)
s = NULL;
else
s[-1] = 0;
*stringp = s;
return (tok);
}
} while (sc != 0);
}
/* NOTREACHED */
}
#endif

View File

@ -1,5 +1,3 @@
#include <sys/types.h>
#if !defined(__BEGIN_DECLS)
# ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
@ -15,30 +13,30 @@
# endif
#endif
#ifndef HAVE_BETOH64
# if defined(__APPLE__)
# define betoh64(x) OSSwapBigToHostInt64(x)
# define htobe64(x) OSSwapHostToBigInt64(x)
# elif defined(__sun)
# define betoh64(x) BE_64(x)
# define htobe64(x) BE_64(x)
# else
# define betoh64(x) be64toh(x)
# endif
#ifndef HAVE_FGETLN
extern char *fgetln(FILE *, size_t *);
#endif
#ifndef HAVE_GETSUBOPT
extern int getsubopt(char **, char * const *, char **);
extern char *suboptarg;
#endif
#ifndef HAVE_REALLOCARRAY
extern void *reallocarray(void *, size_t, size_t);
#endif
#ifndef HAVE_SQLITE3_ERRSTR
extern const char *sqlite3_errstr(int);
#endif
#ifndef HAVE_STRCASESTR
extern char *strcasestr(const char *, const char *);
#endif
#ifndef HAVE_STRLCAT
extern size_t strlcat(char *, const char *, size_t);
#endif
#ifndef HAVE_STRLCPY
extern size_t strlcpy(char *, const char *, size_t);
#endif
#ifndef HAVE_GETSUBOPT
extern int getsubopt(char **, char * const *, char **);
extern char *suboptarg;
#endif
#ifndef HAVE_FGETLN
extern char *fgetln(FILE *, size_t *);
#ifndef HAVE_STRSEP
extern char *strsep(char **, const char *);
#endif
#endif /* MANDOC_CONFIG_H */

View File

@ -2,7 +2,8 @@
#define MANDOC_CONFIG_H
#if defined(__linux__) || defined(__MINT__)
# define _GNU_SOURCE /* strptime(), getsubopt() */
# define _GNU_SOURCE /* getsubopt(), strcasestr(), strptime() */
#endif
#include <sys/types.h>
#include <stdio.h>

49
configure vendored Executable file
View File

@ -0,0 +1,49 @@
#!/bin/sh
#
# Copyright (c) 2014 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.
echo "/* RUNNING ./CONFIGURE - SHOULD BE USED ONLY VIA MAKE, READ INSTALL */"
set -e
exec > config.h 2> config.log
CFLAGS="${CFLAGS} -Wno-unused -Werror"
runtest() {
echo ${CC} ${CFLAGS} ${3} -o test-${1} test-${1}.c 1>&2
${CC} ${CFLAGS} ${3} -o "test-${1}" "test-${1}.c" 1>&2 || return 0
"./test-${1}" && echo "#define HAVE_${2}" \
|| echo FAILURE: test-${1} returned $? 1>&2
rm "test-${1}"
}
cat config.h.pre
echo
echo "#define VERSION \"${VERSION}\""
runtest fgetln FGETLN
runtest getsubopt GETSUBOPT
runtest mmap MMAP
runtest ohash OHASH "${DBLIB}"
runtest reallocarray REALLOCARRAY
runtest sqlite3_errstr SQLITE3_ERRSTR "${DBLIB}"
runtest strcasestr STRCASESTR
runtest strlcat STRLCAT
runtest strlcpy STRLCPY
runtest strptime STRPTIME
runtest strsep STRSEP
echo
cat config.h.post
exit 0

View File

@ -1,4 +1,4 @@
/* $Id: demandoc.c,v 1.7 2012/05/31 22:27:14 schwarze Exp $ */
/* $Id: demandoc.c,v 1.10 2014/03/19 22:20:43 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -76,7 +76,7 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL, NULL);
mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL, NULL);
assert(mp);
if (0 == argc)
@ -110,7 +110,7 @@ pmandoc(struct mparse *mp, int fd, const char *fn, int list)
return;
}
mparse_result(mp, &mdoc, &man);
mparse_result(mp, &mdoc, &man, NULL);
line = 1;
col = 0;

73
eqn.c
View File

@ -1,4 +1,4 @@
/* $Id: eqn.c,v 1.38 2011/07/25 15:37:00 kristaps Exp $ */
/* $Id: eqn.c,v 1.44 2014/07/06 19:09:00 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -26,6 +26,7 @@
#include <time.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmandoc.h"
#include "libroff.h"
@ -137,12 +138,11 @@ struct eqnsym {
const char *sym;
};
static enum eqn_rest eqn_box(struct eqn_node *, struct eqn_box *);
static struct eqn_box *eqn_box_alloc(struct eqn_node *,
static struct eqn_box *eqn_box_alloc(struct eqn_node *,
struct eqn_box *);
static void eqn_box_free(struct eqn_box *);
static struct eqn_def *eqn_def_find(struct eqn_node *,
static struct eqn_def *eqn_def_find(struct eqn_node *,
const char *, size_t);
static int eqn_do_gfont(struct eqn_node *);
static int eqn_do_gsize(struct eqn_node *);
@ -156,7 +156,7 @@ static enum eqn_rest eqn_list(struct eqn_node *, struct eqn_box *);
static enum eqn_rest eqn_matrix(struct eqn_node *, struct eqn_box *);
static const char *eqn_nexttok(struct eqn_node *, size_t *);
static const char *eqn_nextrawtok(struct eqn_node *, size_t *);
static const char *eqn_next(struct eqn_node *,
static const char *eqn_next(struct eqn_node *,
char, size_t *, int);
static void eqn_rewind(struct eqn_node *);
@ -277,9 +277,9 @@ static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
{ { ">=", 2 }, ">=" }, /* EQNSYM_moreequal */
};
/* ARGSUSED */
enum rofferr
eqn_read(struct eqn_node **epp, int ln,
eqn_read(struct eqn_node **epp, int ln,
const char *p, int pos, int *offs)
{
size_t sz;
@ -298,9 +298,10 @@ eqn_read(struct eqn_node **epp, int ln,
p += 3;
while (' ' == *p || '\t' == *p)
p++;
if ('\0' == *p)
if ('\0' == *p)
return(er);
mandoc_msg(MANDOCERR_ARGSLOST, ep->parse, ln, pos, NULL);
mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse,
ln, pos, "EN %s", p);
return(er);
}
@ -413,11 +414,11 @@ eqn_matrix(struct eqn_node *ep, struct eqn_box *last)
while (EQN_OK == (c = eqn_box(ep, bp)))
switch (bp->last->pile) {
case (EQNPILE_LCOL):
case EQNPILE_LCOL:
/* FALLTHROUGH */
case (EQNPILE_CCOL):
case EQNPILE_CCOL:
/* FALLTHROUGH */
case (EQNPILE_RCOL):
case EQNPILE_RCOL:
continue;
default:
EQN_MSG(MANDOCERR_EQNSYNT, ep);
@ -512,9 +513,8 @@ eqn_box(struct eqn_node *ep, struct eqn_box *last)
for (i = 0; i < (int)EQN__MAX; i++) {
if ( ! EQNSTREQ(&eqnparts[i].str, start, sz))
continue;
return((*eqnparts[i].fp)(ep) ?
EQN_OK : EQN_ERR);
}
return((*eqnparts[i].fp)(ep) ? EQN_OK : EQN_ERR);
}
if (STRNEQ(start, sz, "{", 1)) {
if (EQN_DESCOPE != (c = eqn_eqn(ep, last))) {
@ -529,7 +529,7 @@ eqn_box(struct eqn_node *ep, struct eqn_box *last)
return(EQN_OK);
EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
return(EQN_ERR);
}
}
for (i = 0; i < (int)EQNPILE__MAX; i++) {
if ( ! EQNSTREQ(&eqnpiles[i], start, sz))
@ -575,7 +575,7 @@ eqn_box(struct eqn_node *ep, struct eqn_box *last)
if (NULL == last->last) {
EQN_MSG(MANDOCERR_EQNSYNT, ep);
return(EQN_ERR);
}
}
last->last->pos = (enum eqn_post)i;
if (EQN_EOF == (c = eqn_box(ep, last))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
@ -590,7 +590,7 @@ eqn_box(struct eqn_node *ep, struct eqn_box *last)
if (NULL == last->last) {
EQN_MSG(MANDOCERR_EQNSYNT, ep);
return(EQN_ERR);
}
}
last->last->mark = (enum eqn_markt)i;
if (EQN_EOF == (c = eqn_box(ep, last))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
@ -629,7 +629,7 @@ eqn_box(struct eqn_node *ep, struct eqn_box *last)
for (i = 0; i < (int)EQNSYM__MAX; i++)
if (EQNSTREQ(&eqnsyms[i].str, start, sz)) {
sym[63] = '\0';
snprintf(sym, 62, "\\[%s]", eqnsyms[i].sym);
(void)snprintf(sym, 62, "\\[%s]", eqnsyms[i].sym);
bp->text = mandoc_strdup(sym);
return(EQN_OK);
}
@ -762,13 +762,13 @@ eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)
if (q)
ep->cur++;
while (' ' == ep->data[(int)ep->cur] ||
'\t' == ep->data[(int)ep->cur] ||
'^' == ep->data[(int)ep->cur] ||
'~' == ep->data[(int)ep->cur])
'\t' == ep->data[(int)ep->cur] ||
'^' == ep->data[(int)ep->cur] ||
'~' == ep->data[(int)ep->cur])
ep->cur++;
} else {
if (q)
EQN_MSG(MANDOCERR_BADQUOTE, ep);
EQN_MSG(MANDOCERR_ARG_QUOTE, ep);
next = strchr(start, '\0');
*sz = (size_t)(next - start);
ep->cur += *sz;
@ -790,8 +790,8 @@ eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)
}
diff = def->valsz - *sz;
memmove(start + *sz + diff, start + *sz,
(strlen(start) - *sz) + 1);
memmove(start + *sz + diff, start + *sz,
(strlen(start) - *sz) + 1);
memcpy(start, def->val, def->valsz);
goto again;
}
@ -852,8 +852,8 @@ eqn_do_define(struct eqn_node *ep)
return(0);
}
/*
* Search for a key that already exists.
/*
* Search for a key that already exists.
* Create a new key if none is found.
*/
@ -865,15 +865,14 @@ eqn_do_define(struct eqn_node *ep)
if (i == (int)ep->defsz) {
ep->defsz++;
ep->defs = mandoc_realloc
(ep->defs, ep->defsz *
sizeof(struct eqn_def));
ep->defs = mandoc_reallocarray(ep->defs,
ep->defsz, sizeof(struct eqn_def));
ep->defs[i].key = ep->defs[i].val = NULL;
}
ep->defs[i].keysz = sz;
ep->defs[i].key = mandoc_realloc
(ep->defs[i].key, sz + 1);
ep->defs[i].key = mandoc_realloc(
ep->defs[i].key, sz + 1);
memcpy(ep->defs[i].key, start, sz);
ep->defs[i].key[(int)sz] = '\0';
@ -901,7 +900,7 @@ eqn_do_gfont(struct eqn_node *ep)
if (NULL == eqn_nextrawtok(ep, NULL)) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(0);
}
}
return(1);
}
@ -914,7 +913,7 @@ eqn_do_gsize(struct eqn_node *ep)
if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(0);
}
}
ep->gsize = mandoc_strntoi(start, sz, 10);
return(1);
}
@ -940,9 +939,9 @@ eqn_def_find(struct eqn_node *ep, const char *key, size_t sz)
{
int i;
for (i = 0; i < (int)ep->defsz; i++)
if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
ep->defs[i].keysz, key, sz))
for (i = 0; i < (int)ep->defsz; i++)
if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
ep->defs[i].keysz, key, sz))
return(&ep->defs[i]);
return(NULL);

View File

@ -1,4 +1,4 @@
/* $Id: eqn_html.c,v 1.2 2011/07/24 10:09:03 kristaps Exp $ */
/* $Id: eqn_html.c,v 1.3 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -35,9 +35,9 @@ static const enum htmltag fontmap[EQNFONT__MAX] = {
TAG_I /* EQNFONT_ITALIC */
};
static void eqn_box(struct html *, const struct eqn_box *);
void
print_eqn(struct html *p, const struct eqn *ep)
{
@ -59,12 +59,12 @@ eqn_box(struct html *p, const struct eqn_box *bp)
{
struct tag *t;
t = EQNFONT_NONE == bp->font ? NULL :
print_otag(p, fontmap[(int)bp->font], 0, NULL);
t = EQNFONT_NONE == bp->font ? NULL :
print_otag(p, fontmap[(int)bp->font], 0, NULL);
if (bp->left)
print_text(p, bp->left);
if (bp->text)
print_text(p, bp->text);

View File

@ -1,4 +1,4 @@
/* $Id: eqn_term.c,v 1.4 2011/07/24 10:09:03 kristaps Exp $ */
/* $Id: eqn_term.c,v 1.5 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -37,6 +37,7 @@ static const enum termfont fontmap[EQNFONT__MAX] = {
static void eqn_box(struct termp *, const struct eqn_box *);
void
term_eqn(struct termp *p, const struct eqn *ep)
{
@ -68,7 +69,7 @@ eqn_box(struct termp *p, const struct eqn_box *bp)
term_word(p, ")");
if (bp->right)
term_word(p, bp->right);
if (EQNFONT_NONE != bp->font)
if (EQNFONT_NONE != bp->font)
term_fontpop(p);
if (bp->next)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 B

9
gmdiff
View File

@ -1,5 +1,5 @@
#!/bin/sh
# Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
# Copyright (c) 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@ -27,8 +27,11 @@ while [ -n "$1" ]; do
file=$1
shift
echo " ========== $file ========== "
tbl $file | groff -mandoc -Tascii -P -c 2>&1 > /tmp/groff.out
mandoc -Ios='OpenBSD ports' -Werror $file 2>&1 > /tmp/mandoc.out
tbl $file | groff -mandoc -Tascii -P -c 2> /tmp/groff.err > /tmp/groff.out
mandoc -Ios='OpenBSD ports' -Werror $file 2> /tmp/mandoc.err > /tmp/mandoc.out
for i in groff mandoc; do
[[ -s /tmp/$i.err ]] && echo "$i errors:" && cat /tmp/$i.err
done
diff -au /tmp/groff.out /tmp/mandoc.out 2>&1
done

183
html.c
View File

@ -1,7 +1,7 @@
/* $Id: html.c,v 1.152 2013/08/08 20:07:47 schwarze Exp $ */
/* $Id: html.c,v 1.159 2014/07/23 15:00:08 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011, 2012, 2013, 2014 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,6 +31,7 @@
#include <unistd.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmandoc.h"
#include "out.h"
#include "html.h"
@ -109,11 +110,13 @@ static const char *const roffscales[SCALE_MAX] = {
static void bufncat(struct html *, const char *, size_t);
static void print_ctag(struct html *, enum htmltag);
static int print_escape(char);
static int print_encode(struct html *, const char *, int);
static void print_metaf(struct html *, enum mandoc_esc);
static void print_attr(struct html *, const char *, const char *);
static void *ml_alloc(char *, enum htmltype);
static void *
ml_alloc(char *outopts, enum htmltype type)
{
@ -135,16 +138,16 @@ ml_alloc(char *outopts, enum htmltype type)
while (outopts && *outopts)
switch (getsubopt(&outopts, UNCONST(toks), &v)) {
case (0):
case 0:
h->style = v;
break;
case (1):
case 1:
h->base_man = v;
break;
case (2):
case 2:
h->base_includes = v;
break;
case (3):
case 3:
h->oflags |= HTML_FRAGMENT;
break;
default:
@ -161,7 +164,6 @@ html_alloc(char *outopts)
return(ml_alloc(outopts, HTML_HTML_4_01_STRICT));
}
void *
xhtml_alloc(char *outopts)
{
@ -169,7 +171,6 @@ xhtml_alloc(char *outopts)
return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT));
}
void
html_free(void *p)
{
@ -179,17 +180,16 @@ html_free(void *p)
h = (struct html *)p;
while ((tag = h->tags.head) != NULL) {
h->tags.head = tag->next;
h->tags.head = tag->next;
free(tag);
}
if (h->symtab)
mchars_free(h->symtab);
free(h);
}
void
print_gen_head(struct html *h)
{
@ -226,21 +226,21 @@ print_metaf(struct html *h, enum mandoc_esc deco)
enum htmlfont font;
switch (deco) {
case (ESCAPE_FONTPREV):
case ESCAPE_FONTPREV:
font = h->metal;
break;
case (ESCAPE_FONTITALIC):
case ESCAPE_FONTITALIC:
font = HTMLFONT_ITALIC;
break;
case (ESCAPE_FONTBOLD):
case ESCAPE_FONTBOLD:
font = HTMLFONT_BOLD;
break;
case (ESCAPE_FONTBI):
case ESCAPE_FONTBI:
font = HTMLFONT_BI;
break;
case (ESCAPE_FONT):
case ESCAPE_FONT:
/* FALLTHROUGH */
case (ESCAPE_FONTROMAN):
case ESCAPE_FONTROMAN:
font = HTMLFONT_NONE;
break;
default:
@ -257,13 +257,13 @@ print_metaf(struct html *h, enum mandoc_esc deco)
h->metac = font;
switch (font) {
case (HTMLFONT_ITALIC):
case HTMLFONT_ITALIC:
h->metaf = print_otag(h, TAG_I, 0, NULL);
break;
case (HTMLFONT_BOLD):
case HTMLFONT_BOLD:
h->metaf = print_otag(h, TAG_B, 0, NULL);
break;
case (HTMLFONT_BI):
case HTMLFONT_BI:
h->metaf = print_otag(h, TAG_B, 0, NULL);
print_otag(h, TAG_I, 0, NULL);
break;
@ -302,19 +302,19 @@ html_strlen(const char *cp)
break;
cp++;
switch (mandoc_escape(&cp, NULL, NULL)) {
case (ESCAPE_ERROR):
case ESCAPE_ERROR:
return(sz);
case (ESCAPE_UNICODE):
case ESCAPE_UNICODE:
/* FALLTHROUGH */
case (ESCAPE_NUMBERED):
case ESCAPE_NUMBERED:
/* FALLTHROUGH */
case (ESCAPE_SPECIAL):
case ESCAPE_SPECIAL:
if (skip)
skip = 0;
else
sz++;
break;
case (ESCAPE_SKIPCHAR):
case ESCAPE_SKIPCHAR:
skip = 1;
break;
default:
@ -324,6 +324,37 @@ html_strlen(const char *cp)
return(sz);
}
static int
print_escape(char c)
{
switch (c) {
case '<':
printf("&lt;");
break;
case '>':
printf("&gt;");
break;
case '&':
printf("&amp;");
break;
case '"':
printf("&quot;");
break;
case ASCII_NBRSP:
putchar('-');
break;
case ASCII_HYPH:
putchar('-');
/* FALLTHROUGH */
case ASCII_BREAK:
break;
default:
return(0);
}
return(1);
}
static int
print_encode(struct html *h, const char *p, int norecurse)
{
@ -331,7 +362,8 @@ print_encode(struct html *h, const char *p, int norecurse)
int c, len, nospace;
const char *seq;
enum mandoc_esc esc;
static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' };
static const char rejs[9] = { '\\', '<', '>', '&', '"',
ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' };
nospace = 0;
@ -350,43 +382,29 @@ print_encode(struct html *h, const char *p, int norecurse)
if ('\0' == *p)
break;
switch (*p++) {
case ('<'):
printf("&lt;");
if (print_escape(*p++))
continue;
case ('>'):
printf("&gt;");
continue;
case ('&'):
printf("&amp;");
continue;
case (ASCII_HYPH):
putchar('-');
continue;
default:
break;
}
esc = mandoc_escape(&p, &seq, &len);
if (ESCAPE_ERROR == esc)
break;
switch (esc) {
case (ESCAPE_FONT):
case ESCAPE_FONT:
/* FALLTHROUGH */
case (ESCAPE_FONTPREV):
case ESCAPE_FONTPREV:
/* FALLTHROUGH */
case (ESCAPE_FONTBOLD):
case ESCAPE_FONTBOLD:
/* FALLTHROUGH */
case (ESCAPE_FONTITALIC):
case ESCAPE_FONTITALIC:
/* FALLTHROUGH */
case (ESCAPE_FONTBI):
case ESCAPE_FONTBI:
/* FALLTHROUGH */
case (ESCAPE_FONTROMAN):
case ESCAPE_FONTROMAN:
if (0 == norecurse)
print_metaf(h, esc);
continue;
case (ESCAPE_SKIPCHAR):
case ESCAPE_SKIPCHAR:
h->flags |= HTML_SKIPCHAR;
continue;
default:
@ -399,25 +417,26 @@ print_encode(struct html *h, const char *p, int norecurse)
}
switch (esc) {
case (ESCAPE_UNICODE):
/* Skip passed "u" header. */
case ESCAPE_UNICODE:
/* Skip past "u" header. */
c = mchars_num2uc(seq + 1, len - 1);
if ('\0' != c)
printf("&#x%x;", c);
break;
case (ESCAPE_NUMBERED):
case ESCAPE_NUMBERED:
c = mchars_num2char(seq, len);
if ('\0' != c)
if ( ! ('\0' == c || print_escape(c)))
putchar(c);
break;
case (ESCAPE_SPECIAL):
case ESCAPE_SPECIAL:
c = mchars_spec2cp(h->symtab, seq, len);
if (c > 0)
printf("&#%d;", c);
else if (-1 == c && 1 == len)
else if (-1 == c && 1 == len &&
!print_escape(*seq))
putchar((int)*seq);
break;
case (ESCAPE_NOSPACE):
case ESCAPE_NOSPACE:
if ('\0' == *p)
nospace = 1;
break;
@ -429,7 +448,6 @@ print_encode(struct html *h, const char *p, int norecurse)
return(nospace);
}
static void
print_attr(struct html *h, const char *key, const char *val)
{
@ -438,9 +456,8 @@ print_attr(struct html *h, const char *key, const char *val)
putchar('\"');
}
struct tag *
print_otag(struct html *h, enum htmltag tag,
print_otag(struct html *h, enum htmltag tag,
int sz, const struct htmlpair *p)
{
int i;
@ -490,7 +507,7 @@ print_otag(struct html *h, enum htmltag tag,
if (HTML_AUTOCLOSE & htmltags[tag].flags)
switch (h->type) {
case (HTML_XHTML_1_0_STRICT):
case HTML_XHTML_1_0_STRICT:
putchar('/');
break;
default:
@ -507,16 +524,15 @@ print_otag(struct html *h, enum htmltag tag,
return(t);
}
static void
print_ctag(struct html *h, enum htmltag tag)
{
printf("</%s>", htmltags[tag].name);
if (HTML_CLRLINE & htmltags[tag].flags) {
h->flags |= HTML_NOSPACE;
putchar('\n');
}
}
}
void
@ -527,7 +543,7 @@ print_gen_decls(struct html *h)
const char *name;
switch (h->type) {
case (HTML_HTML_4_01_STRICT):
case HTML_HTML_4_01_STRICT:
name = "HTML";
doctype = "-//W3C//DTD HTML 4.01//EN";
dtd = "http://www.w3.org/TR/html4/strict.dtd";
@ -540,8 +556,8 @@ print_gen_decls(struct html *h)
break;
}
printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n",
name, doctype, dtd);
printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n",
name, doctype, dtd);
}
void
@ -560,13 +576,13 @@ print_text(struct html *h, const char *word)
assert(NULL == h->metaf);
switch (h->metac) {
case (HTMLFONT_ITALIC):
case HTMLFONT_ITALIC:
h->metaf = print_otag(h, TAG_I, 0, NULL);
break;
case (HTMLFONT_BOLD):
case HTMLFONT_BOLD:
h->metaf = print_otag(h, TAG_B, 0, NULL);
break;
case (HTMLFONT_BI):
case HTMLFONT_BI:
h->metaf = print_otag(h, TAG_B, 0, NULL);
print_otag(h, TAG_I, 0, NULL);
break;
@ -589,14 +605,13 @@ print_text(struct html *h, const char *word)
h->flags &= ~HTML_IGNDELIM;
}
void
print_tagq(struct html *h, const struct tag *until)
{
struct tag *tag;
while ((tag = h->tags.head) != NULL) {
/*
/*
* Remember to close out and nullify the current
* meta-font and table, if applicable.
*/
@ -612,7 +627,6 @@ print_tagq(struct html *h, const struct tag *until)
}
}
void
print_stagq(struct html *h, const struct tag *suntil)
{
@ -621,7 +635,7 @@ print_stagq(struct html *h, const struct tag *suntil)
while ((tag = h->tags.head) != NULL) {
if (suntil && tag == suntil)
return;
/*
/*
* Remember to close out and nullify the current
* meta-font and table, if applicable.
*/
@ -657,6 +671,12 @@ void
bufcat(struct html *h, const char *p)
{
/*
* 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.
*/
h->buflen = strlcat(h->buf, p, BUFSIZ);
assert(h->buflen < BUFSIZ);
}
@ -667,8 +687,8 @@ bufcat_fmt(struct html *h, const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
(void)vsnprintf(h->buf + (int)h->buflen,
BUFSIZ - h->buflen - 1, fmt, ap);
(void)vsnprintf(h->buf + (int)h->buflen,
BUFSIZ - h->buflen - 1, fmt, ap);
va_end(ap);
h->buflen = strlen(h->buf);
}
@ -688,12 +708,12 @@ 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'):
case'I':
bufcat(h, name);
break;
default:
@ -707,22 +727,21 @@ buffmt_includes(struct html *h, const char *name)
}
void
buffmt_man(struct html *h,
const char *name, const char *sec)
buffmt_man(struct html *h, const char *name, const char *sec)
{
const char *p, *pp;
pp = h->base_man;
bufinit(h);
while (NULL != (p = strchr(pp, '%'))) {
bufncat(h, pp, (size_t)(p - pp));
switch (*(p + 1)) {
case('S'):
case 'S':
bufcat(h, sec ? sec : "1");
break;
case('N'):
bufcat_fmt(h, name);
case 'N':
bufcat_fmt(h, "%s", name);
break;
default:
bufncat(h, p, 2);

17
html.h
View File

@ -1,4 +1,4 @@
/* $Id: html.h,v 1.49 2013/08/08 20:07:47 schwarze Exp $ */
/* $Id: html.h,v 1.51 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -105,7 +105,7 @@ struct htmlpair {
#define PAIR_STYLE_INIT(p, h) PAIR_INIT(p, ATTR_STYLE, (h)->buf)
#define PAIR_SUMMARY_INIT(p, v) PAIR_INIT(p, ATTR_SUMMARY, v)
enum htmltype {
enum htmltype {
HTML_HTML_4_01_STRICT,
HTML_XHTML_1_0_STRICT
};
@ -127,7 +127,7 @@ struct html {
char *base_includes; /* base for include href */
char *style; /* style-sheet URI */
char buf[BUFSIZ]; /* see bufcat and friends */
size_t buflen;
size_t buflen;
struct tag *metaf; /* current open font scope */
enum htmlfont metal; /* last used font */
enum htmlfont metac; /* current font mode */
@ -138,7 +138,7 @@ struct html {
void print_gen_decls(struct html *);
void print_gen_head(struct html *);
struct tag *print_otag(struct html *, enum htmltag,
struct tag *print_otag(struct html *, enum htmltag,
int, const struct htmlpair *);
void print_tagq(struct html *, const struct tag *);
void print_stagq(struct html *, const struct tag *);
@ -147,15 +147,18 @@ void print_tblclose(struct html *);
void print_tbl(struct html *, const struct tbl_span *);
void print_eqn(struct html *, const struct eqn *);
#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 *,
void bufcat_style(struct html *,
const char *, const char *);
void bufcat_su(struct html *, const char *,
void bufcat_su(struct html *, const char *,
const struct roffsu *);
void bufinit(struct html *);
void buffmt_man(struct html *,
void buffmt_man(struct html *,
const char *, const char *);
void buffmt_includes(struct html *, const char *);

View File

@ -1,48 +0,0 @@
html { min-width: 40em;
margin-top: 2em;
margin-left: auto;
margin-right: auto;
width: 80%; }
body { text-align: justify;
font-family: Helvetica,Arial,sans-serif;
line-height: 120%;
font-size: small; }
p,ul,table { margin-left: 3em; }
p.head,
p.subhead,
p.foot { margin-left: 0.0em; margin-right: 0.0em; }
p.news { margin-left: 2.0em; }
li { margin: 0.25em; }
h1 { font-size: 110%; }
h2 { font-size: 105%; margin-left: 1.5em }
p.head { margin-bottom: 0.5em;
border-bottom: 1px solid #dddddd;
padding-bottom: 0.2em; }
p.subhead { margin-top: 0em;
margin-bottom: 1.75em; }
p.foot { border-top: 1px solid #dddddd;
color: #666666;
padding-top: 0.2em;
margin-top: 1.75em; }
span.nm { color: green; }
span.file { font-style: italic; }
span.attn { font-weight: bold; }
span.flag { font-weight: bold; }
a { text-decoration: none; }
a.external { background: transparent url(external.png) center right no-repeat;
padding-right: 12px; }

View File

@ -1,438 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<LINK REL="stylesheet" HREF="index.css" TYPE="text/css" MEDIA="all">
<TITLE>mdocml | UNIX manpage compiler</TITLE>
</HEAD>
<BODY>
<P CLASS="head">
<A HREF="http://www.openbsd.org/"><IMG SRC="puffy.gif" ALT="Puffy" WIDTH="100" HEIGHT="91" STYLE="float: right"></A>
<B>mdocml</B> &#8211; UNIX manpage compiler, current version @VERSION@ (@VDATE@)
</P>
<P CLASS="subhead">
Sources: <A HREF="/snapshots/mdocml.tar.gz">current</A>,
<A HREF="/cgi-bin/cvsweb/?cvsroot=mdocml">cvsweb</A>
(<A HREF="/snapshots/">archives</A>)
</P>
<H1>
<A NAME="description">Description</A>
</H1>
<P>
<SPAN CLASS="nm">mdocml</SPAN> is a suite of tools compiling <I><A HREF="mdoc.7.html">mdoc</A></I>, the roff macro
package of choice for BSD manual pages, and <I><A HREF="man.7.html">man</A></I>, the predominant historical package for
UNIX manuals.
It is small, ISO C, <A CLASS="external" HREF="http://www.isc.org/software/license">ISC</A>-licensed, and quite fast.
</P>
<P>
The tool set features <A HREF="mandoc.1.html">mandoc</A>,
based on the <A HREF="mandoc.3.html">libmandoc</A> validating compiler,
to format output for UNIX terminals (with
support for wide-character locales), XHTML, HTML, PostScript, and PDF.
It also includes <A HREF="preconv.1.html">preconv</A>, for recoding multibyte manuals;
<A HREF="demandoc.1.html">demandoc</A>, for emitting only text parts of manuals;
<A HREF="mandocdb.8.html">mandocdb</A>, for indexing manuals; and
<A HREF="apropos.1.html">apropos</A>, <A HREF="whatis.1.html">whatis</A>, and
<A HREF="man.cgi.7.html">man.cgi</A> (via <A HREF="catman.8.html">catman</A>) for semantic search of manual content.
</P>
<P>
<SPAN CLASS="nm">mdocml</SPAN> has predominantly been developed on OpenBSD
and is both an <A CLASS="external" HREF="http://www.openbsd.org/">OpenBSD</A>
and a <A CLASS="external" HREF="http://bsd.lv/">BSD.lv</A> project.
We strive to support all interested free operating systems, in particular
<A CLASS="external" HREF="http://www.dragonflybsd.org/">DragonFly</A>,
<A CLASS="external" HREF="http://www.netbsd.org/">NetBSD</A>,
<A CLASS="external" HREF="http://www.freebsd.org/">FreeBSD</A>,
<A CLASS="external" HREF="http://www.minix3.org/">Minix 3</A>,
and <A CLASS="external" HREF="http://www.gnu.org/">GNU</A>/Linux,
as well as all systems running the <A CLASS="external" HREF="http://www.pkgsrc.org/">pkgsrc</A> portable package build system.
All of these projects have helped to make <SPAN CLASS="nm">mdocml</SPAN> better, by providing feedback and advice,
bug reports, and patches.
</P>
<P>
<I>Disambiguation</I>: <SPAN CLASS="nm">mdocml</SPAN> is often referred to by its installed binary, <Q>mandoc</Q>.
</P>
<H2>
<A NAME="sources">Sources</A>
</H2>
<P>
<SPAN CLASS="nm">mdocml</SPAN> should build and run on any modern system with
<A HREF="http://www.oracle.com/technetwork/database/berkeleydb/overview/index.html">libdb</A>
(this is installed by default on BSD UNIX systems &mdash; see the <I>Makefile</I> if you're running Linux).
To build and install into <I>/usr/local/</I>, just run <CODE>make install</CODE>.
Be careful: the <B>preconv</B>, <B>apropos</B>, and <B>whatis</B> installed binary names
may be taken by existing utilities.
</P>
<H2>
Downstream
</H2>
<P>
Several systems come bundled with <SPAN CLASS="nm">mdocml</SPAN> utilities.
If your system does not appear below, the maintainers have not contacted me and it should not be considered
<Q>official</Q>, so please <A HREF="#contact">contact us</A> if you plan on maintaining a downstream version!
</P>
<TABLE WIDTH="100%" SUMMARY="Downstream Sources">
<COL WIDTH="175">
<COL>
<TBODY>
<TR>
<TD>DragonFly BSD</TD>
<TD>
<A HREF="http://gitweb.dragonflybsd.org/dragonfly.git/tree/HEAD:/contrib/mdocml" CLASS="external">contrib/mdocml</A> (1.12.3 sources)
<A HREF="http://gitweb.dragonflybsd.org/dragonfly.git/tree/HEAD:/lib/libmandoc" CLASS="external">lib/libmandoc</A>
<A HREF="http://gitweb.dragonflybsd.org/dragonfly.git/tree/HEAD:/usr.bin/mandoc" CLASS="external">usr.bin/mandoc</A> (build system)
</TD>
</TR>
<TR>
<TD>FreeBSD 10.0, -CURRENT</TD>
<TD>
<A HREF="http://svnweb.freebsd.org/base/head/contrib/mdocml/" CLASS="external">contrib/mdocml</A> (1.12.1 sources)
<A HREF="http://svnweb.freebsd.org/base/head/usr.bin/mandoc/" CLASS="external">usr.bin/mandoc</A> (build system)
</TD>
</TR>
<TR>
<TD>FreeBSD 9.x, 8.x</TD>
<TD>
<A HREF="http://svnweb.freebsd.org/ports/head/textproc/mdocml/" CLASS="external">ports/textproc/mdocml</A> (1.12.2 port)
</TD>
</TR>
<TR>
<TD>NetBSD</TD>
<TD>
<A HREF="http://cvsweb.netbsd.org/bsdweb.cgi/src/external/bsd/mdocml/" CLASS="external">src/external/bsd/mdocml</A> (1.12.1 sources plus patches and build system)
</TD>
</TR>
<TR>
<TD>OpenBSD</TD>
<TD>
<A HREF="http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/mandoc/" CLASS="external">src/usr.bin/mandoc</A> (1.12.3 sources under active development and build system)
</TD>
</TR>
<TR>
<TD>pkgsrc</TD>
<TD>
<A HREF="http://pkgsrc.se/textproc/mdocml" CLASS="external">textproc/mdocml</A> (1.12.2 port)
</TD>
</TR>
<TR>
<TD>Minix3</TD>
<TD>
<A HREF="http://git.minix3.org/?p=minix.git;a=tree;f=external/bsd/mdocml" CLASS="external">external/bsd/mdocml</A> (1.10.9 sources and build system)
</TD>
</TR>
<TR>
<TD>Alpine Linux</TD>
<TD>
<A HREF="http://git.alpinelinux.org/cgit/aports/tree/main/mdocml" CLASS="external">aports/main/mdocml</A> (1.12.2 port)
</TD>
</TR>
</TBODY>
</TABLE>
<H1>
<A NAME="documentation">Documentation</A>
</H1>
<P>
These manuals are generated automatically and refer to the current release.
They are the authoritative documentation for the <SPAN CLASS="nm">mdocml</SPAN> system.
</P>
<TABLE WIDTH="100%" SUMMARY="Documentation">
<COL WIDTH="175">
<COL>
<TBODY>
<TR>
<TD VALIGN="top"><A HREF="apropos.1.html">apropos(1)</A></TD>
<TD VALIGN="top">
search the manual page database
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="demandoc.1.html">demandoc(1)</A></TD>
<TD VALIGN="top">
emit only text of UNIX manuals
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="mandoc.1.html">mandoc(1)</A></TD>
<TD VALIGN="top">
format and display UNIX manuals
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="preconv.1.html">preconv(1)</A></TD>
<TD VALIGN="top">
recode multibyte UNIX manuals
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="whatis.1.html">whatis(1)</A></TD>
<TD VALIGN="top">
search the manual page database
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="mandoc.3.html">mandoc(3)</A></TD>
<TD VALIGN="top">
mandoc macro compiler library
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="tbl.3.html">tbl(3)</A></TD>
<TD VALIGN="top">
roff table parser library for mandoc
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="eqn.7.html">eqn(7)</A></TD>
<TD VALIGN="top">
eqn-mandoc language reference
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="man.7.html">man(7)</A></TD>
<TD VALIGN="top">
man language reference
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="man.cgi.7.html">man.cgi(7)</A></TD>
<TD VALIGN="top">
cgi for manpage query and display
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="mandoc_char.7.html">mandoc_char(7)</A></TD>
<TD VALIGN="top">
mandoc special characters
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="mdoc.7.html">mdoc(7)</A></TD>
<TD VALIGN="top">
mdoc language reference
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="roff.7.html">roff(7)</A></TD>
<TD VALIGN="top">
roff-mandoc language reference
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="tbl.7.html">tbl(7)</A></TD>
<TD VALIGN="top">
tbl-mandoc language reference
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="catman.8.html">catman(8)</A></TD>
<TD VALIGN="top">
update a man.cgi manpage cache
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="mandocdb.8.html">mandocdb(8)</A></TD>
<TD VALIGN="top">
index UNIX manuals
</TD>
</TR>
</TBODY>
</TABLE>
<H2>
<A NAME="links">Supplementary Information</A>
</H2>
<UL>
<LI>
<A HREF="http://manpages.bsd.lv/">Practical UNIX Manuals</A>: mdoc tutorial by Kristaps Dzonsons
</LI>
<LI>
<A HREF="http://www.openbsd.org/faq/ports/specialtopics.html#Mandoc" CLASS="external">OpenBSD porting guide</A>
chapter regarding manual pages
</LI>
<LI>
<A HREF="press.html">Publications and media coverage</A>
concerning mdocml and mandoc
</LI>
<LI>
<A HREF="http://manpages.bsd.lv/history.html">History of UNIX Manpages</A>: a comprehensive overview by Kristaps Dzonsons
</LI>
</UL>
<H1>
<A NAME="contact">Contact</A>
</H1>
<P>
Use the mailing lists for bug-reports, patches, questions, etc. Please check the
<A HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/TODO?cvsroot=mdocml">TODO</A> for known issues
before posting. All lists are subscription-only: send a blank e-mail to the listed address to subscribe. Beyond that,
contact Kristaps at <A HREF="http://mailhide.recaptcha.net/d?k=01M6h_w7twDp58ZgH57eWC_w==&amp;c=Q2DBUt401ePlSeupJFrq_Q==" TITLE="Reveal
this e-mail address">kris...</A>@bsd.lv. Archives are available at <A HREF="http://gmane.org/" CLASS="external">Gmane</A>.
</P>
<TABLE WIDTH="100%" SUMMARY="Mailing Lists">
<COL WIDTH="175">
<COL>
<TBODY>
<TR>
<TD>
disc<A CLASS="external" TITLE="Reveal this e-mail address"
HREF="http://www.google.com/recaptcha/mailhide/d?k=01KQ80PFH5n3BBNpF5Gs4sRg==&amp;c=EV1QytpQqTHSItc2IXvZyocgYLPnG5K0JKw_gwMC9yc=">...</A>@mdocml.bsd.lv
</TD>
<TD>
bug-reports, general questions, and announcements
</TD>
</TR>
<TR>
<TD>
tec<A CLASS="external" TITLE="Reveal this e-mail address"
HREF="http://www.google.com/recaptcha/mailhide/d?k=01qDX_iV0RlUOarEvb6mR28g==&amp;c=gRXsTjza0NNCFPaYu-Taj2tF0pmYZSc90EZkFkhkxgo=">...</A>@mdocml.bsd.lv
</TD>
<TD>
patches and system discussions
</TD>
</TR>
<TR>
<TD>
sou<A CLASS="external" TITLE="Reveal this e-mail address"
HREF="http://www.google.com/recaptcha/mailhide/d?k=01prQrAZhhl2EbIwVcRfABsQ==&amp;c=KtTW4Yic9xk-8g40KzJoca4fR3MYXv28g8NC6OQV-T8=">...</A>@mdocml.bsd.lv
</TD>
<TD>
source commit messages
</TD>
</TR>
</TBODY>
</TABLE>
<H1>
<A NAME="news">News</A>
</H1>
<P CLASS="news">
31-12-2013: version 1.12.3
</P>
<P>
In the <A HREF="mdoc.7.html">mdoc(7)</A> SYNOPSIS, line breaks and hanging indentation
now work correctly for .Fo/.Fa/.Fc and .Fn blocks.
Thanks to Franco Fichtner for doing part of the work.
</P>
<P>
The <A HREF="mdoc.7.html">mdoc(7)</A> .Bk macro got some addititonal bugfixes.
</P>
<P>
In <A HREF="mdoc.7.html">mdoc(7)</A> macro arguments, double quotes can now be quoted
by doubling them, just like in <A HREF="man.7.html">man(7)</A>.
Thanks to Tsugutomo ENAMI for the patch.
</P>
<P>
At the end of <A HREF="man.7.html">man(7)</A> macro lines, end-of-sentence spacing
now works. Thanks to Franco Fichtner for the patch.
</P>
<P>
For backward compatibility, the <A HREF="man.7.html">man(7)</A> parser now supports the
man-ext .UR/.UE (uniform resource identifier) block macros.
</P>
<P>
The <A HREF="man.7.html">man(7)</A> parser now handles closing blocks that are not open
more gracefully.
</P>
<P>
The <A HREF="man.7.html">man(7)</A> parser now ignores blank lines right after .SH and .SS.
</P>
<P>
In the <A HREF="man.7.html">man(7)</A> formatter, reset indentation when leaving a block,
not just when entering the next one.
</P>
<P>
The <A HREF="roff.7.html">roff(7)</A> .nr request now supports incrementing and decrementing
number registers and stops parsing the number right before the first non-digit character.
</P>
<P>
The <A HREF="roff.7.html">roff(7)</A> parser now supports the alternative escape sequence
syntax \C'uXXXX' for Unicode characters.
</P>
<P>
The <A HREF="roff.7.html">roff(7)</A> parser now parses and ignores the .fam (font family)
and .hw (hyphenation points) requests and the \d and \u escape sequences.
</P>
<P>
The <A HREF="roff.7.html">roff(7)</A> manual got a new ESCAPE SEQUENCE REFERENCE.
</P>
<P CLASS="news">
05-10-2013: version 1.12.2
</P>
<P>
The <A HREF="mdoc.7.html">mdoc(7)</A> to <A HREF="man.7.html">man(7)</A> converter,
to be called as <CODE>mandoc -Tman</CODE>, is now fully functional.
</P>
<P>
The <A HREF="mandoc.1.html">mandoc(1)</A> utility now supports the <CODE>-Ios</CODE> (default operating system)
input option, and the <CODE>-Tutf8</CODE> output mode now actually works.
</P>
<P>
The <A HREF="mandocdb.8.html">mandocdb(8)</A> utility no longer truncates existing databases when starting to build new ones,
but only replaces them when the build actually succeeds.
</P>
<P>
The <A HREF="man.7.html">man(7)</A> parser now supports the <EM>PD</EM> macro (paragraph distance),
and (for GNU man-ext compatibility only) <EM>EX</EM> (example block) and <EM>EE</EM> (example end).
Plus several bugfixes regarding indentation, line breaks, and vertical spacing,
and regarding <EM>RS</EM> following <EM>TP</EM>.
</P>
<P>
The <A HREF="roff.7.html">roff(7)</A> parser now supports the <EM>\f(BI</EM> (bold+italic) font escape,
the <EM>\z</EM> (zero cursor advance) escape and the <EM>cc</EM> (change control character)
and <EM>it</EM> (input line trap) requests.
Plus bugfixes regarding the <EM>\t</EM> (tab) escape, nested escape sequences, and conditional requests.
</P>
<P>
In <A HREF="mdoc.7.html">mdoc(7)</A>, several bugs were fixed related to UTF-8 output of quoting enclosures,
delimiter handling, list indentation and horizontal and vertical spacing,
formatting of the <EM>Lk</EM>, <EM>%U</EM>, and <EM>%C</EM> macros,
plus some bugfixes related to the handling of syntax errors like badly nested font blocks,
stray <EM>Ta</EM> macros outside column lists, unterminated <EM>It Xo</EM> blocks,
and non-text children of <EM>Nm</EM> blocks.
</P>
<P>
In <A HREF="tbl.7.html">tbl(7)</A>, the width of horizontal spans and the vertical spacing around tables was corrected,
and in <A HREF="man.7.html">man(7)</A> files, a crash was fixed that was triggered by some particular unclosed <EM>T{</EM> macros.
</P>
<P>
For mandoc developers, we now provide a <A HREF="tbl.3.html">tbl(3)</A> library manual and <CODE>gmdiff</CODE>,
a very small, very simplistic groff-versus-mandoc output comparison tool.
</P>
<H2>
<A>History</A>
</H2>
<UL>
<LI>
<A HREF="NEWS">Release notes</A> going back to release 1.9.15, February 18, 2010.
Briefly explaining the most important changes in each release in relatively easy terms.
Very many changes are not mentioned here.
</LI>
<LI>
<A HREF="history.html">Development history</A> going back to the beginning of the project, November 22, 2008.
One-line entries for important commits, releases, merges, hackathons and talks.
Makes it easy to find out who did what, and when, and when it became available where.
However, this is still incomplete, mentioning only a small fraction of all commits,
and to keep the size down, the individual entries are extremely terse and technical.
Feel free to look up more details and longer explanations about individual entries
in the ChangeLog or in CVS.
</LI>
<LI>
<A HREF="ChangeLog">CVS ChangeLog</A> going back to the beginning of the project.
Very technical information of varying quality, strictly chronological.
All commits are mentioned, but some messages neglect to mention some changes.
Partly terse, partly detailed and verbose. In any case, the ChangeLog is very long -
more than 25,000 lines, more than 700 kB.
</LI>
<LI>
<A HREF="/cgi-bin/cvsweb/?cvsroot=mdocml">CVS</A> web interface, going back to the beginning of the project.
Source code, diffs and commit messages for each source file. The real thing.
</LI>
</UL>
<P CLASS="foot">
<SMALL>
Copyright &#169; 2008&#8211;2011
<A CLASS="external" HREF="http://kristaps.bsd.lv">Kristaps Dzonsons</A>,
&#169; 2013 Ingo Schwarze,
$Date: 2013/12/31 $
</SMALL>
</P>
</BODY>
</HTML>

5
lib.c
View File

@ -1,4 +1,4 @@
/* $Id: lib.c,v 1.9 2011/03/22 14:33:05 kristaps Exp $ */
/* $Id: lib.c,v 1.10 2014/03/23 11:25:26 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -18,12 +18,9 @@
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mdoc.h"
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \

3
lib.in
View File

@ -1,6 +1,7 @@
/* $Id: lib.in,v 1.17 2013/10/13 15:24:03 schwarze Exp $ */
/* $Id: lib.in,v 1.18 2014/01/06 00:53:33 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2009, 2012 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above

View File

@ -1,4 +1,4 @@
/* $Id: libman.h,v 1.56 2012/11/17 00:26:33 schwarze Exp $ */
/* $Id: libman.h,v 1.63 2014/08/01 21:24:17 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -24,13 +24,11 @@ enum man_next {
struct man {
struct mparse *parse; /* parse pointer */
int quick; /* abort parse early */
int flags; /* parse flags */
#define MAN_HALT (1 << 0) /* badness happened: die */
#define MAN_ELINE (1 << 1) /* Next-line element scope. */
#define MAN_BLINE (1 << 2) /* Next-line block scope. */
#define MAN_ILINE (1 << 3) /* Ignored in next-line scope. */
#define MAN_LITERAL (1 << 4) /* Literal input. */
#define MAN_BPLINE (1 << 5)
#define MAN_NEWLINE (1 << 6) /* first macro/text in a line */
enum man_next next; /* where to put the next node */
struct man_node *last; /* the last parsed node */
@ -61,10 +59,6 @@ extern const struct man_macro *const man_macros;
__BEGIN_DECLS
#define man_pmsg(man, l, p, t) \
mandoc_msg((t), (man)->parse, (l), (p), NULL)
#define man_nmsg(man, n, t) \
mandoc_msg((t), (man)->parse, (n)->line, (n)->pos, NULL)
int man_word_alloc(struct man *, int, int, const char *);
int man_block_alloc(struct man *, int, int, enum mant);
int man_head_alloc(struct man *, int, int, enum mant);
@ -76,9 +70,7 @@ void man_hash_init(void);
enum mant man_hash_find(const char *);
int man_macroend(struct man *);
int man_valid_post(struct man *);
int man_valid_pre(struct man *, struct man_node *);
int man_unscope(struct man *,
const struct man_node *, enum mandocerr);
int man_unscope(struct man *, const struct man_node *);
__END_DECLS

View File

@ -1,7 +1,7 @@
/* $Id: libmandoc.h,v 1.35 2013/12/15 21:23:52 schwarze Exp $ */
/* $Id: libmandoc.h,v 1.42 2014/07/09 11:31:43 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -36,46 +36,50 @@ struct roff;
struct mdoc;
struct man;
void mandoc_msg(enum mandocerr, struct mparse *,
void mandoc_msg(enum mandocerr, struct mparse *,
int, int, const char *);
void mandoc_vmsg(enum mandocerr, struct mparse *,
#if __GNUC__ - 0 >= 4
__attribute__((__format__ (__printf__, 5, 6)))
#endif
void mandoc_vmsg(enum mandocerr, struct mparse *,
int, int, const char *, ...);
char *mandoc_getarg(struct mparse *, char **, int, int *);
char *mandoc_normdate(struct mparse *, char *, int, int);
int mandoc_eos(const char *, size_t, int);
int mandoc_eos(const char *, size_t);
int mandoc_strntoi(const char *, size_t, int);
const char *mandoc_a2msec(const char*);
void mdoc_free(struct mdoc *);
struct mdoc *mdoc_alloc(struct roff *, struct mparse *, char *);
void mdoc_free(struct mdoc *);
struct mdoc *mdoc_alloc(struct roff *, struct mparse *,
const char *, int);
void mdoc_reset(struct mdoc *);
int mdoc_parseln(struct mdoc *, int, char *, int);
int mdoc_parseln(struct mdoc *, int, char *, int);
int mdoc_endparse(struct mdoc *);
int mdoc_addspan(struct mdoc *, const struct tbl_span *);
int mdoc_addeqn(struct mdoc *, const struct eqn *);
void man_free(struct man *);
struct man *man_alloc(struct roff *, struct mparse *);
void man_free(struct man *);
struct man *man_alloc(struct roff *, struct mparse *, int);
void man_reset(struct man *);
int man_parseln(struct man *, int, char *, int);
int man_parseln(struct man *, int, char *, int);
int man_endparse(struct man *);
int man_addspan(struct man *, const struct tbl_span *);
int man_addeqn(struct man *, const struct eqn *);
void roff_free(struct roff *);
struct roff *roff_alloc(enum mparset, struct mparse *);
void roff_free(struct roff *);
struct roff *roff_alloc(struct mparse *, int);
void roff_reset(struct roff *);
enum rofferr roff_parseln(struct roff *, int,
enum rofferr roff_parseln(struct roff *, int,
char **, size_t *, int, int *);
void roff_endparse(struct roff *);
void roff_setreg(struct roff *, const char *, int, char sign);
int roff_getreg(const struct roff *, const char *);
char *roff_strdup(const struct roff *, const char *);
int roff_getcontrol(const struct roff *,
int roff_getcontrol(const struct roff *,
const char *, int *);
#if 0
char roff_eqndelim(const struct roff *);
void roff_openeqn(struct roff *, const char *,
void roff_openeqn(struct roff *, const char *,
int, int, const char *);
int roff_closeeqn(struct roff *);
#endif

View File

@ -1,4 +1,4 @@
/* $Id: libmdoc.h,v 1.82 2013/10/21 23:47:58 schwarze Exp $ */
/* $Id: libmdoc.h,v 1.88 2014/08/01 17:40:34 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
@ -25,9 +25,9 @@ enum mdoc_next {
struct mdoc {
struct mparse *parse; /* parse pointer */
char *defos; /* default argument for .Os */
const char *defos; /* default argument for .Os */
int quick; /* abort parse early */
int flags; /* parse flags */
#define MDOC_HALT (1 << 0) /* error in parse: halt */
#define MDOC_LITERAL (1 << 1) /* in a literal scope */
#define MDOC_PBODY (1 << 2) /* in the document body */
#define MDOC_NEWLINE (1 << 3) /* first macro/text in a line */
@ -40,6 +40,7 @@ struct mdoc {
enum mdoc_next next; /* where to put the next node */
struct mdoc_node *last; /* the last node parsed */
struct mdoc_node *first; /* the first node parsed */
struct mdoc_node *last_es; /* the most recent Es node */
struct mdoc_meta meta; /* document meta-data */
enum mdoc_sec lastnamed;
enum mdoc_sec lastsec;
@ -103,17 +104,13 @@ extern const struct mdoc_macro *const mdoc_macros;
__BEGIN_DECLS
#define mdoc_pmsg(mdoc, l, p, t) \
mandoc_msg((t), (mdoc)->parse, (l), (p), NULL)
#define mdoc_nmsg(mdoc, n, t) \
mandoc_msg((t), (mdoc)->parse, (n)->line, (n)->pos, NULL)
int mdoc_macro(MACRO_PROT_ARGS);
int mdoc_word_alloc(struct mdoc *,
int mdoc_word_alloc(struct mdoc *,
int, int, const char *);
void mdoc_word_append(struct mdoc *, const char *);
int mdoc_elem_alloc(struct mdoc *, int, int,
int mdoc_elem_alloc(struct mdoc *, int, int,
enum mdoct, struct mdoc_arg *);
int mdoc_block_alloc(struct mdoc *, int, int,
int mdoc_block_alloc(struct mdoc *, int, int,
enum mdoct, struct mdoc_arg *);
int mdoc_head_alloc(struct mdoc *, int, int, enum mdoct);
int mdoc_tail_alloc(struct mdoc *, int, int, enum mdoct);
@ -136,7 +133,7 @@ enum margverr mdoc_argv(struct mdoc *, int, enum mdoct,
void mdoc_argv_free(struct mdoc_arg *);
enum margserr mdoc_args(struct mdoc *, int,
int *, char *, enum mdoct, char **);
enum margserr mdoc_zargs(struct mdoc *, int,
enum margserr mdoc_zargs(struct mdoc *, int,
int *, char *, char **);
int mdoc_macroend(struct mdoc *);
enum mdelim mdoc_isdelim(const char *);

View File

@ -1,4 +1,4 @@
/* $Id: libroff.h,v 1.28 2013/05/31 21:37:17 schwarze Exp $ */
/* $Id: libroff.h,v 1.29 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -66,7 +66,7 @@ struct tbl_node *tbl_alloc(int, int, struct mparse *);
void tbl_restart(int, int, struct tbl_node *);
void tbl_free(struct tbl_node *);
void tbl_reset(struct tbl_node *);
enum rofferr tbl_read(struct tbl_node *, int, const char *, int);
enum rofferr tbl_read(struct tbl_node *, int, const char *, int);
int tbl_option(struct tbl_node *, int, const char *);
int tbl_layout(struct tbl_node *, int, const char *);
int tbl_data(struct tbl_node *, int, const char *);
@ -76,7 +76,7 @@ void tbl_end(struct tbl_node **);
struct eqn_node *eqn_alloc(const char *, int, int, struct mparse *);
enum rofferr eqn_end(struct eqn_node **);
void eqn_free(struct eqn_node *);
enum rofferr eqn_read(struct eqn_node **, int,
enum rofferr eqn_read(struct eqn_node **, int,
const char *, int, int *);
__END_DECLS

131
main.c
View File

@ -1,7 +1,8 @@
/* $Id: main.c,v 1.167 2012/11/19 17:22:26 schwarze Exp $ */
/* $Id: main.c,v 1.177 2014/06/21 22:24:01 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -27,6 +28,7 @@
#include <unistd.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "main.h"
#include "mdoc.h"
#include "man.h"
@ -58,18 +60,18 @@ struct curparse {
struct mparse *mp;
enum mandoclevel wlevel; /* ignore messages below this */
int wstop; /* stop after a file with a warning */
enum outt outtype; /* which output to use */
enum outt outtype; /* which output to use */
out_mdoc outmdoc; /* mdoc output ptr */
out_man outman; /* man output ptr */
out_man outman; /* man output ptr */
out_free outfree; /* free output ptr */
void *outdata; /* data for output */
char outopts[BUFSIZ]; /* buf of output opts */
};
static int moptions(enum mparset *, char *);
static int moptions(int *, char *);
static void mmsg(enum mandocerr, enum mandoclevel,
const char *, int, int, const char *);
static void parse(struct curparse *, int,
static void parse(struct curparse *, int,
const char *, enum mandoclevel *);
static int toptions(struct curparse *, char *);
static void usage(void) __attribute__((noreturn));
@ -78,12 +80,13 @@ static int woptions(struct curparse *, char *);
static const char *progname;
int
main(int argc, char *argv[])
{
int c;
struct curparse curp;
enum mparset type;
int options;
enum mandoclevel rc;
char *defos;
@ -95,44 +98,45 @@ main(int argc, char *argv[])
memset(&curp, 0, sizeof(struct curparse));
type = MPARSE_AUTO;
options = MPARSE_SO;
curp.outtype = OUTT_ASCII;
curp.wlevel = MANDOCLEVEL_FATAL;
defos = NULL;
/* LINTED */
while (-1 != (c = getopt(argc, argv, "I:m:O:T:VW:")))
switch (c) {
case ('I'):
case 'I':
if (strncmp(optarg, "os=", 3)) {
fprintf(stderr, "-I%s: Bad argument\n",
optarg);
fprintf(stderr,
"%s: -I%s: Bad argument\n",
progname, optarg);
return((int)MANDOCLEVEL_BADARG);
}
if (defos) {
fprintf(stderr, "-I%s: Duplicate argument\n",
optarg);
fprintf(stderr,
"%s: -I%s: Duplicate argument\n",
progname, optarg);
return((int)MANDOCLEVEL_BADARG);
}
defos = mandoc_strdup(optarg + 3);
break;
case ('m'):
if ( ! moptions(&type, optarg))
case 'm':
if ( ! moptions(&options, optarg))
return((int)MANDOCLEVEL_BADARG);
break;
case ('O'):
case 'O':
(void)strlcat(curp.outopts, optarg, BUFSIZ);
(void)strlcat(curp.outopts, ",", BUFSIZ);
break;
case ('T'):
case 'T':
if ( ! toptions(&curp, optarg))
return((int)MANDOCLEVEL_BADARG);
break;
case ('W'):
case 'W':
if ( ! woptions(&curp, optarg))
return((int)MANDOCLEVEL_BADARG);
break;
case ('V'):
case 'V':
version();
/* NOTREACHED */
default:
@ -140,7 +144,7 @@ main(int argc, char *argv[])
/* NOTREACHED */
}
curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp, defos);
curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos);
/*
* Conditionally start up the lookaside buffer before parsing.
@ -191,15 +195,15 @@ usage(void)
"[-Ooption] "
"[-Toutput] "
"[-Wlevel]\n"
"\t [file ...]\n",
"\t [file ...]\n",
progname);
exit((int)MANDOCLEVEL_BADARG);
}
static void
parse(struct curparse *curp, int fd,
const char *file, enum mandoclevel *level)
parse(struct curparse *curp, int fd, const char *file,
enum mandoclevel *level)
{
enum mandoclevel rc;
struct mdoc *mdoc;
@ -229,31 +233,31 @@ parse(struct curparse *curp, int fd,
if ( ! (curp->outman && curp->outmdoc)) {
switch (curp->outtype) {
case (OUTT_XHTML):
case OUTT_XHTML:
curp->outdata = xhtml_alloc(curp->outopts);
curp->outfree = html_free;
break;
case (OUTT_HTML):
case OUTT_HTML:
curp->outdata = html_alloc(curp->outopts);
curp->outfree = html_free;
break;
case (OUTT_UTF8):
case OUTT_UTF8:
curp->outdata = utf8_alloc(curp->outopts);
curp->outfree = ascii_free;
break;
case (OUTT_LOCALE):
case OUTT_LOCALE:
curp->outdata = locale_alloc(curp->outopts);
curp->outfree = ascii_free;
break;
case (OUTT_ASCII):
case OUTT_ASCII:
curp->outdata = ascii_alloc(curp->outopts);
curp->outfree = ascii_free;
break;
case (OUTT_PDF):
case OUTT_PDF:
curp->outdata = pdf_alloc(curp->outopts);
curp->outfree = pspdf_free;
break;
case (OUTT_PS):
case OUTT_PS:
curp->outdata = ps_alloc(curp->outopts);
curp->outfree = pspdf_free;
break;
@ -262,29 +266,29 @@ parse(struct curparse *curp, int fd,
}
switch (curp->outtype) {
case (OUTT_HTML):
case OUTT_HTML:
/* FALLTHROUGH */
case (OUTT_XHTML):
case OUTT_XHTML:
curp->outman = html_man;
curp->outmdoc = html_mdoc;
break;
case (OUTT_TREE):
case OUTT_TREE:
curp->outman = tree_man;
curp->outmdoc = tree_mdoc;
break;
case (OUTT_MAN):
case OUTT_MAN:
curp->outmdoc = man_mdoc;
curp->outman = man_man;
break;
case (OUTT_PDF):
case OUTT_PDF:
/* FALLTHROUGH */
case (OUTT_ASCII):
case OUTT_ASCII:
/* FALLTHROUGH */
case (OUTT_UTF8):
case OUTT_UTF8:
/* FALLTHROUGH */
case (OUTT_LOCALE):
case OUTT_LOCALE:
/* FALLTHROUGH */
case (OUTT_PS):
case OUTT_PS:
curp->outman = terminal_man;
curp->outmdoc = terminal_mdoc;
break;
@ -293,7 +297,7 @@ parse(struct curparse *curp, int fd,
}
}
mparse_result(curp->mp, &mdoc, &man);
mparse_result(curp->mp, &mdoc, &man, NULL);
/* Execute the out device, if it exists. */
@ -311,17 +315,18 @@ parse(struct curparse *curp, int fd,
}
static int
moptions(enum mparset *tflags, char *arg)
moptions(int *options, char *arg)
{
if (0 == strcmp(arg, "doc"))
*tflags = MPARSE_MDOC;
*options |= MPARSE_MDOC;
else if (0 == strcmp(arg, "andoc"))
*tflags = MPARSE_AUTO;
/* nothing to do */;
else if (0 == strcmp(arg, "an"))
*tflags = MPARSE_MAN;
*options |= MPARSE_MAN;
else {
fprintf(stderr, "%s: Bad argument\n", arg);
fprintf(stderr, "%s: -m%s: Bad argument\n",
progname, arg);
return(0);
}
@ -354,7 +359,8 @@ toptions(struct curparse *curp, char *arg)
else if (0 == strcmp(arg, "pdf"))
curp->outtype = OUTT_PDF;
else {
fprintf(stderr, "%s: Bad argument\n", arg);
fprintf(stderr, "%s: -T%s: Bad argument\n",
progname, arg);
return(0);
}
@ -365,7 +371,7 @@ static int
woptions(struct curparse *curp, char *arg)
{
char *v, *o;
const char *toks[6];
const char *toks[6];
toks[0] = "stop";
toks[1] = "all";
@ -377,22 +383,23 @@ woptions(struct curparse *curp, char *arg)
while (*arg) {
o = arg;
switch (getsubopt(&arg, UNCONST(toks), &v)) {
case (0):
case 0:
curp->wstop = 1;
break;
case (1):
case 1:
/* FALLTHROUGH */
case (2):
case 2:
curp->wlevel = MANDOCLEVEL_WARNING;
break;
case (3):
case 3:
curp->wlevel = MANDOCLEVEL_ERROR;
break;
case (4):
case 4:
curp->wlevel = MANDOCLEVEL_FATAL;
break;
default:
fprintf(stderr, "-W%s: Bad argument\n", o);
fprintf(stderr, "%s: -W%s: Bad argument\n",
progname, o);
return(0);
}
}
@ -401,14 +408,20 @@ woptions(struct curparse *curp, char *arg)
}
static void
mmsg(enum mandocerr t, enum mandoclevel lvl,
mmsg(enum mandocerr t, enum mandoclevel lvl,
const char *file, int line, int col, const char *msg)
{
const char *mparse_msg;
fprintf(stderr, "%s:%d:%d: %s: %s",
file, line, col + 1,
mparse_strlevel(lvl),
mparse_strerror(t));
fprintf(stderr, "%s: %s:", progname, file);
if (line)
fprintf(stderr, "%d:%d:", line, col + 1);
fprintf(stderr, " %s", mparse_strlevel(lvl));
if (NULL != (mparse_msg = mparse_strerror(t)))
fprintf(stderr, ": %s", mparse_msg);
if (msg)
fprintf(stderr, ": %s", msg);

4
main.h
View File

@ -1,4 +1,4 @@
/* $Id: main.h,v 1.15 2011/10/06 22:29:12 kristaps Exp $ */
/* $Id: main.h,v 1.16 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -25,7 +25,7 @@ struct man;
#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
/*
/*
* Definitions for main.c-visible output device functions, e.g., -Thtml
* and -Tascii. Note that ascii_alloc() is named as such in
* anticipation of latin1_alloc() and so on, all of which map into the

217
makewhatis.8 Normal file
View File

@ -0,0 +1,217 @@
.\" $Id: makewhatis.8,v 1.2 2014/04/25 12:13:15 schwarze Exp $
.\"
.\" Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2011, 2012 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.
.\"
.Dd $Mdocdate: April 25 2014 $
.Dt MAKEWHATIS 8
.Os
.Sh NAME
.Nm makewhatis
.Nd index UNIX manuals
.Sh SYNOPSIS
.Nm
.Op Fl aDnpQ
.Op Fl T Cm utf8
.Op Fl C Ar file
.Nm
.Op Fl aDnpQ
.Op Fl T Cm utf8
.Ar dir ...
.Nm
.Op Fl DnpQ
.Op Fl T Cm utf8
.Fl d Ar dir
.Op Ar
.Nm
.Op Fl Dnp
.Op Fl T Cm utf8
.Fl u Ar dir
.Op Ar
.Nm
.Op Fl DQ
.Fl t Ar
.Sh DESCRIPTION
The
.Nm
utility extracts keywords from
.Ux
manuals and indexes them in a database for fast retrieval by
.Xr apropos 1 ,
.Xr whatis 1 ,
and
.Xr man 1 Ns 's
.Fl k
option.
.Pp
By default,
.Nm
creates a database in each
.Ar dir
using the files
.Sm off
.Sy man Ar section Li /
.Op Ar arch Li /
.Ar title . section
.Sm on
and
.Sm off
.Sy cat Ar section Li /
.Op Ar arch Li /
.Ar title . Sy 0
.Sm on
in that directory.
Existing databases are replaced.
If
.Ar dir
is not provided,
.Nm
uses the default paths stipulated by
.Xr manpath 1 ,
or
.Xr man.conf 5 .
.Pp
The arguments are as follows:
.Bl -tag -width "-C file"
.It Fl a
Use all directories and files found below
.Ar dir ... .
.It Fl C Ar file
Specify an alternative configuration
.Ar file
in
.Xr man.conf 5
format.
.It Fl D
Display all files added or removed to the index.
With a second
.Fl D ,
also show all keyswords added for each file.
.It Fl d Ar dir
Merge (remove and re-add)
.Ar
to the database in
.Ar dir .
.It Fl n
Do not create or modify any database; scan and parse only,
and print manual page names and descriptions to standard output.
.It Fl p
Print warnings about potential problems with manual pages
to the standard error output.
.It Fl Q
Quickly build reduced-size databases
by reading only the NAME sections of manuals.
The resulting databases will usually contain names and descriptions only.
.It Fl T Cm utf8
Use UTF-8 encoding instead of ASCII for strings stored in the databases.
.It Fl t Ar
Check the given
.Ar files
for potential problems.
Implies
.Fl a ,
.Fl n ,
and
.Fl p .
All diagnostic messages are printed to the standard output;
the standard error output is not used.
.It Fl u Ar dir
Remove
.Ar
from the database in
.Ar dir .
.El
.Pp
If fatal parse errors are encountered while parsing, the offending file
is printed to stderr, omitted from the index, and the parse continues
with the next input file.
.Sh FILES
.Bl -tag -width Ds
.It Pa mandoc.db
A database of manpages relative to the directory of the file.
This file is portable across architectures and systems, so long as the
manpage hierarchy it indexes does not change.
.It Pa /etc/man.conf
The default
.Xr man 1
configuration file.
.El
.Sh EXIT STATUS
The
.Nm
utility exits with one of the following values:
.Pp
.Bl -tag -width Ds -compact
.It 0
No errors occurred.
.It 5
Invalid command line arguments were specified.
No input files have been read.
.It 6
An operating system error occurred, for example memory exhaustion or an
error accessing input files.
Such errors cause
.Nm
to exit at once, possibly in the middle of parsing or formatting a file.
The output databases are corrupt and should be removed.
.El
.Sh SEE ALSO
.Xr apropos 1 ,
.Xr man 1 ,
.Xr whatis 1 ,
.Xr man.conf 5
.Sh HISTORY
A
.Nm
utility first appeared in
.Bx 2 .
It was rewritten in
.Xr perl 1
for
.Ox 2.7
and in C for
.Ox 5.6 .
.Pp
The
.Ar dir
argument first appeared in
.Nx 1.0 ;
the options
.Fl dpt
in
.Ox 2.7 ;
the option
.Fl u
in
.Ox 3.4 ;
and the options
.Fl aCDnQT
in
.Ox 5.6 .
.Sh AUTHORS
.An -nosplit
.An Bill Joy
wrote the original
.Bx
.Nm
in February 1979,
.An Marc Espie
started the Perl version in 2000,
and the current version of
.Nm
was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
and
.An Ingo Schwarze Aq Mt schwarze@openbsd.org .

116
man.7
View File

@ -1,7 +1,8 @@
.\" $Id: man.7,v 1.120 2013/09/16 22:58:57 schwarze Exp $
.\" $Id: man.7,v 1.127 2014/06/22 16:39:45 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.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 +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 16 2013 $
.Dd $Mdocdate: June 22 2014 $
.Dt MAN 7
.Os
.Sh NAME
@ -97,30 +98,32 @@ file for a utility
.Bd -literal -offset indent
\&.TH PROGNAME 1 2009-10-10
\&.SH NAME
\efBprogname\efR \e(en a description goes here
\efBprogname\efR \e(en one line about what it does
\&.\e\(dq .SH LIBRARY
\&.\e\(dq For sections 2 & 3 only.
\&.\e\(dq For sections 2, 3, and 9 only.
\&.\e\(dq Not used in OpenBSD.
\&.SH SYNOPSIS
\efBprogname\efR [\efB\e-options\efR] arguments...
\efBprogname\efR [\efB\e-options\efR] \efIfile ...\efR
\&.SH DESCRIPTION
The \efBfoo\efR utility processes files...
The \efBfoo\efR utility processes files ...
\&.\e\(dq .Sh CONTEXT
\&.\e\(dq For section 9 functions only.
\&.\e\(dq .SH IMPLEMENTATION NOTES
\&.\e\(dq Not used in OpenBSD.
\&.\e\(dq .SH RETURN VALUES
\&.\e\(dq For sections 2, 3, & 9 only.
\&.\e\(dq For sections 2, 3, and 9 function return values only.
\&.\e\(dq .SH ENVIRONMENT
\&.\e\(dq For sections 1, 6, 7, & 8 only.
\&.\e\(dq For sections 1, 6, 7, and 8 only.
\&.\e\(dq .SH FILES
\&.\e\(dq .SH EXIT STATUS
\&.\e\(dq For sections 1, 6, & 8 only.
\&.\e\(dq For sections 1, 6, and 8 only.
\&.\e\(dq .SH EXAMPLES
\&.\e\(dq .SH DIAGNOSTICS
\&.\e\(dq For sections 1, 4, 6, 7, & 8 only.
\&.\e\(dq For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only.
\&.\e\(dq .SH ERRORS
\&.\e\(dq For sections 2, 3, & 9 only.
\&.\e\(dq For sections 2, 3, 4, and 9 errno settings only.
\&.\e\(dq .SH SEE ALSO
\&.\e\(dq .BR foo ( 1 )
\&.\e\(dq .BR foobar ( 1 )
\&.\e\(dq .SH STANDARDS
\&.\e\(dq .SH HISTORY
\&.\e\(dq .SH AUTHORS
@ -170,6 +173,9 @@ This expands upon the brief, one-line description in
.Em NAME .
It usually contains a break-down of the options (if documenting a
command).
.It Em CONTEXT
This section lists the contexts in which functions can be called in section 9.
The contexts are autoconf, process, or interrupt.
.It Em IMPLEMENTATION NOTES
Implementation-specific notes should be kept here.
This is useful when implementing standard functions that may have side
@ -196,13 +202,19 @@ well-tested invocations.
Make sure that examples work properly!
.It Em DIAGNOSTICS
Documents error conditions.
This is most useful in section 4 manuals.
In section 4 and 9 manuals, these are usually messages
printed by the kernel to the console and to the kernel log.
In section 1, 6, 7, and 8, these are usually messages
printed by userland programs to the standard error output.
.Pp
Historically, this section was used in place of
.Em EXIT STATUS
for manuals in sections 1, 6, and 8; however, this practise is
discouraged.
.It Em ERRORS
Documents error handling in sections 2, 3, and 9.
Documents
.Xr errno 2
settings in sections 2, 3, 4, and 9.
.It Em SEE ALSO
References other manuals with related topics.
This section should exist for most manuals.
@ -280,7 +292,7 @@ For the scoping of individual macros, see
.Sx MACRO SYNTAX .
.Ss \&AT
Sets the volume for the footer for compatibility with man pages from
.Tn AT&T UNIX
.At
releases.
The optional arguments specify which release it is from.
.Ss \&B
@ -656,6 +668,20 @@ Sets the volume for the footer for compatibility with man pages from
.Bx
releases.
The optional first argument specifies which release it is from.
.Ss \&UE
End a uniform resource identifier block.
This is a non-standard GNU extension, included only for compatibility.
See
.Sx \&UE .
.Ss \&UR
Begin a uniform resource identifier block.
This is a non-standard GNU extension, included only for compatibility.
It has the following syntax:
.Bd -literal -offset indent
.Pf \. Sx \&UR Ar uri
link description to be shown
.Pf \. Sx UE
.Ed
.Ss \&br
Breaks the current line.
Consecutive invocations have no further effect.
@ -665,11 +691,6 @@ See also
.Ss \&fi
End literal mode begun by
.Sx \&nf .
.Ss \&ft
Change the current font mode.
See
.Sx Text Decoration
for a listing of available font modes.
.Ss \&in
Indent relative to the current indentation:
.Pp
@ -750,10 +771,13 @@ The syntax is as follows:
.It Sx \&BI Ta n Ta current Ta \&
.It Sx \&BR Ta n Ta current Ta \&
.It Sx \&DT Ta 0 Ta current Ta \&
.It Sx \&EE Ta 0 Ta current Ta compat
.It Sx \&EX Ta 0 Ta current Ta compat
.It Sx \&I Ta n Ta next-line Ta \&
.It Sx \&IB Ta n Ta current Ta \&
.It Sx \&IR Ta n Ta current Ta \&
.It Sx \&OP Ta 0, 1 Ta current Ta compat
.It Sx \&PD Ta 1 Ta current Ta \&
.It Sx \&R Ta n Ta next-line Ta \&
.It Sx \&RB Ta n Ta current Ta \&
.It Sx \&RI Ta n Ta current Ta \&
@ -763,7 +787,6 @@ The syntax is as follows:
.It Sx \&UC Ta <=1 Ta current Ta \&
.It Sx \&br Ta 0 Ta current Ta compat
.It Sx \&fi Ta 0 Ta current Ta compat
.It Sx \&ft Ta 1 Ta current Ta compat
.It Sx \&in Ta 1 Ta current Ta compat
.It Sx \&na Ta 0 Ta current Ta compat
.It Sx \&nf Ta 0 Ta current Ta compat
@ -823,6 +846,8 @@ implicitly closed, is syntactically incorrect.
.It Sx \&SH Ta >0 Ta next-line Ta section Ta \&
.It Sx \&SS Ta >0 Ta next-line Ta sub-section Ta \&
.It Sx \&TP Ta n Ta next-line Ta paragraph Ta \&
.It Sx \&UE Ta 0 Ta current Ta none Ta compat
.It Sx \&UR Ta 1 Ta current Ta part Ta compat
.El
.Pp
Macros marked
@ -848,10 +873,11 @@ Note that macros like
.Sx \&BR
open and close a font scope for each argument.
.Sh COMPATIBILITY
This section documents areas of questionable portability between
This section mentions some areas of questionable portability between
implementations of the
.Nm
language.
More incompatibilities exist.
.Pp
.Bl -dash -compact
.It
@ -863,47 +889,12 @@ to close out a literal context opened with
.Sx \&nf .
This behaviour may not be portable.
.It
In quoted literals, GNU troff allowed pair-wise double-quotes to produce
a standalone double-quote in formatted output.
It is not known whether this behaviour is exhibited by other formatters.
.It
troff suppresses a newline before
.Sq \(aq
macro output; in mandoc, it is an alias for the standard
.Sq \&.
control character.
.It
The
.Sq \eh
.Pq horizontal position ,
.Sq \ev
.Pq vertical position ,
.Sq \em
.Pq text colour ,
.Sq \eM
.Pq text filling colour ,
.Sq \ez
.Pq zero-length character ,
.Sq \ew
.Pq string length ,
.Sq \ek
.Pq horizontal position marker ,
.Sq \eo
.Pq text overstrike ,
and
.Sq \es
.Pq text size
escape sequences are all discarded in mandoc.
.It
The
.Sq \ef
scaling unit is accepted by mandoc, but rendered as the default unit.
.It
The
.Sx \&sp
macro does not accept negative values in mandoc.
In GNU troff, this would result in strange behaviour.
.It
In page header lines, GNU troff versions up to and including 1.21
only print
.Ar volume
@ -919,8 +910,13 @@ is given, like in
.El
.Pp
The
.Sx OP
macro is part of the extended
.Sx EE ,
.Sx EX ,
.Sx OP ,
.Sx UE ,
and
.Sx UR
macros are part of the GNU extended
.Nm
macro set, and may not be portable to non-GNU troff implementations.
.Sh SEE ALSO

218
man.c
View File

@ -1,6 +1,8 @@
/* $Id: man.c,v 1.121 2013/11/10 22:54:40 schwarze Exp $ */
/* $Id: man.c,v 1.137 2014/08/01 21:24:17 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -21,6 +23,7 @@
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
@ -28,12 +31,13 @@
#include "man.h"
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libman.h"
#include "libmandoc.h"
const char *const __man_macronames[MAN_MAX] = {
const char *const __man_macronames[MAN_MAX] = {
"br", "TH", "SH", "SS",
"TP", "LP", "PP", "P",
"TP", "LP", "PP", "P",
"IP", "HP", "SM", "SB",
"BI", "IB", "BR", "RB",
"R", "B", "I", "IR",
@ -41,17 +45,17 @@ const char *const __man_macronames[MAN_MAX] = {
"fi", "RE", "RS", "DT",
"UC", "PD", "AT", "in",
"ft", "OP", "EX", "EE",
"UR", "UE"
"UR", "UE", "ll"
};
const char * const *man_macronames = __man_macronames;
static struct man_node *man_node_alloc(struct man *, int, int,
static struct man_node *man_node_alloc(struct man *, int, int,
enum man_type, enum mant);
static int man_node_append(struct man *,
static int man_node_append(struct man *,
struct man_node *);
static void man_node_free(struct man_node *);
static void man_node_unlink(struct man *,
static void man_node_unlink(struct man *,
struct man_node *);
static int man_ptext(struct man *, int, char *, int);
static int man_pmacro(struct man *, int, char *, int);
@ -64,20 +68,16 @@ const struct man_node *
man_node(const struct man *man)
{
assert( ! (MAN_HALT & man->flags));
return(man->first);
}
const struct man_meta *
man_meta(const struct man *man)
{
assert( ! (MAN_HALT & man->flags));
return(&man->meta);
}
void
man_reset(struct man *man)
{
@ -86,7 +86,6 @@ man_reset(struct man *man)
man_alloc1(man);
}
void
man_free(struct man *man)
{
@ -95,9 +94,8 @@ man_free(struct man *man)
free(man);
}
struct man *
man_alloc(struct roff *roff, struct mparse *parse)
man_alloc(struct roff *roff, struct mparse *parse, int quick)
{
struct man *p;
@ -105,39 +103,31 @@ man_alloc(struct roff *roff, struct mparse *parse)
man_hash_init();
p->parse = parse;
p->quick = quick;
p->roff = roff;
man_alloc1(p);
return(p);
}
int
man_endparse(struct man *man)
{
assert( ! (MAN_HALT & man->flags));
if (man_macroend(man))
return(1);
man->flags |= MAN_HALT;
return(0);
return(man_macroend(man));
}
int
man_parseln(struct man *man, int ln, char *buf, int offs)
{
man->flags |= MAN_NEWLINE;
assert( ! (MAN_HALT & man->flags));
return (roff_getcontrol(man->roff, buf, &offs) ?
man_pmacro(man, ln, buf, offs) :
man_ptext(man, ln, buf, offs));
man_pmacro(man, ln, buf, offs) :
man_ptext(man, ln, buf, offs));
}
static void
man_free1(struct man *man)
{
@ -156,7 +146,6 @@ man_free1(struct man *man)
free(man->meta.msec);
}
static void
man_alloc1(struct man *man)
{
@ -180,12 +169,12 @@ man_node_append(struct man *man, struct man_node *p)
assert(MAN_ROOT != p->type);
switch (man->next) {
case (MAN_NEXT_SIBLING):
case MAN_NEXT_SIBLING:
man->last->next = p;
p->prev = man->last;
p->parent = man->last->parent;
break;
case (MAN_NEXT_CHILD):
case MAN_NEXT_CHILD:
man->last->child = p;
p->parent = man->last;
break;
@ -193,23 +182,24 @@ man_node_append(struct man *man, struct man_node *p)
abort();
/* NOTREACHED */
}
assert(p->parent);
p->parent->nchild++;
if ( ! man_valid_pre(man, p))
return(0);
switch (p->type) {
case (MAN_HEAD):
case MAN_BLOCK:
if (p->tok == MAN_SH || p->tok == MAN_SS)
man->flags &= ~MAN_LITERAL;
break;
case MAN_HEAD:
assert(MAN_BLOCK == p->parent->type);
p->parent->head = p;
break;
case (MAN_TAIL):
case MAN_TAIL:
assert(MAN_BLOCK == p->parent->type);
p->parent->tail = p;
break;
case (MAN_BODY):
case MAN_BODY:
assert(MAN_BLOCK == p->parent->type);
p->parent->body = p;
break;
@ -220,9 +210,9 @@ man_node_append(struct man *man, struct man_node *p)
man->last = p;
switch (p->type) {
case (MAN_TBL):
case MAN_TBL:
/* FALLTHROUGH */
case (MAN_TEXT):
case MAN_TEXT:
if ( ! man_valid_post(man))
return(0);
break;
@ -233,9 +223,8 @@ man_node_append(struct man *man, struct man_node *p)
return(1);
}
static struct man_node *
man_node_alloc(struct man *man, int line, int pos,
man_node_alloc(struct man *man, int line, int pos,
enum man_type type, enum mant tok)
{
struct man_node *p;
@ -252,7 +241,6 @@ man_node_alloc(struct man *man, int line, int pos,
return(p);
}
int
man_elem_alloc(struct man *man, int line, int pos, enum mant tok)
{
@ -265,7 +253,6 @@ man_elem_alloc(struct man *man, int line, int pos, enum mant tok)
return(1);
}
int
man_tail_alloc(struct man *man, int line, int pos, enum mant tok)
{
@ -278,7 +265,6 @@ man_tail_alloc(struct man *man, int line, int pos, enum mant tok)
return(1);
}
int
man_head_alloc(struct man *man, int line, int pos, enum mant tok)
{
@ -291,7 +277,6 @@ man_head_alloc(struct man *man, int line, int pos, enum mant tok)
return(1);
}
int
man_body_alloc(struct man *man, int line, int pos, enum mant tok)
{
@ -304,7 +289,6 @@ man_body_alloc(struct man *man, int line, int pos, enum mant tok)
return(1);
}
int
man_block_alloc(struct man *man, int line, int pos, enum mant tok)
{
@ -332,7 +316,6 @@ man_word_alloc(struct man *man, int line, int pos, const char *word)
return(1);
}
/*
* Free all of the resources held by a node. This does NOT unlink a
* node from its context; for that, see man_node_unlink().
@ -346,7 +329,6 @@ man_node_free(struct man_node *p)
free(p);
}
void
man_node_delete(struct man *man, struct man_node *p)
{
@ -363,8 +345,6 @@ man_addeqn(struct man *man, const struct eqn *ep)
{
struct man_node *n;
assert( ! (MAN_HALT & man->flags));
n = man_node_alloc(man, ep->ln, ep->pos, MAN_EQN, MAN_MAX);
n->eqn = ep;
@ -380,8 +360,6 @@ man_addspan(struct man *man, const struct tbl_span *sp)
{
struct man_node *n;
assert( ! (MAN_HALT & man->flags));
n = man_node_alloc(man, sp->line, 0, MAN_TBL, MAN_MAX);
n->span = sp;
@ -403,7 +381,7 @@ man_descope(struct man *man, int line, int offs)
if (MAN_ELINE & man->flags) {
man->flags &= ~MAN_ELINE;
if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
if ( ! man_unscope(man, man->last->parent))
return(0);
}
@ -411,7 +389,7 @@ man_descope(struct man *man, int line, int offs)
return(1);
man->flags &= ~MAN_BLINE;
if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
if ( ! man_unscope(man, man->last->parent))
return(0);
return(man_body_alloc(man, line, offs, man->last->tok));
}
@ -448,9 +426,9 @@ man_ptext(struct man *man, int line, char *buf, int offs)
return(1);
}
/*
/*
* Warn if the last un-escaped character is whitespace. Then
* strip away the remaining spaces (tabs stay!).
* strip away the remaining spaces (tabs stay!).
*/
i = (int)strlen(buf);
@ -458,7 +436,8 @@ man_ptext(struct man *man, int line, char *buf, int offs)
if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
if (i > 1 && '\\' != buf[i - 2])
man_pmsg(man, line, i - 1, MANDOCERR_EOLNSPACE);
mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
line, i - 1, NULL);
for (--i; i && ' ' == buf[i]; i--)
/* Spin back to non-space. */ ;
@ -479,7 +458,7 @@ man_ptext(struct man *man, int line, char *buf, int offs)
*/
assert(i);
if (mandoc_eos(buf, (size_t)i, 0))
if (mandoc_eos(buf, (size_t)i))
man->last->flags |= MAN_EOS;
return(man_descope(man, line, offs));
@ -488,13 +467,15 @@ man_ptext(struct man *man, int line, char *buf, int offs)
static int
man_pmacro(struct man *man, int ln, char *buf, int offs)
{
int i, ppos;
enum mant tok;
char mac[5];
struct man_node *n;
enum mant tok;
int i, ppos;
int bline;
if ('"' == buf[offs]) {
man_pmsg(man, ln, offs, MANDOCERR_BADCOMMENT);
mandoc_msg(MANDOCERR_COMMENT_BAD, man->parse,
ln, offs, NULL);
return(1);
} else if ('\0' == buf[offs])
return(1);
@ -507,8 +488,8 @@ man_pmacro(struct man *man, int ln, char *buf, int offs)
*/
i = 0;
while (i < 4 && '\0' != buf[offs] &&
' ' != buf[offs] && '\t' != buf[offs])
while (i < 4 && '\0' != buf[offs] && ' ' != buf[offs] &&
'\t' != buf[offs])
mac[i++] = buf[offs++];
mac[i] = '\0';
@ -516,8 +497,8 @@ man_pmacro(struct man *man, int ln, char *buf, int offs)
tok = (i > 0 && i < 4) ? man_hash_find(mac) : MAN_MAX;
if (MAN_MAX == tok) {
mandoc_vmsg(MANDOCERR_MACRO, man->parse, ln,
ppos, "%s", buf + ppos - 1);
mandoc_msg(MANDOCERR_MACRO, man->parse,
ln, ppos, buf + ppos - 1);
return(1);
}
@ -526,15 +507,16 @@ man_pmacro(struct man *man, int ln, char *buf, int offs)
while (buf[offs] && ' ' == buf[offs])
offs++;
/*
/*
* Trailing whitespace. Note that tabs are allowed to be passed
* into the parser as "text", so we only warn about spaces here.
*/
if ('\0' == buf[offs] && ' ' == buf[offs - 1])
man_pmsg(man, ln, offs - 1, MANDOCERR_EOLNSPACE);
mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
ln, offs - 1, NULL);
/*
/*
* Remove prior ELINE macro, as it's being clobbered by a new
* macro. Note that NSCOPED macros do not close out ELINE
* macros---they don't print text---so we let those slip by.
@ -550,7 +532,7 @@ man_pmacro(struct man *man, int ln, char *buf, int offs)
if (MAN_NSCOPED & man_macros[n->tok].flags)
n = n->parent;
mandoc_vmsg(MANDOCERR_LINESCOPE, man->parse, n->line,
mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line,
n->pos, "%s breaks %s", man_macronames[tok],
man_macronames[n->tok]);
@ -581,7 +563,7 @@ man_pmacro(struct man *man, int ln, char *buf, int offs)
assert(MAN_BLOCK == n->type);
assert(MAN_SCOPED & man_macros[n->tok].flags);
mandoc_vmsg(MANDOCERR_LINESCOPE, man->parse, n->line,
mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line,
n->pos, "%s breaks %s", man_macronames[tok],
man_macronames[n->tok]);
@ -589,63 +571,41 @@ man_pmacro(struct man *man, int ln, char *buf, int offs)
man->flags &= ~MAN_BLINE;
}
/*
* Save the fact that we're in the next-line for a block. In
* this way, embedded roff instructions can "remember" state
* when they exit.
*/
/* Remember whether we are in next-line scope for a block head. */
if (MAN_BLINE & man->flags)
man->flags |= MAN_BPLINE;
bline = man->flags & MAN_BLINE;
/* Call to handler... */
assert(man_macros[tok].fp);
if ( ! (*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf))
goto err;
return(0);
/*
* We weren't in a block-line scope when entering the
* above-parsed macro, so return.
*/
/* In quick mode (for mandocdb), abort after the NAME section. */
if ( ! (MAN_BPLINE & man->flags)) {
man->flags &= ~MAN_ILINE;
return(1);
if (man->quick && MAN_SH == tok) {
n = man->last;
if (MAN_BODY == n->type &&
strcmp(n->prev->child->string, "NAME"))
return(2);
}
man->flags &= ~MAN_BPLINE;
/*
* If we're in a block scope, then allow this macro to slip by
* without closing scope around it.
* If we are in a next-line scope for a block head,
* close it out now and switch to the body,
* unless the next-line scope is allowed to continue.
*/
if (MAN_ILINE & man->flags) {
man->flags &= ~MAN_ILINE;
if ( ! bline || man->flags & MAN_ELINE ||
man_macros[tok].flags & MAN_NSCOPED)
return(1);
}
/*
* If we've opened a new next-line element scope, then return
* now, as the next line will close out the block scope.
*/
if (MAN_ELINE & man->flags)
return(1);
/* Close out the block scope opened in the prior line. */
assert(MAN_BLINE & man->flags);
man->flags &= ~MAN_BLINE;
if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
if ( ! man_unscope(man, man->last->parent))
return(0);
return(man_body_alloc(man, ln, ppos, man->last->tok));
err: /* Error out. */
man->flags |= MAN_HALT;
return(0);
}
/*
@ -696,3 +656,49 @@ man_mparse(const struct man *man)
assert(man && man->parse);
return(man->parse);
}
void
man_deroff(char **dest, const struct man_node *n)
{
char *cp;
size_t sz;
if (MAN_TEXT != n->type) {
for (n = n->child; n; n = n->next)
man_deroff(dest, n);
return;
}
/* Skip leading whitespace and escape sequences. */
cp = n->string;
while ('\0' != *cp) {
if ('\\' == *cp) {
cp++;
mandoc_escape((const char **)&cp, NULL, NULL);
} else if (isspace((unsigned char)*cp))
cp++;
else
break;
}
/* Skip trailing whitespace. */
for (sz = strlen(cp); sz; sz--)
if (0 == isspace((unsigned char)cp[sz-1]))
break;
/* Skip empty strings. */
if (0 == sz)
return;
if (NULL == *dest) {
*dest = mandoc_strndup(cp, sz);
return;
}
mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
free(*dest);
*dest = cp;
}

122
man.cgi.7
View File

@ -1,122 +0,0 @@
.Dd $Mdocdate: July 13 2013 $
.Dt MAN.CGI 7
.Os
.Sh NAME
.Nm man.cgi
.Nd cgi for manpage query and display
.Sh SYNOPSIS
.Nm
.Sh DESCRIPTION
The
.Nm
script queries and displays manual pages.
It interfaces with
.Xr mandocdb 8
databases cached with
.Xr catman 8 .
.Pp
To use
.Nm ,
create a manual cache in
.Xr catman 8 .
Assign this directory to the environment variable
.Ev CACHE_DIR ,
defaulting to
.Pa /cache/man.cgi .
Copy the
.Pa man.cgi
script into your CGI directory (see
.Sx FILES
for other relevant files).
.Pp
Multiple
.Xr catman 8
trees may be managed by
.Nm :
directories under
.Ev CACHE_DIR
containing
.Pa etc/catman.conf
are identified as
.Qq manroots .
The path of a manroot under
.Ev CACHE_DIR
is converted to a name by replacing path separators with spaces.
.Pp
Thus, if
.Ev CACHE_DIR
is the default
.Pa /cache/man.cgi ,
the web-server is jailed to
.Pa /var/www ,
and cache subdirectories
.Pa ./foo/1
and
.Pa ./bar/2
contain
.Pa etc/catman.conf ,
.Nm
will assign these to manroots
.Qq foo 1
and
.Qq bar 2 ,
respectively.
These names will appear as choices when searching for manuals.
.Pp
If
.Nm
finds only one manroot, or none, then the selection box is omitted.
If no manroot is specified during search, the first manroot is used by
default.
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It Ev CACHE_DIR
The absolute path of the
.Xr catman 8
cache directory.
This must not have a trailing slash.
.It Ev CSS_DIR
Prepended to CSS file links in outputted HTML files.
This must not have a trailing slash.
.El
.Sh FILES
.Bl -tag -width Ds
.It Pa etc/catman.conf
Built by
.Xr catman 8
and must exist at least once under the configuration directory root.
.It Pa man.css
Should be visible in the server document root or within
.Ev CSS_DIR .
Included in each page after
.Pa man-cgi.css ,
ostensibly for
.Xr mandoc 1
HTML output styling.
.It Pa man.cgi.css
Should be visible in the server document root or within
.Ev CSS_DIR .
Included in each page, ostensibly for general
.Nm
styling.
.El
.Sh COMPATIBILITY
The
.Nm
script is call-compatible with queries from the traditional
.Pa man.cgi
script by Wolfram Schneider.
However, the results may not be quite the same.
.Sh SEE ALSO
.Xr catman 8 ,
.Xr mandocdb 8
.Sh AUTHORS
The
.Nm
utility was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
.Sh CAVEATS
If you're running in a jailed web-server, make sure the
.Pa /tmp
directory exists and is writable.
The databases may need this for scratch space.

409
man.cgi.8 Normal file
View File

@ -0,0 +1,409 @@
.\" $Id: man.cgi.8,v 1.9 2014/07/22 18:14:13 schwarze Exp $
.\"
.\" Copyright (c) 2014 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.
.\"
.Dd $Mdocdate: July 22 2014 $
.Dt MAN.CGI 8
.Os
.Sh NAME
.Nm man.cgi
.Nd CGI program to search and display manual pages
.Sh DESCRIPTION
The
.Nm
CGI program searches for manual pages on a WWW server
and displays them to HTTP clients,
providing functionality equivalent to the
.Xr apropos 1
and
.Xr man 1
utilities.
It can use multiple manual trees in parallel.
.Ss HTML search interface
At the top of each generated HTML page,
.Nm
displays a search form containing these elements:
.Bl -enum
.It
An input box for search queries, expecting
either a name of a manual page or an
.Ar expression
using the syntax described in the
.Xr apropos 1
manual; filling this in is required for each search.
.It
A
.Dq Submit
button to send a search request from the client to the server.
.It
A
.Dq Reset
button to undo any changes to the input boxes and the dropdown menus
and reset them to the values contained in the
.Ev QUERY_STRING .
.It
Radio buttons to select pages either by name like in
.Xr man 1
or using
.Xr apropos 1
queries.
.It
A dropdown menu to optionally select a manual section.
If one is provided, it has the same effect as the
.Xr man 1
and
.Xr apropos 1
.Fl s
option.
Otherwise, pages from all sections are shown.
.It
A dropdown menu to optionally select an architecture.
If one is provided, it has the same effect as the
.Xr man 1
and
.Xr apropos 1
.Fl S
option.
By default, pages for all architectures are shown.
.It
A dropdown menu to select a manual tree.
If the configuration file
.Pa /var/www/man/manpath.conf
contains only one manpath, the dropdown menu is not shown.
By default, the first manpath given in the file is used.
.El
.Ss Program output
The
.Nm
program generates five kinds of output pages:
.Bl -tag -width Ds
.It The index page.
This is returned when calling
.Nm
without
.Ev PATH_INFO
and without a
.Ev QUERY_STRING .
It serves as a starting point for using the program
and shows the search form only.
.It A list page.
Lists are returned when searches match more than one manual page.
The first column shows the names and section numbers of manuals
as clickable links.
The second column shows the one-line descriptions of the manuals.
.It A manual page.
This output format is used when a search matches exactly one
manual page, or when a link on a list page or an
.Ic \&Xr
link on another manual page is followed.
.It A no-result page.
This is shown when a search request returns no results -
eiher because it violates the query syntax, or because
the search does not match any manual pages.
.It \&An error page.
This cannot happen by merely clicking the
.Dq Search
button, but only by manually entering an invalid URI.
It does not show the search form, but only an error message
and a link back to the index page.
.El
.Ss Setup
For each manual tree, create one first-level subdirectory below
.Pa /var/www/man .
The name of one of these directories is called a
.Dq manpath
in the context of
.Nm .
Create a single ASCII text file
.Pa /var/www/man/manpath.conf
containing the names of these directories, one per line.
The directory given first is used as the default manpath.
.Pp
Inside each of these directories, use the same directory and file
structure as found below
.Pa /usr/share/man ,
that is, second-level subdirectories
.Pa /var/www/man/*/man1 , /var/www/man/*/man2
etc. containing source
.Xr mdoc 7
and
.Xr man 7
manuals with file name extensions matching the section numbers,
second-level subdirectories
.Pa /var/www/man/*/cat1 , /var/www/man/*/cat2
etc. containing preformatted manuals with the file name extension
.Sq 0 ,
and optional third-level subdirectories for architectures.
Use
.Xr makewhatis 8
to create a
.Xr mandoc.db 5
database inside each manpath.
.Pp
Configure your web server to execute CGI programs located in
.Pa /cgi-bin .
When using
.Xr nginx 8 ,
the
.Xr slowcgi 8
proxy daemon is needed to translate FastCGI requests to plain old CGI.
.Pp
To compile
.Nm ,
first copy
.Pa cgi.h.example
to
.Pa cgi.h
and edit it according to your needs.
It contains the following compile-time definitions:
.Bl -tag -width Ds
.It Ev COMPAT_OLDURI
Only useful for running on www.openbsd.org to deal with old URIs containing
.Qq "manpath=OpenBSD "
where the blank character has to be translated to a hyphen.
When compiling for other sites, this definition can be deleted.
.It Ev CSS_DIR
An optional path to the directory containing the CSS files,
to be specified relative to the server's document root,
and to be specified without a trailing slash.
When not specified, the CSS files
are assumed to be in the document root.
This is used in generated HTML code.
.It Ev CUSTOMIZE_BEGIN
A HTML string to be inserted right after opening the
.Aq BODY
element.
.It Ev CUSTOMIZE_TITLE
An ASCII string to be used for the HTML
.Aq TITLE
element.
.It Ev HTTP_HOST
The FQDN of the (possibly virtual) host the HTTP server is running on.
This is used for
.Ic Location:
headers in HTTP 303 responses.
.It Ev MAN_DIR
A path to the
.Nm
data directory to be used instead of
.Pa /var/www/man ,
relative to the web server
.Xr chroot 2
directory, to be specified without a trailing slash.
This is prepended to the manpath when opening
.Xr mandoc.db 5
and manual page files.
.El
.Pp
After editing
.Pa cgi.h ,
run
.Pp
.Dl make man.cgi
.Pp
and copy the files to the proper locations.
Reading the
.Cm installcgi
target in the
.Pa Makefile
can help with that, but do not run it without carefully checking it
because the directory layouts of web servers vary greatly.
.Ss URI interface
.Nm
uniform resource identifiers are not needed for interactive use,
but can be useful for deep linking.
They consist of:
.Bl -enum
.It
The
.Cm http://
protocol specifier.
.It
The host name and a following slash.
.It
The path to the program, normally
.Pa cgi-bin/man.cgi/ .
.It
To show a single page, a slash, the manpath, another slash,
and the name of the requested file, for example
.Pa /OpenBSD-current/man1/mandoc.1 .
.It
For searches, a query string starting with a question mark
and consisting of
.Ar key Ns = Ns Ar value
pairs, separated by ampersands, for example
.Pa ?manpath=OpenBSD-current&query=mandoc .
Supported keys are
.Cm manpath ,
.Cm query ,
.Cm sec ,
.Cm arch ,
corresponding to
.Xr apropos 1
.Fl M ,
.Ar expression ,
.Fl s ,
.Fl S ,
respectively, and
.Cm apropos ,
which is a boolean parameter to select or deselect the
.Xr apropos 1
query mode.
For backward compatibility with the traditional
.Nm ,
.Cm sektion
is supported as an alias for
.Cm sec .
.El
.Ss Restricted character set
For security reasons, in particular to prevent cross site scripting
attacks, some strings used by
.Nm
can only contain the following characters:
.Pp
.Bl -dash -compact -offset indent
.It
lower case and upper case ASCII letters
.It
the ten decimal digits
.It
the dash
.Pq Sq -
.It
the dot
.Pq Sq \&.
.It
the slash
.Pq Sq /
.It
the underscore
.Pq Sq _
.El
.Pp
In particular, this applies to the
.Ev SCRIPT_NAME ,
to all manpaths, and to all architecture names.
.Sh ENVIRONMENT
The web server may pass the following CGI variables to
.Nm :
.Bl -tag -width Ds
.It Ev PATH_INFO
The final part of the URI path passed from the client to the server,
starting after the
.Ev SCRIPT_NAME
and ending before the
.Ev QUERY_STRING .
It is used by the
.Cm show
page to aquire the manpath and filename it needs.
.It Ev QUERY_STRING
The HTTP query string passed from the client to the server.
It is the final part of the URI, after the question mark.
It is used by the
.Cm search
page to acquire the named parameters it needs.
.It Ev SCRIPT_NAME
The path to the
.Nm
binary relative to the server root, usually
.Pa /cgi-bin/man.cgi .
This is used for generating URIs to be embedded
in generated HTML code and HTTP headers.
If this contains any character not contained in the
.Sx Restricted character set ,
.Nm
reports an internal server error and exits without doing anything.
.El
.Sh FILES
.Bl -tag -width Ds
.It Pa /var/www
Default web server
.Xr chroot 2
directory.
All the following paths are specified relative to this directory.
.It Pa /cgi-bin/man.cgi
The path to the
.Nm
program relative to the server root.
Can be overridden by
.Ev SCRIPT_NAME .
.It Pa /htdocs
The path to the server document root relative to the server root.
This is part of the web server configuration and not specific to
.Nm .
.It Pa /htdocs/man-cgi.css
A style sheet for general
.Nm
styling, referenced from each generated HTML page.
.It Pa /htdocs/man.css
A style sheet for
.Xr mandoc 1
HTML styling, referenced from each generated HTML page after
.Pa man-cgi.css .
.It Pa /man
Default
.Nm
data directory containing all the manual trees.
Can be overridden by
.Ev MAN_DIR .
.It Pa /man/mandoc/man1/apropos.1 , /man/mandoc/man8/man.cgi.8
Manual pages documenting
.Nm
itself, linked from the index page.
.It Pa /man/manpath.conf
The list of available manpaths, one per line.
If any of the lines in this file contains a slash
.Pq Sq /
or any character not contained in the
.Sx Restricted character set ,
.Nm
reports an internal server error and exits without doing anything.
.It Pa /man/OpenBSD-current/man1/mandoc.1
An example
.Xr mdoc 7
source file located below the
.Dq OpenBSD-current
manpath.
.El
.Sh COMPATIBILITY
The
.Nm
CGI program is call-compatible with queries from the traditional
.Pa man.cgi
script by Wolfram Schneider.
However, the output may not be quite the same.
.Sh SEE ALSO
.Xr apropos 1 ,
.Xr mandoc.db 5 ,
.Xr makewhatis 8 ,
.Xr slowcgi 8
.Sh HISTORY
A version of
.Nm
based on
.Xr mandoc 1
first appeared in mdocml-1.12.1 (March 2012).
The current SQLite3-based version first appeared in
.Ox 5.6 .
.Sh AUTHORS
.An -nosplit
The
.Nm
program was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
and ported to the SQLite3-based
.Xr mandoc.db 5
backend by
.An Ingo Schwarze Aq Mt schwarze@openbsd.org .

6
man.h
View File

@ -1,6 +1,7 @@
/* $Id: man.h,v 1.62 2013/10/17 20:54:58 schwarze Exp $ */
/* $Id: man.h,v 1.65 2014/06/20 23:02:31 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014 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
@ -56,6 +57,7 @@ enum mant {
MAN_EE,
MAN_UR,
MAN_UE,
MAN_ll,
MAN_MAX
};
@ -77,6 +79,7 @@ struct man_meta {
char *vol; /* `TH' volume */
char *title; /* `TH' title (e.g., FOO) */
char *source; /* `TH' source (e.g., GNU) */
int hasbody; /* document is not empty */
};
struct man_node {
@ -111,6 +114,7 @@ struct man;
const struct man_node *man_node(const struct man *);
const struct man_meta *man_meta(const struct man *);
const struct mparse *man_mparse(const struct man *);
void man_deroff(char **, const struct man_node *);
__END_DECLS

View File

@ -1,4 +1,4 @@
/* $Id: man_hash.c,v 1.25 2011/07/24 18:15:14 kristaps Exp $ */
/* $Id: man_hash.c,v 1.27 2014/04/20 16:46:04 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -23,7 +23,6 @@
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "man.h"
@ -49,6 +48,7 @@
*/
static unsigned char table[26 * HASH_DEPTH];
/*
* XXX - this hash has global scope, so if intended for use as a library
* with multiple callers, it will need re-invocation protection.
@ -60,8 +60,7 @@ man_hash_init(void)
memset(table, UCHAR_MAX, sizeof(table));
assert(/* LINTED */
MAN_MAX < UCHAR_MAX);
assert(MAN_MAX < UCHAR_MAX);
for (i = 0; i < (int)MAN_MAX; i++) {
x = man_macronames[i][0];
@ -80,7 +79,6 @@ man_hash_init(void)
}
}
enum mant
man_hash_find(const char *tmp)
{

View File

@ -1,7 +1,7 @@
/* $Id: man_html.c,v 1.90 2013/10/17 20:54:58 schwarze Exp $ */
/* $Id: man_html.c,v 1.96 2014/08/01 19:25:52 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -28,6 +28,7 @@
#include <string.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "out.h"
#include "html.h"
#include "man.h"
@ -53,7 +54,7 @@ struct htmlman {
int (*post)(MAN_ARGS);
};
static void print_bvspace(struct html *,
static void print_bvspace(struct html *,
const struct man_node *);
static void print_man(MAN_ARGS);
static void print_man_head(MAN_ARGS);
@ -90,7 +91,7 @@ static const struct htmlman mans[MAN_MAX] = {
{ man_PP_pre, NULL }, /* PP */
{ man_PP_pre, NULL }, /* P */
{ man_IP_pre, NULL }, /* IP */
{ man_HP_pre, NULL }, /* HP */
{ man_HP_pre, NULL }, /* HP */
{ man_SM_pre, NULL }, /* SM */
{ man_SM_pre, NULL }, /* SB */
{ man_alt_pre, NULL }, /* BI */
@ -119,8 +120,10 @@ static const struct htmlman mans[MAN_MAX] = {
{ man_literal_pre, NULL }, /* EE */
{ man_UR_pre, NULL }, /* UR */
{ NULL, NULL }, /* UE */
{ man_ign_pre, NULL }, /* ll */
};
/*
* Printing leading vertical space before a block.
* This is used for the paragraph macros.
@ -155,7 +158,7 @@ html_man(void *arg, const struct man *man)
}
static void
print_man(MAN_ARGS)
print_man(MAN_ARGS)
{
struct tag *t, *tt;
struct htmlpair tag;
@ -170,15 +173,13 @@ print_man(MAN_ARGS)
print_tagq(h, tt);
print_otag(h, TAG_BODY, 0, NULL);
print_otag(h, TAG_DIV, 1, &tag);
} else
} else
t = print_otag(h, TAG_DIV, 1, &tag);
print_man_nodelist(man, n, mh, h);
print_tagq(h, t);
}
/* ARGSUSED */
static void
print_man_head(MAN_ARGS)
{
@ -191,7 +192,6 @@ print_man_head(MAN_ARGS)
print_text(h, h->buf);
}
static void
print_man_nodelist(MAN_ARGS)
{
@ -201,7 +201,6 @@ print_man_nodelist(MAN_ARGS)
print_man_nodelist(man, n->next, mh, h);
}
static void
print_man_node(MAN_ARGS)
{
@ -212,10 +211,10 @@ print_man_node(MAN_ARGS)
t = h->tags.head;
switch (n->type) {
case (MAN_ROOT):
case MAN_ROOT:
man_root_pre(man, n, mh, h);
break;
case (MAN_TEXT):
case MAN_TEXT:
/*
* If we have a blank line, output a vertical space.
* If we have a space as the first character, break
@ -233,10 +232,10 @@ print_man_node(MAN_ARGS)
print_text(h, n->string);
return;
case (MAN_EQN):
case MAN_EQN:
print_eqn(h, n->eqn);
break;
case (MAN_TBL):
case MAN_TBL:
/*
* This will take care of initialising all of the table
* state data for the first table, then tearing it down
@ -245,7 +244,7 @@ print_man_node(MAN_ARGS)
print_tbl(h, n->span);
return;
default:
/*
/*
* Close out scope of font prior to opening a macro
* scope.
*/
@ -275,10 +274,10 @@ print_man_node(MAN_ARGS)
print_stagq(h, t);
switch (n->type) {
case (MAN_ROOT):
case MAN_ROOT:
man_root_post(man, n, mh, h);
break;
case (MAN_EQN):
case MAN_EQN:
break;
default:
if (mans[n->tok].post)
@ -287,7 +286,6 @@ print_man_node(MAN_ARGS)
}
}
static int
a2width(const struct man_node *n, struct roffsu *su)
{
@ -300,22 +298,16 @@ a2width(const struct man_node *n, struct roffsu *su)
return(0);
}
/* ARGSUSED */
static void
man_root_pre(MAN_ARGS)
{
struct htmlpair tag[3];
struct tag *t, *tt;
char b[BUFSIZ], title[BUFSIZ];
b[0] = 0;
if (man->vol)
(void)strlcat(b, man->vol, BUFSIZ);
char *title;
assert(man->title);
assert(man->msec);
snprintf(title, BUFSIZ - 1, "%s(%s)", man->title, man->msec);
mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
PAIR_SUMMARY_INIT(&tag[0], "Document Header");
PAIR_CLASS_INIT(&tag[1], "head");
@ -338,7 +330,8 @@ man_root_pre(MAN_ARGS)
PAIR_CLASS_INIT(&tag[0], "head-vol");
PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
print_otag(h, TAG_TD, 2, tag);
print_text(h, b);
if (NULL != man->vol)
print_text(h, man->vol);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag[0], "head-rtitle");
@ -346,10 +339,9 @@ man_root_pre(MAN_ARGS)
print_otag(h, TAG_TD, 2, tag);
print_text(h, title);
print_tagq(h, t);
free(title);
}
/* ARGSUSED */
static void
man_root_post(MAN_ARGS)
{
@ -383,7 +375,6 @@ man_root_post(MAN_ARGS)
}
/* ARGSUSED */
static int
man_br_pre(MAN_ARGS)
{
@ -397,7 +388,7 @@ man_br_pre(MAN_ARGS)
if ( ! a2roffsu(n->string, &su, SCALE_VS))
SCALE_VS_INIT(&su, atoi(n->string));
} else
su.scale = 0;
su.scale = 0.0;
bufinit(h);
bufcat_su(h, "height", &su);
@ -410,7 +401,6 @@ man_br_pre(MAN_ARGS)
return(0);
}
/* ARGSUSED */
static int
man_SH_pre(MAN_ARGS)
{
@ -428,7 +418,6 @@ man_SH_pre(MAN_ARGS)
return(1);
}
/* ARGSUSED */
static int
man_alt_pre(MAN_ARGS)
{
@ -437,7 +426,7 @@ man_alt_pre(MAN_ARGS)
enum htmltag fp;
struct tag *t;
if ((savelit = mh->fl & MANH_LITERAL))
if ((savelit = mh->fl & MANH_LITERAL))
print_otag(h, TAG_BR, 0, NULL);
mh->fl &= ~MANH_LITERAL;
@ -445,22 +434,22 @@ man_alt_pre(MAN_ARGS)
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
t = NULL;
switch (n->tok) {
case (MAN_BI):
case MAN_BI:
fp = i % 2 ? TAG_I : TAG_B;
break;
case (MAN_IB):
case MAN_IB:
fp = i % 2 ? TAG_B : TAG_I;
break;
case (MAN_RI):
case MAN_RI:
fp = i % 2 ? TAG_I : TAG_MAX;
break;
case (MAN_IR):
case MAN_IR:
fp = i % 2 ? TAG_MAX : TAG_I;
break;
case (MAN_BR):
case MAN_BR:
fp = i % 2 ? TAG_MAX : TAG_B;
break;
case (MAN_RB):
case MAN_RB:
fp = i % 2 ? TAG_B : TAG_MAX;
break;
default:
@ -486,18 +475,16 @@ man_alt_pre(MAN_ARGS)
return(0);
}
/* ARGSUSED */
static int
man_SM_pre(MAN_ARGS)
{
print_otag(h, TAG_SMALL, 0, NULL);
if (MAN_SB == n->tok)
print_otag(h, TAG_B, 0, NULL);
return(1);
}
/* ARGSUSED */
static int
man_SS_pre(MAN_ARGS)
{
@ -515,7 +502,6 @@ man_SS_pre(MAN_ARGS)
return(1);
}
/* ARGSUSED */
static int
man_PP_pre(MAN_ARGS)
{
@ -528,13 +514,12 @@ man_PP_pre(MAN_ARGS)
return(1);
}
/* ARGSUSED */
static int
man_IP_pre(MAN_ARGS)
{
const struct man_node *nn;
if (MAN_BODY == n->type) {
if (MAN_BODY == n->type) {
print_otag(h, TAG_DD, 0, NULL);
return(1);
} else if (MAN_HEAD != n->type) {
@ -553,15 +538,19 @@ man_IP_pre(MAN_ARGS)
/* For TP, only print next-line header elements. */
if (MAN_TP == n->tok)
for (nn = n->child; nn; nn = nn->next)
if (nn->line > n->line)
print_man_node(man, nn, mh, h);
if (MAN_TP == n->tok) {
nn = n->child;
while (NULL != nn && 0 == (MAN_LINE & nn->flags))
nn = nn->next;
while (NULL != nn) {
print_man_node(man, nn, mh, h);
nn = nn->next;
}
}
return(0);
}
/* ARGSUSED */
static int
man_HP_pre(MAN_ARGS)
{
@ -590,7 +579,6 @@ man_HP_pre(MAN_ARGS)
return(1);
}
/* ARGSUSED */
static int
man_OP_pre(MAN_ARGS)
{
@ -620,8 +608,6 @@ man_OP_pre(MAN_ARGS)
return(0);
}
/* ARGSUSED */
static int
man_B_pre(MAN_ARGS)
{
@ -630,16 +616,14 @@ man_B_pre(MAN_ARGS)
return(1);
}
/* ARGSUSED */
static int
man_I_pre(MAN_ARGS)
{
print_otag(h, TAG_I, 0, NULL);
return(1);
}
/* ARGSUSED */
static int
man_literal_pre(MAN_ARGS)
{
@ -653,7 +637,6 @@ man_literal_pre(MAN_ARGS)
return(0);
}
/* ARGSUSED */
static int
man_in_pre(MAN_ARGS)
{
@ -662,7 +645,6 @@ man_in_pre(MAN_ARGS)
return(0);
}
/* ARGSUSED */
static int
man_ign_pre(MAN_ARGS)
{
@ -670,7 +652,6 @@ man_ign_pre(MAN_ARGS)
return(0);
}
/* ARGSUSED */
static int
man_RS_pre(MAN_ARGS)
{
@ -693,7 +674,6 @@ man_RS_pre(MAN_ARGS)
return(1);
}
/* ARGSUSED */
static int
man_UR_pre(MAN_ARGS)
{

View File

@ -1,4 +1,4 @@
/* $Id: man_macro.c,v 1.79 2013/12/25 00:50:05 schwarze Exp $ */
/* $Id: man_macro.c,v 1.87 2014/07/30 23:01:39 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
@ -40,17 +40,15 @@ static int blk_close(MACRO_PROT_ARGS);
static int blk_exp(MACRO_PROT_ARGS);
static int blk_imp(MACRO_PROT_ARGS);
static int in_line_eoln(MACRO_PROT_ARGS);
static int man_args(struct man *, int,
static int man_args(struct man *, int,
int *, char *, char **);
static int rew_scope(enum man_type,
static int rew_scope(enum man_type,
struct man *, enum mant);
static enum rew rew_dohalt(enum mant, enum man_type,
static enum rew rew_dohalt(enum mant, enum man_type,
const struct man_node *);
static enum rew rew_block(enum mant, enum man_type,
static enum rew rew_block(enum mant, enum man_type,
const struct man_node *);
static void rew_warn(struct man *,
struct man_node *, enum mandocerr);
const struct man_macro __man_macros[MAN_MAX] = {
{ in_line_eoln, MAN_NSCOPED }, /* br */
@ -91,85 +89,80 @@ const struct man_macro __man_macros[MAN_MAX] = {
{ in_line_eoln, MAN_BSCOPE }, /* EE */
{ blk_exp, MAN_BSCOPE | MAN_EXPLICIT }, /* UR */
{ blk_close, 0 }, /* UE */
{ in_line_eoln, 0 }, /* ll */
};
const struct man_macro * const man_macros = __man_macros;
/*
* Warn when "n" is an explicit non-roff macro.
*/
static void
rew_warn(struct man *man, struct man_node *n, enum mandocerr er)
{
if (er == MANDOCERR_MAX || MAN_BLOCK != n->type)
return;
if (MAN_VALID & n->flags)
return;
if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags))
return;
assert(er < MANDOCERR_FATAL);
man_nmsg(man, n, er);
}
/*
* Rewind scope. If a code "er" != MANDOCERR_MAX has been provided, it
* will be used if an explicit block scope is being closed out.
*/
int
man_unscope(struct man *man, const struct man_node *to,
enum mandocerr er)
man_unscope(struct man *man, const struct man_node *to)
{
struct man_node *n;
assert(to);
man->next = MAN_NEXT_SIBLING;
to = to->parent;
n = man->last;
while (n != to) {
/* Reached the end of the document? */
if (to == NULL && ! (n->flags & MAN_VALID)) {
if (man->flags & (MAN_BLINE | MAN_ELINE) &&
man_macros[n->tok].flags & MAN_SCOPED) {
mandoc_vmsg(MANDOCERR_BLK_LINE,
man->parse, n->line, n->pos,
"EOF breaks %s",
man_macronames[n->tok]);
if (man->flags & MAN_ELINE)
man->flags &= ~MAN_ELINE;
else {
assert(n->type == MAN_HEAD);
n = n->parent;
man->flags &= ~MAN_BLINE;
}
man->last = n;
n = n->parent;
man_node_delete(man, man->last);
continue;
}
if (n->type == MAN_BLOCK &&
man_macros[n->tok].flags & MAN_EXPLICIT)
mandoc_msg(MANDOCERR_BLK_NOEND,
man->parse, n->line, n->pos,
man_macronames[n->tok]);
}
/* LINTED */
while (man->last != to) {
/*
* Save the parent here, because we may delete the
* man->last node in the post-validation phase and reset
* it to man->last->parent, causing a step in the closing
* out to be lost.
* We might delete the man->last node
* in the post-validation phase.
* Save a pointer to the parent such that
* we know where to continue the iteration.
*/
n = man->last->parent;
rew_warn(man, man->last, er);
man->last = n;
n = n->parent;
if ( ! man_valid_post(man))
return(0);
man->last = n;
assert(man->last);
}
rew_warn(man, man->last, er);
if ( ! man_valid_post(man))
return(0);
return(1);
}
static enum rew
rew_block(enum mant ntok, enum man_type type, const struct man_node *n)
{
if (MAN_BLOCK == type && ntok == n->parent->tok &&
MAN_BODY == n->parent->type)
if (MAN_BLOCK == type && ntok == n->parent->tok &&
MAN_BODY == n->parent->type)
return(REW_REWIND);
return(ntok == n->tok ? REW_HALT : REW_NOHALT);
}
/*
* There are three scope levels: scoped to the root (all), scoped to the
* section (all less sections), and scoped to subsections (all less
* sections and subsections).
*/
static enum rew
static enum rew
rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
{
enum rew c;
@ -196,20 +189,20 @@ rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
return(REW_REWIND);
}
/*
/*
* Next follow the implicit scope-smashings as defined by man.7:
* section, sub-section, etc.
*/
switch (tok) {
case (MAN_SH):
case MAN_SH:
break;
case (MAN_SS):
case MAN_SS:
/* Rewind to a section, if a block. */
if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
return(c);
break;
case (MAN_RS):
case MAN_RS:
/* Preserve empty paragraphs before RS. */
if (0 == n->nchild && (MAN_P == n->tok ||
MAN_PP == n->tok || MAN_LP == n->tok))
@ -237,7 +230,6 @@ rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
return(REW_NOHALT);
}
/*
* Rewinding entails ascending the parse tree until a coherent point,
* for example, the `SH' macro will close out any intervening `SS'
@ -249,9 +241,8 @@ rew_scope(enum man_type type, struct man *man, enum mant tok)
struct man_node *n;
enum rew c;
/* LINTED */
for (n = man->last; n; n = n->parent) {
/*
/*
* Whether we should stop immediately (REW_HALT), stop
* and rewind until this point (REW_REWIND), or keep
* rewinding (REW_NOHALT).
@ -263,31 +254,30 @@ rew_scope(enum man_type type, struct man *man, enum mant tok)
break;
}
/*
/*
* Rewind until the current point. Warn if we're a roff
* instruction that's mowing over explicit scopes.
*/
assert(n);
return(man_unscope(man, n, MANDOCERR_MAX));
return(man_unscope(man, n));
}
/*
* Close out a generic explicit macro.
*/
/* ARGSUSED */
int
blk_close(MACRO_PROT_ARGS)
{
enum mant ntok;
enum mant ntok;
const struct man_node *nn;
switch (tok) {
case (MAN_RE):
case MAN_RE:
ntok = MAN_RS;
break;
case (MAN_UE):
case MAN_UE:
ntok = MAN_UR;
break;
default:
@ -300,17 +290,16 @@ blk_close(MACRO_PROT_ARGS)
break;
if (NULL == nn) {
man_pmsg(man, line, ppos, MANDOCERR_NOSCOPE);
mandoc_msg(MANDOCERR_BLK_NOTOPEN, man->parse,
line, ppos, man_macronames[tok]);
if ( ! rew_scope(MAN_BLOCK, man, MAN_PP))
return(0);
} else
man_unscope(man, nn, MANDOCERR_MAX);
} else
man_unscope(man, nn);
return(1);
}
/* ARGSUSED */
int
blk_exp(MACRO_PROT_ARGS)
{
@ -343,22 +332,19 @@ blk_exp(MACRO_PROT_ARGS)
if (n->tok != tok)
continue;
assert(MAN_HEAD == n->type);
man_unscope(man, n, MANDOCERR_MAX);
man_unscope(man, n);
break;
}
return(man_body_alloc(man, line, ppos, tok));
}
/*
* Parse an implicit-block macro. These contain a MAN_HEAD and a
* MAN_BODY contained within a MAN_BLOCK. Rules for closing out other
* scopes, such as `SH' closing out an `SS', are defined in the rew
* routines.
*/
/* ARGSUSED */
int
blk_imp(MACRO_PROT_ARGS)
{
@ -410,8 +396,6 @@ blk_imp(MACRO_PROT_ARGS)
return(man_body_alloc(man, line, ppos, tok));
}
/* ARGSUSED */
int
in_line_eoln(MACRO_PROT_ARGS)
{
@ -438,7 +422,7 @@ in_line_eoln(MACRO_PROT_ARGS)
*/
if (n != man->last &&
mandoc_eos(man->last->string, strlen(man->last->string), 0))
mandoc_eos(man->last->string, strlen(man->last->string)))
man->last->flags |= MAN_EOS;
/*
@ -451,18 +435,11 @@ in_line_eoln(MACRO_PROT_ARGS)
assert( ! (MAN_NSCOPED & man_macros[tok].flags));
man->flags |= MAN_ELINE;
return(1);
}
/* Set ignorable context, if applicable. */
if (MAN_NSCOPED & man_macros[tok].flags) {
assert( ! (MAN_SCOPED & man_macros[tok].flags));
man->flags |= MAN_ILINE;
}
assert(MAN_ROOT != man->last->type);
man->next = MAN_NEXT_SIBLING;
/*
* Rewind our element scope. Note that when TH is pruned, we'll
* be back at the root, so make sure that we don't clobber as
@ -481,7 +458,7 @@ in_line_eoln(MACRO_PROT_ARGS)
assert(man->last);
/*
* Same here regarding whether we're back at the root.
* Same here regarding whether we're back at the root.
*/
if (man->last->type != MAN_ROOT && ! man_valid_post(man))
@ -495,7 +472,7 @@ int
man_macroend(struct man *man)
{
return(man_unscope(man, man->first, MANDOCERR_SCOPEEXIT));
return(man_unscope(man, man->first));
}
static int

View File

@ -1,7 +1,7 @@
/* $Id: man_term.c,v 1.139 2013/12/22 23:34:13 schwarze Exp $ */
/* $Id: man_term.c,v 1.149 2014/06/20 23:02:31 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -28,6 +28,7 @@
#include <string.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "out.h"
#include "man.h"
#include "term.h"
@ -45,7 +46,7 @@ struct mtermp {
int pardist; /* vert. space before par., unit: [v] */
};
#define DECL_ARGS struct termp *p, \
#define DECL_ARGS struct termp *p, \
struct mtermp *mt, \
const struct man_node *n, \
const struct man_meta *meta
@ -64,7 +65,7 @@ static void print_man_nodelist(DECL_ARGS);
static void print_man_node(DECL_ARGS);
static void print_man_head(struct termp *, const void *);
static void print_man_foot(struct termp *, const void *);
static void print_bvspace(struct termp *,
static void print_bvspace(struct termp *,
const struct man_node *, int);
static int pre_B(DECL_ARGS);
@ -84,6 +85,7 @@ static int pre_ft(DECL_ARGS);
static int pre_ign(DECL_ARGS);
static int pre_in(DECL_ARGS);
static int pre_literal(DECL_ARGS);
static int pre_ll(DECL_ARGS);
static int pre_sp(DECL_ARGS);
static void post_IP(DECL_ARGS);
@ -104,7 +106,7 @@ static const struct termact termacts[MAN_MAX] = {
{ pre_PP, NULL, 0 }, /* PP */
{ pre_PP, NULL, 0 }, /* P */
{ pre_IP, post_IP, 0 }, /* IP */
{ pre_HP, post_HP, 0 }, /* HP */
{ pre_HP, post_HP, 0 }, /* HP */
{ NULL, NULL, 0 }, /* SM */
{ pre_B, NULL, 0 }, /* SB */
{ pre_alternate, NULL, 0 }, /* BI */
@ -133,10 +135,10 @@ static const struct termact termacts[MAN_MAX] = {
{ pre_literal, NULL, 0 }, /* EE */
{ pre_UR, post_UR, 0 }, /* UR */
{ NULL, NULL, 0 }, /* UE */
{ pre_ll, NULL, MAN_NOTEXT }, /* ll */
};
void
terminal_man(void *arg, const struct man *man)
{
@ -187,7 +189,6 @@ a2height(const struct termp *p, const char *cp)
return(term_vspan(p, &su));
}
static int
a2width(const struct termp *p, const char *cp)
{
@ -226,7 +227,7 @@ print_bvspace(struct termp *p, const struct man_node *n, int pardist)
term_vspace(p);
}
/* ARGSUSED */
static int
pre_ign(DECL_ARGS)
{
@ -234,8 +235,14 @@ pre_ign(DECL_ARGS)
return(0);
}
static int
pre_ll(DECL_ARGS)
{
term_setwidth(p, n->nchild ? n->child->string : NULL);
return(0);
}
/* ARGSUSED */
static int
pre_I(DECL_ARGS)
{
@ -244,8 +251,6 @@ pre_I(DECL_ARGS)
return(1);
}
/* ARGSUSED */
static int
pre_literal(DECL_ARGS)
{
@ -266,14 +271,13 @@ pre_literal(DECL_ARGS)
p->offset = p->rmargin;
p->rmargin = p->maxrmargin;
p->trailspace = 0;
p->flags &= ~TERMP_NOBREAK;
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
p->flags |= TERMP_NOSPACE;
}
return(0);
}
/* ARGSUSED */
static int
pre_PD(DECL_ARGS)
{
@ -288,7 +292,6 @@ pre_PD(DECL_ARGS)
return(0);
}
/* ARGSUSED */
static int
pre_alternate(DECL_ARGS)
{
@ -297,27 +300,27 @@ pre_alternate(DECL_ARGS)
int savelit, i;
switch (n->tok) {
case (MAN_RB):
case MAN_RB:
font[0] = TERMFONT_NONE;
font[1] = TERMFONT_BOLD;
break;
case (MAN_RI):
case MAN_RI:
font[0] = TERMFONT_NONE;
font[1] = TERMFONT_UNDER;
break;
case (MAN_BR):
case MAN_BR:
font[0] = TERMFONT_BOLD;
font[1] = TERMFONT_NONE;
break;
case (MAN_BI):
case MAN_BI:
font[0] = TERMFONT_BOLD;
font[1] = TERMFONT_UNDER;
break;
case (MAN_IR):
case MAN_IR:
font[0] = TERMFONT_UNDER;
font[1] = TERMFONT_NONE;
break;
case (MAN_IB):
case MAN_IB:
font[0] = TERMFONT_UNDER;
font[1] = TERMFONT_BOLD;
break;
@ -340,7 +343,6 @@ pre_alternate(DECL_ARGS)
return(0);
}
/* ARGSUSED */
static int
pre_B(DECL_ARGS)
{
@ -349,7 +351,6 @@ pre_B(DECL_ARGS)
return(1);
}
/* ARGSUSED */
static int
pre_OP(DECL_ARGS)
{
@ -372,7 +373,6 @@ pre_OP(DECL_ARGS)
return(0);
}
/* ARGSUSED */
static int
pre_ft(DECL_ARGS)
{
@ -385,26 +385,26 @@ pre_ft(DECL_ARGS)
cp = n->child->string;
switch (*cp) {
case ('4'):
case '4':
/* FALLTHROUGH */
case ('3'):
case '3':
/* FALLTHROUGH */
case ('B'):
case 'B':
term_fontrepl(p, TERMFONT_BOLD);
break;
case ('2'):
case '2':
/* FALLTHROUGH */
case ('I'):
case 'I':
term_fontrepl(p, TERMFONT_UNDER);
break;
case ('P'):
case 'P':
term_fontlast(p);
break;
case ('1'):
case '1':
/* FALLTHROUGH */
case ('C'):
case 'C':
/* FALLTHROUGH */
case ('R'):
case 'R':
term_fontrepl(p, TERMFONT_NONE);
break;
default:
@ -413,7 +413,6 @@ pre_ft(DECL_ARGS)
return(0);
}
/* ARGSUSED */
static int
pre_in(DECL_ARGS)
{
@ -447,7 +446,7 @@ pre_in(DECL_ARGS)
p->offset -= p->offset > v ? v : p->offset;
else if (less > 0)
p->offset += v;
else
else
p->offset = v;
/* Don't let this creep beyond the right margin. */
@ -458,8 +457,6 @@ pre_in(DECL_ARGS)
return(0);
}
/* ARGSUSED */
static int
pre_sp(DECL_ARGS)
{
@ -469,15 +466,15 @@ pre_sp(DECL_ARGS)
if ((NULL == n->prev && n->parent)) {
switch (n->parent->tok) {
case (MAN_SH):
case MAN_SH:
/* FALLTHROUGH */
case (MAN_SS):
case MAN_SS:
/* FALLTHROUGH */
case (MAN_PP):
case MAN_PP:
/* FALLTHROUGH */
case (MAN_LP):
case MAN_LP:
/* FALLTHROUGH */
case (MAN_P):
case MAN_P:
/* FALLTHROUGH */
return(0);
default:
@ -487,7 +484,7 @@ pre_sp(DECL_ARGS)
neg = 0;
switch (n->tok) {
case (MAN_br):
case MAN_br:
len = 0;
break;
default:
@ -515,8 +512,6 @@ pre_sp(DECL_ARGS)
return(0);
}
/* ARGSUSED */
static int
pre_HP(DECL_ARGS)
{
@ -525,17 +520,17 @@ pre_HP(DECL_ARGS)
const struct man_node *nn;
switch (n->type) {
case (MAN_BLOCK):
case MAN_BLOCK:
print_bvspace(p, n, mt->pardist);
return(1);
case (MAN_BODY):
case MAN_BODY:
break;
default:
return(0);
}
if ( ! (MANT_LITERAL & mt->fl)) {
p->flags |= TERMP_NOBREAK;
p->flags |= TERMP_NOBREAK | TERMP_BRIND;
p->trailspace = 2;
}
@ -561,16 +556,14 @@ pre_HP(DECL_ARGS)
return(1);
}
/* ARGSUSED */
static void
post_HP(DECL_ARGS)
{
switch (n->type) {
case (MAN_BODY):
case MAN_BODY:
term_newln(p);
p->flags &= ~TERMP_NOBREAK;
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
p->trailspace = 0;
p->offset = mt->offset;
p->rmargin = p->maxrmargin;
@ -580,14 +573,12 @@ post_HP(DECL_ARGS)
}
}
/* ARGSUSED */
static int
pre_PP(DECL_ARGS)
{
switch (n->type) {
case (MAN_BLOCK):
case MAN_BLOCK:
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
print_bvspace(p, n, mt->pardist);
break;
@ -599,8 +590,6 @@ pre_PP(DECL_ARGS)
return(MAN_HEAD != n->type);
}
/* ARGSUSED */
static int
pre_IP(DECL_ARGS)
{
@ -609,14 +598,14 @@ pre_IP(DECL_ARGS)
int savelit, ival;
switch (n->type) {
case (MAN_BODY):
case MAN_BODY:
p->flags |= TERMP_NOSPACE;
break;
case (MAN_HEAD):
case MAN_HEAD:
p->flags |= TERMP_NOBREAK;
p->trailspace = 1;
break;
case (MAN_BLOCK):
case MAN_BLOCK:
print_bvspace(p, n, mt->pardist);
/* FALLTHROUGH */
default:
@ -633,7 +622,7 @@ pre_IP(DECL_ARGS)
len = (size_t)ival;
switch (n->type) {
case (MAN_HEAD):
case MAN_HEAD:
/* Handle zero-width lengths. */
if (0 == len)
len = term_len(p, 1);
@ -656,9 +645,10 @@ pre_IP(DECL_ARGS)
mt->fl |= MANT_LITERAL;
return(0);
case (MAN_BODY):
case MAN_BODY:
p->offset = mt->offset + len;
p->rmargin = p->maxrmargin;
p->rmargin = p->maxrmargin > p->offset ?
p->maxrmargin : p->offset;
break;
default:
break;
@ -667,20 +657,18 @@ pre_IP(DECL_ARGS)
return(1);
}
/* ARGSUSED */
static void
post_IP(DECL_ARGS)
{
switch (n->type) {
case (MAN_HEAD):
case MAN_HEAD:
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
p->trailspace = 0;
p->rmargin = p->maxrmargin;
break;
case (MAN_BODY):
case MAN_BODY:
term_newln(p);
p->offset = mt->offset;
break;
@ -689,8 +677,6 @@ post_IP(DECL_ARGS)
}
}
/* ARGSUSED */
static int
pre_TP(DECL_ARGS)
{
@ -699,14 +685,14 @@ pre_TP(DECL_ARGS)
int savelit, ival;
switch (n->type) {
case (MAN_HEAD):
case MAN_HEAD:
p->flags |= TERMP_NOBREAK;
p->trailspace = 1;
break;
case (MAN_BODY):
case MAN_BODY:
p->flags |= TERMP_NOSPACE;
break;
case (MAN_BLOCK):
case MAN_BLOCK:
print_bvspace(p, n, mt->pardist);
/* FALLTHROUGH */
default:
@ -719,12 +705,12 @@ pre_TP(DECL_ARGS)
/* Calculate offset. */
if (NULL != (nn = n->parent->head->child))
if (nn->string && nn->parent->line == nn->line)
if (nn->string && 0 == (MAN_LINE & nn->flags))
if ((ival = a2width(p, nn->string)) >= 0)
len = (size_t)ival;
switch (n->type) {
case (MAN_HEAD):
case MAN_HEAD:
/* Handle zero-length properly. */
if (0 == len)
len = term_len(p, 1);
@ -736,9 +722,14 @@ pre_TP(DECL_ARGS)
mt->fl &= ~MANT_LITERAL;
/* Don't print same-line elements. */
for (nn = n->child; nn; nn = nn->next)
if (nn->line > n->line)
print_man_node(p, mt, nn, meta);
nn = n->child;
while (NULL != nn && 0 == (MAN_LINE & nn->flags))
nn = nn->next;
while (NULL != nn) {
print_man_node(p, mt, nn, meta);
nn = nn->next;
}
if (savelit)
mt->fl |= MANT_LITERAL;
@ -746,9 +737,10 @@ pre_TP(DECL_ARGS)
mt->lmargin[mt->lmargincur] = (size_t)ival;
return(0);
case (MAN_BODY):
case MAN_BODY:
p->offset = mt->offset + len;
p->rmargin = p->maxrmargin;
p->rmargin = p->maxrmargin > p->offset ?
p->maxrmargin : p->offset;
p->trailspace = 0;
p->flags &= ~TERMP_NOBREAK;
break;
@ -759,17 +751,15 @@ pre_TP(DECL_ARGS)
return(1);
}
/* ARGSUSED */
static void
post_TP(DECL_ARGS)
{
switch (n->type) {
case (MAN_HEAD):
case MAN_HEAD:
term_flushln(p);
break;
case (MAN_BODY):
case MAN_BODY:
term_newln(p);
p->offset = mt->offset;
break;
@ -778,15 +768,13 @@ post_TP(DECL_ARGS)
}
}
/* ARGSUSED */
static int
pre_SS(DECL_ARGS)
{
int i;
switch (n->type) {
case (MAN_BLOCK):
case MAN_BLOCK:
mt->fl &= ~MANT_LITERAL;
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
mt->offset = term_len(p, p->defindent);
@ -799,11 +787,11 @@ pre_SS(DECL_ARGS)
for (i = 0; i < mt->pardist; i++)
term_vspace(p);
break;
case (MAN_HEAD):
case MAN_HEAD:
term_fontrepl(p, TERMFONT_BOLD);
p->offset = term_len(p, 3);
break;
case (MAN_BODY):
case MAN_BODY:
p->offset = mt->offset;
break;
default:
@ -813,17 +801,15 @@ pre_SS(DECL_ARGS)
return(1);
}
/* ARGSUSED */
static void
post_SS(DECL_ARGS)
{
switch (n->type) {
case (MAN_HEAD):
case MAN_HEAD:
term_newln(p);
break;
case (MAN_BODY):
case MAN_BODY:
term_newln(p);
break;
default:
@ -831,15 +817,13 @@ post_SS(DECL_ARGS)
}
}
/* ARGSUSED */
static int
pre_SH(DECL_ARGS)
{
int i;
switch (n->type) {
case (MAN_BLOCK):
case MAN_BLOCK:
mt->fl &= ~MANT_LITERAL;
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
mt->offset = term_len(p, p->defindent);
@ -853,11 +837,11 @@ pre_SH(DECL_ARGS)
for (i = 0; i < mt->pardist; i++)
term_vspace(p);
break;
case (MAN_HEAD):
case MAN_HEAD:
term_fontrepl(p, TERMFONT_BOLD);
p->offset = 0;
break;
case (MAN_BODY):
case MAN_BODY:
p->offset = mt->offset;
break;
default:
@ -867,17 +851,15 @@ pre_SH(DECL_ARGS)
return(1);
}
/* ARGSUSED */
static void
post_SH(DECL_ARGS)
{
switch (n->type) {
case (MAN_HEAD):
case MAN_HEAD:
term_newln(p);
break;
case (MAN_BODY):
case MAN_BODY:
term_newln(p);
break;
default:
@ -885,7 +867,6 @@ post_SH(DECL_ARGS)
}
}
/* ARGSUSED */
static int
pre_RS(DECL_ARGS)
{
@ -893,10 +874,10 @@ pre_RS(DECL_ARGS)
size_t sz;
switch (n->type) {
case (MAN_BLOCK):
case MAN_BLOCK:
term_newln(p);
return(1);
case (MAN_HEAD):
case MAN_HEAD:
return(0);
default:
break;
@ -905,12 +886,13 @@ pre_RS(DECL_ARGS)
sz = term_len(p, p->defindent);
if (NULL != (n = n->parent->head->child))
if ((ival = a2width(p, n->string)) >= 0)
if ((ival = a2width(p, n->string)) >= 0)
sz = (size_t)ival;
mt->offset += sz;
p->rmargin = p->maxrmargin;
p->offset = mt->offset < p->rmargin ? mt->offset : p->rmargin;
p->offset = mt->offset;
p->rmargin = p->maxrmargin > p->offset ?
p->maxrmargin : p->offset;
if (++mt->lmarginsz < MAXMARGINS)
mt->lmargincur = mt->lmarginsz;
@ -919,7 +901,6 @@ pre_RS(DECL_ARGS)
return(1);
}
/* ARGSUSED */
static void
post_RS(DECL_ARGS)
{
@ -927,9 +908,9 @@ post_RS(DECL_ARGS)
size_t sz;
switch (n->type) {
case (MAN_BLOCK):
case MAN_BLOCK:
return;
case (MAN_HEAD):
case MAN_HEAD:
return;
default:
term_newln(p);
@ -938,8 +919,8 @@ post_RS(DECL_ARGS)
sz = term_len(p, p->defindent);
if (NULL != (n = n->parent->head->child))
if ((ival = a2width(p, n->string)) >= 0)
if (NULL != (n = n->parent->head->child))
if ((ival = a2width(p, n->string)) >= 0)
sz = (size_t)ival;
mt->offset = mt->offset < sz ? 0 : mt->offset - sz;
@ -949,7 +930,6 @@ post_RS(DECL_ARGS)
mt->lmargincur = mt->lmarginsz;
}
/* ARGSUSED */
static int
pre_UR(DECL_ARGS)
{
@ -957,7 +937,6 @@ pre_UR(DECL_ARGS)
return (MAN_HEAD != n->type);
}
/* ARGSUSED */
static void
post_UR(DECL_ARGS)
{
@ -982,7 +961,7 @@ print_man_node(DECL_ARGS)
int c;
switch (n->type) {
case(MAN_TEXT):
case MAN_TEXT:
/*
* If we have a blank line, output a vertical space.
* If we have a space as the first character, break
@ -997,15 +976,15 @@ print_man_node(DECL_ARGS)
term_word(p, n->string);
goto out;
case (MAN_EQN):
case MAN_EQN:
term_eqn(p, n->eqn);
return;
case (MAN_TBL):
case MAN_TBL:
/*
* Tables are preceded by a newline. Then process a
* table line, which will cause line termination,
*/
if (TBL_SPAN_FIRST & n->span->flags)
if (TBL_SPAN_FIRST & n->span->flags)
term_newln(p);
term_tbl(p, n->span);
return;
@ -1037,7 +1016,7 @@ print_man_node(DECL_ARGS)
* more specific than this.
*/
if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
(NULL == n->next || n->next->line > n->line)) {
(NULL == n->next || MAN_LINE & n->next->flags)) {
rm = p->rmargin;
rmax = p->maxrmargin;
p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
@ -1068,13 +1047,12 @@ print_man_nodelist(DECL_ARGS)
print_man_nodelist(p, mt, n->next, meta);
}
static void
print_man_foot(struct termp *p, const void *arg)
{
char title[BUFSIZ];
size_t datelen;
const struct man_meta *meta;
const struct man_meta *meta;
char *title;
size_t datelen;
meta = (const struct man_meta *)arg;
assert(meta->title);
@ -1083,7 +1061,8 @@ print_man_foot(struct termp *p, const void *arg)
term_fontrepl(p, TERMFONT_NONE);
term_vspace(p);
if (meta->hasbody)
term_vspace(p);
/*
* Temporary, undocumented option to imitate mdoc(7) output.
@ -1092,13 +1071,16 @@ print_man_foot(struct termp *p, const void *arg)
*/
if ( ! p->mdocstyle) {
term_vspace(p);
term_vspace(p);
snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
if (meta->hasbody) {
term_vspace(p);
term_vspace(p);
}
mandoc_asprintf(&title, "%s(%s)",
meta->title, meta->msec);
} else if (meta->source) {
strlcpy(title, meta->source, BUFSIZ);
title = mandoc_strdup(meta->source);
} else {
title[0] = '\0';
title = mandoc_strdup("");
}
datelen = term_strlen(p, meta->date);
@ -1134,38 +1116,35 @@ print_man_foot(struct termp *p, const void *arg)
term_word(p, title);
term_flushln(p);
free(title);
}
static void
print_man_head(struct termp *p, const void *arg)
{
char buf[BUFSIZ], title[BUFSIZ];
size_t buflen, titlen;
const struct man_meta *meta;
const struct man_meta *meta;
const char *volume;
char *title;
size_t vollen, titlen;
meta = (const struct man_meta *)arg;
assert(meta->title);
assert(meta->msec);
if (meta->vol)
strlcpy(buf, meta->vol, BUFSIZ);
else
buf[0] = '\0';
buflen = term_strlen(p, buf);
volume = NULL == meta->vol ? "" : meta->vol;
vollen = term_strlen(p, volume);
/* Top left corner: manual title and section. */
snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
titlen = term_strlen(p, title);
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
p->trailspace = 1;
p->offset = 0;
p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
(p->maxrmargin -
term_strlen(p, buf) + term_len(p, 1)) / 2 :
p->maxrmargin - buflen;
p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
(p->maxrmargin - vollen + term_len(p, 1)) / 2 :
p->maxrmargin - vollen;
term_word(p, title);
term_flushln(p);
@ -1174,10 +1153,10 @@ print_man_head(struct termp *p, const void *arg)
p->flags |= TERMP_NOSPACE;
p->offset = p->rmargin;
p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
p->maxrmargin - titlen : p->maxrmargin;
term_word(p, buf);
term_word(p, volume);
term_flushln(p);
/* Top right corner: title and section, again. */
@ -1196,7 +1175,7 @@ print_man_head(struct termp *p, const void *arg)
p->offset = 0;
p->rmargin = p->maxrmargin;
/*
/*
* Groff prints three blank lines before the content.
* Do the same, except in the temporary, undocumented
* mode imitating mdoc(7) output.
@ -1207,4 +1186,5 @@ print_man_head(struct termp *p, const void *arg)
term_vspace(p);
term_vspace(p);
}
free(title);
}

View File

@ -1,7 +1,7 @@
/* $Id: man_validate.c,v 1.86 2013/10/17 20:54:58 schwarze Exp $ */
/* $Id: man_validate.c,v 1.105 2014/08/06 15:09:05 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -32,6 +32,7 @@
#include "man.h"
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libman.h"
#include "libmandoc.h"
@ -39,21 +40,14 @@
typedef int (*v_check)(CHKARGS);
struct man_valid {
v_check *pres;
v_check *posts;
};
static int check_eq0(CHKARGS);
static int check_eq2(CHKARGS);
static int check_le1(CHKARGS);
static int check_ge2(CHKARGS);
static int check_le5(CHKARGS);
static int check_head1(CHKARGS);
static int check_par(CHKARGS);
static int check_part(CHKARGS);
static int check_root(CHKARGS);
static void check_text(CHKARGS);
static int check_text(CHKARGS);
static int post_AT(CHKARGS);
static int post_IP(CHKARGS);
@ -61,174 +55,122 @@ static int post_vs(CHKARGS);
static int post_fi(CHKARGS);
static int post_ft(CHKARGS);
static int post_nf(CHKARGS);
static int post_sec(CHKARGS);
static int post_TH(CHKARGS);
static int post_UC(CHKARGS);
static int pre_sec(CHKARGS);
static int post_UR(CHKARGS);
static v_check posts_at[] = { post_AT, NULL };
static v_check posts_br[] = { post_vs, check_eq0, NULL };
static v_check posts_eq0[] = { check_eq0, NULL };
static v_check posts_eq2[] = { check_eq2, NULL };
static v_check posts_fi[] = { check_eq0, post_fi, NULL };
static v_check posts_ft[] = { post_ft, NULL };
static v_check posts_ip[] = { post_IP, NULL };
static v_check posts_le1[] = { check_le1, NULL };
static v_check posts_nf[] = { check_eq0, post_nf, NULL };
static v_check posts_par[] = { check_par, NULL };
static v_check posts_part[] = { check_part, NULL };
static v_check posts_sec[] = { post_sec, NULL };
static v_check posts_sp[] = { post_vs, check_le1, NULL };
static v_check posts_th[] = { check_ge2, check_le5, post_TH, NULL };
static v_check posts_uc[] = { post_UC, NULL };
static v_check posts_ur[] = { check_head1, check_part, NULL };
static v_check pres_sec[] = { pre_sec, NULL };
static const struct man_valid man_valids[MAN_MAX] = {
{ NULL, posts_br }, /* br */
{ NULL, posts_th }, /* TH */
{ pres_sec, posts_sec }, /* SH */
{ pres_sec, posts_sec }, /* SS */
{ NULL, NULL }, /* TP */
{ NULL, posts_par }, /* LP */
{ NULL, posts_par }, /* PP */
{ NULL, posts_par }, /* P */
{ NULL, posts_ip }, /* IP */
{ NULL, NULL }, /* HP */
{ NULL, NULL }, /* SM */
{ NULL, NULL }, /* SB */
{ NULL, NULL }, /* BI */
{ NULL, NULL }, /* IB */
{ NULL, NULL }, /* BR */
{ NULL, NULL }, /* RB */
{ NULL, NULL }, /* R */
{ NULL, NULL }, /* B */
{ NULL, NULL }, /* I */
{ NULL, NULL }, /* IR */
{ NULL, NULL }, /* RI */
{ NULL, posts_eq0 }, /* na */
{ NULL, posts_sp }, /* sp */
{ NULL, posts_nf }, /* nf */
{ NULL, posts_fi }, /* fi */
{ NULL, NULL }, /* RE */
{ NULL, posts_part }, /* RS */
{ NULL, NULL }, /* DT */
{ NULL, posts_uc }, /* UC */
{ NULL, posts_le1 }, /* PD */
{ NULL, posts_at }, /* AT */
{ NULL, NULL }, /* in */
{ NULL, posts_ft }, /* ft */
{ NULL, posts_eq2 }, /* OP */
{ NULL, posts_nf }, /* EX */
{ NULL, posts_fi }, /* EE */
{ NULL, posts_ur }, /* UR */
{ NULL, NULL }, /* UE */
static v_check man_valids[MAN_MAX] = {
post_vs, /* br */
post_TH, /* TH */
NULL, /* SH */
NULL, /* SS */
NULL, /* TP */
check_par, /* LP */
check_par, /* PP */
check_par, /* P */
post_IP, /* IP */
NULL, /* HP */
NULL, /* SM */
NULL, /* SB */
NULL, /* BI */
NULL, /* IB */
NULL, /* BR */
NULL, /* RB */
NULL, /* R */
NULL, /* B */
NULL, /* I */
NULL, /* IR */
NULL, /* RI */
check_eq0, /* na */
post_vs, /* sp */
post_nf, /* nf */
post_fi, /* fi */
NULL, /* RE */
check_part, /* RS */
NULL, /* DT */
post_UC, /* UC */
check_le1, /* PD */
post_AT, /* AT */
NULL, /* in */
post_ft, /* ft */
check_eq2, /* OP */
post_nf, /* EX */
post_fi, /* EE */
post_UR, /* UR */
NULL, /* UE */
NULL, /* ll */
};
int
man_valid_pre(struct man *man, struct man_node *n)
{
v_check *cp;
switch (n->type) {
case (MAN_TEXT):
/* FALLTHROUGH */
case (MAN_ROOT):
/* FALLTHROUGH */
case (MAN_EQN):
/* FALLTHROUGH */
case (MAN_TBL):
return(1);
default:
break;
}
if (NULL == (cp = man_valids[n->tok].pres))
return(1);
for ( ; *cp; cp++)
if ( ! (*cp)(man, n))
return(0);
return(1);
}
int
man_valid_post(struct man *man)
{
struct man_node *n;
v_check *cp;
if (MAN_VALID & man->last->flags)
n = man->last;
if (n->flags & MAN_VALID)
return(1);
man->last->flags |= MAN_VALID;
n->flags |= MAN_VALID;
switch (man->last->type) {
case (MAN_TEXT):
check_text(man, man->last);
return(1);
case (MAN_ROOT):
return(check_root(man, man->last));
case (MAN_EQN):
switch (n->type) {
case MAN_TEXT:
return(check_text(man, n));
case MAN_ROOT:
return(check_root(man, n));
case MAN_EQN:
/* FALLTHROUGH */
case (MAN_TBL):
case MAN_TBL:
return(1);
default:
break;
cp = man_valids + n->tok;
return(*cp ? (*cp)(man, n) : 1);
}
if (NULL == (cp = man_valids[man->last->tok].posts))
return(1);
for ( ; *cp; cp++)
if ( ! (*cp)(man, man->last))
return(0);
return(1);
}
static int
check_root(CHKARGS)
check_root(CHKARGS)
{
if (MAN_BLINE & man->flags)
man_nmsg(man, n, MANDOCERR_SCOPEEXIT);
else if (MAN_ELINE & man->flags)
man_nmsg(man, n, MANDOCERR_SCOPEEXIT);
assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0);
man->flags &= ~MAN_BLINE;
man->flags &= ~MAN_ELINE;
if (NULL == man->first->child)
mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse,
n->line, n->pos, NULL);
else
man->meta.hasbody = 1;
if (NULL == man->first->child) {
man_nmsg(man, n, MANDOCERR_NODOCBODY);
return(0);
} else if (NULL == man->meta.title) {
man_nmsg(man, n, MANDOCERR_NOTITLE);
if (NULL == man->meta.title) {
mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
n->line, n->pos, NULL);
/*
* If a title hasn't been set, do so now (by
* implication, date and section also aren't set).
*/
man->meta.title = mandoc_strdup("unknown");
man->meta.msec = mandoc_strdup("1");
man->meta.date = mandoc_normdate
(man->parse, NULL, n->line, n->pos);
man->meta.title = mandoc_strdup("");
man->meta.msec = mandoc_strdup("");
man->meta.date = man->quick ? mandoc_strdup("") :
mandoc_normdate(man->parse, NULL, n->line, n->pos);
}
return(1);
}
static void
static int
check_text(CHKARGS)
{
char *cp, *p;
if (MAN_LITERAL & man->flags)
return;
return(1);
cp = n->string;
for (p = cp; NULL != (p = strchr(p, '\t')); p++)
man_pmsg(man, n->line, (int)(p - cp), MANDOCERR_BADTAB);
mandoc_msg(MANDOCERR_FI_TAB, man->parse,
n->line, n->pos + (p - cp), NULL);
return(1);
}
#define INEQ_DEFINE(x, ineq, name) \
@ -238,26 +180,25 @@ check_##name(CHKARGS) \
if (n->nchild ineq (x)) \
return(1); \
mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line, n->pos, \
"line arguments %s %d (have %d)", \
#ineq, (x), n->nchild); \
"line arguments %s %d (have %d)", \
#ineq, (x), n->nchild); \
return(1); \
}
INEQ_DEFINE(0, ==, eq0)
INEQ_DEFINE(2, ==, eq2)
INEQ_DEFINE(1, <=, le1)
INEQ_DEFINE(2, >=, ge2)
INEQ_DEFINE(5, <=, le5)
static int
check_head1(CHKARGS)
post_UR(CHKARGS)
{
if (MAN_HEAD == n->type && 1 != n->nchild)
mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line,
n->pos, "line arguments eq 1 (have %d)", n->nchild);
return(1);
return(check_part(man, n));
}
static int
@ -272,27 +213,27 @@ post_ft(CHKARGS)
ok = 0;
cp = n->child->string;
switch (*cp) {
case ('1'):
case '1':
/* FALLTHROUGH */
case ('2'):
case '2':
/* FALLTHROUGH */
case ('3'):
case '3':
/* FALLTHROUGH */
case ('4'):
case '4':
/* FALLTHROUGH */
case ('I'):
case 'I':
/* FALLTHROUGH */
case ('P'):
case 'P':
/* FALLTHROUGH */
case ('R'):
case 'R':
if ('\0' == cp[1])
ok = 1;
break;
case ('B'):
case 'B':
if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
ok = 1;
break;
case ('C'):
case 'C':
if ('W' == cp[1] && '\0' == cp[2])
ok = 1;
break;
@ -301,69 +242,51 @@ post_ft(CHKARGS)
}
if (0 == ok) {
mandoc_vmsg
(MANDOCERR_BADFONT, man->parse,
n->line, n->pos, "%s", cp);
mandoc_vmsg(MANDOCERR_FT_BAD, man->parse,
n->line, n->pos, "ft %s", cp);
*cp = '\0';
}
if (1 < n->nchild)
mandoc_vmsg
(MANDOCERR_ARGCOUNT, man->parse, n->line,
n->pos, "want one child (have %d)",
n->nchild);
mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line,
n->pos, "want one child (have %d)", n->nchild);
return(1);
}
static int
pre_sec(CHKARGS)
{
if (MAN_BLOCK == n->type)
man->flags &= ~MAN_LITERAL;
return(1);
}
static int
post_sec(CHKARGS)
{
if ( ! (MAN_HEAD == n->type && 0 == n->nchild))
return(1);
man_nmsg(man, n, MANDOCERR_SYNTARGCOUNT);
return(0);
}
static int
check_part(CHKARGS)
{
if (MAN_BODY == n->type && 0 == n->nchild)
mandoc_msg(MANDOCERR_ARGCWARN, man->parse, n->line,
n->pos, "want children (have none)");
mandoc_msg(MANDOCERR_ARGCWARN, man->parse, n->line,
n->pos, "want children (have none)");
return(1);
}
static int
check_par(CHKARGS)
{
switch (n->type) {
case (MAN_BLOCK):
case MAN_BLOCK:
if (0 == n->body->nchild)
man_node_delete(man, n);
break;
case (MAN_BODY):
case MAN_BODY:
if (0 == n->nchild)
man_nmsg(man, n, MANDOCERR_IGNPAR);
mandoc_vmsg(MANDOCERR_PAR_SKIP,
man->parse, n->line, n->pos,
"%s empty", man_macronames[n->tok]);
break;
case (MAN_HEAD):
case MAN_HEAD:
if (n->nchild)
man_nmsg(man, n, MANDOCERR_ARGSLOST);
mandoc_vmsg(MANDOCERR_ARG_SKIP,
man->parse, n->line, n->pos,
"%s %s%s", man_macronames[n->tok],
n->child->string,
n->nchild > 1 ? " ..." : "");
break;
default:
break;
@ -377,13 +300,15 @@ post_IP(CHKARGS)
{
switch (n->type) {
case (MAN_BLOCK):
case MAN_BLOCK:
if (0 == n->head->nchild && 0 == n->body->nchild)
man_node_delete(man, n);
break;
case (MAN_BODY):
case MAN_BODY:
if (0 == n->parent->head->nchild && 0 == n->nchild)
man_nmsg(man, n, MANDOCERR_IGNPAR);
mandoc_vmsg(MANDOCERR_PAR_SKIP,
man->parse, n->line, n->pos,
"%s empty", man_macronames[n->tok]);
break;
default:
break;
@ -394,8 +319,10 @@ post_IP(CHKARGS)
static int
post_TH(CHKARGS)
{
struct man_node *nb;
const char *p;
int line, pos;
check_le5(man, n);
free(man->meta.title);
free(man->meta.vol);
@ -403,10 +330,10 @@ post_TH(CHKARGS)
free(man->meta.msec);
free(man->meta.date);
line = n->line;
pos = n->pos;
man->meta.title = man->meta.vol = man->meta.date =
man->meta.msec = man->meta.source = NULL;
man->meta.msec = man->meta.source = NULL;
nb = n;
/* ->TITLE<- MSEC DATE SOURCE VOL */
@ -414,15 +341,21 @@ post_TH(CHKARGS)
if (n && n->string) {
for (p = n->string; '\0' != *p; p++) {
/* Only warn about this once... */
if (isalpha((unsigned char)*p) &&
! isupper((unsigned char)*p)) {
man_nmsg(man, n, MANDOCERR_UPPERCASE);
if (isalpha((unsigned char)*p) &&
! isupper((unsigned char)*p)) {
mandoc_vmsg(MANDOCERR_TITLE_CASE,
man->parse, n->line,
n->pos + (p - n->string),
"TH %s", n->string);
break;
}
}
man->meta.title = mandoc_strdup(n->string);
} else
} else {
man->meta.title = mandoc_strdup("");
mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
nb->line, nb->pos, "TH");
}
/* TITLE ->MSEC<- DATE SOURCE VOL */
@ -430,19 +363,27 @@ post_TH(CHKARGS)
n = n->next;
if (n && n->string)
man->meta.msec = mandoc_strdup(n->string);
else
else {
man->meta.msec = mandoc_strdup("");
mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse,
nb->line, nb->pos, "TH %s", man->meta.title);
}
/* TITLE MSEC ->DATE<- SOURCE VOL */
if (n)
n = n->next;
if (n && n->string && '\0' != n->string[0]) {
pos = n->pos;
man->meta.date = mandoc_normdate
(man->parse, n->string, line, pos);
} else
man->meta.date = man->quick ?
mandoc_strdup(n->string) :
mandoc_normdate(man->parse, n->string,
n->line, n->pos);
} else {
man->meta.date = mandoc_strdup("");
mandoc_msg(MANDOCERR_DATE_MISSING, man->parse,
n ? n->line : nb->line,
n ? n->pos : nb->pos, "TH");
}
/* TITLE MSEC DATE ->SOURCE<- VOL */
@ -470,8 +411,11 @@ static int
post_nf(CHKARGS)
{
check_eq0(man, n);
if (MAN_LITERAL & man->flags)
man_nmsg(man, n, MANDOCERR_SCOPEREP);
mandoc_msg(MANDOCERR_NF_SKIP, man->parse,
n->line, n->pos, "nf");
man->flags |= MAN_LITERAL;
return(1);
@ -481,8 +425,11 @@ static int
post_fi(CHKARGS)
{
check_eq0(man, n);
if ( ! (MAN_LITERAL & man->flags))
man_nmsg(man, n, MANDOCERR_WNOSCOPE);
mandoc_msg(MANDOCERR_FI_SKIP, man->parse,
n->line, n->pos, "fi");
man->flags &= ~MAN_LITERAL;
return(1);
@ -568,17 +515,24 @@ static int
post_vs(CHKARGS)
{
if (n->tok == MAN_br)
check_eq0(man, n);
else
check_le1(man, n);
if (NULL != n->prev)
return(1);
switch (n->parent->tok) {
case (MAN_SH):
case MAN_SH:
/* FALLTHROUGH */
case (MAN_SS):
man_nmsg(man, n, MANDOCERR_IGNPAR);
case MAN_SS:
mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos,
"%s after %s", man_macronames[n->tok],
man_macronames[n->parent->tok]);
/* FALLTHROUGH */
case (MAN_MAX):
/*
case MAN_MAX:
/*
* Don't warn about this because it occurs in pod2man
* and would cause considerable (unfixable) warnage.
*/

875
mandoc.1
View File

@ -1,7 +1,7 @@
.\" $Id: mandoc.1,v 1.103 2013/07/13 19:41:16 schwarze Exp $
.\" $Id: mandoc.1,v 1.106 2014/08/08 01:50:59 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2012 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2012, 2014 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: July 13 2013 $
.Dd $Mdocdate: August 8 2014 $
.Dt MANDOC 1
.Os
.Sh NAME
@ -496,30 +496,28 @@ parser:
.Pp
.Dl $ mandoc \-Tman foo.mdoc \*(Gt foo.man
.Sh DIAGNOSTICS
Standard error messages reporting parsing errors are prefixed by
Messages displayed by
.Nm
follow this format:
.Pp
.Sm off
.D1 Ar file : line : column : \ level :
.Sm on
.D1 Nm Ns : Ar file : Ns Ar line : Ns Ar column : level : message : macro args
.Pp
where the fields have the following meanings:
.Bl -tag -width "column"
.It Ar file
The name of the input file causing the message.
.It Ar line
The line number in that input file.
Line numbering starts at 1.
.It Ar column
The column number in that input file.
Column numbering starts at 1.
If the issue is caused by a word, the column number usually
points to the first character of the word.
.It Ar level
The message level, printed in capital letters.
.El
Line and column numbers start at 1.
Both are omitted for messages referring to an input file as a whole.
Macro names and arguments are omitted where meaningless.
Fatal messages about invalid command line arguments
or operating system errors, for example when memory is exhausted,
may also omit the
.Ar file
and
.Ar level
fields.
.Pp
Message levels have the following meanings:
.Bl -tag -width "warning"
.It Cm syserr
Opening or reading an input file failed, so the parser cannot
even be started and no output is produced from that input file.
.It Cm fatal
The parser is unable to parse a given input file at all.
No formatted output is produced from that input file.
@ -551,13 +549,836 @@ levels are hidden unless their level, or a lower level, is requested using a
option or
.Fl T Ns Cm lint
output mode.
.Pp
.Ss Warnings related to the document prologue
.Bl -ohang
.It Sy "missing manual title, using UNTITLED"
.Pq mdoc
A
.Ic \&Dt
macro has no arguments, or there is no
.Ic \&Dt
macro before the first non-prologue macro.
.It Sy "missing manual title, using \(dq\(dq"
.Pq man
There is no
.Ic \&TH
macro, or it has no arguments.
.It Sy "lower case character in document title"
.Pq mdoc , man
The title is still used as given in the
.Ic \&Dt
or
.Ic \&TH
macro.
.It Sy "missing manual section, using \(dq\(dq"
.Pq mdoc , man
A
.Ic \&Dt
or
.Ic \&TH
macro lacks the mandatory section argument.
.It Sy "unknown manual section"
.Pq mdoc
The section number in a
.Ic \&Dt
line is invalid, but still used.
.It Sy "unknown manual volume or arch"
.Pq mdoc
The volume name in a
.Ic \&Dt
line is invalid, but still used.
The manual is assumed to be architecture-independent.
.It Sy "missing date, using today's date"
.Pq mdoc, man
The document was parsed as
.Xr mdoc 7
and it has no
.Ic \&Dd
macro, or the
.Ic \&Dd
macro has no arguments or only empty arguments;
or the document was parsed as
.Xr man 7
and it has no
.Ic \&TH
macro, or the
.Ic \&TH
macro has less than three arguments or its third argument is empty.
.It Sy "cannot parse date, using it verbatim"
.Pq mdoc , man
The date given in a
.Ic \&Dd
or
.Ic \&TH
macro does not follow the conventional format.
.It Sy "missing Os macro, using \(dq\(dq"
.Pq mdoc
The default or current system is not shown in this case.
.It Sy "duplicate prologue macro"
.Pq mdoc
One of the prologue macros occurs more than once.
The last instance overrides all previous ones.
.It Sy "late prologue macro"
.Pq mdoc
A
.Ic \&Dd
or
.Ic \&Os
macro occurs after some non-prologue macro, but still takes effect.
.It Sy "skipping late title macro"
.Pq mdoc
The
.Ic \&Dt
macro can only occur before the first non-prologue macro
because traditional formatters write the page header
before parsing the document body.
Even though this technical restriction does not apply to
.Nm ,
traditional semantics is preserved.
The late macro is discarded including its arguments.
.It Sy "prologue macros out of order"
.Pq mdoc
The prologue macros are not given in the conventional order
.Ic \&Dd ,
.Ic \&Dt ,
.Ic \&Os .
All three macros are used even when given in another order.
.El
.Ss Warnings regarding document structure
.Bl -ohang
.It Sy ".so is fragile, better use ln(1)"
.Pq roff
Including files only works when the parser program runs with the correct
current working directory.
.It Sy "no document body"
.Pq mdoc , man
The document body contains neither text nor macros.
An empty document is shown, consisting only of a header and a footer line.
.It Sy "content before first section header"
.Pq mdoc , man
Some macros or text precede the first
.Ic \&Sh
or
.Ic \&SH
section header.
The offending macros and text are parsed and added to the top level
of the syntax tree, outside any section block.
.It Sy "first section is not NAME"
.Pq mdoc
The argument of the first
.Ic \&Sh
macro is not
.Sq NAME .
This may confuse
.Xr makewhatis 8
and
.Xr apropos 1 .
.It Sy "bad NAME section contents"
.Pq mdoc
The last node in the NAME section is not an
.Ic \&Nd
macro, or any preceding macro is not
.Ic \&Nm ,
or the NAME section is completely empty.
This may confuse
.Xr makewhatis 8
and
.Xr apropos 1 .
.It Sy "sections out of conventional order"
.Pq mdoc
A standard section occurs after another section it usually precedes.
All section titles are used as given,
and the order of sections is not changed.
.It Sy "duplicate section title"
.Pq mdoc
The same standard section title occurs more than once.
.It Sy "unexpected section"
.Pq mdoc
A standard section header occurs in a section of the manual
where it normally isn't useful.
.El
.Ss "Warnings related to macros and nesting"
.Bl -ohang
.It Sy "obsolete macro"
.Pq mdoc
See the
.Xr mdoc 7
manual for replacements.
.It Sy "skipping paragraph macro"
In
.Xr mdoc 7
documents, this happens
.Bl -dash -compact
.It
at the beginning and end of sections and subsections
.It
right before non-compact lists and displays
.It
at the end of items in non-column, non-compact lists
.It
and for multiple consecutive paragraph macros.
.El
In
.Xr man 7
documents, it happens
.Bl -dash -compact
.It
for empty
.Ic \&P ,
.Ic \&PP ,
and
.Ic \&LP
macros
.It
for
.Ic \&IP
macros having neither head nor body arguments
.It
for
.Ic \&br
or
.Ic \&sp
right after
.Ic \&SH
or
.Ic \&SS
.El
.It Sy "moving paragraph macro out of list"
.Pq mdoc
A list item in a
.Ic \&Bl
list contains a trailing paragraph macro.
The paragraph macro is moved after the end of the list.
.It Sy "skipping no-space macro"
.Pq mdoc
An input line begins with an
.Ic \&Ns
macro.
The macro is ignored.
.It Sy "blocks badly nested"
.Pq mdoc
If two blocks intersect, one should completely contain the other.
Otherwise, rendered output is likely to look strange in any output
format, and rendering in SGML-based output formats is likely to be
outright wrong because such languages do not support badly nested
blocks at all.
Typical examples of badly nested blocks are
.Qq Ic \&Ao \&Bo \&Ac \&Bc
and
.Qq Ic \&Ao \&Bq \&Ac .
In these examples,
.Ic \&Ac
breaks
.Ic \&Bo
and
.Ic \&Bq ,
respectively.
.It Sy "nested displays are not portable"
.Pq mdoc
A
.Ic \&Bd ,
.Ic \&D1 ,
or
.Ic \&Dl
display occurs nested inside another
.Ic \&Bd
display.
This works with
.Nm ,
but fails with most other implementations.
.It Sy "moving content out of list"
.Pq mdoc
A
.Ic \&Bl
list block contains text or macros before the first
.Ic \&It
macro.
The offending children are moved before the beginning of the list.
.It Sy ".Vt block has child macro"
.Pq mdoc
The
.Ic \&Vt
macro supports plain text arguments only.
Formatting may be ugly and semantic searching
for the affected content might not work.
.It Sy "fill mode already enabled, skipping"
.Pq man
A
.Ic \&fi
request occurs even though the document is still in fill mode,
or already switched back to fill mode.
It has no effect.
.It Sy "fill mode already disabled, skipping"
.Pq man
An
.Ic \&nf
request occurs even though the document already switched to no-fill mode
and did not switch back to fill mode yet.
It has no effect.
.It Sy "line scope broken"
.Pq man
While parsing the next-line scope of the previous macro,
another macro is found that prematurely terminates the previous one.
The previous, interrupted macro is deleted from the parse tree.
.El
.Ss "Warnings related to missing arguments"
.Bl -ohang
.It Sy "skipping empty request"
.Pq roff
The macro name is missing from a macro definition request.
.It Sy "conditional request controls empty scope"
.Pq roff
A conditional request is only useful if any of the following
follows it on the same logical input line:
.Bl -dash -compact
.It
The
.Sq \e{
keyword to open a multi-line scope.
.It
A request or macro or some text, resulting in a single-line scope.
.It
The immediate end of the logical line without any intervening whitespace,
resulting in next-line scope.
.El
Here, a conditional request is followed by trailing whitespace only,
and there is no other content on its logical input line.
Note that it doesn't matter whether the logical input line is split
across multiple physical input lines using
.Sq \e
line continuation characters.
This is one of the rare cases
where trailing whitespace is syntactically significant.
The conditional request controls a scope containing whitespace only,
so it is unlikely to have a significant effect,
except that it may control a following
.Ic \&el
clause.
.It Sy "skipping empty macro"
.Pq mdoc
The indicated macro has no arguments and hence no effect.
.It Sy "empty argument, using 0n"
.Pq mdoc
The required width is missing after
.Ic \&Bd
or
.Ic \&Bl
.Fl offset
or
.Fl width.
.It Sy "argument count wrong"
.Pq mdoc , man
The indicated macro has too few or too many arguments.
The syntax tree will contain the wrong number of arguments as given.
Formatting behaviour depends on the specific macro in question.
Note that the same message may also occur as an ERROR, see below.
.It Sy "missing display type, using -ragged"
.Pq mdoc
The
.Ic \&Bd
macro is invoked without the required display type.
.It Sy "list type is not the first argument"
.Pq mdoc
In a
.Ic \&Bl
macro, at least one other argument precedes the type argument.
The
.Nm
utility may also print messages related to invalid command line arguments
or operating system errors, for example when memory is exhausted or
input files cannot be read.
Such messages do not carry the prefix described above.
utility copes with any argument order, but some other
.Xr mdoc 7
implementations do not.
.It Sy "missing -width in -tag list, using 8n"
.Pq mdoc
Every
.Ic \&Bl
macro having the
.Fl tag
argument requires
.Fl width ,
too.
.It Sy "missing utility name, using \(dq\(dq"
.Pq mdoc
The
.Ic \&Ex Fl std
macro is called without an argument before
.Ic \&Nm
has first been called with an argument.
.It Sy "empty head in list item"
.Pq mdoc
In a
.Ic \&Bl
.Fl diag ,
.Fl hang ,
.Fl inset ,
.Fl ohang ,
or
.Fl tag
list, an
.Ic \&It
macro lacks the required argument.
The item head is left empty.
.It Sy "empty list item"
.Pq mdoc
In a
.Ic \&Bl
.Fl bullet ,
.Fl dash ,
.Fl enum ,
or
.Fl hyphen
list, an
.Ic \&It
block is empty.
An empty list item is shown.
.It Sy "missing font type"
.Pq mdoc
A
.Ic \&Bf
macro has no argument.
It switches to the default font,
.Cm \efR .
.It Sy "unknown font type"
.Pq mdoc
The
.Ic \&Bf
argument is invalid.
The default font
.Cm \efR
is used instead.
.It Sy "missing -std argument, adding it"
.Pq mdoc
An
.Ic \&Ex
or
.Ic \&Rv
macro lacks the required
.Fl std
argument.
The
.Nm
utility assumes
.Fl std
even when it is not specified, but other implementations may not.
.El
.Ss "Warnings related to bad macro arguments"
.Bl -ohang
.It Sy "unterminated quoted argument"
.Pq roff
Macro arguments can be enclosed in double quote characters
such that space characters and macro names contained in the quoted
argument need not be escaped.
The closing quote of the last argument of a macro can be omitted.
However, omitting it is not recommended because it makes the code
harder to read.
.It Sy "duplicate argument"
.Pq mdoc
A
.Ic \&Bd
or
.Ic \&Bl
macro has more than one
.Fl compact ,
more than one
.Fl offset ,
or more than one
.Fl width
argument.
All but the last instances of these arguments are ignored.
.It Sy "skipping duplicate argument"
.Pq mdoc
An
.Ic \&An
macro has more than one
.Fl split
or
.Fl nosplit
argument.
All but the first of these arguments are ignored.
.It Sy "skipping duplicate display type"
.Pq mdoc
A
.Ic \&Bd
macro has more than one type argument; the first one is used.
.It Sy "skipping duplicate list type"
.Pq mdoc
A
.Ic \&Bl
macro has more than one type argument; the first one is used.
.It Sy "skipping -width argument"
.Pq mdoc
A
.Ic \&Bl
.Fl column ,
.Fl diag ,
.Fl ohang ,
.Fl inset ,
or
.Fl item
list has a
.Fl width
argument.
That has no effect.
.It Sy "unknown AT&T UNIX version"
.Pq mdoc
An
.Ic \&At
macro has an invalid argument.
It is used verbatim, with
.Qq "AT&T UNIX "
prefixed to it.
.It Sy "invalid content in Rs block"
.Pq mdoc
An
.Ic \&Rs
block contains plain text or non-% macros.
The bogus content is left in the syntax tree.
Formatting may be poor.
.It Sy "invalid Boolean argument"
.Pq mdoc
An
.Ic \&Sm
macro has an argument other than
.Cm on
or
.Cm off .
The invalid argument is moved out of the macro, which leaves the macro
empty, causing it to toggle the spacing mode.
.It Sy "unknown font, skipping request"
.Pq man
A
.Xr roff 7
.Ic \&ft
request has an invalid argument.
.El
.Ss "Warnings related to plain text"
.Bl -ohang
.It Sy "blank line in fill mode, using .sp"
.Pq mdoc
The meaning of blank input lines is only well-defined in non-fill mode:
In fill mode, line breaks of text input lines are not supposed to be
significant.
However, for compatibility with groff, blank lines in fill mode
are replaced with
.Ic \&sp
requests.
.It Sy "tab in filled text"
.Pq mdoc , man
The meaning of tab characters is only well-defined in non-fill mode:
In fill mode, whitespace is not supposed to be significant
on text input lines.
As an implementation dependent choice, tab characters on text lines
are passed through to the formatters in any case.
Given that the text before the tab character will be filled,
it is hard to predict which tab stop position the tab will advance to.
.It Sy "whitespace at end of input line"
.Pq mdoc , man , roff
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 "bad comment style"
.Pq roff
Comment lines start with a dot, a backslash, and a double-quote character.
The
.Nm
utility treats the line as a comment line even without the backslash,
but leaving out the backslash might not be portable.
.It Sy "invalid escape sequence"
.Pq roff
An escape sequence has an invalid opening argument delimiter, lacks the
closing argument delimiter, or the argument has too few characters.
If the argument is incomplete,
.Ic \e*
and
.Ic \en
expand to an empty string,
.Ic \eB
to the digit
.Sq 0 ,
and
.Ic \ew
to the length of the incomplete argument.
All other invalid escape sequences are ignored.
.It Sy "undefined string, using \(dq\(dq"
.Pq roff
If a string is used without being defined before,
its value is implicitly set to the empty string.
However, defining strings explicitly before use
keeps the code more readable.
.El
.Ss "Errors related to equations"
.Bl -inset -compact
.It "unexpected equation scope closure"
.It "equation scope open on exit"
.It "overlapping equation scopes"
.It "unexpected end of equation"
.It "equation syntax error"
.El
.Ss "Errors related to tables"
.Bl -inset -compact
.It "bad table syntax"
.It "bad table option"
.It "bad table layout"
.It "no table layout cells specified"
.It "no table data cells specified"
.It "ignore data in cell"
.It "data block still open"
.It "ignoring extra data cells"
.El
.Ss "Errors related to roff, mdoc, and man code"
.Bl -ohang
.It Sy "input stack limit exceeded, infinite loop?"
.Pq roff
Explicit recursion limits are implemented for the following features,
in order to prevent infinite loops:
.Bl -dash -compact
.It
expansion of nested escape sequences
including expansion of strings and number registers,
.It
expansion of nested user-defined macros,
.It
and
.Ic \&so
file inclusion.
.El
When a limit is hit, the output is incorrect, typically losing
some content, but the parser can continue.
.It Sy "skipping bad character"
.Pq mdoc , man , roff
The input file contains a byte that is not a printable
.Xr ascii 7
character.
The message mentions the character number.
The offending byte is replaced with a question mark
.Pq Sq \&? .
Consider editing the input file to replace the byte with an ASCII
transliteration of the intended character.
.It Sy "skipping unknown macro"
.Pq mdoc , man , roff
The first identifier on a request or macro line is neither recognized as a
.Xr roff 7
request, nor as a user-defined macro, nor, respectively, as an
.Xr mdoc 7
or
.Xr man 7
macro.
It may be mistyped or unsupported.
The request or macro is discarded including its arguments.
.It Sy "skipping item outside list"
.Pq mdoc
An
.Ic \&It
macro occurs outside any
.Ic \&Bl
list.
It is discarded including its arguments.
.It Sy "skipping column outside column list"
.Pq mdoc
A
.Ic \&Ta
macro occurs outside any
.Ic \&Bl Fl column
block.
It is discarded including its arguments.
.It Sy "skipping end of block that is not open"
.Pq mdoc , man , eqn , tbl , roff
Various syntax elements can only be used to explicitly close blocks
that have previously been opened.
An
.Xr mdoc 7
block closing macro, a
.Xr man 7
.Ic \&RE
or
.Ic \&UE
macro, or the end of an equation, table, or
.Xr roff 7
conditional request is encountered but no matching block is open.
The offending request or macro is discarded.
.It Sy "inserting missing end of block"
.Pq mdoc , tbl
Various
.Xr mdoc 7
macros as well as tables require explicit closing by dedicated macros.
A block that doesn't support bad nesting
ends before all of its children are properly closed.
The open child nodes are closed implicitly.
.It Sy "scope open on exit"
.Pq mdoc , man , eqn , tbl , roff
At the end of the document, an explicit
.Xr mdoc 7
block, a
.Xr man 7
next-line scope or
.Ic \&RS
or
.Ic \&UR
block, an equation, table, or
.Xr roff 7
conditional or ignore block is still open.
The open block is closed implicitly.
.It Sy "escaped character not allowed in a name"
.Pq roff
Macro, string and register identifiers consist of printable,
non-whitespace ASCII characters.
Escape sequences and characters and strings expressed in terms of them
cannot form part of a name.
The first argument of an
.Ic \&am ,
.Ic \&as ,
.Ic \&de ,
.Ic \&ds ,
.Ic \&nr ,
or
.Ic \&rr
request, or any argument of an
.Ic \&rm
request, or the name of a request or user defined macro being called,
is terminated by an escape sequence.
In the cases of
.Ic \&as ,
.Ic \&ds ,
and
.Ic \&nr ,
the request has no effect at all.
In the cases of
.Ic \&am ,
.Ic \&de ,
.Ic \&rr ,
and
.Ic \&rm ,
what was parsed up to this point is used as the arguments to the request,
and the rest of the input line is discarded including the escape sequence.
When parsing for a request or a user-defined macro name to be called,
only the escape sequence is discarded.
The characters preceding it are used as the request or macro name,
the characters following it are used as the arguments to the request or macro.
.It Sy "argument count wrong"
.Pq mdoc , man , roff
The indicated request or macro has too few or too many arguments.
The syntax tree will contain the wrong number of arguments as given.
Formatting behaviour depends on the specific request or macro in question.
Note that the same message may also occur as a WARNING, see above.
.It Sy "missing list type, using -item"
.Pq mdoc
A
.Ic \&Bl
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.
.It Sy "uname(3) system call failed, using UNKNOWN"
.Pq mdoc
The
.Ic \&Os
macro is called without arguments, and the
.Xr uname 3
system call failed.
As a workaround,
.Nm
can be compiled with
.Sm off
.Fl D Cm OSNAME=\(dq\e\(dq Ar string Cm \e\(dq\(dq .
.Sm on
.It Sy "unknown standard specifier"
.Pq mdoc
An
.Ic \&St
macro has an unknown argument and is discarded.
.It Sy "skipping request without numeric argument"
.Pq roff
An
.Ic \&it
request has a non-numeric or negative argument or no argument at all.
The invalid request is ignored.
.It Sy "skipping all arguments"
.Pq mdoc , man , eqn , roff
An
.Xr mdoc 7
.Ic \&Bt ,
.Ic \&Ed ,
.Ic \&Ef ,
.Ic \&Ek ,
.Ic \&El ,
.Ic \&Re ,
or
.Ic \&Ud
macro, an
.Ic \&It
macro in a list that don't support item heads, a
.Xr man 7
.Ic \&LP ,
.Ic \&P ,
or
.Ic \&PP
macro, an
.Xr eqn 7
.Ic \&EN
macro, or a
.Xr roff 7
.Sq \&..
block closing request is invoked with at least one argument.
All arguments are ignored.
.It Sy "skipping excess arguments"
.Pq mdoc , roff
The
.Ic \&Bf
macro is invoked with more than one argument, or a request of the
.Ic \&de
family is invoked with more than two arguments.
The excess arguments are ignored.
.El
.Ss FATAL errors
.Bl -ohang
.It Sy "input too large"
.Pq mdoc , man
Currently,
.Nm
cannot handle input files larger than its arbitrary size limit
of 2^31 bytes (2 Gigabytes).
Since useful manuals are always small, this is not a problem in practice.
Parsing is aborted as soon as the condition is detected.
.It Sy "NOT IMPLEMENTED: Bd -file"
.Pq mdoc
For security reasons, the
.Ic \&Bd
macro does not support the
.Fl file
argument.
By requesting the inclusion of a sensitive file, a malicious document
might otherwise trick a privileged user into inadvertently displaying
the file on the screen, revealing the file content to bystanders.
The parser exits immediately.
.It Sy "NOT IMPLEMENTED: .so with absolute path or \(dq..\(dq"
.Pq roff
For security reasons,
.Nm
allows
.Ic \&so
file inclusion requests only with relative paths
and only without ascending to any parent directory.
By requesting the inclusion of a sensitive file, a malicious document
might otherwise trick a privileged user into inadvertently displaying
the file on the screen, revealing the file content to bystanders.
The parser exits immediately.
.It Sy ".so request failed"
.Pq roff
Servicing a
.Ic \&so
request requires reading an external file.
While trying to do so, an
.Xr open 2 ,
.Xr stat 2 ,
or
.Xr read 2
system call failed.
The parser exits immediately.
Before showing this message,
.Nm
always shows another message explaining why the system call failed.
.El
.Sh COMPATIBILITY
This section summarises
.Nm

341
mandoc.3
View File

@ -1,4 +1,4 @@
.\" $Id: mandoc.3,v 1.22 2013/10/06 17:01:52 schwarze Exp $
.\" $Id: mandoc.3,v 1.25 2014/08/05 05:48:56 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
@ -15,21 +15,16 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: October 6 2013 $
.Dd $Mdocdate: August 5 2014 $
.Dt MANDOC 3
.Os
.Sh NAME
.Nm mandoc ,
.Nm mandoc_escape ,
.Nm man_deroff ,
.Nm man_meta ,
.Nm man_mparse ,
.Nm man_node ,
.Nm mchars_alloc ,
.Nm mchars_free ,
.Nm mchars_num2char ,
.Nm mchars_num2uc ,
.Nm mchars_spec2cp ,
.Nm mchars_spec2str ,
.Nm mdoc_deroff ,
.Nm mdoc_meta ,
.Nm mdoc_node ,
.Nm mparse_alloc ,
@ -45,68 +40,32 @@
.Sh LIBRARY
.Lb libmandoc
.Sh SYNOPSIS
.In man.h
.In mdoc.h
.In sys/types.h
.In mandoc.h
.Ft "enum mandoc_esc"
.Fo mandoc_escape
.Fa "const char const **end"
.Fa "const char const **start"
.Fa "int *sz"
.Fc
.Ft "const struct man_meta *"
.Fo man_meta
.Fa "const struct man *man"
.Fc
.Ft "const struct mparse *"
.Fo man_mparse
.Fa "const struct man *man"
.Fc
.Ft "const struct man_node *"
.Fo man_node
.Fa "const struct man *man"
.Fc
.Ft "struct mchars *"
.Fn mchars_alloc "void"
.Ft void
.Fn mchars_free "struct mchars *p"
.Ft char
.Fn mchars_num2char "const char *cp" "size_t sz"
.Ft int
.Fn mchars_num2uc "const char *cp" "size_t sz"
.Ft "const char *"
.Fo mchars_spec2str
.Fa "const struct mchars *p"
.Fa "const char *cp"
.Fa "size_t sz"
.Fa "size_t *rsz"
.Fc
.Ft int
.Fo mchars_spec2cp
.Fa "const struct mchars *p"
.Fa "const char *cp"
.Fa "size_t sz"
.Fc
.Ft "const struct mdoc_meta *"
.Fo mdoc_meta
.Fa "const struct mdoc *mdoc"
.Fc
.Ft "const struct mdoc_node *"
.Fo mdoc_node
.Fa "const struct mdoc *mdoc"
.Fc
.Ft void
.Fd "#define ASCII_NBRSP"
.Fd "#define ASCII_HYPH"
.Fd "#define ASCII_BREAK"
.Ft struct mparse *
.Fo mparse_alloc
.Fa "enum mparset type"
.Fa "int options"
.Fa "enum mandoclevel wlevel"
.Fa "mandocmsg msg"
.Fa "void *msgarg"
.Fa "mandocmsg mmsg"
.Fa "char *defos"
.Fc
.Ft void
.Fo (*mandocmsg)
.Fa "enum mandocerr errtype"
.Fa "enum mandoclevel level"
.Fa "const char *file"
.Fa "int line"
.Fa "int col"
.Fa "const char *msg"
.Fc
.Ft void
.Fo mparse_free
.Fa "struct mparse *parse"
.Fc
.Ft void
.Ft const char *
.Fo mparse_getkeep
.Fa "const struct mparse *parse"
.Fc
@ -129,6 +88,7 @@
.Fa "struct mparse *parse"
.Fa "struct mdoc **mdoc"
.Fa "struct man **man"
.Fa "char **sodest"
.Fc
.Ft "const char *"
.Fo mparse_strerror
@ -138,11 +98,45 @@
.Fo mparse_strlevel
.Fa "enum mandoclevel"
.Fc
.Vt extern const char * const * man_macronames;
.In sys/types.h
.In mandoc.h
.In mdoc.h
.Ft void
.Fo mdoc_deroff
.Fa "char **dest"
.Fa "const struct mdoc_node *node"
.Fc
.Ft "const struct mdoc_meta *"
.Fo mdoc_meta
.Fa "const struct mdoc *mdoc"
.Fc
.Ft "const struct mdoc_node *"
.Fo mdoc_node
.Fa "const struct mdoc *mdoc"
.Fc
.Vt extern const char * const * mdoc_argnames;
.Vt extern const char * const * mdoc_macronames;
.Fd "#define ASCII_NBRSP"
.Fd "#define ASCII_HYPH"
.In sys/types.h
.In mandoc.h
.In man.h
.Ft void
.Fo man_deroff
.Fa "char **dest"
.Fa "const struct man_node *node"
.Fc
.Ft "const struct man_meta *"
.Fo man_meta
.Fa "const struct man *man"
.Fc
.Ft "const struct mparse *"
.Fo man_mparse
.Fa "const struct man *man"
.Fc
.Ft "const struct man_node *"
.Fo man_node
.Fa "const struct man *man"
.Fc
.Vt extern const char * const * man_macronames;
.Sh DESCRIPTION
The
.Nm mandoc
@ -184,37 +178,22 @@ or invoke
.Fn mparse_reset
and parse new files.
.El
.Pp
The
.Nm
library also contains routines for translating character strings into glyphs
.Pq see Fn mchars_alloc
and parsing escape sequences from strings
.Pq see Fn mandoc_escape .
.Sh REFERENCE
This section documents the functions, types, and variables available
via
.In mandoc.h .
.In mandoc.h ,
with the exception of those documented in
.Xr mandoc_escape 3
and
.Xr mchars_alloc 3 .
.Ss Types
.Bl -ohang
.It Vt "enum mandoc_esc"
An escape sequence classification.
.It Vt "enum mandocerr"
A fatal error, error, or warning message during parsing.
.It Vt "enum mandoclevel"
A classification of an
.Vt "enum mandoclevel"
.Vt "enum mandocerr"
as regards system operation.
.It Vt "struct mchars"
An opaque pointer to an object allowing for translation between
character strings and glyphs.
See
.Fn mchars_alloc .
.It Vt "enum mparset"
The type of parser when reading input.
This should usually be
.Dv MPARSE_AUTO
for auto-detection.
.It Vt "struct mparse"
An opaque pointer to a running parse sequence.
Created with
@ -230,38 +209,20 @@ messages emitted by the parser.
.El
.Ss Functions
.Bl -ohang
.It Fn mandoc_escape
Scan an escape sequence, i.e., a character string beginning with
.Sq \e .
Pass a pointer to the character after the
.Sq \e
as
.Va end ;
it will be set to the supremum of the parsed escape sequence unless
returning
.Dv ESCAPE_ERROR ,
in which case the string is bogus and should be
thrown away.
If not
.Dv ESCAPE_ERROR
or
.Dv ESCAPE_IGNORE ,
.Va start
is set to the first relevant character of the substring (font, glyph,
whatever) of length
.Va sz .
Both
.Va start
and
.Va sz
may be
.Dv NULL .
Declared in
.In mandoc.h ,
implemented in
.Pa mandoc.c .
.It Fn man_deroff
Obtain a text-only representation of a
.Vt struct man_node ,
including text contained in its child nodes.
To be used on children of the pointer returned from
.Fn man_node .
When it is no longer needed, the pointer returned from
.Fn man_deroff
can be passed to
.Xr free 3 .
.It Fn man_meta
Obtain the meta-data of a successful parse.
Obtain the meta-data of a successful
.Xr man 7
parse.
This may only be used on a pointer returned by
.Fn mparse_result .
Declared in
@ -275,67 +236,29 @@ Declared in
implemented in
.Pa man.c .
.It Fn man_node
Obtain the root node of a successful parse.
Obtain the root node of a successful
.Xr man 7
parse.
This may only be used on a pointer returned by
.Fn mparse_result .
Declared in
.In man.h ,
implemented in
.Pa man.c .
.It Fn mchars_alloc
Allocate an
.Vt "struct mchars *"
object for translating special characters into glyphs.
See
.Xr mandoc_char 7
for an overview of special characters.
The object must be freed with
.Fn mchars_free .
Declared in
.In mandoc.h ,
implemented in
.Pa chars.c .
.It Fn mchars_free
Free an object created with
.Fn mchars_alloc .
Declared in
.In mandoc.h ,
implemented in
.Pa chars.c .
.It Fn mchars_num2char
Convert a character index (e.g., the \eN\(aq\(aq escape) into a
printable ASCII character.
Returns \e0 (the nil character) if the input sequence is malformed.
Declared in
.In mandoc.h ,
implemented in
.Pa chars.c .
.It Fn mchars_num2uc
Convert a hexadecimal character index (e.g., the \e[uNNNN] escape) into
a Unicode codepoint.
Returns \e0 (the nil character) if the input sequence is malformed.
Declared in
.In mandoc.h ,
implemented in
.Pa chars.c .
.It Fn mchars_spec2cp
Convert a special character into a valid Unicode codepoint.
Returns \-1 on failure or a non-zero Unicode codepoint on success.
Declared in
.In mandoc.h ,
implemented in
.Pa chars.c .
.It Fn mchars_spec2str
Convert a special character into an ASCII string.
Returns
.Dv NULL
on failure.
Declared in
.In mandoc.h ,
implemented in
.Pa chars.c .
.It Fn mdoc_deroff
Obtain a text-only representation of a
.Vt struct mdoc_node ,
including text contained in its child nodes.
To be used on children of the pointer returned from
.Fn mdoc_node .
When it is no longer needed, the pointer returned from
.Fn mdoc_deroff
can be passed to
.Xr free 3 .
.It Fn mdoc_meta
Obtain the meta-data of a successful parse.
Obtain the meta-data of a successful
.Xr mdoc
parse.
This may only be used on a pointer returned by
.Fn mparse_result .
Declared in
@ -343,7 +266,9 @@ Declared in
implemented in
.Pa mdoc.c .
.It Fn mdoc_node
Obtain the root node of a successful parse.
Obtain the root node of a successful
.Xr mdoc
parse.
This may only be used on a pointer returned by
.Fn mparse_result .
Declared in
@ -352,6 +277,57 @@ implemented in
.Pa mdoc.c .
.It Fn mparse_alloc
Allocate a parser.
The arguments have the following effect:
.Bl -tag -offset 5n -width inttype
.It Ar options
When the
.Dv MPARSE_MDOC
or
.Dv MPARSE_MAN
bit is set, only that parser is used.
Otherwise, the document type is automatically detected.
.Pp
When the
.Dv MPARSE_SO
bit is set,
.Xr roff 7
.Ic \&so
file inclusion requests are always honoured.
Otherwise, if the request is the only content in an input file,
only the file name is remembered, to be returned in the
.Fa sodest
argument of
.Fn mparse_result .
.Pp
When the
.Dv MPARSE_QUICK
bit is set, parsing is aborted after the NAME section.
This is for example useful in
.Xr makewhatis 8
.Fl Q
to quickly build minimal databases.
.It Ar wlevel
Can be set to
.Dv MANDOCLEVEL_FATAL ,
.Dv MANDOCLEVEL_ERROR ,
or
.Dv MANDOCLEVEL_WARNING .
Messages below the selected level will be suppressed.
.It Ar mmsg
A callback function to handle errors and warnings.
See
.Pa main.c
for an example.
.It Ar defos
A default string for the
.Xr mdoc 7
.Sq \&Os
macro, overriding the
.Dv OSNAME
preprocessor definition and the results of
.Xr uname 3 .
.El
.Pp
The same parser may be used for multiple files so long as
.Fn mparse_reset
is called between parses.
@ -419,7 +395,7 @@ i.e., those where
.Fn mparse_readfd
returned less than MANDOCLEVEL_FATAL
.Pc
should invoke this function, in which case one of the two pointers will
should invoke this function, in which case one of the three pointers will
be filled in.
Declared in
.In mandoc.h ,
@ -473,6 +449,8 @@ The following non-printing characters may be embedded in text strings:
A non-breaking space character.
.It Dv ASCII_HYPH
A soft hyphen.
.It Dv ASCII_BREAK
A breakable zero-width space.
.El
.Pp
Escape characters are also passed verbatim into text strings.
@ -480,11 +458,9 @@ An escape character is a sequence of characters beginning with the
backslash
.Pq Sq \e .
To construct human-readable text, these should be intercepted with
.Fn mandoc_escape
and converted with one of
.Fn mchars_num2char ,
.Fn mchars_spec2str ,
and so on.
.Xr mandoc_escape 3
and converted with one the functions described in
.Xr mchars_alloc 3 .
.Ss Man Abstract Syntax Tree
This AST is governed by the ontological rules dictated in
.Xr man 7
@ -529,7 +505,7 @@ where capitalised non-terminals represent nodes.
.El
.Pp
The only elements capable of nesting other elements are those with
next-lint scope as documented in
next-line scope as documented in
.Xr man 7 .
.Ss Mdoc Abstract Syntax Tree
This AST is governed by the ontological
@ -665,10 +641,13 @@ front-ends to
.Xr mandoc 1
are unable to render them in any meaningful way.
Furthermore, behaviour when encountering badly-nested blocks is not
consistent across troff implementations, especially when using multiple
consistent across troff implementations, especially when using multiple
levels of badly-nested blocks.
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr mandoc_escape 3 ,
.Xr mandoc_malloc 3 ,
.Xr mchars_alloc 3 ,
.Xr eqn 7 ,
.Xr man 7 ,
.Xr mandoc_char 7 ,

226
mandoc.c
View File

@ -1,7 +1,7 @@
/* $Id: mandoc.c,v 1.74 2013/12/30 18:30:32 schwarze Exp $ */
/* $Id: mandoc.c,v 1.83 2014/07/06 19:09:00 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011, 2012, 2013, 2014 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,6 +31,7 @@
#include <time.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmandoc.h"
#define DATESIZE 32
@ -45,7 +46,7 @@ mandoc_escape(const char **end, const char **start, int *sz)
const char *local_start;
int local_sz;
char term;
enum mandoc_esc gly;
enum mandoc_esc gly;
/*
* When the caller doesn't provide return storage,
@ -74,11 +75,11 @@ mandoc_escape(const char **end, const char **start, int *sz)
* these, but each eventually returns a substring of the glyph
* name.
*/
case ('('):
case '(':
gly = ESCAPE_SPECIAL;
*sz = 2;
break;
case ('['):
case '[':
gly = ESCAPE_SPECIAL;
/*
* Unicode escapes are defined in groff as \[uXXXX] to
@ -90,7 +91,7 @@ mandoc_escape(const char **end, const char **start, int *sz)
gly = ESCAPE_UNICODE;
term = ']';
break;
case ('C'):
case 'C':
if ('\'' != **start)
return(ESCAPE_ERROR);
*start = ++*end;
@ -104,50 +105,50 @@ mandoc_escape(const char **end, const char **start, int *sz)
/*
* Escapes taking no arguments at all.
*/
case ('d'):
case 'd':
/* FALLTHROUGH */
case ('u'):
case 'u':
return(ESCAPE_IGNORE);
/*
* The \z escape is supposed to output the following
* character without advancing the cursor position.
* character without advancing the cursor position.
* Since we are mostly dealing with terminal mode,
* let us just skip the next character.
*/
case ('z'):
case 'z':
return(ESCAPE_SKIPCHAR);
/*
* Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where
* 'X' is the trigger. These have opaque sub-strings.
*/
case ('F'):
case 'F':
/* FALLTHROUGH */
case ('g'):
case 'g':
/* FALLTHROUGH */
case ('k'):
case 'k':
/* FALLTHROUGH */
case ('M'):
case 'M':
/* FALLTHROUGH */
case ('m'):
case 'm':
/* FALLTHROUGH */
case ('n'):
case 'n':
/* FALLTHROUGH */
case ('V'):
case 'V':
/* FALLTHROUGH */
case ('Y'):
case 'Y':
gly = ESCAPE_IGNORE;
/* FALLTHROUGH */
case ('f'):
case 'f':
if (ESCAPE_ERROR == gly)
gly = ESCAPE_FONT;
switch (**start) {
case ('('):
case '(':
*start = ++*end;
*sz = 2;
break;
case ('['):
case '[':
*start = ++*end;
term = ']';
break;
@ -160,60 +161,59 @@ mandoc_escape(const char **end, const char **start, int *sz)
/*
* These escapes are of the form \X'Y', where 'X' is the trigger
* and 'Y' is any string. These have opaque sub-strings.
* The \B and \w escapes are handled in roff.c, roff_res().
*/
case ('A'):
case 'A':
/* FALLTHROUGH */
case ('b'):
case 'b':
/* FALLTHROUGH */
case ('B'):
case 'D':
/* FALLTHROUGH */
case ('D'):
case 'o':
/* FALLTHROUGH */
case ('o'):
case 'R':
/* FALLTHROUGH */
case ('R'):
case 'X':
/* FALLTHROUGH */
case ('w'):
/* FALLTHROUGH */
case ('X'):
/* FALLTHROUGH */
case ('Z'):
if ('\'' != **start)
case 'Z':
if ('\0' == **start)
return(ESCAPE_ERROR);
gly = ESCAPE_IGNORE;
term = **start;
*start = ++*end;
term = '\'';
break;
/*
* These escapes are of the form \X'N', where 'X' is the trigger
* and 'N' resolves to a numerical expression.
*/
case ('h'):
case 'h':
/* FALLTHROUGH */
case ('H'):
case 'H':
/* FALLTHROUGH */
case ('L'):
case 'L':
/* FALLTHROUGH */
case ('l'):
case 'l':
/* FALLTHROUGH */
case ('S'):
case 'S':
/* FALLTHROUGH */
case ('v'):
case 'v':
/* FALLTHROUGH */
case ('x'):
if ('\'' != **start)
case 'x':
if (strchr(" %&()*+-./0123456789:<=>", **start)) {
++*end;
return(ESCAPE_ERROR);
}
gly = ESCAPE_IGNORE;
term = **start;
*start = ++*end;
term = '\'';
break;
/*
* Special handling for the numbered character escape.
* XXX Do any other escapes need similar handling?
*/
case ('N'):
case 'N':
if ('\0' == **start)
return(ESCAPE_ERROR);
(*end)++;
@ -229,10 +229,10 @@ mandoc_escape(const char **end, const char **start, int *sz)
(*end)++;
return(ESCAPE_NUMBERED);
/*
/*
* Sizes get a special category of their own.
*/
case ('s'):
case 's':
gly = ESCAPE_IGNORE;
/* See +/- counts as a sign. */
@ -240,15 +240,15 @@ mandoc_escape(const char **end, const char **start, int *sz)
(*end)++;
switch (**end) {
case ('('):
case '(':
*start = ++*end;
*sz = 2;
break;
case ('['):
case '[':
*start = ++*end;
term = ']';
break;
case ('\''):
case '\'':
*start = ++*end;
term = '\'';
break;
@ -280,9 +280,9 @@ mandoc_escape(const char **end, const char **start, int *sz)
if ('\0' != term) {
while (**end != term) {
switch (**end) {
case ('\0'):
case '\0':
return(ESCAPE_ERROR);
case ('\\'):
case '\\':
(*end)++;
if (ESCAPE_ERROR ==
mandoc_escape(end, NULL, NULL))
@ -304,7 +304,7 @@ mandoc_escape(const char **end, const char **start, int *sz)
/* Run post-processors. */
switch (gly) {
case (ESCAPE_FONT):
case ESCAPE_FONT:
if (2 == *sz) {
if ('C' == **start) {
/*
@ -322,27 +322,27 @@ mandoc_escape(const char **end, const char **start, int *sz)
break;
switch (**start) {
case ('3'):
case '3':
/* FALLTHROUGH */
case ('B'):
case 'B':
gly = ESCAPE_FONTBOLD;
break;
case ('2'):
case '2':
/* FALLTHROUGH */
case ('I'):
case 'I':
gly = ESCAPE_FONTITALIC;
break;
case ('P'):
case 'P':
gly = ESCAPE_FONTPREV;
break;
case ('1'):
case '1':
/* FALLTHROUGH */
case ('R'):
case 'R':
gly = ESCAPE_FONTROMAN;
break;
}
break;
case (ESCAPE_SPECIAL):
case ESCAPE_SPECIAL:
if (1 == *sz && 'c' == **start)
gly = ESCAPE_NOSPACE;
break;
@ -353,74 +353,6 @@ mandoc_escape(const char **end, const char **start, int *sz)
return(gly);
}
void *
mandoc_calloc(size_t num, size_t size)
{
void *ptr;
ptr = calloc(num, size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
void *
mandoc_malloc(size_t size)
{
void *ptr;
ptr = malloc(size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
void *
mandoc_realloc(void *ptr, size_t size)
{
ptr = realloc(ptr, size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
char *
mandoc_strndup(const char *ptr, size_t sz)
{
char *p;
p = mandoc_malloc(sz + 1);
memcpy(p, ptr, sz);
p[(int)sz] = '\0';
return(p);
}
char *
mandoc_strdup(const char *ptr)
{
char *p;
p = strdup(ptr);
if (NULL == p) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(p);
}
/*
* Parse a quoted or unquoted roff-style request or macro argument.
* Return a pointer to the parsed argument, which is either the original
@ -442,7 +374,7 @@ mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
if ('"' == *start) {
quoted = 1;
start++;
}
}
pairs = 0;
white = 0;
@ -461,14 +393,14 @@ mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
* backslashes and backslash-t to literal tabs.
*/
switch (cp[1]) {
case ('t'):
case 't':
cp[0] = '\t';
/* FALLTHROUGH */
case ('\\'):
case '\\':
pairs++;
cp++;
break;
case (' '):
case ' ':
/* Skip escaped blanks. */
if (0 == quoted)
cp++;
@ -497,7 +429,7 @@ mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
/* Quoted argument without a closing quote. */
if (1 == quoted)
mandoc_msg(MANDOCERR_BADQUOTE, parse, ln, *pos, NULL);
mandoc_msg(MANDOCERR_ARG_QUOTE, parse, ln, *pos, NULL);
/* NUL-terminate this argument and move to the next one. */
if (pairs)
@ -511,7 +443,7 @@ mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
*cpp = cp;
if ('\0' == *cp && (white || ' ' == cp[-1]))
mandoc_msg(MANDOCERR_EOLNSPACE, parse, ln, *pos, NULL);
mandoc_msg(MANDOCERR_SPACE_EOL, parse, ln, *pos, NULL);
return(start);
}
@ -579,14 +511,14 @@ mandoc_normdate(struct mparse *parse, char *in, int ln, int pos)
if (NULL == in || '\0' == *in ||
0 == strcmp(in, "$" "Mdocdate$")) {
mandoc_msg(MANDOCERR_NODATE, parse, ln, pos, NULL);
mandoc_msg(MANDOCERR_DATE_MISSING, parse, ln, pos, NULL);
time(&t);
}
else if (a2time(&t, "%Y-%m-%d", in))
t = 0;
else if (!a2time(&t, "$" "Mdocdate: %b %d %Y $", in) &&
!a2time(&t, "%b %d, %Y", in)) {
mandoc_msg(MANDOCERR_BADDATE, parse, ln, pos, NULL);
mandoc_msg(MANDOCERR_DATE_BAD, parse, ln, pos, in);
t = 0;
}
out = t ? time2a(t) : NULL;
@ -594,10 +526,10 @@ mandoc_normdate(struct mparse *parse, char *in, int ln, int pos)
}
int
mandoc_eos(const char *p, size_t sz, int enclosed)
mandoc_eos(const char *p, size_t sz)
{
const char *q;
int found;
const char *q;
int enclosed, found;
if (0 == sz)
return(0);
@ -608,24 +540,24 @@ mandoc_eos(const char *p, size_t sz, int enclosed)
* propagate outward.
*/
found = 0;
enclosed = found = 0;
for (q = p + (int)sz - 1; q >= p; q--) {
switch (*q) {
case ('\"'):
case '\"':
/* FALLTHROUGH */
case ('\''):
case '\'':
/* FALLTHROUGH */
case (']'):
case ']':
/* FALLTHROUGH */
case (')'):
case ')':
if (0 == found)
enclosed = 1;
break;
case ('.'):
case '.':
/* FALLTHROUGH */
case ('!'):
case '!':
/* FALLTHROUGH */
case ('?'):
case '?':
found = 1;
break;
default:

144
mandoc.db.5 Normal file
View File

@ -0,0 +1,144 @@
.\" $Id: mandoc.db.5,v 1.1 2014/04/15 20:18:26 schwarze Exp $
.\"
.\" Copyright (c) 2014 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.
.\"
.Dd $Mdocdate: April 15 2014 $
.Dt MANDOC.DB 5
.Os
.Sh NAME
.Nm mandoc.db
.Nd manual page database
.Sh DESCRIPTION
The
.Nm
SQLite3 file format is used to store information about installed manual
pages to facilitate semantic searching for manuals.
Each manual page tree contains its own
.Nm
file; see
.Sx FILES
for examples.
.Pp
Such database files are generated by
.Xr makewhatis 8
and used by
.Xr apropos 1
and
.Xr whatis 1 .
.Pp
One line in the following tables describes:
.Bl -tag -width Ds
.It Sy mpages
One physical manual page file, no matter how many times and under which
names it may appear in the file system.
.It Sy mlinks
One entry in the file system, no matter which content it points to.
.It Sy names
One manual page name, no matter whether it appears in a page header,
in a NAME or SYNOPSIS section, or as a file name.
.It Sy keys
One chunk of text from some macro invocation.
.El
.Pp
Each record in the latter three tables uses its
.Va pageid
column to point to a record in the
.Sy mpages
table.
.Pp
The other columns are as follows; unless stated otherwise, they are
of type
.Vt TEXT .
.Bl -tag -width mpages.desc
.It Sy mpages.desc
The description line
.Pq Sq \&Nd
of the page.
.It Sy mpages.form
The
.Vt INTEGER
1 if the page is unformatted, i.e. in
.Xr mdoc 7
or
.Xr man 7
format, and 2 if it is formatted, i.e. a
.Sq cat
page.
.It Sy mlinks.sec
The manual section as found in the subdirectory name.
.It Sy mlinks.arch
The manual architecture as found in the subdirectory name, or
.Qq any .
.It Sy mlinks.name
The manual name as found in the file name.
.It Sy names.bits
An
.Vt INTEGER
bit mask telling whether the name came from a header line, from the
NAME or SYNOPSIS section, or from a file name.
Bits are defined in
.In mansearch.h .
.It Sy names.name
The name itself.
.It Sy keys.bits
An
.Vt INTEGER
bit mask telling which semantic contexts the key was found in;
defined in
.In mansearch.h ,
documented in
.Xr apropos 1 .
.It Sy keys.key
The string found in those contexts.
.El
.Sh FILES
.Bl -tag -width /usr/share/mandoc.db -compact
.It Pa /usr/share/mandoc.db
The manual page database for the base system.
.It Pa /usr/X11R6/mandoc.db
The same for the
.Xr X 7
Window System.
.It Pa /usr/local/mandoc.db
The same for
.Xr packages 7 .
.El
.Sh SEE ALSO
.Xr apropos 1 ,
.Xr man 1 ,
.Xr sqlite3 1 ,
.Xr whatis 1 ,
.Xr mansearch 3 ,
.Xr makewhatis 8
.Sh HISTORY
A manual page database
.Pa /usr/lib/whatis
first appeared in
.Bx 2 .
The present format first appeared in
.Ox 5.6 .
.Sh AUTHORS
.An -nosplit
The original version of
.Xr makewhatis 8
was written by
.An Bill Joy
in 1979.
An SQLite3 version was first implemented by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
in 2012.
The present database format was designed by
.An Ingo Schwarze Aq Mt schwarze@openbsd.org
in 2014.

212
mandoc.h
View File

@ -1,7 +1,7 @@
/* $Id: mandoc.h,v 1.112 2013/12/30 18:30:32 schwarze Exp $ */
/* $Id: mandoc.h,v 1.152 2014/08/06 15:09:05 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,6 +20,7 @@
#define ASCII_NBRSP 31 /* non-breaking space */
#define ASCII_HYPH 30 /* breakable hyphen */
#define ASCII_BREAK 29 /* breakable zero-width space */
/*
* Status level. This refers to both internal status (i.e., whilst
@ -48,66 +49,78 @@ enum mandocerr {
MANDOCERR_WARNING, /* ===== start of warnings ===== */
/* related to the prologue */
MANDOCERR_NOTITLE, /* no title in document */
MANDOCERR_UPPERCASE, /* document title should be all caps */
MANDOCERR_BADMSEC, /* unknown manual section */
MANDOCERR_BADVOLARCH, /* unknown manual volume or arch */
MANDOCERR_NODATE, /* date missing, using today's date */
MANDOCERR_BADDATE, /* cannot parse date, using it verbatim */
MANDOCERR_PROLOGOOO, /* prologue macros out of order */
MANDOCERR_PROLOGREP, /* duplicate prologue macro */
MANDOCERR_BADPROLOG, /* macro not allowed in prologue */
MANDOCERR_BADBODY, /* macro not allowed in body */
MANDOCERR_DT_NOTITLE, /* missing manual title, using UNTITLED: line */
MANDOCERR_TH_NOTITLE, /* missing manual title, using "": [macro] */
MANDOCERR_TITLE_CASE, /* lower case character in document title */
MANDOCERR_MSEC_MISSING, /* missing manual section, using "": macro */
MANDOCERR_MSEC_BAD, /* unknown manual section: Dt ... section */
MANDOCERR_ARCH_BAD, /* unknown manual volume or arch: Dt ... volume */
MANDOCERR_DATE_MISSING, /* missing date, using today's date */
MANDOCERR_DATE_BAD, /* cannot parse date, using it verbatim: date */
MANDOCERR_OS_MISSING, /* missing Os macro, using "" */
MANDOCERR_PROLOG_REP, /* duplicate prologue macro: macro */
MANDOCERR_PROLOG_LATE, /* late prologue macro: macro */
MANDOCERR_DT_LATE, /* skipping late title macro: Dt args */
MANDOCERR_PROLOG_ORDER, /* prologue macros out of order: macros */
/* related to document structure */
MANDOCERR_SO, /* .so is fragile, better use ln(1) */
MANDOCERR_NAMESECFIRST, /* NAME section must come first */
MANDOCERR_BADNAMESEC, /* bad NAME section contents */
MANDOCERR_SECOOO, /* sections out of conventional order */
MANDOCERR_SECREP, /* duplicate section name */
MANDOCERR_SECMSEC, /* section header suited to sections ... */
MANDOCERR_SO, /* .so is fragile, better use ln(1): so path */
MANDOCERR_DOC_EMPTY, /* no document body */
MANDOCERR_SEC_BEFORE, /* content before first section header: macro */
MANDOCERR_NAMESEC_FIRST, /* first section is not NAME: Sh title */
MANDOCERR_NAMESEC_BAD, /* bad NAME section contents: macro */
MANDOCERR_SEC_ORDER, /* sections out of conventional order: Sh title */
MANDOCERR_SEC_REP, /* duplicate section title: Sh title */
MANDOCERR_SEC_MSEC, /* unexpected section: Sh title for ... only */
/* related to macros and nesting */
MANDOCERR_MACROOBS, /* skipping obsolete macro */
MANDOCERR_IGNPAR, /* skipping paragraph macro */
MANDOCERR_MOVEPAR, /* moving paragraph macro out of list */
MANDOCERR_IGNNS, /* skipping no-space macro */
MANDOCERR_SCOPENEST, /* blocks badly nested */
MANDOCERR_CHILD, /* child violates parent syntax */
MANDOCERR_NESTEDDISP, /* nested displays are not portable */
MANDOCERR_SCOPEREP, /* already in literal mode */
MANDOCERR_LINESCOPE, /* line scope broken */
MANDOCERR_MACRO_OBS, /* obsolete macro: macro */
MANDOCERR_PAR_SKIP, /* skipping paragraph macro: macro ... */
MANDOCERR_PAR_MOVE, /* moving paragraph macro out of list: macro */
MANDOCERR_NS_SKIP, /* skipping no-space macro */
MANDOCERR_BLK_NEST, /* blocks badly nested: macro ... */
MANDOCERR_BD_NEST, /* nested displays are not portable: macro ... */
MANDOCERR_BL_MOVE, /* moving content out of list: macro */
MANDOCERR_VT_CHILD, /* .Vt block has child macro: macro */
MANDOCERR_FI_SKIP, /* fill mode already enabled, skipping: fi */
MANDOCERR_NF_SKIP, /* fill mode already disabled, skipping: nf */
MANDOCERR_BLK_LINE, /* line scope broken: macro breaks macro */
/* related to missing macro arguments */
MANDOCERR_MACROEMPTY, /* skipping empty macro */
/* related to missing arguments */
MANDOCERR_REQ_EMPTY, /* skipping empty request: request */
MANDOCERR_COND_EMPTY, /* conditional request controls empty scope */
MANDOCERR_MACRO_EMPTY, /* skipping empty macro: macro */
MANDOCERR_ARG_EMPTY, /* empty argument, using 0n: macro arg */
MANDOCERR_ARGCWARN, /* argument count wrong */
MANDOCERR_DISPTYPE, /* missing display type */
MANDOCERR_LISTFIRST, /* list type must come first */
MANDOCERR_NOWIDTHARG, /* tag lists require a width argument */
MANDOCERR_FONTTYPE, /* missing font type */
MANDOCERR_WNOSCOPE, /* skipping end of block that is not open */
MANDOCERR_BD_NOTYPE, /* missing display type, using -ragged: Bd */
MANDOCERR_BL_LATETYPE, /* list type is not the first argument: Bl arg */
MANDOCERR_BL_NOWIDTH, /* missing -width in -tag list, using 8n */
MANDOCERR_EX_NONAME, /* missing utility name, using "": Ex */
MANDOCERR_IT_NOHEAD, /* empty head in list item: Bl -type It */
MANDOCERR_IT_NOBODY, /* empty list item: Bl -type It */
MANDOCERR_BF_NOFONT, /* missing font type, using \fR: Bf */
MANDOCERR_BF_BADFONT, /* unknown font type, using \fR: Bf font */
MANDOCERR_ARG_STD, /* missing -std argument, adding it: macro */
/* related to bad macro arguments */
MANDOCERR_IGNARGV, /* skipping argument */
MANDOCERR_ARGVREP, /* duplicate argument */
MANDOCERR_DISPREP, /* duplicate display type */
MANDOCERR_LISTREP, /* duplicate list type */
MANDOCERR_BADATT, /* unknown AT&T UNIX version */
MANDOCERR_BADBOOL, /* bad Boolean value */
MANDOCERR_BADFONT, /* unknown font */
MANDOCERR_BADSTANDARD, /* unknown standard specifier */
MANDOCERR_BADWIDTH, /* bad width argument */
/* related to bad arguments */
MANDOCERR_ARG_QUOTE, /* unterminated quoted argument */
MANDOCERR_ARG_REP, /* duplicate argument: macro arg */
MANDOCERR_AN_REP, /* skipping duplicate argument: An -arg */
MANDOCERR_BD_REP, /* skipping duplicate display type: Bd -type */
MANDOCERR_BL_REP, /* skipping duplicate list type: Bl -type */
MANDOCERR_BL_SKIPW, /* skipping -width argument: Bl -type */
MANDOCERR_AT_BAD, /* unknown AT&T UNIX version: At version */
MANDOCERR_RS_BAD, /* invalid content in Rs block: macro */
MANDOCERR_SM_BAD, /* invalid Boolean argument: macro arg */
MANDOCERR_FT_BAD, /* unknown font, skipping request: ft font */
/* related to plain text */
MANDOCERR_NOBLANKLN, /* blank line in non-literal context */
MANDOCERR_BADTAB, /* tab in non-literal context */
MANDOCERR_EOLNSPACE, /* end of line whitespace */
MANDOCERR_BADCOMMENT, /* bad comment style */
MANDOCERR_BADESCAPE, /* unknown escape sequence */
MANDOCERR_BADQUOTE, /* unterminated quoted string */
/* related to equations */
MANDOCERR_EQNQUOTE, /* unexpected literal in equation */
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_COMMENT_BAD, /* bad comment style */
MANDOCERR_ESC_BAD, /* invalid escape sequence: esc */
MANDOCERR_STR_UNDEF, /* undefined string, using "": name */
MANDOCERR_ERROR, /* ===== start of errors ===== */
@ -128,40 +141,40 @@ enum mandocerr {
MANDOCERR_TBLBLOCK, /* data block still open */
MANDOCERR_TBLEXTRADAT, /* ignoring extra data cells */
/* related to document structure and macros */
MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */
MANDOCERR_BADCHAR, /* skipping bad character */
MANDOCERR_NAMESC, /* escaped character not allowed in a name */
MANDOCERR_NONAME, /* manual name not yet set */
MANDOCERR_NOTEXT, /* skipping text before the first section header */
MANDOCERR_MACRO, /* skipping unknown macro */
MANDOCERR_REQUEST, /* NOT IMPLEMENTED: skipping request */
MANDOCERR_BADCHAR, /* skipping bad character: number */
MANDOCERR_MACRO, /* skipping unknown macro: macro */
MANDOCERR_IT_STRAY, /* skipping item outside list: It ... */
MANDOCERR_TA_STRAY, /* skipping column outside column list: Ta */
MANDOCERR_BLK_NOTOPEN, /* skipping end of block that is not open */
MANDOCERR_BLK_BROKEN, /* inserting missing end of block: macro ... */
MANDOCERR_BLK_NOEND, /* appending missing end of block: macro */
/* related to request and macro arguments */
MANDOCERR_NAMESC, /* escaped character not allowed in a name: name */
MANDOCERR_ARGCOUNT, /* argument count wrong */
MANDOCERR_STRAYTA, /* skipping column outside column list */
MANDOCERR_NOSCOPE, /* skipping end of block that is not open */
MANDOCERR_SCOPEBROKEN, /* missing end of block */
MANDOCERR_SCOPEEXIT, /* scope open on exit */
MANDOCERR_UNAME, /* uname(3) system call failed */
/* FIXME: merge following with MANDOCERR_ARGCOUNT */
MANDOCERR_NOARGS, /* macro requires line argument(s) */
MANDOCERR_NOBODY, /* macro requires body argument(s) */
MANDOCERR_NOARGV, /* macro requires argument(s) */
MANDOCERR_NUMERIC, /* request requires a numeric argument */
MANDOCERR_LISTTYPE, /* missing list type */
MANDOCERR_ARGSLOST, /* line argument(s) will be lost */
MANDOCERR_BODYLOST, /* body argument(s) will be lost */
MANDOCERR_BL_NOTYPE, /* missing list type, using -item: Bl */
MANDOCERR_NM_NONAME, /* missing manual name, using "": Nm */
MANDOCERR_OS_UNAME, /* uname(3) system call failed, using UNKNOWN */
MANDOCERR_ST_BAD, /* unknown standard specifier: St standard */
MANDOCERR_IT_NONUM, /* skipping request without numeric argument */
MANDOCERR_ARG_SKIP, /* skipping all arguments: macro args */
MANDOCERR_ARG_EXCESS, /* skipping excess arguments: macro ... args */
MANDOCERR_FATAL, /* ===== start of fatal errors ===== */
MANDOCERR_NOTMANUAL, /* manual isn't really a manual */
MANDOCERR_COLUMNS, /* column syntax is inconsistent */
MANDOCERR_BADDISP, /* NOT IMPLEMENTED: .Bd -file */
MANDOCERR_SYNTARGVCOUNT, /* argument count wrong, violates syntax */
MANDOCERR_SYNTCHILD, /* child violates parent syntax */
MANDOCERR_SYNTARGCOUNT, /* argument count wrong, violates syntax */
MANDOCERR_SOPATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */
MANDOCERR_NODOCBODY, /* no document body */
MANDOCERR_NODOCPROLOG, /* no document prologue */
MANDOCERR_MEM, /* static buffer exhausted */
MANDOCERR_TOOLARGE, /* input too large */
MANDOCERR_BD_FILE, /* NOT IMPLEMENTED: Bd -file */
MANDOCERR_SO_PATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */
MANDOCERR_SO_FAIL, /* .so request failed */
/* ===== system errors ===== */
MANDOCERR_SYSOPEN, /* cannot open file */
MANDOCERR_SYSSTAT, /* cannot stat file */
MANDOCERR_SYSREAD, /* cannot read file */
MANDOCERR_MAX
};
@ -231,6 +244,7 @@ struct tbl_row {
struct tbl_row *next;
struct tbl_cell *first;
struct tbl_cell *last;
int vert; /* trailing vertical line */
};
enum tbl_datt {
@ -353,7 +367,7 @@ struct eqn_box {
/*
* An equation consists of a tree of expressions starting at a given
* line and position.
* line and position.
*/
struct eqn {
char *name; /* identifier (or NULL) */
@ -363,15 +377,12 @@ struct eqn {
};
/*
* The type of parse sequence. This value is usually passed via the
* mandoc(1) command line of -man and -mdoc. It's almost exclusively
* -mandoc but the others have been retained for compatibility.
* Parse options.
*/
enum mparset {
MPARSE_AUTO, /* magically determine the document type */
MPARSE_MDOC, /* assume -mdoc */
MPARSE_MAN /* assume -man */
};
#define MPARSE_MDOC 1 /* assume -mdoc */
#define MPARSE_MAN 2 /* assume -man */
#define MPARSE_SO 4 /* honour .so requests */
#define MPARSE_QUICK 8 /* abort the parse early */
enum mandoc_esc {
ESCAPE_ERROR = 0, /* bail! unparsable escape */
@ -399,30 +410,25 @@ struct man;
__BEGIN_DECLS
void *mandoc_calloc(size_t, size_t);
enum mandoc_esc mandoc_escape(const char **, const char **, int *);
void *mandoc_malloc(size_t);
void *mandoc_realloc(void *, size_t);
char *mandoc_strdup(const char *);
char *mandoc_strndup(const char *, size_t);
struct mchars *mchars_alloc(void);
void mchars_free(struct mchars *);
char mchars_num2char(const char *, size_t);
char mchars_num2char(const char *, size_t);
int mchars_num2uc(const char *, size_t);
int mchars_spec2cp(const struct mchars *,
int mchars_spec2cp(const struct mchars *,
const char *, size_t);
const char *mchars_spec2str(const struct mchars *,
const char *mchars_spec2str(const struct mchars *,
const char *, size_t, size_t *);
struct mparse *mparse_alloc(enum mparset, enum mandoclevel,
mandocmsg, void *, char *);
struct mparse *mparse_alloc(int, enum mandoclevel, mandocmsg,
const char *);
void mparse_free(struct mparse *);
void mparse_keep(struct mparse *);
enum mandoclevel mparse_readfd(struct mparse *, int, const char *);
enum mandoclevel mparse_readmem(struct mparse *, const void *, size_t,
const char *);
void mparse_reset(struct mparse *);
void mparse_result(struct mparse *,
struct mdoc **, struct man **);
void mparse_result(struct mparse *,
struct mdoc **, struct man **, char **);
const char *mparse_getkeep(const struct mparse *);
const char *mparse_strerror(enum mandocerr);
const char *mparse_strlevel(enum mandoclevel);

121
mandoc_aux.c Normal file
View File

@ -0,0 +1,121 @@
/* $Id: mandoc_aux.c,v 1.3 2014/07/09 08:20:34 schwarze Exp $ */
/*
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "mandoc_aux.h"
int
mandoc_asprintf(char **dest, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = vasprintf(dest, fmt, ap);
va_end(ap);
if (-1 == ret) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ret);
}
void *
mandoc_calloc(size_t num, size_t size)
{
void *ptr;
ptr = calloc(num, size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
void *
mandoc_malloc(size_t size)
{
void *ptr;
ptr = malloc(size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
void *
mandoc_realloc(void *ptr, size_t size)
{
ptr = realloc(ptr, size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
void *
mandoc_reallocarray(void *ptr, size_t num, size_t size)
{
ptr = reallocarray(ptr, num, size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
char *
mandoc_strdup(const char *ptr)
{
char *p;
p = strdup(ptr);
if (NULL == p) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(p);
}
char *
mandoc_strndup(const char *ptr, size_t sz)
{
char *p;
p = mandoc_malloc(sz + 1);
memcpy(p, ptr, sz);
p[(int)sz] = '\0';
return(p);
}

33
mandoc_aux.h Normal file
View File

@ -0,0 +1,33 @@
/* $Id: mandoc_aux.h,v 1.2 2014/04/23 21:06:41 schwarze Exp $ */
/*
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014 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.
*/
#ifndef MANDOC_AUX_H
#define MANDOC_AUX_H
__BEGIN_DECLS
int mandoc_asprintf(char **, const char *, ...);
void *mandoc_calloc(size_t, size_t);
void *mandoc_malloc(size_t);
void *mandoc_realloc(void *, size_t);
void *mandoc_reallocarray(void *, size_t, size_t);
char *mandoc_strdup(const char *);
char *mandoc_strndup(const char *, size_t);
__END_DECLS
#endif /*!MANDOC_AUX_H*/

362
mandoc_escape.3 Normal file
View File

@ -0,0 +1,362 @@
.\" $Id: mandoc_escape.3,v 1.1 2014/08/05 05:48:56 schwarze Exp $
.\"
.\" Copyright (c) 2014 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.
.\"
.Dd $Mdocdate: August 5 2014 $
.Dt MANDOC_ESCAPE 3
.Os
.Sh NAME
.Nm mandoc_escape
.Nd parse roff escape sequences
.Sh LIBRARY
.Lb libmandoc
.Sh SYNOPSIS
.In sys/types.h
.In mandoc.h
.Ft "enum mandoc_esc"
.Fo mandoc_escape
.Fa "const char **end"
.Fa "const char **start"
.Fa "int *sz"
.Fc
.Sh DESCRIPTION
This function scans a
.Xr roff 7
escape sequence.
.Pp
An escape sequence consists of
.Bl -dash -compact -width 2n
.It
an initial backslash character
.Pq Sq \e ,
.It
a single ASCII character called the escape sequence identifier,
.It
and, with only a few exceptions, an argument.
.El
.Pp
Arguments can be given in the following forms; some escape sequence
identifiers only accept some of these forms as specified below.
The first three forms are called the standard forms.
.Bl -tag -width 2n
.It \&In brackets: Ic \&[ Ns Ar argument Ns Ic \&]
The argument starts after the initial
.Sq \&[ ,
ends before the final
.Sq \&] ,
and the escape sequence ends with the final
.Sq \&] .
.It Two-character argument short form: Ic \&( Ns Ar ar
This form can only be used for arguments
consisting of exactly two characters.
It has the same effect as
.Ic \&[ Ns Ar ar Ns Ic \&] .
.It One-character argument short form: Ar a
This form can only be used for arguments
consisting of exactly one character.
It has the same effect as
.Ic \&[ Ns Ar a Ns Ic \&] .
.It Delimited form: Ar C Ns Ar argument Ns Ar C
The argument starts after the initial delimiter character
.Ar C ,
ends before the next occurrence of the delimiter character
.Ar C ,
and the escape sequence ends with that second
.Ar C .
Some escape sequences allow arbitrary characters
.Ar C
as quoting characters, some restrict the range of characters
that can be used as quoting characters.
.El
.Pp
Upon function entry,
.Fa end
is expected to point to the escape sequence identifier.
The values passed in as
.Fa start
and
.Fa sz
are ignored and overwritten.
.Pp
By design, this function cannot handle those
.Xr roff 7
escape sequences that require in-place expansion, in particular
user-defined strings
.Ic \e* ,
number registers
.Ic \en ,
width measurements
.Ic \ew ,
and numerical expression control
.Ic \eB .
These are handled by
.Fn roff_res ,
a private preprocessor function called from
.Fn roff_parseln ,
see the file
.Pa roff.c .
.Pp
The function
.Fn mandoc_escape
is used
.Bl -dash -compact -width 2n
.It
recursively by itself, because some escape sequence arguments can
in turn contain other escape sequences,
.It
for error detection internally by the
.Xr roff 7
parser part of the
.Lb libmandoc ,
see the file
.Pa roff.c ,
.It
above all externally by the
.Xr mandoc
formatting modules, in particular
.Fl Tascii
and
.Fl Thtml ,
for formatting purposes, see the files
.Pa term.c
and
.Pa html.c ,
.It
and rarely externally by high-level utilities using the mandoc library,
for example
.Xr makewhatis 8 ,
to purge escape sequences from text.
.El
.Sh RETURN VALUES
Upon function return, the pointer
.Fa end
is set to the character after the end of the escape sequence,
such that the calling higher-level parser can easily continue.
.Pp
For escape sequences taking an argument, the pointer
.Fa start
is set to the beginning of the argument and
.Fa sz
is set to the length of the argument.
For escape sequences not taking an argument,
.Fa start
is set to the character after the end of the sequence and
.Fa sz
is set to 0.
Both
.Fa start
and
.Fa sz
may be
.Dv NULL ;
in that case, the argument and the length are not returned.
.Pp
For sequences taking an argument, the function
.Fn mandoc_escape
returns one of the following values:
.Bl -tag -width 2n
.It Dv ESCAPE_FONT
The escape sequence
.Ic \ef
taking an argument in standard form:
.Ic \ef[ , \ef( , \ef Ns Ar a .
Two-character arguments starting with the character
.Sq C
are reduced to one-character arguments by skipping the
.Sq C .
More specific values are returned for the most commonly used arguments:
.Bl -column "argument" "ESCAPE_FONTITALIC"
.It argument Ta return value
.It Cm R No or Cm 1 Ta Dv ESCAPE_FONTROMAN
.It Cm I No or Cm 2 Ta Dv ESCAPE_FONTITALIC
.It Cm B No or Cm 3 Ta Dv ESCAPE_FONTBOLD
.It Cm P Ta Dv ESCAPE_FONTPREV
.It Cm BI Ta Dv ESCAPE_FONTBI
.El
.It Dv ESCAPE_SPECIAL
The escape sequence
.Ic \eC
taking an argument delimited with the single quote character
and, as a special exception, the escape sequences
.Em not
having an identifier, that is, those where the argument, in standard
form, directly follows the initial backslash:
.Ic \eC' , \e[ , \e( , \e Ns Ar a .
Note that the one-character argument short form can only be used for
argument characters that do not clash with escape sequence identifiers.
.Pp
If the argument consists of more than one character
and starts with the character
.Sq u ,
.Dv ESCAPE_UNICODE
is returned as described below.
If the argument is just the single character
.Sq u ,
.Dv ESCAPE_ERROR
is returned.
.Pp
The
.Dv ESCAPE_SPECIAL
special character escape sequences can be rendered using the functions
.Fn mchars_spec2cp
and
.Fn mchars_spec2str
described in the
.Xr mchars_alloc 3
manual.
.It Dv ESCAPE_UNICODE
Escape sequences of the same format as described above under
.Dv ESCAPE_SPECIAL ,
but with an argument starting with the character
.Sq u :
.Ic \eC'u , \e[u .
As a special exception,
.Fa start
is set to the character after the
.Sq u ,
and the
.Fa sz
return value does not include the
.Sq u
either.
.Pp
Such Unicode character escape sequences can be rendered using the function
.Fn mchars_num2uc
described in the
.Xr mchars_alloc 3
manual.
.It Dv ESCAPE_NUMBERED
The escape sequence
.Ic \eN
followed by a delimited argument.
The delimiter character is arbitrary except that digits cannot be used.
If a digit is encountered instead of the opening delimiter, that
digit is considered to be the argument and the end of the sequence, and
.Dv ESCAPE_IGNORE
is returned.
.Pp
Such ASCII character escape sequences can be rendered using the function
.Fn mchars_num2char
described in the
.Xr mchars_alloc 3
manual.
.It Dv ESCAPE_IGNORE
.Bl -bullet -width 2n
.It
The escape sequence
.Ic \es
followed by an argument in standard form or by an argument delimited
by the single quote character:
.Ic \es' , \es[ , \es( , \es Ns Ar a .
As a special exception, an optional
.Sq +
or
.Sq \-
character is allowed after the
.Sq s
for all forms.
.It
The escape sequences
.Ic \eF ,
.Ic \eg ,
.Ic \ek ,
.Ic \eM ,
.Ic \em ,
.Ic \en ,
.Ic \eV ,
and
.Ic \eY
followed by an argument in standard form.
.It
The escape sequences
.Ic \eA ,
.Ic \eb ,
.Ic \eD ,
.Ic \eo ,
.Ic \eR ,
.Ic \eX ,
and
.Ic \eZ
followed by an argument delimited by an arbitrary character.
.It
The escape sequences
.Ic \eH ,
.Ic \eh ,
.Ic \eL ,
.Ic \el ,
.Ic \eS ,
.Ic \ev ,
and
.Ic \ex
followed by an argument delimited by a character that cannot occur
in numerical expressions.
However, if any character that can occur in numerical expressions
is found instead of a delimiter, the sequence is considered to end
with that character, and
.Dv ESCAPE_ERROR
is returned.
.El
.It Dv ESCAPE_ERROR
Escape sequences taking an argument but not matching any of the above patterns.
In particular, that happens if the end of the logical input line
is reached before the end of the argument.
.El
.Pp
For sequences that do not take an argument, the function
.Fn mandoc_escape
returns one of the following values:
.Bl -tag -width 2n
.It Dv ESCAPE_SKIPCHAR
The escape sequence
.Qq \ez .
.It Dv ESCAPE_NOSPACE
The escape sequence
.Qq \ec .
.It Dv ESCAPE_IGNORE
The escape sequences
.Qq \ed
and
.Qq \eu .
.El
.Sh FILES
This function is implemented in
.Pa mandoc.c .
.Sh SEE ALSO
.Xr mchars_alloc 3 ,
.Xr mandoc_char 7 ,
.Xr roff 7
.Sh HISTORY
This function has been available since mandoc 1.11.2.
.Sh AUTHORS
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
.An Ingo Schwarze Aq Mt schwarze@openbsd.org
.Sh BUGS
The function doesn't cleanly distinguish between sequences that are
valid and supported, valid and ignored, valid and unsupported,
syntactically invalid, or undefined.
For sequences that are ignored or unsupported, it doesn't tell
whether that deficiency is likely to cause major formatting problems
and/or loss of document content.
The function is already rather complicated and still parses some
sequences incorrectly.
.
.ig
For these sequences, the list given below specifies a starting string
and either the length of the argument or an ending character.
The argument starts after the starting string.
In the former case, the sequence ends with the end of the argument.
In the latter case, the argument ends before the ending character,
and the sequence ends with the ending character.
..

249
mandoc_html.3 Normal file
View File

@ -0,0 +1,249 @@
.\" $Id: mandoc_html.3,v 1.1 2014/07/23 18:13:09 schwarze Exp $
.\"
.\" Copyright (c) 2014 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.
.\"
.Dd $Mdocdate: July 23 2014 $
.Dt MANDOC_HTML 3
.Os
.Sh NAME
.Nm mandoc_html
.Nd internals of the mandoc HTML formatter
.Sh SYNOPSIS
.In "html.h"
.Ft void
.Fn print_gen_decls "struct html *h"
.Ft void
.Fn print_gen_head "struct html *h"
.Ft struct tag *
.Fo print_otag
.Fa "struct html *h"
.Fa "enum htmltag tag"
.Fa "int sz"
.Fa "const struct htmlpair *p"
.Fc
.Ft void
.Fo print_tagq
.Fa "struct html *h"
.Fa "const struct tag *until"
.Fc
.Ft void
.Fo print_stagq
.Fa "struct html *h"
.Fa "const struct tag *suntil"
.Fc
.Ft void
.Fo print_text
.Fa "struct html *h"
.Fa "const char *word"
.Fc
.Sh DESCRIPTION
The mandoc HTML formatter is not a formal library.
However, as it is compiled into more than one program, in particular
.Xr mandoc 1
and
.Xr man.cgi 8 ,
and because it may be security-critical in some contexts,
some documentation is useful to help to use it correctly and
to prevent XSS vulnerabilities.
.Pp
The formatter produces HTML output on the standard output.
Since proper escaping is usually required and best taken care of
at one central place, the language-specific formatters
.Po
.Pa *_html.c ,
see
.Sx FILES
.Pc
are not supposed to print directly to
.Dv stdout
using functions like
.Xr printf 3 ,
.Xr putc 3 ,
.Xr puts 3 ,
or
.Xr write 2 .
Instead, they are expected to use the output functions declared in
.Pa html.h
and implemented as part of the main HTML formatting engine in
.Pa html.c .
.Ss Data structures
These structures are declared in
.Pa html.h .
.Bl -tag -width Ds
.It Vt struct html
Internal state of the HTML formatter.
.It Vt struct htmlpair
Holds one HTML attribute.
Members are
.Fa "enum htmlattr key"
and
.Fa "const char *val" .
Helper macros
.Fn PAIR_*
are provided to support initialization of such structures.
.It Vt struct tag
One entry for the LIFO stack of HTML elements.
Members are
.Fa "enum htmltag tag"
and
.Fa "struct tag *next" .
.El
.Ss Private interface functions
The function
.Fn print_gen_decls
prints the opening
.Ao Pf \&? Ic xml ? Ac
and
.Aq Pf \&! Ic DOCTYPE
declarations required for the current document type.
.Pp
The function
.Fn print_gen_head
prints the opening
.Aq Ic META
and
.Aq Ic LINK
elements for the document
.Aq Ic HEAD ,
using the
.Fa style
member of
.Fa h
unless that is
.Dv NULL .
It uses
.Fn print_otag
which takes care of properly encoding attributes,
which is relevant for the
.Fa style
link in particular.
.Pp
The function
.Fn print_otag
prints the start tag of an HTML element with the name
.Fa tag ,
including the
.Fa sz
attributes that can optionally be provided in the
.Fa p
array.
It uses the private function
.Fn print_attr
which in turn uses the private function
.Fn print_encode
to take care of HTML encoding.
If required by the element type, it remembers in
.Fa h
that the element is open.
The function
.Fn print_tagq
is used to close out all open elements up to and including
.Fa until ;
.Fn print_stagq
is a variant to close out all open elements up to but excluding
.Fa suntil .
.Pp
The function
.Fn print_text
prints HTML element content.
It uses the private function
.Fn print_encode
to take care of HTML encoding.
If the document has requested a non-standard font, for example using a
.Xr roff 7
.Ic \ef
font escape sequence,
.Fn print_text
wraps
.Fa word
in an HTML font selection element using the
.Fn print_otag
and
.Fn print_tagq
functions.
.Pp
The functions
.Fn bufinit ,
.Fn bufcat* ,
and
.Fn buffmt*
do not directly produce output but buffer text in the
.Fa buf
member of
.Fa h .
They are not used internally by
.Pa html.c
but intended for use by the language-specific formatters
to ease preparation of strings for the
.Fa p
argument of
.Fn print_otag
and for the
.Fa word
argument of
.Fn print_text .
Consequently, these functions do not do any HTML encoding.
.Pp
The functions
.Fn html_strlen ,
.Fn print_eqn ,
.Fn print_tbl ,
and
.Fn print_tblclose
are not yet documented.
.Sh FILES
.Bl -tag -width mandoc_aux.c -compact
.It Pa main.h
declarations of public functions for use by the main program,
not yet documented
.It Pa html.h
declarations of data types and private functions
for use by language-specific HTML formatters
.It Pa html.c
main HTML formatting engine and utility functions
.It Pa mdoc_html.c
.Xr mdoc 7
HTML formatter
.It Pa man_html.c
.Xr man 7
HTML formatter
.It Pa tbl_html.c
.Xr tbl 7
HTML formatter
.It Pa eqn_html.c
.Xr eqn 7
HTML formatter
.It Pa out.h
declarations of data types and private functions
for shared use by all mandoc formatters,
not yet documented
.It Pa out.c
private functions for shared use by all mandoc formatters
.It Pa mandoc_aux.h
declarations of common mandoc utility functions, see
.Xr mandoc 3
.It Pa mandoc_aux.c
implementation of common mandoc utility functions
.El
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr mandoc 3 ,
.Xr man.cgi 8
.Sh AUTHORS
.An -nosplit
The mandoc HTML formatter was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
This manual was written by
.An Ingo Schwarze Aq Mt schwarze@openbsd.org .

197
mandoc_malloc.3 Normal file
View File

@ -0,0 +1,197 @@
.\" $Id: mandoc_malloc.3,v 1.1 2014/08/05 05:48:56 schwarze Exp $
.\"
.\" Copyright (c) 2014 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.
.\"
.Dd $Mdocdate: August 5 2014 $
.Dt MANDOC_MALLOC 3
.Os
.Sh NAME
.Nm mandoc_malloc ,
.Nm mandoc_realloc ,
.Nm mandoc_reallocarray ,
.Nm mandoc_calloc ,
.Nm mandoc_strdup ,
.Nm mandoc_strndup ,
.Nm mandoc_asprintf
.Nd memory allocation function wrappers used in the mandoc library
.Sh LIBRARY
.Lb libmandoc
.Sh SYNOPSIS
.In sys/types.h
.In mandoc_aux.h
.Ft "void *"
.Fo mandoc_malloc
.Fa "size_t size"
.Fc
.Ft "void *"
.Fo mandoc_realloc
.Fa "void *ptr"
.Fa "size_t size"
.Fc
.Ft "void *"
.Fo mandoc_reallocarray
.Fa "void *ptr"
.Fa "size_t nmemb"
.Fa "size_t size"
.Fc
.Ft "void *"
.Fo mandoc_calloc
.Fa "size_t nmemb"
.Fa "size_t size"
.Fc
.Ft "char *"
.Fo mandoc_strdup
.Fa "const char *s"
.Fc
.Ft "char *"
.Fo mandoc_strndup
.Fa "const char *s"
.Fa "size_t maxlen"
.Fc
.Ft int
.Fo mandoc_asprintf
.Fa "char **ret"
.Fa "const char *format"
.Fa "..."
.Fc
.Sh DESCRIPTION
These functions call the
.Lb libc
functions of the same names, passing through their return values when
successful.
In case of failure, they do not return, but instead call
.Xr perror 3
and
.Xr exit 3 .
They can be used both internally by any code in the
.Lb libmandoc
and externally by programs using that library, for example
.Xr mandoc 1 ,
.Xr apropos 1 ,
and
.Xr makewhatis 8 .
.Pp
The function
.Fn mandoc_malloc
allocates one new object, leaving the memory uninitialized.
The functions
.Fn mandoc_realloc
and
.Fn mandoc_reallocarray
change the size of an existing object or array, possibly moving it.
When shrinking the size, existing data is truncated; when growing,
the additional memory is not initialized.
The function
.Fn mandoc_calloc
allocates a new array, initializing it to zero.
.Pp
The argument
.Fa size
is the size of each object.
The argument
.Fa nmemb
is the new number of objects in the array.
The argument
.Fa ptr
is a pointer to the existing object or array to be resized; if it is
.Dv NULL ,
a new object or array is allocated.
.Pp
The functions
.Fn mandoc_strdup
and
.Fn mandoc_strndup
copy a string into newly allocated memory.
For
.Fn mandoc_strdup ,
the string pointed to by
.Fa s
needs to be NUL-terminated.
For
.Fn mandoc_strndup ,
at most
.Fa maxlen
bytes are copied.
The function
.Fn mandoc_asprintf
writes output formatted according to
.Fa format
into newly allocated memory and returns a pointer to the result in
.Fa ret .
For all three string functions, the result is always NUL-terminated.
.Pp
When the objects and strings are no longer needed,
the pointers returned by these functions can be passed to
.Xr free 3 .
.Sh RETURN VALUES
The function
.Fn mandoc_asprintf
always returns the number of characters written, excluding the
final NUL byte.
It never returns -1.
.Pp
The other functions always return a valid pointer; they never return
.Dv NULL .
.Sh FILES
These functions are implemented in
.Pa mandoc_aux.c .
.Sh SEE ALSO
.Xr asprintf 3 ,
.Xr exit 3 ,
.Xr malloc 3 ,
.Xr perror 3 ,
.Xr strdup 3
.Sh STANDARDS
The functions
.Fn malloc ,
.Fn realloc ,
and
.Fn calloc
are required by
.St -ansiC .
The functions
.Fn strdup
and
.Fn strndup
are required by
.St -p1003.1-2008 .
The function
.Fn asprintf
is a widespread extension that first appeared in the GNU C library.
.Pp
The function
.Fn reallocarray
is an extension that first appeared in
.Ox 5.6 .
If it is not provided by the operating system, the mandoc build system
uses a bundled portable implementation.
.Sh HISTORY
The functions
.Fn mandoc_malloc ,
.Fn mandoc_realloc ,
.Fn mandoc_calloc ,
and
.Fn mandoc_strdup
have been available since mandoc 1.9.12,
.Fn mandoc_strndup
since 1.11.5,
and
.Fn mandoc_asprintf
and
.Fn mandoc_reallocarray
since 1.12.4 and 1.13.0.
.Sh AUTHORS
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
.An Ingo Schwarze Aq Mt schwarze@openbsd.org

View File

@ -1,324 +0,0 @@
.\" $Id: mandocdb.8,v 1.17.2.1 2013/09/18 01:04:07 schwarze Exp $
.\"
.\" Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" 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.
.\"
.Dd $Mdocdate: September 18 2013 $
.Dt MANDOCDB 8
.Os
.Sh NAME
.Nm mandocdb
.Nd index UNIX manuals
.Sh SYNOPSIS
.Nm
.Op Fl avW
.Op Fl C Ar file
.Nm
.Op Fl avW
.Ar dir ...
.Nm
.Op Fl vW
.Fl d Ar dir
.Op Ar
.Nm
.Op Fl vW
.Fl u Ar dir
.Op Ar
.Nm
.Fl t Ar
.Sh DESCRIPTION
The
.Nm
utility extracts keywords from
.Ux
manuals and indexes them in a
.Sx Keyword Database
and
.Sx Index Database
for fast retrieval by
.Xr apropos 1 ,
.Xr whatis 1 ,
and
.Xr man 1 Ns 's
.Fl k
option.
.Pp
By default,
.Nm
creates databases in each
.Ar dir
using the files
.Sm off
.Sy man Ar section Li /
.Op Ar arch Li /
.Ar title . section
.Sm on
and
.Sm off
.Sy cat Ar section Li /
.Op Ar arch Li /
.Ar title . Sy 0
.Sm on
in that directory;
existing databases are truncated.
If
.Ar dir
is not provided,
.Nm
uses the default paths stipulated by
.Xr man 1 .
.Pp
The arguments are as follows:
.Bl -tag -width "-C file"
.It Fl a
Use all directories and files found below
.Ar dir ... .
.It Fl C Ar file
Specify an alternative configuration
.Ar file
in
.Xr man.conf 5
format.
.It Fl d Ar dir
Merge (remove and re-add)
.Ar
to the database in
.Ar dir
without truncating it.
.It Fl t Ar
Check the given
.Ar files
for potential problems.
No databases are modified.
Implies
.Fl a
and
.Fl W .
All diagnostic messages are printed to the standard output;
the standard error output is not used.
.It Fl u Ar dir
Remove
.Ar
from the database in
.Ar dir
without truncating it.
.It Fl v
Display all files added or removed to the index.
.It Fl W
Print warnings about potential problems with manual pages
to the standard error output.
.El
.Pp
If fatal parse errors are encountered while parsing, the offending file
is printed to stderr, omitted from the index, and the parse continues
with the next input file.
.Ss Index Database
The index database,
.Pa mandoc.index ,
is a
.Xr recno 3
database with record values consisting of
.Pp
.Bl -enum -compact
.It
the character
.Cm d ,
.Cm a ,
or
.Cm c
to indicate the file type
.Po
.Xr mdoc 7 ,
.Xr man 7 ,
and post-formatted, respectively
.Pc ,
.It
the filename relative to the databases' path,
.It
the manual section,
.It
the manual title,
.It
the architecture
.Pq often empty ,
.It
and the description.
.El
.Pp
Each of the above is NUL-terminated.
.Pp
If the record value is zero-length, it is unassigned.
.Ss Keyword Database
The keyword database,
.Pa mandoc.db ,
is a
.Xr btree 3
database of NUL-terminated keywords (record length is non-zero string
length plus one) mapping to a 16-byte binary field consisting of the
64-bit keyword type and the 64-bit
.Sx Index Database
record number, both in network-byte order.
.Pp
The type bit-mask consists of the following
values mapping into
.Xr mdoc 7
macro identifiers:
.Pp
.Bl -column "x0x0000000000000001ULLx" "xLix" -offset indent -compact
.It Li 0x0000000000000001ULL Ta \&An
.It Li 0x0000000000000002ULL Ta \&Ar
.It Li 0x0000000000000004ULL Ta \&At
.It Li 0x0000000000000008ULL Ta \&Bsx
.It Li 0x0000000000000010ULL Ta \&Bx
.It Li 0x0000000000000020ULL Ta \&Cd
.It Li 0x0000000000000040ULL Ta \&Cm
.It Li 0x0000000000000080ULL Ta \&Dv
.It Li 0x0000000000000100ULL Ta \&Dx
.It Li 0x0000000000000200ULL Ta \&Em
.It Li 0x0000000000000400ULL Ta \&Er
.It Li 0x0000000000000800ULL Ta \&Ev
.It Li 0x0000000000001000ULL Ta \&Fa
.It Li 0x0000000000002000ULL Ta \&Fl
.It Li 0x0000000000004000ULL Ta \&Fn
.It Li 0x0000000000008000ULL Ta \&Ft
.It Li 0x0000000000010000ULL Ta \&Fx
.It Li 0x0000000000020000ULL Ta \&Ic
.It Li 0x0000000000040000ULL Ta \&In
.It Li 0x0000000000080000ULL Ta \&Lb
.It Li 0x0000000000100000ULL Ta \&Li
.It Li 0x0000000000200000ULL Ta \&Lk
.It Li 0x0000000000400000ULL Ta \&Ms
.It Li 0x0000000000800000ULL Ta \&Mt
.It Li 0x0000000001000000ULL Ta \&Nd
.It Li 0x0000000002000000ULL Ta \&Nm
.It Li 0x0000000004000000ULL Ta \&Nx
.It Li 0x0000000008000000ULL Ta \&Ox
.It Li 0x0000000010000000ULL Ta \&Pa
.It Li 0x0000000020000000ULL Ta \&Rs
.It Li 0x0000000040000000ULL Ta \&Sh
.It Li 0x0000000080000000ULL Ta \&Ss
.It Li 0x0000000100000000ULL Ta \&St
.It Li 0x0000000200000000ULL Ta \&Sy
.It Li 0x0000000400000000ULL Ta \&Tn
.It Li 0x0000000800000000ULL Ta \&Va
.It Li 0x0000001000000000ULL Ta \&Vt
.It Li 0x0000002000000000ULL Ta \&Xr
.El
.Sh IMPLEMENTATION NOTES
The time to construct a new database pair grows linearly with the
number of keywords in the input files.
However, removing or updating entries with
.Fl u
or
.Fl d ,
respectively, grows as a multiple of the index length and input size.
.Sh FILES
.Bl -tag -width Ds
.It Pa mandoc.db
A
.Xr btree 3
keyword database mapping keywords to a type and file reference in
.Pa mandoc.index .
.It Pa mandoc.index
A
.Xr recno 3
database of indexed file-names.
.It Pa /etc/man.conf
The default
.Xr man 1
configuration file.
.El
.Sh EXIT STATUS
The
.Nm
utility exits with one of the following values:
.Pp
.Bl -tag -width Ds -compact
.It 0
No errors occurred.
.It 5
Invalid command line arguments were specified.
No input files have been read.
.It 6
An operating system error occurred, for example memory exhaustion or an
error accessing input files.
Such errors cause
.Nm
to exit at once, possibly in the middle of parsing or formatting a file.
The output databases are corrupt and should be removed.
.El
.Sh DIAGNOSTICS
If the following errors occur, the
.Nm
databases should be rebuilt.
.Bl -diag
.It "%s: Corrupt database"
The keyword database file indicated by
.Pa %s
is unreadable.
.It "%s: Corrupt index"
The index database file indicated by
.Pa %s
is unreadable.
.It "%s: Path too long"
The file
.Pa %s
is too long.
This usually indicates database corruption or invalid command-line
arguments.
.El
.Sh SEE ALSO
.Xr apropos 1 ,
.Xr man 1 ,
.Xr whatis 1 ,
.Xr btree 3 ,
.Xr recno 3 ,
.Xr man.conf 5
.Sh HISTORY
A
.Nm makewhatis
utility first appeared in
.Bx 2 .
It was rewritten in
.Xr perl 1
for
.Ox 2.7
and in C for
.Ox 5.1 .
.Pp
The
.Ar dir
argument first appeared in
.Nx 1.0 ;
the options
.Fl dtu
in
.Ox 2.7 ;
and the options
.Fl aCvW
in
.Ox 5.1 .
.Sh AUTHORS
.An -nosplit
.An Bill Joy
wrote the original
.Bx
.Nm makewhatis
in February 1979,
.An Marc Espie
started the Perl version in 2000,
and the current version of
.Nm
was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .

3855
mandocdb.c

File diff suppressed because it is too large Load Diff

View File

@ -1,62 +0,0 @@
/* $Id: mandocdb.h,v 1.6.2.1 2013/09/18 00:54:20 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* 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.
*/
#ifndef MANDOCDB_H
#define MANDOCDB_H
#define MANDOC_DB "mandoc.db"
#define MANDOC_IDX "mandoc.index"
#define TYPE_An 0x0000000000000001ULL
#define TYPE_Ar 0x0000000000000002ULL
#define TYPE_At 0x0000000000000004ULL
#define TYPE_Bsx 0x0000000000000008ULL
#define TYPE_Bx 0x0000000000000010ULL
#define TYPE_Cd 0x0000000000000020ULL
#define TYPE_Cm 0x0000000000000040ULL
#define TYPE_Dv 0x0000000000000080ULL
#define TYPE_Dx 0x0000000000000100ULL
#define TYPE_Em 0x0000000000000200ULL
#define TYPE_Er 0x0000000000000400ULL
#define TYPE_Ev 0x0000000000000800ULL
#define TYPE_Fa 0x0000000000001000ULL
#define TYPE_Fl 0x0000000000002000ULL
#define TYPE_Fn 0x0000000000004000ULL
#define TYPE_Ft 0x0000000000008000ULL
#define TYPE_Fx 0x0000000000010000ULL
#define TYPE_Ic 0x0000000000020000ULL
#define TYPE_In 0x0000000000040000ULL
#define TYPE_Lb 0x0000000000080000ULL
#define TYPE_Li 0x0000000000100000ULL
#define TYPE_Lk 0x0000000000200000ULL
#define TYPE_Ms 0x0000000000400000ULL
#define TYPE_Mt 0x0000000000800000ULL
#define TYPE_Nd 0x0000000001000000ULL
#define TYPE_Nm 0x0000000002000000ULL
#define TYPE_Nx 0x0000000004000000ULL
#define TYPE_Ox 0x0000000008000000ULL
#define TYPE_Pa 0x0000000010000000ULL
#define TYPE_Rs 0x0000000020000000ULL
#define TYPE_Sh 0x0000000040000000ULL
#define TYPE_Ss 0x0000000080000000ULL
#define TYPE_St 0x0000000100000000ULL
#define TYPE_Sy 0x0000000200000000ULL
#define TYPE_Tn 0x0000000400000000ULL
#define TYPE_Va 0x0000000800000000ULL
#define TYPE_Vt 0x0000001000000000ULL
#define TYPE_Xr 0x0000002000000000ULL
#endif /*!MANDOCDB_H */

190
manpage.c Normal file
View File

@ -0,0 +1,190 @@
/* $Id: manpage.c,v 1.7 2014/01/06 03:02:46 schwarze Exp $ */
/*
* Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <getopt.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "manpath.h"
#include "mansearch.h"
static void show(const char *, const char *);
int
main(int argc, char *argv[])
{
int ch, term;
size_t i, sz, len;
struct mansearch search;
struct manpage *res;
char *conf_file, *defpaths, *auxpaths, *cp;
char buf[PATH_MAX];
const char *cmd;
struct manpaths paths;
char *progname;
extern char *optarg;
extern int optind;
term = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO);
progname = strrchr(argv[0], '/');
if (progname == NULL)
progname = argv[0];
else
++progname;
auxpaths = defpaths = conf_file = NULL;
memset(&paths, 0, sizeof(struct manpaths));
memset(&search, 0, sizeof(struct mansearch));
while (-1 != (ch = getopt(argc, argv, "C:M:m:S:s:")))
switch (ch) {
case ('C'):
conf_file = optarg;
break;
case ('M'):
defpaths = optarg;
break;
case ('m'):
auxpaths = optarg;
break;
case ('S'):
search.arch = optarg;
break;
case ('s'):
search.sec = optarg;
break;
default:
goto usage;
}
argc -= optind;
argv += optind;
if (0 == argc)
goto usage;
search.deftype = TYPE_Nm | TYPE_Nd;
manpath_parse(&paths, conf_file, defpaths, auxpaths);
ch = mansearch(&search, &paths, argc, argv, "Nd", &res, &sz);
manpath_free(&paths);
if (0 == ch)
goto usage;
if (0 == sz) {
free(res);
return(EXIT_FAILURE);
} else if (1 == sz && term) {
i = 1;
goto show;
} else if (NULL == res)
return(EXIT_FAILURE);
for (i = 0; i < sz; i++) {
printf("%6zu %s: %s\n",
i + 1, res[i].names, res[i].output);
free(res[i].names);
free(res[i].output);
}
if (0 == term) {
for (i = 0; i < sz; i++)
free(res[i].file);
free(res);
return(EXIT_SUCCESS);
}
i = 1;
printf("Enter a choice [1]: ");
fflush(stdout);
if (NULL != (cp = fgetln(stdin, &len)))
if ('\n' == cp[--len] && len > 0) {
cp[len] = '\0';
if ((i = atoi(cp)) < 1 || i > sz)
i = 0;
}
if (0 == i) {
for (i = 0; i < sz; i++)
free(res[i].file);
free(res);
return(EXIT_SUCCESS);
}
show:
cmd = res[i - 1].form ? "mandoc" : "cat";
strlcpy(buf, res[i - 1].file, PATH_MAX);
for (i = 0; i < sz; i++)
free(res[i].file);
free(res);
show(cmd, buf);
/* NOTREACHED */
usage:
fprintf(stderr, "usage: %s [-C conf] "
"[-M paths] "
"[-m paths] "
"[-S arch] "
"[-s section] "
"expr ...\n",
progname);
return(EXIT_FAILURE);
}
static void
show(const char *cmd, const char *file)
{
int fds[2];
pid_t pid;
if (-1 == pipe(fds)) {
perror(NULL);
exit(EXIT_FAILURE);
}
if (-1 == (pid = fork())) {
perror(NULL);
exit(EXIT_FAILURE);
} else if (pid > 0) {
dup2(fds[0], STDIN_FILENO);
close(fds[1]);
cmd = NULL != getenv("MANPAGER") ?
getenv("MANPAGER") :
(NULL != getenv("PAGER") ?
getenv("PAGER") : "more");
execlp(cmd, cmd, (char *)NULL);
perror(cmd);
exit(EXIT_FAILURE);
}
dup2(fds[1], STDOUT_FILENO);
close(fds[0]);
execlp(cmd, cmd, file, (char *)NULL);
perror(cmd);
exit(EXIT_FAILURE);
}

View File

@ -1,4 +1,4 @@
/* $Id: manpath.c,v 1.12 2013/11/21 01:49:18 schwarze Exp $ */
/* $Id: manpath.c,v 1.15 2014/04/23 21:06:41 schwarze Exp $ */
/*
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
@ -26,7 +26,7 @@
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "manpath.h"
#define MAN_CONF_FILE "/etc/man.conf"
@ -169,9 +169,8 @@ manpath_add(struct manpaths *dirs, const char *dir)
if (0 == strcmp(dirs->paths[i], dir))
return;
dirs->paths = mandoc_realloc
(dirs->paths,
(dirs->sz + 1) * sizeof(char *));
dirs->paths = mandoc_reallocarray(dirs->paths,
dirs->sz + 1, sizeof(char *));
dirs->paths[dirs->sz++] = mandoc_strdup(cp);
}

228
mansearch.3 Normal file
View File

@ -0,0 +1,228 @@
.\" $Id: mansearch.3,v 1.2 2014/08/05 15:29:30 schwarze Exp $
.\"
.\" Copyright (c) 2014 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.
.\"
.Dd $Mdocdate: August 5 2014 $
.Dt MANSEARCH 3
.Os
.Sh NAME
.Nm mansearch ,
.Nm mansearch_setup
.Nd search manual page databases
.Sh SYNOPSIS
.In stdint.h
.In manpath.h
.In mansearch.h
.Ft int
.Fo mansearch_setup
.Fa "int start"
.Fc
.Ft int
.Fo mansearch
.Fa "const struct mansearch *search"
.Fa "const struct manpaths *paths"
.Fa "int argc"
.Fa "char *argv[]"
.Fa "const char *outkey"
.Fa "struct manpage **res"
.Fa "size_t *sz"
.Fc
.Sh DESCRIPTION
The
.Fn mansearch
function returns information about manuals matching a search query from a
.Xr mandoc.db 5
SQLite3 database.
.Pp
The query arguments are as follows:
.Bl -tag -width Ds
.It Fa "const struct mansearch *search"
Search options, defined in
.In mansearch.h .
.It Fa "const struct manpaths *paths"
Directories to be searched, defined in
.In manpath.h .
.It Fa "int argc" , "char *argv[]"
Search criteria, usually taken from the command line.
.El
.Pp
The
.Fa "const char *outkey"
selects which data to return in the
.Va output
field of the
.Fa res
structures.
It takes any of the macro keys defined in
.Pa mansearch_const.c
and described in
.Xr apropos 1 .
.Pp
The output arguments are as follows:
.Bl -tag -width Ds
.It Fa "struct manpage **res"
Returns a pointer to an array of result structures defined in
.In mansearch.h .
The user is expected to call
.Xr free 3
on the
.Va file ,
.Va names ,
and
.Va output
fields of all structures, as well as the
.Fa res
array itself.
.It Fa "size_t *sz"
Returns the number of result structures contained in
.Fa res .
.El
.Pp
To speed up searches, the
.Fn mansearch_setup
function can optionally be called with a
.Fa start
argument of 1 before
.Fn mansearch
to set up an SQLite3 pagecache.
If it was called, it has to be called again with a
.Fa start
argument of 0 after the last call to
.Fn mansearch
to release the memory used for the pagecache.
.Sh IMPLEMENTATION NOTES
For each manual page tree, the search is done in two steps.
In the first step, a list of pages matching the search criteria is built.
In the second step, the requested information about these pages is
retrieved from the database and assembled into the
.Fa res
array.
.Pp
All function mentioned here are defined in the file
.Pa mansearch.c .
No functions except
.Fn mansearch
and
.Fn sql_statement
build any SQL code, and no functions except
.Fn mansearch ,
.Fn buildnames ,
and
.Fn buildoutput
execute it.
.Ss Finding matches
The query is built using the following grammar:
.Bd -literal -offset indent
<query> ::= "SELECT * FROM mpages WHERE" <condition>
<condition> ::= "(" <condition> ")" |
<condition> "OR" <condition> |
<condition> "AND" <condition> |
"desc" <operator> "?" |
"id IN (SELECT pageid FROM" <subquery> ")"
<subquery> ::= "names WHERE name" <operator> "?" |
"keys WHERE key" <operator> "? AND bits & ?"
<operator> ::= "MATCH" | "REGEXP"
.Ed
.Pp
The MATCH and REGEXP operators are implemented by the functions
.Fn sql_match
and
.Fn sql_regexp ,
respectively.
This is required because SQLite3 natively neither supports
case-insensitive substring matching nor regular expression matching,
but only string identity, shell globbing, and the weird home-brewed
LIKE operator.
.Pp
Command line parsing is done by the function
.Fn exprcomp
building a singly linked list of
.Vt expr
structures, using the helper functions
.Fn exprterm
and
.Fn exprspec .
The resulting SQL statement is assembled by the function
.Fn sql_statement
and evaluated in the main loop of the
.Fn mansearch
function.
.Ss Assembling the results
The names, sections, and architectures of the manuals found
are assembled into the
.Va names
field of the result structure by the function
.Fn buildnames ,
using the following query:
.Pp
.Dl "SELECT * FROM mlinks WHERE pageid=? ORDER BY sec, arch, name"
.Pp
If the
.Fa outkey
differs from
.Qq Nd ,
the requested output data is assembled into the
.Va output
field of the result structure by the function
.Fn buildoutput ,
using the following query:
.Pp
.Dl "SELECT * FROM keys WHERE pageid=? AND bits & ?"
.Sh FILES
.Bl -tag -width mandoc.db -compact
.It Pa mandoc.db
The manual page database.
.El
.Sh EXAMPLES
The simplest invocation
.Pp
.Dl apropos keyword
.Pp
results in the following SQL query:
.Bd -literal
SELECT * FROM mpages WHERE (
id IN (SELECT pageid FROM names WHERE name MATCH 'keyword') OR
desc MATCH 'keyword'
);
.Ed
.Pp
A more complicated request like
.Pp
.Dl apropos -s 2 Nm,Xr=getuid
.Pp
results in:
.Bd -literal
SELECT * FROM mpages WHERE (
id IN (SELECT pageid FROM names WHERE name MATCH 'getuid') OR
id IN (SELECT pageid FROM keys WHERE key MATCH 'getuid' AND bits & 4)
) AND id IN (SELECT pageid FROM keys WHERE key REGEXP '^2$' AND bits & 2);
.Ed
.Sh SEE ALSO
.Xr apropos 1 ,
.Xr mandoc.db 5 ,
.Xr makewhatis 8
.Sh HISTORY
The
.Fn mansearch
subsystem first appeared in
.Ox 5.6 .
.Sh AUTHORS
.An -nosplit
A module to search manual page databases was first written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
in 2011, at first using the Berkeley DB;
he rewrote it for SQLite3 in 2012.
The current version received major changes from
.An Ingo Schwarze Aq Mt schwarze@openbsd.org .

861
mansearch.c Normal file
View File

@ -0,0 +1,861 @@
/* $Id: mansearch.c,v 1.42 2014/08/09 14:24:53 schwarze Exp $ */
/*
* Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/mman.h>
#include <assert.h>
#include <fcntl.h>
#include <getopt.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>
#ifdef HAVE_OHASH
#include <ohash.h>
#else
#include "compat_ohash.h"
#endif
#include <sqlite3.h>
#ifndef SQLITE_DETERMINISTIC
#define SQLITE_DETERMINISTIC 0
#endif
#include "mandoc.h"
#include "mandoc_aux.h"
#include "manpath.h"
#include "mansearch.h"
extern int mansearch_keymax;
extern const char *const mansearch_keynames[];
#define SQL_BIND_TEXT(_db, _s, _i, _v) \
do { if (SQLITE_OK != sqlite3_bind_text \
((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \
fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \
} while (0)
#define SQL_BIND_INT64(_db, _s, _i, _v) \
do { if (SQLITE_OK != sqlite3_bind_int64 \
((_s), (_i)++, (_v))) \
fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \
} while (0)
#define SQL_BIND_BLOB(_db, _s, _i, _v) \
do { if (SQLITE_OK != sqlite3_bind_blob \
((_s), (_i)++, (&_v), sizeof(_v), SQLITE_STATIC)) \
fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \
} while (0)
struct expr {
regex_t regexp; /* compiled regexp, if applicable */
const char *substr; /* to search for, if applicable */
struct expr *next; /* next in sequence */
uint64_t bits; /* type-mask */
int equal; /* equality, not subsring match */
int open; /* opening parentheses before */
int and; /* logical AND before */
int close; /* closing parentheses after */
};
struct match {
uint64_t pageid; /* identifier in database */
char *desc; /* manual page description */
int form; /* 0 == catpage */
};
static void buildnames(struct manpage *, sqlite3 *,
sqlite3_stmt *, uint64_t,
const char *, int form);
static char *buildoutput(sqlite3 *, sqlite3_stmt *,
uint64_t, uint64_t);
static void *hash_alloc(size_t, void *);
static void hash_free(void *, void *);
static void *hash_calloc(size_t, size_t, void *);
static struct expr *exprcomp(const struct mansearch *,
int, char *[]);
static void exprfree(struct expr *);
static struct expr *exprspec(struct expr *, uint64_t,
const char *, const char *);
static struct expr *exprterm(const struct mansearch *, char *, int);
static int manpage_compare(const void *, const void *);
static void sql_append(char **sql, size_t *sz,
const char *newstr, int count);
static void sql_match(sqlite3_context *context,
int argc, sqlite3_value **argv);
static void sql_regexp(sqlite3_context *context,
int argc, sqlite3_value **argv);
static char *sql_statement(const struct expr *);
int
mansearch_setup(int start)
{
static void *pagecache;
int c;
#define PC_PAGESIZE 1280
#define PC_NUMPAGES 256
if (start) {
if (NULL != pagecache) {
fprintf(stderr, "pagecache already enabled\n");
return((int)MANDOCLEVEL_BADARG);
}
pagecache = mmap(NULL, PC_PAGESIZE * PC_NUMPAGES,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANON, -1, 0);
if (MAP_FAILED == pagecache) {
perror("mmap");
pagecache = NULL;
return((int)MANDOCLEVEL_SYSERR);
}
c = sqlite3_config(SQLITE_CONFIG_PAGECACHE,
pagecache, PC_PAGESIZE, PC_NUMPAGES);
if (SQLITE_OK == c)
return((int)MANDOCLEVEL_OK);
fprintf(stderr, "pagecache: %s\n", sqlite3_errstr(c));
} else if (NULL == pagecache) {
fprintf(stderr, "pagecache missing\n");
return((int)MANDOCLEVEL_BADARG);
}
if (-1 == munmap(pagecache, PC_PAGESIZE * PC_NUMPAGES)) {
perror("munmap");
pagecache = NULL;
return((int)MANDOCLEVEL_SYSERR);
}
pagecache = NULL;
return((int)MANDOCLEVEL_OK);
}
int
mansearch(const struct mansearch *search,
const struct manpaths *paths,
int argc, char *argv[],
const char *outkey,
struct manpage **res, size_t *sz)
{
int fd, rc, c, indexbit;
int64_t pageid;
uint64_t outbit, iterbit;
char buf[PATH_MAX];
char *sql;
struct manpage *mpage;
struct expr *e, *ep;
sqlite3 *db;
sqlite3_stmt *s, *s2;
struct match *mp;
struct ohash_info info;
struct ohash htab;
unsigned int idx;
size_t i, j, cur, maxres;
info.calloc = hash_calloc;
info.alloc = hash_alloc;
info.free = hash_free;
info.key_offset = offsetof(struct match, pageid);
*sz = cur = maxres = 0;
sql = NULL;
*res = NULL;
fd = -1;
e = NULL;
rc = 0;
if (0 == argc)
goto out;
if (NULL == (e = exprcomp(search, argc, argv)))
goto out;
outbit = 0;
if (NULL != outkey) {
for (indexbit = 0, iterbit = 1;
indexbit < mansearch_keymax;
indexbit++, iterbit <<= 1) {
if (0 == strcasecmp(outkey,
mansearch_keynames[indexbit])) {
outbit = iterbit;
break;
}
}
}
/*
* Save a descriptor to the current working directory.
* Since pathnames in the "paths" variable might be relative,
* and we'll be chdir()ing into them, we need to keep a handle
* on our current directory from which to start the chdir().
*/
if (NULL == getcwd(buf, PATH_MAX)) {
perror("getcwd");
goto out;
} else if (-1 == (fd = open(buf, O_RDONLY, 0))) {
perror(buf);
goto out;
}
sql = sql_statement(e);
/*
* 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.
*/
for (i = 0; i < paths->sz; i++) {
if (-1 == fchdir(fd)) {
perror(buf);
free(*res);
break;
} else if (-1 == chdir(paths->paths[i])) {
perror(paths->paths[i]);
continue;
}
c = sqlite3_open_v2(MANDOC_DB, &db,
SQLITE_OPEN_READONLY, NULL);
if (SQLITE_OK != c) {
perror(MANDOC_DB);
sqlite3_close(db);
continue;
}
/*
* Define the SQL functions for substring
* and regular expression matching.
*/
c = sqlite3_create_function(db, "match", 2,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
NULL, sql_match, NULL, NULL);
assert(SQLITE_OK == c);
c = sqlite3_create_function(db, "regexp", 2,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
NULL, sql_regexp, NULL, NULL);
assert(SQLITE_OK == c);
j = 1;
c = sqlite3_prepare_v2(db, sql, -1, &s, NULL);
if (SQLITE_OK != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
for (ep = e; NULL != ep; ep = ep->next) {
if (NULL == ep->substr) {
SQL_BIND_BLOB(db, s, j, ep->regexp);
} else
SQL_BIND_TEXT(db, s, j, ep->substr);
if (0 == ((TYPE_Nd | TYPE_Nm) & ep->bits))
SQL_BIND_INT64(db, s, j, ep->bits);
}
memset(&htab, 0, sizeof(struct ohash));
ohash_init(&htab, 4, &info);
/*
* Hash each entry on its [unique] document identifier.
* This is a uint64_t.
* Instead of using a hash function, simply convert the
* uint64_t to a uint32_t, the hash value's type.
* This gives good performance and preserves the
* distribution of buckets in the table.
*/
while (SQLITE_ROW == (c = sqlite3_step(s))) {
pageid = sqlite3_column_int64(s, 2);
idx = ohash_lookup_memory(&htab,
(char *)&pageid, sizeof(uint64_t),
(uint32_t)pageid);
if (NULL != ohash_find(&htab, idx))
continue;
mp = mandoc_calloc(1, sizeof(struct match));
mp->pageid = pageid;
mp->form = sqlite3_column_int(s, 1);
if (TYPE_Nd == outbit)
mp->desc = mandoc_strdup((const char *)
sqlite3_column_text(s, 0));
ohash_insert(&htab, idx, mp);
}
if (SQLITE_DONE != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
sqlite3_finalize(s);
c = sqlite3_prepare_v2(db,
"SELECT sec, arch, name, pageid FROM mlinks "
"WHERE pageid=? ORDER BY sec, arch, name",
-1, &s, NULL);
if (SQLITE_OK != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
c = sqlite3_prepare_v2(db,
"SELECT bits, key, pageid FROM keys "
"WHERE pageid=? AND bits & ?",
-1, &s2, NULL);
if (SQLITE_OK != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
for (mp = ohash_first(&htab, &idx);
NULL != mp;
mp = ohash_next(&htab, &idx)) {
if (cur + 1 > maxres) {
maxres += 1024;
*res = mandoc_reallocarray(*res,
maxres, sizeof(struct manpage));
}
mpage = *res + cur;
mpage->sec = 10;
mpage->form = mp->form;
buildnames(mpage, db, s, mp->pageid,
paths->paths[i], mp->form);
mpage->output = TYPE_Nd & outbit ?
mp->desc : outbit ?
buildoutput(db, s2, mp->pageid, outbit) : NULL;
free(mp);
cur++;
}
sqlite3_finalize(s);
sqlite3_finalize(s2);
sqlite3_close(db);
ohash_delete(&htab);
}
qsort(*res, cur, sizeof(struct manpage), manpage_compare);
rc = 1;
out:
if (-1 != fd) {
if (-1 == fchdir(fd))
perror(buf);
close(fd);
}
exprfree(e);
free(sql);
*sz = cur;
return(rc);
}
static int
manpage_compare(const void *vp1, const void *vp2)
{
const struct manpage *mp1, *mp2;
int diff;
mp1 = vp1;
mp2 = vp2;
diff = mp1->sec - mp2->sec;
return(diff ? diff : strcasecmp(mp1->names, mp2->names));
}
static void
buildnames(struct manpage *mpage, sqlite3 *db, sqlite3_stmt *s,
uint64_t pageid, const char *path, int form)
{
char *newnames, *prevsec, *prevarch;
const char *oldnames, *sep1, *name, *sec, *sep2, *arch, *fsec;
size_t i;
int c;
mpage->file = NULL;
mpage->names = NULL;
prevsec = prevarch = NULL;
i = 1;
SQL_BIND_INT64(db, s, i, pageid);
while (SQLITE_ROW == (c = sqlite3_step(s))) {
/* Decide whether we already have some names. */
if (NULL == mpage->names) {
oldnames = "";
sep1 = "";
} else {
oldnames = mpage->names;
sep1 = ", ";
}
/* Fetch the next name. */
sec = (const char *)sqlite3_column_text(s, 0);
arch = (const char *)sqlite3_column_text(s, 1);
name = (const char *)sqlite3_column_text(s, 2);
/* Remember the first section found. */
if (9 < mpage->sec && '1' <= *sec && '9' >= *sec)
mpage->sec = (*sec - '1') + 1;
/* If the section changed, append the old one. */
if (NULL != prevsec &&
(strcmp(sec, prevsec) ||
strcmp(arch, prevarch))) {
sep2 = '\0' == *prevarch ? "" : "/";
mandoc_asprintf(&newnames, "%s(%s%s%s)",
oldnames, prevsec, sep2, prevarch);
free(mpage->names);
oldnames = mpage->names = newnames;
free(prevsec);
free(prevarch);
prevsec = prevarch = NULL;
}
/* Save the new section, to append it later. */
if (NULL == prevsec) {
prevsec = mandoc_strdup(sec);
prevarch = mandoc_strdup(arch);
}
/* Append the new name. */
mandoc_asprintf(&newnames, "%s%s%s",
oldnames, sep1, name);
free(mpage->names);
mpage->names = newnames;
/* Also save the first file name encountered. */
if (NULL != mpage->file)
continue;
if (form) {
sep1 = "man";
fsec = sec;
} else {
sep1 = "cat";
fsec = "0";
}
sep2 = '\0' == *arch ? "" : "/";
mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.%s",
path, sep1, sec, sep2, arch, name, fsec);
}
if (SQLITE_DONE != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
sqlite3_reset(s);
/* Append one final section to the names. */
if (NULL != prevsec) {
sep2 = '\0' == *prevarch ? "" : "/";
mandoc_asprintf(&newnames, "%s(%s%s%s)",
mpage->names, prevsec, sep2, prevarch);
free(mpage->names);
mpage->names = newnames;
free(prevsec);
free(prevarch);
}
}
static char *
buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t pageid, uint64_t outbit)
{
char *output, *newoutput;
const char *oldoutput, *sep1, *data;
size_t i;
int c;
output = NULL;
i = 1;
SQL_BIND_INT64(db, s, i, pageid);
SQL_BIND_INT64(db, s, i, outbit);
while (SQLITE_ROW == (c = sqlite3_step(s))) {
if (NULL == output) {
oldoutput = "";
sep1 = "";
} else {
oldoutput = output;
sep1 = " # ";
}
data = (const char *)sqlite3_column_text(s, 1);
mandoc_asprintf(&newoutput, "%s%s%s",
oldoutput, sep1, data);
free(output);
output = newoutput;
}
if (SQLITE_DONE != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
sqlite3_reset(s);
return(output);
}
/*
* Implement substring match as an application-defined SQL function.
* Using the SQL LIKE or GLOB operators instead would be a bad idea
* because that would require escaping metacharacters in the string
* being searched for.
*/
static void
sql_match(sqlite3_context *context, int argc, sqlite3_value **argv)
{
assert(2 == argc);
sqlite3_result_int(context, NULL != strcasestr(
(const char *)sqlite3_value_text(argv[1]),
(const char *)sqlite3_value_text(argv[0])));
}
/*
* Implement regular expression match
* as an application-defined SQL function.
*/
static void
sql_regexp(sqlite3_context *context, int argc, sqlite3_value **argv)
{
assert(2 == argc);
sqlite3_result_int(context, !regexec(
(regex_t *)sqlite3_value_blob(argv[0]),
(const char *)sqlite3_value_text(argv[1]),
0, NULL, 0));
}
static void
sql_append(char **sql, size_t *sz, const char *newstr, int count)
{
size_t newsz;
newsz = 1 < count ? (size_t)count : strlen(newstr);
*sql = mandoc_realloc(*sql, *sz + newsz + 1);
if (1 < count)
memset(*sql + *sz, *newstr, (size_t)count);
else
memcpy(*sql + *sz, newstr, newsz);
*sz += newsz;
(*sql)[*sz] = '\0';
}
/*
* Prepare the search SQL statement.
*/
static char *
sql_statement(const struct expr *e)
{
char *sql;
size_t sz;
int needop;
sql = mandoc_strdup(
"SELECT desc, form, pageid FROM mpages WHERE ");
sz = strlen(sql);
for (needop = 0; NULL != e; e = e->next) {
if (e->and)
sql_append(&sql, &sz, " AND ", 1);
else if (needop)
sql_append(&sql, &sz, " OR ", 1);
if (e->open)
sql_append(&sql, &sz, "(", e->open);
sql_append(&sql, &sz,
TYPE_Nd & e->bits
? (NULL == e->substr
? "desc REGEXP ?"
: "desc MATCH ?")
: TYPE_Nm == e->bits
? (NULL == e->substr
? "pageid IN (SELECT pageid FROM names "
"WHERE name REGEXP ?)"
: e->equal
? "pageid IN (SELECT pageid FROM names "
"WHERE name = ?)"
: "pageid IN (SELECT pageid FROM names "
"WHERE name MATCH ?)")
: (NULL == e->substr
? "pageid IN (SELECT pageid FROM keys "
"WHERE key REGEXP ? AND bits & ?)"
: "pageid IN (SELECT pageid FROM keys "
"WHERE key MATCH ? AND bits & ?)"), 1);
if (e->close)
sql_append(&sql, &sz, ")", e->close);
needop = 1;
}
return(sql);
}
/*
* 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[])
{
uint64_t mask;
int i, toopen, logic, igncase, toclose;
struct expr *first, *prev, *cur, *next;
first = cur = NULL;
logic = igncase = toclose = 0;
toopen = NULL != search->sec || NULL != search->arch;
for (i = 0; i < argc; i++) {
if (0 == strcmp("(", argv[i])) {
if (igncase)
goto fail;
toopen++;
toclose++;
continue;
} else if (0 == strcmp(")", argv[i])) {
if (toopen || logic || igncase || NULL == cur)
goto fail;
cur->close++;
if (0 > --toclose)
goto fail;
continue;
} else if (0 == strcmp("-a", argv[i])) {
if (toopen || logic || igncase || NULL == cur)
goto fail;
logic = 1;
continue;
} else if (0 == strcmp("-o", argv[i])) {
if (toopen || logic || igncase || NULL == cur)
goto fail;
logic = 2;
continue;
} else if (0 == strcmp("-i", argv[i])) {
if (igncase)
goto fail;
igncase = 1;
continue;
}
next = exprterm(search, argv[i], !igncase);
if (NULL == next)
goto fail;
if (NULL == first)
first = next;
else
cur->next = next;
prev = cur = next;
/*
* Searching for descriptions must be split out
* because they are stored in the mpages table,
* not in the keys table.
*/
for (mask = TYPE_Nm; mask <= TYPE_Nd; mask <<= 1) {
if (mask & cur->bits && ~mask & cur->bits) {
next = mandoc_calloc(1,
sizeof(struct expr));
memcpy(next, cur, sizeof(struct expr));
prev->open = 1;
cur->bits = mask;
cur->next = next;
cur = next;
cur->bits &= ~mask;
}
}
prev->and = (1 == logic);
prev->open += toopen;
if (cur != prev)
cur->close = 1;
toopen = logic = igncase = 0;
}
if (toopen || logic || igncase || toclose)
goto fail;
if (NULL != search->sec || NULL != search->arch)
cur->close++;
if (NULL != search->arch)
cur = exprspec(cur, TYPE_arch, search->arch, "^(%s|any)$");
if (NULL != search->sec)
exprspec(cur, TYPE_sec, search->sec, "^%s$");
return(first);
fail:
if (NULL != first)
exprfree(first);
return(NULL);
}
static struct expr *
exprspec(struct expr *cur, uint64_t key, const char *value,
const char *format)
{
char errbuf[BUFSIZ];
char *cp;
int irc;
mandoc_asprintf(&cp, format, value);
cur->next = mandoc_calloc(1, sizeof(struct expr));
cur = cur->next;
cur->and = 1;
cur->bits = key;
if (0 != (irc = regcomp(&cur->regexp, cp,
REG_EXTENDED | REG_NOSUB | REG_ICASE))) {
regerror(irc, &cur->regexp, errbuf, sizeof(errbuf));
fprintf(stderr, "regcomp: %s\n", errbuf);
cur->substr = value;
}
free(cp);
return(cur);
}
static struct expr *
exprterm(const struct mansearch *search, char *buf, int cs)
{
char errbuf[BUFSIZ];
struct expr *e;
char *key, *val;
uint64_t iterbit;
int i, irc;
if ('\0' == *buf)
return(NULL);
e = mandoc_calloc(1, sizeof(struct expr));
if (MANSEARCH_MAN & search->flags) {
e->bits = search->deftype;
e->substr = buf;
e->equal = 1;
return(e);
}
/*
* Look for an '=' or '~' operator,
* unless forced to some fixed macro keys.
*/
if (MANSEARCH_WHATIS & search->flags)
val = NULL;
else
val = strpbrk(buf, "=~");
if (NULL == val) {
e->bits = search->deftype;
e->substr = buf;
/*
* Found an operator.
* Regexp search is requested by !e->substr.
*/
} else {
if (val == buf)
e->bits = search->deftype;
if ('=' == *val)
e->substr = val + 1;
*val++ = '\0';
if (NULL != strstr(buf, "arch"))
cs = 0;
}
/* Compile regular expressions. */
if (MANSEARCH_WHATIS & search->flags) {
e->substr = NULL;
mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf);
}
if (NULL == e->substr) {
irc = regcomp(&e->regexp, val,
REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE));
if (MANSEARCH_WHATIS & search->flags)
free(val);
if (irc) {
regerror(irc, &e->regexp, errbuf, sizeof(errbuf));
fprintf(stderr, "regcomp: %s\n", errbuf);
free(e);
return(NULL);
}
}
if (e->bits)
return(e);
/*
* Parse out all possible fields.
* If the field doesn't resolve, bail.
*/
while (NULL != (key = strsep(&buf, ","))) {
if ('\0' == *key)
continue;
for (i = 0, iterbit = 1;
i < mansearch_keymax;
i++, iterbit <<= 1) {
if (0 == strcasecmp(key,
mansearch_keynames[i])) {
e->bits |= iterbit;
break;
}
}
if (i == mansearch_keymax) {
if (strcasecmp(key, "any")) {
free(e);
return(NULL);
}
e->bits |= ~0ULL;
}
}
return(e);
}
static void
exprfree(struct expr *p)
{
struct expr *pp;
while (NULL != p) {
pp = p->next;
free(p);
p = pp;
}
}
static void *
hash_calloc(size_t nmemb, size_t sz, void *arg)
{
return(mandoc_calloc(nmemb, sz));
}
static void *
hash_alloc(size_t sz, void *arg)
{
return(mandoc_malloc(sz));
}
static void
hash_free(void *p, void *arg)
{
free(p);
}

101
mansearch.h Normal file
View File

@ -0,0 +1,101 @@
/* $Id: mansearch.h,v 1.15 2014/07/24 20:30:45 schwarze Exp $ */
/*
* Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014 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.
*/
#ifndef MANSEARCH_H
#define MANSEARCH_H
#define MANDOC_DB "mandoc.db"
#define TYPE_arch 0x0000000000000001ULL
#define TYPE_sec 0x0000000000000002ULL
#define TYPE_Xr 0x0000000000000004ULL
#define TYPE_Ar 0x0000000000000008ULL
#define TYPE_Fa 0x0000000000000010ULL
#define TYPE_Fl 0x0000000000000020ULL
#define TYPE_Dv 0x0000000000000040ULL
#define TYPE_Fn 0x0000000000000080ULL
#define TYPE_Ic 0x0000000000000100ULL
#define TYPE_Pa 0x0000000000000200ULL
#define TYPE_Cm 0x0000000000000400ULL
#define TYPE_Li 0x0000000000000800ULL
#define TYPE_Em 0x0000000000001000ULL
#define TYPE_Cd 0x0000000000002000ULL
#define TYPE_Va 0x0000000000004000ULL
#define TYPE_Ft 0x0000000000008000ULL
#define TYPE_Tn 0x0000000000010000ULL
#define TYPE_Er 0x0000000000020000ULL
#define TYPE_Ev 0x0000000000040000ULL
#define TYPE_Sy 0x0000000000080000ULL
#define TYPE_Sh 0x0000000000100000ULL
#define TYPE_In 0x0000000000200000ULL
#define TYPE_Ss 0x0000000000400000ULL
#define TYPE_Ox 0x0000000000800000ULL
#define TYPE_An 0x0000000001000000ULL
#define TYPE_Mt 0x0000000002000000ULL
#define TYPE_St 0x0000000004000000ULL
#define TYPE_Bx 0x0000000008000000ULL
#define TYPE_At 0x0000000010000000ULL
#define TYPE_Nx 0x0000000020000000ULL
#define TYPE_Fx 0x0000000040000000ULL
#define TYPE_Lk 0x0000000080000000ULL
#define TYPE_Ms 0x0000000100000000ULL
#define TYPE_Bsx 0x0000000200000000ULL
#define TYPE_Dx 0x0000000400000000ULL
#define TYPE_Rs 0x0000000800000000ULL
#define TYPE_Vt 0x0000001000000000ULL
#define TYPE_Lb 0x0000002000000000ULL
#define TYPE_Nm 0x0000004000000000ULL
#define TYPE_Nd 0x0000008000000000ULL
#define NAME_SYN 0x0000004000000001ULL
#define NAME_FILE 0x0000004000000002ULL
#define NAME_TITLE 0x000000400000000cULL
#define NAME_FIRST 0x0000004000000008ULL
#define NAME_HEAD 0x0000004000000010ULL
#define NAME_MASK 0x000000000000001fULL
__BEGIN_DECLS
struct manpage {
char *file; /* to be prefixed by manpath */
char *names; /* a list of names with sections */
char *output; /* user-defined additional output */
int sec; /* section number, 10 means invalid */
int form; /* 0 == catpage */
};
struct mansearch {
const char *arch; /* architecture/NULL */
const char *sec; /* mansection/NULL */
uint64_t deftype; /* type if no key */
int flags;
#define MANSEARCH_WHATIS 0x01 /* whatis(1) mode: whole words, no keys */
#define MANSEARCH_MAN 0x02 /* man(1) mode: string equality, no keys */
};
int mansearch_setup(int);
int mansearch(const struct mansearch *cfg, /* options */
const struct manpaths *paths, /* manpaths */
int argc, /* size of argv */
char *argv[], /* search terms */
const char *outkey, /* name of additional output key */
struct manpage **res, /* results */
size_t *ressz); /* results returned */
__END_DECLS
#endif /*!MANSEARCH_H*/

35
mansearch_const.c Normal file
View File

@ -0,0 +1,35 @@
/* $Id: mansearch_const.c,v 1.5 2014/08/09 14:05:21 schwarze Exp $ */
/*
* Copyright (c) 2014 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <stdint.h>
#include "manpath.h"
#include "mansearch.h"
const int mansearch_keymax = 40;
const char *const mansearch_keynames[40] = {
"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"
};

224
mchars_alloc.3 Normal file
View File

@ -0,0 +1,224 @@
.\" $Id: mchars_alloc.3,v 1.1 2014/08/05 05:48:56 schwarze Exp $
.\"
.\" Copyright (c) 2014 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.
.\"
.Dd $Mdocdate: August 5 2014 $
.Dt MCHARS_ALLOC 3
.Os
.Sh NAME
.Nm mchars_alloc ,
.Nm mchars_free ,
.Nm mchars_num2char ,
.Nm mchars_num2uc ,
.Nm mchars_spec2cp ,
.Nm mchars_spec2str
.Nd character table for mandoc
.Sh LIBRARY
.Lb libmandoc
.Sh SYNOPSIS
.In sys/types.h
.In mandoc.h
.Ft "struct mchars *"
.Fn mchars_alloc "void"
.Ft void
.Fo mchars_free
.Fa "struct mchars *table"
.Fc
.Ft char
.Fo mchars_num2char
.Fa "const char *decimal"
.Fa "size_t sz"
.Fc
.Ft int
.Fo mchars_num2uc
.Fa "const char *hexadecimal"
.Fa "size_t sz"
.Fc
.Ft int
.Fo mchars_spec2cp
.Fa "const struct mchars *table"
.Fa "const char *name"
.Fa "size_t sz"
.Fc
.Ft "const char *"
.Fo mchars_spec2str
.Fa "const struct mchars *table"
.Fa "const char *name"
.Fa "size_t sz"
.Fa "size_t *rsz"
.Fc
.Sh DESCRIPTION
These functions translate Unicode character numbers and
.Xr roff 7
character names into glyphs.
See
.Xr mandoc_char 7
for a list of
.Xr roff 7
special characters.
These functions are intended for external use by programs formatting
.Xr mdoc 7
and
.Xr man 7
pages for output, for example the
.Xr mandoc 1
output formatter modules and
.Xr makewhatis 8 .
The
.Fa decimal ,
.Fa hexadecimal ,
.Fa name ,
and
.Fa size
input arguments are usually obtained from the
.Xr mandoc_escape 3
parser function.
.Pp
The function
.Fn mchars_num2char
converts a
.Fa decimal
string representation of a character number consisting of
.Fa sz
digits into a printable ASCII character.
If the input string is non-numeric or does not represent a printable
ASCII character, the NUL character
.Pq Sq \e0
is returned.
For example, the
.Xr mandoc 1
.Fl Tascii ,
.Fl Tutf8 ,
and
.Fl Thtml
output modules use this function to render
.Xr roff 7
.Ic \eN
escape sequences.
.Pp
The function
.Fn mchars_num2uc
converts a
.Fa hexadecimal
string representation of a Unicode codepoint consisting of
.Fa sz
digits into an integer representation.
If the input string is non-numeric or represents an ASCII character,
the NUL character
.Pq Sq \e0
is returned.
For example, the
.Xr mandoc 1
.Fl Tutf8
and
.Fl Thtml
output modules use this function to render
.Xr roff 7
.Ic \e[u Ns Ar XXXX Ns Ic \&]
and
.Ic \eC\(aqu Ns Ar XXXX Ns Ic \(aq
escape sequences.
.Pp
The function
.Fn mchars_alloc
allocates an opaque
.Vt "struct mchars *"
table object for subsequent use by the following two lookup functions.
When no longer needed, this object can be destroyed with
.Fn mchars_free .
.Pp
The function
.Fn mchars_spec2cp
looks up a
.Xr roff 7
special character
.Fa name
consisting of
.Fa sz
characters in the
.Fa table
and returns the corresponding Unicode codepoint.
If the
.Ar name
is not recognized, \-1 is returned.
For example, the
.Xr mandoc 1
.Fl Tutf8
and
.Fl Thtml
output modules use this function to render
.Xr roff 7
.Ic \e[ Ns Ar name Ns Ic \&]
and
.Ic \eC\(aq Ns Ar name Ns Ic \(aq
escape sequences.
.Pp
The function
.Fn mchars_spec2str
looks up a
.Xr roff 7
special character
.Fa name
consisting of
.Fa sz
characters in the
.Fa table
and returns an ASCII string representation.
The length of the representation is returned in
.Fa rsz .
In many cases, the meaning of such ASCII representations
is not quite obvious, so using
.Xr roff 7
special characters in documents intended for ASCII rendering
is usually a bad idea.
If the
.Ar name
is not recognized,
.Dv NULL
is returned.
For example,
.Xr makewhatis 8
and the
.Xr mandoc 1
.Fl Tascii
output module use this function to render
.Xr roff 7
.Ic \e[ Ns Ar name Ns Ic \&]
and
.Ic \eC\(aq Ns Ar name Ns Ic \(aq
escape sequences.
.Sh FILES
These funtions are implemented in the file
.Pa chars.c .
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr mandoc_escape 3 ,
.Xr mandoc_char 7 ,
.Xr roff 7
.Sh HISTORY
These functions and their predecessors have been available since the
following mandoc versions:
.Bl -column "mchars_num2char()" "1.11.3" "chars_num2char()" "1.10.10"
.It Sy function Ta since Ta Sy predecessor Ta since
.It Fn mchars_alloc Ta 1.11.3 Ta Fn ascii2htab Ta 1.5.3
.It Fn mchars_free Ta 1.11.2 Ta Fn asciifree Ta 1.6.0
.It Fn mchars_num2char Ta 1.11.2 Ta Fn chars_num2char Ta 1.10.10
.It Fn mchars_num2uc Ta 1.11.3 Ta \(em Ta \(em
.It Fn mchars_spec2cp Ta 1.11.2 Ta Fn chars_spec2cp Ta 1.10.5
.It Fn mchars_spec2str Ta 1.11.2 Ta Fn a2ascii Ta 1.5.3
.El
.Sh AUTHORS
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
.An Ingo Schwarze Aq Mt schwarze@openbsd.org

608
mdoc.7
View File

@ -1,4 +1,4 @@
.\" $Id: mdoc.7,v 1.223 2013/12/25 14:09:32 schwarze Exp $
.\" $Id: mdoc.7,v 1.234 2014/08/08 16:38:06 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010, 2011, 2013 Ingo Schwarze <schwarze@openbsd.org>
@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: December 25 2013 $
.Dd $Mdocdate: August 8 2014 $
.Dt MDOC 7
.Os
.Sh NAME
@ -125,7 +125,7 @@ file for a utility
\&.Nm progname
\&.Nd one line about what it does
\&.\e\(dq .Sh LIBRARY
\&.\e\(dq For sections 2, 3, & 9 only.
\&.\e\(dq For sections 2, 3, and 9 only.
\&.\e\(dq Not used in OpenBSD.
\&.Sh SYNOPSIS
\&.Nm progname
@ -135,20 +135,22 @@ file for a utility
The
\&.Nm
utility processes files ...
\&.\e\(dq .Sh CONTEXT
\&.\e\(dq For section 9 functions only.
\&.\e\(dq .Sh IMPLEMENTATION NOTES
\&.\e\(dq Not used in OpenBSD.
\&.\e\(dq .Sh RETURN VALUES
\&.\e\(dq For sections 2, 3, & 9 only.
\&.\e\(dq For sections 2, 3, and 9 function return values only.
\&.\e\(dq .Sh ENVIRONMENT
\&.\e\(dq For sections 1, 6, 7, & 8 only.
\&.\e\(dq For sections 1, 6, 7, and 8 only.
\&.\e\(dq .Sh FILES
\&.\e\(dq .Sh EXIT STATUS
\&.\e\(dq For sections 1, 6, & 8 only.
\&.\e\(dq For sections 1, 6, and 8 only.
\&.\e\(dq .Sh EXAMPLES
\&.\e\(dq .Sh DIAGNOSTICS
\&.\e\(dq For sections 1, 4, 6, 7, & 8 only.
\&.\e\(dq For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only.
\&.\e\(dq .Sh ERRORS
\&.\e\(dq For sections 2, 3, & 9 only.
\&.\e\(dq For sections 2, 3, 4, and 9 errno settings only.
\&.\e\(dq .Sh SEE ALSO
\&.\e\(dq .Xr foobar 1
\&.\e\(dq .Sh STANDARDS
@ -318,6 +320,9 @@ macro followed by a non-standard section name, and each having
several subsections, like in the present
.Nm
manual.
.It Em CONTEXT
This section lists the contexts in which functions can be called in section 9.
The contexts are autoconf, process, or interrupt.
.It Em IMPLEMENTATION NOTES
Implementation-specific notes should be kept here.
This is useful when implementing standard functions that may have side
@ -358,8 +363,12 @@ Example usages.
This often contains snippets of well-formed, well-tested invocations.
Make sure that examples work properly!
.It Em DIAGNOSTICS
Documents error conditions.
This is most useful in section 4 manuals.
Documents error messages.
In section 4 and 9 manuals, these are usually messages printed by the
kernel to the console and to the kernel log.
In section 1, 6, 7, and 8, these are usually messages printed by
userland programs to the standard error output.
.Pp
Historically, this section was used in place of
.Em EXIT STATUS
for manuals in sections 1, 6, and 8; however, this practise is
@ -369,7 +378,9 @@ See
.Sx \&Bl
.Fl diag .
.It Em ERRORS
Documents error handling in sections 2, 3, and 9.
Documents
.Xr errno 2
settings in sections 2, 3, 4, and 9.
.Pp
See
.Sx \&Er .
@ -457,7 +468,7 @@ in the alphabetical
.It Sx \&Pf Ta prefix, no following horizontal space (one argument)
.It Sx \&Ns Ta roman font, no preceding horizontal space (no arguments)
.It Sx \&Ap Ta apostrophe without surrounding whitespace (no arguments)
.It Sx \&Sm Ta switch horizontal spacing mode: Cm on | off
.It Sx \&Sm Ta switch horizontal spacing mode: Op Cm on | off
.It Sx \&Bk , \&Ek Ta keep block: Fl words
.It Sx \&br Ta force output line break in text mode (no arguments)
.It Sx \&sp Ta force vertical space: Op Ar height
@ -502,7 +513,6 @@ in the alphabetical
.It Sx \&Cd Ta kernel configuration declaration (>0 arguments)
.It Sx \&Ad Ta memory address (>0 arguments)
.It Sx \&Ms Ta mathematical symbol (>0 arguments)
.It Sx \&Tn Ta tradename (>0 arguments)
.El
.Ss Physical markup
.Bl -column "Brq, Bro, Brc" description
@ -530,7 +540,6 @@ in the alphabetical
.It Sx \&Ex Fl std Ta standard command exit values: Op Ar utility ...
.It Sx \&Rv Fl std Ta standard function return values: Op Ar function ...
.It Sx \&St Ta reference to a standards document (one argument)
.It Sx \&Ux Ta Ux
.It Sx \&At Ta At
.It Sx \&Bx Ta Bx
.It Sx \&Bsx Ta Bsx
@ -741,9 +750,8 @@ See also
.Sx \&Dx ,
.Sx \&Fx ,
.Sx \&Nx ,
.Sx \&Ox ,
and
.Sx \&Ux .
.Sx \&Ox .
.Ss \&Bc
Close a
.Sx \&Bo
@ -1107,10 +1115,10 @@ See also
.Sx \&Dx ,
.Sx \&Fx ,
.Sx \&Nx ,
.Sx \&Ox ,
and
.Sx \&Ux .
.Sx \&Ox .
.Ss \&Bt
Supported only for compatibility, do not use this in new manuals.
Prints
.Dq is currently in beta test.
.Ss \&Bx
@ -1130,9 +1138,8 @@ See also
.Sx \&Dx ,
.Sx \&Fx ,
.Sx \&Nx ,
.Sx \&Ox ,
and
.Sx \&Ux .
.Sx \&Ox .
.Ss \&Cd
Kernel configuration declaration.
This denotes strings accepted by
@ -1188,7 +1195,7 @@ Close a
block.
Does not have any tail arguments.
.Ss \&Dd
Document date.
Document date for display in the page footer.
This is the mandatory first macro of any
.Nm
manual.
@ -1217,8 +1224,11 @@ the special string
.Dq $\&Mdocdate$
can be given as an argument.
.It
A few alternative date formats are accepted as well
and converted to the standard form.
The traditional, purely numeric
.Xr man 7
format
.Ar year Ns \(en Ns Ar month Ns \(en Ns Ar day
is accepted, too.
.It
If a date string cannot be parsed, it is used verbatim.
.It
@ -1278,97 +1288,92 @@ See also
and
.Sx \&Do .
.Ss \&Dt
Document title.
Document title for display in the page header.
This is the mandatory second macro of any
.Nm
file.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Dt
.Oo
.Ar title
.Oo
.Ar TITLE
.Ar section
.Op Ar volume
.Op Ar arch
.Oc
.Oc
.Op Ar volume | arch
.Ed
.Pp
Its arguments are as follows:
.Bl -tag -width Ds -offset Ds
.It Ar title
.Bl -tag -width section -offset 2n
.It Ar TITLE
The document's title (name), defaulting to
.Dq UNKNOWN
.Dq UNTITLED
if unspecified.
It should be capitalised.
To achieve a uniform appearance of page header lines,
it should by convention be all caps.
.It Ar section
The manual section.
This may be one of
.Ar 1
.Cm 1
.Pq utilities ,
.Ar 2
.Cm 2
.Pq system calls ,
.Ar 3
.Cm 3
.Pq libraries ,
.Ar 3p
.Cm 3p
.Pq Perl libraries ,
.Ar 4
.Cm 4
.Pq devices ,
.Ar 5
.Cm 5
.Pq file formats ,
.Ar 6
.Cm 6
.Pq games ,
.Ar 7
.Cm 7
.Pq miscellaneous ,
.Ar 8
.Cm 8
.Pq system utilities ,
.Ar 9
.Cm 9
.Pq kernel functions ,
.Ar X11
.Cm X11
.Pq X Window System ,
.Ar X11R6
.Cm X11R6
.Pq X Window System ,
.Ar unass
.Cm unass
.Pq unassociated ,
.Ar local
.Cm local
.Pq local system ,
.Ar draft
.Cm draft
.Pq draft manual ,
or
.Ar paper
.Cm paper
.Pq paper .
It should correspond to the manual's filename suffix and defaults to
.Dq 1
if unspecified.
the empty string if unspecified.
.It Ar volume
This overrides the volume inferred from
.Ar section .
This field is optional, and if specified, must be one of
.Ar USD
.Cm USD
.Pq users' supplementary documents ,
.Ar PS1
.Cm PS1
.Pq programmers' supplementary documents ,
.Ar AMD
.Cm AMD
.Pq administrators' supplementary documents ,
.Ar SMM
.Cm SMM
.Pq system managers' manuals ,
.Ar URM
.Cm URM
.Pq users' reference manuals ,
.Ar PRM
.Cm PRM
.Pq programmers' reference manuals ,
.Ar KM
.Cm KM
.Pq kernel manuals ,
.Ar IND
.Cm IND
.Pq master index ,
.Ar MMI
.Cm MMI
.Pq master index ,
.Ar LOCAL
.Cm LOCAL
.Pq local manuals ,
.Ar LOC
.Cm LOC
.Pq local manuals ,
or
.Ar CON
.Cm CON
.Pq contributed manuals .
.It Ar arch
This specifies the machine architecture a manual page applies to,
@ -1430,9 +1435,8 @@ See also
.Sx \&Bx ,
.Sx \&Fx ,
.Sx \&Nx ,
.Sx \&Ox ,
and
.Sx \&Ux .
.Sx \&Ox .
.Ss \&Ec
Close a scope started by
.Sx \&Eo .
@ -1481,8 +1485,14 @@ See also
and
.Sx \&Sy .
.Ss \&En
This macro is obsolete and not implemented in
.Xr mandoc 1 .
This macro is obsolete.
Use
.Sx \&Eo
or any of the other enclosure macros.
.Pp
It encloses its argument in the delimiters specified by the last
.Sx \&Es
macro.
.Ss \&Eo
An arbitrary enclosure.
Its syntax is as follows:
@ -1508,7 +1518,14 @@ See also
.Sx \&Dv
for general constants.
.Ss \&Es
This macro is obsolete and not implemented.
This macro is obsolete.
Use
.Sx \&Eo
or any of the other enclosure macros.
.Pp
It takes two arguments, defining the delimiters to be used by subsequent
.Sx \&En
macros.
.Ss \&Ev
Environmental variables such as those specified in
.Xr environ 7 .
@ -1544,19 +1561,31 @@ Function argument.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Fa
.Op Cm argtype
.Cm argname
.Qo
.Op Ar argtype
.Op Ar argname
.Qc Ar \&...
.Ed
.Pp
This may be invoked for names with or without the corresponding type.
It is also used to specify the field name of a structure.
Each argument may be a name and a type (recommended for the
.Em SYNOPSIS
section), a name alone (for function invocations),
or a type alone (for function prototypes).
If both a type and a name are given or if the type consists of multiple
words, all words belonging to the same function argument have to be
given in a single argument to the
.Sx \&Fa
macro.
.Pp
This macro is also used to specify the field name of a structure.
.Pp
Most often, the
.Sx \&Fa
macro is used in the
.Em SYNOPSIS
within
.Sx \&Fo
section when documenting multi-line function prototypes.
blocks when documenting multi-line function prototypes.
If invoked with multiple arguments, the arguments are separated by a
comma.
Furthermore, if the following macro is another
@ -1566,7 +1595,7 @@ the last argument will also have a trailing comma.
Examples:
.Dl \&.Fa \(dqconst char *p\(dq
.Dl \&.Fa \(dqint a\(dq \(dqint b\(dq \(dqint c\(dq
.Dl \&.Fa foo
.Dl \&.Fa \(dqchar *\(dq size_t
.Pp
See also
.Sx \&Fo .
@ -1669,7 +1698,7 @@ Invocations usually occur in the following context:
.br
.Pf \. Sx \&Fo Ar funcname
.br
.Pf \. Sx \&Fa Oo Ar argtype Oc Ar argname
.Pf \. Sx \&Fa Qq Ar argtype Ar argname
.br
\&.\.\.
.br
@ -1688,13 +1717,10 @@ See also
and
.Sx \&Ft .
.Ss \&Fr
This macro is obsolete and not implemented in
.Xr mandoc 1 .
This macro is obsolete.
No replacement markup is needed.
.Pp
It was used to show function return values.
The syntax was:
.Pp
.Dl Pf . Sx \&Fr Ar value
It was used to show numerical function return values in an italic font.
.Ss \&Ft
A function type.
Its syntax is as follows:
@ -1733,9 +1759,8 @@ See also
.Sx \&Bx ,
.Sx \&Dx ,
.Sx \&Nx ,
.Sx \&Ox ,
and
.Sx \&Ux .
.Sx \&Ox .
.Ss \&Hf
This macro is not implemented in
.Xr mandoc 1 .
@ -2053,9 +2078,8 @@ See also
.Sx \&Bx ,
.Sx \&Dx ,
.Sx \&Fx ,
.Sx \&Ox ,
and
.Sx \&Ux .
.Sx \&Ox .
.Ss \&Oc
Close multi-line
.Sx \&Oo
@ -2084,7 +2108,7 @@ Examples:
See also
.Sx \&Oo .
.Ss \&Os
Document operating system version.
Operating system version for display in the page footer.
This is the mandatory third macro of
any
.Nm
@ -2109,8 +2133,12 @@ See also
and
.Sx \&Dt .
.Ss \&Ot
This macro is obsolete and not implemented in
.Xr mandoc 1 .
This macro is obsolete.
Use
.Sx \&Ft
instead; with
.Xr mandoc 1 ,
both have the same effect.
.Pp
Historical
.Nm
@ -2132,9 +2160,8 @@ See also
.Sx \&Bx ,
.Sx \&Dx ,
.Sx \&Fx ,
.Sx \&Nx ,
and
.Sx \&Ux .
.Sx \&Nx .
.Ss \&Pa
An absolute or relative file system path, or a file or directory name.
If an argument is not provided, the character
@ -2308,7 +2335,7 @@ and
Switches the spacing mode for output generated from macros.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Sm Cm on | off
.D1 Pf \. Sx \&Sm Op Cm on | off
.Pp
By default, spacing is
.Cm on .
@ -2317,6 +2344,11 @@ When switched
no white space is inserted between macro arguments and between the
output generated from adjacent macros, but text lines
still get normal spacing between words and sentences.
.Pp
When called without an argument, the
.Sx \&Sm
macro toggles the spacing mode.
Using this is not recommended because it makes the code harder to read.
.Ss \&So
Multi-line version of
.Sx \&Sq .
@ -2354,113 +2386,243 @@ and
.Sx \&Sx .
.Ss \&St
Replace an abbreviation for a standard with the full form.
The following standards are recognised:
The following standards are recognised.
Where multiple lines are given without a blank line in between,
they all refer to the same standard, and using the first form
is recommended.
.Bl -tag -width 1n
.It C language standards
.Pp
.Bl -tag -width "-p1003.1g-2000X" -compact
.It \-p1003.1-88
.St -p1003.1-88
.It \-p1003.1-90
.St -p1003.1-90
.It \-p1003.1-96
.St -p1003.1-96
.It \-p1003.1-2001
.St -p1003.1-2001
.It \-p1003.1-2004
.St -p1003.1-2004
.It \-p1003.1-2008
.St -p1003.1-2008
.It \-p1003.1
.St -p1003.1
.It \-p1003.1b
.St -p1003.1b
.It \-p1003.1b-93
.St -p1003.1b-93
.It \-p1003.1c-95
.St -p1003.1c-95
.It \-p1003.1d-99
.St -p1003.1d-99
.It \-p1003.1g-2000
.St -p1003.1g-2000
.It \-p1003.1i-95
.St -p1003.1i-95
.It \-p1003.1j-2000
.St -p1003.1j-2000
.It \-p1003.1q-2000
.St -p1003.1q-2000
.It \-p1003.2
.St -p1003.2
.It \-p1003.2-92
.St -p1003.2-92
.It \-p1003.2a-92
.St -p1003.2a-92
.It \-p1387.2
.St -p1387.2
.It \-p1387.2-95
.St -p1387.2-95
.It \-isoC
.St -isoC
.It \-isoC-90
.St -isoC-90
.It \-isoC-amd1
.St -isoC-amd1
.It \-isoC-tcor1
.St -isoC-tcor1
.It \-isoC-tcor2
.St -isoC-tcor2
.It \-isoC-99
.St -isoC-99
.It \-isoC-2011
.St -isoC-2011
.It \-iso9945-1-90
.St -iso9945-1-90
.It \-iso9945-1-96
.St -iso9945-1-96
.It \-iso9945-2-93
.St -iso9945-2-93
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-ansiC
.St -ansiC
.It \-ansiC-89
.St -ansiC-89
.It \-isoC
.St -isoC
.It \-isoC-90
.St -isoC-90
.br
The original C standard.
.Pp
.It \-isoC-amd1
.St -isoC-amd1
.Pp
.It \-isoC-tcor1
.St -isoC-tcor1
.Pp
.It \-isoC-tcor2
.St -isoC-tcor2
.Pp
.It \-isoC-99
.St -isoC-99
.It \-ansiC-99
.St -ansiC-99
.It \-ieee754
.St -ieee754
.It \-iso8802-3
.St -iso8802-3
.It \-iso8601
.St -iso8601
.It \-ieee1275-94
.St -ieee1275-94
.br
The second major version of the C language standard.
.Pp
.It \-isoC-2011
.St -isoC-2011
.br
The third major version of the C language standard.
.El
.It POSIX.1 before the Single UNIX Specification
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-p1003.1-88
.St -p1003.1-88
.It \-p1003.1
.St -p1003.1
.br
The original POSIX standard, based on ANSI C.
.Pp
.It \-p1003.1-90
.St -p1003.1-90
.It \-iso9945-1-90
.St -iso9945-1-90
.br
The first update of POSIX.1.
.Pp
.It \-p1003.1b-93
.St -p1003.1b-93
.It \-p1003.1b
.St -p1003.1b
.br
Real-time extensions.
.Pp
.It \-p1003.1c-95
.St -p1003.1c-95
.br
POSIX thread interfaces.
.Pp
.It \-p1003.1i-95
.St -p1003.1i-95
.br
Technical Corrigendum.
.Pp
.It \-p1003.1-96
.St -p1003.1-96
.It \-iso9945-1-96
.St -iso9945-1-96
.br
Includes POSIX.1-1990, 1b, 1c, and 1i.
.El
.It X/Open Portability Guide version 4 and related standards
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-xpg3
.St -xpg3
.br
An XPG4 precursor, published in 1989.
.Pp
.It \-p1003.2
.St -p1003.2
.It \-p1003.2-92
.St -p1003.2-92
.It \-iso9945-2-93
.St -iso9945-2-93
.br
An XCU4 precursor.
.Pp
.It \-p1003.2a-92
.St -p1003.2a-92
.br
Updates to POSIX.2.
.Pp
.It \-xpg4
.St -xpg4
.br
Based on POSIX.1 and POSIX.2, published in 1992.
.El
.It Single UNIX Specification version 1 and related standards
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-xpg4.2
.St -xpg4.2
.It \-xpg4.3
.St -xpg4.3
.It \-xbd5
.St -xbd5
.It \-xcu5
.St -xcu5
.br
This standard was published in 1994 and is also called SUSv1.
It was used as the basis for UNIX 95 certification.
The following three refer to parts of it.
.Pp
.It \-xsh4.2
.St -xsh4.2
.It \-xsh5
.St -xsh5
.It \-xns5
.St -xns5
.It \-xns5.2
.St -xns5.2
.It \-xns5.2d2.0
.St -xns5.2d2.0
.Pp
.It \-xcurses4.2
.St -xcurses4.2
.Pp
.It \-p1003.1g-2000
.St -p1003.1g-2000
.br
Networking APIs, including sockets.
.Pp
.It \-xpg4.3
.St -xpg4.3
.Pp
.It \-svid4
.St -svid4 ,
.br
Published in 1995.
.El
.It Single UNIX Specification version 2 and related standards
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-susv2
.St -susv2
This Standard was published in 1997
and is also called X/Open Portability Guide version 5.
It was used as the basis for UNIX 98 certification.
The following refer to parts of it.
.Pp
.It \-xbd5
.St -xbd5
.Pp
.It \-xsh5
.St -xsh5
.Pp
.It \-xcu5
.St -xcu5
.Pp
.It \-xns5
.St -xns5
.It \-xns5.2d2.0
.St -xns5.2d2.0
.It \-xns5.2
.St -xns5.2
.Pp
.It \-p1387.2
.St -p1387.2
.It \-p1387.2-95
.St -p1387.2-95
.br
POSIX software administration.
.El
.It Single UNIX Specification version 3 and related standards
.Pp
.Bl -tag -width "-p1003.1g-2000X" -compact
.It \-p1003.1d-99
.St -p1003.1d-99
.br
Additional real-time extensions.
.Pp
.It \-p1003.1j-2000
.St -p1003.1j-2000
.br
Advanced real-time extensions.
.Pp
.It \-p1003.1q-2000
.St -p1003.1q-2000
.br
Amendment 7: Tracing [C Language].
.Pp
.It \-p1003.1-2001
.St -p1003.1-2001
.It \-susv3
.St -susv3
.It \-svid4
.St -svid4
.br
This standard is based on C99, SUSv2, POSIX.1-1996, 1d, and 1j.
It is also called X/Open Portability Guide version 6.
It is used as the basis for UNIX 03 certification.
.Pp
.It \-p1003.1-2004
.St -p1003.1-2004
.br
The second and last Technical Corrigendum.
.El
.It Single UNIX Specification version 4
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-p1003.1-2008
.St -p1003.1-2008
.br
This standard is also called SUSv4 and
X/Open Portability Guide version 7.
.Pp
.It \-p1003.1-2013
.St -p1003.1-2013
.br
This is the first Technical Corrigendum.
.El
.It Other standards
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-ieee754
.St -ieee754
.br
Floating-point arithmetic.
.Pp
.It \-iso8601
.St -iso8601
.br
Representation of dates and times, published in 1988.
.Pp
.It \-iso8802-3
.St -iso8802-3
.br
Ethernet local area networks.
.Pp
.It \-ieee1275-94
.St -ieee1275-94
.El
.El
.Ss \&Sx
Reference a section or subsection in the same manual page.
@ -2492,36 +2654,19 @@ Table cell separator in
lists; can only be used below
.Sx \&It .
.Ss \&Tn
Format a tradename.
.Pp
Since this macro is often implemented to use a small caps font,
it has historically been used for acronyms (like ASCII) as well.
Such usage is not recommended because it would use the same macro
sometimes for semantical annotation, sometimes for physical formatting.
.Pp
Examples:
.Dl \&.Tn IBM
Supported only for compatibility, do not use this in new manuals.
Even though the macro name
.Pq Dq tradename
suggests a semantic function, historic usage is inconsistent, mostly
using it as a presentation-level macro to request a small caps font.
.Ss \&Ud
Supported only for compatibility, do not use this in new manuals.
Prints out
.Dq currently under development.
.Ss \&Ux
Format the
.Ux
name.
Accepts no argument.
.Pp
Examples:
.Dl \&.Ux
.Pp
See also
.Sx \&At ,
.Sx \&Bsx ,
.Sx \&Bx ,
.Sx \&Dx ,
.Sx \&Fx ,
.Sx \&Nx ,
and
.Sx \&Ox .
Supported only for compatibility, do not use this in new manuals.
Prints out
.Dq Ux .
.Ss \&Va
A variable name.
.Pp
@ -2773,6 +2918,7 @@ end of the line.
.It Sx \&D1 Ta \&No Ta \&Yes
.It Sx \&Dl Ta \&No Ta Yes
.It Sx \&Dq Ta Yes Ta Yes
.It Sx \&En Ta Yes Ta Yes
.It Sx \&Op Ta Yes Ta Yes
.It Sx \&Pq Ta Yes Ta Yes
.It Sx \&Ql Ta Yes Ta Yes
@ -2850,16 +2996,15 @@ then the macro accepts an arbitrary number of arguments.
.It Sx \&Dv Ta Yes Ta Yes Ta >0
.It Sx \&Dx Ta Yes Ta Yes Ta n
.It Sx \&Em Ta Yes Ta Yes Ta >0
.It Sx \&En Ta \&No Ta \&No Ta 0
.It Sx \&Er Ta Yes Ta Yes Ta >0
.It Sx \&Es Ta \&No Ta \&No Ta 0
.It Sx \&Es Ta Yes Ta Yes Ta 2
.It Sx \&Ev Ta Yes Ta Yes Ta >0
.It Sx \&Ex Ta \&No Ta \&No Ta n
.It Sx \&Fa Ta Yes Ta Yes Ta >0
.It Sx \&Fd Ta \&No Ta \&No Ta >0
.It Sx \&Fl Ta Yes Ta Yes Ta n
.It Sx \&Fn Ta Yes Ta Yes Ta >0
.It Sx \&Fr Ta \&No Ta \&No Ta n
.It Sx \&Fr Ta Yes Ta Yes Ta >0
.It Sx \&Ft Ta Yes Ta Yes Ta >0
.It Sx \&Fx Ta Yes Ta Yes Ta n
.It Sx \&Hf Ta \&No Ta \&No Ta n
@ -2876,13 +3021,13 @@ then the macro accepts an arbitrary number of arguments.
.It Sx \&Ns Ta Yes Ta Yes Ta 0
.It Sx \&Nx Ta Yes Ta Yes Ta n
.It Sx \&Os Ta \&No Ta \&No Ta n
.It Sx \&Ot Ta \&No Ta \&No Ta n
.It Sx \&Ot Ta Yes Ta Yes Ta >0
.It Sx \&Ox Ta Yes Ta Yes Ta n
.It Sx \&Pa Ta Yes Ta Yes Ta n
.It Sx \&Pf Ta Yes Ta Yes Ta 1
.It Sx \&Pp Ta \&No Ta \&No Ta 0
.It Sx \&Rv Ta \&No Ta \&No Ta n
.It Sx \&Sm Ta \&No Ta \&No Ta 1
.It Sx \&Sm Ta \&No Ta \&No Ta <2
.It Sx \&St Ta \&No Ta Yes Ta 1
.It Sx \&Sx Ta Yes Ta Yes Ta >0
.It Sx \&Sy Ta Yes Ta Yes Ta >0
@ -2998,8 +3143,9 @@ Manually switching the font using the
.Ql \ef
font escape sequences is never required.
.Sh COMPATIBILITY
This section documents compatibility between mandoc and other
troff implementations, at this time limited to GNU troff
This section provides an incomplete list of compatibility issues
between mandoc and other troff implementations, at this time limited
to GNU troff
.Pq Qq groff .
The term
.Qq historic groff
@ -3108,7 +3254,7 @@ certain list types.
can only be called by other macros, but not at the beginning of a line.
.It
.Sx \&%C
is not implemented.
is not implemented (up to and including groff-1.22.2).
.It
Historic groff only allows up to eight or nine arguments per macro input
line, depending on the exact situation.
@ -3126,7 +3272,7 @@ in new groff and mandoc.
.Sq \ef
.Pq font face
and
.Sq \ef
.Sq \eF
.Pq font family face
.Sx Text Decoration
escapes behave irregularly when specified within line-macro scopes.
@ -3143,41 +3289,11 @@ The following features are unimplemented in mandoc:
.Fl file Ar file .
.It
.Sx \&Bd
.Fl offset Ar center
.Fl offset Cm center
and
.Fl offset Ar right .
.Fl offset Cm right .
Groff does not implement centred and flush-right rendering either,
but produces large indentations.
.It
The
.Sq \eh
.Pq horizontal position ,
.Sq \ev
.Pq vertical position ,
.Sq \em
.Pq text colour ,
.Sq \eM
.Pq text filling colour ,
.Sq \ez
.Pq zero-length character ,
.Sq \ew
.Pq string length ,
.Sq \ek
.Pq horizontal position marker ,
.Sq \eo
.Pq text overstrike ,
and
.Sq \es
.Pq text size
escape sequences are all discarded in mandoc.
.It
The
.Sq \ef
scaling unit is accepted by mandoc, but rendered as the default unit.
.It
In quoted literals, groff allows pairwise double-quotes to produce a
standalone double-quote in formatted output.
This is not supported by mandoc.
.El
.Sh SEE ALSO
.Xr man 1 ,

341
mdoc.c
View File

@ -1,7 +1,7 @@
/* $Id: mdoc.c,v 1.206 2013/12/24 19:11:46 schwarze Exp $ */
/* $Id: mdoc.c,v 1.223 2014/08/06 15:09:05 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -22,6 +22,7 @@
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -30,10 +31,11 @@
#include "mdoc.h"
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmdoc.h"
#include "libmandoc.h"
const char *const __mdoc_macronames[MDOC_MAX] = {
const char *const __mdoc_macronames[MDOC_MAX + 1] = {
"Ap", "Dd", "Dt", "Os",
"Sh", "Ss", "Pp", "D1",
"Dl", "Bd", "Ed", "Bl",
@ -44,11 +46,8 @@ const char *const __mdoc_macronames[MDOC_MAX] = {
"Ic", "In", "Li", "Nd",
"Nm", "Op", "Ot", "Pa",
"Rv", "St", "Va", "Vt",
/* LINTED */
"Xr", "%A", "%B", "%D",
/* LINTED */
"%I", "%J", "%N", "%O",
/* LINTED */
"%P", "%R", "%T", "%V",
"Ac", "Ao", "Aq", "At",
"Bc", "Bf", "Bo", "Bq",
@ -65,22 +64,19 @@ const char *const __mdoc_macronames[MDOC_MAX] = {
"Bk", "Ek", "Bt", "Hf",
"Fr", "Ud", "Lb", "Lp",
"Lk", "Mt", "Brq", "Bro",
/* LINTED */
"Brc", "%C", "Es", "En",
/* LINTED */
"Dx", "%Q", "br", "sp",
/* LINTED */
"%U", "Ta"
"%U", "Ta", "ll", "text",
};
const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
"split", "nosplit", "ragged",
"unfilled", "literal", "file",
"offset", "bullet", "dash",
"hyphen", "item", "enum",
"tag", "diag", "hang",
"ohang", "inset", "column",
"width", "compact", "std",
"unfilled", "literal", "file",
"offset", "bullet", "dash",
"hyphen", "item", "enum",
"tag", "diag", "hang",
"ohang", "inset", "column",
"width", "compact", "std",
"filled", "words", "emphasis",
"symbolic", "nested", "centered"
};
@ -89,13 +85,13 @@ const char * const *mdoc_macronames = __mdoc_macronames;
const char * const *mdoc_argnames = __mdoc_argnames;
static void mdoc_node_free(struct mdoc_node *);
static void mdoc_node_unlink(struct mdoc *,
static void mdoc_node_unlink(struct mdoc *,
struct mdoc_node *);
static void mdoc_free1(struct mdoc *);
static void mdoc_alloc1(struct mdoc *);
static struct mdoc_node *node_alloc(struct mdoc *, int, int,
static struct mdoc_node *node_alloc(struct mdoc *, int, int,
enum mdoct, enum mdoc_type);
static int node_append(struct mdoc *,
static int node_append(struct mdoc *,
struct mdoc_node *);
#if 0
static int mdoc_preptext(struct mdoc *, int, char *, int);
@ -103,24 +99,21 @@ static int mdoc_preptext(struct mdoc *, int, char *, int);
static int mdoc_ptext(struct mdoc *, int, char *, int);
static int mdoc_pmacro(struct mdoc *, int, char *, int);
const struct mdoc_node *
mdoc_node(const struct mdoc *mdoc)
{
assert( ! (MDOC_HALT & mdoc->flags));
return(mdoc->first);
}
const struct mdoc_meta *
mdoc_meta(const struct mdoc *mdoc)
{
assert( ! (MDOC_HALT & mdoc->flags));
return(&mdoc->meta);
}
/*
* Frees volatile resources (parse tree, meta-data, fields).
*/
@ -130,23 +123,15 @@ mdoc_free1(struct mdoc *mdoc)
if (mdoc->first)
mdoc_node_delete(mdoc, mdoc->first);
if (mdoc->meta.title)
free(mdoc->meta.title);
if (mdoc->meta.os)
free(mdoc->meta.os);
if (mdoc->meta.name)
free(mdoc->meta.name);
if (mdoc->meta.arch)
free(mdoc->meta.arch);
if (mdoc->meta.vol)
free(mdoc->meta.vol);
if (mdoc->meta.msec)
free(mdoc->meta.msec);
if (mdoc->meta.date)
free(mdoc->meta.date);
free(mdoc->meta.msec);
free(mdoc->meta.vol);
free(mdoc->meta.arch);
free(mdoc->meta.date);
free(mdoc->meta.title);
free(mdoc->meta.os);
free(mdoc->meta.name);
}
/*
* Allocate all volatile resources (parse tree, meta-data, fields).
*/
@ -164,7 +149,6 @@ mdoc_alloc1(struct mdoc *mdoc)
mdoc->next = MDOC_NEXT_CHILD;
}
/*
* Free up volatile resources (see mdoc_free1()) then re-initialises the
* data with mdoc_alloc1(). After invocation, parse data has been reset
@ -179,7 +163,6 @@ mdoc_reset(struct mdoc *mdoc)
mdoc_alloc1(mdoc);
}
/*
* Completely free up all volatile and non-volatile parse resources.
* After invocation, the pointer is no longer usable.
@ -192,12 +175,12 @@ mdoc_free(struct mdoc *mdoc)
free(mdoc);
}
/*
* Allocate volatile and non-volatile parse resources.
* Allocate volatile and non-volatile parse resources.
*/
struct mdoc *
mdoc_alloc(struct roff *roff, struct mparse *parse, char *defos)
mdoc_alloc(struct roff *roff, struct mparse *parse,
const char *defos, int quick)
{
struct mdoc *p;
@ -205,6 +188,7 @@ mdoc_alloc(struct roff *roff, struct mparse *parse, char *defos)
p->parse = parse;
p->defos = defos;
p->quick = quick;
p->roff = roff;
mdoc_hash_init();
@ -212,20 +196,11 @@ mdoc_alloc(struct roff *roff, struct mparse *parse, char *defos)
return(p);
}
/*
* Climb back up the parse tree, validating open scopes. Mostly calls
* through to macro_end() in macro.c.
*/
int
mdoc_endparse(struct mdoc *mdoc)
{
assert( ! (MDOC_HALT & mdoc->flags));
if (mdoc_macroend(mdoc))
return(1);
mdoc->flags |= MDOC_HALT;
return(0);
return(mdoc_macroend(mdoc));
}
int
@ -233,15 +208,6 @@ mdoc_addeqn(struct mdoc *mdoc, const struct eqn *ep)
{
struct mdoc_node *n;
assert( ! (MDOC_HALT & mdoc->flags));
/* No text before an initial macro. */
if (SEC_NONE == mdoc->lastnamed) {
mdoc_pmsg(mdoc, ep->ln, ep->pos, MANDOCERR_NOTEXT);
return(1);
}
n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN);
n->eqn = ep;
@ -257,15 +223,6 @@ mdoc_addspan(struct mdoc *mdoc, const struct tbl_span *sp)
{
struct mdoc_node *n;
assert( ! (MDOC_HALT & mdoc->flags));
/* No text before an initial macro. */
if (SEC_NONE == mdoc->lastnamed) {
mdoc_pmsg(mdoc, sp->line, 0, MANDOCERR_NOTEXT);
return(1);
}
n = node_alloc(mdoc, sp->line, 0, MDOC_MAX, MDOC_TBL);
n->span = sp;
@ -276,7 +233,6 @@ mdoc_addspan(struct mdoc *mdoc, const struct tbl_span *sp)
return(1);
}
/*
* Main parse routine. Parses a single line -- really just hands off to
* the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
@ -285,8 +241,6 @@ int
mdoc_parseln(struct mdoc *mdoc, int ln, char *buf, int offs)
{
assert( ! (MDOC_HALT & mdoc->flags));
mdoc->flags |= MDOC_NEWLINE;
/*
@ -301,8 +255,8 @@ mdoc_parseln(struct mdoc *mdoc, int ln, char *buf, int offs)
mdoc->flags &= ~MDOC_SYNOPSIS;
return(roff_getcontrol(mdoc->roff, buf, &offs) ?
mdoc_pmacro(mdoc, ln, buf, offs) :
mdoc_ptext(mdoc, ln, buf, offs));
mdoc_pmacro(mdoc, ln, buf, offs) :
mdoc_ptext(mdoc, ln, buf, offs));
}
int
@ -310,30 +264,22 @@ mdoc_macro(MACRO_PROT_ARGS)
{
assert(tok < MDOC_MAX);
/* If we're in the body, deny prologue calls. */
if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
MDOC_PBODY & mdoc->flags) {
mdoc_pmsg(mdoc, line, ppos, MANDOCERR_BADBODY);
return(1);
}
/* If we're in the prologue, deny "body" macros. */
if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
! (MDOC_PBODY & mdoc->flags)) {
mdoc_pmsg(mdoc, line, ppos, MANDOCERR_BADPROLOG);
if (NULL == mdoc->meta.msec)
mdoc->meta.msec = mandoc_strdup("1");
if (NULL == mdoc->meta.title)
mdoc->meta.title = mandoc_strdup("UNKNOWN");
if (mdoc->flags & MDOC_PBODY) {
if (tok == MDOC_Dt) {
mandoc_vmsg(MANDOCERR_DT_LATE,
mdoc->parse, line, ppos,
"Dt %s", buf + *pos);
return(1);
}
} else if ( ! (mdoc_macros[tok].flags & MDOC_PROLOGUE)) {
if (mdoc->meta.title == NULL) {
mandoc_vmsg(MANDOCERR_DT_NOTITLE,
mdoc->parse, line, ppos, "%s %s",
mdoc_macronames[tok], buf + *pos);
mdoc->meta.title = mandoc_strdup("UNTITLED");
}
if (NULL == mdoc->meta.vol)
mdoc->meta.vol = mandoc_strdup("LOCAL");
if (NULL == mdoc->meta.os)
mdoc->meta.os = mandoc_strdup("LOCAL");
if (NULL == mdoc->meta.date)
mdoc->meta.date = mandoc_normdate
(mdoc->parse, NULL, line, ppos);
mdoc->flags |= MDOC_PBODY;
}
@ -350,12 +296,12 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p)
assert(MDOC_ROOT != p->type);
switch (mdoc->next) {
case (MDOC_NEXT_SIBLING):
case MDOC_NEXT_SIBLING:
mdoc->last->next = p;
p->prev = mdoc->last;
p->parent = mdoc->last->parent;
break;
case (MDOC_NEXT_CHILD):
case MDOC_NEXT_CHILD:
mdoc->last->child = p;
p->parent = mdoc->last;
break;
@ -372,13 +318,13 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p)
*/
switch (p->type) {
case (MDOC_BODY):
case MDOC_BODY:
if (ENDBODY_NOT != p->end)
break;
/* FALLTHROUGH */
case (MDOC_TAIL):
case MDOC_TAIL:
/* FALLTHROUGH */
case (MDOC_HEAD):
case MDOC_HEAD:
p->norm = p->parent->norm;
break;
default:
@ -389,15 +335,15 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p)
return(0);
switch (p->type) {
case (MDOC_HEAD):
case MDOC_HEAD:
assert(MDOC_BLOCK == p->parent->type);
p->parent->head = p;
break;
case (MDOC_TAIL):
case MDOC_TAIL:
assert(MDOC_BLOCK == p->parent->type);
p->parent->tail = p;
break;
case (MDOC_BODY):
case MDOC_BODY:
if (p->end)
break;
assert(MDOC_BLOCK == p->parent->type);
@ -410,9 +356,9 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p)
mdoc->last = p;
switch (p->type) {
case (MDOC_TBL):
case MDOC_TBL:
/* FALLTHROUGH */
case (MDOC_TEXT):
case MDOC_TEXT:
if ( ! mdoc_valid_post(mdoc))
return(0);
break;
@ -423,9 +369,8 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p)
return(1);
}
static struct mdoc_node *
node_alloc(struct mdoc *mdoc, int line, int pos,
node_alloc(struct mdoc *mdoc, int line, int pos,
enum mdoct tok, enum mdoc_type type)
{
struct mdoc_node *p;
@ -451,7 +396,6 @@ node_alloc(struct mdoc *mdoc, int line, int pos,
return(p);
}
int
mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
{
@ -464,7 +408,6 @@ mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
return(1);
}
int
mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
{
@ -480,7 +423,6 @@ mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
return(1);
}
int
mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
{
@ -493,7 +435,6 @@ mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
return(1);
}
int
mdoc_endbody_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok,
struct mdoc_node *body, enum mdoc_endbody end)
@ -510,9 +451,8 @@ mdoc_endbody_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok,
return(1);
}
int
mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
enum mdoct tok, struct mdoc_arg *args)
{
struct mdoc_node *p;
@ -523,13 +463,15 @@ mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
(args->refcnt)++;
switch (tok) {
case (MDOC_Bd):
case MDOC_Bd:
/* FALLTHROUGH */
case (MDOC_Bf):
case MDOC_Bf:
/* FALLTHROUGH */
case (MDOC_Bl):
case MDOC_Bl:
/* FALLTHROUGH */
case (MDOC_Rs):
case MDOC_En:
/* FALLTHROUGH */
case MDOC_Rs:
p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
break;
default:
@ -542,9 +484,8 @@ mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
return(1);
}
int
mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
enum mdoct tok, struct mdoc_arg *args)
{
struct mdoc_node *p;
@ -555,7 +496,7 @@ mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
(args->refcnt)++;
switch (tok) {
case (MDOC_An):
case MDOC_An:
p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
break;
default:
@ -591,10 +532,7 @@ mdoc_word_append(struct mdoc *mdoc, const char *p)
n = mdoc->last;
addstr = roff_strdup(mdoc->roff, p);
if (-1 == asprintf(&newstr, "%s %s", n->string, addstr)) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
free(addstr);
free(n->string);
n->string = newstr;
@ -614,7 +552,6 @@ mdoc_node_free(struct mdoc_node *p)
free(p);
}
static void
mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n)
{
@ -652,7 +589,6 @@ mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n)
mdoc->first = NULL;
}
void
mdoc_node_delete(struct mdoc *mdoc, struct mdoc_node *p)
{
@ -723,7 +659,7 @@ mdoc_preptext(struct mdoc *mdoc, int line, char *buf, int offs)
if ( ! mdoc_addeqn(mdoc, roff_eqn(mdoc->roff)))
return(0);
offs += (end - (buf + offs));
}
}
return(1);
}
@ -739,13 +675,6 @@ mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs)
char *c, *ws, *end;
struct mdoc_node *n;
/* No text before an initial macro. */
if (SEC_NONE == mdoc->lastnamed) {
mdoc_pmsg(mdoc, line, offs, MANDOCERR_NOTEXT);
return(1);
}
assert(mdoc->last);
n = mdoc->last;
@ -757,16 +686,16 @@ mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs)
*/
if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
LIST_column == n->norm->Bl.type) {
LIST_column == n->norm->Bl.type) {
/* `Bl' is open without any children. */
mdoc->flags |= MDOC_FREECOL;
return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
}
if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
NULL != n->parent &&
MDOC_Bl == n->parent->tok &&
LIST_column == n->parent->norm->Bl.type) {
NULL != n->parent &&
MDOC_Bl == n->parent->tok &&
LIST_column == n->parent->norm->Bl.type) {
/* `Bl' has block-level `It' children. */
mdoc->flags |= MDOC_FREECOL;
return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
@ -814,10 +743,12 @@ mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs)
*end = '\0';
if (ws)
mdoc_pmsg(mdoc, line, (int)(ws-buf), MANDOCERR_EOLNSPACE);
mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
line, (int)(ws-buf), NULL);
if ('\0' == buf[offs] && ! (MDOC_LITERAL & mdoc->flags)) {
mdoc_pmsg(mdoc, line, (int)(c-buf), MANDOCERR_NOBLANKLN);
mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse,
line, (int)(c - buf), NULL);
/*
* Insert a `sp' in the case of a blank line. Technically,
@ -846,13 +777,12 @@ mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs)
assert(buf < end);
if (mandoc_eos(buf+offs, (size_t)(end-buf-offs), 0))
if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
mdoc->last->flags |= MDOC_EOS;
return(1);
}
/*
* Parse a macro line, that is, a line beginning with the control
* character.
@ -868,30 +798,31 @@ mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs)
/* Empty post-control lines are ignored. */
if ('"' == buf[offs]) {
mdoc_pmsg(mdoc, ln, offs, MANDOCERR_BADCOMMENT);
mandoc_msg(MANDOCERR_COMMENT_BAD, mdoc->parse,
ln, offs, NULL);
return(1);
} else if ('\0' == buf[offs])
return(1);
sv = offs;
/*
/*
* Copy the first word into a nil-terminated buffer.
* Stop copying when a tab, space, or eoln is encountered.
*/
i = 0;
while (i < 4 && '\0' != buf[offs] &&
' ' != buf[offs] && '\t' != buf[offs])
while (i < 4 && '\0' != buf[offs] && ' ' != buf[offs] &&
'\t' != buf[offs])
mac[i++] = buf[offs++];
mac[i] = '\0';
tok = (i > 1 || i < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
if (MDOC_MAX == tok) {
mandoc_vmsg(MANDOCERR_MACRO, mdoc->parse,
ln, sv, "%s", buf + sv - 1);
mandoc_msg(MANDOCERR_MACRO, mdoc->parse,
ln, sv, buf + sv - 1);
return(1);
}
@ -905,24 +836,22 @@ mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs)
while (buf[offs] && ' ' == buf[offs])
offs++;
/*
/*
* Trailing whitespace. Note that tabs are allowed to be passed
* into the parser as "text", so we only warn about spaces here.
*/
if ('\0' == buf[offs] && ' ' == buf[offs - 1])
mdoc_pmsg(mdoc, ln, offs - 1, MANDOCERR_EOLNSPACE);
mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
ln, offs - 1, NULL);
/*
* If an initial macro or a list invocation, divert directly
* into macro processing.
*/
if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) {
if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf))
goto err;
return(1);
}
if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok)
return(mdoc_macro(mdoc, tok, ln, sv, &offs, buf));
n = mdoc->last;
assert(mdoc->last);
@ -933,11 +862,9 @@ mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs)
*/
if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
LIST_column == n->norm->Bl.type) {
LIST_column == n->norm->Bl.type) {
mdoc->flags |= MDOC_FREECOL;
if ( ! mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf))
goto err;
return(1);
return(mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf));
}
/*
@ -947,26 +874,25 @@ mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs)
*/
if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
NULL != n->parent &&
MDOC_Bl == n->parent->tok &&
LIST_column == n->parent->norm->Bl.type) {
NULL != n->parent &&
MDOC_Bl == n->parent->tok &&
LIST_column == n->parent->norm->Bl.type) {
mdoc->flags |= MDOC_FREECOL;
if ( ! mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf))
goto err;
return(1);
return(mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf));
}
/* Normal processing of a macro. */
if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf))
goto err;
if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf))
return(0);
/* In quick mode (for mandocdb), abort after the NAME section. */
if (mdoc->quick && MDOC_Sh == tok &&
SEC_NAME != mdoc->last->sec)
return(2);
return(1);
err: /* Error out. */
mdoc->flags |= MDOC_HALT;
return(0);
}
enum mdelim
@ -978,27 +904,27 @@ mdoc_isdelim(const char *p)
if ('\0' == p[1])
switch (p[0]) {
case('('):
case '(':
/* FALLTHROUGH */
case('['):
case '[':
return(DELIM_OPEN);
case('|'):
case '|':
return(DELIM_MIDDLE);
case('.'):
case '.':
/* FALLTHROUGH */
case(','):
case ',':
/* FALLTHROUGH */
case(';'):
case ';':
/* FALLTHROUGH */
case(':'):
case ':':
/* FALLTHROUGH */
case('?'):
case '?':
/* FALLTHROUGH */
case('!'):
case '!':
/* FALLTHROUGH */
case(')'):
case ')':
/* FALLTHROUGH */
case(']'):
case ']':
return(DELIM_CLOSE);
default:
return(DELIM_NONE);
@ -1014,3 +940,42 @@ mdoc_isdelim(const char *p)
return(DELIM_NONE);
}
void
mdoc_deroff(char **dest, const struct mdoc_node *n)
{
char *cp;
size_t sz;
if (MDOC_TEXT != n->type) {
for (n = n->child; n; n = n->next)
mdoc_deroff(dest, n);
return;
}
/* Skip leading whitespace. */
for (cp = n->string; '\0' != *cp; cp++)
if (0 == isspace((unsigned char)*cp))
break;
/* Skip trailing whitespace. */
for (sz = strlen(cp); sz; sz--)
if (0 == isspace((unsigned char)cp[sz-1]))
break;
/* Skip empty strings. */
if (0 == sz)
return;
if (NULL == *dest) {
*dest = mandoc_strndup(cp, sz);
return;
}
mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
free(*dest);
*dest = cp;
}

26
mdoc.h
View File

@ -1,4 +1,4 @@
/* $Id: mdoc.h,v 1.125 2013/12/24 19:11:45 schwarze Exp $ */
/* $Id: mdoc.h,v 1.131 2014/07/29 13:58:18 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -140,6 +140,7 @@ enum mdoct {
MDOC_sp,
MDOC__U,
MDOC_Ta,
MDOC_ll,
MDOC_MAX
};
@ -186,7 +187,7 @@ enum mdoc_type {
MDOC_ROOT
};
/*
/*
* Section (named/unnamed) of `Sh'. Note that these appear in the
* conventional order imposed by mdoc.7. In the case of SEC_NONE, no
* section has been invoked (this shouldn't happen). SEC_CUSTOM refers
@ -198,6 +199,7 @@ enum mdoc_sec {
SEC_LIBRARY, /* LIBRARY */
SEC_SYNOPSIS, /* SYNOPSIS */
SEC_DESCRIPTION, /* DESCRIPTION */
SEC_CONTEXT, /* CONTEXT */
SEC_IMPLEMENTATION, /* IMPLEMENTATION NOTES */
SEC_RETURN_VALUES, /* RETURN VALUES */
SEC_ENVIRONMENT, /* ENVIRONMENT */
@ -214,7 +216,7 @@ enum mdoc_sec {
SEC_CAVEATS, /* CAVEATS */
SEC_BUGS, /* BUGS */
SEC_SECURITY, /* SECURITY */
SEC_CUSTOM,
SEC_CUSTOM,
SEC__MAX
};
@ -228,11 +230,11 @@ struct mdoc_meta {
char *name; /* leading `Nm' name */
};
/*
* An argument to a macro (multiple values = `-column xxx yyy').
/*
* An argument to a macro (multiple values = `-column xxx yyy').
*/
struct mdoc_argv {
enum mdocargt arg; /* type of argument */
enum mdocargt arg; /* type of argument */
int line;
int pos;
size_t sz; /* elements in "value" */
@ -244,7 +246,7 @@ struct mdoc_argv {
* blocks have multiple instances of the same arguments spread across
* the HEAD, BODY, TAIL, and BLOCK node types.
*/
struct mdoc_arg {
struct mdoc_arg {
size_t argc;
struct mdoc_argv *argv;
unsigned int refcnt;
@ -278,7 +280,7 @@ enum mdoc_list {
enum mdoc_disp {
DISP__NONE = 0,
DISP_centred, /* -centered */
DISP_centered, /* -centered */
DISP_ragged, /* -ragged */
DISP_unfilled, /* -unfilled */
DISP_filled, /* -filled */
@ -332,15 +334,16 @@ struct mdoc_rs {
* provided, etc.
*/
union mdoc_data {
struct mdoc_an An;
struct mdoc_an An;
struct mdoc_bd Bd;
struct mdoc_bf Bf;
struct mdoc_bl Bl;
struct mdoc_node *Es;
struct mdoc_rs Rs;
};
/*
* Single node in tree-linked AST.
/*
* Single node in tree-linked AST.
*/
struct mdoc_node {
struct mdoc_node *parent; /* parent AST node */
@ -389,6 +392,7 @@ struct mdoc;
const struct mdoc_node *mdoc_node(const struct mdoc *);
const struct mdoc_meta *mdoc_meta(const struct mdoc *);
void mdoc_deroff(char **, const struct mdoc_node *);
__END_DECLS

View File

@ -1,4 +1,4 @@
/* $Id: mdoc_argv.c,v 1.89 2013/12/25 00:50:05 schwarze Exp $ */
/* $Id: mdoc_argv.c,v 1.95 2014/07/06 19:09:00 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2012 Ingo Schwarze <schwarze@openbsd.org>
@ -28,11 +28,12 @@
#include "mdoc.h"
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmdoc.h"
#include "libmandoc.h"
#define MULTI_STEP 5 /* pre-allocate argument values */
#define DELIMSZ 6 /* max possible size of a delimiter */
#define DELIMSZ 6 /* max possible size of a delimiter */
enum argsflag {
ARGSFL_NONE = 0,
@ -52,12 +53,12 @@ struct mdocarg {
};
static void argn_free(struct mdoc_arg *, int);
static enum margserr args(struct mdoc *, int, int *,
static enum margserr args(struct mdoc *, int, int *,
char *, enum argsflag, char **);
static int args_checkpunct(const char *, int);
static int argv_multi(struct mdoc *, int,
static int argv_multi(struct mdoc *, int,
struct mdoc_argv *, int *, char *);
static int argv_single(struct mdoc *, int,
static int argv_single(struct mdoc *, int,
struct mdoc_argv *, int *, char *);
static const enum argvflag argvflags[MDOC_ARG_MAX] = {
@ -149,8 +150,8 @@ static const struct mdocarg mdocargs[MDOC_MAX] = {
{ ARGSFL_NONE, NULL }, /* Dt */
{ ARGSFL_NONE, NULL }, /* Os */
{ ARGSFL_NONE, NULL }, /* Sh */
{ ARGSFL_NONE, NULL }, /* Ss */
{ ARGSFL_NONE, NULL }, /* Pp */
{ ARGSFL_NONE, NULL }, /* Ss */
{ ARGSFL_NONE, NULL }, /* Pp */
{ ARGSFL_DELIM, NULL }, /* D1 */
{ ARGSFL_DELIM, NULL }, /* Dl */
{ ARGSFL_NONE, args_Bd }, /* Bd */
@ -158,32 +159,32 @@ static const struct mdocarg mdocargs[MDOC_MAX] = {
{ ARGSFL_NONE, args_Bl }, /* Bl */
{ ARGSFL_NONE, NULL }, /* El */
{ ARGSFL_NONE, NULL }, /* It */
{ ARGSFL_DELIM, NULL }, /* Ad */
{ ARGSFL_DELIM, NULL }, /* Ad */
{ ARGSFL_DELIM, args_An }, /* An */
{ ARGSFL_DELIM, NULL }, /* Ar */
{ ARGSFL_DELIM, NULL }, /* Cd */
{ ARGSFL_DELIM, NULL }, /* Cm */
{ ARGSFL_DELIM, NULL }, /* Dv */
{ ARGSFL_DELIM, NULL }, /* Er */
{ ARGSFL_DELIM, NULL }, /* Ev */
{ ARGSFL_DELIM, NULL }, /* Dv */
{ ARGSFL_DELIM, NULL }, /* Er */
{ ARGSFL_DELIM, NULL }, /* Ev */
{ ARGSFL_NONE, args_Ex }, /* Ex */
{ ARGSFL_DELIM, NULL }, /* Fa */
{ ARGSFL_NONE, NULL }, /* Fd */
{ ARGSFL_DELIM, NULL }, /* Fa */
{ ARGSFL_NONE, NULL }, /* Fd */
{ ARGSFL_DELIM, NULL }, /* Fl */
{ ARGSFL_DELIM, NULL }, /* Fn */
{ ARGSFL_DELIM, NULL }, /* Ft */
{ ARGSFL_DELIM, NULL }, /* Ic */
{ ARGSFL_DELIM, NULL }, /* In */
{ ARGSFL_DELIM, NULL }, /* Fn */
{ ARGSFL_DELIM, NULL }, /* Ft */
{ ARGSFL_DELIM, NULL }, /* Ic */
{ ARGSFL_DELIM, NULL }, /* In */
{ ARGSFL_DELIM, NULL }, /* Li */
{ ARGSFL_NONE, NULL }, /* Nd */
{ ARGSFL_DELIM, NULL }, /* Nm */
{ ARGSFL_NONE, NULL }, /* Nd */
{ ARGSFL_DELIM, NULL }, /* Nm */
{ ARGSFL_DELIM, NULL }, /* Op */
{ ARGSFL_NONE, NULL }, /* Ot */
{ ARGSFL_DELIM, NULL }, /* Ot */
{ ARGSFL_DELIM, NULL }, /* Pa */
{ ARGSFL_NONE, args_Ex }, /* Rv */
{ ARGSFL_DELIM, NULL }, /* St */
{ ARGSFL_DELIM, NULL }, /* St */
{ ARGSFL_DELIM, NULL }, /* Va */
{ ARGSFL_DELIM, NULL }, /* Vt */
{ ARGSFL_DELIM, NULL }, /* Vt */
{ ARGSFL_DELIM, NULL }, /* Xr */
{ ARGSFL_NONE, NULL }, /* %A */
{ ARGSFL_NONE, NULL }, /* %B */
@ -201,7 +202,7 @@ static const struct mdocarg mdocargs[MDOC_MAX] = {
{ ARGSFL_DELIM, NULL }, /* Aq */
{ ARGSFL_DELIM, NULL }, /* At */
{ ARGSFL_DELIM, NULL }, /* Bc */
{ ARGSFL_NONE, args_Bf }, /* Bf */
{ ARGSFL_NONE, args_Bf }, /* Bf */
{ ARGSFL_NONE, NULL }, /* Bo */
{ ARGSFL_DELIM, NULL }, /* Bq */
{ ARGSFL_DELIM, NULL }, /* Bsx */
@ -212,7 +213,7 @@ static const struct mdocarg mdocargs[MDOC_MAX] = {
{ ARGSFL_DELIM, NULL }, /* Dq */
{ ARGSFL_DELIM, NULL }, /* Ec */
{ ARGSFL_NONE, NULL }, /* Ef */
{ ARGSFL_DELIM, NULL }, /* Em */
{ ARGSFL_DELIM, NULL }, /* Em */
{ ARGSFL_NONE, NULL }, /* Eo */
{ ARGSFL_DELIM, NULL }, /* Fx */
{ ARGSFL_DELIM, NULL }, /* Ms */
@ -240,15 +241,15 @@ static const struct mdocarg mdocargs[MDOC_MAX] = {
{ ARGSFL_DELIM, NULL }, /* Ux */
{ ARGSFL_DELIM, NULL }, /* Xc */
{ ARGSFL_NONE, NULL }, /* Xo */
{ ARGSFL_NONE, NULL }, /* Fo */
{ ARGSFL_DELIM, NULL }, /* Fc */
{ ARGSFL_NONE, NULL }, /* Fo */
{ ARGSFL_DELIM, NULL }, /* Fc */
{ ARGSFL_NONE, NULL }, /* Oo */
{ ARGSFL_DELIM, NULL }, /* Oc */
{ ARGSFL_NONE, args_Bk }, /* Bk */
{ ARGSFL_NONE, NULL }, /* Ek */
{ ARGSFL_NONE, NULL }, /* Bt */
{ ARGSFL_NONE, NULL }, /* Hf */
{ ARGSFL_NONE, NULL }, /* Fr */
{ ARGSFL_DELIM, NULL }, /* Fr */
{ ARGSFL_NONE, NULL }, /* Ud */
{ ARGSFL_DELIM, NULL }, /* Lb */
{ ARGSFL_NONE, NULL }, /* Lp */
@ -259,13 +260,14 @@ static const struct mdocarg mdocargs[MDOC_MAX] = {
{ ARGSFL_DELIM, NULL }, /* Brc */
{ ARGSFL_NONE, NULL }, /* %C */
{ ARGSFL_NONE, NULL }, /* Es */
{ ARGSFL_NONE, NULL }, /* En */
{ ARGSFL_DELIM, NULL }, /* En */
{ ARGSFL_DELIM, NULL }, /* Dx */
{ ARGSFL_NONE, NULL }, /* %Q */
{ ARGSFL_NONE, NULL }, /* br */
{ ARGSFL_NONE, NULL }, /* sp */
{ ARGSFL_NONE, NULL }, /* %U */
{ ARGSFL_NONE, NULL }, /* Ta */
{ ARGSFL_NONE, NULL }, /* ll */
};
@ -300,14 +302,14 @@ mdoc_argv(struct mdoc *mdoc, int line, enum mdoct tok,
if (' ' == buf[*pos] && '\\' != buf[*pos - 1])
break;
/*
/*
* We want to nil-terminate the word to look it up (it's easier
* that way). But we may not have a flag, in which case we need
* to restore the line as-is. So keep around the stray byte,
* which we'll reset upon exiting (if necessary).
*/
if ('\0' != (sv = buf[*pos]))
if ('\0' != (sv = buf[*pos]))
buf[(*pos)++] = '\0';
/*
@ -326,7 +328,7 @@ mdoc_argv(struct mdoc *mdoc, int line, enum mdoct tok,
break;
if (MDOC_ARG_MAX == tmp.arg) {
/*
/*
* The flag was not found.
* Restore saved zeroed byte and return as a word.
*/
@ -341,15 +343,15 @@ mdoc_argv(struct mdoc *mdoc, int line, enum mdoct tok,
(*pos)++;
switch (argvflags[tmp.arg]) {
case (ARGV_SINGLE):
case ARGV_SINGLE:
if ( ! argv_single(mdoc, line, &tmp, pos, buf))
return(ARGV_ERROR);
break;
case (ARGV_MULTI):
case ARGV_MULTI:
if ( ! argv_multi(mdoc, line, &tmp, pos, buf))
return(ARGV_ERROR);
break;
case (ARGV_NONE):
case ARGV_NONE:
break;
}
@ -357,11 +359,11 @@ mdoc_argv(struct mdoc *mdoc, int line, enum mdoct tok,
arg = *v = mandoc_calloc(1, sizeof(struct mdoc_arg));
arg->argc++;
arg->argv = mandoc_realloc
(arg->argv, arg->argc * sizeof(struct mdoc_argv));
arg->argv = mandoc_reallocarray(arg->argv,
arg->argc, sizeof(struct mdoc_argv));
memcpy(&arg->argv[(int)arg->argc - 1],
&tmp, sizeof(struct mdoc_argv));
memcpy(&arg->argv[(int)arg->argc - 1], &tmp,
sizeof(struct mdoc_argv));
return(ARGV_ARG);
}
@ -397,7 +399,7 @@ argn_free(struct mdoc_arg *p, int iarg)
arg = &p->argv[iarg];
if (arg->sz && arg->value) {
for (j = (int)arg->sz - 1; j >= 0; j--)
for (j = (int)arg->sz - 1; j >= 0; j--)
free(arg->value[j]);
free(arg->value);
}
@ -414,7 +416,7 @@ mdoc_zargs(struct mdoc *mdoc, int line, int *pos, char *buf, char **v)
}
enum margserr
mdoc_args(struct mdoc *mdoc, int line, int *pos,
mdoc_args(struct mdoc *mdoc, int line, int *pos,
char *buf, enum mdoct tok, char **v)
{
enum argsflag fl;
@ -443,7 +445,7 @@ mdoc_args(struct mdoc *mdoc, int line, int *pos,
}
static enum margserr
args(struct mdoc *mdoc, int line, int *pos,
args(struct mdoc *mdoc, int line, int *pos,
char *buf, enum argsflag fl, char **v)
{
char *p, *pp;
@ -459,7 +461,8 @@ args(struct mdoc *mdoc, int line, int *pos,
* is unterminated.
*/
if (MDOC_PHRASELIT & mdoc->flags)
mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE);
mandoc_msg(MANDOCERR_ARG_QUOTE,
mdoc->parse, line, *pos, NULL);
mdoc->flags &= ~MDOC_PHRASELIT;
return(ARGS_EOLN);
@ -484,7 +487,7 @@ args(struct mdoc *mdoc, int line, int *pos,
pp = NULL;
/* Scan ahead to unescaped `Ta'. */
if ( ! (MDOC_PHRASELIT & mdoc->flags))
if ( ! (MDOC_PHRASELIT & mdoc->flags))
for (pp = *v; ; pp++) {
if (NULL == (pp = strstr(pp, "Ta")))
break;
@ -497,7 +500,7 @@ args(struct mdoc *mdoc, int line, int *pos,
/* By default, assume a phrase. */
rc = ARGS_PHRASE;
/*
/*
* Adjust new-buffer position to be beyond delimiter
* mark (e.g., Ta -> end + 2).
*/
@ -518,7 +521,8 @@ args(struct mdoc *mdoc, int line, int *pos,
/* Whitespace check for eoln case... */
if ('\0' == *p && ' ' == *(p - 1))
mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE);
mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
line, *pos, NULL);
*pos += (int)(p - *v);
@ -573,7 +577,8 @@ args(struct mdoc *mdoc, int line, int *pos,
if ('\0' == buf[*pos]) {
if (MDOC_PPHRASE & mdoc->flags)
return(ARGS_QWORD);
mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE);
mandoc_msg(MANDOCERR_ARG_QUOTE,
mdoc->parse, line, *pos, NULL);
return(ARGS_QWORD);
}
@ -587,7 +592,8 @@ args(struct mdoc *mdoc, int line, int *pos,
(*pos)++;
if ('\0' == buf[*pos])
mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE);
mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
line, *pos, NULL);
return(ARGS_QWORD);
}
@ -598,7 +604,7 @@ args(struct mdoc *mdoc, int line, int *pos,
return(ARGS_WORD);
}
/*
/*
* Check if the string consists only of space-separated closing
* delimiters. This is a bit of a dance: the first must be a close
* delimiter, but it may be followed by middle delimiters. Arbitrary
@ -627,7 +633,7 @@ args_checkpunct(const char *buf, int i)
i++;
/* Remaining must NOT be open/none. */
while (buf[i]) {
j = 0;
while (buf[i] && ' ' != buf[i] && j < DELIMSZ)
@ -649,7 +655,7 @@ args_checkpunct(const char *buf, int i)
}
static int
argv_multi(struct mdoc *mdoc, int line,
argv_multi(struct mdoc *mdoc, int line,
struct mdoc_argv *v, int *pos, char *buf)
{
enum margserr ac;
@ -665,8 +671,8 @@ argv_multi(struct mdoc *mdoc, int line,
break;
if (0 == v->sz % MULTI_STEP)
v->value = mandoc_realloc(v->value,
(v->sz + MULTI_STEP) * sizeof(char *));
v->value = mandoc_reallocarray(v->value,
v->sz + MULTI_STEP, sizeof(char *));
v->value[(int)v->sz] = mandoc_strdup(p);
}
@ -675,7 +681,7 @@ argv_multi(struct mdoc *mdoc, int line,
}
static int
argv_single(struct mdoc *mdoc, int line,
argv_single(struct mdoc *mdoc, int line,
struct mdoc_argv *v, int *pos, char *buf)
{
enum margserr ac;

View File

@ -1,4 +1,4 @@
/* $Id: mdoc_hash.c,v 1.18 2011/07/24 18:15:14 kristaps Exp $ */
/* $Id: mdoc_hash.c,v 1.20 2014/04/20 16:46:05 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -28,11 +28,11 @@
#include <string.h>
#include "mdoc.h"
#include "mandoc.h"
#include "libmdoc.h"
static unsigned char table[27 * 12];
/*
* XXX - this hash has global scope, so if intended for use as a library
* with multiple callers, it will need re-invocation protection.
@ -77,7 +77,7 @@ mdoc_hash_find(const char *p)
major = 12 * (tolower((unsigned char)p[1]) - 97);
else if ('1' == p[1])
major = 12 * 26;
else
else
return(MDOC_MAX);
if (p[2] && p[3])

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $Id: mdoc_macro.c,v 1.125 2013/12/24 20:45:27 schwarze Exp $ */
/* $Id: mdoc_macro.c,v 1.139 2014/08/01 17:27:44 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
@ -40,33 +40,32 @@ enum rew { /* see rew_dohalt() */
REWIND_ERROR
};
static int blk_full(MACRO_PROT_ARGS);
static int blk_exp_close(MACRO_PROT_ARGS);
static int blk_part_exp(MACRO_PROT_ARGS);
static int blk_part_imp(MACRO_PROT_ARGS);
static int ctx_synopsis(MACRO_PROT_ARGS);
static int in_line_eoln(MACRO_PROT_ARGS);
static int in_line_argn(MACRO_PROT_ARGS);
static int in_line(MACRO_PROT_ARGS);
static int obsolete(MACRO_PROT_ARGS);
static int phrase_ta(MACRO_PROT_ARGS);
static int blk_full(MACRO_PROT_ARGS);
static int blk_exp_close(MACRO_PROT_ARGS);
static int blk_part_exp(MACRO_PROT_ARGS);
static int blk_part_imp(MACRO_PROT_ARGS);
static int ctx_synopsis(MACRO_PROT_ARGS);
static int in_line_eoln(MACRO_PROT_ARGS);
static int in_line_argn(MACRO_PROT_ARGS);
static int in_line(MACRO_PROT_ARGS);
static int phrase_ta(MACRO_PROT_ARGS);
static int dword(struct mdoc *, int, int, const char *,
enum mdelim, int);
static int append_delims(struct mdoc *,
static int append_delims(struct mdoc *,
int, int *, char *);
static enum mdoct lookup(enum mdoct, const char *);
static enum mdoct lookup_raw(const char *);
static int make_pending(struct mdoc_node *, enum mdoct,
struct mdoc *, int, int);
static int phrase(struct mdoc *, int, int, char *);
static enum mdoct rew_alt(enum mdoct);
static enum rew rew_dohalt(enum mdoct, enum mdoc_type,
static int phrase(struct mdoc *, int, int, char *);
static enum mdoct rew_alt(enum mdoct);
static enum rew rew_dohalt(enum mdoct, enum mdoc_type,
const struct mdoc_node *);
static int rew_elem(struct mdoc *, enum mdoct);
static int rew_last(struct mdoc *,
static int rew_elem(struct mdoc *, enum mdoct);
static int rew_last(struct mdoc *,
const struct mdoc_node *);
static int rew_sub(enum mdoc_type, struct mdoc *,
static int rew_sub(enum mdoc_type, struct mdoc *,
enum mdoct, int, int);
const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
@ -87,7 +86,7 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* An */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cd */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Cd */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */
@ -98,13 +97,13 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ft */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ic */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* In */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Li */
{ blk_full, MDOC_JOIN }, /* Nd */
{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
{ obsolete, 0 }, /* Ot */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ot */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
{ in_line_eoln, 0 }, /* Rv */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */
@ -191,7 +190,7 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ek */
{ in_line_eoln, 0 }, /* Bt */
{ in_line_eoln, 0 }, /* Hf */
{ obsolete, 0 }, /* Fr */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fr */
{ in_line_eoln, 0 }, /* Ud */
{ in_line, 0 }, /* Lb */
{ in_line_eoln, 0 }, /* Lp */
@ -203,14 +202,15 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Brc */
{ in_line_eoln, MDOC_JOIN }, /* %C */
{ obsolete, 0 }, /* Es */
{ obsolete, 0 }, /* En */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Es */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* En */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */
{ in_line_eoln, MDOC_JOIN }, /* %Q */
{ in_line_eoln, 0 }, /* br */
{ in_line_eoln, 0 }, /* sp */
{ in_line_eoln, 0 }, /* %U */
{ phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */
{ in_line_eoln, 0 }, /* ll */
};
const struct mdoc_macro * const mdoc_macros = __mdoc_macros;
@ -229,19 +229,19 @@ mdoc_macroend(struct mdoc *mdoc)
/* Scan for open explicit scopes. */
n = MDOC_VALID & mdoc->last->flags ?
mdoc->last->parent : mdoc->last;
mdoc->last->parent : mdoc->last;
for ( ; n; n = n->parent)
if (MDOC_BLOCK == n->type &&
MDOC_EXPLICIT & mdoc_macros[n->tok].flags)
mdoc_nmsg(mdoc, n, MANDOCERR_SCOPEEXIT);
mandoc_msg(MANDOCERR_BLK_NOEND, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
/* Rewind to the first. */
return(rew_last(mdoc, mdoc->first));
}
/*
* Look up a macro from within a subsequent context.
*/
@ -254,7 +254,6 @@ lookup(enum mdoct from, const char *p)
return(lookup_raw(p));
}
/*
* Lookup a macro following the initial line macro.
*/
@ -270,7 +269,6 @@ lookup_raw(const char *p)
return(MDOC_MAX);
}
static int
rew_last(struct mdoc *mdoc, const struct mdoc_node *to)
{
@ -279,7 +277,7 @@ rew_last(struct mdoc *mdoc, const struct mdoc_node *to)
assert(to);
mdoc->next = MDOC_NEXT_SIBLING;
/* LINTED */
while (mdoc->last != to) {
/*
* Save the parent here, because we may delete the
@ -299,7 +297,6 @@ rew_last(struct mdoc *mdoc, const struct mdoc_node *to)
return(mdoc_valid_post(mdoc));
}
/*
* For a block closing macro, return the corresponding opening one.
* Otherwise, return the macro itself.
@ -308,37 +305,37 @@ static enum mdoct
rew_alt(enum mdoct tok)
{
switch (tok) {
case (MDOC_Ac):
case MDOC_Ac:
return(MDOC_Ao);
case (MDOC_Bc):
case MDOC_Bc:
return(MDOC_Bo);
case (MDOC_Brc):
case MDOC_Brc:
return(MDOC_Bro);
case (MDOC_Dc):
case MDOC_Dc:
return(MDOC_Do);
case (MDOC_Ec):
case MDOC_Ec:
return(MDOC_Eo);
case (MDOC_Ed):
case MDOC_Ed:
return(MDOC_Bd);
case (MDOC_Ef):
case MDOC_Ef:
return(MDOC_Bf);
case (MDOC_Ek):
case MDOC_Ek:
return(MDOC_Bk);
case (MDOC_El):
case MDOC_El:
return(MDOC_Bl);
case (MDOC_Fc):
case MDOC_Fc:
return(MDOC_Fo);
case (MDOC_Oc):
case MDOC_Oc:
return(MDOC_Oo);
case (MDOC_Pc):
case MDOC_Pc:
return(MDOC_Po);
case (MDOC_Qc):
case MDOC_Qc:
return(MDOC_Qo);
case (MDOC_Re):
case MDOC_Re:
return(MDOC_Rs);
case (MDOC_Sc):
case MDOC_Sc:
return(MDOC_So);
case (MDOC_Xc):
case MDOC_Xc:
return(MDOC_Xo);
default:
return(tok);
@ -346,7 +343,6 @@ rew_alt(enum mdoct tok)
/* NOTREACHED */
}
/*
* Rewinding to tok, how do we have to handle *p?
* REWIND_NONE: *p would delimit tok, but no tok scope is open
@ -358,7 +354,7 @@ rew_alt(enum mdoct tok)
* REWIND_ERROR: No tok block is open at all.
*/
static enum rew
rew_dohalt(enum mdoct tok, enum mdoc_type type,
rew_dohalt(enum mdoct tok, enum mdoc_type type,
const struct mdoc_node *p)
{
@ -374,7 +370,7 @@ rew_dohalt(enum mdoct tok, enum mdoc_type type,
REWIND_ERROR : REWIND_NONE);
/*
* When starting to rewind, skip plain text
* When starting to rewind, skip plain text
* and nodes that have already been rewound.
*/
if (MDOC_TEXT == p->type || MDOC_VALID & p->flags)
@ -398,14 +394,14 @@ rew_dohalt(enum mdoct tok, enum mdoc_type type,
/*
* Blocks delimited by our target token get REWIND_MORE.
* Blocks delimiting our target token get REWIND_NONE.
* Blocks delimiting our target token get REWIND_NONE.
*/
switch (tok) {
case (MDOC_Bl):
case MDOC_Bl:
if (MDOC_It == p->tok)
return(REWIND_MORE);
break;
case (MDOC_It):
case MDOC_It:
if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
return(REWIND_NONE);
break;
@ -415,19 +411,21 @@ rew_dohalt(enum mdoct tok, enum mdoc_type type,
* This is an incomplete and extremely ugly workaround,
* required to let the OpenBSD tree build.
*/
case (MDOC_Oo):
case MDOC_Oo:
if (MDOC_Op == p->tok)
return(REWIND_MORE);
break;
case (MDOC_Nm):
case MDOC_Nm:
return(REWIND_NONE);
case (MDOC_Nd):
case MDOC_Nd:
/* FALLTHROUGH */
case (MDOC_Ss):
case MDOC_Ss:
if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
return(REWIND_NONE);
/* FALLTHROUGH */
case (MDOC_Sh):
case MDOC_Sh:
if (MDOC_ROOT == p->parent->type)
return(REWIND_THIS);
if (MDOC_Nd == p->tok || MDOC_Ss == p->tok ||
MDOC_Sh == p->tok)
return(REWIND_MORE);
@ -456,7 +454,6 @@ rew_dohalt(enum mdoct tok, enum mdoc_type type,
REWIND_FORCE : REWIND_LATER);
}
static int
rew_elem(struct mdoc *mdoc, enum mdoct tok)
{
@ -471,7 +468,6 @@ rew_elem(struct mdoc *mdoc, enum mdoct tok)
return(rew_last(mdoc, n));
}
/*
* We are trying to close a block identified by tok,
* but the child block *broken is still open.
@ -535,9 +531,9 @@ make_pending(struct mdoc_node *broken, enum mdoct tok,
taker->pending = broken->pending;
}
broken->pending = breaker;
mandoc_vmsg(MANDOCERR_SCOPENEST, mdoc->parse, line, ppos,
"%s breaks %s", mdoc_macronames[tok],
mdoc_macronames[broken->tok]);
mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos,
"%s breaks %s", mdoc_macronames[tok],
mdoc_macronames[broken->tok]);
return(1);
}
@ -548,9 +544,8 @@ make_pending(struct mdoc_node *broken, enum mdoct tok,
return(0);
}
static int
rew_sub(enum mdoc_type t, struct mdoc *mdoc,
rew_sub(enum mdoc_type t, struct mdoc *mdoc,
enum mdoct tok, int line, int ppos)
{
struct mdoc_node *n;
@ -558,31 +553,33 @@ rew_sub(enum mdoc_type t, struct mdoc *mdoc,
n = mdoc->last;
while (n) {
switch (rew_dohalt(tok, t, n)) {
case (REWIND_NONE):
case REWIND_NONE:
return(1);
case (REWIND_THIS):
case REWIND_THIS:
n->lastline = line -
(MDOC_NEWLINE & mdoc->flags &&
! (MDOC_EXPLICIT & mdoc_macros[tok].flags));
break;
case (REWIND_FORCE):
mandoc_vmsg(MANDOCERR_SCOPEBROKEN, mdoc->parse,
line, ppos, "%s breaks %s",
mdoc_macronames[tok],
mdoc_macronames[n->tok]);
case REWIND_FORCE:
mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse,
line, ppos, "%s breaks %s",
mdoc_macronames[tok],
mdoc_macronames[n->tok]);
/* FALLTHROUGH */
case (REWIND_MORE):
case REWIND_MORE:
n->lastline = line -
(MDOC_NEWLINE & mdoc->flags ? 1 : 0);
n = n->parent;
continue;
case (REWIND_LATER):
case REWIND_LATER:
if (make_pending(n, tok, mdoc, line, ppos) ||
MDOC_BLOCK != t)
return(1);
/* FALLTHROUGH */
case (REWIND_ERROR):
mdoc_pmsg(mdoc, line, ppos, MANDOCERR_NOSCOPE);
case REWIND_ERROR:
mandoc_msg(MANDOCERR_BLK_NOTOPEN,
mdoc->parse, line, ppos,
mdoc_macronames[tok]);
return(1);
}
break;
@ -615,7 +612,7 @@ static int
dword(struct mdoc *mdoc, int line, int col, const char *p,
enum mdelim d, int may_append)
{
if (DELIM_MAX == d)
d = mdoc_isdelim(p);
@ -644,8 +641,8 @@ dword(struct mdoc *mdoc, int line, int col, const char *p,
*/
else if (DELIM_CLOSE == d && mdoc->last->prev &&
mdoc->last->prev->tok != MDOC_No &&
mdoc->last->parent->tok != MDOC_Fd)
mdoc->last->prev->tok != MDOC_No &&
mdoc->last->parent->tok != MDOC_Fd)
mdoc->last->flags |= MDOC_DELIMC;
return(1);
@ -683,16 +680,15 @@ append_delims(struct mdoc *mdoc, int line, int *pos, char *buf)
* knowing which symbols break this behaviour, for
* example, `. ;' shouldn't propagate the double-space.
*/
if (mandoc_eos(p, strlen(p), 0))
if (mandoc_eos(p, strlen(p)))
mdoc->last->flags |= MDOC_EOS;
}
return(1);
}
/*
* Close out block partial/full explicit.
* Close out block partial/full explicit.
*/
static int
blk_exp_close(MACRO_PROT_ARGS)
@ -701,7 +697,7 @@ blk_exp_close(MACRO_PROT_ARGS)
struct mdoc_node *later; /* A sub-block starting later. */
struct mdoc_node *n; /* For searching backwards. */
int j, lastarg, maxargs, flushed, nl;
int j, lastarg, maxargs, flushed, nl;
enum margserr ac;
enum mdoct atok, ntok;
char *p;
@ -709,10 +705,10 @@ blk_exp_close(MACRO_PROT_ARGS)
nl = MDOC_NEWLINE & mdoc->flags;
switch (tok) {
case (MDOC_Ec):
case MDOC_Ec:
maxargs = 1;
break;
case (MDOC_Ek):
case MDOC_Ek:
mdoc->flags &= ~MDOC_KEEP;
default:
maxargs = 0;
@ -749,7 +745,7 @@ blk_exp_close(MACRO_PROT_ARGS)
if (NULL == later)
break;
/*
/*
* When there is a pending sub block,
* postpone closing out the current block
* until the rew_sub() closing out the sub-block.
@ -778,11 +774,12 @@ blk_exp_close(MACRO_PROT_ARGS)
later = n;
}
if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
/* FIXME: do this in validate */
if (buf[*pos])
mdoc_pmsg(mdoc, line, ppos, MANDOCERR_ARGSLOST);
if ( ! (MDOC_PARSED & mdoc_macros[tok].flags)) {
if ('\0' != buf[*pos])
mandoc_vmsg(MANDOCERR_ARG_SKIP,
mdoc->parse, line, ppos,
"%s %s", mdoc_macronames[tok],
buf + *pos);
if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
return(0);
return(rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos));
@ -791,7 +788,7 @@ blk_exp_close(MACRO_PROT_ARGS)
if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
return(0);
if (NULL == later && maxargs > 0)
if (NULL == later && maxargs > 0)
if ( ! mdoc_tail_alloc(mdoc, line, ppos, rew_alt(tok)))
return(0);
@ -843,7 +840,6 @@ blk_exp_close(MACRO_PROT_ARGS)
return(append_delims(mdoc, line, pos, buf));
}
static int
in_line(MACRO_PROT_ARGS)
{
@ -863,17 +859,17 @@ in_line(MACRO_PROT_ARGS)
*/
switch (tok) {
case (MDOC_An):
case MDOC_An:
/* FALLTHROUGH */
case (MDOC_Ar):
case MDOC_Ar:
/* FALLTHROUGH */
case (MDOC_Fl):
case MDOC_Fl:
/* FALLTHROUGH */
case (MDOC_Mt):
case MDOC_Mt:
/* FALLTHROUGH */
case (MDOC_Nm):
case MDOC_Nm:
/* FALLTHROUGH */
case (MDOC_Pa):
case MDOC_Pa:
nc = 1;
break;
default:
@ -888,7 +884,7 @@ in_line(MACRO_PROT_ARGS)
if (ARGV_WORD == av) {
*pos = la;
break;
}
}
if (ARGV_EOLN == av)
break;
if (ARGV_ARG == av)
@ -911,7 +907,7 @@ in_line(MACRO_PROT_ARGS)
ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
/*
/*
* In this case, we've located a submacro and must
* execute it. Close out scope, if open. If no
* elements have been generated, either create one (nc)
@ -922,15 +918,16 @@ in_line(MACRO_PROT_ARGS)
if (scope && ! rew_elem(mdoc, tok))
return(0);
if (nc && 0 == cnt) {
if ( ! mdoc_elem_alloc(mdoc, line,
ppos, tok, arg))
if ( ! mdoc_elem_alloc(mdoc,
line, ppos, tok, arg))
return(0);
if ( ! rew_last(mdoc, mdoc->last))
return(0);
} else if ( ! nc && 0 == cnt) {
mdoc_argv_free(arg);
mdoc_pmsg(mdoc, line, ppos,
MANDOCERR_MACROEMPTY);
mandoc_msg(MANDOCERR_MACRO_EMPTY,
mdoc->parse, line, ppos,
mdoc_macronames[tok]);
}
if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
@ -938,12 +935,12 @@ in_line(MACRO_PROT_ARGS)
if ( ! nl)
return(1);
return(append_delims(mdoc, line, pos, buf));
}
}
/*
/*
* Non-quote-enclosed punctuation. Set up our scope, if
* a word; rewind the scope, if a delimiter; then append
* the word.
* the word.
*/
d = ARGS_QWORD == ac ? DELIM_NONE : mdoc_isdelim(p);
@ -957,13 +954,13 @@ in_line(MACRO_PROT_ARGS)
* this once per invocation. There may be more
* of these (all of them?).
*/
if (0 == cnt && (nc || MDOC_Li == tok) &&
DELIM_CLOSE == d && ! scope) {
if ( ! mdoc_elem_alloc(mdoc, line,
ppos, tok, arg))
if (0 == cnt && (nc || MDOC_Li == tok) &&
DELIM_CLOSE == d && ! scope) {
if ( ! mdoc_elem_alloc(mdoc,
line, ppos, tok, arg))
return(0);
if (MDOC_Ar == tok || MDOC_Li == tok ||
MDOC_Fl == tok)
if (MDOC_Ar == tok || MDOC_Li == tok ||
MDOC_Fl == tok)
cnt++;
scope = 1;
}
@ -1015,7 +1012,8 @@ in_line(MACRO_PROT_ARGS)
return(0);
} else if ( ! nc && 0 == cnt) {
mdoc_argv_free(arg);
mdoc_pmsg(mdoc, line, ppos, MANDOCERR_MACROEMPTY);
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
line, ppos, mdoc_macronames[tok]);
}
if ( ! nl)
@ -1023,7 +1021,6 @@ in_line(MACRO_PROT_ARGS)
return(append_delims(mdoc, line, pos, buf));
}
static int
blk_full(MACRO_PROT_ARGS)
{
@ -1040,6 +1037,22 @@ blk_full(MACRO_PROT_ARGS)
nl = MDOC_NEWLINE & mdoc->flags;
/* Skip items outside lists. */
if (tok == MDOC_It) {
for (n = mdoc->last; n; n = n->parent)
if (n->tok == MDOC_Bl)
break;
if (n == NULL) {
mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse,
line, ppos, "It %s", buf + *pos);
if ( ! mdoc_elem_alloc(mdoc, line, ppos,
MDOC_br, NULL))
return(0);
return(rew_elem(mdoc, MDOC_br));
}
}
/* Close out prior implicit scope. */
if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
@ -1065,7 +1078,7 @@ blk_full(MACRO_PROT_ARGS)
if (ARGV_WORD == av) {
*pos = la;
break;
}
}
if (ARGV_EOLN == av)
break;
@ -1086,8 +1099,8 @@ blk_full(MACRO_PROT_ARGS)
* parsed, even though `It' macros in general are parsed.
*/
nparsed = MDOC_It == tok &&
MDOC_Bl == mdoc->last->parent->tok &&
LIST_diag == mdoc->last->parent->norm->Bl.type;
MDOC_Bl == mdoc->last->parent->tok &&
LIST_diag == mdoc->last->parent->norm->Bl.type;
/*
* The `Nd' macro has all arguments in its body: it's a hybrid
@ -1140,17 +1153,17 @@ blk_full(MACRO_PROT_ARGS)
break;
}
/*
/*
* Emit leading punctuation (i.e., punctuation before
* the MDOC_HEAD) for non-phrase types.
*/
if (NULL == head &&
ARGS_PEND != ac &&
ARGS_PHRASE != ac &&
ARGS_PPHRASE != ac &&
ARGS_QWORD != ac &&
DELIM_OPEN == mdoc_isdelim(p)) {
if (NULL == head &&
ARGS_PEND != ac &&
ARGS_PHRASE != ac &&
ARGS_PPHRASE != ac &&
ARGS_QWORD != ac &&
DELIM_OPEN == mdoc_isdelim(p)) {
if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
return(0);
continue;
@ -1164,9 +1177,9 @@ blk_full(MACRO_PROT_ARGS)
head = mdoc->last;
}
if (ARGS_PHRASE == ac ||
ARGS_PEND == ac ||
ARGS_PPHRASE == ac) {
if (ARGS_PHRASE == ac ||
ARGS_PEND == ac ||
ARGS_PPHRASE == ac) {
/*
* If we haven't opened a body yet, rewind the
* head; if we have, rewind that instead.
@ -1175,7 +1188,7 @@ blk_full(MACRO_PROT_ARGS)
mtt = body ? MDOC_BODY : MDOC_HEAD;
if ( ! rew_sub(mtt, mdoc, tok, line, ppos))
return(0);
/* Then allocate our body context. */
if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
@ -1200,8 +1213,8 @@ blk_full(MACRO_PROT_ARGS)
continue;
}
ntok = nparsed || ARGS_QWORD == ac ?
MDOC_MAX : lookup(tok, p);
ntok = nparsed || ARGS_QWORD == ac ?
MDOC_MAX : lookup(tok, p);
if (MDOC_MAX == ntok) {
if ( ! dword(mdoc, line, la, p, DELIM_MAX,
@ -1220,7 +1233,7 @@ blk_full(MACRO_PROT_ARGS)
return(0);
head = mdoc->last;
}
if (nl && ! append_delims(mdoc, line, pos, buf))
return(0);
@ -1236,9 +1249,9 @@ blk_full(MACRO_PROT_ARGS)
* sub-block.
*/
for (n = mdoc->last; n && n != head; n = n->parent) {
if (MDOC_BLOCK == n->type &&
MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
! (MDOC_VALID & n->flags)) {
if (MDOC_BLOCK == n->type &&
MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
! (MDOC_VALID & n->flags)) {
n->pending = head;
return(1);
}
@ -1264,7 +1277,6 @@ blk_full(MACRO_PROT_ARGS)
return(1);
}
static int
blk_part_imp(MACRO_PROT_ARGS)
{
@ -1315,7 +1327,7 @@ blk_part_imp(MACRO_PROT_ARGS)
break;
if (NULL == body && ARGS_QWORD != ac &&
DELIM_OPEN == mdoc_isdelim(p)) {
DELIM_OPEN == mdoc_isdelim(p)) {
if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
return(0);
continue;
@ -1349,32 +1361,13 @@ blk_part_imp(MACRO_PROT_ARGS)
body = mdoc->last;
}
for (n = body->child; n && n->next; n = n->next)
/* Do nothing. */ ;
/*
* End of sentence spacing: if the last node is a text node and
* has a trailing period, then mark it as being end-of-sentence.
*/
if (n && MDOC_TEXT == n->type && n->string)
if (mandoc_eos(n->string, strlen(n->string), 1))
n->flags |= MDOC_EOS;
/* Up-propagate the end-of-space flag. */
if (n && (MDOC_EOS & n->flags)) {
body->flags |= MDOC_EOS;
body->parent->flags |= MDOC_EOS;
}
/*
* If there is an open sub-block requiring explicit close-out,
* postpone closing out the current block
* until the rew_sub() call closing out the sub-block.
*/
for (n = mdoc->last; n && n != body && n != blk->parent;
n = n->parent) {
n = n->parent) {
if (MDOC_BLOCK == n->type &&
MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
! (MDOC_VALID & n->flags)) {
@ -1385,18 +1378,9 @@ blk_part_imp(MACRO_PROT_ARGS)
return(1);
}
}
assert(n == body);
/*
* If we can't rewind to our body, then our scope has already
* been closed by another macro (like `Oc' closing `Op'). This
* is ugly behaviour nodding its head to OpenBSD's overwhelming
* crufty use of `Op' breakage.
*/
if (n != body)
mandoc_vmsg(MANDOCERR_SCOPENEST, mdoc->parse, line, ppos,
"%s broken", mdoc_macronames[tok]);
if (n && ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
return(0);
/* Standard appending of delimiters. */
@ -1406,7 +1390,7 @@ blk_part_imp(MACRO_PROT_ARGS)
/* Rewind scope, if applicable. */
if (n && ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
return(0);
/* Move trailing .Ns out of scope. */
@ -1419,7 +1403,6 @@ blk_part_imp(MACRO_PROT_ARGS)
return(1);
}
static int
blk_part_exp(MACRO_PROT_ARGS)
{
@ -1439,7 +1422,7 @@ blk_part_exp(MACRO_PROT_ARGS)
*/
if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
return(0);
return(0);
for (head = body = NULL; ; ) {
la = *pos;
@ -1455,7 +1438,7 @@ blk_part_exp(MACRO_PROT_ARGS)
/* Flush out leading punctuation. */
if (NULL == head && ARGS_QWORD != ac &&
DELIM_OPEN == mdoc_isdelim(p)) {
DELIM_OPEN == mdoc_isdelim(p)) {
assert(NULL == body);
if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
return(0);
@ -1527,8 +1510,6 @@ blk_part_exp(MACRO_PROT_ARGS)
return(append_delims(mdoc, line, pos, buf));
}
/* ARGSUSED */
static int
in_line_argn(MACRO_PROT_ARGS)
{
@ -1550,18 +1531,20 @@ in_line_argn(MACRO_PROT_ARGS)
*/
switch (tok) {
case (MDOC_Ap):
case MDOC_Ap:
/* FALLTHROUGH */
case (MDOC_No):
case MDOC_No:
/* FALLTHROUGH */
case (MDOC_Ns):
case MDOC_Ns:
/* FALLTHROUGH */
case (MDOC_Ux):
case MDOC_Ux:
maxargs = 0;
break;
case (MDOC_Bx):
case MDOC_Bx:
/* FALLTHROUGH */
case (MDOC_Xr):
case MDOC_Es:
/* FALLTHROUGH */
case MDOC_Xr:
maxargs = 2;
break;
default:
@ -1576,7 +1559,7 @@ in_line_argn(MACRO_PROT_ARGS)
if (ARGV_WORD == av) {
*pos = la;
break;
}
}
if (ARGV_EOLN == av)
break;
@ -1598,14 +1581,14 @@ in_line_argn(MACRO_PROT_ARGS)
if (ARGS_EOLN == ac)
break;
if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
ARGS_QWORD != ac && 0 == j &&
DELIM_OPEN == mdoc_isdelim(p)) {
if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
ARGS_QWORD != ac && 0 == j &&
DELIM_OPEN == mdoc_isdelim(p)) {
if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
return(0);
continue;
} else if (0 == j)
if ( ! mdoc_elem_alloc(mdoc, line, la, tok, arg))
if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
return(0);
if (j == maxargs && ! flushed) {
@ -1627,9 +1610,9 @@ in_line_argn(MACRO_PROT_ARGS)
}
if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
ARGS_QWORD != ac &&
! flushed &&
DELIM_NONE != mdoc_isdelim(p)) {
ARGS_QWORD != ac &&
! flushed &&
DELIM_NONE != mdoc_isdelim(p)) {
if ( ! rew_elem(mdoc, tok))
return(0);
flushed = 1;
@ -1641,7 +1624,7 @@ in_line_argn(MACRO_PROT_ARGS)
j++;
}
if (0 == j && ! mdoc_elem_alloc(mdoc, line, la, tok, arg))
if (0 == j && ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
return(0);
/* Close out in a consistent state. */
@ -1653,7 +1636,6 @@ in_line_argn(MACRO_PROT_ARGS)
return(append_delims(mdoc, line, pos, buf));
}
static int
in_line_eoln(MACRO_PROT_ARGS)
{
@ -1679,7 +1661,7 @@ in_line_eoln(MACRO_PROT_ARGS)
*pos = la;
break;
}
if (ARGV_EOLN == av)
if (ARGV_EOLN == av)
break;
if (ARGV_ARG == av)
continue;
@ -1723,8 +1705,6 @@ in_line_eoln(MACRO_PROT_ARGS)
return(rew_elem(mdoc, tok));
}
/* ARGSUSED */
static int
ctx_synopsis(MACRO_PROT_ARGS)
{
@ -1751,17 +1731,6 @@ ctx_synopsis(MACRO_PROT_ARGS)
return(blk_part_imp(mdoc, tok, line, ppos, pos, buf));
}
/* ARGSUSED */
static int
obsolete(MACRO_PROT_ARGS)
{
mdoc_pmsg(mdoc, line, ppos, MANDOCERR_MACROOBS);
return(1);
}
/*
* Phrases occur within `Bl -column' entries, separated by `Ta' or tabs.
* They're unusual because they're basically free-form text until a
@ -1801,8 +1770,6 @@ phrase(struct mdoc *mdoc, int line, int ppos, char *buf)
return(1);
}
/* ARGSUSED */
static int
phrase_ta(MACRO_PROT_ARGS)
{
@ -1817,7 +1784,8 @@ phrase_ta(MACRO_PROT_ARGS)
while (NULL != n && MDOC_Bl != n->tok)
n = n->parent;
if (NULL == n || LIST_column != n->norm->Bl.type) {
mdoc_pmsg(mdoc, line, ppos, MANDOCERR_STRAYTA);
mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse,
line, ppos, "Ta");
return(1);
}

View File

@ -1,6 +1,6 @@
/* $Id: mdoc_man.c,v 1.57 2013/12/25 22:00:45 schwarze Exp $ */
/* $Id: mdoc_man.c,v 1.68 2014/08/06 15:09:05 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -23,6 +23,7 @@
#include <string.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "out.h"
#include "man.h"
#include "mdoc.h"
@ -50,6 +51,7 @@ static void post_bf(DECL_ARGS);
static void post_bk(DECL_ARGS);
static void post_bl(DECL_ARGS);
static void post_dl(DECL_ARGS);
static void post_en(DECL_ARGS);
static void post_enc(DECL_ARGS);
static void post_eo(DECL_ARGS);
static void post_fa(DECL_ARGS);
@ -77,8 +79,11 @@ 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);
static int pre_em(DECL_ARGS);
static int pre_es(DECL_ARGS);
static int pre_ex(DECL_ARGS);
static int pre_fa(DECL_ARGS);
static int pre_fd(DECL_ARGS);
static int pre_fl(DECL_ARGS);
@ -89,11 +94,13 @@ static int pre_in(DECL_ARGS);
static int pre_it(DECL_ARGS);
static int pre_lk(DECL_ARGS);
static int pre_li(DECL_ARGS);
static int pre_ll(DECL_ARGS);
static int pre_nm(DECL_ARGS);
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);
@ -134,9 +141,7 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ NULL, pre_li, post_font, NULL, NULL }, /* Dv */
{ NULL, pre_li, post_font, NULL, NULL }, /* Er */
{ NULL, pre_li, post_font, NULL, NULL }, /* Ev */
{ NULL, pre_enc, post_enc, "The \\fB",
"\\fP\nutility exits 0 on success, and >0 if an error occurs."
}, /* Ex */
{ NULL, pre_ex, NULL, NULL, NULL }, /* Ex */
{ NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
{ NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
{ NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
@ -148,13 +153,9 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
{ NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
{ NULL, NULL, NULL, NULL, NULL }, /* Ot */
{ NULL, pre_ft, post_font, NULL, NULL }, /* Ot */
{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
{ NULL, pre_enc, post_enc, "The \\fB",
"\\fP\nfunction returns the value 0 if successful;\n"
"otherwise the value -1 is returned and the global\n"
"variable \\fIerrno\\fP is set to indicate the error."
}, /* Rv */
{ NULL, pre_rv, 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 */
@ -222,7 +223,7 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
{ NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
{ NULL, NULL, NULL, NULL, NULL }, /* Fr */
{ NULL, pre_em, post_font, NULL, NULL }, /* Fr */
{ NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
{ NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
@ -232,14 +233,15 @@ static const struct manact manacts[MDOC_MAX + 1] = {
{ cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
{ NULL, NULL, post_percent, NULL, NULL }, /* %C */
{ NULL, NULL, NULL, NULL, NULL }, /* Es */
{ NULL, NULL, NULL, NULL, NULL }, /* En */
{ NULL, pre_es, NULL, NULL, NULL }, /* Es */
{ cond_body, pre_en, post_en, NULL, NULL }, /* En */
{ NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
{ NULL, pre_br, NULL, NULL, NULL }, /* br */
{ NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
{ NULL, pre_ll, post_sp, NULL, NULL }, /* ll */
{ NULL, NULL, NULL, NULL, NULL }, /* ROOT */
};
@ -271,6 +273,7 @@ static struct {
size_t size;
} fontqueue;
static void
font_push(char newfont)
{
@ -278,7 +281,7 @@ font_push(char newfont)
if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
fontqueue.size += 8;
fontqueue.head = mandoc_realloc(fontqueue.head,
fontqueue.size);
fontqueue.size);
}
*fontqueue.tail = newfont;
print_word("");
@ -304,7 +307,7 @@ print_word(const char *s)
{
if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
/*
/*
* If we need a newline, print it now and start afresh.
*/
if (MMAN_PP & outflags) {
@ -359,13 +362,16 @@ print_word(const char *s)
for ( ; *s; s++) {
switch (*s) {
case (ASCII_NBRSP):
case ASCII_NBRSP:
printf("\\ ");
break;
case (ASCII_HYPH):
case ASCII_HYPH:
putchar('-');
break;
case (' '):
case ASCII_BREAK:
printf("\\:");
break;
case ' ':
if (MMAN_nbrword & outflags) {
printf("\\ ");
break;
@ -450,7 +456,7 @@ print_offs(const char *v)
if (Bl_stack_len)
sz += Bl_stack[Bl_stack_len - 1];
snprintf(buf, sizeof(buf), "%zun", sz);
(void)snprintf(buf, sizeof(buf), "%zun", sz);
print_word(buf);
outflags |= MMAN_nl;
}
@ -458,7 +464,7 @@ print_offs(const char *v)
/*
* Set up the indentation for a list item; used from pre_it().
*/
void
static void
print_width(const char *v, const struct mdoc_node *child, size_t defsz)
{
char buf[24];
@ -484,7 +490,7 @@ print_width(const char *v, const struct mdoc_node *child, size_t defsz)
/* XXX Rough estimation, might have multiple parts. */
chsz = (NULL != child && MDOC_TEXT == child->type) ?
strlen(child->string) : 0;
strlen(child->string) : 0;
/* Maybe we are inside an enclosing list? */
mid_it();
@ -503,19 +509,19 @@ print_width(const char *v, const struct mdoc_node *child, size_t defsz)
remain = sz + 2;
}
if (numeric) {
snprintf(buf, sizeof(buf), "%zun", sz + 2);
(void)snprintf(buf, sizeof(buf), "%zun", sz + 2);
print_word(buf);
} else
print_word(v);
TPremain = remain;
}
void
static void
print_count(int *count)
{
char buf[12];
char buf[24];
snprintf(buf, sizeof(buf), "%d.", ++*count);
(void)snprintf(buf, sizeof(buf), "%d.", ++*count);
print_word(buf);
}
@ -542,8 +548,9 @@ man_mdoc(void *arg, const struct mdoc *mdoc)
n = mdoc_node(mdoc);
printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
meta->title, meta->msec, meta->date,
meta->os, meta->vol);
meta->title,
(meta->msec == NULL ? "" : meta->msec),
meta->date, meta->os, meta->vol);
/* Disable hyphenation and if nroff, disable justification. */
printf(".nh\n.if n .ad l");
@ -581,8 +588,8 @@ print_node(DECL_ARGS)
* Make sure that we don't happen to start with a
* control character at the start of a line.
*/
if (MMAN_nl & outflags && ('.' == *n->string ||
'\'' == *n->string)) {
if (MMAN_nl & outflags &&
('.' == *n->string || '\'' == *n->string)) {
print_word("");
printf("\\&");
outflags &= ~MMAN_spc;
@ -595,11 +602,11 @@ print_node(DECL_ARGS)
*/
act = manacts + n->tok;
cond = NULL == act->cond || (*act->cond)(meta, n);
if (cond && act->pre)
if (cond && act->pre && ENDBODY_NOT == n->end)
do_sub = (*act->pre)(meta, n);
}
/*
/*
* Conditionally run all child nodes.
* Note that this iterates over children instead of using
* recursion. This prevents unnecessary depth in the stack.
@ -611,8 +618,17 @@ print_node(DECL_ARGS)
/*
* Lastly, conditionally run the post-node handler.
*/
if (MDOC_ENDED & n->flags)
return;
if (cond && act->post)
(*act->post)(meta, n);
if (ENDBODY_NOT != n->end)
n->pending->flags |= MDOC_ENDED;
if (ENDBODY_NOSPACE == n->end)
outflags &= ~(MMAN_spc | MMAN_nl);
}
static int
@ -650,10 +666,46 @@ post_enc(DECL_ARGS)
suffix = manacts[n->tok].suffix;
if (NULL == suffix)
return;
outflags &= ~MMAN_spc;
outflags &= ~(MMAN_spc | MMAN_nl);
print_word(suffix);
}
static int
pre_ex(DECL_ARGS)
{
int nchild;
outflags |= MMAN_br | MMAN_nl;
print_word("The");
nchild = n->nchild;
for (n = n->child; n; n = n->next) {
font_push('B');
print_word(n->string);
font_pop();
if (n->next == NULL)
continue;
if (nchild > 2) {
outflags &= ~MMAN_spc;
print_word(",");
}
if (n->next->next == NULL)
print_word("and");
}
if (nchild > 1)
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);
}
static void
post_font(DECL_ARGS)
{
@ -682,8 +734,8 @@ static int
pre__t(DECL_ARGS)
{
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T) {
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T) {
print_word("");
putchar('\"');
outflags &= ~MMAN_spc;
@ -696,8 +748,8 @@ static void
post__t(DECL_ARGS)
{
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T) {
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T) {
outflags &= ~MMAN_spc;
print_word("");
putchar('\"');
@ -749,26 +801,26 @@ pre_syn(const struct mdoc_node *n)
return;
if (n->prev->tok == n->tok &&
MDOC_Ft != n->tok &&
MDOC_Fo != n->tok &&
MDOC_Fn != n->tok) {
MDOC_Ft != n->tok &&
MDOC_Fo != n->tok &&
MDOC_Fn != n->tok) {
outflags |= MMAN_br;
return;
}
switch (n->prev->tok) {
case (MDOC_Fd):
case MDOC_Fd:
/* FALLTHROUGH */
case (MDOC_Fn):
case MDOC_Fn:
/* FALLTHROUGH */
case (MDOC_Fo):
case MDOC_Fo:
/* FALLTHROUGH */
case (MDOC_In):
case MDOC_In:
/* FALLTHROUGH */
case (MDOC_Vt):
case MDOC_Vt:
outflags |= MMAN_sp;
break;
case (MDOC_Ft):
case MDOC_Ft:
if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
outflags |= MMAN_sp;
break;
@ -785,11 +837,11 @@ pre_an(DECL_ARGS)
{
switch (n->norm->An.auth) {
case (AUTH_split):
case AUTH_split:
outflags &= ~MMAN_An_nosplit;
outflags |= MMAN_An_split;
return(0);
case (AUTH_nosplit):
case AUTH_nosplit:
outflags &= ~MMAN_An_split;
outflags |= MMAN_An_nosplit;
return(0);
@ -848,18 +900,18 @@ pre_bf(DECL_ARGS)
{
switch (n->type) {
case (MDOC_BLOCK):
case MDOC_BLOCK:
return(1);
case (MDOC_BODY):
case MDOC_BODY:
break;
default:
return(0);
}
switch (n->norm->Bf.font) {
case (FONT_Em):
case FONT_Em:
font_push('I');
break;
case (FONT_Sy):
case FONT_Sy:
font_push('B');
break;
default:
@ -882,9 +934,9 @@ pre_bk(DECL_ARGS)
{
switch (n->type) {
case (MDOC_BLOCK):
case MDOC_BLOCK:
return(1);
case (MDOC_BODY):
case MDOC_BODY:
outflags |= MMAN_Bk;
return(1);
default:
@ -916,10 +968,10 @@ pre_bl(DECL_ARGS)
}
switch (n->norm->Bl.type) {
case (LIST_enum):
case LIST_enum:
n->norm->Bl.count = 0;
return(1);
case (LIST_column):
case LIST_column:
break;
default:
return(1);
@ -938,10 +990,10 @@ post_bl(DECL_ARGS)
{
switch (n->norm->Bl.type) {
case (LIST_column):
case LIST_column:
print_line(".TE", 0);
break;
case (LIST_enum):
case LIST_enum:
n->norm->Bl.count = 0;
break;
default:
@ -1019,6 +1071,33 @@ pre_em(DECL_ARGS)
return(1);
}
static int
pre_en(DECL_ARGS)
{
if (NULL == n->norm->Es ||
NULL == n->norm->Es->child)
return(1);
print_word(n->norm->Es->child->string);
outflags &= ~MMAN_spc;
return(1);
}
static void
post_en(DECL_ARGS)
{
if (NULL == n->norm->Es ||
NULL == n->norm->Es->child ||
NULL == n->norm->Es->child->next)
return;
outflags &= ~MMAN_spc;
print_word(n->norm->Es->child->next->string);
return;
}
static void
post_eo(DECL_ARGS)
{
@ -1027,6 +1106,13 @@ post_eo(DECL_ARGS)
outflags &= ~MMAN_spc;
}
static int
pre_es(DECL_ARGS)
{
return(0);
}
static int
pre_fa(DECL_ARGS)
{
@ -1136,15 +1222,15 @@ pre_fo(DECL_ARGS)
{
switch (n->type) {
case (MDOC_BLOCK):
case MDOC_BLOCK:
pre_syn(n);
break;
case (MDOC_HEAD):
case MDOC_HEAD:
if (MDOC_SYNPRETTY & n->flags)
print_block(".HP 4n", MMAN_nl);
font_push('B');
break;
case (MDOC_BODY):
case MDOC_BODY:
outflags &= ~MMAN_spc;
print_word("(");
outflags &= ~MMAN_spc;
@ -1160,10 +1246,10 @@ post_fo(DECL_ARGS)
{
switch (n->type) {
case (MDOC_HEAD):
case MDOC_HEAD:
font_pop();
break;
case (MDOC_BODY):
case MDOC_BODY:
post_fn(meta, n);
break;
default:
@ -1219,7 +1305,7 @@ pre_it(DECL_ARGS)
const struct mdoc_node *bln;
switch (n->type) {
case (MDOC_HEAD):
case MDOC_HEAD:
outflags |= MMAN_PP | MMAN_nl;
bln = n->parent->parent;
if (0 == bln->norm->Bl.comp ||
@ -1228,24 +1314,24 @@ pre_it(DECL_ARGS)
outflags |= MMAN_sp;
outflags &= ~MMAN_br;
switch (bln->norm->Bl.type) {
case (LIST_item):
case LIST_item:
return(0);
case (LIST_inset):
case LIST_inset:
/* FALLTHROUGH */
case (LIST_diag):
case LIST_diag:
/* FALLTHROUGH */
case (LIST_ohang):
case LIST_ohang:
if (bln->norm->Bl.type == LIST_diag)
print_line(".B \"", 0);
else
print_line(".R \"", 0);
outflags &= ~MMAN_spc;
return(1);
case (LIST_bullet):
case LIST_bullet:
/* FALLTHROUGH */
case (LIST_dash):
case LIST_dash:
/* FALLTHROUGH */
case (LIST_hyphen):
case LIST_hyphen:
print_width(bln->norm->Bl.width, NULL, 0);
TPremain = 0;
outflags |= MMAN_nl;
@ -1255,18 +1341,21 @@ pre_it(DECL_ARGS)
else
print_word("-");
font_pop();
break;
case (LIST_enum):
outflags |= MMAN_nl;
return(0);
case LIST_enum:
print_width(bln->norm->Bl.width, NULL, 0);
TPremain = 0;
outflags |= MMAN_nl;
print_count(&bln->norm->Bl.count);
break;
case (LIST_hang):
outflags |= MMAN_nl;
return(0);
case LIST_hang:
print_width(bln->norm->Bl.width, n->child, 6);
TPremain = 0;
break;
case (LIST_tag):
outflags |= MMAN_nl;
return(1);
case LIST_tag:
print_width(bln->norm->Bl.width, n->child, 0);
putchar('\n');
outflags &= ~MMAN_spc;
@ -1274,7 +1363,6 @@ pre_it(DECL_ARGS)
default:
return(1);
}
outflags |= MMAN_nl;
default:
break;
}
@ -1300,7 +1388,8 @@ mid_it(void)
/* Restore the indentation of the enclosing list. */
print_line(".RS", MMAN_Bk_susp);
snprintf(buf, sizeof(buf), "%zun", Bl_stack[Bl_stack_len - 1]);
(void)snprintf(buf, sizeof(buf), "%zun",
Bl_stack[Bl_stack_len - 1]);
print_word(buf);
/* Remeber to close out this .RS block later. */
@ -1315,32 +1404,32 @@ post_it(DECL_ARGS)
bln = n->parent->parent;
switch (n->type) {
case (MDOC_HEAD):
case MDOC_HEAD:
switch (bln->norm->Bl.type) {
case (LIST_diag):
case LIST_diag:
outflags &= ~MMAN_spc;
print_word("\\ ");
break;
case (LIST_ohang):
case LIST_ohang:
outflags |= MMAN_br;
break;
default:
break;
}
break;
case (MDOC_BODY):
case MDOC_BODY:
switch (bln->norm->Bl.type) {
case (LIST_bullet):
case LIST_bullet:
/* FALLTHROUGH */
case (LIST_dash):
case LIST_dash:
/* FALLTHROUGH */
case (LIST_hyphen):
case LIST_hyphen:
/* FALLTHROUGH */
case (LIST_enum):
case LIST_enum:
/* FALLTHROUGH */
case (LIST_hang):
case LIST_hang:
/* FALLTHROUGH */
case (LIST_tag):
case LIST_tag:
assert(Bl_stack_len);
Bl_stack[--Bl_stack_len] = 0;
@ -1354,7 +1443,7 @@ post_it(DECL_ARGS)
Bl_stack_post[Bl_stack_len] = 0;
}
break;
case (LIST_column):
case LIST_column:
if (NULL != n->next) {
putchar('\t');
outflags &= ~MMAN_spc;
@ -1401,6 +1490,14 @@ pre_lk(DECL_ARGS)
return(0);
}
static int
pre_ll(DECL_ARGS)
{
print_line(".ll", 0);
return(1);
}
static int
pre_li(DECL_ARGS)
{
@ -1441,13 +1538,14 @@ post_nm(DECL_ARGS)
{
switch (n->type) {
case (MDOC_BLOCK):
case MDOC_BLOCK:
outflags &= ~MMAN_Bk;
break;
case (MDOC_HEAD):
case MDOC_HEAD:
/* FALLTHROUGH */
case (MDOC_ELEM):
font_pop();
case MDOC_ELEM:
if (n->child != NULL || meta->name != NULL)
font_pop();
break;
default:
break;
@ -1499,15 +1597,72 @@ pre_rs(DECL_ARGS)
return(1);
}
static int
pre_rv(DECL_ARGS)
{
int nchild;
outflags |= MMAN_br | MMAN_nl;
nchild = n->nchild;
if (nchild > 0) {
print_word("The");
for (n = n->child; n; n = n->next) {
font_push('B');
print_word(n->string);
font_pop();
outflags &= ~MMAN_spc;
print_word("()");
if (n->next == NULL)
continue;
if (nchild > 2) {
outflags &= ~MMAN_spc;
print_word(",");
}
if (n->next->next == NULL)
print_word("and");
}
if (nchild > 1)
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_sm(DECL_ARGS)
{
assert(n->child && MDOC_TEXT == n->child->type);
if (0 == strcmp("on", n->child->string))
outflags |= MMAN_Sm | MMAN_spc;
if (NULL == n->child)
outflags ^= MMAN_Sm;
else if (0 == strcmp("on", n->child->string))
outflags |= MMAN_Sm;
else
outflags &= ~MMAN_Sm;
if (MMAN_Sm & outflags)
outflags |= MMAN_spc;
return(0);
}
@ -1544,10 +1699,10 @@ pre_vt(DECL_ARGS)
if (MDOC_SYNPRETTY & n->flags) {
switch (n->type) {
case (MDOC_BLOCK):
case MDOC_BLOCK:
pre_syn(n);
return(1);
case (MDOC_BODY):
case MDOC_BODY:
break;
default:
return(0);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3
msec.c
View File

@ -1,4 +1,4 @@
/* $Id: msec.c,v 1.10 2011/12/02 01:37:14 schwarze Exp $ */
/* $Id: msec.c,v 1.11 2014/03/23 11:25:26 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -18,7 +18,6 @@
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"

58
out.c
View File

@ -1,4 +1,4 @@
/* $Id: out.c,v 1.46 2013/10/05 20:30:05 schwarze Exp $ */
/* $Id: out.c,v 1.49 2014/08/01 19:25:52 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@ -28,6 +28,7 @@
#include <string.h>
#include <time.h>
#include "mandoc_aux.h"
#include "mandoc.h"
#include "out.h"
@ -38,7 +39,8 @@ static void tblcalc_literal(struct rofftbl *, struct roffcol *,
static void tblcalc_number(struct rofftbl *, struct roffcol *,
const struct tbl_opts *, const struct tbl_dat *);
/*
/*
* Convert a `scaling unit' to a consistent form, or fail. Scaling
* units are documented in groff.7, mdoc.7, man.7.
*/
@ -55,10 +57,10 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
i = hasd = 0;
switch (*src) {
case ('+'):
case '+':
src++;
break;
case ('-'):
case '-':
buf[i++] = *src++;
break;
default:
@ -86,39 +88,39 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
buf[i] = '\0';
switch (*src) {
case ('c'):
case 'c':
unit = SCALE_CM;
break;
case ('i'):
case 'i':
unit = SCALE_IN;
break;
case ('P'):
case 'P':
unit = SCALE_PC;
break;
case ('p'):
case 'p':
unit = SCALE_PT;
break;
case ('f'):
case 'f':
unit = SCALE_FS;
break;
case ('v'):
case 'v':
unit = SCALE_VS;
break;
case ('m'):
case 'm':
unit = SCALE_EM;
break;
case ('\0'):
case '\0':
if (SCALE_MAX == def)
return(0);
unit = SCALE_BU;
break;
case ('u'):
case 'u':
unit = SCALE_BU;
break;
case ('M'):
case 'M':
unit = SCALE_MM;
break;
case ('n'):
case 'n':
unit = SCALE_EN;
break;
default:
@ -126,8 +128,8 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
}
/* FIXME: do this in the caller. */
if ((dst->scale = atof(buf)) < 0)
dst->scale = 0;
if ((dst->scale = atof(buf)) < 0.0)
dst->scale = 0.0;
dst->unit = unit;
return(1);
}
@ -152,8 +154,8 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
*/
assert(NULL == tbl->cols);
tbl->cols = mandoc_calloc
((size_t)sp->opts->cols, sizeof(struct roffcol));
tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
sizeof(struct roffcol));
for ( ; sp; sp = sp->next) {
if (TBL_SPAN_DATA != sp->pos)
@ -186,26 +188,26 @@ tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
/* Branch down into data sub-types. */
switch (dp->layout->pos) {
case (TBL_CELL_HORIZ):
case TBL_CELL_HORIZ:
/* FALLTHROUGH */
case (TBL_CELL_DHORIZ):
case TBL_CELL_DHORIZ:
sz = (*tbl->len)(1, tbl->arg);
if (col->width < sz)
col->width = sz;
break;
case (TBL_CELL_LONG):
case TBL_CELL_LONG:
/* FALLTHROUGH */
case (TBL_CELL_CENTRE):
case TBL_CELL_CENTRE:
/* FALLTHROUGH */
case (TBL_CELL_LEFT):
case TBL_CELL_LEFT:
/* FALLTHROUGH */
case (TBL_CELL_RIGHT):
case TBL_CELL_RIGHT:
tblcalc_literal(tbl, col, dp);
break;
case (TBL_CELL_NUMBER):
case TBL_CELL_NUMBER:
tblcalc_number(tbl, col, opts, dp);
break;
case (TBL_CELL_DOWN):
case TBL_CELL_DOWN:
break;
default:
abort();
@ -231,7 +233,7 @@ static void
tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
const struct tbl_opts *opts, const struct tbl_dat *dp)
{
int i;
int i;
size_t sz, psz, ssz, d;
const char *str;
char *cp;

6
out.h
View File

@ -1,4 +1,4 @@
/* $Id: out.h,v 1.21 2011/07/17 15:24:25 kristaps Exp $ */
/* $Id: out.h,v 1.22 2014/04/20 16:46:05 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -63,8 +63,8 @@ __BEGIN_DECLS
(p)->scale = (v); } \
while (/* CONSTCOND */ 0)
int a2roffsu(const char *, struct roffsu *, enum roffscale);
void tblcalc(struct rofftbl *tbl, const struct tbl_span *);
int a2roffsu(const char *, struct roffsu *, enum roffscale);
void tblcalc(struct rofftbl *tbl, const struct tbl_span *);
__END_DECLS

365
read.c
View File

@ -1,7 +1,8 @@
/* $Id: read.c,v 1.39 2013/09/16 00:25:07 schwarze Exp $ */
/* $Id: read.c,v 1.79 2014/08/06 15:09:05 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -26,6 +27,7 @@
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdint.h>
@ -35,6 +37,7 @@
#include <unistd.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmandoc.h"
#include "mdoc.h"
#include "man.h"
@ -43,7 +46,7 @@
#define REPARSE_LIMIT 1000
struct buf {
char *buf; /* binary input buffer */
char *buf; /* binary input buffer */
size_t sz; /* size of binary buffer */
};
@ -51,24 +54,25 @@ struct mparse {
enum mandoclevel file_status; /* status of current parse */
enum mandoclevel wlevel; /* ignore messages below this */
int line; /* line number in the file */
enum mparset inttype; /* which parser to use */
int options; /* parser options */
struct man *pman; /* persistent man parser */
struct mdoc *pmdoc; /* persistent mdoc parser */
struct man *man; /* man parser */
struct mdoc *mdoc; /* mdoc parser */
struct roff *roff; /* roff parser (!NULL) */
char *sodest; /* filename pointed to by .so */
int reparse_count; /* finite interp. stack */
mandocmsg mmsg; /* warning/error message handler */
void *arg; /* argument to mmsg */
const char *file;
const char *file;
struct buf *secondary;
char *defos; /* default operating system */
const char *defos; /* default operating system */
};
static void resize_buf(struct buf *, size_t);
static void mparse_buf_r(struct mparse *, struct buf, int);
static void pset(const char *, int, struct mparse *);
static int read_whole_file(const char *, int, struct buf *, int *);
static int read_whole_file(struct mparse *, const char *, int,
struct buf *, int *);
static void mparse_end(struct mparse *);
static void mparse_parse_buffer(struct mparse *, struct buf,
const char *);
@ -89,67 +93,79 @@ static const char * const mandocerrs[MANDOCERR_MAX] = {
"generic warning",
/* related to the prologue */
"no title in document",
"document title should be all caps",
"missing manual title, using UNTITLED",
"missing manual title, using \"\"",
"lower case character in document title",
"missing manual section, using \"\"",
"unknown manual section",
"unknown manual volume or arch",
"date missing, using today's date",
"missing date, using today's date",
"cannot parse date, using it verbatim",
"prologue macros out of order",
"missing Os macro, using \"\"",
"duplicate prologue macro",
"macro not allowed in prologue",
"macro not allowed in body",
"late prologue macro",
"skipping late title macro",
"prologue macros out of order",
/* related to document structure */
".so is fragile, better use ln(1)",
"NAME section must come first",
"no document body",
"content before first section header",
"first section is not \"NAME\"",
"bad NAME section contents",
"sections out of conventional order",
"duplicate section name",
"section header suited to sections 2, 3, and 9 only",
"duplicate section title",
"unexpected section",
/* related to macros and nesting */
"skipping obsolete macro",
"obsolete macro",
"skipping paragraph macro",
"moving paragraph macro out of list",
"skipping no-space macro",
"blocks badly nested",
"child violates parent syntax",
"nested displays are not portable",
"already in literal mode",
"moving content out of list",
".Vt block has child macro",
"fill mode already enabled, skipping",
"fill mode already disabled, skipping",
"line scope broken",
/* related to missing macro arguments */
"skipping empty request",
"conditional request controls empty scope",
"skipping empty macro",
"empty argument, using 0n",
"argument count wrong",
"missing display type",
"list type must come first",
"tag lists require a width argument",
"missing font type",
"skipping end of block that is not open",
"missing display type, using -ragged",
"list type is not the first argument",
"missing -width in -tag list, using 8n",
"missing utility name, using \"\"",
"empty head in list item",
"empty list item",
"missing font type, using \\fR",
"unknown font type, using \\fR",
"missing -std argument, adding it",
/* related to bad macro arguments */
"skipping argument",
"unterminated quoted argument",
"duplicate argument",
"duplicate display type",
"duplicate list type",
"skipping duplicate argument",
"skipping duplicate display type",
"skipping duplicate list type",
"skipping -width argument",
"unknown AT&T UNIX version",
"bad Boolean value",
"unknown font",
"unknown standard specifier",
"bad width argument",
"invalid content in Rs block",
"invalid Boolean argument",
"unknown font, skipping request",
/* related to plain text */
"blank line in non-literal context",
"tab in non-literal context",
"end of line whitespace",
"blank line in fill mode, using .sp",
"tab in filled text",
"whitespace at end of input line",
"bad comment style",
"bad escape sequence",
"unterminated quoted string",
"invalid escape sequence",
"undefined string, using \"\"",
/* related to equations */
"unexpected literal in equation",
"generic error",
/* related to equations */
@ -169,39 +185,38 @@ static const char * const mandocerrs[MANDOCERR_MAX] = {
"data block still open",
"ignoring extra data cells",
/* related to document structure and macros */
"input stack limit exceeded, infinite loop?",
"skipping bad character",
"escaped character not allowed in a name",
"manual name not yet set",
"skipping text before the first section header",
"skipping unknown macro",
"NOT IMPLEMENTED, please use groff: skipping request",
"argument count wrong",
"skipping item outside list",
"skipping column outside column list",
"skipping end of block that is not open",
"missing end of block",
"scope open on exit",
"uname(3) system call failed",
"macro requires line argument(s)",
"macro requires body argument(s)",
"macro requires argument(s)",
"request requires a numeric argument",
"missing list type",
"line argument(s) will be lost",
"body argument(s) will be lost",
"inserting missing end of block",
"appending missing end of block",
/* related to request and macro arguments */
"escaped character not allowed in a name",
"argument count wrong",
"missing list type, using -item",
"missing manual name, using \"\"",
"uname(3) system call failed, using UNKNOWN",
"unknown standard specifier",
"skipping request without numeric argument",
"skipping all arguments",
"skipping excess arguments",
"generic fatal error",
"not a manual",
"column syntax is inconsistent",
"NOT IMPLEMENTED: .Bd -file",
"argument count wrong, violates syntax",
"child violates parent syntax",
"argument count wrong, violates syntax",
"input too large",
"NOT IMPLEMENTED: Bd -file",
"NOT IMPLEMENTED: .so with absolute path or \"..\"",
"no document body",
"no document prologue",
"static buffer exhausted",
".so request failed",
/* system errors */
NULL,
"cannot stat file",
"cannot read file",
};
static const char * const mandoclevels[MANDOCLEVEL_MAX] = {
@ -214,6 +229,7 @@ static const char * const mandoclevels[MANDOCLEVEL_MAX] = {
"SYSERR"
};
static void
resize_buf(struct buf *buf, size_t initial)
{
@ -246,35 +262,27 @@ pset(const char *buf, int pos, struct mparse *curp)
return;
}
switch (curp->inttype) {
case (MPARSE_MDOC):
if (NULL == curp->pmdoc)
curp->pmdoc = mdoc_alloc(curp->roff, curp,
curp->defos);
assert(curp->pmdoc);
if (MPARSE_MDOC & curp->options) {
curp->mdoc = curp->pmdoc;
return;
case (MPARSE_MAN):
if (NULL == curp->pman)
curp->pman = man_alloc(curp->roff, curp);
assert(curp->pman);
} else if (MPARSE_MAN & curp->options) {
curp->man = curp->pman;
return;
default:
break;
}
if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
if (NULL == curp->pmdoc)
curp->pmdoc = mdoc_alloc(curp->roff, curp,
curp->defos);
if (NULL == curp->pmdoc)
curp->pmdoc = mdoc_alloc(
curp->roff, curp, curp->defos,
MPARSE_QUICK & curp->options ? 1 : 0);
assert(curp->pmdoc);
curp->mdoc = curp->pmdoc;
return;
}
}
if (NULL == curp->pman)
curp->pman = man_alloc(curp->roff, curp);
if (NULL == curp->pman)
curp->pman = man_alloc(curp->roff, curp,
MPARSE_QUICK & curp->options ? 1 : 0);
assert(curp->pman);
curp->man = curp->pman;
}
@ -297,8 +305,8 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
memset(&ln, 0, sizeof(struct buf));
lnn = curp->line;
pos = 0;
lnn = curp->line;
pos = 0;
for (i = 0; i < (int)blk.sz; ) {
if (0 == pos && '\0' == blk.buf[i])
@ -335,7 +343,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
if (pos + 2 >= (int)ln.sz)
resize_buf(&ln, 256);
/*
/*
* Warn about bogus characters. If you're using
* non-ASCII encoding, you're screwing your
* readers. Since I'd rather this not happen,
@ -346,10 +354,10 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
c = (unsigned char) blk.buf[i];
if ( ! (isascii(c) &&
(isgraph(c) || isblank(c)))) {
mandoc_msg(MANDOCERR_BADCHAR, curp,
curp->line, pos, NULL);
if ( ! (isascii(c) &&
(isgraph(c) || isblank(c)))) {
mandoc_vmsg(MANDOCERR_BADCHAR, curp,
curp->line, pos, "0x%x", c);
i++;
ln.buf[pos++] = '?';
continue;
@ -403,10 +411,10 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
c = (unsigned char) blk.buf[i+1];
if ( ! (isascii(c) &&
(isgraph(c) || isblank(c)))) {
mandoc_msg(MANDOCERR_BADCHAR, curp,
curp->line, pos, NULL);
if ( ! (isascii(c) &&
(isgraph(c) || isblank(c)))) {
mandoc_vmsg(MANDOCERR_BADCHAR, curp,
curp->line, pos, "0x%x", c);
i += 2;
ln.buf[pos++] = '?';
continue;
@ -418,7 +426,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
ln.buf[pos++] = blk.buf[i++];
}
if (pos >= (int)ln.sz)
if (pos >= (int)ln.sz)
resize_buf(&ln, 256);
ln.buf[pos] = '\0';
@ -441,13 +449,12 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
*/
if (curp->secondary) {
curp->secondary->buf =
mandoc_realloc
(curp->secondary->buf,
curp->secondary->sz + pos + 2);
memcpy(curp->secondary->buf +
curp->secondary->sz,
ln.buf, pos);
curp->secondary->buf = mandoc_realloc(
curp->secondary->buf,
curp->secondary->sz + pos + 2);
memcpy(curp->secondary->buf +
curp->secondary->sz,
ln.buf, pos);
curp->secondary->sz += pos;
curp->secondary->buf
[curp->secondary->sz] = '\n';
@ -456,41 +463,50 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
[curp->secondary->sz] = '\0';
}
rerun:
rr = roff_parseln
(curp->roff, curp->line,
&ln.buf, &ln.sz, of, &of);
rr = roff_parseln(curp->roff, curp->line,
&ln.buf, &ln.sz, of, &of);
switch (rr) {
case (ROFF_REPARSE):
case ROFF_REPARSE:
if (REPARSE_LIMIT >= ++curp->reparse_count)
mparse_buf_r(curp, ln, 0);
else
mandoc_msg(MANDOCERR_ROFFLOOP, curp,
curp->line, pos, NULL);
curp->line, pos, NULL);
pos = 0;
continue;
case (ROFF_APPEND):
case ROFF_APPEND:
pos = (int)strlen(ln.buf);
continue;
case (ROFF_RERUN):
case ROFF_RERUN:
goto rerun;
case (ROFF_IGN):
case ROFF_IGN:
pos = 0;
continue;
case (ROFF_ERR):
case ROFF_ERR:
assert(MANDOCLEVEL_FATAL <= curp->file_status);
break;
case (ROFF_SO):
case ROFF_SO:
if (0 == (MPARSE_SO & curp->options) &&
(i >= (int)blk.sz || '\0' == blk.buf[i])) {
curp->sodest = mandoc_strdup(ln.buf + of);
free(ln.buf);
return;
}
/*
* We remove `so' clauses from our lookaside
* buffer because we're going to descend into
* the file recursively.
*/
if (curp->secondary)
if (curp->secondary)
curp->secondary->sz -= pos + 1;
mparse_readfd(curp, -1, ln.buf + of);
if (MANDOCLEVEL_FATAL <= curp->file_status)
if (MANDOCLEVEL_FATAL <= curp->file_status) {
mandoc_vmsg(MANDOCERR_SO_FAIL,
curp, curp->line, pos,
".so %s", ln.buf + of);
break;
}
pos = 0;
continue;
default:
@ -515,7 +531,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
if ( ! (curp->man || curp->mdoc))
pset(ln.buf + of, pos - of, curp);
/*
/*
* Lastly, push down into the parsers themselves. One
* of these will have already been set in the pset()
* routine.
@ -531,28 +547,29 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
if (ROFF_TBL == rr)
while (NULL != (span = roff_span(curp->roff))) {
rc = curp->man ?
man_addspan(curp->man, span) :
mdoc_addspan(curp->mdoc, span);
man_addspan(curp->man, span) :
mdoc_addspan(curp->mdoc, span);
if (0 == rc)
break;
}
else if (ROFF_EQN == rr)
rc = curp->mdoc ?
mdoc_addeqn(curp->mdoc,
roff_eqn(curp->roff)) :
man_addeqn(curp->man,
roff_eqn(curp->roff));
rc = curp->mdoc ?
mdoc_addeqn(curp->mdoc,
roff_eqn(curp->roff)) :
man_addeqn(curp->man,
roff_eqn(curp->roff));
else if (curp->man || curp->mdoc)
rc = curp->man ?
man_parseln(curp->man,
curp->line, ln.buf, of) :
mdoc_parseln(curp->mdoc,
curp->line, ln.buf, of);
man_parseln(curp->man,
curp->line, ln.buf, of) :
mdoc_parseln(curp->mdoc,
curp->line, ln.buf, of);
if (0 == rc) {
assert(MANDOCLEVEL_FATAL <= curp->file_status);
break;
}
} else if (2 == rc)
break;
/* Temporary buffers typically are not full. */
@ -568,7 +585,8 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
}
static int
read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
read_whole_file(struct mparse *curp, const char *file, int fd,
struct buf *fb, int *with_mmap)
{
size_t off;
ssize_t ssz;
@ -576,7 +594,10 @@ read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
#ifdef HAVE_MMAP
struct stat st;
if (-1 == fstat(fd, &st)) {
perror(file);
curp->file_status = MANDOCLEVEL_SYSERR;
if (curp->mmsg)
(*curp->mmsg)(MANDOCERR_SYSSTAT, curp->file_status,
file, 0, 0, strerror(errno));
return(0);
}
@ -589,7 +610,10 @@ read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
if (S_ISREG(st.st_mode)) {
if (st.st_size >= (1U << 31)) {
fprintf(stderr, "%s: input too large\n", file);
curp->file_status = MANDOCLEVEL_FATAL;
if (curp->mmsg)
(*curp->mmsg)(MANDOCERR_TOOLARGE,
curp->file_status, file, 0, 0, NULL);
return(0);
}
*with_mmap = 1;
@ -612,7 +636,11 @@ read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
for (;;) {
if (off == fb->sz) {
if (fb->sz == (1U << 31)) {
fprintf(stderr, "%s: input too large\n", file);
curp->file_status = MANDOCLEVEL_FATAL;
if (curp->mmsg)
(*curp->mmsg)(MANDOCERR_TOOLARGE,
curp->file_status,
file, 0, 0, NULL);
break;
}
resize_buf(fb, 65536);
@ -623,7 +651,11 @@ read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
return(1);
}
if (ssz == -1) {
perror(file);
curp->file_status = MANDOCLEVEL_SYSERR;
if (curp->mmsg)
(*curp->mmsg)(MANDOCERR_SYSREAD,
curp->file_status, file, 0, 0,
strerror(errno));
break;
}
off += (size_t)ssz;
@ -641,6 +673,19 @@ mparse_end(struct mparse *curp)
if (MANDOCLEVEL_FATAL <= curp->file_status)
return;
if (curp->mdoc == NULL &&
curp->man == NULL &&
curp->sodest == NULL) {
if (curp->options & MPARSE_MDOC)
curp->mdoc = curp->pmdoc;
else {
if (curp->pman == NULL)
curp->pman = man_alloc(curp->roff, curp,
curp->options & MPARSE_QUICK ? 1 : 0);
curp->man = curp->pman;
}
}
if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
assert(MANDOCLEVEL_FATAL <= curp->file_status);
return;
@ -651,12 +696,6 @@ mparse_end(struct mparse *curp)
return;
}
if ( ! (curp->man || curp->mdoc)) {
mandoc_msg(MANDOCERR_NOTMANUAL, curp, 1, 0, NULL);
curp->file_status = MANDOCLEVEL_FATAL;
return;
}
roff_endparse(curp->roff);
}
@ -704,12 +743,15 @@ mparse_readfd(struct mparse *curp, int fd, const char *file)
struct buf blk;
int with_mmap;
if (-1 == fd)
if (-1 == (fd = open(file, O_RDONLY, 0))) {
perror(file);
curp->file_status = MANDOCLEVEL_SYSERR;
goto out;
}
if (-1 == fd && -1 == (fd = open(file, O_RDONLY, 0))) {
curp->file_status = MANDOCLEVEL_SYSERR;
if (curp->mmsg)
(*curp->mmsg)(MANDOCERR_SYSOPEN,
curp->file_status,
file, 0, 0, strerror(errno));
goto out;
}
/*
* Run for each opened file; may be called more than once for
* each full parse sequence if the opened file is nested (i.e.,
@ -717,10 +759,8 @@ mparse_readfd(struct mparse *curp, int fd, const char *file)
* the parse phase for the file.
*/
if ( ! read_whole_file(file, fd, &blk, &with_mmap)) {
curp->file_status = MANDOCLEVEL_SYSERR;
if ( ! read_whole_file(curp, file, fd, &blk, &with_mmap))
goto out;
}
mparse_parse_buffer(curp, blk, file);
@ -738,8 +778,8 @@ mparse_readfd(struct mparse *curp, int fd, const char *file)
}
struct mparse *
mparse_alloc(enum mparset inttype, enum mandoclevel wlevel,
mandocmsg mmsg, void *arg, char *defos)
mparse_alloc(int options, enum mandoclevel wlevel,
mandocmsg mmsg, const char *defos)
{
struct mparse *curp;
@ -747,13 +787,20 @@ mparse_alloc(enum mparset inttype, enum mandoclevel wlevel,
curp = mandoc_calloc(1, sizeof(struct mparse));
curp->options = options;
curp->wlevel = wlevel;
curp->mmsg = mmsg;
curp->arg = arg;
curp->inttype = inttype;
curp->defos = defos;
curp->roff = roff_alloc(inttype, curp);
curp->roff = roff_alloc(curp, options);
if (curp->options & MPARSE_MDOC)
curp->pmdoc = mdoc_alloc(
curp->roff, curp, curp->defos,
curp->options & MPARSE_QUICK ? 1 : 0);
if (curp->options & MPARSE_MAN)
curp->pman = man_alloc(curp->roff, curp,
curp->options & MPARSE_QUICK ? 1 : 0);
return(curp);
}
@ -773,6 +820,9 @@ mparse_reset(struct mparse *curp)
curp->file_status = MANDOCLEVEL_OK;
curp->mdoc = NULL;
curp->man = NULL;
free(curp->sodest);
curp->sodest = NULL;
}
void
@ -789,13 +839,20 @@ mparse_free(struct mparse *curp)
free(curp->secondary->buf);
free(curp->secondary);
free(curp->sodest);
free(curp);
}
void
mparse_result(struct mparse *curp, struct mdoc **mdoc, struct man **man)
mparse_result(struct mparse *curp,
struct mdoc **mdoc, struct man **man, char **sodest)
{
if (sodest && NULL != (*sodest = curp->sodest)) {
*mdoc = NULL;
*man = NULL;
return;
}
if (mdoc)
*mdoc = curp->mdoc;
if (man)
@ -810,14 +867,14 @@ mandoc_vmsg(enum mandocerr t, struct mparse *m,
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
(void)vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
mandoc_msg(t, m, ln, pos, buf);
}
void
mandoc_msg(enum mandocerr er, struct mparse *m,
mandoc_msg(enum mandocerr er, struct mparse *m,
int ln, int col, const char *msg)
{
enum mandoclevel level;

305
roff.7
View File

@ -1,7 +1,7 @@
.\" $Id: roff.7,v 1.46 2013/12/26 02:43:18 schwarze Exp $
.\" $Id: roff.7,v 1.55 2014/07/07 11:35:06 schwarze Exp $
.\"
.\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010, 2011, 2013 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2010, 2011, 2013, 2014 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: December 26 2013 $
.Dd $Mdocdate: July 7 2014 $
.Dt ROFF 7
.Os
.Sh NAME
@ -409,24 +409,27 @@ and the number of arguments is not checked.
Append to a macro definition.
The syntax of this request is the same as that of
.Sx \&de .
It is currently ignored by
.Xr mandoc 1 ,
as are its children.
.Ss \&ami
Append to a macro definition, specifying the macro name indirectly.
The syntax of this request is the same as that of
.Sx \&dei .
It is currently ignored by
.Xr mandoc 1 ,
as are its children.
.Ss \&am1
Append to a macro definition, switching roff compatibility mode off
during macro execution.
The syntax of this request is the same as that of
.Sx \&de1 .
It is currently ignored by
.Xr mandoc 1 ,
as are its children.
Since
.Xr mandoc 1
does not implement
.Nm
compatibility mode at all, it handles this request as an alias for
.Sx \&am .
.Ss \&as
Append to a user-defined string.
The syntax of this request is the same as that of
.Sx \&ds .
If a user-defined string with the specified name does not yet exist,
it is set to the empty string before appending.
.Ss \&cc
Changes the control character.
Its syntax is as follows:
@ -439,6 +442,12 @@ If
is not specified, the control character is reset to
.Sq \&. .
Trailing characters are ignored.
.Ss \&ce
Center some lines.
This line-scoped request is intended to take one integer argument,
specifying how many lines to center.
Currently, it is ignored including its arguments, and the number
of arguments is not checked.
.Ss \&de
Define a
.Nm
@ -542,9 +551,13 @@ Define a
macro, specifying the macro name indirectly.
The syntax of this request is the same as that of
.Sx \&de .
It is currently ignored by
.Xr mandoc 1 ,
as are its children.
The request
.Pp
.D1 Pf . Cm \&dei Ar name Op Ar end
.Pp
has the same effect as:
.Pp
.D1 Pf . Cm \&de No \e* Ns Bo Ar name Bc Op \e* Ns Bq Ar end
.Ss \&de1
Define a
.Nm
@ -647,6 +660,34 @@ This line-scoped request is intended to have one argument specifying
the font family to be selected.
It is a groff extension, and currently, it is ignored including its
arguments, and the number of arguments is not checked.
.Ss \&ft
Change the font.
Its syntax is as follows:
.Pp
.D1 Pf . Cm \&ft Op Ar font
.Pp
The following
.Ar font
arguments are supported:
.Bl -tag -width 4n -offset indent
.It Cm B , BI , 3 , 4
switches to
.Sy bold
font
.It Cm I , 2
switches to
.Em underlined
font
.It Cm R , CW , 1
switches to normal font
.It Cm P No "or no argument"
switches back to the previous font
.El
.Pp
This request takes effect only locally, may be overridden by macros
and escape sequences, and is only supported in
.Xr man 7
for now.
.Ss \&hw
Specify hyphenation points in words.
This line-scoped request is currently ignored.
@ -665,10 +706,67 @@ Its syntax is equivalent to
.Sx \&if .
.Ss \&if
Begins a conditional.
Right now, the conditional evaluates to true
if and only if it starts with the letter
.Sy n ,
indicating processing in nroff style as opposed to troff style.
This request has the following syntax:
.Bd -literal -offset indent
\&.if COND BODY
.Ed
.Bd -literal -offset indent
\&.if COND \e{BODY
BODY...\e}
.Ed
.Bd -literal -offset indent
\&.if COND \e{\e
BODY...
\&.\e}
.Ed
.Pp
COND is a conditional statement.
Currently,
.Xr mandoc 1
supports the following subset of roff conditionals:
.Bl -bullet
.It
If
.Sq \&!
is prefixed to COND, the condition is logically inverted.
.It
If the first character of COND is
.Sq n
.Pq nroff mode
or
.Sq o
.Pq odd page ,
COND evaluates to true.
.It
If the first character of COND is
.Sq c
.Pq character available ,
.Sq d
.Pq string defined ,
.Sq e
.Pq even page ,
.Sq r
.Pq register accessed ,
or
.Sq t
.Pq troff mode ,
COND evaluates to false.
.It
If COND starts with a parenthesis or with an optionally signed
integer number, it is evaluated according to the rules of
.Sx Numerical expressions
explained below.
It evaluates to true if the the result is positive,
or to false if the result is zero or negative.
.It
Otherwise, the first character of COND is regarded as a delimiter
and COND evaluates to true if the string extending from its first
to its second occurrence is equal to the string extending from its
second to its third occurrence.
.It
If COND cannot be parsed, it evaluates to false.
.El
.Pp
If a conditional is false, its children are not processed, but are
syntactically interpreted to preserve the integrity of the input
document.
@ -686,44 +784,12 @@ will continue to syntactically interpret to the block close of the final
conditional.
Sub-conditionals, in this case, obviously inherit the truth value of
the parent.
This request has the following syntax:
.Bd -literal -offset indent
\&.if COND \e{\e
BODY...
\&.\e}
.Ed
.Bd -literal -offset indent
\&.if COND \e{ BODY
BODY... \e}
.Ed
.Bd -literal -offset indent
\&.if COND \e{ BODY
BODY...
\&.\e}
.Ed
.Bd -literal -offset indent
\&.if COND \e
BODY
.Ed
.Pp
COND is a conditional statement.
roff allows for complicated conditionals; mandoc is much simpler.
At this time, mandoc supports only
.Sq n ,
evaluating to true;
and
.Sq t ,
.Sq e ,
and
.Sq o ,
evaluating to false.
All other invocations are read up to the next end of line or space and
evaluate as false.
.Pp
If the BODY section is begun by an escaped brace
.Sq \e{ ,
scope continues until a closing-brace escape sequence
.Sq \.\e} .
scope continues until the end of the input line containing the
matching closing-brace escape sequence
.Sq \e} .
If the BODY is not enclosed in braces, scope continues until
the end of the line.
If the COND is followed by a BODY on the same line, whether after a
@ -803,6 +869,23 @@ Otherwise, it only terminates the
and arguments following it or the
.Sq \&..
request are discarded.
.Ss \&ll
Change the output line length.
Its syntax is as follows:
.Pp
.D1 Pf . Cm \&ll Op Oo +|- Oc Ns Ar width
.Pp
If the
.Ar width
argument is omitted, the line length is reset to its previous value.
The default setting for terminal output is 78n.
If a sign is given, the line length is added to or subtracted from;
otherwise, it is set to the provided value.
Using this request in new manuals is discouraged for several reasons,
among others because it overrides the
.Xr mandoc 1
.Fl O Cm width
command line option.
.Ss \&ne
Declare the need for the specified minimum vertical space
before the next trap or the bottom of the page.
@ -810,23 +893,19 @@ This line-scoped request is currently ignored.
.Ss \&nh
Turn off automatic hyphenation mode.
This line-scoped request is currently ignored.
.Ss \&rm
Remove a request, macro or string.
This request is intended to have one argument,
the name of the request, macro or string to be undefined.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
.Ss \&nr
Define or change a register.
A register is an arbitrary string value that defines some sort of state,
which influences parsing and/or formatting.
Its syntax is as follows:
.Pp
.D1 Pf \. Cm \&nr Ar name Oo +|- Oc Ns Ar value
.D1 Pf \. Cm \&nr Ar name Oo +|- Oc Ns Ar expression
.Pp
The
.Ar value
may, at the moment, only be an integer.
For the syntax of
.Ar expression ,
see
.Sx Numerical expressions
below.
If it is prefixed by a sign, the register will be
incremented or decremented instead of assigned to.
.Pp
@ -861,6 +940,16 @@ Change point size.
This line-scoped request is intended to take one numerical argument.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
.Ss \&rm
Remove a request, macro or string.
Its syntax is as follows:
.Pp
.D1 Pf \. Cm \&rm Ar name
.Ss \&rr
Remove a register.
Its syntax is as follows:
.Pp
.D1 Pf \. Cm \&rr Ar name
.Ss \&so
Include a source file.
Its syntax is as follows:
@ -929,6 +1018,73 @@ Begin a table, which formats input in aligned rows and columns.
See
.Xr tbl 7
for a description of the tbl language.
.Ss Numerical expressions
The
.Sx \&nr ,
.Sx \&if ,
and
.Sx \&ie
requests accept integer numerical expressions as arguments.
These are always evaluated using the C
.Vt int
type; integer overflow works the same way as in the C language.
Numbers consist of an arbitrary number of digits
.Sq 0
to
.Sq 9
prefixed by an optional sign
.Sq +
or
.Sq - .
.Pp
The following binary operators are implemented.
Unless otherwise stated, they behave as in the C language:
.Pp
.Bl -tag -width 2n -compact
.It Ic +
addition
.It Ic -
subtraction
.It Ic *
multiplication
.It Ic /
division
.It Ic %
remainder of division
.It Ic <
less than
.It Ic >
greater than
.It Ic ==
equal to
.It Ic =
equal to, same effect as
.Ic ==
(this differs from C)
.It Ic <=
less than or equal to
.It Ic >=
greater than or equal to
.It Ic <>
not equal to (corresponds to C
.Ic != ;
this one is of limited portability, it is supported by Heirloom roff,
but not by groff)
.It Ic &
logical and (corresponds to C
.Ic && )
.It Ic \&:
logical or (corresponds to C
.Ic \&|| )
.It Ic <?
minimum (not available in C)
.It Ic >?
maximum (not available in C)
.El
.Pp
There is no concept of precendence; evaluation proceeds from left to right,
except when subexpressions are enclosed in parantheses.
Inside parentheses, whitespace is ignored.
.Sh ESCAPE SEQUENCE REFERENCE
The
.Xr mandoc 1
@ -1017,10 +1173,15 @@ Digit width space character.
Anchor definition; ignored by
.Xr mandoc 1 .
.Ss \eB\(aq Ns Ar string Ns \(aq
Test whether
Interpolate
.Sq 1
if
.Ar string
is a numerical expession; ignored by
.Xr mandoc 1 .
conforms to the syntax of
.Sx Numerical expressions
explained above and
.Sq 0
otherwise.
.Ss \eb\(aq Ns Ar string Ns \(aq
Bracket building function; ignored by
.Xr mandoc 1 .
@ -1144,9 +1305,13 @@ Vertical motion; ignored by
.Xr mandoc 1 .
.Ss \ew\(aq Ns Ar string Ns \(aq
Interpolate the width of the
.Ar string ;
ignored by
.Xr mandoc 1 .
.Ar string .
The
.Xr mandoc 1
implementation assumes that after expansion of user-defined strings, the
.Ar string
only contains normal characters, no escape sequences, and that each
character has a width of 24 basic units.
.Ss \eX\(aq Ns Ar string Ns \(aq
Output
.Ar string

1272
roff.c

File diff suppressed because it is too large Load Diff

5
st.c
View File

@ -1,4 +1,4 @@
/* $Id: st.c,v 1.9 2011/03/22 14:33:05 kristaps Exp $ */
/* $Id: st.c,v 1.10 2014/03/23 11:25:26 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -18,12 +18,9 @@
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mdoc.h"
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \

5
st.in
View File

@ -1,4 +1,4 @@
/* $Id: st.in,v 1.22 2013/12/25 14:09:32 schwarze Exp $ */
/* $Id: st.in,v 1.24 2014/04/20 16:46:05 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -22,7 +22,7 @@
* the formatted output string.
*
* Be sure to escape strings.
* The non-breaking blanks prevent ending an output line right before
* The non-breaking blanks prevent ending an output line right before
* a number. Groff prevent line breaks at the same places.
*
* REMEMBER TO ADD NEW STANDARDS TO MDOC.7!
@ -34,6 +34,7 @@ LINE("-p1003.1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2001", "IEEE Std 1003.1-2001 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2004", "IEEE Std 1003.1-2004 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2008", "IEEE Std 1003.1-2008 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2013", "IEEE Std 1003.1-2008/Cor 1-2013 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1", "IEEE Std 1003.1 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1b", "IEEE Std 1003.1b (\\(lqPOSIX.1b\\(rq)")
LINE("-p1003.1b-93", "IEEE Std 1003.1b-1993 (\\(lqPOSIX.1b\\(rq)")

25
tbl.c
View File

@ -1,4 +1,4 @@
/* $Id: tbl.c,v 1.27 2013/05/31 22:08:09 schwarze Exp $ */
/* $Id: tbl.c,v 1.29 2014/04/20 16:46:05 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@ -26,9 +26,11 @@
#include <time.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmandoc.h"
#include "libroff.h"
enum rofferr
tbl_read(struct tbl_node *tbl, int ln, const char *p, int offs)
{
@ -52,11 +54,11 @@ tbl_read(struct tbl_node *tbl, int ln, const char *p, int offs)
/* Now process each logical section of the table. */
switch (tbl->part) {
case (TBL_PART_OPTS):
case TBL_PART_OPTS:
return(tbl_option(tbl, ln, p) ? ROFF_IGN : ROFF_ERR);
case (TBL_PART_LAYOUT):
case TBL_PART_LAYOUT:
return(tbl_layout(tbl, ln, p) ? ROFF_IGN : ROFF_ERR);
case (TBL_PART_CDATA):
case TBL_PART_CDATA:
return(tbl_cdata(tbl, ln, p) ? ROFF_TBL : ROFF_IGN);
default:
break;
@ -128,8 +130,8 @@ void
tbl_restart(int line, int pos, struct tbl_node *tbl)
{
if (TBL_PART_CDATA == tbl->part)
mandoc_msg(MANDOCERR_TBLBLOCK, tbl->parse,
tbl->line, tbl->pos, NULL);
mandoc_msg(MANDOCERR_TBLBLOCK, tbl->parse,
tbl->line, tbl->pos, NULL);
tbl->part = TBL_PART_LAYOUT;
tbl->line = line;
@ -137,7 +139,7 @@ tbl_restart(int line, int pos, struct tbl_node *tbl)
if (NULL == tbl->first_span || NULL == tbl->first_span->first)
mandoc_msg(MANDOCERR_TBLNODATA, tbl->parse,
tbl->line, tbl->pos, NULL);
tbl->line, tbl->pos, NULL);
}
const struct tbl_span *
@ -162,14 +164,13 @@ tbl_end(struct tbl_node **tblp)
*tblp = NULL;
if (NULL == tbl->first_span || NULL == tbl->first_span->first)
mandoc_msg(MANDOCERR_TBLNODATA, tbl->parse,
tbl->line, tbl->pos, NULL);
mandoc_msg(MANDOCERR_TBLNODATA, tbl->parse,
tbl->line, tbl->pos, NULL);
if (tbl->last_span)
tbl->last_span->flags |= TBL_SPAN_LAST;
if (TBL_PART_CDATA == tbl->part)
mandoc_msg(MANDOCERR_TBLBLOCK, tbl->parse,
tbl->line, tbl->pos, NULL);
mandoc_msg(MANDOCERR_TBLBLOCK, tbl->parse,
tbl->line, tbl->pos, NULL);
}

View File

@ -1,4 +1,4 @@
/* $Id: tbl_data.c,v 1.27 2013/06/01 04:56:50 schwarze Exp $ */
/* $Id: tbl_data.c,v 1.31 2014/04/23 16:08:33 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@ -26,16 +26,18 @@
#include <time.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmandoc.h"
#include "libroff.h"
static int data(struct tbl_node *, struct tbl_span *,
static int getdata(struct tbl_node *, struct tbl_span *,
int, const char *, int *);
static struct tbl_span *newspan(struct tbl_node *, int,
static struct tbl_span *newspan(struct tbl_node *, int,
struct tbl_row *);
static int
data(struct tbl_node *tbl, struct tbl_span *dp,
getdata(struct tbl_node *tbl, struct tbl_span *dp,
int ln, const char *p, int *pos)
{
struct tbl_dat *dat;
@ -48,7 +50,7 @@ data(struct tbl_node *tbl, struct tbl_span *dp,
else if (NULL == dp->last)
cp = dp->layout->first;
/*
/*
* Skip over spanners, since
* we want to match data with data layout cells in the header.
*/
@ -62,8 +64,8 @@ data(struct tbl_node *tbl, struct tbl_span *dp,
*/
if (NULL == cp) {
mandoc_msg(MANDOCERR_TBLEXTRADAT,
tbl->parse, ln, *pos, NULL);
mandoc_msg(MANDOCERR_TBLEXTRADAT, tbl->parse,
ln, *pos, NULL);
/* Skip to the end... */
while (p[*pos])
(*pos)++;
@ -81,7 +83,7 @@ data(struct tbl_node *tbl, struct tbl_span *dp,
spans++;
else
break;
dat->spans = spans;
if (dp->last) {
@ -126,21 +128,20 @@ data(struct tbl_node *tbl, struct tbl_span *dp,
dat->pos = TBL_DATA_DATA;
if (TBL_CELL_HORIZ == dat->layout->pos ||
TBL_CELL_DHORIZ == dat->layout->pos ||
TBL_CELL_DOWN == dat->layout->pos)
TBL_CELL_DHORIZ == dat->layout->pos ||
TBL_CELL_DOWN == dat->layout->pos)
if (TBL_DATA_DATA == dat->pos && '\0' != *dat->string)
mandoc_msg(MANDOCERR_TBLIGNDATA,
tbl->parse, ln, sv, NULL);
mandoc_msg(MANDOCERR_TBLIGNDATA,
tbl->parse, ln, sv, NULL);
return(1);
}
/* ARGSUSED */
int
tbl_cdata(struct tbl_node *tbl, int ln, const char *p)
{
struct tbl_dat *dat;
size_t sz;
size_t sz;
int pos;
pos = 0;
@ -152,7 +153,7 @@ tbl_cdata(struct tbl_node *tbl, int ln, const char *p)
if (p[pos] == tbl->opts.tab) {
tbl->part = TBL_PART_DATA;
pos++;
return(data(tbl, tbl->last_span, ln, p, &pos));
return(getdata(tbl, tbl->last_span, ln, p, &pos));
} else if ('\0' == p[pos]) {
tbl->part = TBL_PART_DATA;
return(1);
@ -166,14 +167,14 @@ tbl_cdata(struct tbl_node *tbl, int ln, const char *p)
if (dat->string) {
sz = strlen(p) + strlen(dat->string) + 2;
dat->string = mandoc_realloc(dat->string, sz);
strlcat(dat->string, " ", sz);
strlcat(dat->string, p, sz);
(void)strlcat(dat->string, " ", sz);
(void)strlcat(dat->string, p, sz);
} else
dat->string = mandoc_strdup(p);
if (TBL_CELL_DOWN == dat->layout->pos)
mandoc_msg(MANDOCERR_TBLIGNDATA,
tbl->parse, ln, pos, NULL);
if (TBL_CELL_DOWN == dat->layout->pos)
mandoc_msg(MANDOCERR_TBLIGNDATA, tbl->parse,
ln, pos, NULL);
return(0);
}
@ -215,7 +216,7 @@ tbl_data(struct tbl_node *tbl, int ln, const char *p)
return(0);
}
/*
/*
* Choose a layout row: take the one following the last parsed
* span's. If that doesn't exist, use the last parsed span's.
* If there's no last parsed span, use the first row. Lastly,
@ -229,11 +230,11 @@ tbl_data(struct tbl_node *tbl, int ln, const char *p)
for (rp = tbl->last_span->layout->next;
rp && rp->first; rp = rp->next) {
switch (rp->first->pos) {
case (TBL_CELL_HORIZ):
case TBL_CELL_HORIZ:
dp = newspan(tbl, ln, rp);
dp->pos = TBL_SPAN_HORIZ;
continue;
case (TBL_CELL_DHORIZ):
case TBL_CELL_DHORIZ:
dp = newspan(tbl, ln, rp);
dp->pos = TBL_SPAN_DHORIZ;
continue;
@ -267,7 +268,7 @@ tbl_data(struct tbl_node *tbl, int ln, const char *p)
/* This returns 0 when TBL_PART_CDATA is entered. */
while ('\0' != p[pos])
if ( ! data(tbl, dp, ln, p, &pos))
if ( ! getdata(tbl, dp, ln, p, &pos))
return(0);
return(1);

View File

@ -1,4 +1,4 @@
/* $Id: tbl_html.c,v 1.10 2012/05/27 17:54:54 schwarze Exp $ */
/* $Id: tbl_html.c,v 1.11 2014/04/20 16:46:05 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -31,15 +31,14 @@ static void html_tblopen(struct html *, const struct tbl_span *);
static size_t html_tbl_len(size_t, void *);
static size_t html_tbl_strlen(const char *, void *);
/* ARGSUSED */
static size_t
html_tbl_len(size_t sz, void *arg)
{
return(sz);
}
/* ARGSUSED */
static size_t
html_tbl_strlen(const char *p, void *arg)
{
@ -107,9 +106,9 @@ print_tbl(struct html *h, const struct tbl_span *sp)
tt = print_otag(h, TAG_TR, 0, NULL);
switch (sp->pos) {
case (TBL_SPAN_HORIZ):
case TBL_SPAN_HORIZ:
/* FALLTHROUGH */
case (TBL_SPAN_DHORIZ):
case TBL_SPAN_DHORIZ:
PAIR_INIT(&tag, ATTR_COLSPAN, "0");
print_otag(h, TAG_TD, 1, &tag);
break;

View File

@ -1,7 +1,7 @@
/* $Id: tbl_layout.c,v 1.23 2012/05/27 17:54:54 schwarze Exp $ */
/* $Id: tbl_layout.c,v 1.26 2014/04/20 16:46:05 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2012 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2012, 2014 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,13 +19,13 @@
#include "config.h"
#endif
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmandoc.h"
#include "libroff.h"
@ -55,16 +55,17 @@ static const struct tbl_phrase keys[KEYS_MAX] = {
{ '=', TBL_CELL_DHORIZ }
};
static int mods(struct tbl_node *, struct tbl_cell *,
static int mods(struct tbl_node *, struct tbl_cell *,
int, const char *, int *);
static int cell(struct tbl_node *, struct tbl_row *,
static int cell(struct tbl_node *, struct tbl_row *,
int, const char *, int *);
static void row(struct tbl_node *, int, const char *, int *);
static struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
enum tbl_cellt, int vert);
static int
mods(struct tbl_node *tbl, struct tbl_cell *cp,
mods(struct tbl_node *tbl, struct tbl_cell *cp,
int ln, const char *p, int *pos)
{
char buf[5];
@ -73,33 +74,35 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
/* Not all types accept modifiers. */
switch (cp->pos) {
case (TBL_CELL_DOWN):
case TBL_CELL_DOWN:
/* FALLTHROUGH */
case (TBL_CELL_HORIZ):
case TBL_CELL_HORIZ:
/* FALLTHROUGH */
case (TBL_CELL_DHORIZ):
case TBL_CELL_DHORIZ:
return(1);
default:
break;
}
mod:
/*
/*
* XXX: since, at least for now, modifiers are non-conflicting
* (are separable by value, regardless of position), we let
* modifiers come in any order. The existing tbl doesn't let
* this happen.
*/
switch (p[*pos]) {
case ('\0'):
case '\0':
/* FALLTHROUGH */
case (' '):
case ' ':
/* FALLTHROUGH */
case ('\t'):
case '\t':
/* FALLTHROUGH */
case (','):
case ',':
/* FALLTHROUGH */
case ('.'):
case '.':
/* FALLTHROUGH */
case '|':
return(1);
default:
break;
@ -115,8 +118,8 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
(*pos)++;
goto mod;
}
mandoc_msg(MANDOCERR_TBLLAYOUT,
tbl->parse, ln, *pos, NULL);
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
return(0);
}
@ -133,8 +136,8 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
/* No greater than 4 digits. */
if (4 == i) {
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
mandoc_msg(MANDOCERR_TBLLAYOUT,
tbl->parse, ln, *pos, NULL);
return(0);
}
@ -143,69 +146,69 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
goto mod;
/* NOTREACHED */
}
}
/* TODO: GNU has many more extensions. */
switch (tolower((unsigned char)p[(*pos)++])) {
case ('z'):
case 'z':
cp->flags |= TBL_CELL_WIGN;
goto mod;
case ('u'):
case 'u':
cp->flags |= TBL_CELL_UP;
goto mod;
case ('e'):
case 'e':
cp->flags |= TBL_CELL_EQUAL;
goto mod;
case ('t'):
case 't':
cp->flags |= TBL_CELL_TALIGN;
goto mod;
case ('d'):
case 'd':
cp->flags |= TBL_CELL_BALIGN;
goto mod;
case ('w'): /* XXX for now, ignore minimal column width */
case 'w': /* XXX for now, ignore minimal column width */
goto mod;
case ('f'):
case 'f':
break;
case ('r'):
case 'r':
/* FALLTHROUGH */
case ('b'):
case 'b':
/* FALLTHROUGH */
case ('i'):
case 'i':
(*pos)--;
break;
default:
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos - 1, NULL);
ln, *pos - 1, NULL);
return(0);
}
switch (tolower((unsigned char)p[(*pos)++])) {
case ('3'):
case '3':
/* FALLTHROUGH */
case ('b'):
case 'b':
cp->flags |= TBL_CELL_BOLD;
goto mod;
case ('2'):
case '2':
/* FALLTHROUGH */
case ('i'):
case 'i':
cp->flags |= TBL_CELL_ITALIC;
goto mod;
case ('1'):
case '1':
/* FALLTHROUGH */
case ('r'):
case 'r':
goto mod;
default:
break;
}
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos - 1, NULL);
ln, *pos - 1, NULL);
return(0);
}
static int
cell(struct tbl_node *tbl, struct tbl_row *rp,
cell(struct tbl_node *tbl, struct tbl_row *rp,
int ln, const char *p, int *pos)
{
int vert, i;
@ -218,6 +221,13 @@ cell(struct tbl_node *tbl, struct tbl_row *rp,
while (' ' == p[*pos])
(*pos)++;
/* Handle trailing vertical lines */
if ('.' == p[*pos] || '\0' == p[*pos]) {
rp->vert = vert;
return(1);
}
/* Parse the column position (`c', `l', `r', ...). */
for (i = 0; i < KEYS_MAX; i++)
@ -225,8 +235,8 @@ cell(struct tbl_node *tbl, struct tbl_row *rp,
break;
if (KEYS_MAX == i) {
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
return(0);
}
@ -243,14 +253,15 @@ cell(struct tbl_node *tbl, struct tbl_row *rp,
if (TBL_CELL_SPAN == c) {
if (NULL == rp->first) {
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
ln, *pos, NULL);
return(0);
} else if (rp->last)
switch (rp->last->pos) {
case (TBL_CELL_HORIZ):
case (TBL_CELL_DHORIZ):
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
case TBL_CELL_HORIZ:
/* FALLTHROUGH */
case TBL_CELL_DHORIZ:
mandoc_msg(MANDOCERR_TBLLAYOUT,
tbl->parse, ln, *pos, NULL);
return(0);
default:
break;
@ -281,7 +292,6 @@ cell(struct tbl_node *tbl, struct tbl_row *rp,
return(mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos));
}
static void
row(struct tbl_node *tbl, int ln, const char *p, int *pos)
{
@ -312,9 +322,9 @@ row(struct tbl_node *tbl, int ln, const char *p, int *pos)
if ('.' == p[*pos]) {
tbl->part = TBL_PART_DATA;
if (NULL == tbl->first_row)
mandoc_msg(MANDOCERR_TBLNOLAYOUT, tbl->parse,
ln, *pos, NULL);
if (NULL == tbl->first_row)
mandoc_msg(MANDOCERR_TBLNOLAYOUT,
tbl->parse, ln, *pos, NULL);
(*pos)++;
return;
}

View File

@ -1,4 +1,4 @@
/* $Id: tbl_opts.c,v 1.12 2011/09/18 14:14:15 schwarze Exp $ */
/* $Id: tbl_opts.c,v 1.13 2014/04/20 16:46:05 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -62,10 +62,10 @@ struct tbl_phrase {
static const struct tbl_phrase keys[KEY_MAXKEYS] = {
{ "center", TBL_OPT_CENTRE, KEY_CENTRE},
{ "centre", TBL_OPT_CENTRE, KEY_CENTRE},
{ "delim", 0, KEY_DELIM},
{ "delim", 0, KEY_DELIM},
{ "expand", TBL_OPT_EXPAND, KEY_EXPAND},
{ "box", TBL_OPT_BOX, KEY_BOX},
{ "doublebox", TBL_OPT_DBOX, KEY_DBOX},
{ "box", TBL_OPT_BOX, KEY_BOX},
{ "doublebox", TBL_OPT_DBOX, KEY_DBOX},
{ "allbox", TBL_OPT_ALLBOX, KEY_ALLBOX},
{ "frame", TBL_OPT_BOX, KEY_FRAME},
{ "doubleframe", TBL_OPT_DBOX, KEY_DFRAME},
@ -76,11 +76,12 @@ static const struct tbl_phrase keys[KEY_MAXKEYS] = {
{ "nospaces", TBL_OPT_NOSPACE, KEY_NOSPACE},
};
static int arg(struct tbl_node *, int,
static int arg(struct tbl_node *, int,
const char *, int *, enum tbl_ident);
static void opt(struct tbl_node *, int,
static void opt(struct tbl_node *, int,
const char *, int *);
static int
arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key)
{
@ -93,8 +94,8 @@ arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key)
/* Arguments always begin with a parenthesis. */
if ('(' != p[*pos]) {
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos, NULL);
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos, NULL);
return(0);
}
@ -107,27 +108,27 @@ arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key)
*/
switch (key) {
case (KEY_DELIM):
case KEY_DELIM:
if ('\0' == p[(*pos)++]) {
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos - 1, NULL);
ln, *pos - 1, NULL);
return(0);
}
}
if ('\0' == p[(*pos)++]) {
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos - 1, NULL);
ln, *pos - 1, NULL);
return(0);
}
}
break;
case (KEY_TAB):
case KEY_TAB:
if ('\0' != (tbl->opts.tab = p[(*pos)++]))
break;
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos - 1, NULL);
ln, *pos - 1, NULL);
return(0);
case (KEY_LINESIZE):
case KEY_LINESIZE:
for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) {
buf[i] = p[*pos];
if ( ! isdigit((unsigned char)buf[i]))
@ -142,12 +143,12 @@ arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key)
mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL);
return(0);
case (KEY_DPOINT):
case KEY_DPOINT:
if ('\0' != (tbl->opts.decimal = p[(*pos)++]))
break;
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos - 1, NULL);
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos - 1, NULL);
return(0);
default:
abort();
@ -182,7 +183,7 @@ opt(struct tbl_node *tbl, int ln, const char *p, int *pos)
* options ::= option_list [:space:]* [;][\n]
* option_list ::= option option_tail
* option_tail ::= [:space:]+ option_list |
* ::= epsilon
* ::= epsilon
* option ::= [:alpha:]+ args
* args ::= [:space:]* [(] [:alpha:]+ [)]
*/
@ -215,7 +216,7 @@ opt(struct tbl_node *tbl, int ln, const char *p, int *pos)
while (isspace((unsigned char)p[*pos]))
(*pos)++;
/*
/*
* Look through all of the available keys to find one that
* matches the input. FIXME: hashtable this.
*/
@ -231,7 +232,7 @@ opt(struct tbl_node *tbl, int ln, const char *p, int *pos)
* of the sequence altogether.
*/
if (keys[i].key)
if (keys[i].key)
tbl->opts.opts |= keys[i].key;
else if ( ! arg(tbl, ln, p, pos, keys[i].ident))
return;
@ -239,7 +240,7 @@ opt(struct tbl_node *tbl, int ln, const char *p, int *pos)
break;
}
/*
/*
* Allow us to recover from bad options by continuing to another
* parse sequence.
*/

Some files were not shown because too many files have changed in this diff Show More