Import mandoc 1.4.5

This commit is contained in:
Baptiste Daroussin 2019-03-29 13:13:30 +00:00
parent 1f1d4007ae
commit 8f0c701250
104 changed files with 8291 additions and 6423 deletions

View File

@ -1,4 +1,4 @@
$Id: INSTALL,v 1.22 2018/07/31 15:34:00 schwarze Exp $
$Id: INSTALL,v 1.23 2019/03/06 15:58:10 schwarze Exp $
About the portable mandoc distribution
--------------------------------------
@ -18,7 +18,7 @@ tech@ mailing list, too.
Enjoy using the mandoc toolset!
Ingo Schwarze, Karlsruhe, August 2018
Ingo Schwarze, Karlsruhe, March 2019
Installation
@ -67,7 +67,8 @@ variables into "configure.local" and go back to step 4.
7. Optionally run the regression suite.
Basically, that amounts to "cd regress && ./regress.pl".
But you should probably look at "./mandoc -l regress/regress.pl.1"
first.
first. In particular, regarding Solaris systems, look at the BUGS
section of that manual page.
8. Run "sudo make install". If you intend to build a binary
package using some kind of fake root mechanism, you may need a

11
LICENSE
View File

@ -1,8 +1,8 @@
$Id: LICENSE,v 1.19 2018/07/31 10:18:15 schwarze Exp $
$Id: LICENSE,v 1.21 2018/11/26 17:11:11 schwarze Exp $
With the exceptions noted below, all code and documentation
contained in the mandoc toolkit is protected by the Copyright
of the following developers:
With the exceptions noted below, all non-trivial files contained
in the mandoc toolkit are protected by the Copyright of the following
developers:
Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org>
@ -12,13 +12,14 @@ Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
Copyright (c) 2014 Baptiste Daroussin <bapt@freebsd.org>
Copyright (c) 2016 Ed Maste <emaste@freebsd.org>
Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
Copyright (c) 2017 Anthony Bentley <bentley@openbsd.org>
Copyright (c) 1998, 2004, 2010 Todd C. Miller <Todd.Miller@courtesan.com>
Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
Copyright (c) 2004 Ted Unangst <tedu@openbsd.org>
Copyright (c) 1994 Christos Zoulas <christos@netbsd.org>
Copyright (c) 2003, 2007, 2008, 2014 Jason McIntyre <jmc@openbsd.org>
See the individual source files for information about who contributed
See the individual files for information about who contributed
to which file during which years.

View File

@ -1,7 +1,7 @@
# $Id: Makefile,v 1.519 2018/07/31 15:34:00 schwarze Exp $
# $Id: Makefile,v 1.530 2019/03/06 16:08:41 schwarze Exp $
#
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
# Copyright (c) 2011, 2013-2018 Ingo Schwarze <schwarze@openbsd.org>
# Copyright (c) 2011, 2013-2019 Ingo Schwarze <schwarze@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@ -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.
VERSION = 1.14.4
VERSION = 1.14.5
# === LIST OF FILES ====================================================
@ -37,9 +37,9 @@ TESTSRCS = test-be32toh.c \
test-PATH_MAX.c \
test-pledge.c \
test-progname.c \
test-recvmsg.c \
test-reallocarray.c \
test-recallocarray.c \
test-recvmsg.c \
test-rewb-bsd.c \
test-rewb-sysv.c \
test-sandbox_init.c \
@ -54,7 +54,8 @@ TESTSRCS = test-be32toh.c \
test-vasprintf.c \
test-wchar.c
SRCS = att.c \
SRCS = arch.c \
att.c \
catman.c \
cgi.c \
chars.c \
@ -96,6 +97,7 @@ SRCS = att.c \
man_validate.c \
mandoc.c \
mandoc_aux.c \
mandoc_msg.c \
mandoc_ohash.c \
mandoc_xr.c \
mandocd.c \
@ -155,13 +157,14 @@ DISTFILES = INSTALL \
dbm_map.h \
demandoc.1 \
eqn.7 \
eqn.h \
eqn_parse.h \
gmdiff \
html.h \
lib.in \
libman.h \
libmandoc.h \
libmdoc.h \
libroff.h \
main.h \
makewhatis.8 \
man.1 \
@ -184,6 +187,7 @@ DISTFILES = INSTALL \
mandoc_html.3 \
mandoc_malloc.3 \
mandoc_ohash.h \
mandoc_parse.h \
mandoc_xr.h \
mandocd.8 \
mansearch.3 \
@ -198,10 +202,12 @@ DISTFILES = INSTALL \
roff.h \
roff_int.h \
soelim.1 \
st.in \
tag.h \
tbl.3 \
tbl.7 \
tbl.h \
tbl_int.h \
tbl_parse.h \
term.h \
$(SRCS) \
$(TESTSRCS)
@ -230,9 +236,11 @@ LIBROFF_OBJS = eqn.o \
LIBMANDOC_OBJS = $(LIBMAN_OBJS) \
$(LIBMDOC_OBJS) \
$(LIBROFF_OBJS) \
arch.o \
chars.o \
mandoc.o \
mandoc_aux.o \
mandoc_msg.o \
mandoc_ohash.o \
mandoc_xr.o \
msec.o \
@ -320,6 +328,7 @@ SOELIM_OBJS = soelim.o \
WWW_MANS = apropos.1.html \
demandoc.1.html \
man.1.html \
man.options.1.html \
mandoc.1.html \
soelim.1.html \
man.cgi.3.html \
@ -336,20 +345,27 @@ WWW_MANS = apropos.1.html \
eqn.7.html \
man.7.html \
mandoc_char.7.html \
mandocd.8.html \
mdoc.7.html \
roff.7.html \
tbl.7.html \
catman.8.html \
makewhatis.8.html \
man.cgi.8.html \
mandocd.8.html
WWW_INCS = eqn.h.html \
html.h.html \
man.h.html \
manconf.h.html \
mandoc.h.html \
mandoc_aux.h.html \
mandoc_parse.h.html \
mansearch.h.html \
mdoc.h.html \
roff.h.html
roff.h.html \
tbl.h.html \
tbl_int.h.html \
tbl_parse.h.html
# === USER CONFIGURATION ===============================================
@ -361,9 +377,9 @@ all: mandoc demandoc soelim $(BUILD_TARGETS) Makefile.local
install: base-install $(INSTALL_TARGETS)
www: $(WWW_MANS)
www: $(WWW_MANS) $(WWW_INCS)
$(WWW_MANS): mandoc
$(WWW_MANS) $(WWW_INCS): mandoc
.PHONY: base-install cgi-install install www-install
.PHONY: clean distclean depend
@ -382,7 +398,7 @@ clean:
rm -f mandocd catman catman.o $(MANDOCD_OBJS)
rm -f demandoc $(DEMANDOC_OBJS)
rm -f soelim $(SOELIM_OBJS)
rm -f $(WWW_MANS) mandoc.tar.gz mandoc.sha256
rm -f $(WWW_MANS) $(WWW_INCS) mandoc*.tar.gz mandoc*.sha256
rm -rf *.dSYM
base-install: mandoc demandoc soelim
@ -420,8 +436,8 @@ lib-install: libmandoc.a
mkdir -p $(DESTDIR)$(INCLUDEDIR)
mkdir -p $(DESTDIR)$(MANDIR)/man3
$(INSTALL_LIB) libmandoc.a $(DESTDIR)$(LIBDIR)
$(INSTALL_LIB) man.h mandoc.h mandoc_aux.h mdoc.h roff.h \
$(DESTDIR)$(INCLUDEDIR)
$(INSTALL_LIB) eqn.h man.h mandoc.h mandoc_aux.h mandoc_parse.h \
mdoc.h roff.h tbl.h $(DESTDIR)$(INCLUDEDIR)
$(INSTALL_MAN) mandoc.3 mandoc_escape.3 mandoc_malloc.3 \
mansearch.3 mchars_alloc.3 tbl.3 $(DESTDIR)$(MANDIR)/man3
@ -475,11 +491,14 @@ uninstall:
rm -f $(DESTDIR)$(MANDIR)/man3/mansearch.3
rm -f $(DESTDIR)$(MANDIR)/man3/mchars_alloc.3
rm -f $(DESTDIR)$(MANDIR)/man3/tbl.3
rm -f $(DESTDIR)$(INCLUDEDIR)/eqn.h
rm -f $(DESTDIR)$(INCLUDEDIR)/man.h
rm -f $(DESTDIR)$(INCLUDEDIR)/mandoc.h
rm -f $(DESTDIR)$(INCLUDEDIR)/mandoc_aux.h
rm -f $(DESTDIR)$(INCLUDEDIR)/mandoc_parse.h
rm -f $(DESTDIR)$(INCLUDEDIR)/mdoc.h
rm -f $(DESTDIR)$(INCLUDEDIR)/roff.h
rm -f $(DESTDIR)$(INCLUDEDIR)/tbl.h
[ ! -e $(DESTDIR)$(INCLUDEDIR) ] || rmdir $(DESTDIR)$(INCLUDEDIR)
regress: all
@ -516,7 +535,9 @@ soelim: $(SOELIM_OBJS)
# --- maintainer targets ---
www-install: www
$(INSTALL_DATA) $(WWW_MANS) mandoc.css $(HTDOCDIR)
$(INSTALL_DATA) mandoc.css $(HTDOCDIR)
$(INSTALL_DATA) $(WWW_MANS) $(HTDOCDIR)/man
$(INSTALL_DATA) $(WWW_INCS) $(HTDOCDIR)/includes
depend: config.h
mkdep -f Makefile.depend $(CFLAGS) $(SRCS)
@ -564,6 +585,10 @@ mandoc-$(VERSION).tar.gz: $(DISTFILES)
( cd .dist/ && tar zcf ../$@ mandoc-$(VERSION) )
rm -rf .dist/
dist-install: dist
$(INSTALL_DATA) mandoc-$(VERSION).tar.gz mandoc-$(VERSION).sha256 \
$(HTDOCDIR)/snapshots
# === SUFFIX RULES =====================================================
.SUFFIXES: .1 .3 .5 .7 .8 .h
@ -573,5 +598,6 @@ mandoc-$(VERSION).tar.gz: $(DISTFILES)
highlight -I $< > $@
.1.1.html .3.3.html .5.5.html .7.7.html .8.8.html: mandoc
./mandoc -Thtml -Wall,stop \
-Ostyle=mandoc.css,man=%N.%S.html,includes=%I.html $< > $@
mandoc -Thtml -Wwarning,stop \
-O 'style=/mandoc.css,man=/man/%N.%S.html;https://man.openbsd.org/%N.%S,includes=/includes/%I.html' \
$< > $@

View File

@ -1,6 +1,7 @@
att.o: att.c config.h mandoc.h roff.h mdoc.h libmdoc.h
arch.o: arch.c config.h roff.h
att.o: att.c config.h roff.h libmdoc.h
catman.o: catman.c config.h compat_fts.h
cgi.o: cgi.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h main.h manconf.h mansearch.h cgi.h
cgi.o: cgi.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h main.h manconf.h mansearch.h cgi.h
chars.o: chars.c config.h mandoc.h mandoc_aux.h mandoc_ohash.h compat_ohash.h libmandoc.h
compat_err.o: compat_err.c config.h
compat_fts.o: compat_fts.c config.h compat_fts.h
@ -26,54 +27,55 @@ dba_read.o: dba_read.c mandoc_aux.h mansearch.h dba_array.h dba.h dbm.h
dba_write.o: dba_write.c config.h dba_write.h
dbm.o: dbm.c config.h mansearch.h dbm_map.h dbm.h
dbm_map.o: dbm_map.c config.h mansearch.h dbm_map.h dbm.h
demandoc.o: demandoc.c config.h mandoc.h roff.h man.h mdoc.h
eqn.o: eqn.c config.h mandoc_aux.h mandoc.h roff.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
demandoc.o: demandoc.c config.h mandoc.h roff.h man.h mdoc.h mandoc_parse.h
eqn.o: eqn.c config.h mandoc_aux.h mandoc.h roff.h eqn.h libmandoc.h eqn_parse.h
eqn_html.o: eqn_html.c config.h mandoc.h eqn.h out.h html.h
eqn_term.o: eqn_term.c config.h eqn.h out.h term.h
html.o: html.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h out.h html.h manconf.h main.h
lib.o: lib.c config.h mandoc.h roff.h mdoc.h libmdoc.h lib.in
main.o: main.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h man.h tag.h main.h manconf.h mansearch.h
lib.o: lib.c config.h roff.h libmdoc.h lib.in
main.o: main.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h man.h mandoc_parse.h tag.h main.h manconf.h mansearch.h
man.o: man.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
man_html.o: man_html.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h html.h main.h
man_macro.o: man_macro.c config.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
man_term.o: man_term.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h term.h main.h
man_term.o: man_term.c config.h mandoc_aux.h roff.h man.h out.h term.h main.h
man_validate.o: man_validate.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
mandoc.o: mandoc.c config.h mandoc_aux.h mandoc.h roff.h libmandoc.h
mandoc.o: mandoc.c config.h mandoc_aux.h mandoc.h roff.h libmandoc.h roff_int.h
mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h
mandoc_msg.o: mandoc_msg.c mandoc.h
mandoc_ohash.o: mandoc_ohash.c mandoc_aux.h mandoc_ohash.h compat_ohash.h
mandoc_xr.o: mandoc_xr.c mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc_xr.h
mandocd.o: mandocd.c config.h mandoc.h roff.h mdoc.h man.h main.h manconf.h
mandocdb.o: mandocdb.c config.h compat_fts.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mdoc.h man.h manconf.h mansearch.h dba_array.h dba.h
mandocd.o: mandocd.c config.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h main.h manconf.h
mandocdb.o: mandocdb.c config.h compat_fts.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h manconf.h mansearch.h dba_array.h dba.h
manpath.o: manpath.c config.h mandoc_aux.h manconf.h
mansearch.o: mansearch.c config.h mandoc.h mandoc_aux.h mandoc_ohash.h compat_ohash.h manconf.h mansearch.h dbm.h
mansearch.o: mansearch.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h manconf.h mansearch.h dbm.h
mdoc.o: mdoc.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
mdoc_argv.o: mdoc_argv.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
mdoc_html.o: mdoc_html.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h out.h html.h main.h
mdoc_macro.o: mdoc_macro.c config.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
mdoc_man.o: mdoc_man.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h out.h main.h
mdoc_markdown.o: mdoc_markdown.c mandoc_aux.h mandoc.h roff.h mdoc.h main.h
mdoc_state.o: mdoc_state.c mandoc.h roff.h mdoc.h libmandoc.h libmdoc.h
mdoc_term.o: mdoc_term.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h out.h term.h tag.h main.h
mdoc_state.o: mdoc_state.c mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
mdoc_term.o: mdoc_term.c config.h mandoc_aux.h roff.h mdoc.h out.h term.h tag.h main.h
mdoc_validate.o: mdoc_validate.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.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 mandoc.h libmandoc.h
read.o: read.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h libmandoc.h
roff.o: roff.c config.h mandoc.h mandoc_aux.h mandoc_ohash.h compat_ohash.h roff.h libmandoc.h roff_int.h libroff.h predefs.in
out.o: out.c config.h mandoc_aux.h tbl.h out.h
preconv.o: preconv.c config.h mandoc.h roff.h mandoc_parse.h libmandoc.h
read.o: read.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h libmandoc.h roff_int.h
roff.o: roff.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mandoc_parse.h libmandoc.h roff_int.h tbl_parse.h eqn_parse.h predefs.in
roff_html.o: roff_html.c mandoc.h roff.h out.h html.h
roff_term.o: roff_term.c mandoc.h roff.h out.h term.h
roff_validate.o: roff_validate.c mandoc.h roff.h libmandoc.h roff_int.h
soelim.o: soelim.c config.h compat_stringlist.h
st.o: st.c config.h mandoc.h roff.h mdoc.h libmdoc.h st.in
st.o: st.c config.h mandoc.h roff.h libmdoc.h
tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h tag.h
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
tbl.o: tbl.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_parse.h tbl_int.h
tbl_data.o: tbl_data.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_int.h
tbl_html.o: tbl_html.c config.h mandoc.h tbl.h out.h html.h
tbl_layout.o: tbl_layout.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_int.h
tbl_opts.o: tbl_opts.c config.h mandoc.h tbl.h libmandoc.h tbl_int.h
tbl_term.o: tbl_term.c config.h mandoc.h tbl.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 manconf.h main.h
term_ps.o: term_ps.c config.h mandoc_aux.h out.h term.h manconf.h main.h
term_tab.o: term_tab.c mandoc_aux.h out.h term.h
tree.o: tree.c config.h mandoc.h roff.h mdoc.h man.h main.h
tree.o: tree.c config.h mandoc.h roff.h mdoc.h man.h tbl.h eqn.h main.h

76
NEWS
View File

@ -1,7 +1,81 @@
$Id: NEWS,v 1.32 2018/08/08 14:47:38 schwarze Exp $
$Id: NEWS,v 1.34 2019/03/10 09:32:00 schwarze Exp $
This file lists the most important changes in the mandoc.bsd.lv distribution.
Changes in version 1.14.5, released on March 10, 2019
--- MAJOR NEW FEATURES ---
* apropos(1): improve POSIX compliance by accepting case-insensitive
extended regular expressions by default
* new -O tag[=term] output option (open a page at the definition of a term)
* tbl(7) -T html: spanning and horizontal and vertical alignment of cells
* tbl(7) -T html: draw lines on the edges of table cells
* tbl(7) -T utf8: render lines with the Unicode box drawing characters
* mandoc is now able to handle the manual pages of the groff package.
--- MINOR NEW FEATURES ---
* -T html: new option -O toc (table of contents)
* -T html: second argument to -O man to support local and remote links
* mdoc(7) .Bd -centered now fills the text contained in it
* man-ext .SY and .YS macros (synopsis block)
* man-ext .TQ macro (tagged paragraph without vertical space before it)
* tbl(7) \& explicit alignment indicator
* roff(7) .shift, .while, and .return requests
* roff(7) .char request (output glyph definition)
* roff(7) .nop request (no operation)
* roff(7) .ft request: handle the CB, CI, and CR fonts
* roff(7) .if c conditional (character available)
* roff(7) \\$@ escape sequence (insert all macro arguments, quoted)
* roff(7) \*(.T predefined string (interpolate output device name)
* roff(7) \[charNNN] escape sequence (for printable ASCII characters)
* roff(7) \# escape sequence (line continuation with comment)
--- HTML OUTPUT SYNTAX CORRECTIONS ---
* Render .br and \p as <br/>, not as an empty <div>.
* Render .Pp and .PP as <p> and automatically close it when needed.
* Stop writing empty list elements for non-compact .Bl -tag lists.
* Do not put <p> inside <a> if .UR or .MT contain .PP.
* Implement tooltips purely in CSS rather than abusing title= attributes.
--- MINOR FUNCTIONAL IMPROVEMENTS ---
* many improvements to the handling of fill and no-fill mode
* tbl(7): better column widths in the presence of horizontal spans
* several minor improvements to escape sequence handling
* several minor improvements to manual font handling
* portability: autodetect need for _GNU_SOURCE or _OPENBSD_SOURCE
* portability: autodetect whether less(1) supports the -T option
* large numbers of bugfixes of diverse kinds
--- STRUCTURAL IMPROVEMENTS ---
* Disentangle eqn(7) and tbl(7) from other parser header files,
and clean up some parser data structures.
* Substantially simplify error and warning message infrastructure.
--- THANKS TO ---
* John Gardner for crucial help implementing tooltips in CSS.
* Alexander Bluhm, Raphael Graf, Ted Unangst (OpenBSD)
and Daniel Sabogal (Alpine Linux) for patches.
* Anthony Bentley and Jason McIntyre (OpenBSD) for documentation patches,
suggesting new features, bug reports, and useful discussions.
* Kyle Evans and Baptiste Daroussin (FreeBSD) for minor patches.
* Pali Rohar for suggesting multiple new features and for reporting
several bugs and missing features.
* Klemens Nanni (OpenBSD) for suggesting multiple new features.
* Kristaps Dzonsons (bsd.lv), Marc Espie (OpenBSD), Adam Kalisz,
and Laura Morales for suggesting new features.
* Wolfram Schneider and Yuri Pankov (FreeBSD) for reporting missing features.
* Edward Tomasz Napierala (FreeBSD) for suggesting a feature improvement.
* Thomas Klausner (NetBSD) and Sevan Janiyan (SmartOS)
for bug reports and release testing.
* Bryan Steele, Janne Johansson, Kurt Mosiejczuk, Mike Belopuhov, Theo
Buehler, Todd Miller (OpenBSD), Andreas Gustafsson, Christos Zoulas,
Robert Elz (NetBSD), Kurt Jaeger (FreeBSD), Fabio Scotoni, Kelvin
Sherlock, Mark Harris, Orestis Ioannou, Raf Czlonka, and Sean Farrell
for bug reports.
* Ulrich Spoerlein (FreeBSD), Leah Neukirchen (Void Linux),
Matej Cepl (openSUSE), and Jan Stary (MacOS X) for release testing.
* Brian Callahan and Stuart Henderson (OpenBSD) for help
with the OpenBSD groff port.
* Bertrand Garrigues, Branden Robinson, Ralph Corderoy, and Werner
Lemberg (GNU troff) for checking groff patches.
* Scott Cheloha, Theo de Raadt (OpenBSD)
and Natanael Copa (Alpine Linux) for useful discussions.
Changes in version 1.14.4, released on August 8, 2018
--- MAJOR NEW FEATURES ---

124
TODO
View File

@ -1,6 +1,6 @@
************************************************************************
* Official mandoc TODO.
* $Id: TODO,v 1.258 2018/08/06 14:16:30 schwarze Exp $
* $Id: TODO,v 1.289 2019/03/04 13:01:57 schwarze Exp $
************************************************************************
Many issues are annotated for difficulty as follows:
@ -38,18 +38,6 @@ are mere guesses, and some may be wrong.
--- missing roff features ----------------------------------------------
- .nop prints its arguments as text,
see groff(7) for an example
- .ft CB selects constant-width bold font
see groff_out(7) for examples
- \*(.T prints the device being used,
see groff_char(7) for an example
- \[charNN], \[charNNN] prints a single-byte codepoint
see groff_char(7) for examples
- .ad (adjust margins)
.ad l -- adjust left margin only (flush left)
.ad r -- adjust right margin only (flush right)
@ -69,34 +57,11 @@ are mere guesses, and some may be wrong.
reported by brad@ Sat, 15 Jan 2011 15:45:23 -0500
loc *** exist *** algo *** size ** imp *
- .while and .shift
found by jca@ in ratpoison(1) Sun, 30 Jun 2013 12:01:09 +0200
loc * exist ** algo ** size ** imp **
- \w'' improve 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
loc ** exist *** algo *** size * imp ***
- \\ in high-level macro arguments
Currently, \\ is expanded in two situations:
1) macro and string definition (roff.c setstrn())
2) macro argument parsing (mandoc.c mandoc_getarg())
For user defined macros, the second happens in time because of ROFF_REPARSE.
But for standard high-level macros, it only happens after entering the
high level parsers, which is too late because the code doesn't get
back to roff.c roff_res() from that point. Because this requires
distinguishing requests, user-defined macros and standard macros
on the roff_res() level, it is hard to solve without the parser reorg.
Found by naddy@ in devel/cutils cobfusc(1) Mon, 16 Feb 2015 19:10:52 +0100
loc *** exist *** algo *** size ** imp *
- check for missing roff escape sequences, implement those that are
trivial even if not usually appearing in manual pages, gracefully
ignore the non-trivial ones, document what they are supposed to do
and what mandoc does instead
loc * exist ** algo * size * imp *
--- missing mdoc features ----------------------------------------------
- .Bl -column .Xo support is missing
@ -112,13 +77,6 @@ are mere guesses, and some may be wrong.
from jmc@ Wed, 14 Jul 2010 18:10:32 +0100
loc * exist *** algo *** size ** imp **
- .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.
loc *** exist *** algo ** size ** imp ** (parser reorg would help)
- .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).
@ -174,15 +132,6 @@ are mere guesses, and some may be wrong.
--- missing man features -----------------------------------------------
- .SY and .YS,
used by many groff manual pages
- preserve punctuation following .ME,
see ditroff(7) for an example
- .TQ tagged paragraph continuation,
see groff_diff(7) for examples
- groff_www(7) .MTO and .URL
These macros were used by the GNU grep(1) man page.
The groff_www(7) manual page itself uses them, too.
@ -193,18 +142,10 @@ are mere guesses, and some may be wrong.
--- missing tbl features -----------------------------------------------
- the "s" layout column specifier is used for placement of data
into columns, but ignored during column width calculations
synaptics(4) found by tedu@ Mon, 17 Aug 2015 21:17:42 -0400
loc * exist ** algo *** size * imp **
- vertical centering in cells vertically spanned with ^
pali dot rohar at gmail dot com 16 Jul 2018 13:03:35 +0200
loc * exist *** algo *** size ** imp *
- support .ds requests inside tbl(7) code,
see tbl(1) for an example
- support mdoc(7) and man(7) macros inside tbl(7) code;
probably requires the parser reorg and letting tbl(7)
use roff_node such that macro sets can mix;
@ -212,35 +153,24 @@ are mere guesses, and some may be wrong.
loc *** exist ** algo *** size ** imp ***
- look at the POSIX manuals in the books/man-pages-posix port,
they use some unsupported tbl(7) features.
they use some unsupported tbl(7) features, mostly macros in tbl(7).
loc * exist ** algo ** size ** imp ***
- look what Joerg Schilling manual pages use
Thu, 19 Mar 2015 18:31:48 +0100
- use Unicode U+2500 to U+256C for table borders
in tbl(7) -Tutf-8 output
suggested by bentley@ Tue, 14 Oct 2014 04:10:55 -0600
loc * exist ** algo * size * imp **
- implement horizontal and vertical alignment in HTML output
pali dot rohar at gmail dot com 16 Jul 2018 13:03:35 +0200
loc * exist * algo * size * imp ***
- implement cell spanning in HTML output
pali dot rohar at gmail dot com 16 Jul 2018 13:03:35 +0200
loc * exist * algo ** size ** imp **
- implement table borders in HTML output
pali dot rohar at gmail dot com 16 Jul 2018 13:03:35 +0200
loc * exist * algo ** size ** imp **
--- missing eqn features -----------------------------------------------
- In a matrix, break the output line after each matrix line.
Found in the discussion at CDBUG 2015.
Suggested by Avi Weinstock.
loc * exist * algo * size * imp **
Found in the discussion at CDBUG 2015. Suggested by Avi Weinstock.
This may not be the ideal solution after all: eqn(7) matrices
are lists of columns, so Avi's proposal would show each *column*
on its own *line*, which is likely to cause confusion.
A better solution, but much harder to implement, would be to
actually show the coordinates of column vectors on different
terminal output lines, using the clumnated output facilities
developed for .Bl -tag, .Bl -column, and also used for tbl(7).
loc * exist * algo ** size ** imp **
- The "size" keyword is parsed, but ignored by the formatter.
loc * exist * algo * size * imp *
@ -341,9 +271,6 @@ are mere guesses, and some may be wrong.
* formatting issues: ugly output
************************************************************************
- .UR can nest inside .TP,
see roff(7) for examples
- revisit empty in-line macros
look at the difference between "Em x Em ." and "Sq x Em ."
Carsten Kunze Fri, 12 Dec 2014 00:15:41 +0100
@ -400,6 +327,8 @@ are mere guesses, and some may be wrong.
- a line starting with "\fB something" counts as starting with whitespace
and triggers a line break; found in audio/normalize-mp3(1)
This will become easier once escape sequences are represented
by syntax tree nodes.
loc ** exist * algo ** size * imp **
- formatting /usr/local/man/man1/latex2man.1 with groff and mandoc
@ -421,17 +350,6 @@ are mere guesses, and some may be wrong.
--- HTML issues --------------------------------------------------------
- wrap Sh and Ss content into <div>
Laura Morales <lauretas at mail dot com> 21 Apr 2018 18:10:48 +0200
(Evaluate whether this is really useful and has no adverse
side effects before implementing; if it is possible,
it does seem cleaner.)
loc ** exist ** algo * size * imp ***
- format ".IP *" etc. as <ul> rather than <dl>
https://github.com/Debian/debiman/issues/67
loc ** exist ** algo ** size * imp ***
- .Bf at the beginning of a paragraph inserts a bogus 1ex horizontal
space, see for example random(3). Introduced in
http://mdocml.bsd.lv/cgi-bin/cvsweb/mdoc_html.c.diff?r1=1.91&r2=1.92
@ -558,18 +476,11 @@ are mere guesses, and some may be wrong.
all over mdoc_macro.c and all subtly different.
loc ** exist ** algo ** size ** imp **
- style message about suspicious uses of - vs. \- vs. \(mi
e.g. -1 is likely wrong (from the mdoclint TODO)
- warn about punctuation - e.g. ',' and ';' - at the beginning
of a text line, if it is likely intended to follow the preceding
output without intervening whitespace, in particular after a
macro line (from the mdoclint TODO)
- mandoc_special does not really check the escape sequence,
but just the overall format
loc ** exist ** algo *** size ** imp **
- makewhatis -p complains about language subdirectories:
/usr/local/man//ru: Unknown directory part
@ -578,9 +489,6 @@ are mere guesses, and some may be wrong.
* documentation issues
************************************************************************
- dashes, hyphens, and minus signs in manual pages
jmc@ Fri, 28 Mar 2014 07:19:27 +0000
- mark macros as: page structure domain, manual domain, general text domain
is this useful?
@ -606,10 +514,6 @@ are mere guesses, and some may be wrong.
Found by Aaron M. Ucko in the GNU Hurd via Bdale Garbee,
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=829624
- We use the input line number at several places to distinguish
same-line from different-line input. That plainly doesn't work
with user-defined macros, leading to random breakage.
- Is it possible to further simplify ENDBODY_SPACE?
- Find better ways to prevent endless loops
@ -629,8 +533,6 @@ are mere guesses, and some may be wrong.
output through libz.
- Privilege separation (see OpenSSH).
- Enable caching support via HTTP 304 and If-Modified-Since.
- Have Mac OSX systems automatically disable -static compilation of the
CGI: -static isn't supported.
************************************************************************
* to improve in the groff_mdoc(7) macros

View File

@ -1,7 +1,7 @@
.\" $Id: apropos.1,v 1.47 2018/02/23 18:54:02 schwarze Exp $
.\" $Id: apropos.1,v 1.49 2018/11/22 12:33:52 schwarze Exp $
.\"
.\" Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2011, 2012, 2014, 2017 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2011,2012,2014,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: February 23 2018 $
.Dd $Mdocdate: November 22 2018 $
.Dt APROPOS 1
.Os
.Sh NAME
@ -51,8 +51,7 @@ searches for
.Xr makewhatis 8
databases in the default paths stipulated by
.Xr man 1
and uses case-insensitive substring matching
.Pq the Cm = No operator
and uses case-insensitive extended regular expression matching
over manual names and descriptions
.Pq the Li \&Nm No and Li \&Nd No macro keys .
Multiple terms imply pairwise
@ -93,7 +92,7 @@ format.
Search for all words in
.Ar expression
in manual page names only.
The search is case insensitive and matches whole words only.
The search is case-insensitive and matches whole words only.
In this mode, macro keys, comparison operators, and logical operators
are not available.
.It Fl k
@ -123,7 +122,7 @@ Restrict the search to pages for the specified
.Xr machine 1
architecture.
.Ar arch
is case insensitive.
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.
@ -199,7 +198,7 @@ Operator
.Cm =
evaluates a substring, while
.Cm \(ti
evaluates a regular expression.
evaluates a case-sensitive extended regular expression.
.It Fl i Ar term
If
.Ar term
@ -208,26 +207,10 @@ is evaluated case-insensitively.
Has no effect on substring terms.
.El
.Pp
Results are sorted according to the following criteria:
.Bl -enum
.It
The manpath directory tree the page is found in, according to the
order specified with
.Fl M ,
.Fl m ,
the
.Ev MANPATH
environment variable, the
.Xr man.conf 5
configuration file, or the default documented in
.Xr man.conf 5 .
.It
The section number in ascending numerical order.
.It
The page name in ascending
Results are sorted first according to the section number in ascending
numerical order, then by the page name in ascending
.Xr ascii 7
alphabetical order, case-insensitive.
.El
.Pp
Each output line is formatted as
.Pp
@ -339,7 +322,7 @@ function arguments appearing on
.Ic \&Fn
lines
.It Li \&Fn
fuction names marked up with
function names marked up with
.Ic \&Fo
macros
.It Li \&In
@ -407,7 +390,7 @@ Search for
.Qq .cf
as a substring of manual names and descriptions:
.Pp
.Dl $ apropos .cf
.Dl $ apropos =.cf
.Pp
Include matches for
.Qq .cnf
@ -415,9 +398,9 @@ and
.Qq .conf
as well:
.Pp
.Dl $ apropos .cf .cnf .conf
.Dl $ apropos =.cf =.cnf =.conf
.Pp
Search in names and descriptions using a regular expression:
Search in names and descriptions using a case-sensitive regular expression:
.Pp
.Dl $ apropos \(aq\(tiset.?[ug]id\(aq
.Pp
@ -448,6 +431,24 @@ The following two invocations are equivalent:
.Xr man 1 ,
.Xr re_format 7 ,
.Xr makewhatis 8
.Sh STANDARDS
The
.Nm
utility is compliant with the
.St -p1003.1-2008
specification of
.Xr man 1
.Fl k .
.Pp
All options, the
.Nm whatis
command, support for logical operators, macro keys,
substring matching, sorting of results, the environment variables
.Ev MANPAGER
and
.Ev MANPATH ,
the database format, and the configuration file
are extensions to that specification.
.Sh HISTORY
Part of the functionality of
.Nm whatis

54
arch.c Normal file
View File

@ -0,0 +1,54 @@
/* $Id: arch.c,v 1.14 2019/03/04 13:01:57 schwarze Exp $ */
/*
* Copyright (c) 2017, 2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <string.h>
#include "roff.h"
int
arch_valid(const char *arch, enum mandoc_os os)
{
const char *openbsd_arch[] = {
"alpha", "amd64", "arm64", "armv7", "hppa", "i386",
"landisk", "loongson", "luna88k", "macppc", "mips64",
"octeon", "sgi", "socppc", "sparc64", NULL
};
const char *netbsd_arch[] = {
"acorn26", "acorn32", "algor", "alpha", "amiga",
"arc", "atari",
"bebox", "cats", "cesfic", "cobalt", "dreamcast",
"emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
"hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
"i386", "ibmnws", "luna68k",
"mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
"netwinder", "news68k", "newsmips", "next68k",
"pc532", "playstation2", "pmax", "pmppc", "prep",
"sandpoint", "sbmips", "sgimips", "shark",
"sparc", "sparc64", "sun2", "sun3",
"vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
};
const char **arches[] = { NULL, netbsd_arch, openbsd_arch };
const char **arch_p;
if ((arch_p = arches[os]) == NULL)
return 1;
for (; *arch_p != NULL; arch_p++)
if (strcmp(*arch_p, arch) == 0)
return 1;
return 0;
}

4
att.c
View File

@ -1,4 +1,4 @@
/* $Id: att.c,v 1.16 2017/06/24 14:38:32 schwarze Exp $ */
/* $Id: att.c,v 1.18 2018/12/13 11:55:46 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -19,9 +19,7 @@
#include <sys/types.h>
#include <string.h>
#include "mandoc.h"
#include "roff.h"
#include "mdoc.h"
#include "libmdoc.h"
#define LINE(x, y) \

156
cgi.c
View File

@ -1,7 +1,7 @@
/* $Id: cgi.c,v 1.158 2018/05/29 20:32:45 schwarze Exp $ */
/* $Id: cgi.c,v 1.166 2019/03/06 12:32:41 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@usta.de>
* Copyright (c) 2014, 2015, 2016, 2017, 2018 Ingo Schwarze <schwarze@usta.de>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -38,6 +38,7 @@
#include "roff.h"
#include "mdoc.h"
#include "man.h"
#include "mandoc_parse.h"
#include "main.h"
#include "manconf.h"
#include "mansearch.h"
@ -69,6 +70,7 @@ enum focus {
static void html_print(const char *);
static void html_putchar(char);
static int http_decode(char *);
static void http_encode(const char *p);
static void parse_manpath_conf(struct req *);
static void parse_path_info(struct req *req, const char *path);
static void parse_query_string(struct req *, const char *);
@ -90,6 +92,7 @@ static void resp_format(const struct req *, const char *);
static void resp_searchform(const struct req *, enum focus);
static void resp_show(const struct req *, const char *);
static void set_query_attr(char **, char **);
static int validate_arch(const char *);
static int validate_filename(const char *);
static int validate_manpath(const struct req *, const char *);
static int validate_urifrag(const char *);
@ -315,6 +318,18 @@ http_decode(char *p)
return 1;
}
static void
http_encode(const char *p)
{
for (; *p != '\0'; p++) {
if (isalnum((unsigned char)*p) == 0 &&
strchr("-._~", *p) == NULL)
printf("%%%2.2X", (unsigned char)*p);
else
putchar(*p);
}
}
static void
resp_begin_http(int code, const char *msg)
{
@ -489,6 +504,18 @@ validate_manpath(const struct req *req, const char* manpath)
return 0;
}
static int
validate_arch(const char *arch)
{
int i;
for (i = 0; i < arch_MAX; i++)
if (strcmp(arch, arch_names[i]) == 0)
return 1;
return 0;
}
static int
validate_filename(const char *file)
{
@ -562,9 +589,11 @@ pg_redirect(const struct req *req, const char *name)
printf("%s/", req->q.manpath);
if (req->q.arch != NULL)
printf("%s/", req->q.arch);
printf("%s", name);
if (req->q.sec != NULL)
printf(".%s", req->q.sec);
http_encode(name);
if (req->q.sec != NULL) {
putchar('.');
http_encode(req->q.sec);
}
printf("\r\nContent-Type: text/html; charset=utf-8\r\n\r\n");
}
@ -820,7 +849,7 @@ resp_format(const struct req *req, const char *file)
{
struct manoutput conf;
struct mparse *mp;
struct roff_man *man;
struct roff_meta *meta;
void *vp;
int fd;
int usepath;
@ -831,37 +860,26 @@ resp_format(const struct req *req, const char *file)
}
mchars_alloc();
mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1,
MANDOCERR_MAX, NULL, MANDOC_OS_OTHER, req->q.manpath);
mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
MPARSE_VALIDATE, MANDOC_OS_OTHER, req->q.manpath);
mparse_readfd(mp, fd, file);
close(fd);
meta = mparse_result(mp);
memset(&conf, 0, sizeof(conf));
conf.fragment = 1;
conf.style = mandoc_strdup(CSS_DIR "/mandoc.css");
conf.toc = 1;
usepath = strcmp(req->q.manpath, req->p[0]);
mandoc_asprintf(&conf.man, "/%s%s%s%s%%N.%%S",
scriptname, *scriptname == '\0' ? "" : "/",
usepath ? req->q.manpath : "", usepath ? "/" : "");
mparse_result(mp, &man, NULL);
if (man == NULL) {
warnx("fatal mandoc error: %s/%s", req->q.manpath, file);
pg_error_internal();
mparse_free(mp);
mchars_free();
return;
}
vp = html_alloc(&conf);
if (man->macroset == MACROSET_MDOC) {
mdoc_validate(man);
html_mdoc(vp, man);
} else {
man_validate(man);
html_man(vp, man);
}
if (meta->macroset == MACROSET_MDOC)
html_mdoc(vp, meta);
else
html_man(vp, meta);
html_free(vp);
mparse_free(mp);
@ -1089,7 +1107,7 @@ main(void)
return EXIT_FAILURE;
}
if ( ! (NULL == req.q.arch || validate_urifrag(req.q.arch))) {
if (req.q.arch != NULL && validate_arch(req.q.arch) == 0) {
pg_error_badrequest(
"You specified an invalid architecture.");
return EXIT_FAILURE;
@ -1115,80 +1133,74 @@ main(void)
}
/*
* If PATH_INFO is not a file name, translate it to a query.
* Translate PATH_INFO to a query.
*/
static void
parse_path_info(struct req *req, const char *path)
{
char *dir[4];
int i;
const char *name, *sec, *end;
req->isquery = 0;
req->q.equal = 1;
req->q.manpath = mandoc_strdup(path);
req->q.manpath = NULL;
req->q.arch = NULL;
/* Mandatory manual page name. */
if ((req->q.query = strrchr(req->q.manpath, '/')) == NULL) {
req->q.query = req->q.manpath;
req->q.manpath = NULL;
} else
*req->q.query++ = '\0';
if ((name = strrchr(path, '/')) == NULL)
name = path;
else
name++;
/* Optional trailing section. */
if ((req->q.sec = strrchr(req->q.query, '.')) != NULL) {
if(isdigit((unsigned char)req->q.sec[1])) {
*req->q.sec++ = '\0';
req->q.sec = mandoc_strdup(req->q.sec);
} else
req->q.sec = NULL;
sec = strrchr(name, '.');
if (sec != NULL && isdigit((unsigned char)*++sec)) {
req->q.query = mandoc_strndup(name, sec - name - 1);
req->q.sec = mandoc_strdup(sec);
} else {
req->q.query = mandoc_strdup(name);
req->q.sec = NULL;
}
/* Handle the case of name[.section] only. */
if (req->q.manpath == NULL)
if (name == path)
return;
req->q.query = mandoc_strdup(req->q.query);
/* Split directory components. */
dir[i = 0] = req->q.manpath;
while ((dir[i + 1] = strchr(dir[i], '/')) != NULL) {
if (++i == 3) {
pg_error_badrequest(
"You specified too many directory components.");
exit(EXIT_FAILURE);
}
*dir[i]++ = '\0';
}
/* Optional manpath. */
if ((i = validate_manpath(req, req->q.manpath)) == 0)
end = strchr(path, '/');
req->q.manpath = mandoc_strndup(path, end - path);
if (validate_manpath(req, req->q.manpath)) {
path = end + 1;
if (name == path)
return;
} else {
free(req->q.manpath);
req->q.manpath = NULL;
else if (dir[1] == NULL)
return;
}
/* Optional section. */
if (strncmp(dir[i], "man", 3) == 0) {
if (strncmp(path, "man", 3) == 0 || strncmp(path, "cat", 3) == 0) {
path += 3;
end = strchr(path, '/');
free(req->q.sec);
req->q.sec = mandoc_strdup(dir[i++] + 3);
req->q.sec = mandoc_strndup(path, end - path);
path = end + 1;
if (name == path)
return;
}
if (dir[i] == NULL) {
if (req->q.manpath == NULL)
free(dir[0]);
return;
/* Optional architecture. */
end = strchr(path, '/');
if (end + 1 != name) {
pg_error_badrequest(
"You specified too many directory components.");
exit(EXIT_FAILURE);
}
if (dir[i + 1] != NULL) {
req->q.arch = mandoc_strndup(path, end - path);
if (validate_arch(req->q.arch) == 0) {
pg_error_badrequest(
"You specified an invalid directory component.");
exit(EXIT_FAILURE);
}
/* Optional architecture. */
if (i) {
req->q.arch = mandoc_strdup(dir[i]);
if (req->q.manpath == NULL)
free(dir[0]);
} else
req->q.arch = dir[0];
}
/*

37
chars.c
View File

@ -1,7 +1,7 @@
/* $Id: chars.c,v 1.73 2017/08/23 13:01:29 schwarze Exp $ */
/* $Id: chars.c,v 1.78 2018/12/15 19:30:26 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -23,6 +23,7 @@
#include <ctype.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -47,20 +48,13 @@ static struct ln lines[] = {
{ " ", ascii_nbrsp, 0x00a0 },
{ "~", ascii_nbrsp, 0x00a0 },
{ "0", " ", 0x2002 },
{ "|", "", 0 },
{ "^", "", 0 },
{ "&", "", 0 },
{ "%", "", 0 },
{ ":", ascii_break, 0 },
/* XXX The following three do not really belong here. */
{ "t", "", 0 },
{ "c", "", 0 },
{ "}", "", 0 },
/* Lines. */
{ "ba", "|", 0x007c },
{ "br", "|", 0x2502 },
{ "ul", "_", 0x005f },
{ "_", "_", 0x005f },
{ "ru", "_", 0x005f },
{ "rn", "-", 0x203e },
{ "bb", "|", 0x00a6 },
@ -82,10 +76,10 @@ static struct ln lines[] = {
{ "sh", "#", 0x0023 },
{ "CR", "<cr>", 0x21b5 },
{ "OK", "\\/", 0x2713 },
{ "CL", "<club>", 0x2663 },
{ "SP", "<spade>", 0x2660 },
{ "HE", "<heart>", 0x2665 },
{ "DI", "<diamond>", 0x2666 },
{ "CL", "C", 0x2663 },
{ "SP", "S", 0x2660 },
{ "HE", "H", 0x2665 },
{ "DI", "D", 0x2666 },
/* Legal symbols. */
{ "co", "(C)", 0x00a9 },
@ -240,7 +234,7 @@ static struct ln lines[] = {
{ "Ah", "<Aleph>", 0x2135 },
{ "Im", "<Im>", 0x2111 },
{ "Re", "<Re>", 0x211c },
{ "wp", "P", 0x2118 },
{ "wp", "p", 0x2118 },
{ "pd", "<del>", 0x2202 },
{ "-h", "/h", 0x210f },
{ "hbar", "/h", 0x210f },
@ -287,6 +281,7 @@ static struct ln lines[] = {
{ "ho", ",", 0x02db },
{ "ha", "^", 0x005e },
{ "ti", "~", 0x007e },
{ "u02DC", "~", 0x02dc },
/* Accented letters. */
{ "'A", "'\bA", 0x00c1 },
@ -294,11 +289,13 @@ static struct ln lines[] = {
{ "'I", "'\bI", 0x00cd },
{ "'O", "'\bO", 0x00d3 },
{ "'U", "'\bU", 0x00da },
{ "'Y", "'\bY", 0x00dd },
{ "'a", "'\ba", 0x00e1 },
{ "'e", "'\be", 0x00e9 },
{ "'i", "'\bi", 0x00ed },
{ "'o", "'\bo", 0x00f3 },
{ "'u", "'\bu", 0x00fa },
{ "'y", "'\by", 0x00fd },
{ "`A", "`\bA", 0x00c0 },
{ "`E", "`\bE", 0x00c8 },
{ "`I", "`\bI", 0x00cc },
@ -359,7 +356,7 @@ static struct ln lines[] = {
{ "Eu", "EUR", 0x20ac },
{ "eu", "EUR", 0x20ac },
{ "Ye", "=\bY", 0x00a5 },
{ "Po", "GBP", 0x00a3 },
{ "Po", "-\bL", 0x00a3 },
{ "Cs", "o\bx", 0x00a4 },
{ "Fn", ",\bf", 0x0192 },
@ -460,7 +457,7 @@ mchars_spec2cp(const char *p, size_t sz)
end = p + sz;
ln = ohash_find(&mchars, ohash_qlookupi(&mchars, p, &end));
return ln != NULL ? ln->unicode : sz == 1 ? (unsigned char)*p : -1;
return ln != NULL ? ln->unicode : -1;
}
int
@ -490,10 +487,8 @@ mchars_spec2str(const char *p, size_t sz, size_t *rsz)
end = p + sz;
ln = ohash_find(&mchars, ohash_qlookupi(&mchars, p, &end));
if (ln == NULL) {
*rsz = 1;
return sz == 1 ? p : NULL;
}
if (ln == NULL)
return NULL;
*rsz = strlen(ln->ascii);
return ln->ascii;

142
configure vendored
View File

@ -1,8 +1,8 @@
#!/bin/sh
#
# $Id: configure,v 1.66 2018/07/31 15:34:00 schwarze Exp $
# $Id: configure,v 1.70 2019/03/06 16:04:31 schwarze Exp $
#
# Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
# Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@ -37,6 +37,7 @@ SOURCEDIR=`dirname "$0"`
MANPATH_BASE="/usr/share/man:/usr/X11R6/man"
MANPATH_DEFAULT="/usr/share/man:/usr/X11R6/man:/usr/local/man"
OSENUM=
OSNAME=
UTF8_LOCALE=
@ -64,6 +65,7 @@ HAVE_FTS_COMPARE_CONST=
HAVE_GETLINE=
HAVE_GETSUBOPT=
HAVE_ISBLANK=
HAVE_LESS_T=
HAVE_MKDTEMP=
HAVE_NANOSLEEP=
HAVE_NTOHL=
@ -90,6 +92,9 @@ HAVE_SYS_ENDIAN=
HAVE_VASPRINTF=
HAVE_WCHAR=
NEED_GNU_SOURCE=0
NEED_OPENBSD_SOURCE=0
PREFIX="/usr/local"
BINDIR=
SBINDIR=
@ -154,31 +159,34 @@ ismanual() {
# In case of failure, do not decide anything yet.
# Arguments: test file name, test var name, additional CFLAGS
singletest() {
n=${1}${3}${4}
cat 1>&3 << __HEREDOC__
testing ${1}${3} ...
${COMP} -o test-${1} test-${1}.c ${3}
testing ${n} ...
${COMP} -o test-${1} test-${1}.c ${3} ${4}
__HEREDOC__
if ${COMP} -o "test-${1}" "${SOURCEDIR}/test-${1}.c" ${3} 1>&3 2>&3
if ${COMP} -o "test-${1}" "${SOURCEDIR}/test-${1}.c" ${3} ${4} 1>&3 2>&3
then
echo "partial result of ${1}${3}: ${CC} succeeded" 1>&3
echo "partial result of ${n}: ${CC} succeeded" 1>&3
else
echo "result of ${1}${3}: ${CC} failed with exit status $?" 1>&3
echo "result of compiling ${1}${3}: no" 1>&3
echo "result of ${n}: ${CC} failed with exit status $?" 1>&3
echo "result of compiling ${n}: no" 1>&3
echo 1>&3
return 1
fi
if ./test-${1} 1>&3 2>&3; then
echo "tested ${1}${3}: yes" 1>&2
echo "result of running ${1}${3}: yes" 1>&3
echo "tested ${n}: yes" 1>&2
echo "result of running ${n}: yes" 1>&3
echo 1>&3
eval HAVE_${2}=1
[ "X$3" = "X-D_GNU_SOURCE" ] && NEED_GNU_SOURCE=1
[ "X$3" = "X-D_OPENBSD_SOURCE" ] && NEED_OPENBSD_SOURCE=1
rm "test-${1}"
return 0
else
echo "result of ${1}${3}: execution failed with exit status $?" 1>&3
echo "result of running ${1}${3}: no" 1>&3
echo "result of ${n}: execution failed with exit status $?" 1>&3
echo "result of running ${n}: no" 1>&3
echo 1>&3
rm "test-${1}"
return 1
@ -191,8 +199,8 @@ __HEREDOC__
runtest() {
eval _manual=\${HAVE_${2}}
ismanual "${1}" "${2}" "${_manual}" && return 0
singletest "${1}" "${2}" "${3}" && return 0
echo "tested ${1}${3}: no" 1>&2
singletest "${1}" "${2}" "${3}" "${4}" && return 0
echo "tested ${1}${3}${4}: no" 1>&2
eval HAVE_${2}=0
return 1
}
@ -213,28 +221,52 @@ get_locale() {
return 0;
}
# --- operating system -------------------------------------------------
if [ -n "${OSENUM}" ]; then
echo "OSENUM specified manually: ${OSENUM}" 1>&2
echo "OSENUM specified manually: ${OSENUM}" 1>&3
else
OSDETECT=`uname`
if [ "X${OSDETECT}" = "XNetBSD" ]; then
OSENUM=MANDOC_OS_NETBSD
elif [ "X${OSDETECT}" = "XOpenBSD" ]; then
OSENUM=MANDOC_OS_OPENBSD
else
OSENUM=MANDOC_OS_OTHER
fi
echo "tested operating system: ${OSDETECT} -> OSENUM=${OSENUM}" 1>&2
echo "tested operating system: ${OSDETECT} -> OSENUM=${OSENUM}" 1>&3
unset OSDETECT
fi
echo 1>&3
# --- compiler options -------------------------------------------------
DEFCFLAGS="-g -W -Wall -Wmissing-prototypes -Wstrict-prototypes -Wwrite-strings -Wno-unused-parameter"
if [ -n "${CFLAGS}" ]; then
COMP="${CC} ${CFLAGS}"
echo "selected CFLAGS=\"${CFLAGS}\" (manual)" 1>&2
echo "selected CFLAGS=\"${CFLAGS}\" (manual)" 1>&3
echo 1>&3
else
CFLAGS="-g -W -Wall -Wmissing-prototypes -Wstrict-prototypes"
CFLAGS="${CFLAGS} -Wwrite-strings -Wno-unused-parameter"
COMP="${CC} ${CFLAGS} -Wno-unused -Werror"
echo -n "tested ${CC} -W: " 1>&2
echo -n "testing ${CC} -W: " 1>&3
runtest noop WFLAG || true
if [ "${HAVE_WFLAG}" -eq 0 ]; then
CFLAGS="-g"
COMP="${CC} ${CFLAGS}"
fi
echo "selected CFLAGS=\"${CFLAGS}\"" 1>&2
echo "selected CFLAGS=\"${CFLAGS}\"" 1>&3
echo 1>&3
else
COMP="${CC} ${DEFCFLAGS} -Wno-unused -Werror"
fi
echo -n "tested ${CC} -W: " 1>&2
echo -n "testing ${CC} -W: " 1>&3
runtest noop WFLAG || true
if [ -n "${CFLAGS}" ]; then
echo "CFLAGS specified manually:" 1>&3
elif [ ${HAVE_WFLAG} -eq 0 ]; then
CFLAGS="-g"
else
CFLAGS="${DEFCFLAGS}"
fi
echo "selected CFLAGS=\"${CFLAGS}\"" 1>&2
echo "selected CFLAGS=\"${CFLAGS}\"" 1>&3
echo 1>&3
COMP="${CC} ${CFLAGS}"
[ ${HAVE_WFLAG} -eq 0 ] || COMP="${COMP} -Wno-unused -Werror"
if [ -n "${STATIC}" ]; then
echo "selected STATIC=\"${STATIC}\" (manual)" 1>&2
@ -257,7 +289,8 @@ runtest be32toh SYS_ENDIAN -DSYS_ENDIAN || true
runtest EFTYPE EFTYPE || true
runtest err ERR || true
runtest getline GETLINE || true
runtest getsubopt GETSUBOPT || true
singletest getsubopt GETSUBOPT || \
runtest getsubopt GETSUBOPT -D_GNU_SOURCE || true
runtest isblank ISBLANK || true
runtest mkdtemp MKDTEMP || true
runtest ntohl NTOHL || true
@ -266,19 +299,25 @@ runtest PATH_MAX PATH_MAX || true
runtest pledge PLEDGE || true
runtest sandbox_init SANDBOX_INIT || true
runtest progname PROGNAME || true
runtest reallocarray REALLOCARRAY || true
runtest recallocarray RECALLOCARRAY || true
singletest reallocarray REALLOCARRAY || \
runtest reallocarray REALLOCARRAY -D_OPENBSD_SOURCE || true
singletest recallocarray RECALLOCARRAY || \
runtest recallocarray RECALLOCARRAY -D_OPENBSD_SOURCE || true
runtest rewb-bsd REWB_BSD || true
runtest rewb-sysv REWB_SYSV || true
runtest strcasestr STRCASESTR || true
singletest strcasestr STRCASESTR || \
runtest strcasestr STRCASESTR -D_GNU_SOURCE || true
runtest stringlist STRINGLIST || true
runtest strlcat STRLCAT || true
runtest strlcpy STRLCPY || true
runtest strndup STRNDUP || true
runtest strptime STRPTIME || true
singletest strptime STRPTIME || \
runtest strptime STRPTIME -D_GNU_SOURCE || true
runtest strsep STRSEP || true
runtest strtonum STRTONUM || true
runtest vasprintf VASPRINTF || true
singletest strtonum STRTONUM || \
runtest strtonum STRTONUM -D_OPENBSD_SOURCE || true
singletest vasprintf VASPRINTF || \
runtest vasprintf VASPRINTF -D_GNU_SOURCE || true
if [ ${HAVE_ENDIAN} -eq 0 -a \
${HAVE_SYS_ENDIAN} -eq 0 -a \
@ -296,9 +335,25 @@ else
runtest fts FTS || true
fi
if ismanual "less -T" LESS_T ${HAVE_LESS_T}; then
:
elif less -ET /dev/null test-noop.c 1>/dev/null 2>&3; then
HAVE_LESS_T=1
echo "tested less -T: yes" 1>&2
echo "tested less -T: yes" 1>&3
echo 1>&3
else
HAVE_LESS_T=0
echo "tested less -T: no" 1>&2
echo "tested less -T: no" 1>&3
echo 1>&3
fi
# --- wide character and locale support ---
if get_locale; then
runtest wchar WCHAR -DUTF8_LOCALE=\"${UTF8_LOCALE}\" || true
singletest wchar WCHAR -DUTF8_LOCALE=\"${UTF8_LOCALE}\" || \
runtest wchar WCHAR -D_GNU_SOURCE \
-DUTF8_LOCALE=\"${UTF8_LOCALE}\" || true
else
HAVE_WCHAR=0
echo "tested wchar: no (no UTF8_LOCALE)" 1>&2
@ -383,12 +438,11 @@ cat << __HEREDOC__
#define __attribute__(x)
#endif
#if defined(__linux__) || defined(__MINT__)
#define _GNU_SOURCE /* See test-*.c what needs this. */
#endif
__HEREDOC__
[ ${NEED_GNU_SOURCE} -eq 0 ] || echo "#define _GNU_SOURCE"
[ ${NEED_OPENBSD_SOURCE} -eq 0 ] || echo "#define _OPENBSD_SOURCE"
[ ${HAVE_GETLINE} -eq 0 -o \
${HAVE_REALLOCARRAY} -eq 0 -o ${HAVE_RECALLOCARRAY} -eq 0 -o \
${HAVE_STRLCAT} -eq 0 -o ${HAVE_STRLCPY} -eq 0 -o \
@ -401,6 +455,7 @@ echo
echo "#define MAN_CONF_FILE \"/etc/${MANM_MANCONF}\""
echo "#define MANPATH_BASE \"${MANPATH_BASE}\""
echo "#define MANPATH_DEFAULT \"${MANPATH_DEFAULT}\""
echo "#define OSENUM ${OSENUM}"
[ -n "${OSNAME}" ] && echo "#define OSNAME \"${OSNAME}\""
[ -n "${UTF8_LOCALE}" ] && echo "#define UTF8_LOCALE \"${UTF8_LOCALE}\""
[ -n "${HOMEBREWDIR}" ] && echo "#define HOMEBREWDIR \"${HOMEBREWDIR}\""
@ -422,6 +477,7 @@ cat << __HEREDOC__
#define HAVE_GETLINE ${HAVE_GETLINE}
#define HAVE_GETSUBOPT ${HAVE_GETSUBOPT}
#define HAVE_ISBLANK ${HAVE_ISBLANK}
#define HAVE_LESS_T ${HAVE_LESS_T}
#define HAVE_MKDTEMP ${HAVE_MKDTEMP}
#define HAVE_NTOHL ${HAVE_NTOHL}
#define HAVE_PLEDGE ${HAVE_PLEDGE}

View File

@ -1,6 +1,6 @@
# $Id: configure.local.example,v 1.34 2018/07/31 15:34:00 schwarze Exp $
# $Id: configure.local.example,v 1.36 2019/03/06 10:18:58 schwarze Exp $
#
# Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
# Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@ -67,6 +67,17 @@ MANPATH_DEFAULT="/usr/share/man:/usr/X11R6/man:/usr/local/man"
MANPATH_BASE="/usr/share/man:/usr/X11R6/man"
# When man(1) is called with the -S option and no manual page is
# found matching the requested name and the requested architecture,
# it tries to figure out whether the requested architecture is valid
# for the present operating system. Normally, ./configure detects
# the operating system using uname(1). If that fails or is not
# desired, either of the following lines can be used:
OSENUM=MANDOC_OS_NETBSD
OSENUM=MANDOC_OS_OPENBSD
OSENUM=MANDOC_OS_OTHER
# In manual pages written in the mdoc(7) language, the operating system
# version is displayed in the page footer line. If an operating system
# is specified as an argument to the .Os macro, that is always used.
@ -77,7 +88,7 @@ MANPATH_BASE="/usr/share/man:/usr/X11R6/man"
# If you do not want uname(3) to be called but instead want a fixed
# string to be used, use the following line:
OSNAME="OpenBSD 6.3"
OSNAME="OpenBSD 6.5"
# The following installation directories are used.
# It is possible to set only one or a few of these variables,
@ -293,6 +304,7 @@ HAVE_FTS_COMPARE_CONST=0 # Setting this implies HAVE_FTS=1.
HAVE_GETLINE=0
HAVE_GETSUBOPT=0
HAVE_ISBLANK=0
HAVE_LESS_T=0
HAVE_MKDTEMP=0
HAVE_NTOHL=0
HAVE_O_DIRECTORY=0

8
dbm.c
View File

@ -1,4 +1,4 @@
/* $Id: dbm.c,v 1.5 2016/10/18 22:27:25 schwarze Exp $ */
/* $Id: dbm.c,v 1.6 2018/11/19 19:22:07 schwarze Exp $ */
/*
* Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
*
@ -151,17 +151,17 @@ dbm_page_get(int32_t ip)
assert(ip < npages);
res.name = dbm_get(pages[ip].name);
if (res.name == NULL)
res.name = "(NULL)";
res.name = "(NULL)\0";
res.sect = dbm_get(pages[ip].sect);
if (res.sect == NULL)
res.sect = "(NULL)";
res.sect = "(NULL)\0";
res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
res.desc = dbm_get(pages[ip].desc);
if (res.desc == NULL)
res.desc = "(NULL)";
res.file = dbm_get(pages[ip].file);
if (res.file == NULL)
res.file = " (NULL)";
res.file = " (NULL)\0";
res.addr = dbm_addr(pages + ip);
return &res;
}

View File

@ -1,4 +1,4 @@
/* $Id: demandoc.c,v 1.29 2017/06/24 14:38:32 schwarze Exp $ */
/* $Id: demandoc.c,v 1.33 2019/03/03 11:01:15 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -29,6 +29,7 @@
#include "roff.h"
#include "man.h"
#include "mdoc.h"
#include "mandoc_parse.h"
static void pline(int, int *, int *, int);
static void pman(const struct roff_node *, int *, int *, int);
@ -78,8 +79,8 @@ main(int argc, char *argv[])
argv += optind;
mchars_alloc();
mp = mparse_alloc(MPARSE_SO, MANDOCERR_MAX, NULL,
MANDOC_OS_OTHER, NULL);
mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
MPARSE_VALIDATE, MANDOC_OS_OTHER, NULL);
assert(mp);
if (argc < 1)
@ -109,24 +110,19 @@ usage(void)
static void
pmandoc(struct mparse *mp, int fd, const char *fn, int list)
{
struct roff_man *man;
struct roff_meta *meta;
int line, col;
mparse_readfd(mp, fd, fn);
close(fd);
mparse_result(mp, &man, NULL);
meta = mparse_result(mp);
line = 1;
col = 0;
if (man == NULL)
return;
if (man->macroset == MACROSET_MDOC) {
mdoc_validate(man);
pmdoc(man->first->child, &line, &col, list);
} else {
man_validate(man);
pman(man->first->child, &line, &col, list);
}
if (meta->macroset == MACROSET_MDOC)
pmdoc(meta->first->child, &line, &col, list);
else
pman(meta->first->child, &line, &col, list);
if ( ! list)
putchar('\n');

93
eqn.c
View File

@ -1,7 +1,7 @@
/* $Id: eqn.c,v 1.78 2017/07/15 16:26:17 schwarze Exp $ */
/* $Id: eqn.c,v 1.83 2018/12/14 06:33:14 schwarze Exp $ */
/*
* Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -30,8 +30,9 @@
#include "mandoc_aux.h"
#include "mandoc.h"
#include "roff.h"
#include "eqn.h"
#include "libmandoc.h"
#include "libroff.h"
#include "eqn_parse.h"
#define EQN_NEST_MAX 128 /* maximum nesting of defines */
#define STRNEQ(p1, sz1, p2, sz2) \
@ -284,6 +285,13 @@ enum parse_mode {
MODE_TOK
};
struct eqn_def {
char *key;
size_t keysz;
char *val;
size_t valsz;
};
static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *);
static struct eqn_box *eqn_box_makebinary(struct eqn_node *,
struct eqn_box *);
@ -295,12 +303,11 @@ static void eqn_undef(struct eqn_node *);
struct eqn_node *
eqn_alloc(struct mparse *parse)
eqn_alloc(void)
{
struct eqn_node *ep;
ep = mandoc_calloc(1, sizeof(*ep));
ep->parse = parse;
ep->gsize = EQN_DEFSIZE;
return ep;
}
@ -399,7 +406,7 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode)
ep->end = strchr(ep->start + 1, *ep->start);
ep->start++; /* Skip opening quote. */
if (ep->end == NULL) {
mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse,
mandoc_msg(MANDOCERR_ARG_QUOTE,
ep->node->line, ep->node->pos, NULL);
ep->end = strchr(ep->start, '\0');
}
@ -420,7 +427,7 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode)
if ((def = eqn_def_find(ep)) == NULL)
break;
if (++lim > EQN_NEST_MAX) {
mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse,
mandoc_msg(MANDOCERR_ROFFLOOP,
ep->node->line, ep->node->pos, NULL);
return EQN_TOK_EOF;
}
@ -468,6 +475,8 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode)
void
eqn_box_free(struct eqn_box *bp)
{
if (bp == NULL)
return;
if (bp->first)
eqn_box_free(bp->first);
@ -482,6 +491,16 @@ eqn_box_free(struct eqn_box *bp)
free(bp);
}
struct eqn_box *
eqn_box_new(void)
{
struct eqn_box *bp;
bp = mandoc_calloc(1, sizeof(*bp));
bp->expectargs = UINT_MAX;
return bp;
}
/*
* Allocate a box as the last child of the parent node.
*/
@ -490,10 +509,9 @@ eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
{
struct eqn_box *bp;
bp = mandoc_calloc(1, sizeof(struct eqn_box));
bp = eqn_box_new();
bp->parent = parent;
bp->parent->args++;
bp->expectargs = UINT_MAX;
bp->font = bp->parent->font;
bp->size = ep->gsize;
@ -542,7 +560,7 @@ static void
eqn_delim(struct eqn_node *ep)
{
if (ep->end[0] == '\0' || ep->end[1] == '\0') {
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
mandoc_msg(MANDOCERR_REQ_EMPTY,
ep->node->line, ep->node->pos, "delim");
if (ep->end[0] != '\0')
ep->end++;
@ -569,7 +587,7 @@ eqn_undef(struct eqn_node *ep)
struct eqn_def *def;
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
mandoc_msg(MANDOCERR_REQ_EMPTY,
ep->node->line, ep->node->pos, "undef");
return;
}
@ -588,7 +606,7 @@ eqn_def(struct eqn_node *ep)
int i;
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
mandoc_msg(MANDOCERR_REQ_EMPTY,
ep->node->line, ep->node->pos, "define");
return;
}
@ -617,7 +635,7 @@ eqn_def(struct eqn_node *ep)
}
if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse,
mandoc_msg(MANDOCERR_REQ_EMPTY,
ep->node->line, ep->node->pos, "define %s", def->key);
free(def->key);
free(def->val);
@ -666,7 +684,7 @@ eqn_parse(struct eqn_node *ep)
case EQN_TOK_TDEFINE:
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
mandoc_msg(MANDOCERR_REQ_EMPTY,
ep->node->line, ep->node->pos, "tdefine");
break;
case EQN_TOK_DELIM:
@ -674,8 +692,8 @@ eqn_parse(struct eqn_node *ep)
break;
case EQN_TOK_GFONT:
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
break;
case EQN_TOK_MARK:
case EQN_TOK_LINEUP:
@ -690,8 +708,8 @@ eqn_parse(struct eqn_node *ep)
case EQN_TOK_DOT:
case EQN_TOK_DOTDOT:
if (parent->last == NULL) {
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
cur = eqn_box_alloc(ep, parent);
cur->type = EQN_TEXT;
cur->text = mandoc_strdup("");
@ -735,8 +753,8 @@ eqn_parse(struct eqn_node *ep)
case EQN_TOK_DOWN:
case EQN_TOK_UP:
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
break;
case EQN_TOK_FAT:
case EQN_TOK_ROMAN:
@ -773,14 +791,14 @@ eqn_parse(struct eqn_node *ep)
case EQN_TOK_GSIZE:
/* Accept two values: integral size and a single. */
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
break;
}
size = mandoc_strntoi(ep->start, ep->toksz, 10);
if (-1 == size) {
mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
break;
}
if (EQN_TOK_GSIZE == tok) {
@ -804,8 +822,8 @@ eqn_parse(struct eqn_node *ep)
* and keep on reading.
*/
if (parent->last == NULL) {
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
cur = eqn_box_alloc(ep, parent);
cur->type = EQN_TEXT;
cur->text = mandoc_strdup("");
@ -871,8 +889,8 @@ eqn_parse(struct eqn_node *ep)
* rebalance and continue reading.
*/
if (parent->last == NULL) {
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
cur = eqn_box_alloc(ep, parent);
cur->type = EQN_TEXT;
cur->text = mandoc_strdup("");
@ -898,16 +916,16 @@ eqn_parse(struct eqn_node *ep)
cur->left != NULL))
break;
if (cur == NULL) {
mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
break;
}
parent = cur;
if (EQN_TOK_RIGHT == tok) {
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
mandoc_msg(MANDOCERR_REQ_EMPTY,
ep->parse, ep->node->line,
ep->node->pos, eqn_toks[tok]);
ep->node->line, ep->node->pos,
"%s", eqn_toks[tok]);
break;
}
/* Handling depends on right/left. */
@ -941,8 +959,8 @@ eqn_parse(struct eqn_node *ep)
parent = parent->parent;
if (EQN_TOK_LEFT == tok &&
eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
break;
}
parent = eqn_box_alloc(ep, parent);
@ -975,8 +993,8 @@ eqn_parse(struct eqn_node *ep)
if (cur->type == EQN_PILE)
break;
if (cur == NULL) {
mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,
ep->node->line, ep->node->pos, eqn_toks[tok]);
mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line,
ep->node->pos, "%s", eqn_toks[tok]);
break;
}
parent = eqn_box_alloc(ep, cur);
@ -1092,6 +1110,9 @@ eqn_free(struct eqn_node *p)
{
int i;
if (p == NULL)
return;
for (i = 0; i < (int)p->defsz; i++) {
free(p->defs[i].key);
free(p->defs[i].val);

72
eqn.h Normal file
View File

@ -0,0 +1,72 @@
/* $Id: eqn.h,v 1.1 2018/12/13 05:23:38 schwarze Exp $ */
/*
* Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Public data types for eqn(7) syntax trees.
*/
enum eqn_boxt {
EQN_TEXT, /* Text, e.g. number, variable, operator, ... */
EQN_SUBEXPR, /* Nested eqn(7) subexpression. */
EQN_LIST, /* List, for example in braces. */
EQN_PILE, /* Vertical pile. */
EQN_MATRIX /* List of columns. */
};
enum eqn_fontt {
EQNFONT_NONE = 0,
EQNFONT_ROMAN,
EQNFONT_BOLD,
EQNFONT_FAT,
EQNFONT_ITALIC,
EQNFONT__MAX
};
enum eqn_post {
EQNPOS_NONE = 0,
EQNPOS_SUP,
EQNPOS_SUBSUP,
EQNPOS_SUB,
EQNPOS_TO,
EQNPOS_FROM,
EQNPOS_FROMTO,
EQNPOS_OVER,
EQNPOS_SQRT,
EQNPOS__MAX
};
/*
* A "box" is a parsed mathematical expression as defined by the eqn.7
* grammar.
*/
struct eqn_box {
struct eqn_box *parent;
struct eqn_box *prev;
struct eqn_box *next;
struct eqn_box *first; /* First child node. */
struct eqn_box *last; /* Last child node. */
char *text; /* Text (or NULL). */
char *left; /* Left-hand fence. */
char *right; /* Right-hand fence. */
char *top; /* Symbol above. */
char *bottom; /* Symbol below. */
size_t expectargs; /* Maximal number of arguments. */
size_t args; /* Actual number of arguments. */
int size; /* Font size. */
#define EQN_DEFSIZE INT_MIN
enum eqn_boxt type; /* Type of node. */
enum eqn_fontt font; /* Font in this box. */
enum eqn_post pos; /* Position of the next box. */
};

View File

@ -1,4 +1,4 @@
/* $Id: eqn_html.c,v 1.17 2017/07/14 13:32:35 schwarze Exp $ */
/* $Id: eqn_html.c,v 1.18 2018/12/13 05:23:38 schwarze Exp $ */
/*
* Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
@ -26,6 +26,7 @@
#include <string.h>
#include "mandoc.h"
#include "eqn.h"
#include "out.h"
#include "html.h"

48
eqn_parse.h Normal file
View File

@ -0,0 +1,48 @@
/* $Id: eqn_parse.h,v 1.3 2018/12/14 06:33:14 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* External interface of the eqn(7) parser.
* For use in the roff(7) and eqn(7) parsers only.
*/
struct roff_node;
struct eqn_box;
struct eqn_def;
struct eqn_node {
struct roff_node *node; /* Syntax tree of this equation. */
struct eqn_def *defs; /* Array of definitions. */
char *data; /* Source code of this equation. */
char *start; /* First byte of the current token. */
char *end; /* First byte of the next token. */
size_t defsz; /* Number of definitions. */
size_t sz; /* Length of the source code. */
size_t toksz; /* Length of the current token. */
int gsize; /* Default point size. */
int delim; /* In-line delimiters enabled. */
char odelim; /* In-line opening delimiter. */
char cdelim; /* In-line closing delimiter. */
};
struct eqn_node *eqn_alloc(void);
struct eqn_box *eqn_box_new(void);
void eqn_box_free(struct eqn_box *);
void eqn_free(struct eqn_node *);
void eqn_parse(struct eqn_node *);
void eqn_read(struct eqn_node *, const char *);
void eqn_reset(struct eqn_node *);

View File

@ -1,4 +1,4 @@
/* $Id: eqn_term.c,v 1.17 2017/08/23 21:56:20 schwarze Exp $ */
/* $Id: eqn_term.c,v 1.19 2018/12/13 05:23:38 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
@ -25,7 +25,7 @@
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "eqn.h"
#include "out.h"
#include "term.h"
@ -106,7 +106,7 @@ eqn_box(struct termp *p, const struct eqn_box *bp)
/* Special box types. */
if (bp->pos == EQNPOS_SQRT) {
term_word(p, "sqrt");
term_word(p, "\\(sr");
if (bp->first != NULL) {
p->flags |= TERMP_NOSPACE;
eqn_box(p, bp->first);

4
gmdiff
View File

@ -36,7 +36,7 @@ elif [ "X$1" = "X-u" ]; then
MOPT="-Ios=OpenBSD -Wall -Tutf8 $MOPT"
COLPIPE="cat"
else
ROFF="groff -et -ww -mtty-char -Tascii -P -c"
ROFF="groff -ket -ww -mtty-char -Tascii -P -c"
MOPT="-Ios=OpenBSD -Wall -Tascii $MOPT"
COLPIPE="cat"
fi
@ -51,7 +51,7 @@ while [ -n "$1" ]; do
for i in roff mandoc; do
[ -s /tmp/$i.err ] && echo "$i errors:" && cat /tmp/$i.err
done
diff -au /tmp/roff.out /tmp/mandoc.out 2>&1
diff -au $DIFFOPT /tmp/roff.out /tmp/mandoc.out 2>&1
done
exit 0

275
html.c
View File

@ -1,7 +1,7 @@
/* $Id: html.c,v 1.238 2018/06/25 16:54:59 schwarze Exp $ */
/* $Id: html.c,v 1.254 2019/03/03 13:02:11 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -18,6 +18,7 @@
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <ctype.h>
@ -62,6 +63,7 @@ static const struct htmldata htmltags[TAG_MAX] = {
{"title", HTML_NLAROUND},
{"div", HTML_NLAROUND},
{"div", 0},
{"section", HTML_NLALL},
{"h1", HTML_NLAROUND},
{"h2", HTML_NLAROUND},
{"span", 0},
@ -77,6 +79,7 @@ static const struct htmldata htmltags[TAG_MAX] = {
{"dl", HTML_NLALL | HTML_INDENT},
{"dt", HTML_NLAROUND},
{"dd", HTML_NLAROUND | HTML_INDENT},
{"p", HTML_NLAROUND | HTML_INDENT},
{"pre", HTML_NLALL | HTML_NOINDENT},
{"var", 0},
{"cite", 0},
@ -107,6 +110,7 @@ static const struct htmldata htmltags[TAG_MAX] = {
/* Avoid duplicate HTML id= attributes. */
static struct ohash id_unique;
static void html_reset_internal(struct html *);
static void print_byte(struct html *, char);
static void print_endword(struct html *);
static void print_indent(struct html *);
@ -116,7 +120,6 @@ static void print_ctag(struct html *, struct tag *);
static int print_escape(struct html *, char);
static int print_encode(struct html *, const char *, const char *, int);
static void print_href(struct html *, const char *, const char *, int);
static void print_metaf(struct html *, enum mandoc_esc);
void *
@ -128,31 +131,32 @@ html_alloc(const struct manoutput *outopts)
h->tag = NULL;
h->style = outopts->style;
h->base_man = outopts->man;
if ((h->base_man1 = outopts->man) == NULL)
h->base_man2 = NULL;
else if ((h->base_man2 = strchr(h->base_man1, ';')) != NULL)
*h->base_man2++ = '\0';
h->base_includes = outopts->includes;
if (outopts->fragment)
h->oflags |= HTML_FRAGMENT;
if (outopts->toc)
h->oflags |= HTML_TOC;
mandoc_ohash_init(&id_unique, 4, 0);
return h;
}
void
html_free(void *p)
static void
html_reset_internal(struct html *h)
{
struct tag *tag;
struct html *h;
char *cp;
unsigned int slot;
h = (struct html *)p;
while ((tag = h->tag) != NULL) {
h->tag = tag->next;
free(tag);
}
free(h);
cp = ohash_first(&id_unique, &slot);
while (cp != NULL) {
free(cp);
@ -161,6 +165,20 @@ html_free(void *p)
ohash_delete(&id_unique);
}
void
html_reset(void *p)
{
html_reset_internal(p);
mandoc_ohash_init(&id_unique, 4, 0);
}
void
html_free(void *p)
{
html_reset_internal(p);
free(p);
}
void
print_gen_head(struct html *h)
{
@ -204,7 +222,7 @@ print_gen_head(struct html *h)
print_tagq(h, t);
}
static void
void
print_metaf(struct html *h, enum mandoc_esc deco)
{
enum htmlfont font;
@ -222,12 +240,15 @@ print_metaf(struct html *h, enum mandoc_esc deco)
case ESCAPE_FONTBI:
font = HTMLFONT_BI;
break;
case ESCAPE_FONTCW:
font = HTMLFONT_CW;
break;
case ESCAPE_FONT:
case ESCAPE_FONTROMAN:
font = HTMLFONT_NONE;
break;
default:
abort();
return;
}
if (h->metaf) {
@ -249,11 +270,69 @@ print_metaf(struct html *h, enum mandoc_esc deco)
h->metaf = print_otag(h, TAG_B, "");
print_otag(h, TAG_I, "");
break;
case HTMLFONT_CW:
h->metaf = print_otag(h, TAG_SPAN, "c", "Li");
break;
default:
break;
}
}
void
html_close_paragraph(struct html *h)
{
struct tag *t;
for (t = h->tag; t != NULL && t->closed == 0; t = t->next) {
switch(t->tag) {
case TAG_P:
case TAG_PRE:
print_tagq(h, t);
break;
case TAG_A:
print_tagq(h, t);
continue;
default:
continue;
}
break;
}
}
/*
* ROFF_nf switches to no-fill mode, ROFF_fi to fill mode.
* TOKEN_NONE does not switch. The old mode is returned.
*/
enum roff_tok
html_fillmode(struct html *h, enum roff_tok want)
{
struct tag *t;
enum roff_tok had;
for (t = h->tag; t != NULL; t = t->next)
if (t->tag == TAG_PRE)
break;
had = t == NULL ? ROFF_fi : ROFF_nf;
if (want != had) {
switch (want) {
case ROFF_fi:
print_tagq(h, t);
break;
case ROFF_nf:
html_close_paragraph(h);
print_otag(h, TAG_PRE, "");
break;
case TOKEN_NONE:
break;
default:
abort();
}
}
return had;
}
char *
html_make_id(const struct roff_node *n, int unique)
{
@ -345,7 +424,6 @@ static int
print_encode(struct html *h, const char *p, const char *pend, int norecurse)
{
char numbuf[16];
struct tag *t;
const char *seq;
size_t sz;
int c, len, breakline, nospace;
@ -371,9 +449,7 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse)
if (breakline &&
(p >= pend || *p == ' ' || *p == ASCII_NBRSP)) {
t = print_otag(h, TAG_DIV, "");
print_text(h, "\\~");
print_tagq(h, t);
print_otag(h, TAG_BR, "");
breakline = 0;
while (p < pend && (*p == ' ' || *p == ASCII_NBRSP))
p++;
@ -393,22 +469,25 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse)
continue;
esc = mandoc_escape(&p, &seq, &len);
if (ESCAPE_ERROR == esc)
break;
switch (esc) {
case ESCAPE_FONT:
case ESCAPE_FONTPREV:
case ESCAPE_FONTBOLD:
case ESCAPE_FONTITALIC:
case ESCAPE_FONTBI:
case ESCAPE_FONTCW:
case ESCAPE_FONTROMAN:
if (0 == norecurse)
if (0 == norecurse) {
h->flags |= HTML_NOSPACE;
print_metaf(h, esc);
h->flags &= ~HTML_NOSPACE;
}
continue;
case ESCAPE_SKIPCHAR:
h->flags |= HTML_SKIPCHAR;
continue;
case ESCAPE_ERROR:
continue;
default:
break;
}
@ -433,6 +512,12 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse)
if (c <= 0)
continue;
break;
case ESCAPE_UNDEF:
c = *seq;
break;
case ESCAPE_DEVICE:
print_word(h, "html");
continue;
case ESCAPE_BREAK:
breakline = 1;
continue;
@ -464,9 +549,21 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse)
static void
print_href(struct html *h, const char *name, const char *sec, int man)
{
struct stat sb;
const char *p, *pp;
char *filename;
if (man) {
pp = h->base_man1;
if (h->base_man2 != NULL) {
mandoc_asprintf(&filename, "%s.%s", name, sec);
if (stat(filename, &sb) == -1)
pp = h->base_man2;
free(filename);
}
} else
pp = h->base_includes;
pp = man ? h->base_man : h->base_includes;
while ((p = strchr(pp, '%')) != NULL) {
print_encode(h, pp, p, 1);
if (man && p[1] == 'S') {
@ -492,7 +589,7 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
struct tag *t;
const char *attr;
char *arg1, *arg2;
int tflags;
int style_written, tflags;
tflags = htmltags[tag].flags;
@ -502,6 +599,8 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
t = mandoc_malloc(sizeof(struct tag));
t->tag = tag;
t->next = h->tag;
t->refcnt = 0;
t->closed = 0;
h->tag = t;
} else
t = NULL;
@ -532,7 +631,7 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
va_start(ap, fmt);
while (*fmt != '\0') {
while (*fmt != '\0' && *fmt != 's') {
/* Parse attributes and arguments. */
@ -548,10 +647,6 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
case 'i':
attr = "id";
break;
case 's':
attr = "style";
arg2 = va_arg(ap, char *);
break;
case '?':
attr = arg1;
arg1 = va_arg(ap, char *);
@ -584,26 +679,33 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
print_encode(h, arg1, NULL, 1);
fmt++;
break;
case 'T':
print_encode(h, arg1, NULL, 1);
print_word(h, "\" title=\"");
print_encode(h, arg1, NULL, 1);
fmt++;
break;
default:
if (arg2 == NULL)
print_encode(h, arg1, NULL, 1);
else {
print_word(h, arg1);
print_byte(h, ':');
print_byte(h, ' ');
print_word(h, arg2);
print_byte(h, ';');
}
print_encode(h, arg1, NULL, 1);
break;
}
print_byte(h, '"');
}
style_written = 0;
while (*fmt++ == 's') {
arg1 = va_arg(ap, char *);
arg2 = va_arg(ap, char *);
if (arg2 == NULL)
continue;
print_byte(h, ' ');
if (style_written == 0) {
print_word(h, "style=\"");
style_written = 1;
}
print_word(h, arg1);
print_byte(h, ':');
print_byte(h, ' ');
print_word(h, arg2);
print_byte(h, ';');
}
if (style_written)
print_byte(h, '"');
va_end(ap);
/* Accommodate for "well-formed" singleton escaping. */
@ -631,33 +733,32 @@ print_ctag(struct html *h, struct tag *tag)
{
int tflags;
/*
* Remember to close out and nullify the current
* meta-font and table, if applicable.
*/
if (tag == h->metaf)
h->metaf = NULL;
if (tag == h->tblt)
h->tblt = NULL;
if (tag->closed == 0) {
tag->closed = 1;
if (tag == h->metaf)
h->metaf = NULL;
if (tag == h->tblt)
h->tblt = NULL;
tflags = htmltags[tag->tag].flags;
if (tflags & HTML_INDENT)
h->indent--;
if (tflags & HTML_NOINDENT)
h->noindent--;
if (tflags & HTML_NLEND)
print_endline(h);
print_indent(h);
print_byte(h, '<');
print_byte(h, '/');
print_word(h, htmltags[tag->tag].name);
print_byte(h, '>');
if (tflags & HTML_NLAFTER)
print_endline(h);
h->tag = tag->next;
free(tag);
tflags = htmltags[tag->tag].flags;
if (tflags & HTML_INDENT)
h->indent--;
if (tflags & HTML_NOINDENT)
h->noindent--;
if (tflags & HTML_NLEND)
print_endline(h);
print_indent(h);
print_byte(h, '<');
print_byte(h, '/');
print_word(h, htmltags[tag->tag].name);
print_byte(h, '>');
if (tflags & HTML_NLAFTER)
print_endline(h);
}
if (tag->refcnt == 0) {
h->tag = tag->next;
free(tag);
}
}
void
@ -717,6 +818,9 @@ print_text(struct html *h, const char *word)
h->metaf = print_otag(h, TAG_B, "");
print_otag(h, TAG_I, "");
break;
case HTMLFONT_CW:
h->metaf = print_otag(h, TAG_SPAN, "c", "Li");
break;
default:
print_indent(h);
break;
@ -741,36 +845,33 @@ print_text(struct html *h, const char *word)
void
print_tagq(struct html *h, const struct tag *until)
{
struct tag *tag;
struct tag *this, *next;
while ((tag = h->tag) != NULL) {
print_ctag(h, tag);
if (until && tag == until)
return;
for (this = h->tag; this != NULL; this = next) {
next = this == until ? NULL : this->next;
print_ctag(h, this);
}
}
/*
* Close out all open elements up to but excluding suntil.
* Note that a paragraph just inside stays open together with it
* because paragraphs include subsequent phrasing content.
*/
void
print_stagq(struct html *h, const struct tag *suntil)
{
struct tag *tag;
struct tag *this, *next;
while ((tag = h->tag) != NULL) {
if (suntil && tag == suntil)
return;
print_ctag(h, tag);
for (this = h->tag; this != NULL; this = next) {
next = this->next;
if (this == suntil || (next == suntil &&
(this->tag == TAG_P || this->tag == TAG_PRE)))
break;
print_ctag(h, this);
}
}
void
print_paragraph(struct html *h)
{
struct tag *t;
t = print_otag(h, TAG_DIV, "c", "Pp");
print_tagq(h, t);
}
/***********************************************************************
* Low level output functions.

19
html.h
View File

@ -1,7 +1,7 @@
/* $Id: html.h,v 1.92 2018/06/25 16:54:59 schwarze Exp $ */
/* $Id: html.h,v 1.102 2019/03/01 10:57:18 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2017, 2018, 2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -24,6 +24,7 @@ enum htmltag {
TAG_TITLE,
TAG_DIV,
TAG_IDIV,
TAG_SECTION,
TAG_H1,
TAG_H2,
TAG_SPAN,
@ -39,6 +40,7 @@ enum htmltag {
TAG_DL,
TAG_DT,
TAG_DD,
TAG_P,
TAG_PRE,
TAG_VAR,
TAG_CITE,
@ -72,11 +74,14 @@ enum htmlfont {
HTMLFONT_BOLD,
HTMLFONT_ITALIC,
HTMLFONT_BI,
HTMLFONT_CW,
HTMLFONT_MAX
};
struct tag {
struct tag *next;
int refcnt;
int closed;
enum htmltag tag;
};
@ -87,12 +92,12 @@ struct html {
#define HTML_KEEP (1 << 2)
#define HTML_PREKEEP (1 << 3)
#define HTML_NONOSPACE (1 << 4) /* never add spaces */
#define HTML_LITERAL (1 << 5) /* literal (e.g., <PRE>) context */
#define HTML_SKIPCHAR (1 << 6) /* skip the next character */
#define HTML_NOSPLIT (1 << 7) /* do not break line before .An */
#define HTML_SPLIT (1 << 8) /* break line before .An */
#define HTML_NONEWLINE (1 << 9) /* No line break in nofill mode. */
#define HTML_BUFFER (1 << 10) /* Collect a word to see if it fits. */
#define HTML_TOCDONE (1 << 11) /* The TOC was already written. */
size_t indent; /* current output indentation level */
int noindent; /* indent disabled by <pre> */
size_t col; /* current output byte position */
@ -101,7 +106,8 @@ struct html {
struct tag *tag; /* last open tag */
struct rofftbl tbl; /* current table */
struct tag *tblt; /* current open table scope */
char *base_man; /* base for manpage href */
char *base_man1; /* bases for manpage href */
char *base_man2;
char *base_includes; /* base for include href */
char *style; /* style-sheet URI */
struct tag *metaf; /* current open font scope */
@ -109,6 +115,7 @@ struct html {
enum htmlfont metac; /* current font mode */
int oflags; /* output options */
#define HTML_FRAGMENT (1 << 0) /* don't emit HTML/HEAD/BODY */
#define HTML_TOC (1 << 1) /* emit a table of contents */
};
@ -121,6 +128,7 @@ void roff_html_pre(struct html *, const struct roff_node *);
void print_gen_comment(struct html *, struct roff_node *);
void print_gen_decls(struct html *);
void print_gen_head(struct html *);
void print_metaf(struct html *, enum mandoc_esc);
struct tag *print_otag(struct html *, enum htmltag, const char *, ...);
void print_tagq(struct html *, const struct tag *);
void print_stagq(struct html *, const struct tag *);
@ -128,7 +136,8 @@ void print_text(struct html *, const char *);
void print_tblclose(struct html *);
void print_tbl(struct html *, const struct tbl_span *);
void print_eqn(struct html *, const struct eqn_box *);
void print_paragraph(struct html *);
void print_endline(struct html *);
void html_close_paragraph(struct html *);
enum roff_tok html_fillmode(struct html *, enum roff_tok);
char *html_make_id(const struct roff_node *, int);

5
lib.c
View File

@ -1,4 +1,4 @@
/* $Id: lib.c,v 1.14 2017/06/24 14:38:32 schwarze Exp $ */
/* $Id: lib.c,v 1.15 2018/12/13 11:55:46 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -17,12 +17,9 @@
#include "config.h"
#include <sys/types.h>
#include <string.h>
#include "mandoc.h"
#include "roff.h"
#include "mdoc.h"
#include "libmdoc.h"
#define LINE(x, y) \

3
lib.in
View File

@ -1,4 +1,4 @@
/* $Id: lib.in,v 1.20 2017/08/20 02:30:27 schwarze Exp $ */
/* $Id: lib.in,v 1.21 2019/03/04 17:35:21 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2009, 2012 Joerg Sonnenberger <joerg@netbsd.org>
@ -29,6 +29,7 @@ LINE("libalias", "Packet Aliasing Library (libalias, \\-lalias)")
LINE("libarchive", "Streaming Archive Library (libarchive, \\-larchive)")
LINE("libarm", "ARM Architecture Library (libarm, \\-larm)")
LINE("libarm32", "ARM32 Architecture Library (libarm32, \\-larm32)")
LINE("libbe", "Boot Environment Library (libbe, \\-lbe)")
LINE("libbluetooth", "Bluetooth Library (libbluetooth, \\-lbluetooth)")
LINE("libbsdxml", "eXpat XML parser library (libbsdxml, \\-lbsdxml)")
LINE("libbsm", "Basic Security Module Library (libbsm, \\-lbsm)")

View File

@ -1,7 +1,7 @@
/* $Id: libman.h,v 1.81 2017/04/29 12:45:41 schwarze Exp $ */
/* $Id: libman.h,v 1.86 2018/12/31 10:04:39 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2014, 2015, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -16,6 +16,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct roff_node;
struct roff_man;
#define MACRO_PROT_ARGS struct roff_man *man, \
enum roff_tok tok, \
int line, \
@ -26,15 +29,14 @@
struct man_macro {
void (*fp)(MACRO_PROT_ARGS);
int flags;
#define MAN_SCOPED (1 << 0) /* Optional next-line scope. */
#define MAN_NSCOPED (1 << 1) /* Allowed in next-line element scope. */
#define MAN_BSCOPE (1 << 2) /* Break next-line block scope. */
#define MAN_JOIN (1 << 3) /* Join arguments together. */
#define MAN_BSCOPED (1 << 0) /* Optional next-line block scope. */
#define MAN_ESCOPED (1 << 1) /* Optional next-line element scope. */
#define MAN_NSCOPED (1 << 2) /* Allowed in next-line element scope. */
#define MAN_XSCOPE (1 << 3) /* Exit next-line block scope. */
#define MAN_JOIN (1 << 4) /* Join arguments together. */
};
extern const struct man_macro *const man_macros;
const struct man_macro *man_macro(enum roff_tok);
void man_node_validate(struct roff_man *);
void man_state(struct roff_man *, struct roff_node *);
void man_descope(struct roff_man *, int, int, char *);
void man_unscope(struct roff_man *, const struct roff_node *);

View File

@ -1,7 +1,7 @@
/* $Id: libmandoc.h,v 1.71 2018/04/09 22:27:04 schwarze Exp $ */
/* $Id: libmandoc.h,v 1.77 2018/12/21 17:15:18 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -16,31 +16,38 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
enum rofferr {
ROFF_CONT, /* continue processing line */
ROFF_RERUN, /* re-run roff interpreter with offset */
ROFF_APPEND, /* re-run main parser, appending next line */
ROFF_REPARSE, /* re-run main parser on the result */
ROFF_SO, /* include another file */
ROFF_IGN, /* ignore current line */
};
/*
* Return codes passed from the roff parser to the main parser.
*/
/* Main instruction: what to do with the returned line. */
#define ROFF_IGN 0x000 /* Don't do anything with it. */
#define ROFF_CONT 0x001 /* Give it to the high-level parser. */
#define ROFF_RERUN 0x002 /* Re-run the roff parser with an offset. */
#define ROFF_REPARSE 0x004 /* Recursively run the main parser on it. */
#define ROFF_SO 0x008 /* Include the named file. */
#define ROFF_MASK 0x00f /* Only one of these bits should be set. */
/* Options for further parsing, to be OR'ed with the above. */
#define ROFF_APPEND 0x010 /* Append the next line to this one. */
#define ROFF_USERCALL 0x020 /* Start execution of a new macro. */
#define ROFF_USERRET 0x040 /* Abort execution of the current macro. */
#define ROFF_WHILE 0x100 /* Start a new .while loop. */
#define ROFF_LOOPCONT 0x200 /* Iterate the current .while loop. */
#define ROFF_LOOPEXIT 0x400 /* Exit the current .while loop. */
#define ROFF_LOOPMASK 0xf00
struct buf {
char *buf;
size_t sz;
char *buf;
size_t sz;
struct buf *next;
};
struct mparse;
struct roff;
struct roff_man;
void mandoc_msg(enum mandocerr, struct mparse *,
int, int, const char *);
void mandoc_vmsg(enum mandocerr, struct mparse *,
int, int, const char *, ...)
__attribute__((__format__ (__printf__, 5, 6)));
char *mandoc_getarg(struct mparse *, char **, int, int *);
char *mandoc_normdate(struct roff_man *, char *, int, int);
int mandoc_eos(const char *, size_t);
int mandoc_strntoi(const char *, size_t, int);
@ -57,17 +64,18 @@ int preconv_encode(const struct buf *, size_t *,
struct buf *, size_t *, int *);
void roff_free(struct roff *);
struct roff *roff_alloc(struct mparse *, int);
struct roff *roff_alloc(int);
void roff_reset(struct roff *);
void roff_man_free(struct roff_man *);
struct roff_man *roff_man_alloc(struct roff *, struct mparse *,
const char *, int);
struct roff_man *roff_man_alloc(struct roff *, const char *, int);
void roff_man_reset(struct roff_man *);
enum rofferr roff_parseln(struct roff *, int, struct buf *, int *);
int roff_parseln(struct roff *, int, struct buf *, int *);
void roff_userret(struct roff *);
void roff_endparse(struct roff *);
void roff_setreg(struct roff *, const char *, int, char sign);
int roff_getreg(struct roff *, const char *);
char *roff_strdup(const struct roff *, const char *);
char *roff_getarg(struct roff *, char **, int, int *);
int roff_getcontrol(const struct roff *,
const char *, int *);
int roff_getformat(const struct roff *);

View File

@ -1,7 +1,7 @@
/* $Id: libmdoc.h,v 1.112 2017/05/30 16:22:03 schwarze Exp $ */
/* $Id: libmdoc.h,v 1.117 2018/12/31 04:55:46 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -16,6 +16,10 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct roff_node;
struct roff_man;
struct mdoc_arg;
#define MACRO_PROT_ARGS struct roff_man *mdoc, \
enum roff_tok tok, \
int line, \
@ -38,6 +42,7 @@ enum margserr {
ARGS_ERROR,
ARGS_EOLN, /* end-of-line */
ARGS_WORD, /* normal word */
ARGS_ALLOC, /* normal word from roff_getarg() */
ARGS_PUNCT, /* series of punctuation */
ARGS_PHRASE /* Bl -column phrase */
};
@ -59,10 +64,8 @@ enum mdelim {
DELIM_MAX
};
extern const struct mdoc_macro *const mdoc_macros;
const struct mdoc_macro *mdoc_macro(enum roff_tok);
void mdoc_macro(MACRO_PROT_ARGS);
void mdoc_elem_alloc(struct roff_man *, int, int,
enum roff_tok, struct mdoc_arg *);
struct roff_node *mdoc_block_alloc(struct roff_man *, int, int,
@ -71,10 +74,7 @@ void mdoc_tail_alloc(struct roff_man *, int, int,
enum roff_tok);
struct roff_node *mdoc_endbody_alloc(struct roff_man *, int, int,
enum roff_tok, struct roff_node *);
void mdoc_node_relink(struct roff_man *, struct roff_node *);
void mdoc_node_validate(struct roff_man *);
void mdoc_state(struct roff_man *, struct roff_node *);
void mdoc_state_reset(struct roff_man *);
const char *mdoc_a2arch(const char *);
const char *mdoc_a2att(const char *);
const char *mdoc_a2lib(const char *);

223
main.c
View File

@ -1,7 +1,7 @@
/* $Id: main.c,v 1.306 2018/05/14 14:10:23 schwarze Exp $ */
/* $Id: main.c,v 1.322 2019/03/06 10:18:58 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2012, 2014-2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010-2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@ -21,7 +21,6 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/param.h> /* MACHINE */
#include <sys/termios.h>
#include <sys/wait.h>
#include <assert.h>
@ -40,6 +39,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
@ -49,6 +49,7 @@
#include "roff.h"
#include "mdoc.h"
#include "man.h"
#include "mandoc_parse.h"
#include "tag.h"
#include "main.h"
#include "manconf.h"
@ -81,7 +82,6 @@ struct curparse {
void *outdata; /* data for output */
char *os_s; /* operating system for display */
int wstop; /* stop after a file with a warning */
enum mandocerr mmin; /* ignore messages below this */
enum mandoc_os os_e; /* check base system conventions */
enum outt outtype; /* which output to use */
};
@ -89,7 +89,7 @@ struct curparse {
int mandocdb(int, char *[]);
static void check_xr(const char *);
static void check_xr(void);
static int fs_lookup(const struct manpaths *,
size_t ipath, const char *,
const char *, const char *,
@ -99,8 +99,6 @@ static int fs_search(const struct mansearch *,
struct manpage **, size_t *);
static int koptions(int *, char *);
static void moptions(int *, char *);
static void mmsg(enum mandocerr, enum mandoclevel,
const char *, int, int, const char *);
static void outdata_alloc(struct curparse *);
static void parse(struct curparse *, int, const char *);
static void passthrough(const char *, int, int);
@ -112,8 +110,6 @@ static int woptions(struct curparse *, char *);
static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
static char help_arg[] = "help";
static char *help_argv[] = {help_arg, NULL};
static enum mandoclevel rc;
static FILE *mmsg_stream;
int
@ -127,7 +123,7 @@ main(int argc, char *argv[])
struct manpage *res, *resp;
const char *progname, *sec, *thisarg;
char *conf_file, *defpaths, *auxpaths;
char *oarg;
char *oarg, *tagarg;
unsigned char *uc;
size_t i, sz;
int prio, best_prio;
@ -152,6 +148,7 @@ main(int argc, char *argv[])
setprogname(progname);
#endif
mandoc_msg_setoutfile(stderr);
if (strncmp(progname, "mandocdb", 8) == 0 ||
strcmp(progname, BINM_MAKEWHATIS) == 0)
return mandocdb(argc, argv);
@ -191,10 +188,8 @@ main(int argc, char *argv[])
memset(&curp, 0, sizeof(struct curparse));
curp.outtype = OUTT_LOCALE;
curp.mmin = MANDOCERR_MAX;
curp.outopts = &conf.output;
options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1;
mmsg_stream = stderr;
use_pager = 1;
tag_files = NULL;
@ -314,6 +309,9 @@ main(int argc, char *argv[])
}
}
if (curp.outtype != OUTT_TREE || !curp.outopts->noval)
options |= MPARSE_VALIDATE;
if (outmode == OUTMODE_FLN ||
outmode == OUTMODE_LST ||
!isatty(STDOUT_FILENO))
@ -371,7 +369,16 @@ main(int argc, char *argv[])
#endif
}
rc = MANDOCLEVEL_OK;
/*
* Use the first argument for -O tag in addition to
* using it as a search term for man(1) or apropos(1).
*/
if (conf.output.tag != NULL && *conf.output.tag == '\0') {
tagarg = argc > 0 && search.argmode == ARG_EXPR ?
strchr(*argv, '=') : NULL;
conf.output.tag = tagarg == NULL ? *argv : tagarg + 1;
}
/* man(1), whatis(1), apropos(1) */
@ -405,7 +412,6 @@ main(int argc, char *argv[])
res[sz].names = NULL;
res[sz].output = NULL;
res[sz].ipath = SIZE_MAX;
res[sz].bits = 0;
res[sz].sec = 10;
res[sz].form = FORM_SRC;
sz++;
@ -415,7 +421,7 @@ main(int argc, char *argv[])
if (sz == 0) {
if (search.argmode != ARG_NAME)
warnx("nothing appropriate");
rc = MANDOCLEVEL_BADARG;
mandoc_msg_setrc(MANDOCLEVEL_BADARG);
goto out;
}
@ -483,19 +489,17 @@ main(int argc, char *argv[])
moptions(&options, auxpaths);
mchars_alloc();
curp.mp = mparse_alloc(options, curp.mmin, mmsg,
curp.os_e, curp.os_s);
/*
* Conditionally start up the lookaside buffer before parsing.
*/
if (OUTT_MAN == curp.outtype)
mparse_keep(curp.mp);
curp.mp = mparse_alloc(options, curp.os_e, curp.os_s);
if (argc < 1) {
if (use_pager)
if (use_pager) {
tag_files = tag_init();
parse(&curp, STDIN_FILENO, "<stdin>");
tag_files->tagname = conf.output.tag;
}
thisarg = "<stdin>";
mandoc_msg_setinfilename(thisarg);
parse(&curp, STDIN_FILENO, thisarg);
mandoc_msg_setinfilename(NULL);
}
/*
@ -519,22 +523,25 @@ main(int argc, char *argv[])
(void)chdir(conf.manpath.paths[resp->ipath]);
else if (startdir != -1)
(void)fchdir(startdir);
}
thisarg = resp->file;
} else
thisarg = *argv;
fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv);
fd = mparse_open(curp.mp, thisarg);
if (fd != -1) {
if (use_pager) {
tag_files = tag_init();
use_pager = 0;
tag_files = tag_init();
tag_files->tagname = conf.output.tag;
}
if (resp == NULL)
parse(&curp, fd, *argv);
else if (resp->form == FORM_SRC)
parse(&curp, fd, resp->file);
mandoc_msg_setinfilename(thisarg);
if (resp == NULL || resp->form == FORM_SRC)
parse(&curp, fd, thisarg);
else
passthrough(resp->file, fd,
conf.output.synopsisonly);
mandoc_msg_setinfilename(NULL);
if (ferror(stdout)) {
if (tag_files != NULL) {
@ -543,7 +550,7 @@ main(int argc, char *argv[])
tag_files = NULL;
} else
warn("stdout");
rc = MANDOCLEVEL_SYSERR;
mandoc_msg_setrc(MANDOCLEVEL_SYSERR);
break;
}
@ -552,10 +559,11 @@ main(int argc, char *argv[])
outdata_alloc(&curp);
terminal_sepline(curp.outdata);
}
} else if (rc < MANDOCLEVEL_ERROR)
rc = MANDOCLEVEL_ERROR;
} else
mandoc_msg(MANDOCERR_FILE, 0, 0,
"%s: %s", thisarg, strerror(errno));
if (MANDOCLEVEL_OK != rc && curp.wstop)
if (curp.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK)
break;
if (resp != NULL)
@ -646,7 +654,7 @@ main(int argc, char *argv[])
if (pid == -1) {
warn("wait");
rc = MANDOCLEVEL_SYSERR;
mandoc_msg_setrc(MANDOCLEVEL_SYSERR);
break;
}
if (!WIFSTOPPED(status))
@ -656,8 +664,7 @@ main(int argc, char *argv[])
}
tag_unlink();
}
return (int)rc;
return (int)mandoc_msg_getrc();
}
static void
@ -755,7 +762,6 @@ fs_lookup(const struct manpaths *paths, size_t ipath,
page->names = NULL;
page->output = NULL;
page->ipath = ipath;
page->bits = NAME_FILE & NAME_MASK;
page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10;
page->form = form;
return 1;
@ -790,8 +796,18 @@ fs_search(const struct mansearch *cfg, const struct manpaths *paths,
return 1;
}
if (res != NULL && *ressz == lastsz &&
strchr(*argv, '/') == NULL)
warnx("No entry for %s in the manual.", *argv);
strchr(*argv, '/') == NULL) {
if (cfg->arch != NULL &&
arch_valid(cfg->arch, OSENUM) == 0)
warnx("Unknown architecture \"%s\".",
cfg->arch);
else if (cfg->sec == NULL)
warnx("No entry for %s in the manual.",
*argv);
else
warnx("No entry for %s in section %s "
"of the manual.", *argv, cfg->sec);
}
lastsz = *ressz;
argv++;
argc--;
@ -802,101 +818,92 @@ fs_search(const struct mansearch *cfg, const struct manpaths *paths,
static void
parse(struct curparse *curp, int fd, const char *file)
{
enum mandoclevel rctmp;
struct roff_man *man;
struct roff_meta *meta;
/* Begin by parsing the file itself. */
assert(file);
assert(fd >= 0);
rctmp = mparse_readfd(curp->mp, fd, file);
mparse_readfd(curp->mp, fd, file);
if (fd != STDIN_FILENO)
close(fd);
if (rc < rctmp)
rc = rctmp;
/*
* With -Wstop and warnings or errors of at least the requested
* level, do not produce output.
*/
if (rctmp != MANDOCLEVEL_OK && curp->wstop)
if (curp->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK)
return;
if (curp->outdata == NULL)
outdata_alloc(curp);
else if (curp->outtype == OUTT_HTML)
html_reset(curp);
mparse_result(curp->mp, &man, NULL);
mandoc_xr_reset();
meta = mparse_result(curp->mp);
/* Execute the out device, if it exists. */
if (man == NULL)
return;
mandoc_xr_reset();
if (man->macroset == MACROSET_MDOC) {
if (curp->outtype != OUTT_TREE || !curp->outopts->noval)
mdoc_validate(man);
if (meta->macroset == MACROSET_MDOC) {
switch (curp->outtype) {
case OUTT_HTML:
html_mdoc(curp->outdata, man);
html_mdoc(curp->outdata, meta);
break;
case OUTT_TREE:
tree_mdoc(curp->outdata, man);
tree_mdoc(curp->outdata, meta);
break;
case OUTT_MAN:
man_mdoc(curp->outdata, man);
man_mdoc(curp->outdata, meta);
break;
case OUTT_PDF:
case OUTT_ASCII:
case OUTT_UTF8:
case OUTT_LOCALE:
case OUTT_PS:
terminal_mdoc(curp->outdata, man);
terminal_mdoc(curp->outdata, meta);
break;
case OUTT_MARKDOWN:
markdown_mdoc(curp->outdata, man);
markdown_mdoc(curp->outdata, meta);
break;
default:
break;
}
}
if (man->macroset == MACROSET_MAN) {
if (curp->outtype != OUTT_TREE || !curp->outopts->noval)
man_validate(man);
if (meta->macroset == MACROSET_MAN) {
switch (curp->outtype) {
case OUTT_HTML:
html_man(curp->outdata, man);
html_man(curp->outdata, meta);
break;
case OUTT_TREE:
tree_man(curp->outdata, man);
tree_man(curp->outdata, meta);
break;
case OUTT_MAN:
man_man(curp->outdata, man);
mparse_copy(curp->mp);
break;
case OUTT_PDF:
case OUTT_ASCII:
case OUTT_UTF8:
case OUTT_LOCALE:
case OUTT_PS:
terminal_man(curp->outdata, man);
terminal_man(curp->outdata, meta);
break;
default:
break;
}
}
if (curp->mmin < MANDOCERR_STYLE)
check_xr(file);
mparse_updaterc(curp->mp, &rc);
if (mandoc_msg_getmin() < MANDOCERR_STYLE)
check_xr();
}
static void
check_xr(const char *file)
check_xr(void)
{
static struct manpaths paths;
struct mansearch search;
struct mandoc_xr *xr;
char *cp;
size_t sz;
if (paths.sz == 0)
@ -915,13 +922,12 @@ check_xr(const char *file)
if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz))
continue;
if (xr->count == 1)
mandoc_asprintf(&cp, "Xr %s %s", xr->name, xr->sec);
mandoc_msg(MANDOCERR_XR_BAD, xr->line,
xr->pos + 1, "Xr %s %s", xr->name, xr->sec);
else
mandoc_asprintf(&cp, "Xr %s %s (%d times)",
mandoc_msg(MANDOCERR_XR_BAD, xr->line,
xr->pos + 1, "Xr %s %s (%d times)",
xr->name, xr->sec, xr->count);
mmsg(MANDOCERR_XR_BAD, MANDOCLEVEL_STYLE,
file, xr->line, xr->pos + 1, cp);
free(cp);
}
}
@ -1020,8 +1026,7 @@ passthrough(const char *file, int fd, int synopsis_only)
fail:
free(line);
warn("%s: SYSERR: %s", file, syscall);
if (rc < MANDOCLEVEL_SYSERR)
rc = MANDOCLEVEL_SYSERR;
mandoc_msg_setrc(MANDOCLEVEL_SYSERR);
}
static int
@ -1063,8 +1068,8 @@ toptions(struct curparse *curp, char *arg)
curp->outtype = OUTT_ASCII;
else if (0 == strcmp(arg, "lint")) {
curp->outtype = OUTT_LINT;
curp->mmin = MANDOCERR_BASE;
mmsg_stream = stdout;
mandoc_msg_setoutfile(stdout);
mandoc_msg_setmin(MANDOCERR_BASE);
} else if (0 == strcmp(arg, "tree"))
curp->outtype = OUTT_TREE;
else if (0 == strcmp(arg, "man"))
@ -1115,29 +1120,29 @@ woptions(struct curparse *curp, char *arg)
break;
case 1:
case 2:
curp->mmin = MANDOCERR_BASE;
mandoc_msg_setmin(MANDOCERR_BASE);
break;
case 3:
curp->mmin = MANDOCERR_STYLE;
mandoc_msg_setmin(MANDOCERR_STYLE);
break;
case 4:
curp->mmin = MANDOCERR_WARNING;
mandoc_msg_setmin(MANDOCERR_WARNING);
break;
case 5:
curp->mmin = MANDOCERR_ERROR;
mandoc_msg_setmin(MANDOCERR_ERROR);
break;
case 6:
curp->mmin = MANDOCERR_UNSUPP;
mandoc_msg_setmin(MANDOCERR_UNSUPP);
break;
case 7:
curp->mmin = MANDOCERR_MAX;
mandoc_msg_setmin(MANDOCERR_MAX);
break;
case 8:
curp->mmin = MANDOCERR_BASE;
mandoc_msg_setmin(MANDOCERR_BASE);
curp->os_e = MANDOC_OS_OPENBSD;
break;
case 9:
curp->mmin = MANDOCERR_BASE;
mandoc_msg_setmin(MANDOCERR_BASE);
curp->os_e = MANDOC_OS_NETBSD;
break;
default:
@ -1148,29 +1153,6 @@ woptions(struct curparse *curp, char *arg)
return 1;
}
static void
mmsg(enum mandocerr t, enum mandoclevel lvl,
const char *file, int line, int col, const char *msg)
{
const char *mparse_msg;
fprintf(mmsg_stream, "%s: %s:", getprogname(),
file == NULL ? "<stdin>" : file);
if (line)
fprintf(mmsg_stream, "%d:%d:", line, col + 1);
fprintf(mmsg_stream, " %s", mparse_strlevel(lvl));
if ((mparse_msg = mparse_strerror(t)) != NULL)
fprintf(mmsg_stream, ": %s", mparse_msg);
if (msg)
fprintf(mmsg_stream, ": %s", msg);
fputc('\n', mmsg_stream);
}
static pid_t
spawn_pager(struct tag_files *tag_files)
{
@ -1179,8 +1161,10 @@ spawn_pager(struct tag_files *tag_files)
char *argv[MAX_PAGER_ARGS];
const char *pager;
char *cp;
#if HAVE_LESS_T
size_t cmdlen;
int argc;
#endif
int argc, use_ofn;
pid_t pager_pid;
pager = getenv("MANPAGER");
@ -1196,7 +1180,7 @@ spawn_pager(struct tag_files *tag_files)
*/
argc = 0;
while (argc + 4 < MAX_PAGER_ARGS) {
while (argc + 5 < MAX_PAGER_ARGS) {
argv[argc++] = cp;
cp = strchr(cp, ' ');
if (cp == NULL)
@ -1210,14 +1194,23 @@ spawn_pager(struct tag_files *tag_files)
/* For less(1), use the tag file. */
use_ofn = 1;
#if HAVE_LESS_T
if ((cmdlen = strlen(argv[0])) >= 4) {
cp = argv[0] + cmdlen - 4;
if (strcmp(cp, "less") == 0) {
argv[argc++] = mandoc_strdup("-T");
argv[argc++] = tag_files->tfn;
if (tag_files->tagname != NULL) {
argv[argc++] = mandoc_strdup("-t");
argv[argc++] = tag_files->tagname;
use_ofn = 0;
}
}
}
argv[argc++] = tag_files->ofn;
#endif
if (use_ofn)
argv[argc++] = tag_files->ofn;
argv[argc] = NULL;
switch (pager_pid = fork()) {

24
main.h
View File

@ -1,7 +1,7 @@
/* $Id: main.h,v 1.27 2017/03/03 14:23:23 schwarze Exp $ */
/* $Id: main.h,v 1.30 2019/03/03 13:02:11 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2014, 2015, 2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -16,7 +16,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct roff_man;
struct roff_meta;
struct manoutput;
/*
@ -27,15 +27,15 @@ struct manoutput;
*/
void *html_alloc(const struct manoutput *);
void html_mdoc(void *, const struct roff_man *);
void html_man(void *, const struct roff_man *);
void html_mdoc(void *, const struct roff_meta *);
void html_man(void *, const struct roff_meta *);
void html_reset(void *);
void html_free(void *);
void tree_mdoc(void *, const struct roff_man *);
void tree_man(void *, const struct roff_man *);
void tree_mdoc(void *, const struct roff_meta *);
void tree_man(void *, const struct roff_meta *);
void man_mdoc(void *, const struct roff_man *);
void man_man(void *, const struct roff_man *);
void man_mdoc(void *, const struct roff_meta *);
void *locale_alloc(const struct manoutput *);
void *utf8_alloc(const struct manoutput *);
@ -46,8 +46,8 @@ void *pdf_alloc(const struct manoutput *);
void *ps_alloc(const struct manoutput *);
void pspdf_free(void *);
void terminal_mdoc(void *, const struct roff_man *);
void terminal_man(void *, const struct roff_man *);
void terminal_mdoc(void *, const struct roff_meta *);
void terminal_man(void *, const struct roff_meta *);
void terminal_sepline(void *);
void markdown_mdoc(void *, const struct roff_man *);
void markdown_mdoc(void *, const struct roff_meta *);

25
man.1
View File

@ -1,9 +1,9 @@
.\" $Id: man.1,v 1.33 2018/04/19 23:41:16 schwarze Exp $
.\" $Id: man.1,v 1.35 2019/03/09 15:55:01 schwarze Exp $
.\"
.\" Copyright (c) 1989, 1990, 1993
.\" The Regents of the University of California. All rights reserved.
.\" Copyright (c) 2003, 2007, 2008, 2014 Jason McIntyre <jmc@openbsd.org>
.\" Copyright (c) 2010, 2011, 2014-2017 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2010, 2011, 2014-2018 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@ -31,7 +31,7 @@
.\"
.\" @(#)man.1 8.2 (Berkeley) 1/2/94
.\"
.Dd $Mdocdate: April 19 2018 $
.Dd $Mdocdate: March 9 2019 $
.Dt MAN 1
.Os
.Sh NAME
@ -218,12 +218,24 @@ Guidelines for writing
man pages can be found in
.Xr mdoc 7 .
.Pp
The
.Xr mandoc.db 5
database is used for looking up manual page entries.
In cases where the database is absent, outdated, or corrupt,
.Nm
falls back to looking for files called
.Ar name . Ns Ar section .
If both a formatted and an unformatted version of the same manual page,
for example
.Pa cat1/foo.0
and
.Pa man1/foo.1 ,
exist in the same directory, only the unformatted version is used.
The database is kept up to date with
.Xr makewhatis 8 ,
which is run by the
.Xr weekly 8
maintenance script.
.Sh ENVIRONMENT
.Bl -tag -width MANPATHX
.It Ev MACHINE
@ -266,6 +278,13 @@ and
can be used to move to the next and to the previous place providing
information about the term last searched for with
.Ic :t .
The
.Fl O Cm tag Ns Op = Ns Ar term
option documented in the
.Xr mandoc 1
manual opens a manual page at the definition of a specific
.Ar term
rather than at the beginning.
.It Ev MANPATH
The standard search path used by
.Nm

770
man.7

File diff suppressed because it is too large Load Diff

170
man.c
View File

@ -1,7 +1,7 @@
/* $Id: man.c,v 1.176 2017/06/28 12:52:45 schwarze Exp $ */
/* $Id: man.c,v 1.187 2019/01/05 00:36:50 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@ -35,7 +35,7 @@
#include "roff_int.h"
#include "libman.h"
static void man_descope(struct roff_man *, int, int);
static char *man_hasc(char *);
static int man_ptext(struct roff_man *, int, char *, int);
static int man_pmacro(struct roff_man *, int, char *, int);
@ -52,38 +52,62 @@ man_parseln(struct roff_man *man, int ln, char *buf, int offs)
man_ptext(man, ln, buf, offs);
}
static void
man_descope(struct roff_man *man, int line, int offs)
/*
* If the string ends with \c, return a pointer to the backslash.
* Otherwise, return NULL.
*/
static char *
man_hasc(char *start)
{
char *cp, *ep;
ep = strchr(start, '\0') - 2;
if (ep < start || ep[0] != '\\' || ep[1] != 'c')
return NULL;
for (cp = ep; cp > start; cp--)
if (cp[-1] != '\\')
break;
return (ep - cp) % 2 ? NULL : ep;
}
void
man_descope(struct roff_man *man, int line, int offs, char *start)
{
/* Trailing \c keeps next-line scope open. */
if (start != NULL && man_hasc(start) != NULL)
return;
/*
* Co-ordinate what happens with having a next-line scope open:
* first close out the element scope (if applicable), then close
* out the block scope (also if applicable).
* first close out the element scopes (if applicable),
* then close out the block scope (also if applicable).
*/
if (man->flags & MAN_ELINE) {
while (man->last->parent->type != ROFFT_ROOT &&
man_macro(man->last->parent->tok)->flags & MAN_ESCOPED)
man_unscope(man, man->last->parent);
man->flags &= ~MAN_ELINE;
man_unscope(man, man->last->parent);
}
if ( ! (man->flags & MAN_BLINE))
return;
man->flags &= ~MAN_BLINE;
man_unscope(man, man->last->parent);
roff_body_alloc(man, line, offs, man->last->tok);
man->flags &= ~(MAN_BLINE | ROFF_NONOFILL);
}
static int
man_ptext(struct roff_man *man, int line, char *buf, int offs)
{
int i;
const char *cp, *sp;
char *ep;
/* Literal free-form text whitespace is preserved. */
/* In no-fill mode, whitespace is preserved on text lines. */
if (man->flags & MAN_LITERAL) {
if (man->flags & ROFF_NOFILL) {
roff_word_alloc(man, line, offs, buf + offs);
man_descope(man, line, offs);
man_descope(man, line, offs, buf + offs);
return 1;
}
@ -98,26 +122,15 @@ man_ptext(struct roff_man *man, int line, char *buf, int offs)
if (buf[i] == '\0') {
if (man->flags & (MAN_ELINE | MAN_BLINE)) {
mandoc_msg(MANDOCERR_BLK_BLANK, man->parse,
line, 0, NULL);
mandoc_msg(MANDOCERR_BLK_BLANK, line, 0, NULL);
return 1;
}
if (man->last->tok == MAN_SH || man->last->tok == MAN_SS)
return 1;
switch (man->last->type) {
case ROFFT_TEXT:
sp = man->last->string;
cp = ep = strchr(sp, '\0') - 2;
if (cp < sp || cp[0] != '\\' || cp[1] != 'c')
break;
while (cp > sp && cp[-1] == '\\')
cp--;
if ((ep - cp) % 2)
break;
if (man->last->type == ROFFT_TEXT &&
((ep = man_hasc(man->last->string)) != NULL)) {
*ep = '\0';
return 1;
default:
break;
}
roff_elem_alloc(man, line, offs, ROFF_sp);
man->next = ROFF_NEXT_SIBLING;
@ -134,8 +147,7 @@ man_ptext(struct roff_man *man, int line, char *buf, int offs)
if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
if (i > 1 && '\\' != buf[i - 2])
mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
line, i - 1, NULL);
mandoc_msg(MANDOCERR_SPACE_EOL, line, i - 1, NULL);
for (--i; i && ' ' == buf[i]; i--)
/* Spin back to non-space. */ ;
@ -157,7 +169,7 @@ man_ptext(struct roff_man *man, int line, char *buf, int offs)
if (mandoc_eos(buf, (size_t)i))
man->last->flags |= NODE_EOS;
man_descope(man, line, offs);
man_descope(man, line, offs, buf + offs);
return 1;
}
@ -180,8 +192,7 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs)
if (sz > 0 && sz < 4)
tok = roffhash_find(man->manmac, buf + ppos, sz);
if (tok == TOKEN_NONE) {
mandoc_msg(MANDOCERR_MACRO, man->parse,
ln, ppos, buf + ppos - 1);
mandoc_msg(MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1);
return 1;
}
@ -211,8 +222,7 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs)
*/
if (buf[offs] == '\0' && buf[offs - 1] == ' ')
mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
ln, offs - 1, NULL);
mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL);
/*
* Some macros break next-line scopes; otherwise, remember
@ -230,16 +240,12 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs)
* page, that's very likely what the author intended.
*/
if (bline) {
cp = strchr(buf + offs, '\0') - 2;
if (cp >= buf && cp[0] == '\\' && cp[1] == 'c')
bline = 0;
}
if (bline && man_hasc(buf + offs))
bline = 0;
/* Call to handler... */
assert(man_macros[tok].fp);
(*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf);
(*man_macro(tok)->fp)(man, tok, ln, ppos, &offs, buf);
/* In quick mode (for mandocdb), abort after the NAME section. */
@ -256,15 +262,15 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs)
* unless the next-line scope is allowed to continue.
*/
if ( ! bline || man->flags & MAN_ELINE ||
man_macros[tok].flags & MAN_NSCOPED)
if (bline == 0 ||
(man->flags & MAN_BLINE) == 0 ||
man->flags & MAN_ELINE ||
man_macro(tok)->flags & MAN_NSCOPED)
return 1;
assert(man->flags & MAN_BLINE);
man->flags &= ~MAN_BLINE;
man_unscope(man, man->last->parent);
roff_body_alloc(man, ln, ppos, man->last->tok);
man->flags &= ~(MAN_BLINE | ROFF_NONOFILL);
return 1;
}
@ -280,17 +286,17 @@ man_breakscope(struct roff_man *man, int tok)
*/
if (man->flags & MAN_ELINE && (tok < MAN_TH ||
! (man_macros[tok].flags & MAN_NSCOPED))) {
(man_macro(tok)->flags & MAN_NSCOPED) == 0)) {
n = man->last;
if (n->type == ROFFT_TEXT)
n = n->parent;
if (n->tok < MAN_TH ||
man_macros[n->tok].flags & MAN_NSCOPED)
(man_macro(n->tok)->flags & (MAN_NSCOPED | MAN_ESCOPED))
== MAN_NSCOPED)
n = n->parent;
mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse,
n->line, n->pos, "%s breaks %s",
roff_name[tok], roff_name[n->tok]);
mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos,
"%s breaks %s", roff_name[tok], roff_name[n->tok]);
roff_node_delete(man, n);
man->flags &= ~MAN_ELINE;
@ -302,12 +308,12 @@ man_breakscope(struct roff_man *man, int tok)
*/
if (man->flags & MAN_BLINE &&
(tok == MAN_nf || tok == MAN_fi) &&
(tok == ROFF_nf || tok == ROFF_fi) &&
(man->last->tok == MAN_SH || man->last->tok == MAN_SS)) {
n = man->last;
man_unscope(man, n);
roff_body_alloc(man, n->line, n->pos, n->tok);
man->flags &= ~MAN_BLINE;
man->flags &= ~(MAN_BLINE | ROFF_NONOFILL);
}
/*
@ -316,68 +322,24 @@ man_breakscope(struct roff_man *man, int tok)
* Delete the block that is being broken.
*/
if (man->flags & MAN_BLINE && (tok < MAN_TH ||
man_macros[tok].flags & MAN_BSCOPE)) {
if (man->flags & MAN_BLINE && tok != ROFF_nf && tok != ROFF_fi &&
(tok < MAN_TH || man_macro(tok)->flags & MAN_XSCOPE)) {
n = man->last;
if (n->type == ROFFT_TEXT)
n = n->parent;
if (n->tok < MAN_TH ||
(man_macros[n->tok].flags & MAN_BSCOPE) == 0)
(man_macro(n->tok)->flags & MAN_XSCOPE) == 0)
n = n->parent;
assert(n->type == ROFFT_HEAD);
n = n->parent;
assert(n->type == ROFFT_BLOCK);
assert(man_macros[n->tok].flags & MAN_SCOPED);
assert(man_macro(n->tok)->flags & MAN_BSCOPED);
mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse,
n->line, n->pos, "%s breaks %s",
roff_name[tok], roff_name[n->tok]);
mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos,
"%s breaks %s", roff_name[tok], roff_name[n->tok]);
roff_node_delete(man, n);
man->flags &= ~MAN_BLINE;
man->flags &= ~(MAN_BLINE | ROFF_NONOFILL);
}
}
const struct mparse *
man_mparse(const struct roff_man *man)
{
assert(man && man->parse);
return man->parse;
}
void
man_state(struct roff_man *man, struct roff_node *n)
{
switch(n->tok) {
case MAN_nf:
case MAN_EX:
if (man->flags & MAN_LITERAL && ! (n->flags & NODE_VALID))
mandoc_msg(MANDOCERR_NF_SKIP, man->parse,
n->line, n->pos, "nf");
man->flags |= MAN_LITERAL;
break;
case MAN_fi:
case MAN_EE:
if ( ! (man->flags & MAN_LITERAL) &&
! (n->flags & NODE_VALID))
mandoc_msg(MANDOCERR_FI_SKIP, man->parse,
n->line, n->pos, "fi");
man->flags &= ~MAN_LITERAL;
break;
default:
break;
}
man->last->flags |= NODE_VALID;
}
void
man_validate(struct roff_man *man)
{
man->last = man->first;
man_node_validate(man);
man->flags &= ~MAN_LITERAL;
}

View File

@ -1,6 +1,6 @@
.\" $Id: man.conf.5,v 1.5 2017/08/22 18:17:52 schwarze Exp $
.\" $Id: man.conf.5,v 1.6 2018/10/02 14:56:47 schwarze Exp $
.\"
.\" Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: August 22 2017 $
.Dd $Mdocdate: October 2 2018 $
.Dt MAN.CONF 5
.Os
.Sh NAME
@ -98,6 +98,7 @@ manual.
.It Ic man Ta string Ta Cm html Ta path for \&Xr links
.It Ic paper Ta string Ta Cm ps , pdf Ta paper size
.It Ic style Ta string Ta Cm html Ta CSS file
.It Ic toc Ta none Ta Cm html Ta print table of contents
.It Ic width Ta integer Ta Cm ascii , utf8 Ta right margin
.El
.It Ic _whatdb Ar path Ns Cm /whatis.db

3
man.h
View File

@ -1,4 +1,4 @@
/* $Id: man.h,v 1.78 2017/04/24 23:06:18 schwarze Exp $ */
/* $Id: man.h,v 1.79 2018/08/23 19:33:27 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@ -18,5 +18,4 @@
struct roff_man;
const struct mparse *man_mparse(const struct roff_man *);
void man_validate(struct roff_man *);

View File

@ -1,7 +1,7 @@
/* $Id: man_html.c,v 1.153 2018/07/27 17:49:31 schwarze Exp $ */
/* $Id: man_html.c,v 1.173 2019/03/02 16:30:53 schwarze Exp $ */
/*
* Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -33,26 +33,22 @@
#include "html.h"
#include "main.h"
/* FIXME: have PD set the default vspace width. */
#define MAN_ARGS const struct roff_meta *man, \
const struct roff_node *n, \
struct html *h
struct htmlman {
struct man_html_act {
int (*pre)(MAN_ARGS);
int (*post)(MAN_ARGS);
};
static void print_bvspace(struct html *,
const struct roff_node *);
static void print_man_head(const struct roff_meta *,
struct html *);
static void print_man_nodelist(MAN_ARGS);
static void print_man_node(MAN_ARGS);
static int fillmode(struct html *, int);
static char list_continues(const struct roff_node *,
const struct roff_node *);
static int man_B_pre(MAN_ARGS);
static int man_HP_pre(MAN_ARGS);
static int man_IP_pre(MAN_ARGS);
static int man_I_pre(MAN_ARGS);
static int man_OP_pre(MAN_ARGS);
@ -60,8 +56,9 @@ static int man_PP_pre(MAN_ARGS);
static int man_RS_pre(MAN_ARGS);
static int man_SH_pre(MAN_ARGS);
static int man_SM_pre(MAN_ARGS);
static int man_SS_pre(MAN_ARGS);
static int man_SY_pre(MAN_ARGS);
static int man_UR_pre(MAN_ARGS);
static int man_abort_pre(MAN_ARGS);
static int man_alt_pre(MAN_ARGS);
static int man_ign_pre(MAN_ARGS);
static int man_in_pre(MAN_ARGS);
@ -70,16 +67,17 @@ static void man_root_post(const struct roff_meta *,
static void man_root_pre(const struct roff_meta *,
struct html *);
static const struct htmlman __mans[MAN_MAX - MAN_TH] = {
static const struct man_html_act man_html_acts[MAN_MAX - MAN_TH] = {
{ NULL, NULL }, /* TH */
{ man_SH_pre, NULL }, /* SH */
{ man_SS_pre, NULL }, /* SS */
{ man_SH_pre, NULL }, /* SS */
{ man_IP_pre, NULL }, /* TP */
{ man_PP_pre, NULL }, /* LP */
{ man_IP_pre, NULL }, /* TQ */
{ man_abort_pre, NULL }, /* LP */
{ man_PP_pre, NULL }, /* PP */
{ man_PP_pre, NULL }, /* P */
{ man_abort_pre, NULL }, /* P */
{ man_IP_pre, NULL }, /* IP */
{ man_HP_pre, NULL }, /* HP */
{ man_PP_pre, NULL }, /* HP */
{ man_SM_pre, NULL }, /* SM */
{ man_SM_pre, NULL }, /* SB */
{ man_alt_pre, NULL }, /* BI */
@ -91,8 +89,6 @@ static const struct htmlman __mans[MAN_MAX - MAN_TH] = {
{ man_I_pre, NULL }, /* I */
{ man_alt_pre, NULL }, /* IR */
{ man_alt_pre, NULL }, /* RI */
{ NULL, NULL }, /* nf */
{ NULL, NULL }, /* fi */
{ NULL, NULL }, /* RE */
{ man_RS_pre, NULL }, /* RS */
{ man_ign_pre, NULL }, /* DT */
@ -100,6 +96,8 @@ static const struct htmlman __mans[MAN_MAX - MAN_TH] = {
{ man_ign_pre, NULL }, /* PD */
{ man_ign_pre, NULL }, /* AT */
{ man_in_pre, NULL }, /* in */
{ man_SY_pre, NULL }, /* SY */
{ NULL, NULL }, /* YS */
{ man_OP_pre, NULL }, /* OP */
{ NULL, NULL }, /* EX */
{ NULL, NULL }, /* EE */
@ -108,34 +106,10 @@ static const struct htmlman __mans[MAN_MAX - MAN_TH] = {
{ man_UR_pre, NULL }, /* MT */
{ NULL, NULL }, /* ME */
};
static const struct htmlman *const mans = __mans - MAN_TH;
/*
* Printing leading vertical space before a block.
* This is used for the paragraph macros.
* The rules are pretty simple, since there's very little nesting going
* on here. Basically, if we're the first within another block (SS/SH),
* then don't emit vertical space. If we are (RS), then do. If not the
* first, print it.
*/
static void
print_bvspace(struct html *h, const struct roff_node *n)
{
if (n->body && n->body->child)
if (n->body->child->type == ROFFT_TBL)
return;
if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
if (NULL == n->prev)
return;
print_paragraph(h);
}
void
html_man(void *arg, const struct roff_man *man)
html_man(void *arg, const struct roff_meta *man)
{
struct html *h;
struct roff_node *n;
@ -147,19 +121,19 @@ html_man(void *arg, const struct roff_man *man)
if ((h->oflags & HTML_FRAGMENT) == 0) {
print_gen_decls(h);
print_otag(h, TAG_HTML, "");
if (n->type == ROFFT_COMMENT)
if (n != NULL && n->type == ROFFT_COMMENT)
print_gen_comment(h, n);
t = print_otag(h, TAG_HEAD, "");
print_man_head(&man->meta, h);
print_man_head(man, h);
print_tagq(h, t);
print_otag(h, TAG_BODY, "");
}
man_root_pre(&man->meta, h);
man_root_pre(man, h);
t = print_otag(h, TAG_DIV, "c", "manual-text");
print_man_nodelist(&man->meta, n, h);
print_man_nodelist(man, n, h);
print_tagq(h, t);
man_root_post(&man->meta, h);
man_root_post(man, h);
print_tagq(h, NULL);
}
@ -178,7 +152,6 @@ print_man_head(const struct roff_meta *man, struct html *h)
static void
print_man_nodelist(MAN_ARGS)
{
while (n != NULL) {
print_man_node(man, n, h);
n = n->next;
@ -188,99 +161,33 @@ print_man_nodelist(MAN_ARGS)
static void
print_man_node(MAN_ARGS)
{
static int want_fillmode = MAN_fi;
static int save_fillmode;
struct tag *t;
int child;
/*
* Handle fill mode switch requests up front,
* they would just cause trouble in the subsequent code.
*/
switch (n->tok) {
case MAN_nf:
case MAN_EX:
want_fillmode = MAN_nf;
if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
return;
case MAN_fi:
case MAN_EE:
want_fillmode = MAN_fi;
if (fillmode(h, 0) == MAN_fi)
print_otag(h, TAG_BR, "");
return;
default:
break;
}
/* Set up fill mode for the upcoming node. */
switch (n->type) {
case ROFFT_BLOCK:
save_fillmode = 0;
/* Some block macros suspend or cancel .nf. */
switch (n->tok) {
case MAN_TP: /* Tagged paragraphs */
case MAN_IP: /* temporarily disable .nf */
case MAN_HP: /* for the head. */
save_fillmode = want_fillmode;
/* FALLTHROUGH */
case MAN_SH: /* Section headers */
case MAN_SS: /* permanently cancel .nf. */
want_fillmode = MAN_fi;
/* FALLTHROUGH */
case MAN_PP: /* These have no head. */
case MAN_LP: /* They will simply */
case MAN_P: /* reopen .nf in the body. */
case MAN_RS:
case MAN_UR:
case MAN_MT:
fillmode(h, MAN_fi);
break;
default:
break;
}
break;
case ROFFT_TBL:
fillmode(h, MAN_fi);
break;
case ROFFT_ELEM:
/*
* Some in-line macros produce tags and/or text
* in the handler, so they require fill mode to be
* configured up front just like for text nodes.
* For the others, keep the traditional approach
* of doing the same, for now.
*/
fillmode(h, want_fillmode);
break;
case ROFFT_TEXT:
if (fillmode(h, want_fillmode) == MAN_fi &&
want_fillmode == MAN_fi &&
n->flags & NODE_LINE && *n->string == ' ' &&
(h->flags & HTML_NONEWLINE) == 0)
print_otag(h, TAG_BR, "");
if (*n->string != '\0')
break;
print_paragraph(h);
return;
case ROFFT_COMMENT:
return;
default:
break;
}
/* Produce output for this node. */
html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi);
child = 1;
switch (n->type) {
case ROFFT_TEXT:
if (*n->string == '\0') {
print_endline(h);
return;
}
if (*n->string == ' ' && n->flags & NODE_LINE &&
(h->flags & HTML_NONEWLINE) == 0)
print_endline(h);
else if (n->flags & NODE_DELIMC)
h->flags |= HTML_NOSPACE;
t = h->tag;
t->refcnt++;
print_text(h, n->string);
break;
case ROFFT_EQN:
t = h->tag;
t->refcnt++;
print_eqn(h, n->eqn);
break;
case ROFFT_TBL:
@ -306,62 +213,51 @@ print_man_node(MAN_ARGS)
* the "meta" table state. This will be reopened on the
* next table element.
*/
if (h->tblt)
if (h->tblt != NULL)
print_tblclose(h);
t = h->tag;
t->refcnt++;
if (n->tok < ROFF_MAX) {
roff_html_pre(h, n);
child = 0;
break;
t->refcnt--;
print_stagq(h, t);
return;
}
assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
if (mans[n->tok].pre)
child = (*mans[n->tok].pre)(man, n, h);
/* Some block macros resume .nf in the body. */
if (save_fillmode && n->type == ROFFT_BODY)
want_fillmode = save_fillmode;
if (man_html_acts[n->tok - MAN_TH].pre != NULL)
child = (*man_html_acts[n->tok - MAN_TH].pre)(man,
n, h);
break;
}
if (child && n->child)
if (child && n->child != NULL)
print_man_nodelist(man, n->child, h);
/* This will automatically close out any font scope. */
print_stagq(h, t);
if (fillmode(h, 0) == MAN_nf &&
n->next != NULL && n->next->flags & NODE_LINE)
print_endline(h);
}
/*
* MAN_nf switches to no-fill mode, MAN_fi to fill mode.
* Other arguments do not switch.
* The old mode is returned.
*/
static int
fillmode(struct html *h, int want)
{
struct tag *pre;
int had;
for (pre = h->tag; pre != NULL; pre = pre->next)
if (pre->tag == TAG_PRE)
break;
had = pre == NULL ? MAN_fi : MAN_nf;
if (want && want != had) {
if (want == MAN_nf)
print_otag(h, TAG_PRE, "");
else
print_tagq(h, pre);
t->refcnt--;
if (n->type == ROFFT_BLOCK &&
(n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) {
t = h->tag;
while (t->tag != TAG_DL && t->tag != TAG_UL)
t = t->next;
/*
* Close the list if no further item of the same type
* follows; otherwise, close the item only.
*/
if (list_continues(n, n->next) == '\0') {
print_tagq(h, t);
t = NULL;
}
}
if (t != NULL)
print_stagq(h, t);
if (n->flags & NODE_NOFILL && n->tok != MAN_YS &&
(n->next != NULL && n->next->flags & NODE_LINE)) {
/* In .nf = <pre>, print even empty lines. */
h->col++;
print_endline(h);
}
return had;
}
static void
@ -382,7 +278,7 @@ man_root_pre(const struct roff_meta *man, struct html *h)
print_stagq(h, tt);
print_otag(h, TAG_TD, "c", "head-vol");
if (NULL != man->vol)
if (man->vol != NULL)
print_text(h, man->vol);
print_stagq(h, tt);
@ -405,7 +301,7 @@ man_root_post(const struct roff_meta *man, struct html *h)
print_stagq(h, tt);
print_otag(h, TAG_TD, "c", "foot-os");
if (man->os)
if (man->os != NULL)
print_text(h, man->os);
print_tagq(h, t);
}
@ -413,13 +309,32 @@ man_root_post(const struct roff_meta *man, struct html *h)
static int
man_SH_pre(MAN_ARGS)
{
char *id;
const char *class;
char *id;
enum htmltag tag;
if (n->type == ROFFT_HEAD) {
if (n->tok == MAN_SH) {
tag = TAG_H1;
class = "Sh";
} else {
tag = TAG_H2;
class = "Ss";
}
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
print_otag(h, TAG_SECTION, "c", class);
break;
case ROFFT_HEAD:
id = html_make_id(n, 1);
print_otag(h, TAG_H1, "cTi", "Sh", id);
print_otag(h, tag, "ci", class, id);
if (id != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
break;
case ROFFT_BODY:
break;
default:
abort();
}
return 1;
}
@ -428,11 +343,11 @@ static int
man_alt_pre(MAN_ARGS)
{
const struct roff_node *nn;
struct tag *t;
int i;
enum htmltag fp;
struct tag *t;
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i++) {
switch (n->tok) {
case MAN_BI:
fp = i % 2 ? TAG_I : TAG_B;
@ -474,87 +389,134 @@ static int
man_SM_pre(MAN_ARGS)
{
print_otag(h, TAG_SMALL, "");
if (MAN_SB == n->tok)
if (n->tok == MAN_SB)
print_otag(h, TAG_B, "");
return 1;
}
static int
man_SS_pre(MAN_ARGS)
{
char *id;
if (n->type == ROFFT_HEAD) {
id = html_make_id(n, 1);
print_otag(h, TAG_H2, "cTi", "Ss", id);
if (id != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
}
return 1;
}
static int
man_PP_pre(MAN_ARGS)
{
if (n->type == ROFFT_HEAD)
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
break;
case ROFFT_HEAD:
return 0;
else if (n->type == ROFFT_BLOCK)
print_bvspace(h, n);
case ROFFT_BODY:
if (n->child != NULL &&
(n->child->flags & NODE_NOFILL) == 0)
print_otag(h, TAG_P, "c",
n->tok == MAN_PP ? "Pp" : "Pp HP");
break;
default:
abort();
}
return 1;
}
static char
list_continues(const struct roff_node *n1, const struct roff_node *n2)
{
const char *s1, *s2;
char c1, c2;
if (n1 == NULL || n1->type != ROFFT_BLOCK ||
n2 == NULL || n2->type != ROFFT_BLOCK)
return '\0';
if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) &&
(n2->tok == MAN_TP || n2->tok == MAN_TQ))
return ' ';
if (n1->tok != MAN_IP || n2->tok != MAN_IP)
return '\0';
n1 = n1->head->child;
n2 = n2->head->child;
s1 = n1 == NULL ? "" : n1->string;
s2 = n2 == NULL ? "" : n2->string;
c1 = strcmp(s1, "*") == 0 ? '*' :
strcmp(s1, "\\-") == 0 ? '-' :
strcmp(s1, "\\(bu") == 0 ? 'b' : ' ';
c2 = strcmp(s2, "*") == 0 ? '*' :
strcmp(s2, "\\-") == 0 ? '-' :
strcmp(s2, "\\(bu") == 0 ? 'b' : ' ';
return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1;
}
static int
man_IP_pre(MAN_ARGS)
{
const struct roff_node *nn;
const char *list_class;
enum htmltag list_elem, body_elem;
char list_type;
if (n->type == ROFFT_BODY) {
print_otag(h, TAG_DD, "");
nn = n->type == ROFFT_BLOCK ? n : n->parent;
if ((list_type = list_continues(nn->prev, nn)) == '\0') {
/* Start a new list. */
if ((list_type = list_continues(nn, nn->next)) == '\0')
list_type = ' ';
switch (list_type) {
case ' ':
list_class = "Bl-tag";
list_elem = TAG_DL;
break;
case '*':
list_class = "Bl-bullet";
list_elem = TAG_UL;
break;
case '-':
list_class = "Bl-dash";
list_elem = TAG_UL;
break;
default:
abort();
}
} else {
/* Continue a list that was started earlier. */
list_class = NULL;
list_elem = TAG_MAX;
}
body_elem = list_type == ' ' ? TAG_DD : TAG_LI;
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
if (list_elem != TAG_MAX)
print_otag(h, list_elem, "c", list_class);
return 1;
} else if (n->type != ROFFT_HEAD) {
print_otag(h, TAG_DL, "c", "Bl-tag");
case ROFFT_HEAD:
if (body_elem == TAG_LI)
return 0;
print_otag(h, TAG_DT, "");
break;
case ROFFT_BODY:
print_otag(h, body_elem, "");
return 1;
default:
abort();
}
/* FIXME: width specification. */
print_otag(h, TAG_DT, "");
/* For IP, only print the first header element. */
if (MAN_IP == n->tok && n->child)
print_man_node(man, n->child, h);
/* For TP, only print next-line header elements. */
if (MAN_TP == n->tok) {
switch(n->tok) {
case MAN_IP: /* Only print the first header element. */
if (n->child != NULL)
print_man_node(man, n->child, h);
break;
case MAN_TP: /* Only print next-line header elements. */
case MAN_TQ:
nn = n->child;
while (NULL != nn && 0 == (NODE_LINE & nn->flags))
while (nn != NULL && (NODE_LINE & nn->flags) == 0)
nn = nn->next;
while (NULL != nn) {
while (nn != NULL) {
print_man_node(man, nn, h);
nn = nn->next;
}
break;
default:
abort();
}
return 0;
}
static int
man_HP_pre(MAN_ARGS)
{
if (n->type == ROFFT_HEAD)
return 0;
if (n->type == ROFFT_BLOCK) {
print_bvspace(h, n);
print_otag(h, TAG_DIV, "c", "HP");
}
return 1;
}
static int
man_OP_pre(MAN_ARGS)
{
@ -564,14 +526,14 @@ man_OP_pre(MAN_ARGS)
h->flags |= HTML_NOSPACE;
tt = print_otag(h, TAG_SPAN, "c", "Op");
if (NULL != (n = n->child)) {
if ((n = n->child) != NULL) {
print_otag(h, TAG_B, "");
print_text(h, n->string);
}
print_stagq(h, tt);
if (NULL != n && NULL != n->next) {
if (n != NULL && n->next != NULL) {
print_otag(h, TAG_I, "");
print_text(h, n->next->string);
}
@ -606,17 +568,46 @@ man_in_pre(MAN_ARGS)
static int
man_ign_pre(MAN_ARGS)
{
return 0;
}
static int
man_RS_pre(MAN_ARGS)
{
if (n->type == ROFFT_HEAD)
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
break;
case ROFFT_HEAD:
return 0;
if (n->type == ROFFT_BLOCK)
case ROFFT_BODY:
print_otag(h, TAG_DIV, "c", "Bd-indent");
break;
default:
abort();
}
return 1;
}
static int
man_SY_pre(MAN_ARGS)
{
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
print_otag(h, TAG_TABLE, "c", "Nm");
print_otag(h, TAG_TR, "");
break;
case ROFFT_HEAD:
print_otag(h, TAG_TD, "");
print_otag(h, TAG_CODE, "c", "Nm");
break;
case ROFFT_BODY:
print_otag(h, TAG_TD, "");
break;
default:
abort();
}
return 1;
}
@ -624,16 +615,17 @@ static int
man_UR_pre(MAN_ARGS)
{
char *cp;
n = n->child;
assert(n->type == ROFFT_HEAD);
if (n->child != NULL) {
assert(n->child->type == ROFFT_TEXT);
if (n->tok == MAN_MT) {
mandoc_asprintf(&cp, "mailto:%s", n->child->string);
print_otag(h, TAG_A, "cTh", "Mt", cp);
print_otag(h, TAG_A, "ch", "Mt", cp);
free(cp);
} else
print_otag(h, TAG_A, "cTh", "Lk", n->child->string);
print_otag(h, TAG_A, "ch", "Lk", n->child->string);
}
assert(n->next->type == ROFFT_BODY);
@ -641,6 +633,11 @@ man_UR_pre(MAN_ARGS)
n = n->next;
print_man_nodelist(man, n->child, h);
return 0;
}
static int
man_abort_pre(MAN_ARGS)
{
abort();
}

View File

@ -1,7 +1,7 @@
/* $Id: man_macro.c,v 1.123 2017/06/25 11:45:37 schwarze Exp $ */
/* $Id: man_macro.c,v 1.144 2019/01/05 18:59:46 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2012-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2012-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
*
* Permission to use, copy, modify, and distribute this software for any
@ -22,6 +22,7 @@
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -40,47 +41,54 @@ static int man_args(struct roff_man *, int,
int *, char *, char **);
static void rew_scope(struct roff_man *, enum roff_tok);
const struct man_macro __man_macros[MAN_MAX - MAN_TH] = {
{ in_line_eoln, MAN_BSCOPE }, /* TH */
{ blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SH */
{ blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SS */
{ blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* TP */
{ blk_imp, MAN_BSCOPE }, /* LP */
{ blk_imp, MAN_BSCOPE }, /* PP */
{ blk_imp, MAN_BSCOPE }, /* P */
{ blk_imp, MAN_BSCOPE }, /* IP */
{ blk_imp, MAN_BSCOPE }, /* HP */
{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SM */
{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SB */
static const struct man_macro man_macros[MAN_MAX - MAN_TH] = {
{ in_line_eoln, MAN_XSCOPE }, /* TH */
{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SH */
{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SS */
{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TP */
{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TQ */
{ blk_imp, MAN_XSCOPE }, /* LP */
{ blk_imp, MAN_XSCOPE }, /* PP */
{ blk_imp, MAN_XSCOPE }, /* P */
{ blk_imp, MAN_XSCOPE }, /* IP */
{ blk_imp, MAN_XSCOPE }, /* HP */
{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SM */
{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SB */
{ in_line_eoln, 0 }, /* BI */
{ in_line_eoln, 0 }, /* IB */
{ in_line_eoln, 0 }, /* BR */
{ in_line_eoln, 0 }, /* RB */
{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* R */
{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* B */
{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* I */
{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* R */
{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* B */
{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* I */
{ in_line_eoln, 0 }, /* IR */
{ in_line_eoln, 0 }, /* RI */
{ in_line_eoln, MAN_NSCOPED }, /* nf */
{ in_line_eoln, MAN_NSCOPED }, /* fi */
{ blk_close, MAN_BSCOPE }, /* RE */
{ blk_exp, MAN_BSCOPE }, /* RS */
{ blk_close, MAN_XSCOPE }, /* RE */
{ blk_exp, MAN_XSCOPE }, /* RS */
{ in_line_eoln, 0 }, /* DT */
{ in_line_eoln, 0 }, /* UC */
{ in_line_eoln, MAN_NSCOPED }, /* PD */
{ in_line_eoln, 0 }, /* AT */
{ in_line_eoln, MAN_NSCOPED }, /* in */
{ blk_imp, MAN_XSCOPE }, /* SY */
{ blk_close, MAN_XSCOPE }, /* YS */
{ in_line_eoln, 0 }, /* OP */
{ in_line_eoln, MAN_BSCOPE }, /* EX */
{ in_line_eoln, MAN_BSCOPE }, /* EE */
{ blk_exp, MAN_BSCOPE }, /* UR */
{ blk_close, MAN_BSCOPE }, /* UE */
{ blk_exp, MAN_BSCOPE }, /* MT */
{ blk_close, MAN_BSCOPE }, /* ME */
{ in_line_eoln, MAN_XSCOPE }, /* EX */
{ in_line_eoln, MAN_XSCOPE }, /* EE */
{ blk_exp, MAN_XSCOPE }, /* UR */
{ blk_close, MAN_XSCOPE }, /* UE */
{ blk_exp, MAN_XSCOPE }, /* MT */
{ blk_close, MAN_XSCOPE }, /* ME */
};
const struct man_macro *const man_macros = __man_macros - MAN_TH;
const struct man_macro *
man_macro(enum roff_tok tok)
{
assert(tok >= MAN_TH && tok <= MAN_MAX);
return man_macros + (tok - MAN_TH);
}
void
man_unscope(struct roff_man *man, const struct roff_node *to)
{
@ -94,9 +102,10 @@ man_unscope(struct roff_man *man, const struct roff_node *to)
if (to == NULL && ! (n->flags & NODE_VALID)) {
if (man->flags & (MAN_BLINE | MAN_ELINE) &&
man_macros[n->tok].flags & MAN_SCOPED) {
mandoc_vmsg(MANDOCERR_BLK_LINE,
man->parse, n->line, n->pos,
man_macro(n->tok)->flags &
(MAN_BSCOPED | MAN_NSCOPED)) {
mandoc_msg(MANDOCERR_BLK_LINE,
n->line, n->pos,
"EOF breaks %s", roff_name[n->tok]);
if (man->flags & MAN_ELINE)
man->flags &= ~MAN_ELINE;
@ -111,9 +120,9 @@ man_unscope(struct roff_man *man, const struct roff_node *to)
continue;
}
if (n->type == ROFFT_BLOCK &&
man_macros[n->tok].fp == blk_exp)
man_macro(n->tok)->fp == blk_exp)
mandoc_msg(MANDOCERR_BLK_NOEND,
man->parse, n->line, n->pos,
n->line, n->pos, "%s",
roff_name[n->tok]);
}
@ -175,7 +184,7 @@ rew_scope(struct roff_man *man, enum roff_tok tok)
}
if (tok != MAN_SH && (n->tok == MAN_SH ||
(tok != MAN_SS && (n->tok == MAN_SS ||
man_macros[n->tok].fp == blk_exp))))
man_macro(n->tok)->fp == blk_exp))))
return;
man_unscope(man, n);
n = man->last;
@ -189,33 +198,39 @@ rew_scope(struct roff_man *man, enum roff_tok tok)
void
blk_close(MACRO_PROT_ARGS)
{
enum roff_tok ntok;
enum roff_tok ctok, ntok;
const struct roff_node *nn;
char *p;
int nrew, target;
char *p, *ep;
int cline, cpos, la, nrew, target;
nrew = 1;
switch (tok) {
case MAN_RE:
ntok = MAN_RS;
la = *pos;
if ( ! man_args(man, line, pos, buf, &p))
break;
for (nn = man->last->parent; nn; nn = nn->parent)
if (nn->tok == ntok && nn->type == ROFFT_BLOCK)
nrew++;
target = strtol(p, &p, 10);
if (*p != '\0')
mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
line, p - buf, "RE ... %s", p);
target = strtol(p, &ep, 10);
if (*ep != '\0')
mandoc_msg(MANDOCERR_ARG_EXCESS, line,
la + (buf[la] == '"') + (int)(ep - p),
"RE ... %s", ep);
free(p);
if (target == 0)
target = 1;
nrew -= target;
if (nrew < 1) {
mandoc_vmsg(MANDOCERR_RE_NOTOPEN, man->parse,
mandoc_msg(MANDOCERR_RE_NOTOPEN,
line, ppos, "RE %d", target);
return;
}
break;
case MAN_YS:
ntok = MAN_SY;
break;
case MAN_UE:
ntok = MAN_UR;
break;
@ -231,25 +246,47 @@ blk_close(MACRO_PROT_ARGS)
break;
if (nn == NULL) {
mandoc_msg(MANDOCERR_BLK_NOTOPEN, man->parse,
line, ppos, roff_name[tok]);
mandoc_msg(MANDOCERR_BLK_NOTOPEN,
line, ppos, "%s", roff_name[tok]);
rew_scope(man, MAN_PP);
} else {
line = man->last->line;
ppos = man->last->pos;
ntok = man->last->tok;
man_unscope(man, nn);
if (tok == MAN_RE && nn->head->aux > 0)
roff_setreg(man->roff, "an-margin",
nn->head->aux, '-');
/* Move a trailing paragraph behind the block. */
if (ntok == MAN_LP || ntok == MAN_PP || ntok == MAN_P) {
*pos = strlen(buf);
blk_imp(man, ntok, line, ppos, pos, buf);
if (tok == MAN_RE) {
roff_elem_alloc(man, line, ppos, ROFF_br);
man->last->flags |= NODE_LINE |
NODE_VALID | NODE_ENDED;
man->next = ROFF_NEXT_SIBLING;
}
return;
}
cline = man->last->line;
cpos = man->last->pos;
ctok = man->last->tok;
man_unscope(man, nn);
if (tok == MAN_RE && nn->head->aux > 0)
roff_setreg(man->roff, "an-margin", nn->head->aux, '-');
/* Trailing text. */
if (buf[*pos] != '\0') {
roff_word_alloc(man, line, ppos, buf + *pos);
man->last->flags |= NODE_DELIMC;
if (mandoc_eos(man->last->string, strlen(man->last->string)))
man->last->flags |= NODE_EOS;
}
/* Move a trailing paragraph behind the block. */
if (ctok == MAN_LP || ctok == MAN_PP || ctok == MAN_P) {
*pos = strlen(buf);
blk_imp(man, ctok, cline, cpos, pos, buf);
}
/* Synopsis blocks need an explicit end marker for spacing. */
if (tok == MAN_YS && man->last == nn) {
roff_elem_alloc(man, line, ppos, tok);
man_unscope(man, man->last);
}
}
@ -260,7 +297,10 @@ blk_exp(MACRO_PROT_ARGS)
char *p;
int la;
rew_scope(man, tok);
if (tok == MAN_RS) {
rew_scope(man, tok);
man->flags |= ROFF_NONOFILL;
}
roff_block_alloc(man, line, ppos, tok);
head = roff_head_alloc(man, line, ppos, tok);
@ -275,14 +315,16 @@ blk_exp(MACRO_PROT_ARGS)
roff_setreg(man->roff, "an-margin",
head->aux, '+');
}
free(p);
}
if (buf[*pos] != '\0')
mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, line,
*pos, "%s ... %s", roff_name[tok], buf + *pos);
mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
"%s ... %s", roff_name[tok], buf + *pos);
man_unscope(man, head);
roff_body_alloc(man, line, ppos, tok);
man->flags &= ~ROFF_NONOFILL;
}
/*
@ -299,9 +341,10 @@ blk_imp(MACRO_PROT_ARGS)
struct roff_node *n;
rew_scope(man, tok);
n = roff_block_alloc(man, line, ppos, tok);
if (n->tok == MAN_SH || n->tok == MAN_SS)
man->flags &= ~MAN_LITERAL;
man->flags |= ROFF_NONOFILL;
if (tok == MAN_SH || tok == MAN_SS)
man->flags &= ~ROFF_NOFILL;
roff_block_alloc(man, line, ppos, tok);
n = roff_head_alloc(man, line, ppos, tok);
/* Add line arguments. */
@ -311,16 +354,17 @@ blk_imp(MACRO_PROT_ARGS)
if ( ! man_args(man, line, pos, buf, &p))
break;
roff_word_alloc(man, line, la, p);
free(p);
}
/*
* For macros having optional next-line scope,
* keep the head open if there were no arguments.
* For `TP', always keep the head open.
* For `TP' and `TQ', always keep the head open.
*/
if (man_macros[tok].flags & MAN_SCOPED &&
(tok == MAN_TP || n == man->last)) {
if (man_macro(tok)->flags & MAN_BSCOPED &&
(tok == MAN_TP || tok == MAN_TQ || n == man->last)) {
man->flags |= MAN_BLINE;
return;
}
@ -329,6 +373,7 @@ blk_imp(MACRO_PROT_ARGS)
man_unscope(man, n);
roff_body_alloc(man, line, ppos, tok);
man->flags &= ~ROFF_NONOFILL;
}
void
@ -341,27 +386,26 @@ in_line_eoln(MACRO_PROT_ARGS)
roff_elem_alloc(man, line, ppos, tok);
n = man->last;
if (tok == MAN_EX)
man->flags |= ROFF_NOFILL;
else if (tok == MAN_EE)
man->flags &= ~ROFF_NOFILL;
for (;;) {
if (buf[*pos] != '\0' && (tok == MAN_fi || tok == MAN_nf)) {
mandoc_vmsg(MANDOCERR_ARG_SKIP,
man->parse, line, *pos, "%s %s",
roff_name[tok], buf + *pos);
break;
}
if (buf[*pos] != '\0' && man->last != n && tok == MAN_PD) {
mandoc_vmsg(MANDOCERR_ARG_EXCESS,
man->parse, line, *pos, "%s ... %s",
roff_name[tok], buf + *pos);
mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
"%s ... %s", roff_name[tok], buf + *pos);
break;
}
la = *pos;
if ( ! man_args(man, line, pos, buf, &p))
break;
if (man_macros[tok].flags & MAN_JOIN &&
if (man_macro(tok)->flags & MAN_JOIN &&
man->last->type == ROFFT_TEXT)
roff_word_append(man, p);
else
roff_word_alloc(man, line, la, p);
free(p);
}
/*
@ -374,13 +418,12 @@ in_line_eoln(MACRO_PROT_ARGS)
man->last->flags |= NODE_EOS;
/*
* If no arguments are specified and this is MAN_SCOPED (i.e.,
* If no arguments are specified and this is MAN_ESCOPED (i.e.,
* next-line scoped), then set our mode to indicate that we're
* waiting for terms to load into our context.
*/
if (n == man->last && man_macros[tok].flags & MAN_SCOPED) {
assert( ! (man_macros[tok].flags & MAN_NSCOPED));
if (n == man->last && man_macro(tok)->flags & MAN_ESCOPED) {
man->flags |= MAN_ELINE;
return;
}
@ -391,18 +434,21 @@ in_line_eoln(MACRO_PROT_ARGS)
/* Rewind our element scope. */
for ( ; man->last; man->last = man->last->parent) {
man_state(man, man->last);
man->last->flags |= NODE_VALID;
if (man->last == n)
break;
}
/* Rewind next-line scoped ancestors, if any. */
if (man_macro(tok)->flags & MAN_ESCOPED)
man_descope(man, line, ppos, NULL);
}
void
man_endparse(struct roff_man *man)
{
man_unscope(man, man->first);
man->flags &= ~MAN_LITERAL;
man_unscope(man, man->meta.first);
}
static int
@ -417,6 +463,6 @@ man_args(struct roff_man *man, int line, int *pos, char *buf, char **v)
if ('\0' == *start)
return 0;
*v = mandoc_getarg(man->parse, v, line, pos);
*v = roff_getarg(man->roff, v, line, pos);
return 1;
}

View File

@ -1,7 +1,7 @@
/* $Id: man_term.c,v 1.211 2018/06/10 15:12:35 schwarze Exp $ */
/* $Id: man_term.c,v 1.228 2019/01/05 21:18:26 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -27,7 +27,6 @@
#include <string.h>
#include "mandoc_aux.h"
#include "mandoc.h"
#include "roff.h"
#include "man.h"
#include "out.h"
@ -37,8 +36,6 @@
#define MAXMARGINS 64 /* maximum number of indented scopes */
struct mtermp {
int fl;
#define MANT_LITERAL (1 << 0)
int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
int lmargincur; /* index of current margin */
int lmarginsz; /* actual number of nested margins */
@ -51,7 +48,7 @@ struct mtermp {
struct roff_node *n, \
const struct roff_meta *meta
struct termact {
struct man_term_act {
int (*pre)(DECL_ARGS);
void (*post)(DECL_ARGS);
int flags;
@ -78,8 +75,10 @@ static int pre_PP(DECL_ARGS);
static int pre_RS(DECL_ARGS);
static int pre_SH(DECL_ARGS);
static int pre_SS(DECL_ARGS);
static int pre_SY(DECL_ARGS);
static int pre_TP(DECL_ARGS);
static int pre_UR(DECL_ARGS);
static int pre_abort(DECL_ARGS);
static int pre_alternate(DECL_ARGS);
static int pre_ign(DECL_ARGS);
static int pre_in(DECL_ARGS);
@ -89,18 +88,19 @@ static void post_IP(DECL_ARGS);
static void post_HP(DECL_ARGS);
static void post_RS(DECL_ARGS);
static void post_SH(DECL_ARGS);
static void post_SS(DECL_ARGS);
static void post_SY(DECL_ARGS);
static void post_TP(DECL_ARGS);
static void post_UR(DECL_ARGS);
static const struct termact __termacts[MAN_MAX - MAN_TH] = {
static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = {
{ NULL, NULL, 0 }, /* TH */
{ pre_SH, post_SH, 0 }, /* SH */
{ pre_SS, post_SS, 0 }, /* SS */
{ pre_SS, post_SH, 0 }, /* SS */
{ pre_TP, post_TP, 0 }, /* TP */
{ pre_PP, NULL, 0 }, /* LP */
{ pre_TP, post_TP, 0 }, /* TQ */
{ pre_abort, NULL, 0 }, /* LP */
{ pre_PP, NULL, 0 }, /* PP */
{ pre_PP, NULL, 0 }, /* P */
{ pre_abort, NULL, 0 }, /* P */
{ pre_IP, post_IP, 0 }, /* IP */
{ pre_HP, post_HP, 0 }, /* HP */
{ NULL, NULL, 0 }, /* SM */
@ -114,8 +114,6 @@ static const struct termact __termacts[MAN_MAX - MAN_TH] = {
{ pre_I, NULL, 0 }, /* I */
{ pre_alternate, NULL, 0 }, /* IR */
{ pre_alternate, NULL, 0 }, /* RI */
{ pre_literal, NULL, 0 }, /* nf */
{ pre_literal, NULL, 0 }, /* fi */
{ NULL, NULL, 0 }, /* RE */
{ pre_RS, post_RS, 0 }, /* RS */
{ pre_DT, NULL, 0 }, /* DT */
@ -123,6 +121,8 @@ static const struct termact __termacts[MAN_MAX - MAN_TH] = {
{ pre_PD, NULL, MAN_NOTEXT }, /* PD */
{ pre_ign, NULL, 0 }, /* AT */
{ pre_in, NULL, MAN_NOTEXT }, /* in */
{ pre_SY, post_SY, 0 }, /* SY */
{ NULL, NULL, 0 }, /* YS */
{ pre_OP, NULL, 0 }, /* OP */
{ pre_literal, NULL, 0 }, /* EX */
{ pre_literal, NULL, 0 }, /* EE */
@ -131,15 +131,22 @@ static const struct termact __termacts[MAN_MAX - MAN_TH] = {
{ pre_UR, post_UR, 0 }, /* MT */
{ NULL, NULL, 0 }, /* ME */
};
static const struct termact *termacts = __termacts - MAN_TH;
static const struct man_term_act *man_term_act(enum roff_tok);
static const struct man_term_act *
man_term_act(enum roff_tok tok)
{
assert(tok >= MAN_TH && tok <= MAN_MAX);
return man_term_acts + (tok - MAN_TH);
}
void
terminal_man(void *arg, const struct roff_man *man)
terminal_man(void *arg, const struct roff_meta *man)
{
struct mtermp mt;
struct termp *p;
struct roff_node *n;
struct mtermp mt;
size_t save_defindent;
p = (struct termp *)arg;
@ -151,7 +158,7 @@ terminal_man(void *arg, const struct roff_man *man)
term_tab_set(p, "T");
term_tab_set(p, ".5i");
memset(&mt, 0, sizeof(struct mtermp));
memset(&mt, 0, sizeof(mt));
mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
mt.offset = term_len(p, p->defindent);
mt.pardist = 1;
@ -164,18 +171,17 @@ terminal_man(void *arg, const struct roff_man *man)
!strcmp(n->child->child->string, "SYNOPSIS")) {
if (n->child->next->child != NULL)
print_man_nodelist(p, &mt,
n->child->next->child,
&man->meta);
n->child->next->child, man);
term_newln(p);
break;
}
n = n->next;
}
} else {
term_begin(p, print_man_head, print_man_foot, &man->meta);
term_begin(p, print_man_head, print_man_foot, man);
p->flags |= TERMP_NOSPACE;
if (n != NULL)
print_man_nodelist(p, &mt, n, &man->meta);
print_man_nodelist(p, &mt, n, man);
term_end(p);
}
p->defindent = save_defindent;
@ -196,12 +202,12 @@ print_bvspace(struct termp *p, const struct roff_node *n, int pardist)
term_newln(p);
if (n->body && n->body->child)
if (n->body != NULL && n->body->child != NULL)
if (n->body->child->type == ROFFT_TBL)
return;
if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
if (NULL == n->prev)
if (n->prev == NULL)
return;
for (i = 0; i < pardist; i++)
@ -209,17 +215,21 @@ print_bvspace(struct termp *p, const struct roff_node *n, int pardist)
}
static int
pre_abort(DECL_ARGS)
{
abort();
}
static int
pre_ign(DECL_ARGS)
{
return 0;
}
static int
pre_I(DECL_ARGS)
{
term_fontrepl(p, TERMFONT_UNDER);
return 1;
}
@ -227,14 +237,8 @@ pre_I(DECL_ARGS)
static int
pre_literal(DECL_ARGS)
{
term_newln(p);
if (n->tok == MAN_nf || n->tok == MAN_EX)
mt->fl |= MANT_LITERAL;
else
mt->fl &= ~MANT_LITERAL;
/*
* Unlike .IP and .TP, .HP does not have a HEAD.
* So in case a second call to term_flushln() is needed,
@ -247,7 +251,6 @@ pre_literal(DECL_ARGS)
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
p->flags |= TERMP_NOSPACE;
}
return 0;
}
@ -272,7 +275,7 @@ pre_alternate(DECL_ARGS)
{
enum termfont font[2];
struct roff_node *nn;
int savelit, i;
int i;
switch (n->tok) {
case MAN_RB:
@ -302,29 +305,21 @@ pre_alternate(DECL_ARGS)
default:
abort();
}
savelit = MANT_LITERAL & mt->fl;
mt->fl &= ~MANT_LITERAL;
for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i = 1 - i) {
term_fontrepl(p, font[i]);
if (savelit && NULL == nn->next)
mt->fl |= MANT_LITERAL;
assert(nn->type == ROFFT_TEXT);
term_word(p, nn->string);
if (nn->flags & NODE_EOS)
p->flags |= TERMP_SENTENCE;
if (nn->next)
if (nn->next != NULL)
p->flags |= TERMP_NOSPACE;
}
return 0;
}
static int
pre_B(DECL_ARGS)
{
term_fontrepl(p, TERMFONT_BOLD);
return 1;
}
@ -332,20 +327,19 @@ pre_B(DECL_ARGS)
static int
pre_OP(DECL_ARGS)
{
term_word(p, "[");
p->flags |= TERMP_NOSPACE;
p->flags |= TERMP_KEEP | TERMP_NOSPACE;
if (NULL != (n = n->child)) {
if ((n = n->child) != NULL) {
term_fontrepl(p, TERMFONT_BOLD);
term_word(p, n->string);
}
if (NULL != n && NULL != n->next) {
if (n != NULL && n->next != NULL) {
term_fontrepl(p, TERMFONT_UNDER);
term_word(p, n->next->string);
}
term_fontrepl(p, TERMFONT_NONE);
p->flags &= ~TERMP_KEEP;
p->flags |= TERMP_NOSPACE;
term_word(p, "]");
return 0;
@ -369,9 +363,9 @@ pre_in(DECL_ARGS)
cp = n->child->string;
less = 0;
if ('-' == *cp)
if (*cp == '-')
less = -1;
else if ('+' == *cp)
else if (*cp == '+')
less = 1;
else
cp--;
@ -413,13 +407,18 @@ pre_HP(DECL_ARGS)
case ROFFT_BLOCK:
print_bvspace(p, n, mt->pardist);
return 1;
case ROFFT_HEAD:
return 0;
case ROFFT_BODY:
break;
default:
return 0;
abort();
}
if ( ! (MANT_LITERAL & mt->fl)) {
if (n->child == NULL)
return 0;
if ((n->child->flags & NODE_NOFILL) == 0) {
p->flags |= TERMP_NOBREAK | TERMP_BRIND;
p->trailspace = 2;
}
@ -445,8 +444,10 @@ pre_HP(DECL_ARGS)
static void
post_HP(DECL_ARGS)
{
switch (n->type) {
case ROFFT_BLOCK:
case ROFFT_HEAD:
break;
case ROFFT_BODY:
term_newln(p);
@ -466,25 +467,27 @@ post_HP(DECL_ARGS)
p->tcol->rmargin = p->maxrmargin;
break;
default:
break;
abort();
}
}
static int
pre_PP(DECL_ARGS)
{
switch (n->type) {
case ROFFT_BLOCK:
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
print_bvspace(p, n, mt->pardist);
break;
default:
case ROFFT_HEAD:
return 0;
case ROFFT_BODY:
p->tcol->offset = mt->offset;
break;
default:
abort();
}
return n->type != ROFFT_HEAD;
return 1;
}
static int
@ -492,21 +495,21 @@ pre_IP(DECL_ARGS)
{
struct roffsu su;
const struct roff_node *nn;
int len, savelit;
int len;
switch (n->type) {
case ROFFT_BODY:
p->flags |= TERMP_NOSPACE;
break;
case ROFFT_BLOCK:
print_bvspace(p, n, mt->pardist);
return 1;
case ROFFT_HEAD:
p->flags |= TERMP_NOBREAK;
p->trailspace = 1;
break;
case ROFFT_BLOCK:
print_bvspace(p, n, mt->pardist);
/* FALLTHROUGH */
case ROFFT_BODY:
p->flags |= TERMP_NOSPACE;
break;
default:
return 1;
abort();
}
/* Calculate the offset from the optional second argument. */
@ -526,33 +529,25 @@ pre_IP(DECL_ARGS)
case ROFFT_HEAD:
p->tcol->offset = mt->offset;
p->tcol->rmargin = mt->offset + len;
savelit = MANT_LITERAL & mt->fl;
mt->fl &= ~MANT_LITERAL;
if (n->child)
if (n->child != NULL)
print_man_node(p, mt, n->child, meta);
if (savelit)
mt->fl |= MANT_LITERAL;
return 0;
case ROFFT_BODY:
p->tcol->offset = mt->offset + len;
p->tcol->rmargin = p->maxrmargin;
break;
default:
break;
abort();
}
return 1;
}
static void
post_IP(DECL_ARGS)
{
switch (n->type) {
case ROFFT_BLOCK:
break;
case ROFFT_HEAD:
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
@ -564,7 +559,7 @@ post_IP(DECL_ARGS)
p->tcol->offset = mt->offset;
break;
default:
break;
abort();
}
}
@ -573,9 +568,13 @@ pre_TP(DECL_ARGS)
{
struct roffsu su;
struct roff_node *nn;
int len, savelit;
int len;
switch (n->type) {
case ROFFT_BLOCK:
if (n->tok == MAN_TP)
print_bvspace(p, n, mt->pardist);
return 1;
case ROFFT_HEAD:
p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
p->trailspace = 1;
@ -583,11 +582,8 @@ pre_TP(DECL_ARGS)
case ROFFT_BODY:
p->flags |= TERMP_NOSPACE;
break;
case ROFFT_BLOCK:
print_bvspace(p, n, mt->pardist);
/* FALLTHROUGH */
default:
return 1;
abort();
}
/* Calculate offset. */
@ -609,21 +605,15 @@ pre_TP(DECL_ARGS)
p->tcol->offset = mt->offset;
p->tcol->rmargin = mt->offset + len;
savelit = MANT_LITERAL & mt->fl;
mt->fl &= ~MANT_LITERAL;
/* Don't print same-line elements. */
nn = n->child;
while (NULL != nn && 0 == (NODE_LINE & nn->flags))
while (nn != NULL && (nn->flags & NODE_LINE) == 0)
nn = nn->next;
while (NULL != nn) {
while (nn != NULL) {
print_man_node(p, mt, nn, meta);
nn = nn->next;
}
if (savelit)
mt->fl |= MANT_LITERAL;
return 0;
case ROFFT_BODY:
p->tcol->offset = mt->offset + len;
@ -632,17 +622,17 @@ pre_TP(DECL_ARGS)
p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
break;
default:
break;
abort();
}
return 1;
}
static void
post_TP(DECL_ARGS)
{
switch (n->type) {
case ROFFT_BLOCK:
break;
case ROFFT_HEAD:
term_flushln(p);
break;
@ -651,7 +641,7 @@ post_TP(DECL_ARGS)
p->tcol->offset = mt->offset;
break;
default:
break;
abort();
}
}
@ -662,7 +652,6 @@ pre_SS(DECL_ARGS)
switch (n->type) {
case ROFFT_BLOCK:
mt->fl &= ~MANT_LITERAL;
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
mt->offset = term_len(p, p->defindent);
@ -674,7 +663,7 @@ pre_SS(DECL_ARGS)
do {
n = n->prev;
} while (n != NULL && n->tok >= MAN_TH &&
termacts[n->tok].flags & MAN_NOTEXT);
man_term_act(n->tok)->flags & MAN_NOTEXT);
if (n == NULL || n->type == ROFFT_COMMENT ||
(n->tok == MAN_SS && n->body->child == NULL))
break;
@ -698,26 +687,9 @@ pre_SS(DECL_ARGS)
default:
break;
}
return 1;
}
static void
post_SS(DECL_ARGS)
{
switch (n->type) {
case ROFFT_HEAD:
term_newln(p);
break;
case ROFFT_BODY:
term_newln(p);
break;
default:
break;
}
}
static int
pre_SH(DECL_ARGS)
{
@ -725,7 +697,6 @@ pre_SH(DECL_ARGS)
switch (n->type) {
case ROFFT_BLOCK:
mt->fl &= ~MANT_LITERAL;
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
mt->offset = term_len(p, p->defindent);
@ -737,7 +708,7 @@ pre_SH(DECL_ARGS)
do {
n = n->prev;
} while (n != NULL && n->tok >= MAN_TH &&
termacts[n->tok].flags & MAN_NOTEXT);
man_term_act(n->tok)->flags & MAN_NOTEXT);
if (n == NULL || n->type == ROFFT_COMMENT ||
(n->tok == MAN_SH && n->body->child == NULL))
break;
@ -759,25 +730,23 @@ pre_SH(DECL_ARGS)
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
break;
default:
break;
abort();
}
return 1;
}
static void
post_SH(DECL_ARGS)
{
switch (n->type) {
case ROFFT_HEAD:
term_newln(p);
case ROFFT_BLOCK:
break;
case ROFFT_HEAD:
case ROFFT_BODY:
term_newln(p);
break;
default:
break;
abort();
}
}
@ -792,8 +761,10 @@ pre_RS(DECL_ARGS)
return 1;
case ROFFT_HEAD:
return 0;
default:
case ROFFT_BODY:
break;
default:
abort();
}
n = n->parent->head;
@ -821,42 +792,99 @@ pre_RS(DECL_ARGS)
static void
post_RS(DECL_ARGS)
{
switch (n->type) {
case ROFFT_BLOCK:
return;
case ROFFT_HEAD:
return;
default:
term_newln(p);
case ROFFT_BODY:
break;
default:
abort();
}
term_newln(p);
mt->offset -= n->parent->head->aux;
p->tcol->offset = mt->offset;
if (--mt->lmarginsz < MAXMARGINS)
mt->lmargincur = mt->lmarginsz;
}
static int
pre_SY(DECL_ARGS)
{
const struct roff_node *nn;
int len;
switch (n->type) {
case ROFFT_BLOCK:
if (n->prev == NULL || n->prev->tok != MAN_SY)
print_bvspace(p, n, mt->pardist);
return 1;
case ROFFT_HEAD:
case ROFFT_BODY:
break;
default:
abort();
}
nn = n->parent->head->child;
len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1;
switch (n->type) {
case ROFFT_HEAD:
p->tcol->offset = mt->offset;
p->tcol->rmargin = mt->offset + len;
if (n->next->child == NULL ||
(n->next->child->flags & NODE_NOFILL) == 0)
p->flags |= TERMP_NOBREAK;
term_fontrepl(p, TERMFONT_BOLD);
break;
case ROFFT_BODY:
mt->lmargin[mt->lmargincur] = len;
p->tcol->offset = mt->offset + len;
p->tcol->rmargin = p->maxrmargin;
p->flags |= TERMP_NOSPACE;
break;
default:
abort();
}
return 1;
}
static void
post_SY(DECL_ARGS)
{
switch (n->type) {
case ROFFT_BLOCK:
break;
case ROFFT_HEAD:
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
break;
case ROFFT_BODY:
term_newln(p);
p->tcol->offset = mt->offset;
break;
default:
abort();
}
}
static int
pre_UR(DECL_ARGS)
{
return n->type != ROFFT_HEAD;
}
static void
post_UR(DECL_ARGS)
{
if (n->type != ROFFT_BLOCK)
return;
term_word(p, "<");
p->flags |= TERMP_NOSPACE;
if (NULL != n->child->child)
if (n->child->child != NULL)
print_man_node(p, mt, n->child->child, meta);
p->flags |= TERMP_NOSPACE;
@ -866,7 +894,8 @@ post_UR(DECL_ARGS)
static void
print_man_node(DECL_ARGS)
{
int c;
const struct man_term_act *act;
int c;
switch (n->type) {
case ROFFT_TEXT:
@ -884,6 +913,8 @@ print_man_node(DECL_ARGS)
} else if (*n->string == ' ' && n->flags & NODE_LINE &&
(p->flags & TERMP_NONEWLINE) == 0)
term_newln(p);
else if (n->flags & NODE_DELIMC)
p->flags |= TERMP_NOSPACE;
term_word(p, n->string);
goto out;
@ -910,20 +941,20 @@ print_man_node(DECL_ARGS)
return;
}
assert(n->tok >= MAN_TH && n->tok <= MAN_MAX);
if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
act = man_term_act(n->tok);
if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
term_fontrepl(p, TERMFONT_NONE);
c = 1;
if (termacts[n->tok].pre)
c = (*termacts[n->tok].pre)(p, mt, n, meta);
if (act->pre != NULL)
c = (*act->pre)(p, mt, n, meta);
if (c && n->child)
if (c && n->child != NULL)
print_man_nodelist(p, mt, n->child, meta);
if (termacts[n->tok].post)
(*termacts[n->tok].post)(p, mt, n, meta);
if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
if (act->post != NULL)
(*act->post)(p, mt, n, meta);
if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
term_fontrepl(p, TERMFONT_NONE);
out:
@ -934,7 +965,7 @@ print_man_node(DECL_ARGS)
* -man doesn't have nested macros, we don't need to be
* more specific than this.
*/
if (mt->fl & MANT_LITERAL &&
if (n->flags & NODE_NOFILL &&
! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
(n->next == NULL || n->next->flags & NODE_LINE)) {
p->flags |= TERMP_BRNEVER | TERMP_NOSPACE;
@ -949,15 +980,13 @@ print_man_node(DECL_ARGS)
p->tcol->rmargin = p->maxrmargin;
}
}
if (NODE_EOS & n->flags)
if (n->flags & NODE_EOS)
p->flags |= TERMP_SENTENCE;
}
static void
print_man_nodelist(DECL_ARGS)
{
while (n != NULL) {
print_man_node(p, mt, n, meta);
n = n->next;
@ -992,7 +1021,7 @@ print_man_foot(struct termp *p, const struct roff_meta *meta)
}
mandoc_asprintf(&title, "%s(%s)",
meta->title, meta->msec);
} else if (meta->os) {
} else if (meta->os != NULL) {
title = mandoc_strdup(meta->os);
} else {
title = mandoc_strdup("");

View File

@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $Id: man_validate.c,v 1.146 2018/12/31 10:04:39 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org>
@ -24,6 +24,7 @@
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@ -40,28 +41,32 @@
typedef void (*v_check)(CHKARGS);
static void check_abort(CHKARGS);
static void check_par(CHKARGS);
static void check_part(CHKARGS);
static void check_root(CHKARGS);
static void check_text(CHKARGS);
static void post_AT(CHKARGS);
static void post_EE(CHKARGS);
static void post_EX(CHKARGS);
static void post_IP(CHKARGS);
static void post_OP(CHKARGS);
static void post_SH(CHKARGS);
static void post_TH(CHKARGS);
static void post_UC(CHKARGS);
static void post_UR(CHKARGS);
static void post_in(CHKARGS);
static void post_vs(CHKARGS);
static const v_check __man_valids[MAN_MAX - MAN_TH] = {
static const v_check man_valids[MAN_MAX - MAN_TH] = {
post_TH, /* TH */
NULL, /* SH */
NULL, /* SS */
post_SH, /* SH */
post_SH, /* SS */
NULL, /* TP */
check_par, /* LP */
NULL, /* TQ */
check_abort,/* LP */
check_par, /* PP */
check_par, /* P */
check_abort,/* P */
post_IP, /* IP */
NULL, /* HP */
NULL, /* SM */
@ -75,8 +80,6 @@ static const v_check __man_valids[MAN_MAX - MAN_TH] = {
NULL, /* I */
NULL, /* IR */
NULL, /* RI */
NULL, /* nf */
NULL, /* fi */
NULL, /* RE */
check_part, /* RS */
NULL, /* DT */
@ -84,33 +87,56 @@ static const v_check __man_valids[MAN_MAX - MAN_TH] = {
NULL, /* PD */
post_AT, /* AT */
post_in, /* in */
NULL, /* SY */
NULL, /* YS */
post_OP, /* OP */
NULL, /* EX */
NULL, /* EE */
post_EX, /* EX */
post_EE, /* EE */
post_UR, /* UR */
NULL, /* UE */
post_UR, /* MT */
NULL, /* ME */
};
static const v_check *man_valids = __man_valids - MAN_TH;
/* Validate the subtree rooted at man->last. */
void
man_node_validate(struct roff_man *man)
man_validate(struct roff_man *man)
{
struct roff_node *n;
const v_check *cp;
/*
* Translate obsolete macros such that later code
* does not need to look for them.
*/
n = man->last;
switch (n->tok) {
case MAN_LP:
case MAN_P:
n->tok = MAN_PP;
break;
default:
break;
}
/*
* Iterate over all children, recursing into each one
* in turn, depth-first.
*/
man->last = man->last->child;
while (man->last != NULL) {
man_node_validate(man);
man_validate(man);
if (man->last == n)
man->last = man->last->child;
else
man->last = man->last->next;
}
/* Finally validate the macro itself. */
man->last = n;
man->next = ROFF_NEXT_SIBLING;
switch (n->type) {
@ -126,23 +152,15 @@ man_node_validate(struct roff_man *man)
break;
default:
if (n->tok < ROFF_MAX) {
switch (n->tok) {
case ROFF_br:
case ROFF_sp:
post_vs(man, n);
break;
default:
roff_validate(man);
break;
}
roff_validate(man);
break;
}
assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
cp = man_valids + n->tok;
cp = man_valids + (n->tok - MAN_TH);
if (*cp)
(*cp)(man, n);
if (man->last == n)
man_state(man, n);
n->flags |= NODE_VALID;
break;
}
}
@ -153,14 +171,12 @@ check_root(CHKARGS)
assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0);
if (n->last == NULL || n->last->type == ROFFT_COMMENT)
mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse,
n->line, n->pos, NULL);
mandoc_msg(MANDOCERR_DOC_EMPTY, n->line, n->pos, NULL);
else
man->meta.hasbody = 1;
if (NULL == man->meta.title) {
mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
n->line, n->pos, NULL);
mandoc_msg(MANDOCERR_TH_NOTITLE, n->line, n->pos, NULL);
/*
* If a title hasn't been set, do so now (by
@ -175,23 +191,43 @@ check_root(CHKARGS)
if (man->meta.os_e &&
(man->meta.rcsids & (1 << man->meta.os_e)) == 0)
mandoc_msg(MANDOCERR_RCS_MISSING, man->parse, 0, 0,
mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0,
man->meta.os_e == MANDOC_OS_OPENBSD ?
"(OpenBSD)" : "(NetBSD)");
}
static void
check_abort(CHKARGS)
{
abort();
}
static void
check_text(CHKARGS)
{
char *cp, *p;
if (MAN_LITERAL & man->flags)
if (n->flags & NODE_NOFILL)
return;
cp = n->string;
for (p = cp; NULL != (p = strchr(p, '\t')); p++)
mandoc_msg(MANDOCERR_FI_TAB, man->parse,
n->line, n->pos + (p - cp), NULL);
mandoc_msg(MANDOCERR_FI_TAB,
n->line, n->pos + (int)(p - cp), NULL);
}
static void
post_EE(CHKARGS)
{
if ((n->flags & NODE_NOFILL) == 0)
mandoc_msg(MANDOCERR_FI_SKIP, n->line, n->pos, "EE");
}
static void
post_EX(CHKARGS)
{
if (n->flags & NODE_NOFILL)
mandoc_msg(MANDOCERR_NF_SKIP, n->line, n->pos, "EX");
}
static void
@ -199,21 +235,55 @@ post_OP(CHKARGS)
{
if (n->child == NULL)
mandoc_msg(MANDOCERR_OP_EMPTY, man->parse,
n->line, n->pos, "OP");
mandoc_msg(MANDOCERR_OP_EMPTY, n->line, n->pos, "OP");
else if (n->child->next != NULL && n->child->next->next != NULL) {
n = n->child->next->next;
mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
mandoc_msg(MANDOCERR_ARG_EXCESS,
n->line, n->pos, "OP ... %s", n->string);
}
}
static void
post_SH(CHKARGS)
{
struct roff_node *nc;
if (n->type != ROFFT_BODY || (nc = n->child) == NULL)
return;
if (nc->tok == MAN_PP && nc->body->child != NULL) {
while (nc->body->last != NULL) {
man->next = ROFF_NEXT_CHILD;
roff_node_relink(man, nc->body->last);
man->last = n;
}
}
if (nc->tok == MAN_PP || nc->tok == ROFF_sp || nc->tok == ROFF_br) {
mandoc_msg(MANDOCERR_PAR_SKIP, nc->line, nc->pos,
"%s after %s", roff_name[nc->tok], roff_name[n->tok]);
roff_node_delete(man, nc);
}
/*
* Trailing PP is empty, so it is deleted by check_par().
* Trailing sp is significant.
*/
if ((nc = n->last) != NULL && nc->tok == ROFF_br) {
mandoc_msg(MANDOCERR_PAR_SKIP,
nc->line, nc->pos, "%s at the end of %s",
roff_name[nc->tok], roff_name[n->tok]);
roff_node_delete(man, nc);
}
}
static void
post_UR(CHKARGS)
{
if (n->type == ROFFT_HEAD && n->child == NULL)
mandoc_msg(MANDOCERR_UR_NOHEAD, man->parse,
n->line, n->pos, roff_name[n->tok]);
mandoc_msg(MANDOCERR_UR_NOHEAD, n->line, n->pos,
"%s", roff_name[n->tok]);
check_part(man, n);
}
@ -222,8 +292,8 @@ check_part(CHKARGS)
{
if (n->type == ROFFT_BODY && n->child == NULL)
mandoc_msg(MANDOCERR_BLK_EMPTY, man->parse,
n->line, n->pos, roff_name[n->tok]);
mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos,
"%s", roff_name[n->tok]);
}
static void
@ -236,15 +306,22 @@ check_par(CHKARGS)
roff_node_delete(man, n);
break;
case ROFFT_BODY:
if (n->child != NULL &&
(n->child->tok == ROFF_sp || n->child->tok == ROFF_br)) {
mandoc_msg(MANDOCERR_PAR_SKIP,
n->child->line, n->child->pos,
"%s after %s", roff_name[n->child->tok],
roff_name[n->tok]);
roff_node_delete(man, n->child);
}
if (n->child == NULL)
mandoc_vmsg(MANDOCERR_PAR_SKIP,
man->parse, n->line, n->pos,
mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos,
"%s empty", roff_name[n->tok]);
break;
case ROFFT_HEAD:
if (n->child != NULL)
mandoc_vmsg(MANDOCERR_ARG_SKIP,
man->parse, n->line, n->pos, "%s %s%s",
mandoc_msg(MANDOCERR_ARG_SKIP,
n->line, n->pos, "%s %s%s",
roff_name[n->tok], n->child->string,
n->child->next != NULL ? " ..." : "");
break;
@ -264,8 +341,7 @@ post_IP(CHKARGS)
break;
case ROFFT_BODY:
if (n->parent->head->child == NULL && n->child == NULL)
mandoc_vmsg(MANDOCERR_PAR_SKIP,
man->parse, n->line, n->pos,
mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos,
"%s empty", roff_name[n->tok]);
break;
default:
@ -298,9 +374,8 @@ post_TH(CHKARGS)
/* Only warn about this once... */
if (isalpha((unsigned char)*p) &&
! isupper((unsigned char)*p)) {
mandoc_vmsg(MANDOCERR_TITLE_CASE,
man->parse, n->line,
n->pos + (p - n->string),
mandoc_msg(MANDOCERR_TITLE_CASE, n->line,
n->pos + (int)(p - n->string),
"TH %s", n->string);
break;
}
@ -308,8 +383,7 @@ post_TH(CHKARGS)
man->meta.title = mandoc_strdup(n->string);
} else {
man->meta.title = mandoc_strdup("");
mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
nb->line, nb->pos, "TH");
mandoc_msg(MANDOCERR_TH_NOTITLE, nb->line, nb->pos, "TH");
}
/* TITLE ->MSEC<- DATE OS VOL */
@ -320,7 +394,7 @@ post_TH(CHKARGS)
man->meta.msec = mandoc_strdup(n->string);
else {
man->meta.msec = mandoc_strdup("");
mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse,
mandoc_msg(MANDOCERR_MSEC_MISSING,
nb->line, nb->pos, "TH %s", man->meta.title);
}
@ -334,7 +408,7 @@ post_TH(CHKARGS)
mandoc_normdate(man, n->string, n->line, n->pos);
} else {
man->meta.date = mandoc_strdup("");
mandoc_msg(MANDOCERR_DATE_MISSING, man->parse,
mandoc_msg(MANDOCERR_DATE_MISSING,
n ? n->line : nb->line,
n ? n->pos : nb->pos, "TH");
}
@ -362,7 +436,7 @@ post_TH(CHKARGS)
man->meta.vol = mandoc_strdup(p);
if (n != NULL && (n = n->next) != NULL)
mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
mandoc_msg(MANDOCERR_ARG_EXCESS,
n->line, n->pos, "TH ... %s", n->string);
/*
@ -463,32 +537,3 @@ post_in(CHKARGS)
free(n->child->string);
n->child->string = s;
}
static void
post_vs(CHKARGS)
{
if (NULL != n->prev)
return;
switch (n->parent->tok) {
case MAN_SH:
case MAN_SS:
case MAN_PP:
case MAN_LP:
case MAN_P:
mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos,
"%s after %s", roff_name[n->tok],
roff_name[n->parent->tok]);
/* FALLTHROUGH */
case TOKEN_NONE:
/*
* Don't warn about this because it occurs in pod2man
* and would cause considerable (unfixable) warnage.
*/
roff_node_delete(man, n);
break;
default:
break;
}
}

View File

@ -1,6 +1,6 @@
/* $Id: manconf.h,v 1.5 2017/07/01 09:47:30 schwarze Exp $ */
/* $Id: manconf.h,v 1.7 2018/11/22 11:30:23 schwarze Exp $ */
/*
* Copyright (c) 2011, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@ -30,12 +30,14 @@ struct manoutput {
char *man;
char *paper;
char *style;
char *tag;
size_t indent;
size_t width;
int fragment;
int mdoc;
int synopsisonly;
int noval;
int synopsisonly;
int toc;
};
struct manconf {

138
mandoc.1
View File

@ -1,4 +1,4 @@
.\" $Id: mandoc.1,v 1.226 2018/07/28 18:34:15 schwarze Exp $
.\" $Id: mandoc.1,v 1.237 2019/02/23 18:53:54 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2012, 2014-2018 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: July 28 2018 $
.Dd $Mdocdate: February 23 2019 $
.Dt MANDOC 1
.Os
.Sh NAME
@ -256,10 +256,28 @@ where
is the back-space character number 8.
Emboldened characters are rendered as
.Sq c Ns \e[bs] Ns c .
This markup is typically converted to appropriate terminal sequences by
the pager or
.Xr ul 1 .
To remove the markup, pipe the output to
.Xr col 1
.Fl b
instead.
.Pp
The special characters documented in
.Xr mandoc_char 7
are rendered best-effort in an ASCII equivalent.
In particular, opening and closing
.Sq single quotes
are represented as characters number 0x60 and 0x27, respectively,
which agrees with all ASCII standards from 1965 to the latest
revision (2012) and which matches the traditional way in which
.Xr roff 7
formatters represent single quotes in ASCII output.
This correct ASCII rendering may look strange with modern
Unicode-compatible fonts because contrary to ASCII, Unicode uses
the code point U+0060 for the grave accent only, never for an opening
quote.
.Pp
The following
.Fl O
@ -290,6 +308,26 @@ One useful application is for checking that
output formats in the same way as the
.Xr mdoc 7
source it was generated from.
.It Cm tag Ns Op = Ns Ar term
If the formatted manual page is opened in a pager,
go to the definition of the
.Ar term
rather than showing the manual page from the beginning.
If no
.Ar term
is specified, reuse the first command line argument that is not a
.Ar section
number.
If that argument is in
.Xr apropos 1
.Ar key Ns = Ns Ar val
format, only the
.Ar val
is used rather than the argument as a whole.
This is useful for commands like
.Ql man -akO tag Ic=ulimit
to search for a keyword and jump right to its definition
in the matching manual pages.
.It Cm width Ns = Ns Ar width
The output width is set to
.Ar width
@ -308,9 +346,9 @@ Equations rendered from
.Xr eqn 7
blocks use MathML.
.Pp
The
.Pa mandoc.css
file documents style-sheet classes available for customising output.
The file
.Pa /usr/share/misc/mandoc.css
documents style-sheet classes available for customising output.
If a style-sheet is not specified with
.Fl O Cm style ,
.Fl T Cm html
@ -345,7 +383,7 @@ Instances of
are replaced with the include filename.
The default is not to present a
hyperlink.
.It Cm man Ns = Ns Ar fmt
.It Cm man Ns = Ns Ar fmt Ns Op ; Ns Ar fmt
The string
.Ar fmt ,
for example,
@ -361,12 +399,19 @@ are replaced with the linked manual's name and section, respectively.
If no section is included, section 1 is assumed.
The default is not to
present a hyperlink.
If two formats are given and a file
.Ar %N.%S
exists in the current directory, the first format is used;
otherwise, the second format is used.
.It Cm style Ns = Ns Ar style.css
The file
.Ar style.css
is used for an external style-sheet.
This must be a valid absolute or
relative URI.
.It Cm toc
If an input file contains at least two non-standard sections,
print a table of contents near the beginning of the output.
.El
.Ss Locale Output
By default,
@ -667,10 +712,10 @@ To page manuals to the terminal:
.Dl $ mandoc -l mandoc.1 man.1 apropos.1 makewhatis.8
.Pp
To produce HTML manuals with
.Pa mandoc.css
.Pa /usr/share/misc/mandoc.css
as the style-sheet:
.Pp
.Dl $ mandoc \-T html -O style=mandoc.css mdoc.7 \*(Gt mdoc.7.html
.Dl $ mandoc \-T html -O style=/usr/share/misc/mandoc.css mdoc.7 > mdoc.7.html
.Pp
To check over a large set of manuals:
.Pp
@ -678,7 +723,7 @@ To check over a large set of manuals:
.Pp
To produce a series of PostScript manuals for A4 paper:
.Pp
.Dl $ mandoc \-T ps \-O paper=a4 mdoc.7 man.7 \*(Gt manuals.ps
.Dl $ mandoc \-T ps \-O paper=a4 mdoc.7 man.7 > manuals.ps
.Pp
Convert a modern
.Xr mdoc 7
@ -688,20 +733,36 @@ format, for use on systems lacking an
.Xr mdoc 7
parser:
.Pp
.Dl $ mandoc \-T man foo.mdoc \*(Gt foo.man
.Dl $ mandoc \-T man foo.mdoc > foo.man
.Sh DIAGNOSTICS
Messages displayed by
.Nm
follow this format:
.Bd -ragged -offset indent
.Nm :
.Ar file : Ns Ar line : Ns Ar column : level : message : macro args
.Ar file : Ns Ar line : Ns Ar column : level : message : macro arguments
.Pq Ar os
.Ed
.Pp
Line and column numbers start at 1.
The first three fields identify the
.Ar file
name,
.Ar line
number, and
.Ar column
number of the input file where the message was triggered.
The 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.
All
.Ar level
and
.Ar message
strings are explained below.
The name of the
.Ar macro
triggering the message and its
.Ar arguments
are omitted where meaningless.
The
.Ar os
operating system specifier is omitted for messages that are relevant
@ -1606,6 +1667,12 @@ or
.Cm off .
The invalid argument is moved out of the macro, which leaves the macro
empty, causing it to toggle the spacing mode.
.It Sy "argument contains two font escapes"
.Pq roff
The second argument of a
.Ic char
request contains more than one font escape sequence.
A wrong font may remain active after using the character.
.It Sy "unknown font, skipping request"
.Pq man , tbl
A
@ -1651,7 +1718,8 @@ Start it on a new input line to help formatters produce correct spacing.
.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.
closing argument delimiter, the argument is of an invalid form, or it is
a character escape sequence with an invalid name.
If the argument is incomplete,
.Ic \e*
and
@ -1664,6 +1732,12 @@ and
.Ic \ew
to the length of the incomplete argument.
All other invalid escape sequences are ignored.
.It Sy "undefined escape, printing literally"
.Pq roff
In an escape sequence, the first character
right after the leading backslash is invalid.
That character is printed literally,
which is equivalent to ignoring the backslash.
.It Sy "undefined string, using \(dq\(dq"
.Pq roff
If a string is used without being defined before,
@ -1807,6 +1881,13 @@ or
macro.
It may be mistyped or unsupported.
The request or macro is discarded including its arguments.
.It Sy "skipping request outside macro"
.Pq roff
A
.Ic shift
or
.Ic return
request occurs outside any macro definition and has no effect.
.It Sy "skipping insecure request"
.Pq roff
An input file attempted to run a shell command
@ -1916,6 +1997,14 @@ 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 "using macro argument outside macro"
.Pq roff
The escape sequence \e$ occurs outside any macro definition
and expands to the empty string.
.It Sy "argument number is not numeric"
.Pq roff
The argument of the escape sequence \e$ is not a digit;
the escape sequence expands to the empty string.
.It Sy "NOT IMPLEMENTED: Bd -file"
.Pq mdoc
For security reasons, the
@ -1944,6 +2033,13 @@ macro fails to specify the list type.
The argument of a
.Ic \&ce
request is not a number.
.It Sy "argument is not a character"
.Pq roff
The first argument of a
.Ic char
request is neither a single ASCII character
nor a single character escape sequence.
The request is ignored including all its arguments.
.It Sy "missing manual name, using \(dq\(dq"
.Pq mdoc
The first call to
@ -1978,6 +2074,13 @@ or
.Ic \&gsize
statement has a non-numeric or negative argument or no argument at all.
The invalid request or statement is ignored.
.It Sy "excessive shift"
.Pq roff
The argument of a
.Ic shift
request is larger than the number of arguments of the macro that is
currently being executed.
All macro arguments are deleted and \en(.$ is set to zero.
.It Sy "NOT IMPLEMENTED: .so with absolute path or \(dq..\(dq"
.Pq roff
For security reasons,
@ -2100,6 +2203,13 @@ implementations but not by
.Nm
was found in an input file.
It is replaced by a question mark.
.It Sy "unsupported escape sequence"
.Pq roff
An input file contains an escape sequence supported by GNU troff
or Heirloom troff but not by
.Nm ,
and it is likely that this will cause information loss
or considerable misformatting.
.It Sy "unsupported roff request"
.Pq roff
An input file contains a

187
mandoc.3
View File

@ -1,4 +1,4 @@
.\" $Id: mandoc.3,v 1.41 2017/07/04 23:40:01 schwarze Exp $
.\" $Id: mandoc.3,v 1.44 2018/12/30 00:49:55 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
@ -15,30 +15,23 @@
.\" 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 4 2017 $
.Dd $Mdocdate: December 30 2018 $
.Dt MANDOC 3
.Os
.Sh NAME
.Nm mandoc ,
.Nm deroff ,
.Nm mandocmsg ,
.Nm man_mparse ,
.Nm man_validate ,
.Nm mdoc_validate ,
.Nm mparse_alloc ,
.Nm mparse_copy ,
.Nm mparse_free ,
.Nm mparse_getkeep ,
.Nm mparse_keep ,
.Nm mparse_open ,
.Nm mparse_readfd ,
.Nm mparse_reset ,
.Nm mparse_result ,
.Nm mparse_strerror ,
.Nm mparse_strlevel ,
.Nm mparse_updaterc
.Nm mparse_result
.Nd mandoc macro compiler library
.Sh SYNOPSIS
.In sys/types.h
.In stdio.h
.In mandoc.h
.Pp
.Fd "#define ASCII_NBRSP"
@ -47,38 +40,23 @@
.Ft struct mparse *
.Fo mparse_alloc
.Fa "int options"
.Fa "enum mandocerr mmin"
.Fa "mandocmsg mmsg"
.Fa "enum mandoc_os oe_e"
.Fa "char *os_s"
.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 const char *
.Fo mparse_getkeep
.Fa "const struct mparse *parse"
.Fc
.Ft void
.Fo mparse_keep
.Fa "struct mparse *parse"
.Fo mparse_copy
.Fa "const struct mparse *parse"
.Fc
.Ft int
.Fo mparse_open
.Fa "struct mparse *parse"
.Fa "const char *fname"
.Fc
.Ft "enum mandoclevel"
.Ft void
.Fo mparse_readfd
.Fa "struct mparse *parse"
.Fa "int fd"
@ -88,24 +66,9 @@
.Fo mparse_reset
.Fa "struct mparse *parse"
.Fc
.Ft void
.Ft struct roff_meta *
.Fo mparse_result
.Fa "struct mparse *parse"
.Fa "struct roff_man **man"
.Fa "char **sodest"
.Fc
.Ft "const char *"
.Fo mparse_strerror
.Fa "enum mandocerr"
.Fc
.Ft "const char *"
.Fo mparse_strlevel
.Fa "enum mandoclevel"
.Fc
.Ft void
.Fo mparse_updaterc
.Fa "struct mparse *parse"
.Fa "enum mandoclevel *rc"
.Fc
.In roff.h
.Ft void
@ -118,22 +81,10 @@
.In mdoc.h
.Vt extern const char * const * mdoc_argnames;
.Vt extern const char * const * mdoc_macronames;
.Ft void
.Fo mdoc_validate
.Fa "struct roff_man *mdoc"
.Fc
.In sys/types.h
.In mandoc.h
.In man.h
.Vt extern const char * const * man_macronames;
.Ft "const struct mparse *"
.Fo man_mparse
.Fa "const struct roff_man *man"
.Fc
.Ft void
.Fo man_validate
.Fa "struct roff_man *man"
.Fc
.Sh DESCRIPTION
The
.Nm mandoc
@ -174,27 +125,13 @@ close it with
retrieve the syntax tree with
.Fn mparse_result ;
.It
depending on whether the
.Fa macroset
member of the returned
.Vt struct roff_man
is
.Dv MACROSET_MDOC
or
.Dv MACROSET_MAN ,
validate it with
.Fn mdoc_validate
or
.Fn man_validate ,
respectively;
.It
if information about the validity of the input is needed, fetch it with
.Fn mparse_updaterc ;
.It
iterate over parse nodes with starting from the
.Fa first
member of the returned
.Vt struct roff_man ;
.Vt struct roff_meta ;
.It
free all allocated memory with
.Fn mparse_free
@ -232,9 +169,6 @@ and freed with
This may be used across parsed input if
.Fn mparse_reset
is called between parses.
.It Vt "mandocmsg"
A prototype for a function to handle error and warning
messages emitted by the parser.
.El
.Ss Functions
.Bl -ohang
@ -245,35 +179,11 @@ including text contained in its child nodes.
To be used on children of the
.Fa first
member of
.Vt struct roff_man .
.Vt struct roff_meta .
When it is no longer needed, the pointer returned from
.Fn deroff
can be passed to
.Xr free 3 .
.It Fn man_mparse
Get the parser used for the current output.
Declared in
.In man.h ,
implemented in
.Pa man.c .
.It Fn man_validate
Validate the
.Dv MACROSET_MAN
parse tree obtained with
.Fn mparse_result .
Declared in
.In man.h ,
implemented in
.Pa man.c .
.It Fn mdoc_validate
Validate the
.Dv MACROSET_MDOC
parse tree obtained with
.Fn mparse_result .
Declared in
.In mdoc.h ,
implemented in
.Pa mdoc.c .
.It Fn mparse_alloc
Allocate a parser.
The arguments have the following effect:
@ -295,8 +205,8 @@ 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 .
field of
.Vt struct roff_meta .
.Pp
When the
.Dv MPARSE_QUICK
@ -305,24 +215,14 @@ This is for example useful in
.Xr makewhatis 8
.Fl Q
to quickly build minimal databases.
.It Ar mmin
Can be set to
.Dv MANDOCERR_BASE ,
.Dv MANDOCERR_STYLE ,
.Dv MANDOCERR_WARNING ,
.Dv MANDOCERR_ERROR ,
.Dv MANDOCERR_UNSUPP ,
or
.Dv MANDOCERR_MAX .
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.
If printing of error messages is not desired,
.Dv NULL
may be passed.
.Pp
When the
.Dv MARSE_VALIDATE
bit is set,
.Fn mparse_result
runs the validation functions before returning the syntax tree.
This is almost always required, except in certain debugging scenarios,
for example to dump unvalidated syntax trees.
.It Ar os_e
Operating system to check base system conventions for.
If
@ -361,19 +261,9 @@ Declared in
.In mandoc.h ,
implemented in
.Pa read.c .
.It Fn mparse_getkeep
Acquire the keep buffer.
Must follow a call of
.Fn mparse_keep .
Declared in
.In mandoc.h ,
implemented in
.Pa read.c .
.It Fn mparse_keep
Instruct the parser to retain a copy of its parsed input.
This can be acquired with subsequent
.Fn mparse_getkeep
calls.
.It Fn mparse_copy
Dump a copy of the input to the standard output; used for
.Fl man T Ns Cm man .
Declared in
.In mandoc.h ,
implemented in
@ -421,35 +311,6 @@ implemented in
.Pa read.c .
.It Fn mparse_result
Obtain the result of a parse.
One of the two pointers will be filled in.
Declared in
.In mandoc.h ,
implemented in
.Pa read.c .
.It Fn mparse_strerror
Return a statically-allocated string representation of an error code.
Declared in
.In mandoc.h ,
implemented in
.Pa read.c .
.It Fn mparse_strlevel
Return a statically-allocated string representation of a level code.
Declared in
.In mandoc.h ,
implemented in
.Pa read.c .
.It Fn mparse_updaterc
If the highest warning or error level that occurred during the current
.Fa parse
is higher than
.Pf * Fa rc ,
update
.Pf * Fa rc
accordingly.
This is useful after calling
.Fn mdoc_validate
or
.Fn man_validate .
Declared in
.In mandoc.h ,
implemented in

318
mandoc.c
View File

@ -1,4 +1,4 @@
/* $Id: mandoc.c,v 1.104 2018/07/28 18:34:15 schwarze Exp $ */
/* $Id: mandoc.c,v 1.114 2018/12/30 00:49:55 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
@ -32,16 +32,70 @@
#include "mandoc.h"
#include "roff.h"
#include "libmandoc.h"
#include "roff_int.h"
static int a2time(time_t *, const char *, const char *);
static char *time2a(time_t);
enum mandoc_esc
mandoc_font(const char *cp, int sz)
{
switch (sz) {
case 0:
return ESCAPE_FONTPREV;
case 1:
switch (cp[0]) {
case 'B':
case '3':
return ESCAPE_FONTBOLD;
case 'I':
case '2':
return ESCAPE_FONTITALIC;
case 'P':
return ESCAPE_FONTPREV;
case 'R':
case '1':
return ESCAPE_FONTROMAN;
case '4':
return ESCAPE_FONTBI;
default:
return ESCAPE_ERROR;
}
case 2:
switch (cp[0]) {
case 'B':
switch (cp[1]) {
case 'I':
return ESCAPE_FONTBI;
default:
return ESCAPE_ERROR;
}
case 'C':
switch (cp[1]) {
case 'B':
return ESCAPE_FONTBOLD;
case 'I':
return ESCAPE_FONTITALIC;
case 'R':
case 'W':
return ESCAPE_FONTCW;
default:
return ESCAPE_ERROR;
}
default:
return ESCAPE_ERROR;
}
default:
return ESCAPE_ERROR;
}
}
enum mandoc_esc
mandoc_escape(const char **end, const char **start, int *sz)
{
const char *local_start;
int local_sz;
int local_sz, c, i;
char term;
enum mandoc_esc gly;
@ -55,6 +109,14 @@ mandoc_escape(const char **end, const char **start, int *sz)
if (NULL == sz)
sz = &local_sz;
/*
* Treat "\E" just like "\";
* it only makes a difference in copy mode.
*/
if (**end == 'E')
++*end;
/*
* Beyond the backslash, at least one input character
* is part of the escape sequence. With one exception
@ -77,6 +139,10 @@ mandoc_escape(const char **end, const char **start, int *sz)
*sz = 2;
break;
case '[':
if (**start == ' ') {
++*end;
return ESCAPE_ERROR;
}
gly = ESCAPE_SPECIAL;
term = ']';
break;
@ -91,11 +157,26 @@ mandoc_escape(const char **end, const char **start, int *sz)
/*
* Escapes taking no arguments at all.
*/
case 'd':
case 'u':
case '!':
case '?':
return ESCAPE_UNSUPP;
case '%':
case '&':
case ')':
case ',':
case '/':
case '^':
case 'a':
case 'd':
case 'r':
case 't':
case 'u':
case '{':
case '|':
case '}':
return ESCAPE_IGNORE;
case 'c':
return ESCAPE_NOSPACE;
case 'p':
return ESCAPE_BREAK;
@ -113,32 +194,57 @@ mandoc_escape(const char **end, const char **start, int *sz)
* 'X' is the trigger. These have opaque sub-strings.
*/
case 'F':
case 'f':
case 'g':
case 'k':
case 'M':
case 'm':
case 'n':
case 'O':
case 'V':
case 'Y':
gly = ESCAPE_IGNORE;
/* FALLTHROUGH */
case 'f':
if (ESCAPE_ERROR == gly)
gly = ESCAPE_FONT;
gly = (*start)[-1] == 'f' ? ESCAPE_FONT : ESCAPE_IGNORE;
switch (**start) {
case '(':
if ((*start)[-1] == 'O')
gly = ESCAPE_ERROR;
*start = ++*end;
*sz = 2;
break;
case '[':
if ((*start)[-1] == 'O')
gly = (*start)[1] == '5' ?
ESCAPE_UNSUPP : ESCAPE_ERROR;
*start = ++*end;
term = ']';
break;
default:
if ((*start)[-1] == 'O') {
switch (**start) {
case '0':
gly = ESCAPE_UNSUPP;
break;
case '1':
case '2':
case '3':
case '4':
break;
default:
gly = ESCAPE_ERROR;
break;
}
}
*sz = 1;
break;
}
break;
case '*':
if (strncmp(*start, "(.T", 3) != 0)
abort();
gly = ESCAPE_DEVICE;
*start = ++*end;
*sz = 2;
break;
/*
* These escapes are of the form \X'Y', where 'X' is the trigger
@ -250,18 +356,29 @@ mandoc_escape(const char **end, const char **start, int *sz)
break;
/*
* Anything else is assumed to be a glyph.
* In this case, pass back the character after the backslash.
* Several special characters can be encoded as
* one-byte escape sequences without using \[].
*/
default:
case ' ':
case '\'':
case '-':
case '.':
case '0':
case ':':
case '_':
case '`':
case 'e':
case '~':
gly = ESCAPE_SPECIAL;
/* FALLTHROUGH */
default:
if (gly == ESCAPE_ERROR)
gly = ESCAPE_UNDEF;
*start = --*end;
*sz = 1;
break;
}
assert(ESCAPE_ERROR != gly);
/*
* Read up to the terminating character,
* paying attention to nested escapes.
@ -284,6 +401,15 @@ mandoc_escape(const char **end, const char **start, int *sz)
}
}
*sz = (*end)++ - *start;
/*
* The file chars.c only provides one common list
* of character names, but \[-] == \- is the only
* one of the characters with one-byte names that
* allows enclosing the name in brackets.
*/
if (gly == ESCAPE_SPECIAL && *sz == 1 && **start != '-')
return ESCAPE_ERROR;
} else {
assert(*sz > 0);
if ((size_t)*sz > strlen(*start))
@ -295,43 +421,25 @@ mandoc_escape(const char **end, const char **start, int *sz)
switch (gly) {
case ESCAPE_FONT:
if (2 == *sz) {
if ('C' == **start) {
/*
* Treat constant-width font modes
* just like regular font modes.
*/
(*start)++;
(*sz)--;
} else {
if ('B' == (*start)[0] && 'I' == (*start)[1])
gly = ESCAPE_FONTBI;
break;
}
} else if (1 != *sz)
break;
switch (**start) {
case '3':
case 'B':
gly = ESCAPE_FONTBOLD;
break;
case '2':
case 'I':
gly = ESCAPE_FONTITALIC;
break;
case 'P':
gly = ESCAPE_FONTPREV;
break;
case '1':
case 'R':
gly = ESCAPE_FONTROMAN;
break;
}
gly = mandoc_font(*start, *sz);
break;
case ESCAPE_SPECIAL:
if (1 == *sz && 'c' == **start)
gly = ESCAPE_NOSPACE;
if (**start == 'c') {
if (*sz < 6 || *sz > 7 ||
strncmp(*start, "char", 4) != 0 ||
(int)strspn(*start + 4, "0123456789") + 4 < *sz)
break;
c = 0;
for (i = 4; i < *sz; i++)
c = 10 * c + ((*start)[i] - '0');
if (c < 0x21 || (c > 0x7e && c < 0xa0) || c > 0xff)
break;
*start += 4;
*sz -= 4;
gly = ESCAPE_NUMBERED;
break;
}
/*
* Unicode escapes are defined in groff as \[u0000]
* to \[u10FFFF], where the contained value must be
@ -358,101 +466,6 @@ mandoc_escape(const char **end, const char **start, int *sz)
return gly;
}
/*
* Parse a quoted or unquoted roff-style request or macro argument.
* Return a pointer to the parsed argument, which is either the original
* pointer or advanced by one byte in case the argument is quoted.
* NUL-terminate the argument in place.
* Collapse pairs of quotes inside quoted arguments.
* Advance the argument pointer to the next argument,
* or to the NUL byte terminating the argument line.
*/
char *
mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
{
char *start, *cp;
int quoted, pairs, white;
/* Quoting can only start with a new word. */
start = *cpp;
quoted = 0;
if ('"' == *start) {
quoted = 1;
start++;
}
pairs = 0;
white = 0;
for (cp = start; '\0' != *cp; cp++) {
/*
* Move the following text left
* after quoted quotes and after "\\" and "\t".
*/
if (pairs)
cp[-pairs] = cp[0];
if ('\\' == cp[0]) {
/*
* In copy mode, translate double to single
* backslashes and backslash-t to literal tabs.
*/
switch (cp[1]) {
case 't':
cp[0] = '\t';
/* FALLTHROUGH */
case '\\':
pairs++;
cp++;
break;
case ' ':
/* Skip escaped blanks. */
if (0 == quoted)
cp++;
break;
default:
break;
}
} else if (0 == quoted) {
if (' ' == cp[0]) {
/* Unescaped blanks end unquoted args. */
white = 1;
break;
}
} else if ('"' == cp[0]) {
if ('"' == cp[1]) {
/* Quoted quotes collapse. */
pairs++;
cp++;
} else {
/* Unquoted quotes end quoted args. */
quoted = 2;
break;
}
}
}
/* Quoted argument without a closing quote. */
if (1 == quoted)
mandoc_msg(MANDOCERR_ARG_QUOTE, parse, ln, *pos, NULL);
/* NUL-terminate this argument and move to the next one. */
if (pairs)
cp[-pairs] = '\0';
if ('\0' != *cp) {
*cp++ = '\0';
while (' ' == *cp)
cp++;
}
*pos += (int)(cp - start) + (quoted ? 1 : 0);
*cpp = cp;
if ('\0' == *cp && (white || ' ' == cp[-1]))
mandoc_msg(MANDOCERR_SPACE_EOL, parse, ln, *pos, NULL);
return start;
}
static int
a2time(time_t *t, const char *fmt, const char *p)
{
@ -529,7 +542,7 @@ mandoc_normdate(struct roff_man *man, char *in, int ln, int pos)
/* No date specified: use today's date. */
if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) {
mandoc_msg(MANDOCERR_DATE_MISSING, man->parse, ln, pos, NULL);
mandoc_msg(MANDOCERR_DATE_MISSING, ln, pos, NULL);
return time2a(time(NULL));
}
@ -539,23 +552,20 @@ mandoc_normdate(struct roff_man *man, char *in, int ln, int pos)
a2time(&t, "%b %d, %Y", in)) {
cp = time2a(t);
if (t > time(NULL) + 86400)
mandoc_msg(MANDOCERR_DATE_FUTURE, man->parse,
ln, pos, cp);
mandoc_msg(MANDOCERR_DATE_FUTURE, ln, pos, "%s", cp);
else if (*in != '$' && strcmp(in, cp) != 0)
mandoc_msg(MANDOCERR_DATE_NORM, man->parse,
ln, pos, cp);
mandoc_msg(MANDOCERR_DATE_NORM, ln, pos, "%s", cp);
return cp;
}
/* In man(7), do not warn about the legacy format. */
if (a2time(&t, "%Y-%m-%d", in) == 0)
mandoc_msg(MANDOCERR_DATE_BAD, man->parse, ln, pos, in);
mandoc_msg(MANDOCERR_DATE_BAD, ln, pos, "%s", in);
else if (t > time(NULL) + 86400)
mandoc_msg(MANDOCERR_DATE_FUTURE, man->parse, ln, pos, in);
else if (man->macroset == MACROSET_MDOC)
mandoc_vmsg(MANDOCERR_DATE_LEGACY, man->parse,
ln, pos, "Dd %s", in);
mandoc_msg(MANDOCERR_DATE_FUTURE, ln, pos, "%s", in);
else if (man->meta.macroset == MACROSET_MDOC)
mandoc_msg(MANDOCERR_DATE_LEGACY, ln, pos, "Dd %s", in);
/* Use any non-mdoc(7) date verbatim. */

View File

@ -1,6 +1,11 @@
/* $Id: mandoc.css,v 1.36 2018/07/23 22:51:26 schwarze Exp $ */
/* $Id: mandoc.css,v 1.45 2019/03/01 10:57:18 schwarze Exp $ */
/*
* Standard style sheet for mandoc(1) -Thtml and man.cgi(8).
*
* Written by Ingo Schwarze <schwarze@openbsd.org>.
* I place this file into the public domain.
* Permission to use, copy, modify, and distribute it for any purpose
* with or without fee is hereby granted, without any conditions.
*/
/* Global defaults. */
@ -8,8 +13,16 @@
html { max-width: 65em; }
body { font-family: Helvetica,Arial,sans-serif; }
table { margin-top: 0em;
margin-bottom: 0em; }
td { vertical-align: top; }
margin-bottom: 0em;
border-collapse: collapse; }
/* Some browsers set border-color in a browser style for tbody,
* but not for table, resulting in inconsistent border styling. */
tbody { border-color: inherit; }
tr { border-color: inherit; }
td { vertical-align: top;
padding-left: 0.2em;
padding-right: 0.2em;
border-color: inherit; }
ul, ol, dl { margin-top: 0em;
margin-bottom: 0em; }
li, dt { margin-top: 1em; }
@ -52,12 +65,14 @@ td.foot-os { text-align: right; }
.manual-text {
margin-left: 3.8em; }
.Nd { display: inline; }
.Sh { margin-top: 1.2em;
.Nd { }
section.Sh { }
h1.Sh { margin-top: 1.2em;
margin-bottom: 0.6em;
margin-left: -3.2em;
font-size: 110%; }
.Ss { margin-top: 1.2em;
section.Ss { }
h2.Ss { margin-top: 1.2em;
margin-bottom: 0.6em;
margin-left: -1.2em;
font-size: 105%; }
@ -106,20 +121,25 @@ td.foot-os { text-align: right; }
.Bl-ohang > dt { }
.Bl-ohang > dd {
margin-left: 0em; }
.Bl-tag { margin-left: 5.5em; }
.Bl-tag { margin-top: 0.6em;
margin-left: 5.5em; }
.Bl-tag > dt {
float: left;
margin-top: 0em;
margin-left: -5.5em;
padding-right: 1.2em;
padding-right: 0.5em;
vertical-align: top; }
.Bl-tag > dd {
clear: right;
width: 100%;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0.6em;
vertical-align: top;
overflow: auto; }
.Bl-compact { margin-top: 0em; }
.Bl-compact > dd {
margin-bottom: 0em; }
.Bl-compact > dt {
margin-top: 0em; }
@ -151,7 +171,7 @@ td.foot-os { text-align: right; }
.RsV { }
.eqn { }
.tbl { }
.tbl td { vertical-align: middle; }
.HP { margin-left: 3.8em;
text-indent: -3.8em; }
@ -236,12 +256,86 @@ a.In { }
font-weight: normal;
font-family: monospace; }
/* Tooltip support. */
h1.Sh, h2.Ss { position: relative; }
.An, .Ar, .Cd, .Cm, .Dv, .Em, .Er, .Ev, .Fa, .Fd, .Fl, .Fn, .Ft,
.Ic, code.In, .Lb, .Lk, .Ms, .Mt, .Nd, code.Nm, .Pa, .Rs,
.St, .Sx, .Sy, .Va, .Vt, .Xr {
display: inline-block;
position: relative; }
.An::before { content: "An"; }
.Ar::before { content: "Ar"; }
.Cd::before { content: "Cd"; }
.Cm::before { content: "Cm"; }
.Dv::before { content: "Dv"; }
.Em::before { content: "Em"; }
.Er::before { content: "Er"; }
.Ev::before { content: "Ev"; }
.Fa::before { content: "Fa"; }
.Fd::before { content: "Fd"; }
.Fl::before { content: "Fl"; }
.Fn::before { content: "Fn"; }
.Ft::before { content: "Ft"; }
.Ic::before { content: "Ic"; }
code.In::before { content: "In"; }
.Lb::before { content: "Lb"; }
.Lk::before { content: "Lk"; }
.Ms::before { content: "Ms"; }
.Mt::before { content: "Mt"; }
.Nd::before { content: "Nd"; }
code.Nm::before { content: "Nm"; }
.Pa::before { content: "Pa"; }
.Rs::before { content: "Rs"; }
h1.Sh::before { content: "Sh"; }
h2.Ss::before { content: "Ss"; }
.St::before { content: "St"; }
.Sx::before { content: "Sx"; }
.Sy::before { content: "Sy"; }
.Va::before { content: "Va"; }
.Vt::before { content: "Vt"; }
.Xr::before { content: "Xr"; }
.An::before, .Ar::before, .Cd::before, .Cm::before,
.Dv::before, .Em::before, .Er::before, .Ev::before,
.Fa::before, .Fd::before, .Fl::before, .Fn::before, .Ft::before,
.Ic::before, code.In::before, .Lb::before, .Lk::before,
.Ms::before, .Mt::before, .Nd::before, code.Nm::before,
.Pa::before, .Rs::before,
h1.Sh::before, h2.Ss::before, .St::before, .Sx::before, .Sy::before,
.Va::before, .Vt::before, .Xr::before {
opacity: 0;
transition: .15s ease opacity;
pointer-events: none;
position: absolute;
bottom: 100%;
box-shadow: 0 0 .35em #000;
padding: .15em .25em;
white-space: nowrap;
font-family: Helvetica,Arial,sans-serif;
font-style: normal;
font-weight: bold;
color: black;
background: #fff; }
.An:hover::before, .Ar:hover::before, .Cd:hover::before, .Cm:hover::before,
.Dv:hover::before, .Em:hover::before, .Er:hover::before, .Ev:hover::before,
.Fa:hover::before, .Fd:hover::before, .Fl:hover::before, .Fn:hover::before,
.Ft:hover::before, .Ic:hover::before, code.In:hover::before,
.Lb:hover::before, .Lk:hover::before, .Ms:hover::before, .Mt:hover::before,
.Nd:hover::before, code.Nm:hover::before, .Pa:hover::before,
.Rs:hover::before, h1.Sh:hover::before, h2.Ss:hover::before, .St:hover::before,
.Sx:hover::before, .Sy:hover::before, .Va:hover::before, .Vt:hover::before,
.Xr:hover::before {
opacity: 1;
pointer-events: inherit; }
/* Overrides to avoid excessive margins on small devices. */
@media (max-width: 37.5em) {
.manual-text {
margin-left: 0.5em; }
.Sh, .Ss { margin-left: 0em; }
h1.Sh, h2.Ss { margin-left: 0em; }
.Bd-indent { margin-left: 2em; }
.Bl-hang > dd {
margin-left: 2em; }

241
mandoc.h
View File

@ -1,7 +1,7 @@
/* $Id: mandoc.h,v 1.248 2018/07/28 18:34:15 schwarze Exp $ */
/* $Id: mandoc.h,v 1.262 2018/12/16 00:17:02 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2012-2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -14,6 +14,8 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Error handling, escape sequence, and character utilities.
*/
#define ASCII_NBRSP 31 /* non-breaking space */
@ -158,6 +160,7 @@ enum mandocerr {
MANDOCERR_LB_BAD, /* unknown library name: Lb ... */
MANDOCERR_RS_BAD, /* invalid content in Rs block: macro */
MANDOCERR_SM_BAD, /* invalid Boolean argument: macro arg */
MANDOCERR_CHAR_FONT, /* argument contains two font escapes */
MANDOCERR_FT_BAD, /* unknown font, skipping request: ft font */
MANDOCERR_TR_ODD, /* odd number of characters in request: tr char */
@ -166,6 +169,7 @@ enum mandocerr {
MANDOCERR_FI_TAB, /* tab in filled text */
MANDOCERR_EOS, /* new sentence, new line */
MANDOCERR_ESC_BAD, /* invalid escape sequence: esc */
MANDOCERR_ESC_UNDEF, /* undefined escape, printing literally: char */
MANDOCERR_STR_UNDEF, /* undefined string, using "": name */
/* related to tables */
@ -195,6 +199,7 @@ enum mandocerr {
MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */
MANDOCERR_CHAR_BAD, /* skipping bad character: number */
MANDOCERR_MACRO, /* skipping unknown macro: macro */
MANDOCERR_REQ_NOMAC, /* skipping request outside macro: ... */
MANDOCERR_REQ_INSEC, /* skipping insecure request: request */
MANDOCERR_IT_STRAY, /* skipping item outside list: It ... */
MANDOCERR_TA_STRAY, /* skipping column outside column list: Ta */
@ -205,14 +210,18 @@ enum mandocerr {
/* related to request and macro arguments */
MANDOCERR_NAMESC, /* escaped character not allowed in a name: name */
MANDOCERR_ARG_UNDEF, /* using macro argument outside macro */
MANDOCERR_ARG_NONUM, /* argument number is not numeric */
MANDOCERR_BD_FILE, /* NOT IMPLEMENTED: Bd -file */
MANDOCERR_BD_NOARG, /* skipping display without arguments: Bd */
MANDOCERR_BL_NOTYPE, /* missing list type, using -item: Bl */
MANDOCERR_CE_NONUM, /* argument is not numeric, using 1: ce ... */
MANDOCERR_CHAR_ARG, /* argument is not a character: char ... */
MANDOCERR_NM_NONAME, /* missing manual name, using "": Nm */
MANDOCERR_OS_UNAME, /* uname(3) system call failed, using UNKNOWN */
MANDOCERR_ST_BAD, /* unknown standard specifier: St standard */
MANDOCERR_IT_NONUM, /* skipping request without numeric argument */
MANDOCERR_SHIFT, /* excessive shift: ..., but max is ... */
MANDOCERR_SO_PATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */
MANDOCERR_SO_FAIL, /* .so request failed */
MANDOCERR_ARG_SKIP, /* skipping all arguments: macro args */
@ -223,7 +232,12 @@ enum mandocerr {
MANDOCERR_TOOLARGE, /* input too large */
MANDOCERR_CHAR_UNSUPP, /* unsupported control character: number */
MANDOCERR_ESC_UNSUPP, /* unsupported escape sequence: escape */
MANDOCERR_REQ_UNSUPP, /* unsupported roff request: request */
MANDOCERR_WHILE_NEST, /* nested .while loops */
MANDOCERR_WHILE_OUTOF, /* end of scope with open .while loop */
MANDOCERR_WHILE_INTO, /* end of .while loop in inner scope */
MANDOCERR_WHILE_FAIL, /* cannot continue this .while loop */
MANDOCERR_TBLOPT_EQN, /* eqn delim option in tbl: arg */
MANDOCERR_TBLLAYOUT_MOD, /* unsupported tbl layout modifier: m */
MANDOCERR_TBLMACRO, /* ignoring macro in table: macro */
@ -231,206 +245,22 @@ enum mandocerr {
MANDOCERR_MAX
};
struct tbl_opts {
char tab; /* cell-separator */
char decimal; /* decimal point */
int opts;
#define TBL_OPT_CENTRE (1 << 0)
#define TBL_OPT_EXPAND (1 << 1)
#define TBL_OPT_BOX (1 << 2)
#define TBL_OPT_DBOX (1 << 3)
#define TBL_OPT_ALLBOX (1 << 4)
#define TBL_OPT_NOKEEP (1 << 5)
#define TBL_OPT_NOSPACE (1 << 6)
#define TBL_OPT_NOWARN (1 << 7)
int cols; /* number of columns */
int lvert; /* width of left vertical line */
int rvert; /* width of right vertical line */
};
enum tbl_cellt {
TBL_CELL_CENTRE, /* c, C */
TBL_CELL_RIGHT, /* r, R */
TBL_CELL_LEFT, /* l, L */
TBL_CELL_NUMBER, /* n, N */
TBL_CELL_SPAN, /* s, S */
TBL_CELL_LONG, /* a, A */
TBL_CELL_DOWN, /* ^ */
TBL_CELL_HORIZ, /* _, - */
TBL_CELL_DHORIZ, /* = */
TBL_CELL_MAX
};
/*
* A cell in a layout row.
*/
struct tbl_cell {
struct tbl_cell *next;
char *wstr; /* min width represented as a string */
size_t width; /* minimum column width */
size_t spacing; /* to the right of the column */
int vert; /* width of subsequent vertical line */
int col; /* column number, starting from 0 */
int flags;
#define TBL_CELL_TALIGN (1 << 0) /* t, T */
#define TBL_CELL_BALIGN (1 << 1) /* d, D */
#define TBL_CELL_BOLD (1 << 2) /* fB, B, b */
#define TBL_CELL_ITALIC (1 << 3) /* fI, I, i */
#define TBL_CELL_EQUAL (1 << 4) /* e, E */
#define TBL_CELL_UP (1 << 5) /* u, U */
#define TBL_CELL_WIGN (1 << 6) /* z, Z */
#define TBL_CELL_WMAX (1 << 7) /* x, X */
enum tbl_cellt pos;
};
/*
* A layout row.
*/
struct tbl_row {
struct tbl_row *next;
struct tbl_cell *first;
struct tbl_cell *last;
int vert; /* width of left vertical line */
};
enum tbl_datt {
TBL_DATA_NONE, /* has no data */
TBL_DATA_DATA, /* consists of data/string */
TBL_DATA_HORIZ, /* horizontal line */
TBL_DATA_DHORIZ, /* double-horizontal line */
TBL_DATA_NHORIZ, /* squeezed horizontal line */
TBL_DATA_NDHORIZ /* squeezed double-horizontal line */
};
/*
* A cell within a row of data. The "string" field contains the actual
* string value that's in the cell. The rest is layout.
*/
struct tbl_dat {
struct tbl_cell *layout; /* layout cell */
struct tbl_dat *next;
char *string; /* data (NULL if not TBL_DATA_DATA) */
int spans; /* how many spans follow */
int block; /* T{ text block T} */
enum tbl_datt pos;
};
enum tbl_spant {
TBL_SPAN_DATA, /* span consists of data */
TBL_SPAN_HORIZ, /* span is horizontal line */
TBL_SPAN_DHORIZ /* span is double horizontal line */
};
/*
* A row of data in a table.
*/
struct tbl_span {
struct tbl_opts *opts;
struct tbl_row *layout; /* layout row */
struct tbl_dat *first;
struct tbl_dat *last;
struct tbl_span *prev;
struct tbl_span *next;
int line; /* parse line */
enum tbl_spant pos;
};
enum eqn_boxt {
EQN_TEXT, /* text (number, variable, whatever) */
EQN_SUBEXPR, /* nested `eqn' subexpression */
EQN_LIST, /* list (braces, etc.) */
EQN_PILE, /* vertical pile */
EQN_MATRIX /* pile of piles */
};
enum eqn_fontt {
EQNFONT_NONE = 0,
EQNFONT_ROMAN,
EQNFONT_BOLD,
EQNFONT_FAT,
EQNFONT_ITALIC,
EQNFONT__MAX
};
enum eqn_post {
EQNPOS_NONE = 0,
EQNPOS_SUP,
EQNPOS_SUBSUP,
EQNPOS_SUB,
EQNPOS_TO,
EQNPOS_FROM,
EQNPOS_FROMTO,
EQNPOS_OVER,
EQNPOS_SQRT,
EQNPOS__MAX
};
enum eqn_pilet {
EQNPILE_NONE = 0,
EQNPILE_PILE,
EQNPILE_CPILE,
EQNPILE_RPILE,
EQNPILE_LPILE,
EQNPILE_COL,
EQNPILE_CCOL,
EQNPILE_RCOL,
EQNPILE_LCOL,
EQNPILE__MAX
};
/*
* A "box" is a parsed mathematical expression as defined by the eqn.7
* grammar.
*/
struct eqn_box {
int size; /* font size of expression */
#define EQN_DEFSIZE INT_MIN
enum eqn_boxt type; /* type of node */
struct eqn_box *first; /* first child node */
struct eqn_box *last; /* last child node */
struct eqn_box *next; /* node sibling */
struct eqn_box *prev; /* node sibling */
struct eqn_box *parent; /* node sibling */
char *text; /* text (or NULL) */
char *left; /* fence left-hand */
char *right; /* fence right-hand */
char *top; /* expression over-symbol */
char *bottom; /* expression under-symbol */
size_t args; /* arguments in parent */
size_t expectargs; /* max arguments in parent */
enum eqn_post pos; /* position of next box */
enum eqn_fontt font; /* font of box */
enum eqn_pilet pile; /* equation piling */
};
/*
* Parse options.
*/
#define MPARSE_MDOC 1 /* assume -mdoc */
#define MPARSE_MAN 2 /* assume -man */
#define MPARSE_SO 4 /* honour .so requests */
#define MPARSE_QUICK 8 /* abort the parse early */
#define MPARSE_UTF8 16 /* accept UTF-8 input */
#define MPARSE_LATIN1 32 /* accept ISO-LATIN-1 input */
enum mandoc_os {
MANDOC_OS_OTHER = 0,
MANDOC_OS_NETBSD,
MANDOC_OS_OPENBSD
};
enum mandoc_esc {
ESCAPE_ERROR = 0, /* bail! unparsable escape */
ESCAPE_UNSUPP, /* unsupported escape; ignore it */
ESCAPE_IGNORE, /* escape to be ignored */
ESCAPE_UNDEF, /* undefined escape; print literal character */
ESCAPE_SPECIAL, /* a regular special character */
ESCAPE_FONT, /* a generic font mode */
ESCAPE_FONTBOLD, /* bold font mode */
ESCAPE_FONTITALIC, /* italic font mode */
ESCAPE_FONTBI, /* bold italic font mode */
ESCAPE_FONTROMAN, /* roman font mode */
ESCAPE_FONTCW, /* constant width font mode */
ESCAPE_FONTPREV, /* previous font mode */
ESCAPE_NUMBERED, /* a numbered glyph */
ESCAPE_UNICODE, /* a unicode codepoint */
ESCAPE_DEVICE, /* print the output device name */
ESCAPE_BREAK, /* break the output line */
ESCAPE_NOSPACE, /* suppress space if the last on a line */
ESCAPE_HORIZ, /* horizontal movement */
@ -439,14 +269,18 @@ enum mandoc_esc {
ESCAPE_OVERSTRIKE /* overstrike all chars in the argument */
};
typedef void (*mandocmsg)(enum mandocerr, enum mandoclevel,
const char *, int, int, const char *);
struct mparse;
struct roff_man;
enum mandoc_esc mandoc_font(const char *, int sz);
enum mandoc_esc mandoc_escape(const char **, const char **, int *);
void mandoc_msg_setoutfile(FILE *);
const char *mandoc_msg_getinfilename(void);
void mandoc_msg_setinfilename(const char *);
enum mandocerr mandoc_msg_getmin(void);
void mandoc_msg_setmin(enum mandocerr);
enum mandoclevel mandoc_msg_getrc(void);
void mandoc_msg_setrc(enum mandoclevel);
void mandoc_msg(enum mandocerr, int, int, const char *, ...)
__attribute__((__format__ (__printf__, 4, 5)));
void mchars_alloc(void);
void mchars_free(void);
int mchars_num2char(const char *, size_t);
@ -454,18 +288,3 @@ const char *mchars_uc2str(int);
int mchars_num2uc(const char *, size_t);
int mchars_spec2cp(const char *, size_t);
const char *mchars_spec2str(const char *, size_t, size_t *);
struct mparse *mparse_alloc(int, enum mandocerr, mandocmsg,
enum mandoc_os, const char *);
void mparse_free(struct mparse *);
void mparse_keep(struct mparse *);
int mparse_open(struct mparse *, const char *);
enum mandoclevel mparse_readfd(struct mparse *, int, const char *);
enum mandoclevel mparse_readmem(struct mparse *, void *, size_t,
const char *);
void mparse_reset(struct mparse *);
void mparse_result(struct mparse *,
struct roff_man **, char **);
const char *mparse_getkeep(const struct mparse *);
const char *mparse_strerror(enum mandocerr);
const char *mparse_strlevel(enum mandoclevel);
void mparse_updaterc(struct mparse *, enum mandoclevel *);

View File

@ -1,8 +1,8 @@
.\" $Id: mandoc_char.7,v 1.72 2018/08/08 14:30:48 schwarze Exp $
.\" $Id: mandoc_char.7,v 1.75 2018/12/15 19:30:26 schwarze Exp $
.\"
.\" Copyright (c) 2003 Jason McIntyre <jmc@openbsd.org>
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2011, 2013, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2011,2013,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -16,7 +16,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: August 8 2018 $
.Dd $Mdocdate: December 15 2018 $
.Dt MANDOC_CHAR 7
.Os
.Sh NAME
@ -266,11 +266,13 @@ Spacing:
.It Em Input Ta Em Description
.It Sq \e\ \& Ta unpaddable non-breaking space
.It \e\(ti Ta paddable non-breaking space
.It \e0 Ta unpaddable, breaking digit-width space
.It \e0 Ta digit-width space allowing line break
.It \e| Ta one-sixth \e(em narrow space, zero width in nroff mode
.It \e^ Ta one-twelfth \e(em half-narrow space, zero width in nroff
.It \e& Ta zero-width space
.It \e& Ta zero-width non-breaking space
.It \e) Ta zero-width space transparent to end-of-sentence detection
.It \e% Ta zero-width space allowing hyphenation
.It \e: Ta zero-width space allowing line break
.El
.Pp
Lines:
@ -543,11 +545,13 @@ Accented letters:
.It \e(\(aqI Ta \('I Ta acute I
.It \e(\(aqO Ta \('O Ta acute O
.It \e(\(aqU Ta \('U Ta acute U
.It \e(\(aqY Ta \('Y Ta acute Y
.It \e(\(aqa Ta \('a Ta acute a
.It \e(\(aqe Ta \('e Ta acute e
.It \e(\(aqi Ta \('i Ta acute i
.It \e(\(aqo Ta \('o Ta acute o
.It \e(\(aqu Ta \('u Ta acute u
.It \e(\(aqy Ta \('y Ta acute y
.It \e(\(gaA Ta \(`A Ta grave A
.It \e(\(gaE Ta \(`E Ta grave E
.It \e(\(gaI Ta \(`I Ta grave I
@ -761,14 +765,16 @@ For backward compatibility with existing manuals,
.Xr mandoc 1
also supports the
.Pp
.Dl \eN\(aq Ns Ar number Ns \(aq
.Dl \eN\(aq Ns Ar number Ns \(aq and \e[ Ns Cm char Ns Ar number ]
.Pp
escape sequence, inserting the character
escape sequences, inserting the character
.Ar number
from the current character set into the output.
Of course, this is inherently non-portable and is already marked
as deprecated in the Heirloom roff manual.
For example, do not use \eN\(aq34\(aq, use \e(dq, or even the plain
as deprecated in the Heirloom roff manual;
on top of that, the second form is a GNU extension.
For example, do not use \eN\(aq34\(aq or \e[char34], use \e(dq,
or even the plain
.Sq \(dq
character where possible.
.Sh COMPATIBILITY

View File

@ -1,4 +1,4 @@
.Dd $Mdocdate: July 8 2017 $
.Dd $Mdocdate: December 30 2018 $
.Dt MANDOC_HEADERS 3
.Os
.Sh NAME
@ -25,15 +25,15 @@ separate from each other:
.Pp
.Bl -dash -offset indent -compact
.It
.Xr roff 7
parser
.It
.Xr mdoc 7
parser
.It
.Xr man 7
parser
.It
.Xr roff 7
parser
.It
.Xr tbl 7
parser
.It
@ -45,6 +45,8 @@ terminal formatters
HTML formatters
.It
search tools
.It
main programs
.El
.Pp
Note that mere usage of an opaque struct type does
@ -56,14 +58,18 @@ any other mandoc header.
These headers should be included before any other mandoc headers.
.Bl -tag -width Ds
.It Qq Pa mandoc_aux.h
Memory allocation utility functions; can be used everywhere.
.Pp
Requires
.In sys/types.h
for
.Vt size_t .
.Pp
Provides the utility functions documented in
Provides the functions documented in
.Xr mandoc_malloc 3 .
.It Qq Pa mandoc_ohash.h
Hashing utility functions; can be used everywhere.
.Pp
Requires
.In stddef.h
for
@ -78,73 +84,37 @@ Includes
and provides
.Fn mandoc_ohash_init .
.It Qq Pa mandoc.h
Error handling, escape sequence, and character utilities;
can be used everywhere.
.Pp
Requires
.In sys/types.h
for
.Vt size_t .
.Vt size_t
and
.In stdio.h
for
.Vt FILE .
.Pp
Provides
.Vt enum mandoc_esc ,
.Vt enum mandocerr ,
.Vt enum mandoclevel ,
.Vt enum mandoc_os ,
.Vt enum tbl_cellt ,
.Vt enum tbl_datt ,
.Vt enum tbl_spant ,
.Vt enum eqn_boxt ,
.Vt enum eqn_fontt ,
.Vt enum eqn_pilet ,
.Vt enum eqn_post ,
.Vt struct tbl_opts ,
.Vt struct tbl_cell ,
.Vt struct tbl_row ,
.Vt struct tbl_dat ,
.Vt struct tbl_span ,
.Vt struct eqn_box ,
the function prototype typedef
.Fn mandocmsg ,
the function
.Xr mandoc_escape 3 ,
the functions described in
.Xr mchars_alloc 3 ,
and the functions
.Fn mparse_*
described in
.Xr mandoc 3 .
.Pp
Uses the opaque type
.Vt struct mparse
from
.Pa read.c
for function prototypes.
Uses the type
.Vt struct roff_man
from
.Pa roff.h
as an opaque type for function prototypes.
.It Qq Pa mandoc_xr.h
Provides
.Vt struct mandoc_xr
and the functions
.Fn mandoc_xr_reset ,
.Fn mandoc_xr_add ,
.Fn mandoc_xr_get ,
and
.Fn mandoc_xr_free .
and the
.Fn mandoc_msg*
functions.
.It Qq Pa roff.h
Requires
.Qq Pa mandoc_ohash.h
for
.Vt struct ohash
and
.Qq Pa mandoc.h
for
.Vt enum mandoc_os .
Common data types for all syntax trees and related functions;
can be used everywhere.
.Pp
Provides
.Vt enum mandoc_os ,
.Vt enum mdoc_endbody ,
.Vt enum roff_macroset ,
.Vt enum roff_next ,
.Vt enum roff_sec ,
.Vt enum roff_tok ,
.Vt enum roff_type ,
@ -153,21 +123,99 @@ Provides
.Vt struct roff_node ,
the constant array
.Va roff_name
and the functions
.Fn deroff ,
.Fn roffhash_alloc ,
.Fn roffhash_find ,
.Fn roffhash_free ,
and
.Fn roff_validate .
and the function
.Fn deroff .
.Pp
Uses pointers to the types
.Vt struct ohash
from
.Pa mandoc_ohash.h ,
.Vt struct mdoc_arg
and
.Vt union mdoc_data
from
.Pa mdoc.h
.Pa mdoc.h ,
.Vt struct tbl_span
from
.Pa tbl.h ,
and
.Vt struct eqn_box
from
.Pa eqn.h
as opaque struct members.
.It Qq Pa tbl.h
Data structures for the
.Xr tbl 7
parse tree; can be used everywhere.
.Pp
Requires
.In sys/types.h
for
.Vt size_t .
.Pp
Provides
.Vt enum tbl_cellt ,
.Vt enum tbl_datt ,
.Vt enum tbl_spant ,
.Vt struct tbl_opts ,
.Vt struct tbl_cell ,
.Vt struct tbl_row ,
.Vt struct tbl_dat ,
and
.Vt struct tbl_span .
.It Qq Pa eqn.h
Data structures for the
.Xr eqn 7
parse tree; can be used everywhere.
.Pp
Requires
.In sys/types.h
for
.Vt size_t .
.Pp
Provides
.Vt enum eqn_boxt ,
.Vt enum eqn_fontt ,
.Vt enum eqn_post ,
and
.Vt struct eqn_box .
.It Qq Pa mandoc_parse.h
Top level parser interface, for use in the main program
and in the main parser, but not in formatters.
.Pp
Requires
.Pa mandoc.h
for
.Vt enum mandocerr
and
.Vt enum mandoclevel
and
.Pa roff.h
for
.Vt enum mandoc_os .
.Pp
Uses the opaque type
.Vt struct mparse
from
.Pa read.c
for function prototypes.
Uses
.Vt struct roff_meta
from
.Pa roff.h
as an opaque type for function prototypes.
.It Qq Pa mandoc_xr.h
Cross reference validation; intended for use in the main program
and in parsers, but not in formatters.
.Pp
Provides
.Vt struct mandoc_xr
and the functions
.Fn mandoc_xr_reset ,
.Fn mandoc_xr_add ,
.Fn mandoc_xr_get ,
and
.Fn mandoc_xr_free .
.El
.Pp
The following two require
@ -200,27 +248,24 @@ and the functions
described in
.Xr mandoc 3 .
.Pp
Uses the type
.Vt struct roff_man
Uses the types
.Vt struct roff_node
from
.Pa roff.h
as an opaque type for function prototypes.
and
.Vt struct roff_man
from
.Pa roff_int.h
as opaque types for function prototypes.
.Pp
When this header is included, the same file should not include
.Pa libman.h
or
.Pa libroff.h .
internals of different parsers.
.It Qq Pa man.h
Provides the functions
.Fn man_*
described in
.Xr mandoc 3 .
.Pp
Uses the opaque type
.Vt struct mparse
from
.Pa read.c
for function prototypes.
Uses the type
.Vt struct roff_man
from
@ -228,12 +273,10 @@ from
as an opaque type for function prototypes.
.Pp
When this header is included, the same file should not include
.Pa libmdoc.h
or
.Pa libroff.h .
internals of different parsers.
.El
.Ss Parser internals
The following headers require inclusion of a parser interface header
Most of the following headers require inclusion of a parser interface header
before they can be included.
All parser interface headers should precede all parser internal headers.
When any parser internal headers are included, the same file should
@ -250,16 +293,11 @@ for
.Vt enum mandocerr .
.Pp
Provides
.Vt enum rofferr ,
.Vt struct buf ,
utility functions needed by multiple parsers,
and the top-level functions to call the parsers.
.Pp
Uses the opaque types
.Vt struct mparse
from
.Pa read.c
and
Uses the opaque type
.Vt struct roff
from
.Pa roff.c
@ -270,14 +308,28 @@ from
.Pa roff.h
as an opaque type for function prototypes.
.It Qq Pa roff_int.h
Parser internals shared by multiple parsers.
Can be used in all parsers, but not in main programs or formatters.
.Pp
Requires
.Qq Pa roff.h
for
.Vt enum roff_type .
.Vt enum roff_type
and
.Vt enum roff_tok .
.Pp
Provides functions named
Provides
.Vt enum roff_next ,
.Vt struct roff_man ,
functions named
.Fn roff_*
to handle roff nodes and the two special functions
to handle roff nodes,
.Fn roffhash_alloc ,
.Fn roffhash_find ,
.Fn roffhash_free ,
and
.Fn roff_validate ,
and the two special functions
.Fn man_breakscope
and
.Fn mdoc_argv_free
@ -285,11 +337,17 @@ because the latter two are needed by
.Qq Pa roff.c .
.Pp
Uses the types
.Vt struct roff_man
and
.Vt struct roff_node
.Vt struct ohash
from
.Pa roff.h
.Pa mandoc_ohash.h ,
.Vt struct roff_node
and
.Vt struct roff_meta
from
.Pa roff.h ,
.Vt struct roff
from
.Pa roff.c ,
and
.Vt struct mdoc_arg
from
@ -301,11 +359,7 @@ Requires
for
.Vt enum roff_tok
and
.Qq Pa mdoc.h
for
.Vt enum mdoc_*
and
.Vt struct mdoc_* .
.Vt enum roff_sec .
.Pp
Provides
.Vt enum margserr ,
@ -315,23 +369,21 @@ and many functions internal to the
.Xr mdoc 7
parser.
.Pp
Uses the opaque type
.Vt struct mparse
from
.Pa read.c .
Uses the types
.Vt struct roff_man
and
.Vt struct roff_node
from
.Pa roff.h
.Pa roff.h ,
.Vt struct roff_man
from
.Pa roff_int.h ,
and
.Vt struct mdoc_arg
from
.Pa mdoc.h
as opaque types for function prototypes.
.Pp
When this header is included, the same file should not include
.Pa man.h ,
.Pa libman.h ,
or
.Pa libroff.h .
interfaces of different parsers.
.It Qq Pa libman.h
Requires
.Qq Pa roff.h
@ -345,52 +397,109 @@ and some functions internal to the
parser.
.Pp
Uses the types
.Vt struct roff_man
and
.Vt struct roff_node
from
.Pa roff.h
and
.Vt struct roff_man
from
.Pa roff_int.h
as opaque types for function prototypes.
.Pp
When this header is included, the same file should not include
.Pa mdoc.h ,
.Pa libmdoc.h ,
or
.Pa libroff.h .
.It Qq Pa libroff.h
interfaces of different parsers.
.It Qq Pa eqn_parse.h
External interface of the
.Xr eqn 7
parser, for use in the
.Xr roff 7
and
.Xr eqn 7
parsers only.
.Pp
Requires
.In sys/types.h
for
.Vt size_t
.Vt size_t .
.Pp
Provides
.Vt struct eqn_node
and the functions
.Fn eqn_alloc ,
.Fn eqn_box_new ,
.Fn eqn_box_free ,
.Fn eqn_free ,
.Fn eqn_parse ,
.Fn eqn_read ,
and
.Qq Pa mandoc.h
.Fn eqn_reset .
.Pp
Uses the type
.Vt struct eqn_box
from
.Pa mandoc.h
as an opaque type for function prototypes.
Uses the types
.Vt struct roff_node
from
.Pa roff.h
and
.Vt struct eqn_def
from
.Pa eqn.c
as opaque struct members.
.Pp
When this header is included, the same file should not include
internals of different parsers.
.It Qq Pa tbl_parse.h
External interface of the
.Xr tbl 7
parser, for use in the
.Xr roff 7
and
.Xr tbl 7
parsers only.
.Pp
Provides the functions documented in
.Xr tbl 3 .
.Pp
Uses the types
.Vt struct tbl_span
from
.Pa tbl.h
and
.Vt struct tbl_node
from
.Pa tbl_int.h
as opaque types for function prototypes.
.Pp
When this header is included, the same file should not include
internals of different parsers.
.It Qq Pa tbl_int.h
Internal interfaces of the
.Xr tbl 7
parser, for use inside the
.Xr tbl 7
parser only.
.Pp
Requires
.Qq Pa tbl.h
for
.Vt struct tbl_*
and
.Vt struct eqn_box .
.Vt struct tbl_opts .
.Pp
Provides
.Vt enum tbl_part ,
.Vt struct tbl_node ,
.Vt struct eqn_def ,
.Vt struct eqn_node ,
and many functions internal to the
.Xr tbl 7
and the functions
.Fn tbl_option ,
.Fn tbl_layout ,
.Fn tbl_data ,
.Fn tbl_cdata ,
and
.Xr eqn 7
parsers.
.Pp
Uses the opaque type
.Vt struct mparse
from
.Pa read.c .
.Fn tbl_reset .
.Pp
When this header is included, the same file should not include
.Pa man.h ,
.Pa mdoc.h ,
.Pa libman.h ,
or
.Pa libmdoc.h .
interfaces of different parsers.
.El
.Ss Formatter interface
These headers should be included after any parser interface headers.
@ -466,7 +575,10 @@ or
Requires
.In sys/types.h
for
.Vt size_t
.Vt size_t ,
.Pa mandoc.h
for
.Vt enum mandoc_esc ,
and
.Qq Pa out.h
for
@ -517,7 +629,7 @@ functionality mentioned in
Provides the top level steering functions for all formatters.
.Pp
Uses the type
.Vt struct roff_man
.Vt struct roff_meta
from
.Pa roff.h
as an opaque type for function prototypes.

View File

@ -1,4 +1,4 @@
.\" $Id: mandoc_html.3,v 1.17 2018/06/25 16:54:59 schwarze Exp $
.\" $Id: mandoc_html.3,v 1.19 2019/01/11 12:56:43 schwarze Exp $
.\"
.\" Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
.\"
@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: June 25 2018 $
.Dd $Mdocdate: January 11 2019 $
.Dt MANDOC_HTML 3
.Os
.Sh NAME
@ -167,11 +167,6 @@ the respective attribute is not written.
Print a
.Cm class
attribute.
This attribute letter can optionally be followed by the modifier letter
.Cm T .
In that case, a
.Cm title
attribute with the same value is also printed.
.It Cm h
Print a
.Cm href
@ -216,6 +211,14 @@ It requires two
.Va char *
arguments.
The first is the name of the style property, the second its value.
The name must not be
.Dv NULL .
The
.Cm s
.Ar fmt
letter can be repeated, each repetition requiring an additional pair of
.Va char *
arguments.
.El
.Pp
.Fn print_otag

329
mandoc_msg.c Normal file
View File

@ -0,0 +1,329 @@
/* $Id: mandoc_msg.c,v 1.6 2019/03/06 15:55:38 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "mandoc.h"
static const enum mandocerr lowest_type[MANDOCLEVEL_MAX] = {
MANDOCERR_OK,
MANDOCERR_OK,
MANDOCERR_WARNING,
MANDOCERR_ERROR,
MANDOCERR_UNSUPP,
MANDOCERR_MAX,
MANDOCERR_MAX
};
static const char *const level_name[MANDOCLEVEL_MAX] = {
"SUCCESS",
"STYLE",
"WARNING",
"ERROR",
"UNSUPP",
"BADARG",
"SYSERR"
};
static const char *const type_message[MANDOCERR_MAX] = {
"ok",
"base system convention",
"Mdocdate found",
"Mdocdate missing",
"unknown architecture",
"operating system explicitly specified",
"RCS id missing",
"referenced manual not found",
"generic style suggestion",
"legacy man(7) date format",
"normalizing date format to",
"lower case character in document title",
"duplicate RCS id",
"possible typo in section name",
"unterminated quoted argument",
"useless macro",
"consider using OS macro",
"errnos out of order",
"duplicate errno",
"trailing delimiter",
"no blank before trailing delimiter",
"fill mode already enabled, skipping",
"fill mode already disabled, skipping",
"verbatim \"--\", maybe consider using \\(em",
"function name without markup",
"whitespace at end of input line",
"bad comment style",
"generic warning",
/* related to the prologue */
"missing manual title, using UNTITLED",
"missing manual title, using \"\"",
"missing manual section, using \"\"",
"unknown manual section",
"missing date, using today's date",
"cannot parse date, using it verbatim",
"date in the future, using it anyway",
"missing Os macro, using \"\"",
"late prologue macro",
"prologue macros out of order",
/* related to document structure */
".so is fragile, better use ln(1)",
"no document body",
"content before first section header",
"first section is not \"NAME\"",
"NAME section without Nm before Nd",
"NAME section without description",
"description not at the end of NAME",
"bad NAME section content",
"missing comma before name",
"missing description line, using \"\"",
"description line outside NAME section",
"sections out of conventional order",
"duplicate section title",
"unexpected section",
"cross reference to self",
"unusual Xr order",
"unusual Xr punctuation",
"AUTHORS section without An macro",
/* related to macros and nesting */
"obsolete macro",
"macro neither callable nor escaped",
"skipping paragraph macro",
"moving paragraph macro out of list",
"skipping no-space macro",
"blocks badly nested",
"nested displays are not portable",
"moving content out of list",
"first macro on line",
"line scope broken",
"skipping blank line in line scope",
/* related to missing macro arguments */
"skipping empty request",
"conditional request controls empty scope",
"skipping empty macro",
"empty block",
"empty argument, using 0n",
"missing display type, using -ragged",
"list type is not the first argument",
"missing -width in -tag list, using 6n",
"missing utility name, using \"\"",
"missing function name, using \"\"",
"empty head in list item",
"empty list item",
"missing argument, using next line",
"missing font type, using \\fR",
"unknown font type, using \\fR",
"nothing follows prefix",
"empty reference block",
"missing section argument",
"missing -std argument, adding it",
"missing option string, using \"\"",
"missing resource identifier, using \"\"",
"missing eqn box, using \"\"",
/* related to bad macro arguments */
"duplicate argument",
"skipping duplicate argument",
"skipping duplicate display type",
"skipping duplicate list type",
"skipping -width argument",
"wrong number of cells",
"unknown AT&T UNIX version",
"comma in function argument",
"parenthesis in function name",
"unknown library name",
"invalid content in Rs block",
"invalid Boolean argument",
"argument contains two font escapes",
"unknown font, skipping request",
"odd number of characters in request",
/* related to plain text */
"blank line in fill mode, using .sp",
"tab in filled text",
"new sentence, new line",
"invalid escape sequence",
"undefined escape, printing literally",
"undefined string, using \"\"",
/* related to tables */
"tbl line starts with span",
"tbl column starts with span",
"skipping vertical bar in tbl layout",
"generic error",
/* related to tables */
"non-alphabetic character in tbl options",
"skipping unknown tbl option",
"missing tbl option argument",
"wrong tbl option argument size",
"empty tbl layout",
"invalid character in tbl layout",
"unmatched parenthesis in tbl layout",
"tbl without any data cells",
"ignoring data in spanned tbl cell",
"ignoring extra tbl data cells",
"data block open at end of tbl",
/* related to document structure and macros */
NULL,
"duplicate prologue macro",
"skipping late title macro",
"input stack limit exceeded, infinite loop?",
"skipping bad character",
"skipping unknown macro",
"ignoring request outside macro",
"skipping insecure request",
"skipping item outside list",
"skipping column outside column list",
"skipping end of block that is not open",
"fewer RS blocks open, skipping",
"inserting missing end of block",
"appending missing end of block",
/* related to request and macro arguments */
"escaped character not allowed in a name",
"using macro argument outside macro",
"argument number is not numeric",
"NOT IMPLEMENTED: Bd -file",
"skipping display without arguments",
"missing list type, using -item",
"argument is not numeric, using 1",
"argument is not a character",
"missing manual name, using \"\"",
"uname(3) system call failed, using UNKNOWN",
"unknown standard specifier",
"skipping request without numeric argument",
"excessive shift",
"NOT IMPLEMENTED: .so with absolute path or \"..\"",
".so request failed",
"skipping all arguments",
"skipping excess arguments",
"divide by zero",
"unsupported feature",
"input too large",
"unsupported control character",
"unsupported escape sequence",
"unsupported roff request",
"nested .while loops",
"end of scope with open .while loop",
"end of .while loop in inner scope",
"cannot continue this .while loop",
"eqn delim option in tbl",
"unsupported tbl layout modifier",
"ignoring macro in table",
};
static FILE *fileptr = NULL;
static const char *filename = NULL;
static enum mandocerr min_type = MANDOCERR_MAX;
static enum mandoclevel rc = MANDOCLEVEL_OK;
void
mandoc_msg_setoutfile(FILE *fp)
{
fileptr = fp;
}
const char *
mandoc_msg_getinfilename(void)
{
return filename;
}
void
mandoc_msg_setinfilename(const char *fn)
{
filename = fn;
}
enum mandocerr
mandoc_msg_getmin(void)
{
return min_type;
}
void
mandoc_msg_setmin(enum mandocerr t)
{
min_type = t;
}
enum mandoclevel
mandoc_msg_getrc(void)
{
return rc;
}
void
mandoc_msg_setrc(enum mandoclevel level)
{
if (rc < level)
rc = level;
}
void
mandoc_msg(enum mandocerr t, int line, int col, const char *fmt, ...)
{
va_list ap;
enum mandoclevel level;
if (t < min_type && t != MANDOCERR_FILE)
return;
level = MANDOCLEVEL_UNSUPP;
while (t < lowest_type[level])
level--;
mandoc_msg_setrc(level);
if (fileptr == NULL)
return;
fprintf(fileptr, "%s:", getprogname());
if (filename != NULL)
fprintf(fileptr, " %s:", filename);
if (line > 0)
fprintf(fileptr, "%d:%d:", line, col + 1);
fprintf(fileptr, " %s", level_name[level]);
if (type_message[t] != NULL)
fprintf(fileptr, ": %s", type_message[t]);
if (fmt != NULL) {
fprintf(fileptr, ": ");
va_start(ap, fmt);
vfprintf(fileptr, fmt, ap);
va_end(ap);
}
fputc('\n', fileptr);
}

43
mandoc_parse.h Normal file
View File

@ -0,0 +1,43 @@
/* $Id: mandoc_parse.h,v 1.4 2018/12/30 00:49:55 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Top level parser interface. For use in the main program
* and in the main parser, but not in formatters.
*/
/*
* Parse options.
*/
#define MPARSE_MDOC (1 << 0) /* assume -mdoc */
#define MPARSE_MAN (1 << 1) /* assume -man */
#define MPARSE_SO (1 << 2) /* honour .so requests */
#define MPARSE_QUICK (1 << 3) /* abort the parse early */
#define MPARSE_UTF8 (1 << 4) /* accept UTF-8 input */
#define MPARSE_LATIN1 (1 << 5) /* accept ISO-LATIN-1 input */
#define MPARSE_VALIDATE (1 << 6) /* call validation functions */
struct roff_meta;
struct mparse;
struct mparse *mparse_alloc(int, enum mandoc_os, const char *);
void mparse_copy(const struct mparse *);
void mparse_free(struct mparse *);
int mparse_open(struct mparse *, const char *);
void mparse_readfd(struct mparse *, int, const char *);
void mparse_reset(struct mparse *);
struct roff_meta *mparse_result(struct mparse *);

View File

@ -1,7 +1,7 @@
/* $Id: mandocd.c,v 1.6 2017/06/24 14:38:32 schwarze Exp $ */
/* $Id: mandocd.c,v 1.11 2019/03/03 13:02:11 schwarze Exp $ */
/*
* Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
* Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2017, 2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -38,6 +38,7 @@
#include "roff.h"
#include "mdoc.h"
#include "man.h"
#include "mandoc_parse.h"
#include "main.h"
#include "manconf.h"
@ -170,8 +171,8 @@ main(int argc, char *argv[])
errx(1, "file descriptor %s %s", argv[1], errstr);
mchars_alloc();
parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1,
MANDOCERR_MAX, NULL, MANDOC_OS_OTHER, defos);
parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
MPARSE_VALIDATE, MANDOC_OS_OTHER, defos);
memset(&options, 0, sizeof(options));
switch (outtype) {
@ -212,6 +213,8 @@ main(int argc, char *argv[])
process(parser, outtype, formatter);
mparse_reset(parser);
if (outtype == OUTT_HTML)
html_reset(formatter);
fflush(stdout);
fflush(stderr);
@ -243,35 +246,29 @@ main(int argc, char *argv[])
static void
process(struct mparse *parser, enum outt outtype, void *formatter)
{
struct roff_man *man;
struct roff_meta *meta;
mparse_readfd(parser, STDIN_FILENO, "<unixfd>");
mparse_result(parser, &man, NULL);
if (man == NULL)
return;
if (man->macroset == MACROSET_MDOC) {
mdoc_validate(man);
meta = mparse_result(parser);
if (meta->macroset == MACROSET_MDOC) {
switch (outtype) {
case OUTT_ASCII:
case OUTT_UTF8:
terminal_mdoc(formatter, man);
terminal_mdoc(formatter, meta);
break;
case OUTT_HTML:
html_mdoc(formatter, man);
html_mdoc(formatter, meta);
break;
}
}
if (man->macroset == MACROSET_MAN) {
man_validate(man);
if (meta->macroset == MACROSET_MAN) {
switch (outtype) {
case OUTT_ASCII:
case OUTT_UTF8:
terminal_man(formatter, man);
terminal_man(formatter, meta);
break;
case OUTT_HTML:
html_man(formatter, man);
html_man(formatter, meta);
break;
}
}

View File

@ -1,7 +1,7 @@
/* $Id: mandocdb.c,v 1.258 2018/02/23 18:25:57 schwarze Exp $ */
/* $Id: mandocdb.c,v 1.262 2018/12/30 00:49:55 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011-2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011-2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2016 Ed Maste <emaste@freebsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@ -52,6 +52,7 @@
#include "roff.h"
#include "mdoc.h"
#include "man.h"
#include "mandoc_parse.h"
#include "manconf.h"
#include "mansearch.h"
#include "dba_array.h"
@ -185,7 +186,7 @@ static struct ohash names; /* table of all names */
static struct ohash strings; /* table of all strings */
static uint64_t name_mask;
static const struct mdoc_handler __mdocs[MDOC_MAX - MDOC_Dd] = {
static const struct mdoc_handler mdoc_handlers[MDOC_MAX - MDOC_Dd] = {
{ NULL, 0, NODE_NOPRT }, /* Dd */
{ NULL, 0, NODE_NOPRT }, /* Dt */
{ NULL, 0, NODE_NOPRT }, /* Os */
@ -307,7 +308,6 @@ static const struct mdoc_handler __mdocs[MDOC_MAX - MDOC_Dd] = {
{ NULL, 0, 0 }, /* %U */
{ NULL, 0, 0 }, /* Ta */
};
static const struct mdoc_handler *const mdocs = __mdocs - MDOC_Dd;
int
@ -347,6 +347,7 @@ mandocdb(int argc, char *argv[])
goto usage; \
} while (/*CONSTCOND*/0)
mparse_options = MPARSE_VALIDATE;
path_arg = NULL;
op = OP_DEFAULT;
@ -422,8 +423,7 @@ mandocdb(int argc, char *argv[])
exitcode = (int)MANDOCLEVEL_OK;
mchars_alloc();
mp = mparse_alloc(mparse_options, MANDOCERR_MAX, NULL,
MANDOC_OS_OTHER, NULL);
mp = mparse_alloc(mparse_options, MANDOC_OS_OTHER, NULL);
mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev));
mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file));
@ -1116,8 +1116,7 @@ mpages_merge(struct dba *dba, struct mparse *mp)
{
struct mpage *mpage, *mpage_dest;
struct mlink *mlink, *mlink_dest;
struct roff_man *man;
char *sodest;
struct roff_meta *meta;
char *cp;
int fd;
@ -1130,8 +1129,7 @@ mpages_merge(struct dba *dba, struct mparse *mp)
mandoc_ohash_init(&names, 4, offsetof(struct str, key));
mandoc_ohash_init(&strings, 6, offsetof(struct str, key));
mparse_reset(mp);
man = NULL;
sodest = NULL;
meta = NULL;
if ((fd = mparse_open(mp, mlink->file)) == -1) {
say(mlink->file, "&open");
@ -1146,14 +1144,14 @@ mpages_merge(struct dba *dba, struct mparse *mp)
mparse_readfd(mp, fd, mlink->file);
close(fd);
fd = -1;
mparse_result(mp, &man, &sodest);
meta = mparse_result(mp);
}
if (sodest != NULL) {
if (meta != NULL && meta->sodest != NULL) {
mlink_dest = ohash_find(&mlinks,
ohash_qlookup(&mlinks, sodest));
ohash_qlookup(&mlinks, meta->sodest));
if (mlink_dest == NULL) {
mandoc_asprintf(&cp, "%s.gz", sodest);
mandoc_asprintf(&cp, "%s.gz", meta->sodest);
mlink_dest = ohash_find(&mlinks,
ohash_qlookup(&mlinks, cp));
free(cp);
@ -1190,39 +1188,36 @@ mpages_merge(struct dba *dba, struct mparse *mp)
mpage->mlinks = NULL;
}
goto nextpage;
} else if (man != NULL && man->macroset == MACROSET_MDOC) {
mdoc_validate(man);
} else if (meta != NULL && meta->macroset == MACROSET_MDOC) {
mpage->form = FORM_SRC;
mpage->sec = man->meta.msec;
mpage->sec = meta->msec;
mpage->sec = mandoc_strdup(
mpage->sec == NULL ? "" : mpage->sec);
mpage->arch = man->meta.arch;
mpage->arch = meta->arch;
mpage->arch = mandoc_strdup(
mpage->arch == NULL ? "" : mpage->arch);
mpage->title = mandoc_strdup(man->meta.title);
} else if (man != NULL && man->macroset == MACROSET_MAN) {
man_validate(man);
if (*man->meta.msec != '\0' ||
*man->meta.title != '\0') {
mpage->title = mandoc_strdup(meta->title);
} else if (meta != NULL && meta->macroset == MACROSET_MAN) {
if (*meta->msec != '\0' || *meta->title != '\0') {
mpage->form = FORM_SRC;
mpage->sec = mandoc_strdup(man->meta.msec);
mpage->sec = mandoc_strdup(meta->msec);
mpage->arch = mandoc_strdup(mlink->arch);
mpage->title = mandoc_strdup(man->meta.title);
mpage->title = mandoc_strdup(meta->title);
} else
man = NULL;
meta = NULL;
}
assert(mpage->desc == NULL);
if (man == NULL) {
if (meta == NULL) {
mpage->form = FORM_CAT;
mpage->sec = mandoc_strdup(mlink->dsec);
mpage->arch = mandoc_strdup(mlink->arch);
mpage->title = mandoc_strdup(mlink->name);
parse_cat(mpage, fd);
} else if (man->macroset == MACROSET_MDOC)
parse_mdoc(mpage, &man->meta, man->first);
} else if (meta->macroset == MACROSET_MDOC)
parse_mdoc(mpage, meta, meta->first);
else
parse_man(mpage, &man->meta, man->first);
parse_man(mpage, meta, meta->first);
if (mpage->desc == NULL) {
mpage->desc = mandoc_strdup(mlink->name);
if (warnings)
@ -1546,25 +1541,28 @@ static void
parse_mdoc(struct mpage *mpage, const struct roff_meta *meta,
const struct roff_node *n)
{
const struct mdoc_handler *handler;
for (n = n->child; n != NULL; n = n->next) {
if (n->tok == TOKEN_NONE ||
n->tok < ROFF_MAX ||
n->flags & mdocs[n->tok].taboo)
if (n->tok == TOKEN_NONE || n->tok < ROFF_MAX)
continue;
assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
handler = mdoc_handlers + (n->tok - MDOC_Dd);
if (n->flags & handler->taboo)
continue;
switch (n->type) {
case ROFFT_ELEM:
case ROFFT_BLOCK:
case ROFFT_HEAD:
case ROFFT_BODY:
case ROFFT_TAIL:
if (mdocs[n->tok].fp != NULL &&
(*mdocs[n->tok].fp)(mpage, meta, n) == 0)
if (handler->fp != NULL &&
(*handler->fp)(mpage, meta, n) == 0)
break;
if (mdocs[n->tok].mask)
if (handler->mask)
putmdockey(mpage, n->child,
mdocs[n->tok].mask, mdocs[n->tok].taboo);
handler->mask, handler->taboo);
break;
default:
continue;

View File

@ -1,6 +1,6 @@
/* $Id: manpath.c,v 1.35 2017/07/01 09:47:30 schwarze Exp $ */
/* $Id: manpath.c,v 1.37 2018/11/22 11:30:23 schwarze Exp $ */
/*
* Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@ -232,8 +232,8 @@ int
manconf_output(struct manoutput *conf, const char *cp, int fromfile)
{
const char *const toks[] = {
"includes", "man", "paper", "style",
"indent", "width", "fragment", "mdoc", "noval"
"includes", "man", "paper", "style", "indent", "width",
"tag", "fragment", "mdoc", "noval", "toc"
};
const char *errstr;
@ -257,7 +257,7 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile)
warnx("-O %s=?: Missing argument value", toks[tok]);
return -1;
}
if ((tok == 6 || tok == 7) && *cp != '\0') {
if (tok > 6 && *cp != '\0') {
warnx("-O %s: Does not take a value: %s", toks[tok], cp);
return -1;
}
@ -312,14 +312,24 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile)
warnx("-O width=%s is %s", cp, errstr);
return -1;
case 6:
conf->fragment = 1;
if (conf->tag != NULL) {
oldval = mandoc_strdup(conf->tag);
break;
}
conf->tag = mandoc_strdup(cp);
return 0;
case 7:
conf->mdoc = 1;
conf->fragment = 1;
return 0;
case 8:
conf->mdoc = 1;
return 0;
case 9:
conf->noval = 1;
return 0;
case 10:
conf->toc = 1;
return 0;
default:
if (fromfile)
warnx("-O %s: Bad argument", cp);

View File

@ -1,7 +1,7 @@
/* $Id: mansearch.c,v 1.77 2017/08/22 17:50:11 schwarze Exp $ */
/* $Id: mansearch.c,v 1.80 2018/12/13 11:55:46 schwarze Exp $ */
/*
* Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013-2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013-2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -36,7 +36,6 @@
#include <string.h>
#include <unistd.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "mandoc_ohash.h"
#include "manconf.h"
@ -201,7 +200,6 @@ mansearch(const struct mansearch *search,
mpage->names = buildnames(page);
mpage->output = buildoutput(outkey, page);
mpage->ipath = i;
mpage->bits = rp->bits;
mpage->sec = *page->sect - '0';
if (mpage->sec < 0 || mpage->sec > 9)
mpage->sec = 10;
@ -296,10 +294,8 @@ manmerge_term(struct expr *e, struct ohash *htab)
break;
slot = ohash_lookup_memory(htab,
(char *)&res, sizeof(res.page), res.page);
if ((rp = ohash_find(htab, slot)) != NULL) {
rp->bits |= res.bits;
if ((rp = ohash_find(htab, slot)) != NULL)
continue;
}
rp = mandoc_malloc(sizeof(*rp));
*rp = res;
ohash_insert(htab, slot, rp);
@ -412,8 +408,7 @@ manpage_compare(const void *vp1, const void *vp2)
mp1 = vp1;
mp2 = vp2;
if ((diff = mp2->bits - mp1->bits) ||
(diff = mp1->sec - mp2->sec))
if ((diff = mp1->sec - mp2->sec))
return diff;
/* Fall back to alphabetic ordering of names. */
@ -774,8 +769,9 @@ exprterm(const struct mansearch *search, int argc, char *argv[], int *argi)
cs = 0;
} else if ((val = strpbrk(argv[*argi], "=~")) == NULL) {
e->bits = TYPE_Nm | TYPE_Nd;
e->match.type = DBM_SUB;
e->match.str = argv[*argi];
e->match.type = DBM_REGEX;
val = argv[*argi];
cs = 0;
} else {
if (val == argv[*argi])
e->bits = TYPE_Nm | TYPE_Nd;

View File

@ -1,4 +1,4 @@
/* $Id: mansearch.h,v 1.28 2017/04/17 20:05:08 schwarze Exp $ */
/* $Id: mansearch.h,v 1.29 2018/11/22 12:01:46 schwarze Exp $ */
/*
* Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org>
@ -93,7 +93,6 @@ struct manpage {
char *names; /* a list of names with sections */
char *output; /* user-defined additional output */
size_t ipath; /* number of the manpath */
uint64_t bits; /* name type mask */
int sec; /* section number, 10 means invalid */
enum form form;
};

1523
mdoc.7

File diff suppressed because it is too large Load Diff

59
mdoc.c
View File

@ -1,7 +1,7 @@
/* $Id: mdoc.c,v 1.268 2017/08/11 16:56:21 schwarze Exp $ */
/* $Id: mdoc.c,v 1.274 2018/12/31 07:46:07 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -79,13 +79,6 @@ mdoc_parseln(struct roff_man *mdoc, int ln, char *buf, int offs)
mdoc_ptext(mdoc, ln, buf, offs);
}
void
mdoc_macro(MACRO_PROT_ARGS)
{
assert(tok >= MDOC_Dd && tok < MDOC_MAX);
(*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf);
}
void
mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, enum roff_tok tok)
{
@ -162,15 +155,6 @@ mdoc_elem_alloc(struct roff_man *mdoc, int line, int pos,
mdoc->next = ROFF_NEXT_CHILD;
}
void
mdoc_node_relink(struct roff_man *mdoc, struct roff_node *p)
{
roff_node_unlink(mdoc, p);
p->prev = p->next = NULL;
roff_node_append(mdoc, p);
}
/*
* Parse free-form text, that is, a line that does not begin with the
* control character.
@ -196,7 +180,8 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
(n->parent != NULL && n->parent->tok == MDOC_Bl &&
n->parent->norm->Bl.type == LIST_column)) {
mdoc->flags |= MDOC_FREECOL;
mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf);
(*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It,
line, offs, &offs, buf);
return 1;
}
@ -225,7 +210,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
* Strip trailing tabs in literal context only;
* outside, they affect the next line.
*/
if (MDOC_LITERAL & mdoc->flags)
if (mdoc->flags & ROFF_NOFILL)
continue;
break;
case '\\':
@ -242,8 +227,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
*end = '\0';
if (ws)
mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
line, (int)(ws-buf), NULL);
mandoc_msg(MANDOCERR_SPACE_EOL, line, (int)(ws - buf), NULL);
/*
* Blank lines are allowed in no-fill mode
@ -251,7 +235,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
* but add a single vertical space elsewhere.
*/
if (buf[offs] == '\0' && ! (mdoc->flags & MDOC_LITERAL)) {
if (buf[offs] == '\0' && (mdoc->flags & ROFF_NOFILL) == 0) {
switch (mdoc->last->type) {
case ROFFT_TEXT:
sp = mdoc->last->string;
@ -267,8 +251,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
default:
break;
}
mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse,
line, (int)(c - buf), NULL);
mandoc_msg(MANDOCERR_FI_BLANK, line, (int)(c - buf), NULL);
roff_elem_alloc(mdoc, line, offs, ROFF_sp);
mdoc->last->flags |= NODE_VALID | NODE_ENDED;
mdoc->next = ROFF_NEXT_SIBLING;
@ -277,7 +260,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
roff_word_alloc(mdoc, line, offs, buf+offs);
if (mdoc->flags & MDOC_LITERAL)
if (mdoc->flags & ROFF_NOFILL)
return 1;
/*
@ -308,8 +291,7 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
if (*c == ' ')
c++;
if (isupper((unsigned char)(*c)))
mandoc_msg(MANDOCERR_EOS, mdoc->parse,
line, (int)(c - buf), NULL);
mandoc_msg(MANDOCERR_EOS, line, (int)(c - buf), NULL);
}
return 1;
@ -337,8 +319,7 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs)
if (sz == 2 || sz == 3)
tok = roffhash_find(mdoc->mdocmac, buf + sv, sz);
if (tok == TOKEN_NONE) {
mandoc_msg(MANDOCERR_MACRO, mdoc->parse,
ln, sv, buf + sv - 1);
mandoc_msg(MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1);
return 1;
}
@ -368,8 +349,7 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs)
*/
if ('\0' == buf[offs] && ' ' == buf[offs - 1])
mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
ln, offs - 1, NULL);
mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL);
/*
* If an initial macro or a list invocation, divert directly
@ -378,7 +358,7 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs)
n = mdoc->last;
if (n == NULL || tok == MDOC_It || tok == MDOC_El) {
mdoc_macro(mdoc, tok, ln, sv, &offs, buf);
(*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf);
return 1;
}
@ -394,13 +374,13 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs)
(n->parent != NULL && n->parent->tok == MDOC_Bl &&
n->parent->norm->Bl.type == LIST_column)) {
mdoc->flags |= MDOC_FREECOL;
mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf);
(*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, ln, sv, &sv, buf);
return 1;
}
/* Normal processing of a macro. */
mdoc_macro(mdoc, tok, ln, sv, &offs, buf);
(*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf);
/* In quick mode (for mandocdb), abort after the NAME section. */
@ -448,12 +428,3 @@ mdoc_isdelim(const char *p)
return DELIM_NONE;
}
void
mdoc_validate(struct roff_man *mdoc)
{
mdoc->last = mdoc->first;
mdoc_node_validate(mdoc);
mdoc_state_reset(mdoc);
}

5
mdoc.h
View File

@ -1,4 +1,4 @@
/* $Id: mdoc.h,v 1.145 2017/04/24 23:06:18 schwarze Exp $ */
/* $Id: mdoc.h,v 1.146 2018/12/30 00:49:55 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@ -16,6 +16,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct roff_node;
struct roff_man;
enum mdocargt {
MDOC_Split, /* -split */
MDOC_Nosplit, /* -nospli */

View File

@ -1,7 +1,7 @@
/* $Id: mdoc_argv.c,v 1.115 2017/05/30 16:22:03 schwarze Exp $ */
/* $Id: mdoc_argv.c,v 1.119 2018/12/21 17:15:19 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2012, 2014-2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2012, 2014-2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -144,7 +144,7 @@ static const enum mdocargt args_Bl[] = {
MDOC_ARG_MAX
};
static const struct mdocarg __mdocargs[MDOC_MAX - MDOC_Dd] = {
static const struct mdocarg mdocargs[MDOC_MAX - MDOC_Dd] = {
{ ARGSFL_NONE, NULL }, /* Dd */
{ ARGSFL_NONE, NULL }, /* Dt */
{ ARGSFL_NONE, NULL }, /* Os */
@ -266,7 +266,6 @@ static const struct mdocarg __mdocargs[MDOC_MAX - MDOC_Dd] = {
{ ARGSFL_NONE, NULL }, /* %U */
{ ARGSFL_NONE, NULL }, /* Ta */
};
static const struct mdocarg *const mdocargs = __mdocargs - MDOC_Dd;
/*
@ -290,7 +289,7 @@ mdoc_argv(struct roff_man *mdoc, int line, enum roff_tok tok,
/* Which flags does this macro support? */
assert(tok >= MDOC_Dd && tok < MDOC_MAX);
argtable = mdocargs[tok].argvs;
argtable = mdocargs[tok - MDOC_Dd].argvs;
if (argtable == NULL)
return;
@ -368,7 +367,7 @@ mdoc_argv(struct roff_man *mdoc, int line, enum roff_tok tok,
/* Prepare for parsing the next flag. */
*pos = ipos;
argtable = mdocargs[tok].argvs;
argtable = mdocargs[tok - MDOC_Dd].argvs;
}
}
@ -417,12 +416,9 @@ mdoc_args(struct roff_man *mdoc, int line, int *pos,
char *buf, enum roff_tok tok, char **v)
{
struct roff_node *n;
char *v_local;
enum argsflag fl;
if (v == NULL)
v = &v_local;
fl = tok == TOKEN_NONE ? ARGSFL_NONE : mdocargs[tok].flags;
fl = tok == TOKEN_NONE ? ARGSFL_NONE : mdocargs[tok - MDOC_Dd].flags;
/*
* We know that we're in an `It', so it's reasonable to expect
@ -449,18 +445,20 @@ args(struct roff_man *mdoc, int line, int *pos,
char *buf, enum argsflag fl, char **v)
{
char *p;
char *v_local;
int pairs;
if (buf[*pos] == '\0') {
if (mdoc->flags & MDOC_PHRASELIT &&
! (mdoc->flags & MDOC_PHRASE)) {
mandoc_msg(MANDOCERR_ARG_QUOTE,
mdoc->parse, line, *pos, NULL);
mandoc_msg(MANDOCERR_ARG_QUOTE, line, *pos, NULL);
mdoc->flags &= ~MDOC_PHRASELIT;
}
return ARGS_EOLN;
}
if (v == NULL)
v = &v_local;
*v = buf + *pos;
if (fl == ARGSFL_DELIM && args_checkpunct(buf, *pos))
@ -506,7 +504,7 @@ args(struct roff_man *mdoc, int line, int *pos,
p = strchr(*v, '\0');
if (p[-1] == ' ')
mandoc_msg(MANDOCERR_SPACE_EOL,
mdoc->parse, line, *pos, NULL);
line, *pos, NULL);
*pos += (int)(p - *v);
}
@ -527,13 +525,12 @@ args(struct roff_man *mdoc, int line, int *pos,
* Whitespace is NOT involved in literal termination.
*/
if (mdoc->flags & MDOC_PHRASELIT || buf[*pos] == '\"') {
if ( ! (mdoc->flags & MDOC_PHRASELIT))
if (mdoc->flags & MDOC_PHRASELIT ||
(mdoc->flags & MDOC_PHRASE && buf[*pos] == '\"')) {
if ((mdoc->flags & MDOC_PHRASELIT) == 0) {
*v = &buf[++(*pos)];
if (mdoc->flags & MDOC_PHRASE)
mdoc->flags |= MDOC_PHRASELIT;
}
pairs = 0;
for ( ; buf[*pos]; (*pos)++) {
/* Move following text left after quoted quotes. */
@ -554,7 +551,7 @@ args(struct roff_man *mdoc, int line, int *pos,
if (buf[*pos] == '\0') {
if ( ! (mdoc->flags & MDOC_PHRASE))
mandoc_msg(MANDOCERR_ARG_QUOTE,
mdoc->parse, line, *pos, NULL);
line, *pos, NULL);
return ARGS_WORD;
}
@ -568,14 +565,15 @@ args(struct roff_man *mdoc, int line, int *pos,
(*pos)++;
if ('\0' == buf[*pos])
mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
line, *pos, NULL);
mandoc_msg(MANDOCERR_SPACE_EOL, line, *pos, NULL);
return ARGS_WORD;
}
p = &buf[*pos];
*v = mandoc_getarg(mdoc->parse, &p, line, pos);
*v = roff_getarg(mdoc->roff, &p, line, pos);
if (v == &v_local)
free(*v);
/*
* After parsing the last word in this phrase,
@ -586,7 +584,7 @@ args(struct roff_man *mdoc, int line, int *pos,
mdoc->flags &= ~MDOC_PHRASEQL;
mdoc->flags |= MDOC_PHRASEQF;
}
return ARGS_WORD;
return ARGS_ALLOC;
}
/*
@ -657,7 +655,9 @@ argv_multi(struct roff_man *mdoc, int line,
v->value = mandoc_reallocarray(v->value,
v->sz + MULTI_STEP, sizeof(char *));
v->value[(int)v->sz] = mandoc_strdup(p);
if (ac != ARGS_ALLOC)
p = mandoc_strdup(p);
v->value[(int)v->sz] = p;
}
}
@ -672,7 +672,10 @@ argv_single(struct roff_man *mdoc, int line,
if (ac == ARGS_EOLN)
return;
if (ac != ARGS_ALLOC)
p = mandoc_strdup(p);
v->sz = 1;
v->value = mandoc_malloc(sizeof(char *));
v->value[0] = mandoc_strdup(p);
v->value[0] = p;
}

View File

@ -1,7 +1,7 @@
/* $Id: mdoc_html.c,v 1.310 2018/07/27 17:49:31 schwarze Exp $ */
/* $Id: mdoc_html.c,v 1.328 2019/03/01 10:57:18 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -42,7 +42,7 @@
#define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b))
#endif
struct htmlmdoc {
struct mdoc_html_act {
int (*pre)(MDOC_ARGS);
void (*post)(MDOC_ARGS);
};
@ -62,6 +62,7 @@ static int mdoc_root_pre(const struct roff_meta *,
static void mdoc__x_post(MDOC_ARGS);
static int mdoc__x_pre(MDOC_ARGS);
static int mdoc_abort_pre(MDOC_ARGS);
static int mdoc_ad_pre(MDOC_ARGS);
static int mdoc_an_pre(MDOC_ARGS);
static int mdoc_ap_pre(MDOC_ARGS);
@ -119,7 +120,7 @@ static int mdoc_vt_pre(MDOC_ARGS);
static int mdoc_xr_pre(MDOC_ARGS);
static int mdoc_xx_pre(MDOC_ARGS);
static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = {
static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = {
{NULL, NULL}, /* Dd */
{NULL, NULL}, /* Dt */
{NULL, NULL}, /* Os */
@ -154,7 +155,7 @@ static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = {
{mdoc_nd_pre, NULL}, /* Nd */
{mdoc_nm_pre, NULL}, /* Nm */
{mdoc_quote_pre, mdoc_quote_post}, /* Op */
{mdoc_ft_pre, NULL}, /* Ot */
{mdoc_abort_pre, NULL}, /* Ot */
{mdoc_pa_pre, NULL}, /* Pa */
{mdoc_ex_pre, NULL}, /* Rv */
{mdoc_st_pre, NULL}, /* St */
@ -227,7 +228,7 @@ static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = {
{mdoc_em_pre, NULL}, /* Fr */
{NULL, NULL}, /* Ud */
{mdoc_lb_pre, NULL}, /* Lb */
{mdoc_pp_pre, NULL}, /* Lp */
{mdoc_abort_pre, NULL}, /* Lp */
{mdoc_lk_pre, NULL}, /* Lk */
{mdoc_mt_pre, NULL}, /* Mt */
{mdoc_quote_pre, mdoc_quote_post}, /* Brq */
@ -241,7 +242,6 @@ static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = {
{mdoc__x_pre, mdoc__x_post}, /* %U */
{NULL, NULL}, /* Ta */
};
static const struct htmlmdoc *const mdocs = __mdocs - MDOC_Dd;
/*
@ -268,22 +268,21 @@ synopsis_pre(struct html *h, const struct roff_node *n)
case MDOC_Fo:
case MDOC_In:
case MDOC_Vt:
print_paragraph(h);
break;
case MDOC_Ft:
if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
print_paragraph(h);
if (n->tok != MDOC_Fn && n->tok != MDOC_Fo)
break;
}
/* FALLTHROUGH */
default:
print_otag(h, TAG_BR, "");
break;
return;
}
html_close_paragraph(h);
print_otag(h, TAG_P, "c", "Pp");
}
void
html_mdoc(void *arg, const struct roff_man *mdoc)
html_mdoc(void *arg, const struct roff_meta *mdoc)
{
struct html *h;
struct roff_node *n;
@ -295,19 +294,19 @@ html_mdoc(void *arg, const struct roff_man *mdoc)
if ((h->oflags & HTML_FRAGMENT) == 0) {
print_gen_decls(h);
print_otag(h, TAG_HTML, "");
if (n->type == ROFFT_COMMENT)
if (n != NULL && n->type == ROFFT_COMMENT)
print_gen_comment(h, n);
t = print_otag(h, TAG_HEAD, "");
print_mdoc_head(&mdoc->meta, h);
print_mdoc_head(mdoc, h);
print_tagq(h, t);
print_otag(h, TAG_BODY, "");
}
mdoc_root_pre(&mdoc->meta, h);
mdoc_root_pre(mdoc, h);
t = print_otag(h, TAG_DIV, "c", "manual-text");
print_mdoc_nodelist(&mdoc->meta, n, h);
print_mdoc_nodelist(mdoc, n, h);
print_tagq(h, t);
mdoc_root_post(&mdoc->meta, h);
mdoc_root_post(mdoc, h);
print_tagq(h, NULL);
}
@ -346,18 +345,21 @@ print_mdoc_nodelist(MDOC_ARGS)
static void
print_mdoc_node(MDOC_ARGS)
{
int child;
struct tag *t;
int child;
if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
return;
child = 1;
t = h->tag;
n->flags &= ~NODE_ENDED;
html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi);
child = 1;
n->flags &= ~NODE_ENDED;
switch (n->type) {
case ROFFT_TEXT:
t = h->tag;
t->refcnt++;
/* No tables in this mode... */
assert(NULL == h->tblt);
@ -366,15 +368,18 @@ print_mdoc_node(MDOC_ARGS)
* (i.e., within a <PRE>) don't print the newline.
*/
if (*n->string == ' ' && n->flags & NODE_LINE &&
(h->flags & (HTML_LITERAL | HTML_NONEWLINE)) == 0)
(h->flags & HTML_NONEWLINE) == 0 &&
(n->flags & NODE_NOFILL) == 0)
print_otag(h, TAG_BR, "");
if (NODE_DELIMC & n->flags)
h->flags |= HTML_NOSPACE;
print_text(h, n->string);
if (NODE_DELIMO & n->flags)
h->flags |= HTML_NOSPACE;
return;
break;
case ROFFT_EQN:
t = h->tag;
t->refcnt++;
print_eqn(h, n->eqn);
break;
case ROFFT_TBL:
@ -391,20 +396,22 @@ print_mdoc_node(MDOC_ARGS)
* the "meta" table state. This will be reopened on the
* next table element.
*/
if (h->tblt != NULL) {
if (h->tblt != NULL)
print_tblclose(h);
t = h->tag;
}
assert(h->tblt == NULL);
t = h->tag;
t->refcnt++;
if (n->tok < ROFF_MAX) {
roff_html_pre(h, n);
child = 0;
break;
t->refcnt--;
print_stagq(h, t);
return;
}
assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
if (mdocs[n->tok].pre != NULL &&
if (mdoc_html_acts[n->tok - MDOC_Dd].pre != NULL &&
(n->end == ENDBODY_NOT || n->child != NULL))
child = (*mdocs[n->tok].pre)(meta, n, h);
child = (*mdoc_html_acts[n->tok - MDOC_Dd].pre)(meta,
n, h);
break;
}
@ -413,24 +420,31 @@ print_mdoc_node(MDOC_ARGS)
h->flags |= HTML_PREKEEP;
}
if (child && n->child)
if (child && n->child != NULL)
print_mdoc_nodelist(meta, n->child, h);
t->refcnt--;
print_stagq(h, t);
switch (n->type) {
case ROFFT_TEXT:
case ROFFT_EQN:
break;
default:
if (n->tok < ROFF_MAX ||
mdocs[n->tok].post == NULL ||
if (mdoc_html_acts[n->tok - MDOC_Dd].post == NULL ||
n->flags & NODE_ENDED)
break;
(*mdocs[n->tok].post)(meta, n, h);
(*mdoc_html_acts[n->tok - MDOC_Dd].post)(meta, n, h);
if (n->end != ENDBODY_NOT)
n->body->flags |= NODE_ENDED;
break;
}
if (n->flags & NODE_NOFILL &&
(n->next == NULL || n->next->flags & NODE_LINE)) {
h->col++;
print_endline(h);
}
}
static void
@ -507,12 +521,65 @@ cond_id(const struct roff_node *n)
static int
mdoc_sh_pre(MDOC_ARGS)
{
char *id;
struct roff_node *sn, *subn;
struct tag *t, *tsec, *tsub;
char *id;
int sc;
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
if ((h->oflags & HTML_TOC) == 0 ||
h->flags & HTML_TOCDONE ||
n->sec <= SEC_SYNOPSIS) {
print_otag(h, TAG_SECTION, "c", "Sh");
break;
}
h->flags |= HTML_TOCDONE;
sc = 0;
for (sn = n->next; sn != NULL; sn = sn->next)
if (sn->sec == SEC_CUSTOM)
if (++sc == 2)
break;
if (sc < 2)
break;
t = print_otag(h, TAG_H1, "c", "Sh");
print_text(h, "TABLE OF CONTENTS");
print_tagq(h, t);
t = print_otag(h, TAG_UL, "c", "Bl-compact");
for (sn = n; sn != NULL; sn = sn->next) {
tsec = print_otag(h, TAG_LI, "");
id = html_make_id(sn->head, 0);
tsub = print_otag(h, TAG_A, "hR", id);
free(id);
print_mdoc_nodelist(meta, sn->head->child, h);
print_tagq(h, tsub);
tsub = NULL;
for (subn = sn->body->child; subn != NULL;
subn = subn->next) {
if (subn->tok != MDOC_Ss)
continue;
id = html_make_id(subn->head, 0);
if (id == NULL)
continue;
if (tsub == NULL)
print_otag(h, TAG_UL,
"c", "Bl-compact");
tsub = print_otag(h, TAG_LI, "");
print_otag(h, TAG_A, "hR", id);
free(id);
print_mdoc_nodelist(meta,
subn->head->child, h);
print_tagq(h, tsub);
}
print_tagq(h, tsec);
}
print_tagq(h, t);
print_otag(h, TAG_SECTION, "c", "Sh");
break;
case ROFFT_HEAD:
id = html_make_id(n, 1);
print_otag(h, TAG_H1, "cTi", "Sh", id);
print_otag(h, TAG_H1, "ci", "Sh", id);
if (id != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
break;
@ -531,11 +598,21 @@ mdoc_ss_pre(MDOC_ARGS)
{
char *id;
if (n->type != ROFFT_HEAD)
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
print_otag(h, TAG_SECTION, "c", "Ss");
return 1;
case ROFFT_HEAD:
break;
case ROFFT_BODY:
return 1;
default:
abort();
}
id = html_make_id(n, 1);
print_otag(h, TAG_H2, "cTi", "Ss", id);
print_otag(h, TAG_H2, "ci", "Ss", id);
if (id != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
return 1;
@ -548,7 +625,7 @@ mdoc_fl_pre(MDOC_ARGS)
if ((id = cond_id(n)) != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
print_otag(h, TAG_CODE, "cTi", "Fl", id);
print_otag(h, TAG_CODE, "ci", "Fl", id);
print_text(h, "\\-");
if (!(n->child == NULL &&
@ -567,19 +644,27 @@ mdoc_cm_pre(MDOC_ARGS)
if ((id = cond_id(n)) != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
print_otag(h, TAG_CODE, "cTi", "Cm", id);
print_otag(h, TAG_CODE, "ci", "Cm", id);
return 1;
}
static int
mdoc_nd_pre(MDOC_ARGS)
{
if (n->type != ROFFT_BODY)
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
return 1;
case ROFFT_HEAD:
return 0;
case ROFFT_BODY:
break;
default:
abort();
}
print_text(h, "\\(em");
/* Cannot use TAG_SPAN because it may contain blocks. */
print_otag(h, TAG_DIV, "cT", "Nd");
print_otag(h, TAG_DIV, "c", "Nd");
return 1;
}
@ -587,18 +672,21 @@ static int
mdoc_nm_pre(MDOC_ARGS)
{
switch (n->type) {
case ROFFT_BLOCK:
break;
case ROFFT_HEAD:
print_otag(h, TAG_TD, "");
/* FALLTHROUGH */
case ROFFT_ELEM:
print_otag(h, TAG_CODE, "cT", "Nm");
print_otag(h, TAG_CODE, "c", "Nm");
return 1;
case ROFFT_BODY:
print_otag(h, TAG_TD, "");
return 1;
default:
break;
abort();
}
html_close_paragraph(h);
synopsis_pre(h, n);
print_otag(h, TAG_TABLE, "c", "Nm");
print_otag(h, TAG_TR, "");
@ -611,12 +699,12 @@ mdoc_xr_pre(MDOC_ARGS)
if (NULL == n->child)
return 0;
if (h->base_man)
print_otag(h, TAG_A, "cThM", "Xr",
if (h->base_man1)
print_otag(h, TAG_A, "chM", "Xr",
n->child->string, n->child->next == NULL ?
NULL : n->child->next->string);
else
print_otag(h, TAG_A, "cT", "Xr");
print_otag(h, TAG_A, "c", "Xr");
n = n->child;
print_text(h, n->string);
@ -645,7 +733,7 @@ mdoc_ns_pre(MDOC_ARGS)
static int
mdoc_ar_pre(MDOC_ARGS)
{
print_otag(h, TAG_VAR, "cT", "Ar");
print_otag(h, TAG_VAR, "c", "Ar");
return 1;
}
@ -660,7 +748,6 @@ static int
mdoc_it_pre(MDOC_ARGS)
{
const struct roff_node *bl;
struct tag *t;
enum mdoc_list type;
bl = n->parent;
@ -702,17 +789,6 @@ mdoc_it_pre(MDOC_ARGS)
case LIST_tag:
switch (n->type) {
case ROFFT_HEAD:
if (h->style != NULL && !bl->norm->Bl.comp &&
(n->parent->prev == NULL ||
n->parent->prev->body == NULL ||
n->parent->prev->body->child != NULL)) {
t = print_otag(h, TAG_DT, "");
print_text(h, "\\ ");
print_tagq(h, t);
t = print_otag(h, TAG_DD, "");
print_text(h, "\\ ");
print_tagq(h, t);
}
print_otag(h, TAG_DT, "");
break;
case ROFFT_BODY:
@ -746,17 +822,20 @@ mdoc_it_pre(MDOC_ARGS)
static int
mdoc_bl_pre(MDOC_ARGS)
{
char cattr[28];
char cattr[32];
struct mdoc_bl *bl;
enum htmltag elemtype;
switch (n->type) {
case ROFFT_BODY:
return 1;
case ROFFT_BLOCK:
html_close_paragraph(h);
break;
case ROFFT_HEAD:
return 0;
case ROFFT_BODY:
return 1;
default:
break;
abort();
}
bl = &n->norm->Bl;
@ -826,28 +905,34 @@ mdoc_ex_pre(MDOC_ARGS)
static int
mdoc_st_pre(MDOC_ARGS)
{
print_otag(h, TAG_SPAN, "cT", "St");
print_otag(h, TAG_SPAN, "c", "St");
return 1;
}
static int
mdoc_em_pre(MDOC_ARGS)
{
print_otag(h, TAG_I, "cT", "Em");
print_otag(h, TAG_I, "c", "Em");
return 1;
}
static int
mdoc_d1_pre(MDOC_ARGS)
{
if (n->type != ROFFT_BLOCK)
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
break;
case ROFFT_HEAD:
return 0;
case ROFFT_BODY:
return 1;
default:
abort();
}
print_otag(h, TAG_DIV, "c", "Bd Bd-indent");
if (n->tok == MDOC_Dl)
print_otag(h, TAG_CODE, "c", "Li");
return 1;
}
@ -857,7 +942,7 @@ mdoc_sx_pre(MDOC_ARGS)
char *id;
id = html_make_id(n, 0);
print_otag(h, TAG_A, "cThR", "Sx", id);
print_otag(h, TAG_A, "chR", "Sx", id);
free(id);
return 1;
}
@ -865,86 +950,51 @@ mdoc_sx_pre(MDOC_ARGS)
static int
mdoc_bd_pre(MDOC_ARGS)
{
int comp, sv;
char buf[16];
struct roff_node *nn;
int comp;
if (n->type == ROFFT_HEAD)
return 0;
if (n->type == ROFFT_BLOCK) {
comp = n->norm->Bd.comp;
for (nn = n; nn && ! comp; nn = nn->parent) {
if (nn->type != ROFFT_BLOCK)
continue;
if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
comp = 1;
if (nn->prev)
break;
}
if ( ! comp)
print_paragraph(h);
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
return 1;
case ROFFT_HEAD:
return 0;
case ROFFT_BODY:
break;
default:
abort();
}
/* Handle preceding whitespace. */
comp = n->norm->Bd.comp;
for (nn = n; nn != NULL && comp == 0; nn = nn->parent) {
if (nn->type != ROFFT_BLOCK)
continue;
if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
comp = 1;
if (nn->prev != NULL)
break;
}
(void)strlcpy(buf, "Bd", sizeof(buf));
if (comp == 0)
(void)strlcat(buf, " Pp", sizeof(buf));
/* Handle the -offset argument. */
if (n->norm->Bd.offs == NULL ||
! strcmp(n->norm->Bd.offs, "left"))
print_otag(h, TAG_DIV, "c", "Bd");
else
print_otag(h, TAG_DIV, "c", "Bd Bd-indent");
if (n->norm->Bd.offs != NULL &&
strcmp(n->norm->Bd.offs, "left") != 0)
(void)strlcat(buf, " Bd-indent", sizeof(buf));
if (n->norm->Bd.type != DISP_unfilled &&
n->norm->Bd.type != DISP_literal)
return 1;
print_otag(h, TAG_PRE, "c", "Li");
/* This can be recursive: save & set our literal state. */
sv = h->flags & HTML_LITERAL;
h->flags |= HTML_LITERAL;
for (nn = n->child; nn; nn = nn->next) {
print_mdoc_node(meta, nn, h);
/*
* If the printed node flushes its own line, then we
* needn't do it here as well. This is hacky, but the
* notion of selective eoln whitespace is pretty dumb
* anyway, so don't sweat it.
*/
switch (nn->tok) {
case ROFF_br:
case ROFF_sp:
case MDOC_Sm:
case MDOC_Bl:
case MDOC_D1:
case MDOC_Dl:
case MDOC_Lp:
case MDOC_Pp:
continue;
default:
break;
}
if (h->flags & HTML_NONEWLINE ||
(nn->next && ! (nn->next->flags & NODE_LINE)))
continue;
else if (nn->next)
print_text(h, "\n");
h->flags |= HTML_NOSPACE;
}
if (0 == sv)
h->flags &= ~HTML_LITERAL;
return 0;
print_otag(h, TAG_DIV, "c", buf);
return 1;
}
static int
mdoc_pa_pre(MDOC_ARGS)
{
print_otag(h, TAG_SPAN, "cT", "Pa");
print_otag(h, TAG_SPAN, "c", "Pa");
return 1;
}
@ -975,7 +1025,7 @@ mdoc_an_pre(MDOC_ARGS)
if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT))
h->flags |= HTML_SPLIT;
print_otag(h, TAG_SPAN, "cT", "An");
print_otag(h, TAG_SPAN, "c", "An");
return 1;
}
@ -983,7 +1033,7 @@ static int
mdoc_cd_pre(MDOC_ARGS)
{
synopsis_pre(h, n);
print_otag(h, TAG_CODE, "cT", "Cd");
print_otag(h, TAG_CODE, "c", "Cd");
return 1;
}
@ -994,7 +1044,7 @@ mdoc_dv_pre(MDOC_ARGS)
if ((id = cond_id(n)) != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
print_otag(h, TAG_CODE, "cTi", "Dv", id);
print_otag(h, TAG_CODE, "ci", "Dv", id);
return 1;
}
@ -1005,7 +1055,7 @@ mdoc_ev_pre(MDOC_ARGS)
if ((id = cond_id(n)) != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
print_otag(h, TAG_CODE, "cTi", "Ev", id);
print_otag(h, TAG_CODE, "ci", "Ev", id);
return 1;
}
@ -1022,7 +1072,7 @@ mdoc_er_pre(MDOC_ARGS)
if (id != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
print_otag(h, TAG_CODE, "cTi", "Er", id);
print_otag(h, TAG_CODE, "ci", "Er", id);
return 1;
}
@ -1033,12 +1083,12 @@ mdoc_fa_pre(MDOC_ARGS)
struct tag *t;
if (n->parent->tok != MDOC_Fo) {
print_otag(h, TAG_VAR, "cT", "Fa");
print_otag(h, TAG_VAR, "c", "Fa");
return 1;
}
for (nn = n->child; nn; nn = nn->next) {
t = print_otag(h, TAG_VAR, "cT", "Fa");
t = print_otag(h, TAG_VAR, "c", "Fa");
print_text(h, nn->string);
print_tagq(h, t);
if (nn->next) {
@ -1069,11 +1119,11 @@ mdoc_fd_pre(MDOC_ARGS)
assert(n->type == ROFFT_TEXT);
if (strcmp(n->string, "#include")) {
print_otag(h, TAG_CODE, "cT", "Fd");
print_otag(h, TAG_CODE, "c", "Fd");
return 1;
}
print_otag(h, TAG_CODE, "cT", "In");
print_otag(h, TAG_CODE, "c", "In");
print_text(h, n->string);
if (NULL != (n = n->next)) {
@ -1087,10 +1137,10 @@ mdoc_fd_pre(MDOC_ARGS)
cp = strchr(buf, '\0') - 1;
if (cp >= buf && (*cp == '>' || *cp == '"'))
*cp = '\0';
t = print_otag(h, TAG_A, "cThI", "In", buf);
t = print_otag(h, TAG_A, "chI", "In", buf);
free(buf);
} else
t = print_otag(h, TAG_A, "cT", "In");
t = print_otag(h, TAG_A, "c", "In");
print_text(h, n->string);
print_tagq(h, t);
@ -1117,7 +1167,7 @@ mdoc_vt_pre(MDOC_ARGS)
} else if (n->type == ROFFT_HEAD)
return 0;
print_otag(h, TAG_VAR, "cT", "Vt");
print_otag(h, TAG_VAR, "c", "Vt");
return 1;
}
@ -1125,7 +1175,7 @@ static int
mdoc_ft_pre(MDOC_ARGS)
{
synopsis_pre(h, n);
print_otag(h, TAG_VAR, "cT", "Ft");
print_otag(h, TAG_VAR, "c", "Ft");
return 1;
}
@ -1146,7 +1196,7 @@ mdoc_fn_pre(MDOC_ARGS)
ep = strchr(sp, ' ');
if (NULL != ep) {
t = print_otag(h, TAG_VAR, "cT", "Ft");
t = print_otag(h, TAG_VAR, "c", "Ft");
while (ep) {
sz = MIN((int)(ep - sp), BUFSIZ - 1);
@ -1159,7 +1209,7 @@ mdoc_fn_pre(MDOC_ARGS)
print_tagq(h, t);
}
t = print_otag(h, TAG_CODE, "cT", "Fn");
t = print_otag(h, TAG_CODE, "c", "Fn");
if (sp)
print_text(h, sp);
@ -1172,10 +1222,10 @@ mdoc_fn_pre(MDOC_ARGS)
for (n = n->child->next; n; n = n->next) {
if (NODE_SYNPRETTY & n->flags)
t = print_otag(h, TAG_VAR, "cTs", "Fa",
t = print_otag(h, TAG_VAR, "cs", "Fa",
"white-space", "nowrap");
else
t = print_otag(h, TAG_VAR, "cT", "Fa");
t = print_otag(h, TAG_VAR, "c", "Fa");
print_text(h, n->string);
print_tagq(h, t);
if (n->next) {
@ -1222,8 +1272,10 @@ mdoc_skip_pre(MDOC_ARGS)
static int
mdoc_pp_pre(MDOC_ARGS)
{
print_paragraph(h);
if ((n->flags & NODE_NOFILL) == 0) {
html_close_paragraph(h);
print_otag(h, TAG_P, "c", "Pp");
}
return 0;
}
@ -1246,7 +1298,7 @@ mdoc_lk_pre(MDOC_ARGS)
descr = link->next;
if (descr == punct)
descr = link; /* no text */
t = print_otag(h, TAG_A, "cTh", "Lk", link->string);
t = print_otag(h, TAG_A, "ch", "Lk", link->string);
do {
if (descr->flags & (NODE_DELIMC | NODE_DELIMO))
h->flags |= HTML_NOSPACE;
@ -1274,7 +1326,7 @@ mdoc_mt_pre(MDOC_ARGS)
assert(n->type == ROFFT_TEXT);
mandoc_asprintf(&cp, "mailto:%s", n->string);
t = print_otag(h, TAG_A, "cTh", "Mt", cp);
t = print_otag(h, TAG_A, "ch", "Mt", cp);
print_text(h, n->string);
print_tagq(h, t);
free(cp);
@ -1302,7 +1354,7 @@ mdoc_fo_pre(MDOC_ARGS)
return 0;
assert(n->child->string);
t = print_otag(h, TAG_CODE, "cT", "Fn");
t = print_otag(h, TAG_CODE, "c", "Fn");
print_text(h, n->child->string);
print_tagq(h, t);
return 0;
@ -1326,7 +1378,7 @@ mdoc_in_pre(MDOC_ARGS)
struct tag *t;
synopsis_pre(h, n);
print_otag(h, TAG_CODE, "cT", "In");
print_otag(h, TAG_CODE, "c", "In");
/*
* The first argument of the `In' gets special treatment as
@ -1345,9 +1397,9 @@ mdoc_in_pre(MDOC_ARGS)
assert(n->type == ROFFT_TEXT);
if (h->base_includes)
t = print_otag(h, TAG_A, "cThI", "In", n->string);
t = print_otag(h, TAG_A, "chI", "In", n->string);
else
t = print_otag(h, TAG_A, "cT", "In");
t = print_otag(h, TAG_A, "c", "In");
print_text(h, n->string);
print_tagq(h, t);
@ -1372,14 +1424,14 @@ mdoc_ic_pre(MDOC_ARGS)
if ((id = cond_id(n)) != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
print_otag(h, TAG_CODE, "cTi", "Ic", id);
print_otag(h, TAG_CODE, "ci", "Ic", id);
return 1;
}
static int
mdoc_va_pre(MDOC_ARGS)
{
print_otag(h, TAG_VAR, "cT", "Va");
print_otag(h, TAG_VAR, "c", "Va");
return 1;
}
@ -1398,10 +1450,17 @@ mdoc_bf_pre(MDOC_ARGS)
{
const char *cattr;
if (n->type == ROFFT_HEAD)
return 0;
else if (n->type != ROFFT_BODY)
switch (n->type) {
case ROFFT_BLOCK:
html_close_paragraph(h);
return 1;
case ROFFT_HEAD:
return 0;
case ROFFT_BODY:
break;
default:
abort();
}
if (FONT_Em == n->norm->Bf.font)
cattr = "Bf Em";
@ -1424,7 +1483,7 @@ mdoc_ms_pre(MDOC_ARGS)
if ((id = cond_id(n)) != NULL)
print_otag(h, TAG_A, "chR", "permalink", id);
print_otag(h, TAG_SPAN, "cTi", "Ms", id);
print_otag(h, TAG_SPAN, "ci", "Ms", id);
return 1;
}
@ -1447,13 +1506,21 @@ mdoc_pf_post(MDOC_ARGS)
static int
mdoc_rs_pre(MDOC_ARGS)
{
if (n->type != ROFFT_BLOCK)
return 1;
if (n->prev && SEC_SEE_ALSO == n->sec)
print_paragraph(h);
print_otag(h, TAG_CITE, "cT", "Rs");
switch (n->type) {
case ROFFT_BLOCK:
if (n->sec == SEC_SEE_ALSO)
html_close_paragraph(h);
break;
case ROFFT_HEAD:
return 0;
case ROFFT_BODY:
if (n->sec == SEC_SEE_ALSO)
print_otag(h, TAG_P, "c", "Pp");
print_otag(h, TAG_CITE, "c", "Rs");
break;
default:
abort();
}
return 1;
}
@ -1482,7 +1549,7 @@ mdoc_li_pre(MDOC_ARGS)
static int
mdoc_sy_pre(MDOC_ARGS)
{
print_otag(h, TAG_B, "cT", "Sy");
print_otag(h, TAG_B, "c", "Sy");
return 1;
}
@ -1492,7 +1559,7 @@ mdoc_lb_pre(MDOC_ARGS)
if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev)
print_otag(h, TAG_BR, "");
print_otag(h, TAG_SPAN, "cT", "Lb");
print_otag(h, TAG_SPAN, "c", "Lb");
return 1;
}
@ -1630,9 +1697,15 @@ mdoc_quote_pre(MDOC_ARGS)
case MDOC_Oo:
case MDOC_Op:
print_text(h, "\\(lB");
h->flags |= HTML_NOSPACE;
/* Cannot use TAG_SPAN because it may contain blocks. */
print_otag(h, TAG_IDIV, "c", "Op");
/*
* Give up on semantic markup for now.
* We cannot use TAG_SPAN because .Oo may contain blocks.
* We cannot use TAG_IDIV because we might be in a
* phrasing context (like .Dl or .Pp); we cannot
* close out a .Pp at this point either because
* that would break the line.
*/
/* XXX print_otag(h, TAG_???, "c", "Op"); */
break;
case MDOC_En:
if (NULL == n->norm->Es ||
@ -1760,3 +1833,9 @@ mdoc_eo_post(MDOC_ARGS)
else if ( ! tail)
h->flags &= ~HTML_NOSPACE;
}
static int
mdoc_abort_pre(MDOC_ARGS)
{
abort();
}

View File

@ -1,7 +1,7 @@
/* $Id: mdoc_macro.c,v 1.224 2017/05/30 16:22:03 schwarze Exp $ */
/* $Id: mdoc_macro.c,v 1.232 2019/01/07 07:26:29 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2012-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -49,7 +49,7 @@ static void dword(struct roff_man *, int, int, const char *,
static int find_pending(struct roff_man *, enum roff_tok,
int, int, struct roff_node *);
static int lookup(struct roff_man *, int, int, int, const char *);
static int macro_or_word(MACRO_PROT_ARGS, int);
static int macro_or_word(MACRO_PROT_ARGS, char *, int);
static void break_intermediate(struct roff_node *,
struct roff_node *);
static int parse_rest(struct roff_man *, enum roff_tok,
@ -60,7 +60,7 @@ static void rew_last(struct roff_man *, const struct roff_node *);
static void rew_pending(struct roff_man *,
const struct roff_node *);
const struct mdoc_macro __mdoc_macros[MDOC_MAX - MDOC_Dd] = {
static const struct mdoc_macro mdoc_macros[MDOC_MAX - MDOC_Dd] = {
{ in_line_eoln, MDOC_PROLOGUE }, /* Dd */
{ in_line_eoln, MDOC_PROLOGUE }, /* Dt */
{ in_line_eoln, MDOC_PROLOGUE }, /* Os */
@ -201,9 +201,15 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX - MDOC_Dd] = {
{ in_line_eoln, 0 }, /* %U */
{ phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */
};
const struct mdoc_macro *const mdoc_macros = __mdoc_macros - MDOC_Dd;
const struct mdoc_macro *
mdoc_macro(enum roff_tok tok)
{
assert(tok >= MDOC_Dd && tok < MDOC_MAX);
return mdoc_macros + (tok - MDOC_Dd);
}
/*
* This is called at the end of parsing. It must traverse up the tree,
* closing out open [implicit] scopes. Obviously, open explicit scopes
@ -221,14 +227,13 @@ mdoc_endparse(struct roff_man *mdoc)
for ( ; n; n = n->parent)
if (n->type == ROFFT_BLOCK &&
mdoc_macros[n->tok].flags & MDOC_EXPLICIT)
mandoc_msg(MANDOCERR_BLK_NOEND, mdoc->parse,
n->line, n->pos, roff_name[n->tok]);
mdoc_macro(n->tok)->flags & MDOC_EXPLICIT)
mandoc_msg(MANDOCERR_BLK_NOEND,
n->line, n->pos, "%s", roff_name[n->tok]);
/* Rewind to the first. */
rew_last(mdoc, mdoc->first);
mdoc_state_reset(mdoc);
rew_last(mdoc, mdoc->meta.first);
}
/*
@ -244,13 +249,12 @@ lookup(struct roff_man *mdoc, int from, int line, int ppos, const char *p)
mdoc->flags &= ~MDOC_PHRASEQF;
return TOKEN_NONE;
}
if (from == TOKEN_NONE || mdoc_macros[from].flags & MDOC_PARSED) {
if (from == TOKEN_NONE || mdoc_macro(from)->flags & MDOC_PARSED) {
res = roffhash_find(mdoc->mdocmac, p, 0);
if (res != TOKEN_NONE) {
if (mdoc_macros[res].flags & MDOC_CALLABLE)
if (mdoc_macro(res)->flags & MDOC_CALLABLE)
return res;
mandoc_msg(MANDOCERR_MACRO_CALL,
mdoc->parse, line, ppos, p);
mandoc_msg(MANDOCERR_MACRO_CALL, line, ppos, "%s", p);
}
}
return TOKEN_NONE;
@ -291,6 +295,8 @@ rew_pending(struct roff_man *mdoc, const struct roff_node *n)
case ROFFT_HEAD:
roff_body_alloc(mdoc, n->line, n->pos,
n->tok);
if (n->tok == MDOC_Ss)
mdoc->flags &= ~ROFF_NONOFILL;
break;
case ROFFT_BLOCK:
break;
@ -409,16 +415,15 @@ find_pending(struct roff_man *mdoc, enum roff_tok tok, int line, int ppos,
if (n->flags & NODE_ENDED)
continue;
if (n->type == ROFFT_BLOCK &&
mdoc_macros[n->tok].flags & MDOC_EXPLICIT) {
mdoc_macro(n->tok)->flags & MDOC_EXPLICIT) {
irc = 1;
break_intermediate(mdoc->last, target);
if (target->type == ROFFT_HEAD)
target->flags |= NODE_ENDED;
else if ( ! (target->flags & NODE_ENDED)) {
mandoc_vmsg(MANDOCERR_BLK_NEST,
mdoc->parse, line, ppos,
"%s breaks %s", roff_name[tok],
roff_name[n->tok]);
mandoc_msg(MANDOCERR_BLK_NEST,
line, ppos, "%s breaks %s",
roff_name[tok], roff_name[n->tok]);
mdoc_endbody_alloc(mdoc, line, ppos,
tok, target);
}
@ -470,14 +475,15 @@ append_delims(struct roff_man *mdoc, int line, int *pos, char *buf)
{
char *p;
int la;
enum margserr ac;
if (buf[*pos] == '\0')
return;
for (;;) {
la = *pos;
if (mdoc_args(mdoc, line, pos, buf, TOKEN_NONE, &p) ==
ARGS_EOLN)
ac = mdoc_args(mdoc, line, pos, buf, TOKEN_NONE, &p);
if (ac == ARGS_EOLN)
break;
dword(mdoc, line, la, p, DELIM_MAX, 1);
@ -495,6 +501,8 @@ append_delims(struct roff_man *mdoc, int line, int *pos, char *buf)
if (mandoc_eos(p, strlen(p)))
mdoc->last->flags |= NODE_EOS;
if (ac == ARGS_ALLOC)
free(p);
}
}
@ -504,27 +512,23 @@ append_delims(struct roff_man *mdoc, int line, int *pos, char *buf)
* Otherwise, allocate it and return 0.
*/
static int
macro_or_word(MACRO_PROT_ARGS, int parsed)
macro_or_word(MACRO_PROT_ARGS, char *p, int parsed)
{
char *p;
int ntok;
p = buf + ppos;
ntok = TOKEN_NONE;
if (*p == '"')
p++;
else if (parsed && ! (mdoc->flags & MDOC_PHRASELIT))
ntok = lookup(mdoc, tok, line, ppos, p);
ntok = buf[ppos] == '"' || parsed == 0 ||
mdoc->flags & MDOC_PHRASELIT ? TOKEN_NONE :
lookup(mdoc, tok, line, ppos, p);
if (ntok == TOKEN_NONE) {
dword(mdoc, line, ppos, p, DELIM_MAX, tok == TOKEN_NONE ||
mdoc_macros[tok].flags & MDOC_JOIN);
mdoc_macro(tok)->flags & MDOC_JOIN);
return 0;
} else {
if (tok != TOKEN_NONE &&
mdoc_macros[tok].fp == in_line_eoln)
mdoc_macro(tok)->fp == in_line_eoln)
rew_elem(mdoc, tok);
mdoc_macro(mdoc, ntok, line, ppos, pos, buf);
(*mdoc_macro(ntok)->fp)(mdoc, ntok, line, ppos, pos, buf);
if (tok == TOKEN_NONE)
append_delims(mdoc, line, pos, buf);
return 1;
@ -629,7 +633,7 @@ blk_exp_close(MACRO_PROT_ARGS)
* the scope - of the current block ends.
*/
mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse,
mandoc_msg(MANDOCERR_BLK_NEST,
line, ppos, "%s breaks %s",
roff_name[atok], roff_name[later->tok]);
@ -672,8 +676,8 @@ blk_exp_close(MACRO_PROT_ARGS)
}
if (body == NULL) {
mandoc_msg(MANDOCERR_BLK_NOTOPEN, mdoc->parse,
line, ppos, roff_name[tok]);
mandoc_msg(MANDOCERR_BLK_NOTOPEN, line, ppos,
"%s", roff_name[tok]);
if (maxargs && endbody == NULL) {
/*
* Stray .Ec without previous .Eo:
@ -688,14 +692,25 @@ blk_exp_close(MACRO_PROT_ARGS)
mdoc_tail_alloc(mdoc, line, ppos, atok);
}
if ( ! (mdoc_macros[tok].flags & MDOC_PARSED)) {
if ((mdoc_macro(tok)->flags & MDOC_PARSED) == 0) {
if (buf[*pos] != '\0')
mandoc_vmsg(MANDOCERR_ARG_SKIP,
mdoc->parse, line, ppos,
"%s %s", roff_name[tok],
buf + *pos);
mandoc_msg(MANDOCERR_ARG_SKIP, line, ppos,
"%s %s", roff_name[tok], buf + *pos);
if (endbody == NULL && n != NULL)
rew_pending(mdoc, n);
/*
* Restore the fill mode that was set before the display.
* This needs to be done here rather than during validation
* such that subsequent nodes get the right flags.
*/
if (tok == MDOC_Ed && body != NULL) {
if (body->flags & NODE_NOFILL)
mdoc->flags |= ROFF_NOFILL;
else
mdoc->flags &= ~ROFF_NOFILL;
}
return;
}
@ -717,14 +732,18 @@ blk_exp_close(MACRO_PROT_ARGS)
if (ntok == TOKEN_NONE) {
dword(mdoc, line, lastarg, p, DELIM_MAX,
MDOC_JOIN & mdoc_macros[tok].flags);
mdoc_macro(tok)->flags & MDOC_JOIN);
if (ac == ARGS_ALLOC)
free(p);
continue;
}
if (ac == ARGS_ALLOC)
free(p);
if (n != NULL)
rew_last(mdoc, n);
mdoc->flags &= ~MDOC_NEWLINE;
mdoc_macro(mdoc, ntok, line, lastarg, pos, buf);
(*mdoc_macro(ntok)->fp)(mdoc, ntok, line, lastarg, pos, buf);
break;
}
@ -828,12 +847,14 @@ in_line(MACRO_PROT_ARGS)
} else if ( ! nc && ! cnt) {
mdoc_argv_free(arg);
mandoc_msg(MANDOCERR_MACRO_EMPTY,
mdoc->parse, line, ppos,
roff_name[tok]);
line, ppos, "%s", roff_name[tok]);
}
mdoc_macro(mdoc, ntok, line, la, pos, buf);
(*mdoc_macro(ntok)->fp)(mdoc, ntok,
line, la, pos, buf);
if (nl)
append_delims(mdoc, line, pos, buf);
if (ac == ARGS_ALLOC)
free(p);
return;
}
@ -875,7 +896,10 @@ in_line(MACRO_PROT_ARGS)
}
dword(mdoc, line, la, p, d,
mdoc_macros[tok].flags & MDOC_JOIN);
mdoc_macro(tok)->flags & MDOC_JOIN);
if (ac == ARGS_ALLOC)
free(p);
/*
* If the first argument is a closing delimiter,
@ -914,8 +938,8 @@ in_line(MACRO_PROT_ARGS)
rew_last(mdoc, mdoc->last);
} else {
mdoc_argv_free(arg);
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
line, ppos, roff_name[tok]);
mandoc_msg(MANDOCERR_MACRO_EMPTY,
line, ppos, "%s", roff_name[tok]);
}
}
if (nl)
@ -927,24 +951,25 @@ in_line(MACRO_PROT_ARGS)
static void
blk_full(MACRO_PROT_ARGS)
{
int la, nl, parsed;
struct mdoc_arg *arg;
struct roff_node *blk; /* Our own or a broken block. */
struct roff_node *head; /* Our own head. */
struct roff_node *body; /* Our own body. */
struct roff_node *n;
enum margserr ac, lac;
char *p;
size_t iarg;
int done, la, nl, parsed;
enum margserr ac, lac;
nl = MDOC_NEWLINE & mdoc->flags;
if (buf[*pos] == '\0' && (tok == MDOC_Sh || tok == MDOC_Ss)) {
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
line, ppos, roff_name[tok]);
mandoc_msg(MANDOCERR_MACRO_EMPTY,
line, ppos, "%s", roff_name[tok]);
return;
}
if ( ! (mdoc_macros[tok].flags & MDOC_EXPLICIT)) {
if ((mdoc_macro(tok)->flags & MDOC_EXPLICIT) == 0) {
/* Here, tok is one of Sh Ss Nm Nd It. */
@ -960,21 +985,20 @@ blk_full(MACRO_PROT_ARGS)
if (tok == MDOC_It && n->tok == MDOC_Bl) {
if (blk != NULL) {
mandoc_vmsg(MANDOCERR_BLK_BROKEN,
mdoc->parse, line, ppos,
"It breaks %s",
mandoc_msg(MANDOCERR_BLK_BROKEN,
line, ppos, "It breaks %s",
roff_name[blk->tok]);
rew_pending(mdoc, blk);
}
break;
}
if (mdoc_macros[n->tok].flags & MDOC_EXPLICIT) {
if (mdoc_macro(n->tok)->flags & MDOC_EXPLICIT) {
switch (tok) {
case MDOC_Sh:
case MDOC_Ss:
mandoc_vmsg(MANDOCERR_BLK_BROKEN,
mdoc->parse, line, ppos,
mandoc_msg(MANDOCERR_BLK_BROKEN,
line, ppos,
"%s breaks %s", roff_name[tok],
roff_name[n->tok]);
rew_pending(mdoc, n);
@ -1000,8 +1024,7 @@ blk_full(MACRO_PROT_ARGS)
/* Item breaking an explicit block. */
if (blk != NULL) {
mandoc_vmsg(MANDOCERR_BLK_BROKEN,
mdoc->parse, line, ppos,
mandoc_msg(MANDOCERR_BLK_BROKEN, line, ppos,
"It breaks %s", roff_name[blk->tok]);
rew_pending(mdoc, blk);
blk = NULL;
@ -1015,7 +1038,7 @@ blk_full(MACRO_PROT_ARGS)
/* Skip items outside lists. */
if (tok == MDOC_It && (n == NULL || n->tok != MDOC_Bl)) {
mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse,
mandoc_msg(MANDOCERR_IT_STRAY,
line, ppos, "It %s", buf + *pos);
roff_elem_alloc(mdoc, line, ppos, ROFF_br);
rew_elem(mdoc, ROFF_br);
@ -1032,6 +1055,16 @@ blk_full(MACRO_PROT_ARGS)
* regular child nodes.
*/
switch (tok) {
case MDOC_Sh:
mdoc->flags &= ~ROFF_NOFILL;
break;
case MDOC_Ss:
mdoc->flags |= ROFF_NONOFILL;
break;
default:
break;
}
mdoc_argv(mdoc, line, tok, &arg, pos, buf);
blk = mdoc_block_alloc(mdoc, line, ppos, tok, arg);
head = body = NULL;
@ -1093,14 +1126,17 @@ blk_full(MACRO_PROT_ARGS)
}
if (tok == MDOC_Bd || tok == MDOC_Bk) {
mandoc_vmsg(MANDOCERR_ARG_EXCESS,
mdoc->parse, line, la, "%s ... %s",
roff_name[tok], buf + la);
mandoc_msg(MANDOCERR_ARG_EXCESS, line, la,
"%s ... %s", roff_name[tok], buf + la);
if (ac == ARGS_ALLOC)
free(p);
break;
}
if (tok == MDOC_Rs) {
mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse,
mandoc_msg(MANDOCERR_ARG_SKIP,
line, la, "Rs %s", buf + la);
if (ac == ARGS_ALLOC)
free(p);
break;
}
if (ac == ARGS_PUNCT)
@ -1115,6 +1151,8 @@ blk_full(MACRO_PROT_ARGS)
ac != ARGS_PHRASE &&
mdoc_isdelim(p) == DELIM_OPEN) {
dword(mdoc, line, la, p, DELIM_OPEN, 0);
if (ac == ARGS_ALLOC)
free(p);
continue;
}
@ -1146,7 +1184,10 @@ blk_full(MACRO_PROT_ARGS)
continue;
}
if (macro_or_word(mdoc, tok, line, la, pos, buf, parsed))
done = macro_or_word(mdoc, tok, line, la, pos, buf, p, parsed);
if (ac == ARGS_ALLOC)
free(p);
if (done)
break;
}
@ -1165,6 +1206,33 @@ blk_full(MACRO_PROT_ARGS)
rew_last(mdoc, head);
body = roff_body_alloc(mdoc, line, ppos, tok);
if (tok == MDOC_Ss)
mdoc->flags &= ~ROFF_NONOFILL;
/*
* Set up fill mode for display blocks.
* This needs to be done here up front rather than during
* validation such that child nodes get the right flags.
*/
if (tok == MDOC_Bd && arg != NULL) {
for (iarg = 0; iarg < arg->argc; iarg++) {
switch (arg->argv[iarg].arg) {
case MDOC_Unfilled:
case MDOC_Literal:
mdoc->flags |= ROFF_NOFILL;
break;
case MDOC_Filled:
case MDOC_Ragged:
case MDOC_Centred:
mdoc->flags &= ~ROFF_NOFILL;
break;
default:
continue;
}
break;
}
}
out:
if (mdoc->flags & MDOC_FREECOL) {
rew_last(mdoc, body);
@ -1176,7 +1244,7 @@ blk_full(MACRO_PROT_ARGS)
static void
blk_part_imp(MACRO_PROT_ARGS)
{
int la, nl;
int done, la, nl;
enum margserr ac;
char *p;
struct roff_node *blk; /* saved block context */
@ -1211,13 +1279,18 @@ blk_part_imp(MACRO_PROT_ARGS)
if (body == NULL && mdoc_isdelim(p) == DELIM_OPEN) {
dword(mdoc, line, la, p, DELIM_OPEN, 0);
if (ac == ARGS_ALLOC)
free(p);
continue;
}
if (body == NULL)
body = roff_body_alloc(mdoc, line, ppos, tok);
if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
done = macro_or_word(mdoc, tok, line, la, pos, buf, p, 1);
if (ac == ARGS_ALLOC)
free(p);
if (done)
break;
}
if (body == NULL)
@ -1236,13 +1309,13 @@ blk_part_imp(MACRO_PROT_ARGS)
for (n = body->child; n && n->next; n = n->next)
/* Do nothing. */ ;
if (n && n->tok == MDOC_Ns)
mdoc_node_relink(mdoc, n);
roff_node_relink(mdoc, n);
}
static void
blk_part_exp(MACRO_PROT_ARGS)
{
int la, nl;
int done, la, nl;
enum margserr ac;
struct roff_node *head; /* keep track of head */
char *p;
@ -1267,6 +1340,8 @@ blk_part_exp(MACRO_PROT_ARGS)
if (head == NULL && mdoc_isdelim(p) == DELIM_OPEN) {
dword(mdoc, line, la, p, DELIM_OPEN, 0);
if (ac == ARGS_ALLOC)
free(p);
continue;
}
@ -1276,11 +1351,17 @@ blk_part_exp(MACRO_PROT_ARGS)
dword(mdoc, line, la, p, DELIM_MAX, 0);
rew_last(mdoc, head);
roff_body_alloc(mdoc, line, ppos, tok);
if (tok == MDOC_Eo)
if (tok == MDOC_Eo) {
if (ac == ARGS_ALLOC)
free(p);
continue;
}
}
if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
done = macro_or_word(mdoc, tok, line, la, pos, buf, p, 1);
if (ac == ARGS_ALLOC)
free(p);
if (done)
break;
}
@ -1338,10 +1419,12 @@ in_line_argn(MACRO_PROT_ARGS)
la = *pos;
ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
if (ac == ARGS_WORD && state == -1 &&
! (mdoc_macros[tok].flags & MDOC_IGNDELIM) &&
if ((ac == ARGS_WORD || ac == ARGS_ALLOC) && state == -1 &&
(mdoc_macro(tok)->flags & MDOC_IGNDELIM) == 0 &&
mdoc_isdelim(p) == DELIM_OPEN) {
dword(mdoc, line, la, p, DELIM_OPEN, 0);
if (ac == ARGS_ALLOC)
free(p);
continue;
}
@ -1353,8 +1436,8 @@ in_line_argn(MACRO_PROT_ARGS)
if (ac == ARGS_PUNCT || ac == ARGS_EOLN) {
if (abs(state) < 2 && tok == MDOC_Pf)
mandoc_vmsg(MANDOCERR_PF_SKIP,
mdoc->parse, line, ppos, "Pf %s",
mandoc_msg(MANDOCERR_PF_SKIP,
line, ppos, "Pf %s",
p == NULL ? "at eol" : p);
break;
}
@ -1372,11 +1455,14 @@ in_line_argn(MACRO_PROT_ARGS)
rew_elem(mdoc, tok);
state = -2;
}
mdoc_macro(mdoc, ntok, line, la, pos, buf);
(*mdoc_macro(ntok)->fp)(mdoc, ntok,
line, la, pos, buf);
if (ac == ARGS_ALLOC)
free(p);
break;
}
if (mdoc_macros[tok].flags & MDOC_IGNDELIM ||
if (mdoc_macro(tok)->flags & MDOC_IGNDELIM ||
mdoc_isdelim(p) == DELIM_NONE) {
if (state == -1) {
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
@ -1389,12 +1475,15 @@ in_line_argn(MACRO_PROT_ARGS)
}
dword(mdoc, line, la, p, DELIM_MAX,
mdoc_macros[tok].flags & MDOC_JOIN);
mdoc_macro(tok)->flags & MDOC_JOIN);
if (ac == ARGS_ALLOC)
free(p);
p = mdoc->last->string;
}
if (state == -1) {
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
line, ppos, roff_name[tok]);
mandoc_msg(MANDOCERR_MACRO_EMPTY,
line, ppos, "%s", roff_name[tok]);
return;
}
@ -1423,8 +1512,8 @@ in_line_eoln(MACRO_PROT_ARGS)
if (buf[*pos] == '\0' &&
(tok == MDOC_Fd || *roff_name[tok] == '%')) {
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
line, ppos, roff_name[tok]);
mandoc_msg(MANDOCERR_MACRO_EMPTY,
line, ppos, "%s", roff_name[tok]);
return;
}
@ -1444,13 +1533,19 @@ static int
parse_rest(struct roff_man *mdoc, enum roff_tok tok,
int line, int *pos, char *buf)
{
int la;
char *p;
int done, la;
enum margserr ac;
for (;;) {
la = *pos;
if (mdoc_args(mdoc, line, pos, buf, tok, NULL) == ARGS_EOLN)
ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
if (ac == ARGS_EOLN)
return 0;
if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
done = macro_or_word(mdoc, tok, line, la, pos, buf, p, 1);
if (ac == ARGS_ALLOC)
free(p);
if (done)
return 1;
}
}
@ -1492,8 +1587,7 @@ phrase_ta(MACRO_PROT_ARGS)
}
if (n == NULL || n->norm->Bl.type != LIST_column) {
mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse,
line, ppos, "Ta");
mandoc_msg(MANDOCERR_TA_STRAY, line, ppos, "Ta");
return;
}

View File

@ -1,6 +1,6 @@
/* $Id: mdoc_man.c,v 1.126 2018/04/11 17:11:13 schwarze Exp $ */
/* $Id: mdoc_man.c,v 1.132 2019/01/04 03:17:36 schwarze Exp $ */
/*
* Copyright (c) 2011-2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -36,7 +36,7 @@
typedef int (*int_fp)(DECL_ARGS);
typedef void (*void_fp)(DECL_ARGS);
struct manact {
struct mdoc_man_act {
int_fp cond; /* DON'T run actions */
int_fp pre; /* pre-node action */
void_fp post; /* post-node action */
@ -75,6 +75,7 @@ static void post_pf(DECL_ARGS);
static void post_sect(DECL_ARGS);
static void post_vt(DECL_ARGS);
static int pre__t(DECL_ARGS);
static int pre_abort(DECL_ARGS);
static int pre_an(DECL_ARGS);
static int pre_ap(DECL_ARGS);
static int pre_aq(DECL_ARGS);
@ -103,6 +104,7 @@ static int pre_lk(DECL_ARGS);
static int pre_li(DECL_ARGS);
static int pre_nm(DECL_ARGS);
static int pre_no(DECL_ARGS);
static void pre_noarg(DECL_ARGS);
static int pre_ns(DECL_ARGS);
static void pre_onearg(DECL_ARGS);
static int pre_pp(DECL_ARGS);
@ -124,12 +126,14 @@ static void print_width(const struct mdoc_bl *,
static void print_count(int *);
static void print_node(DECL_ARGS);
static const void_fp roff_manacts[ROFF_MAX] = {
static const void_fp roff_man_acts[ROFF_MAX] = {
pre_br, /* br */
pre_onearg, /* ce */
pre_noarg, /* fi */
pre_ft, /* ft */
pre_onearg, /* ll */
pre_onearg, /* mc */
pre_noarg, /* nf */
pre_onearg, /* po */
pre_onearg, /* rj */
pre_sp, /* sp */
@ -137,7 +141,7 @@ static const void_fp roff_manacts[ROFF_MAX] = {
pre_onearg, /* ti */
};
static const struct manact __manacts[MDOC_MAX - MDOC_Dd] = {
static const struct mdoc_man_act mdoc_man_acts[MDOC_MAX - MDOC_Dd] = {
{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
{ NULL, NULL, NULL, NULL, NULL }, /* Os */
@ -172,7 +176,7 @@ static const struct manact __manacts[MDOC_MAX - MDOC_Dd] = {
{ cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
{ NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
{ NULL, pre_Ft, post_font, NULL, NULL }, /* Ot */
{ NULL, pre_abort, NULL, NULL, NULL }, /* Ot */
{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
{ NULL, pre_ex, NULL, NULL, NULL }, /* Rv */
{ NULL, NULL, NULL, NULL, NULL }, /* St */
@ -245,7 +249,7 @@ static const struct manact __manacts[MDOC_MAX - MDOC_Dd] = {
{ NULL, pre_em, post_font, NULL, NULL }, /* Fr */
{ NULL, NULL, NULL, NULL, NULL }, /* Ud */
{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
{ NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
{ NULL, pre_abort, NULL, NULL, NULL }, /* Lp */
{ NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
{ NULL, pre_em, post_font, NULL, NULL }, /* Mt */
{ cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
@ -259,7 +263,7 @@ static const struct manact __manacts[MDOC_MAX - MDOC_Dd] = {
{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
};
static const struct manact *const manacts = __manacts - MDOC_Dd;
static const struct mdoc_man_act *mdoc_man_act(enum roff_tok);
static int outflags;
#define MMAN_spc (1 << 0) /* blank character before next word */
@ -290,6 +294,13 @@ static struct {
} fontqueue;
static const struct mdoc_man_act *
mdoc_man_act(enum roff_tok tok)
{
assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
return mdoc_man_acts + (tok - MDOC_Dd);
}
static int
man_strlen(const char *cp)
{
@ -317,6 +328,7 @@ man_strlen(const char *cp)
case ESCAPE_UNICODE:
case ESCAPE_NUMBERED:
case ESCAPE_SPECIAL:
case ESCAPE_UNDEF:
case ESCAPE_OVERSTRIKE:
if (skip)
skip = 0;
@ -593,20 +605,7 @@ print_count(int *count)
}
void
man_man(void *arg, const struct roff_man *man)
{
/*
* Dump the keep buffer.
* We're guaranteed by now that this exists (is non-NULL).
* Flush stdout afterward, just in case.
*/
fputs(mparse_getkeep(man_mparse(man)), stdout);
fflush(stdout);
}
void
man_mdoc(void *arg, const struct roff_man *mdoc)
man_mdoc(void *arg, const struct roff_meta *mdoc)
{
struct roff_node *n;
@ -619,9 +618,8 @@ man_mdoc(void *arg, const struct roff_man *mdoc)
}
printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
mdoc->meta.title,
(mdoc->meta.msec == NULL ? "" : mdoc->meta.msec),
mdoc->meta.date, mdoc->meta.os, mdoc->meta.vol);
mdoc->title, (mdoc->msec == NULL ? "" : mdoc->msec),
mdoc->date, mdoc->os, mdoc->vol);
/* Disable hyphenation and if nroff, disable justification. */
printf(".nh\n.if n .ad l");
@ -633,16 +631,16 @@ man_mdoc(void *arg, const struct roff_man *mdoc)
*fontqueue.tail = 'R';
}
for (; n != NULL; n = n->next)
print_node(&mdoc->meta, n);
print_node(mdoc, n);
putchar('\n');
}
static void
print_node(DECL_ARGS)
{
const struct manact *act;
struct roff_node *sub;
int cond, do_sub;
const struct mdoc_man_act *act;
struct roff_node *sub;
int cond, do_sub;
if (n->flags & NODE_NOPRT)
return;
@ -680,15 +678,14 @@ print_node(DECL_ARGS)
else if (outflags & MMAN_Sm)
outflags |= MMAN_spc;
} else if (n->tok < ROFF_MAX) {
(*roff_manacts[n->tok])(meta, n);
(*roff_man_acts[n->tok])(meta, n);
return;
} else {
assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
/*
* Conditionally run the pre-node action handler for a
* node.
*/
act = manacts + n->tok;
act = mdoc_man_act(n->tok);
cond = act->cond == NULL || (*act->cond)(meta, n);
if (cond && act->pre != NULL &&
(n->end == ENDBODY_NOT || n->child != NULL))
@ -731,12 +728,18 @@ cond_body(DECL_ARGS)
return n->type == ROFFT_BODY;
}
static int
pre_abort(DECL_ARGS)
{
abort();
}
static int
pre_enc(DECL_ARGS)
{
const char *prefix;
prefix = manacts[n->tok].prefix;
prefix = mdoc_man_act(n->tok)->prefix;
if (NULL == prefix)
return 1;
print_word(prefix);
@ -749,7 +752,7 @@ post_enc(DECL_ARGS)
{
const char *suffix;
suffix = manacts[n->tok].suffix;
suffix = mdoc_man_act(n->tok)->suffix;
if (NULL == suffix)
return;
outflags &= ~(MMAN_spc | MMAN_nl);
@ -774,7 +777,7 @@ static void
post_percent(DECL_ARGS)
{
if (pre_em == manacts[n->tok].pre)
if (mdoc_man_act(n->tok)->pre == pre_em)
font_pop();
if (n->next) {
print_word(",");
@ -820,7 +823,7 @@ pre_sect(DECL_ARGS)
if (n->type == ROFFT_HEAD) {
outflags |= MMAN_sp;
print_block(manacts[n->tok].prefix, 0);
print_block(mdoc_man_act(n->tok)->prefix, 0);
print_word("");
putchar('\"');
outflags &= ~MMAN_spc;
@ -936,7 +939,6 @@ post_aq(DECL_ARGS)
static int
pre_bd(DECL_ARGS)
{
outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
if (DISP_unfilled == n->norm->Bd.type ||
@ -951,12 +953,27 @@ pre_bd(DECL_ARGS)
static void
post_bd(DECL_ARGS)
{
enum roff_tok bef, now;
/* Close out this display. */
print_line(".RE", MMAN_nl);
if (DISP_unfilled == n->norm->Bd.type ||
DISP_literal == n->norm->Bd.type)
print_line(".fi", MMAN_nl);
bef = n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi;
if (n->last == NULL)
now = n->norm->Bd.type == DISP_unfilled ||
n->norm->Bd.type == DISP_literal ? ROFF_nf : ROFF_fi;
else if (n->last->tok == ROFF_nf)
now = ROFF_nf;
else if (n->last->tok == ROFF_fi)
now = ROFF_fi;
else
now = n->last->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi;
if (bef != now) {
outflags |= MMAN_nl;
print_word(".");
outflags &= ~MMAN_spc;
print_word(roff_name[bef]);
outflags |= MMAN_nl;
}
/* Maybe we are inside an enclosing list? */
if (NULL != n->parent->next)
@ -1607,7 +1624,6 @@ pre_onearg(DECL_ARGS)
static int
pre_li(DECL_ARGS)
{
font_push('R');
return 1;
}
@ -1640,7 +1656,6 @@ pre_nm(DECL_ARGS)
static void
post_nm(DECL_ARGS)
{
switch (n->type) {
case ROFFT_BLOCK:
outflags &= ~MMAN_Bk;
@ -1658,15 +1673,23 @@ post_nm(DECL_ARGS)
static int
pre_no(DECL_ARGS)
{
outflags |= MMAN_spc_force;
return 1;
}
static void
pre_noarg(DECL_ARGS)
{
outflags |= MMAN_nl;
print_word(".");
outflags &= ~MMAN_spc;
print_word(roff_name[n->tok]);
outflags |= MMAN_nl;
}
static int
pre_ns(DECL_ARGS)
{
outflags &= ~MMAN_spc;
return 0;
}

View File

@ -1,6 +1,6 @@
/* $Id: mdoc_markdown.c,v 1.24 2018/04/11 17:11:13 schwarze Exp $ */
/* $Id: mdoc_markdown.c,v 1.30 2018/12/30 00:49:55 schwarze Exp $ */
/*
* Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -19,6 +19,7 @@
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc_aux.h"
@ -48,6 +49,7 @@ static void md_uri(const char *);
static int md_cond_head(struct roff_node *);
static int md_cond_body(struct roff_node *);
static int md_pre_abort(struct roff_node *);
static int md_pre_raw(struct roff_node *);
static int md_pre_word(struct roff_node *);
static int md_pre_skip(struct roff_node *);
@ -103,7 +105,7 @@ static void md_post_Pf(struct roff_node *);
static void md_post_Vt(struct roff_node *);
static void md_post__T(struct roff_node *);
static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = {
static const struct md_act md_acts[MDOC_MAX - MDOC_Dd] = {
{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
{ NULL, NULL, NULL, NULL, NULL }, /* Os */
@ -138,7 +140,7 @@ static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = {
{ md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
{ NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */
{ NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ot */
{ NULL, md_pre_abort, NULL, NULL, NULL }, /* Ot */
{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */
{ NULL, NULL, NULL, NULL, NULL }, /* Rv */
{ NULL, NULL, NULL, NULL, NULL }, /* St */
@ -211,7 +213,7 @@ static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = {
{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */
{ NULL, NULL, NULL, NULL, NULL }, /* Ud */
{ NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
{ NULL, md_pre_Pp, NULL, NULL, NULL }, /* Lp */
{ NULL, md_pre_abort, NULL, NULL, NULL }, /* Lp */
{ NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */
{ NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */
{ md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */
@ -225,7 +227,7 @@ static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = {
{ NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */
{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
};
static const struct md_act *const md_acts = __md_acts - MDOC_Dd;
static const struct md_act *md_act(enum roff_tok);
static int outflags;
#define MD_spc (1 << 0) /* Blank character before next word. */
@ -250,22 +252,30 @@ static int escflags; /* Escape in generated markdown code: */
static int code_blocks, quote_blocks, list_blocks;
static int outcount;
static const struct md_act *
md_act(enum roff_tok tok)
{
assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
return md_acts + (tok - MDOC_Dd);
}
void
markdown_mdoc(void *arg, const struct roff_man *mdoc)
markdown_mdoc(void *arg, const struct roff_meta *mdoc)
{
outflags = MD_Sm;
md_word(mdoc->meta.title);
if (mdoc->meta.msec != NULL) {
md_word(mdoc->title);
if (mdoc->msec != NULL) {
outflags &= ~MD_spc;
md_word("(");
md_word(mdoc->meta.msec);
md_word(mdoc->msec);
md_word(")");
}
md_word("-");
md_word(mdoc->meta.vol);
if (mdoc->meta.arch != NULL) {
md_word(mdoc->vol);
if (mdoc->arch != NULL) {
md_word("(");
md_word(mdoc->meta.arch);
md_word(mdoc->arch);
md_word(")");
}
outflags |= MD_sp;
@ -273,9 +283,9 @@ markdown_mdoc(void *arg, const struct roff_man *mdoc)
md_nodelist(mdoc->first->child);
outflags |= MD_sp;
md_word(mdoc->meta.os);
md_word(mdoc->os);
md_word("-");
md_word(mdoc->meta.date);
md_word(mdoc->date);
putchar('\n');
}
@ -330,8 +340,7 @@ md_node(struct roff_node *n)
break;
}
} else {
assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
act = md_acts + n->tok;
act = md_act(n->tok);
cond = act->cond == NULL || (*act->cond)(n);
if (cond && act->pre != NULL &&
(n->end == ENDBODY_NOT || n->child != NULL))
@ -580,6 +589,12 @@ md_word(const char *s)
case ESCAPE_SPECIAL:
uc = mchars_spec2cp(seq, sz);
break;
case ESCAPE_UNDEF:
uc = *seq;
break;
case ESCAPE_DEVICE:
md_rawword("markdown");
continue;
case ESCAPE_FONTBOLD:
nextfont = "**";
break;
@ -590,6 +605,7 @@ md_word(const char *s)
nextfont = "***";
break;
case ESCAPE_FONT:
case ESCAPE_FONTCW:
case ESCAPE_FONTROMAN:
nextfont = "";
break;
@ -711,12 +727,18 @@ md_cond_body(struct roff_node *n)
return n->type == ROFFT_BODY;
}
static int
md_pre_abort(struct roff_node *n)
{
abort();
}
static int
md_pre_raw(struct roff_node *n)
{
const char *prefix;
if ((prefix = md_acts[n->tok].prefix) != NULL) {
if ((prefix = md_act(n->tok)->prefix) != NULL) {
md_rawword(prefix);
outflags &= ~MD_spc;
if (*prefix == '`')
@ -730,7 +752,7 @@ md_post_raw(struct roff_node *n)
{
const char *suffix;
if ((suffix = md_acts[n->tok].suffix) != NULL) {
if ((suffix = md_act(n->tok)->suffix) != NULL) {
outflags &= ~(MD_spc | MD_nl);
md_rawword(suffix);
if (*suffix == '`')
@ -743,7 +765,7 @@ md_pre_word(struct roff_node *n)
{
const char *prefix;
if ((prefix = md_acts[n->tok].prefix) != NULL) {
if ((prefix = md_act(n->tok)->prefix) != NULL) {
md_word(prefix);
outflags &= ~MD_spc;
}
@ -755,7 +777,7 @@ md_post_word(struct roff_node *n)
{
const char *suffix;
if ((suffix = md_acts[n->tok].suffix) != NULL) {
if ((suffix = md_act(n->tok)->suffix) != NULL) {
outflags &= ~(MD_spc | MD_nl);
md_word(suffix);
}

View File

@ -1,4 +1,4 @@
/* $Id: mdoc_state.c,v 1.9 2017/11/29 20:05:33 schwarze Exp $ */
/* $Id: mdoc_state.c,v 1.15 2019/01/01 07:42:04 schwarze Exp $ */
/*
* Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
@ -17,6 +17,7 @@
#include <sys/types.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -24,19 +25,18 @@
#include "roff.h"
#include "mdoc.h"
#include "libmandoc.h"
#include "roff_int.h"
#include "libmdoc.h"
#define STATE_ARGS struct roff_man *mdoc, struct roff_node *n
typedef void (*state_handler)(STATE_ARGS);
static void state_bd(STATE_ARGS);
static void state_bl(STATE_ARGS);
static void state_dl(STATE_ARGS);
static void state_sh(STATE_ARGS);
static void state_sm(STATE_ARGS);
static const state_handler __state_handlers[MDOC_MAX - MDOC_Dd] = {
static const state_handler state_handlers[MDOC_MAX - MDOC_Dd] = {
NULL, /* Dd */
NULL, /* Dt */
NULL, /* Os */
@ -44,8 +44,8 @@ static const state_handler __state_handlers[MDOC_MAX - MDOC_Dd] = {
NULL, /* Ss */
NULL, /* Pp */
NULL, /* D1 */
state_dl, /* Dl */
state_bd, /* Bd */
NULL, /* Dl */
NULL, /* Bd */
NULL, /* Ed */
state_bl, /* Bl */
NULL, /* El */
@ -158,7 +158,6 @@ static const state_handler __state_handlers[MDOC_MAX - MDOC_Dd] = {
NULL, /* %U */
NULL, /* Ta */
};
static const state_handler *const state_handlers = __state_handlers - MDOC_Dd;
void
@ -170,41 +169,14 @@ mdoc_state(struct roff_man *mdoc, struct roff_node *n)
return;
assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
if ( ! (mdoc_macros[n->tok].flags & MDOC_PROLOGUE))
if ((mdoc_macro(n->tok)->flags & MDOC_PROLOGUE) == 0)
mdoc->flags |= MDOC_PBODY;
handler = state_handlers[n->tok];
handler = state_handlers[n->tok - MDOC_Dd];
if (*handler)
(*handler)(mdoc, n);
}
void
mdoc_state_reset(struct roff_man *mdoc)
{
roff_setreg(mdoc->roff, "nS", 0, '=');
mdoc->flags = 0;
}
static void
state_bd(STATE_ARGS)
{
enum mdocargt arg;
if (n->type != ROFFT_HEAD &&
(n->type != ROFFT_BODY || n->end != ENDBODY_NOT))
return;
if (n->parent->args == NULL)
return;
arg = n->parent->args->argv[0].arg;
if (arg != MDOC_Literal && arg != MDOC_Unfilled)
return;
state_dl(mdoc, n);
}
static void
state_bl(STATE_ARGS)
{
@ -229,22 +201,6 @@ state_bl(STATE_ARGS)
}
}
static void
state_dl(STATE_ARGS)
{
switch (n->type) {
case ROFFT_HEAD:
mdoc->flags |= MDOC_LITERAL;
break;
case ROFFT_BODY:
mdoc->flags &= ~MDOC_LITERAL;
break;
default:
break;
}
}
static void
state_sh(STATE_ARGS)
{

View File

@ -1,7 +1,7 @@
/* $Id: mdoc_term.c,v 1.367 2018/04/11 17:11:13 schwarze Exp $ */
/* $Id: mdoc_term.c,v 1.372 2019/01/04 03:39:01 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2012-2019 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
*
* Permission to use, copy, modify, and distribute this software for any
@ -29,7 +29,6 @@
#include <string.h>
#include "mandoc_aux.h"
#include "mandoc.h"
#include "roff.h"
#include "mdoc.h"
#include "out.h"
@ -47,7 +46,7 @@ struct termpair {
const struct roff_meta *meta, \
struct roff_node *n
struct termact {
struct mdoc_term_act {
int (*pre)(DECL_ARGS);
void (*post)(DECL_ARGS);
};
@ -84,6 +83,7 @@ static void termp_xx_post(DECL_ARGS);
static int termp__a_pre(DECL_ARGS);
static int termp__t_pre(DECL_ARGS);
static int termp_abort_pre(DECL_ARGS);
static int termp_an_pre(DECL_ARGS);
static int termp_ap_pre(DECL_ARGS);
static int termp_bd_pre(DECL_ARGS);
@ -124,7 +124,7 @@ static int termp_vt_pre(DECL_ARGS);
static int termp_xr_pre(DECL_ARGS);
static int termp_xx_pre(DECL_ARGS);
static const struct termact __termacts[MDOC_MAX - MDOC_Dd] = {
static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
{ NULL, NULL }, /* Dd */
{ NULL, NULL }, /* Dt */
{ NULL, NULL }, /* Os */
@ -159,7 +159,7 @@ static const struct termact __termacts[MDOC_MAX - MDOC_Dd] = {
{ termp_nd_pre, NULL }, /* Nd */
{ termp_nm_pre, termp_nm_post }, /* Nm */
{ termp_quote_pre, termp_quote_post }, /* Op */
{ termp_ft_pre, NULL }, /* Ot */
{ termp_abort_pre, NULL }, /* Ot */
{ termp_under_pre, NULL }, /* Pa */
{ termp_ex_pre, NULL }, /* Rv */
{ NULL, NULL }, /* St */
@ -232,7 +232,7 @@ static const struct termact __termacts[MDOC_MAX - MDOC_Dd] = {
{ termp_under_pre, NULL }, /* Fr */
{ NULL, NULL }, /* Ud */
{ NULL, termp_lb_post }, /* Lb */
{ termp_pp_pre, NULL }, /* Lp */
{ termp_abort_pre, NULL }, /* Lp */
{ termp_lk_pre, NULL }, /* Lk */
{ termp_under_pre, NULL }, /* Mt */
{ termp_quote_pre, termp_quote_post }, /* Brq */
@ -246,13 +246,12 @@ static const struct termact __termacts[MDOC_MAX - MDOC_Dd] = {
{ NULL, termp____post }, /* %U */
{ NULL, NULL }, /* Ta */
};
static const struct termact *const termacts = __termacts - MDOC_Dd;
static int fn_prio;
void
terminal_mdoc(void *arg, const struct roff_man *mdoc)
terminal_mdoc(void *arg, const struct roff_meta *mdoc)
{
struct roff_node *n;
struct termp *p;
@ -270,8 +269,7 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc)
if (n->tok == MDOC_Sh && n->sec == SEC_SYNOPSIS) {
if (n->child->next->child != NULL)
print_mdoc_nodelist(p, NULL,
&mdoc->meta,
n->child->next->child);
mdoc, n->child->next->child);
term_newln(p);
break;
}
@ -281,8 +279,7 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc)
save_defindent = p->defindent;
if (p->defindent == 0)
p->defindent = 5;
term_begin(p, print_mdoc_head, print_mdoc_foot,
&mdoc->meta);
term_begin(p, print_mdoc_head, print_mdoc_foot, mdoc);
while (n != NULL &&
(n->type == ROFFT_COMMENT ||
n->flags & NODE_NOPRT))
@ -290,7 +287,7 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc)
if (n != NULL) {
if (n->tok != MDOC_Sh)
term_vspace(p);
print_mdoc_nodelist(p, NULL, &mdoc->meta, n);
print_mdoc_nodelist(p, NULL, mdoc, n);
}
term_end(p);
p->defindent = save_defindent;
@ -310,9 +307,23 @@ print_mdoc_nodelist(DECL_ARGS)
static void
print_mdoc_node(DECL_ARGS)
{
int chld;
const struct mdoc_term_act *act;
struct termpair npair;
size_t offset, rmargin;
int chld;
/*
* In no-fill mode, break the output line at the beginning
* of new input lines except after \c, and nowhere else.
*/
if (n->flags & NODE_NOFILL) {
if (n->flags & NODE_LINE &&
(p->flags & TERMP_NONEWLINE) == 0)
term_newln(p);
p->flags |= TERMP_BRNEVER;
} else
p->flags &= ~TERMP_BRNEVER;
if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
return;
@ -343,9 +354,22 @@ print_mdoc_node(DECL_ARGS)
switch (n->type) {
case ROFFT_TEXT:
if (*n->string == ' ' && n->flags & NODE_LINE &&
(p->flags & TERMP_NONEWLINE) == 0)
term_newln(p);
if (n->flags & NODE_LINE) {
switch (*n->string) {
case '\0':
if (p->flags & TERMP_NONEWLINE)
term_newln(p);
else
term_vspace(p);
return;
case ' ':
if ((p->flags & TERMP_NONEWLINE) == 0)
term_newln(p);
break;
default:
break;
}
}
if (NODE_DELIMC & n->flags)
p->flags |= TERMP_NOSPACE;
term_word(p, n->string);
@ -370,10 +394,10 @@ print_mdoc_node(DECL_ARGS)
return;
}
assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
if (termacts[n->tok].pre != NULL &&
act = mdoc_term_acts + (n->tok - MDOC_Dd);
if (act->pre != NULL &&
(n->end == ENDBODY_NOT || n->child != NULL))
chld = (*termacts[n->tok].pre)
(p, &npair, meta, n);
chld = (*act->pre)(p, &npair, meta, n);
break;
}
@ -391,9 +415,9 @@ print_mdoc_node(DECL_ARGS)
case ROFFT_EQN:
break;
default:
if (termacts[n->tok].post == NULL || n->flags & NODE_ENDED)
if (act->post == NULL || n->flags & NODE_ENDED)
break;
(void)(*termacts[n->tok].post)(p, &npair, meta, n);
(void)(*act->post)(p, &npair, meta, n);
/*
* Explicit end tokens not only call the post
@ -1420,8 +1444,6 @@ termp_fa_pre(DECL_ARGS)
static int
termp_bd_pre(DECL_ARGS)
{
size_t lm, len;
struct roff_node *nn;
int offset;
if (n->type == ROFFT_BLOCK) {
@ -1447,66 +1469,19 @@ termp_bd_pre(DECL_ARGS)
p->tcol->offset += offset;
}
/*
* If -ragged or -filled are specified, the block does nothing
* but change the indentation. If -unfilled or -literal are
* specified, text is printed exactly as entered in the display:
* for macro lines, a newline is appended to the line. Blank
* lines are allowed.
*/
if (n->norm->Bd.type != DISP_literal &&
n->norm->Bd.type != DISP_unfilled &&
n->norm->Bd.type != DISP_centered)
return 1;
if (n->norm->Bd.type == DISP_literal) {
switch (n->norm->Bd.type) {
case DISP_literal:
term_tab_set(p, NULL);
term_tab_set(p, "T");
term_tab_set(p, "8n");
break;
case DISP_centered:
p->flags |= TERMP_CENTER;
break;
default:
break;
}
lm = p->tcol->offset;
p->flags |= TERMP_BRNEVER;
for (nn = n->child; nn != NULL; nn = nn->next) {
if (n->norm->Bd.type == DISP_centered) {
if (nn->type == ROFFT_TEXT) {
len = term_strlen(p, nn->string);
p->tcol->offset = len >= p->tcol->rmargin ?
0 : lm + len >= p->tcol->rmargin ?
p->tcol->rmargin - len :
(lm + p->tcol->rmargin - len) / 2;
} else
p->tcol->offset = lm;
}
print_mdoc_node(p, pair, meta, nn);
/*
* If the printed node flushes its own line, then we
* needn't do it here as well. This is hacky, but the
* notion of selective eoln whitespace is pretty dumb
* anyway, so don't sweat it.
*/
if (nn->tok < ROFF_MAX)
continue;
switch (nn->tok) {
case MDOC_Sm:
case MDOC_Bl:
case MDOC_D1:
case MDOC_Dl:
case MDOC_Lp:
case MDOC_Pp:
continue;
default:
break;
}
if (p->flags & TERMP_NONEWLINE ||
(nn->next && ! (nn->next->flags & NODE_LINE)))
continue;
term_flushln(p);
p->flags |= TERMP_NOSPACE;
}
p->flags &= ~TERMP_BRNEVER;
return 0;
return 1;
}
static void
@ -1514,12 +1489,14 @@ termp_bd_post(DECL_ARGS)
{
if (n->type != ROFFT_BODY)
return;
if (DISP_literal == n->norm->Bd.type ||
DISP_unfilled == n->norm->Bd.type)
if (n->norm->Bd.type == DISP_unfilled ||
n->norm->Bd.type == DISP_literal)
p->flags |= TERMP_BRNEVER;
p->flags |= TERMP_NOSPACE;
term_newln(p);
p->flags &= ~TERMP_BRNEVER;
if (n->norm->Bd.type == DISP_centered)
p->flags &= ~TERMP_CENTER;
}
static int
@ -2098,3 +2075,9 @@ termp_tag_pre(DECL_ARGS)
tag_put(n->child->string, 1, p->line);
return 1;
}
static int
termp_abort_pre(DECL_ARGS)
{
abort();
}

File diff suppressed because it is too large Load Diff

3
msec.c
View File

@ -1,4 +1,4 @@
/* $Id: msec.c,v 1.15 2015/10/06 18:32:19 schwarze Exp $ */
/* $Id: msec.c,v 1.16 2018/12/14 01:18:26 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -18,6 +18,7 @@
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"

334
out.c
View File

@ -1,7 +1,7 @@
/* $Id: out.c,v 1.70 2017/06/27 18:25:02 schwarze Exp $ */
/* $Id: out.c,v 1.77 2018/12/13 11:55:47 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,21 +20,29 @@
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc_aux.h"
#include "mandoc.h"
#include "tbl.h"
#include "out.h"
static void tblcalc_data(struct rofftbl *, struct roffcol *,
struct tbl_colgroup {
struct tbl_colgroup *next;
size_t wanted;
int startcol;
int endcol;
};
static size_t tblcalc_data(struct rofftbl *, struct roffcol *,
const struct tbl_opts *, const struct tbl_dat *,
size_t);
static void tblcalc_literal(struct rofftbl *, struct roffcol *,
static size_t tblcalc_literal(struct rofftbl *, struct roffcol *,
const struct tbl_dat *, size_t);
static void tblcalc_number(struct rofftbl *, struct roffcol *,
static size_t tblcalc_number(struct rofftbl *, struct roffcol *,
const struct tbl_opts *, const struct tbl_dat *);
@ -103,16 +111,18 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
* used for the actual width calculations.
*/
void
tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
size_t offset, size_t rmargin)
{
struct roffsu su;
const struct tbl_opts *opts;
const struct tbl_span *sp;
const struct tbl_dat *dp;
struct roffcol *col;
size_t ewidth, xwidth;
int spans;
int icol, maxcol, necol, nxcol, quirkcol;
struct tbl_colgroup *first_group, **gp, *g;
size_t *colwidth;
size_t ewidth, min1, min2, wanted, width, xwidth;
int done, icol, maxcol, necol, nxcol, quirkcol;
/*
* Allocate the master column specifiers. These will hold the
@ -120,33 +130,34 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
* must be freed and nullified by the caller.
*/
assert(NULL == tbl->cols);
tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
assert(tbl->cols == NULL);
tbl->cols = mandoc_calloc((size_t)sp_first->opts->cols,
sizeof(struct roffcol));
opts = sp->opts;
opts = sp_first->opts;
for (maxcol = -1; sp; sp = sp->next) {
if (TBL_SPAN_DATA != sp->pos)
maxcol = -1;
first_group = NULL;
for (sp = sp_first; sp != NULL; sp = sp->next) {
if (sp->pos != TBL_SPAN_DATA)
continue;
spans = 1;
/*
* Account for the data cells in the layout, matching it
* to data cells in the data section.
*/
for (dp = sp->first; dp; dp = dp->next) {
/* Do not used spanned cells in the calculation. */
if (0 < --spans)
continue;
spans = dp->spans;
if (1 < spans)
continue;
gp = &first_group;
for (dp = sp->first; dp != NULL; dp = dp->next) {
icol = dp->layout->col;
while (maxcol < icol)
while (icol > maxcol)
tbl->cols[++maxcol].spacing = SIZE_MAX;
col = tbl->cols + icol;
col->flags |= dp->layout->flags;
if (dp->layout->flags & TBL_CELL_WIGN)
continue;
/* Handle explicit width specifications. */
if (dp->layout->wstr != NULL &&
dp->layout->width == 0 &&
a2roffsu(dp->layout->wstr, &su, SCALE_EN)
@ -159,15 +170,165 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
(col->spacing == SIZE_MAX ||
col->spacing < dp->layout->spacing))
col->spacing = dp->layout->spacing;
tblcalc_data(tbl, col, opts, dp,
/*
* Calculate an automatic width.
* Except for spanning cells, apply it.
*/
width = tblcalc_data(tbl,
dp->hspans == 0 ? col : NULL,
opts, dp,
dp->block == 0 ? 0 :
dp->layout->width ? dp->layout->width :
rmargin ? (rmargin + sp->opts->cols / 2)
/ (sp->opts->cols + 1) : 0);
if (dp->hspans == 0)
continue;
/*
* Build an ordered, singly linked list
* of all groups of columns joined by spans,
* recording the minimum width for each group.
*/
while (*gp != NULL && ((*gp)->startcol < icol ||
(*gp)->endcol < icol + dp->hspans))
gp = &(*gp)->next;
if (*gp == NULL || (*gp)->startcol > icol ||
(*gp)->endcol > icol + dp->hspans) {
g = mandoc_malloc(sizeof(*g));
g->next = *gp;
g->wanted = width;
g->startcol = icol;
g->endcol = icol + dp->hspans;
*gp = g;
} else if ((*gp)->wanted < width)
(*gp)->wanted = width;
}
}
/*
* Column spacings are needed for span width calculations,
* so set the default values now.
*/
for (icol = 0; icol <= maxcol; icol++)
if (tbl->cols[icol].spacing == SIZE_MAX || icol == maxcol)
tbl->cols[icol].spacing = 3;
/*
* Replace the minimum widths with the missing widths,
* and dismiss groups that are already wide enough.
*/
gp = &first_group;
while ((g = *gp) != NULL) {
done = 0;
for (icol = g->startcol; icol <= g->endcol; icol++) {
width = tbl->cols[icol].width;
if (icol < g->endcol)
width += tbl->cols[icol].spacing;
if (g->wanted <= width) {
done = 1;
break;
} else
(*gp)->wanted -= width;
}
if (done) {
*gp = g->next;
free(g);
} else
gp = &(*gp)->next;
}
colwidth = mandoc_reallocarray(NULL, maxcol + 1, sizeof(*colwidth));
while (first_group != NULL) {
/*
* Rebuild the array of the widths of all columns
* participating in spans that require expansion.
*/
for (icol = 0; icol <= maxcol; icol++)
colwidth[icol] = SIZE_MAX;
for (g = first_group; g != NULL; g = g->next)
for (icol = g->startcol; icol <= g->endcol; icol++)
colwidth[icol] = tbl->cols[icol].width;
/*
* Find the smallest and second smallest column width
* among the columns which may need expamsion.
*/
min1 = min2 = SIZE_MAX;
for (icol = 0; icol <= maxcol; icol++) {
if (min1 > colwidth[icol]) {
min2 = min1;
min1 = colwidth[icol];
} else if (min1 < colwidth[icol] &&
min2 > colwidth[icol])
min2 = colwidth[icol];
}
/*
* Find the minimum wanted width
* for any one of the narrowest columns,
* and mark the columns wanting that width.
*/
wanted = min2;
for (g = first_group; g != NULL; g = g->next) {
necol = 0;
for (icol = g->startcol; icol <= g->endcol; icol++)
if (tbl->cols[icol].width == min1)
necol++;
if (necol == 0)
continue;
width = min1 + (g->wanted - 1) / necol + 1;
if (width > min2)
width = min2;
if (wanted > width)
wanted = width;
for (icol = g->startcol; icol <= g->endcol; icol++)
if (colwidth[icol] == min1 ||
(colwidth[icol] < min2 &&
colwidth[icol] > width))
colwidth[icol] = width;
}
/* Record the effect of the widening on the group list. */
gp = &first_group;
while ((g = *gp) != NULL) {
done = 0;
for (icol = g->startcol; icol <= g->endcol; icol++) {
if (colwidth[icol] != wanted ||
tbl->cols[icol].width == wanted)
continue;
if (g->wanted <= wanted - min1) {
done = 1;
break;
}
g->wanted -= wanted - min1;
}
if (done) {
*gp = g->next;
free(g);
} else
gp = &(*gp)->next;
}
/* Record the effect of the widening on the columns. */
for (icol = 0; icol <= maxcol; icol++)
if (colwidth[icol] == wanted)
tbl->cols[icol].width = wanted;
}
free(colwidth);
/*
* Align numbers with text.
* Count columns to equalize and columns to maximize.
* Find maximum width of the columns to equalize.
* Find total width of the columns *not* to maximize.
@ -177,8 +338,10 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
ewidth = xwidth = 0;
for (icol = 0; icol <= maxcol; icol++) {
col = tbl->cols + icol;
if (col->spacing == SIZE_MAX || icol == maxcol)
col->spacing = 3;
if (col->width > col->nwidth)
col->decimal += (col->width - col->nwidth) / 2;
else
col->width = col->nwidth;
if (col->flags & TBL_CELL_EQUAL) {
necol++;
if (ewidth < col->width)
@ -251,7 +414,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
}
}
static void
static size_t
tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
const struct tbl_opts *opts, const struct tbl_dat *dp, size_t mw)
{
@ -263,26 +426,24 @@ tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
case TBL_CELL_HORIZ:
case TBL_CELL_DHORIZ:
sz = (*tbl->len)(1, tbl->arg);
if (col->width < sz)
if (col != NULL && col->width < sz)
col->width = sz;
break;
return sz;
case TBL_CELL_LONG:
case TBL_CELL_CENTRE:
case TBL_CELL_LEFT:
case TBL_CELL_RIGHT:
tblcalc_literal(tbl, col, dp, mw);
break;
return tblcalc_literal(tbl, col, dp, mw);
case TBL_CELL_NUMBER:
tblcalc_number(tbl, col, opts, dp);
break;
return tblcalc_number(tbl, col, opts, dp);
case TBL_CELL_DOWN:
break;
return 0;
default:
abort();
}
}
static void
static size_t
tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
const struct tbl_dat *dp, size_t mw)
{
@ -291,11 +452,12 @@ tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
char *end; /* End of the current line. */
size_t lsz; /* Length of the current line. */
size_t wsz; /* Length of the current word. */
size_t msz; /* Length of the longest line. */
if (dp->string == NULL || *dp->string == '\0')
return;
return 0;
str = mw ? mandoc_strdup(dp->string) : dp->string;
lsz = 0;
msz = lsz = 0;
for (beg = str; beg != NULL && *beg != '\0'; beg = end) {
end = mw ? strchr(beg, ' ') : NULL;
if (end != NULL) {
@ -308,62 +470,84 @@ tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
lsz += 1 + wsz;
else
lsz = wsz;
if (col->width < lsz)
col->width = lsz;
if (msz < lsz)
msz = lsz;
}
if (mw)
free((void *)str);
if (col != NULL && col->width < msz)
col->width = msz;
return msz;
}
static void
static size_t
tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
const struct tbl_opts *opts, const struct tbl_dat *dp)
{
int i;
size_t sz, psz, ssz, d;
const char *str;
char *cp;
const char *cp, *lastdigit, *lastpoint;
size_t intsz, totsz;
char buf[2];
if (dp->string == NULL || *dp->string == '\0')
return 0;
totsz = (*tbl->slen)(dp->string, tbl->arg);
if (col == NULL)
return totsz;
/*
* First calculate number width and decimal place (last + 1 for
* non-decimal numbers). If the stored decimal is subsequent to
* ours, make our size longer by that difference
* (right-"shifting"); similarly, if ours is subsequent the
* stored, then extend the stored size by the difference.
* Finally, re-assign the stored values.
* Find the last digit and
* the last decimal point that is adjacent to a digit.
* The alignment indicator "\&" overrides everything.
*/
str = dp->string ? dp->string : "";
sz = (*tbl->slen)(str, tbl->arg);
lastdigit = lastpoint = NULL;
for (cp = dp->string; cp[0] != '\0'; cp++) {
if (cp[0] == '\\' && cp[1] == '&') {
lastdigit = lastpoint = cp;
break;
} else if (cp[0] == opts->decimal &&
(isdigit((unsigned char)cp[1]) ||
(cp > dp->string && isdigit((unsigned char)cp[-1]))))
lastpoint = cp;
else if (isdigit((unsigned char)cp[0]))
lastdigit = cp;
}
/* FIXME: TBL_DATA_HORIZ et al.? */
/* Not a number, treat as a literal string. */
buf[0] = opts->decimal;
if (lastdigit == NULL) {
if (col != NULL && col->width < totsz)
col->width = totsz;
return totsz;
}
/* Measure the width of the integer part. */
if (lastpoint == NULL)
lastpoint = lastdigit + 1;
intsz = 0;
buf[1] = '\0';
for (cp = dp->string; cp < lastpoint; cp++) {
buf[0] = cp[0];
intsz += (*tbl->slen)(buf, tbl->arg);
}
psz = (*tbl->slen)(buf, tbl->arg);
/*
* If this number has more integer digits than all numbers
* seen on earlier lines, shift them all to the right.
* If it has fewer, shift this number to the right.
*/
if (NULL != (cp = strrchr(str, opts->decimal))) {
buf[1] = '\0';
for (ssz = 0, i = 0; cp != &str[i]; i++) {
buf[0] = str[i];
ssz += (*tbl->slen)(buf, tbl->arg);
}
d = ssz + psz;
if (intsz > col->decimal) {
col->nwidth += intsz - col->decimal;
col->decimal = intsz;
} else
d = sz + psz;
totsz += col->decimal - intsz;
/* Adjust the settings for this column. */
/* Update the maximum total width seen so far. */
if (col->decimal > d) {
sz += col->decimal - d;
d = col->decimal;
} else
col->width += d - col->decimal;
if (sz > col->width)
col->width = sz;
if (d > col->decimal)
col->decimal = d;
if (totsz > col->nwidth)
col->nwidth = totsz;
return totsz;
}

5
out.h
View File

@ -1,7 +1,7 @@
/* $Id: out.h,v 1.32 2018/06/25 16:54:59 schwarze Exp $ */
/* $Id: out.h,v 1.33 2018/08/18 20:18:14 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -32,6 +32,7 @@ enum roffscale {
struct roffcol {
size_t width; /* width of cell */
size_t nwidth; /* max. width of number in cell */
size_t decimal; /* decimal position in cell */
size_t spacing; /* spacing after the column */
int flags; /* layout flags, see tbl_cell */

View File

@ -1,4 +1,4 @@
/* $Id: preconv.c,v 1.16 2017/02/18 13:43:52 schwarze Exp $ */
/* $Id: preconv.c,v 1.17 2018/12/13 11:55:47 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
@ -22,7 +22,10 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "roff.h"
#include "mandoc_parse.h"
#include "libmandoc.h"
int

742
read.c

File diff suppressed because it is too large Load Diff

607
roff.7
View File

@ -1,7 +1,7 @@
.\" $Id: roff.7,v 1.96 2018/04/10 00:52:30 schwarze Exp $
.\" $Id: roff.7,v 1.111 2019/01/01 03:45:29 schwarze Exp $
.\"
.\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -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: April 10 2018 $
.Dd $Mdocdate: January 1 2019 $
.Dt ROFF 7
.Os
.Sh NAME
@ -39,17 +39,15 @@ or
code.
To properly format such manuals, the
.Xr mandoc 1
utility supports a tiny subset of
utility supports a subset of
.Nm
requests and escapes.
Only these requests and escapes supported by
Even though this manual page lists all
.Nm
requests and escape sequences, it only contains partial information
about requests not supported by
.Xr mandoc 1
are documented in the present manual,
together with the basic language syntax shared by
.Nm ,
.Xr mdoc 7 ,
and
.Xr man 7 .
and about language features that do not matter for manual pages.
For complete
.Nm
manuals, consult the
@ -86,14 +84,10 @@ character, and, in certain circumstances, the tab character.
The backslash character
.Sq \e
indicates the start of an escape sequence, used for example for
.Sx Comments ,
.Sx Special Characters ,
.Sx Predefined Strings ,
.Sx Comments
and
user-defined strings defined using the
.Sx ds
request.
For a listing of escape sequences, consult the
.Sx Special Characters .
For a complete listing of escape sequences, consult the
.Sx ESCAPE SEQUENCE REFERENCE
below.
.Ss Comments
@ -139,20 +133,73 @@ One-letter backslash escape.
See
.Xr mandoc_char 7
for a complete list.
.Ss Text Decoration
Terms may be text-decorated using the
.Sq \ef
escape followed by an indicator: B (bold), I (italic), R (regular), or P
(revert to previous mode).
A numerical representation 3, 2, or 1 (bold, italic, and regular,
respectively) may be used instead.
The indicator or numerical representative may be preceded by C
(constant-width), which is ignored.
.Ss Font Selection
In
.Xr mdoc 7
and
.Xr man 7
documents, fonts are usually selected with macros.
The
.Ic \ef
escape sequence and the
.Ic \&ft
request can be used to manually change the font,
but this is not recommended in
.Xr mdoc 7
documents.
Such manual font changes are overridden by many subsequent macros.
.Pp
The two-character indicator
.Sq BI
requests a font that is both bold and italic.
It may not be portable to old roff implementations.
The following fonts are supported:
.Pp
.Bl -tag -width CW -offset indent -compact
.It Cm B
Bold font.
.It Cm BI
A font that is both bold and italic.
.It Cm CB
Bold constant width font.
Same as
.Cm B
in terminal output.
.It Cm CI
Italic constant width font.
Same as
.Cm I
in terminal output.
.It Cm CR
Regular constant width font.
Same as
.Cm R
in terminal output.
.It Cm CW
An alias for
.Cm CR .
.It Cm I
Italic font.
.It Cm P
Return to the previous font.
If a macro caused a font change since the last
.Ic \ef
eascape sequence or
.Ic \&ft
request, this returns to the font before the last font change in
the macro rather than to the font before the last manual font change.
.It Cm R
Roman font.
This is the default font.
.It Cm 1
An alias for
.Cm R .
.It Cm 2
An alias for
.Cm I .
.It Cm 3
An alias for
.Cm B .
.It Cm 4
An alias for
.Cm BI .
.El
.Pp
Examples:
.Bl -tag -width Ds -offset indent -compact
@ -163,40 +210,6 @@ Write in \fIitalic\fP, then return to previous font mode.
.It Li \ef(BIbold italic\efP
Write in \f(BIbold italic\fP, then return to previous font mode.
.El
.Pp
Text decoration is
.Em not
recommended for
.Xr mdoc 7 ,
which encourages semantic annotation.
.Ss Predefined Strings
Predefined strings, like
.Sx Special Characters ,
mark special output glyphs.
Predefined strings are escaped with the slash-asterisk,
.Sq \e* :
single-character
.Sq \e*X ,
two-character
.Sq \e*(XX ,
and N-character
.Sq \e* Ns Bq N .
.Pp
Examples:
.Bl -tag -width Ds -offset indent -compact
.It Li \e*(Am
Two-letter ampersand predefined string.
.It Li \e*q
One-letter double-quote predefined string.
.El
.Pp
Predefined strings are not recommended for use,
as they differ across implementations.
Those supported by
.Xr mandoc 1
are listed in
.Xr mandoc_char 7 .
Manuals using these predefined strings are almost certainly not portable.
.Ss Whitespace
Whitespace consists of the space character.
In text lines, whitespace is preserved within a line.
@ -206,7 +219,7 @@ Unescaped trailing spaces are stripped from text line input unless in a
literal context.
In general, trailing whitespace on any input line is discouraged for
reasons of portability.
In the rare case that a blank character is needed at the end of an
In the rare case that a space character is needed at the end of an
input line, it may be forced by
.Sq \e\ \e& .
.Pp
@ -225,7 +238,6 @@ Many requests and macros support scaled widths for their arguments.
The syntax for a scaled width is
.Sq Li [+-]?[0-9]*.[0-9]*[:unit:] ,
where a decimal must be preceded or followed by at least one digit.
Negative numbers, while accepted, are truncated to zero.
.Pp
The following scaling units are accepted:
.Pp
@ -235,9 +247,9 @@ centimetre
.It i
inch
.It P
pica (~1/6 inch)
pica (1/6 inch)
.It p
point (~1/72 inch)
point (1/72 inch)
.It f
scale
.Sq u
@ -257,7 +269,7 @@ character
.It u
default horizontal span for the terminal
.It M
mini-em (~1/100 em)
mini-em (1/100 em)
.El
.Pp
Using anything other than
@ -341,7 +353,7 @@ Macros are provided by the
and
.Xr man 7
languages and can be defined by the
.Sx \&de
.Ic \&de
request.
When called, they follow the same syntax as requests, except that
macro arguments may optionally be quoted by enclosing them
@ -447,7 +459,7 @@ compatibility mode at all, it handles this request as an alias for
.It Ic \&as Ar stringname Op Ar string
Append to a user-defined string.
The syntax of this request is the same as that of
.Sx \&ds .
.Ic \&ds .
If a user-defined string with the specified name does not yet exist,
it is set to the empty string before appending.
.It Ic \&as1 Ar stringname Op Ar string
@ -539,9 +551,16 @@ This is a groff extension and currently ignored.
.It Ic \&ch Ar macroname Op Ar dist
Change a trap location.
Currently ignored.
.It Ic \&char Ar glyphname Op Ar string
Define a new glyph.
Currently unsupported.
.It Ic \&char Ar glyph Op Ar string
Define or redefine the ASCII character or character escape sequence
.Ar glyph
to be rendered as
.Ar string ,
which can be empty.
Only partially supported in
.Xr mandoc 1 ;
may interact incorrectly with
.Ic \&tr .
.It Ic \&chop Ar stringname
Remove the last character from a macro, string, or diversion.
Currently unsupported.
@ -622,7 +641,7 @@ macros, whichever applies to the document in question.
.Pp
Specifying a custom
.Ar endmacro
macro works in the same way as for
works in the same way as for
.Ic \&ig ;
namely, the call to
.Sq Pf . Ar endmacro
@ -665,7 +684,9 @@ produces
.Pp
in the input stream, and thus in the output: \fI\^XtFree\^\fP.
Each occurrence of \e\e$* is replaced with all the arguments,
joined together with single blank characters.
joined together with single space characters.
The variant \e\e$@ is similar, except that each argument is
individually quoted.
.Pp
Since macros and user-defined strings share a common string table,
defining a macro
@ -887,11 +908,23 @@ This is a Heirloom extension and currently ignored.
Enable or disable an OpenType feature.
This is a Heirloom extension and currently ignored.
.It Ic \&fi
Switch to fill mode.
See
.Xr man 7 .
Ignored in
.Xr mdoc 7 .
Break the output line and switch to fill mode,
which is active by default but can be ended with the
.Ic \&nf
request.
In fill mode, input from subsequent input lines is added to
the same output line until the next word no longer fits,
at which point the output line is broken.
This request is implied by the
.Xr mdoc 7
.Ic \&Sh
macro and by the
.Xr man 7
.Ic \&SH ,
.Ic \&SS ,
and
.Ic \&EE
macros.
.It Ic \&fkern Ar font minkern
Control the use of kerning tables for a font.
This is a Heirloom extension and currently ignored.
@ -917,27 +950,12 @@ This is a Heirloom extension and currently ignored.
Conditionally define a special font.
This is a groff extension and currently ignored.
.It Ic \&ft Op Ar font
Change the font.
The following
Change the font; see
.Sx Font Selection .
The
.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 and may be overridden
by macros and escape sequences.
argument defaults to
.Cm P .
.It Ic \&ftr Ar newname Op Ar oldname
Translate font name.
This is a groff extension and currently ignored.
@ -1037,13 +1055,13 @@ is
or
.Sq o
.Pq odd page ,
it evaluates to true.
it evaluates to true, and the
.Ar body
starts with the next character.
.It
If the first character of
.Ar condition
is
.Sq c
.Pq character available ,
.Sq e
.Pq even page ,
.Sq t
@ -1051,7 +1069,20 @@ is
or
.Sq v
.Pq vroff mode ,
it evaluates to false.
it evaluates to false, and the
.Ar body
starts with the next character.
.It
If the first character of
.Ar condition
is
.Sq c
.Pq character available ,
it evaluates to true if the following character is an ASCII character
or a valid character escape sequence, or to false otherwise.
The
.Ar body
starts with the character following that next character.
.It
If the first character of
.Ar condition
@ -1219,7 +1250,7 @@ While evaluating the
the unit suffixes described below
.Sx Scaling Widths
are ignored.
.It Ic \&it Ar expression macro
.It Ic \&itc Ar expression macro
Set an input line trap, not counting lines ending with \ec.
Currently unsupported.
.It Ic \&IX Ar class keystring
@ -1328,11 +1359,22 @@ Declare the need for the specified minimum vertical space
before the next trap or the bottom of the page.
Currently ignored.
.It Ic \&nf
Switch to no-fill mode.
See
.Xr man 7 .
Ignored by
.Xr mdoc 7 .
Break the output line and switch to no-fill mode.
Subsequent input lines are kept together on the same output line
even when exceeding the right margin,
and line breaks in subsequent input cause output line breaks.
This request is implied by the
.Xr mdoc 7
.Ic \&Bd Fl unfilled
and
.Ic \&Bd Fl literal
macros and by the
.Xr man 7
.Ic \&EX
macro.
The
.Ic \&fi
request switches back to the default fill mode.
.It Ic \&nh
Turn off automatic hyphenation mode.
Currently ignored.
@ -1346,8 +1388,11 @@ Currently unsupported.
Temporarily turn off line numbering.
Currently unsupported.
.It Ic \&nop Ar body
Execute the rest of the input line as a request or macro line.
Currently unsupported.
Execute the rest of the input line as a request, macro, or text line,
skipping the
.Ic \&nop
request and any space characters immediately following it.
This is mostly used to indent text lines inside macro definitions.
.It Ic \&nr Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar expression Op Ar stepsize
Define or change a register.
A register is an arbitrary string value that defines some sort of state,
@ -1486,8 +1531,8 @@ Currently ignored.
Set the maximum stack depth for recursive macros.
This is a Heirloom extension and currently ignored.
.It Ic \&return Op Ar twice
Exit a macro and return to the caller.
Currently unsupported.
Exit the presently executed macro and return to the caller.
The argument is currently ignored.
.It Ic \&rfschar Ar font glyph ...
Remove font-specific fallback glyph definitions.
Currently unsupported.
@ -1536,8 +1581,11 @@ This is a Heirloom extension and currently ignored.
Change the soft hyphen character.
Currently ignored.
.It Ic \&shift Op Ar number
Shift macro arguments.
Currently unsupported.
Shift macro arguments
.Ar number
times, by default once: \e\e$i becomes what \e\e$i+number was.
Also decrement \en(.$ by
.Ar number .
.It Ic \&sizes Ar size ...
Define permissible point sizes.
This is a groff extension and currently ignored.
@ -1602,7 +1650,7 @@ Ignored because insecure.
Re-start a table layout, retaining the options of the prior table
invocation.
See
.Sx \&TS .
.Ic \&TS .
.It Ic \&ta Op Ar width ... Op Cm T Ar width ...
Set tab stops.
Each
@ -1623,7 +1671,7 @@ Currently unsupported.
.It Ic \&TE
End a table context.
See
.Sx \&TS .
.Ic \&TS .
.It Ic \&ti Oo Cm + Ns | Ns Cm - Oc Ns Ar width
Break the output line and indent the next output line by
.Ar width .
@ -1726,8 +1774,12 @@ This is a Heirloom extension and currently ignored.
Set a page location trap.
Currently unsupported.
.It Ic \&while Ar condition body
Repeated execution while a condition is true.
Currently unsupported.
Repeated execution while a
.Ar condition
is true, with syntax similar to
.Ic \&if .
Currently implemented with two restrictions: cannot nest,
and each loop must start and end in the same scope.
.It Ic \&write Oo \(dq Oc Ns Ar string
Write to an open file.
Ignored because insecure.
@ -1743,10 +1795,10 @@ This is a Heirloom extension and currently ignored.
.El
.Ss Numerical expressions
The
.Sx \&nr ,
.Sx \&if ,
.Ic \&nr ,
.Ic \&if ,
and
.Sx \&ie
.Ic \&ie
requests accept integer numerical expressions as arguments.
These are always evaluated using the C
.Vt int
@ -1821,10 +1873,6 @@ The
.Xr mandoc 1
.Nm
parser recognises the following escape sequences.
Note that the
.Nm
language defines more escape sequences not implemented in
.Xr mandoc 1 .
In
.Xr mdoc 7
and
@ -1836,228 +1884,315 @@ section above.
.Pp
A backslash followed by any character not listed here
simply prints that character itself.
.Ss \e<newline>
.Bl -tag -width Ds
.It Ic \e<newline>
A backslash at the end of an input line can be used to continue the
logical input line on the next physical input line, joining the text
on both lines together as if it were on a single input line.
.Ss \e<space>
.It Ic \e<space>
The escape sequence backslash-space
.Pq Sq \e\ \&
is an unpaddable space-sized non-breaking space character; see
.Sx Whitespace .
.Ss \e\(dq
.Sx Whitespace
and
.Xr mandoc_char 7 .
.It Ic \e!
Embed text up to and including the end of the input line into the
current diversion or into intermediate output without interpreting
requests, macros, and escapes.
Currently unsupported.
.It Ic \e\(dq
The rest of the input line is treated as
.Sx Comments .
.Ss \e%
.It Ic \e#
Line continuation with comment.
Discard the rest of the physical input line and continue the logical
input line on the next physical input line, joining the text on
both lines together as if it were on a single input line.
This is a groff extension.
.It Ic \e$ Ns Ar arg
Macro argument expansion, see
.Ic \&de .
.It Ic \e%
Hyphenation allowed at this point of the word; ignored by
.Xr mandoc 1 .
.Ss \e&
Non-printing zero-width character; see
.Sx Whitespace .
.Ss \e\(aq
.It Ic \e&
Non-printing zero-width character,
often used for various kinds of escaping; see
.Sx Whitespace ,
.Xr mandoc_char 7 ,
and the
.Dq MACRO SYNTAX
and
.Dq Delimiters
sections in
.Xr mdoc 7 .
.It Ic \e\(aq
Acute accent special character; use
.Sq \e(aa
.Ic \e(aa
instead.
.Ss \e( Ns Ar cc
.It Ic \e( Ns Ar cc
.Sx Special Characters
with two-letter names, see
.Xr mandoc_char 7 .
.Ss \e* Ns Bq Ar name
.It Ic \e)
Zero-width space transparent to end-of-sentence detection;
ignored by
.Xr mandoc 1 .
.It Ic \e*[ Ns Ar name Ns Ic \&]
Interpolate the string with the
.Ar name ;
see
.Sx Predefined Strings
and
.Sx ds .
.Ar name .
For short names, there are variants
.No \e* Ns Ar c
.Ic \e* Ns Ar c
and
.No \e*( Ns Ar cc .
.Ss \e,
.Ic \e*( Ns Ar cc .
.Pp
One string is predefined on the
.Nm
language level:
.Ic \e*(.T
expands to the name of the output device,
for example ascii, utf8, ps, pdf, html, or markdown.
.Pp
Macro sets traditionally predefine additional strings which are not
portable and differ across implementations.
Those supported by
.Xr mandoc 1
are listed in
.Xr mandoc_char 7 .
.Pp
Strings can be defined, changed, and deleted with the
.Ic \&ds ,
.Ic \&as ,
and
.Ic \&rm
requests.
.It Ic \e,
Left italic correction (groff extension); ignored by
.Xr mandoc 1 .
.Ss \e-
.It Ic \e-
Special character
.Dq mathematical minus sign .
.Ss \e/
.Dq mathematical minus sign ;
see
.Xr mandoc_char 7
for details.
.It Ic \e/
Right italic correction (groff extension); ignored by
.Xr mandoc 1 .
.Ss \e Ns Bq Ar name
.It Ic \e:
Breaking the line is allowed at this point of the word
without inserting a hyphen.
.It Ic \e?
Embed the text up to the next
.Ic \e?
into the current diversion without interpreting requests, macros,
and escapes.
This is a groff extension and currently unsupported.
.It Ic \e[ Ns Ar name Ns Ic \&]
.Sx Special Characters
with names of arbitrary length, see
.Xr mandoc_char 7 .
.Ss \e^
.It Ic \e^
One-twelfth em half-narrow space character, effectively zero-width in
.Xr mandoc 1 .
.Ss \e`
Grave accent special character; use
.Sq \e(ga
.It Ic \e_
Underline special character; use
.Ic \e(ul
instead.
.Ss \e{
.It Ic \e`
Grave accent special character; use
.Ic \e(ga
instead.
.It Ic \e{
Begin conditional input; see
.Sx if .
.Ss \e\(ba
.Ic \&if .
.It Ic \e\(ba
One-sixth em narrow space character, effectively zero-width in
.Xr mandoc 1 .
.Ss \e}
.It Ic \e}
End conditional input; see
.Sx if .
.Ss \e~
.Ic \&if .
.It Ic \e~
Paddable non-breaking space character.
.Ss \e0
.It Ic \e0
Digit width space character.
.Ss \eA\(aq Ns Ar string Ns \(aq
.It Ic \eA\(aq Ns Ar string Ns Ic \(aq
Anchor definition; ignored by
.Xr mandoc 1 .
.Ss \eB\(aq Ns Ar string Ns \(aq
.It Ic \ea
Leader character; ignored by
.Xr mandoc 1 .
.It Ic \eB\(aq Ns Ar string Ns Ic \(aq
Interpolate
.Sq 1
if
.Ar string
conforms to the syntax of
.Sx Numerical expressions
explained above and
explained above or
.Sq 0
otherwise.
.Ss \eb\(aq Ns Ar string Ns \(aq
.It Ic \eb\(aq Ns Ar string Ns Ic \(aq
Bracket building function; ignored by
.Xr mandoc 1 .
.Ss \eC\(aq Ns Ar name Ns \(aq
.It Ic \eC\(aq Ns Ar name Ns Ic \(aq
.Sx Special Characters
with names of arbitrary length.
.Ss \ec
.It Ic \ec
When encountered at the end of an input text line,
the next input text line is considered to continue that line,
even if there are request or macro lines in between.
No whitespace is inserted.
.Ss \eD\(aq Ns Ar string Ns \(aq
.It Ic \eD\(aq Ns Ar string Ns Ic \(aq
Draw graphics function; ignored by
.Xr mandoc 1 .
.Ss \ed
.It Ic \ed
Move down by half a line; ignored by
.Xr mandoc 1 .
.Ss \ee
.It Ic \eE
Escape character intended to not be interpreted in copy mode.
In
.Xr mandoc 1 ,
it currently does the same as
.Ic \e
itself.
.It Ic \ee
Backslash special character.
.Ss \eF Ns Bq Ar name
.It Ic \eF[ Ns Ar name Ns Ic \&]
Switch font family (groff extension); ignored by
.Xr mandoc 1 .
For short names, there are variants
.No \eF Ns Ar c
.Ic \eF Ns Ar c
and
.No \eF( Ns Ar cc .
.Ss \ef Ns Bq Ar name
.Ic \eF( Ns Ar cc .
.It Ic \ef[ Ns Ar name Ns Ic \&]
Switch to the font
.Ar name ,
see
.Sx Text Decoration .
.Sx Font Selection .
For short names, there are variants
.No \ef Ns Ar c
.Ic \ef Ns Ar c
and
.No \ef( Ns Ar cc .
.Ss \eg Ns Bq Ar name
.Ic \ef( Ns Ar cc .
An empty name
.Ic \ef[]
defaults to
.Ic \efP .
.It Ic \eg[ Ns Ar name Ns Ic \&]
Interpolate the format of a number register; ignored by
.Xr mandoc 1 .
For short names, there are variants
.No \eg Ns Ar c
.Ic \eg Ns Ar c
and
.No \eg( Ns Ar cc .
.Ss \eH\(aq Ns Oo +|- Oc Ns Ar number Ns \(aq
.Ic \eg( Ns Ar cc .
.It Ic \eH\(aq Ns Oo +|- Oc Ns Ar number Ns Ic \(aq
Set the height of the current font; ignored by
.Xr mandoc 1 .
.Ss \eh\(aq Ns Oo Cm \&| Oc Ns Ar width Ns \(aq
.It Ic \eh\(aq Ns Oo Cm \&| Oc Ns Ar width Ns Ic \(aq
Horizontal motion.
If the vertical bar is given, the motion is relative to the current
indentation.
Otherwise, it is relative to the current position.
The default scaling unit is
.Cm m .
.Ss \ek Ns Bq Ar name
.It Ic \ek[ Ns Ar name Ns Ic \&]
Mark horizontal input place in register; ignored by
.Xr mandoc 1 .
For short names, there are variants
.No \ek Ns Ar c
.Ic \ek Ns Ar c
and
.No \ek( Ns Ar cc .
.Ss \eL\(aq Ns Ar number Ns Oo Ar c Oc Ns \(aq
.Ic \ek( Ns Ar cc .
.It Ic \eL\(aq Ns Ar number Ns Oo Ar c Oc Ns Ic \(aq
Vertical line drawing function; ignored by
.Xr mandoc 1 .
.Ss \el\(aq Ns Ar width Ns Oo Ar c Oc Ns \(aq
.It Ic \el\(aq Ns Ar width Ns Oo Ar c Oc Ns Ic \(aq
Draw a horizontal line of
.Ar width
using the glyph
.Ar c .
.Ss \eM Ns Bq Ar name
.It Ic \eM[ Ns Ar name Ns Ic \&]
Set fill (background) color (groff extension); ignored by
.Xr mandoc 1 .
For short names, there are variants
.No \eM Ns Ar c
.Ic \eM Ns Ar c
and
.No \eM( Ns Ar cc .
.Ss \em Ns Bq Ar name
.Ic \eM( Ns Ar cc .
.It Ic \em[ Ns Ar name Ns Ic \&]
Set glyph drawing color (groff extension); ignored by
.Xr mandoc 1 .
For short names, there are variants
.No \em Ns Ar c
.Ic \em Ns Ar c
and
.No \em( Ns Ar cc .
.Ss \eN\(aq Ns Ar number Ns \(aq
.Ic \em( Ns Ar cc .
.It Ic \eN\(aq Ns Ar number Ns Ic \(aq
Character
.Ar number
on the current font.
.Ss \en Ns Oo +|- Oc Ns Bq Ar name
.It Ic \en Ns Oo +|- Oc Ns Ic \&[ Ns Ar name Ns Ic \&]
Interpolate the number register
.Ar name .
For short names, there are variants
.No \en Ns Ar c
.Ic \en Ns Ar c
and
.No \en( Ns Ar cc .
.Ic \en( Ns Ar cc .
If the optional sign is specified,
the register is first incremented or decremented by the
.Ar stepsize
that was specified in the relevant
.Ic \&nr
request, and the changed value is interpolated.
.Ss \eo\(aq Ns Ar string Ns \(aq
.It Ic \eO Ns Ar digit , Ic \eO[5 Ns arguments Ns Ic \&]
Suppress output.
This is a groff extension and currently unsupported.
With an argument of
.Ic 1 , 2 , 3 ,
or
.Ic 4 ,
it is ignored.
.It Ic \eo\(aq Ns Ar string Ns Ic \(aq
Overstrike, writing all the characters contained in the
.Ar string
to the same output position.
In terminal and HTML output modes,
only the last one of the characters is visible.
.Ss \ep
.It Ic \ep
Break the output line at the end of the current word.
.Ss \eR\(aq Ns Ar name Oo +|- Oc Ns Ar number Ns \(aq
.It Ic \eR\(aq Ns Ar name Oo +|- Oc Ns Ar number Ns Ic \(aq
Set number register; ignored by
.Xr mandoc 1 .
.Ss \eS\(aq Ns Ar number Ns \(aq
.It Ic \er
Move up by one line; ignored by
.Xr mandoc 1 .
.It Ic \eS\(aq Ns Ar number Ns Ic \(aq
Slant output; ignored by
.Xr mandoc 1 .
.Ss \es\(aq Ns Oo +|- Oc Ns Ar number Ns \(aq
.It Ic \es\(aq Ns Oo +|- Oc Ns Ar number Ns Ic \(aq
Change point size; ignored by
.Xr mandoc 1 .
Alternative forms
.No \es Ns Oo +|- Oc Ns Ar n ,
.No \es Ns Oo +|- Oc Ns \(aq Ns Ar number Ns \(aq ,
.No \es Ns Bq Oo +|- Oc Ns Ar number ,
.Ic \es Ns Oo +|- Oc Ns Ar n ,
.Ic \es Ns Oo +|- Oc Ns Ic \(aq Ns Ar number Ns Ic \(aq ,
.Ic \es[ Ns Oo +|- Oc Ns Ar number Ns Ic \&] ,
and
.No \es Ns Oo +|- Oc Ns Bq Ar number
.Ic \es Ns Oo +|- Oc Ns Ic \&[ Ns Ar number Ns Ic \&]
are also parsed and ignored.
.Ss \et
.It Ic \et
Horizontal tab; ignored by
.Xr mandoc 1 .
.Ss \eu
.It Ic \eu
Move up by half a line; ignored by
.Xr mandoc 1 .
.Ss \eV Ns Bq Ar name
.It Ic \eV[ Ns Ar name Ns Ic \&]
Interpolate an environment variable; ignored by
.Xr mandoc 1 .
For short names, there are variants
.No \eV Ns Ar c
.Ic \eV Ns Ar c
and
.No \eV( Ns Ar cc .
.Ss \ev\(aq Ns Ar number Ns \(aq
.Ic \eV( Ns Ar cc .
.It Ic \ev\(aq Ns Ar number Ns Ic \(aq
Vertical motion; ignored by
.Xr mandoc 1 .
.Ss \ew\(aq Ns Ar string Ns \(aq
.It Ic \ew\(aq Ns Ar string Ns Ic \(aq
Interpolate the width of the
.Ar string .
The
@ -2066,49 +2201,49 @@ 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
.It Ic \eX\(aq Ns Ar string Ns Ic \(aq
Output
.Ar string
as device control function; ignored in nroff mode and by
.Xr mandoc 1 .
.Ss \ex\(aq Ns Ar number Ns \(aq
.It Ic \ex\(aq Ns Ar number Ns Ic \(aq
Extra line space function; ignored by
.Xr mandoc 1 .
.Ss \eY Ns Bq Ar name
.It Ic \eY[ Ns Ar name Ns Ic \&]
Output a string as a device control function; ignored in nroff mode and by
.Xr mandoc 1 .
For short names, there are variants
.No \eY Ns Ar c
.Ic \eY Ns Ar c
and
.No \eY( Ns Ar cc .
.Ss \eZ\(aq Ns Ar string Ns \(aq
.Ic \eY( Ns Ar cc .
.It Ic \eZ\(aq Ns Ar string Ns Ic \(aq
Print
.Ar string
with zero width and height; ignored by
.Xr mandoc 1 .
.Ss \ez
.It Ic \ez
Output the next character without advancing the cursor position.
.El
.Sh COMPATIBILITY
The
.Xr mandoc 1
implementation of the
.Nm
language is intentionally incomplete.
Unimplemented features include:
language is incomplete.
Major unimplemented features include:
.Pp
.Bl -dash -compact
.It
For security reasons,
.Xr mandoc 1
never reads or writes external files except via
.Sx \&so
.Ic \&so
requests with safe relative paths.
.It
There is no automatic hyphenation, no adjustment to the right margin,
and no centering; the output is always set flush-left.
and very limited support for centering; the output is always set flush-left.
.It
Support for setting tabulator positions
and tabulator and leader characters is missing,
Support for setting tabulator and leader characters is missing,
and support for manually changing indentation is limited.
.It
The
@ -2119,14 +2254,14 @@ output media.
.It
Width measurements are implemented in a crude way
and often yield wrong results.
Explicit movement requests and escapes are ignored.
Support for explicit movement requests and escapes is limited.
.It
There is no concept of output pages, no support for floats,
graphics drawing, and picture inclusion;
terminal output is always continuous.
.It
Requests regarding color, font families, and glyph manipulation
are ignored.
Requests regarding color, font families, font sizes,
and glyph manipulation are ignored.
Font support is very limited.
Kerning is not implemented, and no ligatures are produced.
.It
@ -2134,12 +2269,12 @@ The
.Qq \(aq
macro control character does not suppress output line breaks.
.It
Diversions are not implemented,
Diversions and environments are not implemented,
and support for traps is very incomplete.
.It
While recursion is supported,
.Sx \&while
loops are not.
Use of macros is not supported inside
.Xr tbl 7
code.
.El
.Pp
The special semantics of the

1309
roff.c

File diff suppressed because it is too large Load Diff

86
roff.h
View File

@ -1,7 +1,7 @@
/* $Id: roff.h,v 1.59 2018/04/11 17:11:13 schwarze Exp $ */
/* $Id: roff.h,v 1.69 2019/03/04 13:01:57 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -14,11 +14,15 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Common data types for all syntax trees and related functions.
*/
struct ohash;
struct mdoc_arg;
union mdoc_data;
struct tbl_span;
struct eqn_box;
enum roff_macroset {
MACROSET_NONE = 0,
@ -69,9 +73,11 @@ enum roff_type {
enum roff_tok {
ROFF_br = 0,
ROFF_ce,
ROFF_fi,
ROFF_ft,
ROFF_ll,
ROFF_mc,
ROFF_nf,
ROFF_po,
ROFF_rj,
ROFF_sp,
@ -156,7 +162,6 @@ enum roff_tok {
ROFF_fcolor,
ROFF_fdeferlig,
ROFF_feature,
/* MAN_fi; ignored in mdoc(7) */
ROFF_fkern,
ROFF_fl,
ROFF_flig,
@ -216,7 +221,6 @@ enum roff_tok {
ROFF_mso,
ROFF_na,
ROFF_ne,
/* MAN_nf; ignored in mdoc(7) */
ROFF_nh,
ROFF_nhychar,
ROFF_nm,
@ -438,6 +442,7 @@ enum roff_tok {
MAN_SH,
MAN_SS,
MAN_TP,
MAN_TQ,
MAN_LP,
MAN_PP,
MAN_P,
@ -454,8 +459,6 @@ enum roff_tok {
MAN_I,
MAN_IR,
MAN_RI,
MAN_nf,
MAN_fi,
MAN_RE,
MAN_RS,
MAN_DT,
@ -463,6 +466,8 @@ enum roff_tok {
MAN_PD,
MAN_AT,
MAN_in,
MAN_SY,
MAN_YS,
MAN_OP,
MAN_EX,
MAN_EE,
@ -473,11 +478,6 @@ enum roff_tok {
MAN_MAX
};
enum roff_next {
ROFF_NEXT_SIBLING = 0,
ROFF_NEXT_CHILD
};
/*
* Indicates that a BODY's formatting has ended, but
* the scope is still open. Used for badly nested blocks.
@ -487,6 +487,12 @@ enum mdoc_endbody {
ENDBODY_SPACE /* Is broken: append a space. */
};
enum mandoc_os {
MANDOC_OS_OTHER = 0,
MANDOC_OS_NETBSD,
MANDOC_OS_OPENBSD
};
struct roff_node {
struct roff_node *parent; /* Parent AST node. */
struct roff_node *child; /* First child AST node. */
@ -499,21 +505,22 @@ struct roff_node {
struct mdoc_arg *args; /* BLOCK/ELEM */
union mdoc_data *norm; /* Normalized arguments. */
char *string; /* TEXT */
const struct tbl_span *span; /* TBL */
struct tbl_span *span; /* TBL */
struct eqn_box *eqn; /* EQN */
int line; /* Input file line number. */
int pos; /* Input file column number. */
int flags;
#define NODE_VALID (1 << 0) /* Has been validated. */
#define NODE_ENDED (1 << 1) /* Gone past body end mark. */
#define NODE_EOS (1 << 2) /* At sentence boundary. */
#define NODE_BROKEN (1 << 2) /* Must validate parent when ending. */
#define NODE_LINE (1 << 3) /* First macro/text on line. */
#define NODE_SYNPRETTY (1 << 4) /* SYNOPSIS-style formatting. */
#define NODE_BROKEN (1 << 5) /* Must validate parent when ending. */
#define NODE_DELIMO (1 << 6)
#define NODE_DELIMC (1 << 7)
#define NODE_NOSRC (1 << 8) /* Generated node, not in input file. */
#define NODE_NOPRT (1 << 9) /* Shall not print anything. */
#define NODE_DELIMO (1 << 4)
#define NODE_DELIMC (1 << 5)
#define NODE_EOS (1 << 6) /* At sentence boundary. */
#define NODE_SYNPRETTY (1 << 7) /* SYNOPSIS-style formatting. */
#define NODE_NOFILL (1 << 8) /* Fill mode switched off. */
#define NODE_NOSRC (1 << 9) /* Generated node, not in input file. */
#define NODE_NOPRT (1 << 10) /* Shall not print anything. */
int prev_font; /* Before entering this node. */
int aux; /* Decoded node data, type-dependent. */
enum roff_tok tok; /* Request or macro ID. */
@ -523,6 +530,7 @@ struct roff_node {
};
struct roff_meta {
struct roff_node *first; /* The first node parsed. */
char *msec; /* Manual section, usually a digit. */
char *vol; /* Manual volume title. */
char *os; /* Operating system. */
@ -530,51 +538,15 @@ struct roff_meta {
char *title; /* Manual title, usually CAPS. */
char *name; /* Leading manual name. */
char *date; /* Normalized date. */
char *sodest; /* .so target file name or NULL. */
int hasbody; /* Document is not empty. */
int rcsids; /* Bits indexed by enum mandoc_os. */
enum mandoc_os os_e; /* Operating system. */
};
struct roff_man {
struct roff_meta meta; /* Document meta-data. */
struct mparse *parse; /* Parse pointer. */
struct roff *roff; /* Roff parser state data. */
struct ohash *mdocmac; /* Mdoc macro lookup table. */
struct ohash *manmac; /* Man macro lookup table. */
const char *os_s; /* Default operating system. */
struct roff_node *first; /* The first node parsed. */
struct roff_node *last; /* The last node parsed. */
struct roff_node *last_es; /* The most recent Es node. */
int quick; /* Abort parse early. */
int flags; /* Parse flags. */
#define MDOC_LITERAL (1 << 1) /* In a literal scope. */
#define MDOC_PBODY (1 << 2) /* In the document body. */
#define MDOC_NEWLINE (1 << 3) /* First macro/text in a line. */
#define MDOC_PHRASE (1 << 4) /* In a Bl -column phrase. */
#define MDOC_PHRASELIT (1 << 5) /* Literal within a phrase. */
#define MDOC_FREECOL (1 << 6) /* `It' invocation should close. */
#define MDOC_SYNOPSIS (1 << 7) /* SYNOPSIS-style formatting. */
#define MDOC_KEEP (1 << 8) /* In a word keep. */
#define MDOC_SMOFF (1 << 9) /* Spacing is off. */
#define MDOC_NODELIMC (1 << 10) /* Disable closing delimiter handling. */
#define MAN_ELINE (1 << 11) /* Next-line element scope. */
#define MAN_BLINE (1 << 12) /* Next-line block scope. */
#define MDOC_PHRASEQF (1 << 13) /* Quote first word encountered. */
#define MDOC_PHRASEQL (1 << 14) /* Quote last word of this phrase. */
#define MDOC_PHRASEQN (1 << 15) /* Quote first word of the next phrase. */
#define MAN_LITERAL MDOC_LITERAL
#define MAN_NEWLINE MDOC_NEWLINE
enum roff_macroset macroset; /* Kind of high-level macros used. */
enum roff_sec lastsec; /* Last section seen. */
enum roff_sec lastnamed; /* Last standard section seen. */
enum roff_next next; /* Where to put the next node. */
};
extern const char *const *roff_name;
int arch_valid(const char *, enum mandoc_os);
void deroff(char **, const struct roff_node *);
struct ohash *roffhash_alloc(enum roff_tok, enum roff_tok);
enum roff_tok roffhash_find(struct ohash *, const char *, size_t);
void roffhash_free(struct ohash *);
void roff_validate(struct roff_man *);

View File

@ -1,7 +1,7 @@
/* $Id: roff_html.c,v 1.12 2018/06/25 14:53:58 schwarze Exp $ */
/* $Id: roff_html.c,v 1.19 2019/01/07 07:26:29 schwarze Exp $ */
/*
* Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2014, 2017, 2018, 2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -18,7 +18,8 @@
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "roff.h"
@ -31,14 +32,19 @@ typedef void (*roff_html_pre_fp)(ROFF_HTML_ARGS);
static void roff_html_pre_br(ROFF_HTML_ARGS);
static void roff_html_pre_ce(ROFF_HTML_ARGS);
static void roff_html_pre_fi(ROFF_HTML_ARGS);
static void roff_html_pre_ft(ROFF_HTML_ARGS);
static void roff_html_pre_nf(ROFF_HTML_ARGS);
static void roff_html_pre_sp(ROFF_HTML_ARGS);
static const roff_html_pre_fp roff_html_pre_acts[ROFF_MAX] = {
roff_html_pre_br, /* br */
roff_html_pre_ce, /* ce */
NULL, /* ft */
roff_html_pre_fi, /* fi */
roff_html_pre_ft, /* ft */
NULL, /* ll */
NULL, /* mc */
roff_html_pre_nf, /* nf */
NULL, /* po */
roff_html_pre_ce, /* rj */
roff_html_pre_sp, /* sp */
@ -58,11 +64,7 @@ roff_html_pre(struct html *h, const struct roff_node *n)
static void
roff_html_pre_br(ROFF_HTML_ARGS)
{
struct tag *t;
t = print_otag(h, TAG_DIV, "");
print_text(h, "\\~"); /* So the div isn't empty. */
print_tagq(h, t);
print_otag(h, TAG_BR, "");
}
static void
@ -79,8 +81,37 @@ roff_html_pre_ce(ROFF_HTML_ARGS)
roff_html_pre_br(h, n);
}
static void
roff_html_pre_fi(ROFF_HTML_ARGS)
{
if (html_fillmode(h, TOKEN_NONE) == ROFF_fi)
print_otag(h, TAG_BR, "");
}
static void
roff_html_pre_ft(ROFF_HTML_ARGS)
{
const char *cp;
cp = n->child->string;
print_metaf(h, mandoc_font(cp, (int)strlen(cp)));
}
static void
roff_html_pre_nf(ROFF_HTML_ARGS)
{
if (html_fillmode(h, TOKEN_NONE) == ROFF_nf)
print_otag(h, TAG_BR, "");
}
static void
roff_html_pre_sp(ROFF_HTML_ARGS)
{
print_paragraph(h);
if (html_fillmode(h, TOKEN_NONE) == ROFF_nf) {
h->col++;
print_endline(h);
} else {
html_close_paragraph(h);
print_otag(h, TAG_P, "c", "Pp");
}
}

View File

@ -1,7 +1,7 @@
/* $Id: roff_int.h,v 1.9 2017/07/08 17:52:50 schwarze Exp $ */
/* $Id: roff_int.h,v 1.16 2019/01/05 00:36:50 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -14,8 +14,54 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Parser internals shared by multiple parsers.
*/
struct ohash;
struct roff_node;
struct roff_meta;
struct roff;
struct mdoc_arg;
enum roff_next {
ROFF_NEXT_SIBLING = 0,
ROFF_NEXT_CHILD
};
struct roff_man {
struct roff_meta meta; /* Public parse results. */
struct roff *roff; /* Roff parser state data. */
struct ohash *mdocmac; /* Mdoc macro lookup table. */
struct ohash *manmac; /* Man macro lookup table. */
const char *os_s; /* Default operating system. */
struct roff_node *last; /* The last node parsed. */
struct roff_node *last_es; /* The most recent Es node. */
int quick; /* Abort parse early. */
int flags; /* Parse flags. */
#define ROFF_NOFILL (1 << 1) /* Fill mode switched off. */
#define MDOC_PBODY (1 << 2) /* In the document body. */
#define MDOC_NEWLINE (1 << 3) /* First macro/text in a line. */
#define MDOC_PHRASE (1 << 4) /* In a Bl -column phrase. */
#define MDOC_PHRASELIT (1 << 5) /* Literal within a phrase. */
#define MDOC_FREECOL (1 << 6) /* `It' invocation should close. */
#define MDOC_SYNOPSIS (1 << 7) /* SYNOPSIS-style formatting. */
#define MDOC_KEEP (1 << 8) /* In a word keep. */
#define MDOC_SMOFF (1 << 9) /* Spacing is off. */
#define MDOC_NODELIMC (1 << 10) /* Disable closing delimiter handling. */
#define MAN_ELINE (1 << 11) /* Next-line element scope. */
#define MAN_BLINE (1 << 12) /* Next-line block scope. */
#define MDOC_PHRASEQF (1 << 13) /* Quote first word encountered. */
#define MDOC_PHRASEQL (1 << 14) /* Quote last word of this phrase. */
#define MDOC_PHRASEQN (1 << 15) /* Quote first word of the next phrase. */
#define ROFF_NONOFILL (1 << 16) /* Temporarily suspend no-fill mode. */
#define MAN_NEWLINE MDOC_NEWLINE
enum roff_sec lastsec; /* Last section seen. */
enum roff_sec lastnamed; /* Last standard section seen. */
enum roff_next next; /* Where to put the next node. */
};
struct roff_node *roff_node_alloc(struct roff_man *, int, int,
enum roff_type, int);
void roff_node_append(struct roff_man *, struct roff_node *);
@ -26,9 +72,17 @@ struct roff_node *roff_block_alloc(struct roff_man *, int, int, int);
struct roff_node *roff_head_alloc(struct roff_man *, int, int, int);
struct roff_node *roff_body_alloc(struct roff_man *, int, int, int);
void roff_node_unlink(struct roff_man *, struct roff_node *);
void roff_node_relink(struct roff_man *, struct roff_node *);
void roff_node_free(struct roff_node *);
void roff_node_delete(struct roff_man *, struct roff_node *);
struct ohash *roffhash_alloc(enum roff_tok, enum roff_tok);
enum roff_tok roffhash_find(struct ohash *, const char *, size_t);
void roffhash_free(struct ohash *);
void roff_state_reset(struct roff_man *);
void roff_validate(struct roff_man *);
/*
* Functions called from roff.c need to be declared here,
* not in libmdoc.h or libman.h, even if they are specific

View File

@ -1,6 +1,6 @@
/* $Id: roff_term.c,v 1.14 2017/06/24 14:38:33 schwarze Exp $ */
/* $Id: roff_term.c,v 1.19 2019/01/04 03:24:33 schwarze Exp $ */
/*
* Copyright (c) 2010, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -17,7 +17,8 @@
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "roff.h"
@ -41,9 +42,11 @@ static void roff_term_pre_ti(ROFF_TERM_ARGS);
static const roff_term_pre_fp roff_term_pre_acts[ROFF_MAX] = {
roff_term_pre_br, /* br */
roff_term_pre_ce, /* ce */
roff_term_pre_br, /* fi */
roff_term_pre_ft, /* ft */
roff_term_pre_ll, /* ll */
roff_term_pre_mc, /* mc */
roff_term_pre_br, /* nf */
roff_term_pre_po, /* po */
roff_term_pre_ce, /* rj */
roff_term_pre_sp, /* sp */
@ -66,7 +69,9 @@ roff_term_pre_br(ROFF_TERM_ARGS)
if (p->flags & TERMP_BRIND) {
p->tcol->offset = p->tcol->rmargin;
p->tcol->rmargin = p->maxrmargin;
p->trailspace = 0;
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
p->flags |= TERMP_NOSPACE;
}
}
@ -74,27 +79,16 @@ static void
roff_term_pre_ce(ROFF_TERM_ARGS)
{
const struct roff_node *nc1, *nc2;
size_t len, lm;
roff_term_pre_br(p, n);
lm = p->tcol->offset;
p->flags |= n->tok == ROFF_ce ? TERMP_CENTER : TERMP_RIGHT;
nc1 = n->child->next;
while (nc1 != NULL) {
nc2 = nc1;
len = 0;
do {
if (nc2->type == ROFFT_TEXT) {
if (len)
len++;
len += term_strlen(p, nc2->string);
}
nc2 = nc2->next;
} while (nc2 != NULL && (nc2->type != ROFFT_TEXT ||
(nc2->flags & NODE_LINE) == 0));
p->tcol->offset = len >= p->tcol->rmargin ? 0 :
lm + len >= p->tcol->rmargin ? p->tcol->rmargin - len :
n->tok == ROFF_rj ? p->tcol->rmargin - len :
(lm + p->tcol->rmargin - len) / 2;
while (nc1 != nc2) {
if (nc1->type == ROFFT_TEXT)
term_word(p, nc1->string);
@ -105,28 +99,30 @@ roff_term_pre_ce(ROFF_TERM_ARGS)
p->flags |= TERMP_NOSPACE;
term_flushln(p);
}
p->tcol->offset = lm;
p->flags &= ~(TERMP_CENTER | TERMP_RIGHT);
}
static void
roff_term_pre_ft(ROFF_TERM_ARGS)
{
switch (*n->child->string) {
case '4':
case '3':
case 'B':
const char *cp;
cp = n->child->string;
switch (mandoc_font(cp, (int)strlen(cp))) {
case ESCAPE_FONTBOLD:
term_fontrepl(p, TERMFONT_BOLD);
break;
case '2':
case 'I':
case ESCAPE_FONTITALIC:
term_fontrepl(p, TERMFONT_UNDER);
break;
case 'P':
case ESCAPE_FONTBI:
term_fontrepl(p, TERMFONT_BI);
break;
case ESCAPE_FONTPREV:
term_fontlast(p);
break;
case '1':
case 'C':
case 'R':
case ESCAPE_FONTROMAN:
case ESCAPE_FONTCW:
term_fontrepl(p, TERMFONT_NONE);
break;
default:

View File

@ -1,6 +1,6 @@
/* $Id: roff_validate.c,v 1.9 2017/06/14 22:51:25 schwarze Exp $ */
/* $Id: roff_validate.c,v 1.18 2018/12/31 09:02:37 schwarze Exp $ */
/*
* Copyright (c) 2010, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -17,7 +17,8 @@
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "roff.h"
@ -28,17 +29,23 @@
typedef void (*roff_valid_fp)(ROFF_VALID_ARGS);
static void roff_valid_br(ROFF_VALID_ARGS);
static void roff_valid_fi(ROFF_VALID_ARGS);
static void roff_valid_ft(ROFF_VALID_ARGS);
static void roff_valid_nf(ROFF_VALID_ARGS);
static void roff_valid_sp(ROFF_VALID_ARGS);
static const roff_valid_fp roff_valids[ROFF_MAX] = {
NULL, /* br */
roff_valid_br, /* br */
NULL, /* ce */
roff_valid_fi, /* fi */
roff_valid_ft, /* ft */
NULL, /* ll */
NULL, /* mc */
roff_valid_nf, /* nf */
NULL, /* po */
NULL, /* rj */
NULL, /* sp */
roff_valid_sp, /* sp */
NULL, /* ta */
NULL, /* ti */
};
@ -55,10 +62,46 @@ roff_validate(struct roff_man *man)
(*roff_valids[n->tok])(man, n);
}
static void
roff_valid_br(ROFF_VALID_ARGS)
{
struct roff_node *np;
if (n->next != NULL && n->next->type == ROFFT_TEXT &&
*n->next->string == ' ') {
mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos,
"br before text line with leading blank");
roff_node_delete(man, n);
return;
}
if ((np = n->prev) == NULL)
return;
switch (np->tok) {
case ROFF_br:
case ROFF_sp:
case MDOC_Pp:
mandoc_msg(MANDOCERR_PAR_SKIP,
n->line, n->pos, "br after %s", roff_name[np->tok]);
roff_node_delete(man, n);
break;
default:
break;
}
}
static void
roff_valid_fi(ROFF_VALID_ARGS)
{
if ((n->flags & NODE_NOFILL) == 0)
mandoc_msg(MANDOCERR_FI_SKIP, n->line, n->pos, "fi");
}
static void
roff_valid_ft(ROFF_VALID_ARGS)
{
char *cp;
const char *cp;
if (n->child == NULL) {
man->next = ROFF_NEXT_CHILD;
@ -68,30 +111,39 @@ roff_valid_ft(ROFF_VALID_ARGS)
}
cp = n->child->string;
switch (*cp) {
case '1':
case '2':
case '3':
case '4':
case 'I':
case 'P':
case 'R':
if (cp[1] == '\0')
return;
if (mandoc_font(cp, (int)strlen(cp)) != ESCAPE_ERROR)
return;
mandoc_msg(MANDOCERR_FT_BAD, n->line, n->pos, "ft %s", cp);
roff_node_delete(man, n);
}
static void
roff_valid_nf(ROFF_VALID_ARGS)
{
if (n->flags & NODE_NOFILL)
mandoc_msg(MANDOCERR_NF_SKIP, n->line, n->pos, "nf");
}
static void
roff_valid_sp(ROFF_VALID_ARGS)
{
struct roff_node *np;
if ((np = n->prev) == NULL)
return;
switch (np->tok) {
case ROFF_br:
mandoc_msg(MANDOCERR_PAR_SKIP,
np->line, np->pos, "br before sp");
roff_node_delete(man, np);
break;
case 'B':
if (cp[1] == '\0' || (cp[1] == 'I' && cp[2] == '\0'))
return;
break;
case 'C':
if (cp[1] == 'W' && cp[2] == '\0')
return;
case MDOC_Pp:
mandoc_msg(MANDOCERR_PAR_SKIP,
n->line, n->pos, "sp after Pp");
roff_node_delete(man, n);
break;
default:
break;
}
mandoc_vmsg(MANDOCERR_FT_BAD, man->parse,
n->line, n->pos, "ft %s", cp);
roff_node_delete(man, n);
}

54
st.c
View File

@ -1,6 +1,6 @@
/* $Id: st.c,v 1.14 2017/06/24 14:38:33 schwarze Exp $ */
/* $Id: st.c,v 1.16 2018/12/14 01:18:26 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -18,11 +18,11 @@
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "roff.h"
#include "mdoc.h"
#include "libmdoc.h"
#define LINE(x, y) \
@ -31,8 +31,52 @@
const char *
mdoc_a2st(const char *p)
{
#include "st.in"
LINE("-p1003.1-88", "IEEE Std 1003.1-1988 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-90", "IEEE Std 1003.1-1990 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2001", "IEEE Std 1003.1-2001 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2004", "IEEE Std 1003.1-2004 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2008", "IEEE Std 1003.1-2008 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1", "IEEE Std 1003.1 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1b", "IEEE Std 1003.1b (\\(lqPOSIX.1b\\(rq)")
LINE("-p1003.1b-93", "IEEE Std 1003.1b-1993 (\\(lqPOSIX.1b\\(rq)")
LINE("-p1003.1c-95", "IEEE Std 1003.1c-1995 (\\(lqPOSIX.1c\\(rq)")
LINE("-p1003.1g-2000", "IEEE Std 1003.1g-2000 (\\(lqPOSIX.1g\\(rq)")
LINE("-p1003.1i-95", "IEEE Std 1003.1i-1995 (\\(lqPOSIX.1i\\(rq)")
LINE("-p1003.2", "IEEE Std 1003.2 (\\(lqPOSIX.2\\(rq)")
LINE("-p1003.2-92", "IEEE Std 1003.2-1992 (\\(lqPOSIX.2\\(rq)")
LINE("-p1003.2a-92", "IEEE Std 1003.2a-1992 (\\(lqPOSIX.2\\(rq)")
LINE("-isoC", "ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)")
LINE("-isoC-90", "ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)")
LINE("-isoC-amd1", "ISO/IEC 9899/AMD1:1995 (\\(lqISO\\~C90, Amendment 1\\(rq)")
LINE("-isoC-tcor1", "ISO/IEC 9899/TCOR1:1994 (\\(lqISO\\~C90, Technical Corrigendum 1\\(rq)")
LINE("-isoC-tcor2", "ISO/IEC 9899/TCOR2:1995 (\\(lqISO\\~C90, Technical Corrigendum 2\\(rq)")
LINE("-isoC-99", "ISO/IEC 9899:1999 (\\(lqISO\\~C99\\(rq)")
LINE("-isoC-2011", "ISO/IEC 9899:2011 (\\(lqISO\\~C11\\(rq)")
LINE("-iso9945-1-90", "ISO/IEC 9945-1:1990 (\\(lqPOSIX.1\\(rq)")
LINE("-iso9945-1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
LINE("-iso9945-2-93", "ISO/IEC 9945-2:1993 (\\(lqPOSIX.2\\(rq)")
LINE("-ansiC", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)")
LINE("-ansiC-89", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)")
LINE("-ieee754", "IEEE Std 754-1985")
LINE("-iso8802-3", "ISO 8802-3: 1989")
LINE("-iso8601", "ISO 8601")
LINE("-ieee1275-94", "IEEE Std 1275-1994 (\\(lqOpen Firmware\\(rq)")
LINE("-xpg3", "X/Open Portability Guide Issue\\~3 (\\(lqXPG3\\(rq)")
LINE("-xpg4", "X/Open Portability Guide Issue\\~4 (\\(lqXPG4\\(rq)")
LINE("-xpg4.2", "X/Open Portability Guide Issue\\~4, Version\\~2 (\\(lqXPG4.2\\(rq)")
LINE("-xbd5", "X/Open Base Definitions Issue\\~5 (\\(lqXBD5\\(rq)")
LINE("-xcu5", "X/Open Commands and Utilities Issue\\~5 (\\(lqXCU5\\(rq)")
LINE("-xsh4.2", "X/Open System Interfaces and Headers Issue\\~4, Version\\~2 (\\(lqXSH4.2\\(rq)")
LINE("-xsh5", "X/Open System Interfaces and Headers Issue\\~5 (\\(lqXSH5\\(rq)")
LINE("-xns5", "X/Open Networking Services Issue\\~5 (\\(lqXNS5\\(rq)")
LINE("-xns5.2", "X/Open Networking Services Issue\\~5.2 (\\(lqXNS5.2\\(rq)")
LINE("-xcurses4.2", "X/Open Curses Issue\\~4, Version\\~2 (\\(lqXCURSES4.2\\(rq)")
LINE("-susv1", "Version\\~1 of the Single UNIX Specification (\\(lqSUSv1\\(rq)")
LINE("-susv2", "Version\\~2 of the Single UNIX Specification (\\(lqSUSv2\\(rq)")
LINE("-susv3", "Version\\~3 of the Single UNIX Specification (\\(lqSUSv3\\(rq)")
LINE("-susv4", "Version\\~4 of the Single UNIX Specification (\\(lqSUSv4\\(rq)")
LINE("-svid4", "System\\~V Interface Definition, Fourth Edition (\\(lqSVID4\\(rq)")
return NULL;
}

76
st.in
View File

@ -1,76 +0,0 @@
/* $Id: st.in,v 1.30 2018/04/05 09:17:26 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file defines the .St macro arguments. If you add a new
* standard, make sure that the left-and side corresponds to the .St
* argument (like .St -p1003.1) and the right-hand side corresponds to
* the formatted output string.
*
* Be sure to escape strings.
* The non-breaking blanks prevent ending an output line right before
* a number. Groff prevent line breaks at the same places.
*
* REMEMBER TO ADD NEW STANDARDS TO MDOC.7!
*/
LINE("-p1003.1-88", "IEEE Std 1003.1-1988 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-90", "IEEE Std 1003.1-1990 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2001", "IEEE Std 1003.1-2001 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2004", "IEEE Std 1003.1-2004 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2008", "IEEE Std 1003.1-2008 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1", "IEEE Std 1003.1 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1b", "IEEE Std 1003.1b (\\(lqPOSIX.1b\\(rq)")
LINE("-p1003.1b-93", "IEEE Std 1003.1b-1993 (\\(lqPOSIX.1b\\(rq)")
LINE("-p1003.1c-95", "IEEE Std 1003.1c-1995 (\\(lqPOSIX.1c\\(rq)")
LINE("-p1003.1g-2000", "IEEE Std 1003.1g-2000 (\\(lqPOSIX.1g\\(rq)")
LINE("-p1003.1i-95", "IEEE Std 1003.1i-1995 (\\(lqPOSIX.1i\\(rq)")
LINE("-p1003.2", "IEEE Std 1003.2 (\\(lqPOSIX.2\\(rq)")
LINE("-p1003.2-92", "IEEE Std 1003.2-1992 (\\(lqPOSIX.2\\(rq)")
LINE("-p1003.2a-92", "IEEE Std 1003.2a-1992 (\\(lqPOSIX.2\\(rq)")
LINE("-isoC", "ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)")
LINE("-isoC-90", "ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)")
LINE("-isoC-amd1", "ISO/IEC 9899/AMD1:1995 (\\(lqISO\\~C90, Amendment 1\\(rq)")
LINE("-isoC-tcor1", "ISO/IEC 9899/TCOR1:1994 (\\(lqISO\\~C90, Technical Corrigendum 1\\(rq)")
LINE("-isoC-tcor2", "ISO/IEC 9899/TCOR2:1995 (\\(lqISO\\~C90, Technical Corrigendum 2\\(rq)")
LINE("-isoC-99", "ISO/IEC 9899:1999 (\\(lqISO\\~C99\\(rq)")
LINE("-isoC-2011", "ISO/IEC 9899:2011 (\\(lqISO\\~C11\\(rq)")
LINE("-iso9945-1-90", "ISO/IEC 9945-1:1990 (\\(lqPOSIX.1\\(rq)")
LINE("-iso9945-1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
LINE("-iso9945-2-93", "ISO/IEC 9945-2:1993 (\\(lqPOSIX.2\\(rq)")
LINE("-ansiC", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)")
LINE("-ansiC-89", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)")
LINE("-ieee754", "IEEE Std 754-1985")
LINE("-iso8802-3", "ISO 8802-3: 1989")
LINE("-iso8601", "ISO 8601")
LINE("-ieee1275-94", "IEEE Std 1275-1994 (\\(lqOpen Firmware\\(rq)")
LINE("-xpg3", "X/Open Portability Guide Issue\\~3 (\\(lqXPG3\\(rq)")
LINE("-xpg4", "X/Open Portability Guide Issue\\~4 (\\(lqXPG4\\(rq)")
LINE("-xpg4.2", "X/Open Portability Guide Issue\\~4, Version\\~2 (\\(lqXPG4.2\\(rq)")
LINE("-xbd5", "X/Open Base Definitions Issue\\~5 (\\(lqXBD5\\(rq)")
LINE("-xcu5", "X/Open Commands and Utilities Issue\\~5 (\\(lqXCU5\\(rq)")
LINE("-xsh4.2", "X/Open System Interfaces and Headers Issue\\~4, Version\\~2 (\\(lqXSH4.2\\(rq)")
LINE("-xsh5", "X/Open System Interfaces and Headers Issue\\~5 (\\(lqXSH5\\(rq)")
LINE("-xns5", "X/Open Networking Services Issue\\~5 (\\(lqXNS5\\(rq)")
LINE("-xns5.2", "X/Open Networking Services Issue\\~5.2 (\\(lqXNS5.2\\(rq)")
LINE("-xcurses4.2", "X/Open Curses Issue\\~4, Version\\~2 (\\(lqXCURSES4.2\\(rq)")
LINE("-susv1", "Version\\~1 of the Single UNIX Specification (\\(lqSUSv1\\(rq)")
LINE("-susv2", "Version\\~2 of the Single UNIX Specification (\\(lqSUSv2\\(rq)")
LINE("-susv3", "Version\\~3 of the Single UNIX Specification (\\(lqSUSv3\\(rq)")
LINE("-susv4", "Version\\~4 of the Single UNIX Specification (\\(lqSUSv4\\(rq)")
LINE("-svid4", "System\\~V Interface Definition, Fourth Edition (\\(lqSVID4\\(rq)")

45
tag.c
View File

@ -1,6 +1,6 @@
/* $Id: tag.c,v 1.19 2018/02/23 16:47:10 schwarze Exp $ */
/* $Id: tag.c,v 1.21 2018/11/22 11:30:23 schwarze Exp $ */
/*
* Copyright (c) 2015, 2016 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2015, 2016, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -18,6 +18,10 @@
#include <sys/types.h>
#if HAVE_ERR
#include <err.h>
#endif
#include <limits.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
@ -121,41 +125,57 @@ tag_init(void)
/*
* Set the line number where a term is defined,
* unless it is already defined at a higher priority.
* unless it is already defined at a lower priority.
*/
void
tag_put(const char *s, int prio, size_t line)
{
struct tag_entry *entry;
const char *se;
size_t len;
unsigned int slot;
/* Sanity checks. */
if (tag_files.tfd <= 0)
return;
if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e'))
s += 2;
if (*s == '\0' || strchr(s, ' ') != NULL)
/*
* Skip whitespace and whatever follows it,
* and if there is any, downgrade the priority.
*/
len = strcspn(s, " \t");
if (len == 0)
return;
slot = ohash_qlookup(&tag_data, s);
se = s + len;
if (*se != '\0')
prio = INT_MAX;
slot = ohash_qlookupi(&tag_data, s, &se);
entry = ohash_find(&tag_data, slot);
if (entry == NULL) {
/* Build a new entry. */
len = strlen(s) + 1;
entry = mandoc_malloc(sizeof(*entry) + len);
entry = mandoc_malloc(sizeof(*entry) + len + 1);
memcpy(entry->s, s, len);
entry->s[len] = '\0';
entry->lines = NULL;
entry->maxlines = entry->nlines = 0;
ohash_insert(&tag_data, slot, entry);
} else {
/* Handle priority 0 entries. */
/*
* Lower priority numbers take precedence,
* but 0 is special.
* A tag with priority 0 is only used
* if the tag occurs exactly once.
*/
if (prio == 0) {
if (entry->prio == 0)
@ -199,6 +219,11 @@ tag_write(void)
if (tag_files.tfd <= 0)
return;
if (tag_files.tagname != NULL && ohash_find(&tag_data,
ohash_qlookup(&tag_data, tag_files.tagname)) == NULL) {
warnx("%s: no such tag", tag_files.tagname);
tag_files.tagname = NULL;
}
stream = fdopen(tag_files.tfd, "w");
entry = ohash_first(&tag_data, &slot);
while (entry != NULL) {

3
tag.h
View File

@ -1,4 +1,4 @@
/* $Id: tag.h,v 1.7 2015/11/20 21:59:54 schwarze Exp $ */
/* $Id: tag.h,v 1.8 2018/11/22 11:30:23 schwarze Exp $ */
/*
* Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
*
@ -18,6 +18,7 @@
struct tag_files {
char ofn[20];
char tfn[20];
char *tagname;
int ofd;
int tfd;
pid_t tcpgid;

33
tbl.3
View File

@ -1,6 +1,6 @@
.\" $Id: tbl.3,v 1.2 2015/01/30 04:11:50 schwarze Exp $
.\" $Id: tbl.3,v 1.6 2018/12/14 06:33:14 schwarze Exp $
.\"
.\" Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2013, 2015, 2018 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: January 30 2015 $
.Dd $Mdocdate: December 14 2018 $
.Dt TBL 3
.Os
.Sh NAME
@ -26,16 +26,15 @@
.Nm tbl_free
.Nd roff table parser library for mandoc
.Sh SYNOPSIS
.In mandoc.h
.In libmandoc.h
.In libroff.h
.In sys/types.h
.In tbl.h
.In tbl_parse.h
.Ft struct tbl_node *
.Fo tbl_alloc
.Fa "int pos"
.Fa "int line"
.Fa "struct mparse *parse"
.Fc
.Ft enum rofferr
.Ft void
.Fo tbl_read
.Fa "struct tbl_node *tbl"
.Fa "int ln"
@ -67,15 +66,15 @@ utility and not designed for stand-alone use.
The present manual is intended as a reference for developers working on
.Xr mandoc 1 .
.Ss Data structures
Unless otherwise noted, all of the following data structures are defined in
.In mandoc.h
Unless otherwise noted, all of the following data structures are declared in
.In tbl.h
and are deleted in
.Fn tbl_free .
.Bl -tag -width Ds
.It Vt struct tbl_node
This structure describes a complete table.
It is defined in
.In libroff.h ,
It is declared in
.In tbl_int.h ,
created in
.Fn tbl_alloc ,
and stored in the members
@ -104,12 +103,6 @@ but if there is a span, the function
.Fn tbl_layout
guarantees that these pointers are not
.Dv NULL .
The function
.Fn tbl_alloc
guarantees that the
.Fa parse
member is not
.Dv NULL .
.It Vt struct tbl_opts
This structure describes the options of one table.
It is used as a substructure of
@ -226,7 +219,7 @@ member is not
.Ss Interface functions
The following functions are implemented in
.Pa tbl.c ,
and all callers in
and all callers are in
.Pa roff.c .
.Bl -tag -width Ds
.It Fn tbl_alloc
@ -279,6 +272,8 @@ and
.Fn roff_reset .
.El
.Ss Private functions
The following functions are declared in
.In tbl_int.h .
.Bl -tag -width Ds
.It Ft int Fn tbl_options "struct tbl_node *tbl" "int ln" "const char *p"
Parses the options line into

44
tbl.7
View File

@ -1,7 +1,7 @@
.\" $Id: tbl.7,v 1.29 2017/10/17 23:19:12 schwarze Exp $
.\" $Id: tbl.7,v 1.34 2019/03/02 21:03:02 schwarze Exp $
.\"
.\" Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2014,2015,2017,2018,2019 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -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: October 17 2017 $
.Dd $Mdocdate: March 2 2019 $
.Dt TBL 7
.Os
.Sh NAME
@ -147,9 +147,9 @@ The combined cell as a whole consumes only one cell
of the corresponding data line.
.It Cm a
Left-justify a string and pad with one space.
.It Cm ^
.It Cm \(ha
Vertically span rows from the last
.Pf non- Cm ^
.Pf non- Cm \(ha
layout cell.
It is an error to invoke a vertical span on the first layout line.
Unlike a horizontal span, a vertical span consumes a data cell
@ -231,13 +231,19 @@ Each data line consists of one or more data cells, delimited by
.Cm tab
characters.
.Pp
If a data cells contains only the single character
If a data cell contains only the two bytes
.Ql \e\(ha ,
the cell above spans to this row, as if the layout specification
of this cell were
.Cm \(ha .
.Pp
If a data cell contains only the single character
.Ql _
or
.Ql = ,
a single or double horizontal line is drawn across the cell,
joining its neighbours.
If a data cells contains only the two character sequence
If a data cell contains only the two character sequence
.Ql \e_
or
.Ql \e= ,
@ -323,7 +329,7 @@ _
AFL:2.39b
Mutt:1.8.0
Ruby:1.8.7.374
TeX Live:2015
TeX Live:2015
.TE
.Ed
.sp 2v
@ -332,8 +338,8 @@ Spans and skipping width calculations:
\&.TS
box tab(:);
lz s | rt
lt| cb| ^
^ | rz s.
lt| cb| \(ha
\(ha | rz s.
left:r
l:center:
:right
@ -388,8 +394,8 @@ T}::line 5
These examples were constructed to demonstrate many
.Nm
features in a compact way.
In real manual pages, keep tables as simple as possible:
Like that, they usually look better, are less fragile, and more portable.
In real manual pages, keep tables as simple as possible.
They usually look better, are less fragile, and are more portable.
.Sh COMPATIBILITY
The
.Xr mandoc 1
@ -432,3 +438,17 @@ reference was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
and
.An Ingo Schwarze Aq Mt schwarze@openbsd.org .
.Sh BUGS
In
.Fl T
.Cm utf8
output mode, heavy lines are drawn instead of double lines.
This cannot be improved because the Unicode standard only provides
an incomplete set of box drawing characters with double lines,
whereas it provides a full set of box drawing characters
with heavy lines.
It is unlikely this can be improved in the future because the box
drawing characters are already marked in Unicode as characters
intended only for backward compatibility with legacy systems,
and their use is not encouraged.
So it seems unlikely that the missing ones might get added in the future.

76
tbl.c
View File

@ -1,4 +1,4 @@
/* $Id: tbl.c,v 1.42 2017/07/08 17:52:50 schwarze Exp $ */
/* $Id: tbl.c,v 1.46 2018/12/14 06:33:14 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011, 2015 Ingo Schwarze <schwarze@openbsd.org>
@ -25,10 +25,12 @@
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "mandoc.h"
#include "tbl.h"
#include "libmandoc.h"
#include "libroff.h"
#include "tbl_parse.h"
#include "tbl_int.h"
void
@ -86,14 +88,15 @@ tbl_read(struct tbl_node *tbl, int ln, const char *p, int pos)
}
struct tbl_node *
tbl_alloc(int pos, int line, struct mparse *parse)
tbl_alloc(int pos, int line, struct tbl_node *last_tbl)
{
struct tbl_node *tbl;
tbl = mandoc_calloc(1, sizeof(*tbl));
if (last_tbl != NULL)
last_tbl->next = tbl;
tbl->line = line;
tbl->pos = pos;
tbl->parse = parse;
tbl->part = TBL_PART_OPTS;
tbl->opts.tab = '\t';
tbl->opts.decimal = '.';
@ -103,76 +106,77 @@ tbl_alloc(int pos, int line, struct mparse *parse)
void
tbl_free(struct tbl_node *tbl)
{
struct tbl_node *old_tbl;
struct tbl_row *rp;
struct tbl_cell *cp;
struct tbl_span *sp;
struct tbl_dat *dp;
while ((rp = tbl->first_row) != NULL) {
tbl->first_row = rp->next;
while (rp->first != NULL) {
cp = rp->first;
rp->first = cp->next;
free(cp->wstr);
free(cp);
while (tbl != NULL) {
while ((rp = tbl->first_row) != NULL) {
tbl->first_row = rp->next;
while (rp->first != NULL) {
cp = rp->first;
rp->first = cp->next;
free(cp->wstr);
free(cp);
}
free(rp);
}
free(rp);
}
while ((sp = tbl->first_span) != NULL) {
tbl->first_span = sp->next;
while (sp->first != NULL) {
dp = sp->first;
sp->first = dp->next;
free(dp->string);
free(dp);
while ((sp = tbl->first_span) != NULL) {
tbl->first_span = sp->next;
while (sp->first != NULL) {
dp = sp->first;
sp->first = dp->next;
free(dp->string);
free(dp);
}
free(sp);
}
free(sp);
old_tbl = tbl;
tbl = tbl->next;
free(old_tbl);
}
free(tbl);
}
void
tbl_restart(int line, int pos, struct tbl_node *tbl)
{
if (tbl->part == TBL_PART_CDATA)
mandoc_msg(MANDOCERR_TBLDATA_BLK, tbl->parse,
line, pos, "T&");
mandoc_msg(MANDOCERR_TBLDATA_BLK, line, pos, "T&");
tbl->part = TBL_PART_LAYOUT;
tbl->line = line;
tbl->pos = pos;
}
const struct tbl_span *
struct tbl_span *
tbl_span(struct tbl_node *tbl)
{
struct tbl_span *span;
assert(tbl);
span = tbl->current_span ? tbl->current_span->next
: tbl->first_span;
if (span)
if (span != NULL)
tbl->current_span = span;
return span;
}
int
tbl_end(struct tbl_node *tbl)
tbl_end(struct tbl_node *tbl, int still_open)
{
struct tbl_span *sp;
if (tbl->part == TBL_PART_CDATA)
mandoc_msg(MANDOCERR_TBLDATA_BLK, tbl->parse,
tbl->line, tbl->pos, "TE");
if (still_open)
mandoc_msg(MANDOCERR_BLK_NOEND, tbl->line, tbl->pos, "TS");
else if (tbl->part == TBL_PART_CDATA)
mandoc_msg(MANDOCERR_TBLDATA_BLK, tbl->line, tbl->pos, "TE");
sp = tbl->first_span;
while (sp != NULL && sp->first == NULL)
sp = sp->next;
if (sp == NULL) {
mandoc_msg(MANDOCERR_TBLDATA_NONE, tbl->parse,
tbl->line, tbl->pos, NULL);
mandoc_msg(MANDOCERR_TBLDATA_NONE, tbl->line, tbl->pos, NULL);
return 0;
}
return 1;

122
tbl.h Normal file
View File

@ -0,0 +1,122 @@
/* $Id: tbl.h,v 1.1 2018/12/12 21:54:35 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct tbl_opts {
int opts;
#define TBL_OPT_ALLBOX (1 << 0) /* Option "allbox". */
#define TBL_OPT_BOX (1 << 1) /* Option "box". */
#define TBL_OPT_CENTRE (1 << 2) /* Option "center". */
#define TBL_OPT_DBOX (1 << 3) /* Option "doublebox". */
#define TBL_OPT_EXPAND (1 << 4) /* Option "expand". */
#define TBL_OPT_NOKEEP (1 << 5) /* Option "nokeep". */
#define TBL_OPT_NOSPACE (1 << 6) /* Option "nospaces". */
#define TBL_OPT_NOWARN (1 << 7) /* Option "nowarn". */
int cols; /* Number of columns. */
int lvert; /* Width of left vertical line. */
int rvert; /* Width of right vertical line. */
char tab; /* Option "tab": cell separator. */
char decimal; /* Option "decimalpoint". */
};
enum tbl_cellt {
TBL_CELL_CENTRE, /* c, C */
TBL_CELL_RIGHT, /* r, R */
TBL_CELL_LEFT, /* l, L */
TBL_CELL_NUMBER, /* n, N */
TBL_CELL_SPAN, /* s, S */
TBL_CELL_LONG, /* a, A */
TBL_CELL_DOWN, /* ^ */
TBL_CELL_HORIZ, /* _, - */
TBL_CELL_DHORIZ, /* = */
TBL_CELL_MAX
};
/*
* A cell in a layout row.
*/
struct tbl_cell {
struct tbl_cell *next; /* Layout cell to the right. */
char *wstr; /* Min width represented as a string. */
size_t width; /* Minimum column width. */
size_t spacing; /* To the right of the column. */
int vert; /* Width of subsequent vertical line. */
int col; /* Column number, starting from 0. */
int flags;
#define TBL_CELL_BOLD (1 << 0) /* b, B, fB */
#define TBL_CELL_ITALIC (1 << 1) /* i, I, fI */
#define TBL_CELL_TALIGN (1 << 2) /* t, T */
#define TBL_CELL_UP (1 << 3) /* u, U */
#define TBL_CELL_BALIGN (1 << 4) /* d, D */
#define TBL_CELL_WIGN (1 << 5) /* z, Z */
#define TBL_CELL_EQUAL (1 << 6) /* e, E */
#define TBL_CELL_WMAX (1 << 7) /* x, X */
enum tbl_cellt pos;
};
/*
* A layout row.
*/
struct tbl_row {
struct tbl_row *next; /* Layout row below. */
struct tbl_cell *first; /* Leftmost layout cell. */
struct tbl_cell *last; /* Rightmost layout cell. */
int vert; /* Width of left vertical line. */
};
enum tbl_datt {
TBL_DATA_NONE, /* Uninitialized row. */
TBL_DATA_DATA, /* Contains data rather than a line. */
TBL_DATA_HORIZ, /* _: connecting horizontal line. */
TBL_DATA_DHORIZ, /* =: connecting double horizontal line. */
TBL_DATA_NHORIZ, /* \_: isolated horizontal line. */
TBL_DATA_NDHORIZ /* \=: isolated double horizontal line. */
};
/*
* A cell within a row of data. The "string" field contains the
* actual string value that's in the cell. The rest is layout.
*/
struct tbl_dat {
struct tbl_dat *next; /* Data cell to the right. */
struct tbl_cell *layout; /* Associated layout cell. */
char *string; /* Data, or NULL if not TBL_DATA_DATA. */
int hspans; /* How many horizontal spans follow. */
int vspans; /* How many vertical spans follow. */
int block; /* T{ text block T} */
enum tbl_datt pos;
};
enum tbl_spant {
TBL_SPAN_DATA, /* Contains data rather than a line. */
TBL_SPAN_HORIZ, /* _: horizontal line. */
TBL_SPAN_DHORIZ /* =: double horizontal line. */
};
/*
* A row of data in a table.
*/
struct tbl_span {
struct tbl_opts *opts; /* Options for the table as a whole. */
struct tbl_span *prev; /* Data row above. */
struct tbl_span *next; /* Data row below. */
struct tbl_row *layout; /* Associated layout row. */
struct tbl_dat *first; /* Leftmost data cell. */
struct tbl_dat *last; /* Rightmost data cell. */
int line; /* Input file line number. */
enum tbl_spant pos;
};

View File

@ -1,7 +1,7 @@
/* $Id: tbl_data.c,v 1.45 2017/07/08 17:52:50 schwarze Exp $ */
/* $Id: tbl_data.c,v 1.52 2019/02/09 16:00:39 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011,2015,2017,2018,2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -21,14 +21,16 @@
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "mandoc.h"
#include "tbl.h"
#include "libmandoc.h"
#include "libroff.h"
#include "tbl_int.h"
static void getdata(struct tbl_node *, struct tbl_span *,
int, const char *, int *);
@ -40,10 +42,20 @@ static void
getdata(struct tbl_node *tbl, struct tbl_span *dp,
int ln, const char *p, int *pos)
{
struct tbl_dat *dat;
struct tbl_dat *dat, *pdat;
struct tbl_cell *cp;
struct tbl_span *pdp;
int sv;
/*
* Determine the length of the string in the cell
* and advance the parse point to the end of the cell.
*/
sv = *pos;
while (p[*pos] != '\0' && p[*pos] != tbl->opts.tab)
(*pos)++;
/* Advance to the next layout cell, skipping spanners. */
cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next;
@ -65,34 +77,68 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp,
cp->col = dp->layout->last->col + 1;
dp->layout->last = cp;
} else {
mandoc_msg(MANDOCERR_TBLDATA_EXTRA, tbl->parse,
ln, *pos, p + *pos);
while (p[*pos])
mandoc_msg(MANDOCERR_TBLDATA_EXTRA,
ln, sv, "%s", p + sv);
while (p[*pos] != '\0')
(*pos)++;
return;
}
}
dat = mandoc_calloc(1, sizeof(*dat));
dat = mandoc_malloc(sizeof(*dat));
dat->layout = cp;
dat->next = NULL;
dat->string = NULL;
dat->hspans = 0;
dat->vspans = 0;
dat->block = 0;
dat->pos = TBL_DATA_NONE;
dat->spans = 0;
/*
* Increment the number of vertical spans in a data cell above,
* if this cell vertically extends one or more cells above.
* The iteration must be done over data rows,
* not over layout rows, because one layout row
* can be reused for more than one data row.
*/
if (cp->pos == TBL_CELL_DOWN ||
(*pos - sv == 2 && p[sv] == '\\' && p[sv + 1] == '^')) {
pdp = dp;
while ((pdp = pdp->prev) != NULL) {
pdat = pdp->first;
while (pdat != NULL &&
pdat->layout->col < dat->layout->col)
pdat = pdat->next;
if (pdat == NULL)
break;
if (pdat->layout->pos != TBL_CELL_DOWN &&
strcmp(pdat->string, "\\^") != 0) {
pdat->vspans++;
break;
}
}
}
/*
* Count the number of horizontal spans to the right of this cell.
* This is purely a matter of the layout, independent of the data.
*/
for (cp = cp->next; cp != NULL; cp = cp->next)
if (cp->pos == TBL_CELL_SPAN)
dat->spans++;
dat->hspans++;
else
break;
/* Append the new data cell to the data row. */
if (dp->last == NULL)
dp->first = dat;
else
dp->last->next = dat;
dp->last = dat;
sv = *pos;
while (p[*pos] && p[*pos] != tbl->opts.tab)
(*pos)++;
/*
* Check for a continued-data scope opening. This consists of a
* trailing `T{' at the end of the line. Subsequent lines,
@ -106,7 +152,7 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp,
dat->string = mandoc_strndup(p + sv, *pos - sv);
if (p[*pos])
if (p[*pos] != '\0')
(*pos)++;
if ( ! strcmp(dat->string, "_"))
@ -125,7 +171,7 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp,
dat->layout->pos == TBL_CELL_DOWN) &&
dat->pos == TBL_DATA_DATA && *dat->string != '\0')
mandoc_msg(MANDOCERR_TBLDATA_SPAN,
tbl->parse, ln, sv, dat->string);
ln, sv, "%s", dat->string);
}
void
@ -164,8 +210,8 @@ tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos)
dat->string = mandoc_strdup(p + pos);
if (dat->layout->pos == TBL_CELL_DOWN)
mandoc_msg(MANDOCERR_TBLDATA_SPAN, tbl->parse,
ln, pos, dat->string);
mandoc_msg(MANDOCERR_TBLDATA_SPAN,
ln, pos, "%s", dat->string);
}
static struct tbl_span *
@ -202,14 +248,27 @@ tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos)
assert(rp != NULL);
if ( ! strcmp(p, "_")) {
sp = newspan(tbl, ln, rp);
sp->pos = TBL_SPAN_HORIZ;
return;
} else if ( ! strcmp(p, "=")) {
sp = newspan(tbl, ln, rp);
sp->pos = TBL_SPAN_DHORIZ;
return;
if (p[1] == '\0') {
switch (p[0]) {
case '.':
/*
* Empty request lines must be handled here
* and cannot be discarded in roff_parseln()
* because in the layout section, they
* are significant and end the layout.
*/
return;
case '_':
sp = newspan(tbl, ln, rp);
sp->pos = TBL_SPAN_HORIZ;
return;
case '=':
sp = newspan(tbl, ln, rp);
sp->pos = TBL_SPAN_DHORIZ;
return;
default:
break;
}
}
/*

View File

@ -1,7 +1,7 @@
/* $Id: tbl_html.c,v 1.24 2018/06/25 13:45:57 schwarze Exp $ */
/* $Id: tbl_html.c,v 1.32 2019/01/06 04:55:09 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -25,6 +25,7 @@
#include <string.h>
#include "mandoc.h"
#include "tbl.h"
#include "out.h"
#include "html.h"
@ -79,6 +80,7 @@ html_tbl_sulen(const struct roffsu *su, void *arg)
static void
html_tblopen(struct html *h, const struct tbl_span *sp)
{
html_close_paragraph(h);
if (h->tbl.cols == NULL) {
h->tbl.len = html_tbl_len;
h->tbl.slen = html_tbl_strlen;
@ -86,7 +88,15 @@ html_tblopen(struct html *h, const struct tbl_span *sp)
tblcalc(&h->tbl, sp, 0, 0);
}
assert(NULL == h->tblt);
h->tblt = print_otag(h, TAG_TABLE, "c", "tbl");
h->tblt = print_otag(h, TAG_TABLE, "c?ss", "tbl",
"border",
sp->opts->opts & TBL_OPT_ALLBOX ? "1" : NULL,
"border-style",
sp->opts->opts & TBL_OPT_DBOX ? "double" :
sp->opts->opts & TBL_OPT_BOX ? "solid" : NULL,
"border-top-style",
sp->pos == TBL_SPAN_DHORIZ ? "double" :
sp->pos == TBL_SPAN_HORIZ ? "solid" : NULL);
}
void
@ -101,43 +111,138 @@ print_tblclose(struct html *h)
void
print_tbl(struct html *h, const struct tbl_span *sp)
{
const struct tbl_dat *dp;
struct tag *tt;
int ic;
/* Inhibit printing of spaces: we do padding ourselves. */
const struct tbl_dat *dp;
const struct tbl_cell *cp;
const struct tbl_span *psp;
struct tag *tt;
const char *hspans, *vspans, *halign, *valign;
const char *bborder, *lborder, *rborder;
char hbuf[4], vbuf[4];
int i;
if (h->tblt == NULL)
html_tblopen(h, sp);
assert(h->tblt);
/*
* Horizontal lines spanning the whole table
* are handled by previous or following table rows.
*/
if (sp->pos != TBL_SPAN_DATA)
return;
/* Inhibit printing of spaces: we do padding ourselves. */
h->flags |= HTML_NONOSPACE;
h->flags |= HTML_NOSPACE;
tt = print_otag(h, TAG_TR, "");
/* Draw a vertical line left of this row? */
switch (sp->pos) {
case TBL_SPAN_HORIZ:
case TBL_SPAN_DHORIZ:
print_otag(h, TAG_TD, "?", "colspan", "0");
switch (sp->layout->vert) {
case 2:
lborder = "double";
break;
case 1:
lborder = "solid";
break;
default:
dp = sp->first;
for (ic = 0; ic < sp->opts->cols; ic++) {
print_stagq(h, tt);
print_otag(h, TAG_TD, "");
if (dp == NULL || dp->layout->col > ic)
continue;
if (dp->layout->pos != TBL_CELL_DOWN)
if (dp->string != NULL)
print_text(h, dp->string);
dp = dp->next;
}
lborder = NULL;
break;
}
/* Draw a horizontal line below this row? */
bborder = NULL;
if ((psp = sp->next) != NULL) {
switch (psp->pos) {
case TBL_SPAN_DHORIZ:
bborder = "double";
break;
case TBL_SPAN_HORIZ:
bborder = "solid";
break;
default:
break;
}
}
tt = print_otag(h, TAG_TR, "ss",
"border-left-style", lborder,
"border-bottom-style", bborder);
for (dp = sp->first; dp != NULL; dp = dp->next) {
print_stagq(h, tt);
/*
* Do not generate <td> elements for continuations
* of spanned cells. Larger <td> elements covering
* this space were already generated earlier.
*/
cp = dp->layout;
if (cp->pos == TBL_CELL_SPAN || cp->pos == TBL_CELL_DOWN ||
(dp->string != NULL && strcmp(dp->string, "\\^") == 0))
continue;
/* Determine the attribute values. */
if (dp->hspans > 0) {
(void)snprintf(hbuf, sizeof(hbuf),
"%d", dp->hspans + 1);
hspans = hbuf;
} else
hspans = NULL;
if (dp->vspans > 0) {
(void)snprintf(vbuf, sizeof(vbuf),
"%d", dp->vspans + 1);
vspans = vbuf;
} else
vspans = NULL;
switch (cp->pos) {
case TBL_CELL_CENTRE:
halign = "center";
break;
case TBL_CELL_RIGHT:
case TBL_CELL_NUMBER:
halign = "right";
break;
default:
halign = NULL;
break;
}
if (cp->flags & TBL_CELL_TALIGN)
valign = "top";
else if (cp->flags & TBL_CELL_BALIGN)
valign = "bottom";
else
valign = NULL;
for (i = dp->hspans; i > 0; i--)
cp = cp->next;
switch (cp->vert) {
case 2:
rborder = "double";
break;
case 1:
rborder = "solid";
break;
default:
rborder = NULL;
break;
}
/* Print the element and the attributes. */
print_otag(h, TAG_TD, "??sss",
"colspan", hspans, "rowspan", vspans,
"vertical-align", valign,
"text-align", halign,
"border-right-style", rborder);
if (dp->string != NULL)
print_text(h, dp->string);
}
print_tagq(h, tt);
h->flags &= ~HTML_NONOSPACE;
@ -148,5 +253,4 @@ print_tbl(struct html *h, const struct tbl_span *sp)
h->tbl.cols = NULL;
print_tblclose(h);
}
}

47
tbl_int.h Normal file
View File

@ -0,0 +1,47 @@
/* $Id: tbl_int.h,v 1.2 2018/12/14 06:33:14 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011,2013,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Internal interfaces of the tbl(7) parser.
* For use inside the tbl(7) parser only.
*/
enum tbl_part {
TBL_PART_OPTS, /* In the first line, ends with semicolon. */
TBL_PART_LAYOUT, /* In the layout section, ends with full stop. */
TBL_PART_DATA, /* In the data section, ends with TE. */
TBL_PART_CDATA /* In a T{ block, ends with T} */
};
struct tbl_node {
struct tbl_opts opts; /* Options for the whole table. */
struct tbl_node *next; /* Next table. */
struct tbl_row *first_row; /* First layout row. */
struct tbl_row *last_row; /* Last layout row. */
struct tbl_span *first_span; /* First data row. */
struct tbl_span *current_span; /* Data row being parsed. */
struct tbl_span *last_span; /* Last data row. */
int line; /* Line number in input file. */
int pos; /* Column number in input file. */
enum tbl_part part; /* Table section being parsed. */
};
void tbl_option(struct tbl_node *, int, const char *, int *);
void tbl_layout(struct tbl_node *, int, const char *, int);
void tbl_data(struct tbl_node *, int, const char *, int);
void tbl_cdata(struct tbl_node *, int, const char *, int);
void tbl_reset(struct tbl_node *);

View File

@ -1,4 +1,4 @@
/* $Id: tbl_layout.c,v 1.44 2017/06/27 18:25:02 schwarze Exp $ */
/* $Id: tbl_layout.c,v 1.48 2018/12/14 05:18:03 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2012, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
@ -21,14 +21,16 @@
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "mandoc.h"
#include "tbl.h"
#include "libmandoc.h"
#include "libroff.h"
#include "tbl_int.h"
struct tbl_phrase {
char name;
@ -84,8 +86,7 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
(*pos)++;
goto mod;
}
mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, tbl->parse,
ln, *pos, NULL);
mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, ln, *pos, NULL);
return;
}
@ -113,8 +114,7 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
cp->flags |= TBL_CELL_ITALIC;
goto mod;
case 'm':
mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, tbl->parse,
ln, *pos, "m");
mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, ln, *pos, "m");
goto mod;
case 'p':
case 'v':
@ -157,10 +157,10 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
cp->vert++;
else
mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
tbl->parse, ln, *pos - 1, NULL);
ln, *pos - 1, NULL);
goto mod;
default:
mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
mandoc_msg(MANDOCERR_TBLLAYOUT_CHAR,
ln, *pos - 1, "%c", p[*pos - 1]);
goto mod;
}
@ -173,7 +173,7 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
/* Support only one-character font-names for now. */
if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) {
mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
mandoc_msg(MANDOCERR_FT_BAD,
ln, *pos, "TS %s", p + *pos - 1);
if (p[*pos] != '\0')
(*pos)++;
@ -195,7 +195,7 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
case 'R':
goto mod;
default:
mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
mandoc_msg(MANDOCERR_FT_BAD,
ln, *pos - 1, "TS f%c", p[*pos - 1]);
goto mod;
}
@ -216,7 +216,7 @@ cell(struct tbl_node *tbl, struct tbl_row *rp,
rp->vert++;
else
mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
tbl->parse, ln, *pos, NULL);
ln, *pos, NULL);
}
(*pos)++;
}
@ -235,7 +235,7 @@ cell(struct tbl_node *tbl, struct tbl_row *rp,
break;
if (i == KEYS_MAX) {
mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
mandoc_msg(MANDOCERR_TBLLAYOUT_CHAR,
ln, *pos, "%c", p[*pos]);
(*pos)++;
goto again;
@ -246,14 +246,12 @@ cell(struct tbl_node *tbl, struct tbl_row *rp,
if (c == TBL_CELL_SPAN) {
if (rp->last == NULL)
mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN,
tbl->parse, ln, *pos, NULL);
mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN, ln, *pos, NULL);
else if (rp->last->pos == TBL_CELL_HORIZ ||
rp->last->pos == TBL_CELL_DHORIZ)
c = rp->last->pos;
} else if (c == TBL_CELL_DOWN && rp == tbl->first_row)
mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN,
tbl->parse, ln, *pos, NULL);
mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN, ln, *pos, NULL);
(*pos)++;
@ -296,7 +294,7 @@ tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos)
}
if (tbl->first_row->first == NULL) {
mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
tbl->parse, ln, pos, NULL);
ln, pos, NULL);
cell_alloc(tbl, tbl->first_row,
TBL_CELL_LEFT);
if (tbl->opts.lvert < tbl->first_row->vert)

View File

@ -1,4 +1,4 @@
/* $Id: tbl_opts.c,v 1.21 2015/09/26 00:54:04 schwarze Exp $ */
/* $Id: tbl_opts.c,v 1.24 2018/12/14 05:18:03 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
@ -25,8 +25,9 @@
#include <string.h>
#include "mandoc.h"
#include "tbl.h"
#include "libmandoc.h"
#include "libroff.h"
#include "tbl_int.h"
#define KEY_DPOINT 0
#define KEY_DELIM 1
@ -80,7 +81,7 @@ arg(struct tbl_node *tbl, int ln, const char *p, int *pos, int key)
switch (key) {
case KEY_DELIM:
mandoc_vmsg(MANDOCERR_TBLOPT_EQN, tbl->parse,
mandoc_msg(MANDOCERR_TBLOPT_EQN,
ln, *pos, "%.*s", len, p + *pos);
want = 2;
break;
@ -102,12 +103,11 @@ arg(struct tbl_node *tbl, int ln, const char *p, int *pos, int key)
}
if (len == 0)
mandoc_msg(MANDOCERR_TBLOPT_NOARG,
tbl->parse, ln, *pos, keys[key].name);
mandoc_msg(MANDOCERR_TBLOPT_NOARG, ln, *pos,
"%s", keys[key].name);
else if (want && len != want)
mandoc_vmsg(MANDOCERR_TBLOPT_ARGSZ,
tbl->parse, ln, *pos, "%s want %d have %d",
keys[key].name, want, len);
mandoc_msg(MANDOCERR_TBLOPT_ARGSZ, ln, *pos,
"%s want %d have %d", keys[key].name, want, len);
*pos += len;
if (p[*pos] == ')')
@ -141,8 +141,8 @@ tbl_option(struct tbl_node *tbl, int ln, const char *p, int *offs)
len++;
if (len == 0) {
mandoc_vmsg(MANDOCERR_TBLOPT_ALPHA,
tbl->parse, ln, pos, "%c", p[pos]);
mandoc_msg(MANDOCERR_TBLOPT_ALPHA,
ln, pos, "%c", p[pos]);
pos++;
continue;
}
@ -156,7 +156,7 @@ tbl_option(struct tbl_node *tbl, int ln, const char *p, int *offs)
i++;
if (i == KEY_MAXKEYS) {
mandoc_vmsg(MANDOCERR_TBLOPT_BAD, tbl->parse,
mandoc_msg(MANDOCERR_TBLOPT_BAD,
ln, pos, "%.*s", len, p + pos);
pos += len;
continue;

30
tbl_parse.h Normal file
View File

@ -0,0 +1,30 @@
/* $Id: tbl_parse.h,v 1.2 2018/12/14 06:33:14 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* External interface of the tbl(7) parser.
* For use in the roff(7) and tbl(7) parsers only.
*/
struct tbl_node;
struct tbl_span;
struct tbl_node *tbl_alloc(int, int, struct tbl_node *);
int tbl_end(struct tbl_node *, int);
void tbl_free(struct tbl_node *);
void tbl_read(struct tbl_node *, int, const char *, int);
void tbl_restart(int, int, struct tbl_node *);
struct tbl_span *tbl_span(struct tbl_node *);

View File

@ -1,7 +1,7 @@
/* $Id: tbl_term.c,v 1.57 2017/07/31 16:14:10 schwarze Exp $ */
/* $Id: tbl_term.c,v 1.68 2019/02/09 21:02:47 schwarze Exp $ */
/*
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011,2012,2014,2015,2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,34 +20,121 @@
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "tbl.h"
#include "out.h"
#include "term.h"
#define IS_HORIZ(cp) ((cp)->pos == TBL_CELL_HORIZ || \
(cp)->pos == TBL_CELL_DHORIZ)
static size_t term_tbl_len(size_t, void *);
static size_t term_tbl_strlen(const char *, void *);
static size_t term_tbl_sulen(const struct roffsu *, void *);
static void tbl_char(struct termp *, char, size_t);
static void tbl_data(struct termp *, const struct tbl_opts *,
const struct tbl_cell *,
const struct tbl_dat *,
const struct roffcol *);
static void tbl_direct_border(struct termp *, int, size_t);
static void tbl_fill_border(struct termp *, int, size_t);
static void tbl_fill_char(struct termp *, char, size_t);
static void tbl_fill_string(struct termp *, const char *, size_t);
static void tbl_hrule(struct termp *, const struct tbl_span *,
const struct tbl_span *, int);
static void tbl_literal(struct termp *, const struct tbl_dat *,
const struct roffcol *);
static void tbl_number(struct termp *, const struct tbl_opts *,
const struct tbl_dat *,
const struct roffcol *);
static void tbl_hrule(struct termp *, const struct tbl_span *, int);
static void tbl_word(struct termp *, const struct tbl_dat *);
/*
* The following border-character tables are indexed
* by ternary (3-based) numbers, as opposed to binary or decimal.
* Each ternary digit describes the line width in one direction:
* 0 means no line, 1 single or light line, 2 double or heavy line.
*/
/* Positional values of the four directions. */
#define BRIGHT 1
#define BDOWN 3
#define BLEFT (3 * 3)
#define BUP (3 * 3 * 3)
#define BHORIZ (BLEFT + BRIGHT)
/* Code points to use for each combination of widths. */
static const int borders_utf8[81] = {
0x0020, 0x2576, 0x257a, /* 000 right */
0x2577, 0x250c, 0x250d, /* 001 down */
0x257b, 0x250e, 0x250f, /* 002 */
0x2574, 0x2500, 0x257c, /* 010 left */
0x2510, 0x252c, 0x252e, /* 011 left down */
0x2512, 0x2530, 0x2532, /* 012 */
0x2578, 0x257e, 0x2501, /* 020 left */
0x2511, 0x252d, 0x252f, /* 021 left down */
0x2513, 0x2531, 0x2533, /* 022 */
0x2575, 0x2514, 0x2515, /* 100 up */
0x2502, 0x251c, 0x251d, /* 101 up down */
0x257d, 0x251f, 0x2522, /* 102 */
0x2518, 0x2534, 0x2536, /* 110 up left */
0x2524, 0x253c, 0x253e, /* 111 all */
0x2527, 0x2541, 0x2546, /* 112 */
0x2519, 0x2535, 0x2537, /* 120 up left */
0x2525, 0x253d, 0x253f, /* 121 all */
0x252a, 0x2545, 0x2548, /* 122 */
0x2579, 0x2516, 0x2517, /* 200 up */
0x257f, 0x251e, 0x2521, /* 201 up down */
0x2503, 0x2520, 0x2523, /* 202 */
0x251a, 0x2538, 0x253a, /* 210 up left */
0x2526, 0x2540, 0x2544, /* 211 all */
0x2528, 0x2542, 0x254a, /* 212 */
0x251b, 0x2539, 0x253b, /* 220 up left */
0x2529, 0x2543, 0x2547, /* 221 all */
0x252b, 0x2549, 0x254b, /* 222 */
};
/* ASCII approximations for these code points, compatible with groff. */
static const int borders_ascii[81] = {
' ', '-', '=', /* 000 right */
'|', '+', '+', /* 001 down */
'|', '+', '+', /* 002 */
'-', '-', '=', /* 010 left */
'+', '+', '+', /* 011 left down */
'+', '+', '+', /* 012 */
'=', '=', '=', /* 020 left */
'+', '+', '+', /* 021 left down */
'+', '+', '+', /* 022 */
'|', '+', '+', /* 100 up */
'|', '+', '+', /* 101 up down */
'|', '+', '+', /* 102 */
'+', '+', '+', /* 110 up left */
'+', '+', '+', /* 111 all */
'+', '+', '+', /* 112 */
'+', '+', '+', /* 120 up left */
'+', '+', '+', /* 121 all */
'+', '+', '+', /* 122 */
'|', '+', '+', /* 200 up */
'|', '+', '+', /* 201 up down */
'|', '+', '+', /* 202 */
'+', '+', '+', /* 210 up left */
'+', '+', '+', /* 211 all */
'+', '+', '+', /* 212 */
'+', '+', '+', /* 220 up left */
'+', '+', '+', /* 221 all */
'+', '+', '+', /* 222 */
};
/* Either of the above according to the selected output encoding. */
static const int *borders_locale;
static size_t
term_tbl_sulen(const struct roffsu *su, void *arg)
{
@ -69,19 +156,22 @@ term_tbl_len(size_t sz, void *arg)
return term_len((const struct termp *)arg, sz);
}
void
term_tbl(struct termp *tp, const struct tbl_span *sp)
{
const struct tbl_cell *cp, *cpn, *cpp;
const struct tbl_cell *cp, *cpn, *cpp, *cps;
const struct tbl_dat *dp;
static size_t offset;
size_t save_offset;
size_t coloff, tsz;
int ic, horiz, spans, vert, more;
char fc;
int hspans, ic, more;
int dvert, fc, horiz, lhori, rhori, uvert;
/* Inhibit printing of spaces: we do padding ourselves. */
tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
save_offset = tp->tcol->offset;
/*
* The first time we're invoked for a given table block,
@ -89,6 +179,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
*/
if (tp->tbl.cols == NULL) {
borders_locale = tp->enc == TERMENC_UTF8 ?
borders_utf8 : borders_ascii;
tp->tbl.len = term_tbl_len;
tp->tbl.slen = term_tbl_strlen;
tp->tbl.sulen = term_tbl_sulen;
@ -120,21 +213,24 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tsz += tp->tbl.cols[sp->opts->cols - 1].width;
if (offset + tsz > tp->tcol->rmargin)
tsz -= 1;
tp->tcol->offset = offset + tp->tcol->rmargin > tsz ?
offset = offset + tp->tcol->rmargin > tsz ?
(offset + tp->tcol->rmargin - tsz) / 2 : 0;
tp->tcol->offset = offset;
}
/* Horizontal frame at the start of boxed tables. */
if (sp->opts->opts & TBL_OPT_DBOX)
tbl_hrule(tp, sp, 3);
if (tp->enc == TERMENC_ASCII &&
sp->opts->opts & TBL_OPT_DBOX)
tbl_hrule(tp, NULL, sp, TBL_OPT_DBOX);
if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
tbl_hrule(tp, sp, 2);
tbl_hrule(tp, NULL, sp, TBL_OPT_BOX);
}
/* Set up the columns. */
tp->flags |= TERMP_MULTICOL;
tp->tcol->offset = offset;
horiz = 0;
switch (sp->pos) {
case TBL_SPAN_HORIZ:
@ -156,9 +252,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
/* Set up the data columns. */
dp = sp->first;
spans = 0;
hspans = 0;
for (ic = 0; ic < sp->opts->cols; ic++) {
if (spans == 0) {
if (hspans == 0) {
tp->tcol++;
tp->tcol->offset = coloff;
}
@ -166,13 +262,13 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tcol->rmargin = coloff;
if (ic + 1 < sp->opts->cols)
coloff += tp->tbl.cols[ic].spacing;
if (spans) {
spans--;
if (hspans) {
hspans--;
continue;
}
if (dp == NULL)
continue;
spans = dp->spans;
hspans = dp->hspans;
if (ic || sp->layout->first->pos != TBL_CELL_SPAN)
dp = dp->next;
}
@ -192,14 +288,14 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tcol = tp->tcols;
cp = cpn = sp->layout->first;
dp = sp->first;
spans = 0;
hspans = 0;
for (ic = 0; ic < sp->opts->cols; ic++) {
if (cpn != NULL) {
cp = cpn;
cpn = cpn->next;
}
if (spans) {
spans--;
if (hspans) {
hspans--;
continue;
}
tp->tcol++;
@ -207,7 +303,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
if (dp == NULL)
continue;
spans = dp->spans;
hspans = dp->hspans;
if (cp->pos != TBL_CELL_SPAN)
dp = dp->next;
}
@ -218,37 +314,43 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
/* Print the vertical frame at the start of each row. */
tp->tcol = tp->tcols;
fc = '\0';
if (sp->layout->vert ||
(sp->next != NULL && sp->next->layout->vert &&
sp->next->pos == TBL_SPAN_DATA) ||
(sp->prev != NULL && sp->prev->layout->vert &&
(horiz || (IS_HORIZ(sp->layout->first) &&
!IS_HORIZ(sp->prev->layout->first)))) ||
sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))
fc = horiz || IS_HORIZ(sp->layout->first) ? '+' : '|';
else if (horiz && sp->opts->lvert)
fc = '-';
if (fc != '\0') {
uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 :
sp->opts->opts & TBL_OPT_BOX ? 1 : 0;
if (sp->pos == TBL_SPAN_DATA && uvert < sp->layout->vert)
uvert = dvert = sp->layout->vert;
if (sp->next != NULL && sp->next->pos == TBL_SPAN_DATA &&
dvert < sp->next->layout->vert)
dvert = sp->next->layout->vert;
if (sp->prev != NULL && uvert < sp->prev->layout->vert &&
(horiz || (IS_HORIZ(sp->layout->first) &&
!IS_HORIZ(sp->prev->layout->first))))
uvert = sp->prev->layout->vert;
rhori = sp->pos == TBL_SPAN_DHORIZ ||
(sp->first != NULL && sp->first->pos == TBL_DATA_DHORIZ) ||
sp->layout->first->pos == TBL_CELL_DHORIZ ? 2 :
sp->pos == TBL_SPAN_HORIZ ||
(sp->first != NULL && sp->first->pos == TBL_DATA_HORIZ) ||
sp->layout->first->pos == TBL_CELL_HORIZ ? 1 : 0;
fc = BUP * uvert + BDOWN * dvert + BRIGHT * rhori;
if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) {
(*tp->advance)(tp, tp->tcols->offset);
(*tp->letter)(tp, fc);
tp->viscol = tp->tcol->offset + 1;
tp->viscol = tp->tcol->offset;
tbl_direct_border(tp, fc, 1);
}
/* Print the data cells. */
more = 0;
if (horiz) {
tbl_hrule(tp, sp, 0);
term_flushln(tp);
} else {
if (horiz)
tbl_hrule(tp, sp->prev, sp, 0);
else {
cp = sp->layout->first;
cpn = sp->next == NULL ? NULL :
sp->next->layout->first;
cpp = sp->prev == NULL ? NULL :
sp->prev->layout->first;
dp = sp->first;
spans = 0;
hspans = 0;
for (ic = 0; ic < sp->opts->cols; ic++) {
/*
@ -257,25 +359,27 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
* and advance to next layout cell.
*/
uvert = dvert = fc = 0;
if (cp != NULL) {
vert = cp->vert;
cps = cp;
while (cps->next != NULL &&
cps->next->pos == TBL_CELL_SPAN)
cps = cps->next;
if (sp->pos == TBL_SPAN_DATA)
uvert = dvert = cps->vert;
switch (cp->pos) {
case TBL_CELL_HORIZ:
fc = '-';
fc = BHORIZ;
break;
case TBL_CELL_DHORIZ:
fc = '=';
fc = BHORIZ * 2;
break;
default:
fc = ' ';
break;
}
} else {
vert = 0;
fc = ' ';
}
if (cpp != NULL) {
if (vert == 0 &&
if (uvert < cpp->vert &&
cp != NULL &&
((IS_HORIZ(cp) &&
!IS_HORIZ(cpp)) ||
@ -283,19 +387,31 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
cpp->next != NULL &&
IS_HORIZ(cp->next) &&
!IS_HORIZ(cpp->next))))
vert = cpp->vert;
uvert = cpp->vert;
cpp = cpp->next;
}
if (vert == 0 &&
sp->opts->opts & TBL_OPT_ALLBOX)
vert = 1;
if (sp->opts->opts & TBL_OPT_ALLBOX) {
if (uvert == 0)
uvert = 1;
if (dvert == 0)
dvert = 1;
}
if (cpn != NULL) {
if (vert == 0)
vert = cpn->vert;
if (dvert == 0 ||
(dvert < cpn->vert &&
tp->enc == TERMENC_UTF8))
dvert = cpn->vert;
cpn = cpn->next;
}
if (cp != NULL)
cp = cp->next;
lhori = (cp != NULL &&
cp->pos == TBL_CELL_DHORIZ) ||
(dp != NULL &&
dp->pos == TBL_DATA_DHORIZ) ? 2 :
(cp != NULL &&
cp->pos == TBL_CELL_HORIZ) ||
(dp != NULL &&
dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
/*
* Skip later cells in a span,
@ -303,12 +419,13 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
* and advance to next data cell.
*/
if (spans) {
spans--;
if (hspans) {
hspans--;
cp = cp->next;
continue;
}
if (dp != NULL) {
spans = dp->spans;
hspans = dp->hspans;
if (ic || sp->layout->first->pos
!= TBL_CELL_SPAN)
dp = dp->next;
@ -330,10 +447,16 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
* but not after the last column.
*/
if (fc == ' ' && ((vert == 0 &&
(cp == NULL || !IS_HORIZ(cp))) ||
tp->tcol + 1 == tp->tcols + tp->lasttcol))
if (fc == 0 &&
((uvert == 0 && dvert == 0 &&
cp != NULL && (cp->next == NULL ||
!IS_HORIZ(cp->next))) ||
tp->tcol + 1 ==
tp->tcols + tp->lasttcol)) {
if (cp != NULL)
cp = cp->next;
continue;
}
if (tp->viscol < tp->tcol->rmargin) {
(*tp->advance)(tp, tp->tcol->rmargin
@ -341,77 +464,83 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->viscol = tp->tcol->rmargin;
}
while (tp->viscol < tp->tcol->rmargin +
tp->tbl.cols[ic].spacing / 2) {
(*tp->letter)(tp, fc);
tp->viscol++;
}
tp->tbl.cols[ic].spacing / 2)
tbl_direct_border(tp,
BHORIZ * lhori, 1);
if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
continue;
if (fc == ' ' && cp != NULL) {
switch (cp->pos) {
case TBL_CELL_HORIZ:
fc = '-';
break;
case TBL_CELL_DHORIZ:
fc = '=';
break;
default:
break;
}
}
if (tp->tbl.cols[ic].spacing) {
(*tp->letter)(tp, fc == ' ' ? '|' :
vert ? '+' : fc);
tp->viscol++;
}
if (cp != NULL)
cp = cp->next;
rhori = (cp != NULL &&
cp->pos == TBL_CELL_DHORIZ) ||
(dp != NULL &&
dp->pos == TBL_DATA_DHORIZ) ? 2 :
(cp != NULL &&
cp->pos == TBL_CELL_HORIZ) ||
(dp != NULL &&
dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
if (tp->tbl.cols[ic].spacing)
tbl_direct_border(tp,
BLEFT * lhori + BRIGHT * rhori +
BUP * uvert + BDOWN * dvert, 1);
if (tp->enc == TERMENC_UTF8)
uvert = dvert = 0;
if (fc != ' ') {
if (cp != NULL &&
cp->pos == TBL_CELL_HORIZ)
fc = '-';
else if (cp != NULL &&
cp->pos == TBL_CELL_DHORIZ)
fc = '=';
else
fc = ' ';
}
if (tp->tbl.cols[ic].spacing > 2 &&
(vert > 1 || fc != ' ')) {
(*tp->letter)(tp, fc == ' ' ? '|' :
vert > 1 ? '+' : fc);
tp->viscol++;
}
(uvert > 1 || dvert > 1 || rhori))
tbl_direct_border(tp,
BHORIZ * rhori +
BUP * (uvert > 1) +
BDOWN * (dvert > 1), 1);
}
}
/* Print the vertical frame at the end of each row. */
fc = '\0';
if ((sp->layout->last->vert &&
sp->layout->last->col + 1 == sp->opts->cols) ||
(sp->next != NULL &&
sp->next->layout->last->vert &&
sp->next->layout->last->col + 1 == sp->opts->cols) ||
(sp->prev != NULL &&
sp->prev->layout->last->vert &&
sp->prev->layout->last->col + 1 == sp->opts->cols &&
(horiz || (IS_HORIZ(sp->layout->last) &&
!IS_HORIZ(sp->prev->layout->last)))) ||
(sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)))
fc = horiz || IS_HORIZ(sp->layout->last) ? '+' : '|';
else if (horiz && sp->opts->rvert)
fc = '-';
if (fc != '\0') {
uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 :
sp->opts->opts & TBL_OPT_BOX ? 1 : 0;
if (sp->pos == TBL_SPAN_DATA &&
uvert < sp->layout->last->vert &&
sp->layout->last->col + 1 == sp->opts->cols)
uvert = dvert = sp->layout->last->vert;
if (sp->next != NULL &&
dvert < sp->next->layout->last->vert &&
sp->next->layout->last->col + 1 == sp->opts->cols)
dvert = sp->next->layout->last->vert;
if (sp->prev != NULL &&
uvert < sp->prev->layout->last->vert &&
sp->prev->layout->last->col + 1 == sp->opts->cols &&
(horiz || (IS_HORIZ(sp->layout->last) &&
!IS_HORIZ(sp->prev->layout->last))))
uvert = sp->prev->layout->last->vert;
lhori = sp->pos == TBL_SPAN_DHORIZ ||
(sp->last != NULL &&
sp->last->pos == TBL_DATA_DHORIZ &&
sp->last->layout->col + 1 == sp->opts->cols) ||
(sp->layout->last->pos == TBL_CELL_DHORIZ &&
sp->layout->last->col + 1 == sp->opts->cols) ? 2 :
sp->pos == TBL_SPAN_HORIZ ||
(sp->last != NULL &&
sp->last->pos == TBL_DATA_HORIZ &&
sp->last->layout->col + 1 == sp->opts->cols) ||
(sp->layout->last->pos == TBL_CELL_HORIZ &&
sp->layout->last->col + 1 == sp->opts->cols) ? 1 : 0;
fc = BUP * uvert + BDOWN * dvert + BLEFT * lhori;
if (uvert > 0 || dvert > 0 || (horiz && sp->opts->rvert)) {
if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||
sp->layout->last->col + 1 < sp->opts->cols)) {
tp->tcol++;
(*tp->advance)(tp,
tp->tcol->offset > tp->viscol ?
tp->tcol->offset - tp->viscol : 1);
do {
tbl_direct_border(tp,
BHORIZ * lhori, 1);
} while (tp->viscol < tp->tcol->offset);
}
(*tp->letter)(tp, fc);
tbl_direct_border(tp, fc, 1);
}
(*tp->endline)(tp);
tp->viscol = 0;
@ -428,80 +557,156 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tcol->rmargin = tp->maxrmargin;
if (sp->next == NULL) {
if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) {
tbl_hrule(tp, sp, 2);
tbl_hrule(tp, sp, NULL, TBL_OPT_BOX);
tp->skipvsp = 1;
}
if (sp->opts->opts & TBL_OPT_DBOX) {
tbl_hrule(tp, sp, 3);
if (tp->enc == TERMENC_ASCII &&
sp->opts->opts & TBL_OPT_DBOX) {
tbl_hrule(tp, sp, NULL, TBL_OPT_DBOX);
tp->skipvsp = 2;
}
assert(tp->tbl.cols);
free(tp->tbl.cols);
tp->tbl.cols = NULL;
tp->tcol->offset = offset;
} else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX &&
(sp->next == NULL || sp->next->pos == TBL_SPAN_DATA ||
sp->next->next != NULL))
tbl_hrule(tp, sp, 1);
tbl_hrule(tp, sp, sp->next, TBL_OPT_ALLBOX);
tp->tcol->offset = save_offset;
tp->flags &= ~TERMP_NONOSPACE;
}
/*
* Kinds of horizontal rulers:
* 0: inside the table (single or double line with crossings)
* 1: inside the table (single or double line with crossings and ends)
* 2: inner frame (single line with crossings and ends)
* 3: outer frame (single line without crossings with ends)
*/
static void
tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind)
tbl_hrule(struct termp *tp, const struct tbl_span *spp,
const struct tbl_span *spn, int flags)
{
const struct tbl_cell *cp, *cpn, *cpp;
const struct roffcol *col;
int vert;
char line, cross;
const struct tbl_cell *cpp; /* Layout cell above this line. */
const struct tbl_cell *cpn; /* Layout cell below this line. */
const struct tbl_dat *dpn; /* Data cell below this line. */
const struct roffcol *col; /* Contains width and spacing. */
int opts; /* For the table as a whole. */
int bw; /* Box line width. */
int hw; /* Horizontal line width. */
int lw, rw; /* Left and right line widths. */
int uw, dw; /* Vertical line widths. */
line = (kind < 2 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-';
cross = (kind < 3) ? '+' : '-';
cpp = spp == NULL ? NULL : spp->layout->first;
cpn = spn == NULL ? NULL : spn->layout->first;
dpn = NULL;
if (spn != NULL) {
if (spn->pos == TBL_SPAN_DATA)
dpn = spn->first;
else if (spn->next != NULL)
dpn = spn->next->first;
}
opts = spn == NULL ? spp->opts->opts : spn->opts->opts;
bw = opts & TBL_OPT_DBOX ? (tp->enc == TERMENC_UTF8 ? 2 : 1) :
opts & (TBL_OPT_BOX | TBL_OPT_ALLBOX) ? 1 : 0;
hw = flags == TBL_OPT_DBOX || flags == TBL_OPT_BOX ? bw :
spn->pos == TBL_SPAN_DHORIZ ? 2 : 1;
/* Print the left end of the line. */
if (tp->viscol == 0) {
(*tp->advance)(tp, tp->tcols->offset);
tp->viscol = tp->tcols->offset;
}
if (flags != 0)
tbl_direct_border(tp,
(spp == NULL ? 0 : BUP * bw) +
(spn == NULL ? 0 : BDOWN * bw) +
(spp == NULL || cpn == NULL ||
cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1);
if (kind)
term_word(tp, "+");
cp = sp->layout->first;
cpp = kind || sp->prev == NULL ? NULL : sp->prev->layout->first;
if (cpp == cp)
cpp = NULL;
cpn = kind > 1 || sp->next == NULL ? NULL : sp->next->layout->first;
if (cpn == cp)
cpn = NULL;
for (;;) {
col = tp->tbl.cols + cp->col;
tbl_char(tp, line, col->width + col->spacing / 2);
vert = cp->vert;
if ((cp = cp->next) == NULL)
break;
col = tp->tbl.cols + (cpn == NULL ? cpp->col : cpn->col);
/* Print the horizontal line inside this column. */
lw = cpp == NULL || cpn == NULL ||
(cpn->pos != TBL_CELL_DOWN &&
(dpn == NULL || strcmp(dpn->string, "\\^") != 0))
? hw : 0;
tbl_direct_border(tp, BHORIZ * lw,
col->width + col->spacing / 2);
/*
* Figure out whether a vertical line is crossing
* at the end of this column,
* and advance to the next column.
*/
uw = dw = 0;
if (cpp != NULL) {
if (vert < cpp->vert)
vert = cpp->vert;
if (flags != TBL_OPT_DBOX) {
uw = cpp->vert;
if (uw == 0 && opts & TBL_OPT_ALLBOX)
uw = 1;
}
cpp = cpp->next;
}
if (cpn != NULL) {
if (vert < cpn->vert)
vert = cpn->vert;
if (flags != TBL_OPT_DBOX) {
dw = cpn->vert;
if (dw == 0 && opts & TBL_OPT_ALLBOX)
dw = 1;
}
cpn = cpn->next;
while (dpn != NULL && dpn->layout != cpn)
dpn = dpn->next;
}
if (sp->opts->opts & TBL_OPT_ALLBOX && !vert)
vert = 1;
if (cpp == NULL && cpn == NULL)
break;
/* Vertical lines do not cross spanned cells. */
if (cpp != NULL && cpp->pos == TBL_CELL_SPAN)
uw = 0;
if (cpn != NULL && cpn->pos == TBL_CELL_SPAN)
dw = 0;
/* The horizontal line inside the next column. */
rw = cpp == NULL || cpn == NULL ||
(cpn->pos != TBL_CELL_DOWN &&
(dpn == NULL || strcmp(dpn->string, "\\^") != 0))
? hw : 0;
/* The line crossing at the end of this column. */
if (col->spacing)
tbl_char(tp, vert ? cross : line, 1);
tbl_direct_border(tp, BLEFT * lw +
BRIGHT * rw + BUP * uw + BDOWN * dw, 1);
/*
* In ASCII output, a crossing may print two characters.
*/
if (tp->enc != TERMENC_ASCII || (uw < 2 && dw < 2))
uw = dw = 0;
if (col->spacing > 2)
tbl_char(tp, vert > 1 ? cross : line, 1);
tbl_direct_border(tp,
BHORIZ * rw + BUP * uw + BDOWN * dw, 1);
/* Padding before the start of the next column. */
if (col->spacing > 4)
tbl_char(tp, line, (col->spacing - 3) / 2);
tbl_direct_border(tp,
BHORIZ * rw, (col->spacing - 3) / 2);
}
if (kind) {
term_word(tp, "+");
term_flushln(tp);
/* Print the right end of the line. */
if (flags != 0) {
tbl_direct_border(tp,
(spp == NULL ? 0 : BUP * bw) +
(spn == NULL ? 0 : BDOWN * bw) +
(spp == NULL || spn == NULL ||
spn->layout->last->pos != TBL_CELL_DOWN ?
BLEFT * hw : 0), 1);
(*tp->endline)(tp);
tp->viscol = 0;
}
}
@ -512,10 +717,10 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
{
switch (cp->pos) {
case TBL_CELL_HORIZ:
tbl_char(tp, '-', col->width);
tbl_fill_border(tp, BHORIZ, col->width);
return;
case TBL_CELL_DHORIZ:
tbl_char(tp, '=', col->width);
tbl_fill_border(tp, BHORIZ * 2, col->width);
return;
default:
break;
@ -529,11 +734,11 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
return;
case TBL_DATA_HORIZ:
case TBL_DATA_NHORIZ:
tbl_char(tp, '-', col->width);
tbl_fill_border(tp, BHORIZ, col->width);
return;
case TBL_DATA_NDHORIZ:
case TBL_DATA_DHORIZ:
tbl_char(tp, '=', col->width);
tbl_fill_border(tp, BHORIZ * 2, col->width);
return;
default:
break;
@ -558,18 +763,48 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
}
static void
tbl_char(struct termp *tp, char c, size_t len)
tbl_fill_string(struct termp *tp, const char *cp, size_t len)
{
size_t i, sz;
char cp[2];
size_t i, sz;
sz = term_strlen(tp, cp);
for (i = 0; i < len; i += sz)
term_word(tp, cp);
}
static void
tbl_fill_char(struct termp *tp, char c, size_t len)
{
char cp[2];
cp[0] = c;
cp[1] = '\0';
tbl_fill_string(tp, cp, len);
}
sz = term_strlen(tp, cp);
static void
tbl_fill_border(struct termp *tp, int c, size_t len)
{
char buf[12];
for (i = 0; i < len; i += sz)
term_word(tp, cp);
if ((c = borders_locale[c]) > 127) {
(void)snprintf(buf, sizeof(buf), "\\[u%04x]", c);
tbl_fill_string(tp, buf, len);
} else
tbl_fill_char(tp, c, len);
}
static void
tbl_direct_border(struct termp *tp, int c, size_t len)
{
size_t i, sz;
c = borders_locale[c];
sz = (*tp->width)(tp, c);
for (i = 0; i < len; i += sz) {
(*tp->letter)(tp, c);
tp->viscol += sz;
}
}
static void
@ -577,14 +812,14 @@ tbl_literal(struct termp *tp, const struct tbl_dat *dp,
const struct roffcol *col)
{
size_t len, padl, padr, width;
int ic, spans;
int ic, hspans;
assert(dp->string);
len = term_strlen(tp, dp->string);
width = col->width;
ic = dp->layout->col;
spans = dp->spans;
while (spans--)
hspans = dp->hspans;
while (hspans--)
width += tp->tbl.cols[++ic].width + 3;
padr = width > len ? width - len : 0;
@ -609,9 +844,9 @@ tbl_literal(struct termp *tp, const struct tbl_dat *dp,
break;
}
tbl_char(tp, ASCII_NBRSP, padl);
tbl_fill_char(tp, ASCII_NBRSP, padl);
tbl_word(tp, dp);
tbl_char(tp, ASCII_NBRSP, padr);
tbl_fill_char(tp, ASCII_NBRSP, padr);
}
static void
@ -619,44 +854,66 @@ tbl_number(struct termp *tp, const struct tbl_opts *opts,
const struct tbl_dat *dp,
const struct roffcol *col)
{
char *cp;
const char *cp, *lastdigit, *lastpoint;
size_t intsz, padl, totsz;
char buf[2];
size_t sz, psz, ssz, d, padl;
int i;
/*
* See calc_data_number(). Left-pad by taking the offset of our
* and the maximum decimal; right-pad by the remaining amount.
* Almost the same code as in tblcalc_number():
* First find the position of the decimal point.
*/
assert(dp->string);
lastdigit = lastpoint = NULL;
for (cp = dp->string; cp[0] != '\0'; cp++) {
if (cp[0] == '\\' && cp[1] == '&') {
lastdigit = lastpoint = cp;
break;
} else if (cp[0] == opts->decimal &&
(isdigit((unsigned char)cp[1]) ||
(cp > dp->string && isdigit((unsigned char)cp[-1]))))
lastpoint = cp;
else if (isdigit((unsigned char)cp[0]))
lastdigit = cp;
}
sz = term_strlen(tp, dp->string);
/* Then measure both widths. */
buf[0] = opts->decimal;
buf[1] = '\0';
psz = term_strlen(tp, buf);
if ((cp = strrchr(dp->string, opts->decimal)) != NULL) {
for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
buf[0] = dp->string[i];
ssz += term_strlen(tp, buf);
padl = 0;
totsz = term_strlen(tp, dp->string);
if (lastdigit != NULL) {
if (lastpoint == NULL)
lastpoint = lastdigit + 1;
intsz = 0;
buf[1] = '\0';
for (cp = dp->string; cp < lastpoint; cp++) {
buf[0] = cp[0];
intsz += term_strlen(tp, buf);
}
d = ssz + psz;
} else
d = sz + psz;
if (col->decimal > d && col->width > sz) {
padl = col->decimal - d;
if (padl + sz > col->width)
padl = col->width - sz;
tbl_char(tp, ASCII_NBRSP, padl);
} else
padl = 0;
/*
* Pad left to match the decimal position,
* but avoid exceeding the total column width.
*/
if (col->decimal > intsz && col->width > totsz) {
padl = col->decimal - intsz;
if (padl + totsz > col->width)
padl = col->width - totsz;
}
/* If it is not a number, simply center the string. */
} else if (col->width > totsz)
padl = (col->width - totsz) / 2;
tbl_fill_char(tp, ASCII_NBRSP, padl);
tbl_word(tp, dp);
if (col->width > sz + padl)
tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
/* Pad right to fill the column. */
if (col->width > padl + totsz)
tbl_fill_char(tp, ASCII_NBRSP, col->width - padl - totsz);
}
static void

527
term.c
View File

@ -1,7 +1,7 @@
/* $Id: term.c,v 1.274 2017/07/28 14:25:48 schwarze Exp $ */
/* $Id: term.c,v 1.280 2019/01/15 12:16:18 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -21,6 +21,7 @@
#include <assert.h>
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -37,6 +38,10 @@ static void bufferc(struct termp *, char);
static void encode(struct termp *, const char *, size_t);
static void encode1(struct termp *, int);
static void endline(struct termp *);
static void term_field(struct termp *, size_t, size_t,
size_t, size_t);
static void term_fill(struct termp *, size_t *, size_t *,
size_t);
void
@ -83,223 +88,139 @@ term_end(struct termp *p)
* Flush a chunk of text. By default, break the output line each time
* the right margin is reached, and continue output on the next line
* at the same offset as the chunk itself. By default, also break the
* output line at the end of the chunk.
* The following flags may be specified:
*
* - TERMP_NOBREAK: Do not break the output line at the right margin,
* but only at the max right margin. Also, do not break the output
* line at the end of the chunk, such that the next call can pad to
* the next column. However, if less than p->trailspace blanks,
* which can be 0, 1, or 2, remain to the right margin, the line
* will be broken.
* - TERMP_BRTRSP: Consider trailing whitespace significant
* when deciding whether the chunk fits or not.
* - TERMP_BRIND: If the chunk does not fit and the output line has
* to be broken, start the next line at the right margin instead
* of at the offset. Used together with TERMP_NOBREAK for the tags
* in various kinds of tagged lists.
* - TERMP_HANG: Do not break the output line at the right margin,
* append the next chunk after it even if this one is too long.
* To be used together with TERMP_NOBREAK.
* - TERMP_NOPAD: Start writing at the current position,
* do not pad with blank characters up to the offset.
* output line at the end of the chunk. There are many flags modifying
* this behaviour, see the comments in the body of the function.
*/
void
term_flushln(struct termp *p)
{
size_t vis; /* current visual position on output */
size_t vbl; /* number of blanks to prepend to output */
size_t vend; /* end of word visual position on output */
size_t bp; /* visual right border position */
size_t dv; /* temporary for visual pos calculations */
size_t j; /* temporary loop index for p->tcol->buf */
size_t jhy; /* last hyph before overflow w/r/t j */
size_t maxvis; /* output position of visible boundary */
int ntab; /* number of tabs to prepend */
int breakline; /* after this word */
size_t vbl; /* Number of blanks to prepend to the output. */
size_t vbr; /* Actual visual position of the end of field. */
size_t vfield; /* Desired visual field width. */
size_t vtarget; /* Desired visual position of the right margin. */
size_t ic; /* Character position in the input buffer. */
size_t nbr; /* Number of characters to print in this field. */
/*
* Normally, start writing at the left margin, but with the
* NOPAD flag, start writing at the current position instead.
*/
vbl = (p->flags & TERMP_NOPAD) || p->tcol->offset < p->viscol ?
0 : p->tcol->offset - p->viscol;
if (p->minbl && vbl < p->minbl)
vbl = p->minbl;
maxvis = p->tcol->rmargin > p->viscol + vbl ?
p->tcol->rmargin - p->viscol - vbl : 0;
bp = !(p->flags & TERMP_NOBREAK) ? maxvis :
p->maxrmargin > p->viscol + vbl ?
p->maxrmargin - p->viscol - vbl : 0;
vis = vend = 0;
if ((p->flags & TERMP_MULTICOL) == 0)
p->tcol->col = 0;
while (p->tcol->col < p->tcol->lastcol) {
/* Loop over output lines. */
for (;;) {
vfield = p->tcol->rmargin > p->viscol + vbl ?
p->tcol->rmargin - p->viscol - vbl : 0;
/*
* Handle literal tab characters: collapse all
* subsequent tabs into a single huge set of spaces.
* Normally, break the line at the the right margin
* of the field, but with the NOBREAK flag, only
* break it at the max right margin of the screen,
* and with the BRNEVER flag, never break it at all.
*/
vtarget = p->flags & TERMP_BRNEVER ? SIZE_MAX :
(p->flags & TERMP_NOBREAK) == 0 ? vfield :
p->maxrmargin > p->viscol + vbl ?
p->maxrmargin - p->viscol - vbl : 0;
/*
* Figure out how much text will fit in the field.
* If there is whitespace only, print nothing.
*/
term_fill(p, &nbr, &vbr, vtarget);
if (nbr == 0)
break;
/*
* With the CENTER or RIGHT flag, increase the indentation
* to center the text between the left and right margins
* or to adjust it to the right margin, respectively.
*/
if (vbr < vtarget) {
if (p->flags & TERMP_CENTER)
vbl += (vtarget - vbr) / 2;
else if (p->flags & TERMP_RIGHT)
vbl += vtarget - vbr;
}
/* Finally, print the field content. */
term_field(p, vbl, nbr, vbr, vtarget);
/*
* If there is no text left in the field, exit the loop.
* If the BRTRSP flag is set, consider trailing
* whitespace significant when deciding whether
* the field fits or not.
*/
for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) {
switch (p->tcol->buf[ic]) {
case '\t':
if (p->flags & TERMP_BRTRSP)
vbr = term_tab_next(vbr);
continue;
case ' ':
if (p->flags & TERMP_BRTRSP)
vbr += (*p->width)(p, ' ');
continue;
case '\n':
case ASCII_BREAK:
continue;
default:
break;
}
break;
}
if (ic == p->tcol->lastcol)
break;
/*
* At the location of an automtic line break, input
* space characters are consumed by the line break.
*/
ntab = 0;
while (p->tcol->col < p->tcol->lastcol &&
p->tcol->buf[p->tcol->col] == '\t') {
vend = term_tab_next(vis);
vbl += vend - vis;
vis = vend;
ntab++;
p->tcol->buf[p->tcol->col] == ' ')
p->tcol->col++;
}
/*
* Count up visible word characters. Control sequences
* (starting with the CSI) aren't counted. A space
* generates a non-printing word, which is valid (the
* space is printed according to regular spacing rules).
* In multi-column mode, leave the rest of the text
* in the buffer to be handled by a subsequent
* invocation, such that the other columns of the
* table can be handled first.
* In single-column mode, simply break the line.
*/
jhy = 0;
breakline = 0;
for (j = p->tcol->col; j < p->tcol->lastcol; j++) {
if (p->tcol->buf[j] == '\n') {
if ((p->flags & TERMP_BRIND) == 0)
breakline = 1;
continue;
}
if (p->tcol->buf[j] == ' ' || p->tcol->buf[j] == '\t')
break;
/* Back over the last printed character. */
if (p->tcol->buf[j] == '\b') {
assert(j);
vend -= (*p->width)(p, p->tcol->buf[j - 1]);
continue;
}
/* Regular word. */
/* Break at the hyphen point if we overrun. */
if (vend > vis && vend < bp &&
(p->tcol->buf[j] == ASCII_HYPH||
p->tcol->buf[j] == ASCII_BREAK))
jhy = j;
/*
* Hyphenation now decided, put back a real
* hyphen such that we get the correct width.
*/
if (p->tcol->buf[j] == ASCII_HYPH)
p->tcol->buf[j] = '-';
vend += (*p->width)(p, p->tcol->buf[j]);
}
/*
* Find out whether we would exceed the right margin.
* If so, break to the next line.
*/
if (vend > bp && jhy == 0 && vis > 0 &&
(p->flags & TERMP_BRNEVER) == 0) {
if (p->flags & TERMP_MULTICOL)
return;
endline(p);
vend -= vis;
/* Use pending tabs on the new line. */
vbl = 0;
while (ntab--)
vbl = term_tab_next(vbl);
/* Re-establish indentation. */
if (p->flags & TERMP_BRIND)
vbl += p->tcol->rmargin;
else
vbl += p->tcol->offset;
maxvis = p->tcol->rmargin > vbl ?
p->tcol->rmargin - vbl : 0;
bp = !(p->flags & TERMP_NOBREAK) ? maxvis :
p->maxrmargin > vbl ? p->maxrmargin - vbl : 0;
}
/*
* Write out the rest of the word.
*/
for ( ; p->tcol->col < p->tcol->lastcol; p->tcol->col++) {
if (vend > bp && jhy > 0 && p->tcol->col > jhy)
break;
if (p->tcol->buf[p->tcol->col] == '\n')
continue;
if (p->tcol->buf[p->tcol->col] == '\t')
break;
if (p->tcol->buf[p->tcol->col] == ' ') {
j = p->tcol->col;
while (p->tcol->col < p->tcol->lastcol &&
p->tcol->buf[p->tcol->col] == ' ')
p->tcol->col++;
dv = (p->tcol->col - j) * (*p->width)(p, ' ');
vbl += dv;
vend += dv;
break;
}
if (p->tcol->buf[p->tcol->col] == ASCII_NBRSP) {
vbl += (*p->width)(p, ' ');
continue;
}
if (p->tcol->buf[p->tcol->col] == ASCII_BREAK)
continue;
/*
* Now we definitely know there will be
* printable characters to output,
* so write preceding white space now.
*/
if (vbl) {
(*p->advance)(p, vbl);
p->viscol += vbl;
vbl = 0;
}
(*p->letter)(p, p->tcol->buf[p->tcol->col]);
if (p->tcol->buf[p->tcol->col] == '\b')
p->viscol -= (*p->width)(p,
p->tcol->buf[p->tcol->col - 1]);
else
p->viscol += (*p->width)(p,
p->tcol->buf[p->tcol->col]);
}
vis = vend;
if (breakline == 0)
continue;
/* Explicitly requested output line break. */
if (p->flags & TERMP_MULTICOL)
return;
endline(p);
breakline = 0;
vis = vend = 0;
p->viscol = 0;
/* Re-establish indentation. */
/*
* Normally, start the next line at the same indentation
* as this one, but with the BRIND flag, start it at the
* right margin instead. This is used together with
* NOBREAK for the tags in various kinds of tagged lists.
*/
vbl = p->tcol->offset;
maxvis = p->tcol->rmargin > vbl ?
p->tcol->rmargin - vbl : 0;
bp = !(p->flags & TERMP_NOBREAK) ? maxvis :
p->maxrmargin > vbl ? p->maxrmargin - vbl : 0;
vbl = p->flags & TERMP_BRIND ?
p->tcol->rmargin : p->tcol->offset;
}
/*
* If there was trailing white space, it was not printed;
* so reset the cursor position accordingly.
*/
if (vis > vbl)
vis -= vbl;
else
vis = 0;
/* Reset output state in preparation for the next field. */
p->col = p->tcol->col = p->tcol->lastcol = 0;
p->minbl = p->trailspace;
@ -308,18 +229,186 @@ term_flushln(struct termp *p)
if (p->flags & TERMP_MULTICOL)
return;
/* Trailing whitespace is significant in some columns. */
/*
* The HANG flag means that the next field
* always follows on the same line.
* The NOBREAK flag means that the next field
* follows on the same line unless the field was overrun.
* Normally, break the line at the end of each field.
*/
if (vis && vbl && (TERMP_BRTRSP & p->flags))
vis += vbl;
/* If the column was overrun, break the line. */
if ((p->flags & TERMP_NOBREAK) == 0 ||
((p->flags & TERMP_HANG) == 0 &&
vis + p->trailspace * (*p->width)(p, ' ') > maxvis))
if ((p->flags & TERMP_HANG) == 0 &&
((p->flags & TERMP_NOBREAK) == 0 ||
vbr + term_len(p, p->trailspace) > vfield))
endline(p);
}
/*
* Store the number of input characters to print in this field in *nbr
* and their total visual width to print in *vbr.
* If there is only whitespace in the field, both remain zero.
* The desired visual width of the field is provided by vtarget.
* If the first word is longer, the field will be overrun.
*/
static void
term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
{
size_t ic; /* Character position in the input buffer. */
size_t vis; /* Visual position of the current character. */
size_t vn; /* Visual position of the next character. */
int breakline; /* Break at the end of this word. */
int graph; /* Last character was non-blank. */
*nbr = *vbr = vis = 0;
breakline = graph = 0;
for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) {
switch (p->tcol->buf[ic]) {
case '\b': /* Escape \o (overstrike) or backspace markup. */
assert(ic > 0);
vis -= (*p->width)(p, p->tcol->buf[ic - 1]);
continue;
case '\t': /* Normal ASCII whitespace. */
case ' ':
case ASCII_BREAK: /* Escape \: (breakpoint). */
switch (p->tcol->buf[ic]) {
case '\t':
vn = term_tab_next(vis);
break;
case ' ':
vn = vis + (*p->width)(p, ' ');
break;
case ASCII_BREAK:
vn = vis;
break;
}
/* Can break at the end of a word. */
if (breakline || vn > vtarget)
break;
if (graph) {
*nbr = ic;
*vbr = vis;
graph = 0;
}
vis = vn;
continue;
case '\n': /* Escape \p (break at the end of the word). */
breakline = 1;
continue;
case ASCII_HYPH: /* Breakable hyphen. */
graph = 1;
/*
* We are about to decide whether to break the
* line or not, so we no longer need this hyphen
* to be marked as breakable. Put back a real
* hyphen such that we get the correct width.
*/
p->tcol->buf[ic] = '-';
vis += (*p->width)(p, '-');
if (vis > vtarget) {
ic++;
break;
}
*nbr = ic + 1;
*vbr = vis;
continue;
case ASCII_NBRSP: /* Non-breakable space. */
p->tcol->buf[ic] = ' ';
/* FALLTHROUGH */
default: /* Printable character. */
graph = 1;
vis += (*p->width)(p, p->tcol->buf[ic]);
if (vis > vtarget && *nbr > 0)
return;
continue;
}
break;
}
/*
* If the last word extends to the end of the field without any
* trailing whitespace, the loop could not check yet whether it
* can remain on this line. So do the check now.
*/
if (graph && (vis <= vtarget || *nbr == 0)) {
*nbr = ic;
*vbr = vis;
}
}
/*
* Print the contents of one field
* with an indentation of vbl visual columns,
* an input string length of nbr characters,
* an output width of vbr visual columns,
* and a desired field width of vtarget visual columns.
*/
static void
term_field(struct termp *p, size_t vbl, size_t nbr, size_t vbr, size_t vtarget)
{
size_t ic; /* Character position in the input buffer. */
size_t vis; /* Visual position of the current character. */
size_t dv; /* Visual width of the current character. */
size_t vn; /* Visual position of the next character. */
vis = 0;
for (ic = p->tcol->col; ic < nbr; ic++) {
/*
* To avoid the printing of trailing whitespace,
* do not print whitespace right away, only count it.
*/
switch (p->tcol->buf[ic]) {
case '\n':
case ASCII_BREAK:
continue;
case '\t':
vn = term_tab_next(vis);
vbl += vn - vis;
vis = vn;
continue;
case ' ':
case ASCII_NBRSP:
dv = (*p->width)(p, ' ');
vbl += dv;
vis += dv;
continue;
default:
break;
}
/*
* We found a non-blank character to print,
* so write preceding white space now.
*/
if (vbl > 0) {
(*p->advance)(p, vbl);
p->viscol += vbl;
vbl = 0;
}
/* Print the character and adjust the visual position. */
(*p->letter)(p, p->tcol->buf[ic]);
if (p->tcol->buf[ic] == '\b') {
dv = (*p->width)(p, p->tcol->buf[ic - 1]);
p->viscol -= dv;
vis -= dv;
} else {
dv = (*p->width)(p, p->tcol->buf[ic]);
p->viscol += dv;
vis += dv;
}
}
p->tcol->col = nbr;
}
static void
endline(struct termp *p)
{
@ -477,9 +566,6 @@ term_word(struct termp *p, const char *word)
word++;
esc = mandoc_escape(&word, &seq, &sz);
if (ESCAPE_ERROR == esc)
continue;
switch (esc) {
case ESCAPE_UNICODE:
uc = mchars_num2uc(seq + 1, sz - 1);
@ -500,6 +586,9 @@ term_word(struct termp *p, const char *word)
encode1(p, uc);
}
continue;
case ESCAPE_UNDEF:
uc = *seq;
break;
case ESCAPE_FONTBOLD:
term_fontrepl(p, TERMFONT_BOLD);
continue;
@ -510,6 +599,7 @@ term_word(struct termp *p, const char *word)
term_fontrepl(p, TERMFONT_BI);
continue;
case ESCAPE_FONT:
case ESCAPE_FONTCW:
case ESCAPE_FONTROMAN:
term_fontrepl(p, TERMFONT_NONE);
continue;
@ -525,6 +615,16 @@ term_word(struct termp *p, const char *word)
else if (*word == '\0')
p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE);
continue;
case ESCAPE_DEVICE:
if (p->type == TERMTYPE_PDF)
encode(p, "pdf", 3);
else if (p->type == TERMTYPE_PS)
encode(p, "ps", 2);
else if (p->enc == TERMENC_ASCII)
encode(p, "ascii", 5);
else
encode(p, "utf8", 4);
continue;
case ESCAPE_HORIZ:
if (*seq == '|') {
seq++;
@ -576,6 +676,9 @@ term_word(struct termp *p, const char *word)
case ESCAPE_SPECIAL:
uc = mchars_spec2cp(cp, sz);
break;
case ESCAPE_UNDEF:
uc = *seq;
break;
default:
uc = -1;
break;
@ -834,12 +937,8 @@ term_strlen(const struct termp *p, const char *cp)
switch (*cp) {
case '\\':
cp++;
esc = mandoc_escape(&cp, &seq, &ssz);
if (ESCAPE_ERROR == esc)
continue;
rhs = NULL;
esc = mandoc_escape(&cp, &seq, &ssz);
switch (esc) {
case ESCAPE_UNICODE:
uc = mchars_num2uc(seq + 1, ssz - 1);
@ -860,6 +959,24 @@ term_strlen(const struct termp *p, const char *cp)
sz += cond_width(p, uc, &skip);
}
continue;
case ESCAPE_UNDEF:
uc = *seq;
break;
case ESCAPE_DEVICE:
if (p->type == TERMTYPE_PDF) {
rhs = "pdf";
rsz = 3;
} else if (p->type == TERMTYPE_PS) {
rhs = "ps";
rsz = 2;
} else if (p->enc == TERMENC_ASCII) {
rhs = "ascii";
rsz = 5;
} else {
rhs = "utf8";
rsz = 4;
}
break;
case ESCAPE_SKIPCHAR:
skip = 1;
continue;

6
term.h
View File

@ -1,7 +1,7 @@
/* $Id: term.h,v 1.130 2017/07/08 14:51:05 schwarze Exp $ */
/* $Id: term.h,v 1.131 2019/01/04 03:21:02 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011-2015, 2017, 2019 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -99,6 +99,8 @@ struct termp {
#define TERMP_NEWMC (1 << 18) /* No .mc printed yet. */
#define TERMP_ENDMC (1 << 19) /* Next break ends .mc mode. */
#define TERMP_MULTICOL (1 << 20) /* Multiple column mode. */
#define TERMP_CENTER (1 << 21) /* Center output lines. */
#define TERMP_RIGHT (1 << 22) /* Adjust to the right margin. */
enum termtype type; /* Terminal, PS, or PDF. */
enum termenc enc; /* Type of encoding. */
enum termfont fontl; /* Last font set. */

View File

@ -1,4 +1,4 @@
/* $Id: term_ascii.c,v 1.61 2018/05/20 21:37:34 schwarze Exp $ */
/* $Id: term_ascii.c,v 1.64 2018/11/28 14:23:06 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
@ -90,7 +90,7 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
p->width = ascii_width;
#if HAVE_WCHAR
if (TERMENC_ASCII != enc) {
if (enc != TERMENC_ASCII) {
/*
* Do not change any of this to LC_ALL. It might break
@ -99,7 +99,7 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
* worst case, it might even cause buffer overflows.
*/
v = TERMENC_LOCALE == enc ?
v = enc == TERMENC_LOCALE ?
setlocale(LC_CTYPE, "") :
setlocale(LC_CTYPE, UTF8_LOCALE);
@ -113,7 +113,7 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
v = setlocale(LC_CTYPE, "C");
if (v != NULL && MB_CUR_MAX > 1) {
p->enc = enc;
p->enc = TERMENC_UTF8;
p->advance = locale_advance;
p->endline = locale_endline;
p->letter = locale_letter;
@ -196,8 +196,7 @@ terminal_sepline(void *arg)
static size_t
ascii_width(const struct termp *p, int c)
{
return 1;
return c != ASCII_BREAK;
}
void
@ -311,7 +310,7 @@ ascii_uc2str(int uc)
"<88>", "<89>", "<8A>", "<8B>", "<8C>", "<8D>", "<8E>", "<8F>",
"<90>", "<91>", "<92>", "<93>", "<94>", "<95>", "<96>", "<97>",
"<98>", "<99>", "<9A>", "<9B>", "<9C>", "<9D>", "<9E>", "<9F>",
nbrsp, "!", "/\bc", "GBP", "o\bx", "=\bY", "|", "<section>",
nbrsp, "!", "/\bc", "-\bL", "o\bx", "=\bY", "|", "<section>",
"\"", "(C)", "_\ba", "<<", "~", "", "(R)", "-",
"<degree>","+-","^2", "^3", "'","<micro>","<paragraph>",".",
",", "^1", "_\bo", ">>", "1/4", "1/2", "3/4", "?",

View File

@ -1,4 +1,4 @@
/* $OpenBSD: term.c,v 1.119 2017/01/08 18:08:44 schwarze Exp $ */
/* $Id: term_tab.c,v 1.5 2018/12/16 00:21:05 schwarze Exp $ */
/*
* Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
*

View File

@ -1,4 +1,4 @@
/* $Id: test-getsubopt.c,v 1.4 2015/10/06 18:32:20 schwarze Exp $ */
/* $Id: test-getsubopt.c,v 1.6 2018/08/15 14:37:41 schwarze Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -15,12 +15,15 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#if defined(__linux__) || defined(__MINT__)
#define _GNU_SOURCE /* getsubopt() */
#endif
#include <stdlib.h>
/*
* NetBSD declared this function in the wrong header before August 2018.
* No harm is done by allowing that, too:
* The only file using it, main.c, also includes unistd.h, anyway.
*/
#include <unistd.h>
int
main(void)
{

View File

@ -1,7 +1,3 @@
#if defined(__linux__) || defined(__MINT__)
# define _GNU_SOURCE /* strcasestr() */
#endif
#include <string.h>
int

View File

@ -1,4 +1,4 @@
/* $Id: test-stringlist.c,v 1.2 2015/10/06 18:32:20 schwarze Exp $ */
/* $Id: test-stringlist.c,v 1.3 2018/08/15 02:48:51 schwarze Exp $ */
/*
* Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
*
@ -15,6 +15,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stddef.h>
#include <stringlist.h>
int

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